From 1fd5cf2b4a6f592cc8df945e540ea523a799bb97 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 27 Sep 2022 21:24:52 -0300 Subject: [PATCH 001/737] Fix ListOpenContextStoredUsers and stub LoadOpenContext (#3718) * Fix ListOpenContextStoredUsers and stub LoadOpenContext * Remove nonsensical comment --- .../Services/Account/Acc/AccountManager.cs | 18 ++++++++++++--- .../Acc/AccountService/ManagerServer.cs | 2 +- .../Account/Acc/ApplicationServiceServer.cs | 5 +++++ .../Acc/IAccountServiceForApplication.cs | 22 +++++++++---------- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs index 9d9cb6ff8..41d5028fb 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs @@ -22,7 +22,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // outside of the AccountManager. private readonly HorizonClient _horizonClient; - private ConcurrentDictionary _profiles; + private readonly ConcurrentDictionary _profiles; + private UserProfile[] _storedOpenedUsers; public UserProfile LastOpenedUser { get; private set; } @@ -31,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc _horizonClient = horizonClient; _profiles = new ConcurrentDictionary(); + _storedOpenedUsers = Array.Empty(); _accountSaveDataManager = new AccountSaveDataManager(_profiles); @@ -44,9 +46,9 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc } else { - UserId commandLineUserProfileOverride = default; + UserId commandLineUserProfileOverride = default; if (!string.IsNullOrEmpty(initialProfileName)) - { + { commandLineUserProfileOverride = _profiles.Values.FirstOrDefault(x => x.Name == initialProfileName)?.UserId ?? default; if (commandLineUserProfileOverride.IsNull) Logger.Warning?.Print(LogClass.Application, $"The command line specified profile named '{initialProfileName}' was not found"); @@ -221,6 +223,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _profiles.Values.Where(x => x.AccountState == AccountState.Open); } + internal IEnumerable GetStoredOpenedUsers() + { + return _storedOpenedUsers; + } + + internal void StoreOpenedUsers() + { + _storedOpenedUsers = _profiles.Values.Where(x => x.AccountState == AccountState.Open).ToArray(); + } + internal UserProfile GetFirst() { return _profiles.First().Value; diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs index d74f5ecf2..011accd28 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs @@ -162,7 +162,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode StoreOpenContext(ServiceCtx context) { - Logger.Stub?.PrintStub(LogClass.ServiceAcc); + context.Device.System.AccountManager.StoreOpenedUsers(); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs index aa6873604..7b474f0e7 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs @@ -201,6 +201,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } + public ResultCode ListOpenContextStoredUsers(ServiceCtx context) + { + return WriteUserList(context, context.Device.System.AccountManager.GetStoredOpenedUsers()); + } + public ResultCode ListQualifiedUsers(ServiceCtx context) { // TODO: Determine how users are "qualified". We assume all users are "qualified" for now. diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 4de0b34bb..920823229 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Logging; -using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService; using Ryujinx.HLE.HOS.Services.Arp; @@ -139,20 +138,21 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _applicationServiceServer.ClearSaveDataThumbnail(context); } + [CommandHipc(130)] // 5.0.0+ + // LoadOpenContext(nn::account::Uid) + public ResultCode LoadOpenContext(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + + [CommandHipc(60)] // 5.0.0-5.1.0 [CommandHipc(131)] // 6.0.0+ // ListOpenContextStoredUsers() -> array public ResultCode ListOpenContextStoredUsers(ServiceCtx context) { - ulong outputPosition = context.Request.RecvListBuff[0].Position; - ulong outputSize = context.Request.RecvListBuff[0].Size; - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); - - // TODO: This seems to write stored userids of the OpenContext in the buffer. We needs to determine them. - - Logger.Stub?.PrintStub(LogClass.ServiceAcc); - - return ResultCode.Success; + return _applicationServiceServer.ListOpenContextStoredUsers(context); } [CommandHipc(141)] // 6.0.0+ From f502cfaf62d52decb5c74d42f33ce6643f62fc26 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 29 Sep 2022 16:32:49 +0100 Subject: [PATCH 002/737] Vulkan: Zero blend state when disabled or write mask is 0 (#3719) * Zero blend state when disabled or write mask is 0 Any difference in the blend state when blend is disabled is meaningless, but Ryujinx would compare different disabled blends and compile them as separate pipelines. This change ensures that all pipelines where blend state is meaningless record it as such, which avoids compiling a bunch of pipelines that are essentially identical. The NVIDIA driver is pretty forgiving when it comes to silly pipeline misses like this, but other drivers don't offer the same level of kindness. This should reduce stuttering on those drivers, and might improve overall performance very slightly due to less pipeline variants being in the hash table. * Fix blend possibly being wrong when an attachment is unmasked --- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 52 +++++++++++++++++--- Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 26 ++++++---- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 39acc5d9e..1a284e209 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -68,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan private bool _tfEnabled; private bool _tfActive; + private PipelineColorBlendAttachmentState[] _storedBlend; + public ulong DrawCount { get; private set; } public unsafe PipelineBase(VulkanRenderer gd, Device device) @@ -104,6 +106,8 @@ namespace Ryujinx.Graphics.Vulkan _newState.Initialize(); _newState.LineWidth = 1f; _newState.SamplesCount = 1; + + _storedBlend = new PipelineColorBlendAttachmentState[8]; } public void Initialize() @@ -498,13 +502,28 @@ namespace Ryujinx.Graphics.Vulkan { ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; - vkBlend.BlendEnable = blend.Enable; - vkBlend.SrcColorBlendFactor = blend.ColorSrcFactor.Convert(); - vkBlend.DstColorBlendFactor = blend.ColorDstFactor.Convert(); - vkBlend.ColorBlendOp = blend.ColorOp.Convert(); - vkBlend.SrcAlphaBlendFactor = blend.AlphaSrcFactor.Convert(); - vkBlend.DstAlphaBlendFactor = blend.AlphaDstFactor.Convert(); - vkBlend.AlphaBlendOp = blend.AlphaOp.Convert(); + if (blend.Enable) + { + vkBlend.BlendEnable = blend.Enable; + vkBlend.SrcColorBlendFactor = blend.ColorSrcFactor.Convert(); + vkBlend.DstColorBlendFactor = blend.ColorDstFactor.Convert(); + vkBlend.ColorBlendOp = blend.ColorOp.Convert(); + vkBlend.SrcAlphaBlendFactor = blend.AlphaSrcFactor.Convert(); + vkBlend.DstAlphaBlendFactor = blend.AlphaDstFactor.Convert(); + vkBlend.AlphaBlendOp = blend.AlphaOp.Convert(); + } + else + { + vkBlend = new PipelineColorBlendAttachmentState( + colorWriteMask: vkBlend.ColorWriteMask); + } + + if (vkBlend.ColorWriteMask == 0) + { + _storedBlend[index] = vkBlend; + + vkBlend = new PipelineColorBlendAttachmentState(); + } _newState.BlendConstantR = blend.BlendConstant.Red; _newState.BlendConstantG = blend.BlendConstant.Green; @@ -669,8 +688,25 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; + var newMask = (ColorComponentFlags)componentMask[i]; - vkBlend.ColorWriteMask = (ColorComponentFlags)componentMask[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 (vkBlend.ColorWriteMask != newMask) + { + if (newMask == 0) + { + _storedBlend[i] = vkBlend; + + vkBlend = new PipelineColorBlendAttachmentState(); + } + else if (vkBlend.ColorWriteMask == 0) + { + vkBlend = _storedBlend[i]; + } + } + + vkBlend.ColorWriteMask = newMask; if (componentMask[i] != 0) { diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 3ff111e87..477d0cec1 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -257,15 +257,23 @@ namespace Ryujinx.Graphics.Vulkan { var blend = state.BlendDescriptors[i]; - pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( - blend.Enable, - blend.ColorSrcFactor.Convert(), - blend.ColorDstFactor.Convert(), - blend.ColorOp.Convert(), - blend.AlphaSrcFactor.Convert(), - blend.AlphaDstFactor.Convert(), - blend.AlphaOp.Convert(), - (ColorComponentFlags)state.ColorWriteMask[i]); + if (blend.Enable && state.ColorWriteMask[i] != 0) + { + pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( + blend.Enable, + blend.ColorSrcFactor.Convert(), + blend.ColorDstFactor.Convert(), + blend.ColorOp.Convert(), + blend.AlphaSrcFactor.Convert(), + blend.AlphaDstFactor.Convert(), + blend.AlphaOp.Convert(), + (ColorComponentFlags)state.ColorWriteMask[i]); + } + else + { + pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( + colorWriteMask: (ColorComponentFlags)state.ColorWriteMask[i]); + } } int maxAttachmentIndex = 0; From dbe43c17199a96e86175c9dd39d6062ce58cefb4 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 29 Sep 2022 12:45:25 -0300 Subject: [PATCH 003/737] Fix SSL GetCertificates with certificate ID set to All (#3727) * Fix SSL GetCertificates with certificate ID set to All * Fix last entry status value --- .../Services/Ssl/BuiltInCertificateManager.cs | 13 ++++- Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs | 49 +++++++++++-------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs b/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs index 70691e1bd..a164c7455 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs @@ -181,7 +181,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl } } - public bool TryGetCertificates(ReadOnlySpan ids, out CertStoreEntry[] entries) + public bool TryGetCertificates( + ReadOnlySpan ids, + out CertStoreEntry[] entries, + out bool hasAllCertificates, + out int requiredSize) { lock (_lock) { @@ -190,7 +194,8 @@ namespace Ryujinx.HLE.HOS.Services.Ssl throw new InvalidSystemResourceException(CertStoreTitleMissingErrorMessage); } - bool hasAllCertificates = false; + requiredSize = 0; + hasAllCertificates = false; foreach (CaCertificateId id in ids) { @@ -205,12 +210,14 @@ namespace Ryujinx.HLE.HOS.Services.Ssl if (hasAllCertificates) { entries = new CertStoreEntry[_certificates.Count]; + requiredSize = (_certificates.Count + 1) * Unsafe.SizeOf(); int i = 0; foreach (CertStoreEntry entry in _certificates.Values) { entries[i++] = entry; + requiredSize += (entry.Data.Length + 3) & ~3; } return true; @@ -218,6 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl else { entries = new CertStoreEntry[ids.Length]; + requiredSize = ids.Length * Unsafe.SizeOf(); for (int i = 0; i < ids.Length; i++) { @@ -227,6 +235,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl } entries[i] = entry; + requiredSize += (entry.Data.Length + 3) & ~3; } return true; diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs index d397ef5b0..73f044418 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs @@ -29,42 +29,40 @@ namespace Ryujinx.HLE.HOS.Services.Ssl return ResultCode.Success; } - private uint ComputeCertificateBufferSizeRequired(ReadOnlySpan entries) - { - uint totalSize = 0; - - for (int i = 0; i < entries.Length; i++) - { - totalSize += (uint)Unsafe.SizeOf(); - totalSize += (uint)entries[i].Data.Length; - } - - return totalSize; - } - [CommandHipc(2)] // GetCertificates(buffer ids) -> (u32 certificates_count, buffer certificates) public ResultCode GetCertificates(ServiceCtx context) { ReadOnlySpan ids = MemoryMarshal.Cast(context.Memory.GetSpan(context.Request.SendBuff[0].Position, (int)context.Request.SendBuff[0].Size)); - if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out BuiltInCertificateManager.CertStoreEntry[] entries)) + if (!BuiltInCertificateManager.Instance.TryGetCertificates( + ids, + out BuiltInCertificateManager.CertStoreEntry[] entries, + out bool hasAllCertificates, + out int requiredSize)) { throw new InvalidOperationException(); } - if (ComputeCertificateBufferSizeRequired(entries) > context.Request.ReceiveBuff[0].Size) + if ((uint)requiredSize > (uint)context.Request.ReceiveBuff[0].Size) { return ResultCode.InvalidCertBufSize; } + int infosCount = entries.Length; + + if (hasAllCertificates) + { + infosCount++; + } + using (WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size)) { Span rawData = region.Memory.Span; - Span infos = MemoryMarshal.Cast(rawData)[..entries.Length]; - Span certificatesData = rawData[(Unsafe.SizeOf() * entries.Length)..]; + Span infos = MemoryMarshal.Cast(rawData)[..infosCount]; + Span certificatesData = rawData[(Unsafe.SizeOf() * infosCount)..]; - for (int i = 0; i < infos.Length; i++) + for (int i = 0; i < entries.Length; i++) { entries[i].Data.CopyTo(certificatesData); @@ -78,6 +76,17 @@ namespace Ryujinx.HLE.HOS.Services.Ssl certificatesData = certificatesData[entries[i].Data.Length..]; } + + if (hasAllCertificates) + { + infos[entries.Length] = new BuiltInCertificateInfo + { + Id = CaCertificateId.All, + Status = TrustedCertStatus.Invalid, + CertificateDataSize = 0, + CertificateDataOffset = 0 + }; + } } context.ResponseData.Write(entries.Length); @@ -91,12 +100,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { ReadOnlySpan ids = MemoryMarshal.Cast(context.Memory.GetSpan(context.Request.SendBuff[0].Position, (int)context.Request.SendBuff[0].Size)); - if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out BuiltInCertificateManager.CertStoreEntry[] entries)) + if (!BuiltInCertificateManager.Instance.TryGetCertificates(ids, out _, out _, out int requiredSize)) { throw new InvalidOperationException(); } - context.ResponseData.Write(ComputeCertificateBufferSizeRequired(entries)); + context.ResponseData.Write(requiredSize); return ResultCode.Success; } From 9c2500de5ffa76d74e1761be9e6a1e50b36af7c5 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 1 Oct 2022 02:35:52 -0300 Subject: [PATCH 004/737] Fix incorrect tessellation inputs/outputs (#3728) * Fix incorrect tessellation inputs/outputs * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 51 ++++----- .../CodeGen/Glsl/OperandManager.cs | 83 ++++++++------- .../CodeGen/Spirv/CodeGenContext.cs | 10 +- .../CodeGen/Spirv/Declarations.cs | 26 +++-- .../CodeGen/Spirv/Instructions.cs | 3 +- .../CodeGen/Spirv/SpirvGenerator.cs | 15 ++- Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 24 ++++- .../Instructions/InstEmitAttribute.cs | 9 +- .../StructuredIr/StructuredProgram.cs | 10 +- .../Translation/AttributeConsts.cs | 3 + .../Translation/AttributeInfo.cs | 91 ++++++++++------ .../Translation/EmitterContext.cs | 4 +- .../Translation/ShaderConfig.cs | 100 ++++++++++++------ .../Translation/Translator.cs | 14 ++- .../Translation/TranslatorContext.cs | 3 +- 16 files changed, 284 insertions(+), 164 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 24b05b90b..e5efa0c52 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3697; + private const uint CodeGenVersion = 3728; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 65e781216..ff808b04b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; +using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -163,9 +164,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (context.Config.Stage == ShaderStage.TessellationEvaluation) { + bool tessCw = context.Config.GpuAccessor.QueryTessCw(); + + if (context.Config.Options.TargetApi == TargetApi.Vulkan) + { + // We invert the front face on Vulkan backend, so we need to do that here aswell. + tessCw = !tessCw; + } + string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl(); string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl(); - string windingOrder = context.Config.GpuAccessor.QueryTessCw() ? "cw" : "ccw"; + string windingOrder = tessCw ? "cw" : "ccw"; context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;"); context.AppendLine(); @@ -185,14 +194,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - if (context.Config.UsedInputAttributesPerPatch != 0) + if (context.Config.UsedInputAttributesPerPatch.Count != 0) { DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch); context.AppendLine(); } - if (context.Config.UsedOutputAttributesPerPatch != 0) + if (context.Config.UsedOutputAttributesPerPatch.Count != 0) { DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch); @@ -509,13 +518,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareInputAttributesPerPatch(CodeGenContext context, int usedAttributes) + private static void DeclareInputAttributesPerPatch(CodeGenContext context, HashSet attrs) { - while (usedAttributes != 0) + foreach (int attr in attrs.OrderBy(x => x)) { - int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareInputAttributePerPatch(context, index); - usedAttributes &= ~(1 << index); + DeclareInputAttributePerPatch(context, attr); } } @@ -566,16 +573,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr) { - string layout = string.Empty; - - if (context.Config.Options.TargetApi == TargetApi.Vulkan) - { - layout = $"layout (location = {32 + attr}) "; - } - + int location = context.Config.GetPerPatchAttributeLocation(attr); string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; - context.AppendLine($"{layout}patch in vec4 {name};"); + context.AppendLine($"layout (location = {location}) patch in vec4 {name};"); } private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) @@ -624,28 +625,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, int usedAttributes) + private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet attrs) { - while (usedAttributes != 0) + foreach (int attr in attrs.OrderBy(x => x)) { - int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareOutputAttributePerPatch(context, index); - usedAttributes &= ~(1 << index); + DeclareOutputAttributePerPatch(context, attr); } } private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr) { - string layout = string.Empty; - - if (context.Config.Options.TargetApi == TargetApi.Vulkan) - { - layout = $"layout (location = {32 + attr}) "; - } - + int location = context.Config.GetPerPatchAttributeLocation(attr); string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; - context.AppendLine($"{layout}patch out vec4 {name};"); + context.AppendLine($"layout (location = {location}) patch out vec4 {name};"); } private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 1ab91f77f..fd284316f 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -28,33 +28,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static Dictionary _builtInAttributes = new Dictionary() { - { AttributeConsts.TessLevelOuter0, new BuiltInAttribute("gl_TessLevelOuter[0]", VariableType.F32) }, - { AttributeConsts.TessLevelOuter1, new BuiltInAttribute("gl_TessLevelOuter[1]", VariableType.F32) }, - { AttributeConsts.TessLevelOuter2, new BuiltInAttribute("gl_TessLevelOuter[2]", VariableType.F32) }, - { AttributeConsts.TessLevelOuter3, new BuiltInAttribute("gl_TessLevelOuter[3]", VariableType.F32) }, - { AttributeConsts.TessLevelInner0, new BuiltInAttribute("gl_TessLevelInner[0]", VariableType.F32) }, - { AttributeConsts.TessLevelInner1, new BuiltInAttribute("gl_TessLevelInner[1]", VariableType.F32) }, - { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, - { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, - { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, - { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) }, - { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) }, - { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) }, - { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) }, - { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) }, - { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) }, - { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) }, - { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) }, - { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) }, - { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) }, - { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) }, - { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) }, - { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, - { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, - { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, - { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, - { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, - { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, + { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, + { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, + { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, + { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) }, + { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) }, + { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) }, + { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) }, + { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) }, + { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) }, + { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) }, + { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) }, + { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) }, + { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) }, + { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) }, + { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) }, + { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, + { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, + { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, + { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, + { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, + { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, // Special. { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, @@ -170,7 +164,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl value &= AttributeConsts.Mask & ~3; char swzMask = GetSwizzleMask((value >> 2) & 3); - if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) + if (perPatch) + { + if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd) + { + value -= AttributeConsts.UserAttributePerPatchBase; + + return $"{DefaultNames.PerPatchAttributePrefix}{(value >> 4)}.{swzMask}"; + } + else if (value < AttributeConsts.UserAttributePerPatchBase) + { + return value switch + { + AttributeConsts.TessLevelOuter0 => "gl_TessLevelOuter[0]", + AttributeConsts.TessLevelOuter1 => "gl_TessLevelOuter[1]", + AttributeConsts.TessLevelOuter2 => "gl_TessLevelOuter[2]", + AttributeConsts.TessLevelOuter3 => "gl_TessLevelOuter[3]", + AttributeConsts.TessLevelInner0 => "gl_TessLevelInner[0]", + AttributeConsts.TessLevelInner1 => "gl_TessLevelInner[1]", + _ => null + }; + } + } + else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) { value -= AttributeConsts.UserAttributeBase; @@ -180,11 +196,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing); - if (!indexable && perPatch) - { - prefix = DefaultNames.PerPatchAttributePrefix; - } - if (indexable) { string name = prefix; @@ -202,7 +213,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string name = $"{prefix}{(value >> 4)}_{swzMask}"; - if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) + if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) { name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; } @@ -213,7 +224,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string name = $"{prefix}{(value >> 4)}"; - if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) + if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) { name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; } @@ -277,7 +288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string name = builtInAttr.Name; - if (!perPatch && AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) + if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) { name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}"; } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index d70a00edc..fe5e11f4e 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -382,17 +382,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType) { var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrInfo = AttributeInfo.From(Config, attr, isOutAttr); + var attrInfo = AttributeInfo.FromPatch(Config, attr, isOutAttr); int attrOffset = attrInfo.BaseValue; - Instruction ioVariable; - - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; + Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset]; elemType = attrInfo.Type & AggregateType.ElementTypeMask; - ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset]; - if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0) { return ioVariable; @@ -404,7 +400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr) { - if (!AttributeInfo.Validate(Config, attr, isOutAttr: false)) + if (!AttributeInfo.ValidatePerPatch(Config, attr, isOutAttr: false)) { return GetConstant(type, new AstOperand(IrOperandType.Constant, 0)); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index dce5e48ad..1a4decf5c 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -403,7 +403,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv foreach (int attr in inputs) { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false)) + if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch)) { continue; } @@ -459,7 +459,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv foreach (int attr in outputs) { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true)) + if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch)) { continue; } @@ -519,7 +519,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv ? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch) : (isOutAttr ? context.Outputs : context.Inputs); - var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); + var attrInfo = perPatch + ? AttributeInfo.FromPatch(context.Config, attr, isOutAttr) + : AttributeInfo.From(context.Config, attr, isOutAttr); if (dict.ContainsKey(attrInfo.BaseValue)) { @@ -544,11 +546,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var spvType = context.TypePointer(storageClass, attrType); var spvVar = context.Variable(spvType, storageClass); - if (perPatch) - { - context.Decorate(spvVar, Decoration.Patch); - } - if (builtInPassthrough) { context.Decorate(spvVar, Decoration.PassthroughNV); @@ -556,6 +553,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (attrInfo.IsBuiltin) { + if (perPatch) + { + context.Decorate(spvVar, Decoration.Patch); + } + context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) @@ -569,6 +571,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } } + else if (perPatch) + { + context.Decorate(spvVar, Decoration.Patch); + + int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16); + + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } else if (isUserAttr) { int location = (attr - AttributeConsts.UserAttributeBase) / 16; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index a7fb78b44..c743a274e 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -882,7 +882,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (src2 is AstOperand operand && operand.Type == OperandType.Constant) { int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2); - return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr: false, index)); + bool isOutAttr = (baseAttr.Value & AttributeConsts.LoadOutputMask) != 0; + return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr, index)); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 23c6af810..fad7f9b88 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -191,7 +191,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } - if (context.Config.GpuAccessor.QueryTessCw()) + bool tessCw = context.Config.GpuAccessor.QueryTessCw(); + + if (context.Config.Options.TargetApi == TargetApi.Vulkan) + { + // We invert the front face on Vulkan backend, so we need to do that here aswell. + tessCw = !tessCw; + } + + if (tessCw) { context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw); } @@ -375,9 +383,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch) { - if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true)) + bool perPatch = dest.Type == OperandType.AttributePerPatch; + + if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch)) { - bool perPatch = dest.Type == OperandType.AttributePerPatch; AggregateType elemType; var elemPointer = perPatch diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 69f9a5206..1c329b593 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -306,18 +306,36 @@ namespace Ryujinx.Graphics.Shader.Decoders for (int elemIndex = 0; elemIndex < count; elemIndex++) { int attr = offset + elemIndex * 4; - if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) + + if (perPatch) + { + if (attr >= AttributeConsts.UserAttributePerPatchBase && attr < AttributeConsts.UserAttributePerPatchEnd) + { + int userAttr = attr - AttributeConsts.UserAttributePerPatchBase; + int index = userAttr / 16; + + if (isStore) + { + config.SetOutputUserAttributePerPatch(index); + } + else + { + config.SetInputUserAttributePerPatch(index); + } + } + } + else if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) { int userAttr = attr - AttributeConsts.UserAttributeBase; int index = userAttr / 16; if (isStore) { - config.SetOutputUserAttribute(index, perPatch); + config.SetOutputUserAttribute(index); } else { - config.SetInputUserAttribute(index, (userAttr >> 2) & 3, perPatch); + config.SetInputUserAttribute(index, (userAttr >> 2) & 3); } } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 6ce2e5372..7edf5deb8 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeRead(offset); - if (op.O) + if (op.O && CanLoadOutput(offset)) { offset |= AttributeConsts.LoadOutputMask; } @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeRead(offset); - if (op.O) + if (op.O && CanLoadOutput(offset)) { offset |= AttributeConsts.LoadOutputMask; } @@ -241,6 +241,11 @@ namespace Ryujinx.Graphics.Shader.Instructions } } + private static bool CanLoadOutput(int attr) + { + return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY; + } + private static bool TryFixedFuncToUserAttributeIpa(EmitterContext context, int attr, out Operand selectedAttr) { if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.BackColorDiffuseR) diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 9d8e64bf0..85049abb2 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -97,7 +97,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant) { int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2); - context.Info.Inputs.Add(attrOffset); + + if ((src1.Value & AttributeConsts.LoadOutputMask) != 0) + { + context.Info.Outputs.Add(attrOffset); + } + else + { + context.Info.Inputs.Add(attrOffset); + } } } diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 0c3ab08e2..f4e39d0da 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -54,6 +54,9 @@ namespace Ryujinx.Graphics.Shader.Translation public const int UserAttributeBase = 0x80; public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16; + public const int UserAttributePerPatchBase = 0x18; + public const int UserAttributePerPatchEnd = 0x200; + public const int LoadOutputMask = 1 << 30; public const int Mask = 0x3fffffff; diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index 6680a3328..35dd56e82 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -4,36 +4,30 @@ namespace Ryujinx.Graphics.Shader.Translation { struct AttributeInfo { - private static readonly Dictionary BuiltInAttributes = new Dictionary() + private static readonly Dictionary _builtInAttributes = new Dictionary() { - { AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) }, - { AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) }, - { AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) }, - { AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, - { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, - { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, + { AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) }, + { AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) }, + { AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) }, + { AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, + { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, + { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, // Special. { AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) }, @@ -55,6 +49,16 @@ namespace Ryujinx.Graphics.Shader.Translation { AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector | AggregateType.U32) }, }; + private static readonly Dictionary _builtInAttributesPerPatch = new Dictionary() + { + { AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) }, + }; + public int BaseValue { get; } public int Value { get; } public int Length { get; } @@ -76,6 +80,11 @@ namespace Ryujinx.Graphics.Shader.Translation return (Value - BaseValue) / 4; } + public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch) + { + return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr); + } + public static bool Validate(ShaderConfig config, int value, bool isOutAttr) { if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex()) @@ -86,6 +95,11 @@ namespace Ryujinx.Graphics.Shader.Translation return From(config, value, isOutAttr).IsValid; } + public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr) + { + return FromPatch(config, value, isOutAttr).IsValid; + } + public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr) { value &= ~3; @@ -115,7 +129,24 @@ namespace Ryujinx.Graphics.Shader.Translation { return new AttributeInfo(value, 0, 1, AggregateType.FP32); } - else if (BuiltInAttributes.TryGetValue(value, out AttributeInfo info)) + else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info)) + { + return info; + } + + return new AttributeInfo(value, 0, 0, AggregateType.Invalid); + } + + public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr) + { + value &= ~3; + + if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd) + { + int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf; + return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector | AggregateType.FP32, false); + } + else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info)) { return info; } diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index e8b682d0b..3e50ce2fd 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int index = BitOperations.TrailingZeroCount(passthroughAttributes); WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex); - Config.SetOutputUserAttribute(index, perPatch: false); + Config.SetOutputUserAttribute(index); passthroughAttributes &= ~(1 << index); } @@ -364,7 +364,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0; if (targetEnabled) { - Config.SetOutputUserAttribute(rtIndex, perPatch: false); + Config.SetOutputUserAttribute(rtIndex); regIndexBase += 4; } } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 221ca1d47..b18979d8f 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -50,16 +50,16 @@ namespace Ryujinx.Graphics.Shader.Translation public bool NextUsesFixedFuncAttributes { get; private set; } public int UsedInputAttributes { get; private set; } public int UsedOutputAttributes { get; private set; } - public int UsedInputAttributesPerPatch { get; private set; } - public int UsedOutputAttributesPerPatch { get; private set; } + public HashSet UsedInputAttributesPerPatch { get; } + public HashSet UsedOutputAttributesPerPatch { get; } + public HashSet NextUsedInputAttributesPerPatch { get; private set; } public int PassthroughAttributes { get; private set; } private int _nextUsedInputAttributes; private int _thisUsedInputAttributes; + private Dictionary _perPatchAttributeLocations; public UInt128 NextInputAttributesComponents { get; private set; } public UInt128 ThisInputAttributesComponents { get; private set; } - public UInt128 NextInputAttributesPerPatchComponents { get; private set; } - public UInt128 ThisInputAttributesPerPatchComponents { get; private set; } private int _usedConstantBuffers; private int _usedStorageBuffers; @@ -119,9 +119,13 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options) { - Stage = ShaderStage.Compute; - GpuAccessor = gpuAccessor; - Options = options; + Stage = ShaderStage.Compute; + GpuAccessor = gpuAccessor; + Options = options; + + UsedInputAttributesPerPatch = new HashSet(); + UsedOutputAttributesPerPatch = new HashSet(); + _usedTextures = new Dictionary(); _usedImages = new Dictionary(); } @@ -244,49 +248,71 @@ namespace Ryujinx.Graphics.Shader.Translation UsedOutputAttributes |= 1 << index; } - public void SetInputUserAttribute(int index, int component, bool perPatch) + public void SetInputUserAttribute(int index, int component) { - if (perPatch) - { - UsedInputAttributesPerPatch |= 1 << index; - ThisInputAttributesPerPatchComponents |= UInt128.Pow2(index * 4 + component); - } - else - { - int mask = 1 << index; + int mask = 1 << index; - UsedInputAttributes |= mask; - _thisUsedInputAttributes |= mask; - ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component); - } + UsedInputAttributes |= mask; + _thisUsedInputAttributes |= mask; + ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component); } - public void SetOutputUserAttribute(int index, bool perPatch) + public void SetInputUserAttributePerPatch(int index) { - if (perPatch) - { - UsedOutputAttributesPerPatch |= 1 << index; - } - else - { - UsedOutputAttributes |= 1 << index; - } + UsedInputAttributesPerPatch.Add(index); + } + + public void SetOutputUserAttribute(int index) + { + UsedOutputAttributes |= 1 << index; + } + + public void SetOutputUserAttributePerPatch(int index) + { + UsedOutputAttributesPerPatch.Add(index); } public void MergeFromtNextStage(ShaderConfig config) { NextInputAttributesComponents = config.ThisInputAttributesComponents; - NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents; + NextUsedInputAttributesPerPatch = config.UsedInputAttributesPerPatch; NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr); MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch); + if (UsedOutputAttributesPerPatch.Count != 0) + { + // Regular and per-patch input/output locations can't overlap, + // so we must assign on our location using unused regular input/output locations. + + Dictionary locationsMap = new Dictionary(); + + int freeMask = ~UsedOutputAttributes; + + foreach (int attr in UsedOutputAttributesPerPatch) + { + int location = BitOperations.TrailingZeroCount(freeMask); + if (location == 32) + { + config.GpuAccessor.Log($"No enough free locations for patch input/output 0x{attr:X}."); + break; + } + + locationsMap.Add(attr, location); + freeMask &= ~(1 << location); + } + + // Both stages must agree on the locations, so use the same "map" for both. + _perPatchAttributeLocations = locationsMap; + config._perPatchAttributeLocations = locationsMap; + } + if (config.Stage != ShaderStage.Fragment) { LastInVertexPipeline = false; } } - public void MergeOutputUserAttributes(int mask, int maskPerPatch) + public void MergeOutputUserAttributes(int mask, IEnumerable perPatch) { _nextUsedInputAttributes = mask; @@ -297,10 +323,20 @@ namespace Ryujinx.Graphics.Shader.Translation else { UsedOutputAttributes |= mask; - UsedOutputAttributesPerPatch |= maskPerPatch; + UsedOutputAttributesPerPatch.UnionWith(perPatch); } } + public int GetPerPatchAttributeLocation(int index) + { + if (_perPatchAttributeLocations == null || !_perPatchAttributeLocations.TryGetValue(index, out int location)) + { + return index; + } + + return location; + } + public bool IsUsedOutputAttribute(int attr) { // The check for fixed function attributes on the next stage is conservative, diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 8657c0f7d..78fd9498a 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -204,14 +204,12 @@ namespace Ryujinx.Graphics.Shader.Translation InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false); } - UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents; - while (usedAttributesPerPatch != UInt128.Zero) + if (context.Config.NextUsedInputAttributesPerPatch != null) { - int index = usedAttributesPerPatch.TrailingZeroCount(); - - InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: true); - - usedAttributesPerPatch &= ~UInt128.Pow2(index); + foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.OrderBy(x => x)) + { + InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true); + } } if (config.NextUsesFixedFuncAttributes) @@ -236,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.Translation for (int c = 0; c < 4; c++) { int attrOffset = baseAttr + c * 4; - context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f)); + InitializeOutputComponent(context, attrOffset, perPatch); } } diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 8900f9fe6..7d820f033 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System.Collections.Generic; +using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.Translation.Translator; @@ -137,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (other != null) { - other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, 0); + other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty()); FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart); From 33e673ceb84edf31dcb29abdf5df004cfd3beca5 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sun, 2 Oct 2022 10:30:46 +0200 Subject: [PATCH 005/737] fatal: Implement Service (#3573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fatal: Implement Service This PR adds a basic implementation of fatal service, guest processes call it when there is something wrong. But since we can already have all informations by debugging it's not really useful. In any case, that's avoid an unimplemented service exception. Structs/Enum are based on Atmosphère source code. After logs the error report, I call SvcBreak. Feedbacks are welcome on this, since some guests calls it right after fatal service so I can remove it if needed. * Addresses gdkchan feedback --- Ryujinx.Common/Logging/LogClass.cs | 1 + Ryujinx.HLE/HOS/Services/Fatal/IService.cs | 141 +++++++++++++++++- .../HOS/Services/Fatal/Types/CpuContext32.cs | 25 ++++ .../HOS/Services/Fatal/Types/CpuContext64.cs | 24 +++ .../HOS/Services/Fatal/Types/FatalPolicy.cs | 9 ++ 5 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs create mode 100644 Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs create mode 100644 Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 20c8da3f8..2e936fc72 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -30,6 +30,7 @@ namespace Ryujinx.Common.Logging ServiceBsd, ServiceBtm, ServiceCaps, + ServiceFatal, ServiceFriend, ServiceFs, ServiceHid, diff --git a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index 692d2b0b7..6d663a4de 100644 --- a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -1,8 +1,147 @@ -namespace Ryujinx.HLE.HOS.Services.Fatal +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Fatal.Types; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Fatal { [Service("fatal:u")] class IService : IpcService { public IService(ServiceCtx context) { } + + [CommandHipc(0)] + // ThrowFatal(u64 result_code, u64 pid) + public ResultCode ThrowFatal(ServiceCtx context) + { + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + ulong pid = context.Request.HandleDesc.PId; + + return ThrowFatalWithCpuContextImpl(context, resultCode, pid, FatalPolicy.ErrorReportAndErrorScreen, null); + } + + [CommandHipc(1)] + // ThrowFatalWithPolicy(u64 result_code, u32 fatal_policy, u64 pid) + public ResultCode ThrowFatalWithPolicy(ServiceCtx context) + { + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + FatalPolicy fatalPolicy = (FatalPolicy)context.RequestData.ReadUInt32(); + ulong pid = context.Request.HandleDesc.PId; + + return ThrowFatalWithCpuContextImpl(context, resultCode, pid, fatalPolicy, null); + } + + [CommandHipc(2)] + // ThrowFatalWithCpuContext(u64 result_code, u32 fatal_policy, u64 pid, buffer cpu_context) + public ResultCode ThrowFatalWithCpuContext(ServiceCtx context) + { + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + FatalPolicy fatalPolicy = (FatalPolicy)context.RequestData.ReadUInt32(); + ulong pid = context.Request.HandleDesc.PId; + + ulong cpuContextPosition = context.Request.SendBuff[0].Position; + ulong cpuContextSize = context.Request.SendBuff[0].Size; + + ReadOnlySpan cpuContextData = context.Memory.GetSpan(cpuContextPosition, (int)cpuContextSize); + + return ThrowFatalWithCpuContextImpl(context, resultCode, pid, fatalPolicy, cpuContextData); + } + + private ResultCode ThrowFatalWithCpuContextImpl(ServiceCtx context, ResultCode resultCode, ulong pid, FatalPolicy fatalPolicy, ReadOnlySpan cpuContext) + { + StringBuilder errorReport = new StringBuilder(); + + errorReport.AppendLine(); + errorReport.AppendLine("ErrorReport log:"); + + errorReport.AppendLine($"\tTitleId: {context.Device.Application.TitleId:x16}"); + errorReport.AppendLine($"\tPid: {pid}"); + errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}"); + errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}"); + + if (cpuContext != null) + { + errorReport.AppendLine("CPU Context:"); + + if (context.Device.Application.TitleIs64Bit) + { + CpuContext64 cpuContext64 = MemoryMarshal.Cast(cpuContext)[0]; + + errorReport.AppendLine($"\tStartAddress: 0x{cpuContext64.StartAddress:x16}"); + errorReport.AppendLine($"\tRegisterSetFlags: {cpuContext64.RegisterSetFlags}"); + + if (cpuContext64.StackTraceSize > 0) + { + errorReport.AppendLine("\tStackTrace:"); + + for (int i = 0; i < cpuContext64.StackTraceSize; i++) + { + errorReport.AppendLine($"\t\t0x{cpuContext64.StackTrace[i]:x16}"); + } + } + + errorReport.AppendLine("\tRegisters:"); + + for (int i = 0; i < cpuContext64.X.Length; i++) + { + errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext64.X[i]:x16}"); + } + + errorReport.AppendLine(); + errorReport.AppendLine($"\t\tFP:\t0x{cpuContext64.FP:x16}"); + errorReport.AppendLine($"\t\tLR:\t0x{cpuContext64.LR:x16}"); + errorReport.AppendLine($"\t\tSP:\t0x{cpuContext64.SP:x16}"); + errorReport.AppendLine($"\t\tPC:\t0x{cpuContext64.PC:x16}"); + errorReport.AppendLine($"\t\tPState:\t0x{cpuContext64.PState:x16}"); + errorReport.AppendLine($"\t\tAfsr0:\t0x{cpuContext64.Afsr0:x16}"); + errorReport.AppendLine($"\t\tAfsr1:\t0x{cpuContext64.Afsr1:x16}"); + errorReport.AppendLine($"\t\tEsr:\t0x{cpuContext64.Esr:x16}"); + errorReport.AppendLine($"\t\tFar:\t0x{cpuContext64.Far:x16}"); + } + else + { + CpuContext32 cpuContext32 = MemoryMarshal.Cast(cpuContext)[0]; + + errorReport.AppendLine($"\tStartAddress: 0x{cpuContext32.StartAddress:16}"); + errorReport.AppendLine($"\tRegisterSetFlags: {cpuContext32.RegisterSetFlags}"); + + if (cpuContext32.StackTraceSize > 0) + { + errorReport.AppendLine("\tStackTrace:"); + + for (int i = 0; i < cpuContext32.StackTraceSize; i++) + { + errorReport.AppendLine($"\t\t0x{cpuContext32.StackTrace[i]:x16}"); + } + } + + errorReport.AppendLine("\tRegisters:"); + + for (int i = 0; i < cpuContext32.X.Length; i++) + { + errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext32.X[i]:x16}"); + } + + errorReport.AppendLine(); + errorReport.AppendLine($"\t\tFP:\t0x{cpuContext32.FP:x16}"); + errorReport.AppendLine($"\t\tFP:\t0x{cpuContext32.IP:x16}"); + errorReport.AppendLine($"\t\tSP:\t0x{cpuContext32.SP:x16}"); + errorReport.AppendLine($"\t\tLR:\t0x{cpuContext32.LR:x16}"); + errorReport.AppendLine($"\t\tPC:\t0x{cpuContext32.PC:x16}"); + errorReport.AppendLine($"\t\tPState:\t0x{cpuContext32.PState:x16}"); + errorReport.AppendLine($"\t\tAfsr0:\t0x{cpuContext32.Afsr0:x16}"); + errorReport.AppendLine($"\t\tAfsr1:\t0x{cpuContext32.Afsr1:x16}"); + errorReport.AppendLine($"\t\tEsr:\t0x{cpuContext32.Esr:x16}"); + errorReport.AppendLine($"\t\tFar:\t0x{cpuContext32.Far:x16}"); + } + } + + Logger.Info?.Print(LogClass.ServiceFatal, errorReport.ToString()); + + context.Device.System.KernelContext.Syscall.Break((ulong)resultCode); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs new file mode 100644 index 000000000..5c0b116bc --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs @@ -0,0 +1,25 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.HLE.HOS.Services.Fatal.Types +{ + public struct CpuContext32 + { + public Array11 X; + public uint FP; + public uint IP; + public uint SP; + public uint LR; + public uint PC; + + public uint PState; + public uint Afsr0; + public uint Afsr1; + public uint Esr; + public uint Far; + + public Array32 StackTrace; + public uint StackTraceSize; + public uint StartAddress; + public uint RegisterSetFlags; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs new file mode 100644 index 000000000..24829a78a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs @@ -0,0 +1,24 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.HLE.HOS.Services.Fatal.Types +{ + public struct CpuContext64 + { + public Array29 X; + public ulong FP; + public ulong LR; + public ulong SP; + public ulong PC; + + public ulong PState; + public ulong Afsr0; + public ulong Afsr1; + public ulong Esr; + public ulong Far; + + public Array32 StackTrace; + public ulong StartAddress; + public ulong RegisterSetFlags; + public uint StackTraceSize; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs b/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs new file mode 100644 index 000000000..fe55cf12b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Fatal.Types +{ + enum FatalPolicy + { + ErrorReportAndErrorScreen, + ErrorReport, + ErrorScreen + } +} \ No newline at end of file From 96bf7f8522e38c36d792a6ac2173497c3674e920 Mon Sep 17 00:00:00 2001 From: mageven <62494521+mageven@users.noreply.github.com> Date: Sun, 2 Oct 2022 14:29:34 +0530 Subject: [PATCH 006/737] Avoid allocating unmanaged string per shader (#3730) * Avoid reallocating same unmanaged string per shader * Address PR feedback * Rename to _disposed --- Ryujinx.Graphics.Vulkan/Shader.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/Shader.cs b/Ryujinx.Graphics.Vulkan/Shader.cs index db13af314..26d0ca408 100644 --- a/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/Ryujinx.Graphics.Vulkan/Shader.cs @@ -9,17 +9,19 @@ using System.Threading.Tasks; namespace Ryujinx.Graphics.Vulkan { - class Shader + class Shader : IDisposable { // The shaderc.net dependency's Options constructor and dispose are not thread safe. // Take this lock when using them. private static object _shaderOptionsLock = new object(); + private static readonly IntPtr _ptrMainEntryPointName = Marshal.StringToHGlobalAnsi("main"); + private readonly Vk _api; private readonly Device _device; private readonly ShaderStageFlags _stage; - private IntPtr _entryPointName; + private bool _disposed; private ShaderModule _module; public ShaderStageFlags StageFlags => _stage; @@ -39,7 +41,6 @@ namespace Ryujinx.Graphics.Vulkan CompileStatus = ProgramLinkStatus.Incomplete; _stage = shaderSource.Stage.Convert(); - _entryPointName = Marshal.StringToHGlobalAnsi("main"); CompileTask = Task.Run(() => { @@ -145,7 +146,7 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PipelineShaderStageCreateInfo, Stage = _stage, Module = _module, - PName = (byte*)_entryPointName + PName = (byte*)_ptrMainEntryPointName }; } @@ -156,11 +157,10 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void Dispose() { - if (_entryPointName != IntPtr.Zero) + if (!_disposed) { _api.DestroyShaderModule(_device, _module, null); - Marshal.FreeHGlobal(_entryPointName); - _entryPointName = IntPtr.Zero; + _disposed = true; } } } From 45ce540b9b756f372840e923b73cfd7e3edd85f8 Mon Sep 17 00:00:00 2001 From: Wunk Date: Sun, 2 Oct 2022 02:17:19 -0700 Subject: [PATCH 007/737] ARMeilleure: Add `gfni` acceleration (#3669) * ARMeilleure: Add `GFNI` detection This is intended for utilizing the `gf2p8affineqb` instruction * ARMeilleure: Add `gf2p8affineqb` Not using the VEX or EVEX-form of this instruction is intentional. There are `GFNI`-chips that do not support AVX(so no VEX encoding) such as Tremont(Lakefield) chips as well as Jasper Lake. https://github.com/InstLatx64/InstLatx64/blob/13df339fe7150b114929f71b19a6b2fe72fc751e/GenuineIntel/GenuineIntel00806A1_Lakefield_LC_InstLatX64.txt#L1297-L1299 https://github.com/InstLatx64/InstLatx64/blob/13df339fe7150b114929f71b19a6b2fe72fc751e/GenuineIntel/GenuineIntel00906C0_JasperLake_InstLatX64.txt#L1252-L1254 * ARMeilleure: Add `gfni` acceleration of `Rbit_V` Passes all `Rbit_V*` unit tests on my `i9-11900k` * ARMeilleure: Add `gfni` acceleration of `S{l,r}i_V` Also added a fast-path for when the shift amount is greater than the size of the element. * ARMeilleure: Add `gfni` acceleration of `Shl_V` and `Sshr_V` * ARMeilleure: Increment InternalVersion * ARMeilleure: Fix Intrinsic and Assembler Table alignment `gf2p8affineqb` is the longest instruction name I know of. It shouldn't get any wider than this. * ARMeilleure: Remove SSE2+SHA requirement for GFNI * ARMeilleure Add `X86GetGf2p8LogicalShiftLeft` Used to generate GF(2^8) 8x8 bit-matrices for bit-shifting for the `gf2p8affineqb` instruction. * ARMeilleure: Append `FeatureInfo7Ecx` to `FeatureInfo` --- ARMeilleure/CodeGen/X86/AssemblerTable.cs | 437 +++++++++--------- .../CodeGen/X86/HardwareCapabilities.cs | 11 +- ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 341 +++++++------- ARMeilleure/CodeGen/X86/X86Instruction.cs | 1 + .../Instructions/InstEmitSimdHelper.cs | 15 + .../Instructions/InstEmitSimdLogical.cs | 43 +- ARMeilleure/Instructions/InstEmitSimdShift.cs | 136 +++++- .../IntermediateRepresentation/Intrinsic.cs | 1 + ARMeilleure/Optimizations.cs | 2 + ARMeilleure/Translation/PTC/Ptc.cs | 11 +- 10 files changed, 589 insertions(+), 409 deletions(-) diff --git a/ARMeilleure/CodeGen/X86/AssemblerTable.cs b/ARMeilleure/CodeGen/X86/AssemblerTable.cs index e40ffad48..ecdc029f9 100644 --- a/ARMeilleure/CodeGen/X86/AssemblerTable.cs +++ b/ARMeilleure/CodeGen/X86/AssemblerTable.cs @@ -62,224 +62,225 @@ namespace ARMeilleure.CodeGen.X86 _instTable = new InstructionInfo[(int)X86Instruction.Count]; // Name RM/R RM/I8 RM/I32 R/I64 R/RM Flags - Add(X86Instruction.Add, new InstructionInfo(0x00000001, 0x00000083, 0x00000081, BadOp, 0x00000003, InstructionFlags.None)); - Add(X86Instruction.Addpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Addps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex)); - Add(X86Instruction.Addsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Addss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Aesdec, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38de, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Aesdeclast, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38df, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Aesenc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38dc, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Aesenclast, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38dd, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Aesimc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38db, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.And, new InstructionInfo(0x00000021, 0x04000083, 0x04000081, BadOp, 0x00000023, InstructionFlags.None)); - Add(X86Instruction.Andnpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Andnps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex)); - Add(X86Instruction.Andpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Andps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex)); - Add(X86Instruction.Blendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3815, InstructionFlags.Prefix66)); - Add(X86Instruction.Blendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3814, InstructionFlags.Prefix66)); - Add(X86Instruction.Bsr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbd, InstructionFlags.None)); - Add(X86Instruction.Bswap, new InstructionInfo(0x00000fc8, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RegOnly)); - Add(X86Instruction.Call, new InstructionInfo(0x020000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Cmovcc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f40, InstructionFlags.None)); - Add(X86Instruction.Cmp, new InstructionInfo(0x00000039, 0x07000083, 0x07000081, BadOp, 0x0000003b, InstructionFlags.None)); - Add(X86Instruction.Cmppd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Cmpps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex)); - Add(X86Instruction.Cmpsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Cmpss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Cmpxchg, new InstructionInfo(0x00000fb1, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Cmpxchg16b, new InstructionInfo(0x01000fc7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RexW)); - Add(X86Instruction.Cmpxchg8, new InstructionInfo(0x00000fb0, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Reg8Src)); - Add(X86Instruction.Comisd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Comiss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstructionFlags.Vex)); - Add(X86Instruction.Crc32, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f1, InstructionFlags.PrefixF2)); - Add(X86Instruction.Crc32_16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f1, InstructionFlags.PrefixF2 | InstructionFlags.Prefix66)); - Add(X86Instruction.Crc32_8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f0, InstructionFlags.PrefixF2 | InstructionFlags.Reg8Src)); - Add(X86Instruction.Cvtdq2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe6, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Cvtdq2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex)); - Add(X86Instruction.Cvtpd2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe6, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Cvtpd2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Cvtps2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Cvtps2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex)); - Add(X86Instruction.Cvtsd2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Cvtsd2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Cvtsi2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Cvtsi2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Cvtss2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Cvtss2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Div, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x060000f7, InstructionFlags.None)); - Add(X86Instruction.Divpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Divps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex)); - Add(X86Instruction.Divsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Divss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Haddpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7c, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Haddps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7c, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Idiv, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x070000f7, InstructionFlags.None)); - Add(X86Instruction.Imul, new InstructionInfo(BadOp, 0x0000006b, 0x00000069, BadOp, 0x00000faf, InstructionFlags.None)); - Add(X86Instruction.Imul128, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x050000f7, InstructionFlags.None)); - Add(X86Instruction.Insertps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a21, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Jmp, new InstructionInfo(0x040000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Ldmxcsr, new InstructionInfo(0x02000fae, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex)); - Add(X86Instruction.Lea, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x0000008d, InstructionFlags.None)); - Add(X86Instruction.Maxpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Maxps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex)); - Add(X86Instruction.Maxsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Maxss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Minpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Minps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex)); - Add(X86Instruction.Minsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Minss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Mov, new InstructionInfo(0x00000089, BadOp, 0x000000c7, 0x000000b8, 0x0000008b, InstructionFlags.None)); - Add(X86Instruction.Mov16, new InstructionInfo(0x00000089, BadOp, 0x000000c7, BadOp, 0x0000008b, InstructionFlags.Prefix66)); - Add(X86Instruction.Mov8, new InstructionInfo(0x00000088, 0x000000c6, BadOp, BadOp, 0x0000008a, InstructionFlags.Reg8Src | InstructionFlags.Reg8Dest)); - Add(X86Instruction.Movd, new InstructionInfo(0x00000f7e, BadOp, BadOp, BadOp, 0x00000f6e, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Movdqu, new InstructionInfo(0x00000f7f, BadOp, BadOp, BadOp, 0x00000f6f, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Movhlps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f12, InstructionFlags.Vex)); - Add(X86Instruction.Movlhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f16, InstructionFlags.Vex)); - Add(X86Instruction.Movq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7e, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Movsd, new InstructionInfo(0x00000f11, BadOp, BadOp, BadOp, 0x00000f10, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Movss, new InstructionInfo(0x00000f11, BadOp, BadOp, BadOp, 0x00000f10, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Movsx16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbf, InstructionFlags.None)); - Add(X86Instruction.Movsx32, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000063, InstructionFlags.None)); - Add(X86Instruction.Movsx8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbe, InstructionFlags.Reg8Src)); - Add(X86Instruction.Movzx16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb7, InstructionFlags.None)); - Add(X86Instruction.Movzx8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb6, InstructionFlags.Reg8Src)); - Add(X86Instruction.Mul128, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x040000f7, InstructionFlags.None)); - Add(X86Instruction.Mulpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Mulps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex)); - Add(X86Instruction.Mulsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Mulss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Neg, new InstructionInfo(0x030000f7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Not, new InstructionInfo(0x020000f7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Or, new InstructionInfo(0x00000009, 0x01000083, 0x01000081, BadOp, 0x0000000b, InstructionFlags.None)); - Add(X86Instruction.Paddb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffc, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Paddd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffe, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Paddq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fd4, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Paddw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffd, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Palignr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0f, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pand, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fdb, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pandn, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fdf, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pavgb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe0, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pavgw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe3, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3810, InstructionFlags.Prefix66)); - Add(X86Instruction.Pclmulqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a44, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpeqb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f74, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpeqd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f76, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpeqq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3829, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpeqw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f75, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpgtb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f64, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpgtd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f66, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpgtq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3837, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pcmpgtw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f65, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pextrb, new InstructionInfo(0x000f3a14, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pextrd, new InstructionInfo(0x000f3a16, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pextrq, new InstructionInfo(0x000f3a16, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.RexW | InstructionFlags.Prefix66)); - Add(X86Instruction.Pextrw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc5, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pinsrb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a20, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pinsrd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a22, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pinsrq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a22, InstructionFlags.Vex | InstructionFlags.RexW | InstructionFlags.Prefix66)); - Add(X86Instruction.Pinsrw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc4, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmaxsb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383c, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmaxsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383d, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmaxsw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fee, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmaxub, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fde, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmaxud, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383f, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmaxuw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383e, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pminsb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3838, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pminsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3839, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pminsw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fea, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pminub, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fda, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pminud, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383b, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pminuw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383a, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmovsxbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3820, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmovsxdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3825, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmovsxwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3823, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmovzxbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3830, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmovzxdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3835, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmovzxwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3833, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmulld, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3840, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pmullw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fd5, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pop, new InstructionInfo(0x0000008f, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Popcnt, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb8, InstructionFlags.PrefixF3)); - Add(X86Instruction.Por, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000feb, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pshufb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3800, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pshufd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f70, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pslld, new InstructionInfo(BadOp, 0x06000f72, BadOp, BadOp, 0x00000ff2, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Pslldq, new InstructionInfo(BadOp, 0x07000f73, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psllq, new InstructionInfo(BadOp, 0x06000f73, BadOp, BadOp, 0x00000ff3, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psllw, new InstructionInfo(BadOp, 0x06000f71, BadOp, BadOp, 0x00000ff1, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psrad, new InstructionInfo(BadOp, 0x04000f72, BadOp, BadOp, 0x00000fe2, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psraw, new InstructionInfo(BadOp, 0x04000f71, BadOp, BadOp, 0x00000fe1, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psrld, new InstructionInfo(BadOp, 0x02000f72, BadOp, BadOp, 0x00000fd2, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psrlq, new InstructionInfo(BadOp, 0x02000f73, BadOp, BadOp, 0x00000fd3, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psrldq, new InstructionInfo(BadOp, 0x03000f73, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psrlw, new InstructionInfo(BadOp, 0x02000f71, BadOp, BadOp, 0x00000fd1, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psubb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ff8, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psubd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffa, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psubq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffb, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Psubw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ff9, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpckhbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f68, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpckhdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6a, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpckhqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6d, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpckhwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f69, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpcklbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f60, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpckldq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f62, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpcklqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6c, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Punpcklwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f61, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Push, new InstructionInfo(BadOp, 0x0000006a, 0x00000068, BadOp, 0x060000ff, InstructionFlags.None)); - Add(X86Instruction.Pxor, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fef, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Rcpps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f53, InstructionFlags.Vex)); - Add(X86Instruction.Rcpss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f53, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Ror, new InstructionInfo(0x010000d3, 0x010000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Roundpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a09, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Roundps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a08, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Roundsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0b, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Roundss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0a, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Rsqrtps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f52, InstructionFlags.Vex)); - Add(X86Instruction.Rsqrtss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f52, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Sar, new InstructionInfo(0x070000d3, 0x070000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Setcc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f90, InstructionFlags.Reg8Dest)); - Add(X86Instruction.Sha256Msg1, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cc, InstructionFlags.None)); - Add(X86Instruction.Sha256Msg2, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cd, InstructionFlags.None)); - Add(X86Instruction.Sha256Rnds2, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cb, InstructionFlags.None)); - Add(X86Instruction.Shl, new InstructionInfo(0x040000d3, 0x040000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Shr, new InstructionInfo(0x050000d3, 0x050000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Shufpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc6, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Shufps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc6, InstructionFlags.Vex)); - Add(X86Instruction.Sqrtpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Sqrtps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex)); - Add(X86Instruction.Sqrtsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Sqrtss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Stmxcsr, new InstructionInfo(0x03000fae, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex)); - Add(X86Instruction.Sub, new InstructionInfo(0x00000029, 0x05000083, 0x05000081, BadOp, 0x0000002b, InstructionFlags.None)); - Add(X86Instruction.Subpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Subps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex)); - Add(X86Instruction.Subsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.PrefixF2)); - Add(X86Instruction.Subss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.PrefixF3)); - Add(X86Instruction.Test, new InstructionInfo(0x00000085, BadOp, 0x000000f7, BadOp, BadOp, InstructionFlags.None)); - Add(X86Instruction.Unpckhpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Unpckhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex)); - Add(X86Instruction.Unpcklpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Unpcklps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex)); - Add(X86Instruction.Vblendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4b, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); - Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); - Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); - Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); - Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None)); - Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66)); - Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex)); + Add(X86Instruction.Add, new InstructionInfo(0x00000001, 0x00000083, 0x00000081, BadOp, 0x00000003, InstructionFlags.None)); + Add(X86Instruction.Addpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Addps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex)); + Add(X86Instruction.Addsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Addss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Aesdec, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38de, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Aesdeclast, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38df, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Aesenc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38dc, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Aesenclast, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38dd, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Aesimc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38db, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.And, new InstructionInfo(0x00000021, 0x04000083, 0x04000081, BadOp, 0x00000023, InstructionFlags.None)); + Add(X86Instruction.Andnpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Andnps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex)); + Add(X86Instruction.Andpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Andps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex)); + Add(X86Instruction.Blendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3815, InstructionFlags.Prefix66)); + Add(X86Instruction.Blendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3814, InstructionFlags.Prefix66)); + Add(X86Instruction.Bsr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbd, InstructionFlags.None)); + Add(X86Instruction.Bswap, new InstructionInfo(0x00000fc8, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RegOnly)); + Add(X86Instruction.Call, new InstructionInfo(0x020000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Cmovcc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f40, InstructionFlags.None)); + Add(X86Instruction.Cmp, new InstructionInfo(0x00000039, 0x07000083, 0x07000081, BadOp, 0x0000003b, InstructionFlags.None)); + Add(X86Instruction.Cmppd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Cmpps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex)); + Add(X86Instruction.Cmpsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Cmpss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Cmpxchg, new InstructionInfo(0x00000fb1, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Cmpxchg16b, new InstructionInfo(0x01000fc7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RexW)); + Add(X86Instruction.Cmpxchg8, new InstructionInfo(0x00000fb0, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Reg8Src)); + Add(X86Instruction.Comisd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Comiss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstructionFlags.Vex)); + Add(X86Instruction.Crc32, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f1, InstructionFlags.PrefixF2)); + Add(X86Instruction.Crc32_16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f1, InstructionFlags.PrefixF2 | InstructionFlags.Prefix66)); + Add(X86Instruction.Crc32_8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f0, InstructionFlags.PrefixF2 | InstructionFlags.Reg8Src)); + Add(X86Instruction.Cvtdq2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe6, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Cvtdq2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex)); + Add(X86Instruction.Cvtpd2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe6, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Cvtpd2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Cvtps2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Cvtps2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex)); + Add(X86Instruction.Cvtsd2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Cvtsd2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Cvtsi2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Cvtsi2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Cvtss2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Cvtss2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Div, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x060000f7, InstructionFlags.None)); + Add(X86Instruction.Divpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Divps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex)); + Add(X86Instruction.Divsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Divss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Gf2p8affineqb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3ace, InstructionFlags.Prefix66)); + Add(X86Instruction.Haddpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7c, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Haddps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7c, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Idiv, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x070000f7, InstructionFlags.None)); + Add(X86Instruction.Imul, new InstructionInfo(BadOp, 0x0000006b, 0x00000069, BadOp, 0x00000faf, InstructionFlags.None)); + Add(X86Instruction.Imul128, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x050000f7, InstructionFlags.None)); + Add(X86Instruction.Insertps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a21, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Jmp, new InstructionInfo(0x040000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Ldmxcsr, new InstructionInfo(0x02000fae, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex)); + Add(X86Instruction.Lea, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x0000008d, InstructionFlags.None)); + Add(X86Instruction.Maxpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Maxps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex)); + Add(X86Instruction.Maxsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Maxss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Minpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Minps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex)); + Add(X86Instruction.Minsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Minss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Mov, new InstructionInfo(0x00000089, BadOp, 0x000000c7, 0x000000b8, 0x0000008b, InstructionFlags.None)); + Add(X86Instruction.Mov16, new InstructionInfo(0x00000089, BadOp, 0x000000c7, BadOp, 0x0000008b, InstructionFlags.Prefix66)); + Add(X86Instruction.Mov8, new InstructionInfo(0x00000088, 0x000000c6, BadOp, BadOp, 0x0000008a, InstructionFlags.Reg8Src | InstructionFlags.Reg8Dest)); + Add(X86Instruction.Movd, new InstructionInfo(0x00000f7e, BadOp, BadOp, BadOp, 0x00000f6e, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Movdqu, new InstructionInfo(0x00000f7f, BadOp, BadOp, BadOp, 0x00000f6f, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Movhlps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f12, InstructionFlags.Vex)); + Add(X86Instruction.Movlhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f16, InstructionFlags.Vex)); + Add(X86Instruction.Movq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7e, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Movsd, new InstructionInfo(0x00000f11, BadOp, BadOp, BadOp, 0x00000f10, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Movss, new InstructionInfo(0x00000f11, BadOp, BadOp, BadOp, 0x00000f10, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Movsx16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbf, InstructionFlags.None)); + Add(X86Instruction.Movsx32, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000063, InstructionFlags.None)); + Add(X86Instruction.Movsx8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbe, InstructionFlags.Reg8Src)); + Add(X86Instruction.Movzx16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb7, InstructionFlags.None)); + Add(X86Instruction.Movzx8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb6, InstructionFlags.Reg8Src)); + Add(X86Instruction.Mul128, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x040000f7, InstructionFlags.None)); + Add(X86Instruction.Mulpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Mulps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex)); + Add(X86Instruction.Mulsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Mulss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Neg, new InstructionInfo(0x030000f7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Not, new InstructionInfo(0x020000f7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Or, new InstructionInfo(0x00000009, 0x01000083, 0x01000081, BadOp, 0x0000000b, InstructionFlags.None)); + Add(X86Instruction.Paddb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffc, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Paddd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffe, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Paddq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fd4, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Paddw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffd, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Palignr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0f, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pand, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fdb, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pandn, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fdf, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pavgb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe0, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pavgw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe3, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3810, InstructionFlags.Prefix66)); + Add(X86Instruction.Pclmulqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a44, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpeqb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f74, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpeqd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f76, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpeqq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3829, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpeqw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f75, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpgtb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f64, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpgtd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f66, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpgtq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3837, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pcmpgtw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f65, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pextrb, new InstructionInfo(0x000f3a14, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pextrd, new InstructionInfo(0x000f3a16, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pextrq, new InstructionInfo(0x000f3a16, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.RexW | InstructionFlags.Prefix66)); + Add(X86Instruction.Pextrw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc5, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pinsrb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a20, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pinsrd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a22, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pinsrq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a22, InstructionFlags.Vex | InstructionFlags.RexW | InstructionFlags.Prefix66)); + Add(X86Instruction.Pinsrw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc4, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmaxsb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383c, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmaxsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383d, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmaxsw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fee, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmaxub, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fde, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmaxud, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383f, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmaxuw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383e, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pminsb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3838, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pminsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3839, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pminsw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fea, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pminub, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fda, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pminud, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383b, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pminuw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383a, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmovsxbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3820, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmovsxdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3825, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmovsxwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3823, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmovzxbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3830, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmovzxdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3835, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmovzxwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3833, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmulld, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3840, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pmullw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fd5, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pop, new InstructionInfo(0x0000008f, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Popcnt, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb8, InstructionFlags.PrefixF3)); + Add(X86Instruction.Por, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000feb, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pshufb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3800, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pshufd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f70, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pslld, new InstructionInfo(BadOp, 0x06000f72, BadOp, BadOp, 0x00000ff2, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Pslldq, new InstructionInfo(BadOp, 0x07000f73, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psllq, new InstructionInfo(BadOp, 0x06000f73, BadOp, BadOp, 0x00000ff3, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psllw, new InstructionInfo(BadOp, 0x06000f71, BadOp, BadOp, 0x00000ff1, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psrad, new InstructionInfo(BadOp, 0x04000f72, BadOp, BadOp, 0x00000fe2, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psraw, new InstructionInfo(BadOp, 0x04000f71, BadOp, BadOp, 0x00000fe1, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psrld, new InstructionInfo(BadOp, 0x02000f72, BadOp, BadOp, 0x00000fd2, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psrlq, new InstructionInfo(BadOp, 0x02000f73, BadOp, BadOp, 0x00000fd3, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psrldq, new InstructionInfo(BadOp, 0x03000f73, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psrlw, new InstructionInfo(BadOp, 0x02000f71, BadOp, BadOp, 0x00000fd1, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psubb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ff8, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psubd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffa, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psubq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffb, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Psubw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ff9, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpckhbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f68, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpckhdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6a, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpckhqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6d, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpckhwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f69, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpcklbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f60, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpckldq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f62, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpcklqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6c, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Punpcklwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f61, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Push, new InstructionInfo(BadOp, 0x0000006a, 0x00000068, BadOp, 0x060000ff, InstructionFlags.None)); + Add(X86Instruction.Pxor, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fef, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Rcpps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f53, InstructionFlags.Vex)); + Add(X86Instruction.Rcpss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f53, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Ror, new InstructionInfo(0x010000d3, 0x010000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Roundpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a09, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Roundps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a08, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Roundsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0b, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Roundss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0a, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Rsqrtps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f52, InstructionFlags.Vex)); + Add(X86Instruction.Rsqrtss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f52, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Sar, new InstructionInfo(0x070000d3, 0x070000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Setcc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f90, InstructionFlags.Reg8Dest)); + Add(X86Instruction.Sha256Msg1, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cc, InstructionFlags.None)); + Add(X86Instruction.Sha256Msg2, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cd, InstructionFlags.None)); + Add(X86Instruction.Sha256Rnds2, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cb, InstructionFlags.None)); + Add(X86Instruction.Shl, new InstructionInfo(0x040000d3, 0x040000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Shr, new InstructionInfo(0x050000d3, 0x050000c1, BadOp, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Shufpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc6, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Shufps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc6, InstructionFlags.Vex)); + Add(X86Instruction.Sqrtpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Sqrtps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex)); + Add(X86Instruction.Sqrtsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Sqrtss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Stmxcsr, new InstructionInfo(0x03000fae, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex)); + Add(X86Instruction.Sub, new InstructionInfo(0x00000029, 0x05000083, 0x05000081, BadOp, 0x0000002b, InstructionFlags.None)); + Add(X86Instruction.Subpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Subps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex)); + Add(X86Instruction.Subsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Subss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.PrefixF3)); + Add(X86Instruction.Test, new InstructionInfo(0x00000085, BadOp, 0x000000f7, BadOp, BadOp, InstructionFlags.None)); + Add(X86Instruction.Unpckhpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Unpckhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex)); + Add(X86Instruction.Unpcklpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Unpcklps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex)); + Add(X86Instruction.Vblendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4b, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); + Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); + Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); + Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); + Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None)); + Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex)); static void Add(X86Instruction inst, in InstructionInfo info) { diff --git a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index a29dd5bef..c12a4e28b 100644 --- a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -20,8 +20,9 @@ namespace ARMeilleure.CodeGen.X86 if (maxNum >= 7) { - (_, int ebx7, _, _) = X86Base.CpuId(0x00000007, 0x00000000); + (_, int ebx7, int ecx7, _) = X86Base.CpuId(0x00000007, 0x00000000); FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7; + FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7; } } @@ -54,9 +55,16 @@ namespace ARMeilleure.CodeGen.X86 Sha = 1 << 29 } + [Flags] + public enum FeatureFlags7Ecx + { + Gfni = 1 << 8, + } + public static FeatureFlags1Edx FeatureInfo1Edx { get; } public static FeatureFlags1Ecx FeatureInfo1Ecx { get; } public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0; + public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0; public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse); public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2); @@ -72,6 +80,7 @@ namespace ARMeilleure.CodeGen.X86 public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx; public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c); public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha); + public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni); public static bool ForceLegacySse { get; set; } diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs index ada86cfae..6407a9a7b 100644 --- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs +++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs @@ -13,176 +13,177 @@ namespace ARMeilleure.CodeGen.X86 { _intrinTable = new IntrinsicInfo[EnumUtils.GetCount(typeof(Intrinsic))]; - Add(Intrinsic.X86Addpd, new IntrinsicInfo(X86Instruction.Addpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Addps, new IntrinsicInfo(X86Instruction.Addps, IntrinsicType.Binary)); - Add(Intrinsic.X86Addsd, new IntrinsicInfo(X86Instruction.Addsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Addss, new IntrinsicInfo(X86Instruction.Addss, IntrinsicType.Binary)); - Add(Intrinsic.X86Aesdec, new IntrinsicInfo(X86Instruction.Aesdec, IntrinsicType.Binary)); - Add(Intrinsic.X86Aesdeclast, new IntrinsicInfo(X86Instruction.Aesdeclast, IntrinsicType.Binary)); - Add(Intrinsic.X86Aesenc, new IntrinsicInfo(X86Instruction.Aesenc, IntrinsicType.Binary)); - Add(Intrinsic.X86Aesenclast, new IntrinsicInfo(X86Instruction.Aesenclast, IntrinsicType.Binary)); - Add(Intrinsic.X86Aesimc, new IntrinsicInfo(X86Instruction.Aesimc, IntrinsicType.Unary)); - Add(Intrinsic.X86Andnpd, new IntrinsicInfo(X86Instruction.Andnpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Andnps, new IntrinsicInfo(X86Instruction.Andnps, IntrinsicType.Binary)); - Add(Intrinsic.X86Andpd, new IntrinsicInfo(X86Instruction.Andpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Andps, new IntrinsicInfo(X86Instruction.Andps, IntrinsicType.Binary)); - Add(Intrinsic.X86Blendvpd, new IntrinsicInfo(X86Instruction.Blendvpd, IntrinsicType.Ternary)); - Add(Intrinsic.X86Blendvps, new IntrinsicInfo(X86Instruction.Blendvps, IntrinsicType.Ternary)); - Add(Intrinsic.X86Cmppd, new IntrinsicInfo(X86Instruction.Cmppd, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Cmpps, new IntrinsicInfo(X86Instruction.Cmpps, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Cmpsd, new IntrinsicInfo(X86Instruction.Cmpsd, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Cmpss, new IntrinsicInfo(X86Instruction.Cmpss, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Comisdeq, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_)); - Add(Intrinsic.X86Comisdge, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_)); - Add(Intrinsic.X86Comisdlt, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_)); - Add(Intrinsic.X86Comisseq, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_)); - Add(Intrinsic.X86Comissge, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_)); - Add(Intrinsic.X86Comisslt, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_)); - Add(Intrinsic.X86Crc32, new IntrinsicInfo(X86Instruction.Crc32, IntrinsicType.Crc32)); - Add(Intrinsic.X86Crc32_16, new IntrinsicInfo(X86Instruction.Crc32_16, IntrinsicType.Crc32)); - Add(Intrinsic.X86Crc32_8, new IntrinsicInfo(X86Instruction.Crc32_8, IntrinsicType.Crc32)); - Add(Intrinsic.X86Cvtdq2pd, new IntrinsicInfo(X86Instruction.Cvtdq2pd, IntrinsicType.Unary)); - Add(Intrinsic.X86Cvtdq2ps, new IntrinsicInfo(X86Instruction.Cvtdq2ps, IntrinsicType.Unary)); - Add(Intrinsic.X86Cvtpd2dq, new IntrinsicInfo(X86Instruction.Cvtpd2dq, IntrinsicType.Unary)); - Add(Intrinsic.X86Cvtpd2ps, new IntrinsicInfo(X86Instruction.Cvtpd2ps, IntrinsicType.Unary)); - Add(Intrinsic.X86Cvtps2dq, new IntrinsicInfo(X86Instruction.Cvtps2dq, IntrinsicType.Unary)); - Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary)); - Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr)); - Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary)); - Add(Intrinsic.X86Cvtsi2sd, new IntrinsicInfo(X86Instruction.Cvtsi2sd, IntrinsicType.BinaryGpr)); - Add(Intrinsic.X86Cvtsi2si, new IntrinsicInfo(X86Instruction.Movd, IntrinsicType.UnaryToGpr)); - Add(Intrinsic.X86Cvtsi2ss, new IntrinsicInfo(X86Instruction.Cvtsi2ss, IntrinsicType.BinaryGpr)); - Add(Intrinsic.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary)); - Add(Intrinsic.X86Cvtss2si, new IntrinsicInfo(X86Instruction.Cvtss2si, IntrinsicType.UnaryToGpr)); - Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary)); - Add(Intrinsic.X86Divsd, new IntrinsicInfo(X86Instruction.Divsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Divss, new IntrinsicInfo(X86Instruction.Divss, IntrinsicType.Binary)); - Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary)); - Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary)); - Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Maxss, new IntrinsicInfo(X86Instruction.Maxss, IntrinsicType.Binary)); - Add(Intrinsic.X86Minpd, new IntrinsicInfo(X86Instruction.Minpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Minps, new IntrinsicInfo(X86Instruction.Minps, IntrinsicType.Binary)); - Add(Intrinsic.X86Minsd, new IntrinsicInfo(X86Instruction.Minsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Minss, new IntrinsicInfo(X86Instruction.Minss, IntrinsicType.Binary)); - Add(Intrinsic.X86Movhlps, new IntrinsicInfo(X86Instruction.Movhlps, IntrinsicType.Binary)); - Add(Intrinsic.X86Movlhps, new IntrinsicInfo(X86Instruction.Movlhps, IntrinsicType.Binary)); - Add(Intrinsic.X86Movss, new IntrinsicInfo(X86Instruction.Movss, IntrinsicType.Binary)); - Add(Intrinsic.X86Mulpd, new IntrinsicInfo(X86Instruction.Mulpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary)); - Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary)); - Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits. - Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits. - Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary)); - Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary)); - Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary)); - Add(Intrinsic.X86Paddw, new IntrinsicInfo(X86Instruction.Paddw, IntrinsicType.Binary)); - Add(Intrinsic.X86Palignr, new IntrinsicInfo(X86Instruction.Palignr, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Pand, new IntrinsicInfo(X86Instruction.Pand, IntrinsicType.Binary)); - Add(Intrinsic.X86Pandn, new IntrinsicInfo(X86Instruction.Pandn, IntrinsicType.Binary)); - Add(Intrinsic.X86Pavgb, new IntrinsicInfo(X86Instruction.Pavgb, IntrinsicType.Binary)); - Add(Intrinsic.X86Pavgw, new IntrinsicInfo(X86Instruction.Pavgw, IntrinsicType.Binary)); - Add(Intrinsic.X86Pblendvb, new IntrinsicInfo(X86Instruction.Pblendvb, IntrinsicType.Ternary)); - Add(Intrinsic.X86Pclmulqdq, new IntrinsicInfo(X86Instruction.Pclmulqdq, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Pcmpeqb, new IntrinsicInfo(X86Instruction.Pcmpeqb, IntrinsicType.Binary)); - Add(Intrinsic.X86Pcmpeqd, new IntrinsicInfo(X86Instruction.Pcmpeqd, IntrinsicType.Binary)); - Add(Intrinsic.X86Pcmpeqq, new IntrinsicInfo(X86Instruction.Pcmpeqq, IntrinsicType.Binary)); - Add(Intrinsic.X86Pcmpeqw, new IntrinsicInfo(X86Instruction.Pcmpeqw, IntrinsicType.Binary)); - Add(Intrinsic.X86Pcmpgtb, new IntrinsicInfo(X86Instruction.Pcmpgtb, IntrinsicType.Binary)); - Add(Intrinsic.X86Pcmpgtd, new IntrinsicInfo(X86Instruction.Pcmpgtd, IntrinsicType.Binary)); - Add(Intrinsic.X86Pcmpgtq, new IntrinsicInfo(X86Instruction.Pcmpgtq, IntrinsicType.Binary)); - Add(Intrinsic.X86Pcmpgtw, new IntrinsicInfo(X86Instruction.Pcmpgtw, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmaxsb, new IntrinsicInfo(X86Instruction.Pmaxsb, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmaxsd, new IntrinsicInfo(X86Instruction.Pmaxsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmaxsw, new IntrinsicInfo(X86Instruction.Pmaxsw, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmaxub, new IntrinsicInfo(X86Instruction.Pmaxub, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmaxud, new IntrinsicInfo(X86Instruction.Pmaxud, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmaxuw, new IntrinsicInfo(X86Instruction.Pmaxuw, IntrinsicType.Binary)); - Add(Intrinsic.X86Pminsb, new IntrinsicInfo(X86Instruction.Pminsb, IntrinsicType.Binary)); - Add(Intrinsic.X86Pminsd, new IntrinsicInfo(X86Instruction.Pminsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Pminsw, new IntrinsicInfo(X86Instruction.Pminsw, IntrinsicType.Binary)); - Add(Intrinsic.X86Pminub, new IntrinsicInfo(X86Instruction.Pminub, IntrinsicType.Binary)); - Add(Intrinsic.X86Pminud, new IntrinsicInfo(X86Instruction.Pminud, IntrinsicType.Binary)); - Add(Intrinsic.X86Pminuw, new IntrinsicInfo(X86Instruction.Pminuw, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmovsxbw, new IntrinsicInfo(X86Instruction.Pmovsxbw, IntrinsicType.Unary)); - Add(Intrinsic.X86Pmovsxdq, new IntrinsicInfo(X86Instruction.Pmovsxdq, IntrinsicType.Unary)); - Add(Intrinsic.X86Pmovsxwd, new IntrinsicInfo(X86Instruction.Pmovsxwd, IntrinsicType.Unary)); - Add(Intrinsic.X86Pmovzxbw, new IntrinsicInfo(X86Instruction.Pmovzxbw, IntrinsicType.Unary)); - Add(Intrinsic.X86Pmovzxdq, new IntrinsicInfo(X86Instruction.Pmovzxdq, IntrinsicType.Unary)); - Add(Intrinsic.X86Pmovzxwd, new IntrinsicInfo(X86Instruction.Pmovzxwd, IntrinsicType.Unary)); - Add(Intrinsic.X86Pmulld, new IntrinsicInfo(X86Instruction.Pmulld, IntrinsicType.Binary)); - Add(Intrinsic.X86Pmullw, new IntrinsicInfo(X86Instruction.Pmullw, IntrinsicType.Binary)); - Add(Intrinsic.X86Popcnt, new IntrinsicInfo(X86Instruction.Popcnt, IntrinsicType.PopCount)); - Add(Intrinsic.X86Por, new IntrinsicInfo(X86Instruction.Por, IntrinsicType.Binary)); - Add(Intrinsic.X86Pshufb, new IntrinsicInfo(X86Instruction.Pshufb, IntrinsicType.Binary)); - Add(Intrinsic.X86Pshufd, new IntrinsicInfo(X86Instruction.Pshufd, IntrinsicType.BinaryImm)); - Add(Intrinsic.X86Pslld, new IntrinsicInfo(X86Instruction.Pslld, IntrinsicType.Binary)); - Add(Intrinsic.X86Pslldq, new IntrinsicInfo(X86Instruction.Pslldq, IntrinsicType.Binary)); - Add(Intrinsic.X86Psllq, new IntrinsicInfo(X86Instruction.Psllq, IntrinsicType.Binary)); - Add(Intrinsic.X86Psllw, new IntrinsicInfo(X86Instruction.Psllw, IntrinsicType.Binary)); - Add(Intrinsic.X86Psrad, new IntrinsicInfo(X86Instruction.Psrad, IntrinsicType.Binary)); - Add(Intrinsic.X86Psraw, new IntrinsicInfo(X86Instruction.Psraw, IntrinsicType.Binary)); - Add(Intrinsic.X86Psrld, new IntrinsicInfo(X86Instruction.Psrld, IntrinsicType.Binary)); - Add(Intrinsic.X86Psrlq, new IntrinsicInfo(X86Instruction.Psrlq, IntrinsicType.Binary)); - Add(Intrinsic.X86Psrldq, new IntrinsicInfo(X86Instruction.Psrldq, IntrinsicType.Binary)); - Add(Intrinsic.X86Psrlw, new IntrinsicInfo(X86Instruction.Psrlw, IntrinsicType.Binary)); - Add(Intrinsic.X86Psubb, new IntrinsicInfo(X86Instruction.Psubb, IntrinsicType.Binary)); - Add(Intrinsic.X86Psubd, new IntrinsicInfo(X86Instruction.Psubd, IntrinsicType.Binary)); - Add(Intrinsic.X86Psubq, new IntrinsicInfo(X86Instruction.Psubq, IntrinsicType.Binary)); - Add(Intrinsic.X86Psubw, new IntrinsicInfo(X86Instruction.Psubw, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpckhbw, new IntrinsicInfo(X86Instruction.Punpckhbw, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpckhdq, new IntrinsicInfo(X86Instruction.Punpckhdq, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpckhqdq, new IntrinsicInfo(X86Instruction.Punpckhqdq, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpckhwd, new IntrinsicInfo(X86Instruction.Punpckhwd, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpcklbw, new IntrinsicInfo(X86Instruction.Punpcklbw, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpckldq, new IntrinsicInfo(X86Instruction.Punpckldq, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpcklqdq, new IntrinsicInfo(X86Instruction.Punpcklqdq, IntrinsicType.Binary)); - Add(Intrinsic.X86Punpcklwd, new IntrinsicInfo(X86Instruction.Punpcklwd, IntrinsicType.Binary)); - Add(Intrinsic.X86Pxor, new IntrinsicInfo(X86Instruction.Pxor, IntrinsicType.Binary)); - Add(Intrinsic.X86Rcpps, new IntrinsicInfo(X86Instruction.Rcpps, IntrinsicType.Unary)); - Add(Intrinsic.X86Rcpss, new IntrinsicInfo(X86Instruction.Rcpss, IntrinsicType.Unary)); - Add(Intrinsic.X86Roundpd, new IntrinsicInfo(X86Instruction.Roundpd, IntrinsicType.BinaryImm)); - Add(Intrinsic.X86Roundps, new IntrinsicInfo(X86Instruction.Roundps, IntrinsicType.BinaryImm)); - Add(Intrinsic.X86Roundsd, new IntrinsicInfo(X86Instruction.Roundsd, IntrinsicType.BinaryImm)); - Add(Intrinsic.X86Roundss, new IntrinsicInfo(X86Instruction.Roundss, IntrinsicType.BinaryImm)); - Add(Intrinsic.X86Rsqrtps, new IntrinsicInfo(X86Instruction.Rsqrtps, IntrinsicType.Unary)); - Add(Intrinsic.X86Rsqrtss, new IntrinsicInfo(X86Instruction.Rsqrtss, IntrinsicType.Unary)); - Add(Intrinsic.X86Sha256Msg1, new IntrinsicInfo(X86Instruction.Sha256Msg1, IntrinsicType.Binary)); - Add(Intrinsic.X86Sha256Msg2, new IntrinsicInfo(X86Instruction.Sha256Msg2, IntrinsicType.Binary)); - Add(Intrinsic.X86Sha256Rnds2, new IntrinsicInfo(X86Instruction.Sha256Rnds2, IntrinsicType.Ternary)); - Add(Intrinsic.X86Shufpd, new IntrinsicInfo(X86Instruction.Shufpd, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Shufps, new IntrinsicInfo(X86Instruction.Shufps, IntrinsicType.TernaryImm)); - Add(Intrinsic.X86Sqrtpd, new IntrinsicInfo(X86Instruction.Sqrtpd, IntrinsicType.Unary)); - Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary)); - Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary)); - Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary)); - Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary)); - Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary)); - Add(Intrinsic.X86Subss, new IntrinsicInfo(X86Instruction.Subss, IntrinsicType.Binary)); - Add(Intrinsic.X86Unpckhpd, new IntrinsicInfo(X86Instruction.Unpckhpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Unpckhps, new IntrinsicInfo(X86Instruction.Unpckhps, IntrinsicType.Binary)); - Add(Intrinsic.X86Unpcklpd, new IntrinsicInfo(X86Instruction.Unpcklpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary)); - Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary)); - Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm)); - Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma)); - Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma)); - Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary)); - Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary)); + Add(Intrinsic.X86Addpd, new IntrinsicInfo(X86Instruction.Addpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Addps, new IntrinsicInfo(X86Instruction.Addps, IntrinsicType.Binary)); + Add(Intrinsic.X86Addsd, new IntrinsicInfo(X86Instruction.Addsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Addss, new IntrinsicInfo(X86Instruction.Addss, IntrinsicType.Binary)); + Add(Intrinsic.X86Aesdec, new IntrinsicInfo(X86Instruction.Aesdec, IntrinsicType.Binary)); + Add(Intrinsic.X86Aesdeclast, new IntrinsicInfo(X86Instruction.Aesdeclast, IntrinsicType.Binary)); + Add(Intrinsic.X86Aesenc, new IntrinsicInfo(X86Instruction.Aesenc, IntrinsicType.Binary)); + Add(Intrinsic.X86Aesenclast, new IntrinsicInfo(X86Instruction.Aesenclast, IntrinsicType.Binary)); + Add(Intrinsic.X86Aesimc, new IntrinsicInfo(X86Instruction.Aesimc, IntrinsicType.Unary)); + Add(Intrinsic.X86Andnpd, new IntrinsicInfo(X86Instruction.Andnpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Andnps, new IntrinsicInfo(X86Instruction.Andnps, IntrinsicType.Binary)); + Add(Intrinsic.X86Andpd, new IntrinsicInfo(X86Instruction.Andpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Andps, new IntrinsicInfo(X86Instruction.Andps, IntrinsicType.Binary)); + Add(Intrinsic.X86Blendvpd, new IntrinsicInfo(X86Instruction.Blendvpd, IntrinsicType.Ternary)); + Add(Intrinsic.X86Blendvps, new IntrinsicInfo(X86Instruction.Blendvps, IntrinsicType.Ternary)); + Add(Intrinsic.X86Cmppd, new IntrinsicInfo(X86Instruction.Cmppd, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Cmpps, new IntrinsicInfo(X86Instruction.Cmpps, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Cmpsd, new IntrinsicInfo(X86Instruction.Cmpsd, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Cmpss, new IntrinsicInfo(X86Instruction.Cmpss, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Comisdeq, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_)); + Add(Intrinsic.X86Comisdge, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_)); + Add(Intrinsic.X86Comisdlt, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_)); + Add(Intrinsic.X86Comisseq, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_)); + Add(Intrinsic.X86Comissge, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_)); + Add(Intrinsic.X86Comisslt, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_)); + Add(Intrinsic.X86Crc32, new IntrinsicInfo(X86Instruction.Crc32, IntrinsicType.Crc32)); + Add(Intrinsic.X86Crc32_16, new IntrinsicInfo(X86Instruction.Crc32_16, IntrinsicType.Crc32)); + Add(Intrinsic.X86Crc32_8, new IntrinsicInfo(X86Instruction.Crc32_8, IntrinsicType.Crc32)); + Add(Intrinsic.X86Cvtdq2pd, new IntrinsicInfo(X86Instruction.Cvtdq2pd, IntrinsicType.Unary)); + Add(Intrinsic.X86Cvtdq2ps, new IntrinsicInfo(X86Instruction.Cvtdq2ps, IntrinsicType.Unary)); + Add(Intrinsic.X86Cvtpd2dq, new IntrinsicInfo(X86Instruction.Cvtpd2dq, IntrinsicType.Unary)); + Add(Intrinsic.X86Cvtpd2ps, new IntrinsicInfo(X86Instruction.Cvtpd2ps, IntrinsicType.Unary)); + Add(Intrinsic.X86Cvtps2dq, new IntrinsicInfo(X86Instruction.Cvtps2dq, IntrinsicType.Unary)); + Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary)); + Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr)); + Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary)); + Add(Intrinsic.X86Cvtsi2sd, new IntrinsicInfo(X86Instruction.Cvtsi2sd, IntrinsicType.BinaryGpr)); + Add(Intrinsic.X86Cvtsi2si, new IntrinsicInfo(X86Instruction.Movd, IntrinsicType.UnaryToGpr)); + Add(Intrinsic.X86Cvtsi2ss, new IntrinsicInfo(X86Instruction.Cvtsi2ss, IntrinsicType.BinaryGpr)); + Add(Intrinsic.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary)); + Add(Intrinsic.X86Cvtss2si, new IntrinsicInfo(X86Instruction.Cvtss2si, IntrinsicType.UnaryToGpr)); + Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary)); + Add(Intrinsic.X86Divsd, new IntrinsicInfo(X86Instruction.Divsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Divss, new IntrinsicInfo(X86Instruction.Divss, IntrinsicType.Binary)); + Add(Intrinsic.X86Gf2p8affineqb, new IntrinsicInfo(X86Instruction.Gf2p8affineqb, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary)); + Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary)); + Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Maxss, new IntrinsicInfo(X86Instruction.Maxss, IntrinsicType.Binary)); + Add(Intrinsic.X86Minpd, new IntrinsicInfo(X86Instruction.Minpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Minps, new IntrinsicInfo(X86Instruction.Minps, IntrinsicType.Binary)); + Add(Intrinsic.X86Minsd, new IntrinsicInfo(X86Instruction.Minsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Minss, new IntrinsicInfo(X86Instruction.Minss, IntrinsicType.Binary)); + Add(Intrinsic.X86Movhlps, new IntrinsicInfo(X86Instruction.Movhlps, IntrinsicType.Binary)); + Add(Intrinsic.X86Movlhps, new IntrinsicInfo(X86Instruction.Movlhps, IntrinsicType.Binary)); + Add(Intrinsic.X86Movss, new IntrinsicInfo(X86Instruction.Movss, IntrinsicType.Binary)); + Add(Intrinsic.X86Mulpd, new IntrinsicInfo(X86Instruction.Mulpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary)); + Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary)); + Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits. + Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits. + Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary)); + Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary)); + Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary)); + Add(Intrinsic.X86Paddw, new IntrinsicInfo(X86Instruction.Paddw, IntrinsicType.Binary)); + Add(Intrinsic.X86Palignr, new IntrinsicInfo(X86Instruction.Palignr, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Pand, new IntrinsicInfo(X86Instruction.Pand, IntrinsicType.Binary)); + Add(Intrinsic.X86Pandn, new IntrinsicInfo(X86Instruction.Pandn, IntrinsicType.Binary)); + Add(Intrinsic.X86Pavgb, new IntrinsicInfo(X86Instruction.Pavgb, IntrinsicType.Binary)); + Add(Intrinsic.X86Pavgw, new IntrinsicInfo(X86Instruction.Pavgw, IntrinsicType.Binary)); + Add(Intrinsic.X86Pblendvb, new IntrinsicInfo(X86Instruction.Pblendvb, IntrinsicType.Ternary)); + Add(Intrinsic.X86Pclmulqdq, new IntrinsicInfo(X86Instruction.Pclmulqdq, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Pcmpeqb, new IntrinsicInfo(X86Instruction.Pcmpeqb, IntrinsicType.Binary)); + Add(Intrinsic.X86Pcmpeqd, new IntrinsicInfo(X86Instruction.Pcmpeqd, IntrinsicType.Binary)); + Add(Intrinsic.X86Pcmpeqq, new IntrinsicInfo(X86Instruction.Pcmpeqq, IntrinsicType.Binary)); + Add(Intrinsic.X86Pcmpeqw, new IntrinsicInfo(X86Instruction.Pcmpeqw, IntrinsicType.Binary)); + Add(Intrinsic.X86Pcmpgtb, new IntrinsicInfo(X86Instruction.Pcmpgtb, IntrinsicType.Binary)); + Add(Intrinsic.X86Pcmpgtd, new IntrinsicInfo(X86Instruction.Pcmpgtd, IntrinsicType.Binary)); + Add(Intrinsic.X86Pcmpgtq, new IntrinsicInfo(X86Instruction.Pcmpgtq, IntrinsicType.Binary)); + Add(Intrinsic.X86Pcmpgtw, new IntrinsicInfo(X86Instruction.Pcmpgtw, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmaxsb, new IntrinsicInfo(X86Instruction.Pmaxsb, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmaxsd, new IntrinsicInfo(X86Instruction.Pmaxsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmaxsw, new IntrinsicInfo(X86Instruction.Pmaxsw, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmaxub, new IntrinsicInfo(X86Instruction.Pmaxub, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmaxud, new IntrinsicInfo(X86Instruction.Pmaxud, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmaxuw, new IntrinsicInfo(X86Instruction.Pmaxuw, IntrinsicType.Binary)); + Add(Intrinsic.X86Pminsb, new IntrinsicInfo(X86Instruction.Pminsb, IntrinsicType.Binary)); + Add(Intrinsic.X86Pminsd, new IntrinsicInfo(X86Instruction.Pminsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Pminsw, new IntrinsicInfo(X86Instruction.Pminsw, IntrinsicType.Binary)); + Add(Intrinsic.X86Pminub, new IntrinsicInfo(X86Instruction.Pminub, IntrinsicType.Binary)); + Add(Intrinsic.X86Pminud, new IntrinsicInfo(X86Instruction.Pminud, IntrinsicType.Binary)); + Add(Intrinsic.X86Pminuw, new IntrinsicInfo(X86Instruction.Pminuw, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmovsxbw, new IntrinsicInfo(X86Instruction.Pmovsxbw, IntrinsicType.Unary)); + Add(Intrinsic.X86Pmovsxdq, new IntrinsicInfo(X86Instruction.Pmovsxdq, IntrinsicType.Unary)); + Add(Intrinsic.X86Pmovsxwd, new IntrinsicInfo(X86Instruction.Pmovsxwd, IntrinsicType.Unary)); + Add(Intrinsic.X86Pmovzxbw, new IntrinsicInfo(X86Instruction.Pmovzxbw, IntrinsicType.Unary)); + Add(Intrinsic.X86Pmovzxdq, new IntrinsicInfo(X86Instruction.Pmovzxdq, IntrinsicType.Unary)); + Add(Intrinsic.X86Pmovzxwd, new IntrinsicInfo(X86Instruction.Pmovzxwd, IntrinsicType.Unary)); + Add(Intrinsic.X86Pmulld, new IntrinsicInfo(X86Instruction.Pmulld, IntrinsicType.Binary)); + Add(Intrinsic.X86Pmullw, new IntrinsicInfo(X86Instruction.Pmullw, IntrinsicType.Binary)); + Add(Intrinsic.X86Popcnt, new IntrinsicInfo(X86Instruction.Popcnt, IntrinsicType.PopCount)); + Add(Intrinsic.X86Por, new IntrinsicInfo(X86Instruction.Por, IntrinsicType.Binary)); + Add(Intrinsic.X86Pshufb, new IntrinsicInfo(X86Instruction.Pshufb, IntrinsicType.Binary)); + Add(Intrinsic.X86Pshufd, new IntrinsicInfo(X86Instruction.Pshufd, IntrinsicType.BinaryImm)); + Add(Intrinsic.X86Pslld, new IntrinsicInfo(X86Instruction.Pslld, IntrinsicType.Binary)); + Add(Intrinsic.X86Pslldq, new IntrinsicInfo(X86Instruction.Pslldq, IntrinsicType.Binary)); + Add(Intrinsic.X86Psllq, new IntrinsicInfo(X86Instruction.Psllq, IntrinsicType.Binary)); + Add(Intrinsic.X86Psllw, new IntrinsicInfo(X86Instruction.Psllw, IntrinsicType.Binary)); + Add(Intrinsic.X86Psrad, new IntrinsicInfo(X86Instruction.Psrad, IntrinsicType.Binary)); + Add(Intrinsic.X86Psraw, new IntrinsicInfo(X86Instruction.Psraw, IntrinsicType.Binary)); + Add(Intrinsic.X86Psrld, new IntrinsicInfo(X86Instruction.Psrld, IntrinsicType.Binary)); + Add(Intrinsic.X86Psrlq, new IntrinsicInfo(X86Instruction.Psrlq, IntrinsicType.Binary)); + Add(Intrinsic.X86Psrldq, new IntrinsicInfo(X86Instruction.Psrldq, IntrinsicType.Binary)); + Add(Intrinsic.X86Psrlw, new IntrinsicInfo(X86Instruction.Psrlw, IntrinsicType.Binary)); + Add(Intrinsic.X86Psubb, new IntrinsicInfo(X86Instruction.Psubb, IntrinsicType.Binary)); + Add(Intrinsic.X86Psubd, new IntrinsicInfo(X86Instruction.Psubd, IntrinsicType.Binary)); + Add(Intrinsic.X86Psubq, new IntrinsicInfo(X86Instruction.Psubq, IntrinsicType.Binary)); + Add(Intrinsic.X86Psubw, new IntrinsicInfo(X86Instruction.Psubw, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpckhbw, new IntrinsicInfo(X86Instruction.Punpckhbw, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpckhdq, new IntrinsicInfo(X86Instruction.Punpckhdq, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpckhqdq, new IntrinsicInfo(X86Instruction.Punpckhqdq, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpckhwd, new IntrinsicInfo(X86Instruction.Punpckhwd, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpcklbw, new IntrinsicInfo(X86Instruction.Punpcklbw, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpckldq, new IntrinsicInfo(X86Instruction.Punpckldq, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpcklqdq, new IntrinsicInfo(X86Instruction.Punpcklqdq, IntrinsicType.Binary)); + Add(Intrinsic.X86Punpcklwd, new IntrinsicInfo(X86Instruction.Punpcklwd, IntrinsicType.Binary)); + Add(Intrinsic.X86Pxor, new IntrinsicInfo(X86Instruction.Pxor, IntrinsicType.Binary)); + Add(Intrinsic.X86Rcpps, new IntrinsicInfo(X86Instruction.Rcpps, IntrinsicType.Unary)); + Add(Intrinsic.X86Rcpss, new IntrinsicInfo(X86Instruction.Rcpss, IntrinsicType.Unary)); + Add(Intrinsic.X86Roundpd, new IntrinsicInfo(X86Instruction.Roundpd, IntrinsicType.BinaryImm)); + Add(Intrinsic.X86Roundps, new IntrinsicInfo(X86Instruction.Roundps, IntrinsicType.BinaryImm)); + Add(Intrinsic.X86Roundsd, new IntrinsicInfo(X86Instruction.Roundsd, IntrinsicType.BinaryImm)); + Add(Intrinsic.X86Roundss, new IntrinsicInfo(X86Instruction.Roundss, IntrinsicType.BinaryImm)); + Add(Intrinsic.X86Rsqrtps, new IntrinsicInfo(X86Instruction.Rsqrtps, IntrinsicType.Unary)); + Add(Intrinsic.X86Rsqrtss, new IntrinsicInfo(X86Instruction.Rsqrtss, IntrinsicType.Unary)); + Add(Intrinsic.X86Sha256Msg1, new IntrinsicInfo(X86Instruction.Sha256Msg1, IntrinsicType.Binary)); + Add(Intrinsic.X86Sha256Msg2, new IntrinsicInfo(X86Instruction.Sha256Msg2, IntrinsicType.Binary)); + Add(Intrinsic.X86Sha256Rnds2, new IntrinsicInfo(X86Instruction.Sha256Rnds2, IntrinsicType.Ternary)); + Add(Intrinsic.X86Shufpd, new IntrinsicInfo(X86Instruction.Shufpd, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Shufps, new IntrinsicInfo(X86Instruction.Shufps, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Sqrtpd, new IntrinsicInfo(X86Instruction.Sqrtpd, IntrinsicType.Unary)); + Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary)); + Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary)); + Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary)); + Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary)); + Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary)); + Add(Intrinsic.X86Subss, new IntrinsicInfo(X86Instruction.Subss, IntrinsicType.Binary)); + Add(Intrinsic.X86Unpckhpd, new IntrinsicInfo(X86Instruction.Unpckhpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Unpckhps, new IntrinsicInfo(X86Instruction.Unpckhps, IntrinsicType.Binary)); + Add(Intrinsic.X86Unpcklpd, new IntrinsicInfo(X86Instruction.Unpcklpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary)); + Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary)); + Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm)); + Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma)); + Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary)); + Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary)); } private static void Add(Intrinsic intrin, IntrinsicInfo info) diff --git a/ARMeilleure/CodeGen/X86/X86Instruction.cs b/ARMeilleure/CodeGen/X86/X86Instruction.cs index 37926a169..b024394e1 100644 --- a/ARMeilleure/CodeGen/X86/X86Instruction.cs +++ b/ARMeilleure/CodeGen/X86/X86Instruction.cs @@ -54,6 +54,7 @@ namespace ARMeilleure.CodeGen.X86 Divps, Divsd, Divss, + Gf2p8affineqb, Haddpd, Haddps, Idiv, diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs index 49c9e6879..49c17560b 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -243,6 +243,21 @@ namespace ARMeilleure.Instructions throw new ArgumentException($"Invalid rounding mode \"{roundMode}\"."); } + public static ulong X86GetGf2p8LogicalShiftLeft(int shift) + { + ulong identity = + (0b00000001UL << 56) | + (0b00000010UL << 48) | + (0b00000100UL << 40) | + (0b00001000UL << 32) | + (0b00010000UL << 24) | + (0b00100000UL << 16) | + (0b01000000UL << 8) | + (0b10000000UL << 0); + + return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8); + } + public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.). { Debug.Assert(op.Type == OperandType.I32 || op.Type == OperandType.I64); diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical.cs b/ARMeilleure/Instructions/InstEmitSimdLogical.cs index dbd1a1a00..624ae841d 100644 --- a/ARMeilleure/Instructions/InstEmitSimdLogical.cs +++ b/ARMeilleure/Instructions/InstEmitSimdLogical.cs @@ -336,20 +336,45 @@ namespace ARMeilleure.Instructions { OpCodeSimd op = (OpCodeSimd)context.CurrOp; - Operand res = context.VectorZero(); - - int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; - - for (int index = 0; index < elems; index++) + if (Optimizations.UseGfni) { - Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0); + const long bitMatrix = + (0b10000000L << 56) | + (0b01000000L << 48) | + (0b00100000L << 40) | + (0b00010000L << 32) | + (0b00001000L << 24) | + (0b00000100L << 16) | + (0b00000010L << 8) | + (0b00000001L << 0); - Operand de = EmitReverseBits8Op(context, ne); + Operand vBitMatrix = X86GetAllElements(context, bitMatrix); - res = EmitVectorInsert(context, res, de, index, 0); + Operand res = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, GetVec(op.Rn), vBitMatrix, Const(0)); + + if (op.RegisterSize == RegisterSize.Simd64) + { + res = context.VectorZeroUpper64(res); + } + + context.Copy(GetVec(op.Rd), res); } + else + { + Operand res = context.VectorZero(); + int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; - context.Copy(GetVec(op.Rd), res); + for (int index = 0; index < elems; index++) + { + Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0); + + Operand de = EmitReverseBits8Op(context, ne); + + res = EmitVectorInsert(context, res, de, index, 0); + } + + context.Copy(GetVec(op.Rd), res); + } } private static Operand EmitReverseBits8Op(ArmEmitterContext context, Operand op) diff --git a/ARMeilleure/Instructions/InstEmitSimdShift.cs b/ARMeilleure/Instructions/InstEmitSimdShift.cs index 146aeafa7..cf3b51bd6 100644 --- a/ARMeilleure/Instructions/InstEmitSimdShift.cs +++ b/ARMeilleure/Instructions/InstEmitSimdShift.cs @@ -88,8 +88,35 @@ namespace ARMeilleure.Instructions OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; int shift = GetImmShl(op); + int eSize = 8 << op.Size; - if (Optimizations.UseSse2 && op.Size > 0) + if (shift >= eSize) + { + if ((op.RegisterSize == RegisterSize.Simd64)) + { + Operand res = context.VectorZeroUpper64(GetVec(op.Rd)); + + context.Copy(GetVec(op.Rd), res); + } + } + else if (Optimizations.UseGfni && op.Size == 0) + { + Operand n = GetVec(op.Rn); + + ulong bitMatrix = X86GetGf2p8LogicalShiftLeft(shift); + + Operand vBitMatrix = X86GetElements(context, bitMatrix, bitMatrix); + + Operand res = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, n, vBitMatrix, Const(0)); + + if (op.RegisterSize == RegisterSize.Simd64) + { + res = context.VectorZeroUpper64(res); + } + + context.Copy(GetVec(op.Rd), res); + } + else if (Optimizations.UseSse2 && op.Size > 0) { Operand n = GetVec(op.Rn); @@ -396,10 +423,40 @@ namespace ARMeilleure.Instructions { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) - { - int shift = GetImmShr(op); + int shift = GetImmShr(op); + if (Optimizations.UseGfni && op.Size == 0) + { + Operand n = GetVec(op.Rn); + + ulong bitMatrix; + + if (shift < 8) + { + bitMatrix = X86GetGf2p8LogicalShiftLeft(-shift); + + // Extend sign-bit + bitMatrix |= 0x8080808080808080UL >> (64 - shift * 8); + } + else + { + // Replicate sign-bit into all bits + bitMatrix = 0x8080808080808080UL; + } + + Operand vBitMatrix = X86GetElements(context, bitMatrix, bitMatrix); + + Operand res = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, n, vBitMatrix, Const(0)); + + if (op.RegisterSize == RegisterSize.Simd64) + { + res = context.VectorZeroUpper64(res); + } + + context.Copy(GetVec(op.Rd), res); + } + else if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) + { Operand n = GetVec(op.Rn); Intrinsic sraInst = X86PsraInstruction[op.Size]; @@ -929,10 +986,44 @@ namespace ARMeilleure.Instructions OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; int shift = GetImmShl(op); + int eSize = 8 << op.Size; ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL; - if (Optimizations.UseSse2 && op.Size > 0) + if (shift >= eSize) + { + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + Operand res = context.VectorZeroUpper64(GetVec(op.Rd)); + + context.Copy(GetVec(op.Rd), res); + } + } + else if (Optimizations.UseGfni && op.Size == 0) + { + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + ulong bitMatrix = X86GetGf2p8LogicalShiftLeft(shift); + + Operand vBitMatrix = X86GetElements(context, bitMatrix, bitMatrix); + + Operand nShifted = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, n, vBitMatrix, Const(0)); + + Operand dMask = X86GetAllElements(context, (long)mask * _masks_SliSri[op.Size]); + + Operand dMasked = context.AddIntrinsic(Intrinsic.X86Pand, d, dMask); + + Operand res = context.AddIntrinsic(Intrinsic.X86Por, nShifted, dMasked); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + res = context.VectorZeroUpper64(res); + } + + context.Copy(d, res); + } + else if (Optimizations.UseSse2 && op.Size > 0) { Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); @@ -988,7 +1079,40 @@ namespace ARMeilleure.Instructions ulong mask = (ulong.MaxValue << (eSize - shift)) & (ulong.MaxValue >> (64 - eSize)); - if (Optimizations.UseSse2 && op.Size > 0) + if (shift >= eSize) + { + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + Operand res = context.VectorZeroUpper64(GetVec(op.Rd)); + + context.Copy(GetVec(op.Rd), res); + } + } + else if (Optimizations.UseGfni && op.Size == 0) + { + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + ulong bitMatrix = X86GetGf2p8LogicalShiftLeft(-shift); + + Operand vBitMatrix = X86GetElements(context, bitMatrix, bitMatrix); + + Operand nShifted = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, n, vBitMatrix, Const(0)); + + Operand dMask = X86GetAllElements(context, (long)mask * _masks_SliSri[op.Size]); + + Operand dMasked = context.AddIntrinsic(Intrinsic.X86Pand, d, dMask); + + Operand res = context.AddIntrinsic(Intrinsic.X86Por, nShifted, dMasked); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + res = context.VectorZeroUpper64(res); + } + + context.Copy(d, res); + } + else if (Optimizations.UseSse2 && op.Size > 0) { Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index 71bfc3bec..bc1285be2 100644 --- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -47,6 +47,7 @@ namespace ARMeilleure.IntermediateRepresentation X86Divps, X86Divsd, X86Divss, + X86Gf2p8affineqb, X86Haddpd, X86Haddps, X86Insertps, diff --git a/ARMeilleure/Optimizations.cs b/ARMeilleure/Optimizations.cs index 19193971c..97defd9a9 100644 --- a/ARMeilleure/Optimizations.cs +++ b/ARMeilleure/Optimizations.cs @@ -22,6 +22,7 @@ namespace ARMeilleure public static bool UseAesniIfAvailable { get; set; } = true; public static bool UsePclmulqdqIfAvailable { get; set; } = true; public static bool UseShaIfAvailable { get; set; } = true; + public static bool UseGfniIfAvailable { get; set; } = true; public static bool ForceLegacySse { @@ -42,5 +43,6 @@ namespace ARMeilleure internal static bool UseAesni => UseAesniIfAvailable && HardwareCapabilities.SupportsAesni; internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && HardwareCapabilities.SupportsPclmulqdq; internal static bool UseSha => UseShaIfAvailable && HardwareCapabilities.SupportsSha; + internal static bool UseGfni => UseGfniIfAvailable && HardwareCapabilities.SupportsGfni; } } \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index f4ae411b7..1515713be 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 3703; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 3710; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; @@ -951,7 +951,8 @@ namespace ARMeilleure.Translation.PTC return new FeatureInfo( (uint)HardwareCapabilities.FeatureInfo1Ecx, (uint)HardwareCapabilities.FeatureInfo1Edx, - (uint)HardwareCapabilities.FeatureInfo7Ebx); + (uint)HardwareCapabilities.FeatureInfo7Ebx, + (uint)HardwareCapabilities.FeatureInfo7Ecx); } private static byte GetMemoryManagerMode() @@ -971,7 +972,7 @@ namespace ARMeilleure.Translation.PTC return osPlatform; } - [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 54*/)] + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 58*/)] private struct OuterHeader { public ulong Magic; @@ -1002,8 +1003,8 @@ namespace ARMeilleure.Translation.PTC } } - [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 12*/)] - private record struct FeatureInfo(uint FeatureInfo0, uint FeatureInfo1, uint FeatureInfo2); + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 16*/)] + private record struct FeatureInfo(uint FeatureInfo0, uint FeatureInfo1, uint FeatureInfo2, uint FeatureInfo3); [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)] private struct InnerHeader From 358a7816393fab8c2c323f718c18d3610e91e681 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Sun, 2 Oct 2022 10:38:37 +0100 Subject: [PATCH 008/737] Volume Hotkeys (#3500) * Initial GTK implementation * Less messy and Avalonia imp * Move clamping to HLE and streamline imps * Make viewmodel update consistent * Fix rebase and add an english locale. Co-authored-by: Mary-nyan --- Ryujinx.Ava/AppHost.cs | 23 ++++++++++++++ Ryujinx.Ava/Assets/Locales/en_US.json | 2 ++ Ryujinx.Ava/Common/KeyboardHotkeyState.cs | 4 ++- Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml | 16 ++++++++++ .../Configuration/Hid/KeyboardHotkeys.cs | 2 ++ Ryujinx.HLE/Switch.cs | 2 +- .../Configuration/ConfigurationFileFormat.cs | 2 +- .../Configuration/ConfigurationState.cs | 22 +++++++++++++- Ryujinx/Ui/RendererWidgetBase.cs | 30 ++++++++++++++++++- 9 files changed, 98 insertions(+), 5 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 320efeb5e..d039ded79 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -58,6 +58,8 @@ namespace Ryujinx.Ava private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const int TargetFps = 60; + private const float VolumeDelta = 0.05f; + private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); private readonly long _ticksPerFrame; @@ -73,6 +75,7 @@ namespace Ryujinx.Ava private bool _isStopped; private bool _isActive; private long _lastCursorMoveTime; + private float _newVolume; private long _ticks = 0; private KeyboardHotkeyState _prevHotkeyState; @@ -1003,6 +1006,18 @@ namespace Ryujinx.Ava GraphicsConfig.ResScale = (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; break; + case KeyboardHotkeyState.VolumeUp: + _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); + Device.SetVolume(_newVolume); + + _parent.ViewModel.Volume = Device.GetVolume(); + break; + case KeyboardHotkeyState.VolumeDown: + _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); + Device.SetVolume(_newVolume); + + _parent.ViewModel.Volume = Device.GetVolume(); + break; case KeyboardHotkeyState.None: (_keyboardInterface as AvaloniaKeyboard).Clear(); break; @@ -1068,6 +1083,14 @@ namespace Ryujinx.Ava { state = KeyboardHotkeyState.ResScaleDown; } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) + { + state = KeyboardHotkeyState.VolumeUp; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) + { + state = KeyboardHotkeyState.VolumeDown; + } return state; } diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index a6641a028..95f663aa6 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -589,5 +589,7 @@ "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", "SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied", "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", + "SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:", + "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", "VolumeShort": "Vol" } diff --git a/Ryujinx.Ava/Common/KeyboardHotkeyState.cs b/Ryujinx.Ava/Common/KeyboardHotkeyState.cs index a4e9c5550..e85bdf341 100644 --- a/Ryujinx.Ava/Common/KeyboardHotkeyState.cs +++ b/Ryujinx.Ava/Common/KeyboardHotkeyState.cs @@ -9,6 +9,8 @@ Pause, ToggleMute, ResScaleUp, - ResScaleDown + ResScaleDown, + VolumeUp, + VolumeDown } } \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml index 43cef98cd..55fe83656 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml @@ -257,6 +257,22 @@ TextAlignment="Center" /> + + + + + + + + + + + + diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index a6f69e8d1..45217e02f 100644 --- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -9,5 +9,7 @@ public Key ToggleMute { get; set; } public Key ResScaleUp { get; set; } public Key ResScaleDown { get; set; } + public Key VolumeUp { get; set; } + public Key VolumeDown { get; set; } } } diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 4bd1fe397..de0f98e46 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -124,7 +124,7 @@ namespace Ryujinx.HLE public void SetVolume(float volume) { - System.SetVolume(volume); + System.SetVolume(Math.Clamp(volume, 0, 1)); } public float GetVolume() diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index fbac88908..f27116574 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 40; + public const int CurrentVersion = 41; /// /// Version of the configuration file format diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index b8c174da2..3dbbb3dd3 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -677,7 +677,9 @@ namespace Ryujinx.Ui.Common.Configuration ShowUi = Key.F4, Pause = Key.F5, ResScaleUp = Key.Unbound, - ResScaleDown = Key.Unbound + ResScaleDown = Key.Unbound, + VolumeUp = Key.Unbound, + VolumeDown = Key.Unbound }; Hid.InputConfig.Value = new List { @@ -1156,6 +1158,24 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 41) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 41."); + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, + Screenshot = configurationFileFormat.Hotkeys.Screenshot, + ShowUi = configurationFileFormat.Hotkeys.ShowUi, + Pause = configurationFileFormat.Hotkeys.Pause, + ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, + ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp, + ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown, + VolumeUp = Key.Unbound, + VolumeDown = Key.Unbound + }; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 6a728a26d..7e25ba2d9 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Ui private const int SwitchPanelHeight = 720; private const int TargetFps = 60; private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. + private const float VolumeDelta = 0.05f; public ManualResetEvent WaitEvent { get; set; } public NpadManager NpadManager { get; } @@ -57,6 +58,7 @@ namespace Ryujinx.Ui private readonly long _ticksPerFrame; private long _ticks = 0; + private float _newVolume; private readonly Stopwatch _chrono; @@ -643,6 +645,20 @@ namespace Ryujinx.Ui (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; } + if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp) && + !_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp)) + { + _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); + Device.SetVolume(_newVolume); + } + + if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown) && + !_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown)) + { + _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); + Device.SetVolume(_newVolume); + } + _prevHotkeyState = currentHotkeyState; } @@ -675,7 +691,9 @@ namespace Ryujinx.Ui Pause = 1 << 3, ToggleMute = 1 << 4, ResScaleUp = 1 << 5, - ResScaleDown = 1 << 6 + ResScaleDown = 1 << 6, + VolumeUp = 1 << 7, + VolumeDown = 1 << 8 } private KeyboardHotkeyState GetHotkeyState() @@ -717,6 +735,16 @@ namespace Ryujinx.Ui state |= KeyboardHotkeyState.ResScaleDown; } + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) + { + state |= KeyboardHotkeyState.VolumeUp; + } + + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) + { + state |= KeyboardHotkeyState.VolumeDown; + } + return state; } } From 81f848e54f4c66af82297b4c9d26cacf79ec10be Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 2 Oct 2022 18:50:03 -0300 Subject: [PATCH 009/737] Allow Surface Flinger frame enqueue after process has exited (#3733) --- Ryujinx.Graphics.Gpu/Window.cs | 7 +++-- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 27 +++++++++++-------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs index 8ad70c7f1..18320c74d 100644 --- a/Ryujinx.Graphics.Gpu/Window.cs +++ b/Ryujinx.Graphics.Gpu/Window.cs @@ -123,7 +123,8 @@ namespace Ryujinx.Graphics.Gpu /// Texture release callback /// User defined object passed to the release callback /// Thrown when is invalid - public void EnqueueFrameThreadSafe( + /// True if the frame was added to the queue, false otherwise + public bool EnqueueFrameThreadSafe( ulong pid, ulong address, int width, @@ -140,7 +141,7 @@ namespace Ryujinx.Graphics.Gpu { if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory)) { - throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid)); + return false; } FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4); @@ -184,6 +185,8 @@ namespace Ryujinx.Graphics.Gpu acquireCallback, releaseCallback, userObj)); + + return true; } /// diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index b24a94db6..c7cddf100 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -453,7 +453,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Item = item }; - _device.Gpu.Window.EnqueueFrameThreadSafe( + if (_device.Gpu.Window.EnqueueFrameThreadSafe( layer.Owner, frameBufferAddress, frameBufferWidth, @@ -466,20 +466,25 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger crop, AcquireBuffer, ReleaseBuffer, - textureCallbackInformation); - - if (item.Fence.FenceCount == 0) + textureCallbackInformation)) { - _device.Gpu.Window.SignalFrameReady(); - _device.Gpu.GPFifo.Interrupt(); - } - else - { - item.Fence.RegisterCallback(_device.Gpu, (x) => + if (item.Fence.FenceCount == 0) { _device.Gpu.Window.SignalFrameReady(); _device.Gpu.GPFifo.Interrupt(); - }); + } + else + { + item.Fence.RegisterCallback(_device.Gpu, (x) => + { + _device.Gpu.Window.SignalFrameReady(); + _device.Gpu.GPFifo.Interrupt(); + }); + } + } + else + { + ReleaseBuffer(textureCallbackInformation); } } From 1c3697b6a4127bb0ef953604af766c57ef15518a Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 2 Oct 2022 15:02:11 -0700 Subject: [PATCH 010/737] Update AboutWindow.axaml (#3724) --- Ryujinx.Ava/Ui/Windows/AboutWindow.axaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Ryujinx.Ava/Ui/Windows/AboutWindow.axaml b/Ryujinx.Ava/Ui/Windows/AboutWindow.axaml index 505b8004e..8f5e2d23d 100644 --- a/Ryujinx.Ava/Ui/Windows/AboutWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/AboutWindow.axaml @@ -6,7 +6,6 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" - Title="Ryujinx - About" Width="850" Height="550" MinWidth="500" From 7539e26144c71f3d73684bf721c9fee115a1a75b Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 3 Oct 2022 14:25:25 +0000 Subject: [PATCH 011/737] Avalonia - Fixes updater (#3670) * update avalonia * fix updater * fix spacing * addressed review * convert permission value to octal * Add missing comma * revert package updates --- Ryujinx.Ava/Assets/Locales/en_US.json | 5 +- Ryujinx.Ava/Modules/Updater/Updater.cs | 146 ++++++++++++------ Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml | 66 -------- Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml.cs | 73 --------- 4 files changed, 103 insertions(+), 187 deletions(-) delete mode 100644 Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml delete mode 100644 Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml.cs diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 95f663aa6..3a841157f 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -574,12 +574,12 @@ "Discard": "Discard", "UserProfilesSetProfileImage": "Set Profile Image", "UserProfileEmptyNameError": "Name is required", - "UserProfileNoImageError": "Profile image must be set", + "UserProfileNoImageError": "Profile image must be set", "GameUpdateWindowHeading": "Updates Available for {0} [{1}]", "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:", "UserProfilesName": "Name:", - "UserProfilesUserId" : "User Id:", + "UserProfilesUserId": "User Id:", "SettingsTabGraphicsBackend": "Graphics Backend", "SettingsTabGraphicsBackendTooltip": "Graphics Backend to use", "SettingsEnableTextureRecompression": "Enable Texture Recompression", @@ -589,6 +589,7 @@ "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", "SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied", "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", + "RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?", "SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:", "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", "VolumeShort": "Vol" diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index bbcf66790..d3c655b01 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -1,4 +1,6 @@ +using Avalonia.Controls; using Avalonia.Threading; +using FluentAvalonia.UI.Controls; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; @@ -11,11 +13,13 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.NetworkInformation; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -40,6 +44,8 @@ namespace Ryujinx.Modules private static readonly string[] WindowsDependencyDirs = Array.Empty(); + public static bool UpdateSuccessful { get; private set; } + public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) { if (Running) @@ -198,11 +204,18 @@ namespace Ryujinx.Modules _buildSize = -1; } } + Dispatcher.UIThread.Post(async () => { // Show a message asking the user if they want to update - UpdaterWindow updateDialog = new(mainWindow, newVersion, _buildUrl); - await updateDialog.ShowDialog(mainWindow); + var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], + LocaleManager.Instance["RyujinxUpdaterMessage"], + $"{Program.Version} -> {newVersion}"); + + if (shouldUpdate) + { + UpdateRyujinx(mainWindow, _buildUrl); + } }); } @@ -216,8 +229,10 @@ namespace Ryujinx.Modules return result; } - public static void UpdateRyujinx(UpdaterWindow updateDialog, string downloadUrl) + public static async void UpdateRyujinx(Window parent, string downloadUrl) { + UpdateSuccessful = false; + // Empty update dir, although it shouldn't ever have anything inside it if (Directory.Exists(UpdateDir)) { @@ -228,25 +243,56 @@ namespace Ryujinx.Modules string updateFile = Path.Combine(UpdateDir, "update.bin"); - // Download the update .zip - updateDialog.MainText.Text = LocaleManager.Instance["UpdaterDownloading"]; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.Maximum = 100; + var taskDialog = new TaskDialog() + { + Header = LocaleManager.Instance["RyujinxUpdater"], + SubHeader = LocaleManager.Instance["UpdaterDownloading"], + IconSource = new SymbolIconSource { Symbol = Symbol.Download }, + Buttons = { }, + ShowProgressBar = true + }; - Task.Run(() => + taskDialog.XamlRoot = parent; + + taskDialog.Opened += (s, e) => { if (_buildSize >= 0) { - DoUpdateWithMultipleThreads(updateDialog, downloadUrl, updateFile); + DoUpdateWithMultipleThreads(taskDialog, downloadUrl, updateFile); } else { - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); } - }); + }; + + await taskDialog.ShowAsync(true); + + if (UpdateSuccessful) + { + var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], + LocaleManager.Instance["DialogUpdaterCompleteMessage"], + LocaleManager.Instance["DialogUpdaterRestartMessage"]); + + if (shouldRestart) + { + string ryuName = Path.GetFileName(Environment.ProcessPath); + string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); + string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray()); + + if (!OperatingSystem.IsWindows()) + { + chmod(ryuExe, Convert.ToUInt32("0777", 8)); + } + + Process.Start(ryuExe, ryuArg); + + Environment.Exit(0); + } + } } - private static void DoUpdateWithMultipleThreads(UpdaterWindow updateDialog, string downloadUrl, string updateFile) + private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile) { // Multi-Threaded Updater long chunkSize = _buildSize / ConnectionCount; @@ -290,7 +336,7 @@ namespace Ryujinx.Modules Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; + taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal); }; client.DownloadDataCompleted += (_, args) => @@ -301,6 +347,8 @@ namespace Ryujinx.Modules { webClients[index].Dispose(); + taskDialog.Hide(); + return; } @@ -320,14 +368,14 @@ namespace Ryujinx.Modules try { - InstallUpdate(updateDialog, updateFile); + InstallUpdate(taskDialog, updateFile); } catch (Exception e) { Logger.Warning?.Print(LogClass.Application, e.Message); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); return; } @@ -348,7 +396,7 @@ namespace Ryujinx.Modules webClients[j].CancelAsync(); } - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); return; } @@ -356,7 +404,7 @@ namespace Ryujinx.Modules } } - private static void DoUpdateWithSingleThreadWorker(UpdaterWindow updateDialog, string downloadUrl, string updateFile) + private static void DoUpdateWithSingleThreadWorker(TaskDialog taskDialog, string downloadUrl, string updateFile) { using (HttpClient client = new HttpClient()) { @@ -384,19 +432,26 @@ namespace Ryujinx.Modules byteWritten += readSize; - updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; + taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal); + updateFileStream.Write(buffer, 0, readSize); } } } - InstallUpdate(updateDialog, updateFile); + InstallUpdate(taskDialog, updateFile); } } - private static void DoUpdateWithSingleThread(UpdaterWindow updateDialog, string downloadUrl, string updateFile) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double GetPercentage(double value, double max) { - Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)); + return max == 0 ? 0 : value / max * 100; + } + + private static void DoUpdateWithSingleThread(TaskDialog taskDialog, string downloadUrl, string updateFile) + { + Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile)); worker.Name = "Updater.SingleThreadWorker"; worker.Start(); } @@ -414,11 +469,11 @@ namespace Ryujinx.Modules } } - private static async void InstallUpdate(UpdaterWindow updateDialog, string updateFile) + private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) { // Extract Update - updateDialog.MainText.Text = LocaleManager.Instance["UpdaterExtracting"]; - updateDialog.ProgressBar.Value = 0; + taskDialog.SubHeader = LocaleManager.Instance["UpdaterExtracting"]; + taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); if (OperatingSystem.IsLinux()) { @@ -426,8 +481,6 @@ namespace Ryujinx.Modules using (Stream gzipStream = new GZipInputStream(inStream)) using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII)) { - updateDialog.ProgressBar.Maximum = inStream.Length; - await Task.Run(() => { TarEntry tarEntry; @@ -450,12 +503,12 @@ namespace Ryujinx.Modules Dispatcher.UIThread.Post(() => { - updateDialog.ProgressBar.Value += entry.Size; + taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal); }); } }); - updateDialog.ProgressBar.Value = inStream.Length; + taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal); } } else @@ -463,12 +516,12 @@ namespace Ryujinx.Modules using (Stream inStream = File.OpenRead(updateFile)) using (ZipFile zipFile = new ZipFile(inStream)) { - updateDialog.ProgressBar.Maximum = zipFile.Count; - await Task.Run(() => { + double count = 0; foreach (ZipEntry zipEntry in zipFile) { + count++; if (zipEntry.IsDirectory) continue; string outPath = Path.Combine(UpdateDir, zipEntry.Name); @@ -485,7 +538,7 @@ namespace Ryujinx.Modules Dispatcher.UIThread.Post(() => { - updateDialog.ProgressBar.Value++; + taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); }); } }); @@ -497,22 +550,23 @@ namespace Ryujinx.Modules List allFiles = EnumerateFilesToDelete().ToList(); - updateDialog.MainText.Text = LocaleManager.Instance["UpdaterRenaming"]; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.Maximum = allFiles.Count; + taskDialog.SubHeader = LocaleManager.Instance["UpdaterRenaming"]; + taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); // Replace old files await Task.Run(() => { + double count = 0; foreach (string file in allFiles) { + count++; try { File.Move(file, file + ".ryuold"); Dispatcher.UIThread.Post(() => { - updateDialog.ProgressBar.Value++; + taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal); }); } catch @@ -523,23 +577,20 @@ namespace Ryujinx.Modules Dispatcher.UIThread.Post(() => { - updateDialog.MainText.Text = LocaleManager.Instance["UpdaterAddingFiles"]; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.Maximum = Directory.GetFiles(UpdatePublishDir, "*", SearchOption.AllDirectories).Length; + taskDialog.SubHeader = LocaleManager.Instance["UpdaterAddingFiles"]; + taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); }); - MoveAllFilesOver(UpdatePublishDir, HomeDir, updateDialog); + MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog); }); Directory.Delete(UpdateDir, true); SetUnixPermissions(); - updateDialog.MainText.Text = LocaleManager.Instance["DialogUpdaterCompleteMessage"]; - updateDialog.SecondaryText.Text = LocaleManager.Instance["DialogUpdaterRestartMessage"]; + UpdateSuccessful = true; - updateDialog.ProgressBar.IsVisible = false; - updateDialog.ButtonBox.IsVisible = true; + taskDialog.Hide(); } #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed @@ -618,8 +669,9 @@ namespace Ryujinx.Modules return files; } - private static void MoveAllFilesOver(string root, string dest, UpdaterWindow dialog) + private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog) { + var total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length; foreach (string directory in Directory.GetDirectories(root)) { string dirName = Path.GetFileName(directory); @@ -629,16 +681,18 @@ namespace Ryujinx.Modules Directory.CreateDirectory(Path.Combine(dest, dirName)); } - MoveAllFilesOver(directory, Path.Combine(dest, dirName), dialog); + MoveAllFilesOver(directory, Path.Combine(dest, dirName), taskDialog); } + double count = 0; foreach (string file in Directory.GetFiles(root)) { + count++; File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true); Dispatcher.UIThread.InvokeAsync(() => { - dialog.ProgressBar.Value++; + taskDialog.SetProgressBarState(GetPercentage(count, total), TaskDialogProgressState.Normal); }); } } diff --git a/Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml b/Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml deleted file mode 100644 index 180ba52d9..000000000 --- a/Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml.cs deleted file mode 100644 index 526ca0bb7..000000000 --- a/Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Modules; -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; - -namespace Ryujinx.Ava.Ui.Windows -{ - public partial class UpdaterWindow : StyleableWindow - { - private readonly string _buildUrl; - private readonly MainWindow _mainWindow; - private readonly Version _newVersion; - private bool _restartQuery; - - public UpdaterWindow() - { - DataContext = this; - - InitializeComponent(); - - Title = LocaleManager.Instance["RyujinxUpdater"]; - } - - public UpdaterWindow(MainWindow mainWindow, Version newVersion, string buildUrl) : this() - { - _mainWindow = mainWindow; - _newVersion = newVersion; - _buildUrl = buildUrl; - } - - [DllImport("libc", SetLastError = true)] - private static extern int chmod(string path, uint mode); - - public void YesPressed() - { - if (_restartQuery) - { - string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.Ava.exe" : "Ryujinx.Ava"; - string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); - string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray()); - - if (!OperatingSystem.IsWindows()) - { - chmod(ryuExe, 0777); - } - - Process.Start(ryuExe, ryuArg); - - Environment.Exit(0); - } - else - { - ButtonBox.IsVisible = false; - ProgressBar.IsVisible = true; - - SecondaryText.Text = ""; - _restartQuery = true; - - Updater.UpdateRyujinx(this, _buildUrl); - } - } - - public void NoPressed() - { - _mainWindow.UpdateMenuItem.IsEnabled = true; - - Close(); - } - } -} \ No newline at end of file From 5437d6cb13a1487e4ab7cfd64c8b66ed7f9e8c81 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 3 Oct 2022 19:45:33 -0300 Subject: [PATCH 012/737] Vulkan: Fix buffer texture storage not being updated on buffer handle reuse (#3731) --- Ryujinx.Graphics.Vulkan/BufferManager.cs | 4 ++++ Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index 65883fd0d..e4820a303 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -38,6 +38,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly IdList _buffers; + public int BufferCount { get; private set; } + public StagingBuffer StagingBuffer { get; } public BufferManager(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device) @@ -56,6 +58,8 @@ namespace Ryujinx.Graphics.Vulkan return BufferHandle.Null; } + BufferCount++; + ulong handle64 = (uint)_buffers.Add(holder); return Unsafe.As(ref handle64); diff --git a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index 3e90a96f5..fca0598fe 100644 --- a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Vulkan private Auto _bufferView; private Dictionary> _selfManagedViews; + private int _bufferCount; + public int Width { get; } public int Height { get; } @@ -107,7 +109,8 @@ namespace Ryujinx.Graphics.Vulkan { if (_bufferHandle == buffer.Handle && _offset == buffer.Offset && - _size == buffer.Size) + _size == buffer.Size && + _bufferCount == _gd.BufferManager.BufferCount) { return; } @@ -115,6 +118,7 @@ namespace Ryujinx.Graphics.Vulkan _bufferHandle = buffer.Handle; _offset = buffer.Offset; _size = buffer.Size; + _bufferCount = _gd.BufferManager.BufferCount; ReleaseImpl(); } From a4fc9f8050405c8589793d93caa8f259d1618708 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 3 Oct 2022 20:08:38 -0300 Subject: [PATCH 013/737] Support use of buffer ranges with size 0 (#3736) --- Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs b/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs index 3242b9fc9..920501d3a 100644 --- a/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs +++ b/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs @@ -24,6 +24,11 @@ public void Add(int cbIndex, int offset, int size) { + if (size == 0) + { + return; + } + // Some usages can be out of bounds (vertex buffer on amd), so bound if necessary. if (offset + size > _size) { @@ -39,6 +44,11 @@ public bool OverlapsWith(int cbIndex, int offset, int size) { + if (size == 0) + { + return false; + } + int cbBase = cbIndex * _bitsPerCb; int start = cbBase + offset / _granularity; int end = cbBase + (offset + size - 1) / _granularity; From 2068445939e1b39f3e07f64aa11b93491d1116a7 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 3 Oct 2022 20:40:22 -0300 Subject: [PATCH 014/737] Fix shader SULD (bindless) instruction using wrong register as handle (#3732) * GLSL: Do not generate scale helpers if we have no textures * Fix shader SULD (bindless) instruction using wrong register as handle --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 2 +- Ryujinx.Graphics.Shader/Decoders/InstTable.cs | 14 +++++++------- .../Instructions/InstEmitSurface.cs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index e5efa0c52..edb05cc07 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3728; + private const uint CodeGenVersion = 3732; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index ff808b04b..91fd286d4 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements); - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling)) + if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0) { AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl"); context.AppendLine(); diff --git a/Ryujinx.Graphics.Shader/Decoders/InstTable.cs b/Ryujinx.Graphics.Shader/Decoders/InstTable.cs index eb3d6f3d2..0a64632ef 100644 --- a/Ryujinx.Graphics.Shader/Decoders/InstTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/InstTable.cs @@ -279,16 +279,16 @@ namespace Ryujinx.Graphics.Shader.Decoders Add("1110101110xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuatomB2, InstEmit.SuatomB2, InstProps.Rd | InstProps.Ra | InstProps.Rb | InstProps.Rc); Add("1110101011010xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuatomCasB, InstEmit.SuatomCasB, InstProps.Rd | InstProps.Ra | InstProps.Rb | InstProps.Rc | InstProps.SPd); Add("1110101x1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuatomCas, InstEmit.SuatomCas, InstProps.Rd | InstProps.Ra | InstProps.Rb | InstProps.SPd); - Add("111010110001xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuldDB, InstEmit.SuldDB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.SPd | InstProps.TexB); + Add("1110101100010xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuldDB, InstEmit.SuldDB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.SPd | InstProps.TexB); Add("1110101100011xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuldD, InstEmit.SuldD, InstProps.Rd | InstProps.Ra | InstProps.SPd | InstProps.Tex); - Add("11101011000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuldB, InstEmit.SuldB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.SPd | InstProps.TexB); - Add("11101011000x1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Suld, InstEmit.Suld, InstProps.Rd | InstProps.Ra | InstProps.SPd | InstProps.Tex); - Add("111010110101xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuredB, InstEmit.SuredB, InstProps.Rd | InstProps.Ra | InstProps.Rc); + Add("1110101100000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuldB, InstEmit.SuldB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.SPd | InstProps.TexB); + Add("1110101100001xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Suld, InstEmit.Suld, InstProps.Rd | InstProps.Ra | InstProps.SPd | InstProps.Tex); + Add("1110101101010xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SuredB, InstEmit.SuredB, InstProps.Rd | InstProps.Ra | InstProps.Rc); Add("1110101101011xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Sured, InstEmit.Sured, InstProps.Rd | InstProps.Ra); - Add("111010110011xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SustDB, InstEmit.SustDB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.TexB); + Add("1110101100110xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SustDB, InstEmit.SustDB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.TexB); Add("1110101100111xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SustD, InstEmit.SustD, InstProps.Rd | InstProps.Ra | InstProps.Tex); - Add("11101011001xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SustB, InstEmit.SustB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.TexB); - Add("11101011001x1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Sust, InstEmit.Sust, InstProps.Rd | InstProps.Ra | InstProps.Tex); + Add("1110101100100xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.SustB, InstEmit.SustB, InstProps.Rd | InstProps.Ra | InstProps.Rc | InstProps.TexB); + Add("1110101100101xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Sust, InstEmit.Sust, InstProps.Rd | InstProps.Ra | InstProps.Tex); Add("1111000011111xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Sync, InstEmit.Sync, InstProps.Bra); Add("11000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Tex, InstEmit.Tex, InstProps.Rd | InstProps.Ra | InstProps.Rb | InstProps.TPd | InstProps.Tex); Add("1101111010xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.TexB, InstEmit.TexB, InstProps.Rd | InstProps.Ra | InstProps.Rb | InstProps.TPd | InstProps.TexB); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index 6d59b6207..a81362b8b 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { InstSuldB op = context.GetOp(); - EmitSuld(context, op.CacheOp, op.Dim, 0, 0, op.Rgba, op.SrcA, op.Dest, 0, useComponents: true, false, isBindless: true); + EmitSuld(context, op.CacheOp, op.Dim, 0, 0, op.Rgba, op.SrcA, op.Dest, op.SrcC, useComponents: true, false, isBindless: true); } public static void Suld(EmitterContext context) From 60e16c15b6e9351371711356d205a12435b3c574 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 4 Oct 2022 20:12:54 -0300 Subject: [PATCH 015/737] Fix memory corruption in BCAT and FS Read methods when buffer is larger than needed (#3739) * Fix memory corruption in FS Read methods when buffer is larger than needed * PR feedback * nit: Don't move this around --- .../IDeliveryCacheDirectoryService.cs | 17 ++++++++--------- .../IDeliveryCacheFileService.cs | 17 ++++++++--------- .../IDeliveryCacheStorageService.cs | 17 ++++++++--------- .../Services/Fs/FileSystemProxy/IDirectory.cs | 14 +++++++------- .../HOS/Services/Fs/FileSystemProxy/IFile.cs | 16 ++++++++-------- .../Services/Fs/FileSystemProxy/IFileSystem.cs | 8 +------- .../Services/Fs/FileSystemProxy/IStorage.cs | 18 +++++++++--------- .../HOS/Services/Fs/IFileSystemProxy.cs | 14 +++++++------- .../HOS/Services/Fs/ISaveDataInfoReader.cs | 14 +++++++------- .../Services/Ssl/SslService/ISslConnection.cs | 13 ++++++------- 10 files changed, 69 insertions(+), 79 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs index 34176b4e3..e7ccb6d09 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs @@ -38,18 +38,17 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator // Read() -> (u32, buffer) public ResultCode Read(ServiceCtx context) { - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; - byte[] data = new byte[size]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast(region.Memory.Span)); - Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast(data)); + context.ResponseData.Write(entriesRead); - context.Memory.Write(position, data); - - context.ResponseData.Write(entriesRead); - - return (ResultCode)result.Value; + return (ResultCode)result.Value; + } } [CommandHipc(2)] diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs index b7b034476..c7706f921 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs @@ -38,20 +38,19 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator // Read(u64) -> (u64, buffer) public ResultCode Read(ServiceCtx context) { - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; long offset = context.RequestData.ReadInt64(); - byte[] data = new byte[size]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _base.Get.Read(out long bytesRead, offset, region.Memory.Span); - Result result = _base.Get.Read(out long bytesRead, offset, data); + context.ResponseData.Write(bytesRead); - context.Memory.Write(position, data); - - context.ResponseData.Write(bytesRead); - - return (ResultCode)result.Value; + return (ResultCode)result.Value; + } } [CommandHipc(2)] diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs index edb4b03aa..71d7aed70 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs @@ -50,18 +50,17 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator // EnumerateDeliveryCacheDirectory() -> (u32, buffer) public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context) { - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; - byte[] data = new byte[size]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast(region.Memory.Span)); - Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast(data)); + context.ResponseData.Write(count); - context.Memory.Write(position, data); - - context.ResponseData.Write(count); - - return (ResultCode)result.Value; + return (ResultCode)result.Value; + } } protected override void Dispose(bool isDisposing) diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs index b04e8675e..bfe13592a 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs @@ -17,17 +17,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy // Read() -> (u64 count, buffer entries) public ResultCode Read(ServiceCtx context) { - ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; ulong bufferLen = context.Request.ReceiveBuff[0].Size; - byte[] entryBuffer = new byte[bufferLen]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _baseDirectory.Get.Read(out long entriesRead, new OutBuffer(region.Memory.Span)); - Result result = _baseDirectory.Get.Read(out long entriesRead, new OutBuffer(entryBuffer)); + context.ResponseData.Write(entriesRead); - context.Memory.Write(bufferPosition, entryBuffer); - context.ResponseData.Write(entriesRead); - - return (ResultCode)result.Value; + return (ResultCode)result.Value; + } } [CommandHipc(1)] diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs index fa5e05d65..878fcacf6 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs @@ -19,7 +19,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy // Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer out_buf) public ResultCode Read(ServiceCtx context) { - ulong position = context.Request.ReceiveBuff[0].Position; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; ReadOption readOption = context.RequestData.ReadStruct(); context.RequestData.BaseStream.Position += 4; @@ -27,15 +28,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy long offset = context.RequestData.ReadInt64(); long size = context.RequestData.ReadInt64(); - byte[] data = new byte[context.Request.ReceiveBuff[0].Size]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _baseFile.Get.Read(out long bytesRead, offset, new OutBuffer(region.Memory.Span), size, readOption); - Result result = _baseFile.Get.Read(out long bytesRead, offset, new OutBuffer(data), size, readOption); + context.ResponseData.Write(bytesRead); - context.Memory.Write(position, data); - - context.ResponseData.Write(bytesRead); - - return (ResultCode)result.Value; + return (ResultCode)result.Value; + } } [CommandHipc(1)] diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs index a551d163f..d68ef3952 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs @@ -197,13 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy context.ResponseData.Write(timestamp.Created); context.ResponseData.Write(timestamp.Modified); context.ResponseData.Write(timestamp.Accessed); - - byte[] data = new byte[8]; - - // is valid? - data[0] = 1; - - context.ResponseData.Write(data); + context.ResponseData.Write(1L); // Is valid? return (ResultCode)result.Value; } diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs index 9dbfd2b0d..5359cadd7 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs @@ -23,21 +23,21 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy if (context.Request.ReceiveBuff.Count > 0) { - IpcBuffDesc buffDesc = context.Request.ReceiveBuff[0]; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; // Use smaller length to avoid overflows. - if (size > buffDesc.Size) + if (size > bufferLen) { - size = buffDesc.Size; + size = bufferLen; } - byte[] data = new byte[size]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size); - Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(data), (long)size); - - context.Memory.Write(buffDesc.Position, data); - - return (ResultCode)result.Value; + return (ResultCode)result.Value; + } } return ResultCode.Success; diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 01e1aa34d..c32750be1 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -500,16 +500,16 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); SaveDataFilter filter = context.RequestData.ReadStruct(); - ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; ulong bufferLen = context.Request.ReceiveBuff[0].Size; - byte[] infoBuffer = new byte[bufferLen]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _baseFileSystemProxy.Get.FindSaveDataWithFilter(out long count, new OutBuffer(region.Memory.Span), spaceId, in filter); + if (result.IsFailure()) return (ResultCode)result.Value; - Result result = _baseFileSystemProxy.Get.FindSaveDataWithFilter(out long count, new OutBuffer(infoBuffer), spaceId, in filter); - if (result.IsFailure()) return (ResultCode)result.Value; - - context.Memory.Write(bufferPosition, infoBuffer); - context.ResponseData.Write(count); + context.ResponseData.Write(count); + } return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs index bb664e55f..0bb4123f2 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs @@ -17,17 +17,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs // ReadSaveDataInfo() -> (u64, buffer) public ResultCode ReadSaveDataInfo(ServiceCtx context) { - ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; ulong bufferLen = context.Request.ReceiveBuff[0].Size; - byte[] infoBuffer = new byte[bufferLen]; + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _baseReader.Get.Read(out long readCount, new OutBuffer(region.Memory.Span)); - Result result = _baseReader.Get.Read(out long readCount, new OutBuffer(infoBuffer)); + context.ResponseData.Write(readCount); - context.Memory.Write(bufferPosition, infoBuffer); - context.ResponseData.Write(readCount); - - return (ResultCode)result.Value; + return (ResultCode)result.Value; + } } protected override void Dispose(bool isDisposing) diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs index 96ae84be7..ff6586734 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs @@ -142,14 +142,13 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService // GetHostName(buffer) -> u32 public ResultCode GetHostName(ServiceCtx context) { - ulong hostNameDataPosition = context.Request.ReceiveBuff[0].Position; - ulong hostNameDataSize = context.Request.ReceiveBuff[0].Size; + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; - byte[] hostNameData = new byte[hostNameDataSize]; - - Encoding.ASCII.GetBytes(_hostName, hostNameData); - - context.Memory.Write(hostNameDataPosition, hostNameData); + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Encoding.ASCII.GetBytes(_hostName, region.Memory.Span); + } context.ResponseData.Write((uint)_hostName.Length); From 599d485bffc9080d1c54acce929bbda0c7077499 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 5 Oct 2022 17:49:18 -0300 Subject: [PATCH 016/737] Change NvMap ID allocation to match nvservices (#3741) * Change NvMap ID allocation to match nvservices * Move NvMapIdDictionary to Types --- .../Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs | 24 ++------ .../NvMap/Types/NvMapIdDictionary.cs | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+), 20 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs index a8455bdb9..a52b36a22 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs @@ -3,7 +3,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Memory; using System; -using System.Collections.Concurrent; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { @@ -11,13 +10,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { private const int FlagNotFreedYet = 1; - private static ConcurrentDictionary _maps = new ConcurrentDictionary(); + private static NvMapIdDictionary _maps = new NvMapIdDictionary(); public NvMapDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { - IdDictionary dict = _maps.GetOrAdd(Owner, (key) => new IdDictionary()); - - dict.Add(0, new NvMapHandle()); } public override NvInternalResult Ioctl(NvIoctl command, Span arguments) @@ -232,19 +228,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap private int CreateHandleFromMap(NvMapHandle map) { - IdDictionary dict = _maps.GetOrAdd(Owner, (key) => new IdDictionary()); - - return dict.Add(map); + return _maps.Add(map); } private static bool DeleteMapWithHandle(ulong pid, int handle) { - if (_maps.TryGetValue(pid, out IdDictionary dict)) - { - return dict.Delete(handle) != null; - } - - return false; + return _maps.Delete(handle) != null; } public static void IncrementMapRefCount(ulong pid, int handle) @@ -277,12 +266,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap public static NvMapHandle GetMapFromHandle(ulong pid, int handle) { - if (_maps.TryGetValue(pid, out IdDictionary dict)) - { - return dict.GetData(handle); - } - - return null; + return _maps.Get(handle); } } } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs new file mode 100644 index 000000000..c4733e948 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap +{ + class NvMapIdDictionary + { + private readonly ConcurrentDictionary _nvmapHandles; + private int _id; + + public ICollection Values => _nvmapHandles.Values; + + public NvMapIdDictionary() + { + _nvmapHandles = new ConcurrentDictionary(); + } + + public int Add(NvMapHandle handle) + { + int id = Interlocked.Add(ref _id, 4); + + if (id != 0 && _nvmapHandles.TryAdd(id, handle)) + { + return id; + } + + throw new InvalidOperationException("NvMap ID overflow."); + } + + public NvMapHandle Get(int id) + { + if (_nvmapHandles.TryGetValue(id, out NvMapHandle handle)) + { + return handle; + } + + return null; + } + + public NvMapHandle Delete(int id) + { + if (_nvmapHandles.TryRemove(id, out NvMapHandle handle)) + { + return handle; + } + + return null; + } + + public ICollection Clear() + { + ICollection values = _nvmapHandles.Values; + + _nvmapHandles.Clear(); + + return values; + } + } +} \ No newline at end of file From 1ca0517c99af1914b887d6197b816c84537a9145 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 8 Oct 2022 15:28:27 +0100 Subject: [PATCH 017/737] Vulkan: Fix some issues with CacheByRange (#3743) * Fix some issues with CacheByRange - Cache now clears under more circumstances, the most important being the fast path write. - Cache supports partial clear which should help when more buffers join. - Fixed an issue with I8->I16 conversion where it wouldn't register the buffer for use on dispose. Should hopefully fix issues with https://github.com/Ryujinx/Ryujinx-Games-List/issues/4010 and maybe others. * Fix collection modified exception * Fix accidental use of parameterless constructor * Replay DynamicState when restoring from helper shader --- Ryujinx.Graphics.Vulkan/BufferHolder.cs | 34 +++++++++++++-- Ryujinx.Graphics.Vulkan/BufferManager.cs | 10 +++++ Ryujinx.Graphics.Vulkan/CacheByRange.cs | 48 +++++++++++++++++++++ Ryujinx.Graphics.Vulkan/PipelineBase.cs | 53 ++++++++++++++---------- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 2 + Ryujinx.Graphics.Vulkan/StagingBuffer.cs | 2 +- 6 files changed, 124 insertions(+), 25 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs index f449c1026..20571253f 100644 --- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -109,12 +109,34 @@ namespace Ryujinx.Graphics.Vulkan { if (isWrite) { - _cachedConvertedBuffers.Clear(); + SignalWrite(0, Size); } return _buffer; } + public Auto GetBuffer(CommandBuffer commandBuffer, int offset, int size, bool isWrite = false) + { + 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); + } + } + public BufferHandle GetHandle() { var handle = _bufferHandle; @@ -183,6 +205,8 @@ namespace Ryujinx.Graphics.Vulkan data.Slice(0, dataSize).CopyTo(new Span((void*)(_map + offset), dataSize)); + SignalWrite(offset, dataSize); + return; } } @@ -240,7 +264,7 @@ namespace Ryujinx.Graphics.Vulkan endRenderPass?.Invoke(); - var dstBuffer = GetBuffer(cbs.CommandBuffer, true).Get(cbs, dstOffset, data.Length).Value; + var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value; InsertBufferBarrier( _gd, @@ -364,7 +388,7 @@ namespace Ryujinx.Graphics.Vulkan public Auto GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size) { - var key = new I8ToI16CacheKey(); + var key = new I8ToI16CacheKey(_gd); if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) { @@ -373,6 +397,8 @@ namespace Ryujinx.Graphics.Vulkan _gd.PipelineInternal.EndRenderPass(); _gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size); + key.SetBuffer(holder.GetBuffer()); + _cachedConvertedBuffers.Add(offset, size, key, holder); } @@ -417,6 +443,8 @@ namespace Ryujinx.Graphics.Vulkan _gd.PipelineInternal.EndRenderPass(); _gd.HelperShader.ConvertIndexBuffer(_gd, cbs, this, holder, pattern, indexSize, offset, indexCount); + key.SetBuffer(holder.GetBuffer()); + _cachedConvertedBuffers.Add(offset, size, key, holder); } diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index e4820a303..57d672423 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -124,6 +124,16 @@ namespace Ryujinx.Graphics.Vulkan return null; } + public Auto GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, int offset, int size, bool isWrite) + { + if (TryGetBuffer(handle, out var holder)) + { + return holder.GetBuffer(commandBuffer, offset, size, isWrite); + } + + return null; + } + public Auto GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size) { if (TryGetBuffer(handle, out var holder)) diff --git a/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/Ryujinx.Graphics.Vulkan/CacheByRange.cs index 4c47e1c17..c77e66ae8 100644 --- a/Ryujinx.Graphics.Vulkan/CacheByRange.cs +++ b/Ryujinx.Graphics.Vulkan/CacheByRange.cs @@ -25,6 +25,11 @@ namespace Ryujinx.Graphics.Vulkan return other is I8ToI16CacheKey; } + public void SetBuffer(Auto buffer) + { + _buffer = buffer; + } + public void Dispose() { _gd.PipelineInternal.DirtyIndexBuffer(_buffer); @@ -160,6 +165,44 @@ namespace Ryujinx.Graphics.Vulkan } } + public 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) + { + entry.Key.Dispose(); + entry.Value.Dispose(); + } + + (toRemove ??= new List()).Add(range.Key); + } + } + + if (toRemove != null) + { + foreach (ulong range in toRemove) + { + _ranges.Remove(range); + } + } + } + } + private List GetEntries(int offset, int size) { if (_ranges == null) @@ -184,6 +227,11 @@ namespace Ryujinx.Graphics.Vulkan 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/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 1a284e209..0eb611237 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan protected readonly AutoFlushCounter AutoFlush; - private PipelineDynamicState _dynamicState; + protected PipelineDynamicState DynamicState; private PipelineState _newState; private bool _stateDirty; private GAL.PrimitiveTopology _topology; @@ -150,7 +150,7 @@ namespace Ryujinx.Graphics.Vulkan { EndRenderPass(); - var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, true).Get(Cbs, offset, size).Value; + var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, offset, size, true).Get(Cbs, offset, size).Value; BufferHolder.InsertBufferBarrier( Gd, @@ -238,8 +238,8 @@ namespace Ryujinx.Graphics.Vulkan { EndRenderPass(); - var src = Gd.BufferManager.GetBuffer(CommandBuffer, source, false); - var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, true); + var src = Gd.BufferManager.GetBuffer(CommandBuffer, source, srcOffset, size, false); + var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, dstOffset, size, true); BufferHolder.Copy(Gd, Cbs, src, dst, srcOffset, dstOffset, size); } @@ -388,7 +388,7 @@ namespace Ryujinx.Graphics.Vulkan var oldDepthTestEnable = _newState.DepthTestEnable; var oldDepthWriteEnable = _newState.DepthWriteEnable; var oldTopology = _newState.Topology; - var oldViewports = _dynamicState.Viewports; + var oldViewports = DynamicState.Viewports; var oldViewportsCount = _newState.ViewportsCount; _newState.CullMode = CullModeFlags.CullModeNone; @@ -411,9 +411,9 @@ namespace Ryujinx.Graphics.Vulkan _newState.DepthWriteEnable = oldDepthWriteEnable; _newState.Topology = oldTopology; - _dynamicState.Viewports = oldViewports; - _dynamicState.ViewportsCount = (int)oldViewportsCount; - _dynamicState.SetViewportsDirty(); + DynamicState.Viewports = oldViewports; + DynamicState.ViewportsCount = (int)oldViewportsCount; + DynamicState.SetViewportsDirty(); _newState.ViewportsCount = oldViewportsCount; SignalStateChange(); @@ -448,8 +448,13 @@ namespace Ryujinx.Graphics.Vulkan ResumeTransformFeedbackInternal(); DrawCount++; - var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, indirectBuffer.Handle, true).Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - var countBuffer = Gd.BufferManager.GetBuffer(CommandBuffer, parameterBuffer.Handle, true).Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, true) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + var countBuffer = Gd.BufferManager + .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true) + .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; Gd.DrawIndirectCountApi.CmdDrawIndirectCount( CommandBuffer, @@ -478,8 +483,13 @@ namespace Ryujinx.Graphics.Vulkan ResumeTransformFeedbackInternal(); DrawCount++; - var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, indirectBuffer.Handle, true).Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - var countBuffer = Gd.BufferManager.GetBuffer(CommandBuffer, parameterBuffer.Handle, true).Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + var countBuffer = Gd.BufferManager + .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true) + .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount( CommandBuffer, @@ -535,7 +545,7 @@ namespace Ryujinx.Graphics.Vulkan public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) { - _dynamicState.SetDepthBias(factor, units, clamp); + DynamicState.SetDepthBias(factor, units, clamp); _newState.DepthBiasEnable = enables != 0; SignalStateChange(); @@ -753,10 +763,10 @@ namespace Ryujinx.Graphics.Vulkan var offset = new Offset2D(region.X, region.Y); var extent = new Extent2D((uint)region.Width, (uint)region.Height); - _dynamicState.SetScissor(i, new Rect2D(offset, extent)); + DynamicState.SetScissor(i, new Rect2D(offset, extent)); } - _dynamicState.ScissorsCount = count; + DynamicState.ScissorsCount = count; _newState.ScissorsCount = (uint)count; SignalStateChange(); @@ -764,7 +774,7 @@ namespace Ryujinx.Graphics.Vulkan public void SetStencilTest(StencilTestDescriptor stencilTest) { - _dynamicState.SetStencilMasks( + DynamicState.SetStencilMasks( (uint)stencilTest.BackFuncMask, (uint)stencilTest.BackMask, (uint)stencilTest.BackFuncRef, @@ -813,7 +823,8 @@ namespace Ryujinx.Graphics.Vulkan if (range.Handle != BufferHandle.Null) { - _transformFeedbackBuffers[i] = new BufferState(Gd.BufferManager.GetBuffer(CommandBuffer, range.Handle, true), range.Offset, range.Size); + _transformFeedbackBuffers[i] = + new BufferState(Gd.BufferManager.GetBuffer(CommandBuffer, range.Handle, range.Offset, range.Size, true), range.Offset, range.Size); _transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i); } else @@ -975,7 +986,7 @@ namespace Ryujinx.Graphics.Vulkan { var viewport = viewports[i]; - _dynamicState.SetViewport(i, new Silk.NET.Vulkan.Viewport( + DynamicState.SetViewport(i, new Silk.NET.Vulkan.Viewport( viewport.Region.X, viewport.Region.Y, viewport.Region.Width == 0f ? 1f : viewport.Region.Width, @@ -984,7 +995,7 @@ namespace Ryujinx.Graphics.Vulkan Clamp(viewport.DepthFar))); } - _dynamicState.ViewportsCount = count; + DynamicState.ViewportsCount = count; float disableTransformF = disableTransform ? 1.0f : 0.0f; if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform) @@ -1063,7 +1074,7 @@ namespace Ryujinx.Graphics.Vulkan _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); _descriptorSetUpdater.SignalCommandBufferChange(); - _dynamicState.ForceAllDirty(); + DynamicState.ForceAllDirty(); _currentPipelineHandle = 0; } @@ -1201,7 +1212,7 @@ namespace Ryujinx.Graphics.Vulkan private void RecreatePipelineIfNeeded(PipelineBindPoint pbp) { - _dynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); + DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); // Commit changes to the support buffer before drawing. SupportBufferUpdater.Commit(); diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index c86929931..1ad9f3e6e 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -204,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan } SignalCommandBufferChange(); + + DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); } public void FlushCommandsImpl() diff --git a/Ryujinx.Graphics.Vulkan/StagingBuffer.cs b/Ryujinx.Graphics.Vulkan/StagingBuffer.cs index fe7a786b9..df353453f 100644 --- a/Ryujinx.Graphics.Vulkan/StagingBuffer.cs +++ b/Ryujinx.Graphics.Vulkan/StagingBuffer.cs @@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) { var srcBuffer = _buffer.GetBuffer(); - var dstBuffer = dst.GetBuffer(); + var dstBuffer = dst.GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true); int offset = _freeOffset; int capacity = BufferSize - offset; From bf77d1cab93467676156ebfbd5cf0ae057266e6f Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 8 Oct 2022 16:04:47 +0100 Subject: [PATCH 018/737] GPU: Pass SpanOrArray for Texture SetData to avoid copy (#3745) * GPU: Pass SpanOrArray for Texture SetData to avoid copy Texture data is often converted before upload, meaning that an array was allocated to perform the conversion into. However, the backend SetData methods were being passed a Span of that data, and the Multithreaded layer does `ToArray()` on it so that it can be stored for later! This method can't extract the original array, so it creates a copy. This PR changes the type passed for textures to a new ref struct called SpanOrArray, which is backed by either a ReadOnlySpan or an array. The benefit here is that we can have a ToArray method that doesn't copy if it is originally backed by an array. This will also avoid a copy when running the ASTC decoder. On NieR this was taking 38% of texture upload time, which it does a _lot_ of when you move between areas, so there should be a 1.6x performance boost when strictly uploading textures. No doubt this will also improve texture streaming performance in UE4 games, and maybe a small reduction with video playback. From the numbers, it's probably possible to improve the upload rate by a further 1.6x by performing layout conversion on GPU. I'm not sure if we could improve it further than that - multithreading conversion on CPU would probably result in memory bottleneck. This doesn't extend to buffers, since we don't convert their data on the GPU emulator side. * Remove implicit cast to array. --- Ryujinx.Common/Memory/SpanOrArray.cs | 89 +++++++++++++++++++ Ryujinx.Graphics.GAL/ITexture.cs | 7 +- .../Resources/ThreadedTexture.cs | 9 +- Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs | 2 +- Ryujinx.Graphics.Gpu/Image/Texture.cs | 39 ++++---- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 7 +- .../Image/TextureBuffer.cs | 11 ++- Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 27 +++--- Ryujinx.Graphics.Texture/LayoutConverter.cs | 11 +-- Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 9 +- Ryujinx.Graphics.Vulkan/TextureView.cs | 9 +- 11 files changed, 164 insertions(+), 56 deletions(-) create mode 100644 Ryujinx.Common/Memory/SpanOrArray.cs diff --git a/Ryujinx.Common/Memory/SpanOrArray.cs b/Ryujinx.Common/Memory/SpanOrArray.cs new file mode 100644 index 000000000..c1f066555 --- /dev/null +++ b/Ryujinx.Common/Memory/SpanOrArray.cs @@ -0,0 +1,89 @@ +using System; + +namespace Ryujinx.Common.Memory +{ + /// + /// A struct that can represent both a Span and Array. + /// This is useful to keep the Array representation when possible to avoid copies. + /// + /// Element Type + public ref struct SpanOrArray where T : unmanaged + { + public readonly T[] Array; + public readonly ReadOnlySpan Span; + + /// + /// Create a new SpanOrArray from an array. + /// + /// Array to store + public SpanOrArray(T[] array) + { + Array = array; + Span = ReadOnlySpan.Empty; + } + + /// + /// Create a new SpanOrArray from a readonly span. + /// + /// Span to store + public SpanOrArray(ReadOnlySpan span) + { + Array = null; + Span = span; + } + + /// + /// Return the contained array, or convert the span if necessary. + /// + /// An array containing the data + public T[] ToArray() + { + return Array ?? Span.ToArray(); + } + + /// + /// Return a ReadOnlySpan from either the array or ReadOnlySpan. + /// + /// A ReadOnlySpan containing the data + public ReadOnlySpan AsSpan() + { + return Array ?? Span; + } + + /// + /// Cast an array to a SpanOrArray. + /// + /// Source array + public static implicit operator SpanOrArray(T[] array) + { + return new SpanOrArray(array); + } + + /// + /// Cast a ReadOnlySpan to a SpanOrArray. + /// + /// Source ReadOnlySpan + public static implicit operator SpanOrArray(ReadOnlySpan span) + { + return new SpanOrArray(span); + } + + /// + /// Cast a Span to a SpanOrArray. + /// + /// Source Span + public static implicit operator SpanOrArray(Span span) + { + return new SpanOrArray(span); + } + + /// + /// Cast a SpanOrArray to a ReadOnlySpan + /// + /// Source SpanOrArray + public static implicit operator ReadOnlySpan(SpanOrArray spanOrArray) + { + return spanOrArray.AsSpan(); + } + } +} diff --git a/Ryujinx.Graphics.GAL/ITexture.cs b/Ryujinx.Graphics.GAL/ITexture.cs index 7f46806c7..4dc933036 100644 --- a/Ryujinx.Graphics.GAL/ITexture.cs +++ b/Ryujinx.Graphics.GAL/ITexture.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Memory; using System; namespace Ryujinx.Graphics.GAL @@ -17,9 +18,9 @@ namespace Ryujinx.Graphics.GAL ReadOnlySpan GetData(); ReadOnlySpan GetData(int layer, int level); - void SetData(ReadOnlySpan data); - void SetData(ReadOnlySpan data, int layer, int level); - void SetData(ReadOnlySpan data, int layer, int level, Rectangle region); + void SetData(SpanOrArray data); + void SetData(SpanOrArray data, int layer, int level); + void SetData(SpanOrArray data, int layer, int level, Rectangle region); void SetStorage(BufferRange buffer); void Release(); } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index 1e7d86ba3..1267ab797 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -1,4 +1,5 @@ -using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; using Ryujinx.Graphics.GAL.Multithreading.Model; using System; @@ -107,19 +108,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } } - public void SetData(ReadOnlySpan data) + public void SetData(SpanOrArray data) { _renderer.New().Set(Ref(this), Ref(data.ToArray())); _renderer.QueueCommand(); } - public void SetData(ReadOnlySpan data, int layer, int level) + public void SetData(SpanOrArray data, int layer, int level) { _renderer.New().Set(Ref(this), Ref(data.ToArray()), layer, level); _renderer.QueueCommand(); } - public void SetData(ReadOnlySpan data, int layer, int level, Rectangle region) + public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { _renderer.New().Set(Ref(this), Ref(data.ToArray()), layer, level, region); _renderer.QueueCommand(); diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index da25a89d8..aa94f1f88 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma if (target != null) { - ReadOnlySpan data; + byte[] data; if (srcLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 320bc0149..c104e860d 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1,5 +1,6 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; @@ -720,9 +721,9 @@ namespace Ryujinx.Graphics.Gpu.Image } } - data = ConvertToHostCompatibleFormat(data); + SpanOrArray result = ConvertToHostCompatibleFormat(data); - HostTexture.SetData(data); + HostTexture.SetData(result); _hasData = true; } @@ -731,7 +732,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Uploads new texture data to the host GPU. /// /// New data - public void SetData(ReadOnlySpan data) + public void SetData(SpanOrArray data) { BlacklistScale(); @@ -750,7 +751,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// New data /// Target layer /// Target level - public void SetData(ReadOnlySpan data, int layer, int level) + public void SetData(SpanOrArray data, int layer, int level) { BlacklistScale(); @@ -786,7 +787,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Mip level to convert /// True to convert a single slice /// Converted data - public ReadOnlySpan ConvertToHostCompatibleFormat(ReadOnlySpan data, int level = 0, bool single = false) + public SpanOrArray ConvertToHostCompatibleFormat(ReadOnlySpan data, int level = 0, bool single = false) { int width = Info.Width; int height = Info.Height; @@ -799,9 +800,11 @@ namespace Ryujinx.Graphics.Gpu.Image height = Math.Max(height >> level, 1); depth = Math.Max(depth >> level, 1); + SpanOrArray result; + if (Info.IsLinear) { - data = LayoutConverter.ConvertLinearStridedToLinear( + result = LayoutConverter.ConvertLinearStridedToLinear( width, height, Info.FormatInfo.BlockWidth, @@ -813,7 +816,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - data = LayoutConverter.ConvertBlockLinearToLinear( + result = LayoutConverter.ConvertBlockLinearToLinear( width, height, depth, @@ -836,7 +839,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc()) { if (!AstcDecoder.TryDecodeToRgba8P( - data.ToArray(), + result.ToArray(), Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, width, @@ -856,11 +859,11 @@ namespace Ryujinx.Graphics.Gpu.Image decoded = BCnEncoder.EncodeBC7(decoded, width, height, depth, levels, layers); } - data = decoded; + result = decoded; } else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm) { - data = PixelConverter.ConvertR4G4ToR4G4B4A4(data); + result = PixelConverter.ConvertR4G4ToR4G4B4A4(result); } else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities)) { @@ -868,36 +871,36 @@ namespace Ryujinx.Graphics.Gpu.Image { case Format.Bc1RgbaSrgb: case Format.Bc1RgbaUnorm: - data = BCnDecoder.DecodeBC1(data, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC1(result, width, height, depth, levels, layers); break; case Format.Bc2Srgb: case Format.Bc2Unorm: - data = BCnDecoder.DecodeBC2(data, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC2(result, width, height, depth, levels, layers); break; case Format.Bc3Srgb: case Format.Bc3Unorm: - data = BCnDecoder.DecodeBC3(data, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC3(result, width, height, depth, levels, layers); break; case Format.Bc4Snorm: case Format.Bc4Unorm: - data = BCnDecoder.DecodeBC4(data, width, height, depth, levels, layers, Format == Format.Bc4Snorm); + result = BCnDecoder.DecodeBC4(result, width, height, depth, levels, layers, Format == Format.Bc4Snorm); break; case Format.Bc5Snorm: case Format.Bc5Unorm: - data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Format == Format.Bc5Snorm); + result = BCnDecoder.DecodeBC5(result, width, height, depth, levels, layers, Format == Format.Bc5Snorm); break; case Format.Bc6HSfloat: case Format.Bc6HUfloat: - data = BCnDecoder.DecodeBC6(data, width, height, depth, levels, layers, Format == Format.Bc6HSfloat); + result = BCnDecoder.DecodeBC6(result, width, height, depth, levels, layers, Format == Format.Bc6HSfloat); break; case Format.Bc7Srgb: case Format.Bc7Unorm: - data = BCnDecoder.DecodeBC7(data, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC7(result, width, height, depth, levels, layers); break; } } - return data; + return result; } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 4bdc50788..9efd18028 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1,4 +1,5 @@ -using Ryujinx.Cpu.Tracking; +using Ryujinx.Common.Memory; +using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; @@ -348,9 +349,9 @@ namespace Ryujinx.Graphics.Gpu.Image ReadOnlySpan data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size)); - data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true); + SpanOrArray result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true); - Storage.SetData(data, info.BaseLayer, info.BaseLevel); + Storage.SetData(result, info.BaseLayer, info.BaseLevel); offsetIndex++; } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index e46d5c48e..76d0149b0 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -1,4 +1,5 @@ using OpenTK.Graphics.OpenGL; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using System; @@ -48,17 +49,19 @@ namespace Ryujinx.Graphics.OpenGL.Image return GetData(); } - public void SetData(ReadOnlySpan data) + public void SetData(SpanOrArray data) { - Buffer.SetData(_buffer, _bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize))); + var dataSpan = data.AsSpan(); + + Buffer.SetData(_buffer, _bufferOffset, dataSpan.Slice(0, Math.Min(dataSpan.Length, _bufferSize))); } - public void SetData(ReadOnlySpan data, int layer, int level) + public void SetData(SpanOrArray data, int layer, int level) { throw new NotSupportedException(); } - public void SetData(ReadOnlySpan data, int layer, int level, Rectangle region) + public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index f17243d2b..3e7da6e36 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -1,5 +1,6 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using System; @@ -317,32 +318,36 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(ReadOnlySpan data) + public void SetData(SpanOrArray data) { + var dataSpan = data.AsSpan(); + if (Format == Format.S8UintD24Unorm) { - data = FormatConverter.ConvertS8D24ToD24S8(data); + dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan); } unsafe { - fixed (byte* ptr = data) + fixed (byte* ptr = dataSpan) { - ReadFrom((IntPtr)ptr, data.Length); + ReadFrom((IntPtr)ptr, dataSpan.Length); } } } - public void SetData(ReadOnlySpan data, int layer, int level) + public void SetData(SpanOrArray data, int layer, int level) { + var dataSpan = data.AsSpan(); + if (Format == Format.S8UintD24Unorm) { - data = FormatConverter.ConvertS8D24ToD24S8(data); + dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan); } unsafe { - fixed (byte* ptr = data) + fixed (byte* ptr = dataSpan) { int width = Math.Max(Info.Width >> level, 1); int height = Math.Max(Info.Height >> level, 1); @@ -352,11 +357,13 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - public void SetData(ReadOnlySpan data, int layer, int level, Rectangle region) + public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { + var dataSpan = data.AsSpan(); + if (Format == Format.S8UintD24Unorm) { - data = FormatConverter.ConvertS8D24ToD24S8(data); + dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan); } int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth); @@ -364,7 +371,7 @@ namespace Ryujinx.Graphics.OpenGL.Image unsafe { - fixed (byte* ptr = data) + fixed (byte* ptr = dataSpan) { ReadFrom2D( (IntPtr)ptr, diff --git a/Ryujinx.Graphics.Texture/LayoutConverter.cs b/Ryujinx.Graphics.Texture/LayoutConverter.cs index 2b327375a..188ae0c15 100644 --- a/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Texture }; } - public static Span ConvertBlockLinearToLinear( + public static byte[] ConvertBlockLinearToLinear( int width, int height, int depth, @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Texture blockHeight, bytesPerPixel); - Span output = new byte[outSize]; + byte[] output = new byte[outSize]; int outOffs = 0; @@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static Span ConvertLinearStridedToLinear( + public static byte[] ConvertLinearStridedToLinear( int width, int height, int blockWidth, @@ -262,14 +262,15 @@ namespace Ryujinx.Graphics.Texture int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment); lineSize = Math.Min(lineSize, outStride); - Span output = new byte[h * outStride]; + byte[] output = new byte[h * outStride]; + Span outSpan = output; int outOffs = 0; int inOffs = 0; for (int y = 0; y < h; y++) { - data.Slice(inOffs, lineSize).CopyTo(output.Slice(outOffs, lineSize)); + data.Slice(inOffs, lineSize).CopyTo(outSpan.Slice(outOffs, lineSize)); inOffs += stride; outOffs += outStride; diff --git a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index fca0598fe..bf9a6eadd 100644 --- a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -1,4 +1,5 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; using System.Collections.Generic; @@ -90,17 +91,17 @@ namespace Ryujinx.Graphics.Vulkan _bufferView = null; } - public void SetData(ReadOnlySpan data) + public void SetData(SpanOrArray data) { _gd.SetBufferData(_bufferHandle, _offset, data); } - public void SetData(ReadOnlySpan data, int layer, int level) + public void SetData(SpanOrArray data, int layer, int level) { throw new NotSupportedException(); } - public void SetData(ReadOnlySpan data, int layer, int level, Rectangle region) + public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index fbe32eca9..129a77efa 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -1,4 +1,5 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; using System.Collections.Generic; @@ -873,17 +874,17 @@ namespace Ryujinx.Graphics.Vulkan return GetDataFromBuffer(result, size, result); } - public void SetData(ReadOnlySpan data) + public void SetData(SpanOrArray data) { SetData(data, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false); } - public void SetData(ReadOnlySpan data, int layer, int level) + public void SetData(SpanOrArray data, int layer, int level) { SetData(data, layer, level, 1, 1, singleSlice: true); } - public void SetData(ReadOnlySpan data, int layer, int level, Rectangle region) + public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { SetData(data, layer, level, 1, 1, singleSlice: true, region); } From 88a8d1e5674075040713bf71666498a4a94b4d29 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 9 Oct 2022 15:23:52 -0300 Subject: [PATCH 019/737] Fix disposed textures being updated on TextureBindingsManager (#3750) * Fix disposed textures being updated on TextureBindingsManager * PR feedback --- Ryujinx.Graphics.Gpu/Image/Texture.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index c104e860d..847cfbfe5 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -137,11 +137,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// public LinkedListNode CacheNode { get; set; } - /// - /// Event to fire when texture data is disposed. - /// - public event Action Disposed; - /// /// Physical memory ranges where the texture data is located. /// @@ -1448,7 +1443,6 @@ namespace Ryujinx.Graphics.Gpu.Image DisposeTextures(); HostTexture = hostTexture; - InvalidatedSequence++; } /// @@ -1603,6 +1597,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// private void DisposeTextures() { + InvalidatedSequence++; + _currentData = null; HostTexture.Release(); @@ -1637,8 +1633,6 @@ namespace Ryujinx.Graphics.Gpu.Image { DisposeTextures(); - Disposed?.Invoke(this); - if (Group.Storage == this) { Group.Dispose(); From 5af132706822d5aa3fc3942af2d5bd7abb185f2d Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 10 Oct 2022 03:35:44 -0300 Subject: [PATCH 020/737] Vulkan: Fix sampler custom border color (#3751) --- Ryujinx.Graphics.Vulkan/SamplerHolder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Ryujinx.Graphics.Vulkan/SamplerHolder.cs b/Ryujinx.Graphics.Vulkan/SamplerHolder.cs index a8f7c9444..a95e4dba2 100644 --- a/Ryujinx.Graphics.Vulkan/SamplerHolder.cs +++ b/Ryujinx.Graphics.Vulkan/SamplerHolder.cs @@ -64,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan }; samplerCreateInfo.PNext = &customBorderColor; + samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt; } gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError(); From e43390c7233af875133f23bfcfc6e4a211080070 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 15 Oct 2022 22:52:49 +0200 Subject: [PATCH 021/737] bsd: Check if socket is bound before calling RecvFrom() (#3761) --- .../HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs index 1b6ede86c..3db7c2223 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs @@ -235,6 +235,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd shouldBlockAfterOperation = true; } + if (!Socket.IsBound) + { + receiveSize = -1; + + return LinuxError.EOPNOTSUPP; + } + receiveSize = Socket.ReceiveFrom(buffer[..size], ConvertBsdSocketFlags(flags), ref temp); remoteEndPoint = (IPEndPoint)temp; @@ -519,4 +526,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } } } -} +} \ No newline at end of file From 2df16ded9ba06a874b58132cc2c78175631a3b8d Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 15 Oct 2022 20:20:16 -0300 Subject: [PATCH 022/737] Improve shader BRX instruction code generation (#3759) * Improve shader BRX instruction code generation * Shader cache version bump, add some comments and asserts --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 13 ++- .../Instructions/InstEmitFlowControl.cs | 82 ++++++++++++++++--- .../Translation/EmitterContext.cs | 65 +++++++++++++-- .../Translation/Translator.cs | 2 +- 5 files changed, 140 insertions(+), 24 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index edb05cc07..2168c1859 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3732; + private const uint CodeGenVersion = 3759; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 1c329b593..9dafb089f 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -377,6 +377,8 @@ namespace Ryujinx.Graphics.Shader.Decoders if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0)) { + HashSet visited = new HashSet(); + InstBrx opBrx = new InstBrx(lastOp.RawOpCode); ulong baseOffset = lastOp.GetAbsoluteAddress(); @@ -392,9 +394,14 @@ namespace Ryujinx.Graphics.Shader.Decoders for (int i = 0; i < cbOffsetsCount; i++) { uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4); - Block target = getBlock(baseOffset + targetOffset); - target.Predecessors.Add(block); - block.Successors.Add(target); + ulong targetAddress = baseOffset + targetOffset; + + if (visited.Add(targetAddress)) + { + Block target = getBlock(targetAddress); + target.Predecessors.Add(block); + block.Successors.Add(target); + } } } } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs index aee52c517..f1dd279c8 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs @@ -41,24 +41,82 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand address = context.IAdd(Register(op.SrcA, RegisterType.Gpr), Const(offset)); - // Sorting the target addresses in descending order improves the code, - // since it will always check the most distant targets first, then the - // near ones. This can be easily transformed into if/else statements. - var sortedTargets = context.CurrBlock.Successors.Skip(startIndex).OrderByDescending(x => x.Address); + var targets = context.CurrBlock.Successors.Skip(startIndex); - Block lastTarget = sortedTargets.LastOrDefault(); + bool allTargetsSinglePred = true; + int total = context.CurrBlock.Successors.Count - startIndex; + int count = 0; - foreach (Block possibleTarget in sortedTargets) + foreach (var target in targets.OrderBy(x => x.Address)) { - Operand label = context.GetLabel(possibleTarget.Address); - - if (possibleTarget != lastTarget) + if (++count < total && (target.Predecessors.Count > 1 || target.Address <= context.CurrBlock.Address)) { - context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)possibleTarget.Address))); + allTargetsSinglePred = false; + break; } - else + } + + if (allTargetsSinglePred) + { + // Chain blocks, each target block will check if the BRX target address + // matches its own address, if not, it jumps to the next target which will do the same check, + // until it reaches the last possible target, which executed unconditionally. + // We can only do this if the BRX block is the only predecessor of all target blocks. + // Additionally, this is not supported for blocks located before the current block, + // since it will be too late to insert a label, but this is something that can be improved + // in the future if necessary. + + var sortedTargets = targets.OrderBy(x => x.Address); + + Block currentTarget = null; + ulong firstTargetAddress = 0; + + foreach (Block nextTarget in sortedTargets) { - context.Branch(label); + if (currentTarget != null) + { + if (currentTarget.Address != nextTarget.Address) + { + context.SetBrxTarget(currentTarget.Address, address, (int)currentTarget.Address, nextTarget.Address); + } + } + else + { + firstTargetAddress = nextTarget.Address; + } + + currentTarget = nextTarget; + } + + context.Branch(context.GetLabel(firstTargetAddress)); + } + else + { + // Emit the branches sequentially. + // This generates slightly worse code, but should work for all cases. + + var sortedTargets = targets.OrderByDescending(x => x.Address); + ulong lastTargetAddress = ulong.MaxValue; + + count = 0; + + foreach (Block target in sortedTargets) + { + Operand label = context.GetLabel(target.Address); + + if (++count < total) + { + if (target.Address != lastTargetAddress) + { + context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)target.Address))); + } + + lastTargetAddress = target.Address; + } + else + { + context.Branch(label); + } } } } diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 3e50ce2fd..ef5d7b96d 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -21,8 +21,33 @@ namespace Ryujinx.Graphics.Shader.Translation public int OperationsCount => _operations.Count; + private struct BrxTarget + { + public readonly Operand Selector; + public readonly int ExpectedValue; + public readonly ulong NextTargetAddress; + + public BrxTarget(Operand selector, int expectedValue, ulong nextTargetAddress) + { + Selector = selector; + ExpectedValue = expectedValue; + NextTargetAddress = nextTargetAddress; + } + } + + private class BlockLabel + { + public readonly Operand Label; + public BrxTarget BrxTarget; + + public BlockLabel(Operand label) + { + Label = label; + } + } + private readonly List _operations; - private readonly Dictionary _labels; + private readonly Dictionary _labels; public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) { @@ -30,7 +55,7 @@ namespace Ryujinx.Graphics.Shader.Translation Config = config; IsNonMain = isNonMain; _operations = new List(); - _labels = new Dictionary(); + _labels = new Dictionary(); EmitStart(); } @@ -158,14 +183,40 @@ namespace Ryujinx.Graphics.Shader.Translation public Operand GetLabel(ulong address) { - if (!_labels.TryGetValue(address, out Operand label)) - { - label = Label(); + return EnsureBlockLabel(address).Label; + } - _labels.Add(address, label); + public void SetBrxTarget(ulong address, Operand selector, int targetValue, ulong nextTargetAddress) + { + BlockLabel blockLabel = EnsureBlockLabel(address); + Debug.Assert(blockLabel.BrxTarget.Selector == null); + blockLabel.BrxTarget = new BrxTarget(selector, targetValue, nextTargetAddress); + } + + public void EnterBlock(ulong address) + { + BlockLabel blockLabel = EnsureBlockLabel(address); + + MarkLabel(blockLabel.Label); + + BrxTarget brxTarget = blockLabel.BrxTarget; + + if (brxTarget.Selector != null) + { + this.BranchIfFalse(GetLabel(brxTarget.NextTargetAddress), this.ICompareEqual(brxTarget.Selector, Const(brxTarget.ExpectedValue))); + } + } + + private BlockLabel EnsureBlockLabel(ulong address) + { + if (!_labels.TryGetValue(address, out BlockLabel blockLabel)) + { + blockLabel = new BlockLabel(Label()); + + _labels.Add(address, blockLabel); } - return label; + return blockLabel; } public void PrepareForVertexReturn() diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 78fd9498a..ff0de1bd0 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.Translation { context.CurrBlock = block; - context.MarkLabel(context.GetLabel(block.Address)); + context.EnterBlock(block.Address); EmitOps(context, block); } From 49eadbc209fa27271eb6110b85776fdfd54a445c Mon Sep 17 00:00:00 2001 From: mageven <62494521+mageven@users.noreply.github.com> Date: Mon, 17 Oct 2022 00:04:42 +0530 Subject: [PATCH 023/737] Fix phantom configured Controllers (#3720) Enable guest controller only when a valid host controller is mapped. --- Ryujinx.Input/HLE/NpadManager.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Input/HLE/NpadManager.cs b/Ryujinx.Input/HLE/NpadManager.cs index b2269d5c4..34e05687f 100644 --- a/Ryujinx.Input/HLE/NpadManager.cs +++ b/Ryujinx.Input/HLE/NpadManager.cs @@ -51,7 +51,16 @@ namespace Ryujinx.Input.HLE { lock (_lock) { - _device.Hid.RefreshInputConfig(_inputConfig); + List validInputs = new List(); + foreach (var inputConfigEntry in _inputConfig) + { + if (_controllers[(int)inputConfigEntry.PlayerIndex] != null) + { + validInputs.Add(inputConfigEntry); + } + } + + _device.Hid.RefreshInputConfig(validInputs); } } @@ -103,6 +112,8 @@ namespace Ryujinx.Input.HLE _controllers[i] = null; } + List validInputs = new List(); + foreach (InputConfig inputConfigEntry in inputConfig) { NpadController controller = new NpadController(_cemuHookClient); @@ -116,6 +127,7 @@ namespace Ryujinx.Input.HLE else { _controllers[(int)inputConfigEntry.PlayerIndex] = controller; + validInputs.Add(inputConfigEntry); } } @@ -123,7 +135,7 @@ namespace Ryujinx.Input.HLE _enableKeyboard = enableKeyboard; _enableMouse = enableMouse; - _device.Hid.RefreshInputConfig(inputConfig); + _device.Hid.RefreshInputConfig(validInputs); } } From 2b50e52e4815f5dc2306ebfec6b15625ff864839 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 16 Oct 2022 23:25:40 +0100 Subject: [PATCH 024/737] Fix primitive count calculation for topology conversion (#3763) Luigi's Mansion 3 performs a non-index quads draw with 6 vertices. It's meant to ignore the last two, but the index pattern's primitive count calculation was rounding up. No idea why the game does this but this should fix random triangles in the map. --- Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs index 8439e79db..907742931 100644 --- a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs +++ b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan public int GetPrimitiveCount(int vertexCount) { - return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride); + return Math.Max(0, (vertexCount - BaseIndex) / IndexStride); } public int GetConvertedCount(int indexCount) From 0dbe45ae37a7aea1bf86787a638ddb68df97a50c Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 16 Oct 2022 23:38:58 +0100 Subject: [PATCH 025/737] Fix various issues caused by Vertex/Index buffer conversions (#3762) * Fix various issues caused by #3679 - The arguments for the 0th dummy vertex buffer were incorrect - it was given an offset of 16 rather than a size of 16. - The wrong size was used when doing `autoBuffer.Get` on a converted vertex buffer. - The possibility of a vertex buffer being disposed and then rebound can rebindings to find a different buffer where the current range is out of bounds. Avoid binding when out of range to prevent validation errors. - The above also affects generation of converted buffers, which was a bit more fatal. Conversion functions now attempt to bound input offset/size. * Fix offset for converted buffer --- Ryujinx.Graphics.Vulkan/BufferHolder.cs | 27 +++++++++++ Ryujinx.Graphics.Vulkan/IndexBufferState.cs | 7 ++- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 +- Ryujinx.Graphics.Vulkan/VertexBufferState.cs | 49 ++++++++++++-------- 4 files changed, 65 insertions(+), 22 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 20571253f..fb1332662 100644 --- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -386,8 +386,25 @@ namespace Ryujinx.Graphics.Vulkan _waitable.WaitForFences(_gd.Api, _device, 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; + } + var key = new I8ToI16CacheKey(_gd); if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) @@ -407,6 +424,11 @@ namespace Ryujinx.Graphics.Vulkan public Auto GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment) { + if (!BoundToRange(offset, ref size)) + { + return null; + } + var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment); if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) @@ -428,6 +450,11 @@ namespace Ryujinx.Graphics.Vulkan public Auto GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize) { + if (!BoundToRange(offset, ref size)) + { + return null; + } + var key = new TopologyConversionCacheKey(_gd, pattern, indexSize); if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs index 1a112d4d7..205eab277 100644 --- a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs +++ b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs @@ -49,7 +49,12 @@ namespace Ryujinx.Graphics.Vulkan } else { - autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _); + autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int bufferSize); + + if (_offset >= bufferSize) + { + autoBuffer = null; + } offset = _offset; size = _size; diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 0eb611237..23abde414 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize); emptyVb.SetData(0, new byte[EmptyVbSize]); - _vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0); + _vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize, 0); _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); ClearScissor = new Rectangle(0, 0, 0xffff, 0xffff); @@ -1243,7 +1243,7 @@ namespace Ryujinx.Graphics.Vulkan _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState); - _vertexBuffersDirty &= ~(1u << i); + _vertexBuffersDirty &= ~(1UL << i); } } diff --git a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index 5710f0b12..661bb7747 100644 --- a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -57,37 +57,48 @@ namespace Ryujinx.Graphics.Vulkan if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0) { autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment); - int stride = (_stride + (alignment - 1)) & -alignment; - var buffer = autoBuffer.Get(cbs, _offset, _size).Value; + if (autoBuffer != null) + { + int stride = (_stride + (alignment - 1)) & -alignment; + int newSize = (_size / _stride) * stride; - if (gd.Capabilities.SupportsExtendedDynamicState) - { - gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( - cbs.CommandBuffer, - binding, - 1, - buffer, - 0, - (ulong)(_size / _stride) * (ulong)stride, - (ulong)stride); - } - else - { - gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0); + var buffer = autoBuffer.Get(cbs, 0, newSize).Value; + + if (gd.Capabilities.SupportsExtendedDynamicState) + { + gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( + cbs.CommandBuffer, + binding, + 1, + buffer, + 0, + (ulong)newSize, + (ulong)stride); + } + else + { + gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0); + } + + _buffer = autoBuffer; } - _buffer = autoBuffer; - state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride; + state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; return; } else { - autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _); + autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size); // The original stride must be reapplied in case it was rewritten. state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; + + if (_offset >= size) + { + autoBuffer = null; + } } } From beacf8c1c89d94a31d98c1b4ad74ee84f80945bc Mon Sep 17 00:00:00 2001 From: mageven <62494521+mageven@users.noreply.github.com> Date: Mon, 17 Oct 2022 04:21:52 +0530 Subject: [PATCH 026/737] TamperMachine: Fix input mask check (#3764) --- Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs b/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs index 38ea90c58..8d75a0e18 100644 --- a/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs +++ b/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions public bool Evaluate() { - return (_input.Value & _mask) != 0; + return (_input.Value & _mask) == _mask; } } } From 7c1d2bbb989f314e4a8316b33651b27e8a7024bc Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 17 Oct 2022 10:37:05 -0300 Subject: [PATCH 027/737] Implement OpenDataStorageWithProgramIndex partially (#3765) * Implement OpenDataStorageWithProgramIndex partially * Was not supposed to change this --- .../HOS/Services/Fs/IFileSystemProxy.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index c32750be1..2ec45aa53 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -12,6 +12,7 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy; +using System; using System.IO; using static Ryujinx.HLE.Utilities.StringUtils; @@ -787,6 +788,26 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } + [CommandHipc(205)] + // OpenDataStorageWithProgramIndex(u8 program_index) -> object + public ResultCode OpenDataStorageWithProgramIndex(ServiceCtx context) + { + byte programIndex = context.RequestData.ReadByte(); + + if ((context.Device.Application.TitleId & 0xf) != programIndex) + { + throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex})."); + } + + var storage = context.Device.FileSystem.RomFs.AsStorage(true); + using var sharedStorage = new SharedRef(storage); + using var sfStorage = new SharedRef(new StorageInterfaceAdapter(ref sharedStorage.Ref())); + + MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref())); + + return ResultCode.Success; + } + [CommandHipc(400)] // OpenDataStorageByCurrentProcess() -> object dataStorage public ResultCode OpenDeviceOperator(ServiceCtx context) From 60ba7b71f26b928c98b2f8412e5ffaa6b4fdec5d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 17 Oct 2022 16:48:14 +0000 Subject: [PATCH 028/737] remove property changed call in time zone validation (#3752) --- Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs | 2 -- Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs index 10dd2da34..f4807023d 100644 --- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs @@ -295,8 +295,6 @@ namespace Ryujinx.Ava.Ui.ViewModels if (_validTzRegions.Contains(location)) { TimeZone = location; - - OnPropertyChanged(nameof(TimeZone)); } } diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs index 73ac06242..810beb3e0 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Windows InitializeComponent(); Load(); - FuncMultiValueConverter converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray())); + FuncMultiValueConverter converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim()); MultiBinding tzMultiBinding = new() { Converter = converter }; tzMultiBinding.Bindings.Add(new Binding("UtcDifference")); tzMultiBinding.Bindings.Add(new Binding("Location")); From 2aeb5b00e300c4deeb9912b4b31d726d76cf7bc2 Mon Sep 17 00:00:00 2001 From: MetrosexualGarbodor <79612681+MetrosexualGarbodor@users.noreply.github.com> Date: Mon, 17 Oct 2022 22:58:11 +0100 Subject: [PATCH 029/737] Update README.md (#3767) Update compatibility numbers --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a8581e06..d5fef7721 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ ## Compatibility -As of September 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,400 boot past menus and into gameplay, with roughly 2,700 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). +As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already! ## Usage From f5a1de6ac53e70a88c0aafefd7013c557d7671c0 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 17 Oct 2022 19:12:49 -0300 Subject: [PATCH 030/737] Fix kernel VA allocation when random allocation fails (#3755) * Fix kernel VA allocation when random allocation fails * This was off by one --- .../HOS/Kernel/Memory/KPageTableBase.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index 857be7a65..501d1cc42 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -2540,11 +2540,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory for (int attempt = 0; attempt < 8; attempt++) { - address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment); + ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment); + ulong aslrEndAddr = aslrAddress + totalNeededSize; - ulong endAddr = address + totalNeededSize; - - KMemoryInfo info = _blockManager.FindBlock(address).GetInfo(); + KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo(); if (info.State != MemoryState.Unmapped) { @@ -2554,11 +2553,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong currBaseAddr = info.Address + reservedPagesCount * PageSize; ulong currEndAddr = info.Address + info.Size; - if (address >= regionStart && - address >= currBaseAddr && - endAddr - 1 <= regionEndAddr - 1 && - endAddr - 1 <= currEndAddr - 1) + if (aslrAddress >= regionStart && + aslrAddress >= currBaseAddr && + aslrEndAddr - 1 <= regionEndAddr - 1 && + aslrEndAddr - 1 <= currEndAddr - 1) { + address = aslrAddress; break; } } @@ -2603,7 +2603,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong totalNeededSize = reservedSize + neededPagesCount * PageSize; - ulong regionEndAddr = regionStart + regionPagesCount * PageSize; + ulong regionEndAddr = (regionStart + regionPagesCount * PageSize) - 1; KMemoryBlock currBlock = _blockManager.FindBlock(regionStart); From a6cd044f0f675ee0bd9d3954228bbd3f2f992a22 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 18 Oct 2022 05:13:44 -0300 Subject: [PATCH 031/737] Vulkan: Fix blit levels/layers parameters being inverted (#3768) --- Ryujinx.Graphics.Vulkan/TextureView.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index 129a77efa..10d0ef05d 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -515,10 +515,10 @@ namespace Ryujinx.Graphics.Vulkan dst.Info, srcRegion, dstRegion, - src.FirstLevel, - dst.FirstLevel, src.FirstLayer, dst.FirstLayer, + src.FirstLevel, + dst.FirstLevel, layers, levels, linearFilter, From c40c3905e2836e8b105406430c33659a84c2e3ca Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Wed, 19 Oct 2022 01:31:34 +0200 Subject: [PATCH 032/737] Avoid allocations in .Parse methods (#3760) * Avoid allocations in .Parse methods Use the Span overloads of the Parse methods when possible to avoid string allocations and remove one unnecessarry array allocation * Avoid another string allocation --- Ryujinx.Common/Utilities/EmbeddedResources.cs | 2 +- Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs | 2 +- Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs | 10 +++++----- Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs | 3 +-- Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs | 2 +- Ryujinx.HLE/Utilities/StringUtils.cs | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Ryujinx.Common/Utilities/EmbeddedResources.cs b/Ryujinx.Common/Utilities/EmbeddedResources.cs index 6113bc726..286d2c5ce 100644 --- a/Ryujinx.Common/Utilities/EmbeddedResources.cs +++ b/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -129,7 +129,7 @@ namespace Ryujinx.Common private static (Assembly, string) ResolveManifestPath(string filename) { - var segments = filename.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries); + var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries); if (segments.Length >= 2) { diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs index 1e6211211..be5b75392 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -918,7 +918,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return -1; } - return int.Parse(part.Substring(0, numberLength)); + return int.Parse(part.AsSpan(0, numberLength)); } private string ParseNumber(bool isSigned = false) diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs index 9dfa677cf..fc0c06479 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs @@ -816,11 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp Reserved = new Array57() }; - modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber)); - modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber); - modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber); - modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber); - modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber); + modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(0, 4), NumberStyles.HexNumber)); + modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(4, 2), NumberStyles.HexNumber); + modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(12, 2), NumberStyles.HexNumber); + modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(8, 4), NumberStyles.HexNumber); + modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber); context.Memory.Write(outputPosition, modelInfo); diff --git a/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs b/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs index d34f4cf8d..e85d99c76 100644 --- a/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs +++ b/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs @@ -122,9 +122,8 @@ namespace Ryujinx.HLE.HOS.Tamper for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++) { int index = wordIndex * wordSize + nybbleIndex; - string byteData = word.Substring(nybbleIndex, 1); - instruction[index] = byte.Parse(byteData, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + instruction[index] = byte.Parse(word.AsSpan(nybbleIndex, 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture); } } diff --git a/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs b/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs index 8cc03d05b..416fc1b49 100644 --- a/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs +++ b/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs @@ -132,7 +132,7 @@ namespace Ryujinx.HLE.Loaders.Mods { if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { - return int.TryParse(str.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out value); + return int.TryParse(str.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out value); } else { diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs index 269f9b558..a64d451c1 100644 --- a/Ryujinx.HLE/Utilities/StringUtils.cs +++ b/Ryujinx.HLE/Utilities/StringUtils.cs @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.Utilities for (int index = 0; index < bytesInHex; index++) { - output[index] = byte.Parse(hexString.Substring(index * 2, 2), NumberStyles.HexNumber); + output[index] = byte.Parse(hexString.AsSpan(index * 2, 2), NumberStyles.HexNumber); } return output; From 9b852c74816d3f9b76e51af479d32d1cd6498c30 Mon Sep 17 00:00:00 2001 From: Yohoki <44595371+Yohoki@users.noreply.github.com> Date: Tue, 18 Oct 2022 18:41:16 -0500 Subject: [PATCH 033/737] Fix: Arguments Break when Updating (#3744) * Wrap Args in quotes -Wrap args in quotes to allow for spaces in dir paths when restarting Ryujinxs from Update. * Wrap second instance of GetCommandLineArgs() * Changed ryuArgs from string to string[] * Update Ryujinx.Ava/Modules/Updater/Updater.cs Co-authored-by: mageven <62494521+mageven@users.noreply.github.com> * Update UpdateDialog.cs Co-authored-by: mageven <62494521+mageven@users.noreply.github.com> --- Ryujinx.Ava/Modules/Updater/Updater.cs | 2 +- Ryujinx/Modules/Updater/UpdateDialog.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index d3c655b01..2f9258bd0 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -278,7 +278,7 @@ namespace Ryujinx.Modules { string ryuName = Path.GetFileName(Environment.ProcessPath); string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); - string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray()); + var ryuArg = Environment.GetCommandLineArgs().Skip(1); if (!OperatingSystem.IsWindows()) { diff --git a/Ryujinx/Modules/Updater/UpdateDialog.cs b/Ryujinx/Modules/Updater/UpdateDialog.cs index 5e5ef84d6..cdf85427e 100644 --- a/Ryujinx/Modules/Updater/UpdateDialog.cs +++ b/Ryujinx/Modules/Updater/UpdateDialog.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Modules { string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); - string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray()); + var ryuArg = Environment.GetCommandLineArgs().AsEnumerable().Skip(1); Process.Start(ryuExe, ryuArg); From 6e92b7a3782f2b46c57c833d68087d7f5b1feb80 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 19 Oct 2022 00:52:08 +0100 Subject: [PATCH 034/737] Dispose Vulkan TextureStorage when views hit 0 instead of immediately (#3738) Due to the `using` statement being scoped to the `CreateTextureView` method, `TextureStorage` would be disposed as soon as the view was returned. This was largely fine as the TextureStorage resources were being kept alive by the views holding their own references to them, but it also meant that dispose is only called as soon as the texture is created. Aliased Storages are TextureStorages created with the same allocation as another TextureStorage, if they have to be aliased as another format. We keep track of a TextureStorage's `_aliasedStorages` as they are created, and dispose them when the TextureStorage is disposed... ...except it is disposed immediately, before any aliased storages are even created. The aliased storages added after this will never be disposed. This PR attempts to fix this by disposing TextureStorage when its view count reaches 0. The other use of texture storage - the D32S8 blit - still manually disposes the storage, but regular uses created via the GAL are now disposed by the view count. I think this makes the most sense, as otherwise in the future this behaviour might be forgotton and more things could be added to the Dispose() method that don't work due to it not actually calling at the right time. This should improve memory leaks in Super Mario Odyssey, most noticeable when resolution scaling. The memory usage of the game is still wildly unpredictable due to how it interacts with the texture cache, but now it shouldn't get considerably longer as you play... I hope. I've seen it typically recover back to the same level occasionally, though it can spike significantly. Please test a bunch of games on multiple GPUs to make sure this doesn't break anything. --- Ryujinx.Graphics.Vulkan/TextureStorage.cs | 2 ++ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index b2cbd6029..c4ebaef3b 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -480,6 +480,8 @@ namespace Ryujinx.Graphics.Vulkan if (--_viewsCount == 0) { _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size); + + Dispose(); } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index d92fff49e..96c7da6c7 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -310,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan internal TextureView CreateTextureView(TextureCreateInfo info, float scale) { // This should be disposed when all views are destroyed. - using var storage = CreateTextureStorage(info, scale); + var storage = CreateTextureStorage(info, scale); return storage.CreateView(info, 0, 0); } From 77c4291c3482c7adf707d2353128dded5a24bab3 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 18 Oct 2022 17:10:28 -0700 Subject: [PATCH 035/737] Avalonia: Update Polish Translation (#3722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add new string You need the period there otherwise it could be read as "Głoś" -> "Preach" * Update MainWindow.axaml Updating to bring it in line with the other languages naming themselves in their respective languages * Update pl_PL.json realizing that period isn't necessary considering the string's usage (which to be fair, I should have checked when I added it) * Update pl_PL.json * Add Updater Message --- Ryujinx.Ava/Assets/Locales/pl_PL.json | 6 +++++- Ryujinx.Ava/Ui/Windows/MainWindow.axaml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/Ryujinx.Ava/Assets/Locales/pl_PL.json index 23ae3318e..95329b2b6 100644 --- a/Ryujinx.Ava/Assets/Locales/pl_PL.json +++ b/Ryujinx.Ava/Assets/Locales/pl_PL.json @@ -588,5 +588,9 @@ "SettingsTabGraphicsPreferredGpuTooltip": "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.", "SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx", "SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia", - "SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?" + "SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?", + "RyujinxUpdaterMessage": "Czy chcesz zaktualizować Ryujinx do najnowszej wersji?", + "SettingsTabHotkeysVolumeUpHotkey": "Zwiększ Głośność:", + "SettingsTabHotkeysVolumeDownHotkey": "Zmniejsz Głośność:", + "VolumeShort": "Głoś" } diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml index b28f05063..313e701c8 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml @@ -164,7 +164,7 @@ + Header="Polski" /> Date: Wed, 19 Oct 2022 02:21:33 +0200 Subject: [PATCH 036/737] =?UTF-8?q?A64:=20Add=20fast=20path=20for=20Fcvtas?= =?UTF-8?q?=5FGp/S/V,=20Fcvtau=5FGp/S/V=20and=20Frinta=5FS/V=20in=E2=80=A6?= =?UTF-8?q?=20(#3712)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * A64: Add fast path for Fcvtas_Gp/S/V, Fcvtau_Gp/S/V and Frinta_S/V instructions; they use "Round to Nearest with Ties to Away" rounding mode not supported in x86. All instructions involved have been tested locally in both release and debug modes, in both lowcq and highcq. The titles Mario Strikers and Super Smash Bros. U. use these instructions intensively. * Update Ptc.cs * A32: Add fast path for Vcvta_RM, Vrinta_RM and Vrinta_V instructions aswell. --- .../Instructions/InstEmitSimdArithmetic.cs | 52 ++++++-- ARMeilleure/Instructions/InstEmitSimdCvt.cs | 126 ++++++++++++++++-- ARMeilleure/Instructions/InstEmitSimdCvt32.cs | 49 +++++-- .../Instructions/InstEmitSimdHelper.cs | 55 ++++++-- ARMeilleure/State/FPRoundingMode.cs | 5 +- ARMeilleure/Translation/PTC/Ptc.cs | 2 +- 6 files changed, 243 insertions(+), 46 deletions(-) diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs index a35e28a15..b91c522ec 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs @@ -1617,18 +1617,32 @@ namespace ARMeilleure.Instructions public static void Frinta_S(ArmEmitterContext context) { - EmitScalarUnaryOpF(context, (op1) => + if (Optimizations.UseSse41) { - return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1); - }); + EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearestAway); + } + else + { + EmitScalarUnaryOpF(context, (op1) => + { + return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1); + }); + } } public static void Frinta_V(ArmEmitterContext context) { - EmitVectorUnaryOpF(context, (op1) => + if (Optimizations.UseSse41) { - return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1); - }); + EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearestAway); + } + else + { + EmitVectorUnaryOpF(context, (op1) => + { + return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1); + }); + } } public static void Frinti_S(ArmEmitterContext context) @@ -3516,9 +3530,18 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); - Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss; + Operand res; - Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss; + + res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode))); + } + else + { + res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: true); + } if ((op.Size & 1) != 0) { @@ -3538,9 +3561,18 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); - Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps; + Operand res; - Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps; + + res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode))); + } + else + { + res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: false); + } if (op.RegisterSize == RegisterSize.Simd64) { diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt.cs b/ARMeilleure/Instructions/InstEmitSimdCvt.cs index c8c427b79..9329f2b75 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt.cs @@ -164,32 +164,74 @@ namespace ARMeilleure.Instructions public static void Fcvtas_Gp(ArmEmitterContext context) { - EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1)); + if (Optimizations.UseSse41) + { + EmitSse41Fcvts_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false); + } + else + { + EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1)); + } } public static void Fcvtas_S(ArmEmitterContext context) { - EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true); + if (Optimizations.UseSse41) + { + EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: true); + } + else + { + EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true); + } } public static void Fcvtas_V(ArmEmitterContext context) { - EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false); + if (Optimizations.UseSse41) + { + EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: false); + } + else + { + EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false); + } } public static void Fcvtau_Gp(ArmEmitterContext context) { - EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1)); + if (Optimizations.UseSse41) + { + EmitSse41Fcvtu_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false); + } + else + { + EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1)); + } } public static void Fcvtau_S(ArmEmitterContext context) { - EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true); + if (Optimizations.UseSse41) + { + EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: true); + } + else + { + EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true); + } } public static void Fcvtau_V(ArmEmitterContext context) { - EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false); + if (Optimizations.UseSse41) + { + EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: false); + } + else + { + EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false); + } } public static void Fcvtl_V(ArmEmitterContext context) @@ -1223,7 +1265,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar); + } Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes); @@ -1265,7 +1314,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar); + } Operand nLong = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar); @@ -1314,7 +1370,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar); + } Operand zero = context.VectorZero(); @@ -1369,7 +1432,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar); + } Operand zero = context.VectorZero(); @@ -1424,7 +1494,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true); + } Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32 ? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes) @@ -1464,7 +1541,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true); + } Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32 ? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes) @@ -1512,7 +1596,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true); + } Operand zero = context.VectorZero(); @@ -1567,7 +1658,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask); } - nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true); + } Operand zero = context.VectorZero(); diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index 69ba42747..c76634ebf 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -203,6 +203,9 @@ namespace ARMeilleure.Instructions FPRoundingMode roundMode; switch (rm) { + case 0b00: + roundMode = FPRoundingMode.ToNearestAway; + break; case 0b01: roundMode = FPRoundingMode.ToNearest; break; @@ -228,7 +231,7 @@ namespace ARMeilleure.Instructions bool unsigned = op.Opc == 0; int rm = op.Opc2 & 3; - if (Optimizations.UseSse41 && rm != 0b00) + if (Optimizations.UseSse41) { EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned); } @@ -267,15 +270,21 @@ namespace ARMeilleure.Instructions int rm = op.Opc2 & 3; - if (Optimizations.UseSse2 && rm != 0b00) + if (Optimizations.UseSse41) { EmitScalarUnaryOpSimd32(context, (m) => { - Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd; - FPRoundingMode roundMode = RMToRoundMode(rm); - return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd; + return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode))); + } + else + { + return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: true); + } }); } else @@ -305,7 +314,17 @@ namespace ARMeilleure.Instructions // VRINTA (vector). public static void Vrinta_V(ArmEmitterContext context) { - EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m)); + if (Optimizations.UseSse41) + { + EmitVectorUnaryOpSimd32(context, (m) => + { + return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: false); + }); + } + else + { + EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m)); + } } // VRINTM (vector). @@ -413,7 +432,14 @@ namespace ARMeilleure.Instructions Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); - nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true); + } Operand zero = context.VectorZero(); @@ -464,7 +490,14 @@ namespace ARMeilleure.Instructions Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ)); nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); - nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode))); + if (roundMode != FPRoundingMode.ToNearestAway) + { + nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode))); + } + else + { + nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true); + } Operand zero = context.VectorZero(); diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs index 49c17560b..0e7af794a 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -33,6 +33,14 @@ namespace ARMeilleure.Instructions }; public static readonly long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0; + + public static ulong X86GetGf2p8LogicalShiftLeft(int shift) + { + ulong identity = (0b00000001UL << 56) | (0b00000010UL << 48) | (0b00000100UL << 40) | (0b00001000UL << 32) | + (0b00010000UL << 24) | (0b00100000UL << 16) | (0b01000000UL << 8) | (0b10000000UL << 0); + + return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8); + } #endregion #region "X86 SSE Intrinsics" @@ -243,19 +251,44 @@ namespace ARMeilleure.Instructions throw new ArgumentException($"Invalid rounding mode \"{roundMode}\"."); } - public static ulong X86GetGf2p8LogicalShiftLeft(int shift) + public static Operand EmitSse41RoundToNearestWithTiesToAwayOpF(ArmEmitterContext context, Operand n, bool scalar) { - ulong identity = - (0b00000001UL << 56) | - (0b00000010UL << 48) | - (0b00000100UL << 40) | - (0b00001000UL << 32) | - (0b00010000UL << 24) | - (0b00100000UL << 16) | - (0b01000000UL << 8) | - (0b10000000UL << 0); + Debug.Assert(n.Type == OperandType.V128); - return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8); + Operand nCopy = context.Copy(n); + + Operand rC = Const(X86GetRoundControl(FPRoundingMode.TowardsZero)); + + IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; + + if ((op.Size & 1) == 0) + { + Operand signMask = scalar ? X86GetScalar(context, int.MinValue) : X86GetAllElements(context, int.MinValue); + signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy); + + // 0x3EFFFFFF == BitConverter.SingleToInt32Bits(0.5f) - 1 + Operand valueMask = scalar ? X86GetScalar(context, 0x3EFFFFFF) : X86GetAllElements(context, 0x3EFFFFFF); + valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask); + + nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addss : Intrinsic.X86Addps, nCopy, valueMask); + + nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundss : Intrinsic.X86Roundps, nCopy, rC); + } + else + { + Operand signMask = scalar ? X86GetScalar(context, long.MinValue) : X86GetAllElements(context, long.MinValue); + signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy); + + // 0x3FDFFFFFFFFFFFFFL == BitConverter.DoubleToInt64Bits(0.5d) - 1L + Operand valueMask = scalar ? X86GetScalar(context, 0x3FDFFFFFFFFFFFFFL) : X86GetAllElements(context, 0x3FDFFFFFFFFFFFFFL); + valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask); + + nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addsd : Intrinsic.X86Addpd, nCopy, valueMask); + + nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundsd : Intrinsic.X86Roundpd, nCopy, rC); + } + + return nCopy; } public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.). diff --git a/ARMeilleure/State/FPRoundingMode.cs b/ARMeilleure/State/FPRoundingMode.cs index ee4f87668..8d757a151 100644 --- a/ARMeilleure/State/FPRoundingMode.cs +++ b/ARMeilleure/State/FPRoundingMode.cs @@ -2,9 +2,10 @@ namespace ARMeilleure.State { public enum FPRoundingMode { - ToNearest = 0, + ToNearest = 0, // With ties to even. TowardsPlusInfinity = 1, TowardsMinusInfinity = 2, - TowardsZero = 3 + TowardsZero = 3, + ToNearestAway = 4 // With ties to away. } } diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 1515713be..70f6e0127 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 3710; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 3713; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; From 8d41402fa603a2f00ccd08239d3b938fd60715a3 Mon Sep 17 00:00:00 2001 From: merry Date: Wed, 19 Oct 2022 01:36:04 +0100 Subject: [PATCH 037/737] A32: Implement VCVTT, VCVTB (#3710) * A32: Implement VCVTT, VCVTB * A32: F16C implementation of VCVTT/VCVTB --- ARMeilleure/Decoders/OpCode32SimdCvtTB.cs | 44 ++++++++++ ARMeilleure/Decoders/OpCodeTable.cs | 1 + ARMeilleure/Instructions/InstEmitSimdCvt32.cs | 62 +++++++++++++ .../Instructions/InstEmitSimdHelper32.cs | 16 ++++ Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs | 87 +++++++++++++++++++ 5 files changed, 210 insertions(+) create mode 100644 ARMeilleure/Decoders/OpCode32SimdCvtTB.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs b/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs new file mode 100644 index 000000000..a95b32ab0 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs @@ -0,0 +1,44 @@ +namespace ARMeilleure.Decoders +{ + class OpCode32SimdCvtTB : OpCode32, IOpCode32Simd + { + public int Vd { get; } + public int Vm { get; } + public bool Op { get; } // Convert to Half / Convert from Half + public bool T { get; } // Top / Bottom + public int Size { get; } // Double / Single + + public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, false); + public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, true); + + public OpCode32SimdCvtTB(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode) + { + IsThumb = isThumb; + + Op = ((opCode >> 16) & 0x1) != 0; + T = ((opCode >> 7) & 0x1) != 0; + Size = ((opCode >> 8) & 0x1); + + RegisterSize = Size == 1 ? RegisterSize.Int64 : RegisterSize.Int32; + + if (Size == 1) + { + if (Op) + { + Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf); + Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e); + } + else + { + Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e); + Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf); + } + } + else + { + Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e); + Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e); + } + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index b4f4b1796..f44c15400 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -828,6 +828,7 @@ namespace ARMeilleure.Decoders SetVfp("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // FP32 to int. SetVfp("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // Int to FP32. SetVfp("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // The many FP32 to int encodings (fp). + SetVfp("<<<<11101x11001xxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_TB, OpCode32SimdCvtTB.Create, OpCode32SimdCvtTB.CreateT32); SetVfp("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32); SetVfp("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create, OpCode32SimdDupGP.CreateT32); SetVfp("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32); diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index c76634ebf..ba713feb7 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -261,6 +261,68 @@ namespace ARMeilleure.Instructions } } + public static void Vcvt_TB(ArmEmitterContext context) + { + OpCode32SimdCvtTB op = (OpCode32SimdCvtTB)context.CurrOp; + + if (Optimizations.UseF16c) + { + Debug.Assert(!Optimizations.ForceLegacySse); + + if (op.Op) + { + Operand res = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm); + if (op.Size == 1) + { + res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), res); + } + res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest))); + res = context.VectorExtract16(res, 0); + InsertScalar16(context, op.Vd, op.T, res); + } + else + { + Operand res = context.VectorCreateScalar(ExtractScalar16(context, op.Vm, op.T)); + res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res); + if (op.Size == 1) + { + res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res); + } + res = context.VectorExtract(op.Size == 1 ? OperandType.I64 : OperandType.I32, res, 0); + InsertScalar(context, op.Vd, res); + } + } + else + { + if (op.Op) + { + // Convert to half + + Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm); + + MethodInfo method = op.Size == 1 + ? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)) + : typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)); + Operand res = context.Call(method, src); + + InsertScalar16(context, op.Vd, op.T, res); + } + else + { + // Convert from half + + Operand src = ExtractScalar16(context, op.Vm, op.T); + + MethodInfo method = op.Size == 1 + ? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)) + : typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)); + Operand res = context.Call(method, src); + + InsertScalar(context, op.Vd, res); + } + } + } + // VRINTA/M/N/P (floating-point). public static void Vrint_RM(ArmEmitterContext context) { diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs index 0620ea332..84b01d05c 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs @@ -70,6 +70,22 @@ namespace ARMeilleure.Instructions context.Copy(vec, insert); } + public static Operand ExtractScalar16(ArmEmitterContext context, int reg, bool top) + { + return context.VectorExtract16(GetVecA32(reg >> 2), ((reg & 3) << 1) | (top ? 1 : 0)); + } + + public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value) + { + Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.I32); + + Operand vec, insert; + vec = GetVecA32(reg >> 2); + insert = context.VectorInsert16(vec, value, ((reg & 3) << 1) | (top ? 1 : 0)); + + context.Copy(vec, insert); + } + public static Operand ExtractElement(ArmEmitterContext context, int reg, int size, bool signed) { return EmitVectorExtract32(context, reg >> (4 - size), reg & ((16 >> size) - 1), size, signed); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs index 78d5c3cc2..0c90d0bad 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs @@ -339,6 +339,93 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } + + [Explicit] + [Test, Pairwise, Description("VCVT.F16.F32 , ")] + public void Vcvt_F32_F16([Values(0u, 1u, 2u, 3u)] uint rd, + [Values(0u, 1u, 2u, 3u)] uint rm, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3, + [Values] bool top) + { + uint opcode = 0xeeb30a40; // VCVTB.F16.F32 S0, D0 + + if (top) + { + opcode |= 1 << 7; + } + + opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); + opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); + + V128 v0 = MakeVectorE0E1E2E3(s0, s1, s2, s3); + + SingleOpcode(opcode, v0: v0); + + CompareAgainstUnicorn(); + } + + [Explicit] + [Test, Pairwise, Description("VCVT.F16.F64 , ")] + public void Vcvt_F64_F16([Values(0u, 1u, 2u, 3u)] uint rd, + [Values(0u, 1u)] uint rm, + [ValueSource(nameof(_1D_F_))] ulong d0, + [ValueSource(nameof(_1D_F_))] ulong d1, + [Values] bool top) + { + uint opcode = 0xeeb30b40; // VCVTB.F16.F64 S0, D0 + + if (top) + { + opcode |= 1 << 7; + } + + opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + + V128 v0 = MakeVectorE0E1(d0, d1); + + SingleOpcode(opcode, v0: v0); + + CompareAgainstUnicorn(); + } + + [Explicit] + [Test, Pairwise, Description("VCVT.F.F16 , ")] + public void Vcvt_F16_Fx([Values(0u, 1u, 2u, 3u)] uint rd, + [Values(0u, 1u, 2u, 3u)] uint rm, + [ValueSource(nameof(_1D_F_))] ulong d0, + [ValueSource(nameof(_1D_F_))] ulong d1, + [Values] bool top, + [Values] bool sz) + { + uint opcode = 0xeeb20a40; // VCVTB.F32.F16 S0, S0 + + if (top) + { + opcode |= 1 << 7; + } + + if (sz) + { + opcode |= 1 << 8; + opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); + } + else + { + opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); + } + + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + + V128 v0 = MakeVectorE0E1(d0, d1); + + SingleOpcode(opcode, v0: v0); + + CompareAgainstUnicorn(); + } #endif } } From 7d26e4ac7b61c4b6a860f6fd9aabdc8e653bc2d7 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 18 Oct 2022 22:02:45 -0300 Subject: [PATCH 038/737] Fix mapping leaks caused by UnmapView not working on Linux (#3650) * Add test for UnmapView mapping leaks * Throw when UnmapView fails on Linux * Fix UnmapView * Remove throw --- Ryujinx.Memory.Tests/Tests.cs | 26 ++++++++++++++++++++++++++ Ryujinx.Memory/MemoryManagementUnix.cs | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Memory.Tests/Tests.cs b/Ryujinx.Memory.Tests/Tests.cs index 45d00e51d..c5a7842ec 100644 --- a/Ryujinx.Memory.Tests/Tests.cs +++ b/Ryujinx.Memory.Tests/Tests.cs @@ -92,5 +92,31 @@ namespace Ryujinx.Memory.Tests } } } + + [Test] + public void Test_AliasMapLeak() + { + if (OperatingSystem.IsMacOS()) + { + // Memory aliasing tests fail on CI at the moment. + return; + } + + ulong pageSize = 4096; + ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that. + + using MemoryBlock backing = new MemoryBlock(pageSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + + for (ulong offset = 0; offset < size; offset += pageSize) + { + toAlias.MapView(backing, 0, offset, pageSize); + + toAlias.Write(offset, 0xbadc0de); + Assert.AreEqual(0xbadc0de, backing.Read(0)); + + toAlias.UnmapView(backing, offset, pageSize); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Memory/MemoryManagementUnix.cs b/Ryujinx.Memory/MemoryManagementUnix.cs index db7539f03..df3fcea91 100644 --- a/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/Ryujinx.Memory/MemoryManagementUnix.cs @@ -177,7 +177,7 @@ namespace Ryujinx.Memory public static void UnmapView(IntPtr location, ulong size) { - mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED, -1, 0); + mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0); } } } \ No newline at end of file From 2099a3e84b8e01138ebb32b305eea01359e7b4ed Mon Sep 17 00:00:00 2001 From: Carl Ouellette Date: Tue, 18 Oct 2022 21:14:31 -0400 Subject: [PATCH 039/737] Manage state of NfcManager (#3678) * Manage state of NfcManager Very basic state management but works with Hyrule Warriors Definitive Edition. Partially fixes #2122 * Fixes changes from review --- .../HOS/Services/Nfc/NfcManager/INfc.cs | 28 ++++++++++++++++++- .../Services/Nfc/NfcManager/Types/State.cs | 8 ++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs index f2fc867d8..a1ae0b9c7 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs @@ -5,22 +5,48 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager class INfc : IpcService { private NfcPermissionLevel _permissionLevel; + private State _state; public INfc(NfcPermissionLevel permissionLevel) { _permissionLevel = permissionLevel; + _state = State.NonInitialized; } [CommandHipc(0)] [CommandHipc(400)] // 4.0.0+ - // Initialize() + // Initialize(u64, u64, pid, buffer) public ResultCode Initialize(ServiceCtx context) { + _state = State.Initialized; + Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel }); return ResultCode.Success; } + [CommandHipc(1)] + [CommandHipc(401)] // 4.0.0+ + // Finalize() + public ResultCode Finalize(ServiceCtx context) + { + _state = State.NonInitialized; + + Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel }); + + return ResultCode.Success; + } + + [CommandHipc(2)] + [CommandHipc(402)] // 4.0.0+ + // GetState() -> u32 + public ResultCode GetState(ServiceCtx context) + { + context.ResponseData.Write((int)_state); + + return ResultCode.Success; + } + [CommandHipc(3)] [CommandHipc(403)] // 4.0.0+ // IsNfcEnabled() -> b8 diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs new file mode 100644 index 000000000..85f999507 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager +{ + enum State + { + NonInitialized, + Initialized + } +} \ No newline at end of file From 56621615b1ca52822141862d660a661054fd6694 Mon Sep 17 00:00:00 2001 From: WilliamWsyHK Date: Wed, 19 Oct 2022 09:27:11 +0800 Subject: [PATCH 040/737] Implement the GetSessionCacheMode in SSL servuce (#3735) --- Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs index ff6586734..726ee122d 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs @@ -349,7 +349,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService // GetSessionCacheMode() -> nn::ssl::sf::SessionCacheMode public ResultCode GetSessionCacheMode(ServiceCtx context) { - throw new ServiceNotImplementedException(this, context); + context.ResponseData.Write((uint)_sessionCacheMode); + + Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { _sessionCacheMode }); + + return ResultCode.Success; } [CommandHipc(19)] From 62585755fdd79a95a1e276a7ba742c2d7a9bed54 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Wed, 19 Oct 2022 04:08:34 +0200 Subject: [PATCH 041/737] Do not clear the rejit queue when overlaps count is equal to 0. (#3721) * Do not clear the rejit queue when overlaps count is equal to 0. * Ptc and PtcProfiler must be invalidated. * Revert "Ptc and PtcProfiler must be invalidated." This reverts commit f5b0ad9d7dc3c0b3a0da184de4d04d7234939c81. * Fix #3710 slow path due to #3701. --- ARMeilleure/Instructions/InstEmitSimdCvt32.cs | 10 ++++++++-- ARMeilleure/Translation/Translator.cs | 9 ++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index ba713feb7..f3f239589 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -296,27 +296,33 @@ namespace ARMeilleure.Instructions { if (op.Op) { - // Convert to half + // Convert to half. Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm); MethodInfo method = op.Size == 1 ? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)) : typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)); + + context.StoreToContext(); Operand res = context.Call(method, src); + context.LoadFromContext(); InsertScalar16(context, op.Vd, op.T, res); } else { - // Convert from half + // Convert from half. Operand src = ExtractScalar16(context, op.Vm, op.T); MethodInfo method = op.Size == 1 ? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)) : typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)); + + context.StoreToContext(); Operand res = context.Call(method, src); + context.LoadFromContext(); InsertScalar(context, op.Vd, res); } diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index ee8e3e8b5..c50b1c3d0 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -455,13 +455,16 @@ namespace ARMeilleure.Translation public void InvalidateJitCacheRegion(ulong address, ulong size) { - // If rejit is running, stop it as it may be trying to rejit a function on the invalidated region. - ClearRejitQueue(allowRequeue: true); - ulong[] overlapAddresses = Array.Empty(); int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses); + if (overlapsCount != 0) + { + // If rejit is running, stop it as it may be trying to rejit a function on the invalidated region. + ClearRejitQueue(allowRequeue: true); + } + for (int index = 0; index < overlapsCount; index++) { ulong overlapAddress = overlapAddresses[index]; From 1e5b45f58097858c61217be7ad5707cefc3eb1df Mon Sep 17 00:00:00 2001 From: Mortus06 <92671266+Mortus06@users.noreply.github.com> Date: Wed, 19 Oct 2022 11:30:28 +0200 Subject: [PATCH 042/737] Avalonia: update it_IT.json (#3742) * Avalonia: update it_IT.json * Fixed ; instead of : * Update it_IT.json port di #3766 * Update Ryujinx.Ava/Assets/Locales/it_IT.json Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com> * Grammar fix Co-authored-by: Lorenzo Giannini <55211569+Lorenzo0310200@users.noreply.github.com> Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com> --- Ryujinx.Ava/Assets/Locales/it_IT.json | 68 ++++++++++++++++++++------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/it_IT.json b/Ryujinx.Ava/Assets/Locales/it_IT.json index 3e403094a..f2e6188eb 100644 --- a/Ryujinx.Ava/Assets/Locales/it_IT.json +++ b/Ryujinx.Ava/Assets/Locales/it_IT.json @@ -49,8 +49,8 @@ "GameListContextMenuManageTitleUpdatesToolTip": "Apre la finestra di gestione aggiornamenti del gioco", "GameListContextMenuManageDlc": "Gestici DLC", "GameListContextMenuManageDlcToolTip": "Apre la finestra di gestione DLC", - "GameListContextMenuOpenModsDirectory": "Apri cartella delle mods", - "GameListContextMenuOpenModsDirectoryToolTip": "Apre la cartella che contiene le mods dell'applicazione", + "GameListContextMenuOpenModsDirectory": "Apri cartella delle mod", + "GameListContextMenuOpenModsDirectoryToolTip": "Apre la cartella che contiene le mod dell'applicazione", "GameListContextMenuCacheManagement": "Gestione della cache", "GameListContextMenuCacheManagementPurgePptc": "Pulisci PPTC cache", "GameListContextMenuCacheManagementPurgePptcToolTip": "Elimina la PPTC cache dell'applicazione", @@ -81,7 +81,7 @@ "SettingsTabGeneralRemove": "Rimuovi", "SettingsTabSystem": "Sistema", "SettingsTabSystemCore": "Core", - "SettingsTabSystemSystemRegion": "Regione di sistema:", + "SettingsTabSystemSystemRegion": "Regione del sistema:", "SettingsTabSystemSystemRegionJapan": "Giappone", "SettingsTabSystemSystemRegionUSA": "Stati Uniti d'America", "SettingsTabSystemSystemRegionEurope": "Europa", @@ -89,7 +89,7 @@ "SettingsTabSystemSystemRegionChina": "Cina", "SettingsTabSystemSystemRegionKorea": "Corea", "SettingsTabSystemSystemRegionTaiwan": "Taiwan", - "SettingsTabSystemSystemLanguage": "Lingua di sistema:", + "SettingsTabSystemSystemLanguage": "Lingua del sistema:", "SettingsTabSystemSystemLanguageJapanese": "Giapponese", "SettingsTabSystemSystemLanguageAmericanEnglish": "Inglese americano", "SettingsTabSystemSystemLanguageFrench": "Francese", @@ -107,8 +107,8 @@ "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Spagnolo latino americano", "SettingsTabSystemSystemLanguageSimplifiedChinese": "Cinese semplificato", "SettingsTabSystemSystemLanguageTraditionalChinese": "Cinese tradizionale", - "SettingsTabSystemSystemTimeZone": "Fuso orario di sistema:", - "SettingsTabSystemSystemTime": "Data e ora di sistema:", + "SettingsTabSystemSystemTimeZone": "Fuso orario del sistema:", + "SettingsTabSystemSystemTime": "Data e ora del sistema:", "SettingsTabSystemEnableVsync": "Attiva VSync", "SettingsTabSystemEnablePptc": "Attiva PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableFsIntegrityChecks": "Attiva controlli d'integrità FS", @@ -155,7 +155,7 @@ "SettingsTabLoggingEnableTraceLogs": "Attiva Trace Logs", "SettingsTabLoggingEnableGuestLogs": "Attiva Guest Logs", "SettingsTabLoggingEnableFsAccessLogs": "Attiva Fs Access Logs", - "SettingsTabLoggingFsGlobalAccessLogMode": "Modalità di log accesso globale Fs:", + "SettingsTabLoggingFsGlobalAccessLogMode": "Modalità log accesso globale Fs:", "SettingsTabLoggingDeveloperOptions": "Opzioni da sviluppatore (AVVISO: Ridurrà le prestazioni)", "SettingsTabLoggingOpenglLogLevel": "Livello di log OpenGL:", "SettingsTabLoggingOpenglLogLevelNone": "Nessuno", @@ -240,7 +240,7 @@ "ControllerSettingsRightSR": "SR", "ControllerSettingsExtraButtonsLeft": "Tasto sinitro", "ControllerSettingsExtraButtonsRight": "Tasto destro", - "ControllerSettingsMisc": "Miscellanee", + "ControllerSettingsMisc": "Varie", "ControllerSettingsTriggerThreshold": "Sensibilità dei grilletti:", "ControllerSettingsMotion": "Movimento", "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usa sensore compatibile con CemuHook", @@ -261,7 +261,7 @@ "UserProfilesClose": "Chiudi", "ProfileImageSelectionTitle": "Selezione dell'immagine profilo", "ProfileImageSelectionHeader": "Scegli un'immagine profilo", - "ProfileImageSelectionNote": "Puoi importare un'immagine profilo personalizzata o selezionare un avatar dal firmware di sistema", + "ProfileImageSelectionNote": "Puoi importare un'immagine profilo personalizzata o selezionare un avatar dal firmware del sistema", "ProfileImageSelectionImportImage": "Importa file immagine", "ProfileImageSelectionSelectAvatar": "Seleziona avatar dal firmware", "InputDialogTitle": "Input Dialog", @@ -293,7 +293,7 @@ "ControllerSettingsRumbleStrongMultiplier": "Moltiplicatore vibrazione forte", "ControllerSettingsRumbleWeakMultiplier": "Moltiplicatore vibrazione debole", "DialogMessageSaveNotAvailableMessage": "Non ci sono dati di salvataggio per {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Vuoi creare dei dat di salvataggio per questo gioco?", + "DialogMessageSaveNotAvailableCreateSaveMessage": "Vuoi creare dei dati di salvataggio per questo gioco?", "DialogConfirmationTitle": "Ryujinx - Conferma", "DialogUpdaterTitle": "Ryujinx - Updater", "DialogErrorTitle": "Ryujinx - Errore", @@ -318,7 +318,7 @@ "DialogUpdaterDownloadingMessage": "Download dell'aggiornamento...", "DialogUpdaterExtractionMessage": "Estrazione dell'aggiornamento...", "DialogUpdaterRenamingMessage": "Rinominazione dell'aggiornamento...", - "DialogUpdaterAddingFilesMessage": "Aggiunta nuovo aggiornamento...", + "DialogUpdaterAddingFilesMessage": "Aggiunta del nuovo aggiornamento...", "DialogUpdaterCompleteMessage": "Aggiornamento completato!", "DialogUpdaterRestartMessage": "Vuoi riavviare Ryujinx adesso?", "DialogUpdaterArchNotSupportedMessage": "Non stai usando un'architettura di sistema supportata!", @@ -331,7 +331,7 @@ "DialogThemeRestartMessage": "Il tema è stato salvato. E' richiesto un riavvio per applicare un tema.", "DialogThemeRestartSubMessage": "Vuoi riavviare?", "DialogFirmwareInstallEmbeddedMessage": "Vuoi installare il firmware incorporato in questo gioco? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Non è stato trovato alcun firmware installato, ma Ryujinx è riuscito di installare il firmware {0} dal gioco fornito.\nL'emulatore si avvierà adesso.", + "DialogFirmwareInstallEmbeddedSuccessMessage": "Non è stato trovato alcun firmware installato, ma Ryujinx è riuscito ad installare il firmware {0} dal gioco fornito.\nL'emulatore si avvierà adesso.", "DialogFirmwareNoFirmwareInstalledMessage": "Nessun firmware installato", "DialogFirmwareInstalledMessage": "Il firmware {0} è stato installato", "DialogOpenSettingsWindowLabel": "Apri finestra delle impostazioni", @@ -355,14 +355,14 @@ "DialogShaderDeletionMessage": "Stai per eliminare la Shader cache per :\n\n{0}\n\nSei sicuro di voler proseguire?", "DialogShaderDeletionErrorMessage": "Errore nell'eliminazione della Shader cache a {0}: {1}", "DialogRyujinxErrorMessage": "Ryujinx ha incontrato un errore", - "DialogInvalidTitleIdErrorMessage": "Errore UI: Il gioco selezionato non ha un title ID valido", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Un firmware di sistema valido non è stato trovato in {0}.", + "DialogInvalidTitleIdErrorMessage": "Errore UI: Il gioco selezionato non ha un ID titolo valido", + "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Un firmware del sistema valido non è stato trovato in {0}.", "DialogFirmwareInstallerFirmwareInstallTitle": "Installa firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "La versione di sistema {0} sarà installata.", + "DialogFirmwareInstallerFirmwareInstallMessage": "La versione del sistema {0} sarà installata.", "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nQuesta sostituirà l'attuale versione di sistema {0}.", "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nVuoi continuare?", "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installazione del firmware...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "La versione di sistema {0} è stata installata.", + "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "La versione del sistema {0} è stata installata.", "DialogUserProfileDeletionWarningMessage": "Non ci sarebbero altri profili da aprire se il profilo selezionato viene cancellato", "DialogUserProfileDeletionConfirmMessage": "Vuoi eliminare il profilo selezionato?", "DialogControllerSettingsModifiedConfirmMessage": "Le attuali impostazioni del controller sono state aggiornate.", @@ -557,4 +557,40 @@ "SettingsXamlThemeFile" : "File del tema xaml", "SettingsTabHotkeysResScaleUpHotkey": "Aumentare la risoluzione:", "SettingsTabHotkeysResScaleDownHotkey": "Diminuire la risoluzione:" + "AvatarWindowTitle": "Gestisci account - Avatar" + "Amiibo": "Amiibo", + "Unknown": "Sconosciuto", + "Usage": "Utilizzo", + "Writable": "Scrivibile", + "SelectDlcDialogTitle": "Seleziona file dei DLC", + "SelectUpdateDialogTitle": "Seleziona file di aggiornamento", + "UserProfileWindowTitle": "Gestisci profili degli utenti", + "CheatWindowTitle": "Gestisci cheat dei giochi", + "DlcWindowTitle": "Gestisci DLC dei giochi", + "UpdateWindowTitle": "Gestisci aggiornamenti dei giochi", + "CheatWindowHeading": "Cheat disponibiili per {0} [{1}]", + "DlcWindowHeading": "DLC disponibili per {0} [{1}]", + "UserProfilesEditProfile": "Modifica selezionati", + "Cancel": "Annulla", + "Save": "Salva", + "Discard": "Scarta", + "UserProfilesSetProfileImage": "Imposta immagine profilo", + "UserProfileEmptyNameError": "È richiesto un nome", + "UserProfileNoImageError": "Dev'essere impostata un'immagine profilo", + "GameUpdateWindowHeading": "Aggiornamenti disponibili per {0} [{1}]", + "UserProfilesName": "Name:", + "UserProfilesUserId": "User Id:", + "SettingsTabGraphicsBackend": "Backend grafica", + "SettingsTabGraphicsBackendTooltip": "Backend grafica da usare", + "SettingsEnableTextureRecompression": "Abilita Ricompressione Texture", + "SettingsEnableTextureRecompressionTooltip": "Comprime alcune texture per ridurre l'utilizzo della VRAM.\n\nL'utilizzo è consigliato con GPU con meno di 4GB di VRAM.\n\nLascia su OFF se non sei sicuro.", + "SettingsTabGraphicsPreferredGpu": "GPU preferita", + "SettingsTabGraphicsPreferredGpuTooltip": "Seleziona la scheda grafica che verrà usata con la backend grafica Vulkan.\n\nNon influenza la GPU che userà OpenGL.\n\nImposta la GPU contrassegnata come \"dGPU\" se non sei sicuro. Se non ce n'è una, lascia intatta quest'impostazione.", + "SettingsAppRequiredRestartMessage": "È richiesto un riavvio di Ryujinx", + "SettingsGpuBackendRestartMessage": "Le impostazioni della backend grafica o della GPU sono state modificate. Questo richiederà un riavvio perché le modifiche siano applicate", + "SettingsGpuBackendRestartSubMessage": "Vuoi riavviare ora?", + "RyujinxUpdaterMessage": "Vuoi aggiornare Ryujinx all'ultima versione?", + "SettingsTabHotkeysVolumeUpHotkey": "Aumentare il volume:", + "SettingsTabHotkeysVolumeDownHotkey": "Diminuire il volume:", + "VolumeShort": "Vol" } From 5fdc46ac7f6e0858c65bc6f30beb043faecccc05 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 21 Oct 2022 01:48:21 -0300 Subject: [PATCH 043/737] Vulkan: Fix vertex position Z conversion with geometry shader passthrough (#3781) * Vulkan: Fix vertex position Z conversion with geometry shader passthrough * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 2168c1859..86d453cc7 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3759; + private const uint CodeGenVersion = 3781; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index b18979d8f..227471601 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -306,7 +306,10 @@ namespace Ryujinx.Graphics.Shader.Translation config._perPatchAttributeLocations = locationsMap; } - if (config.Stage != ShaderStage.Fragment) + // We don't consider geometry shaders using the geometry shader passthrough feature + // as being the last because when this feature is used, it can't actually modify any of the outputs, + // so the stage that comes before it is the last one that can do modifications. + if (config.Stage != ShaderStage.Fragment && (config.Stage != ShaderStage.Geometry || !config.GpPassthrough)) { LastInVertexPipeline = false; } From d8e487d018b156270e6cfdbbeaacacd62d270e7f Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 21 Oct 2022 10:16:28 +0200 Subject: [PATCH 044/737] gha: Add dependabot.yml (#3778) --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..3369e3a8e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: weekly + labels: + - "infra" + reviewers: + - marysaka + commit-message: + prefix: "ci" From c7cf1cbc3521657d3fa1619f187df632561d2528 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 21 Oct 2022 10:31:38 +0200 Subject: [PATCH 045/737] CI: Update workflows (#3774) * ci: Update workflows * gha: Add no-build switch to test action --- .github/workflows/build.yml | 14 +++++++------- .github/workflows/nightly_pr_comment.yml | 2 +- .github/workflows/release.yml | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0bc13055..0ba0e01a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,21 +48,21 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: 1 RYUJINX_BASE_VERSION: "1.1.0" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-dotnet@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x - name: Ensure NuGet Source uses: fabriciomurta/ensure-nuget-source@v1 - name: Get git short hash id: git_short_hash - run: echo "::set-output name=result::$(git rev-parse --short "${{ github.sha }}")" + run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT - name: Clear run: dotnet clean && dotnet nuget locals all --clear - name: Build run: dotnet build -c "${{ matrix.configuration }}" /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER - name: Test - run: dotnet test -c "${{ matrix.configuration }}" + run: dotnet test --no-build -c "${{ matrix.configuration }}" - name: Publish Ryujinx run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained if: github.event_name == 'pull_request' @@ -73,19 +73,19 @@ jobs: run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava if: github.event_name == 'pull_request' - name: Upload Ryujinx artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish if: github.event_name == 'pull_request' - name: Upload Ryujinx.Headless.SDL2 artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_sdl2_headless if: github.event_name == 'pull_request' - name: Upload Ryujinx.Ava artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_ava diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index 0ab77b178..f6d52de18 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -8,7 +8,7 @@ jobs: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - - uses: actions/github-script@v3 + - uses: actions/github-script@v6 with: script: | const {owner, repo} = context.repo; diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e68ae3a61..af665d51c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,8 +25,8 @@ jobs: RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-dotnet@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x - name: Ensure NuGet Source @@ -36,8 +36,8 @@ jobs: - name: Get version info id: version_info run: | - echo "::set-output name=build_version::${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" - echo "::set-output name=git_short_hash::$(git rev-parse --short "${{ github.sha }}")" + echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT shell: bash - name: Configure for release run: | From dc529c11813410bd738afa753f58c24be699113d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 21 Oct 2022 14:23:16 +0200 Subject: [PATCH 046/737] ci: Add updates for nuget packages to dependabot (#3786) --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3369e3a8e..1516f8a7d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,3 +10,15 @@ updates: - marysaka commit-message: prefix: "ci" + + - package-ecosystem: nuget + directory: / + open-pull-requests-limit: 5 + schedule: + interval: daily + labels: + - "infra" + reviewers: + - marysaka + commit-message: + prefix: nuget From 286e5d39b28bdcf1615797d10091430d77c332a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Oct 2022 10:32:12 +0200 Subject: [PATCH 047/737] nuget: bump SPB from 0.0.4-build24 to 0.0.4-build27 (#3791) Bumps [SPB](https://github.com/Thog/SPB) from 0.0.4-build24 to 0.0.4-build27. - [Release notes](https://github.com/Thog/SPB/releases) - [Commits](https://github.com/Thog/SPB/commits) --- updated-dependencies: - dependency-name: SPB dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- Ryujinx/Ryujinx.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 7af50c548..551fa976f 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -34,7 +34,7 @@ - + diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 614a3b136..582f99f0f 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -23,7 +23,7 @@ - + From baba2c2467e59c52d391b0caec7dec4186d64d99 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Sun, 23 Oct 2022 09:15:45 +0000 Subject: [PATCH 048/737] Avalonia: Use overlay dialog for controller applet (#3777) * use overlay dialog for controller applet * Update Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs Co-authored-by: riperiperi Co-authored-by: riperiperi --- .../Ui/Controls/ContentDialogHelper.cs | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs index e774a09a0..9fcc2d2ec 100644 --- a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs +++ b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Primitives; using Avalonia.Media; using Avalonia.Threading; +using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Ui.Models; @@ -27,7 +28,10 @@ namespace Ryujinx.Ava.Ui.Controls string secondaryButton, string closeButton, int iconSymbol, - UserResult primaryButtonResult = UserResult.Ok) + UserResult primaryButtonResult = UserResult.Ok, + ManualResetEvent deferResetEvent = null, + Func doWhileDeferred = null, + TypedEventHandler deferCloseAction = null) { UserResult result = UserResult.None; @@ -110,12 +114,19 @@ namespace Ryujinx.Ava.Ui.Controls contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => { result = UserResult.No; + contentDialog.PrimaryButtonClick -= deferCloseAction; }); contentDialog.CloseButtonCommand = MiniCommand.Create(() => { result = UserResult.Cancel; + contentDialog.PrimaryButtonClick -= deferCloseAction; }); + if (deferResetEvent != null) + { + contentDialog.PrimaryButtonClick += deferCloseAction; + } + await contentDialog.ShowAsync(ContentDialogPlacement.Popup); overlay?.Close(); @@ -143,35 +154,20 @@ namespace Ryujinx.Ava.Ui.Controls Func doWhileDeferred = null) { bool startedDeferring = false; - UserResult result = UserResult.None; - ContentDialog contentDialog = new ContentDialog - { - Title = title, - PrimaryButtonText = primaryButton, - SecondaryButtonText = secondaryButton, - CloseButtonText = closeButton, - Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol), - PrimaryButtonCommand = MiniCommand.Create(() => - { - result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok; - }), - }; - contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => - { - contentDialog.PrimaryButtonClick -= DeferClose; - result = UserResult.No; - }); - contentDialog.CloseButtonCommand = MiniCommand.Create(() => - { - contentDialog.PrimaryButtonClick -= DeferClose; - result = UserResult.Cancel; - }); - contentDialog.PrimaryButtonClick += DeferClose; - await contentDialog.ShowAsync(ContentDialogPlacement.Popup); - - return result; + return await ShowContentDialog( + title, + primaryText, + secondaryText, + primaryButton, + secondaryButton, + closeButton, + iconSymbol, + primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok, + deferResetEvent, + doWhileDeferred, + DeferClose); async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args) { @@ -180,7 +176,7 @@ namespace Ryujinx.Ava.Ui.Controls return; } - contentDialog.PrimaryButtonClick -= DeferClose; + sender.PrimaryButtonClick -= DeferClose; startedDeferring = true; @@ -188,7 +184,7 @@ namespace Ryujinx.Ava.Ui.Controls result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok; - contentDialog.PrimaryButtonClick -= DeferClose; + sender.PrimaryButtonClick -= DeferClose; #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Task.Run(() => From 9b06ee7736993494c9b7f730f22283edf8e550b7 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Sun, 23 Oct 2022 17:15:15 +0200 Subject: [PATCH 049/737] Attempt to fix issues since github-script v6 upgrade --- .github/workflows/nightly_pr_comment.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index f6d52de18..bc3d1c43f 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -16,7 +16,7 @@ jobs: const pull_head_sha = '${{github.event.workflow_run.head_sha}}'; const issue_number = await (async () => { - const pulls = await github.pulls.list({owner, repo}); + const pulls = await github.rest.pulls.list({owner, repo}); for await (const {data} of github.paginate.iterator(pulls)) { for (const pull of data) { if (pull.head.sha === pull_head_sha) { @@ -31,7 +31,7 @@ jobs: return core.error(`No matching pull request found`); } - const {data: {artifacts}} = await github.actions.listWorkflowRunArtifacts({owner, repo, run_id}); + const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id}); if (!artifacts.length) { return core.error(`No artifacts found`); } @@ -57,12 +57,12 @@ jobs: body += hidden_headless_artifacts; body += hidden_debug_artifacts; - const {data: comments} = await github.issues.listComments({repo, owner, issue_number}); + const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number}); const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]'); if (existing_comment) { core.info(`Updating comment ${existing_comment.id}`); - await github.issues.updateComment({repo, owner, comment_id: existing_comment.id, body}); + await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body}); } else { core.info(`Creating a comment`); - await github.issues.createComment({repo, owner, issue_number, body}); + await github.rest.issues.createComment({repo, owner, issue_number, body}); } From eafadf10c7d8fe51ca6af11aadef64f5f6bcf8e0 Mon Sep 17 00:00:00 2001 From: merry Date: Mon, 24 Oct 2022 00:51:54 +0100 Subject: [PATCH 050/737] Ryujinx.Tests.Unicorn: Implement IDisposable (#3794) Dispose unicorn when done --- Ryujinx.Tests.Unicorn/UnicornAArch32.cs | 20 ++++++++++++++++++-- Ryujinx.Tests.Unicorn/UnicornAArch64.cs | 20 ++++++++++++++++++-- Ryujinx.Tests/Cpu/CpuTest.cs | 6 ++++++ Ryujinx.Tests/Cpu/CpuTest32.cs | 6 ++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs index e1efb52fe..e634e0d2a 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs @@ -3,9 +3,10 @@ using System; namespace Ryujinx.Tests.Unicorn { - public class UnicornAArch32 + public class UnicornAArch32 : IDisposable { internal readonly IntPtr uc; + private bool _isDisposed = false; public IndexedProperty R { @@ -107,7 +108,22 @@ namespace Ryujinx.Tests.Unicorn ~UnicornAArch32() { - Interface.Checked(Native.Interface.uc_close(uc)); + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + Interface.Checked(Native.Interface.uc_close(uc)); + _isDisposed = true; + } } public void RunForCount(ulong count) diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs index 4453d18d0..c5d5540b1 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs @@ -3,9 +3,10 @@ using System; namespace Ryujinx.Tests.Unicorn { - public class UnicornAArch64 + public class UnicornAArch64 : IDisposable { internal readonly IntPtr uc; + private bool _isDisposed = false; public IndexedProperty X { @@ -96,7 +97,22 @@ namespace Ryujinx.Tests.Unicorn ~UnicornAArch64() { - Interface.Checked(Native.Interface.uc_close(uc)); + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + Interface.Checked(Native.Interface.uc_close(uc)); + _isDisposed = true; + } } public void RunForCount(ulong count) diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 5fe43dec4..f983a03fb 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -80,6 +80,12 @@ namespace Ryujinx.Tests.Cpu [TearDown] public void Teardown() { + if (_unicornAvailable) + { + _unicornEmu.Dispose(); + _unicornEmu = null; + } + _memory.DecrementReferenceCount(); _context.Dispose(); _ram.Dispose(); diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs index aaf0ecfb6..2c36396f9 100644 --- a/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -76,6 +76,12 @@ namespace Ryujinx.Tests.Cpu [TearDown] public void Teardown() { + if (_unicornAvailable) + { + _unicornEmu.Dispose(); + _unicornEmu = null; + } + _memory.DecrementReferenceCount(); _context.Dispose(); _ram.Dispose(); From f70236f9477ccae43d3d44f91445a44c7cabb4de Mon Sep 17 00:00:00 2001 From: VocalFan <45863583+Mou-Ikkai@users.noreply.github.com> Date: Mon, 24 Oct 2022 12:40:39 -0400 Subject: [PATCH 051/737] Updated Compatibility info and GPU info (Vulkan, SPIRV, and Texture Recompression) (#3568) * Updated Compatibility info and GPU info (Vulkan, SPIRV, and Texture Recompression) * Added Mutant's changes. Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com> * Five to four. * Fixed github's terrible conflict diffs Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com> --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d5fef7721..3b7e802f9 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ ## Compatibility -As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). -Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already! +As of October 2022, Ryujinx has been tested on approximately 3,700 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. +You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already! ## Usage @@ -90,7 +90,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located - **GPU** - The GPU emulator emulates the Switch's Maxwell GPU using the OpenGL API (version 4.5 minimum) through a custom build of OpenTK. There are currently four graphics enhancements available to the end user in Ryujinx: disk shader caching, resolution scaling, aspect ratio adjustment and anisotropic filtering. These enhancements can be adjusted or toggled as desired in the GUI. + The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum) or Vulkan APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI. - **Input** From 9719b6a1129c017d96532ff026e2bb933c0b2d0b Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 26 Oct 2022 00:49:23 +0100 Subject: [PATCH 052/737] Vulkan: Use dynamic state for blend constants (#3793) --- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 9 ++--- Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 5 --- .../PipelineDynamicState.cs | 33 ++++++++++++++++--- Ryujinx.Graphics.Vulkan/PipelineState.cs | 5 +-- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 23abde414..7c6234b17 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -535,10 +535,11 @@ namespace Ryujinx.Graphics.Vulkan vkBlend = new PipelineColorBlendAttachmentState(); } - _newState.BlendConstantR = blend.BlendConstant.Red; - _newState.BlendConstantG = blend.BlendConstant.Green; - _newState.BlendConstantB = blend.BlendConstant.Blue; - _newState.BlendConstantA = blend.BlendConstant.Alpha; + DynamicState.SetBlendConstants( + blend.BlendConstant.Red, + blend.BlendConstant.Green, + blend.BlendConstant.Blue, + blend.BlendConstant.Alpha); SignalStateChange(); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 477d0cec1..55d29ffa0 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -135,11 +135,6 @@ namespace Ryujinx.Graphics.Vulkan // It is assumed that Dynamic State is enabled when this conversion is used. - pipeline.BlendConstantA = state.BlendDescriptors[0].BlendConstant.Alpha; - pipeline.BlendConstantB = state.BlendDescriptors[0].BlendConstant.Blue; - pipeline.BlendConstantG = state.BlendDescriptors[0].BlendConstant.Green; - pipeline.BlendConstantR = state.BlendDescriptors[0].BlendConstant.Red; - pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.CullModeNone; pipeline.DepthBoundsTestEnable = false; // Not implemented. diff --git a/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index 4f73b17b3..ae29be51c 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -19,21 +19,34 @@ namespace Ryujinx.Graphics.Vulkan private uint _frontWriteMask; private uint _frontReference; + private Array4 _blendConstants; + public int ViewportsCount; public Array16 Viewports; private enum DirtyFlags { None = 0, - DepthBias = 1 << 0, - Scissor = 1 << 1, - Stencil = 1 << 2, - Viewport = 1 << 3, - All = DepthBias | Scissor | Stencil | Viewport + Blend = 1 << 0, + DepthBias = 1 << 1, + Scissor = 1 << 2, + Stencil = 1 << 3, + Viewport = 1 << 4, + All = Blend | DepthBias | Scissor | Stencil | Viewport } private DirtyFlags _dirty; + public void SetBlendConstants(float r, float g, float b, float a) + { + _blendConstants[0] = r; + _blendConstants[1] = g; + _blendConstants[2] = b; + _blendConstants[3] = a; + + _dirty |= DirtyFlags.Blend; + } + public void SetDepthBias(float slopeFactor, float constantFactor, float clamp) { _depthBiasSlopeFactor = slopeFactor; @@ -87,6 +100,11 @@ namespace Ryujinx.Graphics.Vulkan public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer) { + if (_dirty.HasFlag(DirtyFlags.Blend)) + { + RecordBlend(api, commandBuffer); + } + if (_dirty.HasFlag(DirtyFlags.DepthBias)) { RecordDepthBias(api, commandBuffer); @@ -110,6 +128,11 @@ namespace Ryujinx.Graphics.Vulkan _dirty = DirtyFlags.None; } + private void RecordBlend(Vk api, CommandBuffer commandBuffer) + { + api.CmdSetBlendConstants(commandBuffer, _blendConstants.AsSpan()); + } + private void RecordDepthBias(Vk api, CommandBuffer commandBuffer) { api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor); diff --git a/Ryujinx.Graphics.Vulkan/PipelineState.cs b/Ryujinx.Graphics.Vulkan/PipelineState.cs index 15c4d79e0..d33bc8ce8 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -499,7 +499,7 @@ namespace Ryujinx.Graphics.Vulkan colorBlendState.BlendConstants[3] = BlendConstantA; bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState; - int dynamicStatesCount = supportsExtDynamicState ? 8 : 7; + int dynamicStatesCount = supportsExtDynamicState ? 9 : 8; DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount]; @@ -510,10 +510,11 @@ namespace Ryujinx.Graphics.Vulkan dynamicStates[4] = DynamicState.StencilCompareMask; dynamicStates[5] = DynamicState.StencilWriteMask; dynamicStates[6] = DynamicState.StencilReference; + dynamicStates[7] = DynamicState.BlendConstants; if (supportsExtDynamicState) { - dynamicStates[7] = DynamicState.VertexInputBindingStrideExt; + dynamicStates[8] = DynamicState.VertexInputBindingStrideExt; } var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo() From 28ba55598df384c4b1e6892c49e95376d2b582fb Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 26 Oct 2022 14:53:11 -0300 Subject: [PATCH 053/737] Vulkan: Fix indirect buffer barrier (#3798) --- Ryujinx.Graphics.Vulkan/BufferHolder.cs | 5 ++--- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs index fb1332662..4660765a4 100644 --- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -12,13 +12,12 @@ namespace Ryujinx.Graphics.Vulkan private const int MaxUpdateBufferSize = 0x10000; public const AccessFlags DefaultAccessFlags = + AccessFlags.AccessIndirectCommandReadBit | AccessFlags.AccessShaderReadBit | AccessFlags.AccessShaderWriteBit | AccessFlags.AccessTransferReadBit | AccessFlags.AccessTransferWriteBit | - AccessFlags.AccessUniformReadBit | - AccessFlags.AccessShaderReadBit | - AccessFlags.AccessShaderWriteBit; + AccessFlags.AccessUniformReadBit; private readonly VulkanRenderer _gd; private readonly Device _device; diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 7c6234b17..2a376261e 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -228,10 +228,26 @@ namespace Ryujinx.Graphics.Vulkan Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } - public void CommandBufferBarrier() + public unsafe void CommandBufferBarrier() { - // TODO: More specific barrier? - Barrier(); + MemoryBarrier memoryBarrier = new MemoryBarrier() + { + SType = StructureType.MemoryBarrier, + SrcAccessMask = BufferHolder.DefaultAccessFlags, + DstAccessMask = AccessFlags.AccessIndirectCommandReadBit + }; + + Gd.Api.CmdPipelineBarrier( + CommandBuffer, + PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.PipelineStageDrawIndirectBit, + 0, + 1, + memoryBarrier, + 0, + null, + 0, + null); } public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) @@ -824,7 +840,7 @@ namespace Ryujinx.Graphics.Vulkan if (range.Handle != BufferHandle.Null) { - _transformFeedbackBuffers[i] = + _transformFeedbackBuffers[i] = new BufferState(Gd.BufferManager.GetBuffer(CommandBuffer, range.Handle, range.Offset, range.Size, true), range.Offset, range.Size); _transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i); } From f7c7b66fc0156e031eef1b37e41825c54ce29ff4 Mon Sep 17 00:00:00 2001 From: Luke Warner <65521430+LukeWarnut@users.noreply.github.com> Date: Thu, 27 Oct 2022 14:36:37 -0700 Subject: [PATCH 054/737] hid/irs: Stub StopImageProcessorAsync (#3799) * Update IIrSensorServer.cs * Update IIrSensorServer.cs * Apply suggestions from code review Addressed formatting feedback Co-authored-by: Ac_K * Update IIrSensorServer.cs Co-authored-by: Ac_K --- .../HOS/Services/Hid/Irs/IIrSensorServer.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs index 9efb679ac..a0bd63752 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs @@ -203,6 +203,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } + [CommandHipc(318)] // 4.0.0+ + // StopImageProcessorAsync(nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, pid) + public ResultCode StopImageProcessorAsync(ServiceCtx context) + { + int irCameraHandle = context.RequestData.ReadInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); + + Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle }); + + return ResultCode.Success; + } + [CommandHipc(319)] // 4.0.0+ // ActivateIrsensorWithFunctionLevel(nn::applet::AppletResourceUserId, nn::irsensor::PackedFunctionLevel, pid) public ResultCode ActivateIrsensorWithFunctionLevel(ServiceCtx context) @@ -225,4 +237,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } } -} \ No newline at end of file +} From d540af5dc0d45fc5057a99190fe3e896f6a01c19 Mon Sep 17 00:00:00 2001 From: Luke Warner <65521430+LukeWarnut@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:15:57 -0700 Subject: [PATCH 055/737] AppletAE: stub SetRecordVolumeMuted (#3804) * Update IIrSensorServer.cs * Update IIrSensorServer.cs * Apply suggestions from code review Addressed formatting feedback Co-authored-by: Ac_K * Update IIrSensorServer.cs * Update ISelfController.cs * Update ISelfController.cs Co-authored-by: Ac_K --- .../SystemAppletProxy/ISelfController.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 7f0f66ea7..e8bbf748b 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -32,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private bool _handlesRequestToDisplay = false; private bool _autoSleepDisabled = false; private bool _albumImageTakenNotificationEnabled = false; + private bool _recordVolumeMuted = false; private uint _screenShotImageOrientation = 0; private uint _idleTimeDetectionExtension = 0; @@ -389,5 +390,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + + [CommandHipc(130)] // 13.0.0+ + // SetRecordVolumeMuted(b8) + public ResultCode SetRecordVolumeMuted(ServiceCtx context) + { + bool recordVolumeMuted = context.RequestData.ReadBoolean(); + + Logger.Stub?.PrintStub(LogClass.ServiceAm, new { recordVolumeMuted }); + + _recordVolumeMuted = recordVolumeMuted; + + return ResultCode.Success; + } } -} \ No newline at end of file +} From 4e34170a84fc1b2096ad4588dec9460a5f8c9870 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 Oct 2022 10:46:46 +0200 Subject: [PATCH 056/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.15.0 to 6.25.0 (#3806) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.25.0. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index 91d82755c..c04ba7cf4 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -26,7 +26,7 @@ - + From 59cdf310bdc16d537ba5ff3813399c54abbce2b7 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 29 Oct 2022 13:45:30 -0300 Subject: [PATCH 057/737] SPIR-V: Fix tessellation control shader output types (#3807) * SPIR-V: Fix tessellation control shader output types * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Spirv/CodeGenContext.cs | 59 +++++++++++++++++-- .../CodeGen/Spirv/Declarations.cs | 15 +++++ .../StructuredIr/StructuredProgramContext.cs | 7 ++- 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 86d453cc7..02ae06f65 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3781; + private const uint CodeGenVersion = 3807; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index fe5e11f4e..04c053253 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -262,6 +262,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Instruction ioVariable, elemIndex; + Instruction invocationId = null; + + if (Config.Stage == ShaderStage.TessellationControl && isOutAttr) + { + invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]); + } + bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; if (isUserAttr && @@ -273,7 +280,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4); - if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr)) + bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr); + + if (invocationId != null && isArray) + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex); + } + else if (invocationId != null) + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex); + } + else if (isArray) { return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex); } @@ -308,12 +325,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if ((type & (AggregateType.Array | AggregateType.Vector)) == 0) { - return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable; + if (invocationId != null) + { + return isIndexed + ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index) + : AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId); + } + else + { + return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable; + } } elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); - if (isIndexed) + if (invocationId != null && isIndexed) + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, elemIndex); + } + else if (invocationId != null) + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, elemIndex); + } + else if (isIndexed) { return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex); } @@ -327,12 +361,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; + Instruction invocationId = null; + + if (Config.Stage == ShaderStage.TessellationControl && isOutAttr) + { + invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]); + } + elemType = AggregateType.FP32; var ioVariable = isOutAttr ? OutputsArray : InputsArray; var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2)); var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3)); - if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr)) + bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr); + + if (invocationId != null && isArray) + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex); + } + else if (invocationId != null) + { + return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex); + } + else if (isArray) { return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 1a4decf5c..9f8dd7dfa 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -473,6 +473,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); + if (context.Config.Stage == ShaderStage.TessellationControl) + { + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + } + var spvType = context.TypePointer(StorageClass.Output, attrType); var spvVar = context.Variable(spvType, StorageClass.Output); @@ -543,6 +548,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } + if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch) + { + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + } + var spvType = context.TypePointer(storageClass, attrType); var spvVar = context.Variable(spvType, storageClass); @@ -634,6 +644,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); } + if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr) + { + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + } + var spvType = context.TypePointer(storageClass, attrType); var spvVar = context.Variable(spvType, storageClass); diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index 4d2fdb54d..e9f8467d5 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -37,7 +37,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Config = config; - if (config.GpPassthrough) + if (config.Stage == ShaderStage.TessellationControl) + { + // Required to index outputs. + Info.Inputs.Add(AttributeConsts.InvocationId); + } + else if (config.GpPassthrough) { int passthroughAttributes = config.PassthroughAttributes; while (passthroughAttributes != 0) From 3fe3598d41a1d673f917fa30bed04b2ed19e0c82 Mon Sep 17 00:00:00 2001 From: Wunk Date: Sat, 29 Oct 2022 10:09:25 -0700 Subject: [PATCH 058/737] Vulkan: Replace `VK_EXT_debug_report` usage with `VK_EXT_debug_utils` (#3802) * Vulkan: Replace `VK_EXT_debug_report` usage with `VK_EXT_debug_utils` [VK_EXT_debug_report](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_report.html) has been depreciated for quite some time now in favor of the much more featureful [VK_EXT_debug_utils](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html) extension. This PR converts our debug-report-callback into the newer debug-messenger pattern. `VK_EXT_debug_utils` adds some additional diagnostic tooling for marking debug-label scopes for queue-operations, command-buffers, and assigning name-labels to vulkan objects to aid in debugging(for a later PR). * Vulkan: Fix `DebugMessenger` severity-flag classification Extension bits between the two flags, for reference: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugUtilsMessageSeverityFlagBitsEXT.html https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugReportFlagBitsEXT.html --- .../VulkanInitialization.cs | 89 ++++++++++--------- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 12 +-- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 54d98386d..5d8accd27 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan "VUID-VkSubpassDependency-srcSubpass-00867" }; - internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugReport debugReport, out DebugReportCallbackEXT debugReportCallback) + internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger) { var enabledLayers = new List(); @@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Vulkan AddAvailableLayer("VK_LAYER_KHRONOS_validation"); } - var enabledExtensions = requiredExtensions.Append(ExtDebugReport.ExtensionName).ToArray(); + var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); var appName = Marshal.StringToHGlobalAnsi(AppName); @@ -139,22 +139,18 @@ namespace Ryujinx.Graphics.Vulkan Marshal.FreeHGlobal(ppEnabledLayers[i]); } - CreateDebugCallbacks(api, logLevel, instance, out debugReport, out debugReportCallback); + CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger); return instance; } - private unsafe static uint DebugReport( - uint flags, - DebugReportObjectTypeEXT objectType, - ulong @object, - nuint location, - int messageCode, - byte* layerPrefix, - byte* message, - void* userData) + private unsafe static uint DebugMessenger( + DebugUtilsMessageSeverityFlagsEXT messageSeverity, + DebugUtilsMessageTypeFlagsEXT messageTypes, + DebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) { - var msg = Marshal.PtrToStringAnsi((IntPtr)message); + var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage); foreach (string excludedMessagePart in _excludedMessages) { @@ -164,26 +160,20 @@ namespace Ryujinx.Graphics.Vulkan } } - DebugReportFlagsEXT debugFlags = (DebugReportFlagsEXT)flags; - - if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportErrorBitExt)) + if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt)) { Logger.Error?.Print(LogClass.Gpu, msg); //throw new Exception(msg); } - else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportWarningBitExt)) + else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt)) { Logger.Warning?.Print(LogClass.Gpu, msg); } - else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportInformationBitExt)) + else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt)) { Logger.Info?.Print(LogClass.Gpu, msg); } - else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt)) - { - Logger.Warning?.Print(LogClass.Gpu, msg); - } - else + else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt)) { Logger.Debug?.Print(LogClass.Gpu, msg); } @@ -551,46 +541,59 @@ namespace Ryujinx.Graphics.Vulkan return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex); } - internal unsafe static void CreateDebugCallbacks( + internal unsafe static void CreateDebugMessenger( Vk api, GraphicsDebugLevel logLevel, Instance instance, - out ExtDebugReport debugReport, - out DebugReportCallbackEXT debugReportCallback) + out ExtDebugUtils debugUtils, + out DebugUtilsMessengerEXT debugUtilsMessenger) { - debugReport = default; + debugUtils = default; if (logLevel != GraphicsDebugLevel.None) { - if (!api.TryGetInstanceExtension(instance, out debugReport)) + if (!api.TryGetInstanceExtension(instance, out debugUtils)) { - debugReportCallback = default; + debugUtilsMessenger = default; return; } - var flags = logLevel switch + var filterLogType = logLevel switch { - GraphicsDebugLevel.Error => DebugReportFlagsEXT.DebugReportErrorBitExt, - GraphicsDebugLevel.Slowdowns => DebugReportFlagsEXT.DebugReportErrorBitExt | DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt, - GraphicsDebugLevel.All => DebugReportFlagsEXT.DebugReportInformationBitExt | - DebugReportFlagsEXT.DebugReportWarningBitExt | - DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt | - DebugReportFlagsEXT.DebugReportErrorBitExt | - DebugReportFlagsEXT.DebugReportDebugBitExt, + GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt, + GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt | + DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt, + GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt | + DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt | + DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt, _ => throw new ArgumentException($"Invalid log level \"{logLevel}\".") }; - var debugReportCallbackCreateInfo = new DebugReportCallbackCreateInfoEXT() + + var filterLogSeverity = logLevel switch { - SType = StructureType.DebugReportCallbackCreateInfoExt, - Flags = flags, - PfnCallback = new PfnDebugReportCallbackEXT(DebugReport) + GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt, + GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt | + DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt, + GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt | + DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt | + DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt | + DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt, + _ => throw new ArgumentException($"Invalid log level \"{logLevel}\".") }; - debugReport.CreateDebugReportCallback(instance, in debugReportCallbackCreateInfo, null, out debugReportCallback).ThrowOnError(); + var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT() + { + SType = StructureType.DebugUtilsMessengerCreateInfoExt, + MessageType = filterLogType, + MessageSeverity = filterLogSeverity, + PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger) + }; + + debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError(); } else { - debugReportCallback = default; + debugUtilsMessenger = default; } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 96c7da6c7..ef089c32e 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } - internal ExtDebugReport DebugReportApi { get; private set; } + internal ExtDebugUtils DebugUtilsApi { get; private set; } internal uint QueueFamilyIndex { get; private set; } internal Queue Queue { get; private set; } @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Vulkan private SyncManager _syncManager; private PipelineFull _pipeline; - private DebugReportCallbackEXT _debugReportCallback; + private DebugUtilsMessengerEXT _debugUtilsMessenger; internal HelperShader HelperShader { get; private set; } internal PipelineFull PipelineInternal => _pipeline; @@ -237,9 +237,9 @@ namespace Ryujinx.Graphics.Vulkan Api = api; - _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugReport debugReport, out _debugReportCallback); + _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugUtils debugUtils, out _debugUtilsMessenger); - DebugReportApi = debugReport; + DebugUtilsApi = debugUtils; if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi)) { @@ -584,9 +584,9 @@ namespace Ryujinx.Graphics.Vulkan MemoryAllocator.Dispose(); - if (_debugReportCallback.Handle != 0) + if (_debugUtilsMessenger.Handle != 0) { - DebugReportApi.DestroyDebugReportCallback(_instance, _debugReportCallback, null); + DebugUtilsApi.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger, null); } foreach (var shader in Shaders) From 141cf61ff72224c394cab4e5e252907d0cdb797a Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 29 Oct 2022 21:00:08 +0200 Subject: [PATCH 059/737] CI: Run git_short_hash inside of bash (#3808) --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ba0e01a3..6feb14b84 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,6 +57,7 @@ jobs: - name: Get git short hash id: git_short_hash run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT + shell: bash - name: Clear run: dotnet clean && dotnet nuget locals all --clear - name: Build From 3d98e1361b2f1c3993aa7f1e9b1ac49fc5b6e512 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 29 Oct 2022 23:07:37 +0100 Subject: [PATCH 060/737] GPU: Use a bitmap to track buffer modified flags. (#3775) * Initial implementation * Some improvements. * Fix incorrect cast * Performance improvement and improved correctness * Add very fast path when all handles are checked. * Slightly faster * Add comment * De-virtualize region handle All region handles are now bitmap backed. * Remove non-bitmap tracking * Remove unused methods * Add docs, remove unused methods * Address Feedback * Rename file --- Ryujinx.Memory/Tracking/BitMap.cs | 199 ++++++++++++ Ryujinx.Memory/Tracking/ConcurrentBitmap.cs | 161 ++++++++++ Ryujinx.Memory/Tracking/MemoryTracking.cs | 20 ++ Ryujinx.Memory/Tracking/MultiRegionHandle.cs | 304 +++++++++++++++---- Ryujinx.Memory/Tracking/RegionHandle.cs | 86 +++++- 5 files changed, 696 insertions(+), 74 deletions(-) create mode 100644 Ryujinx.Memory/Tracking/BitMap.cs create mode 100644 Ryujinx.Memory/Tracking/ConcurrentBitmap.cs diff --git a/Ryujinx.Memory/Tracking/BitMap.cs b/Ryujinx.Memory/Tracking/BitMap.cs new file mode 100644 index 000000000..d3f15994f --- /dev/null +++ b/Ryujinx.Memory/Tracking/BitMap.cs @@ -0,0 +1,199 @@ +using System.Runtime.CompilerServices; + +namespace Ryujinx.Memory.Tracking +{ + /// + /// A bitmap that can check or set large ranges of true/false values at once. + /// + struct BitMap + { + public const int IntSize = 64; + + private const int IntShift = 6; + private const int IntMask = IntSize - 1; + + /// + /// Masks representing the bitmap. Least significant bit first, 64-bits per mask. + /// + public readonly long[] Masks; + + /// + /// Create a new bitmap. + /// + /// The number of bits to reserve + public BitMap(int count) + { + Masks = new long[(count + IntMask) / IntSize]; + } + + /// + /// Check if any bit in the bitmap is set. + /// + /// True if any bits are set, false otherwise + public bool AnySet() + { + for (int i = 0; i < Masks.Length; i++) + { + if (Masks[i] != 0) + { + return true; + } + } + + return false; + } + + /// + /// Check if a bit in the bitmap is set. + /// + /// The bit index to check + /// True if the bit is set, false otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsSet(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + return (Masks[wordIndex] & wordMask) != 0; + } + + /// + /// Check if any bit in a range of bits in the bitmap are set. (inclusive) + /// + /// The first bit index to check + /// The last bit index to check + /// True if a bit is set, false otherwise + 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; + } + + /// + /// Set a bit at a specific index to 1. + /// + /// The bit index to set + /// True if the bit is set, false if it was already set + 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; + } + + /// + /// Set a range of bits in the bitmap to 1. + /// + /// The first bit index to set + /// The last bit index to set + 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; + } + } + + /// + /// Clear a bit at a specific index to 0. + /// + /// The bit index to clear + /// True if the bit was set, false if it was not + public bool Clear(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + bool wasSet = (Masks[wordIndex] & wordMask) != 0; + + Masks[wordIndex] &= ~wordMask; + + return wasSet; + } + + /// + /// Clear the bitmap entirely, setting all bits to 0. + /// + public void Clear() + { + for (int i = 0; i < Masks.Length; i++) + { + Masks[i] = 0; + } + } + } +} diff --git a/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs b/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs new file mode 100644 index 000000000..2e007bb56 --- /dev/null +++ b/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs @@ -0,0 +1,161 @@ +using System; +using System.Threading; + +namespace Ryujinx.Memory.Tracking +{ + /// + /// A bitmap that can be safely modified from multiple threads. + /// + internal class ConcurrentBitmap + { + public const int IntSize = 64; + + public const int IntShift = 6; + public const int IntMask = IntSize - 1; + + /// + /// Masks representing the bitmap. Least significant bit first, 64-bits per mask. + /// + public readonly long[] Masks; + + /// + /// Create a new multithreaded bitmap. + /// + /// The number of bits to reserve + /// Whether the bits should be initially set or not + public ConcurrentBitmap(int count, bool set) + { + Masks = new long[(count + IntMask) / IntSize]; + + if (set) + { + Array.Fill(Masks, -1L); + } + } + + /// + /// Check if any bit in the bitmap is set. + /// + /// True if any bits are set, false otherwise + public bool AnySet() + { + for (int i = 0; i < Masks.Length; i++) + { + if (Volatile.Read(ref Masks[i]) != 0) + { + return true; + } + } + + return false; + } + + /// + /// Check if a bit in the bitmap is set. + /// + /// The bit index to check + /// True if the bit is set, false otherwise + public bool IsSet(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + return (Volatile.Read(ref Masks[wordIndex]) & wordMask) != 0; + } + + /// + /// Check if any bit in a range of bits in the bitmap are set. (inclusive) + /// + /// The first bit index to check + /// The last bit index to check + /// True if a bit is set, false otherwise + 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)); + + long startValue = Volatile.Read(ref Masks[startIndex]); + + if (startIndex == endIndex) + { + return (startValue & startMask & endMask) != 0; + } + + if ((startValue & startMask) != 0) + { + return true; + } + + for (int i = startIndex + 1; i < endIndex; i++) + { + if (Volatile.Read(ref Masks[i]) != 0) + { + return true; + } + } + + long endValue = Volatile.Read(ref Masks[endIndex]); + + if ((endValue & endMask) != 0) + { + return true; + } + + return false; + } + + /// + /// Set a bit at a specific index to either true or false. + /// + /// The bit index to set + /// Whether the bit should be set or not + public void Set(int bit, bool value) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + long existing; + long newValue; + + do + { + existing = Volatile.Read(ref Masks[wordIndex]); + + if (value) + { + newValue = existing | wordMask; + } + else + { + newValue = existing & ~wordMask; + } + } + while (Interlocked.CompareExchange(ref Masks[wordIndex], newValue, existing) != existing); + } + + /// + /// Clear the bitmap entirely, setting all bits to 0. + /// + public void Clear() + { + for (int i = 0; i < Masks.Length; i++) + { + Volatile.Write(ref Masks[i], 0); + } + } + } +} diff --git a/Ryujinx.Memory/Tracking/MemoryTracking.cs b/Ryujinx.Memory/Tracking/MemoryTracking.cs index f2ac17ff9..9aa7c7ff3 100644 --- a/Ryujinx.Memory/Tracking/MemoryTracking.cs +++ b/Ryujinx.Memory/Tracking/MemoryTracking.cs @@ -176,6 +176,26 @@ namespace Ryujinx.Memory.Tracking } } + /// + /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. + /// + /// CPU virtual address of the region + /// Size of the region + /// The bitmap owning the dirty flag for this handle + /// The bit of this handle within the dirty flag + /// The memory tracking handle + internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit) + { + (address, size) = PageAlign(address, size); + + lock (TrackingLock) + { + RegionHandle handle = new RegionHandle(this, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size)); + + return handle; + } + } + /// /// Signal that a virtual memory event happened at the given location (one byte). /// diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs index cce7ccb4c..45138ff35 100644 --- a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs @@ -1,11 +1,15 @@ using System; using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Threading; namespace Ryujinx.Memory.Tracking { /// /// A region handle that tracks a large region using many smaller handles, to provide - /// granular tracking that can be used to track partial updates. + /// granular tracking that can be used to track partial updates. Backed by a bitmap + /// to improve performance when scanning large regions. /// public class MultiRegionHandle : IMultiRegionHandle { @@ -17,6 +21,12 @@ namespace Ryujinx.Memory.Tracking private readonly ulong Granularity; private readonly ulong Size; + private ConcurrentBitmap _dirtyBitmap; + + private int _sequenceNumber; + private BitMap _sequenceNumberBitmap; + private int _uncheckedHandles; + public bool Dirty { get; private set; } = true; internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable handles, ulong granularity) @@ -24,6 +34,9 @@ namespace Ryujinx.Memory.Tracking _handles = new RegionHandle[size / granularity]; Granularity = granularity; + _dirtyBitmap = new ConcurrentBitmap(_handles.Length, true); + _sequenceNumberBitmap = new BitMap(_handles.Length); + int i = 0; if (handles != null) @@ -40,37 +53,43 @@ namespace Ryujinx.Memory.Tracking // Fill any gap left before this handle. while (i < startIndex) { - RegionHandle fillHandle = tracking.BeginTracking(address + (ulong)i * granularity, granularity); + RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); fillHandle.Parent = this; _handles[i++] = fillHandle; } - if (handle.Size == granularity) + lock (tracking.TrackingLock) { - handle.Parent = this; - _handles[i++] = handle; - } - else - { - int endIndex = (int)((handle.EndAddress - address) / granularity); - - while (i < endIndex) + if (handle is RegionHandle bitHandle && handle.Size == granularity) { - RegionHandle splitHandle = tracking.BeginTracking(address + (ulong)i * granularity, granularity); - splitHandle.Parent = this; + handle.Parent = this; - splitHandle.Reprotect(handle.Dirty); + bitHandle.ReplaceBitmap(_dirtyBitmap, i); - RegionSignal signal = handle.PreAction; - if (signal != null) + _handles[i++] = bitHandle; + } + else + { + int endIndex = (int)((handle.EndAddress - address) / granularity); + + while (i < endIndex) { - splitHandle.RegisterAction(signal); + RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); + splitHandle.Parent = this; + + splitHandle.Reprotect(handle.Dirty); + + RegionSignal signal = handle.PreAction; + if (signal != null) + { + splitHandle.RegisterAction(signal); + } + + _handles[i++] = splitHandle; } - _handles[i++] = splitHandle; + handle.Dispose(); } - - handle.Dispose(); } } } @@ -78,15 +97,27 @@ namespace Ryujinx.Memory.Tracking // Fill any remaining space with new handles. while (i < _handles.Length) { - RegionHandle handle = tracking.BeginTracking(address + (ulong)i * granularity, granularity); + RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); handle.Parent = this; _handles[i++] = handle; } + _uncheckedHandles = _handles.Length; + Address = address; Size = size; } + public void SignalWrite() + { + Dirty = true; + } + + public IEnumerable GetHandles() + { + return _handles; + } + public void ForceDirty(ulong address, ulong size) { Dirty = true; @@ -96,21 +127,15 @@ namespace Ryujinx.Memory.Tracking for (int i = startHandle; i <= lastHandle; i++) { - _handles[i].SequenceNumber--; + if (_sequenceNumberBitmap.Clear(i)) + { + _uncheckedHandles++; + } + _handles[i].ForceDirty(); } } - public IEnumerable GetHandles() - { - return _handles; - } - - public void SignalWrite() - { - Dirty = true; - } - public void QueryModified(Action modifiedAction) { if (!Dirty) @@ -123,33 +148,95 @@ namespace Ryujinx.Memory.Tracking QueryModified(Address, Size, modifiedAction); } - public void QueryModified(ulong address, ulong size, Action modifiedAction) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ParseDirtyBits(long dirtyBits, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action modifiedAction) { - int startHandle = (int)((address - Address) / Granularity); - int lastHandle = (int)((address + (size - 1) - Address) / Granularity); - - ulong rgStart = _handles[startHandle].Address; - ulong rgSize = 0; - - for (int i = startHandle; i <= lastHandle; i++) + while (dirtyBits != 0) { - RegionHandle handle = _handles[i]; + int bit = BitOperations.TrailingZeroCount(dirtyBits); + + dirtyBits &= ~(1L << bit); + + int handleIndex = baseBit + bit; + + RegionHandle handle = _handles[handleIndex]; + + if (handleIndex != prevHandle + 1) + { + // Submit handles scanned until the gap as dirty + if (rgSize != 0) + { + modifiedAction(rgStart, rgSize); + rgSize = 0; + } + rgStart = handle.Address; + } if (handle.Dirty) { rgSize += handle.Size; handle.Reprotect(); } - else + + prevHandle = handleIndex; + } + + baseBit += ConcurrentBitmap.IntSize; + } + + public void QueryModified(ulong address, ulong size, Action modifiedAction) + { + int startHandle = (int)((address - Address) / Granularity); + int lastHandle = (int)((address + (size - 1) - Address) / Granularity); + + ulong rgStart = _handles[startHandle].Address; + + if (startHandle == lastHandle) + { + RegionHandle handle = _handles[startHandle]; + + if (handle.Dirty) { - // Submit the region scanned so far as dirty - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - rgSize = 0; - } - rgStart = handle.EndAddress; + handle.Reprotect(); + modifiedAction(rgStart, handle.Size); } + + return; + } + + ulong rgSize = 0; + + long[] masks = _dirtyBitmap.Masks; + + int startIndex = startHandle >> ConcurrentBitmap.IntShift; + int startBit = startHandle & ConcurrentBitmap.IntMask; + long startMask = -1L << startBit; + + int endIndex = lastHandle >> ConcurrentBitmap.IntShift; + int endBit = lastHandle & ConcurrentBitmap.IntMask; + long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit)); + + long startValue = Volatile.Read(ref masks[startIndex]); + + int baseBit = startIndex << ConcurrentBitmap.IntShift; + int prevHandle = startHandle - 1; + + if (startIndex == endIndex) + { + ParseDirtyBits(startValue & startMask & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + } + else + { + ParseDirtyBits(startValue & startMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + + for (int i = startIndex + 1; i < endIndex; i++) + { + ParseDirtyBits(Volatile.Read(ref masks[i]), ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + } + + long endValue = Volatile.Read(ref masks[endIndex]); + + ParseDirtyBits(endValue & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); } if (rgSize != 0) @@ -158,35 +245,120 @@ namespace Ryujinx.Memory.Tracking } } - public void QueryModified(ulong address, ulong size, Action modifiedAction, int sequenceNumber) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action modifiedAction) { - int startHandle = (int)((address - Address) / Granularity); - int lastHandle = (int)((address + (size - 1) - Address) / Granularity); + long seqMask = mask & ~seqMasks[index]; + dirtyBits &= seqMask; - ulong rgStart = _handles[startHandle].Address; - ulong rgSize = 0; - - for (int i = startHandle; i <= lastHandle; i++) + while (dirtyBits != 0) { - RegionHandle handle = _handles[i]; + int bit = BitOperations.TrailingZeroCount(dirtyBits); - if (sequenceNumber != handle.SequenceNumber && handle.DirtyOrVolatile()) + dirtyBits &= ~(1L << bit); + + int handleIndex = baseBit + bit; + + RegionHandle handle = _handles[handleIndex]; + + if (handleIndex != prevHandle + 1) { - rgSize += handle.Size; - handle.Reprotect(); - } - else - { - // Submit the region scanned so far as dirty + // Submit handles scanned until the gap as dirty if (rgSize != 0) { modifiedAction(rgStart, rgSize); rgSize = 0; } - rgStart = handle.EndAddress; + rgStart = handle.Address; } - handle.SequenceNumber = sequenceNumber; + rgSize += handle.Size; + handle.Reprotect(); + + prevHandle = handleIndex; + } + + seqMasks[index] |= mask; + _uncheckedHandles -= BitOperations.PopCount((ulong)seqMask); + + baseBit += ConcurrentBitmap.IntSize; + } + + public void QueryModified(ulong address, ulong size, Action modifiedAction, int sequenceNumber) + { + int startHandle = (int)((address - Address) / Granularity); + int lastHandle = (int)((address + (size - 1) - Address) / Granularity); + + ulong rgStart = Address + (ulong)startHandle * Granularity; + + if (sequenceNumber != _sequenceNumber) + { + if (_uncheckedHandles != _handles.Length) + { + _sequenceNumberBitmap.Clear(); + _uncheckedHandles = _handles.Length; + } + + _sequenceNumber = sequenceNumber; + } + + if (startHandle == lastHandle) + { + var handle = _handles[startHandle]; + if (_sequenceNumberBitmap.Set(startHandle)) + { + _uncheckedHandles--; + + if (handle.DirtyOrVolatile()) + { + handle.Reprotect(); + + modifiedAction(rgStart, handle.Size); + } + } + + return; + } + + if (_uncheckedHandles == 0) + { + return; + } + + ulong rgSize = 0; + + long[] seqMasks = _sequenceNumberBitmap.Masks; + long[] masks = _dirtyBitmap.Masks; + + int startIndex = startHandle >> ConcurrentBitmap.IntShift; + int startBit = startHandle & ConcurrentBitmap.IntMask; + long startMask = -1L << startBit; + + int endIndex = lastHandle >> ConcurrentBitmap.IntShift; + int endBit = lastHandle & ConcurrentBitmap.IntMask; + long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit)); + + long startValue = Volatile.Read(ref masks[startIndex]); + + int baseBit = startIndex << ConcurrentBitmap.IntShift; + int prevHandle = startHandle - 1; + + if (startIndex == endIndex) + { + ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + } + else + { + ParseDirtyBits(startValue, startMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + + for (int i = startIndex + 1; i < endIndex; i++) + { + ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + } + + long endValue = Volatile.Read(ref masks[endIndex]); + + ParseDirtyBits(endValue, endMask, endIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); } if (rgSize != 0) diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs index 14c6de2c9..363bedef0 100644 --- a/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -1,5 +1,4 @@ -using Ryujinx.Memory.Range; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -10,7 +9,7 @@ namespace Ryujinx.Memory.Tracking /// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made, /// and an action can be performed when the region is read to or written from. /// - public class RegionHandle : IRegionHandle, IRange + public class RegionHandle : IRegionHandle { /// /// If more than this number of checks have been performed on a dirty flag since its last reprotect, @@ -23,7 +22,20 @@ namespace Ryujinx.Memory.Tracking /// private static int VolatileThreshold = 5; - public bool Dirty { get; private set; } + public bool Dirty + { + get + { + return Bitmap.IsSet(DirtyBit); + } + protected set + { + Bitmap.Set(DirtyBit, value); + } + } + + internal int SequenceNumber { get; set; } + public bool Unmapped { get; private set; } public ulong Address { get; } @@ -31,7 +43,6 @@ namespace Ryujinx.Memory.Tracking public ulong EndAddress { get; } internal IMultiRegionHandle Parent { get; set; } - internal int SequenceNumber { get; set; } private event Action _onDirty; @@ -68,17 +79,26 @@ namespace Ryujinx.Memory.Tracking internal RegionSignal PreAction => _preAction; + internal ConcurrentBitmap Bitmap; + internal int DirtyBit; + /// - /// Create a new region handle. The handle is registered with the given tracking object, + /// Create a new bitmap backed region handle. The handle is registered with the given tracking object, /// and will be notified of any changes to the specified region. /// /// Tracking object for the target memory block /// Virtual address of the region to track /// Size of the region to track + /// The bitmap the dirty flag for this handle is stored in + /// The bit index representing the dirty flag for this handle /// True if the region handle starts mapped - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true) + internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ConcurrentBitmap bitmap, int bit, bool mapped = true) { + Bitmap = bitmap; + DirtyBit = bit; + Dirty = mapped; + Unmapped = !mapped; Address = address; Size = size; @@ -92,6 +112,54 @@ namespace Ryujinx.Memory.Tracking } } + /// + /// Create a new region handle. The handle is registered with the given tracking object, + /// and will be notified of any changes to the specified region. + /// + /// Tracking object for the target memory block + /// Virtual address of the region to track + /// Size of the region to track + /// True if the region handle starts mapped + internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true) + { + Bitmap = new ConcurrentBitmap(1, mapped); + + Unmapped = !mapped; + Address = address; + Size = size; + EndAddress = address + size; + + _tracking = tracking; + _regions = tracking.GetVirtualRegionsForHandle(address, size); + foreach (var region in _regions) + { + region.Handles.Add(this); + } + } + + /// + /// Replace the bitmap and bit index used to track dirty state. + /// + /// + /// The tracking lock should be held when this is called, to ensure neither bitmap is modified. + /// + /// The bitmap the dirty flag for this handle is stored in + /// The bit index representing the dirty flag for this handle + internal void ReplaceBitmap(ConcurrentBitmap bitmap, int bit) + { + // Assumes the tracking lock is held, so nothing else can signal right now. + + var oldBitmap = Bitmap; + var oldBit = DirtyBit; + + bitmap.Set(bit, Dirty); + + Bitmap = bitmap; + DirtyBit = bit; + + Dirty |= oldBitmap.IsSet(oldBit); + } + /// /// Clear the volatile state of this handle. /// @@ -108,7 +176,7 @@ namespace Ryujinx.Memory.Tracking public bool DirtyOrVolatile() { _checkCount++; - return Dirty || _volatile; + return _volatile || Dirty; } /// @@ -195,6 +263,8 @@ namespace Ryujinx.Memory.Tracking /// public void ForceDirty() { + _checkCount++; + Dirty = true; } From 7d8e198c33b7ad283db53315129209a2bd310f23 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Wed, 2 Nov 2022 09:26:50 +0100 Subject: [PATCH 061/737] fix: Support FFmpeg 5.1.x for decoding (#3816) For some reason FFmpeg 5.1.x reverted part of the changes made in 5.0.x on AVCodec. This fix decoding issues with it. --- Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs | 10 ++++++---- Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs | 1 + .../Native/{AVCodecLegacy.cs => AVCodec501.cs} | 3 +-- Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs | 2 +- Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs | 6 ++---- 5 files changed, 11 insertions(+), 11 deletions(-) rename Ryujinx.Graphics.Nvdec.FFmpeg/Native/{AVCodecLegacy.cs => AVCodec501.cs} (92%) diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs b/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs index 8c2e94c34..572ceaaab 100644 --- a/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs +++ b/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs @@ -7,7 +7,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg { unsafe class FFmpegContext : IDisposable { - private readonly FFCodec.AVCodec_decode _decodeFrame; + private unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt); + + private readonly AVCodec_decode _decodeFrame; private static readonly FFmpegApi.av_log_set_callback_callback _logFunc; private readonly AVCodec* _codec; private AVPacket* _packet; @@ -53,17 +55,17 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg // libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union. if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24)) { - _decodeFrame = Marshal.GetDelegateForFunctionPointer(((FFCodec*)_codec)->CodecCallback); + _decodeFrame = Marshal.GetDelegateForFunctionPointer(((FFCodec*)_codec)->CodecCallback); } // libavcodec 59.x changed AvCodec private API layout. else if (avCodecMajorVersion == 59) { - _decodeFrame = Marshal.GetDelegateForFunctionPointer(((FFCodecLegacy*)_codec)->Decode); + _decodeFrame = Marshal.GetDelegateForFunctionPointer(((FFCodecLegacy*)_codec)->Decode); } // libavcodec 58.x and lower else { - _decodeFrame = Marshal.GetDelegateForFunctionPointer(((FFCodecLegacy*)_codec)->Decode); + _decodeFrame = Marshal.GetDelegateForFunctionPointer(((FFCodecLegacy*)_codec)->Decode); } } diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs index 93edb7883..46d3ad61c 100644 --- a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs +++ b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs @@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public unsafe IntPtr PrivClass; public IntPtr Profiles; public unsafe byte* WrapperName; + public IntPtr ChLayouts; #pragma warning restore CS0649 } } diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecLegacy.cs b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs similarity index 92% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecLegacy.cs rename to Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs index 0913cbc4b..47d4969ac 100644 --- a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecLegacy.cs +++ b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { - struct AVCodecLegacy + struct AVCodec501 { #pragma warning disable CS0649 public unsafe byte* Name; @@ -20,7 +20,6 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public unsafe IntPtr PrivClass; public IntPtr Profiles; public unsafe byte* WrapperName; - public IntPtr ChLayouts; #pragma warning restore CS0649 } } diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs index 11bd63b07..6c9fbc893 100644 --- a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs +++ b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public unsafe IntPtr AvClass; public int LogLevelOffset; public int CodecType; - public unsafe AVCodecLegacy* Codec; + public unsafe AVCodec* Codec; public AVCodecID CodecId; public uint CodecTag; public IntPtr PrivData; diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs index 8b08c02c0..4df45af46 100644 --- a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs +++ b/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs @@ -2,12 +2,10 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { - struct FFCodec + struct FFCodec where T: struct { - public unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt); - #pragma warning disable CS0649 - public AVCodec Base; + public T Base; public int CapsInternalOrCbType; public int PrivDataSize; public IntPtr UpdateThreadContext; From f82309fa2dd46d4339e0709ab835d927fd25361b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 2 Nov 2022 18:17:19 -0300 Subject: [PATCH 062/737] Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve (#3723) * Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve * FramebufferParams is no longer required there * Implement Specialization Constants and merge CopyMS Shaders (#15) * Vulkan: Initial Specialization Constants * Replace with specialized helper shader * Reimplement everything Fix nonexistant interaction with Ryu pipeline caching Decouple specialization info from data and relocate them Generalize mapping and add type enum to better match spv types Use local fixed scopes instead of global unmanaged allocs * Fix misses in initial implementation Use correct info variable in Create2DLayerView Add ShaderStorageImageMultisample to required feature set * Use texture for source image * No point in using ReadOnlyMemory * Apply formatting feedback Co-authored-by: gdkchan * Apply formatting suggestions on shader source Co-authored-by: gdkchan Co-authored-by: gdkchan * Support conversion with samples count that does not match the requested count, other minor changes Co-authored-by: mageven <62494521+mageven@users.noreply.github.com> --- .../Image/TextureCompatibility.cs | 7 +- Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 2 +- Ryujinx.Graphics.Vulkan/HelperShader.cs | 234 +++++++++++-- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 23 +- Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 38 ++- Ryujinx.Graphics.Vulkan/PipelineState.cs | 33 +- Ryujinx.Graphics.Vulkan/ShaderCollection.cs | 47 ++- .../Shaders/ColorCopyBetweenMsNonMs.comp | 89 +++++ .../Shaders/ShaderBinaries.cs | 319 ++++++++++++++++++ Ryujinx.Graphics.Vulkan/SpecInfo.cs | 103 ++++++ Ryujinx.Graphics.Vulkan/TextureCopy.cs | 117 +++++++ Ryujinx.Graphics.Vulkan/TextureView.cs | 200 +++-------- .../VulkanInitialization.cs | 3 +- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 4 +- 14 files changed, 997 insertions(+), 222 deletions(-) create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/ColorCopyBetweenMsNonMs.comp create mode 100644 Ryujinx.Graphics.Vulkan/SpecInfo.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 5ea9ee2f6..91a1a728d 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -710,7 +710,12 @@ namespace Ryujinx.Graphics.Gpu.Image break; case Target.Texture2DMultisample: case Target.Texture2DMultisampleArray: - if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) + // We don't support copy between multisample and non-multisample depth-stencil textures + // because there's no way to emulate that since most GPUs don't support writing a + // custom stencil value into the texture, among several other API limitations. + + if ((rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) && + !rhs.FormatInfo.Format.IsDepthOrStencil()) { return TextureViewCompatibility.CopyOnly; } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 3e7da6e36..68cd2d30f 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.OpenGL.Image if (!destinationView.Target.IsMultisample() && Target.IsMultisample()) { - _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer,1); + _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1); } else if (destinationView.Target.IsMultisample() && !Target.IsMultisample()) { diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 0201de0ad..c842b9222 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programColorBlitClearAlpha; private readonly IProgram _programColorClear; private readonly IProgram _programStrideChange; + private readonly IProgram _programColorCopyBetweenMsNonMs; public HelperShader(VulkanRenderer gd, Device device) { @@ -73,6 +74,20 @@ namespace Ryujinx.Graphics.Vulkan { new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv), }); + + var colorCopyMSBindings = new ShaderBindings( + new[] { 0 }, + Array.Empty(), + new[] { 0 }, + new[] { 0 }); + + _programColorCopyBetweenMsNonMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorCopyBetweenMsNonMs, colorCopyMSBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }, new[] + { + new SpecDescription((0, SpecConstType.Int32)) + }); } public void Blit( @@ -136,11 +151,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, region); - Span bufferRanges = stackalloc BufferRange[1]; - - bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize); - - _pipeline.SetUniformBuffers(1, bufferRanges); + _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -203,11 +214,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, clearColor); - Span bufferRanges = stackalloc BufferRange[1]; - - bufferRanges[0] = new BufferRange(bufferHandle, 0, ClearColorBufferSize); - - _pipeline.SetUniformBuffers(1, bufferRanges); + _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, ClearColorBufferSize) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -269,11 +276,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, region); - Span bufferRanges = stackalloc BufferRange[1]; - - bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize); - - pipeline.SetUniformBuffers(1, bufferRanges); + pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -351,11 +354,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetCommandBuffer(cbs); - Span cbRanges = stackalloc BufferRange[1]; - - cbRanges[0] = new BufferRange(bufferHandle, 0, ParamsBufferSize); - - _pipeline.SetUniformBuffers(0, cbRanges); + _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) }); Span> sbRanges = new Auto[2]; @@ -480,12 +479,207 @@ namespace Ryujinx.Graphics.Vulkan convertedCount * outputIndexSize); } + public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth) + { + CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, src.Info.Samples, dst.Info.Width, dst.Info.Height); + } + + public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth) + { + CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, dst.Info.Samples, src.Info.Width, src.Info.Height); + } + + private void CopyMS( + VulkanRenderer gd, + CommandBufferScoped cbs, + TextureView src, + TextureView dst, + int srcLayer, + int dstLayer, + int depth, + int samples, + int nonMSWidth, + int nonMSHeight) + { + const int ParamsBufferSize = 16; + + Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; + + // X and Y are the expected texture samples. + // Z and W are the actual texture samples used. + // They may differ if the GPU does not support the samples count requested and we had to use a lower amount. + (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); + (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags((uint)samples)); + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false); + + gd.BufferManager.SetData(bufferHandle, 0, shaderParams); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + src.GetImage().Get(cbs).Value, + TextureStorage.DefaultAccessMask, + AccessFlags.AccessShaderReadBit, + PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.PipelineStageComputeShaderBit, + ImageAspectFlags.ImageAspectColorBit, + src.FirstLayer + srcLayer, + src.FirstLevel, + depth, + 1); + + _pipeline.SetCommandBuffer(cbs); + + _pipeline.SetProgram(_programColorCopyBetweenMsNonMs); + + var format = GetFormat(src.Info.BytesPerPixel); + + int dispatchX = (nonMSWidth + 31) / 32; + int dispatchY = (nonMSHeight + 31) / 32; + + // Specialize shader. + bool srcIsMs = src.Info.Target.IsMultisample(); + int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel; + _pipeline.Specialize(conversionType); + + _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) }); + + if (src.Info.Target == Target.Texture2DMultisampleArray || + dst.Info.Target == Target.Texture2DMultisampleArray) + { + for (int z = 0; z < depth; z++) + { + var srcView = Create2DLayerView(src, srcLayer + z, format); + var dstView = Create2DLayerView(dst, dstLayer + z); + + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); + _pipeline.SetImage(0, dstView, format); + + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + + srcView.Release(); + dstView.Release(); + } + } + else + { + var srcView = Create2DLayerView(src, srcLayer, format); + + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); + _pipeline.SetImage(0, dst, format); + + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + + srcView.Release(); + } + + gd.BufferManager.Delete(bufferHandle); + + _pipeline.Finish(gd, cbs); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + dst.GetImage().Get(cbs).Value, + AccessFlags.AccessShaderWriteBit, + TextureStorage.DefaultAccessMask, + PipelineStageFlags.PipelineStageComputeShaderBit, + PipelineStageFlags.PipelineStageAllCommandsBit, + ImageAspectFlags.ImageAspectColorBit, + dst.FirstLayer + dstLayer, + dst.FirstLevel, + depth, + 1); + } + + private static (int, int) GetSampleCountXYLog2(int samples) + { + int samplesInXLog2 = 0; + int samplesInYLog2 = 0; + + switch (samples) + { + case 2: // 2x1 + samplesInXLog2 = 1; + break; + case 4: // 2x2 + samplesInXLog2 = 1; + samplesInYLog2 = 1; + break; + case 8: // 4x2 + samplesInXLog2 = 2; + samplesInYLog2 = 1; + break; + case 16: // 4x4 + samplesInXLog2 = 2; + samplesInYLog2 = 2; + break; + case 32: // 8x4 + samplesInXLog2 = 3; + samplesInYLog2 = 2; + break; + case 64: // 8x8 + samplesInXLog2 = 3; + samplesInYLog2 = 3; + break; + } + + return (samplesInXLog2, samplesInYLog2); + } + + private static ITexture Create2DLayerView(TextureView from, int layer, GAL.Format? format = null) + { + var target = from.Info.Target switch + { + Target.Texture1DArray => Target.Texture1D, + Target.Texture2DArray => Target.Texture2D, + Target.Texture2DMultisampleArray => Target.Texture2DMultisample, + _ => from.Info.Target + }; + + var info = new TextureCreateInfo( + from.Info.Width, + from.Info.Height, + from.Info.Depth, + 1, + from.Info.Samples, + from.Info.BlockWidth, + from.Info.BlockHeight, + from.Info.BytesPerPixel, + format ?? from.Info.Format, + from.Info.DepthStencilMode, + target, + from.Info.SwizzleR, + from.Info.SwizzleG, + from.Info.SwizzleB, + from.Info.SwizzleA); + + return from.CreateView(info, layer, 0); + } + + private static GAL.Format GetFormat(int bytesPerPixel) + { + return bytesPerPixel switch + { + 1 => GAL.Format.R8Uint, + 2 => GAL.Format.R16Uint, + 4 => GAL.Format.R32Uint, + 8 => GAL.Format.R32G32Uint, + 16 => GAL.Format.R32G32B32A32Uint, + _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.") + }; + } + protected virtual void Dispose(bool disposing) { if (disposing) { _programColorBlitClearAlpha.Dispose(); _programColorBlit.Dispose(); + _programColorClear.Dispose(); + _programStrideChange.Dispose(); + _programColorCopyBetweenMsNonMs.Dispose(); _samplerNearest.Dispose(); _samplerLinear.Dispose(); _pipeline.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 2a376261e..f333c944b 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -3,6 +3,8 @@ using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Vulkan { @@ -697,6 +699,18 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } + public void Specialize(in T data) where T : unmanaged + { + var dataSpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in data), 1)); + + if (!dataSpan.SequenceEqual(_newState.SpecializationData.Span)) + { + _newState.SpecializationData = new SpecData(dataSpan); + + SignalStateChange(); + } + } + protected virtual void SignalAttachmentChange() { } @@ -1188,14 +1202,7 @@ namespace Ryujinx.Graphics.Vulkan } } - var subpassDependency = new SubpassDependency( - 0, - 0, - PipelineStageFlags.PipelineStageAllGraphicsBit, - PipelineStageFlags.PipelineStageAllGraphicsBit, - AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessColorAttachmentWriteBit, - AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessShaderReadBit, - 0); + var subpassDependency = PipelineConverter.CreateSubpassDependency(); fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 55d29ffa0..a01c5ad72 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -6,6 +6,9 @@ namespace Ryujinx.Graphics.Vulkan { static class PipelineConverter { + private const AccessFlags SubpassSrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessColorAttachmentWriteBit; + private const AccessFlags SubpassDstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessShaderReadBit; + public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device) { const int MaxAttachments = Constants.MaxRenderTargets + 1; @@ -100,14 +103,7 @@ namespace Ryujinx.Graphics.Vulkan } } - var subpassDependency = new SubpassDependency( - 0, - 0, - PipelineStageFlags.PipelineStageAllGraphicsBit, - PipelineStageFlags.PipelineStageAllGraphicsBit, - AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit, - AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit, - 0); + var subpassDependency = CreateSubpassDependency(); fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { @@ -128,6 +124,32 @@ namespace Ryujinx.Graphics.Vulkan } } + public static SubpassDependency CreateSubpassDependency() + { + return new SubpassDependency( + 0, + 0, + PipelineStageFlags.PipelineStageAllGraphicsBit, + PipelineStageFlags.PipelineStageAllGraphicsBit, + SubpassSrcAccessMask, + SubpassDstAccessMask, + 0); + } + + public unsafe static SubpassDependency2 CreateSubpassDependency2() + { + return new SubpassDependency2( + StructureType.SubpassDependency2, + null, + 0, + 0, + PipelineStageFlags.PipelineStageAllGraphicsBit, + PipelineStageFlags.PipelineStageAllGraphicsBit, + SubpassSrcAccessMask, + SubpassDstAccessMask, + 0); + } + public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd) { PipelineState pipeline = new PipelineState(); diff --git a/Ryujinx.Graphics.Vulkan/PipelineState.cs b/Ryujinx.Graphics.Vulkan/PipelineState.cs index d33bc8ce8..7b6a26490 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -312,6 +312,7 @@ namespace Ryujinx.Graphics.Vulkan public NativeArray Stages; public NativeArray StageRequiredSubgroupSizes; public PipelineLayout PipelineLayout; + public SpecData SpecializationData; public void Initialize() { @@ -334,7 +335,7 @@ namespace Ryujinx.Graphics.Vulkan ShaderCollection program, PipelineCache cache) { - if (program.TryGetComputePipeline(out var pipeline)) + if (program.TryGetComputePipeline(ref SpecializationData, out var pipeline)) { return pipeline; } @@ -354,20 +355,36 @@ namespace Ryujinx.Graphics.Vulkan Pipeline pipelineHandle = default; - gd.Api.CreateComputePipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError(); + bool hasSpec = program.SpecDescriptions != null; + + var desc = hasSpec ? program.SpecDescriptions[0] : SpecDescription.Empty; + + if (hasSpec && SpecializationData.Length < (int)desc.Info.DataSize) + { + throw new InvalidOperationException("Specialization data size does not match description"); + } + + fixed (SpecializationInfo* info = &desc.Info) + fixed (SpecializationMapEntry* map = desc.Map) + fixed (byte* data = SpecializationData.Span) + { + if (hasSpec) + { + info->PMapEntries = map; + info->PData = data; + pipelineCreateInfo.Stage.PSpecializationInfo = info; + } + + gd.Api.CreateComputePipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError(); + } pipeline = new Auto(new DisposablePipeline(gd.Api, device, pipelineHandle)); - program.AddComputePipeline(pipeline); + program.AddComputePipeline(ref SpecializationData, pipeline); return pipeline; } - public unsafe void DestroyComputePipeline(ShaderCollection program) - { - program.RemoveComputePipeline(); - } - public unsafe Auto CreateGraphicsPipeline( VulkanRenderer gd, Device device, diff --git a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index 82866f7b7..ab0ea2e95 100644 --- a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -26,6 +26,8 @@ namespace Ryujinx.Graphics.Vulkan public ProgramLinkStatus LinkStatus { get; private set; } + public readonly SpecDescription[] SpecDescriptions; + public bool IsLinked { get @@ -40,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan } private HashTableSlim> _graphicsPipelineCache; - private Auto _computePipeline; + private HashTableSlim> _computePipelineCache; private VulkanRenderer _gd; private Device _device; @@ -52,17 +54,24 @@ namespace Ryujinx.Graphics.Vulkan private Task _compileTask; private bool _firstBackgroundUse; - public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, bool isMinimal = false) + public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false) { _gd = gd; _device = device; + if (specDescription != null && specDescription.Length != shaders.Length) + { + throw new ArgumentException($"{nameof(specDescription)} array length must match {nameof(shaders)} array if provided"); + } + gd.Shaders.Add(this); var internalShaders = new Shader[shaders.Length]; _infos = new PipelineShaderStageCreateInfo[shaders.Length]; + SpecDescriptions = specDescription; + LinkStatus = ProgramLinkStatus.Incomplete; uint stages = 0; @@ -314,14 +323,9 @@ namespace Ryujinx.Graphics.Vulkan return null; } - public void AddComputePipeline(Auto pipeline) + public void AddComputePipeline(ref SpecData key, Auto pipeline) { - _computePipeline = pipeline; - } - - public void RemoveComputePipeline() - { - _computePipeline = null; + (_computePipelineCache ??= new()).Add(ref key, pipeline); } public void AddGraphicsPipeline(ref PipelineUid key, Auto pipeline) @@ -329,10 +333,20 @@ namespace Ryujinx.Graphics.Vulkan (_graphicsPipelineCache ??= new()).Add(ref key, pipeline); } - public bool TryGetComputePipeline(out Auto pipeline) + public bool TryGetComputePipeline(ref SpecData key, out Auto pipeline) { - pipeline = _computePipeline; - return pipeline != null; + if (_computePipelineCache == null) + { + pipeline = default; + return false; + } + + if (_computePipelineCache.TryGetValue(ref key, out pipeline)) + { + return true; + } + + return false; } public bool TryGetGraphicsPipeline(ref PipelineUid key, out Auto pipeline) @@ -390,7 +404,14 @@ namespace Ryujinx.Graphics.Vulkan } } - _computePipeline?.Dispose(); + if (_computePipelineCache != null) + { + foreach (Auto pipeline in _computePipelineCache.Values) + { + pipeline.Dispose(); + } + } + if (_dummyRenderPass.Value.Handle != 0) { _dummyRenderPass.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyBetweenMsNonMs.comp b/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyBetweenMsNonMs.comp new file mode 100644 index 000000000..c1ae2444c --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyBetweenMsNonMs.comp @@ -0,0 +1,89 @@ +#version 450 core + +// +ve for MsToNonMs, -ve for reverse +layout (constant_id = 0) const int convType = 0; + +layout (std140, binding = 0) uniform sample_counts_log2_in +{ + ivec4 sample_counts_log2; +}; + +#define R8_ID 1 +#define R16_ID 2 +#define R32_ID 4 +#define RG32_ID 8 +#define RGBA32_ID 16 + +#define R8_TYPE r8ui +#define R16_TYPE r16ui +#define R32_TYPE r32ui +#define RG32_TYPE rg32ui +#define RGBA32_TYPE rgba32ui + +#define DECLARE_BINDINGS(type) layout (set = 3, binding = 0, type##_TYPE) uniform uimage2DMS dstMS ## type; \ + layout (set = 3, binding = 0, type##_TYPE) uniform uimage2D dst ## type; + +#define CASE_SIZE(type) case type##_ID: imageSz = imageSize(dst ## type); break; + +#define CASE_CONVERT(type) case type##_ID: imageStore(dst ## type, ivec2(coords), texelFetch(srcMS, shiftedCoords, sampleIdx)); break; \ + case -type##_ID: imageStore(dstMS ## type, shiftedCoords, sampleIdx, texelFetch(src, ivec2(coords), 0)); break; + +// src tex +layout (set = 2, binding = 0) uniform usampler2DMS srcMS; +layout (set = 2, binding = 0) uniform usampler2D src; + +// dst img +DECLARE_BINDINGS(R8) +DECLARE_BINDINGS(R16) +DECLARE_BINDINGS(R32) +DECLARE_BINDINGS(RG32) +DECLARE_BINDINGS(RGBA32) + +layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; + +void main() +{ + uvec2 coords = gl_GlobalInvocationID.xy; + + ivec2 imageSz = ivec2(0, 0); + + switch (convType) + { + case 0: break; + CASE_SIZE(R8 ) + CASE_SIZE(R16 ) + CASE_SIZE(R32 ) + CASE_SIZE(RG32 ) + CASE_SIZE(RGBA32) + default: + imageSz = textureSize(src, 0); + break; + } + + if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) + { + return; + } + + int deltaX = sample_counts_log2.x - sample_counts_log2.z; + int deltaY = sample_counts_log2.y - sample_counts_log2.w; + int samplesInXLog2 = sample_counts_log2.z; + int samplesInYLog2 = sample_counts_log2.w; + int samplesInX = 1 << samplesInXLog2; + int samplesInY = 1 << samplesInYLog2; + int sampleIdx = ((int(coords.x) >> deltaX) & (samplesInX - 1)) | (((int(coords.y) >> deltaY) & (samplesInY - 1)) << samplesInXLog2); + + samplesInXLog2 = sample_counts_log2.x; + samplesInYLog2 = sample_counts_log2.y; + + ivec2 shiftedCoords = ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2); + + switch (convType) + { + CASE_CONVERT(R8 ) + CASE_CONVERT(R16 ) + CASE_CONVERT(R32 ) + CASE_CONVERT(RG32 ) + CASE_CONVERT(RGBA32) + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index b7064f78c..9cc7e034e 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -551,5 +551,324 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x21, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x35, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + + public static readonly byte[] ColorCopyBetweenMsNonMs = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x2D, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, + 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x47, + 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6E, 0x76, + 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x52, 0x38, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x52, 0x31, 0x36, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x52, 0x33, 0x32, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x52, 0x47, 0x33, 0x32, 0x00, 0x05, 0x00, 0x05, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x52, 0x47, 0x42, 0x41, 0x33, 0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x00, 0x05, 0x00, 0x08, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, + 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, + 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4D, + 0x53, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D, + 0x53, 0x52, 0x38, 0x00, 0x05, 0x00, 0x05, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D, + 0x53, 0x52, 0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x4D, 0x53, 0x52, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D, 0x53, 0x52, 0x47, 0x33, 0x32, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x11, 0x01, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D, 0x53, 0x52, 0x47, 0x42, + 0x41, 0x33, 0x32, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1D, 0x01, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x25, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x46, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0xB1, 0x00, 0x00, 0x00, + 0xB0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xB2, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0xD0, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0xD1, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0xE5, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0xE6, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xFB, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x01, 0x00, 0x00, + 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1C, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x1D, 0x01, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0x1E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x1F, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x0F, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x67, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x11, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, + 0x4A, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x52, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1E, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x63, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x63, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x6C, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x7D, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, + 0x8B, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, + 0x89, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xA1, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x17, 0x00, 0x15, 0x00, 0x00, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xA3, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0xA5, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, + 0xA7, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, + 0xA9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, + 0xAB, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xB0, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xAF, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xBB, 0x00, 0x00, 0x00, + 0xBE, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0xC5, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xBE, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0xC5, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x25, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0xC9, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xB0, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xC7, 0x00, 0x00, 0x00, + 0xC9, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xD0, 0x00, 0x00, 0x00, + 0xD3, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0xD6, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xD8, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0xD9, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0xDA, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0xDA, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA6, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xB0, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xDC, 0x00, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xE5, 0x00, 0x00, 0x00, + 0xE8, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0xEB, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xED, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0xEE, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0xEF, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0xEF, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x31, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xB0, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xF1, 0x00, 0x00, 0x00, + 0xF3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xFA, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xB0, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0x06, 0x01, 0x00, 0x00, + 0x08, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x01, 0x00, 0x00, + 0x12, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x15, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x17, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x18, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0x19, 0x01, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0x12, 0x01, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x19, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x1E, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x01, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00, + }; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/SpecInfo.cs b/Ryujinx.Graphics.Vulkan/SpecInfo.cs new file mode 100644 index 000000000..83a34cde0 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/SpecInfo.cs @@ -0,0 +1,103 @@ +using Silk.NET.Vulkan; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan +{ + public enum SpecConstType + { + Bool32, + Int16, + Int32, + Int64, + Float16, + Float32, + Float64 + } + + sealed class SpecDescription + { + public readonly SpecializationInfo Info; + public readonly SpecializationMapEntry[] Map; + + // For mapping a simple packed struct or single entry + public SpecDescription(params (uint Id, SpecConstType Type)[] description) + { + int count = description.Length; + Map = new SpecializationMapEntry[count]; + + uint structSize = 0; + + for (int i = 0; i < count; ++i) + { + var typeSize = SizeOf(description[i].Type); + Map[i] = new SpecializationMapEntry(description[i].Id, structSize, typeSize); + structSize += typeSize; + } + + Info = new SpecializationInfo() + { + DataSize = structSize, + MapEntryCount = (uint)count + }; + } + + // For advanced mapping with overlapping or staggered fields + public SpecDescription(SpecializationMapEntry[] map) + { + int count = map.Length; + Map = map; + + uint structSize = 0; + for (int i = 0; i < count; ++i) + { + structSize = Math.Max(structSize, map[i].Offset + (uint)map[i].Size); + } + + Info = new SpecializationInfo() + { + DataSize = structSize, + MapEntryCount = (uint)map.Length + }; + } + + private static uint SizeOf(SpecConstType type) => type switch + { + SpecConstType.Int16 or SpecConstType.Float16 => 2, + SpecConstType.Bool32 or SpecConstType.Int32 or SpecConstType.Float32 => 4, + SpecConstType.Int64 or SpecConstType.Float64 => 8, + _ => throw new ArgumentOutOfRangeException(nameof(type)) + }; + + private SpecDescription() + { + Info = new(); + } + + public static readonly SpecDescription Empty = new(); + } + + readonly struct SpecData : IRefEquatable + { + private readonly byte[] _data; + private readonly int _hash; + + public int Length => _data.Length; + public ReadOnlySpan Span => _data.AsSpan(); + public override int GetHashCode() => _hash; + + public SpecData(ReadOnlySpan data) + { + _data = new byte[data.Length]; + data.CopyTo(_data); + + var hc = new HashCode(); + hc.AddBytes(data); + _hash = hc.ToHashCode(); + } + + public override bool Equals(object obj) => obj is SpecData other && Equals(other); + public bool Equals(ref SpecData other) => _data.AsSpan().SequenceEqual(other._data); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/Ryujinx.Graphics.Vulkan/TextureCopy.cs index 05e110936..d14c2a324 100644 --- a/Ryujinx.Graphics.Vulkan/TextureCopy.cs +++ b/Ryujinx.Graphics.Vulkan/TextureCopy.cs @@ -355,5 +355,122 @@ namespace Ryujinx.Graphics.Vulkan dstLayers, levels); } + + public unsafe static void ResolveDepthStencil( + VulkanRenderer gd, + Device device, + CommandBufferScoped cbs, + TextureView src, + TextureView dst) + { + var dsAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 0, ImageLayout.General); + var dsResolveAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 1, ImageLayout.General); + + var subpassDsResolve = new SubpassDescriptionDepthStencilResolve() + { + SType = StructureType.SubpassDescriptionDepthStencilResolve, + PDepthStencilResolveAttachment = &dsResolveAttachmentReference, + DepthResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit, + StencilResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit + }; + + var subpass = new SubpassDescription2() + { + SType = StructureType.SubpassDescription2, + PipelineBindPoint = PipelineBindPoint.Graphics, + PDepthStencilAttachment = &dsAttachmentReference, + PNext = &subpassDsResolve + }; + + AttachmentDescription2[] attachmentDescs = new AttachmentDescription2[2]; + + attachmentDescs[0] = new AttachmentDescription2( + StructureType.AttachmentDescription2, + null, + 0, + src.VkFormat, + TextureStorage.ConvertToSampleCountFlags((uint)src.Info.Samples), + AttachmentLoadOp.Load, + AttachmentStoreOp.Store, + AttachmentLoadOp.Load, + AttachmentStoreOp.Store, + ImageLayout.General, + ImageLayout.General); + + attachmentDescs[1] = new AttachmentDescription2( + StructureType.AttachmentDescription2, + null, + 0, + dst.VkFormat, + TextureStorage.ConvertToSampleCountFlags((uint)dst.Info.Samples), + AttachmentLoadOp.Load, + AttachmentStoreOp.Store, + AttachmentLoadOp.Load, + AttachmentStoreOp.Store, + ImageLayout.General, + ImageLayout.General); + + var subpassDependency = PipelineConverter.CreateSubpassDependency2(); + + fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs) + { + var renderPassCreateInfo = new RenderPassCreateInfo2() + { + SType = StructureType.RenderPassCreateInfo2, + PAttachments = pAttachmentDescs, + AttachmentCount = (uint)attachmentDescs.Length, + PSubpasses = &subpass, + SubpassCount = 1, + PDependencies = &subpassDependency, + DependencyCount = 1 + }; + + gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); + + using var rp = new Auto(new DisposableRenderPass(gd.Api, device, renderPass)); + + ImageView* attachments = stackalloc ImageView[2]; + + var srcView = src.GetImageViewForAttachment(); + var dstView = dst.GetImageViewForAttachment(); + + attachments[0] = srcView.Get(cbs).Value; + attachments[1] = dstView.Get(cbs).Value; + + var framebufferCreateInfo = new FramebufferCreateInfo() + { + SType = StructureType.FramebufferCreateInfo, + RenderPass = rp.Get(cbs).Value, + AttachmentCount = 2, + PAttachments = attachments, + Width = (uint)src.Width, + Height = (uint)src.Height, + Layers = (uint)src.Layers + }; + + gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); + using var fb = new Auto(new DisposableFramebuffer(gd.Api, device, framebuffer), null, new[] { srcView, dstView }); + + var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height)); + var clearValue = new ClearValue(); + + var renderPassBeginInfo = new RenderPassBeginInfo() + { + SType = StructureType.RenderPassBeginInfo, + RenderPass = rp.Get(cbs).Value, + Framebuffer = fb.Get(cbs).Value, + RenderArea = renderArea, + PClearValues = &clearValue, + ClearValueCount = 1 + }; + + // The resolve operation happens at the end of the subpass, so let's just do a begin/end + // to resolve the depth-stencil texture. + // TODO: Do speculative resolve and part of the same render pass as the draw to avoid + // ending the current render pass? + gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline); + gd.Api.CmdEndRenderPass(cbs.CommandBuffer); + } + } } } diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index 10d0ef05d..f47e431b1 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -169,12 +169,15 @@ namespace Ryujinx.Graphics.Vulkan var srcImage = src.GetImage().Get(cbs).Value; var dstImage = dst.GetImage().Get(cbs).Value; - if (src.Info.Target.IsMultisample()) + if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) { - int depth = Math.Min(src.Info.Depth, dst.Info.Depth - firstLayer); - int levels = Math.Min(src.Info.Levels, dst.Info.Levels - firstLevel); - - CopyMSToNonMS(_gd, cbs, src, dst, srcImage, dstImage, 0, firstLayer, 0, firstLevel, depth, levels); + 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 { @@ -213,9 +216,13 @@ namespace Ryujinx.Graphics.Vulkan var srcImage = src.GetImage().Get(cbs).Value; var dstImage = dst.GetImage().Get(cbs).Value; - if (src.Info.Target.IsMultisample()) + if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) { - CopyMSToNonMS(_gd, cbs, src, dst, srcImage, dstImage, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + _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 { @@ -239,142 +246,6 @@ namespace Ryujinx.Graphics.Vulkan } } - private static void CopyMSToNonMS( - VulkanRenderer gd, - CommandBufferScoped cbs, - TextureView src, - TextureView dst, - Image srcImage, - Image dstImage, - int srcLayer, - int dstLayer, - int srcLevel, - int dstLevel, - int layers, - int levels) - { - bool differentFormats = src.Info.Format != dst.Info.Format; - - var target = src.Info.Target switch - { - Target.Texture2D => Target.Texture2DMultisample, - Target.Texture2DArray => Target.Texture2DMultisampleArray, - Target.Texture2DMultisampleArray => Target.Texture2DArray, - _ => Target.Texture2D - }; - - var intermmediateTarget = differentFormats ? dst.Info.Target : target; - using var intermmediate = CreateIntermmediateTexture(gd, src, ref dst._info, intermmediateTarget, layers, levels); - var intermmediateImage = intermmediate.GetImage().Get(cbs).Value; - - if (differentFormats) - { - // If the formats are different, the resolve would perform format conversion. - // So we need yet another intermmediate texture and do a copy to reinterpret the - // data into the correct (destination) format, without doing any sort of conversion. - using var intermmediate2 = CreateIntermmediateTexture(gd, src, ref src._info, target, layers, levels); - var intermmediate2Image = intermmediate2.GetImage().Get(cbs).Value; - - TextureCopy.Copy( - gd.Api, - cbs.CommandBuffer, - srcImage, - intermmediate2Image, - src.Info, - intermmediate2.Info, - src.FirstLayer, - 0, - src.FirstLevel, - 0, - srcLayer, - 0, - srcLevel, - 0, - layers, - levels); - - TextureCopy.Copy( - gd.Api, - cbs.CommandBuffer, - intermmediate2Image, - intermmediateImage, - intermmediate2.Info, - intermmediate.Info, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - layers, - levels); - } - else - { - TextureCopy.Copy( - gd.Api, - cbs.CommandBuffer, - srcImage, - intermmediateImage, - src.Info, - intermmediate.Info, - src.FirstLayer, - 0, - src.FirstLevel, - 0, - srcLayer, - 0, - srcLevel, - 0, - layers, - levels); - } - - var srcRegion = new Extents2D(0, 0, src.Width, src.Height); - var dstRegion = new Extents2D(0, 0, dst.Width, dst.Height); - - TextureCopy.Blit( - gd.Api, - cbs.CommandBuffer, - intermmediateImage, - dstImage, - intermmediate.Info, - dst.Info, - srcRegion, - dstRegion, - 0, - dst.FirstLevel + dstLevel, - 0, - dst.FirstLayer + dstLayer, - layers, - levels, - true, - ImageAspectFlags.ImageAspectColorBit, - ImageAspectFlags.ImageAspectColorBit); - } - - private static TextureView CreateIntermmediateTexture(VulkanRenderer gd, TextureView src, ref TextureCreateInfo formatInfo, Target target, int depth, int levels) - { - return gd.CreateTextureView(new GAL.TextureCreateInfo( - src.Width, - src.Height, - depth, - levels, - 1, - formatInfo.BlockWidth, - formatInfo.BlockHeight, - formatInfo.BytesPerPixel, - formatInfo.Format, - DepthStencilMode.Depth, - target, - SwizzleComponent.Red, - SwizzleComponent.Green, - SwizzleComponent.Blue, - SwizzleComponent.Alpha), 1f); - } - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { var dst = (TextureView)destination; @@ -422,23 +293,32 @@ namespace Ryujinx.Graphics.Vulkan src.Height == dst.Height && src.VkFormat == dst.VkFormat) { - TextureCopy.Copy( - _gd.Api, - cbs.CommandBuffer, - src.GetImage().Get(cbs).Value, - dst.GetImage().Get(cbs).Value, - src.Info, - dst.Info, - src.FirstLayer, - dst.FirstLayer, - src.FirstLevel, - dst.FirstLevel, - 0, - 0, - 0, - 0, - layers, - levels); + if (src.Info.Samples > 1 && src.Info.Samples != dst.Info.Samples && src.Info.Format.IsDepthOrStencil()) + { + // CmdResolveImage does not support depth-stencil resolve, so we need to use an alternative path + // for those textures. + TextureCopy.ResolveDepthStencil(_gd, _device, cbs, src, dst); + } + else + { + TextureCopy.Copy( + _gd.Api, + cbs.CommandBuffer, + src.GetImage().Get(cbs).Value, + dst.GetImage().Get(cbs).Value, + src.Info, + dst.Info, + src.FirstLayer, + dst.FirstLayer, + src.FirstLevel, + dst.FirstLevel, + 0, + 0, + 0, + 0, + layers, + levels); + } return; } diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 5d8accd27..e54eef4f9 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; @@ -393,6 +393,7 @@ namespace Ryujinx.Graphics.Vulkan ShaderClipDistance = true, ShaderFloat64 = supportedFeatures.ShaderFloat64, ShaderImageGatherExtended = true, + ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, // ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true, TessellationShader = true, diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index ef089c32e..ef1360e75 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -287,9 +287,9 @@ namespace Ryujinx.Graphics.Vulkan } } - internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources) + internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, SpecDescription[] specDescription = null) { - return new ShaderCollection(this, _device, sources, isMinimal: true); + return new ShaderCollection(this, _device, sources, specDescription: specDescription, isMinimal: true); } public ISampler CreateSampler(GAL.SamplerCreateInfo info) From 647de4cd31a82f39d23f92ea4377eeae5817b6c2 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 3 Nov 2022 19:54:30 -0300 Subject: [PATCH 063/737] Ensure all pending draws are done before compute dispatch (#3822) --- Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 84de779c5..bc2911748 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -94,6 +94,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { var memoryManager = _channel.MemoryManager; + // Since we're going to change the state, make sure any pending instanced draws are done. + _3dEngine.PerformDeferredDraws(); + + // Make sure all pending uniform buffer data is written to memory. _3dEngine.FlushUboDirty(); uint qmdAddress = _state.State.SendPcasA; From c6d05301aae7509ea6a1ec29d39a72bac94d80b0 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Wed, 9 Nov 2022 20:22:43 +0100 Subject: [PATCH 064/737] infra: Migrate to .NET 7 (#3795) * Update readme to mention .NET 7 * infra: Migrate to .NET 7 .NET 7 is still in preview but this prepare for the release coming up next month. * Use Random.Shared in CreateRandom * Move UInt128Utils.cs to Ryujinx.Common project * Fix inverted parameters in System.UInt128 constructor * Fix Visual Studio complains on Ryujinx.Graphics.Vic * time: Fix missing alignment enforcement in SystemClockContext Fixes at least Smash * time: Fix missing alignment enforcement in SteadyClockContext Fix games (like recent version of Smash) using time shared memory * Switch to .NET 7.0.100 release * Enable Tiered PGO * Ensure CreateId validity requirements are meet when doing random generation Also enforce correct packing layout for other Mii structures. This fix a Mario Kart 8 crashes related to the default Miis. --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- ARMeilleure/ARMeilleure.csproj | 2 +- README.md | 2 +- .../Ryujinx.Audio.Backends.OpenAL.csproj | 2 +- .../Ryujinx.Audio.Backends.SDL2.csproj | 2 +- .../Ryujinx.Audio.Backends.SoundIo.csproj | 2 +- .../Server/Splitter/SplitterContext.cs | 4 +- Ryujinx.Audio/Ryujinx.Audio.csproj | 2 +- Ryujinx.Ava/Ryujinx.Ava.csproj | 4 +- .../Extensions/BinaryReaderExtensions.cs | 6 + Ryujinx.Common/Ryujinx.Common.csproj | 2 +- Ryujinx.Common/Utilities/SpanHelpers.cs | 16 +-- Ryujinx.Common/Utilities/UInt128Utils.cs | 17 +++ Ryujinx.Cpu/Ryujinx.Cpu.csproj | 2 +- .../Ryujinx.Graphics.Device.csproj | 2 +- .../Ryujinx.Graphics.GAL.csproj | 2 +- .../Image/TextureBindingsManager.cs | 8 +- .../Ryujinx.Graphics.Gpu.csproj | 2 +- .../Shader/HashTable/PartitionHashTable.cs | 2 +- .../Shader/HashTable/PartitionedHashTable.cs | 2 +- .../Shader/ShaderSpecializationState.cs | 10 +- .../Ryujinx.Graphics.Host1x.csproj | 2 +- .../Ryujinx.Graphics.Nvdec.FFmpeg.csproj | 2 +- .../Ryujinx.Graphics.Nvdec.Vp9.csproj | 2 +- .../Ryujinx.Graphics.Nvdec.csproj | 2 +- .../Ryujinx.Graphics.OpenGL.csproj | 2 +- .../Ryujinx.Graphics.Shader.csproj | 2 +- .../Translation/ShaderConfig.cs | 2 +- .../Translation/Translator.cs | 4 +- .../Translation/UInt128.cs | 112 ------------------ .../Ryujinx.Graphics.Texture.csproj | 2 +- Ryujinx.Graphics.Vic/Image/SurfaceReader.cs | 4 +- .../Ryujinx.Graphics.Vic.csproj | 2 +- .../Ryujinx.Graphics.Video.csproj | 2 +- .../Ryujinx.Graphics.Vulkan.csproj | 2 +- Ryujinx.HLE/FileSystem/ContentManager.cs | 8 +- Ryujinx.HLE/HOS/Horizon.cs | 4 +- .../HOS/Services/Account/Acc/Types/UserId.cs | 3 +- Ryujinx.HLE/HOS/Services/Mii/Helper.cs | 4 +- .../HOS/Services/Mii/Types/CharInfo.cs | 2 +- .../HOS/Services/Mii/Types/CreateId.cs | 13 +- .../HOS/Services/Mii/Types/StoreData.cs | 3 +- Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs | 11 +- .../Nifm/StaticService/IGeneralService.cs | 4 +- .../Nifm/StaticService/Types/DnsSetting.cs | 9 +- .../StaticService/Types/IpAddressSetting.cs | 5 +- .../StaticService/Types/NetworkProfileData.cs | 2 +- .../Settings/ISystemSettingsServer.cs | 6 +- .../SslService/SslManagedSocketConnection.cs | 3 + .../Services/Time/Clock/SteadyClockCore.cs | 6 +- .../Time/Clock/Types/SteadyClockTimePoint.cs | 6 +- .../Time/Clock/Types/SystemClockContext.cs | 2 +- Ryujinx.HLE/HOS/Services/Time/TimeManager.cs | 2 +- .../Services/Time/TimeZone/TimeZoneManager.cs | 2 +- .../Services/Time/Types/SteadyClockContext.cs | 4 +- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- Ryujinx.HLE/Utilities/UInt128.cs | 75 ------------ .../Ryujinx.Headless.SDL2.csproj | 4 +- Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj | 2 +- Ryujinx.Input/Ryujinx.Input.csproj | 4 +- .../Ryujinx.Memory.Tests.csproj | 2 +- Ryujinx.Memory/Ryujinx.Memory.csproj | 2 +- .../Ryujinx.SDL2.Common.csproj | 2 +- .../Ryujinx.ShaderTools.csproj | 2 +- .../Ryujinx.Tests.Unicorn.csproj | 2 +- Ryujinx.Tests/Ryujinx.Tests.csproj | 2 +- Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj | 2 +- Ryujinx/Ryujinx.csproj | 4 +- Spv.Generator/Spv.Generator.csproj | 2 +- global.json | 2 +- 71 files changed, 144 insertions(+), 301 deletions(-) create mode 100644 Ryujinx.Common/Utilities/UInt128Utils.cs delete mode 100644 Ryujinx.Graphics.Shader/Translation/UInt128.cs delete mode 100644 Ryujinx.HLE/Utilities/UInt128.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6feb14b84..0970d7288 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,7 +51,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Ensure NuGet Source uses: fabriciomurta/ensure-nuget-source@v1 - name: Get git short hash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index af665d51c..1b24ce3f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Ensure NuGet Source uses: fabriciomurta/ensure-nuget-source@v1 - name: Clear diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj index e29e33e89..bb3f47219 100644 --- a/ARMeilleure/ARMeilleure.csproj +++ b/ARMeilleure/ARMeilleure.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true diff --git a/README.md b/README.md index 3b7e802f9..0b59dd348 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of If you wish to build the emulator yourself, follow these steps: ### Step 1 -Install the X64 version of [.NET 6.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/6.0). +Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0). ### Step 2 Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files. diff --git a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj index f1e783e6e..a29848cf3 100644 --- a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj +++ b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj index fa70d3410..525f1f5b6 100644 --- a/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj +++ b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true diff --git a/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj b/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj index 1c8f7ad0e..9f242dbe2 100644 --- a/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj +++ b/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true win10-x64;linux-x64;osx-x64 diff --git a/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs index 72f19a672..91877cdda 100644 --- a/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs +++ b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs @@ -150,7 +150,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// /// The splitter header. /// The raw data after the splitter header. - private void UpdateState(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan input) + private void UpdateState(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan input) { for (int i = 0; i < inputHeader.SplitterCount; i++) { @@ -177,7 +177,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// /// The splitter header. /// The raw data after the splitter header. - private void UpdateData(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan input) + private void UpdateData(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan input) { for (int i = 0; i < inputHeader.SplitterDestinationCount; i++) { diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj index 2499bb44e..4a159eb5c 100644 --- a/Ryujinx.Audio/Ryujinx.Audio.csproj +++ b/Ryujinx.Audio/Ryujinx.Audio.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 551fa976f..3b4d8cf02 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -1,6 +1,6 @@  - net6.0 + net7.0 win10-x64;osx-x64;linux-x64 Exe true @@ -8,11 +8,13 @@ $(DefineConstants);$(ExtraDefineConstants) Ryujinx.Ava Ryujinx.ico + true true true + partial diff --git a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs index 05c77fe9a..2c24678d1 100644 --- a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs +++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs @@ -53,5 +53,11 @@ namespace Ryujinx.Common writer.Write(data); } + + public static void Write(this BinaryWriter writer, UInt128 value) + { + writer.Write((ulong)value); + writer.Write((ulong)(value >> 64)); + } } } diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj index e0cc2d56c..818e5ba33 100644 --- a/Ryujinx.Common/Ryujinx.Common.csproj +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true diff --git a/Ryujinx.Common/Utilities/SpanHelpers.cs b/Ryujinx.Common/Utilities/SpanHelpers.cs index 84c130233..4765eab3a 100644 --- a/Ryujinx.Common/Utilities/SpanHelpers.cs +++ b/Ryujinx.Common/Utilities/SpanHelpers.cs @@ -7,19 +7,19 @@ namespace Ryujinx.Common.Utilities public static class SpanHelpers { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span CreateSpan(ref T reference, int length) + public static Span CreateSpan(scoped ref T reference, int length) { return MemoryMarshal.CreateSpan(ref reference, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(ref T reference) where T : unmanaged + public static Span AsSpan(scoped ref T reference) where T : unmanaged { return CreateSpan(ref reference, 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(ref TStruct reference) + public static Span AsSpan(scoped ref TStruct reference) where TStruct : unmanaged where TSpan : unmanaged { return CreateSpan(ref Unsafe.As(ref reference), @@ -27,25 +27,25 @@ namespace Ryujinx.Common.Utilities } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsByteSpan(ref T reference) where T : unmanaged + public static Span AsByteSpan(scoped ref T reference) where T : unmanaged { return CreateSpan(ref Unsafe.As(ref reference), Unsafe.SizeOf()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan CreateReadOnlySpan(ref T reference, int length) + public static ReadOnlySpan CreateReadOnlySpan(scoped ref T reference, int length) { return MemoryMarshal.CreateReadOnlySpan(ref reference, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsReadOnlySpan(ref T reference) where T : unmanaged + public static ReadOnlySpan AsReadOnlySpan(scoped ref T reference) where T : unmanaged { return CreateReadOnlySpan(ref reference, 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsReadOnlySpan(ref TStruct reference) + public static ReadOnlySpan AsReadOnlySpan(scoped ref TStruct reference) where TStruct : unmanaged where TSpan : unmanaged { return CreateReadOnlySpan(ref Unsafe.As(ref reference), @@ -53,7 +53,7 @@ namespace Ryujinx.Common.Utilities } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsReadOnlyByteSpan(ref T reference) where T : unmanaged + public static ReadOnlySpan AsReadOnlyByteSpan(scoped ref T reference) where T : unmanaged { return CreateReadOnlySpan(ref Unsafe.As(ref reference), Unsafe.SizeOf()); } diff --git a/Ryujinx.Common/Utilities/UInt128Utils.cs b/Ryujinx.Common/Utilities/UInt128Utils.cs new file mode 100644 index 000000000..8cc437d17 --- /dev/null +++ b/Ryujinx.Common/Utilities/UInt128Utils.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Common.Utilities +{ + public static class UInt128Utils + { + public static UInt128 FromHex(string hex) + { + return new UInt128((ulong)Convert.ToInt64(hex.Substring(0, 16), 16), (ulong)Convert.ToInt64(hex.Substring(16), 16)); + } + + public static UInt128 CreateRandom() + { + return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/Ryujinx.Cpu.csproj b/Ryujinx.Cpu/Ryujinx.Cpu.csproj index 84972af11..7da8da25a 100644 --- a/Ryujinx.Cpu/Ryujinx.Cpu.csproj +++ b/Ryujinx.Cpu/Ryujinx.Cpu.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj b/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj index fff78129b..082dac9c2 100644 --- a/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj +++ b/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj b/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj index 725f48eab..189108a39 100644 --- a/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj +++ b/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index cd84024b3..892d9f6a7 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -413,10 +413,10 @@ namespace Ryujinx.Graphics.Gpu.Image [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateCachedBuffer( int stageIndex, - ref int cachedTextureBufferIndex, - ref int cachedSamplerBufferIndex, - ref ReadOnlySpan cachedTextureBuffer, - ref ReadOnlySpan cachedSamplerBuffer, + scoped ref int cachedTextureBufferIndex, + scoped ref int cachedSamplerBufferIndex, + scoped ref ReadOnlySpan cachedTextureBuffer, + scoped ref ReadOnlySpan cachedSamplerBuffer, int textureBufferIndex, int samplerBufferIndex) { diff --git a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj index 7b5d73b69..5255a6e00 100644 --- a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj +++ b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs b/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs index 9b5e8013e..d7cb3d99e 100644 --- a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs +++ b/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs @@ -350,7 +350,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// The item on the table, if found, otherwise unmodified /// The data on the table, if found, otherwise unmodified /// Table lookup result - public SearchResult TryFindItem(ref SmartDataAccessor dataAccessor, int size, ref T item, ref byte[] data) + public SearchResult TryFindItem(scoped ref SmartDataAccessor dataAccessor, int size, scoped ref T item, scoped ref byte[] data) { if (_count == 0) { diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs b/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs index 4c9cc4d40..f26fbdbb3 100644 --- a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs +++ b/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// The item on the table, if found, otherwise unmodified /// The data on the table, if found, otherwise unmodified /// Table lookup result - public PartitionHashTable.SearchResult TryFindItem(ref SmartDataAccessor dataAccessor, ref T item, ref byte[] data) + public PartitionHashTable.SearchResult TryFindItem(scoped ref SmartDataAccessor dataAccessor, scoped ref T item, scoped ref byte[] data) { return _table.TryFindItem(ref dataAccessor, Size, ref item, ref data); } diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index a8047f08c..28818304a 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -552,11 +552,11 @@ namespace Ryujinx.Graphics.Gpu.Shader private static void UpdateCachedBuffer( GpuChannel channel, bool isCompute, - ref int cachedTextureBufferIndex, - ref int cachedSamplerBufferIndex, - ref ReadOnlySpan cachedTextureBuffer, - ref ReadOnlySpan cachedSamplerBuffer, - ref int cachedStageIndex, + scoped ref int cachedTextureBufferIndex, + scoped ref int cachedSamplerBufferIndex, + scoped ref ReadOnlySpan cachedTextureBuffer, + scoped ref ReadOnlySpan cachedSamplerBuffer, + scoped ref int cachedStageIndex, int textureBufferIndex, int samplerBufferIndex, int stageIndex) diff --git a/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj b/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj index 49ed1a5c5..3cff4061e 100644 --- a/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj +++ b/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj b/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj index b30e6aea0..bff1e803b 100644 --- a/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj +++ b/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj b/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj index b30e6aea0..bff1e803b 100644 --- a/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj +++ b/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj b/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj index 68c0c2af1..bfba98a73 100644 --- a/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj +++ b/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj index d84ab2368..c12d6ff26 100644 --- a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj +++ b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index eed27c94e..3434e2a81 100644 --- a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 227471601..c70ec16c6 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -254,7 +254,7 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributes |= mask; _thisUsedInputAttributes |= mask; - ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component); + ThisInputAttributesComponents |= UInt128.One << (index * 4 + component); } public void SetInputUserAttributePerPatch(int index) diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index ff0de1bd0..8741f8487 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -190,10 +190,10 @@ namespace Ryujinx.Graphics.Shader.Translation UInt128 usedAttributes = context.Config.NextInputAttributesComponents; while (usedAttributes != UInt128.Zero) { - int index = usedAttributes.TrailingZeroCount(); + int index = (int)UInt128.TrailingZeroCount(usedAttributes); int vecIndex = index / 4; - usedAttributes &= ~UInt128.Pow2(index); + usedAttributes &= ~(UInt128.One << index); // We don't need to initialize passthrough attributes. if ((context.Config.PassthroughAttributes & (1 << vecIndex)) != 0) diff --git a/Ryujinx.Graphics.Shader/Translation/UInt128.cs b/Ryujinx.Graphics.Shader/Translation/UInt128.cs deleted file mode 100644 index ffbce77a1..000000000 --- a/Ryujinx.Graphics.Shader/Translation/UInt128.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Numerics; - -namespace Ryujinx.Graphics.Shader.Translation -{ - struct UInt128 : IEquatable - { - public static UInt128 Zero => new UInt128() { _v0 = 0, _v1 = 0 }; - - private ulong _v0; - private ulong _v1; - - public UInt128(ulong low, ulong high) - { - _v0 = low; - _v1 = high; - } - - public int TrailingZeroCount() - { - int count = BitOperations.TrailingZeroCount(_v0); - if (count == 64) - { - count += BitOperations.TrailingZeroCount(_v1); - } - - return count; - } - - public static UInt128 Pow2(int x) - { - if (x >= 64) - { - return new UInt128(0, 1UL << (x - 64)); - } - - return new UInt128(1UL << x, 0); - } - - public static UInt128 operator ~(UInt128 x) - { - return new UInt128(~x._v0, ~x._v1); - } - - public static UInt128 operator &(UInt128 x, UInt128 y) - { - return new UInt128(x._v0 & y._v0, x._v1 & y._v1); - } - - public static UInt128 operator |(UInt128 x, UInt128 y) - { - return new UInt128(x._v0 | y._v0, x._v1 | y._v1); - } - - public static UInt128 operator <<(UInt128 x, int shift) - { - if (shift == 0) - { - return new UInt128(x._v0, x._v1); - } - else if (shift >= 64) - { - return new UInt128(0, x._v0 << (shift - 64)); - } - - ulong shiftOut = x._v0 >> (64 - shift); - - return new UInt128(x._v0 << shift, (x._v1 << shift) | shiftOut); - } - - public static UInt128 operator >>(UInt128 x, int shift) - { - if (shift == 0) - { - return new UInt128(x._v0, x._v1); - } - else if (shift >= 64) - { - return new UInt128(x._v1 >> (shift - 64), 0); - } - - ulong shiftOut = x._v1 & ((1UL << shift) - 1); - - return new UInt128((x._v0 >> shift) | (shiftOut << (64 - shift)), x._v1 >> shift); - } - - public static bool operator ==(UInt128 x, UInt128 y) - { - return x.Equals(y); - } - - public static bool operator !=(UInt128 x, UInt128 y) - { - return !x.Equals(y); - } - - public override bool Equals(object obj) - { - return obj is UInt128 other && Equals(other); - } - - public bool Equals(UInt128 other) - { - return _v0 == other._v0 && _v1 == other._v1; - } - - public override int GetHashCode() - { - return HashCode.Combine(_v0, _v1); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj index 6af7e775e..70e3453c3 100644 --- a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj +++ b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj @@ -1,6 +1,6 @@ - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs b/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs index dda766a58..d9717bf85 100644 --- a/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs +++ b/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs @@ -229,8 +229,8 @@ namespace Ryujinx.Graphics.Vic.Image private static RentedBuffer ReadBuffer( ResourceManager rm, - ref SlotConfig config, - ref Array8 offsets, + scoped ref SlotConfig config, + scoped ref Array8 offsets, bool linear, int plane, int width, diff --git a/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj b/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj index 0e564d029..2a7cdd985 100644 --- a/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj +++ b/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true diff --git a/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj b/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj index 484b71774..9cf37670e 100644 --- a/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj +++ b/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 diff --git a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index fe22f3f5f..c98d66423 100644 --- a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/Ryujinx.HLE/FileSystem/ContentManager.cs b/Ryujinx.HLE/FileSystem/ContentManager.cs index 24945af28..652c24695 100644 --- a/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -9,10 +9,10 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.Ncm; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Services.Ssl; using Ryujinx.HLE.HOS.Services.Time; -using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; using System.IO; @@ -293,7 +293,7 @@ namespace Ryujinx.HLE.FileSystem } aocStorage = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()).OpenStorage(NcaSectionType.Data, integrityCheckLevel); - + return true; } @@ -354,7 +354,7 @@ namespace Ryujinx.HLE.FileSystem { if (_contentDictionary.ContainsKey((titleId, contentType))) { - return new UInt128(_contentDictionary[(titleId, contentType)]); + return UInt128Utils.FromHex(_contentDictionary[(titleId, contentType)]); } } @@ -407,7 +407,7 @@ namespace Ryujinx.HLE.FileSystem { return false; } - + string installedPath = _virtualFileSystem.SwitchPathToSystemPath(locationEntry.ContentPath); if (!string.IsNullOrWhiteSpace(installedPath)) diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index b93ebc032..d2716beb6 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -10,6 +10,7 @@ using Ryujinx.Audio.Integration; using Ryujinx.Audio.Output; using Ryujinx.Audio.Renderer.Device; using Ryujinx.Audio.Renderer.Server; +using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.Cpu.Jit; using Ryujinx.HLE.FileSystem; @@ -35,7 +36,6 @@ using Ryujinx.HLE.HOS.Services.SurfaceFlinger; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Executables; -using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; using System.IO; @@ -201,7 +201,7 @@ namespace Ryujinx.HLE.HOS // TODO: use set:sys (and get external clock source id from settings) // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. - UInt128 clockSourceId = new UInt128(Guid.NewGuid().ToByteArray()); + UInt128 clockSourceId = UInt128Utils.CreateRandom(); IRtcManager.GetExternalRtcValue(out ulong rtcValue); // We assume the rtc is system time. diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs index 85ddb4393..8cf4bff16 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs @@ -1,5 +1,4 @@ using LibHac.Account; -using Ryujinx.HLE.Utilities; using System; using System.IO; using System.Linq; @@ -83,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public readonly UInt128 ToUInt128() { - return new UInt128(Low, High); + return new UInt128((ulong)High, (ulong)Low); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Mii/Helper.cs b/Ryujinx.HLE/HOS/Services/Mii/Helper.cs index 47debd59d..b02bbfd18 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/Helper.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/Helper.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.Utilities; +using Ryujinx.Common.Utilities; using System; using System.Buffers.Binary; @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii public static UInt128 GetDeviceId() { // FIXME: call set:sys GetMiiAuthorId - return new UInt128("5279754d69694e780000000000000000"); // RyuMiiNx + return UInt128Utils.FromHex("5279754d69694e780000000000000000"); // RyuMiiNx } public static ReadOnlySpan Ver3FacelineColorTable => new byte[] { 0, 1, 2, 3, 4, 5 }; diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs b/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs index 98e0f3072..256ec9e06 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Mii.Types { - [StructLayout(LayoutKind.Sequential, Size = 0x58)] + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x58)] struct CharInfo : IStoredData { public CreateId CreateId; diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs b/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs index 630a8c9d3..c1a97f526 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs @@ -1,20 +1,19 @@ -using Ryujinx.HLE.Utilities; -using System; +using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Mii.Types { - [StructLayout(LayoutKind.Sequential, Size = 0x10)] + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] struct CreateId : IEquatable { public UInt128 Raw; - public bool IsNull => Raw.IsNull; - public bool IsValid => !IsNull && (Raw.High & 0xC0) == 0x80; + public bool IsNull => Raw == UInt128.Zero; + public bool IsValid => !IsNull && ((Raw >> 64) & 0xC0) == 0x80; - public CreateId(byte[] data) + public CreateId(UInt128 raw) { - Raw = new UInt128(data); + Raw = raw; } public static bool operator ==(CreateId x, CreateId y) diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs b/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs index 6b7e3e5a0..31c46bc09 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs @@ -1,11 +1,12 @@ using LibHac.Common; using Ryujinx.HLE.Utilities; using System; +using System.Diagnostics; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Mii.Types { - [StructLayout(LayoutKind.Sequential, Pack = 4, Size = Size)] + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] struct StoreData : IStoredData { public const int Size = 0x44; diff --git a/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs b/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs index 8b7f33130..30b201f65 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs @@ -1,4 +1,5 @@ -using Ryujinx.Cpu; +using Ryujinx.Common.Utilities; +using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Mii.Types; using Ryujinx.HLE.HOS.Services.Time; using Ryujinx.HLE.HOS.Services.Time.Clock; @@ -62,7 +63,13 @@ namespace Ryujinx.HLE.HOS.Services.Mii public CreateId MakeCreateId() { - return new CreateId(Guid.NewGuid().ToByteArray()); + UInt128 value = UInt128Utils.CreateRandom(); + + // Ensure the random ID generated is valid as a create id. + value &= ~new UInt128(0xC0, 0); + value |= new UInt128(0x80, 0); + + return new CreateId(value); } } } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index e04dd8134..4a0259e60 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -1,8 +1,8 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types; -using Ryujinx.HLE.Utilities; using System; using System.Net.NetworkInformation; using System.Runtime.CompilerServices; @@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService NetworkProfileData networkProfile = new NetworkProfileData { - Uuid = new UInt128(Guid.NewGuid().ToByteArray()) + Uuid = UInt128Utils.CreateRandom() }; networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress); diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs index 9092f6e08..374558eab 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs @@ -14,14 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types public DnsSetting(IPInterfaceProperties interfaceProperties) { - try - { - IsDynamicDnsEnabled = interfaceProperties.IsDynamicDnsEnabled; - } - catch (PlatformNotSupportedException) - { - IsDynamicDnsEnabled = false; - } + IsDynamicDnsEnabled = OperatingSystem.IsWindows() && interfaceProperties.IsDynamicDnsEnabled; if (interfaceProperties.DnsAddresses.Count == 0) { diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs index 50e6b01c6..5bb046abe 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs @@ -1,4 +1,5 @@ -using System.Net.NetworkInformation; +using System; +using System.Net.NetworkInformation; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types @@ -14,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types public IpAddressSetting(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastIPAddressInformation) { - IsDhcpEnabled = interfaceProperties.DhcpServerAddresses.Count != 0; + IsDhcpEnabled = !OperatingSystem.IsMacOS() && interfaceProperties.DhcpServerAddresses.Count != 0; Address = new IpV4Address(unicastIPAddressInformation.Address); IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask); GatewayAddress = new IpV4Address(interfaceProperties.GatewayAddresses[0].Address); diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs index 3c86aed50..e270c10ae 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs @@ -1,5 +1,5 @@ using Ryujinx.Common.Memory; -using Ryujinx.HLE.Utilities; +using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index 1ce7bbfc1..7f32ce6bd 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -5,9 +5,9 @@ using LibHac.Fs.Fsa; using LibHac.FsSystem; using LibHac.Ncm; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Utilities; using System; using System.IO; using System.Text; @@ -290,9 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings // NOTE: If miiAuthorId is null ResultCode.NullMiiAuthorIdBuffer is returned. // Doesn't occur in our case. - UInt128 miiAuthorId = Mii.Helper.GetDeviceId(); - - miiAuthorId.Write(context.ResponseData); + context.ResponseData.Write(Mii.Helper.GetDeviceId()); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs index 36c8b51a7..56bfa709b 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs @@ -66,6 +66,8 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService EndSslOperation(); } +// NOTE: We silence warnings about TLS 1.0 and 1.1 as games will likely use it. +#pragma warning disable SYSLIB0039 private static SslProtocols TranslateSslVersion(SslVersion version) { switch (version & SslVersion.VersionMask) @@ -84,6 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService throw new NotImplementedException(version.ToString()); } } +#pragma warning restore SYSLIB0039 public ResultCode Handshake(string hostName) { diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs index 4bb19e752..18da4ed3e 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs @@ -1,5 +1,5 @@ -using Ryujinx.Cpu; -using Ryujinx.HLE.Utilities; +using Ryujinx.Common.Utilities; +using Ryujinx.Cpu; using System; namespace Ryujinx.HLE.HOS.Services.Time.Clock @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public SteadyClockCore() { - _clockSourceId = new UInt128(Guid.NewGuid().ToByteArray()); + _clockSourceId = UInt128Utils.CreateRandom(); _isRtcResetDetected = false; _isInitialized = false; } diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs index 71fb45212..729e11b6b 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs @@ -1,10 +1,10 @@ -using Ryujinx.HLE.Utilities; +using Ryujinx.Common.Utilities; using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time.Clock { - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SteadyClockTimePoint { public long TimePoint; @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return new SteadyClockTimePoint { TimePoint = 0, - ClockSourceId = new UInt128(Guid.NewGuid().ToByteArray()) + ClockSourceId = UInt128Utils.CreateRandom() }; } } diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs index 38e10480e..6b589c28a 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SystemClockContext { public long Offset; diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs index ac9f0880b..e3b65f2ad 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs @@ -3,7 +3,7 @@ using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.Services.Time.TimeZone; -using Ryujinx.HLE.Utilities; +using System; using System.IO; namespace Ryujinx.HLE.HOS.Services.Time diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs index 736bc1025..ef4b7b399 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs @@ -1,6 +1,6 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Time.Clock; -using Ryujinx.HLE.Utilities; +using System; using System.IO; namespace Ryujinx.HLE.HOS.Services.Time.TimeZone diff --git a/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs b/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs index 4cf1fc991..38d37055c 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs @@ -1,9 +1,9 @@ -using Ryujinx.HLE.Utilities; +using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time.Types { - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SteadyClockContext { public ulong InternalOffset; diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index c04ba7cf4..bdcbaca86 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs deleted file mode 100644 index 22d87f6b1..000000000 --- a/Ryujinx.HLE/Utilities/UInt128.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.Utilities -{ - [StructLayout(LayoutKind.Sequential)] - public struct UInt128 : IEquatable - { - public readonly long Low; - public readonly long High; - - public bool IsNull => (Low | High) == 0; - - public UInt128(long low, long high) - { - Low = low; - High = high; - } - - public UInt128(byte[] bytes) - { - Low = BitConverter.ToInt64(bytes, 0); - High = BitConverter.ToInt64(bytes, 8); - } - - public UInt128(string hex) - { - if (hex == null || hex.Length != 32 || !hex.All("0123456789abcdefABCDEF".Contains)) - { - throw new ArgumentException("Invalid Hex value!", nameof(hex)); - } - - Low = Convert.ToInt64(hex.Substring(16), 16); - High = Convert.ToInt64(hex.Substring(0, 16), 16); - } - - public void Write(BinaryWriter binaryWriter) - { - binaryWriter.Write(Low); - binaryWriter.Write(High); - } - - public override string ToString() - { - return High.ToString("x16") + Low.ToString("x16"); - } - - public static bool operator ==(UInt128 x, UInt128 y) - { - return x.Equals(y); - } - - public static bool operator !=(UInt128 x, UInt128 y) - { - return !x.Equals(y); - } - - public override bool Equals(object obj) - { - return obj is UInt128 uint128 && Equals(uint128); - } - - public bool Equals(UInt128 cmpObj) - { - return Low == cmpObj.Low && High == cmpObj.High; - } - - public override int GetHashCode() - { - return HashCode.Combine(Low, High); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index 929b4131c..a9d227c63 100644 --- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -1,12 +1,13 @@  - net6.0 + net7.0 win10-x64;osx-x64;linux-x64 Exe true 1.0.0-dirty $(DefineConstants);$(ExtraDefineConstants) + true @@ -46,5 +47,6 @@ true true + partial diff --git a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj index e93c02ee7..817a96e2e 100644 --- a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj +++ b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.Input/Ryujinx.Input.csproj b/Ryujinx.Input/Ryujinx.Input.csproj index c7c76abc6..55111f77f 100644 --- a/Ryujinx.Input/Ryujinx.Input.csproj +++ b/Ryujinx.Input/Ryujinx.Input.csproj @@ -1,14 +1,14 @@ - net6.0 + net7.0 true - + diff --git a/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj b/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj index 90a7e54a1..323b51823 100644 --- a/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj +++ b/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 false diff --git a/Ryujinx.Memory/Ryujinx.Memory.csproj b/Ryujinx.Memory/Ryujinx.Memory.csproj index 0b2ed7061..91e46e48e 100644 --- a/Ryujinx.Memory/Ryujinx.Memory.csproj +++ b/Ryujinx.Memory/Ryujinx.Memory.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 true diff --git a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj index bf4e04271..51b3b374e 100644 --- a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj +++ b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 diff --git a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj index 898528277..51416232e 100644 --- a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj +++ b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Exe Debug;Release diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj index 2005e4fb4..b3ee86d37 100644 --- a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj +++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true Debug;Release diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 42a35e9ea..6ab2fa6b1 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 Exe false diff --git a/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj b/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj index c5247279a..4dd1dba83 100644 --- a/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj +++ b/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 582f99f0f..23f5d1559 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 win10-x64;osx-x64;linux-x64 Exe true @@ -9,11 +9,13 @@ $(DefineConstants);$(ExtraDefineConstants) true + true true true + partial diff --git a/Spv.Generator/Spv.Generator.csproj b/Spv.Generator/Spv.Generator.csproj index fff78129b..082dac9c2 100644 --- a/Spv.Generator/Spv.Generator.csproj +++ b/Spv.Generator/Spv.Generator.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/global.json b/global.json index d6c2c37f7..1c7274b72 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.100", + "version": "7.0.100", "rollForward": "latestFeature" } } \ No newline at end of file From a6a67a2b7add9a9dc8c4f0bab730957b8ebaf6e8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 10 Nov 2022 13:38:38 -0300 Subject: [PATCH 065/737] Minor improvement to Vulkan pipeline state and bindings management (#3829) * Minor improvement to Vulkan pipeline state and bindings management * Clean up buffer textures too * Use glBindTextureUnit --- Ryujinx.Graphics.Gpu/Image/Texture.cs | 3 +- Ryujinx.Graphics.OpenGL/Image/TextureBase.cs | 6 + Ryujinx.Graphics.OpenGL/Pipeline.cs | 5 + .../DescriptorSetUpdater.cs | 26 +-- Ryujinx.Graphics.Vulkan/PipelineState.cs | 167 ++++++++---------- Ryujinx.Graphics.Vulkan/PipelineUid.cs | 20 +-- 6 files changed, 103 insertions(+), 124 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 847cfbfe5..4203cb003 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -708,11 +708,12 @@ namespace Ryujinx.Graphics.Gpu.Image else { bool dataMatches = _currentData != null && data.SequenceEqual(_currentData); - _currentData = data.ToArray(); if (dataMatches) { return; } + + _currentData = data.ToArray(); } } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs index 2e70fa82a..2ab9dffbc 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs @@ -34,5 +34,11 @@ namespace Ryujinx.Graphics.OpenGL.Image GL.ActiveTexture(TextureUnit.Texture0 + unit); GL.BindTexture(target, Handle); } + + public static void ClearBinding(int unit) + { + GL.ActiveTexture(TextureUnit.Texture0 + unit); + GL.BindTextureUnit(unit, 0); + } } } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 5f2246253..5911758ef 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -919,6 +919,7 @@ namespace Ryujinx.Graphics.OpenGL if (texture == null) { + GL.BindImageTexture(binding, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); return; } @@ -1275,6 +1276,10 @@ namespace Ryujinx.Graphics.OpenGL ((TextureBase)texture).Bind(binding); } } + else + { + TextureBase.ClearBinding(binding); + } Sampler glSampler = (Sampler)sampler; diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 9e3723116..e0d5d2863 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -138,11 +138,6 @@ namespace Ryujinx.Graphics.Vulkan public void SetImage(int binding, ITexture image, GAL.Format imageFormat) { - if (image == null) - { - return; - } - if (image is TextureBuffer imageBuffer) { _bufferImageRefs[binding] = imageBuffer; @@ -152,6 +147,12 @@ namespace Ryujinx.Graphics.Vulkan { _imageRefs[binding] = view.GetView(imageFormat).GetIdentityImageView(); } + else + { + _imageRefs[binding] = null; + _bufferImageRefs[binding] = null; + _bufferImageFormats[binding] = default; + } SignalDirty(DirtyFlags.Image); } @@ -215,24 +216,23 @@ namespace Ryujinx.Graphics.Vulkan public void SetTextureAndSampler(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture texture, ISampler sampler) { - if (texture == null) - { - return; - } - if (texture is TextureBuffer textureBuffer) { _bufferTextureRefs[binding] = textureBuffer; } - else + else if (texture is TextureView view) { - TextureView view = (TextureView)texture; - view.Storage.InsertBarrier(cbs, AccessFlags.AccessShaderReadBit, stage.ConvertToPipelineStageFlags()); _textureRefs[binding] = view.GetImageView(); _samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler(); } + else + { + _textureRefs[binding] = null; + _samplerRefs[binding] = null; + _bufferTextureRefs[binding] = null; + } SignalDirty(DirtyFlags.Texture); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineState.cs b/Ryujinx.Graphics.Vulkan/PipelineState.cs index 7b6a26490..e20a66493 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -81,232 +81,208 @@ namespace Ryujinx.Graphics.Vulkan set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); } - public float BlendConstantR - { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id6 >> 0) & 0xFFFFFFFF)); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); - } - - public float BlendConstantG - { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id6 >> 32) & 0xFFFFFFFF)); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); - } - - public float BlendConstantB - { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id7 >> 0) & 0xFFFFFFFF)); - set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); - } - - public float BlendConstantA - { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id7 >> 32) & 0xFFFFFFFF)); - set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); - } - public PolygonMode PolygonMode { - get => (PolygonMode)((Internal.Id8 >> 0) & 0x3FFFFFFF); - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFC0000000) | ((ulong)value << 0); + get => (PolygonMode)((Internal.Id6 >> 0) & 0x3FFFFFFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC0000000) | ((ulong)value << 0); } public uint StagesCount { - get => (byte)((Internal.Id8 >> 30) & 0xFF); - set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30); + get => (byte)((Internal.Id6 >> 30) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30); } public uint VertexAttributeDescriptionsCount { - get => (byte)((Internal.Id8 >> 38) & 0xFF); - set => Internal.Id8 = (Internal.Id8 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38); + get => (byte)((Internal.Id6 >> 38) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38); } public uint VertexBindingDescriptionsCount { - get => (byte)((Internal.Id8 >> 46) & 0xFF); - set => Internal.Id8 = (Internal.Id8 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46); + get => (byte)((Internal.Id6 >> 46) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46); } public uint ViewportsCount { - get => (byte)((Internal.Id8 >> 54) & 0xFF); - set => Internal.Id8 = (Internal.Id8 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54); + get => (byte)((Internal.Id6 >> 54) & 0xFF); + set => Internal.Id6 = (Internal.Id6 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54); } public uint ScissorsCount { - get => (byte)((Internal.Id9 >> 0) & 0xFF); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); + get => (byte)((Internal.Id7 >> 0) & 0xFF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); } public uint ColorBlendAttachmentStateCount { - get => (byte)((Internal.Id9 >> 8) & 0xFF); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); + get => (byte)((Internal.Id7 >> 8) & 0xFF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); } public PrimitiveTopology Topology { - get => (PrimitiveTopology)((Internal.Id9 >> 16) & 0xF); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); + get => (PrimitiveTopology)((Internal.Id7 >> 16) & 0xF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); } public LogicOp LogicOp { - get => (LogicOp)((Internal.Id9 >> 20) & 0xF); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20); + get => (LogicOp)((Internal.Id7 >> 20) & 0xF); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20); } public CompareOp DepthCompareOp { - get => (CompareOp)((Internal.Id9 >> 24) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24); + get => (CompareOp)((Internal.Id7 >> 24) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24); } public StencilOp StencilFrontFailOp { - get => (StencilOp)((Internal.Id9 >> 27) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27); + get => (StencilOp)((Internal.Id7 >> 27) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27); } public StencilOp StencilFrontPassOp { - get => (StencilOp)((Internal.Id9 >> 30) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30); + get => (StencilOp)((Internal.Id7 >> 30) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30); } public StencilOp StencilFrontDepthFailOp { - get => (StencilOp)((Internal.Id9 >> 33) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33); + get => (StencilOp)((Internal.Id7 >> 33) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33); } public CompareOp StencilFrontCompareOp { - get => (CompareOp)((Internal.Id9 >> 36) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36); + get => (CompareOp)((Internal.Id7 >> 36) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36); } public StencilOp StencilBackFailOp { - get => (StencilOp)((Internal.Id9 >> 39) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39); + get => (StencilOp)((Internal.Id7 >> 39) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39); } public StencilOp StencilBackPassOp { - get => (StencilOp)((Internal.Id9 >> 42) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42); + get => (StencilOp)((Internal.Id7 >> 42) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42); } public StencilOp StencilBackDepthFailOp { - get => (StencilOp)((Internal.Id9 >> 45) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45); + get => (StencilOp)((Internal.Id7 >> 45) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45); } public CompareOp StencilBackCompareOp { - get => (CompareOp)((Internal.Id9 >> 48) & 0x7); - set => Internal.Id9 = (Internal.Id9 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48); + get => (CompareOp)((Internal.Id7 >> 48) & 0x7); + set => Internal.Id7 = (Internal.Id7 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48); } public CullModeFlags CullMode { - get => (CullModeFlags)((Internal.Id9 >> 51) & 0x3); - set => Internal.Id9 = (Internal.Id9 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51); + get => (CullModeFlags)((Internal.Id7 >> 51) & 0x3); + set => Internal.Id7 = (Internal.Id7 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51); } public bool PrimitiveRestartEnable { - get => ((Internal.Id9 >> 53) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53); + get => ((Internal.Id7 >> 53) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53); } public bool DepthClampEnable { - get => ((Internal.Id9 >> 54) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54); + get => ((Internal.Id7 >> 54) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54); } public bool RasterizerDiscardEnable { - get => ((Internal.Id9 >> 55) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55); + get => ((Internal.Id7 >> 55) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55); } public FrontFace FrontFace { - get => (FrontFace)((Internal.Id9 >> 56) & 0x1); - set => Internal.Id9 = (Internal.Id9 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56); + get => (FrontFace)((Internal.Id7 >> 56) & 0x1); + set => Internal.Id7 = (Internal.Id7 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56); } public bool DepthBiasEnable { - get => ((Internal.Id9 >> 57) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57); + get => ((Internal.Id7 >> 57) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57); } public bool DepthTestEnable { - get => ((Internal.Id9 >> 58) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58); + get => ((Internal.Id7 >> 58) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58); } public bool DepthWriteEnable { - get => ((Internal.Id9 >> 59) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59); + get => ((Internal.Id7 >> 59) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59); } public bool DepthBoundsTestEnable { - get => ((Internal.Id9 >> 60) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60); + get => ((Internal.Id7 >> 60) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60); } public bool StencilTestEnable { - get => ((Internal.Id9 >> 61) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61); + get => ((Internal.Id7 >> 61) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61); } public bool LogicOpEnable { - get => ((Internal.Id9 >> 62) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62); + get => ((Internal.Id7 >> 62) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62); } public bool HasDepthStencil { - get => ((Internal.Id9 >> 63) & 0x1) != 0UL; - set => Internal.Id9 = (Internal.Id9 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); + get => ((Internal.Id7 >> 63) & 0x1) != 0UL; + set => Internal.Id7 = (Internal.Id7 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); } public uint PatchControlPoints { - get => (uint)((Internal.Id10 >> 0) & 0xFFFFFFFF); - set => Internal.Id10 = (Internal.Id10 & 0xFFFFFFFF00000000) | ((ulong)value << 0); + get => (uint)((Internal.Id8 >> 0) & 0xFFFFFFFF); + set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF00000000) | ((ulong)value << 0); } public uint SamplesCount { - get => (uint)((Internal.Id10 >> 32) & 0xFFFFFFFF); - set => Internal.Id10 = (Internal.Id10 & 0xFFFFFFFF) | ((ulong)value << 32); + get => (uint)((Internal.Id8 >> 32) & 0xFFFFFFFF); + set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF) | ((ulong)value << 32); } public bool AlphaToCoverageEnable { - get => ((Internal.Id11 >> 0) & 0x1) != 0UL; - set => Internal.Id11 = (Internal.Id11 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0); + get => ((Internal.Id9 >> 0) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0); } public bool AlphaToOneEnable { - get => ((Internal.Id11 >> 1) & 0x1) != 0UL; - set => Internal.Id11 = (Internal.Id11 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); + get => ((Internal.Id9 >> 1) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); } public NativeArray Stages; @@ -356,7 +332,7 @@ namespace Ryujinx.Graphics.Vulkan Pipeline pipelineHandle = default; bool hasSpec = program.SpecDescriptions != null; - + var desc = hasSpec ? program.SpecDescriptions[0] : SpecDescription.Empty; if (hasSpec && SpecializationData.Length < (int)desc.Info.DataSize) @@ -510,11 +486,6 @@ namespace Ryujinx.Graphics.Vulkan PAttachments = pColorBlendAttachmentState }; - colorBlendState.BlendConstants[0] = BlendConstantR; - colorBlendState.BlendConstants[1] = BlendConstantG; - colorBlendState.BlendConstants[2] = BlendConstantB; - colorBlendState.BlendConstants[3] = BlendConstantA; - bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState; int dynamicStatesCount = supportsExtDynamicState ? 9 : 8; diff --git a/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/Ryujinx.Graphics.Vulkan/PipelineUid.cs index 8ea52e20d..78d6e9f71 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineUid.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineUid.cs @@ -21,15 +21,13 @@ namespace Ryujinx.Graphics.Vulkan public ulong Id8; public ulong Id9; - public ulong Id10; - public ulong Id11; - private uint VertexAttributeDescriptionsCount => (byte)((Id8 >> 38) & 0xFF); - private uint VertexBindingDescriptionsCount => (byte)((Id8 >> 46) & 0xFF); - private uint ViewportsCount => (byte)((Id8 >> 54) & 0xFF); - private uint ScissorsCount => (byte)((Id9 >> 0) & 0xFF); - private uint ColorBlendAttachmentStateCount => (byte)((Id9 >> 8) & 0xFF); - private bool HasDepthStencil => ((Id9 >> 63) & 0x1) != 0UL; + private uint VertexAttributeDescriptionsCount => (byte)((Id6 >> 38) & 0xFF); + private uint VertexBindingDescriptionsCount => (byte)((Id6 >> 46) & 0xFF); + private uint ViewportsCount => (byte)((Id6 >> 54) & 0xFF); + private uint ScissorsCount => (byte)((Id7 >> 0) & 0xFF); + private uint ColorBlendAttachmentStateCount => (byte)((Id7 >> 8) & 0xFF); + private bool HasDepthStencil => ((Id7 >> 63) & 0x1) != 0UL; public Array32 VertexAttributeDescriptions; public Array33 VertexBindingDescriptions; @@ -47,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan { if (!Unsafe.As>(ref Id0).Equals(Unsafe.As>(ref other.Id0)) || !Unsafe.As>(ref Id4).Equals(Unsafe.As>(ref other.Id4)) || - !Unsafe.As>(ref Id8).Equals(Unsafe.As>(ref other.Id8))) + !Unsafe.As>(ref Id8).Equals(Unsafe.As>(ref other.Id8))) { return false; } @@ -91,9 +89,7 @@ namespace Ryujinx.Graphics.Vulkan Id6 * 23 ^ Id7 * 23 ^ Id8 * 23 ^ - Id9 * 23 ^ - Id10 * 23 ^ - Id11 * 23; + Id9 * 23; for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) { From 51a27032f01826e0cec56c53da4359fd2c38c8f3 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 11 Nov 2022 13:22:49 -0300 Subject: [PATCH 066/737] Fix VertexId and InstanceId on Vulkan (#3833) * Fix VertexId and InstanceId on Vulkan * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/OperandManager.cs | 4 ++++ .../CodeGen/Spirv/Declarations.cs | 8 ++++++-- .../Instructions/InstEmitAttribute.cs | 19 ++++++++++++++++++- .../Translation/AttributeConsts.cs | 5 +++++ .../Translation/AttributeInfo.cs | 4 ++++ 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 02ae06f65..3f3a3c50e 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3807; + private const uint CodeGenVersion = 3833; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index fd284316f..67442e5a1 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -48,6 +48,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, + { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstance", VariableType.S32) }, + { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertex", VariableType.S32) }, + { AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) }, + { AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) }, { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, // Special. diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 9f8dd7dfa..c007b9a20 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -704,8 +704,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance, AttributeConsts.PointCoordX => BuiltIn.PointCoord, AttributeConsts.TessCoordX => BuiltIn.TessCoord, - AttributeConsts.InstanceId => BuiltIn.InstanceId, // FIXME: Invalid - AttributeConsts.VertexId => BuiltIn.VertexId, // FIXME: Invalid + AttributeConsts.InstanceId => BuiltIn.InstanceId, + AttributeConsts.VertexId => BuiltIn.VertexId, + AttributeConsts.BaseInstance => BuiltIn.BaseInstance, + AttributeConsts.BaseVertex => BuiltIn.BaseVertex, + AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex, + AttributeConsts.VertexIndex => BuiltIn.VertexIndex, AttributeConsts.FrontFacing => BuiltIn.FrontFacing, AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth, AttributeConsts.ThreadKill => BuiltIn.HelperInvocation, diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 7edf5deb8..2f75d248b 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Shader.Instructions offset |= AttributeConsts.LoadOutputMask; } - Operand src = op.P ? AttributePerPatch(offset) : Attribute(offset); + Operand src = op.P ? AttributePerPatch(offset) : CreateInputAttribute(context, offset); context.Copy(Register(rd), src); } @@ -312,5 +312,22 @@ namespace Ryujinx.Graphics.Shader.Instructions return attr; } + + private static Operand CreateInputAttribute(EmitterContext context, int attr) + { + if (context.Config.Options.TargetApi == TargetApi.Vulkan) + { + if (attr == AttributeConsts.InstanceId) + { + return context.ISubtract(Attribute(AttributeConsts.InstanceIndex), Attribute(AttributeConsts.BaseInstance)); + } + else if (attr == AttributeConsts.VertexId) + { + return Attribute(AttributeConsts.VertexIndex); + } + } + + return Attribute(attr); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index f4e39d0da..47367f89c 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -95,5 +95,10 @@ namespace Ryujinx.Graphics.Shader.Translation public const int LtMask = 0x2000040; public const int ThreadKill = 0x2000044; + + public const int BaseInstance = 0x2000050; + public const int BaseVertex = 0x2000054; + public const int InstanceIndex = 0x2000058; + public const int VertexIndex = 0x200005c; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index 35dd56e82..6e95722ff 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -27,6 +27,10 @@ namespace Ryujinx.Graphics.Shader.Translation { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, + { AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) }, + { AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) }, + { AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) }, + { AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) }, { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, // Special. From 9daf029f356898336de1ad0c63b6c36e261e4f9b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 12 Nov 2022 20:20:40 -0300 Subject: [PATCH 067/737] Use vector transform feedback outputs if possible (#3832) --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/CodeGenContext.cs | 20 +---- .../CodeGen/Glsl/Declarations.cs | 36 ++++++-- .../CodeGen/Glsl/GlslGenerator.cs | 2 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 2 +- .../Glsl/Instructions/InstGenMemory.cs | 4 +- .../CodeGen/Glsl/OperandManager.cs | 22 +++-- .../CodeGen/Spirv/CodeGenContext.cs | 28 +++--- .../CodeGen/Spirv/Declarations.cs | 87 +++++++++++++------ .../CodeGen/Spirv/SpirvGenerator.cs | 14 ++- .../StructuredIr/StructuredProgram.cs | 6 +- .../StructuredIr/StructuredProgramInfo.cs | 35 ++++++++ .../Translation/ShaderConfig.cs | 4 + 13 files changed, 180 insertions(+), 82 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 3f3a3c50e..e728c48c4 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3833; + private const uint CodeGenVersion = 3831; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs index 418af6cb7..9eb20f6f8 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs @@ -10,12 +10,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public StructuredFunction CurrentFunction { get; set; } + public StructuredProgramInfo Info { get; } + public ShaderConfig Config { get; } public OperandManager OperandManager { get; } - private readonly StructuredProgramInfo _info; - private readonly StringBuilder _sb; private int _level; @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) { - _info = info; + Info = info; Config = config; OperandManager = new OperandManager(); @@ -72,19 +72,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public StructuredFunction GetFunction(int id) { - return _info.Functions[id]; - } - - public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component) - { - int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component; - return _info.TransformFeedbackOutputs[index]; - } - - public TransformFeedbackOutput GetTransformFeedbackOutput(int location) - { - int index = location / 4; - return _info.TransformFeedbackOutputs[index]; + return Info.Functions[id]; } private void UpdateIndentation() diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 91fd286d4..4f2751b12 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -210,7 +210,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) { - var tfOutput = context.GetTransformFeedbackOutput(AttributeConsts.PositionX); + var tfOutput = context.Info.GetTransformFeedbackOutput(AttributeConsts.PositionX); if (tfOutput.Valid) { context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex"); @@ -604,19 +604,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) { - for (int c = 0; c < 4; c++) + int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; + int components = context.Config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1; + + if (components > 1) { - char swzMask = "xyzw"[c]; + string type = components switch + { + 2 => "vec2", + 3 => "vec3", + 4 => "vec4", + _ => "float" + }; string xfb = string.Empty; - var tfOutput = context.GetTransformFeedbackOutput(attr, c); + var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset); if (tfOutput.Valid) { xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; } - context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};"); + context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};"); + } + else + { + for (int c = 0; c < 4; c++) + { + char swzMask = "xyzw"[c]; + + string xfb = string.Empty; + + var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4); + if (tfOutput.Valid) + { + xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; + } + + context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};"); + } } } else diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index e9dbdd2d3..e1b8eb6ec 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute()) { bool perPatch = operand.Type == OperandType.AttributePerPatch; - dest = OperandManager.GetOutAttributeName(operand.Value, context.Config, perPatch); + dest = OperandManager.GetOutAttributeName(context, operand.Value, perPatch); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 388285a8f..b890b0158 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else if (node is AstOperand operand) { - return context.OperandManager.GetExpression(operand, context.Config); + return context.OperandManager.GetExpression(context, operand); } throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 094040013..022e3a444 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (src2 is AstOperand operand && operand.Type == OperandType.Constant) { int attrOffset = baseAttr.Value + (operand.Value << 2); - return OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: false, indexExpr); + return OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: false, indexExpr); } else { @@ -332,7 +332,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (src2 is AstOperand operand && operand.Type == OperandType.Constant) { int attrOffset = baseAttr.Value + (operand.Value << 2); - attrName = OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: true); + attrName = OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: true); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 67442e5a1..031b1c44c 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -103,15 +103,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return name; } - public string GetExpression(AstOperand operand, ShaderConfig config) + public string GetExpression(CodeGenContext context, AstOperand operand) { return operand.Type switch { OperandType.Argument => GetArgumentName(operand.Value), - OperandType.Attribute => GetAttributeName(operand.Value, config, perPatch: false), - OperandType.AttributePerPatch => GetAttributeName(operand.Value, config, perPatch: true), + OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false), + OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), - OperandType.ConstantBuffer => GetConstantBufferName(operand, config), + OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config), OperandType.LocalVariable => _locals[operand], OperandType.Undefined => DefaultNames.UndefinedName, _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") @@ -153,13 +153,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); } - public static string GetOutAttributeName(int value, ShaderConfig config, bool perPatch) + public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch) { - return GetAttributeName(value, config, perPatch, isOutAttr: true); + return GetAttributeName(context, value, perPatch, isOutAttr: true); } - public static string GetAttributeName(int value, ShaderConfig config, bool perPatch, bool isOutAttr = false, string indexExpr = "0") + public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0") { + ShaderConfig config = context.Config; + if ((value & AttributeConsts.LoadOutputMask) != 0) { isOutAttr = true; @@ -192,6 +194,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) { + int attrOffset = value; value -= AttributeConsts.UserAttributeBase; string prefix = isOutAttr @@ -215,14 +218,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl ((config.LastInVertexPipeline && isOutAttr) || (config.Stage == ShaderStage.Fragment && !isOutAttr))) { - string name = $"{prefix}{(value >> 4)}_{swzMask}"; + int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1; + string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}"; if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) { name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; } - return name; + return components > 1 ? name + '.' + swzMask : name; } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 04c053253..dff5474a1 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private const uint SpirvVersionRevision = 0; private const uint SpirvVersionPacked = (SpirvVersionMajor << 16) | (SpirvVersionMinor << 8) | SpirvVersionRevision; - private readonly StructuredProgramInfo _info; + public StructuredProgramInfo Info { get; } public ShaderConfig Config { get; } @@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv GeneratorPool instPool, GeneratorPool integerPool) : base(SpirvVersionPacked, instPool, integerPool) { - _info = info; + Info = info; Config = config; if (config.Stage == ShaderStage.Geometry) @@ -317,6 +317,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { attrOffset = attr; type = elemType; + + if (Config.LastInPipeline && isOutAttr) + { + int components = Info.GetTransformFeedbackOutputComponents(attr); + + if (components > 1) + { + attrOffset &= ~0xf; + type = AggregateType.Vector | AggregateType.FP32; + attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false); + } + } } ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset]; @@ -536,18 +548,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return _functions[funcIndex]; } - public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component) - { - int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component; - return _info.TransformFeedbackOutputs[index]; - } - - public TransformFeedbackOutput GetTransformFeedbackOutput(int location) - { - int index = location / 4; - return _info.TransformFeedbackOutputs[index]; - } - public Instruction GetType(AggregateType type, int length = 1) { if (type.HasFlag(AggregateType.Array)) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index c007b9a20..fafb917db 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -440,11 +440,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { PixelImap iq = PixelImap.Unused; - if (context.Config.Stage == ShaderStage.Fragment && - attr >= AttributeConsts.UserAttributeBase && - attr < AttributeConsts.UserAttributeEnd) + if (context.Config.Stage == ShaderStage.Fragment) { - iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); + if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) + { + iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); + } + else + { + AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false); + AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask; + + if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32)) + { + iq = PixelImap.Constant; + } + } } DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq); @@ -516,7 +527,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv ((isOutAttr && context.Config.LastInVertexPipeline) || (!isOutAttr && context.Config.Stage == ShaderStage.Fragment))) { - DeclareInputOrOutput(context, attr, (attr >> 2) & 3, isOutAttr, iq); + DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq); return; } @@ -572,7 +583,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) { - var tfOutput = context.GetTransformFeedbackOutput(attrInfo.BaseValue); + var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue); if (tfOutput.Valid) { context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); @@ -595,24 +606,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); - if (!isOutAttr) + if (!isOutAttr && + !perPatch && + (context.Config.PassthroughAttributes & (1 << location)) != 0 && + context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { - if (!perPatch && - (context.Config.PassthroughAttributes & (1 << location)) != 0 && - context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) - { - context.Decorate(spvVar, Decoration.PassthroughNV); - } - - switch (iq) - { - case PixelImap.Constant: - context.Decorate(spvVar, Decoration.Flat); - break; - case PixelImap.ScreenLinear: - context.Decorate(spvVar, Decoration.NoPerspective); - break; - } + context.Decorate(spvVar, Decoration.PassthroughNV); } } else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) @@ -621,22 +620,52 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); } + if (!isOutAttr) + { + switch (iq) + { + case PixelImap.Constant: + context.Decorate(spvVar, Decoration.Flat); + break; + case PixelImap.ScreenLinear: + context.Decorate(spvVar, Decoration.NoPerspective); + break; + } + } + context.AddGlobalVariable(spvVar); dict.Add(attrInfo.BaseValue, spvVar); } - private static void DeclareInputOrOutput(CodeGenContext context, int attr, int component, bool isOutAttr, PixelImap iq = PixelImap.Unused) + private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused) { var dict = isOutAttr ? context.Outputs : context.Inputs; var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); + bool hasComponent = true; + int component = (attr >> 2) & 3; + int components = 1; + var type = attrInfo.Type & AggregateType.ElementTypeMask; + + if (context.Config.LastInPipeline && isOutAttr) + { + components = context.Info.GetTransformFeedbackOutputComponents(attr); + + if (components > 1) + { + attr &= ~0xf; + type = AggregateType.Vector | AggregateType.FP32; + hasComponent = false; + } + } + if (dict.ContainsKey(attr)) { return; } var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrType = context.GetType(attrInfo.Type & AggregateType.ElementTypeMask); + var attrType = context.GetType(type, components); if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) { @@ -656,11 +685,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv int location = (attr - AttributeConsts.UserAttributeBase) / 16; context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); - context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component); + + if (hasComponent) + { + context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component); + } if (isOutAttr) { - var tfOutput = context.GetTransformFeedbackOutput(location, component); + var tfOutput = context.Info.GetTransformFeedbackOutput(attr); if (tfOutput.Valid) { context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index fad7f9b88..69283b0a3 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -62,10 +62,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddCapability(Capability.TransformFeedback); } - if (config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + if (config.Stage == ShaderStage.Fragment) { - context.AddCapability(Capability.FragmentShaderPixelInterlockEXT); - context.AddExtension("SPV_EXT_fragment_shader_interlock"); + if (context.Info.Inputs.Contains(AttributeConsts.Layer)) + { + context.AddCapability(Capability.Geometry); + } + + if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + { + context.AddCapability(Capability.FragmentShaderPixelInterlockEXT); + context.AddExtension("SPV_EXT_fragment_shader_interlock"); + } } else if (config.Stage == ShaderStage.Geometry) { diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 85049abb2..7678a4bf6 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -71,12 +71,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); - for (int j = 0; j < locations.Length; j++) + for (int i = 0; i < locations.Length; i++) { - byte location = locations[j]; + byte location = locations[i]; if (location < 0xc0) { - context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, j * 4, stride); + context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, i * 4, stride); } } } diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index 43bdfaba5..57253148f 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -42,5 +42,40 @@ namespace Ryujinx.Graphics.Shader.StructuredIr TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; } + + public TransformFeedbackOutput GetTransformFeedbackOutput(int attr) + { + int index = attr / 4; + return TransformFeedbackOutputs[index]; + } + + public int GetTransformFeedbackOutputComponents(int attr) + { + int index = attr / 4; + int baseIndex = index & ~3; + + int count = 1; + + for (; count < 4; count++) + { + ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1]; + ref var curr = ref TransformFeedbackOutputs[baseIndex + count]; + + int prevOffset = prev.Offset; + int currOffset = curr.Offset; + + if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset) + { + break; + } + } + + if (baseIndex + count <= index) + { + return 1; + } + + return count; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index c70ec16c6..fcf35ce27 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderStage Stage { get; } public bool GpPassthrough { get; } + public bool LastInPipeline { get; private set; } public bool LastInVertexPipeline { get; private set; } public int ThreadsPerInputPrimitive { get; } @@ -143,6 +144,7 @@ namespace Ryujinx.Graphics.Shader.Translation OmapSampleMask = header.OmapSampleMask; OmapDepth = header.OmapDepth; TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); + LastInPipeline = true; LastInVertexPipeline = header.Stage < ShaderStage.Fragment; } @@ -306,6 +308,8 @@ namespace Ryujinx.Graphics.Shader.Translation config._perPatchAttributeLocations = locationsMap; } + LastInPipeline = false; + // We don't consider geometry shaders using the geometry shader passthrough feature // as being the last because when this feature is used, it can't actually modify any of the outputs, // so the stage that comes before it is the last one that can do modifications. From eebc39228db4663e03fa73306e725424f7ce1273 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 13 Nov 2022 00:36:36 +0100 Subject: [PATCH 068/737] UI: Allow overriding graphics backend + Move command line parser into a new class (#3707) * Ava: Keep command line args when restarting * UI: Move common UI functions to ProgramHelper Add command line option to override the configured graphics backend * Ava: Add CleanupUpdate task back * Remove unused usings * Revert combining common UI functions Rename ProgramHelper to CommandLineState Move command line parsing to CommandLineState * Rename CommandLineProfile to Profile * Fix assigning the wrong array to Arguments --- Ryujinx.Ava/App.axaml.cs | 4 +- Ryujinx.Ava/Program.cs | 65 +++++---------- .../Ui/ViewModels/SettingsViewModel.cs | 4 - Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 5 +- Ryujinx.Ui.Common/Helper/CommandLineState.cs | 81 +++++++++++++++++++ Ryujinx/Modules/Updater/UpdateDialog.cs | 7 +- Ryujinx/Program.cs | 78 ++++++------------ Ryujinx/Ui/MainWindow.cs | 4 +- 8 files changed, 137 insertions(+), 111 deletions(-) create mode 100644 Ryujinx.Ui.Common/Helper/CommandLineState.cs diff --git a/Ryujinx.Ava/App.axaml.cs b/Ryujinx.Ava/App.axaml.cs index 180c74b43..3ab1b7b8f 100644 --- a/Ryujinx.Ava/App.axaml.cs +++ b/Ryujinx.Ava/App.axaml.cs @@ -10,6 +10,7 @@ using Ryujinx.Ava.Ui.Windows; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Helper; using System; using System.Diagnostics; using System.IO; @@ -64,8 +65,7 @@ namespace Ryujinx.Ava if (result == UserResult.Yes) { var path = Process.GetCurrentProcess().MainModule.FileName; - var info = new ProcessStartInfo() { FileName = path, UseShellExecute = false }; - var proc = Process.Start(info); + var proc = Process.Start(path, CommandLineState.Arguments); desktop.Shutdown(); Environment.Exit(0); } diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 61b184c61..053bccab6 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -13,6 +13,7 @@ using Ryujinx.Common.SystemInfo; using Ryujinx.Modules; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Helper; using System; using System.IO; using System.Runtime.InteropServices; @@ -26,7 +27,6 @@ namespace Ryujinx.Ava public static double ActualScaleFactor { get; set; } public static string Version { get; private set; } public static string ConfigurationPath { get; private set; } - public static string CommandLineProfile { get; set; } public static bool PreviewerDetached { get; private set; } public static RenderTimer RenderTimer { get; private set; } @@ -87,46 +87,8 @@ namespace Ryujinx.Ava private static void Initialize(string[] args) { - // Parse Arguments. - string launchPathArg = null; - string baseDirPathArg = null; - bool startFullscreenArg = false; - - for (int i = 0; i < args.Length; ++i) - { - string arg = args[i]; - - if (arg == "-r" || arg == "--root-data-dir") - { - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - baseDirPathArg = args[++i]; - } - else if (arg == "-p" || arg == "--profile") - { - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - CommandLineProfile = args[++i]; - } - else if (arg == "-f" || arg == "--fullscreen") - { - startFullscreenArg = true; - } - else - { - launchPathArg = arg; - } - } + // Parse arguments + CommandLineState.ParseArguments(args); // Delete backup files after updating. Task.Run(Updater.CleanupUpdate); @@ -135,10 +97,10 @@ namespace Ryujinx.Ava // Hook unhandled exception and process exit events. AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); + AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); // Setup base data directory. - AppDataManager.Initialize(baseDirPathArg); + AppDataManager.Initialize(CommandLineState.BaseDirPathArg); // Initialize the configuration. ConfigurationState.Initialize(); @@ -173,9 +135,9 @@ namespace Ryujinx.Ava } } - if (launchPathArg != null) + if (CommandLineState.LaunchPathArg != null) { - MainWindow.DeferLoadApplication(launchPathArg, startFullscreenArg); + MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); } } @@ -215,6 +177,19 @@ namespace Ryujinx.Ava Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}"); } } + + // Check if graphics backend was overridden + if (CommandLineState.OverrideGraphicsBackend != null) + { + if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl") + { + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; + } + else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan") + { + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; + } + } } private static void PrintSystemInfo() diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs index f4807023d..088bf3ac9 100644 --- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs @@ -21,14 +21,10 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.Input; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; -using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; -using System.Threading.Tasks; using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone; namespace Ryujinx.Ava.Ui.ViewModels diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs index ae79b1c9d..774178d66 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs @@ -23,6 +23,7 @@ using Ryujinx.Modules; using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Helper; using SixLabors.ImageSharp.PixelFormats; using System; using System.ComponentModel; @@ -247,7 +248,7 @@ namespace Ryujinx.Ava.Ui.Windows { RendererControl.CreateVulkan(); } - + AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this); if (!AppHost.LoadGuestApplication().Result) @@ -432,7 +433,7 @@ namespace Ryujinx.Ava.Ui.Windows // Consider removing this at some point in the future when we don't need to worry about old saves. VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient); - AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, Program.CommandLineProfile); + AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, CommandLineState.Profile); VirtualFileSystem.ReloadKeySet(); diff --git a/Ryujinx.Ui.Common/Helper/CommandLineState.cs b/Ryujinx.Ui.Common/Helper/CommandLineState.cs new file mode 100644 index 000000000..cda4af4ed --- /dev/null +++ b/Ryujinx.Ui.Common/Helper/CommandLineState.cs @@ -0,0 +1,81 @@ +using Ryujinx.Common.Logging; +using System.Collections.Generic; + +namespace Ryujinx.Ui.Common.Helper +{ + public static class CommandLineState + { + public static string[] Arguments { get; private set; } + + public static string OverrideGraphicsBackend { get; private set; } + public static string BaseDirPathArg { get; private set; } + public static string Profile { get; private set; } + public static string LaunchPathArg { get; private set; } + public static bool StartFullscreenArg { get; private set; } + + public static void ParseArguments(string[] args) + { + List arguments = new(); + + // Parse Arguments. + for (int i = 0; i < args.Length; ++i) + { + string arg = args[i]; + + switch (arg) + { + case "-r": + case "--root-data-dir": + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + BaseDirPathArg = args[++i]; + + arguments.Add(arg); + arguments.Add(args[i]); + break; + case "-p": + case "--profile": + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + Profile = args[++i]; + + arguments.Add(arg); + arguments.Add(args[i]); + break; + case "-f": + case "--fullscreen": + StartFullscreenArg = true; + + arguments.Add(arg); + break; + case "-g": + case "--graphics-backend": + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + OverrideGraphicsBackend = args[++i]; + break; + default: + LaunchPathArg = arg; + break; + } + } + + Arguments = arguments.ToArray(); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Modules/Updater/UpdateDialog.cs b/Ryujinx/Modules/Updater/UpdateDialog.cs index cdf85427e..cb71fafc9 100644 --- a/Ryujinx/Modules/Updater/UpdateDialog.cs +++ b/Ryujinx/Modules/Updater/UpdateDialog.cs @@ -2,9 +2,9 @@ using Gdk; using Gtk; using Ryujinx.Ui; using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Helper; using System; using System.Diagnostics; -using System.Linq; using System.Reflection; namespace Ryujinx.Modules @@ -48,9 +48,8 @@ namespace Ryujinx.Modules { string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); - var ryuArg = Environment.GetCommandLineArgs().AsEnumerable().Skip(1); - Process.Start(ryuExe, ryuArg); + Process.Start(ryuExe, CommandLineState.Arguments); Environment.Exit(0); } @@ -81,4 +80,4 @@ namespace Ryujinx.Modules Dispose(); } } -} +} \ No newline at end of file diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index d9db941de..a91f9aa52 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -6,10 +6,11 @@ using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.System; using Ryujinx.Common.SystemInfo; -using Ryujinx.Ui.Common.Configuration; using Ryujinx.Modules; using Ryujinx.Ui; using Ryujinx.Ui.Common; +using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Widgets; using SixLabors.ImageSharp.Formats.Jpeg; using System; @@ -28,8 +29,6 @@ namespace Ryujinx public static string ConfigurationPath { get; set; } - public static string CommandLineProfile { get; set; } - [DllImport("libX11")] private extern static int XInitThreads(); @@ -47,46 +46,13 @@ namespace Ryujinx MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING); } - // Parse Arguments. - string launchPathArg = null; - string baseDirPathArg = null; - bool startFullscreenArg = false; + // Parse arguments + CommandLineState.ParseArguments(args); - for (int i = 0; i < args.Length; ++i) - { - string arg = args[i]; - - if (arg == "-r" || arg == "--root-data-dir") - { - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - baseDirPathArg = args[++i]; - } - else if (arg == "-p" || arg == "--profile") - { - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - CommandLineProfile = args[++i]; - } - else if (arg == "-f" || arg == "--fullscreen") - { - startFullscreenArg = true; - } - else if (launchPathArg == null) - { - launchPathArg = arg; - } - } + // Hook unhandled exception and process exit events. + GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); + AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); + AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); // Make process DPI aware for proper window sizing on high-res screens. ForceDpiAware.Windows(); @@ -95,8 +61,6 @@ namespace Ryujinx // Delete backup files after updating. Task.Run(Updater.CleanupUpdate); - Console.Title = $"Ryujinx Console {Version}"; - // NOTE: GTK3 doesn't init X11 in a multi threaded way. // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads). if (OperatingSystem.IsLinux()) @@ -107,13 +71,8 @@ namespace Ryujinx string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}"); - // Hook unhandled exception and process exit events. - GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); - // Setup base data directory. - AppDataManager.Initialize(baseDirPathArg); + AppDataManager.Initialize(CommandLineState.BaseDirPathArg); // Initialize the configuration. ConfigurationState.Initialize(); @@ -173,6 +132,21 @@ namespace Ryujinx } } + // Check if graphics backend was overridden + if (CommandLineState.OverrideGraphicsBackend != null) + { + if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl") + { + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; + showVulkanPrompt = false; + } + else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan") + { + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; + showVulkanPrompt = false; + } + } + // Logging system information. PrintSystemInfo(); @@ -195,9 +169,9 @@ namespace Ryujinx MainWindow mainWindow = new MainWindow(); mainWindow.Show(); - if (launchPathArg != null) + if (CommandLineState.LaunchPathArg != null) { - mainWindow.LoadApplication(launchPathArg, startFullscreenArg); + mainWindow.LoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); } if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index c0b2e1b66..c78b7a2f2 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -179,7 +179,7 @@ namespace Ryujinx.Ui VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient); _contentManager = new ContentManager(_virtualFileSystem); - _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, Program.CommandLineProfile); + _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile); _userChannelPersistence = new UserChannelPersistence(); // Instantiate GUI objects. @@ -1752,4 +1752,4 @@ namespace Ryujinx.Ui UpdateGameTable(); } } -} +} \ No newline at end of file From b8de72de8f25f0bb7f994bc07a0387c1c247b6fe Mon Sep 17 00:00:00 2001 From: EmulationFanatic <62343878+EmulationFanatic@users.noreply.github.com> Date: Tue, 15 Nov 2022 14:19:25 -0700 Subject: [PATCH 069/737] It's REE-YOU-JINX (#3839) --- Ryujinx/Ui/Windows/AboutWindow.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx/Ui/Windows/AboutWindow.Designer.cs b/Ryujinx/Ui/Windows/AboutWindow.Designer.cs index c0721664f..8117cf36b 100644 --- a/Ryujinx/Ui/Windows/AboutWindow.Designer.cs +++ b/Ryujinx/Ui/Windows/AboutWindow.Designer.cs @@ -113,7 +113,7 @@ namespace Ryujinx.Ui.Windows // // _ryujinxPhoneticLabel // - _ryujinxPhoneticLabel = new Label("(REE-YOU-JI-NX)") + _ryujinxPhoneticLabel = new Label("(REE-YOU-JINX)") { Justify = Justification.Center }; From f1d1670b0b1b5c08064df95dabd295f3cf5dcf7f Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 16 Nov 2022 14:53:04 -0300 Subject: [PATCH 070/737] Implement HLE macro for DrawElementsIndirect (#3748) * Implement HLE macro for DrawElementsIndirect * Shader cache version bump * Use GL_ARB_shader_draw_parameters extension on OpenGL * Fix DrawIndexedIndirectCount on Vulkan when extension is not supported * Implement DrawIndex * Alignment * Fix some validation errors * Rename BaseIds to DrawParameters * Fix incorrect index buffer and vertex buffer size in some cases * Add HLE macros for DrawArraysInstanced and DrawElementsInstanced * Perform a regular draw when indirect data is not modified * Use non-indirect draw methods if indirect buffer was not GPU modified * Only check if draw parameters match if the shader actually uses them * Expose Macro HLE setting on GUI * Reset FirstVertex and FirstInstance after draw * Update shader cache version again since some people already tested this * PR feedback Co-authored-by: riperiperi --- Ryujinx.Ava/Assets/Locales/en_US.json | 6 +- Ryujinx.Ava/Assets/Locales/pt_BR.json | 4 +- .../Ui/ViewModels/SettingsViewModel.cs | 3 + Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 1 + Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml | 4 + Ryujinx.Graphics.GAL/IPipeline.cs | 7 +- .../Multithreading/CommandHelper.cs | 12 +- .../Multithreading/CommandType.cs | 6 +- .../Commands/DrawIndexedIndirectCommand.cs | 18 + ....cs => DrawIndexedIndirectCountCommand.cs} | 8 +- .../Commands/DrawIndirectCommand.cs | 18 + ...Command.cs => DrawIndirectCountCommand.cs} | 8 +- .../Multithreading/ThreadedPipeline.cs | 36 +- Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs | 10 +- Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs | 177 +++++- .../Engine/MME/MacroHLEFunctionName.cs | 3 + .../Engine/MME/MacroHLETable.cs | 29 +- .../Engine/Threed/ConditionalRendering.cs | 6 +- .../Engine/Threed/DrawManager.cs | 175 +++++- .../Engine/Threed/DrawState.cs | 10 + .../Engine/Threed/IndirectDrawType.cs | 38 ++ .../Engine/Threed/StateUpdater.cs | 29 +- .../Engine/Threed/ThreedClass.cs | 64 ++- Ryujinx.Graphics.Gpu/Memory/BufferCache.cs | 40 +- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 6 + .../Shader/DiskCache/DiskCacheHostStorage.cs | 9 +- Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 6 + .../Shader/GpuChannelGraphicsState.cs | 10 +- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 4 +- .../Shader/ShaderSpecializationList.cs | 4 +- .../Shader/ShaderSpecializationState.cs | 13 +- Ryujinx.Graphics.OpenGL/Pipeline.cs | 140 +++-- .../CodeGen/Glsl/Declarations.cs | 5 + .../CodeGen/Glsl/OperandManager.cs | 5 +- .../CodeGen/Spirv/Declarations.cs | 1 + .../CodeGen/Spirv/SpirvGenerator.cs | 4 + Ryujinx.Graphics.Shader/Constants.cs | 4 + Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 + Ryujinx.Graphics.Shader/ShaderProgramInfo.cs | 3 + .../Translation/AttributeConsts.cs | 1 + .../Translation/AttributeInfo.cs | 1 + .../Translation/FeatureFlags.cs | 11 +- .../Translation/Rewriter.cs | 70 +++ .../Translation/Translator.cs | 1 + Ryujinx.Graphics.Vulkan/BufferHolder.cs | 20 + Ryujinx.Graphics.Vulkan/BufferManager.cs | 142 ++++- Ryujinx.Graphics.Vulkan/CacheByRange.cs | 166 +++++- Ryujinx.Graphics.Vulkan/HelperShader.cs | 159 +++++- Ryujinx.Graphics.Vulkan/IndexBufferState.cs | 64 ++- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 283 ++++++--- .../ConvertIndexBufferShaderSource.comp | 58 ++ .../ConvertIndirectDataShaderSource.comp | 103 ++++ .../Shaders/ShaderBinaries.cs | 538 ++++++++++++++++++ .../VulkanInitialization.cs | 3 +- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 2 +- .../Configuration/ConfigurationFileFormat.cs | 7 +- .../Configuration/ConfigurationState.cs | 17 + Ryujinx/Ui/MainWindow.cs | 1 + Ryujinx/Ui/Windows/SettingsWindow.cs | 7 + Ryujinx/Ui/Windows/SettingsWindow.glade | 24 +- 60 files changed, 2336 insertions(+), 277 deletions(-) create mode 100644 Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs rename Ryujinx.Graphics.GAL/Multithreading/Commands/{MultiDrawIndexedIndirectCountCommand.cs => DrawIndexedIndirectCountCommand.cs} (70%) create mode 100644 Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs rename Ryujinx.Graphics.GAL/Multithreading/Commands/{MultiDrawIndirectCountCommand.cs => DrawIndirectCountCommand.cs} (71%) create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 3a841157f..3a72be34f 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -587,10 +587,12 @@ "SettingsTabGraphicsPreferredGpu": "Preferred GPU", "SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", - "SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied", + "SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied", "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", "RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?", "SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:", "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", - "VolumeShort": "Vol" + "VolumeShort": "Vol", + "SettingsEnableMacroHLE": "Enable Macro HLE", + "SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure." } diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json index c5dbe49a4..01f629095 100644 --- a/Ryujinx.Ava/Assets/Locales/pt_BR.json +++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json @@ -556,5 +556,7 @@ "SettingsSelectThemeFileDialogTitle" : "Selecionar arquivo do tema", "SettingsXamlThemeFile" : "Arquivo de tema Xaml", "SettingsTabHotkeysResScaleUpHotkey": "Aumentar a resolução:", - "SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:" + "SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:", + "SettingsEnableMacroHLE": "Habilitar emulação de alto nível para Macros", + "SettingsEnableMacroHLETooltip": "Habilita emulação de alto nível de códigos Macro da GPU.\n\nMelhora a performance, mas pode causar problemas gráficos em alguns jogos.\n\nEm caso de dúvida, deixe ATIVADO." } diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs index 088bf3ac9..584627417 100644 --- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs @@ -134,6 +134,7 @@ namespace Ryujinx.Ava.Ui.ViewModels public bool ExpandDramSize { get; set; } public bool EnableShaderCache { get; set; } public bool EnableTextureRecompression { get; set; } + public bool EnableMacroHLE { get; set; } public bool EnableFileLog { get; set; } public bool EnableStub { get; set; } public bool EnableInfo { get; set; } @@ -335,6 +336,7 @@ namespace Ryujinx.Ava.Ui.ViewModels ExpandDramSize = config.System.ExpandRam; EnableShaderCache = config.Graphics.EnableShaderCache; EnableTextureRecompression = config.Graphics.EnableTextureRecompression; + EnableMacroHLE = config.Graphics.EnableMacroHLE; EnableFileLog = config.Logger.EnableFileLog; EnableStub = config.Logger.EnableStub; EnableInfo = config.Logger.EnableInfo; @@ -418,6 +420,7 @@ namespace Ryujinx.Ava.Ui.ViewModels config.Graphics.EnableVsync.Value = EnableVsync; config.Graphics.EnableShaderCache.Value = EnableShaderCache; config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression; + config.Graphics.EnableMacroHLE.Value = EnableMacroHLE; config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex; config.System.EnablePtc.Value = EnablePptc; config.System.EnableInternetAccess.Value = EnableInternetAccess; diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs index 774178d66..0e03803ba 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs @@ -526,6 +526,7 @@ namespace Ryujinx.Ava.Ui.Windows GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; + GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; } public void LoadHotKeys() diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml index 55fe83656..0946a2eb9 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml @@ -568,6 +568,10 @@ ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}"> + + + (memory), threaded, renderer); _lookup[(int)CommandType.DrawIndexed] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => DrawIndexedCommand.Run(ref GetCommand(memory), threaded, renderer); + _lookup[(int)CommandType.DrawIndexedIndirect] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => + DrawIndexedIndirectCommand.Run(ref GetCommand(memory), threaded, renderer); + _lookup[(int)CommandType.DrawIndexedIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => + DrawIndexedIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer); + _lookup[(int)CommandType.DrawIndirect] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => + DrawIndirectCommand.Run(ref GetCommand(memory), threaded, renderer); + _lookup[(int)CommandType.DrawIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => + DrawIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer); _lookup[(int)CommandType.DrawTexture] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => DrawTextureCommand.Run(ref GetCommand(memory), threaded, renderer); _lookup[(int)CommandType.EndHostConditionalRendering] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => EndHostConditionalRenderingCommand.Run(renderer); _lookup[(int)CommandType.EndTransformFeedback] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => EndTransformFeedbackCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.MultiDrawIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => - MultiDrawIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.MultiDrawIndexedIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => - MultiDrawIndexedIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer); _lookup[(int)CommandType.SetAlphaTest] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => SetAlphaTestCommand.Run(ref GetCommand(memory), threaded, renderer); _lookup[(int)CommandType.SetBlendState] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs index c25f0834e..c199ff34c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -52,11 +52,13 @@ DispatchCompute, Draw, DrawIndexed, + DrawIndexedIndirect, + DrawIndexedIndirectCount, + DrawIndirect, + DrawIndirectCount, DrawTexture, EndHostConditionalRendering, EndTransformFeedback, - MultiDrawIndirectCount, - MultiDrawIndexedIndirectCount, SetAlphaTest, SetBlendState, SetDepthBias, diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs new file mode 100644 index 000000000..3a47e9621 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndexedIndirectCommand : IGALCommand + { + public CommandType CommandType => CommandType.DrawIndexedIndirect; + private BufferRange _indirectBuffer; + + public void Set(BufferRange indirectBuffer) + { + _indirectBuffer = indirectBuffer; + } + + public static void Run(ref DrawIndexedIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndexedIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer)); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs similarity index 70% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs rename to Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs index 6798f8cc5..79d9792e9 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs @@ -1,8 +1,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct MultiDrawIndexedIndirectCountCommand : IGALCommand + struct DrawIndexedIndirectCountCommand : IGALCommand { - public CommandType CommandType => CommandType.MultiDrawIndexedIndirectCount; + public CommandType CommandType => CommandType.DrawIndexedIndirectCount; private BufferRange _indirectBuffer; private BufferRange _parameterBuffer; private int _maxDrawCount; @@ -16,9 +16,9 @@ _stride = stride; } - public static void Run(ref MultiDrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) + public static void Run(ref DrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.MultiDrawIndexedIndirectCount( + renderer.Pipeline.DrawIndexedIndirectCount( threaded.Buffers.MapBufferRange(command._indirectBuffer), threaded.Buffers.MapBufferRange(command._parameterBuffer), command._maxDrawCount, diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs new file mode 100644 index 000000000..73414e441 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndirectCommand : IGALCommand + { + public CommandType CommandType => CommandType.DrawIndirect; + private BufferRange _indirectBuffer; + + public void Set(BufferRange indirectBuffer) + { + _indirectBuffer = indirectBuffer; + } + + public static void Run(ref DrawIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer)); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs similarity index 71% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs rename to Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs index 7a9d07f33..96f60f4aa 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs @@ -1,8 +1,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct MultiDrawIndirectCountCommand : IGALCommand + struct DrawIndirectCountCommand : IGALCommand { - public CommandType CommandType => CommandType.MultiDrawIndirectCount; + public CommandType CommandType => CommandType.DrawIndirectCount; private BufferRange _indirectBuffer; private BufferRange _parameterBuffer; private int _maxDrawCount; @@ -16,9 +16,9 @@ _stride = stride; } - public static void Run(ref MultiDrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) + public static void Run(ref DrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.MultiDrawIndirectCount( + renderer.Pipeline.DrawIndirectCount( threaded.Buffers.MapBufferRange(command._indirectBuffer), threaded.Buffers.MapBufferRange(command._parameterBuffer), command._maxDrawCount, diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 723d29f1f..52d699335 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -83,6 +83,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } + public void DrawIndexedIndirect(BufferRange indirectBuffer) + { + _renderer.New().Set(indirectBuffer); + _renderer.QueueCommand(); + } + + public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.QueueCommand(); + } + + public void DrawIndirect(BufferRange indirectBuffer) + { + _renderer.New().Set(indirectBuffer); + _renderer.QueueCommand(); + } + + public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.QueueCommand(); + } + public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { _renderer.New().Set(Ref(texture), Ref(sampler), srcRegion, dstRegion); @@ -101,18 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); - _renderer.QueueCommand(); - } - - public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); - _renderer.QueueCommand(); - } - public void SetAlphaTest(bool enable, float reference, CompareOp op) { _renderer.New().Set(enable, reference, op); diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs index 1d054969a..12a3ac028 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs @@ -62,10 +62,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } } - if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount) + // We don't consume the parameter buffer value, so we don't need to flush it. + // Doing so improves performance if the value was written by a GPU shader. + if (_hleFunction == MacroHLEFunctionName.DrawElementsIndirect) + { + context.GPFifo.SetFlushSkips(1); + } + else if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount) { - // We don't consume the parameter buffer value, so we don't need to flush it. - // Doing so improves performance if the value was written by a GPU shader. context.GPFifo.SetFlushSkips(2); } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs index 5f238a718..8630bbc42 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs @@ -16,6 +16,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME private const int ColorStructSize = 0x40; private const int ZetaLayerCountOffset = 0x1230; + private const int IndirectDataEntrySize = 0x10; + private const int IndirectIndexedDataEntrySize = 0x14; + private readonly GPFifoProcessor _processor; private readonly MacroHLEFunctionName _functionName; @@ -27,9 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// /// Creates a new instance of the HLE macro handler. /// - /// GPU context the macro is being executed on - /// GPU memory manager - /// 3D engine where this macro is being called + /// GPU GP FIFO command processor /// Name of the HLE macro function to be called public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName) { @@ -55,12 +56,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME case MacroHLEFunctionName.ClearDepthStencil: ClearDepthStencil(state, arg0); break; + case MacroHLEFunctionName.DrawArraysInstanced: + DrawArraysInstanced(state, arg0); + break; + case MacroHLEFunctionName.DrawElementsInstanced: + DrawElementsInstanced(state, arg0); + break; + case MacroHLEFunctionName.DrawElementsIndirect: + DrawElementsIndirect(state, arg0); + break; case MacroHLEFunctionName.MultiDrawElementsIndirectCount: MultiDrawElementsIndirectCount(state, arg0); break; default: throw new NotImplementedException(_functionName.ToString()); } + + // It should be empty at this point, but clear it just to be safe. + Fifo.Clear(); } /// @@ -89,7 +102,118 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } /// - /// Performs a indirect multi-draw, with parameters from a GPU buffer. + /// Performs a draw. + /// + /// GPU state at the time of the call + /// First argument of the call + private void DrawArraysInstanced(IDeviceState state, int arg0) + { + var topology = (PrimitiveTopology)arg0; + + var count = FetchParam(); + var instanceCount = FetchParam(); + var firstVertex = FetchParam(); + var firstInstance = FetchParam(); + + if (ShouldSkipDraw(state, instanceCount.Word)) + { + return; + } + + _processor.ThreedClass.Draw( + topology, + count.Word, + instanceCount.Word, + 0, + firstVertex.Word, + firstInstance.Word, + indexed: false); + } + + /// + /// Performs a indexed draw. + /// + /// GPU state at the time of the call + /// First argument of the call + private void DrawElementsInstanced(IDeviceState state, int arg0) + { + var topology = (PrimitiveTopology)arg0; + + var count = FetchParam(); + var instanceCount = FetchParam(); + var firstIndex = FetchParam(); + var firstVertex = FetchParam(); + var firstInstance = FetchParam(); + + if (ShouldSkipDraw(state, instanceCount.Word)) + { + return; + } + + _processor.ThreedClass.Draw( + topology, + count.Word, + instanceCount.Word, + firstIndex.Word, + firstVertex.Word, + firstInstance.Word, + indexed: true); + } + + /// + /// Performs a indirect indexed draw, with parameters from a GPU buffer. + /// + /// GPU state at the time of the call + /// First argument of the call + private void DrawElementsIndirect(IDeviceState state, int arg0) + { + var topology = (PrimitiveTopology)arg0; + + var count = FetchParam(); + var instanceCount = FetchParam(); + var firstIndex = FetchParam(); + var firstVertex = FetchParam(); + var firstInstance = FetchParam(); + + ulong indirectBufferGpuVa = count.GpuVa; + + var bufferCache = _processor.MemoryManager.Physical.BufferCache; + + bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress); + + if (useBuffer) + { + int indexCount = firstIndex.Word + count.Word; + + _processor.ThreedClass.DrawIndirect( + topology, + indirectBufferAddress, + 0, + 1, + IndirectIndexedDataEntrySize, + indexCount, + Threed.IndirectDrawType.DrawIndexedIndirect); + } + else + { + if (ShouldSkipDraw(state, instanceCount.Word)) + { + return; + } + + _processor.ThreedClass.Draw( + topology, + count.Word, + instanceCount.Word, + firstIndex.Word, + firstVertex.Word, + firstInstance.Word, + indexed: true); + } + } + + /// + /// Performs a indirect indexed multi-draw, with parameters from a GPU buffer. /// /// GPU state at the time of the call /// First argument of the call @@ -132,8 +256,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME return; } - int indirectBufferSize = maxDrawCount * stride; - ulong indirectBufferGpuVa = 0; int indexCount = 0; @@ -142,8 +264,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME var count = FetchParam(); var instanceCount = FetchParam(); var firstIndex = FetchParam(); - var baseVertex = FetchParam(); - var baseInstance = FetchParam(); + var firstVertex = FetchParam(); + var firstInstance = FetchParam(); if (i == 0) { @@ -161,15 +283,32 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } } - // It should be empty at this point, but clear it just to be safe. - Fifo.Clear(); - var bufferCache = _processor.MemoryManager.Physical.BufferCache; - var parameterBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, parameterBufferGpuVa, 4); - var indirectBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, indirectBufferGpuVa, (ulong)indirectBufferSize); + ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride; - _processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride); + ulong indirectBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize); + ulong parameterBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, parameterBufferGpuVa, 4); + + _processor.ThreedClass.DrawIndirect( + topology, + indirectBufferAddress, + parameterBufferAddress, + maxDrawCount, + stride, + indexCount, + Threed.IndirectDrawType.DrawIndexedIndirectCount); + } + + /// + /// Checks if the draw should be skipped, because the masked instance count is zero. + /// + /// Current GPU state + /// Draw instance count + /// True if the draw should be skipped, false otherwise + private static bool ShouldSkipDraw(IDeviceState state, int instanceCount) + { + return (Read(state, 0xd1b) & instanceCount) == 0; } /// @@ -189,14 +328,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } /// - /// Performs a GPU method call. + /// Reads data from a GPU register. /// /// Current GPU state - /// Address, in words, of the method - /// Call argument - private static void Send(IDeviceState state, int methAddr, int value) + /// Register offset to read + /// GPU register value + private static int Read(IDeviceState state, int reg) { - state.Write(methAddr * 4, value); + return state.Read(reg * 4); } } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs index 4cce07fa0..751867fc7 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs @@ -8,6 +8,9 @@ None, ClearColor, ClearDepthStencil, + DrawArraysInstanced, + DrawElementsInstanced, + DrawElementsIndirect, MultiDrawElementsIndirectCount } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs index c5d988488..ab6f54efb 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs @@ -44,17 +44,29 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } } - private static readonly TableEntry[] Table = new TableEntry[] + private static readonly TableEntry[] _table = new TableEntry[] { new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28), new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24), + new TableEntry(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48), + new TableEntry(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c), + new TableEntry(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c), new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C) }; + /// + /// Checks if the host supports all features required by the HLE macro. + /// + /// Host capabilities + /// Name of the HLE macro to be checked + /// True if the host supports the HLE macro, false otherwise private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name) { if (name == MacroHLEFunctionName.ClearColor || - name == MacroHLEFunctionName.ClearDepthStencil) + name == MacroHLEFunctionName.ClearDepthStencil || + name == MacroHLEFunctionName.DrawArraysInstanced || + name == MacroHLEFunctionName.DrawElementsInstanced || + name == MacroHLEFunctionName.DrawElementsIndirect) { return true; } @@ -77,15 +89,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME { var mc = MemoryMarshal.Cast(code); - for (int i = 0; i < Table.Length; i++) + for (int i = 0; i < _table.Length; i++) { - ref var entry = ref Table[i]; + ref var entry = ref _table[i]; var hash = XXHash128.ComputeHash(mc.Slice(0, entry.Length)); if (hash == entry.Hash) { - name = entry.Name; - return IsMacroHLESupported(caps, name); + if (IsMacroHLESupported(caps, entry.Name)) + { + name = entry.Name; + return true; + } + + break; } } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs index 85f669850..a6b62a4a8 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed else { evt.Flush(); - return (memoryManager.Read(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; + return (memoryManager.Read(gpuVa, true) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; } } @@ -108,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed evt?.Flush(); evt2?.Flush(); - ulong x = memoryManager.Read(gpuVa); - ulong y = memoryManager.Read(gpuVa + 16); + ulong x = memoryManager.Read(gpuVa, true); + ulong y = memoryManager.Read(gpuVa + 16, true); return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index e02c8cdc6..a7acb4691 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Types; +using Ryujinx.Graphics.Gpu.Memory; using System; namespace Ryujinx.Graphics.Gpu.Engine.Threed @@ -9,6 +10,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// class DrawManager { + // Since we don't know the index buffer size for indirect draws, + // we must assume a minimum and maximum size and use that for buffer data update purposes. + private const int MinIndirectIndexCount = 0x10000; + private const int MaxIndirectIndexCount = 0x4000000; + private readonly GpuContext _context; private readonly GpuChannel _channel; private readonly DeviceStateWithShadow _state; @@ -28,6 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private int _instanceIndex; + private const int VertexBufferFirstMethodOffset = 0x35d; private const int IndexBufferCountMethodOffset = 0x5f8; /// @@ -237,6 +244,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _instanceIndex = 0; } + UpdateTopology(topology); + } + + /// + /// Updates the current primitive topology if needed. + /// + /// New primitive topology + private void UpdateTopology(PrimitiveTopology topology) + { if (_drawState.Topology != topology || !_topologySet) { _context.Renderer.Pipeline.SetPrimitiveTopology(topology); @@ -383,28 +399,27 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } /// - /// Performs a indirect multi-draw, with parameters from a GPU buffer. + /// Performs a indexed or non-indexed draw. /// /// 3D engine where this method is being called /// Primitive topology - /// GPU buffer with the draw parameters, such as count, first index, etc - /// GPU buffer with the draw count - /// Maximum number of draws that can be made - /// Distance in bytes between each element on the array - public void MultiDrawIndirectCount( + /// Index count for indexed draws, vertex count for non-indexed draws + /// Instance count + /// First index on the index buffer for indexed draws, ignored for non-indexed draws + /// First vertex on the vertex buffer + /// First instance + /// True if the draw is indexed, false otherwise + public void Draw( ThreedClass engine, - int indexCount, PrimitiveTopology topology, - BufferRange indirectBuffer, - BufferRange parameterBuffer, - int maxDrawCount, - int stride) + int count, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance, + bool indexed) { - engine.Write(IndexBufferCountMethodOffset * 4, indexCount); - - _context.Renderer.Pipeline.SetPrimitiveTopology(topology); - _drawState.Topology = topology; - _topologySet = true; + UpdateTopology(topology); ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable( _context, @@ -418,21 +433,133 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return; } - _drawState.FirstIndex = _state.State.IndexBufferState.First; - _drawState.IndexCount = indexCount; - - engine.UpdateState(); - - if (_drawState.DrawIndexed) + if (indexed) { - _context.Renderer.Pipeline.MultiDrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _drawState.FirstIndex = firstIndex; + _drawState.IndexCount = count; + _state.State.FirstVertex = (uint)firstVertex; + engine.ForceStateDirty(IndexBufferCountMethodOffset * 4); } else { - _context.Renderer.Pipeline.MultiDrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _state.State.VertexBufferDrawState.First = firstVertex; + _state.State.VertexBufferDrawState.Count = count; + engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4); + } + + _state.State.FirstInstance = (uint)firstInstance; + + _drawState.DrawIndexed = indexed; + _drawState.HasConstantBufferDrawParameters = true; + + engine.UpdateState(); + + if (indexed) + { + _context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance); + _state.State.FirstVertex = 0; + } + else + { + _context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance); + } + + _state.State.FirstInstance = 0; + + _drawState.DrawIndexed = false; + _drawState.HasConstantBufferDrawParameters = false; + + if (renderEnable == ConditionalRenderEnabled.Host) + { + _context.Renderer.Pipeline.EndHostConditionalRendering(); + } + } + + /// + /// Performs a indirect draw, with parameters from a GPU buffer. + /// + /// 3D engine where this method is being called + /// Primitive topology + /// Address of the buffer with the draw parameters, such as count, first index, etc + /// Address of the buffer with the draw count + /// Maximum number of draws that can be made + /// Distance in bytes between each entry on the data pointed to by + /// Maximum number of indices that the draw can consume + /// Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count + public void DrawIndirect( + ThreedClass engine, + PrimitiveTopology topology, + ulong indirectBufferAddress, + ulong parameterBufferAddress, + int maxDrawCount, + int stride, + int indexCount, + IndirectDrawType drawType) + { + UpdateTopology(topology); + + ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable( + _context, + _channel.MemoryManager, + _state.State.RenderEnableAddress, + _state.State.RenderEnableCondition); + + if (renderEnable == ConditionalRenderEnabled.False) + { + _drawState.DrawIndexed = false; + return; + } + + PhysicalMemory memory = _channel.MemoryManager.Physical; + + bool hasCount = (drawType & IndirectDrawType.Count) != 0; + bool indexed = (drawType & IndirectDrawType.Indexed) != 0; + + if (indexed) + { + indexCount = Math.Clamp(indexCount, MinIndirectIndexCount, MaxIndirectIndexCount); + _drawState.FirstIndex = 0; + _drawState.IndexCount = indexCount; + engine.ForceStateDirty(IndexBufferCountMethodOffset * 4); + } + + _drawState.DrawIndexed = indexed; + _drawState.DrawIndirect = true; + _drawState.HasConstantBufferDrawParameters = true; + + engine.UpdateState(); + + if (hasCount) + { + var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)maxDrawCount * (ulong)stride); + var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferAddress, 4); + + if (indexed) + { + _context.Renderer.Pipeline.DrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + } + else + { + _context.Renderer.Pipeline.DrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + } + } + else + { + var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)stride); + + if (indexed) + { + _context.Renderer.Pipeline.DrawIndexedIndirect(indirectBuffer); + } + else + { + _context.Renderer.Pipeline.DrawIndirect(indirectBuffer); + } } _drawState.DrawIndexed = false; + _drawState.DrawIndirect = false; + _drawState.HasConstantBufferDrawParameters = false; if (renderEnable == ConditionalRenderEnabled.Host) { diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs index ff186acc9..fd1cb0ea6 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs @@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// public bool DrawIndexed; + /// + /// Indicates if the next draw will be a indirect draw. + /// + public bool DrawIndirect; + /// /// Indicates if any of the currently used vertex shaders reads the instance ID. /// @@ -32,6 +37,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// public bool IsAnyVbInstanced; + /// + /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. + /// + public bool HasConstantBufferDrawParameters; + /// /// Primitive topology for the next draw. /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs new file mode 100644 index 000000000..d78aa4982 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs @@ -0,0 +1,38 @@ +namespace Ryujinx.Graphics.Gpu.Engine.Threed +{ + /// + /// Indirect draw type, which can be indexed or non-indexed, with or without a draw count. + /// + enum IndirectDrawType + { + /// + /// Non-indexed draw without draw count. + /// + DrawIndirect = 0, + + /// + /// Indexed draw without draw count. + /// + DrawIndexedIndirect = Indexed, + + /// + /// Non-indexed draw with draw count. + /// + DrawIndirectCount = Count, + + /// + /// Indexed draw with draw count. + /// + DrawIndexedIndirectCount = Indexed | Count, + + /// + /// Indexed flag. + /// + Indexed = 1 << 0, + + /// + /// Draw count flag. + /// + Count = 1 << 1 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index fdf8f8222..3f71172c0 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -34,10 +34,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private ProgramPipelineState _pipeline; + private bool _vsUsesDrawParameters; private bool _vtgWritesRtLayer; private byte _vsClipDistancesWritten; private bool _prevDrawIndexed; + private bool _prevDrawIndirect; private IndexType _prevIndexType; private uint _prevFirstVertex; private bool _prevTfEnable; @@ -210,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed // of the shader for the new state. if (_shaderSpecState != null) { - if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), false)) + if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), _vsUsesDrawParameters, false)) { ForceShaderUpdate(); } @@ -237,6 +239,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _prevDrawIndexed = _drawState.DrawIndexed; } + // Some draw parameters are used to restrict the vertex buffer size, + // but they can't be used on indirect draws because their values are unknown in this case. + // When switching between indirect and non-indirect draw, we need to + // make sure the vertex buffer sizes are still correct. + if (_drawState.DrawIndirect != _prevDrawIndirect) + { + _updateTracker.ForceDirty(VertexBufferStateIndex); + } + // In some cases, the index type is also used to guess the // vertex buffer size, so we must update it if the type changed too. if (_drawState.DrawIndexed && @@ -938,6 +949,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _drawState.IsAnyVbInstanced = false; + bool drawIndexed = _drawState.DrawIndexed; + bool drawIndirect = _drawState.DrawIndirect; + for (int index = 0; index < Constants.TotalVertexBuffers; index++) { var vertexBuffer = _state.State.VertexBufferState[index]; @@ -965,14 +979,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ulong vbSize = endAddress.Pack() - address + 1; ulong size; - if (_drawState.IbStreamer.HasInlineIndexData || _drawState.DrawIndexed || stride == 0 || instanced) + if (_drawState.IbStreamer.HasInlineIndexData || drawIndexed || stride == 0 || instanced) { // This size may be (much) larger than the real vertex buffer size. // Avoid calculating it this way, unless we don't have any other option. size = vbSize; - if (stride > 0 && indexTypeSmall && _drawState.DrawIndexed && !instanced) + if (stride > 0 && indexTypeSmall && drawIndexed && !drawIndirect && !instanced) { // If the index type is a small integer type, then we might be still able // to reduce the vertex buffer size based on the maximum possible index value. @@ -1207,6 +1221,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed byte oldVsClipDistancesWritten = _vsClipDistancesWritten; _drawState.VsUsesInstanceId = gs.Shaders[1]?.Info.UsesInstanceId ?? false; + _vsUsesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false; _vsClipDistancesWritten = gs.Shaders[1]?.Info.ClipDistancesWritten ?? 0; if (oldVsClipDistancesWritten != _vsClipDistancesWritten) @@ -1222,6 +1237,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.SetProgram(gs.HostProgram); } + /// + /// Updates bindings consumed by the shader stage on the texture and buffer managers. + /// + /// Shader stage to have the bindings updated + /// Shader stage bindings info private void UpdateStageBindings(int stage, ShaderProgramInfo info) { _currentProgramInfo[stage] = info; @@ -1340,7 +1360,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.AlphaTestEnable, _state.State.AlphaTestFunc, _state.State.AlphaTestRef, - ref attributeTypes); + ref attributeTypes, + _drawState.HasConstantBufferDrawParameters); } /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 8e222e719..106a6f3f4 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -497,6 +497,50 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return 0; } + /// + /// Performs a indexed or non-indexed draw. + /// + /// Primitive topology + /// Index count for indexed draws, vertex count for non-indexed draws + /// Instance count + /// First index on the index buffer for indexed draws, ignored for non-indexed draws + /// First vertex on the vertex buffer + /// First instance + /// True if the draw is indexed, false otherwise + public void Draw( + PrimitiveTopology topology, + int count, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance, + bool indexed) + { + _drawManager.Draw(this, topology, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed); + } + + /// + /// Performs a indirect draw, with parameters from a GPU buffer. + /// + /// Primitive topology + /// Address of the buffer with the draw parameters, such as count, first index, etc + /// Address of the buffer with the draw count + /// Maximum number of draws that can be made + /// Distance in bytes between each entry on the data pointed to by + /// Maximum number of indices that the draw can consume + /// Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count + public void DrawIndirect( + PrimitiveTopology topology, + ulong indirectBufferAddress, + ulong parameterBufferAddress, + int maxDrawCount, + int stride, + int indexCount, + IndirectDrawType drawType) + { + _drawManager.DrawIndirect(this, topology, indirectBufferAddress, parameterBufferAddress, maxDrawCount, stride, indexCount, drawType); + } + /// /// Clears the current color and depth-stencil buffers. /// Which buffers should be cleared can also specified with the arguments. @@ -507,25 +551,5 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { _drawManager.Clear(this, argument, layerCount); } - - /// - /// Performs a indirect multi-draw, with parameters from a GPU buffer. - /// - /// Index Buffer Count - /// Primitive topology - /// GPU buffer with the draw parameters, such as count, first index, etc - /// GPU buffer with the draw count - /// Maximum number of draws that can be made - /// Distance in bytes between each element on the array - public void MultiDrawIndirectCount( - int indexCount, - PrimitiveTopology topology, - BufferRange indirectBuffer, - BufferRange parameterBuffer, - int maxDrawCount, - int stride) - { - _drawManager.MultiDrawIndirectCount(this, indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride); - } } } diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 14b177aa4..894d009c0 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private Buffer[] _bufferOverlaps; private readonly Dictionary _dirtyCache; + private readonly Dictionary _modifiedCache; public event Action NotifyBuffersModified; @@ -45,6 +46,9 @@ namespace Ryujinx.Graphics.Gpu.Memory _bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity]; _dirtyCache = new Dictionary(); + + // There are a lot more entries on the modified cache, so it is separate from the one for ForceDirty. + _modifiedCache = new Dictionary(); } /// @@ -145,6 +149,30 @@ namespace Ryujinx.Graphics.Gpu.Memory result.Buffer.ForceDirty(result.Address, size); } + /// + /// Checks if the given buffer range has been GPU modifed. + /// + /// GPU memory manager where the buffer is mapped + /// Start GPU virtual address of the buffer + /// Size in bytes of the buffer + /// True if modified, false otherwise + public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr) + { + if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) || + result.EndGpuAddress < gpuVa + size || + result.UnmappedSequence != result.Buffer.UnmappedSequence) + { + ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size); + result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size)); + + _modifiedCache[gpuVa] = result; + } + + outAddr = result.Address; + + return result.Buffer.IsModified(result.Address, size); + } + /// /// Creates a new buffer for the specified range, if needed. /// If a buffer where this range can be fully contained already exists, @@ -326,18 +354,6 @@ namespace Ryujinx.Graphics.Gpu.Memory buffer.SignalModified(address, size); } - /// - /// Gets a buffer sub-range for a given GPU memory range. - /// - /// GPU memory manager where the buffer is mapped - /// Start GPU virtual address of the buffer - /// Size in bytes of the buffer - /// The buffer sub-range for the given range - public BufferRange GetGpuBufferRange(MemoryManager memoryManager, ulong gpuVa, ulong size) - { - return GetBufferRange(TranslateAndCreateBuffer(memoryManager, gpuVa, size), size); - } - /// /// Gets a buffer sub-range starting at a given memory address. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 68ff4f2a7..98748bf62 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -129,6 +129,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.ConstantBufferUse[_stageIndex]; } + /// + public bool QueryHasConstantBufferDrawParameters() + { + return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; + } + /// public InputTopology QueryPrimitiveTopology() { diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index e728c48c4..0bdf49499 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3831; + private const uint CodeGenVersion = 3747; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; @@ -159,6 +159,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// Bit mask of the render target components written by the fragment stage. /// public int FragmentOutputMap; + + /// + /// Indicates if the vertex shader accesses draw parameters. + /// + public bool UsesDrawParameters; } private readonly DiskCacheGuestStorage _guestStorage; @@ -771,6 +776,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache images, dataInfo.Stage, dataInfo.UsesInstanceId, + dataInfo.UsesDrawParameters, dataInfo.UsesRtLayer, dataInfo.ClipDistancesWritten, dataInfo.FragmentOutputMap); @@ -796,6 +802,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache dataInfo.ImagesCount = (ushort)info.Images.Count; dataInfo.Stage = info.Stage; dataInfo.UsesInstanceId = info.UsesInstanceId; + dataInfo.UsesDrawParameters = info.UsesDrawParameters; dataInfo.UsesRtLayer = info.UsesRtLayer; dataInfo.ClipDistancesWritten = info.ClipDistancesWritten; dataInfo.FragmentOutputMap = info.FragmentOutputMap; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 44c26efbf..b8cb1107f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -139,6 +139,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return useMask; } + /// + public bool QueryHasConstantBufferDrawParameters() + { + return _state.GraphicsState.HasConstantBufferDrawParameters; + } + /// public InputTopology QueryPrimitiveTopology() { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 82252ceda..b07276774 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -77,6 +77,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public Array32 AttributeTypes; + /// + /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. + /// + public readonly bool HasConstantBufferDrawParameters; + /// /// Creates a new GPU graphics state. /// @@ -93,6 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded /// When alpha test is enabled, indicates the value to compare with the fragment output alpha /// Type of the vertex attributes consumed by the shader + /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0 public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -106,7 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader bool alphaTestEnable, CompareOp alphaTestCompare, float alphaTestReference, - ref Array32 attributeTypes) + ref Array32 attributeTypes, + bool hasConstantBufferDrawParameters) { EarlyZForce = earlyZForce; Topology = topology; @@ -121,6 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Shader AlphaTestCompare = alphaTestCompare; AlphaTestReference = alphaTestReference; AttributeTypes = attributeTypes; + HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index c998fe093..1803dae61 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -520,7 +520,9 @@ namespace Ryujinx.Graphics.Gpu.Shader } } - return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true); + bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false; + + return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs index 43ccd892c..abc9d913b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs @@ -35,7 +35,9 @@ namespace Ryujinx.Graphics.Gpu.Shader { foreach (var entry in _entries) { - if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true)) + bool usesDrawParameters = entry.Shaders[1]?.Info.UsesDrawParameters ?? false; + + if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true)) { program = entry; return true; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 28818304a..0aecc5b7b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -481,9 +481,15 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU channel /// Texture pool state /// Graphics state + /// Indicates whether the vertex shader accesses draw parameters /// Indicates whether texture descriptors should be checked /// True if the state matches, false otherwise - public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState, bool checkTextures) + public bool MatchesGraphics( + GpuChannel channel, + GpuChannelPoolState poolState, + GpuChannelGraphicsState graphicsState, + bool usesDrawParameters, + bool checkTextures) { if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable) { @@ -520,6 +526,11 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + if (usesDrawParameters && graphicsState.HasConstantBufferDrawParameters != GraphicsState.HasConstantBufferDrawParameters) + { + return false; + } + return Matches(channel, poolState, checkTextures, isCompute: false); } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 5911758ef..3b234eb08 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -586,6 +586,95 @@ namespace Ryujinx.Graphics.OpenGL } } + public void DrawIndexedIndirect(BufferRange indirectBuffer) + { + if (!_program.IsLinked) + { + Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked."); + return; + } + + PreDrawVbUnbounded(); + + _vertexArray.SetRangeOfIndexBuffer(); + + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); + + GL.DrawElementsIndirect(_primitiveType, _elementsType, (IntPtr)indirectBuffer.Offset); + + _vertexArray.RestoreIndexBuffer(); + + PostDraw(); + } + + public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + if (!_program.IsLinked) + { + Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked."); + return; + } + + PreDrawVbUnbounded(); + + _vertexArray.SetRangeOfIndexBuffer(); + + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); + GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32()); + + GL.MultiDrawElementsIndirectCount( + _primitiveType, + (All)_elementsType, + (IntPtr)indirectBuffer.Offset, + (IntPtr)parameterBuffer.Offset, + maxDrawCount, + stride); + + _vertexArray.RestoreIndexBuffer(); + + PostDraw(); + } + + public void DrawIndirect(BufferRange indirectBuffer) + { + if (!_program.IsLinked) + { + Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked."); + return; + } + + PreDrawVbUnbounded(); + + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); + + GL.DrawArraysIndirect(_primitiveType, (IntPtr)indirectBuffer.Offset); + + PostDraw(); + } + + public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + if (!_program.IsLinked) + { + Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked."); + return; + } + + PreDrawVbUnbounded(); + + GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); + GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32()); + + GL.MultiDrawArraysIndirectCount( + _primitiveType, + (IntPtr)indirectBuffer.Offset, + (IntPtr)parameterBuffer.Offset, + maxDrawCount, + stride); + + PostDraw(); + } + public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { if (texture is TextureView view && sampler is Sampler samp) @@ -683,57 +772,6 @@ namespace Ryujinx.Graphics.OpenGL _tfEnabled = false; } - public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - if (!_program.IsLinked) - { - Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked."); - return; - } - - PreDrawVbUnbounded(); - - GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); - GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32()); - - GL.MultiDrawArraysIndirectCount( - _primitiveType, - (IntPtr)indirectBuffer.Offset, - (IntPtr)parameterBuffer.Offset, - maxDrawCount, - stride); - - PostDraw(); - } - - public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - if (!_program.IsLinked) - { - Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked."); - return; - } - - PreDrawVbUnbounded(); - - _vertexArray.SetRangeOfIndexBuffer(); - - GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); - GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32()); - - GL.MultiDrawElementsIndirectCount( - _primitiveType, - (All)_elementsType, - (IntPtr)indirectBuffer.Offset, - (IntPtr)parameterBuffer.Offset, - maxDrawCount, - stride); - - _vertexArray.RestoreIndexBuffer(); - - PostDraw(); - } - public void SetAlphaTest(bool enable, float reference, CompareOp op) { if (!enable) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 4f2751b12..b2eeb5f5f 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -46,6 +46,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { + if (context.Config.Stage == ShaderStage.Vertex) + { + context.AppendLine("#extension GL_ARB_shader_draw_parameters : enable"); + } + context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable"); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 031b1c44c..b78914260 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -48,10 +48,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, - { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstance", VariableType.S32) }, - { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertex", VariableType.S32) }, + { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", VariableType.S32) }, + { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", VariableType.S32) }, { AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) }, { AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) }, + { AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", VariableType.S32) }, { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, // Special. diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index fafb917db..54b00708b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -743,6 +743,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AttributeConsts.BaseVertex => BuiltIn.BaseVertex, AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex, AttributeConsts.VertexIndex => BuiltIn.VertexIndex, + AttributeConsts.DrawIndex => BuiltIn.DrawIndex, AttributeConsts.FrontFacing => BuiltIn.FrontFacing, AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth, AttributeConsts.ThreadKill => BuiltIn.HelperInvocation, diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 69283b0a3..95df077bf 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -89,6 +89,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { context.AddCapability(Capability.Tessellation); } + else if (config.Stage == ShaderStage.Vertex) + { + context.AddCapability(Capability.DrawParameters); + } context.AddExtension("SPV_KHR_shader_ballot"); context.AddExtension("SPV_KHR_subgroup_vote"); diff --git a/Ryujinx.Graphics.Shader/Constants.cs b/Ryujinx.Graphics.Shader/Constants.cs index 86af48cfa..7f1445ed0 100644 --- a/Ryujinx.Graphics.Shader/Constants.cs +++ b/Ryujinx.Graphics.Shader/Constants.cs @@ -6,5 +6,9 @@ namespace Ryujinx.Graphics.Shader public const int MaxAttributes = 16; public const int AllAttributesMask = (int)(uint.MaxValue >> (32 - MaxAttributes)); + + public const int NvnBaseVertexByteOffset = 0x640; + public const int NvnBaseInstanceByteOffset = 0x644; + public const int NvnDrawIndexByteOffset = 0x648; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 2cdc81fbc..4f800a145 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -168,6 +168,15 @@ namespace Ryujinx.Graphics.Shader return 0; } + /// + /// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0. + /// + /// True if the shader translator can assume that the constant buffer contains the base IDs, false otherwise + bool QueryHasConstantBufferDrawParameters() + { + return false; + } + /// /// Queries host about the presence of the FrontFacing built-in variable bug. /// diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index 659f6167e..bb75b10ae 100644 --- a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Shader public ShaderStage Stage { get; } public bool UsesInstanceId { get; } + public bool UsesDrawParameters { get; } public bool UsesRtLayer { get; } public byte ClipDistancesWritten { get; } public int FragmentOutputMap { get; } @@ -23,6 +24,7 @@ namespace Ryujinx.Graphics.Shader TextureDescriptor[] images, ShaderStage stage, bool usesInstanceId, + bool usesDrawParameters, bool usesRtLayer, byte clipDistancesWritten, int fragmentOutputMap) @@ -34,6 +36,7 @@ namespace Ryujinx.Graphics.Shader Stage = stage; UsesInstanceId = usesInstanceId; + UsesDrawParameters = usesDrawParameters; UsesRtLayer = usesRtLayer; ClipDistancesWritten = clipDistancesWritten; FragmentOutputMap = fragmentOutputMap; diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 47367f89c..863e19a0d 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -100,5 +100,6 @@ namespace Ryujinx.Graphics.Shader.Translation public const int BaseVertex = 0x2000054; public const int InstanceIndex = 0x2000058; public const int VertexIndex = 0x200005c; + public const int DrawIndex = 0x2000060; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index 6e95722ff..839edbe9e 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Shader.Translation { AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) }, { AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) }, { AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) }, + { AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) }, { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, // Special. diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index a2363fcbb..c035f212d 100644 --- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -17,10 +17,11 @@ namespace Ryujinx.Graphics.Shader.Translation Bindless = 1 << 2, InstanceId = 1 << 3, - RtLayer = 1 << 4, - CbIndexing = 1 << 5, - IaIndexing = 1 << 6, - OaIndexing = 1 << 7, - FixedFuncAttr = 1 << 8 + DrawParameters = 1 << 4, + RtLayer = 1 << 5, + CbIndexing = 1 << 6, + IaIndexing = 1 << 7, + OaIndexing = 1 << 8, + FixedFuncAttr = 1 << 9 } } diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 4d66597f7..640717f91 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -12,6 +12,9 @@ namespace Ryujinx.Graphics.Shader.Translation { public static void RunPass(BasicBlock[] blocks, ShaderConfig config) { + bool isVertexShader = config.Stage == ShaderStage.Vertex; + bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { BasicBlock block = blocks[blkIndex]; @@ -23,6 +26,21 @@ namespace Ryujinx.Graphics.Shader.Translation continue; } + if (isVertexShader) + { + if (hasConstantBufferDrawParameters) + { + if (ReplaceConstantBufferWithDrawParameters(operation)) + { + config.SetUsedFeature(FeatureFlags.DrawParameters); + } + } + else if (HasConstantBufferDrawParameters(operation)) + { + config.SetUsedFeature(FeatureFlags.DrawParameters); + } + } + if (UsesGlobalMemory(operation.Inst)) { node = RewriteGlobalAccess(node, config); @@ -528,5 +546,57 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } + + private static bool ReplaceConstantBufferWithDrawParameters(Operation operation) + { + bool modified = false; + + for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + { + Operand src = operation.GetSource(srcIndex); + + if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0) + { + switch (src.GetCbufOffset()) + { + case Constants.NvnBaseVertexByteOffset / 4: + operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseVertex)); + modified = true; + break; + case Constants.NvnBaseInstanceByteOffset / 4: + operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseInstance)); + modified = true; + break; + case Constants.NvnDrawIndexByteOffset / 4: + operation.SetSource(srcIndex, Attribute(AttributeConsts.DrawIndex)); + modified = true; + break; + } + } + } + + return modified; + } + + private static bool HasConstantBufferDrawParameters(Operation operation) + { + for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + { + Operand src = operation.GetSource(srcIndex); + + if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0) + { + switch (src.GetCbufOffset()) + { + case Constants.NvnBaseVertexByteOffset / 4: + case Constants.NvnBaseInstanceByteOffset / 4: + case Constants.NvnDrawIndexByteOffset / 4: + return true; + } + } + } + + return false; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 8741f8487..58a934c73 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -86,6 +86,7 @@ namespace Ryujinx.Graphics.Shader.Translation config.GetImageDescriptors(), config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), + config.UsedFeatures.HasFlag(FeatureFlags.DrawParameters), config.UsedFeatures.HasFlag(FeatureFlags.RtLayer), config.ClipDistancesWritten, config.OmapTargets); diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 4660765a4..24f789f62 100644 --- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -477,6 +477,26 @@ namespace Ryujinx.Graphics.Vulkan 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() { _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index 57d672423..432898a9c 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -52,7 +52,12 @@ namespace Ryujinx.Graphics.Vulkan public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal) { - var holder = Create(gd, size, deviceLocal: deviceLocal); + return CreateWithHandle(gd, size, deviceLocal, out _); + } + + public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal, out BufferHolder holder) + { + holder = Create(gd, size, deviceLocal: deviceLocal); if (holder == null) { return BufferHandle.Null; @@ -164,6 +169,141 @@ namespace Ryujinx.Graphics.Vulkan return null; } + public (Auto, Auto) GetBufferTopologyConversionIndirect( + VulkanRenderer gd, + CommandBufferScoped cbs, + BufferRange indexBuffer, + BufferRange indirectBuffer, + BufferRange drawCountBuffer, + IndexBufferPattern pattern, + int indexSize, + bool hasDrawCount, + int maxDrawCount, + int indirectDataStride) + { + BufferHolder drawCountBufferHolder = null; + + if (!TryGetBuffer(indexBuffer.Handle, out var indexBufferHolder) || + !TryGetBuffer(indirectBuffer.Handle, out var indirectBufferHolder) || + (hasDrawCount && !TryGetBuffer(drawCountBuffer.Handle, out drawCountBufferHolder))) + { + return (null, null); + } + + var indexBufferKey = new TopologyConversionIndirectCacheKey( + gd, + pattern, + indexSize, + indirectBufferHolder, + indirectBuffer.Offset, + indirectBuffer.Size); + + bool hasConvertedIndexBuffer = indexBufferHolder.TryGetCachedConvertedBuffer( + indexBuffer.Offset, + indexBuffer.Size, + indexBufferKey, + out var convertedIndexBuffer); + + var indirectBufferKey = new IndirectDataCacheKey(pattern); + bool hasConvertedIndirectBuffer = indirectBufferHolder.TryGetCachedConvertedBuffer( + indirectBuffer.Offset, + indirectBuffer.Size, + indirectBufferKey, + out var convertedIndirectBuffer); + + var drawCountBufferKey = new DrawCountCacheKey(); + bool hasCachedDrawCount = true; + + if (hasDrawCount) + { + hasCachedDrawCount = drawCountBufferHolder.TryGetCachedConvertedBuffer( + drawCountBuffer.Offset, + drawCountBuffer.Size, + drawCountBufferKey, + out _); + } + + if (!hasConvertedIndexBuffer || !hasConvertedIndirectBuffer || !hasCachedDrawCount) + { + // The destination index size is always I32. + + int indexCount = indexBuffer.Size / indexSize; + + int convertedCount = pattern.GetConvertedCount(indexCount); + + if (!hasConvertedIndexBuffer) + { + convertedIndexBuffer = Create(gd, convertedCount * 4); + indexBufferKey.SetBuffer(convertedIndexBuffer.GetBuffer()); + indexBufferHolder.AddCachedConvertedBuffer(indexBuffer.Offset, indexBuffer.Size, indexBufferKey, convertedIndexBuffer); + } + + if (!hasConvertedIndirectBuffer) + { + convertedIndirectBuffer = Create(gd, indirectBuffer.Size); + indirectBufferHolder.AddCachedConvertedBuffer(indirectBuffer.Offset, indirectBuffer.Size, indirectBufferKey, convertedIndirectBuffer); + } + + gd.PipelineInternal.EndRenderPass(); + gd.HelperShader.ConvertIndexBufferIndirect( + gd, + cbs, + indirectBufferHolder, + convertedIndirectBuffer, + drawCountBuffer, + indexBufferHolder, + convertedIndexBuffer, + pattern, + indexSize, + indexBuffer.Offset, + indexBuffer.Size, + indirectBuffer.Offset, + hasDrawCount, + maxDrawCount, + indirectDataStride); + + // Any modification of the indirect buffer should invalidate the index buffers that are associated with it, + // since we used the indirect data to find the range of the index buffer that is used. + + var indexBufferDependency = new Dependency( + indexBufferHolder, + indexBuffer.Offset, + indexBuffer.Size, + indexBufferKey); + + indirectBufferHolder.AddCachedConvertedBufferDependency( + indirectBuffer.Offset, + indirectBuffer.Size, + indirectBufferKey, + indexBufferDependency); + + if (hasDrawCount) + { + if (!hasCachedDrawCount) + { + drawCountBufferHolder.AddCachedConvertedBuffer(drawCountBuffer.Offset, drawCountBuffer.Size, drawCountBufferKey, null); + } + + // If we have a draw count, any modification of the draw count should invalidate all indirect buffers + // where we used it to find the range of indirect data that is actually used. + + var indirectBufferDependency = new Dependency( + indirectBufferHolder, + indirectBuffer.Offset, + indirectBuffer.Size, + indirectBufferKey); + + drawCountBufferHolder.AddCachedConvertedBufferDependency( + drawCountBuffer.Offset, + drawCountBuffer.Size, + drawCountBufferKey, + indirectBufferDependency); + } + } + + return (convertedIndexBuffer.GetBuffer(), convertedIndirectBuffer.GetBuffer()); + } + public Auto GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size) { if (TryGetBuffer(handle, out var holder)) diff --git a/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/Ryujinx.Graphics.Vulkan/CacheByRange.cs index c77e66ae8..afd7140eb 100644 --- a/Ryujinx.Graphics.Vulkan/CacheByRange.cs +++ b/Ryujinx.Graphics.Vulkan/CacheByRange.cs @@ -106,17 +106,125 @@ namespace Ryujinx.Graphics.Vulkan } } + struct TopologyConversionIndirectCacheKey : ICacheKey + { + private readonly TopologyConversionCacheKey _baseKey; + private readonly BufferHolder _indirectDataBuffer; + private readonly int _indirectDataOffset; + private readonly int _indirectDataSize; + + public TopologyConversionIndirectCacheKey( + VulkanRenderer gd, + IndexBufferPattern pattern, + int indexSize, + BufferHolder indirectDataBuffer, + int indirectDataOffset, + int indirectDataSize) + { + _baseKey = new TopologyConversionCacheKey(gd, pattern, indexSize); + _indirectDataBuffer = indirectDataBuffer; + _indirectDataOffset = indirectDataOffset; + _indirectDataSize = indirectDataSize; + } + + public bool KeyEqual(ICacheKey other) + { + return other is TopologyConversionIndirectCacheKey entry && + entry._baseKey.KeyEqual(_baseKey) && + entry._indirectDataBuffer == _indirectDataBuffer && + entry._indirectDataOffset == _indirectDataOffset && + entry._indirectDataSize == _indirectDataSize; + } + + public void SetBuffer(Auto buffer) + { + _baseKey.SetBuffer(buffer); + } + + public void Dispose() + { + _baseKey.Dispose(); + } + } + + struct IndirectDataCacheKey : ICacheKey + { + private IndexBufferPattern _pattern; + + public IndirectDataCacheKey(IndexBufferPattern pattern) + { + _pattern = pattern; + } + + public bool KeyEqual(ICacheKey other) + { + return other is IndirectDataCacheKey entry && entry._pattern == _pattern; + } + + public void Dispose() + { + } + } + + struct DrawCountCacheKey : ICacheKey + { + public bool KeyEqual(ICacheKey other) + { + return other is DrawCountCacheKey; + } + + public void Dispose() + { + } + } + + 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); + } + } + struct CacheByRange where T : IDisposable { private struct Entry { public ICacheKey Key; public T Value; + public List DependencyList; public Entry(ICacheKey key, T value) { Key = key; Value = value; + DependencyList = null; + } + + public void InvalidateDependencies() + { + if (DependencyList != null) + { + foreach (Dependency dependency in DependencyList) + { + dependency.RemoveFromOwner(); + } + + DependencyList.Clear(); + } } } @@ -129,6 +237,51 @@ namespace Ryujinx.Graphics.Vulkan 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 = new List(); + 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); @@ -155,8 +308,7 @@ namespace Ryujinx.Graphics.Vulkan { foreach (Entry entry in entries) { - entry.Key.Dispose(); - entry.Value.Dispose(); + DestroyEntry(entry); } } @@ -185,8 +337,7 @@ namespace Ryujinx.Graphics.Vulkan foreach (Entry entry in entries) { - entry.Key.Dispose(); - entry.Value.Dispose(); + DestroyEntry(entry); } (toRemove ??= new List()).Add(range.Key); @@ -222,6 +373,13 @@ namespace Ryujinx.Graphics.Vulkan 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); diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index c842b9222..1ef22dc29 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Vulkan { class HelperShader : IDisposable { + private const int UniformBufferAlignment = 256; + private readonly PipelineHelperShader _pipeline; private readonly ISampler _samplerLinear; private readonly ISampler _samplerNearest; @@ -19,6 +21,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programColorClear; private readonly IProgram _programStrideChange; private readonly IProgram _programColorCopyBetweenMsNonMs; + private readonly IProgram _programConvertIndexBuffer; + private readonly IProgram _programConvertIndirectData; public HelperShader(VulkanRenderer gd, Device device) { @@ -88,6 +92,28 @@ namespace Ryujinx.Graphics.Vulkan { new SpecDescription((0, SpecConstType.Int32)) }); + + var convertIndexBufferBindings = new ShaderBindings( + new[] { 0 }, + new[] { 1, 2 }, + Array.Empty(), + Array.Empty()); + + _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + + var convertIndirectDataBindings = new ShaderBindings( + new[] { 0 }, + new[] { 1, 2, 3 }, + Array.Empty(), + Array.Empty()); + + _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); } public void Blit( @@ -408,10 +434,12 @@ namespace Ryujinx.Graphics.Vulkan int srcOffset, int indexCount) { + // TODO: Support conversion with primitive restart enabled. + // TODO: Convert with a compute shader? + int convertedCount = pattern.GetConvertedCount(indexCount); int outputIndexSize = 4; - // TODO: Do this with a compute shader? var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value; var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value; @@ -671,6 +699,133 @@ namespace Ryujinx.Graphics.Vulkan }; } + public void ConvertIndexBufferIndirect( + VulkanRenderer gd, + CommandBufferScoped cbs, + BufferHolder srcIndirectBuffer, + BufferHolder dstIndirectBuffer, + BufferRange drawCountBuffer, + BufferHolder srcIndexBuffer, + BufferHolder dstIndexBuffer, + IndexBufferPattern pattern, + int indexSize, + int srcIndexBufferOffset, + int srcIndexBufferSize, + int srcIndirectBufferOffset, + bool hasDrawCount, + int maxDrawCount, + int indirectDataStride) + { + // TODO: Support conversion with primitive restart enabled. + + BufferRange drawCountBufferAligned = new BufferRange( + drawCountBuffer.Handle, + drawCountBuffer.Offset & ~(UniformBufferAlignment - 1), + UniformBufferAlignment); + + int indirectDataSize = maxDrawCount * indirectDataStride; + + int indexCount = srcIndexBufferSize / indexSize; + int primitivesCount = pattern.GetPrimitiveCount(indexCount); + int convertedCount = pattern.GetConvertedCount(indexCount); + int outputIndexSize = 4; + + var srcBuffer = srcIndexBuffer.GetBuffer().Get(cbs, srcIndexBufferOffset, indexCount * indexSize).Value; + var dstBuffer = dstIndexBuffer.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value; + + const int ParamsBufferSize = 24 * sizeof(int); + const int ParamsIndirectDispatchOffset = 16 * sizeof(int); + const int ParamsIndirectDispatchSize = 3 * sizeof(int); + + 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] = srcIndexBufferOffset; + shaderParams[15] = primitivesCount; + shaderParams[16] = 1; + shaderParams[17] = 1; + shaderParams[18] = 1; + shaderParams[19] = hasDrawCount ? 1 : 0; + shaderParams[20] = maxDrawCount; + shaderParams[21] = (drawCountBuffer.Offset & (UniformBufferAlignment - 1)) / 4; + shaderParams[22] = indirectDataStride / 4; + shaderParams[23] = srcIndirectBufferOffset / 4; + + pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length)); + + var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false, out var patternBuffer); + var patternBufferAuto = patternBuffer.GetBuffer(); + + gd.BufferManager.SetData(patternBufferHandle, 0, shaderParams); + + _pipeline.SetCommandBuffer(cbs); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value, + BufferHolder.DefaultAccessFlags, + AccessFlags.AccessShaderReadBit, + PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.PipelineStageComputeShaderBit, + srcIndirectBufferOffset, + indirectDataSize); + + _pipeline.SetUniformBuffers(0, stackalloc[] { drawCountBufferAligned }); + _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() }); + + _pipeline.SetProgram(_programConvertIndirectData); + _pipeline.DispatchCompute(1, 1, 1); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value, + AccessFlags.AccessShaderWriteBit, + AccessFlags.AccessIndirectCommandReadBit, + PipelineStageFlags.PipelineStageComputeShaderBit, + PipelineStageFlags.PipelineStageDrawIndirectBit, + ParamsIndirectDispatchOffset, + ParamsIndirectDispatchSize); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + BufferHolder.DefaultAccessFlags, + AccessFlags.AccessTransferWriteBit, + PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.PipelineStageTransferBit, + 0, + convertedCount * outputIndexSize); + + _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(patternBufferHandle, 0, ParamsBufferSize) }); + _pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() }); + + _pipeline.SetProgram(_programConvertIndexBuffer); + _pipeline.DispatchComputeIndirect(patternBufferAuto, ParamsIndirectDispatchOffset); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + AccessFlags.AccessTransferWriteBit, + BufferHolder.DefaultAccessFlags, + PipelineStageFlags.PipelineStageTransferBit, + PipelineStageFlags.PipelineStageAllCommandsBit, + 0, + convertedCount * outputIndexSize); + + gd.BufferManager.Delete(patternBufferHandle); + + _pipeline.Finish(gd, cbs); + } + protected virtual void Dispose(bool disposing) { if (disposing) @@ -680,6 +835,8 @@ namespace Ryujinx.Graphics.Vulkan _programColorClear.Dispose(); _programStrideChange.Dispose(); _programColorCopyBetweenMsNonMs.Dispose(); + _programConvertIndexBuffer.Dispose(); + _programConvertIndirectData.Dispose(); _samplerNearest.Dispose(); _samplerLinear.Dispose(); _pipeline.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs index 205eab277..64b95f600 100644 --- a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs +++ b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs @@ -1,5 +1,4 @@ using Silk.NET.Vulkan; -using System; namespace Ryujinx.Graphics.Vulkan { @@ -68,17 +67,18 @@ namespace Ryujinx.Graphics.Vulkan } } - public void BindConvertedIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, int firstIndex, int indexCount, int convertedCount, IndexBufferPattern pattern) + public void BindConvertedIndexBuffer( + VulkanRenderer gd, + CommandBufferScoped cbs, + int firstIndex, + int indexCount, + int convertedCount, + IndexBufferPattern pattern) { Auto autoBuffer; // Convert the index buffer using the given pattern. - int indexSize = _type switch - { - IndexType.Uint32 => 4, - IndexType.Uint16 => 2, - _ => 1, - }; + int indexSize = GetIndexSize(); int firstIndexOffset = firstIndex * indexSize; @@ -94,6 +94,54 @@ namespace Ryujinx.Graphics.Vulkan } } + public Auto BindConvertedIndexBufferIndirect( + VulkanRenderer gd, + CommandBufferScoped cbs, + GAL.BufferRange indirectBuffer, + GAL.BufferRange drawCountBuffer, + IndexBufferPattern pattern, + bool hasDrawCount, + int maxDrawCount, + int indirectDataStride) + { + // Convert the index buffer using the given pattern. + int indexSize = GetIndexSize(); + + (var indexBufferAuto, var indirectBufferAuto) = gd.BufferManager.GetBufferTopologyConversionIndirect( + gd, + cbs, + new GAL.BufferRange(_handle, _offset, _size), + indirectBuffer, + drawCountBuffer, + pattern, + indexSize, + hasDrawCount, + maxDrawCount, + indirectDataStride); + + int convertedCount = pattern.GetConvertedCount(_size / indexSize); + int size = convertedCount * 4; + + _buffer = indexBufferAuto; + + if (indexBufferAuto != null) + { + gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, indexBufferAuto.Get(cbs, 0, size).Value, 0, IndexType.Uint32); + } + + return indirectBufferAuto; + } + + private int GetIndexSize() + { + return _type switch + { + IndexType.Uint32 => 4, + IndexType.Uint16 => 2, + _ => 1, + }; + } + public bool BoundEquals(Auto buffer) { return _buffer == buffer; diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index f333c944b..55c3fea22 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -294,6 +294,19 @@ namespace Ryujinx.Graphics.Vulkan Gd.Api.CmdDispatch(CommandBuffer, (uint)groupsX, (uint)groupsY, (uint)groupsZ); } + public void DispatchComputeIndirect(Auto indirectBuffer, int indirectBufferOffset) + { + if (!_program.IsLinked) + { + return; + } + + EndRenderPass(); + RecreatePipelineIfNeeded(PipelineBindPoint.Compute); + + Gd.Api.CmdDispatchIndirect(CommandBuffer, indirectBuffer.Get(Cbs, indirectBufferOffset, 12).Value, (ulong)indirectBufferOffset); + } + public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { if (!_program.IsLinked) @@ -395,6 +408,204 @@ namespace Ryujinx.Graphics.Vulkan } } + public void DrawIndexedIndirect(BufferRange indirectBuffer) + { + if (!_program.IsLinked) + { + return; + } + + UpdateIndexBufferPattern(); + RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); + BeginRenderPass(); + DrawCount++; + + if (_indexBufferPattern != null) + { + // Convert the index buffer into a supported topology. + IndexBufferPattern pattern = _indexBufferPattern; + + Auto indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect( + Gd, + Cbs, + indirectBuffer, + BufferRange.Empty, + pattern, + false, + 1, + indirectBuffer.Size); + + _needsIndexBufferRebind = false; + + BeginRenderPass(); // May have been interrupted to set buffer data. + ResumeTransformFeedbackInternal(); + + Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, 0, 1, (uint)indirectBuffer.Size); + } + else + { + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + ResumeTransformFeedbackInternal(); + + Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size); + } + } + + public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + if (!_program.IsLinked) + { + return; + } + + UpdateIndexBufferPattern(); + RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); + BeginRenderPass(); + DrawCount++; + + var countBuffer = Gd.BufferManager + .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) + .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; + + if (_indexBufferPattern != null) + { + // Convert the index buffer into a supported topology. + IndexBufferPattern pattern = _indexBufferPattern; + + Auto indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect( + Gd, + Cbs, + indirectBuffer, + parameterBuffer, + pattern, + true, + maxDrawCount, + stride); + + _needsIndexBufferRebind = false; + + BeginRenderPass(); // May have been interrupted to set buffer data. + ResumeTransformFeedbackInternal(); + + if (Gd.Capabilities.SupportsIndirectParameters) + { + Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount( + CommandBuffer, + indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, + 0, + countBuffer, + (ulong)parameterBuffer.Offset, + (uint)maxDrawCount, + (uint)stride); + } + else + { + // This is also fine because the indirect data conversion always zeros + // the entries that are past the current draw count. + + Gd.Api.CmdDrawIndexedIndirect( + CommandBuffer, + indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, + 0, + (uint)maxDrawCount, + (uint)stride); + } + + } + else + { + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + ResumeTransformFeedbackInternal(); + + if (Gd.Capabilities.SupportsIndirectParameters) + { + Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount( + CommandBuffer, + buffer, + (ulong)indirectBuffer.Offset, + countBuffer, + (ulong)parameterBuffer.Offset, + (uint)maxDrawCount, + (uint)stride); + } + else + { + // Not fully correct, but we can't do much better if the host does not support indirect count. + Gd.Api.CmdDrawIndexedIndirect( + CommandBuffer, + buffer, + (ulong)indirectBuffer.Offset, + (uint)maxDrawCount, + (uint)stride); + } + } + } + + public void DrawIndirect(BufferRange indirectBuffer) + { + if (!_program.IsLinked) + { + return; + } + + // TODO: Support quads and other unsupported topologies. + + RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); + BeginRenderPass(); + ResumeTransformFeedbackInternal(); + DrawCount++; + + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + Gd.Api.CmdDrawIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size); + } + + public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + if (!Gd.Capabilities.SupportsIndirectParameters) + { + // TODO: Fallback for when this is not supported. + throw new NotSupportedException(); + } + + if (!_program.IsLinked) + { + return; + } + + // TODO: Support quads and other unsupported topologies. + + RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); + BeginRenderPass(); + ResumeTransformFeedbackInternal(); + DrawCount++; + + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + + var countBuffer = Gd.BufferManager + .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) + .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; + + Gd.DrawIndirectCountApi.CmdDrawIndirectCount( + CommandBuffer, + buffer, + (ulong)indirectBuffer.Offset, + countBuffer, + (ulong)parameterBuffer.Offset, + (uint)maxDrawCount, + (uint)stride); + } + public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { if (texture is TextureView srcTexture) @@ -449,76 +660,6 @@ namespace Ryujinx.Graphics.Vulkan return CommandBuffer.Handle == cb.Handle; } - public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - if (!Gd.Capabilities.SupportsIndirectParameters) - { - throw new NotSupportedException(); - } - - if (_program.LinkStatus != ProgramLinkStatus.Success) - { - return; - } - - RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); - BeginRenderPass(); - ResumeTransformFeedbackInternal(); - DrawCount++; - - var buffer = Gd.BufferManager - .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, true) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - - var countBuffer = Gd.BufferManager - .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true) - .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; - - Gd.DrawIndirectCountApi.CmdDrawIndirectCount( - CommandBuffer, - buffer, - (ulong)indirectBuffer.Offset, - countBuffer, - (ulong)parameterBuffer.Offset, - (uint)maxDrawCount, - (uint)stride); - } - - public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - if (!Gd.Capabilities.SupportsIndirectParameters) - { - throw new NotSupportedException(); - } - - if (_program.LinkStatus != ProgramLinkStatus.Success) - { - return; - } - - RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); - BeginRenderPass(); - ResumeTransformFeedbackInternal(); - DrawCount++; - - var buffer = Gd.BufferManager - .GetBuffer(CommandBuffer, indirectBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - - var countBuffer = Gd.BufferManager - .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true) - .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; - - Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount( - CommandBuffer, - buffer, - (ulong)indirectBuffer.Offset, - countBuffer, - (ulong)parameterBuffer.Offset, - (uint)maxDrawCount, - (uint)stride); - } - public void SetAlphaTest(bool enable, float reference, GAL.CompareOp op) { // This is currently handled using shader specialization, as Vulkan does not support alpha test. @@ -706,7 +847,7 @@ namespace Ryujinx.Graphics.Vulkan if (!dataSpan.SequenceEqual(_newState.SpecializationData.Span)) { _newState.SpecializationData = new SpecData(dataSpan); - + SignalStateChange(); } } diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp new file mode 100644 index 000000000..d56d6cfd1 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp @@ -0,0 +1,58 @@ +#version 450 core + +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_shader_8bit_storage : require + +layout (local_size_x = 16, local_size_y = 1, local_size_z = 1) in; + +layout (std430, set = 0, binding = 0) uniform index_buffer_pattern +{ + int ibp_pattern[8]; + int ibp_primitive_vertices; + int ibp_primitive_vertices_out; + int ibp_index_size; + int ibp_index_size_out; + int ibp_base_index; + int ibp_index_stride; + int src_offset; + int total_primitives; +}; + +layout (std430, set = 1, binding = 1) buffer in_s +{ + uint8_t[] in_data; +}; + +layout (std430, set = 1, binding = 2) buffer out_s +{ + uint8_t[] out_data; +}; + +void main() +{ + int primitiveIndex = int(gl_GlobalInvocationID.x); + if (primitiveIndex >= total_primitives) + { + return; + } + + int inOffset = primitiveIndex * ibp_index_stride; + int outOffset = primitiveIndex * ibp_primitive_vertices_out; + + for (int i = 0; i < ibp_primitive_vertices_out; i++) + { + int j; + int io = max(0, inOffset + ibp_base_index + ibp_pattern[i]) * ibp_index_size; + int oo = (outOffset + i) * ibp_index_size_out; + + for (j = 0; j < ibp_index_size; j++) + { + out_data[oo + j] = in_data[src_offset + io + j]; + } + + for (; j < ibp_index_size_out; j++) + { + out_data[oo + j] = uint8_t(0); + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp new file mode 100644 index 000000000..6ee96b7b3 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp @@ -0,0 +1,103 @@ +#version 450 core + +#extension GL_EXT_scalar_block_layout : require + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout (std430, set = 0, binding = 0) uniform draw_count_uniform +{ + uint[64] draw_count_buffer; +}; + +layout (std430, set = 1, binding = 1) buffer indirect_in +{ + int[] indirect_data_in; +}; + +layout (std430, set = 1, binding = 2) buffer indirect_out +{ + int[] indirect_data_out; +}; + +layout (std430, set = 1, binding = 3) buffer index_buffer_pattern +{ + int ibp_pattern[8]; + int ibp_primitive_vertices; + int ibp_primitive_vertices_out; + int ibp_index_size; + int ibp_index_size_out; + int ibp_base_index; + int ibp_index_stride; + int src_offset; + int total_primitives; + int dispatch_x; + int dispatch_y; + int dispatch_z; + int has_draw_count; + uint max_draw_count; + int draw_count_offset; + int indirect_data_stride; + int indirect_data_offset; +}; + +int GetPrimitiveCount(int vertexCount) +{ + return max(0, (vertexCount - ibp_base_index) / ibp_index_stride); +} + +int GetConvertedCount(int indexCount) +{ + int primitiveCount = GetPrimitiveCount(indexCount); + return primitiveCount * ibp_primitive_vertices_out; +} + +void main() +{ + uint drawCount = has_draw_count != 0 ? min(draw_count_buffer[draw_count_offset], max_draw_count) : max_draw_count; + uint i = 0; + + if (drawCount != 0) + { + int firstIndex = indirect_data_in[indirect_data_offset + 2]; + int endIndex = firstIndex + indirect_data_in[indirect_data_offset]; + + for (i = 1; i < drawCount; i++) + { + int offset = int(i) * indirect_data_stride; + int inOffset = indirect_data_offset + offset; + + int currentFirstIndex = indirect_data_in[inOffset + 2]; + firstIndex = min(firstIndex, currentFirstIndex); + endIndex = max(endIndex, currentFirstIndex + indirect_data_in[inOffset]); + } + + int indexCount = endIndex - firstIndex; + + dispatch_x = (indexCount + 15) / 16; + src_offset += firstIndex * ibp_index_size; + total_primitives = GetPrimitiveCount(indexCount); + + for (i = 0; i < drawCount; i++) + { + int offset = int(i) * indirect_data_stride; + int inOffset = indirect_data_offset + offset; + + indirect_data_out[offset] = GetConvertedCount(indirect_data_in[inOffset]); // Index count + indirect_data_out[offset + 1] = indirect_data_in[inOffset + 1]; // Instance count + indirect_data_out[offset + 2] = GetConvertedCount(indirect_data_in[inOffset + 2] - firstIndex); // First index + indirect_data_out[offset + 3] = indirect_data_in[inOffset + 3]; // Vertex offset + indirect_data_out[offset + 4] = indirect_data_in[inOffset + 4]; // First instance + } + } + + for (; i < max_draw_count; i++) + { + int offset = int(i) * indirect_data_stride; + + indirect_data_out[offset] = 0; + indirect_data_out[offset + 1] = 0; + indirect_data_out[offset + 2] = 0; + indirect_data_out[offset + 3] = 0; + indirect_data_out[offset + 4] = 0; + } +} diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index 9cc7e034e..f05f04a89 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -870,5 +870,543 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x1E, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x01, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + + public static readonly byte[] ConvertIndexBufferShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x61, 0x11, 0x00, 0x00, 0x0A, 0x00, 0x07, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F, + 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, + 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x63, 0x61, 0x6C, 0x61, + 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F, 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, + 0x72, 0x5F, 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, + 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, + 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, 0x06, 0x00, 0x09, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x5F, 0x6F, + 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, + 0x06, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, + 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, + 0x06, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, + 0x62, 0x61, 0x73, 0x65, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, + 0x78, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x5F, 0x6F, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x74, 0x6F, 0x74, 0x61, 0x6C, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, + 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x69, 0x6E, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x87, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x0B, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, + 0x5A, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x5A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x61, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x6C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xF7, 0x00, 0x03, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, + 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xF6, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x8E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4D, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4E, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x4E, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x6C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x52, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x73, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0xB1, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x4D, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x75, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x88, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00, + }; + + public static readonly byte[] ConvertIndirectDataShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3D, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, + 0x58, 0x54, 0x5F, 0x73, 0x63, 0x61, 0x6C, 0x61, 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F, + 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5F, 0x70, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, + 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, + 0x65, 0x73, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, + 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, + 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, + 0x7A, 0x65, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x5F, 0x69, 0x6E, 0x64, + 0x65, 0x78, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x73, 0x72, 0x63, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x74, 0x6F, 0x74, 0x61, 0x6C, 0x5F, 0x70, 0x72, + 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x5F, 0x78, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x79, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x5F, 0x7A, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x68, 0x61, 0x73, 0x5F, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, + 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x78, 0x5F, + 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, + 0x6E, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, + 0x6E, 0x74, 0x5F, 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, + 0x6E, 0x74, 0x5F, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x69, 0x6E, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0xB6, 0x00, 0x00, 0x00, + 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x08, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xB5, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x8E, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9D, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0xB5, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0xB6, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x02, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x06, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x37, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x47, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0xF7, 0x00, 0x03, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, + 0x4E, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x37, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, + 0x37, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, + 0x39, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x8D, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9A, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x07, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, + 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, + 0x09, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0D, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0C, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0x0D, 0x01, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0xA2, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, + 0x4C, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0xB0, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, + 0x36, 0x01, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0xA4, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, + 0xAE, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, + 0xB2, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xBC, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, + 0x1C, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, + 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x1D, 0x01, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x16, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x17, 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0xAF, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, + 0xB4, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0xC5, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, + 0xAF, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0xCB, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, + 0xCC, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x31, 0x01, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x00, 0x00, 0x00, + 0x2B, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, + 0xAF, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xD5, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0xD5, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, + 0xD6, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xD8, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDB, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDF, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA4, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, + 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0xE3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xE9, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00, + 0x33, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, + 0xF6, 0x00, 0x04, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0x00, 0x04, 0x00, 0xEB, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xEE, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xEF, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF3, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, + 0xF1, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xF6, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF9, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, + 0xF1, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x3B, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index e54eef4f9..48f80ad48 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -444,7 +444,8 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceVulkan12Features, PNext = pExtendedFeatures, DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"), - DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName) + DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), + UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout") }; pExtendedFeatures = &featuresVk12; diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index ef1360e75..4a905d33d 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -403,7 +403,7 @@ namespace Ryujinx.Graphics.Vulkan supportsTextureShadowLod: false, supportsViewportIndex: featuresVk12.ShaderOutputViewportIndex, supportsViewportSwizzle: false, - supportsIndirectParameters: Capabilities.SupportsIndirectParameters, + supportsIndirectParameters: true, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage, diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index f27116574..acfcdcb46 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 41; + public const int CurrentVersion = 42; /// /// Version of the configuration file format @@ -166,6 +166,11 @@ namespace Ryujinx.Ui.Common.Configuration /// public bool EnableTextureRecompression { get; set; } + /// + /// Enables or disables Macro high-level emulation + /// + public bool EnableMacroHLE { get; set; } + /// /// Enables or disables profiled translation cache persistency /// diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index 3dbbb3dd3..18cf7640c 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -416,6 +416,11 @@ namespace Ryujinx.Ui.Common.Configuration /// public ReactiveObject EnableTextureRecompression { get; private set; } + /// + /// Enables or disables Macro high-level emulation + /// + public ReactiveObject EnableMacroHLE { get; private set; } + /// /// Graphics backend /// @@ -449,6 +454,8 @@ namespace Ryujinx.Ui.Common.Configuration GraphicsBackend.Event += static (sender, e) => LogValueChange(sender, e, nameof(GraphicsBackend)); PreferredGpu = new ReactiveObject(); PreferredGpu.Event += static (sender, e) => LogValueChange(sender, e, nameof(PreferredGpu)); + EnableMacroHLE = new ReactiveObject(); + EnableMacroHLE.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableMacroHLE)); } } @@ -549,6 +556,7 @@ namespace Ryujinx.Ui.Common.Configuration EnableVsync = Graphics.EnableVsync, EnableShaderCache = Graphics.EnableShaderCache, EnableTextureRecompression = Graphics.EnableTextureRecompression, + EnableMacroHLE = Graphics.EnableMacroHLE, EnablePtc = System.EnablePtc, EnableInternetAccess = System.EnableInternetAccess, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, @@ -634,6 +642,7 @@ namespace Ryujinx.Ui.Common.Configuration Graphics.EnableVsync.Value = true; Graphics.EnableShaderCache.Value = true; Graphics.EnableTextureRecompression.Value = false; + Graphics.EnableMacroHLE.Value = true; System.EnablePtc.Value = true; System.EnableInternetAccess.Value = false; System.EnableFsIntegrityChecks.Value = true; @@ -1176,6 +1185,13 @@ namespace Ryujinx.Ui.Common.Configuration }; } + if (configurationFileFormat.Version < 42) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42."); + + configurationFileFormat.EnableMacroHLE = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1207,6 +1223,7 @@ namespace Ryujinx.Ui.Common.Configuration Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; + Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; System.EnablePtc.Value = configurationFileFormat.EnablePtc; System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index c78b7a2f2..7b8b6a98d 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -1039,6 +1039,7 @@ namespace Ryujinx.Ui Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; + Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; } public void SaveConfig() diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs index c07ad1155..901973188 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -56,6 +56,7 @@ namespace Ryujinx.Ui.Windows [GUI] CheckButton _vSyncToggle; [GUI] CheckButton _shaderCacheToggle; [GUI] CheckButton _textureRecompressionToggle; + [GUI] CheckButton _macroHLEToggle; [GUI] CheckButton _ptcToggle; [GUI] CheckButton _internetToggle; [GUI] CheckButton _fsicToggle; @@ -239,6 +240,11 @@ namespace Ryujinx.Ui.Windows _textureRecompressionToggle.Click(); } + if (ConfigurationState.Instance.Graphics.EnableMacroHLE) + { + _macroHLEToggle.Click(); + } + if (ConfigurationState.Instance.System.EnablePtc) { _ptcToggle.Click(); @@ -566,6 +572,7 @@ namespace Ryujinx.Ui.Windows ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active; + ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active; ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active; ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade index 24d36ebd4..88e6dae59 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.glade +++ b/Ryujinx/Ui/Windows/SettingsWindow.glade @@ -2070,6 +2070,24 @@ 1 + + + Enable Macro HLE + True + True + False + Enables or disables high-level emulation of Macro code. Improves performance but may cause graphical glitches in some games + start + 5 + 5 + True + + + False + True + 2 + + True @@ -2131,7 +2149,7 @@ False True 5 - 2 + 3 @@ -2179,7 +2197,7 @@ False True 5 - 3 + 4 @@ -2228,7 +2246,7 @@ False True 5 - 4 + 5 From b96794e72b76138ee1cf36226c47554b4cf1d670 Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Wed, 16 Nov 2022 19:17:03 +0100 Subject: [PATCH 071/737] Use new LINQ Order() methods (#3851) --- Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs | 4 ++-- Ryujinx.Graphics.Shader/Translation/Translator.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index b2eeb5f5f..c6e3b3390 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -525,7 +525,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareInputAttributesPerPatch(CodeGenContext context, HashSet attrs) { - foreach (int attr in attrs.OrderBy(x => x)) + foreach (int attr in attrs.Order()) { DeclareInputAttributePerPatch(context, attr); } @@ -658,7 +658,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet attrs) { - foreach (int attr in attrs.OrderBy(x => x)) + foreach (int attr in attrs.Order()) { DeclareOutputAttributePerPatch(context, attr); } diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 58a934c73..825391960 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -207,7 +207,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (context.Config.NextUsedInputAttributesPerPatch != null) { - foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.OrderBy(x => x)) + foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.Order()) { InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true); } From 11aae9cfbc07d337c8b222e15f5d8e676f3bf46f Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Wed, 16 Nov 2022 19:34:18 +0100 Subject: [PATCH 072/737] Make use of Random.Shared (#3852) --- .../HOS/Services/Friend/ServiceCreator/IFriendService.cs | 3 +-- Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 2 +- Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs | 2 +- Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs | 4 +--- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 1cf03f5bc..77499fa53 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -266,9 +266,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // NOTE: Calls nn::friends::detail::service::core::PlayHistoryManager::GetInstance and stores the instance. byte[] randomBytes = new byte[8]; - Random random = new Random(); - random.NextBytes(randomBytes); + Random.Shared.NextBytes(randomBytes); // NOTE: Calls nn::friends::detail::service::core::UuidManager::GetInstance and stores the instance. // Then call nn::friends::detail::service::core::AccountStorageManager::GetInstance and store the instance. diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 851b67a5b..4e445c4fc 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { byte[] uuid = new byte[9]; - new Random().NextBytes(uuid); + Random.Shared.NextBytes(uuid); uuid[3] = (byte)(0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2]); uuid[8] = (byte)(uuid[3] ^ uuid[4] ^ uuid[5] ^ uuid[6]); diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs index 3fa1182db..019626f1b 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Prepo { byte[] randomBuffer = new byte[8]; - new Random().NextBytes(randomBuffer); + Random.Shared.NextBytes(randomBuffer); _systemSessionId = BitConverter.ToUInt64(randomBuffer, 0); } diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index 5590bfddf..0d1ae27f2 100644 --- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -32,8 +32,6 @@ namespace Ryujinx.HLE.HOS.Services.Ro private KProcess _owner; private IVirtualMemoryManager _ownerMm; - private static Random _random = new Random(); - public IRoInterface(ServiceCtx context) { _nrrInfos = new List(MaxNrr); @@ -283,7 +281,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro { while (true) { - ulong randomOffset = (ulong)(uint)_random.Next(0, (int)addressSpacePageLimit) << 12; + ulong randomOffset = (ulong)(uint)Random.Shared.Next(0, (int)addressSpacePageLimit) << 12; targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset; From d751da84f941e0d089040cb7aad2c1b3224ae6b7 Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Wed, 16 Nov 2022 20:30:12 +0100 Subject: [PATCH 073/737] Use new C# 11 u8 string literals (#3854) --- Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs | 8 ++++---- Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 2 +- .../HOS/Services/Nifm/StaticService/IGeneralService.cs | 2 +- Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs | 2 +- Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs | 10 +++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs b/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs index 830b36eeb..d49313ea1 100644 --- a/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs +++ b/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs @@ -34,10 +34,10 @@ namespace Ryujinx.Audio.Renderer.Utils writer.Seek(0, SeekOrigin.Begin); - writer.Write(Encoding.ASCII.GetBytes("RIFF")); + writer.Write("RIFF"u8); writer.Write((int)(writer.BaseStream.Length - 8)); - writer.Write(Encoding.ASCII.GetBytes("WAVE")); - writer.Write(Encoding.ASCII.GetBytes("fmt ")); + writer.Write("WAVE"u8); + writer.Write("fmt "u8); writer.Write(16); writer.Write((short)1); writer.Write((short)GetChannelCount()); @@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Utils writer.Write(GetSampleRate() * GetChannelCount() * sizeof(short)); writer.Write((short)(GetChannelCount() * sizeof(short))); writer.Write((short)(sizeof(short) * 8)); - writer.Write(Encoding.ASCII.GetBytes("data")); + writer.Write("data"u8); writer.Write((int)(writer.BaseStream.Length - HeaderSize)); writer.Seek((int)currentPos, SeekOrigin.Begin); diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 4e445c4fc..4fdeadcb2 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp Reserved1 = new Array64(), Reserved2 = new Array58() }; - Encoding.ASCII.GetBytes("Ryujinx").CopyTo(registerInfo.Nickname.AsSpan()); + "Ryujinx"u8.CopyTo(registerInfo.Nickname.AsSpan()); return registerInfo; } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index 4a0259e60..bc70750d9 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress); networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties); - Encoding.ASCII.GetBytes("RyujinxNetwork").CopyTo(networkProfile.Name.AsSpan()); + "RyujinxNetwork"u8.CopyTo(networkProfile.Name.AsSpan()); context.Memory.Write(networkProfileDataPosition, networkProfile); diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs index 3b13b583a..12f6e90d4 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; - private static readonly byte[] TimeZoneDefaultRule = Encoding.ASCII.GetBytes(",M4.1.0,M10.5.0"); + private static ReadOnlySpan TimeZoneDefaultRule => ",M4.1.0,M10.5.0"u8; [StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)] private struct CalendarTimeInternal diff --git a/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs b/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs index de50c6eb4..510fec059 100644 --- a/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs +++ b/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs @@ -20,10 +20,10 @@ namespace Ryujinx.HLE.Loaders.Mods private static MemPatch ParseIps(BinaryReader reader) { - Span IpsHeaderMagic = Encoding.ASCII.GetBytes("PATCH").AsSpan(); - Span IpsTailMagic = Encoding.ASCII.GetBytes("EOF").AsSpan(); - Span Ips32HeaderMagic = Encoding.ASCII.GetBytes("IPS32").AsSpan(); - Span Ips32TailMagic = Encoding.ASCII.GetBytes("EEOF").AsSpan(); + ReadOnlySpan IpsHeaderMagic = "PATCH"u8; + ReadOnlySpan IpsTailMagic = "EOF"u8; + ReadOnlySpan Ips32HeaderMagic = "IPS32"u8; + ReadOnlySpan Ips32TailMagic = "EEOF"u8; MemPatch patches = new MemPatch(); var header = reader.ReadBytes(IpsHeaderMagic.Length).AsSpan(); @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.Loaders.Mods } bool is32; - Span tailSpan; + ReadOnlySpan tailSpan; if (header.SequenceEqual(IpsHeaderMagic)) { From d536cc8ae6d6725780365d858f2fd64b66d90b7f Mon Sep 17 00:00:00 2001 From: Wunk Date: Wed, 16 Nov 2022 14:27:42 -0800 Subject: [PATCH 074/737] Update units of memory from decimal to binary prefixes (#3716) `MB` and `GB` can either be interpreted as having base-10 units, or base-2. `MiB` and `GiB` removes this discrepancy so that units of memory are always interpreted using base-2 units. --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .../Diagnostics/TranslatorEventSource.cs | 4 +- README.md | 2 +- Ryujinx.Ava/AppHost.cs | 2 +- Ryujinx.Ava/Assets/Locales/de_DE.json | 6 +- Ryujinx.Ava/Assets/Locales/el_GR.json | 18 +++--- Ryujinx.Ava/Assets/Locales/en_US.json | 6 +- Ryujinx.Ava/Assets/Locales/es_ES.json | 4 +- Ryujinx.Ava/Assets/Locales/fr_FR.json | 2 +- Ryujinx.Ava/Assets/Locales/it_IT.json | 4 +- Ryujinx.Ava/Assets/Locales/ja_JP.json | 6 +- Ryujinx.Ava/Assets/Locales/ko_KR.json | 4 +- Ryujinx.Ava/Assets/Locales/pl_PL.json | 6 +- Ryujinx.Ava/Assets/Locales/pt_BR.json | 4 +- Ryujinx.Ava/Assets/Locales/ru_RU.json | 2 +- Ryujinx.Ava/Assets/Locales/tr_TR.json | 4 +- Ryujinx.Ava/Assets/Locales/zh_CN.json | 6 +- Ryujinx.Ava/Assets/Locales/zh_TW.json | 4 +- Ryujinx.Ava/Ui/Models/FileSizeSortComparer.cs | 12 ++-- .../Ui/Models/Generic/FileSizeSortComparer.cs | 12 ++-- Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs | 10 ++-- Ryujinx.Common/SystemInfo/SystemInfo.cs | 4 +- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 2 +- .../HOS/Kernel/Common/KSystemControl.cs | 38 ++++++------ .../HOS/Kernel/Common/MemoryArrange.cs | 12 ++-- Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs | 6 +- .../NvHostAsGpu/NvHostAsGpuDeviceFile.cs | 2 +- .../HOS/Services/Spl/IGeneralInterface.cs | 10 ++-- Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs | 58 +++++++++---------- Ryujinx.HLE/MemoryConfiguration.cs | 50 ++++++++-------- Ryujinx.Headless.SDL2/Options.cs | 2 +- Ryujinx.Headless.SDL2/Program.cs | 2 +- Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 2 +- .../Configuration/ConfigurationFileFormat.cs | 2 +- Ryujinx/Ui/Helper/SortHelper.cs | 12 ++-- Ryujinx/Ui/MainWindow.cs | 4 +- Ryujinx/Ui/Windows/SettingsWindow.glade | 4 +- 37 files changed, 165 insertions(+), 165 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index cf1a7cc69..574877570 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -34,7 +34,7 @@ about: Something doesn't work correctly in Ryujinx. Note that game-specific issu - OS: *(e.g. Windows 10)* - CPU: *(e.g. i7-6700)* - GPU: *(e.g. NVIDIA RTX 2070)* - - RAM: *(e.g. 16GB)* + - RAM: *(e.g. 16GiB)* - Applied Mods : [ Yes (Which ones) / No ] ### Additional context? diff --git a/ARMeilleure/Diagnostics/TranslatorEventSource.cs b/ARMeilleure/Diagnostics/TranslatorEventSource.cs index a0456e984..a4f17844d 100644 --- a/ARMeilleure/Diagnostics/TranslatorEventSource.cs +++ b/ARMeilleure/Diagnostics/TranslatorEventSource.cs @@ -25,13 +25,13 @@ namespace ARMeilleure.Diagnostics _funcTabSizeCounter = new PollingCounter("addr-tab-alloc", this, () => _funcTabSize / 1024d / 1024d) { DisplayName = "AddressTable Total Bytes Allocated", - DisplayUnits = "MB" + DisplayUnits = "MiB" }; _funcTabLeafSizeCounter = new PollingCounter("addr-tab-leaf-alloc", this, () => _funcTabLeafSize / 1024d / 1024d) { DisplayName = "AddressTable Total Leaf Bytes Allocated", - DisplayUnits = "MB" + DisplayUnits = "MiB" }; } diff --git a/README.md b/README.md index 0b59dd348..0731884b3 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ You can check out the compatibility list [here](https://github.com/Ryujinx/Ryuji ## Usage -To run this emulator, your PC must be equipped with at least 8GB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes. +To run this emulator, your PC must be equipped with at least 8GiB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes. See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator. diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index d039ded79..3d4c6cde8 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -741,7 +741,7 @@ namespace Ryujinx.Ava } } - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GB : HLE.MemoryConfiguration.MemoryConfiguration4GB; + var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB; IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; diff --git a/Ryujinx.Ava/Assets/Locales/de_DE.json b/Ryujinx.Ava/Assets/Locales/de_DE.json index 0b9dc4d9b..cb8d787b8 100644 --- a/Ryujinx.Ava/Assets/Locales/de_DE.json +++ b/Ryujinx.Ava/Assets/Locales/de_DE.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Kann Fehler verursachen)", - "SettingsTabSystemExpandDramSize": "Erweitere DRAM Größe auf 6GB", + "SettingsTabSystemExpandDramSize": "Erweitere DRAM Größe auf 6GiB", "SettingsTabSystemIgnoreMissingServices": "Ignoriere fehlende Dienste", "SettingsTabGraphics": "Grafik", "SettingsTabGraphicsAPI": "Grafik-API", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Verwendung einer Software-Seitentabelle für die Adressumsetzung. Höchste Genauigkeit, aber langsamste Leistung.", "MemoryManagerHostTooltip": "Direkte Zuordnung von Speicher im Host-Adressraum. Viel schnellere JIT-Kompilierung und Ausführung.", "MemoryManagerUnsafeTooltip": "Direkte Zuordnung des Speichers, aber keine Maskierung der Adresse innerhalb des Gastadressraums vor dem Zugriff. Schneller, aber auf Kosten der Sicherheit. Die Gastanwendung kann von überall in Ryujinx auf den Speicher zugreifen, daher sollte in diesem Modus nur Programme ausgeführt werden denen vertraut wird.", - "DRamTooltip": "Erhöht den Arbeitsspeicher des emulierten Systems von 4 GB auf 6 GB.\n\nDies ist nur für Texturenpakete mit höherer Auflösung oder Mods mit 4K-Auflösung nützlich. Diese Option verbessert NICHT die Leistung.\n\nIm Zweifelsfall AUS lassen.", + "DRamTooltip": "Erhöht den Arbeitsspeicher des emulierten Systems von 4 GiB auf 6 GiB.\n\nDies ist nur für Texturenpakete mit höherer Auflösung oder Mods mit 4K-Auflösung nützlich. Diese Option verbessert NICHT die Leistung.\n\nIm Zweifelsfall AUS lassen.", "IgnoreMissingServicesTooltip": "Durch diese Option werden nicht implementierte Dienste der Switch-Firmware ignoriert. Dies kann dabei helfen, Abstürze beim Starten bestimmter Spiele zu umgehen.\n\nIm Zweifelsfall AUS lassen.", "GraphicsBackendThreadingTooltip": "Führt Grafik-Backend Befehle auf einem zweiten Thread aus.\n\nDies beschleunigt die Shader-Kompilierung, reduziert Stottern und verbessert die Leistung auf GPU-Treibern ohne eigene Multithreading-Unterstützung. Geringfügig bessere Leistung bei Treibern mit Multithreading.\n\nIm Zweifelsfall auf AUTO stellen.", "GalThreadingTooltip": "Führt Grafik-Backend Befehle auf einem zweiten Thread aus.\n\nDies Beschleunigt die Shader-Kompilierung, reduziert Stottern und verbessert die Leistung auf GPU-Treibern ohne eigene Multithreading-Unterstützung. Geringfügig bessere Leistung bei Treibern mit Multithreading.\n\nIm Zweifelsfall auf auf AUTO stellen.", @@ -583,7 +583,7 @@ "SettingsTabGraphicsBackend": "Grafik-Backend:", "SettingsTabGraphicsBackendTooltip": "Verwendendetes Grafik-Backend", "SettingsEnableTextureRecompression": "Textur-Rekompression", - "SettingsEnableTextureRecompressionTooltip": "Komprimiert bestimmte Texturen, um den VRAM-Verbrauch zu reduzieren.\n\nEmpfohlen für die Verwendung von GPUs, die weniger als 4 GB VRAM haben.\n\nIm Zweifelsfall AUS lassen", + "SettingsEnableTextureRecompressionTooltip": "Komprimiert bestimmte Texturen, um den VRAM-Verbrauch zu reduzieren.\n\nEmpfohlen für die Verwendung von GPUs, die weniger als 4 GiB VRAM haben.\n\nIm Zweifelsfall AUS lassen", "SettingsTabGraphicsPreferredGpu": "Bevorzugte GPU:", "SettingsTabGraphicsPreferredGpuTooltip": "Wähle die Grafikkarte aus, die mit dem Vulkan Grafik-Backend verwendet werden soll.\n\nDies hat keinen Einfluss auf die GPU die OpenGL verwendet.\n\nIm Zweifelsfall die als \"dGPU\" gekennzeichnete GPU auswählen. Diese Einstellung unberührt lassen, wenn keine zur Auswahl steht.", "SettingsAppRequiredRestartMessage": "Ein Neustart von Ryujinx ist erforderlich", diff --git a/Ryujinx.Ava/Assets/Locales/el_GR.json b/Ryujinx.Ava/Assets/Locales/el_GR.json index 242bcec77..4fb8bf567 100644 --- a/Ryujinx.Ava/Assets/Locales/el_GR.json +++ b/Ryujinx.Ava/Assets/Locales/el_GR.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Μικροδιορθώσεις", "SettingsTabSystemHacksNote": " (Μπορεί να προκαλέσουν αστάθεια)", - "SettingsTabSystemExpandDramSize": "Επέκταση μεγέθους DRAM στα 6GB", + "SettingsTabSystemExpandDramSize": "Επέκταση μεγέθους DRAM στα 6GiB", "SettingsTabSystemIgnoreMissingServices": "Αγνόηση υπηρεσιών που λείπουν", "SettingsTabGraphics": "Γραφικά", "SettingsTabGraphicsAPI": "API Γραφικά", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Χρησιμοποιήστε έναν πίνακα σελίδων λογισμικού για τη μετάφραση διευθύνσεων. Υψηλότερη ακρίβεια αλλά πιο αργή απόδοση.", "MemoryManagerHostTooltip": "Απευθείας αντιστοίχιση της μνήμης στον χώρο διευθύνσεων υπολογιστή υποδοχής. Πολύ πιο γρήγορη μεταγλώττιση και εκτέλεση JIT.", "MemoryManagerUnsafeTooltip": "Απευθείας χαρτογράφηση της μνήμης, αλλά μην καλύπτετε τη διεύθυνση εντός του χώρου διευθύνσεων επισκέπτη πριν από την πρόσβαση. Πιο γρήγορα, αλλά με κόστος ασφάλειας. Η εφαρμογή μπορεί να έχει πρόσβαση στη μνήμη από οπουδήποτε στο Ryujinx, επομένως εκτελείτε μόνο προγράμματα που εμπιστεύεστε με αυτήν τη λειτουργία.", - "DRamTooltip": "Επεκτείνει την ποσότητα της μνήμης στο εξομοιούμενο σύστημα από 4 GB σε 6 GB", + "DRamTooltip": "Επεκτείνει την ποσότητα της μνήμης στο εξομοιούμενο σύστημα από 4 GiB σε 6 GiB", "IgnoreMissingServicesTooltip": "Ενεργοποίηση ή απενεργοποίηση της αγνοώησης για υπηρεσίες που λείπουν", "GraphicsBackendThreadingTooltip": "Ενεργοποίηση Πολυνηματικής Επεξεργασίας Γραφικών", "GalThreadingTooltip": "Εκτελεί εντολές γραφικών σε ένα δεύτερο νήμα. Επιτρέπει την πολυνηματική μεταγλώττιση Shader σε χρόνο εκτέλεσης, μειώνει το τρεμόπαιγμα και βελτιώνει την απόδοση των προγραμμάτων οδήγησης χωρίς τη δική τους υποστήριξη πολλαπλών νημάτων. Ποικίλες κορυφαίες επιδόσεις σε προγράμματα οδήγησης με multithreading. Μπορεί να χρειαστεί επανεκκίνηση του Ryujinx για να απενεργοποιήσετε σωστά την ενσωματωμένη λειτουργία πολλαπλών νημάτων του προγράμματος οδήγησης ή ίσως χρειαστεί να το κάνετε χειροκίνητα για να έχετε την καλύτερη απόδοση.", @@ -480,17 +480,17 @@ "AudioVolumeTooltip": "Αλλαγή Έντασης Ήχου", "SettingsTabSystemEnableInternetAccess": "Ενεργοποίηση πρόσβασης επισκέπτη στο Διαδίκτυο", "EnableInternetAccessTooltip": "Επιτρέπει την πρόσβαση επισκέπτη στο Διαδίκτυο. Εάν ενεργοποιηθεί, η εξομοιωμένη κονσόλα Switch θα συμπεριφέρεται σαν να είναι συνδεδεμένη στο Διαδίκτυο. Λάβετε υπόψη ότι σε ορισμένες περιπτώσεις, οι εφαρμογές ενδέχεται να εξακολουθούν να έχουν πρόσβαση στο Διαδίκτυο, ακόμη και όταν αυτή η επιλογή είναι απενεργοποιημένη", - "GameListContextMenuManageCheatToolTip" : "Διαχείριση Κόλπων", - "GameListContextMenuManageCheat" : "Διαχείριση Κόλπων", - "ControllerSettingsStickRange" : "Εύρος:", - "DialogStopEmulationTitle" : "Ryujinx - Διακοπή εξομοίωσης", + "GameListContextMenuManageCheatToolTip": "Διαχείριση Κόλπων", + "GameListContextMenuManageCheat": "Διαχείριση Κόλπων", + "ControllerSettingsStickRange": "Εύρος:", + "DialogStopEmulationTitle": "Ryujinx - Διακοπή εξομοίωσης", "DialogStopEmulationMessage": "Είστε βέβαιοι ότι θέλετε να σταματήσετε την εξομοίωση;", "SettingsTabCpu": "Επεξεργαστής", "SettingsTabAudio": "Ήχος", "SettingsTabNetwork": "Δίκτυο", - "SettingsTabNetworkConnection" : "Σύνδεση δικτύου", - "SettingsTabCpuCache" : "Προσωρινή Μνήμη CPU", - "SettingsTabCpuMemory" : "Μνήμη CPU", + "SettingsTabNetworkConnection": "Σύνδεση δικτύου", + "SettingsTabCpuCache": "Προσωρινή Μνήμη CPU", + "SettingsTabCpuMemory": "Μνήμη CPU", "ControllerMotionTitle": "Motion Control Settings", "ControllerRumbleTitle": "Rumble Settings" } diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 3a72be34f..0bfa45d99 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (may cause instability)", - "SettingsTabSystemExpandDramSize": "Expand DRAM Size to 6GB", + "SettingsTabSystemExpandDramSize": "Expand DRAM Size to 6GiB", "SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services", "SettingsTabGraphics": "Graphics", "SettingsTabGraphicsAPI": "Graphics API", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.", "MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", "MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", - "DRamTooltip": "Increases the amount of memory on the emulated system from 4GB to 6GB.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", + "DRamTooltip": "Increases the amount of memory on the emulated system from 4GiB to 6GiB.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.", "GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", "GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", @@ -583,7 +583,7 @@ "SettingsTabGraphicsBackend": "Graphics Backend", "SettingsTabGraphicsBackendTooltip": "Graphics Backend to use", "SettingsEnableTextureRecompression": "Enable Texture Recompression", - "SettingsEnableTextureRecompressionTooltip": "Compresses certain textures in order to reduce VRAM usage.\n\nRecommended for use with GPUs that have less than 4GB VRAM.\n\nLeave OFF if unsure.", + "SettingsEnableTextureRecompressionTooltip": "Compresses certain textures in order to reduce VRAM usage.\n\nRecommended for use with GPUs that have less than 4GiB VRAM.\n\nLeave OFF if unsure.", "SettingsTabGraphicsPreferredGpu": "Preferred GPU", "SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", diff --git a/Ryujinx.Ava/Assets/Locales/es_ES.json b/Ryujinx.Ava/Assets/Locales/es_ES.json index 693bac95a..e6ba35c07 100644 --- a/Ryujinx.Ava/Assets/Locales/es_ES.json +++ b/Ryujinx.Ava/Assets/Locales/es_ES.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Pueden causar inestabilidad)", - "SettingsTabSystemExpandDramSize": "Expandir DRAM a 6GB", + "SettingsTabSystemExpandDramSize": "Expandir DRAM a 6GiB", "SettingsTabSystemIgnoreMissingServices": "Ignorar servicios no implementados", "SettingsTabGraphics": "Gráficos", "SettingsTabGraphicsAPI": "API de gráficos", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Usa una tabla de paginación de software para traducir direcciones. Ofrece la precisión más exacta pero el rendimiento más lento.", "MemoryManagerHostTooltip": "Mapea la memoria directamente en la dirección de espacio del host. Compilación y ejecución JIT mucho más rápida.", "MemoryManagerUnsafeTooltip": "Mapea la memoria directamente, pero no enmascara la dirección dentro del espacio de dirección del guest antes del acceso. El modo más rápido, pero a costa de seguridad. La aplicación guest puede acceder a la memoria desde cualquier parte en Ryujinx, así que ejecuta solo programas en los que confíes cuando uses este modo.", - "DRamTooltip": "Expande la memoria DRAM del sistema emulado de 4GB a 6GB.\n\nUtilizar solo con packs de texturas HD o mods de resolución 4K. NO mejora el rendimiento.\n\nDesactívalo si no sabes qué hacer.", + "DRamTooltip": "Expande la memoria DRAM del sistema emulado de 4GiB a 6GiB.\n\nUtilizar solo con packs de texturas HD o mods de resolución 4K. NO mejora el rendimiento.\n\nDesactívalo si no sabes qué hacer.", "IgnoreMissingServicesTooltip": "Hack para ignorar servicios no implementados del Horizon OS. Esto puede ayudar a sobrepasar crasheos cuando inicies ciertos juegos.\n\nDesactívalo si no sabes qué hacer.", "GraphicsBackendThreadingTooltip": "Ejecuta los comandos del motor gráfico en un segundo hilo. Acelera la compilación de sombreadores, reduce los tirones, y mejora el rendimiento en controladores gráficos que no realicen su propio multihilado. Rendimiento máximo ligeramente superior en controladores gráficos que soporten multihilado.\n\nSelecciona \"Auto\" si no sabes qué hacer.", "GalThreadingTooltip": "Ejecuta los comandos del motor gráfico en un segundo hilo. Acelera la compilación de sombreadores, reduce los tirones, y mejora el rendimiento en controladores gráficos que no realicen su propio multihilado. Rendimiento máximo ligeramente superior en controladores gráficos que soporten multihilado.\n\nSelecciona \"Auto\" si no sabes qué hacer.", diff --git a/Ryujinx.Ava/Assets/Locales/fr_FR.json b/Ryujinx.Ava/Assets/Locales/fr_FR.json index 2cd5cfd2f..29d26ec88 100644 --- a/Ryujinx.Ava/Assets/Locales/fr_FR.json +++ b/Ryujinx.Ava/Assets/Locales/fr_FR.json @@ -112,7 +112,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Cela peut causer des instabilitées)", - "SettingsTabSystemExpandDramSize": "Augmenter la taille de la DRAM à 6GB", + "SettingsTabSystemExpandDramSize": "Augmenter la taille de la DRAM à 6GiB", "SettingsTabSystemIgnoreMissingServices": "Ignorer les services manquant", "SettingsTabGraphics": "Graphique", "SettingsTabGraphicsAPI": "API Graphique", diff --git a/Ryujinx.Ava/Assets/Locales/it_IT.json b/Ryujinx.Ava/Assets/Locales/it_IT.json index f2e6188eb..beed9a995 100644 --- a/Ryujinx.Ava/Assets/Locales/it_IT.json +++ b/Ryujinx.Ava/Assets/Locales/it_IT.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Possono causare instabilità)", - "SettingsTabSystemExpandDramSize": "Espandi dimensione DRAM a 6GB", + "SettingsTabSystemExpandDramSize": "Espandi dimensione DRAM a 6GiB", "SettingsTabSystemIgnoreMissingServices": "Ignora servizi mancanti", "SettingsTabGraphics": "Grafica", "SettingsTabGraphicsAPI": "API Grafiche", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Usa una software page table per la traduzione degli indirizzi. Massima precisione ma prestazioni più lente.", "MemoryManagerHostTooltip": "Mappa direttamente la memoria nello spazio degli indirizzi dell'host. Compilazione ed esecuzione JIT molto più veloce.", "MemoryManagerUnsafeTooltip": "Mappa direttamente la memoria, ma non maschera l'indirizzo all'interno dello spazio degli indirizzi guest prima dell'accesso. Più veloce, ma a costo della sicurezza. L'applicazione guest può accedere alla memoria da qualsiasi punto di Ryujinx, quindi esegui solo programmi di cui ti fidi con questa modalità.", - "DRamTooltip": "Espande l'ammontare di memoria sul sistema emulato da 4GB A 6GB", + "DRamTooltip": "Espande l'ammontare di memoria sul sistema emulato da 4GiB A 6GiB", "IgnoreMissingServicesTooltip": "Attiva o disattiva l'opzione di ignorare i servizi mancanti", "GraphicsBackendThreadingTooltip": "Attiva il Graphics Backend Multithreading", "GalThreadingTooltip": "Esegue i comandi del backend grafico su un secondo thread. Permette il multithreading runtime della compilazione degli shader, riduce lo stuttering e migliora le prestazioni sui driver senza supporto multithreading proprio. Varia leggermente le prestazioni di picco sui driver con multithreading. Ryujinx potrebbe aver bisogno di essere riavviato per disabilitare correttamente il multithreading integrato nel driver, o potrebbe essere necessario farlo manualmente per ottenere le migliori prestazioni.", diff --git a/Ryujinx.Ava/Assets/Locales/ja_JP.json b/Ryujinx.Ava/Assets/Locales/ja_JP.json index f26fa9e0f..5d95d7d08 100644 --- a/Ryujinx.Ava/Assets/Locales/ja_JP.json +++ b/Ryujinx.Ava/Assets/Locales/ja_JP.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "ハック", "SettingsTabSystemHacksNote": " (挙動が不安定になる可能性があります)", - "SettingsTabSystemExpandDramSize": "DRAMサイズを6GBに拡大", + "SettingsTabSystemExpandDramSize": "DRAMサイズを6GiBに拡大", "SettingsTabSystemIgnoreMissingServices": "未実装サービスを無視", "SettingsTabGraphics": "グラフィックス", "SettingsTabGraphicsAPI": "グラフィックスAPI", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "アドレス変換にソフトウェアページテーブルを使用します. 非常に正確ですがパフォーマンスが大きく低下します.", "MemoryManagerHostTooltip": "ホストのアドレス空間にメモリを直接マップします.JITのコンパイルと実行速度が大きく向上します.", "MemoryManagerUnsafeTooltip": "メモリを直接マップしますが, アクセス前にゲストのアドレス空間内のアドレスをマスクしません. より高速になりますが, 安全性が犠牲になります. ゲストアプリケーションは Ryujinx のどこからでもメモリにアクセスできるので,このモードでは信頼できるプログラムだけを実行するようにしてください.", - "DRamTooltip": "エミュレートされたシステムのメモリ容量を 4GB から 6GB に増加します.\n\n高解像度のテクスチャパックや 4K解像度の mod を使用する場合に有用です. パフォーマンスを改善するものではありません.\n\nよくわからない場合はオフのままにしてください.", + "DRamTooltip": "エミュレートされたシステムのメモリ容量を 4GiB から 6GiB に増加します.\n\n高解像度のテクスチャパックや 4K解像度の mod を使用する場合に有用です. パフォーマンスを改善するものではありません.\n\nよくわからない場合はオフのままにしてください.", "IgnoreMissingServicesTooltip": "未実装の Horizon OS サービスを無視します. 特定のゲームにおいて起動時のクラッシュを回避できる場合があります.\n\nよくわからない場合はオフのままにしてください.", "GraphicsBackendThreadingTooltip": "グラフィックスバックエンドのコマンドを別スレッドで実行します.\n\nシェーダのコンパイルを高速化し, 遅延を軽減し, マルチスレッド非対応の GPU ドライバにおいてパフォーマンスを改善します. マルチスレッド対応のドライバでも若干パフォーマンス改善が見られます.\n\nよくわからない場合は自動に設定してください.", "GalThreadingTooltip": "グラフィックスバックエンドのコマンドを別スレッドで実行します.\n\nシェーダのコンパイルを高速化し, 遅延を軽減し, マルチスレッド非対応の GPU ドライバにおいてパフォーマンスを改善します. マルチスレッド対応のドライバでも若干パフォーマンス改善が見られます.\n\nよくわからない場合は自動に設定してください.", @@ -583,7 +583,7 @@ "SettingsTabGraphicsBackend": "グラフィックスバックエンド", "SettingsTabGraphicsBackendTooltip": "使用するグラフィックスバックエンドです", "SettingsEnableTextureRecompression": "テクスチャの再圧縮を有効", - "SettingsEnableTextureRecompressionTooltip": "VRAMの使用量を削減するためテクスチャを圧縮します.\n\nGPUのVRAMが4GB未満の場合は使用を推奨します.\n\nよくわからない場合はオフのままにしてください.", + "SettingsEnableTextureRecompressionTooltip": "VRAMの使用量を削減するためテクスチャを圧縮します.\n\nGPUのVRAMが4GiB未満の場合は使用を推奨します.\n\nよくわからない場合はオフのままにしてください.", "SettingsTabGraphicsPreferredGpu": "優先使用するGPU", "SettingsTabGraphicsPreferredGpuTooltip": "Vulkanグラフィックスバックエンドで使用されるグラフィックスカードを選択します.\n\nOpenGLが使用するGPUには影響しません.\n\n不明な場合は, \"dGPU\" としてフラグが立っているGPUに設定します. ない場合はそのままにします.", "SettingsAppRequiredRestartMessage": "Ryujinx の再起動が必要です", diff --git a/Ryujinx.Ava/Assets/Locales/ko_KR.json b/Ryujinx.Ava/Assets/Locales/ko_KR.json index 34253314f..cc7232c9f 100644 --- a/Ryujinx.Ava/Assets/Locales/ko_KR.json +++ b/Ryujinx.Ava/Assets/Locales/ko_KR.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "해킹", "SettingsTabSystemHacksNote": " (불안정을 일으킬 수 있음)", - "SettingsTabSystemExpandDramSize": "DRAM 크기를 6GB로 확장", + "SettingsTabSystemExpandDramSize": "DRAM 크기를 6GiB로 확장", "SettingsTabSystemIgnoreMissingServices": "누락된 서비스 무시", "SettingsTabGraphics": "제도법", "SettingsTabGraphicsAPI": "그래픽 API", @@ -439,7 +439,7 @@ "MemoryManagerSoftwareTooltip": "주소 변환을 위해 소프트웨어 페이지 테이블을 사용하십시오. 정확도는 가장 높지만 성능은 가장 느립니다.", "MemoryManagerHostTooltip": "호스트 주소 공간에서 메모리를 직접 매핑합니다. 훨씬 더 빠른 JIT 컴파일 및 실행.", "MemoryManagerUnsafeTooltip": "메모리를 직접 매핑하지만 액세스하기 전에 게스트 주소 공간 내의 주소를 마스킹하지 마십시오. 더 빠르지만 안전을 희생해야 합니다. 게스트 응용 프로그램은 Ryujinx의 어디에서나 메모리에 액세스할 수 있으므로 이 모드로 신뢰할 수 있는 프로그램만 실행하십시오.", - "DRamTooltip": "에뮬레이트된 시스템의 메모리 양을 4GB에서 6GB로 확장", + "DRamTooltip": "에뮬레이트된 시스템의 메모리 양을 4GiB에서 6GiB로 확장", "IgnoreMissingServicesTooltip": "누락된 서비스 무시 옵션 활성화 또는 비활성화", "GraphicsBackendThreadingTooltip": "그래픽 백엔드 멀티스레딩 활성화", "GalThreadingTooltip": "두 번째 스레드에서 그래픽 백엔드 명령을 실행합니다. 셰이더 컴파일의 런타임 멀티스레딩을 허용하고, 말더듬을 줄이고, 자체 멀티스레딩 지원 없이 드라이버의 성능을 개선합니다. 멀티스레딩이 있는 드라이버에서 약간 다른 최대 성능. 드라이버 내장 멀티스레딩을 올바르게 비활성화하려면 Ryujinx를 다시 시작해야 할 수도 있고 최상의 성능을 얻으려면 수동으로 수행해야 할 수도 있습니다.", diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/Ryujinx.Ava/Assets/Locales/pl_PL.json index 95329b2b6..d7f8a1bcb 100644 --- a/Ryujinx.Ava/Assets/Locales/pl_PL.json +++ b/Ryujinx.Ava/Assets/Locales/pl_PL.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacki", "SettingsTabSystemHacksNote": " (mogą powodować niestabilność)", - "SettingsTabSystemExpandDramSize": "Rozszerz Rozmiar DRAM do 6 GB", + "SettingsTabSystemExpandDramSize": "Rozszerz Rozmiar DRAM do 6 GiB", "SettingsTabSystemIgnoreMissingServices": "Ignoruj Brakujące Usługi", "SettingsTabGraphics": "Grafika", "SettingsTabGraphicsAPI": "Graficzne API", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Użyj tabeli stron oprogramowania do translacji adresów. Najwyższa celność, ale najwolniejsza wydajność.", "MemoryManagerHostTooltip": "Bezpośrednio mapuj pamięć w przestrzeni adresowej hosta. Znacznie szybsza kompilacja i wykonanie JIT.", "MemoryManagerUnsafeTooltip": "Bezpośrednio mapuj pamięć, ale nie maskuj adresu w przestrzeni adresowej gościa przed uzyskaniem dostępu. Szybciej, ale kosztem bezpieczeństwa. Aplikacja gościa może uzyskać dostęp do pamięci z dowolnego miejsca w Ryujinx, więc w tym trybie uruchamiaj tylko programy, którym ufasz.", - "DRamTooltip": "Zwiększa ilość pamięci w emulowanym systemie z 4 GB do 6 GB.\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.", + "DRamTooltip": "Zwiększa ilość pamięci w emulowanym systemie z 4 GiB do 6 GiB.\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.", "IgnoreMissingServicesTooltip": "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.", "GraphicsBackendThreadingTooltip": "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.", "GalThreadingTooltip": "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.", @@ -583,7 +583,7 @@ "SettingsTabGraphicsBackend": "Backend Graficzny", "SettingsTabGraphicsBackendTooltip": "Używalne Backendy Graficzne", "SettingsEnableTextureRecompression": "Włącz Rekompresję Tekstur", - "SettingsEnableTextureRecompressionTooltip": "Kompresuje niektóre tekstury w celu zmniejszenia zużycia pamięci VRAM.\n\nZalecane do użytku z GPU, które mają mniej niż 4 GB pamięci VRAM.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", + "SettingsEnableTextureRecompressionTooltip": "Kompresuje niektóre tekstury w celu zmniejszenia zużycia pamięci VRAM.\n\nZalecane do użytku z GPU, które mają mniej niż 4 GiB pamięci VRAM.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", "SettingsTabGraphicsPreferredGpu": "Preferowane GPU", "SettingsTabGraphicsPreferredGpuTooltip": "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.", "SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx", diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json index 01f629095..c49084d89 100644 --- a/Ryujinx.Ava/Assets/Locales/pt_BR.json +++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (Pode causar instabilidade)", - "SettingsTabSystemExpandDramSize": "Expandir memória para 6GB", + "SettingsTabSystemExpandDramSize": "Expandir memória para 6GiB", "SettingsTabSystemIgnoreMissingServices": "Ignorar serviços não implementados", "SettingsTabGraphics": "Gráficos", "SettingsTabGraphicsAPI": "API gráfica", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Usar uma tabela de página via software para tradução de endereços. Maior precisão, porém performance mais baixa.", "MemoryManagerHostTooltip": "Mapeia memória no espaço de endereço hóspede diretamente. Compilação e execução do JIT muito mais rápida.", "MemoryManagerUnsafeTooltip": "Mapeia memória diretamente, mas sem limitar o acesso ao espaço de endereçamento do sistema convidado. Mais rápido, porém menos seguro. O aplicativo convidado pode acessar memória de qualquer parte do Ryujinx, então apenas rode programas em que você confia nesse modo.", - "DRamTooltip": "Expande a memória do sistema emulado de 4GB para 6GB", + "DRamTooltip": "Expande a memória do sistema emulado de 4GiB para 6GiB", "IgnoreMissingServicesTooltip": "Habilita ou desabilita a opção de ignorar serviços não implementados", "GraphicsBackendThreadingTooltip": "Habilita multithreading do backend gráfico", "GalThreadingTooltip": "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.", diff --git a/Ryujinx.Ava/Assets/Locales/ru_RU.json b/Ryujinx.Ava/Assets/Locales/ru_RU.json index 62835f0a8..e2788296c 100644 --- a/Ryujinx.Ava/Assets/Locales/ru_RU.json +++ b/Ryujinx.Ava/Assets/Locales/ru_RU.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Хаки", "SettingsTabSystemHacksNote": " (Эти многие настройки вызывают нестабильность)", - "SettingsTabSystemExpandDramSize": "Увеличение размера DRAM до 6GB", + "SettingsTabSystemExpandDramSize": "Увеличение размера DRAM до 6GiB", "SettingsTabSystemIgnoreMissingServices": "Игнорировать отсутствующие службы", "SettingsTabGraphics": "Графика", "SettingsTabGraphicsAPI": "Графические API", diff --git a/Ryujinx.Ava/Assets/Locales/tr_TR.json b/Ryujinx.Ava/Assets/Locales/tr_TR.json index dbdebad6a..59979de3c 100644 --- a/Ryujinx.Ava/Assets/Locales/tr_TR.json +++ b/Ryujinx.Ava/Assets/Locales/tr_TR.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacklar", "SettingsTabSystemHacksNote": " (Bunlar birçok dengesizlik oluşturabilir)", - "SettingsTabSystemExpandDramSize": "DRAM boyutunu 6GB'a genişlet", + "SettingsTabSystemExpandDramSize": "DRAM boyutunu 6GiB'a genişlet", "SettingsTabSystemIgnoreMissingServices": "Eksik Servisleri Görmezden Gel", "SettingsTabGraphics": "Grafikler", "SettingsTabGraphicsAPI": "Grafikler API", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Adres çevirisi için bir işlemci sayfası kullanır. En yüksek doğruluğu ve en yavaş performansı sunar.", "MemoryManagerHostTooltip": "Hafızayı doğrudan host adres aralığında tahsis eder. Çok daha hızlı JIT derleme ve işletimi sunar.", "MemoryManagerUnsafeTooltip": "Hafızayı doğrudan tahsis eder, ancak host aralığına erişimden önce adresi maskelemez. Daha iyi performansa karşılık emniyetten ödün verir. Misafir uygulama Ryujinx içerisinden istediği hafızaya erişebilir, bu sebeple bu seçenek ile sadece güvendiğiniz uygulamaları çalıştırın.", - "DRamTooltip": "Emüle edilen sistem hafızasını 4GB'dan 6GB'a yükseltir.\n\nBu seçenek yalnızca yüksek çözünürlük doku paketleri veya 4k çözünürlük modları için kullanılır. Performansı artırMAZ!\n\nEmin değilseniz devre dışı bırakın.", + "DRamTooltip": "Emüle edilen sistem hafızasını 4GiB'dan 6GiB'a yükseltir.\n\nBu seçenek yalnızca yüksek çözünürlük doku paketleri veya 4k çözünürlük modları için kullanılır. Performansı artırMAZ!\n\nEmin değilseniz devre dışı bırakın.", "IgnoreMissingServicesTooltip": "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.", "GraphicsBackendThreadingTooltip": "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.", "GalThreadingTooltip": "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.", diff --git a/Ryujinx.Ava/Assets/Locales/zh_CN.json b/Ryujinx.Ava/Assets/Locales/zh_CN.json index 09131e527..5e52f78ab 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_CN.json +++ b/Ryujinx.Ava/Assets/Locales/zh_CN.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "修正", "SettingsTabSystemHacksNote": " (会引起模拟器不稳定)", - "SettingsTabSystemExpandDramSize": "将模拟RAM大小扩展到 6GB", + "SettingsTabSystemExpandDramSize": "将模拟RAM大小扩展到 6GiB", "SettingsTabSystemIgnoreMissingServices": "忽略缺少的服务", "SettingsTabGraphics": "图形", "SettingsTabGraphicsAPI": "图形 API", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "使用软件内存页管理,最精确但是速度最慢", "MemoryManagerHostTooltip": "直接映射内存页到电脑内存,JIT效率高", "MemoryManagerUnsafeTooltip": "直接映射内存页,但不检查内存溢出,JIT效率最高。\nRyujinx可以访问任何位置的内存,因而相对不安全。\n此模式下只应运行您信任的游戏或软件(即官方游戏)", - "DRamTooltip": "扩展模拟的 Switch 内存为6GB。\n某些高清纹理MOD或4K MOD需要此选项", + "DRamTooltip": "扩展模拟的 Switch 内存为6GiB。\n某些高清纹理MOD或4K MOD需要此选项", "IgnoreMissingServicesTooltip": "开启后,游戏会忽略未实现的系统服务,从而继续运行。\n少部分新发布的游戏由于使用新的未知系统服务,可能需要此选项来避免闪退。\n模拟器更新完善系统服务之后,则无需开启选项。\n如您的游戏已经正常运行,请保持此选项关闭", "GraphicsBackendThreadingTooltip": "启用后端多线程", "GalThreadingTooltip": "使用模拟器内置的多线程优化,减少着色器编译的卡顿,并提高驱动程序的性能(尤其是缺失多线程的AMD)。\nNVIDIA显卡需要重启模拟器才能禁用驱动本身的线程优化,您也可以手动在控制面板将其禁用", @@ -583,7 +583,7 @@ "SettingsTabGraphicsBackend": "图形后端", "SettingsTabGraphicsBackendTooltip": "显卡使用的图形后端", "SettingsEnableTextureRecompression": "启用纹理重压缩", - "SettingsEnableTextureRecompressionTooltip": "压缩某些纹理以减少显存的使用。\n适合显存小于 4GB 的 GPU开启。\n如果您不确定,请保持此项为关闭。", + "SettingsEnableTextureRecompressionTooltip": "压缩某些纹理以减少显存的使用。\n适合显存小于 4GiB 的 GPU开启。\n如果您不确定,请保持此项为关闭。", "SettingsTabGraphicsPreferredGpu": "首选 GPU", "SettingsTabGraphicsPreferredGpuTooltip": "选择Vulkan API使用的显卡。\n此选项不会影响OpenGL API。\n如果您不确定,建议选择\"dGPU(独立显卡)\"。如果没有独立显卡,则无需改动此选项", "SettingsAppRequiredRestartMessage": "Ryujinx 需要重启", diff --git a/Ryujinx.Ava/Assets/Locales/zh_TW.json b/Ryujinx.Ava/Assets/Locales/zh_TW.json index 667c14005..6b1d9d49f 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "修正", "SettingsTabSystemHacksNote": " (會引起模擬器不穩定)", - "SettingsTabSystemExpandDramSize": "將模擬記憶體大小擴充至 6GB", + "SettingsTabSystemExpandDramSize": "將模擬記憶體大小擴充至 6GiB", "SettingsTabSystemIgnoreMissingServices": "忽略缺少的服務", "SettingsTabGraphics": "圖形", "SettingsTabGraphicsEnhancements": "增強", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "使用軟體記憶體頁管理,最精確但是速度最慢", "MemoryManagerHostTooltip": "直接映射記憶體頁到電腦記憶體,JIT效率高", "MemoryManagerUnsafeTooltip": "直接映射記憶體頁,但是不檢查記憶體溢出,JIT效率最高。\nRyujinx可以存取任何位置的記憶體,因而相對不安全。此模式下只應執行您信任的遊戲或軟體(即官方遊戲)", - "DRamTooltip": "擴展模擬的 Switch 記憶體為6GB,某些高畫質材質模組或 4K 模組需要此選項", + "DRamTooltip": "擴展模擬的 Switch 記憶體為6GiB,某些高畫質材質模組或 4K 模組需要此選項", "IgnoreMissingServicesTooltip": "忽略某些未實現的系統服務,少部分遊戲需要此選項才能啟動", "GraphicsBackendThreadingTooltip": "啟用後端多執行緒", "GalThreadingTooltip": "使用模擬器自帶的多執行緒調度,減少渲染器編譯的卡頓,並提高驅動程式的效能(尤其是缺失多執行緒的AMD)。\nNVIDIA使用者需要重啟模擬器才能停用驅動本身的多執行緒,否則您需手動執行停用獲得最佳效能", diff --git a/Ryujinx.Ava/Ui/Models/FileSizeSortComparer.cs b/Ryujinx.Ava/Ui/Models/FileSizeSortComparer.cs index 4005f3285..d4ac1412f 100644 --- a/Ryujinx.Ava/Ui/Models/FileSizeSortComparer.cs +++ b/Ryujinx.Ava/Ui/Models/FileSizeSortComparer.cs @@ -10,22 +10,22 @@ namespace Ryujinx.Ava.Ui.Models string aValue = (x as ApplicationData).TimePlayed; string bValue = (y as ApplicationData).TimePlayed; - if (aValue[^2..] == "GB") + if (aValue[^3..] == "GiB") { - aValue = (float.Parse(aValue[0..^2]) * 1024).ToString(); + aValue = (float.Parse(aValue[0..^3]) * 1024).ToString(); } else { - aValue = aValue[0..^2]; + aValue = aValue[0..^3]; } - if (bValue[^2..] == "GB") + if (bValue[^3..] == "GiB") { - bValue = (float.Parse(bValue[0..^2]) * 1024).ToString(); + bValue = (float.Parse(bValue[0..^3]) * 1024).ToString(); } else { - bValue = bValue[0..^2]; + bValue = bValue[0..^3]; } if (float.Parse(aValue) > float.Parse(bValue)) diff --git a/Ryujinx.Ava/Ui/Models/Generic/FileSizeSortComparer.cs b/Ryujinx.Ava/Ui/Models/Generic/FileSizeSortComparer.cs index 174098352..08b1754c2 100644 --- a/Ryujinx.Ava/Ui/Models/Generic/FileSizeSortComparer.cs +++ b/Ryujinx.Ava/Ui/Models/Generic/FileSizeSortComparer.cs @@ -15,22 +15,22 @@ namespace Ryujinx.Ava.Ui.Models.Generic string aValue = x.FileSize; string bValue = y.FileSize; - if (aValue[^2..] == "GB") + if (aValue[^3..] == "GiB") { - aValue = (float.Parse(aValue[0..^2]) * 1024).ToString(); + aValue = (float.Parse(aValue[0..^3]) * 1024).ToString(); } else { - aValue = aValue[0..^2]; + aValue = aValue[0..^3]; } - if (bValue[^2..] == "GB") + if (bValue[^3..] == "GiB") { - bValue = (float.Parse(bValue[0..^2]) * 1024).ToString(); + bValue = (float.Parse(bValue[0..^3]) * 1024).ToString(); } else { - bValue = bValue[0..^2]; + bValue = bValue[0..^3]; } if (float.Parse(aValue) > float.Parse(bValue)) diff --git a/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs b/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs index 069cd5aa2..cd4a3d821 100644 --- a/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs +++ b/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs @@ -36,13 +36,13 @@ namespace Ryujinx.Common.SystemInfo ParseKeyValues("/proc/meminfo", memDict); - // Entries are in KB - ulong.TryParse(memDict["MemTotal"]?.Split(' ')[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong totalKB); - ulong.TryParse(memDict["MemAvailable"]?.Split(' ')[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong availableKB); + // Entries are in KiB + ulong.TryParse(memDict["MemTotal"]?.Split(' ')[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong totalKiB); + ulong.TryParse(memDict["MemAvailable"]?.Split(' ')[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong availableKiB); CpuName = $"{cpuName} ; {LogicalCoreCount} logical"; - RamTotal = totalKB * 1024; - RamAvailable = availableKB * 1024; + RamTotal = totalKiB * 1024; + RamAvailable = availableKiB * 1024; } private static void ParseKeyValues(string filePath, Dictionary itemDict) diff --git a/Ryujinx.Common/SystemInfo/SystemInfo.cs b/Ryujinx.Common/SystemInfo/SystemInfo.cs index 98f520bc1..1db72d9b9 100644 --- a/Ryujinx.Common/SystemInfo/SystemInfo.cs +++ b/Ryujinx.Common/SystemInfo/SystemInfo.cs @@ -20,13 +20,13 @@ namespace Ryujinx.Common.SystemInfo CpuName = "Unknown"; } - private static string ToMBString(ulong bytesValue) => (bytesValue == 0) ? "Unknown" : $"{bytesValue / 1024 / 1024} MB"; + private static string ToMiBString(ulong bytesValue) => (bytesValue == 0) ? "Unknown" : $"{bytesValue / 1024 / 1024} MiB"; public void Print() { Logger.Notice.Print(LogClass.Application, $"Operating System: {OsDescription}"); Logger.Notice.Print(LogClass.Application, $"CPU: {CpuName}"); - Logger.Notice.Print(LogClass.Application, $"RAM: Total {ToMBString(RamTotal)} ; Available {ToMBString(RamAvailable)}"); + Logger.Notice.Print(LogClass.Application, $"RAM: Total {ToMiBString(RamTotal)} ; Available {ToMiBString(RamAvailable)}"); } public static SystemInfo Gather() diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 1ad9f3e6e..36f2b1b2a 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan { class PipelineFull : PipelineBase, IPipeline { - private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MB + private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB private readonly List _activeQueries; private CounterQueueEvent _activeConditionalRender; diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs index 881421424..8a727c30c 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs @@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { static class KSystemControl { - private const ulong Kb = 1024; - private const ulong Mb = 1024 * Kb; - private const ulong Gb = 1024 * Mb; + private const ulong KiB = 1024; + private const ulong MiB = 1024 * KiB; + private const ulong GiB = 1024 * MiB; - private const ulong PageSize = 4 * Kb; + private const ulong PageSize = 4 * KiB; private const ulong RequiredNonSecureSystemPoolSizeVi = 0x2238 * PageSize; private const ulong RequiredNonSecureSystemPoolSizeNvservices = 0x710 * PageSize; @@ -24,12 +24,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { return arrange switch { - MemoryArrange.MemoryArrange4GB or - MemoryArrange.MemoryArrange4GBSystemDev or - MemoryArrange.MemoryArrange6GBAppletDev => 3285 * Mb, - MemoryArrange.MemoryArrange4GBAppletDev => 2048 * Mb, - MemoryArrange.MemoryArrange6GB or - MemoryArrange.MemoryArrange8GB => 4916 * Mb, + MemoryArrange.MemoryArrange4GiB or + MemoryArrange.MemoryArrange4GiBSystemDev or + MemoryArrange.MemoryArrange6GiBAppletDev => 3285 * MiB, + MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB, + MemoryArrange.MemoryArrange6GiB or + MemoryArrange.MemoryArrange8GiB => 4916 * MiB, _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".") }; } @@ -38,12 +38,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { return arrange switch { - MemoryArrange.MemoryArrange4GB => 507 * Mb, - MemoryArrange.MemoryArrange4GBAppletDev => 1554 * Mb, - MemoryArrange.MemoryArrange4GBSystemDev => 448 * Mb, - MemoryArrange.MemoryArrange6GB => 562 * Mb, - MemoryArrange.MemoryArrange6GBAppletDev or - MemoryArrange.MemoryArrange8GB => 2193 * Mb, + MemoryArrange.MemoryArrange4GiB => 507 * MiB, + MemoryArrange.MemoryArrange4GiBAppletDev => 1554 * MiB, + MemoryArrange.MemoryArrange4GiBSystemDev => 448 * MiB, + MemoryArrange.MemoryArrange6GiB => 562 * MiB, + MemoryArrange.MemoryArrange6GiBAppletDev or + MemoryArrange.MemoryArrange8GiB => 2193 * MiB, _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".") }; } @@ -68,9 +68,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { return size switch { - MemorySize.MemorySize4GB => 4 * Gb, - MemorySize.MemorySize6GB => 6 * Gb, - MemorySize.MemorySize8GB => 8 * Gb, + MemorySize.MemorySize4GiB => 4 * GiB, + MemorySize.MemorySize6GiB => 6 * GiB, + MemorySize.MemorySize8GiB => 8 * GiB, _ => throw new ArgumentException($"Invalid memory size \"{size}\".") }; } diff --git a/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs b/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs index 136b0240b..d2bcfd62e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs @@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { enum MemoryArrange : byte { - MemoryArrange4GB, - MemoryArrange4GBAppletDev, - MemoryArrange4GBSystemDev, - MemoryArrange6GB, - MemoryArrange6GBAppletDev, - MemoryArrange8GB + MemoryArrange4GiB, + MemoryArrange4GiBAppletDev, + MemoryArrange4GiBSystemDev, + MemoryArrange6GiB, + MemoryArrange6GiBAppletDev, + MemoryArrange8GiB } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs b/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs index 1148b0f40..159385b63 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { enum MemorySize : byte { - MemorySize4GB = 0, - MemorySize6GB = 1, - MemorySize8GB = 2 + MemorySize4GiB = 0, + MemorySize6GiB = 1, + MemorySize8GiB = 2 } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index 3314002c6..a1ad1c941 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu private static readonly uint[] _pageSizes = new uint[] { SmallPageSize, BigPageSize }; - private const ulong SmallRegionLimit = 0x400000000UL; // 16 GB + private const ulong SmallRegionLimit = 0x400000000UL; // 16 GiB private const ulong DefaultUserSize = 1UL << 37; private struct VmRegion diff --git a/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs b/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs index 53c9eb486..30e22e120 100644 --- a/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs @@ -67,17 +67,17 @@ namespace Ryujinx.HLE.HOS.Services.Spl configValue = 0; break; case ConfigItem.DramId: - if (memorySize == MemorySize.MemorySize8GB) + if (memorySize == MemorySize.MemorySize8GiB) { - configValue = (ulong)DramId.IowaSamsung8GB; + configValue = (ulong)DramId.IowaSamsung8GiB; } - else if (memorySize == MemorySize.MemorySize6GB) + else if (memorySize == MemorySize.MemorySize6GiB) { - configValue = (ulong)DramId.IcosaSamsung6GB; + configValue = (ulong)DramId.IcosaSamsung6GiB; } else { - configValue = (ulong)DramId.IcosaSamsung4GB; + configValue = (ulong)DramId.IcosaSamsung4GiB; } break; case ConfigItem.SecurityEngineInterruptNumber: diff --git a/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs b/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs index 4e1d1f0e1..422c8d69f 100644 --- a/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs +++ b/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs @@ -2,34 +2,34 @@ { enum DramId { - IcosaSamsung4GB, - IcosaHynix4GB, - IcosaMicron4GB, - IowaHynix1y4GB, - IcosaSamsung6GB, - HoagHynix1y4GB, - AulaHynix1y4GB, - IowaX1X2Samsung4GB, - IowaSansung4GB, - IowaSamsung8GB, - IowaHynix4GB, - IowaMicron4GB, - HoagSamsung4GB, - HoagSamsung8GB, - HoagHynix4GB, - HoagMicron4GB, - IowaSamsung4GBY, - IowaSamsung1y4GBX, - IowaSamsung1y8GBX, - HoagSamsung1y4GBX, - IowaSamsung1y4GBY, - IowaSamsung1y8GBY, - AulaSamsung1y4GB, - HoagSamsung1y8GBX, - AulaSamsung1y4GBX, - IowaMicron1y4GB, - HoagMicron1y4GB, - AulaMicron1y4GB, - AulaSamsung1y8GBX + IcosaSamsung4GiB, + IcosaHynix4GiB, + IcosaMicron4GiB, + IowaHynix1y4GiB, + IcosaSamsung6GiB, + HoagHynix1y4GiB, + AulaHynix1y4GiB, + IowaX1X2Samsung4GiB, + IowaSansung4GiB, + IowaSamsung8GiB, + IowaHynix4GiB, + IowaMicron4GiB, + HoagSamsung4GiB, + HoagSamsung8GiB, + HoagHynix4GiB, + HoagMicron4GiB, + IowaSamsung4GiBY, + IowaSamsung1y4GiBX, + IowaSamsung1y8GiBX, + HoagSamsung1y4GiBX, + IowaSamsung1y4GiBY, + IowaSamsung1y8GiBY, + AulaSamsung1y4GiB, + HoagSamsung1y8GiBX, + AulaSamsung1y4GiBX, + IowaMicron1y4GiB, + HoagMicron1y4GiB, + AulaMicron1y4GiB, + AulaSamsung1y8GiBX } } \ No newline at end of file diff --git a/Ryujinx.HLE/MemoryConfiguration.cs b/Ryujinx.HLE/MemoryConfiguration.cs index e56658f81..25044bb5f 100644 --- a/Ryujinx.HLE/MemoryConfiguration.cs +++ b/Ryujinx.HLE/MemoryConfiguration.cs @@ -5,28 +5,28 @@ namespace Ryujinx.HLE { public enum MemoryConfiguration { - MemoryConfiguration4GB = 0, - MemoryConfiguration4GBAppletDev = 1, - MemoryConfiguration4GBSystemDev = 2, - MemoryConfiguration6GB = 3, - MemoryConfiguration6GBAppletDev = 4, - MemoryConfiguration8GB = 5 + MemoryConfiguration4GiB = 0, + MemoryConfiguration4GiBAppletDev = 1, + MemoryConfiguration4GiBSystemDev = 2, + MemoryConfiguration6GiB = 3, + MemoryConfiguration6GiBAppletDev = 4, + MemoryConfiguration8GiB = 5 } static class MemoryConfigurationExtensions { - private const ulong Gb = 1024 * 1024 * 1024; + private const ulong GiB = 1024 * 1024 * 1024; public static MemoryArrange ToKernelMemoryArrange(this MemoryConfiguration configuration) { return configuration switch { - MemoryConfiguration.MemoryConfiguration4GB => MemoryArrange.MemoryArrange4GB, - MemoryConfiguration.MemoryConfiguration4GBAppletDev => MemoryArrange.MemoryArrange4GBAppletDev, - MemoryConfiguration.MemoryConfiguration4GBSystemDev => MemoryArrange.MemoryArrange4GBSystemDev, - MemoryConfiguration.MemoryConfiguration6GB => MemoryArrange.MemoryArrange6GB, - MemoryConfiguration.MemoryConfiguration6GBAppletDev => MemoryArrange.MemoryArrange6GBAppletDev, - MemoryConfiguration.MemoryConfiguration8GB => MemoryArrange.MemoryArrange8GB, + MemoryConfiguration.MemoryConfiguration4GiB => MemoryArrange.MemoryArrange4GiB, + MemoryConfiguration.MemoryConfiguration4GiBAppletDev => MemoryArrange.MemoryArrange4GiBAppletDev, + MemoryConfiguration.MemoryConfiguration4GiBSystemDev => MemoryArrange.MemoryArrange4GiBSystemDev, + MemoryConfiguration.MemoryConfiguration6GiB => MemoryArrange.MemoryArrange6GiB, + MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemoryArrange.MemoryArrange6GiBAppletDev, + MemoryConfiguration.MemoryConfiguration8GiB => MemoryArrange.MemoryArrange8GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".") }; } @@ -35,12 +35,12 @@ namespace Ryujinx.HLE { return configuration switch { - MemoryConfiguration.MemoryConfiguration4GB or - MemoryConfiguration.MemoryConfiguration4GBAppletDev or - MemoryConfiguration.MemoryConfiguration4GBSystemDev => MemorySize.MemorySize4GB, - MemoryConfiguration.MemoryConfiguration6GB or - MemoryConfiguration.MemoryConfiguration6GBAppletDev => MemorySize.MemorySize6GB, - MemoryConfiguration.MemoryConfiguration8GB => MemorySize.MemorySize8GB, + MemoryConfiguration.MemoryConfiguration4GiB or + MemoryConfiguration.MemoryConfiguration4GiBAppletDev or + MemoryConfiguration.MemoryConfiguration4GiBSystemDev => MemorySize.MemorySize4GiB, + MemoryConfiguration.MemoryConfiguration6GiB or + MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemorySize.MemorySize6GiB, + MemoryConfiguration.MemoryConfiguration8GiB => MemorySize.MemorySize8GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".") }; } @@ -49,12 +49,12 @@ namespace Ryujinx.HLE { return configuration switch { - MemoryConfiguration.MemoryConfiguration4GB or - MemoryConfiguration.MemoryConfiguration4GBAppletDev or - MemoryConfiguration.MemoryConfiguration4GBSystemDev => 4 * Gb, - MemoryConfiguration.MemoryConfiguration6GB or - MemoryConfiguration.MemoryConfiguration6GBAppletDev => 6 * Gb, - MemoryConfiguration.MemoryConfiguration8GB => 8 * Gb, + MemoryConfiguration.MemoryConfiguration4GiB or + MemoryConfiguration.MemoryConfiguration4GiBAppletDev or + MemoryConfiguration.MemoryConfiguration4GiBSystemDev => 4 * GiB, + MemoryConfiguration.MemoryConfiguration6GiB or + MemoryConfiguration.MemoryConfiguration6GiBAppletDev => 6 * GiB, + MemoryConfiguration.MemoryConfiguration8GiB => 8 * GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".") }; } diff --git a/Ryujinx.Headless.SDL2/Options.cs b/Ryujinx.Headless.SDL2/Options.cs index 956941798..209ce2288 100644 --- a/Ryujinx.Headless.SDL2/Options.cs +++ b/Ryujinx.Headless.SDL2/Options.cs @@ -175,7 +175,7 @@ namespace Ryujinx.Headless.SDL2 // Hacks - [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GB to 6GB.")] + [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")] public bool? ExpandRam { get; set; } [Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 5c24095e7..168e826f5 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -510,7 +510,7 @@ namespace Ryujinx.Headless.SDL2 _userChannelPersistence, renderer, new SDL2HardwareDeviceDriver(), - (bool)options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GB : MemoryConfiguration.MemoryConfiguration4GB, + (bool)options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, window, options.SystemLanguage, options.SystemRegion, diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 7f1f692c7..9a6c1997e 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -464,7 +464,7 @@ namespace Ryujinx.Ui.App.Common TimePlayed = ConvertSecondsToReadableString(appMetadata.TimePlayed), LastPlayed = appMetadata.LastPlayed, FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1), - FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + "MB" : fileSize.ToString("0.##") + "GB", + FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + "MiB" : fileSize.ToString("0.##") + "GiB", Path = applicationPath, ControlHolder = controlHolder }; diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index acfcdcb46..52ca71538 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -207,7 +207,7 @@ namespace Ryujinx.Ui.Common.Configuration public MemoryManagerMode MemoryManagerMode { get; set; } /// - /// Expands the RAM amount on the emulated system from 4GB to 6GB + /// Expands the RAM amount on the emulated system from 4GiB to 6GiB /// public bool ExpandRam { get; set; } diff --git a/Ryujinx/Ui/Helper/SortHelper.cs b/Ryujinx/Ui/Helper/SortHelper.cs index 78917fe7c..33ae1bb28 100644 --- a/Ryujinx/Ui/Helper/SortHelper.cs +++ b/Ryujinx/Ui/Helper/SortHelper.cs @@ -81,22 +81,22 @@ namespace Ryujinx.Ui.Helper string aValue = model.GetValue(a, 8).ToString(); string bValue = model.GetValue(b, 8).ToString(); - if (aValue[^2..] == "GB") + if (aValue[^3..] == "GiB") { - aValue = (float.Parse(aValue[0..^2]) * 1024).ToString(); + aValue = (float.Parse(aValue[0..^3]) * 1024).ToString(); } else { - aValue = aValue[0..^2]; + aValue = aValue[0..^3]; } - if (bValue[^2..] == "GB") + if (bValue[^3..] == "GiB") { - bValue = (float.Parse(bValue[0..^2]) * 1024).ToString(); + bValue = (float.Parse(bValue[0..^3]) * 1024).ToString(); } else { - bValue = bValue[0..^2]; + bValue = bValue[0..^3]; } if (float.Parse(aValue) > float.Parse(bValue)) diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 7b8b6a98d..802a86ece 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -549,8 +549,8 @@ namespace Ryujinx.Ui } var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value - ? HLE.MemoryConfiguration.MemoryConfiguration6GB - : HLE.MemoryConfiguration.MemoryConfiguration4GB; + ? HLE.MemoryConfiguration.MemoryConfiguration6GiB + : HLE.MemoryConfiguration.MemoryConfiguration4GiB; IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade index 88e6dae59..c2ffe5b1d 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.glade +++ b/Ryujinx/Ui/Windows/SettingsWindow.glade @@ -1749,11 +1749,11 @@ vertical - Expand DRAM Size to 6GB + Expand DRAM Size to 6GiB True True False - Increases the amount of memory on the emulated system from 4GB to 6GB. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure. + Increases the amount of memory on the emulated system from 4GiB to 6GiB. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure. start 5 5 From 2c9ab5e45fd45d45909b9b348580c44bdfc4d36f Mon Sep 17 00:00:00 2001 From: Logan Stromberg Date: Wed, 16 Nov 2022 14:53:17 -0800 Subject: [PATCH 075/737] Prevent raw Unicode control codes from showing on software keyboard applet. (#3845) * Revert "Add support for releasing a semaphore to DmaClass (#2926)" This reverts commit 521a07e6125d3a5d9781512639387a9be5f09107. * Revert "Revert "Add support for releasing a semaphore to DmaClass (#2926)"" This reverts commit ec8a5fd05362f04cc77436ee3e45a9188777f75e. * Strip non-visible control codes from strings before they are sent to the software keyboard to prevent ugly unicode blocks from being shown on the UI. * remove debugging junk * Initialize stringbuilder capacity at the start to prevent resizing (a tiny tiny microoptimization) * Update remarks documentation. Remove unneeded imports. * Removing a test that's actually just redundant Co-authored-by: Logan Stromberg --- Ryujinx.HLE/AssemblyInfo.cs | 3 + .../SoftwareKeyboardApplet.cs | 42 +++++++++-- Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs | 71 +++++++++++++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 Ryujinx.HLE/AssemblyInfo.cs create mode 100644 Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs diff --git a/Ryujinx.HLE/AssemblyInfo.cs b/Ryujinx.HLE/AssemblyInfo.cs new file mode 100644 index 000000000..9d7bad6be --- /dev/null +++ b/Ryujinx.HLE/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Ryujinx.Tests")] \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 3cfd192c7..e287318a6 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -204,12 +204,11 @@ namespace Ryujinx.HLE.HOS.Applets else { // Call the configured GUI handler to get user's input. - var args = new SoftwareKeyboardUiArgs { - HeaderText = _keyboardForegroundConfig.HeaderText, - SubtitleText = _keyboardForegroundConfig.SubtitleText, - GuideText = _keyboardForegroundConfig.GuideText, + HeaderText = StripUnicodeControlCodes(_keyboardForegroundConfig.HeaderText), + SubtitleText = StripUnicodeControlCodes(_keyboardForegroundConfig.SubtitleText), + GuideText = StripUnicodeControlCodes(_keyboardForegroundConfig.GuideText), SubmitText = (!string.IsNullOrWhiteSpace(_keyboardForegroundConfig.SubmitText) ? _keyboardForegroundConfig.SubmitText : "OK"), StringLengthMin = _keyboardForegroundConfig.StringLengthMin, @@ -764,6 +763,41 @@ namespace Ryujinx.HLE.HOS.Applets } } + /// + /// Removes all Unicode control code characters from the input string. + /// This includes CR/LF, tabs, null characters, escape characters, + /// and special control codes which are used for formatting by the real keyboard applet. + /// + /// + /// Some games send special control codes (such as 0x13 "Device Control 3") as part of the string. + /// Future implementations of the emulated keyboard applet will need to handle these as well. + /// + /// The input string to sanitize (may be null). + /// The sanitized string. + internal static string StripUnicodeControlCodes(string input) + { + if (input is null) + { + return null; + } + + if (input.Length == 0) + { + return string.Empty; + } + + StringBuilder sb = new StringBuilder(capacity: input.Length); + foreach (char c in input) + { + if (!char.IsControl(c)) + { + sb.Append(c); + } + } + + return sb.ToString(); + } + private static T ReadStruct(byte[] data) where T : struct { diff --git a/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs b/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs new file mode 100644 index 000000000..d16039ad3 --- /dev/null +++ b/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs @@ -0,0 +1,71 @@ +using NUnit.Framework; +using Ryujinx.HLE.HOS.Applets; +using System.Text; + +namespace Ryujinx.Tests.HLE +{ + public class SoftwareKeyboardTests + { + [Test] + public void StripUnicodeControlCodes_NullInput() + { + Assert.IsNull(SoftwareKeyboardApplet.StripUnicodeControlCodes(null)); + } + + [Test] + public void StripUnicodeControlCodes_EmptyInput() + { + Assert.AreEqual(string.Empty, SoftwareKeyboardApplet.StripUnicodeControlCodes(string.Empty)); + } + + [Test] + public void StripUnicodeControlCodes_Passthrough() + { + string[] prompts = new string[] + { + "Please name him.", + "Name her, too.", + "Name your friend.", + "Name another friend.", + "Name your pet.", + "Favorite homemade food?", + "What’s your favorite thing?", + "Are you sure?", + }; + + foreach (string prompt in prompts) + { + Assert.AreEqual(prompt, SoftwareKeyboardApplet.StripUnicodeControlCodes(prompt)); + } + } + + [Test] + public void StripUnicodeControlCodes_StripsNewlines() + { + Assert.AreEqual("I am very tall", SoftwareKeyboardApplet.StripUnicodeControlCodes("I \r\nam \r\nvery \r\ntall")); + } + + [Test] + public void StripUnicodeControlCodes_StripsDeviceControls() + { + // 0x13 is control code DC3 used by some games + string specialInput = Encoding.UTF8.GetString(new byte[] { 0x13, 0x53, 0x68, 0x69, 0x6E, 0x65, 0x13 }); + Assert.AreEqual("Shine", SoftwareKeyboardApplet.StripUnicodeControlCodes(specialInput)); + } + + [Test] + public void StripUnicodeControlCodes_StripsToEmptyString() + { + string specialInput = Encoding.UTF8.GetString(new byte[] { 17, 18, 19, 20 }); // DC1 - DC4 special codes + Assert.AreEqual(string.Empty, SoftwareKeyboardApplet.StripUnicodeControlCodes(specialInput)); + } + + [Test] + public void StripUnicodeControlCodes_PreservesMultiCodePoints() + { + // Turtles are a good example of multi-codepoint Unicode chars + string specialInput = "♀ 🐢 🐢 ♂ "; + Assert.AreEqual(specialInput, SoftwareKeyboardApplet.StripUnicodeControlCodes(specialInput)); + } + } +} From 5d73a9f5fce23b030821f5fb2d71855099f14ec2 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 17 Nov 2022 00:18:29 +0100 Subject: [PATCH 076/737] Fix Fedora support (#3815) For some reasons, my fresh installation of Fedora 36 (KDE) doesn't have a symlink for libX11.so. This commit fixes this by trying to import the library with its major version or fallback to the normal way. --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- Ryujinx/Program.cs | 30 +++++++++++++++++++++++++++++- Ryujinx/Ryujinx.csproj | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 3b4d8cf02..d36fa6298 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -36,7 +36,7 @@ - + diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index a91f9aa52..be790a48f 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -29,7 +29,11 @@ namespace Ryujinx public static string ConfigurationPath { get; set; } - [DllImport("libX11")] + public static string CommandLineProfile { get; set; } + + private const string X11LibraryName = "libX11"; + + [DllImport(X11LibraryName)] private extern static int XInitThreads(); [DllImport("user32.dll", SetLastError = true)] @@ -37,6 +41,30 @@ namespace Ryujinx private const uint MB_ICONWARNING = 0x30; + static Program() + { + if (OperatingSystem.IsLinux()) + { + NativeLibrary.SetDllImportResolver(typeof(Program).Assembly, (name, assembly, path) => + { + if (name != X11LibraryName) + { + return IntPtr.Zero; + } + + if (!NativeLibrary.TryLoad("libX11.so.6", assembly, path, out IntPtr result)) + { + if (!NativeLibrary.TryLoad("libX11.so", assembly, path, out result)) + { + return IntPtr.Zero; + } + } + + return result; + }); + } + } + static void Main(string[] args) { Version = ReleaseInformations.GetVersion(); diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 23f5d1559..58b1555cf 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -25,7 +25,7 @@ - + From 55043c8afcf3803b091c2770d7f528bbeb238f68 Mon Sep 17 00:00:00 2001 From: Alberto Fanjul Date: Thu, 17 Nov 2022 13:02:43 +0100 Subject: [PATCH 077/737] Allow to start Ryujinx in wayland environment (#3516) PrimaryMonitor is only available on X11 At some point it will be deprecated, this change support wayland --- Ryujinx/Ui/MainWindow.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 802a86ece..3f261f4d0 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -148,10 +148,10 @@ namespace Ryujinx.Ui // Apply custom theme if needed. ThemeHelper.ApplyTheme(); - + Gdk.Monitor monitor = Display.GetMonitor(0); // Sets overridden fields. - int monitorWidth = Display.PrimaryMonitor.Geometry.Width * Display.PrimaryMonitor.ScaleFactor; - int monitorHeight = Display.PrimaryMonitor.Geometry.Height * Display.PrimaryMonitor.ScaleFactor; + int monitorWidth = monitor.Geometry.Width * monitor.ScaleFactor; + int monitorHeight = monitor.Geometry.Height * monitor.ScaleFactor; DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280; DefaultHeight = monitorHeight < 760 ? monitorHeight : 760; From b5cf8b8af9a8316a32322c8a7dc6d1798490e241 Mon Sep 17 00:00:00 2001 From: Matthew Wells <91291346+richarm4@users.noreply.github.com> Date: Thu, 17 Nov 2022 09:13:37 -0800 Subject: [PATCH 078/737] Capitalization to be consistent (#3860) Thread ID Register, Floating-point Control Register, and Floating-point Status Register all had Register capitalized, so the Register in Processor State register should be capitalized. --- Ryujinx.Cpu/IExecutionContext.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Cpu/IExecutionContext.cs b/Ryujinx.Cpu/IExecutionContext.cs index 3455b5c1e..c38210800 100644 --- a/Ryujinx.Cpu/IExecutionContext.cs +++ b/Ryujinx.Cpu/IExecutionContext.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Cpu long TpidrroEl0 { get; set; } /// - /// Processor State register. + /// Processor State Register. /// uint Pstate { get; set; } @@ -109,4 +109,4 @@ namespace Ryujinx.Cpu /// void StopRunning(); } -} \ No newline at end of file +} From 391e08dd27661b72674f91450ac00d1363938251 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 17 Nov 2022 18:30:54 +0100 Subject: [PATCH 079/737] ci: Clean up Actions leftovers (#3859) As title say. Fix Avalonia build versions for PRs. Also ensure that the --self-contained doesn't warn at build. --- .github/workflows/build.yml | 12 ++++-------- .github/workflows/release.yml | 16 ++++++---------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0970d7288..ac19f717a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,26 +52,22 @@ jobs: - uses: actions/setup-dotnet@v3 with: dotnet-version: 7.0.x - - name: Ensure NuGet Source - uses: fabriciomurta/ensure-nuget-source@v1 - name: Get git short hash id: git_short_hash run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT shell: bash - - name: Clear - run: dotnet clean && dotnet nuget locals all --clear - name: Build - run: dotnet build -c "${{ matrix.configuration }}" /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER + run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER - name: Test run: dotnet test --no-build -c "${{ matrix.configuration }}" - name: Publish Ryujinx - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained true if: github.event_name == 'pull_request' - name: Publish Ryujinx.Headless.SDL2 - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained true if: github.event_name == 'pull_request' - name: Publish Ryujinx.Ava - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true if: github.event_name == 'pull_request' - name: Upload Ryujinx artifact uses: actions/upload-artifact@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b24ce3f6..741995cde 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,10 +29,6 @@ jobs: - uses: actions/setup-dotnet@v3 with: dotnet-version: 7.0.x - - name: Ensure NuGet Source - uses: fabriciomurta/ensure-nuget-source@v1 - - name: Clear - run: dotnet clean && dotnet nuget locals all --clear - name: Get version info id: version_info run: | @@ -51,9 +47,9 @@ jobs: run: "mkdir release_output" - name: Publish Windows run: | - dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained - dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained - dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained + dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true + dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true + dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true - name: Packing Windows builds run: | pushd publish_windows @@ -71,9 +67,9 @@ jobs: - name: Publish Linux run: | - dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained - dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained - dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained + dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true + dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true + dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true - name: Packing Linux builds run: | From 33a4d7d1badbebd2dc05114ef17c85678baed843 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 17 Nov 2022 17:47:41 +0000 Subject: [PATCH 080/737] GPU: Eliminate CB0 accesses when storage buffer accesses are resolved (#3847) * Eliminate CB0 accesses Still some work to do, decouple from hle? * Forgot the important part somehow * Fix and improve alignment test * Address Feedback * Remove some complexity when checking storage buffer alignment * Update Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs Co-authored-by: gdkchan Co-authored-by: gdkchan --- Ryujinx.Graphics.Gpu/Constants.cs | 5 + .../Engine/Compute/ComputeClass.cs | 45 ++-- .../Engine/Threed/StateUpdater.cs | 8 +- Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 46 +++- .../Shader/ComputeShaderCacheHashTable.cs | 4 +- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 6 + .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 6 + .../Shader/GpuChannelComputeState.cs | 10 +- .../Shader/GpuChannelGraphicsState.cs | 10 +- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 8 +- .../Shader/ShaderSpecializationList.cs | 5 +- .../Shader/ShaderSpecializationState.cs | 13 +- Ryujinx.Graphics.Shader/Constants.cs | 2 + Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 + .../Optimizations/GlobalToStorage.cs | 206 ++++++++++++++---- 16 files changed, 317 insertions(+), 68 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Constants.cs b/Ryujinx.Graphics.Gpu/Constants.cs index d580049f2..1897f5d0f 100644 --- a/Ryujinx.Graphics.Gpu/Constants.cs +++ b/Ryujinx.Graphics.Gpu/Constants.cs @@ -95,5 +95,10 @@ namespace Ryujinx.Graphics.Gpu /// Byte alignment for block linear textures /// public const int GobAlignment = 64; + + /// + /// Expected byte alignment for storage buffers + /// + public const int StorageAlignment = 16; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index bc2911748..cd509471e 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -138,7 +138,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute qmd.CtaThreadDimension1, qmd.CtaThreadDimension2, localMemorySize, - sharedMemorySize); + sharedMemorySize, + _channel.BufferManager.HasUnalignedStorageBuffers); CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); @@ -150,6 +151,33 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute ShaderProgramInfo info = cs.Shaders[0].Info; + bool hasUnaligned = _channel.BufferManager.HasUnalignedStorageBuffers; + + for (int index = 0; index < info.SBuffers.Count; index++) + { + BufferDescriptor sb = info.SBuffers[index]; + + ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0); + + int sbDescOffset = 0x310 + sb.Slot * 0x10; + + sbDescAddress += (ulong)sbDescOffset; + + SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read(sbDescAddress); + + _channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); + } + + if ((_channel.BufferManager.HasUnalignedStorageBuffers) != hasUnaligned) + { + // Refetch the shader, as assumptions about storage buffer alignment have changed. + cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); + + _context.Renderer.Pipeline.SetProgram(cs.HostProgram); + + info = cs.Shaders[0].Info; + } + for (int index = 0; index < info.CBuffers.Count; index++) { BufferDescriptor cb = info.CBuffers[index]; @@ -174,21 +202,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute _channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size); } - for (int index = 0; index < info.SBuffers.Count; index++) - { - BufferDescriptor sb = info.SBuffers[index]; - - ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0); - - int sbDescOffset = 0x310 + sb.Slot * 0x10; - - sbDescAddress += (ulong)sbDescOffset; - - SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read(sbDescAddress); - - _channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); - } - _channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers); _channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers); diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 3f71172c0..d51077dc7 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -293,9 +293,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// private void CommitBindings() { + var buffers = _channel.BufferManager; + var hasUnaligned = buffers.HasUnalignedStorageBuffers; + UpdateStorageBuffers(); - if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState)) + if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState) || (buffers.HasUnalignedStorageBuffers != hasUnaligned)) { // Shader must be reloaded. UpdateShaderState(); @@ -1361,7 +1364,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.AlphaTestFunc, _state.State.AlphaTestRef, ref attributeTypes, - _drawState.HasConstantBufferDrawParameters); + _drawState.HasConstantBufferDrawParameters, + _channel.BufferManager.HasUnalignedStorageBuffers); } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 9f1f88b1e..1b67f6507 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -17,6 +17,9 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly GpuContext _context; private readonly GpuChannel _channel; + private int _unalignedStorageBuffers; + public bool HasUnalignedStorageBuffers => _unalignedStorageBuffers > 0; + private IndexBuffer _indexBuffer; private readonly VertexBuffer[] _vertexBuffers; private readonly BufferBounds[] _transformFeedbackBuffers; @@ -38,6 +41,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public BufferBounds[] Buffers { get; } + /// + /// Flag indicating if this binding is unaligned. + /// + public bool[] Unaligned { get; } + /// /// Total amount of buffers used on the shader. /// @@ -51,6 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { Bindings = new BufferDescriptor[count]; Buffers = new BufferBounds[count]; + Unaligned = new bool[count]; } /// @@ -202,6 +211,31 @@ namespace Ryujinx.Graphics.Gpu.Memory _transformFeedbackBuffersDirty = true; } + /// + /// Records the alignment of a storage buffer. + /// Unaligned storage buffers disable some optimizations on the shader. + /// + /// The binding list to modify + /// Index of the storage buffer + /// Start GPU virtual address of the buffer + private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa) + { + bool unaligned = (gpuVa & (Constants.StorageAlignment - 1)) != 0; + + if (unaligned || HasUnalignedStorageBuffers) + { + // Check if the alignment changed for this binding. + + ref bool currentUnaligned = ref buffers.Unaligned[index]; + + if (currentUnaligned != unaligned) + { + currentUnaligned = unaligned; + _unalignedStorageBuffers += unaligned ? 1 : -1; + } + } + } + /// /// Sets a storage buffer on the compute pipeline. /// Storage buffers can be read and written to on shaders. @@ -214,6 +248,8 @@ namespace Ryujinx.Graphics.Gpu.Memory { size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1); + RecordStorageAlignment(_cpStorageBuffers, index, gpuVa); + gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment); ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size); @@ -234,17 +270,21 @@ namespace Ryujinx.Graphics.Gpu.Memory { size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1); + BuffersPerStage buffers = _gpStorageBuffers[stage]; + + RecordStorageAlignment(buffers, index, gpuVa); + gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment); ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size); - if (_gpStorageBuffers[stage].Buffers[index].Address != address || - _gpStorageBuffers[stage].Buffers[index].Size != size) + if (buffers.Buffers[index].Address != address || + buffers.Buffers[index].Size != size) { _gpStorageBuffersDirty = true; } - _gpStorageBuffers[stage].SetBounds(index, address, size, flags); + buffers.SetBounds(index, address, size, flags); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs b/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs index 08154df32..a67182112 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs @@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel /// Texture pool state + /// Compute state /// GPU virtual address of the compute shader /// Cached host program for the given state, if found /// Cached guest code, if any found @@ -43,6 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool TryFind( GpuChannel channel, GpuChannelPoolState poolState, + GpuChannelComputeState computeState, ulong gpuVa, out CachedShaderProgram program, out byte[] cachedGuestCode) @@ -50,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Shader program = null; ShaderCodeAccessor codeAccessor = new ShaderCodeAccessor(channel.MemoryManager, gpuVa); bool hasSpecList = _cache.TryFindItem(codeAccessor, out var specList, out cachedGuestCode); - return hasSpecList && specList.TryFindForCompute(channel, poolState, out program); + return hasSpecList && specList.TryFindForCompute(channel, poolState, computeState, out program); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 98748bf62..c567c2c06 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -225,6 +225,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.EarlyZForce; } + /// + public bool QueryHasUnalignedStorageBuffer() + { + return _oldSpecState.GraphicsState.HasUnalignedStorageBuffer || _oldSpecState.ComputeState.HasUnalignedStorageBuffer; + } + /// public bool QueryViewportTransformDisable() { diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 0bdf49499..e23b4d50e 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3747; + private const uint CodeGenVersion = 3848; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index b8cb1107f..28ea430cd 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -145,6 +145,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.HasConstantBufferDrawParameters; } + /// + public bool QueryHasUnalignedStorageBuffer() + { + return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; + } + /// public InputTopology QueryPrimitiveTopology() { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs index 89a3db712..356d3f3e4 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs @@ -32,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public readonly int SharedMemorySize; + /// + /// Indicates that any storage buffer use is unaligned. + /// + public readonly bool HasUnalignedStorageBuffer; + /// /// Creates a new GPU compute state. /// @@ -40,18 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Local group size Z of the compute shader /// Local memory size of the compute shader /// Shared memory size of the compute shader + /// Indicates that any storage buffer use is unaligned public GpuChannelComputeState( int localSizeX, int localSizeY, int localSizeZ, int localMemorySize, - int sharedMemorySize) + int sharedMemorySize, + bool hasUnalignedStorageBuffer) { LocalSizeX = localSizeX; LocalSizeY = localSizeY; LocalSizeZ = localSizeZ; LocalMemorySize = localMemorySize; SharedMemorySize = sharedMemorySize; + HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index b07276774..511f4c235 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -82,6 +82,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public readonly bool HasConstantBufferDrawParameters; + /// + /// Indicates that any storage buffer use is unaligned. + /// + public readonly bool HasUnalignedStorageBuffer; + /// /// Creates a new GPU graphics state. /// @@ -99,6 +104,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// When alpha test is enabled, indicates the value to compare with the fragment output alpha /// Type of the vertex attributes consumed by the shader /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0 + /// Indicates that any storage buffer use is unaligned public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -113,7 +119,8 @@ namespace Ryujinx.Graphics.Gpu.Shader CompareOp alphaTestCompare, float alphaTestReference, ref Array32 attributeTypes, - bool hasConstantBufferDrawParameters) + bool hasConstantBufferDrawParameters, + bool hasUnalignedStorageBuffer) { EarlyZForce = earlyZForce; Topology = topology; @@ -129,6 +136,7 @@ namespace Ryujinx.Graphics.Gpu.Shader AlphaTestReference = alphaTestReference; AttributeTypes = attributeTypes; HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; + HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 1803dae61..2a9dd6a5c 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -203,12 +203,12 @@ namespace Ryujinx.Graphics.Gpu.Shader GpuChannelComputeState computeState, ulong gpuVa) { - if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, cpShader, gpuVa)) + if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa)) { return cpShader; } - if (_computeShaderCache.TryFind(channel, poolState, gpuVa, out cpShader, out byte[] cachedGuestCode)) + if (_computeShaderCache.TryFind(channel, poolState, computeState, gpuVa, out cpShader, out byte[] cachedGuestCode)) { _cpPrograms[gpuVa] = cpShader; return cpShader; @@ -473,18 +473,20 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel using the shader /// GPU channel state to verify shader compatibility + /// GPU channel compute state to verify shader compatibility /// Cached compute shader /// GPU virtual address of the shader code in memory /// True if the code is different, false otherwise private static bool IsShaderEqual( GpuChannel channel, GpuChannelPoolState poolState, + GpuChannelComputeState computeState, CachedShaderProgram cpShader, ulong gpuVa) { if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa)) { - return cpShader.SpecializationState.MatchesCompute(channel, poolState, true); + return cpShader.SpecializationState.MatchesCompute(channel, poolState, computeState, true); } return false; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs index abc9d913b..cb6ab49a8 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs @@ -53,13 +53,14 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel /// Texture pool state + /// Compute state /// Cached program, if found /// True if a compatible program is found, false otherwise - public bool TryFindForCompute(GpuChannel channel, GpuChannelPoolState poolState, out CachedShaderProgram program) + public bool TryFindForCompute(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelComputeState computeState, out CachedShaderProgram program) { foreach (var entry in _entries) { - if (entry.SpecializationState.MatchesCompute(channel, poolState, true)) + if (entry.SpecializationState.MatchesCompute(channel, poolState, computeState, true)) { program = entry; return true; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 0aecc5b7b..8f931507a 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -531,6 +531,11 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + if (graphicsState.HasUnalignedStorageBuffer != GraphicsState.HasUnalignedStorageBuffer) + { + return false; + } + return Matches(channel, poolState, checkTextures, isCompute: false); } @@ -539,10 +544,16 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel /// Texture pool state + /// Compute state /// Indicates whether texture descriptors should be checked /// True if the state matches, false otherwise - public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures) + public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelComputeState computeState, bool checkTextures) { + if (computeState.HasUnalignedStorageBuffer != ComputeState.HasUnalignedStorageBuffer) + { + return false; + } + return Matches(channel, poolState, checkTextures, isCompute: true); } diff --git a/Ryujinx.Graphics.Shader/Constants.cs b/Ryujinx.Graphics.Shader/Constants.cs index 7f1445ed0..c6f9ef494 100644 --- a/Ryujinx.Graphics.Shader/Constants.cs +++ b/Ryujinx.Graphics.Shader/Constants.cs @@ -10,5 +10,7 @@ namespace Ryujinx.Graphics.Shader public const int NvnBaseVertexByteOffset = 0x640; public const int NvnBaseInstanceByteOffset = 0x644; public const int NvnDrawIndexByteOffset = 0x648; + + public const int StorageAlignment = 16; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 4f800a145..f05a8527b 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -177,6 +177,15 @@ namespace Ryujinx.Graphics.Shader return false; } + /// + /// Queries whenever the current draw uses unaligned storage buffer addresses. + /// + /// True if any storage buffer address is not aligned to 16 bytes, false otherwise + bool QueryHasUnalignedStorageBuffer() + { + return false; + } + /// /// Queries host about the presence of the FrontFacing built-in variable bug. /// diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index d2200d0b8..25c0eb25b 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // we can guess which storage buffer it is accessing. // We can then replace the global memory access with a storage // buffer access. - node = ReplaceGlobalWithStorage(node, config, storageIndex); + node = ReplaceGlobalWithStorage(block, node, config, storageIndex); } else if (config.Stage == ShaderStage.Compute && operation.Inst == Instruction.LoadGlobal) { @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - private static LinkedListNode ReplaceGlobalWithStorage(LinkedListNode node, ShaderConfig config, int storageIndex) + private static LinkedListNode ReplaceGlobalWithStorage(BasicBlock block, LinkedListNode node, ShaderConfig config, int storageIndex) { Operation operation = (Operation)node.Value; @@ -64,42 +64,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations config.SetUsedStorageBuffer(storageIndex, isWrite); - Operand GetStorageOffset() - { - Operand addrLow = operation.GetSource(0); - - Operand baseAddrLow = Cbuf(0, GetStorageCbOffset(config.Stage, storageIndex)); - - Operand baseAddrTrunc = Local(); - - Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); - - Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); - - node.List.AddBefore(node, andOp); - - Operand byteOffset = Local(); - Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc); - - node.List.AddBefore(node, subOp); - - if (isStg16Or8) - { - return byteOffset; - } - - Operand wordOffset = Local(); - Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2)); - - node.List.AddBefore(node, shrOp); - - return wordOffset; - } - Operand[] sources = new Operand[operation.SourcesCount]; sources[0] = Const(storageIndex); - sources[1] = GetStorageOffset(); + sources[1] = GetStorageOffset(block, node, config, storageIndex, operation.GetSource(0), isStg16Or8); for (int index = 2; index < operation.SourcesCount; index++) { @@ -144,6 +112,170 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return node; } + private static Operand GetStorageOffset( + BasicBlock block, + LinkedListNode node, + ShaderConfig config, + int storageIndex, + Operand addrLow, + bool isStg16Or8) + { + int baseAddressCbOffset = GetStorageCbOffset(config.Stage, storageIndex); + + bool storageAligned = !(config.GpuAccessor.QueryHasUnalignedStorageBuffer() || config.GpuAccessor.QueryHostStorageBufferOffsetAlignment() > Constants.StorageAlignment); + + (Operand byteOffset, int constantOffset) = storageAligned ? + GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) : + (null, 0); + + if (byteOffset == null) + { + Operand baseAddrLow = Cbuf(0, baseAddressCbOffset); + Operand baseAddrTrunc = Local(); + + Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); + + Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); + + node.List.AddBefore(node, andOp); + + Operand offset = Local(); + Operation subOp = new Operation(Instruction.Subtract, offset, addrLow, baseAddrTrunc); + + node.List.AddBefore(node, subOp); + + byteOffset = offset; + } + else if (constantOffset != 0) + { + Operand offset = Local(); + Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset)); + + node.List.AddBefore(node, addOp); + + byteOffset = offset; + } + + if (byteOffset != null) + { + ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset); + } + + if (isStg16Or8) + { + return byteOffset; + } + + Operand wordOffset = Local(); + Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2)); + + node.List.AddBefore(node, shrOp); + + return wordOffset; + } + + private static bool IsCb0Offset(Operand operand, int offset) + { + return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == 0 && operand.GetCbufOffset() == offset; + } + + private static void ReplaceAddressAlignment(LinkedList list, Operand address, Operand byteOffset, int constantOffset) + { + // When we emit 16/8-bit LDG, we add extra code to determine the address alignment. + // Eliminate the storage buffer base address from this too, leaving only the byte offset. + + foreach (INode useNode in address.UseOps) + { + if (useNode is Operation op && op.Inst == Instruction.BitwiseAnd) + { + Operand src1 = op.GetSource(0); + Operand src2 = op.GetSource(1); + + int addressIndex = -1; + + if (src1 == address && src2.Type == OperandType.Constant && src2.Value == 3) + { + addressIndex = 0; + } + else if (src2 == address && src1.Type == OperandType.Constant && src1.Value == 3) + { + addressIndex = 1; + } + + if (addressIndex != -1) + { + LinkedListNode node = list.Find(op); + + // Add offset calculation before the use. Needs to be on the same block. + if (node != null) + { + Operand offset = Local(); + Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset)); + list.AddBefore(node, addOp); + + op.SetSource(addressIndex, offset); + } + } + } + } + } + + private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int baseAddressCbOffset) + { + if (IsCb0Offset(address, baseAddressCbOffset)) + { + // Direct offset: zero. + return (Const(0), 0); + } + + (address, int constantOffset) = GetStorageConstantOffset(block, address); + + address = Utils.FindLastOperation(address, block); + + if (IsCb0Offset(address, baseAddressCbOffset)) + { + // Only constant offset + return (Const(0), constantOffset); + } + + if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add) + { + return (null, 0); + } + + Operand src1 = offsetAdd.GetSource(0); + Operand src2 = Utils.FindLastOperation(offsetAdd.GetSource(1), block); + + if (IsCb0Offset(src2, baseAddressCbOffset)) + { + return (src1, constantOffset); + } + else if (IsCb0Offset(src1, baseAddressCbOffset)) + { + return (src2, constantOffset); + } + + return (null, 0); + } + + private static (Operand, int) GetStorageConstantOffset(BasicBlock block, Operand address) + { + if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add) + { + return (address, 0); + } + + Operand src1 = offsetAdd.GetSource(0); + Operand src2 = offsetAdd.GetSource(1); + + if (src2.Type != OperandType.Constant) + { + return (address, 0); + } + + return (src1, src2.Value); + } + private static LinkedListNode ReplaceLdgWithLdc(LinkedListNode node, ShaderConfig config, int storageIndex) { Operation operation = (Operation)node.Value; @@ -165,7 +297,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand byteOffset = Local(); Operand wordOffset = Local(); - Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc); + Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc); Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2)); node.List.AddBefore(node, subOp); @@ -260,7 +392,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { if (operand.Type == OperandType.ConstantBuffer) { - int slot = operand.GetCbufSlot(); + int slot = operand.GetCbufSlot(); int offset = operand.GetCbufOffset(); if (slot == 0 && offset >= sbStart && offset < sbEnd) From 7c53b69c300def240648900aafeca6ef774ac8a5 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 18 Nov 2022 02:37:37 +0000 Subject: [PATCH 081/737] SPIR-V: Fix unscaling helper not being able to find Array textures (#3863) The type in the `texOp` in the textureSize instruction doesn't have the exact type on SPIR-V (for example, it is missing the Array flag). This PR gives it the proper type before giving it to the unscaling helper. This fixes the ground textures being broken on Pokemon Scarlet/Violet when scaling. It wasn't finding the texture, so the descriptor index it provided was -1... --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs | 2 +- Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index e23b4d50e..7923a393e 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3848; + private const uint CodeGenVersion = 3863; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index c743a274e..ea83061ec 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -1829,7 +1829,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (texOp.Index < 2 || (type & SamplerType.Mask) == SamplerType.Texture3D) { - result = ScalingHelpers.ApplyUnscaling(context, texOp, result, isBindless, isIndexed); + result = ScalingHelpers.ApplyUnscaling(context, texOp.WithType(type), result, isBindless, isIndexed); } return new OperationResult(AggregateType.S32, result); diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 7d1d0ae35..957a956fc 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -27,5 +27,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr CbufSlot = cbufSlot; Handle = handle; } + + public AstTextureOperation WithType(SamplerType type) + { + return new AstTextureOperation(Inst, type, Format, Flags, CbufSlot, Handle, Index); + } } } \ No newline at end of file From a16682cfd3c9be230bb5fb8b9b656ba182ef1f44 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 18 Nov 2022 02:54:20 +0000 Subject: [PATCH 082/737] Allow _volatile to be set from MultiRegionHandle checks again (#3830) * Allow _volatile to be set from MultiRegionHandle checks again Tracking handles have a `_volatile` flag which indicates that the resource being tracked is modified every time it is used under a new sequence number. This is used to reduce the time spent reprotecting memory for tracking writes to commonly modified buffers, like constant buffers. This optimisation works by detecting if a buffer is modified every time a check happens. If a buffer is checked but it is not dirty, then that data is likely not modified every sequence number, and should use memory protection for write tracking. If the opposite is the case all the time, it is faster to just assume it's dirty as we'd just be wasting time protecting the memory. The new MultiRegionBitmap could not notify handles that they had been checked as part of the fast bitmap lookup, so bindings larger than 4096 bytes wouldn't trigger it at all. This meant that they would be subject to a ton of reprotection if they were modified often. This does mean there are two separate sources for a _volatile set: VolatileOrDirty + _checkCount, and the bitmap check. These shouldn't interfere with each other, though. This fixes performance regressions from #3775 in Pokemon Sword, and hopefully Yu-Gi-Oh! RUSH DUEL: Dawn of the Battle Royale. May affect other games. * Fix stupid mistake --- Ryujinx.Memory/Tracking/MultiRegionHandle.cs | 22 +++++++++++++------- Ryujinx.Memory/Tracking/RegionHandle.cs | 17 +++++++++++---- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs index 45138ff35..6cbea7f31 100644 --- a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs @@ -25,6 +25,7 @@ namespace Ryujinx.Memory.Tracking private int _sequenceNumber; private BitMap _sequenceNumberBitmap; + private BitMap _dirtyCheckedBitmap; private int _uncheckedHandles; public bool Dirty { get; private set; } = true; @@ -36,6 +37,7 @@ namespace Ryujinx.Memory.Tracking _dirtyBitmap = new ConcurrentBitmap(_handles.Length, true); _sequenceNumberBitmap = new BitMap(_handles.Length); + _dirtyCheckedBitmap = new BitMap(_handles.Length); int i = 0; @@ -246,16 +248,18 @@ namespace Ryujinx.Memory.Tracking } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action modifiedAction) + private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, long[] checkMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action modifiedAction) { long seqMask = mask & ~seqMasks[index]; + long checkMask = (~dirtyBits) & seqMask; dirtyBits &= seqMask; while (dirtyBits != 0) { int bit = BitOperations.TrailingZeroCount(dirtyBits); + long bitValue = 1L << bit; - dirtyBits &= ~(1L << bit); + dirtyBits &= ~bitValue; int handleIndex = baseBit + bit; @@ -273,11 +277,14 @@ namespace Ryujinx.Memory.Tracking } rgSize += handle.Size; - handle.Reprotect(); + handle.Reprotect(false, (checkMasks[index] & bitValue) == 0); + + checkMasks[index] &= ~bitValue; prevHandle = handleIndex; } + checkMasks[index] |= checkMask; seqMasks[index] |= mask; _uncheckedHandles -= BitOperations.PopCount((ulong)seqMask); @@ -328,6 +335,7 @@ namespace Ryujinx.Memory.Tracking ulong rgSize = 0; long[] seqMasks = _sequenceNumberBitmap.Masks; + long[] checkedMasks = _dirtyCheckedBitmap.Masks; long[] masks = _dirtyBitmap.Masks; int startIndex = startHandle >> ConcurrentBitmap.IntShift; @@ -345,20 +353,20 @@ namespace Ryujinx.Memory.Tracking if (startIndex == endIndex) { - ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); } else { - ParseDirtyBits(startValue, startMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + ParseDirtyBits(startValue, startMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); for (int i = startIndex + 1; i < endIndex; i++) { - ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); } long endValue = Volatile.Read(ref masks[endIndex]); - ParseDirtyBits(endValue, endMask, endIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); + ParseDirtyBits(endValue, endMask, endIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); } if (rgSize != 0) diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs index 363bedef0..affc84abb 100644 --- a/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -263,15 +263,15 @@ namespace Ryujinx.Memory.Tracking /// public void ForceDirty() { - _checkCount++; - Dirty = true; } /// /// Consume the dirty flag for this handle, and reprotect so it can be set on the next write. /// - public void Reprotect(bool asDirty = false) + /// True if the handle should be reprotected as dirty, rather than have it cleared + /// True if this reprotect is the result of consecutive dirty checks + public void Reprotect(bool asDirty, bool consecutiveCheck = false) { if (_volatile) return; @@ -296,7 +296,7 @@ namespace Ryujinx.Memory.Tracking } else if (!asDirty) { - if (_checkCount > 0 && _checkCount < CheckCountForInfrequent) + if (consecutiveCheck || (_checkCount > 0 && _checkCount < CheckCountForInfrequent)) { if (++_volatileCount >= VolatileThreshold && _preAction == null) { @@ -313,6 +313,15 @@ namespace Ryujinx.Memory.Tracking } } + /// + /// Consume the dirty flag for this handle, and reprotect so it can be set on the next write. + /// + /// True if the handle should be reprotected as dirty, rather than have it cleared + public void Reprotect(bool asDirty = false) + { + Reprotect(asDirty, false); + } + /// /// Register an action to perform when the tracked region is read or written. /// The action is automatically removed after it runs. From c1372ed775e11aa4759fd3460f2e01d16372205a Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Fri, 18 Nov 2022 04:10:44 +0100 Subject: [PATCH 083/737] Use ReadOnlySpan compiler optimization in more places (#3853) * Use ReadOnlySpan compiler optimization in more places * Revert changes in ShaderBinaries.cs * Remove unused using; * Use ReadOnlySpan compiler optimization in more places --- ARMeilleure/Common/BitUtils.cs | 8 ++------ Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs | 2 +- Ryujinx.Graphics.Nvdec.Vp9/Luts.cs | 26 +++++++++++------------- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/ARMeilleure/Common/BitUtils.cs b/ARMeilleure/Common/BitUtils.cs index 51160eff0..e7697ff31 100644 --- a/ARMeilleure/Common/BitUtils.cs +++ b/ARMeilleure/Common/BitUtils.cs @@ -1,15 +1,11 @@ +using System; using System.Numerics; namespace ARMeilleure.Common { static class BitUtils { - private static readonly sbyte[] HbsNibbleLut; - - static BitUtils() - { - HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; - } + private static ReadOnlySpan HbsNibbleLut => new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; public static long FillWithOnes(int bits) { diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs b/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs index bde774c8e..52b1b3dc4 100644 --- a/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs +++ b/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 short dqv = dq[0]; ReadOnlySpan cat6Prob = (xd.Bd == 12) ? Luts.Vp9Cat6ProbHigh12 - : (xd.Bd == 10) ? new ReadOnlySpan(Luts.Vp9Cat6ProbHigh12).Slice(2) : Luts.Vp9Cat6Prob; + : (xd.Bd == 10) ? Luts.Vp9Cat6ProbHigh12.Slice(2) : Luts.Vp9Cat6Prob; 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. diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs b/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs index f703d214f..140181ef8 100644 --- a/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs +++ b/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs @@ -1,14 +1,12 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Types; +using System; namespace Ryujinx.Graphics.Nvdec.Vp9 { internal static class Luts { - public static readonly byte[] SizeGroupLookup = new byte[] - { - 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 - }; + public static ReadOnlySpan SizeGroupLookup => new byte[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 }; public static readonly BlockSize[][] SubsizeLookup = new BlockSize[][] { @@ -1070,18 +1068,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 -(sbyte)MvClassType.MvClass10, }; - public static readonly sbyte[] Vp9MvFPTree = new sbyte[] { -0, 2, -1, 4, -2, -3 }; + public static ReadOnlySpan Vp9MvFPTree => new sbyte[] { -0, 2, -1, 4, -2, -3 }; // Entropy - public static readonly byte[] Vp9Cat1Prob = new byte[] { 159 }; - public static readonly byte[] Vp9Cat2Prob = new byte[] { 165, 145 }; - public static readonly byte[] Vp9Cat3Prob = new byte[] { 173, 148, 140 }; - public static readonly byte[] Vp9Cat4Prob = new byte[] { 176, 155, 140, 135 }; - public static readonly byte[] Vp9Cat5Prob = new byte[] { 180, 157, 141, 134, 130 }; - public static readonly byte[] Vp9Cat6Prob = new byte[] { 254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; + 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 readonly byte[] Vp9Cat6ProbHigh12 = new byte[] + public static ReadOnlySpan Vp9Cat6ProbHigh12 => new byte[] { 255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; @@ -1131,12 +1129,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; - private static readonly byte[] Vp9CoefbandTrans4X4 = new byte[] + private static ReadOnlySpan Vp9CoefbandTrans4X4 => new byte[] { 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, }; - public static byte[] get_band_translate(TxSize txSize) + public static ReadOnlySpan get_band_translate(TxSize txSize) { return txSize == TxSize.Tx4x4 ? Vp9CoefbandTrans4X4 : Vp9CoefbandTrans8X8Plus; } From 022d4953356ba999c447a4165891052eba8e307e Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 18 Nov 2022 04:29:01 +0100 Subject: [PATCH 084/737] am: Stub GetSaveDataSizeMax (#3857) * am: Stub GetSaveDataSizeMax() * am: Remove todo comment for GetSaveDataSizeMax() * am: saveDataSize & journalDataSize should be of type long * am: Add explanation for returning default values in GetSaveDataSizeMax() --- .../ApplicationProxy/IApplicationFunctions.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index f89827d32..609bba1ed 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -26,8 +26,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati { class IApplicationFunctions : IpcService { - private ulong _defaultSaveDataSize = 200000000; - private ulong _defaultJournalSaveDataSize = 200000000; + private long _defaultSaveDataSize = 200000000; + private long _defaultJournalSaveDataSize = 200000000; private KEvent _gpuErrorDetectedSystemEvent; private KEvent _friendInvitationStorageChannelEvent; @@ -203,13 +203,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati } [CommandHipc(25)] // 3.0.0+ - // ExtendSaveData(u8 save_data_type, nn::account::Uid, u64 save_size, u64 journal_size) -> u64 result_code + // ExtendSaveData(u8 save_data_type, nn::account::Uid, s64 save_size, s64 journal_size) -> u64 result_code public ResultCode ExtendSaveData(ServiceCtx context) { SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64(); Uid userId = context.RequestData.ReadStruct(); - ulong saveDataSize = context.RequestData.ReadUInt64(); - ulong journalSize = context.RequestData.ReadUInt64(); + long saveDataSize = context.RequestData.ReadInt64(); + long journalSize = context.RequestData.ReadInt64(); // NOTE: Service calls nn::fs::ExtendApplicationSaveData. // Since LibHac currently doesn't support this method, we can stub it for now. @@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati } [CommandHipc(26)] // 3.0.0+ - // GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (u64 save_size, u64 journal_size) + // GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (s64 save_size, s64 journal_size) public ResultCode GetSaveDataSize(ServiceCtx context) { SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64(); @@ -268,6 +268,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } + [CommandHipc(28)] // 11.0.0+ + // GetSaveDataSizeMax() -> (s64 save_size_max, s64 journal_size_max) + public ResultCode GetSaveDataSizeMax(ServiceCtx context) + { + // NOTE: We are currently using a stub for GetSaveDataSize() which returns the default values. + // For this method we shouldn't return anything lower than that, but since we aren't interacting + // with fs to get the actual sizes, we return the default values here as well. + // This also helps in case ExtendSaveData() has been executed and the default values were modified. + + context.ResponseData.Write(_defaultSaveDataSize); + context.ResponseData.Write(_defaultJournalSaveDataSize); + + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + [CommandHipc(30)] // BeginBlockingHomeButtonShortAndLongPressed() public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context) From 187372cbde56a8892e4810d8c99d6e2debd96ad5 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 18 Nov 2022 14:58:24 +0000 Subject: [PATCH 085/737] Prune ForceDirty and CheckModified caches on unmap (#3862) * Prune ForceDirty and CheckModified caches on unmap Since we're now using this for modified checks on the HLE indirect draw method, I'm worried that leaving these to forever gather cache entries isn't the best idea for performance in the long term, and it could keep old buffer objects alive for longer than they should be. This PR adds the ability to prune invalid entries before checking these caches, and queues it whenever gpu memory is unmapped. It also aligns modified checks to the page size, as I figured it would be possible for a huge number of overlapping over a game's runtime. This prevents Super Mario Odyssey from having 10s of thousands of entries in the modified cache in Metro Kingdom, and them duplicating when entering and leaving a building (should be cleared, as they were unmapped). * Address Feedback --- Ryujinx.Graphics.Gpu/GpuChannel.cs | 2 + Ryujinx.Graphics.Gpu/Memory/BufferCache.cs | 82 +++++++++++++++++++--- 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs index f3bdd5763..b73756c64 100644 --- a/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs @@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Gpu // Since the memory manager changed, make sure we will get pools from addresses of the new memory manager. TextureManager.ReloadPools(); + MemoryManager.Physical.BufferCache.QueuePrune(); } /// @@ -77,6 +78,7 @@ namespace Ryujinx.Graphics.Gpu private void MemoryUnmappedHandler(object sender, UnmapEventArgs e) { TextureManager.ReloadPools(); + MemoryManager.Physical.BufferCache.QueuePrune(); } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 894d009c0..85ed49d59 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly Dictionary _dirtyCache; private readonly Dictionary _modifiedCache; + private bool _pruneCaches; public event Action NotifyBuffersModified; @@ -136,6 +137,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the buffer public void ForceDirty(MemoryManager memoryManager, ulong gpuVa, ulong size) { + if (_pruneCaches) + { + Prune(); + } + if (!_dirtyCache.TryGetValue(gpuVa, out BufferCacheEntry result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence) @@ -158,17 +164,29 @@ namespace Ryujinx.Graphics.Gpu.Memory /// True if modified, false otherwise public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr) { - if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) || - result.EndGpuAddress < gpuVa + size || - result.UnmappedSequence != result.Buffer.UnmappedSequence) + if (_pruneCaches) { - ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size); - result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size)); - - _modifiedCache[gpuVa] = result; + Prune(); } - outAddr = result.Address; + // Align the address to avoid creating too many entries on the quick lookup dictionary. + ulong mask = BufferAlignmentMask; + ulong alignedGpuVa = gpuVa & (~mask); + ulong alignedEndGpuVa = (gpuVa + size + mask) & (~mask); + + size = alignedEndGpuVa - alignedGpuVa; + + if (!_modifiedCache.TryGetValue(alignedGpuVa, out BufferCacheEntry result) || + result.EndGpuAddress < alignedEndGpuVa || + result.UnmappedSequence != result.Buffer.UnmappedSequence) + { + ulong address = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size); + result = new BufferCacheEntry(address, alignedGpuVa, GetBuffer(address, size)); + + _modifiedCache[alignedGpuVa] = result; + } + + outAddr = result.Address | (gpuVa & mask); return result.Buffer.IsModified(result.Address, size); } @@ -435,6 +453,54 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Prune any invalid entries from a quick access dictionary. + /// + /// Dictionary to prune + /// List used to track entries to delete + private void Prune(Dictionary dictionary, ref List toDelete) + { + foreach (var entry in dictionary) + { + if (entry.Value.UnmappedSequence != entry.Value.Buffer.UnmappedSequence) + { + (toDelete ??= new()).Add(entry.Key); + } + } + + if (toDelete != null) + { + foreach (ulong entry in toDelete) + { + dictionary.Remove(entry); + } + } + } + + /// + /// Prune any invalid entries from the quick access dictionaries. + /// + private void Prune() + { + List toDelete = null; + + Prune(_dirtyCache, ref toDelete); + + toDelete?.Clear(); + + Prune(_modifiedCache, ref toDelete); + + _pruneCaches = false; + } + + /// + /// Queues a prune of invalid entries the next time a dictionary cache is accessed. + /// + public void QueuePrune() + { + _pruneCaches = true; + } + /// /// Disposes all buffers in the cache. /// It's an error to use the buffer manager after disposal. From 131baebe2a569cfe8533aa57ca6df2c8f846f6ad Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 18 Nov 2022 14:58:56 +0000 Subject: [PATCH 086/737] Vulkan: Don't create preload command buffer outside a render pass (#3864) * Vulkan: Don't create preload buffer outside a render pass The preload command buffer is used to avoid render pass splits and barriers when updating buffer data. However, when a render pass is not active (for example, at the start of a pass, or during compute invocations) buffer uploads can be performed at any time, so the optimization isn't as useful. This PR makes it so that the preload command buffer is only used for buffer updates outside of a render pass. It's still used for textures as I don't want to shake things up right now regarding how the preload buffer is obtained before some other changes, and texture updates are a lot rarer anyways. Improves performance slightly in Pokemon Scarlet/Violet (43 -> 48), as it was switching to compute, writing a bunch of buffers inline, then dispatching, then flushing commands... It uses 1 command buffer instead of 2 every time it does this now. Maybe it would be nice to find a faster way to sync without creating so many command buffers in a short period of time. * Address feedback --- Ryujinx.Graphics.Vulkan/BufferHolder.cs | 5 ++++- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 15 ++++++++++----- Ryujinx.Graphics.Vulkan/ShaderCollection.cs | 6 +++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 24f789f62..f5c478db7 100644 --- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -210,7 +210,10 @@ namespace Ryujinx.Graphics.Vulkan } } - if (cbs != null && !(_buffer.HasCommandBufferDependency(cbs.Value) && _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize))) + if (cbs != null && + _gd.PipelineInternal.RenderPassActive && + !(_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. diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 55c3fea22..4efe61bf3 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -49,7 +49,6 @@ namespace Ryujinx.Graphics.Vulkan private Auto _framebuffer; private Auto _renderPass; private int _writtenAttachmentCount; - private bool _renderPassActive; private readonly DescriptorSetUpdater _descriptorSetUpdater; @@ -73,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan private PipelineColorBlendAttachmentState[] _storedBlend; public ulong DrawCount { get; private set; } + public bool RenderPassActive { get; private set; } public unsafe PipelineBase(VulkanRenderer gd, Device device) { @@ -838,6 +838,11 @@ namespace Ryujinx.Graphics.Vulkan stages.CopyTo(_newState.Stages.AsSpan().Slice(0, stages.Length)); SignalStateChange(); + + if (_program.IsCompute) + { + EndRenderPass(); + } } public void Specialize(in T data) where T : unmanaged @@ -1451,7 +1456,7 @@ namespace Ryujinx.Graphics.Vulkan private unsafe void BeginRenderPass() { - if (!_renderPassActive) + if (!RenderPassActive) { var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height)); var clearValue = new ClearValue(); @@ -1467,18 +1472,18 @@ namespace Ryujinx.Graphics.Vulkan }; Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline); - _renderPassActive = true; + RenderPassActive = true; } } public void EndRenderPass() { - if (_renderPassActive) + if (RenderPassActive) { PauseTransformFeedbackInternal(); Gd.Api.CmdEndRenderPass(CommandBuffer); SignalRenderPassEnd(); - _renderPassActive = false; + RenderPassActive = false; } } diff --git a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index ab0ea2e95..bf2874d7c 100644 --- a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Vulkan public bool HasMinimalLayout { get; } public bool UsePushDescriptors { get; } + public bool IsCompute { get; } public uint Stages { get; } @@ -47,7 +48,6 @@ namespace Ryujinx.Graphics.Vulkan private VulkanRenderer _gd; private Device _device; private bool _initialized; - private bool _isCompute; private ProgramPipelineState _state; private DisposableRenderPass _dummyRenderPass; @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit) { - _isCompute = true; + IsCompute = true; } internalShaders[i] = shader; @@ -163,7 +163,7 @@ namespace Ryujinx.Graphics.Vulkan try { - if (_isCompute) + if (IsCompute) { CreateBackgroundComputePipeline(); } From de162a648b3bb1c8080460bfb8392951340ef40b Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 18 Nov 2022 20:47:29 +0000 Subject: [PATCH 087/737] Gpu: Fix thread safety of ReregisterRanges (#3865) A quick fix to prevent reading the wrong value of Count when reregistering ranges for a new target buffer. Buffer flushes from another thread can modify the range list when the lock isn't active, which can change the count. This prevents some crashes in Pokemon Scarlet/Violet. It's probably likely that buffer migration during flush is causing some other issues in this game, but this at least prevents the crashing. --- Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index b9b533fb7..07dbd2094 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -325,13 +325,15 @@ namespace Ryujinx.Graphics.Gpu.Memory public void ReregisterRanges(Action rangeAction) { ref var ranges = ref ThreadStaticArray.Get(); + int count; // Range list must be consistent for this operation. lock (_lock) { - if (ranges.Length < Count) + count = Count; + if (ranges.Length < count) { - Array.Resize(ref ranges, Count); + Array.Resize(ref ranges, count); } int i = 0; @@ -342,7 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory } ulong currentSync = _context.SyncNumber; - for (int i = 0; i < Count; i++) + for (int i = 0; i < count; i++) { BufferModifiedRange range = ranges[i]; if (range.SyncNumber != currentSync) From 7373ec579226e198d3d7825811eb592489acee1c Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 19 Nov 2022 02:11:34 +0000 Subject: [PATCH 088/737] Vulkan: Clear dummy texture to (0,0,0,0) on creation (#3867) This might fix an issue with AMD gpus on linux where the data could contain random garbage data. On the switch, it always samples as 0. --- Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs | 6 ++++++ Ryujinx.Graphics.Vulkan/PipelineBase.cs | 2 ++ 2 files changed, 8 insertions(+) diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index e0d5d2863..8479bcf78 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -130,6 +130,12 @@ namespace Ryujinx.Graphics.Vulkan 1f)); } + public void Initialize() + { + Span dummyTextureData = stackalloc byte[4]; + _dummyTexture.SetData(dummyTextureData); + } + public void SetProgram(ShaderCollection program) { _program = program; diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 4efe61bf3..5d2263aa2 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -114,6 +114,8 @@ namespace Ryujinx.Graphics.Vulkan public void Initialize() { + _descriptorSetUpdater.Initialize(); + SupportBufferUpdater = new SupportBufferUpdater(Gd); SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount); From 2e43d01d3658d82f98c9eeea8280d8ec122c0c6b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 18 Nov 2022 23:27:54 -0300 Subject: [PATCH 089/737] Move gl_Layer from vertex to geometry if GPU does not support it on vertex (#3866) * Move gl_Layer from vertex to geometry if GPU does not support it on vertex * Shader cache version bump * PR feedback --- Ryujinx.Graphics.GAL/Capabilities.cs | 3 + .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../DiskCache/ParallelDiskCacheLoader.cs | 12 +++ .../Shader/GpuAccessorBase.cs | 2 + Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 12 +++ Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 2 + Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 3 +- Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 ++ .../Instructions/InstEmitAttribute.cs | 14 ++- .../Translation/ShaderConfig.cs | 38 ++++++++ .../Translation/Translator.cs | 12 +-- .../Translation/TranslatorContext.cs | 97 ++++++++++++++++++- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 13 files changed, 190 insertions(+), 17 deletions(-) diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index 3138a43b5..60f93bc4c 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsImageLoadFormatted; + public readonly bool SupportsLayerVertexTessellation; public readonly bool SupportsMismatchingViewFormat; public readonly bool SupportsCubemapView; public readonly bool SupportsNonConstantTextureOffset; @@ -55,6 +56,7 @@ namespace Ryujinx.Graphics.GAL bool supportsFragmentShaderOrderingIntel, bool supportsGeometryShaderPassthrough, bool supportsImageLoadFormatted, + bool supportsLayerVertexTessellation, bool supportsMismatchingViewFormat, bool supportsCubemapView, bool supportsNonConstantTextureOffset, @@ -86,6 +88,7 @@ namespace Ryujinx.Graphics.GAL SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsImageLoadFormatted = supportsImageLoadFormatted; + SupportsLayerVertexTessellation = supportsLayerVertexTessellation; SupportsMismatchingViewFormat = supportsMismatchingViewFormat; SupportsCubemapView = supportsCubemapView; SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 7923a393e..69067fe6f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3863; + private const uint CodeGenVersion = 3866; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 04d93bba5..9261cb0dc 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -636,6 +636,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length]; List translatedStages = new List(); + TranslatorContext previousStage = null; + for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) { TranslatorContext currentStage = translatorContexts[stageIndex + 1]; @@ -668,6 +670,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { translatedStages.Add(program); } + + previousStage = currentStage; + } + else if ( + previousStage != null && + previousStage.LayerOutputWritten && + stageIndex == 3 && + !_context.Capabilities.SupportsLayerVertexTessellation) + { + translatedStages.Add(previousStage.GenerateGeometryPassthrough()); } } diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 40c5ed64b..9648298e9 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -128,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted; + public bool QueryHostSupportsLayerVertexTessellation() => _context.Capabilities.SupportsLayerVertexTessellation; + public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset; public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 2a9dd6a5c..3eaab79f0 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -356,6 +356,8 @@ namespace Ryujinx.Graphics.Gpu.Shader CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; List shaderSources = new List(); + TranslatorContext previousStage = null; + for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) { TranslatorContext currentStage = translatorContexts[stageIndex + 1]; @@ -392,6 +394,16 @@ namespace Ryujinx.Graphics.Gpu.Shader { shaderSources.Add(CreateShaderSource(program)); } + + previousStage = currentStage; + } + else if ( + previousStage != null && + previousStage.LayerOutputWritten && + stageIndex == 3 && + !_context.Capabilities.SupportsLayerVertexTessellation) + { + shaderSources.Add(CreateShaderSource(previousStage.GenerateGeometryPassthrough())); } } diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index ba2cc2df4..8caf11dd5 100644 --- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.OpenGL private static readonly Lazy _supportsQuads = new Lazy(SupportsQuadsCheck); private static readonly Lazy _supportsSeamlessCubemapPerTexture = new Lazy(() => HasExtension("GL_ARB_seamless_cubemap_per_texture")); private static readonly Lazy _supportsShaderBallot = new Lazy(() => HasExtension("GL_ARB_shader_ballot")); + private static readonly Lazy _supportsShaderViewportLayerArray = new Lazy(() => HasExtension("GL_ARB_shader_viewport_layer_array")); private static readonly Lazy _supportsTextureCompressionBptc = new Lazy(() => HasExtension("GL_EXT_texture_compression_bptc")); private static readonly Lazy _supportsTextureCompressionRgtc = new Lazy(() => HasExtension("GL_EXT_texture_compression_rgtc")); private static readonly Lazy _supportsTextureCompressionS3tc = new Lazy(() => HasExtension("GL_EXT_texture_compression_s3tc")); @@ -61,6 +62,7 @@ namespace Ryujinx.Graphics.OpenGL public static bool SupportsQuads => _supportsQuads.Value; public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value; public static bool SupportsShaderBallot => _supportsShaderBallot.Value; + public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value; public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value; public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value; public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value; diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 418976e66..e26fe6b6c 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -117,12 +117,13 @@ namespace Ryujinx.Graphics.OpenGL supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted, + supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat, supportsCubemapView: true, supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset, supportsShaderBallot: HwCapabilities.SupportsShaderBallot, supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, - supportsViewportIndex: true, + supportsViewportIndex: HwCapabilities.SupportsShaderViewportLayerArray, supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver? diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index f05a8527b..cc690eedd 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -258,6 +258,15 @@ namespace Ryujinx.Graphics.Shader return true; } + /// + /// Queries host support for writes to Layer from vertex or tessellation shader stages. + /// + /// True if writes to layer from vertex or tessellation are supported, false otherwise + bool QueryHostSupportsLayerVertexTessellation() + { + return true; + } + /// /// Queries host GPU non-constant texture offset support. /// diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 2f75d248b..9f9ac1412 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -278,13 +278,21 @@ namespace Ryujinx.Graphics.Shader.Instructions private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput) { - if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) + bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation(); + int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1; + + if (attr == AttributeConsts.Layer && config.Stage != ShaderStage.Geometry && !supportsLayerFromVertexOrTess) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, 0, isOutput); + attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput); + config.SetLayerOutputAttribute(attr); + } + else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) + { + attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr, isOutput); } else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, 4, isOutput); + attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 4, isOutput); } return attr; diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index fcf35ce27..ae4107e80 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -48,6 +48,9 @@ namespace Ryujinx.Graphics.Shader.Translation public int Cb1DataSize { get; private set; } + public bool LayerOutputWritten { get; private set; } + public int LayerOutputAttribute { get; private set; } + public bool NextUsesFixedFuncAttributes { get; private set; } public int UsedInputAttributes { get; private set; } public int UsedOutputAttributes { get; private set; } @@ -131,6 +134,20 @@ namespace Ryujinx.Graphics.Shader.Translation _usedImages = new Dictionary(); } + public ShaderConfig( + ShaderStage stage, + OutputTopology outputTopology, + int maxOutputVertices, + IGpuAccessor gpuAccessor, + TranslationOptions options) : this(gpuAccessor, options) + { + Stage = stage; + ThreadsPerInputPrimitive = 1; + OutputTopology = outputTopology; + MaxOutputVertices = maxOutputVertices; + TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); + } + public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options) { Stage = header.Stage; @@ -240,6 +257,12 @@ namespace Ryujinx.Graphics.Shader.Translation } } + public void SetLayerOutputAttribute(int attr) + { + LayerOutputWritten = true; + LayerOutputAttribute = attr; + } + public void SetInputUserAttributeFixedFunc(int index) { UsedInputAttributes |= 1 << index; @@ -694,5 +717,20 @@ namespace Ryujinx.Graphics.Shader.Translation { return FindDescriptorIndex(GetImageDescriptors(), texOp); } + + public ShaderProgramInfo CreateProgramInfo() + { + return new ShaderProgramInfo( + GetConstantBufferDescriptors(), + GetStorageBufferDescriptors(), + GetTextureDescriptors(), + GetImageDescriptors(), + Stage, + UsedFeatures.HasFlag(FeatureFlags.InstanceId), + UsedFeatures.HasFlag(FeatureFlags.DrawParameters), + UsedFeatures.HasFlag(FeatureFlags.RtLayer), + ClipDistancesWritten, + OmapTargets); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 825391960..f8795c0ff 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -79,17 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); - var info = new ShaderProgramInfo( - config.GetConstantBufferDescriptors(), - config.GetStorageBufferDescriptors(), - config.GetTextureDescriptors(), - config.GetImageDescriptors(), - config.Stage, - config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), - config.UsedFeatures.HasFlag(FeatureFlags.DrawParameters), - config.UsedFeatures.HasFlag(FeatureFlags.RtLayer), - config.ClipDistancesWritten, - config.OmapTargets); + var info = config.CreateProgramInfo(); return config.Options.TargetLanguage switch { diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 7d820f033..127f84a67 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -1,7 +1,12 @@ -using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.CodeGen.Glsl; +using Ryujinx.Graphics.Shader.CodeGen.Spirv; +using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.Translation.Translator; @@ -18,6 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderStage Stage => _config.Stage; public int Size => _config.Size; public int Cb1DataSize => _config.Cb1DataSize; + public bool LayerOutputWritten => _config.LayerOutputWritten; public IGpuAccessor GpuAccessor => _config.GpuAccessor; @@ -149,5 +155,94 @@ namespace Ryujinx.Graphics.Shader.Translation return Translator.Translate(code, _config); } + + public ShaderProgram GenerateGeometryPassthrough() + { + int outputAttributesMask = _config.UsedOutputAttributes; + int layerOutputAttr = _config.LayerOutputAttribute; + + OutputTopology outputTopology; + int maxOutputVertices; + + switch (GpuAccessor.QueryPrimitiveTopology()) + { + case InputTopology.Points: + outputTopology = OutputTopology.PointList; + maxOutputVertices = 1; + break; + case InputTopology.Lines: + case InputTopology.LinesAdjacency: + outputTopology = OutputTopology.LineStrip; + maxOutputVertices = 2; + break; + default: + outputTopology = OutputTopology.TriangleStrip; + maxOutputVertices = 3; + break; + } + + ShaderConfig config = new ShaderConfig(ShaderStage.Geometry, outputTopology, maxOutputVertices, GpuAccessor, _config.Options); + + EmitterContext context = new EmitterContext(default, config, false); + + for (int v = 0; v < maxOutputVertices; v++) + { + int outAttrsMask = outputAttributesMask; + + while (outAttrsMask != 0) + { + int attrIndex = BitOperations.TrailingZeroCount(outAttrsMask); + + outAttrsMask &= ~(1 << attrIndex); + + for (int c = 0; c < 4; c++) + { + int attr = AttributeConsts.UserAttributeBase + attrIndex * 16 + c * 4; + + Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v)); + + if (attr == layerOutputAttr) + { + context.Copy(Attribute(AttributeConsts.Layer), value); + } + else + { + context.Copy(Attribute(attr), value); + config.SetOutputUserAttribute(attrIndex); + } + + config.SetInputUserAttribute(attrIndex, c); + } + } + + for (int c = 0; c < 4; c++) + { + int attr = AttributeConsts.PositionX + c * 4; + + Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v)); + + context.Copy(Attribute(attr), value); + } + + context.EmitVertex(); + } + + context.EndPrimitive(); + + var operations = context.GetOperations(); + var cfg = ControlFlowGraph.Create(operations); + var function = new Function(cfg.Blocks, "main", false, 0, 0); + + var sInfo = StructuredProgram.MakeStructuredProgram(new[] { function }, config); + + var info = config.CreateProgramInfo(); + + return config.Options.TargetLanguage switch + { + TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)), + TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)), + _ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()) + }; + } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 4a905d33d..3f8ebe67b 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -396,6 +396,7 @@ namespace Ryujinx.Graphics.Vulkan supportsFragmentShaderOrderingIntel: false, supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat, + supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer, supportsMismatchingViewFormat: true, supportsCubemapView: !IsAmdGcn, supportsNonConstantTextureOffset: false, From 69ced3a6e8636cfe7ea2f7333dd7cb4512def241 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 19 Nov 2022 06:24:23 -0300 Subject: [PATCH 090/737] Fix shader cache on Vulkan when geometry shaders are inserted (#3868) --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 4 +-- .../DiskCache/ShaderBinarySerializer.cs | 36 +++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 69067fe6f..a91ab9e4f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3866; + private const uint CodeGenVersion = 3868; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; @@ -379,7 +379,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache if (context.Capabilities.Api == TargetApi.Vulkan) { - ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode, isCompute); + ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode); hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo); } diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs index 11e54220f..0e5ce292c 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs @@ -1,5 +1,7 @@ using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; +using System; using System.Collections.Generic; using System.IO; @@ -12,8 +14,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache using MemoryStream output = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(output); + writer.Write(sources.Length); + for (int i = 0; i < sources.Length; i++) { + writer.Write((int)sources[i].Stage); writer.Write(sources[i].BinaryCode.Length); writer.Write(sources[i].BinaryCode); } @@ -21,29 +26,40 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return output.ToArray(); } - public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code, bool compute) + public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code) { using MemoryStream input = new MemoryStream(code); using BinaryReader reader = new BinaryReader(input); List output = new List(); - for (int i = compute ? 0 : 1; i < stages.Length; i++) + int count = reader.ReadInt32(); + + for (int i = 0; i < count; i++) { - CachedShaderStage stage = stages[i]; - - if (stage == null) - { - continue; - } - + ShaderStage stage = (ShaderStage)reader.ReadInt32(); int binaryCodeLength = reader.ReadInt32(); byte[] binaryCode = reader.ReadBytes(binaryCodeLength); - output.Add(new ShaderSource(binaryCode, ShaderCache.GetBindings(stage.Info), stage.Info.Stage, TargetLanguage.Spirv)); + output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv)); } return output.ToArray(); } + + private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage) + { + for (int i = 0; i < stages.Length; i++) + { + CachedShaderStage currentStage = stages[i]; + + if (currentStage != null && currentStage.Info.Stage == stage && currentStage.Info != null) + { + return ShaderCache.GetBindings(currentStage.Info); + } + } + + return new ShaderBindings(Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); + } } } \ No newline at end of file From 5de6ae426eaf1570d375ad5570e80f01a8eb4806 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 19 Nov 2022 23:54:33 -0300 Subject: [PATCH 091/737] Unsubscribe MemoryUnmappedHandler even when GPU channel is destroyed (#3872) --- Ryujinx.Graphics.Gpu/GpuChannel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs index b73756c64..57c632dac 100644 --- a/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs @@ -133,6 +133,7 @@ namespace Ryujinx.Graphics.Gpu { oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind; oldMemoryManager.Physical.DecrementReferenceCount(); + oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler; } } } From ab0491817e87b9dd134c41764ba213f8c8559e9b Mon Sep 17 00:00:00 2001 From: EmulationFanatic <62343878+EmulationFanatic@users.noreply.github.com> Date: Sun, 20 Nov 2022 10:15:57 -0700 Subject: [PATCH 092/737] Reword the description of the 6GB expand DRAM hack to be less tantalizing (#3870) * Reword the hack to something less tantalizing * Address feedback --- Ryujinx.Ava/Assets/Locales/en_US.json | 4 ++-- Ryujinx/Ui/Windows/SettingsWindow.glade | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 0bfa45d99..5e3ddb8ae 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -119,7 +119,7 @@ "SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": " (may cause instability)", - "SettingsTabSystemExpandDramSize": "Expand DRAM Size to 6GiB", + "SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)", "SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services", "SettingsTabGraphics": "Graphics", "SettingsTabGraphicsAPI": "Graphics API", @@ -440,7 +440,7 @@ "MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.", "MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", "MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", - "DRamTooltip": "Increases the amount of memory on the emulated system from 4GiB to 6GiB.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", + "DRamTooltip": "Utilizes an alternative MemoryMode layout to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.", "GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", "GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade index c2ffe5b1d..e39be81a9 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.glade +++ b/Ryujinx/Ui/Windows/SettingsWindow.glade @@ -1749,11 +1749,11 @@ vertical - Expand DRAM Size to 6GiB + Use alternative memory layout (Developers) True True False - Increases the amount of memory on the emulated system from 4GiB to 6GiB. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure. + Utilizes an alternative MemoryMode layout to mimic a Switch development model. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure. start 5 5 From 905a191e28fd9262d0fde97f3c2d100f74693c8d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 20 Nov 2022 20:18:21 +0100 Subject: [PATCH 093/737] Use upstream unicorn for Ryujinx.Tests.Unicorn (#3771) * unicorn: Add modified ver of unicorns const gen * unicorn: Use upstream consts These consts were generated from the dev branch of unicorn * unicorn: Split common consts into multiple enums * unicorn: Remove arch prefix from consts * unicorn: Add new windows dll Windows 10 - MSVC x64 shared build * unicorn: Use absolute path for const generation * unicorn: Remove fspcr patch * unicorn: Fix using the wrong file extension For some reason _NativeLibraryExtension evaluates to ".so" even on Windows. * unicorn: Add linux shared object again * unicron: Add DllImportResolver * unicorn: Try to import unicorn using an absolute path * unicorn: Add clean target * unicorn: Replace IsUnicornAvailable() methods * unicorn: Skip tests instead of silently passing them if unicorn is missing * unicorn: Write error message to stderr * unicorn: Make Interface static * unicron: Include prefixed unicorn libs (libunicorn.so) Co-authored-by: merry * unicorn: Add lib prefix to shared object for linux Co-authored-by: merry --- Ryujinx.Tests.Unicorn/Native/Arm32Register.cs | 135 ------- Ryujinx.Tests.Unicorn/Native/ArmRegister.cs | 295 --------------- Ryujinx.Tests.Unicorn/Native/Const/Arch.cs | 20 + Ryujinx.Tests.Unicorn/Native/Const/Arm.cs | 200 ++++++++++ Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs | 341 ++++++++++++++++++ Ryujinx.Tests.Unicorn/Native/Const/Common.cs | 44 +++ Ryujinx.Tests.Unicorn/Native/Const/Error.cs | 31 ++ Ryujinx.Tests.Unicorn/Native/Const/Hook.cs | 33 ++ Ryujinx.Tests.Unicorn/Native/Const/Memory.cs | 19 + Ryujinx.Tests.Unicorn/Native/Const/Mode.cs | 35 ++ .../Native/Const/Permission.cs | 14 + Ryujinx.Tests.Unicorn/Native/Const/TCG.cs | 12 + Ryujinx.Tests.Unicorn/Native/Interface.cs | 62 +++- Ryujinx.Tests.Unicorn/Native/UnicornArch.cs | 14 - Ryujinx.Tests.Unicorn/Native/UnicornMode.cs | 33 -- Ryujinx.Tests.Unicorn/UnicornAArch32.cs | 120 +++--- Ryujinx.Tests.Unicorn/UnicornAArch64.cs | 176 +++++---- Ryujinx.Tests.Unicorn/UnicornError.cs | 29 -- Ryujinx.Tests.Unicorn/UnicornException.cs | 7 +- Ryujinx.Tests.Unicorn/libs/README.md | 8 +- .../libs/linux/libunicorn.so | Bin 0 -> 4500288 bytes .../libs/linux/unicorn_fspcr.patch | 24 -- .../libs/windows/unicorn.dll | Bin 4582400 -> 2178048 bytes .../unicorn_const_generator.py | 199 ++++++++++ Ryujinx.Tests/Cpu/CpuTest.cs | 11 +- Ryujinx.Tests/Cpu/CpuTest32.cs | 9 +- Ryujinx.Tests/Ryujinx.Tests.csproj | 12 +- 27 files changed, 1165 insertions(+), 718 deletions(-) delete mode 100644 Ryujinx.Tests.Unicorn/Native/Arm32Register.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/ArmRegister.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Arch.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Arm.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Common.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Error.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Hook.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Memory.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Mode.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Permission.cs create mode 100644 Ryujinx.Tests.Unicorn/Native/Const/TCG.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/UnicornArch.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/UnicornMode.cs delete mode 100644 Ryujinx.Tests.Unicorn/UnicornError.cs create mode 100644 Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so delete mode 100644 Ryujinx.Tests.Unicorn/libs/linux/unicorn_fspcr.patch create mode 100644 Ryujinx.Tests.Unicorn/unicorn_const_generator.py diff --git a/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs b/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs deleted file mode 100644 index 419f8e4d9..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs +++ /dev/null @@ -1,135 +0,0 @@ -namespace Ryujinx.Tests.Unicorn.Native -{ - public enum Arm32Register - { - INVALID = 0, - - APSR, - APSR_NZCV, - CPSR, - FPEXC, - FPINST, - FPSCR, - FPSCR_NZCV, - FPSID, - ITSTATE, - LR, - PC, - SP, - SPSR, - D0, - D1, - D2, - D3, - D4, - D5, - D6, - D7, - D8, - D9, - D10, - D11, - D12, - D13, - D14, - D15, - D16, - D17, - D18, - D19, - D20, - D21, - D22, - D23, - D24, - D25, - D26, - D27, - D28, - D29, - D30, - D31, - FPINST2, - MVFR0, - MVFR1, - MVFR2, - Q0, - Q1, - Q2, - Q3, - Q4, - Q5, - Q6, - Q7, - Q8, - Q9, - Q10, - Q11, - Q12, - Q13, - Q14, - Q15, - R0, - R1, - R2, - R3, - R4, - R5, - R6, - R7, - R8, - R9, - R10, - R11, - R12, - S0, - S1, - S2, - S3, - S4, - S5, - S6, - S7, - S8, - S9, - S10, - S11, - S12, - S13, - S14, - S15, - S16, - S17, - S18, - S19, - S20, - S21, - S22, - S23, - S24, - S25, - S26, - S27, - S28, - S29, - S30, - S31, - C1_C0_2, - C13_C0_2, - C13_C0_3, - IPSR, - MSP, - PSP, - CONTROL, - ENDING, - - // Alias registers. - R13 = SP, - R14 = LR, - R15 = PC, - SB = R9, - SL = R10, - FP = R11, - IP = R12, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs deleted file mode 100644 index af331bd1c..000000000 --- a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs +++ /dev/null @@ -1,295 +0,0 @@ -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native -{ - public enum ArmRegister - { - INVALID = 0, - - X29, - X30, - NZCV, - SP, - WSP, - WZR, - XZR, - B0, - B1, - B2, - B3, - B4, - B5, - B6, - B7, - B8, - B9, - B10, - B11, - B12, - B13, - B14, - B15, - B16, - B17, - B18, - B19, - B20, - B21, - B22, - B23, - B24, - B25, - B26, - B27, - B28, - B29, - B30, - B31, - D0, - D1, - D2, - D3, - D4, - D5, - D6, - D7, - D8, - D9, - D10, - D11, - D12, - D13, - D14, - D15, - D16, - D17, - D18, - D19, - D20, - D21, - D22, - D23, - D24, - D25, - D26, - D27, - D28, - D29, - D30, - D31, - H0, - H1, - H2, - H3, - H4, - H5, - H6, - H7, - H8, - H9, - H10, - H11, - H12, - H13, - H14, - H15, - H16, - H17, - H18, - H19, - H20, - H21, - H22, - H23, - H24, - H25, - H26, - H27, - H28, - H29, - H30, - H31, - Q0, - Q1, - Q2, - Q3, - Q4, - Q5, - Q6, - Q7, - Q8, - Q9, - Q10, - Q11, - Q12, - Q13, - Q14, - Q15, - Q16, - Q17, - Q18, - Q19, - Q20, - Q21, - Q22, - Q23, - Q24, - Q25, - Q26, - Q27, - Q28, - Q29, - Q30, - Q31, - S0, - S1, - S2, - S3, - S4, - S5, - S6, - S7, - S8, - S9, - S10, - S11, - S12, - S13, - S14, - S15, - S16, - S17, - S18, - S19, - S20, - S21, - S22, - S23, - S24, - S25, - S26, - S27, - S28, - S29, - S30, - S31, - W0, - W1, - W2, - W3, - W4, - W5, - W6, - W7, - W8, - W9, - W10, - W11, - W12, - W13, - W14, - W15, - W16, - W17, - W18, - W19, - W20, - W21, - W22, - W23, - W24, - W25, - W26, - W27, - W28, - W29, - W30, - X0, - X1, - X2, - X3, - X4, - X5, - X6, - X7, - X8, - X9, - X10, - X11, - X12, - X13, - X14, - X15, - X16, - X17, - X18, - X19, - X20, - X21, - X22, - X23, - X24, - X25, - X26, - X27, - X28, - - V0, - V1, - V2, - V3, - V4, - V5, - V6, - V7, - V8, - V9, - V10, - V11, - V12, - V13, - V14, - V15, - V16, - V17, - V18, - V19, - V20, - V21, - V22, - V23, - V24, - V25, - V26, - V27, - V28, - V29, - V30, - V31, - - // > pseudo registers - PC, // program counter register - - CPACR_EL1, - ESR, - - // > thread registers - TPIDR_EL0, - TPIDRRO_EL0, - TPIDR_EL1, - - PSTATE, // PSTATE pseudoregister - - // > floating point control and status registers - FPCR, - FPSR, - - ENDING, // <-- mark the end of the list of registers - - // > alias registers - - IP0 = X16, - IP1 = X17, - FP = X29, - LR = X30, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Arch.cs b/Ryujinx.Tests.Unicorn/Native/Const/Arch.cs new file mode 100644 index 000000000..f614d091b --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Arch.cs @@ -0,0 +1,20 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Arch + { + ARM = 1, + ARM64 = 2, + MIPS = 3, + X86 = 4, + PPC = 5, + SPARC = 6, + M68K = 7, + RISCV = 8, + S390X = 9, + TRICORE = 10, + MAX = 11, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Arm.cs b/Ryujinx.Tests.Unicorn/Native/Const/Arm.cs new file mode 100644 index 000000000..4b7b3d6f3 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Arm.cs @@ -0,0 +1,200 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Arm + { + + // ARM CPU + + CPU_ARM_926 = 0, + CPU_ARM_946 = 1, + CPU_ARM_1026 = 2, + CPU_ARM_1136_R2 = 3, + CPU_ARM_1136 = 4, + CPU_ARM_1176 = 5, + CPU_ARM_11MPCORE = 6, + CPU_ARM_CORTEX_M0 = 7, + CPU_ARM_CORTEX_M3 = 8, + CPU_ARM_CORTEX_M4 = 9, + CPU_ARM_CORTEX_M7 = 10, + CPU_ARM_CORTEX_M33 = 11, + CPU_ARM_CORTEX_R5 = 12, + CPU_ARM_CORTEX_R5F = 13, + CPU_ARM_CORTEX_A7 = 14, + CPU_ARM_CORTEX_A8 = 15, + CPU_ARM_CORTEX_A9 = 16, + CPU_ARM_CORTEX_A15 = 17, + CPU_ARM_TI925T = 18, + CPU_ARM_SA1100 = 19, + CPU_ARM_SA1110 = 20, + CPU_ARM_PXA250 = 21, + CPU_ARM_PXA255 = 22, + CPU_ARM_PXA260 = 23, + CPU_ARM_PXA261 = 24, + CPU_ARM_PXA262 = 25, + CPU_ARM_PXA270 = 26, + CPU_ARM_PXA270A0 = 27, + CPU_ARM_PXA270A1 = 28, + CPU_ARM_PXA270B0 = 29, + CPU_ARM_PXA270B1 = 30, + CPU_ARM_PXA270C0 = 31, + CPU_ARM_PXA270C5 = 32, + CPU_ARM_MAX = 33, + CPU_ARM_ENDING = 34, + + // ARM registers + + REG_INVALID = 0, + REG_APSR = 1, + REG_APSR_NZCV = 2, + REG_CPSR = 3, + REG_FPEXC = 4, + REG_FPINST = 5, + REG_FPSCR = 6, + REG_FPSCR_NZCV = 7, + REG_FPSID = 8, + REG_ITSTATE = 9, + REG_LR = 10, + REG_PC = 11, + REG_SP = 12, + REG_SPSR = 13, + REG_D0 = 14, + REG_D1 = 15, + REG_D2 = 16, + REG_D3 = 17, + REG_D4 = 18, + REG_D5 = 19, + REG_D6 = 20, + REG_D7 = 21, + REG_D8 = 22, + REG_D9 = 23, + REG_D10 = 24, + REG_D11 = 25, + REG_D12 = 26, + REG_D13 = 27, + REG_D14 = 28, + REG_D15 = 29, + REG_D16 = 30, + REG_D17 = 31, + REG_D18 = 32, + REG_D19 = 33, + REG_D20 = 34, + REG_D21 = 35, + REG_D22 = 36, + REG_D23 = 37, + REG_D24 = 38, + REG_D25 = 39, + REG_D26 = 40, + REG_D27 = 41, + REG_D28 = 42, + REG_D29 = 43, + REG_D30 = 44, + REG_D31 = 45, + REG_FPINST2 = 46, + REG_MVFR0 = 47, + REG_MVFR1 = 48, + REG_MVFR2 = 49, + REG_Q0 = 50, + REG_Q1 = 51, + REG_Q2 = 52, + REG_Q3 = 53, + REG_Q4 = 54, + REG_Q5 = 55, + REG_Q6 = 56, + REG_Q7 = 57, + REG_Q8 = 58, + REG_Q9 = 59, + REG_Q10 = 60, + REG_Q11 = 61, + REG_Q12 = 62, + REG_Q13 = 63, + REG_Q14 = 64, + REG_Q15 = 65, + REG_R0 = 66, + REG_R1 = 67, + REG_R2 = 68, + REG_R3 = 69, + REG_R4 = 70, + REG_R5 = 71, + REG_R6 = 72, + REG_R7 = 73, + REG_R8 = 74, + REG_R9 = 75, + REG_R10 = 76, + REG_R11 = 77, + REG_R12 = 78, + REG_S0 = 79, + REG_S1 = 80, + REG_S2 = 81, + REG_S3 = 82, + REG_S4 = 83, + REG_S5 = 84, + REG_S6 = 85, + REG_S7 = 86, + REG_S8 = 87, + REG_S9 = 88, + REG_S10 = 89, + REG_S11 = 90, + REG_S12 = 91, + REG_S13 = 92, + REG_S14 = 93, + REG_S15 = 94, + REG_S16 = 95, + REG_S17 = 96, + REG_S18 = 97, + REG_S19 = 98, + REG_S20 = 99, + REG_S21 = 100, + REG_S22 = 101, + REG_S23 = 102, + REG_S24 = 103, + REG_S25 = 104, + REG_S26 = 105, + REG_S27 = 106, + REG_S28 = 107, + REG_S29 = 108, + REG_S30 = 109, + REG_S31 = 110, + REG_C1_C0_2 = 111, + REG_C13_C0_2 = 112, + REG_C13_C0_3 = 113, + REG_IPSR = 114, + REG_MSP = 115, + REG_PSP = 116, + REG_CONTROL = 117, + REG_IAPSR = 118, + REG_EAPSR = 119, + REG_XPSR = 120, + REG_EPSR = 121, + REG_IEPSR = 122, + REG_PRIMASK = 123, + REG_BASEPRI = 124, + REG_BASEPRI_MAX = 125, + REG_FAULTMASK = 126, + REG_APSR_NZCVQ = 127, + REG_APSR_G = 128, + REG_APSR_NZCVQG = 129, + REG_IAPSR_NZCVQ = 130, + REG_IAPSR_G = 131, + REG_IAPSR_NZCVQG = 132, + REG_EAPSR_NZCVQ = 133, + REG_EAPSR_G = 134, + REG_EAPSR_NZCVQG = 135, + REG_XPSR_NZCVQ = 136, + REG_XPSR_G = 137, + REG_XPSR_NZCVQG = 138, + REG_CP_REG = 139, + REG_ENDING = 140, + + // alias registers + REG_R13 = 12, + REG_R14 = 10, + REG_R15 = 11, + REG_SB = 75, + REG_SL = 76, + REG_FP = 77, + REG_IP = 78, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs b/Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs new file mode 100644 index 000000000..11344557b --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs @@ -0,0 +1,341 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Arm64 + { + + // ARM64 CPU + + CPU_ARM64_A57 = 0, + CPU_ARM64_A53 = 1, + CPU_ARM64_A72 = 2, + CPU_ARM64_MAX = 3, + CPU_ARM64_ENDING = 4, + + // ARM64 registers + + REG_INVALID = 0, + REG_X29 = 1, + REG_X30 = 2, + REG_NZCV = 3, + REG_SP = 4, + REG_WSP = 5, + REG_WZR = 6, + REG_XZR = 7, + REG_B0 = 8, + REG_B1 = 9, + REG_B2 = 10, + REG_B3 = 11, + REG_B4 = 12, + REG_B5 = 13, + REG_B6 = 14, + REG_B7 = 15, + REG_B8 = 16, + REG_B9 = 17, + REG_B10 = 18, + REG_B11 = 19, + REG_B12 = 20, + REG_B13 = 21, + REG_B14 = 22, + REG_B15 = 23, + REG_B16 = 24, + REG_B17 = 25, + REG_B18 = 26, + REG_B19 = 27, + REG_B20 = 28, + REG_B21 = 29, + REG_B22 = 30, + REG_B23 = 31, + REG_B24 = 32, + REG_B25 = 33, + REG_B26 = 34, + REG_B27 = 35, + REG_B28 = 36, + REG_B29 = 37, + REG_B30 = 38, + REG_B31 = 39, + REG_D0 = 40, + REG_D1 = 41, + REG_D2 = 42, + REG_D3 = 43, + REG_D4 = 44, + REG_D5 = 45, + REG_D6 = 46, + REG_D7 = 47, + REG_D8 = 48, + REG_D9 = 49, + REG_D10 = 50, + REG_D11 = 51, + REG_D12 = 52, + REG_D13 = 53, + REG_D14 = 54, + REG_D15 = 55, + REG_D16 = 56, + REG_D17 = 57, + REG_D18 = 58, + REG_D19 = 59, + REG_D20 = 60, + REG_D21 = 61, + REG_D22 = 62, + REG_D23 = 63, + REG_D24 = 64, + REG_D25 = 65, + REG_D26 = 66, + REG_D27 = 67, + REG_D28 = 68, + REG_D29 = 69, + REG_D30 = 70, + REG_D31 = 71, + REG_H0 = 72, + REG_H1 = 73, + REG_H2 = 74, + REG_H3 = 75, + REG_H4 = 76, + REG_H5 = 77, + REG_H6 = 78, + REG_H7 = 79, + REG_H8 = 80, + REG_H9 = 81, + REG_H10 = 82, + REG_H11 = 83, + REG_H12 = 84, + REG_H13 = 85, + REG_H14 = 86, + REG_H15 = 87, + REG_H16 = 88, + REG_H17 = 89, + REG_H18 = 90, + REG_H19 = 91, + REG_H20 = 92, + REG_H21 = 93, + REG_H22 = 94, + REG_H23 = 95, + REG_H24 = 96, + REG_H25 = 97, + REG_H26 = 98, + REG_H27 = 99, + REG_H28 = 100, + REG_H29 = 101, + REG_H30 = 102, + REG_H31 = 103, + REG_Q0 = 104, + REG_Q1 = 105, + REG_Q2 = 106, + REG_Q3 = 107, + REG_Q4 = 108, + REG_Q5 = 109, + REG_Q6 = 110, + REG_Q7 = 111, + REG_Q8 = 112, + REG_Q9 = 113, + REG_Q10 = 114, + REG_Q11 = 115, + REG_Q12 = 116, + REG_Q13 = 117, + REG_Q14 = 118, + REG_Q15 = 119, + REG_Q16 = 120, + REG_Q17 = 121, + REG_Q18 = 122, + REG_Q19 = 123, + REG_Q20 = 124, + REG_Q21 = 125, + REG_Q22 = 126, + REG_Q23 = 127, + REG_Q24 = 128, + REG_Q25 = 129, + REG_Q26 = 130, + REG_Q27 = 131, + REG_Q28 = 132, + REG_Q29 = 133, + REG_Q30 = 134, + REG_Q31 = 135, + REG_S0 = 136, + REG_S1 = 137, + REG_S2 = 138, + REG_S3 = 139, + REG_S4 = 140, + REG_S5 = 141, + REG_S6 = 142, + REG_S7 = 143, + REG_S8 = 144, + REG_S9 = 145, + REG_S10 = 146, + REG_S11 = 147, + REG_S12 = 148, + REG_S13 = 149, + REG_S14 = 150, + REG_S15 = 151, + REG_S16 = 152, + REG_S17 = 153, + REG_S18 = 154, + REG_S19 = 155, + REG_S20 = 156, + REG_S21 = 157, + REG_S22 = 158, + REG_S23 = 159, + REG_S24 = 160, + REG_S25 = 161, + REG_S26 = 162, + REG_S27 = 163, + REG_S28 = 164, + REG_S29 = 165, + REG_S30 = 166, + REG_S31 = 167, + REG_W0 = 168, + REG_W1 = 169, + REG_W2 = 170, + REG_W3 = 171, + REG_W4 = 172, + REG_W5 = 173, + REG_W6 = 174, + REG_W7 = 175, + REG_W8 = 176, + REG_W9 = 177, + REG_W10 = 178, + REG_W11 = 179, + REG_W12 = 180, + REG_W13 = 181, + REG_W14 = 182, + REG_W15 = 183, + REG_W16 = 184, + REG_W17 = 185, + REG_W18 = 186, + REG_W19 = 187, + REG_W20 = 188, + REG_W21 = 189, + REG_W22 = 190, + REG_W23 = 191, + REG_W24 = 192, + REG_W25 = 193, + REG_W26 = 194, + REG_W27 = 195, + REG_W28 = 196, + REG_W29 = 197, + REG_W30 = 198, + REG_X0 = 199, + REG_X1 = 200, + REG_X2 = 201, + REG_X3 = 202, + REG_X4 = 203, + REG_X5 = 204, + REG_X6 = 205, + REG_X7 = 206, + REG_X8 = 207, + REG_X9 = 208, + REG_X10 = 209, + REG_X11 = 210, + REG_X12 = 211, + REG_X13 = 212, + REG_X14 = 213, + REG_X15 = 214, + REG_X16 = 215, + REG_X17 = 216, + REG_X18 = 217, + REG_X19 = 218, + REG_X20 = 219, + REG_X21 = 220, + REG_X22 = 221, + REG_X23 = 222, + REG_X24 = 223, + REG_X25 = 224, + REG_X26 = 225, + REG_X27 = 226, + REG_X28 = 227, + REG_V0 = 228, + REG_V1 = 229, + REG_V2 = 230, + REG_V3 = 231, + REG_V4 = 232, + REG_V5 = 233, + REG_V6 = 234, + REG_V7 = 235, + REG_V8 = 236, + REG_V9 = 237, + REG_V10 = 238, + REG_V11 = 239, + REG_V12 = 240, + REG_V13 = 241, + REG_V14 = 242, + REG_V15 = 243, + REG_V16 = 244, + REG_V17 = 245, + REG_V18 = 246, + REG_V19 = 247, + REG_V20 = 248, + REG_V21 = 249, + REG_V22 = 250, + REG_V23 = 251, + REG_V24 = 252, + REG_V25 = 253, + REG_V26 = 254, + REG_V27 = 255, + REG_V28 = 256, + REG_V29 = 257, + REG_V30 = 258, + REG_V31 = 259, + + // pseudo registers + REG_PC = 260, + REG_CPACR_EL1 = 261, + + // thread registers, depreciated, use UC_ARM64_REG_CP_REG instead + REG_TPIDR_EL0 = 262, + REG_TPIDRRO_EL0 = 263, + REG_TPIDR_EL1 = 264, + REG_PSTATE = 265, + + // exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead + REG_ELR_EL0 = 266, + REG_ELR_EL1 = 267, + REG_ELR_EL2 = 268, + REG_ELR_EL3 = 269, + + // stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead + REG_SP_EL0 = 270, + REG_SP_EL1 = 271, + REG_SP_EL2 = 272, + REG_SP_EL3 = 273, + + // other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead + REG_TTBR0_EL1 = 274, + REG_TTBR1_EL1 = 275, + REG_ESR_EL0 = 276, + REG_ESR_EL1 = 277, + REG_ESR_EL2 = 278, + REG_ESR_EL3 = 279, + REG_FAR_EL0 = 280, + REG_FAR_EL1 = 281, + REG_FAR_EL2 = 282, + REG_FAR_EL3 = 283, + REG_PAR_EL1 = 284, + REG_MAIR_EL1 = 285, + REG_VBAR_EL0 = 286, + REG_VBAR_EL1 = 287, + REG_VBAR_EL2 = 288, + REG_VBAR_EL3 = 289, + REG_CP_REG = 290, + + // floating point control and status registers + REG_FPCR = 291, + REG_FPSR = 292, + REG_ENDING = 293, + + // alias registers + REG_IP0 = 215, + REG_IP1 = 216, + REG_FP = 1, + REG_LR = 2, + + // ARM64 instructions + + INS_INVALID = 0, + INS_MRS = 1, + INS_MSR = 2, + INS_SYS = 3, + INS_SYSL = 4, + INS_ENDING = 5, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Common.cs b/Ryujinx.Tests.Unicorn/Native/Const/Common.cs new file mode 100644 index 000000000..e4b59a489 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Common.cs @@ -0,0 +1,44 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Common + { + API_MAJOR = 2, + + API_MINOR = 0, + + API_PATCH = 0, + API_EXTRA = 255, + VERSION_MAJOR = 2, + + VERSION_MINOR = 0, + + VERSION_PATCH = 0, + VERSION_EXTRA = 255, + SECOND_SCALE = 1000000, + MILISECOND_SCALE = 1000, + QUERY_MODE = 1, + QUERY_PAGE_SIZE = 2, + QUERY_ARCH = 3, + QUERY_TIMEOUT = 4, + + CTL_IO_NONE = 0, + CTL_IO_WRITE = 1, + CTL_IO_READ = 2, + CTL_IO_READ_WRITE = 3, + + CTL_UC_MODE = 0, + CTL_UC_PAGE_SIZE = 1, + CTL_UC_ARCH = 2, + CTL_UC_TIMEOUT = 3, + CTL_UC_USE_EXITS = 4, + CTL_UC_EXITS_CNT = 5, + CTL_UC_EXITS = 6, + CTL_CPU_MODEL = 7, + CTL_TB_REQUEST_CACHE = 8, + CTL_TB_REMOVE_CACHE = 9, + CTL_TB_FLUSH = 10, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Error.cs b/Ryujinx.Tests.Unicorn/Native/Const/Error.cs new file mode 100644 index 000000000..9cedb0fcc --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Error.cs @@ -0,0 +1,31 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Error + { + OK = 0, + NOMEM = 1, + ARCH = 2, + HANDLE = 3, + MODE = 4, + VERSION = 5, + READ_UNMAPPED = 6, + WRITE_UNMAPPED = 7, + FETCH_UNMAPPED = 8, + HOOK = 9, + INSN_INVALID = 10, + MAP = 11, + WRITE_PROT = 12, + READ_PROT = 13, + FETCH_PROT = 14, + ARG = 15, + READ_UNALIGNED = 16, + WRITE_UNALIGNED = 17, + FETCH_UNALIGNED = 18, + HOOK_EXIST = 19, + RESOURCE = 20, + EXCEPTION = 21, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Hook.cs b/Ryujinx.Tests.Unicorn/Native/Const/Hook.cs new file mode 100644 index 000000000..a6b9dca6e --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Hook.cs @@ -0,0 +1,33 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Hook + { + INTR = 1, + INSN = 2, + CODE = 4, + BLOCK = 8, + MEM_READ_UNMAPPED = 16, + MEM_WRITE_UNMAPPED = 32, + MEM_FETCH_UNMAPPED = 64, + MEM_READ_PROT = 128, + MEM_WRITE_PROT = 256, + MEM_FETCH_PROT = 512, + MEM_READ = 1024, + MEM_WRITE = 2048, + MEM_FETCH = 4096, + MEM_READ_AFTER = 8192, + INSN_INVALID = 16384, + EDGE_GENERATED = 32768, + TCG_OPCODE = 65536, + MEM_UNMAPPED = 112, + MEM_PROT = 896, + MEM_READ_INVALID = 144, + MEM_WRITE_INVALID = 288, + MEM_FETCH_INVALID = 576, + MEM_INVALID = 1008, + MEM_VALID = 7168, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Memory.cs b/Ryujinx.Tests.Unicorn/Native/Const/Memory.cs new file mode 100644 index 000000000..a7d60e611 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Memory.cs @@ -0,0 +1,19 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Memory + { + READ = 16, + WRITE = 17, + FETCH = 18, + READ_UNMAPPED = 19, + WRITE_UNMAPPED = 20, + FETCH_UNMAPPED = 21, + WRITE_PROT = 22, + READ_PROT = 23, + FETCH_PROT = 24, + READ_AFTER = 25, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Mode.cs b/Ryujinx.Tests.Unicorn/Native/Const/Mode.cs new file mode 100644 index 000000000..804d01a97 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Mode.cs @@ -0,0 +1,35 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Mode + { + LITTLE_ENDIAN = 0, + BIG_ENDIAN = 1073741824, + ARM = 0, + THUMB = 16, + MCLASS = 32, + V8 = 64, + ARMBE8 = 1024, + ARM926 = 128, + ARM946 = 256, + ARM1176 = 512, + MICRO = 16, + MIPS3 = 32, + MIPS32R6 = 64, + MIPS32 = 4, + MIPS64 = 8, + MODE_16 = 2, + MODE_32 = 4, + MODE_64 = 8, + PPC32 = 4, + PPC64 = 8, + QPX = 16, + SPARC32 = 4, + SPARC64 = 8, + V9 = 16, + RISCV32 = 4, + RISCV64 = 8, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Permission.cs b/Ryujinx.Tests.Unicorn/Native/Const/Permission.cs new file mode 100644 index 000000000..19ddc4f27 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/Permission.cs @@ -0,0 +1,14 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum Permission + { + NONE = 0, + READ = 1, + WRITE = 2, + EXEC = 4, + ALL = 7, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/TCG.cs b/Ryujinx.Tests.Unicorn/Native/Const/TCG.cs new file mode 100644 index 000000000..f38785db7 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Const/TCG.cs @@ -0,0 +1,12 @@ +// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +// ReSharper disable InconsistentNaming +namespace Ryujinx.Tests.Unicorn.Native.Const +{ + public enum TCG + { + OP_SUB = 0, + OP_FLAG_CMP = 1, + OP_FLAG_DIRECT = 2, + } +} diff --git a/Ryujinx.Tests.Unicorn/Native/Interface.cs b/Ryujinx.Tests.Unicorn/Native/Interface.cs index 59b1da079..0ecda22ea 100644 --- a/Ryujinx.Tests.Unicorn/Native/Interface.cs +++ b/Ryujinx.Tests.Unicorn/Native/Interface.cs @@ -1,13 +1,43 @@ +using Ryujinx.Tests.Unicorn.Native.Const; using System; +using System.IO; +using System.Reflection; using System.Runtime.InteropServices; namespace Ryujinx.Tests.Unicorn.Native { - public class Interface + public static class Interface { - public static void Checked(UnicornError error) + public static bool IsUnicornAvailable { get; private set; } = true; + + private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) { - if (error != UnicornError.UC_ERR_OK) + if (libraryName == "unicorn") + { + string loadPath = $"{Path.GetDirectoryName(assembly.Location)}/"; + loadPath += OperatingSystem.IsWindows() ? $"{libraryName}.dll" : $"lib{libraryName}.so"; + + if (!NativeLibrary.TryLoad(loadPath, out IntPtr libraryPtr)) + { + IsUnicornAvailable = false; + Console.Error.WriteLine($"ERROR: Could not find unicorn at: {loadPath}"); + } + + return libraryPtr; + } + + // Otherwise, fallback to default import resolver. + return IntPtr.Zero; + } + + static Interface() + { + NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), ImportResolver); + } + + public static void Checked(Error error) + { + if (error != Error.OK) { throw new UnicornException(error); } @@ -31,39 +61,39 @@ namespace Ryujinx.Tests.Unicorn.Native public static extern uint uc_version(out uint major, out uint minor); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_open(UnicornArch arch, UnicornMode mode, out IntPtr uc); + public static extern Error uc_open(Arch arch, Mode mode, out IntPtr uc); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_close(IntPtr uc); + public static extern Error uc_close(IntPtr uc); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr uc_strerror(UnicornError err); + public static extern IntPtr uc_strerror(Error err); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_reg_write(IntPtr uc, int regid, byte[] value); + public static extern Error uc_reg_write(IntPtr uc, int regid, byte[] value); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_reg_read(IntPtr uc, int regid, byte[] value); + public static extern Error uc_reg_read(IntPtr uc, int regid, byte[] value); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size); + public static extern Error uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size); + public static extern Error uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count); + public static extern Error uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms); + public static extern Error uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_mem_unmap(IntPtr uc, ulong address, ulong size); + public static extern Error uc_mem_unmap(IntPtr uc, ulong address, ulong size); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms); + public static extern Error uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms); [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)] - public static extern UnicornError uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count); + public static extern Error uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count); } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs deleted file mode 100644 index ff633293e..000000000 --- a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Tests.Unicorn.Native -{ - public enum UnicornArch : uint - { - UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2) - UC_ARCH_ARM64, // ARM-64, also called AArch64 - UC_ARCH_MIPS, // Mips architecture - UC_ARCH_X86, // X86 architecture (including x86 & x86-64) - UC_ARCH_PPC, // PowerPC architecture (currently unsupported) - UC_ARCH_SPARC, // Sparc architecture - UC_ARCH_M68K, // M68K architecture - UC_ARCH_MAX, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs deleted file mode 100644 index 8045f2dac..000000000 --- a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs +++ /dev/null @@ -1,33 +0,0 @@ -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native -{ - public enum UnicornMode : uint - { - UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) - UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode - // arm / arm64 - UC_MODE_ARM = 0, // ARM mode - UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2) - UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported) - UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported) - // mips - UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported) - UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported) - UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported) - UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA - UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA - // x86 / x64 - UC_MODE_16 = 1 << 1, // 16-bit mode - UC_MODE_32 = 1 << 2, // 32-bit mode - UC_MODE_64 = 1 << 3, // 64-bit mode - // ppc - UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported) - UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported) - UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported) - // sparc - UC_MODE_SPARC32 = 1 << 2, // 32-bit mode - UC_MODE_SPARC64 = 1 << 3, // 64-bit mode - UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported) - // m68k - } -} diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs index e634e0d2a..8b3e79b69 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs @@ -1,4 +1,5 @@ using Ryujinx.Tests.Unicorn.Native; +using Ryujinx.Tests.Unicorn.Native.Const; using System; namespace Ryujinx.Tests.Unicorn @@ -30,32 +31,32 @@ namespace Ryujinx.Tests.Unicorn public uint LR { - get => GetRegister(Arm32Register.LR); - set => SetRegister(Arm32Register.LR, value); + get => GetRegister(Arm.REG_LR); + set => SetRegister(Arm.REG_LR, value); } public uint SP { - get => GetRegister(Arm32Register.SP); - set => SetRegister(Arm32Register.SP, value); + get => GetRegister(Arm.REG_SP); + set => SetRegister(Arm.REG_SP, value); } public uint PC { - get => GetRegister(Arm32Register.PC) & 0xfffffffeu; - set => SetRegister(Arm32Register.PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u)); + get => GetRegister(Arm.REG_PC) & 0xfffffffeu; + set => SetRegister(Arm.REG_PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u)); } public uint CPSR { - get => (uint)GetRegister(Arm32Register.CPSR); - set => SetRegister(Arm32Register.CPSR, (uint)value); + get => GetRegister(Arm.REG_CPSR); + set => SetRegister(Arm.REG_CPSR, value); } public int Fpscr { - get => (int)GetRegister(Arm32Register.FPSCR) | ((int)GetRegister(Arm32Register.FPSCR_NZCV)); - set => SetRegister(Arm32Register.FPSCR, (uint)value); + get => (int)GetRegister(Arm.REG_FPSCR) | ((int)GetRegister(Arm.REG_FPSCR_NZCV)); + set => SetRegister(Arm.REG_FPSCR, (uint)value); } public bool QFlag @@ -94,16 +95,16 @@ namespace Ryujinx.Tests.Unicorn set { CPSR = (CPSR & ~0x00000020u) | (value ? 0x00000020u : 0u); - SetRegister(Arm32Register.PC, (GetRegister(Arm32Register.PC) & 0xfffffffeu) | (value ? 1u : 0u)); + SetRegister(Arm.REG_PC, (GetRegister(Arm.REG_PC) & 0xfffffffeu) | (value ? 1u : 0u)); } } public UnicornAArch32() { - Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc)); + Interface.Checked(Interface.uc_open(Arch.ARM, Mode.LITTLE_ENDIAN, out uc)); - SetRegister(Arm32Register.C1_C0_2, GetRegister(Arm32Register.C1_C0_2) | 0xf00000); - SetRegister(Arm32Register.FPEXC, 0x40000000); + SetRegister(Arm.REG_C1_C0_2, GetRegister(Arm.REG_C1_C0_2) | 0xf00000); + SetRegister(Arm.REG_FPEXC, 0x40000000); } ~UnicornAArch32() @@ -136,44 +137,44 @@ namespace Ryujinx.Tests.Unicorn RunForCount(1); } - private static Arm32Register[] XRegisters = new Arm32Register[16] + private static Arm[] XRegisters = new Arm[16] { - Arm32Register.R0, - Arm32Register.R1, - Arm32Register.R2, - Arm32Register.R3, - Arm32Register.R4, - Arm32Register.R5, - Arm32Register.R6, - Arm32Register.R7, - Arm32Register.R8, - Arm32Register.R9, - Arm32Register.R10, - Arm32Register.R11, - Arm32Register.R12, - Arm32Register.R13, - Arm32Register.R14, - Arm32Register.R15, + Arm.REG_R0, + Arm.REG_R1, + Arm.REG_R2, + Arm.REG_R3, + Arm.REG_R4, + Arm.REG_R5, + Arm.REG_R6, + Arm.REG_R7, + Arm.REG_R8, + Arm.REG_R9, + Arm.REG_R10, + Arm.REG_R11, + Arm.REG_R12, + Arm.REG_R13, + Arm.REG_R14, + Arm.REG_R15, }; - private static Arm32Register[] QRegisters = new Arm32Register[16] + private static Arm[] QRegisters = new Arm[16] { - Arm32Register.Q0, - Arm32Register.Q1, - Arm32Register.Q2, - Arm32Register.Q3, - Arm32Register.Q4, - Arm32Register.Q5, - Arm32Register.Q6, - Arm32Register.Q7, - Arm32Register.Q8, - Arm32Register.Q9, - Arm32Register.Q10, - Arm32Register.Q11, - Arm32Register.Q12, - Arm32Register.Q13, - Arm32Register.Q14, - Arm32Register.Q15 + Arm.REG_Q0, + Arm.REG_Q1, + Arm.REG_Q2, + Arm.REG_Q3, + Arm.REG_Q4, + Arm.REG_Q5, + Arm.REG_Q6, + Arm.REG_Q7, + Arm.REG_Q8, + Arm.REG_Q9, + Arm.REG_Q10, + Arm.REG_Q11, + Arm.REG_Q12, + Arm.REG_Q13, + Arm.REG_Q14, + Arm.REG_Q15 }; public uint GetX(int index) @@ -204,7 +205,7 @@ namespace Ryujinx.Tests.Unicorn } // Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead. - return GetVector((Arm32Register)((int)Arm32Register.D0 + index * 2)); + return GetVector((Arm)((int)Arm.REG_D0 + index * 2)); } public void SetQ(int index, SimdValue value) @@ -214,10 +215,10 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - SetVector((Arm32Register)((int)Arm32Register.D0 + index * 2), value); + SetVector((Arm)((int)Arm.REG_D0 + index * 2), value); } - public uint GetRegister(Arm32Register register) + public uint GetRegister(Arm register) { byte[] data = new byte[4]; @@ -226,14 +227,14 @@ namespace Ryujinx.Tests.Unicorn return (uint)BitConverter.ToInt32(data, 0); } - public void SetRegister(Arm32Register register, uint value) + public void SetRegister(Arm register, uint value) { byte[] data = BitConverter.GetBytes(value); Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); } - public SimdValue GetVector(Arm32Register register) + public SimdValue GetVector(Arm register) { byte[] data = new byte[8]; @@ -245,7 +246,7 @@ namespace Ryujinx.Tests.Unicorn return new SimdValue(lo, hi); } - private void SetVector(Arm32Register register, SimdValue value) + private void SetVector(Arm register, SimdValue value) { byte[] data = BitConverter.GetBytes(value.GetUInt64(0)); Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); @@ -300,13 +301,10 @@ namespace Ryujinx.Tests.Unicorn try { Interface.uc_version(out _, out _); + } + catch (DllNotFoundException) { } - return true; - } - catch (DllNotFoundException) - { - return false; - } + return Interface.IsUnicornAvailable; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs index c5d5540b1..5cd5f88cb 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs @@ -1,4 +1,5 @@ using Ryujinx.Tests.Unicorn.Native; +using Ryujinx.Tests.Unicorn.Native.Const; using System; namespace Ryujinx.Tests.Unicorn @@ -30,38 +31,38 @@ namespace Ryujinx.Tests.Unicorn public ulong LR { - get => GetRegister(ArmRegister.LR); - set => SetRegister(ArmRegister.LR, value); + get => GetRegister(Arm64.REG_LR); + set => SetRegister(Arm64.REG_LR, value); } public ulong SP { - get => GetRegister(ArmRegister.SP); - set => SetRegister(ArmRegister.SP, value); + get => GetRegister(Arm64.REG_SP); + set => SetRegister(Arm64.REG_SP, value); } public ulong PC { - get => GetRegister(ArmRegister.PC); - set => SetRegister(ArmRegister.PC, value); + get => GetRegister(Arm64.REG_PC); + set => SetRegister(Arm64.REG_PC, value); } public uint Pstate { - get => (uint)GetRegister(ArmRegister.PSTATE); - set => SetRegister(ArmRegister.PSTATE, (uint)value); + get => (uint)GetRegister(Arm64.REG_PSTATE); + set => SetRegister(Arm64.REG_PSTATE, (uint)value); } public int Fpcr { - get => (int)GetRegister(ArmRegister.FPCR); - set => SetRegister(ArmRegister.FPCR, (uint)value); + get => (int)GetRegister(Arm64.REG_FPCR); + set => SetRegister(Arm64.REG_FPCR, (uint)value); } public int Fpsr { - get => (int)GetRegister(ArmRegister.FPSR); - set => SetRegister(ArmRegister.FPSR, (uint)value); + get => (int)GetRegister(Arm64.REG_FPSR); + set => SetRegister(Arm64.REG_FPSR, (uint)value); } public bool OverflowFlag @@ -90,9 +91,9 @@ namespace Ryujinx.Tests.Unicorn public UnicornAArch64() { - Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM64, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc)); + Interface.Checked(Interface.uc_open(Arch.ARM64, Mode.LITTLE_ENDIAN, out uc)); - SetRegister(ArmRegister.CPACR_EL1, 0x00300000); + SetRegister(Arm64.REG_CPACR_EL1, 0x00300000); } ~UnicornAArch64() @@ -125,75 +126,75 @@ namespace Ryujinx.Tests.Unicorn RunForCount(1); } - private static ArmRegister[] XRegisters = new ArmRegister[31] + private static Arm64[] XRegisters = new Arm64[31] { - ArmRegister.X0, - ArmRegister.X1, - ArmRegister.X2, - ArmRegister.X3, - ArmRegister.X4, - ArmRegister.X5, - ArmRegister.X6, - ArmRegister.X7, - ArmRegister.X8, - ArmRegister.X9, - ArmRegister.X10, - ArmRegister.X11, - ArmRegister.X12, - ArmRegister.X13, - ArmRegister.X14, - ArmRegister.X15, - ArmRegister.X16, - ArmRegister.X17, - ArmRegister.X18, - ArmRegister.X19, - ArmRegister.X20, - ArmRegister.X21, - ArmRegister.X22, - ArmRegister.X23, - ArmRegister.X24, - ArmRegister.X25, - ArmRegister.X26, - ArmRegister.X27, - ArmRegister.X28, - ArmRegister.X29, - ArmRegister.X30, + Arm64.REG_X0, + Arm64.REG_X1, + Arm64.REG_X2, + Arm64.REG_X3, + Arm64.REG_X4, + Arm64.REG_X5, + Arm64.REG_X6, + Arm64.REG_X7, + Arm64.REG_X8, + Arm64.REG_X9, + Arm64.REG_X10, + Arm64.REG_X11, + Arm64.REG_X12, + Arm64.REG_X13, + Arm64.REG_X14, + Arm64.REG_X15, + Arm64.REG_X16, + Arm64.REG_X17, + Arm64.REG_X18, + Arm64.REG_X19, + Arm64.REG_X20, + Arm64.REG_X21, + Arm64.REG_X22, + Arm64.REG_X23, + Arm64.REG_X24, + Arm64.REG_X25, + Arm64.REG_X26, + Arm64.REG_X27, + Arm64.REG_X28, + Arm64.REG_X29, + Arm64.REG_X30, }; - private static ArmRegister[] QRegisters = new ArmRegister[32] + private static Arm64[] QRegisters = new Arm64[32] { - ArmRegister.Q0, - ArmRegister.Q1, - ArmRegister.Q2, - ArmRegister.Q3, - ArmRegister.Q4, - ArmRegister.Q5, - ArmRegister.Q6, - ArmRegister.Q7, - ArmRegister.Q8, - ArmRegister.Q9, - ArmRegister.Q10, - ArmRegister.Q11, - ArmRegister.Q12, - ArmRegister.Q13, - ArmRegister.Q14, - ArmRegister.Q15, - ArmRegister.Q16, - ArmRegister.Q17, - ArmRegister.Q18, - ArmRegister.Q19, - ArmRegister.Q20, - ArmRegister.Q21, - ArmRegister.Q22, - ArmRegister.Q23, - ArmRegister.Q24, - ArmRegister.Q25, - ArmRegister.Q26, - ArmRegister.Q27, - ArmRegister.Q28, - ArmRegister.Q29, - ArmRegister.Q30, - ArmRegister.Q31, + Arm64.REG_Q0, + Arm64.REG_Q1, + Arm64.REG_Q2, + Arm64.REG_Q3, + Arm64.REG_Q4, + Arm64.REG_Q5, + Arm64.REG_Q6, + Arm64.REG_Q7, + Arm64.REG_Q8, + Arm64.REG_Q9, + Arm64.REG_Q10, + Arm64.REG_Q11, + Arm64.REG_Q12, + Arm64.REG_Q13, + Arm64.REG_Q14, + Arm64.REG_Q15, + Arm64.REG_Q16, + Arm64.REG_Q17, + Arm64.REG_Q18, + Arm64.REG_Q19, + Arm64.REG_Q20, + Arm64.REG_Q21, + Arm64.REG_Q22, + Arm64.REG_Q23, + Arm64.REG_Q24, + Arm64.REG_Q25, + Arm64.REG_Q26, + Arm64.REG_Q27, + Arm64.REG_Q28, + Arm64.REG_Q29, + Arm64.REG_Q30, + Arm64.REG_Q31, }; public ulong GetX(int index) @@ -236,7 +237,7 @@ namespace Ryujinx.Tests.Unicorn SetVector(QRegisters[index], value); } - private ulong GetRegister(ArmRegister register) + private ulong GetRegister(Arm64 register) { byte[] data = new byte[8]; @@ -245,14 +246,14 @@ namespace Ryujinx.Tests.Unicorn return (ulong)BitConverter.ToInt64(data, 0); } - private void SetRegister(ArmRegister register, ulong value) + private void SetRegister(Arm64 register, ulong value) { byte[] data = BitConverter.GetBytes(value); Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); } - private SimdValue GetVector(ArmRegister register) + private SimdValue GetVector(Arm64 register) { byte[] data = new byte[16]; @@ -261,7 +262,7 @@ namespace Ryujinx.Tests.Unicorn return new SimdValue(data); } - private void SetVector(ArmRegister register, SimdValue value) + private void SetVector(Arm64 register, SimdValue value) { byte[] data = value.ToArray(); @@ -315,13 +316,10 @@ namespace Ryujinx.Tests.Unicorn try { Interface.uc_version(out _, out _); + } + catch (DllNotFoundException) { } - return true; - } - catch (DllNotFoundException) - { - return false; - } + return Interface.IsUnicornAvailable; } } } \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/UnicornError.cs b/Ryujinx.Tests.Unicorn/UnicornError.cs deleted file mode 100644 index ac324089c..000000000 --- a/Ryujinx.Tests.Unicorn/UnicornError.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn -{ - public enum UnicornError - { - UC_ERR_OK = 0, // No error: everything was fine - UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate() - UC_ERR_ARCH, // Unsupported architecture: uc_open() - UC_ERR_HANDLE, // Invalid handle - UC_ERR_MODE, // Invalid/unsupported mode: uc_open() - UC_ERR_VERSION, // Unsupported version (bindings) - UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start() - UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start() - UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start() - UC_ERR_HOOK, // Invalid hook type: uc_hook_add() - UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start() - UC_ERR_MAP, // Invalid memory mapping: uc_mem_map() - UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start() - UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start() - UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start() - UC_ERR_ARG, // Invalid argument provided to uc_xxx function (See specific function API) - UC_ERR_READ_UNALIGNED, // Unaligned read - UC_ERR_WRITE_UNALIGNED, // Unaligned write - UC_ERR_FETCH_UNALIGNED, // Unaligned fetch - UC_ERR_HOOK_EXIST, // hook for this event already existed - UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start() - UC_ERR_EXCEPTION // Unhandled CPU exception - } -} diff --git a/Ryujinx.Tests.Unicorn/UnicornException.cs b/Ryujinx.Tests.Unicorn/UnicornException.cs index 3cb693703..b5c5f980e 100644 --- a/Ryujinx.Tests.Unicorn/UnicornException.cs +++ b/Ryujinx.Tests.Unicorn/UnicornException.cs @@ -1,3 +1,4 @@ +using Ryujinx.Tests.Unicorn.Native.Const; using System; using System.Runtime.InteropServices; @@ -5,9 +6,9 @@ namespace Ryujinx.Tests.Unicorn { public class UnicornException : Exception { - public readonly UnicornError Error; + public readonly Error Error; - internal UnicornException(UnicornError error) + internal UnicornException(Error error) { Error = error; } @@ -20,4 +21,4 @@ namespace Ryujinx.Tests.Unicorn } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/libs/README.md b/Ryujinx.Tests.Unicorn/libs/README.md index 427044f90..d05291e5c 100644 --- a/Ryujinx.Tests.Unicorn/libs/README.md +++ b/Ryujinx.Tests.Unicorn/libs/README.md @@ -9,14 +9,12 @@ CPU simulator, Armeilleure. On Windows, Unicorn is shipped as a pre-compiled dynamic library (`.dll`), licenced under the GPLv2. -The source code for `windows/unicorn.dll` is available at: https://github.com/MerryMage/UnicornDotNet/tree/299451c02d9c810d2feca51f5e9cb6d8b2f38960 +The source code for `windows/unicorn.dll` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698 ## Linux -On Linux, you will first need to download Unicorn from https://github.com/unicorn-engine/unicorn. +On Windows, Unicorn is shipped as a pre-compiled shared object (`.so`), licenced under the GPLv2. -Then you need to patch it to expose the FSPCR register by applying `linux/unicorn_fspcr.patch` - -Then, compile Unicorn from source with its `make.sh` script. +The source code for `linux/unicorn.so` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698 See https://github.com/Ryujinx/Ryujinx/pull/1433 for details. diff --git a/Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so b/Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so new file mode 100644 index 0000000000000000000000000000000000000000..8d0948af99546040326cf2da088ba0a062d21f1c GIT binary patch literal 4500288 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ɣ*b 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

CIzOVJx`R-C;S|<3`?ujge(JNoBzVb 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 qjo%!8y^W#7rt&nRL37Z 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%0A_uRYXSXE=q(g;*CUbP4e+wjBN8wyF#+j?qlR# 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%0A`KO85&i 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

e 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Ξ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

#oD{XP@2P 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%0AX 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

O2K_@c46{2*z-b^Lu*YJvV`6GPA%uhE?B1ja&|s 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%0DqBKBhXnVU5~WcUTVc9M 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

xF+x{>+P5$# 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 z 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�Mdw0KGLN-!J~AL9Bp)W>99Uhl*a~r`y0V+pbICK;`^Za!o8&`YA@%u} zW^M|t^f4u1ePG{`#d}|!)Nxhk1?Dav;{L~?e1iFuPfo}`8bOkMhK~WR4eDOPS^L$J zb!BFHv^~4wn0`5_S2AUD@rIdREPYU)m0~~*2#?Jz3xu~$c#CVynVox^+1N*~AOUgW(fTKy0?G(we<+rK8bI!G$u5VseNaDkP5l~!=h)$%~>5N<})z1(&g>~2r2 z0e6j_Fb?8Y>SJ>e_JE6)t@~utTu`37^+C^ReL(6%wpz_DuImFU%Dz~1HaAdG7n3qy zB>b``EnBlrALgE~bMwmdVf2vg1yi&|`|hUgsLV*FZbeDI++6D<{M+Hb3_rJD6c9MO)MRhu*dNkA7cbj;W7*rtsTt-a zvh&>Dw>*Qq3Ax&})PlyIGLL-K$kmSK$m`X==wo?Lw*9}=X&df1<@UiQpOTTDkSve< zOy3mTtP`$_@x)&v4eSbi4?r?1sJw4bH;q9;c~D|UptpT=u=1wiejNlP-xf(%?o*d- zJ09t##dEf0zmsn{e?RgL87}HfoB08d0tdLg3W>D zmMmMcCrdsuA5DQ(g54~DuMN6wAU(cbrjIpV8K`~OVN~(>RnilNq$vT`1@=uR3}5}x zw{+5BL_QeQk|h2E@Z>%%Gfksll|F0=tO?AU&RMWNu=AD9lBY0U_`m4=R2Mq z@K%MO+?A$|5|4+HP<7(*{;S~>7rMb8DnKFUNzAx^8 zeTR3p_HA^IlBY*<-g)7VtRBgyf@bfPlDc=d26^N2m#uP|KFD{>IWH#X^9jG@mN3)!PPx0R%1$5_wB3a5lK59i#AmMyvFPCkOI zf*k`(kHdm(fUWwl!e?MV0oyGe`Bd?*46O3S%hpE)$gt-|WV5(^=3slQh41!0XB72H zoHpTa41ZFVK7X>|-1e8?a^CdzXq*j5`1pA!Ko8UW@aoI)NMB%XFd0k0bqub?mn~b* zm4a~ltsonYJofS7FfJ+6@>2TmS1#N4i=yWoK*JGxU>v*_{39m5ha=Qc^WFxlH1al4 zW%OR^pR9K{2OSRRy>eanoqF}Mvpy|lG5|ISR={`pH1Tf~Yy#{{5(eh;2js4~`=h&@ zaZdYnb4O@A!a`AY1=%LAWIp=+9R=}>YOMUZ7c*XMr(Q}vx%_&zUC&Z``IC{hP5G0V z{E~Mq@E_f|Y&|Vq@xi2gy1-|4Ej#ZN8GERnz1BN1+`2F8u0IbepH8@D;5xN?*?L0e z*yrmjRR5B)ep3jSzAYAzd$B8m5>2?$ht@t5dsJfCn&G?DbB+mcvV~JPR}|z)nYJL0 z?^$;CzYEq0Rs)vKl47uauxhYp@!jURM)l|#evTolNA`%wsLN)*yxvoR4tr=TSDeaw zf$_-4)d1yP9+WYB75~TZ-^)<$Zwqc->)l#%Wp?yK(wS23K3kTa1rWaKXGL;K>X)q- zI_*^NIMcS3rBieN$d`gUXI+Dl{OE&kW51cF@Vk+J!(hb+mYsLMy!LW83{&s1avo!T zzl+H4Aff#=0?4^l>5cz!uvQM)>!b@UJo9N4^=DIK4+f>e2$?jbFNK z-#00zP;RA0a_fqe-MYlDVcD8<+KR@-*DpHx8olidWmo%b`lrjx+#7xwGaA8;fPJ3t znSK8ybp13E*8LK4;?8{M%1|Ar2JlyV`EtgY_ffDoSh}r99#4VQfc=T^^N}>qf;EC& zEP%GdtF`@9@N*Se8?tvgGA#w(FO>i4JXL?Tve8cw<0bW?~G%4ZDl9 zeMs8I@LzGovh{NDzeoBQISsp8-{YA^mPEEoWVhSrQbi{@2Hpq0Q*ijqStq?;v{N(Q z-oGSi%X@Al=VEK#IX@&=3D^++y?IaxHV7vDx_qP`uLT zZ35bk{dUoS@L#o@ab9i|Y`}+2f%W^aS+G7ZF}2A@(y$2D>v1RXT?gw1`$wlO`sV6x zTV$+=J`mOw3V*yD`^YuR&N;wRum-SFugel79o>>)_%}K7x&ct%2#j4t`60m;$T6cG*6YNc+B*+8|1xxSvNhfGk~4%fVK` z`oWs{F7?N}ov!zj++0xHnBL1)jkxGfsK2=VuDGS%7@dZ3`A{x*PKr*WuVG}UG@Qkg zcqQ@MaNV+XiTJh0LN&5A@Usos5VFHgzHib8Hb+wWw32O_kGvzW&g_mg!8;A_-1W&h`EPYB(IZ5>lsGIBhpJmxE8x2wXUrjTm{K}>dRK{iZUy5PZqhopV8vkLVCi-! zeRu`f7}zx5?f#VWv*zq5XBMw3NMNo!SdiF;7M8HjFVX_&V`-S;zYYJ@t>^B49RD-; z|MoeCe+vJNx1L@2yNJJYIGTLY^uK`e{~i3d;lJ~?vx|RF_~ZD`d+oAyBq050zSk5a z4iqH!YyR`(bg6UHTlz(b|0(?U;s0#PkAu1{K|1{}L?3NCcmHkpAH)Cm0^;wlKmPD* z@iUJ9>enq>KQR6e%N>s9Onsg7%exAa81;B-*tY*iw)v)=ECdSAIy`gdfv4tAv1h#g zcaKjmJUO>7TYvXErfU|SA$ZdE;PCv}t42>MNyZQ+%9i9$Q6=-?_GRmbzhiuw;2Ap) zJfrZ$?^t&B_l2ix^Kw50&jLI_=~7z-x94tJf3S;5{#5-L-&bQU+$_fZzJeK7du&S-$l75%>q7CNw>qc?rd)fNuurxk~ zcokN&9(Lcd)gLyW!c^qB+xid1Hjr%x+^ut{Cb$~kw(PvQB=d@Pum&(x4gCn#3zh_v zWiI&$HUw4=mbSf=fsKPDz-|#o*eEy)a$`Z9_brIPRm}gTBanVz9(Mz{yIj-zG&3rlHt^;?KJnV54Af6pTEKplFL-RNUvk0)3=+*|`%> z=Dej~xnR#1_X)A(+@LlbabJV17}>=l<0HCA608dBmBz+l`gF|k=H4sTG@`52nv6bo zTdX|`vC}XDU)iC|bWMVlf;~$^wcX8t)qr{9aty2y?0-yLvg{&i=Tf_zOrE2=WtlX) zGVAyv>8&79dGBy-0)oZCa>3H|RImm~JXpGXBp+MBHu}td73xM$Gj|UzSKgNf`6+H4kLgnN_eRFbP6FlJ6T}jbNAtO}?w%?ABG1L6gkL z_H-E4ucCi?&$4}H4ewR_s|T9`yOr;@eMIlpvfs0kTf4AZVGXH|aYb1A@T{gEe{1+# zbIQ=QVd>hG)>AFRJy`6b`-8Js?RDD!!8h=}W$P-3Pw&{)bw#y_>AIq>b?sySy1t*A zU1GI*2UzK==`TKD?C#V}V?$@}VNWi7ZgJYcqv^-(6mIvK^n2^E>L!HSo|&CmUcLC6 z!{1ZIAD?pm4S^j4+aZAVrFyR#;a*Rm4`aUimeUVS<2L7m?)pm+UKYUe!IYDHm-oe` zZmfY9flHf|kA#)`8rmP&I|Lv>=Dt0`ig0pDl8CL0l_kmBO5FD1c2L}sAKv$fTzjLQ z5tAFRs6GLW0b8-mBfH>Qfa~ARhASEQW)PLce;S^Fzg)Ji6`qXvV-+{im-Qtnqcz-4 z<2JpH>b?I>o;Wadw2raRlr8-p{>tODpW$WuZj*Ya9@ur8W@FdqU97U(HiSmp596LG zh&P{tY{t=6>T@NAbryqIL}kHXmwon4+QtYxgMW?PKA>+fb&6f&@=Q}k=deAfa}XQY z8hoQ4a@X9m`Wij?S}ALTeSy}$Ui{@e$UYy#d5EW+(Hp3LZuwz)(DIwWZ53|yzBk`kk#1x6X2xCX z?JEA7@OMbUz@PVwE&kH$ZyZXLbUP~0_814+ynwW!s_obt;QAxsA{|mr39xanV@_X6 zpVKdI`5lrovpricVGR2a?5x<6I&?wBLkX|Q3iw7w+mIe!QCGqC>*tFv{Y`_{>u zCynf(I7FD)o2qJKcLd_RX)l`$vtMH6Ve8gFANq!c#A}pz<$QA4dXbYCIzDKB?&gJ# z4-ND={ef(IW2~(P#Fuzss}G15hZ}AS^dVJI%c!On{qj@G)@Pk~QE%>#2DNd?ufK%t z>VisMv%rN@!x*)sy!+rP`RuawHBp+hUv=Mw;M}y?6I~~HPkGx1Z?f~!KIc9U*T9#T zt-GB)Wg0K7JG#GG--glrVgqQjNruyGF55m-x|9C$>+F+oxKzJ#x%Ax&ecvQOJ^WfA zmAR+04X%ZmWoudL7aiuINS(CvD`**W^bR1pDZsA(xE=o9vh@omKio3ccklG9q`vLP zT3YnSqPk{};oJ0$p%h&4A1zyd>7>EsIw*Oo^;&f~Z0FGrN`7Q>+3yuq?xH_F#@xl> z(!AAuMmqj!y!0(H@;36FKq}g$^ch`n%`7ils9`2Q^gdSd;}*NKBs+G*WRIKlmips5 z=R4Jt>m`k#PIZ-8_@-7E^QAuOI9Mn7djt9VQ^zS~Az`1!?f5Eto~1L;`sSv?x9{*; z(YYFRC)t2UTV-#TrCVhFw?7pANi zO9!R>HSsfdE4y=8S)T8pxJ29b_R=)!70^qyyTu~}^sN%v66n_9e&x^S?Y^%d*>l06 zt^K)&qxVZORPe7TfqgYUWzR=6AGKV#9+u+~EE;05EUYca+n4SKYH^?QxRiCL5V(06 z)DOtsvlHZGc5|anNZbbC8vU6Yw+jzl(7*L??)}jRnENB2Kwk9tl=a243+vZ`rA8f) zur}a&RY+J;ElT!apL{|p<6f9bunw?iAeWE0s|D)@^RAyZf^~sitgdW#ZD2!SI&K=j z-C(0&&-b_+1Uu?;HwHEf=G`-V1ne}}6O7*h{?38rJ~3r=8hy#!sZb~N(A7`tq#tF; zkQNkhwxmw3h7RZfcE)5tK#BLw?RLf3i&LFFah7#Q$)A=w<~PL2o9<4q4Y22U(%lbM z{G^m)uaNQ{0V@LgQ>PzfPIZ6upwq5&ZlwM1<@8-1Ao*2}}VbWW?f z`xS8ADqPtW)qcOZdN2C}%2M{5dBjuFSr4}G>`bf~Z2CDV`(0Jzt^=&HJZ1Gr8S&Ba z4{ZETQr0e~U)BCY_qb|*uj$l&Rr=_wgX3oJU6_Y!qB3PY&hSgzR)zo1QubT1{FXj& z11#^wDeK>*;%d8f-~Ms?K+|W@2i64lS+v}ooH+mL>*;^Dr7W&p_VxoTy7aKeD}FaC z6ck>%FVg3PMDeo$^Z<#|0301VQq~dEzIC5x=Dq33=!2v_Lp|RcV^>|q1VADs`ML;S zYG=y&MlL_}{aDRcZ4d4q49(X@@>QQywZ}f&5{MUi&L*{IR_vocs7u-FboQlxG=Il&+jk&k zv3=2u3G9PDEN`EA?V2*}9H#R}t7k}HLPh>^eSWj83$MY^aWG}Q3JUqe`IoyN{TR&U zO*&(Z&JAc6Jr{)V-W1Czg(Cq+|D`GG%_bc^oNU%jmoiec#nj%?hMU!fly$F^1+h9T zcaQhWTXV)Xcr7{fUJ8<|cj^TNDYlw1{8n6i~UT6mYW61yN%gCR*z4;Rm|1bOaA4C2O z!9Uz(;vd%Td08VP{*NJlPQzb+?-}?%=i`42`O|ng`SVtjKVk9zf{*_!^GDl1{KNNU z_G7bPV?JyVY|@9VgB=C)_C|+A@pYA@j8uw9JOJ~fy5*CD&l`Q<*fS)Rt$CoERXN< z5v&4i3d|diIM^guzPhsAHGoZkrQ=qLtQBk=Y%MDuSd*~&3x7XuItMc2F#^^B=8eZB zSUZ?E9y4HVVBUBf18W8I#$yev1?)=_4_&{}{6J%u6C`%sExH=vdo$xv4z}^0%y`s* zod)y9BMG(+=8Z=S*cwEj|Go2#%1{p) zQI0;Y%N?!}xXM0%ez;D-)%k_9=b{{a@nU(=lw)->ove|N3{ z;x-3Y#}Vv(rae9&{p$T$fe}(|*2+UDSyab1Cor1-drm#7T~&WmDCj!}_h<`KS^Q?o`kLrg`i`UOq^vDRP$r7m zd4bK5{W>*>7jfJ1os{*T#%?J47fV^jDBK|{S(w46YDRKKtWV`)shn_7iy%GEF zqbcidxN;&KE+*@{YK~shjVo;oAKq)1zLCZ6~twICVL%bk6mrE1zg3(wzPnio?WVs zm9(%wZZ0z|m0%5EX?;xQ^0i<|u>4G00cUFu$=Pl>msQCMQN~oYPS%n#5Y{YZ_lft2 z20I`mUq<22`2lO8e3wr%|E9nyz_v=5+F$Fq%Uhnt7LK*O=K_{2B@AZ;bXQF5c3#BZ zmLZ`!j&y%RDYPXwv7SR1YAe?>=M5}KKdif853=h=+{AISp5X?`F4v9Jl@{C-{xD@- z#dqH7E=}*_?E-HEPv55{I=}!}FW66HGc z@tcY7Kcwutv2=dH+F{=QSR1k?WUI*XrJa%%!_Sy1{FPGoWPQT!52%Z*;gr%drs?nW z2EQ%V!B4yQAT~Q=?A~@GXV=$R!t48a%HA_bkWzOR!KT5|c`VpE*qYB>-YwLpkU{a&Be5eKKVY$#<=z)If8lSzp?<^L3BDC~be&eYUkYufcC8erJOG zvN>6-Ba}&9jDAgOweFcPH$Q< z|Khl>T1`1;GtHhy4KpHXwcYQECGGE|L-h~Izb^bP;P=s>u-Gi;q}7{lV|7Z`^*DZ~ z;c5QIl=WeUN6S#-z@oEB_ipL?O}e+o>KuGqtR8=PuSK7Qqtw(}vqng}Gi!v>uCbhC zwCma!Hz#DYYfYQf$42;uf9cjo=MC@D2nWt28tiA%m(Yuo`ke*BLHrHiulJuQL(x}4 z`+wcLEqA{CQu=?Q=4%n#rOgI3AT)sw+XBvo(ib?*ey6mJ03t2t7NSz##{B44q!Dr(A+LW@)!sxplzNCyG;TxZgxs@biN-Vz-SQ9``rcJoFFS=$8zQ-5y8aNWt-aheO*V zYu7!wGGLl))G(zz&f&iIHz_OXl#{x*!(?bS?h`S|OS@muF+=h%|Mlo2>nZ0vW=30< z^x5urrTa6|U8}Fq+x?Kk(*RH5zoo3NJ8^c~ql}Fvfx4O?oTPiZmWj=ycQ}>dcNm@_ zcuq)KkkO}KMc>VZhJ_42`aFuBlbXieTfdmrb8v=p>C29RC;o$Z0^f5OcYEXxX}sB% zzIwjb$~JNSx9d;-?es^upL6sfH?L{>ChJ)2W{-mTf!5;wkh3Ah1l0h~`roAELqQ#i zs(DEAxE)#Tf2FM7N}SLn`#F!A?q8)!lO5W635z@H;SUW*0(eM zY`E>u?C;P$8mb#HqNBxSX#KmTU&v7U_v_qa2(JA9PFc^C^zjk?aj1;r|(`)-};5I6e$yql#jLO}CEBb9!a_J!9X{+)*FvM;2|$gVR|=L+wjz5P$h zKEtD7NS)aqYgHy{DsIGGHSQ{`&D}|RY{p#&?tWt8?CkB2YfB*4bfZYwPY^GH0-wg)g87ua8AG5M)pOxUiBia^ArBec_7L8aCfh zeNFd()p2u9gq}Mf5H!N+NMIG-y+=_LVNsBofM13{mtK7)Tpqx<^_QfJY zzWO2qtd!prd~Hu$vC5_HUQtkZDJ3KL9C+`OR;;HBezl9Qg7+6?<2iR?@BYITTmR#n zk(?7M#cMHm$&**CdxS^p0bMTjObSREVL5OOZaV*H#k$tGamIMn6ijKN*NfX$+>RHo z*k?oFbKQzUV;8(sPH{Vk+r_7>SpUj*3HuOxj!M}QZC4c(TKPgolp;Qg+sQv(v3SlH zwTU#gwP$cA?RW-vO}INI^F8v;oOKPd8_3%h3t`Q8 zV@sgq098}>Ojo`slJjR1>GI3G6Szy#k9_huw5Qz7;-USC+-W);*lFyJMc?AagYm=B zQ?Wwix)QGjOAN*4F-Q3C`%G~B+L5nG{GPXBu>`F7l+_Q(4s|gb*|T+fZeAy|ZJDhz z#@fj4EX^NHfa;VnJxHH^Glw?;A{j^@EjHu2|o9_5f>t zueNL6LG0%qm$a>~fmr@Gd6zv$rkx4b0$k%STd{s4T)OX7&#hpSJDi&oF10!9JYp|g zoIeTZ*3F!xtdsfGYgVj3bM{SZy!32?a;XiH=T3Vryowt(?tm-XL{6i+J}{8FSoE7R zc0huX;4M<#_Shzp>>2-lwW}W?UU`WX>u_d%X}maP)W7v|=e(9Yc_PD0Kq7a_7OQ{= zZ;zpD24wKASoFPEjjI_^eesn;f`ept!<(sG2Ue_$^7x^1INH8>ug~3sujBUh60dE6 z@!BhuE+D>jF){1gxrHj=G!4e!j;%=QHD0zs6H~Zqlbo?8DyN87=jAKb3uLUPPTbF_ zZPqFwF>B7c#wB)Hj_slP+Esmf3{6>vWd}*fppaojr3`oG+OB9K_IenbB#EeabXpCE zP-Q&oxSRE!rWJcVA#%A6(t`JcPl2mW8#(0Qqu>MAtk`#+MBeA%N5EUJU9sP56dnd| z89(R27s0>DcS&cz+{184)_{6)Wg2LA`Mk6!^0%NbT(@H1aqn}h8k+5x9WG+PNKa6x z;>GqHR}Sfko|k~9t9iw$kg?0pBgO`YM<$SI9($hHi{JL^SFA~ikIyeEhjG1AHoplC zN@$%<-)xqIu2amwaSDzbC4BTD(PQF#T?DVaam9Xr4A0Kl=1McfsXo&MGm{a%GrK{L z=8Y5i!R%kkrL2qot0kTP+|>!WaMyr$-n3%XN|;m@Gfz&U3K*-EP7|n5#dTT{x5w_` zyIm=n<%tf`4M*?IE7n)|F1k*SjJ7Br)P4TPdETXJ!jnm1B~B)AUv$fg^=b+8b*k@N zCzp$&<~A4PJ**lKOElZ+O!cS@IigWf~f|5RX1H2ggPT}Gs{Dt=rpIcWfb!^W$ zSs7vyA;&$*ce8Cqh6tTZh)z?BpX6&-?7QVP4Hy@}Wo@JxSp%|{nmqE_VKLgI?;@f9 z&A57mhnGNThEDU|?Z2JG9*>X0|Jkwhc*l~5=PZ|$-6Ha$wiW9^zUM^VsrRf3ej0on zT+b?F1{Q(jN#VWBU0=6iy~5;)j3s4YrMIKI3wEjK7Fg}g`lXIbdgf2ZlIRPzWErE( z)$4Xyw8PcVzG6KfT+uEk+&=KxJ65cBn{XwL!(fefu2?%{+>BO6({Y>xA9&M>ea{^t zX~#2QHFvF86XMpr-&N12M}LUQU>e}An4B84t00rb2}eL(*YbZW`+M(Z3>N)>Ced1u zN1Z~qq%qk4i)~t($v^UEcPx&xTKvTCS+PDVIuGr@d*2L0gnKu6w3gwllH<@!dz^x` z+ixEnNA6p({zAfyzAYkYl{^>$ukFpmrH`8epFOlx73F!u;zE7 zyPEVqz$<9f2h~$_d}C;IZ2Zm9i*kdepYRBOH(U+(uUJpwyL<#26n-%IE+1)QV_;2S zbpnVjHcmXz^2N z@6oACpI&kw{lfz*_Sr4U)z~u_(OjEw^d;1-42y<+KCBVHO#_T863&eUg%Zy;us*OC z3C2gTZm>x(vRgkA?jYC%n7ba}+VJjjDlE14a?cO^Nj}Ztx9r_3_T5LsRA+S5?If~O z$cB*JA#F_iYn@-=3R|MfMPGNTLyX$y_T@Wcaxj-X(ixM^>*H_3Ui+REYeP1{!MVwL zrQWRY-=d;q8zV$@vd@3Q_fH>5*VXPV-q#ewjxd3|;jdP#zczI*OMgcrjt=?DWP2?7 zYQabR8I`MPoeyzyrkrh9z#5|`zz_9EW>S&iEZAkd?dLuc14xgB3a^OqbETz4dA?qGpu`V-w zo0Kuv$w$@$OPB#%xZ2?w{OF4HIA<RvK35*RNuJB}y>=-Kq54+Q!+VB4PqyzsnVAQi^Pp9KmCJb03!OC$l454NmGhZC@? z2dZr$VML!UUYHm7R*CR}G8&K$Lb(=oKA@cR_Rkr#zq8`J4}@L%BkA$88QCDRcMyO1l=H6x ztnjn?ow~WO#U_(De?-j*M>%=B)s5=tKV0t;EeO$rNAQ3(C+9BOM zaOVoFFGZtIrUMKpRoyF73dIC`|M-El-qRiR>W7H6?B{XQ{i2hOPAz@mDp(iT8^n}5@dZLvT9_qam^pWJRwI<~)L| zHAJ?6>_~`g9a-UPf;kJ{&H1npSp~AG5Lp6Qemt171zB^5tQ*;6h-?^H-nL-QDP&C{ zvUy|^A+j}Ox!Z#|^WTHM7$Pf2HXb6YMV7N8n6nvKLx`*k*;t5d2-!x6Y!X?rHaNUF zWTPRnQ^-z-$nxHcezP-}vkcith%An5Jw(=oEU_z?vlH2Hh-?tqT8L}{S?%s%&RJwb zA+l9ur$S`8gV-+D1ao#E8wimNAWMbF#*tOm1#`|I>kpBo zkS&JDaxl17?G5HELDm-{t44M#MAm?;vObuz9a(RPtRLAzh-?g5#lB$9X=L3YvPER` zA+il*<@Y$`;SK$d@5FlP(0<`7vovdIwHFtWVMgE^;=HHFCLkxhii){y01 z5zLwY0mi=&Svj)t5Lqp~x4M z?}LngR|RvHAsY#i#gVOt$eNHPt`6qxL^d2E8$`AiBAY-~+jP>tw?ge!`_yhFw#8Xw z)yPU^euT_f>n^qTm@_li`QbY5Dsd-k4)T$?Yu;bdzk*$?h>ewiB|MnShbzHaz@8=U z+_|&bOza(AEG-}s&L-Tp;#TLw25UF|pTckXh`U~}cHDWfA+RnVHV)S3!;XRt`mlMh z5g)b+HtxeVz@|Ky%-0Kt>EFQq)3J;C>FgMW#)2+-g7;Kzb^c}1|G@hAb&Rm_?yFDFa8pn_Aa~E2AJqLq-`F_u;(mp-d#rm$Z&a^9bOXf+hoMz}c;XVd; zZJ0e(^V#g}o!HVwE@R>G8SvR+!(Q)z{ZV^jU4(IrFlL%hT5V1kKKqn> zr{Y7jKlne9$)B;lmt7wyI^RLRT9C{dLT-370e-1}!-O|Oc;~zR3HNEZH~v3yH;mA~ zww(WXOFNx_y9MqYe3y@u`!v`H*jogkig@>5=pGyDdCvv)cCJ$;>{_%tc0CB2791oI ztIOPl1D%9f_F?oh_%9KDKGpIMEdQpH)@}jZFtKZT&sWm&DQG7EoJ!tz;I|aNA2fc= zS+k6KDy_i2DnDK4S$fZ?NAxtlT2l+(9NaB%-)^|?m-{Ns9zQmonEghr(woFD({(&p zd~p<>;s=>eod=#~cqZX_?WXZ*Ib(dr;5qg?^2nI522bwIC+&3s*3-;>EG;*3;t+2; zQ&-r1CN+a)Is3To?$}#>xg`2V%}3A=;Ci*BP4$g>Sq)A6F7A^%%J%O?~*=z~3Pg2UBk~P1=T8C*XbfR$npb zI_NZhleZYZ^v0c-_8Vja9R_|$a|-!Ue6aMJVS;smRfBa(9QgF|Zvbo*jAcSC3-4YFcYlQHU!0g}FKDbU zXueEeb0uzp!*t{WfupqUU=PqK*!xsL(8V94z1@1!dOhD+FDc}#S6-4HhX!G^X~JXw zo7{s?jFSZJOKvl|x9yC}6703+7G$L%vTkH$9$B5x8-K&d$^&GgqfH^JKqmE9;&+9K z8D6z+E+DT*E=zs#k^DXd*671>9-_YllPx6j5qCvk?H)|Vm~yZ#u(&A~vksm9X@H_9eUZ(G!*lU2#I|XdnDdug#3dFj%Vx>mVKzU>#sL1;<04 za}#QE1Vu@=(VDG39M;?~=We|LzeV2ljJ*OTYAh?Bl*) z?u#~e+3VV2x-H1*?7#+~gNuK!zSN-&_#e6_c(01YspR9>HwoWcH!8u_J($dyYQb_| zf6}=>+k5s#?`Y9@>RUB+oR5f}3f7x-&Ln-)Abg9rpS1UkXkOEc`{o@S&dQi~)CbHv z=qzcij%uyz$ZK#HwV$+ZBOJT0&*qlV&pW5t6KnSMa<(2*^9lMdxZbu2*V)9=&n~+5 z5ZrBW7d!QwbR6PMq)IgSVm7=|z3t=k_V)vN35NZBzvuf-x!9+e!I@3r`}EQb+YOB} zy#r3`cit!IzwS6`{bgoaGIX*Q@`VcTWT<_K2TmMhSL`O-c=a|pm@VmPg}({@&E^i- zdG&hOyhXSxpp^Yupj|c#gf~ohy6@;b)*-2%RpZ!y|37gLz@7Vs^B-^R|KYBN+dFs2 z`4s&r*kheL$UM?Jci0^=cW8!x5Pt96p#y9Y>|Ev!6Zl=n@6aaoROSvSZ*;K~JnbYQ zr|7r5)PP1nLuim)>Z!n$p+@S%U_Vd6q49^%ms)K~a=gf15 z6g+dkBahUdiqBv_g~vM{#KCeqGO-4*LLb%&R^r3Dz{-8t09cg|8wHDl-700yN8&OC zHtcgZ3pVD%7QrTc*gDuz4^|Cd-URwESkRopE&I3l)o-2O)Z%vyzb}`_={$)2X+6;f zod*CUiys0Dzb_OFulgahQG29j0I&Beu$N{ju$&W@V|%U!yGM?9X8iR0vd7BY&r^TzI%!?) z#DNfdIL}fkr@f4xYAYBGM)8o70k)3aph|`P{h%ry_IC#UX42OT_j>0^E9vDndp9Kj ztb-CcT8--Ka>bJSyH2!a_vcQz&%*tw`z9q{rwL>6&6)Ga1+eL&6GVGeCUYJ+fctsemy#~(uuM0ddE_{bJBjlTgOCr4o69@=^waj5j#nPzoVA8p2BY< zelO;`e1tFOZ_t0hbWfqn=k_<0EPSX7nMOk4tHJLWe$(Tj*e{Y`lVGD2Wj-S6q1 zeKvEpc0GELeY~3!NS79LUdnwV_ZD<+D`@BZY{1M;s!=uV-BY zw_YV9?+Qwjgqb%b=MPU>V*BxjiEYL1pAbp8SK;<3ZXYjh`3ROU{__>F_pLX9P2o;$ zda4&Nf0wg%mHKu7^M5o0X1x-AA8t$DX7unYGtT8PAnbk^x8D-GkhIB3+_vNPG6@S_ z&RZ~LWje^uBL2-IpMPC?UXK_|{G9?H!k?aNu>G+pAUDO@{_?*R$@#F)UjkVf_)+|A zb;^6Qwj2F};OYHdy_2&YuI)}3?HSvbWeguc-qL&0-s`V-vb{ePP%b)Z4geodnD)x&mdk4zfAaVXTP?SR(HR)+kVZuWIJcBJNMH} z`9m{h{uzo^+EBiL)=-=nxq%rspnUMlf}I-?FFrgg7z+T#5f+9DlV)H z>Y{XwyX$XR-|I8$2WSmwdYBIE8&U01k-Vo@F^HD6j8l{Nn>z=8r|`FM4*trHFn*qc zzb5>xo`b(Z{H>jXzghg9J_mofU!l^x%d>(IrtmL zU-3EkOX08d9Q>7hmHFv8_-nvlew!E%+Nb2YnBKwmotf3^5KdJg`&@Hd0M$J+iT@i+Gv z{fWKf6#f?Qr+2aNZh+Y*;#>Qd`wlX0USXu?vI*byEDZj6axDK*cx6mZJdFPF&Xdkr zO7DI(?%M6ydZ3{4a-Bpuiv{Yh8-LUIQ=7EfV9;C4T2I;^Ys}t7hMZ2qo5J5Q{C$q^ zw!X*S19KjbJpkseFr)7!+36CXnX&h&ppn;vlg|2a_G1z129@Zby0Oz*HO+YPu9H^1 z2|FmQ_CmdzR+%HT;jbEh>G@|FST|S$m}@)4kKV^|n_5`Xw!<;pwc+kn5~j{2((YdG zxRWsEaMz2wi^Lrt!BSw|KI}AD7uZwPmF+J7oAf7O5-<6P-%_wquu4U2tP1QX*z*l0 z?h|0gz*INa_%UtKbX=XjO-cFMakq}UwB1p{=mpE^cQKg<41wi?=^PL{2ur4VKwD&! z$V!nthwow|;n4$bda>sSbI7VfWT%iNkZJmC+lo9zpmU47Z($Eb_B_MM1KW<>r3_hn zh%An*H$>KiY$!k`?WGghII{E_h~@~^(p+W;`dL)ua*(|nU)b{g`JvrK-68)x6wb(!d(sSPM^VDKS8zNF6Xd& z-qw46QubW~w-hAm3X+!=B(@dAWlltbd=`#c+O4=bxQQEXHw&8T=SY0kiBIM4QG9x5 z&~JWE;?qI?6nzK%fcQMbce`&zvp0Qfozu6PbKA))3mWMkH`{}gh;3Ilm)iYoP?74} z3H^jSL%1Ju!qquEOL>QL)vm-_6V`)eY1x@3t4Y zN&ju6KKo?OF1=EitJ;09zi^I-h3;wuK2399O!B4dhs-zr;`c9KByLBETkr2-+=hu; z)88@Q_&to9RPSQ`Z4kf2`%c>TC-e3}k6d$R?ldi)$DV=g1E#)Z*-BJ5W2riNE6ZI{ zZ@lZn_Pf$ea1{SP;?4&=wrgDYb7rP(n8-H>!VRVbK`;n{AU$W!IdjfQ+f>swZPS{z zX`5E)AQ%K8xu-$e20>h*L0mx)JaIYwy{6?VZ`D ziNy2m=Q-`<_rB|0|NpPO_P{+xsd&iKz`r!C0`~m-@CWlZ{jBVcxzOC9Q0yEE=FZ#1 z)n2LCl=<^CHlrWB$G+cJ{9Aw(!_-+Awby6rZom_8b>Ap!zT!UoBi3_a*1R|-tPIws z=}CH3!7{LSNSeqULhmQJ2#KtNB#~`@h%+SC{*ykRRqqqfmW5tTQn{9L=(Sor2z%oH z0QQPM!ah2`<&k~JQCKVN9LtY0q}?$x=5E=C+}WI%-@DqvR@Fy2&n*`|s(eswui7_O z`Jm<+YW+yv+xg#Nj1dP<1EJ0=w@CPYOn67{G5&0aP4x**`=r`()pp9x+FC(tv_pqW zGFQ;7x2vtf;1^3Wm-E+g^_M!QJBB~QpW;5Ma6a6ep0WB2r~Ifr{CZr5zFjyG*p$44 z-sCE|e*FkPPN1voM}NZkiqG9+Oxo$fLP_Cs8ZQ+4-wgW28x68P#7cO^H_`T=bFn%x1bN8FwW;$iYc_&DfnUmZv82WUGn+8wGHHFRmI4tO= z5}cjYH>vKJ4F8Pv(O=$UJd&i7^+eYG_|%lyN5qhI`^ajM^&nGcpKwQI=lV(0T@$k2 z0=vtUvF=r-1F`Q_ZU|jQh+S=^`7YNOZl{0c-0h~?f7ldEU-MmnO?vcl zunCV|;OCrI^5~Vq#yonJuu<5OY+N z=9EeG&PzfX*?VXx>S-}9;{WFXLR*C|gOrY4`q}~PPp(_*^OP;N(bal%BeHR1Q^;OVR8Ds*_frkRs|48Ij>Y&RBHHlH*XW1Es^CFVpmA~Zvs6YH;`o%Q% zd$3>s-;`l@c~WQUobqJ#i+k&vLT3djbT>+O^(yrTf zHZ6UfOPq_BO#4r^`#4XWRasVXma-h`m+`^QnsqAjGBqO6hd!#!Xv2Q;oE2lY_TN?5 z{>ei2rOlYdep&H~@hCICW$Fb4t3S#jt3vkZ0Je5AYk=j? zy$Vahnmw!;*6LwtSi6UH!#X`|0M_eaqp$&3zWot@r(pdaHV^Cbu&iZIce@Si@#sZ= zO@HrU6)*{3J`INOw+7Y$`;0v%QR8su+^L#BsI?nu_CtRxJRahMD!(dNi1@m}@sg24 z4cto-yo?`*#mI9){Av=^$dB`+Mt`TE>!a$O+C{=R@Sqjf{W}}5eh&-(hV+8nk9f%= z=~@Qs^{^^f4@}0>@`zp?tlPsHVVy92Jkkp5fQcJ<P1%hVf&rD1w` zo3QNZY!)Wr)7cVC;;XYw*bq!F3q{MEm-eu7n53Vs7l(`Wxp$UgDsmvHtr)^ zL^k6i+d{U8OpRTYfAYm7)4$T+GTuh^7~8+=Y*`%HrjM*1Sn^XcA;EQw5wos~=-Pf%rG1X(?@LS~K~i8}gAgARG6QwIQ297PI4iwQaKp*{qLj7}=bUYzo=D zk8Bayf{$zq*`klEG)wvSk;RcM7m!IAs7IDX_6#e2RvDlj;0*%v^I|G5+tCX=#OjMx z+FoI&Lm#qIWcjv)?}6E}QDl`qvKeHxKC)$G4alBor@PaB1Xelk?jtKlmhq9*AnQd| zs5~|z8}gB*k&XMvdXdfe$VQMYB2(vcRGK;IGL3A*N4A74TvpiKHnK7wS=k!x2QoF5 zQvUIIHPTP&Z8fqwWRmA*9lln{I z?sCY=kU8bnDbq#2qrCgbDv;Ir$m)|DD%zR31(7FotemPOXmz1eciQS&WE04o{?i=Ow4vIBY!cbo zA^uS7Lu!4(Im_g%i^zAyLuVD9AMjmfS>C=vTL58Gpy$x#faCcU5h)Wh_}5&n*I~6> zq}r2ZWGQ3?%l{upPh|NsfnC8H?er{1mhq9*AnWy!H6j~A=A?^L-_pp2kvZks8CUco zoA8m1Ae;4(O(R=E79$mNR{YGfNevIbdS&Q2bpSF*_@9o@F&U# zvV!Fw*_My223heV3cG7W7W0v%kyRrr)PMIPOCrmU9lC<%ysMf#8$s5LtWNTe_C!B( zuJ%_s?ZDc&>zur_3ro0b!JX5`m~#p#SKG*1kzFP>opQyy)HFI}00t}Kcg25EpV7Ng z^c=r=Z}@bWcPqQj;#|lL>Vg#(s#&P#sx8&-G~;dtcTU7<2N5!HL@`u zSp%|hA6Xl+2_IPxvdIE6)&3!yLiS`UepVS^ZtX4uv*^vB=ahA49GgWpi>y#T9%j>i z4q2fx5JR@?BdbNW&#-;8a_cZwH!PbL%vJqs3>g)irNgvrbvN<2w9I|C(h1$6svaFA+=&#f- zA6W&m4Ifz@vQ1>pI`Wlvf7Xm_%SV<$w(TS9M;3VWPMeZ1W5~kD^7YLs1I(>2cg?LA z&?`pINe3sNH<3k=Iqk1A2Z?Ue{`$zOkk$CelE~_jIpx+V(=EtSKC(_^?LM+WWIf0V z&2=Y`4f@FDk&XGtHjqvG$cq1$_7|DjBUEFPYwWzNM3(cBC6EOw3cE`oEA^3eAge^? z%oClqdH`7!vKV=;&*`KLj3bL9bJkZFcXovsw^9boVXGQ{E}~b1o>TXn^_4ATwa5yU z+tR<$-fQ-|`2J~r?Jth3(MMK~tkp->imU^f6CbCZbR+9T=9EWgeRc?0zmIGZ*@%y9 z0of$7LT$??vN<1F)S&(FkyRnv@{uKx6+fo1e=W#jKC(_^)jqO8WXS?DDFYM8nvu!; z#GJdRGQgae@qjibUP7-OJ*N&iWo#Q+53;9PHoAgsc0QK{LV-~qSv9gbA6WykO=S78 zs$O>5kQF_)u&f7JxsPlZSsYoR@-c-h;Uil_*5o7GLe}OZD?JnnbRx^wFFg)%Wc@y} zdSoL$vQ}i1KC*6PbI1zyyX<-gmXYQA{H~xmUuq)HCXsC+D+=?6T4PgZkJOnN=ZuPV zhBqC0liw2C_2wyCF8RuwC`ofy8Rp7@^j28D<5YKrW+xA_BZJ&s|z9b{(a9Od* z@ImO%pAMB|+U4)_OESDYVfxjLUt>>MF;ctpYof4UC%u0C(d$d4vtZ24MNU zc41?%ewZA)kVn`wtk1(1V7(regY|e=ARG#Gdsr!~6IQHzF#V{6b$Ik@VHuBJ1FRi( zU(Ia`EbU<#SR3qqnqDug)nji6)&i4w$|L_DhoxZmSHxs9mOcHP!y-(=r^dnP${DH+ zn8a7vqn$iOizmG3EHi$2E@!Ud@EBa;^9bvlU2l+k2Tq4rXX;|@r%ukF#31y#D!Y}f zM%<*HW9@C>2WC{b+u*J6pEF#@vrbs`^H$7n`lT3UckDP zW^WF*eBO%jLrrfPHg=e_mN@W8fVNhi?EjdcHe+qp;QsU{=MEcuc`6E?F_wwRp_K0++5B z&(q?Yg;ia)V%{5ouDIQXWniC_wtz=i^qf$j^eT7wDqu@6-Chl>_(g7e^{^&bx0W9% z*!(ps#-j4U^rIa%bS-CmwD9%72CrK&U**ztn}b!hVNYX$BFZwL+Ik~U)RDs4vW8h#rToNW?!ig8L~t+2`0y7Qw0Hu5^R zAAPWv4!6BwSi>9K_9kGxZ*<$6g=IS3_LgAPZ=(Iu%H$@ju8Vr9g|Fzo*z0!cV2rda zhxPpPit#r|XU=bM&muQh(y0aCuEvQ{9_!F)IlW?BEjm2H8ewA|)(YE(se3yty$)FN zR=3+eSigr2!jFqov`V5tr!<;tREJA?~3tGjg7zt2Um>$ zGQCjgJPGT0|BCTIEg$D#{U31aEyFtRbn9)wdWYPsxS00(gKids4S&epzE;CJKg>Bu z&EF)fW7w_N3~Ty`TQ3c3{8#3rn%izz>Z7Ee#sS5ckEf0&{kNL#MSBzh3VX1&cN2wcDe#FRw8vZE$^op?|dfXk;6%2JT1}r)f;oevV zaWYqFMrUDc#pvUA;E3#7a*s$?D9CO6vbA(F!l>aS%2H=~(5?N9+uuQ0(dXR$j={p8 zclp~R{!%F$^8U`FGxvp^{&MrC_dZ$F2Ksa#TZPpmxEP)f5ixCZUg^^_{^;s zM>SRoOZ~uIrYd2hu#aeCg<4qhCvJNUu*8DfUJI=Ar*3;0*er~rxayHG_rgXNSBzh1 zVHtw8|8m8+L}TNysb9J6&A^J6-1Zh>*XIt-f zq(e8#s~0=p7d%hBFIfEm8jHW_XZO_gW9pvX24rDm68FGmB~f;v&o^gTj0-?%-WBo8S!fdyR`=EVeT@*9;tq}w~8NjgL4IQ&wBT| z^nr{o16h0iTaW!pSQV^N%8g1t^=^!_*5ZWwxV+x>jKc3rtM6&mVIqUQ;-akaQY*ar zcL<&L|0D?xkpywi`}OAg4U^cYxlh*k9QNdqHe?RA3@cW|WXrH6n0O_R=xxCkH74b& zxRm}1R%?}!+oYcL${Fs{VO75B3C=XZaUHWb9;wA`61R(ytPzp+mh>rnUqGnaa&51W zBqHYh&ONw)_5TEq8|71Pnnsa zMddsXq5jJrP!@Z?iRRyJbf>T6ebIyI-%7Ja)a@T}vc4-o`NqHhQh9HCXO43cxb1vU z)_4zg%)JVfgIwA#1A-*Wa`Jyf60bD6o9M2IF6Bf|V^zkacFG5g^be3RxH)P@%Wl@> z;sL4mQ`qZ&aMt*iWzVWd)Jy9QF*DqJ15rNRWe5C1#z0bvZ6IZ=_#vS{*+a5M*s3VP zVzAOOH>-w4ADT7(B7X9SUJ|wq`>W-)3f2tE{!`Ydx6-s%-ibKPcSTq`3jXH4`Bft+ ze#%BK`b7`Vy4C=PU}2a#OJnJc!=kVU^S3(f z?Y9wY(_RB^=bw@_KEdDeNI7VMrQ=!S7XnBNS2>ul%Yih7s*d!dAA4%n_`Owb?qFr! zl@Cr|t?uUd9*t^&v534%ZJvL{-qh1b6R}5pn8$xtDrB5L0}rEoS5uaxp}o?ME7y$% ze`LpwsAi?VMMcuA8aIJwW?k=;C1J&|{5Vk3u^EyI=3p()&Kes67+>Vw z{YrDUiB9}Elugm$5nDz7ME?RyD`K*8SQd7P#^SK*=VtBiREpaKtP}PnO|J=7Rhu>E ze()Io+F&EFZ%KNnd^o?PE_4aGXU?1?&HB)3e16tABKg5t!rR#?q1{*QFz-R1K8r83 zcby%&f6(20sk?+{aobv#HRrCR(V+{5LxCx>a1C0smGX3Jo? zBUxkEY74CIonni&YnhV4e3lh&Rqmx-Pom$J%o=g2Gt3LEb#nC`r{Jtz#(AxiGxv<# zxa6@bd^*${48G~UcXLDTH!wFVUwFc~q{9IA>Mx+b7khkl$*P0c3mqlFtZH{O5*i7M zJv#WV&}q`F67_l9)-`60`2t~2*+Ds`-VqHvoc^dOYo0m9t>kAZEP6TpnWP8n_rfY+ zgI6%FvNmIc)xrj@q0i*EJo5hrSU>DBikPegHU@i>#xk&~YqQ4vtu&W#_rhwf%NqTn zr`ie1{OORyE&N(L+(#p#JAv*fx`S?AmENzjbx%cPZ(|wVSWDJC_eFeerj7~U7XJ$H z(nrw#!PU7{^yPGj=vTp83-AQIvjA^~4|sSrcG}@%@R#%3j3?*0t#4wz%ntj75%Z0q zgfvyEe2?O83wLiX;4a9xM+yaH^1{fq>ZcsTHQPVjrLNB!9a5)g6IiQB2dy{-9!Y<3 zqt%uX*ZjNoW$;F6JJzVNb621uUX#GkSlA~G3?$Y9pN7x{&?B=X_hRI@MunL%7zoubznqD2c3$S{asvr2-D{F678RHvdQdB4( zN7M(~%-_f4>uOdJIvNohP_o}+iu0*qs|DizK7Nx$3~`S}7`R%a18K;l zT})vk{9&9LS zzs){`W9X9c5&(MqGVnY)bLc!@bZ8OUSs#?afXFtGZ6cF7o5)^n$&&n8{3z19Eo<&o z(f;V)jC0m1@@oXa*PEjbM(z5R_hIw<;Z4{Y#a5&EqtZ^jvw@FSlvG|^62By$P>99v z9`qM?@3-7%!sx-{_Yu?Yd2FR$vOB+}tcM@XejNHu{MPC&XX?7bmz7jDmc$!WZ%fXY zS%I3ktHs?6?$nr6wb#^E`9?ABco%`2xGvb?t`v9exZA*;+LtD;^KDBne5(K-h6i4{ zQ-2a(RDjRJqXl>lURHpIE9f5z@N#&ihfBPx;c@s8eoJ~;?_#KN2Hzg&I;*}U-kcxU zn->?U^lHOh^6zjrjJwp|;cgLknZLtb>0?-*{yW^&<8I{daMz8yslUVBB<>de4tJZl z+rXU~|Eh9((rz22@1c;>HlAlS(J~@8+p?<1QXX!}n)`vc)9>qMPG@}|IM~CaPG4S0 zM7UP6l9OrnWShym)PoM}EnrX0ZK(%xGFZlb{qQCD3;8YO%lZbYnm6#i&1tzCxa;hj zRNb<##zIwW&fzwkw)zZg%3CnPv+&{qJn%T?Kky24WUMD|Q;L2WybAs(i_4p2!sGA+ z{0SD9(^$fj@MgID7CZHDv6+Imz$=BTc2J$uqm8)KZUJR|rU$(?^qwkubSLyhMTf}& zNqdoxAn!%~Y)yXBl1pDOgM1YE#S#G0Okdab+@GY@wHsuCmll+Hn7j23of|4BvGOcl z$$ntBJD#n0nS_tRd8OR)-*?V>Qr1U*?Kfu)-h}sLuqR;ZqDf54xL_Q=;1=``dO(7SQ|{83$*lRVLdQaHZ8UU z8}_hG*p%j0%4yLP=zn1^;JKc| z1nY(A>sD>B0hp9$dBkleY#64;qaQW})8jD$TZBDYxisBQ!nR<;8k>XlztwGT88!yf z-EP5VVfWSC7C({x9A-_)RQO`BX%DN0O~KUM0QVA)Buwn--^6UT?A@UGk%m>gE$cd? z&<#t&biDzw=g}L5mG!#gF$JrJU9b5u59@>F&%H_bvamszp3d8_37DP+(JJ=GU@}&e zN8DDxreJ#9YG9Kvy-e1_#9oPVX}V2W_KG#u4jV&Hy@O);(PP=u^#(0_y51OU+T+JG zOv0zfW5Keghb3p(i)&#CJc;=nZuPa)QrHqqPlHNW4)$2hZ7rhI>Rlue+R)^af*ep!#xvMb*?Y%X>dXYJXU<#9nSWBe* zJ|1C|K@7~ftoYx7jiTGL#$kTTQ_a6VSOWGs0rZ2`x|14PI2Pg4=GO0%5oy0> zaMz1FRlZd}tJ*ITi|;R)&6ipm+CZ-#z4OHW1u~~&%&xuxWH)oCWT8{SSN;^rGrCU` zU7kAr#bL`ZHFolZkB@a*VM;{k#q8fF^FMJfcH6NLy~EuO_Q1+LY!Frn)7zObST#)2 zN*=K{4NH000<70#F9#d&ut1#l7pD7B3LEp-tAs6jSS_rm&%Hj{0BeNl<*Wsk@~{l7 z&BJH=;>|X5UdoIpDRi@$6+y;s(ajy7v4gcTi_$6VA!74N+{#V1|u=nyi5Ns)ly`-c_cs+aqeoS!^g#S15 zuLZsVf2BP(RCj^!zS3=B&)76kk_t7+&~WF+3;1-yr%GCnmZXoCWRCI*!6hXrY>OY$ z*pI&3-3~9niajg`D}w3cx4_eBuVH$7T?z}p^7$ZftAuU8%Zl4AX4w$G>)_SsN_mh+ zSRwt~G^zlX?Y}mtwVM87^0UPwNS=fMwEy4OdY!lY!VMWzu z`1G)p!+Jb=aagy9C19N%)&%Q->FL@A%fR%urqi-_u9a4jj{Pu+@6&~;dPOK=ATZir@x~e}>VIZtl zyYn)w-Ip-5p0w`mt82yC$ZRXzb$mO;^3zO1*_u%K~khQ0-(owCYaSDL_Ca3*Y`$M8X zfqusav*!0NReP`WDI@52V%==-)p?r+^t0#}e<*9bz>bqOK8Odcp#)*%OP_rF(;6;Z z5D8vv|KW}y_N1QIK8yYWo2~qoM_2=_@xyM`0vm%3iQhb;mx0xO#J%3#3mbrOoxXZR zZwOZUQEOht-@?XWDcBd4mdR#d#UsvMsOT-iMqp!_-Ucl3F~;qdALYoxHLRb1JZruy z!2R@5w&-4GE=#JlhYIAePZXAmKMCZ4(X26J`6D))V3RQA5BGJ7&DWMh)q;k|Gss6j zSy(P>Km*7JKSkIjoT@xH<4d_R)*f74r2R%!Y|rC1I+iuxhX}r{B+7LlH)?)wiu^NK z^Gs^+qa`t3Z%voPRM?WZFMT%Y@!71=CIfN&zC*@{`Z);JO{}wWd<7)*(!xtA33X;K zg{_t^WQ}o5nRSzWZtMH5^nL0aM34;$*O4m5?A53b;x>!h{5qKa?vXRTOh(L+t2wTY z<8B^z(TS|lEPkr`tL}=RZt#_QYk%Un<%&|T{3?15UYCZzR6ZIp-%NJGn895K?k*H}t}x2%fy%3z4Cj63TGU2y zGk}}ViyOkIpY>t6D8K&5I|{Dx&2d>vk@h!-`?9;T#%+SN1BQ~0`vE^ffGvJmkhbw$+NL~(Mqy3B4 z8iy;)FSdisMMMPr2DFZ0l=owgl_H8+#I79`R!n7MphO#}_@1_6DY(IVy)`Vb>{_rrS6y z@pbn)V*)k@yU7~th#yU`s&BdV+F%o~{GPw)b;726>ca6I~sQtDr+?IbgYy7}&<2+}*oHkBQeLku1 zqVN%Qh9dEN`nUgZ#<4PnYl4l!RJyBjtL8?scyVjKtg854;58==8h%~)s=RgZ1^Q$S`b^n}tA?{RKsrJQe>)v1| z(5c9E_V1ICOU=O2fU{d|=0(wY7?Jc~l|ka# zjLyU_Id{VEe457i*$$8WiuF{>E$8uWBcEOvVRX%ZmD>SyikIB}N_@xQ!|8uJ4A%{eo+Dw(R7^}8I|OaYnHwHjnOWQFRQl;lxJcZF6U=vU`zOn7!SuU|^kY|-SeP>_%DA|#!|m|z_wH8Gr~|jHf7p9m z$8i_kDC{RELd>$lP}eo4rld)DH*h=uces^#LDf;lpMSL07**cn+bC;gnxcMQB-l?1 zn{Cwbh&2R8aB+kee@rZGLk3&Tf3n*6;G0UKd^y{>h}7eJ5nD5V%^LUcTWsm$Wzy;NS$v~|GyQzU&^2B@Kf+l$ z7h#1^`q`>uR|maX$$13;9nzb4tA3Ol}>8?hSKBgB}!a7&@21jtnney zbHeAmbD+ZaMo(Z)F_)-BhwlpDMouS4IA*Zbfh{$E;csD!uo;-l9psU)ZNN5RX9`<4 zdS%C{Pk-ZFV3`4*h(<1=3$ide$Uqub;kZ z1gw6@DqGIb6RVUhZ|14BW>x-i-&VbvU)NS2j*~p=gSW$P=eImkPKIF{qHE`cCr

zr_WaJB^+ZuetqFYKcU8|8`zqzSvB8@z_$1qK0$ef74cghVP&vcn7)5q1zUjKPia}@ zA2ttDYu<1PZzC-F>{W9;#mNK8to|FOa|o}`_sM9lBpgH7n#R`0t+_=7Y#f$) z-l|b)^|_p9EhWxHFOG!1DF45fOsYd~5xv&BRpUN(AEd_MQ38Ie!?F0N93SNzV@Z&%}jpl;wcr}l|sf$<5 z_h^;B>YYp4A8Wrsl^2!%>YHrxX*Kys-7)IkG3wsMQXv^R7pNJ>BR8Xb3S~lKE90j` zvHle8P2;LPzmWQvf=$Cp&*l%OEO7i?FAG-LO^50Wm)-Lsp{u2(-+3-Z<`NT_p244v zD^`tGnIQS5wRLur4=AmaOc3WO+MF5wKgSL0V~5yPE|C1Z()za`+Q%&IV`$$jug3PD z1w9{n%(8K*&_@@JL473jA7S)g=&{mBaJ%c-PkP0w(JUMIg#C7TPs-Z2QtLWu-%7G7 zP2$TJz+E{tv-ADPBas-U7O`9N`c-36$}as7_2e~DKaNQ;XHY8RxWM_;-#4xrS6Ow0 zv3LtYMq}nYE{44HO{+$y=+b>7XZB#5Q2CxfCymZEqQfI)p$Rq%Q}6s(tPM8s=2i2) zX^VBjmSMl+Z+XORKWwUN)pe$01XkX?YJU${^d?~$Sfx1R5jF?w_vkIdHeioZTBhC> ztoWZ-%{#3vx5XFmz8LJ$nqCan4wJq}9`U0Z)(3m8BIZ~*37dp<3uA|Xbh=R{!=m4U zEPmRppMf>Q3hDPFn?t6bl^TI2j_emSyC&#Jk%gE@&y95(J@3D^egT=A1f^qOGBZ&`J{uhs@@g6Vpl zuy&7LKWqx7_dO%9Sr40pEx;b3d^P=@gDu0fDgdC?3@!s_@<-HE-5ijzNwoS zo2VPHB!b&FISwG%+VTw;d|zX?LSVb z1s^k%GRcqQG>e_wJE<%DHs@sOo)=kPJ7SOdl98M8b5u$1@iuSq?u#{D$*=GwwBP8e`$hO$%4r#_?R|{vr4hotUQUn58pE|E z@z4W8?sAH1J^vbU+xdZ26D+B&-iM4Qmo59$~|< z=qIhYro|>;+pt?iiAVHiVNIV}HQ$Z0*b;0U_7YLz5xq^Z_i4A=qRYr{Sew!^^~zy0 zFsX;~h}$?UJm&Os!V<7L*vBM)c!V{<7C*D5ageVd`Cei%r5>VPDhS&cQ0iSB>*EwhT+bVjA0mwZT52g}Jzi{ejQB!yJQ+ z!(^N;kA%4z7Wl%d@diaqmV~vzsx{UO8-P7nV`uu<4!EWI-R zKMI?HJzU1~ypzcobDL&Tg=q%a>=##EcQP!(Ccd<4o@vL4*w}!DCs&OJ+BRh1V6Gt* zUrzkL;gTmdTc#Nv$ciJ_K#Q1wNcAf@s-&B z$*SuPz!j5(;r24HQl zXGk1)#E((fz@j_MQ?T}5IPn*~dDs-}>6+UtEcYw7zuT~xB`3a8rlQTvcmI?2NXj1h zz?@l?ZIM+W%lvxP=n~oGN_JDe%qEfLezR)shlm~iHN$$At#P)+(y;2^uA2L}7VC!9 z!DQ?#kGLIx)x#d9h!uZW6YPN+n}W5#Bz*Fy_`}jL{e6HetPOUlsiyR{VH2=?8zXL` zSFs-8aa#eKgI%k+t$|hDOT?91=TRp61ebu}--C`+NBW#VoPSa+nXLU(CAPF>3|yVw5Ehwy;0@Urv3H zF>`T>@%Sd`WBFJM7Uo12`GK)S$bK?zio~;Lvne!*{V-S{sjY&Ip=CP<;l=f5^aqb+89*>BvEVdT6XrFgwD|nZWDOF~w zuVKFMmsMktYAjC`|B|rDzcI#=^R$GuSB~PG4ykKSLYEP;GIM$&^D#;P4Em#It=VfW zDOfLT0@i%SFk1I7rGj7a38P%kQXm};t9wH>@MHX(HS;}FC!EX!yTZJa&!~#oBw>;8 zlwV8#fbOg9@USkcpMz9o&{|Wb41Sujc7dm?1>apTFzT)&Nrz7SSiaAi@n(L@)55=g zSn9rOMuRk@PQH8gPn5q2zA3OCF6hO{Na)As6qLv&R1DMG_>+9-nla69^M2M;iQF;t zUbC4~`4qd3@gF)Diw=*3xf)jYur=3sIti+7mS zsLM^nW)Hem|Fq^>-yMYY!18Ug=#9aKVNVsCJW_9`VRNvXq%NvHJ2=cz!o?9@=H@mO zcJ9sfY;nJVZt262TZoms_=_}KyrJkp(B8CWr_2Dv;j{`JCYU|$iy8uDr0Es^}VL(7jbbmq}f z=SEQz`_r&!Y|S+;Sb&wo^5X(YryQ&j_CNfV`9m-7&X6D6b;X(SN%a{uRrtkb+4Zz< z=-wo{Ji@AAP3OAvqYl;%%jbvaHNu8qH;Y3aVXa~hrq9bdU^S0gGrz%O>Gi=9Fn!!J z3~PYt^QZ|}vxm*XT4AphPkF@OCD=SH- zOTc0*bMC98ta73dsshg36{@03Ev1T=V6nuEtu}_9IUZo&2?L}J&A56Db z0UPtUt$|HyO!BH8HUm?8_ZO=6p-wszk+mYLecYP4j%~Ibc3tZh`^faQg8|s0hmFFD zD%V`^7f->$9ySjP!1Q&NEUXsxO2Q(Kq{%jH1om8MH+h6bZ=yUte$69AN2tQMAU*CZ_lVHwyT#2#zg z);%kntv?+;DQl2Hj&H>56Ip6$P2w<*yZBSq%yYq({VXg8JI>$osQAMw<4!ngh2Kp6 z!zQdgU)>>hTL?dU&r)ODAcxVc`!wm-$jD1%Of5DWaKHRC`VuV-T42#?ci)zQ zwZZc3YdLOvVKcC2i{EBH_6vFzbw*Qck0Q@LbIm-@z~92AV9U>P*W-EEhQ=gbS=csg z$d1=Z8Lx8ozc+l!i5IUc%W##k8{-ZxL9x@c^2M~b&!J7y;#CW4eeRn1-nA8;2G}(0 z7XFq;)(%@>vD!6*Pf)6SwC+_>X8<@~(<^Cu`s|Z(9z@>kwr(|I?OY3k^k1XcS$O`M z`CSP!eK-QlmVqqcb5~f9i{B(CBwZHJ-F|_)J<7q7b!$cjxjdp5XrujrN&3qptQ0oo zVU@61*m+9J)T@ORo#$?+8(^g{y{&D5#XKwntAgqC@m^TQqc;TWhUsnSIIPdZW?+Lb zy$xN24S4Krz=l06{1Vz9&8?(G8Eh1$&S9EsjZCqMF3p$o8su$<*Nl{w9`&#Z*g0Am zNx{aCthw%*Y==b?Yvy~yxR*5VfmOktso5KZHNg&RYz)>9d%4D@VS%I07+U2&tOFL; z^m4EPSdGR4FQvS~o~*G_*b+?a$Rq!+ge}7KaZW9)=-8TTy`upZhMiC@O}8zuI+#9= z&A^&rkJi$v7nV7`W}cb1{2dZ|$u-xy(l~4g_A1Sf8CdZNcUmpNlCb>QEQ!YktQ)4M zOZXP-!D5=dGT12W(Hg6Q&A{}us)H@V^n7cCMNhiZuN77d)7Q8)ZR0?l74a6ChR?$ z+XSq$e$BooLG+qnqcFV>Zi8iEG9Hje^g6{JOds#|!vg2i&M7TZZv<8gyGUb`upXG6 z26M1}4_k%}!Je0omcEx4znuDlopVK>S2H-0tn@43sRFzfo`FA6^e-xj%GbsvoQ?2450|jD!AIb4 z*6MsGtm1`h#>d14kLdNoreNnOV&y+<8}`2%n}lUAcIG?MN6x_-8`q3g@k6a|aQ{dC z9*AxALV`W++(x(T(lz7Pn$74dD37qqG*$ttyKK#MFL({Cw#nTF)x$<$4-vQIgTy5T z8;8BbinD~V9TvU9>N^OdHO5l=!|GhNT01k>m4?twp!+hh>F6?cu+|){vvGBZWBi*# zzaRa1ep_Rx8|k+iBFFhJXZEG;ZKBg~<(hGwt;5@-3?n4G(RRu^viy3eunO21OmDwy zV6z@p56i-0VwXq7AGQh8>sdQ2(7a}!+Y-GVSQYHO%BAUc5Hv%A!|f7NfK&n zW1jtawMKY~rQXncLxt7?%{XpjYvx9GI~#o!@o9CKgrNdf2m2_$1Hqn>XtJb8#z?jB z1-Kd`ae_w{nvPp1Q;aTE^AyZ@-ki#Ev!Cw(}W732sq|KsH@ z^=_QF9l&jQ8*5E@w;x2P4z_UVtjq035u6v{22|xXj@xsCdtru?Oyyp?#D!zy9L{FXeExBH}QRKsI%$pU$VC1K?nlYDH3mBFqTB{g1W z+C-!!ei>wK$ZnAMQLn9UZ7}yZ9aiT{2x#aXR_SDlhYIc1VO2-RaXa*Kci%b#TZGAc zLmmm!qWJ%cHRD-|m}~=<(3s?7IK%n_>^Gvv8|}e638p&LW4ft8Ue~^6Jiuzh#C8p= z{*`OSgDh4DtA{ncYR!18Bn)$W>pMMa-B!K3!uuSj&ps~moX{VFC2`_Oxn)h6$T8Pa zXXpE{k$BCT@fWE)aBHsQoZVLEPt;kLQzfmT@7yO}yzzw{2s4>*Ds^QMTU&3o`Wt1- zd2c|?yVUx%sw>C2cjif?wsr4}ZtRHKKd5{S?R_`%5UWgC;o>a4eus@^t3lpU=dvB~p+_^*Z3chZde@A;-K85d+NgC*_J=ndAyU^y;k(}c02HT*1KRM2h)#nSo}Tiu*|?ZV0u^xJLIcpawu zQ3l(F>G7?CCEx3ITL&BVutr$mU)*k6VfC=tl* zvfx+q-IM+jo!H4>XA(PW68=JV_|Av*PMg^IgrA)$?6llz?J?fFozX&eqz)BzQXYra z%(F164zWJk72<3I=MMOrs;1tbjH6eIo|+4|^e%GgHKA9Ip0t@V_OZ^8V9)$8LEc^` zdKJ6S8$qvX7kYE(RqrBvTj5F6;tqt_kg4>cyHXTM$cL2e75FMAPcd~KcSmm7=crcvn8lBtPygfmko1fHGws#q*hiFjsI&O&6*h^0xoV|JMP2GKP z_9|{=eEH3Tu_t-nj=j{k|FL<#fFIl6J(w^{o>%tJzy07~%8;Z%274Xz|JXEG#E-It zg9)>wLDgFr|Ni`7>?N_+fxXNx*UazyvA%SNoF~0q&J5lbR_hbz%NX!N8B5l)J|W*d zE6^05jK~;x23zGzYsTa3cAa+EI;-d0v1_eQurB!A!p3+DQV#6xynXj%B*^k!0q>cC zOWGvgO8@g4<`B03PQT*#pMU>6q`rDrz?yp3OXg7n*xO!l_J?KNWE7Unu9@#o-l)!( z+{|SAZqc7Xwur1r+9V!fi?GTZ`&=5^fOY@Dtrvb9=jZ;!nFmd;4A${yw_X)&=6^Ut zpy}1Y8voa=*9aR5th>U}3d@Do?YV@6uLG70ue;_%eXwCz3b{O@Hw@c=)hc4L30V1A zZhNz^4D32hZwWRJOKEHq7CYN*uc(*$4a?7U#NTq*2u!yZhlS5^+e^S2V0!qPU>&e) zHGkV+3ox}F3Ku^*Vbw)$y?$69>;;%{Hp5zBH)&x`!=_<+8g#>A#p}iqO>Y3! z47*Wdqp%5>Zf^=!dOx?lc~}~z+snd6VY?($v(YlgYnxrRq|2NZ$a!KJ0Z$EqMw5;z|sOtefkemR`hn( z7s}Snvs|W3KJBF3RUqs1k<}raN2bo>SmDmQYerV{&~R$9L9Iv<&U^?6tmtoY$p-dJwiV6CuQ#Tbv+>x3iz5l`st7M8J-%#ki^b5cGQ|VW!?HVoO7muuOW7w{Sx=fK)92%>&9*D(d(|WLIbd7 zSfj*`NAyNvBd|*pG1(Ms6DDDhNA%{!Ud6iW9fT~b4W_T@Z^I^F`Fm`{ZS-A~M~~YI zSo|?=w>7X1nC`Y7HUiVn_@~4kOs{M0u-IeWdOfga*wZBvJQ9{cSPx7uk7KZDn2fpQ z5xr?x;BjuY0IP*PRcV=eIasTQ1>Q~n0gG#TrLZxW9*;^`4yOBC3#+Vjvj$iTO!v11 z*5_dv*d$E%w->emYu3Ut1gm)by6ZiHaabC5rKUF{_F&J^*ditj1Jn0J>R_c$beG9Su?N%Lw!(U0dR}&jJy=Q$bD!9&T6dj=ABNSz zx-`8B*d*+u8k>bRJ!#$eu*IZKEWtKmUlYdqob^3%-g)T_Un%EcE>ZW#T<@)zQm)De zD1T3O=SLhi4@-+Z9??s{;!kn2CRi`*Dy3!WwZRr(FVk2jED?9Je%KgHulpmgZJ6HX zPQsd=>SlAWao9=Ck7d{fOfS1zu&SrI^@>^0YK1*Ta~p$od-SSdQ!w3L61MHpYlhW4 z-EA)oYlP|VM|H!7V0wH9U^$Q8sMxDscb(^$g0;Z(vO6#KV0u~0!WKPx+pw}{xZ@H1 z7s3bA?Nz|~JbE>-S(u(y^{^~VuOBH`^)uadwH?+8YtquWN9@6#udzYc7EEum$6$$P zt-IdGn}+ql8Z@^HVh^UD%gu>Bm>#~s`>Z2JOtJ6;8=yl~xk;#rPf9W2r4 zw$}*TxOCn4m1eIMHhG2HUI#1-Q+GaKQvB_MZ8p$s&7~~F4NLr6V`b%^+;ny?`MAeQn$Tw*a9p+Uacm4aaiLmutWU8X%Fk%7Vpnm z=e9y8Sy^+AbI36&iC3%GPp_NbZ{=@c9bzArp9iV3 zR&6bz{7(9p*RC6#9-S9!YYBDejH9E*^VpGjtIW$JkD8H{y>8w0j!PQW0?W@k%FyeE zO~KT>*_J{rY19cz!2YPUNByw)+t!V&#ztVpw==fU{GEi2ynWrg2NzpXkLF<0{p-eerG9cv zECU|T#RP0-cAXu%f?Ddjn1CCw%5f6?Aocs*>&6|*Ju6FGfb!5ooQ}TRia+w{_t5rg zbteI9ct3MQjWxkWKSaBuu{PM&N8RJ3PFUl}x-qY%Q$H;B$#vsBdi=%Sr=2xX$)`!! z0_<9T%lTBwo?LOL(scn@)fnqfn%x|1V4U%iW;gI5+S|{&+oe)i>0_o@SlM0f@yrse5tg^tLU1-=ov@hXv1c#Z*{e7v`z}|O%mw9o!~$Ct z$0PEcu>t~hms$m;n<=a{UtHJDG^jPri%VkMN6guU{MnIi^y+q@H;P^oy~ktQJX4dm z$G(wfZyvpdd$A{JxQ$*DdJnbALkbohCjBSZjUP(6QqGD66HiQCy7X9-b%SS5@^6|JAN#Ejk$~XLH_S}|3TgR9{Mde9=i-4j)boB zGTAT^n={y~{pPx1Nc-S#^PApk&*jo{UY5V3wEQECPrkiwzWZ~WTc;(8oHCcPUxTde zJL|^h_#HSYGFh%Xsbr1FqO09BJcRoy74&M_G!fC z9mwH-rG71}8y&XoewXbE;V8LAUQO>&E@X9(flIRY>00;p-Op z-`0)KNZu%0>g?U|lCf~83QfzJI&&lO8Am@B%o*40LSOt}Mn82{&N#=)d)@yy^6&`l zMN!W9xWtA0>t)%IccwUV-xY2s84Cu-8PVGJo*s+DP^-bs?EP}aSFQMW1+SD!aCiRH zkCeG)bXp&nGdjdyRe!!uw9Pt_5%&+y8CAAy3E7E=#IGOO(nE7bTJnoA_G!KoBxj1s zNDFB+kIFkH@`Dr<{b}@L`0BbW5pM8ED4Ipfu; zk8e^1;un&=$V6WHG3INJ%o&f<UguOCQxuY^@SCTA@0TizwSUH0elcc=VY{-ZF=j}SMa zyM%7m*5%%t-q7VTM>?kNE%;nXj8CT!eGZhHZhCMt_}H9zAD^=Cb@Q1WZYFWl^tha{ z;_J_6U2Y^E8@MU0++94%Kf(GOx_gO718%w>zqfdFr-jG_; zX;SUgS6%Ihv~e}KDX+;H|LyDMD=s(E_O#(9i<{kqX#m~9XYVacv$$z}&fdZl7-N0x zxw{Kf6}nk;_mWO2+>F=mElmBm>3rVq!Zan}fBxRWw27O*3-%VKN(N`MxcP;h-&%RP z+g+X-(e13;y?rT9J-DeqFK2Ap_Pu5B%R9=`ByP$M?=DPP2|v2KEw2)$^3T%V9@$%% z8gSE+*j?Urpj&oyZ+SO{n`PYWHXc&%mT@z7YdxxQQqC7N!htwsEtY z@-&R@*oocSm%LlRO~=W-kFrL7p59?x6$2gK1i6_a5GcCw=fOirvLn$d7lB_ zm%g3PKr!w#+wr^H{pzfQ|AL%(x3bb@8ep~K>~(Tq)~KrvNtgov#(4a~obem^UC_-Z zcDSj=%~alvS|d?oRn=dZ{(Ri!M#^9-ZaNxv7mt2);}`8M9y7Sf;bt%K*v8Gs3oSRh zj7R+Q)Q^jI7msFi%Nq9?Iz(xXCo_Egn<2slI%7@yMZ@MR(b^tnaKJu?g0zuGm{V8gY}la&PhI!A*Jd?&2|y z?i{*%iANSUy;tom9_4q@o?N}Rcr@T9{G#2(qXXRuboUaEG2FDL_7;z2+{CZhTRh6X zNPB{tdAqFmx4Q{+hpydQJTkaxzHV>v7{yIl%iiL#gqwNX>?R(?Ut&Mw`n|=Y4mXJ# z_7;zJ+yrjiTRcW^Gl`qs#A6=abnD*YQ8dZ=&P{uZM=fr$c{jW0KcbYSG;W4(&KYyE zw#Fl@8b zQ`y3M3(IIMScjg$GOqG-3!UcnoN>|$hqx>LGUMG><_x+9ue-cf1pr|8~Xi@Kly5`GR8XP)X6<-{7hfS%s81XK*w5`ke88%}y^Y`No_P z_t^Pw$4=C1X9_ooH|LB&Y|10H=V5cOJ1yHauq>>(J7?aJul7e6v)mR^`#S1g&w6zZ ztjoRQV%n?t3iEH=E?D+t9kT{DdOBxZX|H2CcdK~bgFIfc6bwD6@H(ctWhRNe9_&r_ zwsOxSHRKE}mjM z*ne`%3tRPr2N&1m*J!WqJh-?HV6WqY2NTz2Y;AlvXO!-; z4Bu6_46D0#qzqU8JM*7^r7f|`T%kH)*VTJ1!`;}6e{BD8O%S#@Yz==RXS|EwW__id zv-Tp@-oE>MeX7tkKw_utZsxbx>9Otb4f7p#R2=1;{-uS!!d{Kr6m}A$IrFRz?Z<8M zZQEXX-~P0`-XHv|wky@tVNSHWv66+6Ay*>5=s3iuoM2w{^M0P!*~%p`;Au$JHrQHX9PRB1BllKb`qc6f4pkGMR`5|JDu2>$IgD+ zjT!7zeQy8pihi5&d;oTuurrCBcia8W8T+r7`1LzO*eM-nzGd4vWB>IcKRa3MG-KyZ zw;gq+$Qc*d{osv$cH-Y*KMOni&94l0ivMl@`89=|M(ljV9WTHBz1=TfMYH4=cJ>>u z2JDo6e*f_rz)tf4*jd8PFm~>+)2C25-*_!xXYc^R6Z<~C--`48sD2N0f4>?9`lpFT6#>BY`|;}!iOPZM^^ zzr6o=4PmDRJMVY*JLfpN<%e*I1sJCoRX-|p=Aj>qEj^zUEUe|R$3X~WKb!!w1Q zY3#ho9k0UUpSoa!U-}gNi2m!VIpdAHv%>|iJ1<3)b)yFCB(O8QJ3FuNvonC5(E|w2 z5_Xmkz)t0lDbG{;k5?Ky-3MT20y{bE>^I*7KVdxfHTIe8{NjFE>po0R{gQRIPQUz0 zVrLXP`_1=0?3Did{>$M4cIpqnPHchv!p?r<)ry_m0oWPCPUGGC&-X3t3}R=$`Cj)^ z@@snk;pxFnGj{gd{>)*g{{ZZi{fzU12N16mc1pj#|9FjHr~Ux!Y+$DwJNs=nYJN`o z96)$Fu@nBr{?lg$JGI!^Z@i+5q|X6_rwKcg2ViFiJDUd(uPk$pf%6g`LdJ$i9ii^pC9mV!M% zg~SX?lVwk5Z7>Pn{VhG2FLc5rzIO?8+NBHRKDZP1yR+84=w}#z&R}O7JL-L8$4;Sn zVk6_&*ZZ|^(O;8Z-`T&NChX*}-_9wv9ovp zcH+Nb{Q2Ge=T`<8HSdzj(dX&&~pNMzQl{ zw;jKD-Rfs2wnBL3bH?}GcKqUXho7BR?2KZ^S?_Z8p`3GiDt(%0pMCcq$FLLrQO@`m zJ3ME!bAw-awy@KD0CwuKtS4jVy}Jv~^?u>$!A|jybLO|7J>{@K`n34jnZr)Y0oW;9 zr9H&Xe)B7ZozkD|Kfgw>({=!MHn1~|o&Dxl%^K^03;WNnPVBTFfSnoa%wlK1`4wHK zzW;Rp`PGD-jsvhWgq?Zp>^ELn>{R`1|M80F7_T0HoeXvsvGZxW-B9bU>f0C2x~uxm z=L_jC+WhMK6m}9n-*K+PvBP~ur_Vki`}-GSr`gX=(eLQrU3LnEN7*?^`+T*Zod)b| zVkdBjh^cmryGyKZdZ>6g-&c};aNf_(0Cq+fbH*R-^ig)y_eNEFsGt8#1;6HJX9+tk zzsMQ?*KJ4bkL<9M4t}SQ9XV%S`Fra7FLTC|C;$} z4kZd7AwH(g=eJ`ku#_|YYNrwJ7Tq2?CAKcK=RDR}AsssIXG_i^Pho5PKXc|CRw}*E zV5{>`=&HiT)ZIFA_e=B-l<(i<%Ep_i#y^PcVx-j=UfF6Zy@ZYkct2EpI?R zd>s~4 z^3pTN8<1C=L7qlleFk|S^28bBW5^qkt2@w$KVRW<#~*pi8RQ$t+s`16{*nAYgFKGB zAGvS-BOgA4JdJ$(4EFnwPoF_PhJ5}E@;T(oXOM3o-#mjn`X}=Lw}sPJ>RTLnDe@oM zbyuCQSKnt=cXM4R?X5abb$&_bS)n%;u5b4HFrC;Nz9(lq*?yNm-G}dt(^dO@T-tB! zofF!;7khKq>(1tk58u1JlO=uskGS^%i}Wh*_h)BjHzb1-R5WPNprWFpqN1Xr&hF0c z&hE0=NkS5mU@|diP|=PN6(cGZM5?nJY*DGAQcIO8RjkxfMT<%+wP?}eSF~8s(v~U< zWI-j0zQ6l9^D;9G=b0tDeae9r&>?>pymUlZRcWjV@C#jj+I{QSj|^W$Bn zxAOw=i=)@Pw0C;r==qnIoabIRy(a%QE)c(hb?V2;-s#2ATeyG88N6`(OnJQV0_8D` zUU+@)^gO?!{{7m0x6OGh*G@xYSv>wW*M?rh{&RcQ zey7o^y=?FFN;b&9%g^mu`;DVF5ZD_%neU9F=MSE<&ew|I^YB8rxOL}id=Y8#d;T_I zMKIe~E`Ez2;QP$r*SLPUwmjvx3Ra%OZymf^@!JdwKxy@jC~b&EayQmqYYRwOe>?J-_yPozm>31 z4!^bVsNy#cYslfZ8{Vw=9fEb`@H++XRs1f(hI07L`vd!(ir-S$bPm5^_`Kpb23ybJ zw+-%p_|7ttv(Y|S5zKt=pBcOPPQ}c-mbs?1^T0`28*o3W_j9(1kLWC*Gmg$bO8;VX z%o-!T2V(YQ%wE&A==5kh%{9z3TPs z5j}FBO-Q1^zh1Pa9(B%H;d8Rga+dvpWjn_)uoqx9G#v$&>2N1 zzv$BAo@>rIUy-<(zS?}ZM7mEi``!`hiz3A5tQMbHbn;6sU8iI}?E@WCuhV0P z*`G@9pQpx-aV`CX6-@}w1L(=Ck>3Z~9;uXkMzkAS{F1>Uu)BmHj@Z?Lc-({EiJ&C7Wx=tLO%BNmh zJ&<&bqBD1iI#LHbdDQQ+y{Q8=Xtg|TZ?t;RYI^#m*8xf60$Od)xO6SE|9v_AQ~5dP z)>QjT_fP5mz&x*aSFp|JE4;vdTpM}=&)ge5N$WIvBhO;LBNab0mos;9q|a5&+1jz- zs@MAqWxG(cim&i{x}Lo^T8(JcS7d7O{mJfJ{=vjc%3uVois$T2o{646;P-@{yEl50 zZ?)(}pLfoA+%C)DmpRIyAFa~Lz0q1iYh#;M?lREgC3#kr&;0!Pdy{9P*M;8P3-(4& z%3v0~r59$_w~KEFr4RFa#;f*5D~?vri!!xxmx0zkCC|ptYJBnD2E^hyuzP5)6|K>x8vS}kZz9mL7fqnme|T^5Oxi&q7c~us_eM|3paH${ zBbjN<-43+0iq-R=$ z5v}4IGPN!~jg^n~dseq;U3?nb(3+^tjMv3$O`+9$W2V-{r?KcN)}J?>a~`wH{^Eag z^cM|ig>K#(tzonZ>oT=+r!iN&Hqlz#rj?fXLKk<9kVmuN!j-At{gYqb72`hejSFVpb7VtAUhD zOaB}@=k#q4BmFF1dT)_umwj)x58)@wGro0b)!cQ?`9ex7>uk!z_k`@@hS?8%jix2} zJAl^ss~+0?&AZy~>AU-(4Wo%LT3&O`c~L4)a+cx!)-pVny9}oZqxN-sQ-;OGe$U|R z_ofUR(JGFgOWx6G<|d|p(Apt)s9K(4JF2w9)5&%?MHqSaJhWj{JkIY~f73bV;O@%s zH`a6<*UM1qdk(ZZWSksZCKaq6QKc4g8x9z$- zergTlm{R6$-#bni18;w5!zjIm@#r0yd6l!>{=u4#L%GYmi!fT>_0Wc~L>P_l&I}`G znLE~W9L$}LXbJsK`#C3)Y9D*w&PNC%^qz+{jG`xSUiaS2yvkYT54wHGc-u-CMSrq4 zef1<-P4C~EzPj*<^gm~IoxeY^)|2D8+ieSBOx^d;hA~GNqaWCH8D3O<4?T(g=g%J6 zFa`*t`-A74Z`G?jr*ZOLAd~PC)x<7wt z!MovgVbx zHgd~6_7t91_~b(y#spyu4`k+5&c6D$)^r@rUFKy^Wqt6ehc=7^VRQ{kJiZN_GVmLLaXcxyH5K>)syJc*q{00 zLmS2jVR-&7GmO2jCq+-^{Pat^UaMYIJ!vJ3$fMxtjzju9NSNjcx z&*1r^ukJeS7gbMM2xI6UAKEbH2&3!k=bZO!&l6?d;C;~Pkmry><+RUlo^!s>=j`Xv zvYzWW#k0c$wq?EObfHr-ap^kK=q&7kPSG=&5B<}*(X_qp2hPTd!W;XPV?lY=g%ZM`Tuh1It9;WK99~{Z(q7jKRV5OptFe196J5GOP6{NMnwhl>wmp;ofdQwd!RFp&cYt( z#0jGuP8bLd!W;cPX2c;JwEg3G^2B=G@nz$@$qH=rp1;c8NN&wirgI@Ozh*F6jd0 z`$9L-YDDX(82Axe!Sl&~Y-f#y@4Mi4DYlaDpL0IUv^zP+IVI1on!S;o-_ePCoYmnc z@&C>_^`gW4;f$B>zf8z|yPbaS+il-}EEp4aZMd7jUE_9lVR47HaTkXT;x7M(?3bm^ zqs{lerO)QfyKrWHdo0+$&wDI5xbIkSc;C_BDF4Ry-GH$X+X-x^`F)hXYpn}NHrDx( zyeNJF=fi|)46dyL+XA*cam0_f3uBwd7BrTmtq$8~fwqxhV>+xZ5=Bpy?+BG^{k%COD5 z?w>C1DTf3(Q`G4rf7|)4p(4J?h;v+!+|`-?!leId*XJ_ap(W+qi>(#gjUvujl=mbTc=N3+!bV`@ z3Y&)ce`NQ!1dG7#N@|9@x1=@mUc~;6!b)MQ3Jbx)KelVu!+K$_+Y0Z_ba*YWC583C zLjPg+Hw^1h*c5D0VT-WpIlHF+#q2LDtOT~8uxePzVe<;>hE@KjU2_Q5 zrLalZoWd4h6+g9WdS1f&FA6J$&BAU-mS2UJr$DyqU=^%*o_9*N*bQk`3yUc%4jWKd z0=B5IL0I|E&N-)&;n8EJ!<&%s6gCg*f!X4@37du4+E`IF>HE2zRl;I0n`RU?p!jQo zEh?-FR{mdh%>h^(W{c-IY(!ymunk3X16I9Y*DQP~?+Yoc0yY7&rM(80_uqESMp%u) zI$+%}n`S?3M)5ZWEBYUMc(bs2m`!sX*01<0cp2?oVdb#0|FvsIU`;Sv88yI$71jn@ zS2X)zRf~4bQCO?OW?%if_1@cedMBqr}*<9B)nyNcqOnf>{VN1_-*Naw;I+9vxQd&n}V77zsz5*Py1_z z6|C69>xR`TY)Ha`+1l77Y))Ydu##1~KhGi7cM2w)tQeEPvfT zj~sx-6gCbUf!Q?YV0pi?vkh3i!V1Im*9xnE`G0NKtbx_RZ1d|z*pR|HV4mODHTz+; z3LAqB!fb7G7PhIdby#%6?yuko^B0Ac!!}^H^hIDb57=1)tY2YmuyvTN{Q6*#-`d$I ztWRMxuvLYv!ot6^_XYV!=|2@#2Afh?7*?=p*Nnkx71j#tRah@<4rc3LMqnksx3g(j z1I*S(F2M#BmUoQ)Okt(4ia*#jL$GFr)x$;=)&kpv+0xeo3psW+4C_$X6l_vqi?Dpp zvNb$^g!Z7Y5?Bw+mY>zIS%uZXiuc*WYlg)X)(sm_*br<{VUw_O@3M2-)_Ux^^mw}f zi^FVVfaf^jDXbVatFS6qzR#{%3kxYM4r_+l@-qP&RoEbGQ(+Slp5LxH59?6aCTvn+ zMJGt#e!FHREUK_5tVdx@uvvw5!HN&q!yACb6gCbUP}m%7QDGaf^2_Yu6`rKNz-(=^ z0yd(s8rX)y8e!FW_V7AjZ7|z7)(@M3+2S$=E4tjyW?}UTTZi>qnC#&d)Uf`8J(xX5 z@fX40(na`d!{0joPD;Bq`{ZW-(d@Y%cK2NCf@ORqdE%^0(Rl z#a4#R>`C$+G_qGXkF65h5TDIH;vMu0M*_zECT!+M-*@&43s!ha_*HO<^2Pl=AAiWa zy&Ly60bef5iC2HQR?jdir-=U zRzBk5{4Pm+F4Aw=Y33sr;aBQl1AZ$Wx$HcT&&e~3H<~jCVQsKl3zM^gK3EGZBL2+T z1>g4H=_6O#{ckY`Cdso4aXX3IKHSQCz)80psdUO2RlEP~8E&N>t>bnax0!d-FE5S$wa-c3s&)E?OCsuiB1DLp~6eo8A4}p4|LYiDR|VS#YghI z`Z~%7on}5K&$H;G1l(}>$C+?qOob18>bY#SLi(d@=n`sn54J*DQ1 zjB~!E<}0r-=T&LVG2C|H_T~7KAJLq~Hj-fz%>``p*j^@%Og-XU%dJO$luxGTfm&;{H{{zY*&gS&mH zb6WF!t@-{XOa8sxnt!~HDPi`Z)%zHCPHW;uStQR(DL@7|s)B?$fxBtk{VAWNe9w9b z^Ni$8!hbYa*14Z|E!z)xnGu&UuEKCb&v7Jh^LB%b;{~ZcFUQm z$Gy1Q#N89b9Y1FL!&Y?lcH20%N^FlwIy3HOu!XX0i`Z(iY@68XGHgy#%;?k-8SMju}il-xb5GCTNw|>aXW(BZ@BS2BW3TN zS-bP6nD--;JSj*O7=4n&e-pov;$`R0#BXN(s_-^v_BAEovYXgH#qD3YZri=Dpdz$g zVJabGv$(0jP2_R8-NbP-a{)I!xQRVJx6UYTmM-9C4mT~=ArxQ z2yTj>kXxq-H-i^&(~Xulg==mKtvZ{hr@EVoVwHxn0d(}0`k({k%{ z;b!gvZiaBv`1IU5)3{l`fSYyPv^^uYPSLHbugY_~sm4w31>D4NQ~J!@IvuzfzJQxS z+*Cg+x6Tx9CUdxvK5rE_wa;F5-Y)%^>E9Vs-MJ7mSa}M`Osd6g@olu%UAm3nc5@eQ zbm1OYrgdJSPZ?}hI8>ZiNAXMopJpoyk_2Fo^$garBs+}QL#@1?hF>Wk8znf zrssC%5BQ6SKU0?EUAymwV971PqT^OBT_|zk~VZih=F+J}ryHFwNqo#*iRQcODT_1m?57(a^X`bm=e1gV`q0UH$#U`z^|X%3zuoG{ z9B~1iHgx_;;zM0MBl(k%eS}W`)*hSpomu-WCjzG2OJdZ=>fM*S%+qV}6UUD^)8cQb zTaDPJuzicq$@ZQuqZ5)R=KI)9objdA^PDE- zy|vm&&3O+68n&-mSbm8AJJ~-&D}8UZ>5G{ypYf;P8#DdTNy_RIwrEM5BIs1VY}xs5 zH%@LHH0wT7R_6OOjE3c8Id6m{OcWvH)*2grhYT`1FR>#ZTwn5pS zmApD5V@*PyY3zehAa;+fAOD_?z*~&C`(gL$*AXxc8wmi;ij64>Io3C(hx& z?%=X>j~kDKm$S4Sn2{(j+QITJ{}!JszNZFdy3JhXz+VJ|BbEjJlcMsbAQ5r#&=e_cnKp4p9wEJ z!)tYwL;_~7^i{Nv2y5W&`I{c2GUsB$`+Qf6 z7`Ci!PDZ?B{zTE6Ilk~^rzACpQJxa1(w_EQ^lw#|`)`v~`g9pj;^@@gu?eUz{V@H35{--@3L zg&p;N+ZMLyETPj?yX=&t%G}f+dm5tNRhv#DI+d?sf9l3%=h@qJ((^LYXKdv~LoiYw zjMfGt2g#TBQj7S;!K}7>DnLcs*{Mif=qIewn=<#4ZT)|$Evo&$)QKgu7SZ~BD&FLM zWyFXc}>Xo1*!9VTOjiU+G}bkFm+z^s$a|czi!!iUrNu+bJO{0^1)rh#l7DR zwwz$z?tQNnoxpE&n_n}o8^7uO|DArL`1dri9>(wArP68Igz2Bt zdl9A#P2v3^*u{Hg-TS=nwvDNb|47!NIe+`I^Vhr7JbwXA>GuZFtd7wRWXzXxV~3g{ zm3eF4wE=qiJkFd7K-0K;{Fk@J*9tSfE?_Ho<+5|R8y|PxlRclCrzT_$hLHfxG}9pZ z6|ZM}M{BF^j!AT|O=Bxdr8iwy(q}$qTsjd9y@E!yv#lQqc;5nG2A0(~FsqcdF-RDF zcX2K#2NR|b%nBpjHjYv}zN_|Ig9*!c72@9pdUHKIbCAjt%H5qOF#dJ!Kk3dByn{QZ z3GRM>^&4ofA9SB(kTN-F%Hdu7-^_<3zhb!gv(fG4(Tpwjp=IaCaBF{_PD?l#YOrRD zEKo}DK91HpT8pA3aX3xrrvlzn{5p&qiNgYJ3bW#{fo+wJ{TJvZ% ze1!8nKYy5dWBPpNC1?Cu?OP^=AJ`YHyFM5T2OAFsP4-%u>SSGx;kWR!JLc@RIfBgDXNd+iBdZT& z#v$<-LMv}5-Ok+nNRQ{T1O2_M{BXb9coDyI_`Ne{`ETWiEFE@6L&{Rxd*z#`)Su(I zE?3JvZzoL&IU{$^+wUSGM2&1G``KGy$DVIW+-7p))`4EZ=a-$HoN+UClg8%#7i;9) zabg;+hT&!B1F3S)8Yj~2&WsZWWMh42!J9dr)T5GS_J{wLbC>OL%c@6iyNP)tsL_(L z%Fq3-)+V%yMi?))Yo+UgiJPkx^3GYcjt0DMv_?5%)~?fp(foJI&h@DeP zsC*0Obze!RCCAx~d{6i*rEl5Unhu-tYD25?tIN*AwECJJhe?y~RrY==S@u(CZG3In zd1Oi}tGv>EJX&v3^QVq<7QL1I>ha~|T^hBrXO5P;$87pu`WogKWrs5!s|W{JX^P;t`ky$@OO5wg z{Z2Yf^3@aGe$uqF-5oV`q6e*lf8lxPoaN~D)y#j`%eCZB@_Xp#(W?B{W#@^SR%V-t z`x+##iE35{mfROBe;fVRcbMmETIn?Mo%V9-W{DPqNCWxRg4X7Dmz~lJpMQo~H2Yp) zkJzDr@5w@_jXR=j_G{+}=W&0tHUDng*M3>&fyDkZ{jd(5OQzm@gePnVr*Qu$=+f*I>gUt;RFTNk3H z+3l=%oEu7>*P%5uzwCTIrDe)5Yu@1YOEK?^wGJ~_6`?hNR>9Agolo*v_S9v8$u_PD zXB;*F`-@b+lddN_`oEa(W_x2f5s+~5-@*Lyf79V`B+BMOat&Dq>x0SIDL<0NYHX9( z*0;A|dw#`a_Gf9QHOc(yLaX(c%(J$qBb{IAzJ-3=`=3cIcIIeZqnw9;$QblY*~Kru6Cqo%P#mKiU!q|a+Y ztLi-SDVa!^HHodSaq9$o7`66?J{W-BMwEB91ex1NoM#B9|M$!@Q|&4pPP$IG^_Vtl z@6?jxYstG=&TT|}6p`d;F*W-PTixrbWa&iTSM z=(+i`BTkgCIct??nX=cnvA12{d9)q+iKZPRvHPm6JPX`*- zU$}nnljlS1XD;+_%A6K0iAN1uktaX+c!=LF{3f2Vk~;gbwr8?&TYGlv=>&dPpSt4Q zpGrS{RCar|_ze-R=RNFiJZ;7KsoT~vpO2vyxqCTMuMSa$JS~+~ucW<((TJ9>I9I0H zYtB66%{EJOr9MbLbfZ=HtQF@yskB)7CUy(Am8F{xQ}}Is_Jj8uBHX-Ap6{<%alYh+ ztLMWj)qJQyqw2XU&Oj<3a*h?0h@}rCEt03bX!TXDI5VkwY12y0J3fup&SBTpT0pDf z1uM>Pwrge0)3)ZKzPHM_vvX~FI3WAW74PN!*cZ~D^4a5!1q&j{{pASu>Z%pz`Ps>U99>56kam9Ob}R@NS~%vsm0*-CFJXEa@C)x0d7=2~oh*y^x# zS<;+dk8G72jl$Bbjt6|V?+Z3EpJ)g+9t_4L-s|XghgO{XQt`IcEq6{6(H`u%HMuxC= z@%tdhfyuZop&yB?IBThN=j^9=Xvre)j$2jlWB=lKX1_@uTAwqL?W^p;?`WWv^tPhW zTeISPHkID=+9F-P?(;m%)1~9w*~m`>eDAU^6nHL?x4o-s%yKt#J;K~po@Lu4`~!XV zy%A{-#qVc7B z=g}=%Ve`!2I$9<5E6&-6kr8GcYTAFguQq+D+po;;^PTov#Y~1ZHM8vD_jxM#(wlrL#e_Y6$?I4;jUn6B?8y1qBLU7zpK_7=nG`@J~1 zRW_rqPj!=~y%gTZ`0&>4ZKw>}7`Cl8B>7*BZ4ujlr_yQ8nzGh{TlL#)TI{Ue?wYR) zt;D@6&Lg((|0r5-AjinWtSw)P(o;vgX3&bgbH%wPXMJ?*cg**Adj`>aejwO*A{c*% z8933EG*@)9o_zO;^MjOjjx^H_<*d$@=oz$H(HiYoaT*Wshbh|};}Pqzzud9 zD3afyUB{DKNjEp~Tl}2jcV`8_1%=+alY2u_bDknTUGFV^+0fPUnDrq z`!tgzczmDt^L#jt;d<9_2ft79_euBfD8G;J_ig+>h=~J(%JGLf4_piCB`CfFTW*kB=by| zA3VS(iKzr-;{GHrpG0G;1bi>@Vc81h5GtaU`M0lI(jqx3WnfgEB54szs+hj(8n!a6 zp1(7TNkk=+w>(glWxmzac&O`8nY%pJonS_5HB64mkWb09b!QBi`22$cNV2qp z!d~Xbx36ZL31>amO+-@9?lH#SgWS$?4V!X}p;a1NO+82LzI({`61mS{Q|W!r+PA%2 zi9Jybd*<+8bl0kL4WH#lY^xFb@3zW5T@`-n@w4Xo$$STq9zmYXtB{8r zw$)eq1~~_6mhc)@om+26hG&0%#o6bJu}@?F$>aGWYu=S!ce{N6<@;~e@WTP$Go^;@td+#PeI1nAztd0h+^J;i)wx#+^Fm_-aqdRTR`+EbT1V@tiLG^i_qK6J*5rjB z<-GYGj zI(X`lSwwRP&CiPF$?eZG(U#2j`mk9dPcNmSeTFF7-~6#ANzmQOC~OU;_&4gKa2m6jtN8-x}TwtY2ZPuyuvye~kUH zeRj<uwK}b!bV^rpIvhr)}yc`*rLMnKFz8->sD9@ zwxFJW(zE#upZdF!iHg$2ke?tur7ry!sZm_{{-`g%j}vZunvV)!)6s$2P@CB zYc|8$6xI!!QP>cy>~g#2B&=0o3$SU0dHNX-udr(t!&($p1)EY>EvzJ9*NnrO6_$Wa zDr^u|oNw2hfW;Ly51UZfCamaTcFiIlC~8tzC2U+_QCMNnuGs`@R9F{mOko4Cf-CKs zxLvamRyqUgH0-I6qf%eyXFims<2g9 zkHYc?=+6{Z1}lEFJ-je1rmz@nKw+)0MTPak%CEAAHv)?*Y#KJAuqD`r!t(x#@vq1p zUMZ|iVIkOr!s=mpkFjgEz-koM1M61UFlie3LAor!fb7961J(Z1z6~Dc7L8v)BY4z44YI~6)gYp zcFkH?RAF&gkHQkLS%nS4im$PUHvx+&Y#uhCuua$^?5?djJ5TRz-EUSjNP8%;vr1UE z!lJMRg*Cydo^ZeQJNmm|tqL1}jVo*%=6|AHa}E}P+0wTG>r+_aUo)RqSOqNnB)et} ztXE-;uqB0cz(S>V&3;&q!p2~W3Y&#hKiRIi4(nD}!DlExg_Xmqo?>se5m-WD4X}BI zwZST%YS-+8bt!BVHm9%|SVfs#a~0O1u>8-`{uEXQD}S0@GYo4}SPV9!uvS>v)9sqQ zuvUeQz@`;84J&=rpSfj$~U}Fkvh80xUHM?OA3LAorDr^##{~WvK z0xYI5&*zvgDXbWl_guSX6|7!iwXk7@#bN&E*)jd^Hkb3=V7%9+k_1& ztmyO1Clpo*i$32TUKBQ+P z357Ml<`vcktE|4?X-Uq(qFx^D*qVd&!Fm-o3folJ3@r9iyXGovLSgxT%XsuMyT3A6 zLSbRps={Kh+Lzn&tQ9t>uwK}v!bV`xkX>^cHlVO2*oMOLzDWB!XxA)-^(!m{TUS^; zEON-M*#hfRSPyJfVZ*TSVY}uOtXE-+uqB20M`(XxyJiWjM`6{lMTOPDs*l(;n_=Cs z+mdzOd{^gIo$rRtC~OE;bky!|5>^kh&Gi>x!wU2K9plk4yT4*sox-YMLonOgw-)A! z*jXG_tFQ!YP+^0xO_(ix6R_xUJDZ0MC~On9p|GMa(H>6NH7jBL3X8(l71jicoV06p z!TJ<709%FG>dQE+vc}HlU@Z#UfQ>1v@b4+VQ+CY?SXf~-ur7r)!lo3~0V}xH9$r7J zR$*hXUYM;;%)*uwf9tS{)AsNRMw!pUY?|e;NrgpV#ZkMz23TBSZLkT2^}&j+vulpR zniMtz8&}vWtnhlfX8u1gpI2BJY)oNcSiuc;%^0jfVXd%Hh4sSnYwemNu$aQ8VIvA# zg5}+4*UbAe{ini8VZ#aw!TdMbHS1w@3TuH4DXa(Px!JBc469Yx6l_poi?B_Yt-tnv zh5obd{^a)r%h@$A8f=Y6C9n~NRl|yIvHPoowJNL`Hm|U5Sol`E<`8U9VUw`@+wA@p zU~z?c#u#rERt&4Ew`*3x`V>|R^Sr|DFAi%^SOPYwut8Y)?RL!xShvFFVe1Opgw@?) z*DU%f?OkD&u#%YFUli7%uqN1|!n$BJciJ@vV8aRxWgo%C0#E>s8n+Y*S(Du-L2Zng##Jc%-m$SlQin ze-T(hVGXcVg|)$I8||8Xuu+AL!iryG_csG;Q`jnOL1Fph%+FqH*DQk#DJ%>tc%9u} z4A!i$R@khX3Rva6c2)!Hg4yEP2%Cf1!s~!lyxp$Z z59?6a7;F}13vU)y-f9nT9o7c3rLW+hnGY(g99H%Ydw3C8E6kR@2H3RXuMJlEPP=9w ztVLm?uqlPjz)Id_*Ib1)D=hz8%s&-Y1}ko}YldNQn7L;pYyA*|&A@DV-3lvvxBX1N z7uKY(5!f)y7Tz?h@Q>}`Ex{ULwld0_Wc*WDDXgH~?k@yuP*^=|RADW!{0_Tj4=kpz zVc3YmreJyRv1=~E>J{ey7xGhKB`|-dU9%cir?5KMkiwc_p7+`{yJ58o8-figY!bGq zumxDO%O0NR+l+q-D~4?-tO{21Cw9$RSii#JuyutcV3GIPH3wmR3Y&nfDr_DWe!pFF z6V?l}?JE}jEB&>?Dq$rFyT2%`0cJbTX@X5EtP56t#_n$b)~&E{*t){zV0CBhnj5e& zn5|zboT9zoXJ-|#28GqYMitfw%kQ>pcEDl^>xYdfYz&t70lVfbtX^U3uwjK2e24b_ zr*_S9Se?Qmupxytz&w9u*KC8;Dy$DSsIXDkrov`m(H?tvtFQru<$sstVv-VuxW+$!zw>z*BpcODr^?E zsjzie?9+D5f*HzBVdb#0LA$>QETOOl*s8+XV6}g3*X)ChDr^*1{29By8CaXbR$&VY z%l`r6(P!Q?M?DEy9)*=Kmq{rN6anmcT|7 zRt+oqqTOE|tW{ynuz7`b!@?tW%^}#J!X{z)e`oi%0E;WkGt2k~v#kS)VP#*kvnp7t z!fIjDFxwg^4y*ioJ4?WN6*dUlRM-S8Hfq*f?xKVRNv^m|b%NHl(n^AJdvqisSewGyU^5Eq zgOz>5t~m;8RoDz{T4AfO(r?-|^XHf^DXa`OrLZupWWuf)gEcFx6*j4`URd!z*)>OC zafMC8CKR>=EBa@gb>gw=f4t~mkgSJ*skU16KB$iLb9)}ndx zQ(={`f@!b1m~HR3@Mom&-|egd zR;#cYSg*nwVRH)WfR)VH!|R7NC~OQisIXbslET(u6+f_tSMYPnPhsV-QH4cdn=sqi zQUfgd|Lm*{HlVOR*oMMJVKqOrYtF#>6}AdnS6Kdk(H>^)nq{y)g@s|O3X8$QKeB7K z!g>|f3tLjy2rTqtyXG{kM`25_MTO-p(Ek3zu2~A}R#*tOps;#a)tp_k1(r})4{Tmx z!?4Pq*fpnMT?$)-%_+?P-?WGSv}=~YIuuq7n^jmHto)~T&1P7e!n$EI3LAoz&D%96 zVXX>VfK4mR^FPc7e`eP#hP5cH3O1#%T3E@??V52|v%(UvNrer5IRT3+Y#uhD zuuWLef?c!df0+*|tP(b^uqdqXzwMe$uttS-!NwFe04w+(yXH8oL1A;SQH5>5^8eSa zS-8l2P+=9Y5rx&j@)qryjj(!!b-;!d)(`XlpIvhdR;RF8*pR~3VV+;uH4FZa@m*o% zut9}IV4DhSfJJ|453db2ps+sJhQdZ+HA{BQ8Cbugg>Awn6;`xN`Tf?eSqbY_SQNIduqIgD@9dggurY-Vz)Cjl{>EV)3Y&v1 zDr^H*^Lx8y;R@rC!YW{ef3W+jfwd^C5jLl=4p_*sYxcti6gCFS^PIQV`B_+#!q#Ea z3M*Kpf7xf(EQj?fECSn9SOYBPwQIJ)CKT2OEA!d?jlvQNn}Mw=Y!z1Pw`=C#&wNQ? zWw7G?c7I`5o5EtS1%nv-_KdH7jfhHmk6_^NfFacFj^)zrsQ= z|K)an^{_^TwZNtn)&r}!!mc?C>rvPgY(rs-u=;>q)4#@iUSTD$(tNwWYFL-T>R?L> zYlcN1X4mY7jVNpgRur`Rn}oG0Yymc}FwZ*U-<5XFV%VUi?V5$ZX8coF1+4HYyT2M(i^3XVa|-K#g^KK&{jdRrjluFB zWA`@;Yf{)cY+7LjzhV4)tX;Dl)~m1xY*S$ku-Mgh%{JJC!unuk#dd$Au!O>9V5P^3yTVFf z{!+VUHLOlyb+93YHN!klwrh67Y85sF8&udNY*S$iu;^3l;dy>X|Dv#B*oMNYU^P#* zYu3X06&8oBD=Yzvl-V^0VSNglfUPQQ9u|I@U2_xGtFWR?<}V7XgoU1N??a-n0fjZe z@}6P$*9B`**Z^!=VdJpMa=YdntXE+huuX*({+{;tOuJ?UY(il*u(D^_{WZc83hRKa zDy$z?`)s@B7;IEwv#{a{yT5f+e?3xkSkir^Z1<$qnYlAf_tPeJ; zuu)j`^X!^4uzrQD!u*wXfB6pMgTl&SQwj^iDxPoGjKO*o)(YFOFu50`7gqm*^G=?; ztIkgy|Aw&DVf#xlr0?+H#*t3{)*T+a(!T8ukE4N`gH?xup$2muO4bD)CzBU=h<2W5 zzh~$n(5^+h0d3C<&pYpzcyo7)d!I_qcpnSgD3`&6?zF`lnGpZR(5*uEET1)9?;XLa zBf-#7BSR)--ftpt^X%L2=|JPXu7-QZ5O=L4Bu_i#9-sCDZmx2@712M$jn9GWw{kUk z-%94K;HIqV&>cf}Qgp8k7T9&YH^|MGq3G6KHN-9zhAEuya<^*xfzJI2{~6y|xdut< z#t7OKRp*_cX!9d?)=Xk6$2KkoB6*g34!Mulq=Wm2+;qsxsW;f}@2(3*YJ<_*VB}yh zf9RBf+wS)R)c!?tQ(X`e4=R9XG_8d}={w?m>nx zNEd|T24OY~fi+&`$690Zr<%`Zc zcewuCdO&@5`yA>&Qrp^<_^ZQTJ^q?g{;1dq39r+CSneAVe>ZRUC;8irzXANUx&G|+ z_?4znM}m=)!RVdA3L>4^088;Zi_RQ6ZxJ1C?&|bXPuqRm7tE#YMO@~5Fql^*Y(MFH z@pKi1UwYnIarNBxWA1o7k!n9jB+pKA%hu<(#%t%*z+7-vhW|0N2GOc; zwGvXsZk@g%Sad8{aEu$zJjU%JZija1w(zq3o?+a+B&Ew8x1GKkX)CvGH5FGkgxiT- zxRv_Sgxgu%Zo0bnNnW1y+3vRtQ(ykwR$pYS9zm!3W#^qQrgSK^v(nd}@u%VmSE_RlKE)Kcs7l8IC4IDM+#w{^&U^n1IhnR#xo|A_iP_9@h5FRKYzcc5x;V$ zpH^>o#w%GTPASon^fsV7kM1vB-ON74j7hZp*yL07#94-HK=bg{w)N;!*PHVGN3+cq=PR}v48m~X^IJ?nm+oy^7o+^D- zgg)yoejlV4bJJa1u-}tcd)|p}*UuVfO&uoPTxV}76N#(T;YPH|Z#?g`?nWz?yL?B` zirvKcn$j|J3441O-sc;U@?}+Xa(i`=YjRC}@jsmLpl)yUB(1gRjo)(KS?06!qwX4p zHDczTg16T6No;c*VXBQ65ow-z6ZF8NiGSKpkJFkN+5xGnFj$q5t zVBF*3KhGxGi?>lGzGOTz?;%c))oxwHFI}*;Hu{RB0-c}&`5qykqzZXI?AEEebe)ng z1iO%WshS0 z@~ZRB>$ZoX^o6a~Ai@Ff9|v1rL+iMfMsbSv(kM;Q%-g!quX*)(=jZ&L(Js)>9G725 zfoD!#lI1diU(em=of_F7q`%2rQ=75V)Nj*AaC7zTN-Iv*#ezpOKE39=^Jq7nnRPKe zSGzMS9+AMQtg0w=J4SS3=*^&)j$69_GJOnNB5qwg7L0p8MdX;FW>vrA0q-)vwoZ`t zGeH>RuRHJj)VGy>6NV}Otn`PalX}2D-ZP_2=0V|A`#sS=I`2HozY9I){EzoqBX_On z74xqFy|F95pWI8O^xH4%JdoIb#(&m#pI6#z2fW}->|<{)KYRS+toT}Fsqxxi%fVor zfy2@#p9plIJBM!M&F7sfQ@ZKA&ze)oHTSO!MjC?AJIo%iUG`|;jlsxu!RRf<&AWn; z8-mfBWa1yXmT2tU+#*M%em4}+KHqZQ`IGJOpwB^;sio>X(#vGkT$SpHJS!ZrW?QT(ai#lncAF zmU-35z-xjLX8X4#x9mOn+*Ln?ZZEn&`uJ8n)AORN@;)9M-{-qV61{U8eK6qr`hnmi zGs0oiI=`?vBia8>t`QL~`N=ew)(yhUA~O4pU90)-%NCQLGYiqg5o9Q&>*(+jza&$EVw| zT<^N^E}0MFWR~d7^PBQGD|LlAbXLzyq1FRV+Q6$cA1b&`^HHmXao2V zV-i`SMwhcUdnT$WS94blT$Q?B3tWxv-zo|8y-Y?>YoV*r`YWrCQvu(g)Lm&h-q*S{ z(DzoUrP4BP;kRjCf0mdHa^zkBH?qd`mk%=JpxBNBL2(b&tMScIV-{T`UsQx-HEYi$!82 zi7`3wAwEct^BUKQw@=Z3<%6{Qz9L~skMn9*+IP9ywKw5J%BucCvGDy&It*K8OZ;qh z?;p@SjCKvbP1p2riI{YmTLtKA70XsGm~4>}vl<#*6|cxi>cn7rPwDtYRSHlvUdM0g zO}G9g>R2R|ce*Y(rcXLa_1zCw8^BqTXRA0e_E}qu2BalfJ6x^%l&<1BOGkXC60%IE zY%yYyVYUDNVW>RtSLle6!-a$+paN%truHb z?(@pmoYPlW+>c$p!+r6S(SO03vvOdYd-n0r|H6T7?kjOpgZstmHRm5)_h;yPIQKg# zXXWf|weR=65l`D5BS`N5;4gl7&FPVX;n9TqBpqjc=4{lgk7%Wx2Tn+yUl-ii=lhy; zK07OF>KtPRz42?;oDZ-jNa_)fvp$~HIpa^S1FjXlJA;kB58LG;X6;k*6wXKM)|{Iq zZYNTE3t4L)9hNEz%xZ-rv9q}A+?N_`qfuk!V4 zPE%@MhIZpVkHhg~Vn4@}od+TkKQ_5I8?nkoOdHRCD(63MSaa@5X_;~`bwAz4IkJ^< zh@iphOqQ!H6_j&2<+%%ea znfhV0UW?XK>{`k4Tt%z!p0(te0d2CJ2^2=zM_I-=2W!vpyrk9m;hRS&U;Z4-t zR64WzMWe^jW&35f6SpVZ|$~Z zWa`=+dZTYyb4F5n*Gj&bw3=tNZV)|_*1OnH@m1!~leCsUjsBn|-M_kdr}d2=v(^t< zf@rm%HHX$ZpELICE=p?xt?sw&TI=K1cu}GfuY#v@ethrVq_G~Y4Yc+mje}?ny?xhO zIn%g~R;+c+`CTgS({(oe44>&UPBF#}TJy^-$IvrqPw&`uygq5wa{G(~T9at)a%}ud zs}{9i%5fI0)_3kr8cWN0KJ8t5lg2n&3ux^{8pqJ;ZQGkP<~@`B_jm728f($={PEtT zu^+7ww2q|uFjH64&zthVL3-Zl{gJg!FyoUtv4qxW`A5xYVeikZT0CPY@#;Wp`aOG7j?-v$bnZ<#7C)Qkuim>i<=BYU5?XuF=Zv7$ z-?cYs^i=Hk)c(odq_GCAy!Wj+*Qe@)Sv#cbDs$GFGyaMF<_ya8_ou0=^6cTx*|qyTY{he!zn)pk zevZM+Da`uFXq}Kb%FQyTxW=AHGgp`Sa2tBHXVt1t;UA;51Hc9wcn{>+a?(O^K z5#98X#?13&y;q07p%1J%<*vW8;_r;CXA|PDQ}Vq1fR`P{Z3miWeJjsU_Ts|gGB_G>;-k~eZBkbmRPFdSd;x7NgYtAFx_$DO3I(^J1S^JYgMLYw~ ziz{Z`BU z`jk_WkF}{jMH;TP9!NeeqLcXOnzK*x$K)gHMDp>A$$T{RMe?QK`Lv(D%zVt)hZ#xb zi*YaI9KwCW$Jpa=^WCkdrf)IrEu)@(X=nUQ`H4;!I`tpV)G_tNoROL}dwN~w{qjy7 zQ%zbwwty{`W!u2klx53*!G7)^N&A)jD8bf&ZEM_e>k0i6_3EEf z`C;m-xR2t#wtvm>ZLf!v7w*5e-My4c8}2Lr!d-8e_cYk6pj^I{EEjWTD((hxm;cGN z!teP+fCv)^Z)WA+u$hi2=@vqxp<#LyY|t2O5-at=+sOL%XSc1b_L9uh~IZoBkI zpOe7t;NY4QlQ4OE;jFw*a>f^xK8IIjI``9ew;%95Zr}Dk6UWJNp2Gj+U#~ec#*(zn zW9#|Mnsb%d2w&>MI=22STV56YHMV|n$B(!x##Z&&HHX=(`6=fA71%1Vy<6f)IkbDd z-{%%x?-|l7exmS!p|l^dHDK${vbA9A%d#b~^=8@nvGruxMzD2f*(R|ivTSqMy0UDm z*gCRo{ulAQewM8WTWglB99v74ErhK(%T|jmo@Hyq)|6#y#nzZ*>&DiQWgEa2!*&^Y zDL;~@quA=P$rhjdh;0g69kx7UN!sSI)n>U{#}>twpW!a=#jJlaY(=nQY!PfP7k8$t z*>dN?WMNe>Nso=y!lZm`EDjs7utGEwuvyr{B|Pftao*k$`vCTN?9Uf_L$Dy~+Q+cx z{mpi}b4|;%5xoWQLz?%{ll+2G7>qgqM9jtts`S;HAv(vB|pB(;O_27)bbK z*z2)prl}fR9X2gZ_3*9?o*jNGd=#FUrfzH_8Gf?UGz?$J;Mtnf@WSD=rle^BTLHH0 zG;Ly!V9!ib!OLh5*kla!91RxSK{=evD5nZ|a|Y+_vQ#-rK1JaJ@U1jS_zl?lvB~=> z(tagR#omg2GRrO+J=j;XGzPIPS=^3eFZ{yxG|XTtz^0{P9UjWy*=Zv{jjJj7>DQ@Z@Bv3FVQwb+L(_9pDJS$4^* z4(y&UZXe5fv2Eh^N(qynQvMBL+raj0F_<~v);qnt(#zk>1C6^GS1;|3H+Rjuzo}P$ zjk`_UY$on5c=AoXdRyPbL^M5zXMMY#Kr#P6`ojatd+>jFG>VkW5KCXSn)ZEn0L zO%u8&=_rny9tm%Rxu<*9?7lx>o&{#^*(vWzvi6){?a4ZvX9qbiW^H#gz~e>RR?|lV z^4R?L2Zp_)NfX-wcsnAkJ4%>&hnQb~i9SNYOy@1nc6-0Ztew=5yGH_be5Qu-`DDQR z$`m*h@X`^vuFTp<8)jxYBsezWO9(5OnsDF^{7FSh{wWI>spE}kjD2;@ zc_N?XM{KRwX0hF5EJ<58w$iVq`!jJjfUP9UHj1qn+twUM+)ZIC%CJeBna5U$&4g>x zyj6E^GEv-WGkM{pUM_bMMg{I9evjg_{7Alru~lGun3q2|pX`+Pn%jNX2TRh`1;BT>& zV0#%l@<@Y}gUod*v8_(7Ij=A057KIX_r=|tUESyVj^DbYDobP&rw;TAe&yCFQxB5! zKB*)9utL})_$=YKdu8^YjN2Hj2qu5aPX+&`UH64h^xc2_bk zA@42exQxRF6gCIjP}l~nX2Y&o7}@XXS6BsXU12q_$OCrGMp&Q1I$)~`>xYGZYu6lu z^(t%@wxqCiSm<|l&4T0f4+<-XEh;PmtKPJ0Ho&?S)&^TpSRbtF_jb)uSVCbluz7{8 z!Ycn@*UUe`d`@9yusMZ=VHJ*DGY0EWSSxH+VZE?&&$_kFkHFd#HVvCm*b=O4pItNW zB>O!ID}_xfECeg{+BNH8EedOaO)0DgR^qd34#S!iHU*nh*dnahZ`btKu-;Kv32Z`P z)v%)dcFj6ilfs%|;|lAB6&|o_4#64~HVGS3*aEEJGP|bd6#H!oD~63KtO}N&XV=g^j>Mg?7zpSdYS% zV2cXNyPo;WqwJccux^EgU<(SXhgCh=uGs=hD69uIudrcQAx$9>yx{>}9_PVV#*PZEg zawTjWwzXcAy@Cj~F>L0{D{K8tR#mN?Nd{UwWg4_a}9fidBmSZsiuO7-TwB-)i1x z?Y-8?$;sYxlKCM0!}I&itnBsdk7uv_dG^_7ALugBT}2ADp5WWFr1pLDIV6cNj$Xy4 zN8g4Yif1$8**XOAjGd4A8yg&-PViZXc)AdetX0;mlT^-Spgoeb-?BDiyhgDGaVE`gGf&%ldS?pZUbw|satFSRF7?EzBz9{G$O?BR>HnBE)K??+`2 z*UIz+psm3{myd z0tBOWHU1=&H{vuM?H_|Kb}blyBP88Mm(oi$D{THVsDN-*GTwDP}nsHJ8?p9 zd66M(g+tgFc00nR1{gMWGRgyC2f}$F!fqO1*dB!4GXTz1URw}0{(;`|pV(G>&Zw%7 zmG{5Y-X%KsW1Xo6Q9K7MM1P8So~w>WwUJ7_cH_y~;#q`v(jV+CU*X21+M3$iW{3wb z_|(2tPwUQW5zl(W^IeJuYr&N>eeslh92T>&6h5^LN0E%fLr(mjO~+r-$~%4^rG7(? zBVp;e!9ql_2l0(NxVL-;J-1Nfjyi{5eD@%}&g7%va$ zEst~itV;Q+`UV@h@ub2pn#z~j_zJ|c8}S^2-~RpzduF+R_^iKsQ0FIYLfFwCX*?|P zZ#@nC5q6Ow-AccNb1wVoCyD=T#M6m*vTi&x-Ln-d@r1wZ&j5v8j<9P75SI9FMA+y@ z2Vah(Q`oPBc-~%Jj+ORM8ONwT{MlBHa}keoXyau;cE}*?$^nF>`Os$KkFe?)CfsD3 zB6J&}`xSnB+PG<)$05k!wYEAw;dIPzKi2D>(dM0dRQgmk#?QoD?qORzX~ffdSa13L zu8k{a|J1~j4*$~>56y+vBc7dz=Zbpb!JOk{%oA$oFZz7E)-y0(OzN%Yj6rq2(cBii z>D$qsZ3&-ZjFie^0pi?ncyAdqJ9QD~#n7#X?$CNT)4RIH*fr<%h->Y~d&@Tj$2Fba zn@~p-s&jeqo~*$TN$?x}Y4krw^p;<#C(djO9pteFhfn1CvqK7TuKWbwV}-oX69$4-S%cu)pE&;u=`1+9w|ruCepGu_ z<8I~tgWB`?#<*s+eOVn8eq8&4DeXxNsD|{g`+6(Vo4pAAUkC1)>0zaDOwSx14B|yqV?-s?6v--w`eCT{uhf zM0JRn*=We-_rvX7v)a=*gB?dNntjnj@*X6)2I;Sw(_2;x1C=^cv2CSqo<_F4f-G2D zGmdGCERxe+@o|dUd6p`eC&&v~?|3$fnH$ZcHiBEnnHYcO^_GvIJq7)jJDu+~Cot34 z_;ZZ1-`RBfTj~<^8)v)W_wg$5as5~fdyzWYyB6YTy!P&+V2UJ?g#X#D;^g+!DL62$ zqM!-e8c-koSx**J+M#~Rca0XayYMtDqs&zo5|wzq8^+! zgZgJIhK{y0@Z;c*{M-G_XqyXsfhUdQ5GU2g_29bfl-}}JXal&(=9T)O&OE${>O*aD zV;9%yMus?;X>+R)nb}5VKB)`qw`ca2H@kgU<^8;ht+)?E51DRXkG7cS!`lk5*H+}a z*fJk>!>)2eqn_4w9*3dRcjz#>8Srbi=d89l@Jo~Q6X2H)_o4Xd{I+=rbKWRJ;@G7| zdm}?@$AE7HuGY2G9yM&6^WX`_)jFDs&sO2LAdVE{ zUv}F@WxTKS!EC2w_%e&h!_T)bn9-ixX;7ZB&~1nA$8H+F`lHVBl{CUPn_3d_-$DG-)p^=O{C&FUV$^r& zy!jjtT?x9O%I8wV7d^k4%W~)@`*f?I>+|8HbR&8>9#_*)2G`B-MG(JryDvK z>)Re3*?cT?Y3P1bZFB6k<@0mAHcy1VZfZ+p^M#0K2jWq69B$I3p=(`It-Bn$7<5Na zdbr464|KDAy0y@C`*eBeR{L~YpesOkq>4?)w*$I8K7V_lo3OMx-RS4AzZNV{0M!Y>=7#f~b2M7sJUL$?LGx%9G1Wz4IzAq;W6 z_BOK}dvTZSV`Qe@yQerQow1+C^Y<$%>5y&$blae#wN1K6HyOIE(9xJo*Eswoq1ytT z+P95KRsmaJ!PWy? zWx=)pD_XD;u<5aYsi|}=U@2gSUTscq8L(~()&p#V1zQI!bd5RA z0N_S8H)j1YQZK0TW`S@0NZ21mI6y$Z%!`*>~agX7T6{Wwh7qi z8_aQT2bKi(Dd{KhwutmSp>E8lE!Y9TwgNNlA)Ey4fR$!!F0hmZO9NYN!BzmoNHW5JTZHd?U7z(#)49OrUivn|+a zU>RVhcAE#b#e!`EHm=7UZa1)n7HsUr7;i0D9N2aXHXGQ4o6PBT0b6XrE(eygU|C?j zz)bCS1F%Uqo3X9HmRYb~V0jA`{UXM{Tg-7z1lDQ6I)SaQV2gkiESLjq>?(75tAH)A zVC#XcvS3?)6)ji^*!XXm(;K}EnJ@7Rsc5s+vfCk0_(D1Bfo_F0W;OJ@xV$JECKA$tU265VBHq18`uU5wh~zAJLWiZ zz&b40W?;)L*bZQuEm-R%7~gL*rxyb@*Mdz3)?>jI0NZB4mI902ZcZ-)Y>@?93v7)A z+XQT<1=|iRzS^8#=*t-2E!Y9T)?2Vi!1h?Mxxf;4nA1xGyWE1U0Jh13tpPUr&f5K` zbkDUBSjvJGfvvM(dw`Anu4VtLgYm(F9SST5%rqV)f%RIj#lR-5F^5|YY?%dH4J>cL z^1xc}GWSc{C_M|d8`xqCHuftR|14M>*cJ;m8`#LZ&0M;GO$KJN+2z0-3zh}8(SmKD z^uA|~b1Sgf7OWRo#)3sJMgD-9-gB4;Y{FVI)(LDGFjM{(0m}n3jU5iKUJJI0(!0kT zZauJt7HkW!j0GzJ+i1Z?UxxW0uyf=d9goIjo{Wf z&K_W^EZ90=C19p9Dgf)qnX#R~dVrboH}Z1ypB8L9u*Ci5a0y^5E!aX}JAj$$L^rUB z-#23`fu${24%j*iwi(!N3$_E;8${^z=Ev-w#tHS1Xi?QMPTFCo739^tjmIp`zq#x7VJ=9B@31WcIdMfS+EVj)>yEuz;;@&USRP@Yty4O?dVmQ4+8V9X^)344c%L5T=1PwkzoI(^C=br z>qa>5nt7W2w^LlW==u2N9RCbbsC~`qj92VKSR&h()q6v0;jahrO(cI<>t8|N7r8Q` z&bBz#^Nvv`;H~hx3VwaxAFu3t^ggpoh3_yv6~M-rNr-Fw*U(@6sMr0D!qs6sFIf@6 zxk6XAV&~aqbQW$T+oHC$x@Ybn++_Tv5O!?7w|ozN6X%-miGl~tGP=}vPLVnrgvuj} z@RuXJoKZ&4MAkv)Kz9+vsqBUCVbJ+b>SPS;-=GcO;7)#v@VfqPrf>WB>=)kH zJ^=;pnvb}zB0f*_mKV5u0CRa`7Zq_=FN%?!Ho@;?`1QUYv<Azb3gUVSDZ z+{B|Bx`ohP%5m|#DR`#r{oXCMSP>CaHx!+l*L4V+^TbEoHbJ);y0bXG6=7v7RnBw@ zk+#*8HZF5FuF%yO|29^*5#O=U#XP!B=q5ln8M+L|&*x65HlXZ=$AI{b9v!yjmLa|b zh!<*bWD(4SN1!TAifbw2auC-a+~?_){qi&EOyjEOkKuxDn~2J53VdCbxaJYp*rzJ| z$3nLSx;S*V5Z80{d2N*LPUzP|ug;f+LA~F8QTt<5y6PNs+NXCm(yiSf!_V#~T?2bR zJv8qkPPH#Jz?2^E1h$wUa8&hQex!3L!$|CK@@H>Pp<*H8M=VItPH}{tRs`T5Gp7MS< z^s%4ymhWwaLzRnfkB_%LLLr=U}U`_tS3w(y2~4!vKV~KBH|7yrvLt9m1XJ?*CJ5-M819 zZJrLVFxn7X`+nNqJp-Fr7pjjw(~--D?FC1-g+av#;2gbf+6O-e8%ihP(;U7H<9c8E z=<88Ge%o6Pxm;EKFzqp>(!v+xr{XhMMYS*Y3r6C{wBbm%t`pkR3)O~Q*kdXEA)V0k zz2(O#9W{Q#CYNCzlZd<-do8FfVgR4sh7IZTR#ykvGQ2|@8NjqQR0~DBa;I9PTI_Tuw|C)XgDPOiP74NJ1P;(4u?(mCQ zSqgC+x)S}%j^6T;YCBfuma4yU>-3|>{HXS5Fet{CG}7on8ms=)TYiMo@Q&v+(aVlf z&D|Z=>Ci>&G1czrZK&_`Nl5L)Cg(1=(G_SnYST)u8>Us?p&5N6<~KWg%loJxaMGCEZ>P(w1)+SC`UYTwTS@PaY_ZuwLCExXD)`%Pc%>6=6EZY;8- zA32Q-Q%j(;jgF2q8$jj%Cd^+WyUOeOP2bJG{_aj~GSe*f$9P7^L(~onRQ_~NvI*&L z*k@NcXTN1!#1S9`z^c5So-$%iIs2Xp#Jf9Wlz9FC$cZu zA2WM=p9%e@JL;qD?>44C&1(O1`2DdH`28`wm!MxL6NS1bShfn|?YLd#d31wRxgW(1 z+12#DT{Ty7%7ze_P@2y$C~B7yb&+^+I=^Tjxx3 zc-0T6I{!uFx~JttykXYaMj!A!9lt)u@1)T^)FgZ()i>m+_Pr8mO}-WNYvQi*sfM)F z`!8zm@l5&_+VPk`Z`ZdV;R3yvmkz&-$ZP8^?k`Vl@1BS1_$58D*xDR_i8LueWC9gxA?y25+jeJ+i>8oAz|E#J?92M(rD3oSpWbg zcJ$$NED3ly;U0)?j_@5~pB>S!ou$*j;Q-YQPT!BM`Zo3hd~{d&2Zk}F^6nUyWh(MP zs=&#bWH5Ao6@baqC_`#`ty!!;9Jb5-W)#i8$3Zs{x*xcGN2Txbr9B-(L1a%028B)< z9qy6QfiqW=>&W}b@2BARRLBm5MtXFAwgPE(AF<25x4BxcGpn^p#G=-j!=1Q6hsM3t zpWcQuv#T_nZ40UuixBq?#Jw4DuXgA5s!pi+p~?^J_^taiU;FdEJA`$2(@kI}g7zSuwTR~{9FNf!Q+zp})`rhSd5wdz zMH|?TxJroY|HfN5p&_|>FSSI*P(`Y3b9iC--7Wb3_^fsZ9pQ4Uq-Ib+)FEc^AfnVl zQ>xlpHl;1{=T;*!we53=Z4P3akJzYprr0_W+c8u(5TsXF|ApQ+HlYE&iKbz-g#hH^=A58mVEx6+cdrB@ABo^K@|k#z z{CLPIv|jNd6<@8zF>m}BnZsm2wWgH%F6Luj*;Rhh?Z+{WuAp!FT}j`brSFx*X-uhF z_wHx|gy{+%6UuWV8e7&PuI@{Bx!;)J_a*Ug;_B9!?mfP`$HZFdmraGF?n`V&$}^I`ysMk+k0S*{$z8IS#4_lcMawn%XfLsz`;`8HMEAw z-@HYX?l-NI;Z;bawz|!t1HB9-GQDlFi4)CNcOu=bNOx>29O`~UL4D@Agu1`@2XU=k z=TP?$6rV%!eREg&U)6J5wI*L#H&@RjReXn-J0Cn{!ZSbo6%gO{n|77o?bdC@&$kZ9 zbLMpT?)GjBM3{u?gJE!+Lfv6)mF{e#fwH#!qb~HBO9S@TCYr0m;qcvIM zTKhe$r`)=$?6~z;t#PV$xgQ)|)cZ#y|?g5}j(Q8Jv$4{t! zFbn%04=p-M?-L5#Q#}DK?iRdSERs<>+llnL?%Gwpger?_|E9X3+W$;U&nSL_l`#JQ z%G|qQl%Hmv>UOGjzW5%bziw9Wn5Z*l7nzSG40uDUN)>p|)~$4|lNGmMGMXgjLCtFt{lrM;Uf89Y)yTw9qs z+os`=9h`2&9`P*q5q0PePWHiRM>qs%rks2t-h~3i`FXWG^ogIin`Xs z)7nnN;>c9bfjl$d+iU?m%R8D*N+aKN+z*{RrJiEc8Vi+qjaqccww$4421_zMEhi)7 zR6bmY(wPk%E!B8+i=d+vrg1pGJG_-W$SFk>QRE&XHV?s0fKo^GXv!*%OQDzrA4}Lrcc{xYsN~rZ1ef8Q2 ziiFzdiX7I{Htj0o(H{<2q5Ubo{;jfJiMvM2u|dbSMK+qN1@00?5@MXIQU5mje)LB_ z+f_c&ZJ(7jkIGo2o}tkFZsb{8V<-CfTZs62ezB_@qs@M*zp2FM>n~}p8M(&Xtx)$L zT8+Q8h_9=-t31tZzv>&|>KTvvro6g;QT@lKs03}x)+rRm925#wCn`vD^+i8-gV~VS zXYeHKXqx|o)`92T?_)l(eOLKQv|&=^yE4zJ*ip?dPp8KS&l{T>3i8Z-ut0P&OnovW zo#sS~0>;*Jms&|G;r>(KgC0-uPW370L-}cAS~J>~gX(B3c5U6gt9*`YU&ULEuWCJ7 z)ju^ppuhT}F(yok&oGkgXp8)$^8klG3rA_k!+0?eV;L-yw8}{rf-;dw^v8poG zD;wgxIV^W{b+xCDr+6KllVupg5ltR{CB!@apS#M}yLnV|?_+81T^R#ZKX`n55}PXw zogii~+u9eL)1I29x>=l?X|6#t>F7Z8^(UyKV3v)dbE#6ZudHQWvszd+sGrGce$87ewrg9HyyG_MCeJ@QpPDkI^gXjb`w4wZX!VbzX&-C zZZTN+Q&7x-Vury31&AC&7CHt#j)x?!$3hY%lH{f&CGX_E2yTjTCgT({C3%d`?NazB z0E&?EN=c&VsV|@K5caFSwXZzGFg8@$qw0G*5n9iEYv&K@K85OVH^ukPzVh|e=lA*U zQ&eB0=7K+?uDG@i4uG2ml*3xXWM77xQTfQc2Dt>w{!QwFAO_j9BHVQXCtF%{H(RnR28Sz zd(%RD{e*cy!o+(HW*Yc3WEOuq_VCZ~g9u=jq869Y>uMjuD7=z=%wr#s5!4-+2t}@@ zgpXD?7G6rQ%dN)ztG;S8OrCle?`M3Xue{W)HO-&@@5+7EoEryH%AKL_naK?F zncZ}j<#aW0nEMq>uwMt4+B!0$?YQ>t`3OBtJ$Q@Ugkgvtupu%`l22&wK3QYf5b~16U;GiQXHDrVKViH7$MEdl|D!8;+G0_)*j_u7V}tf;G(74w zrg006%r}<`?qh#0{U!Aq)Y`ER0vxx1V{B$$`KP1dP<39}T-|3?)_K*M!ffPLp2GQz zj`&ZdQa9CyaL!ojc+^I>_Q)2JK|R6r0nG&a7qOOmDdnQJaIk9nTEv1J0Q%V{p)OSN__6=s5a0 z+nRds$PdkCnNIsM4d#L&7gI~GEp5!m_t?y$9rvc)F6k>@i{Esuz~3_HI+ym9Pdg9}RZsg_kHC|j zi;Y|{Pk)ALkg1+Vt{csdQ)csXoa1RiG%?H%OVB&(r($Z~_~;+uypS9F%7+s{EIgCw z#NT-66VSir&bNF#k+l2#&ILPBg=3T?ZDmlCEP)+coK7ikP-=_MOHP+a%7>|wJ>SZDFhrh}2I{|*x_X=2-gl-XZG>4~){4Ihm z2i>VkqjlZT?Sbxl9^DG)dT#I2&yG24mf~C8SH9KbZyj_cpKcR$%kJpY=hJi8ZO|>a zv#-3I!_qva1l<At@5M_5m~ zEOd*YJHn$|2VKUaqkd%*bQ?s6y0n7Uj`&?U?BwB|;V5j7;GY~{0=#1V?WFUK zp;%wOlAh7gp7=x_i-kbA4r+=Fvrc$wKJ5pi_N8^*$~z!ps0K zGtj|{G5B2$zpLPPK7Lcba~b%@x<@HL)Y!;wYvE@r{HVTZZhLetUR)&oM(9h>t9}0H zw~uH2Ht2UlPkyLBKNfnLQ}jX~y1%b{f!4=qrB@MwJB7co8&Q9NpUL&d`<|GuUD8&3 zx}B{ZLNOhsRcbwF#%&F{d6*V;RQJK%NMrH$xvWuCi}-L*x;I)0ECcK;?&tfRkEA32 zWuA_p)oe8~Q+*fp4||YK&$_eQkez4LvdEY0*G(|rbPOGZsZgrx1mP5GJ2zNDp z(={1?8=;Fm)aSm}#qUxgZO(_Pzu{k^<)Tu#1wmW4RX^GK6v}&jU-?yv5Lse-SNcgc zzVkD)gj(N%#y6S5oh|W8BCfFy_m%0kmM&_Wi=c}__XH^rKfgPLG4-<6>Fu3M+LJTd zJEycK(EY^lcCHNKD!weju0~k(4GRRPa$5&o=@A}FDtt}%&NOb&?odnigu%%_)*Y#;gLQ=5_H%UZ9DtO%>P7a{!e)8rmT5M7oI zu*-jfbY1&b*77RjB|no-ME+lkB@YQu+l;st{1j_6^c-D{y(iK0EoHyTeqt3@UsLmV{6nu|En&-Rr|G#IG*sq&=SQzcK_p0Lm0 zFmt7H=UA%|U-Z{~<--tN2m^f#Vm)+|p*w?gs=Uyg!c+8sftF2u{x|EF8elGS-sdlY$Jd{XTmt!?i-FG~Cm?6tKr?oqmPp-Vw`Ab!h5eZykt zyP1054nN ze>41l$lxEfLiaJisP4D^6z7$|?^N=OoML64j{OP6;fw)m{VnEmY%A|Q2h9ln2YnVg zXdNE{sr(~M?00?T3#m@?rScE`^5nk=_JvV) z)e#F={NvyBP@m#TB3#F7eeQV;q+0}C0y^*61l`chhEC08;3tK@70`7-x5(`alr1X# zdX+80e>L`V*l(IZCsAt&&98(smhP(5SC#*tp}j)4931GPHZl&nozNZ6&+feCjvECo zaGU{7AKGD8vMn+nr7#X%59T>~VyyCyxI(S3yZ3)8k$?XiRtuEY&|ro82bQ4(&2~@+ zSSrVG{!8ifinFzpq^KK_c1F6Q+6jfnSw1~sLva;)s?!gO^R^{%<;5n0!Vfp@=QZ5v zH5$L4pST;1XSnmrOn8cXqwVMCv28DDuYO+twoi4<5POKc@Dbe}=lJye@c9&--5H@_mKnYbknob_xU@N-G}@1N3y$v)rX0Dmd~f4y7R&lXGi z1r8qyoB3@Gz#|vw_-)}|5Psp1G5;?I;I9VYfA`?A8tbA`UcmWB`X+o-0RH9x{9TNP zBO!XKUUQ8Pz>^nN_|*i4;=};_!ybO7@P`H9M+V^20`Ox3@c99F%EiM*`<*LzOW4@{ z76;%L32te>S27+7hg(hk%XN$+bz}R!IRL+1@Nme;|DFK+A;!`98SC%k0r*cD54DE( zJ;+c&el6kAf5i;=cEQnq8sN|?0r=ko@QBoJOZ@Et_&Wpe_XXe|4!{o++|qx2(u14q zH$4D9HUM7`fTujTseHNu@U-9-`(NzAxu>s6=2F2iewy&F3y$^|tqQ3f-xM76&xGGD zIL0p%zBT}VAOL?X0RKq<{>uP-43CdA{+jqd&*5qOG~s^?z+VZ#-{l#fP4T}j{87J6 zcsT$c@rAf5Na~+V;m3Gz?X$WPygdMq1>h3`@Ix4<@!7=h;{o{DGJmqHcxmHzn22=R|D`p0r|KL9@<0G|+m9~yus0`SfN zJSn)P{hjE+P4(;a0Q{@~{PO|$#U9*L{+9;eUlZI?ek&QL`KKxVZw26Y3Xb`wDSR#f ze}r-B-%a-27=S;+cqkMx&R>7y;^rIF7Xt8?1#bx%Z(#q%IE}xfeQlSV|B~<+e@*x( z&-hL6sQ9n9dB#^0evlhJ6i)dA!~H?QF@G}Q9~B(+BN3{-ju0H>ld6S79fBkO2?IWh zahiXa@Z$r*Um!TrH-*0{06#eZ|Fq!9f6BoB91m_ve~I80eqRc}zZ!sF7l7XqfUgd~ z?+L&k48R`?z&8cpPY2*X55Tv2a8vme1-F#%4i`7q&sPKRe+1x>i*>I?{hO)&j1Iux z8i2n$0Dpe~9uL3|ckz~16aOiKM?$S8`_C2}_g^M_KI0)=G5Y7b7D;%_-x5BIoJ#~p z`X>Ae!7br$aB*|_+!lb}FSw=u%L{Hv|CcUqPXB)c@c94eFKXt0qGgh0ag%l%OlDN2 z#Xrl;)AfaqvA^6?+JAy&;m7rb7cwdKU)rd{IV=UY$te5FKB>dSS>~T;Ed3<=FFvlr zrCHk2b@l8olh@&5EOU=FmVSc$7k;F}rC7S@Sbmfq)&3opQI^>ajin!F|M?&4a7mVy zWL_hcu4zCu`E5P-407juo`ug{bwK0;o~f8 zJo*&*b=rTDW$_2voo4CtZg;Z(%=dNp7)yHqjpXjv{u3+_XlBF%2Ute6S!=+h*>8HgX%gkyWF2+*g50bgtb+`n}!fo}X zpJIQd?`Z!H%P7mBbOq05b@({T{I}~%Kgs@zw`%`smLZXnK!D7AONWoK%&n>~{RI0f z+@k%bSe9lhKKe1x)|BN%FC0iPqbF+|A+ONG1dBCus-ec|2*qM`&9e? zqr~IW|AqDOeXITdo%MOvN8hCJ$Ro_(r{9nDsS(yw;+DYWxh@A&lHtk3%NUuJ#Y zr@xZ*C7=Fk*2hLw^I6IIq)&ef>m8r|HrD5S`nxRpAF#gY^ZzL8qdxmQ&H98-|19g% zKK<`lpY`eg$okS-tMe=N7P*`KjiIwXJ=Y%3Z|~2d@hQ63$H0Dvf2iAm;?Yux!^UIl zF!31W<>7fEZR4?Rn0UO^_yhc=nO*d};+@J@$j z@c4+!V;|QZ%_x_{T^{>3l}EzmF`}tFOckhH_H}9|Ps6O6A@;$+K+=+%QD5eId2l;! z1`m_Dn^{gj-J-{x(3{#Lv;&K5LVs&aMElX@*7iTn_7k*U=sD&$hAI#5a2!avB_19e zzh8NH(6{yIA|566tGWN0S%01vyNr;Nzi_sVGx+hbwAi#8z;5T4ygaz0X$B7y!)BJx zU)e695pPmfp?^bRm)v=cmCtT350GdUkG-vYT7Jd-?|z|3E8B%QTKkn@nr}yi2cFZ_ z!y|6oZC-2L_(P5LyYCbp?U4v=7^ZrX=*J%TbWB}W@^jTZ3iGPn{zyLHO7!Swg0ehg55|0%AZ1pr@yP;NEd%K%2g}2)CzWcV2))tvm{qB-VIm-OZ0&l7+-&-cUHH-9%4MF*gYMomj~JwF z{2DGEDdB0@sWAA8VU)487aNb* zoz>|!%HvM2JwQd+f%NG*~}2NV(woWIgqCT%GmQK($}hQ(hh@dofAg5~_QEJaP+lJ&v7HU7sbHXM{&9 z?7>we1~4xkd@?yq_4GO6v5)8eupb_!0ouctof_xwg~z`2)sMKPp4LR%pyDOrLFIz5 zR6k<)%j3^<^+*LWfrHSEk^L546NhH-c$=38+CsB< z#DvGlh&pJJnx%uk1pWqdr580;Z$ISa0TM&SBmU{ecpUEK!Oxo<7$PwkV<2p!Jf^uk zMht1WBu{J19>;lk3}Lw>&uEOt$zC2qST2q7Fja$Q+`pX3_MmxkE019*kh9%aZm4tq zk@-wx<+8Wf!_VU!sV^9R$vo7bB=Bdm&-yy;Q@pq_`z-O66Vj(Eg}*`b_+n!`zTmY7 z)_!EXvGE|D!*Myn1M@`LZy7^EHXfZsbQrGX!UOYnsvnY!jmM5U>qnzhKm41x4ubi6 zJv=6GTW>U-ApaSurTPU@EfJk_majjk1?pH#DlK>>S?_^bWHXCf4A@$Il{f)br3vRe-uWa zxdz+I2VEW*=hz@a!K2XNdf6nG2kgN)8wwto2JJD$x_L%MRfIYae42eA& zT&J4n@~F&%hJr`B!F9|O^9YR`p*}D~CK(bQ4c3oyygWc+$apkZE*E%tc-9XG+kd1R z*-Bi9z$3zjqbuVjx^LL!v4`Bxb@XG8;o=dJ_2l--`dGdn9)SA2Qv7~) z-12^Q%4a8uY>bx&3WaXh68N*#kDfZqCFV;ns9gR-c+k2f^B|5k9=k33VkyjEuJD#djz)*!7QzVh?)WfHbJR(A6jp9ctkJ4-YB2)Rcwl7X3A9KWBaQeAkwqP5-#8gN|Y&HHpV| zw#OLH`vTPE1lc3S{Zev>`X|TbAw8mAZwulf`+i5N(hP78R|d$@0*(V4!5ou_VDNFS+UQUf6ov6wjuX6*FMs_XkWH@ZoV=+n4)uaxd!i# zb_frm;h|#Vu|ql`H^rg(u}gU9<7!o;^a|t~Jr3^oyuPm5H{T7%_xi}1ic9*SFPbC8{e`hNZnFAo@_ULHezKYyQ>2S@~!%Yr(~#lIg> zo(tICBYuc^j3Gm?F=F@`R4(1jM|k<`N5S`;MBZmc)OBCYY}Z0^rr5_b?#2KH*+<@U zmiFYgkHlZEeALTBDoY(a3S3T`SPquQ6T+i%d#JJyWDj{?BgXgGgXQrw^T3>2Zn&I_qza>xqQ-%jC&={j}KNbMjw%d2nIH5V@_q)&=m0 z@%acj=HusSbN|81LpuL{cm&zQmQIlV&t4v$ybjbJLFw7>*M*127Ub8)qsPP57Uw|p z;otLon|MfL?uW;!VdAl$@Sy&U*Rv@inFpXZLK&akaN6aFdoZhM}a@_i3M!r9~!DIe^kl=5+$ z#@BW7msUT&ZS*@)CVtH$(aiGM%61v^rpj~E6nNR};#4;?H#`3CuEtkA+-z&x2=+(^nYU5gx?~$FL z%R9;vqoumy?9Ooh6a4;>)D8(BXMK$QzlXyo*q!Hc%&{CFfKO)Jv4ns21+H(jUzqzo zI*GX9a^`dfDz*xb%4aB5RJL(+F>@VgdbV&cdU2DF6x#H`=1mxT>q|;av6!|(42?^IoNtyxP2q9 z`&H+F+HYkfs$kpeoG%s|Y`<^rhlghx>k`>9Ogs)?9wD4NLyH_@1Dic|TX^`N>lVLO zJs$|_&kpw5!(#yA;c>e580mSw>MHSLuspnR$?s;=S6RP`-&k$OXmqTi6@QpLfZpnklpz!hi-`;O{iAQAOPdfd=ty-22JVYSK+L9ChDTDu(e*Bld zRU3b}AODWQzpCIKcJwpzvW_>({zC`#&tLp!SLtwG|0e$8KW^}E;xGR5x75aO;$PgM z{UzCdG=RVOFW$`g_V_pP7yoI4e-nT4A2Rqi@sBhA%uSk4%)`f2f5d;x;NQew{O5XV z<2Uga{|SSCFaOMT9iQ`CEu~teE&OwT4EFEkpPj7RZ-4O<|LoD#?Yuw#5t<oYbkp%IbO%6i&(}=Hpr1ubQ{yug90a)H|yE1%>nHuf%+V!b$!dU;cu^ zN&b?)a8aLI@|X4HuXsbXJ%hse^B4P@4j&Xw@|R-&!Y3%4cY`>>u*m)QLp%a1;y^?mIAJIg&R%Phm= zb$t7>9L2Jo<(pZ)jb(JkI7K za~+JcEX>sI6w4Cxaab0PW}Ic|DD8GwioHeV5B|9P)N-QFU5$nu_Mc^0^o5TFg!e@k ziX}C_DDxZn5%2uR?>prB&rZ?dy#6br3(gBUMdK2G+~A+?HS!C!KS@da=M%N@N4yDe zfE@Ervj1oRfAL@Zq>kU~-y40L`HTOw!9Vejo?6Xc{D%zwP5d**>hPH(wHz0~U;M`m z{=NK*Cv&_Uf9?~t@e}`~&;AmB!r-6yr#L<-uk;)pZ|?0{ie5?hPqM!BHs-^!%Adzk z{Im8~V(Ec~b51if3?Ya;~6f0 zeqO_s&Oqb!^Aq<#;~VO5!soYK&O;slVB-^Be)$~p6*<^+1{!{EJ z#eRD$zu#~@6P_a9X#4qjY}-rPtDo1u?NePd#2z9qg-qtQ87s5~McQE(Yf;U?V zKatIzkNfQ*yhQ$CFPS$9PvIry`{G{CZ*OZSVn3048$aPA{G{CX7Afs@Z{_zAw}-uz zpYRg7x0I{!*jssB@SvWTzRczO3d`46?q>NfmaR**|4}TbU8mjc?0yT&aV%FdegL}< zWGQwL`99Vk%rfrtcPP6L_vw#hcL&Q^ERSJ%9LsquB|VXcu>MZY-)*1J_hBcpej&@0 zCHx}RW1YAqCXssNVmDk%%RwH!{JhY`Ba!i<^vEx|cnfXIt+_7o;58r=Utt`N(!()> zhpPk+wHWd11rMvwmDI#=tKhBb^Wio4-Gbx%^%@Y09|YhV0`R8-@YlO_{cN$MU*Pbe zu$kZ106cP$j^7sk1>wi%GWl)rasd8n0RDFm9`i>IcgqEwf2421M+M+-4#3~VI8MJZ z?$8?_fG01k@H5a4ofv?B*u&2h{;&Z2$N+p=0DepWK0g3Yxp>%UzjFm|2^)7}E)Kvi z65P^$uVg$F4$J27)R%4tiO*3;6G&? z9|PUj)c^ik!lVC+RWZ|!?SiBKG{B)(0`R{F;1Q|cmiXHP@OK8_?+d^`9DpAtxTXL4 zqz508e>vQ~7iS;Az1v_P^MJb5CED%%y^3{50WT7aZ*`S`|_|z9~5B zp9#NRaExCjd~E>!Kmh(&0REEz{Fedv7#<&K{5A1^p2O4lX~O>)fWH!ezsoZ|o8o_6 z_@jQC@Nxh?;tO$Akkmh!!jJLb+GlkoczXaI3&1A?;D<0yeuN3_*nt?=L7JIJ-Dg-FAc!ICb*^iRx(cWPgDHg3c&9a9P>|8 z_*?+~2;%{Qno1mG_Vjt#=58|=R^PUElXNTGj8c#OX$ ze3WPWKETj_yv;Mdn(%|%@S$+Z5dMRLWBz2qKPoutMDKVE&#tJ0AC$| z-xGj87=S+(fNu)GpANu(9)NH4;HL5`3T`Ri9WHLJpRWes{|LY%7wcY)`ZrVk86AMX zH2{Bi0RH{}JRX1_?&2-2CjL_d$JhHz_Ma^{?!Qd}84@WTEHZPh4i{sYTdds)mX=^O>cTlXT#9AsZ0)Y`=uGN)N z=DT#bB+KGw>q|c^{#a(t)Zt<*tqJ;o+-G#S1k1vr`qEFaztX3*e}`q1r7sz)Gkb;( zA7`0Ay}tC5?5~*8{?jZ&BCRP_1DVrw_!!IFsr99wV1I>EwEq;#(!%<}3k!$+M_Fc1 zt}lF?{pC;6{*x?=C)O8U(oc(jmYD_hg^#hn+hj6#FmD)8QPJg4<-2 z{blFsaB-ITPc@c)lKmHt*WuDEZRxst_Ln(Mhl{byB^ygW!Tt-!>ToHRZaS79rDL>z zhh>yyc1~mI$Ju{=whotMX-U>q7iVezX_g_DnVF5HA7lTy89H2orJIoDN1;>uPq8db z*KUWUC0LC*%Ko#{boe;S8jn6jeyaALWLfOc?leoEce|7QXO7n4V=V0fG?G1v{jX5@PcO(9FAr7liDq~P40V%-cOLa_ub0N{_IZQr`-pz+sSG7A0N^4ap!*R{^=H8 zb-=l_^aPF`LeXEN=L{yU`cE6hLFu1keq&k?4KF|tzda4cTKdM|`2EVmgTC`l7x5^m zU(Nm3jPqn9$IpvhMpO>&bezGT3zeK1lO*brFL`-TUBT519y;af|II9)zp`CIBlHLP z$TUM?m*|{WbtVH`yS+R>qFFplG@DsIEx+Ra7w2WQvR#OywO=vr;xOf^F}#}2ke1L8dN{hb(Tx)uxcJb_2XKX2kcP?52sEZ=?3eEr-_KHH`0&nC!$e*YwS#K6YK6TCdQ9yf!B$29KGX4d0VT)SWgGA?T0bXn(> zagEpfzhPkIBJrH#<-z4cn2pECVd8O-mj~bf^v5GQOgz5o<$;n3DwlD?#N$Snhiblo zJYvJd<908PI_k%SVd8PW%R_JH3@Vr6e@PAZ$w9W4$Gkl1u*Z&J;_)*tk2=~*X_$EY z&dZ~Y_Og4Jc=$?LIh)zPzby9~qx4fZs;?*}YrksZPdewu!S(O2d3kVsZ3d5jdUg^5FYX;?d~(Me5$c)elp>Yi7F6th3yD80e_KH%X;w1uB-Ubo{7=64A5 zYjyh7 z8a`ER8Av}lSRQUYsZ+34Z;w$NQORNtKaZev z{rFOsN5thp>#4T-;SVqF!Sc97c(_liFa`~P$6)K}70d%q&D235)SVHJl4P__IanUo zyY>jX?WGy^FlDzHG*y6!8-3}9%INJLIRQIEfEhFj|YVZy?pE8VJnv+ zO~MVAzg+SS){hNRF4$jIPdy!1XFWAg?N{}bmj}vTOp>>R>K-7E_#|DAvn>75KO;O^ zVGph%F?h=RJ+eDFO!f3R;jxeB{;(e&r2*Q*k}X&Ld*QKfef1-5si$s)1N`xl@St)* z98^DI_{-zZboEFDGJ%88jgkGbqbocpy1XUMt##U?(RTY+u?LMWb+nhFrJfeQQr)jx zvSC&479Q9qTMv&3mVI5;1nLCBzm|2ch+97>%lSIP50$sk!ebvj?h}V*@OYb-2iiij zc*KOq$cQ>9lA5K1zXbjUa}^Vf)!Pqwd4R-F@yLF%F&>9|dGLNA2Zl%t#uy0OD356_ zj}b#!F8KzZqaWwxF@)ukKeDlMIoZo&2+O5W9;Ry0jQf`}*&Z}cZsjp71#-6g$_@3- zKaOgwT=o`w_<5Wo^#$WEnTPt51paLHSzo7p((@a$&k}DrA$_`1_!~5j1eg4Z5c z`;qa+#)Ehc$K?nQ%oF8(nHUnX@#rL?!*DGZ9+Vn z(dd1|w_STw)~RC1Z{CvUE~&^Ce4y$(g$MPgNJEmb)sL;>!Iz-i&!kV%{a0yWWBu4e z!edl@?KW3ux%g-XI`3m1V@6?_o9kcUf&05=+T#~q97jLGP!61o5EDRzJGx9Op8hXsq3Clll?He3ePW z`r)zM!Xw7-`%xYSa=qZ%L-%7ceAw(^<1vu5yaQL zcHu$o7L0<*C2FZ3RhfsQ(f7*7F^@5*r^JJ<{_1JHJakO;|9`je7&*ed-*pf?S$`Bp zpRoqp%LiQ^80Xj^L%}04rLlH9$>jliaL$H;N2o!2OmTU@9z)0@)}TFRyF6eIZY)D$ zj|SHd=eay8^Pr*NQEZS$ig|=aj!+*6B9jaWj|SHd&++mAi6P_B;QHYOULKzH!@>3+ z#RhpeE|18NmP@F?_HwnC#}JlFzQKBWlb6R3mP?~N?i3y)MpnuN4W`lhk@P*UYxKOI z`@B4)T$;dRJ@XhdVq~RX9{`V#?@AvN^J)5bKIY{?od+(*X}nxiK+nHMc|0ZM(q6f* zr07bXIO(TM!&1XEFZ_ke1M@;8MkYz%&(8xYb#>LrBknsdM8ezT@181kZ0Y zkC$9~U_A))^&op>>a<6u!E;M?dF&zga~=KIW4L&PWIegPvObpYhX9`LK`6I_M}iQj>UWXM2qCye~jqPLMrP+%F}E zsDE-?9?~P~^|l}$vhQ~!%t)8NT%rxG*I()3A!EdN#2dt85~n|5hX`2Ot;{m%-q z&zs7ja#<6+z7G5NvN9~ry}>>=i+x7mjU%oLmm;@@_tg@^Vg!zKwaWu1`8AP;DUZ!8 zr~6#H-~|WnyDa5nqCRB0b;2W5xdEVaQX74LZm@)&c6rF%TPV~F9uK=bDqnpg9*v&6 zHCTK6$iqYW|7P%b!ox#G#Afh#%H@H6gs*1Q4@uiAo6(PK7JE?t&P|5;5C8k7UY>8% z?`GJ;pSNelK4bnpKk(az+}m9HNbjP3+2)t|%J5){&eg>myg%9@Jcx#eijBt(>4e-A zhvvsF;h{e_pdzJLAm8Y5aKGpEef1dP!S-kdkAqwu6@wEGTe%EPDb&XDF)xoM+M||P z2=^UhOzJm8w?gbf#l!RJ7=8wo&ocJAkfr}Vb*RYS!2Ckk-%W9H(OLNwnU6pH!ScIB zcwiq7#Vxft$WB9jKYxdp2aHiKkD%N-Vu7%`Gv5#lmjR6d@kG$tB?a6N+iN9X?sF#OS zmO6M8xSTez94wC~gh%D}P-P*=9`e3MjPJ1r%j0S0fjPIznV7`JL(=uzd$2tG@sWFP zsJFuF=TcsE*54f06AAB^1%5uBYVbMvuf06DuwsbZR$l7@cx3-uKZlL8^z*d2|KQ~z zoqs<(g6v^SCrJNiFAq;%2WpR?^lbR+!oy<=@@wPK=`B=2f92^Fm>>dHB_?MP+kYSJdn{kczE{~ z3?+|`xjZnC)xl#Vk9R}Kf#Uk4%L5&I9Xv)46OS1#4>XuMctnSZ$ERE#*tk*$kFmqV z!&J`AoDaCqb4OHX*w&v3{}g+-JpIEP{sp+@>r%ypYWZ(Rs{~w z(aiGM%61v^rpj~E6!KxSi&Ndq+#LJAZ(RC_uO0_{Px1xk5yE$tX{QQJk($B7&5-9u zGt1`{DIeMJ0bUON8eK0ge5<-Vibq$wJ=rz>-0kJT^Vgx^5p9r1xXANpvIo!Uhk{44 z!Tx?fFAq?m%fa7Z%OzpiXG`(ddo{DZ+Bl}U{W~l(EHPTDE646A=ReQikJ_Jc37=zq zmi@nn@jSZ|T#j*;;{))?jEA_t@`r!+1+H(jUzqzoI*GW^a+YKUDz^%c%4ft?RJL(+ zF>@VgdbV&cdU2DF6x#H`=1mx zT>q|;av6!|(42?^IoNvYe7do5;(9NSL5vfAJEhKSjK?ir9)lPs{5+g98so9r%VQAZ zgr7(9GmY`sDLinFiCoQi&ibmC2iK2g@DLk#Wi!UjKCuVgH*(Q5V|?-2L4G&G9+I}m z*M7(2Z=|xnhA`WBBjMI?Tg$TaKL?5W-ses7{6WI|0v-aPM*{%x@1ufHA+#NEtxJnBVmzx!1)r}kSJi7MFkI_HbY2HWqO z`{Ci4#=1my3=@w7m`4cb&d?%<*uZ9w-4-7H{w)33>iIxWe|E6f9v%Y_50BHe$4Jlf zRac20gXQ6kOMW+_zRLPl{`_h?#>Tu)(MlbprE|@Dl{8%twIwJ1lLr4ShLRTl#hYs55BK9gZSb!uxGw{lKk9fx z>^~L2U;Jl!bU3en6Myj^Gx#_07yr3$*2Zt*U)-+!CD?yDfWP=Jd_#xx`Zw_x|0#oi z6Mykvy0JEX6aP5#ci6uZz+e1F4gO91#ea6C=Hrdu#9#c!4gS6SGeyqdb6QHZDp>gE zUI_N@LQ@TeMrsBgyXK&03#kDYz?HpSh_zyo>XX7~}P(pX<@_C0G`|X^ua| z`qDSdxbScoj~$`?MbFSOd%Bi!mibiw@gxKA;%WVdPY2++k8^nTmpN6tV=Qy0^dEmB z052@;KYS_xPcXmI$vPf~Wt3(1r2gZN2jKY=`wyQCzzc`#@WlmMrdftqHoN>Y^ZU@WK%?T)j|AK!oc!(ILk^Gh<1;&GZ! znq`P(CaLu?mbqiKJHfJWjCQA3mgZ=;!!pV;J6r4HEc3IpJIS&*Q@hhFgVGPNzsw9B zF2*v~soe>dh3VRzVp*D|-L~`{#-l8=Q?>s%%Y27+Cs`Jc*6uXR5X;O_S|4MXo1)zb zmW70Nr&yLgsof6Cp!B2cFMFg87iXFOgmx!c7AI?Wnq`QkE&UAl`_lfUpOyYh`VGOQ z9ZG$ddL`pYjO$tczcr5(OTk0z&K{=m1WUn-AJck=WsIfZ`9nEgmLZYs!TG4hLoA(- zuv;Qvw{r;VMN0TM>sdPgrQL;tv`n!q9mxJ99J`||v$1MC^B>wBW0^ak8ZW+6yHhNS z@2JL8>@JRDe=H?i#kaGbW$|sk@a!(WmHn}F!;fV>%i>#n;n`h$Gy7xdhTos{EQ?WJ zcy<@Zus@a(zSOSu4$CM@!9`~F)Bd6?Go!21iLyJ>#{O8k;YYEaW#&!3@a)cvWPdE( z@FQ5yGPAEQJi9adus@b=_*T}l%tUq5!y9+OBeTt<-Q+h$`9hOm+f@g~wkF(7Gp*sE~ zyNkcq`ZP-kA7Xdrd5yrTsZ9qb##qv_8%<|4Z#ovMl~WyVEQ~EHlq)eT-$Upxp_Ug`aD8 zie>2;?RHp3S!REx^>LQ@&Dx!0={&97QI^@K*gwnskJ&%V;zsRGvrPQBx}VEz(C*li z+Mn1-$;xu7t1fJ;4qxE#xhLuipWIR%KF;A&kJlGI>r21%Lmgi!Utjo;FaJ3XpM9*p z@QG)u`KLL2yw331LUs5!hYvkkU-~6q_|hXfzRZUD!pGQO?uXicf@R?m?M|^QJ*?dh z%P7n2daaMM%s-^vNtVS2wL8r+#4_`M*2h@p)@gTwW#I?fonl$~zIHn-qb#%cYkiz$ zKBwJDmc{$DJIyl0GIOuiM_Fd>VK+;u;F-0oXPNn)b_*V5cjj*P$5QajU94xBS>p@O z?#y@DAIr?0>}Hv{gWW7MtJ%#mb340PW^Q9Q%glG!%`%f^H_Obo+0D}Be=F-*X1=A} zf=Ah%S;hWX3ZA)z^(-?t`@*w3a})bx>4xuNJJR@OiCIu`Hcm9X`$OQd;ZNEKBFHn`P;9>}FXySGygSQI^@oS|4XA zJo4vgeUfGIZ0#02EpC>Xvos!Knd{Q-1k1u_wL8VKbf$JYETb&5pV9g_%lsnkPO>b1 zTD#LMLo73AXnl-j?sV-=uq>psJH@hensz%ZqbwzVBJ-zce@T|27g=1W@ia@(huEDt zS>rL5g6B@s`UK0uiQ1iFSz4go4oe9aWp{SI#^Wpn&!3?6NtVTV+AVlm+$=M5H6CM` z`;>MkSQd`g?i9_5da&Hh3xGo9LhjAeehcBfc|SZ1ejyex}TwcBACW0~*Z@GL_tvqx)v zoMrwf?M|{RPSNf(%Mi;=7KE zW%1)2o@I<>{%{V@GQ=`FiNmuj{x^qbDfKJL?(AV2kF(5wOuLgTi-&4=nq`P(=A&94 zW10Jib|+XC4$cBff}SY{?_eT-%9 zVC_z@EPPPAQ!Gm#&~Arilx22;*2h`q->=2_?RHp3S!NH^`Z&vcOuLgTi~p(JX_g_DnRjb_jAiaWv^&AFaDaBFSeD+U-44rK zPTw!YS?2CzH_P0;>}Hv}huti5YuU{*_dRyAl=yRZvz}${F70;X7xx|pMN0Z#Vm(V|8M|3JUt~8+=VEqC`s{WtVm(Wzo82s( zFR)vrq<Y&&Grwm+euf4ckR!5=RC$)iv69> zv0kL4FZMr|ahA?vc1t<1+c}5zl7F#3>z%V1XDRk~&SJetNnh;W#W+jnv+R~~V7GH7 z>m_}$KkJ>(FwRo!?<``yNJ(Gp|7pfqI%lx^8(KOyvRlHj+gZtamd*|A7AZWhXFW^j zI(D;kGVGT4+3j4*dX~;L>=r5UU(I@!&I)!j|B$#Pe&PRh_Qz8AJ6~hH#4r3=?_9;W zg+J>ZuJ?%pw7U{a!RUjP2|78E{FX8j=_}u>Res{4?^DFGo za{1q7U!UKnc8mX%!M}I8HP!kd6+rx#{;xKEdY8e8Xn&HL_;(EcBi;l!K>BqZZ`OYF*S|OV67v%Oaf5&2U*LQSfAOFHV{QB<{zN>q#+YDJ7F zE>$3+GExT#xW_Gm=x+*YL{O=U5k<%9%tQs3xJ6vhx=h?as*G_Rty^42e&^<@?XwwpG53PhY-evL{Xv>}09$F2x z`fFKW#x;f!kM$a?S7W{9Ci8jg_Dy$~ybf9kZEP@k4YXyk{r2_X)zGHfkq=r4ZM@BX z`-y;Z*Y#m_s6!rIPhCgtuVtmVAJwy< zFPQ2nA7+L;JUp#+>rJk=%V<}Z`j0tXi|h9{;~ZD1|ETjS*W#MpvF=dM_i`<6_%rhL zFxC%P+&BTx_oz;$-E&_DCMTK{QrWi_7fYCIoY-10>x zzK`QN*Hez6x;>Rot=H|Gd48Dk_f%|0y}j#H<$rBWU*x~aajJTLtt+bI)$_evYaI{k ze8&0#YnuZZ|LOIbpLITC`{uPxje2{>Y0Cdv;a{x>*Vccc^X+`J%D3LJeJ1`?<#&DM zwwdRR{Ojwt!g{dwRYzm<`ue_lJ@%@jW&Qf<%FOn!I)cIVm7DAJ;H&B?Z+%O59q)P7 z5o=!W>W1~Mo<5G}Y8Tfhu-;>q>W@qRR(r9ox93##{MyQw@O-A@%?EYAnAdmHp#kUx z(2Jp$L9c*b4Sn}+^X~>`V|_04Z_pc{H$xXe7eoJp?eD_+JeHL>KM+2E@Er&rLinEvA0@b)A4U^?IN>#fp8#&1uTCNSOp@n9!sinn zA^ZWtA0qr;gs&j{S;Ai?d=24W68;!bcE(1mWWdKb7#m z2rf>8j>`$Zj_{iWm)~u&MDTuMB6~>i(SkoIxUBOk!dDai3E|%ozMk-2mzw=Eep&YTmGN5&F5?FZF5`y?F5~wS+#wc^;Bt;W zTyPQY7%RAppCEXp*kH2Y{RE#TxJU3=g3IIda=~T%T)}1hO@ePB@;3-B=aXfEOaK2O z{3*d@{uc$8dEO*^jo>oRCxXj7-w^(*;BvpZ1*vj*}4-vkC@aGACjqrB` zm;1Gj@c#-vTGanX!DXJZE3)H6^34U8dHM-1>obt>T?pTk@cjutl<;E(m;NUZK3Q@giRGiGs`hohtIk>&EGXUr6}n zgwF=I*6l{Y<$g5?F8xOYm+|)tF2}>eg3I{j;KqBLa(`Eec*)-*{1d`|AiVslYNM;t zLr~`5g7ATa??(83gdZvR@o5LGXG8BMr$(vorFI~_!ES`O!&Km ze69w088hRe#ml1wF;kOdL zOmOLEx!}^zi-ac#Uq|>igs&G|`sp3Y_M`MOfbg9N-;;0;;YSgE65(eOKArH(2)~Z- zTL_O3{tv=qgs&p}eZoH@JVkipk;A42$EgpVftSi(;w{2an(5q>q{^9f%} z_ydHmApAwb-xj=|IFLRj{5!$r{O_2Z?MKOb6Fz|OT?pTc@IwTb^Z9XvpDwucIgRj3 z3BO)&+3u}^%l!8d{wU!u5S}3XW5T}|T>31ZlkG3b`wA|GgX52c4-u=34-TLWHSK5#89SGlp@B;}yhVWAam-FQL zgwGXR*5?+&?;-qOgg;C8>x8c*{7b@rCcOLg*?y7x)lYCar|dxZ?%+laIX({{d<@|y z2`9+N(DWF;u-s4hV}!>Ef0OVJ3IAGfIX?eK_~!pF+mCX; zwjq2c!uKS66yZk^K7sJF2%ky#RfNwIT#ko31($VunDA!^e}nLk2>+JwE`PJ{mn{k3 zk?=ng?j`&L!lw{^0pZsYzKHPq34em{R|S{-wVLqH3IB=k?)BOF%l!QbA58cN!Ve~V zEa9gSK9%qb2oDi{1K|yXKS20$!e1c#Ey6z}{42tLCA`Nx`+n?4_#ncE6TTnehZF82 zd?Mj>gkM7VwS?bH_!7b&BK%3hUncw=!apYbTf)~9-fMoge{;@Xg3IfW!Gg={mOTg` zMfe!OW&RTh_Y*!{aCttujPSXH-z>PS!(D_wNcalEUm*NV!q*c11>rvuUVejp|5gw_ zfbg9NA0fD$=MN&hhVWAfpGx?JgwGaSj;jTPFD3jjaASMezfgeM745$+6&{u1@+OZXoN-<5DT;ae=*_L625@& zdkBvb{=DR(p05+WhVah_|AFwbo9+9tH{sh5KA7;`2_HrHQG`z*{2anBB7CmoVlr4n z_%gy*NG|$oCE@=j{3F7@Cj2+TdoHl=$Nq%xKzKFb`xAZy;U^G2iSY9Xzm)K~gfArg zUcw(I{58QV#r*It;p+sK*TerM{71p%d8+&t(T}3t3c?2vz7yel67C`V=krZJ#-SoV z|FGE%dtN5Eu5{07vWn8F6+EA;bRCNNBGHtOFz%eviG@MaOtNukc}6+ z<(NYFg(S}vgdc_e5>r^ZnG*z;&()E3uNC=a{OQQ!R9PjT2ELi%^8WE6aO>ya<`RB8 z;SUl19N}*Zeu$WdKPUWuf=mAu3(fwr`rnc8y$L^>@QH#SA_l}P!mk%x`dm!-KM8-4 z@DB+8PH^e7$D(X~B;Q_e>2nz22NCWg{A|K66GM&-R}uaZ;Xe}I>sI@^ z{fTh5;L_*egr7$E48rFUeuv<)o{tc|N^t43neY_hy>83aN9Gws_(;K}&!Y*SB)H5! zlkn>azl-q42!Bm*>GNa4e-&K%>~p()eRd>#AHr)0_X{q4UQGCW!KKe7gg-|3tAwv3 z{3pSs&)$o(^^ts#;L_&^!Vf3>RKlkdK3j0<=QhG07F_y#mhiU;|AO%Kgm2kkU$>nI z-&b(y^C-g4Abcj_*Aae);If`k!e16#`uu?K?+M@Rj%F|$PWV#7A0~VS;m;HP8sYB} zzK-y(2>+SzuJ@90Mff&^?@0J?$;IpK;u5~|p{T&05@vrm%DR*r_e<%Lxg3rb5mU;C z-QI$~UXHXs{ijZw+TS^E>pGQXd>CFfwU3$7M|X$A@K6dLv$cQP?OdaTs-=vLbK;|=LUjgL|t zFCS?iUDUi@!sGVCjE~~xkH){qZ)+<2jQKfyRHz;?KGR?-8dhG(B0|d2Xoo;Z(=1I$(@*OMA{Q#h&-w+J{jOHGj0%N3r8{nD$}R z1GQ*RJ&N^FudjpDL2Jw(#!?C&X3M$1*RQY01)E@PlcRR=(6(%1 zwDh5lTVq}isvqW~8hs~o7?10&l;5`OaKuh2>^MF=e)XA! z`8ZnoKs}1_5rYp$fj*AYJ{*qna`k$U?w9+lJKDzen%4@egHC&j_R+m-neqQ38a~Rp z<$i~xj?JgFPq(jqopX}*p^g`0;z;{Y6WUT8u!;0s?W22{VZrpF9?AE7+G3^girnL+ zE!)VL3o<^smDI--!bfI&C`a;djHPe(xY&AJoAKc+Gdjg6iqS)b_Djp<#r4a4=|hjx zcKB$F6?eSclJT)w_PA9(3hkF->v4zhkvVQlP>-b&(SgTHq4n@cA02qS6zbzh>7xUWmqLAknvGoX1qSuQT1pk@H}^=^r2o8DwU6$GCsO?==f+k zqp1)D~8c71Bou$H&+Db4uNN7(>(?r@?kypIRoH#%YD! zXZ|F8C@1OTr35}+*8O7CL!WO;;NvanL!WO;;A4&Ok(p1Gk7DQbLdVA^(ubZuO5o!g z=|dm4CGhd9^r5dmO5mg0`?zmV$BQaQ=D78!$Hgp#j*mXlhnPP+#h6gr7V2ZGj1S{o zF|!^nrN0>~HcO4VMrq4ZTj1w(2Es>04`o9gFUAs7KgHh9HR#v-jf8Djc9lNVI5n1_ z`YF~&tiaC;xidbLi4Lqs)11Q2`=g|fj;zP*!h9Sqd~_A{D0W`2FYvnXxQq|=ImZt4 zQCHyo%V`-OT{EAzG{$MM^=K@x9%p5IsP~c6S7he#Qml{q0_!m&<3rV>1N)`WdIU2* zR6RP-N1^q&Ui&a#mQo`_&F!jx3Z2J_eSQ6A>BBhTw$q2XW&3~Mk@0~}X{V23>v6C2 zf#anV{qm6XQNn(CRQkaAr9Jg%|Hv@6d@ADu7s>4$AH}{u_k#4H?&pl9J@qK|{mNAt zAL<+4iWsNG*5lvOM+xike#Xb)x96O5ww}b=CD~Asf~bA0)iZc&m&L^?50^ z7(RmPhm8;QRNlpdx-{KaSbetFKDwEIFVw2Nb?Fj9ooARNGVbsG|n2(j1FU+Wp`#nngP#?E!%Qy{cqa9}53FI7#|Yeczgo*&E5nMD3%jyj*>vF6~2=#1S)o*)2Wn zVDW4!d{h|II`-A2($aa1_;5vX=Z~5q=Xqn#O7XdIqg`V;Voh~gE4r;$KGF{zl}cD$ znm5usG9wBf;v@9N{R8u1I_>!1$LM-gr$8*`+^1Nm|1%8fwe8xxaiJkda*+w6E zP1-zpO2$WS$6I~0px<8ENIuTU_%O~g*~RKZKhxAkA9V%x%Q+bz*$E=38f8ATpVl;3 zppOf*kM8QA#c^A&8Y%R3GOMqK0)5QUK6+;7591x_V(Ve`)t>cI&a2w*DIZ{7GfsY3 zf=XF^mWugL$4d>KY@UB=Ecbab#_sV%=>tt6&%k+zKfMV3DZH!Za_9RoxB0d_L z%>8V<#{3*n<7_BiH)`$mqlI~V=suD@mbT(U$9gR9JGbWJ?o7X^zk^f_8*O{k4^E9b zZALd%Js!@~BYU4wo5#m&Qjgk~%zmj1nf(zfuwPbW>QUaNdTcBoD>L=Tj>;J7ktGVW zvgmQ@!8nbfF0E{qdHRh!KJ3P6W<$#Z?W19u*+0!tOBh}KAk#0|zt@eaXf{q+BJHEK zdeoD7?8{6&ve)(P@uA0Q@M?3Mwp?W@^KIp+Og+>%RZkjy5mP^f)}yUH8m8LM>s{C4 zI!&Em^zxi~Q0U*udS1r3u*_dNXKT7Se5B8Fn8#uz@X=q?LmwaQsYhkPtVi{m_H}Qq z9^1=$s0l-lk3##USRXr!dgys;k}}WEw{>J**T-!g=C4|4V;j%6d*tz<`$^Wcp%ouG zw%RgYTRYz#km(mSkD*^YsE5uebz{|IjHrj6$#lQS_Vwc+sfV6F8Zdt~6?ne&W$K~k zu{P9WWBHhr@zKTjbN|fo!mdwsU98%Cm3h3jcAl%Vj2oS2y7Ym0%%h%UKG=1k_M!6@ zdR=(A^db6%`C!M3)kmSni}o+||H6GnTS<-|xnJ#hybwMrdaJGV z1e?c)ZlfB0I?C5wvL4w>czVBk*hZ?ygR&laVpBWK?ss)PJn+*|z8)3zP@fS<&nHG( z?D*MwBh}*>Sr6qyFU*I2ekA;CEMG=Dr96(eZpL-1G1;Ro?fKl5u2*|?JME)qugoBE zsbp*(nI-yAw%K@p?sa0bFL%AQ>yn+|!_l)>=Dyd+zo~t=Wj!#TxjMi{}yQh8=P4=v%`b(J#jGF8??`Ox=Qo7%@LSr5$FF6Eq!(|R&K>Tb*3|5$aLT}Bj&juStq^rrUF zB>b0a6Wxc)&u9;Quz2ZQ;+QHnW|gp{ljcBpSCQ^Jw9T3oEXW} z(%LxvHdBv2nfb$|wqy4(8_S2|LtH2KRQ15|QmcHh`-cT>tVeT!_1GLf96kGFK5y+Q zfsbuvJ#f5qL_I1`%pEUk*YYldWj%1b)T%9t?UzD*jL<&R_hcHw!zf8m{VY{Kg)EiN znfvb|@4K{?YhU5R@PMwN_0ahiAaFKR`_RwV6zJz5;iF8QZ?%^~>k(T~*m%%>wH~T{ zsQ(wkVA=;8A9_2hTK`VM`h08a$4Vb+fRxI|1noopKQe}AOl5R@EG6?;vGpj||NnyW zVf-1M_7O8o*({F|A2y){p$qlrde#4DBbV03hxViEQ*1q+(cepL_|U~rhFbISIO=jg zv{)aL^!VtNJzj#!N1?}Urk2mP@A#bx9~C{aF9;ew3jTbe<@*?0t}e{a4Cw>?(h)ui z{XT+Ar4J0%j_}bEDy$x}Gd_%?)?9*0Hg&(ulRiq=FAJrQ686iSj1S!0sy-=2zs#3D zO4u)pq>mEz%M$4WuUmFRzr+gs9uYMhHdspezJoUNfUZyX?CTJvc>iH}#)mrJ=yRzu zS?KGSdcWGMPiB0mzdwf$qaK^u$1~CgPQj({F;8C?8K)Z@$3|^iyDl1v+U=KZZq{}1SrFds1;U`gC^jd|X$xzyBZd|ty6<_KQd z_UnWH3-d{R|39R6q3Px=(^@@fck3?Ytn2t%n|PeQY~wYQ`Rz)r^V`MabfBL~Yx6ME ze@oc(U(-HA<2(EV* zx4o;#?Hm7GT>GB3ZcpZorV(vx-$S;qZR7S0WL|99vu*7+o?{cI`mr9{kF<>IN)lgf z6Q6ImH0@!wEA!_Y%XK#K`NnW1;yo7sVu+uI~@YNP-9!DhQMUi#NQw7P8K^NpXz9XHIM z&%X!p!8ZDD+QG~(ulol`F9|`zK!*-|D)+g#!LT>N#^r< z-(5EG>G9*wjnnas+i#dZpMQ@{yv}8(P1|jlKcD|voA`YGTLx^HKcD{~;uo~B{*^ZI z(tpr0t{b*B{mA@={{~B(u2r>7d_MoNZ8prWbJ=N)O?*E8&0BAnKcD|PoA`YGD-oaL zRT{z*?Ea+&uaC&>ZQCRDZnnAqwA-HD|J1c{d+qPL3(e2B8S5UopBH8MIP)dA^d=SH z*>~s7O#KPpUgW_`^63q#2;Y-%58+1>elob>b63-1`uFLA3+(U{zIsWv4j!>V#ohLN z0O6mKJm(1iGXMF6UqbkmgwG+op75IqZy;e z34es}rwD(6@Ye}{pYV?e|BUc&2>+Sza#Ek(g#Ur?0fg^B_z=SXO!z3l<@_+3@WTnO zA^Zez>wI+z;b)RO7ZN_7@Ce}#5dIM1{~~+^;m;EOGT}@2&A$FF>;EN*UqRwGyCi!& z$~=7u-_&CB(CHyafi_@Uva>B18{AR(&i3w|o;Qa)DNbu2uKPkAZ z^D4qu6aER|-x9u_@Lrdi{cc=3NI!!G-%)@)h$?^ zsjkdZN%#Q4Wu7X*SKf=mAs2%jvt+^=bbUjS}g=g4{NO2X$89wB_W;Bp>*Rd6{Uegv-U zIF2_=VfjjM`S&Eh2=39rO1oZZ-|rRR*8Sav@Ert~=h@u^mvtLS_`!n9JV%2Y)@7dY zB3|-|f_sdOuuK(sx@xF&I^h=*emUW@!L4<>QE<6m4T4Mm5y55r{esK!@UY-AemS`D z8mZjhRU%&U_Xz)l@E-^-zp6T2U84>%{}zM~Bz!l*_appB!H-WnFn^sSxb!oH@R@{% z1ef`50ykQd`R^qBLBgLP{AI%5CHxb?rOzJ-FTdLCFT=l#--7Ugf*afDWhmi$6MiV+ z#|tiBznv(!Zqv~72)~T*>j}S=@MVHaKg$J|eqJOzLHIhtzaf0R;L=a;P_`eXp8})?u-kb0N zgzrN5UW6YaxSY?ABm8v1rO#=EUrPA(g3ESq6;U5$Jz2MSk`J8Nj zN#0j*F&rF!Bzy?rBMJ8sK9=y)2tSALS%hCj_&maIC;UFbA0hlX!kY+RL-=QerwA{< zHrv0lA1lDSNvIxgC*tL}9ZLBAgda`#X@pNBJSg}Q5{A@TUcr_ZM#x zo+SKd!h6ll)?em#5k7+OLkXWi_%y*~9j+vN0pSl5zDjU$JMMU&@XrYUiSX{%W$V^Y zce>JkgzrH39)urA_%Vc^BDkC<&nJAY;Iclq5PlEg|04WZ!e1wRE#Y4h{xjj-ug~_2 z+^>Fu%Qm&OF30Ep2;cnwW&2U?*EWRjMEIVBk0Sgi!Y2@Z7U44qzl!jAg3Ix6r{J<~ z4-@_j;cpQB5#iqw-sNxh{jw$DI}-k9!o7r_K=>5GFChF{!WR*KKjBXh{;J@zzg83e zIpIGM-n~9sf0@5O;e!buLHNOhk0tyx!lx2`0pTISZy>yZ@COKAPWTIizeV_mgnvc& zuY~uQXWx(g2p>fFaKiT^{BXj3gij>Aj_^wezn1Wu3133^Lxev`_{)U9L-@ype@pm! z!h6ln_HWMlOK^D|GFWhV-LePaqX-`(xXgbd;eNuW3og$`mk~af@S6pfb-0W02MJ$6 z_zQ%;N%&gAzaaca!pm>4@81f-2N1p!;Uff>^ZY@C*ARXx;Zq5}knq`p%W<`U@TG)5 z25yXTdB6EQ;cpPWmhk@){wv|#ZZ!MR>Zg+M0fg^F_@0E1B7BVCCp=Dgg775aDZ-s$(O;rIeF^^~;ky#< zCfrN-Si;XB{9MUJ{Vyi`TEZ6)eh=YM!k?F1)bn-1*AV_0;Xe>wcC&py_9lEA!Uq$+ zJK>`UKZ@`Pgr7tBMTF0lTucUw2wz6{3du!(tt9;4gnvZ%*M$E@c+Um){n(%I9SE-` ze1F1^Ap8WvClP)g;g=FVm+*yz-%I%8gufdKo=6EpnSLW#pZq!GP2N&VP2;ZOZG2lkK{nU?MP7qukzh@Bs7s2KAbVI;ll|ZMffq`M!O#4 z7nYL=Ka21H;a3uV1L1cO{tv>RBmCcluM=E;?)N*vJ-P@=%SL7UMe+*5wK) zDBVWh@^1x~^U_a*|4w*UkJ*pu z<689ZW`tJ~-kfF&fwO0X&B)nL>^h6g9tx{@RJCi1a8bT-;eMygr7|KRKhPN{5ryKCH#KEA1C}3!dDaiCE>pj z-s@mef5L|nz8~RZ2%kXs*@Ry}_-w*&A^bkVR}lUR;cE#0itzP>SB|!?zl-o{!aao7 z5Pllr=MsJi;nxwqi17Oee~j>zguhLAlJFl0?|O)R{kJ515aGKM?jigb!cQgqFN9xA z_*}vl5`Hh?j}rcZ;PU?Kb;92#{A0quCj1w|yLq$yDE(9tK7jBl!gnWpKf(_s{5Zl- zBm5k~XA*uT;eR815#jd`{tv>RBK&2--zIz=;a?N}E8*P_wI2^#5*rfo`+@oUGDGJ zgzrSSoA5&jA4m8^!p|lA0>ZB%{BMNcLik;TKS1~+gg;IA%Y^@%@U?{hm+)T+?|Ha= zzi&nOAi{Sed?euq6Mi(|ClWr1@M(l!Nch!+-$3{sgg;356NIlM{7u3?ApA4JzbAY> z;hP;{KOXuK?jn2$;cmhYB>YIik0<;z!p|anI^mZPehuOCBp0t^FCzSI!XG63F~XlE zd==qu6aJy#^8Kv;684b*e@poP2=8%Jw%=tvw-o#j zV!yU0d?&(pCwyPQWxIzEehlF!53*`o{}JB(X#4TA1>pk-A58dg!uKZpV8V|g`~<=$6Fyz=?Eif1aLg54 z);Uc09faRc_#=crL-;Df-ywV*;a?H{v*co4?RtzD4}$k2yo&I>2tSPQ6A3??@QVn) zj_}(E|2yGN5#B`jhlGDiczKO|o&P}iV8Y#mA4>Rm!p|c7Lc-?~ekpDhR>_-3!Vi*MT+bXs_{oHyMR3j~*+BV9sxBjJw|{u1Hu5S}Fbd&0Yn&-SDAUrG3OgzrrF2*N#tk0E@#3EzY8QG|~n z{6xb2l8g6)&LjL{!mlBGKH;|!zLfBP5dI|LFB1M1;cE&1obVqAcbp{pUDR`P!uu2c zC&Gskz7OGt5$+>=65)R(JV^Ll!o!5$LHL7&KSB7*guhMrI>Nsu{8z%ePq6RzEeRh; z_z=SPBK%;&k0E>l;Zq2oLHHGf*Asp_;rA21obZ)|CkX$D@UIB}g>dJ|_Wj$J@IOi} z&ZoN)?k4;o!jC5WB*OiKpHKJ|gx3>(JK^^ezMSxtgeM6958>Yu?l?vCqv)4Dgl|Xq zu7tY@KZx+737rl%CH$Y@#^1fl*V$r(ze4ysg#U-|?+EX5y4f#Q zKU)y)B78XE2M~S~;U^P*4&fIPK8NrHgx^DWlgl{&{zQ495d{@HvA^Zr!Pa=E@ z;WG&j5q=ZlO9=l5;m;8MCgC3w{v+W%C)wA30O3OkKY;KW!p|W5e8R6L{AR-MC44#I zuMqwL;a?N(m~3CqK7?;i_)x<4CHx4&Pb7Q_;TI4-m+;#Ne~|E}34ep|{}7%cyocYu z{@W5hgz)_dKZfvH!v9M6RfOL}c!cnW34fCCmk3V~{xRX-6JCC%ef|3q{wKn#2_HrH z(S)Bu_*B9#BK%syZy|gs;g1j=C;Z=p|A+AJ2=6k*zF)Q=+(r0s!Ve()2*Q1YpF#L} zga--#8{xMT{s7^ROD^u0Un2Z{!oMW^H^O_JCHhOWyDi~E2;YzJBM6^B_*B9#Cj2_W z7ZSdV@W%;%iSYLc|B~?k5#Hx)`}+Tp@L`1SNBB{MpGx>N!h?kWKf)Ii-bnbpj-fOCT zzYHXN55h+i?jzh!_yvStNBFITKS=m9gug|2lJFGaJ^x}~&+P~wO86+k#}Yo7@L7ak zOZcsX-%t2cgug-f$Atexc#m`K>p6h%VT2z@_&CD-gwG;;HsOm1|2yGN6aE(A>j?jW z@b1&>>$wf#LkQoG@S_MnnecN74-!6~@Fj#lMEKK$zfSmvgnvW$?}YcMv#GS@fN;(f8^)$&F}FxZXx9V8@|~LdtN5E__(*DFX0t4vw7tAZ@UQJN^tr4>YWK6 zL-;ttPZnJId2W`y&*g$kKed5uysX0%!Y?Fwt|0sIu_-2aB&#_+wZvEccxrEapM<|i_y>f4C%E+4V^Ov~l5a1#^f`?1g9!H#em3Ek z3NHQ3Cp;p!^!X^^s|f#y@E-~9b*p{d{zSN2aOv}K!cQZ72H|rFze8|Y&qoMfCAjq2 zOn8d$UbkiIBl8R*e5Byg=h1{u5?tn=N%-}I-$nRiguf=Z^!YL2zX~pW_PO1@K06Y= z58*Y0`vsRiFD87x;L_(3!XG32Rl?U1{*&O+XYa+?`ba)VaOrad;fE7`D&f-!pDnob za~t6g3od;=OZeM_e?jj=L?a9PhN;V%m=eSSdr z_k?eDN47pPkBe}(;L_(9!p{&~=AS|MT*B`l{1L)e2`+uEBm8H+Sg|g;Uft@ zn(#@2OP?1KUN5-xc^Bc25&jzC9~1to;L_(7cV+7%`A&jMpCbq#P53y%ClP+W;L^`k zgx?5m+z(2hw+Sw<|L-CEVZxsfe02KG#r(BW@Nt5_LHHWNKPCJ-!ha`xvn6IfTJMYd z6F!LWp@i>6_(6mpMfiBaClOvp_{D^W2%kszt%To0_``%hN%%{ICkX$D@UIE~jqsj# z+mDBSgbyNoIN|#dei-4$6MhEa(+Iza@Y#glMEISAKS20ngg;OC>x8c+{8PfeC)^RS z@88V{--_@Z2p>-PK7@M-A4~YDg3Eb(3gOcUzm)Jfgx^5;?S$V;_&*7MLU4Irh!g%Q z;cpZEA>p4B{ypLUBiwn9{kW+X-Jv_``&+ApCj4UnBfo!q*Z072!V<-t}HGt_a_T@Er*sF1h%7PjRW< z`Ou~Ue@dAB;wbA zM|nw)msqAAWUC~p3!K4PG8T59+5 zIeb*89?{>=ZD^tA7uS)+`Dl^#Q0*I2NGaxzU$l?1?0LTwKDxex^M3Erj?+HchjF}= zuwS-@4`ZC_*V)@MPSq|HwZwHj`jmJ+&G=CLQeL{_<%3MW^eJh-Bs29;*FoxlE!F(- zgQ!O_^J!6o)Y$l4%pZM9dtF!|eU$LLzrFThT(1}5BY10Z=eeQUhf^K5Y9cevE$unK z6noxxYad2E)cnz2AH|N-VcLgL57eSP^(fXyy}k}o2dy!G7)vR9m@Vi2UcbH~7i@yH zQMzBcb}3iGBR!v%sR!#fLEEy4(b9)HZjE_8sD7A_YV@7VVLWcCb>$$>-h~~E0wjbwuI4`3o<^smDI--!bfI&C`W~kk7Da_ZN`VQ%;*%OC`Jzz z+AlSy71uBGr4Kz$+u&@v(VP^(c0n zHfDUN8(dWf^)Ee6H+8=}CVZ5#U!K-JdKlF(>k(91=yBV0Tki4Fcwp|jkvregBz>S? zf@*_eeFQy)`B*J|VE*U`ABEQAbLj)Ge{_V8LhJFP^r5ahj6qTgA6?(W{k}0y#r4>x z9v^)(KDu}4@!~qGuyHz2`sl#p#dCIHK6a5lI`DWYv>tz!K05GtDYPCQ>7xUWmqL9U zDSdR{@lvRd6EZ&3?SdGmHEJFyX7Lm_uluDB(J!0YM86Vv{bbQ42E36(Xq>mDgkFWLTl)CpY zhNwACgN0ugTIZU|qg+;$cl{^nL(gNuP4DAn-7iKx^!c^~KHic(^!c^~KGp~ynfX-t zD0V(AbbNdwedzh41U|lzKJ;;00w2FhANu;E1U|aGU+qXQ>UdG*$jl!eleYVJq2r^E z^daVtP3>c=j1S{oF|!^nrN0>~HcO4VMrq5^e0X8!mx1t6(L>o#$BVH9)laeabCm_Y z&cCblp~k7P1l3QmK0F0}UdWyCp-gmOJ!<`h^~)&fqXX+vGr2GyhYKHFMLmk0*Bu33 z7ao`Kp+4u>fj*iG^l@6oN7u~fEsb$nY&~2B`Zz1&L%oljz9KV^mtuW53cR12k@2DG z(SiL^Xgz`%AF3W5=%dhjT(5nYFH5PBq2_kgKZVX?#lF6Nv-Dw{aNFs_+_L?@@5uN- zr?k^YvGurD`oQs0ihg-W`Y2()JSu(Q{L-F!w0~rnTRxTXfs5pJj*nvBpL;?2Q1^4j z(w=%0`+nuBj1ToqZ$*sLV(am5>7#`8ct7J~^P=ie?EJAV=-A3|ptn{IdNu!y3K4xtsA16s4s_$F#F?%EVn5cb}m6xke z)TMo>k~m_SbI zv{rOmuY9B*Ix3a0x-@U3d1OWuKEy}pjr#}Y!*ts5zmL)NsK`Aan2+bOx8-@w#75?h zo4mp5$?+Ktt46t^$3`(LQ=+=a0TBY0>qt z8f(XTDd$yf_mmGXuNfylEJ3BLJ|oR=9lD`nyvFt~Y(AMNePBw-=fks+d`yu(a46^V zao9%kac;&(+veAr)_kC+8Vh{?aKjER?e&1w&rDq>^|Fm#TA2n`!zjHD^%-09>=wZk6cv27R{uR1DFVio|0g6$pk{G7~ z?MddGMSRpPHTSb}vMU{4={L0+uN$@Y`q9EXK6D>RA4^;Dp<`o~@5^Y-$K9EJQGW-i z8aCSYs2`&qcFV@9$HSR=WbZR-l>z2sHmOH#quDQY6J2Th)dlv;icCGq+f0 z9@$YDLp`!2y^DE&>2caH$(4?CLGx}To_r&Z54&+HHqCjceN>Mz`zHp?3CgqbL8f1_ zf3F);@oapsRN6;t^{6EC*q51lWUuSn<3o?r<}+Mrt2I#O+sad!dZ=-#o;3O*rhW>o zM_YZk$oaYJTJr>7p-wP*c}_hj`0r$w;kYgEb2GZl&EX?`p2IvAD}j&xq8|GAXiq&{ z|1|62dB{|~pKaCcWj)k{p~pv|{Zg!tokczLyfsPfgfUL_y4qMHeCXK50?)SxWco$TW2yi~+aC2})WdGsSoIhq>Y-;c-7j`Nf}|dL z{%~Oaaus;K^=0az=CL-^V`KT4l=0EU_;dfv@xrc8?P_GOy3@_$wYBqHi)GyCeAA^5 zH8}M6U_R(|A$;h(g}yQndg5WvZ-)@?o6sV(Mp8`U#84;{$^D0{&D5h$X8v%Mz=z{QTqpNb^}z8`t9-EghXrk{ zM{R-i*c?6_J^N%nZ|y08k8Nc=aJ+OxJ?bs>P`j3Q87%98SKiVp}r^6 z7#>DRg6e0f`YB{-zQa5|8vdMnoU}E^zQTv$0bN7uq4O?4;B2V&p`ULm(9c1_N0~a` zYA=P>qwbdW)j|6{RQpi>FNVRi4>msZc2>3iorLxI*4B@eKGXmym5&M9hx&hH49}R# z==fMl=CNYyQLg|01?5Bi{{q zFSX%AcZxF9nvcg(m;0f``k180N3ZPh5>!44J#KX^n;P18{7!|BiXPb)1PvdBem>Fq zeT>10h54BweV|`D!bhRsM{ue1fuY(FK7wZyR*%^kAI4E@EKOjPHR34uN}F(3?J$mG5!A!=#nrWF&$tr5u9Y6_nS{L zHCAhX9Z*TQC0%;;|H6FI)UPV-C3eH$wAO#PyPeex)A2Pn@i={DVvPqnUPGDRuGIPB zaXQec(povh^xr(+^xxQue;r?E6L05V$LmIAemnp9;-&ve-G0LKt@E!W{u}3E|MJ93 z{|$Z3x-{mAxAUJbKK(+CHfqu4mv>2%mM`S^k&bdVsI!Sr{|68VtNCQt+zo8vyQqV@ z<}*tiwyJ+=LqFweLayw-TW(&g#l9PA%dIf>iy7DOvweKSI+J^#%^#WTL8!hjTxLy- z^#+6OenxM{N0u3>>u%l7Zp`Z#Lv=rCrrXOm_KjHGFM7MBZQMWIkJjy4+rLb|V?*6u zYx}3iiM74f_D_!sy&|2u8;Ut<%W&wuk98|KgFzs@E;pMMA9o7?EW{&mxjjL+xa zWfPyze`C{z`SbbrApX-f`fqy8%rE1m|C%1T^H!}*yy3r=&2O)6m_MKYAmUrv=)ck? zUiz=@lIy==mFY+3H~cr2<;LmbvDzj+pa0k^X8xbr=)cA$Ui#NQv^KxIVg7vMr_Lrm zpMMA99lx~gy1M=)(~peL=ig-$pU;2eiyP+8=ih_)9&PmBw9?Ekf;RCwmz`GH#OL$h5Z};GKL6D=@%jA6o;UMXwz2*-Hu2JbY_r_s zviZ3U^BewSy>jDpt?F#z^Z9q!#OqvkTL0{Ze)9Qu*~I7b-}uai`SbbrAb#sM*1zd# zGrx?N{ymm)U279>_^-9Z=~}hKHq4*Tf6yjg=d#mEoA`YG8=l(GPd@+EHu3rVH$7?Q z&+#e^=_g3NDlPY&Mz~#j?Eb0YgALoa+n(M3RJU>a=EuzAEeLf%V~^S&hkCcQ*6KKD z!z1Q)9;l9E+R|voH$ZEk4z1WQ_=3Hk#s^Jazs%HHtb4F-ZCA&!=W7?5c`BiLd)@Va z7+{RD>_<%gXj$JG>-tll`(wSAPE@1bmr_e_y*}3bT;w)buRLw<^rHb-?*ZKwx;38P z&hq>ZzptNWppiJKcEj_J&NKZwu^wDvJ}_N49Jj!q{+RUEh|}lhPZ#f>CT13guvK5B)uOaQ|7uE<#6cseqV3;uS=NEugALf(Sr4{517yY zjP*f(M8loZS%J<9bXK6V0-Y7;tUzZ4IxEmwfzAqaR-m&2ofYV;KxYLyE6`bi&I)u^ zptAy<73i!$X9YSd&{=`b3UpSWvjUwJ=&V3z1v)FxS%J<9bXK6V0-Y7;tUzZ4IxEmw zfzAqaR-m&2ofYV;KxYLyE6`bi&I)u^ptAy<73i!$X9YSd&{=`b3UpSWvjUwJ=&V3z z1v)FxS%J<9bXK6V0-Y7;tUzZ4IxEmwfzAqaR-m&2ofYV;KxYLyE6`bi&I)u^ptAzD z6_|aP`8~7NC*AnITrboI^+N;D5Ht*pK%>w&GyzRQQ&8uz<~QQ@hgLz|P%qR6^+N;D z5Ht*pK%>w&GyzRQQ&8tvv=6O zp+2Y|8i0nNVQ2&zg~p)?XcC%&I>(`XXcg2A^+J76KQsUhLBr4pGzyJF6VN0y1$7>e z_Muf!H`ELDLH*DGGz1MpBhV-`4oyIl&=l0^L;KJws2l2q`k;Pj02+dZp%G{l8iyvJ zNoWe{JOS-PtDtVE7wUuhp#f+J8iq!oQD_{RfF_|SsB=8phgLz|P%qR6^+N;D5Ht*p zK%>w&GyzRQQ&8uLXdhYybwj;SAJh*GKts?lGy;u6V^8CerNz1f`*|HXcQWUCZI`Z3hF!=?L(`e zZm1XPgZiNXXb2jHMxaq>9GZY8p(&{I6toYmg1Vtzs1NFg2B0Bm7#e{_p>b#enuMmH z&QsAovW2oPA!ryHfkvTmXabsqrl8J=XdhYybwj;S zAJh*GKts?lGy;u6p+2Y|8i0nNVQ2&zg~p)?XcC%&Iwzxj zXcg2A^+J76KQsUhLBr4pGzyJF6VN0y1$Fw-KC}wzhI*kss2>`DhM-|+1R90Lp$TXb znu0pdMElSxs2l2q`k;Pj02+dZp%G{l8iyvJNoWe{oPzeDRZut73-v+$&;T?94MQW) zC^QaDK$FlE)Oi-#hgLz|P%qR6^+N;D5Ht*pK%>w&GyzRQQ&8vGXdhYybwj;SAJh*G zKts?lGy;u6V^8CerNz1f`*|HXcQWUCZI`Z3hMj|+J{y_-B2&o2lYb(&=52XjXXozu`hv`DhM-|+1R90Lp$TXbnu0pdL;KJws2l2q`k;Pj02+dZ zp%G{l8iyvJNoWe{{43grRzclRFVqM1Lj%wdGz^VEqtG}s0Zl?vQ0H{C53Pc_pW2oPA!ryHfkvTmXabsqrl8LA(LS^a>V|ruKBylWfQF!9XapLC#-Ryl5}JZKXP|v( z71Ry&LVZv_Gyn}j!_Wvc3XMY(&?Gbkbq3HrvGzE3eLi^Avs2l2q`k;Pj02+dZp%G{l z8iyvJNoWe{ya4S(tDtVE7wUuhp#f+J8iq!oQD_{RfF_|SsPjU!53Pc_pW2oP zA!ryHfkvTmXabsqrl8J?&_1*Z>V|ruKBylWfQF!9XapLC#-Ryl5}JZKFGl;&DySRk zh5Dd=XaE|5hM^H?6dH#nph;*7>bwN)L#v=}s2A#k`k?`62pWb)piyWXnt&#uDX8;O zv=6Op+2Y|8i0nNVQ2&zg~p)? zXcC%&I)i8*S_O4Oy-*+24-G&=&@eOtjY8wl1T+avL7kVQeP|Wb4fR5OP(L&P4MD@u z2s8?fLle*>GzE2Df%c(QP&d>I^+Em605k**LnF{AG!9Kblh72@c_rG1RzclRFVqM1 zLj%wdGz^VEqtG}s0Zl?vQ0G->A6f-p+2Y|8i0nNVQ2&zg~p)?XcC%& zIV^8CerNz1f`*|HXcQWUCZI`Z3hJDV_Muf!H`ELDLH*DGGz1MpBhV-` z4oyIl&=k};2kk?vpl+xa>Vx{B0cZ#shDM-KXdIe=CZQ>)^IEhIt%ACtUZ@Z1hX$Y_ zXc!uSMxk+N0-A)Tpw78yA6f-`DhM-|+1R90Lp$TXbnu0q2hW4RVP&d>I^+Em605k**LnF{AG!9Kb zlh72@S&#OiRZut73-v+$&;T?94MQW)C^QaDK$FlE)Hx6BL#v=}s2A#k`k?`62pWb) zpiyWXnt&#uDX4Qk+J{y_-B2&o2lYb(&=52XjXXoj0O=Xcg2A^+J76KQsUhLBr4pGzyJF6VN0y1$Ewp_Muf! zH`ELDLH*DGGz1MpBhV-`4oyIl&=k}eM*Gkzs2l2q`k;Pj02+dZp%G{l8iyvJNoWe{ zycz97tDtVE7wUuhp#f+J8iq!oQD_{RfF_|SsB;0@hgLz|P%qR6^+N;D5Ht*pK%>w& zGyzRQQ&8tEXdhYybwj;SAJh*GKts?lGy;u6p+2Y|8i0nN zVQ2&zg~p)?XcC%&Iv1gRXcg2A^+J76KQsUhLBr4pGzyJF6VN0y1$Ewv_Muf!H`ELD zLH*DGGz1MpBhV-`4oyIl&=k~p8`_6fLETU<)CcuL1JDpO42?je&^R;!O+r&p=j~`8 zS_O4Oy-*+24-G&=&@eOtjY8wl1T+avL7j`yKC}wzhI*kss2>`DhM-|+1R90Lp$TXb znu0nT&_1*Z>V|ruKBylWfQF!9XapLC#-Ryl5}JZK??C&|DySRkh5Dd=XaE|5hM^H? z6dH#nph;*7YW>_}X8lF`&qeCzKU-d%epu1d;VA2(sl)MG_TeUZ)FOS9n@t*aHnk7C zon61a!VYf|cJd0{KV_NrN>P{7r4Lvsg%7{>QPC|^k22+FTxP9w4@xplUfMdn!$y}J zdho&fxOP7D=;L>B?Y_sbJ%$-0XFv609@ah5agG|yQ|6k(G1eSkcvjPln%nQ7*3F3i z*j`t^2e#MinGNw^pwe!Rvh>F3VbcpwY@>99`Y8+QkiN#+nd;YW>gOQ!lMN}`!cn=^ z@mrPm8PI2NpDm5HHY5`j?WSA~GoCFg`?XJRLyR`nk9sgLU4{W_y^DHW(Zy(`Ed9&T zvx_cE`KYonV`l6>yh|6giNUdZ{g*21i$Mi3iPFJsdCzY+tY(iJC zdZIXkPCe_4iNmM=bs`>3 zoIb;$I^A{Pajrdv?YXz=cH0c+=FhOfHV(9^#MpFu?6(p}cU`Xj-F1O_f?rQSEm8Sne;I)4+`6akS*#o?J2b1rK|E}U?jtYnCJoEhHNoRLBJ_T>6%jG|U z*IKw!)yc@yY~kA|Zq&23yE-=1vRKvKSd8P(;I5t~?*)Dkc->|u?*{Gz|H0z_eDL6w zX8cRapRrs8-qauY)w;3VsMPQon`Z9UEE7Baj`&|J`CkNIZ{hy|?|OG`KbChj^EV*B z3;DMO4_f#z#f>`jwB$J)ypM%X0^icYgWy|R_-)|ZTljMDLEujKd{=S1d1fu*8y++3 zIUW7-1-NU4$=8GbuK5!t?~nXjsR3{7uLsbiUo`oXCU$%a zUjLHGU&HwP9lUmx$s3WUkLn0xdF@>=rm3m*H;%zr%c9|7+A z-Q;)T!ssOMCTE{?=84ADxmqSGZnRsEcFkuUju~mbt)sW4o|l35vhZt>|456^h3WWy zj^-KWb&(4f7WaX>hVGm8(L6RSjwpETbTfXoi5*XX2dC!p=fFL6x%^e|*k5w_J39Zl zx%?w=*I#q_=Q_`nT>dS1^EtWvXPw6qZ`?7ZuMf}6jo%#Hbw)1l3toFxF5gziTlQ-Z zxX0pW2zbNUxq0>gk6HG2AMj>NJxA+2({l413GSGb%g5??3qKLO)^EmFV%*k(dnV`d zGj)DTyVEp3KeyfU!Gq`J@{7T1C+6}iz+;yB&(?WP&yAl4?y1e?w}LlY_+8)*OZ}HA zZXDOaA?6YHU(B~L#Mk0@sl)hxN$1D@>g$cSz#G6lh+l{N4PG<d4e24hpW#(}g zM0~lr{x#--2FwHIs}K&y_Gzx}&Mfh}AwKwU?)haO@J0(iROh$wap3h9emZz1{OI%7 zG;oK7Ujp82$uk$c$--{|Z+Ixz|9#*dOZ+3?)fWCNxXZ#{1Fy62)!?-j{uy|Ug{Qz9 zEk4WDiNolx77O13yxGDBg1anr8=~`A+T9C0X5j~eH(Bx@4PI%9KLy-j;Zt>fOP&i9 zH;#)M%lXmxSb*L2r15s05%0Lc9Or6yrWeM zquqK79|2x@Q*QiN9dF@vIzF5mKUc?tPlnHB;EtQkc+Fo1ue0zkz?&_6b9GynuJeN2 zJVU@^7Jejn^)0#aQ@|T6d^ULH!rb_!;6V#t3EpDipMuve%FWY5b-Cfc$-;LA_uQHr ze+YP^g--%^-Ig1FC3wAs-wE!xJvZLC9WeaVS$H#evxTn*uUVX%XMkEY^298BU-0UN z-1rIL4HkY8c;y|r@r%HN7QO&fqZ%KODUJ@44}3gEv_CZ1BnlbK{qS2Q7Rhc#DO9 z3SQfoo2SQ?sK15p4DNX-H~uj2Mhl+|?s_;k{%Y`g3%?uO@sHg2=fUeN{A2KD3-9&^ z)c>Ekc?N^WEc_tw>VM_NpAOz&;X&}qXm0#s@Suf13EpDiYrtzC$<6Z{c$0;1tu9s5 z^UtHX@gu<-Eqpw<>#^MUS>W{+z5v{@JU9MP@Hz{B2fW$Be*mvBCSJAZ^LT&t0#|ze zvGBdXs~^ve9}nJO;TM2cK9L*05Ikt%E5KVU{9W+cCv)@s0^Vfd1JwO}y8ch)#_tE- zXyK=TyJETVmx9+@_+oI!)4B0afY(|02jI;Xz8<{hncO@sb>ow+zl9$NUj1xt{2Aa4 z7Jemo<#W05cYp^i{2A~T3ttCb`+RPmuG^SCn=E`VxF?<)?*(tP@H4?(FXYBw4PO8M z3A^vOC+h6|{{wbVi3LS03}V3^#RiBD78Gl+D=LGE3+ig@T?aeXSV6H2DvDx>1zh`p zxL}VJyJKPP8awviBuR4Dn&z*mZ zSDv;#i*{%Iov)6UpRxHZ@CIJI+-+~W{0|?7=j3~mui~X=rC)tCUd83#2Ek?j!zbVg z`Qk9!+r4<|d>WoPe+|!_e~Jgs$)0Hc?|A5Z(c%O0_WjZM@_6igEj+=0;r*HZc=a>6 z-F-N&9q&{MbDYqvrzHEtl0iR&lUJE*Hgz2cK$qG?;<@5 z@V?3i_$ZhE6_1vb{BP{f;w$ie-Bo?tlHe;8-dfKiTu($kaQTY%Txok^%{xC*^HEVr+O;_0uw zT|hqGS@QqDufiMnvh+NHH!G5Vg3le^!Rvd7|A+k7c)GXvH1dDo)&0faE+^Nx^5O@RV)M_}-d8Sq+UrSP-{0%=KYZTv zHTnN~zIMJfJ&D_&z5j%ReUr21-$O^@8G%fFYwZpvj5sM7q8%Yy9@S|^OfM+1hRF*8+h^Z zyS=T6H(h=+Ji1D5SMxjLF}?>qhv1p>)A8_X>CyaEc!D29&z*SF`IC4&R(dr57T$3F zEgp@N{9*Jjva+mC-FZ(uyvFAH;x*?(@ZegTAB9()kH&N7*Wi`$w&z|vbN)15zRu=9 zz*FbH;HB$reu-XOC(hTvo6ZN~@eQ_TAH3oGC_K7R^1HGA7vd?tKYl$Pq>@*^3y+;Y zfoINN#KQ^FvlIOv;8o|}YY(osJO3)OKH*K$qt|U$JaN7{p5xln7mseXJv-p3^8@hU z7P}5d<1w!Lb1t4aACHHvH>`?H_vcPL!L{cxJa_&g9^GntKEP94d%nkm+wAc!xT>sY zjO+e%$1|5-7Y`@do~`f%*Ph+++~p6!quXuIiFk@@&jomJhs|G)$Ik0`hHK9ZJe+KM z-oz8rc!F!s*?8{! z8a%qk_SEsz`Aj^h+x&ZY?EFVO!*zcaT%G&xy|!n0JaJyebLU&*(S5dOZ#>2IaprKm z>GBC)x!?9&iP!NFJYG%4!v`d<{ZHUkd_VHf;SF5R`xkh5ioM-Vy=8rBxUO4Iyy<*n zyfRgKG`|C0cRn01J!tbM;8k4r^CGfBc;zA6(`5}=hdQq7 z+zSsLw)su)*!d24hHK9-y!43l==nMvuj2ap+1tGquj6{Zn2Lwf zB(MH5UUU969!$6SPHV|=RdL<_mGRtpKfLm!J+2{mh9AcH`X^qVVUKGJo;sh1m!7iu zX?TL`{q7Ascm53?{abo8ztGxpys7gZcrerEH^F1)yWknF_ur9tI7@oeN8<^u`*RJR zJHHo?p0@k*44yjw2oIjI`QP!_dDnGh9Wq?^a~(W<*6#l{c;fs3Ja>Kq9zAD!YIy4W zc073A?*DW=cK$Y=;ky4n;o%GRc9&R}`>*qL@Z9-eJbF=jhH^c}c#7|YpMW==U#dL~ z>Cyacc+L6ac<%gly!?_~pRcvY`9kZ-@iv`@c;#i=vngJ8J_IklV)KXMRp+DehVyIi z@~gJzUcBaf7T$E8=ZB z^Yic~uJ`u|c;yZ0(Z}s6c!q2L^LY5Cz1>go#QA*d%l_ofd*IPq(xdO6tc54eH^DP} z2d?vN@Y37TQ@osLZ$q^o*Y`HiDQa9#h^H<0xSbICtUeiuB)b$w3I{0EZXnEZG=#s}lm@j9;i^EqDmPJaGY=JMl zk*ss-d^tSFHz&Ua9)2Y~dVlYSSDo*OXZRp`hT)~JrAPfJJjV6;^mOgPFQ?}c&3_|3 z$KluEIesdBv*w$UABNwDXL#{;XnUKEH=XO}gjS`Rm+n ziWd)4?QIU8JO2R>zmxsZ{Cvd&QTy`<=S$)#KArv$uYNB*SKyoCr60s6;=AFQ^CPt9 zN1Gpmhd)`r1#dXd@Z@Kke;Y5)wf+;HJ6~cG9zTDv`E~K=SL=iErt=Ya`kT!sc;$EN zkr~}=P%>&pEmyuUi!=WqMOQbWzN^YtHILkGo|Ni8$9e_J;oc(Pr;LpHh(o< zp3nNdc<%f;yf(khe}YG?FZdOk?q|p139fbBHl44CrwiKrrg&u`>$~B>!q!{Ap3~~D zJHG&r7qR(?c&U^149}dugI7CC{*?C71;KZC)A_>vWPhrQ+WcyG6aR>wjq!Xj$^VEC zQeRwrjSliJ_P{T5uZu?ErIO@7=_vWL@OUZl-RQpz&+xtR$#}T5m~V5@L71WiufG-RlM5zf^M{B2yH6Y<*m;#Y9HQ?ZTIb@0ee3*Abl1;ncxgAuFT(AvkB7U9>-pLYFXO$*?}W#DNWRPq(_wh+T=C7w zpNKck6MvZFItRbQ?dMqSIbZVclb?WB$B2K5--F-ddZy#?1(N@U{PTF_LUH}O9J4il zk@$G_|8qRJM0~^frD*=*2d?(x)#c)g;H%)3tHn>?abPgs7$@HPyD+Wo0K9gs_y@wl zk$5s*{1-e?zfN2q2QE>+UOXp%H6Gs}z9YALJzj79LPW8({?1Ely9IAf5ZCpo;Dcw?gYmE>R3{B7b_bGz^0r8~s+`;edF*<|sD>G=sy?h@Y>?;Oc_ z$?q1QiTB3qb@Au$2v6=4uj4!8*q1^7%c>Ztk;$m%YZ>i4`e^NO39Irnu zzBJeKw|L`u@qg0u8y+>pL%icaS^xND@pbSncz-xEI?ABl$_h=15Y`cJ`2ABj&Pe>UEjBYr4;F&=#?-Uq)9 zPd*p#(Lwre$I~ywAIGQS@mJzENj#W=m%bLCz)L-oC->c1=V!@J#5->(=dt>`_}v{OzZ@R^Nk6_i9{(jiv$N#;;^od=+wgEl@vZUZV&X5- zvkRVe5x;=zWGG%)N_=%Ujg8jK0k9vqd zhEKzTp5ppA_9CA462FA}yLfq3@gwmsG{2hovG~t;*jrp*H!i%DoR@43@ees)-SGOl z;tkekH9XlsJg2`e9&c!UM?BhC{510W;n^nQpW}z)^}aTLDqij{el__^@Wy81-{PtI z=Hl;nl=FC>_745|5zn^~A5H)Jcy(Lr-{8Su@u}oHY|T1vFMcKc zOXKlQ;;)kLg-5%H|Aw!pK1BRT+5KR9ypC^0{~ma-yW~Ui|G-1MANiy4cu(7ZG2U=~ zJ09(2^H1S*T)$7=z=M4xukW{ih*xmk&mZtAUSmK1(4PII=W^C({%vHP)BVLynokaR zNjw}bJ_cVAuOBS_4L$2<&mrP^{cM3Jqr@lEvu)w6@AEf8c>!Buop-}C=fmiE$URRT zj+g%_{mavHHeSK?`W%bLM@s%J*5@`nI$B&`H{M%#Yd=5kDf>A;{g30h^XKTvj+LJ2 z^uLbRP7r^Fb$B0-PZ8Ij>+j@rJ@d8PB<%i%@$Th-WLydPd|c^gqsZF3;vca44SkTBe;m zpLIA1FRw0sKmF(6&EDd_;x+X(#h1nJz)Ne3ufutqfk*3#pUDSQFBIPXeXg}+WfJy( zHu=VBlK&q6T>W(MeRu=(2RwL1{Dqa|KloGg--_?rLwuRyjf>Xx|FU}==!2J+ShgM1 z-=i5^cxykim1RHm^S=G?#Q9NeFaE9$_iKGzIuj2#U+P!jCFi%`q4Ozt8DD|*c>=HC zdfuPLN8$^Ue+#eTi{hW*iSxO5&Gjr)e1P7%ZpS%a22WkjYIxoG#)bdg?^_Klymh{k zT+T~>uD2n0_JO#*A9xr(+nwJF@ajjBAHwyY;*I}^Pow`K{2h80WPN5A{&)L%X(;e{WHlt+UgI2|wJ`uVl~{!Rtg^D>9q^{&6ysiC~eX0;|I^P(tEGs>l-vO^XABLB@+59nh6+eje9E0c1Z^SFhNss2I;FfUo=fq} z`K@@lx6MC}r_Nu;OKaHt*LdQ5ft_U?n$B0i<27whAH3mwdpugp=3~6>`~*B)+vYF9 zYtAR)!8$ho2wrvmGM+pC46m$fdxBkL9Wv+3;N|sfejPk@zBOJd+x$>Gaefru#PxCb zJiM~L^r&Bt*PY*wmo~8ZS$Ng?dw9e74|utc?depJ^{F}Ufj4n|Jn4hS8`^akgg2b; zi$@#T{E>Lw`B`|lvCWUeYtHY)gH3Ec!>i6;!*l1K-p+BgvXh_l2>0F zPn>Us=g#-Rqkguhil@%c!-M`de*+#ne*n*%KZ}Q(*`5#a#Q86H?!3#cvJTMz>Cx+C zHN5V803L2`^Sk3U=ZE6K7B)W`uR0%x=g#lJE0OJa3eTLsgO>-|{P%e3yi@V>P3w85 zw5835c;b9xyy<*LJl@Lo9EdlZpMXbO+x$g%-T4GO+{Wgo;x*^b;K3l9e;=u#@Bu<8}D@copxA_ruejCEpd_7tfrZjOWg;#e-dJ&%=0# zFG>G1c;sBa{~6;w$-hOuhL6F&(f*3uuKN6Y%KFruFNa4%B)=~|PwC%TN_Q1sj{eoj zXU_ZMx$}8{e`z<{vkUpqx&9re$oWC!W9Rxk*u=U1eW%pQM0`@g??> z^Pc0w@m2B4ev&^J?~7;7cf!m2OI~}1;1R#dh-68{M#VdntVMG*@yiff|t$|Ka4kS;=c`^A!q!jH{)MccuKmu>(*6Z){(S9U z!1~47Kfm?Mwcq(z?Vr!)uhagH)+cCx2kWrnmcr z_B;PU`>l2>){W1KlUtqckQ2R*MDz3;q!%%e2k~g zM{1ArDxNz(5f9i;?H`SY&M#b&U@jp^Y!tO&bPp;&UeC3bUqYMoR7lKcYY#XbABE^&iVf} zmoWLXTl-4W?}v0P{$K05ZGI-M-veJAFMT0CtNma7zqy1tuJ?=LZGP#&dAgf7moWHJ zdV0|_l6)C|9zPxrzLLD3OIZ6xeBJrvKe)H}pZ4cpJa6jb#$)uvKS*A`&-MzQ{V2Yi z)XbYpnB#sfVK!ItSCB7mw0*pd&eHG~zF6U{{fXWFw7#G9cc07XT*A@_=~L)W8o2}}2PZ#UmqT$in_b^mMaPvZe`KbJ6^Dz4w} zy}Ee3YUNLM`>%5e%TGw&&m~Nz)6e}~=Mo0&|B13Y!N2G~!}ZU`&vyP3KE}DuCCvNE zx`p)XT*A!xazo{~E^$4Z;#WA|3m@nF1pIpE*v4!*IdHNh~?WybPVTF z=Mpv!5&sL)lOI+w7C`?-X7x&73+gyln}=O%h|E@6)QxrEW-lJ|27lYfez zz<%mn!r(~pOL3h`SgVR(hU;9ya_bK@7n`3;m>ehmIQyyd2czS~>s*IAe=t8qu9FgZ zonzQMN%A_!Q0Ee6r;7Wzgvn@eKbJ5(+q%vr%+3|p$32}(c%NJUxVSJ{_q$+>ug6zisQ38SmTf0u{9d22_JR+I+rk=D0x4Za2k1bf8Jce@KiaEr?L(@m+)EFuX73WNz$*s zkFfnhvd+;R;)^XJMLL(Ta=*BrOW0)n7w2|$F5!)CJ#{YO4bCs%b{lR#buMAp`UTHo z^K%I^eva#Qx7@JKB}})H{Q1K3<`QO`S=YIQNn~B;5(b-F*SUnLbDc{VZ)x+N7su6p zKcug9ol6+H{n5FE@uoJfa|yFetamP6xVB%XZD3vJ5~l8UbuM8%z~*%>Vbb6FTJ-1r ztm|CDz^${+B@74Jyv`-ex3Ing{n3WjbuMARiHT zYkRvomoRhtuX730J~pp&2?OUkmoQu3=5;P%Qns#h2}76HxrC8h|H;L=wI2`Pko)6T zyg!+-p6NEyU*-DOxr8CEuQzlqVa_^4z<^7g~{CvgZMEiS{JpRZ} z8UJrCVg8WK>s-S8Wc$3Ma|uiC`AFvy2F`UZVbk^KT*9i$>s-Rvxy~hwoavE@8>J&Lylk*SUm|bDc|=xOLFE zgcavHmoRj$a|z3?U*{55o$Fk}*tyOn44uC-RPGnAx#!3KYc64Jixt{ea&h-*Z~8qz z?>d(-C4V$|ol6+D222#2y3Qp`o$Fk}aG=fWT*B13&Ls@Dw0WINm^#0(_+YX1_z-R- z`4Q~rWW475Sv=U<=D)zJ&O0wE{kikC@X9u}X9qlUJ`yhvvibAz)cGX5w5`q0!V~A8 z;!Wo|moVPW_UK%~hI5@u7!9_0ol97Eu5$^)?QLG?64spST*6=no6n1vysh(Cb*^&> zbLYz!FTh&)%8s_DKb|=sikEk?`BU)J`FOmvv&}z(C(hr-o6dDEVZ4j&(Yb^T=Q@`# zY7J^BHocy8E@9od&Ls?o*!=ayTj8zssX5oVgu$*huX72j&i|k%cdl~@E4$eqolBTG z*SUn{-EDq!@q<`vKU3#Am$0;l&Ffsk#CdBhMXRUjyyFt$@t(G)7v6BLa|xrpY+mOQ z)}8BI!f=Q@`#*vIB|E@9QV&Lzy9_b$HJ(7yinwLLnQFmtYR3Clxm{(O2; z=acZ#el|Y~Pn>^>H=QrM6zjjg?dgp-oNtFm2iW`wyzcxQJRD~86Y-k!r|=-Q`8jyi z`2tJJ{^!nD#Vf;YPwUS_w9ZTBd^lb{(B^e6Vd}i~Yxu36(m^)=IG#9v7jHUm{km?e zCqCHrbSpkcXnDi=rg${M=J&?y&QHX{Lu`H=UUU90Jotyrzkyes>s-Rzxy~i5jI=#E zmoRg#a|z3%Y+mOQrp|RPVd+qt*SUm=bDc}rbgpv=x3G2>v zE@Ajjo7cI7HRn2)FgU{IbuMAmxy~iborlE_KCSDg(i#x<|9hTleeJO2nezkja@FR~ z!c*rH@X}E>KOIk;e}p%k>s-S4XxpQ62^-FJE@5$rdLPzZZnsLlCwZMq zm^vR#J~&bOi+l=moUcP!}lZqzI#7V=MsjuNY5zp zI+rkdKwRe%4%km__hZ*{0REYCol6)#Dm{KK;nyzzEBjf0T=KuMKZ_UtxBYqUh<0HA6SIPf_>qO@gR(=y-i1+t&E@AwKcyB(q)wzVV z)7HDrem#}$+^xw)%DuxrF)2;(jh+^(=AyeQKRcSi4BPC!aUzT*BlEaa{+UOPGxpKZ3l@B@FHq zAIov+T*5dN@67qtxrBeP{`&cp&LwQzCVBn7@f)14g>JLoLs%yK`|p|P^L2E|s_i}T z&)42pE_&Lp$H?pZdwu?g>+^L?-alV^-GvhaZk7bFOm<%U4SNF#2^aVd}i+%CbJCt88B95+=@d zE@9KT&LxbmwmqZiZ#dVvgwa@=*SUms=Q@`#9B1<%&|h<|a|wfMY<`Jea=caNI+rka zu5$@1*V-PPOPD!7ivIF=$?wMc>s-PbzCW&W37gJ!E@9<5>CwE-C9FGtk=re`2FVrM zPW0p=w>Rjg%HgN6HxrF5#ZI8|+tU1@YgiTy~bS_~f zwLLnQulU%r<5lSv$*b#J!YZ!ErE>`zF0XS5%M)#n&Lynj+M{y`n=aqFJlfBLx7waM zUdOd(241?&=5;P%)%n-t8@TrTg_kGU9-T{AbFOm;O3HUgr|VxbCOU zCCr>ppeKC5?!V3@Oq}an!rZyeC5)!n9-T{=;=0Z{m$2#bI+w6A)%NII!aA-!I+rkb zQ1W`dbS`0x>+2JpOW45mb&k#@jQ%A(>N=OOj%$z3B@7>uyt>XMtU1@Ygu%l$uX72j z&UG$fj_dyDT*AsDc0Ug;UZA#Lf8x47I+w8gsJ&gCOPJy#xXyJhVd*i+tLt3C#JSET zY&!3>mh4abxb*1$>s-QybDc{VJz?`Ym#~iOap_#bFq6Ex&Lyll*SUnjG@IACgjHPc zcRH7_;aukumZwXP=5;P%&AHAcY&zGugq0_4kIp5m<9h$qxrC+G6JN2Z>s-PruKS~N z2^-FJE@Am8d%Mr9E$6G|T;~!to$Fk}%D-)o&Lynlx}Q3iur$-|zs@DBI@h^`4d*(S zusqB5=v=~@bDc}r#C3giE@9s-RpGxm0ME@9QV&LwO(*SUn{XQgK- z_dA_SSi|-4P3IEk&UG$f`8ny)yv`*|oa;LFW=C&UG$f?p)^*MlaYN zolBTH*SUnji#D%w31jD@*W>Zcxy~gF8@5O15+=@dE@AFm=MqLQ*&dxsm^#(xa|(2@~f!moUfmde*sw(W}xkg6m4>64r6;*SUnH*X-@; zT*9jJ`HHuNThA{I=Q@|L{JQk$`zJb=uUgr{)-j@8e^y^&0I-y+i!qU5v-#P`JK%_YpGM?e3XH3(i#tO5=S_S&@|)3<;ydC8<4s)O|J)rfeJuUzr{HCL0zG5!-1%6% zHb;6iKM{}qWBq=-i62Yo4Xa;CUZ0nBzF`BuoPM2eSp8b^$Kg8Pu!*0F>wLrNH*wQD=lc10&H3x>PaT&}1A{>48#bNme8cj$vY(pQ`G!^J zI^VF4PiOyizG3p6^jv|jv8h}?;rHSb@jk3i!@17&OMb9+>oNJd^HKE2zuCObPb~dzUFR)k&UL3Jq$Wq*ZGEtbDeJ(EGqer=+XIxP5eh(=NmQ`lf0jAxQu&! zrSlELE|On%J}Ek@_}WA3yr)Zw??%7QHw=~%-y5GyKE?;&I^QsLK8eS-96z1B&Nqyg zmi~)zop0F0^>u>IH;lSU{vz@}vOf)6=NrzOOV~~F_mN+k=f`+C@q2NdhZuGjpNg+a zKF1%#b-rP`yyTz4cPC$6L0tFqQLfKmMR9%qWTNKrne^yf!e}MQ-+=2}!W@4I*ZG54 z56RELAECbMG(# zlV1~W;xFR^@W!f=pMej?)78XvF5yXdeRc7>cwKQWzP!8s_r&8hB(L+TM&O~#kHbrA zN#4&T%+?Xt`7%?re?9TZ?EegWRo6dT`^%F5nfzz!>x;j{aq0ZQ<_6*)lmC@`-begH z{I24T*;@a`hT>b$qkpfgw6VB;AFmhr(x&2@(4%t+E4Y6DLFW?2xZY27E@2%XLeISK z0rs`GtMdv={lz~guk#9n&BQ0+IoUzh5< z!qPzTKgjD`!f25AP+aE{rrV0|g6mwuX6pl~VjIeS>RiIgVCy=UFx*Mp&m~;bT|YXP zu)K@p{anJRBCh94=Mq+MKbJ5WBKb3UL8)^IOJ|AuxrCM02V}+eFvq2H3DJm*E{5+)ajAIJCK zbS`0VsrVp#-dw^f#C5*R(>$Ixt`dKM{&{l=$BKWzam|}cI8OW*eBNBb)(4Bl_B*a~ z29s;WH|Z$68TuepSGi2v7I!pY+QHJ9)%ah(qsMe?|p z+#^1j+ua#2-z%=qj|m>$XM4ut&HKf54$)0`Fjaghj`vRO`Iq=!-0oC7c}U#P8Eif* z{u}wX$TuGqe=?Bmb3A)Yd|9r~Z}EJZ_z3prH@q@Kd}X}jK-TBq;=S=Mczu?*pEKCk z-5+(HVE(M+x1>ks36@_F*Xvg230528|0J*T1oM~0XR{8cus_+W;`fo)d4kDo@nQJI zyanFzzC*-`m%Df{l{+ z6!uf+3D%YrpG5z=y8m6pk0h^i1MAC(>)+eaxq;p5>eU?1^w=$SVka3kyU<^ygb{$KL}Hx<8yew`0k?Jxc+{W>48GC=%i zT;~H;wh%wEeU%3D<^yK8av3a$E!O=nQe48~C#3M~iQ`fZXugdb{U}>w4;Zz3@D7UC)GkjL+eAFDkrs zT=Rcq*X)t1ci>U$i*UuZI_KrZEoFbIy~OqLxjaz3 zysG%4Odac2AcecCK>12R&?l$>Iab z_WPyI*THk=+u@ba_8f#~&QHh7J#GFPJazs6URv4aU&IsVpW{vE3&e6`Tls#`CfRi zj?Eu~SDjyq=gueNm33{;OgwY`Azoh3=KsJ`=iLsH<1LkKz7L)_-wAIz{|6qgZ+k}L z4d>(WXak#{g4dnDh=+Y_{xiJhe7=L_xPlFBenq_Md=or(J_N69WP1+7Gw0{v<&ACr zMm%-?5MFA1LA}`YapM&{!HcJr_VziRJMS<;jyKv=dNkh+Po1xa2YqdR5FR_=F!b^Zw+Y-aO+;<59thsgS5&ez7n0k&r=JaN7+o;yDZ zk2beG=i{mK8}VQZo1cou&Y#CK=O5!?Bt6C5p}qZz*PNIB!Tn;O&98=6oo|NcxL%)o z;*~9>N4<(?&M&~rTiN_Ac#7+F`w$*%EqV19@YwlBc;3GfgJ9seI=6}Mg&P$_Y9dcYhzgrcL zwwE6Det7D97d&YFLQS!0{$M=Db^lMt8_vh!(T>ui`TOv?^XKqzC!7BdUUMEC%Jsjq z&3DJE&Nsw!=iB3zU2IQ`XU>nu%N3iy2v2dnAKi+VhDcsL!xQIk;Z5g1;PI}uXYr!2 z{kggG-gvZ|&2Ndb&X+t~j<>v* z&99B8&IjS8y=^|m6Xz%6P3KqQ@jkZaZoGl(bv^@+_LaQ)TX@}h6Ay>l{DS|K-@^X>55`2l!kf7^2`o;kk&FCSp@DV{o?ikF7j{IhuCJja{Pf5hY1_H;Ue z>)&|~JQ{BE8{&26gYob{n?C@rIX?yu4zl_4@v8Id@!a`+c;#T*GZW98zk`=Y*!;J6 z>U_cCgMilKL+KEk?|~=IH^rOIhv4x)Y|o*1!+C;7BW?a_yzcx?JRD{7)A5?~H}K$4 zoBtZGI-kEP>ySI|j#mz|J!L#|J_s)#Zu7(N)cJ{c>7O=#8J;-518?Fdwx5NA;0e5X zg!HJts(I(1Y5qu?|5NkMmpV$;r&_i7-gwjb0L>p|^Sf#u*T=n4czm?v)f2qod@LRv zWAk;q?)+&yJl5tv#B0uf!-L~&e(Bm zx8kJ}ZT>MlasDdabp9nCpJaRHKSqwX;d})=I$84iIF3f|o|y{MC5k{7$^-d^#SVX?xzl8@Qg|Z}9S2l2>2wI9Z>X^A+)?^9}LJ z*|ui~yzcx!ymXGupMY1LUxYWDPr%FP+McO+4cGJdEFPR^^B>}|^Plky*Pca>m-Q*1 zFFoo#@znXocxjBy?|>)HW4!77I6S_<_FRBBoZpB?7ux&-c-{HaczBV`zlYbHe~$+j z+x)^OaQ||?BAz?n7_VGndv?Jy=Ogg)r8a*Go;tq_FV$@RHav0u7~XXL3Lal(dp^S( z&i}%r%Wb}E@r2%b-l;oZ3lFc5ygt4S#A|r*@ZR3`$4ggAUi~D^JHJZvSK0i%ns@%Z z=C8K-&ou9Rfs^F8N@H!lCthvN*buaDQ9@1*%# zZGM#Iou8xm+iZS<=AA#L`AIharskdhsQKG%eu>jKU(VOTOLy4(V9h%pq4~)+KU(w7 zuh;yYHvf?3oxi5}yKMej%{yQ0bk5h^Hopd5!;6m(+S@jGaF68GhvQY}r{THtYw$|l z_DsPu=P%*qdu{$JJaxXv864MrHoq#KI3I{No$rsw_uHP6@P_lN@aO@XzZb7Ne;yB~ z*!*XB&G`a}99J;a=6m8*=L7KE`965%LECd2o;kk^FaOKt@4{2(PvfPBY<>=&IPWl8 zj;rZ>MLd4k_VmRY&iBBhM{NEmyzcyBJbcvVZ^vu6zD}5i2aic!{Y|`z>;0mM=eWL( zUGPjf-pb?lc9+L9T=VPUIg2Cm2Z z3LZ_Dy!L;F*KzIdaF(n?_@wRcj@NMQUmp)<*#2$tDz5p1@Z9+sc;zYS(foKkbN(P+ z{)+*zU$;GPkZ<5axjui? z{hTd%^(D`f= zGv^QD<=p09z*FaQ@X`l1|0kX}Uv`YFL(_Q~k3Y0M+u;r8F&=$n^C#kU=a=E($2NZl zUUU8=9?X&a2(H_=@T&7~@!a{s7s&Be{$qQ3;+gY)c=;2X-wjWlAC8wkwfS@K#Q6<) z)A=6~hc}#mgh!v-{I7W3`4Sg${eNNez402Z&vygy;7iG??~7NRAA{%4FUBih z*`C|*%=t9D{I%o{=luFpr$Yksce)j!s}^WQZ8i_I^2iL67-`5JiXSDTMC?|d)K|7P<`=U0D>d)@4$UuY^O@$IzpnX3Z2n8lJ73^3u7^%GzY<<^zKP~L+x*U& zcRoV%i`x9Dns5xaJp^ygpyNh9}Oy#GB6NyIj^G?qYAZJDxjVU-PX$ zxLs^|zt~Rm&WCA!37bD&^Ug2U{E{|*i{_m_qWPt4{$WF2zn-SN1q z&G*r~^Bpz6jLnbGyz|pFzpTxV)x7h%=DXSaOwBugPxH&!{EwP)4*|c;>th zUT(dyQ*8P;up^#2AAy(Fv-#8U1V6Fe76ez}L0R(ZcWU1GlbT=O=HJr1^KUi3fz2;` zjjTiByayiivH6WP?|diCZzy?v|7sK-J3j}{oKL{Rjcm_jc;fs`Ja_&h9&K!Umb_Mu zH+8-q9&BRsJL0kPQF!M3Ts+*=_S}poxc=Pz7~XU~8;|=+kLJI{8_qkAm*a~1+5D<_ z-T4-H*x%;&#B0uv#)Hjl{!+Z^d@`OppM_Tj*q%9f=Dg!|a$M!jZN3MdI`5B{wy^oV z@x=MDc+>eMcpTZDNqEEgbUYep^Y7zz=fC0MmNwt*dO6;j^NsLeE1TaHuR1>x&z)b0 zSGKl2lkm*>biBNc&A)@E&VRy7gKWOb4RTzG^R@7%^X>3>TibIm-f%t|kG8Y<8}PdG zNAPg4&A)-yod19a+uQsSH_CBUov(xE&bP%YJJ_D#c;@^Ryu72$kHb^v58$PpZ2l!Y zasD;lbly3YiB*PWk&hZUQ@3a>f82M>nW{4Bic{6jo<{ySdT z)%GkiL5??bzCK>w&E|K&Q|BY_((X2Y2A(*-7H>MAg2#Ku{mxtT@d3fsl2E25j%}>D-{KP=Em+;^q$*X^@dFP!c%JBvV+k9_4alVb_ zN7(#Wr|5@`RZNBTRa$Je?K6o(7=7(tB`H`AG)aJ)% z-uW$>Kg{Nz(7f}vG=I3w|EPKArQ0}P|FrqF@x=Ldnm@wk57xZ%MDs`5{CLefe^B#P zn}0>~&cD(8Q8wRs66eeL>UeOp&2O!F=fgFBjLn~-dFSIaf2_?<(Y*5)HGiDVf2n!r zoo?rR9dGl!@x=L7nm@tjhil&X8Ja)Q=C9Sf^9MD5lFh%SdFS72{$!h9{0`2S^R@Bd z6r0~(^Ugr0gYoipHh&79I=>t*U2pTZ`) zINu*{IzIuAC)%FN@P_lrcyz1HPsi)H{(STfUcOE8>OX1TdFcT;-tr`yUkk50AEf!) zZGO1sou8`tJ8XWe=AGZC`N=l_oaUYXNAq{ud@x0htL}VRynL6ph^KWY2`M6G8pHn5jFwZY(`1k%TJrVih?UDBOJKl7@(1X&GJ}CVcl3yGz z|4aN5yldgDzpooF(Z4Mrz9RXI{CW5~c;zAKnT3z;F1H&#EWS8?+5hl}Cg zyf40+<{y!M^#k#$^P}*(^F;fdU#k6&O8)`e?hSYi*W0}tZ#aJpFFhtb$IxeO!7HeDQzDIydp-@fGmu6Oz9SUjr{?;;-Qw7T)^1+2NP#waB^yJgzb{Fp;eyHYuCyyUbfB6g9A8LZr$tV1s>P%{a zi|B7om;T4_Tk!gm;@{v?@N|ZF8J~eSoWFtxPf1?x9YOFR9y$LGPw?OAU*I9Rf93A} zwOHY;-{VOBE&avQS$pe-M>EBXkD1zAFFeOPvu+#X%~_JyxmSYxya`{1SL#DC#-N7554&kD>Z|KcR|mBbTzF3_ImrKcl)4f%XI$!GN6iKpGgWBdtv z!WX1xCjJ8XsFUQo;jfWzlE0hVeIHL7(le0!*P3_!2Ohm-^NT(#=c|UlOaC%>^%cp# zLjP)ox7NSv_H!Nbl~*OdKl#n@I{rMq6Fo6KdcWJ9eDa#~d_n#oy!yKMs_f^{csg5L zueVe2_zm%m$X}rOH^tY-uf_AX#4~R97R|qH{T}UqM|?4Q9>lBfir<3IEWEXD!BY18 zzDPcNU-C8b@8b1b{0@8$9)BqQ7ygy{$Kq9dF5Z|Uek!i>_$r@>>pZ@-mY3_H@|pDo zAHly6|C;q#7LUFZe~126@$hT$Q@MYwkEh>?e@Rb2y!M^=_4I6ymwynSKz>g=$3MdV zfj54Xyw3MIUi*I*FFt;1Z|CCCT=BE;%d`id#{OJWc5yUw4) zGw09Xx$~Fsz#Z=!coZP!oTqr|d=ehI{yH8x{}&$PdR{U-aXt%A zoj35z?av!{?mWkXW$p2Piiggdc;tL89y<>nm+L=qzA&CTFX4gP&u)0+Jj7%7__n%w z$sX5wc_qg=6K%4_6$;Yx4SbQx}H7NUC#k{?EDZsb$$e1bI&8k;gNeBK23Xg z-q-8u96WP=F&?^~r>;_W>vkO;Ilmc?o!@~c&hN$3J_CwJr69=K{|tf$wZDU`zdqkR zhG%a5H{$s#;rUDN*E7h+&i8sluFur@v*ZK!JovKSu6y3xa38L7=Wpoky2t(^~{|e8Y|9}Sz%lc^kH#~CQ@d@_R`677cyo9IDm&J4EE8zjp*SbFg_LkcXov%th za=tbmJMV*M?s20(o;%+P54zd=?+$qAd^bFDJ`|6gAA~2)55rUE$KaXsQ}EpRS$M$D z^&$6*3-QqT6?o))JRUp08Bd%~##86_#wiFXW@~%|GtO^?s0Fn z=AFNfC(b{?Gw0uEzk7WCS^J&;r9I9UDjuL(uj>-$UGT`=ub0uh^MO2Gh3;`?Ma?f@ z@7Jrey-SesKAx`U6U7@Nt^LVdel6V}=NtTwp1$NW_jnkQkKFCv!R_Yshkio^>!kjxaYeBPw^;_?R-3N z&$Bf=cGvmUc<%f<&AXpZZ^A>DpQJt9zx2G{t$Ek;0G_)2d=$@|&%k5n&*6bvpI7nR zUH|Xkk;{LqdG~ttOFVS1>wdsfciw+%`+r~moo37RAG>_VX)Q12in{A$k+v7_-@1H> zyuYrN)x5j^u7v0A{<7--=viBP-1BB1Ja*m>&)m9gNq>{$($Bdz9{l%n1_u`2dOtim zZ|$J9_3zOBpI``{oG*SCei(ig@53x0@!$fye1YVL@IFb3*De;<_o*Jjucc=}dS(~? zck>W04P`$Yk4R7P2-4n8$D8<)xPIPOdQ|ed4s*C&?{^e$E49|a`_JS2f zJtqB~x!q3Ni&yZWc!<}XZ;Y27mmbaUfLEOl!yC?z!OKrb&q3Vo7(8`;BVNjEehQvA ze+F+l{{WAt*`B$0!+Dn-ShwjmzbamLz8M}qY4f|`HRp%o!3>*E@T&8x@!a{Hc;zYE zGab*I&&JFDw)wB{)cJfna=p#8`Q`A$`MP-1`8Iew%l7PtH=G}XM^D@Q7`*O$0vZz@X`x5e<_|gzZGvf ze;kirv^}rm4d-9uQN!jJ*jd)0?tBG2e97kf;5FylvJMUBAs&5Z z^Bd!J=R4xz=Qe*JUUPl|9(-Z*7vWXs6Y$*mRJ`(~?Rf^zoWGBkzq0us@YMMtyUY5N zzP9-l@x=KCc+>f|c>Im+*$;0xui{bD=Fi3J&acD6Z*BfQyyko+9(-r>@8DJE-{QIR z1^1BkseEsHy5kvs7_YJitmdj&QHcu=hxzy^M~;qUy}Z3@Zcx8 zUG?Snm*WcYp5)&mU%|)V-)O({`S+Bb>d(@z`Q`9nuK2$EJoQuYz}x!yEB-}%Ir=Np z#1rQu@znXM`$;}?o{-O-FHS!A)!yzw>}TlwUj4bx`R{n_e4OsT^YiFWosY#c=eOax z^9S+ZH`)K^JIKP#z#G4d>-R2R!o$DB`;vbjkArpE1q1P~@OlUFqc|^r;pL9vN8(HD zCF`H!!|_$|(tMIX81IWG&UeC_xb_Uk!%=R`3 zuPiM21Nk0^ejh38B<{bDlr3ugVeRiC-i`H~g~v;X>wdn4H}I7GY2wu-C9l3f@wrLs z{?c4Zd|CSI)42YZ5&wvujmf7y#sAG4H~RTYtylkcQa_j3ntXM2$?rq|5WLY_{4n0Q zi3@MN&m7ZJ=5|M@50cOC)DOkeZS3cIN8YrpdswBPwF+P|soe?$A7zpMSuKh*whZO<3l@2_9y=e6 zC(bWudzavRcf4cqn)8YHIOq4{sq<<0Bj-=FJwO+0hn9}iEG{nY%9 zc;bA2Ja>Ks9-VA^&cIXWH9R=Q=J#G*jyJ}4V*k6WAzpVrL3>V>o)P31q^FAOaV?KG zoIgrW`84S%eoSa@Ta&Nh+Vc|mrt>*?<#c;o*R00=JFnxVGi<)B`|td>-qO=>{)_H^ zVtW>QQt~zDGr8TS^Y`$|XxsA%w_A7Klm60~Hs2SoIv;{JoR7lGXW5?7c+L5Bc+>fV zc;#%{^CDiy_56N}m(G#AdMA#z>ikF6r{Vk;ddlb8p2cUd{?2>hP3Qga%6YbD2fXh5 zV7zp`J>E0$s`G2{hV#9OC(732OnHp$*#fUQKN4>`pF)4-0^9REUUxnRFI{N!f8bTT zc-yDFE&UYNzwm>&^$SE4N#^%;qm3U&Zx)F$r%tpNW?*mmbZ} z!E4UvpDDN7blwxMTw!~*!0XNrz)M%!{AqaA`7XuV2(9b9f$QV>c=F||q(^-u`I__A zGPZx7b$$R|x!U%ugV&u;(fuE5^DpZDJO2!CI6r~@@;KYG4PJBJ6>mE4I7^PJa*gd- z7Oy*B8!ufe`JK4mZHvdwhpZ?4bzHBLh49jN=~3SluR8yl>$c%MBwxPH_PkEM=6pE& z*~Im{pM_Vhmmc+rc-{Hqcq zdHn5oD&mjfg^0h7#}}06*^Fl*US$pEQN)|!iMz}59Ej&4emovqSRUVzc~3?B=O8TH z2O{2p`1n2Lc|IgQ6Y;Uue%zj)pVm(KllLl*`Q>;a;``$MeTpB&d2|e(i}=-Rl;68w z@piu4fu|$B94|)v4cuQ;o@XL!L~;D}G&3MIQHPd3?pMW2^8~#IM1NxQ+Aecw(9In4j2QdGffm|4cmo zSov|U#j_E=2ai2o9)COiO5?T;AIFOkUu)yJTzM>hGoFpOd!6xFQ6Ap}PeweB=Oca$ z?yW4(b1I&W_{DfJ;y2;`6Ut-bc_`zP!)^Q*6Yo8#c(Jbo^ojQA8h zAMt~6Z*_T|J@ItJYvRR-f5-atUntM>9-fW(v$%V)Jbt2$f5fehH;>n1pUiq1pQu|M zZ`5xjzCfPF`a>z_SrQ$@1V|68|P%d|i2hw`0Pe%JNUk9MK|G6Fd;u@u3RpFG#&MLbEK_RZ8D|6S#=_qFZ)ZvVYv-tUz|3~@Ss>;)*yYeJ{mM^0{Ymd_V<6ir& z3Z(rbdn&&8hvMz~g^muwD)#@KNV{Ud(@pM%G%?i=Q}?|Zoi_p8b8*U{ax zxVMA+W%7KCmukuz(BCb1tPZ||5>jG{6IXmoBSL6 z9NaaRFT=0I^Lxr2?U@&N_<6UzHO?OWU5cl1AAiyE?4>;S(BF6P)ZX$b_!qeAAg@Kg ze#T>Q`Bk*1_S+ht?1A!=V>)o<=Z;&eopIXJDu}OWe=+jDb{zMP4i;+gWAWW^pFCdp zx6U2SaT7=CxIOTJxI0RI7VV#ar;d{k$1lPQC&*u59InN)C&?dWUhc=!18~OSIlMGf zZr?lfy5$)z4~8-P*?`BA^38k6zr@|u@~iN_0D)oYzOqmU-Q^ijr|5=u2v;1V*Go3tlkf$bjX5+IXem9nYJ{Jn@_SX4>-~i~n2R9se=R-`Hi^>U^~I!A1vwEr?Z6PGVl8kfNno#ZzWpTkQB%I7oj_Pq)*_Awhz`yQlhWIwd; zTS!LyC2P-N${(+<2HE!|79;g(p9JyY^&(RLwi%DkOJ)7cUgi6?eQ#1R;`Tii$;iCe z_ceKu_-{FGHWF{&>y?be|44uHebnD$SkJq@r*#t-#^F#rH$>jDnY>@%72}YI ztmiIy#wT)}8cw{6tdr4rHgY{WgX0z_>A2_7&gPasvOcF1pPa1t=hzSL5A)Y|CH8^l zYQ9@WeEe#~zgky+@f_aje!bG+u} z^!~Q%RT>3eu}}JKbUoTz;jU@m75l^9k@g(Cojg5>j}uQCcLbh{_&_`x@iPOj;)0@> z*LDBEWy)O?dL!3lL>EgP{+T6DDUxS#7~jyvBJr;VzOA~Q0(smbjlW$lKEpk{AxHlc z_whz}?GH3=NxUiE3{T^2@V0mkx9_9tiWe##6a+sx;!nV1kIP%*!*Czpn}HjLCtgwf zyZ9xR{{?xP>-)68w>3U9w&SzN6aP+mNaL2`K5p~)2A+ucfAC}^&u@4NKb`*W_@TzR zh;!Px7I-O==fJ?X)vs>b@ngx8`mwxUgYY!I8y!0%@QU*&f10jFC_2rLnU90nv#?5aePmsSQo@p%a zhaZfWc9GY@`v$(P_VnM558IBPy&a#t9iO%xzb){J{VMILo%THHzQ8NyB|Td6V(0JW z!9H5yNxT8`^~iSe=gIF{D!=XL>u?WmOP-JL1l|PSjHhthZ-2+L_(8;1|Cn`-Grew4 zyo3kaQTWptcY869?ElB&$u{!&_;8EgM{fJr8Mud^$%%a-p6Q@?{jYO3;8~p8Nq6Q> zRl|A7;j>wX!;Y8dJ1N7P++W=jE+-0eBJTHpiv#_*~^##BtLWk8dT>E#|i?KEpgd zgr~Cd7nU?j#43CiK_wB^*@E`V*?uwsGe8a%EmB00Nyz_Q^ zd)J*KIj*awGuQV2A$SbmllEMI$MIe8X?OxZ7QY40;kzkod3X`Og!oQ)vZnH+@t$}QzY;$QPt{WV ze0($>t1Y+l5V#I5GqVbQ_DL-x#+>iL)cq-zD;Ms^DjTdlU{<_h4319B?b3x!$TvO-0a##p+ zs>rV=K37-mTul7!VSMmC%yYYhlHi5MQsR^K6#pFY&k>)!L-CgXUEv1Gmps+#M|Ko-1g6Ic&V}CPpzVVa2%f8RelHk z9gMr?@{`DaM&RLdwp-QTIQ#JVfp2SnyPEh|x@(xh_TlR-PsDEyJ-BatTK6Z8jcl&@ zi#Y9H5csx^yEO2M`=3lVwf`&PU%}Je}2^&d@G*A z?Rq-?GqtA>@xO>K;dVdMXrtoY0OeY&cKjQ206mEGw$Fukd^86Nf#p`8{ z*UJ~<^)_kU<{KUwN*>=e@QQWf2B|&A;d|o=ycd25p2hdXkHPc!?)YH5h+F%| zbr%$jJ?p=SkD>j$;E5q>|8Tq=p2ID^2ks3mk3R`d@d93#c2@md>$ZsRhBv_7aP`aL z_rznk#qW#9aXW4|+{dlIN8yP`d_O#ivrD_-cm}uou?ct%w{e?{7w|Up>smZMLjAJ# z%)@~>o>DqD3@_pni9g-)oT~hL;Fsd5h~I=~a9bw}@f>c~g=Kgt;;-TH zQRT-w}^T z{4hL;+rDu;p2BUP9EoReTmKXA*y%d%D;)PKyog(#dAJ*=c=Jba51&h(H}E`uB>oj1 z8?QX)SJhwC`-=5*hTN`e&GBr+JK;IJ7WsSOdAu4v2ru9^ZsYMHZta|k$In!I_9D** z?DI+d2z)N_Ieb6-QOl28zh1yg5q}SN6U)cpD?AqQKXEVO^}g14#v{Hb?ngY1CnA10 zo{V^ZJQeY=csk-2;ThcKC4*;iyFXcg=W#o~mf=O*o^RxF?<|d5H^%=>JQ?wgcsAl& zEkADid5v!v&y@0;H^I{pZ;u!7;4x|V(+$s^tvrv_lpl?!E|QOAp$x!_7t4PqPtyDn zxjpYX6VFbT_aM);7JsSybn?u`6PK0y0z8jr$+HAcrWKze&nmot+wZY@(c-UAyxsr5 zWBKtX$Wz4AS1SH~{2M%el{|<4iWhOK%c}jY=BsqI;{PIkSMzJiy^Z-)`OCz2#`DwU zmcJ)nxVGFAc=0-Uj&U1_XRenw$1lQjGvs~o8*q1{{0wfG7T~Fy<;!U2GCY2ZydVAo zp23IW@8Bi;6nrzDxK(+2D zQ;8pdC*~=BAwJ6T%$MJepNnVlCHPd!bDQFuG5)jh)a~+l_#!-Zr~F=g6`s$Q`#Q_B zpxig%-a_)zo*yj_J_WD(oyIwFkK*k)&rWy_A4UA`xOcDOXX5+gd3+oj@o{+kf%5o4 zcmZ!fe^0|p4=O%S{~ z?^-;)LV0YT+<^Nl<+gsl!b|uI^!GPB|73amjwQ`YYL)yp+S3$|JtcpFJnitp)AIKC zfw;?;=RX|JMZ6E5d{*)H{ZvEo^mFpbwC7CA|Gd0D$DM@7R+k@lDxSyfyto-JzMyz( zf7bH6B=5z#dK6Etk*}vct8xET`G?#GzKMHlk*xT|S@ZRR{l>5o%@5-&eqwo@Lc_x{!SG@UDJo|yXChebz z=RTCTudN0x#0#IvyV1_2cICw9}UlCtisprzk8jAlC_p|b8Vz=CmJ5zZM*;jw@6Yb1h zw{r-yh_AwNz3OV`uiRfX#FGyz{|eS;E8Krb9%Gz4YR@L~y5;d(@$C0HuH}i*-}EnX8@GLN|3|s4lVfm~*KzHAoWYi- zRGw!dp8rj5!cS+01Sm-j4x#J|9kkC(^)YVo_PJ=WhHX-~3#`Mm65?fj-ZPdhxlKyLHZ1@{(~ z`_XtdlBd7LKdN{e&!joyVEsK8_g9qXzZTEmBe(PA7QDzlVC`IN{*~fwUA+)`eOFu? z4+lQ2;dk!@Uh#fGHokKxPuJI9d_jDcczd3?70=)?^87=dq^~^o`;qJXr2Ltq<-a%8 zKiJ#+RC)g$pTCc%uU5SI_jv4@@;tSE)_5jyUhcRS zcq$U#1<&Ib(9V9iKUMk1;^XlgZtb69K27m<+_{!N;!E*D#McBK*3m`QPa*Jaz3*8h zPi~ElyC>_R+AkUhZ>@ZHyiMTS%F~JX!WPA!XX^(~mgMvC{@clO>UP|oE5^q+3`cTT z#&aV16Zo0rzhXOiuHBB?`?mS(l;57e+3Ta&bomV&_g0RZ#c$+(8f15g&U}@o&?fZ}1YXzjdzeubRi$Dv7xlc*Q#LT6YRFaM^e5 z@fdFV&;7w00TuBs;s*us+iIuJaow6GVSa|koxGj+Q@7(*H&|S!JobLXc|m;mKJ1(F zT^NTcxEJvo@C<$r@$+&2E#WJKo{zV{txhk6N9*)bM<@@cS7n`E8fO@s)#+t$UKUi= z=@s!s9M|gfGQE{QTBnyeMjoxx^ZLkbeOjGf9QSGH|Ekj~tksS1Beds#)#;_bQvB7# z|Nm8|_d>9)wsju8&U#Beq?KQteiiZ5!{z=Xp3cdCr=2x^)A^EKD*uB#O#=^KH;vK# zn@{}zsv9gUQ=VGHTisw0x98+mH|QQyyq%|3H|XKVlE><`Qg~y$vVI9a5Vv|H{|V)v z$c5ADk#f(-Pa^(*)g6_d=oUuWy0W^XV#KZP$h%zoXSD9fn~F2e|EumOu|e&O)*bmp zxz!!D3Fd2C>!dUDoBUbvvpH^W-2E!A%j?h~;k*a?|1`C;3FEVc`%fRYarj^LPF`Fy zK9W3E@07u3;#Thz@1*!VKWHdM6*(F6x}s zJ0)=&2dj6=;5H6c@07<|aa^l+D&Y%pt9QyCsFqlMt9L5kmdEOy+(C-xWm9FnQw-Yd^TtDP2a^-fvb;;r7v9in*a zuhl!n@zLbBdZ#RI_l;KXl*es6t=_4G+qhZ1lk1}6+CF3TP9ARKVD(OM+|C!Pck*$& zkFt8F1a9}SR_~O=ccWic@05BI~DOb?Xh~N z*r6I9yFOaIQyjN(vwEil-iAC@@07!>Jy!3O$GJSZ(Oee__~kb5c%rM?GXb~ys65^P zxB94J#H~KcbyFUTxB4hA;#ME!Z4o_<+pvn>Z3gT0P&SR)kisB?XlxpeUu+@tB*?KC(}-=k1FCezg8dR4p)A2tB>*`ZuL=q#H~Im ziQBweeN+xVmiAbElZ5WIxB93e zZu^ow$0*^puUdUn>ZAN4mFGy@ z>Z3Avv_2|!jN3}&FZ6)xV6*jqjLCO^apm)9^-(e0=EdrxJlysZtB>+=JHMnBf-s+>0_($Zi`Y8W+#YgL-+yHsBJ}Na( zZm+woJ}NaxZqFI4J}N#~9<7f`ogzP-_E>#XG2&Jql^CMX)nzDt)Tl>Z7b~Dm|*) ztv)J!n*3$*TYXewba{TOk4laycdL&|jg{vZ2dj_r#>u1gQT}*&v_2{`K_0D-a%anz z(N3$6%H#cTtB>-|QT$Nc>Z8*5DY(@~74hD<)knq8Rh|=YtB*?KkKVbz5cSgDR+T!b45$SvOLqmonQ(fX+LG)?Wb%C2DZ^Zdx zbyMEW@;Ss?-BccLid)@O5x*I?x~cRn%473xbyM+M<+guX-Bby;^=5Tb+1ZMZ)=j16 z$}QgNro4Ib+h~u~P37@u-BkKE#rI+ytZu4sdwG7Vn@ZnN?p8Myzf&Hqn@VKmlWC9D zO%?EH-Bj_e^88jem02L~OgpV^s&Kd5+HZAJ-aR-M{s!Oc`B>_Hxz#CI-IRM!9<7@y zE|K@3{Z=>SJyh;iH!iRv+aaE04E&D))G~SJp?ZkpEYG)N}G~w5PH@>Up`{uU6Jat(M#VUs)f8 zTb|1LsP7I9Gh4ll)kk?9^(51;t^-?rRJM`)zv`o|RUWI0u==Pp?-xv?{Z=29eN8Qi z)qT+oA?``Fe)<Up`X6RVFZ{;v7`ullHbd7jGps8{4R z{+0DnFPFzx)<-=hw{f%jsN^~F|EiDrL~i3&Ss!(Kd48*p%09`N!h?y*x~6XwZ|$tC zYx5I$r&&HD%m3w9V)l|97du9Dq#4GEst|^aSK>q9{a%)dzoz<%H zc&oF@pI#nsbymso zj(M^1vHB}-YL<$mtJLxG zwA0U*w9}`Z9dN6^$_)2HS&m!%RbqrZk6Zm!ELrYWe^tQGCEn_<+^LGU*Bw@WmB8&j z-|DZ55x4rQ)Tr`2R)3Yp?RmA;Ulk(pR)6K4raTwWPOHDl;$v~Ezlx16Kd#kZ#m30( zxK?N7McnGL;t{uc>DYP7voH5uR+p8HtgG$SWo>0$Wfv=t)nzsNUF$8DlmFk=W#tQs zkJe=+-jv@>JO7{Svih`e6+iYIva&71DgZ6Lh{p%CySN06$*_rrL@%)+c#`pzz zaiY8p_p50h_+F`TY%)l$^mr}E|KiGLa8&BhVE8N|NCnNFqTK>t(e+BJ%6wh8N zZ;C&S=P#45Cja*8)7}W;w>3T=kta7(c}^tHmw42rx@`jcw(0F zSp432KH`Vq@te!zkH)k3c^r2T?%h(}&hdB}x8q)lmm;3Qled;1_ins^+i@Sq6SK>Y z`!b%x?YJM}-kkFMTks5SU!N?$0Yf?y-0dpG=-% zcznL%%~KYS+i|bJz1zz3--2f%en0MRSG?tU0#D(8)1EcvcPRcV`~y6Br~GUDD?E{v zx5j_P)A&Ak&A&9xIo!_EUGQSWTjR02l;7eHznOYs$$viX z7U;MZKNXMTcHFsm61U?%h-V^sR^fTv`n47>MdFLNcemPO$Nd3M;C9^V!GwnQGq}x5 zV?2l3aogg>h);|5HN~ zyvRH!|C{T`D)VLK{)YJq`T4|e!egt-<9{>Hm&e!rN8_A%UOtyRE%5kD<$eHOSX1uD z;HkCcJ`|6=DYt%|ZT@a~{8f1FeYy2(HXi@DJkJB>pOnWxWqCH0`}UbS6sy_uH*iEUB|8Nt|p$IBhOV;{+;mRYmYj zt<7)7$=?z8@0Qzo_VG0F9{G>9c-)Sg!1LU1q>2Au_3GJcbcP;Dygjcj;Wyw`ub!T& zc>5mF|5dM^exiMtAzH8Qo|6CH)~gprb_nylO?#|fJ$I_y?i;OMy%2G$SC4(Gajr`q zt5y&X>l9)7OpExm*MV@);a%I(Boza772JAOyt z73)0RVt*xO{Xb~lyxf;>Cr^Gm{zl+6+|G=mv(w0ZxSjZo+wszN{O7rRjJG}6l;$NV}T>{_Md>yqNKOyjn?|D0V?O|btTI3mq`?zg0 z6GLz6zIaXX=2wOu{9cTtT5fr!2fnTL%nH0>9OC=L!z1p=ac{>Hxcy#+?R`IdEskZ0opB2l39uT#de(!+AuR6HmdyIE zc+1!!ZBTYK}V;&mAB?20sbUuhhAEDt;!OJxB4i z@eH22rDZ51@Q3jDSzahNa6Z0@XWD8X4qoPjKcC~So$}l4{Ehqly)f=7g}a8q2NNph zD|JAkYi{ z&K_ZWiacBJ7}u9Zc$MG_3M$s2*Hh!8)$3eyJXX{^TKoZcVukwKo&NrKo$_QJ(+aeG z<7ndJbIQ-JF}S~{*7**!=S=gxl>cRXGM=8L{UL+jjAu7$+^o)F8SW-(ov0VVulc}} ztSh@7ZL#3uZs4Vjm(@cj46+>GPy7%cdT zeidF;o_fT$##8qykBx(myBo`oI~FfC)XMBd{;Tlx{az@e7`H6$SJ%9ECVml~-$yI3 z0sbtW>8X}jJ?)2ha&fCLav1IZ33od+4c+R*8t%&mmW}L8`_h{T~%eiiTDrk%uKE467fIc$$9G6M&cXRVIBS; zpN@CLV|A3@_L*bxIP1;!{|R{EJN4K6ddrh&5oWgWxgRgaw9_u)xT`GwukvyG$oxjF zG}}*p!1K>3-pp(@U=cvr34zU0{h_y5!m zsn$Bz+5A%-*CT!q?jn9Eo@%T3%6{Pmt{0lk;64pcR{Y}Q;K$DEH$vxN|N3JS@iBg{ zjqT5W<0a~ywA(w^bSL#U_JP`?tG088;E9QfT+BG1fT#KVyLJQT#^Z(EwH}t!rm1*o zlGgtQ{65_KN8>q!{LffC_X!<|{{+wfuwN)$@h@?|gO0lruO1W*Rm@}lNzLyt+{0sA z)YGQyZ{6|ip7O(pA8h`CuGAOP&Qr~+X#c#8_$he0sPVM(@g6+(gWTqEwZ*@#^FqCI zZX=$lsdzi@{=#!Ht&jsaZqwim;)?#pCTgAAaXaCudTQrs#Gi;K?pA*%cA2N#kXPFl?lAn`js`*f@8lor#Gud-;?JT^X6JVWAPmtslUk=HU53@-Ei+) zwO^y>Tvt5*O}P)nQ^)C!bdk-k`SXgm=Kxn)p0l*l%x}ZvJ#}8#aUaGr8RfCpDXT62 zT&=@Rw0{%sbya_DKP=&~gS0+}6JNWr#vxT(=e51gYK7+~Xq=P8AB-no*1VrWe+S~R z^UBZ13oQT2x?u)8A8*D>Yt)`CPAr~jjowChnfyqHzI)&HN2Cl_npgUdnqb0(fy zs{QI%d@7#e^;bvspSgH;Y>V*USEHSe;kny%UT9Zz?tMJjUE^%)zhrqlwdY6j)D0f6 zRGg>zm*t1xEpWF$`8~Wh9xG^_=q}5-F?ex+&iCLkY4|e*FY&xcqwUE&ex9k2& z^Oc&fJ9(kSSJVJ?mCS_4aWI6i|6&lS@i34+^?#2z8`*7=={gO{+LaCj_ZrtEfV&s9FLlRv zZApJm)A??7-REu-=y{9k!L7gJW}x&;}_zkPt?Nabr^R!?&1R54{yf{ zcWYj(zt7>xQ{}eKKeqhS6=`{XwfwuZ3R|)>ijA&kNxmW zR>xKEoVx(`&+8Couys2RcRVlBRmr)h%_nR8r*Yi%xYtUVv$S)o#SfR;dW-GFeBI%N z8SMFX2Rz;NEj2rB(#&cI|W=E4}4xa3y_FT_DScaG8X~)hG z{~8|at^I8t{?cyM!uF?LRDUnRzaT!9Q2RZ6hqmgk|B%-I3wSd;-b?xC;0NQ$HqN8T+;lwky5`sJEAGI(#=5ReB>$6^C$D|!HvBC-*;4B*f&YNV z8mPa0@SXNne`9ZzpQoM8M`&FQ)OO~M$Ky|Ee)X?{`+wYPs-3C}`KREy>(tIo_+5B< zSM^J+b?#Z*y`k%Zz201Bd8jv-LY{B&%& zH{z#;&VT&tkMoI7T--X;yZF1Caj%K;>{MNUu>>y;(Lg4Ne-1AUmXE~W#^W2*PJ8~d z1y67vTZ{Pm?bP4&U5fv)s{W##`B;sQR+n=};Hf{<>YCi5E}RmAY#c{lz7?8>;gn zxUCI;rsMJPYX3{*S%}Bp)q1e|ndk6ibcN0#Rwx!p(ofX6zhUpCHl_N{n*9n`z_++P{kHyQ?Bas4g)q80{HSrT^{@cXHq!chg*;E2U!s0#RGnLg$F9*l+H=3p@bm`7FCougxc9Zr)0RBf z_2TMpvA+Co;=AIdOSKNS;z{$TH9mHpo`=W&)cv+y7pCCeWUUh&#ku?N>`<*Y^E@73 zt9}KKS;C)p@XS8i*=*nb&hq!r$gHECbvmhE@j=SmnEtlLi>-A3_9pQ?aes%7p?rmp zG;gYv*&UyPCl_hHR@2Tq0}s!aLv;n$Zt2`g%QIK&-0rW|;vUcc_a^^$7C%|*_6>V* zwmTimD)#7)@6C*TV>uJxS=AY~ritWS4<5}L< zvvD{F_kWjs#5%be&opTn<{3-=g_gfmzK=bPrytVzG$ww%RQuy{(a@Q=NR|k>4>kfJcsJc-M~WJ zWO-U^rs~tNKSHN*|N5ieLF!kjo7USr{%#*U{)fiR=A}EH?5FYhhU1=yC(h9L|4g2< zEzi&Dm;TndoAAO<<^Cw{_R{fkUajknU5{R|{J*Hbhga2sH{iK8@en5AUs|5XbFi9Sn8(|7-F%ex zx5N{tYaVr#4C+Ae;$$6Hy>srdwR$e(=d^#mPM+h5_jlGAeG-0}p1e#e!R~(+iTgZ{wd-%aLp2WRnO=BAtw!fM;z_Uk{^2M* zThut%^>+jw`&?&YqiX8F#khZyuBR*?Hw$;KC{OS>DExU4PdC>(v3Y;h^6+}f&ZCWZ zYPojQ81udb&-c_!*?iRrUQk!u&*uhc{Iyz~+Xqiv*)nX$!0Kwx5qR-9`LSX*9QS_L zecA~89K5tZ>vkeO-SYpa_+Iz|+;1(vhjCkB`SV&=b{%{fPc~Qk?f&x<%QH{M9Z&v0 z@c3M{|5ChhH;qH`WUcVaIqv>=?qQ9;W-a*sd_2`qd6MKAi^m_-afdLESKQ{p2_BK8p@g&z}on_AX z=AWp)9(hLN=|~;UrFdqY#-RuCbMf3Gx~}POoy*}Vo;%t3@|xx6^HX-;@)cefp!51} z^8alfc|W~jclFo%Nc;0Z#%+H*GhX9t*Q2BHR7`#V`A@^MS8Ba|iQkC3Mau7!e@W;B z{p*kCiT7XCdg#XAeTl~-uYc?HQ2XQ0YuxPqvlX6b(<;ocl=aySFI}tlv?u>CJj;Bw z#m~o6y|sR()Yf0jz;lt$FFcMXBK{xTn^!kXpw$w5hCUdOiv1*6wfz3J1@3;-N;u2f zj~5Tp_%ETIZMYxK4%0qAkoe)mm;Uy`NV_k+1o!!z%cZ<6hj4JkMN)yK36O_94E27x{uc5C0O+w^omuFg`VV(%((m zXWk;dC7u~yFBJQ{a2Grqxy}y5(<^nIYDT}#z)K%%{~tmAH17A;IM{V?K3-g`k+gkm zDIVKPSG3p2^Aeuutns(cTWz*HN9ugHdEBv=`kUr+UiSLJ!wZk;yuN|_J#cR~<(ZBT zz*GIaQ0#u=Ox%A}{ngp++@*NDhSul1ihmggqz=MT~TsTN<- zdeHwmH_!4vq?ujEiSeN2xm)9}UO4w6o{7|xY`}{b>-_qM{{Dvhjnvw5KEP=BYi;;K$)P>cegQCvoo+t=of$ zpM;mr)VyoAaqc>c_vFVCzYtGSx4#l!iD##(J$7ANkH=orjNAVF9iEBQ3)MVIZ=m)Z zO#I>Iv&!!$Pc`qVS4Q19udl?Dmnz=&hX?RP9lfr!&)u)a)2(zq{>gERxVuUFXKnl^ zyl}G@id`3WK1TgYch)%U#6Y(*KS1-Y-Z^(Xo{ek1rjq{*y!fH^vAdX;DR_$a*KD6z zgcq{q>){p46WJ%Xn3whok9aS~t#T~$(n9U5&2jg{<9sf4F!5dR?A;vW zw{Gx4reYmtc)e!(SZh4-m1fGWJ3j99()iduFbwyPP=0%V=OR4E=MHS#X5#6B7q-Xt zrJUtCP3vt&weYXqi+HlF*2!gJ_ZeQ;P5nIv{~Irk(s}n6-nftY8{>Wd!|)DxiR<0D zcz4`yr}K0QJ{T`@pQ)oecW&SwG5`MK3d<9zlUazDc#du3{{-%M&SUqJZ{xYwb(DLV z-|zACE^6mO4!%=g^*7g3_g&NQ7I?OUX7;-3`it&(a*WR3;N^MvbHb-CTqg;}N4v3e z1Bp-7)(oCa{&VnRb+zYOdCn??V@??N1jo5p7+hC<7wQBJU7dKpmkDwO5@y))~DTQ{uZyP{q`#ER}a8b!`NqN&k1-k zuQR#_`A1nEeou53pNxCA>4^5d2XpWo=P!qKOYuTIt#gfrb1&eddwKjfZjn3*DFQ&2e9{_>tx3=_lqlXnm>|!E=m$>Q`>H7anmW$8C;huhMa~TAVu) zPfckZMrMg0jk~<^H>W*USpHYE{_Q%l2ru5JajrxBv$%J(S~!gS-RG;F`Q0>M7vUR- zPes;e&65cDqdJlB!SY0uMm@^P(%5%_y}?kwF;+Uuzw@z@--$L6s~fAu&1g!0(=5;y-*E$mGG zZg_H|;_Y?!Nw`_p8q6$>ix{ev;Z__e*2({IxnCyAyx8 z`8hfheSD7j2U@6?R99K0qSpN zAH}~;ds^cDeHx$Z@UD1zl6(j94=|svasHb4^YGXuIxoJ#=i-^j>z!xu6!jk3g`9gA zFRs%#v?b3Ecyh3gyD#HbFSt-vTt|vKXnZaveh)nPwbuDs{17~Ukn$|UPr?gzwXT-q z9fB8Pw#P!@ALEI4HMK8U%r$t1x(Kay=Wew8*Qh=ESHb5v@Z{kw!n_^_dB>dmY5Y69 z)KN#Y&k@%cq;c^0J|fG%5AGUk9*2|X2t2u1d7i{a;PGL)leE`I7vuS{TF-XhI}0yG zu162zi8r+K&msTQxOcGjNqZl*h{q!J3_A=~zv3yyze%1Jc(SDV(rh|+IPUX&!`9Uh zJU?CQ>KgK#k0&;39MX8dV8K_cs~F!4VR^16KJ&5qWzU7~#M4WZ=Q8p7kLVa?u z2E0h!f}PiQGhyzrRvjI6GIFc44Qt9Y%t;QO-hVlTDi0P^2r&i-TP^&{pddtszz*|{~ipVSCw z)j9X6#q&O65Ay$v$9~ej(Hn0(jDAJlFKCaK=4hQ*JGot36}MpTgsj z>&_h9wbk{)?pv1Nxm`5EcAvQhPw=@3+c&mYp2<4yaU8eCaP_OOx4a3yC!RS>`OUlG zNxp|uyPR`LJh!i27id>;?m|4hRO_}{6&>J4%YU@yy&1<{h$l+gXXfK8@Ys=ZJ73n| zIqKngOz1XSo&&YshLgvQP=B-BFWrqd!u^xHP=?^`%p>3blfd(xwQoF1`~*B!SL0^q z`&2ypyzWcevtQkeJL((l{^v2g&|UfGkbj-!-$Cc8t+&rD5B00IFa3kZd0%FK@-$6q z9K2atw_Rz^zIdUAGMq*H33%*=@_l#$p1ei#=n+4|^3+oMZ^Rc_o{^fDdl;bSasT%6 z^{^RFTrao#-ufe1w~^=kalH74&UCv!?2YGn4%v?OoQ5Z+DSr+2x9iL!-{bcv9*g)# zxa+R{sxSF}#&dh=j5rdnajM#%d`0KQ5%@lM_71iGMCRpiJk9IGvxy&W@m)1vH{h4y z#Vgb=J5Lwl`4yU%-8mnh#S_zXzcGP4AK-;XieHHTf@k=C5)ZE*T$sY=B)soD5pR!Y zI%t2`g?=3sIzj*XBT0OW_r-KJ1=j^U%l8Co^n&lb#N8ble;eof%-_=ZZ{irw;9gDb zt2OZTcyY1L3)`2rg#NDv^8Z#nP5n(AuJN(giF@EN>KkqRk2JqZ$F=V>ABdNp(|m0t z|JiuqOkI!q;MZB6`pTSOJQw3`SB=;S#J_@PF4MYd$h>c`_~p6-x7SU-;C@ey8{w}0 zX!SR@M*FZw{yp(@WFI&j_aiPoZ)f5h_ey|uR93V5Qw z=A}LD+=BbNXlAXRp?2^fD|}tX_sO&&eqVE6^Rk2u`glA!Li<>6+A|LKc)o4x>QX#a zP`pOfxw&|uoz|_L*UK%>HLb#yyiI%7U5 z{Y{MYLb(^;7tcPZ{HySz@z`$4e-u6%&qc1Ym*IW`t*aA>zXQ+QsxvouoE848#1oO{ zz;9ZfNPX{SJRkY~qJMDrgGP7&$KCaG)-9ih8jSCU=OcBbz3{?gT5p|*AB1PC=scRn z^=rI&^D2?7#5LpZkRwW{{`mIQ2I_KnD@aXTqn}&Ozs9!dZcj39}nqM6~_?}oi z!{=h`e0c#c+@|>&O*=Q>*}pXY9q}LV;%&MH=-K6znuOnWuJk)b) zG@SbqFAdOs`yTDAeWv=GKdG%d*hR=V=Cm za;fmAt2y7>Z~Z+A&wZ};t5wbo$4k#>9X6(47vb5OEkfyw-+~tkns@EeL0vkYpswRw z;@9BCNS*CQ-2JBZ*na*io{iKWHa=VZEm6B#qw9fc=-?EtnvB=H>cZtb6j z`@Z(uM&!T4@=wxvZQtYZ2wwV9<8S^Po?53oIxC&~1kZ9G(v|!_n@8ri**WU3$Mb<% z#2%i$Re$qmm#^pjalg5CVEZ1|AH==mmFH~yMT@6?(%v867Uomsz= zr+RQfueh(sUZMT`N4y=LN$9u@@uNcLKmPT{5aJ7c)c*bWyYunfp&CznE;rNS|I#=F zFRR0!dvHJUds3dn<8Ns_*naqS=>KX6|L+M23#pfE-i{SGS zS2CV_&)d0NFs>!OM7^bUA?KFjiOBl}AB0Zh{`JTA#AhB?J3aoc(FHng`dRG&AJgIX zc=ib03EztQcr5b0a3|wQ-Z$(){)u>zy1_yC3_O0n)_HsU5zF(sc5-{pw$AeGuKU|> ziT~2_TUbxhBo9dyK@ zwDWwtNFAWvf8K&8Uhu-mcZttgp8mQX^{uMEcn#0rs{Q0J{0rQtPNpq+cD$JR;=a-P z)!zImtrNTd>4nF?(~i-E^*IR79NZzy{0I5Z#}kKWT|I+m@D$ITF2?V)_}|q2yYZEH zVTkevkNd)(cknFVQ)m0$H+Z_KW-v{j9WPOTUF5#H174`B5y%nW$Ksc098TLoe{m|F zJG5R1)~{>u?8%yUyC1jybSN>y3LOJBE4eK5Cf7zoh$?BRFmvkKb5+KQkM5TXiMvPyCa3 zvYqy~*V*R_xHnMi!OpKOc#Pjic074%UdsCXN9()+^WGBAKjejxUl89NPtVYG$DTtD z$Ni>Sx7OdYEkB<}n@OJQaBpXgvwH2^y%yh4->@qFZaW&4?5rSrnxw;daJ_OQted(I)g)L-qnjs4+f z+>f^iBWp6w593+BPs2Ws{4yRpO6xXwOdbCG$MQtJkL4#k)0Oqcb++E+8i#yF;}HDX z$MB~GUcB22|9g_y9fl{))cL4cc5V=!<#WU~UuWW}pLIS4Ul$qvOv6hHbtZOXea^#U z=j;B{?jM%mg-!AT?OBay`s%#foBZG6UQ#XmocMZa^|v%q_u)q{ZXNJMq;C8uJhw^v zp}jsj1<%|jug7sOz~k$b|5}ba!<^@h1MwwzVU`z)-EXhKvyuJ(6FhdD)>}i`{}=8j zG|t)$oNE$1kg2$DPaZGt%W*s7d9FJ)Unk(X$on7@@fe>I96+9H@C0>7ng!=}U83Wr z`8|D_W#{g&Jk+t=M4lD6zeeY^?b{#YxybJu`V)7<^oqk?cQn32$4y+VJiW*t$CG!K z`*FBGMB{TO^Ed{N@qC~Qd9JWLku^|;?%#8s9Nn4&>)Zg>) z1-OfRFWGX-bA9 z4D$SSo~bA2d!%77UOZ==W0(o*Wj5K%J=z?&1b7UcHi=c`N;dKPjK7WNCVFB1&b0GqJaeM@tD`&DU>f~>TIa9T)$fD*A7~}d zBTsic#pf(+{6|52Ei-LvY^EPMoBdRymFJ=XJNJoTjfcG`2R#n)AS zyPiIRXISUzopZ04x6=K9J?H!!&t2n%EjfesRJ%^?FHzqz3~z=PduV3uxNY$4sXC8h z#2;?{wFchq(?(i;zR%z0cM6_6S7+kK+m4*|9}^s(Rr=ecdpiSjf3y0{WkA=;a=o@_Aam zvn~G^FKmgelk4&H&YHp7Y3F@-iMm9)&s=VKYHMWl)GqkGL_9u8JsMP1iMHU$28utQ z{5xK+{$^Gxo?&y%@e;pZ$s@ieo``(jc|2a^`46X^n~5iRAKd1BIqp;EuieSHw{UMy zjl&d<`yHO%S?Uxm@#Q`$iYsV;$Q4Qa?P~ zR^#~=`Nvs2&*ycNpgs^!Q1879@w4#E_gZi2wR4Y}pI#@7TuuBdc=kD6-&b&>xe+h& z`=+(a1&mo(KsaeeL8l3*cs1mP)nLI{wJA#s(HMWJQMN!UmCaR_;ft= zvyM9v&zc{h{C2%wg(nB=%)OfU_2xV2`OHW7@3`Af)PqK$M-5XBK~^ZkNkegJMa{*b88a+aOnKUzy5fU_)OJ!s8!il-^JY+ z?Vlz58_Rz}hcMpmqiWrx{<;lnzdaXfiYNG`ky&U^4eE3M})^!X`^e^Qz4^Bu2P{yAP4IfD4jc$6kf70>y>413 z-?49;i|6@%VD-+q44&in74{;}qj-tWk7(C*?lruytB&{%@kQK^d~aK|Thy;4_uF=z zYKi+i_po)?1&>juq}?F+eg-@-u6$l5;-%BI|5RI7k?*%T2+v)t{qtvzI{OOWU=hu07 z<{;(&iuieWf#-O3-C2sqj#K{G#J^{GxGvlJ{0c8F^TNm-h_5kQ?aw#SI`59R#l2Y? z2WwAH+%3`d%f@Y_%wQ|1HJHoGim?tcxt__JGTBC&QX73eE!AmHx9sa{2tir$dkYer|67003VP0tSejR zlkrp!jhpRPb3*4I|Lc#%#Jka2&qpvmPvb@IhuiRPUTQ%5Ptp0~U|8rJ>|cMRi1(;(-;ck$ z8F$SzzuoZ1@l51(-+DZLuvW-$;=jl9)HP|hbZ*Ca>R0Y~jsG8vXL~#o`F)y4cM9$=(RkYL1(=N|AJYE(0`ZUH#Yc2rJkETr#q$evUDl{N_dV`* zF7I#M`RZ5c3SCDYAb)E-yHWE#5kCy~o9TS%hxf9{_(a?tum0NmTp2vi`!aSO z-Ge8WYaTnZp{z3J_o3M9=y&kU=^BR(9QP+Y{h>y{u3xd+=ufM>3?n~`H+1;UjCHXyu7t+qA=8^9|>4}&4{g0Ms zAfDj&RN8)c0UlqXoyzvBYc2mVTCu}u|Lu6;TCKyk@u%=2-!p8lfw=3emDY^$Il zF*H->9jTBG^=_8mwqMWd^KkFq<9q+{_U?IK_qDEdt!rIt?X}llD<0WjeIGpgg6bPQ zUWGr;!*jjGQ_Mp?g6DrxzqWO`opA3<*=hTVt7TPRZ@Kc!!&2|+!1K3D{weey4=+XT zZyf-SkC#0^pyzzkLqApTbM7X%W8bEYBg^55$oY+Lz`e6Hj!#AZCb)mO@`FluZZ|x} zJ-NY*Jp8F0Ed1_k-|R_hcbgBif~Sv>-+CZF2p+G}FeKSZ`o(0pzejOGv$)yt#0%o4 z|3TApfc*IzfAAl8K5`$&H*nAHJHwtoO+WW>nBR_kNcD=Hs&$n{=8WLR+5!`(mf@NDc^3{P@@qOyJPeFr=@+6yJ^x!_}Xp^`GT<)1I$v47+@ z)AKt#&wc~7vU9PARo`S^<%d1k(-fZhP2;Ranc!YIc;aW-e+%;C;U(@zwtjl0>93;p z`VjdA#@RnCZwL1o1$^H*iVsB2QG6Ns*aF4X-{g%?;DuAYP_tUcxt;J#N^x%exB3#* z*L|aWemV9ufX6wf$ktDehx_Ns!jZ@i3OWC=-#=53FYsQ;+Up8L zpDhko!qdCevNo@N6P|rU{#=Znui-gQ@lb;Ag8O`zZSqx@;)f^Ivfm@$1fIW9^PepI zM0kPsfbwc^9xpu3J&&q|b2H(^lT_cw(DN|ddrQwFj)j|*@KofUj!p3R|Kx|u(DO4q zmXaSflUJ)fqI&t$WQm7-J>x^=AJUII0iI-^^C`#=fydFKs0;QL!xO)1zGUO+Rq)cG z>PNO7HP<-Ln_JMo9A4mF6? zhs^%}^g=$cc_emNc<<9vfo@Tu@(2iZ9l|J({M{3<;?kzZ!~OSP=k_jS1Y*9#@B zeZMyOch&AA(EmSp?l9HYo~Mp{T=gw{pa{2lPFr{;^1W38?nmx-mOMaPCMQrz65)k!%Lh~ z(GK3*_?Ux2atVAqJl9V%{O7S}Dm=yh8M?2VW%4odD)P^od`j(m68hhVC(b)6B=)@4 zyBGb7{WCR@{|@;g_u6>y%1=l>ANjtZ6+Cy9`qBT89|+GDHACovo#WvJ_WfF(zYLyw zRXwyTdhReiBVy&oa1Ee`;*C!S3Xao|KO)ouk-}<-_zku;F-w#>p>>prYtN`+MJtg{24{gbJ#Nz zUW}<-#=*02|8@Co4CUtGNycli`x;(aD!+9`&u?%Sxfiec)2dhYjPm)X6+Aso@xKi{ zr@#|Oszz_Z&w|HaR6JO`&4L#*vi~*Ymzw-W#d&Qi@I2gYRerF%^Z`5B#*=DR|~j%{Nw|=N7m-N^!Co zzR2V`ue%rgMR<~ZmGVY#?<(A9zl5xE?tk!Ht*Wp2xewfn?2DKHcb?|? z^Reezc#8Xi)oX(DK;Y>oCEtzqddBn|ugrf1dOm{ttmoeY-wKcarWz^goU8Gy>@QX* z3neXnTEMf=Hg9P_PZ<{$RYf@>L!5RD4e5=8Cd^Jp*n1!*vfu zl6xpLOAo$RMZU1Uk*^|J}Y8~em2lCU-4_9 zd~N5o=n@JRCGOrwK0jLiw0QeEDF+2}13|EWWeC7*y{!+~Y&Ck2xSKogwNa%xnb9g-RKCU0UaD`gL z+IKSC<9(1udFRsbTx8uo3y(EXgg-^OPs2+`s$VDJABLR&*zccjkoTTfJYPgZ{tHh{ zQGfUNnQ%DdRhkgBai9G-`KBY5Fxan;(nZt&QtUMOk&RCwY5l{gtaDbvqBYW-EP&j(&= zC;gTWAA`q-slQl$cn_YLqY^DYZ-y7SH^Ii)N~=__^!s`qG5KTRnV;l0c{^A?hnK2r z=A*v~*4g0kE-H5z^}5RBS1Zn|z%%d|-*c%H=N^Y=_R0gc&hR$eU8;T^{Ac*{t?|wp zxorIU2cG?^Z7BC#MakEARrPh8qq|+$HHODIm)OSB?(iJni(Q5MXn5j7S#I%p5!_G9 z67>$}?uHlJ>4{t32+lEpr$%|9W{bmjOwS(aw|w#gJY6O~oLWf+Rd`MHO0n;KHFnm6 zd$-FTl@i=f0Wb7be?Ja?PJp|f(*HI58hH9}wY%ld2jH2e8o72*?pyFwZAFO9(~9tH zJ$Yt6`u~RK)~Vcs;dS$>SMDg4dnyUB3p_Pd_N$eh8w*btl*yN3&s2D9o$P4}pKbcr zDX)GCUt)ZTTIz53O4EOkTD&{_Yj|OvGVOYJ#ntq$cU5i=cvE<`rN#w|pFVJRwc1PG zaP9(l@^3}vh3L7;#;CGq4=Y`~O@>nk92<`XJYLh=(@(=R6?eO#s@{c{oRe4?Y z%6=*fMdye=Tp5q?I zGtu)aJW;bOB;(<+HN@u>`N5v=8pBJGb16H)y(!8kRnapv8=s*0z+=Mhz_(Q21m7uJ zUTOr-TvNUsPlU%=$53wy&SQsrC#eVBfc`7tSg?Evu6i9R=ag*$zbfM_O|MqOQ^jnRM0P)z_Y_O9*x0o{o(m9)qh7K zKN;@7tP)>D|Bdh%>+1G?WTEN5K=ZD*k$(nWtfzIwn&^KAo*1fuD|ieKf4(sJr=@2c zdj1T!$N%5|pTpi!eO=^x)pqb?WFB$~Jk?qD*myJ!9*caRJ_DXTNdB?sl>6bO$amH& zO+Kl-cN+Ct2TvcO9%1)2`~;7GrMR+j@W6LfuM+#oZ5{Y%cZiXV-x2P!p4=2Z79PL7EF`Aq64P^w+Wlzs-vTc_ zrFgS^elI-1J~8#a;5;1D6X6?8Pd)X!ZRr0S?v|+^sn*UN_CEDupU(@}-w0m3Rr55P zhxau3PRb0YAU_ITI!c;X!Y_qqBInB90xwKern&$-7sJy-cO6vUHt=Qe9QvoiUxFuBc_FzPziogQj@Nw8^3_gwc7yDH89h}$RDBB* z6}HQ{Wla!<$p?T$BG+dBdIyR=^7{s$H() zLG&GX=_%!}dkeF^{$J%ZJB5mz^rz zxwGK0$oGub!1I6TNlH->tW&`Yla`xxBgdlu`DYv6J6s-ibIPZ*wxtZ!HNnEpFh z8Q9|LXn1~s+II`(c7;3E|1I7|z>{w%k|!g7Io$h7elUCPg_kaqe#?ii!2OMix5emL z56?d%Zh5TX9OdoI-hWt0CKdt^^ z6HPrH*JlwFk7uEFM4U&8Z|^{Yx7Xs-xw1uxzr|DS~YJ>ap5vd8ofhi4*r z?*e$diS(R@o*Us=zArXC_rX)YDAI!IQ23KGJ)>3bQ1rY7&n#4XZGdlu`(x$F0qEHS z&($vrCC@>=?kDm?l5_0U3!G~TFAi7So{oGkc$V+!#=?{EbW<-R=Q3bi4v(E9du-gl z9UkW#gJI}d2KV-~4ap_g{{lRJoD^?Eexu26R^GlD`QPE`P37}^>{H^_*OO-Jic%DZ zd-o~xE4w;30-kQ5xa~%{)8Hk}pHwN%-DmnE_y4X8_`ZGJ_eb`17m&|HzJu5d_xLWL z66IF-O!k*I+XZE?XYB z+c@u4edKd+?{E3j#=CdmiL5N#jsLeAPpQ9{{y*Tk<{H0dp#QMX<%e9d{Cu(vy!eeY z*t}&BJi&LJmM71IXLi&GHQPGRRFmgijw2}dcDO%8{cbRPIXr!v7ZUxibMM1ruj#q+ zXzKeJJjcG}4(R#G^swLNDtPrTRNvT2Y2E^F3NKbvKJ?LlGTg7Lc-Tw7I18R&o!|0m z8lKuBzu9x*Qh2&{dH=r-FHMynY`pl^y3zFT9$c;C+yn6VHHz>C=y?*J8m+v&2mU5J&wIVL@GU04QzT8i zxs9rC<`miaH1bEo<5wv^sMVbF;rX)i@pcA0$$mMD&$Q`z!wWS}K>vO4L_3XW7s8)_ zXZfC`GIp+p7g?Xb5BZJog6UVKK0w+ykdB6fIMmWs)xz*TDR}&1#jVvV1NYfKa1jMO2+wm~sLk_N!4tnr&phh&ndxb$xIGd69Xx()`F!ZU zRlQ<=C{9)(e-ymH`ABhidwAkVweQc^IT&7g*9#?WUVR=s!+BQvE9Y*2`^=+I4 zK5arh8nvB!9v+LlXa5`?zg7L>3KabePwrBC{e%6LHmkmw|CRS!J$UYBwfhN`^#`5c z@iKW}0($zxvp=X`|3!xx1JBPAABX%!aMw@zFNWV{`mfOX!*c9@7@pcF`5wr>3imnh zRN3CSPvPmFs#mK@`h%b0iP;*F*H#g)yahk3SKNMt{(A7zKbj}lyth3(#k!)hmUE{V z=e>i)=NNc=ffs5%tfC5-4)@2`48b_+I|rWoSMyuTOG`}8+UBAB`%>fn1CKS*48-Ok zA5~Soif5@_mj5>(@69gXEc?f9zczf18j9(y*zQ_a;;ZMMGeD9-HcJ6g}nmnLZ zaPA9u@{#i8{t0)rl*eqodieLMZ(*9`f1}(E@Z4}|w(m4Y!u@LMK{oE356^Ia?jZEc zf~Ow!j_hFjn8c;U4FZ*!;Q957cX~7g{p8f($wy?haM^ zcE!%Y#@R>o2>cv)_5`(8J^JI#@N`r8^E%`of)_`tr*Eg+Rq$9JweM==KZD2LP#)84 z%DEk;f0kcicOs*M8Zyd9+v6ATX4vP{2Q4$b)nKxxeE=cxQMnqr7MNb|AdCM)7I!JPBT4 z{oLB?DtLUVNups=C@m(G@(un0(~?pRMprZ~58QhY#GLb}R;Mx$o!C z$||@HJpH{F0-GoIfXBJt@H~mT;qc@P#ZPk%vS%H9IXr*5dfXcL zyYK?{5}r+aZHDLWRlhh5`8{xt^DYo~b$?QQ^KF!8tl!1qxxVt`Wd2|%JX2k9cs=?r zfhXQp`})Ywfyetx&rJOAFg*XIMk0%`JUsSqS*ZCX;&UTB!+lsCD0f%D_pSdVn|BFG z5&jSIN$!D~4zIma^>sff159tHUz`BGe@n*8AxUfF_@_D%Ri+{^}N#^UdK4|^Oo@T z#ycra?D=#cJU>M3+Xnfw;2z(%+IV*@JR4a@y32UvIXVX~MD{1Ff#>)h|0K%&5}sh) zNwYiWw!!1fe~v}IR!M#+us&(L1Kb^Ucqn-)@_pdRFXcDecX}E;&HbAm@)yDV$bO#L z@OqnDJK620Ot?)eGw;oNo zkHCu^lz%LK3h>ex)yU>=Kf;r3%jeI%@WgGJ&&))BonPputg~4=c7x{|D8hqnVBybD zlaJhgG1=rBt9?H~|4g{!UgKBcS$Kl)VTZt9hG)4)z}o!_xL+pyHXi+EobO3hJLeAm zRrSiTpXw#-Zwk-$mp?1hjy>V2y&4xj#GX;`_^r}B8-6joaFs@iq3|2v37&Usyn6ti z{#1F=;%5asTT$b(>Hi3xzeDBPc=RJY_xe#`iPlf|ntUbcnTtJj|0h4B-&NkehViQn zJQ>+9aS}Yi{$ZQ{oNan?vj1=NUk%Tcm7ljf0Cy`?u4XUJy<&PU*7&j(Jq37{{hXFp zx0?RW>UaMjU+Fj6mve7!g*SpHrc1sr?cNQZ-!4x+Lc5PJ`3vOF4d|Hy_m7sHozQbF zJljKYV)LjwO;4h{AD)NjIFGR@`ZvM72i0Dqu;*`h>0jAHbGcT(tG?M;%2&pF86PEX z&kLu+z3b%%%iEWme8LMY`3ZYw!%NRq4@qt8xf>okQhKg}KMRku-_P>!`*6p4dn5Gk zgcpZNPdD^b|3mePZFFdMM}9Rt_MZB$$!{?|_2h>h$XEGG^~%guoLk(sfqNCbP}0Vo z-f*A$PZib94GTF+_xtBOPvG$z zRNr6WJK@=u%1ARS>lgpRQ<3)(HU5?#lFauepvQyz52yug{?Hko>7+Qfa!-S2o2j4L zynRZ*!|&pcZ!gW*b2;)}2lePi#D50vK2%<90bdG_9i$eKmz;Y8UOZ3na1!=^0Z*-0 ze}B7@ez6sv9Vczt*wa;{5zK7+rPd#|)Z{^`ZlzSY!5Sh=9f@dQ98o1Y07Fxe}7@n%q zIMh5B{jb84(>2bngMS9k@jk-C&Yf`AUiEzx`RaRBugqIsNNk?m1fDvt{5-a^@%4&_ zn&=-6PfwDakCK-zfhVdeZ|Hx6{eJLd9gPSZ=|{^FGBwp@IqB(Y_*Pa zC3tbG?7R{AYEJu6^9AK?WeMk6!n3S*sdoqa6X2=HeFqca?h=h(oABpUxPQ9*7EDXR zpIPv9(>=4s9r_Rr92$@Ch%P39xflA?jbuj zB7Yj(Yb1ZZ0-pqTe7|Pvfmgxv8{|*RZ;Ro@$T>@I!gJi?vl;!L!@bD<w~6!NhVUa0v- z^uGZwv7T@uya@N&s^5J~Jp5&P$cGkhwJNDzaqfLyjGnSL#FM8f1FIJ}*9`gMH5 zqrV5-yHfdK2YQll7a1?EgD3e;*47~xoBo~Z*LBeUDm=zH)q1)N-fzM)M<@^Le}i*) z;Hk~>d>8CFq_XPec+Wc<-V$DDq;ju=_km~Fhui`_30{0q{lfb7EO@$^Ec_Yy6{hD= zMNVxV*xrLD7ASshhkpgn4U*>539R4XKKBNiorhFWy)u)$u*56yTPt{sefu-8=M>|6 zG>#k(A8Wi&BP6zO;Zk@pa)13T@Or`)AIAHRi>YN!v|9CI(RyAp2-jJa`c{*k4x#J#`O|AJWyzLUIJW6WkjsKQDw2fxE`?!&3MJ({sJrw;_BMJkNK6!{JNe zIrck^hQDBXBHw*~3eWOAto6sgO#fQz;}@@vH{{yQ6wd}lNUo{Wqm*T8-D z?To_Cg{Ft^Tdlq;;ThK7+9CfnJjXsFldp6jaX3RWoGkJU;rVyfzs3-sz2S*7HQ&A- zJ_hbuNnWE~a1RlkXy;66Unv!r}J`5f*? z&fnMrPj!@j`;MSq4cYG^=i!`aoO`uwzBCq|=A5gE*nbr~!+kejSI{r+g2!i;e;&`l zi~q=ed)|Kso?E0IcU?8<*=TwO$^(|?e}k8fS04Tadk(6pdL<83#NG&R0x$4hz7xEM z$-k@daXEZ6JawV;cZR2o-y{3m!so+F|5H3%0$&Ety()RlTAjV_3c&0#El-9&nU<9z6G~pfLF#dj;r~|fV#NRb;jtNtXUp?R z3PEosj{DQo8a-d;-qgy z{n1{yzqWk8XmF(blU=ELHN_7nz*CX$yavM)%aoT2$e#mud=GAU^*XrsrSxy14)?%| zE7k9=L(jADOk{ojEqE-FKexdX;}j>A&{O3o+Wmgnxq)(=$Qo1^Szi#ckVWL`fZhK=U^{`=PsB3m(VV2;BnsX&qjX{?(u<_&4>Sl=Q*F& zo=0j02U+jS!){3HP{Rl0)u!+m`x(m6-`V6N-w~e% z2QM^Jf3*448q;%_c(BYE{%kScQ1S2!`dwYstN5id)kW|_IlstXru;dZ_BtH-#MO#7 z%Rk4$v&U(CoPwSc;W_T%vgft2R&Kq9q2y%buZHI%&&Bgh&uEPZ&9Ubpc=j^Q>pH++ zhWo60%Ub6?F+EAOqk3_0f0oH}kEhLVs|O$O?rZmAneyi-{Mig1<6Z`vclq$-i;6>A zR~ZY>@P6FJ{p;X`rLxfS}3AlHVae%J|Om@o;It_t{fy-ZhkrBR>!Mc>A)D z^oBnLPu}E(UA8v4m=-uAJGFI?m%>V6}2K-;X1IF}$!s{mc9_6P}Ko z@4Xc6wO4(+qyIm~>5rB-K845nm!E&`hR1JHyV&?%qk-z1+~9>4+Bnh}o=vEqwr0Jn z3%tlZGOD$6{ow9uwbaklcM9B(oS!+{IP3qGua?2n^cOwdIJX*}joc&uDLnIz^6F2N zyW8~qpm^Q|Kgd&kbDt=m+xxya+#~;=g8WJFbmU&|v*GRw=~3y<&4MRuYlQuZ_Xg#M;*ubJB2@>?Hx@mSe! z{rz-!_AbT0wc{1=IQxb4+u+_4)5CWPHXc1^dLr-d*1{d@LKgqO!jp5QS=KstcthIj zAup_v&G+K)Yz^7pjQaM2dzZ-1C&4F}o`V(7!8V)l=W2Kc|7$c4&h>)FBlnv;15a^o zuzF?iTm#RyD+~3uq1>HtS3y1WPWVBMRIm7Z%JY}Q8^Me0kJVF8aGp6l`=UHE7yDDw zq^H1lqc;AIG(E4%pY~ii74B9jt}dh8+f4sC8po%?m%?M5d)yQL5@8EIvFPR_qz?0n+CwHT# zZWGnlP3{@7M}e<`t4i@AD-ANe_n_DY4F$< z%~v0UUj+9f`#o-edwbOHtY0sJXL=|D|qoS#np@OU*RS4VN>+PnybF)6<$cHz+1s{oy$UUIJ`eR%RR-Y zcjMtP#uu}H20YV1dO8#5_rv3nbE=<%dy#YJJ~TaD)Gos)cn93)y|!xY+#YyrqcZSx z2Ac#=Y*Kz}iT-Ku{Gp2farkYX>6up} zB(2f^6uekR{o+LA-!%D8l*ep*-vlqzlf3Q2{|O$CyuUlN1@Zip=Bw7PTf)1RKWmHQaHz`e}FNyMw+Db7K)cKHIH&BzZP zNAEbF3yn0L3H^%!EwVHE{;Hiyj7ki)B3!dquncIVE9yb(TGIKLg9XFOH3IQOveNPDd` zK2`o7fu7Ic*{mENyp9Zieuew%WXUPmUnA%^`<`c$k$HF%c=~Hu(gpo>=14yAy!6}r zwj=Vz-_-6?(BB`PJ=F_|<&%lVuhw|64Ebx|iSMQVPVAo#&#`akR^*q$OVu>4-3HIY z{VnppJzsnVPxBp(N(tWU!Bcb8FY?s4W*gPl>!Uc)?9RCkaDSn=**O57W}lMjKMS5~ zuK4VSoj1S}OJt|b2kwWv_he5Gg!kZ!V;AgoofLv)|8~#)7v=vR7YaxaCqWw>G=zO2|OLSPh=t7|4#Edd#+yr z&(@aTlIU4${H)d?=}N`6!%O2dW9dcw)QGEIdCv2!hMq?70_)zSPuB_Vw&qZ(-c|MvA&vE{tJ-043{aZBuUxYo+!Heu~vH8Pm@YGS7FPZ#Wcwvt0 z>4u(d@a&Va+{VE@o8*V=Gm0PUzyF$k-V<3o9NmuiXZ@iA`a8gL5q}PVXUKb&$0nLQ z`!wu%vtz+)ZNgFZ(7BDjBp7m_~k+u)_$(qsE$ zm%%f~NzbRqzW`5gze%t%8~(flcRcS{{A@J+gS}AhOo_YQ@Yso(!Oo%|RXtAiEwN6n z>=fL~33rk6IXl4fk@Fe)!Hbuc&yy2Pe_H%D+T}`kmVGRGs&a0j@mJ&@8~2|y-ltVq zBYQ4;7oLBiR>&=$i}3V)8maBx-bLf}okDJ@?3s-pem6a>%jbc^IuM7EeSpp3v6$v{ z-7Cq_z2RQu{De{P^iArI!7^<4Gu7nlm!G%Hg~#7l&-w!SMeyVTEt~LFU_l)!4`N+8n%S``$$^)CJ*V}Nvo&2yE zzR~0}UP#u!|1tfO>W9SMR~~u1{7}41ad;MX#^L@k;&4jd$jsv*G#ril6nA zdp$hSpnUsg;5qgcYcP5yHA7rWo> z@Do(u1mjL;^t6UM&S$Xkw71DezRwx~&oolG*D?TH0?$Xzi<=Ek^8KvUD{H)pJYetd zo-_Wv@`m~Ob$E>Z&+28)ZH1S9Q9LxKUR65LzU#GevXJ(z3r|jH9ZFhWIu4%aTz4A> zPlm^il;vlleU*Sc@ zcc1t?Bv@G3_gqlmTr#lh2=`7_KT6<-!SGTom8+(g^zs{AGAHa{m8Xc#LyqY##C> zJU2~xZbZ*taL4+;p01}_a!eQn$v4o~v@u2yz#I=t|xGDc1Oxd84* z_R~HAPtQ|7vhN(%z>D0g^$GSD;ki9(_fKfY3f<_BN2-0VL%u#dl~B9vLB6NSN4{S; z3m&Vk`kFt}@cc&_k9wj%V?0u?$4q}8Mc^68zXf-Emtpg$AK~fGL`EQAp*!tzTKPPG z3_O0bBGB69WO(jH)prbf&NumcC9mw{++28q^Z6?we=j`qg!I^XR!_t8zqSn}Z5?SP zJo&Zi+X6kG!qaOsf?GfO3!eBy_I$`X`C&a&U%!dk_jKatSh&agZnLv*z{B_c<2B+W zD0e*a#YKv%Ri7wrAc0(k0K*_ovOJ`T^;);Mm@-D`}u zl^>>H&t|yCbERq*tfQLzzw+lU@#njy=M>qQLVhbe&-?c__^pDkdb!uV zP}0`DkAWw+x5DNLZQwrRls(rBhUYow+{U#t;c3pjvAlGJan@aJK6xWNx4Bkmk6P2Y zN8yPfs@Fj3^#;5|emD>QH9XGz@Cf+7aMw%iW#9K57A!>U8&A_IMZy;q^#{$2bAOGz z?OYGIx55hrPecD0c#eD3FN05l`;qT=X2MInhf=MB@1)?VTczLT6(7K}-^gQAY4`8o zxl7ewHp2gb$KO%@w{h^$-l}h5nDVpDqgungF=e6U77wSu)7*3CQ?F6*QXlEL7d{mp zWB)VZ=5B*$SO+^9`9<*5?ec(ncW}NpJl#u?a3A$r4|hA%PY*#)(fIe`-@7Y*FL;LidsVP!6gipDL-ETUkUdP*2vfm{t3JkS>M|h@O|q}_eQ?I{s;Lq z-<6z>p86-Nz8>ER4uc;LPp}`r{L|ZbE6upZBR>Y7y;dH!`Oj2%k@2W2?KLal;W;E- z%8y_3kdNJ_cCqs-mceuPs{dY3xp{br@0xA?@L#x#oR4=vAJsR_dk}_e*BBm)%l|i{ zzc)O2obsFHtFzY=C#*M)WFQ}fd~S~yYS8~WcNyHzicG8+{@UFE&-7J1S7+h!5qLf& zd!9vpH9W<@B{a-kA@!y&lJ@YU!dH<@M48{ zDET<@BjF|HQI_W?!{c>jPgmq;!2KPn*IxX#(Dd+L%<|Y%@WT1(k8h&qZPS0f;$$Iq zZiGAfspYrb@Z<}MpU03tsIUBxsG$D16#dQNan2*MypezxpOXA`^bgJ zcyhJI6d7*~e(6bZn*mvCl zUVVW4kew+1*!K&~;F-vIV%_1T8InH-`$xh(o(JVU=cd5(ixtmvkiQw87@;`%5PR-} zyH#FDEPuXe`oEE8oBw|R&s9-eSswV-G(P=4@Vlpa~-oCo*5 z6_-`coea-(m*!xZCHxr&j~}OYIZxP4gBQ6MkJfh&n4XCJE8uR7o%;+{2<) zaPA+|KSLg9O}UK+QLjWs4pDzEQLiiDdHT^M=)Vu1yhQnSE&Mrn z;tyFaZv@{31bp9fmj7p$P_l^pcP8IR@&n<&z+;j3w$%r#Ub%;AgpxI}vsuXb#}Sl1WspRRcLf{DvSxEI;qI~^W7 zM3H0ba(5Z${vC^}Rq$+k^~W8Q`+>>x-oFa`d*hrB+#BwKh0}e{gV~v~Q=^G<&EW~o z4{3?~`{VR}D*m$C-QHjKMZVBO7S5x-OWqAnbyd9zwBwWT zOhd{4iaqbaOB3Y()yQvw=Q=hFC3_*i2cC%BKX_=cp)TBiGC}iy8)q92m49ORrzv^{ z!o4+$Pg^fK2kwq2ABVTVOOxdv8($tW{ln$|>O6QX;5}estnv@jOt;F)-K%l4V~l_#qb|FwpxUhW}{3-(=qQ+TF@{QNid?FILW$`6*u&W6W(D01#W z{yKQ#EUjBU2EW7jI`Io=uSejy`kDdScR(xQ*->7oVJLdm!BeNm|I?_~c9Y*F{kJ0T zPNf~&C>}0=*N3Mg_xE*%m(Et+uzA!Fc_jp6cxf4%bXL-5AN zBj54$fhQyP$zB96@;ovPJ$Jx!C#$~q!JmYupYuXu&oA%73oj`ScOt(To??HLMu%YC z5}x3^Ec544BUGk2c(IZCV=(;=e@=s^T8lpep9arI&auqEbAOeEe?N+T_k{8D z)n9DgU=2LUyv61l-@}VPsfQ{WgYV+u>Bv5oBTiGjO6Mw0?7eLlc;*@Dxe~t(fhUiZ zyhlKs1NZMyo@tAG#UN1k<-NqqZ9>u)J{x)G%R-C)CGg^zio@E-zYLGpmHavI&rMGa z`OVt#SGeC-ak3csS|e58^lrbDAgX za{It@JCqOmAwL=JPgg!)NgUn?PqbA2F@G+EXShdR*}%C~@EG?)y^Q{KrvGcL3|Jof z-t_+~Kiq|Uh0&^)i<~3W)Oc;Rqs2*gcr53I8jhh|M#KGYo4u$iRUyf zPeM;$<73Lt2hM{R*_TxbduG7noU5i@>D(fCGID>x3ghe_ke8hM%=Aa@d)*CB{jW}F zVILCK0l@?&d@sQH#@0`3!%NK%4&@h7ua5Bi3-Yk_yCLxSTV5z>@pGQ(XFr(b$!p=x zS3ffS_rkN^DSph)D@@Nqwc~5pvlgDHFAtmzFTqncl<)5cpP_Yt4C??Y#ko2G5B>0+ zGUR6Tw1Vevtr3#e#BE=Arlaz|%}dXMdz}@T7N6I`4~)l^5A8Ybdw49Ti0zF%f5Hn# zC?2{|?vdkE-~0yo^Z(ixp1M-??S;G#cSDtLuZN!jPj^v9rC2u|?nmyI$-pyxrT;H= zKlhO7KU@B{dH7rKY$Y!&(eB;b5^{{#@1F|eRj(rZ3{^@H&+uGh`C&4D(;4pnsD5Pc zgGRwKi^T2x@XO(O_MfN~oSO$vM9zDB9-fQjtM^RLo664?=Rd<^zia$ziJi5Q`}%2J zcTnX0NWFlE&ua(BZ#I6l3Hg4G;s5%`mkv-Iwu6s_r-rIuRD!4B`FG0OxfGt6ulR4y zgW1b)S5RC9uW!PikKsA;s?Ccw!TrenAV0%X6%|)DPpfez{dAYgwR>b*!hOzlm$kun z-|#g1S3T-A8J>^K^Jj+~q5b|@jC|p4#gFClRq*7t^6|3?o@3qhBy{bD$J?u4TYflb zg6ic(&M9pPPmGlQW6{$Op1oY{W%Gmy@EG@KH%ERZ+&`v#{4a&4>nq+YKfDch-ITFy z9NY=dy(j(^?OQc?F|;oZQ;~Con!*dutK1Js5WV2>qtw0@hZEtYqckp9Ub+IFxkdSB zAoZFJFW#b_-W&c<$oY@`{&^nx1owLP;ddXw({1GE7vVp`-Iuc8+P(5w)R*TJ8^@1< z7b4%^b%ne0G%p&A{^9T#=XhJ(UTX5Z_tb3AxePpWry|G3-zVVSd(yuOgYxj~yRtt; zxf|j6w^X9Fd&RR=uXI1fgXQxEaPK4e`Iw4QbOPMxevM)19|3plkCj)Q8xJp@AbYGG zuP{A-OY(m7FNEitDPR2oe;yutN%3iT>EX zQAjR^cYx=fm)~rBNx;*QbLKBG`N@js)6jD(JR3PL^kL&WWRHzUubUp;*IU2d3imHo zjqb!hd*J!Cn*S^%p&xmU>Q#6|`fZ-m5gt#ezW>9{5%BB~wbvkc3SMlW{@5Kp3!ePm z3yH<^BXB<{J?~f6AFMY0tS2)aa9_Z^m9ldTf%qFd&--y-YTQxhs$QkY_o3a5Z<6M= zOuUA|3%lf>*U>Y{IOm(SfL~+0pPmQrRN8e5O@CAQ!R7*3$RV_E69{U?9Hi?6o~|NbBBsXJNqO`R)4G};E|;lp#yrT;)^x5jJabX`{?*Uqf7JN9 z5j_)4KC*vrD!lk#wQLpSZ-ggLDGSMu)N2tucDCC6S?cvNJkPnc4UqrP^s^6E+MN3h zo{sPa!9w@Gc|wBo8*SX|1W!IAJMH;p06aBOo}pMb$#_h5CaqrZ!k_ZLyZO{CoU|X_cj`TNbziW_*ZzUlH@BBpEWO3z0!k}XUxuyaF?nPmbj8%421jLW#=yJ zITN1Dl+WkW;TfKnH`6Y2Og?h2{loC|4{bxub}mgGp8r;P=}gM~3?AdWQ@Xp`0e25e z{vzZnO;LUQ1C>u~+^i4J)=+9PE?E8zRy3#WhY68?QrYMi@B^)0q73rP}w z4BX>=u8oiF;mMTx?+(iC1$TVk;h|>?JjcFS8%HjN$9P|+(JHu4$M^`v;a2o4hZmW@ zsr2A}D|npmXp}vI?~#r39uPBKwUp``dqMNbcIdAka{gn#e>x+dxkmZ(7k)Pw?!Huw zY@T+uan^6m&MV=C`RaGWF=)Q&uc-dTFz%j$$Ev7ZOwar9qNnz~412c0bDYazjob&d8$v>$4 za4mjV0Wb0%+2%{{o1Vi}-wD|BBixUiXI1kO)hiqMp0PPRvqE;-IMvg5JbkVFvyFDS0bbxcT(z2W55hBt%APZ^=QVhY zd#|j%>*3xT(r@GS5Aakyd7wMxR+*~$=5JBG&PRWJxW~Sr$?y|QzOnS%eDYLywx!lp z?3}0zOwVdB)bJvDZh80D)E;Zxw=CCYm@;pf8_vM+*t z2$p|tGX1?(uCxX14$rkxyPt>t*Whu^rECh{Y~`|U+#0?Io@rd(pGRLVKloQ16iR-A zd19yTihZngQp4qtj0PaWDGq=OD zb)`ReJrVv?m_|G=R-D**EDhj=$bBE3jh~~qJ(>0z4lf?B__-N=KHNW}O<3OJ*q??w z?whu8VS&jz>9;s}()7RTg&J-^|GV&9#Lms8=Ula;_3M8^juHF)Q){~X5NoLUjNQA` z0v>0)Q zr!wBpl-mKGj6BEnfv4umZ}xq`>F`V=<(V_la|Jwik9ZnB-}KZ~hOzI4o`UD^mET@N z{sWWms&=t)=R0`1w&I~;3;n@g@WghlNZ7b`=vDGVDk1;>fd2OI_@`<|+y65d?nTbW zz6>5aQ+mEf&jNV<7{yx;_zUn7>$ElCAHn^|d0boJi6ye9~Z*aBhm5uDj zI2oRd@blrt$bOHR@bpVwSfb76AA;vbm#^>ZaPJPupNgH|8egf*(3Sr62Ry_55&Em( z{KIQxe};1)XQ8J9JkED&YE9<`!PAlbqEn2&E<0aA&rH*^R*_)+Vi7z!uYA0{49~E> zr`dIIuTv}8>9gO`^7AI-i+o4fl5+RLQ#_X`dV}*)u9cqHSjE4s4|Ig*9#Z>$MS}i4 zSm@ZdUXkZ}87p@X@`=If*DH~q4A1bLFWth;fu}RlZ+f1AdvD1P$IxDD;rWRDJK=?P zn!gP}QI#30SBiUElqG_DGvV3i^!$Z-=fU%Q*RL$(TrYU;5%p`!w_{BIWVP>a`1wlH zUso-aAP?UUFaGF-lJ@-gp7E2^BIl!L3p_bQm?yu>`JId&coPY+kVs!6#c;d%C1)q|(tnK62Dv~~WO@C5UU zKhggXy!fX2udO4!0#BZ;cC_`w0^CLJtNjyR;JXal&w0f4wBy#cVTrd;Zd15xsdldo zKM9^&ulTflat1uhJ|#t|bJxI&hp0zbKD-B>7@~Hxaq97ahu`z`+lFWgC9 ztt9>y+;7@21ooV@6<)knb_UCa;m=-p=_Iw6dZlwm-5@^{*xzn>%!j8tC_k)_8h1L} ztx+DZ`O>xUQh&9JN)OJ9h9^H({;~LZ0UqZZ7>l=e;ju-EPt&v0_-=W49`+n~qwLSu zke#=}o51}K%ICL}|Bp*^H{{QUmv~>V(u4Ibc#-*`wd2F^%(L>tFX(v%p5y!qwN7yV z6x@q^Z?X;USl{r_bKp(XEAkxL)c9@EY|j@x;mNAXhxgO&m*Uu);_oP+em2XzPV!PBIzo5P9z>~+SAB{mi4$pHAkLei#_dBXb{|EW= z;o04KUYHHP4qlp~d}4l{3(uXeIN6N+@{sc%`~CAO@@e)RS^obD9$TXPb}@f*^8YlS zDV(MH+W55_d9R1c9g6(Hx5z)qOVy5L_`e}M{fF{^)vKHF15~c9kDUUK|JX9rZ0}De z!QIW$vxRbJ!V4YLk9yHAj|MzkpKshHl*}XlJo3dmH2&H=|6|j`d;f2c-waQ&o^0*7 z*Z4!qJeGeN-6}s6HmRjrpr<=LwMG84^V~+lvypwIXTf6&)$aCv&@_1d5ZSM6=G=UE z@fqbwyN~-Rxcf}`+~Q{)Jj3~8){iFFtrX551&KJ#rTPo&D;TPujzi zlhwX&;)eln*HHcCRro}B{shH;I~LBS!?Oi1lvJ+?&J~20)~j9}(esMw|Ga#CH^Ke( z$^$Eq{{tQysz|6=LBBX~w(3=y>xIDP|IOjvL9&0Oig72KJkN1zHRsNS``mlgj&e^K zEdA-TlnH0TZ$>^hUwQR7_)>VB`x6gS3%G=uRbGNKt*Sw8( z{8D;sy|Ep0j5v ztIw7Fj(NM~&xY{ACCc-gknaUgRV)k1YWO(QbA-yB2)`Qco#ut4Cwv|}JyZGf6!_C7 zKUw8=uBcxW;DtGA7mZfIdDL*%UF8l$&p{d0%WEw@0DdeyIkZtoY(1wRJk?qK%hn;! zh8KTP-mvxLE8vM4>KAry%xxxri}Y`%+$Z3%6E%X^{TFWpJUmzGFO8c;LG?fWWxw8+Ki7uMw*z@>axW8NRVB>H7d8%)Y z^%i>$Jr?dd%Kjnf?+uT&R;D@}J_cTVS^91LXBs@&NqWrw3_KrM=YQDrM9$TD2_C;% zPkfdqKY$k&$`2=C&rW!b{c<*5#OABMN#0jl{wagInd(Pb^z<~&Jz^ihN5j+H4|NiJ zI@}+v__uNPK9gUfc-w;fe~hnCKN?89ZGabVS3KK!Sv%m_jMo*PHRWJW_FC_A| zb7k;kTiro~o!{+yVD5RX_a+`G??X+QsHSubO=1z07x}|01>f z`{>yV&$QLJ{13eDUGhV0yT*lkXvgE=9`oMwkRJ?Ba;}c`i)rvwQl9}Z7`C~o=ZVz~df;&3eWnhVcOQ@dXUe*_-8SovWc`q!9zZN=@|@NbRteYc(( zf_R1(&S)J<+I+jpLe)3H{&`vDTw{1Hvd-4s_*cygUI*n6mH@WQL|+l6%O z`S5g1J$g9yzXmVu(DUF9_(pi_O6B20i2o8iad~Z!`)PGOHp5bu+4fVUv;TOWa3u=U(wD+*r z!1H{cr7YxJ7M?6qyHCUaufa3CA5ruM-eYH=G+C&Rt|if0>_Z-l2WR3@2-o<$~qxyH?L@R#AqW7NMoV9y44X`b|J zbPK-khR3(+??sevhuV;rV{@ynUbk89c)} zwxTE)AK{MkI&9ppl2v_E+~Z*Lz`F2Kb&as&i1QBcTvgd~EA|Y5XHHWd+kE%E-* z_~4LZ+I=RCMOARh9!Xvat3 zsq=F=)<&2GsWA-B)Dd9_mFCO3GzMR zDc%=boQ#4Oxu5X{>O=TG3?M)lu;*k3Xpx!RJVziozJZ2CEWPp#nGP4IYRU%^u*AGzm!J-o!Z&>8f92T%S-@h}2j;Stp0cGkKJA!; z$IF!YCnEng+%W4^Sz;J5$vyc zRQ1Z-uKAC$t8;DOxyXI5C&GQ=d>H!2!oBhxcfymbzfFKY0eAG5 z_V6|E!p&-T%jcU-5Bnc3L;i1gf_At0dELk8r+i0X@6p=A^O19kPKD>1%KmfFf2r~6 z>WQ{4moYu>EB?D7{{r0Alb?IQKZ7R@SG{Jycf6)-<{&Ur{%pfk&pAf!^X$!;i=aZR}(8r(Y^3|WW6N^_ju3SjB;1O zOQ)&6Hs9U|FSe5YYQ&FQuKK#Slz$Xm!G1+}hW(Q)Ke*%Iel_`7+MF8zFYZyf&tU&# zc+MBU7dvl)Cm#@RfjtkKd^g2|waY4#XJ5@=^n3-+*OWc_U*~>?r$(t>mS-wGL44j^ zBh*k2I~%}VNgAyG`tStrA0MFHF>sIZ+VaCRc!oS+dFejWA2}E61$eTu^4lKtuZ5@U zDAKH5ehB!!^H{1!&SUulc`tG=QjI6~)%XAZ(;(!*xucDgAr~Uj2JRXuKd*!Lh36yZ zwoQV^Mr&M9YdUv5+>rIio8YdF^jLoT7oN$;o{Nz`>}l0E#XV46 z;Z5L~H5!RdC;ofF{jSo#3;E%2S5J1Xho28mw^zBBz-O8M$ax4)!{d?r+TJwz-ijQH z!y-J%J*PIW_#ZrWr|kK4aKOaPks2J z4;4O_7x(8LafJ^xiu;05aKYsn!4={8{ZF0$xqa`gy5WQ0Z_@XFPMtdE)T!lG)va3Q zIjdSLqv&PCmv|n+akTf{4(CJS&nL+r9jE?zGHKrRY zy6@H-k9GL<vCV`5#9-;yzoA>L@yo_$a^I-A;X4#MilxNJ|0x(})lJLi-VZCK0`dc<~(d^Crq! zAwKd+)lhr?DEdC}=rJ1qRr8{#|~2s52yT74L@Du z?cS7gKJlg3DgT|w|8n9j-fvPM{_n)2(=`6~5dWCT`L6oY+S?xyU-^Lc3&%5qTZk|4 z+_@_$=iVPzyP~(K1J=p^c;d@^Z&E98KNs=UOuiN3tqlGO!%t8NPo*EOAU=A6`oaA6 zMdB;3)d+b9_57L1VS6}F{2#;z{;53|i!r+2f2&h0WskIgBCWiCep;IX5B;wKgG_HQlaw!pC=KT>*Cx3_d z>KV#!{lz833*6^$Hu>L2d~{IDw?X`?hW|pxy(5gbpAjGUs@nA#;(sH)#Pc-FpAWzS zkc<-+cd7iHlwTyi#``6QY1b*l*Lfbri^<e*%DMBWlz-(u8i(H|ULd~2@78Ue;W*=O zDL=(W&m%r^uFAiKewa6Y?gx7|<-d`5f!`ZyFAzl^CBF6t?YZjYUnRcG?;W(%aE~zY zvBwu3r=2hKN8&A(qiP#P_x-fmyZ$V#kEhXqqliZz)%NyF%y$>@#iu;lAvPY^M|>NS@-jb<5-)JwT%$3Hs>GKbsB$i)-(Ev}A+vw%?Zn51 zwZE|aS|299IHj8ZnfhEue3j>7y^#1Xh_7u|{}+fK`dPKBxTYgaE0@O@&N$IpkM&LB ztM66`wh#GC;_HX1e6x2y@zDpV{=Z^8FA`t=u(k|U>hl5OBRnT$g!os8uYFVXyo7$c zj(Fi$`lh^tFJQkSzW7{?ShFkooZ4IHYCG^*%0Ha=0Ne? zk=B>zP@fT#&wY9}UK%t0pJ-eiLH-5eqZez-b2s9b6CdIElTTy5R}(KjPbJ%W*>8-W z=cd{@ezy`ITUY)}s310_uM&@#6oeKToBc z2Yg=bT{%|yZ{hmdk;IGN)Hpnf{3jUB@xV^v&n3QmlGgY45bqLSzfs%6BFC|BBtCY- zv5wQ`CEi7RmFt*h*MDL|h4hcBd{MYJ&zZM+z2ERj zwf9!ac?I#sb(M1&!iLx7s?MH2Gg6KJX9ic{Pe~&Y1Ckr{E-9L;1H8 zU;OmF9B%ROfG@KC@_QK@R}2y_K34O!a(O24sHMma)N`Ep808;I{6&Um=Ep84UigUC z7b~yN6EFTq+evMu@!m>&{p1Ha$#0@vHxXa{mx9A>-+uHZ+BKp6`CsbuVB*C$tH*4- zx5fA`)ByPt`OhJ~wy5QDCi6Yt@M|<*TStC5@v#S~e2c4h5g*}wPnrtf$r_$n$N8P% z_kEPpd9U_I&!c~SVEn(<`uk+c`LoIAJk|4w-}5VK*YdF%pGOftlKA=y zRiD2SKaKcGW<7D-_%r*5FD5?9??6wdoXd$XKUMoRt6!fYzIw)^9p?+l|4rg+Ur;|- z{Qs8t`nryzE++q7zsfk|JtkJKA4Poad5pt5Yl7p5FK5oF8wGA-A&Pc?D&!yKcMyM| z|1Tgu@(L{%^XFTLuU?~mzLfc1NxbzDtuJ=I(+cqct~*$}{R@+GwYCHHzHmG75$-G2 z)+37Uakctk?PirvHKRumA9$4dp-1D6C%$lu`t$CL^RtML-d=E=d&%D>zR2$ct-pS= z;SbUN%j)Te3~wlY66O4W_zL%vuzQbgCf@o}x_s|-joP))QT=~PJ)da!<%*X$J~@H- z%5~bFT)}+Di7(zw#}#&N!z}Ruem8eD`7a?pmN_@(J;Ybf*7|Z2%l8Y!7e1ur^?1tp z5%ER#YlcVvmvLJtI?k=+zc2B^v07hlBK|bu3qMi+*!Nb?A-?=MZRhVyeJ&)v`bUjJ zbkpv?MdFK`XLuUrznAzJzrV0}zLt3FY3fg_U%w{4cA7@u80AFQs$GSv3J$S(m;&(y zeh<|q|5J#s?a^{HKb%c`By)dmlX&r=TJJ8WoL3Vc;C{<9h+kp&(^QjV=!dVGoJ+N5 ze7P29^fThioX@oP=sSH)?ONe}*UwV^qlm9&_N8wlKJXpQx4;Uymv|xbK08l5DrkFt zJmtTY`1($@_btRfMtt;rs<^cqtHc)umH)%!|1I&w%~~J-LHzDtSG%HjshL-M0CtapD6!Cs}({oOeNdnd{oeQT`Rg7kKWsjc2}0y!cs-=WXQw zIq|XI=*amZ;d~W#FJ<-(9%uM3H2%+}{$~(hznjLP^%qs+=lA>CJ4exr4Ci?u_MUYa z@dbY0XYKq49nQ=Wf4*e=-_!Q_L8SeP`1%Vq#{%(zZ!&I&)t{dreiZSg%y|gAh%Y`? z`^Ou|f1ct0rtMFO_yOW8JYQ>&_@&1GRL%FT#FvSWji}!aKTHXqA>P`q?fLJ?|4WmT z*;jMdZ>e1)S7<+SEd%Z$hTlo+o#nfg`0^22e?QGYIfM8B$9p#ZKc9GXo7SV3QlDAF z`Q6qC@fVw%!}N{D>gijEuYOAXY2SS;6JO7q8}=RI3;fPvC*}X1_$cS+Z47qEb@W4~ z|9ynvd>^-Q(N5y)JdgJ|ls`dyiSJG8tS>JnKKcMHm)Dd3GLysocuK`Qt>IkvKZ^W6 zAin%Ct+_8D{%hhRnR{}hZ>wEPne&mJNPOfw+F!q#dLBnS%6#8ZA-=%xf9-wYRm6*# z^}+vR_@KtgBPjnG;$!=?o!>$Hw}wAY#~Z(*UHANs>c7nU3*viqiLW;bj?>#FwtGDm!g@28JpzONv@oar~;Pkc0ke~b9Y$24y3yQ<$ioI(?S?(|)? zE83~!8EfB;AU^tStwCGppJC$boZqXk-t8vd;&5p z&LFtpN8>s++yR&R}pXhr+U)r>HCQ|%;yvTo$>P=3>yy| z@_m+9=3LFAiLV@^{(Ls&Jd^m^1GPqfk@%G1gE}sN$koe-!>Yv-le=YG<-fQ+G;x`%p3sp~x z!^3{aczdNPZOnm8tqT{sj>Q_z92UPwk zl=Bnf#n?Rxd+N#sA3c(JDPuVB5HBEEjC z)}wpUfCb{M>$JTpP|mxEuRcZna2oNeh_BsU{b2F@W8x#n9^*J|Kglh`3vbtakEcF| z{Y32=*sLRv7Ue&V_{w)R-zND-h%fSc(sRgPCcgG^^;?tp0phLissEpNh!S2$eEl-j zM|=M$`Y+-mneUpfaX5u0{`|z`JVooN-OF$r@llR{Y#jd3>($=%A82_!gR+jem!7XR zP}X?1c4>(G3zum8+x){biI0`E-z-p0h4@le>-!Y(IpT}|R5=4I_e+UKi|Xh5FmOLW zd^Ix<`9#9w9`bbj-q@2RQ=HUf|iTzYkCav5$=Oht#Ge5 z=lR#T?qhOJB>&3S)ZVjb?|Hq%@jFKwm%feoD($kk`hf9g<_W(-eB~mI=UM8%Mtq(3TQ zX7V5YGnVh6%K1#7bBXUZIltF(xAE2a#1~G~_#C7Bml{9oqs8Z2iAS72w0iMz;>+JsPJ6HU z3i0Cc8dp>&`Z@8Hs|zm2xIZGk{&4m4&O56@kGetqFu;4Vte%b#U!B+T`ZM((BOX=M z|F<&ZS;KEW)^QGz{{_U0uhMe7jrgS||2&N=)BpX%M;@(yei8YvHu=0K)W*ZVB);?~ z_0P?;E4q>O@lUGHIh6A_;;Y=psNRdBUdVl3Sp7Jj? z{>xQ=o7cLM_$ud}%-&BDAGk*Qub)uPH;Aw7(i-$P+VyMV#aF22nktG0e!+O=cb&T_ zXPEfNg<9@Q#P<-7_??}#W9J(`?~yk@yoz`sGoHMh_}bqzVzu>-qW>noa=5l9H!$CC z6K{Q@;1Fx)*NG2sUE?0)KkS!k*W$Ix|9Rq1Aim0W)ynZ%#-G7k#77>had;f%yxQ>0 zcVzE$IGGZEK5FvMC^)oA+SiB|d9IhOYusS`FV#4|iTro{mD;s(Hyz({xEdWve1PY7 znIBFdzR2?jbd(-N&oepq)cW{T%6}2@k##M>w-f(2;>Ei^+9Br?{|NEXCo6xO__qwt z+++9`;sf00prc3JZ}@AqtN41gw?{dTCtlz^wpJfcAil=)Jq~~usUIFie2w_X$24D?$GDAn z;Tu{a_MO!I5CF2jWo=yB&tFrYLE^2$RsNXiNqprWT3<9uqo_tadV0}uUQ7NC@xmX} zqPr4bbT}Upf36_^>V9pPj-_2+Cccz;5B<5}{9eY|ndmoa*K%3Q_bBG^5aO#Rs{FSR z-%Naf`+KcjI*s`H&$ZvwRyvAW#Md6D_Nq6q4r2J{m0!IVMeiiOuubdHZ`$Hq|) z`JLJo{Ydq<_s}8Y>rdA9-2C$l;)`!qIe(!3Rl~RHIM&V|cmeS-p8NU|^8Xw0h0J#- zpCCT+M6JInEsCx)Ik#xOw(j#s;>-V4aFVSbx%cnYt`_Hu2AS^@iLY}W%-a8_6CcTZ zZ&fC~Hlq4i|8fEGvCMf0uQmLD>iK<^*ZYZ&a-X5a?RSiSm$uuO_HqCHlK9BOG|p{Y zap?c3U8~PgeO^wxPPcQsuXvb4KF0ydqsia;k>+?R`Hv^Q^cXE!>zDTsAIW?lF-N?} zamBqT=QYIF-lm>WtE1?H#K*p^`n-eu-z2`kd6*jU-#DBPi9i2K{-qmrMDZlj9`pzG z!@|tb4zc#+c;YKOclz1nKa2Px_b1!9`h4OeA6EGfp#NVA{C?3xqqS?bC$)b0t>j<3 zTJ8NB<$siT@fq5W*!#pIJ%zazu z5^rU`2Wb;udAF9-y=m8LiI4nK+oiwI5$`5Gmf0Wp8R7%4*7&sZy1!1maBjhAzLWa@ zlK9%Hwm;us{2%g1mKXPz>MTJNJ%acW_mBOUaz==cUZ`>YEaK-9FZ@d5YL#}iiMM{B zalV86mk=*z?0P5h<;;GAPZD2hX!+Va&#KAE%oE;3eC0Ovr_E#EPJASj?*s8-BIVoS z_sFlJp1X*zji}#jUZ_lb^;a67*3ZrpFMd-IyZ7e}#22quKRlHFc^~nm5sj+`^SzpQ z;ZcfPef%Zyb?%?E^D1vAzEae9{siSbVIHIUL{*Iwjb5CmO8&)fX};G{|E~}) zJVbN+3h^6>M?4qhCgS)06Xicu{We7W$%b>?_88))5g+@C_SY6yV^JC)cCu&@+6aO3W=;PYIs8{iw%b(S*l{2(meE|87 zA|CO4wx`jq-NXkjzNbTM+|?jHnpt;#Iq`LVxAhjvxy*2$m-TJppCLZNa}2a~jiMhA zU+HPS-y;9b#23D*elR^B{1>%rf%jRcH=<}W@s)k)e&jsjBOg%tmr{O%c;PY~Pu@uU z65^}>rRDfr;_oBgVm!Z?_*KLg_`Qkdg19yLneV}GB_46Sy-YcW|CQzS7tPn+U$zh* zcx}OPo=N^QiAS8Dw0XrT;w|3O_c*p27Z4wLi^~5T<-CRX!Xp_ujFV;JYngLqzD#`e zQZ1Jw$^Uc1`CfAm%Kshl6@FLy6zX$VOn^xHS-e>5(Lc!laN+};S9urlVd9JP+5)_r zav4|CIX2#?>Du-r{%bnhN`<4d?eq z8bwjGMttR`+U~uK^8Z47;UbL?Yk!XTyZT}Eo7(P;lYblWxnO3rTn(eax3xm$EV+?9&nr5TX?zhTX`Kze1Z2%1v9%a^#9(gK_|hKbxAlVGnVbh{JM$yzbEp4RyOyrec1%YX zQFK4zg-trXx`pzeV)*+C4zcyAbBISgr_S1qeZ*U5Y5V`W!&K1AfXnx@OQj(N*ucG( z{7YT6>rG0Ht{}en8f}?vocVd;EB{e&oTkrDh>z{oxHbMinf%Q9=7Tujxc(3Iz>&;% ztKn}{zu7qdbm9e`k7xbhwBg@Sdnf4U7Z8tlzWo;||INgUchPq2NyI-wd?|BY@ehbE zAE9;?$bU2OkqrNXBU#t>|2xv*h*`cLnSXW?U&*WkK9_habM8u;_`)wW{#lyQD@+c@ z&*q=Y46kU8&!cLeGC4AipIPvAzX!#z+e4k2u{n^?M zZzX;v@zD=xj;8-S@sYo3yiMOl6I@EX_~3N>e9++(n)q`S`B&araOnS#wq|lZq_~a$ zZ!`YKsh;Ma2jRmvvA0lH+~VhGhf`?c&(p|1z<$BX@f_ld&(^s5KGVz*Uw*l|Z; z#Q2NaUfK76?U5zl!)M<^Dy=d8*;} zQ2)P{_;ZP`AFBQ10`VU47SGc-Nc{E07oV@?V*Tco#K-Qba{itCUm(7Awfd(`|6fnM zxaTpBb1(V-PJH!yTEAXR{`=lT?OnZxTKW+3A5DCW=Lu+Q8AWFp|0C27wL58o^MSi_ zU7oKa8H=}B@(=KSM%6ZoUPFB3i>l{2jKeFPoCifCe@^#bpCteK+mydXJ+C9)%Jdh% zG5MMO8;9PL<;(MR)vLJoiul-^${(csv>kp1@w;+==F*S# zJ;mfajQA+`A=`NGDa2cK9e4eN<$Jc_d~e-J`EB9@J5--<5`PWxmFqO3-g6fvTuyxb z^V$x~lK;~t{}Jl{DdIme{AKra$Zv^9c%g9p&h_dy8xKE-_%i3KtsNUO{O;PbXzn=w zg!l;0RohMZ6U0Z~sgZ2)b|LZADaD^p{x<-3@xMvqKRQGSZzF#zlkZ1OKHo>Io_^ir z&lH@1>nQ&(#0xJ_KMygk9(W)1!zj;@V{t`Wh%XPM<7AY0@pl@BcD`bb_|h{q-pb7P zWyBZ1seV3%`M!;K#P5l$fBb^+pQ`b52Kj#qT*jqif75!sM*06k{xQCH*!b;s;>(w5 zJe)xO!|$v17RuWG97X(C;_H9VcK#;fPbI#*OWVEMh@WP1ey9AH7IXjA4FA0P&GsQ& zWO8_Z?{g^U9mLl@UvP-Elb<2JaOC41Zu1F0CEnuu=p)r{k7j+j?||Cd zdacI4{u@P4B0lyyt?ycz*cVsjd<(xPdKu-PNdCq13aStBe<8khm6q=o;%&>9=M`8x zxoC2x9_=`(X7oPd1>T=x{oq%Kj~=i7d>iH8KzyL69<%Z~?0$^rDJ`$74$%Y;CBB?F zzj_Ptv4^QXr&9hN;wwDI-o{;Z;v>hYC#T8(V&aS2HQsD|{x*}7nb-Op@xo)3e}Zy; zLVWeHD*qVbe;{6*(Q(}l;t#yP+8brYz1xY8eM;L;dtW=9_zKr|o<=$Qh_`r8s73|$ z5fLA`K>cs~G_E8bWzK>5I`Op&H6H97vtJNjzD8TlN3p)#ZgO}}tHs0p9-wxuos^FM zA;W*H8vY0Me-`n9%ssqq)3~z!^lIWOZ_=7#^Hjel zzWyRDuWwM!zynqP^}jyWAr}*WGV!tdYdw8F@kTg=l-so)**=;7BLCVW)IZi=Uu*Jt zPuWx0k^GGKBG0i=X;E~m@!v)3U4i*NE*pP-#N<$aE0=4DFYQn{|3>-O6E9w(@&6Cv ze>OZb9=q@1YVXK%Rn8m8|2X1Hx9GU*0^-LLFTPG&RJu9ZL%eXdYW^1zrVZzL{u*^r z^eWwF=^gFxH5o&^PG&-B}6mGC6P6nx)>0qOTG!d`#_q7aOwcO+NRfe30`0PJHZ-DnY%C z`(GZa_AdTZ+sRMdMH3uLy!C#K+aoCFS;R-as}W`W;6CCDJl9cceH6Wt_!{>&+PL~k zlao1b>dV9z_i4EtMZ11Ryuk0NZ9d^vlh6020`+|O!&r_yS7ayg?ZihKT3$~gK1#gB zc`EbIH1RdwFK_FE7nz(Bi%!BGt;W%7fxC7rvv2m_9X~Tm{P}>%|E~6T?;)-Ed7YOY zyIJGV`mZmOfBnna9_}Ik&xo(isT_^2DEc$;QJy1YcHR33miy5f56_^SM-yL}N!Q1x z6JNN8mb=xj=MpcRr|pK-mmcwvqqQ8*r~Ee%U;C5hYjN@slXKQFj??1go5aViDmYxD zG>U#te3bVAS$lHNhpSx+Usru@V7`wh-ukl^RGat?;sgIs-28kx@u;Bj|3LCjnVih~ z-3y41QO+yL{|4fV7izhj#CrXH;tS_!&H4lRKTmx0X7$^9iT}{#XWH}IOwO>vta{zSw=dlkhFd`5N&7o=a@y@)P2P)q>+ZmHf97AH7=pWvw;X$Ms0{ zLo}{6;``)3mUt_34!}-_lPU41M*h|36dY>(>4M35iORQm$R*+f-_~-scIFd??^OSs zz&yT7eBm9+Z}Is*#7EfCB|;-jz9`fK%Kr}0-+&+D0QnfM6%Ypaj* zhVy$^d+)o1cwtc6PkVoSkMZx+IQ$*uuNeM3?FXN57bW}{xU`dnqK@}Q$se_*4|Gbc z@>I3dsdh{C>TI>1z$(p|R=LtmAd|JmMAU1PJDu88qdHNlHB0SkISb9TYu#$ptxT05 zVWyT8fNY}j#6-K=>6AJUUM+Ro%}RmudUp8_^W4xQ#?ce36rcQ;j_ zO1IZ;lp5v6rdGM#73G?}#zd*xEY%v_Xr|VfDbJT2w`OfNy4sLl2%o&D`@UvMfNGU}(pLqDBZgIu7sQ+4a+zJ!E4 za3DQI_Njb!dhcvZjFv0=P8MNn~~53f zV(EdWQs1zI@M4^#GhLhP*5b#R=4||XqSq?TRx43?c7CWN54A?8k)+NzvZ>SQ#x&IM z?o_pbXsUImA-w;aeqjNKXC|uSy(u`nJfmHM6wLYa^Fd|zS7&-92%j#`R$=<=cs~hA zY58D5?$w!A2^AeWAzO4ErE%s6^@VlI?J0DGapF?5JyC6^1@^hc zgmH9VmFd_cPQ8SOni>))UDs8dU#X~3<1@7({Z&6$LH}XSMYrb~PWRj@O;jg4&|xZq zDW&OZy;W^@qIRjhX$u;^QgPGfVfh1nH*MbGepPClHg6G*QX9XvM(vs~4@Kw!hBggv z17-iF%|j7db3AU1T*2YjmS`S)Xv*Qq%BEp7=}xB01cc3lD8AOF&BbWHlP9@4jxEKg z+?d$3d3&_Gw0qO=AaMK{iYncMn}#<>l{)?m3rl6wW@Nr;3v{cBMuX8fvMolPp-n?O zkU9R}G_*~B$WKuSf3`-bs8DD}gys-pBoL*Zl^V@jV>WVbZAe7bCpukp&iIAOI@OSd zOdsntv|}KHAm!gtPDnMk_oRcDsktX&WD>m2<1)THQ}^F5IQDs5B?4{k{lgag0;|M|Ra#+h+unD<-MAilFb8 z?9(8TE>Gp2F3Io zlVW;~Q87Kote9R8s~*#HOpECo7}vN1;~Eni8`qfF*f^0NEEOsz@;9b$Y+Pd^-?(Ob z!-8wJH#Du;-q11;zJcN*d_(evW;NUSR?XjmS)gxd7w8)r=Hzdnx|6>#eM8%jKHoTL z)i*3OVc5_-Vc5_<&0+%sG>bc6*w93gkZ+@ORvX$V3>(@g3>(_0S!`f}W^o4$8`>xm z@@*+0aH|*w98{*w9AJVgnO2i#uT0 z&_T0G%Kd(*cH=r42$VG zmc{fO(_(s#Z81G$oOBo)TGwaDF|W^%V_%;k$G|>Aj)i@O925HtAseN`*wDs4LynDo zh8!FF3^_LT8FFmwGvwIVX9(FS9ma+>_8D?)>@(!p*k{PGvCoiWW1k_%#y&&HM(Hp% zw6V{SV`HBo$HqQGj*WeW92@%#IX3ngLN;#j7AvG2yM&ZukqQj<(<(5Rl%v0-8js)N zo0zGt@g-eeyxlWi(dDIGo-wJEW>6r((QFN8B;Ywgm#Lp*4${4*o`tUS!*Q-?v9pl0 zf?3RoN@OK030qu}#Iu_RNw8PvF_i-PMAN1rnwUTh0R4GAafwR?G3jIpxi(J)QK<M|7(phSX_(h)*;&Egy;FcRbtypFg%lu_h(J8tvS%Bi@Y#BAKE}NvpZi@0fqpw1b6B$zb7TPL zv`VYjt&d{{YqnPD)@Dk0C{1GlRQ_Ud6BCOaEQVp0y0Wi25tT}ly+)-}!eXM#TbE|4 zGcsQc-pP8eGhG@#Ac<-d^QE#(QPhovbr~%O#cHQvJK*@(WYgvSuxr0YH@mn_H3Z6T&Fl5}7@YCG?l_u_A#5eFhm= z6Dv?TDJIL9=Z+WNVy|*0mQ8xiHEY(Uqm2iUXt3N2JewV3iZSp(Jl6W%1aJus#@cl} zr`y0HzBHL>fU^7Zz^!iEO_O#|1F&2xnTu-j=oYB#VS7yKr8(s>=9Gs%CuYmheP9+m zPK(KO4{4*lbv#hk6KpDsH%kSjKSKODcxG!IEUw&`D^v5Cn1^4AEzb{{L zUw$V?zT&=o#eMmTr{vFWpOJb{*;kpaR`w-5jYXqwI&1_E~y6sNU0@-k^(x@9s(e(f+R{K zE>#8s6VyuU8Ngi9EMpHqN-bBr(un6!NtD*XrOH5(ECT_7Vku2sN)ME@9ta?XK!+kPbRzR1JZWWtMYxvQ@%BuhGLk9%*yg!aBIt2*Qp#)hP72Q*c|h zTV$nno1J<9?D-%Xc#lz>ot4;-<>ZX+u++n_Nbh0DBP0^buF87$-ZS=`iLG>5`ID7d z>^MU=DOdoqP(&VvqVzD7k78Or6RGlX9rff)y<9Jq z(?z3Q_(T)?N|J`@I3Ns9R-n7z<2X1~0GewB*@n0Q2e_S%?gb=IPa)>$8YWFT4N4$- z7CI6}U}=&YGwp9r%+&Ep)Jf}+rlsS!Pb+VbPH5?L&QC1_Nxif`siKpoxbHy01AfR+ z+hq~c0@*v7uXaD3ueMXKJU&tCq#E`Z?uFru@qPBRRxutb0Lir%Di|4y_Bc#Wj*RyU zp%Wmq9l_Vs;QaJqjyepGh=-l!4j?Wh`v4?qs ziRyfxicbk-jsW_hB`o9H0?YzAWqep?dt@l)XpM5a-JHWDd!IdqIJsNvlshHa=Z|UT zHpX!!jELs3bF+yr84}Y$6Cu7fzP6aI{eSTNf62q%RBS;V9J&K}SP&T8dIvnZ({^a{ z9kA^!&C90Ops7++<`Vfm(ZD-+KwgM%;tu%KnUeD4nQm!lTZ-K##B|ndIOC3HX?FV{ zUu9Sn<@o4AzMc_CXP=}F$&=3U z*ptr4*ptqm*i&_0zU`@@`oxp2u4cGtXXu3P&pqpGt^zIx*idP8+DbKUcq+{ez|mw% zn4b!;p-~tD6B0TVR9)-J4CN5~~w+EE1$_a4x~? zeL}1VFz=$m_}=9Lm@^f#n=&=YFpxP?na$+DK;}LZRC!ELWA{28WJ*_?S+vb?Ua4^~ zgToFCeorfw=JqsvQe!-qgekhbRK2+{NwzEJ(d3mnM%D;u>g$6=Z>hv>A~A1)wxI?o6(J;y$f@(KawVYgv0 zPK1=BB1n05BV4h+F(^<-`FU*+L3sjQxaMRgF`1)1^vDYmA?2v(GRr9z8CT6<4N%&K zX0k??DnMlgt6g%fd$lxP!zW0|VS;N<@WLi5AJzn8y(IPQnyIie^!>WJE9CT)hW(rSi!;k`QX_v;`l3NRFhfXZ8QU!yh z1McbU(@#5VcZm}*ZaE}ji*$skwVyV6%@k1gSn6q(sV3ffu#6|((!4M!XMGGlPHp#l zb`1MO^}6n1kOb}-<4N;DOlO0A_G!C!n(i=*V{&A(d#}M@alf_a-m~j$>uKqGtS#!Y zo8;H7V!Rc_tQdPGx@CzK6c2{0)ru({v+g}5$?Ejt9j8fgB#)IhK zSW*ml=#$;DmlcsLp<*Cgbqm*VV!T}GAl-?QCrwUePdN2V(^ZiZNB0^aGMvW}%I}qF zRO?{TfW)PN5mlnMY~2W<^2} zs)(_fm|_z$j_>66weaN~+6T92iLB6WhysWY=Cb#MlxLEyk8Eyn)|_?jnLAI28=zf# zcAc3@1F>EEuF~<3KI;@S`rK!Vf%S=UHbq|%?6A;bvy8U2BxM^#ePYJ>2K~mI^R9dq zPkr|8JZY!aLl+{b!-;5xognYlMsjcMDH3R^!OoLT0%y!RpJYYNK_)S)KBg1a zSVbyLd=)R7R#2Uw9G7pI#|bB&cq)IMb=uCIyLH=-yd&~-1`QZh=o$Xe*m{@dldvm5ziP5u@K@umPkhuT!%oF35;%pIwJ{w|8 zGRpC6)JD^3IIR-|VYbRGmsw5%VPGJWScLYRdit4r61vOtb2HE9CZ3@WT#7i2wGgBu zA7NWXKUMOOaBe1vWZCD4%L5Q>b`1)Gt!1r=Y`KbK zWbc`KPB~LPFV2Q@w~hB%iyd$w>S}I*3nFzOWW?Pk1w^R!AniTD6W^x=v#w7hvxW$l zH73C55<@WRa`k0KYB98$@0OEgt=3DmoS(1sRpAe-CM2Djr| z=l<`_M%T7O#qHf=Ly&aCt7R;qgq}Sv0*uLJFsn)ToV*K1P*iQ6aVQd+J>dgwsYjU3 zawQ0dRLueyzw6v|qa3?)*SV*piqQ=ouu)3~0n#Z!7ios1&2XI!9^4FJ+^J{-lAZ(7 zp@3^j`PnijH#((iz3M*B?jC62qyD~&BeVh9v6gVqf#ix}h?`s+O^KT<)n2r}{Yf`$ zq{NWbCG7TXM-+x_cZ9U<38k?mudEH7vtbz4W7}p&jTn{zg1qWr7R!-`#9@ve5}~D^ zEYei1a!%FSVJ_8ZO?#Bl<0@5}qIpVWLofLPzC#C{;0E!f?sRVkn+t8Kx9_sZQDje^ z6Rt<_xM7>5@NqT7`g19{eiB6$!fcito@3HeAjb3XL>-6vV7gDn3<uD5V8v~H0Z$ABQB0w`ZSGrXxKC?^0DlwNX7340j&8ouKuMFI_E4>XWH zP(OQsne}C-mB3-andP#W+ZT>Vu1$4 z0u71{IRd;PM+h929CC#0O$_Ml&eI5J?eKtfhx?klqc?)$b9^m_a|Fc$_r<$&J_6z$ z9?;(50r3tGiASzM@nP-V(IP?dLG9&Zh=6o~1G)ebMrD zNKotYd_c5t2Sf|(idD*fz7s$;eb6q6C5MwC9dxGI~b((H?ct-9}Hm_O>Es< z^D+WFJQRcnw+7+uArzk%1mUpgc35;fB)S|HUB<@>fgHfXM28Uu3--^k+Bd&u-A4-H<q^ z>oOgXZIb;r>za?f?u3Y7X2{SgtxtZ2AKc#Fj*|j z@(G3#Fj*K526!MV!GlxFQqL|8=+ti58O4Xq$p`2ya{5-t#s|yUjjAN?ERxI_+KMky z+8CBdDH=MyBnB^eX^k(Hho4r%%qb_K>JDQ}h~^=25F%8J-cVxY&c1{pz*443z`7nj zgkk{`!bKED+Zj==CJfa|as(#Idy}kgU_z#bUE+9Gg<{cUqqAvikxy81z$BqOxP)&0 z2)*v6Ej#2N)Td2bw#h$R<)1C`4>I1gWmx`!HF6-}l$>FRe}oTb7~&tH;^@ICIsXs; z2mvP*;vXU4TTuLijYM*QApVg&aDpKIkvwpKApSufGV$U@1^A&O0sL^iKRzcF9ub2R z_wbJ-mWFH7pd`j2fA~jq8!XD z(<2s>iH(AKAq{|89>+B1ghH@@Vh9~EkmQqfyzmF&NiO?H9L)jt zIt0Ir*6*B?iL%=oJ!iO$b_pj*Yl(`)Wp2FG%EWd3p6oUS55YmzNB0!)C=4FQ=H|WfW7qd^#Xa=j4>2d3hyhUTz7R zmtSv#{1P-TzXZ+8FG2J2>tJI?o_R6J&n+f-%}p;Rd8wLM3=NlAOvz8o}Z2FW55u&iCrox_Vg+19aUvK97ZE9}Wu z*pn@NK|r>`o@|9Z*$R8IrR@*MR@jrRuqRt#Pd3JL2mPXSs3+N>o?wT1axpuU6VNfm zlOI#OJYtHMPZP5=UIQhdCtW~Kuz;RSGzS5p4)vru)D!DaPcEu|K(0eQxeoQ@I@Fho zEtzOMYQ7`NM)Bnv#m~biUOtt2+s{HkPr87fU;#aub<52w#F*kqk11XjF(r_Xi^qVE z>Uf86-V5YIN+2InynNvC%mG4fP%o$_-|>2~9U79GQ=eGI2ZQo8O(+kgc=^zSfi@wg zcy`1TFQ1qa$j5mwm=7s|d`Jo8LyDIVy)&2VojE^H<20T$+7pl#Qv$lV+KHnmvoC>+ zyr48^Pe8XgjVDcxIP$BfLp@JA)YDXsMDis#)RW*)PXgpl`w|=)k`Um9Tzr+E^%Nw` z`EtPsa3zf(E1m4TuKn$(;-agF`*J4)x?t z28-UIzJze$L$RR$#5y5(a*W_9V+7Am><#tZC!i-=Ku@lK0hxjL#OA?}%s_k!rzg{W z^zRE$*i%bkPl#)t4+dIIBY1+0;E8grc1V;FJW)mnh(ePc7NvxMC?$BJ%J_aIvo*yH zA^>Mrf(_1WPK;qLXK^c`z?0pM45q!hM%;@R;-2!8vX~eU<6vKkgFPXY)`3!}VR5i0 zM7eW>cuo{G)hr4A0tp7AS&3h`vyhnfk$;v^0~GXcG<1oVs$(8~s&mIMae4)vru z)D!DaPi_wvQ|hbmt1_a2riXNwAI>+pFS&a%1@vSJ=*bk&lPSu3G6nQx3h2oc z(38o_aDBN(@MOc;f#Ib?!@Qm%;0+8J9U77l7#y~-qrg|$p}qv+^>q-`^G2stuT@g# z^GIWhg{^6v4kGJDp8FFtFXsf!%RfQ$v=ACTQ_9Vcv|xUu1@j{CbkLW$~0O?rqMz&apY32OrwQl8Z9Uj%dmO2<2)-z3(6!dBokXBa%CDVB-3a? znH$)SEf~3a8Z9JK{SuCAqlMzkXdxSn7R=B2B_}`9g87ja%#XBCej1g_i(L(tdEtH?pLiuUC6t1^M3&}KEP^N1qa%7Sglu24hX1%#VeIP9))6xZH zx;8niC*E4}>jP;and+Co^kUpz*Zkf#K?~VnGK2X!pXKC7S}0z$f2jHWTHJTk{N4>T z^=CLgqlNM_TF5@5h3wOE$jQ%@L)f2|E|?$Fh4Rz3C!C+rLiQOgl%LT;`DuF{&d+F} z{EQaL&uF3iv_A^xXS7g$MhoR_#DeHL43Tu z4@Yz8xm}5u1c8HS!_yS3da_RG=qj~wkYFwu66_}t|ur*n0^E9SJp+H@p{FDGiYWhfYd82S>Rw}k~;bZ~t4MSm1 zF@-$~750pirSy<&2m7)e?8|nrCtH@%1F{wNWGn2+R@jp*OX&gG3VX5@_GByU$(E(` zfNX_5*$R8I6;8=^o2Pqnta*xCcaVo+$1Mko=cMpRZVph69Czz~f$^q0beu%bmLxHi zql%TCkX{(Nlh^&RkHRS-&QLiAUXDCZ3rdqxdZkGrf%0iZjzE$^0!a!8^vqNh1P%xi zI3NV|Eo7p=0U-hhgvh40l*8!^zyTq`9T3u$&1xYrN(cy2f-kCF#c`qmNs9Y|6c5Ph z>~D92f|L-Dq=b|x7nAUQV2W|l!6`9)ZeIx6sm4r53<5D*pI z%`}Ou1M{Z}2ZRK-7EQVxM?qaO)36$FQjpa^!A~k7B`BF9r4I>Gl3`g&2?C~pjw6&l^pC#aj-9>GJ_YdRI3=5ZBP|(2ZaPCiqZe!rN`IA!66|Sq=qyB z91;RJBm^VRkPyHjA%J}$a8*EtpuPw}eF+du-cau@T z5NU3EICUsHGL}|P2my24T}dHPN(hKjLO>KgVercbIfwNLD-)7{&lQ4d1SH6|d|wAa zeF=j4R>;AC19G{6UWh?2B>Sz7W}C?pq|NFG5gXLU#sti25QN?8|Vl zF9ek#GZ1l+=wM%ngMA^0=8zBv`$8P-3xS_PLLBT1aj-80vfCjkiu+;|_vJ{vnh6Oa z!IwlrK$MJcf})fV5T%5GD13evC~;&R*tH^ULtx*E41Iz^1PL4-?r+}{lgqa>r7Vw5N6@dd*3+(%~EO#IILLBT1ad1#bsJekWG#GZU zFJz-Rr#{WssZR^Z!~{CFJA~%3WCNnoLNbl!%WU*B^!=6~`cf0bpjgW@DAtHUu}1X7 z)+YurTF33-?QEQY(!mbQFl8E-sfQ_$hGmuqheShJf4N~TL(iyTrmbs2b!f~wp28uu^FfoS$`@$6QXSjSs$4)*? z{F$3@8u@KN+#pNwXMW1OSfW336OO~mu>Ir6FNZRnO9=*UV}rm2EkAIDm=_D62r$%0&2r)1GK+6wYA?60IBW{po ze&9;Vyubxb{22~h5OV@oDB-{rN;q(Z5)9nUxt+vtE@dc}vO%D4&JFY7T*^=`WrK>b zLABVRHHW~wDzQ1YPaMvr4CPWbs6tW};b4%m2veji!j!x!msjEPs+%j*a28UgVTzP# zm?C8wrfg6-H>jGfOmhY1)mbUia3U$wFh$BVOxd6s=T+joDx6n=H;1e5a2Os6!;_O* za}Rps%=*OSBs-}vk>j8+k-b8g$X+T;q(1(;T2i4f)ierIO{Fl^bP7{Vsh&S_?t7^d zq{d1Gsj*T)YOGX%nqO`kl$t3MC~eauKr%G~By)`SCW6Bkb)0-K;YY63$dj z2_&Q;f$}md0wgPq04Xn`tQ-PKO!ENAsU5)0{W+~fUV)q5p-{<P$|?f^(=89H5%m zK&-?E=yvfRgL^yf^+%H*Zn|lw4_fvjm-P-b4y0xzh4x7Sz(P=kwmv zktXMz7L?E{q&gs+N!}|z(&T0me0^2Fgfq#TJwTe=OoA`Noi6GF>X1Je?sT=ThA3Ji zLljE$8@gCrh(viIk_q(MpT58V$pi*STK_}3YyA&Vw3de`sIeiKnS=~tE+G=L36Uh9 z5N;U-47H2`B+Dp3av241t%yM#v=t7LELA|Dr3#Rc3Sl1OZ>gr23EU^Qiu#`_QTX=)Q02Kk64r{b%>(l0NE{fO5I(!GpM? z)edhSDjmcj>HUjl69lWD`*0avy^2%u9VZUI2Zmi+-C7H1 zF{(8x?NYsrn*qy}ZVd-g*KpS!@JXDClf>QZ6`f|+7tkyANDs%4EFO7=A?}P2{Ejnl zVAWPlcUPCCdN7h^7z%EG^iW8kI_u3ykIPd(vb%&^da_jMQ2F9ZKBH;>m zlDT}shZ{uHGq{3NVq0}NS#EQOSS`cdExU-(XSGa!!(-Jp zZh{AMhSxo2$(j^bx9-=XlysABs~oNqsYdz}!4^lC1Xm(!J-j_GfIhX+P1f;_1~~F@ zicQFMqPSB*oQMBXM(;fRB>fz++1j+jwE_u8Y0OCR;hJ$to}fV$D;yBeJ_*rTwSzZk z47(>>ts%8%hdym~58L$5j&1Hq*#~jkhgLIv-l_?pf}mTaEy16y`bQIPRZd(TA~f7x zA%Aeag?rL$akq$jQeoRv${=nR5p)pOWawX&H^{VuxR%7>n@PuACHkz{58?t7O=t2{ zgF#%Tp?oxjDh&=>I+}_rQyd@eR?)x4PaM~2I2>1|=wCH$5SOVaKcDHNK{{!WUK*sE z2DegAI%*J2hsvWqxV1%|ckmHcp~&NQO2_pmj=M;$i`0rGI#?uM(fCx%Ho~~1MNZj) zh)7QGk-tvdpo-f@ZdGv`!5z4Ib<=h=TaDdezGZ6nM={(;A`&-iT3jt7PxKvKM~~q? z8b{O2a7B%K(sV40t(xgpYQK%fqE!^$L9=Qbi+~|KXdy*&2Dj1WG-z-e)!$Bev~AFw zyq)s4(}~*|QrlHWki{V?bO&ig>QrRfqT$TD$UJegjMIIP^g-okG&6{Czs_vAE;sB5 zFAB{)nHKH|*X9Tf7xTD3su}L;aS3o^j3n2rcBuDu&={hFLlzQp@%1K{CjV}ie|Hq! z?=9|MrBZn+Gzk49pFv#wHx$AL6Qzv6}dmO0CyPmZ?f5hLbRJza94KlsuXvjRpK&{K0hzR(yK{gIT#)V<+v}$ z>~bbvXOg&}$;3J~^r3bWh&J@DKa;DQk15N2IuS3vaTh-0Tg+~`-A%3#w#t=S>bq_M zQlIgbd)+jL09lTSlnii8q&Ofn%^^UFqdYO&sm)BtkP$s~dSf8&hiSV$QpPCUe?5!w zgY0-u+Tt*b|2(WbpPKO}UkbDQvzZ%?F9%Xe84ex7c&!n}-4{GzlKUbjOv058Nvqso z0g;w6)q%Kc$OplhGS)a#pbSMOO4WIc&y!g>I0=~LLP#lPgbKdVL{y28VSM(uVJM;4Vc*ETw8pO`&2G@CdOFUiCKaw6x{HIQD$8&WgpmSz)Y zuCnyVzU=+D$q_sA1My4>`m}5fASGZVDXDCv(d#xl=}!_A?4`k^x*J%MzM0b{LgR_= zC(GSl8~snvnhZzCDzIeFg>}OB3n6okY@ZNPR8xffk$GX2iZ>@vF-Z0bOl~cCJj%jtW z%OIgTU#YgbwPpkJsF-HP<&mkCfp%5qsc|fGyEv373h@8Ut z84X%ScfeOPKD9*zN^@o?)P$ojeVP>$QqQx&8P9bNmBho+y0~I=`~W;vo5Es7ycXgK zFg3EdCk0Pq&L8VBbMaEv`O2yZfN~Cz*ZM*S$k!6KaksFoqfpu*Xuie$Dh>0-i zQ7g^WaGgoJF(H`{AMTWAs-=EO!cez9;o=X=NZI)ZKyD!0Z0;*{4s??AaoD-9)kM&i zD$~`WN8v!eauM`S8jTvgQ0o5feT$bD0(c$u@P{yR-2;$h=mPjmrLzxeE(4nT*owP zlA}AGd~)}c%W`HvP2bFk+T>)U`AhbgAar92_j^m?_mHdSI7&lSO1tAVSA4p1g@&fy z>tHpHIg}@0<~XjJ?dOMUsrB_)*H$b+vi>-M?wIv9Zl9Idn=9kU`f{yp?!$o?_Ol>qZM@dIR#toX2lh}U4=V{$wsYMsL9fl#gb&YN#2xn&JgP#eXVub2dylW_u-5W zH)3#=1I5-ZTeg#}$re5|?>{1!8B8UZ522;20L{kihFLqwlPz>qRzbxcB!vL<4t<9S z%$4n!m~wwY|Gs5?@ukvauTd$LWDYo)mCBrKZJ;f)MFd)D3}M-&|9eZ~_tuoPuJv&% z@m`@wV9eDzRZ~V=J`LokvwyzZYe)>+r1rLGzN^4?OQB7cyLmg6saxwSrG4OngOkR% zbw1%Mw?m5^VXTe@V0ooPp;A{I;YQSb40m zbqSL;Bm}}Q2;hE%Sr8zG2Rm(RNcaQ|Wg*kKdt3%N+5RJ*`bIVApjng#%e4pM(Fn~k zro-E)8_^lNPCKi#bJt!sl&NB80_F!xnCH_RDmbjK+nbz}JF}~3FlrrHtMATWNw_Hm zTPmTCo2oa*@s>M*`?Ncya&JD8T3oMos}tDN5acLft3$1Xd9U(;5=KVt16cBvu5+R{ zGjl+E(0_vd80jThdlhIijXo27hNy&%9;NF39#)?P1V5HBYB1P!kf26A48i6xBy)@G zcyL1qK}r~*p<$I&6Bwj*tC4K|m?@`sk7Tl%F5|!zm!{I}N%uS+mAbW=YO{yQ)j zRFc|gVi=L&uFj)_MMG3=w|lK_lH$gUNkFodH5HtpnS^0sX|CDc7u8YRP;{zZ8`qZE zJz?05+Jqxx#1Xg>4Sz~qQ5ufz)DBi7?2$oPqQgy75r|cs?c!9Eak$JGBS0_&k$g&> zCWt7S^Zwe_T3)xYN-XzL zH!6uFbdQ-Wx6$|ri?mU!yAT}LYFBBsK7fHW4X<%?ZBz~vq(gL=V-Vgyp+1h3!CD(m z-)Xtl0d8v6n^Olcyu$7~RxqumXb!M4|lr zTiY?TMH)S)Sf@NVSd84dg5McX=B>7oOxrU| z+e1v*GHGWNz;hyh6caY>B*Oz##4lsyX+OFgzs4{F+P$PF!WsfQ z5tk)()f3gHGv1uvG!(g40qhQ)si6DAN-W+JoAVf7jrZW$Y8`DDtC~XnDXhs(I~s45 z=483QiyXD?)zGe0D?K#LqA0d40w~KoYP&oKjnLdyXJz`ST%Ri+0B^6=l2Vz7I&;{G z>Us`0lHoH(Q__l74qzInQ{od^G1nft!B$^FYo=G)UK%X943eUiusKA)#>zI@YP8}} zYo=0}tkt_RJ%I+ij5$9*=)TcrcKbX6$PLq$s5y(v^AZn9=IX3Wi9uj_PB~&_RTmjS zArmTwUvUCxfTp80GoM5p12l31oiAY-8uV(TuYrs=QVKhuukh?RW(KJYHva0bN(IfR z+8tHo-bN#ItJRoEZs9=R>4ulWD{ZD>74(Oc1qdq6HYXWk_lc3YR3FpHYj4~!`Q)E)i4Tx&e zhf7>IY;F>BZ2d{Wt5H@ z5 zx>VI-b?es0@p>fcm+STFFmp+xLkSdIrRf7!Si&a=N^qJ?vnU%=HZ5k*I9P(lrG*D; ztMx(4%Eiz&Ccwm}@lIAc)r2;x`D{6$I6xC7YRt^y%G#9PH4Mg*94gD9Ra3QZfaHPJ zP75ss52awsCk1L&DJYd&fP(x61F$Qk96YUv6;z5jE~^yUFaJ=Mvon%upE7NQAyf8| z`8F2lVhIy3Y;pLAH2vsz)I{CD)SQ*Ch!Z7aapUF2z7kGf9!FQj*Eku{;;pY*$7D|z zU%|{GZunZr0V~G9js;Sr3C$z~s=b)u?u|xTKS^F<=uK27%lMEYD+(=2r;F*07CKb0 zqfwAi3idh4%c&cvB&Kw*p9e3mv!zNm(SXj@@b>9m;ouT!XJsCUT?Q*<0_oRY=NdHS zec$x0q&-RHXhwS~?my&J0)xeBeHaBkJcPWYaLNtL6tngB^-Q<@PW2R%RH-zNLz4DF zw+xFzk}Tq)R3{@sk}iURk|+ZqSrSP#Z+*XcW?RhSh7haJ+J$L|nlrkhkqcTNPN5{= z)CmF_*vT|8qFrp?(nPnVJQycov?UW77-GrJp;CKJCetu+9aoX~SG_rB(P?vV7&N)1 zO3v$GFj1{rZ-5t4R>GN9e?WjQa~zN6kF`(oOtvr>#I{EDUR1`M7Js4}bb=&w^>=Jv zM~j1uR+p6=hp#?r$^<7G&T^u8m3EYzy67sJ^(ASO#iq%}(RdP+;i2pOFjBJ#FByut zkrrkzbYVC~Tn=t3a2>NTP5HbmvYcjTF!MKyDG_Z% zvEYjr7gu-U!5c}g$LrQ6295Xp^1&*4P-$i1PZ=jGr_sCEq-|nROXA<1IUOt>L{&(q zuq=inEHSfVRlSTd6ibXx#Wr*>9F0j zgnK2EhFPB*xUFk;E5;HrGP`wO^3gGDOCr*XC;4SeEg5@Yg2}x#^>gJ2A;~a=x$VTW zn>ECpfm(VA!fd&f%E`SlmE^NlEZ0===9A!68fNml2c)&_V(!+Xj^jg%eV9W`k=(mk zf)(=u&Bm0xaVEg{-7I#UTiGd9(i1_fgTmB_FcFAROW)bz%LayEXay3e1dxm@t&sG& z?>>DVj_Iz9QgcQvEE3>_8$RjhrSE}S?C4Yn6C`;=zh(L9>!fl@dt|ueOKlC(*_(Q+ zoGQK9Xa7dJ$foUrU5GgY#LRkQ`= z&G@8C%BPME$*t~OCKV`~5+BrH(pW|nYMB*8Fc;dDu2TvQc;piX?RX7rlr1PD{qM>zHMt+ zc}ssaLKP{Mx5cXi#+97SCYPJAR=4tNIae*+ytX+>ic8Nnz%~Gk72W9t<}ZBNJW;_5 zBWIy7JjdoZX&~KD-P#F!4mSQ#(<0|lAxUY0IC=yf!KrV z%2*5NjiVaxIT6DYmjI7Na=|p=&?L@bOf4G$G2q1vw%j`rtAaX!)oNBCq2Pi59R4$n zwQa{Jk2qC2{fsLvl5`xgm4439ag=3>j>8+1&w6%G3V}pS&!!=FJW2}E^E1dK5 zZ4-}L@<~7oK83jQOCnnADa7M(1(Rgaa!Dc1aY;nIn?kZY8chin22%>jYNG|2VsYi4 zLb5q&{3TelSQ7xwL1UXB_dnwtAXi{|hpDuG?r;++L%p^$1IbNYXlt^30;W09qU7+& z+h8ISckgHxa<`hML20tQq&fL|e4Z+jYg%Ewq%@nSXKj4~=iXC`lS+VzahvN=79R6Q{`U6Vae)j+V(f|9C_DfmBe}(=h@TE_` zU@KRzqW)L^{owya^%C_74uKz8tn%qqy=gVs5BFDeJph8gzSp6`BmIo%yNY*r!h9}le6!vCeK z#;L}CKd%1q=l}Naf8p!@_}jmXPw!6L2cYYQ2XH);c)WZ3@rSGb{(t}e z=fC~izf6w8`5D&t7g%=pF5>a^`^yu;ZUlYz2hj5?ZOao9nbT`@IM1JPyp;Yo>VRH& zu9u$Z8II$JpXb+oR1JCc?FE@-DE?_kvNsFdxi^4vJw#*f+Nh=$trmX%>5FFbpM6Y7 zy@7hOgEPMU6ALk)VnjRKq~nx?b^Cw`=zQSr6>4Wcy`zakd6H3p&Pt#@=nhpwSY3Y1 zm0crS2IKY!6%fl~IR;&26fE&iJ5>`8>)NR)%Xj`ri_i^L9n=%v{?nL5q=9#R#<>#~ zw_@8CfO#$9GfKDa&qvghDi|3~2IJ4!p-ccxEN;cnL*^&RFH8 z>~5x`jB#iir$=sYP=nk@zsCWlRBS~#OZj-0SVGs8{>xW;b`3Y2g|hQS-+_aYR| z%@`%{xL>?6Hc@b-z)UZh;%4(@tQpN|=jTpItf(xMFr6$V18%~h3zW?&&2sVI1nWA>c^q2=Z)m|fkWcOG z!Kc=}6-!*6Ht|1Uh9fZ{wUVfI~}0~ zzui83GFH!}`#eu49P8%FZDl9vWmZ#`wfS5=9?X_0h0lX6`VxiI=fg$2LM0&)oI%ou zcv=Mu>Q<>Mcz01kKcT?E3LAI{s_1b7e&+5@2RV>&{S60Iq)bz3}W`B5w<%IMcGgO6p zmyZ{}phI!I)9A{D;xH1niwC$}3;_*TKk!o)d5?qwZcd#arFgAs9c+0--h@VWi?@58 z8Qwnu7*u-8Ltpo7+mh$U3*D}j7r>gMK}M{Tn8smW><%vC=L4ES0aArWQi9)rb=}d8 zkyh=IQ=3kXUc@sdL#sY{Tbud0Fpa}PIJz%R=maK@1Po3!BhB^O#~7pXGvuVG zx6ClfB*I0|Lwp1%hiwn9Q57OpUr=4vStOi^XNR z=}r|Y44H(-KcERr=@1-D_nsER$^g(<2M*P}p_OXrPn-S;3r$54gBzYOc;UI>{gmW* zdQxG~0}JF3=VKhuu+t^Jv?n+ZkE+77W90FkKW;pYaHsJ}ef@OjvHmz?ph44&aNMzV+6BfMQDg=y zYxoqVGl8bbOB=HcOeFSPKA<3`*+|rxlH>Q#|PSIVtF3 zf{c^nN2Py#gYx!)grKhl?;C$Ap%qa+TVEjOsj_xnDqjMliumJuMwO6hOzMFLYNMn;eD@jL)?<^g5R(VW*L);u00bBSyH`uXh+S2;hl z;LevITBwsrqP9L7T~@?50gJp5VaXTa(fkR`Igo+#0X*(QFZsfAzKrV6^zdc85SB`g z9!HR8$dpNy8h7zMcqtPpn6vy)B>*E&)4hBbDPmO^GJMm4y)pnPGK@#fn&2JWEDt{@ zGX;ix%1n@Th--$q4#wLxlW&={nWjA6`Nqg5Om~pbmHt01u*nMUZsAOFS~Ee((6d^c z{DkvKPf5EjzUc{WWu>jKz|CVEbVWO)jtfX2CZT5OvS`{sjxirNl2k^8(l6{MT z7{bU}$sLL+;nv%*Ho$TI=!=2dQ)2SS^*9Fx!_!4szRVDH=O+8`F?t4~7>9Wo(Y_-rEQ%*MXGkld z3EYu3WDQPa0%$fEy)EjG?*6$;;NhJU!~*zfGz>p*ZcNiAng&U29zkE6)}7qo{$J8Gx5ghZgkhU z;|st*B@=0fTuAB>!e82Dz2S&GCo6juj&Wm4reSQ$Yoohh)CVA)PM&t6^p*4g*Km)% zPvCRR2S|Wq9E110Bp<<}Aoy&yLpGtw8!npN=f5E8=QnJV|^ev@G)N(n{Myi3}%3%1h&T zw+s=wG31&W=LD1*`F`v8-0F$2YK<4G2@nD>lI3#)&ETII%}5`A%H0RYJ;9JB+Zi=7 zDd-KhDC!(2goKXhohhp_*8OuvS;^e~0_tnw@a_`N#+N{R?L63Bweb zzlxjg?!c_oJB_D~hk>USr3KNk#!#k?6)hcp9hOd0;A@XT#o)nZ^^4&?`3H!{P}7^6 zi>nr1C}xn7Zc2{D)IJN01p2)796=!@0Ymz209+pN7LrV=$4ZDi^x^yPv%59`r_&^g zT;Ziba!qX)Q`*qW*>@fTY0gYqOWAdHYU2QcmKY3R@jJjQTB)MmGdByPQdjZ7siK4l zn~}hUJ;}WmF$GPg%8JWcLt&(Ng1mGKtVG&@X)B?}ZmtKl8pbjonS)A?*#RMo{r0fk z+@7$xQJ^rv!!@yx2XF(fjvLfIJwx5&G}9#=a{`|~nBvTjuuaUE%a1B0JGy;nUK|&| zWV)X{S>QS|y}A#|3#u(qPR^r2_&C6K1d?0MsF5<~#(#`WnYc4R7@5V_S*qJA6quvw z1v~_{3Y+I@#A1ZIIJ3hoB1C%WW9qxMByxhAF;Y^h@vuo_+rYOToK?RSESQS z6Ca2UCkCF~fg~Sb7wYi&539UWnVpx-HKB`HmCXGlxqZ0f`(`+&9qXdF%Moj||6cJ+ z-&Fys=LvGZzt% z`%8xYJM5V;G67gXS?D8WYbc$Lk%8cnQSwJ+6>qEJGALeHu0$`iF97Q`LI zs>yp_iuq#a&x_P(72GEd&h`()Qc^C3H5Y#!YvFIKC5gUo?0TlsdwTA+GiZkoxdvef zkh|0rmw8Hxbp4HzW;W&pZo@7lh`YO>dkQ4}o>UM6HUqJ@PB*@4DWUEO20h?Zg;J3qn zFd6XY=ez#}3-niR{ZXERH-~_kzt(A?(JdYCDIGRD{m0?QU>A`dYH&q$ZiOtuSc!oQ z-{~sDoRyhJCPN)W3fCEPUETyc!{!$`^GV$p;~DFNLVRe}pHqr>5RM)tKf%j$KmvX0QewhR9F{scqCTb z-jB)bUkCQ}1V{iQK?=J8XminD8Jk1)6-0tOSorSt8m+zPyI}hc-5(Zj)sXUVRHYX1 zi1<#eQZbB78odHEFvCfm;10*8Fm1`-BC#sjU_&%J-Z?%BWN13=s}&fXef=1MiMW~A zRxg@V)+o9@JAfj?`)Souxt(7K+UgksQCzKI7^n{Eid{4(@_F-i_K13m{}7b!?;o3ECsqV(PGFyPaNN@8NTGaFx^i z@NvxKPxBI0MBeL?o)22cvN<1SVc`_d278&~kV@ooi^=iJjmw;_Z9p zJS#&oh^Wb7f7axNknCKM5v6u4QNx{3zOcBAZBX)bOO*GS#q!E?Wwtv}<|8S`cJZai z2iUEI6}=|I4A?B{Rz1+LU9$^wiyAp(dImEn%rNJFIgP-UgcH-ahs{u*rAleeE-%=< z{rQF1;2z+?-9DbzPqQHr}B*PR!q=n-el0FeB_KBm7gYUjgc^!1#fe@#XC4-V>Q;2ZFVc$``$ zCKo0mF`8M(!zW0m#@T~pgEr6kbCciH^);gQF9!2wMFW;xWOcj{1UD;Nmf>$$9+(Df zlkqI+b$p42%8aX^ioWA#2vf0L^-tD-u`l_|(2iN?&WY2{#ra~udHaN`0h$qa44BhV zstB<007K}v*?9A9ZO;MnFf{N(#)PI_p}=8?JOKPILlR^-U5!SLLt;gEY_5z0+6?VO zO>DJu1FXoKa1kEn-CfV@k)2p4$m6W>31x$QkTPZ3S>MOPSMJUSH~SLcLm4N;{xOBj z%9}lc_VGLyjWkR!fhd7Y67ueAbfbsm3eX-8awWExkSlSt@zJ5z9R|pZst1^!5W#+$ z?|nO*@$wDtp8(UkPsg8;iiU%Gwl>`onr7frqK(HW8_LKQ;X#A|EnPQ)3fL$@K~7+h zCLobUqgh3SIv)CG(AL_OnlzFlVa|2Oh)oeq>xMGlZek$2dY7>I^scIpOwv_VCoi0( zNSl>5{lR@C?feBl`oQg_eI2&Eitl*bV~Z+q6RVep0)EQ;KE%SCcI4byN#LQ1iGE z%UNJ^Ohzw+i=6xP{?Rs3(?kA(C}rKp2~0WV(miTCAb|nVTz76=(-_H^>l!7*#Xb=x z9yZ>}{qYWm>{$m2o0jm#cp*YT_xN4&d|Z5|v5+*CkuB;fa=e%(b9yn%h((FpMGuam zb7#Cn)|yyE6hY|q%esEid%D^AV3Ki)NFHa!s@Ku`?IEykyw2n<{0tRb7A(hLyzPr? ziGO?Q2r9mg9ZyT@I*CttROJ9GX`S}GS_cJ`$lqAy*!fvZTc{QO*_Tha>7xAp>*Ik% zycYGdf1&C-R~e0y;M-!BG>3@l@~p6I~{pX+;0bI+(~Soxasal7~@6I|-3_V(yeA?kyBJKmNA!tUWsM zYSh4%Vyrb@@Tf3k*)Dk*-d6hz8V&twO=^0?tPdy-=S_T(EBsi)4++)UW z({@Kz#`gw?EEcQPO#v2U`9j>u#(Uq^5;{$e>Okzd6ch!tR*+}}wu&B)U<^mKAY^%xjnTQy%m7AG0z6sU zxRAb#h5m`rAf(m`Wg+p)4`&Ws9QGJF9Lgn$)J9;xmwiSgM2P-yUz47Unr^o*BsebC z;DvPHa8}1A%+4L6O?nEkPz}e5*glJ~2=3j(0d+f}SQLDrs26kB)K^GT+pExzX1V?| zF}*XB9Rb`hQ^O*ifUIuB!qqk9846U_IkJ^gm?~$8ha>9E_X|>Yfvxik1fr-}We-XA z?r>@1f_fqM@Yx;gEP_traG|9v>-Vgar&T9nX`1_YjzhmSN7n8fsN$?dU;$aj(9y3k zdCMAZ;a*$T@z^yqA+#gY2H$1qga(;z`RvL zpt41YC{_rVD;ikkP#&Agu~_xFn3L(i5FJw4j|!)}XdLTDwJDe3Qw&kFdk(8O>)pG% z)ZdtVE49;;7NiVe@<6WzGpj}Rd5zsu^)wRmsDCcbAEk7i(XXsXkjZ|CLMNu-(sB~D zxYWmt3N`2&kY+s+!q4yBw@R8~?x`NqClyApWb?0qkm8mts~{hJg$V=B;yV^qP4Ih> zK>xML9le^x@RA`|jWv#D#kGpawz5Mn&Kf>GBR5&ln4xL15PxHe0r%;ko}#=Q#6X3^ zLPLIpUGcK37CiCk1(@*yV8oe}2Gm$TKC@>j?yZfr^L4z1B98;I`gK~f4g9YCEGh@5 zFYx4yPLO z{PKE@!#VRhw7`?j6oNR(T^u`(QZAOD@wi2@d*_m)P}$V_PVK70#rZ?kso6;G`+h9g zqMb@~WQ(oQxNK2OwtaQNMbaDkxxIo0njX3Y0mx79z1xb=MX6rB5=V+650TbZlVDA;K0k(Fg-a$>bMpa}V-%e&jPgqlMeI~3ASl;uLW zab~S%<6(>jo7+=Hf{jks3LKT79mq>miw&)2A%8Rp!#1`XVMS0pD#tN#bbba=?<11n zU4Q{##l-uM)(SZ_4uqoY2hg<8+v27xTy8b~N@$wsBD1krryz&fNssyhK`rsV@)L@1 zkaSMKVyx}?7NiAMArgI5=a-@!lQWNchI)6H94BJ@TO?G}`ov+9jsdVCQf<}brK}n0 zt1y2>g>0OrKU91J=d;9fyEY9AvU?G3AJe$QJFE6bt_Q3>%iXG76xhEa4}$gCY| z26SG2-5!I5)98vI*5k4sn^{2?6F~6V>Zpoo01%?f2}(V&LO*|^=zAzga4I7r8I=<_FdE)(cB%`Kp9*(VcS z>?p2+E4_>qDu&7hIS+7{#Vu{@O$TPw&bZbQ49|xofc6U1-FlZE^zjsT$v0T^kGX4S z>}#R+Pw9N#{4p=x$0^k&5YGuclse>=aL8+ZVi@9KZP}uUAVh>Rq5b#NuU+pFr*M#r z1i{MMczI9}LDe)C@!;g6(!>Gp2z$=YZ-@Iz8q;BFn7aXH=j1ek0X-MZ8aQ`xFtkM5 zvnae1Yx2D);#@J(<|w)|te^Z$7Sq5iXR`G7^Z-W~kr!WWpsyvIB~M3mL_}5M!!<5T zDoicDjE)$XFFZQ2XCOXShWpw^u*Bb>C8DVTKJ`7qP;!9tf+9M6E^CnX!oR~|DzKrt zO+9UVB>_bv9Zg9yQpNMZh7;nN#aTB7W%{EK&v#1C&#Y4NJaDX}sML;BcRoJhWMa-T zz(fwBVT-kC*h)m@d09T+R$PCJJJN1zeK|a|GkcOF9ApC(lbf;8VOOA)@)L)w8v`tG zrGeuwb<3j7v@YNt)xmpZ%5PJZIm&%psD;u*C|GEEr&Ygg0@;birBR&hW%GtpO4?WM zsuq6o$52A%VC{{Pzm=81i)!d=5eH`cwpk3(dHx3fe?jIOOnH??*JIpCizXxU(HsdZ zPSD=n(W}h0bCM&|Cau_)*3no6Tkf~lC^ADmCK7UcvN&e4^m%3X;~l$9SN|;d9cgdu z^c0b5lKABvo3)5TVq%D&-){MOKqHX5*cpn^&?+U1@H!Ypm0f+5=LIZyVaUt<-Sw}d zI;50TJ5`5gP0VpFWVz{Z23Q4VGyq&JV)_8>h||wF#iMJ&Mo+WucgxOmN(9Sa(Bo^?9dOmf}eM|J>$NyUp-|mxP$&LuZX6SyBDEcbk=wyO0kwVY{jf1pQzoO zdt@}?aloP_{n1*=BuL4IY;iw6DjbBC?DG*5spqMdr_;sP5xKr8P-^B`Svp+i!b2Q= z5JAkawTL4XCu|B_<>4(xw3|zzbI^wxdS%-#mQSHIU}mS?$AShtkKNJ*P_dG>Cwx{) z9fJ?x8UND`lnO>xpm5~=b(LHVsAI9!x_91OnO3K-;2D4S^cCBiN1sLq4_eGGv1Yk- zhcN^U7)_=Tc=GRDKrDu2)-lCoe*cwWPd`e`!I|D@GGkPC1z;{TwA9!&a3l;tiQ>jx z+Ht%T($rpRf)Sg@@gu-WFR`$J^3pK5RiXHaT3ohVUWC~EevW+EPi%5Sn`{sR^#Pdr zFOl{WIn;Q$2ui$b{YKMUbJ@>5KXXU59a>v9&COEh?!;HjufHchsoQ2ht@m#z^jW{mzjY%8Y9V z*l`Cj(&s5>SbKu!6_uu^iRrdK{L}svVHyVnVz) zbTkKbr~*0I5n)ft$~??s$bo#qMcAxnn*9q6_<1A^0Yr?3j98w+8U$XFw+S}IQdMt@ z%bxyE$0NVcX3WfAvG_<%EDQr?rLE%eL%$lDC^jinOQ-|6Bsiqn(`D62P# zN0M&+W@nc1rBK8QvkMI^b?_0|31B!DY`53_XNODkqVc%;5XOsxB&O2|^e%SSw>t6- zo#<-;qDs=Quszak$SkDttn;6vO;zfcj<$?<@(m#x4#33rMiV=Q8MKIX`Jm!K>`DP-02AQ^u{y@ln)y$+pC1Vvj(@=Hk`pj zFlZ+hwqPe&(7oGz0XrPUnI_yp?xq}Mmyuyv(_#U#n*Bs`o_id*hgNNz&{yQDgx>Vw zS(g?hX{CMWc6+Q$)1!{*a{`?YrJ?eCq$75{~BHM9}jsT8ftqid4 z(#jw>eP}Ics=|dX{oX{SH1{lw2FtQ=kHeH{=^aetr65Xssbijd<{MrT_&_+ohnc^~ z1ud{)arv2ka74BdtW0aBe@^M8K)?{5`=_I&uQ$Gbc zZEF_(?ch+Jikyq{?7~h@-FclQD*E}J*II5%ExC?&7h}8VLY#sLW%f65&eg6;^Sd)SQnwm51_yO+rE+NLJ?8CokUc032 z^t!2K3t>doW=Z~OJQQCpX=ozml}D5<^IhmeoB-%=3kai({P8)H;T#9X*?Yj<9GLi- zu}mP8M8Jyc2ANBuVdVavJze&1IOnr)K-@nPS!D!3fDTh*>LC24&(fEF@<2#%0xnCB zym}dpzUuD8qg^d+UNp#58tTM^B6?K!5v`**BhS6@=k~r*Xv?oZrmdr2YLr?kN}c@h z28v(mmO)L&r{XVPrPqG-w67>N9sM@D#~n2EVW@(vn94m6A2e8z4b-@to(!sqTc}Om z85&e5sD}l3c(fc?u73bAsoO5j1m8}sOkZM^z>n@q6GiMMI(LSlxQaO5l%#_?r=z{YplyWC`7-ovy()aq&?vTZLAES!Lu08pg{|m1DsOQK zobFYIKLZP^ZIwXLKsC>u*PySiO+}SCRpT`#*363f3S}@DW?M!SpZ!iJU%U|*GxxEJ zE}00}x800-XvHCvXtN!ecB2zY{r*{*%H}pT%i=Hx7O;N_EEJgtQUC_}$@MTY*GVVb zEfbQ6uh@Z;HJ;9uKC!hy?9Ta!q0@BE7Wy;jQ6}Uj?R2&y@Kj7t{lJcNUBXf6_ti1% zb^}5|Z!s~b-*FaIcP3T-1%jF$@@Ec8xWcv@V-aYy`m_bm1-QLF-lPbZRj|*0@~=NI zP~wi;1aWHmu<4m?Y;XrZ)NgRek3%oO-$4L`--8YCva>wA1!DKSS)@|a5Zg;3 z84lG+rZ>?`3=fsia!O4EfMBYpM20WPlU-giw+W~VkCcf_*_CI>KjlZp!El~Hpztc{ zih(R;hDs6voQ)F(pAH|sS6w7`?JilRD7U&mF^FCHTe)&*5;}XMD}3TbpQ(3$;j7cj z{kvXP=B3W!ILSQ{5jQrN0yOvYG2&#ktI&f!X2JYV9 zvKG&02i*z)@TIIJ_vCRg#Qoww3|Wz;z&Jr6)&a%NMNcKr>jAa*H$)lTM>;9(Z;V^d ziFM?QZ4KRpbK|>AFeftfh280t6B$FoVLHQexI3swqfhjD{m5Tki_j-nyh+jN@VQ7s zVZPHh9Op{D>%7aZ&3m<^G4K5nN_>uWbi~Hpp7fFP<~XuQ=|G3C_zU}G+BqcrfK9FA z%kw9c3A9Zu)nxHv2;0wcQ3Yml80P@DVG|rt z@D=Iat-6_`g1MGaji=c6v-t?*j3s>l7

u3Df;J5(}vR)g^I+Yp4w>i{N42>w@hmHsz!{JLPL zu7cBnpN(rf{&lGm+gdD%J`*)w3esJPu_fheX}X@Y$@7-ZGfU${{N8g?#AuOn6Q!`7 zg#{eLvE>crN&Sxf?*;k>m3Ht0tA)pC9JTLJJDrv1bo$luBZa;58aJ+XjOJs=I;|Q+ z%TpmAN|Yf3hvVVtP&zY_x9I>ZZea;?Rt+$(c-Y0Vpws)0t!cpbIJWZ%qW5bYE+4$v zHnClE`$P^1FcpW{!qPZDaBZnWilV3qt!c}J*CxTIEb3`ol2cW|6d=KWnGzD4!b*}< z+!qQ)oR43->Ji_M(?|uWEZ%AtES`QnLF4z6N-;LlBgem2UT|Kbd&eP^1FA}VU9S@sBy8?3;IPig5r7Cd!t)xpk=wqAEf`OuEJy#w~lW6A9D7i;r z+uC2jG$k}&3wl}r|G;zaeKbfE){qj_K{V`o5BH#nzhd(}9Dn{PZAIYS3c`wAjtApZ zpnK}mt^f~K5x{tzjDr+mwDs+v+ukQ`Xzxc-9K752S2C>6GaN&3NbED_XmU@!J|cg% zY32#_Y^S5D!_#o!hS{}3(9yl~3E#@#@C4?><_GNRkggXA zU-j?UP{_}G!i;+@os};zEmJ+_U?yW&?nPF-8hG$a7^Rd!AZhdp)8;yE-JMUZjGHB} zzlK8UaDM)NeOa}#$So_wJ0>~uX|(;FNEsys<1__3w&4~zM3=9xFK_SJGFuB`&R|-N z9D1v4T+OyV(yg3{0^sD_V-K-5y`doFhAtw<%y7vcGG`Q)C2AQyq$Kaf=>;`?lVB6c3n#| zacd>+F9)_TwwK-};t3U~71aeP9X>gPkaeUxfp+1@BEuFPSukA6@FrkH4nVZD?A5kE z1m|0&sn0da;3_<$n8Z7MOcVL16h%Yv#w@?^QhEHp!83%u^zk-#Pwf?gs{*Me0&%+1 zv8Er*n8f?yXDC&KHZ(*~gllZm#%x#N`M6>}B=nS}Uety7xa^HJ<1CQ8!P{778P<#i zO#LD_iltDrS?NJQ;`ZQ#gC~t}FXB7=`?yNG$R$qZOif^Pa_AYJn^GrCui%YRcOSSL z7IoBgI*lv3kyv%dTCb0NTa2f6x-nQ;v*m$Pz4{|9*jo9{m~z6x5lOx!)#PNK4p9um z7RfhHj~~Y8^h8-fBRQ={u~QO78~FU-`-y`!spse7nS-h(OW>tOoCxpVj%$(OXK!Id zo#Ay3JQR-oJRAym>D>@)OG%ZTKAkwsN^?t)7i{a2W>O}#P}3!^nBeyH*Qm@dEskSN zwfN!|iG=sV?&ZXoq~ZO0WrL&fWAdo4B(y@zXlaAIbw-Xhfu@b8JkkG z(nP<^JKi9^AY7vLN-I_O27_|r&`Ss8LIs+}`fP>l4nhp1lMmv73#bPKna=97aoHn6 z`E7M%hTgeeQn`(~ilCk8K@)9+WEJ_0F^@qPEp0>?1*zEs@ao_G@i*wR$dmo!Z~uZM z{NW$hH^*^*vA8=@{>#YJ|2V$m*Ch_jjkDf`nJs8MM$ovy*DKE2tYNuPh}16SvXyzL zK0R3SpKZNto9HFM3*3Q!D|%k&n`d{&cE6kwX!7qlC1v_?WBc#=uot61Vg6&g2#R`Z z-XAQbZ{(2E?fcOLH28&*tB$a;sMgoSF2|;n7ns#cDb>G}Y&JS7J+bf^b)njuZPW~{ zcbv1wtLDy)J)6~?>#Sfbg)^8#TL)W-gLE)t@=L`cbsxjoK{Mm;i!|#qyU<3~HvvFU z7k0{Bq~V2`5emB!kP3Bh?smw5-vVY%*JazW$pH(d%Y8sE=k(_>-^*)S)mZfKnY9t` zAGkjagv4tT59nqxV4xhlDjdRK)z1QC_!B^6UylhoF|zmh@kceQVV9Ud@BDzmrbnD) ze&Xfr%BX(jE%bvA3LrSWj2L@7nN$Cv(8de?sUSfoinv@+NXSS7;ytjoJ54S=Q`KSd zVr&es_d=wUE}Qq^_$P$_j)AfmR07xqdo_JHxhOTP!nlERZ0GEl3~%G4i~IO(COelf z<>q2E30S%59TS07OO$cEv;LdDhCttia<_G*Fy=Q8C2T5!#pHP?qtMQ3dXQM8W`OjV z1Mehn$BYF}>Sz!ta+moj{K(+>!2wp=Z`}!OZ zs0}+!P?U{{gj$HUh)hZt`(23Ji`%r>QDTZgy{xY9{&|M3Ejfr_+8v}G*RP1>#HaV4 zfLBOI(oZ~vw}HhdqUr1BcRV+?82acXxV~=FK+EPdO-MiR-1YLTQI$^5rHrLk0{+}= zp8FW--|UJG`Ms_jctzR3x-2}kjp2uy0m%)dv3GR(_4#%^Bg~v5Tzg~yFc2kLclQ*| zP@oPzYi!|wQ?kVNBfBj6kdp@kPlab(4a~(_NYL;JmdUNEm11?{C_N;*5gWj+?9MTV7oN^V;dM1$EG|9 z%Pc4QAR6fq_<+g{U6+G^QKR^WtLGooyWuc;H_%6I2OLf(bb%md0sU}9!CAOtkv~O3 z#&REHBl6=)LOp@(m; zZ$W=w$~30WtHUss^ojQ+ZL#BgNi)Na;n7qt=|IA0poHAh|A5qgb~uLK8aozYS9qOK zzD_V8)yB5zoZRCa!R_F3wqKn+-dgOE!*Tg=X5P?p)}g00AAAmjEraj_h>f5^a{ zQUp)g%6(RSQSsgeM5fy>`F3`694ZdT=ozj} z?V_(0C?^8Wa)NrL7EKTyS7iHeFEADsM~;Z9;#(^6OK-+&@|( zJ0LSm^PJZPPj)sm*=!!{5#<}iQ-N?qfYm$m=O8wXStWS+a`dTO7AB^bzmlK?{D`~T z^~8Zvcx3B!)|Fgx2~YrB;Fs3dsT272xh(xGM^xn``9Rxis}R`xl+w+GlC$0!`6I`K zKDKltU1>~4)tt?1ywLRuojRso{KOJYT^Bzla1@7-kf6`T_!v2!J4~5JZ@Z5Un7=ga z(~KYp-4XZSE>RJ|x~KoU7+HmDcEwrZl(xxmV74otBpEiBB8ZJCSSvZebqq@@*P%B7 z(pY1YHT+*EU+Co#wU+MfS*uw(EVkHa2c^H1EX(WD z>H5o`uAV^B&{}~cv|w!>4wj(pj_;(e<5QM5GelR92Ay;12u@BwOYzfr*-(;dMP-N1 z!qjU(S3V6U37W#^1ZO2hI21}XYK2nJ0o$B^@(z+6Jbt5y+RvPn$RGLx+iSnPzA&hb zE7JRHQCqO+Utg6aY&S&Zb1^uMGPGrRUPw!HBUMN`cJo@ABo^5epm{=l2r3ciif?1e z&_%w3j9t^BDB_{FQw$z9FM1+>=3iqq0Tjw6miyHeouW@ox*TPV@N%NKFrU*Wd73U{uT<>DFrLRg~j)@bIG`xh&qLfx$QHvY-6ihNg;+FsP;brI4wz%w=g-(&v z?U)PvXm7NWiRd}o4SN1EY7%e_R1;C3Pw$T6uaMjCoo*I{yen=~VrJ5Ce~-`wHnyh3 z3xObFy$Y!=JqrzhDX!5r#PiDMRs0y)y;x+I!cgY3gja1oSstptaJuCFB#a+8U>Wk) z#{(~jNNy1dt;o=U!xb8gb7L?QfKb3*tvK_!j1D__Z}y$s$N-vBJlO-F(Yw=21XeVG z{dIz=QYDxO$%}C-pnn;UUS2b63;>$x-#RjQDbAJljYeQ>f*HmR>LgC`;kov-V*_Zn zqiqbvHVfddqHC==6SUe0Vv;_>9#=@u3_quwYE!ic>xwkH=5lkC)fKcA6xId~l2vNq zY`TIuL$KzX$r4Hps9p3W#=2+MDqIy)c&@2xU_8^wT;PQs(|*kj-Y9!Fz_yo_+*E%% zBmf0L?=RfgSmYqyu>9ikSgmR-;pcKdB^$-#D<_3K_{;8LU@5vPY{FAia@NKvUD`&{ zX4yW8_s;K+i`N*#mFYgoIg2n8;3hPF0;7hW z@hxaqyg_RQ5C1cWq0Mn&31a{~YfGwi#D!*tvQJWl9{1uMnmtX5PYs4d^Q>cF$i)sG) z1aO0h>1;&@0BOb;FU8>{z{cVuHBiRlSM4!gVO; zK71=alRvU=&N$sLHXe5+BV6T~4I|4PZDnwrxt#k;l=0z}a{m;7#sa}MULn9=!4S=t zVu#l0w&}oGD8=R)wufNuOcL`+T;Ya^;zhj^_SXURCb(0R2S@2=1HY_T#q;x;70&qe z0CK3tQOeK>b2)oDV8!>E110Bdn6uk!HD^2?DPDcA=)jP1dKQN}Lb#bfcH+>AV(uu(h#-&HaDA;$~j zree(9A+HhZs)Cw{E7~w%^*zz9+lYPb`b4efvg?ClrXBJf{`8+NB`?Q|;YVG15vNe1 zb&7)=+qfNM@-}qPqAj$3UHLJ0xZ!nXOD|0pLfjm`vF}+T`ovx7C6Vg765+u9Iygx; zz>m5%2jBRU-di=BxRC*H8e~H5x^!DYQwT!TD@xTyw1k@=4uw!OS`2TLo1FS6eqF9cqU-#-okd#hi{rljVL(LDZw7Z2x3*>`yeLAT`mrWc2>4RwqV)!qEg%iNh7 z+_Zxj>XGz3mx?2Q&7GJ>=1o@FRaZZyD3{0IP(`$u2aZOKHO2}!xiNqUW@Sf?ay?!z z_)rn!rZ!K6QggI2m^meg;czP~s%MsZ6I#%^M^nK1h#uk*yOim9<>IEkf(z7>8Cf7e zuR@`2l38J^+etUUT&eeH{@j%*@05qOpBhThwhrfO&vS6D92FfF%=h^-P)R;8a<@I2nfym)KOhG(|E=6^f=$x&3gY+ND%MRuOzN@R-Z`W72VMJZkc4__CHGBHVYYD!>`^{zXtQuzc zHqUN~WGSMp<^J*{SZgy5?FAk)yoFQ+54FnQS|rY?Mup!ZY{I4Wj@Z;OrOw!taF_{n zXKEFD>LN9mukzQRMB=_WR@_%jZ5-02S&*fw$74HVrp_D;Uzk#dbyAo36NB9kICR+U z5d@c3KPefOco{0F65=5sYOF#w*100=q$7U5expn1d(Q<>I`^?_aA}gBX${hsLFfsw zB0LB1@ZQcvRCI$g5y+;rk^OgPrV<4qcO_&ADrLgZk-0>wJZ#LJ=fSWA>g@ZPaL33Z z2h0{??t~=lFeCvguAIS%ub>>eza=jO6var8$9b$Xets}_qa(!9t;Kl^oOiEgvzKll z%4gkepYZXJ5U|WZ3j_96a~hEEnpPAXa(lao4}D*534L6a$|TE$s%EPASYR0%O%M^P z&rie+mNs1CFjuaphx2_(w2!Fo@-8M2btqmIXO8BTc)*>#cLej-u+qDv5oJ<3pGg&@ zeCmm6O5I%cfguMrS7(PLx#z~o0y3ZQAWLm;Zw$_B&kktKhB9Jq1rg(xl}%n113w?= zXylLL4(~&_Os6ljUx7#c8=Q7Wx1&8Rz{A6}E7ar0Kz_h}3UG0HJx(Acx6xdRL!9hR zt-gfPTp(c_If(qpc>&D+>75Ond}z(7y}MEEgKuX0fa)QfULl=PKtlH9p|=>KGL))< zrZ7TyJo2uFf<u^Ww7aWY zfE8cx6$=}#jMe+1jGa`)UlY{ObS>}s-9CGLmGziM(lV5Bsh6lk7@f-V3qnTH`)SD3 z?}PqQ$K3?;qr3|-zu6r6!c0SbbmdV*003C#uWlZf=XS;|g`_;WrmNE-t5D6WSus>rl_(Ng!5p^L(p?zk%vSam^V}i7 za0bbBT(__}90z4iVTmWL9RubiDp-SICr-pcs7rhl@SqP=# zl7)oswXsJipri6@wzYhZk`3#jcPXxC#73!eLIegBMYhyh9W*YTNWE8{vUdiZwZ#As zYPQmv$VzS5ynqz5g=v;7s!{o;3)LLOm=^|#HAbCg&S#_D>z4KA-oz(On^^0@i5Vf| zaf$&W)Jhpgk;4Q-9n~a5XY``ri6Yj;w~(r!+&XZk31M-@@#S5@bA);0 z;1w5)3s4I0<^GGY6p&*HQ#vB7v{W}hhmAUl)z{X?xyv$*4kks6nD`w9AM{Iu{O(Z1Z< zJFlQhAqd{mPVM;y{8V#6@w8mryK)@%uWDV_FdfbFoY$tzSR|8`&*v#B@`*Wn59m*o zXB$C9-CPnmWn9&VWae!i-z=(dFm#X@-T0WZ@f*(^kRZ*L>kD%;W!hSf1xf&_@5_P1 zPKf&az+gPjKC+InC=2tmhNmko;vVgj&hRHhs`%7~UWHAwX|uF8ooSc)XN*f#oHIIZtfx5C8-Yks_^?U-8vZmpHXlrt-PKwTY9zu_Ry>6e9#Yz$T|jx&xiS_`;%t0?Ls0-t_= zL5-@*T693LCXf*SUQCzxcfp`bF~|jC0I`iyTF8Y1;~j0KC~Z>b#bLv&!E>eGZXd{* zBZ9Zx2Bqi+3kdf(*C9|x={A1vOyaj+2ag08v@#GEkT)c&V0?ReOp|}!+&neU*B}R= z%}B8q!3?}|JAH>L!t)>G#o%P0c!k z#?_oWN;{yjN;*@+(U06^7iIz*1?kdwd)4*ug#hqEq%>PSF2%4@rMR=8*-X_hbyB)s z#V*`0+%g4)0@u#YUTBX8o{dJHupW?nSQy zp_j;9O+{4A8E-byzr5uvx}xXnZayJwT>B$p4XN7xw2@BKkllDw`?s=m)_j2KO2{8~@FPNu6GcjyCPBw8Bk|)?SMB+2SAIpA`i# zIy|R-5gxPo#TFiNiqAxgKYeWHmx&NOi0MaZejiY?`F-uH2GrBihj&G?n^vr9s#c?a}RRF(QL)w)HbzkS0S$ELd5ye zn)xMQp$Q(4zMzK0e?lV%TJ@nyGxIb3=DxmBJM^=3I$m^raU0*K!aO$pO3w3L8t&Gdx=y)zx6WJ5>ZPiLGQ$xmYM^2k=#9RlI| z@VW7NFwDlv8Z*%CXly=AYIEuUz<5A2>SwhB0(0xU_dTJF7o6Fl4kn+I6{Ac#;by1D zTb%#d)%UZpP0SE1XD2AL_Bu1kYa57w13~~e;OF3v)%^I#bT>Ar)BPv~|Aw^>>~7Q0 z=A$q-0QvmZ_!BN9YA>-nNNMaen{1lj$Gx#>k&@tlkk z)iSVxq9wWpbZt+EyxhW!v<^_a zjz{7KUoM3sed2+D5|jlMSt*elAt5GK`y4mRQkO%Il;baj@ei>la~+v-Dv`g@spcKrk4Y zXzUI`N@ii8Gxddz8Z5j|u{fc-M>80?jc{uzw^&E@potPG(~N7lrbH{G_w`-35_l zAdYc^)PC*){U#PB)4`w=gsW8L`WbyNS#-my`rsVn?ChkHY%nN$d_(D)ve+q90j16q z*3v+Jh0PGv{6eTv9f)mztmx(JhYF0q2hCTIl^ zbKa*IVj3i;+GX_33L)*25?-W?4wZx~wYonwJLQMXJ7?=rm_3jsk*_E0>OM=l9(-J? zg;4=t3p+zMXT@U<%{({GoZnODbZ~ryyv4&G&{?24uXm03qgjf(!F95j(;r)z{$*gD zNvdfZXx*l|T#t||5>6luRXreVF83utFma4-EUR!nj>BdEfeR+NMOn7D5@^QBI8+fP`B zx9jtH{5|7qZZL*C`U7l5JuU~roY~yZYrGr_7vSH68s28 zYC-k(>z%QfqWQc<=EBL%M`!rVdM#3@+xQf(>G1aUl69Tjk0Q0^?O(Q^3Whc);f628 z%pJVgPB7pC=TM8eiy9KZ-9%9L+n8fK@Ei=NplMa=i_FZ;a$d@)_J9ooBsn@CxJV3F z&niAJWWdzq7hs2T5zn#pT_arD7Q4G@>$}LfN>|v*$v(`*u~SJ1IWWX7 z1Qi~_?3ZIPH7W)n@&6lb9fdtA>xeM{VaIgeK1^l_0t^wFn5mDB7=5a6h(Z$&P|I*A zHGW-ekfO2Pgt^ZE?(iA>cS#S@$oHr^fVZ2-;{&{HIUj$y5g4d54(P}*2Et0#u){Sp zP_S>GA$mopqpl7$>ha)=QqQsK1kVmWiuL8Q#8_G-yboZMWEDFwna+QCY863geEpHR z?rcL8LL)f?ylei7e6RH^0l;WFy$V&0jXE=ED28(V=P+QQJ}#+G!|Z#cKti6>n<-q| z_|eB4sjM!y8mX0vm1WS4n*-ckW4BDCqermiMZ)Ys_@m^gvmB`={7eBLKlD4`<3m6+ z6d?32`8^gV@ITJSWzdwG$<)qB?Gv8$3ZgqVbmI-<%Xx;r4|7K)93RGr5GuHcq~yB9 z;~h`XY|`^xEIsWVY3m(GsEQ&)_@5jjU4YyUt_*N$zf+;Oy05rz5{~QCmG1s{MkC|X z3r_n(S+f9(QxyrKUaz7Tb3xUy{Q`NauideUxzJc^|I9WZT(kKrj2oX%MIFyZ9vN-4no z?Oab_#VtaIZ;yQfhs1TlH7=_XGu3_*^hT_f&M?`<8Ct%wpCda}BD8$rPOTV3?`jS2 zU?1r6uB@)7SW6wa)Ytn*_!Bc?)o@Dvrkor@GuYu6|J7Hi`#y0GES6GA5x5lyz?#>d zVpjI$)~!mTEAwGR%>>oR@<^5!A&e&||Cyq5ycDXeo<$lT!2ULWZ_n|M&$&~B6~x!$ z;^*>PlNFLS9L!52r_D>;X!$C#=vpT3B~LjB3*OJg6nM&JoR1aI({KV|(d==0Di8pF z9zF2|NX|h5?-h_D9pk2Y$p!HvT+BG5sHcDWLozfiIMOS{6uEf0r~N|t zkwe$N6_Y~EK0j55%GpK%+znfC0CwJ7c524k-)eG9*UV}rrsbNAYY++zXWmnoqG6(_ z#0w-52%6Hv9eU>tSfVce*-|N?prbZ%G7k}Qw+&taWUN=qGZzK5g+vI3(5YLwy4)am ze7;Xd$dRD6uo<6{3%Q;UBt4g(pAZqfC%bLImqm^fk9v}#pUi}xg5rW!IbymDLCs(o zz}$>H6(gpUZ!TL84R`-iy=g1@nMwe?3DDP~_r*m}}x#X6-fs zxN1S?cgciIM!?bU^fh_@ustapw1-c>d?#@!Dc*u!}f*(hj@f+OQvR+!jg+o3npqxh3MgzhD1- z%wDuRjL^eew^bhdQMjDw|NG0|{?8R_y${dwjq4=5pg$0?Wjm3}6sbC)L!YhMGH@7;>zKq~->rs@;qg3$zxDnn-bE`R!;$C5$1V_oxMdm*uM)9DX-FSzu!+(Q-!$&AV}a zum?vAoGkebA5M(QK)6Ij`rZN{-^3_Vgh4TjZANb#z_?gBBMq>0?26)EE*x8P-U1$x z@21_$MavX}QfrbQX7KI9$E%y`=U=9F<#Z9wPf;Jp(FxW)3RPPCYsXBsr3t~!N4K-9 zqZEP{un)&^#@bLl#1U#%?x=}c`>h}jP!6z%Bse)_c+o{7(O1Zx#a4@a`4SLbote}r zgb!mFd4VR*0~q(#eKH*Yc4>jyk@tsU*Ix#-`VKE3_&8yE5mh><4V*3+6tsajA6S>I zT#@EoTSf1vhJra>MqL0gA{Kw=r2?%=meixRZS|^ps&7!1HC1KnhpAf z;X(+dg3AJ_A*i}{%}Zps0iP8m^c70Gfwk))@dCRdyLL5tNR`#lmKE3igqJrf5&5F6 zqSoF}SKXHxsK#^o>cjMQjaJ#OlKmpHP77kQY-j~0@pDhP#1uFnx=95)*{Ss|B$l}{ zu*n(evNzo4O}2EO2AXyB#7{;-ur=;lT2Xv@JwDbEq z+s{9xcLHP-4>*i6VsQxPBLd`{CImCLPjf{f>lp2n$87GA4F2-SrhlFp&Q--=DA+1= zLubP2`WPx7EEZJH$--H7)X^zG3cQE%#^b#t+wtU4KxxEd{wGS~uitZrFZ4;#A?=7|CJV7?IJx*+{FE@$XyQPh@b;X31V`e_O@bONmv!@MJGd4Y zr_;UelLw?4-4cp;KhUxBVncr5qR7@3A%&$y?H3}kmv^+j{oxBL671^G7?SQwCX(=L zOwaHy_QT2qtkX#y;18VjLpUO+uJnTb1HH8L;5|%ht3pjcVK0wefJF5NDF-BPtIK^7h)oylyh3w~_AGxr3sY>a==z|(N!I;xrc5d=B z|LX&N<9BLJ-{=vjQS^6b9_q4^jZ}>s71$AqaFA1>Y>#FfT*Ehpw6gFHa8WF5>+BCF zZ|K5ua2Z)6th{TOkW^_70ZGHM@J26o2Mbp)n18rW@M{M!bs1@5IsCGPAlOsqhs)21|*at<)Au-gXfKAYYPV z4#38t$es@;$gF!q7iw6$_5+34&0L4I2@5@_+^w2kgepS3mh$N! z_yE*YvNZWUh!jYIOt^L`0cxNmR7#&u#JGPy(d{-B)X3!B6Z{+a?d1%D&Lk)ge}B!5 z&y;=17}DIh!-00lzZR50u0si;29zD730HAbZ-1$iB;a$W|K;_M6`b#$rO_=^zhPUl z!wIzxI4y-RG_M7&xlU&!Z~+eTa{bzC=aCSmq1nohme>TT({b_^YOof>yl@eDM}y-E zD%#jW+pB?ayL9N``t&e7sJ7{B)$!Q*dqnpfhmWL#%Ue@c!4Qou^w3w$B5ndJK`1($ zdO$pxxLnFv`$ssAb7CBg%my7e6?7!lHI{XeHGf8pFb;p{Mh4rTc5ov!*zSfy<9@cp zNj5Er@Egi%yWa5ai#~Lqc}CuEKfOKUJVf)e?$uvnV1+2<_QVMnJYD_saZ%plZa}pS zg~KgDAdkBNCqy?QF^|<0g+i5*@AS1AbyVQ9c!)=~oo(`A2!&~0ap2=QgyN%s&%T1; zv_L1x=`Tk*5P}p4RYDA2$<-HrT=a|3!0{tu;jRV){@ zAY@&_9jq={Go$2*u}%|xg~`~2wX8pQ8Yi4u84Xyaq*0c}0d-b7|NC3LVB6P@iwjrU zVWs?;sE8Dp&(nS0`F?YJ8k~}Tz(oV=EB1Uwugj!Pz_QOwYj^aK8Yq(aPGPSr_KolB z*I&aqi9tCJqs*3|(QR?oNutl)A%0;95}X!-1Ds(4APbtjOrjp^lKL6vj}zUsrH;=V zql#xNMT_^%B#zD<=oM7XFvTaZ$GsxLnby|;wi+4C?IJVX+_dYy;4l&(J_l(e)A9=2 zk}*#e8~70LmwyAFA{FzSkI)d+75Wvm$U~+~FxzD;=X!Z2+d0cF>`CgoEIx7(I=3_4f7Kt~q$Z!Li;B zUy6r-w)*yHuUa5r$KP@(KdpMPSf)hQE$LVK|;s#VWq>450R@)Ib9^cZm* ztH1AIhzj^>QeTWi6?{qYiw)EudM^?%#>$^3u^opNPmrB00>~HWY)m_aj{Y!>j!x=1 zZHBWl_bPM%1_~ORE#uLf!t}AfnZ)HTKlB^CGxhG^yJOvXKEnHHC!Ab5^t_$Z->HOR zY4R%t+_>GqWR6#}1C)_OFYVdo=ngE_!HBiM)1wi5 z5J0-l*l1#O)3Ec&c-vF$90jBgw@9v5^tehOoC?)^;LR|TzFJfEc3QIeZ(a*`O{6;gfKM ztP--ChE#aN?u^XD^r1O$4>;R(I3myTRt_L`Wlw*mGk$}cdSqCGaYv^wfcqlEqnTzf zpSHu#&+CRbc-7I0zarNIHo#`<^Zb0vaqb>u-4_CXvBW~D+2Agsi8qz8 zATrgD<1pNzDiDTO+>B@CSDR}>^c5U=w{D&2nX?(Vu~aeZ0Q3Hyi6#xPU$lmzqh zK$~r#FY!TuDan|)ILoszTF9y@(RJ%owK}_1)Xs1|a!5kw3u-Xp`24h>*d$b84>v(_ zRlekrl+q4OT}0z^)Bw+(daOs}_}KImIZwycE@hu|{+X%jgm;*ql$!)rbATgxheMil zhwurydCFoJ*r||n7V>bt+|PqDwTfcPye|KZEwsZjsjD&%o6ppSBjr)F`UcU8=hN_~ zUA$>_A!vY^!|tPJr{m>g8Sn2bAbqpbR1f6stjg13771kQ<;m6=VaWRNivR+briW< zN4`B$hv`AbCtA4O9I!(zOx~Z6KLSVul&dB;gCI->Oj~#0 zeEDSV|BwonLHM{{0rUKf_A4jOPu2GP&4d7}V=NTc>)wKFn~;nl@lD&G2n>Vw$CQ){2feA#aJ432B#J2J zNV0cF$WN*iA~0t@Z#q{5D=A!7S3b2nuTw|(Bgy;7%=nKBclYF$zjgml+eO=~ip+8Y zN9F>jpvDf>d<#_t3pLF4I2svU=k+)7msxxB$IJ3V%n>wOQmZ`kMYt42$%vI>8dE5p zpg+rJ=vedktVqUFizhpMg!1~&=N93V-0^{D+sGUN5Uto$!qXUriYFbDU_pp>W5J*U z>nH#sXq)^;-nwTHl*~0Ix1-cxN{bS9du4N&-LVDmOO;(nLQ&QJI)Qk6zB_zd_b?U~ z8$N%4sqJnodE+gbw)t^BXlQ6?0@+ns9-X0DvUL1^0h9f^<0_c0G#m7-gid zNAxl?hCII9z+d8KCJ)nL`okNlgezW}t!a)@SSXcacWe`xaKIZ|7Pmb87}mEUD|2^H z=yr3yM_#hdPE_+;>As6TpQ>P9#W;(CvyagZ1C%Z*huN6Z!y}3deyBCKz%kDI)?EOg zgL3CBYLbv*3U$ycj+gU2rW(6A9Ve#ZAl(?VvKT8)!cQ6k@Rmv|=ao&wUbQ}Fa|3Y> zs8%*=Jw|1c>F^9(j{M2sG+MmaYT=I#I6bNx9SG0g1hPEBq6XuAkeEjX%h3yek|C!Z z&iwWH?(JpW4QUc-hes%pm4Sh&u>}KSRL00knjio8^MiSg9*nC7`sVsQFZm)PQ16OO z()I+lL3!*qaEZC}GaMFt@~!@69E@wIcsM#*W#Jo|1B4veAKbaj~mdRA*26{?LaiUW+hk4V>o4cACX+F1VyV7hSF~McYJPl z$M$%xJU+dDgiZzlv7Wwq^Da?7RXa0=o*X)FU2cD65}L+5B<;pj^mpP)Yp$B@)^DC3 zKZv+7kJ(o|{>&Y(9s1nWV1|s~_%eSlzQ%}y(cJt3UW*|s)W}#diO|YH;zKYauO4+4 zbwzGP9U;FFASPAGcJQ1IAZG!W{Can|IU^wb2EKg&^B<{mH$($o(L!$%6gXyycdcU_ z*A0061^KMLQiuJ?%6fL_6_+6^3m&|9nqp&eoe8;G*l(W62MUHfwWD{58{=qP&@$&T z3ri!_i3K*6ro8ssIjOR8*v4{jt+RV!zGK3M;Bk_*33t?pl}j+drTrdWna;yjorGp@ z>fKwuHU~2NkkLV{EZt7}FM1%FG2pgD6b3^hFK=j4V5uupc;|6JC)p3x?w<}HMhb!W z!(mG7&+NFuy$tY;PEm9`B6x;kS)d?_*Widy0N7Yh%u|)iEY+^QhR=DSE(vzrC2-XE3C6+%7S!5Zp4LY2OWoQSIH%%Unf8M4O<3BC`BH; zDQTuIhV-`x?sW3myxhvv(4xqjF4GI{mnUp1!$u8$3B1C&mg2404BDGIHwDMI&12<6 zoyp*}H8RYfT~|H+w&h&2wBW+xv+ic(-4iu?Z8|S1o!UaMN00m(g4E_K7TjPPdro$j zrzQSQ1eNDjthOvhbhUy=9h3k9DE_rQ0r#vYy+v{mnHwA6JkEvzjYEyUZm@zHi*=lN z!l=n%eWX&_&y~vAfumm7_6NmeS~%YQoy~E0e!vX0 z+u}r!&Cd^;93JV#GXS+*rpvUs(+C@uRn!g{DN#-P!KK+5UQ;vDGTl7cu7mGqVN4Rz zDh23F_cL@)FL#gkIsiVBK!MB7P0nu#NM{G)2Iq&8d;IG#pziP1Uv&~Gc~IoRYmxLQ zs<}gwh8{YlnIHxnOx+|NfeLR{Sbw#19Ql@jM0{VSBqLD~k1AROW5D4%)u#@zI!s1{ z-SxUD_o+yf*nA$0t<7>Zkp+*s01PW~70>J|b_y%cCfs zA+VANJB8ZM{`lfU?*)H3M~0SD-X#*5!Hc#dJG4uMjqf+*CMG(3_FtZ*oqJ* z5>9V^{vXRUA-o2*4{zwGlk1Lm)z>F~CK|aDivu7tl-g_kw!0ERkn&c(73~_2b^7~9 zEO*#;sFO%dqGJ<6Vd!pT?jRY7<9%Tc8h48Wurt!>=)zmeT{}3>F9=dASzUb6RFB*- zKPYkdgA0h$&{qsd&@|!p;TPo1ACDsn8D*-@s-Bu9&1c6nIZU~K zrJxEo8VA{FCJ-ENOSADr4qG!kT(cH)!o8a-a9uT%&JBd&%pAXXC<#A@h?q%i#o|7; zJmAzO*iii==c3%2QxtDNsL&eorRfGDW`(gVwljn+m>8zFV;U)t50mim$80G=kiXFS z(~{ZQBmfyN&&6eB{WsYD{jQQV#~fj}so4&pB$wMKt0oZeI+D^zV8a_rLGDjQ* z+({CLSk?s_nh*snkCeYrWfIC6Zm-|&c$cEv5s5&U(l;EwuQUUWe5-d*?}F+B^3`Kx zT{fkso-GMnkJIIwPvqpa$T%qRfD>1jycINj?I~1l*Nd^=;79>o>rC7bo|u{l8I|si zkcw3_Kr^iRk1jb3CyR#?t6cVh*RM9q87-x|>nK(^9?CR6S_IQyK7B=+u`ko$Lmv4^ zZ+!hW`ETJO@)+Yo-5a{bht|sl{NhkFmSs`^1LEmu-F0`c)?F0^D(_$05ldcZ0~bXl zaG~UX{;3-a{r=N9h*RfKwxon1{!FX03}CeheKC+{%Zp{*MJ||9hAiMCPIyj87c_Cr zQhJy@?4a)pCNDzG0U<8CIJ9gAKHu;CyAudDpS0 z^H8HKH5@$m0=8zv+WcH&!7r|(nKw^k&eSbC+)-;3l7%?IiZdYr=zSYb0cJ}@V zaRAR?SCJuj{Ip_U1V7@|_p}AkC_D%ZfP*2n8%U#pfU}{G!Z{!vaNOmFCw*oSVWwab z1J^*Ijpc~fbY!0*?m0~B5%BsraHwLtqr+(8R>UwqWV?=+JkCA{>?yuFevlyVv?3iNa!#0u(-Y)&7)!SVmsA6`+qi#lYsvcx4B z69w3A9BK5a0ZWJwqOtACOy^q*=Ql-qwh&%U1Ve-9xhw_qw;h2C4U7BQihY~TRfhmr z)*#?U$oVHkoL#ZFugD$a&FIRLq|)o+0Gm36Uwwp$0lZa6)s76}FiJ5xWe;#92P{NX zQ2c^=GcP3!fqBH)2e$s2z*qi1A@{e17%(_-umv_0`HytZ|A*!!o0rONW}g{RQ0zH1v<<3(I+QUIz62fTNcz zg`;7~+q5xfuoACNtX9V%bXaoNe$+(NCA`UCCv5}*tAQw-lti76zWXp{6V-lf+MKd< z1|dD^m)si=B1rRE5vP*~Y0UOLRXzf%OK9ZGDpy~Pn#t1X2<6?osV22VcbeVafUFS_ zAS{Y@WPoz0_6_xGD~oi53t9%OiJ|%gUp+rv*bO$FJ1MF_uTP$C!td7}-PuWzqH2YV z!40`GQ`+PblcW=69A_Su+doaC{?3IP38RhHd6kGWd)l6IWF%&>Zxe!2&}P-$+-F@3 zCAAZ}eX8Ve{&ey{tkl|v6)F%MXE;(H)ZTG46%-VtCeexKXpMNh|5(6kR<3s#-MBik zyqgq3KC63nNMuh(zyOmpEG zr3B7JgpiKg!p8YMb~GMdUVb_KTC3?8uD|{|Qt-UPvZyb&nqh}9R^QFC@`jv33&Vn2 zVM!(3@n9=1i00AFqTq018}~7y{cO+XoXSpqXCTrMw$OZ`+QgyEVaMZ^>T%n@^D|;} z)6l4abuYK5(qnSiCu)T*UIjQ@22v}0g$`KLnjoI=1b;FZ2Yvv|NAVv# z*M&;s>tnZAAFKF8o(#G;RdHxj5a$%fo;xTrxM`J~&1{RF*>7tEl;(On&G?H%qsCNj&5TJAC2zzlyc)^<>FXkXzETfe+B%(Q8ZPQ}1j*{89E>wU z`P3`|3Ir2O`D)X~>I~orqk?61jw@ME8#Tq}*H$6mpo~j9s#ELG3ORm-<^%Gelf|EI zzK+A10qlG+G&$umsdH6Q#dbTVFNe$UVtj5x^qqr-QurM$Vk9D>5HE_Rawb=NxC-tm z?J_6Z6$W%(JfS0IH!f29sH4X%v_s?^k6ICE#}E22!BKW=RYK;As$MAhwtp=0yi&_W z{x4BHg-AY+9g+1>^fDH!~}^BTi}WwcnfHggs-%qufK_6#EhsgB{DaqbXts8f~Kx4 z0CAm4fhHmSDN6(k9|wKlG(xuH_{EO8YM>)m{KAFCJg095v6B1+B(zV*Cfkm4#$IhI z%<`SW*||EwpF5ZLB?VC#as;1Jk5EBU{jh$uldpIQ3kaGbZnnZ_rxVl{Jp;!P<}NN8 zB82z1mv%2nWC1Qr`CE}SHFPRnmbsq;hi`X$i;F8}((xg_7}uEQj4mO=u(=Hl;%ki% zea7VZk@K$h1R(MGz(=!;BUPLnn2r6>xX>q2bXur-q~kpWc529@>pMfXY(0#^;zr^a z0#|-+jfndpk@Bc$sK^xyPf#0K1jECe)n)dgXT=C4xXAVli2Z! z&l=IF5=%uYO>0JX?ed^Pi!@b-8f`|J*`?!eAj} zQ?M4g3yw{JR=23@ZY(f&^-d*pyVwX$EKE)YkDqA6PbUDq(O{Xct{6JOW&F#ZaB}N# zdw$~uXp^)wmQ(Z*u=`kz?V=zLG!Ip#+QgN4RPye-Q48*TOvhdBKPM|m0v=AQm<=u@h^U5x;%l3F|%bP7D5wmo0_!otrEc*L0rUgTk8o|$fDT^ z{s#4W_Ob2WbjETfSd^Blr>E=NzM z9yS?K9^LzcO4!&1CEPeo<(K{7wb4?+8cd&bI`RH+{eDJ7Hq?N3Peulp?Wu-xaR_to zW$a2*E7PfE4tu@686-m8XYY*W7!r64cZ%TnBXNP;7n-ePBmM5T+b)58d00Zd@U_)R z(_C>n^L0ZNuRso4?%v_>RolV6$}&pWQ)}1-asIk#=;ds-WCFTLRy`Nvjv>6^d4(W2 zLO5S~a3_Auqx2e*rWLI0tzWxNG(lipNRl>W6B6LH*n54dZ{aemE*+`$Fv_L_GVHmj zj|%~=rXO^r`t{2*JA95=EXo-E(s{GNcaf~hH1l@AWYFbB+QFCG9htdcaDbJQZOTBz zS=vY|OC;Z0`1ZX%?>;ohqyTYol7;Krr}ImpQ{YtsqWb~@tt(sFc6~>MzN>v3ljj_r zrbLOWOJ2RJFJJ|&+R{8R<$_Zu?`YHVc#A6entd@O#!0XI#(@@8$_)9@3J0213-732 zsLdYy6XE?cz51{@CQ2;o1LFi%0$XE((!;L5ot)2@ zL=09BRX?4d(OGrJRs!EP%U2aA!ozFeC9Nr<4zhDi)YGN+;?GTWoDpnHPT7VLkicPn zBar1IIDM6^YTMcihk%fYp4>ZqU-sGkKDu?OGM{t(w3dio2en{6a4c5gzE_!9$n+fU z#wb8DL1kJ{9>XvrfOG09PWfHc^yE`nBHK4jUj0JzH zs6fK0hPnt@e9OyPm3MArp@kox!Mt=C7z~dxWk__%4EsHVI;)C9ipQ69cO3}RC%V31 z(KW%Q57+3jw7Id&H6(kk>Yz{I*{Q&74XDM4Ji=C-3>JaBIdK}?g|Dm^_)8mJH^n#hsCR8~@3w`&$`?p%o7 z13T%zR3zM?bIlF4){F#$FHSz?5~(`atxJv3x!=`+cRl!MUOlHkQ? zivkur7VQ+1KTVL)Oq*hQYwX|ew2;=ewjxU`)}{xJ$#|&T9#A#8bzM0+E)vwnNM=qI z<4+|jU38tXq0D$wBj$`XihCI2=vObSI6vd;Y6Ky~G{U51tBZVe`oQBh+=@M;D-R-% z*K1^rU*1p=q47{@a|gW#FNF;(S#&e=O-LNN&33?82S!c0(fp&+XL=BYN13`oU)zNv zxDIO;27S2jub$$< z(|dR>|2^IbWzi5W>l$p^)zHY+NlUDl7d_d0Y#0DwHZ;n`%+B$ST`MmcBHbbMt?=1I zLf4ut%4A)i>=ry#|qqrb%p7fl0 z<5$?_nRlU%!FqJ)X@TPU`!K_nZ~5&+Z}cuB^2I<0r|1Dj&o)%?#OD!MNN#kn;JAFm z!HJ!(z$@!FB9sT85-Oi!=&Q({S-L2N+8t;fAIl&8tMH#QMTA#KAnzbH10!L#McguQWdlwLjAef^FS9Qro9>`RkS*p)n>LBqyi z)XZA0+hq|70l(2k^Vr!Hb`IYTbWB#%)05P-Lh!dzjWiyHdYZp%fI+_~k-#Z+-PY4> zSO60dYu61Sx#Y1J5~~PUOST$0gs0@0UkG-%k%4Gt#DFBs^Lm20Wv+C<9Sf2johmH} zB)+SL{SxnKos&@!%18|Bv8yLDH8(Id+E2(29Nhw5%4-M5 zcqt5fdnrU2==VAOLr}9@WYFpES@Z0*r z_48=qod}6Y)N0a`M|~R8XD4>U@AHdKvA3%|_306P;?3*imD%&}I?rTbEuTb@YWwI~HHjV!$5+eyU+? zL`UPYS>r``=K8wYls3u$NAWv`kQUGiw4Fnq_2p=a_}Vl(YhFX1O3PyU*2uF*?9Gt z)5%Lc=x9@&-{C`mDqxsoM~^Ol13|u8y2mS&Nh~Fa(=nK-DO>1D9>h(^Yi8Ia?ZwT{ zi>{TVofob1Hr(k$kka(HbD-%$#PLJB@Ap)Uc%@Thpj9u(pFd986*`7ul{q3Rvm~G13WL!g6+NtD@S523_Tm4}ysWaeUrFcv=X4}AZ zv%;4aDmcXn;462BT;4bo2Ob>^6T^y^z@p;;{%%_W=KWQMt_X8{a*NWA@lA*9?%9^r z8nty?kXYHA3VRyC$v)F`@aGAP17YQcOwnsiau`)sMKyRVIkv}kVUV?YjH-W=K?^nr zX5LZZ(>sW5c6^kv*+f|@uie;M_NT>)in9@9$>s?Fj8Y&!ycJ3D)$A*rO0a_k_mXvj zwT^-xx_4wb%1#;QVD3_$EXg-8xnm$C@1hX6k0Wk>B{J8%lCFvB{JQ; z5?aoeN=+{9xl?BTPBR&k%r=ZE`7mM3;WNFf-LD1qd5mqRm-doZHqMmM_G7RzsOq&r z_m6L<_hHb-O;w$#gmPN+Qh{vD6HTOeuC0^F78NH{b6vrSFRy<;inu<))HXM?hO+VdS>U^i5q{xp`>g&BF;}QSPO`wWbrE6@Cy2r+G zve}4CP$rcmvCXH#y*VyUl)#{tfQ0sZVA2C_seNLYz0+vBfp6>P7^Ua7k^Ok*{aZLBL`(c#d*$MRCFN@o@c&-Rg+} zi@xFggdGgdH+pwoGE#nkJnk_{wGk9>Y=#b)9pR<0ni2V2SnX&fK%_@qeZe^WkDT8h zG-6JtN2OcoS5@8G4sExcJs*k!lX3M1XI4qyQY3p|nt2zeBAj&w7Hs%?JsSjuSV@lA zW~!6N=tl0g#~W6R#V55J}IeoRS2FYr}#J~H%D+4inh^yfb#tROS_gBNt2^ou)GXnS>V75OLz_j zi36>9z-j;?vB;RWSld`2ap9AcOWMJf*-dX}2&NZ=I19FYl2?$EK#7o(BTvz@Jvh<{QGf;g zOkB|dd&YRwmqmuuRazR}Kx-FmtYdR}2;t!5WOaqOj>7CSSn}*K(Bz}C?ro!8T(6wO z{VXPtIRVXGYrZDkfiuddIvQmW9*~eHYnhOwn$i@e=Ygs_WjZy~PDV$+JmPYeqpOr@BF0 z6OVId6RSvp+<@D{z^FnVdPQS2=<{(>tXs!46iVuIkAffC7V_fBF-3f(Je#OMLP&eq zs97)4Mw|{>E2InRwzyhCF6G=naEn*0%-W&Z90n5Qn{|Tl2;CDp}|hAv`LH>ykMC@QTCOL zvwt}Nlb8%15BOD#Hk;(WljgU%)KdZntvu*C2L}|Y_Y;^nY`?r*ofHj&?e9rXSNj=wG8~TTi^g?y32TJh)*v40ZC6iY zIWs({;1$bg$BN7>3PWLDQ5_~Bty@P4t+Y$RRV)M#`9KiH9ze2bA7UB0U$Rb#qnxCg z3|*^?r5p|kYuK$xWDSFohzop&GiaP4EWTD%_K5jC!avK#RbA}bv(r}Nn7UV z?lrNf+7!#`m`ybTA`DzD8IwV%T&EQzTt4b7x-MSwnzom79wwT@3J-E{Al7ckxm=_m z4za(QCF8>j(8koGCnpjn7eFc9USD3~meWxT%?u*eH<;1<&Vl4@T&iM-5-yspFZ7oJ zZv$NwrJ)WKTI*l(ljQP}!YMfo+FFiI&Nc|Rd*dgI?94-agg_2Uxe!Bg0S_-K-91YO zRCrujaCVt!C8DxNTSN|EuNki=$6+FNB^alP8XKRLR#%KAQ$pFiJp;nw+SyFsQUA^<&0t$S52mX$6B5^2X}saW zJ}IIz@P&3!k)YxasEB;tb({)GTG2u`uqL=!0Zr2|H}9Q1vDtHK*aB%>!WW?mrVUdUd@nB&r*9h-)-Zu6}wu-PeaGJ5ePSEEddcB+hO zT%$2pM;ktN%S#n*&F50i2z=P}9ZSD@gLPYFyw6IY2n%@vEX5m6$5QAHmlTHD+=MaF zDBuw~M?Ts03NEXMa2vZ?oo<`JQVb!stZQ5>Y`R4l^*-!n=rc0i`ug2(Bm^6P&jWf? zgbQctKH)toGi$7jZhe~Cs$?HBH5Bt8Bm#+ktk9%K=^;DXv%xQ_IVaWOr#(;QtJv}V z)SbKMh4|UMsKS$l%hv?Z&mOC;M2@D313%zSo-FBwo*B5BEOEOz+ffIjFJUfiQ__Xc zi_WB-+1f5pZ^61D7dx6yDivVvNQQo!($EDmq)~Cht(v(kd>T;8*+vB(Btl-PH5bO# z{)&aTq-3r@?6-u9GujS4 zsb1b7jp3A`LPMJKfW1J8O}&K~$AGW4NU#P1?3$_0dp?08H@TosHtzOLTkezdDO zj76eW#VAU{%|qKs5cx?NMYX{{fEuX*i zY46zz#4Q~ViZxLdWT3vJPVMBFl88i@Sqb*1lKM`JM#tAUd0}n~IVYGg7{{}Pk$qUr ziRClwP8oqcEu>h6HEq6Ak#XtfJ89mS%;4e?2-3MQ9y`3H3^+pa=D16qtzed+9zR@{ zz_d6;?Z{4;fyv;qy~+A9#F|>I8}k?-`@!9x=LtuXbk2BRFo$ldA;AXih2U$UDauG# z&#pE*D&LQd6&cr!_Dz1040F09x$tcd9Wl)@s;*GXri!GrTwQ&78 zGz!bpw>_mDLsPvld->hA6qxzWNP(l2EeO^qcQthE(7X5e^ine0@vLnmwat~?Wf-iQ zuz|~N5V4BA%~h-G$8#-F9{Z=1nK||7BBVy-a@goX2bz#5mHUj)E@579X#=r<)6T)_ zYSS=!!=lGDNJy-Nz-N|0b`yw?zeWEJ(c9sFrblynlR< zpjJteHVn0F?AbaoTBF-bVVxLXG6dN@k490>)L<2J8X?>+VOiTuPn79?0{B9ssFORv z<)=5{`BOKhw9EH9=4RQQTq&t7}Kw*Q!nR6xz z(h{$$2}RGSNZ@pi8Dt-)#3(IMZ}7U6_H-6o)u1(LU6OESha*n4&EgG4Vpc zm^=iO%CbImjiLUXevf3=Ku9i>S?^Aq?eL+EuIzk?DfcOdr zcVq+1QRnKmvr=A8B@YWj9IE+QF$m99|>mhwr9$6+z93fJVjpc3>r zP+rV9-~Gp6b}-w;Af=Y%`5&Yhi}Jl|!wW*z=44(sq6##yC{?-vW+4!`TQi{eqy?a` zg@+2wl43d~gN?M24OJ%U48t-`v#(o8=?(VY4k8yPcU8F<*u3x;Hfe*QR@NO_=xJwt z!7#BxcQ#-dCS09{lW;;45!lG36!O!irqJqx<$bZBhAv>4EkG0@qD z&~fBz>aYhI37b)?G1@Z(dark9v;CVB!^MSqfG4iRjmhK9U9~GiEljcQJs@KQg@BXM zWhG1N3u5}g&JI+#;Nm@VTG1HED;Es{U5J=TB#lydY#@VsZ36njes>K|_`B>g{$`?` z`>1ssqmDjlT`9yZr3HZs9MkgL(h3R?$N^v=T+F#4Jt0JQ(^%r#+t^$=o!fi7Q9(^j z6;m#NBxloHl&A-PMcLpmo5;~2jZ@&b;me0c7E~IbxTs?xZp@mvZ=QuI$SN>}#Ji*# zS!^`Dm9-AA&}=+fjh$}0(|beR(AZIK-@~Efcp80XQT^J;hFF=0)U#jl@P^8)Iay?C&x02}x3w$!QA9w>zpo zkhG!x_{hmd!@(2YRYPa&@~m-VUGv%-)kxUX z+Wt24Q_#_Z@Sn+xnU9y#IE4)Tgaf^Ixl>tk53x7WOr z3X{Bb4+OC@)eXKfJjw2;smZ8noo(kA=}f zMeJ=aFk$5TC*PRVHg#>ct5s?dr`1f#q4R(27k}fEhYTmDJ?3;R`rqL@r%#G$OUDKR^YyVuFdfVAkyJlF~BEZ2XN6oi^F&mOEqWOQD4r+FbV>2!hq*RM9V82^P1iO4*)1%h9sARQ0op zM%iFsX?=mctSf@Y}o4)SOeLK zafxNf68fUr%GCB3zP1{;J8x%_k2E~gg4+n1~>qqEub_6cs zK!UVw(j+)JpgmqP?II$px(RRfTndP)@M$;sRgel2P9G&$H_mK(@206}?1wc_n`d^5 z1OagYI0mlZMcq=7kYun^WcJo!tLVlQAmZ*&C{U1GIw7nLax#9s<5R6qZP7YDLYfm@ zw45=*f^p~Y9-W7rzMPEpdzJAji{S0 z78rTiL9oKn)jOBVVFj^~wEb;3=2Y29OOhOa#WGjVLd(fK1&H2dAy;del{51`M?_)w zXpifyQQ(+4H7KA94y_a%yaU(1?KJ}SVm=-8_t~mx4Svam2&E-18Y)%LmC%r`4rchA zi=|2s$shub_3bUvmLfCZDelCRtG%Hpz_|TD^la|hH0YzTh_z^Ir0rJ&YtA9;DywBy zjd&6F(e0jlhy*cAtZaeZ-9$0vZlVEqp~BTMFn$@{zo}my4m?HZdyq7a_FiPer{=v& zKE`$G5E1K;GFGN^MJ>)X*`8HRfdO^rB(!(hO_aLXA!&3RTSz(|ov1v4N|9*HOu~~=pcoLrWHy7KTX6kct zfskan3^#F9NGp+DRMnV6Z@aX8z|tf4Z05>C)f9&L1Ur|utazZXeAo(E<|`=2(5M-p z8rDD|hvX&~#HxhCC27oHAYOju7C0N193>+pNOZm`kaAChKo@l3;6wr+Vhrw%ozxxc zNazBC+p~M5f|%peg#xj^Qp5klB~*#%MMfqo%>>d#!lA<&P@mCZfrFSEnwHd~+w&FZ z+K&45&aQOSpJ$@CLI#m&TPa2$y-d&ffltVkx?HOYVecz%NhT_ZRj9@|nRs}_1Vjj3 zy8lg#KvD$zNX}kiofS~U4#LR58402XZjN;zRx8Md zwp!b%c7g=^@0AgdW{HlK6NR#o4fKlRF!~0)#g9S^au}X9UI!Lzts0fwS#eUWic$rX z=Vgd)oYTc+7e7n-!;^5kq)`{gMi`x=#wIR3>E4d8j~2Tio+ZX%+2lqXAkQ}hTGi|>EG-lv!*~>xayU+I0e~Zm0HS-Fjmj-RmTKU514jwTw!ghX zSQ8HDPuqowoIkDE+lfs_y1OJLkLeSP_$>x_hDQ1?+daEs^1+zvHDtA>py}c?uq3MV zG>F;B9vTE=_=%(17L)3nT`V7j$Tq~7cC>m55gjzDfZ%04*ozK}FzvM(K0v^C9RY-l zX31BsT314m8Zsa2!6*lc5U|EoBYUIWP3!Z;-I_}#z!fg915y6tj#yGiO$zNYF(V!# z1IHXYFmw}gSuHuHgKvP(M^G4mJNh|D+IB3>9384OB|L?Gc>rPV=oIjn*r=PkY5>tc z->3kKJ~*Kuxgsp#Rse%sy3=FBG9BQi9ZV)1s_)ZGJ1e!-Y(khJO2TsA#Km%Vvq~ltgPy^s=?w~9>$)}r+b*)ybvcw15_lAm$k9p!wwQa$#(Vsb z##Qs9^jV0{kB{FEr69Il-LoP2>v~PQF-jVQ1b`)q9@g}PeQ!&KxM2Wr;FCg#M$N#Z zbt1HyF8#~M#6m(1%c+ZNgw4{Wg|P^b-6==nIdiUX@ z8#$+hB{pV+=?c3KUcAZ=6cB?578NpNj&#fu`*wkCBFuJ3YiOc981TTFxWX}zL#fiq z1+t|kG#GhC1VKcdS-tiKh)=9(!yAIJW0;A2$69N;32ud*ej*NgiF^$kf%{Sf@KU*i z`E-^oEB0GP42`wt93DhVa!{X2le>GYu079&>r3rM(D=x&$4Od_4Z~T}lUOQ|!pJaY z468%)pxS^288+YZth?9}U2C1HtcM3Tu1{o^`?@t&%F}@VXx*wBE)XqxbP42v%7@ju8aR`&IRHAE84Zgro;rQL z;+QZ&^bgcSfF#4jrc%ZWZCDqwG*8{64%ggRly zavO`k%WI)JvsQ5MOe-mzS0RMgaMFi6PN2@(S(|Q3M0JZOZnmmE1~*_uOniF-60+gasrErzt6C3ZY=%`dz*5+Krw*YI2b8Wa3WO8gdvMNA?W$3^R4o;q| z(U?AWS6Xs&dbM3`*5%6~%C1f?maJf(5HP(spwL(x;Nt-<@md^kd%QTHQi+!~a$n)W z={XK|m+)OYKS3k@4e!EupbY?lj3JYH{+V^zBbt%XEg!CqF`~_mo#7W=g?Jz>z#{b! znN;xG>CrVU;mb8r{Q1|_`iu=|(GlEuZu}cBjrGp}pMR9QWYhPn^#i68*e?G6e;-5> z--&7GQvJ(+(Kr41w_1I{@pGI$e*1axE^e^bBf|54xP{|8q87p%eGdG7=A$)BHpss*giI^Mt6|0DcHn0yER$=_Sw zU7n<`{O9BF-~JFEa{XIY-=EJv=5OlrHT&w%Pj~efe`WpK^D7P*tLNkQ=bv}=pMO^C z`}0RW_oG%*AKtJ3_!)f2<6nIIwiem*d;LXw_VfR1tKWwCw$-=i^M+Y%;Fh)HkH5hO z_V4wdzkOGV{P|lSVrl&K{eJ;Px&HlU_vB@NK7Nms7k>^n|K&^gz~5f6hF?4UX(@W` zH}%=S-+%vSy!|TcORdG?|61SM`3mZNU#kDQ_5b=y`uF<(hPs@;_kYXk-?IAW|L!Gt z34gcb|M>4e@sYIeZ++^Qe@+TNbzh(VVc>TtdcRIbe~Pzr|BruF>plJ%eZKukX|Da} z_5JzZUHz~Ah1UD(AM0~r^2ZzSdj5O`o#*}+U$%OGug|ah#H@i^-jF>%fXZyEz4o5f z`+`2-V&m!WR{G43uUh{`^{w>lR{sauFZ#io^XCs){qD8J;vcmCXI+8A-GBbEJ@ubi z{g3*K_Vj-J=ev9Af63~9*k9aJ|L6DA|C-hR@=NOfy4COD_jmuO^XK}M|9t%Z{OvvU zU;QVo|FgL=K6^cXKCt@TYl~m8`o6Asx!23zU3U3zeEx~m|Kb;wvIxiD|G{zn;cC@r nj}`6jt!Hn_r~mp;E$#dNe!NxR&<1|#Gar!;?{*csuZ#Z$Mla0b literal 0 HcmV?d00001 diff --git a/Ryujinx.Tests.Unicorn/libs/linux/unicorn_fspcr.patch b/Ryujinx.Tests.Unicorn/libs/linux/unicorn_fspcr.patch deleted file mode 100644 index 391c2fb62..000000000 --- a/Ryujinx.Tests.Unicorn/libs/linux/unicorn_fspcr.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/qemu/target-arm/unicorn_arm.c b/qemu/target-arm/unicorn_arm.c -index 5ff9ebb..d4953f4 100644 ---- a/qemu/target-arm/unicorn_arm.c -+++ b/qemu/target-arm/unicorn_arm.c -@@ -101,6 +101,9 @@ int arm_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int coun - case UC_ARM_REG_FPEXC: - *(int32_t *)value = ARM_CPU(uc, mycpu)->env.vfp.xregs[ARM_VFP_FPEXC]; - break; -+ case UC_ARM_REG_FPSCR: -+ *(int32_t *)value = vfp_get_fpscr(&ARM_CPU(uc, mycpu)->env); -+ break; - case UC_ARM_REG_IPSR: - *(uint32_t *)value = xpsr_read(&ARM_CPU(uc, mycpu)->env) & 0x1ff; - break; -@@ -175,6 +178,9 @@ int arm_reg_write(struct uc_struct *uc, unsigned int *regs, void* const* vals, i - case UC_ARM_REG_FPEXC: - ARM_CPU(uc, mycpu)->env.vfp.xregs[ARM_VFP_FPEXC] = *(int32_t *)value; - break; -+ case UC_ARM_REG_FPSCR: -+ vfp_set_fpscr(&ARM_CPU(uc, mycpu)->env, *(uint32_t *)value); -+ break; - case UC_ARM_REG_IPSR: - xpsr_write(&ARM_CPU(uc, mycpu)->env, *(uint32_t *)value, 0x1ff); - break; diff --git a/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll b/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll index 146628e14328ff7a412954c9328de044ce9570a7..1c84586e410a0169002b19897cc7613336b7a62f 100644 GIT binary patch literal 2178048 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

|%r|5h6eAH3t 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�pT2$DrtC+0N@}L|FD)}oJf)>+5BO95F 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 z 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 |PF%bQz9 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¥l;uPlTvx>&KcmjZ zL??*D;!(kF#l}16w*t~@nJH9-x^-q_2m}Wlx^A4(n2Se1jlCh z9}j2qW>6ReMfi)5nD~y50FMH5!325P@`qUWR)wSd7S>_%3I6;xNIwSW$1Mz1h`$?7 z)cqEmX#Yt#<#-2tKupShyOL*EmNMU|WDw0E@wa6gvp=H^%B#V|UsxVnANCn;nXh=% z(`%mYvlRAJ{GhaV)!KVFIzAS~cZ7!VSGEU6qNl)UFdc;#117SQg9u`^*H+%O@bi`4 zL((iEZkhk!VQHi+$_j$+Xg4RC$(({oI9P& zAaL35IEUao0q31?;sL)CJ~Ejbh9Av_=loe)K4-q!zh_{`t0jCIN=0K+S53K1J27&q ze-vJAfS=xmC!_r%L@&MVRI-o3!E0|#!*`DF z9?#x|^+|p=)&39uj+gIPtN#nC?-2Z0`I+DVh z_domDsh%sZ9J&3ftHuvJ=Q*!9^xWqTCptQ&j$D29tByVId9Ob4{OA8t-!<1v+Yk=C9v&+cye5JqvH`?fuL9Z@>Mo-tnRr{b=UEf$zQh;K9Fm;LxG( zzVGni?-Ub>+6Q0!;-7r@$dUi^(W6KIWA@mwpMU)L@$;WJapI?+yyK4lD)sd(e&!`F z`TswA=biujxtG55pXN@U{PFL<>}7xV;LBhBH-FII|G&z~WbrEl10QmJv;cw2KecO+-*?0VHF8B7uiHQgPVRG{Q|9I})yZ`y_yWdrtnwtK%d+vGD zzrXU8zy5z-^{QW6y7%7ue)>zl^oF0m`qi)d@7KKMwNJ_C-}9u`zINsruY28lpZ3eY z{JzKDci%f7{VTt6{}W&T`nNp(4R3hsBc`W+`LZ{@F~9X!fAv=$^`PcYNUb zcfRw3&!3t3ovYvVuF?zM{qEnr_C4?U+_N8e;7iYZ?|UD7?)%>Ndsn^x{lEXxV)17$ z`M?L3`ak%=U%c!?ANud3AO7%vzW5^_`S&|M`q6(q{+qw~Z!emi{olPG`&jkx$3Oo6 z9sI4|`rGbLeBxiae*3ro@wQKX^6&S3>Qn#ls#58X?)mhm=U?-g&wTrre&=`oIQQAl ze*Nt4{_aC}f9`W%p8WjhzjD{y+@B8r-tSe$fB*OYZ1f9X`18RBAN=cqFMjcdXa3+1 zzMuNimwxb`a`~n2`tq0i-v5=ayzISy_=hil+d~iC@s_WC_1HVV_O%zk{p(*p@}~Lu z?l*qp8}Z-x<~R5K+8_PV{$Kvqw+_Gd+uuI$hClw}-e38XKe_!2mC9Ye_nq&gzx3Vj zj(+h^|8(qkzW2Q|pZ>Ey8~Xg8|M|f0{>5LM`mO)V-kE?oQdNJuW`W7D%!JjjNwL}@ zNPtmWL@9L8s3;`@wu*LUB_p#G(=$UDQAXJkS5jGnwl1-gD3W-E)_!RMP2YbL2bMU1$C1`s+9T;D#GE zU3}w>AGzqJo6h+5%{QO^&0B8Se1q%0=DJ&N9lYhX+g^Lq?YF<~pLg8xs%!4NbJ4YT z-F3*n@4oxcD_gC7uDItO{px$~-S2Pr-M9ZA@4x@x-#_rcfq!}M!I%E&p@$CmZ@azq zQx89!dhU@&w*B9uk1lxhvB%~;{P^Q@pM2tpS&u*YZ$ZYPd~lW1ONT+ z&bL4F%$~RY?|-}B{p_>5-1*#dyUO*yVE(pS@4o%6J8!?+i}o1YchP~bJm}Rg+4q$% zKm33r_c_GM6^1^1)QWeme$V>1FMa1nj?Rfw$QxI@<0Hp^_>D{7bJ|;1xur(!!LQu( z>u1jV>$e{H%wK-cy!4I>{_~rAzI^$&p8fP+fAr`_OU-)g!fSqZ``Lf}#*TZ$19et1o@PkL_K zFCF#p-11w#H}cPYzPb74fnO}S;uXIvT>iRq-gWoFuf6}?xt~A!f4hA2nD+Ls`Q9m4 zzTz7zotOOb)SF&fJL5ldK0kc-wx2le{yo2T^uybon|u1Lx6e7}=zABG-}Ue=AAfge z$Ip%Y6%O)0!tP{z(3RKVE*C`@1!p@B7km@4o*0_a1-E zr;ImT^~pEC{VzAYZi^rO^Hr&D{{Fx{>sRl${ZH@Q^^h~~+u?{$J(YUdxlhj8|LbqN z>o;$F!$Y;Vz2mWOo5TP6?t3@>;Zq;@;FV`BS=Bh_jFYds@U++d@ZT>!?Ay(q_PXrq z)WJXd!<;QXee0oHf9(EO?^6EnzAyU169;d4+kZY=TztbPJ}`XM`O8oIed+D1ZoA0H zKlI(>KG^xiN$VfKY|AZv`|s2?KfQU^7hUql9bf#NvDfYO>8G*-pL}HR!%7bwc=*@u zIPaz{Ut9jy*022Z)w{p!tUqt3|M00px2v>|-1R5-4a~djw*9ub>q~1_v@Te-_UTV< z7^f(-$xJ(r)K} z;lhi~*y>F`+Bol+->gl)gZLQzVMAl zPpEwV>7noa_SR#6UTeMnH}`&h{=e=xZ|*Hme0GOFbk2J576174YdbgoB>U*!zw?p@ zoyq}sZhq5`kJ#|`%U-qegzp_tIO$t^y?)up)5m;Zbhr1P`t>c%;up8wYT+w%KH>ha{$kc`=lp7`_UI2@^z26~yZ`IA zA3Ny!Uwm%=vG0H7HGlr*x!DW;am%~Ec*8%AvzTXS{ylJ?q~( z`?kEf({pRzoq25W%GdnqE$d$S_n{4Y{qw{R9oT%;McZtCb#>OMFZ=1vtM~iWZYMAN z_~41N&VI%FcKGt%#vT_Q{MI+!vwZJ2yQ^OM-v54h(Fu=?9Ql#InF~(6{C!(2yX7sJ zHUD|j4%Z%X<6o~Eyyo{;?(^px|8&5W*FLmU>#kdOzw?1d=RMN?--74<`|Z;&{o;45 zUw-A+r~L4&pDn%k^Iw0T{o5}b|Jk2??AWhXKl{cnU;LlaJuZ0p^Urtu!@z$J z+~Q>?e|6IVzn%NX?=Ie9|2KbP%dcGUdh@X_JXGF%*zsGx;eo5azx0lu?*5%CQmrr9 zx19U*#?K%5tvAj4_A`%;yy#aSd-oB$-1YHetA$fO`jbPjp1AGhXRiEf-g)Uh-`n<`Z(sNC8(wwv&Hww{ z!E>Jb!1^_--}{cw?)IZE{`#iNKC<0)CmpqC`aR99et*d5H~;p*-yF1|v3>o{OKYE6 z_2sv0bKYqmUh&I^=l^N#pU=v)KXm`$H@;0j{lVeaU3=xLbCbH>!Y@NouQ5+pE53VY z-N|dkMgDSqow)Q|B46R?`z;qQc=YA#hQ%u(@o8xB{kALDtzN!r?a6fI-)N+~>ofBj zJpKcYa$ipJXkh(D!usYvp}htj|24VxO{{1BJ6Zh(`kViOdgiss>QjyySih05zS)HK z8g#sFa_yT~&%8ca{RaA*H$Xk}#$@&V{$u?{!usY-&|ZU%H&3p86YH6`B&***f769} z=B>%<`~AoIjfC~h+n~J$9dDmp`zF>i??_g^f&S*5P|v(8S$)6%Sih05zIiva*Pvr- za_yT~&%7sD{RaA*_d-4MzGU_N{$u?{!usa@&|ZU%4@|Co6YH4|Cad2-fAb-zXSS2o z_xq3a8wu;14?}wmIzBSF_D!s3KANn41O3g%pq}}7vig4iv3?_Aee(%uuR+HrC)d7- z^~_GP`VI6qpMrYk)5+@l{m1%^g!R+UV!PUW?>Z#=+~nGKuwDh@k*;3HsopOu9lxcg zo7&003Hs4^GaQL~M{y%8zi*gV#-P9LQ^K75>HQ3P-@-jA>}UTL^Y<3u?nUbR$D+O0 zNBcVw{_6V!?mMAh6hHgc?)dV3t2FCB`Bx(R<>!Cm^rw&MjxTc%r{9k7A5VWf!hbyd z`L}iJ@$|ENrvDE4FC7d1EuQ~30QbgI<_rBbv)fSF+Bgs-;MAe zPk#gZJ5l|{+mq?9Mfi`WUysPA)GyyRln?p-Ueu%VXIu~O0DbLC!s~(8A%7>rUmXwo z6A+L5v&VJEm;G@*37py?|4M|v><@pCfBK!>@wu<$A>TiadbF`WC9H3J zd;IxC{@S~sJ=H&|Kjd#k_(%1J{M`tD*&qHeug3K-1bT8k^pn36;jikq@H|KU*>`ux zm-^E=;G~QED-r%uzkHuwKE48nuKz9UPx?I&w^1E`JjmaU@K^g&L!IQ`j_{ZLG1z&X z{L?3O>xmi<1Lsrvec<2hq2Kp^f49G?Kl=(OCx1J_U+S-*A0?LjE5qIKrGELD3;8%7 z4qg8{cs|Q6hx+;Njz1p0|B7yZwLkgSKsotWBK&248t6yD$UnWZJATx7)UJf}QOSdU zp@)9oe@VB$s^44$<>a4U+U+m(JLpIH$v?ZS+h6Lx03GG$VWJ*c91k1&-~QhC<3axI z2!FLd_Tiw1{PQP+t;U!AX`>%$BmYK(zwD3Bu4m1^z<3x1h~HuQ?HK<`gukl4hUagJ zudV8i@9AgfZ}QJa_{;O(x!BQ*@tDQyYU;m!8T7}&`t}dP<6*@3H$DLRRDZQUF1}u( z_*s1Yp!&=HRAxaOuBQ~?FZ9_X@a|f>cvo{@(RhOg;^(JHF>1GhVeAf0w;}sr?o7>!*g}kMeoFI9yKk?3jA$+?Kg$90K*J zo*Pq7&T;v$4vlWlSRdx3)60dZN1vmVpJSAd3e%mX^8<@x{5ZUhwWGuBP(5uV%&8vl zZ~8Fsqk8$6deq+<`h#lmIuLa_G4-gwd9IVjb3<5Xj`c79#&CHS>)A2&c>l5oVgIn6 z8&i+^*Fb;2e^}4h7}h~LsekQ{V>`GmT&$PI{Ls9mc}Vk$=E=9PLHRjT`OtAc%wxPf z#wqc6fqAEKqjC5)gz-MlDt|OFUWwwNz7igF%nRj}@? zittzaQ^D&+^4B(Z$Cv%7;B`9JV@3GO{qdK_`HDlJ}3EAPV20L6PzCvmXBWZ!%_1g zbjW<<`=Fzt<^%ic2puPgXOf4*){A{~c>VN>)l~B#bjW--;`w~*xI@hcekYJFbi~cm zQr!Gt{m*WhZ2xGh`4Bo}KIG&1)}ft?`}+KCL4f!_KaLLHiTz+;KFl}5yr|%jxy70( z)S=Dac8*-OG9QL`#^ocWo&<#-J-7~wbl`s8Yu#oj9}C16T;X@t><@k~*U9Wb>h?V4b)- zCd)^T@*(Gk&>^MtL&uPs4?N#aMu&~#lHUpQKu6K9i98BwKJa|&h>A(aWvV)gem)k2 zWIr3S0%J60KXnfe&j*L)qt`qg_4Bd7-#>KG?DMUotmXsPOJ3-R`(C>81oL6ePj-H( zD*2F$MM^RHprfwl1J_G_8g$q!A0p(qk1;hLxL)$2##F~8sg5S;5Wk8p$3&*Y7Eii= zBz~OGqqZ}w7Z;Bcs-!r%iVmp(hbA^3RJDwbqj)48RIY6uo~J&K`Jtugm?h40q$O!S zoEL@nkC^6Sahr5(F-u;4_&Va|M_eG|HB-}}eF?6|G^s;O5X#5%ts_l3wwmqbL(WzC zh|^Jx*~g}u4_(o*rD&gYI8sc+qaLFp!Zi^(5Oplv3MW3+j>`uHCq0y(iRL5jzRe<4 z2PH!^)=S4_n3wP4ajH5D8kcQ?^jy{fyuIlj4xYIK(N%s#i9l9R<_AOC(r>O6Q|CGS!NS0wS*&ChHd z)q`%9-X_`W-8AVC1EcDQ8y`))%^CG*Px5*>Di*PRj!y>jqHU|qnIrZ$+-hUzZ92Hz zC;8KI96%il<=Y{+&&t5D)y-^<)fhHL)pXQbWlD9QO`{2lI`?!{njZj%G&_C&=fac z&6x4gGs#}>%6_X4hvvxwv5$xXnkO@*qqbMH-iEIuxIZFw#?6zVn0%;xo6OHme_Vo1 zH7^3m{Y=^3{LsZ19r<0LpOxL=*w_vGJ;C=?TjS`UG4e%-AF4fckT2gadc9B7@yBJh z|30E2g2nCcGnJ3jm+^h~1;OT?A4kV%Og=0e7j4hUjY|e~X!3GV41^pDqYpX;{Cotn z(m@>o5unF|=4l(}r-AdeM>W5Aa2y>`^VF}%ejpt#j*G72dSQNXX3ifMF;9h)Y}*mf zxOpmyA|IrqS3dYUqF_UQK7y0KBMOu1pm}P(81hiTBem1(Sn%`Vy{wsZJ|?OoE+0k8 zhZdaZE*IkCKIa|RF9X+YcGvOeMVt=X&xbhA;ktEZP)A%p&Q|h4>tizcFfkwbN#>)h z=0j{4v|f^q%S3g=<)f=|4KS8Ek7S(ADcivW=coL z*D+guyv$!Oao4Am7k;#H-S#@q@p(gWQe}LLC5!HgpLo$> z+sWSt$nG6J&z6|Ynw^l@k8vSkgOryogWX)DC3buU#dqM3_t5u;{1|xTzI;7VxuNR7 z6P_&!B!BH4%63x$-qR#<-_cu1FLIx!f~N`>am~mb%^q!oL6-r zE-o)MI!UV?P=2;|h5PO%9+jeTf6S{oaOvoxMx2gId+2bn{aQjD1yzSKRpNB?%E!2! z9;dLV>QMR`r=wRs#%XHYqEXVJ9)s1v3F1=^4_I@pXt-RTI#$;|%GDnsog<|L;qAnc(@K z^tW+8aq&3O&#h8@emqdsF@gP^>Nl|cv1!t=h;*nQ0uYVG?eC;x0OyO2$EoJSKpo=4 z2wu-9C2=~aUD89xsp_CO>am~mHAi{bVq%|vr}?Ssmp@N6FDB`j4dX+XM{(mr?PqYF zrefx+J`jFAO!eZ9c~ys)Booy!i0uzdlMYMOAw=|=uS3`_?-w7J_DjO~p!-Gc?$Mv` zDWZ;rG7*>zUHsT7)quQ%rh zBKq-tNEuZJO6iA=tgj;tw9Q%=% z)s&BU;!iE(Yw$BgXD9pntJ9gMe&l0=pAYd12Y4wW^O5xPGgHmO8KgrCP9R%|bv)Jc ze^Om%`8t9dH2u(Vwyz`jMG&bY>Gx(O?LYTFSJ8pns`!e6=U+!z(LraLe(1Qs*AaXU zupje-kCU|fv42!4A96p%msBzPhY_WDx>()UA;zVTI->g1k8vR#W0V)=_00d}^M4cb zp>5@TyxO~kC-;-2*F#giU**!cY$YZuZmj*#;eXzXzVuGyLS6>+%10ir72-bcMe}c> z#}?&-ZmNok`%XYd2G{y@-S2KII_P{i5gmi5W4i8lN9C;_`$&h@%Utn(2u@Xd0^<^; zUouCB`-TlX#`(qZXH-364$3jI#k1FZjf>YN9eX9apXtXtA3w(`@*}5!)FXfMOL+^} zcb&-}QmPIig7OllBTE-iOq9#++tnyP%rEY9foWgITt6QvaRWY12MgO}>Zr$Hy1E=0 z$f!CnA5+m`WBZkaI&{(@u1e_pJZ+(MvahdyWP^`lq9y=(&;>r0a!$A7A>w zWc#b5>JUn#nCZ|_dwDV)4OIv3AJd^DX+B(4hjRTs)p1FhkG85qTzB+qT(k@DdR@*_ z@lnlw=}7xJ zQXP3!$26{&q&h5Lhc;`jGEYspc&6|$6TY6YRUOK>Jl{G>zK%JwN={Lj zr*`t|)2^*%zhvtr^)w}Jrs)NqAeb6zW>Y(#&A9M^VI{f`q=$Pt$swJEs zIaLSkAAQg*v^g%~a(IGz>;+^M&4nzDrrANZ~F{)WK?OI%ZRhws=0@ zIvihz5HXGUNcw%^4OPc9<|C;NSJ5$7$;VXp^@@?~yx;b9%+aP(hm%l;b|HR#S^Q$K zd>thpws=l8A8x|GSCRI0h|e2LqYgb`K6GD)$j3C!kEHp?`Z`2Drcp=Id>EtyZcEAO zF{$@orh0$fRCUM`?j&_U&B_0t_jTZ)Oj5^G^HETB;Ch+N`(jhoVW~R$I4(t12cBOh zlaI;I45(@QI`ATSlJjG#?*omhI>hTa`It;Truu$m$=4x14?2Z;I@NrXRULiIN5$76 z{;}(!4Cydb9b!$&X2#P|oFN^i zszZ!@FCC*Zq$5u{X3d!+zcQ4XaYSAsAHmlEL>5mLu0|aT<+hF@bP+6eAB)jZ8w~Fs z`Ik;@pO^jV$LH#0yYf+_@!1CUGdhjLtzW1W|37{ILfq%G<7$qQj#;z3eMIVz&&leD zYo`}4sXD~#x_;@XkPh)ZP;y`GmyRmwkmDjm^h-ybbcnwT-OqenjymMuW2XCtGAGaX zJZ<>-fb(v@#>MsXA?_>VzS@s`v?w3q@0a&;esokFA|L%4msAC>Q^ohU^>f{(eH~NV zKP-0M?X{2d^4(F#LNOwE+t52EUAM**bquKa5F+K0>Bo8*BptJY^L{^c^R(oT%Ywd+ zOWDtdxDFDD=-2*HRq`>F{d5Xb)T>%o^C5_w8~vCcW2%lmp7)!iLtd{>L5Fd8_;qg5 zacYqc@%_H?^K6qHm#LojJETMAL+l@u)seLS+&^^@UXRIq;2iD8JWZpHg<{dl{X;(b zp#xeD|KE@MAQ{Ssc(aneGynf`KKOkYd44g(Gw%AMF4ojU9%jP(?z%cI*g#$!r>etF zsN;olKWKpV5Ai-7-S5}O$wv;?ws4u&N7Q{O*+AU5v?e$|l8TJlPsh_i-(R1!onHP? z`>8G}#pUBbx~T6JIAM8KogbpET(`P-#_1?YhZ%Szy`S8eU_NT|lig=O!q3O7Ir93H z^5MkvxiCZPCF=gEln^&A%@`f#8{m3OUj)ZW!hJ02{^@u+Y7-onm~_VCsQah7@Q%yJ zr8A`C|KC3i57Ga>e;Uq5ie5J;_fMrq+&nEz@I1%=-b1+6Zfh}J2hA4Or?@xh4|cX56^cV$LsJIt~rzqu1xia->7Kk2{_YJ@_~?Z+c4h5b03vc0wH`Ux&yCZer7sk4i!v6<&;`Aw*1<4lC*R2&p>6p4~4UuCGIU-^4VIOEzIX+NzFe9G6DI@2kh%0_~RlLOw#j9W8d)gM$+F0)>R!eE`88(AnI5+FBRS{hj`|@RyGmZ{XH15OL6ZSJ3kKCE(iOR=R9b(-|9fo+u&5!a7jZ2esDDNXn zjd40U_&bo&XF4B8&^{*LuaqYke8lM(RD8%+9;bT$qec0^_p@LC18B$;wm-n_buXd@ZYycs$)pahmb;Q91_Kj?Frr=W7T?^ z%hi042vN(4sX0?RR+A3-XPp!sapU5~>>qqwlDF;dBq-)Z6RMjEI zo>Jn(L@Z8lz3}Sv_mt!Qu5*cWi1+vD_oQ?Y#NmO^HfLy_mPv>By_1QIOM8;}i0PJ( zE2Kl5ZzrN78#7P+fRp^8N;>4Yh!}Ey=;G;!XVjxi6DX>13am~##P4a54jImgiP@ZB zK9a86G14K=FB4cVZp=KT_p@W<_ry)oA>MD8h>n2?UMKf#m^rjahxmI=G%m8s4)dF7 zvqL(>@4rYLQbRv$c9a$<<*R32u+`-KCl4v`PJJ=o%D zi04!u?S%IW2UQ(nE%ZxA&et(-TF0f9@c#Ocs$&|*rIc`f6jUA4I4((bM9sy1ynZQS zJ{HIciVwMmB>j6ul~*Twy+Uo#(WY^kL^h@WaIegM?T7=Lz_KY zW`yL#y>4j5+z*=S?-f*i9kVd-EM>#+rL-HM0&uOIn{>R(^aFW!iNDtUdF z^!yUlpCruR*ZrLoe$Vsy))A$-ALBwg(l}qWS;6;B$qhbkA8}*8=ZWIcv4WqQCcD@B zQskFW^w2t%dg9h`>jkEV`Vn`W?Dw@$JYC5Tz0Q*Pamaxk+cTV>0n#&n!5mEplKY)} z#$6vUd4aijYS)hxXpr=56?{**)Dt(KsUAO8UNJR2IaP-^4~c(XUY>6qL#hsuh<@oP zs5-L-pCKKNuOs-r((%TnIYaBEq3W1GKHM47;i@_&uwGg-q@%6sn812z&yWr| z7x<$e`?{~;*R;O}f1iC!6i)VaPid0-d`i`U^R*v3{JbbX^mBaDl$Uwi_b>RCZ z{m@ZWb>Q`TKXlYl$HIByWFv1>^l4liRR_N3-j95YsXFkUcRzH%NQD3I=lNAuN!`Cr;&K5X7^5f_z#^`WZ|9QWAjf?5)m@^?Aqcfx< z@9Plx5P6XE)Ey@uqA2n~`DpJ7^CG)j^!$jdETHA<5cv@HRCTz!L;Kbq$#mGh4v`Nr zNu`dwc*f0BYlg<989}S zgKG1H4xvPf>4y&94QcmdTu4U+=j+0G^9fB$UP^l>Tfa3`hc{EEXMB?C@ROkY(9iL? zjPfFLg4aCq9GvvJ*?CQ}@ld+&{WzxRfOmS~S|DzJ<@IOM?&tVy99gfeTy?6pYW=C& z$fjl5kw+fsJ=d&Tt1Vf&!M6*iTiV98%Y9q=jhsHB<>~L_Z5z&z9A7!k{BZ}~`{110 z`GkKbuP}}dT#lh8S~H}hh&nX!dPHv0l!Lf@bYgVyzZ=+2n2%94A4&qG4#o6yl8QNx zPgRHCfbv5>=X>yX9iJ2Dtw2<-{iuq2R|SteFG>GCT!qF*eDITW$opTtbOeg0=cho& ztQUxmxi1hMslMu{(fpVvCMnL-?Zo^@{Q2eSnI@qq{anA7`FTnCo2$G}`hPVqm;3n; zLxks&e&nM;I)ck^Z_dQ+XRBk*d!dOo3By%&C?~o&9fcXvaX;x0eVB#o_e}9~()-qv zNI|Gd2X)B%=k$=*B$ItVpJF6Fo&)zs7pBB{7c1N1IMsFAcwMsB1u0br&X4KPkxlqL zhiO#@UL;P3j->g}RULS3GaWjT=Ho!pA^vV3J^FDyv`E!~^P?X+CI(R$1>>*VL zzIL0;`{%vd=~aFb>5#u%5G>uEuVd)%EbNOpq5|l(t0zLl9gi{> zANAIw-+j!n!$%);h!BN#?#+SydR8uNrTk5-zGo}tZ)WA5t(LR+Zw|#j?(K_@lev<| zK-vVQoq^{Y0l7+$hu{j$O<7lDVz z*RNQzJdCwXE3M&^S8NzwX^kvfuarqX62UhvU$sfb9KqxIrK>hbdyQyz)$oWlvSPK@ zAlS>7tXe6e2ol)KRQ@P?%z zMzk;e*KAx}9D#KB_Hwk>u2{Erc-e|mhgU*8uuopWY>_A^U%x_Rbmj2MLtY61$Uf}Q zunpdtwtnQ)^&6B5zKs=p8!LG2FIm2PwHSs$5mK=?GJ8a7P|KygQVJ@P+Q24#R8IO= zF59%hb06NYZg?emY1*>&%MLqqn0kTsvH)01R;^#LTn=4vS>fbYE*f6F!b?B|AGVgP zU$S~NOpvbfr7`uEt_amzwq(Q-ZG@O?(BsTk@+HeRc~fD{l42obuUZ?nmlW4-TvG`A zSFH`%VkkoXBO8~7Y%#Y&wpbn^+nZV<*`#NgoLV7UOfAJ;LJm^2cu+mT!4a^}<=mDH zlAo|W$z%)TP_581*$WDETvTjVcnuUZ?nz5IE|Q%3Es@$wkKggam5z7+pum#I(u$@T#s)IxLk(8s@1d=#bqn34XecBU%z6*#`SAu z?d8jcH*Z=ZK6|lb*(!1Hj!WUV9PQ<3pDgVq8#j1<@+i4DdVcaK?V=2XN7`>jHisec#6VzK!*L8|$xIAFl7)Sl_p?zHejwrK?1u75%=A^?e)b`!?1Wb2?Ptx2gYB zPW>kv>x(rIs_)xa-?y>8Z&Q7-C@6r;=ZJ3?y!;0C2(y=a_D0`-DYKz|P!9D28|sgU z#T9O!Y-*ouYM*RspKNNMY-*ouYM*Ruf5}pR0KESDHn#8E*uHOL`@W6s`!=@k+t|Ku zLw&JlQTM(4`8JN9Z{zs+wm*Ji)Al3CRR`;0L^vWBk>CO2>vIt?l8eXz-x$P^+kp3w zaTFekukcWOg@@wHYcPhJ_h-)@={rNWL&$lsuzK!`?&FcF$*7t3! z@7sR;@c6ABLEEp7wqGA@tS_!LL;8Ii>-#p=_ie0?{W;CwKcPRT4Tt-G+OXfgmoMKR z`{Vojw(sxhQ~c2m_s@^NZv7@H#h)kA7Oo*%gbvxFE50rEXFq_PXP%7#d>aG!HU^Nk zIDdrdd$v;Fvz7Xut<)b88!!cs@xX=vfeitqy^;BY9r%OI{h>iGp4dM_?Rj=kU+%w~ z9$V?4Ul_L4B};`f~qO>w_KC2Ro<_c2IvKs}FWiAMBt$*g^eetJjC- zi?pFO^jF$YJFuZP*lX5p@~#~yKc0;NWWGEb1Nb%ukT$NLRc!sNV(Uk|EQI!ae-T60 z$MxdZ_idpeRF3uia=*T}o|O7%`}NWG>!XeJg`VYYuNgbcDR06zbseluUfl& zMG+Oq`2sdX2y9Rg*bqV5-hQ*$JN-g_MJGX7wD z;k@wzJLo^yLI0&qJ1oW9tmF^M!}V#0mHzV8wmhy{D<%#uB;N)ZK{?sE>sQcK z0_0WNSlutj>b{NDJzLzyq~?8FtuM>f`qEbGk8E6Pouc#`?4aLZ2YK7b{J{?V!4CX4 zud;?Mv4-SB=4a#Pu)SgZ+EBUdzv^#!?E#VXWjuA?lD67kX{-H}w%T9WzFJ?}YJF*| z^`))WmwMIu(pKwBTdgl`wf?eoYs4L&4W5te582X3+N+n0Y#0^}tHjSMRQ&3&Hm%nW zdB6VDVGHlzc*9S8t+=}t#l>CEXuctu7gojak!VhOXt=l@&Ph)fM?IXAo-U4hI43xgO3*PZvi$oRgj|j(Ru;J#dQlCXk$mVqU@g>E@s( zf_r+pIp~Sto}O+l-&wHml&fxOF+r?22=hWXW z9uuGX+vOhTefEJ)Bd2yEy9Moci0vW8zbPyZmF~Lx1raN=~5AeD3Dr z%^We`BDgmXyE)9m2p;2K@N{8yZx!Z z+#klV+n@T&{i(m4Q-8ZS_Lp<&FP{(8U(Tt&T^z@YbLwvwkMXDea)0VC=hWXWj{W7F z`pfri>M!Ti-!6{&Ij8=1@fd&VFZZYZa!&p2;@IC17xy#8YP7ryQMqpA`%S7X-~Uo= z`Ql2o<@;M1h;4g&;0Ta-h9pgd%I@1H9A*V2TN{^I@=UG{tX!D)q6_-O&y zC&jD3^|bX!f3RT*1~xQ^*vju@7YRy&sOWNS{tq}ZMD9%)%wy7>dX7X zYJISS`d|n3!B*>s+n4uy)%L+w>j&+hylKTU@d817Cr*KfdU1*dOe`AMC(i-oID$i1_OL ze%byeWqek8_uEx}dH-I~@0Bb1MSny0TRdCw_iV*~*_xAAWI>SEKd`+DitSY(djsWxI+l#K0d(mk=i8q8*fBA;5>Mw28U%nrz`b%5&m$vFJ=10h1+N!^_ zL;m9P0cw0PAH)8_4*7c@mhq~<`T$!tq1v(v*xrY36o0S-f3SUj`Tn`8N7|}>X{&lh zmMvMm6uRuShqfOdZ9l%qFLfC7NgEmnY-qr<#eASU!hx;$d*zD182^yJXDj}mt@sN) zA%D+S{5@Oo7xmX;1y2vyUIoSWDtNZHpFsuHmK9W6RsdV>U#dUYfj`)R|B_-@zqD2T z(pL0~`4{$=w(2i!#b12pZ+HYIgr`s1@G&>gEA4_(-z#T!fyWEmn}s0R0>ziMjG)*f z%wEoHuLad#wxHO{L-t12p0GplHV%i|+c+F^K4mL zv2lU;{iBqe5D&`3{-N;{{%{wU z=9{!b{^IpfkUtSm*rE8s4*84MO(B0_hx~;d^53{F>@Vz)zpxd5??aaX|V(y)KHzrhLIn}5rst@wMk5?{U{C_i9CZ@SPr2sl*k<`5*{;$=u=9O66<@o=2A zLBo(G99M-?Ag_N4r$EH10Un1qk3&2ZXIYFMg@@uOJQQcc8u7V)W!ya*YI=G-8)`^< z_3}fOE*)MkvJ3rRz5GySAI9uO%s!-0kRQSpAt2tN%s!0SiZ`V_uiCP{ zY9BH(5^kT_hcSB*vt@lX-l5Ev_0@7&U$qYr^FGvnW*^4vMa-7<)p&<8Th>?0Wqs8? zWNBd|3?Pl?2(ye%L^i&*Fp3zhVew_T8c&uh_R5ts!y%ruA!C6J84GO4SYSiO0$V8d=c|kd8S~5i8SX7B zTo=O6&m`y6L(VC)oKprkrx!bV#Uh% ztC!=V2c)cH$wj>`4#Ooe)Q5Bj_EG_$62RD|si%aE`hVl*b6Mm0HtS9U@+0#sP*X-5d^ zhj#>(djfDIiBoelzzSF0nO>S-GC$!NMfH8XtUcNxd_P(ttY0)!yvVeQcj>(=d^nf#rE;?s^nHeOEpJAlg70U=H+h|T~ikMgH{ z@bRX6a85f2#g!`xPa)m}S9w%>em>YdkUYwtc90-0?I7XxMDxR2{1Njbip%ym$9xdx z^P`K?juYsEe2}BcquQfFe9s@&_s9l{r`Vx#`Fo`yect7jqR+d$3T(Li3fUojG&@N@%}TPp_Ei1Y9_fS2 zJ-WUT`dK+#?gi!49@VGz$d>KFh+w`To{G#ctmB=7{>UbhQ9~wy<0j&)Pep{0zT*&YXQC%Fpu4#W&GJl#eRq5ye^w z%f)w5&DJ9v%2Y1?fOu4SmC9$&nj2AFt5f;hSu%B9k1;Br1%GdoJ2ZQgw|bO!dX&#y ziQ^N~ej3ZQ+0ptkJ<9j(QJ$st#owXfeV6uNk8-0&dFP$jKM`M#Xdrd`_~lxU@=TBN z9O@H)eONBFuE$W1@K#0%Rf)l^+=rz^GCEExxO@D{WH|<$PsxS60J)(S+%EkT}QLL4FlvjI{*L#$Y^(b%lC~x&BZ}%wgPpjXZ?@`{sa(VuZ>R+=*d8rtLTee(Pq)xUur<%2!?XY?q~^(Y@w^hfE- z_b4y)C^vhwpYKs_`Q@`C^Ji1JI6p_}FDd0w<6oijnEjzff<#vzqQjhX-kMatYi~S>N z{A)^i)cmUVD0h04H+qy;daS={k8+pFW9Cm2+n3j$QT=cADDP0Yxc-c)pE?DvZ)3`B z(igLTTRqA%7+>6lh`fFs@XMoLpk{lN4^p}KD|AsWFmqTgzPEOcya&P`LsVa!f1}C^ zO8wn=WnmYU`cdVhetm6rKB9h!%Ek3lRC$HU#r!a|?njl%#rWjAi?vIsT=d_FD8CHL z<@HBY`B;zoO)3}dN7Z+Gl(%}6x2at8KOdpLqm)OrudNQQZ=%Z6J<9bS<=GzPMvro{ zM|r+Sxz(fG?omG4qrB9kyxgO_s+32KUyaIR#<%X5$IQ+hznvcCsWs!z zuS}2ffga_9J<4-_`K-B7^V_8IIojN){O74$y#9(Rx3F9j4_*Gp9yZk%*KY8_b8{7=m$X@vip797xlbQbPc?~ z^c?sC(?jD#$9~Tr(vf|E%xls^^XP?=pG7aQ{7{}~{r4+Rq=SzA9yiiK<3)DA^Fw-G zX!#lJ^Sq|J!DhfNdHV9O&qV|*|-g%O}b`U2gT=l z$bP6)qb;^J~0dDIo>KU19R_`jYH>OURn^$ZL?k`8_(J#>t#KV7`G1N_~>f-+ByS>it2j=+lwKVE#G)BPI!UBgl2^dT(wo4`wq zJKN2(ML4aj#E~D&Dj}~j-q;QN>x{eTKgM`^ckpjAZhjT?w2(`k^6wp1z7D*taDlb- zmw|T}H@^a0dwFm_&Jh0Y1;A5`myo9!*Dy|obJVFbUcx+Q8P8t`?H<9na|!Upj9bW$ zVO+yF$1`qVe)5dlIKCF+?Q@{rGZZfK>3j+JS&V0qpUrp)(>C|m7hQy=hwS?<#G;6Jq~h{`D@$b ze#`u8e}Fg^^Kbtac#&}vxy`u#8}KhN?pz7{0>-r~fL9r}t_JQf-uN5v2IK8t0(TjA zYQWnHm%rCg+XeEFIwTlJ$=f>s&oEv={{e*`ulNrt-20sg)RR;AV&QM$JRDNEH(nUO zz<2}wi;QP~4*eQsJijC8FEgITbysEF`62v$=Q?tEu9tdD~m+XLWVQn>WD zkXIBg{cG56P2tkNg4|KK^l$GC@f!-4{w{J?;nF{iaoP&^{4o#OVZl6<{wDgT6)ydC zYBZ@esp3#>WFZH3D{y0$y`mlQ7j zE66Jfm;O4AOHJYO^Gj*uj>2~mJWu&oxFNWU^TAcPT&M0{FkWqiOaDA_ZBdYa>91kl z(h8Tq8!>r!xQ6{T7&mBsD*PxBUq1-?n`hj{ zb}fZVJtoGt8MiP_N#Qbn1^ZP|xQt_={u<*Y)afW(#wlU`8w!{C&tv{w#v7=!&3FxU zYOf0NEaTWXE@{Tws8eU$#&w!ixFOk$+(TYz_@|jR=C`U9pq(&%l$Bq=h+%^fwU8ZC>yz> za7}P$H_+2ixFPCf>3qky{UY#hBd7j0GQhQ0Lq3U{y8=%uT%KoZy93u1F7xK@0X)li zdQaen!eyN7i{N^|L@tn*|Hf{>^NPQW-`*LnV=aXng6Hu%_6#M?T)}I|&tg1}*VSh; zt|LE}@e200f}HY`M_yC7?3ac7GKEY1S>%^1+{-7<&#M$J=YyMuJTw(9{k5GTpDuFw zOL?-t+85yUU`O$n-26Q7)ZxMSdb|WY!+7O$__~sD2mJ>b*D-#M@%&FA&Jg3)Pkx`F>k11TnZ~p+|G?5GB<=OZ?@D}4H z`gb`09{8tT6YN(~XAS)`j63K*z_?5KSGd$!`834OG2X!VLyT*vzrZ-%XDcdP_Bnkv zth-Uh>3&+7apyDOUuC?4ysmJ0|F(^MOyOR;4#a6PuKgZ(i}Bj;fOi$RKcH~wPxl)K8E<2La*We`$RWln=wD#mMqX67)YG8+D_rWyehT_qX51PD zUS+(Eyw13ee2jC9-()DsPh$QVuYCe|>b2qgXVE{ycpm)+7`M=Wknsxo=NLE7 zgLa1)uYC!4fpH!Ei;UaoKgxI-d6{wRT!>#~+(lkz+(16Yc;1FMO~&<)0B6-&sUw5@%Q&S=fM*yt zD!>OAZ=?U9!sY&7L;oD()^{MzkiuoV`nQ1>7`Ljxi;TO-M-}e1dja^D6)xjfz6`v| zc=oHn>x^q(0zSrg<$T~x#;x;!w;0z^PeTY^Yp)OTCb|7Z$a6;F zQhy%#0OO?}L!3c{%hwmRvw@q8S3U#0z_^BSY{pHDQ)WDiacYd`G0qs{C5+=TZeyGd z_>s; z8Bc!yH6Xy(yf3w+%eQc(wz4K;d#++Q<#Y8_0(k zcOQW`7URyNz(*OcAg^%#1o+n(cb){^V7!F9#klz}_-nZ!53*kt>dY{1AkQ*h`8~wR zDO`@1c{%VQ#+^R`FEDOg0ldg~<4WM8jGM^Ij9Y&K|0?7981OpdrN00lV_g3?@FwG0 z6L^d9{I$S4j2qVhPrW(Fv&@fvBk&C4_6@)X7bQmW+skemlPkJ(( zqn-iANzWkTq$kIC_I}Vg#QB533yhPVBI8ya{6{(e74S0Sq^HWb^=t62GoJqq@G-_q zzXRUn+yUNVTt5Kz|Bk}tev-xgKlRoi|MGgn&VqkN;qv^~Kt8~@gM5(j%H9wsr*IiR zy$|pq#x>*x#!GJp|DwX>`AEm}<0#`A@-pM*-H@NE!et(6cLA?6uA~1L1<2LdE#?!Y#oI%D77kG~G2J#`st=quAz<34E<3)wb zeKLO&_>VG9=lwF{6sO8K#i=t+amE-oaeSK!m-8x*yv2A4d53ZB?{J-;ejDUp&T;vA z*7_T8o$c9=gP4qVzulxi2^NgpFTZ~)30e_qE_OF4L7}wCh!g%^=7_S=R z&VPYBoUey<*HHNJB0r@Kz*`EJd2^6=7`HZof9mZ)o@JcuM&KF7ts?LNh0FN*>A($z z%Q*IC;6sdS$P0|8KM4Lsh0FMPYA_S4ZU{KhC>=r%Yvj z3Z6azc!u%Xdw>rxo<9lrAmjG?f#(?4^T3A~Prn;@f${bl;6;^-{5xxbk20>Kf0^;T z1^!jWv!?>DGhSK;e2j7Poxqza7kSGb54^>=bsX>xRrgFI% ztN`9*+*k^{#dvmSSQj0Y%lZEz;Hl%n`L{E`GmN(}&H&?$6!;G^?ra4-$9QFH;6sep z<^wMLdy8|yXp4}CAm2rC;;C05W1;ED`Hx~kLGM?WSc#HFuFfJX& zOUP624CmiLo?*O=Jj-|uKhKj>xct13j-S^WV!VR>1%=D&&oufM8PBGnU!w|_=czXG zGUG1hzsh)J4~SD|yzxJ{o*84j^c?Ueuo?|?Xe28&pFNjl6_)#LhfxO7Lwm0~XGH#=PnQ;gETUEHc zKG)E{&bWhl7(*`mD*fBoZWFo8p&_5hTMC!{`inqkhj9aW>NoIv7BY_X&m+$;UfK=f z3@F?X{>JXW2N`c5&naBSFYN*TLkjoeycl?a@domu!abckga0UUnIHMoP=A?m9sR3} zyU6Q|8#_Y$G2}8oJBepy2jESGd!JX_9(arK8uE_9<@Kbs75Jxr3;CDYW&9d)o$>T8 z;Gab<-Wv2iFX|%CF>YfX3XJD75XWY`F&}sZxd^W<7NWAq>x}DLfPVuy%|m@l;4b48 z^lx*H{@U+?d1#2XjX4k}ja=%HJdJvE#+`ZKpJiO13*2BlkMT{$ZRB~zE66Rz?b#6D zX1s>H%(#&P{~F^J)bA)<6G?S3PD9}`4>gSAGVWkL+nl3*%?W<5O2%<8KWWZ!ymZD5 z~9JCHL7sAzVgT`j2o!G#<-1qjC0(FUBw1erP*euKj`%$IBb?BID@~g8wMzX8{n7x9{n?n8%5~X0OM`s zgN$3JgMW_k8u|}0?#u%{1;$G$;6=vM^MQ{tZlHge@zNuhf5weRf!7&#kdHC0A#XD7 zj6r`}j9XU#?=ap*p88`r|5@Z2#v5P3{4?%+75E_I=J~*LjOWpRi1GIA;9p?ey#siW zaqCXtqm0|=UuN9+I`~%^&sKof8F!G6G2Xy9O~#FTAWn<%?7hG{j629vmxuG;z&IJk zwa-GF0mieR13t*Oi#*484dV#?GA9h z6!;+H#xmeJ&e4B}@$7rRzrgu>ffpHXARpx%Vl_Ork{jO#dFslSBt?+k;^4CDDc@Bzj(9G5}H%^~p5G2TW##CYl5;9p?e#q(@Y z;qvvDwHf?J8F!GE8P6iGGG4;#ggWE;=V5-1F>WDmGVWrW7UMO%&gn32{1?V0H5TMw z_N#nY%yM&3%tX)xdrglmErvB=$~P{f#W{F zxV{6#8D!i+o@3nF9{h(G&+iDlz<7Hn;6=`N20qHTiT-8AwKKuL%6RQ#!0U{c&H_Hh zcm?A$8MkckZ!xZa6nKa667tkl;rzQ8C&RdT4#XK?+(ACbxQ0B(cteBvHpI9w3wVKX z8~uxnnXEqaXO4U$Wwm{=Rb`+ z!+7PN5NCk#Jn})tttY`h$9NX`5aa28fq#K<7yXNjSK6@ejWTXO47|*En(hxUo<;vU zeR@&e}=r^tAIH;6N;aQQj92J$lF>D|G<%6J9+>k60W!Srn0e;6+zZ!(^y{hx6c zUk7yuB3isk5&oQn&0{%k^m+?*XFDU$Y!Sm=}WW4f8$n&Vm z#r;)#6!Xux{wd&9#trnZGwz`On97A7>(daY$+(NW#dsEZhj9mA$EB_b@-O>q;yO(; z-axK1p56oUnPt3${s!a5$HCuJ_>Q7o{gc4+jF&zI++w`(3E(#4CZ0!13YYz|{|){X z#`XUIuQBdo9EWk|YVdC;T1Kef2^mpKG#v6|U*ZvXYU&d)a20YDp4dC@ahLM1a8J*N!QW!s(4bvg;j-PvEZ`-^tu2687}wCh#<<>r zb{)o@r+_yYcb^6BGTz2GZN_Uj?%F?t{7d~7<|oZ~?K+6BGoD8OEaMvP4+i6kqduucYsc-sIz#(4H^z+J|zcLVP*-mrkDuMOuRy9D?EN$1D?8$<>3_I zI^*dK@IjS}^MbJfxXHMc1zup>*cZ6XxV;*9nQ{FD;5EkG9Ply5D@Oo#8FxKMBp~#?wf&^8Lzw+ zc#U!QeZa>UudM*?GH&7P_m0ZNd0*QS{8Kk6c@W(E4Dbx&&UtXXG{AUe6YxRC-3`EV zjF(OYKE$}O7I=a2_O_s>$ar=k@KMHV3xJmychSGfxV{zm*Htdk)7~2R7~`e+z?+O) z=-*=8!T24U#E7#v8~} zw}kVb{s#DG7%!p!0OQ(S;6KQ?jXcMA4fzn`2J!;q=6w*q$an+!DC6n-!N1IS3H_^# z8)v}l<+{qnKA?XH_!#5)4+3v8?tU0}i*e^8z&ng<=$~?x{EK#-3e3-p!sX{k-HU(^ zFkbpP@Il5aw*b#E?zq5*7`JW)USM2*2zZfkqYZqN@%972%Z%sm23}>{ZUL_|p1lkB z7~}2}z?+O~9pEj-YmWo(Fm7G|Jauc3fBAjOZR8oo8|Q=n0OPgq10Phl{QRf=1K>Hv z+us8|#JKfi;04Bwp8zj1UaJ8gWjy~o;AO_M4)7}DmEQudGoJnp@G-_+CMHJB&M3;Hle!{L8#$(Lcj@?f2k6z_|Sf;Dd~}vA;RS&98$0kjlmTL1`S{0^_Bx zfPazk%C@l2j53~Ifcp>Q?l!=yjMtuq_;tpu{{kOly!{mLCgb+Cz*~$PP2e5Io&Nw& z-LB+c3|#`CuWuQTr63Ve)l>kGh}j2mAB-eSB`0^VUfKOg2p>W*;!^{s$s7`IZu2N>6$ zgZP7tJIHg48_$CO5aZbkffpEekrx@azXtxJjJw-`&NAbr?SWSr&!T^warYUBKgM|J zf54lJXVJgKcw-^7+hN>J15e$_@{j%*#?v=Jy910n$OjqE-w6IW#!L4DA7b3V^Jal@ z7uQ{p@x~nxXOwaKPT*z6b@Ze1D=lU4%4&&xu zfv4_L@-O^d^v^I}x&r(M7*AungN!%+4*ogDD^~*_VqCihc!BZ!-+&hxPh}K zV7zfN@FL^JZNNtvcQJmMar+kVuQIM<{5s?5AL0C0xwt-YkvADPeu(p*@y1VqcNni+ ziu3;-CI2G6hW;7G&F_N$0OR`ifDbZme+PJu@$}`uhZuK}7Z^AG2>wOJ?LPw_W!(G= z@G|4IKLM{Yu73%5o$>T}z{eQRo(sImc;m~!TZ}tp;2p+YjGwwUoPX;Q@Xs*reiQfr zv%XkIjw;69!{vQbE-@y23 z#;rXu|BR<8|BUA;|BTnLUnb*OjGt%R#eP|gXDR=T+mwICtrvs-3gd0$HO_H9JB(-1 zzrlD7xyw1mX*1s53-o9YhVyUXJWO+r{yO7r%v+Z68u}ZIXEDCXIr`@ruVH+P@%G-J z-)7uGUShn4yux@Ed5!VPfe_!}{2<^B#`Tv2cR5G@Hsg8Z+C$;|yU5d=qaK}cJqzt- zIX?t=j`7OD!1J7AoFd~o`j;4Ykykm#I1b}^^lx&G{%yux%x9_{&QIk~(62MDBOheE zf_X3*&!c~VaTon<#`DO_j8{-+jqyD4F~%#$fPR|JBWO6=HU$I9E2y^l8>QGee1fA4+o``);Q zmA!U3`<$6fLK0#Or^^&?jYi<8+bwG;_L@j$LRDh zn^9MDy0jj3iPHtDqv3Ru5#zO-E^k3y$LRvqmpR=;{p&ehruqg>m#Dsx(@h(2Tob3u z)W4b2C0Yjyrwd!Lj+N78sxQ1tx&9K>S8=*P^+irMQU7XAm)7C95~s_wJ{nG!*!pw2 zz}BDFslLqV5?g;x7ufo9x{2mx~}Ed7b(f-ltrDnc`Kv zPVpkAOLQF7oGy?qak{P)9v6+h4zB}fnxn4ebYnBrqd8sZfV!U3we3+iaJso9>PAji zQGFAq>zZPGJg1A)zm?Mk>RryHqHHK(gcmpEOf{xzI#qCT~pE|MP2 z>1wK@=X7%uS~pI&G)6s+(@pJAH*>nVE$WG!t|^1M@R3`e(x|IAT`GaP$m!NbsH-_$ zAiXiCTiRf}meZ}RQIFywmneN=3HINeIW z_lcZtVC%!_#)dfG#+%-{=wmzJ$XY0f1YPLR{F46iJIbEdnF>$(@*2m20Mp_38 zr<>UQk<$&dKEkJz>#3pjQE|GK)<@)YiPlHW=^9!eiPI%dTxSiZ3lg1&oGy9OdC2Jk z^`qx>sR72va=KgvbrY}GM%~Qmrdp^ca=L};2%l4~n}PaNak_=-m*8{*^(k?>r9O`9 z!|5W8tK)U5FLSz<`j6psQ)R4goXYPS@7Mc(*Sp*GKk3UF39a zRn)yW-B1a24X2x`qpsz2Lp9W+IbEuYx}MX;I;h8Tx|Y_*#Oa2b7;olunbs$f(?wbz z;cLou5NLgJa=J+Cqvmvh)~7M2OSC>(PS?`<=r~=X^%>9U7Fq`br<>^h7{}=Xt&f@0 zC0ZW~r;D^cNt|w?bx?gvxjqIuUL`o)MC%}Nx`D1IKAdi;h{sFE=>n~f%;^?d{}@h} zX?={GuBG)k!RhK9IIe}$tp`!Ja=M0e;d{z;(4D||6{nkzqb_o~WjpF>PB&0}iPHtr zHJmQexLQt^X7ohy(Q~@I4|M~l8;+rFLyON>_*+p=~5i(7ETwp zp>E}L=>Y0Na?15L>_uJ0>87Koi<~YVMqSP60`)0zx^^eVYdBrnin^B5wfj-mak^;_ z>N2NWXx;RjE*-*n1E-6-P&abAOy{$S)AjWKftpEIUc*t|AJL4#@6%4A@6%S^!c_j> zaMd`}g?9M;bS>#V zoUS8X%jp`@L+o^=ALCe@ua46#q|0`??epHFFkWw`Lm5G!{$n`ZMDYeYU8$p@_*hQY zP`uGjSIX;1H`(dRu~Ci2e#~~dEuM6Xovy@dM&aj*t)we|!>Z_N(uMYT+|z-ke2!h{ zklyyXpNe$$y|r>@)J0A=lkUao64lXgx{34nS2i8gCbVD%eLdTTz)sUW((^aIaIo&|@8*{qkkM*^jZtaeGG^fj?>p5K_J(kn; zRNutu=0L0;&*>`a)5___AdGkONx2R>(nU@eN%!J(iH@U&)2&*pAHwM}=`yEFq{nc& zp6VMpT?)YZCpcZEb+B-{NO}^dTd0nzQ_6MFlU{<;rM5V(#Oa!LsQYlbmEv`rZX$g= zr|U>JaJo$O<2c>a9>+Cvx{mZjPFIsIbWS;66V=bj=@xeWbGj6Y<2L4WO9<*(PB)Mq z&FLcPdQLa@!8);=u8Kt6#ObEKsK;}rgjxx|;M8oNl2y7Eafbp2X=g9Y>XK%5{*a&k~$&?uq?K zoGz2@!|7(Kuj6zBt><`7w}xYV1E=drkK=TSbTg-0seU4-TWH;ct|{khpz}?|={nLy zPM1hmbGnLjiPH_W@c)0&kghymRX)F@D~r0;PPhG?T^ZDMoUS8X=5#CRdON*7)HfH! zItDx4_B^==>PAjClWyX44b?Gox=<49SU6oOj=Gi8O{5FmaQ)fyc5MlaS8=*76m^l) zt-+|PIb9cmy2R;L(lwl}4a0aXr(1fWuH$rVFVtmDw~(&qbfY)M8#rB68+9Y68%Z~D zx|-^kIo(nR>sUBll2EsDx`lMXk6VALqvCXRAl4B%-RzILn$zX(s7su#2|!)L=@K1B zEvH*(Tpg$DgRqXw>DnHs>p9)n4|M~l>-wT@LyONl5Xa7{Q!)&aJn`Mbt|W< z`lBvrQ?9?6bQPyt{4ieRbfFvSYECzhE^)dPS-_X9Sf)Hd!ugUbagoDfam<|=zw}0r<*&XeuC5G zR;b5wx|!yi$mwDWj8EcpO-t0>0{&TlWgSekJ~{1lkilqB&jbgX50pbdio@45#awV|*;98)<#wINd_)bArpYp3;bvOm36k($M~GQPWPh{oNla#@m`#+qdJW_U8s)nKAbLkp&r8N zCeouhU0V&~$8)-|66!IWZl?QnET`)#VtgE@tEkQiP8aEUbv&mVYG9p2P8VyUp2X<} z(%phmuD@Ir<8yMlt_tcUI9=_Dx)-NgNpH;Q2D*;<*z545Sb1KoycXvMds^84+0uJB zI>Bq0X)^`t}f?XGdzdxD!A?t~?#Q)U!{VgN?o-Gq=h<~32-&aE8 zOT<vp!HUjbW!wPrDW!0rosB<%fQkAl5F>?I*45I6|-9)!}vxvhRRh z8*Y1(&FVG+yHWOk#%Ta{13SB+UHogP@doy{uq*i&fG@&+iR`<;_vm(B$jb+Ne#!?~ zfYVigQ&j*-NDsRQ>=|Is2)kDV-rpqR#>CBuHN@?RAtlA;L%J`qmN<|&ggA^?N8FEi z2=Qp*@x+sfXA!3u6pV$WuOME}#Tm(tAjs;*>-rJ}Q`#+oVKLaY8n~-eVG7>A%xc%FFI1Ox@a(z--NrpWK?8RYcn@l$( zU}bn)oN|M7h1?Y%oFK_?f+)Q!bFgi;H)-IOauI}0iLtSABH3-uLLs56uvy6Gu*AXB z(c-w!Da5%!nhGwZT`RgZOdFi8Y`RTuon1@1c0? zG0LgO;+5Nq&t79b#){wmwtX!{|0*OoyJocgbIk~wD{QtOt}ei7Y}*QmQ#My82f@|V z!2x$A5;kXmvI)-4;IJu*=?MS$Z6(yu+bRh#>Q_Oj%WP-C!ErhHX4vV9q}bDy8?>z_ zo(a4L_O-AxO>rsueApG=I@lH8M%dXl2W}`D8+$fq$B@m&oXgpQEWedx<68~vTG$2I z@>VblR%JcyH@5Cf`;%XZ<8xRp<3IUX9M37AxA9*7dEL)`zV{#3MhdmR6k4y~^}rgB z%&H~b!s`p#|5Q|6`*Xa5ZDlZ1KJFwd*}tpr2=UShjAP^fF5b!EpMK=4*pKFV%KqJM zr{2GT`u|OteIsT6IaBn{Rt47ae~ABQb>R2#len(G ztFK&VKkx7svh6c*=c3*<^~h8SvvzdF;NS-j{NI@-OCR;pV6N%RGL5 zM#}%wcx-;c{=e&!&Ckf?S$;9U@!b6611ZP*QyxD*jS2Jrl*i_0qIh=v{*=e&Cml-J zr}c8`=ckpMpX%`6vv^Gs z=Gn)m)<284q!OQ6|14hj>F4^k{>_$TtJ(Tv7r=(+)k)$#jMqIx79NpJtbUBP^c?w5 z@ls;Scy`YI*WV{6Pr~1a^~AG?V~7_L8;DmB#}cn6HWF_mjw9YnY$85Fe1iDD_RrSg z$7>>Vv5y3}(WE@uUE0Ib~ zWGrt}F~wHa=A;rz8O5)VE#Jl%_RkRLR{V+^iz{riFdu6uKb6sKC0S|3qA`fUV~exZ zLZ>3wDpNk%SP5?165t0i5c$crB{3%qqGTvZ#ENCJiIqt7vrt>EE!>s@GWwBODMdy< zGWPccX|rvk;w{@xszy=FQkt^;0|EajrQsjKb_;D~1m&Mnz~)3& z6q`+KYr^KY`=Q)q6_s$zXBE-5#b7lGB|i}6qpdVxD}l^9f*6~wj88+LZF49!P*+6C zw=q~~6DvN-CqLP?0=6Pb3RY8Ml_rrb6N5p1WMzExldS~UY9T8>Dc{CQF#3PAKz_1Q4hOyzCRc;|I5rOl%17b zuvX0}G_>Rrs!z!y+^C&TsQ5m=(Ap>p^>l@V>{W^g*S;4OW}PiAbXi(bs6ME);O$dZ z7+9mc@S$)8_|wu#!dvGm!i;a8!lo}?LYL&~!V0%q!u%ZGLW?qWgtHCn38jJ?2vJiS z3Hx_75wbmRCQL5UQm7oD5%R~j5xTr>Cw!~lQOLfilMwf%i|}6CO-MMS6()EG2(G(= zgr3zxgyW{3g1S|2VfTXwVZeyK!qB2oLV|gKuyxcRA${$kKMt6`JfQacGVu9@G1)E| zI|;&x1yMO0jDz=<7>D$3SQl#!UuUd3b%*c3u@5IQMh}j!23n;HmtkT zgH>ZLIttl#R!H~V#Ysrc9=OG=rju}bP4%j)`#1?7mprU|bG4IDsB-4!tL``nrOJIQ zm@d1sPi%FyT-R&*QF5}7u=97;&B>bO#AA_VMSboqJ`Rz^y=;+oUJju$6|wvP(F81%`FKo zLO;LfiBWL;PX^cR@XXIu80YXTGV4-Tp?k~oo^S8D3R%uIpEseXoA6>r{RP)U+=QEk zQgt7$cN07ZWSh6~wVUwp=viacs%ZsH%R5U(4oxfQt;)`Z4sQ%S0ac6pA$OPA>WnDdlaVx&u%IV`FjH!6j>CqYw!EeFfN^`$@ z2p_yBJuauoAl%TeShfky-Qu17U*`FoL691Rcj>RmD8%a*k2ttGqk!*sw{KsGHs$?D zWwSky*&fXBv7vn>W*)oj06E!uA3kD5YU^^$hBAbYP{TWbH<@oqJDxoLLwUgYU% z?}b;Ay}>@PYHg`nXgK2!B9y>o3JW%aIy@53KR|5gR>z>n}gR(@A5c;Ed%>5Fe$ zEAO{YADnT=y@#Fmmv0|3UW$MC`zE4|&T+upxF5TCdi>U^#p@@`~b zc)xysmOOzM=fL~)4%@!S0Uu-cu~+!)S-JhsuDmMq&p)9i_s=#zWsYoDI%54;y}u{( z|B^vTxhvNc;NmF zaY>iK)itfk%`IoGUv~jF_WlL!Bxg0sFsdMHmQ88Dp(v@@PF6WsiPg$?Ld5cufm#=uu z{P_#jE>NJLB#PR)1q%k$FI1>U!@`Aw8W$-N*0gBRh~~wL^=w(Zc&MgCiRd;ZOAc;V zs?@L!rArU>DO09j=dxu7_?9czw_Ex0eY9$|rhA17Z38P-?9ii9rS>6}EBp4WQpGRK z)3bAU)vBFzUS5s+R;$*mU-jxO``4(^BD!YH`h#lKYBZ#F?YhIfz3a%5WExqg&f(E@ z>mD0huinw|_3NLR*r37r$qgHxn%bz*NqytS@iUqrsTHMJGyjv5$EgsV0YK9kN0-#c5lC*-(8bdYdGZZzx+t|?yHUk1gty}7`XmaP|&6` zJ$kG?7aY9C91;?DF*J0~<(@tFU+vXvUwl~Dwi~^B@4OWrzU59t#Ab_5r@z;y&&&sX z`_6e3896(#U%!PahB1e=Vlr+W>$`|W2a{uH*SXe`0=CCPM9EP zoH%ie$D~PPohDD7<}hW-WY?)vC#0D+ZDI|*eqFWc(__77%-B$C=FE+iX3bh%VfO45 zRp-oET4nCsWu;?cww9bX&scu`{Ox5IEZ9+a;lcw27cJUbeDUI4MVBnu-Nj(|(rM|^ zB)?_LzI9!`{CnFKD?YYfx$<4dRjXdNU%mQGv)I`CP1dZjv|79NVT*O^9@SgF{#Bg~ z8=g1XxbaDYO`Dz$HX2V4+`Re3@GV=;4&A!-T;#TG$NOyGeq_Lo9S5U!?mW~hF78t3 zu3hGc-Mg>!-m~Xwz}~$H{`>ab3f{l}deDIbHx`&oUFID;=(FU|p{|P#AMQ5u$dQiI zj~;C|_t>%4vyUHdGwH;MdJ|5bl%}0J)nLl$(+x+RIn!#y*|W{ZojccL%=z<8_n6JS zc3rp-a^T{{-uo_H3g3G9a`5IWR|0olz3RXH+O_WM;^QONT)(c{bmKKdhnp?g@+F-UwibZ!j;F5E1phF zEPV3G6Y>1hr$x^`dsgi5^XKIczIajQ_{*0ikG^_U>WkIt{^|8=x8yf(Jifhso8j%d zcdoDBzjyxl;e*5bj~^YMB_(Bf^68W6)#uOIUVQnI{r=al1@3Ic_88mdd4WX@UT2!s_;a)SPBR-k%@EV^b*?d;lCn;X zJYMy{&~w$dMO-Pn#{ZVMY(xLvAC?c-e%l;4z;dAXpl7q&bh@#iOS=mbq-t%tdw;ys!Gx2Uk_fgNQ<%;=IB**j* z<@>j|IwiXP&9OcG&MXM*a8Tbe^xjfQ`)X~Q!AU!P`@Y}nl4k!ExAa@@0gzpzUaZ6+b*?YF1ve9JfFAp=wq40jh`xJGFZKHZ+TY2b??2R z=^xDw4@z7Z5dL{wzsPq}2S(r7+_{N4u4COp%O$@vYg@Fd^ZdrgRu8Xz?fTtxx9!KX zPY0fOd2_(Q`{tk<@6PtR^yyfI6KA!TCDR^QqlyZtF_gF-?mMom8)6{|lA77f7fTk@ak`=(LB+ zM>#%Gb*S_>R~N7Mj?IgHaj#Qu{x$!}v+jhA8+&x{{PAb{Pha-7yDCtY+jDD*zcd+2Jsi|%bIpD&_g3xEeqE^$ z-&FZP?>RnCw?0c;G|@9W(q+6J=NI(|cr?;{L(FYg~!_QuYs#ZIiBSL4{qS?Yxs6FSYfd9jUbI@)UL z$rJTge0UbMHu=%8xCgHTcD(o+l0IGVW=E%47I%_ftI zRjDrzshjah-)4^Yw3=+m;hl26tKXx>(WVie=Y0AVywqW6nfR(p(+{c^>(Hn4*6d+L zcjpPtIL51k!{jPmv&=0dAGOLqA(taAG|k>114YBKaW(lt#$LereR zuQtkZt-`HS*S#(rJYD$Mt)rz*Uwf4;@zLG94_~}>ePjKWA^FSdf!k)R(e0YIr`N{O z+q*BHwxp}Pe|ozq+efvUA3LSt+~rsGPrjeITWe?b9a#^R_);cK>EQYOOYL_)ytaOp zVok=n%v)Gj^KSa9m_e1=xz~Gfetq``Tl20t>6SQKPMGyMYMS?|My{*BzKzVWdsL^I zxgL#a6&uoP#FjpRHzp-L%h*iUVNsR*MKW{?@VT(8XrUoB912E{yDT*9xa3mtHqAX9 z9~+LGSp4+B#Ea5#ukFh}zB%{FKig8v#;z+CRLy zuk&czGUCe3stxXZn_Au}xo6+rVO?5J$+Kz3p4)qeXS(iNr+}(+e7b`b#;-g(cwgCm z=FD*qw;3md&TX20&VXS-yI!X~8h&haPV3O;)o-;B2fp;HeeHC$#{c#Cm2&-5o?j`S zZB{;y^D=KLSeJ}~b9&bwA9<-CsdFkfF;^CI zg(W2md8J)3A*|)|n9t?h24pdNJt|nD0kmS<3?W%;&;P_w;-1LN|G%<<@(q8PFZROm z42;A6-S{HqiM{_W-$3zVIOd5Ff0xhJ$3XdpzswhPIG%xVpMN*LNO@x4zsomJycmgj zV!yx3|1p2cH~eM37=_~*82A5oc*gn2`g#9ue39~nzs&!y&&Oub0xT!ieNEZF*p23Q8(EbBUteSECjTN{ zb?4`J<#{CAqy^|_^>wMlE1x$O7GfOBWAWxx;+4;Po5!ZLJs)K8hW+%te+en;*!)UE z?OjsDOD8d2u>2x^T*~_T^BAxDMZ6(R%6Rbr##??7Z!MNG-bDRq?xx<4T$qbLhV@Oq zh_`a_suLKmzL$D^DQn95;!%t@{38BW>#w^1bA8+A^MA3P@~`55alEX*h_}>B*}o|e z`_Mi}*-uXHyq4;xo@e?+p8dJ4oLlA!n9s%&AErK@?Hq@AUB%SLlYfzCTRX^Oe@CVK z9h9;exW1(z9M{6tF=tA7KNcFK9G~S$k5cx{^7#3xDq%j$GyEct&DTir*tHWi^(u{XLv= zQ$zLitRD4eAl6bITUWMDY~Ha{Px%`Zd=~XUEXKCGiTz87MyH5B;-v)icbqkGJn%rNzEj zyqrqB(!a_-W&bSRno7LVKRYh=fBR@ZAN@7#OZyyI%>Mi1!Q#!S#M{ru5P*HMcVQ+yZ)skKiB_V|Hf3}mHw@tu#W9`!VfVR zZObv_#@zbrKBpe9IsS9JegC>a8Ewy#S$$I~@k;-?!5GK)qdxI-{onO39{o99srjq^ z^{K@Fu74qw_4!@@x>Vwo{*}KYS2pGEy;Iw?0XPo>&0muz<^6%rVezVym}ehvKOdPr zu2X$Em3U=7CVHG^DDv+I(2N`uxxJf4rGvHxX?J#Zi3hANptUDl_KU$J_U> zqsKWGFQ*c(^sj21^6`hoTT_Ww`d7ZULD}XZ{q$3&`Dia-f9e9iKOQXJoJzd?e5`bx zX7Lg|54Nwb%t!u$Z{NSV3a&GYH>MJ= z^lzXz)<26k)AMHg`bz)GcW@}%0;HdQn(2Hu#$$gb@%P7r#f#T}j<=tWwg%3J#p_dv zSLS1;IMx`87jFDqKegjwqTkD;s~CUc5B;-vV=D3X{c9>?|14gl=kfOSmHtf>$NFdS z60N6wywd+d3RJ!yCACeW zQ!4SweAL|YDVZK$)wi&|57)o`7xDU3;_dr4(&IO)Z%rlM)<5_7&EhrmdaQjv%Hy9U zUsimcMl2Jv`SYyKk@9%xIGc~R((ijJiqmlMdd}wa14ASIBmE*ndk6aUjtKM%?cEzA zibhr!13(G#tK?Uqx>z)tz6{1R6Xzoln_|!wW+BV-$Y#t9C{vzHc@h_=`H;c3Uh){K zL;0#C)J^2qy+_+ZtR9W=24WSL&$S`QR8Rd8>llc&9k7nIJ+jmh*-X0VgSL@$p%dDA z(k*1`h)tc*uOVjrv+>zHSRZUYKl&Mq`Bs{bG!AVu>Ed{_jiei2<9zh*ki|F10P-0^0|j~#Dz98JryzPy6QAvO_9Okat9(=ud<@&uMo ztfJo^+3zvZ5~@pVB9@8&dB>>Wgf>k*;H#P*+Vr))2GbgRMH$ zjl`NLwAFnmp04BUI%$odcw!ypsTtGbFngSnzu|FIcfmY~Y#rG~Vk=`m8efa7BQ^!1 zEd-H26j}O?^>t(mWE;u0603V+{NJ9(){&Se4?)(ALRJ%7iH*eCzdY~NgRq{S*4;?9 zj#xby{Z_L7bU)G4{mM+tj72BL!zM1MEwzn{j+$G<=ojXmN>gKiLBK(~C0Zmgwky0e!*;&4eZ9TNLlC*hx#wbR5A4w!Fl=j*SQTlf?r@ zI-lXICT+Gv=QGqR2mKvT`?7jsH|Re>qQ@ubzZ>`y1OuxF{`6vkbqd*Tid{QFqU)33 zrncE8I&YyL$hRcu=zN0l{6I^v>mO`6K{b)pE720_B@1uZc#uC?OxC>p;aBP>tKX9? zYtIQrOS1L@vrXp&YjU#u`A7aa_-?vn!xv`D=ipgJvfzm4r65b^1d}D%;!L))ltDP6 zT_SF!c(-Uc9>bJ3)V4J7i{OfQwed){GRXF!ClO7GCf0II^*`KOHXv?W>%J2Vsx-$@w_-%}Y4U;G(e1$G!e8vYt6 z+6(q-uvbvSUIF%suvdb;GVE1g_k_JF>|U@}gE|mb5#lOBTt$eh2yqo5t|G)$gt&?j zSFxH9))&5`S7*1FaI;=@-}aF~eItFFMumm?hxi-`1%3J-~K|x~8S|UuxwxRvQM1SpC zB23w~1w{vhMMZ}85Aq8M4Gtj@NRG*FJFZNzkS)$nsoVVUd{#X#rLJT4f|NRoH!(jA zV5GYGU&M5=JR$sH2i(i7aiSlSzBq>Q(cZRF~(`qI*r@p`iLG!9?i z!o{nXVZN3dm(~BT*Ju2zt>k3(cXjyvv6(VC(8k^uV154T^=X;)skZ<9!{L{&?^;ud zxBvbi_BbTc@0;4?v<`OlmG2K?&+Ax!EZ&?-yzM)*(xkKfVW9jnr|rBoyh2u6kyT`C zXXdc^Lg?p+1eTy zFV{p?)k3zCZh46D!eeATvF;JsWiZ}Y7FjBXERt=wjB%zb$eOFjYO(TvTpig;tRj{_W1jX4vYFV* zbZ;6@LN*ecNf#3_Pfsj7L7T0YW)03uMdz`OozJ9OE@Hgt60(}^?;5hTb1`3vK{gVb zNw*xM{Nt2=0$D@0?@Y*9!xUqdrOXi49a=eTZ~o z)gr7T6I+SdI_c@UBAmrM9q|u8T~`E}ua21cEfg=Fr*Vnpb7*VL$c78Z60N&Hwz)Ow zZIF$`tiHAY>Ly}=F$GvP=+{ymnQYZVoVWTBvWeJ0y5=$Y6Oj$XCep>WSXXF=tRvQw zuDy%8cn{e^Y$DxIg3kYvbp8{oh}ryjmWpD$sUEU{Y>}=T7J8i3l3&vVcILStkLv7U5oDb%IX$Ra&1nn~yTGd!SmBW8Z%eOiwP$P(Gi|AXmqSx<3>1dNw0 z$W~$%v9JZl6SpE8iS?xG?x3zupg4M55y-aeLtQvPam2d)Xj^w8t9Bt9h~+r6&4(%O z2(o+>SxdI*B-)l!$hywAJa5 zRb*@2(Y6qq$Z!5e{d`B3$QC>>UQet`kG8fIvX$7}lIniIeBDQ64cYAeD-J>3NUR@> zHuD?Dpe>C>7RmnMAC9_}j-yC6^J~YEpV&-wm|ryk{RU!rJlf1}nuxZB&ObHT%&&Tn zej~B&UCMlO6I=&fQ)D&SEMM?K-9W6ZiZ=60rO~z!8|b`Yev^oPZ9!y_Z048Mq*tIi zbpA5GPz-egv9>7M%rCyg`Iw017b)i_HKe-4h6X9~P5IE)=0_IEX7x=q$X}EEWE+xj z9Et9K0@(t*R%zR6vI>Io{YJ1U-~Yol3*EMQ>sw%5HJy*v=4k(I9ZNOLldB_3WdEom zv9Vb{!as_@`Vk~!GtAdeA1bnc)cGIhWhg{_(Dg#%sy3G11NWk;${0Y(?JHLl1ivRS@9 z8P7vCyS~!(koip+Fi)EiStOhJ4Jy>7OvnP+%&&Qk^{vE4Ys&kV`YqaKV*MNPe?>MB zYrmw-ldIr(7GjZ@jcf3ve&~85F`EL6wb8HgM%Iwc^39~HCG_i9oEzFkVk@ny^$|WU zs}hmTbums`4_P8xu8+2gt|M0Ri*=|jv7W|NHAdY)EH^@%^~tmHy%EaiUf8bue{9OG ze6~f|mH(HD?Y7TDIXF5wDZ8VxJ2+YB_c*g3j$`SLYz?A5I{bcox#REsruM)08@m49 zFLXh_-XGZzh-?f-Hu)mMG*jKg&cF9doqq4P(D#P35@zMI=_(mMw6Y2P?>G(@@{55|#{>DEXUo)-G zua2+o569oijxQa56&-((j=zfee=)%S-N)Zb=Z{3kUqi=VOUGY`r1PR5vLTAj3#Jc7 zTO3IGAY>^TSvvq(AYC4cwqXdej_RnXu7<{u`%@kp=NIE^XnZ4$r=xMqG_IBE=&3&y zjj#U0_$tb?uEjo#>yh;vkj?8z--@i-O!^jNwGmmf3E4`1-FCF~+mN+XN2IzEjicL0 zd4CvRqVWwho|eWp(YO|>Bh&Z-jW7OTe1Y=V{hwWD+4YQFH<+K@583(7&MQ?i&4XAg z;P1UM**{KuC)6#(q9fWr{B*;R26dBj>V6h4IiUZ49bZr5>1cc-<+FN>)il25596C@ zo+=mY!|aM|bVIh1E~Z7>k`CGAjx3O^PEYY3$mR^lDzaI9E!CH(zRcCvQ9TXS*VDMH zzD)JBRNp}3YJ21HGp|Nw>tF~+TSMokbO3Er7_yp+7bxDi2K|Qpls}SqIx?%TABDDg zCh;O<;|O9sGK;rT9W}+PDW3JOUzIYR^{=9S1nP(NZ(W%(p7pPbMP0iOSwrKhxPDmw z`WYCfUx>{5*G)s4@83%6U|oQIsVC(xrTASb^F&&24fSi2Y@LJ5`q#;5t0~_&A8q4Uil^(nc>>xRF5gW3>8GGyMfobqXX|fVf_{;kKU;s@ za`anvBg;Buw*D&0mv^Af`e*BJUWU3v`IZQZqxIJiTo1zS$%B|s*{g63)P`~Q+Bik@(bi=^+ob)^J1JfGxC4@d|71wApCq8 zJ06w^1@Ln;6OjXY;|JRQ_i>{7g@*a|3F(J*!-EEttsU;yJu0AAP`^mAU@b8sEYMD2 zR_V5o43krlk zScqg1@P8+d>hvCEBaRyjjlFIO#M;u0>wjRCJt}>^XY* ztRsdc5+ zyk+CC%}wsBx11V(sA0AE3k`N}Exhs0z>gccW=_*-W8LhX%a(jFEK~K}Lua&ZJi*_g z%fx#<*Z7Y$4DFug^t3r27A%|{Wewk(Z*ZLhi&a~j^_C2cUzTpOeB#})hO`s=wflS{ zxZ}Xhvm+ZUJFPo*vfAh>^Gc1|@g_d(qF4He)@{EpSmyL1reH{cOE-$PHg{^dyvW-8 z#^NQT>YizFV9xn-mji3m8a=>k|En>1M}ECoz+rC5XK#*IPwcb)gj2R2d4)M1FW&`q z{r2&ZXU6U`kNNwnHq4xT|C>BVJE!kZXWv!B##L=Mv0reb2_MsM-*CFXvyF}Ws7J6dt#^6$$8Rl`x?`|Vf>0dId2xv{j~h$ zrk~1mEa-l`(zApkM!BrIp1F1<)1EV{wl`b0(o@v5KB}wNreU7Vy9!+h+P!r5+?H*g zj&5;bME}qiGfMOf|8!&VqBSQMT@wg{5=t>yVtgb}f&W zXM5*=(bDDlkz!L0A7A;XLZuEn3aPK$Hs9Qw!#Tda;`oA2y9S*Kj@)?iK~CRV zIU2XBJ*)7(sEF{l{hz0C7+Iiw<`D~PTY4tFs~kFb$+1P_&tF;WzM}Z%q1Y8 zR>sfoJ=~_85W{P9Q-wBvP)B>EZ=c|07alm(Y;@VV*|#ws7kh0_xAJrB_9|g9Tk2jt zx#y_c@)~l}M@oD_FTu*X1jz9Ug?nM;5)aG9gk#uf9$$3Nb_W8=BIJ{ zCk+|D>uW;3+ufU#y^-zhkV}(;nqU6n*19fjt|~oj?>Zv zog2b-o5WFZFLrD_FhBjh9ZvHvZthpD(Uqs023%Z}t=LWHQQ2=rW_Ay_Ke|TXtlB+$ zd&G9`ImrEdh6jt1)6OiuQAoFWvFp(KGc#H*E_WLj^2Q#<9(@cwx&f47vrG@%x+ZwE&t?E3WS)&8mV_V3JbWvWq|xcutXVKXl*J#r}C ztIeB)RD;4w>@{4BDC&7Hq|NLB>CZSda^JHnyJMm9#nQCjUt{;t67Ku9%;>zW zv~$m`zN?e39jb8NynWK*>t*t7ywk;aC~!fsHv!KVF7FwYZf0-CH7;ovdV6MgHYV7$ zZXD09@pB9a&CEOt5Y@qV!^9FX4jfqGzx8Ub3l0Qradogyw&;Y^&ajC zlC?&B)wUb=t~t7R&z<4tw={Z^yj`95&AxpT5A8msp1HN{s^!~?r13QGp6PP!Y{`yy z+8qeKUbx(#9$Vv@`X6!36Vi27>4;KQ_q%UfIyL>_JPBz!PkH57Hekqr!Ebs+99266 zm;QcjUH?-fPY(UusgZTV=^&3xC) z^1Wxpw?w5KRVH_y@Xm%dHLh+gdB6PVwikpK;@8bxdY;~2t!Z9!Im&CSSE=`-U$m{*y9XAT9lHXoDn?B2Pzq$2u99ccAR$$j$k=hZhZWSt}AK&n0 zT-H0?+;3VcR!`T&rHJpLOY6H&pZ#Fq;}cbTPr1@^O<0F_PlE<+blV+Wv|Qd*+m0pP z?pL7MzDW~xwYT?Z?DQdnf0d5A#vaVK_-;~6rqJtCXD!G-?#a3KB@!kto#1icQQeR_ z^U7v4w%e1vP1>GU)SfH74oJ%vk9d3S%aHrkMlL9yJg)N8di9Uod2+hl;8_({9V%Sw zW%JqYbw3|&=~ixOj}q65Z0X-+S(DzAE)?yX^dWF;NKE$2D}2v%4cajyOxIZR?ZvJe z-Lf_8xO&Q>(fasx!sNY|(tUj3lO}F!0~d3d{L@Z(XLLNcX`hormWjQpPtR1RRq(B+ zbKjglXWjNFa?bjGg&uFHGUnc=s)>1{62cq3&fO#& zXd+3MvyivX$v$^kqp6!tC!r~oJo@b(F5tONJ}LI9212Gtn`XbT7e)M?qb za~Pl{pf8|vgW+wv0~!E^1KRfU4QmYO2k-@i4R0OP5HK9j8W7aI;jngq?tq4XVWG_i zbOwY1ngIqF&3X&K2rvWmdv4Er3D^U;4Vbs;)X1-ZRe)1~ksFRrdIQ)1I1ZS!@WHlt zz(T+Sz_z(h_FVs(ae(;y+b=8z z+y`t2T=>51)MUVSz%sz8_nQun1iS}q0vtXv`D-lT2w*bc>-iDyR{_ogMgZQ!f3~v? za1k&E@bdbC$GZX70Sf?+2k)tI6EGOC2T((|qv92S4zL4IvG>xF#{sVH-e#-^;5ncTVC?G#iwXc<0~!DpeQrD}6W}wT zF<{nAr~AzSGXYM3`!N}wHUPu`G60^A$@RJ;U<@D^;Puq3Nu2;w0a*b_2dW;A1RMZV z1sva2{oGK%Hb8a2xiw|4L;%(R$^xz|6L0wgmH|Y-tq;BXZvcD%^ak|*rX9W<@C~2^ z47Usj+zhY)1^@z|4eEUW@C+~r(ECQGHnRaY0G$ABF0|{i0B`}&4$$RHJ!t~q44@uB zI^MF$IKXj0OF)y2X=>jFYy_kM)L!XU^%7tuzzt9}u0WZCfH*(_K$)$Xi=GE;1!M*k z9a$mwL%>Kt1wifzHM2YeOaRmbWSLdk?Gs=Ypftd3Nim0yfF*!p0EdSA9<=~81ndJm zYGHg;56}W&1iaF$e%BhH0jvhR>$3KnAD|0hE#O#gU{sMWF}VOm0ABzxI7z*4|HK2c)^PHN6|)4qz)F{oxfk zasv(nRseFGSd%|1-~?a|ApfU{1#1I70VV2PpAMU#Sq_6+jQD^k7l- zGJpqwMS$wHTyD<+)B?BwZhN|4jsbWA+yR$M=RH3WP#TaIa9+%GY&1XwWC9$^RPobB zKqf#%z^B~aRs$e6z#Cw7E%9s%z!gve@GO1NdwT)t0Yw4#g2HD%0t5lV0kgvc7A6A1 z0Re!8k^RPf21Ek-0melSocazB4HyWR+NAU5JAfvD&VbEzJI0v-bpag#aemVBLjXU3 z1X$j##o9A~c7PUuwI6RhuLJl9xB+QZK+Uz$d_^OgW+!>-sJWdXYYv49<0Rvst}*aBDyI529goE9(&Fcu(>pESW0Fdi@o zFk$w>8QB1{0Sf^$7EfQ02e2409k8HLx*rDdm2b2WV zE6~021wa8ncR=N=5w#KkSpgA%T4|$;odu)?L<5RBMwLGda0EmF%2(>3dIG2f=m1c8 zb;(*J^&5@vH+%>%s)B_a1xLoFgmH$ zo{NAaKrO(Yx9Tmo0B-?mz?PTA*PH{q1QZ9Xd05I|0z3qi0vKv;ZZrr`6R;W3sOsL9 z{Qy+~djTy=t!v){PztaP(7xa*-w;4Sz$$>RYD_>~fC?}M5Rhkl&*p$UfboEyE=&4! z1GoT|0Qz{$h}Hl+05bs5f!8yp0|WxD12RTjc5wzo04@Vu`X0`g9ncqW7?5w^xvV(> z0|Dm%SsTA7T@lb2@B&b}o~2MtKs|s3P^jC-YUKdk03QL>+J9DyfcAjT0JX!;`Og6k zfSrK(>2}Y&2S^9l4Val@!+0wo2VetWd|tzd?|{4j17JkiQTs0g$^u3K_7|JF^9G<8 zU@Bl|jd|-&0BQi{0oJQ$tvm)$17-nMc1pOo5YP#b0JzxZ;>{U=Hh_zOo2`zTWI!vx zQGlubiIYM*LAUWXmgE&9{;5Fbu$k!J;03m>{ zfESyC)1?P&1_T4r?e6L12-pqi32<6HG$(vSLbVz&6p(YhE>nKMdVmg)X;Is9l>v(Y zZ2{%xbSqvRFbB{LP<&F8nk4{}08Icj<@!~M0c1dZK$RyM>ka`t0b~T!z314hFW?@) z5zs6-n??&r24n-k$L~9Z1Kt600y-V7(W5@#D4+(Q$2rf4rhsz*Pe8<_g8h5|mjDF; z{o>0E?Er`elmQGKlzwSdz#u?+z|uYrvDE;501klIu@Erj2IK+k z?%*{hBcKDo3oxc@mB|i(u7E0l$x@-YSpX8C5MXYz@{98UngPlK78eS9(g;up5D0iu zGSt!pP!bRduv8xQr3;`kU>M+wcjTM)0B=Af;7tb2+2Mc;01e=5nyyFs0MY=u0*>Tt znh*fU31|vP$kOO)Z$K76Bf!;Dw<=r%oC4eeR5*CS>pI{d-~zzw*0I8;0k;6h0EMre zE_D=e4R9Jz>QQ30SAa)=L_oF|5A)syyZ}4|r_yR}q_AMh_X1G;hFwms180{^0TEP4d28 zy{>MKh`7`4=+WsKwOR{*ckZ|*IQZPq(xp4(`u_cK_iNX-KU%l0#r*#LpEo#lYTBZa zBMV3;Pfi#&bm-@7pFh8P)~VB$lZ_fpb+=l-Jm0XPPVnW+TfZ$^=J#~drUnNVE-d_U z#fr9b`u2U$#bjD=Xy?wTyx+dv-8ge*t)9h-HSg@{8CJ*JdzjzxOk{EA3v}{P+FM>vdkz*`1l4otzyJO6S=H5b zS6y<+M#@{!v_=dR?CTzI;sH5KeY@dDZ_FKAJfAz~>Ucc~#7w(lAWzMIcKHOSU(>UdaAO88o z-~M*ur~B@E*Mq+s+nzi7v(FxX@t%7Q->jly)`@SweZ#v49I#?iVPVyAZ@lq;n+^`Xu}6J<+pa(R zS?A<}f{HhG*kMUQJYICf^y%9V?YG}O`# zyxvAbLoc3l;)!3>fAGOeTRri_IeTxk(NT9FdF1Qoo_AiX?WLFgwsO~9FB`n+rXB7$ z;)qxOv&SB{=8cSeIpbgddj7Tr3!Xavq?1NI`s-h}t4<_RFWqaxzx4{N7P+2XC8cDm%v2Oc=+ z%yZ5uKI81Olils@gIjK~#iG7D?>zeELk@Z7)$OkmD&V7>R>f90_)w>~~1ODFw|F41nF2MgF;Qt)(|1I#}0{9;d{4WLmUjcu2 zq73k#1N>Wo{}kZ=1n~b9_&*5zw*~&M0sozV|J%TS0Qi3h{C5QYhXen$f&Y8JzY6$2 z3jALI{=WkL7Xkn0fqx44$ASOvfd4S?-x~Pe2mJpD{J#YLKLY<=;D0vo|3Bcr0{Cwa z{BwZ+JHY>8;D0ReKN$Ge0sry9zXbRn3H)~k{uRJ~1K__d@c#$!zZ&>I1N=7u{(A!d zFM$7Pz<)d7e=G3+0Qj#1{4WOn%Ygq<;Qs>fKNa{N0{l+_{?zc=u|8~C3K{M&&4O5i^T{Of&Y5I|1scy z5Agpx@b3Zs#{>UT;Quu6{{i^F4E%ov{0|2Hi-7;}!2h?v{|n%MIPm`s@P8Ee*8%?+ z@IMszuLJyl1pco8|673nalrp6;C~wM$FX$Q$-w_Vz<+Pxza8)&2LAsC{0D&l)xiHY z;C~tLzZUp+0RLX#e>d>|FYvz*_|F9X8v_5I1OIP;|1-e90r+1B{NDrq{|5d?1OHcn z|9Zf`68Pr;|Hpy3;cf#{GSH?4+8&9fd7ube`nyo7w~@``1b?<&w>BV!2f&TzZLM` z1^BNI{LcXX+W`OXfPWV7KNa{l0sqZ_|CPZ1M&Q3Y@V^H5Hv<0)fd3NUe>w2q82En$ z{8s?~yMX`ez`qvwr-1+efd7}kefPWwGzXAB4 z0{k}z{%yd2f8d`6{>K3SR^b0T;J+L2e-rpm1^#OT|8C&F82E1s{67Q!_W=J2;Qu!8 zKLGd_0{=IF{~++M2mU_;{sq8)2jCwE{?mc~e!%|`;Qu1e-^ z@c#h#KLPwV0{%w=|MP(VOTd3u;C~bFKLYsg0sKdR|G$9$0^olV@c%3DPXPb>fd2`= ze+KZM2mB`j|LcMO9l-xF;Qt};e+&3`0{Fre;Dw84fvlA z{4WCje*ylJf&W>+|0Cf49Ps}Y@Sg?z-vR#T0{^Rk{~v&VG4Ou~_`eVQw*>wRf&VXn z|DS;WT;QJs{yo6I1^C|&{HuWf!N7kJ@IM~-{}%Xv0sIdK{=WhKj{^TX;2#72hXVg~ zfd7xc{}teW3-CV<_&){wPXqo>0{@eN|9^o0-oSr5;6Duf{}1>N0RO9j|82nkGT?tL z@b3Wry}Twv6M+9wz<(d$zYO>{1OH8d|I5JtcHlo7`2Q35uM7N72mZeV{to}Z|5M;!1N?sg z{(l4h`vU)81OKOi|AWAP6X3ri@ZTBu?*;sy2mbxQ|8wAfGw}Z&_-_ULcLDzE1OGFC z|2Dw?JK&!M{7(h`O~8LM;D06XzY+NF4*ahH{*A!@0^q*{_+JkEHwOM+0sj@i|1RMF zI`FRr{wd)9Kj8l*@Lvi1p9TI!z`q>$e**ju1pb!-|E+=lIN;w0{BHpMrvU%WfqxtD z-yis=f&VeUzZLlZ4*2f|{NDurQ-S~5z`q;#F9!bG0{_o||2@FJ0{FiT{0{*Bg~0y} z;6Dib>w*8zfPVq--vRi?f&X;izaQ{_1o*!Q{BH&R^MU^#f&az8e+c-W2>d?){!ak^ zje!4=!2dkp{}S-u75Lu-{Eq@Sg$v=K=qT z!2f#Se+Te?4ETQt{NDopoxpzz@ZSmeKLGsC0sdzL|90TN1@ONU_#XoNw+H^&!2fID zUjqCO0{$BS|2={Ky}U!jFVk2)7W9BRoYojqoJl zWWs+4dlR-J3={s3FhIDPa2w$=!nK4BLNDQN!hZ=D5@r%MB>bH44dEF=1K~QtdxU=z zjwZZHSdUOi$RRvVm`3CA>_yoiLm5Pr|x{(+R&Myg+c{|EHgRT0{7O@HfJ~gkKY$COk;kgs>xF zXTn~D=L!9U&j~jZz9(!&*oCk@;S9nygzpGhgi{Gkgv|(75^f~yPPm59NVtHogm5`w zW5QR26@IOxTw28Q~s61>tSN0fa&V=zn97P*3<7p@6UhAx@Z1*pKiC z;YGr&g!zO&5-uhT5l$q0KzM?%5#dO}d4!h;yAp0996{KFFhckjVFBSJ!e0pq!hM7j z2r~%t2onj{6Yd~9M);8M7NL_cg|HLh0m3PEC?Om~*nqGn z;aAgtG`A5uPLHtjk%1cL?Vat|I(_ zP)vA;@IGNn!a~9?2!A5XB_s(wgcid6gerpk+v42F&nnxs_z>bO$d4m#g8T*I2*~dv zUy}0=VgQvLTee8~A7v93Z$Z97`KQFOkeyM!KJg>ugA;E+TmktDWRsSiSGIil&*jsU zA5B~f`HSRp5YI$h2H9`romReV`77mQuLN4`P% zpXEaqk3sYS*nR2@(%NzJK|@{G0N@$qz2BknH>N z!HEYWU$FcKvS~wgDV42Xd?oqF<%f`O9ty}=vbl@rB;ULEOX2{@k0f8P{PpsgiH9IA zm;8a^Ov$e$E|l!w;#i31B5sKMP4d~uk1lSL{HOAn$j>EPxcu$%5s6nJ+r9Wu@|nmF zDQ=Ja(c-AdPbDsp{DIP<>QjyR=x=NU*&@luSeV!`76bFk{?07N%_a*^N`<9Tp#)0#X*suSX>S9 z$;62g4^O^+`3uD{kl#t%EBSN8sSr<1+zs(l#PJeuPFw+TTg0~$$3lK9aiPRlk`GtB zB>4&*KM04f{K(=4$p0Z9mi!Rn9>^ag&XRZn@=c3hB_D(Q{NhH*|1QpuctY|O${!^k zkNkMz&WO(d{0|q;O1@(G8^tjbk4e58@q6S`7w=D8B>6++gA}hv+(GfXwmZh~#^b|4BYv`R&BL5g$+-DDi&e`xZYzK6>#c#66JzPd;to zUt9+HbLC?c?@_)Y`Pak|60cIeV)>uNu@bLRzDx1n#PJiaLB3M)yTox6Z&SWp`Mc#4 z7H?DBPVpDTsSuAw+%oYU#Ca6&OWY{&bL3N$-%VTv`M=~dl^<2UGWomZ(-RLy+y?pI z#eoq|P`*9+-^GCw4@AC0`Lo1%lb=sqMe$?ggA^}ETrm0D#BmhwM%+d5|Kvjk{@04D zBY(L#NaAsd3n0FveA?nc%J(IHl{jwVg~`_}f2}w);x&ppA%Cg!apMFN*GB$fajN9E z6BkncQgIN(>k}76{8ag<#RC)SZ}M8(SzcTs#9`5eWg5_d{`F>z|dTM&0s{(o`S z#6uMqQT|YIc*MI9cSQbeaW>?K6!${@S8@8pTN777d`I~Z#Y>QHT>eh^{KfMV_eT6P zaYV$^5|>nbNO5q*^AT4-{37|B#k-U*S9~gQRK?p8H%x^~G%w zzfK$j@#@5F6Q4tzAo0$`9TR_1976eB<$D%?Lq2=)?!*Na|52Pj@m|DD5h^SMhtrX%bID+)nYE#n~2* zLtGc}LB!b>uUp(@@u|e21O91ooy2z*=Tp2$ar49{kk4H_CHcO^mk@_SesXbLJ#l5kKNN>sJRotU#b*%*T7GYFE5&aWCsaHWaW%xB6Q^0cDRCvlZ;=mNJUeki z#HWyk&9a#5WL!QoKoVMa8ERXGy$m zaeu^D6$f2BWN{(H-xKFbyfJZG#McrBLA)z*FU02+XH`5Uah=5<5GP5z7;zoM_Yuce zygPAE#SarlQ#^8Up~b%!$4@*VaSO#a0sa?@$007I`03(oikB*GxA^kn2#V7xp1ru0 z;;V=QEuONtTjC>&BP3q1xL)Fqi4!Q^1MuHK`~z_g#RCynS^Pk8(!{G4w?Oe#~iK8eUsxxgr!eE{Mh|4DYi^C`$m$*gR?;wtdcs1e%Yd?iJV&a{NTdn;R;w*{xE^fd0jN(X% z*R8z`+OHuFm*csy0MLF7?dj0I1#v0GpB1NEJV)(y5Z@d4$HdDOmq>gYaTLXS5m!q5 zDRE@Pa}$?G{8w=r#k&*tRr>?P=@IWv+;{Om#TgQhMO=6Bd$dPEyjXFM#K#o}Ts#}? zZ4lp9dltm|7k5njPjPg`lN0w?`!~b^)IJGu*|h&boEPz`#cdZKTpTm;w8Wj&eg<(S z#gh`ZP5c*eY{d%`ms)%s?ST+aO58*7`Ncuiz7%oO#QzruQ2Rc#H$;4F?O6~{R$L?T zr^T5TPh4DD@fEdaL%ct6)wTabdl1Bf5_ea8OL0cUYZF&f{3CHL#k&%hUwn0O6t&Mm z+#&H1#fcNoNnAhi`^C8x?_Ata@w2su1o$u1-W&0I#Ss**SX?vhw-AR*JZkMt5no%J zbnWZVUK#O&W#5twOm-^SO=Rzr4Nf*U*^p#UkZnmeCfO@w>yhn9HbB{{WIL2SOtvlA z0cEd{ZA>;M*(_wglPyDbE!mVXC-un&BwL2;VzNcZ?kHP_>_D=;$mSz^p6pGs9m!rM zn~!WpvTMl3C|jRwJhI`)ZX+9r>@Tv5$bKUGk!*ajQOXV{`=e}2vJuI4Bb%LUKC(l~ z{vvygY+JI=$ZjHgjO-(_UCLf5Ta@gPvVF;hB%7LSZL%-P1|++WY;&?@Vg~$FwjSBs zWb2ZhQg$%efMiRPjY>8;*$-tWlRZ=RRN-It9NFw-^O8MGc2L>6WG|FWQ+71jA!Qeo zolN#a*)C-xlKoHiSlLfy>ygb+wkFwKWwVl9O150t`eeJ3omX}@+0 z%agrKwm{j0Wvi6!Nj6>C#$;oX{ZF=5+2&*)lkHFTIN5w<^OF5iHdom%WuKERP4+d} zU1htHomF;X*>z=mm2FpcG1*vUdzMX0c4*m%W&4voO?Fb*cx6wOtyZ>H+4p3FlWk5m zJJ~g58w zTef4_k^@_*?4Pp1$~G%|qwJ!x1;_#(S!QwVjZMw|x~Co!k(ZF7{fjJ# zR3a_Np2z~^GUQ6+I^<2{9ppV^>jR2xJLFf$xyS{`#mE5i3i2K@@xUURg3Le`9a3bc zB4;2&$Op(*$ecrqY+Ymnq!T$FIRjaNJcK-o{DAxq$z51vM>gc?=b2K zsYLce4n!6ppB!CepCex(dv+ArKF9*(GUQ6+I%Ej>0Qm};a}4D{Hb6EWO4|JQlXk%e zNy}cCwgl3Q{OPc?-G-ccMA|xzOxv}{$48~@(BGtO5u#qbagVh1ACk7Fe6wZkY3t_4 z0ULjtwzW9$_WD!Pwj}6th}%w@d7TSR=$wA!QM%mDeGLM^wdr+rqiMWrC(x0lZ881X zyu;cK>O5#Vzc6S<|Lu;po>y0Q@F9otd*0|8Wae$v+%kQyt$v|6PeiZnqig+YYv(MUr{^v3^zLJ(*ttB{uXFcvhZ8%eSjE^i_Y;YATnl|0egV?#p5vz2 zw;X=#r}f78t|`{a2b$+)Z>@``SSk;F{xRN(Qr%PRh|ZYJYxg6hYOPhz>Nuw3;OTYi zDc@GEb;U-n1wX;4TWbulB>in$o+TUjTVL27TQ6?wb>`Wewz$RXv+82w^KAV5s^N;k zvi8!}R8z8!iAzbAmDCs1#VQh3(T23-Sq14U>Qn8p(u9>Z6j^C~@nB(pes5lDys5Sz z#|lOYEw{74mQkv;shPc~-&#r))??`cOLrDpx{bdr`IfG)94;R$?XAttvfPG*wXbXT zIdYMoHmt|W;#SsBVr3mAgGH@{O)gDmkqyr<`}IuPwVx)c(~vUa#Nt`rdlxYlH7ADS zgE{Tltyx}v4Y}6JAbl}XbCt?Z`ctA=AaG0WMKwy3tnwSxM1d#pHS#k%I+E#Fdpj|ColyU3d#N(B^^uBeygCW%F}0Nj#?9tLyV|=e6J=GIytW&+_A0ack^TJi zVLg@=v#eIL+VMHOXEx8vvbu5n)+%zYA3v3>x|idi`>#&gOH!JYnU$XF2ayUY{HWseU;V)G|X_&Xw>_sdyD5u)XEdKgmL&!@wjRpRx|P{wE{`m;RjyeiPRx~`Ukg>6 z=~SP>|7u^Iv@b9;o_@@4UxT$3V2*Z|q}>(pErr)*%*!7{HVgW$u&%s2`OdrGOGK)J z{vq>PQH~Xnuky29&B_s%pRV()T%M(C(~uLc^Xev352uS&GFNV+FKD4}sQ3GWhPVx~ zX2{;2Fbd$cpKoa_Q^3 zDCf<{Vwax&hkM4o=ym$fP&}`%WDeLye@P!#*-<(eTPJSoFs>EwPOiW3`ub6f_B5M4 zhUDDfl{2jS<>cOgBZ$7{nMH_PS0XOE*zQ9K%_z1VLex{z~Cz{Q=(W|eh z4j#!Pd@VMaXPxGr(_hgWnosCEbYup;#f+++;-b7^yR=b7D&Y<-hgKVdyqU0~Htg7Vz_Kj<2hp#eLiFPaKWJ0 z-`7@ipT4HNGhs!3A2xiZS?O70eOOrc+BN-My+3E8u5)SI!>60&p2666v-cih-78Nq zb3i~W|J40ZSSXz>a>xJII)UctGJqX@8=6O(f+qd*fv9ayS{wd zcmZuYWpx*sbs$S!p7f*KH|`tvqxaNTta?3`pJVy;xr1(ASDr)tcw>Ce`DXVbKPX?} zZlu8JZ*VPfi}&ujAJ)TZ+}yF5IkV22%S^oF%v*CGEwnMRkQu9@dkTv&53infn)H>D$+NbNCV~ZT9Ez z?eW&yvK%WL@#b>vPqM6i%537TUY&&X*u;cQ1ZNXz{}Y?(QQ9k8W5sbRuFoI#=L1c( zrEx1AV!q_#WZ7e8&mu>-{Ad&0i(aG7LUCL@muYRHvAdA5o4?fec-zorUtxz!k#lb z2dVx)@7=?CdXDGY@5Y;tU(l?|r`OM1O_tZ(%y_;vmBz*Jkt&gF^sG2M0f@Xo7T zv`UxO>}kf1&QXcO7riZ$K95n$}tanb+u7B5}Yw`5JlZS)=(G z^AGgV=lf-9y~gZlWKCt#y%UfhLS>Tv3X-~QtW42sUYfBuV`YluN0}ne_si6Hz1i-F zD-&bEG~Q z{~o%(0Wz|hXGHH6c+V%jzK_aae9vec*2|%1dgCVR+7`xi=3>rn;QCC>JFrDyhj9Hb zV^1rfxdrL&@#{O|T6%V&jbjZo4va@fOs{`rN?@`4EX)5QtF^YS$m&M&ZJnV4n+cvv zJBzKTrG&9MX^lhJ(~(c~9QFOoF{K~y9<56`+v;Z_iyB_IS01H+_n;T?PRXE>N@3AoUk5F=f>ua_;5~tR)h-2pO>jCuhF!WCz8MLm+{Q>v^;QLX5MN!w<)L2 ztMe|>rfc%7gnM~|(e<0YM|w8%9SkGcby-d7Tk2})7pKN;YF|OlxXx5gqAi)z)fck| zkwwdS5B0wziJZhxevYmUBHg;KGI2e6jWt>%4(kH+ATMuh9&Kl>uHHJq(6%w#75UcX zOCdvgnss^M7tRjk*0LvX+Z#Ja$9S*3wy;LrGiGP(9ka&URX+OgkRDT;_#)9yzt~z^ z!8~t-ab}1%*jd7wI7vTQVND0b>{z7JPaoFZIJ(jwN84%7vUxF97V}*ar0rf1vw}ln zc88xftjFk>6I+h0CtByl?06(tPlR=EeNfC;#yX*eF^#c&aj6rwc!tp+jo*i*O(tl-y4~uYkcoaJ)9;+e=yQeZ22Aj-1E5{dye%^ z?nsXH-x;%K?&7;EpP4x}Q*-aWxV!f9-7!1e+lOwc`R6U{i)V#&9uMT0;{F$Y1Zo%odxuz-aNXu zDrV0io89HrNkrH4X*YEfV)jcUSZjxMw|1w#olS%BV06vd$$D~9%sxRb^Yf1Ae%@WP zVzvSa@(%0i^$Trk=bkpTVGriSyIT2h>0q+IxL0cpyw8W5%YNF^Q@<8X@j-(lZGb@ru)bBR)+3z9ZW{3LSxmL`5RcD)KKZ1?KATm7iBMU>Llr08ps)LltXcfGN32^+_?aa1W!PF zs@0qGCzmk{e z@g*+7wygj8Rt3%2Sk$L5hbZvdd;exx_6bsVuh-tgdQ5Y;hH{(MF=cqt;Dr8k@3{6V zP8N%mW9N&_w7lUle7SblH^+K*&9aY?F6F1W@X)$Wzh}2ByGYm8N!U7zQ#SR4Qma~6 zW{H7$rh83IS$3mehD=@gji#sG!_P?9%KtOPlCf&@T7~z0?jfG{d^ieQWLf9Wv#jVo z{~bfRw=P(hb-_H=1#?*!%v=Q z-ndhiEkJHmeAl1lIsFx`-Qnk<`(ZttCRT=halTsxySDD~pl#GW4}Ss)`i-z2d#Bjm z8G+9*RASG9i-*BQc)sV#$(SVDeGJE4>Jhho?2WhQw&u{c)K=tJ#enQWjQM9~*-mF= z*?#wXbs5&ZcATUgGoCV5gvU}>PHY{6S7q6OS7+JjcYEohIvrR>9OqZVZj$bZj*hXN z6SgyTn`Hf*r2k2dwt4ECb{^ZMz;?u(N|3D2=!IYADOb!4(xaGN6MBF%)zPS{{6G;ngf{!tnk})NO!)^ ztDRp(c3BhT*ieXx~`8(WqRlUsjbZ+?5CHLodN2cBFXPe0GMt127o zAYpNZD@dn7?hbAj>xM(?@meb;OtL&xkLdU-*8kDBFtO z#RI3jH9m~s3m#yM>?_3nOMeXha`|?{M`hWQzsa)8{AYx9Z@k6sPn)Zx-O5+$wOhC? zj1kF>p#Q;U_(GPwj*J9ljp)P#2W1Ce5Ax`Udyuj*Lm-Uyn2i3EuZJf*5rJj`pz|RtD*laW=@|A zCYDuX+X`fHuj-L{<6iU{YpZaajP!$*+4chxP9MEy7S^53mB-Ysmw^9Z*v#7wK{3v{m#-S~pi_jWsrdXNgkCn_HE2ytBH-C(!6P8}ce1v$Tes_0`zM~;&MIHY9)?on5 zGnXF4d<%G{zK%gY^xrM4$Hr;>!#s5i;coro=0YP=tZ7f$)!x}Q@$X)}M0H_28prwn zI(+}1o39jEm2o7Aj{rMOU$MJ>2KP>I`I8^l6?g2KMbgI-*_JphXHa-@ z?S!$h`=D&QZGN^rqdfG?&VlwYCzIe%!&?_KACzt)eTB(KO5l!}&U%MO!KJ zf|R9J7J2gk#;o3>v+dtV*asfI=H^GIjr!8Ox>di!_?Q?d0){`>1RpAA3O%P*{ZK1arT#-CuFRI4&+ZO@!)_)PG6R!~p) z9z%M1BEA^<3$00esQ1zFdh{E^)wnp@-b8xzY}c;xoj&st`c7Tb9AbpNVF-IC*XAMp zemOIB2$EHayMn@dr6mn6FW_& zH&yXvS4-F6YkfEKu9x3J?t2`uVmM+&mS!AJH@h*h*Xth(Z1BZw`}IJ!-Se3DJtDf} zSa>#yroyp^h3B;KxTdS9=NF>j)cd2?FkvXrZu zIYZzJO)K3>Y`241y-KHgaJD;jujJ~f?%jI*qzTFQA=nYCULw^xxN zKi`PHrZg9k1{qBg(nrT6Isw;yG-q!pvi>XMw$?KII#2OVexIMqn6GOaB1h=D=6m$< z(QDW-LuqA`FIw*SWzL5nEIPZ$2z?#xG0hrM@6~u&-2R00x_lWo!}{EW%_aUU;*a;^ zw=Rv_b4UuyT=|YGuyG^o6BwfJ=%ekoB(eKu#37$>n$h`3 zD?C<>UCcE!&uHz5+oQ-QuDrrBd1YSLH`2U>_xqZ--yx}Iy!Q*IPfy5Yep_aYkZw(> z_o151t(W^!_%}+e>4LaDjWqe`qdLzgo@K@pzl|}ucp%C5#824`CXjCOaDKfdypzV% z7L~EsdauZ}rYm#3Z{W{+q@TAe*PaQUo1ur(#7gmLy$kOlw9fY0DL$HkYHR#Uu04rV zKkL;&SohXxRhG{5X^3y>*(yeJb#uu9aCdR8y@MR#=NHx;Pt!}+AFTRay1p_SUXp94 zUzlsx`su=Yx;oeLuMSokW7v1sABkS4>6zsqKkgu%&o)Eb7quY&L3wYy>) z?#{LS{+Meu&v|tb*4-HTu0Mv7q1uBR_naMsF_Jc$qWvb|sw_J{*XCXTx7yDqtb5~Y zzOzTAXVCVEKW=%{(;MWShF6dR^Qk3ct4-Z=X2Y5ogp-u{`RhNLPC6`q6rm@4Umu7-NRxwLQ!n3mN6Lg>^T-_2M=5W!p(e5HGBI zwh7q{uswEQn{a-Q68s+cKIm&|n?rsN=TpH)&=D=GHy30c*hYWff{nzV3#Mn_kHSBb zJ1h^g*Y;jz8C&b>ZV`2NGIe)~r8-ZwMBACxdt{ESeN>LU+3$U$sIIz%z(yJ)jgiJO z(s=gAa_o;~*dMV&E*Y~wdTqAa#;shn%`Ozqw6>jJGF&uR&>yT@9Cx4(?m$D*8b8Uk ztB}Pn`29^tk4-ATK3ZXumN@%pe|fJwB*~`qc(@4f;(6~E!!3k8$Ga)AarE&i#w}s5 zF%KI;f_-j5{vqA->EKV|n^f>kDwg0^+A(1}HuyeO=8=vgx@JCUe{2DJtU8m{%35<@ z^4WuYm@B)vaBTMi+nsss&dgO)e8UvqPk*k^YxbHJ7Uw+ib% zo`z3lA7a;7oUk6-Dq&l77TXq-Bi~=o#r8_rUR?WS=vunkaYo$Sf+{pO#*AnVTx|B6 zxNVBmyhs@|K2evcHjPge?s^yZ>U58EiWk1JPB~y zestXai=6J~7uI7tC2S|=`p%vy95sWZ&EvK?-$$Hv$8lDi%yQULT#v8V2}hax?T5u}1rn?`!n(5|j>h-fto8gA43n=6wf{u9&hEH@hCT)veW*yY@LYuN2RM`Y5K2YEIey+qgZ2 zRKMiCV_1(hCajV5{if7;A!{z}`|++7aE*OH`JRHOyP5FmGiJ7VI_g`<8oX=V^HDi6w5mI96|J$3xy)ruz`~Bp^*MdvzAp zW17=auj7_P`$%u>$LBg~zbUZxX4ap`wSGQf{U>-U{oXzN%k)E&X=jsZXTf-qp1}T{ zCG7Qrmm`jTF!s5%*bw7uXPI?vz&Zyhdc`Y$Sa<1H;;-l(d;ifj{s?VrKx_Oe>-|OC zrftbw-p?bfr!%>B73( z<3%}xzQ^_J)Q!s7yA5OEwsHH`PZ!p`JyTjQkIo;xF^P2TbE%hk^!cy({Xtj{&4+5% z!G3`R`G<8kmubVUMcL8~8FnrDo}RVhwk6-K+kZw_j~!BAhjixKL5!m%Eu;I_T4P7D zN4m4f7V;eqrku|2-t6@`uCB;;a8ugkW@B^Tu=UEkitH1kUu$iO+f~T)*S&X+ z=%H`lOWt{#kA3^F9%CQ+zI{pC15EDTz@E`&-|h$JDt1(n9W|1&L*Pmt#M%yfe!q{e zy4cA@b~59|3Dnmy`j*U-N;(P#6L{M_eh9n}+S7Qx)p*`}dKEZj_PU*)266Ojz%2+4edza*da7SaPJfmhk|B$D}Vx^_PDjGtGg9!aP97I~>odYH%eBLN>QqH7YsJAt~Dd=x=x$S)FbKqyoQPY-D>jE zwKB@R!p|#HkEHYS>OMZtZbyPWy%F7y(|Rmz5((ZjqAL#1OdZF5VZ`kf)HU!ReN&|A zUGLp8^;Oc4Pe1YB%E(9h_sCK|pGB`v?Fn9y;N2s7@a}u% z*_lWXr!}nmaoTszvrCX5PDJq2~^7P2YtC@gjQYxx-u19<~a_3+tih_7JB( z6fdHyPH0128}Y74(APxt&@u%1_!rGZ<;kEJlS<2dV1^ox|gr6p?J9}*>{tVjv zMAA+q?Zn~ngVp`)e;+SjAm>2P=EhT&@s!2e`NG_&3Z7+Wu2u2=Rd9!@sDr9O%IVg4 z-q{Vkv4p*VIPQz;mi(oEhwS`;SGSpZBuyxs2kF{q$?|@I~O*rvLpYoB7dWiFRX!AL&Uv&L8&7 zk0PtfTi46q&a+wn^6Dm2kEHeUE)SL4-e8S^tS)a|Ux1wM=bfpC)4IA@i63ri!ln*z zz6I-8*A} zVubT02H3~Pdb#P{JUbNG`eU9kFJbeBN^E9dim?`cT1&aRH^}`a-KPyHZdi|`U(GwT zQ|2>})xCqR{~Nj0&pT5Or;Sz7hs7$bc6!35Q}#)ey^!*0FJkKbJe!FmKB0_rz&P(W zo%g8bJrcZ!?%jZ-bf0&~)FbKjt@#eJCj{TY-r-v#?j6eGR!*4<{qLab=OFEV-kEwh zt(#*vbAAZ^;Tc0YHi>n$&dJhw5xL-b@XI`VA9+T3x$!RH^r<6xHsI&4`(ZtjW)1oF zkp3^3G$|& zZ>AnevxaQw~Y)vl4cG0c74U18wuwt{W0WRKi^C}oW}7(I&yn! z*DkWPM{;c10AqcpxWw$;X`wG@D6q!Y@@xlWNcm}hJbM|XpM`wlKO<9*r1^=wYhGur zgM{;zemJuI=U%;J>X9@*k$3B>d3G2Q&RhB|$hCgnnR+D6PvqVIZJxb`g!7g@?mOms z!(O>F^+=kZ$h(I;?nT0ROaC6(&(AwkkEHpDyoYIH-yz|=rEf~v@A31_)FWwrD(_Fp z8;RtNzR_o_b^q;c%Lt$h+H8wj z_<3jQ;WV-0EGzEFAFkCtM&>{hnFCdVp+d${%_Vxj&$DNdSzk~GI_r@BHwU}CuI2oY zXLY*H*pR75(q-mNdga|u`nQls-aJ>=s{czL;OCvGhtv6Uyy4oJI`bjdrcnNBm0xo` z=5pQt$+N4F55whW%+R$VWY~X3rXEfgD~ws;)yx5Ey?s>U=|c+pcz@<$!=KZ(k)6M! z9;&qeOTJ|M!kVY?eBIk(IL}&ipL)vFBk5O@f9l_PHUU{v{<=3Gxx>#tQ;(!yP5%9~ z?>CS&<*$2Fd54M-ul$*MIK8WOj%W~I@V!ZAQveg)dA&|ek^iXaE%DuX9!~4`zrEgBC9I+8|Fp*L{vZ7=vQT+AURsV#pkGe8e!1A)dz1U^ z{xcLetcTOPF={1al=ex2%gO=P=3q17u*vy5FXq_;$TP}Ib?#$c*A5uqJ^g27>fvL1%n@{1z-#ZZxLGEi)5cWVC}`z~8pqlt6U0gwKe zgx%vmKdgKEhw*1Nu!lIbf7r8a!cS^ou3YAy@8HgTVeD^XZG}y&vaED4y_WWHm$Tmm zE_(xf)aBkDZj}|YpT9Q-9!ME}dD^!-Yq)HTo131>_m)3#aO~V9rR^nct!s1BsNbo4 z$6D*TC}GE4oUqgXxpC+t(@6P!N@HMMCI6<-B z^Ow5sHj=cO6$yI)vG2X-MRjbm8F7AsU)1YyTg%|KmcngKS?!V#w{>;<;5o(DOGEsY zBx&Hqc7)Ps?`?h7V6Dap`hns$?|qxTPuP*j3ct)@-SK+aE3Eo;+$6P6zGE+Eaz2X$ zKF_f3an0E4+~V;(y|r`P2D9>QUUt4s|G}%9i0<;m@3Kk0ZSk|QI1$~C(-_aUMM#iO zMEB!#uLXu?jm3%RiqnFxce8w3f&^#zM07u&{`EL}w0X6$@Pg5gLHcwJJZ0*ur0Jv{OY-e^$YpnVX~MeOZ`%>&L^!@; zL%F3kBn?vcqn9qC`|s98dwDL@W+Hk}w|V(?91^^LU0CC5~RtG#bgY+Ysf_B_(^ zKk7$zt`WE{@Q!RK;~w8B95*KZ%@Ut$!bv4AKgN{0M82JlT&w(aE+^wq0xpHFU4Y!7 z>*C>XeU)n&>0>(<+K#lBEwSf~yPDIGypy=RGl0bF?t?4Y$zSHklUqj50qd`IoFtDo zSMvk&FeKplhxJ$meH8su@cRH>pI@dvpOZMEXU2Kwj)upKS2FeESGF-Q{f-)_-cBY%A@^acmfq{XG$5=c~Cr1Dvnoo#{QgXTbIK z^^T(i`!1KWxUrQnpD3oCiX*)2f_!@cNo9F;AJ!cgvmvSdro)_3(p%VG(B#?&b&-p$ zt@lFu@QdL2`)R|v+w;#DH=%(&|IM5-?w825^${?#G~f0>7W-+#dW`jtzsJ@ey$d{y zw3JxS{d6?e<*sdV4N-U-b(E`PP4JzHNP7zWv~*3+vvP%y>-xwUl?& zJt~}G)ts?MMQ6>nF`pn!O#}ZokF~$_6y_}*>d6ZnXS$B>=E^}`Hocs09mwg~UVVr4 zG`5V27-vIdoJg&){YK>>A0qQ$`LJvLi$4nq&KnNvKXDFjzndoyfbYHn8%!10;ib&= z{?F^*BYHw-@lUm$>;jvUQxG|qHhK*|bU2QmE|p(k=OW?rY@^rABD!p}4U?_w^L+aw zvZk|Jb#EzhPY(6xo;BX--*D|2T~q(ad8LXoc8%s1(X}VC8Z6hkXN~4Z9Y>z;m#OEA ze0vyKQ<-${31sKou`)?t>&tv=)iu9N(QBOHABi(orbvF2De`>3Ox<5$%R+wYtXJK? z4jFW1fSam!_Rn&S-h&UTdl_fFM&p=;pXr_TS(+oZpxS3^4ee9kqoFb<^>l&liY$!# z_0d+9BYl5lQE;syMOh7HBd@*V>V5bzODG_2@Ou6OP2;j8*i}yr{Ft z^Uxz@Ql9{Z>;TG%jGhe|($kZ$^}&_&cFNQB^Nx$j#~Z6jyK8|pAi>-2+}=Y%dhp}-zRf^$M6x;X!=2ZM7#2Tv`qeYy%p&jpR>Za$P_ z-P;$~4M?yK2w9SfE@RB0B;?N#@*~j=M611s^?&s59UtqT)K|T>(_~U%b!IN0`FQQ#D-}mMV z@NnxhzHx=-Z~Pts>(+{uDSyucW!Rw5vNtRoUC%^yu-K3hC*!vrni>mj4`fZh?Vx*u zNPB_TPBQgKdhw=N2f49oyF#0@ec|ZuI1Gk$$FKJ0tnJ$r+BZlrXASG#Z!lD7zvD30 zkI~=H5w;ukeO1Sdi|rd1+JBMYw+*5?b&wfnn^k|ypnc~;TaK*mw+y29Xur%I^&1C~ z>!IH-7~ZzfzC*&lUm$$~b?`)?SC5%`B#qm*;n@;u_Aaz-k>H%nh_3cCG?}xFslUBA z*Xlm6--_sdJ4_u|Xd57-++NlK(r-Xoi@Y*r>X9@RW9KF|o?B?YJ+E-|+{B2k=M7;) z{d1vRi3Dd5Ms>bPMjWr*!$EJ3wtL6LXFZ{F=6Vk=v|MZFX@?Y! zo(CAwWn&$Qp8q#|5Ez;DRE5fVPfEqaaf)5CEx(l0Z98xjepk6xqy564+=)$`v{KXPU;;_)dd zGiRXEd5 ze#*L+ZgMQQx_K5vq-Q9C#=V2^P8v4KUTX(MsqCqPMQ6^JFo8D)tp!N zdZE3JO`(=~Uud=G&a(43{odJNO*a(TH8)Zo|IDq3uD+Hzisl+U%Zlt%#PJvzUn08d zwk6-X?klqMkYEo?L=WwI>0MrAuOh*|mx!*scwWr|{I)C-{!NAOHSbKy$sV^YG6FIL zF$Q*P4E%}TQm`lajU^=fTME)AKgGFsWnO#F)Wd1~-%=QMdyH5V*H+`RAHr@nAbSFP z#GYrLX+OW)s=QqNc>3mCyVcKM_rv-sX~?ht*&_SdbNn{p-{_lMKE+Nyifcb8AD_D! z{CHU5}#VWs_p!)UBTH630Oao`F)w{FSTp8(S z?Z+ZJ8wt)hi|EcS=lM)~*Gt-GNHE`w=vpIS*9*=c>wmMvHhQ~c^!%}i9y))lcXra= zKt}Tn>zZF-_jY?{o=@7;{^aP{Vm)Eq&DXs1#Co3QEbnKqk36CAL2FytMGGx*-F=$^ zn@_xj$VC-i{jU-STT{)GN&7Rhx--Ib{c5D&PaoC!KAGwH4w3vEe!$IRNn7jjV7F{Um=<2Z$+-F4&mIYZfCxnwAo1a zIInF*;%Oc#UM_8?^$swK1m|o;b*&>(?3c}mCyqFCvEZDoS$ER*kZ<)Yja4lw17|)x z&iR;6ltj+glD_iE5}Q-)l`B(^q;YM+J4>q-TldFEaF$j?cQ(5+YkIK6UP6NNu_C(S z^p#ov{U!G66(yr*VMX+btDalce=lc^+=o5(Id4uH(cL^fdOlUx-JCamPs!-{R1sb6 zq7xhG+DV(aPIB}Nsfh0S4LB@YC2cn(I72F;yExbzYLoU95}XAU(fv5R(~|ZdB#0B% zN6(8I44oG>vg&zJ!?`84b-cu0P3xPiYi8GpPX|eqp=^F2~v8Yb@S93;FB#wT2r_Vc6si%UoT`QBL zXDUVX)tr&kGdF2x&r6P;krdJWZ_?kBvKUS>5?Uy523B;MGH>9!aaQq|-Y`sOu8WZM&4Qu3h_3f-!FF>h{Ra|0qeuGGuB7cZ(JOzZ9!cZoC}ZdDbS+ET>qv0! zPDJmxvx}n@vX~?Per6XC_3X~s6wPCKMResw-`}`v(pDnD*(?#=kJCP%Z-fMA3`TUtY2!EC zk4xHm$0tY6REg-J`fNHlX_q2FeMa=voNLm3WYT6F#j``_nnbQ;oL$niyu=Q;oiW}& zGbE~GTOS%byCix~^V^9Qx#rd*-q|JX@3C${g0o8^y8AA0=RPR1>yhBRl8CN20|{&V zkbDq#Mv3-sVHZtydS{eG?#WLu!n5&(zCrvikSA)??`ZxR&$wpMe6{w&ZyU^e8b2wr z4kUaoiS#Rxnp&?fi|Wj?NUw8AB59cqhIQ{ujftzAsnJV3?@W#HoT)K^^}uu+d?#rK z41y8=c@f>!ALnbGTxtg(f!#l>$7W!U0*~WZ=ZvF&9#=p5>sRgW9=7FiE!=C!xFpeW#Vk~^Gj_Z62ytmPSc-C?L;KVC!#waD(A=?R%#a@ zp8YRFckP?K(RvNfULx46C3?cVxLd%d%*wIXE~ zAQjWSbP?Un>AiTvl_{H2H5MzwH@gj~DMiTg|1dE;}t{Pa!LA_2vT+UFGTIce5@`*-?mR zBj$HHL%Op?@%%#SK)+k&*@@Wuz5tvdvu1ep5z&=zOP19fQfj**L7a%LIJB8w?!S)& z?JuJH?XQ>Tyo&_wFQWU;YC5#kPCUZ5skUa z#?i-Bm-3sBUj0XO*(OOdi!@V6GmbRHo!U!LU|s1_`zLbQE&e<>tp7y1o=P}mRb%NQ zx?eB7HH5^59PUj1FYs#jkNwKM?k&_W@x*yiVX<`csYyo_TR@x1}dpI2L51o0&K9WJ|uV-;Hy9#}?M&8H!ecNqq?2dW7eWn)P@9C|>Z;{L#{VkH9j=gb{In-I$Y7ob_ zaNm;iGtuWFC(QQhHdBwJarQAddZGSt_z$Hv?(EXh{xPEa?WO;mQu_i4+DllE<#6s> zJ7)&h@x8R?e1tup*sP2DyuP&Q_odc}RIKflFRZ)YCz-0Ao3vZA=BGVxNMF3pKo)xj zeU3WgPw($a?PH|L&m*jRytcJg%WHEu(K|F`S^GChyBKl(q1)?a^f~-~gFjB_-e<@i z{xdT5NP0g{i>GV`;`zd`4Gx*HR#rk1n-4ku}x7?v-@#u6~}GdL+G{XX8<& zwg6dEp1SuS@}{3>rXEi3=2ZCCD_N&iHn=&}u){KQjlpEfrlnF=H`jaLh`xq-N$VLY zyBrDn_OKq?Dcg49JLhZ(mi#$D>r8S_cxuiRgZup7;3;m=DI{MD!q^Psj%u z%_pRLzb!i(KiMq&WHa3yE$D0AIM+6Nm2uAFAI`GwD^s@TvXl+Y^S)bH_qe{CL(e*C zmEYQ(tl#*>pIIOB{_^{fo{yXJTQfOS?)UpR+coq0ba>;Z)eKJ_oY+6Ux4L~?YgJPv z{b#I_cJ^+j_nYhPY|8Pgo=wTV!P173;QZ8HG3(vj9e;&>i?OBxJR;Am>%C`0*WBHY zH%y#*<~c#Uh#rd9SYB#pB0;>c?(M5$PgO%w`>OQ2!=ZCB;l|&|S`gW*&MQkq7jFBo z%lE2J8;cXs{Wx6@l-h?#kWWPS<1Bj&KicDCaU!}O$NDG_666!n{Wyd7u{OPbEKWrC z<22q=YBwT5J`vrIQ*#IJg#>XTdXUdUx#Ayucew>jp*#~Gf&8O=3gaUy!CPMW5d*>OlvClTE*(=hKfG4%cs-H%fXNa@(W|!HENU**S>ux_%nfKcZwe0O( zi#dGFK+LL`znA#Dj@HR#b{0~xq4&+hx;KAcZ?*IHcC9(ZU^Bu-**2ocXg^pxT%CXj1N4+BV`1yo& zFQ5O9z4wokdg}lGpR>PO{T%zFZ7Z1wi88Vjav4gBWU7r2&DL)1W^2dpREsbOafOi( zx)=!|jD!#dA%u}63iBqe&~;tIwHG0m@BR5YXKQ9Ee|&G>+volL1OZ9#t^QC)FJ#S4FFlV7==Bk2-p+n3cc8e-isXJ2vCB;loB7$j;>;B9 ze96J|4W|sg9dJNPKi-QtLJC6zC01G_Lpm49dRtC<*roG{E-y~(8*qDjI_tu7#k5}u z_s86{Umo+L#fBD1n^RerW6k>*Ii{v1*r?NGE23lsz&JJhf)$1JGQ%av7Fm`T{ zypHxXx1PoydLDE59dGKL9nQ$B?>w~ZniDWEzDQ=Fz0C=ze@mfiTXuFhBmW-i9loGQ zZbN&jx5m3BFpk->v%?vA*%!0t0`vrm9q|MXk0(76l z_C$M{3sB?lMzd|%+2M@5dznivHLXZ~Lg{nKX}JINBH3um%?@Ye*-N=`^1p%7%hhl} zLy>q-Fl~_?&d9Tuay``T;H>&;cru!6%gqjFRI3v%WlpUvxw@`Z78g3UVl5sxMCfVVPJbzNQP{)0;>aF3^ z(F$93b~q!?pOsxp*(kl<8a@@dPcrq+4oiBTKPlT+UnIj(df6KOE2_6;XNNQL{7KoK zDIBX%df6Jj5UsIgXNNQLWY5dTwIpX=z8OXG8OoTKkMmlME16j&y-qf5k{wQ@=gTge z{91OB{Qp21WwTr-ZWiC+vSnw7GxF_auD>Y#>`9b8*Pq6~KcDJJ~zlZXZ^wsat-pbd%^{*?E z?Y8{va7O+;%)J+#R3wk0z0K{XfBPMc8)DigJDib!59Lb@?T_|0_n!VOfL7Y_v%?wr z_b|7gZzAIe+FSYhwc@`Li+MGR6dyF(wec5N)<)>)+GE9>P2G{7Kn?ON*oqrI)SYN72Bc zrfssr8F{qqy|hX4a*j(Vy-hTH$Q4Dhz?PjI&dBp8^^Q}M$rh*M3QWQWu9nD5T*;Tvp~e5>oKB6$}rwdG`l_c9lrZ)B02fwJeq z)3|@4q%A8uoRQC2-#yQP7x5R#-%$D-cp5%!6xRWVnf1*M@0N$_75+JFYt;G;jm$6? zjIz@>*TA?JH@8TNuIIP4^9~~ExXzjx$2>aeY3#K#-+e{$8fyKPMMgO7n-{S~MY0^V zrppMYedi*22fxcv>vt|P!n({5&k)^JBsZbf?^u9l8Cw!l1c7(}DXld@HPf_PlrP#M_}*&g@hy+lQNNl^%BPOI@{x z`%)$P{v_WC?!WUISdeQ^{_HvYA_I!$tbxTl=kUu2>-zGnkmrJ8c@gcLcP}HX+dV5y zN#-1Qo?DCM1k^eQUPjp2*7S!LilyJG;+=DNWrS^-=--NED{6fPFC(navM}$NtSpv! zsCC}6jIb>y$Z}pvTUJKcrtv>jEbpS7bBd*hH4WE{0xuTJa@0D+ZN;+8sTcPl(!zV2Z_JsaC2el8)XRMHAYQz4Zn4ZT7lnEDN#_Le zhXrysg!<8O(zDHE;e7oAtN5VTD z3pz_BS~b#+Tj^mfv!YOfkC;IdLV@FOb%zS(hMQmD%;_{tk3IUlJipUA@f)sFW9xUA zcuw<-&eAxtv-tgHz0$+Z^MdK$z)t)2vNHz@a{%mVj84*xbYFw%c=@ zG0m^<+N|gMm#e#qcSWf@j0TRf{V^lVctSeKO6Po+s+w!4l|0wV@w)r`onP7xx}0My z_nAg#*4@>SZ)1JQcWo1`U&poU!G0#eQuz;Bqjk`}OMlU@_o7nSZ0nUBPS4}?ovi0U zoo8pZbQPIVD%(-7(PsV9!_GPDx=ty+$CYsIBI$gBddv)Uk$7{dJaTcVjJNqR!yHqx z(m3A+uP)`igc7|c!F|$i$sVy%(?DVhYyFbsW{ncp??oTDB zQJ3kZvc=}l3{#)1G_FoO^R>2NK4}?V*Yp+ zzcVtPxTS8|R$KO!a2@;Q)KZz_lqVTsmz%khU)?pAa=w>*dar1`&V$rm!k3lGrLy}lxunv`=nDJ#xyygRBl0CD((917DvOTr7`0c z&uMCTcBVzs?i@$YER`ldD)8{k7 z_Sg`)wX=MPTHjGi4?FF#t8JY-lup}*cz0<|soaS|w(Rt`<9RN91})XORC3Eo?K{`HEp@Io-bqV~caH1J z3xfH9Jg;-zgL9|kfu&O1t5g=~@^!w?mbT0X+m3$DoO_)A>EF7d=+64-yTE*-JtLmq zd}-x4_qTs!IexpG>N4>kQYy324lT#I-^ji0EghuY`i?AzcmDa?RjBZ6Z3oE;>-*DY zKHr`5zh;+7p7dpyeS_nEw;f#&bjF`8yhqHrnP)hC4h_>h+Ak{$op9;trBZ3j(ZBBs>wAT?BYB0i z8Tqx}*}tX7mvhgktb$|19**a_4ldsJ+uBmhoIj=f-Yt>N%Xj_Wb&f0auQJZf%Nlnc zR{~C5L(KWrr?dQUSZ9%Q%sOX;b$q7px%+jNhmPpHbKZgUu-U$((e0Zsv&=ee$~jj) z+bwWNXIX#(w*2(4*B*KMQ6F0iC zm8Z>vCzi^sXxO>5qjP+szq0-s_pXn=pz$mtGt9n}y+2W&WR+Lmow4gxzDB-t#%+f4 z^#04aapCID;vHWiAEVWFS?OVOK3BZk`COPf_0;FV7-tGscb8b8L`GJXNa1;=zUg7- zy*XF0@Low!-<~k)9R*fttIq`pLwwyf-MdLFZ% z@Lov;`^s+n3Gau`D3LWYOQiRB(=M4|J)g@;qjjiY4#P`JWS`k3JLfP;51anoW$*o4 ze{1OP2Ir|N&Mk{{zEj>YS;I422??JPNG@jK6{lb*xz+b<(d$C^Sq3u8@IdS{NY zSNAZ-n6uAw&8M!je1Vp{tM89dHrF;X?&P3AKW<_1x=c;_5)ph1@DVZ>nt6ocb0kQoAyo*@4QZubo!y+nKz66>()NV z_H*A*BG01rPF_9!avhVu$!>pR&YHBe<@@m)f;k#yF)$>&CTb%#;7L>@Bx0OiT)+0S!-hsd^>C8#O`6%Z#9jRkps8~{4O68!hOJ$K$ zHhr1%B0cYM+IY9WbB>&r-sRypF5|uD$MV#!D-oUiX5P1+%=@{Xhu7xCbeLth4Bp&5BwX8%^ zS`PKk4D0W>%ryGln{pTLPns*4@zxoDo#QjlE1KhFDaT8WnH3x}(~p<>y%+jMmvzp! za$4uD_nSF|JGVansORGB5vE@)rB2TNzq+S*&g(2MqDYmgYkJsxgNFCH=vO@|JMoSR z_X6gdcT}A7k##$dF~Qu{{iV8tgs(4^UvJ`fk}WeM?2OmUP4Ohx?3S0<_hxC6itIT0 zSXvr=_hw($0g|8CwH`@bk5k&pDSTt?xGnojzttW&kh#?ht9y&*sS>#n1t!|{N(-BQ z*^Pe5Shk1x2siLPnOZn5tmhmdr0@PR_sQta@-#{~%g}vB z$3VVm$=_wSzw=u?ExoHV-(>yLIe!dx2()*;kKz1ASvrlZ$=eg{j`fPMdC(h~I5+z@7M~Q54%GckvT;ty@o?$#Ko$FcV;N?2n6YH5P zO~)j&_dDxS?$P(KN@d^W&d3ngXTCGqkaOr{Ua4G{&v|c+X~Xoed48F5%%pRib;{KI z+P{0Am?tMPw@P6P_Z0W#nh;u`WjNc0duoI`7L`iOmYW%7eX?0ipZo%xv<0ErtM)LE$4ni2ew0j zo|mvzx=xzM{9fQ1`Fg%rM!OesymwD1mkFreE;BuBt}%Dy8gmCd{|h+v47HcwsB&3` zR@gilVW*#PjeK;u%tozgGQu{EcMQu#t!dK3&b#SXIo~NW{Z^m9ZDCI5EpF);VxDMz zhsVw=mpp&Dc&C{5N)J2lRxIb;3jLj=a~hEMK<;z&SZ(KA;=X`XmP-W++WZ+|J;!a) z^Aj_tQtF&?IqY1PW#^X72xq4qS1$XJerMXW@XqV#mG(NibN*tM;XS`xE=Gm5W_>ck zx*yrPxGyM|vrucg^sw_AZLxE{we$LtZvSE0XFAX?MmxXX;?D6%zjwqnK0OE1@5V?& znG8d7?DEsYE}cJOopTNr@2T%et>-r^_0exTo9`YJOL$b7Jcr^oe|p$CS02q=0Jcx| zJg7hpVxKPO+(DO}8doLRuTyskU@4}Z&y<9L^r#`I14~cuJ=_JO5lt#S&X*ZycuELPq+>f43^3JXsJz;9(FYp zN<*k^#=Riten4dhDXwTQ#iPv^f?AJB{{Ch1S5!ZhcGmJ++BxCdhzn~R{Y3ws9?r<4 z_i)+n&fFeHl*ucob>6RVdRWV@DCC_W%0;c$XEVaiIKX-1NoDdYYQ6T85q8cq*(U7w z?d^UaKC4XLM6LUMdf3&mKsqvBbsWucFTpbr`rCC2{X*-^aW7R_CMS(8liCZ-x~7L+ z9rLB*RnFBTe}R|r(s_1|YoJ@WuEcn$V@|klnXE)hZ2pX}GY;(P7pcR_Z znkGGLu8o!Kc5STnSQ0q4OqQe8ycuDqe7?~!uuN`3t>52B51YR3rmu5MoA0!vJx+1o z`=T=G)mSFurkQq351W2cBpsJK=SM;3cVxABzQH@FOfE$cn=d`=;@gcq7H7VP-if~A z#J{?S_;$>%!h56bcwbQGGcscjV@@Ua8>w^m#+|?Cg8Mq({M0!QnD3y1`C|G_`yqKo!-+K(m?)v)0Hl0f5# zk9X(yGV=A{5Bqc72=OyoA}Fb0k4PA$kgq2^i1>2J~>mn=!p^QbXhPe%1B$ zGGo$&s>VsR6PjiOgAI+Lnre-iRNv54nxu^6^p@znc)J;`0GsAV4m~hBhr+6lo>qvq6Lu(2Uj zGs!7kgRQG>{FJ(e=}oOEXG~8kW#>vyn^xD*T6{yWX1b=nXhu!r#cEm`F5T~Hl#IsP z8QAsrrch%|V`GC;)tNPoO;m^(t{$$y9mZ`%5 zmi;Y9T28jS-12tIC6+PES1muc6m1Lsxh*}G{Ve}txq`auzk4jNvaGcnZ8^yDOUpHu zKUub&ZOS>=a)@QXa;{~_(pf0~d)(IJE*oEM!_zIBEpNA+XL*%nYdPQ9u;*e^j~zCg z%u4sU{rh{CYb;k;F1LKpa*^eH%MF&T%e{&Ip#S>V`2LndEW?&FZMt(U2U_;9Y;XBr zoBl`3Z!G6pwk~(F4OdzgWc~feto6QL2J7HAtZI67eT|IH8>Ks?zov0=L*uln>62;( zH_Tv2Y?SZv&#tNq4Q*%~Q#XA|ea)E@r|NJpH-Ah`NdHhZvA(9VZW;;2WJ5URgh zAlxm!#5JU*z9zKWAIkI2WU#5Mb;c{%kUOG*g$&iP{0SH9GOa3fMom*w)s!0QH)gkW zCn00h^hp;>qf_^WDOyK=W5XmeH8c*N?lh89Vflxq7;|w`sAihR%P09~)is7@RMns4 z97<$!YuH)(54%!ptI1vYCaVT28-~`I#uPdIl+hzk8F5s9=g=jCgzxt%E6-v3^nm^_xCnW>c`SZhB}k$7HsFQ$VFuP2|YS5qrYKnkoFz**Z1NoPWrK zn(5WM!a`|PlNv)4>KZ0am^@?pB+dhzdZ(2?*_J<6YMSe)c~8o44tbmlOsH zB~=HhP>{2&36rhm%waI1(W1Xw%BC5)ja^+;HmYqi% zPM&DFx)9xTnhetCQPql2bjfa$ds8P zN12>vH)#D+&GeadjSbVM)l6sE6&WcSrt5l6aCSI7dz?_yTr+7#sAfXOA3C!<(;ij9 zy1vtz`p&AG-j|l_TU%2V>|1|i-y{3l?V9y>R*(Jr&r?*_HKZrVDxs0_f?oV5DcI|3 z)Bh|zb#E9vqp^|owR_j_YB?w^*6hJV%D*%BoRgY%?koqnhSW7VsQw|3h4uUJqN9|BZS?TkFAn#nyi=>QmVo z_+R87e{i4b|M0*1OIrH}XH2?5?7#oF{UdmJuG8P*$7S}z)^hj8=Ja~-dYu0I-{>D^ z$(jHE-`Fm#H3-{w2{)Q{ZH=F622fv zmzwmg@dI1SvHi3)ewdB-&dy9f&c+8W%Z#tJ@zEww!Glwwz-* z*K(d^#BzbWx{g3bb1SMrF7^ujCm;qlf%ng$+$^n2jSzoKn;C9f1q9%*9mA7}?&$z#FdUed!M zkCRF-d<4FS^cz44*sqjd==b#yFFfD+AdlU@fb<=HWmj&+*5Z|Y&}=-naD`iZG7qo( z7A?Rl+jbFIhIhkNXbts@!=Ajhwo3ED7rAG#8L#&)j_2M*muL^?9>x#&LVWAJ44t1} z?`fQe*5mcQ#wP3a-bORh^22be^?I-4#GYoo^q$8y?s@d4OuhFp^W$9LPPH6?o-sXboQZDB6tIdp2JoKkc9#!Tp;6 zUU@E>jaSy9d3e3Q^Dnf3^;Hh#Ue7YTvI)iUdjF@E`#(!HJ-inQUU_MM`W0R|5B0_? z??MCdde5lOQM3c)D4#|3c;)wK4qopmEvsPLX#L?YN89awjL0KM`+(kSI&J{{AFubE zmZBkez4z30po!OeP;-&SCy!H|lfME@bZAAW>(;N@hV#YMUwDHos$;*}4fVfYvf z4CWpmUO9LO`v_j?A4*@vD??}tJ_=t#+9t~Nr_v`M=;82Fybqs*xx?rucx6Yl5wAP} zZNn>1M)F_}hhy+=d;rcwy1rrf2%1a0!{Mw0elFaKR^pXEpafn{qi-Xpec-=PAWlaHc}pRCfkVmDEp&fct7myr=9TuI0tF_MBroA$KWQU^-RJJ zqij3CYmoOL(!&QwGv-iE48DQ%J7LPLs6Af!6Y}6?4AZrv7ShLI$4b*aZg?4T#yxl& z8bo^KW;6`1Ori03_gLBqY5E}i7&-NXz0Wf1;)NrT#{1!QsGhva?q}1Uc;!eG!3W`7 z6vcB6C#%s~ys`yt#48URM}Ncn;Vh)>rd)=W60dv`t->ea@N>*|_rvF`kHe3V=JlUT z|3^A5rQmhvv9FOg0-Mh_`(t>5$n8jvaZ&h;^>KJ&E$g_H{stckn)Zyr&o46VBaQ4k zAyWr8ES+h}_cn{Xi*$caz~YN(JIYa3qd|D(CCHCg&PD;eGHm0O7hNi{kZ-p}V2{~s zuSa?~y!SHF2(Mg?=HL@>-4&EYUU!(QxmSxEj`zZm$l0dw%Q@sHJ_Yly;k<&p z%8AH>S589%@xf~uSCH;^DY)--WgU_KI)Iqrk74i)!<>;GP zFT8Rhs=zC6K|}CSxDBOPmU3N$b)igU3+j&dE)Z!%3n^du@on@mymA{_gI6YRr%mw6 zGw(3{Ar5^DP5*J<$#{5|X;1gvBGVVq4wN5+ZU1KCJ#Zy*KhAy)PrHZn415$8-fPQ& zU!gp{o0fv^sOis2ul354(0KAHhoM@$@*y-Euk5&(;|pHdAI0#>O0*iUYLLj2R;bhZ?kTA<#ou5SKf(+;iItp9kZ`_;6kMB7KLwH?_JASft+(o zc<;MrT#drtPzmM8d;AtdU06qD`TO)Ayz*T%1fPH(e!xCL`Xp?>&MeCfmm(bxz6vYSs z!FsD`Tcz(ubF5U}`)|sj{3z`CAGVP$>u0tpa<;{Gkvou{Q$^u`UyS#`kF8I_<9{{j zeeg=8{U-uf+4wlzW_=2F|IMTiz^{?^s}#)J!QY-{ABX3l!|}?ozf*6#GJq=a$_T2) z$6?ujO_?5eoAoJfl$@F4a@NHUXIUSCrMV_P4v*nEOI=?dyv+JAe9`(i+-|+(yW|Aq zq=$7#`=PQ0ZKpoUyaJcBe}=vR4@24}J~#zA=b!Ker2Bpx{>#RvVBa<-O`y;v&mboa z?94qe&6|Yxv}JkvI|!~tS|4vam#jy+tR!sT-o$(0$=3Vf^bV%X7z}hYX_VI^XWPN< zoy@X4aDw##xB@x#f!mR$=Yx_`g0v1Ecp-Au5x#>oeL}gfN$-ZYBaM&36N+4-eZU8s zk(L>TYmt_dfQJ{G^j=ttG))jbfiz7F=9QQABXQD{RX|VftT2Z;FWivT6`4#ikx;RcgaD>*%oj_S5v;Xn@h$Z{f(onMa|?@ zPDk_b?(Qzxh_pVkpG*27t-lw(i8MX|w;_GrTX{|om&EYO%h76l7`ENtq>sX!$?HDIB`+Z@Cw#C=4miZt z8NP0P3SQLPrh%)Fwu3VFQ2Hx%R(3{P@g8_K(lX^R_AjLC9)uqwtxpn;KHT^Ke8+m} zOFu!H*9%AYb4lV^)(g%;8n1i!yAf00(``IUnkHdx~HXdH~fXOQlx}^Le(@#9`Jv5N?2^e~qHrM6ChaO=Y zabA~#w>@gsHwuR@v;7VJ&H5<3_c60xQF!s=rrpAD&=bZ7;2IU%C;|J$jQ7HukRCfC zFy~1VAApA~H}QV>1k$#O!8e~~%+)%>wa-v*j?u~u&$2(_m4~imT*WKTM=S8kCr|>f z+<~^@w_0rABHQekHZ%013hzOCeq)YVR*as@dI<@1LV}FR}OQ886SX6*2f@^@|iU74D0=H zf%Q?i$@(Phajq1@G9#g@Ga{TaQ{BGOjvJy5I$pl9R6;-9L9D>+HO90iS=Rlr1f#Q z)%q0dbGS+Gg;mxE;X>=9@SoPF;PHKJ`S2?1Bk(Qj6R@D4N#lm+Ss#Flt&hQPt@j*3 zpFvvZ0Bo{83ZJq*0s9`ABhI;B{~W1Anm!1BJSs8dyhYy~`SdCY%IXg!p_yjy;9DM+<{O5T&G7zs^eLl<9H1Pfj>`SY9 zIP6ofcceQCZFC0+Awtk6wyYT5+`W{{xNA-B+chhrZHa-R4 zYM|}#$~ld!J6?G+O5h{V*JQS}(sfCWETax?*cS6Be_*IhRm&xSxHB z^;I5#*5H-bp^bRuNlQ$;ABLZ%{}Zo_Kf_qZxr*|)XW8~FOIFfPo+BRbhJ#j-2k(P} zUo_)`A08EFxx~ld>93gj`(edvw!gu{R-1M4!cS2i>65VI8u~WNb;HF;9Yc+{TtQZ^xvt|RREflUuTMJ@DgW%)YtzRa~& zco^z}R|ZgTyfSFxm8&+g?-1|#BuDyxO5O0v&}RA@UU@aD#w%|?A$$a${e@ZI0G#tR zZK(0^=x=Br_8lKw^mkLwDBOgcb1j(q*7PyuQ{S26T@n_4Z~8+NKEKWM^EmtzIeFn9 z*7L%UoQ(9C<%bpjH0|tzlm3+>N!la`KSA2Bl!+8|ru~&)qX0eyNB>~*2H>P0=_j;v z1U`XuxpDZj^}IkKy^${03s<(7z7dDFZfD$nm39A>;~J`ejpG`;1~ucAeSTvc!7IaP zDPB4HceV#U0B`&+bzMyz;D$epPr@CND}`~=yK?1@oLuRGk3vsguJpnyOY?KZr)l7# z0+T)p`xoZQMz)a;ZrI28B=onVJtzZ?>V`31_=XGZrRVQOA4OflRT6efF~Sa+R6tHJJ4)9 zFMO$2uFNKl=b&6U2|4$z;3)^^%39jf4_`aP60rpNaKU>9i({^aKFP$UJtwmIeh^BWW5|tzw2wZqZf`sTF(IdvR|&OXFCRt z$dwO|%$0iD%H2O#4nn#em3>hRuY41&#w$NVNxbsnqjLF8Or7CvNb@THhJ^B!11fT* z1g|^;dGX3`kPn}NWyhFp;en?PFy0Sm4Yb<}K6I?{F*sn5sk0CMaw6N6<%*B>Lb_k1 z;MXUa?UjNr4yLb=#xsQNg`DjLhn`}$7i=2JI%@mF6w>xoo^`5e^8j3lwEQ^q4l`+# z4_hCDorjxv51fs3dxhb9Nb@G(F{ha{KKPLJG1zv5sgDQtK7;yz(ft60bb0(!_gV6WYplQJy%K zzKB;2Ih*gtGY0zM+;eiJJ6^fzyj+gKJlg>C##UcxCMcCO!z~R^>{FG9&Pb ziL5ValpD}SymICw_G`TI#A@1&dMbyY61;La>V;P>L>2fb966bEct2b^#rPO3sHOkh z&+j9+0{N+fay_cXC*hlQY_BD>=T!EW3mJ=u55uMP#>b(1n%OSObEdN$S(b7$O5v5i zqP*A1+rYL6nr-BRKUyyrF`gscc5(Ov(qozOn8sYGq#PyhXGkrc_b!BYEF?;qyn7%= z%r@zHPF-@ZG2R2;xR&-IO$zqC&crLPy)jpsiC4};5qt!Gbrby+E<6eErD;M*DB@ydQEfmhyt2W^E{`WG^$yxnRZWAAbKUgn|a^*^-=_7F8zZvg=_gNob-)ZRVT}_IZl+!7F{J z7hc)(X~KAA)icx+udGA$_#j-2bpCne`)Dp+`Q)?oIlOYf^Q;$Mc^O)RSH5VyveygL zlXx$@5b3p%AbiyN7)&AkZK(9FGG2KZ(l!sncdbvrJ}C#*y1q$xG}rX^VY|q_id9`8C8)+`NvwuDC#5cGVZ-k zAI2*`L96jecxS@+C_Lu_;{&jKo$(&H2I+OD1RU_88LxbB6Y8~&a^TeU^fC5R<+~_> zS9agPc!l@C_{WsVH3a43%!d^IsE5N_yXVOQywY=Uo-D;H5ABoZoacLC=e~Keo<87# zosQ0v+7B4d;j#h5v){$ws%d$$0w0Hcrsv6K)=_!xC3(_%@ew$Glkrj5f|kC|^AFJVS)Q!KD`$Pfa`7oR$CWShHjo$29+fY> z@xC$n(*3M_XS;jgb#wA1_zr2{)a&zQ^;-53c=-+aQo&fE4F5G>{CMR8RF7A_IFEAi z%Foe4ymG>gCXI3$(lpAoHa-DwyD8tfE*gb>Z_bwh?WTM$k}u78<=@dldCC10$`Ui|p!XF7$*?14U&-xfVGH&91@D=M5aMUX%J^&L)&mWY&SB+P`g0%bu9QB$_5C6yd6ufA) zi4VgR(sGo6*Ns;utXKNpu;s(0NY^n2yS!<<2hO!V0=HW)Ygi7_^g+1M`XuzfWy(~p zM4BcJd%tbG7e0)%ZZUZ1JH~tA!`8>(p=)hCT!^%sC@g%}csFdeJ`A^7pMvAwGid^F zjr9pQ@O=~SgG;TC!7d3C?}2lzkHB}3bI%2~`@ncNJl%Rf{Hyg5_>T1n*lwLo4^Ow= z5C3X?1ioW^0=E0mriZ6n?}saq(}u7&-#u3Eg$u2Z!c9o)nS|XwGTsBvvpxXtwmu3s zS)YX6HrVv=JnI8+4bo*L;J}ZK_rays$6%L@Cf)<*S|5S0B0U#Pz=5CGyl|=YG1%o( zn-`vkwEh8jxAjrD$@(Phw#n8Lo@adk-fevpZn8cJyKT1V;d#~v;N8|o;U?>ou-j)g zJ)Dbl-6L?j_3}A=9%+0KZnQqB{KCZh;Z;c2Jp%t_<5RFt(vmzWx_3|ZkKu#UtM(dNxuWTLQO6%jW_ZAcHg$u2Z!osgjyc=GRbbpM%?KWP% zVOhvo7F>+9&N29<^(i>+?zR~GYLT;C z_ywwK8`9$xya8!?<-I#NFTpFHMho$Ac<}GkiG9Qiry!^Qz=y1l!PEY0 z&eQzxJL^+$)*oyGJ*LB2DR7R;2it>@CoGnHiW*M0_S(7@;s!^SSsH}%c!$5 zfmY*{=jRqk1wH^DL@Ab~9G6!hdDLII5_QpafhXsi^8K)1L4nf_UU*y^Q#T*%T38^P z*>=j0+7?I)UOA#2-*V<0-VZNFS~q1O-~Vl)pC~)(w}7dS8=k~m%B#uihgb1EP(441 zz;BSQZwgLj4&-Ltui;I_1+q~01?Vp+F!wUx3&?37*u9kH(U&~%A*9vZ$z3V0*~xz z*4+n>IG{i_eoCK&F{EvxY(Zo#^IB& zjhFS+dy(*%BaQdLBm2{zi1)#TN3rivW)xOc6v$fg2I1_Z3uFsEtVHdpzw+f{3Zxfa zxe*P*C*g(x1t^|Fm#;R2g9)Zc(dPm;D<=-lY|EjGW)XfJ|wh{ z@@Z6pkHa=6nC+^jtJFJ%lB(pJjt$b*+t3*p60mNx854r=JG6wpp`0~_^~5XJq1E^#%&Ro}tQ%HX?}OFW zCzRI9Sn7gwANRtZXVJDS*9&`|Z9S|+nkE2G9A}RUaQ}0tGvlxaZbZ&=K5*>0)L*ap zz^!P>dfpd?Mdy(huRIN{#VapBNxX6fO5wwB%y{}aJ^)M4FL3U0cwjTqZ)z*IX*^yz z`T~v@_yF93HhxIogQF(c`oIURkHIqork(xpX{6V;P=oh{10-T`2g{1+SfK%89@v^6PeiBWu|Pcx4TmjSs@QI`&hR6@;sh9?O*Dr_yIw zcV#uIzz1Or>3OoUdp-LX@gA5rjs1&l;f9Z(f%q6~JKeOK8=i;sSQCJcSRaGGS}zR+ zay;@-A7vPM@yhxj$7sB=8CBzzH=z(d0w-TYJK%%xcQgmD+|)>&@yg%P3cNHGF!w(D zFa0D2t3sx3LD(>ZeUt4NgdJwGY`h!ZipJri@W5Hdd*S=mC*YyY^a;`^PraD^AFmvR zy3pnUc;Y3t55sxLIS+&lmzww>d>eHkukyCp1#&oExeN`%$Kb)2nPapUUWA6QEtCgc z&VGSc_C?irFD$r%{>-|&;Wq12aBP_NVS72mug5Qf@1w2w1YCBd$s2?7uQEOgE3al9 zNfUr2bBy=E0CIj$!I!Qv{Ui?WzSiusQF!Hbru`%Ew7JIn;gQ!H?}NWuFE?;T{qI!v_lkjyNSN>Ru6~$@C)(EUr`LN^xjNf zymBa7kN3lOke_Q}%0EyoUfCr=f5j`ip$J}i!+ee}c;zif(?sFVNUy&s{R=qHz$?#1 z0ek>%L|a&{@}%4818?*GGCT+Q@d3CIIcea~g|t8El^3Ayng+gy;&^47MeM7Tuk@i_ zc;%f)(Za_3 zJb2{|_tOq|<%h_RSEf(^uiSw`cv-@E5n8l{^CEZ^T83A4evta(m1EFmyfTQk;gz$I zP)-iGU-=W-j#vJI-1HOWJIgue#4A5SK7106c#3{YdgT>p30~=5 z$ryrH?uRzxJ#Z@0aV7{Kw>}1cwO*d1Y~-A8!*`J$cM|Y`=S^NO9D?*X<%iYQ2jR`g z8OLDZi?qXM)B&D`bR6)*8?2AOx2;b=*Gp!(Za4sGdLNu(eGuMZeH6ZFeFFY$y}V5S zM4HzN&$d1Qw<4W4AO(+lh4$wh!w07#r%m9K*2m#i>r?RLS4|o}ywv(Ie8&1XJmfXo zK5&xtLAc8LIQ-dqS*KKSJ)0Mf zwLSoEw>}DYSTFB$PKC4_KU`>i6n<@e3Lcy=X}oZr^%3}y^+{Orfl1?mS6d%}uUMZ@ zuCr-irS$=LyY*36{Gm+)$5JQ~3(^kq6$ccw9Ss#b_8%(?# zo@0Fgx<58~mE)1-4Z!!TPr%ZRCf)< zm0PS&!Lp<&#{-8T=Nt*n-%3CDoOSt@^ChI`x^Z~MkE|o{{(p1)Le9Fh@EaV}Gj1zu z(QLf(?*A0X0=)7`v<$C&}%qssErgOjrYL6AgBMpacB+8Qu>P9uxch*Zu8#xPU znNhgC)U1o}o!76C=1swoWo?|c^~1N3vtF>g+?EduyHX#@Pr>WDne<-1P2ZL8X78Xr zNqANdlQ#gTBCSslUWOKsMp?Cg8(D%^evDRXUbq8kdhxW8>yi2hTx@*|zG=O;C+&&! z+1eQFbAa()_?Y!EIJ}pM_rtZ;C*bgdY&?9+`UI>v#I%nOzJRp+I2_g6ta||dV7(mL zMm&8?yz)+@>7%g6Va9vlLhGY&4bu79l;wxFk?vo!zreA`ix0vDNY`EYBx?T!`wRRQ zdGIM%($}O>_D5dgl~d6Wd>BTNKJ%~q6(zsow+TF^AN>IDg8`&zl+U2TEj=9mi1*;- zh&FOJ((N9F4$46@r7F0wuf_dCJH!@pS{h3)=g^19(tq|1uI z!%j5mz3>t1V{m}ar18Oztxv*pPcrEPaHI7}IQe9gJ_!GTwEa`iJ=nCBaw;0f`YInp zL3|7*khZ7t;2~`!OuTX;T8IzA6mrUhKOs%8ynZNsk2J~{8iw}{gTqaFKYX{=$s_E#z}t~N`>Om1ji8Sy_dSEYi}%2#NXv}D4M@`~i$=1Jq*0DU zbMSun3DWdQSa7CEuRIwoAdT{Jv=kqPC4N&M4_s<}3?4tql;eY+BHgA*SUcLp2Vt8r zCf*J2M7rE4Jhsxr``~j(`6!QdQND+|;1lqiaVC8LUW+un@>SHEG|K;?L3lZbwn5G~0DGTnycfP@eF9EC z&%_5|!Fc1{aQpej%LTLpa>{|nOfcRDH&~xkR+;z&{0Zs)t2}vP8);^IQ(lhd=~xGE zMEdTe@^Q3?cxC=1`XyeuKU$4f4n^znet16Gf>&OSQh4R}C@;yolQ6%Uu^O*DT!r_- zvyj$Jc@L^2ukuq=i%-IW8k0sj8U@I!ya|Qy5%@Qx^;f=x<`Azep3FXoSN28A@XAWG z3Lk*ekk(&$6Ix5W@>i6^D@&%Z4e-k2QQ`kE{=>;g>#uxVJ!zERAwNC^>uXIv3Bqr! zPrxQRO9z3^C6i&u_DLA>%IGzYJI1ueoW z|Bja7Q}7q0d6h*WwhLZaixPO{RcI?-xfE^3$6x|EV=pY9!M;hnaxC)V12BvjuO*sh7BVIWYMe)jW&-b3(lLLC#B& z;FYO|+Q>k>GPsofiC4bxF#Q=Hf255Je$=E_PF_Y|B3`)-b=SPG!{a7>65bs%>lKAx zK561p%UQn_CO-Z&`|LBu2cKoXSZUhC2S+}~e#-Xs!?j4abpn3*0%>$v@aI+h9{4-Q zz8Blbg-F{o2*VsEqMDAa>whVtq9=vRzZIKtRd>#$NE1N&2Kj4)y6v8V%LUZs*IBKKq z+prF8`6qMM!AH@Of9X7mv@KeTSC)O!Mv{2tAe6%U;BcgQmDizdc;#wTLK@{Ss5f5O z<5TL1_rODu=2c#Zh7qs44+Zhc4QLKt`5Rh*mreZMN2|Z>;jjij=R2O|fIH9W^1mh6+iqT#UNlWAJsPd6mVVvtGn2A4bFQ$`4UB zUb!8G@bU$9MoGN#uPC^UxiR6$B*#3w@?x|Mue=AX!bjoLNb@So|A&5vSN?Wkr=dmoAiM_Ya+N=zWyC9=|C)Bg zE5Alt@yfQ}u+DflJQx*Hf8|5SjaRP!JN*`~Y`c~E;FWz)0PlszqfiUSL>NJX{==BB z@!xWcz$xEaojaK26|3qu?Dfk=Gyh{J~ZDccE zxgG7mD-YVn_{Or7!_nb*Kb(#9T3i?|vpxnlTc3pO|6$U&VL8(NuRIT}{efrR;QarG zrTdSQYV7|weo?aRZgonCe(WHm!lWXEL1+nce+wZQgb)TRAsmFbyBP{0jQb8DOj#7g zL1;-16_sKTLKs>qh41TquFoIu$MfxS&6)FiW@oou(oC1{lB(bM_prE*bfQ;q6VYSk z0Y7uCF<&;2fpqy4$TIS3DkuLNSzC`p``3LFExa{^j$AvCWAfxE=3erFi@m8Y8%6G^lwq5>0 zN_1KG2lpbn97LAVvv@MmW92<$70Z;b5yu?)GwDc|+il>!K~LcxM30r{k$#NJJIQdm ze2t8u%dg3JdIdL;Ym#avgF7M}W+)kp)*U3z} zTtnv3EBM-0Rmtb-5q?DUnX}wPhVcGMp0{Z8? zOl6MTy=9dv(dA&WfG#g0OX+#Mn&`1|8d<}*+w^{FQ>2o;#vh2j&r-oZh`y$lsrEdc z$IAQ21iG9{rqSgpGLv4$?}?6utnEJ@dP`hICeY>9J8;bCa!=AoPvb9SKqLDB zU)hm;@;K{;*LCvaA>K*!nShMQG>)&_OyeCk&2WeZt8E+Lvz#um5v9^#h&a9?_cbMj8^2Y0bO+>0Vy zTc}Dt^Q>TWx1TS2+~a#1pCCH-O8EU`yO-kQQ*0c^-plpHw#dFE&N;gL_e<;tdI8sy zG;iF|-8r^^RPgz~2dMEA)E_kEA;=UAt4IMFh5IFab*D+*Y$ak<=j8LQs+a{^2gJys64 zp2LYGSq_$LT<*7sWvyfz@lES7R*04(gAaU{w-Rkj0b4Hi`!K*XNuC#H6D>2sBR=#! zi+Q3svS3|q9sB(d;Q2(K?dLJJF6%#Hd*0z1#3t)8)_-i{*knD%`Xx4wP1a+qUuxsn zWIe|EWj2mY)?=*y#Ky77dW`j-+Bh~@kFkEajboGb80%NqI5t_2v3{kEW0Un5>p!z` zY_c9>{VE&BChIZQueNb)vL0jo=QfT_)?=*y!p5=5dW`jD8^>p z9Gk4iSpT(+W0Un5>(|&gHd&9c{u>*|ChIZQe{18|WIe|E?`#~KtjAct*2b~PdW`ko z+c-8^kFov-8^QEnBt(URKHe?3BlNqliqyOUbE4-VGrx)>{R;`nJbq2>1J(qk%|4o;>Y}?uu z=xH2FG)Fd)Nvwn1wW>Ayi8(ldO#g>}pNMynM!Gz|b!)efF6WXmT^?QC+BMU&IG_#d z!~W0Ue~Dg?N;t5sttZ|{roGfHv58*G^U5zsieAQD+cBSglE%xehxjAW{;yyh_~RnG zwr}l9>=StfSwNTPc4+OE)8&O^9bH~Ww$MZTkPKt}WqP~TZWLYi?AY3kqs#te3O$3* z60JiCW1{zOvcvX%ol|(W^*qkD9^uwI_&EXopY^I(5r}1*4=MC{q z>oIod=EqYwkZ9X8_%hMI`Hk=cqW8UWEpeQ0@+Z=fUcm$EIG=T%U{%VmTY$^0m+`q> zI4&Hc5>DQ=wd=zEFXA@4`TZZ@mDWT2$$ACP=3-CMgp zL_LG|5X~>*FV-t~%pSH6aV^n)tKc1b`d+{`J^b}0hma!MD@Tw((Ehw6B$IW;4!^z{c#@AHb&Tc zZ{O2+J<)RHe3EAQ@&hu69%HwC{2X}(*~m33FDA7-R~|Q$%g^ z<4d$3O88D6KOSSZ{cQPo23gDc$cxDqdLCaRT2J{GDX|W+O<$Hn4{#*W9QiP*W&P!| zBt(XWYxXfFZW$QBgFF!{{)@Azt{J1Pym%&g!E<@|G zVqNBj`8hJSE;A?jaapo1Qz!dzS+FkMDSlk$t;@1?nH}!u$jG`(pX$eD(Yg#y^W!qK zE-Th$?sPv#wiw~hjQ~TU<11^=uyG7YGLCIW+Bk+J8OMrsIr&UKr-(t$&ymxJ?t4WH z&a!cQge2n_oNeP6T9><>DeqKN-kcNesFXA+I)@OA4E)?MhwQ}`Cq_QtsHMSeVucUdpsFV-t~ z;ut?CixJVb$n?d&%c6A|T;j)NXkAvU%iN`Yj*P9#Ox};nl69H7%#X_(h_*4rFRYjG zu*>~;1|PLv#DA>2E7(6I*+2NY^$6=4{CEm)upZ(U*2{Rk#(6K@5g1)x(u%K<1(}^ zE7oQ1dOt_T)@9}fKQ2qwWh(UJvS3}h8~wPyV!ShnI$E95kDfq)^&-43+!vUJd+Hh z=dk*o*2&*N2=JB3zDL+*3h#-TBVQspy8MESqnGi*`szQ;J| zao%$>M~-}gZEsPRIGx^|Ucz^t+;?O>`%TP zEaJCB^DDU1+w3crBd=;=pU~y^WEfrceUI~=p25qA9vk8;>k+QAUcruw`Py|J*ACuE zG{1nKSxU?JmR5Va-zi*Z@zr zp2KIXmvFOnx2&~0j_A3v7+IIuPuPaJjN|2G0>@PTL#EQD`?R&2Ntb7T#@B50Jgy-6 zx~Yuyt9;Mm`6T(C6#h+S=xf{6yniOi*Xg*DY~g#gWnB1$zYnCoWDe23%HYY?b2#x^ z|Gv6_^Vazu;m<$$ua_%0@n^1!=h*+a^Di80dIobu%aoyYc{kDDj+ax(oR)Qo8|aJZ za^$ZZ7rMNOxNYhZr_(#qB%S%X1&trik&x?-{J(ql(l$j&L4a}j-xujF8y2NGlestM= zqaT<1k>QMIa5T}ml*j8g`S(#Fp1ZjNFhfT0SUH`n;#`uA zq?sOJ@V7rtm#FboR@g<_qjw<-v4u1J1Tt##}20K=}Mxw_?xQ=MNf*m^f zp29*~kz*ruD`hXD>Inv=z8MC(w*Ekw(4b?h^uIT^fxXikVriRP5CF6GBl*g!NM zVyj(z5Aa!%eBOee60LuF*J^hoQ7_;lL_eD(59`i()w(Y6e0oUF<9j685BNLLW8Lo6 zuFD>N`58Qi=x0~tI8xxT@_I6zp4yXhoFt#OV7DH&{`fLU))Q9|eWoqz_u_c-Sa}Iq zsP(}=iRPzzR=d7L>yyE;B-vMZpN$vsV;e8yZoT}rr13uMMQqjEkB7MX-hP>>eXHGK zlI*K=wcD?cUw#IcSuf*R`}sL}oJq7F+V37PxRO#9&nKF z8T{0G84o_#KQ@Cm5-qcU^K3lAb=E7`WuTv*!*57(e6i&r{J$6ZIW9bvjHs$hJn2yG zS9F;p6X>!)rqE;D^Dw{6G(Kv*as>CEBmH<5ud?n2aomZvH^lkYBmB#{JBn+HXif&N zvmWA8q)_7Lw6WjOY%j-14kEqjS-eQ&?1wzwPm*gEe9Cl8%3)uD?b{)mNkMR`FAzBB1u+l~6`+X}XT5VkxR%TdKq^Z&wk)o$kjKn zk2q(_cv{H4i^t0OH*#NNzI=~tq{p~sBCk*E&kD|)#P!HyBV7C+zs@o4SztNLNn^Xa zSqHW&z!r~kesazPn0=h{l`fah;IVwoQpP)3ashCCN35&p+$O zOZX|#{w!nL=X?+FD5CQ}i?7&tg!?~l`yYQM$@dnq^(=q90z6=Lb@G{22G_j6InUR3 z=|+wV(eWzd?JxRXz}42v_{B^9dCNHWWxvi5j-TUuh--<~t%4`awe`g3h|Yl$?)r)! zPvd5y@zkrl&Jt~Ji1)u%o&1gRB2IgqbAbDpd?MntjQuP(k$H5v_Zz%6)8!4MLYIq3 z6~{Ej58w3rHpac)vUR{a7O)N6e+oG0UEVu!?6UX?N%lWBE%ftaoYLg?PZ1}*=f?}! z?tR|_Jb{eny{GMz44wW@J*=yLa}8W+>$>b5m*4P71;)VOB4+^>C& zYson*?VZB!7!M z#-n$xN&Xgj7Uy-YN&Xgjgy}AfbG+nfM8`dk4_Ytb@~(b<8C%!+mj~sJ;IIFUC$bK5Ycinc$xJOXIL-c57sNVOD|hK z4!54e=d71-vvt?I#vMVl{4CyXy@2mnkFoRKeohL{ww}kQte5Z~>u#SKH-se1#QUrl zakcd_?!2!p6Hl|A!@I2)aiR4XtJ8jdfQMVp;yCLeF18+H=RUT4JllF6pR!)UZ>?AG z;Qjpk4BlkDfKAq2-x}ARB*y}WS`Tru^%Az}U*q53;(Y57w%ETW`8gIjj11tlRbEJj z(B&91f-dtmF3%oN$&{IOBBr0+~sd5t&Dq8};9uGwz5Qcm7eF$8U1|;s&Ch zO_Em}&Go{3xsKGbJ{8`-6h z7O{NUkF25Rht#;)WGc&-JD$M0u^ic(OrgtNPORbU<~o=tynqa#=kf8&IM&Q5 z;men^4`1Wom}CF3HT+%Ny2K1UqRR`%vw!qhtiP@%`FrTHWytx%xNJu{wdFk+esnA6 zIoskU*0>Xi_Ei=Sn^fc9^We4r;o5wapFP97?qnabPvkNZ(aX5{E`Kb_IQ%|d=a`?v zWkjEImT|&V-wRmxpkICpA0>K!E{}hR^=Fy#Y_f(f-zCj-dE+!cUcl&Ct}&iVzVRH# zi+vU2mRU8)&sVtF9IqERrkoD}u78nZz#R8djq5d+b!MNWF`mzThWB;yve!AEIp;&{ z8S%XAb9n*j!G4q1l7VztB17mhvT<4aF8lU%?hkm=Lgv%ug7-KFm@k)*2D)5HCeX|H zE#oaqIqn><0{?GmKE#Ukw*k22ngRQ>wJ;2^yF(=~M$Ctn6 zvGfSnt>OH6gZH=CdBJQ-okEd`vN&dzzE+(22mXs+x*Qvhxq~SO7y!ay|SvN04T^Tu!Q3ei>a0{vYcj zQ!Ra$nQeTRxmLc*=~cDKwv@1IE&q?_O5tBb`^2@Wb)OTR4`qD6Ez4p&#*sm7^0%-f z{E{U52fu0W=U4EY?fi1`_yy5%FXK@inNQE+lO);yShu~OlfsZ_TyC^+w?nNvmuNY8 ze4l7JF?QY2&rjhkL~{!Gxs8|c=uUn-iw!%~CdW6#$B5QHwR0_h)3G-ByVymXO>}(a zD`X4%Sq|&Uc))utypZ&!%QoF=-9UPPyAwTD?oCGUSoto=)8%S1i7v0Mt98@pA>K>$ zSotuS$+$cqRqG@44%`sHo0%)u}qS4 z0FUlx=K#(i$vJ?j{(g?Uk!ZYtt@roi0gkmE;wqwZpp5$*;OD3DKBDI;;x9H{!BYnK z@f^N#pkL42LA9>)!G1l{_?30uSh<(T0+tiuszbO|`95YD&pou(wd8w4AwG5($B27H zj2kn)2ZwV#5&xdH*1dX!UrvlS9qD@!Pab5;KepDjJ&xnTK9qSfgmX!zhp;TVe2nO| zql62HmLKCUHeSJrC)T>!_I$>O?-6}(E5_=q?*SfbJ&QLI9lIjFYvVC)uz~J2)+1bNy@E#!^K-Izt@RLJvL4}{ zC;2&PJjZ$-=UI<%gLQW@=OjtKHpf@3N4Va)JEhjOIE`!jUCw8`hUn)CLVSnlu`#wf z-S+_ZC)$<_-e|pmFItbV-3UJ?!2PXfu)%tWUt6!>E@#+w;W+CdHd>Fc*}5Cawv%Lk z;$zlJxY4>hv)1(^$$H{M>jgY4XZr-l63q$mXX_R0f0jKKCt5GyVQ1T8aV$w5i$7bh zVE=RMu{hCs0hd`X;|`$W@d@iCtUb@q3GgKAIhh%L^y zIe57BEKapv#HH5DxWj0BUYu;bh)b-O@xTlGoD5!NJ;WyKF@_iVeI*wW?W-6My~rMm zw^%RW3hQMojPZ|^tB4+3#uF~~J&X5RFXH#sE7<=MTRu**Ucj~1E7<>1n}fGoFW@rk zW$c~zbJ94`dI6VNFJr6A{G0#}w4T9pt>>|5UA}8Q##WcxWAQ5MA--Te!rCir4jyAY zi&qid-$H!SdI>+ZUd9#;{{9wV+PWNOJ%_guEvJAlTaWNN>lNJYO23>GW~|Fm*7JBj z(Q=Boz{X?je0{C!&OJGWlZpPFND=qCk!xG`cKq>n-ajy>f}QWM<>1muwaM4!WxVu1 z{(LCnF?Vu2^csPk?(#i_lM1!`9tiILSalEUtnc+;?PUI&ulED|fav#r%bTWfUNT?) zb1$#cbax-?_8{v(Pvh!`cuk_0@vdpS7h;*|hily@kJP%3Z}XnvQSNC(=Vz%{>wY5Y z^5`eI2G}k+giNJp@qaT|2OcZmd8*b8XIy^x49`W6X0mUeBh1M@U+bPCdc7;*!6m;f z8JtHn9$|5|AD6EYtwV&%iGClrj9y@SwR|j-HFSAkV{P)eO$LV(Ei;D~TF>JcDZqpGfjO zHJtgTe{2aiF7(^$nrdAKvX14*ZtrnD@K|~D`?YQ~U5+K=>2eO4M2|53fcH^!dC=lo zSDMu=@e=wL#^sG9*sd;d1HC(4?(iYcMVGsgEM1;Ja&&nK$UAdS`xy6Tc;c=@sm` z%+HaF$#BNy*Cekwc*Q4NBQeXz7N7ba;AHDX+-qp8BQlIo#Ed%iqXCx?KAM*En7FU&lE{mxqvG7nXyUlkqG^K1x!I%Nu{S_TxGH(s~6i z``wR+xYZxN2Y9#jBJQ%mkEd~#^#~8&=*P48k@Yg3waJg?@mK5aPtHxEe-|Lj)@63H zAD5AJIeLq~_ZILw(w)yID|pIZ{$7{E%|!nm%Kgo;BXd~3{E0Mjt;vC|jax|1;5S>f zN#2uJ@XMpxxNupw#HOR$xVTH5lf$!Z+!}fgFBsM)`HUiur<~Fz`CF(tJn_snZuCdI zj^Mhp80Q?T;FkN_xJJ60+QwZ-Las;o)ATlODqU7R-p0+N%legV+#zV7 z@UyMjx*x!x-{=GnG+@^fUCXpVf|#!GnZ%Wacm8sc_ycn z`^NFLSJ~c9b&2oMt275MoY&S(|A2oxjQhXFwlGH?LzdICxQvWpj%&!u zsSDT^=H&24(uppYzuVT0V~+fe6zCPac_I6XeJDR}YU_G4F2}ydaiYtR45!OG$Y^>2 zhrQp{tve?ZEMp()XI8PxC#)-9&!+G#lKlH1EPd+7W&0IuC->eI zp1RWa9L^!SCr7w=75iN8dGLVGITkEa=F4rJ>&SVFQ%Dzj5%>DiKQ@iOlP=76U$u3U ziQaz}G50m+G|QA<5-q=iZ?9qh^PVck0pGMueuh7TZxG!}WBk{*zUS~u>lM80J3k)c zR%?9^@NVlx+~s>ep2k_$BTWCmv1a+QXkG5|qs_-z)+0Q8ogdHQN7l=j{mCy!M%HEe zXInlNt;?!k{5A$S&U%Pztyge(#m~v%LhCUO_|^6gmWbA0cKXear*NY60=8c7=LC4W z^#ZnTwsE}OdI2{Q{TmARJKIXs^SIo4S^nY2vpC0kggrL+@ib1gUc{=6emuZ&)$a)^1v0lPXTl{*a zaH91BZnWUrx*jult@RLJwI1PS>+WynlVm<#YdyqQtw-47AKPy@)p`-DT)Sld z2RP1ph->#jvRcQ8rT6R))%;;Ysp z+-%*oWIjpe=AQWbqE7=PKZvL~~-?y0z~Cjw5>B5Z79-;P7f&2V7`9#sM|94miVl2|LyL z@f1$9UcimkU7L1pG)b-_{GRBVt;n|aT)5DBj9a&}^~58rXK@bEaw6;z_@2h8){9ux z-j4@3&U%Pztyge(2YX&zXg$Ugw`<4mW#si2kLcJgdF{yJQ$%0emazTyY$xw?=qT>?c_MLoB;R4c$uQg&E)45&pJ^DPp*M;rW zI1cFQ_elnC-K8D>_K4$)?~x%N@_VH5>s{Nq0`D>8CNiDwc4NPh!Mq+t_$SePx!vyV zTqASjUSuIXjrS4#Omz`IB|3k~xZ@tar|=b`uf-xfZBO5G_#M&LG8Nf_eZ;oMxbI$U zBd@I)j7aj@jh_+y?7cj;XFJ!M*Hf7#gXnTK>Bst)agSc?bCxfclfkUB9MHR+8$r+D zn?%cx@wvVIwwLgZecHKI96NbbAJ&KCCFhX=^ay_@S|2%XKc0j0L0(TP^blwD=a{ja z2!Gq(pC|5scJ5@NZOq}y0X&{_wTyos$T>=P2eIvnKDWVM2mASHoJ4fa6mZ9Ze*dR% zru7mAhxq+04<|Y;p{yCy&V}>fQJ{1&1HYbwtnMgJcMEFeOSk5DVQe1>GFCKf6DJR!SPv+4Ud)g zk>2zoKK(DwEw-hE)&Iw_;d$kB(uL*7b)+A?f*XdipZVIy4Py>TvktP=Nj#Qi%5zCA z^Yi%d$!ssjv51RK@jb>jhO>PA{0a6rjq{dertw#@h-JFdS;mNVZVf%a22#tm$P>?K z=caV6OB_v~NzdbXBe|D7$$KH}d!}E{47Shtp2C)A`Rgvgr-^=WXgNi!kSzOIj{Fa=N35H?lH}+iUVSIqz;lJTi0Cy)j=76%XS-wr=}r$Z zB6=?&d*99fWB;VF<2~#{jztPzAp__U?mXG=w-jDPbg$0iE7l{dp5n&?JdUJTA6Za% zeU#(x<@JItCz1tp`3PA`FXGty7}vI7oBLTGy8Mx>VUBDwmF=YmIGgBe;Rr8#z^_Ay zH$2Go%kd4d>qG1ddJ6wXhSN*fcbe}R{K9$}4}6$=D|6&2k8ph1&+;rXK<5UY_^6%3 zIF0DsDB=x8KOW*r(uw8B#~1th>j#UdS|#mz+t)uz%ziB&3&d-V^?@5&ri{-%B`Z2GRjDIp4Eb{k-o1{vXk6dJf+zaS!KtW9&c6 zufH5m2D8rcc`|}t!l!3*k7ECnu-6Nm8%^C352a7tzRtmyklKr5BgfV>_s-x<%2Kt8qH@}@-d=0CERun$LM{2$3C7tm-CHrIfkU@ zd0b1@u%2@0tDFxz)Foa-A4t#RT9VAc;q&RNr_7To&B4z}^j^2bu5Yl;jLTu9LeJq` zqB-(~1?+Ri_k5TAz_!SJNk7(8 zKDUtLMVA}MD7x&}#5qHkDKe2RA0*TG-PH2GB<8)kJotUKoi4`_$9#D&3Fz`M(w$zy zofolOmLtz5<9VzcO{UQ0uVf}&ZYB}keZa9;Y_Ey<{D;1maMzgQ!Fvx`^%3LDmz~KN zdI~2jT0?07+isaIasvH#Ksv#%+G{Uta`xGtu|W3;4J7%zDmkqVWjR&A!WNL_hy2 zkNur>-Gi?O@j8;HhxiN89QoiM97o3Ghh!=}zk&0OBtK7xKa<9u{M&zAu#wk6y8N50 zrOOtZxL42vyq+ZMhJF9^J%c6d<;@(=Eq)s#JpM1=v$(=~8L#=<_6c_V$M+P@wI1PV zE=cxU4&Na9xrG=n-O7*Wv1V)E1N=8h_9wP&;d_7$M2`)zQ_CRv`Ywe}S})=8+xYP; z{z`PbT&uwCPLiM7#Op~_ue!vBZ3EYZE*r@}x|~af&?D?qW$T06wzl=bo2?gcyK28~ zDSUuv{fqdj#yQU;ytpPv{#|Dte<9kI3Z7f*=jZX?MDq)HU>iR_gJX%VsSv**dVg5K z|FsR0>!pM}+Xcycj5K~hlIsX}3j&wkoA0ONBr=FDUm(Njar?lv=n(jSbA&@lemCy_ zcmbI}&*Kwh6XnX>=(f$L6}N0Nc`EdD~0_u*K#gP$+IBtw`Ze<7pj73{HN;Ck=M>n8p} z8W@)?It6YbJ;3Ung5+m%0^D_H-_v+tXWuh;QWxKIIJT?rAr`y&E*IDN9^=O;-^=*t zF21{6Iq!D!J%tB$_dSCL?QR`U+Qau8Ua+U{dAy~E?*)8fFW*bDr|$w zeGjnbKE9`M$iBX3@q)DPdAzWXb-ZOi-wXIaU*C)PVn5#_{G`9{WpoGlF83MWdm8sU z(Dw|Uc98Em9DlIyAwDqB_aZ)ch;^KOsP7Rr9p-zC-(-BR;5JA29^hd|`kujy2l<}I z%a8Is#0QV|y@>OU@jb#7gMBaK%6jYg+p)g8;{w-ui0=XJe!TB#Jn97Bvv|XazK8g1 z*7p+5{+I6&uKYjW%eZBz?`~M&b~?%T6z+Vob?kqN?-{Hg?t2!`J=OO--g=tv1uUKJ zyZn5F?`2$hhIRaHr0?!bwkzj*fV-dNdm4{A+xINqaE|XGK0C_y5v-=az8CSSOMNfl+j-w({Pr^6D;Qki zyF9eP_Y59%rFHzjvA*Z+3q-1MGRd?`a%z zgYQ|qAoM+tD~Rs@sT;Z15%mK8W!>ErxJQUS!z|)^M1R9YPQRJuaKDmIlV*AemyoG+ zIdKB_Bf9)IiRcA91lf#FJmMO2fHE^SOEs!^pN%R74AbKC-ZsT4{^j=8rO~&oT zzkR?wnM9Y@kRn~)OG@-2?l;lD9)-BW?Y^h*CF>FPxP#~5y<8qwSuf*|fBW$)UP<&i zE*Fy#yhh9ElUNVB{G1f%@@rD0S8(Ki>~$HpyVLg+7OY2i1@8U=%h{84#tZJ|wPs(o zOWxyq2DgwD^;RF-!$%cZ{Wu8*c8^>=X-!(kYS9M z@!k9Vc#N-3^*zG79`JJtSpT4{1J*p`dw>hA$9UW{|N5B4uSxQ=e;7XOyX^4@?|WY6 za}``^y^Oa#>cog-ZS7|+dk88`gbclW=*oj;xT56sEq zXOH_{#-305p2iB%zfqO5X80aq_>}MRn5X@=XR*^WzNhe8>lG}`^z-Fq&)Q=#^PKN; z+vojb16*#sj8ByOcnR~f{CwGGwmlYk!<4M2e3$5W#W=Om_adJ6qVIW(US=O^|KR4i zftyBmukapq9-W`L&0;X$FH?SGy^O`z{J6}$?z`M2@;!|e>$3EQAD5TC>3fKow|tk| zF7WFX;BxC_eBy0CUczzj_#R^YyS`^}$A!M9@DrkQri?c?`Q;bz$oKqs7F)dUdw{cu zj&Fox7y0oJPyfJ==dk5s-vfNldW^*n{d}2;{T#W;y8DPZM9Y`ge(ZaQyDjlOjXzkg zV6@aPQ|6ZWIdZ2@d{1FyU0(gEzrI2|c)9Nx+-N zpWrLjBRv0eKc2@2zVO#h5$BYBk8sJCc75RkU)l2U^sjCC*lCULDO^Z&jAFdz8{b3R z^IPB3IO03M%p8te>wAcIfA4z{PyNA`kDY(C<>PYeWt_OqFSCFL{p5QFzq4M!oqqAl zOyPiv?-?BWtM577={H+GerCOlQ`h_PA`Wi$%g^GU*4^)X4M?=TC0y}`UuGFMZt&f0 zlJ+JPrq&@ylAuUdF-*p_cSgi$@PMF z{N;NA5BS^n3~nMif80OZ^ND(d<6Qfs$9ULQ?UVNj8Juaogt0o?B7d}A!P>3;cz|i^ z@;K{Ryuf-M+qYm@Tt_K<(|U~gmVR8e-^S+Po7Q8@x3W3dep{P^Z(5HrUuARfq}G1B za@e`r_Y^L-Ud9`1{QLs$SL=HQ%hu%sZTw@4czRn~CU$CP%fy8wxfbx6!1oaMZ0~y- zms>C69Uc650T0;D_YB@j7I9o;RY&$|n$O&DXEKiLTgA9Mm9*3{yR>(=5PhCdz;~_3xNTQI9^lc|^Y|<&@_o`0R;#}5BR;15E7oOtSDTM{>#}5B zR;$}E#k$+Qz3WZ1%ruU$p2JDj z3pmeugzKzVu*)9)u_+v4J&WV5hd9%E30GM!W9^={{y5Nj2FF;>OY22kOLU%8a6k{= zGdR(D0he1ZV~@RTf8sdnAuhBYW2c^eP6|g`&*L2H5mxo`a{?T0J%=-_mvEzX*PCmX zXg#wy)p`+ETQ6g3Z+l+MSx>ZH#Ikjn+Q;T&&U&KtB9^Vo)V?+!bJi2B7qM(zrqVVa zbJi2B7qM(zrux`?%vn#gUc|C>ncC0hW6pY_^&*z7%YJ?R{0vU9Uc_eWu3vjMjA%dS zu+e&i9sApJVT1J$ms&4lzx{3bIK_Gqo2|P8SUyRXkB!zN>^Q)dj}6vCTxz|H{SLI{ z;}q*fY_{$WV)-OlJ~mp9u;anDd~C2D;!^8n>^IPsk5jA{vDvyigyoZD`PgVZ!j6a9 z^0C2sh)b=PvEN~~e4Jvvh|ShrhUJrF`PgVZ!j6aA^0C2sh)b=PvELE4e4Jvvh|SjB zku0Ai%g09R5q2D8%f|-mAuhFE#(qcH@^OmwA~suhN3(pAEFWiBFX2Y(?ij9RqB&Wd zYQ2bStygfsV0$i{XuW{Tt(UP!y`PiDan?gzXg$VG$ND)b9Bn<1bF4>Lb)26Q;Bf0X zoMF9$8?Cz`tUpQCAE#O`;sT<3evE%wcgMGPJxTIffTvl{;T_fs_=@!if3RM`olo$O zP2sWDvv`g55TCYQ!j;y`Sbd^@Y=8$@&)|jD^Z20kBEDxm#y_pQEbC8_^~ck!=kN~e z1$@PNgg;oX;LiWD^~YnaXK^agJ}lx|>lGaEe||iJ6Rj6;x%D#k80zPwah&xK7g~?8 z(=b0Lg`=(KagOx}t4{KB0vv8Vhcm2~aHDm1GRK`H#~qiFS`<}%w ztyl1tQ~h`WcRS4Jt>k*!o^o18@k(B*tGM4!dsS45v5%1-CCcheb{UCA7J3Qr(fj(mcQ zpv&1LPml0hGL!Em$-~ZX&+mHV_i5wNWC}ft*Ap#AE+q@;vP@RdWsA|Ai+p}44<-G0 ze=aj*FkN0kv>e$)*3soM;+Rv$wioz0@@x_?E-xY7>3O_M<7~TpQ{#NEO}okKQCtAlH~lvK9~4ol)-a}o=d()R`urJh+*TUyoaXC z^(5ePX1S51==@_jH=5{tC}G#jd{5yW)(iL|(eh>6%UNfZDO+B_J%KKdB=hKUFj+*; z;vAyoyN34eVxpeMmDbC+&6R%nawO@;W96{1EQ>CukkNGc02xOw;@d>)Cc9n5@#gbq z*{smzKI3>@WWMZ6y3;fG9MO4R!E>(mJ&$GU!L=-VJmK7v$6x2aF3#deqTfj_Khror zBO$*fYv>hRcRlN%j$Lo$oZ<8N6uwT9ar82VcX7Ub$M^d2 zJEG^R;Hd>$XIyAK#>?*Z=RkmT6$K$lrkq06g@ z=Ex@Exc|tQbkzL{Pk+$w+Z@g%+E)>t@Q@$R;wtNfX&hgoYg6VQ=J@eF3;8e^LN8*c zNBp+Oc-W(UnHhYa=o*Q!N70X`F(S#egU3GRd+=XAhaq|{xz+!;j#$3jo8;+foI#S; zwduUiA<6pVkjHKP@nh2ISzebh|AZfx?n&R}=o#FPI6ou&lIR?k{hwl4Ts!grlB2t) zIlqYBcV)1_dWenIBW$+ro?$*o=3|5P5F4#W*lgX+WIjpeV}tb&8?8s!Y~4M}e3Hz^ z2J0a#|G;mHd>Fc z*}9v>e3Hz^2J0alFY{j>mfE;kFeRgdx7~RnU6Eba6TuLf0EJr)g^Xp z-3MUfHkt;|SmM_gG@(w*hY*<=7c!XESd9C-~X@mM*9ET9*$YQCQ% zPa};yRyL4@^bpq)t&cqLHSU3QIg~7-=Wrp>9J%xBye6`I*^dKzi z8p6&;dnWz&_i5Ev>#&Zw9xkyj<%l1Io2cGr^(-+^Z2#( z3g+JPN6%p+(HwcmBDS6Tzr29-py%-eqB-)!4>(VFth|<# z=^-vBnj_Cx%yuy@Cz6&dr+~9QWZk&WN4WJzz6Y53*!K{BAj$g*JavihMQpv)_bjd? zYxKSguU_VRi0wb|J%!I(k3VhiJ||OnZzd-!=lrD0Q&+H0>GB-1h%O%|tLVW>&H>V! z*LpdfOr*;>WEwrf%xC`fPJU1H9;t$bRlds&MB9~F-QGP#)JxdubKg_=2GRGqVr=-r z_Yik0`<}*+t(Wn}Fa3A{`+ntn2FupvF<<-hA&Wl{yi zTdbG9<-YfwKfba+LLF0_Po~f#9I)0uSLS=(laS;+3HJEGFF%bDN#2v-u|N7Atm9fB zI)CI=Ke4Y_rrev%p{H>MN#2wE%(+dp&Jhmz#V;p|ACu%g3Fa$)T>fLd^c(wWJEP0I zIfe|P=kb76etrhOAo_XTGM>0?hves`vbch1JgDm6azsB*C3mat;6`y@4Qo2M-^qBr zuGMyMR}%FQeeImJ={el5zaqx@*sJ zNU|LKog~ZYza5L#LPZrSSWHOI!k?Y7Jy4*s_+7CN*aD9oscbUN@WCqt=89VLl$5Z$M z(RhR#Y}|G3z~6xL;{|+|XgM(s>*B|Act6p25&L%a;~5-FG#=u1-TZh8PbM1A;ccWN z`$XPX*TJRe^1oz|_7$!nnqR@~Qhs~o#iY`&F7Y~g%l;fwY$Tc=>Hm`+%#qn$SO@0F zbICk6N%;*Fd~{Sx8IHZ#WH12GL@dj^NHr?u}Cyu zt|t@dvZ_1t=>Z-?G(U@DNrmGme;^%Mru>uirn}ucxPCWpqKDlqV=g@mmYq;ypjxL zneq;jr5ErWqWLjyAzHp1x)<~5ax}@(^Y{eO{1Pr9`rJ<*-ji)-nKDaSa{kEMh~^h? zyI!m-=XnZGAsWx&*EU|kJ$n1`G`>KR>jM9fh^{*sl0`hPEReFc3*REicHw%W z`SSREn9sJzGf0`9!^uSRi`aQz_9yEvKO+UYJT%Sv&@&j3Idoa-!~KLV`|ihn&>W12 zJAgU(Q(uk;T~6uO!404nv8KO&-T<$$9^&`bD|qVueohWgKEOJ@XFbN94&pjtnJJ78 z=G@Z#1jiiWdmeuz+Qtf=eyA-6KeArNV-NG=S$x}ij0a}?cm_+>BwcBec2%hc0XrzwprC_NjS4dL0gQq=fc1$2y3gyrFL?BQ_CEGM@A2%{W7p&O zdH%RMYt}vYT=(Zp5)@JfVenq+5HGv{wd2chpZD40U3jJX2u?al&jW8#Ux9y9?<^z_ zk(9p-e}XzVr||CgPjY(j!ano}z6RfPGIdUWF5H2d@WLOVQ}MNC#>7)5IZtrB@NFNM zf=c(CDp`1r&bj_^e^f)~#IF!g{Jo{FBw2k>kppB3JT#@;i-@NvA$F~WV$oa7ve z7aoOp~YD=OlJUqe1Vfjg0WR(MDYc}!e*FDm1O z|AB773+v8io`!efER_A8GQ%&UtLM!y{Mbi11~0q`4daEI(Fnczv(TCN5Pm}9&mC--<1ZpEobxg2R*r{fqZlu|2G#HpyaUPi3MZT| zeFyVx_yd&Tg}c!!c;U2=dq9~zcr2=W{$Rsz;-?cAKD~te#0#fgKwH2Ik4G!;KKvkB z`)7W(9mc4zkp58O7t%N4g>9$>FYH9Eats_m@>$`lsO6*?hC>(e8NBe9r~@zjJF4QH zrQ{fr&k92{Ok7xZ@g!#yFPw|U@xrrF9p4+mTafwLH+aAL1U|1mhtn^y$9V8CB>68~ zjz$*nJ2YT7%JIUVqlxe3cfi6|P(7YsWb5pMz)k<0PI8&C-^yceB{7ycTZi_hS5 z5`T%`LkQowjB9xxe8QkKg^!^Yyzo`jh8G^Z zoPGxH!NZY!R=65<5EuRt4daE=R#4A);R$FA@557&T(9tEw1R69K8S9|3;%+uc;SI7 zWxU~g;oH$8BRR^a@^h8*({b z_;uvr6ZkzOpB3(X8T|=y;Zf*JyzpbF6)*f0YQtCH7m<8c_%l?YUlslX-Gdh%T_$Jn z!ZXn*K7y^BQ%j?gnvf`yzr1!^da~nJQm4kg_oleap70dxp?7^P#G_L z2}O8kH93h)PQnkOdwkj{ydFJ)7k(4vc;OSs;T~o1cSt@foV|v+rJomGgbw9+VJ9l# zh2KWa_yj(Tzk*OG_Cg$q!G7haBT#|yuVdhjvaipE|%*znKzCum>7@;b^!?g?X5 zzzcVvCVUE?6whac^_NplJ}dkrYQ_tPP>2`)1eNg_`~#BD8h(Ou5^sXnqaM6)E4m*q z{26*gJ_|Ru(dWd>vEhSA*5kt8p>r9_glAnrKZX}xf!guHyHE!{hAXeM=PASMkeo-@ zkE$FaY`BVcfftt0<9OjY=xKZimx-tTg#)Pce)cioZ%~LAPQ9A);Dz(h4R{}(g4*am zg`YtYUie*9#S6#L{dnO)*H9054<3f(dxgu<6U2pGXd>qq{tOlH!k1AK-uWcCisZAx z5290SvdPz@``f`FGTm_g&iox3m-rkK7kJ-ldEvw zh_*mnxBwM6zwmN&1YY=MG!I|7j<$I{V+VU#75FHs5*NCk;(PJJLs5z^!jqBg&jj$K z8|at#T?zqgL;f=d8{UNPU>+#^BO1gDUqbieg?ry<jXn#DzIJ2`@Y$CeAT_ z7i|H_@xo`jX|o(7T(N=qgnSm>+GCFodl~yS+UxS*1yvg__tPg0*tkDPz71KQ!}~UC zoowNIwvrF54a2X~M(?rl3LO0g_nmm~O~$KX8!z8C$yxk>^&$Msc0C?mpV;$PVEmx< z!f$_m&M0g1U+DSa6~D4^?})pRia=!60Im6m5 zfj7NE|BtW0XaCOq{Tu6Xc*umw{JwMEe}fs4-`OZ!_lC)4EQnwgNxP`QQS})-=8coh z?^5vLA|%Iz@M`rD{EGS*KA=8<^%L!9Jvax+XMMOveFV3uPhkCCdOVzi%<=GcjmL1S z#uHe-w;m7YAagvtUE?v_s__KYze$gWUqTNJvL^%YMKbqJ;A2S6lfh%^?D0Nagd`rq zTaoM)RA86JYjEN|_IMZ0L~?u){t%hJ=>{)+Gxaa+4pz}a_!@jxeGX^7h420VeFFT@ z{*#??juB3qG}&>93pb!c@iq7-B;#)m@0e`M7Q=VfTkpe(4c5ExH1z?zS$zdIOtEG5 zrcQR=iA;UMbJU0MD)kZkaAC6ZIAsgrghqQ^F3ga`h1X59@ydaeXFB(Q-%IAhN7SeA zQwQ021s;E}9s_@@K82s2VdDwB(xZOl9U-_LNjVevs(NSUWM@7y<%id(kKhC96Zoon z=WQI1%<=G9By*%3u0F*28a(RI$@X`Qz|HDw@TgfDhnv;c;M?D>aoDH61}Dw7*W$r1 zBJ;N@;FKooop(%j4mzBB$~AiMnj>s|MsU-Slg;mbtiel;vSlm7jdLbDr%FD+Iq$S_ zA6|eYw<3U7S3J*EQBDsdeBhH&zSCp#B$e&O=7Cp!_o z43GQhWT*XQ?kPOxoXPwh1w8!exm*iAgWks`o1ecHK7btJ!r}8LI~C3&d<^yAg;#}> zoqO;RjFFUIxN3<#e+0t|Cp#-Rk1#^!8sV`Q+4}KeE0TB_b|86&B!-E`g=6Y-SXgS0 z@t}|7v%*&OW!Qntd0?V(;d?Kp-{ki+2Jk29Gw57m&m;8I3w`y%?;!bX3eRY@K7SNeg);R1`AH&8~8i$?gW7xP_(*I9CoUYVdGkj!%p=vY+R>t z*r`5-jhAa2cB+qI<0mu@JJrXqu}$N!Q+*5@uh2N`R3F2}D>V)~)yJ^$DviTV^)YO` zTH~-&eGD6~(Kzf>AH&8^Y8-Z|k747r8i$?gW7rsJ9CoUYVdHfghn?zU*m%9hVW;{S zHhxOuuv2{u8*k7!>{K7a#v3&bJJrXq@zWZIo$6!Q_!*7EPW3TtY}YvKR3F2}n=}qP z)yJ^$vl@q;>SNe=v&Lbk`WQCeqH)-%K8B6AY8-Z|k746&8i$?gW7zmPjl)j$F>I`8 z9CoUYVdHv@!%p=vZ2Y{&VW;{SHhw|la6Y=9eoy!e8lj&QK98Qp=WyZe%tM%43cJw_ zr?Q6wN6=mP6rOv>WM>dBe9sp-Cti3C8pVh3<99O0aOme@YlnLHD#|H?)5%;2iLbze zzGS@zht${L{4d+M5C4wjcX2p((SIWId&1%TuS|AItPy;8Cz9Xa6~i$kzb{L8!dK~| zKgjPMgp1G-_z>QMdj7%tMQ}T+r~JY@@8+|3;qQ=-7hWDucFx2{aPhk=(MiI?F{B<0EA z!msQ3;iKr#S9k{u?spG)OBsaIQ3u|G!^r$TL+Ed_Uid5Z8EpNAjhEr~k>pYeU-_mj zr!!3XQSt8w8-DiQ$<92^FZ?wM@EIKa7Gp5K8##lc_o;_R-EYe&JP-Yub|+kk>iBFK zejCZP2>*aW;=)(aTD zabZop(A%N?6|ATiI^VN#VOhN}Q!fm@Z;ufs>V?G-8yCjvh3>;PE{xO*bM?aT2lg0Y zs$S^-(8h%|^+N9v8y8m83!O)8Tv%2w%+w2mAK7DsiF#r2$2KmE)eGI!#)Xl3VXj^n zK4y;*rs{?MPi$ORQ!o7RPwm<^gej80$076|*W+PLz0lifFV=>0;EhZXfg=a)7vEUOo0>V?6t>@mVby|DO%jSD}A|FCgkq+Xb-7lu#UV}#q0`8z3a#xvG?@LKf|d`f)=Pkh!M-EA^z0m)IjSFk)h29@+Tv$;rbpB-H!m@f{rd}B2_84KJURZp=#)YwZq5GnZ z3nTTyT)i-S$sQw2)eHSU+qkf%Ug+(%abZQh(D{pv3(M+-nR;RHpY|AGqFz`Ww{c;t zUO4lwjFH^yBCH@8gM_(y;YEM5ZMh7eR-eNwUbgGg2=4QW^)9>&nKlU5zG}}I!Smlx z?<5nLr@>|D2E6c#Z>)D7VLTG1=y|+w!o+&VeFN_az&7*>apC7s;dGv5z~lF>=Vup~ zE5hHu$@(15ud8=X<+H+PQ4_}t_ur?UcVu|R0l)NSj$xdQ;XC%Tz6d{!+WD;TEpMrJ z0*)7+fG)!O@H6|@vu{cn;Li`RK7+?ks&^jZv%){2wHz;e+hop(FT(w&)H~yR)`kBl zSno7)EP92{3U8anwQ#)faWsg};9m}`cUs@boMC#s({NC|+5hq2wMgnbf={W>;E4y@ zxDWT4QE&cEhYL^hxGv69hF?CUp1-rjJPqDg5W|l)QGVVP58+vd ztA_(eT3>_tQML|+^NZH|@TqszI}^Fy3|>07o_%D#7w-4&dMD(by72dXy6UxU9uhf)UNyBE|uSMy!M zesmXJ_!7Dw&mV+x<{L2`TwPddf=05*KUUY7@-L({pI9Nzaq>l1kMsn%EEuaKN4gNL7H zeGy)zK7v0~pTYxAx5s#JrTQ|wPkjRS{g6Gzh3BXb;eh%Y^a4E(dc(iX~aRDCA=sEr43 zSbYMU&av?#T#lsP%5Xq^4L+wnhlihQk14{RqlZ4qdo{3nKK%e*_**oN&*9;r9uJqR zFT(-#HF)}xdZ+Fj-hqMFUqr6rh2KW=@CkewN!gsG^hHR#4<}qqTjLnv_SSmmT)c4B zGV&Z>gv*h+E;yjR2A@-(!^1zW<%G-Cm*If=8vNUGa+GrlXRqLT@xm8YlEZl6)t7QD z=koU;;4W0a=kV~$^!#wS`Z63)UxUx7&*9-^JsvJsUxowfYw$VsIXrxo9uJqRFT>__ zwl50c%}DB@0)MDJg(H`f2b5FTa0O!oUf7Jz#0!_9i}1qYl{PM1j!fOc0rfTbocbId zewCIDE>~ZM`&>;flB>e=P#fnLUW;zQNANx*=TBgMJ)XGm@K5PE;d1q5IH0};pHrX1 z!*9^zVF!{n62rP1t#@IY`UsAx&tc1_?J*%7QJ=!nXKdVuRrNJkXt!|>wyUqeA0oMy z6z+SI^)CFN`T*Xnz5;)!K85>!R*#3@M30}xdKTVrbG`F4Ug+Oa?>HZ0jE4EG^oMw1 z?``$Y5qRPA70QMezG*#U5MH?D^Y#4AE8dNV_accW@I@s1B+eJ=op&O$X9BN6axD?O zPkjPkR?i=`a85vSj1PZv2lF4!Da`Jq{CMH&4(b{o!TXT8*YIWaPABI<<~;B!^%1;J zeF9%Zrkr1*oXC_D&i*oe5$6|=n7#qcQ~1&g{5Rftk@6$?yF$XlULx0t3l}2iBHp!zZ~rqnfEVWI z2)wYkoA!ejzK9Bod1o6Q`xnYgTzDqB2p__I$C9%EFI@gN%77Ps64me#JmF<~ zPT@5uAujv^dK@3a@+-7uyl~+^$XmSdu?Y>1yOd{w@Kw}=cizz8JTbAsDd98tnY|jE zGx5R??cLy%@xoQ;27Cnlx&|}96?P!=w-jODJ`Ij@F?9~V`sM~_-bPs$G&n!mm+vJm zJZHZKXF6W!y`{k^;)Q1*X(J*0y!sgaM12M)z11G$!2|YhF#9SV>_M{Ts=<%A4fgN3 z!&{L2y))rY&@msP3~<5$4NfyYgg2nuxh~;bCp9=Vyzr}&8=Mim@Vn>*@b~Cyd=5X^*x(G31Hwa(3$uQPCk@+pv=N)D8>u7&uVb)!3*cjrp$QZDd>580Ix=`;Dy(tx{pvE_(Bu+ z8ZVr57@x-re}qoO3!g+~dQiHQ!3;hY4eH`_V z7mlD3K82qa5xTnJDr`Wg$KaH9xv#{ZVd@o-3 zfm0it6?ozKD8dVup$cC3uG7dXybmupoxBS89Zm4>D8&n(`%r`PG(LxGf(GXz$|F2- zQG?Tl7j~mt@WNpfIL){>@(D$e+l=OTooSH%Dp4cg?~V&;)UlfYj8rma5Y+skKowHX}5Uc z+gEZg@WN44!)Nf^ODWqKd=_?IM*R~PZbS8W;cwBQc;O2gcghXUZZtyKgvC|dZ@h5l zTJ9ZQIEE&2e&MUA9`CH9?V>~R!kgQ;F1#>7r{aa1uAqE)Vat`|KVI01ZomtVzKZ(6 z3*V0<9>8O-rXKOak6y!h@xry}aeM@S`AOnjmvHWN#PPyEpy%;9{QIY9JM2X`H&8!l zE%_;Y{L>9iJ6`x_bQj+FOoManP5c|4N6{^; zm4wxPgco|78l3y_!k?n)@>~qwI$(VT&L8Cd@{W@auRt>HL~!;H*FMVMdV_0_%NQZt zjt<2q@L4p57cSjGzlRs@y_FoGoGyGHDiasRU*nv3VbeX-C%y>#QHfjt6a+d;;I~Ejz~funS4u)?n>E`a1p|nXvJGu5kj_4!0qx^8{{xfIQ=S6L|8s z?PmixES~jC0-Ls5UxXd%W7wGRS&s2wJCfroa9q9fAbE?-UI83cpTQHqW8*%&TYU_F ziDW*R!D-*M-h7__BIuq`^4>$uT~B5VcbN6fSz$`Vj6?pTku@u<;0v zA-R?uw*1ig5RRx%Vd)Vq54;MQ^1v~T=dk5b{VW_&pTg3Q^t13vWIhY;Q=h=&er)fr z535MZQ-g)ndJn$$G5Q~JJAmJQ-1gxKT)mULm3oHP{EYl3-)eB~&-pxiRz95bB*$}3 zA6|q8$wOi7*Yr_%;k=Bz!Tazl$gB%s{xoAB>mA{&XDB~jc<1jp4_-L=_YKY?cwq@W zkN4q=f22IDP58rJ&J0v!4ljH=IteelFI2iBp_&@kLm9>l9}mUikO{Q=E2LtHPsZPI2zS7vb_l zrZ~?tw<*JG(bZ2JZ20n_Q=DPq!UJYaah||?@WZpGn0apqZ$}|xvGC}3OmWuYg$cR^ zFMQ$fDUO2|{_=<^P6036@2Dxx5qKAFJbH@L^XS2b?M2E=T zh8M0wuizv2b2y&;XAeGrD%?lmiVLRjH@f-z!tnbSa*g;D{-u@gX8*)l zHpO`gNqq{7%Mo$m(<`PpC*g$$teoPs;63=(OSxZo;kQNe0i0$Qg1nggF|-3y-{xYsU-E`V{ph$G|UqnjGMqF?{b$ zeh!zcx1TM;hR@Sx7~?#69g=ml@C{!eZ}GxUBOhOZKSlC4!G-(ZPA(A_&O#UAi||Y& zWe|3u5xnp>=m~rdqdT}qtR;odAi4G&p8iG3AkQP<{&#X;c&6{d9Z34X6t3;CK7u=u z9G}6XJMDE9VHrtWxLxB3Z1|Fmd$0@1I8uZEKvK8Pm#GIN@d`ZPF6%uwgrqz*IOi)i z?!)_##1pvSt2Q3MUL<9x!8v#9@$hpR zp24PW8!y7Ek(@t*Ig)vWFx+6hFjX(~du&{IXfO9y-v5Uu9X^%!g2`L>EHckW;9*Bh zb?#rrIs#sNtF;HmGVoV?2yz?;zujuGC0BE0a7cTII}#|!U4 zRlM*AXc(WurN>M)&$-I*=3}RtvA6I>#Wh#|Upj^8TA}{&D=1z#@N4KU>O@#PeyZaV7dE3q@d0d_M?G*%5nhEV#D%xapX$VTL;Ns4hDV=BolM|( zINzUYo|F1;J2HQ-9G<&?dZt~6@NY=QCgIZePIc<3H{n*)giqkIlc|5sUxxlElp$p< z0pIa~sV29J@Yf%l>eO9Ie+Vx<)qYnQ{)hS;o^=}6OFIo=HyS5Tgwg3!o&Bh1;hpGE zd<=hyB$tHy22-6!@WMtkhWFrED8LIZSTxlM@xry}YJ3FmMM*0^_YE7)gbGY|;l!3CjunT2<^cC>@^QkZLA%w?-w%z%#?*ejy z&(`2I7js{j8$@v8C7d&%pM-x!@~oCW?Cjiy#wBOq!z(yHK85R7+GApP#--MW@MZPR zW#lKifwBn~m#GuH@FG;hm*J05mGTIut)i^lQ{f!cjQ8QMk@>kTc<5^CjWP&tMl0~b z9u(ng@Z>f2_yBezX*)u1E%}TWme3g9hrdViZeb1w)>&VJ7hZ15R)&*5VXx7H=c8sm zD;!4W;)N+HJ(ys&~E!V7P^ z%Jx?kc)-=<0R6KEyVTdT|g2dK-^mj?8!g!%tZ+Ow|kh8*E%yQ!n&xv~gh($55{k)zZM(TyRdSUnlJwM!q%=zJ} z+x7f#!X0{kxL$n>>%K^yQyv$#sgK~8`W&|0X^#nEisZfv{SNDeHT6QT)5e8e$lPBz z=S$Z6@Imz{T=Zod58*ELIb3y@jYn|8SFCqogrp3@T)i;-s*MX%^+Nw{Ehnt07kaUl z6Luj}PB^E_dLKThK81_AZ9If2GW7uc4SIffulfWY*Q0S*Q!n&-^?PBb`WQC$X&hG6 z3-{S*<1So}q?|E4vdZ-{pDDtvNa6`Rt>4B2xCJ#6ufhE{>Ai-RsV~DHt52anpk;g;qlJlqVyl+|` z!au0b;WfiH9>KTVYrPAHL$dJlH0k746?G!BQ5oTmoof7f~+ zKB_*2=RIWOA^d~-9A2}-#v}Na?^*A{n~;>D0{8j8^)9?geFg3_qH*{YBxQ)z6R(2#Gc28kE&1Mc|W!B5dH#5c`|tR1RnQ<#^Jr{6L{K__B;W6Qhf$j{@TXN@KyEBQ`A2)^$!oqtoPuM`Wp0h z+3ylo)C-;8*toE)UYMyD2LEA?5hm(|#iwmt7^@e$&)B$dJu>|>ocXNvMYvUc0vG(& z#siojsefT{OwSKv^+NY|dVaVbne)S$zt{7_t?CoF;5j`%97fXrC$Q;x>x-~MeGKdV zpyL9JkjZ)2@JGf2c^?37Q=h=af3ooqrbvz#`nmPOntI{F7wqu?d`5i^Z+y|lD{$6J z))(Op^(kEXXB#iWy>?sg!d~??xbQDF9>6D&-d{wnbfF7-LQ@gMg1 z3Y_H>%z27%hx!yQnouz33E?jFIb8Jy8;@Ya8|`O3xJ`Wm7f-bD5bi=!o*b^)OFs)I z>}|aZdy!mA4K93>^#OcFeGWfVS1@@Mz%A-)aQ}U5+=c6r$yGS>&DIy;R`m&7u&<2= z@GyVtY0uOkr^&Z@Yq|6CiyuV&A+^ybm3(g(L+#@*m0PB6Y zQ+)=POw#X#FQ|7W^S#J?FFc@LzZZ6?ufaJDHtxd*)u(XL6dMoWF7-KFHPyx=IH6#@ z3)icU;mk%GFT$L1QM#Cjj@RG-1K4=tGf zB!s_EpTVN#P<96cx8 zrapm-->K(>yVX1Iq8^Z`2iSOwUJLA0AH&AE8i&^+xu+3)N__@TJl4j2c(?i({zJX< zZt?+{e1N-=xfdnMfJ_pi$leF7KHv+)q_R?mxEP8mrZ z3N!V>;5{}j+=8TRHMswY*1PaB^=0^D^(j2sx5pIW=hRo=0SmN^z%KPQIOn}K?!yPw zr*P5xY&?X!)aP*3Nj4tA2@9=v;d=Ekocn&QE4WjA2G^aebpu2G5^)Z}zij5cH z4kY!T!lfUuz6|&Jp!F{7RbPV(Pqpy?KBGQ|H=btW6*%j3>x*!Q`V=nukp12e?oyw_ zRe_C1Fh_E~gyACVg{gX>e};_MpCgo%1#@hlq` z#_EOcM{HaesTbzzg<*?5MwqG>`e)m?u%=$g*Ej;Z;6cyE9!;L1vV}$s~2YKg~5gP z7-6DbSiH!_g|T{}yVS;ok$PdSUKn0%j}fNoh5jWrF082+daX7ttf&_{%WPbD2QvK? zocnR>eYjJ72G=dO@d!4ou-=2))F*K9N*fR1ZuQQkoFAFug*Ys~5VL+qf`NFU-{o!%x^_ zgsFO=-)7^&ntGvkg^dd<>V?jgHZCly7iQ{(!BzGcVWM7GyxPWvv3j9oyRcV%4KBRi#sm0_`W)W)DI2fASvOc;ggexy zaOsUUUWR*p+IkoEs;|L?pRw@(KBGQ|H@4e&1`4cVVyk8eF*E#sm0_`W)W)c^j|5 zSzoZe2zRJY;nLe}ybSlc!+IC?s;|L?U$pT6KBGQ|H{NOE6*#NI`Xbz+K7~s=ZM+Qk z`jYi7>{VZb3%_jR0enV%4sX26#w&2vSFA6>9qLoK^s6>rhI`#@y$gHQ*Wkj~#sm0_ z`W)WaW#bh%tK0e_+@U^&OE=hf8Sd3%y$gHQ*Wkil8xPT`HwpN&`Gtc}(e;STjF zTw1m9GTf`*dKdPpufc_zY&?L^sLx?IVB4TDRWI}hZCqGWFZ70NTv$;rbT-?#u&iE~ zsTT%Y>@mVby|7rbabc`p=x()fVWeJ|s~3h}v&RTi^+NyaHZH8G7kc;DxUiyL=xnob zVOhN}Q!iZi4O@p1Z1|@29^9rrfs2Q2JcPT|JNGiCATy@Gx!IqD zt@q$I^$A@3fQ^T6w|eK>oFAFj3uE=dncvYlD%`3* zfeXHC;{kk3eFm32WaDM{f_i5MYgp9Ee6j+a?`b{5vU*{rUKo5|%LxQ)+Y&}P?;m6i{ zaGUxBE>3McguB%{k8!_{xnFSZPptRhPW2fKeya5V6ZOL4<5~|eRxg~nQ|ke4RiD5G zKht`EkEzdK@N+#sOwySAQJm5J!5A0H3gL9s@aUVXY zK81_^VB;a&r9OwN{%GS7obV^>UASI-3}@yxUW8lKCvd?FHXgvo)Ms$Xi#A?{FQ|83 zqTZ01Kf_GDF!-~L3lsIirrow4im*d{4D0@4<1TDdAHfO#spW_3)yHt=xRxJoRiD7( zU+wn_WA#G!Z#FKB)Cr3 zgnwA?!U#!S33K(r&}lSrVX9u}Pq1-eO})^2gN+L->V?i5ZCqGZFU-^ngNgPSVWM7G z+{?y=v3jAqw~Y%U^}<}eFnp6eMwqG>`gJxgtf?1z``Ea!qF(5{*~W!s^}3h!rkhf$&LK(WgCy-+A6S#P)jfZfzdZ)nokr~V2 z+(zqtxKn)w*G;qW2sRvOy$83cPvGL|HXg#=>Yal)KQiZsa}T!Ohdb40aNP_Wk6?pm zy$83cPvGL2HXg#=>YcZ7eq_!MXCBgM+FB8ARiD5GhuU}mA5)*f6K6G=w(P^Z)yME3 z>YcZfSIG2NFjFrKX4|+hQ7*?0(dsn6l6V{ANv6Xsg)!u9H7IP+K=FT$ z>V?JQY+M+t7uFqbuhE5V>LWO&K8GzQ*keMt3rT;T!&URFkKly)*1K@M`WU+JvEM6< z)C+U#hsZ@mwz>T9sDz{Wk;g`_++IOo0A`|v^aDO~hE8xP@jB{@LKf|%#q~0FkGl*gQ2J7+dJ$DlG^c*j|s7cY!OcwzPtzL)o1g-^FM+Mf%62c6Be z>^Ru)3H%crFZ?YU$LH|ykJ@8|e?&Fn!oQ+N@Xk4n&Rfp4=Xc?u=h^cY;T5QkcaMcH zp#omG_s2LV-i2o&IY#&!b04UWD+Ft85PY@CqdNG=e+PD;ytQ-ROJ=Nj!z~u2BzPRL?KAaK3}; z%H#~Z6+OmzUM7(p0{Mpnx!i* zSaF5N2`*hTfA+f7mma_7!q%lLm#jH*<+2M`U$*A5i`O1`;bkk2U$SQ9QJ2q|?X6sL z>9UKLu33B5KYtsi^1OL#SFc;M_LNI6zD&>dw*Qy2z3Xk}b9{2m(hJwEUbglM{cj?x zm#$jJw=BIVSiS7>Wh<6mvUJVs|M<`Uys(XruH~{%U3&S_72XQ@|M|0*tU2Y<%P(8L zboFd+-LeH2Ubu7(*LLxe6>FBx_Ktr2JoAqJfBu$vNB>Ki<{ka|#qsfZN558Y{KtR4 z9sfO2X>?8{+TLC1?&yxYtKGxhS#_*h*YEW=^_Mq|Y#QA(z9|?82cm(Ifzg4nfx5w9 zFdQrodPA*4ZA0xt)uH0%;m!V*ku6Tln8dlVIPYrfb~dzcaC@*c0Zt+(3i^cDNs z`-c1cjX`y^TI>(|$2k8mXO}aloI4-r7>oz2gTsT#;K<PZK!P+-{ADr_3)IWr_>YmwD(kc zlAgNWLT^)Vb8n@$qc`qNdkcMDUsGR8U))#itMz4lO&g0FOB-7^);1;^(~bGYQnk6- zQf;dy)wG&bo&M(jmj2fM_Wra#>vuP`Y--)qu_@a$w#gf49cUYo4qZ~Tf!~1En{*`iLKjW?8oJJm}?r3eXc0#8sUoSx&yAL(ml%c6gGrh zO}t@@Yia5!a}~9oajqfht?MiFHTP90ciLCj*tD@_V@#RL)#2(0cVxWk_Xpe+SIbu$ zC~+^6!RDdip>(Kav(zuOn=-bTx^1OqrA}L@)z+@E*6g^c+g56~P3t$OhMf)nRmbfc zDzDe`Ki9EKEf@b=E&E!}BU;Z=%flN-O)dMTjz_5F(f_5MN2%$t{@m1d$n}&rwP~H_ zJ|Fpg+{X9dI{$n=`Viv?bWm%5^7OMz`c!_ybN(*<5=w zdDRxDWL2drGx;P}9CtTyWhFAEnJj6c#I2OGty<|H?JsN!H^toZCcf9_n??p*aysDK zs;}pB^XnNcbqxti0se7==4b+wu6`md6W^tioVZ$P_~t1R|~^dDoC zue@=Ds|~6VcfO8tRk+&)N)}VHCQ4SLWF<*H>b4erY$X7;w_ofO>vrYrk0^K&f~T& zr@Ot|-O#Zi+mLUl^*FtCy(QXhGwroyW45uinpfNUolWhV+<}&Xj)82zqlFe}pFX`{ zK#p=Uf0`43klZYHjdw-eBlK?pZLZK0_tbh5T7EO_KHb>DJ#D3pw9y_~2HFSQ!4~@U zR@#8~&wYAe>&c@p?VukGyQ*EoWV}zlkI>(YcSjpWH`LKjj`b9J%Vg4cZ&P1HzgS0~ zSfNKO&>P0I(I$Gq8tt+~drUUvWKc6LFrnvn_~v$cdzaRb)3>+LuRB}ZEyXSETRNmA zgwyz284h!4H*TW;>xirLe*x`bv@7f`Zy4DS^^DTP1-&7CbbvP1U9Dq}9<@px7isIw+&j0Yg;wg3|HFOmMt@_+#wz)5uPvxn>Ay#- zUVpLQrzKSThsngT{vu`YX%WNJ^$4xPANc1nyu!#@7%EeWc1BQFT54sQvmZq%&`O=y z`evz<+BD}L;w9>RgjUi**~VzMZS?MS8zfg_Mwud`MAC;YmACbLq#3=hiOG4 zwB_c_0j)XY-jx|8GsgdrR$XSC%15bA+4s3ex+i1;bGc)O8t%X z=FFI6#P*msl^C}JdcRicGa}zQ%*-R{8zJ+uzMML)+vsle7}HDSN5IJ5N*@_f?;U20 zA0}T?dXO=CO{eNs3)QA-vFe+7N?0v3j<(as$0nC#)-uWrFlXjc*I!^BSnMyE`xP>a zX``pBm^`obC)`09Y4iSZ`aXA)w@EVJrx$GDo|ZR7^i&=6$g4ipBO z28shdJwOY6Wto1Uov}D(G#)lR!YI9GPQOq`CU}g^CB~V+^iFN2N9{1Vk?1TjXO>tu zB;#q5$&`T6sFm!FXyYC9{xy0?$(w9wY-oJQ+3apEY;H0$$YzsAW!hr<=E~;S%qJ6i z=}~ejrw^*zQrP0r50#jS19v~KRfV3Z+Fk2TOi!FKUX7bJ=`o)zZSd(iTFBV4nJq{=tugA4Y)Hx2F*DC~ zndM5`^?RDh*pQ5EGc(?}r)pYrO0PP`{M}(3DliTg8I7cUhrO+2Y-Hxc)n3LF`oU3J zd`{nA*XNqqajDNIV_V4BG8x-W#>R{Z!(?nq#!CO}kg)|awn)Y{ld&Ng+h%%-nEq|p z%%w-k*qn^5BV#=>wnWAT%tu0<&j?=F+E?PP4s z=p%h&YUYOHjHB+R0vTH*W1Gp?kc@33V=H8A)%2SwW4ZK9&OjX*>yfb~GFJN0Rx&ms zV>`&$8W}sn>Lnv%$4&3$4K|UnJ{jA>cv>c7+f9Eaz1aw}rHtNpoDs-nUDHJG<}*iW z84AhRHZrzC##YJLgp3^}V{F(MrDAk+UUoHZU{)Hr5~&de&-JjhvO$NXGau&YHv}XPd}bpPX$WXUmKk?Pir( zW0g5VKbx8Lm%}(xAZLr@Y%@61UhiXG8kg zHdZ(l`q?V|Y(hUfN1SK$XUp`n?PjG^C1(?Ic9fjW$=Nz`)-&t9X8PHXezuK%wqnNQ8aX?{oH-+B$C)|1 zyxuByWbra<*v3#TG{KGW~2j{cLQ;^MsroHR~B? zQyn?$k+UUoHXvtP$=QgU?I34sW~?8jpUoLf>*!}a`q>iwY(PKTNKU*{F_SB5s<7Tc`AZLr@Y%@6bmSUvpMnMdkrz)>$=1{+xY|A~V#o*$qq?+g(QWfH`P|aXe)NcbRVnjM)`N z>y){r%lI5HGFO;Kri{fdqj12SvBEk(WyE#qwF1W23ZrYvT+n4a4H!u)%==Qa7g%J( zEHhTtSc~NBp%fVx%go|xjD$HW^dd91ve~am8S7kTW&v}u3gcSJh~_f?3K+vGj9w|L z1DEkCV5F)ruS)IMHEnX6!GT`D(M7v4&>OD(ftn0aVQz{IywYuKV#abLB$I2Rao{>n?2wn0JM& zTFcDrBj!Gps+_@oJ~4awGJ9}Yqm`KN2D(1$Fsn0}>1MhzYclJy7UsAzTc|KsklC%& zx6Bg?W;N!UwV2FmrS6B#?yRiBoajKu)tr@?fp6AdZQO?{cS7a^W6WS>E+Dhl7Vbzp zGuNt_0mz)yF=Ky;ES0%xyIEZ&W|k`Jssj1iOvbjFUCP+3{!{XHoLQ-7)=`03MMY)} zRWmE7%&ebWvwHH)+No?-PBHtLHM7po%qm}UTUJb=Sua)0YDwmsvP!Bm>m<*tluEL? zH*1wvvs#JFdZojxSZcc06Pq=`@MdmI#!W@T;f zVI6A|nc)TI&dKUI<(U!vjINty^%0nLhs@qmGjEH`+QP0ZsbVhXQ*yO=eXQ=&B-Oe_2))HM6Q1H7j9RRk*sU@O4#DHmiyb_HL?XRgs!i z#XsLiSxvN=HSmAGm$H(OyBYk;{Y=c-*WOcmH)Zu8{Z|KjG_rD#UTmDzuB;l0X3Y?q zb*|jG*sK{6v&MDI3b&|jTUHB^Y2h{2w~0Raux(sc3ihtrOtCp4D}*t#u6512*52c? zS<`lyU6`8g!esP{1$JQU9dD&~w5>YU{TEpmWV$YJ&05yB?$EUFido6Zsvu=0Yg@SF zgtmC&b9o+9Hfz=n)7BF+ z_m^FkI`&vR)BlD%b7*^g)-Q8@*Uom$l0z*gN^sWM8*8g-NvYR*oeOZ&IkR5Gj7 z(Cn9qg!7&7O+QtWmSC@~?eHk^R8H?5)TewWhl&(jT;%wP;#xG5aEt zHL`ct%zCo+zwFLutPf>X*h-5}x!bPUUF$HbJcoPPYIe`6+Cvx34w>}KX6K&gmIpcy zam$>zj+umScEV)$-Zl3n&>h)Po&z;8KM2i!)-ZKmH2YU&vtu<%AM2adeq`47W3TV^ zk22yEOe>3Z&%ex7j`IXIqOYr?g`_+e^64>SdbEsZW36VtCg<5^Gr62_MUL5hX*Xk# zYu2e9tV?sA3CM0=RmPR{KqofWP&7H_n=Be(M&mJ7bWr9|o(~im3uITKg}x&(yS)`H zU6Fd{=>UCii5bagf6lXyHd@#iH7j*0ZD`EQI9kklTIzPptf6iFmZ{;0e)P3EmVWfL zdLEWFadx12-u+*7EURD{@&2`z6Ea@vxmoL3YPm$uD7BoJI&L<5*CF{T`zW@aTg-l+ z%)>@CFUPgcZ7xd9OV+uJP-U|hB6pyU9lL~QhRtRdBQ$%!DZP&DMzk>NN*GNtW*`B1 z<(hqj4s*>OS6P&2KKX&p3NFp(Dgw$Kk`rah=(1zpK{-4|$|~QR@J-F8e@ywdCU*Q{ z=6f=xy_V5Z+Y#fIedZAlHBpx}<`Q-6kyV&G$c>%u`22qwH!&;7x>{-RZ5t{*ac`}! zM9l$YV@bz+e)6M)$%GGRk^U}CL=~^gV)>UGblW?WY%x6-@z!v5$ z8ME$I>Nlr;+o)fMId(hQ>yo`4|NZJ5R&hO`khCOu5E+;v6vF8=+ivW?!g6 zsS4CmOo^JPr<&Arak_baUdNtIg&mqYW??Ds3$*a$CF^cA?MK>5|njQGz*k`9F zpsj}V&t-N9BYM~hJ9`E8@nTwNjrN)JH?uluAvarTk!|d~Hq-J_X8Rpm_(M+4xm*Tw zOP+srFgkeL*U=3jEv3x*HsV}$^s}SpNlSq-yNO&Z(VsRms-^=4deMyMStVL|Li=u} zw`n62TbK_y;dCeB+%EfA9jqBV#;Pjwog(wl;cl1Su!FsOkM>!mWfp0Z!}Kmb>!D%h z72|!(3u$|!w1$w@P+(3F^HiqE>4jO3N#-*VjzHBTwzKp?q_*+?%q! zHx6^=cCt95@93bnknyleKheRgX_#Jkq$i*sDAMOvY4>GXdPF}P(#psF+Y|VNTxe!S zn34%CWI{$Jw2}#lnX6AX&st0Ls0p+3X8P2W9<_x!t!_womfXbZugX`POmjHsU+$s8`SI-4@xC9q#ux1?=?~X@kRL`xsC5eQL4HoOhH~ zXy-JM*^eKiB?e|UvtphSjWO4qZk}To%xoo!WAhxSj@DSme74dZaOI;sVa|I>%q>%% zV3!!XYxF=F*;z89c8y*&tw!YkC^cTkT)jYUH4U<+;u%0_-xaD&=jW{C`R6!&XGHH= zM~`2jA1cV)m$O&&t%3sQmva|6dti2=>&z3sCiDEHncg(iXMXMG2}-rUI>3pYgZNv< z(l>{+teE?kaR)tiS3|xn<|#l%=6cMiLh}w#LhE*zJ^AeHN0cC8-tDkv^63d9=GY1S zj>BHPPgY0l&L`Y+hk1`rmPO2R68ZpVqemYZlH;+d51A$VWI;q8BVR_Aw@R3T-ye=On*Ulgl~x ztnUBcPdADOnSEl1xpHC8vkkL~Zq`+F8$CgVoT{?oNz7P~nOSGup7$Q;xnxek2w+xb z<^S3D9(<0Q{7a5YPG<#>LO381D;p6nRk?`=B*2P;+xAm5ZOUapzlw5`je{i{5R5{QvJVr zIYS^hu^?GN% zTwTmoMWi+}Ki&O!-`4~A54{OaXiXKk*%xQ3;_d!Oihai&zuXZEIho_8|AxE%8u!IL ziKmUkbc}oB9Jl^=aJMxW$q}T{3X038!*>C5Sw#qBMoSgUPwdVNY*y=M<)Sa6Qne#*sTW#!J8!B3-c`qc%)XqXy@ozo-0;ImwI~tb^R0FCcFs zxD5q(2T6ne9wCT_oPKpp`e+9A5aK;dPQ$MT_b`z>3~_%bH-}fc(h(T}jrERcJ?A9$ z9nI(NiOtm~HiChoPwnlA+8>SE-z<@GcOqVjyoHu;v*iu7ey7_@f(U#jkZTb3qJ=NCY+5Vt%Daj*iMHH%uPV!LsBN>%>%ThRI(lfjCZ}0f}SDT1InO(pGXGgjhK<^0uR*`A>eqjVyRW z24+N6o53*&3+^*-i8=OHlHd;{uBKTqoR0klrzx_WU5))7*%7u6dmvIvS#feSHFrUD zsC)xG<@qFUwF*l8h^-b%`UX zniik>lUCLvtx{iCORP`ZIRdMj7-go_%z@=Q(p#glwZE*q$(iOBH0nrOe<$l0BfA-$ z=R1w(mW1d`+u5hEgea=-TS)@8(&m>qI(t<5mfs2%eLyb5_ZHC{k}*^KM4#zOPdJ zmHckDBPFD}f1zn5{o4J|=k=`6(P{PP zxZQT$n%7WFM%x8$f%(h;A@* zs|EVO<;`n3dmN{S#Ja+jec^aYTl9s2mYH@3opvKkBVfC&L&$TgPib>*x0ui;R=R>d zcLhY4PGl~*87Zkxo<42*gpHg&0vcx5b1zW!i|0psLP7W9PP$T`p#F;;;l1`Brfk7& z72=z9r)lQnj3K7k&8Ca?DeapXv;`U9+v(Bnm?eBn6A7k>+j;)z%s%{aYEyFw3rp_Z zvRh+1ZMl(RpkX*$f_IxTLf#$ftdZID`t>A-q~k8&(A}9{&F+9nWHrmi|5u59&+CpE zMbjFeSuHTDXi{Zf(VX^}STv<8PGGy7!SXlH=NU}j%fy(IQzSL#{+0I$rsl-lom*Hm ztvhDtfPF>snsR=A?kv@ZzM|8Oq;|)I6Y$g)ZIl4*kodNf` zm&p9nelp=UocCV!lm@3hd=Ax{_XmFXoG3by!8$GAFH7t^#t%71)G#6v?vKIw64U&O zd0zh2kSEFX!#0=)_rb`&-*Kjw4mDek+Ibp}(4*uD%V85qmn#2*y)Xec!^ALVQf@TL Tc?xweX8iu;R3-n>|NqZF&SDnX literal 4582400 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ÁK>bjWA9lxlS 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≷!5lAjD;-}(pCr*L47QlAw+&&gEU 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

ylV)l{sQa*mwW0E&C0JP1qxIjx z^iU`^Ss%?TkZ;)cTK%>|{W4xfmzL!)*n~7(7xC9A8L%?*#Z1(Pqp>F|JxFT;gz8K6 zTW5m@R)=TGqmkUD1H=|w&d6V}rCaE%H1|h;);(VFsOasyEcq2}&J&l$E1uLybpKWY zvyRkv4Po~|e$ol1L7!7YbkdW77mInR?!|eZX3Q;B*LOJ>Jt)nThXC;7nG&5z@BnjxNg_Caqkj_H4j6L0Fu z1Cy)Oy?BZgZo|1Jk2Y2a-$O|HGwWmfX{eyMzv4-zf|?xW zF^(-|Dl>ZM;UA>2`4zy)-N7CiFJ0bo*V-Eb-h{(qE`Q`CTpTtB6#RkI2=x;G|y@FV`snKOK(MEgsd0})7 zIhsT9nkSswCigx~CBNGCA&c8)Jnabct`P6SFCX#47qNW}L!wo~F hBhx8q>PO;R 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

Nff+_s-I5t#-qZ%Vj(kSP`t@+Mr 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%0DE}cJ-}WMPxaNSc;eC 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

+R^<+~UaKZ%i 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�$X?^OU>wXQqMQSJ;Ms`^- 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

XH~L7q(ZgV@a-whY_P5CA?eS-s$hHKLF&U_ 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

~-4 z^On*N^{i!{^u5B^&w6Nw4dH6n%ue>cTJ8w)vTYM{#q!KFF?Or%!t3$PV2tv1^ZJUR zXLRAlPi1;sQoB*~1eFwlW2aUWxk^0Ye2$n}rT-*}ULhhAz~hqMoa+G1PyeDKXBOo0 z{ud|ya`kQ-bEd;?&$7yy;ZJ%=Qqq68NxfMO_=|5@obM%843wJN}JL=|hgyfk(O#5Y4=9RsjsVqC?9czh$sx$(Mv$(@q$OC z{g_6dn~1(tqSx|Gg`Xnx`N3N2n}8JGa4KxH0bg+bONkV>ONx~mK9X=A1||H3MEGAM z{8_&LO#YCq%s79Hj~&}1TGp**1?10nK8*Qe>9DT2nchq5Fx@|V-vO2 z*iYiKAQuwv6{J-2#m-$~{UE_2jx{57X zc=Wm{yJrsTjPiCz+(;5580vaw_%?daHc_97HbKPond7U?y4Jo>mq>D1m3`9nV0}jz zSS00t;xSm)c2cYZ=5fSno)CIBK{Q!rkv9hA{#zD@f8N|7PiA}EQgy6IE4hI{(Shu- zc41&jRNn@OtlAG&>Mh+ZgFkM|oJhTjY6KNZSkClJ*8@Ge<20U|t~_CD!$1CL;y>wN zE)85jrceEuWt;CSn==Lj46W^aJn-*^1ph8h6!RxE7tWy?l}DI5;Bg5`Sb*(K_FqPm zCF<1Ki2hXbeIF0`Rwwd(E0OQDlJCpJV7yB}Ckpf-4?)Zz$o|}2P%+>nqV|)hVMVYy z0X;yV=KvL6I+@=;vwAm>kH6n89XI5c59Wo&YNUqGfW(I6TaRwABCE|gn*Uki zxlK2fCoT?^XTIH!F(V}RV7W`GY2*={zcEqx?x-=G^MGFXK4)B4<^---#kssSyn}^X zvo;^J?ll&~(fnxT~RW7Vh_cUnOiM z?)QE@HA>zYr@P8aRhqD6)u7~MSAQvk;l8O*hH#6BQK8IkMa!1AyYK*`Ldn}2SD}Q5 zGw@iYR}vwhx_1z?lW4R>>%NVR}q-cLk zV7g#A)sH1V3Cn5wgyr=E!P3-^uH6={e~Md*z(vUBR-SA4`)YEZ6K)2bm#Q)-?Ck&s%&XO0;R8upBQ~Vty<) zBw^{jPgr*3f~CNZrC$=3^)K&By}Te;YSDDYx%g*35+&-hPqbVjSQhxPJd=c_W}j&3 zCRnDDP-gQ>69`zpdBfgU++g>6qu00pA9Ys(9!0VImm4|UaNi+HR1kb3pbtbO3khyu zgD3=04$%mrqM{~%2$E$tgmqX&MNywV@kG3R9)v?p067E_lq;TuLzp1~K@`Ga_W!Hu z>6zIpS)cH}zxlq-&h&IwS65Y6S65%{k-Z0J*Bu|1;Dtx5@pur9kER0bS!<&~j*q3n zrKUjm zkIJr*pYedkYb(=jGE*>eV`AwI1Z@$3gbXu#Y1S$YiV3q?1Z!A0tV605mOUm|%P_pr z>W0I5ISAHP;RCIrQ@V#>t%UP5q>kx9uolJy>jHu`IULq`L9j-}1Zz76TUsJuQFCf5 zVakEl(gRki(fJouD^U+Y+z59mLHx>en_3#^xYti(+!)!sNT%%b^z2H+?2T~ZMzrj; zDElxyyDMV$SKf-AeQg_LKVM|$jb=2^n?nCC;%0XWF43T7_w;`0T+uekZ>C3OD!!Vn z;oF$ndVfMe35V4=2v)g3 z0sp5NBu0<(@BqcyGF-79#=$XFH^uUnfR+RxrVMV6DdQKEu})CNO)+JhLm3NUC=w^h z#jBTWVc&z5G{5U8`!mSKMxh@M!X@-YJ$4Cod>^$o98JcECJkd9jCXuvROxe~LvT|3 z&YJ4+79_gtZuQu0M5wPISlOH%&UKnPpM)2F$dC^=(*w<;kHmxK!atMeZsB&S zqKDep{D0BPF*aZ-paUih&sy6Ia&yr>V3wdk!cE&Xya62|dRI9hSlSGfuPvedL5|OP z&kqTQc0I{-mC)`aX!@BBx}CP;wOBHCrHtg_&7bg!GLG zjU|+^9J-psxQsiLv&X<7M;bQep^(g}rX$Ub7~x2J@S(FIHb?Q9Q~w6lGrlQQVJkwbUF~eQAZ$SB4xAoCSclMXcRSlqgy#@;AzTDJ??xB| zJiQ285ZeBXGr15xMtBW)5$qrJUwUp1{C>9JLUEy?J8+wX5bbw4z8yn|_RH&$Xg#1j z`pY=?{nA&YTMa9b960%Ycd~NNIOV%k-2bi|O;#K&9XJL}953uS(ASr>h3YzP zEF}_Zn=q(%&H>a3k)a;?R^s}g73!Q&s9z2Z#dY-rK#hwGb)f;ZofT@=P^kBXL4Efg zKwXY~2SV(A0&nDwC~i=F!g8!2njJ}yIo=7)Q9mrlc|?!Tj2zQLb9{SaXkFJ)T@@pT zGc?EIupHZ|9ycKe-+zW*CUE`|HpIDnK}tZ6wU=dm``m7evh40C`EuKQ7;k>wU{<*GUWX@u|v! z6SV7u_bax>64-r#z1SV}I2m4!ym2afC}y&u-H z80w*`U(L%4R!A_o-!@dF)+kHQlFlFk`rkn~}GoJly5AWg!nb~y=Zc*i+zt0e1w5MLM6 za+UbQ;vuc7o)*Nns+U?27gm*XDk#38x=C>>l@taosWj7C&}6)XQE8*gN~O>67L7_@ zPl`+>UmYuz2CX-!H11}DO6mMKMo?+1IsmWQdBo>%DqW5JRH8-YgO)*hp}hq=L5j~X z=b?R#{!~hFW}3+BMP7r#Qzalt;T?m_N^e!y<4smtfUSKOz+zQ+B>bNLTH===!HULF zuFkRmGGq*VA{l!S&BW=O2^m|*4VYy3`r07FAz0@Ud%O23L)=$^Mn(=nU1=87%KWf(rkpmX&P_)*5VEGSXn%zwxNu8rcYX5+(VyMscPvPIqGl-z;h zR6oZXWCQln?MSTKOUUa~vbN++fM~ju@x{v%@PHjoVWv|rX=@m!6Rs_nCJCEfoJ$%f z_(tX!rY~_$iA=`7E}WVq&6du`4a05uO;W5Ct$bifD3-sQVnFdpd z&{#Htfy2iyure&DP~^hVE3B zs88;Lf8f7=b|A3p4^R2E_T0WG9*IAz*sZ${YPMqc!3=m2cuTNopsg&($ulbo)e#1M ze}E)<^C(x#@DhYy;5rsg9lxV^Ly-rk0KbPDOBE2ggx@zx3jE7j!egN&yckr%VAvlgLE3W;!*wHZ)4rh>R=bgk z@)$*R3N5Nb1i$pK0Xo5SqK2$laM(zkqVf9&C-Z2fM}quYs_9ZhNE+aZjts4#6VHjhA6q#0NxuMXW5zuB4T{=aDw#a~Xu@zdcP-sp8 zts2qk`#RBZyU~DFe1oN?rFaRd>8bu)(_=WfTJsaM@bO9=fC}?CJg^vE0~WCGo^%I} zYOKuTFyJEOy^{n)&#NuKdI;mC6?9<)=4S({p!UU~mAFz=;#yc`+CnU@ijcdXky{-S zLV^?hm;?#;BKJ*{JDdbHjofb`H)BC}e!ghFK>tg)avb^+;L;#im;>s3yqY7(*`m-= z$ZP532qiuVi&50Jr&&mu+Sz<5SK~uVyezoH_@ENwM2V@`xPX3LOcjxWO2es;jRJQTHKXpShCyrk-Omyc+}=uOf1XD_cNS8B`TGH zn;?SZ@Bs%ip)(B)wvj3fXTmHa_eG((dvNX$b$j1n40m6)z{>0=3g182;339`cnRtj z)g)`T_#NJ$y8DyimKVq7tkFw@y}U+DZr#o6bSlv=i|51Dnc3 zUX0r?(T@?4+6s*={%R;vY@De0u?rW`d zdk}9BvololKd6h4(qP)K*#KO*4SW84SgwtLmfAEIPjv{vh~40o297HRS#T`GOORyz z*oU#Ycqp)%OmYzlN#hydLE>0){f5K5WQ0at=UFqe0yi3TC1J zbM%HVp%p9CSyhiQJRR!9-{G4VlTm3-q6SnoH3zl*rzE?0EK+;mgLVQmIN{)hBXTIy z1vo{}Xw3{%SEPo}_bbpBf_jW9*#-6j50c{8QJ~ke7zdvS=8Q#Vy`IJD{SuNKoqy0P zlvDp6Z1^fl^wsz@OoLB{>X7}LQP1($S?jsetfvbfG&|}ULi=O`r-Pt`LHi$>UTXxs z$Z!42)z(J&6W&nQe-1=lC)R>w;7X{ze|Sz~fc9_j7oXZ5#Q-g1HBIm?5+k%^{D=`+ zxcU0|=aMqFTd^1sip7K=EJg|}UMAy)_P<8p*LVYPeJkJ!CX=Ol4TAs&zyQ?2$+|i+ z;06ZZ|43L;R~K;ZkS@1ve{{LD%Qpg&`LG;>Tucasl?7P5GAu$ipONkI&}`R6LLqek zPiyKJ0 zEV6`+#Vn|hu$m~?Xyqr8&GS`xcB(dGfp)7K(zmvE!Q+9j;{!07sV;e&=#$|6zeG6U0(`h`&G zf$r3j^kGu7*U$yfNJ(20ncVv@G!RvOG!)QtJ(ytKQloU|SYe93TueKXetGVY)&4yF zzR^S`C!r$UBUj{hqx^$R2gtV6)2ZD;yV;#Ug-WGKPtna<%WsA9(K290kPY!YZqw15 zXjxyBj}{w=ko%U(`rn1}jRrDCA|qM8@(N}-AOooS9^OPb=z-O)?~#2g$ChfRSf~@pdSTy9CC%t~k>bd$or|0Jj%5h!B7~ zt>8w7f@>25t|8t`THp7MP5`#vE?~;D^)}^NV}|B#JeFP&=Eep=GwY}ob8vQXQ}w`1 z%Mr#{|H0;wxM_2qQykm%CI~B!?b1&#UfudA?y5!j7@^v)?QDY)vJpN(C`Cv(hW)Gv zn-DJi4R?(ryoK-s!uh|qvn3(ikMJ%+-{V-jcLMuo5k5e;th}AA`bn$@L|BOM*{OE6 z^{3m}=A3C~%liX&;8wJ=^+$LPA)qDH*!a)j9k`3MyV z*VK%+O-ERd(7aZ>?RJF!AnZkGUpwB`1)&$hRD@3uE~yi5JA-g_-FVv^gwE$79-(i& zc$>R^ysZeKT?6F7Z4jIll8dZf(zo?Y%YJE3NuBYT}%I;Nr0)dZ` zm6_vGl(J-B%6gZg!8bP1r7U_Doq4h^ZB4Ro<|cf}_nbTZ)-y^qigo5)YM{TlrRH%qDO@q!d|lJ<{V$yVFs z%qUjB!6OiY;)EA#GuAq(*_Sh<W)jD+b$L|Q< zyhNJuf(FhThpd>C%R^;=)VxG-aJ!ekV`2{TcV3Qzts_1q9mXra*+otZ}YI*n$FS4pE+7YEU3+Zz@dkwAcyY@&k1l@Li?*^^|A1V;T3 zk6JV2bsSHztoPciz6o1}x{giiKcpa$xpaZ~Qmi(`3!l$O>?MhQ@KOVN>Pc@GuUMXg zuRGLqykZ!TsIL{qI@e~6VcybmSGuX0q?qg{!`WMc(&79nR@?qeoj(fMq*epI-0N*p zkK^=6KJuC6W&P_U++3$0@j-L&cqyfeH-B0yKC+R7YAfs(2d*hhR?x7Ol>9f7vf4Rt z9bx@-j`{@-o=ue#mYdZ#kXTT_1s?GeQJ}B+g7vR>38H{Q=O5#^S?wlX_4ccFz$Qi_L;>xcV z2XSSuc$Hk)hSw0TxLqiII<5l_t4+)o+#eE(Y%^VibgtCa zMM#(06PGYo7Ll8)CXX+|xKh(7^{p!{Bzn<&DOTs>C5S{X0WP(Dz0~>QRmy@}l=>Ex z`WTHWLe%0DZ_A3dFiR;{>zOab>K{EqiX44W5LeQ~tK`bHcn#sozlbaQaKUdFR~n)G zn7Oj=Es5--K*lhfW{8$isL=k?T+-86^4?A-asTE$A}1kFfL_Sr~+45J}y}&p#R6 z##cCKk}I@HrSmS$ZsUrVjc(%&d@$}<*{m*JLSMutsM6kIfP!NMX@D}BHbcn)3id=aWHK1r|4`&8maVKcQWTk*&FQ(XdTrE_nU)|tR=B=ei z+%A-+oTsiGQ_bf0fC3+T${X5qJEKj4EgyWh8>d{&e)baG0>%;Wa@jZmy%lDhII{Zy z$m`0uz9XMWef>x$owQv{cXMNI+onFWmn>NtX47WqHkTg|jkZWFdy&|{v9-FQU;h}5 z61N4S1Bv5qoeDVvvj}nM&9OFcom^Q#U-saOD1L5e@muub>r?Tk_K++ZNI6SWDt zvEr%==#nXeKt!5)gxg`@rcKx+R%WGDqr!4eb{816W}wp!*8gEl4jQtH6BkczBF zyrm+`3d1XslPD_U{|6}#s&4;KoNv4^2xp*4XfsLcfTKFvE&vepv0Tf7)8odWEp_FM zg<2;hnLqj>jDHa_AxtuKy4ZZ4gy&V3cS`ds-Yab%xXpzxqWm_veM|!>lkH2jr zNCV$u^fm5PbpHVgud#c7!4E%gvwL5Z&-*gRaolhEIqW19Ghlma0i+OAY6s9POazqX zZ@TE5<*C?Wd@X`physu=IsTobtr~>6qf7_ls+Vieg&CGhK!Pe zq`WCono@q%3r5R&8;ACnIs}Nm(LiuJt+vYwV(9Y*h=~9pP6&+3;}j%ejmG?<+oqDl zSz#MoN-Jju6j>gPx>J=JsonFPOKfF<0Jf5YAfAWXVpI7ic+$7SE_(>ij^&s~r(q$E zgc3tLvHwNc$Y(If#ib6=JL>5TaX83<5AgQzc@7x;)?GG@%87b}gN^|Wv-}O(;7w$g zt6DARfYSv;xQgo*IuwzPjS%?q9od{3JCRU)^!)tAa;Le2$dEajy73LF4|)}6dlC7I z)OlZ#5J|7_*vB!Zig4$GTuKAIZA%r$@T;qK>f%?VF20l+DPtx`KsP86bwf~GhwMZk zL0?frc`%&puROo>bYUnr#OcRB7d$33u0v;$flfoLJ5-%ivS&?CI-4Jo5IKGSE=Ky6 z!Rf)&S`F{eojJ*L>614xoA5$ONmOiCLZBbf0Nbr-T? zmvg-yVwbbHDFFBo{C6k(8Th$z!SL^}!hfrK1o&xx3jVqf_;sxC2S$Q_X#AhSe`g5% z#oI0PIoK@%{{BA&e{rK=`dnv)|7axmm;EXD3mXT+Kd{Y$|D}=OZ@&G{&}VT7{9G&i zudawdpL_li{7bCx?}`M!;h%zkA_V_+a4nkh9|S*nZZchsF)R`GrYo^7pY|wSss2fc zyPfV|iqY0wimxSB%((nZCeOx{N%wsY`=|NnmU_TAf-zU%@{M1Zs(i?v)Va4OcJ=0G zw!>{C{)TNkw701zn(e{W?fy4kB0wp3IFXs zp~9E4+M(poF~h!;jVLRpI(iFVN~uy%P~IT18V*9qottt5N$4Em0=1o|q=w6v7VxDU zqZF+<#rtzpPT);zn2k%j%G)GXo11pPp%l$cDR=L-^~{Dzl(Ll2_l=q58-sl*>5Kht z8?3!#-}vw03f_vab4lPpAduC>ebSbI+oJ2IDmmFe5L+yEEep&7-q-`%)1jKo!*y?=e6o~!r$E$wrjf&uI7eM3uo zu8*D+N|?1XO_@tfbF6bu1Q4n!Av2JXp+eVE6i8WT!h{L-_FnLT@@AL0MM2H&?UQWI zUOjQo8`=L#QXD}&oIP_Y7B4BBmaia%V(sDVyw-V{dOwnVUEX{B%&SuaYvI!lKrXWC zK(2bYdLNs4q0|3Z`3Sf%JO7kg1LpH;Y(x2@UrSrH(fD5D7$sH4%o^xhdAuCnYf$wf zx*WWp&->}3MGaAY%C3Pv@9{-%p?h8Hy^0`Jn=rul+LZDescTFLID6+2$#Ibm&;_SR z+#FawVzaGHnFUs5J?`}6(dFV`q;a27%EA=ofWy6yio$K92kO~pe}fb$O3n;oG{y-9 zY6A!OYyWf$+=tGBlDf_su(U9K_{w=v4cINtg59+S_VRYhL;B1N_FZmPvldhYO7GH< z0M(b5@2QQ705c>1?m(;7M!dRDDcfCrXj1|aMV5O{(#n- z&iVDvjwdb`l++ri)a}g-`@~1uSOZmd(Pnnep53E0Z@d~sZo4UCcmc(Ucda zW`Nf)#2T!^?8;WhiFHKF%=73W+ilCNm6EeB(~i-WKW(x4$56rtZA(GJ0n_eq1lBk+ zPCIMNd>`ZZ_dR)}4MelbY^Z~>d*4Bv^TfIU*7VxF;3@{Ak8R7uPcOcb$?hR(Ozu7Y zDLYy)Moekn=WMX&bVDtaG3#6kL~zV3?LwRu%#$;WNDqwrbfcDHef^tmQBB4`N+0&5UjU2Blk$E|O@Z(M0gWocQi*Ci=mtwRc-58v zM!8M-Tao%jJ^>I1YLFF|@{(M1mM)6*H=DtiYQTswbVQL_zF9A~Nc}N^rW8PRn$Mp3 zK9jZdN>kU2Ap_BuGRx34vkW-8u6Y~4gsB8>_|m?2D$kIG?efnd3a9qo@?dSE?2KA# zVPjR(8I*aq=VbNF3EcyplQnRY6RK!`dX-v&@w-9OyJk5(W%}U$$W=t4dCK-GUMK|p7Jg( ztu7~DM<_zw+4DbvdZXE}UW@OP~#?qPE$;Qr~Qn3S=VI@_eE*n7L%1^XAy~6IIsK248h3SG z<*ZSd8psESez~Tltu)TQH0AvXj?Qb7eYZ`X;OmDA-dUU5KgG3jbLUc*=yb0kyTtU) z;kY6il0b*f$LfQW?)lmUCjtTa?)adZ)`;GdhNerO+Bm0D>yw+Ub|Ia*gG>0z$kB#7 zS-HjH0_CiRCmi6CZJa&lIk;(@zK{9VvF_iv(EVfdP~^h3%0tvf?b*#RFYyDUmBV?S zlel9LJPoH2JIS7VC-k!2i+e)(t~c96+}a43G#)Qfi{keM1b2ip!Ty;!=S$uyayfn9 zSa@pow%r#HV;ovLBb-_9!;51$dWzI7E6wasK%D&rJ^O3O?#f7miw^V2X{f{w!<+Lh z99{b8(DB(#z}hGP63{WlpSC|_6pp*;jWMD=f{YN;Ql?QScE%m?%gOk_-z=*5#lRGb zkmS&jcQY4RjKZp$1uf*UH^Hp|PtS=!eP7Tzt?xbtPMRwhLyQhY;OzZf)}^Rn0><*eUIknus1LUVDP6vvrM<#EADDa(Nf;>xjavdMQt70l9f`7HPtF%sv3x=x=SiGwYRWMS%cC@IyS{t^{52n8AI z77#%)+#*gepgqz>5yD~I889wFBCy>Sxq%}2BXotub&D*0(vy)76!l~V!PKS8m@hg@ zV&Vl?)8*?`>R@ih%8&?X1o5O@E7%R|U}KSOUZln|V$2oLDI!0NOA(~xK=Vi7M=R|( zX8Ks^%(v&-z;^5{fN8=p-l?}{_H&=cGRzrv?=&bR_h~Fh#q$*yO>v5Bd(IPhCg$Xk zAxSy{?SyavX(os8GN>iUC}M?<7jU!adp31+T&Ewyc4<`g&GB{k2Ua}|Ctp9JHv z?h-Z@o7$I`*p~r48<%G9SK#ct#p&5|hKO=rUXTQBRy(G!FDzC0TK%y_Bne{oWn;qR zb0BY#FhY|*{th>q_A)*S#XB5qkWB-Yiu%k_vq;&(Z)+%o_AV3|s(7zz&x#imXEu(o zWS5G!gPy%}M2y+{KaPESLgphX-nB7_zR~2v1MQTkRIYR6s#_+$#mhTT7LsLjbrkKT<68O{?IH`6OI$I@K3dzoJ>V;EYK;7$!_w+aO`8X8|dN zX-7Q)@~5Ve#Gesu0z_pm&g>O70x3`&A^W9dGtfxQ`dor9YA$49zzHHI&Pg;(mRq|zV#76`I`;xjd}5Wq-Kt6v3sAE z&p%{7$cp3#d(I)m#jZ&50ol?OZB}<-;txfVB3aNR5+!+dYr|Shx=ahH2Y-2=zKk_0 zQ8M4C#LxfXN>p4;LrLMSr$%JniYz_!+#ka)(32FYZ**dk+L{N2q;=FlV>y3KZ@WE& zEgLHP@xi6Q=yYKm4dYE1q=t*{l6LH(97opiEn`Mm@5F`uJKS;?^(WUG_V17rjxMB( zF}=)_&A1pB>$FZfm1oJTmucrwt2*OR%zqsL*&+YFmE!hYWuw{*f`}56 zy6NsD(uo_L%62sgL~$r1z-7FuYQcwl#ef_sX)WZ-k5{vKDr#k3>Zz!c+169hF!Ov* zg*|IDgkc8`*C}mbe)u`bEbbxRs6Nw(ln-WgH>wIAd2(x`n$5AYlKQ>F8{3$)9UDwr9)Ey` zFgYfXKUH2jjI%z)?cZGu67MDwI}}wpWx&~%y zx%wVn#7riZnPUYY{1QaDI(=~u)e#(^d>C!YbEJm2Cfsub`w8C$t2}`k_M91jm`qFR zAwY%dMPR8bV=yKU;m8_1(y;yed`UF%P#?gzVx^J)37g+&tsyx<6pyf0IYo889hYP9 zQG8ivgV_udO~k4*`;g2QCi_#cgmj1c9(Tv##jafgh43AaH!<{tCuP0%FC_ho7y`11 z;rG`BVb~x_v5ACXPhjZi{fpgu9Ud&?L)8ol{%aL2;}vw?s9u&$(9a${F9Wu+DD6?y zgVD*uLpVBY=rjmI=ijsbGCD!T`*&}F4i!ZRuyhR`sXhaTn$B}-QHhnqTM)^xjcECk&hKD1&iP}fj1!& zo_046AeWJxp#OnPx`niIJSKc$dUK5Y!ZWTSM(Rk?!TQo0ObsT`=2EBfE49|tjAGp- zVE6un5=a3~f_8ysZeb_&@xm@bWqAU=A(AxQ_3x6bvN^w;Mzs@swB*<@*Y#lx54!Y0 ztzk$R{jOnbS;kif8lb%1ztf%c3`5j7eEv4VD7>Sz&sg07ORmCbQjnRLP4%ovxJ8>w z;5{2Lu2{fTa`yi9zbVQx&b|9iM?s5##DYB9+`yt7kIE_49`5LDeKW}IudVEJsYY~6B5rQ6{*Ts*DLeB zqQb6Myj-X{r7=7dJY!9qcL{kTPf!vjVhrU7orqT|Hq~Q@7#?**_}94(MN*-}3hj3|1#A+C&5-M>l8D03>fI}bKJrW%-ZZBY)}jOZstcU!FHRqTg}uvHz0 zgpOrcm;qMmpVgJ~P603Wc=9l2IZk)vu}r9o8r}#=Xa2?RQcDc62#6Bt_)>b2MAnXc zKOvb7KP(C-hAwk*1-7zR$Vf|IxhD^S2d!YFVR68bZK#qFDace%Qo~6sG)XnKE(sRV zMIFkIkOtD>NiJyaAt^b>GY1V+<{cM{3>!InZznw>h@(rpjxLHK+=Pw}LB)a`t-B)~ zLq-A^yHLWWV&%s2y7e{H@JO^PKb(0S?rQkV*D{%fS^*?HLs=p}_ zu&E>ScNP`0K2(w~i4mP!ery+T~oDLLQRy>k#rvn2@_GR{P2 z2TN$M;@wFsaQfS>bI@GHQ}l+GR8SJ<%2LU!DO7q(fj?!A@iQhE%(fS>bUsUZPewCz zs+zySI@CGy5~uQn0Bu%PY0R2}YC*}hEHWq*w@6l3qQs>#p2XWQWXuW)G+mZTn@BW8 z*TW)CxV!>&D~L@#1*ZZcwo1c-@FC9L$4nc}(7hqG5BIO3qv15*1jCaR!f{<+ljVEQ zD|+_`a!cpbb2ZsH6_P{iRvnhVNwo{3QjH0gc$y@F2_~s!NJA{N((Nt-eWn+h0n7jW zutt*M4QNTukaV`6lBg-9VAc-?%{1jZn%M*Sja!)g03p7_^tGul!C+A-sw6S>6jV zK2je?l>0P;&81Z+nOC@1GvL#9+?v%0?ZV%%^V?X2?|p9G1X_e&5Ge7_-iN`zr@Y$K zobG|rIZ%Ix9m}p~m@F0CVQ}XP8AIQ}oolMm-J&xR-L3wGk7f;t!JbRmB}U2a46xlgZL~U=czeYq6wGFlA>V!XUK`aFo(%28R~X zF4RKW@4H7nmZ>}Wk&Rhc)fwe^qyu3s%R*CyDem%JV8UgSEI39m1CEwLGb5uKgivUV zy5!%4lWwq!EyIq_$WKcch>|5dOn`-8!25+IJ#?Hp2@Hr7Qs8*6ut=tMsz7=EE)Y{; zhvcwUT(=4(W>+Px&^Z!;FR%&#rUC;c&+24js3p;nQr|HG4?!a#y%O96FfCMyTgs1W z&Wi?(R*XZrG+XLx6-J#<{-E2CAPgRM3W=dz*vv7ems6VLrqUzQitTadr)5rlbI&pFuxhV_Ljyj^&Y~kxfwg`k? z8ZjU=O;<@Pr}DJuI~%L5XVB1y@zrew60$#`i1gGFG|JVQHy}|eR0l60Hgogr&yQk& zNqWecQ3m&oiD88HC)&PM1_V}(u#VrT8OdFkk_e4aoBhj>H*lV}@jK3Cn|%5d%JW}i zw8>ne_Gi?>z`@`~3D%mm(OVxlKsqO=#Y;ofh-G3#k_3ax1|KVt7A*Xbl92Q;Ndlt^ z6#t7ZDNGW=HAPpg0o;is8{M8XWaAR~Sgy9^N2{2q|3fi3{H#?>J}1pIfz$~Lr{q#3 zr5&mzoa?HQ-#!8x>F{>JLUl6Tcq8;Q5#;`$!lihImu_tkBq5QNVz>aAy{LNp1S!Sg zFHy0l%+6BTL9~uqTeKYgW8@+Yfn{EH%Kid7Ga)a8TM^N8LGts&eLvFI{qhBq;r z2F%;n(bZow8NS7?VAYAupA z6w-}S0}svCQCnAAG@?U~>5ZsN-NTR8mQyZdD5d)b3|*3f`VnE#aqwAhP!PZq8L4J~Qm;o#|{0ZktMW$r~_UQewa+{oCqewg$ww7=-n zNrb+bg2`aoq(SYnv0rCxxmrg)s_L0}nDgbH3pOvBfg7|fH3h3dVhH0)fS$4$m) zV3<`&NcH!?WaDE((^C`IJCF19b75GTQt%yreG|PW3Kdj%J(>Qp zNwJ4t%8yxy!7laPlUg9npLU#ZMxCf|kYXWX;rJHxA_Its^(zUOs0fG%lcK6czHv12oenWC$%$>jTdyVU(p9%()AX(K+%%IaR2p-V9Qexm1bI z$q<0c#KMOlD?m~RAw+gv3!d8m#u`WcD8k0hS>UR?q+dxTVFzD`}L^*QyaPXUWGhH4~4JI}EPCCd~0AfGQ6E4RzJh6PivAQaZ#yL#l+Js~eDlc`THY z&f|{|t7FPq!VupHk!)S3vP)9N>>`??i;x3qS!s;XgGj3@@V{nYg_E{Y_41@>c8yhg zmMKjTrZPwY2~$U(Ax!P?7+shiKmIoqrc;@gN_W*oPfB7?)5QHRh3cpyL=5beDO88x zktRN{6_ve5@FcKLhU2_6ykB~Aysbt?yzOTAK&)ZF z=y=;<#4ESN+lGu6`6v&iCpZ)+zcnxAq4W|C<)^&T158tQEor-7*d4SE(+zA%gC1z(KHU(%Q|kLYzXe{x=@8YX))jlKN^7KaKh7Iniqtw~ zw9tqAfpk*TNsxt4MWn-q4)&wmca~k>=>*=4zqK$Xi z?F7&L$Lw}$A>2;;7P8x^Y#PW&ZZz!ji4HIKJ^^nQ!5dRm>37~GQr|$SFRv=K@}NlF zfl^niDs`V*Me5(5L+Y)Vq^uP0z-=P+I!gU|RjFUr@qUg{PpvBTvAYHAyD4=lQu9ow z?y%QaaMB6@R^ZaL9brL2sLz?!6zM*C-StcpmNCH5R$>*(^1;wi+7xhmMFO6Z{euD; zowm+zI4OY_s1#AOx@sSJm>IuiG{8qO{y|sAQj&BkYsYDRy)wc1hc1W%FjUeKfgcHR z62!VTi#2Y-<0HLy3DKN^71Z#ErasN0+2x^FMDxtOf(`pHD$-UlfJy{8euPNbTpH8r{v5x@T3Xf71oI3#G1CRqAK%5U?wrLFymitf`dh>O_%x z3#ERqs?^g*i`1`B>TIN5k)%tGAusAP*fYEACPZsBVC`}O<0A`G783Xewbu5*-l`P0 zNje95P-%@(n%@%$d;vuYkN6BMDkc6uMf`V9jEVoQI|cUpo(8;C44@MJ4<05`e@dwr zRFyhgm#w*!nr;cJv}Sw83D`GNYPu}D($w4ZmeGY$*Q+Y^+6e-71)Or)4{*p-3VY)1 zBJ~zZ{T@=w724|OxjbEE@Qya4Jo=+p&GF;`bXv=3d7JTB9ec#o5n=&X-DTrR3_6^) zYMAfQjw{7z$4vsGYYC&az=-rfHuS(v&>(j2Mfgv0KpiUNAQR4a$bsVtmV?(~l7j`h zI{x7)l(3uuR3Zm&3>EmiN2%vkm3o>EJDXCEtt$1wTLkPIDRtMXQcu$5Adym6uPXIe zowLW`bN%Z|Wvt>8vpz9(b`qkpnpfzG(=_S8M_^W@F8!6ppQ3BK8*$-iT2Kf_3Ltq~ z4=**p`#WqqzY_yMTJKe)cG*v}1z4|*^Fe1)V_NIVDB-o8MQYq3Dpo9aOygp)Tt%#3 zl>N8w3W<4mu|^CQLJ8^!qw_Bji2W6Mx33nJ?Z^5J>T92KxLR8#QeLtl}~Mm zqL(*AGO~>0MzMdJEGTw+5F0}mEVR_Vgz^yB&c`(iI&X3w4&hB^smU9%k>FaBc>TA$ zp@G2J<4t#{T9Y>y@!MJ&3WGNXc0vYk?HG9O?Md&-RWe9A& zmD!5T*VmTfzDf-nOe*hHZq|k10ZNU#pej$@cD%qo znNsuG*-FO?x}DIRQvZgURocI0>q4Nyqo=K{D)lS6fGwxg&mwh@$RtDkX|yTMDl#NG z+Ino_iprdZI>O@#D6Om{!=?nx`8TrKy-(o3yVPdOACx)DQ(7&vBmUXFFPy|0kkeR! zso*n@%w(bDnA1jx0;3Pdi}jwm3_d-r5HKpG(sW&ZR#ECjNX-M)(i_~R)qrVcGU3$) z=;(+1k>XvL4q`lyq!KUuCUnKSwgOu`gXq?FG|pU1<6);E(EshQSMT+3VyzRasR)rr zT+{<_KYZ*5@-y+HibrfW5G#ks!DKn2wBy8h+i(OgLKiqUtl>Y1D?vE_PTV1S1V`4_k|C zF!26(+x`dQZME*j-2n(25l$jpa$k7fbi{cOb|HL-AoDhy9B=E5&@~fhu_9C;SnEp9 z^ANWjAu%i7HWt5iro`K>L})WL-gX5-X!&Q}v;#31qpuIhJB9C*MP0ZOH=%;v> zzxF6;r&WQHobT;kl4EWix#9tWNloNRWTBh*Yzb&i9Vx$(D~`o&_F*kP+a)1c*@y;^ zfKdzC7Q97!2gW&CEo;_8Gz>PfPSCR&$HD~VewK2tLk6SZ_k(iJ{Q*+xOG${F`)11R zLk36QFX&uYW((C!=o0Ng#ClHGvFF}`2W8&#i3$7tUeM7w zC@29L3^eQF#gX@$S!xCRQraIFT^dP##)~7b+)VO4CHc;pghtVhyfbE!JW8_En&c%; zVnd&j`;JO;YZ#n2LAhI1n)_|a{T+t8M!i?jNZF`&{YrD+ zL%CnD=Jp2V{&8nz^}d{Pk3|M3z??xMBbk$DMSeS4F_$B* z`=FARVwZ^{Z-$w02PNDdp71eF=rt3*O9|fzPdJ_v&NCBwDWNAk;WeCafthd=CB!zb zP#l|Z!WYbh-6$a)&_azRUS+)hmOpOGT_ksBaJ z-{Z-V_pzDdYcxP@3x6Mi?{t8mBku$A`-k+M-p{6dyh-qM!&j?;YY?b*Num(yzTjQ2 zMkW9Tg14T5E(tMo4n49rux>yPNU%+7hFp}p`dLOX--_Zmj9sXZ7hxVa^48;%ZUH=n zR;fLSSbX))13pCavGh-f`y_k{68CPY#Ia%%!VLsrh)iZm>v^1To0+g9CA=g&VTqF< z5(jmh0%#ms1%{}OyiI1TMzJblsUF+;>ksDFjr8>^{VScDayGeLf^rOQH^Vu2hBin; zNkQak!j<{OM64Sjb_JuWnz3yuwhdz09;$-}XoDH1HYhWb9z|o&${;E*5zW}|DVB~N zfK_P5l2c8?h2@B)V`_DJzE82Zlm@YM?yMgB9K}A%v31Q@YWqB(cI3sGvG-8yBu?AZ zj2%I-!x2jyp;0tw9wPh;PKFDA8Jv7tdnD(&*T;i625#cL=z#pqc2gE%1ky%aAMUpx z--A9zpy7gVOu3jpS6>GxVY#V~76R%95@52_Kf!~5+E_q+7*I>!Bt&U|43^a-1`;GfQ%83Y3> z(I?V&q{(!kfLN1(A@dmQS(0O^X5~OV>X0UPI?j1nhC<&uo7Ky+RY zgFhi1k0ks1SKx#d7!E(c8dL@(+g~lVd$y3r=`08?d;WQkTg@+fLL#y3*udZtVBp9r zF)I&;n@zhFCC`5yUdksPj<i9^eet&O2yYjxZ9zjqnyi z1@LMLoGwL3LKu!P31K$Evj_#ik*>;PRFCDpynU%5j(PJRYl>(a&&G^R-UJX&**f zu_}}1aIhX!tn1U)HwN$hr@VYJi7bRRUGnxz4{aqqh=aZ4YA*Pv;DmCpw$)Fm&`*;{ zReU`9$Hfy>pHTtteFl`dJXJ!^K-;RtX&3SX6bv4DazIbRD?Wn{yr4Cp`e7I3KcgMS ziX(v)R4|GJO^2`esL~F138{CSaPG;kp6cU*Xn&bJ#wEWlA}vbHC?|&WP?Z5Ay-B`s z|DHVjUi$d@!N+$h+5YLvnY0 z8hAftUboRO*Bsp+!?Hr#@dA5R3oI#|k3+2OpE_&eWcxV;w|Bu59LymmY-v+mxF(Nq z%B9$mZzz~4u!KA_VY%m=57p!EAMlhnx964uI<_zSn;n}>Ga|D~p4NSiIM9&}RsN^v zB)p8{p@>mFNeq%Y$#C$cbWK(^z+%8^M$@r4^FbH~4N#Jqdb*z4@CF8Da8U-bv2YLL zNQd76tZHM~17n4|q^@~J_W+vk28yn@Yh-w59soRrRJ(z0tKS8>kuz+h3^cwnGfX^Q zS%&8+L#Ln&ODiqmPRc;Gqgg`jN;CAN4C{h2q*sbk9m?=bP=?nk&9LKsWEhVOIQxAh z9mCNi1&46a-g`_d6sebJxSu)0M0e@0l@Y`TU}oAu$0|!Z25C$C$Wk{1mHMGdy)4gQ zT)YO;+;j>$Vbkdv%(gTEvYweEZCO8>MQu4!Sy8`EMjhP{B+mARnJu-_+N_{#14TAS z{@ucc(j|ZJ(aK89rmS`n-Heoxo^n{nWQt*70BL}X45E^}m?ul1uAA4Pwl`Xo+MR%{ zLc61eK}SV2G5M2=P14#ML2WV-0~czOk%fH!eE>!_76CKi^d&fB5sg?nU;_`tz`+=fc{n=Qo%MMDkrbpzoDvWy^AMi#l89}ZgxYOz$c*HE(Obeui6knA#lv!0Vkbv-|lbp@tN zgcEP}t&D5^6Tp?mOd@uYh^6+VU1r1%7qPp3L2L`evJxk*^${P|Qt}F5YkWu)9~#n! zpRFJ2iw_G3)F%1>nxf?wslSe6d>*D8A6Y+qD?Y4(PT)yJBS*gYkV>H3lmnv0pcab{ zT>+}}ZX@;y5j!1lO4E(lhed2n04eQjHiBf#9!Hm#Pwzo9)yI2R3jj}1hNz=Oxy*V@ zImzx+WY=?#&d1{JoI`xh&k?b20O$PdsQRo4nk1#Z+*s0L6$w@n)d1S%$6-RL$|&s3 zeh|M8J5!ata=LTOYTU=z>T%q|h;Zb|=pis)e9L+Qb}+(Tgw{x(UP+!;@a;{&{T87f z^7lrVgzzFlA;Pw&;%x^JYzyLTj{{zF{9caW)ZcH#?{tKx^!Io0y9%LLe?N?0i+w83 ziyVQoBE=k|=NA}#WILUL(UFz`p?4^lXyoHrNZeipby(!-pt)y=R7!43IxtPy?#g&a zKUD&IwdeqPoJFEG`Di~4v%EzlXi&B+Xzz_BMv4_%#;PEgtlWHI$mmO%=u~i>S4NCn z*cI&z@cy6g4W!{nK-~10iW4TG_-PXTd=#us6CFxNV7ua4#-y&ZcOf0%$K_7IdZ^H4 zPGR#?rnHpSVjvFOB;oS+X)tlOFfnqe2Y=R~%*!o*CEk`=qbRW3sP@t^=d9YU&!Se_ z4ULbAB`}k?##jA!H8$#TWb`?!hj(SvdMra{Lx^Kz81@?y=l*IvE;)DgSh*r7@rA=0l zv0K_;1sSU7cgc(}*m>J2#SS)~i3S82pVuF^P@oBw@ns*BqJjoPdjc1~(D z?Bl2m^4-C;8F@BpQ^P*zORLZw8hTD@Gv}kIwOKKNYcu4p*9KBa20NSRSVR?v3vAVU z7SkFM9CKQvRtFBcyX3@wFkBN8!nDu0{8AdKji&RlC&`H|kGNf%Z>pzp(aIO#v;P;? zI3nEse7x<2=P+h@Hs00%;a@O_uX`!pHW(otp|bbUmhY5~)0Ug3)b z_+)#hvQa&T>d<(FkN)4!bt@Cf#)egIPJgW%NAC`>m$;v@V9~aZ$S=4h*?(ssu*a!f z2PZ5BGTU8BHlO^hWb3QWK`5MRN@J5$569sIS?q1+7Gl}E)<$!^jjV1<6-HG81x?1iwC#NnbAK5qHNJX@Rq8h5+%pa%<9;OP2F zEg6ri_QaWIf3-qbiV>+o=Z95D=#X<%A^X{@5OpOHIMnCIP!0(-a*ir=?d>#oD5p6Y_zu1%LD*{}ul5A7ix;_@nnB8`Y_U zNi`|d1|6T9T|M@8iF+aNjhdR14ilHxLl`moH$Hw_kHneM8)Zth-g}`uAVGn`y9hk?WjP zsn9BNS|#C7I7e05 z2G64*c$b`;D&;Q;t&;F9oTDmLvsP(X>N(-kp*KUTB-{+=s7n8UqtxJ%+j&l^)HHo(bo}IjZ1vI3ZENY}5=Mm!JxMG2on3aO4}I z6%>Amb5z0S;GHDrL>Jj7>jw}ucYMz7iSr$wvws4ey2U}UQ7uR@YVY{Mv|4aQ$XT#j zhP@t2gL68Rcp4r-ho=Ir3cu;9*r@)LY*bMgC1>#BVl zzrGP~vxaVXw}uI?<6Ja7P`amI6JZU!nf>04x83&+{2_10+cvxvZ#%vu-qrzO$eUPK z38%xp_p!DS;WvcJ-p^RRQ#!e%EGd1cxL+LW$K1b=5_T%JuKy{~=Ip)(^YJCTUQa1c z%Q%T?U0-^he)k_1fMnIf8Vjt=pk*9%#dsd_iYvyaVR%GWj89hI1F>``3hql3FTRuy z$iMQG{N%!X@Ep7(E9pa0mF+3YDwLZKzY*=pyXUsi6PG#l? z_MC2r#kr<5z;ybXt?yGEEAH8G;>%hu?%s9!W|DWAQt?nz>TsGD4jOXehV|()oI#@{$(~_(s#2ABd_+USxp||Ihfb6=MqRAg?Kx zxF=ovKVgYBF{sq9AiX5j|L-aGGKMT#9xb^txIOrPBgEQODAj#rC20+BzSIZ~Jh+eK zLku%#LNnNNTAH{e`>$Ue+`6DA%5W#_S#Dn13)r$~(&I6d@VDUQb_`{-UvNm7iTgLW zC^wj7=R_fCI7QM@>S(^6_irHmuVQSZEyo5#lVaokF1R1S$VRKc&P4RZwW#MEiar;o zub`84AHhKeXjKgq4(KwOUjB&Nb5ekh(4(O*Fv!Jc2AyT;Bn3yg*%= zs8-ukJB6Kt4rOplw4av-6GhqF(KUU6R^PL`hP7xeQy9&%$Vj%cu&QaZQYA&t;-ZJQ z)C<$i2G}~N=Xx$Es#S6E%v*P zI^7cg7`7ExRs;3jB(QePZ&r?-!XD%QML*dOr5Yow1UbUO@M`%55UH$Mygx#1fNi~E zl$BSVG&>zqVD~n`e|MlKkFu^~U7y3cUJT+sgB-H1$MA_|Q1%t3fZ|?m^r7Ux-ELpi&!hi~bv!Pq+60#U4ItrPzmq z{wCah^BUm(1uj~2ADPBOkl=RnGQL#SZtg@q&vv`H$Sf1GSLg3S$+5jb$*dy=y@31M zssEdHbL_CS)TeP9$lpTKbh#Q7>kr)B0c;_Jg8vqZ-0n?hbzV*?-@6rBT#SRUgG;J* zI$^{e$j5(sjt$v$3*g85xWnU>8Ny>$pv8&JJMP^9^c2)HS0$`Q-*5ELp{}njR@av{ z?U0r9_5Mc;uO3wDg1-yHd-nvxZ~VndF*W6X(?qtE8gh~~FI-OQ3kf+_#^Q}A-F+ml zHi`66$Ze3(+y@_Z9Sfat-3_4xv54$hF`B2b(%NY>~~< zVx7x@0eM!Vj0*m`fjnjs%R$u9g*xv_i0iA^FRV{+V18pbFZvXRU{1vB#{FN)*$q|9 zZj_~E>`qn=I*t8m`s~I;%x?6B0&`(@<4s5aZ(pN(mB*>ueLS(@gF_ulU)PB@I%d2~ zU5*zViHa!;@@~_?;}d7l&B-a_@Jh#Z--K6(`w*^2hGe}fHgFs*qdfXO5~)Qv?TSia=6ktD@|GEGT&ut`jn*$$s~Ra8>pgD?R<9~Tqg88HBU9Dt=XTc}hXnM#+& z0I^SGh*MfcqZBF;3$fARF#ra8iQ!&47QlTtiP^peOor0Wyp3D%WLbQiEh`dl zX9X2~19&T2N9DR+vs(a1)6N$L0sk#_;QxUSorwmlqXGZ2+)A{C_LwnGm%yJ0qw-lC0-1_>2 zwayt(F)?&^<1Ij=)puGD;OVVn0^A@5!0m$oA8j5J;E&)`mt=^B@!I27QaFdj%%R}` z?mpazd%pK{>W|>Il58n967`3oNZC3peaA12BWjhyaGFD_~#@5XS(bsrl}!8xseQ!j0~(z@AG8{Y)16i&*Gy;NB=dNJVW%T1lu# zWdGqeD*`_Ogk(Y$Dr%Aia5OQR6$Je2*lO1#2EZ2w0nR%wCbBJu16`6L8pfNCMda`s z*GJ^=L_mbJ^AHt>5^>Majir6KEGtUSFdpS#;CenJr?fL8Rf#h0DH7ig#pi_e&#ePz zGtmG+h#Xs^82vy7)%qRx1_mngctn9Kc%8ks{x@f(9kx`L zw59>ZdgE|*S|^q@SsZ*lb2@!X!J?~{5JNi74kHrhY`ztZ;NX=8FljU}qtEIKGjR1x z7kK@vB0~3HnPP1lR<~)#VL9vL%aHpP9K&(bLe~@hF#d|OIh1mz(r=_w`7SNvsD64M zI;DOIG$wN@Uj+?zw!+82JC_<_?1zyi*3Tu;LCJSNOvA}!+B}KM@NUGG_k!(5uHaGN z0%Cgr!^gE4EGmOq!Y5M4cE+t;$zOZXR}EvtZ$J&VmW}_J_qb0cA+0?Ji{5OCD{-P{ zcOAPo7g2sk#S+-W0lW7hdTYBG$$@9#K>szz>Cy%zmsgUBou9|D_v4iOB&5KJDXz}= zie+1c>PkUtN~pa~*)wpKDFM)P;}HE+&xQm(Q;>I%D#(yWU_2WWIJ85mPDtobMc z2R0EbY^TI0&ed2|r-NrZ(DOPoW+YC`0lpjXGSJ`lxQir2zr`Mh7IFcX$^rnO^E!a$ z0t(e`44o507El07OQHHGL7xVIMD>X&Ir(<)T?Ag4cbqU`TpgYBA^jbbd5lbW;c_ah zSNuvn^a$=K>W$<||CSdKCoU6gqJ_a86^vMqn)b;DdG+gCz*4pN2u{|d8#bvxPe~1( z34SE?xBU|TqcY*%SwtZ|sB!rOq?6Yc2!<3+z%EaaQl8qVAL5AZq2?KnV&9t)qn=@^ zFr#@3D{95SWNI5Y=d7by z1Q;F1h;%Lhvq=Jcu0)b$_4gT?feI4oH5X+IUtmayak8$GG_)aC9-zt6|DP;c7Rg#* zbfS|E)Eq*`0gr?WrK@A?YF+=pRb}_iXlt{vp9@DZ2|pKhqUItT61yfd^n8t0%@rDC`I0wooAmd)#2pqBZN zJ5UTgHr1JVwfhJZ`i#tr$qU$eSTQ5jF@epg!{;i ztj~S?oKLjr&qTNm0HjS}m{%ioZ{LV*>daDdU&4FmPl0&|cton4e4fIt-smUL$tv*rG%Yy_sZ;*jM7#ejDY%Qn5@yRMs9cD8_)<$ z3EW8Xl5=eg5GMnoiO`KWfzCkaM@W~XV>D`95ecEugwT>0av#&Ei@N6ca+{4@`FdB1 zPO7C^+=vYt+$P9})bYai*X|Go%{$`kG12kXT`qK)ell-tYE{=NT3a9qYSkgDJ&Psl z3YN7SdA;2eBw2wQ;c&h_n9C&3mSzhfDWXTVG4y^q<~==#@%Q&$ZAt6LCkKGa8FqJ!3^TRf)Gq@+G@3M0rf znC;U%&8ZNgVm*Egz~{f&cwEr`zkhhqTtdra^Yz5Em-kkSZK# z#LMh4nFVFP7XRHx8nF~Uif6KC)7^n=`iP@bB9Y)_82gw$9apv7!$7w-)FFVO&#_LcLgtOQ{YK8;hE_(8Fn0p){7HN@~A*mjQG_ zWk4V5rvu%M&5lw$qe;~~05xsg^HK9C{P`Wg+<|A<2=IUsSn7cP?!XQndfWm{W%sth zfA^8$Yy@m!n&sm^CLi&iq(+QER$I`DM7)!9$W!PTr)k#f(W+}&fy zJu^~n+MlWY-k@p@^+j%zn7e=CCR##u(cY2SD-l;)q{7<}>eG-LgypU~Y)nVDj!HI1 zqUVwyBhWMX8i2jPk~11T?~asvEnHUGUR=&8%Z--%GUPTX@=}R~BG1Q={qPSFP`{H3 zpMc!%Kyx0Q_*wW;S@?FanhLP+WwY>o%+LAwpE-~lZ+}a{k?8dh3Y9HZILLUUg`rnt z^|DED_Eon4h*t3)3`F;TVGKv3I2y;bBJWR&n&AVuy*G&gTxkXtBrU2}HEF;3-b&g; ztFZNrA^TIv9@ffeKcrSahTQ3qawm{XZ9b1hl^7G!?lS|J?!1F=FsTURb;LBNjc_&ZzUv zc!sXCduwte$(`N%HvW?Wyh$!;{Q^*#7M1Axqu*K?ymf!n4*zBetPZUO0$b4uu2#(IS5$e(NQt` zwl>N9g33l#g-9Y99!dCX29>oVI)Co50E$M^r8})uc?3IkNTH-_AK$ZdAIZ()u+ zicNPx48ZOV0`}r}QR@X_8tOfoV8#al>k1tj1v8pnlzl}sU|(;uRD_^Jmh78YvTL(6H)F~EN=ow+cyJ%d#{bM~+yP!1mV=J9N)q2I zidxzesOuu9bL(Qmk(2YaCK@iKiSimE`QKXnz-Ul~LC zCn&vv?1QLd6lBTu;y%)hO_~o)O7>yt`5h5bB22wt&Zu-oDGplNU8sI=at~eiv*b!F zqPR-ti6%wIzBckr6Zr~22)^hvMTJ31ZctN2K!owFkk?qKd9`UK5KFEL)faJ&R%v=w zY|BMiKx{H7C&B7#gD?$*^)>q7Re-0%V1}cFR0R`!g=%k_at>Y^F#yH6E3jYy-9;H%iQS6@!B|rH45yn7 z(c*RV{{k#`MLu5bIZdQF0r_loqJILd*l_qqv?D)AO+3V@9WWqvD)aabp=A5g{;e^m z!-qUO3QB5YhFsim>L@6$9qDpBO!mqfY8KKIyu-dUUs>-g*d6DrANu2~r_G3=Uw^U) zXOG=Z^zi4*fd%XIx5a!?&2`ECfv@p&h<;fO?pK8Lipy2y#W}QZy2wO{Ke~JgGRCFU zAStokLQ^4l#Lx-LXn0+~Xbf}kS2^+>bmOZrz-i9+|GiMIK)U!P_07hiOT28TFL8z3Q7`Ek;zw0Yi6+tqP64B zKt#+p`P&}RCx$@cxBoJUdD4l<_uLp`#wm!@M@JB;IT;8jN3u5cPBT52f-L&7rvjwc z2BH-}(rI3AIA|?LzHCcrnMX)G_72Av{a0ft5R4y zHl5AJlxqJ1GO!+;F4&zw?FlW2ZHB{7a*KZ-t)E*w%vBJFTfCRt4YacQc6WshB(mqk zNi(97E-D^)yA-duNX(B#T4Ii!^w>Bk;Olpv!T||bCWrPyoIPTLFCY)4S4EGt8_%_n z^%xad$mXR}ydvhC8!gKqeIi*Op*fjtNeG26PTLYgYu*n_#%RfBL3w_WkCyMuSWjwH zXE^b*Dl*>!G|sKc+zhoBsxmtb3s;%1M@Ux<>6}V^zJ|K0n*1MgUjiOgvFtxtfUr4% zh(=HnF=9Z_C|m{EGLXoOOc2>Gi=a_1Dk|!PEg%V#5XQqODhetpDk|={VF)e>psYzy zRB#U{*yE_+axow>|6f)2S!S}$^WFFUFW+}Eb^3HycUMG4YF7EE8cmms z^^QRfQx>}A^=Oc&j@F>1`tQu$?P|T-#<^#8x09-Hr(h+i@&aTWe`7yX`wcbYI1KaUvS0G>i$q2?!Rv?OC_MoHb#)` z7z*csBvO}cObJ|lrS&dmG9i=aBgaUqv6)V-*{G$u)tI2Bfw>qi560ZgN3QtTRLtW0 zv8n^DYuBjLku+nQj)|i@B6;5kixt_^=F1VW5-;lZYDXE-74~It{rD-2%U~-9Q%b(2 z8s-=@;>5B;MBOq@-Cx}e^Dy09L$n;i$171+Hb^@@m>fiw_r$&z)OWM7J?s_n_M*|4 z6*BN?I|7pJMb|-($i5>#IhCXl=jQMpj6)eP$c5SK@mxqR1OK{BE6T!TuzC}<}DEQ_M&9`FAVjz?5nh-ap!PvF5pY3KdoKI>pO5mQ5wCw8w2z>Q!(-U z`&?ROR`s^L%^0U5kuL`B>}|A&PzNFdhfsUQ)yT4JLKwbPCs-lS0KNyKz}G4a-+XDm z&;Y*6b$pGCv1MDV_5D&BS2Td{C>pu&u!lPE7+%dCM?hU-nyb0VVaOlv%xlI5kUwO> z)zIv)Z5X~#qr`MYf$yEo*2*ueZLD#Qfw2oiZ?I;25;)9WBy|VBvmTS=81b2;%K^k~ zj4s(>`EG?RCDzgn(529Vt0B7ldy};u>i*^i@Ld!IzNf?R&5miT-S$Hg=yh$Vd}G4! ztuT&A)z_)+1-`<=zk|+-m=9PoFng@YWVIF6%C|C30M>`Rw*^;2gsKg3Vsk0sj%gDl047y+J~;D4gwXvXrJiN>!CxDV5Q( zv1V;$!PO9@?hM1XyV07JozgU$To8t@ylrFUyDxZzX3Wc zw(D%kpgv+2h9SSdQDVLm#a{YFVfbEew6Hfvfp7o2*2>3;#$?P{z=uA^DqmtUNHTj- zoDPIDS#wL?_0i>quza;ZWBEQE#g6(JVfZ%2H^vlWqQJNP9V=a4kh?t%$jW9>;F}wU zFF&ra^1X@aSW~jHTNu7a8ZF=Tz*iW`;4w)9SK~A@{uhSeyaECQrVt3JK{FN}&fwY5 ze7#A@^n@Fk_S$LOn#|*QcT4%|Yg_-@R@%K+U)y4Wl(*aJF=BhGrtAEVFuc#!H>s(E z_ck4GBa^SMHd@R3N>V*-QwQ%EQQ$2O!#l3NNmCuX75ct<75?vnT0yLbk+edDf7)#eO`UovJq~wjybO$8CHZf$LkRb zb*gr^jfVvi=)8%YpInE zSvXf+XG&Rzz86G+_u(+SZ%5VTKZQQMFtm>6EU<`&5JECA(@frGD`vO=3}i2& zvJPG?3cP=;x6)zzAJNdER}^?355rp+mDKnG<4;o~+|V$*XPk_tyj~jD4rlNTrGdfg zW<6_23zJ>Ub;9m9tz=pR%Z8?VNE-{zhS+6J7~UJ}n`qT(RxBeWnHrI7sbP3OifT}t z6b0UGZ&>M2b|@M;#7BX5Ch+0}AT8VmsIJ_t@URX2U!4ft1rTccgx1(`)+GET!3!mv zh^bxR4hgS8rwh=nAg~kS5eT-PQcyh2f+7>_>$#aGE`9U?h_pdE=b6oK4nQBi&beQP ziFyDl3nOkr&H99l|1gyb<|$3azfH#foJ#&u6!9H0e#aJwzaWbE?PUDp%@98a@o~h6 z%{gH?2|@ZClVusHW^N<0klg}kWG@~U1?u&$F=+--ReuE{os12UX0ePPqWGJlh(A`w zpF;8Piz0qk8UHXy8N41RI#5r~#XLdvSyG#gZ1(XVU*(#-2}8rC9WT5mBX6UnmNaaT zN2j31{+SiV%CRG8RXslkpPvSHmGfBi+?T3 zt~+a6K7_aUXQ~Z;b=r8>EhRMg-Vj`xyyrExc=7RvD+){B}tu0LQL zFIA2~n%Ilz+V}x0c%_1~q6l~y{|CV04NI5vI3NIu^*o*NEdz)X{+?0Z+Dv=#c+0xX zV1p5g4Muz{4!%O9Ue0R&9BmhNYe=7)Lt6;dYR9m>Sl2cb3N*8^=YUS?aWdG}yN}bM%&U=ZzzVh&k*&Ok zT6r%^dA*foZEn|BVp!SK*UF)njM}U@N0n-@UaCe`LtBx~Ge0*GHG{UyP!DBdLBZ^!hbI zt%rb3asNQQ4ZqZjkZ!=~ye^<^8+*|f>hj}wf5+10SMWMPX9n5j zuh-XX4KTy6FGNx41+7r&_7?Ir)Y;Ymo2v8;+)>b|-|KUrMk+lctP~A2>fY8U#ZpUs z8Y)F+vlOFJ6$!6FDSECTgA>_Fa`f)o09`(R-l&P4aiAt=MNyL{kyHFyH5IctVwwblxttnU zw^)bwUz&qpU84}hr6?Tcc2w2@Ea38LVRR%-2K?DaENaCLRU-#Db}ze(*0~Qv{vS2?c#V3cn7$8Yn>+sjj9H&}CN4AdQgf;L}us z>#0dQV$(U0u9u*ZG_RUznp3HNkHUZL0yE7bY-$F|k!HXs9^cR~=uTv0-ZP1ZGn?K< zn^n`yr0#U-fJ};5ycqwRVtYGsgDG?1LS2S6!g}AXF(@$pOq8r^6t#H;Y3gIwFfN!t zZBQLWX|LfDaJTxD=()z?DYqfc>DfRD!nj~x97;ed;HK#H%~J*+W|0u;f)ez+pNE$B z^?tGhK+gjF^W^ZTV(}Zc8p#6(@u?|po6Y$!x9y^Q%5#UtGf+p}d1!kD>Q6a#c?Z>JHV$Q*4B znWN2w;ZtVB0=AAl+Gq!yW?2o+tS0+LrzMh&eguJV4KPs${tQ>HY({zR z6l+0zGRT5&uy-nOBm!>2a%;qE06`_1B>RUa`-;+tliwj4Wp@e{AI*y;mwsL*);s7c zwrr~HUXYAXzL~1%bse10Yg=J)4}$F!C%X{AE{Cbt63!f!esjP%6|2d$W545`0Zy%u zvHH41_%g&iP_P%*T8VZz9{>Yr!pG!gddj;- z)D2{87RDh7j<|n&;{3NHt14-gjy(ra9j??)aVQ0*oVjl>38*e$1=z|_&8SxCs1+G7 zqAHwmdPh=Q6r~lQ4k{>{%fff$K71ZgyRl-0-M%o1yFmusj4K`QPqwtSYHxBjndn4` zlIt`#I)gj+2efyukGMY=js80Pe4_Bw=-b{i`@?YR583DtM~x04+nAk?aqDs|KJILG z2yS5N5j(|M=qFBGo}d~S$F>9inYQH)gq$nIWy`~x6%^){MO7ji7px*1H=<+d_LNM| zK&!4+o66#k>_xM{&eV@m$KYyh{e+7{JN5^)1*klN?8C=*I%;+ySFUj9{wnPz=e>IX z=UhNVqDKxI+kYjvss>iBx`YWQD4*NeDZFQy1cf|h3A0mFg7H*@YtJPSiHwK)<9~B$ z1=8Xar78X?uRl&Nlnr8bW5!~~kzv$u;xL6l4f$m&;={bv2}ACnZoUe)Tm5!WH&mDU z4MYsfT`1-qA$72k|3jVHjWIXNFg+rCi?RxnTjc5~U~NhFv2?HqqOe)^;fAZ~eHeib>>jDJo zraOw8g1ChTLAMkBVtiTRtv%0PLIMxdx?WW^A^ZBG*xFtxHf`ymkMZM-+Yy3vW7+*( zXR;AiFMB)T1wxy@Jlq~H6V=OC5V67G>ZJg}Gw4t+xoz;$n?1e`vekqNk9D>c%$c28 z2&o$?DLiJg71(Ac{814KrPw^z7`40k5n_Hf41;luYaLXVPHmNR1L@r|6?WU3{e=fY zQ%ar-g}w=Sx)V}czNZ8Irsn5uP+1F1?}GZvOW z`w`u~w|OKG^D%Z(J*z5yz$vL(?|~F?gBjEsYF`Q(i=c{c5uvK$&(mNQ3=``i2*PMs z_!qL6bNfw&f1QKhS!Wggbsm0agI;$li`dU`V&4hBoEgC+bMN&Z<0&vJEyIaZ8W~Ou z!+jhZKwAz>HLO8A>~oj;p_3SCgcwjwT6B!iRiaB55_qgSKOLYtBiDvQsX`(KAk9ID z0qE|N0{jNuaB4!^L(Nc)Q17>K2t{K;(7%Uwx)q4p;lLoTk5o z!7F8D%}foZ+D5h8q_jRLjnt5%{+UDxr&v)*yutIR`Ki=OaG+W(ZUd+$jng=kyVpbp z-LQ{G4|uuz={C0?vnH1|G1aBrl;P6u=;PA9<$L6LoK*D2&Jlz%E9t!UT!26YqmxR^}Kps;1?>i!m zl7pR22V5k>FZ>3(UipNmPyXz|o=XY3IN#HietRo9a|ux1C8L-BjOcFQ+ZqHg1i-)p z04@VCrU#hjB1(5zzNZWQ_RsgUqu+t~dBYLG10luiHS8b&Vz0p$#(}-q9jVS<*}5GV zxiokBChr5~nYW;xs)2$)hygpz{@>Z?^rth($7S09WZ{SY&!9XZJ0{&^EMlrm#Z-<- zG)P(gO#ULFlsd+kZW~;-X@bsWwX#?L04~b{k5>HH!BOxXdS@xkg&p?wC{VTN^p4TF zAwOA~CYV7w|NZ-Iy}x_EO_2ij0D>G^j?QcaV79gxQtTy|dV<-g2@5OWD80mc3{HMm z#$lMOaQfR0+gbxBzlFO7(D+snssW|77 z#LtxQDnH=f5>6yM=@K5j5Ac~19{)06x+058Xq!8Go@Yj3#jl}-C|L`8$w`b(T(Va` z;F2A&lx!fhLjkxHA|{Q6ABDgU33uI&@OLHr!vw&uN%)qR06!z)!3YaHEa6MwIVNy7 zV5ZSrd&y!@$1{gY5n9Zpn4_2Cd|8UbfhK*#6r>&@F`fB1N|7bu-J=2bk?`-k5C=?7 zB~32^oGRfHj{rVZ!Xw@ROs+c-r^th}Cy*)AWZvvsJX4t_C{1&F2`Pt}CXc?)G|8}( z<_9S31DlW%SRPvqEbAqF`*^_5OZd=k#Cc4@$5#QqPr}3RMx4cfnGW;xmIxIy4R8)a z=6asY)osaj^i9A5|8$h=_y>U{Q^IX-0Nh)`tFH!(okWWB-7|opQy_TyFrC@bj|~PKU^u2Klqcc!m}RutA>h>E#W?db-fRcKDZ<3xqvgNH|6& zbld^hCE>iAfn2Ow1bTZB32*PQ7^NU` zVX3q5Ud%v9^_pGuU7nocQt)&HJGAWNa|hW9LIXP3Z1ZMc@0l2039_!H!SN{oL8g;L_Z#|FSfGTiniL~TAI>|F=ob^u?$2;c?)4_pR$ zJr#QUf_a_`ydu_<+LLCep2Pw4leZ924xkqV2LR~n0MHwNZ6JX20ko(9nl1naqUCw^ z@Q*wIG<$d(Ry8c%y>R1X%pT;-E<(KLLA=YUgD&17@{foK?<)~sEe0hUGlt#()ugr? zgFL(F%wSF}%^hy!s7d(IA8QVX_*0L|=fp{k3 z$-?`;@OuZIg?Q+@hTj!<_T%{p&oMmBE=#es!$aTC!f!V`x8ljiQ-bGyJcNtBKMwdH zo?r0%g{NgdM}ZFd^(VYg%!Ld>hn%R zWl*r5Mi|V^cA68<8I~~kTjudK$V<0^2MMLm^z?rskjCVBg zu@}9I{{uSGn(8LcN3w9u0K*ED5 zzpo^`p7Qum!dJ9H-tSBJ>a7Uh02p=6L|vgfMAb_0pK3JMEG!CC^vgC?YyD_ zPZRP2g4IgipF`B9*KlfEbR-6){T;?GL}ygX`9NQZ^1dH|vZsWnQGL&raL48-ZwJ6V z&qNtLvt+eVs92*=OQft{N43FJ@NX8X5HiCk)EbF$0X4$Pzm4*0$Rh(K<`YD^vfV(L z4Hz`HVXpo$(|i-L6B>8k?3+DPi1_n7ZlZfX$xi9k>OLf8|3x%&J|W?!lL5oTn=*I^ zQ-J`S1rfYpCouaFo+6~+fNcTESFv~rbf9uDB*7i#_?yuoRk9i zP6?M#j<-wr1smXLfT?qYRS#KV(DTTwuA$0};Qp}#RsVZz^h%C}yfl`hTwm0p#)l+) zA(ip-zm0yKl}qoJD9rS;ArWH$`plwZ20u>^!hj)q@?Py5D z;I&g+!ys#4#{y(s2J~GF$|3FXPE8GIJRZm ztg~t9(-QNZ*$X_k7anUxW56(23JPug#$h9j0)5?|+&Gnpuxmq4AY6EM*GadlDv91Z; z1YkC%puuZ7>$#p$R2Ybh!Ca_$P_ocFDOsqHrjn&nECb&LucP#slJb1%-I)ML+wuTR zn0dZ5sTcq)hkL=oyK&~C`I2NVbG`)T>W+1yVlKPgV54||iqDRnqGnM$#m@kpP=!&m z1kDNP_gF5$(3Gc9=bs^!kgJJeP(sR$3S*UQ=)2_p1TT`69)?{<6|Q}pv~9<N2QL7lu6j>BY7IFOP@p z#dJbR#Vxu6Ff3M=VAl#xJ1Vp7>pyT|Y6^d}IYUo|0;BPnJza@gK-En9`cJ^5{x

Fy~wqinbW!d|2Oflb~`31U>I0GgtN@`SSA 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Ⓢj9f_cb*#|x)-)$iIw^+;S4BcSKB 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 +5Ak z))&DXN3?ShjJf1R&7mxfBXW}Sr5;HDu5;&ndrTGO)Ou>A9Q)uTGia%9fSgomKmMg2 zIr>UM2O)bc2@N)#BiNW3|BuV=VdRlNyq0FeX37B{;8)l;8H8lQBSH{rqd`d8E)bI(9xXKJajALNBwgF@?q8~xdG7t^Wo z=Ljxj%DX7^WA>*|UH;`)r)PZv*T{~!N5$r=*pnqz|3_@T{;#?XvChBjPxGCBB}sjA zVCj+$qT|j91!g=VLv^|R>`&XkASADm`Jx6>Xtt>$5d$|=)oKVzU@aFo0C}BU_TMxF zdFB+m{%(I{Aee}TK*$ZPjDYl3RY3=P(?p@TJ7lI)FBvk;SgD6jb@x%s=qNE}Se3hv z^k^gZCe;}(mwB21)C_p`Rhu>;%I|ciC9~u^ zE&Ss*5$Qll`>-&LaOP*-0U`iUjn2yCv_ws&lm6pLEkU)v?>Kp$P}v*gf~CTI9aNhS^x1eT$B2O2X>l zI;vbp-SD>Tw&|#kb<}rMzxpg4^}3EKc#9~nj(Su_wUkt?bkt-W6{18ohq)QP3JtwT zFuu7=waizUmD!FpV@YOZPH~>imW|%GEuOf`{-8*DifVx;zLrNutQB?%k4j8jVNX`E zxiwy3SMIv`SpNi-XjNDL1J%GivVdUN8RP~Rh#~Mc7tfNw~?KOB|H#tCVdRPpE+a4I(P8AdDpoUci>-%c+^LVnpWWSA>|db z!hSPPanYl~*8NE8L_eyd53MA+Yb5$U9X*>vq3U3hlllf7J^gc{e`hRoq6h2fnVh{3 z{}72jTSsrjn?$%G65UxxuUJO(oJe#t9lb|jzYvMu<5zgU^%>EkeQ}E1q@&wm)KqbO%GC8{rS;$F~k|AZ9P=QwdCI&Q#X;!bel?sniogzDB#+$bG) z5#_QHVDM*hP_7~BogA1vm>epc$zk{|`!TghV^xd)>o1&+#QK6qEmPa0)T8cGbH6=o zpZcMT^r@MXyq;3R2ExyThWYprCiEg)K)9ALo-mCtgD{J*obU}{7eV^e;pc~~DRKSj zPvo7ezl@q6F6M=9=&^QMC9w~c}F7N_?j)@LI`c{B4W=4e2xe~ z-P*;>9mnt*Gk4rPQS-e&7&CW#c!y%22ZRSBbI0>Kw(?72BSKKIj(t%$Ffw=CsbkMw zM{H#77_MVaSVL??2pXtk-~EEv$lTFG$DXsA*mq!IEgT%JV`mB6$lTFT$6mCS*vQG39WgBLsb;qeg*%nL9qyQFq`Y$jlud>8J{-Z|07dbkqcTyqP!>glH*?2C z9rY0<{wH(C+xH1aD!OR_hjo){-`0M)I%Ozm}IjkcaLHMDz5p zBVu?uo7Whge&Qa@(^*9^JiX;M#SI69%OX6zLdU-RJ+Tp<{!quhEnF7i>F0Iqur0(! zc)D1}cK(*w2v6UsV}II4Y=ozW>)7?&dl1Fb19j|(t;9xnx`&Q!%5o)&r;paLZN7%- z9G*_rQDq=tc=}(rs=7WAZ%gD|A%y2BI9ne!hYRt^7K+ zNNNRdCMIw&`HohBgH7(bQsqywf|n8NSY5JzG*Qg4XL<9}bINmk)}kdWsNJYPv7gy->qn}=aK%tQI9cH)gj@2wFOHS_bIYQ6VB zPzl|d>{ci7XJ6uu@L6o1sHzX8f30@cY}6j$}pp@r0t?QJMdR(pOWOiPkvj$M6!^R=hjL7R`M%eE!Rro7nQA3k+}TVwoakT zk^>7F;rU-}eXt~^F}3R6>_R9T4B1{Ikv4f4xiCu<$Xe4M7?7M4z6`+7Ju&P{tggOE z%Jd8#cb0#&dTQ=h{?$AwIR29_ZLHu4q*u?W{(kDWgH_m&xNr;eVTk`#^UV@i{h@M+P_4TbK;DUb9`PdcGL~_X&zn7F# z+{w_+q?KImUXr2zG7q?-Ro7&;8!eSxxo5mHhzuWz!aW|L!cqTogdkf4p}Zwg2u*?h z@d)j?{(p{8428a19EH#)G<`fmBmegZ#Zc(dXoR}egV5TM|5Xab?Y%AYL}Mcp#==Dr z3hAyBeiX&HbOjkVIPj^STl4%9O)c?^9fg1A6kR+mkNh8_9^JBndgHpNw{BFoiSw`f z-%~Vvy`KJJhJ(icGZY0HqXPX zpm;Y?!oyWo;l+FEvh44}|BqSrr%$3-q5+u3^XT9IGDeZMS3=BR?LTom`nM>As&D&0 zXfJJkpxM^tmsHtt_}Q=#w+MKPZ{C&HP++&m}ne1!AdAQLMr;u15tC5jv+6H_@! z*l>Xy-N7v7304r;KRV0&K8rDxz3NYsq>{v(vpfqJVmxFH$vNjO#Z*Y2j3c48Qgma~ zB26~qXI$wlF|{QVW=oa!x$o*F=6CaiIsuuMIr!mN=>B_0JJlU$8VR3_Iq*>UPNv>J zt=qYaIHe+Io&GdcJ5v3BP#}Ce1&CGC3j3Llg$Ak(Fr(IOK+_+YQIeZ#zq1fKqKOHG zwRvq-V^z1(=XLDI^oPJQm=?0B>{!{*iuS#H!fMr+fUU5fIuGyLH4ZCuf-TS&Di#r+ z)MBtHtl`Ua4D^K4hM~>~U*V#-pQebQs1R}SY^(5WEa+q=aA<-f$`#d?;PGpjipwa; zN{t{WHU63pF$m6_$MJ!Z+y-)X0tX6Zsnv2cJ(9_OJwC@cas-yJ!afB0G*oymznY#o zheh^EjaUq_1O5ovQ{sz$=B>jZd$2UFdN+|nAv*+tPms;wS0nrU+-S@Jtr?RSgc)cx*e68@L_9a0yAoL2_u z%;M4AeT>e!s-osf7puW6F(Mo+2Is?^^9mfZ4sYQ;ocQrI);5(p>@gZd?WfFA-H~<7 z9F%+mm6A_X<(C>0)e8I8{ktP%c<8daWEk#{VH9Qi0!x!J61i8HE%vlb|GDxzgFYjB z=#E;i!tRC#f%8ULV=k9S{>Ni3P^@`j zbhqQW7Z37=H^B9g{q6ev1Hu66_yY#V(x_W}G9Id%N=0dRCscF{L2Y|9FZ(hbH7;IP zk3wkh7076SgX~T$C4K5d6@K=K@|8^Cuhu&HhwEf%R&a~^Nh=<4UV_MqCkf>TV3oaf zpWLLq7>i};SzquL3VLUdDXU~?_fT#!w-_a&L4L%WPh4?RwJT9r>u>1m^IP5pm`93Y zE;JOCT#~l&cT7rB;w&fCv2tTMsqHcym7I=@AarpILj4bm&@}LhVG+P z4>y`fqg}=2NU*k6cIqgWlW*`vzjW|l?KWE@66%*>R$*sU&Y6YZ`Hut778uK_Ru3;k z4I-_eVwlEoSWOIb4toh{xm+kKal%u#^<-*%iElA5WQi8OFB*nguiqRC0}YUZ(N$nv z?c23Ie<9QXQDWt}2B1gtJ z#I@SqxhIW0oa2UB`T| z&wWQGQGI+!wqW3<)%FJ1gcEA?Hq&h%U5Gz$b|>sNPZRgRL&P^jxn29V;42vMvRl1i zt5R-n=h5M~Z+Hx|gW{owAK7ayecXV2WFhdv>**D`TXTR*{JA=x^DO02*q+896D}o@ zI;UYzl-Kv9C#l=*bmHY!aHBh_RQiw7u{ir);>I$^$c1jwy%&)?GOW)3g@H6TS^WxHRI6;9OtRD zH}4WBG>V@zxfEa5J#dgU(wQ0MtW7M($lj8Pi5z%yO*yegaOOoSqHHvc^`@XLoh8PA zQ}u_q751+m6Pv1?>JUNks?zl za)BfcnIhkAa`+<{T0tj2yC_oi|0npO|=+ziNX#gsU0d(140Cb4}g8x0CfvK-I zaIe6ru-jirrx>U%_PVo$vkuocbwsMXLaZ*%?0jET7REd3~N^IiPEN^gNV6vPhk-{qM4xRVZ z67idy2Wu|JtQ9Ld91n}T953826P#lEMv;Qu;kv?JJxmXVBSc3s7R?CfcCWd5(ewsy zc|8{ro+JE1IOlDzCqSqs45wSHBX|-Th-*RZY0&+v{0!o0u(3O&EBh+(H>mwoQdGl& zt;ZTC=7Y7Bl~Q0c!Yeh4yCUE6s?4>v8X+^RB4`62eGMJd*qLDk?{}WlGyLaB+=vXT zus^Bv!1N;7WeF1EKpjWuEA4B4ko5)vu9~2>y(HrFx@y0gq-=kN5^bHK9a(ZD_U5RC1m|a*oAFVV-K zoFZvyhW~0IV^v0!uJ%og%)(!hB7)jUE^2*2P5A<1>{ldy;6?G&f`+5q4n#^BFQq&h z1LJ$zf>esF?W~S_YIQ68+z**bJ&L?o#Tzwt+T?wtWv5ND;=Bsrh0dJIn1G*Ta|eM8 z_{|5K7P5bNQ6AL-;0GRk&V`i@Kde(}|4eNv zsLqLwg{}X&*Z^Ge0hPg(=(!E-F( z?DgF5s&0!rq%PVjIGzq)(sn`)7yCUS_r;ZdrRJsX{HV((^o6SPp{;f~4Z+^wMGM%_ zR#?8}HkpetW8-a|?cvS!F`ZeORXntm5-vdNmsLCwP0H}q;R`WN(tt-s1HLRC@P}KY z0l&suUBFwgI&}f>#`H`BZW;~vcc>nN@l`Q^FRB;t)XFHpPr88L1C~pQm*W8^#sL1; zmijQRk5n1N+RlG#zYH4(oQYvP|enL3^jpQDf*n%GZ5^l|Fnol!t9{-&6j*k#p};NCQ1j!VF4!DTfoC~h z6MhJ&nzv6iIZP9l)XDLN&}?BxljFDN;~M>Mb#m+%-kR>__&h$xNp*6}mbzZ;=6E7L z$3DC?oBpa>a*5QV8#&k{PW7aDJnjL2a(m`rSuyB~Y+y&ZH--C&GRg2G2~7s~L|3^z zo(!R-BrQACfSvKc56T%cAbg=OByaBmY>D5lKkvEZ6)sOYlfimqjjRu3n;Bn;%7c|m zvhC&j;&WwvX&iGIpIpSqaydIKW z3>sC^r?{jqKdNz8USwGRj(A9GzKQ1&i-9-X=$N`trnpeL#X||zfpXF&ppH`wiY)x)Mtdd*_WuVlhI8(y!z zx1PdFa}6yU46D~t=CrRO zh_AEn<1LmS&Rq}1KV!LWnl743o1cuP(i-)qaI3Kw^HPUO9is}{V7H1YES^eBR@6_W zO)P^VRH{vMsWj_xmr66$<8V!-b@o%dhO4BV>r&}f%r|v|w*lW6=DV{pniRr(@2!Ir zugeVWaIrsE=dGk2T=rPOOALii29RNmb#{jsu+rlW$Z_>gWOT2KUwO8{&u_r$&QW8p z+YrI8L<`H#J!d6tZ*T-qxi~`B*TTau^@j}0k>m~b`W7c$gLct1Xkv;oJTiu&A~m=& zs^H?GQiDor;EWk|WXuq~1;T2T6n+AQN183Gf!l1qKk7EyZXUzE>ldq*W*Iu%Ol!Kd zzJ`|=T7SAe8q;@qi{<8)Uqvt#+zf$q?-WcY^b>>_)++341);}S^~ju*o_B4wpH7^P$BkG+TgM~2C_)lo@-vF$M|D?%b#=V*^qw$s;>r2UT3Y%*6 z@W?+08rlqw#$%*`YVdfm4%F3xhhHja7}}hAM}7ioJ%yd;7M2((jQI~zxvlaUFEOps zg_7MVXr0}j*Bmi$!VRKW-Pu_WDpiNje8)dre%Q*R;u)Dn?O6%JGio;Zj1OT8<2@H` zbCw_fR0HH-^MI64=9rSpSNrysrVIbWyj(F47=KgyZ)^#K*FZ|!H=n0vJfW^n&uixk zE%7y5+-xa_|C=iF7JD$joc@gt{aUS}J^ZLuwC26`QOHHw=v`!7jo)35MA837 zUSjA!UeO;TLp599#2ZB>a#kojuC?Lr`b)UTEul?x360}RXctpLvM%8T8Q9t$ri7m| zOdZ~*SzR7_MKwOV#%2tf^@Zx$P90w5jXGGLQo;wVOdXEYREqN6P;O6i;a_4vnr?Qj z26B^lUl`z`Ss##rE|3=SKsqQOGX4&aESwvUWi{K}fV2vKMr&bk3$K+IHKNp4)Hf&y zb(_BUu+xkN-}lx@rQ?d~=BKC^gambGkLfO~%i>{;Qn1=yR6*S@s6Y25Ztm3h+$ZYX zyM%Jh>(AZB&HeR5arFqVRgKs0BXXBJ#&K~={`LI~6`qOD{f5q+A!FBc$!!Mb#nFTF zqb`6x0C3srqIhV-HM9XjhjFZH>W4Pag|-8Cpe{5|JTyx~dqU`P(jP+G?=zKqJ{sEl zyr^z#+PiFxqSH}Ar;TiH*N@v9F0_lIpu^wiX@kd7oLB}#8sE+ zpLTO!8=rfu&V6<{xratgdr|AV;co8Z$nCWBF1uairN(Yz;F|SaDsW|f)1#H$gx83$ zbFf08oWxL7J=Rn`s>Eu13F{93-UCr3z8ra}u^;C}v4$z}Re(m7_&BeT5{q?-*DVyy zz=K*lEepEbN@vQIDbcLyhVMcSvelxl;A| zH4qFoF9RV$m&Y``TCn*ume)&?;jsI}D*NFxquG7PK9}8ZEOy!bY97NctJ%AbQnu&0 zhR`+lM`2pcOAI$0SP_lsPrMz{$?0;?vNyfKQ=%YP z?=<)TpmbL_JsQV>21mmgmual}egIZug|F&`mhM9P0lK(tv!9oki7Qz{TO-gq*UzQ@ zI>#XPaeVI2KUWl5_b$2LfkGO8e|)la%i|YGous^EtjA=NuoQ{=4Ad6v$bvyZbtjR* z)n$)?>V{Tu6D(U$eW13Z$ItmG?#g-}E)4nf)PFS?7y@Y;7MuLT&I ze9p3=y_<8v)F?8o)ep@ZWMQC2`C@o}8qNY-Z{XN}!ep?t_&v$=y z>$!vv;bzn`j`q*?HWeBf!Kuc+%;02(k5O<>Wcaw8H#+6ue^S@$8^RfjYHR!c9yrJX z?J3%qPmi9)0&V0X+2{0bR4cUQ?h380405E4#U|Rr?I^2Nt3@qpHba>R7HfE;sCgpX ze2Z;VJ%jtFXPN2+qXCbL2RtPP@OTaQT1LlkVf}zdxq!PGSY~WEN9B&2avv>V$~_rU z)q`Yv7uIIgd{o%L`gtkWZzk5CZR?q)Zm-8@n@zSlE1!iyPO2OlKNCzxk3ukA zDd!~WHr|!w6*lbL0XF=T9iFY7{}($vb@BQ942{pjN4xllvw%7~JeT|z@qy_Nv%`}y z*#%Iv()JF@QU0z~3V>hEJ*= za1$4Bg&~@3S1;s6A!smOvnYBrSi~C`|0L_R)ex&5PJOqRA;nz=mg#7PI`@6zC-|oN z(YnLU-8(+_K%IMR8M(XFpZjz-_kn__X4t_?Or?HX7+tBKd81OV)m@sn8dz5?D`Fax zg8gGRcRHw;6g?fhu*01WKH@|8H`%4A+kft70juY@?glwSrJbcRGdWA7wq*?eGW&)X z*H3Pr3#f5Cpmw@mgJ%B0RIcZ7y_+UuOFQZmS!z}Opt0QW4n{8cpQ`@_vOS?I~v=yH+r94o7 zi#_PVI3pTHmv|W4ff1R9@8+#~a6O!q?t=RPGPwl!ikBFkee{~ys){CxXy8wFSG@3i4ozpcb(#BzPAPPp4 zW8Hh;Fc6#m)CXSAuY{ag_?#mgB6R=I>lsCOny{L1!bkX}BdjHKneFvlNtj0Xl(37? zX%2RlgdK#{bG@E+^SFne@D<_hkG-CKg#PongZ>lzM1Jb^^jv`d>{5Je6Lu0Z%eeT7jj{I@{ zrN#Z5K6$m*vypK68m}jxFq@FL*6Zm@s33g1&g(hr3$JH7Axt>&OWKTJt@nC{5h~?( z1Nae|Lf0z@UlTNq+qHwr?aV*OEsx3VnzU0y=h-5p5TJgWwsHSlTUm4dA*EjX!V6{_ z`@&sfHCwQ7Yi;cZxH=z^$;G(EnXF@Qe4aTbCp1HRm}eB{F3u>PxRNiWa=mQPW)bEW zGC7>=Rd((TQh!i#cGdT0CnS^^YZ&*)( z#D{2ZYxS5lveNFyBM32Yq0U-`q-XD%FQe$nc_N$GiS1Q8T$TWz##?EIBl>w<~UTy6YRNuLZhf5@bDh|O%e^drPb>b2zFe<73uC9@E zlG=;ME9}|SVjx*%FPSSNSSGKW)?IJ6o2OovJ6|_^ryTt9Dpj?oelJ&WYNX~0rU+t6 zAd|>AjFHD&>~szimt6e2n^Yqz^{0MZOlc5YbYz4w^SY=)8-Kaa3B&p24FgimG;krctRwl!xaS8Yz-}u+7j&5%hXU=F&(Q zf?P^og~WeCB)c1BDl)IMJMkE9OJ3jJ%qW|tRdx-6qRbvE?dNDk^&|q}Ly?zCJIISN zX%=jRqqogg=z=}3>+CP|D<&wsuD6%)%8J0Nw7tbcSJRI=BkMBzuNF_I!o@LK-zrK+ z(yFwl9Vg_Xkd^lBcR4fAN_#Yq>d=fL+!I@6mQp0m<3w~L{`e2C;y;GM`zrTro=F%)=DHyCPm zMEX_si~7~+zxF?PrPj?~L1yi)mJW4>i+s1#0g-}g?6V^;m8$(>O6?O{YPZ-@kI|(z zl2YZ=S$tEsaEtr^%DEMNHS$tvKdFe#K>*X#H6O+_by&Zerv94OxTbC_ii;Eu3`qmT zv`Wc||3<5fAhG7l6C(K4*zF@Pm3H%ZWRG_E(rK0E4qqyMS!MtJk!qCzuY=%Is9y!a zn7zJL_Kzo+Qs2#sBGF5cmrDCFUSeA172pb&Ii)_PUkxu5=~C0BRIij8*8#`5MII9= zh4Y`0mr6S^zR2rl#k9($`qi{b2Cs3g^3a$J+g5VzE$A_djhEg~sp!i0V_M}C{c2j}ZC>MA3;xsj=^gyg;A$A}7Qac}r}OSL-4-P^8SK&R!u?M(%2+3^hkY?GHWI#dr;T=ea1M6K(7lgc0ah!!>>suX7HpyFU08<6p-;_+?X@oRRoU=JZ!);o7{ma=H9?gc~^k z_XaGdcEseG_wnLoNqJr7994z=Am6IVHEpS!CDpgSXW_a&CzQbDgzNPIQ=}yAbN)|m z;lXvpAKk*^+}M*NSN-TTB>}{jS%I1sR?&%cc35m#>PEbuOr8*SL~C)X@9f?Rn!`E? zqj6-)K6IPZbP$?(mh6K#UraSB@fyvImWasR7utx(vY3~&J#XP=VL8uMVc+nzQYc7l zhg%UC*hAp-qRdPwuH&Fz)8zD`T)Ki`-7;glA-TWei!NU7hlnX23ajE}BhN0NcvYaf z$L1d!(Iv1YCqScS7y&pHfb(jUpVM-?YYRuN2X8stCQjFuU8m*x+irnPgK^Zjz(r>l zb%{AjJm_sK*&^f-!a!h;Jr2wqs&oKk_+29F)W4&t{}A8ar3wCvT*`dKm+HQCt5HV0 zTaD|R*R6)fsmAL!%ZMb@zd9b{Ti%Jm7$gO0Mb3vKNZ$YeO&vLlK3I-l4l~!vcNcZS zHQ@}BWN!C*W)iM|k4$K7N1iX`d1lnRr1^^X=mq7U_CM|4ifg!I4_=OerWw|3uE^F` zaMQ-Jwo7QcyHLVl}?X1I`fAK+Xb>SJVG4$=y$q|1UDfRfva2;XXuU4@m*v5Rr6Jp5q z)Y+lWxbOQ{b3Jv1{dltUH=~Dck8J>~CkwXT(FaNO{aw!9us%a-uWG9%Um4}zA0v-? zcSY4%+EQp7hvhQpArx_Hswx@jkr`9DY9b3?3Mo=_^3Y2;8a>3PkCK+$?A?-8@RPJ= zPN;aq5t!(gyf=0-XK#Cb$IZ2+%Q7ElkY!2;=M;~6Ej#p{D0(u3rT+FzjU_F9xjxln zue^LCUVI+dH60W}LvQni9{5HC^iZLkRuA37oeB+nq33qBkpsOo#hEL7p($UGpg40K zB~00>9(RVy%4(W>6VM$so|^e9Nveo1vyTZ>CFT_8))r^(lN3j(6bq(i9^@_DA|Ia- zHAj0Brsh_MDyC-sRQQL|qZY-X-d1mc) zE}Upxu-}s=+vyZm33A1m+vnC!5;fMSr$fsqw>WcyHM?KyecIgE?lIc|*2yAQ*+Y+VQA4g5jg*j__i-ZD#Kmt`|T9vqJ^L zc+V;<_4gS)dbHIs2*HGiS+wxQDLH)>-qAFtk1y4lQphBj)#u_=tLR>N3Q3;44Y{Eb zX))ijf-%68ItyyK3N=9ivuK*G_!cWGl)tM$7u43rAb57N6}$raW`)vI1k%s>6USjz zF+~-w+`fczAJ6K#H0u=mc2cdW>9c%d@9f$o=-6t@1YmLQ<2ik*Cw9szd7y?9OxayG zWhdfpYZ0f?z74kWBn*E%C$U-*hK5fXP(1(b8gxmaWyi}MCH;znZ_juPKXRGd)#Hp7 zo_B^6>U0)+(aAVw&1Q|CO<@;?f<2PkO20{JkW-TT zd~xQooRZ8*6pbUM*&m~b@Yp>Eh=27_&xe2Jjr-bScYj+NoAnB2UYXPoP>^ zCHKcvi<9B1S{Bu^W|vmC$|~F1@PbgVr{K`fnw^v)rGtc&F7=;sD!UWoC^W?i$WrhB zx8TRN{>6fyJ&U_LD)T2!^7vaazPy$_a&eZpfNH)lG;!m;Wzx+4WPbAtJpP86IGSyV z@LG~P+xAZ+~i>xf&;<�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

B%*{5MTV0Y^Ic5x&wt!$iB+DPW|hcAPPGp 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

    F-^zIg9I~7c!j^y`VV3$9lc8KXmje78GfFl%K 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

    (nZ6ROxl| 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

    QKS2D}=ktxuFg4!K1FRE@p!k=cwK?GF=f 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 z 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

    ~m!LQe==4`j1;IgTJ=izgW-g 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 z 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�OCYXMCA*``?lr~3d-1wc+_SO|?wcX$Z#e|UhB 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

    2R~YtK_*#>Tn9< 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

    R0R&?V5N(B;ro&^6HY(9573p*KJWp`*|S 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 n+ILZda(Whi4#&3jCN$OQD^!`^X1gCVek diff --git a/Ryujinx.Tests.Unicorn/unicorn_const_generator.py b/Ryujinx.Tests.Unicorn/unicorn_const_generator.py new file mode 100644 index 000000000..813485fd2 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/unicorn_const_generator.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +# Unicorn Engine +# By Dang Hoang Vu, 2013 +# Modified for Ryujinx from: https://github.com/unicorn-engine/unicorn/blob/6c1cbef6ac505d355033aef1176b684d02e1eb3a/bindings/const_generator.py +from __future__ import print_function +import sys, re, os + +include = [ 'arm.h', 'arm64.h', 'unicorn.h' ] +split_common = [ 'ARCH', 'MODE', 'ERR', 'MEM', 'TCG', 'HOOK', 'PROT' ] + +template = { + 'dotnet': { + 'header': "// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT\n\n// ReSharper disable InconsistentNaming\nnamespace Ryujinx.Tests.Unicorn.Native.Const\n{\n public enum %s\n {\n", + 'footer': " }\n}\n", + 'line_format': ' %s = %s,\n', + 'out_file': os.path.join(os.path.dirname(__file__), 'Native', 'Const', '%s.cs'), + # prefixes for constant filenames of all archs - case sensitive + 'arm.h': 'Arm', + 'arm64.h': 'Arm64', + 'unicorn.h': 'Common', + # prefixes for filenames of split_common values - case sensitive + 'ARCH': 'Arch', + 'MODE': 'Mode', + 'ERR': 'Error', + 'MEM': 'Memory', + 'TCG': 'TCG', + 'HOOK': 'Hook', + 'PROT': 'Permission', + 'comment_open': ' //', + 'comment_close': '', + } +} + +# markup for comments to be added to autogen files +MARKUP = '//>' + +def gen(unicorn_repo_path): + global include + include_dir = os.path.join(unicorn_repo_path, 'include', 'unicorn') + templ = template["dotnet"] + for target in include: + prefix = templ[target] + outfile = open(templ['out_file'] %(prefix), 'wb') # open as binary prevents windows newlines + outfile.write((templ['header'] % (prefix)).encode("utf-8")) + if target == 'unicorn.h': + prefix = '' + for cat in split_common: + with open(templ['out_file'] %(templ[cat]), 'wb') as file: + file.write((templ['header'] %(templ[cat])).encode("utf-8")) + with open(os.path.join(include_dir, target)) as f: + lines = f.readlines() + + previous = {} + count = 0 + skip = 0 + in_comment = False + + for lno, line in enumerate(lines): + if "/*" in line: + in_comment = True + if "*/" in line: + in_comment = False + if in_comment: + continue + if skip > 0: + # Due to clang-format, values may come up in the next line + skip -= 1 + continue + line = line.strip() + + if line.startswith(MARKUP): # markup for comments + outfile.write(("\n%s%s%s\n" %(templ['comment_open'], \ + line.replace(MARKUP, ''), templ['comment_close'])).encode("utf-8")) + continue + + if line == '' or line.startswith('//'): + continue + + tmp = line.strip().split(',') + if len(tmp) >= 2 and tmp[0] != "#define" and not tmp[0].startswith("UC_"): + continue + for t in tmp: + t = t.strip() + if not t or t.startswith('//'): continue + f = re.split('\s+', t) + + # parse #define UC_TARGET (num) + define = False + if f[0] == '#define' and len(f) >= 3: + define = True + f.pop(0) + f.insert(1, '=') + if f[0].startswith("UC_" + prefix.upper()) or f[0].startswith("UC_CPU"): + if len(f) > 1 and f[1] not in ('//', '='): + print("WARNING: Unable to convert %s" % f) + print(" Line =", line) + continue + elif len(f) > 1 and f[1] == '=': + # Like: + # UC_A = + # (1 << 2) + # #define UC_B \ + # (UC_A | UC_C) + # Let's search the next line + if len(f) == 2: + if lno == len(lines) - 1: + print("WARNING: Unable to convert %s" % f) + print(" Line =", line) + continue + skip += 1 + next_line = lines[lno + 1] + next_line_tmp = next_line.strip().split(",") + rhs = next_line_tmp[0] + elif f[-1] == "\\": + idx = 0 + rhs = "" + while True: + idx += 1 + if lno + idx == len(lines): + print("WARNING: Unable to convert %s" % f) + print(" Line =", line) + continue + skip += 1 + next_line = lines[lno + idx] + next_line_f = re.split('\s+', next_line.strip()) + if next_line_f[-1] == "\\": + rhs += "".join(next_line_f[:-1]) + else: + rhs += next_line.strip() + break + else: + rhs = ''.join(f[2:]) + else: + rhs = str(count) + + + lhs = f[0].strip() + #print(f'lhs: {lhs} rhs: {rhs} f:{f}') + # evaluate bitshifts in constants e.g. "UC_X86 = 1 << 1" + match = re.match(r'(?P\s*\d+\s*<<\s*\d+\s*)', rhs) + if match: + rhs = str(eval(match.group(1))) + else: + # evaluate references to other constants e.g. "UC_ARM_REG_X = UC_ARM_REG_SP" + match = re.match(r'^([^\d]\w+)$', rhs) + if match: + rhs = previous[match.group(1)] + + if not rhs.isdigit(): + for k, v in previous.items(): + rhs = re.sub(r'\b%s\b' % k, v, rhs) + rhs = str(eval(rhs)) + + lhs_strip = re.sub(r'^UC_', '', lhs) + count = int(rhs) + 1 + + if target == "unicorn.h": + matched_cat = False + for cat in split_common: + if lhs_strip.startswith(f"{cat}_"): + with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file: + cat_lhs_strip = lhs_strip + if not lhs_strip.lstrip(f"{cat}_").isnumeric(): + cat_lhs_strip = lhs_strip.replace(f"{cat}_", "", 1) + cat_file.write( + (templ['line_format'] % (cat_lhs_strip, rhs)).encode("utf-8")) + matched_cat = True + break + if matched_cat: + previous[lhs] = str(rhs) + continue + + if (count == 1): + outfile.write(("\n").encode("utf-8")) + + if lhs_strip.startswith(f"{prefix.upper()}_") and not lhs_strip.replace(f"{prefix.upper()}_", "", 1).isnumeric(): + lhs_strip = lhs_strip.replace(f"{prefix.upper()}_", "", 1) + + outfile.write((templ['line_format'] % (lhs_strip, rhs)).encode("utf-8")) + previous[lhs] = str(rhs) + + outfile.write((templ['footer']).encode("utf-8")) + outfile.close() + + if target == "unicorn.h": + for cat in split_common: + with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file: + cat_file.write(templ['footer'].encode('utf-8')) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage:", sys.argv[0], " ") + sys.exit(1) + unicorn_repo_path = sys.argv[1] + if os.path.isdir(unicorn_repo_path): + print("Generating constants for dotnet") + gen(unicorn_repo_path) + else: + print("Couldn't find unicorn repo at:", unicorn_repo_path) diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index f983a03fb..cafed37da 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -38,14 +38,11 @@ namespace Ryujinx.Tests.Cpu private bool _usingMemory; - static CpuTest() + [OneTimeSetUp] + public void OneTimeSetup() { _unicornAvailable = UnicornAArch64.IsAvailable(); - - if (!_unicornAvailable) - { - Console.WriteLine("WARNING: Could not find Unicorn."); - } + Assume.That(_unicornAvailable, "Unicorn is not available"); } [SetUp] @@ -610,4 +607,4 @@ namespace Ryujinx.Tests.Cpu return rnd & 0x800FFFFFFFFFFFFFul; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs index 2c36396f9..53fea943d 100644 --- a/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -33,14 +33,11 @@ namespace Ryujinx.Tests.Cpu private bool _usingMemory; - static CpuTest32() + [OneTimeSetUp] + public void OneTimeSetup() { _unicornAvailable = UnicornAArch32.IsAvailable(); - - if (!_unicornAvailable) - { - Console.WriteLine("WARNING: Could not find Unicorn."); - } + Assume.That(_unicornAvailable, "Unicorn is not available"); } [SetUp] diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 6ab2fa6b1..b56929dc9 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -34,7 +34,17 @@ - + + + + + + + + + + + From f088c3d3449f2fafce0c38ee9a0a2b9e4b83179e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 21 Nov 2022 14:16:00 -0300 Subject: [PATCH 094/737] Do not update shader state for DrawTextures (#3876) --- Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index a7acb4691..ed8ed2064 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -372,7 +372,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed float srcX1 = ((float)_state.State.DrawTextureDuDx / (1UL << 32)) * dstWidth + srcX0; float srcY1 = ((float)_state.State.DrawTextureDvDy / (1UL << 32)) * dstHeight + srcY0; - engine.UpdateState(); + engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex)); + + _channel.TextureManager.UpdateRenderTargets(); int textureId = _state.State.DrawTextureTextureId; int samplerId = _state.State.DrawTextureSamplerId; From 0fd47ff49077e56f6fd4b9b429c00179fd0e06bd Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 23 Nov 2022 10:28:46 +0000 Subject: [PATCH 095/737] remove redundant logs (#3877) --- Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs index c936fc547..8d0124437 100644 --- a/Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs @@ -460,8 +460,6 @@ namespace Ryujinx.Ava.Ui.ViewModels { using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id); - Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}"); - if (gamepad != null) { Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}")); @@ -472,8 +470,6 @@ namespace Ryujinx.Ava.Ui.ViewModels { using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id); - Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}"); - if (gamepad != null) { if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id))) From 748d87adccd0f331bd6d1163703040b6d62353b8 Mon Sep 17 00:00:00 2001 From: WilliamWsyHK Date: Wed, 23 Nov 2022 23:25:49 +0800 Subject: [PATCH 096/737] Stub IFriendService: 1 (Cancel) (#3841) * Add friend/Cancel. Closes #3824 * Update according to review comments. * Add comment base on request * Update Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs Co-authored-by: Ac_K --- .../Services/Friend/ServiceCreator/IFriendService.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 77499fa53..ec947414f 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -43,6 +43,16 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } + [CommandHipc(1)] + // nn::friends::Cancel() + public ResultCode Cancel(ServiceCtx context) + { + // TODO: Original service sets an internal field to 1 here. Determine usage. + Logger.Stub?.PrintStub(LogClass.ServiceFriend); + + return ResultCode.Success; + } + [CommandHipc(10100)] // nn::friends::GetFriendListIds(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid) // -> int outCount, array From 36f00985d3191ebd6ea909ddc4ed447ebdbf51b5 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 23 Nov 2022 10:32:35 -0700 Subject: [PATCH 097/737] Update to LibHac 0.17.0 (#3878) * Update to LibHac 0.17.0 * Don't clear SD card saves when starting the emulator This was an old workaround for errors that happened when a user's SD card encryption seed changed. SD card saves have been unencrypted for over a year, so we should be fine to remove the workaround. --- Ryujinx.HLE/FileSystem/VirtualFileSystem.cs | 9 +++++--- Ryujinx.HLE/HOS/ApplicationLoader.cs | 2 +- Ryujinx.HLE/HOS/LibHacHorizonManager.cs | 23 ------------------- .../HOS/Services/Fs/IDeviceOperator.cs | 5 ++-- .../HOS/Services/Fs/IFileSystemProxy.cs | 16 +++++++++++-- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- 6 files changed, 25 insertions(+), 32 deletions(-) diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 001a1f5f5..c958c6e8a 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -172,9 +172,11 @@ namespace Ryujinx.HLE.FileSystem fsServerClient = horizon.CreatePrivilegedHorizonClient(); var fsServer = new FileSystemServer(fsServerClient); - DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet, fsServer); + RandomDataGenerator randomGenerator = buffer => Random.Shared.NextBytes(buffer); - // Use our own encrypted fs creator that always uses all-zero keys + DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet, fsServer, randomGenerator); + + // Use our own encrypted fs creator that doesn't actually do any encryption fsServerObjects.FsCreators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(); GameCard = fsServerObjects.GameCard; @@ -186,7 +188,8 @@ namespace Ryujinx.HLE.FileSystem { DeviceOperator = fsServerObjects.DeviceOperator, ExternalKeySet = KeySet.ExternalKeySet, - FsCreators = fsServerObjects.FsCreators + FsCreators = fsServerObjects.FsCreators, + RandomGenerator = randomGenerator }; FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig); diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 41d487eb1..199da37a3 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -850,7 +850,7 @@ namespace Ryujinx.HLE.HOS for (int i = 0; i < programCount; i++) { mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i); - mapInfo[i].MainProgramId = new ProgramId(applicationId); + mapInfo[i].MainProgramId = new ApplicationId(applicationId); mapInfo[i].ProgramIndex = (byte)i; } diff --git a/Ryujinx.HLE/HOS/LibHacHorizonManager.cs b/Ryujinx.HLE/HOS/LibHacHorizonManager.cs index 35e5c6e91..8fde5dd6a 100644 --- a/Ryujinx.HLE/HOS/LibHacHorizonManager.cs +++ b/Ryujinx.HLE/HOS/LibHacHorizonManager.cs @@ -60,8 +60,6 @@ namespace Ryujinx.HLE.HOS virtualFileSystem.InitializeFsServer(Server, out var fsClient); FsClient = fsClient; - - CleanSdCardDirectory(); } public void InitializeSystemClients() @@ -78,27 +76,6 @@ namespace Ryujinx.HLE.HOS ApplicationClient = Server.CreateHorizonClient(new ProgramLocation(programId, StorageId.BuiltInUser), npdm.FsAccessControlData, npdm.FsAccessControlDescriptor); } - // This function was added to avoid errors that come from a user's keys or SD encryption seed changing. - // Catching these errors and recreating the file ended up not working because of the different ways - // applications respond to a file suddenly containing all zeros or having a length of zero. - // Clearing the SD card save directory was determined to be the best option for the moment since - // the saves on the SD card are meant as caches that can be deleted at any time. - private void CleanSdCardDirectory() - { - Result rc = RyujinxClient.Fs.MountSdCard("sdcard".ToU8Span()); - if (rc.IsFailure()) return; - - try - { - RyujinxClient.Fs.CleanDirectoryRecursively("sdcard:/Nintendo/save".ToU8Span()).IgnoreResult(); - RyujinxClient.Fs.DeleteDirectoryRecursively("sdcard:/save".ToU8Span()).IgnoreResult(); - } - finally - { - RyujinxClient.Fs.Unmount("sdcard".ToU8Span()); - } - } - private static AccessControlBits.Bits AccountFsPermissions => AccessControlBits.Bits.SystemSaveData | AccessControlBits.Bits.GameCard | AccessControlBits.Bits.SaveDataMeta | diff --git a/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs b/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs index 07ade0c65..f069fa3e0 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs @@ -1,6 +1,7 @@ using LibHac; using LibHac.Common; -using LibHac.Fs; + +using GameCardHandle = System.UInt32; namespace Ryujinx.HLE.HOS.Services.Fs { @@ -41,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs { Result result = _baseOperator.Get.GetGameCardHandle(out GameCardHandle handle); - context.ResponseData.Write(handle.Value); + context.ResponseData.Write(handle); return (ResultCode)result.Value; } diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 2ec45aa53..970aab958 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -19,6 +19,7 @@ using static Ryujinx.HLE.Utilities.StringUtils; using IFileSystem = LibHac.FsSrv.Sf.IFileSystem; using IStorage = LibHac.FsSrv.Sf.IStorage; using RightsId = LibHac.Fs.RightsId; +using GameCardHandle = System.UInt32; namespace Ryujinx.HLE.HOS.Services.Fs { @@ -239,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // OpenGameCardStorage(u32 handle, u32 partitionId) -> object public ResultCode OpenGameCardStorage(ServiceCtx context) { - GameCardHandle handle = new GameCardHandle(context.RequestData.ReadInt32()); + GameCardHandle handle = context.RequestData.ReadUInt32(); GameCardPartitionRaw partitionId = (GameCardPartitionRaw)context.RequestData.ReadInt32(); using var storage = new SharedRef(); @@ -255,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // OpenGameCardFileSystem(u32 handle, u32 partitionId) -> object public ResultCode OpenGameCardFileSystem(ServiceCtx context) { - GameCardHandle handle = new GameCardHandle(context.RequestData.ReadInt32()); + GameCardHandle handle = context.RequestData.ReadUInt32(); GameCardPartition partitionId = (GameCardPartition)context.RequestData.ReadInt32(); using var fileSystem = new SharedRef(); @@ -315,6 +316,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, in metaCreateInfo, in hashSalt).Value; } + [CommandHipc(37)] // 14.0.0+ + // CreateSaveDataFileSystemWithCreationInfo2(buffer creationInfo) -> () + public ResultCode CreateSaveDataFileSystemWithCreationInfo2(ServiceCtx context) + { + byte[] creationInfoBuffer = new byte[context.Request.SendBuff[0].Size]; + context.Memory.Read(context.Request.SendBuff[0].Position, creationInfoBuffer); + ref readonly SaveDataCreationInfo2 creationInfo = ref SpanHelpers.AsReadOnlyStruct(creationInfoBuffer); + + return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemWithCreationInfo2(in creationInfo).Value; + } + [CommandHipc(51)] // OpenSaveDataFileSystem(u8 spaceId, nn::fs::SaveDataAttribute attribute) -> object saveDataFs public ResultCode OpenSaveDataFileSystem(ServiceCtx context) diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index bdcbaca86..1ec92a449 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -22,7 +22,7 @@ - + From 567c64e149f1ec3487dea34abdffc7bfa2f55400 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Wed, 23 Nov 2022 18:55:26 +0100 Subject: [PATCH 098/737] ava: Fix JsonSerializer warnings (#3884) Since we move to .NET7, JsonSerializer now needs to have explicit options as arguments, which leads to some warnings in Avalonia project. This is fixed by using our `JsonHelper` class. --- Ryujinx.Ava/Common/Locale/LocaleManager.cs | 3 ++- Ryujinx.Ava/Ui/ViewModels/AmiiboWindowViewModel.cs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index 427569909..41b981368 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Ava.Ui.ViewModels; using Ryujinx.Common; +using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration; using System.Collections.Concurrent; using System.Collections.Generic; @@ -93,7 +94,7 @@ namespace Ryujinx.Ava.Common.Locale return; } - var strings = JsonSerializer.Deserialize>(languageJson); + var strings = JsonHelper.Deserialize>(languageJson); foreach (var item in strings) { diff --git a/Ryujinx.Ava/Ui/ViewModels/AmiiboWindowViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/AmiiboWindowViewModel.cs index fc809e52b..d007e0e74 100644 --- a/Ryujinx.Ava/Ui/ViewModels/AmiiboWindowViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/AmiiboWindowViewModel.cs @@ -8,6 +8,7 @@ using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -189,7 +190,7 @@ namespace Ryujinx.Ava.Ui.ViewModels { amiiboJsonString = File.ReadAllText(_amiiboJsonPath); - if (await NeedsUpdate(JsonSerializer.Deserialize(amiiboJsonString).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -206,7 +207,7 @@ namespace Ryujinx.Ava.Ui.ViewModels } } - _amiiboList = JsonSerializer.Deserialize(amiiboJsonString).Amiibo; + _amiiboList = JsonHelper.Deserialize(amiiboJsonString).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); ParseAmiiboData(); From cc51a03af96d2ed24d06ff0e44c38af8e5565194 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 01:26:53 +0000 Subject: [PATCH 099/737] nuget: bump Avalonia from 0.10.15 to 0.10.18 (#3817) Bumps [Avalonia](https://github.com/AvaloniaUI/Avalonia) from 0.10.15 to 0.10.18. - [Release notes](https://github.com/AvaloniaUI/Avalonia/releases) - [Commits](https://github.com/AvaloniaUI/Avalonia/compare/0.10.15...0.10.18) --- updated-dependencies: - dependency-name: Avalonia dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index d36fa6298..656d3b077 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -18,7 +18,7 @@ - + From 5a39d3c4a13d9de67aa324a487af3756e4ce4930 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 24 Nov 2022 01:41:16 +0000 Subject: [PATCH 100/737] GPU: Relax locking on Buffer Cache (#3883) I did this on ncbuffer2 when we were using it for LDN 3, but I noticed that it can apply to the current buffer manager too, and it's an easy performance win. The only buffer access that can come from another thread is the overlap search for buffers that have been unmapped. Everything else, including modifications, come from the main GPU thread. That means we only need to lock the range list when it's being modified, as that's the only time where we'll cause a race with the unmapped handler. This has a significant performance improvements in situations where FIFO is high, like the other two PRs. Joined together they give a nice boost (73.6 master -> 79 -> 83 fps in SMO). --- Ryujinx.Graphics.Gpu/Memory/BufferCache.cs | 28 +++++++--------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 85ed49d59..a523c76fb 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -22,6 +22,10 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly GpuContext _context; private readonly PhysicalMemory _physicalMemory; + /// + /// Only modified from the GPU thread. Must lock for add/remove. + /// Must lock for any access from other threads. + /// private readonly RangeList _buffers; private Buffer[] _bufferOverlaps; @@ -200,12 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the buffer private void CreateBufferAligned(ulong address, ulong size) { - int overlapsCount; - - lock (_buffers) - { - overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps); - } + int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps); if (overlapsCount != 0) { @@ -410,10 +409,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (size != 0) { - lock (_buffers) - { - buffer = _buffers.FindFirstOverlap(address, size); - } + buffer = _buffers.FindFirstOverlap(address, size); buffer.SynchronizeMemory(address, size); @@ -424,10 +420,7 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - lock (_buffers) - { - buffer = _buffers.FindFirstOverlap(address, 1); - } + buffer = _buffers.FindFirstOverlap(address, 1); } return buffer; @@ -442,12 +435,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (size != 0) { - Buffer buffer; - - lock (_buffers) - { - buffer = _buffers.FindFirstOverlap(address, size); - } + Buffer buffer = _buffers.FindFirstOverlap(address, size); buffer.SynchronizeMemory(address, size); } From f3cc2e5703e5df5c359ce1789a4fb0d73fb9a637 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 24 Nov 2022 01:56:55 +0000 Subject: [PATCH 101/737] GPU: Access non-prefetch command buffers directly (#3882) * GPU: Access non-prefetch command buffers directly Saves allocating new arrays for them constantly - they can be quite small so it can be very wasteful. About 0.4% of GPU thread in SMO, but was a bit higher in S/V when I checked. Assumes that non-prefetch command buffers won't be randomly clobbered before they finish executing, though that's probably a safe bet. * Small change while I'm here * Address feedback --- .../Engine/GPFifo/GPFifoDevice.cs | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs index b3de738d6..cd29a9da0 100644 --- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs @@ -51,16 +51,35 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo /// public uint EntryCount; + ///

    + /// Get the entries for the command buffer from memory. + /// + /// The memory manager used to fetch the data + /// If true, flushes potential GPU written data before reading the command buffer + /// The fetched data + private ReadOnlySpan GetWords(MemoryManager memoryManager, bool flush) + { + return MemoryMarshal.Cast(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, flush)); + } + + /// + /// Prefetch the command buffer. + /// + /// The memory manager used to fetch the data + public void Prefetch(MemoryManager memoryManager) + { + Words = GetWords(memoryManager, true).ToArray(); + } + /// /// Fetch the command buffer. /// + /// The memory manager used to fetch the data /// If true, flushes potential GPU written data before reading the command buffer - public void Fetch(MemoryManager memoryManager, bool flush = true) + /// The command buffer words + public ReadOnlySpan Fetch(MemoryManager memoryManager, bool flush) { - if (Words == null) - { - Words = MemoryMarshal.Cast(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, flush)).ToArray(); - } + return Words ?? GetWords(memoryManager, flush); } } @@ -158,7 +177,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch) { - commandBuffer.Fetch(processor.MemoryManager); + commandBuffer.Prefetch(processor.MemoryManager); } if (commandBuffer.Type == CommandBufferType.NoPrefetch) @@ -199,7 +218,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo } _currentCommandBuffer = entry; - _currentCommandBuffer.Fetch(entry.Processor.MemoryManager, flushCommandBuffer); + ReadOnlySpan words = entry.Fetch(entry.Processor.MemoryManager, flushCommandBuffer); // If we are changing the current channel, // we need to force all the host state to be updated. @@ -209,7 +228,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo entry.Processor.ForceAllDirty(); } - entry.Processor.Process(entry.EntryAddress, _currentCommandBuffer.Words); + entry.Processor.Process(entry.EntryAddress, words); } _interrupt = false; From ece36b274da3957d727387d2f7c96adbd0f29bc3 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 24 Nov 2022 07:50:59 +0000 Subject: [PATCH 102/737] GAL: Send all buffer assignments at once rather than individually (#3881) * GAL: Send all buffer assignments at once rather than individually The `(int first, BufferRange[] ranges)` method call has very significant performance implications when the bindings are spread out, which they generally always are in Vulkan. This change makes it so that these methods are only called a maximum of one time per draw. Significantly improves GPU thread performance in Pokemon Scarlet/Violet. * Address Feedback Removed SetUniformBuffers(int first, ReadOnlySpan buffers) --- Ryujinx.Graphics.GAL/BufferAssignment.cs | 14 ++++++ Ryujinx.Graphics.GAL/IPipeline.cs | 4 +- .../Multithreading/BufferMap.cs | 24 ++++++++++ .../Commands/SetStorageBuffersCommand.cs | 10 ++-- .../Commands/SetUniformBuffersCommand.cs | 10 ++-- .../Multithreading/ThreadedPipeline.cs | 8 ++-- Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 46 +++++-------------- Ryujinx.Graphics.OpenGL/Pipeline.cs | 17 +++---- .../DescriptorSetUpdater.cs | 14 +++--- Ryujinx.Graphics.Vulkan/HelperShader.cs | 14 +++--- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 8 ++-- 11 files changed, 91 insertions(+), 78 deletions(-) create mode 100644 Ryujinx.Graphics.GAL/BufferAssignment.cs diff --git a/Ryujinx.Graphics.GAL/BufferAssignment.cs b/Ryujinx.Graphics.GAL/BufferAssignment.cs new file mode 100644 index 000000000..9f0f56c5f --- /dev/null +++ b/Ryujinx.Graphics.GAL/BufferAssignment.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL +{ + public struct BufferAssignment + { + public readonly int Binding; + public readonly BufferRange Range; + + public BufferAssignment(int binding, BufferRange range) + { + Binding = binding; + Range = range; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 38bf54f7b..26d019eb4 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -86,12 +86,12 @@ namespace Ryujinx.Graphics.GAL void SetStencilTest(StencilTestDescriptor stencilTest); - void SetStorageBuffers(int first, ReadOnlySpan buffers); + void SetStorageBuffers(ReadOnlySpan buffers); void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler); void SetTransformFeedbackBuffers(ReadOnlySpan buffers); - void SetUniformBuffers(int first, ReadOnlySpan buffers); + void SetUniformBuffers(ReadOnlySpan buffers); void SetUserClipDistance(int index, bool enableClip); diff --git a/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs b/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs index fcf09f9f4..24b0af2d7 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs @@ -142,6 +142,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading return ranges; } + internal Span MapBufferRanges(Span ranges) + { + // Rewrite the buffer ranges to point to the mapped handles. + + lock (_bufferMap) + { + for (int i = 0; i < ranges.Length; i++) + { + ref BufferAssignment assignment = ref ranges[i]; + BufferRange range = assignment.Range; + BufferHandle result; + + if (!_bufferMap.TryGetValue(range.Handle, out result)) + { + result = BufferHandle.Null; + } + + assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size)); + } + } + + return ranges; + } + internal Span MapBufferRanges(Span ranges) { // Rewrite the buffer ranges to point to the mapped handles. diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs index c29633739..610603caa 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs @@ -6,19 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands struct SetStorageBuffersCommand : IGALCommand { public CommandType CommandType => CommandType.SetStorageBuffers; - private int _first; - private SpanRef _buffers; + private SpanRef _buffers; - public void Set(int first, SpanRef buffers) + public void Set(SpanRef buffers) { - _first = first; _buffers = buffers; } public static void Run(ref SetStorageBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) { - Span buffers = command._buffers.Get(threaded); - renderer.Pipeline.SetStorageBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers)); + Span buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetStorageBuffers(threaded.Buffers.MapBufferRanges(buffers)); command._buffers.Dispose(threaded); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs index 750d8daca..e4abb403a 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs @@ -6,19 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands struct SetUniformBuffersCommand : IGALCommand { public CommandType CommandType => CommandType.SetUniformBuffers; - private int _first; - private SpanRef _buffers; + private SpanRef _buffers; - public void Set(int first, SpanRef buffers) + public void Set(SpanRef buffers) { - _first = first; _buffers = buffers; } public static void Run(ref SetUniformBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) { - Span buffers = command._buffers.Get(threaded); - renderer.Pipeline.SetUniformBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers)); + Span buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetUniformBuffers(threaded.Buffers.MapBufferRanges(buffers)); command._buffers.Dispose(threaded); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 52d699335..ba120867c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -275,9 +275,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetStorageBuffers(int first, ReadOnlySpan buffers) + public void SetStorageBuffers(ReadOnlySpan buffers) { - _renderer.New().Set(first, _renderer.CopySpan(buffers)); + _renderer.New().Set(_renderer.CopySpan(buffers)); _renderer.QueueCommand(); } @@ -293,9 +293,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetUniformBuffers(int first, ReadOnlySpan buffers) + public void SetUniformBuffers(ReadOnlySpan buffers) { - _renderer.New().Set(first, _renderer.CopySpan(buffers)); + _renderer.New().Set(_renderer.CopySpan(buffers)); _renderer.QueueCommand(); } diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 1b67f6507..f0831e158 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly VertexBuffer[] _vertexBuffers; private readonly BufferBounds[] _transformFeedbackBuffers; private readonly List _bufferTextures; - private readonly BufferRange[] _ranges; + private readonly BufferAssignment[] _ranges; /// /// Holds shader stage buffer state and binding information. @@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _bufferTextures = new List(); - _ranges = new BufferRange[Constants.TotalGpUniformBuffers * Constants.ShaderStages]; + _ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages]; } @@ -618,10 +618,9 @@ namespace Ryujinx.Graphics.Gpu.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage) { - int rangesFirst = 0; int rangesCount = 0; - Span ranges = _ranges; + Span ranges = _ranges; for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) { @@ -640,25 +639,14 @@ namespace Ryujinx.Graphics.Gpu.Memory ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) : bufferCache.GetBufferRange(bounds.Address, bounds.Size); - if (rangesCount == 0) - { - rangesFirst = bindingInfo.Binding; - } - else if (bindingInfo.Binding != rangesFirst + rangesCount) - { - SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage); - rangesFirst = bindingInfo.Binding; - rangesCount = 0; - } - - ranges[rangesCount++] = range; + ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); } } } if (rangesCount != 0) { - SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage); + SetHostBuffers(ranges, rangesCount, isStorage); } } @@ -671,10 +659,9 @@ namespace Ryujinx.Graphics.Gpu.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage) { - int rangesFirst = 0; int rangesCount = 0; - Span ranges = _ranges; + Span ranges = _ranges; for (int index = 0; index < buffers.Count; index++) { @@ -689,24 +676,13 @@ namespace Ryujinx.Graphics.Gpu.Memory ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) : bufferCache.GetBufferRange(bounds.Address, bounds.Size); - if (rangesCount == 0) - { - rangesFirst = bindingInfo.Binding; - } - else if (bindingInfo.Binding != rangesFirst + rangesCount) - { - SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage); - rangesFirst = bindingInfo.Binding; - rangesCount = 0; - } - - ranges[rangesCount++] = range; + ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); } } if (rangesCount != 0) { - SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage); + SetHostBuffers(ranges, rangesCount, isStorage); } } @@ -718,15 +694,15 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Number of bindings /// Indicates if the buffers are storage or uniform buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetHostBuffers(ReadOnlySpan ranges, int first, int count, bool isStorage) + private void SetHostBuffers(ReadOnlySpan ranges, int count, bool isStorage) { if (isStorage) { - _context.Renderer.Pipeline.SetStorageBuffers(first, ranges.Slice(0, count)); + _context.Renderer.Pipeline.SetStorageBuffers(ranges.Slice(0, count)); } else { - _context.Renderer.Pipeline.SetUniformBuffers(first, ranges.Slice(0, count)); + _context.Renderer.Pipeline.SetUniformBuffers(ranges.Slice(0, count)); } } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 3b234eb08..8bcaf4c77 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -1296,9 +1296,9 @@ namespace Ryujinx.Graphics.OpenGL _stencilFrontMask = stencilTest.FrontMask; } - public void SetStorageBuffers(int first, ReadOnlySpan buffers) + public void SetStorageBuffers(ReadOnlySpan buffers) { - SetBuffers(first, buffers, isStorage: true); + SetBuffers(buffers, isStorage: true); } public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) @@ -1366,9 +1366,9 @@ namespace Ryujinx.Graphics.OpenGL } } - public void SetUniformBuffers(int first, ReadOnlySpan buffers) + public void SetUniformBuffers(ReadOnlySpan buffers) { - SetBuffers(first, buffers, isStorage: false); + SetBuffers(buffers, isStorage: false); } public void SetUserClipDistance(int index, bool enableClip) @@ -1460,21 +1460,22 @@ namespace Ryujinx.Graphics.OpenGL GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit); } - private void SetBuffers(int first, ReadOnlySpan buffers, bool isStorage) + private void SetBuffers(ReadOnlySpan buffers, bool isStorage) { BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer; for (int index = 0; index < buffers.Length; index++) { - BufferRange buffer = buffers[index]; + BufferAssignment assignment = buffers[index]; + BufferRange buffer = assignment.Range; if (buffer.Handle == BufferHandle.Null) { - GL.BindBufferRange(target, first + index, 0, IntPtr.Zero, 0); + GL.BindBufferRange(target, assignment.Binding, 0, IntPtr.Zero, 0); continue; } - GL.BindBufferRange(target, first + index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size); + GL.BindBufferRange(target, assignment.Binding, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size); } } diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 8479bcf78..4cf9ce877 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -163,12 +163,13 @@ namespace Ryujinx.Graphics.Vulkan SignalDirty(DirtyFlags.Image); } - public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan buffers) + public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan buffers) { for (int i = 0; i < buffers.Length; i++) { - var buffer = buffers[i]; - int index = first + i; + var assignment = buffers[i]; + var buffer = assignment.Range; + int index = assignment.Binding; Auto vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); ref Auto currentVkBuffer = ref _storageBufferRefs[index]; @@ -243,12 +244,13 @@ namespace Ryujinx.Graphics.Vulkan SignalDirty(DirtyFlags.Texture); } - public void SetUniformBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan buffers) + public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan buffers) { for (int i = 0; i < buffers.Length; i++) { - var buffer = buffers[i]; - int index = first + i; + var assignment = buffers[i]; + var buffer = assignment.Range; + int index = assignment.Binding; Auto vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); ref Auto currentVkBuffer = ref _uniformBufferRefs[index]; diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 1ef22dc29..076a3baab 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, region); - _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, clearColor); - _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, ClearColorBufferSize) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -302,7 +302,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, region); - pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) }); + pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -380,7 +380,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetCommandBuffer(cbs); - _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); Span> sbRanges = new Auto[2]; @@ -571,7 +571,7 @@ namespace Ryujinx.Graphics.Vulkan int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel; _pipeline.Specialize(conversionType); - _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); if (src.Info.Target == Target.Texture2DMultisampleArray || dst.Info.Target == Target.Texture2DMultisampleArray) @@ -776,7 +776,7 @@ namespace Ryujinx.Graphics.Vulkan srcIndirectBufferOffset, indirectDataSize); - _pipeline.SetUniformBuffers(0, stackalloc[] { drawCountBufferAligned }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) }); _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() }); _pipeline.SetProgram(_programConvertIndirectData); @@ -804,7 +804,7 @@ namespace Ryujinx.Graphics.Vulkan 0, convertedCount * outputIndexSize); - _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(patternBufferHandle, 0, ParamsBufferSize) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternBufferHandle, 0, ParamsBufferSize)) }); _pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() }); _pipeline.SetProgram(_programConvertIndexBuffer); diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 5d2263aa2..87155a0d5 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -973,9 +973,9 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetStorageBuffers(int first, ReadOnlySpan buffers) + public void SetStorageBuffers(ReadOnlySpan buffers) { - _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers); + _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, buffers); } public void SetStorageBuffers(int first, ReadOnlySpan> buffers) @@ -1013,9 +1013,9 @@ namespace Ryujinx.Graphics.Vulkan } } - public void SetUniformBuffers(int first, ReadOnlySpan buffers) + public void SetUniformBuffers(ReadOnlySpan buffers) { - _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, first, buffers); + _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers); } public void SetUserClipDistance(int index, bool enableClip) From a0c77f8d11cf553b7737fc3e10186d013e50f633 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 24 Nov 2022 05:31:00 -0300 Subject: [PATCH 103/737] Fix NRE on Avalonia for error applets with unknown error message (#3888) --- Ryujinx.Ava/Ui/Applet/AvaHostUiHandler.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Ryujinx.Ava/Ui/Applet/AvaHostUiHandler.cs b/Ryujinx.Ava/Ui/Applet/AvaHostUiHandler.cs index 05e92c367..a8d6a6dff 100644 --- a/Ryujinx.Ava/Ui/Applet/AvaHostUiHandler.cs +++ b/Ryujinx.Ava/Ui/Applet/AvaHostUiHandler.cs @@ -57,14 +57,14 @@ namespace Ryujinx.Ava.Ui.Applet bool opened = false; - UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent, - title, - message, - "", - LocaleManager.Instance["DialogOpenSettingsWindowLabel"], - "", - LocaleManager.Instance["SettingsButtonClose"], - (int)Symbol.Important, + UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent, + title, + message, + "", + LocaleManager.Instance["DialogOpenSettingsWindowLabel"], + "", + LocaleManager.Instance["SettingsButtonClose"], + (int)Symbol.Important, deferEvent, async (window) => { @@ -168,7 +168,7 @@ namespace Ryujinx.Ava.Ui.Applet object response = await msgDialog.Run(); - if (response != null && buttons.Length > 1 && (int)response != buttons.Length - 1) + if (response != null && buttons != null && buttons.Length > 1 && (int)response != buttons.Length - 1) { showDetails = true; } From 008286b79fd7a5d223ccd7bd99c86d8058e216e1 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 24 Nov 2022 14:52:39 +0100 Subject: [PATCH 104/737] Ryujinx.Ava: Add missing redefinition of app name (#3890) Before this, Ryujinx would possibly report as "Avalonia Application". --- Ryujinx.Ava/App.axaml.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Ryujinx.Ava/App.axaml.cs b/Ryujinx.Ava/App.axaml.cs index 3ab1b7b8f..3c28c0191 100644 --- a/Ryujinx.Ava/App.axaml.cs +++ b/Ryujinx.Ava/App.axaml.cs @@ -21,6 +21,8 @@ namespace Ryujinx.Ava { public override void Initialize() { + Name = $"Ryujinx {Program.Version}"; + AvaloniaXamlLoader.Load(this); } From a1ddaa2736b188de928564af56aa787a25831ff7 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 24 Nov 2022 15:08:27 +0100 Subject: [PATCH 105/737] ui: Fixes disposing on GTK/Avalonia and Firmware Messages on Avalonia (#3885) * ui: Only wait on _exitEvent when MainLoop is active under GTK This fixes a dispose issue under Horizon/GTK, we don't check if the ApplicationClient is null so it throw NCE. We don't check if the main loop is active and waiting an event which is set in the main loop... So that could lead to a freeze. Everything works fine in GTK now. Related issue: https://github.com/Ryujinx/Ryujinx/issues/3873 As a side note, same kind of issue appear in Avalonia UI too. Firmware's popup doesn't show anything and the emulator just freeze. * TSRBerry's change Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Fix Avalonia crashing/freezing * Add Avalonia OpenGL fixes * Fix firmware popup on windows * Fixes everything * Add _initialized bool to VulkanRenderer and OpenGL Window Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- Ryujinx.Ava/AppHost.cs | 40 +++++++++---------- .../Ui/Controls/OpenGLEmbeddedWindow.cs | 6 +-- Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 39 ++++++++++-------- .../Multithreading/ThreadedRenderer.cs | 8 +++- Ryujinx.Graphics.OpenGL/Queries/Counters.cs | 2 +- Ryujinx.Graphics.OpenGL/Window.cs | 10 ++++- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 11 ++++- Ryujinx.HLE/HOS/Horizon.cs | 5 ++- Ryujinx/Ui/MainWindow.cs | 3 -- Ryujinx/Ui/RendererWidgetBase.cs | 10 +++-- Ryujinx/Ui/VKRenderer.cs | 3 +- 11 files changed, 83 insertions(+), 54 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 3d4c6cde8..a016ebd5a 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -60,7 +60,7 @@ namespace Ryujinx.Ava private const float VolumeDelta = 0.05f; - private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); + private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; @@ -349,7 +349,10 @@ namespace Ryujinx.Ava _isActive = false; - _renderingThread.Join(); + if (_renderingThread.IsAlive) + { + _renderingThread.Join(); + } DisplaySleep.Restore(); @@ -378,7 +381,7 @@ namespace Ryujinx.Ava _gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Dispose(); - + _chrono.Stop(); } @@ -393,7 +396,7 @@ namespace Ryujinx.Ava Renderer?.MakeCurrent(); Device.DisposeGpu(); - + Renderer?.MakeCurrent(null); } @@ -417,7 +420,6 @@ namespace Ryujinx.Ava public async Task LoadGuestApplication() { InitializeSwitchInstance(); - MainWindow.UpdateGraphicsConfig(); SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); @@ -428,17 +430,16 @@ namespace Ryujinx.Ava { if (userError == UserError.NoFirmware) { - string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], - firmwareVersion.VersionString); - UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], message, - LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], ""); + LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], + string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString), + LocaleManager.Instance["InputDialogYes"], + LocaleManager.Instance["InputDialogNo"], + ""); if (result != UserResult.Yes) { - Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(userError, _parent)); + await UserErrorDialog.ShowUserErrorDialog(userError, _parent); Device.Dispose(); return false; @@ -447,8 +448,7 @@ namespace Ryujinx.Ava if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) { - Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(userError, _parent)); + await UserErrorDialog.ShowUserErrorDialog(userError, _parent); Device.Dispose(); return false; @@ -461,11 +461,9 @@ namespace Ryujinx.Ava _parent.RefreshFirmwareStatus(); - string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString); - await ContentDialogHelper.CreateInfoDialog( string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString), - message, + string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString), LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]); @@ -473,9 +471,7 @@ namespace Ryujinx.Ava } else { - Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(userError, _parent)); - + await UserErrorDialog.ShowUserErrorDialog(userError, _parent); Device.Dispose(); return false; @@ -514,7 +510,7 @@ namespace Ryujinx.Ava } else if (File.Exists(ApplicationPath)) { - switch (System.IO.Path.GetExtension(ApplicationPath).ToLowerInvariant()) + switch (Path.GetExtension(ApplicationPath).ToLowerInvariant()) { case ".xci": { @@ -602,7 +598,7 @@ namespace Ryujinx.Ava if (Renderer.IsVulkan) { string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - + renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); } else diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs index ce579fdfd..f32bf0415 100644 --- a/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs +++ b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.Ui.Controls { throw new PlatformNotSupportedException(); } - + var flags = OpenGLContextFlags.Compat; if (_graphicsDebugLevel != GraphicsDebugLevel.None) { @@ -69,12 +69,12 @@ namespace Ryujinx.Ava.Ui.Controls public void MakeCurrent() { - Context.MakeCurrent(_window); + Context?.MakeCurrent(_window); } public void MakeCurrent(NativeWindowBase window) { - Context.MakeCurrent(window); + Context?.MakeCurrent(window); } public void SwapBuffers() diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs index 0e03803ba..be4517c86 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs @@ -251,24 +251,29 @@ namespace Ryujinx.Ava.Ui.Windows AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this); - if (!AppHost.LoadGuestApplication().Result) + Dispatcher.UIThread.Post(async () => { - AppHost.DisposeContext(); + if (!await AppHost.LoadGuestApplication()) + { + AppHost.DisposeContext(); + AppHost = null; - return; - } + return; + } - ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName; - ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; + ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName; + ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; - SwitchToGameControl(startFullscreen); + SwitchToGameControl(startFullscreen); - _currentEmulatedGamePath = path; - Thread gameThread = new Thread(InitializeGame) - { - Name = "GUI.WindowThread" - }; - gameThread.Start(); + _currentEmulatedGamePath = path; + + Thread gameThread = new(InitializeGame) + { + Name = "GUI.WindowThread" + }; + gameThread.Start(); + }); } private void InitializeGame() @@ -546,10 +551,12 @@ namespace Ryujinx.Ava.Ui.Windows { ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { - DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed); - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; + if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) + { + double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + } }); } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index e105a0926..7a625af38 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -422,7 +422,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading // Stop the GPU thread. _disposed = true; - _gpuThread.Join(); + + if (_gpuThread != null && _gpuThread.IsAlive) + { + _gpuThread.Join(); + } // Dispose the renderer. _baseRenderer.Dispose(); @@ -435,4 +439,4 @@ namespace Ryujinx.Graphics.GAL.Multithreading Sync.Dispose(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index 582800c8d..ebfd899c5 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -54,4 +54,4 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index 61b739b11..8f7917f91 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.OpenGL private const int TextureCount = 3; private readonly OpenGLRenderer _renderer; + private bool _initialized; + private int _width; private int _height; private int _copyFramebufferHandle; @@ -179,6 +181,7 @@ namespace Ryujinx.Graphics.OpenGL public void InitializeBackgroundContext(IOpenGLContext baseContext) { BackgroundContext = new BackgroundContextWorker(baseContext); + _initialized = true; } public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY) @@ -193,6 +196,11 @@ namespace Ryujinx.Graphics.OpenGL public void Dispose() { + if (!_initialized) + { + return; + } + BackgroundContext.Dispose(); if (_copyFramebufferHandle != 0) @@ -203,4 +211,4 @@ namespace Ryujinx.Graphics.OpenGL } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 3f8ebe67b..22e303294 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan private Device _device; private WindowBase _window; + private bool _initialized; + internal FormatCapabilities FormatCapabilities { get; private set; } internal HardwareCapabilities Capabilities; @@ -266,6 +268,8 @@ namespace Ryujinx.Graphics.Vulkan LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex); _window = new Window(this, _surface, _physicalDevice, _device); + + _initialized = true; } public BufferHandle CreateBuffer(int size) @@ -573,6 +577,11 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void Dispose() { + if (!_initialized) + { + return; + } + CommandBufferPool.Dispose(); BackgroundResources.Dispose(); _counters.Dispose(); @@ -613,4 +622,4 @@ namespace Ryujinx.Graphics.Vulkan Api.DestroyInstance(_instance, null); } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index d2716beb6..288c308a0 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -479,7 +479,10 @@ namespace Ryujinx.HLE.HOS AudioRendererManager.Dispose(); - LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure(); + if (LibHacHorizonManager.ApplicationClient != null) + { + LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure(); + } KernelContext.Dispose(); } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 3f261f4d0..9f9016b9f 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -735,7 +735,6 @@ namespace Ryujinx.Ui _emulationContext.Dispose(); SwitchToGameTable(); - RendererWidget.Dispose(); return; } @@ -747,7 +746,6 @@ namespace Ryujinx.Ui _emulationContext.Dispose(); SwitchToGameTable(); - RendererWidget.Dispose(); return; } @@ -770,7 +768,6 @@ namespace Ryujinx.Ui _emulationContext.Dispose(); SwitchToGameTable(); - RendererWidget.Dispose(); return; } diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 7e25ba2d9..576d2d121 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -519,10 +519,14 @@ namespace Ryujinx.Ui _gpuCancellationTokenSource.Cancel(); _isStopped = true; - _isActive = false; + + if (_isActive) + { + _isActive = false; - _exitEvent.WaitOne(); - _exitEvent.Dispose(); + _exitEvent.WaitOne(); + _exitEvent.Dispose(); + } } private void NVStutterWorkaround() diff --git a/Ryujinx/Ui/VKRenderer.cs b/Ryujinx/Ui/VKRenderer.cs index 49578d2af..7e02c689d 100644 --- a/Ryujinx/Ui/VKRenderer.cs +++ b/Ryujinx/Ui/VKRenderer.cs @@ -72,7 +72,8 @@ namespace Ryujinx.Ui protected override void Dispose(bool disposing) { - Device.DisposeGpu(); + Device?.DisposeGpu(); + NpadManager.Dispose(); } } From f4e879a1e6ad810aa38c1c020467a2589441871b Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 24 Nov 2022 15:26:29 +0100 Subject: [PATCH 106/737] Reduce usage of Marshal.PtrToStructure and Marshal.StructureToPtr (#3805) * common: Make BinaryReaderExtensions Read & Write take unamanged types This allows us to not rely on Marshal.PtrToStructure and Marshal.StructureToPtr for those. * common: Make MemoryHelper Read & Write takes unamanged types * Update Marshal.SizeOf => Unsafe.SizeOf when appropriate and start moving software applet to unmanaged types --- .../Extensions/BinaryReaderExtensions.cs | 43 +++---------------- Ryujinx.Cpu/MemoryHelper.cs | 27 +++--------- .../SoftwareKeyboardApplet.cs | 23 +++++----- .../SoftwareKeyboardCustomizeDic.cs | 5 +-- .../SoftwareKeyboardDictSet.cs | 6 +-- .../SoftwareKeyboardUserWord.cs | 5 +-- .../FriendService/Types/UserPresence.cs | 22 +++++++--- .../Friend/ServiceCreator/IFriendService.cs | 13 +----- .../Types/NotificationInfo.cs | 10 ++--- .../QueryPlayStatisticsManager.cs | 3 +- .../Time/Clock/Types/ClockSnapshot.cs | 13 +++++- .../HOS/Services/Time/IStaticServiceForPsc.cs | 13 +++--- .../HOS/Services/Time/TimeZone/TimeZone.cs | 5 ++- .../CemuHook/Protocol/ControllerData.cs | 18 +++----- .../CemuHook/Protocol/ControllerInfo.cs | 13 +++--- .../CemuHook/Protocol/SharedResponse.cs | 6 +-- 16 files changed, 88 insertions(+), 137 deletions(-) diff --git a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs index 2c24678d1..ea404d2a2 100644 --- a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs +++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Ryujinx.Common @@ -7,49 +8,15 @@ namespace Ryujinx.Common public static class BinaryReaderExtensions { public unsafe static T ReadStruct(this BinaryReader reader) - where T : struct + where T : unmanaged { - int size = Marshal.SizeOf(); - - byte[] data = reader.ReadBytes(size); - - fixed (byte* ptr = data) - { - return Marshal.PtrToStructure((IntPtr)ptr); - } - } - - public unsafe static T[] ReadStructArray(this BinaryReader reader, int count) - where T : struct - { - int size = Marshal.SizeOf(); - - T[] result = new T[count]; - - for (int i = 0; i < count; i++) - { - byte[] data = reader.ReadBytes(size); - - fixed (byte* ptr = data) - { - result[i] = Marshal.PtrToStructure((IntPtr)ptr); - } - } - - return result; + return MemoryMarshal.Cast(reader.ReadBytes(Unsafe.SizeOf()))[0]; } public unsafe static void WriteStruct(this BinaryWriter writer, T value) - where T : struct + where T : unmanaged { - long size = Marshal.SizeOf(); - - byte[] data = new byte[size]; - - fixed (byte* ptr = data) - { - Marshal.StructureToPtr(value, (IntPtr)ptr, false); - } + ReadOnlySpan data = MemoryMarshal.Cast(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); writer.Write(data); } diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs index 6194d5b2d..64ff360e5 100644 --- a/Ryujinx.Cpu/MemoryHelper.cs +++ b/Ryujinx.Cpu/MemoryHelper.cs @@ -1,6 +1,7 @@ using Ryujinx.Memory; using System; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -23,34 +24,18 @@ namespace Ryujinx.Cpu } } - public unsafe static T Read(IVirtualMemoryManager memory, ulong position) where T : struct + public unsafe static T Read(IVirtualMemoryManager memory, ulong position) where T : unmanaged { - long size = Marshal.SizeOf(); - - byte[] data = new byte[size]; - - memory.Read(position, data); - - fixed (byte* ptr = data) - { - return Marshal.PtrToStructure((IntPtr)ptr); - } + return MemoryMarshal.Cast(memory.GetSpan(position, Unsafe.SizeOf()))[0]; } - public unsafe static ulong Write(IVirtualMemoryManager memory, ulong position, T value) where T : struct + public unsafe static ulong Write(IVirtualMemoryManager memory, ulong position, T value) where T : unmanaged { - long size = Marshal.SizeOf(); - - byte[] data = new byte[size]; - - fixed (byte* ptr = data) - { - Marshal.StructureToPtr(value, (IntPtr)ptr, false); - } + ReadOnlySpan data = MemoryMarshal.Cast(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); memory.Write(position, data); - return (ulong)size; + return (ulong)data.Length; } public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1) diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index e287318a6..74073420f 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -1,4 +1,5 @@ -using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common; +using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using Ryujinx.HLE.HOS.Services.Am.AppletAE; @@ -9,6 +10,7 @@ using Ryujinx.Memory; using System; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -78,13 +80,13 @@ namespace Ryujinx.HLE.HOS.Applets var launchParams = _normalSession.Pop(); var keyboardConfig = _normalSession.Pop(); - _isBackground = keyboardConfig.Length == Marshal.SizeOf(); + _isBackground = keyboardConfig.Length == Unsafe.SizeOf(); if (_isBackground) { // Initialize the keyboard applet in background mode. - _keyboardBackgroundInitialize = ReadStruct(keyboardConfig); + _keyboardBackgroundInitialize = MemoryMarshal.Read(keyboardConfig); _backgroundState = InlineKeyboardState.Uninitialized; if (_device.UiHandler == null) @@ -342,7 +344,7 @@ namespace Ryujinx.HLE.HOS.Applets else { int wordsCount = reader.ReadInt32(); - int wordSize = Marshal.SizeOf(); + int wordSize = Unsafe.SizeOf(); remaining = stream.Length - stream.Position; if (wordsCount > MaxUserWords) @@ -359,8 +361,7 @@ namespace Ryujinx.HLE.HOS.Applets for (int word = 0; word < wordsCount; word++) { - byte[] wordData = reader.ReadBytes(wordSize); - _keyboardBackgroundUserWords[word] = ReadStruct(wordData); + _keyboardBackgroundUserWords[word] = reader.ReadStruct(); } } } @@ -369,27 +370,25 @@ namespace Ryujinx.HLE.HOS.Applets case InlineKeyboardRequest.SetCustomizeDic: // Read the custom dic data. remaining = stream.Length - stream.Position; - if (remaining != Marshal.SizeOf()) + if (remaining != Unsafe.SizeOf()) { Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes"); } else { - var keyboardDicData = reader.ReadBytes((int)remaining); - _keyboardBackgroundDic = ReadStruct(keyboardDicData); + _keyboardBackgroundDic = reader.ReadStruct(); } break; case InlineKeyboardRequest.SetCustomizedDictionaries: // Read the custom dictionaries data. remaining = stream.Length - stream.Position; - if (remaining != Marshal.SizeOf()) + if (remaining != Unsafe.SizeOf()) { Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes"); } else { - var keyboardDictData = reader.ReadBytes((int)remaining); - _keyboardBackgroundDictSet = ReadStruct(keyboardDictData); + _keyboardBackgroundDictSet = reader.ReadStruct(); } break; case InlineKeyboardRequest.Calc: diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs index 538fb9274..53c8c8950 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs @@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard /// /// A structure used by SetCustomizeDic request to software keyboard. /// - [StructLayout(LayoutKind.Sequential, Pack = 4)] + [StructLayout(LayoutKind.Sequential, Size = 0x70)] struct SoftwareKeyboardCustomizeDic { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 112)] - public byte[] Unknown; + // Unknown } } diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs index b4ffdb908..385548816 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { @@ -21,8 +22,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard /// /// Array of word entries in the buffer. /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] - public ulong[] Entries; + public Array24 Entries; /// /// Number of used entries in the Entries field. diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs index 08f1c3d33..f1bfec2b3 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs @@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard /// /// A structure used by SetUserWordInfo request to the software keyboard. /// - [StructLayout(LayoutKind.Sequential, Pack = 4)] + [StructLayout(LayoutKind.Sequential, Size = 0x64)] struct SoftwareKeyboardUserWord { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] - public byte[] Unknown; + // Unknown } } diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs index e7568a4aa..058c5646a 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs @@ -1,9 +1,12 @@ -using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using System; using System.Runtime.InteropServices; +using System.Text; namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService { - [StructLayout(LayoutKind.Sequential, Pack = 0x8, CharSet = CharSet.Ansi)] + [StructLayout(LayoutKind.Sequential, Pack = 0x8)] struct UserPresence { public UserId UserId; @@ -13,15 +16,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService [MarshalAs(UnmanagedType.I1)] public bool SamePresenceGroupApplication; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)] - public char[] Unknown; + public Array3 Unknown; + private AppKeyValueStorageHolder _appKeyValueStorage; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xC0)] - public char[] AppKeyValueStorage; + public Span AppKeyValueStorage => MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref _appKeyValueStorage, AppKeyValueStorageHolder.Size)); + + [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = Size)] + private struct AppKeyValueStorageHolder + { + public const int Size = 0xC0; + } public override string ToString() { - return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {AppKeyValueStorage} }}"; + return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {Encoding.ASCII.GetString(AppKeyValueStorage)} }}"; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index ec947414f..8159d0918 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -236,23 +236,14 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator ulong position = context.Request.PtrBuff[0].Position; ulong size = context.Request.PtrBuff[0].Size; - byte[] bufferContent = new byte[size]; - - context.Memory.Read(position, bufferContent); + ReadOnlySpan userPresenceInputArray = MemoryMarshal.Cast(context.Memory.GetSpan(position, (int)size)); if (uuid.IsNull) { return ResultCode.InvalidArgument; } - int elementCount = bufferContent.Length / Marshal.SizeOf(); - - using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(bufferContent))) - { - UserPresence[] userPresenceInputArray = bufferReader.ReadStructArray(elementCount); - - Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray }); - } + Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray = userPresenceInputArray.ToArray() }); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs index 1bd6f0118..e710bf064 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs @@ -1,15 +1,13 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService { - [StructLayout(LayoutKind.Sequential, Pack = 0x8, Size = 0x10)] + [StructLayout(LayoutKind.Sequential, Size = 0x10)] struct NotificationInfo { public NotificationEventType Type; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4)] - public char[] Padding; - + private Array4 _padding; public long NetworkUserIdPlaceholder; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs index fc6635fa7..1d6cc118e 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs @@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types; using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService @@ -75,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++) { - MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Marshal.SizeOf()), filteredApplicationPlayStatistics.ElementAt(i).Value); + MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Unsafe.SizeOf()), filteredApplicationPlayStatistics.ElementAt(i).Value); } context.ResponseData.Write(filteredApplicationPlayStatistics.Count()); diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs index df1f151fe..07c1b405f 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone; +using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time.Clock @@ -16,14 +17,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public CalendarAdditionalInfo NetworkCalendarAdditionalTime; public SteadyClockTimePoint SteadyClockTimePoint; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24)] - public char[] LocationName; + private LocationNameStorageHolder _locationName; + + public Span LocationName => MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref _locationName, LocationNameStorageHolder.Size)); [MarshalAs(UnmanagedType.I1)] public bool IsAutomaticCorrectionEnabled; public byte Type; public ushort Unknown; + + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] + private struct LocationNameStorageHolder + { + public const int Size = 0x24; + } + public static ResultCode GetCurrentTime(out long currentTime, SteadyClockTimePoint steadyClockTimePoint, SystemClockContext context) { currentTime = 0; diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs index 441e4267b..4f3518121 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs @@ -8,7 +8,9 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone; using System; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text; namespace Ryujinx.HLE.HOS.Services.Time { @@ -281,7 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { byte type = context.RequestData.ReadByte(); - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf()); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf()); context.RequestData.BaseStream.Position += 7; @@ -372,12 +374,9 @@ namespace Ryujinx.HLE.HOS.Services.Time return result; } - char[] tzName = deviceLocationName.ToCharArray(); - char[] locationName = new char[0x24]; + ReadOnlySpan tzName = Encoding.ASCII.GetBytes(deviceLocationName); - Array.Copy(tzName, locationName, tzName.Length); - - clockSnapshot.LocationName = locationName; + tzName.CopyTo(clockSnapshot.LocationName); result = ClockSnapshot.GetCurrentTime(out clockSnapshot.UserTime, currentTimePoint, clockSnapshot.UserContext); @@ -414,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Services.Time private ClockSnapshot ReadClockSnapshotFromBuffer(ServiceCtx context, IpcPtrBuffDesc ipcDesc) { - Debug.Assert(ipcDesc.Size == (ulong)Marshal.SizeOf()); + Debug.Assert(ipcDesc.Size == (ulong)Unsafe.SizeOf()); byte[] temp = new byte[ipcDesc.Size]; diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs index 12f6e90d4..f7477e97d 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs @@ -5,6 +5,7 @@ using Ryujinx.HLE.Utilities; using System; using System.Buffers.Binary; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -890,14 +891,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone long streamLength = reader.BaseStream.Length; - if (streamLength < Marshal.SizeOf()) + if (streamLength < Unsafe.SizeOf()) { return false; } TzifHeader header = reader.ReadStruct(); - streamLength -= Marshal.SizeOf(); + streamLength -= Unsafe.SizeOf(); int ttisGMTCount = Detzcode32(header.TtisGMTCount); int ttisSTDCount = Detzcode32(header.TtisSTDCount); diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs b/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs index 9ff8dc910..7fb72344c 100644 --- a/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs +++ b/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs @@ -1,16 +1,15 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.Input.Motion.CemuHook.Protocol { [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ControllerDataRequest { - public MessageType Type; + public MessageType Type; public SubscriberType SubscriberType; - public byte Slot; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public byte[] MacAddress; + public byte Slot; + public Array6 MacAddress; } [StructLayout(LayoutKind.Sequential, Pack = 1)] @@ -27,11 +26,8 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol public uint DPadAnalog; public ulong MainButtonsAnalog; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public byte[] Touch1; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public byte[] Touch2; + public Array6 Touch1; + public Array6 Touch2; public ulong MotionTimestamp; public float AccelerometerX; diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs b/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs index d483633eb..63d4524a8 100644 --- a/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs +++ b/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs @@ -1,21 +1,20 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.Input.Motion.CemuHook.Protocol { [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ControllerInfoResponse { - public SharedResponse Shared; - private byte _zero; + public SharedResponse Shared; + private byte _zero; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ControllerInfoRequest { public MessageType Type; - public int PortsCount; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] PortIndices; + public int PortsCount; + public Array4 PortIndices; } } \ No newline at end of file diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs b/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs index 0593286d3..e2e1ee9b4 100644 --- a/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs +++ b/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.Input.Motion.CemuHook.Protocol { @@ -11,8 +12,7 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol public DeviceModelType ModelType; public ConnectionType ConnectionType; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public byte[] MacAddress; + public Array6 MacAddress; public BatteryStatus BatteryStatus; } From 65778a6b78ab8bde4090478482227e40c551db4d Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 24 Nov 2022 14:50:15 +0000 Subject: [PATCH 107/737] GPU: Don't trigger uploads for redundant buffer updates (#3828) * Initial implementation * Actually do The Thing * Add remark about performance to IVirtualMemoryManager --- Ryujinx.Cpu/Jit/MemoryManager.cs | 31 +++++++++++++++++++ Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs | 28 +++++++++++++++++ .../Engine/Threed/ConstantBufferUpdater.cs | 24 +++++++++----- Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 13 ++++++++ .../MockVirtualMemoryManager.cs | 5 +++ Ryujinx.Memory/AddressSpaceManager.cs | 8 +++++ Ryujinx.Memory/IVirtualMemoryManager.cs | 11 +++++++ 7 files changed, 113 insertions(+), 7 deletions(-) diff --git a/Ryujinx.Cpu/Jit/MemoryManager.cs b/Ryujinx.Cpu/Jit/MemoryManager.cs index 86c69431d..21c50d51f 100644 --- a/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -180,6 +180,37 @@ namespace Ryujinx.Cpu.Jit WriteImpl(va, data); } + /// + public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data) + { + if (data.Length == 0) + { + return false; + } + + SignalMemoryTracking(va, (ulong)data.Length, false); + + if (IsContiguousAndMapped(va, data.Length)) + { + var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length); + + bool changed = !data.SequenceEqual(target); + + if (changed) + { + data.CopyTo(target); + } + + return changed; + } + else + { + WriteImpl(va, data); + + return true; + } + } + /// /// Writes data to CPU mapped memory. /// diff --git a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 8994e9c0e..c4e59db9f 100644 --- a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -307,6 +307,34 @@ namespace Ryujinx.Cpu.Jit } } + /// + public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data) + { + try + { + SignalMemoryTracking(va, (ulong)data.Length, false); + + Span target = _addressSpaceMirror.GetSpan(va, data.Length); + bool changed = !data.SequenceEqual(target); + + if (changed) + { + data.CopyTo(target); + } + + return changed; + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; + } + + return true; + } + } + /// public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) { diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs index f4006ba99..5c9366160 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// class ConstantBufferUpdater { + private const int UniformDataCacheSize = 512; + private readonly GpuChannel _channel; private readonly DeviceStateWithShadow _state; @@ -16,6 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private ulong _ubBeginCpuAddress = 0; private ulong _ubFollowUpAddress = 0; private ulong _ubByteCount = 0; + private int _ubIndex = 0; + private int[] _ubData = new int[UniformDataCacheSize]; /// /// Creates a new instance of the constant buffer updater. @@ -108,9 +112,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (_ubFollowUpAddress != 0) { var memoryManager = _channel.MemoryManager; - memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount); + + Span data = MemoryMarshal.Cast(_ubData.AsSpan(0, (int)(_ubByteCount / 4))); + + if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data)) + { + memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount); + } _ubFollowUpAddress = 0; + _ubIndex = 0; } } @@ -124,7 +135,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset; - if (_ubFollowUpAddress != address) + if (_ubFollowUpAddress != address || _ubIndex == _ubData.Length) { FlushUboDirty(); @@ -132,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _ubBeginCpuAddress = _channel.MemoryManager.Translate(address); } - var byteData = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref argument, 1)); - _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData); + _ubData[_ubIndex++] = argument; _ubFollowUpAddress = address + 4; _ubByteCount += 4; @@ -153,7 +163,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ulong size = (ulong)data.Length * 4; - if (_ubFollowUpAddress != address) + if (_ubFollowUpAddress != address || _ubIndex + data.Length > _ubData.Length) { FlushUboDirty(); @@ -161,8 +171,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _ubBeginCpuAddress = _channel.MemoryManager.Translate(address); } - var byteData = MemoryMarshal.Cast(data); - _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData); + data.CopyTo(_ubData.AsSpan(_ubIndex)); + _ubIndex += data.Length; _ubFollowUpAddress = address + size; _ubByteCount += size; diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index 155cba0f5..051838f1f 100644 --- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -242,6 +242,19 @@ namespace Ryujinx.Graphics.Gpu.Memory WriteImpl(range, data, _cpuMemory.WriteUntracked); } + /// + /// Writes data to the application process, returning false if the data was not changed. + /// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date. + /// + /// The memory manager can return that memory has changed when it hasn't to avoid expensive data copies. + /// Address to write into + /// Data to be written + /// True if the data was changed, false otherwise + public bool WriteWithRedundancyCheck(ulong address, ReadOnlySpan data) + { + return _cpuMemory.WriteWithRedundancyCheck(address, data); + } + private delegate void WriteCallback(ulong address, ReadOnlySpan data); /// diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 29922f898..6c4422829 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -44,6 +44,11 @@ namespace Ryujinx.Memory.Tests throw new NotImplementedException(); } + public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data) + { + throw new NotImplementedException(); + } + public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) { throw new NotImplementedException(); diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs index 45f3225e1..ffe880bf8 100644 --- a/Ryujinx.Memory/AddressSpaceManager.cs +++ b/Ryujinx.Memory/AddressSpaceManager.cs @@ -136,6 +136,14 @@ namespace Ryujinx.Memory } } + /// + public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data) + { + Write(va, data); + + return true; + } + /// public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) { diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs index f97cb0b57..c8a74f665 100644 --- a/Ryujinx.Memory/IVirtualMemoryManager.cs +++ b/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -58,6 +58,17 @@ namespace Ryujinx.Memory /// Throw for unhandled invalid or unmapped memory accesses void Write(ulong va, ReadOnlySpan data); + /// + /// Writes data to the application process, returning false if the data was not changed. + /// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date. + /// + /// The memory manager can return that memory has changed when it hasn't to avoid expensive data copies. + /// Virtual address to write the data into + /// Data to be written + /// Throw for unhandled invalid or unmapped memory accesses + /// True if the data was changed, false otherwise + bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data); + void Fill(ulong va, ulong size, byte value) { const int MaxChunkSize = 1 << 24; From 548bfd60a2468aa6258675efae5ed21102985cc6 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 24 Nov 2022 20:13:16 +0100 Subject: [PATCH 108/737] chore: Update Ryujinx.SDL2-CS to 2.24.2 (#3892) * chore: Update Ryujinx.SDL2-CS to 2.24.2 * Disable SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS --- Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs | 8 ++------ Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj | 2 +- Ryujinx.SDL2.Common/SDL2Driver.cs | 7 +++++++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs index 0fcf517be..6eacadc15 100644 --- a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs @@ -40,20 +40,16 @@ namespace Ryujinx.Headless.SDL2.Vulkan return (IntPtr)surfaceHandle; } - // TODO: Fix this in SDL2-CS. - [DllImport("SDL2", EntryPoint = "SDL_Vulkan_GetInstanceExtensions", CallingConvention = CallingConvention.Cdecl)] - public static extern SDL_bool SDL_Vulkan_GetInstanceExtensions_Workaround(IntPtr window, out uint count, IntPtr names); - public unsafe string[] GetRequiredInstanceExtensions() { - if (SDL_Vulkan_GetInstanceExtensions_Workaround(WindowHandle, out uint extensionsCount, IntPtr.Zero) == SDL_bool.SDL_TRUE) + if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out uint extensionsCount, IntPtr.Zero) == SDL_bool.SDL_TRUE) { IntPtr[] rawExtensions = new IntPtr[(int)extensionsCount]; string[] extensions = new string[(int)extensionsCount]; fixed (IntPtr* rawExtensionsPtr = rawExtensions) { - if (SDL_Vulkan_GetInstanceExtensions_Workaround(WindowHandle, out extensionsCount, (IntPtr)rawExtensionsPtr) == SDL_bool.SDL_TRUE) + if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out extensionsCount, (IntPtr)rawExtensionsPtr) == SDL_bool.SDL_TRUE) { for (int i = 0; i < extensions.Length; i++) { diff --git a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj index 51b3b374e..96bebac0a 100644 --- a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj +++ b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj @@ -5,7 +5,7 @@ - + diff --git a/Ryujinx.SDL2.Common/SDL2Driver.cs b/Ryujinx.SDL2.Common/SDL2Driver.cs index fbf2b64ab..4a30523c3 100644 --- a/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -43,6 +43,8 @@ namespace Ryujinx.SDL2.Common private SDL2Driver() {} + private const string SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS = "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS"; + public void Initialize() { lock (_lock) @@ -60,6 +62,11 @@ namespace Ryujinx.SDL2.Common SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + + // NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them. + // We disable this behavior for now. + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0"); + if (SDL_Init(SdlInitFlags) != 0) { string errorMessage = $"SDL2 initlaization failed with error \"{SDL_GetError()}\""; From 7aa6abc12038b949f26c5f1ad1d4559c81fdc1af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 20:30:17 +0100 Subject: [PATCH 109/737] nuget: bump SharpZipLib from 1.3.3 to 1.4.1 (#3893) Bumps [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) from 1.3.3 to 1.4.1. - [Release notes](https://github.com/icsharpcode/SharpZipLib/releases) - [Changelog](https://github.com/icsharpcode/SharpZipLib/blob/master/docs/Changes.txt) - [Commits](https://github.com/icsharpcode/SharpZipLib/compare/v1.3.3...v1.4.1) --- updated-dependencies: - dependency-name: SharpZipLib dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- Ryujinx/Ryujinx.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 656d3b077..52297ceee 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -37,7 +37,7 @@ - + diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 58b1555cf..7681fe54e 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -26,7 +26,7 @@ - + From 3fbacd0f495f087af5a6316da5abe2b4595d8c9d Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 25 Nov 2022 12:41:34 +0100 Subject: [PATCH 110/737] ava: Rework DLC Manager, Add various fixes and cleanup (#3896) * Fixes Everything Part.2 * Change sorting, fix remove and heading --- Ryujinx.Ava/Assets/Locales/en_US.json | 4 +- Ryujinx.Ava/Program.cs | 42 ++- .../Ui/Models/DownloadableContentModel.cs | 20 +- .../Ui/ViewModels/MainWindowViewModel.cs | 286 ++++++++---------- .../DownloadableContentManagerWindow.axaml | 147 +++++---- .../DownloadableContentManagerWindow.axaml.cs | 157 ++++++---- Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 23 +- Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml | 2 +- .../Ui/Windows/SettingsWindow.axaml.cs | 62 ++-- .../Ui/Windows/TitleUpdateWindow.axaml.cs | 7 + 10 files changed, 411 insertions(+), 339 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 5e3ddb8ae..e98988b7c 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -410,6 +410,8 @@ "DlcManagerTableHeadingContainerPathLabel": "Container Path", "DlcManagerTableHeadingFullPathLabel": "Full Path", "DlcManagerRemoveAllButton": "Remove All", + "DlcManagerEnableAllButton": "Enable All", + "DlcManagerDisableAllButton": "Disable All", "MenuBarOptionsChangeLanguage": "Change Language", "CommonSort": "Sort", "CommonShowNames": "Show Names", @@ -567,7 +569,7 @@ "DlcWindowTitle": "Manage Game DLC", "UpdateWindowTitle": "Manage Game Updates", "CheatWindowHeading": "Cheats Available for {0} [{1}]", - "DlcWindowHeading": "DLC Available for {0} [{1}]", + "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})", "UserProfilesEditProfile": "Edit Selected", "Cancel": "Cancel", "Save": "Save", diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 053bccab6..a941302b2 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -23,19 +23,18 @@ namespace Ryujinx.Ava { internal class Program { - public static double WindowScaleFactor { get; set; } - public static double ActualScaleFactor { get; set; } - public static string Version { get; private set; } - public static string ConfigurationPath { get; private set; } - public static bool PreviewerDetached { get; private set; } - - public static RenderTimer RenderTimer { get; private set; } + public static double WindowScaleFactor { get; set; } + public static double ActualScaleFactor { get; set; } + public static string Version { get; private set; } + public static string ConfigurationPath { get; private set; } + public static bool PreviewerDetached { get; private set; } + public static RenderTimer RenderTimer { get; private set; } [DllImport("user32.dll", SetLastError = true)] public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type); private const uint MB_ICONWARNING = 0x30; - private const int BaseDpi = 96; + private const int BaseDpi = 96; public static void Main(string[] args) { @@ -43,7 +42,7 @@ namespace Ryujinx.Ava if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) { - MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING); + _ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING); } PreviewerDetached = true; @@ -64,16 +63,16 @@ namespace Ryujinx.Ava .With(new X11PlatformOptions { EnableMultiTouch = true, - EnableIme = true, - UseEGL = false, - UseGpu = false + EnableIme = true, + UseEGL = false, + UseGpu = false }) .With(new Win32PlatformOptions { - EnableMultitouch = true, - UseWgl = false, - AllowEglInitialization = false, - CompositionBackdropCornerRadius = 8f, + EnableMultitouch = true, + UseWgl = false, + AllowEglInitialization = false, + CompositionBackdropCornerRadius = 8.0f, }) .UseSkia() .AfterSetup(_ => @@ -122,12 +121,10 @@ namespace Ryujinx.Ava PrintSystemInfo(); // Enable OGL multithreading on the driver, when available. - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off); + DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); // Check if keys exists. - bool hasSystemProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")); - if (!hasSystemProdKeys) + if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) { if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys")))) { @@ -143,7 +140,7 @@ namespace Ryujinx.Ava public static void ReloadConfig() { - string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); + string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json"); // Now load the configuration as the other subsystems are now registered @@ -197,8 +194,7 @@ namespace Ryujinx.Ava Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}"); SystemInfo.Gather().Print(); - var enabledLogs = Logger.GetEnabledLevels(); - Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "" : string.Join(", ", enabledLogs))}"); + Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(Logger.GetEnabledLevels().Count == 0 ? "" : string.Join(", ", Logger.GetEnabledLevels()))}"); if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom) { diff --git a/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs b/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs index 67530f62c..5f3ca0317 100644 --- a/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs +++ b/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs @@ -1,8 +1,22 @@ -namespace Ryujinx.Ava.Ui.Models +using Ryujinx.Ava.Ui.ViewModels; + +namespace Ryujinx.Ava.Ui.Models { - public class DownloadableContentModel + public class DownloadableContentModel : BaseModel { - public bool Enabled { get; set; } + private bool _enabled; + + public bool Enabled + { + get => _enabled; + set + { + _enabled = value; + + OnPropertyChanged(); + } + } + public string TitleId { get; } public string ContainerPath { get; } public string FullPath { get; } diff --git a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs index 3672f9633..d5d3b7609 100644 --- a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs @@ -19,6 +19,7 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS; using Ryujinx.Modules; using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; @@ -47,6 +48,7 @@ namespace Ryujinx.Ava.Ui.ViewModels private string _loadHeading; private string _cacheLoadStatus; private string _searchText; + private Timer _searchTimer; private string _dockedStatusText; private string _fifoStatusText; private string _gameStatusText; @@ -115,10 +117,20 @@ namespace Ryujinx.Ava.Ui.ViewModels { _searchText = value; - RefreshView(); + _searchTimer?.Dispose(); + + _searchTimer = new Timer(TimerCallback, null, 1000, 0); } } + private void TimerCallback(object obj) + { + RefreshView(); + + _searchTimer.Dispose(); + _searchTimer = null; + } + public ReadOnlyObservableCollection AppsObservableList { get => _appsObservableList; @@ -200,22 +212,19 @@ namespace Ryujinx.Ava.Ui.ViewModels private string _showUikey = "F4"; private string _pauseKey = "F5"; private string _screenshotkey = "F8"; - private float _volume; + private float _volume; private string _backendText; public ApplicationData SelectedApplication { get { - switch (Glyph) + return Glyph switch { - case Glyph.List: - return _owner.GameList.SelectedApplication; - case Glyph.Grid: - return _owner.GameGrid.SelectedApplication; - default: - return null; - } + Glyph.List => _owner.GameList.SelectedApplication, + Glyph.Grid => _owner.GameGrid.SelectedApplication, + _ => null, + }; } } @@ -408,6 +417,7 @@ namespace Ryujinx.Ava.Ui.ViewModels { _owner.AppHost.Device.SetVolume(_volume); } + OnPropertyChanged(nameof(VolumeStatusText)); OnPropertyChanged(nameof(VolumeMuted)); OnPropertyChanged(); @@ -477,38 +487,36 @@ namespace Ryujinx.Ava.Ui.ViewModels internal void Sort(bool isAscending) { IsAscending = isAscending; + RefreshView(); } internal void Sort(ApplicationSort sort) { SortMode = sort; + RefreshView(); } private IComparer GetComparer() { - switch (SortMode) + return SortMode switch { - case ApplicationSort.LastPlayed: - return new Models.Generic.LastPlayedSortComparer(IsAscending); - case ApplicationSort.FileSize: - return new Models.Generic.FileSizeSortComparer(IsAscending); - case ApplicationSort.TotalTimePlayed: - return new Models.Generic.TimePlayedSortComparer(IsAscending); - case ApplicationSort.Title: - return IsAscending ? SortExpressionComparer.Ascending(app => app.TitleName) : SortExpressionComparer.Descending(app => app.TitleName); - case ApplicationSort.Favorite: - return !IsAscending ? SortExpressionComparer.Ascending(app => app.Favorite) : SortExpressionComparer.Descending(app => app.Favorite); - case ApplicationSort.Developer: - return IsAscending ? SortExpressionComparer.Ascending(app => app.Developer) : SortExpressionComparer.Descending(app => app.Developer); - case ApplicationSort.FileType: - return IsAscending ? SortExpressionComparer.Ascending(app => app.FileExtension) : SortExpressionComparer.Descending(app => app.FileExtension); - case ApplicationSort.Path: - return IsAscending ? SortExpressionComparer.Ascending(app => app.Path) : SortExpressionComparer.Descending(app => app.Path); - default: - return null; - } + ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending), + ApplicationSort.FileSize => new Models.Generic.FileSizeSortComparer(IsAscending), + ApplicationSort.TotalTimePlayed => new Models.Generic.TimePlayedSortComparer(IsAscending), + ApplicationSort.Title => IsAscending ? SortExpressionComparer.Ascending(app => app.TitleName) + : SortExpressionComparer.Descending(app => app.TitleName), + ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer.Ascending(app => app.Favorite) + : SortExpressionComparer.Descending(app => app.Favorite), + ApplicationSort.Developer => IsAscending ? SortExpressionComparer.Ascending(app => app.Developer) + : SortExpressionComparer.Descending(app => app.Developer), + ApplicationSort.FileType => IsAscending ? SortExpressionComparer.Ascending(app => app.FileExtension) + : SortExpressionComparer.Descending(app => app.FileExtension), + ApplicationSort.Path => IsAscending ? SortExpressionComparer.Ascending(app => app.Path) + : SortExpressionComparer.Descending(app => app.Path), + _ => null, + }; } private void RefreshView() @@ -611,40 +619,31 @@ namespace Ryujinx.Ava.Ui.ViewModels } } - public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite; - public bool IsSortedByTitle => SortMode == ApplicationSort.Title; - public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer; + public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite; + public bool IsSortedByTitle => SortMode == ApplicationSort.Title; + public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer; public bool IsSortedByLastPlayed => SortMode == ApplicationSort.LastPlayed; public bool IsSortedByTimePlayed => SortMode == ApplicationSort.TotalTimePlayed; - public bool IsSortedByType => SortMode == ApplicationSort.FileType; - public bool IsSortedBySize => SortMode == ApplicationSort.FileSize; - public bool IsSortedByPath => SortMode == ApplicationSort.Path; + public bool IsSortedByType => SortMode == ApplicationSort.FileType; + public bool IsSortedBySize => SortMode == ApplicationSort.FileSize; + public bool IsSortedByPath => SortMode == ApplicationSort.Path; public string SortName { get { - switch (SortMode) + return SortMode switch { - case ApplicationSort.Title: - return LocaleManager.Instance["GameListHeaderApplication"]; - case ApplicationSort.Developer: - return LocaleManager.Instance["GameListHeaderDeveloper"]; - case ApplicationSort.LastPlayed: - return LocaleManager.Instance["GameListHeaderLastPlayed"]; - case ApplicationSort.TotalTimePlayed: - return LocaleManager.Instance["GameListHeaderTimePlayed"]; - case ApplicationSort.FileType: - return LocaleManager.Instance["GameListHeaderFileExtension"]; - case ApplicationSort.FileSize: - return LocaleManager.Instance["GameListHeaderFileSize"]; - case ApplicationSort.Path: - return LocaleManager.Instance["GameListHeaderPath"]; - case ApplicationSort.Favorite: - return LocaleManager.Instance["CommonFavorite"]; - } - - return string.Empty; + ApplicationSort.Title => LocaleManager.Instance["GameListHeaderApplication"], + ApplicationSort.Developer => LocaleManager.Instance["GameListHeaderDeveloper"], + ApplicationSort.LastPlayed => LocaleManager.Instance["GameListHeaderLastPlayed"], + ApplicationSort.TotalTimePlayed => LocaleManager.Instance["GameListHeaderTimePlayed"], + ApplicationSort.FileType => LocaleManager.Instance["GameListHeaderFileExtension"], + ApplicationSort.FileSize => LocaleManager.Instance["GameListHeaderFileSize"], + ApplicationSort.Path => LocaleManager.Instance["GameListHeaderPath"], + ApplicationSort.Favorite => LocaleManager.Instance["CommonFavorite"], + _ => string.Empty, + }; } } @@ -668,6 +667,7 @@ namespace Ryujinx.Ava.Ui.ViewModels get => KeyGesture.Parse(_showUikey); set { _showUikey = value.ToString(); + OnPropertyChanged(); } } @@ -677,6 +677,7 @@ namespace Ryujinx.Ava.Ui.ViewModels get => KeyGesture.Parse(_screenshotkey); set { _screenshotkey = value.ToString(); + OnPropertyChanged(); } } @@ -686,14 +687,15 @@ namespace Ryujinx.Ava.Ui.ViewModels get => KeyGesture.Parse(_pauseKey); set { _pauseKey = value.ToString(); + OnPropertyChanged(); } } - public bool IsGridSmall => ConfigurationState.Instance.Ui.GridSize == 1; + public bool IsGridSmall => ConfigurationState.Instance.Ui.GridSize == 1; public bool IsGridMedium => ConfigurationState.Instance.Ui.GridSize == 2; - public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; - public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; + public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; + public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; public int GridSizeScale { @@ -728,14 +730,14 @@ namespace Ryujinx.Ava.Ui.ViewModels if (_owner.AppHost.Device.System.SearchingForAmiibo(out int deviceId)) { - string titleId = _owner.AppHost.Device.Application.TitleIdText.ToUpper(); + string titleId = _owner.AppHost.Device.Application.TitleIdText.ToUpper(); AmiiboWindow window = new(_showAll, _lastScannedAmiiboId, titleId); await window.ShowDialog(_owner); if (window.IsScanned) { - _showAll = window.ViewModel.ShowAllAmiibo; + _showAll = window.ViewModel.ShowAllAmiibo; _lastScannedAmiiboId = window.ScannedAmiibo.GetId(); _owner.AppHost.Device.System.ScanAmiibo(deviceId, _lastScannedAmiiboId, window.ViewModel.UseRandomUuid); @@ -766,8 +768,9 @@ namespace Ryujinx.Ava.Ui.ViewModels private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e) { - StatusBarProgressValue = e.NumAppsLoaded; + StatusBarProgressValue = e.NumAppsLoaded; StatusBarProgressMaximum = e.NumAppsFound; + LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", StatusBarProgressValue, StatusBarProgressMaximum); Dispatcher.UIThread.Post(() => @@ -792,9 +795,11 @@ namespace Ryujinx.Ava.Ui.ViewModels await Dispatcher.UIThread.InvokeAsync(() => { Applications.Clear(); + _owner.LoadProgressBar.IsVisible = true; - StatusBarProgressMaximum = 0; - StatusBarProgressValue = 0; + StatusBarProgressMaximum = 0; + StatusBarProgressValue = 0; + LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", 0, 0); }); @@ -842,12 +847,12 @@ namespace Ryujinx.Ava.Ui.ViewModels } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); string[] files = await dialog.ShowAsync(_owner); @@ -878,10 +883,12 @@ namespace Ryujinx.Ava.Ui.ViewModels { ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None); } + if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) { ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None); } + if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) { PauseKey = new KeyGesture(pauseKey, KeyModifiers.None); @@ -941,9 +948,7 @@ namespace Ryujinx.Ava.Ui.ViewModels _lastFullscreenToggle = Environment.TickCount64; - WindowState state = _owner.WindowState; - - if (state == WindowState.FullScreen) + if (_owner.WindowState == WindowState.FullScreen) { _owner.WindowState = WindowState.Normal; @@ -971,8 +976,7 @@ namespace Ryujinx.Ava.Ui.ViewModels { if (IsGameRunning) { - ConfigurationState.Instance.System.EnableDockedMode.Value = - !ConfigurationState.Instance.System.EnableDockedMode.Value; + ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; } } @@ -985,6 +989,7 @@ namespace Ryujinx.Ava.Ui.ViewModels else if (IsGameRunning) { await Task.Delay(100); + _owner.AppHost?.ShowExitPrompt(); } } @@ -994,6 +999,7 @@ namespace Ryujinx.Ava.Ui.ViewModels _owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager); await _owner.SettingsWindow.ShowDialog(_owner); + LoadConfigurableHotKeys(); } @@ -1004,9 +1010,7 @@ namespace Ryujinx.Ava.Ui.ViewModels public async void OpenAboutWindow() { - AboutWindow window = new(); - - await window.ShowDialog(_owner); + await new AboutWindow().ShowDialog(_owner); } public void ChangeLanguage(object obj) @@ -1020,7 +1024,7 @@ namespace Ryujinx.Ava.Ui.ViewModels try { ProgressMaximum = total; - ProgressValue = current; + ProgressValue = current; switch (state) { @@ -1030,13 +1034,13 @@ namespace Ryujinx.Ava.Ui.ViewModels { case PtcLoadingState.Start: case PtcLoadingState.Loading: - LoadHeading = LocaleManager.Instance["CompilingPPTC"]; + LoadHeading = LocaleManager.Instance["CompilingPPTC"]; IsLoadingIndeterminate = false; break; case PtcLoadingState.Loaded: - LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName); + LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName); IsLoadingIndeterminate = true; - CacheLoadStatus = ""; + CacheLoadStatus = ""; break; } break; @@ -1046,13 +1050,13 @@ namespace Ryujinx.Ava.Ui.ViewModels { case ShaderCacheLoadingState.Start: case ShaderCacheLoadingState.Loading: - LoadHeading = LocaleManager.Instance["CompilingShaders"]; + LoadHeading = LocaleManager.Instance["CompilingShaders"]; IsLoadingIndeterminate = false; break; case ShaderCacheLoadingState.Loaded: - LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName); + LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName); IsLoadingIndeterminate = true; - CacheLoadStatus = ""; + CacheLoadStatus = ""; break; } break; @@ -1065,14 +1069,12 @@ namespace Ryujinx.Ava.Ui.ViewModels public void OpenUserSaveDirectory() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { Task.Run(() => { - if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out ulong titleIdNumber)) + if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) { Dispatcher.UIThread.Post(async () => { @@ -1082,8 +1084,8 @@ namespace Ryujinx.Ava.Ui.ViewModels return; } - var userId = new LibHac.Fs.UserId((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low); - var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default); + UserId userId = new((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low); + SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default); OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); }); } @@ -1091,8 +1093,7 @@ namespace Ryujinx.Ava.Ui.ViewModels public void ToggleFavorite() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { selection.Favorite = !selection.Favorite; @@ -1108,11 +1109,10 @@ namespace Ryujinx.Ava.Ui.ViewModels public void OpenModsDirectory() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { - string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath(); + string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath(); string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId); OpenHelper.OpenFolder(titleModsPath); @@ -1121,12 +1121,12 @@ namespace Ryujinx.Ava.Ui.ViewModels public void OpenSdModsDirectory() { - var selection = SelectedApplication; + ApplicationData selection = SelectedApplication; if (selection != null) { string sdModsBasePath = _owner.VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId); + string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -1134,13 +1134,11 @@ namespace Ryujinx.Ava.Ui.ViewModels public void OpenPtcDirectory() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { - string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu"); - - string mainPath = Path.Combine(ptcDir, "0"); + string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu"); + string mainPath = Path.Combine(ptcDir, "0"); string backupPath = Path.Combine(ptcDir, "1"); if (!Directory.Exists(ptcDir)) @@ -1156,16 +1154,18 @@ namespace Ryujinx.Ava.Ui.ViewModels public async void PurgePtcCache() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { - DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0")); + DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0")); DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1")); // FIXME: Found a way to reproduce the bold effect on the title name (fork?). - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"], - string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]); + UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"], + string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName), + LocaleManager.Instance["InputDialogYes"], + LocaleManager.Instance["InputDialogNo"], + LocaleManager.Instance["RyujinxConfirm"]); List cacheFiles = new(); @@ -1198,8 +1198,7 @@ namespace Ryujinx.Ava.Ui.ViewModels public void OpenShaderCacheDirectory() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"); @@ -1220,18 +1219,20 @@ namespace Ryujinx.Ava.Ui.ViewModels public async void PurgeShaderCache() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader")); // FIXME: Found a way to reproduce the bold effect on the title name (fork?). UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"], - string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]); + string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName), + LocaleManager.Instance["InputDialogYes"], + LocaleManager.Instance["InputDialogNo"], + LocaleManager.Instance["RyujinxConfirm"]); - List oldCacheDirectories = new List(); - List newCacheFiles = new List(); + List oldCacheDirectories = new(); + List newCacheFiles = new(); if (shaderCacheDir.Exists) { @@ -1279,38 +1280,28 @@ namespace Ryujinx.Ava.Ui.ViewModels public async void OpenTitleUpdateManager() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { - TitleUpdateWindow titleUpdateManager = - new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName); - - await titleUpdateManager.ShowDialog(_owner); + await new TitleUpdateWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner); } } public async void OpenDownloadableContentManager() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { - DownloadableContentManagerWindow downloadableContentManager = new(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName); - - await downloadableContentManager.ShowDialog(_owner); + await new DownloadableContentManagerWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner); } } public async void OpenCheatManager() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { - CheatWindow cheatManager = new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName); - - await cheatManager.ShowDialog(_owner); + await new CheatWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner); } } @@ -1321,13 +1312,10 @@ namespace Ryujinx.Ava.Ui.ViewModels return; } - var application = _owner.AppHost.Device.Application; - + ApplicationLoader application = _owner.AppHost.Device.Application; if (application != null) { - CheatWindow cheatManager = new(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName); - - await cheatManager.ShowDialog(_owner); + await new CheatWindow(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(_owner); _owner.AppHost.Device.EnableCheats(); } @@ -1335,14 +1323,12 @@ namespace Ryujinx.Ava.Ui.ViewModels public void OpenDeviceSaveDirectory() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { Task.Run(() => { - if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out ulong titleIdNumber)) + if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) { Dispatcher.UIThread.Post(async () => { @@ -1360,14 +1346,12 @@ namespace Ryujinx.Ava.Ui.ViewModels public void OpenBcatSaveDirectory() { - var selection = SelectedApplication; - + ApplicationData selection = SelectedApplication; if (selection != null) { Task.Run(() => { - if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out ulong titleIdNumber)) + if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) { Dispatcher.UIThread.Post(async () => { @@ -1420,12 +1404,10 @@ namespace Ryujinx.Ava.Ui.ViewModels _owner.Close(); } - private async Task HandleFirmwareInstallation(string path) + private async Task HandleFirmwareInstallation(string filename) { try { - string filename = path; - SystemVersion firmwareVersion = _owner.ContentManager.VerifyFirmwarePackage(filename); if (firmwareVersion == null) @@ -1437,7 +1419,6 @@ namespace Ryujinx.Ava.Ui.ViewModels string dialogTitle = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallTitle"], firmwareVersion.VersionString); - SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion(); string dialogMessage = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallMessage"], firmwareVersion.VersionString); @@ -1480,11 +1461,12 @@ namespace Ryujinx.Ava.Ui.ViewModels string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSuccessMessage"], firmwareVersion.VersionString); await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]); + Logger.Info?.Print(LogClass.Application, message); // Purge Applet Cache. - DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); + DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); if (miiEditorCacheFolder.Exists) { @@ -1514,8 +1496,8 @@ namespace Ryujinx.Ava.Ui.ViewModels catch (LibHac.Common.Keys.MissingKeyException ex) { Logger.Error?.Print(LogClass.Application, ex.ToString()); - Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, _owner)); + + Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, _owner)); } catch (Exception ex) { @@ -1527,8 +1509,8 @@ namespace Ryujinx.Ava.Ui.ViewModels { OpenFileDialog dialog = new() { AllowMultiple = false }; dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance["FileDialogAllTypes"], Extensions = { "xci", "zip" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); string[] file = await dialog.ShowAsync(_owner); diff --git a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml index 068ea826a..0189c505e 100644 --- a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml @@ -3,89 +3,126 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" - SizeToContent="Height" - Width="600" MinHeight="500" Height="500" - WindowStartupLocation="CenterOwner" + Width="800" + Height="500" MinWidth="600" + MinHeight="500" + SizeToContent="Height" + WindowStartupLocation="CenterOwner" mc:Ignorable="d"> + - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs index 972ffbc3e..b1c86afcb 100644 --- a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs @@ -8,6 +8,7 @@ using LibHac.FsSystem; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using LibHac.Tools.FsSystem.Save; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Models; @@ -16,8 +17,11 @@ using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; using System.IO; using System.Linq; +using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; using Path = System.IO.Path; @@ -27,14 +31,13 @@ namespace Ryujinx.Ava.Ui.Windows public partial class DownloadableContentManagerWindow : StyleableWindow { private readonly List _downloadableContentContainerList; - private readonly string _downloadableContentJsonPath; + private readonly string _downloadableContentJsonPath; - public VirtualFileSystem VirtualFileSystem { get; } - public AvaloniaList DownloadableContents { get; set; } = new AvaloniaList(); - public ulong TitleId { get; } - public string TitleName { get; } + private VirtualFileSystem _virtualFileSystem { get; } + private AvaloniaList _downloadableContents { get; set; } - public string Heading => string.Format(LocaleManager.Instance["DlcWindowHeading"], TitleName, TitleId.ToString("X16")); + private ulong TitleId { get; } + private string TitleName { get; } public DownloadableContentManagerWindow() { @@ -42,14 +45,15 @@ namespace Ryujinx.Ava.Ui.Windows InitializeComponent(); - Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"]; + Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})"; } public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - VirtualFileSystem = virtualFileSystem; - TitleId = titleId; - TitleName = titleName; + _virtualFileSystem = virtualFileSystem; + _downloadableContents = new AvaloniaList(); + TitleId = titleId; + TitleName = titleName; _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); @@ -66,9 +70,24 @@ namespace Ryujinx.Ava.Ui.Windows InitializeComponent(); - Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"]; + RemoveButton.IsEnabled = false; + + DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged; + + Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})"; LoadDownloadableContents(); + PrintHeading(); + } + + private void DlcDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + RemoveButton.IsEnabled = (DlcDataGrid.SelectedItems.Count > 0); + } + + private void PrintHeading() + { + Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, TitleName, TitleId.ToString("X16")); } private void LoadDownloadableContents() @@ -79,23 +98,23 @@ namespace Ryujinx.Ava.Ui.Windows { using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); - PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); + PartitionFileSystem pfs = new(containerFile.AsStorage()); - VirtualFileSystem.ImportTickets(pfs); + _virtualFileSystem.ImportTickets(pfs); foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) { - using var ncaFile = new UniqueRef(); + using UniqueRef ncaFile = new(); pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); + Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); if (nca != null) { - DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), - downloadableContentContainer.ContainerPath, - downloadableContentNca.FullPath, - downloadableContentNca.Enabled)); + _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), + downloadableContentContainer.ContainerPath, + downloadableContentNca.FullPath, + downloadableContentNca.Enabled)); } } } @@ -105,11 +124,11 @@ namespace Ryujinx.Ava.Ui.Windows Save(); } - private Nca TryCreateNca(IStorage ncaStorage, string containerPath) + private Nca TryOpenNca(IStorage ncaStorage, string containerPath) { try { - return new Nca(VirtualFileSystem.KeySet, ncaStorage); + return new Nca(_virtualFileSystem.KeySet, ncaStorage); } catch (Exception ex) { @@ -124,61 +143,73 @@ namespace Ryujinx.Ava.Ui.Windows private async Task AddDownloadableContent(string path) { - if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) + if (!File.Exists(path) || _downloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) { return; } - using (FileStream containerFile = File.OpenRead(path)) + using FileStream containerFile = File.OpenRead(path); + + PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); + bool containsDownloadableContent = false; + + _virtualFileSystem.ImportTickets(partitionFileSystem); + + foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) { - PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); - bool containsDownloadableContent = false; + using var ncaFile = new UniqueRef(); - VirtualFileSystem.ImportTickets(pfs); + partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path); + if (nca == null) { - using var ncaFile = new UniqueRef(); - - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path); - - if (nca == null) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.PublicData) - { - if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId) - { - break; - } - - DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true)); - - containsDownloadableContent = true; - } + continue; } - if (!containsDownloadableContent) + if (nca.Header.ContentType == NcaContentType.PublicData) { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]); + if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId) + { + break; + } + + _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true)); + + containsDownloadableContent = true; } } + + if (!containsDownloadableContent) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]); + } } private void RemoveDownloadableContents(bool removeSelectedOnly = false) { if (removeSelectedOnly) { - DownloadableContents.RemoveAll(DownloadableContents.Where(x => x.Enabled).ToList()); + AvaloniaList removedItems = new(); + + foreach (var item in DlcDataGrid.SelectedItems) + { + removedItems.Add(item as DownloadableContentModel); + } + + DlcDataGrid.SelectedItems.Clear(); + + foreach (var item in removedItems) + { + _downloadableContents.RemoveAll(_downloadableContents.Where(x => x.TitleId == item.TitleId).ToList()); + } } else { - DownloadableContents.Clear(); + _downloadableContents.Clear(); } + + PrintHeading(); } public void RemoveSelected() @@ -191,6 +222,22 @@ namespace Ryujinx.Ava.Ui.Windows RemoveDownloadableContents(); } + public void EnableAll() + { + foreach(var item in _downloadableContents) + { + item.Enabled = true; + } + } + + public void DisableAll() + { + foreach (var item in _downloadableContents) + { + item.Enabled = false; + } + } + public async void Add() { OpenFileDialog dialog = new OpenFileDialog() @@ -214,6 +261,8 @@ namespace Ryujinx.Ava.Ui.Windows await AddDownloadableContent(file); } } + + PrintHeading(); } public void Save() @@ -222,7 +271,7 @@ namespace Ryujinx.Ava.Ui.Windows DownloadableContentContainer container = default; - foreach (DownloadableContentModel downloadableContent in DownloadableContents) + foreach (DownloadableContentModel downloadableContent in _downloadableContents) { if (container.ContainerPath != downloadableContent.ContainerPath) { diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs index be4517c86..e874982ab 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs @@ -90,8 +90,8 @@ namespace Ryujinx.Ava.Ui.Windows Title = $"Ryujinx {Program.Version}"; - Height = Height / Program.WindowScaleFactor; - Width = Width / Program.WindowScaleFactor; + Height /= Program.WindowScaleFactor; + Width /= Program.WindowScaleFactor; if (Program.PreviewerDetached) { @@ -523,23 +523,20 @@ namespace Ryujinx.Ava.Ui.Windows public static void UpdateGraphicsConfig() { - int resScale = ConfigurationState.Instance.Graphics.ResScale; - float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom; - - GraphicsConfig.ResScale = resScale == -1 ? resScaleCustom : resScale; - GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; - GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; - GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; + GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale; + GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; + GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; + GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; - GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; + GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; } public void LoadHotKeys() { - HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); + HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11)); - HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); - HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); + HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); + HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); } public static void SaveConfig() diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml index 0946a2eb9..1791d8ea5 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml @@ -75,7 +75,7 @@ Spacing="10"> diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs index 810beb3e0..0e610d77b 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone; namespace Ryujinx.Ava.Ui.Windows @@ -31,7 +30,7 @@ namespace Ryujinx.Ava.Ui.Windows { Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["Settings"]}"; - ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this); + ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this); DataContext = ViewModel; InitializeComponent(); @@ -48,7 +47,7 @@ namespace Ryujinx.Ava.Ui.Windows public SettingsWindow() { - ViewModel = new SettingsViewModel(); + ViewModel = new SettingsViewModel(); DataContext = ViewModel; InitializeComponent(); @@ -79,7 +78,7 @@ namespace Ryujinx.Ava.Ui.Windows PointerPressed += MouseClick; - IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad(ViewModel.AvaloniaKeyboardDriver.GamepadsIds[0]); + IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad(ViewModel.AvaloniaKeyboardDriver.GamepadsIds[0]); IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard); _currentAssigner.GetInputAndAssign(assigner); @@ -92,6 +91,7 @@ namespace Ryujinx.Ava.Ui.Windows _currentAssigner.Cancel(); _currentAssigner = null; + button.IsChecked = false; } } @@ -122,36 +122,19 @@ namespace Ryujinx.Ava.Ui.Windows { if (e.SelectedItem is NavigationViewItem navitem) { - switch (navitem.Tag.ToString()) + NavPanel.Content = navitem.Tag.ToString() switch { - case "UiPage": - NavPanel.Content = UiPage; - break; - case "InputPage": - NavPanel.Content = InputPage; - break; - case "HotkeysPage": - NavPanel.Content = HotkeysPage; - break; - case "SystemPage": - NavPanel.Content = SystemPage; - break; - case "CpuPage": - NavPanel.Content = CpuPage; - break; - case "GraphicsPage": - NavPanel.Content = GraphicsPage; - break; - case "AudioPage": - NavPanel.Content = AudioPage; - break; - case "NetworkPage": - NavPanel.Content = NetworkPage; - break; - case "LoggingPage": - NavPanel.Content = LoggingPage; - break; - } + "UiPage" => UiPage, + "InputPage" => InputPage, + "HotkeysPage" => HotkeysPage, + "SystemPage" => SystemPage, + "CpuPage" => CpuPage, + "GraphicsPage" => GraphicsPage, + "AudioPage" => AudioPage, + "NetworkPage" => NetworkPage, + "LoggingPage" => LoggingPage, + _ => throw new NotImplementedException() + }; } } @@ -178,13 +161,18 @@ namespace Ryujinx.Ava.Ui.Windows private void RemoveButton_OnClick(object sender, RoutedEventArgs e) { - List selected = new(GameList.SelectedItems.Cast()); + int oldIndex = GameList.SelectedIndex; - foreach (string path in selected) + foreach (string path in new List(GameList.SelectedItems.Cast())) { ViewModel.GameDirectories.Remove(path); ViewModel.DirectoryChanged = true; } + + if (GameList.ItemCount > 0) + { + GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0; + } } private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) @@ -214,7 +202,6 @@ namespace Ryujinx.Ava.Ui.Windows private void SaveButton_Clicked(object sender, RoutedEventArgs e) { SaveSettings(); - Close(); } @@ -232,7 +219,6 @@ namespace Ryujinx.Ava.Ui.Windows private void SaveSettings() { ViewModel.SaveSettings(); - ControllerSettings?.SaveCurrentProfile(); if (Owner is MainWindow window && ViewModel.DirectoryChanged) @@ -246,8 +232,10 @@ namespace Ryujinx.Ava.Ui.Windows protected override void OnClosed(EventArgs e) { ControllerSettings.Dispose(); + _currentAssigner?.Cancel(); _currentAssigner = null; + base.OnClosed(e); } } diff --git a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs index b4d5d5b5d..751c7afc6 100644 --- a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs @@ -131,6 +131,13 @@ namespace Ryujinx.Ava.Ui.Windows nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); TitleUpdates.Add(new TitleUpdateModel(controlData, path)); + + foreach (var update in TitleUpdates) + { + update.IsEnabled = false; + } + + TitleUpdates.Last().IsEnabled = true; } else { From 5fb5079730e249d426f3f1afb45c4519708dfe4b Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Fri, 25 Nov 2022 14:27:41 +0100 Subject: [PATCH 111/737] chore: Update Avalonia related dependencies (#3891) --- Ryujinx.Ava/Ryujinx.Ava.csproj | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 52297ceee..c851b4513 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -19,16 +19,16 @@ - - - - - - + + + + + + - - - + + + From 476b4683cf428cddef3abbcf256ab7a931b9977c Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 25 Nov 2022 14:39:03 +0000 Subject: [PATCH 112/737] Fix CB0 alignment with addresses used for 8/16-bit LDG/STG (#3897) This replacement is meant to be done with the original identified byteOffset, not the one assigned later on by the below conditionals (that already has the constant offset added, for instance). This fixes videos being pixelated in Xenoblade 3, and other regressions that might have happened since #3847. --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Translation/Optimizations/GlobalToStorage.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index a91ab9e4f..1d4842aad 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3868; + private const uint CodeGenVersion = 3897; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 25c0eb25b..7aabcc9e6 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -128,6 +128,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) : (null, 0); + if (byteOffset != null) + { + ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset); + } + if (byteOffset == null) { Operand baseAddrLow = Cbuf(0, baseAddressCbOffset); @@ -156,11 +161,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations byteOffset = offset; } - if (byteOffset != null) - { - ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset); - } - if (isStg16Or8) { return byteOffset; From 5d3ef7761b9df5ea0db5bdff1eeb7a0e4a80c4fe Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 25 Nov 2022 17:55:08 +0100 Subject: [PATCH 113/737] ava: Refactor Title Update Manager window (#3898) * ava: Refactor TitleUpdate Manager window * Update locale --- Ryujinx.Ava/Assets/Locales/en_US.json | 10 +- .../Ui/ViewModels/MainWindowViewModel.cs | 2 +- .../DownloadableContentManagerWindow.axaml | 4 +- .../DownloadableContentManagerWindow.axaml.cs | 26 ++-- .../Ui/Windows/TitleUpdateWindow.axaml | 36 ++++-- .../Ui/Windows/TitleUpdateWindow.axaml.cs | 117 +++++++++--------- 6 files changed, 105 insertions(+), 90 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index e98988b7c..86cb44c9a 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -564,10 +564,10 @@ "Writable": "Writable", "SelectDlcDialogTitle": "Select DLC files", "SelectUpdateDialogTitle": "Select update files", - "UserProfileWindowTitle": "Manage User Profiles", - "CheatWindowTitle": "Manage Game Cheats", - "DlcWindowTitle": "Manage Game DLC", - "UpdateWindowTitle": "Manage Game Updates", + "UserProfileWindowTitle": "User Profiles Manager", + "CheatWindowTitle": "Cheats Manager", + "DlcWindowTitle": "Downloadable Content Manager", + "UpdateWindowTitle": "Title Update Manager", "CheatWindowHeading": "Cheats Available for {0} [{1}]", "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})", "UserProfilesEditProfile": "Edit Selected", @@ -577,7 +577,7 @@ "UserProfilesSetProfileImage": "Set Profile Image", "UserProfileEmptyNameError": "Name is required", "UserProfileNoImageError": "Profile image must be set", - "GameUpdateWindowHeading": "Updates Available for {0} [{1}]", + "GameUpdateWindowHeading": "{0} Update(s) available for {1} ({2})", "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:", "UserProfilesName": "Name:", diff --git a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs index d5d3b7609..cd4370172 100644 --- a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs @@ -1283,7 +1283,7 @@ namespace Ryujinx.Ava.Ui.ViewModels ApplicationData selection = SelectedApplication; if (selection != null) { - await new TitleUpdateWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner); + await new TitleUpdateWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner); } } diff --git a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml index 0189c505e..2e3fd05bb 100644 --- a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml @@ -8,8 +8,10 @@ xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" Width="800" Height="500" - MinWidth="600" + MinWidth="800" MinHeight="500" + MaxWidth="800" + MaxHeight="500" SizeToContent="Height" WindowStartupLocation="CenterOwner" mc:Ignorable="d"> diff --git a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs index b1c86afcb..e68214eb3 100644 --- a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs @@ -8,7 +8,6 @@ using LibHac.FsSystem; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; -using LibHac.Tools.FsSystem.Save; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Models; @@ -17,8 +16,6 @@ using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.IO; using System.Linq; using System.Reactive.Linq; @@ -36,8 +33,8 @@ namespace Ryujinx.Ava.Ui.Windows private VirtualFileSystem _virtualFileSystem { get; } private AvaloniaList _downloadableContents { get; set; } - private ulong TitleId { get; } - private string TitleName { get; } + private ulong _titleId { get; } + private string _titleName { get; } public DownloadableContentManagerWindow() { @@ -45,15 +42,16 @@ namespace Ryujinx.Ava.Ui.Windows InitializeComponent(); - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})"; + Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})"; } public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { _virtualFileSystem = virtualFileSystem; _downloadableContents = new AvaloniaList(); - TitleId = titleId; - TitleName = titleName; + + _titleId = titleId; + _titleName = titleName; _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); @@ -74,7 +72,7 @@ namespace Ryujinx.Ava.Ui.Windows DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged; - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})"; + Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})"; LoadDownloadableContents(); PrintHeading(); @@ -87,7 +85,7 @@ namespace Ryujinx.Ava.Ui.Windows private void PrintHeading() { - Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, TitleName, TitleId.ToString("X16")); + Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, _titleName, _titleId.ToString("X16")); } private void LoadDownloadableContents() @@ -98,15 +96,15 @@ namespace Ryujinx.Ava.Ui.Windows { using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); - PartitionFileSystem pfs = new(containerFile.AsStorage()); + PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); - _virtualFileSystem.ImportTickets(pfs); + _virtualFileSystem.ImportTickets(partitionFileSystem); foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) { using UniqueRef ncaFile = new(); - pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); if (nca != null) @@ -169,7 +167,7 @@ namespace Ryujinx.Ava.Ui.Windows if (nca.Header.ContentType == NcaContentType.PublicData) { - if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId) + if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId) { break; } diff --git a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml index 347c2cf52..dd690a55c 100644 --- a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml @@ -3,13 +3,17 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" - SizeToContent="Height" - Width="600" MinHeight="500" Height="500" - WindowStartupLocation="CenterOwner" + Width="600" + Height="400" MinWidth="600" + MinHeight="400" + MaxWidth="600" + MaxHeight="400" + SizeToContent="Height" + WindowStartupLocation="CenterOwner" mc:Ignorable="d"> @@ -19,15 +23,15 @@ + TextAlignment="Center" + TextWrapping="Wrap" /> @@ -45,11 +47,19 @@ Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - Items="{Binding TitleUpdates}"> + Items="{Binding _titleUpdates}"> - - diff --git a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs index 751c7afc6..1cc820d82 100644 --- a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs @@ -30,13 +30,11 @@ namespace Ryujinx.Ava.Ui.Windows private readonly string _titleUpdateJsonPath; private TitleUpdateMetadata _titleUpdateWindowData; - public VirtualFileSystem VirtualFileSystem { get; } + private VirtualFileSystem _virtualFileSystem { get; } + private AvaloniaList _titleUpdates { get; set; } - internal AvaloniaList TitleUpdates { get; set; } = new AvaloniaList(); - public string TitleId { get; } - public string TitleName { get; } - - public string Heading => string.Format(LocaleManager.Instance["GameUpdateWindowHeading"], TitleName, TitleId.ToUpper()); + private ulong _titleId { get; } + private string _titleName { get; } public TitleUpdateWindow() { @@ -44,16 +42,18 @@ namespace Ryujinx.Ava.Ui.Windows InitializeComponent(); - Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"]; + Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})"; } - public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) + public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - VirtualFileSystem = virtualFileSystem; - TitleId = titleId; - TitleName = titleName; + _virtualFileSystem = virtualFileSystem; + _titleUpdates = new AvaloniaList(); - _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId, "updates.json"); + _titleId = titleId; + _titleName = titleName; + + _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); try { @@ -64,7 +64,7 @@ namespace Ryujinx.Ava.Ui.Windows _titleUpdateWindowData = new TitleUpdateMetadata { Selected = "", - Paths = new List() + Paths = new List() }; } @@ -72,14 +72,20 @@ namespace Ryujinx.Ava.Ui.Windows InitializeComponent(); - Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"]; + Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})"; LoadUpdates(); + PrintHeading(); + } + + private void PrintHeading() + { + Heading.Text = string.Format(LocaleManager.Instance["GameUpdateWindowHeading"], _titleUpdates.Count, _titleName, _titleId.ToString("X16")); } private void LoadUpdates() { - TitleUpdates.Add(new TitleUpdateModel(default, string.Empty, true)); + _titleUpdates.Add(new TitleUpdateModel(default, string.Empty, true)); foreach (string path in _titleUpdateWindowData.Paths) { @@ -88,12 +94,12 @@ namespace Ryujinx.Ava.Ui.Windows if (_titleUpdateWindowData.Selected == "") { - TitleUpdates[0].IsEnabled = true; + _titleUpdates[0].IsEnabled = true; } else { - TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected); - List enabled = TitleUpdates.Where(x => x.IsEnabled).ToList(); + TitleUpdateModel selected = _titleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected); + List enabled = _titleUpdates.Where(x => x.IsEnabled).ToList(); foreach (TitleUpdateModel update in enabled) { @@ -111,50 +117,47 @@ namespace Ryujinx.Ava.Ui.Windows private void AddUpdate(string path) { - if (File.Exists(path) && !TitleUpdates.Any(x => x.Path == path)) + if (File.Exists(path) && !_titleUpdates.Any(x => x.Path == path)) { - using (FileStream file = new(path, FileMode.Open, FileAccess.Read)) + using FileStream file = new(path, FileMode.Open, FileAccess.Read); + + try { - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); + (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); - try + if (controlNca != null && patchNca != null) { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(VirtualFileSystem, nsp, TitleId, 0); + ApplicationControlProperty controlData = new(); - if (controlNca != null && patchNca != null) + using UniqueRef nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + _titleUpdates.Add(new TitleUpdateModel(controlData, path)); + + foreach (var update in _titleUpdates) { - ApplicationControlProperty controlData = new ApplicationControlProperty(); - - using var nacpFile = new UniqueRef(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - TitleUpdates.Add(new TitleUpdateModel(controlData, path)); - - foreach (var update in TitleUpdates) - { - update.IsEnabled = false; - } - - TitleUpdates.Last().IsEnabled = true; - } - else - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdateAddUpdateErrorMessage"]); - }); + update.IsEnabled = false; } + + _titleUpdates.Last().IsEnabled = true; } - catch (Exception ex) + else { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, path)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdateAddUpdateErrorMessage"]); }); } } + catch (Exception ex) + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, path)); + }); + } } } @@ -162,16 +165,17 @@ namespace Ryujinx.Ava.Ui.Windows { if (removeSelectedOnly) { - TitleUpdates.RemoveAll(TitleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList()); + _titleUpdates.RemoveAll(_titleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList()); } else { - TitleUpdates.RemoveAll(TitleUpdates.Where(x => !x.IsNoUpdate).ToList()); + _titleUpdates.RemoveAll(_titleUpdates.Where(x => !x.IsNoUpdate).ToList()); } - TitleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true; + _titleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true; SortUpdates(); + PrintHeading(); } public void RemoveSelected() @@ -186,7 +190,7 @@ namespace Ryujinx.Ava.Ui.Windows public async void Add() { - OpenFileDialog dialog = new OpenFileDialog() + OpenFileDialog dialog = new() { Title = LocaleManager.Instance["SelectUpdateDialogTitle"], AllowMultiple = true @@ -209,11 +213,12 @@ namespace Ryujinx.Ava.Ui.Windows } SortUpdates(); + PrintHeading(); } private void SortUpdates() { - var list = TitleUpdates.ToList(); + var list = _titleUpdates.ToList(); list.Sort((first, second) => { @@ -229,8 +234,8 @@ namespace Ryujinx.Ava.Ui.Windows return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; }); - TitleUpdates.Clear(); - TitleUpdates.AddRange(list); + _titleUpdates.Clear(); + _titleUpdates.AddRange(list); } public void Save() @@ -239,7 +244,7 @@ namespace Ryujinx.Ava.Ui.Windows _titleUpdateWindowData.Selected = ""; - foreach (TitleUpdateModel update in TitleUpdates) + foreach (TitleUpdateModel update in _titleUpdates) { _titleUpdateWindowData.Paths.Add(update.Path); From 70f2da8fdf313c22ffcb72d492cc7c865d6cb77e Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 25 Nov 2022 18:40:44 +0100 Subject: [PATCH 114/737] ava: Fix invisible vulkan window on Linux (#3901) Co-authored-by: emmauss Co-authored-by: emmauss --- Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs | 6 ++++-- Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs index d9fae93a5..f60ee7e00 100644 --- a/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs +++ b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs @@ -6,8 +6,8 @@ using SPB.Graphics; using SPB.Platform; using SPB.Platform.GLX; using SPB.Platform.X11; +using SPB.Windowing; using System; -using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading.Tasks; @@ -96,12 +96,14 @@ namespace Ryujinx.Ava.Ui.Controls [SupportedOSPlatform("linux")] IPlatformHandle CreateLinux(IPlatformHandle parent) { - X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow; + X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(parent.Handle)); WindowHandle = X11Window.WindowHandle.RawHandle; X11Display = X11Window.DisplayHandle.RawHandle; + X11Window.Hide(); + return new PlatformHandle(WindowHandle, "X11"); } diff --git a/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs index d2c980ddf..4954c882a 100644 --- a/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs +++ b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Ava.Ui } else if (OperatingSystem.IsLinux()) { - _window = X11Window; + _window = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle)); } else { From 6f0f99ee2b844284d00da9ef8533acfd96de08fd Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 26 Nov 2022 12:06:53 +0100 Subject: [PATCH 115/737] Avalonia: Fix OpenGL crashing on Linux (#3902) * ava: Fix OpenGL crashing on Linux Fixes a regression from #3901 * Fix formatting --- Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs | 18 +++++++----------- .../Ui/Controls/VulkanEmbeddedWindow.cs | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs index f60ee7e00..7acbefca5 100644 --- a/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs +++ b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs @@ -15,12 +15,12 @@ using static Ryujinx.Ava.Ui.Controls.Win32NativeInterop; namespace Ryujinx.Ava.Ui.Controls { - public unsafe class EmbeddedWindow : NativeControlHost + public class EmbeddedWindow : NativeControlHost { private WindowProc _wndProcDelegate; private string _className; - protected GLXWindow X11Window { get; private set; } + protected GLXWindow X11Window { get; set; } protected IntPtr WindowHandle { get; set; } protected IntPtr X11Display { get; set; } @@ -94,21 +94,17 @@ namespace Ryujinx.Ava.Ui.Controls } [SupportedOSPlatform("linux")] - IPlatformHandle CreateLinux(IPlatformHandle parent) + protected virtual IPlatformHandle CreateLinux(IPlatformHandle parent) { - X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(parent.Handle)); - + X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow; WindowHandle = X11Window.WindowHandle.RawHandle; - - X11Display = X11Window.DisplayHandle.RawHandle; - - X11Window.Hide(); + X11Display = X11Window.DisplayHandle.RawHandle; return new PlatformHandle(WindowHandle, "X11"); } [SupportedOSPlatform("windows")] - unsafe IPlatformHandle CreateWin32(IPlatformHandle parent) + IPlatformHandle CreateWin32(IPlatformHandle parent) { _className = "NativeWindow-" + Guid.NewGuid(); _wndProcDelegate = WndProc; @@ -144,7 +140,7 @@ namespace Ryujinx.Ava.Ui.Controls } [SupportedOSPlatform("windows")] - internal IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam) + IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam) { var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF); var root = VisualRoot as Window; diff --git a/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs index 4954c882a..236a0a166 100644 --- a/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs +++ b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs @@ -1,10 +1,13 @@ +using Avalonia.Platform; using Ryujinx.Ava.Ui.Controls; using Silk.NET.Vulkan; using SPB.Graphics.Vulkan; +using SPB.Platform.GLX; using SPB.Platform.Win32; using SPB.Platform.X11; using SPB.Windowing; using System; +using System.Runtime.Versioning; namespace Ryujinx.Ava.Ui { @@ -12,6 +15,18 @@ namespace Ryujinx.Ava.Ui { private NativeWindowBase _window; + [SupportedOSPlatform("linux")] + protected override IPlatformHandle CreateLinux(IPlatformHandle parent) + { + X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(parent.Handle)); + WindowHandle = X11Window.WindowHandle.RawHandle; + X11Display = X11Window.DisplayHandle.RawHandle; + + X11Window.Hide(); + + return new PlatformHandle(WindowHandle, "X11"); + } + public SurfaceKHR CreateSurface(Instance instance) { if (OperatingSystem.IsWindows()) From cb22629ac1048960e4f318dedc43ba498debb9b3 Mon Sep 17 00:00:00 2001 From: Luminoso-256 <63971285+Luminoso-256@users.noreply.github.com> Date: Sat, 26 Nov 2022 19:10:42 -0600 Subject: [PATCH 116/737] HLE: fix small issue in IPsmSession (#3909) --- Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs index de40a5ac1..6015c6a4b 100644 --- a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs +++ b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm { if (_stateChangeEventHandle == -1) { - KernelResult resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out int stateChangeEventHandle); + KernelResult resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle); if (resultCode != KernelResult.Success) { From 18b61aff59783e0e9dd01c2d45c44406a47b82a9 Mon Sep 17 00:00:00 2001 From: &Olga <462484+andOlga@users.noreply.github.com> Date: Mon, 28 Nov 2022 00:11:51 +0400 Subject: [PATCH 117/737] Unbreak bug_report.md (#3915) * Unbreak bug_report.md * Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 574877570..9c0b18c54 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,6 @@ --- name: Bug Report -about: Something doesn't work correctly in Ryujinx. Note that game-specific issues should be instead posted on the Game Compatibility List at https://github.com/Ryujinx/Ryujinx-Games-List, unless it is a provable regression. +about: Something doesn't work correctly in Ryujinx. Game-specific issues should be posted at https://github.com/Ryujinx/Ryujinx-Games-List instead, unless it is a provable regression. #assignees: --- From 1865ea87e538047efaf36c7a707c30390d620496 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Sun, 27 Nov 2022 21:18:05 +0100 Subject: [PATCH 118/737] bsd: Fix eventfd broken logic (#3647) * bsd: Fix eventfd broken logic This commit fix eventfd logic being broken. The following changes were made: - EventFd IPC definition had argument inverted - EventFd events weren't fired correctly - Poll logic was wrong and unfinished for eventfd - Reintroduce workaround from #3385 but in a safer way, and spawn 4 threads. * ipc: Rework a bit for multithreads * Clean up debug logs * Make server thread yield when managed lock isn't availaible * Fix replyTargetHandle not being added in the proper locking scope * Simplify some scopes * Address gdkchan's comments * Revert IPC workaround for now * Reintroduce the EventFileDescriptor workaround --- .../HOS/Services/Sockets/Bsd/IClient.cs | 10 ++++- .../Sockets/Bsd/Impl/EventFileDescriptor.cs | 40 ++++++++++++++----- .../Impl/EventFileDescriptorPollManager.cs | 27 ++++++++++--- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs index 98a993119..ece5375b9 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -315,6 +315,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } } + if (updateCount > 0) + { + break; + } + // If we are here, that mean nothing was availaible, sleep for 50ms context.Device.System.KernelContext.Syscall.SleepThread(50 * 1000000); } @@ -972,11 +977,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } [CommandHipc(31)] // 7.0.0+ - // EventFd(u64 initval, nn::socket::EventFdFlags flags) -> (i32 ret, u32 bsd_errno) + // EventFd(nn::socket::EventFdFlags flags, u64 initval) -> (i32 ret, u32 bsd_errno) public ResultCode EventFd(ServiceCtx context) { - ulong initialValue = context.RequestData.ReadUInt64(); EventFdFlags flags = (EventFdFlags)context.RequestData.ReadUInt32(); + context.RequestData.BaseStream.Position += 4; // Padding + ulong initialValue = context.RequestData.ReadUInt64(); EventFileDescriptor newEventFile = new EventFileDescriptor(initialValue, flags); diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs index 239e2434c..f84e9b93b 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs @@ -26,8 +26,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd _value = value; _flags = flags; - WriteEvent = new ManualResetEvent(true); - ReadEvent = new ManualResetEvent(true); + WriteEvent = new ManualResetEvent(false); + ReadEvent = new ManualResetEvent(false); + UpdateEventStates(); } public int Refcount { get; set; } @@ -38,6 +39,25 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd ReadEvent.Dispose(); } + private void ResetEventStates() + { + WriteEvent.Reset(); + ReadEvent.Reset(); + } + + private void UpdateEventStates() + { + if (_value > 0) + { + ReadEvent.Set(); + } + + if (_value != uint.MaxValue - 1) + { + WriteEvent.Set(); + } + } + public LinuxError Read(out int readSize, Span buffer) { if (buffer.Length < sizeof(ulong)) @@ -47,10 +67,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return LinuxError.EINVAL; } - ReadEvent.Reset(); - lock (_lock) { + ResetEventStates(); + ref ulong count = ref MemoryMarshal.Cast(buffer)[0]; if (_value == 0) @@ -66,6 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { readSize = 0; + UpdateEventStates(); return LinuxError.EAGAIN; } } @@ -85,8 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd _value = 0; } - ReadEvent.Set(); - + UpdateEventStates(); return LinuxError.SUCCESS; } } @@ -100,10 +120,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return LinuxError.EINVAL; } - WriteEvent.Reset(); - lock (_lock) { + ResetEventStates(); + if (_value > _value + count) { if (Blocking) @@ -114,6 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { writeSize = 0; + UpdateEventStates(); return LinuxError.EAGAIN; } } @@ -123,8 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd _value += count; Monitor.Pulse(_lock); - WriteEvent.Set(); - + UpdateEventStates(); return LinuxError.SUCCESS; } } diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs index 8bd9652bf..6501d1117 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs @@ -68,20 +68,37 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { for (int i = 0; i < events.Count; i++) { + PollEventTypeMask outputEvents = 0; + PollEvent evnt = events[i]; EventFileDescriptor socket = (EventFileDescriptor)evnt.FileDescriptor; - if ((evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Input) || - evnt.Data.InputEvents.HasFlag(PollEventTypeMask.UrgentInput)) - && socket.ReadEvent.WaitOne(0)) + if (socket.ReadEvent.WaitOne(0)) { - waiters.Add(socket.ReadEvent); + if (evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Input)) + { + outputEvents |= PollEventTypeMask.Input; + } + + if (evnt.Data.InputEvents.HasFlag(PollEventTypeMask.UrgentInput)) + { + outputEvents |= PollEventTypeMask.UrgentInput; + } } + if ((evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Output)) && socket.WriteEvent.WaitOne(0)) { - waiters.Add(socket.WriteEvent); + outputEvents |= PollEventTypeMask.Output; + } + + + if (outputEvents != 0) + { + evnt.Data.OutputEvents = outputEvents; + + updatedCount++; } } } From 472119c8da7edaf7bf60fa75e87812e5cb16e33b Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 28 Nov 2022 02:53:57 +0100 Subject: [PATCH 119/737] sfdnsres; Fix deserializer of AddrInfoSerialized when addresses are empty (#3924) --- Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs | 8 ++++---- .../HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs | 1 + .../Sockets/Sfdnsres/Types/AddrInfoSerialized.cs | 9 ++++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs index dd45e9ea6..80339b4ac 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs @@ -566,7 +566,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres private static List DeserializeAddrInfos(IVirtualMemoryManager memory, ulong address, ulong size) { - List result = new List(); + List result = new(); ReadOnlySpan data = memory.GetSpan(address, (int)size); @@ -606,9 +606,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres } // NOTE: 0 = Any - AddrInfoSerializedHeader header = new AddrInfoSerializedHeader(ip, 0); - AddrInfo4 addr = new AddrInfo4(ip, (short)port); - AddrInfoSerialized info = new AddrInfoSerialized(header, addr, null, hostEntry.HostName); + AddrInfoSerializedHeader header = new(ip, 0); + AddrInfo4 addr = new(ip, (short)port); + AddrInfoSerialized info = new(header, addr, null, hostEntry.HostName); data = info.Write(data); } diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs index 19b570e49..0a20e0572 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs @@ -14,6 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types public byte Family; public short Port; public Array4 Address; + public Array8 Padding; public AddrInfo4(IPAddress address, short port) { diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs index 470123967..a0613d7bc 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types AddrInfo4? socketAddress = null; Array4? rawIPv4Address = null; - string canonicalName = null; + string canonicalName; buffer = buffer[Unsafe.SizeOf()..]; @@ -50,6 +50,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types Debug.Assert(header.Magic == SfdnsresContants.AddrInfoMagic); + if (header.AddressLength == 0) + { + rest = buffer; + + return null; + } + if (header.Family == (int)AddressFamily.InterNetwork) { socketAddress = MemoryMarshal.Read(buffer); From dff138229c79483c189be6f3829ed88a5f95575d Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Mon, 28 Nov 2022 08:28:45 +0100 Subject: [PATCH 120/737] amadeus: Fixes and initial 15.0.0 support (#3908) * amadeus: Allow OOB read of GC-ADPCM coefficients Fixes "Ninja Gaiden Sigma 2" and possibly "NINJA GAIDEN 3: Razor's Edge" * amadeus: Fix wrong variable usage in delay effect We should transform the delay line values, not the input. * amadeus: Update GroupedBiquadFilterCommand documentation * amadeus: Simplify PoolMapper alignment checks * amadeus: Update Surround delay effect matrix to REV11 * amadeus: Add drop parameter support and use 32 bits integers for estimate time Also implement accurate ExecuteAudioRendererRendering stub. * Address gdkchan's comments * Address gdkchan's other comments * Address gdkchan's comment --- Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs | 22 +++++++++--- .../Command/AdpcmDataSourceCommandVersion1.cs | 2 +- .../Dsp/Command/AuxiliaryBufferCommand.cs | 2 +- .../Dsp/Command/BiquadFilterCommand.cs | 2 +- .../Dsp/Command/CaptureBufferCommand.cs | 2 +- .../Dsp/Command/CircularBufferSinkCommand.cs | 2 +- .../Dsp/Command/ClearMixBufferCommand.cs | 2 +- .../Dsp/Command/CopyMixBufferCommand.cs | 2 +- .../Dsp/Command/DataSourceVersion2Command.cs | 2 +- .../Renderer/Dsp/Command/DelayCommand.cs | 30 ++++++++-------- .../Dsp/Command/DepopForMixBuffersCommand.cs | 2 +- .../Dsp/Command/DepopPrepareCommand.cs | 2 +- .../Renderer/Dsp/Command/DeviceSinkCommand.cs | 2 +- .../Command/DownMixSurroundToStereoCommand.cs | 2 +- .../Dsp/Command/GroupedBiquadFilterCommand.cs | 7 ++-- .../Renderer/Dsp/Command/ICommand.cs | 2 +- .../Dsp/Command/LimiterCommandVersion1.cs | 2 +- .../Dsp/Command/LimiterCommandVersion2.cs | 2 +- .../Renderer/Dsp/Command/MixCommand.cs | 2 +- .../Renderer/Dsp/Command/MixRampCommand.cs | 2 +- .../Dsp/Command/MixRampGroupedCommand.cs | 2 +- .../PcmFloatDataSourceCommandVersion1.cs | 2 +- .../PcmInt16DataSourceCommandVersion1.cs | 2 +- .../Dsp/Command/PerformanceCommand.cs | 2 +- .../Renderer/Dsp/Command/Reverb3dCommand.cs | 2 +- .../Renderer/Dsp/Command/ReverbCommand.cs | 2 +- .../Renderer/Dsp/Command/UpsampleCommand.cs | 2 +- .../Renderer/Dsp/Command/VolumeCommand.cs | 2 +- .../Renderer/Dsp/Command/VolumeRampCommand.cs | 2 +- .../Renderer/Server/AudioRenderSystem.cs | 34 ++++++++++++++++--- .../Renderer/Server/BehaviourContext.cs | 3 +- .../Renderer/Server/CommandBuffer.cs | 2 +- .../Renderer/Server/MemoryPool/PoolMapper.cs | 4 +-- Ryujinx.Audio/ResultCode.cs | 1 + .../Audio/AudioRenderer/AudioRenderer.cs | 12 ++++++- .../AudioRenderer/AudioRendererServer.cs | 29 ++++++++++++++++ .../Audio/AudioRenderer/IAudioRenderer.cs | 2 ++ 37 files changed, 140 insertions(+), 58 deletions(-) diff --git a/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs b/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs index f6638a9af..2680dcb1e 100644 --- a/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs +++ b/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs @@ -1,4 +1,5 @@ using Ryujinx.Audio.Renderer.Dsp.State; +using Ryujinx.Common.Logging; using System; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -71,6 +72,19 @@ namespace Ryujinx.Audio.Renderer.Dsp return (short)value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static short GetCoefficientAtIndex(ReadOnlySpan coefficients, int index) + { + if ((uint)index > (uint)coefficients.Length) + { + Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}"); + + return 0; + } + + return coefficients[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Decode(Span output, ReadOnlySpan input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan coefficients, ref AdpcmLoopContext loopContext) { @@ -84,8 +98,8 @@ namespace Ryujinx.Audio.Renderer.Dsp byte coefficientIndex = (byte)((predScale >> 4) & 0xF); short history0 = loopContext.History0; short history1 = loopContext.History1; - short coefficient0 = coefficients[coefficientIndex * 2 + 0]; - short coefficient1 = coefficients[coefficientIndex * 2 + 1]; + short coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 0); + short coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1); int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset); int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset); @@ -109,8 +123,8 @@ namespace Ryujinx.Audio.Renderer.Dsp coefficientIndex = (byte)((predScale >> 4) & 0xF); - coefficient0 = coefficients[coefficientIndex * 2 + 0]; - coefficient1 = coefficients[coefficientIndex * 2 + 1]; + coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2); + coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1); nibbles += 2; diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs index 1ad629f49..1fe6069f7 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.AdpcmDataSourceVersion1; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort OutputBufferIndex { get; } public uint SampleRate { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs index cfa5400c1..5c3c0324b 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.AuxiliaryBuffer; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public uint InputBufferIndex { get; } public uint OutputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs index e35911b24..b994c1cb9 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.BiquadFilter; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public Memory BiquadFilterState { get; } public int InputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs index ab1ea77d9..da1cb2546 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.CaptureBuffer; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public uint InputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs index 27cd6e842..e50637eb3 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.CircularBufferSink; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort[] Input { get; } public uint InputCount { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs index c3530db1a..9e653e804 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.ClearMixBuffer; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ClearMixBufferCommand(int nodeId) { diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs index 64c297d11..7237fddf6 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.CopyMixBuffer; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs index f602262e3..c1503b6a0 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType { get; } - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort OutputBufferIndex { get; } public uint SampleRate { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index 8f11da958..cb5678c7b 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.Delay; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public DelayParameter Parameter => _parameter; public Memory State { get; } @@ -49,15 +49,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]); } - // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour - // TODO: Update delay processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping. - DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices); - DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices); + DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices); + DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount) { + const ushort channelCount = 1; + float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision); float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision); float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); @@ -70,7 +70,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float temp = input * inGain + delayLineValue * feedbackGain; - state.UpdateLowPassFilter(ref temp, 1); + state.UpdateLowPassFilter(ref temp, channelCount); outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64; } @@ -104,7 +104,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Y = state.DelayLines[1].Read(), }; - Vector2 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain; + Vector2 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; state.UpdateLowPassFilter(ref Unsafe.As(ref temp), channelCount); @@ -148,7 +148,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command W = state.DelayLines[3].Read() }; - Vector4 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain; + Vector4 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; state.UpdateLowPassFilter(ref Unsafe.As(ref temp), channelCount); @@ -171,12 +171,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision); - Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, - 0.0f, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, - delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, - 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, - delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackBaseGain, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, feedbackGain); + Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f, + 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, + delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f, + delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, + 0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain); for (int i = 0; i < sampleCount; i++) { @@ -200,7 +200,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command U = state.DelayLines[5].Read() }; - Vector6 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain; + Vector6 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; state.UpdateLowPassFilter(ref Unsafe.As(ref temp), channelCount); diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs index e3a87c102..1dba56e6c 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.DepopForMixBuffers; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public uint MixBufferOffset { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs index 1e37ff715..d02f7c121 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.DepopPrepare; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public uint MixBufferCount { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs index a34fbc562..9c88a4e7f 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.DeviceSink; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public string DeviceName { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs index d75da6f9f..79cefcc53 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.DownMixSurroundToStereo; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort[] InputBufferIndices { get; } public ushort[] OutputBufferIndices { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs index ae1ab12c5..b190cc10d 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.GroupedBiquadFilter; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } private BiquadFilterParameter[] _parameters; private Memory _biquadFilterStates; @@ -47,9 +47,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } - // NOTE: Nintendo also implements a hot path for double biquad filters, but no generic path when the command definition suggests it could be done. - // As such we currently only implement a generic path for simplicity. - // TODO: Implement double biquad filters fast path. + // NOTE: Nintendo only implement single and double biquad filters but no generic path when the command definition suggests it could be done. + // As such we currently only implement a generic path for simplicity for double biquad. if (_parameters.Length == 1) { BiquadFilterHelper.ProcessBiquadFilter(ref _parameters[0], ref states[0], outputBuffer, inputBuffer, context.SampleCount); diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs index dddd2511e..d281e6e9f 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType { get; } - public ulong EstimatedProcessingTime { get; } + public uint EstimatedProcessingTime { get; } public void Process(CommandList context); diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs index a393c8859..9cfef736e 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.LimiterVersion1; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public LimiterParameter Parameter => _parameter; public Memory State { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs index ad703de14..46c95e4f9 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.LimiterVersion2; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public LimiterParameter Parameter => _parameter; public Memory State { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs index d50309964..2616bda57 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.Mix; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs index 06af9f6fa..76a1aba25 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.MixRamp; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs index 97bb0f508..e348e3588 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.MixRampGrouped; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public uint MixBufferCount { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs index 7c48a511c..7cec7d2ab 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.PcmFloatDataSourceVersion1; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort OutputBufferIndex { get; } public uint SampleRate { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs index 8483f6d41..dfe9814fe 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.PcmInt16DataSourceVersion1; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort OutputBufferIndex { get; } public uint SampleRate { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs index c2f94474d..d3e3f8056 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.Performance; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public PerformanceEntryAddresses PerformanceEntryAddresses { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs index 04809245f..eeb645673 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.Reverb3d; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs index 130706d10..0a32a065d 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.Reverb; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ReverbParameter Parameter => _parameter; public Memory State { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs index 6df44b327..1617a6421 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.Upsample; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public uint BufferCount { get; } public uint InputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs index e29478911..0628f6d81 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.Volume; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs index ffda8b1a0..5c0c88451 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CommandType CommandType => CommandType.VolumeRamp; - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } public ushort InputBufferIndex { get; } public ushort OutputBufferIndex { get; } diff --git a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index af163ae04..34fdef8a1 100644 --- a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -28,6 +28,7 @@ namespace Ryujinx.Audio.Renderer.Server { private object _lock = new object(); + private AudioRendererRenderingDevice _renderingDevice; private AudioRendererExecutionMode _executionMode; private IWritableEvent _systemEvent; private ManualResetEvent _terminationEvent; @@ -63,6 +64,7 @@ namespace Ryujinx.Audio.Renderer.Server private uint _renderingTimeLimitPercent; private bool _voiceDropEnabled; private uint _voiceDropCount; + private float _voiceDropParameter; private bool _isDspRunningBehind; private ICommandProcessingTimeEstimator _commandProcessingTimeEstimator; @@ -95,6 +97,7 @@ namespace Ryujinx.Audio.Renderer.Server _totalElapsedTicksUpdating = 0; _sessionId = 0; + _voiceDropParameter = 1.0f; } public ResultCode Initialize( @@ -130,6 +133,7 @@ namespace Ryujinx.Audio.Renderer.Server _upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount; _appletResourceId = appletResourceId; _memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount; + _renderingDevice = parameter.RenderingDevice; _executionMode = parameter.ExecutionMode; _sessionId = sessionId; MemoryManager = memoryManager; @@ -337,6 +341,7 @@ namespace Ryujinx.Audio.Renderer.Server _processHandle = processHandle; _elapsedFrameCount = 0; + _voiceDropParameter = 1.0f; switch (_behaviourContext.GetCommandProcessingTimeEstimatorVersion()) { @@ -515,7 +520,7 @@ namespace Ryujinx.Audio.Renderer.Server return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency); } - private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp) + private uint ComputeVoiceDrop(CommandBuffer commandBuffer, uint voicesEstimatedTime, long deltaTimeDsp) { int i; @@ -584,7 +589,7 @@ namespace Ryujinx.Audio.Renderer.Server { command.Enabled = false; - voicesEstimatedTime -= (long)command.EstimatedProcessingTime; + voicesEstimatedTime -= (uint)(_voiceDropParameter * command.EstimatedProcessingTime); } } } @@ -618,13 +623,13 @@ namespace Ryujinx.Audio.Renderer.Server _voiceContext.Sort(); commandGenerator.GenerateVoices(); - long voicesEstimatedTime = (long)commandBuffer.EstimatedProcessingTime; + uint voicesEstimatedTime = (uint)(_voiceDropParameter * commandBuffer.EstimatedProcessingTime); commandGenerator.GenerateSubMixes(); commandGenerator.GenerateFinalMixes(); commandGenerator.GenerateSinks(); - long totalEstimatedTime = (long)commandBuffer.EstimatedProcessingTime; + uint totalEstimatedTime = (uint)(_voiceDropParameter * commandBuffer.EstimatedProcessingTime); if (_voiceDropEnabled) { @@ -856,5 +861,26 @@ namespace Ryujinx.Audio.Renderer.Server } } } + + public void SetVoiceDropParameter(float voiceDropParameter) + { + _voiceDropParameter = Math.Clamp(voiceDropParameter, 0.0f, 2.0f); + } + + public float GetVoiceDropParameter() + { + return _voiceDropParameter; + } + + public ResultCode ExecuteAudioRendererRendering() + { + if (_executionMode == AudioRendererExecutionMode.Manual && _renderingDevice == AudioRendererRenderingDevice.Cpu) + { + // NOTE: Here Nintendo aborts with this error code, we don't want that. + return ResultCode.InvalidExecutionContextOperation; + } + + return ResultCode.UnsupportedOperation; + } } } \ No newline at end of file diff --git a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs index 417204151..adf5294ea 100644 --- a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs +++ b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs @@ -94,8 +94,9 @@ namespace Ryujinx.Audio.Renderer.Server /// REV11: /// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer. /// A new version of the command estimator was added to address timing changes caused by the legacy effects changes. + /// A voice drop parameter was added in 15.0.0: This allows an application to amplify or attenuate the estimated time of DSP commands. /// - /// This was added in system update 14.0.0 + /// This was added in system update 14.0.0 but some changes were made in 15.0.0 public const int Revision11 = 11 << 24; /// diff --git a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index 3a3a981d7..e0741cc6e 100644 --- a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Server /// /// The estimated total processing time. /// - public ulong EstimatedProcessingTime { get; set; } + public uint EstimatedProcessingTime { get; set; } /// /// The command list that is populated by the . diff --git a/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs b/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs index cf0fc067e..6c79da157 100644 --- a/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs +++ b/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs @@ -263,12 +263,12 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool return UpdateResult.Success; } - if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress & (pageSize - 1)) != 0) + if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % pageSize) != 0) { return UpdateResult.InvalidParameter; } - if (inParameter.Size == 0 || (inParameter.Size & (pageSize - 1)) != 0) + if (inParameter.Size == 0 || (inParameter.Size % pageSize) != 0) { return UpdateResult.InvalidParameter; } diff --git a/Ryujinx.Audio/ResultCode.cs b/Ryujinx.Audio/ResultCode.cs index 8e0bfcb0c..1d05ac65e 100644 --- a/Ryujinx.Audio/ResultCode.cs +++ b/Ryujinx.Audio/ResultCode.cs @@ -17,5 +17,6 @@ namespace Ryujinx.Audio InvalidAddressInfo = (42 << ErrorCodeShift) | ModuleId, InvalidMixSorting = (43 << ErrorCodeShift) | ModuleId, UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId, + InvalidExecutionContextOperation = (514 << ErrorCodeShift) | ModuleId, } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs index d69bde037..5b682bf84 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer public ResultCode ExecuteAudioRendererRendering() { - throw new NotImplementedException(); + return (ResultCode)_impl.ExecuteAudioRendererRendering(); } public uint GetMixBufferCount() @@ -108,5 +108,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer _impl.Dispose(); } } + + public void SetVoiceDropParameter(float voiceDropParameter) + { + _impl.SetVoiceDropParameter(voiceDropParameter); + } + + public float GetVoiceDropParameter() + { + return _impl.GetVoiceDropParameter(); + } } } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index dd48a666b..b2ddb697a 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -172,6 +172,35 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return result; } + [CommandHipc(11)] // 3.0.0+ + // ExecuteAudioRendererRendering() + public ResultCode ExecuteAudioRendererRendering(ServiceCtx context) + { + return _impl.ExecuteAudioRendererRendering(); + } + + [CommandHipc(12)] // 15.0.0+ + // SetVoiceDropParameter(f32 voiceDropParameter) + public ResultCode SetVoiceDropParameter(ServiceCtx context) + { + float voiceDropParameter = context.RequestData.ReadSingle(); + + _impl.SetVoiceDropParameter(voiceDropParameter); + + return ResultCode.Success; + } + + [CommandHipc(13)] // 15.0.0+ + // GetVoiceDropParameter() -> f32 voiceDropParameter + public ResultCode GetVoiceDropParameter(ServiceCtx context) + { + float voiceDropParameter = _impl.GetVoiceDropParameter(); + + context.ResponseData.Write(voiceDropParameter); + + return ResultCode.Success; + } + protected override void Dispose(bool isDisposing) { if (isDisposing) diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs index a59c94e97..404bf4c10 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs @@ -16,5 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer void SetRenderingTimeLimit(uint percent); uint GetRenderingTimeLimit(); ResultCode ExecuteAudioRendererRendering(); + void SetVoiceDropParameter(float voiceDropParameter); + float GetVoiceDropParameter(); } } From 1fc0f569de518581761ab2f4b751b0dbaf2cd279 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Mon, 28 Nov 2022 22:18:22 +0000 Subject: [PATCH 121/737] GPU: Always draw polygon topology as triangle fan (#3932) Polygon topology wasn't really supported and would only work on OpenGL on drivers that haven't removed it. As an alternative, this PR makes all cases of polygon topology use triangle fan. The topology type and transform feedback type have not been changed, as I don't think geo shader/tfb should be used with polygons. The OpenGL spec states: Only convex polygons are guaranteed to be drawn correctly by the GL. For convex polygons, triangle fan is equivalent to polygon. I imagine this is probably how it works on device, as this get-out-of-jail-free card is too enticing to pass up. This fixes the stat display in Pokemon S/V. --- Ryujinx.Graphics.OpenGL/EnumConversion.cs | 2 +- Ryujinx.Graphics.Vulkan/EnumConversion.cs | 1 + Ryujinx.Graphics.Vulkan/PipelineBase.cs | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics.OpenGL/EnumConversion.cs b/Ryujinx.Graphics.OpenGL/EnumConversion.cs index 4a06e9649..f262c584c 100644 --- a/Ryujinx.Graphics.OpenGL/EnumConversion.cs +++ b/Ryujinx.Graphics.OpenGL/EnumConversion.cs @@ -331,7 +331,7 @@ namespace Ryujinx.Graphics.OpenGL case PrimitiveTopology.QuadStrip: return PrimitiveType.QuadStrip; case PrimitiveTopology.Polygon: - return PrimitiveType.Polygon; + return PrimitiveType.TriangleFan; case PrimitiveTopology.LinesAdjacency: return PrimitiveType.LinesAdjacency; case PrimitiveTopology.LineStripAdjacency: diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 804cd70c4..9d9be65eb 100644 --- a/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -180,6 +180,7 @@ namespace Ryujinx.Graphics.Vulkan GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency, GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency, GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList, + GAL.PrimitiveTopology.Polygon => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."), GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."), _ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList) diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 87155a0d5..594ed5e78 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -328,7 +328,8 @@ namespace Ryujinx.Graphics.Vulkan IndexBufferPattern pattern = _topology switch { GAL.PrimitiveTopology.Quads => QuadsToTrisPattern, - GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern, + GAL.PrimitiveTopology.TriangleFan or + GAL.PrimitiveTopology.Polygon => TriFanToTrisPattern, _ => throw new NotSupportedException($"Unsupported topology: {_topology}") }; @@ -359,7 +360,8 @@ namespace Ryujinx.Graphics.Vulkan pattern = _topology switch { GAL.PrimitiveTopology.Quads => QuadsToTrisPattern, - GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern, + GAL.PrimitiveTopology.TriangleFan or + GAL.PrimitiveTopology.Polygon => TriFanToTrisPattern, _ => throw new NotSupportedException($"Unsupported topology: {_topology}") }; } From fbf2b09706a55856c76aaf9051007eedf1f4b0ab Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 29 Nov 2022 06:33:46 +0100 Subject: [PATCH 122/737] ava: Make dialogs using an overlay window work on Linux (#3938) --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index c851b4513..9818914cf 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -27,7 +27,7 @@ - + diff --git a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs index 9fcc2d2ec..9cf4231c5 100644 --- a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs +++ b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs @@ -127,9 +127,16 @@ namespace Ryujinx.Ava.Ui.Controls contentDialog.PrimaryButtonClick += deferCloseAction; } - await contentDialog.ShowAsync(ContentDialogPlacement.Popup); + if (useOverlay) + { + await contentDialog.ShowAsync(overlay, ContentDialogPlacement.Popup); - overlay?.Close(); + overlay!.Close(); + } + else + { + await contentDialog.ShowAsync(ContentDialogPlacement.Popup); + } } if (useOverlay) @@ -391,4 +398,4 @@ namespace Ryujinx.Ava.Ui.Controls return string.Empty; } } -} +} \ No newline at end of file From d41c95dcffb06cb915d20c9bcf2bf7c3a2636b31 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Tue, 29 Nov 2022 14:32:40 +0100 Subject: [PATCH 123/737] chore: Update OpenTK to 4.7.5 (#3944) --- .../Ryujinx.Audio.Backends.OpenAL.csproj | 2 +- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj | 2 +- Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj | 2 +- Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj | 2 +- Ryujinx/Ryujinx.csproj | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj index a29848cf3..9736c348e 100644 --- a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj +++ b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj @@ -5,7 +5,7 @@ - + diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 9818914cf..0edad85dd 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -30,7 +30,7 @@ - + diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj index c12d6ff26..9a4869cec 100644 --- a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj +++ b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj @@ -6,7 +6,7 @@ - + diff --git a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index c98d66423..4f7c8c456 100644 --- a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -13,7 +13,7 @@ - + diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index a9d227c63..15286ea3a 100644 --- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -11,7 +11,7 @@ - + diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 7681fe54e..31f130c4a 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -21,10 +21,10 @@ - - + + From a5c2aead6795192158caeb79e785543a15b5b2f4 Mon Sep 17 00:00:00 2001 From: merry Date: Tue, 29 Nov 2022 13:47:57 +0000 Subject: [PATCH 124/737] ConcurrentBitmap: Use Interlocked Or/And (#3937) --- Ryujinx.Memory/Tracking/ConcurrentBitmap.cs | 33 ++++++++------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs b/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs index 2e007bb56..994fda921 100644 --- a/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs +++ b/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Memory.Tracking { for (int i = 0; i < Masks.Length; i++) { - if (Volatile.Read(ref Masks[i]) != 0) + if (Interlocked.Read(ref Masks[i]) != 0) { return true; } @@ -62,7 +62,7 @@ namespace Ryujinx.Memory.Tracking long wordMask = 1L << wordBit; - return (Volatile.Read(ref Masks[wordIndex]) & wordMask) != 0; + return (Interlocked.Read(ref Masks[wordIndex]) & wordMask) != 0; } /// @@ -86,7 +86,7 @@ namespace Ryujinx.Memory.Tracking int endBit = end & IntMask; long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - long startValue = Volatile.Read(ref Masks[startIndex]); + long startValue = Interlocked.Read(ref Masks[startIndex]); if (startIndex == endIndex) { @@ -100,13 +100,13 @@ namespace Ryujinx.Memory.Tracking for (int i = startIndex + 1; i < endIndex; i++) { - if (Volatile.Read(ref Masks[i]) != 0) + if (Interlocked.Read(ref Masks[i]) != 0) { return true; } } - long endValue = Volatile.Read(ref Masks[endIndex]); + long endValue = Interlocked.Read(ref Masks[endIndex]); if ((endValue & endMask) != 0) { @@ -128,23 +128,14 @@ namespace Ryujinx.Memory.Tracking long wordMask = 1L << wordBit; - long existing; - long newValue; - - do + if (value) { - existing = Volatile.Read(ref Masks[wordIndex]); - - if (value) - { - newValue = existing | wordMask; - } - else - { - newValue = existing & ~wordMask; - } + Interlocked.Or(ref Masks[wordIndex], wordMask); + } + else + { + Interlocked.And(ref Masks[wordIndex], ~wordMask); } - while (Interlocked.CompareExchange(ref Masks[wordIndex], newValue, existing) != existing); } /// @@ -154,7 +145,7 @@ namespace Ryujinx.Memory.Tracking { for (int i = 0; i < Masks.Length; i++) { - Volatile.Write(ref Masks[i], 0); + Interlocked.Exchange(ref Masks[i], 0); } } } From c0821fee1fcf4effb258d286e9e35a3c33c78fd7 Mon Sep 17 00:00:00 2001 From: EmulationFanatic <62343878+EmulationFanatic@users.noreply.github.com> Date: Tue, 29 Nov 2022 09:20:26 -0700 Subject: [PATCH 125/737] Update README.MD (#3946) Updates the readme to include usage of Metal via MoltenVK, updated game compatibility statistics. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0731884b3..ab3800ef8 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ ## Compatibility -As of October 2022, Ryujinx has been tested on approximately 3,700 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. +As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already! ## Usage @@ -90,7 +90,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located - **GPU** - The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum) or Vulkan APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI. + The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI. - **Input** From af01100050268de7a10889008e98e479f1247af1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 23:30:33 +0100 Subject: [PATCH 126/737] nuget: bump System.Management from 6.0.0 to 7.0.0 (#3949) Bumps [System.Management](https://github.com/dotnet/runtime) from 6.0.0 to 7.0.0. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v6.0.0...v7.0.0) --- updated-dependencies: - dependency-name: System.Management dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Ryujinx.Common/Ryujinx.Common.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj index 818e5ba33..fbf069b0c 100644 --- a/Ryujinx.Common/Ryujinx.Common.csproj +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -8,7 +8,7 @@ - + From 8750b90a7f5e76cdff991a137ec8c2eed0db00dd Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 30 Nov 2022 18:06:40 -0300 Subject: [PATCH 127/737] Ensure that vertex attribute buffer index is valid on GPU (#3942) * Ensure that vertex attribute buffer index is valid on GPU * Remove vertex buffer validation code from OpenGL * Remove some fields that are no longer necessary --- .../Engine/Threed/StateUpdater.cs | 27 ++++++++++++- Ryujinx.Graphics.OpenGL/VertexArray.cs | 40 ------------------- 2 files changed, 26 insertions(+), 41 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index d51077dc7..8da5ea5ef 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private bool _vsUsesDrawParameters; private bool _vtgWritesRtLayer; private byte _vsClipDistancesWritten; + private uint _vbEnableMask; private bool _prevDrawIndexed; private bool _prevDrawIndirect; @@ -76,6 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed nameof(ThreedClassState.VertexBufferState), nameof(ThreedClassState.VertexBufferEndAddress)), + // Must be done after vertex buffer updates. new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)), new StateUpdateCallbackEntry(UpdateBlendState, @@ -852,12 +854,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// private void UpdateVertexAttribState() { + uint vbEnableMask = _vbEnableMask; + Span vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs]; for (int index = 0; index < Constants.TotalVertexAttribs; index++) { var vertexAttrib = _state.State.VertexAttribState[index]; + int bufferIndex = vertexAttrib.UnpackBufferIndex(); + + if ((vbEnableMask & (1u << bufferIndex)) == 0) + { + // Using a vertex buffer that doesn't exist is invalid, so let's use a dummy attribute for those cases. + vertexAttribs[index] = new VertexAttribDescriptor(0, 0, true, Format.R32G32B32A32Float); + continue; + } + if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format)) { Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}."); @@ -866,7 +879,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } vertexAttribs[index] = new VertexAttribDescriptor( - vertexAttrib.UnpackBufferIndex(), + bufferIndex, vertexAttrib.UnpackOffset(), vertexAttrib.UnpackIsConstant(), format); @@ -954,6 +967,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool drawIndexed = _drawState.DrawIndexed; bool drawIndirect = _drawState.DrawIndirect; + uint vbEnableMask = 0; for (int index = 0; index < Constants.TotalVertexBuffers; index++) { @@ -971,6 +985,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ulong address = vertexBuffer.Address.Pack(); + if (_channel.MemoryManager.IsMapped(address)) + { + vbEnableMask |= 1u << index; + } + int stride = vertexBuffer.UnpackStride(); bool instanced = _state.State.VertexBufferInstanced[index]; @@ -1017,6 +1036,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor); _channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor); } + + if (_vbEnableMask != vbEnableMask) + { + _vbEnableMask = vbEnableMask; + UpdateVertexAttribState(); + } } /// diff --git a/Ryujinx.Graphics.OpenGL/VertexArray.cs b/Ryujinx.Graphics.OpenGL/VertexArray.cs index d466199d3..7d22033ec 100644 --- a/Ryujinx.Graphics.OpenGL/VertexArray.cs +++ b/Ryujinx.Graphics.OpenGL/VertexArray.cs @@ -10,13 +10,9 @@ namespace Ryujinx.Graphics.OpenGL { public int Handle { get; private set; } - private bool _needsAttribsUpdate; - private readonly VertexAttribDescriptor[] _vertexAttribs; private readonly VertexBufferDescriptor[] _vertexBuffers; - private int _vertexAttribsCount; - private int _vertexBuffersCount; private int _minVertexCount; private uint _vertexAttribsInUse; @@ -76,9 +72,7 @@ namespace Ryujinx.Graphics.OpenGL _vertexBuffers[bindingIndex] = vb; } - _vertexBuffersCount = bindingIndex; _minVertexCount = minVertexCount; - _needsAttribsUpdate = true; } public void SetVertexAttributes(ReadOnlySpan vertexAttribs) @@ -131,8 +125,6 @@ namespace Ryujinx.Graphics.OpenGL _vertexAttribs[index] = attrib; } - _vertexAttribsCount = index; - for (; index < Constants.MaxVertexAttribs; index++) { DisableVertexAttrib(index); @@ -160,13 +152,11 @@ namespace Ryujinx.Graphics.OpenGL public void PreDraw(int vertexCount) { LimitVertexBuffers(vertexCount); - Validate(); } public void PreDrawVbUnbounded() { UnlimitVertexBuffers(); - Validate(); } public void LimitVertexBuffers(int vertexCount) @@ -252,36 +242,6 @@ namespace Ryujinx.Graphics.OpenGL _vertexBuffersLimited = 0; } - public void Validate() - { - for (int attribIndex = 0; attribIndex < _vertexAttribsCount; attribIndex++) - { - VertexAttribDescriptor attrib = _vertexAttribs[attribIndex]; - - if (!attrib.IsZero) - { - if ((uint)attrib.BufferIndex >= _vertexBuffersCount) - { - DisableVertexAttrib(attribIndex); - continue; - } - - if (_vertexBuffers[attrib.BufferIndex].Buffer.Handle == BufferHandle.Null) - { - DisableVertexAttrib(attribIndex); - continue; - } - - if (_needsAttribsUpdate) - { - EnableVertexAttrib(attribIndex); - } - } - } - - _needsAttribsUpdate = false; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnableVertexAttrib(int index) { From 4905101df1b3dcb19682a2f9e83c81afb0627003 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 30 Nov 2022 18:24:15 -0300 Subject: [PATCH 128/737] Remove shader dependency on SPV_KHR_shader_ballot and SPV_KHR_subgroup_vote extensions (#3943) * Remove shader dependency on SPV_KHR_shader_ballot and SPV_KHR_subgroup_vote extensions * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Spirv/Instructions.cs | 19 +++++++++++-------- .../CodeGen/Spirv/SpirvGenerator.cs | 7 ++----- .../VulkanInitialization.cs | 1 - 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 1d4842aad..617e47652 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3897; + private const uint CodeGenVersion = 3943; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index ea83061ec..d4a3102e2 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var source = operation.GetSource(0); var uvec4Type = context.TypeVector(context.TypeU32(), 4); - var execution = context.Constant(context.TypeU32(), 3); // Subgroup + var execution = context.Constant(context.TypeU32(), Scope.Subgroup); var maskVector = context.GroupNonUniformBallot(uvec4Type, execution, context.Get(AggregateType.Bool, source)); var mask = context.CompositeExtract(context.TypeU32(), maskVector, (SpvLiteralInteger)0); @@ -1233,7 +1233,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); var srcThreadId = context.BitwiseOr(context.TypeU32(), indexNotSegMask, minThreadId); var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId); - var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId); + var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId); var result = context.Select(context.TypeFP32(), valid, value, x); var validLocal = (AstOperand)operation.GetSource(3); @@ -1263,7 +1263,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); var srcThreadId = context.IAdd(context.TypeU32(), threadId, index); var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId); - var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId); + var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId); var result = context.Select(context.TypeFP32(), valid, value, x); var validLocal = (AstOperand)operation.GetSource(3); @@ -1289,7 +1289,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var srcThreadId = context.ISub(context.TypeU32(), threadId, index); var valid = context.SGreaterThanEqual(context.TypeBool(), srcThreadId, minThreadId); - var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId); + var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId); var result = context.Select(context.TypeFP32(), valid, value, x); var validLocal = (AstOperand)operation.GetSource(3); @@ -1319,7 +1319,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); var srcThreadId = context.BitwiseXor(context.TypeU32(), threadId, index); var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId); - var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId); + var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId); var result = context.Select(context.TypeFP32(), valid, value, x); var validLocal = (AstOperand)operation.GetSource(3); @@ -1861,19 +1861,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation) { - var result = context.SubgroupAllKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0))); + var execution = context.Constant(context.TypeU32(), Scope.Subgroup); + var result = context.GroupNonUniformAll(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0))); return new OperationResult(AggregateType.Bool, result); } private static OperationResult GenerateVoteAllEqual(CodeGenContext context, AstOperation operation) { - var result = context.SubgroupAllEqualKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0))); + var execution = context.Constant(context.TypeU32(), Scope.Subgroup); + var result = context.GroupNonUniformAllEqual(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0))); return new OperationResult(AggregateType.Bool, result); } private static OperationResult GenerateVoteAny(CodeGenContext context, AstOperation operation) { - var result = context.SubgroupAnyKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0))); + var execution = context.Constant(context.TypeU32(), Scope.Subgroup); + var result = context.GroupNonUniformAny(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0))); return new OperationResult(AggregateType.Bool, result); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 95df077bf..6e1db972d 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -50,12 +50,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool); context.AddCapability(Capability.GroupNonUniformBallot); + context.AddCapability(Capability.GroupNonUniformShuffle); + context.AddCapability(Capability.GroupNonUniformVote); context.AddCapability(Capability.ImageBuffer); context.AddCapability(Capability.ImageGatherExtended); context.AddCapability(Capability.ImageQuery); context.AddCapability(Capability.SampledBuffer); - context.AddCapability(Capability.SubgroupBallotKHR); - context.AddCapability(Capability.SubgroupVoteKHR); if (config.TransformFeedbackEnabled && config.LastInVertexPipeline) { @@ -94,9 +94,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddCapability(Capability.DrawParameters); } - context.AddExtension("SPV_KHR_shader_ballot"); - context.AddExtension("SPV_KHR_subgroup_vote"); - Declarations.DeclareAll(context, info); if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 48f80ad48..190221c78 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -37,7 +37,6 @@ namespace Ryujinx.Graphics.Vulkan public static string[] RequiredExtensions { get; } = new string[] { KhrSwapchain.ExtensionName, - "VK_EXT_shader_subgroup_vote", ExtTransformFeedback.ExtensionName }; From d2686e0a5b4f8ce70cbbd5cfcb02434921580834 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Nov 2022 21:58:45 +0000 Subject: [PATCH 129/737] nuget: bump DiscordRichPresence from 1.0.175 to 1.1.3.18 (#3965) Bumps [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) from 1.0.175 to 1.1.3.18. - [Release notes](https://github.com/Lachee/discord-rpc-csharp/releases) - [Commits](https://github.com/Lachee/discord-rpc-csharp/commits) --- updated-dependencies: - dependency-name: DiscordRichPresence dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj b/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj index 4dd1dba83..ed89e08de 100644 --- a/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj +++ b/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj @@ -41,7 +41,7 @@ - + From 3fb583c98c39da58f0752c652ca60be87ff1f566 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 30 Nov 2022 23:34:25 +0100 Subject: [PATCH 130/737] Avalonia: Clean up leftover RenderTimer & Fix minimum and initial window size (#3935) * ava: Cleanup RenderTimer * ava: Remove ContentControl from RendererHost * ava: Remove unused actual scale factor * ava: Enable UseGpu for Linux * ava: Set better initial size & Scale the window properly * ava: Realign properties * ava: Use explicit type & specify where the note applies --- Ryujinx.Ava/Program.cs | 27 +---- Ryujinx.Ava/Ui/Controls/RenderTimer.cs | 100 ------------------ Ryujinx.Ava/Ui/Controls/RendererHost.axaml | 7 -- Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs | 2 +- Ryujinx.Ava/Ui/Windows/MainWindow.axaml | 5 +- Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 4 +- 6 files changed, 13 insertions(+), 132 deletions(-) delete mode 100644 Ryujinx.Ava/Ui/Controls/RenderTimer.cs diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index a941302b2..910403970 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -1,8 +1,5 @@ using ARMeilleure.Translation.PTC; using Avalonia; -using Avalonia.Rendering; -using Avalonia.Threading; -using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -23,18 +20,15 @@ namespace Ryujinx.Ava { internal class Program { - public static double WindowScaleFactor { get; set; } - public static double ActualScaleFactor { get; set; } - public static string Version { get; private set; } - public static string ConfigurationPath { get; private set; } - public static bool PreviewerDetached { get; private set; } - public static RenderTimer RenderTimer { get; private set; } + public static double WindowScaleFactor { get; set; } + public static string Version { get; private set; } + public static string ConfigurationPath { get; private set; } + public static bool PreviewerDetached { get; private set; } [DllImport("user32.dll", SetLastError = true)] public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type); private const uint MB_ICONWARNING = 0x30; - private const int BaseDpi = 96; public static void Main(string[] args) { @@ -49,11 +43,7 @@ namespace Ryujinx.Ava Initialize(args); - RenderTimer = new RenderTimer(); - BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - - RenderTimer.Dispose(); } public static AppBuilder BuildAvaloniaApp() @@ -65,7 +55,7 @@ namespace Ryujinx.Ava EnableMultiTouch = true, EnableIme = true, UseEGL = false, - UseGpu = false + UseGpu = true }) .With(new Win32PlatformOptions { @@ -75,12 +65,6 @@ namespace Ryujinx.Ava CompositionBackdropCornerRadius = 8.0f, }) .UseSkia() - .AfterSetup(_ => - { - AvaloniaLocator.CurrentMutable - .Bind().ToConstant(RenderTimer) - .Bind().ToConstant(new RenderLoop(RenderTimer, Dispatcher.UIThread)); - }) .LogToTrace(); } @@ -115,7 +99,6 @@ namespace Ryujinx.Ava ForceDpiAware.Windows(); WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); - ActualScaleFactor = ForceDpiAware.GetActualScaleFactor() / BaseDpi; // Logging system information. PrintSystemInfo(); diff --git a/Ryujinx.Ava/Ui/Controls/RenderTimer.cs b/Ryujinx.Ava/Ui/Controls/RenderTimer.cs deleted file mode 100644 index 577115ea6..000000000 --- a/Ryujinx.Ava/Ui/Controls/RenderTimer.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Avalonia.Rendering; -using System; -using System.Threading; -using System.Timers; - -namespace Ryujinx.Ava.Ui.Controls -{ - internal class RenderTimer : IRenderTimer, IDisposable - { - public event Action Tick - { - add - { - _tick += value; - - if (_subscriberCount++ == 0) - { - Start(); - } - } - - remove - { - if (--_subscriberCount == 0) - { - Stop(); - } - - _tick -= value; - } - } - - private Thread _tickThread; - private readonly System.Timers.Timer _timer; - - private Action _tick; - private int _subscriberCount; - - private bool _isRunning; - - private AutoResetEvent _resetEvent; - - public RenderTimer() - { - _timer = new System.Timers.Timer(15); - _resetEvent = new AutoResetEvent(true); - _timer.Elapsed += Timer_Elapsed; - } - - private void Timer_Elapsed(object sender, ElapsedEventArgs e) - { - TickNow(); - } - - public void Start() - { - _timer.Start(); - if (_tickThread == null) - { - _tickThread = new Thread(RunTick); - _tickThread.Name = "RenderTimerTickThread"; - _tickThread.IsBackground = true; - _isRunning = true; - _tickThread.Start(); - } - } - - public void RunTick() - { - while (_isRunning) - { - _resetEvent.WaitOne(); - _tick?.Invoke(TimeSpan.FromMilliseconds(Environment.TickCount)); - } - } - - public void TickNow() - { - lock (_timer) - { - _resetEvent.Set(); - } - } - - public void Stop() - { - _timer.Stop(); - } - - public void Dispose() - { - _timer.Elapsed -= Timer_Elapsed; - _timer.Stop(); - _isRunning = false; - _resetEvent.Set(); - _tickThread.Join(); - _resetEvent.Dispose(); - } - } -} diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml index be72fd61e..5b27182df 100644 --- a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml +++ b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml @@ -4,11 +4,4 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Ryujinx.Ava.Ui.Controls.RendererHost"> - diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs index 0d1984fd5..b6986b7c8 100644 --- a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs +++ b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Ava.Ui.Controls { _currentWindow.WindowCreated += CurrentWindow_WindowCreated; _currentWindow.SizeChanged += CurrentWindow_SizeChanged; - View.Content = _currentWindow; + Content = _currentWindow; } public void CreateVulkan() diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml index 313e701c8..5b9dd8f91 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml @@ -13,7 +13,7 @@ Title="Ryujinx" Width="1280" Height="785" - MinWidth="1024" + MinWidth="1092" MinHeight="680" d:DesignHeight="720" d:DesignWidth="1280" @@ -57,6 +57,8 @@ Date: Thu, 1 Dec 2022 14:08:43 +0100 Subject: [PATCH 131/737] infra: Add distribution files for macOS (#3934) This upstream macOS packing and distribution files --- distribution/macos/Info.plist | 46 ++ distribution/macos/Ryujinx.icns | Bin 0 -> 108982 bytes distribution/macos/bundle_fix_up.py | 609 ++++++++++++++++++ .../macos/construct_universal_dylib.py | 95 +++ distribution/macos/create_app_bundle.sh | 51 ++ distribution/macos/create_macos_release.sh | 105 +++ distribution/macos/entitlements.xml | 23 + distribution/misc/add_tar_exec.py | 24 + 8 files changed, 953 insertions(+) create mode 100644 distribution/macos/Info.plist create mode 100644 distribution/macos/Ryujinx.icns create mode 100644 distribution/macos/bundle_fix_up.py create mode 100644 distribution/macos/construct_universal_dylib.py create mode 100755 distribution/macos/create_app_bundle.sh create mode 100755 distribution/macos/create_macos_release.sh create mode 100644 distribution/macos/entitlements.xml create mode 100644 distribution/misc/add_tar_exec.py diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist new file mode 100644 index 000000000..c3e1e08e4 --- /dev/null +++ b/distribution/macos/Info.plist @@ -0,0 +1,46 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Ryujinx + CFBundleGetInfoString + Ryujinx + CFBundleIconFile + Ryujinx.icns + CFBundleTypeExtensions + + nca + nro + nso + nsp + xci + + CFBundleIdentifier + org.ryujinx.Ryujinx + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + %%RYUJINX_BUILD_VERSION%%-%%RYUJINX_BUILD_GIT_HASH%%" + CFBundleName + Ryujinx + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.1 + CFBundleSignature + ???? + CFBundleVersion + 1.1.0 + NSHighResolutionCapable + + CSResourcesFileMapped + + NSHumanReadableCopyright + Copyright © 2018 - 2022 Ryujinx Team and Contributors. + LSMinimumSystemVersion + 11.0 + + diff --git a/distribution/macos/Ryujinx.icns b/distribution/macos/Ryujinx.icns new file mode 100644 index 0000000000000000000000000000000000000000..f54a9aeb7e4780b09c1bddb1ad8b7fcf4f9a0d3c GIT binary patch literal 108982 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 str: + res = subprocess.check_output([OTOOL, "-D", str(dylib_path.absolute())]).decode( + "utf-8" + ) + + return res.split("\n")[1] + + +def get_dylib_dependencies(dylib_path: Path) -> List[str]: + output = ( + subprocess.check_output([OTOOL, "-L", str(dylib_path.absolute())]) + .decode("utf-8") + .split("\n")[1:] + ) + + res = [] + + for line in output: + line = line.strip() + index = line.find(" (compatibility version ") + if index == -1: + continue + + line = line[:index] + + res.append(line) + + return res + + +def replace_dylib_id(dylib_path: Path, new_id: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-id", new_id, str(dylib_path.absolute())] + ) + + +def change_dylib_link(dylib_path: Path, old: str, new: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-change", old, new, str(dylib_path.absolute())] + ) + + +def add_dylib_rpath(dylib_path: Path, rpath: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-add_rpath", rpath, str(dylib_path.absolute())] + ) + + +def fixup_dylib( + dylib_path: Path, + replacement_path: str, + search_path: List[str], + content_directory: Path, +): + dylib_id = get_dylib_id(dylib_path) + new_dylib_id = replacement_path + "/" + os.path.basename(dylib_id) + replace_dylib_id(dylib_path, new_dylib_id) + + dylib_dependencies = get_dylib_dependencies(dylib_path) + dylib_new_mapping = {} + + for dylib_dependency in dylib_dependencies: + if ( + not dylib_dependency.startswith("@executable_path") + and not dylib_dependency.startswith("/usr/lib") + and not dylib_dependency.startswith("/System/Library") + ): + dylib_dependency_name = os.path.basename(dylib_dependency) + library_found = False + for library_base_path in search_path: + lib_path = Path(os.path.join(library_base_path, dylib_dependency_name)) + + if lib_path.exists(): + target_replacement_path = get_path_related_to_target_exec( + content_directory, lib_path + ) + + dylib_new_mapping[dylib_dependency] = ( + target_replacement_path + + "/" + + os.path.basename(dylib_dependency) + ) + library_found = True + + if not library_found: + raise Exception( + f"{dylib_id}: Cannot find dependency {dylib_dependency_name} for fixup" + ) + + for key in dylib_new_mapping: + change_dylib_link(dylib_path, key, dylib_new_mapping[key]) + + +FILE_TYPE_ASSEMBLY = 1 + +ALIGN_REQUIREMENTS = 4096 + + +def parse_embedded_string(data: bytes) -> Tuple[bytes, str]: + first_byte = data[0] + + if (first_byte & 0x80) == 0: + size = first_byte + data = data[1:] + else: + second_byte = data[1] + + assert (second_byte & 0x80) == 0 + + size = (second_byte << 7) | (first_byte & 0x7F) + + data = data[2:] + + res = data[:size].decode("utf-8") + data = data[size:] + + return (data, res) + + +def write_embedded_string(file, string: str): + raw_str = string.encode("utf-8") + raw_str_len = len(raw_str) + + assert raw_str_len < 0x7FFF + + if raw_str_len > 0x7F: + file.write(struct.pack("b", raw_str_len & 0x7F | 0x80)) + file.write(struct.pack("b", raw_str_len >> 7)) + else: + file.write(struct.pack("b", raw_str_len)) + + file.write(raw_str) + + +class BundleFileEntry(object): + offset: int + size: int + compressed_size: int + file_type: int + relative_path: str + data: bytes + + def __init__( + self, + offset: int, + size: int, + compressed_size: int, + file_type: int, + relative_path: str, + data: bytes, + ) -> None: + self.offset = offset + self.size = size + self.compressed_size = compressed_size + self.file_type = file_type + self.relative_path = relative_path + self.data = data + + def write(self, file): + self.offset = file.tell() + + if ( + self.file_type == FILE_TYPE_ASSEMBLY + and (self.offset % ALIGN_REQUIREMENTS) != 0 + ): + padding_size = ALIGN_REQUIREMENTS - (self.offset % ALIGN_REQUIREMENTS) + file.write(b"\0" * padding_size) + self.offset += padding_size + + file.write(self.data) + + def write_header(self, file): + file.write( + struct.pack( + "QQQb", self.offset, self.size, self.compressed_size, self.file_type + ) + ) + write_embedded_string(file, self.relative_path) + + +class BundleManifest(object): + major: int + minor: int + bundle_id: str + deps_json: BundleFileEntry + runtimeconfig_json: BundleFileEntry + flags: int + files: List[BundleFileEntry] + + def __init__( + self, + major: int, + minor: int, + bundle_id: str, + deps_json: BundleFileEntry, + runtimeconfig_json: BundleFileEntry, + flags: int, + files: List[BundleFileEntry], + ) -> None: + self.major = major + self.minor = minor + self.bundle_id = bundle_id + self.deps_json = deps_json + self.runtimeconfig_json = runtimeconfig_json + self.flags = flags + self.files = files + + def write(self, file) -> int: + for bundle_file in self.files: + bundle_file.write(file) + + bundle_header_offset = file.tell() + file.write(struct.pack("iiI", self.major, self.minor, len(self.files))) + write_embedded_string(file, self.bundle_id) + + if self.deps_json is not None: + deps_json_location_offset = self.deps_json.offset + deps_json_location_size = self.deps_json.size + else: + deps_json_location_offset = 0 + deps_json_location_size = 0 + + if self.runtimeconfig_json is not None: + runtimeconfig_json_location_offset = self.runtimeconfig_json.offset + runtimeconfig_json_location_size = self.runtimeconfig_json.size + else: + runtimeconfig_json_location_offset = 0 + runtimeconfig_json_location_size = 0 + + file.write( + struct.pack("qq", deps_json_location_offset, deps_json_location_size) + ) + file.write( + struct.pack( + "qq", + runtimeconfig_json_location_offset, + runtimeconfig_json_location_size, + ) + ) + file.write(struct.pack("q", self.flags)) + + for bundle_file in self.files: + bundle_file.write_header(file) + + return bundle_header_offset + + +def read_file_entry( + raw_data: bytes, header_bytes: bytes +) -> Tuple[bytes, BundleFileEntry]: + ( + offset, + size, + compressed_size, + file_type, + ) = struct.unpack("QQQb", header_bytes[:0x19]) + (header_bytes, relative_path) = parse_embedded_string(header_bytes[0x19:]) + + target_size = compressed_size + + if target_size == 0: + target_size = size + + return ( + header_bytes, + BundleFileEntry( + offset, + size, + compressed_size, + file_type, + relative_path, + raw_data[offset : offset + target_size], + ), + ) + + +def get_dotnet_bundle_data(data: bytes) -> Optional[Tuple[int, int, BundleManifest]]: + offset = data.find(hashlib.sha256(b".net core bundle\n").digest()) + + if offset == -1: + return None + + raw_header_offset = data[offset - 8 : offset] + (header_offset,) = struct.unpack("q", raw_header_offset) + header_bytes = data[header_offset:] + + ( + major, + minor, + files_count, + ) = struct.unpack("iiI", header_bytes[:0xC]) + header_bytes = header_bytes[0xC:] + + (header_bytes, bundle_id) = parse_embedded_string(header_bytes) + + # v2 header + ( + deps_json_location_offset, + deps_json_location_size, + ) = struct.unpack("qq", header_bytes[:0x10]) + ( + runtimeconfig_json_location_offset, + runtimeconfig_json_location_size, + ) = struct.unpack("qq", header_bytes[0x10:0x20]) + (flags,) = struct.unpack("q", header_bytes[0x20:0x28]) + header_bytes = header_bytes[0x28:] + + files = [] + + deps_json = None + runtimeconfig_json = None + + for _ in range(files_count): + (header_bytes, file_entry) = read_file_entry(data, header_bytes) + + files.append(file_entry) + + if file_entry.offset == deps_json_location_offset: + deps_json = file_entry + elif file_entry.offset == runtimeconfig_json_location_offset: + runtimeconfig_json = file_entry + + file_entry = files[0] + + return ( + file_entry.offset, + header_offset, + BundleManifest( + major, minor, bundle_id, deps_json, runtimeconfig_json, flags, files + ), + ) + + +LC_SYMTAB = 0x2 +LC_SEGMENT_64 = 0x19 +LC_CODE_SIGNATURE = 0x1D + + +def fixup_linkedit(file, data: bytes, new_size: int): + offset = 0 + + ( + macho_magic, + macho_cputype, + macho_cpusubtype, + macho_filetype, + macho_ncmds, + macho_sizeofcmds, + macho_flags, + macho_reserved, + ) = struct.unpack("IiiIIIII", data[offset : offset + 0x20]) + + offset += 0x20 + + linkedit_offset = None + symtab_offset = None + codesign_offset = None + + for _ in range(macho_ncmds): + (cmd, cmdsize) = struct.unpack("II", data[offset : offset + 8]) + + if cmd == LC_SEGMENT_64: + ( + cmd, + cmdsize, + segname_raw, + vmaddr, + vmsize, + fileoff, + filesize, + maxprot, + initprot, + nsects, + flags, + ) = struct.unpack("II16sQQQQiiII", data[offset : offset + 72]) + segname = segname_raw.decode("utf-8").split("\0")[0] + + if segname == "__LINKEDIT": + linkedit_offset = offset + elif cmd == LC_SYMTAB: + symtab_offset = offset + elif cmd == LC_CODE_SIGNATURE: + codesign_offset = offset + + offset += cmdsize + pass + + assert linkedit_offset is not None and symtab_offset is not None + + # If there is a codesign section, clean it up. + if codesign_offset is not None: + ( + codesign_cmd, + codesign_cmdsize, + codesign_dataoff, + codesign_datasize, + ) = struct.unpack("IIII", data[codesign_offset : codesign_offset + 16]) + file.seek(codesign_offset) + file.write(b"\0" * codesign_cmdsize) + + macho_ncmds -= 1 + macho_sizeofcmds -= codesign_cmdsize + file.seek(0) + file.write( + struct.pack( + "IiiIIIII", + macho_magic, + macho_cputype, + macho_cpusubtype, + macho_filetype, + macho_ncmds, + macho_sizeofcmds, + macho_flags, + macho_reserved, + ) + ) + + file.seek(codesign_dataoff) + file.write(b"\0" * codesign_datasize) + + ( + symtab_cmd, + symtab_cmdsize, + symtab_symoff, + symtab_nsyms, + symtab_stroff, + symtab_strsize, + ) = struct.unpack("IIIIII", data[symtab_offset : symtab_offset + 24]) + + symtab_strsize = new_size - symtab_stroff + + new_symtab = struct.pack( + "IIIIII", + symtab_cmd, + symtab_cmdsize, + symtab_symoff, + symtab_nsyms, + symtab_stroff, + symtab_strsize, + ) + + file.seek(symtab_offset) + file.write(new_symtab) + + ( + linkedit_cmd, + linkedit_cmdsize, + linkedit_segname_raw, + linkedit_vmaddr, + linkedit_vmsize, + linkedit_fileoff, + linkedit_filesize, + linkedit_maxprot, + linkedit_initprot, + linkedit_nsects, + linkedit_flags, + ) = struct.unpack("II16sQQQQiiII", data[linkedit_offset : linkedit_offset + 72]) + + linkedit_filesize = new_size - linkedit_fileoff + linkedit_vmsize = linkedit_filesize + + new_linkedit = struct.pack( + "II16sQQQQiiII", + linkedit_cmd, + linkedit_cmdsize, + linkedit_segname_raw, + linkedit_vmaddr, + linkedit_vmsize, + linkedit_fileoff, + linkedit_filesize, + linkedit_maxprot, + linkedit_initprot, + linkedit_nsects, + linkedit_flags, + ) + file.seek(linkedit_offset) + file.write(new_linkedit) + + +def write_bundle_data( + output, + old_bundle_base_offset: int, + new_bundle_base_offset: int, + bundle: BundleManifest, +) -> int: + # Write bundle data + bundle_header_offset = bundle.write(output) + total_size = output.tell() + + # Patch the header position + offset = file_data.find(hashlib.sha256(b".net core bundle\n").digest()) + output.seek(offset - 8) + output.write(struct.pack("q", bundle_header_offset)) + + return total_size - new_bundle_base_offset + + +input_directory: Path = Path(args.input_directory) +content_directory: Path = Path(os.path.join(args.input_directory, "Contents")) +executable_path: Path = Path(os.path.join(content_directory, args.executable_sub_path)) + + +def get_path_related_to_other_path(a: Path, b: Path) -> str: + temp = b + + parts = [] + + while temp != a: + temp = temp.parent + parts.append(temp.name) + + parts.remove(parts[-1]) + parts.reverse() + + return "/".join(parts) + + +def get_path_related_to_target_exec(input_directory: Path, path: Path): + return "@executable_path/../" + get_path_related_to_other_path( + input_directory, path + ) + + +search_path = [ + Path(os.path.join(content_directory, "Frameworks")), + Path(os.path.join(content_directory, "Resources/lib")), +] + + +for path in content_directory.rglob("**/*.dylib"): + current_search_path = [path.parent] + current_search_path.extend(search_path) + + fixup_dylib( + path, + get_path_related_to_target_exec(content_directory, path), + current_search_path, + content_directory, + ) + +for path in content_directory.rglob("**/*.so"): + current_search_path = [path.parent] + current_search_path.extend(search_path) + + fixup_dylib( + path, + get_path_related_to_target_exec(content_directory, path), + current_search_path, + content_directory, + ) + + +with open(executable_path, "rb") as input: + file_data = input.read() + + +(bundle_base_offset, bundle_header_offset, bundle) = get_dotnet_bundle_data(file_data) + +add_dylib_rpath(executable_path, "@executable_path/../Frameworks/") + +# Recent "vanilla" version of LLVM (LLVM 13 and upper) seems to really dislike how .NET package its assemblies. +# As a result, after execution of install_name_tool it will have "fixed" the symtab resulting in a missing .NET bundle... +# To mitigate that, we check if the bundle offset inside the binary is valid after install_name_tool and readd .NET bundle if not. +output_file_size = os.stat(executable_path).st_size +if output_file_size < bundle_header_offset: + print("LLVM broke the .NET bundle, readding bundle data...") + with open(executable_path, "r+b") as output: + file_data = output.read() + bundle_data_size = write_bundle_data( + output, bundle_base_offset, output_file_size, bundle + ) + + # Now patch the __LINKEDIT section + new_size = output_file_size + bundle_data_size + fixup_linkedit(output, file_data, new_size) diff --git a/distribution/macos/construct_universal_dylib.py b/distribution/macos/construct_universal_dylib.py new file mode 100644 index 000000000..b6c3770c6 --- /dev/null +++ b/distribution/macos/construct_universal_dylib.py @@ -0,0 +1,95 @@ +import argparse +import os +from pathlib import Path +import platform +import shutil +import subprocess + +parser = argparse.ArgumentParser( + description="Construct Universal dylibs for nuget package" +) +parser.add_argument( + "arm64_input_directory", help="ARM64 Input directory containing dylibs" +) +parser.add_argument( + "x86_64_input_directory", help="x86_64 Input directory containing dylibs" +) +parser.add_argument("output_directory", help="Output directory") +parser.add_argument("rglob", help="rglob") + +args = parser.parse_args() + +# Use Apple LLVM on Darwin, otherwise standard LLVM. +if platform.system() == "Darwin": + LIPO = "lipo" +else: + LIPO = shutil.which("llvm-lipo") + + if LIPO is None: + for llvm_ver in [15, 14, 13]: + lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}") + if lipo_path is not None: + LIPO = lipo_path + break + +if LIPO is None: + raise Exception("Cannot find a valid location for LLVM lipo!") + +arm64_input_directory: Path = Path(args.arm64_input_directory) +x86_64_input_directory: Path = Path(args.x86_64_input_directory) +output_directory: Path = Path(args.output_directory) +rglob = args.rglob + + +def get_new_name( + input_directory: Path, output_directory: str, input_dylib_path: Path +) -> Path: + input_component = str(input_dylib_path).replace(str(input_directory), "")[1:] + return Path(os.path.join(output_directory, input_component)) + + +def is_fat_file(dylib_path: Path) -> str: + res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode( + "utf-8" + ) + + return not res.split("\n")[0].startswith("Non-fat file") + + +def construct_universal_dylib( + arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path +): + if output_dylib_path.exists() or output_dylib_path.is_symlink(): + os.remove(output_dylib_path) + + os.makedirs(output_dylib_path.parent, exist_ok=True) + + if arm64_input_dylib_path.is_symlink(): + os.symlink( + os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path + ) + else: + if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists(): + with open(output_dylib_path, "wb") as dst: + with open(arm64_input_dylib_path, "rb") as src: + dst.write(src.read()) + else: + subprocess.check_call( + [ + LIPO, + str(arm64_input_dylib_path.absolute()), + str(x86_64_input_dylib_path.absolute()), + "-output", + str(output_dylib_path.absolute()), + "-create", + ] + ) + + +print(rglob) +for path in arm64_input_directory.rglob("**/*.dylib"): + construct_universal_dylib( + path, + get_new_name(arm64_input_directory, x86_64_input_directory, path), + get_new_name(arm64_input_directory, output_directory, path), + ) diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh new file mode 100755 index 000000000..8076303cb --- /dev/null +++ b/distribution/macos/create_app_bundle.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e + +PUBLISH_DIRECTORY=$1 +OUTPUT_DIRECTORY=$2 +ENTITLEMENTS_FILE_PATH=$3 + +APP_BUNDLE_DIRECTORY=$OUTPUT_DIRECTORY/Ryujinx.app + +rm -rf $APP_BUNDLE_DIRECTORY +mkdir -p $APP_BUNDLE_DIRECTORY/Contents +mkdir $APP_BUNDLE_DIRECTORY/Contents/Frameworks +mkdir $APP_BUNDLE_DIRECTORY/Contents/MacOS +mkdir $APP_BUNDLE_DIRECTORY/Contents/Resources + +# Copy executables first +cp $PUBLISH_DIRECTORY/Ryujinx.Ava $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx +chmod u+x $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx + +# Then all libraries +cp $PUBLISH_DIRECTORY/*.dylib $APP_BUNDLE_DIRECTORY/Contents/Frameworks + +# Then resources +cp Info.plist $APP_BUNDLE_DIRECTORY/Contents +cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns +cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources + +echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo + +# Fixup libraries and executable +python3 bundle_fix_up.py $APP_BUNDLE_DIRECTORY MacOS/Ryujinx + +# Now sign it +if ! [ -x "$(command -v codesign)" ]; +then + if ! [ -x "$(command -v rcodesign)" ]; + then + echo "Cannot find rcodesign on your system, please install rcodesign." + exit 1 + fi + + # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. + # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" + echo "Usign rcodesign for ad-hoc signing" + rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $APP_BUNDLE_DIRECTORY +else + echo "Usign codesign for ad-hoc signing" + codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $APP_BUNDLE_DIRECTORY +fi + diff --git a/distribution/macos/create_macos_release.sh b/distribution/macos/create_macos_release.sh new file mode 100755 index 000000000..545baf20e --- /dev/null +++ b/distribution/macos/create_macos_release.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +set -e + +if [ "$#" -ne 6 ]; then + echo "usage " + exit 1 +fi + +mkdir -p $1 +mkdir -p $2 +mkdir -p $3 + +BASE_DIR=$(readlink -f $1) +TEMP_DIRECTORY=$(readlink -f $2) +OUTPUT_DIRECTORY=$(readlink -f $3) +ENTITLEMENTS_FILE_PATH=$(readlink -f $4) +VERSION=$5 +SOURCE_REVISION_ID=$6 + +RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar +ARM64_APP_BUNDLE=$TEMP_DIRECTORY/output_arm64/Ryujinx.app +X64_APP_BUNDLE=$TEMP_DIRECTORY/output_x64/Ryujinx.app +UNIVERSAL_APP_BUNDLE=$OUTPUT_DIRECTORY/Ryujinx.app +EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx + +rm -rf $TEMP_DIRECTORY +mkdir -p $TEMP_DIRECTORY + +DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID -p:ExtraDefineConstants=DISABLE_UPDATER --self-contained true" + +dotnet restore +dotnet build -c Release Ryujinx.Ava +dotnet publish -c Release -r osx-arm64 -o $TEMP_DIRECTORY/publish_arm64 $DOTNET_COMMON_ARGS Ryujinx.Ava +dotnet publish -c Release -r osx-x64 -o $TEMP_DIRECTORY/publish_x64 $DOTNET_COMMON_ARGS Ryujinx.Ava + +# Get ride of the support library for ARMeilleur for x64 (that's only for arm64) +rm -rf $TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib + +# Get ride of libsoundio from arm64 builds as we don't have a arm64 variant +# TODO: remove this once done +rm -rf $TEMP_DIRECTORY/publish_arm64/libsoundio.dylib + +pushd $BASE_DIR/distribution/macos +./create_app_bundle.sh $TEMP_DIRECTORY/publish_x64 $TEMP_DIRECTORY/output_x64 $ENTITLEMENTS_FILE_PATH +./create_app_bundle.sh $TEMP_DIRECTORY/publish_arm64 $TEMP_DIRECTORY/output_arm64 $ENTITLEMENTS_FILE_PATH +popd + +rm -rf $UNIVERSAL_APP_BUNDLE +mkdir -p $OUTPUT_DIRECTORY + +# Let's copy one of the two different app bundle and remove the executable +cp -R $ARM64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE +rm $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH + +# Make it libraries universal +python3 $BASE_DIR/distribution/macos/construct_universal_dylib.py $ARM64_APP_BUNDLE $X64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE "**/*.dylib" + +if ! [ -x "$(command -v lipo)" ]; +then + if ! [ -x "$(command -v llvm-lipo-14)" ]; + then + LIPO=llvm-lipo + else + LIPO=llvm-lipo-14 + fi +else + LIPO=lipo +fi + +# Make it the executable universal +$LIPO $ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH $X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH -output $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH -create + +# Patch up the Info.plist to have appropriate version +sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist +sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist +rm $UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck + +# Now sign it +if ! [ -x "$(command -v codesign)" ]; +then + if ! [ -x "$(command -v rcodesign)" ]; + then + echo "Cannot find rcodesign on your system, please install rcodesign." + exit 1 + fi + + # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. + # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" + echo "Usign rcodesign for ad-hoc signing" + rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $UNIVERSAL_APP_BUNDLE +else + echo "Usign codesign for ad-hoc signing" + codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $UNIVERSAL_APP_BUNDLE +fi + +echo "Creating archive" +pushd $OUTPUT_DIRECTORY +tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf $RELEASE_TAR_FILE_NAME Ryujinx.app 1> /dev/null +python3 $BASE_DIR/distribution/misc/add_tar_exec.py $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx" +gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz +rm $RELEASE_TAR_FILE_NAME +popd + +echo "Done" diff --git a/distribution/macos/entitlements.xml b/distribution/macos/entitlements.xml new file mode 100644 index 000000000..bf3185071 --- /dev/null +++ b/distribution/macos/entitlements.xml @@ -0,0 +1,23 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.disable-executable-page-protection + + com.apple.security.cs.debugger + + com.apple.security.get-task-allow + + com.apple.security.hypervisor + + + diff --git a/distribution/misc/add_tar_exec.py b/distribution/misc/add_tar_exec.py new file mode 100644 index 000000000..fe659c161 --- /dev/null +++ b/distribution/misc/add_tar_exec.py @@ -0,0 +1,24 @@ +import argparse +from io import BytesIO +import tarfile + +parser = argparse.ArgumentParser( + description="Add the main binary to a tar and force it to be executable" +) +parser.add_argument("input_tar_file", help="input tar file") +parser.add_argument("main_binary_path", help="Main executable path") +parser.add_argument("main_binary_tar_path", help="Main executable tar path") + +args = parser.parse_args() +input_tar_file = args.input_tar_file +main_binary_path = args.main_binary_path +main_binary_tar_path = args.main_binary_tar_path + +with open(main_binary_path, "rb") as f: + with tarfile.open(input_tar_file, "a") as tar: + data = f.read() + tar_info = tarfile.TarInfo(main_binary_tar_path) + tar_info.mode = 0o755 + tar_info.size = len(data) + + tar.addfile(tar_info, BytesIO(data)) From 458452279cee03bfe1bbf2c3daf3fc9722b03a74 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 1 Dec 2022 15:30:13 +0000 Subject: [PATCH 132/737] GPU: Track buffer migrations and flush source on incomplete copy (#3952) * Track buffer migrations and flush source on incomplete copy Makes sure that the modified range list is always from the latest iteration of the buffer, and flushes earlier iterations of a buffer if the data has not been migrated yet. * Cleanup 1 * Reduce cost for redundant signal checks on Vulkan * Only inherit the range list if there are pending ranges. * Fix OpenGL * Address Feedback * Whoops --- Ryujinx.Graphics.GAL/IRenderer.cs | 1 + .../Multithreading/ThreadedRenderer.cs | 5 + Ryujinx.Graphics.Gpu/GpuContext.cs | 41 ++- Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 41 ++- Ryujinx.Graphics.Gpu/Memory/BufferCache.cs | 2 +- .../Memory/BufferMigration.cs | 125 +++++++++ .../Memory/BufferModifiedRangeList.cs | 244 +++++++++++++----- Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 5 + Ryujinx.Graphics.OpenGL/Sync.cs | 31 +++ Ryujinx.Graphics.Vulkan/SyncManager.cs | 38 ++- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 5 + 11 files changed, 451 insertions(+), 87 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs index cf67f648b..c72320a2f 100644 --- a/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/Ryujinx.Graphics.GAL/IRenderer.cs @@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL ReadOnlySpan GetBufferData(BufferHandle buffer, int offset, int size); Capabilities GetCapabilities(); + ulong GetCurrentSync(); HardwareInfo GetHardwareInfo(); IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info); diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 7a625af38..a9e3b75c4 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -338,6 +338,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading return box.Result; } + public ulong GetCurrentSync() + { + return _baseRenderer.GetCurrentSync(); + } + public HardwareInfo GetHardwareInfo() { return _baseRenderer.GetHardwareInfo(); diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index 5913ac947..52733165b 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -69,6 +69,12 @@ namespace Ryujinx.Graphics.Gpu ///
    internal List SyncpointActions { get; } + /// + /// Buffer migrations that are currently in-flight. These are checked whenever sync is created to determine if buffer migration + /// copies have completed on the GPU, and their data can be freed. + /// + internal List BufferMigrations { get; } + /// /// Queue with deferred actions that must run on the render thread. /// @@ -90,6 +96,7 @@ namespace Ryujinx.Graphics.Gpu public event Action ShaderCacheStateChanged; private Thread _gpuThread; + private bool _pendingSync; ///
    /// Creates a new instance of the GPU emulation context. @@ -109,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu SyncActions = new List(); SyncpointActions = new List(); + BufferMigrations = new List(); DeferredActions = new Queue(); @@ -273,6 +281,17 @@ namespace Ryujinx.Graphics.Gpu SequenceNumber++; } + /// + /// Registers a buffer migration. These are checked to see if they can be disposed when the sync number increases, + /// and the migration copy has completed. + /// + /// The buffer migration + internal void RegisterBufferMigration(BufferMigration migration) + { + BufferMigrations.Add(migration); + _pendingSync = true; + } + /// /// Registers an action to be performed the next time a syncpoint is incremented. /// This will also ensure a host sync object is created, and is incremented. @@ -288,6 +307,7 @@ namespace Ryujinx.Graphics.Gpu else { SyncActions.Add(action); + _pendingSync = true; } } @@ -298,7 +318,24 @@ namespace Ryujinx.Graphics.Gpu /// True if host sync is being created by a syncpoint public void CreateHostSyncIfNeeded(bool syncpoint) { - if (SyncActions.Count > 0 || (syncpoint && SyncpointActions.Count > 0)) + if (BufferMigrations.Count > 0) + { + ulong currentSyncNumber = Renderer.GetCurrentSync(); + + for (int i = 0; i < BufferMigrations.Count; i++) + { + BufferMigration migration = BufferMigrations[i]; + long diff = (long)(currentSyncNumber - migration.SyncNumber); + + if (diff >= 0) + { + migration.Dispose(); + BufferMigrations.RemoveAt(i--); + } + } + } + + if (_pendingSync || (syncpoint && SyncpointActions.Count > 0)) { Renderer.CreateSync(SyncNumber); @@ -317,6 +354,8 @@ namespace Ryujinx.Graphics.Gpu SyncActions.Clear(); SyncpointActions.Clear(); } + + _pendingSync = false; } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 1e49f6bfa..842249f34 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -65,6 +65,8 @@ namespace Ryujinx.Graphics.Gpu.Memory private bool _useGranular; private bool _syncActionRegistered; + private int _referenceCount = 1; + /// /// Creates a new instance of the buffer. /// @@ -229,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (_modifiedRanges == null) { - _modifiedRanges = new BufferModifiedRangeList(_context); + _modifiedRanges = new BufferModifiedRangeList(_context, this, Flush); } } @@ -290,7 +292,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The buffer to inherit from public void InheritModifiedRanges(Buffer from) { - if (from._modifiedRanges != null) + if (from._modifiedRanges != null && from._modifiedRanges.HasRanges) { if (from._syncActionRegistered && !_syncActionRegistered) { @@ -310,17 +312,9 @@ namespace Ryujinx.Graphics.Gpu.Memory } }; - if (_modifiedRanges == null) - { - _modifiedRanges = from._modifiedRanges; - _modifiedRanges.ReregisterRanges(registerRangeAction); + EnsureRangeList(); - from._modifiedRanges = null; - } - else - { - _modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction); - } + _modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction); } } @@ -456,7 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (ranges != null) { (address, size) = PageAlign(address, size); - ranges.WaitForAndGetRanges(address, size, Flush); + ranges.WaitForAndFlushRanges(address, size); } }, true); } @@ -508,6 +502,25 @@ namespace Ryujinx.Graphics.Gpu.Memory UnmappedSequence++; } + /// + /// Increments the buffer reference count. + /// + public void IncrementReferenceCount() + { + _referenceCount++; + } + + /// + /// Decrements the buffer reference count. + /// + public void DecrementReferenceCount() + { + if (--_referenceCount == 0) + { + DisposeData(); + } + } + /// /// Disposes the host buffer's data, not its tracking handles. /// @@ -528,7 +541,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _memoryTrackingGranular?.Dispose(); _memoryTracking?.Dispose(); - DisposeData(); + DecrementReferenceCount(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index a523c76fb..00f590831 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.Gpu.Memory buffer.CopyTo(newBuffer, dstOffset); newBuffer.InheritModifiedRanges(buffer); - buffer.DisposeData(); + buffer.DecrementReferenceCount(); } newBuffer.SynchronizeMemory(address, newSize); diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs b/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs new file mode 100644 index 000000000..e020c0c39 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs @@ -0,0 +1,125 @@ +using System; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + /// + /// A record of when buffer data was copied from one buffer to another, along with the SyncNumber when the migration will be complete. + /// Keeps the source buffer alive for data flushes until the migration is complete. + /// + internal class BufferMigration : IDisposable + { + /// + /// The offset for the migrated region. + /// + private readonly ulong _offset; + + /// + /// The size for the migrated region. + /// + private readonly ulong _size; + + /// + /// The buffer that was migrated from. + /// + private readonly Buffer _buffer; + + /// + /// The source range action, to be called on overlap with an unreached sync number. + /// + private readonly Action _sourceRangeAction; + + /// + /// The source range list. + /// + private readonly BufferModifiedRangeList _source; + + /// + /// The destination range list. This range list must be updated when flushing the source. + /// + public readonly BufferModifiedRangeList Destination; + + /// + /// The sync number that needs to be reached for this migration to be removed. This is set to the pending sync number on creation. + /// + public readonly ulong SyncNumber; + + /// + /// Creates a record for a buffer migration. + /// + /// The source buffer for this migration + /// The flush action for the source buffer + /// The modified range list for the source buffer + /// The modified range list for the destination buffer + /// The sync number for when the migration is complete + public BufferMigration( + Buffer buffer, + Action sourceRangeAction, + BufferModifiedRangeList source, + BufferModifiedRangeList dest, + ulong syncNumber) + { + _offset = buffer.Address; + _size = buffer.Size; + _buffer = buffer; + _sourceRangeAction = sourceRangeAction; + _source = source; + Destination = dest; + SyncNumber = syncNumber; + } + + /// + /// Determine if the given range overlaps this migration, and has not been completed yet. + /// + /// Start offset + /// Range size + /// The sync number that was waited on + /// True if overlapping and in progress, false otherwise + public bool Overlaps(ulong offset, ulong size, ulong syncNumber) + { + ulong end = offset + size; + ulong destEnd = _offset + _size; + long syncDiff = (long)(syncNumber - SyncNumber); // syncNumber is less if the copy has not completed. + + return !(end <= _offset || offset >= destEnd) && syncDiff < 0; + } + + /// + /// Determine if the given range matches this migration. + /// + /// Start offset + /// Range size + /// True if the range exactly matches, false otherwise + public bool FullyMatches(ulong offset, ulong size) + { + return _offset == offset && _size == size; + } + + /// + /// Perform the migration source's range action on the range provided, clamped to the bounds of the source buffer. + /// + /// Start offset + /// Range size + /// Current sync number + /// The modified range list that originally owned this range + public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferModifiedRangeList parent) + { + ulong end = offset + size; + end = Math.Min(_offset + _size, end); + offset = Math.Max(_offset, offset); + + size = end - offset; + + _source.RangeActionWithMigration(offset, size, syncNumber, parent, _sourceRangeAction); + } + + /// + /// Removes this reference to the range list, potentially allowing for the source buffer to be disposed. + /// + public void Dispose() + { + Destination.RemoveMigration(this); + + _buffer.DecrementReferenceCount(); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index 07dbd2094..d0230b629 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -1,6 +1,8 @@ -using Ryujinx.Common.Pools; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Pools; using Ryujinx.Memory.Range; using System; +using System.Collections.Generic; using System.Linq; namespace Ryujinx.Graphics.Gpu.Memory @@ -30,17 +32,24 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public ulong SyncNumber { get; internal set; } + /// + /// The range list that originally owned this range. + /// + public BufferModifiedRangeList Parent { get; internal set; } + /// /// Creates a new instance of a modified range. /// /// Start address of the range /// Size of the range in bytes /// The GPU sync number at the time of creation - public BufferModifiedRange(ulong address, ulong size, ulong syncNumber) + /// The range list that owns this range + public BufferModifiedRange(ulong address, ulong size, ulong syncNumber, BufferModifiedRangeList parent) { Address = address; Size = size; SyncNumber = syncNumber; + Parent = parent; } /// @@ -63,16 +72,39 @@ namespace Ryujinx.Graphics.Gpu.Memory private const int BackingInitialSize = 8; private GpuContext _context; + private Buffer _parent; + private Action _flushAction; + + private List _sources; + private BufferMigration _migrationTarget; private object _lock = new object(); + /// + /// Whether the modified range list has any entries or not. + /// + public bool HasRanges + { + get + { + lock (_lock) + { + return Count > 0; + } + } + } + /// /// Creates a new instance of a modified range list. /// /// GPU context that the buffer range list belongs to - public BufferModifiedRangeList(GpuContext context) : base(BackingInitialSize) + /// The parent buffer that owns this range list + /// The flush action for the parent buffer + public BufferModifiedRangeList(GpuContext context, Buffer parent, Action flushAction) : base(BackingInitialSize) { _context = context; + _parent = parent; + _flushAction = flushAction; } /// @@ -142,6 +174,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { // Region already exists. Just update the existing sync number. overlap.SyncNumber = syncNumber; + overlap.Parent = this; return; } @@ -152,18 +185,18 @@ namespace Ryujinx.Graphics.Gpu.Memory { // A split item must be created behind this overlap. - Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber)); + Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent)); } if (overlap.Address < endAddress && overlap.EndAddress > endAddress) { // A split item must be created after this overlap. - Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber)); + Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent)); } } - Add(new BufferModifiedRange(address, size, syncNumber)); + Add(new BufferModifiedRange(address, size, syncNumber, this)); } } @@ -207,9 +240,102 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Performs the given range action, or one from a migration that overlaps and has not synced yet. + /// + /// The offset to pass to the action + /// The size to pass to the action + /// The sync number that has been reached + /// The modified range list that originally owned this range + /// The action to perform + public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferModifiedRangeList parent, Action rangeAction) + { + bool firstSource = true; + + if (parent != this) + { + lock (_lock) + { + if (_sources != null) + { + foreach (BufferMigration source in _sources) + { + if (source.Overlaps(offset, size, syncNumber)) + { + if (firstSource && !source.FullyMatches(offset, size)) + { + // Perform this buffer's action first. The migrations will run after. + rangeAction(offset, size); + } + + source.RangeActionWithMigration(offset, size, syncNumber, parent); + + firstSource = false; + } + } + } + } + } + + if (firstSource) + { + // No overlapping migrations, or they are not meant for this range, flush the data using the given action. + rangeAction(offset, size); + } + } + + /// + /// Removes modified ranges ready by the sync number from the list, and flushes their buffer data within a given address range. + /// + /// Overlapping ranges to check + /// Number of overlapping ranges + /// The highest difference between an overlapping range's sync number and the current one + /// The current sync number + /// The start address of the flush range + /// The end address of the flush range + private void RemoveRangesAndFlush( + BufferModifiedRange[] overlaps, + int rangeCount, + long highestDiff, + ulong currentSync, + ulong address, + ulong endAddress) + { + lock (_lock) + { + if (_migrationTarget == null) + { + ulong waitSync = currentSync + (ulong)highestDiff; + + for (int i = 0; i < rangeCount; i++) + { + BufferModifiedRange overlap = overlaps[i]; + + long diff = (long)(overlap.SyncNumber - currentSync); + + if (diff <= highestDiff) + { + ulong clampAddress = Math.Max(address, overlap.Address); + ulong clampEnd = Math.Min(endAddress, overlap.EndAddress); + + ClearPart(overlap, clampAddress, clampEnd); + + RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, overlap.Parent, _flushAction); + } + } + + return; + } + } + + // There is a migration target to call instead. This can't be changed after set so accessing it outside the lock is fine. + + _migrationTarget.Destination.RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress); + } + /// /// Gets modified ranges within the specified region, waits on ones from a previous sync number, - /// and then fires the given action for each range individually. + /// and then fires the flush action for each range individually. /// /// /// This function assumes it is called from the background thread. @@ -218,8 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Start address to query /// Size to query - /// The action to call for each modified range - public void WaitForAndGetRanges(ulong address, ulong size, Action rangeAction) + public void WaitForAndFlushRanges(ulong address, ulong size) { ulong endAddress = address + size; ulong currentSync = _context.SyncNumber; @@ -231,10 +356,23 @@ namespace Ryujinx.Graphics.Gpu.Memory // Range list must be consistent for this operation lock (_lock) { - rangeCount = FindOverlapsNonOverlapping(address, size, ref overlaps); + if (_migrationTarget != null) + { + rangeCount = -1; + } + else + { + rangeCount = FindOverlapsNonOverlapping(address, size, ref overlaps); + } } - if (rangeCount == 0) + if (rangeCount == -1) + { + _migrationTarget.Destination.WaitForAndFlushRanges(address, size); + + return; + } + else if (rangeCount == 0) { return; } @@ -264,47 +402,37 @@ namespace Ryujinx.Graphics.Gpu.Memory // Wait for the syncpoint. _context.Renderer.WaitSync(currentSync + (ulong)highestDiff); - // Flush and remove all regions with the older syncpoint. - lock (_lock) - { - for (int i = 0; i < rangeCount; i++) - { - BufferModifiedRange overlap = overlaps[i]; - - long diff = (long)(overlap.SyncNumber - currentSync); - - if (diff <= highestDiff) - { - ulong clampAddress = Math.Max(address, overlap.Address); - ulong clampEnd = Math.Min(endAddress, overlap.EndAddress); - - ClearPart(overlap, clampAddress, clampEnd); - - rangeAction(clampAddress, clampEnd - clampAddress); - } - } - } + RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress); } /// /// Inherit ranges from another modified range list. /// /// The range list to inherit from - /// The action to call for each modified range - public void InheritRanges(BufferModifiedRangeList ranges, Action rangeAction) + /// The action to call for each modified range + public void InheritRanges(BufferModifiedRangeList ranges, Action registerRangeAction) { BufferModifiedRange[] inheritRanges; lock (ranges._lock) { - inheritRanges = ranges.ToArray(); - } + BufferMigration migration = new(ranges._parent, ranges._flushAction, ranges, this, _context.SyncNumber); - lock (_lock) - { - foreach (BufferModifiedRange range in inheritRanges) + ranges._parent.IncrementReferenceCount(); + ranges._migrationTarget = migration; + + _context.RegisterBufferMigration(migration); + + inheritRanges = ranges.ToArray(); + + lock (_lock) { - Add(range); + (_sources ??= new List()).Add(migration); + + foreach (BufferModifiedRange range in inheritRanges) + { + Add(range); + } } } @@ -313,44 +441,20 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (range.SyncNumber != currentSync) { - rangeAction(range.Address, range.Size); + registerRangeAction(range.Address, range.Size); } } } /// - /// Calls the given action for modified ranges that aren't from the current sync number. + /// Removes a source buffer migration, indicating its copy has completed. /// - /// The action to call for each modified range - public void ReregisterRanges(Action rangeAction) + /// The migration to remove + public void RemoveMigration(BufferMigration migration) { - ref var ranges = ref ThreadStaticArray.Get(); - int count; - - // Range list must be consistent for this operation. lock (_lock) { - count = Count; - if (ranges.Length < count) - { - Array.Resize(ref ranges, count); - } - - int i = 0; - foreach (BufferModifiedRange range in this) - { - ranges[i++] = range; - } - } - - ulong currentSync = _context.SyncNumber; - for (int i = 0; i < count; i++) - { - BufferModifiedRange range = ranges[i]; - if (range.SyncNumber != currentSync) - { - rangeAction(range.Address, range.Size); - } + _sources.Remove(migration); } } @@ -362,12 +466,12 @@ namespace Ryujinx.Graphics.Gpu.Memory if (overlap.Address < address) { - Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber)); + Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent)); } if (overlap.EndAddress > endAddress) { - Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber)); + Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent)); } } diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index e26fe6b6c..63c96fa23 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -238,6 +238,11 @@ namespace Ryujinx.Graphics.OpenGL _sync.Wait(id); } + public ulong GetCurrentSync() + { + return _sync.GetCurrent(); + } + public void Screenshot() { _window.ScreenCaptureRequested = true; diff --git a/Ryujinx.Graphics.OpenGL/Sync.cs b/Ryujinx.Graphics.OpenGL/Sync.cs index de94fd317..58818e6a7 100644 --- a/Ryujinx.Graphics.OpenGL/Sync.cs +++ b/Ryujinx.Graphics.OpenGL/Sync.cs @@ -40,6 +40,37 @@ namespace Ryujinx.Graphics.OpenGL } } + public ulong GetCurrent() + { + lock (_handles) + { + ulong lastHandle = _firstHandle; + + foreach (SyncHandle handle in _handles) + { + lock (handle) + { + if (handle.Handle == IntPtr.Zero) + { + continue; + } + + if (handle.ID > lastHandle) + { + WaitSyncStatus syncResult = GL.ClientWaitSync(handle.Handle, _syncFlags, 0); + + if (syncResult == WaitSyncStatus.AlreadySignaled) + { + lastHandle = handle.ID; + } + } + } + } + + return lastHandle; + } + } + public void Wait(ulong id) { SyncHandle result = null; diff --git a/Ryujinx.Graphics.Vulkan/SyncManager.cs b/Ryujinx.Graphics.Vulkan/SyncManager.cs index a39862d0c..35e3adf1e 100644 --- a/Ryujinx.Graphics.Vulkan/SyncManager.cs +++ b/Ryujinx.Graphics.Vulkan/SyncManager.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Vulkan { public ulong ID; public MultiFenceHolder Waitable; + public bool Signalled; } private ulong _firstHandle = 0; @@ -45,6 +46,37 @@ namespace Ryujinx.Graphics.Vulkan } } + 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(_gd.Api, _device, 0); + if (signaled) + { + lastHandle = handle.ID; + handle.Signalled = true; + } + } + } + } + + return lastHandle; + } + } + public void Wait(ulong id) { SyncHandle result = null; @@ -75,11 +107,15 @@ namespace Ryujinx.Graphics.Vulkan return; } - bool signaled = result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); + bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); if (!signaled) { Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); } + else + { + result.Signalled = true; + } } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 22e303294..5acbe841a 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -565,6 +565,11 @@ namespace Ryujinx.Graphics.Vulkan _syncManager.Wait(id); } + public ulong GetCurrentSync() + { + return _syncManager.GetCurrent(); + } + public void Screenshot() { _window.ScreenCaptureRequested = true; From 456fc04007b7274f6e75d6348c5a4e955f85b488 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Thu, 1 Dec 2022 11:53:13 -0500 Subject: [PATCH 133/737] Better SDL2 Audio Init Error Logging (#3967) * Better SDL2 Audio Init Error Logging * Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs Co-authored-by: Mary-nyan * Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs Co-authored-by: Mary-nyan * Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs Co-authored-by: Mary-nyan * Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs Co-authored-by: Ac_K * Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs Co-authored-by: Ac_K * Update SDL2HardwareDeviceDriver.cs * Update SDL2HardwareDeviceDriver.cs Co-authored-by: Mary-nyan Co-authored-by: Ac_K --- Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs | 5 +++++ Ryujinx.SDL2.Common/SDL2Driver.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index 3953b809e..b190b4c83 100644 --- a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; +using Ryujinx.Common.Logging; using Ryujinx.Memory; using Ryujinx.SDL2.Common; using System; @@ -112,6 +113,9 @@ namespace Ryujinx.Audio.Backends.SDL2 if (device == 0) { + Logger.Error?.Print(LogClass.Application, + $"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\""); + return 0; } @@ -119,6 +123,7 @@ namespace Ryujinx.Audio.Backends.SDL2 if (!isValid) { + Logger.Error?.Print(LogClass.Application, "SDL2 open audio device is not valid"); SDL_CloseAudioDevice(device); return 0; diff --git a/Ryujinx.SDL2.Common/SDL2Driver.cs b/Ryujinx.SDL2.Common/SDL2Driver.cs index 4a30523c3..9d8dacf84 100644 --- a/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -69,7 +69,7 @@ namespace Ryujinx.SDL2.Common if (SDL_Init(SdlInitFlags) != 0) { - string errorMessage = $"SDL2 initlaization failed with error \"{SDL_GetError()}\""; + string errorMessage = $"SDL2 initialization failed with error \"{SDL_GetError()}\""; Logger.Error?.Print(LogClass.Application, errorMessage); From ce92e8cd043c6beeca6969920bc5e3b8eff5f57a Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 1 Dec 2022 19:11:56 +0100 Subject: [PATCH 134/737] chore: Update Silk.NET to 2.16.0 (#3953) --- Ryujinx.Ava/Ryujinx.Ava.csproj | 6 +- Ryujinx.Graphics.Vulkan/BufferHolder.cs | 42 +++++------ Ryujinx.Graphics.Vulkan/BufferManager.cs | 36 +++++----- Ryujinx.Graphics.Vulkan/CommandBufferPool.cs | 4 +- .../DescriptorSetUpdater.cs | 2 +- Ryujinx.Graphics.Vulkan/EnumConversion.cs | 72 +++++++++---------- Ryujinx.Graphics.Vulkan/FormatCapabilities.cs | 14 ++-- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 8 +-- Ryujinx.Graphics.Vulkan/HelperShader.cs | 62 ++++++++-------- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 2 +- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 44 ++++++------ Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 14 ++-- .../PipelineDynamicState.cs | 12 ++-- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 2 +- .../PipelineLayoutFactory.cs | 18 ++--- .../Queries/BufferedQuery.cs | 6 +- .../Ryujinx.Graphics.Vulkan.csproj | 6 +- Ryujinx.Graphics.Vulkan/ShaderCollection.cs | 10 +-- Ryujinx.Graphics.Vulkan/TextureCopy.cs | 28 ++++---- Ryujinx.Graphics.Vulkan/TextureStorage.cs | 60 ++++++++-------- Ryujinx.Graphics.Vulkan/TextureView.cs | 56 +++++++-------- .../VulkanInitialization.cs | 36 +++++----- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 10 +-- Ryujinx.Graphics.Vulkan/Window.cs | 36 +++++----- 24 files changed, 293 insertions(+), 293 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 0edad85dd..c7b0eadc5 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -33,9 +33,9 @@ - - - + + + diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs index f5c478db7..6288f16fb 100644 --- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Graphics.Vulkan private const int MaxUpdateBufferSize = 0x10000; public const AccessFlags DefaultAccessFlags = - AccessFlags.AccessIndirectCommandReadBit | - AccessFlags.AccessShaderReadBit | - AccessFlags.AccessShaderWriteBit | - AccessFlags.AccessTransferReadBit | - AccessFlags.AccessTransferWriteBit | - AccessFlags.AccessUniformReadBit; + AccessFlags.IndirectCommandReadBit | + AccessFlags.ShaderReadBit | + AccessFlags.ShaderWriteBit | + AccessFlags.TransferReadBit | + AccessFlags.TransferWriteBit | + AccessFlags.UniformReadBit; private readonly VulkanRenderer _gd; private readonly Device _device; @@ -87,9 +87,9 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CmdPipelineBarrier( commandBuffer, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - DependencyFlags.DependencyDeviceGroupBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.AllCommandsBit, + DependencyFlags.DeviceGroupBit, 1, memoryBarrier, 0, @@ -273,9 +273,9 @@ namespace Ryujinx.Graphics.Vulkan cbs.CommandBuffer, dstBuffer, BufferHolder.DefaultAccessFlags, - AccessFlags.AccessTransferWriteBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, dstOffset, data.Length); @@ -293,10 +293,10 @@ namespace Ryujinx.Graphics.Vulkan _gd, cbs.CommandBuffer, dstBuffer, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, BufferHolder.DefaultAccessFlags, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, dstOffset, data.Length); @@ -320,9 +320,9 @@ namespace Ryujinx.Graphics.Vulkan cbs.CommandBuffer, dstBuffer, BufferHolder.DefaultAccessFlags, - AccessFlags.AccessTransferWriteBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, dstOffset, size); @@ -334,10 +334,10 @@ namespace Ryujinx.Graphics.Vulkan gd, cbs.CommandBuffer, dstBuffer, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, BufferHolder.DefaultAccessFlags, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, dstOffset, size); } diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index 432898a9c..f32403712 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -10,28 +10,28 @@ namespace Ryujinx.Graphics.Vulkan class BufferManager : IDisposable { private const MemoryPropertyFlags DefaultBufferMemoryFlags = - MemoryPropertyFlags.MemoryPropertyHostVisibleBit | - MemoryPropertyFlags.MemoryPropertyHostCoherentBit | - MemoryPropertyFlags.MemoryPropertyHostCachedBit; + MemoryPropertyFlags.HostVisibleBit | + MemoryPropertyFlags.HostCoherentBit | + MemoryPropertyFlags.HostCachedBit; private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = - MemoryPropertyFlags.MemoryPropertyDeviceLocalBit; + MemoryPropertyFlags.DeviceLocalBit; private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags = - MemoryPropertyFlags.MemoryPropertyHostVisibleBit | - MemoryPropertyFlags.MemoryPropertyHostCoherentBit | - MemoryPropertyFlags.MemoryPropertyDeviceLocalBit; + MemoryPropertyFlags.HostVisibleBit | + MemoryPropertyFlags.HostCoherentBit | + MemoryPropertyFlags.DeviceLocalBit; private const BufferUsageFlags DefaultBufferUsageFlags = - BufferUsageFlags.BufferUsageTransferSrcBit | - BufferUsageFlags.BufferUsageTransferDstBit | - BufferUsageFlags.BufferUsageUniformTexelBufferBit | - BufferUsageFlags.BufferUsageStorageTexelBufferBit | - BufferUsageFlags.BufferUsageUniformBufferBit | - BufferUsageFlags.BufferUsageStorageBufferBit | - BufferUsageFlags.BufferUsageIndexBufferBit | - BufferUsageFlags.BufferUsageVertexBufferBit | - BufferUsageFlags.BufferUsageTransformFeedbackBufferBitExt; + BufferUsageFlags.TransferSrcBit | + BufferUsageFlags.TransferDstBit | + BufferUsageFlags.UniformTexelBufferBit | + BufferUsageFlags.StorageTexelBufferBit | + BufferUsageFlags.UniformBufferBit | + BufferUsageFlags.StorageBufferBit | + BufferUsageFlags.IndexBufferBit | + BufferUsageFlags.VertexBufferBit | + BufferUsageFlags.TransformFeedbackBufferBitExt; private readonly PhysicalDevice _physicalDevice; private readonly Device _device; @@ -76,11 +76,11 @@ namespace Ryujinx.Graphics.Vulkan if (forConditionalRendering && gd.Capabilities.SupportsConditionalRendering) { - usage |= BufferUsageFlags.BufferUsageConditionalRenderingBitExt; + usage |= BufferUsageFlags.ConditionalRenderingBitExt; } else if (gd.Capabilities.SupportsIndirectParameters) { - usage |= BufferUsageFlags.BufferUsageIndirectBufferBit; + usage |= BufferUsageFlags.IndirectBufferBit; } var bufferCreateInfo = new BufferCreateInfo() diff --git a/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs index a27b63a09..c77b00405 100644 --- a/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs +++ b/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs @@ -71,8 +71,8 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.CommandPoolCreateInfo, QueueFamilyIndex = queueFamilyIndex, - Flags = CommandPoolCreateFlags.CommandPoolCreateTransientBit | - CommandPoolCreateFlags.CommandPoolCreateResetCommandBufferBit + Flags = CommandPoolCreateFlags.TransientBit | + CommandPoolCreateFlags.ResetCommandBufferBit }; api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError(); diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 4cf9ce877..9564d7ebc 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan } else if (texture is TextureView view) { - view.Storage.InsertBarrier(cbs, AccessFlags.AccessShaderReadBit, stage.ConvertToPipelineStageFlags()); + view.Storage.InsertBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); _textureRefs[binding] = view.GetImageView(); _samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler(); diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 9d9be65eb..0164ef85c 100644 --- a/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Graphics.Vulkan { return stage switch { - ShaderStage.Vertex => ShaderStageFlags.ShaderStageVertexBit, - ShaderStage.Geometry => ShaderStageFlags.ShaderStageGeometryBit, - ShaderStage.TessellationControl => ShaderStageFlags.ShaderStageTessellationControlBit, - ShaderStage.TessellationEvaluation => ShaderStageFlags.ShaderStageTessellationEvaluationBit, - ShaderStage.Fragment => ShaderStageFlags.ShaderStageFragmentBit, - ShaderStage.Compute => ShaderStageFlags.ShaderStageComputeBit, + ShaderStage.Vertex => ShaderStageFlags.VertexBit, + ShaderStage.Geometry => ShaderStageFlags.GeometryBit, + ShaderStage.TessellationControl => ShaderStageFlags.TessellationControlBit, + ShaderStage.TessellationEvaluation => ShaderStageFlags.TessellationEvaluationBit, + ShaderStage.Fragment => ShaderStageFlags.FragmentBit, + ShaderStage.Compute => ShaderStageFlags.ComputeBit, _ => LogInvalidAndReturn(stage, nameof(ShaderStage), (ShaderStageFlags)0) }; } @@ -26,12 +26,12 @@ namespace Ryujinx.Graphics.Vulkan { return stage switch { - ShaderStage.Vertex => PipelineStageFlags.PipelineStageVertexShaderBit, - ShaderStage.Geometry => PipelineStageFlags.PipelineStageGeometryShaderBit, - ShaderStage.TessellationControl => PipelineStageFlags.PipelineStageTessellationControlShaderBit, - ShaderStage.TessellationEvaluation => PipelineStageFlags.PipelineStageTessellationEvaluationShaderBit, - ShaderStage.Fragment => PipelineStageFlags.PipelineStageFragmentShaderBit, - ShaderStage.Compute => PipelineStageFlags.PipelineStageComputeShaderBit, + ShaderStage.Vertex => PipelineStageFlags.VertexShaderBit, + ShaderStage.Geometry => PipelineStageFlags.GeometryShaderBit, + ShaderStage.TessellationControl => PipelineStageFlags.TessellationControlShaderBit, + ShaderStage.TessellationEvaluation => PipelineStageFlags.TessellationEvaluationShaderBit, + ShaderStage.Fragment => PipelineStageFlags.FragmentShaderBit, + ShaderStage.Compute => PipelineStageFlags.ComputeShaderBit, _ => LogInvalidAndReturn(stage, nameof(ShaderStage), (PipelineStageFlags)0) }; } @@ -112,10 +112,10 @@ namespace Ryujinx.Graphics.Vulkan { return face switch { - Face.Back => CullModeFlags.CullModeBackBit, - Face.Front => CullModeFlags.CullModeFrontBit, - Face.FrontAndBack => CullModeFlags.CullModeFrontAndBack, - _ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.CullModeBackBit) + Face.Back => CullModeFlags.BackBit, + Face.Front => CullModeFlags.FrontBit, + Face.FrontAndBack => CullModeFlags.FrontAndBack, + _ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit) }; } @@ -223,14 +223,14 @@ namespace Ryujinx.Graphics.Vulkan { Target.Texture1D or Target.Texture1DArray or - Target.TextureBuffer => ImageType.ImageType1D, + Target.TextureBuffer => ImageType.Type1D, Target.Texture2D or Target.Texture2DArray or Target.Texture2DMultisample or Target.Cubemap or - Target.CubemapArray => ImageType.ImageType2D, - Target.Texture3D => ImageType.ImageType3D, - _ => LogInvalidAndReturn(target, nameof(Target), ImageType.ImageType2D) + Target.CubemapArray => ImageType.Type2D, + Target.Texture3D => ImageType.Type3D, + _ => LogInvalidAndReturn(target, nameof(Target), ImageType.Type2D) }; } @@ -238,14 +238,14 @@ namespace Ryujinx.Graphics.Vulkan { return target switch { - Target.Texture1D => ImageViewType.ImageViewType1D, - Target.Texture2D or Target.Texture2DMultisample => ImageViewType.ImageViewType2D, - Target.Texture3D => ImageViewType.ImageViewType3D, - Target.Texture1DArray => ImageViewType.ImageViewType1DArray, - Target.Texture2DArray => ImageViewType.ImageViewType2DArray, - Target.Cubemap => ImageViewType.Cube, - Target.CubemapArray => ImageViewType.CubeArray, - _ => LogInvalidAndReturn(target, nameof(Target), ImageViewType.ImageViewType2D) + Target.Texture1D => ImageViewType.Type1D, + Target.Texture2D or Target.Texture2DMultisample => ImageViewType.Type2D, + Target.Texture3D => ImageViewType.Type3D, + Target.Texture1DArray => ImageViewType.Type1DArray, + Target.Texture2DArray => ImageViewType.Type2DArray, + Target.Cubemap => ImageViewType.TypeCube, + Target.CubemapArray => ImageViewType.TypeCubeArray, + _ => LogInvalidAndReturn(target, nameof(Target), ImageViewType.Type2D) }; } @@ -253,12 +253,12 @@ namespace Ryujinx.Graphics.Vulkan { return format switch { - GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.ImageAspectDepthBit, - GAL.Format.S8Uint => ImageAspectFlags.ImageAspectStencilBit, + GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.DepthBit, + GAL.Format.S8Uint => ImageAspectFlags.StencilBit, GAL.Format.D24UnormS8Uint or GAL.Format.D32FloatS8Uint or - GAL.Format.S8UintD24Unorm => ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit, - _ => ImageAspectFlags.ImageAspectColorBit + GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, + _ => ImageAspectFlags.ColorBit }; } @@ -266,12 +266,12 @@ namespace Ryujinx.Graphics.Vulkan { return format switch { - GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.ImageAspectDepthBit, - GAL.Format.S8Uint => ImageAspectFlags.ImageAspectStencilBit, + GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.DepthBit, + GAL.Format.S8Uint => ImageAspectFlags.StencilBit, GAL.Format.D24UnormS8Uint or GAL.Format.D32FloatS8Uint or - GAL.Format.S8UintD24Unorm => depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.ImageAspectStencilBit : ImageAspectFlags.ImageAspectDepthBit, - _ => ImageAspectFlags.ImageAspectColorBit + GAL.Format.S8UintD24Unorm => depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.StencilBit : ImageAspectFlags.DepthBit, + _ => ImageAspectFlags.ColorBit }; } diff --git a/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index 0cac08bf8..7019dfd91 100644 --- a/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -83,22 +83,22 @@ namespace Ryujinx.Graphics.Vulkan { var format = FormatTable.GetFormat(srcFormat); - var requiredFeatures = FormatFeatureFlags.FormatFeatureSampledImageBit | - FormatFeatureFlags.FormatFeatureTransferSrcBit | - FormatFeatureFlags.FormatFeatureTransferDstBit; + var requiredFeatures = FormatFeatureFlags.SampledImageBit | + FormatFeatureFlags.TransferSrcBit | + FormatFeatureFlags.TransferDstBit; if (srcFormat.IsDepthOrStencil()) { - requiredFeatures |= FormatFeatureFlags.FormatFeatureDepthStencilAttachmentBit; + requiredFeatures |= FormatFeatureFlags.DepthStencilAttachmentBit; } else if (srcFormat.IsRtColorCompatible()) { - requiredFeatures |= FormatFeatureFlags.FormatFeatureColorAttachmentBit; + requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit; } if (srcFormat.IsImageCompatible()) { - requiredFeatures |= FormatFeatureFlags.FormatFeatureStorageImageBit; + requiredFeatures |= FormatFeatureFlags.StorageImageBit; } if (!OptimalFormatSupports(requiredFeatures, srcFormat) || (IsD24S8(srcFormat) && VulkanConfiguration.ForceD24S8Unsupported)) @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan { var format = FormatTable.GetFormat(srcFormat); - if (!BufferFormatSupports(FormatFeatureFlags.FormatFeatureVertexBufferBit, srcFormat) || + if (!BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, srcFormat) || (IsRGB16IntFloat(srcFormat) && VulkanConfiguration.ForceRGB16IntFloatUnsupported)) { // The format is not supported. Can we convert it to an alternative format? diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 0dadbf9cf..43ee6f5c4 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -190,14 +190,14 @@ namespace Ryujinx.Graphics.Vulkan for (int index = 0; index < _colors.Length; index++) { _colors[index].Storage.SetModification( - AccessFlags.AccessColorAttachmentWriteBit, - PipelineStageFlags.PipelineStageColorAttachmentOutputBit); + AccessFlags.ColorAttachmentWriteBit, + PipelineStageFlags.ColorAttachmentOutputBit); } } _depthStencil?.Storage.SetModification( - AccessFlags.AccessDepthStencilAttachmentWriteBit, - PipelineStageFlags.PipelineStageColorAttachmentOutputBit); + AccessFlags.DepthStencilAttachmentWriteBit, + PipelineStageFlags.ColorAttachmentOutputBit); } } } diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 076a3baab..ce3734093 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -349,8 +349,8 @@ namespace Ryujinx.Graphics.Vulkan var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value; var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value; - var access = supportsUint8 ? AccessFlags.AccessShaderWriteBit : AccessFlags.AccessTransferWriteBit; - var stage = supportsUint8 ? PipelineStageFlags.PipelineStageComputeShaderBit : PipelineStageFlags.PipelineStageTransferBit; + var access = supportsUint8 ? AccessFlags.ShaderWriteBit : AccessFlags.TransferWriteBit; + var stage = supportsUint8 ? PipelineStageFlags.ComputeShaderBit : PipelineStageFlags.TransferBit; BufferHolder.InsertBufferBarrier( gd, @@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.Vulkan dstBuffer, BufferHolder.DefaultAccessFlags, access, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.AllCommandsBit, stage, 0, newSize); @@ -420,7 +420,7 @@ namespace Ryujinx.Graphics.Vulkan access, BufferHolder.DefaultAccessFlags, stage, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.AllCommandsBit, 0, newSize); } @@ -484,9 +484,9 @@ namespace Ryujinx.Graphics.Vulkan cbs.CommandBuffer, dstBuffer, BufferHolder.DefaultAccessFlags, - AccessFlags.AccessTransferWriteBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, 0, convertedCount * outputIndexSize); @@ -499,10 +499,10 @@ namespace Ryujinx.Graphics.Vulkan gd, cbs.CommandBuffer, dstBuffer, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, BufferHolder.DefaultAccessFlags, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, 0, convertedCount * outputIndexSize); } @@ -548,10 +548,10 @@ namespace Ryujinx.Graphics.Vulkan cbs.CommandBuffer, src.GetImage().Get(cbs).Value, TextureStorage.DefaultAccessMask, - AccessFlags.AccessShaderReadBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageComputeShaderBit, - ImageAspectFlags.ImageAspectColorBit, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.ComputeShaderBit, + ImageAspectFlags.ColorBit, src.FirstLayer + srcLayer, src.FirstLevel, depth, @@ -610,11 +610,11 @@ namespace Ryujinx.Graphics.Vulkan gd.Api, cbs.CommandBuffer, dst.GetImage().Get(cbs).Value, - AccessFlags.AccessShaderWriteBit, + AccessFlags.ShaderWriteBit, TextureStorage.DefaultAccessMask, - PipelineStageFlags.PipelineStageComputeShaderBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - ImageAspectFlags.ImageAspectColorBit, + PipelineStageFlags.ComputeShaderBit, + PipelineStageFlags.AllCommandsBit, + ImageAspectFlags.ColorBit, dst.FirstLayer + dstLayer, dst.FirstLevel, depth, @@ -770,9 +770,9 @@ namespace Ryujinx.Graphics.Vulkan cbs.CommandBuffer, srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value, BufferHolder.DefaultAccessFlags, - AccessFlags.AccessShaderReadBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageComputeShaderBit, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.ComputeShaderBit, srcIndirectBufferOffset, indirectDataSize); @@ -786,10 +786,10 @@ namespace Ryujinx.Graphics.Vulkan gd, cbs.CommandBuffer, patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value, - AccessFlags.AccessShaderWriteBit, - AccessFlags.AccessIndirectCommandReadBit, - PipelineStageFlags.PipelineStageComputeShaderBit, - PipelineStageFlags.PipelineStageDrawIndirectBit, + AccessFlags.ShaderWriteBit, + AccessFlags.IndirectCommandReadBit, + PipelineStageFlags.ComputeShaderBit, + PipelineStageFlags.DrawIndirectBit, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize); @@ -798,9 +798,9 @@ namespace Ryujinx.Graphics.Vulkan cbs.CommandBuffer, dstBuffer, BufferHolder.DefaultAccessFlags, - AccessFlags.AccessTransferWriteBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, 0, convertedCount * outputIndexSize); @@ -814,10 +814,10 @@ namespace Ryujinx.Graphics.Vulkan gd, cbs.CommandBuffer, dstBuffer, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, BufferHolder.DefaultAccessFlags, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, 0, convertedCount * outputIndexSize); diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 0b05ef92a..eea4e60b3 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan return default; } - bool map = flags.HasFlag(MemoryPropertyFlags.MemoryPropertyHostVisibleBit); + bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit); return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 594ed5e78..8679bc840 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -128,14 +128,14 @@ namespace Ryujinx.Graphics.Vulkan MemoryBarrier memoryBarrier = new MemoryBarrier() { SType = StructureType.MemoryBarrier, - SrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit, - DstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit + SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit }; Gd.Api.CmdPipelineBarrier( CommandBuffer, - PipelineStageFlags.PipelineStageFragmentShaderBit, - PipelineStageFlags.PipelineStageFragmentShaderBit, + PipelineStageFlags.FragmentShaderBit, + PipelineStageFlags.FragmentShaderBit, 0, 1, memoryBarrier, @@ -161,9 +161,9 @@ namespace Ryujinx.Graphics.Vulkan Cbs.CommandBuffer, dst, BufferHolder.DefaultAccessFlags, - AccessFlags.AccessTransferWriteBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, offset, size); @@ -173,10 +173,10 @@ namespace Ryujinx.Graphics.Vulkan Gd, Cbs.CommandBuffer, dst, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, BufferHolder.DefaultAccessFlags, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, offset, size); } @@ -196,7 +196,7 @@ namespace Ryujinx.Graphics.Vulkan BeginRenderPass(); var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha)); - var attachment = new ClearAttachment(ImageAspectFlags.ImageAspectColorBit, (uint)index, clearValue); + var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); @@ -219,11 +219,11 @@ namespace Ryujinx.Graphics.Vulkan BeginRenderPass(); var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue)); - var flags = depthMask ? ImageAspectFlags.ImageAspectDepthBit : 0; + var flags = depthMask ? ImageAspectFlags.DepthBit : 0; if (stencilMask != 0) { - flags |= ImageAspectFlags.ImageAspectStencilBit; + flags |= ImageAspectFlags.StencilBit; } var attachment = new ClearAttachment(flags, 0, clearValue); @@ -238,13 +238,13 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.MemoryBarrier, SrcAccessMask = BufferHolder.DefaultAccessFlags, - DstAccessMask = AccessFlags.AccessIndirectCommandReadBit + DstAccessMask = AccessFlags.IndirectCommandReadBit }; Gd.Api.CmdPipelineBarrier( CommandBuffer, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageDrawIndirectBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.DrawIndirectBit, 0, 1, memoryBarrier, @@ -624,7 +624,7 @@ namespace Ryujinx.Graphics.Vulkan var oldViewports = DynamicState.Viewports; var oldViewportsCount = _newState.ViewportsCount; - _newState.CullMode = CullModeFlags.CullModeNone; + _newState.CullMode = CullModeFlags.None; _newState.StencilTestEnable = false; _newState.DepthTestEnable = false; _newState.DepthWriteEnable = false; @@ -737,7 +737,7 @@ namespace Ryujinx.Graphics.Vulkan public void SetFaceCulling(bool enable, Face face) { - _newState.CullMode = enable ? face.Convert() : CullModeFlags.CullModeNone; + _newState.CullMode = enable ? face.Convert() : CullModeFlags.None; SignalStateChange(); } @@ -1200,14 +1200,14 @@ namespace Ryujinx.Graphics.Vulkan MemoryBarrier memoryBarrier = new MemoryBarrier() { SType = StructureType.MemoryBarrier, - SrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit, - DstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit + SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit }; Gd.Api.CmdPipelineBarrier( CommandBuffer, - PipelineStageFlags.PipelineStageFragmentShaderBit, - PipelineStageFlags.PipelineStageFragmentShaderBit, + PipelineStageFlags.FragmentShaderBit, + PipelineStageFlags.FragmentShaderBit, 0, 1, memoryBarrier, diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index a01c5ad72..0f7dfe9b2 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Graphics.Vulkan { static class PipelineConverter { - private const AccessFlags SubpassSrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessColorAttachmentWriteBit; - private const AccessFlags SubpassDstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessShaderReadBit; + private const AccessFlags SubpassSrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit | AccessFlags.ColorAttachmentWriteBit; + private const AccessFlags SubpassDstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit | AccessFlags.ShaderReadBit; public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device) { @@ -129,8 +129,8 @@ namespace Ryujinx.Graphics.Vulkan return new SubpassDependency( 0, 0, - PipelineStageFlags.PipelineStageAllGraphicsBit, - PipelineStageFlags.PipelineStageAllGraphicsBit, + PipelineStageFlags.AllGraphicsBit, + PipelineStageFlags.AllGraphicsBit, SubpassSrcAccessMask, SubpassDstAccessMask, 0); @@ -143,8 +143,8 @@ namespace Ryujinx.Graphics.Vulkan null, 0, 0, - PipelineStageFlags.PipelineStageAllGraphicsBit, - PipelineStageFlags.PipelineStageAllGraphicsBit, + PipelineStageFlags.AllGraphicsBit, + PipelineStageFlags.AllGraphicsBit, SubpassSrcAccessMask, SubpassDstAccessMask, 0); @@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Vulkan // It is assumed that Dynamic State is enabled when this conversion is used. - pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.CullModeNone; + pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None; pipeline.DepthBoundsTestEnable = false; // Not implemented. diff --git a/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index ae29be51c..b4d6e95c7 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -145,12 +145,12 @@ namespace Ryujinx.Graphics.Vulkan private void RecordStencilMasks(Vk api, CommandBuffer commandBuffer) { - api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.StencilFaceBackBit, _backCompareMask); - api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.StencilFaceBackBit, _backWriteMask); - api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.StencilFaceBackBit, _backReference); - api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.StencilFaceFrontBit, _frontCompareMask); - api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.StencilFaceFrontBit, _frontWriteMask); - api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.StencilFaceFrontBit, _frontReference); + api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask); + api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask); + api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceBackBit, _backReference); + api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontCompareMask); + api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontWriteMask); + api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference); } private void RecordViewport(Vk api, CommandBuffer commandBuffer) diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 36f2b1b2a..56a491842 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan if (Gd.Capabilities.SupportsConditionalRendering) { var buffer = evt.GetBuffer().Get(Cbs, 0, sizeof(long)).Value; - var flags = isEqual ? ConditionalRenderingFlagsEXT.ConditionalRenderingInvertedBitExt : 0; + var flags = isEqual ? ConditionalRenderingFlagsEXT.InvertedBitExt : 0; var conditionalRenderingBeginInfo = new ConditionalRenderingBeginInfoEXT() { diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index a064df7a9..e4e2d3349 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.Vulkan static class PipelineLayoutFactory { private const ShaderStageFlags SupportBufferStages = - ShaderStageFlags.ShaderStageVertexBit | - ShaderStageFlags.ShaderStageFragmentBit | - ShaderStageFlags.ShaderStageComputeBit; + ShaderStageFlags.VertexBit | + ShaderStageFlags.FragmentBit | + ShaderStageFlags.ComputeBit; public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout) { @@ -42,11 +42,11 @@ namespace Ryujinx.Graphics.Vulkan var stageFlags = stage switch { - 1 => ShaderStageFlags.ShaderStageFragmentBit, - 2 => ShaderStageFlags.ShaderStageGeometryBit, - 3 => ShaderStageFlags.ShaderStageTessellationControlBit, - 4 => ShaderStageFlags.ShaderStageTessellationEvaluationBit, - _ => ShaderStageFlags.ShaderStageVertexBit | ShaderStageFlags.ShaderStageComputeBit + 1 => ShaderStageFlags.FragmentBit, + 2 => ShaderStageFlags.GeometryBit, + 3 => ShaderStageFlags.TessellationControlBit, + 4 => ShaderStageFlags.TessellationEvaluationBit, + _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit }; void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip) @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.DescriptorSetLayoutCreateInfo, PBindings = uLayoutBindings, BindingCount = (uint)uCount, - Flags = usePd ? DescriptorSetLayoutCreateFlags.DescriptorSetLayoutCreatePushDescriptorBitKhr : 0 + Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0 }; var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index a53e02a1f..4cf258eb7 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries if (_isSupported) { QueryPipelineStatisticFlags flags = type == CounterType.PrimitivesGenerated ? - QueryPipelineStatisticFlags.QueryPipelineStatisticGeometryShaderPrimitivesBit : 0; + QueryPipelineStatisticFlags.GeometryShaderPrimitivesBit : 0; var queryPoolCreateInfo = new QueryPoolCreateInfo() { @@ -175,11 +175,11 @@ namespace Ryujinx.Graphics.Vulkan.Queries { var buffer = _buffer.GetBuffer(cbs.CommandBuffer, true).Get(cbs, 0, sizeof(long)).Value; - QueryResultFlags flags = QueryResultFlags.QueryResultWaitBit; + QueryResultFlags flags = QueryResultFlags.ResultWaitBit; if (!_result32Bit) { - flags |= QueryResultFlags.QueryResult64Bit; + flags |= QueryResultFlags.Result64Bit; } _api.CmdCopyQueryPoolResults( diff --git a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index 4f7c8c456..e4e199e96 100644 --- a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index bf2874d7c..1694049c9 100644 --- a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -82,14 +82,14 @@ namespace Ryujinx.Graphics.Vulkan stages |= 1u << shader.StageFlags switch { - ShaderStageFlags.ShaderStageFragmentBit => 1, - ShaderStageFlags.ShaderStageGeometryBit => 2, - ShaderStageFlags.ShaderStageTessellationControlBit => 3, - ShaderStageFlags.ShaderStageTessellationEvaluationBit => 4, + ShaderStageFlags.FragmentBit => 1, + ShaderStageFlags.GeometryBit => 2, + ShaderStageFlags.TessellationControlBit => 3, + ShaderStageFlags.TessellationEvaluationBit => 4, _ => 0 }; - if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit) + if (shader.StageFlags == ShaderStageFlags.ComputeBit) { IsCompute = true; } diff --git a/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/Ryujinx.Graphics.Vulkan/TextureCopy.cs index d14c2a324..4403df155 100644 --- a/Ryujinx.Graphics.Vulkan/TextureCopy.cs +++ b/Ryujinx.Graphics.Vulkan/TextureCopy.cs @@ -60,9 +60,9 @@ namespace Ryujinx.Graphics.Vulkan commandBuffer, srcImage, TextureStorage.DefaultAccessMask, - AccessFlags.AccessTransferReadBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, srcAspectFlags, srcLayer, srcLevel, @@ -103,10 +103,10 @@ namespace Ryujinx.Graphics.Vulkan api, commandBuffer, dstImage, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, TextureStorage.DefaultAccessMask, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, dstAspectFlags, dstLayer, dstLevel, @@ -285,9 +285,9 @@ namespace Ryujinx.Graphics.Vulkan commandBuffer, srcImage, TextureStorage.DefaultAccessMask, - AccessFlags.AccessTransferReadBit, - PipelineStageFlags.PipelineStageAllCommandsBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, srcAspect, srcViewLayer + srcLayer, srcViewLevel + srcLevel, @@ -345,10 +345,10 @@ namespace Ryujinx.Graphics.Vulkan api, commandBuffer, dstImage, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, TextureStorage.DefaultAccessMask, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, dstAspect, dstViewLayer + dstLayer, dstViewLevel + dstLevel, @@ -370,8 +370,8 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.SubpassDescriptionDepthStencilResolve, PDepthStencilResolveAttachment = &dsResolveAttachmentReference, - DepthResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit, - StencilResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit + DepthResolveMode = ResolveModeFlags.SampleZeroBit, + StencilResolveMode = ResolveModeFlags.SampleZeroBit }; var subpass = new SubpassDescription2() diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index c4ebaef3b..16a490486 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -12,22 +12,22 @@ namespace Ryujinx.Graphics.Vulkan class TextureStorage : IDisposable { private const MemoryPropertyFlags DefaultImageMemoryFlags = - MemoryPropertyFlags.MemoryPropertyDeviceLocalBit; + MemoryPropertyFlags.DeviceLocalBit; private const ImageUsageFlags DefaultUsageFlags = - ImageUsageFlags.ImageUsageSampledBit | - ImageUsageFlags.ImageUsageTransferSrcBit | - ImageUsageFlags.ImageUsageTransferDstBit; + ImageUsageFlags.SampledBit | + ImageUsageFlags.TransferSrcBit | + ImageUsageFlags.TransferDstBit; public const AccessFlags DefaultAccessMask = - AccessFlags.AccessShaderReadBit | - AccessFlags.AccessShaderWriteBit | - AccessFlags.AccessColorAttachmentReadBit | - AccessFlags.AccessColorAttachmentWriteBit | - AccessFlags.AccessDepthStencilAttachmentReadBit | - AccessFlags.AccessDepthStencilAttachmentWriteBit | - AccessFlags.AccessTransferReadBit | - AccessFlags.AccessTransferWriteBit; + AccessFlags.ShaderReadBit | + AccessFlags.ShaderWriteBit | + AccessFlags.ColorAttachmentReadBit | + AccessFlags.ColorAttachmentWriteBit | + AccessFlags.DepthStencilAttachmentReadBit | + AccessFlags.DepthStencilAttachmentWriteBit | + AccessFlags.TransferReadBit | + AccessFlags.TransferWriteBit; private readonly VulkanRenderer _gd; @@ -83,32 +83,32 @@ namespace Ryujinx.Graphics.Vulkan if (info.Format.IsDepthOrStencil()) { - usage |= ImageUsageFlags.ImageUsageDepthStencilAttachmentBit; + usage |= ImageUsageFlags.DepthStencilAttachmentBit; } else if (info.Format.IsRtColorCompatible()) { - usage |= ImageUsageFlags.ImageUsageColorAttachmentBit; + usage |= ImageUsageFlags.ColorAttachmentBit; } if (info.Format.IsImageCompatible()) { - usage |= ImageUsageFlags.ImageUsageStorageBit; + usage |= ImageUsageFlags.StorageBit; } - var flags = ImageCreateFlags.ImageCreateMutableFormatBit; + var flags = ImageCreateFlags.CreateMutableFormatBit; // This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube. bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray; bool cubeCompatible = gd.IsAmdGcn ? isCube : (info.Width == info.Height && layers >= 6); - if (type == ImageType.ImageType2D && cubeCompatible) + if (type == ImageType.Type2D && cubeCompatible) { - flags |= ImageCreateFlags.ImageCreateCubeCompatibleBit; + flags |= ImageCreateFlags.CreateCubeCompatibleBit; } - if (type == ImageType.ImageType3D) + if (type == ImageType.Type3D) { - flags |= ImageCreateFlags.ImageCreate2DArrayCompatibleBit; + flags |= ImageCreateFlags.Create2DArrayCompatibleBit; } var imageCreateInfo = new ImageCreateInfo() @@ -290,8 +290,8 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CmdPipelineBarrier( cbs.CommandBuffer, - PipelineStageFlags.PipelineStageTopOfPipeBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TopOfPipeBit, + PipelineStageFlags.AllCommandsBit, 0, 0, null, @@ -308,9 +308,9 @@ namespace Ryujinx.Graphics.Vulkan public static SampleCountFlags ConvertToSampleCountFlags(uint samples) { - if (samples == 0 || samples > (uint)SampleCountFlags.SampleCount64Bit) + if (samples == 0 || samples > (uint)SampleCountFlags.Count64Bit) { - return SampleCountFlags.SampleCount1Bit; + return SampleCountFlags.Count1Bit; } // Round up to the nearest power of two. @@ -428,7 +428,7 @@ namespace Ryujinx.Graphics.Vulkan public void InsertBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) { - if (_lastModificationAccess != AccessFlags.AccessNoneKhr) + if (_lastModificationAccess != AccessFlags.NoneKhr) { ImageAspectFlags aspectFlags; @@ -436,20 +436,20 @@ namespace Ryujinx.Graphics.Vulkan { if (_info.Format == GAL.Format.S8Uint) { - aspectFlags = ImageAspectFlags.ImageAspectStencilBit; + aspectFlags = ImageAspectFlags.StencilBit; } else if (_info.Format == GAL.Format.D16Unorm || _info.Format == GAL.Format.D32Float) { - aspectFlags = ImageAspectFlags.ImageAspectDepthBit; + aspectFlags = ImageAspectFlags.DepthBit; } else { - aspectFlags = ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit; + aspectFlags = ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit; } } else { - aspectFlags = ImageAspectFlags.ImageAspectColorBit; + aspectFlags = ImageAspectFlags.ColorBit; } TextureView.InsertImageBarrier( @@ -466,7 +466,7 @@ namespace Ryujinx.Graphics.Vulkan _info.GetLayers(), _info.Levels); - _lastModificationAccess = AccessFlags.AccessNoneKhr; + _lastModificationAccess = AccessFlags.NoneKhr; } } diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index f47e431b1..c58c9fc5d 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -126,7 +126,7 @@ namespace Ryujinx.Graphics.Vulkan { subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth); - _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.ImageViewType2DArray); + _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray); } Valid = true; @@ -322,8 +322,8 @@ namespace Ryujinx.Graphics.Vulkan return; } - else if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.FormatFeatureBlitSrcBit, srcFormat) && - _gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.FormatFeatureBlitDstBit, dstFormat)) + else if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitSrcBit, srcFormat) && + _gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitDstBit, dstFormat)) { TextureCopy.Blit( _gd.Api, @@ -402,8 +402,8 @@ namespace Ryujinx.Graphics.Vulkan layers, levels, linearFilter, - ImageAspectFlags.ImageAspectColorBit, - ImageAspectFlags.ImageAspectColorBit); + ImageAspectFlags.ColorBit, + ImageAspectFlags.ColorBit); } private static void BlitDepthStencilWithBuffer( @@ -471,10 +471,10 @@ namespace Ryujinx.Graphics.Vulkan gd, cbs.CommandBuffer, srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, - AccessFlags.AccessTransferWriteBit, - AccessFlags.AccessTransferReadBit, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + AccessFlags.TransferReadBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.TransferBit, 0, srcSize); @@ -498,10 +498,10 @@ namespace Ryujinx.Graphics.Vulkan gd.Api, cbs.CommandBuffer, srcTemp.GetImage().Get(cbs).Value, - AccessFlags.AccessTransferWriteBit, - AccessFlags.AccessTransferReadBit, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + AccessFlags.TransferReadBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.TransferBit, aspectFlags, 0, 0, @@ -531,10 +531,10 @@ namespace Ryujinx.Graphics.Vulkan gd.Api, cbs.CommandBuffer, dstTemp.GetImage().Get(cbs).Value, - AccessFlags.AccessTransferWriteBit, - AccessFlags.AccessTransferReadBit, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + AccessFlags.TransferReadBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.TransferBit, aspectFlags, 0, 0, @@ -561,10 +561,10 @@ namespace Ryujinx.Graphics.Vulkan gd, cbs.CommandBuffer, dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, - AccessFlags.AccessTransferWriteBit, - AccessFlags.AccessTransferReadBit, - PipelineStageFlags.PipelineStageTransferBit, - PipelineStageFlags.PipelineStageTransferBit, + AccessFlags.TransferWriteBit, + AccessFlags.TransferReadBit, + PipelineStageFlags.TransferBit, + PipelineStageFlags.TransferBit, 0, dstSize); @@ -585,8 +585,8 @@ namespace Ryujinx.Graphics.Vulkan false); } - SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.ImageAspectDepthBit); - SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.ImageAspectStencilBit); + SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.DepthBit); + SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.StencilBit); } public static unsafe void InsertImageBarrier( @@ -631,7 +631,7 @@ namespace Ryujinx.Graphics.Vulkan private bool SupportsBlitFromD32FS8ToD32FAndS8() { - var formatFeatureFlags = FormatFeatureFlags.FormatFeatureBlitSrcBit | FormatFeatureFlags.FormatFeatureBlitDstBit; + var formatFeatureFlags = FormatFeatureFlags.BlitSrcBit | FormatFeatureFlags.BlitDstBit; return _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.D32Float) && _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.S8Uint); } @@ -903,9 +903,9 @@ namespace Ryujinx.Graphics.Vulkan var aspectFlags = Info.Format.ConvertAspectFlags(); - if (aspectFlags == (ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit)) + if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit)) { - aspectFlags = ImageAspectFlags.ImageAspectDepthBit; + aspectFlags = ImageAspectFlags.DepthBit; } var sl = new ImageSubresourceLayers( @@ -962,9 +962,9 @@ namespace Ryujinx.Graphics.Vulkan { var aspectFlags = Info.Format.ConvertAspectFlags(); - if (aspectFlags == (ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit)) + if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit)) { - aspectFlags = ImageAspectFlags.ImageAspectDepthBit; + aspectFlags = ImageAspectFlags.DepthBit; } var sl = new ImageSubresourceLayers(aspectFlags, (uint)(FirstLevel + dstLevel), (uint)(FirstLayer + dstLayer), 1); diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 190221c78..7813bb816 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -159,20 +159,20 @@ namespace Ryujinx.Graphics.Vulkan } } - if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt)) + if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) { Logger.Error?.Print(LogClass.Gpu, msg); //throw new Exception(msg); } - else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt)) + else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) { Logger.Warning?.Print(LogClass.Gpu, msg); } - else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt)) + else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt)) { Logger.Info?.Print(LogClass.Gpu, msg); } - else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt)) + else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt)) { Logger.Debug?.Print(LogClass.Gpu, msg); } @@ -317,7 +317,7 @@ namespace Ryujinx.Graphics.Vulkan internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount) { - const QueueFlags RequiredFlags = QueueFlags.QueueGraphicsBit | QueueFlags.QueueComputeBit; + const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit; var khrSurface = new KhrSurface(api.Context); @@ -561,24 +561,24 @@ namespace Ryujinx.Graphics.Vulkan var filterLogType = logLevel switch { - GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt, - GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt | - DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt, - GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt | - DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt | - DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt, + GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt, + GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | + DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, + GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | + DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | + DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, _ => throw new ArgumentException($"Invalid log level \"{logLevel}\".") }; var filterLogSeverity = logLevel switch { - GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt, - GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt | - DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt, - GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt | - DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt | - DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt | - DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt, + GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, + GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt | + DebugUtilsMessageSeverityFlagsEXT.WarningBitExt, + GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt | + DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | + DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | + DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, _ => throw new ArgumentException($"Invalid log level \"{logLevel}\".") }; diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 5acbe841a..f711ac9cb 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -341,11 +341,11 @@ namespace Ryujinx.Graphics.Vulkan public unsafe Capabilities GetCapabilities() { FormatFeatureFlags compressedFormatFeatureFlags = - FormatFeatureFlags.FormatFeatureSampledImageBit | - FormatFeatureFlags.FormatFeatureSampledImageFilterLinearBit | - FormatFeatureFlags.FormatFeatureBlitSrcBit | - FormatFeatureFlags.FormatFeatureTransferSrcBit | - FormatFeatureFlags.FormatFeatureTransferDstBit; + FormatFeatureFlags.SampledImageBit | + FormatFeatureFlags.SampledImageFilterLinearBit | + FormatFeatureFlags.BlitSrcBit | + FormatFeatureFlags.TransferSrcBit | + FormatFeatureFlags.TransferDstBit; bool supportsBc123CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, GAL.Format.Bc1RgbaSrgb, diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 71b542044..d37dd7e96 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -109,11 +109,11 @@ namespace Ryujinx.Graphics.Vulkan ImageFormat = surfaceFormat.Format, ImageColorSpace = surfaceFormat.ColorSpace, ImageExtent = extent, - ImageUsage = ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit, + ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit, ImageSharingMode = SharingMode.Exclusive, ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, - CompositeAlpha = CompositeAlphaFlagsKHR.CompositeAlphaOpaqueBitKhr, + CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), Clipped = true, OldSwapchain = oldSwapchain @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Vulkan ComponentSwizzle.B, ComponentSwizzle.A); - var aspectFlags = ImageAspectFlags.ImageAspectColorBit; + var aspectFlags = ImageAspectFlags.ColorBit; var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, 1, 0, 1); @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.ImageViewCreateInfo, Image = swapchainImage, - ViewType = ImageViewType.ImageViewType2D, + ViewType = ImageViewType.Type2D, Format = format, Components = componentMapping, SubresourceRange = subresourceRange @@ -168,12 +168,12 @@ namespace Ryujinx.Graphics.Vulkan { if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined) { - return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.ColorspaceSrgbNonlinearKhr); + return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr); } foreach (var format in availableFormats) { - if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.ColorspaceSrgbNonlinearKhr) + if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr) { return format; } @@ -184,21 +184,21 @@ namespace Ryujinx.Graphics.Vulkan private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) { - if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.PresentModeImmediateKhr)) + if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) { - return PresentModeKHR.PresentModeImmediateKhr; + return PresentModeKHR.ImmediateKhr; } - else if (availablePresentModes.Contains(PresentModeKHR.PresentModeMailboxKhr)) + else if (availablePresentModes.Contains(PresentModeKHR.MailboxKhr)) { - return PresentModeKHR.PresentModeMailboxKhr; + return PresentModeKHR.MailboxKhr; } - else if (availablePresentModes.Contains(PresentModeKHR.PresentModeFifoKhr)) + else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr)) { - return PresentModeKHR.PresentModeFifoKhr; + return PresentModeKHR.FifoKhr; } else { - return PresentModeKHR.PresentModeFifoKhr; + return PresentModeKHR.FifoKhr; } } @@ -254,7 +254,7 @@ namespace Ryujinx.Graphics.Vulkan cbs.CommandBuffer, swapchainImage, 0, - AccessFlags.AccessTransferWriteBit, + AccessFlags.TransferWriteBit, ImageLayout.Undefined, ImageLayout.General); @@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.Vulkan _gd.CommandBufferPool.Return( cbs, stackalloc[] { _imageAvailableSemaphore }, - stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit }, + stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit }, stackalloc[] { _renderFinishedSemaphore }); // TODO: Present queue. @@ -373,7 +373,7 @@ namespace Ryujinx.Graphics.Vulkan ImageLayout srcLayout, ImageLayout dstLayout) { - var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1); + var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, 0, 1); var barrier = new ImageMemoryBarrier() { @@ -390,8 +390,8 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CmdPipelineBarrier( commandBuffer, - PipelineStageFlags.PipelineStageTopOfPipeBit, - PipelineStageFlags.PipelineStageAllCommandsBit, + PipelineStageFlags.TopOfPipeBit, + PipelineStageFlags.AllCommandsBit, 0, 0, null, From 9677ddaa5d8424604bdbf17496f8a878855a118d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 21:54:41 +0000 Subject: [PATCH 135/737] nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3 (#3976) * nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3 Bumps [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) from 1.0.4 to 2.1.3. - [Release notes](https://github.com/SixLabors/ImageSharp/releases) - [Commits](https://github.com/SixLabors/ImageSharp/compare/v1.0.4...v2.1.3) --- updated-dependencies: - dependency-name: SixLabors.ImageSharp dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Update for 2.x changes Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mary --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- Ryujinx.Ava/Ui/Windows/IconColorPicker.cs | 2 +- .../Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs | 4 ++-- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- Ryujinx/Ryujinx.csproj | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index c7b0eadc5..46b24dbdf 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -38,7 +38,7 @@ - + diff --git a/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs b/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs index 7cf4c2ede..14cd68313 100644 --- a/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs +++ b/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Ava.Ui.Windows public static Bgra32[] GetBuffer(Image image) { - return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : new Bgra32[0]; + return image.DangerousTryGetSinglePixelMemory(out var data) ? data.ToArray() : new Bgra32[0]; } private static int GetColorScore(Dictionary dominantColorBin, int maxHitCount, PaletteColor color) diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index 6c0955ecc..09f81f0f3 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -549,12 +549,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard // Convert the pixel format used in the image to the one used in the Switch surface. - if (!_surface.TryGetSinglePixelSpan(out Span pixels)) + if (!_surface.DangerousTryGetSinglePixelMemory(out Memory pixels)) { return; } - _bufferData = MemoryMarshal.AsBytes(pixels).ToArray(); + _bufferData = MemoryMarshal.AsBytes(pixels.Span).ToArray(); Span dataConvert = MemoryMarshal.Cast(_bufferData); Debug.Assert(_bufferData.Length == _surfaceInfo.Size); diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index 1ec92a449..f5d5042f2 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -24,7 +24,7 @@ - + diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 31f130c4a..0fbd7e3a7 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -27,7 +27,7 @@ - + From d692a9b83ebb1c0303f91adaa22cd67852f2386f Mon Sep 17 00:00:00 2001 From: Mary Date: Thu, 1 Dec 2022 23:06:55 +0100 Subject: [PATCH 136/737] Revert "nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3 (#3976)" This reverts commit 9677ddaa5d8424604bdbf17496f8a878855a118d. SixLabors.ImageShar switched to a shady and vague license starting with 2.x without mentioning it on their changelog. As a result we are staying on 1.x (licensed under Apache-2) and will seak an alternative package. --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- Ryujinx.Ava/Ui/Windows/IconColorPicker.cs | 2 +- .../Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs | 4 ++-- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- Ryujinx/Ryujinx.csproj | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 46b24dbdf..c7b0eadc5 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -38,7 +38,7 @@ - + diff --git a/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs b/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs index 14cd68313..7cf4c2ede 100644 --- a/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs +++ b/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Ava.Ui.Windows public static Bgra32[] GetBuffer(Image image) { - return image.DangerousTryGetSinglePixelMemory(out var data) ? data.ToArray() : new Bgra32[0]; + return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : new Bgra32[0]; } private static int GetColorScore(Dictionary dominantColorBin, int maxHitCount, PaletteColor color) diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index 09f81f0f3..6c0955ecc 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -549,12 +549,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard // Convert the pixel format used in the image to the one used in the Switch surface. - if (!_surface.DangerousTryGetSinglePixelMemory(out Memory pixels)) + if (!_surface.TryGetSinglePixelSpan(out Span pixels)) { return; } - _bufferData = MemoryMarshal.AsBytes(pixels.Span).ToArray(); + _bufferData = MemoryMarshal.AsBytes(pixels).ToArray(); Span dataConvert = MemoryMarshal.Cast(_bufferData); Debug.Assert(_bufferData.Length == _surfaceInfo.Size); diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index f5d5042f2..1ec92a449 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -24,7 +24,7 @@ - + diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 0fbd7e3a7..31f130c4a 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -27,7 +27,7 @@ - + From b540ea80d16488d1e5b3eb6ca6b9e93cc663b06f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Thu, 1 Dec 2022 21:31:21 -0500 Subject: [PATCH 137/737] Ava GUI: Make Dialogue More Intuitive (#3955) * Adjust button position and locales * Shortcuts + Highlight default action * Update Locales - Corrections Welcome * Move `Apply` button back to right side * OS Reactive Button layout * Fix reversed boolean :) * Fix accented button styling --- Ryujinx.Ava/Assets/Locales/de_DE.json | 4 +-- Ryujinx.Ava/Assets/Locales/el_GR.json | 4 +-- Ryujinx.Ava/Assets/Locales/en_US.json | 4 +-- Ryujinx.Ava/Assets/Locales/es_ES.json | 4 +-- Ryujinx.Ava/Assets/Locales/fr_FR.json | 4 +-- Ryujinx.Ava/Assets/Locales/it_IT.json | 4 +-- Ryujinx.Ava/Assets/Locales/ja_JP.json | 4 +-- Ryujinx.Ava/Assets/Locales/ko_KR.json | 4 +-- Ryujinx.Ava/Assets/Locales/pl_PL.json | 4 +-- Ryujinx.Ava/Assets/Locales/pt_BR.json | 4 +-- Ryujinx.Ava/Assets/Locales/ru_RU.json | 4 +-- Ryujinx.Ava/Assets/Locales/tr_TR.json | 4 +-- Ryujinx.Ava/Assets/Locales/zh_CN.json | 4 +-- Ryujinx.Ava/Assets/Locales/zh_TW.json | 4 +-- .../Ui/ViewModels/SettingsViewModel.cs | 36 ++++++++++++++++++- Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml | 23 ++++++++---- .../Ui/Windows/SettingsWindow.axaml.cs | 30 ---------------- 17 files changed, 79 insertions(+), 66 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/de_DE.json b/Ryujinx.Ava/Assets/Locales/de_DE.json index cb8d787b8..449f53886 100644 --- a/Ryujinx.Ava/Assets/Locales/de_DE.json +++ b/Ryujinx.Ava/Assets/Locales/de_DE.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Eingabe", "SettingsTabInputEnableDockedMode": "Docked Modus", "SettingsTabInputDirectKeyboardAccess": "Direkter Tastaturzugriff", - "SettingsButtonSave": "Speichern", - "SettingsButtonClose": "Schließen", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Abbrechen", "SettingsButtonApply": "Übernehmen", "ControllerSettingsPlayer": "Spieler", "ControllerSettingsPlayer1": "Spieler 1", diff --git a/Ryujinx.Ava/Assets/Locales/el_GR.json b/Ryujinx.Ava/Assets/Locales/el_GR.json index 4fb8bf567..cd806544e 100644 --- a/Ryujinx.Ava/Assets/Locales/el_GR.json +++ b/Ryujinx.Ava/Assets/Locales/el_GR.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Χειρισμός", "SettingsTabInputEnableDockedMode": "Ενεργοποίηση Docked Mode", "SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο", - "SettingsButtonSave": "Αποθήκευση", - "SettingsButtonClose": "Κλείσιμο", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Ακύρωση", "SettingsButtonApply": "Εφαρμογή", "ControllerSettingsPlayer": "Παίχτης", "ControllerSettingsPlayer1": "Παίχτης 1", diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 86cb44c9a..87a2f50db 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Input", "SettingsTabInputEnableDockedMode": "Docked Mode", "SettingsTabInputDirectKeyboardAccess": "Direct Keyboard Access", - "SettingsButtonSave": "Save", - "SettingsButtonClose": "Close", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Cancel", "SettingsButtonApply": "Apply", "ControllerSettingsPlayer": "Player", "ControllerSettingsPlayer1": "Player 1", diff --git a/Ryujinx.Ava/Assets/Locales/es_ES.json b/Ryujinx.Ava/Assets/Locales/es_ES.json index e6ba35c07..379682eae 100644 --- a/Ryujinx.Ava/Assets/Locales/es_ES.json +++ b/Ryujinx.Ava/Assets/Locales/es_ES.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Entrada", "SettingsTabInputEnableDockedMode": "Modo dock/TV", "SettingsTabInputDirectKeyboardAccess": "Acceso directo al teclado", - "SettingsButtonSave": "Guardar", - "SettingsButtonClose": "Cerrar", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Cancelar", "SettingsButtonApply": "Aplicar", "ControllerSettingsPlayer": "Jugador", "ControllerSettingsPlayer1": "Jugador 1", diff --git a/Ryujinx.Ava/Assets/Locales/fr_FR.json b/Ryujinx.Ava/Assets/Locales/fr_FR.json index 29d26ec88..4b0780d2e 100644 --- a/Ryujinx.Ava/Assets/Locales/fr_FR.json +++ b/Ryujinx.Ava/Assets/Locales/fr_FR.json @@ -159,8 +159,8 @@ "SettingsTabInput": "Contrôles", "SettingsTabInputEnableDockedMode": "Active le mode station d'accueil", "SettingsTabInputDirectKeyboardAccess": "Accès direct au clavier", - "SettingsButtonSave": "Enregistrer", - "SettingsButtonClose": "Fermer", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Annuler", "SettingsButtonApply": "Appliquer", "ControllerSettingsPlayer": "Joueur", "ControllerSettingsPlayer1": "Joueur 1", diff --git a/Ryujinx.Ava/Assets/Locales/it_IT.json b/Ryujinx.Ava/Assets/Locales/it_IT.json index beed9a995..3ab2617c0 100644 --- a/Ryujinx.Ava/Assets/Locales/it_IT.json +++ b/Ryujinx.Ava/Assets/Locales/it_IT.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Input", "SettingsTabInputEnableDockedMode": "Attiva modalità TV", "SettingsTabInputDirectKeyboardAccess": "Accesso diretto alla tastiera", - "SettingsButtonSave": "Salva", - "SettingsButtonClose": "Chiudi", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Cancella", "SettingsButtonApply": "Applica", "ControllerSettingsPlayer": "Giocatore", "ControllerSettingsPlayer1": "Giocatore 1", diff --git a/Ryujinx.Ava/Assets/Locales/ja_JP.json b/Ryujinx.Ava/Assets/Locales/ja_JP.json index 5d95d7d08..549eabb49 100644 --- a/Ryujinx.Ava/Assets/Locales/ja_JP.json +++ b/Ryujinx.Ava/Assets/Locales/ja_JP.json @@ -166,8 +166,8 @@ "SettingsTabInput": "入力", "SettingsTabInputEnableDockedMode": "ドッキングモード", "SettingsTabInputDirectKeyboardAccess": "キーボード直接アクセス", - "SettingsButtonSave": "セーブ", - "SettingsButtonClose": "閉じる", + "SettingsButtonOk": "オーケー", + "SettingsButtonCancel": "キャンセル", "SettingsButtonApply": "適用", "ControllerSettingsPlayer": "プレイヤー", "ControllerSettingsPlayer1": "プレイヤー 1", diff --git a/Ryujinx.Ava/Assets/Locales/ko_KR.json b/Ryujinx.Ava/Assets/Locales/ko_KR.json index cc7232c9f..c1b000b87 100644 --- a/Ryujinx.Ava/Assets/Locales/ko_KR.json +++ b/Ryujinx.Ava/Assets/Locales/ko_KR.json @@ -165,8 +165,8 @@ "SettingsTabInput": "입력", "SettingsTabInputEnableDockedMode": "도킹 모드 활성화", "SettingsTabInputDirectKeyboardAccess": "직접 키보드 액세스", - "SettingsButtonSave": "구하다", - "SettingsButtonClose": "출구", + "SettingsButtonOk": "좋아", + "SettingsButtonCancel": "취소", "SettingsButtonApply": "적용하다", "ControllerSettingsPlayer": "플레이어", "ControllerSettingsPlayer1": "플레이어 1", diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/Ryujinx.Ava/Assets/Locales/pl_PL.json index d7f8a1bcb..5a701c237 100644 --- a/Ryujinx.Ava/Assets/Locales/pl_PL.json +++ b/Ryujinx.Ava/Assets/Locales/pl_PL.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Sterowanie", "SettingsTabInputEnableDockedMode": "Tryb Zadokowany", "SettingsTabInputDirectKeyboardAccess": "Bezpośredni Dostęp do Klawiatury", - "SettingsButtonSave": "Zapisz", - "SettingsButtonClose": "Zamknij", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Anuluj", "SettingsButtonApply": "Zastosuj", "ControllerSettingsPlayer": "Gracz", "ControllerSettingsPlayer1": "Gracz 1", diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json index c49084d89..a8b02f676 100644 --- a/Ryujinx.Ava/Assets/Locales/pt_BR.json +++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Controle", "SettingsTabInputEnableDockedMode": "Habilitar modo TV", "SettingsTabInputDirectKeyboardAccess": "Acesso direto ao teclado", - "SettingsButtonSave": "Salvar", - "SettingsButtonClose": "Fechar", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Cancelar", "SettingsButtonApply": "Aplicar", "ControllerSettingsPlayer": "Jogador", "ControllerSettingsPlayer1": "Jogador 1", diff --git a/Ryujinx.Ava/Assets/Locales/ru_RU.json b/Ryujinx.Ava/Assets/Locales/ru_RU.json index e2788296c..fa74c4b92 100644 --- a/Ryujinx.Ava/Assets/Locales/ru_RU.json +++ b/Ryujinx.Ava/Assets/Locales/ru_RU.json @@ -165,8 +165,8 @@ "SettingsTabInput": "Управление", "SettingsTabInputEnableDockedMode": "Включить режим закрепления", "SettingsTabInputDirectKeyboardAccess": "Прямой доступ с клавиатуры", - "SettingsButtonSave": "Сохранить", - "SettingsButtonClose": "Закрыть", + "SettingsButtonOk": "OK", + "SettingsButtonCancel": "Отмена", "SettingsButtonApply": "Применить", "ControllerSettingsPlayer": "Игрок", "ControllerSettingsPlayer1": "Игрок 1", diff --git a/Ryujinx.Ava/Assets/Locales/tr_TR.json b/Ryujinx.Ava/Assets/Locales/tr_TR.json index 59979de3c..e8713e0c1 100644 --- a/Ryujinx.Ava/Assets/Locales/tr_TR.json +++ b/Ryujinx.Ava/Assets/Locales/tr_TR.json @@ -166,8 +166,8 @@ "SettingsTabInput": "Giriş Yöntemi", "SettingsTabInputEnableDockedMode": "Docked Modunu Etkinleştir", "SettingsTabInputDirectKeyboardAccess": "Doğrudan Klavye Erişimi", - "SettingsButtonSave": "Kaydet", - "SettingsButtonClose": "Kapat", + "SettingsButtonOk": "Tamam", + "SettingsButtonCancel": "İptal", "SettingsButtonApply": "Uygula", "ControllerSettingsPlayer": "Oyuncu", "ControllerSettingsPlayer1": "Oyuncu 1", diff --git a/Ryujinx.Ava/Assets/Locales/zh_CN.json b/Ryujinx.Ava/Assets/Locales/zh_CN.json index 5e52f78ab..09b344e80 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_CN.json +++ b/Ryujinx.Ava/Assets/Locales/zh_CN.json @@ -166,8 +166,8 @@ "SettingsTabInput": "输入", "SettingsTabInputEnableDockedMode": "主机模式", "SettingsTabInputDirectKeyboardAccess": "直通键盘控制", - "SettingsButtonSave": "保存", - "SettingsButtonClose": "关闭", + "SettingsButtonOk": "批准", + "SettingsButtonCancel": "取消", "SettingsButtonApply": "应用", "ControllerSettingsPlayer": "玩家", "ControllerSettingsPlayer1": "玩家 1", diff --git a/Ryujinx.Ava/Assets/Locales/zh_TW.json b/Ryujinx.Ava/Assets/Locales/zh_TW.json index 6b1d9d49f..8a9e4d61d 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -166,8 +166,8 @@ "SettingsTabInput": "輸入", "SettingsTabInputEnableDockedMode": "Docked 模式", "SettingsTabInputDirectKeyboardAccess": "直通鍵盤控制", - "SettingsButtonSave": "儲存", - "SettingsButtonClose": "關閉", + "SettingsButtonOk": "嘛好", + "SettingsButtonCancel": "取消", "SettingsButtonApply": "套用", "ControllerSettingsPlayer": "玩家", "ControllerSettingsPlayer1": "玩家 1", diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs index 584627417..bd4a55e8f 100644 --- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs @@ -118,7 +118,12 @@ namespace Ryujinx.Ava.Ui.ViewModels OnPropertyChanged(); } } - + + public bool IsMacOS + { + get => OperatingSystem.IsMacOS(); + } + public bool EnableDiscordIntegration { get; set; } public bool CheckUpdatesOnStart { get; set; } public bool ShowConfirmExit { get; set; } @@ -474,11 +479,40 @@ namespace Ryujinx.Ava.Ui.ViewModels MainWindow.UpdateGraphicsConfig(); _previousVolumeLevel = Volume; + + if (_owner is SettingsWindow owner) + { + owner.ControllerSettings?.SaveCurrentProfile(); + } + + if (_owner.Owner is MainWindow window && _directoryChanged) + { + window.ViewModel.LoadApplications(); + } + + _directoryChanged = false; } public void RevertIfNotSaved() { Program.ReloadConfig(); } + + public void ApplyButton() + { + SaveSettings(); + } + + public void OkButton() + { + SaveSettings(); + _owner.Close(); + } + + public void CancelButton() + { + RevertIfNotSaved(); + _owner.Close(); + } } } \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml index 1791d8ea5..0a5cdc893 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml +++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml @@ -955,16 +955,25 @@ Icon="Document" /> - - + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs b/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs new file mode 100644 index 000000000..499cd918e --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs @@ -0,0 +1,160 @@ +using Avalonia.Controls; +using DynamicData; +using DynamicData.Binding; +using LibHac; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Shim; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Ui.Models; +using Ryujinx.HLE.FileSystem; +using Ryujinx.Ui.App.Common; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile; + +namespace Ryujinx.Ava.Ui.Controls +{ + public partial class SaveManager : UserControl + { + private readonly UserProfile _userProfile; + private readonly HorizonClient _horizonClient; + private readonly VirtualFileSystem _virtualFileSystem; + private int _sortIndex; + private int _orderIndex; + private ObservableCollection _view = new ObservableCollection(); + private string _search; + + public ObservableCollection Saves { get; set; } = new ObservableCollection(); + + public ObservableCollection View + { + get => _view; + set => _view = value; + } + + public int SortIndex + { + get => _sortIndex; + set + { + _sortIndex = value; + Sort(); + } + } + + public int OrderIndex + { + get => _orderIndex; + set + { + _orderIndex = value; + Sort(); + } + } + + public string Search + { + get => _search; + set + { + _search = value; + Sort(); + } + } + + public SaveManager() + { + InitializeComponent(); + } + + public SaveManager(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem) + { + _userProfile = userProfile; + _horizonClient = horizonClient; + _virtualFileSystem = virtualFileSystem; + InitializeComponent(); + + DataContext = this; + + Task.Run(LoadSaves); + } + + public void LoadSaves() + { + Saves.Clear(); + var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account, + new UserId((ulong)_userProfile.UserId.High, (ulong)_userProfile.UserId.Low), saveDataId: default, index: default); + + using var saveDataIterator = new UniqueRef(); + + _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); + + Span saveDataInfo = stackalloc SaveDataInfo[10]; + + while (true) + { + saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure(); + + if (readCount == 0) + { + break; + } + + for (int i = 0; i < readCount; i++) + { + var save = saveDataInfo[i]; + if (save.ProgramId.Value != 0) + { + var saveModel = new SaveModel(save, _horizonClient, _virtualFileSystem); + Saves.Add(saveModel); + saveModel.DeleteAction = () => { Saves.Remove(saveModel); }; + } + + Sort(); + } + } + } + + private void Sort() + { + Saves.AsObservableChangeSet() + .Filter(Filter) + .Sort(GetComparer()) + .Bind(out var view).AsObservableList(); + + _view.Clear(); + _view.AddRange(view); + } + + private IComparer GetComparer() + { + switch (SortIndex) + { + case 0: + return OrderIndex == 0 + ? SortExpressionComparer.Ascending(save => save.Title) + : SortExpressionComparer.Descending(save => save.Title); + case 1: + return OrderIndex == 0 + ? SortExpressionComparer.Ascending(save => save.Size) + : SortExpressionComparer.Descending(save => save.Size); + default: + return null; + } + } + + private bool Filter(object arg) + { + if (arg is SaveModel save) + { + return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower()); + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/UserEditor.axaml b/Ryujinx.Ava/Ui/Controls/UserEditor.axaml index 90c5c1fec..898527e6a 100644 --- a/Ryujinx.Ava/Ui/Controls/UserEditor.axaml +++ b/Ryujinx.Ava/Ui/Controls/UserEditor.axaml @@ -10,6 +10,7 @@ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels" Margin="0" + MinWidth="500" Padding="0" mc:Ignorable="d"> @@ -63,7 +64,7 @@ HorizontalAlignment="Stretch" MaxLength="{Binding MaxProfileNameLength}" Text="{Binding Name}" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/ProfileImageSelectionDialog.axaml.cs b/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs similarity index 97% rename from Ryujinx.Ava/Ui/Controls/ProfileImageSelectionDialog.axaml.cs rename to Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs index 5d361af92..00183b698 100644 --- a/Ryujinx.Ava/Ui/Controls/ProfileImageSelectionDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs @@ -4,15 +4,15 @@ using Avalonia.VisualTree; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Ui.Models; -using Ryujinx.Ava.Ui.Windows; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Windows; using Ryujinx.HLE.FileSystem; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; using System.IO; using Image = SixLabors.ImageSharp.Image; -namespace Ryujinx.Ava.Ui.Controls +namespace Ryujinx.Ava.UI.Controls { public partial class ProfileImageSelectionDialog : UserControl { diff --git a/Ryujinx.Ava/UI/Controls/RendererHost.axaml b/Ryujinx.Ava/UI/Controls/RendererHost.axaml new file mode 100644 index 000000000..1cc557f06 --- /dev/null +++ b/Ryujinx.Ava/UI/Controls/RendererHost.axaml @@ -0,0 +1,11 @@ + + diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs b/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs similarity index 98% rename from Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs rename to Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs index b6986b7c8..97058fa49 100644 --- a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs @@ -1,13 +1,14 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Ryujinx.Ava.UI.Helpers; using Ryujinx.Common.Configuration; using Silk.NET.Vulkan; using SPB.Graphics.OpenGL; using SPB.Windowing; using System; -namespace Ryujinx.Ava.Ui.Controls +namespace Ryujinx.Ava.UI.Controls { public partial class RendererHost : UserControl, IDisposable { diff --git a/Ryujinx.Ava/UI/Controls/SaveManager.axaml b/Ryujinx.Ava/UI/Controls/SaveManager.axaml new file mode 100644 index 000000000..b0dc4c6f7 --- /dev/null +++ b/Ryujinx.Ava/UI/Controls/SaveManager.axaml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs b/Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs similarity index 97% rename from Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs rename to Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs index 499cd918e..9910481c5 100644 --- a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs @@ -7,16 +7,16 @@ using LibHac.Fs; using LibHac.Fs.Shim; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Ui.Models; +using Ryujinx.Ava.UI.Models; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.App.Common; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; -using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile; +using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; -namespace Ryujinx.Ava.Ui.Controls +namespace Ryujinx.Ava.UI.Controls { public partial class SaveManager : UserControl { diff --git a/Ryujinx.Ava/Ui/Controls/UpdateWaitWindow.axaml b/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml similarity index 92% rename from Ryujinx.Ava/Ui/Controls/UpdateWaitWindow.axaml rename to Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml index 8309e3690..c5041230d 100644 --- a/Ryujinx.Ava/Ui/Controls/UpdateWaitWindow.axaml +++ b/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml @@ -1,10 +1,9 @@ - + @@ -43,13 +40,13 @@ Margin="5" HorizontalAlignment="Stretch" Click="ChangePictureButton_Click" - Content="{Locale:Locale UserProfilesChangeProfileImage}" /> + Content="{locale:Locale UserProfilesChangeProfileImage}" /> + Text="{locale:Locale UserProfilesRecoverHeading}"/> + Content="{locale:Locale Recover}"/> diff --git a/Ryujinx.Ava/Ui/Controls/UserRecoverer.axaml.cs b/Ryujinx.Ava/UI/Controls/UserRecoverer.axaml.cs similarity index 92% rename from Ryujinx.Ava/Ui/Controls/UserRecoverer.axaml.cs rename to Ryujinx.Ava/UI/Controls/UserRecoverer.axaml.cs index f093686dd..9f29fddbd 100644 --- a/Ryujinx.Ava/Ui/Controls/UserRecoverer.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/UserRecoverer.axaml.cs @@ -4,10 +4,10 @@ using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; -using Ryujinx.Ava.Ui.Models; -using Ryujinx.Ava.Ui.ViewModels; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; -namespace Ryujinx.Ava.Ui.Controls +namespace Ryujinx.Ava.UI.Controls { public partial class UserRecoverer : UserControl { diff --git a/Ryujinx.Ava/Ui/Controls/UserSelector.axaml b/Ryujinx.Ava/UI/Controls/UserSelector.axaml similarity index 88% rename from Ryujinx.Ava/Ui/Controls/UserSelector.axaml rename to Ryujinx.Ava/UI/Controls/UserSelector.axaml index 7cfdc481d..002d27a06 100644 --- a/Ryujinx.Ava/Ui/Controls/UserSelector.axaml +++ b/Ryujinx.Ava/UI/Controls/UserSelector.axaml @@ -1,21 +1,20 @@ - + @@ -109,21 +108,21 @@ Grid.Column="0" Margin="2" Command="{Binding AddUser}" - Content="{Locale:Locale UserProfilesAddNewProfile}" /> + Content="{locale:Locale UserProfilesAddNewProfile}" /> + diff --git a/Ryujinx.Ava/Ui/Windows/MotionSettingsWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs similarity index 96% rename from Ryujinx.Ava/Ui/Windows/MotionSettingsWindow.axaml.cs rename to Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs index b47819841..215525fcf 100644 --- a/Ryujinx.Ava/Ui/Windows/MotionSettingsWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs @@ -1,12 +1,12 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Ui.Models; -using Ryujinx.Ava.Ui.ViewModels; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common.Configuration.Hid.Controller; using System.Threading.Tasks; -namespace Ryujinx.Ava.Ui.Windows +namespace Ryujinx.Ava.UI.Windows { public partial class MotionSettingsWindow : UserControl { diff --git a/Ryujinx.Ava/Ui/Windows/RumbleSettingsWindow.axaml b/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml similarity index 75% rename from Ryujinx.Ava/Ui/Windows/RumbleSettingsWindow.axaml rename to Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml index 120643aad..e47cc5bd1 100644 --- a/Ryujinx.Ava/Ui/Windows/RumbleSettingsWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml @@ -1,13 +1,12 @@ - + diff --git a/Ryujinx.Ava/Ui/Windows/RumbleSettingsWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs similarity index 95% rename from Ryujinx.Ava/Ui/Windows/RumbleSettingsWindow.axaml.cs rename to Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs index afb5a33ac..f645ae359 100644 --- a/Ryujinx.Ava/Ui/Windows/RumbleSettingsWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs @@ -1,12 +1,12 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Ui.Models; -using Ryujinx.Ava.Ui.ViewModels; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common.Configuration.Hid.Controller; using System.Threading.Tasks; -namespace Ryujinx.Ava.Ui.Windows +namespace Ryujinx.Ava.UI.Windows { public partial class RumbleSettingsWindow : UserControl { diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml b/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml similarity index 99% rename from Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml rename to Ryujinx.Ava/UI/Windows/SettingsWindow.axaml index bd3dd613e..e25500828 100644 --- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml @@ -1,14 +1,14 @@ - + @@ -935,7 +935,7 @@ Tag="CpuPage"> + Glyph="{helpers:GlyphValueConverter Chip}" /> - - \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/ProfileImageSelectionDialog.axaml b/Ryujinx.Ava/Ui/Controls/ProfileImageSelectionDialog.axaml deleted file mode 100644 index 7bbd03ca2..000000000 --- a/Ryujinx.Ava/Ui/Controls/ProfileImageSelectionDialog.axaml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml deleted file mode 100644 index 45bc1b2b8..000000000 --- a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml +++ /dev/null @@ -1,8 +0,0 @@ - - diff --git a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml b/Ryujinx.Ava/Ui/Controls/SaveManager.axaml deleted file mode 100644 index 8721d2a7b..000000000 --- a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml deleted file mode 100644 index 037b7af85..000000000 --- a/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - From e20abbf9cc93167aa073a81a65c7e8fd60f259b1 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 29 Dec 2022 14:39:04 +0000 Subject: [PATCH 227/737] Vulkan: Don't flush commands when creating most sync (#4087) * Vulkan: Don't flush commands when creating most sync When the WaitForIdle method is called, we create sync as some internal GPU method may read back written buffer data. Some games randomly intersperse compute dispatch into their render passes, which result in this happening an unbounded number of times depending on how many times they run compute. Creating sync in Vulkan is expensive, as we need to flush the current command buffer so that it can be waited on. We have a limited number of active command buffers due to how we track resource usage, so submitting too many command buffers will force us to wait for them to return to the pool. This PR allows less "important" sync (things which are less likely to be waited on) to wait on a command buffer's result without submitting it, instead relying on AutoFlush or another, more important sync to flush it later on. Because of the possibility of us waiting for a command buffer that hasn't submitted yet, any thread needs to be able to force the active command buffer to submit. The ability to do this has been added to the backend multithreading via an "Interrupt", though it is not supported without multithreading. OpenGL drivers should already be doing something similar so they don't blow up when creating lots of sync, which is why this hasn't been a problem for these games over there. Improves Vulkan performance on Xenoblade DE, Pokemon Scarlet/Violet, and Zelda BOTW (still another large issue here) * Add strict argument This is technically a separate concern from whether the sync is a host syncpoint. * Remove _interrupted variable * Actually wait for the invoke This is required by AMD GPUs, and also may have caused some issues on other GPUs. * Remove unused using. * I don't know why it added these ones. * Address Feedback * Fix typo --- Ryujinx.Graphics.GAL/IRenderer.cs | 4 +- .../Commands/Renderer/CreateSyncCommand.cs | 6 ++- .../Multithreading/ThreadedRenderer.cs | 47 +++++++++++++++++-- .../Engine/GPFifo/GPFifoClass.cs | 6 +-- .../Engine/Threed/ThreedClass.cs | 2 +- Ryujinx.Graphics.Gpu/GpuContext.cs | 5 +- Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 7 ++- Ryujinx.Graphics.Vulkan/CommandBufferPool.cs | 16 +++++++ Ryujinx.Graphics.Vulkan/PipelineFull.cs | 1 + Ryujinx.Graphics.Vulkan/SyncManager.cs | 46 +++++++++++++++--- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 15 +++++- 11 files changed, 134 insertions(+), 21 deletions(-) diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs index c72320a2f..1f2af559f 100644 --- a/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/Ryujinx.Graphics.GAL/IRenderer.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.GAL ISampler CreateSampler(SamplerCreateInfo info); ITexture CreateTexture(TextureCreateInfo info, float scale); - void CreateSync(ulong id); + void CreateSync(ulong id, bool strict); void DeleteBuffer(BufferHandle buffer); @@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.GAL void Initialize(GraphicsDebugLevel logLevel); + void SetInterruptAction(Action interruptAction); + void Screenshot(); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs index 2e23760eb..66f5cf062 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs @@ -4,15 +4,17 @@ { public CommandType CommandType => CommandType.CreateSync; private ulong _id; + private bool _strict; - public void Set(ulong id) + public void Set(ulong id, bool strict) { _id = id; + _strict = strict; } public static void Run(ref CreateSyncCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.CreateSync(command._id); + renderer.CreateSync(command._id, command._strict); threaded.Sync.AssignSync(command._id); } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 62a7dae79..58058be2c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading private int _elementSize; private IRenderer _baseRenderer; private Thread _gpuThread; + private Thread _backendThread; private bool _disposed; private bool _running; @@ -38,6 +39,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading private CircularSpanPool _spanPool; private ManualResetEventSlim _invokeRun; + private AutoResetEvent _interruptRun; private bool _lastSampleCounterClear = true; @@ -54,6 +56,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading private int _refProducerPtr; private int _refConsumerPtr; + private Action _interruptAction; + public event EventHandler ScreenCaptured; internal BufferMap Buffers { get; } @@ -73,6 +77,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading _baseRenderer = renderer; renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info); + renderer.SetInterruptAction(Interrupt); Pipeline = new ThreadedPipeline(this, renderer.Pipeline); Window = new ThreadedWindow(this, renderer); @@ -82,6 +87,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading _galWorkAvailable = new ManualResetEventSlim(false); _invokeRun = new ManualResetEventSlim(); + _interruptRun = new AutoResetEvent(false); _spanPool = new CircularSpanPool(this, SpanPoolBytes); SpanPool = _spanPool; @@ -95,6 +101,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading { _running = true; + _backendThread = Thread.CurrentThread; + _gpuThread = new Thread(() => { gpuLoop(); _running = false; @@ -116,10 +124,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading _galWorkAvailable.Wait(); _galWorkAvailable.Reset(); + if (Volatile.Read(ref _interruptAction) != null) + { + _interruptAction(); + _interruptRun.Set(); + + Interlocked.Exchange(ref _interruptAction, null); + } + // The other thread can only increase the command count. // We can assume that if it is above 0, it will stay there or get higher. - while (_commandCount > 0) + while (_commandCount > 0 && Volatile.Read(ref _interruptAction) == null) { int commandPtr = _consumerPtr; @@ -281,10 +297,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading return sampler; } - public void CreateSync(ulong id) + public void CreateSync(ulong id, bool strict) { Sync.CreateSyncHandle(id); - New().Set(id); + New().Set(id, strict); QueueCommand(); } @@ -421,6 +437,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading _baseRenderer.WaitSync(id); } + private void Interrupt(Action action) + { + // Interrupt the backend thread from any external thread and invoke the given action. + + if (Thread.CurrentThread == _backendThread) + { + // If this is called from the backend thread, the action can run immediately. + action(); + } + else + { + while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { } + + _galWorkAvailable.Set(); + + _interruptRun.WaitOne(); + } + } + + public void SetInterruptAction(Action interruptAction) + { + // Threaded renderer ignores given interrupt action, as it provides its own to the child renderer. + } + public void Dispose() { // Dispose must happen from the render thread, after all commands have completed. @@ -440,6 +480,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading _frameComplete.Dispose(); _galWorkAvailable.Dispose(); _invokeRun.Dispose(); + _interruptRun.Dispose(); Sync.Dispose(); } diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs index 9cb979833..e80d98a15 100644 --- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs @@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo if (_createSyncPending) { _createSyncPending = false; - _context.CreateHostSyncIfNeeded(false); + _context.CreateHostSyncIfNeeded(false, false); } } @@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo } else if (operation == SyncpointbOperation.Incr) { - _context.CreateHostSyncIfNeeded(true); + _context.CreateHostSyncIfNeeded(true, true); _context.Synchronization.IncrementSyncpoint(syncpointId); } @@ -184,7 +184,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo { _context.Renderer.Pipeline.CommandBufferBarrier(); - _context.CreateHostSyncIfNeeded(false); + _context.CreateHostSyncIfNeeded(false, true); } /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 87dd1d8ee..a38c09875 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed uint syncpointId = (uint)argument & 0xFFFF; _context.AdvanceSequence(); - _context.CreateHostSyncIfNeeded(true); + _context.CreateHostSyncIfNeeded(true, true); _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result. _context.Synchronization.IncrementSyncpoint(syncpointId); } diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index 52733165b..917588632 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -316,7 +316,8 @@ namespace Ryujinx.Graphics.Gpu /// If no actions are present, a host sync object is not created. /// /// True if host sync is being created by a syncpoint - public void CreateHostSyncIfNeeded(bool syncpoint) + /// True if the sync should signal as soon as possible + public void CreateHostSyncIfNeeded(bool syncpoint, bool strict) { if (BufferMigrations.Count > 0) { @@ -337,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu if (_pendingSync || (syncpoint && SyncpointActions.Count > 0)) { - Renderer.CreateSync(SyncNumber); + Renderer.CreateSync(SyncNumber, strict); SyncNumber++; diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index de1ce4a34..1733c6f21 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.OpenGL return new Program(programBinary, hasFragmentShader, info.FragmentOutputMap); } - public void CreateSync(ulong id) + public void CreateSync(ulong id, bool strict) { _sync.Create(id); } @@ -247,6 +247,11 @@ namespace Ryujinx.Graphics.OpenGL return _sync.GetCurrent(); } + public void SetInterruptAction(Action interruptAction) + { + // Currently no need for an interrupt action. + } + public void Screenshot() { _window.ScreenCaptureRequested = true; diff --git a/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs index c77b00405..4cbb24ef7 100644 --- a/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs +++ b/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs @@ -116,6 +116,22 @@ namespace Ryujinx.Graphics.Vulkan } } + public void AddInUseWaitable(MultiFenceHolder waitable) + { + lock (_commandBuffers) + { + for (int i = 0; i < _totalCommandBuffers; i++) + { + ref var entry = ref _commandBuffers[i]; + + if (entry.InUse) + { + AddWaitable(i, waitable); + } + } + } + } + public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs) { Debug.Assert(_commandBuffers[cbIndex].InUse); diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 56a491842..2256c5422 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -227,6 +227,7 @@ namespace Ryujinx.Graphics.Vulkan } CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; + Gd.RegisterFlush(); // Restore per-command buffer state. diff --git a/Ryujinx.Graphics.Vulkan/SyncManager.cs b/Ryujinx.Graphics.Vulkan/SyncManager.cs index 35e3adf1e..c046dc3c7 100644 --- a/Ryujinx.Graphics.Vulkan/SyncManager.cs +++ b/Ryujinx.Graphics.Vulkan/SyncManager.cs @@ -11,7 +11,13 @@ namespace Ryujinx.Graphics.Vulkan { 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 = 0; @@ -19,6 +25,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly VulkanRenderer _gd; private readonly Device _device; private List _handles; + private ulong FlushId; public SyncManager(VulkanRenderer gd, Device device) { @@ -27,17 +34,33 @@ namespace Ryujinx.Graphics.Vulkan _handles = new List(); } - public void Create(ulong id) + public void RegisterFlush() { - MultiFenceHolder waitable = new MultiFenceHolder(); + FlushId++; + } - _gd.FlushAllCommands(); - _gd.CommandBufferPool.AddWaitable(waitable); + public void Create(ulong id, bool strict) + { + ulong flushId = FlushId; + MultiFenceHolder waitable = new MultiFenceHolder(); + if (strict || _gd.InterruptAction == null) + { + _gd.FlushAllCommands(); + _gd.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. + + _gd.CommandBufferPool.AddInUseWaitable(waitable); + } SyncHandle handle = new SyncHandle { ID = id, - Waitable = waitable + Waitable = waitable, + FlushId = flushId }; lock (_handles) @@ -107,6 +130,17 @@ namespace Ryujinx.Graphics.Vulkan return; } + if (result.NeedsFlush(FlushId)) + { + _gd.InterruptAction(() => + { + if (result.NeedsFlush(FlushId)) + { + _gd.FlushAllCommands(); + } + }); + } + bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); if (!signaled) { @@ -132,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan first = _handles.FirstOrDefault(); } - if (first == null) break; + if (first == null || first.NeedsFlush(FlushId)) break; bool signaled = first.Waitable.WaitForFences(_gd.Api, _device, 0); if (signaled) diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 3c446abf3..5c77cb001 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -48,6 +48,7 @@ namespace Ryujinx.Graphics.Vulkan internal DescriptorSetManager DescriptorSetManager { get; private set; } internal PipelineLayoutCache PipelineLayoutCache { get; private set; } internal BackgroundResources BackgroundResources { get; private set; } + internal Action InterruptAction { get; private set; } internal BufferManager BufferManager { get; private set; } @@ -354,6 +355,11 @@ namespace Ryujinx.Graphics.Vulkan _pipeline?.FlushCommandsImpl(); } + internal void RegisterFlush() + { + _syncManager.RegisterFlush(); + } + public ReadOnlySpan GetBufferData(BufferHandle buffer, int offset, int size) { return BufferManager.GetData(buffer, offset, size); @@ -593,9 +599,9 @@ namespace Ryujinx.Graphics.Vulkan action(); } - public void CreateSync(ulong id) + public void CreateSync(ulong id, bool strict) { - _syncManager.Create(id); + _syncManager.Create(id, strict); } public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info) @@ -613,6 +619,11 @@ namespace Ryujinx.Graphics.Vulkan return _syncManager.GetCurrent(); } + public void SetInterruptAction(Action interruptAction) + { + InterruptAction = interruptAction; + } + public void Screenshot() { _window.ScreenCaptureRequested = true; From 52c115a1f8f98dcd0a1f9da3d176f4a100f825b4 Mon Sep 17 00:00:00 2001 From: Luminoso-256 <63971285+Luminoso-256@users.noreply.github.com> Date: Thu, 29 Dec 2022 08:57:35 -0600 Subject: [PATCH 228/737] HLE: Add basic stubs to get Labo VR booting to title screen. (#4007) * HLE: Add basic stubs to get Labo VR booting to title screen. * Address code review * Apply suggestions from code review (pt. 2) Co-authored-by: gdkchan * Apply suggestions from code review (pt. 3) Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Co-authored-by: gdkchan * Formatting: final batch? Co-authored-by: Ac_K Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Address last? bit of formatting Co-authored-by: gdkchan Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Co-authored-by: Ac_K --- .../SystemAppletProxy/ISelfController.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index e8bbf748b..567bc2645 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -298,6 +298,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandHipc(67)] //3.0.0+ + // IsIlluminanceAvailable() -> bool + public ResultCode IsIlluminanceAvailable(ServiceCtx context) + { + // NOTE: This should call IsAmbientLightSensorAvailable through to Lbl, but there's no situation where we'd want false. + context.ResponseData.Write(true); + + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + [CommandHipc(68)] // SetAutoSleepDisabled(u8) public ResultCode SetAutoSleepDisabled(ServiceCtx context) @@ -318,6 +330,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandHipc(71)] //5.0.0+ + // GetCurrentIlluminanceEx() -> (bool, f32) + public ResultCode GetCurrentIlluminanceEx(ServiceCtx context) + { + // TODO: The light value should be configurable - presumably users using software that takes advantage will want control. + context.ResponseData.Write(1); // OverLimit + context.ResponseData.Write(10000f); // Lux - 10K lux is ambient light. + + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + [CommandHipc(80)] // 4.0.0+ // SetWirelessPriorityMode(s32 wireless_priority_mode) public ResultCode SetWirelessPriorityMode(ServiceCtx context) From 9dfe81770a8337a7a469eb3bac0ae9599cc0f61c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 29 Dec 2022 12:09:34 -0300 Subject: [PATCH 229/737] Use vector outputs for texture operations (#3939) * Change AggregateType to include vector type counts * Replace VariableType uses with AggregateType and delete VariableType * Support new local vector types on SPIR-V and GLSL * Start using vector outputs for texture operations * Use vectors on more texture operations * Use vector output for ImageLoad operations * Replace all uses of single destination texture constructors with multi destination ones * Update textureGatherOffsets replacement to split vector operations * Shader cache version bump Co-authored-by: Ac_K --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Shader/AttributeType.cs | 12 - .../CodeGen/Glsl/Declarations.cs | 36 ++- .../CodeGen/Glsl/GlslGenerator.cs | 8 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 11 +- .../Glsl/Instructions/InstGenBallot.cs | 3 +- .../Glsl/Instructions/InstGenHelper.cs | 14 +- .../Glsl/Instructions/InstGenMemory.cs | 126 +++++--- .../Glsl/Instructions/InstGenVector.cs | 32 ++ .../CodeGen/Glsl/NumberFormatter.cs | 18 +- .../CodeGen/Glsl/OperandManager.cs | 139 +++++---- .../CodeGen/Glsl/TypeConversion.cs | 33 +- .../CodeGen/Spirv/CodeGenContext.cs | 54 +++- .../CodeGen/Spirv/Declarations.cs | 19 +- .../CodeGen/Spirv/EnumConversion.cs | 17 +- .../CodeGen/Spirv/Instructions.cs | 147 +++++++-- .../CodeGen/Spirv/SpirvGenerator.cs | 8 +- .../Instructions/InstEmitSurface.cs | 116 +++---- .../Instructions/InstEmitTexture.cs | 197 ++++++------ .../IntermediateRepresentation/Instruction.cs | 1 + .../IntermediateRepresentation/Operation.cs | 21 +- .../TextureOperation.cs | 8 +- Ryujinx.Graphics.Shader/SamplerType.cs | 8 +- .../StructuredIr/AstHelper.cs | 3 +- .../StructuredIr/AstOperand.cs | 5 +- .../StructuredIr/AstOperation.cs | 18 ++ .../StructuredIr/InstructionInfo.cs | 272 ++++++++-------- .../StructuredIr/OperandInfo.cs | 17 +- .../StructuredIr/StructuredFunction.cs | 15 +- .../StructuredIr/StructuredProgram.cs | 84 ++++- .../StructuredIr/StructuredProgramContext.cs | 10 +- .../StructuredIr/VariableType.cs | 14 - Ryujinx.Graphics.Shader/TextureFormat.cs | 10 +- .../Translation/AggregateType.cs | 11 +- .../Translation/AttributeInfo.cs | 60 ++-- .../Translation/EmitterContext.cs | 8 +- .../Translation/Rewriter.cs | 290 +++++++++++------- 37 files changed, 1100 insertions(+), 747 deletions(-) create mode 100644 Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs delete mode 100644 Ryujinx.Graphics.Shader/StructuredIr/VariableType.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index bdc6f4d66..2622ea3e8 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4106; + private const uint CodeGenVersion = 3939; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/AttributeType.cs b/Ryujinx.Graphics.Shader/AttributeType.cs index 1ede15600..4e6cad596 100644 --- a/Ryujinx.Graphics.Shader/AttributeType.cs +++ b/Ryujinx.Graphics.Shader/AttributeType.cs @@ -1,4 +1,3 @@ -using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; @@ -25,17 +24,6 @@ namespace Ryujinx.Graphics.Shader }; } - public static VariableType ToVariableType(this AttributeType type) - { - return type switch - { - AttributeType.Float => VariableType.F32, - AttributeType.Sint => VariableType.S32, - AttributeType.Uint => VariableType.U32, - _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") - }; - } - public static AggregateType ToAggregateType(this AttributeType type) { return type switch diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index c6e3b3390..4da21cb72 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -350,19 +350,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - public static string GetVarTypeName(VariableType type) + public static string GetVarTypeName(AggregateType type, bool precise = true) { - switch (type) + return type switch { - case VariableType.Bool: return "bool"; - case VariableType.F32: return "precise float"; - case VariableType.F64: return "double"; - case VariableType.None: return "void"; - case VariableType.S32: return "int"; - case VariableType.U32: return "uint"; - } - - throw new ArgumentException($"Invalid variable type \"{type}\"."); + AggregateType.Void => "void", + AggregateType.Bool => "bool", + AggregateType.FP32 => precise ? "precise float" : "float", + AggregateType.FP64 => "double", + AggregateType.S32 => "int", + AggregateType.U32 => "uint", + AggregateType.Vector2 | AggregateType.Bool => "bvec2", + AggregateType.Vector2 | AggregateType.FP32 => precise ? "precise vec2" : "vec2", + AggregateType.Vector2 | AggregateType.FP64 => "dvec2", + AggregateType.Vector2 | AggregateType.S32 => "ivec2", + AggregateType.Vector2 | AggregateType.U32 => "uvec2", + AggregateType.Vector3 | AggregateType.Bool => "bvec3", + AggregateType.Vector3 | AggregateType.FP32 => precise ? "precise vec3" : "vec3", + AggregateType.Vector3 | AggregateType.FP64 => "dvec3", + AggregateType.Vector3 | AggregateType.S32 => "ivec3", + AggregateType.Vector3 | AggregateType.U32 => "uvec3", + AggregateType.Vector4 | AggregateType.Bool => "bvec4", + AggregateType.Vector4 | AggregateType.FP32 => precise ? "precise vec4" : "vec4", + AggregateType.Vector4 | AggregateType.FP64 => "dvec4", + AggregateType.Vector4 | AggregateType.S32 => "ivec4", + AggregateType.Vector4 | AggregateType.U32 => "uvec4", + _ => throw new ArgumentException($"Invalid variable type \"{type}\".") + }; } private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index e1b8eb6ec..90826a154 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -126,8 +126,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (node is AstAssignment assignment) { - VariableType srcType = OperandManager.GetNodeDestType(context, assignment.Source); - VariableType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true); + AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source); + AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true); string dest; @@ -158,9 +158,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static string GetCondExpr(CodeGenContext context, IAstNode cond) { - VariableType srcType = OperandManager.GetNodeDestType(context, cond); + AggregateType srcType = OperandManager.GetNodeDestType(context, cond); - return ReinterpretCast(context, cond, srcType, VariableType.Bool); + return ReinterpretCast(context, cond, srcType, AggregateType.Bool); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index b890b0158..9ca4618d3 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenBallot; @@ -8,6 +9,7 @@ using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenFSI; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenMemory; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenPacking; +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenVector; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions @@ -32,12 +34,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { IAstNode src = operation.GetSource(0); - VariableType type = GetSrcVarType(operation.Inst, 0); + AggregateType type = GetSrcVarType(operation.Inst, 0); string srcExpr = GetSoureExpr(context, src, type); string zero; - if (type == VariableType.F64) + if (type == AggregateType.FP64) { zero = "0.0"; } @@ -95,7 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else { - VariableType dstType = GetSrcVarType(inst, argIndex); + AggregateType dstType = GetSrcVarType(inst, argIndex); args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); } @@ -226,6 +228,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.UnpackHalf2x16: return UnpackHalf2x16(context, operation); + + case Instruction.VectorExtract: + return VectorExtract(context, operation); } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs index 51e7bd212..68793c5dd 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -9,7 +10,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { public static string Ballot(CodeGenContext context, AstOperation operation) { - VariableType dstType = GetSrcVarType(operation.Inst, 0); + AggregateType dstType = GetSrcVarType(operation.Inst, 0); string arg = GetSoureExpr(context, operation.GetSource(0), dstType); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index c40f96f11..743b695c8 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion; @@ -7,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { static class InstGenHelper { - private static readonly InstInfo[] InfoTable; + private static readonly InstInfo[] _infoTable; static InstGenHelper() { - InfoTable = new InstInfo[(int)Instruction.Count]; + _infoTable = new InstInfo[(int)Instruction.Count]; Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd"); Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomicAnd"); @@ -132,6 +133,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.Truncate, InstType.CallUnary, "trunc"); Add(Instruction.UnpackDouble2x32, InstType.Special); Add(Instruction.UnpackHalf2x16, InstType.Special); + Add(Instruction.VectorExtract, InstType.Special); Add(Instruction.VoteAll, InstType.CallUnary, "allInvocationsARB"); Add(Instruction.VoteAllEqual, InstType.CallUnary, "allInvocationsEqualARB"); Add(Instruction.VoteAny, InstType.CallUnary, "anyInvocationARB"); @@ -139,15 +141,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0) { - InfoTable[(int)inst] = new InstInfo(flags, opName, precedence); + _infoTable[(int)inst] = new InstInfo(flags, opName, precedence); } public static InstInfo GetInstructionInfo(Instruction inst) { - return InfoTable[(int)(inst & Instruction.Mask)]; + return _infoTable[(int)(inst & Instruction.Mask)]; } - public static string GetSoureExpr(CodeGenContext context, IAstNode node, VariableType dstType) + public static string GetSoureExpr(CodeGenContext context, IAstNode node, AggregateType dstType) { return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType); } @@ -191,7 +193,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return false; } - InstInfo info = InfoTable[(int)(operation.Inst & Instruction.Mask)]; + InstInfo info = _infoTable[(int)(operation.Inst & Instruction.Mask)]; if ((info.Type & (InstType.Call | InstType.Special)) != 0) { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 022e3a444..f667d0808 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; @@ -23,7 +24,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.ImageStore: return "// imageStore(bindless)"; case Instruction.ImageLoad: - NumberFormatter.TryFormat(0, texOp.Format.GetComponentType(), out string imageConst); + AggregateType componentType = texOp.Format.GetComponentType(); + + NumberFormatter.TryFormat(0, componentType, out string imageConst); + + AggregateType outputType = texOp.GetVectorType(componentType); + + if ((outputType & AggregateType.ElementCountMask) != 0) + { + return $"{Declarations.GetVarTypeName(outputType, precise: false)}({imageConst})"; + } + return imageConst; default: return NumberFormatter.FormatInt(0); @@ -58,7 +69,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions int srcIndex = isBindless ? 1 : 0; - string Src(VariableType type) + string Src(AggregateType type) { return GetSoureExpr(context, texOp.GetSource(srcIndex++), type); } @@ -67,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (isIndexed) { - indexExpr = Src(VariableType.S32); + indexExpr = Src(AggregateType.S32); } string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); @@ -113,19 +124,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions for (int index = 0; index < pCount; index++) { - elems[index] = Src(VariableType.S32); + elems[index] = Src(AggregateType.S32); } Append(ApplyScaling("ivec" + pCount + "(" + string.Join(", ", elems) + ")")); } else { - Append(Src(VariableType.S32)); + Append(Src(AggregateType.S32)); } if (texOp.Inst == Instruction.ImageStore) { - VariableType type = texOp.Format.GetComponentType(); + AggregateType type = texOp.Format.GetComponentType(); string[] cElems = new string[4]; @@ -139,8 +150,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { cElems[index] = type switch { - VariableType.S32 => NumberFormatter.FormatInt(0), - VariableType.U32 => NumberFormatter.FormatUint(0), + AggregateType.S32 => NumberFormatter.FormatInt(0), + AggregateType.U32 => NumberFormatter.FormatUint(0), _ => NumberFormatter.FormatFloat(0) }; } @@ -148,8 +159,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string prefix = type switch { - VariableType.S32 => "i", - VariableType.U32 => "u", + AggregateType.S32 => "i", + AggregateType.U32 => "u", _ => string.Empty }; @@ -158,7 +169,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (texOp.Inst == Instruction.ImageAtomic) { - VariableType type = texOp.Format.GetComponentType(); + AggregateType type = texOp.Format.GetComponentType(); if ((texOp.Flags & TextureFlags.AtomicMask) == TextureFlags.CAS) { @@ -176,14 +187,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCall += ")"; - if (type != VariableType.S32) + if (type != AggregateType.S32) { texCall = "int(" + texCall + ")"; } } else { - texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : ""); + texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMaskMultiDest(texOp.Index) : ""); } return texCall; @@ -288,7 +299,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (isIndexed) { - indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32); + indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); } string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); @@ -303,14 +314,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions for (int index = 0; index < coordsCount; index++) { - elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), VariableType.F32); + elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), AggregateType.FP32); } coordsExpr = "vec" + coordsCount + "(" + string.Join(", ", elems) + ")"; } else { - coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), VariableType.F32); + coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); } return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}"; @@ -362,9 +373,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - VariableType srcType = OperandManager.GetNodeDestType(context, src2); + AggregateType srcType = OperandManager.GetNodeDestType(context, src2); - string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32); + string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32); return $"{arrayName}[{offsetExpr}] = {src}"; } @@ -376,9 +387,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - VariableType srcType = OperandManager.GetNodeDestType(context, src2); + AggregateType srcType = OperandManager.GetNodeDestType(context, src2); - string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32); + string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32); return $"{HelperFunctionNames.StoreShared16}({offsetExpr}, {src})"; } @@ -390,9 +401,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - VariableType srcType = OperandManager.GetNodeDestType(context, src2); + AggregateType srcType = OperandManager.GetNodeDestType(context, src2); - string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32); + string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32); return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})"; } @@ -406,9 +417,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - VariableType srcType = OperandManager.GetNodeDestType(context, src3); + AggregateType srcType = OperandManager.GetNodeDestType(context, src3); - string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32); + string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32); string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); @@ -424,9 +435,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - VariableType srcType = OperandManager.GetNodeDestType(context, src3); + AggregateType srcType = OperandManager.GetNodeDestType(context, src3); - string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32); + string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32); string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); @@ -442,9 +453,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - VariableType srcType = OperandManager.GetNodeDestType(context, src3); + AggregateType srcType = OperandManager.GetNodeDestType(context, src3); - string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32); + string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32); string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); @@ -469,6 +480,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; + bool colorIsVector = isGather || !isShadow; + SamplerType type = texOp.Type & SamplerType.Mask; bool is2D = type == SamplerType.Texture2D; @@ -492,7 +505,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // TODO: Bindless texture support. For now we just return 0. if (isBindless) { - return NumberFormatter.FormatFloat(0); + string scalarValue = NumberFormatter.FormatFloat(0); + + if (colorIsVector) + { + AggregateType outputType = texOp.GetVectorType(AggregateType.FP32); + + if ((outputType & AggregateType.ElementCountMask) != 0) + { + return $"{Declarations.GetVarTypeName(outputType, precise: false)}({scalarValue})"; + } + } + + return scalarValue; } string texCall = intCoords ? "texelFetch" : "texture"; @@ -521,7 +546,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions int srcIndex = isBindless ? 1 : 0; - string Src(VariableType type) + string Src(AggregateType type) { return GetSoureExpr(context, texOp.GetSource(srcIndex++), type); } @@ -530,7 +555,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (isIndexed) { - indexExpr = Src(VariableType.S32); + indexExpr = Src(AggregateType.S32); } string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); @@ -578,7 +603,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCall += ", " + str; } - VariableType coordType = intCoords ? VariableType.S32 : VariableType.F32; + AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32; string AssemblePVector(int count) { @@ -590,7 +615,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { if (arrayIndexElem == index) { - elems[index] = Src(VariableType.S32); + elems[index] = Src(AggregateType.S32); if (!intCoords) { @@ -652,20 +677,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions for (int index = 0; index < count; index++) { - elems[index] = Src(VariableType.F32); + elems[index] = Src(AggregateType.FP32); } return "vec" + count + "(" + string.Join(", ", elems) + ")"; } else { - return Src(VariableType.F32); + return Src(AggregateType.FP32); } } if (hasExtraCompareArg) { - Append(Src(VariableType.F32)); + Append(Src(AggregateType.FP32)); } if (hasDerivatives) @@ -676,7 +701,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (isMultisample) { - Append(Src(VariableType.S32)); + Append(Src(AggregateType.S32)); } else if (hasLodLevel) { @@ -691,14 +716,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions for (int index = 0; index < count; index++) { - elems[index] = Src(VariableType.S32); + elems[index] = Src(AggregateType.S32); } return "ivec" + count + "(" + string.Join(", ", elems) + ")"; } else { - return Src(VariableType.S32); + return Src(AggregateType.S32); } } @@ -718,17 +743,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (hasLodBias) { - Append(Src(VariableType.F32)); + Append(Src(AggregateType.FP32)); } // textureGather* optional extra component index, // not needed for shadow samplers. if (isGather && !isShadow) { - Append(Src(VariableType.S32)); + Append(Src(AggregateType.S32)); } - texCall += ")" + (isGather || !isShadow ? GetMask(texOp.Index) : ""); + texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); return texCall; } @@ -751,7 +776,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (isIndexed) { - indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32); + indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); } string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); @@ -804,5 +829,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { return '.' + "rgba".Substring(index, 1); } + + private static string GetMaskMultiDest(int mask) + { + string swizzle = "."; + + for (int i = 0; i < 4; i++) + { + if ((mask & (1 << i)) != 0) + { + swizzle += "xyzw"[i]; + } + } + + return swizzle; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs new file mode 100644 index 000000000..f09ea2e8a --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs @@ -0,0 +1,32 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; + +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + static class InstGenVector + { + public static string VectorExtract(CodeGenContext context, AstOperation operation) + { + IAstNode vector = operation.GetSource(0); + IAstNode index = operation.GetSource(1); + + string vectorExpr = GetSoureExpr(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 = GetSoureExpr(context, index, GetSrcVarType(operation.Inst, 1)); + + return $"{vectorExpr}[{indexExpr}]"; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs index 2ec44277c..eb27e9bf1 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System; using System.Globalization; @@ -8,21 +8,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { private const int MaxDecimal = 256; - public static bool TryFormat(int value, VariableType dstType, out string formatted) + public static bool TryFormat(int value, AggregateType dstType, out string formatted) { - if (dstType == VariableType.F32) + if (dstType == AggregateType.FP32) { return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted); } - else if (dstType == VariableType.S32) + else if (dstType == AggregateType.S32) { formatted = FormatInt(value); } - else if (dstType == VariableType.U32) + else if (dstType == AggregateType.U32) { formatted = FormatUint((uint)value); } - else if (dstType == VariableType.Bool) + else if (dstType == AggregateType.Bool) { formatted = value != 0 ? "true" : "false"; } @@ -65,13 +65,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return true; } - public static string FormatInt(int value, VariableType dstType) + public static string FormatInt(int value, AggregateType dstType) { - if (dstType == VariableType.S32) + if (dstType == AggregateType.S32) { return FormatInt(value); } - else if (dstType == VariableType.U32) + else if (dstType == AggregateType.U32) { return FormatUint((uint)value); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index ccc87a7fc..080f1708b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Numerics; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -17,9 +18,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public string Name { get; } - public VariableType Type { get; } + public AggregateType Type { get; } - public BuiltInAttribute(string name, VariableType type) + public BuiltInAttribute(string name, AggregateType type) { Name = name; Type = type; @@ -28,64 +29,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static Dictionary _builtInAttributes = new Dictionary() { - { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, - { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, - { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, - { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) }, - { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) }, - { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) }, - { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) }, - { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) }, - { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) }, - { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) }, - { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) }, - { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) }, - { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) }, - { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) }, - { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) }, - { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, - { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, - { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, - { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, - { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, - { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", VariableType.S32) }, - { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", VariableType.S32) }, - { AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) }, - { AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) }, - { AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", VariableType.S32) }, - { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, + { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", AggregateType.S32) }, + { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", AggregateType.FP32) }, + { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", AggregateType.FP32) }, + { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", AggregateType.FP32) }, + { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", AggregateType.FP32) }, + { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", AggregateType.FP32) }, + { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", AggregateType.FP32) }, + { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", AggregateType.FP32) }, + { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", AggregateType.FP32) }, + { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", AggregateType.FP32) }, + { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", AggregateType.FP32) }, + { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", AggregateType.FP32) }, + { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", AggregateType.FP32) }, + { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", AggregateType.FP32) }, + { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", AggregateType.FP32) }, + { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", AggregateType.FP32) }, + { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", AggregateType.FP32) }, + { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", AggregateType.FP32) }, + { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", AggregateType.S32) }, + { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", AggregateType.S32) }, + { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", AggregateType.S32) }, + { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", AggregateType.S32) }, + { AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", AggregateType.S32) }, + { AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", AggregateType.S32) }, + { AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", AggregateType.S32) }, + { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", AggregateType.Bool) }, // Special. - { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, - { AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", VariableType.Bool) }, - { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", VariableType.U32) }, - { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", VariableType.U32) }, - { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", VariableType.U32) }, - { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", VariableType.U32) }, - { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) }, - { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) }, - { AttributeConsts.LaneId, new BuiltInAttribute(null, VariableType.U32) }, - { AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", VariableType.S32) }, - { AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", VariableType.S32) }, - { AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", VariableType.S32) }, - { AttributeConsts.EqMask, new BuiltInAttribute(null, VariableType.U32) }, - { AttributeConsts.GeMask, new BuiltInAttribute(null, VariableType.U32) }, - { AttributeConsts.GtMask, new BuiltInAttribute(null, VariableType.U32) }, - { AttributeConsts.LeMask, new BuiltInAttribute(null, VariableType.U32) }, - { AttributeConsts.LtMask, new BuiltInAttribute(null, VariableType.U32) }, + { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", AggregateType.FP32) }, + { AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", AggregateType.Bool) }, + { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", AggregateType.U32) }, + { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", AggregateType.U32) }, + { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", AggregateType.U32) }, + { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", AggregateType.U32) }, + { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", AggregateType.U32) }, + { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", AggregateType.U32) }, + { AttributeConsts.LaneId, new BuiltInAttribute(null, AggregateType.U32) }, + { AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", AggregateType.S32) }, + { AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", AggregateType.S32) }, + { AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", AggregateType.S32) }, + { AttributeConsts.EqMask, new BuiltInAttribute(null, AggregateType.U32) }, + { AttributeConsts.GeMask, new BuiltInAttribute(null, AggregateType.U32) }, + { AttributeConsts.GtMask, new BuiltInAttribute(null, AggregateType.U32) }, + { AttributeConsts.LeMask, new BuiltInAttribute(null, AggregateType.U32) }, + { AttributeConsts.LtMask, new BuiltInAttribute(null, AggregateType.U32) }, // Support uniforms. - { AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", AggregateType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", AggregateType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", AggregateType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", AggregateType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", AggregateType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", AggregateType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", AggregateType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", AggregateType.Bool) }, - { AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", VariableType.F32) }, - { AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", VariableType.F32) } + { AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", AggregateType.FP32) }, + { AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", AggregateType.FP32) } }; private Dictionary _locals; @@ -329,7 +330,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { if (cbIndexable) { - return GetUbName(stage, NumberFormatter.FormatInt(slot, VariableType.S32)); + return GetUbName(stage, NumberFormatter.FormatInt(slot, AggregateType.S32)); } return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}"; @@ -404,7 +405,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; } - public static VariableType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false) + public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false) { if (node is AstOperation operation) { @@ -431,12 +432,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return context.GetFunction(funcId.Value).ReturnType; } - else if (operation is AstTextureOperation texOp && - (texOp.Inst == Instruction.ImageLoad || - texOp.Inst == Instruction.ImageStore || - texOp.Inst == Instruction.ImageAtomic)) + else if (operation.Inst == Instruction.VectorExtract) { - return texOp.Format.GetComponentType(); + 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); @@ -458,7 +469,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static VariableType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false) + private static AggregateType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false) { if (operand.Type == OperandType.Attribute) { @@ -474,7 +485,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location); - return type.ToVariableType(); + return type.ToAggregateType(); } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs index b13a74f45..22c8623ce 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System; namespace Ryujinx.Graphics.Shader.CodeGen.Glsl @@ -10,8 +11,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string ReinterpretCast( CodeGenContext context, IAstNode node, - VariableType srcType, - VariableType dstType) + AggregateType srcType, + AggregateType dstType) { if (node is AstOperand operand && operand.Type == OperandType.Constant) { @@ -26,46 +27,46 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return ReinterpretCast(expr, node, srcType, dstType); } - private static string ReinterpretCast(string expr, IAstNode node, VariableType srcType, VariableType dstType) + private static string ReinterpretCast(string expr, IAstNode node, AggregateType srcType, AggregateType dstType) { if (srcType == dstType) { return expr; } - if (srcType == VariableType.F32) + if (srcType == AggregateType.FP32) { switch (dstType) { - case VariableType.Bool: return $"(floatBitsToInt({expr}) != 0)"; - case VariableType.S32: return $"floatBitsToInt({expr})"; - case VariableType.U32: return $"floatBitsToUint({expr})"; + case AggregateType.Bool: return $"(floatBitsToInt({expr}) != 0)"; + case AggregateType.S32: return $"floatBitsToInt({expr})"; + case AggregateType.U32: return $"floatBitsToUint({expr})"; } } - else if (dstType == VariableType.F32) + else if (dstType == AggregateType.FP32) { switch (srcType) { - case VariableType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, VariableType.S32)})"; - case VariableType.S32: return $"intBitsToFloat({expr})"; - case VariableType.U32: return $"uintBitsToFloat({expr})"; + case AggregateType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; + case AggregateType.S32: return $"intBitsToFloat({expr})"; + case AggregateType.U32: return $"uintBitsToFloat({expr})"; } } - else if (srcType == VariableType.Bool) + else if (srcType == AggregateType.Bool) { return ReinterpretBoolToInt(expr, node, dstType); } - else if (dstType == VariableType.Bool) + else if (dstType == AggregateType.Bool) { expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); return $"({expr} != 0)"; } - else if (dstType == VariableType.S32) + else if (dstType == AggregateType.S32) { return $"int({expr})"; } - else if (dstType == VariableType.U32) + else if (dstType == AggregateType.U32) { return $"uint({expr})"; } @@ -73,7 +74,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl throw new ArgumentException($"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); } - private static string ReinterpretBoolToInt(string expr, IAstNode node, VariableType 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); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index dff5474a1..41afdf188 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -241,6 +241,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv throw new NotImplementedException(node.GetType().Name); } + public Instruction GetWithType(IAstNode node, out AggregateType type) + { + if (node is AstOperation operation) + { + var opResult = Instructions.Generate(this, operation); + type = opResult.Type; + return opResult.Value; + } + else if (node is AstOperand operand) + { + switch (operand.Type) + { + case IrOperandType.LocalVariable: + type = operand.VarType; + return GetLocal(type, operand); + default: + throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."); + } + } + + throw new NotImplementedException(node.GetType().Name); + } + private Instruction GetUndefined(AggregateType type) { return type switch @@ -325,7 +348,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (components > 1) { attrOffset &= ~0xf; - type = AggregateType.Vector | AggregateType.FP32; + type = components switch + { + 2 => AggregateType.Vector2 | AggregateType.FP32, + 3 => AggregateType.Vector3 | AggregateType.FP32, + 4 => AggregateType.Vector4 | AggregateType.FP32, + _ => AggregateType.FP32 + }; + attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false); } } @@ -335,7 +365,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv bool isIndexed = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)); - if ((type & (AggregateType.Array | AggregateType.Vector)) == 0) + if ((type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0) { if (invocationId != null) { @@ -452,7 +482,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv elemType = attrInfo.Type & AggregateType.ElementTypeMask; - if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0) + if ((attrInfo.Type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0) { return ioVariable; } @@ -533,13 +563,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Instruction GetLocal(AggregateType dstType, AstOperand local) { - var srcType = local.VarType.Convert(); + var srcType = local.VarType; return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetLocalPointer(local))); } public Instruction GetArgument(AggregateType dstType, AstOperand funcArg) { - var srcType = funcArg.VarType.Convert(); + var srcType = funcArg.VarType; return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetArgumentPointer(funcArg))); } @@ -550,13 +580,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Instruction GetType(AggregateType type, int length = 1) { - if (type.HasFlag(AggregateType.Array)) + if ((type & AggregateType.Array) != 0) { return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length)); } - else if (type.HasFlag(AggregateType.Vector)) + else if ((type & AggregateType.ElementCountMask) != 0) { - return TypeVector(GetType(type & ~AggregateType.Vector), length); + int vectorLength = (type & AggregateType.ElementCountMask) switch + { + AggregateType.Vector2 => 2, + AggregateType.Vector3 => 3, + AggregateType.Vector4 => 4, + _ => 1 + }; + + return TypeVector(GetType(type & ~AggregateType.ElementCountMask), vectorLength); } return type switch diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 819ece416..3da72b402 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -23,11 +23,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv DeclareParameters(context, function.OutArguments, function.InArguments.Length); } - private static void DeclareParameters(CodeGenContext context, IEnumerable argTypes, int argIndex) + private static void DeclareParameters(CodeGenContext context, IEnumerable argTypes, int argIndex) { foreach (var argType in argTypes) { - var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType.Convert())); + var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType)); var spvArg = context.FunctionParameter(argPointerType); context.DeclareArgument(argIndex++, spvArg); @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { foreach (AstOperand local in function.Locals) { - var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType.Convert())); + var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType)); var spvLocal = context.Variable(localPointerType, StorageClass.Function); context.AddLocalVariable(spvLocal); @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv for (int i = 0; i < function.InArguments.Length; i++) { - var type = function.GetArgumentType(i).Convert(); + var type = function.GetArgumentType(i); var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type)); var spvLocal = context.Variable(localPointerType, StorageClass.Function); @@ -303,7 +303,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var dim = GetDim(descriptor.Type); var imageType = context.TypeImage( - context.GetType(meta.Format.GetComponentType().Convert()), + context.GetType(meta.Format.GetComponentType()), dim, descriptor.Type.HasFlag(SamplerType.Shadow), descriptor.Type.HasFlag(SamplerType.Array), @@ -652,7 +652,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (components > 1) { attr &= ~0xf; - type = AggregateType.Vector | AggregateType.FP32; + type = components switch + { + 2 => AggregateType.Vector2 | AggregateType.FP32, + 3 => AggregateType.Vector3 | AggregateType.FP32, + 4 => AggregateType.Vector4 | AggregateType.FP32, + _ => AggregateType.FP32 + }; + hasComponent = false; } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs index 0ddb42640..aa3d046a2 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs @@ -1,5 +1,4 @@ -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; +using Ryujinx.Graphics.Shader.Translation; using System; using static Spv.Specification; @@ -7,20 +6,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { static class EnumConversion { - public static AggregateType Convert(this VariableType type) - { - return type switch - { - VariableType.None => AggregateType.Void, - VariableType.Bool => AggregateType.Bool, - VariableType.F32 => AggregateType.FP32, - VariableType.F64 => AggregateType.FP64, - VariableType.S32 => AggregateType.S32, - VariableType.U32 => AggregateType.U32, - _ => throw new ArgumentException($"Invalid variable type \"{type}\".") - }; - } - public static ExecutionModel Convert(this ShaderStage stage) { return stage switch diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index ae2803779..a02c4c22a 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Numerics; using static Spv.Specification; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv @@ -146,6 +147,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.Truncate, GenerateTruncate); Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32); Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16); + Add(Instruction.VectorExtract, GenerateVectorExtract); Add(Instruction.VoteAll, GenerateVoteAll); Add(Instruction.VoteAllEqual, GenerateVoteAllEqual); Add(Instruction.VoteAny, GenerateVoteAny); @@ -317,7 +319,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else { - var type = function.GetArgumentType(i).Convert(); + var type = function.GetArgumentType(i); var value = context.Get(type, operand); var spvLocal = spvLocals[i]; @@ -327,7 +329,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - var retType = function.ReturnType.Convert(); + var retType = function.ReturnType; var result = context.FunctionCall(context.GetType(retType), spvFunc, args); return new OperationResult(retType, result); } @@ -604,10 +606,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv // TODO: Bindless texture support. For now we just return 0/do nothing. if (isBindless) { - return new OperationResult(componentType.Convert(), componentType switch + return new OperationResult(componentType, componentType switch { - VariableType.S32 => context.Constant(context.TypeS32(), 0), - VariableType.U32 => context.Constant(context.TypeU32(), 0u), + AggregateType.S32 => context.Constant(context.TypeS32(), 0), + AggregateType.U32 => context.Constant(context.TypeU32(), 0u), _ => context.Constant(context.TypeFP32(), 0f), }); } @@ -652,13 +654,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv pCoords = Src(AggregateType.S32); } - SpvInstruction value = Src(componentType.Convert()); + SpvInstruction value = Src(componentType); (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; var image = context.Load(imageType, imageVariable); - SpvInstruction resultType = context.GetType(componentType.Convert()); + SpvInstruction resultType = context.GetType(componentType); SpvInstruction imagePointerType = context.TypePointer(StorageClass.Image, resultType); var pointer = context.ImageTexelPointer(imagePointerType, imageVariable, pCoords, context.Constant(context.TypeU32(), 0)); @@ -668,10 +670,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var result = (texOp.Flags & TextureFlags.AtomicMask) switch { TextureFlags.Add => context.AtomicIAdd(resultType, pointer, one, zero, value), - TextureFlags.Minimum => componentType == VariableType.S32 + TextureFlags.Minimum => componentType == AggregateType.S32 ? context.AtomicSMin(resultType, pointer, one, zero, value) : context.AtomicUMin(resultType, pointer, one, zero, value), - TextureFlags.Maximum => componentType == VariableType.S32 + TextureFlags.Maximum => componentType == AggregateType.S32 ? context.AtomicSMax(resultType, pointer, one, zero, value) : context.AtomicUMax(resultType, pointer, one, zero, value), TextureFlags.Increment => context.AtomicIIncrement(resultType, pointer, one, zero), @@ -680,11 +682,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv TextureFlags.BitwiseOr => context.AtomicOr(resultType, pointer, one, zero, value), TextureFlags.BitwiseXor => context.AtomicXor(resultType, pointer, one, zero, value), TextureFlags.Swap => context.AtomicExchange(resultType, pointer, one, zero, value), - TextureFlags.CAS => context.AtomicCompareExchange(resultType, pointer, one, zero, zero, Src(componentType.Convert()), value), + TextureFlags.CAS => context.AtomicCompareExchange(resultType, pointer, one, zero, zero, Src(componentType), value), _ => context.AtomicIAdd(resultType, pointer, one, zero, value), }; - return new OperationResult(componentType.Convert(), result); + return new OperationResult(componentType, result); } private static OperationResult GenerateImageLoad(CodeGenContext context, AstOperation operation) @@ -698,14 +700,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv // TODO: Bindless texture support. For now we just return 0/do nothing. if (isBindless) { - var zero = componentType switch - { - VariableType.S32 => context.Constant(context.TypeS32(), 0), - VariableType.U32 => context.Constant(context.TypeU32(), 0u), - _ => context.Constant(context.TypeFP32(), 0f), - }; - - return new OperationResult(componentType.Convert(), zero); + return GetZeroOperationResult(context, texOp, componentType, isVector: true); } bool isArray = (texOp.Type & SamplerType.Array) != 0; @@ -753,12 +748,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; var image = context.Load(imageType, imageVariable); - var imageComponentType = context.GetType(componentType.Convert()); + var imageComponentType = context.GetType(componentType); + var swizzledResultType = texOp.GetVectorType(componentType); var texel = context.ImageRead(context.TypeVector(imageComponentType, 4), image, pCoords, ImageOperandsMask.MaskNone); - var result = context.CompositeExtract(imageComponentType, texel, (SpvLiteralInteger)texOp.Index); + var result = GetSwizzledResult(context, texel, swizzledResultType, texOp.Index); - return new OperationResult(componentType.Convert(), result); + return new OperationResult(componentType, result); } private static OperationResult GenerateImageStore(CodeGenContext context, AstOperation operation) @@ -823,20 +819,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { if (srcIndex < texOp.SourcesCount) { - cElems[i] = Src(componentType.Convert()); + cElems[i] = Src(componentType); } else { cElems[i] = componentType switch { - VariableType.S32 => context.Constant(context.TypeS32(), 0), - VariableType.U32 => context.Constant(context.TypeU32(), 0u), + AggregateType.S32 => context.Constant(context.TypeS32(), 0), + AggregateType.U32 => context.Constant(context.TypeU32(), 0u), _ => context.Constant(context.TypeFP32(), 0f), }; } } - var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType.Convert()), ComponentsCount), cElems); + var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType), ComponentsCount), cElems); (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; @@ -1238,7 +1234,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var validLocal = (AstOperand)operation.GetSource(3); - context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid)); + context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid)); return new OperationResult(AggregateType.FP32, result); } @@ -1268,7 +1264,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var validLocal = (AstOperand)operation.GetSource(3); - context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid)); + context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid)); return new OperationResult(AggregateType.FP32, result); } @@ -1294,7 +1290,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var validLocal = (AstOperand)operation.GetSource(3); - context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid)); + context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid)); return new OperationResult(AggregateType.FP32, result); } @@ -1324,7 +1320,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var validLocal = (AstOperand)operation.GetSource(3); - context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType.Convert(), AggregateType.Bool, valid)); + context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid)); return new OperationResult(AggregateType.FP32, result); } @@ -1485,10 +1481,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; + bool colorIsVector = isGather || !isShadow; + // TODO: Bindless texture support. For now we just return 0. if (isBindless) { - return new OperationResult(AggregateType.FP32, context.Constant(context.TypeFP32(), 0f)); + return GetZeroOperationResult(context, texOp, AggregateType.FP32, colorIsVector); } // This combination is valid, but not available on GLSL. @@ -1705,7 +1703,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv operandsList.Add(sample); } - bool colorIsVector = isGather || !isShadow; var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32(); var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); @@ -1758,12 +1755,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv result = context.ImageSampleImplicitLod(resultType, image, pCoords, operandsMask, operands); } + var swizzledResultType = AggregateType.FP32; + if (colorIsVector) { - result = context.CompositeExtract(context.TypeFP32(), result, (SpvLiteralInteger)texOp.Index); + swizzledResultType = texOp.GetVectorType(swizzledResultType); + + result = GetSwizzledResult(context, result, swizzledResultType, texOp.Index); } - return new OperationResult(AggregateType.FP32, result); + return new OperationResult(swizzledResultType, result); } private static OperationResult GenerateTextureSize(CodeGenContext context, AstOperation operation) @@ -1862,6 +1863,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(AggregateType.FP32, result); } + private static OperationResult GenerateVectorExtract(CodeGenContext context, AstOperation operation) + { + var vector = context.GetWithType(operation.GetSource(0), out AggregateType vectorType); + var scalarType = vectorType & ~AggregateType.ElementCountMask; + var resultType = context.GetType(scalarType); + SpvInstruction result; + + if (operation.GetSource(1) is AstOperand indexOperand && indexOperand.Type == OperandType.Constant) + { + result = context.CompositeExtract(resultType, vector, (SpvLiteralInteger)indexOperand.Value); + } + else + { + var index = context.Get(AggregateType.S32, operation.GetSource(1)); + result = context.VectorExtractDynamic(resultType, vector, index); + } + + return new OperationResult(scalarType, result); + } + private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation) { var execution = context.Constant(context.TypeU32(), Scope.Subgroup); @@ -2044,6 +2065,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddLabel(loopEnd); } + private static OperationResult GetZeroOperationResult( + CodeGenContext context, + AstTextureOperation texOp, + AggregateType scalarType, + bool isVector) + { + var zero = scalarType switch + { + AggregateType.S32 => context.Constant(context.TypeS32(), 0), + AggregateType.U32 => context.Constant(context.TypeU32(), 0u), + _ => context.Constant(context.TypeFP32(), 0f), + }; + + if (isVector) + { + AggregateType outputType = texOp.GetVectorType(scalarType); + + if ((outputType & AggregateType.ElementCountMask) != 0) + { + int componentsCount = BitOperations.PopCount((uint)texOp.Index); + + SpvInstruction[] values = new SpvInstruction[componentsCount]; + + values.AsSpan().Fill(zero); + + return new OperationResult(outputType, context.ConstantComposite(context.GetType(outputType), values)); + } + } + + return new OperationResult(scalarType, zero); + } + + private static SpvInstruction GetSwizzledResult(CodeGenContext context, SpvInstruction vector, AggregateType swizzledResultType, int mask) + { + if ((swizzledResultType & AggregateType.ElementCountMask) != 0) + { + SpvLiteralInteger[] components = new SpvLiteralInteger[BitOperations.PopCount((uint)mask)]; + + int componentIndex = 0; + + for (int i = 0; i < 4; i++) + { + if ((mask & (1 << i)) != 0) + { + components[componentIndex++] = i; + } + } + + return context.VectorShuffle(context.GetType(swizzledResultType), vector, vector, components); + } + else + { + int componentIndex = (int)BitOperations.TrailingZeroCount(mask); + + return context.CompositeExtract(context.GetType(swizzledResultType), vector, (SpvLiteralInteger)componentIndex); + } + } + private static SpvInstruction GetStorageElemPointer(CodeGenContext context, AstOperation operation) { var sbVariable = context.StorageBuffersArray; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 6e1db972d..9f08b319d 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -104,13 +104,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++) { var function = info.Functions[funcIndex]; - var retType = context.GetType(function.ReturnType.Convert()); + var retType = context.GetType(function.ReturnType); var funcArgs = new SpvInstruction[function.InArguments.Length + function.OutArguments.Length]; for (int argIndex = 0; argIndex < funcArgs.Length; argIndex++) { - var argType = context.GetType(function.GetArgumentType(argIndex).Convert()); + var argType = context.GetType(function.GetArgumentType(argIndex)); var argPointerType = context.TypePointer(StorageClass.Function, argType); funcArgs[argIndex] = argPointerType; } @@ -387,7 +387,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (dest.Type == OperandType.LocalVariable) { - var source = context.Get(dest.VarType.Convert(), assignment.Source); + var source = context.Get(dest.VarType, assignment.Source); context.Store(context.GetLocalPointer(dest), source); } else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch) @@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else if (dest.Type == OperandType.Argument) { - var source = context.Get(dest.VarType.Convert(), assignment.Source); + var source = context.Get(dest.VarType, assignment.Source); context.Store(context.GetArgumentPointer(dest), source); } else diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index a81362b8b..3d94b8938 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -1,7 +1,9 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; +using System; using System.Collections.Generic; +using System.Numerics; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -217,15 +219,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(srcB++, RegisterType.Gpr)); } - Operand GetDest() - { - if (dest >= RegisterConsts.RegisterZeroIndex) - { - return null; - } - - return Register(dest++, RegisterType.Gpr); - } + Operand destOperand = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null; List sourcesList = new List(); @@ -291,7 +285,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags, imm, 0, - GetDest(), + new[] { destOperand }, sources); context.Add(operation); @@ -371,36 +365,40 @@ namespace Ryujinx.Graphics.Shader.Instructions if (useComponents) { - for (int compMask = (int)componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) - { - if ((compMask & 1) == 0) - { - continue; - } + Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)]; - if (srcB == RegisterConsts.RegisterZeroIndex) + int outputIndex = 0; + + for (int i = 0; i < dests.Length; i++) + { + if (srcB + i >= RegisterConsts.RegisterZeroIndex) { break; } - Operand rd = Register(srcB++, RegisterType.Gpr); - - TextureOperation operation = context.CreateTextureOperation( - Instruction.ImageLoad, - type, - flags, - handle, - compIndex, - rd, - sources); - - if (!isBindless) - { - operation.Format = context.Config.GetTextureFormat(handle); - } - - context.Add(operation); + dests[outputIndex++] = Register(srcB + i, RegisterType.Gpr); } + + if (outputIndex != dests.Length) + { + Array.Resize(ref dests, outputIndex); + } + + TextureOperation operation = context.CreateTextureOperation( + Instruction.ImageLoad, + type, + flags, + handle, + (int)componentMask, + dests, + sources); + + if (!isBindless) + { + operation.Format = context.Config.GetTextureFormat(handle); + } + + context.Add(operation); } else { @@ -412,35 +410,45 @@ namespace Ryujinx.Graphics.Shader.Instructions } int components = GetComponents(size); + int compMask = (1 << components) - 1; - for (int compIndex = 0; compIndex < components; compIndex++) + Operand[] dests = new Operand[components]; + + int outputIndex = 0; + + for (int i = 0; i < dests.Length; i++) { - if (srcB == RegisterConsts.RegisterZeroIndex) + if (srcB + i >= RegisterConsts.RegisterZeroIndex) { break; } - Operand rd = Register(srcB++, RegisterType.Gpr); + dests[outputIndex++] = Register(srcB + i, RegisterType.Gpr); + } - TextureOperation operation = context.CreateTextureOperation( - Instruction.ImageLoad, - type, - GetTextureFormat(size), - flags, - handle, - compIndex, - rd, - sources); + if (outputIndex != dests.Length) + { + Array.Resize(ref dests, outputIndex); + } - context.Add(operation); + TextureOperation operation = context.CreateTextureOperation( + Instruction.ImageLoad, + type, + GetTextureFormat(size), + flags, + handle, + compMask, + dests, + sources); - switch (size) - { - case SuSize.U8: context.Copy(rd, ZeroExtendTo32(context, rd, 8)); break; - case SuSize.U16: context.Copy(rd, ZeroExtendTo32(context, rd, 16)); break; - case SuSize.S8: context.Copy(rd, SignExtendTo32(context, rd, 8)); break; - case SuSize.S16: context.Copy(rd, SignExtendTo32(context, rd, 16)); break; - } + context.Add(operation); + + switch (size) + { + case SuSize.U8: context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 8)); break; + case SuSize.U16: context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 16)); break; + case SuSize.S8: context.Copy(dests[0], SignExtendTo32(context, dests[0], 8)); break; + case SuSize.S16: context.Copy(dests[0], SignExtendTo32(context, dests[0], 16)); break; } } } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index c54b79cdc..caa9a7759 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; +using System.Numerics; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -303,42 +304,37 @@ namespace Ryujinx.Graphics.Shader.Instructions } Operand[] sources = sourcesList.ToArray(); + Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)]; - Operand GetDest() + int outputIndex = 0; + + for (int i = 0; i < dests.Length; i++) { - if (rdIndex >= RegisterConsts.RegisterZeroIndex) + if (rdIndex + i >= RegisterConsts.RegisterZeroIndex) { - return null; + break; } - return Register(rdIndex++, RegisterType.Gpr); + dests[outputIndex++] = Register(rdIndex + i, RegisterType.Gpr); + } + + if (outputIndex != dests.Length) + { + Array.Resize(ref dests, outputIndex); } int handle = !isBindless ? imm : 0; - for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) - { - if ((compMask & 1) != 0) - { - Operand dest = GetDest(); + TextureOperation operation = context.CreateTextureOperation( + Instruction.TextureSample, + type, + flags, + handle, + componentMask, + dests, + sources); - if (dest == null) - { - break; - } - - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - compIndex, - dest, - sources); - - context.Add(operation); - } - } + context.Add(operation); } private static void EmitTexs( @@ -624,18 +620,23 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) }; Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) }; - int destIncrement = 0; + int handle = imm; + int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask]; - Operand GetDest() + int componentsCount = BitOperations.PopCount((uint)componentMask); + + Operand[] dests = new Operand[componentsCount]; + + int outputIndex = 0; + + for (int i = 0; i < componentsCount; i++) { - int high = destIncrement >> 1; - int low = destIncrement & 1; - - destIncrement++; + int high = i >> 1; + int low = i & 1; if (isF16) { - return high != 0 + dests[outputIndex++] = high != 0 ? (rd1[low] = Local()) : (rd0[low] = Local()); } @@ -648,30 +649,26 @@ namespace Ryujinx.Graphics.Shader.Instructions rdIndex += low; } - return Register(rdIndex, RegisterType.Gpr); + dests[outputIndex++] = Register(rdIndex, RegisterType.Gpr); } } - int handle = imm; - int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask]; - - for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + if (outputIndex != dests.Length) { - if ((compMask & 1) != 0) - { - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - compIndex, - GetDest(), - sources); - - context.Add(operation); - } + Array.Resize(ref dests, outputIndex); } + TextureOperation operation = context.CreateTextureOperation( + Instruction.TextureSample, + type, + flags, + handle, + componentMask, + dests, + sources); + + context.Add(operation); + if (isF16) { context.Copy(Register(dest, RegisterType.Gpr), context.PackHalf2x16(rd0[0], rd0[1])); @@ -797,42 +794,37 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(Const((int)component)); Operand[] sources = sourcesList.ToArray(); + Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)]; - Operand GetDest() + int outputIndex = 0; + + for (int i = 0; i < dests.Length; i++) { - if (dest >= RegisterConsts.RegisterZeroIndex) + if (dest + i >= RegisterConsts.RegisterZeroIndex) { - return null; + break; } - return Register(dest++, RegisterType.Gpr); + dests[outputIndex++] = Register(dest + i, RegisterType.Gpr); + } + + if (outputIndex != dests.Length) + { + Array.Resize(ref dests, outputIndex); } int handle = imm; - for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) - { - if ((compMask & 1) != 0) - { - Operand destOperand = GetDest(); + TextureOperation operation = context.CreateTextureOperation( + Instruction.TextureSample, + type, + flags, + handle, + componentMask, + dests, + sources); - if (destOperand == null) - { - break; - } - - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - compIndex, - destOperand, - sources); - - context.Add(operation); - } - } + context.Add(operation); } private static void EmitTmml( @@ -951,7 +943,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags, handle, compIndex ^ 1, // The instruction component order is the inverse of GLSL's. - tempDest, + new[] { tempDest }, sources); context.Add(operation); @@ -1071,42 +1063,37 @@ namespace Ryujinx.Graphics.Shader.Instructions } Operand[] sources = sourcesList.ToArray(); + Operand[] dests = new Operand[BitOperations.PopCount((uint)componentMask)]; - Operand GetDest() + int outputIndex = 0; + + for (int i = 0; i < dests.Length; i++) { - if (dest >= RegisterConsts.RegisterZeroIndex) + if (dest + i >= RegisterConsts.RegisterZeroIndex) { - return null; + break; } - return Register(dest++, RegisterType.Gpr); + dests[outputIndex++] = Register(dest + i, RegisterType.Gpr); + } + + if (outputIndex != dests.Length) + { + Array.Resize(ref dests, outputIndex); } int handle = imm; - for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) - { - if ((compMask & 1) != 0) - { - Operand destOperand = GetDest(); + TextureOperation operation = context.CreateTextureOperation( + Instruction.TextureSample, + type, + flags, + handle, + componentMask, + dests, + sources); - if (destOperand == null) - { - break; - } - - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - compIndex, - destOperand, - sources); - - context.Add(operation); - } - } + context.Add(operation); } private static void EmitTxq( @@ -1188,7 +1175,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags, imm, compIndex, - destOperand, + new[] { destOperand }, sources); context.Add(operation); diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 9a2c844d1..aa9776bcf 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -134,6 +134,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Truncate, UnpackDouble2x32, UnpackHalf2x16, + VectorExtract, VoteAll, VoteAllEqual, VoteAny, diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index 961326338..18e203a70 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -62,18 +62,25 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Inst = inst; Index = index; - // The array may be modified externally, so we store a copy. - _dests = (Operand[])dests.Clone(); - - for (int dstIndex = 0; dstIndex < dests.Length; dstIndex++) + if (dests != null) { - Operand dest = dests[dstIndex]; + // The array may be modified externally, so we store a copy. + _dests = (Operand[])dests.Clone(); - if (dest != null && dest.Type == OperandType.LocalVariable) + for (int dstIndex = 0; dstIndex < dests.Length; dstIndex++) { - dest.AsgOp = this; + Operand dest = dests[dstIndex]; + + if (dest != null && dest.Type == OperandType.LocalVariable) + { + dest.AsgOp = this; + } } } + else + { + _dests = Array.Empty(); + } } public Operation(Instruction inst, Operand dest, params Operand[] sources) : this(sources) diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index 8cfcb0e9a..6ab868cdc 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -19,8 +19,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation int cbufSlot, int handle, int compIndex, - Operand dest, - Operand[] sources) : base(inst, compIndex, dest, sources) + Operand[] dests, + Operand[] sources) : base(inst, compIndex, dests, sources) { Type = type; Format = format; @@ -36,8 +36,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation TextureFlags flags, int handle, int compIndex, - Operand dest, - Operand[] sources) : this(inst, type, format, flags, DefaultCbufSlot, handle, compIndex, dest, sources) + Operand[] dests, + Operand[] sources) : this(inst, type, format, flags, DefaultCbufSlot, handle, compIndex, dests, sources) { } diff --git a/Ryujinx.Graphics.Shader/SamplerType.cs b/Ryujinx.Graphics.Shader/SamplerType.cs index d04b16b38..620f4ccf3 100644 --- a/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/Ryujinx.Graphics.Shader/SamplerType.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System; namespace Ryujinx.Graphics.Shader @@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Shader return typeName; } - public static string ToGlslImageType(this SamplerType type, VariableType componentType) + public static string ToGlslImageType(this SamplerType type, AggregateType componentType) { string typeName = (type & SamplerType.Mask) switch { @@ -90,8 +90,8 @@ namespace Ryujinx.Graphics.Shader switch (componentType) { - case VariableType.U32: typeName = 'u' + typeName; break; - case VariableType.S32: typeName = 'i' + typeName; break; + case AggregateType.U32: typeName = 'u' + typeName; break; + case AggregateType.S32: typeName = 'i' + typeName; break; } return typeName; diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs index 9d3148e1b..7aa0409b6 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; namespace Ryujinx.Graphics.Shader.StructuredIr { @@ -46,7 +47,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return new AstOperand(OperandType.Constant, value); } - public static AstOperand Local(VariableType type) + public static AstOperand Local(AggregateType type) { AstOperand local = new AstOperand(OperandType.LocalVariable); diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs index 97ff3ca97..1fc0035f2 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.StructuredIr @@ -10,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public OperandType Type { get; } - public VariableType VarType { get; set; } + public AggregateType VarType { get; set; } public int Value { get; } @@ -22,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Defs = new HashSet(); Uses = new HashSet(); - VarType = VariableType.S32; + VarType = AggregateType.S32; } public AstOperand(Operand operand) : this() diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs index a8474955b..193972565 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs @@ -1,4 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System.Numerics; using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; @@ -56,5 +58,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr _sources[index] = source; } + + public AggregateType GetVectorType(AggregateType scalarType) + { + int componentsCount = BitOperations.PopCount((uint)Index); + + AggregateType type = scalarType; + + switch (componentsCount) + { + case 2: type |= AggregateType.Vector2; break; + case 3: type |= AggregateType.Vector3; break; + case 4: type |= AggregateType.Vector4; break; + } + + return type; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index aea36423e..0a9a9e511 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; using System; namespace Ryujinx.Graphics.Shader.StructuredIr @@ -7,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { private readonly struct InstInfo { - public VariableType DestType { get; } + public AggregateType DestType { get; } - public VariableType[] SrcTypes { get; } + public AggregateType[] SrcTypes { get; } - public InstInfo(VariableType destType, params VariableType[] srcTypes) + public InstInfo(AggregateType destType, params AggregateType[] srcTypes) { DestType = destType; SrcTypes = srcTypes; @@ -24,176 +25,173 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { _infoTbl = new InstInfo[(int)Instruction.Count]; - // Inst Destination type Source 1 type Source 2 type Source 3 type Source 4 type - Add(Instruction.AtomicAdd, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.AtomicAnd, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.AtomicCompareAndSwap, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32, VariableType.U32); - Add(Instruction.AtomicMaxS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32); - Add(Instruction.AtomicMaxU32, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.AtomicMinS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32); - Add(Instruction.AtomicMinU32, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.AtomicOr, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.AtomicSwap, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.AtomicXor, VariableType.U32, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.Absolute, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.Add, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.Ballot, VariableType.U32, VariableType.Bool); - Add(Instruction.BitCount, VariableType.Int, VariableType.Int); - Add(Instruction.BitfieldExtractS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32); - Add(Instruction.BitfieldExtractU32, VariableType.U32, VariableType.U32, VariableType.S32, VariableType.S32); - Add(Instruction.BitfieldInsert, VariableType.Int, VariableType.Int, VariableType.Int, VariableType.S32, VariableType.S32); - Add(Instruction.BitfieldReverse, VariableType.Int, VariableType.Int); - Add(Instruction.BitwiseAnd, VariableType.Int, VariableType.Int, VariableType.Int); - Add(Instruction.BitwiseExclusiveOr, VariableType.Int, VariableType.Int, VariableType.Int); - Add(Instruction.BitwiseNot, VariableType.Int, VariableType.Int); - Add(Instruction.BitwiseOr, VariableType.Int, VariableType.Int, VariableType.Int); - Add(Instruction.BranchIfTrue, VariableType.None, VariableType.Bool); - Add(Instruction.BranchIfFalse, VariableType.None, VariableType.Bool); - Add(Instruction.Call, VariableType.Scalar); - Add(Instruction.Ceiling, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.Clamp, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.ClampU32, VariableType.U32, VariableType.U32, VariableType.U32, VariableType.U32); - Add(Instruction.CompareEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.CompareGreater, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.CompareGreaterOrEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.CompareGreaterOrEqualU32, VariableType.Bool, VariableType.U32, VariableType.U32); - Add(Instruction.CompareGreaterU32, VariableType.Bool, VariableType.U32, VariableType.U32); - Add(Instruction.CompareLess, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.CompareLessOrEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.CompareLessOrEqualU32, VariableType.Bool, VariableType.U32, VariableType.U32); - Add(Instruction.CompareLessU32, VariableType.Bool, VariableType.U32, VariableType.U32); - Add(Instruction.CompareNotEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.ConditionalSelect, VariableType.Scalar, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.ConvertFP32ToFP64, VariableType.F64, VariableType.F32); - Add(Instruction.ConvertFP64ToFP32, VariableType.F32, VariableType.F64); - Add(Instruction.ConvertFP32ToS32, VariableType.S32, VariableType.F32); - Add(Instruction.ConvertFP32ToU32, VariableType.U32, VariableType.F32); - Add(Instruction.ConvertFP64ToS32, VariableType.S32, VariableType.F64); - Add(Instruction.ConvertFP64ToU32, VariableType.U32, VariableType.F64); - Add(Instruction.ConvertS32ToFP32, VariableType.F32, VariableType.S32); - Add(Instruction.ConvertS32ToFP64, VariableType.F64, VariableType.S32); - Add(Instruction.ConvertU32ToFP32, VariableType.F32, VariableType.U32); - Add(Instruction.ConvertU32ToFP64, VariableType.F64, VariableType.U32); - Add(Instruction.Cosine, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.Ddx, VariableType.F32, VariableType.F32); - Add(Instruction.Ddy, VariableType.F32, VariableType.F32); - Add(Instruction.Divide, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.ExponentB2, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.FindLSB, VariableType.Int, VariableType.Int); - Add(Instruction.FindMSBS32, VariableType.S32, VariableType.S32); - Add(Instruction.FindMSBU32, VariableType.S32, VariableType.U32); - Add(Instruction.Floor, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.FusedMultiplyAdd, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.ImageLoad, VariableType.F32); - Add(Instruction.ImageStore, VariableType.None); - Add(Instruction.ImageAtomic, VariableType.S32); - Add(Instruction.IsNan, VariableType.Bool, VariableType.Scalar); - Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32, VariableType.S32); - Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32); - Add(Instruction.LoadGlobal, VariableType.U32, VariableType.S32, VariableType.S32); - Add(Instruction.LoadLocal, VariableType.U32, VariableType.S32); - Add(Instruction.LoadShared, VariableType.U32, VariableType.S32); - Add(Instruction.LoadStorage, VariableType.U32, VariableType.S32, VariableType.S32); - Add(Instruction.Lod, VariableType.F32); - Add(Instruction.LogarithmB2, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.LogicalAnd, VariableType.Bool, VariableType.Bool, VariableType.Bool); - Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool); - Add(Instruction.LogicalNot, VariableType.Bool, VariableType.Bool); - Add(Instruction.LogicalOr, VariableType.Bool, VariableType.Bool, VariableType.Bool); - Add(Instruction.Maximum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.MaximumU32, VariableType.U32, VariableType.U32, VariableType.U32); - Add(Instruction.Minimum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.MinimumU32, VariableType.U32, VariableType.U32, VariableType.U32); - Add(Instruction.Multiply, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.MultiplyHighS32, VariableType.S32, VariableType.S32, VariableType.S32); - Add(Instruction.MultiplyHighU32, VariableType.U32, VariableType.U32, VariableType.U32); - Add(Instruction.Negate, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.PackDouble2x32, VariableType.F64, VariableType.U32, VariableType.U32); - Add(Instruction.PackHalf2x16, VariableType.U32, VariableType.F32, VariableType.F32); - Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.Round, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.ShiftLeft, VariableType.Int, VariableType.Int, VariableType.Int); - Add(Instruction.ShiftRightS32, VariableType.S32, VariableType.S32, VariableType.Int); - Add(Instruction.ShiftRightU32, VariableType.U32, VariableType.U32, VariableType.Int); - Add(Instruction.Shuffle, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool); - Add(Instruction.ShuffleDown, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool); - Add(Instruction.ShuffleUp, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool); - Add(Instruction.ShuffleXor, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool); - Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.StoreAttribute, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32); - Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.StoreLocal, VariableType.None, VariableType.S32, VariableType.U32); - Add(Instruction.StoreShared, VariableType.None, VariableType.S32, VariableType.U32); - Add(Instruction.StoreShared16, VariableType.None, VariableType.S32, VariableType.U32); - Add(Instruction.StoreShared8, VariableType.None, VariableType.S32, VariableType.U32); - Add(Instruction.StoreStorage, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.StoreStorage16, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.StoreStorage8, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32); - Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.SwizzleAdd, VariableType.F32, VariableType.F32, VariableType.F32, VariableType.S32); - Add(Instruction.TextureSample, VariableType.F32); - Add(Instruction.TextureSize, VariableType.S32, VariableType.S32, VariableType.S32); - Add(Instruction.Truncate, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.UnpackDouble2x32, VariableType.U32, VariableType.F64); - Add(Instruction.UnpackHalf2x16, VariableType.F32, VariableType.U32); - Add(Instruction.VoteAll, VariableType.Bool, VariableType.Bool); - Add(Instruction.VoteAllEqual, VariableType.Bool, VariableType.Bool); - Add(Instruction.VoteAny, VariableType.Bool, VariableType.Bool); + // Inst Destination type Source 1 type Source 2 type Source 3 type Source 4 type + Add(Instruction.AtomicAdd, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.AtomicAnd, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.AtomicCompareAndSwap, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32, AggregateType.U32); + Add(Instruction.AtomicMaxS32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.AtomicMaxU32, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.AtomicMinS32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.AtomicMinU32, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.AtomicOr, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.AtomicSwap, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.AtomicXor, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.Absolute, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.Add, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.Ballot, AggregateType.U32, AggregateType.Bool); + Add(Instruction.BitCount, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitfieldExtractS32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitfieldExtractU32, AggregateType.U32, AggregateType.U32, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitfieldInsert, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitfieldReverse, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitwiseAnd, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitwiseExclusiveOr, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitwiseNot, AggregateType.S32, AggregateType.S32); + Add(Instruction.BitwiseOr, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.BranchIfTrue, AggregateType.Void, AggregateType.Bool); + Add(Instruction.BranchIfFalse, AggregateType.Void, AggregateType.Bool); + Add(Instruction.Call, AggregateType.Scalar); + Add(Instruction.Ceiling, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.Clamp, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.ClampU32, AggregateType.U32, AggregateType.U32, AggregateType.U32, AggregateType.U32); + Add(Instruction.CompareEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.CompareGreater, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.CompareGreaterOrEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.CompareGreaterOrEqualU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32); + Add(Instruction.CompareGreaterU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32); + Add(Instruction.CompareLess, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.CompareLessOrEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.CompareLessOrEqualU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32); + Add(Instruction.CompareLessU32, AggregateType.Bool, AggregateType.U32, AggregateType.U32); + Add(Instruction.CompareNotEqual, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.ConditionalSelect, AggregateType.Scalar, AggregateType.Bool, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.ConvertFP32ToFP64, AggregateType.FP64, AggregateType.FP32); + Add(Instruction.ConvertFP64ToFP32, AggregateType.FP32, AggregateType.FP64); + Add(Instruction.ConvertFP32ToS32, AggregateType.S32, AggregateType.FP32); + Add(Instruction.ConvertFP32ToU32, AggregateType.U32, AggregateType.FP32); + Add(Instruction.ConvertFP64ToS32, AggregateType.S32, AggregateType.FP64); + Add(Instruction.ConvertFP64ToU32, AggregateType.U32, AggregateType.FP64); + Add(Instruction.ConvertS32ToFP32, AggregateType.FP32, AggregateType.S32); + Add(Instruction.ConvertS32ToFP64, AggregateType.FP64, AggregateType.S32); + Add(Instruction.ConvertU32ToFP32, AggregateType.FP32, AggregateType.U32); + Add(Instruction.ConvertU32ToFP64, AggregateType.FP64, AggregateType.U32); + Add(Instruction.Cosine, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.Ddx, AggregateType.FP32, AggregateType.FP32); + Add(Instruction.Ddy, AggregateType.FP32, AggregateType.FP32); + Add(Instruction.Divide, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.ExponentB2, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.FindLSB, AggregateType.S32, AggregateType.S32); + Add(Instruction.FindMSBS32, AggregateType.S32, AggregateType.S32); + Add(Instruction.FindMSBU32, AggregateType.S32, AggregateType.U32); + Add(Instruction.Floor, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.FusedMultiplyAdd, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.ImageLoad, AggregateType.FP32); + Add(Instruction.ImageStore, AggregateType.Void); + Add(Instruction.ImageAtomic, AggregateType.S32); + Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar); + Add(Instruction.LoadAttribute, AggregateType.FP32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32); + Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32); + Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32); + Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32); + Add(Instruction.LoadStorage, AggregateType.U32, AggregateType.S32, AggregateType.S32); + Add(Instruction.Lod, AggregateType.FP32); + Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.LogicalAnd, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool); + Add(Instruction.LogicalExclusiveOr, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool); + Add(Instruction.LogicalNot, AggregateType.Bool, AggregateType.Bool); + Add(Instruction.LogicalOr, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool); + Add(Instruction.Maximum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); + Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); + Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); + Add(Instruction.Negate, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.PackDouble2x32, AggregateType.FP64, AggregateType.U32, AggregateType.U32); + Add(Instruction.PackHalf2x16, AggregateType.U32, AggregateType.FP32, AggregateType.FP32); + Add(Instruction.ReciprocalSquareRoot, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.Round, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.ShiftLeft, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.ShiftRightS32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.ShiftRightU32, AggregateType.U32, AggregateType.U32, AggregateType.S32); + Add(Instruction.Shuffle, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool); + Add(Instruction.ShuffleDown, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool); + Add(Instruction.ShuffleUp, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool); + Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool); + Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.StoreAttribute, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.FP32); + Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32); + Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32); + Add(Instruction.StoreShared16, AggregateType.Void, AggregateType.S32, AggregateType.U32); + Add(Instruction.StoreShared8, AggregateType.Void, AggregateType.S32, AggregateType.U32); + Add(Instruction.StoreStorage, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.StoreStorage16, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.StoreStorage8, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); + Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32); + Add(Instruction.TextureSample, AggregateType.FP32); + Add(Instruction.TextureSize, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar); + Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64); + Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32); + Add(Instruction.VectorExtract, AggregateType.Scalar, AggregateType.Vector4, AggregateType.S32); + Add(Instruction.VoteAll, AggregateType.Bool, AggregateType.Bool); + Add(Instruction.VoteAllEqual, AggregateType.Bool, AggregateType.Bool); + Add(Instruction.VoteAny, AggregateType.Bool, AggregateType.Bool); } - private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes) + private static void Add(Instruction inst, AggregateType destType, params AggregateType[] srcTypes) { _infoTbl[(int)inst] = new InstInfo(destType, srcTypes); } - public static VariableType GetDestVarType(Instruction inst) + public static AggregateType GetDestVarType(Instruction inst) { return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].DestType, inst); } - public static VariableType GetSrcVarType(Instruction inst, int index) + public static AggregateType GetSrcVarType(Instruction inst, int index) { // TODO: Return correct type depending on source index, // that can improve the decompiler output. - if (inst == Instruction.ImageLoad || - inst == Instruction.ImageStore || + if (inst == Instruction.ImageLoad || + inst == Instruction.ImageStore || inst == Instruction.ImageAtomic || - inst == Instruction.Lod || + inst == Instruction.Lod || inst == Instruction.TextureSample) { - return VariableType.F32; + return AggregateType.FP32; } else if (inst == Instruction.Call) { - return VariableType.S32; + return AggregateType.S32; } return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].SrcTypes[index], inst); } - private static VariableType GetFinalVarType(VariableType type, Instruction inst) + private static AggregateType GetFinalVarType(AggregateType type, Instruction inst) { - if (type == VariableType.Scalar) + if (type == AggregateType.Scalar) { if ((inst & Instruction.FP32) != 0) { - return VariableType.F32; + return AggregateType.FP32; } else if ((inst & Instruction.FP64) != 0) { - return VariableType.F64; + return AggregateType.FP64; } else { - return VariableType.S32; + return AggregateType.S32; } } - else if (type == VariableType.Int) - { - return VariableType.S32; - } - else if (type == VariableType.None) + else if (type == AggregateType.Void) { throw new ArgumentException($"Invalid operand for instruction \"{inst}\"."); } diff --git a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs index 34428815e..730468a43 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs @@ -1,11 +1,12 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; using System; namespace Ryujinx.Graphics.Shader.StructuredIr { static class OperandInfo { - public static VariableType GetVarType(AstOperand operand) + public static AggregateType GetVarType(AstOperand operand) { if (operand.Type == OperandType.LocalVariable) { @@ -17,16 +18,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } - public static VariableType GetVarType(OperandType type) + public static AggregateType GetVarType(OperandType type) { return type switch { - OperandType.Argument => VariableType.S32, - OperandType.Attribute => VariableType.F32, - OperandType.AttributePerPatch => VariableType.F32, - OperandType.Constant => VariableType.S32, - OperandType.ConstantBuffer => VariableType.F32, - OperandType.Undefined => VariableType.S32, + OperandType.Argument => AggregateType.S32, + OperandType.Attribute => AggregateType.FP32, + OperandType.AttributePerPatch => AggregateType.FP32, + OperandType.Constant => AggregateType.S32, + OperandType.ConstantBuffer => AggregateType.FP32, + OperandType.Undefined => AggregateType.S32, _ => throw new ArgumentException($"Invalid operand type \"{type}\".") }; } diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs index 3723f2597..61c4fed73 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Shader.Translation; using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.StructuredIr @@ -8,19 +9,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public string Name { get; } - public VariableType ReturnType { get; } + public AggregateType ReturnType { get; } - public VariableType[] InArguments { get; } - public VariableType[] OutArguments { get; } + public AggregateType[] InArguments { get; } + public AggregateType[] OutArguments { get; } public HashSet Locals { get; } public StructuredFunction( AstBlock mainBlock, string name, - VariableType returnType, - VariableType[] inArguments, - VariableType[] outArguments) + AggregateType returnType, + AggregateType[] inArguments, + AggregateType[] outArguments) { MainBlock = mainBlock; Name = name; @@ -31,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Locals = new HashSet(); } - public VariableType GetArgumentType(int index) + public AggregateType GetArgumentType(int index) { return index >= InArguments.Length ? OutArguments[index - InArguments.Length] diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 7678a4bf6..ec989cca5 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; +using System.Numerics; namespace Ryujinx.Graphics.Shader.StructuredIr { @@ -17,19 +18,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr BasicBlock[] blocks = function.Blocks; - VariableType returnType = function.ReturnsValue ? VariableType.S32 : VariableType.None; + AggregateType returnType = function.ReturnsValue ? AggregateType.S32 : AggregateType.Void; - VariableType[] inArguments = new VariableType[function.InArgumentsCount]; - VariableType[] outArguments = new VariableType[function.OutArgumentsCount]; + AggregateType[] inArguments = new AggregateType[function.InArgumentsCount]; + AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount]; for (int i = 0; i < inArguments.Length; i++) { - inArguments[i] = VariableType.S32; + inArguments[i] = AggregateType.S32; } for (int i = 0; i < outArguments.Length; i++) { - outArguments[i] = VariableType.S32; + outArguments[i] = AggregateType.S32; } context.EnterFunction(blocks.Length, function.Name, returnType, inArguments, outArguments); @@ -109,8 +110,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } + bool vectorDest = IsVectorDestInst(inst); + int sourcesCount = operation.SourcesCount; - int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0; + int outDestsCount = operation.DestsCount != 0 && !vectorDest ? operation.DestsCount - 1 : 0; IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount]; @@ -141,7 +144,52 @@ namespace Ryujinx.Graphics.Shader.StructuredIr sources); } - if (operation.Dest != null) + int componentsCount = BitOperations.PopCount((uint)operation.Index); + + if (vectorDest && componentsCount > 1) + { + AggregateType destType = InstructionInfo.GetDestVarType(inst); + + IAstNode source; + + if (operation is TextureOperation texOp) + { + if (texOp.Inst == Instruction.ImageLoad) + { + destType = texOp.Format.GetComponentType(); + } + + source = GetAstTextureOperation(texOp); + } + else + { + source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount); + } + + AggregateType destElemType = destType; + + switch (componentsCount) + { + case 2: destType |= AggregateType.Vector2; break; + case 3: destType |= AggregateType.Vector3; break; + case 4: destType |= AggregateType.Vector4; break; + } + + AstOperand destVec = context.NewTemp(destType); + + context.AddNode(new AstAssignment(destVec, source)); + + for (int i = 0; i < operation.DestsCount; i++) + { + AstOperand dest = context.GetOperandDef(operation.GetDest(i)); + AstOperand index = new AstOperand(OperandType.Constant, i); + + dest.VarType = destElemType; + + context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, new[] { destVec, index }, 2))); + } + } + else if (operation.Dest != null) { AstOperand dest = context.GetOperandDef(operation.Dest); @@ -149,7 +197,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr // logical operations, rather than forcing a cast to int and doing // a bitwise operation with the value, as it is likely to be used as // a bool in the end. - if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, VariableType.Bool)) + if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, AggregateType.Bool)) { inst = GetLogicalFromBitwiseInst(inst); } @@ -159,9 +207,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (isCondSel || isCopy) { - VariableType type = GetVarTypeFromUses(operation.Dest); + AggregateType type = GetVarTypeFromUses(operation.Dest); - if (isCondSel && type == VariableType.F32) + if (isCondSel && type == AggregateType.FP32) { inst |= Instruction.FP32; } @@ -259,7 +307,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } - private static VariableType GetVarTypeFromUses(Operand dest) + private static AggregateType GetVarTypeFromUses(Operand dest) { HashSet visited = new HashSet(); @@ -315,10 +363,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } - return VariableType.S32; + return AggregateType.S32; } - private static bool AreAllSourceTypesEqual(IAstNode[] sources, VariableType type) + private static bool AreAllSourceTypesEqual(IAstNode[] sources, AggregateType type) { foreach (IAstNode node in sources) { @@ -336,6 +384,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return true; } + private static bool IsVectorDestInst(Instruction inst) + { + return inst switch + { + Instruction.ImageLoad or + Instruction.TextureSample => true, + _ => false + }; + } + private static bool IsBranchInst(Instruction inst) { return inst switch diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index e9f8467d5..ce57a5788 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -80,9 +80,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public void EnterFunction( int blocksCount, string name, - VariableType returnType, - VariableType[] inArguments, - VariableType[] outArguments) + AggregateType returnType, + AggregateType[] inArguments, + AggregateType[] outArguments) { _loopTails = new HashSet(); @@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return gotoTempAsg; } - AstOperand gotoTemp = NewTemp(VariableType.Bool); + AstOperand gotoTemp = NewTemp(AggregateType.Bool); gotoTempAsg = Assign(gotoTemp, Const(IrConsts.False)); @@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return _gotos.ToArray(); } - private AstOperand NewTemp(VariableType type) + public AstOperand NewTemp(AggregateType type) { AstOperand newTemp = Local(type); diff --git a/Ryujinx.Graphics.Shader/StructuredIr/VariableType.cs b/Ryujinx.Graphics.Shader/StructuredIr/VariableType.cs deleted file mode 100644 index 0afafb2b7..000000000 --- a/Ryujinx.Graphics.Shader/StructuredIr/VariableType.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Shader.StructuredIr -{ - enum VariableType - { - None, - Bool, - Scalar, - Int, - F32, - F64, - S32, - U32 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/TextureFormat.cs b/Ryujinx.Graphics.Shader/TextureFormat.cs index ecdd15cb2..d4c8b96be 100644 --- a/Ryujinx.Graphics.Shader/TextureFormat.cs +++ b/Ryujinx.Graphics.Shader/TextureFormat.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; namespace Ryujinx.Graphics.Shader { @@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Shader }; } - public static VariableType GetComponentType(this TextureFormat format) + public static AggregateType GetComponentType(this TextureFormat format) { switch (format) { @@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Shader case TextureFormat.R16G16B16A16Uint: case TextureFormat.R32G32B32A32Uint: case TextureFormat.R10G10B10A2Uint: - return VariableType.U32; + return AggregateType.U32; case TextureFormat.R8Sint: case TextureFormat.R16Sint: case TextureFormat.R32Sint: @@ -119,10 +119,10 @@ namespace Ryujinx.Graphics.Shader case TextureFormat.R8G8B8A8Sint: case TextureFormat.R16G16B16A16Sint: case TextureFormat.R32G32B32A32Sint: - return VariableType.S32; + return AggregateType.S32; } - return VariableType.F32; + return AggregateType.FP32; } } } diff --git a/Ryujinx.Graphics.Shader/Translation/AggregateType.cs b/Ryujinx.Graphics.Shader/Translation/AggregateType.cs index dcd1e0bd4..24993e00d 100644 --- a/Ryujinx.Graphics.Shader/Translation/AggregateType.cs +++ b/Ryujinx.Graphics.Shader/Translation/AggregateType.cs @@ -12,7 +12,14 @@ ElementTypeMask = 0xff, - Vector = 1 << 8, - Array = 1 << 9 + ElementCountShift = 8, + ElementCountMask = 3 << ElementCountShift, + + Scalar = 0 << ElementCountShift, + Vector2 = 1 << ElementCountShift, + Vector3 = 2 << ElementCountShift, + Vector4 = 3 << ElementCountShift, + + Array = 1 << 10 } } diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index 1647f6562..fa1ae17f6 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -9,22 +9,22 @@ namespace Ryujinx.Graphics.Shader.Translation { AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) }, { AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) }, { AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) }, - { AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector | AggregateType.FP32) }, - { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) }, + { AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) }, + { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) }, + { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) }, + { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) }, + { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) }, + { AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) }, + { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) }, + { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) }, + { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) }, { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, { AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) }, @@ -37,21 +37,21 @@ namespace Ryujinx.Graphics.Shader.Translation // Special. { AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) }, { AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) }, - { AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector | AggregateType.U32) }, + { AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) }, + { AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) }, + { AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) }, + { AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) }, + { AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) }, + { AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) }, { AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) }, { AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) }, { AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) }, { AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) }, - { AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector | AggregateType.U32) }, - { AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector | AggregateType.U32) }, + { AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, + { AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, + { AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, + { AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, + { AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, }; private static readonly Dictionary _builtInAttributesPerPatch = new Dictionary() @@ -124,11 +124,11 @@ namespace Ryujinx.Graphics.Shader.Translation elemType = AggregateType.FP32; } - return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector | elemType, false); + return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false); } else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) { - return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector | AggregateType.FP32, false); + return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | AggregateType.FP32, false); } else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY) { @@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd) { int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf; - return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector | AggregateType.FP32, false); + return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false); } else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info)) { diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 7961ada89..ad55c0109 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -109,10 +109,10 @@ namespace Ryujinx.Graphics.Shader.Translation TextureFlags flags, int handle, int compIndex, - Operand dest, + Operand[] dests, params Operand[] sources) { - return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dest, sources); + return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dests, sources); } public TextureOperation CreateTextureOperation( @@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Shader.Translation TextureFlags flags, int handle, int compIndex, - Operand dest, + Operand[] dests, params Operand[] sources) { if (!flags.HasFlag(TextureFlags.Bindless)) @@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Shader.Translation Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle); } - return new TextureOperation(inst, type, format, flags, handle, compIndex, dest, sources); + return new TextureOperation(inst, type, format, flags, handle, compIndex, dests, sources); } public void FlagAttributeRead(int attribute) diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 0c3c4a57d..3ec4e49a7 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -385,15 +385,6 @@ namespace Ryujinx.Graphics.Shader.Translation int componentIndex = texOp.Index; - Operand Int(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); - - return res; - } - Operand Float(Operand value) { Operand res = Local(); @@ -436,7 +427,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.CbufSlot, texOp.Handle, index, - coordSize, + new[] { coordSize }, texSizeSources)); config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); @@ -451,80 +442,53 @@ namespace Ryujinx.Graphics.Shader.Translation } } + Operand[] dests = new Operand[texOp.DestsCount]; + + for (int i = 0; i < texOp.DestsCount; i++) + { + dests[i] = texOp.GetDest(i); + } + + Operand bindlessHandle = isBindless || isIndexed ? sources[0] : null; + + LinkedListNode oldNode = node; + // Technically, non-constant texture offsets are not allowed (according to the spec), // however some GPUs does support that. // For GPUs where it is not supported, we can replace the instruction with the following: // For texture*Offset, we replace it by texture*, and add the offset to the P coords. // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. - // For textureGatherOffset, we take advantage of the fact that the operation is already broken down - // to read the 4 pixels separately, and just replace it with 4 textureGather with a different offset - // for each pixel. - if (hasInvalidOffset) + // For textureGatherOffset, we split the operation into up to 4 operations, one for each component + // that is accessed, where each textureGather operation has a different offset for each pixel. + if (hasInvalidOffset && isGather && !isShadow) { - if (intCoords) + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + Operand[] newSources = new Operand[sources.Length]; + + sources.CopyTo(newSources, 0); + + Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + + int destIndex = 0; + + for (int compIndex = 0; compIndex < 4; compIndex++) { - for (int index = 0; index < coordsCount; index++) + if (((texOp.Index >> compIndex) & 1) == 0) { - Operand source = sources[coordsIndex + index]; - - Operand coordPlusOffset = Local(); - - node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); - - sources[coordsIndex + index] = coordPlusOffset; + continue; } - } - else - { - config.SetUsedFeature(FeatureFlags.IntegerSampling); - - Operand lod = Local(); - - node.List.AddBefore(node, new TextureOperation( - Instruction.Lod, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.CbufSlot, - texOp.Handle, - 0, - lod, - lodSources)); for (int index = 0; index < coordsCount; index++) { - Operand coordSize = Local(); - - Operand[] texSizeSources; - - if (isBindless || isIndexed) - { - texSizeSources = new Operand[] { sources[0], Int(lod) }; - } - else - { - texSizeSources = new Operand[] { Int(lod) }; - } - - node.List.AddBefore(node, new TextureOperation( - Instruction.TextureSize, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.CbufSlot, - texOp.Handle, - index, - coordSize, - texSizeSources)); - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); Operand offset = Local(); - Operand intOffset = offsets[index + (hasOffsets ? texOp.Index * coordsCount : 0)]; + Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(coordSize))); + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); Operand source = sources[coordsIndex + index]; @@ -532,45 +496,152 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); - sources[coordsIndex + index] = coordPlusOffset; + newSources[coordsIndex + index] = coordPlusOffset; + } + + TextureOperation newTexOp = new TextureOperation( + Instruction.TextureSample, + texOp.Type, + texOp.Format, + texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), + texOp.CbufSlot, + texOp.Handle, + 1, + new[] { dests[destIndex++] }, + newSources); + + node = node.List.AddBefore(node, newTexOp); + } + } + else + { + if (hasInvalidOffset) + { + if (intCoords) + { + for (int index = 0; index < coordsCount; index++) + { + Operand source = sources[coordsIndex + index]; + + Operand coordPlusOffset = Local(); + + node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); + + sources[coordsIndex + index] = coordPlusOffset; + } + } + else + { + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + + for (int index = 0; index < coordsCount; index++) + { + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + + Operand offset = Local(); + + Operand intOffset = offsets[index]; + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); + + Operand source = sources[coordsIndex + index]; + + Operand coordPlusOffset = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); + + sources[coordsIndex + index] = coordPlusOffset; + } } } - if (isGather && !isShadow) - { - Operand gatherComponent = sources[dstIndex - 1]; + TextureOperation newTexOp = new TextureOperation( + Instruction.TextureSample, + texOp.Type, + texOp.Format, + texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), + texOp.CbufSlot, + texOp.Handle, + componentIndex, + dests, + sources); - Debug.Assert(gatherComponent.Type == OperandType.Constant); - - componentIndex = gatherComponent.Value; - } + node = node.List.AddBefore(node, newTexOp); } - TextureOperation newTexOp = new TextureOperation( - Instruction.TextureSample, - texOp.Type, - texOp.Format, - texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), - texOp.CbufSlot, - texOp.Handle, - componentIndex, - texOp.Dest, - sources); + node.List.Remove(oldNode); for (int index = 0; index < texOp.SourcesCount; index++) { texOp.SetSource(index, null); } - LinkedListNode oldNode = node; - - node = node.List.AddBefore(node, newTexOp); - - node.List.Remove(oldNode); - return node; } + private static Operand[] InsertTextureSize( + LinkedListNode node, + TextureOperation texOp, + Operand[] lodSources, + Operand bindlessHandle, + int coordsCount) + { + Operand Int(Operand value) + { + Operand res = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); + + return res; + } + + Operand[] texSizes = new Operand[coordsCount]; + + Operand lod = Local(); + + node.List.AddBefore(node, new TextureOperation( + Instruction.Lod, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + 0, + new[] { lod }, + lodSources)); + + for (int index = 0; index < coordsCount; index++) + { + texSizes[index] = Local(); + + Operand[] texSizeSources; + + if (bindlessHandle != null) + { + texSizeSources = new Operand[] { bindlessHandle, Int(lod) }; + } + else + { + texSizeSources = new Operand[] { Int(lod) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + index, + new[] { texSizes[index] }, + texSizeSources)); + } + + return texSizes; + } + private static LinkedListNode InsertSnormNormalization(LinkedListNode node, ShaderConfig config) { TextureOperation texOp = (TextureOperation)node.Value; @@ -604,27 +675,32 @@ namespace Ryujinx.Graphics.Shader.Translation // Do normalization. We assume SINT formats are being used // as replacement for SNORM (which is not supported). - INode[] uses = texOp.Dest.UseOps.ToArray(); - - Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), texOp.Dest); - Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive)); - - node = node.List.AddAfter(node, convOp); - node = node.List.AddAfter(node, normOp); - - foreach (INode useOp in uses) + for (int i = 0; i < texOp.DestsCount; i++) { - if (useOp is not Operation op) - { - continue; - } + Operand dest = texOp.GetDest(i); - // Replace all uses of the texture pixel value with the normalized value. - for (int index = 0; index < op.SourcesCount; index++) + INode[] uses = dest.UseOps.ToArray(); + + Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), dest); + Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive)); + + node = node.List.AddAfter(node, convOp); + node = node.List.AddAfter(node, normOp); + + foreach (INode useOp in uses) { - if (op.GetSource(index) == texOp.Dest) + if (useOp is not Operation op) { - op.SetSource(index, normOp.Dest); + continue; + } + + // Replace all uses of the texture pixel value with the normalized value. + for (int index = 0; index < op.SourcesCount; index++) + { + if (op.GetSource(index) == dest) + { + op.SetSource(index, normOp.Dest); + } } } } From 2b23463daa01226c5569d8e61d1d0959570354cf Mon Sep 17 00:00:00 2001 From: Andrew Glaze Date: Thu, 29 Dec 2022 10:52:30 -0500 Subject: [PATCH 230/737] Filter hidden game files from the Game List (#4051) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Filter “._” files from the game list * Filter all hidden files from the game list * Fix style Co-authored-by: gdkchan * merge OR expression into a pattern * migrate from GetFiles/Directories to Enumerate * Remove GetFilesInDirectory() * Update Ryujinx.Ui.Common/App/ApplicationLibrary.cs Co-authored-by: Ac_K * add error handeling * code cleanup Co-authored-by: gdkchan Co-authored-by: Ac_K --- Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 81 +++++---------------- 1 file changed, 18 insertions(+), 63 deletions(-) diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 1af7dc064..b1a8026e5 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -68,53 +68,6 @@ namespace Ryujinx.Ui.App.Common _cancellationToken?.Cancel(); } - public IEnumerable GetFilesInDirectory(string directory) - { - Stack stack = new Stack(); - - stack.Push(directory); - - while (stack.Count > 0) - { - string dir = stack.Pop(); - string[] content = Array.Empty(); - - try - { - content = Directory.GetFiles(dir, "*"); - } - catch (UnauthorizedAccessException) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{dir}\""); - } - - if (content.Length > 0) - { - foreach (string file in content) - { - yield return file; - } - } - - try - { - content = Directory.GetDirectories(dir); - } - catch (UnauthorizedAccessException) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{dir}\""); - } - - if (content.Length > 0) - { - foreach (string subdir in content) - { - stack.Push(subdir); - } - } - } - } - public void ReadControlData(IFileSystem controlFs, Span outProperty) { using var controlFile = new UniqueRef(); @@ -151,26 +104,28 @@ namespace Ryujinx.Ui.App.Common continue; } - foreach (string app in GetFilesInDirectory(appDir)) + try { - if (_cancellationToken.Token.IsCancellationRequested) + foreach (string app in Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories)) { - return; - } - - string extension = Path.GetExtension(app).ToLower(); - - if ((extension == ".nsp") || - (extension == ".pfs0") || - (extension == ".xci") || - (extension == ".nca") || - (extension == ".nro") || - (extension == ".nso")) - { - applications.Add(app); - numApplicationsFound++; + if (_cancellationToken.Token.IsCancellationRequested) + { + return; + } + + string extension = Path.GetExtension(app).ToLower(); + + if (!File.GetAttributes(app).HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso") + { + applications.Add(app); + numApplicationsFound++; + } } } + catch (UnauthorizedAccessException) + { + Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{appDir}\""); + } } // Loops through applications list, creating a struct and then firing an event containing the struct for each application From b1d4b174a654432b84e4d83077ddb9bc43c7b089 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Sun, 1 Jan 2023 14:46:02 +0000 Subject: [PATCH 231/737] fix typo in left joycon sl binding (#4195) --- Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml b/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml index f6bb1aa42..8a4d22ffa 100644 --- a/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml @@ -639,14 +639,14 @@ Width="20" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRightSL}" + Text="{locale:Locale ControllerSettingsLeftSL}" TextAlignment="Center" /> From b6614c6ad5d7d19594b80f4917df27bf476e8f03 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Sun, 1 Jan 2023 17:35:29 +0100 Subject: [PATCH 232/737] chore: Update tests dependencies (#3978) * chore: Update tests dependencies * Apply TSR Berry suggestion to add a GC.SuppressFinalize in MemoryBlock.cs * Ensure we wait for the test thread to be dead on PartialUnmap * Use platform attribute for os specific tests * Make P/Invoke methods private * Downgrade NUnit3TestAdapter to 4.1.0 * test: Disable warning about platform compat for ThreadLocalMap() Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- Directory.Packages.props | 2 +- .../Memory/PartialUnmaps/PartialUnmapState.cs | 6 +-- Ryujinx.Memory.Tests/Tests.cs | 24 +++------ Ryujinx.Memory/MemoryBlock.cs | 7 ++- Ryujinx.Tests/Memory/PartialUnmaps.cs | 51 +++++++------------ 5 files changed, 34 insertions(+), 56 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5f434bf17..c02c1ae56 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,7 +25,7 @@ - + diff --git a/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs b/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs index 3463d06cf..5b0bc07ec 100644 --- a/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs +++ b/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll")] - public static partial int GetCurrentThreadId(); + private static partial int GetCurrentThreadId(); [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] @@ -36,7 +36,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs (UnmanagedType.Bool)] - public static partial bool CloseHandle(IntPtr hObject); + private static partial bool CloseHandle(IntPtr hObject); [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] @@ -160,4 +160,4 @@ namespace Ryujinx.Common.Memory.PartialUnmaps } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Memory.Tests/Tests.cs b/Ryujinx.Memory.Tests/Tests.cs index c5a7842ec..2717b76ad 100644 --- a/Ryujinx.Memory.Tests/Tests.cs +++ b/Ryujinx.Memory.Tests/Tests.cs @@ -39,14 +39,10 @@ namespace Ryujinx.Memory.Tests } [Test] + // Memory aliasing tests fail on CI at the moment. + [Platform(Exclude = "MacOsX")] public void Test_Alias() { - if (OperatingSystem.IsMacOS()) - { - // Memory aliasing tests fail on CI at the moment. - return; - } - using MemoryBlock backing = new MemoryBlock(0x10000, MemoryAllocationFlags.Mirrorable); using MemoryBlock toAlias = new MemoryBlock(0x10000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); @@ -58,14 +54,10 @@ namespace Ryujinx.Memory.Tests } [Test] + // Memory aliasing tests fail on CI at the moment. + [Platform(Exclude = "MacOsX")] public void Test_AliasRandom() { - if (OperatingSystem.IsMacOS()) - { - // Memory aliasing tests fail on CI at the moment. - return; - } - using MemoryBlock backing = new MemoryBlock(0x80000, MemoryAllocationFlags.Mirrorable); using MemoryBlock toAlias = new MemoryBlock(0x80000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); @@ -94,14 +86,10 @@ namespace Ryujinx.Memory.Tests } [Test] + // Memory aliasing tests fail on CI at the moment. + [Platform(Exclude = "MacOsX")] public void Test_AliasMapLeak() { - if (OperatingSystem.IsMacOS()) - { - // Memory aliasing tests fail on CI at the moment. - return; - } - ulong pageSize = 4096; ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that. diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 41e6224bb..6b9d852de 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -379,7 +379,12 @@ namespace Ryujinx.Memory /// /// It's an error to use the memory block after disposal. /// - public void Dispose() => FreeMemory(); + public void Dispose() + { + FreeMemory(); + + GC.SuppressFinalize(this); + } ~MemoryBlock() => FreeMemory(); diff --git a/Ryujinx.Tests/Memory/PartialUnmaps.cs b/Ryujinx.Tests/Memory/PartialUnmaps.cs index 1088b52c4..b805969d3 100644 --- a/Ryujinx.Tests/Memory/PartialUnmaps.cs +++ b/Ryujinx.Tests/Memory/PartialUnmaps.cs @@ -9,6 +9,7 @@ using Ryujinx.Memory.Tests; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; @@ -57,14 +58,10 @@ namespace Ryujinx.Tests.Memory } [Test] + // Memory aliasing tests fail on CI at the moment. + [Platform(Exclude = "MacOsX")] public void PartialUnmap([Values] bool readOnly) { - if (OperatingSystem.IsMacOS()) - { - // Memory aliasing tests fail on CI at the moment. - return; - } - // Set up an address space to test partial unmapping. // Should register the signal handler to deal with this on Windows. ulong vaSize = 0x100000; @@ -78,11 +75,13 @@ namespace Ryujinx.Tests.Memory ref var state = ref PartialUnmapState.GetRef(); + Thread testThread = null; + bool shouldAccess = true; + try { // Globally reset the struct for handling partial unmap races. PartialUnmapState.Reset(); - bool shouldAccess = true; bool error = false; // Create a large mapping. @@ -93,8 +92,6 @@ namespace Ryujinx.Tests.Memory memory.Reprotect(0, vaSize, MemoryPermission.Read); } - Thread testThread; - if (readOnly) { // Write a value to the physical memory, then try to read it repeately from virtual. @@ -193,6 +190,10 @@ namespace Ryujinx.Tests.Memory } finally { + // In case something failed, we want to ensure the test thread is dead before disposing of the memory. + shouldAccess = false; + testThread?.Join(); + exceptionHandler.Dispose(); unusedMainMemory.Dispose(); memory.Dispose(); @@ -201,13 +202,10 @@ namespace Ryujinx.Tests.Memory } [Test] + // Memory aliasing tests fail on CI at the moment. + [Platform(Exclude = "MacOsX")] public unsafe void PartialUnmapNative() { - if (OperatingSystem.IsMacOS()) - { - // Memory aliasing tests fail on CI at the moment. - return; - } // Set up an address space to test partial unmapping. // Should register the signal handler to deal with this on Windows. @@ -284,26 +282,17 @@ namespace Ryujinx.Tests.Memory } [Test] + // Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming. + [Platform("Win")] + [SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")] public void ThreadLocalMap() { - if (!OperatingSystem.IsWindows()) - { - // Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming. - return; - } - PartialUnmapState.Reset(); ref var state = ref PartialUnmapState.GetRef(); bool running = true; var testThread = new Thread(() => { - if (!OperatingSystem.IsWindows()) - { - // Need this here to avoid a warning. - return; - } - PartialUnmapState.GetRef().RetryFromAccessViolation(); while (running) { @@ -330,14 +319,10 @@ namespace Ryujinx.Tests.Memory } [Test] + // Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming. + [Platform("Win")] public unsafe void ThreadLocalMapNative() { - if (!OperatingSystem.IsWindows()) - { - // Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming. - return; - } - EnsureTranslator(); PartialUnmapState.Reset(); @@ -481,4 +466,4 @@ namespace Ryujinx.Tests.Memory Assert.False(error); } } -} +} \ No newline at end of file From 09c9686498c7c987c94f33c79732c7592045e035 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Mon, 2 Jan 2023 15:48:46 +0100 Subject: [PATCH 233/737] misc: Use official names for NVDEC registers (#4192) * misc: Uses official names for NVDEC registers * Address gdkchan's comment * Address comments --- Ryujinx.Graphics.Host1x/ThiDevice.cs | 2 +- .../{CodecId.cs => ApplicationId.cs} | 5 +- .../FrameDecodedEventArgs.cs | 16 ---- Ryujinx.Graphics.Nvdec/H264Decoder.cs | 8 +- Ryujinx.Graphics.Nvdec/NvdecDevice.cs | 14 ++-- Ryujinx.Graphics.Nvdec/NvdecRegisters.cs | 79 ++++++++++++------- Ryujinx.Graphics.Nvdec/Vp8Decoder.cs | 8 +- Ryujinx.Graphics.Nvdec/Vp9Decoder.cs | 24 +++--- 8 files changed, 80 insertions(+), 76 deletions(-) rename Ryujinx.Graphics.Nvdec/{CodecId.cs => ApplicationId.cs} (68%) delete mode 100644 Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs diff --git a/Ryujinx.Graphics.Host1x/ThiDevice.cs b/Ryujinx.Graphics.Host1x/ThiDevice.cs index 114ee26e5..259c88366 100644 --- a/Ryujinx.Graphics.Host1x/ThiDevice.cs +++ b/Ryujinx.Graphics.Host1x/ThiDevice.cs @@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Host1x private void Method1(int data) { - _commandQueue.Add(new MethodCallAction(_currentContextId, (int)_state.State.Method0 * 4, data)); + _commandQueue.Add(new MethodCallAction(_currentContextId, (int)_state.State.Method0 * sizeof(uint), data)); } private void Process(CommandAction cmdAction) diff --git a/Ryujinx.Graphics.Nvdec/CodecId.cs b/Ryujinx.Graphics.Nvdec/ApplicationId.cs similarity index 68% rename from Ryujinx.Graphics.Nvdec/CodecId.cs rename to Ryujinx.Graphics.Nvdec/ApplicationId.cs index 9aaa3d023..ada12f8d3 100644 --- a/Ryujinx.Graphics.Nvdec/CodecId.cs +++ b/Ryujinx.Graphics.Nvdec/ApplicationId.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Nvdec { - public enum CodecId + public enum ApplicationId { Mpeg = 1, Vc1 = 2, @@ -8,6 +8,7 @@ Mpeg4 = 4, Vp8 = 5, Hevc = 7, - Vp9 = 9 + Vp9 = 9, + HevcParser = 12, } } diff --git a/Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs b/Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs deleted file mode 100644 index 4ee29d9d0..000000000 --- a/Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Ryujinx.Graphics.Nvdec -{ - public readonly struct FrameDecodedEventArgs - { - public CodecId CodecId { get; } - public uint LumaOffset { get; } - public uint ChromaOffset { get; } - - internal FrameDecodedEventArgs(CodecId codecId, uint lumaOffset, uint chromaOffset) - { - CodecId = codecId; - LumaOffset = lumaOffset; - ChromaOffset = chromaOffset; - } - } -} diff --git a/Ryujinx.Graphics.Nvdec/H264Decoder.cs b/Ryujinx.Graphics.Nvdec/H264Decoder.cs index 6efeb899e..ecc7dbc79 100644 --- a/Ryujinx.Graphics.Nvdec/H264Decoder.cs +++ b/Ryujinx.Graphics.Nvdec/H264Decoder.cs @@ -12,18 +12,18 @@ namespace Ryujinx.Graphics.Nvdec public static void Decode(NvdecDecoderContext context, ResourceManager rm, ref NvdecRegisters state) { - PictureInfo pictureInfo = rm.Gmm.DeviceRead(state.SetPictureInfoOffset); + PictureInfo pictureInfo = rm.Gmm.DeviceRead(state.SetDrvPicSetupOffset); H264PictureInfo info = pictureInfo.Convert(); - ReadOnlySpan bitstream = rm.Gmm.DeviceGetSpan(state.SetBitstreamOffset, (int)pictureInfo.BitstreamSize); + ReadOnlySpan bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize); int width = (int)pictureInfo.PicWidthInMbs * MbSizeInPixels; int height = (int)pictureInfo.PicHeightInMbs * MbSizeInPixels; int surfaceIndex = (int)pictureInfo.OutputSurfaceIndex; - uint lumaOffset = state.SetSurfaceLumaOffset[surfaceIndex]; - uint chromaOffset = state.SetSurfaceChromaOffset[surfaceIndex]; + uint lumaOffset = state.SetPictureLumaOffset[surfaceIndex]; + uint chromaOffset = state.SetPictureChromaOffset[surfaceIndex]; Decoder decoder = context.GetH264Decoder(); diff --git a/Ryujinx.Graphics.Nvdec/NvdecDevice.cs b/Ryujinx.Graphics.Nvdec/NvdecDevice.cs index 18c2fc130..ef8185f4d 100644 --- a/Ryujinx.Graphics.Nvdec/NvdecDevice.cs +++ b/Ryujinx.Graphics.Nvdec/NvdecDevice.cs @@ -58,24 +58,24 @@ namespace Ryujinx.Graphics.Nvdec private void Execute(int data) { - Decode((CodecId)_state.State.SetCodecID); + Decode((ApplicationId)_state.State.SetApplicationId); } - private void Decode(CodecId codecId) + private void Decode(ApplicationId applicationId) { - switch (codecId) + switch (applicationId) { - case CodecId.H264: + case ApplicationId.H264: H264Decoder.Decode(_currentContext, _rm, ref _state.State); break; - case CodecId.Vp8: + case ApplicationId.Vp8: Vp8Decoder.Decode(_currentContext, _rm, ref _state.State); break; - case CodecId.Vp9: + case ApplicationId.Vp9: Vp9Decoder.Decode(_rm, ref _state.State); break; default: - Logger.Error?.Print(LogClass.Nvdec, $"Unsupported codec \"{codecId}\"."); + Logger.Error?.Print(LogClass.Nvdec, $"Unsupported codec \"{applicationId}\"."); break; } } diff --git a/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs b/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs index 84b2c4e07..cf8677838 100644 --- a/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs +++ b/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs @@ -2,43 +2,62 @@ namespace Ryujinx.Graphics.Nvdec { - // Note: Most of those names are not official. struct NvdecRegisters { #pragma warning disable CS0649 public Array64 Reserved0; - public Array64 Reserved100; - public uint SetCodecID; - public Array63 Reserved204; + public uint Nop; + public Array63 Reserved104; + public uint SetApplicationId; + public uint SetWatchdogTimer; + public Array14 Reserved208; + public uint SemaphoreA; + public uint SemaphoreB; + public uint SemaphoreC; + public uint CtxSaveArea; + public Array44 Reserved254; public uint Execute; - public Array63 Reserved304; - public uint SetPlatformID; - public uint SetPictureInfoOffset; - public uint SetBitstreamOffset; - public uint SetFrameNumber; - public uint SetH264SliceDataOffsetsOffset; // Also used by VC1 - public uint SetH264MvDumpOffset; // Also used by VC1 - public uint Unknown418; // Used by VC1 - public uint Unknown41C; - public uint Unknown420; // Used by VC1 - public uint SetFrameStatsOffset; - public uint SetH264LastSurfaceLumaOffset; - public uint SetH264LastSurfaceChromaOffset; - public Array17 SetSurfaceLumaOffset; - public Array17 SetSurfaceChromaOffset; - public uint Unknown4B8; - public uint Unknown4BC; + public uint SemaphoreD; + public Array62 Reserved308; + public uint SetControlParams; + public uint SetDrvPicSetupOffset; + public uint SetInBufBaseOffset; + public uint SetPictureIndex; + public uint SetSliceOffsetsBufOffset; // Also used by VC1 + public uint SetColocDataOffset; // Also used by VC1 + public uint SetHistoryOffset; // Used by VC1 + public uint SetDisplayBufSize; + public uint SetHistogramOffset; // Used by VC1 + public uint SetNvDecStatusOffset; + public uint SetDisplayBufLumaOffset; + public uint SetDisplayBufChromaOffset; + public Array17 SetPictureLumaOffset; + public Array17 SetPictureChromaOffset; + public uint SetPicScratchBufOffset; + public uint SetExternalMvBufferOffset; public uint SetCryptoData0Offset; public uint SetCryptoData1Offset; - public Array62 Unknown4C8; - public uint SetVp9EntropyProbsOffset; - public uint SetVp9BackwardUpdatesOffset; - public uint SetVp9LastFrameSegMapOffset; - public uint SetVp9CurrFrameSegMapOffset; - public uint Unknown5D0; - public uint SetVp9LastFrameMvsOffset; - public uint SetVp9CurrFrameMvsOffset; - public uint Unknown5DC; + public Array14 Unknown4C8; + public uint H264SetMbHistBufOffset; + public Array15 Unknown504; + public uint Vp8SetProbDataOffset; + public uint Vp8SetHeaderPartitionBufBaseOffset; + public Array14 Unknown548; + public uint HevcSetScalingListOffset; + public uint HevcSetTileSizesOffset; + public uint HevcSetFilterBufferOffset; + public uint HevcSetSaoBufferOffset; + public uint HevcSetSliceInfoBufferOffset; + public uint HevcSetSliceGroupIndex; + public Array10 Unknown598; + public uint Vp9SetProbTabBufOffset; + public uint Vp9SetCtxCounterBufOffset; + public uint Vp9SetSegmentReadBufOffset; + public uint Vp9SetSegmentWriteBufOffset; + public uint Vp9SetTileSizeBufOffset; + public uint Vp9SetColMvWriteBufOffset; + public uint Vp9SetColMvReadBufOffset; + public uint Vp9SetFilterBufferOffset; #pragma warning restore CS0649 } } diff --git a/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs b/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs index 8a369984e..cce9a5744 100644 --- a/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs +++ b/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs @@ -10,8 +10,8 @@ namespace Ryujinx.Graphics.Nvdec { public static void Decode(NvdecDecoderContext context, ResourceManager rm, ref NvdecRegisters state) { - PictureInfo pictureInfo = rm.Gmm.DeviceRead(state.SetPictureInfoOffset); - ReadOnlySpan bitstream = rm.Gmm.DeviceGetSpan(state.SetBitstreamOffset, (int)pictureInfo.VLDBufferSize); + PictureInfo pictureInfo = rm.Gmm.DeviceRead(state.SetDrvPicSetupOffset); + ReadOnlySpan bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.VLDBufferSize); Decoder decoder = context.GetVp8Decoder(); @@ -19,8 +19,8 @@ namespace Ryujinx.Graphics.Nvdec Vp8PictureInfo info = pictureInfo.Convert(); - uint lumaOffset = state.SetSurfaceLumaOffset[3]; - uint chromaOffset = state.SetSurfaceChromaOffset[3]; + uint lumaOffset = state.SetPictureLumaOffset[3]; + uint chromaOffset = state.SetPictureChromaOffset[3]; if (decoder.Decode(ref info, outputSurface, bitstream)) { diff --git a/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs b/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs index b56dc56ec..9bb3529e8 100644 --- a/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs +++ b/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs @@ -17,18 +17,18 @@ namespace Ryujinx.Graphics.Nvdec public unsafe static void Decode(ResourceManager rm, ref NvdecRegisters state) { - PictureInfo pictureInfo = rm.Gmm.DeviceRead(state.SetPictureInfoOffset); - EntropyProbs entropy = rm.Gmm.DeviceRead(state.SetVp9EntropyProbsOffset); + PictureInfo pictureInfo = rm.Gmm.DeviceRead(state.SetDrvPicSetupOffset); + EntropyProbs entropy = rm.Gmm.DeviceRead(state.Vp9SetProbTabBufOffset); ISurface Rent(uint lumaOffset, uint chromaOffset, FrameSize size) { return rm.Cache.Get(_decoder, lumaOffset, chromaOffset, size.Width, size.Height); } - ISurface lastSurface = Rent(state.SetSurfaceLumaOffset[0], state.SetSurfaceChromaOffset[0], pictureInfo.LastFrameSize); - ISurface goldenSurface = Rent(state.SetSurfaceLumaOffset[1], state.SetSurfaceChromaOffset[1], pictureInfo.GoldenFrameSize); - ISurface altSurface = Rent(state.SetSurfaceLumaOffset[2], state.SetSurfaceChromaOffset[2], pictureInfo.AltFrameSize); - ISurface currentSurface = Rent(state.SetSurfaceLumaOffset[3], state.SetSurfaceChromaOffset[3], pictureInfo.CurrentFrameSize); + ISurface lastSurface = Rent(state.SetPictureLumaOffset[0], state.SetPictureChromaOffset[0], pictureInfo.LastFrameSize); + ISurface goldenSurface = Rent(state.SetPictureLumaOffset[1], state.SetPictureChromaOffset[1], pictureInfo.GoldenFrameSize); + ISurface altSurface = Rent(state.SetPictureLumaOffset[2], state.SetPictureChromaOffset[2], pictureInfo.AltFrameSize); + ISurface currentSurface = Rent(state.SetPictureLumaOffset[3], state.SetPictureChromaOffset[3], pictureInfo.CurrentFrameSize); Vp9PictureInfo info = pictureInfo.Convert(); @@ -38,31 +38,31 @@ namespace Ryujinx.Graphics.Nvdec entropy.Convert(ref info.Entropy); - ReadOnlySpan bitstream = rm.Gmm.DeviceGetSpan(state.SetBitstreamOffset, (int)pictureInfo.BitstreamSize); + ReadOnlySpan bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize); ReadOnlySpan mvsIn = ReadOnlySpan.Empty; if (info.UsePrevInFindMvRefs) { - mvsIn = GetMvsInput(rm.Gmm, pictureInfo.CurrentFrameSize, state.SetVp9LastFrameMvsOffset); + mvsIn = GetMvsInput(rm.Gmm, pictureInfo.CurrentFrameSize, state.Vp9SetColMvReadBufOffset); } int miCols = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Width, 8); int miRows = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Height, 8); - using var mvsRegion = rm.Gmm.GetWritableRegion(ExtendOffset(state.SetVp9CurrFrameMvsOffset), miRows * miCols * 16); + using var mvsRegion = rm.Gmm.GetWritableRegion(ExtendOffset(state.Vp9SetColMvWriteBufOffset), miRows * miCols * 16); Span mvsOut = MemoryMarshal.Cast(mvsRegion.Memory.Span); - uint lumaOffset = state.SetSurfaceLumaOffset[3]; - uint chromaOffset = state.SetSurfaceChromaOffset[3]; + uint lumaOffset = state.SetPictureLumaOffset[3]; + uint chromaOffset = state.SetPictureChromaOffset[3]; if (_decoder.Decode(ref info, currentSurface, bitstream, mvsIn, mvsOut)) { SurfaceWriter.Write(rm.Gmm, currentSurface, lumaOffset, chromaOffset); } - WriteBackwardUpdates(rm.Gmm, state.SetVp9BackwardUpdatesOffset, ref info.BackwardUpdateCounts); + WriteBackwardUpdates(rm.Gmm, state.Vp9SetCtxCounterBufOffset, ref info.BackwardUpdateCounts); rm.Cache.Put(lastSurface); rm.Cache.Put(goldenSurface); From 02714a1291e1b548908ffd7adcd537897bf6f541 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Tue, 3 Jan 2023 18:45:08 +0000 Subject: [PATCH 234/737] Avalonia - Add source generator for locale items (#3999) * Add source generator for locale keys * use locale keys in Ui subdir --- Directory.Packages.props | 2 + Ryujinx.Ava/App.axaml.cs | 10 +-- Ryujinx.Ava/AppHost.cs | 22 ++--- Ryujinx.Ava/Common/ApplicationHelper.cs | 22 ++--- Ryujinx.Ava/Common/Locale/LocaleExtension.cs | 6 +- Ryujinx.Ava/Common/Locale/LocaleManager.cs | 22 +++-- Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs | 2 +- Ryujinx.Ava/Modules/Updater/Updater.cs | 50 +++++------ Ryujinx.Ava/Ryujinx.Ava.csproj | 4 + Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs | 18 ++-- .../UI/Applet/ErrorAppletWindow.axaml.cs | 2 +- .../UI/Applet/SwkbdAppletDialog.axaml.cs | 6 +- Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs | 4 +- .../UI/Controls/NavigationDialogHost.axaml.cs | 4 +- .../ProfileImageSelectionDialog.axaml.cs | 2 +- Ryujinx.Ava/UI/Controls/SaveManager.axaml | 2 +- Ryujinx.Ava/UI/Controls/UserEditor.axaml.cs | 4 +- Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs | 36 ++++---- Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs | 34 +++---- .../Models/Generic/LastPlayedSortComparer.cs | 4 +- Ryujinx.Ava/UI/Models/SaveModel.cs | 8 +- Ryujinx.Ava/UI/Models/TitleUpdateModel.cs | 4 +- .../UI/ViewModels/AmiiboWindowViewModel.cs | 20 ++--- .../ViewModels/ControllerSettingsViewModel.cs | 56 ++++++------ .../UI/ViewModels/MainWindowViewModel.cs | 90 +++++++++---------- .../UI/ViewModels/SettingsViewModel.cs | 10 +-- .../UI/ViewModels/UserProfileViewModel.cs | 10 +-- Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs | 8 +- Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs | 4 +- Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs | 6 +- .../Windows/ControllerSettingsWindow.axaml.cs | 10 +-- .../DownloadableContentManagerWindow.axaml.cs | 12 +-- Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 30 +++---- .../UI/Windows/MotionSettingsWindow.axaml.cs | 6 +- .../UI/Windows/RumbleSettingsWindow.axaml.cs | 6 +- .../UI/Windows/SettingsWindow.axaml.cs | 2 +- .../UI/Windows/TitleUpdateWindow.axaml.cs | 12 +-- Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs | 30 +++++++ .../Ryujinx.Ui.LocaleGenerator.csproj | 20 +++++ Ryujinx.sln | 8 +- 40 files changed, 337 insertions(+), 271 deletions(-) create mode 100644 Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs create mode 100644 Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj diff --git a/Directory.Packages.props b/Directory.Packages.props index c02c1ae56..84d83d0d9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -50,5 +50,7 @@ + + diff --git a/Ryujinx.Ava/App.axaml.cs b/Ryujinx.Ava/App.axaml.cs index e59f9bd39..e36cbfdd6 100644 --- a/Ryujinx.Ava/App.axaml.cs +++ b/Ryujinx.Ava/App.axaml.cs @@ -59,11 +59,11 @@ namespace Ryujinx.Ava if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { var result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance["DialogThemeRestartMessage"], - LocaleManager.Instance["DialogThemeRestartSubMessage"], - LocaleManager.Instance["InputDialogYes"], - LocaleManager.Instance["InputDialogNo"], - LocaleManager.Instance["DialogRestartRequiredMessage"]); + LocaleManager.Instance[LocaleKeys.DialogThemeRestartMessage], + LocaleManager.Instance[LocaleKeys.DialogThemeRestartSubMessage], + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.DialogRestartRequiredMessage]); if (result == UserResult.Yes) { diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index f8bd032cb..0baa94c3b 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -432,10 +432,10 @@ namespace Ryujinx.Ava if (userError == UserError.NoFirmware) { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], - string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString), - LocaleManager.Instance["InputDialogYes"], - LocaleManager.Instance["InputDialogNo"], + LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], ""); if (result != UserResult.Yes) @@ -463,11 +463,11 @@ namespace Ryujinx.Ava _parent.RefreshFirmwareStatus(); await ContentDialogHelper.CreateInfoDialog( - string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString), - string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString), - LocaleManager.Instance["InputDialogOk"], + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString), + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogOk], "", - LocaleManager.Instance["RyujinxInfo"]); + LocaleManager.Instance[LocaleKeys.RyujinxInfo]); } } else @@ -869,7 +869,7 @@ namespace Ryujinx.Ava public void UpdateStatus() { // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued - string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance["Docked"] : LocaleManager.Instance["Handheld"]; + string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; float scale = GraphicsConfig.ResScale; if (scale != 1) @@ -879,11 +879,11 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( Device.EnableDeviceVsync, - LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%", + LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", Renderer.IsVulkan ? "Vulkan" : "OpenGL", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - LocaleManager.Instance["Game"] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", + LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", $"GPU: {_renderer.GetHardwareInfo().GpuVendor}")); } diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 0c562dfe0..8e5bdaec7 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { await ContentDialogHelper.CreateErrorDialog( - string.Format(LocaleManager.Instance["DialogMessageCreateSaveErrorMessage"], result.ToStringWithName())); + string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageCreateSaveErrorMessage], result.ToStringWithName())); }); return false; @@ -100,7 +100,7 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageFindSaveErrorMessage"], result.ToStringWithName())); + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageFindSaveErrorMessage], result.ToStringWithName())); }); return false; @@ -151,7 +151,7 @@ namespace Ryujinx.Ava.Common public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, int programIndex = 0) { - OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance["FolderDialogExtractTitle"] }; + OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] }; string destination = await folderDialog.ShowAsync(_owner); @@ -164,11 +164,11 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - string.Format(LocaleManager.Instance["DialogNcaExtractionMessage"], ncaSectionType, Path.GetFileName(titleFilePath)), + string.Format(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMessage], ncaSectionType, Path.GetFileName(titleFilePath)), "", "", - LocaleManager.Instance["InputDialogCancel"], - LocaleManager.Instance["DialogNcaExtractionTitle"]); + LocaleManager.Instance[LocaleKeys.InputDialogCancel], + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]); if (result == UserResult.Cancel) { @@ -234,7 +234,7 @@ namespace Ryujinx.Ava.Common "Extraction failure. The main NCA was not present in the selected file"); Dispatcher.UIThread.InvokeAsync(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionMainNcaNotFoundErrorMessage"]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); }); return; } @@ -275,7 +275,7 @@ namespace Ryujinx.Ava.Common $"LibHac returned error code: {resultCode.Value.ErrorCode}"); Dispatcher.UIThread.InvokeAsync(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionCheckLogErrorMessage"]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); }); } else if (resultCode.Value.IsSuccess()) @@ -283,11 +283,11 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.InvokeAsync(async () => { await ContentDialogHelper.CreateInfoDialog( - LocaleManager.Instance["DialogNcaExtractionSuccessMessage"], + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage], "", - LocaleManager.Instance["InputDialogOk"], + LocaleManager.Instance[LocaleKeys.InputDialogOk], "", - LocaleManager.Instance["DialogNcaExtractionTitle"]); + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]); }); } } diff --git a/Ryujinx.Ava/Common/Locale/LocaleExtension.cs b/Ryujinx.Ava/Common/Locale/LocaleExtension.cs index 8fca1a00d..6c54becdc 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleExtension.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleExtension.cs @@ -7,16 +7,16 @@ namespace Ryujinx.Ava.Common.Locale { internal class LocaleExtension : MarkupExtension { - public LocaleExtension(string key) + public LocaleExtension(LocaleKeys key) { Key = key; } - public string Key { get; } + public LocaleKeys Key { get; } public override object ProvideValue(IServiceProvider serviceProvider) { - string keyToUse = Key; + LocaleKeys keyToUse = Key; ReflectionBindingExtension binding = new($"[{keyToUse}]") { diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index acbbf2dff..c2251f851 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; @@ -13,17 +14,17 @@ namespace Ryujinx.Ava.Common.Locale { private const string DefaultLanguageCode = "en_US"; - private Dictionary _localeStrings; - private ConcurrentDictionary _dynamicValues; + private Dictionary _localeStrings; + private ConcurrentDictionary _dynamicValues; public static LocaleManager Instance { get; } = new LocaleManager(); - public Dictionary LocaleStrings { get => _localeStrings; set => _localeStrings = value; } + public Dictionary LocaleStrings { get => _localeStrings; set => _localeStrings = value; } public LocaleManager() { - _localeStrings = new Dictionary(); - _dynamicValues = new ConcurrentDictionary(); + _localeStrings = new Dictionary(); + _dynamicValues = new ConcurrentDictionary(); Load(); } @@ -49,7 +50,7 @@ namespace Ryujinx.Ava.Common.Locale } } - public string this[string key] + public string this[LocaleKeys key] { get { @@ -63,7 +64,7 @@ namespace Ryujinx.Ava.Common.Locale return value; } - return key; + return key.ToString(); } set { @@ -73,7 +74,7 @@ namespace Ryujinx.Ava.Common.Locale } } - public void UpdateDynamicValue(string key, params object[] values) + public void UpdateDynamicValue(LocaleKeys key, params object[] values) { _dynamicValues[key] = values; @@ -98,7 +99,10 @@ namespace Ryujinx.Ava.Common.Locale foreach (var item in strings) { - this[item.Key] = item.Value; + if (Enum.TryParse(item.Key, out var key)) + { + this[key] = item.Value; + } } if (Program.PreviewerDetached) diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs index 31a53c32c..b107898e4 100644 --- a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs +++ b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Ava.Input return null; } - return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance["AllKeyboards"]); + return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance[LocaleKeys.AllKeyboards]); } protected virtual void Dispose(bool disposing) diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index d495131f0..7bf147fe4 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -84,7 +84,7 @@ namespace Ryujinx.Modules Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]); + await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); }); return; @@ -119,7 +119,7 @@ namespace Ryujinx.Modules { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], ""); + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); }); } @@ -137,7 +137,7 @@ namespace Ryujinx.Modules { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], ""); + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); }); } @@ -150,7 +150,7 @@ namespace Ryujinx.Modules Logger.Error?.Print(LogClass.Application, exception.Message); Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdaterFailedToGetVersionMessage"]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]); }); return; @@ -165,7 +165,7 @@ namespace Ryujinx.Modules Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!"); Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedGithubMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]); + await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); }); return; @@ -177,7 +177,7 @@ namespace Ryujinx.Modules { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], ""); + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); }); } @@ -210,8 +210,8 @@ namespace Ryujinx.Modules Dispatcher.UIThread.Post(async () => { // Show a message asking the user if they want to update - var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], - LocaleManager.Instance["RyujinxUpdaterMessage"], + var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage], $"{Program.Version} -> {newVersion}"); if (shouldUpdate) @@ -247,8 +247,8 @@ namespace Ryujinx.Modules var taskDialog = new TaskDialog() { - Header = LocaleManager.Instance["RyujinxUpdater"], - SubHeader = LocaleManager.Instance["UpdaterDownloading"], + Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], IconSource = new SymbolIconSource { Symbol = Symbol.Download }, Buttons = { }, ShowProgressBar = true @@ -272,9 +272,9 @@ namespace Ryujinx.Modules if (UpdateSuccessful) { - var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], - LocaleManager.Instance["DialogUpdaterCompleteMessage"], - LocaleManager.Instance["DialogUpdaterRestartMessage"]); + var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]); if (shouldRestart) { @@ -478,7 +478,7 @@ namespace Ryujinx.Modules private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) { // Extract Update - taskDialog.SubHeader = LocaleManager.Instance["UpdaterExtracting"]; + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); if (OperatingSystem.IsLinux()) @@ -556,7 +556,7 @@ namespace Ryujinx.Modules List allFiles = EnumerateFilesToDelete().ToList(); - taskDialog.SubHeader = LocaleManager.Instance["UpdaterRenaming"]; + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); // Replace old files @@ -577,13 +577,13 @@ namespace Ryujinx.Modules } catch { - Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance["UpdaterRenameFailed"], file)); + Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance[LocaleKeys.UpdaterRenameFailed], file)); } } Dispatcher.UIThread.Post(() => { - taskDialog.SubHeader = LocaleManager.Instance["UpdaterAddingFiles"]; + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); }); @@ -607,8 +607,8 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterArchNotSupportedMessage"], - LocaleManager.Instance["DialogUpdaterArchNotSupportedSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]); } return false; @@ -618,8 +618,8 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterNoInternetMessage"], - LocaleManager.Instance["DialogUpdaterNoInternetSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]); } return false; @@ -629,8 +629,8 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterDirtyBuildMessage"], - LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); } return false; @@ -642,11 +642,11 @@ namespace Ryujinx.Modules { if (ReleaseInformations.IsFlatHubBuild()) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterFlatpakNotSupportedMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]); } else { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); } } diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 6da118491..3e3bdc8c7 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -58,6 +58,7 @@ + @@ -158,4 +159,7 @@ + + + diff --git a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs index a8e76275c..f4d9bc806 100644 --- a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs +++ b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs @@ -33,15 +33,15 @@ namespace Ryujinx.Ava.UI.Applet ? args.PlayerCountMin.ToString() : $"{args.PlayerCountMin}-{args.PlayerCountMax}"; - string key = args.PlayerCountMin == args.PlayerCountMax ? "DialogControllerAppletMessage" : "DialogControllerAppletMessagePlayerRange"; + LocaleKeys key = args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange; string message = string.Format(LocaleManager.Instance[key], playerCount, args.SupportedStyles, string.Join(", ", args.SupportedPlayers), - args.IsDocked ? LocaleManager.Instance["DialogControllerAppletDockModeSet"] : ""); + args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : ""); - return DisplayMessageDialog(LocaleManager.Instance["DialogControllerAppletTitle"], message); + return DisplayMessageDialog(LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle], message); } public bool DisplayMessageDialog(string title, string message) @@ -62,9 +62,9 @@ namespace Ryujinx.Ava.UI.Applet title, message, "", - LocaleManager.Instance["DialogOpenSettingsWindowLabel"], + LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel], "", - LocaleManager.Instance["SettingsButtonClose"], + LocaleManager.Instance[LocaleKeys.SettingsButtonClose], (int)Symbol.Important, deferEvent, async (window) => @@ -92,7 +92,7 @@ namespace Ryujinx.Ava.UI.Applet } catch (Exception ex) { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageDialogErrorExceptionMessage"], ex)); + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageDialogErrorExceptionMessage], ex)); dialogCloseEvent.Set(); } @@ -115,7 +115,7 @@ namespace Ryujinx.Ava.UI.Applet { try { - var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance["SoftwareKeyboard"], args); + var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args); if (response.Result == UserResult.Ok) { @@ -126,7 +126,7 @@ namespace Ryujinx.Ava.UI.Applet catch (Exception ex) { error = true; - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogSoftwareKeyboardErrorExceptionMessage"], ex)); + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage], ex)); } finally { @@ -181,7 +181,7 @@ namespace Ryujinx.Ava.UI.Applet catch (Exception ex) { dialogCloseEvent.Set(); - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogErrorAppletErrorExceptionMessage"], ex)); + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogErrorAppletErrorExceptionMessage], ex)); } }); diff --git a/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs b/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs index a17826f88..4134797ba 100644 --- a/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Applet } else { - AddButton(LocaleManager.Instance["InputDialogOk"], 0); + AddButton(LocaleManager.Instance[LocaleKeys.InputDialogOk], 0); } } diff --git a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index 80be29798..78e6f237e 100644 --- a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Controls contentDialog.PrimaryButtonText = args.SubmitText; contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length); contentDialog.SecondaryButtonText = ""; - contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"]; + contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel]; contentDialog.Content = content; TypedEventHandler handler = (sender, eventArgs) => @@ -139,14 +139,14 @@ namespace Ryujinx.Ava.UI.Controls else if (_inputMin > 0 && _inputMax == int.MaxValue) { Error.IsVisible = true; - Error.Text = string.Format(LocaleManager.Instance["SwkbdMinCharacters"], _inputMin); + Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinCharacters], _inputMin); _checkLength = length => _inputMin <= length; } else { Error.IsVisible = true; - Error.Text = string.Format(LocaleManager.Instance["SwkbdMinRangeCharacters"], _inputMin, _inputMax); + Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinRangeCharacters], _inputMin, _inputMax); _checkLength = length => _inputMin <= length && length <= _inputMax; } diff --git a/Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs b/Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs index abaabd3b3..8dba5e2b4 100644 --- a/Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs @@ -39,9 +39,9 @@ namespace Ryujinx.Ava.UI.Controls ContentDialog contentDialog = new ContentDialog { Title = title, - PrimaryButtonText = LocaleManager.Instance["InputDialogOk"], + PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk], SecondaryButtonText = "", - CloseButtonText = LocaleManager.Instance["InputDialogCancel"], + CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel], Content = content, PrimaryButtonCommand = MiniCommand.Create(() => { diff --git a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs index 98f9e9e3d..0c3002675 100644 --- a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs @@ -65,10 +65,10 @@ namespace Ryujinx.Ava.UI.Controls var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient); ContentDialog contentDialog = new ContentDialog { - Title = LocaleManager.Instance["UserProfileWindowTitle"], + Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle], PrimaryButtonText = "", SecondaryButtonText = "", - CloseButtonText = LocaleManager.Instance["UserProfilesClose"], + CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], Content = content, Padding = new Thickness(0) }; diff --git a/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs b/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs index 00183b698..46a2f5079 100644 --- a/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Ava.UI.Controls OpenFileDialog dialog = new(); dialog.Filters.Add(new FileDialogFilter { - Name = LocaleManager.Instance["AllSupportedFormats"], + Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats], Extensions = { "jpg", "jpeg", "png", "bmp" } }); dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } }); diff --git a/Ryujinx.Ava/UI/Controls/SaveManager.axaml b/Ryujinx.Ava/UI/Controls/SaveManager.axaml index b0dc4c6f7..64674b65b 100644 --- a/Ryujinx.Ava/UI/Controls/SaveManager.axaml +++ b/Ryujinx.Ava/UI/Controls/SaveManager.axaml @@ -62,7 +62,7 @@ /// Unmaps a region of memory that was previously mapped with . @@ -2926,7 +2927,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Current protection of the destination memory region /// Desired protection of the source memory region /// Result of the unmapping operation - protected abstract KernelResult UnmapMemory(ulong dst, ulong src, ulong pagesCount, KMemoryPermission oldDstPermission, KMemoryPermission newSrcPermission); + protected abstract Result UnmapMemory(ulong dst, ulong src, ulong pagesCount, KMemoryPermission oldDstPermission, KMemoryPermission newSrcPermission); /// /// Maps a region of memory into the specified physical memory region. @@ -2938,7 +2939,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Indicate if the pages should be filled with the value /// The value used to fill pages when is set to true /// Result of the mapping operation - protected abstract KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); + protected abstract Result MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); /// /// Maps a region of memory into the specified physical memory region. @@ -2949,7 +2950,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Indicate if the pages should be filled with the value /// The value used to fill pages when is set to true /// Result of the mapping operation - protected abstract KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); + protected abstract Result MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); /// /// Unmaps a region of memory that was previously mapped with one of the page mapping methods. @@ -2957,7 +2958,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Virtual address of the region to unmap /// Number of pages to unmap /// Result of the unmapping operation - protected abstract KernelResult Unmap(ulong address, ulong pagesCount); + protected abstract Result Unmap(ulong address, ulong pagesCount); /// /// Changes the permissions of a given virtual memory region. @@ -2966,7 +2967,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Number of pages to have their permissions changed /// New permission /// Result of the permission change operation - protected abstract KernelResult Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission); + protected abstract Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission); /// /// Changes the permissions of a given virtual memory region. @@ -2975,7 +2976,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Number of pages to have their permissions changed /// New permission /// Result of the permission change operation - protected abstract KernelResult ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission); + protected abstract Result ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission); /// /// Alerts the memory tracking that a given region has been read from or written to. diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs index 3af627505..2dbaf3cd8 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs @@ -1,6 +1,7 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Kernel.Memory { @@ -26,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _userPermission = userPermission; } - public KernelResult MapIntoProcess( + public Result MapIntoProcess( KPageTableBase memoryManager, ulong address, ulong size, @@ -50,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission); } - public KernelResult UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process) + public Result UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process) { if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) { diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs index 2888efb83..b24495980 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs @@ -1,6 +1,7 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Kernel.Memory @@ -36,15 +37,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _isMapped = false; } - public KernelResult Initialize(ulong address, ulong size, KMemoryPermission permission) + public Result Initialize(ulong address, ulong size, KMemoryPermission permission) { KProcess creator = KernelStatic.GetCurrentProcess(); _creator = creator; - KernelResult result = creator.MemoryManager.BorrowTransferMemory(_pageList, address, size, permission); + Result result = creator.MemoryManager.BorrowTransferMemory(_pageList, address, size, permission); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -60,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - public KernelResult MapIntoProcess( + public Result MapIntoProcess( KPageTableBase memoryManager, ulong address, ulong size, @@ -79,9 +80,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory; - KernelResult result = memoryManager.MapPages(address, _pageList, state, KMemoryPermission.ReadAndWrite); + Result result = memoryManager.MapPages(address, _pageList, state, KMemoryPermission.ReadAndWrite); - if (result == KernelResult.Success) + if (result == Result.Success) { _isMapped = true; } @@ -89,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - public KernelResult UnmapFromProcess( + public Result UnmapFromProcess( KPageTableBase memoryManager, ulong address, ulong size, @@ -102,9 +103,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory; - KernelResult result = memoryManager.UnmapPages(address, _pageList, state); + Result result = memoryManager.UnmapPages(address, _pageList, state); - if (result == KernelResult.Success) + if (result == Result.Success) { _isMapped = false; } @@ -116,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { if (_hasBeenInitialized) { - if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _pageList) != KernelResult.Success) + if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _pageList) != Result.Success) { throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes."); } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs index bcbb3b03a..c15ebef5c 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Kernel.Process @@ -27,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _context = context; } - public KernelResult Initialize(int size) + public Result Initialize(int size) { if ((uint)size > 1024) { @@ -62,10 +63,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _nextFreeEntry = _tableHead; - return KernelResult.Success; + return Result.Success; } - public KernelResult GenerateHandle(KAutoObject obj, out int handle) + public Result GenerateHandle(KAutoObject obj, out int handle) { handle = 0; @@ -99,10 +100,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - return KernelResult.Success; + return Result.Success; } - public KernelResult ReserveHandle(out int handle) + public Result ReserveHandle(out int handle) { handle = 0; @@ -131,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - return KernelResult.Success; + return Result.Success; } public void CancelHandleReservation(int handle) diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 6a2d45ea4..8d9cd242f 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -5,6 +5,7 @@ using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using Ryujinx.Memory; using System; using System.Collections.Generic; @@ -116,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process Debugger = new HleProcessDebugger(this); } - public KernelResult InitializeKip( + public Result InitializeKip( ProcessCreationInfo creationInfo, ReadOnlySpan capabilities, KPageList pageList, @@ -151,7 +152,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ? KernelContext.LargeMemoryBlockSlabManager : KernelContext.SmallMemoryBlockSlabManager; - KernelResult result = MemoryManager.InitializeForProcess( + Result result = MemoryManager.InitializeForProcess( addrSpaceType, aslrEnabled, !aslrEnabled, @@ -160,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process codeSize, slabManager); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -172,14 +173,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = MemoryManager.MapPages(codeAddress, pageList, MemoryState.CodeStatic, KMemoryPermission.None); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } result = Capabilities.InitializeForKernel(capabilities, MemoryManager); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -187,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return ParseProcessInfo(creationInfo); } - public KernelResult Initialize( + public Result Initialize( ProcessCreationInfo creationInfo, ReadOnlySpan capabilities, KResourceLimit resourceLimit, @@ -255,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ulong codeSize = codePagesCount * KPageTableBase.PageSize; - KernelResult result = MemoryManager.InitializeForProcess( + Result result = MemoryManager.InitializeForProcess( addrSpaceType, aslrEnabled, !aslrEnabled, @@ -264,7 +265,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process codeSize, slabManager); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -284,7 +285,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process MemoryState.CodeStatic, KMemoryPermission.None); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -293,7 +294,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = Capabilities.InitializeForUser(capabilities, MemoryManager); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -302,7 +303,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = ParseProcessInfo(creationInfo); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); } @@ -310,7 +311,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - private KernelResult ParseProcessInfo(ProcessCreationInfo creationInfo) + private Result ParseProcessInfo(ProcessCreationInfo creationInfo) { // Ensure that the current kernel version is equal or above to the minimum required. uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19; @@ -334,9 +335,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - KernelResult result = AllocateThreadLocalStorage(out ulong userExceptionContextAddress); + Result result = AllocateThreadLocalStorage(out ulong userExceptionContextAddress); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -378,14 +379,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process GenerateRandomEntropy(); - return KernelResult.Success; + return Result.Success; } - public KernelResult AllocateThreadLocalStorage(out ulong address) + public Result AllocateThreadLocalStorage(out ulong address) { KernelContext.CriticalSection.Enter(); - KernelResult result; + Result result; if (_freeTlsPages.Count > 0) { @@ -404,14 +405,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _fullTlsPages.Add(pageInfo.PageVirtualAddress, pageInfo); } - result = KernelResult.Success; + result = Result.Success; } else { // Otherwise, we need to create a new one. result = AllocateTlsPage(out KTlsPageInfo pageInfo); - if (result == KernelResult.Success) + if (result == Result.Success) { if (!pageInfo.TryGetFreePage(out address)) { @@ -431,7 +432,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - private KernelResult AllocateTlsPage(out KTlsPageInfo pageInfo) + private Result AllocateTlsPage(out KTlsPageInfo pageInfo) { pageInfo = default; @@ -445,7 +446,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ulong regionPagesCount = regionSize / KPageTableBase.PageSize; - KernelResult result = MemoryManager.MapPages( + Result result = MemoryManager.MapPages( 1, KPageTableBase.PageSize, tlsPagePa, @@ -456,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process KMemoryPermission.ReadAndWrite, out ulong tlsPageVa); - if (result != KernelResult.Success) + if (result != Result.Success) { KernelContext.UserSlabHeapPages.Free(tlsPagePa); } @@ -470,13 +471,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - public KernelResult FreeThreadLocalStorage(ulong tlsSlotAddr) + public Result FreeThreadLocalStorage(ulong tlsSlotAddr) { ulong tlsPageAddr = BitUtils.AlignDown(tlsSlotAddr, KPageTableBase.PageSize); KernelContext.CriticalSection.Enter(); - KernelResult result = KernelResult.Success; + Result result = Result.Success; KTlsPageInfo pageInfo; @@ -506,7 +507,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process FreeTlsPage(pageInfo); - return KernelResult.Success; + return Result.Success; } } @@ -515,11 +516,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - private KernelResult FreeTlsPage(KTlsPageInfo pageInfo) + private Result FreeTlsPage(KTlsPageInfo pageInfo) { - KernelResult result = MemoryManager.UnmapForKernel(pageInfo.PageVirtualAddress, 1, MemoryState.ThreadLocal); + Result result = MemoryManager.UnmapForKernel(pageInfo.PageVirtualAddress, 1, MemoryState.ThreadLocal); - if (result == KernelResult.Success) + if (result == Result.Success) { KernelContext.UserSlabHeapPages.Free(pageInfo.PagePhysicalAddress); } @@ -532,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process // TODO. } - public KernelResult Start(int mainThreadPriority, ulong stackSize) + public Result Start(int mainThreadPriority, ulong stackSize) { lock (_processLock) { @@ -580,7 +581,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - KernelResult result; + Result result; KThread mainThread = null; @@ -627,7 +628,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process KMemoryPermission.ReadAndWrite, out ulong stackBottom); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -643,7 +644,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = MemoryManager.SetHeapCapacity(heapCapacity); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -654,7 +655,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = HandleTable.Initialize(Capabilities.HandleTableSize); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -673,7 +674,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ThreadType.User, _customThreadStart); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -682,7 +683,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = HandleTable.GenerateHandle(mainThread, out int mainThreadHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -700,14 +701,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = mainThread.Start(); - if (result != KernelResult.Success) + if (result != Result.Success) { SetState(oldState); CleanUpForError(); } - if (result == KernelResult.Success) + if (result == Result.Success) { mainThread.IncrementReferenceCount(); } @@ -729,7 +730,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - public KernelResult InitializeThread( + public Result InitializeThread( KThread thread, ulong entrypoint, ulong argsPtr, @@ -888,9 +889,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return _signaled; } - public KernelResult Terminate() + public Result Terminate() { - KernelResult result; + Result result; bool shallTerminate = false; @@ -910,7 +911,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process shallTerminate = true; } - result = KernelResult.Success; + result = Result.Success; } else { @@ -1044,9 +1045,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process KernelContext.CriticalSection.Leave(); } - public KernelResult ClearIfNotExited() + public Result ClearIfNotExited() { - KernelResult result; + Result result; KernelContext.CriticalSection.Enter(); @@ -1056,7 +1057,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { _signaled = false; - result = KernelResult.Success; + result = Result.Success; } else { @@ -1107,7 +1108,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process protected override void Destroy() => Context.Dispose(); - public KernelResult SetActivity(bool pause) + public Result SetActivity(bool pause) { KernelContext.CriticalSection.Enter(); @@ -1154,7 +1155,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process KernelContext.CriticalSection.Leave(); - return KernelResult.Success; + return Result.Success; } KernelContext.CriticalSection.Leave(); diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index a08c4b263..ef55a165f 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; using System.Numerics; @@ -25,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process IrqAccessMask = new byte[0x80]; } - public KernelResult InitializeForKernel(ReadOnlySpan capabilities, KPageTableBase memoryManager) + public Result InitializeForKernel(ReadOnlySpan capabilities, KPageTableBase memoryManager) { AllowedCpuCoresMask = 0xf; AllowedThreadPriosMask = ulong.MaxValue; @@ -35,12 +36,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Parse(capabilities, memoryManager); } - public KernelResult InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager) + public Result InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager) { return Parse(capabilities, memoryManager); } - private KernelResult Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager) + private Result Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager) { int mask0 = 0; int mask1 = 0; @@ -51,9 +52,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if (((cap + 1) & ~cap) != 0x40) { - KernelResult result = ParseCapability(cap, ref mask0, ref mask1, memoryManager); + Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -96,7 +97,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ? KMemoryPermission.Read : KMemoryPermission.ReadAndWrite; - KernelResult result; + Result result; if ((cap >> 31) != 0) { @@ -107,17 +108,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Process result = memoryManager.MapIoMemory(address, size, perm); } - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } } } - return KernelResult.Success; + return Result.Success; } - private KernelResult ParseCapability(int cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) + private Result ParseCapability(int cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) { int code = (cap + 1) & ~cap; @@ -127,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } else if (code == 0) { - return KernelResult.Success; + return Result.Success; } int codeMask = 1 << (32 - BitOperations.LeadingZeroCount((uint)code + 1)); @@ -300,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process default: return KernelResult.InvalidCapability; } - return KernelResult.Success; + return Result.Success; } private static ulong GetMaskFromMinMax(int min, int max) diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs index a08412529..77fcdf33b 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs @@ -7,23 +7,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { public ulong Pc => 0UL; - public ulong CntfrqEl0 { get => 0; set { } } + public ulong CntfrqEl0 { get; set; } public ulong CntpctEl0 => 0UL; - public long TpidrEl0 { get => 0; set { } } - public long TpidrroEl0 { get => 0; set { } } + public long TpidrEl0 { get; set; } + public long TpidrroEl0 { get; set; } - public uint Pstate { get => 0; set { } } + public uint Pstate { get; set; } - public uint Fpcr { get => 0; set { } } - public uint Fpsr { get => 0; set { } } + public uint Fpcr { get; set; } + public uint Fpsr { get; set; } public bool IsAarch32 { get => false; set { } } public bool Running { get; private set; } = true; - public ulong GetX(int index) => 0UL; - public void SetX(int index, ulong value) { } + private readonly ulong[] _x = new ulong[32]; + + public ulong GetX(int index) => _x[index]; + public void SetX(int index, ulong value) => _x[index] = value; public V128 GetV(int index) => default; public void SetV(int index, V128 value) { } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index c3fb8b8ad..e23274ebc 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -7,13 +7,14 @@ using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { [SvcImpl] - class Syscall + class Syscall : ISyscallApi { private readonly KernelContext _context; @@ -25,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall // Process [Svc(0x24)] - public KernelResult GetProcessId(out ulong pid, int handle) + public Result GetProcessId(out ulong pid, int handle) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -46,11 +47,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall pid = process?.Pid ?? 0; return process != null - ? KernelResult.Success + ? Result.Success : KernelResult.InvalidHandle; } - public KernelResult CreateProcess( + public Result CreateProcess( out int handle, ProcessCreationInfo info, ReadOnlySpan capabilities, @@ -118,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall _ => MemoryRegion.NvServices }; - KernelResult result = process.Initialize( + Result result = process.Initialize( info, capabilities, resourceLimit, @@ -126,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall contextFactory, customThreadStart); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -136,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return handleTable.GenerateHandle(process, out handle); } - public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize) + public Result StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize) { KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject(handle); @@ -157,30 +158,30 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall process.DefaultCpuCore = cpuCore; - KernelResult result = process.Start(priority, mainThreadStackSize); + Result result = process.Start(priority, mainThreadStackSize); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } process.IncrementReferenceCount(); - return KernelResult.Success; + return Result.Success; } [Svc(0x5f)] - public KernelResult FlushProcessDataCache(int processHandle, ulong address, ulong size) + public Result FlushProcessDataCache(int processHandle, ulong address, ulong size) { // FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0. // As we don't support (and don't actually need) to flush the cache, this is stubbed. - return KernelResult.Success; + return Result.Success; } // IPC [Svc(0x1f)] - public KernelResult ConnectToNamedPort(out int handle, [PointerSized] ulong namePtr) + public Result ConnectToNamedPort(out int handle, [PointerSized] ulong namePtr) { handle = 0; @@ -192,7 +193,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return ConnectToNamedPort(out handle, name); } - public KernelResult ConnectToNamedPort(out int handle, string name) + public Result ConnectToNamedPort(out int handle, string name) { handle = 0; @@ -210,16 +211,16 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle); + Result result = currentProcess.HandleTable.ReserveHandle(out handle); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } result = clientPort.Connect(out KClientSession clientSession); - if (result != KernelResult.Success) + if (result != Result.Success) { currentProcess.HandleTable.CancelHandleReservation(handle); @@ -234,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x21)] - public KernelResult SendSyncRequest(int handle) + public Result SendSyncRequest(int handle) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -249,7 +250,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x22)] - public KernelResult SendSyncRequestWithUserBuffer( + public Result SendSyncRequestWithUserBuffer( [PointerSized] ulong messagePtr, [PointerSized] ulong messageSize, int handle) @@ -271,9 +272,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); + Result result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -289,9 +290,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall result = session.SendSyncRequest(messagePtr, messageSize); } - KernelResult result2 = currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); + Result result2 = currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); - if (result == KernelResult.Success) + if (result == Result.Success) { result = result2; } @@ -300,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x23)] - public KernelResult SendAsyncRequestWithUserBuffer( + public Result SendAsyncRequestWithUserBuffer( out int doneEventHandle, [PointerSized] ulong messagePtr, [PointerSized] ulong messageSize, @@ -325,9 +326,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); + Result result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -353,18 +354,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall result = currentProcess.HandleTable.GenerateHandle(doneEvent.ReadableEvent, out doneEventHandle); - if (result == KernelResult.Success) + if (result == Result.Success) { result = session.SendAsyncRequest(doneEvent.WritableEvent, messagePtr, messageSize); - if (result != KernelResult.Success) + if (result != Result.Success) { currentProcess.HandleTable.CloseHandle(doneEventHandle); } } } - if (result != KernelResult.Success) + if (result != Result.Success) { resourceLimit?.Release(LimitableResource.Event, 1); @@ -375,11 +376,20 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x40)] - public KernelResult CreateSession( + public Result CreateSession( out int serverSessionHandle, out int clientSessionHandle, bool isLight, [PointerSized] ulong namePtr) + { + return CreateSession(out serverSessionHandle, out clientSessionHandle, isLight, null); + } + + public Result CreateSession( + out int serverSessionHandle, + out int clientSessionHandle, + bool isLight, + string name) { serverSessionHandle = 0; clientSessionHandle = 0; @@ -393,7 +403,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.ResLimitExceeded; } - KernelResult result; + Result result; if (isLight) { @@ -401,11 +411,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle); - if (result == KernelResult.Success) + if (result == Result.Success) { result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { currentProcess.HandleTable.CloseHandle(serverSessionHandle); @@ -422,11 +432,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle); - if (result == KernelResult.Success) + if (result == Result.Success) { result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { currentProcess.HandleTable.CloseHandle(serverSessionHandle); @@ -442,7 +452,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x41)] - public KernelResult AcceptSession(out int sessionHandle, int portHandle) + public Result AcceptSession(out int sessionHandle, int portHandle) { sessionHandle = 0; @@ -455,9 +465,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidHandle; } - KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle); + Result result = currentProcess.HandleTable.ReserveHandle(out int handle); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -481,7 +491,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall sessionHandle = handle; - result = KernelResult.Success; + result = Result.Success; } else { @@ -494,7 +504,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x43)] - public KernelResult ReplyAndReceive( + public Result ReplyAndReceive( out int handleIndex, [PointerSized] ulong handlesPtr, int handlesCount, @@ -537,7 +547,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return ReplyAndReceive(out handleIndex, handles, replyTargetHandle, timeout); } - public KernelResult ReplyAndReceive(out int handleIndex, ReadOnlySpan handles, int replyTargetHandle, long timeout) + public Result ReplyAndReceive(out int handleIndex, ReadOnlySpan handles, int replyTargetHandle, long timeout) { handleIndex = 0; @@ -557,7 +567,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall syncObjs[index] = obj; } - KernelResult result = KernelResult.Success; + Result result = Result.Success; if (replyTargetHandle != 0) { @@ -573,14 +583,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } } - if (result == KernelResult.Success) + if (result == Result.Success) { if (timeout > 0) { timeout += KTimeManager.DefaultTimeIncrementNanoseconds; } - while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success) + while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == Result.Success) { KServerSession session = currentProcess.HandleTable.GetObject(handles[handleIndex]); @@ -600,7 +610,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x44)] - public KernelResult ReplyAndReceiveWithUserBuffer( + public Result ReplyAndReceiveWithUserBuffer( out int handleIndex, [PointerSized] ulong messagePtr, [PointerSized] ulong messageSize, @@ -630,9 +640,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } - KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); + Result result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -676,14 +686,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } } - if (result == KernelResult.Success) + if (result == Result.Success) { if (timeout > 0) { timeout += KTimeManager.DefaultTimeIncrementNanoseconds; } - while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success) + while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == Result.Success) { KServerSession session = currentProcess.HandleTable.GetObject(handles[handleIndex]); @@ -705,12 +715,23 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x70)] - public KernelResult CreatePort( + public Result CreatePort( out int serverPortHandle, out int clientPortHandle, int maxSessions, bool isLight, [PointerSized] ulong namePtr) + { + // The kernel doesn't use the name pointer, so we can just pass null as the name. + return CreatePort(out serverPortHandle, out clientPortHandle, maxSessions, isLight, null); + } + + public Result CreatePort( + out int serverPortHandle, + out int clientPortHandle, + int maxSessions, + bool isLight, + string name) { serverPortHandle = clientPortHandle = 0; @@ -719,20 +740,20 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.MaximumExceeded; } - KPort port = new KPort(_context, maxSessions, isLight, (long)namePtr); + KPort port = new KPort(_context, maxSessions, isLight, name); KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle); + Result result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { currentProcess.HandleTable.CloseHandle(clientPortHandle); } @@ -741,7 +762,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x71)] - public KernelResult ManageNamedPort(out int handle, [PointerSized] ulong namePtr, int maxSessions) + public Result ManageNamedPort(out int handle, [PointerSized] ulong namePtr, int maxSessions) { handle = 0; @@ -758,7 +779,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return ManageNamedPort(out handle, name, maxSessions); } - public KernelResult ManageNamedPort(out int handle, string name, int maxSessions) + public Result ManageNamedPort(out int handle, string name, int maxSessions) { handle = 0; @@ -772,20 +793,20 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KAutoObject.RemoveName(_context, name); } - KPort port = new KPort(_context, maxSessions, false, 0); + KPort port = new KPort(_context, maxSessions, false, null); KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle); + Result result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } result = port.ClientPort.SetName(name); - if (result != KernelResult.Success) + if (result != Result.Success) { currentProcess.HandleTable.CloseHandle(handle); } @@ -794,7 +815,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x72)] - public KernelResult ConnectToPort(out int clientSessionHandle, int clientPortHandle) + public Result ConnectToPort(out int clientSessionHandle, int clientPortHandle) { clientSessionHandle = 0; @@ -807,9 +828,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidHandle; } - KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle); + Result result = currentProcess.HandleTable.ReserveHandle(out int handle); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -829,7 +850,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall session = clientSession; } - if (result != KernelResult.Success) + if (result != Result.Success) { currentProcess.HandleTable.CancelHandleReservation(handle); @@ -848,7 +869,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall // Memory [Svc(1)] - public KernelResult SetHeapSize([PointerSized] out ulong address, [PointerSized] ulong size) + public Result SetHeapSize([PointerSized] out ulong address, [PointerSized] ulong size) { if ((size & 0xfffffffe001fffff) != 0) { @@ -863,7 +884,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(2)] - public KernelResult SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) + public Result SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) { if (!PageAligned(address)) { @@ -896,7 +917,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(3)] - public KernelResult SetMemoryAttribute( + public Result SetMemoryAttribute( [PointerSized] ulong address, [PointerSized] ulong size, MemoryAttribute attributeMask, @@ -927,7 +948,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - KernelResult result = process.MemoryManager.SetMemoryAttribute( + Result result = process.MemoryManager.SetMemoryAttribute( address, size, attributeMask, @@ -937,7 +958,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(4)] - public KernelResult MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) + public Result MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) { if (!PageAligned(src | dst)) { @@ -974,7 +995,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(5)] - public KernelResult UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) + public Result UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) { if (!PageAligned(src | dst)) { @@ -1011,21 +1032,21 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(6)] - public KernelResult QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address) + public Result QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address) { - KernelResult result = QueryMemory(out MemoryInfo info, out pageInfo, address); + Result result = QueryMemory(out MemoryInfo info, out pageInfo, address); - if (result == KernelResult.Success) + if (result == Result.Success) { return KernelTransfer.KernelToUser(infoPtr, info) - ? KernelResult.Success + ? Result.Success : KernelResult.InvalidMemState; } return result; } - public KernelResult QueryMemory(out MemoryInfo info, out ulong pageInfo, ulong address) + public Result QueryMemory(out MemoryInfo info, out ulong pageInfo, ulong address) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -1042,11 +1063,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall pageInfo = 0; - return KernelResult.Success; + return Result.Success; } [Svc(0x13)] - public KernelResult MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) + public Result MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) { if (!PageAligned(address)) { @@ -1093,7 +1114,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x14)] - public KernelResult UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) + public Result UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) { @@ -1134,7 +1155,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x15)] - public KernelResult CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) + public Result CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) { handle = 0; @@ -1181,9 +1202,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KTransferMemory transferMemory = new KTransferMemory(_context); - KernelResult result = transferMemory.Initialize(address, size, permission); + Result result = transferMemory.Initialize(address, size, permission); - if (result != KernelResult.Success) + if (result != Result.Success) { CleanUpForError(); @@ -1198,7 +1219,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x51)] - public KernelResult MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) + public Result MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) { if (!PageAligned(address)) { @@ -1245,7 +1266,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x52)] - public KernelResult UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) + public Result UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) { @@ -1286,7 +1307,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x2c)] - public KernelResult MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) + public Result MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) { @@ -1322,7 +1343,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x2d)] - public KernelResult UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) + public Result UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) { @@ -1358,7 +1379,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x4b)] - public KernelResult CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size) + public Result CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size) { handle = 0; @@ -1388,9 +1409,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - KernelResult result = codeMemory.Initialize(address, size); + Result result = codeMemory.Initialize(address, size); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -1399,7 +1420,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x4c)] - public KernelResult ControlCodeMemory( + public Result ControlCodeMemory( int handle, CodeMemoryOperation op, ulong address, @@ -1477,7 +1498,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x73)] - public KernelResult SetProcessMemoryPermission( + public Result SetProcessMemoryPermission( int handle, [PointerSized] ulong src, [PointerSized] ulong size, @@ -1519,7 +1540,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x74)] - public KernelResult MapProcessMemory( + public Result MapProcessMemory( [PointerSized] ulong dst, int handle, ulong src, @@ -1556,7 +1577,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KPageList pageList = new KPageList(); - KernelResult result = srcProcess.MemoryManager.GetPagesIfStateEquals( + Result result = srcProcess.MemoryManager.GetPagesIfStateEquals( src, size, MemoryState.MapProcessAllowed, @@ -1567,7 +1588,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall MemoryAttribute.None, pageList); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -1576,7 +1597,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x75)] - public KernelResult UnmapProcessMemory( + public Result UnmapProcessMemory( [PointerSized] ulong dst, int handle, ulong src, @@ -1611,18 +1632,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemRange; } - KernelResult result = dstProcess.MemoryManager.UnmapProcessMemory(dst, size, srcProcess.MemoryManager, src); + Result result = dstProcess.MemoryManager.UnmapProcessMemory(dst, size, srcProcess.MemoryManager, src); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } - return KernelResult.Success; + return Result.Success; } [Svc(0x77)] - public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) + public Result MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) { if (!PageAligned(dst) || !PageAligned(src)) { @@ -1660,7 +1681,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x78)] - public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) + public Result UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) { if (!PageAligned(dst) || !PageAligned(src)) { @@ -1705,19 +1726,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall // System [Svc(0x7b)] - public KernelResult TerminateProcess(int handle) + public Result TerminateProcess(int handle) { KProcess process = KernelStatic.GetCurrentProcess(); process = process.HandleTable.GetObject(handle); - KernelResult result; + Result result; if (process != null) { if (process == KernelStatic.GetCurrentProcess()) { - result = KernelResult.Success; + result = Result.Success; process.DecrementToZeroWhileTerminatingCurrent(); } else @@ -1741,19 +1762,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x11)] - public KernelResult SignalEvent(int handle) + public Result SignalEvent(int handle) { KProcess process = KernelStatic.GetCurrentProcess(); KWritableEvent writableEvent = process.HandleTable.GetObject(handle); - KernelResult result; + Result result; if (writableEvent != null) { writableEvent.Signal(); - result = KernelResult.Success; + result = Result.Success; } else { @@ -1764,9 +1785,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x12)] - public KernelResult ClearEvent(int handle) + public Result ClearEvent(int handle) { - KernelResult result; + Result result; KProcess process = KernelStatic.GetCurrentProcess(); @@ -1787,21 +1808,21 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x16)] - public KernelResult CloseHandle(int handle) + public Result CloseHandle(int handle) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); - return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle; + return currentProcess.HandleTable.CloseHandle(handle) ? Result.Success : KernelResult.InvalidHandle; } [Svc(0x17)] - public KernelResult ResetSignal(int handle) + public Result ResetSignal(int handle) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); KReadableEvent readableEvent = currentProcess.HandleTable.GetObject(handle); - KernelResult result; + Result result; if (readableEvent != null) { @@ -1868,7 +1889,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x29)] - public KernelResult GetInfo(out ulong value, InfoType id, int handle, long subId) + public Result GetInfo(out ulong value, InfoType id, int handle, long subId) { value = 0; @@ -2010,9 +2031,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KHandleTable handleTable = currentProcess.HandleTable; KResourceLimit resourceLimit = currentProcess.ResourceLimit; - KernelResult result = handleTable.GenerateHandle(resourceLimit, out int resLimHandle); + Result result = handleTable.GenerateHandle(resourceLimit, out int resLimHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -2081,7 +2102,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall if (subId != -1 && subId != currentCore) { - return KernelResult.Success; + return Result.Success; } KScheduler scheduler = _context.Schedulers[currentCore]; @@ -2122,12 +2143,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess currentProcess = KernelStatic.GetCurrentProcess(); KHandleTable handleTable = currentProcess.HandleTable; - KernelResult result = handleTable.GenerateHandle(currentProcess, out int outHandle); + Result result = handleTable.GenerateHandle(currentProcess, out int outHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; - } + } value = (ulong)outHandle; @@ -2137,23 +2158,23 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall default: return KernelResult.InvalidEnumValue; } - return KernelResult.Success; + return Result.Success; } [Svc(0x45)] - public KernelResult CreateEvent(out int wEventHandle, out int rEventHandle) + public Result CreateEvent(out int wEventHandle, out int rEventHandle) { KEvent Event = new KEvent(_context); KProcess process = KernelStatic.GetCurrentProcess(); - KernelResult result = process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle); + Result result = process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle); - if (result == KernelResult.Success) + if (result == Result.Success) { result = process.HandleTable.GenerateHandle(Event.ReadableEvent, out rEventHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { process.HandleTable.CloseHandle(wEventHandle); } @@ -2167,7 +2188,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x65)] - public KernelResult GetProcessList(out int count, [PointerSized] ulong address, int maxCount) + public Result GetProcessList(out int count, [PointerSized] ulong address, int maxCount) { count = 0; @@ -2213,11 +2234,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall count = copyCount; - return KernelResult.Success; + return Result.Success; } [Svc(0x6f)] - public KernelResult GetSystemInfo(out long value, uint id, int handle, long subId) + public Result GetSystemInfo(out long value, uint id, int handle, long subId) { value = 0; @@ -2270,11 +2291,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } } - return KernelResult.Success; + return Result.Success; } [Svc(0x30)] - public KernelResult GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource) + public Result GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource) { limitValue = 0; @@ -2292,11 +2313,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall limitValue = resourceLimit.GetLimitValue(resource); - return KernelResult.Success; + return Result.Success; } [Svc(0x31)] - public KernelResult GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource) + public Result GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource) { limitValue = 0; @@ -2314,11 +2335,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall limitValue = resourceLimit.GetCurrentValue(resource); - return KernelResult.Success; + return Result.Success; } [Svc(0x37)] - public KernelResult GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource) + public Result GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource) { peak = 0; @@ -2336,11 +2357,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall peak = resourceLimit.GetPeakValue(resource); - return KernelResult.Success; + return Result.Success; } [Svc(0x7d)] - public KernelResult CreateResourceLimit(out int handle) + public Result CreateResourceLimit(out int handle) { KResourceLimit limit = new KResourceLimit(_context); @@ -2350,7 +2371,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x7e)] - public KernelResult SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue) + public Result SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue) { if (resource >= LimitableResource.Count) { @@ -2370,7 +2391,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall // Thread [Svc(8)] - public KernelResult CreateThread( + public Result CreateThread( out int handle, [PointerSized] ulong entrypoint, [PointerSized] ulong argsPtr, @@ -2381,7 +2402,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore, null); } - public KernelResult CreateThread( + public Result CreateThread( out int handle, ulong entrypoint, ulong argsPtr, @@ -2419,7 +2440,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KThread thread = new KThread(_context); - KernelResult result = currentProcess.InitializeThread( + Result result = currentProcess.InitializeThread( thread, entrypoint, argsPtr, @@ -2428,7 +2449,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall cpuCore, customThreadStart); - if (result == KernelResult.Success) + if (result == Result.Success) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2445,7 +2466,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(9)] - public KernelResult StartThread(int handle) + public Result StartThread(int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2455,9 +2476,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { thread.IncrementReferenceCount(); - KernelResult result = thread.Start(); + Result result = thread.Start(); - if (result == KernelResult.Success) + if (result == Result.Success) { thread.IncrementReferenceCount(); } @@ -2499,7 +2520,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0xc)] - public KernelResult GetThreadPriority(out int priority, int handle) + public Result GetThreadPriority(out int priority, int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2509,7 +2530,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { priority = thread.DynamicPriority; - return KernelResult.Success; + return Result.Success; } else { @@ -2520,7 +2541,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0xd)] - public KernelResult SetThreadPriority(int handle, int priority) + public Result SetThreadPriority(int handle, int priority) { // TODO: NPDM check. @@ -2535,11 +2556,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall thread.SetPriority(priority); - return KernelResult.Success; + return Result.Success; } [Svc(0xe)] - public KernelResult GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle) + public Result GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2550,7 +2571,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall preferredCore = thread.PreferredCore; affinityMask = thread.AffinityMask; - return KernelResult.Success; + return Result.Success; } else { @@ -2562,7 +2583,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0xf)] - public KernelResult SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask) + public Result SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -2617,7 +2638,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x25)] - public KernelResult GetThreadId(out ulong threadUid, int handle) + public Result GetThreadId(out ulong threadUid, int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2627,7 +2648,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { threadUid = thread.ThreadUid; - return KernelResult.Success; + return Result.Success; } else { @@ -2638,7 +2659,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x32)] - public KernelResult SetThreadActivity(int handle, bool pause) + public Result SetThreadActivity(int handle, bool pause) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2663,7 +2684,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x33)] - public KernelResult GetThreadContext3([PointerSized] ulong address, int handle) + public Result GetThreadContext3([PointerSized] ulong address, int handle) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); KThread currentThread = KernelStatic.GetCurrentThread(); @@ -2685,12 +2706,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidThread; } - KernelResult result = thread.GetThreadContext3(out ThreadContext context); + Result result = thread.GetThreadContext3(out ThreadContext context); - if (result == KernelResult.Success) + if (result == Result.Success) { return KernelTransfer.KernelToUser(address, context) - ? KernelResult.Success + ? Result.Success : KernelResult.InvalidMemState; } @@ -2700,7 +2721,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall // Thread synchronization [Svc(0x18)] - public KernelResult WaitSynchronization(out int handleIndex, [PointerSized] ulong handlesPtr, int handlesCount, long timeout) + public Result WaitSynchronization(out int handleIndex, [PointerSized] ulong handlesPtr, int handlesCount, long timeout) { handleIndex = 0; @@ -2711,8 +2732,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KThread currentThread = KernelStatic.GetCurrentThread(); - var syncObjs = new Span(currentThread.WaitSyncObjects).Slice(0, handlesCount); - if (handlesCount != 0) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -2741,9 +2760,32 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } + return WaitSynchronization(out handleIndex, handles, timeout); + } + + return WaitSynchronization(out handleIndex, ReadOnlySpan.Empty, timeout); + } + + public Result WaitSynchronization(out int handleIndex, ReadOnlySpan handles, long timeout) + { + handleIndex = 0; + + if ((uint)handles.Length > KThread.MaxWaitSyncObjects) + { + return KernelResult.MaximumExceeded; + } + + KThread currentThread = KernelStatic.GetCurrentThread(); + + var syncObjs = new Span(currentThread.WaitSyncObjects).Slice(0, handles.Length); + + if (handles.Length != 0) + { + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + int processedHandles = 0; - for (; processedHandles < handlesCount; processedHandles++) + for (; processedHandles < handles.Length; processedHandles++) { KSynchronizationObject syncObj = currentProcess.HandleTable.GetObject(handles[processedHandles]); @@ -2757,7 +2799,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall syncObj.IncrementReferenceCount(); } - if (processedHandles != handlesCount) + if (processedHandles != handles.Length) { // One or more handles are invalid. for (int index = 0; index < processedHandles; index++) @@ -2774,14 +2816,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall timeout += KTimeManager.DefaultTimeIncrementNanoseconds; } - KernelResult result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex); + Result result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex); if (result == KernelResult.PortRemoteClosed) { - result = KernelResult.Success; + result = Result.Success; } - for (int index = 0; index < handlesCount; index++) + for (int index = 0; index < handles.Length; index++) { currentThread.WaitSyncObjects[index].DecrementReferenceCount(); } @@ -2790,7 +2832,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x19)] - public KernelResult CancelSynchronization(int handle) + public Result CancelSynchronization(int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2803,11 +2845,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall thread.CancelSynchronization(); - return KernelResult.Success; + return Result.Success; } [Svc(0x1a)] - public KernelResult ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle) + public Result ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle) { if (IsPointingInsideKernel(mutexAddress)) { @@ -2825,7 +2867,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x1b)] - public KernelResult ArbitrateUnlock([PointerSized] ulong mutexAddress) + public Result ArbitrateUnlock([PointerSized] ulong mutexAddress) { if (IsPointingInsideKernel(mutexAddress)) { @@ -2843,7 +2885,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x1c)] - public KernelResult WaitProcessWideKeyAtomic( + public Result WaitProcessWideKeyAtomic( [PointerSized] ulong mutexAddress, [PointerSized] ulong condVarAddress, int handle, @@ -2874,17 +2916,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x1d)] - public KernelResult SignalProcessWideKey([PointerSized] ulong address, int count) + public Result SignalProcessWideKey([PointerSized] ulong address, int count) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); currentProcess.AddressArbiter.SignalProcessWideKey(address, count); - return KernelResult.Success; + return Result.Success; } [Svc(0x34)] - public KernelResult WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout) + public Result WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout) { if (IsPointingInsideKernel(address)) { @@ -2916,7 +2958,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x35)] - public KernelResult SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count) + public Result SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count) { if (IsPointingInsideKernel(address)) { @@ -2943,11 +2985,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x36)] - public KernelResult SynchronizePreemptionState() + public Result SynchronizePreemptionState() { KernelStatic.GetCurrentThread().SynchronizePreemptionState(); - return KernelResult.Success; + return Result.Success; } private static bool IsPointingInsideKernel(ulong address) diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index f53b43b3c..a5f9df5ef 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; using System.Linq; @@ -24,14 +25,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _arbiterThreads = new List(); } - public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle) + public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle) { KThread currentThread = KernelStatic.GetCurrentThread(); _context.CriticalSection.Enter(); currentThread.SignaledObj = null; - currentThread.ObjSyncResult = KernelResult.Success; + currentThread.ObjSyncResult = Result.Success; KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -46,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { _context.CriticalSection.Leave(); - return 0; + return Result.Success; } KThread mutexOwner = currentProcess.HandleTable.GetObject(ownerHandle); @@ -78,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return currentThread.ObjSyncResult; } - public KernelResult ArbitrateUnlock(ulong mutexAddress) + public Result ArbitrateUnlock(ulong mutexAddress) { _context.CriticalSection.Enter(); @@ -86,14 +87,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading (int mutexValue, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress); - KernelResult result = KernelResult.Success; + Result result = Result.Success; if (!KernelTransfer.KernelToUser(mutexAddress, mutexValue)) { result = KernelResult.InvalidMemState; } - if (result != KernelResult.Success && newOwnerThread != null) + if (result != Result.Success && newOwnerThread != null) { newOwnerThread.SignaledObj = null; newOwnerThread.ObjSyncResult = result; @@ -104,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return result; } - public KernelResult WaitProcessWideKeyAtomic(ulong mutexAddress, ulong condVarAddress, int threadHandle, long timeout) + public Result WaitProcessWideKeyAtomic(ulong mutexAddress, ulong condVarAddress, int threadHandle, long timeout) { _context.CriticalSection.Enter(); @@ -185,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } newOwnerThread.SignaledObj = null; - newOwnerThread.ObjSyncResult = KernelResult.Success; + newOwnerThread.ObjSyncResult = Result.Success; newOwnerThread.ReleaseAndResume(); } @@ -247,7 +248,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { // We now own the mutex. requester.SignaledObj = null; - requester.ObjSyncResult = KernelResult.Success; + requester.ObjSyncResult = Result.Success; requester.ReleaseAndResume(); @@ -273,7 +274,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - public KernelResult WaitForAddressIfEqual(ulong address, int value, long timeout) + public Result WaitForAddressIfEqual(ulong address, int value, long timeout) { KThread currentThread = KernelStatic.GetCurrentThread(); @@ -344,7 +345,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.InvalidState; } - public KernelResult WaitForAddressIfLessThan(ulong address, int value, bool shouldDecrement, long timeout) + public Result WaitForAddressIfLessThan(ulong address, int value, bool shouldDecrement, long timeout) { KThread currentThread = KernelStatic.GetCurrentThread(); @@ -422,7 +423,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.InvalidState; } - public KernelResult Signal(ulong address, int count) + public Result Signal(ulong address, int count) { _context.CriticalSection.Enter(); @@ -430,10 +431,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Leave(); - return KernelResult.Success; + return Result.Success; } - public KernelResult SignalAndIncrementIfEqual(ulong address, int value, int count) + public Result SignalAndIncrementIfEqual(ulong address, int value, int count) { _context.CriticalSection.Enter(); @@ -467,10 +468,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Leave(); - return KernelResult.Success; + return Result.Success; } - public KernelResult SignalAndModifyIfEqual(ulong address, int value, int count) + public Result SignalAndModifyIfEqual(ulong address, int value, int count) { _context.CriticalSection.Enter(); @@ -539,7 +540,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Leave(); - return KernelResult.Success; + return Result.Success; } private void WakeArbiterThreads(ulong address, int count) @@ -547,7 +548,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading static void RemoveArbiterThread(KThread thread) { thread.SignaledObj = null; - thread.ObjSyncResult = KernelResult.Success; + thread.ObjSyncResult = Result.Success; thread.ReleaseAndResume(); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs index d378b81e3..d9e7befa6 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Kernel.Threading { @@ -27,16 +28,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KernelContext.CriticalSection.Leave(); } - public KernelResult Clear() + public Result Clear() { _signaled = false; - return KernelResult.Success; + return Result.Success; } - public KernelResult ClearIfSignaled() + public Result ClearIfSignaled() { - KernelResult result; + Result result; KernelContext.CriticalSection.Enter(); @@ -44,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { _signaled = false; - result = KernelResult.Success; + result = Result.Success; } else { diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs index 419f15368..01b65f55e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; @@ -13,11 +14,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context = context; } - public KernelResult WaitFor(Span syncObjs, long timeout, out int handleIndex) + public Result WaitFor(Span syncObjs, long timeout, out int handleIndex) { handleIndex = 0; - KernelResult result = KernelResult.TimedOut; + Result result = KernelResult.TimedOut; _context.CriticalSection.Enter(); @@ -33,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Leave(); - return KernelResult.Success; + return Result.Success; } if (timeout == 0) @@ -122,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) { thread.SignaledObj = syncObj; - thread.ObjSyncResult = KernelResult.Success; + thread.ObjSyncResult = Result.Success; thread.Reschedule(ThreadSchedState.Running); } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index b9dd91ef8..6fd496058 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -3,6 +3,7 @@ using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.SupervisorCall; +using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; using System.Numerics; @@ -79,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private ThreadSchedState _forcePauseFlags; private ThreadSchedState _forcePausePermissionFlags; - public KernelResult ObjSyncResult { get; set; } + public Result ObjSyncResult { get; set; } public int BasePriority { get; set; } public int PreferredCore { get; set; } @@ -130,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _activityOperationLock = new object(); } - public KernelResult Initialize( + public Result Initialize( ulong entrypoint, ulong argsPtr, ulong stackTop, @@ -145,8 +146,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading throw new ArgumentException($"Invalid thread type \"{type}\"."); } - ThreadContext = new KThreadContext(); - PreferredCore = cpuCore; AffinityMask |= 1UL << cpuCore; @@ -166,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (type == ThreadType.User) { - if (owner.AllocateThreadLocalStorage(out _tlsAddress) != KernelResult.Success) + if (owner.AllocateThreadLocalStorage(out _tlsAddress) != Result.Success) { return KernelResult.OutOfMemory; } @@ -194,6 +193,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext(); + ThreadContext = new KThreadContext(Context); + Context.IsAarch32 = !is64Bits; Context.SetX(0, argsPtr); @@ -230,7 +231,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KernelContext.CriticalSection.Leave(); - return KernelResult.Success; + return Result.Success; } _forcePauseFlags |= ThreadSchedState.ProcessPauseFlag; @@ -241,10 +242,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - return KernelResult.Success; + return Result.Success; } - public KernelResult Start() + public Result Start() { if (!KernelContext.KernelInitialized) { @@ -260,7 +261,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KernelContext.CriticalSection.Leave(); } - KernelResult result = KernelResult.ThreadTerminating; + Result result = KernelResult.ThreadTerminating; KernelContext.CriticalSection.Enter(); @@ -287,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading StartHostThread(); - result = KernelResult.Success; + result = Result.Success; break; } else @@ -465,7 +466,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return -1; } - public KernelResult Sleep(long timeout) + public Result Sleep(long timeout) { KernelContext.CriticalSection.Enter(); @@ -490,7 +491,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KernelContext.TimeManager.UnscheduleFutureInvocation(this); } - return 0; + return Result.Success; } public void SetPriority(int priority) @@ -534,11 +535,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - public KernelResult SetActivity(bool pause) + public Result SetActivity(bool pause) { lock (_activityOperationLock) { - KernelResult result = KernelResult.Success; + Result result = Result.Success; KernelContext.CriticalSection.Enter(); @@ -581,7 +582,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KernelContext.CriticalSection.Leave(); - if (result == KernelResult.Success && pause) + if (result == Result.Success && pause) { bool isThreadRunning = true; @@ -628,7 +629,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - public KernelResult GetThreadContext3(out ThreadContext context) + public Result GetThreadContext3(out ThreadContext context) { context = default; @@ -651,7 +652,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KernelContext.CriticalSection.Leave(); } - return KernelResult.Success; + return Result.Success; } private static uint GetPsr(IExecutionContext context) @@ -739,7 +740,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KernelContext.CriticalSection.Leave(); } - public KernelResult SetCoreAndAffinityMask(int newCore, ulong newAffinityMask) + public Result SetCoreAndAffinityMask(int newCore, ulong newAffinityMask) { lock (_activityOperationLock) { @@ -838,7 +839,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KernelContext.CriticalSection.Leave(); } - return KernelResult.Success; + return Result.Success; } } @@ -1259,6 +1260,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (_customThreadStart != null) { _customThreadStart(); + + // Ensure that anything trying to join the HLE thread is unblocked. + Exit(); + HandlePostSyscall(); } else { @@ -1304,7 +1309,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { Owner?.RemoveThread(this); - if (_tlsAddress != 0 && Owner.FreeThreadLocalStorage(_tlsAddress) != KernelResult.Success) + if (_tlsAddress != 0 && Owner.FreeThreadLocalStorage(_tlsAddress) != Result.Success) { throw new InvalidOperationException("Unexpected failure freeing thread local storage."); } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs index a7e9c4b3a..e8ad53c28 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs @@ -1,11 +1,25 @@ -using System.Threading; +using Ryujinx.Cpu; +using Ryujinx.Horizon.Common; +using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.Threading { - class KThreadContext + class KThreadContext : IThreadContext { + private readonly IExecutionContext _context; + + public bool Running => _context.Running; + public ulong TlsAddress => (ulong)_context.TpidrroEl0; + + public ulong GetX(int index) => _context.GetX(index); + private int _locked; + public KThreadContext(IExecutionContext context) + { + _context = context; + } + public bool Lock() { return Interlocked.Exchange(ref _locked, 1) == 0; diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs index 7aee0b57c..b46122be7 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Kernel.Threading { @@ -16,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _parent.ReadableEvent.Signal(); } - public KernelResult Clear() + public Result Clear() { return _parent.ReadableEvent.Clear(); } diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index beeb5ad60..b422fef75 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.Horizon.Common; using System; using System.Linq; using System.Runtime.InteropServices; @@ -90,9 +91,9 @@ namespace Ryujinx.HLE.HOS KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion]; - KernelResult result = region.AllocatePages(out KPageList pageList, (ulong)codePagesCount); + Result result = region.AllocatePages(out KPageList pageList, (ulong)codePagesCount); - if (result != KernelResult.Success) + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -111,7 +112,7 @@ namespace Ryujinx.HLE.HOS memoryRegion, processContextFactory); - if (result != KernelResult.Success) + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -120,7 +121,7 @@ namespace Ryujinx.HLE.HOS result = LoadIntoMemory(process, kip, codeBaseAddress); - if (result != KernelResult.Success) + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -131,7 +132,7 @@ namespace Ryujinx.HLE.HOS result = process.Start(kip.Priority, (ulong)kip.StackSize); - if (result != KernelResult.Success) + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); @@ -230,19 +231,35 @@ namespace Ryujinx.HLE.HOS context.Device.System.LibHacHorizonManager.InitializeApplicationClient(new ProgramId(programInfo.ProgramId), in npdm); - KernelResult result; + Result result; KResourceLimit resourceLimit = new KResourceLimit(context); long applicationRgSize = (long)context.MemoryManager.MemoryRegions[(int)MemoryRegion.Application].Size; - result = resourceLimit.SetLimitValue(LimitableResource.Memory, applicationRgSize); - result |= resourceLimit.SetLimitValue(LimitableResource.Thread, 608); - result |= resourceLimit.SetLimitValue(LimitableResource.Event, 700); - result |= resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128); - result |= resourceLimit.SetLimitValue(LimitableResource.Session, 894); + result = resourceLimit.SetLimitValue(LimitableResource.Memory, applicationRgSize); - if (result != KernelResult.Success) + if (result.IsSuccess) + { + result = resourceLimit.SetLimitValue(LimitableResource.Thread, 608); + } + + if (result.IsSuccess) + { + result = resourceLimit.SetLimitValue(LimitableResource.Event, 700); + } + + if (result.IsSuccess) + { + result = resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128); + } + + if (result.IsSuccess) + { + result = resourceLimit.SetLimitValue(LimitableResource.Session, 894); + } + + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values."); @@ -273,7 +290,7 @@ namespace Ryujinx.HLE.HOS memoryRegion, processContextFactory); - if (result != KernelResult.Success) + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -288,7 +305,7 @@ namespace Ryujinx.HLE.HOS result = LoadIntoMemory(process, executables[index], nsoBase[index]); - if (result != KernelResult.Success) + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -302,7 +319,7 @@ namespace Ryujinx.HLE.HOS result = process.Start(meta.MainThreadPriority, meta.MainThreadStackSize); - if (result != KernelResult.Success) + if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); @@ -322,18 +339,18 @@ namespace Ryujinx.HLE.HOS return true; } - private static KernelResult LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) + private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) { - ulong textStart = baseAddress + (ulong)image.TextOffset; - ulong roStart = baseAddress + (ulong)image.RoOffset; - ulong dataStart = baseAddress + (ulong)image.DataOffset; - ulong bssStart = baseAddress + (ulong)image.BssOffset; + ulong textStart = baseAddress + image.TextOffset; + ulong roStart = baseAddress + image.RoOffset; + ulong dataStart = baseAddress + image.DataOffset; + ulong bssStart = baseAddress + image.BssOffset; ulong end = dataStart + (ulong)image.Data.Length; if (image.BssSize != 0) { - end = bssStart + (ulong)image.BssSize; + end = bssStart + image.BssSize; } process.CpuMemory.Write(textStart, image.Text); @@ -342,11 +359,11 @@ namespace Ryujinx.HLE.HOS process.CpuMemory.Fill(bssStart, image.BssSize, 0); - KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission) + Result SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission) { if (size == 0) { - return KernelResult.Success; + return Result.Success; } size = BitUtils.AlignUp(size, KPageTableBase.PageSize); @@ -354,16 +371,16 @@ namespace Ryujinx.HLE.HOS return process.MemoryManager.SetProcessMemoryPermission(address, size, permission); } - KernelResult result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, KMemoryPermission.ReadAndExecute); + Result result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, KMemoryPermission.ReadAndExecute); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, KMemoryPermission.Read); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs index b49a44e7d..9a12e7018 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Account.Acc @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // GetSystemEvent() -> handle public ResultCode GetSystemEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(AsyncExecution.SystemEvent.ReadableEvent, out int _systemEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(AsyncExecution.SystemEvent.ReadableEvent, out int _systemEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 405806c42..134566d96 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -2,8 +2,8 @@ using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator @@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { if (_stateChangedEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out _stateChangedEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out _stateChangedEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -178,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { if (_normalOutDataEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_normalOutDataEvent.ReadableEvent, out _normalOutDataEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_normalOutDataEvent.ReadableEvent, out _normalOutDataEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -195,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { if (_interactiveOutDataEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_interactiveOutDataEvent.ReadableEvent, out _interactiveOutDataEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_interactiveOutDataEvent.ReadableEvent, out _interactiveOutDataEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index cb298fd47..b145a65d1 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -1,10 +1,10 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Settings.Types; using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService; using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys if (_messageEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(messageEvent.ReadableEvent, out _messageEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(messageEvent.ReadableEvent, out _messageEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -211,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // NOTE: Original service calls IOperationModeManager::GetDefaultDisplayResolutionChangeEvent of omm service. if (_displayResolutionChangedEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(context.Device.System.DisplayResolutionChangeEvent.ReadableEvent, out _displayResolutionChangedEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(context.Device.System.DisplayResolutionChangeEvent.ReadableEvent, out _displayResolutionChangedEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs index d7816de94..7c03fc278 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.BufferAlreadyAcquired; } - if (context.Process.HandleTable.GenerateHandle(_transferMem, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_transferMem, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.BufferAlreadyAcquired; } - if (context.Process.HandleTable.GenerateHandle(_transferMem, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_transferMem, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs index 5c53c66f8..2a9848ddc 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { if (_channelEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_channelEvent.ReadableEvent, out _channelEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_channelEvent.ReadableEvent, out _channelEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 567bc2645..39be75776 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -1,8 +1,8 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy.Types; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys if (_libraryAppletLaunchableEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_libraryAppletLaunchableEvent.ReadableEvent, out _libraryAppletLaunchableEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_libraryAppletLaunchableEvent.ReadableEvent, out _libraryAppletLaunchableEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -378,7 +378,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _accumulatedSuspendedTickChangedEvent.ReadableEvent.Signal(); - if (context.Process.HandleTable.GenerateHandle(_accumulatedSuspendedTickChangedEvent.ReadableEvent, out _accumulatedSuspendedTickChangedEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_accumulatedSuspendedTickChangedEvent.ReadableEvent, out _accumulatedSuspendedTickChangedEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index 74068ad66..49331e216 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -1,4 +1,3 @@ -using LibHac; using LibHac.Account; using LibHac.Common; using LibHac.Fs; @@ -9,13 +8,13 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService; using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.Horizon.Common; using System; using System.Numerics; using System.Threading; @@ -43,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati private int _jitLoaded; - private HorizonClient _horizon; + private LibHac.HorizonClient _horizon; public IApplicationFunctions(Horizon system) { @@ -136,8 +135,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } - HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient; - Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in control, in userId); + LibHac.HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient; + LibHac.Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in control, in userId); context.ResponseData.Write(requiredSize); @@ -185,7 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // SetTerminateResult(u32) public ResultCode SetTerminateResult(ServiceCtx context) { - Result result = new Result(context.RequestData.ReadUInt32()); + LibHac.Result result = new LibHac.Result(context.RequestData.ReadUInt32()); Logger.Info?.Print(LogClass.ServiceAm, $"Result = 0x{result.Value:x8} ({result.ToStringWithName()})."); @@ -256,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati BlitStruct controlHolder = context.Device.Application.ControlData; - Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize, + LibHac.Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize, out CacheStorageTargetMedia storageTarget, applicationId, in controlHolder.Value, index, saveSize, journalSize); @@ -584,7 +583,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati { if (_gpuErrorDetectedSystemEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_gpuErrorDetectedSystemEvent.ReadableEvent, out _gpuErrorDetectedSystemEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_gpuErrorDetectedSystemEvent.ReadableEvent, out _gpuErrorDetectedSystemEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -605,7 +604,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati { if (_friendInvitationStorageChannelEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_friendInvitationStorageChannelEvent.ReadableEvent, out _friendInvitationStorageChannelEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_friendInvitationStorageChannelEvent.ReadableEvent, out _friendInvitationStorageChannelEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -636,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati { if (_notificationStorageChannelEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_notificationStorageChannelEvent.ReadableEvent, out _notificationStorageChannelEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_notificationStorageChannelEvent.ReadableEvent, out _notificationStorageChannelEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -653,7 +652,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati { if (_healthWarningDisappearedSystemEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_healthWarningDisappearedSystemEvent.ReadableEvent, out _healthWarningDisappearedSystemEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_healthWarningDisappearedSystemEvent.ReadableEvent, out _healthWarningDisappearedSystemEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs index f9a9447f4..4911b7f00 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs @@ -1,8 +1,8 @@ using Ryujinx.Audio.Common; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using Ryujinx.Memory; using System; using System.Runtime.InteropServices; @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn { KEvent bufferEvent = _impl.RegisterBufferEvent(); - if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs index aff08811c..2d6908e3f 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs @@ -1,8 +1,8 @@ using Ryujinx.Audio.Common; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using Ryujinx.Memory; using System; using System.Runtime.InteropServices; @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut { KEvent bufferEvent = _impl.RegisterBufferEvent(); - if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs index 1ef97ecce..e868ad5af 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs @@ -1,8 +1,8 @@ using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; using System.Text; @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { KEvent deviceSystemEvent = _impl.QueryAudioDeviceSystemEvent(); - if (context.Process.HandleTable.GenerateHandle(deviceSystemEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(deviceSystemEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -230,7 +230,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { KEvent deviceInputEvent = _impl.QueryAudioDeviceInputEvent(); - if (context.Process.HandleTable.GenerateHandle(deviceInputEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(deviceInputEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -248,7 +248,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { KEvent deviceOutputEvent = _impl.QueryAudioDeviceOutputEvent(); - if (context.Process.HandleTable.GenerateHandle(deviceOutputEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(deviceOutputEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index b2ddb697a..3843b408e 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; using System.Buffers; @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer if (result == ResultCode.Success) { - if (context.Process.HandleTable.GenerateHandle(systemEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(systemEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs index 3e516d83e..b176195db 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs @@ -1,9 +1,9 @@ using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator { if (_eventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs b/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs index 5ce434956..65535ea10 100644 --- a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs +++ b/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs @@ -1,8 +1,8 @@ using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Bluetooth.BluetoothDriver; using Ryujinx.HLE.HOS.Services.Settings; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Bluetooth @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth { BluetoothEventManager.InitializeBleDebugEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.InitializeBleDebugEvent.ReadableEvent, out BluetoothEventManager.InitializeBleDebugEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.InitializeBleDebugEvent.ReadableEvent, out BluetoothEventManager.InitializeBleDebugEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth { BluetoothEventManager.UnknownBleDebugEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.UnknownBleDebugEvent.ReadableEvent, out BluetoothEventManager.UnknownBleDebugEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.UnknownBleDebugEvent.ReadableEvent, out BluetoothEventManager.UnknownBleDebugEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth { BluetoothEventManager.RegisterBleDebugEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.RegisterBleDebugEvent.ReadableEvent, out BluetoothEventManager.RegisterBleDebugEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.RegisterBleDebugEvent.ReadableEvent, out BluetoothEventManager.RegisterBleDebugEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth { BluetoothEventManager.InitializeBleEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.InitializeBleEvent.ReadableEvent, out BluetoothEventManager.InitializeBleEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.InitializeBleEvent.ReadableEvent, out BluetoothEventManager.InitializeBleEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -76,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth { BluetoothEventManager.UnknownBleEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.UnknownBleEvent.ReadableEvent, out BluetoothEventManager.UnknownBleEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.UnknownBleEvent.ReadableEvent, out BluetoothEventManager.UnknownBleEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth { BluetoothEventManager.RegisterBleEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.RegisterBleEvent.ReadableEvent, out BluetoothEventManager.RegisterBleEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(BluetoothEventManager.RegisterBleEvent.ReadableEvent, out BluetoothEventManager.RegisterBleEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs b/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs index 8f1386523..026b5bf12 100644 --- a/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs +++ b/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser { @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser // AcquireBleScanEvent() -> (byte<1>, handle) public ResultCode AcquireBleScanEvent(ServiceCtx context) { - KernelResult result = KernelResult.Success; + Result result = Result.Success; if (_bleScanEventHandle == 0) { @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser result = context.Process.HandleTable.GenerateHandle(_bleScanEvent.ReadableEvent, out _bleScanEventHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { // NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not. Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!"); @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_bleScanEventHandle); - context.ResponseData.Write(result == KernelResult.Success ? 1 : 0); + context.ResponseData.Write(result == Result.Success ? 1 : 0); return ResultCode.Success; } @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser // AcquireBleConnectionEvent() -> (byte<1>, handle) public ResultCode AcquireBleConnectionEvent(ServiceCtx context) { - KernelResult result = KernelResult.Success; + Result result = Result.Success; if (_bleConnectionEventHandle == 0) { @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser result = context.Process.HandleTable.GenerateHandle(_bleConnectionEvent.ReadableEvent, out _bleConnectionEventHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { // NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not. Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!"); @@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_bleConnectionEventHandle); - context.ResponseData.Write(result == KernelResult.Success ? 1 : 0); + context.ResponseData.Write(result == Result.Success ? 1 : 0); return ResultCode.Success; } @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser // AcquireBleServiceDiscoveryEvent() -> (byte<1>, handle) public ResultCode AcquireBleServiceDiscoveryEvent(ServiceCtx context) { - KernelResult result = KernelResult.Success; + Result result = Result.Success; if (_bleServiceDiscoveryEventHandle == 0) { @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser result = context.Process.HandleTable.GenerateHandle(_bleServiceDiscoveryEvent.ReadableEvent, out _bleServiceDiscoveryEventHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { // NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not. Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!"); @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_bleServiceDiscoveryEventHandle); - context.ResponseData.Write(result == KernelResult.Success ? 1 : 0); + context.ResponseData.Write(result == Result.Success ? 1 : 0); return ResultCode.Success; } @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser // AcquireBleMtuConfigEvent() -> (byte<1>, handle) public ResultCode AcquireBleMtuConfigEvent(ServiceCtx context) { - KernelResult result = KernelResult.Success; + Result result = Result.Success; if (_bleMtuConfigEventHandle == 0) { @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser result = context.Process.HandleTable.GenerateHandle(_bleMtuConfigEvent.ReadableEvent, out _bleMtuConfigEventHandle); - if (result != KernelResult.Success) + if (result != Result.Success) { // NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not. Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!"); @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_bleMtuConfigEventHandle); - context.ResponseData.Write(result == KernelResult.Success ? 1 : 0); + context.ResponseData.Write(result == Result.Success ? 1 : 0); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 8159d0918..17a33b799 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -4,12 +4,11 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService; +using Ryujinx.Horizon.Common; using System; -using System.IO; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator @@ -33,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator _completionEvent = new KEvent(context.Device.System.KernelContext); } - if (context.Process.HandleTable.GenerateHandle(_completionEvent.ReadableEvent, out int completionEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_completionEvent.ReadableEvent, out int completionEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs index f5614dddc..65cbd38e7 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs @@ -1,9 +1,9 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService; +using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator { if (_notificationEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_notificationEvent.ReadableEvent, out _notificationEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_notificationEvent.ReadableEvent, out _notificationEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs index b38b25c33..29ee1706c 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Hid.HidServer @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer { if (_hidSharedMemHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_hidSharedMem, out _hidSharedMemHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_hidSharedMem, out _hidSharedMemHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index 957cd5530..d347a3bde 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -1,11 +1,11 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Hid.HidServer; using Ryujinx.HLE.HOS.Services.Hid.Types; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad; +using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // TODO: signal event at right place _xpadIdEvent.ReadableEvent.Signal(); - + _vibrationPermitted = true; } @@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long xpadId = context.RequestData.ReadInt64(); - if (context.Process.HandleTable.GenerateHandle(_xpadIdEvent.ReadableEvent, out _xpadIdEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_xpadIdEvent.ReadableEvent, out _xpadIdEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -761,7 +761,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid long npadStyleSet = context.RequestData.ReadInt64(); KEvent evnt = context.Device.Hid.Npads.GetStyleSetUpdateEvent(npadId); - if (context.Process.HandleTable.GenerateHandle(evnt.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(evnt.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -1597,7 +1597,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int palmaConnectionHandle = context.RequestData.ReadInt32(); - if (context.Process.HandleTable.GenerateHandle(_palmaOperationCompleteEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_palmaOperationCompleteEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs index a0bd63752..7af06431a 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs @@ -1,9 +1,9 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.Hid.HidServer; using Ryujinx.HLE.HOS.Services.Hid.Irs.Types; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Hid.Irs @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs if (_irsensorSharedMemoryHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(context.Device.System.IirsSharedMem, out _irsensorSharedMemoryHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(context.Device.System.IirsSharedMem, out _irsensorSharedMemoryHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -172,8 +172,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs { NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32(); - if (npadIdType > NpadIdType.Player8 && - npadIdType != NpadIdType.Unknown && + if (npadIdType > NpadIdType.Player8 && + npadIdType != NpadIdType.Unknown && npadIdType != NpadIdType.Handheld) { return ResultCode.NpadIdOutOfRange; @@ -183,7 +183,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs context.ResponseData.Write((int)irCameraHandle); - // NOTE: If the irCameraHandle pointer is null this error is returned, Doesn't occur in our case. + // NOTE: If the irCameraHandle pointer is null this error is returned, Doesn't occur in our case. // return ResultCode.HandlePointerIsNull; return ResultCode.Success; diff --git a/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index dd3dad59a..0c223c067 100644 --- a/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.Ldn.Types; +using Ryujinx.Horizon.Common; using System; using System.Net; @@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator { if (_stateChangeEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_networkInterface.StateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_networkInterface.StateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs index f4ad03661..a7f2dbb85 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs @@ -2,11 +2,11 @@ using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Hid.HidServer; using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; +using Ryujinx.Horizon.Common; using System; using System.Buffers.Binary; using System.Globalization; @@ -851,7 +851,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { context.Device.System.NfpDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -877,7 +877,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { context.Device.System.NfpDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -960,7 +960,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { _availabilityChangeEvent = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs index 7a3fdabd0..88757bee7 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { if (_event0Handle == 0) { - if (context.Process.HandleTable.GenerateHandle(_event0.ReadableEvent, out _event0Handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_event0.ReadableEvent, out _event0Handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService if (_event1Handle == 0) { - if (context.Process.HandleTable.GenerateHandle(_event1.ReadableEvent, out _event1Handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_event1.ReadableEvent, out _event1Handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs index 919639b63..d6843d12e 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs @@ -1,8 +1,8 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceAccessServer.ShopServiceAccessor; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceAccessServer @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServ if (_eventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs b/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs index fb31bd1f0..3b533f0ff 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs @@ -1,12 +1,12 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService { - class IEnsureNetworkClockAvailabilityService : IpcService + class IEnsureNetworkClockAvailabilityService : IpcService { private KEvent _finishNotificationEvent; private ResultCode _taskResultCode; @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService // GetFinishNotificationEvent() -> handle public ResultCode GetFinishNotificationEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_finishNotificationEvent.ReadableEvent, out int finishNotificationEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_finishNotificationEvent.ReadableEvent, out int finishNotificationEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs index 53866a6b3..0d5520038 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // TODO: Found where stored value is used. ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Application.TitleId); - + if (resultCode != ResultCode.Success) { return resultCode; @@ -327,7 +327,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc { if (_addOnContentListChangedEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out _addOnContentListChangedEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out _addOnContentListChangedEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs index 9b65e0f9e..5ec43a3f5 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Ns.Aoc @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // GetPurchasedEventReadableHandle() -> handle public ResultCode GetPurchasedEventReadableHandle(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_purchasedEvent.ReadableEvent, out int purchasedEventReadableHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_purchasedEvent.ReadableEvent, out int purchasedEventReadableHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs index 5bc3e3bd3..f33cc4601 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs @@ -1,6 +1,6 @@ -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types; +using Ryujinx.Horizon.Common; using Ryujinx.Memory; using System; @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { KEvent evnt = new KEvent(context.Device.System.KernelContext); - if (context.Process.HandleTable.GenerateHandle(evnt.ReadableEvent, out handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(evnt.ReadableEvent, out handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs index d332bb044..ac5512eda 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs @@ -2,9 +2,9 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.Types; +using Ryujinx.Horizon.Common; using System; using System.Threading; @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl Event = new KEvent(system.KernelContext); - if (KernelStatic.GetCurrentProcess().HandleTable.GenerateHandle(Event.ReadableEvent, out EventHandle) != KernelResult.Success) + if (KernelStatic.GetCurrentProcess().HandleTable.GenerateHandle(Event.ReadableEvent, out EventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs index f1a6570b0..d6a8e29fb 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types; +using Ryujinx.Horizon.Common; using Ryujinx.Memory; using System; using System.Diagnostics; @@ -99,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu if (targetEvent != null) { - if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) + if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs b/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs index 8c96c4ad9..94ab49cac 100644 --- a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs +++ b/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs @@ -1,7 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.Pcv.Clkrst.ClkrstManager; using Ryujinx.HLE.HOS.Services.Pcv.Types; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst { if (_moduleStateTableEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(context.Device.System.IirsSharedMem, out _moduleStateTableEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(context.Device.System.IirsSharedMem, out _moduleStateTableEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs index 4b0df9b7b..c9c6354df 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs @@ -1,7 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Services.Pm { @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Pm KProcess process = KernelStatic.GetProcessByPid(pid); - if (context.Process.HandleTable.GenerateHandle(process, out int processHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(process, out int processHandle) != Result.Success) { throw new System.Exception("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs index 6015c6a4b..9b4e996d9 100644 --- a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs +++ b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Services.Ptm.Psm { @@ -22,11 +22,11 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm { if (_stateChangeEventHandle == -1) { - KernelResult resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle); + Result resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle); - if (resultCode != KernelResult.Success) + if (resultCode != Result.Success) { - return (ResultCode)resultCode; + return (ResultCode)resultCode.ErrorCode; } } diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index 36e1078fd..263e1c4ce 100644 --- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -1,10 +1,10 @@ using LibHac.Tools.FsSystem; using Ryujinx.Common; using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.Horizon.Common; using Ryujinx.Memory; using System; using System.Collections.Generic; @@ -217,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro if (info.BssSize > 0) { - KernelResult bssMappingResult = memMgr.MapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); + Result bssMappingResult = memMgr.MapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); if (bssMappingResult == KernelResult.InvalidMemState) { @@ -226,12 +226,12 @@ namespace Ryujinx.HLE.HOS.Services.Ro continue; } - else if (bssMappingResult != KernelResult.Success) + else if (bssMappingResult != Result.Success) { memMgr.UnmapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); memMgr.UnmapProcessCodeMemory(nroMappedAddress, info.NroAddress, info.NroSize); - return (ResultCode)bssMappingResult; + return (ResultCode)bssMappingResult.ErrorCode; } } @@ -286,15 +286,15 @@ namespace Ryujinx.HLE.HOS.Services.Ro } } - KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, baseAddress, size); + Result result = memMgr.MapProcessCodeMemory(targetAddress, baseAddress, size); if (result == KernelResult.InvalidMemState) { continue; } - else if (result != KernelResult.Success) + else if (result != Result.Success) { - return (ResultCode)result; + return (ResultCode)result.ErrorCode; } if (!CanAddGuardRegionsInProcess(process, targetAddress, size)) @@ -313,11 +313,11 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.Success; } - private KernelResult SetNroMemoryPermissions(KProcess process, IExecutable relocatableObject, ulong baseAddress) + private Result SetNroMemoryPermissions(KProcess process, IExecutable relocatableObject, ulong baseAddress) { - ulong textStart = baseAddress + (ulong)relocatableObject.TextOffset; - ulong roStart = baseAddress + (ulong)relocatableObject.RoOffset; - ulong dataStart = baseAddress + (ulong)relocatableObject.DataOffset; + ulong textStart = baseAddress + relocatableObject.TextOffset; + ulong roStart = baseAddress + relocatableObject.RoOffset; + ulong dataStart = baseAddress + relocatableObject.DataOffset; ulong bssStart = dataStart + (ulong)relocatableObject.Data.Length; @@ -329,18 +329,18 @@ namespace Ryujinx.HLE.HOS.Services.Ro MemoryHelper.FillWithZeros(process.CpuMemory, bssStart, (int)(bssEnd - bssStart)); - KernelResult result; + Result result; result = process.MemoryManager.SetProcessMemoryPermission(textStart, roStart - textStart, KMemoryPermission.ReadAndExecute); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } result = process.MemoryManager.SetProcessMemoryPermission(roStart, dataStart - roStart, KMemoryPermission.Read); - if (result != KernelResult.Success) + if (result != Result.Success) { return result; } @@ -385,7 +385,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro ulong dataSize = (ulong)info.Executable.Data.Length; ulong bssSize = (ulong)info.Executable.BssSize; - KernelResult result = KernelResult.Success; + Result result = Result.Success; if (info.Executable.BssSize != 0) { @@ -395,14 +395,14 @@ namespace Ryujinx.HLE.HOS.Services.Ro bssSize); } - if (result == KernelResult.Success) + if (result == Result.Success) { result = _owner.MemoryManager.UnmapProcessCodeMemory( info.NroMappedAddress + textSize + roSize, info.Executable.SourceAddress + textSize + roSize, dataSize); - if (result == KernelResult.Success) + if (result == Result.Success) { result = _owner.MemoryManager.UnmapProcessCodeMemory( info.NroMappedAddress, @@ -411,7 +411,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro } } - return (ResultCode)result; + return (ResultCode)result.ErrorCode; } private ResultCode IsInitialized(ulong pid) @@ -452,7 +452,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro if (result == ResultCode.Success) { - result = (ResultCode)SetNroMemoryPermissions(_owner, info.Executable, nroMappedAddress); + result = (ResultCode)SetNroMemoryPermissions(_owner, info.Executable, nroMappedAddress).ErrorCode; if (result == ResultCode.Success) { diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs index 82e246b73..f95c1d1f3 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.Sdb.Pl.Types; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Sdb.Pl @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl if (_fontSharedMemHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out _fontSharedMemHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out _fontSharedMemHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl // GetSharedFontInOrderOfPriorityForSystem(bytes<8, 1>) -> (u8, u32, buffer, buffer, buffer) public ResultCode GetSharedFontInOrderOfPriorityForSystem(ServiceCtx context) { - // TODO: Check the differencies with GetSharedFontInOrderOfPriority. + // TODO: Check the differencies with GetSharedFontInOrderOfPriority. return GetSharedFontInOrderOfPriority(context); } diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 3c53abeca..50f6c99e6 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -1,9 +1,9 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services replyTargetHandle = 0; - if (rc == KernelResult.Success && signaledIndex >= portHandles.Length) + if (rc == Result.Success && signaledIndex >= portHandles.Length) { // We got a IPC request, process it, pass to the appropriate service if needed. int signaledHandle = handles[signaledIndex]; @@ -141,10 +141,10 @@ namespace Ryujinx.HLE.HOS.Services } else { - if (rc == KernelResult.Success) + if (rc == Result.Success) { // We got a new connection, accept the session to allow servicing future requests. - if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == KernelResult.Success) + if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success) { IpcService obj = _ports[handles[signaledIndex]].Invoke(); diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 8e66b28da..86031a707 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -1,8 +1,8 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Ipc; +using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; using System.IO; @@ -72,14 +72,14 @@ namespace Ryujinx.HLE.HOS.Services.Sm if (_registry.TryGetService(name, out KPort port)) { - KernelResult result = port.EnqueueIncomingSession(session.ServerSession); + Result result = port.EnqueueIncomingSession(session.ServerSession); - if (result != KernelResult.Success) + if (result != Result.Success) { throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\"."); } - if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm } } - if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -182,14 +182,14 @@ namespace Ryujinx.HLE.HOS.Services.Sm Logger.Info?.Print(LogClass.ServiceSm, $"Register \"{name}\"."); - KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, 0); + KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, null); if (!_registry.TryRegister(name, port)) { return ResultCode.AlreadyRegistered; } - if (context.Process.HandleTable.GenerateHandle(port.ServerPort, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(port.ServerPort, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index c3dcbee76..d641f7f09 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger GetNativeHandle(binderId, typeId, out KReadableEvent readableEvent); - if (context.Process.HandleTable.GenerateHandle(readableEvent, out int handle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(readableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs index 4f3518121..abb5bb40d 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs @@ -1,10 +1,10 @@ using Ryujinx.Common; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.Services.Time.StaticService; using Ryujinx.HLE.HOS.Services.Time.TimeZone; +using Ryujinx.Horizon.Common; using System; using System.Diagnostics; using System.IO; @@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { if (_timeSharedMemoryNativeHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_timeManager.SharedMemory.GetSharedMemory(), out _timeSharedMemoryNativeHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_timeManager.SharedMemory.GetSharedMemory(), out _timeSharedMemoryNativeHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs index 1ff5b2d69..aae9aaafc 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs @@ -2,9 +2,9 @@ using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.Utilities; +using Ryujinx.Horizon.Common; using System; using System.IO; @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { if (_automaticCorrectionEvent == 0) { - if (context.Process.HandleTable.GenerateHandle(_timeManager.StandardUserSystemClock.GetAutomaticCorrectionReadableEvent(), out _automaticCorrectionEvent) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(_timeManager.StandardUserSystemClock.GetAutomaticCorrectionReadableEvent(), out _automaticCorrectionEvent) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs index 085cc71dc..c43c15820 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs @@ -1,9 +1,9 @@ using Ryujinx.Common; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Time.Clock; +using Ryujinx.Horizon.Common; using System; namespace Ryujinx.HLE.HOS.Services.Time.StaticService @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService _clockCore.RegisterOperationEvent(kEvent.WritableEvent); - if (context.Process.HandleTable.GenerateHandle(kEvent.ReadableEvent, out _operationEventReadableHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(kEvent.ReadableEvent, out _operationEventReadableHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 885a4cd7c..d6feb33f4 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -14,6 +14,7 @@ using System.Diagnostics; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; +using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Services.Vi.RootService { @@ -471,7 +472,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService if (_vsyncEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != KernelResult.Success) + if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index f1f295a27..5e3aa0eac 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -11,7 +11,9 @@ - + + + diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 46af68f49..61e5e5727 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE public MemoryBlock Memory { get; } public GpuContext Gpu { get; } public VirtualFileSystem FileSystem { get; } - public Horizon System { get; } + public HOS.Horizon System { get; } public ApplicationLoader Application { get; } public PerformanceStatistics Statistics { get; } public Hid Hid { get; } @@ -47,7 +47,7 @@ namespace Ryujinx.HLE AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Gpu = new GpuContext(Configuration.GpuRenderer); - System = new Horizon(this); + System = new HOS.Horizon(this); Statistics = new PerformanceStatistics(); Hid = new Hid(this, System.HidStorage); Application = new ApplicationLoader(this); diff --git a/Ryujinx.Horizon.Common/ISyscallApi.cs b/Ryujinx.Horizon.Common/ISyscallApi.cs new file mode 100644 index 000000000..8fa276b52 --- /dev/null +++ b/Ryujinx.Horizon.Common/ISyscallApi.cs @@ -0,0 +1,33 @@ +using System; + +namespace Ryujinx.Horizon.Common +{ + public interface ISyscallApi + { + Result SetHeapSize(out ulong address, ulong size); + + void SleepThread(long timeout); + + Result CloseHandle(int handle); + + Result WaitSynchronization(out int handleIndex, ReadOnlySpan handles, long timeout); + Result CancelSynchronization(int handle); + + Result GetProcessId(out ulong pid, int handle); + + Result ConnectToNamedPort(out int handle, string name); + Result SendSyncRequest(int handle); + Result CreateSession(out int serverSessionHandle, out int clientSessionHandle, bool isLight, string name); + Result AcceptSession(out int sessionHandle, int portHandle); + Result ReplyAndReceive(out int handleIndex, ReadOnlySpan handles, int replyTargetHandle, long timeout); + + Result CreateEvent(out int writableHandle, out int readableHandle); + Result SignalEvent(int handle); + Result ClearEvent(int handle); + Result ResetSignal(int handle); + + Result CreatePort(out int serverPortHandle, out int clientPortHandle, int maxSessions, bool isLight, string name); + Result ManageNamedPort(out int handle, string name, int maxSessions); + Result ConnectToPort(out int clientSessionHandle, int clientPortHandle); + } +} diff --git a/Ryujinx.Horizon.Common/IThreadContext.cs b/Ryujinx.Horizon.Common/IThreadContext.cs new file mode 100644 index 000000000..47aea1a38 --- /dev/null +++ b/Ryujinx.Horizon.Common/IThreadContext.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Horizon.Common +{ + public interface IThreadContext + { + bool Running { get; } + + ulong TlsAddress { get; } + + ulong GetX(int index); + } +} diff --git a/Ryujinx.Horizon.Common/InvalidResultException.cs b/Ryujinx.Horizon.Common/InvalidResultException.cs new file mode 100644 index 000000000..cf38b6403 --- /dev/null +++ b/Ryujinx.Horizon.Common/InvalidResultException.cs @@ -0,0 +1,23 @@ +using System; + +namespace Ryujinx.Horizon.Common +{ + public class InvalidResultException : Exception + { + public InvalidResultException() + { + } + + public InvalidResultException(Result result) : base($"Unexpected result code {result} returned.") + { + } + + public InvalidResultException(string message) : base(message) + { + } + + public InvalidResultException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Ryujinx.Horizon.Common/KernelResult.cs b/Ryujinx.Horizon.Common/KernelResult.cs new file mode 100644 index 000000000..51fec2057 --- /dev/null +++ b/Ryujinx.Horizon.Common/KernelResult.cs @@ -0,0 +1,39 @@ +namespace Ryujinx.Horizon.Common +{ + public static class KernelResult + { + private const int ModuleId = 1; + + public static Result SessionCountExceeded => new Result(ModuleId, 7); + public static Result InvalidCapability => new Result(ModuleId, 14); + public static Result ThreadNotStarted => new Result(ModuleId, 57); + public static Result ThreadTerminating => new Result(ModuleId, 59); + public static Result InvalidSize => new Result(ModuleId, 101); + public static Result InvalidAddress => new Result(ModuleId, 102); + public static Result OutOfResource => new Result(ModuleId, 103); + public static Result OutOfMemory => new Result(ModuleId, 104); + public static Result HandleTableFull => new Result(ModuleId, 105); + public static Result InvalidMemState => new Result(ModuleId, 106); + public static Result InvalidPermission => new Result(ModuleId, 108); + public static Result InvalidMemRange => new Result(ModuleId, 110); + public static Result InvalidPriority => new Result(ModuleId, 112); + public static Result InvalidCpuCore => new Result(ModuleId, 113); + public static Result InvalidHandle => new Result(ModuleId, 114); + public static Result UserCopyFailed => new Result(ModuleId, 115); + public static Result InvalidCombination => new Result(ModuleId, 116); + public static Result TimedOut => new Result(ModuleId, 117); + public static Result Cancelled => new Result(ModuleId, 118); + public static Result MaximumExceeded => new Result(ModuleId, 119); + public static Result InvalidEnumValue => new Result(ModuleId, 120); + public static Result NotFound => new Result(ModuleId, 121); + public static Result InvalidThread => new Result(ModuleId, 122); + public static Result PortRemoteClosed => new Result(ModuleId, 123); + public static Result InvalidState => new Result(ModuleId, 125); + public static Result ReservedValue => new Result(ModuleId, 126); + public static Result PortClosed => new Result(ModuleId, 131); + public static Result ResLimitExceeded => new Result(ModuleId, 132); + public static Result ReceiveListBroken => new Result(ModuleId, 258); + public static Result OutOfVaSpace => new Result(ModuleId, 259); + public static Result CmdBufferTooSmall => new Result(ModuleId, 260); + } +} diff --git a/Ryujinx.Horizon.Common/OnScopeExit.cs b/Ryujinx.Horizon.Common/OnScopeExit.cs new file mode 100644 index 000000000..2b81e492f --- /dev/null +++ b/Ryujinx.Horizon.Common/OnScopeExit.cs @@ -0,0 +1,19 @@ +using System; + +namespace Ryujinx.Horizon.Common +{ + public struct OnScopeExit : IDisposable + { + private readonly Action _action; + + public OnScopeExit(Action action) + { + _action = action; + } + + public void Dispose() + { + _action(); + } + } +} diff --git a/Ryujinx.Horizon.Common/Result.cs b/Ryujinx.Horizon.Common/Result.cs new file mode 100644 index 000000000..04281199d --- /dev/null +++ b/Ryujinx.Horizon.Common/Result.cs @@ -0,0 +1,118 @@ +using System; + +namespace Ryujinx.Horizon.Common +{ + public struct Result : IEquatable + { + private const int ModuleBits = 9; + private const int DescriptionBits = 13; + private const int ModuleMax = 1 << ModuleBits; + private const int DescriptionMax = 1 << DescriptionBits; + + public static Result Success { get; } = new Result(0, 0); + + public int ErrorCode { get; } + + public bool IsSuccess => ErrorCode == 0; + public bool IsFailure => ErrorCode != 0; + + public int Module => ErrorCode & (ModuleMax - 1); + public int Description => (ErrorCode >> ModuleBits) & (DescriptionMax - 1); + + public string PrintableResult => $"{2000 + Module:D4}-{Description:D4}"; + + public Result(int module, int description) + { + if ((uint)module >= ModuleMax) + { + throw new ArgumentOutOfRangeException(nameof(module)); + } + + if ((uint)description >= DescriptionMax) + { + throw new ArgumentOutOfRangeException(nameof(description)); + } + + ErrorCode = module | (description << ModuleBits); + } + + public override bool Equals(object obj) + { + return obj is Result result && result.Equals(this); + } + + public bool Equals(Result other) + { + return other.ErrorCode == ErrorCode; + } + + public override int GetHashCode() + { + return ErrorCode; + } + + public static bool operator ==(Result lhs, Result rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(Result lhs, Result rhs) + { + return !lhs.Equals(rhs); + } + + public bool InRange(int minInclusive, int maxInclusive) + { + return (uint)(Description - minInclusive) <= (uint)(maxInclusive - minInclusive); + } + + public void AbortOnSuccess() + { + if (IsSuccess) + { + ThrowInvalidResult(); + } + } + + public void AbortOnFailure() + { + if (this == KernelResult.ThreadTerminating) + { + throw new ThreadTerminatedException(); + } + + AbortUnless(Success); + } + + public void AbortUnless(Result result) + { + if (this != result) + { + ThrowInvalidResult(); + } + } + + public void AbortUnless(Result result, Result result2) + { + if (this != result && this != result2) + { + ThrowInvalidResult(); + } + } + + private void ThrowInvalidResult() + { + throw new InvalidResultException(this); + } + + public override string ToString() + { + if (ResultNames.TryGet(ErrorCode, out string name)) + { + return name; + } + + return PrintableResult; + } + } +} diff --git a/Ryujinx.Horizon.Common/ResultNames.cs b/Ryujinx.Horizon.Common/ResultNames.cs new file mode 100644 index 000000000..8f8173ed5 --- /dev/null +++ b/Ryujinx.Horizon.Common/ResultNames.cs @@ -0,0 +1,1701 @@ +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Common +{ + static class ResultNames + { + // Reference: https://github.com/Thealexbarney/LibHac/blob/master/build/CodeGen/results.csv + private static readonly IReadOnlyDictionary _names = new Dictionary() + { + { 0x0, "Success" }, + { 0xE01, "OutOfSessions" }, + { 0x1C01, "InvalidArgument" }, + { 0x4201, "NotImplemented" }, + { 0x6C01, "StopProcessingException" }, + { 0x7201, "NoSynchronizationObject" }, + { 0x7601, "TerminationRequested" }, + { 0x8C01, "NoEvent" }, + { 0xCA01, "InvalidSize" }, + { 0xCC01, "InvalidAddress" }, + { 0xCE01, "OutOfResource" }, + { 0xD001, "OutOfMemory" }, + { 0xD201, "OutOfHandles" }, + { 0xD401, "InvalidCurrentMemory" }, + { 0xD801, "InvalidNewMemoryPermission" }, + { 0xDC01, "InvalidMemoryRegion" }, + { 0xE001, "InvalidPriority" }, + { 0xE201, "InvalidCoreId" }, + { 0xE401, "InvalidHandle" }, + { 0xE601, "InvalidPointer" }, + { 0xE801, "InvalidCombination" }, + { 0xEA01, "TimedOut" }, + { 0xEC01, "Cancelled" }, + { 0xEE01, "OutOfRange" }, + { 0xF001, "InvalidEnumValue" }, + { 0xF201, "NotFound" }, + { 0xF401, "Busy" }, + { 0xF601, "SessionClosed" }, + { 0xF801, "NotHandled" }, + { 0xFA01, "InvalidState" }, + { 0xFC01, "ReservedUsed" }, + { 0xFE01, "NotSupported" }, + { 0x10001, "Debug" }, + { 0x10201, "NoThread" }, + { 0x10401, "UnknownThread" }, + { 0x10601, "PortClosed" }, + { 0x10801, "LimitReached" }, + { 0x10A01, "InvalidMemoryPool" }, + { 0x20401, "ReceiveListBroken" }, + { 0x20601, "OutOfAddressSpace" }, + { 0x20801, "MessageTooLarge" }, + { 0x40A01, "InvalidProcessId" }, + { 0x40C01, "InvalidThreadId" }, + { 0x40E01, "InvalidId" }, + { 0x41001, "ProcessTerminated" }, + { 0x2, "HandledByAllProcess" }, + { 0x202, "PathNotFound" }, + { 0x402, "PathAlreadyExists" }, + { 0x1002, "DirectoryNotEmpty" }, + { 0x1A02, "DirectoryStatusChanged" }, + { 0x3C02, "UsableSpaceNotEnough" }, + { 0x3E02, "UsableSpaceNotEnoughForSaveData" }, + { 0x4002, "UsableSpaceNotEnoughForSaveDataEvenAssistanceSuccess" }, + { 0x4202, "UsableSpaceNotEnoughForCacheStorage" }, + { 0x4402, "UsableSpaceNotEnoughMmc" }, + { 0x4602, "UsableSpaceNotEnoughMmcCalibration" }, + { 0x4802, "UsableSpaceNotEnoughMmcSafe" }, + { 0x4A02, "UsableSpaceNotEnoughMmcUser" }, + { 0x4C02, "UsableSpaceNotEnoughMmcSystem" }, + { 0x4E02, "UsableSpaceNotEnoughSdCard" }, + { 0x6402, "UnsupportedSdkVersion" }, + { 0x7802, "MountNameAlreadyExists" }, + { 0x8C02, "IndividualFileDataCacheAlreadyEnabled" }, + { 0x7D002, "HandledBySystemProcess" }, + { 0x7D202, "PartitionNotFound" }, + { 0x7D402, "TargetNotFound" }, + { 0x7D602, "HasNotGottenPatrolCount" }, + { 0x7D802, "NcaExternalKeyUnregistered" }, + { 0xFA002, "SdCardAccessFailed" }, + { 0xFA202, "PortSdCardNoDevice" }, + { 0xFA402, "PortSdCardNotActivated" }, + { 0xFA602, "PortSdCardDeviceRemoved" }, + { 0xFA802, "PortSdCardNotAwakened" }, + { 0xFE002, "PortSdCardCommunicationError" }, + { 0xFE202, "PortSdCardCommunicationNotAttained" }, + { 0xFE402, "PortSdCardResponseIndexError" }, + { 0xFE602, "PortSdCardResponseEndBitError" }, + { 0xFE802, "PortSdCardResponseCrcError" }, + { 0xFEA02, "PortSdCardResponseTimeoutError" }, + { 0xFEC02, "PortSdCardDataEndBitError" }, + { 0xFEE02, "PortSdCardDataCrcError" }, + { 0xFF002, "PortSdCardDataTimeoutError" }, + { 0xFF202, "PortSdCardAutoCommandResponseIndexError" }, + { 0xFF402, "PortSdCardAutoCommandResponseEndBitError" }, + { 0xFF602, "PortSdCardAutoCommandResponseCrcError" }, + { 0xFF802, "PortSdCardAutoCommandResponseTimeoutError" }, + { 0xFFA02, "PortSdCardCommandCompleteSwTimeout" }, + { 0xFFC02, "PortSdCardTransferCompleteSwTimeout" }, + { 0x100002, "PortSdCardDeviceStatusHasError" }, + { 0x100202, "PortSdCardDeviceStatusAddressOutOfRange" }, + { 0x100402, "PortSdCardDeviceStatusAddressMisalign" }, + { 0x100602, "PortSdCardDeviceStatusBlockLenError" }, + { 0x100802, "PortSdCardDeviceStatusEraseSeqError" }, + { 0x100A02, "PortSdCardDeviceStatusEraseParam" }, + { 0x100C02, "PortSdCardDeviceStatusWpViolation" }, + { 0x100E02, "PortSdCardDeviceStatusLockUnlockFailed" }, + { 0x101002, "PortSdCardDeviceStatusComCrcError" }, + { 0x101202, "PortSdCardDeviceStatusIllegalCommand" }, + { 0x101402, "PortSdCardDeviceStatusDeviceEccFailed" }, + { 0x101602, "PortSdCardDeviceStatusCcError" }, + { 0x101802, "PortSdCardDeviceStatusError" }, + { 0x101A02, "PortSdCardDeviceStatusCidCsdOverwrite" }, + { 0x101C02, "PortSdCardDeviceStatusWpEraseSkip" }, + { 0x101E02, "PortSdCardDeviceStatusEraseReset" }, + { 0x102002, "PortSdCardDeviceStatusSwitchError" }, + { 0x103002, "PortSdCardUnexpectedDeviceState" }, + { 0x103202, "PortSdCardUnexpectedDeviceCsdValue" }, + { 0x103402, "PortSdCardAbortTransactionSwTimeout" }, + { 0x103602, "PortSdCardCommandInhibitCmdSwTimeout" }, + { 0x103802, "PortSdCardCommandInhibitDatSwTimeout" }, + { 0x103A02, "PortSdCardBusySwTimeout" }, + { 0x103C02, "PortSdCardIssueTuningCommandSwTimeout" }, + { 0x103E02, "PortSdCardTuningFailed" }, + { 0x104002, "PortSdCardMmcInitializationSwTimeout" }, + { 0x104202, "PortSdCardMmcNotSupportExtendedCsd" }, + { 0x104402, "PortSdCardUnexpectedMmcExtendedCsdValue" }, + { 0x104602, "PortSdCardMmcEraseSwTimeout" }, + { 0x104802, "PortSdCardSdCardValidationError" }, + { 0x104A02, "PortSdCardSdCardInitializationSwTimeout" }, + { 0x104C02, "PortSdCardSdCardGetValidRcaSwTimeout" }, + { 0x104E02, "PortSdCardUnexpectedSdCardAcmdDisabled" }, + { 0x105002, "PortSdCardSdCardNotSupportSwitchFunctionStatus" }, + { 0x105202, "PortSdCardUnexpectedSdCardSwitchFunctionStatus" }, + { 0x105402, "PortSdCardSdCardNotSupportAccessMode" }, + { 0x105602, "PortSdCardSdCardNot4BitBusWidthAtUhsIMode" }, + { 0x105802, "PortSdCardSdCardNotSupportSdr104AndSdr50" }, + { 0x105A02, "PortSdCardSdCardCannotSwitchedAccessMode" }, + { 0x105C02, "PortSdCardSdCardFailedSwitchedAccessMode" }, + { 0x105E02, "PortSdCardSdCardUnacceptableCurrentConsumption" }, + { 0x106002, "PortSdCardSdCardNotReadyToVoltageSwitch" }, + { 0x106202, "PortSdCardSdCardNotCompleteVoltageSwitch" }, + { 0x10A002, "PortSdCardHostControllerUnexpected" }, + { 0x10A202, "PortSdCardInternalClockStableSwTimeout" }, + { 0x10A402, "PortSdCardSdHostStandardUnknownAutoCmdError" }, + { 0x10A602, "PortSdCardSdHostStandardUnknownError" }, + { 0x10A802, "PortSdCardSdmmcDllCalibrationSwTimeout" }, + { 0x10AA02, "PortSdCardSdmmcDllApplicationSwTimeout" }, + { 0x10AC02, "PortSdCardSdHostStandardFailSwitchTo18V" }, + { 0x10E002, "PortSdCardInternalError" }, + { 0x10E202, "PortSdCardNoWaitedInterrupt" }, + { 0x10E402, "PortSdCardWaitInterruptSwTimeout" }, + { 0x112002, "PortSdCardAbortCommandIssued" }, + { 0x113002, "PortSdCardNotSupported" }, + { 0x113202, "PortSdCardNotImplemented" }, + { 0x138002, "PortSdCardStorageDeviceInvalidated" }, + { 0x138202, "PortSdCardWriteVerifyError" }, + { 0x138402, "PortSdCardFileSystemInvalidatedByRemoved" }, + { 0x138602, "PortSdCardUnexpected" }, + { 0x138802, "GameCardAccessFailed" }, + { 0x138A02, "GameCardUnknown" }, + { 0x138C02, "GameCardUnexpectedDeadCode" }, + { 0x138E02, "GameCardPreconditionViolation" }, + { 0x139002, "GameCardNotImplemented" }, + { 0x139C02, "GameCardQueueFullFailure" }, + { 0x139E02, "GameCardLockerOutOfRange" }, + { 0x13A802, "GameCardFailedIoMappingForGpio" }, + { 0x13B002, "GameCardCardNotInserted" }, + { 0x13B202, "GameCardCardIdMismatch" }, + { 0x13B402, "GameCardCardNotActivated" }, + { 0x13B602, "GameCardNotAwakened" }, + { 0x13C402, "GameCardCardAccessFailure" }, + { 0x13C602, "GameCardCardAccessTimeout" }, + { 0x13C802, "GameCardCardFatal" }, + { 0x13CA02, "GameCardCardNeedRetry" }, + { 0x13CC02, "GameCardCardRetryFailure" }, + { 0x13D002, "GameCardRetryLimitOut" }, + { 0x13D202, "GameCardNeedRefresh" }, + { 0x13D402, "GameCardNeedRefreshAndCardNeedRetry" }, + { 0x13D802, "GameCardInvalidSecureAccess" }, + { 0x13DA02, "GameCardInvalidNormalAccess" }, + { 0x13DC02, "GameCardInvalidAccessAcrossMode" }, + { 0x13DE02, "GameCardWrongCard" }, + { 0x13E002, "GameCardInitialDataMismatch" }, + { 0x13E202, "GameCardInitialNotFilledWithZero" }, + { 0x13E402, "GameCardKekIndexMismatch" }, + { 0x13E802, "GameCardInvalidGetCardDeviceCertificate" }, + { 0x13EA02, "GameCardUnregisteredCardSecureMethod" }, + { 0x13EC02, "GameCardCardNeedRetryAfterAsicReinitialize" }, + { 0x13EE02, "GameCardCardHeaderReadFailure" }, + { 0x13F002, "GameCardCardReinitializeFailure" }, + { 0x13F202, "GameCardInvalidChallengeCardExistenceMode" }, + { 0x13F402, "GameCardInvalidCardHeader" }, + { 0x13F602, "GameCardInvalidT1CardCertificate" }, + { 0x13FA02, "GameCardInvalidCa10Certificate" }, + { 0x13FC02, "GameCardInvalidCa10CardHeader" }, + { 0x140A02, "GameCardCommunicationFailure" }, + { 0x140C02, "GameCardFinishOperationFailed" }, + { 0x144A02, "GameCardStateTransitionFailure" }, + { 0x144C02, "GameCardAlreadyTransitionedState" }, + { 0x144E02, "GameCardShouldTransitFromAsicInitialToSecure" }, + { 0x145002, "GameCardShouldTransitFromInitialToNormal" }, + { 0x145202, "GameCardShouldTransitFromNormalModeToSecure" }, + { 0x145402, "GameCardShouldTransitFromNormalModeToDebug" }, + { 0x148A02, "GameCardInitializeAsicFailure" }, + { 0x148C02, "GameCardAlreadyInitializedAsic" }, + { 0x148E02, "GameCardActivateAsicFailure" }, + { 0x149002, "GameCardAsicBootFailure" }, + { 0x149402, "GameCardSendFirmwareFailure" }, + { 0x149802, "GameCardVerifyCertificateFailure" }, + { 0x149A02, "GameCardReceiveCertificateFailure" }, + { 0x149C02, "GameCardParseCertificateFailure" }, + { 0x149E02, "GameCardInvalidCertificate" }, + { 0x14A002, "GameCardSendSocCertificateFailure" }, + { 0x14A802, "GameCardGenerateCommonKeyFailure" }, + { 0x14AA02, "GameCardReceiveRandomValueFailure" }, + { 0x14AC02, "GameCardSendRandomValueFailure" }, + { 0x14AE02, "GameCardDecryptRandomValueFailure" }, + { 0x14B402, "GameCardAuthenticateMutuallyFailure" }, + { 0x14B602, "GameCardReceiveDeviceChallengeFailure" }, + { 0x14B802, "GameCardRespondDeviceChallengeFailure" }, + { 0x14BA02, "GameCardSendHostChallengeFailure" }, + { 0x14BC02, "GameCardReceiveChallengeResponseFailure" }, + { 0x14BE02, "GameCardChallengeAndResponseFailure" }, + { 0x14C402, "GameCardChangeModeToSecureFailure" }, + { 0x14C602, "GameCardExchangeRandomValuesFailure" }, + { 0x14C802, "GameCardAsicChallengeCardExistenceFailure" }, + { 0x14CE02, "GameCardInitializeAsicTimeOut" }, + { 0x14D202, "GameCardSplFailure" }, + { 0x14D402, "GameCardSplDecryptAesKeyFailure" }, + { 0x14D602, "GameCardSplDecryptAndStoreGcKeyFailure" }, + { 0x14D802, "GameCardSplGenerateRandomBytesFailure" }, + { 0x14DA02, "GameCardSplDecryptGcMessageFailure" }, + { 0x14DE02, "GameCardReadRegisterFailure" }, + { 0x14E002, "GameCardWriteRegisterFailure" }, + { 0x14E202, "GameCardEnableCardBusFailure" }, + { 0x14E402, "GameCardGetCardHeaderFailure" }, + { 0x14E602, "GameCardAsicStatusError" }, + { 0x14E802, "GameCardChangeGcModeToSecureFailure" }, + { 0x14EA02, "GameCardChangeGcModeToDebugFailure" }, + { 0x14EC02, "GameCardReadRmaInfoFailure" }, + { 0x14F002, "GameCardUpdateKeyFailure" }, + { 0x14F202, "GameCardKeySourceNotFound" }, + { 0x150402, "GameCardStateFailure" }, + { 0x150602, "GameCardStateCardNormalModeRequired" }, + { 0x150802, "GameCardStateCardSecureModeRequired" }, + { 0x150A02, "GameCardStateCardDebugModeRequired" }, + { 0x150C02, "GameCardStateAsicInitialRequired" }, + { 0x150E02, "GameCardStateAsicSecureRequired" }, + { 0x151802, "GameCardGeneralIoFailure" }, + { 0x151A02, "GameCardGeneralIoReleaseAsicResetFailure" }, + { 0x151C02, "GameCardGeneralIoHoldAsicResetFailure" }, + { 0x151E02, "GameCardSetVoltageFailure" }, + { 0x152C02, "GameCardDataIoFailure" }, + { 0x152E02, "GameCardDataIoActivateFailure" }, + { 0x155402, "GameCardCardCommandFailure" }, + { 0x155602, "GameCardCommandReadId1Failure" }, + { 0x155802, "GameCardCommandReadId2Failure" }, + { 0x155A02, "GameCardCommandReadId3Failure" }, + { 0x155C02, "GameCardSendCardReadUidFailure" }, + { 0x155E02, "GameCardCommandReadPageFailure" }, + { 0x156002, "GameCardCommandReadPageUnalignedFailure" }, + { 0x156202, "GameCardCommandWritePageFailure" }, + { 0x156402, "GameCardCommandRefreshFailure" }, + { 0x156602, "GameCardCommandUpdateKeyFailure" }, + { 0x156802, "GameCardSendCardSelfRefreshFailure" }, + { 0x156A02, "GameCardSendCardReadRefreshStatusFailure" }, + { 0x156C02, "GameCardCommandReadCrcFailure" }, + { 0x156E02, "GameCardCommandEraseFailure" }, + { 0x157002, "GameCardCommandReadDevParamFailure" }, + { 0x157202, "GameCardCommandWriteDevParamFailure" }, + { 0x157402, "GameCardSendCardReadErrorCountFailure" }, + { 0x16A802, "GameCardDevCardUnexpectedFailure" }, + { 0x16AA02, "GameCardDebugParameterMismatch" }, + { 0x16AC02, "GameCardDebugEraseFailure" }, + { 0x16AE02, "GameCardDebugWriteCrcMismatch" }, + { 0x16B002, "GameCardDebugCardReceivedIdMismatch" }, + { 0x16B202, "GameCardDebugCardId1Mismatch" }, + { 0x16B402, "GameCardDebugCardId2Mismatch" }, + { 0x170C02, "GameCardFsFailure" }, + { 0x170E02, "GameCardFsGetHandleFailure" }, + { 0x171002, "GameCardFsCheckHandleInReadFailure" }, + { 0x171202, "GameCardFsCheckHandleInWriteFailure" }, + { 0x171402, "GameCardFsCheckHandleInGetStatusFailure" }, + { 0x171602, "GameCardFsCheckHandleInGetDeviceCertFailure" }, + { 0x171802, "GameCardFsCheckHandleInGetCardImageHashFailure" }, + { 0x171A02, "GameCardFsCheckHandleInChallengeCardExistence" }, + { 0x171C02, "GameCardFsCheckHandleInOnAcquireLock" }, + { 0x171E02, "GameCardFsCheckModeInOnAcquireSecureLock" }, + { 0x172002, "GameCardFsCheckHandleInCreateReadOnlyFailure" }, + { 0x172202, "GameCardFsCheckHandleInCreateSecureReadOnlyFailure" }, + { 0x172402, "GameCardFsInvalidCompatibilityType" }, + { 0x172602, "GameCardNotSupportedOnDeviceModel" }, + { 0x177002, "Internal" }, + { 0x177202, "NotImplemented" }, + { 0x177402, "UnsupportedVersion" }, + { 0x177602, "AlreadyExists" }, + { 0x177A02, "OutOfRange" }, + { 0x183602, "StorageDeviceInvalidOperation" }, + { 0x183802, "SystemPartitionNotReady" }, + { 0x183A02, "StorageDeviceNotReady" }, + { 0x190002, "AllocationMemoryFailed" }, + { 0x190202, "AllocationMemoryFailedInFatFileSystemA" }, + { 0x190602, "AllocationMemoryFailedInFatFileSystemC" }, + { 0x190802, "AllocationMemoryFailedInFatFileSystemD" }, + { 0x190A02, "AllocationMemoryFailedInFatFileSystemE" }, + { 0x190C02, "AllocationMemoryFailedInFatFileSystemF" }, + { 0x191002, "AllocationMemoryFailedInFatFileSystemH" }, + { 0x191602, "AllocationMemoryFailedInFileSystemAccessorA" }, + { 0x191802, "AllocationMemoryFailedInFileSystemAccessorB" }, + { 0x191A02, "AllocationMemoryFailedInApplicationA" }, + { 0x191C02, "AllocationMemoryFailedInBcatSaveDataA" }, + { 0x191E02, "AllocationMemoryFailedInBisA" }, + { 0x192002, "AllocationMemoryFailedInBisB" }, + { 0x192202, "AllocationMemoryFailedInBisC" }, + { 0x192402, "AllocationMemoryFailedInCodeA" }, + { 0x192602, "AllocationMemoryFailedInContentA" }, + { 0x192802, "AllocationMemoryFailedInContentStorageA" }, + { 0x192A02, "AllocationMemoryFailedInContentStorageB" }, + { 0x192C02, "AllocationMemoryFailedInDataA" }, + { 0x192E02, "AllocationMemoryFailedInDataB" }, + { 0x193002, "AllocationMemoryFailedInDeviceSaveDataA" }, + { 0x193202, "AllocationMemoryFailedInGameCardA" }, + { 0x193402, "AllocationMemoryFailedInGameCardB" }, + { 0x193602, "AllocationMemoryFailedInGameCardC" }, + { 0x193802, "AllocationMemoryFailedInGameCardD" }, + { 0x193A02, "AllocationMemoryFailedInHostA" }, + { 0x193C02, "AllocationMemoryFailedInHostB" }, + { 0x193E02, "AllocationMemoryFailedInHostC" }, + { 0x194002, "AllocationMemoryFailedInImageDirectoryA" }, + { 0x194202, "AllocationMemoryFailedInLogoA" }, + { 0x194402, "AllocationMemoryFailedInRomA" }, + { 0x194602, "AllocationMemoryFailedInRomB" }, + { 0x194802, "AllocationMemoryFailedInRomC" }, + { 0x194A02, "AllocationMemoryFailedInRomD" }, + { 0x194C02, "AllocationMemoryFailedInRomE" }, + { 0x194E02, "AllocationMemoryFailedInRomF" }, + { 0x195402, "AllocationMemoryFailedInSaveDataManagementA" }, + { 0x195602, "AllocationMemoryFailedInSaveDataThumbnailA" }, + { 0x195802, "AllocationMemoryFailedInSdCardA" }, + { 0x195A02, "AllocationMemoryFailedInSdCardB" }, + { 0x195C02, "AllocationMemoryFailedInSystemSaveDataA" }, + { 0x195E02, "AllocationMemoryFailedInRomFsFileSystemA" }, + { 0x196002, "AllocationMemoryFailedInRomFsFileSystemB" }, + { 0x196202, "AllocationMemoryFailedInRomFsFileSystemC" }, + { 0x196602, "AllocationMemoryFailedInGuidPartitionTableA" }, + { 0x196802, "AllocationMemoryFailedInDeviceDetectionEventManagerA" }, + { 0x196A02, "AllocationMemoryFailedInSaveDataFileSystemServiceImplA" }, + { 0x196C02, "AllocationMemoryFailedInFileSystemProxyCoreImplB" }, + { 0x196E02, "AllocationMemoryFailedInSdCardProxyFileSystemCreatorA" }, + { 0x197002, "AllocationMemoryFailedInNcaFileSystemServiceImplA" }, + { 0x197202, "AllocationMemoryFailedInNcaFileSystemServiceImplB" }, + { 0x197402, "AllocationMemoryFailedInProgramRegistryManagerA" }, + { 0x197602, "AllocationMemoryFailedInSdmmcStorageServiceA" }, + { 0x197802, "AllocationMemoryFailedInBuiltInStorageCreatorA" }, + { 0x197A02, "AllocationMemoryFailedInBuiltInStorageCreatorB" }, + { 0x197C02, "AllocationMemoryFailedInBuiltInStorageCreatorC" }, + { 0x198002, "AllocationMemoryFailedFatFileSystemWithBufferA" }, + { 0x198202, "AllocationMemoryFailedInFatFileSystemCreatorA" }, + { 0x198402, "AllocationMemoryFailedInFatFileSystemCreatorB" }, + { 0x198602, "AllocationMemoryFailedInGameCardFileSystemCreatorA" }, + { 0x198802, "AllocationMemoryFailedInGameCardFileSystemCreatorB" }, + { 0x198A02, "AllocationMemoryFailedInGameCardFileSystemCreatorC" }, + { 0x198C02, "AllocationMemoryFailedInGameCardFileSystemCreatorD" }, + { 0x198E02, "AllocationMemoryFailedInGameCardFileSystemCreatorE" }, + { 0x199002, "AllocationMemoryFailedInGameCardFileSystemCreatorF" }, + { 0x199202, "AllocationMemoryFailedInGameCardManagerA" }, + { 0x199402, "AllocationMemoryFailedInGameCardManagerB" }, + { 0x199602, "AllocationMemoryFailedInGameCardManagerC" }, + { 0x199802, "AllocationMemoryFailedInGameCardManagerD" }, + { 0x199A02, "AllocationMemoryFailedInGameCardManagerE" }, + { 0x199C02, "AllocationMemoryFailedInGameCardManagerF" }, + { 0x199E02, "AllocationMemoryFailedInLocalFileSystemCreatorA" }, + { 0x19A002, "AllocationMemoryFailedInPartitionFileSystemCreatorA" }, + { 0x19A202, "AllocationMemoryFailedInRomFileSystemCreatorA" }, + { 0x19A402, "AllocationMemoryFailedInSaveDataFileSystemCreatorA" }, + { 0x19A602, "AllocationMemoryFailedInSaveDataFileSystemCreatorB" }, + { 0x19A802, "AllocationMemoryFailedInSaveDataFileSystemCreatorC" }, + { 0x19AA02, "AllocationMemoryFailedInSaveDataFileSystemCreatorD" }, + { 0x19AC02, "AllocationMemoryFailedInSaveDataFileSystemCreatorE" }, + { 0x19B002, "AllocationMemoryFailedInStorageOnNcaCreatorA" }, + { 0x19B202, "AllocationMemoryFailedInStorageOnNcaCreatorB" }, + { 0x19B402, "AllocationMemoryFailedInSubDirectoryFileSystemCreatorA" }, + { 0x19B602, "AllocationMemoryFailedInTargetManagerFileSystemCreatorA" }, + { 0x19B802, "AllocationMemoryFailedInSaveDataIndexerA" }, + { 0x19BA02, "AllocationMemoryFailedInSaveDataIndexerB" }, + { 0x19BC02, "AllocationMemoryFailedInFileSystemBuddyHeapA" }, + { 0x19BE02, "AllocationMemoryFailedInFileSystemBufferManagerA" }, + { 0x19C002, "AllocationMemoryFailedInBlockCacheBufferedStorageA" }, + { 0x19C202, "AllocationMemoryFailedInBlockCacheBufferedStorageB" }, + { 0x19C402, "AllocationMemoryFailedInDuplexStorageA" }, + { 0x19D002, "AllocationMemoryFailedInIntegrityVerificationStorageA" }, + { 0x19D202, "AllocationMemoryFailedInIntegrityVerificationStorageB" }, + { 0x19D402, "AllocationMemoryFailedInJournalStorageA" }, + { 0x19D602, "AllocationMemoryFailedInJournalStorageB" }, + { 0x19DC02, "AllocationMemoryFailedInSaveDataFileSystemCoreA" }, + { 0x19DE02, "AllocationMemoryFailedInSaveDataFileSystemCoreB" }, + { 0x19E002, "AllocationMemoryFailedInAesXtsFileA" }, + { 0x19E202, "AllocationMemoryFailedInAesXtsFileB" }, + { 0x19E402, "AllocationMemoryFailedInAesXtsFileC" }, + { 0x19E602, "AllocationMemoryFailedInAesXtsFileD" }, + { 0x19E802, "AllocationMemoryFailedInAesXtsFileSystemA" }, + { 0x19EE02, "AllocationMemoryFailedInConcatenationFileSystemA" }, + { 0x19F002, "AllocationMemoryFailedInConcatenationFileSystemB" }, + { 0x19F202, "AllocationMemoryFailedInDirectorySaveDataFileSystemA" }, + { 0x19F402, "AllocationMemoryFailedInLocalFileSystemA" }, + { 0x19F602, "AllocationMemoryFailedInLocalFileSystemB" }, + { 0x1A1A02, "AllocationMemoryFailedInNcaFileSystemDriverI" }, + { 0x1A2602, "AllocationMemoryFailedInPartitionFileSystemA" }, + { 0x1A2802, "AllocationMemoryFailedInPartitionFileSystemB" }, + { 0x1A2A02, "AllocationMemoryFailedInPartitionFileSystemC" }, + { 0x1A2C02, "AllocationMemoryFailedInPartitionFileSystemMetaA" }, + { 0x1A2E02, "AllocationMemoryFailedInPartitionFileSystemMetaB" }, + { 0x1A3002, "AllocationMemoryFailedInRomFsFileSystemD" }, + { 0x1A3602, "AllocationMemoryFailedInSubdirectoryFileSystemA" }, + { 0x1A3802, "AllocationMemoryFailedInTmFileSystemA" }, + { 0x1A3A02, "AllocationMemoryFailedInTmFileSystemB" }, + { 0x1A3E02, "AllocationMemoryFailedInProxyFileSystemA" }, + { 0x1A4002, "AllocationMemoryFailedInProxyFileSystemB" }, + { 0x1A4402, "AllocationMemoryFailedInSaveDataExtraDataAccessorCacheManagerA" }, + { 0x1A4602, "AllocationMemoryFailedInNcaReaderA" }, + { 0x1A4A02, "AllocationMemoryFailedInRegisterA" }, + { 0x1A4C02, "AllocationMemoryFailedInRegisterB" }, + { 0x1A4E02, "AllocationMemoryFailedInPathNormalizer" }, + { 0x1A5E02, "AllocationMemoryFailedInDbmRomKeyValueStorage" }, + { 0x1A6002, "AllocationMemoryFailedInDbmHierarchicalRomFileTable" }, + { 0x1A6202, "AllocationMemoryFailedInRomFsFileSystemE" }, + { 0x1A6402, "AllocationMemoryFailedInISaveFileSystemA" }, + { 0x1A6602, "AllocationMemoryFailedInISaveFileSystemB" }, + { 0x1A6802, "AllocationMemoryFailedInRomOnFileA" }, + { 0x1A6A02, "AllocationMemoryFailedInRomOnFileB" }, + { 0x1A6C02, "AllocationMemoryFailedInRomOnFileC" }, + { 0x1A6E02, "AllocationMemoryFailedInAesXtsFileE" }, + { 0x1A7002, "AllocationMemoryFailedInAesXtsFileF" }, + { 0x1A7202, "AllocationMemoryFailedInAesXtsFileG" }, + { 0x1A7402, "AllocationMemoryFailedInReadOnlyFileSystemA" }, + { 0x1A8402, "AllocationMemoryFailedInEncryptedFileSystemCreatorA" }, + { 0x1A8E02, "AllocationMemoryFailedInAesCtrCounterExtendedStorageA" }, + { 0x1A9002, "AllocationMemoryFailedInAesCtrCounterExtendedStorageB" }, + { 0x1A9C02, "AllocationMemoryFailedInSdmmcStorageServiceB" }, + { 0x1A9E02, "AllocationMemoryFailedInFileSystemInterfaceAdapterA" }, + { 0x1AA002, "AllocationMemoryFailedInGameCardFileSystemCreatorG" }, + { 0x1AA202, "AllocationMemoryFailedInGameCardFileSystemCreatorH" }, + { 0x1AA402, "AllocationMemoryFailedInAesXtsFileSystemB" }, + { 0x1AA602, "AllocationMemoryFailedInBufferedStorageA" }, + { 0x1AA802, "AllocationMemoryFailedInIntegrityRomFsStorageA" }, + { 0x1AB002, "AllocationMemoryFailedInSaveDataFileSystemServiceImplB" }, + { 0x1AB802, "AllocationMemoryFailedNew" }, + { 0x1ABA02, "AllocationMemoryFailedInFileSystemProxyImplA" }, + { 0x1ABC02, "AllocationMemoryFailedMakeUnique" }, + { 0x1ABE02, "AllocationMemoryFailedAllocateShared" }, + { 0x1AC002, "AllocationPooledBufferNotEnoughSize" }, + { 0x1AC802, "AllocationMemoryFailedInWriteThroughCacheStorageA" }, + { 0x1ACA02, "AllocationMemoryFailedInSaveDataTransferManagerA" }, + { 0x1ACC02, "AllocationMemoryFailedInSaveDataTransferManagerB" }, + { 0x1ACE02, "AllocationMemoryFailedInHtcFileSystemA" }, + { 0x1AD002, "AllocationMemoryFailedInHtcFileSystemB" }, + { 0x1AD202, "AllocationMemoryFailedInGameCardManagerG" }, + { 0x1B5802, "MmcAccessFailed" }, + { 0x1B5A02, "PortMmcNoDevice" }, + { 0x1B5C02, "PortMmcNotActivated" }, + { 0x1B5E02, "PortMmcDeviceRemoved" }, + { 0x1B6002, "PortMmcNotAwakened" }, + { 0x1B9802, "PortMmcCommunicationError" }, + { 0x1B9A02, "PortMmcCommunicationNotAttained" }, + { 0x1B9C02, "PortMmcResponseIndexError" }, + { 0x1B9E02, "PortMmcResponseEndBitError" }, + { 0x1BA002, "PortMmcResponseCrcError" }, + { 0x1BA202, "PortMmcResponseTimeoutError" }, + { 0x1BA402, "PortMmcDataEndBitError" }, + { 0x1BA602, "PortMmcDataCrcError" }, + { 0x1BA802, "PortMmcDataTimeoutError" }, + { 0x1BAA02, "PortMmcAutoCommandResponseIndexError" }, + { 0x1BAC02, "PortMmcAutoCommandResponseEndBitError" }, + { 0x1BAE02, "PortMmcAutoCommandResponseCrcError" }, + { 0x1BB002, "PortMmcAutoCommandResponseTimeoutError" }, + { 0x1BB202, "PortMmcCommandCompleteSwTimeout" }, + { 0x1BB402, "PortMmcTransferCompleteSwTimeout" }, + { 0x1BB802, "PortMmcDeviceStatusHasError" }, + { 0x1BBA02, "PortMmcDeviceStatusAddressOutOfRange" }, + { 0x1BBC02, "PortMmcDeviceStatusAddressMisalign" }, + { 0x1BBE02, "PortMmcDeviceStatusBlockLenError" }, + { 0x1BC002, "PortMmcDeviceStatusEraseSeqError" }, + { 0x1BC202, "PortMmcDeviceStatusEraseParam" }, + { 0x1BC402, "PortMmcDeviceStatusWpViolation" }, + { 0x1BC602, "PortMmcDeviceStatusLockUnlockFailed" }, + { 0x1BC802, "PortMmcDeviceStatusComCrcError" }, + { 0x1BCA02, "PortMmcDeviceStatusIllegalCommand" }, + { 0x1BCC02, "PortMmcDeviceStatusDeviceEccFailed" }, + { 0x1BCE02, "PortMmcDeviceStatusCcError" }, + { 0x1BD002, "PortMmcDeviceStatusError" }, + { 0x1BD202, "PortMmcDeviceStatusCidCsdOverwrite" }, + { 0x1BD402, "PortMmcDeviceStatusWpEraseSkip" }, + { 0x1BD602, "PortMmcDeviceStatusEraseReset" }, + { 0x1BD802, "PortMmcDeviceStatusSwitchError" }, + { 0x1BE802, "PortMmcUnexpectedDeviceState" }, + { 0x1BEA02, "PortMmcUnexpectedDeviceCsdValue" }, + { 0x1BEC02, "PortMmcAbortTransactionSwTimeout" }, + { 0x1BEE02, "PortMmcCommandInhibitCmdSwTimeout" }, + { 0x1BF002, "PortMmcCommandInhibitDatSwTimeout" }, + { 0x1BF202, "PortMmcBusySwTimeout" }, + { 0x1BF402, "PortMmcIssueTuningCommandSwTimeout" }, + { 0x1BF602, "PortMmcTuningFailed" }, + { 0x1BF802, "PortMmcMmcInitializationSwTimeout" }, + { 0x1BFA02, "PortMmcMmcNotSupportExtendedCsd" }, + { 0x1BFC02, "PortMmcUnexpectedMmcExtendedCsdValue" }, + { 0x1BFE02, "PortMmcMmcEraseSwTimeout" }, + { 0x1C0002, "PortMmcSdCardValidationError" }, + { 0x1C0202, "PortMmcSdCardInitializationSwTimeout" }, + { 0x1C0402, "PortMmcSdCardGetValidRcaSwTimeout" }, + { 0x1C0602, "PortMmcUnexpectedSdCardAcmdDisabled" }, + { 0x1C0802, "PortMmcSdCardNotSupportSwitchFunctionStatus" }, + { 0x1C0A02, "PortMmcUnexpectedSdCardSwitchFunctionStatus" }, + { 0x1C0C02, "PortMmcSdCardNotSupportAccessMode" }, + { 0x1C0E02, "PortMmcSdCardNot4BitBusWidthAtUhsIMode" }, + { 0x1C1002, "PortMmcSdCardNotSupportSdr104AndSdr50" }, + { 0x1C1202, "PortMmcSdCardCannotSwitchedAccessMode" }, + { 0x1C1402, "PortMmcSdCardFailedSwitchedAccessMode" }, + { 0x1C1602, "PortMmcSdCardUnacceptableCurrentConsumption" }, + { 0x1C1802, "PortMmcSdCardNotReadyToVoltageSwitch" }, + { 0x1C1A02, "PortMmcSdCardNotCompleteVoltageSwitch" }, + { 0x1C5802, "PortMmcHostControllerUnexpected" }, + { 0x1C5A02, "PortMmcInternalClockStableSwTimeout" }, + { 0x1C5C02, "PortMmcSdHostStandardUnknownAutoCmdError" }, + { 0x1C5E02, "PortMmcSdHostStandardUnknownError" }, + { 0x1C6002, "PortMmcSdmmcDllCalibrationSwTimeout" }, + { 0x1C6202, "PortMmcSdmmcDllApplicationSwTimeout" }, + { 0x1C6402, "PortMmcSdHostStandardFailSwitchTo18V" }, + { 0x1C9802, "PortMmcInternalError" }, + { 0x1C9A02, "PortMmcNoWaitedInterrupt" }, + { 0x1C9C02, "PortMmcWaitInterruptSwTimeout" }, + { 0x1CD802, "PortMmcAbortCommandIssued" }, + { 0x1CE802, "PortMmcNotSupported" }, + { 0x1CEA02, "PortMmcNotImplemented" }, + { 0x1F3C02, "PortMmcStorageDeviceInvalidated" }, + { 0x1F3E02, "PortMmcUnexpected" }, + { 0x1F4002, "DataCorrupted" }, + { 0x1F4202, "RomCorrupted" }, + { 0x1F4402, "UnsupportedRomVersion" }, + { 0x1F5602, "AesCtrCounterExtendedStorageCorrupted" }, + { 0x1F5802, "InvalidAesCtrCounterExtendedEntryOffset" }, + { 0x1F5A02, "InvalidAesCtrCounterExtendedTableSize" }, + { 0x1F5C02, "InvalidAesCtrCounterExtendedGeneration" }, + { 0x1F5E02, "InvalidAesCtrCounterExtendedOffset" }, + { 0x1F6002, "InvalidAesCtrCounterExtendedDataStorageSize" }, + { 0x1F6202, "InvalidAesCtrCounterExtendedMetaStorageSize" }, + { 0x1F6A02, "IndirectStorageCorrupted" }, + { 0x1F6C02, "InvalidIndirectEntryOffset" }, + { 0x1F6E02, "InvalidIndirectEntryStorageIndex" }, + { 0x1F7002, "InvalidIndirectStorageSize" }, + { 0x1F7202, "InvalidIndirectVirtualOffset" }, + { 0x1F7402, "InvalidIndirectPhysicalOffset" }, + { 0x1F7602, "InvalidIndirectStorageIndex" }, + { 0x1F7802, "InvalidIndirectStorageBucketTreeSize" }, + { 0x1F7E02, "BucketTreeCorrupted" }, + { 0x1F8002, "InvalidBucketTreeSignature" }, + { 0x1F8202, "InvalidBucketTreeEntryCount" }, + { 0x1F8402, "InvalidBucketTreeNodeEntryCount" }, + { 0x1F8602, "InvalidBucketTreeNodeOffset" }, + { 0x1F8802, "InvalidBucketTreeEntryOffset" }, + { 0x1F8A02, "InvalidBucketTreeEntrySetOffset" }, + { 0x1F8C02, "InvalidBucketTreeNodeIndex" }, + { 0x1F8E02, "InvalidBucketTreeVirtualOffset" }, + { 0x1F9202, "RomNcaCorrupted" }, + { 0x1FA602, "RomNcaFileSystemCorrupted" }, + { 0x1FA802, "InvalidRomNcaFileSystemType" }, + { 0x1FAA02, "InvalidRomAcidFileSize" }, + { 0x1FAC02, "InvalidRomAcidSize" }, + { 0x1FAE02, "InvalidRomAcid" }, + { 0x1FB002, "RomAcidVerificationFailed" }, + { 0x1FB202, "InvalidRomNcaSignature" }, + { 0x1FB402, "RomNcaHeaderSignature1VerificationFailed" }, + { 0x1FB602, "RomNcaHeaderSignature2VerificationFailed" }, + { 0x1FB802, "RomNcaFsHeaderHashVerificationFailed" }, + { 0x1FBA02, "InvalidRomNcaKeyIndex" }, + { 0x1FBC02, "InvalidRomNcaFsHeaderHashType" }, + { 0x1FBE02, "InvalidRomNcaFsHeaderEncryptionType" }, + { 0x1FC002, "InvalidRomNcaPatchInfoIndirectSize" }, + { 0x1FC202, "InvalidRomNcaPatchInfoAesCtrExSize" }, + { 0x1FC402, "InvalidRomNcaPatchInfoAesCtrExOffset" }, + { 0x1FC602, "InvalidRomNcaId" }, + { 0x1FC802, "InvalidRomNcaHeader" }, + { 0x1FCA02, "InvalidRomNcaFsHeader" }, + { 0x1FCC02, "InvalidRomNcaPatchInfoIndirectOffset" }, + { 0x1FCE02, "RomNcaHierarchicalSha256StorageCorrupted" }, + { 0x1FD002, "InvalidRomHierarchicalSha256BlockSize" }, + { 0x1FD202, "InvalidRomHierarchicalSha256LayerCount" }, + { 0x1FD402, "RomHierarchicalSha256BaseStorageTooLarge" }, + { 0x1FD602, "RomHierarchicalSha256HashVerificationFailed" }, + { 0x1FE202, "InvalidRomHierarchicalIntegrityVerificationLayerCount" }, + { 0x1FE402, "RomNcaIndirectStorageOutOfRange" }, + { 0x1FE602, "RomNcaInvalidCompressionInfo" }, + { 0x205A02, "RomIntegrityVerificationStorageCorrupted" }, + { 0x205C02, "IncorrectRomIntegrityVerificationMagicCode" }, + { 0x205E02, "InvalidRomZeroSignature" }, + { 0x206002, "RomNonRealDataVerificationFailed" }, + { 0x206E02, "RomRealDataVerificationFailed" }, + { 0x207002, "ClearedRomRealDataVerificationFailed" }, + { 0x207202, "UnclearedRomRealDataVerificationFailed" }, + { 0x20AA02, "RomPartitionFileSystemCorrupted" }, + { 0x20AC02, "InvalidRomSha256PartitionHashTarget" }, + { 0x20AE02, "RomSha256PartitionHashVerificationFailed" }, + { 0x20B002, "RomPartitionSignatureVerificationFailed" }, + { 0x20B202, "RomSha256PartitionSignatureVerificationFailed" }, + { 0x20B402, "InvalidRomPartitionEntryOffset" }, + { 0x20B602, "InvalidRomSha256PartitionMetaDataSize" }, + { 0x20D202, "RomBuiltInStorageCorrupted" }, + { 0x20D402, "RomGptHeaderSignatureVerificationFailed" }, + { 0x212202, "RomHostFileSystemCorrupted" }, + { 0x212402, "RomHostEntryCorrupted" }, + { 0x212602, "RomHostFileDataCorrupted" }, + { 0x212802, "RomHostFileCorrupted" }, + { 0x212A02, "InvalidRomHostHandle" }, + { 0x214A02, "RomDatabaseCorrupted" }, + { 0x214C02, "InvalidRomAllocationTableBlock" }, + { 0x214E02, "InvalidRomKeyValueListElementIndex" }, + { 0x217002, "RomStorageCorrupted" }, + { 0x217202, "InvalidRomStorageSize" }, + { 0x219A02, "SaveDataCorrupted" }, + { 0x219C02, "UnsupportedSaveDataVersion" }, + { 0x219E02, "InvalidSaveDataEntryType" }, + { 0x21A002, "ReconstructibleSaveDataCorrupted" }, + { 0x21AE02, "SaveDataFileSystemCorrupted" }, + { 0x21B002, "InvalidJournalIntegritySaveDataHashSize" }, + { 0x21B202, "InvalidJournalIntegritySaveDataCommitState" }, + { 0x21B402, "InvalidJournalIntegritySaveDataControlAreaSize" }, + { 0x21B602, "JournalIntegritySaveDataControlAreaVerificationFailed" }, + { 0x21B802, "JournalIntegritySaveDataMasterSignatureVerificationFailed" }, + { 0x21BA02, "IncorrectJournalIntegritySaveDataMagicCode" }, + { 0x21C202, "SaveDataDuplexStorageCorrupted" }, + { 0x21C402, "IncorrectDuplexMagicCode" }, + { 0x21C602, "DuplexStorageAccessOutOfRange" }, + { 0x21D602, "SaveDataMapCorrupted" }, + { 0x21D802, "InvalidMapEntryCount" }, + { 0x21DA02, "InvalidMapOffset" }, + { 0x21DC02, "InvalidMapSize" }, + { 0x21DE02, "InvalidMapAlignment" }, + { 0x21E002, "InvalidMapStorageType" }, + { 0x21E202, "MapAddressAlreadyRegistered" }, + { 0x21E402, "MapStorageNotFound" }, + { 0x21E602, "InvalidMapStorageSize" }, + { 0x21EA02, "SaveDataLogCorrupted" }, + { 0x21EC02, "InvalidLogBlockSize" }, + { 0x21EE02, "InvalidLogOffset" }, + { 0x21F002, "UnexpectedEndOfLog" }, + { 0x21F202, "LogNotFound" }, + { 0x220002, "ThumbnailHashVerificationFailed" }, + { 0x220A02, "InvalidSaveDataInternalStorageIntegritySeedSize" }, + { 0x220C02, "InvalidSaveDataInternalStorageAllocationTableFreeBitmapSizeA" }, + { 0x220E02, "InvalidSaveDataInternalStorageAllocationTableFreeBitmapSizeB" }, + { 0x221202, "SaveDataIntegrityVerificationStorageCorrupted" }, + { 0x221402, "IncorrectSaveDataIntegrityVerificationMagicCode" }, + { 0x221602, "InvalidSaveDataZeroHash" }, + { 0x221802, "SaveDataNonRealDataVerificationFailed" }, + { 0x222602, "SaveDataRealDataVerificationFailed" }, + { 0x222802, "ClearedSaveDataRealDataVerificationFailed" }, + { 0x222A02, "UnclearedSaveDataRealDataVerificationFailed" }, + { 0x226202, "SaveDataBuiltInStorageCorrupted" }, + { 0x226402, "SaveDataGptHeaderSignatureVerificationFailed" }, + { 0x227602, "SaveDataCoreFileSystemCorrupted" }, + { 0x227802, "IncorrectSaveDataFileSystemMagicCode" }, + { 0x227A02, "InvalidSaveDataFileReadOffset" }, + { 0x227C02, "InvalidSaveDataCoreDataStorageSize" }, + { 0x229602, "IncompleteBlockInZeroBitmapHashStorageFileSaveData" }, + { 0x229E02, "JournalStorageCorrupted" }, + { 0x22A002, "JournalStorageAccessOutOfRange" }, + { 0x22A202, "InvalidJournalStorageDataStorageSize" }, + { 0x22B202, "SaveDataHostFileSystemCorrupted" }, + { 0x22B402, "SaveDataHostEntryCorrupted" }, + { 0x22B602, "SaveDataHostFileDataCorrupted" }, + { 0x22B802, "SaveDataHostFileCorrupted" }, + { 0x22BA02, "InvalidSaveDataHostHandle" }, + { 0x22C602, "MappingTableCorrupted" }, + { 0x22C802, "InvalidMappingTableEntryCount" }, + { 0x22CA02, "InvalidMappingTablePhysicalIndex" }, + { 0x22CC02, "InvalidMappingTableVirtualIndex" }, + { 0x22DA02, "SaveDataDatabaseCorrupted" }, + { 0x22DC02, "InvalidSaveDataAllocationTableBlock" }, + { 0x22DE02, "InvalidSaveDataKeyValueListElementIndex" }, + { 0x22E002, "InvalidSaveDataAllocationTableChainEntry" }, + { 0x22E202, "InvalidSaveDataAllocationTableOffset" }, + { 0x22E402, "InvalidSaveDataAllocationTableBlockCount" }, + { 0x22E602, "InvalidSaveDataKeyValueListEntryIndex" }, + { 0x22E802, "InvalidSaveDataBitmapIndex" }, + { 0x230202, "SaveDataExtensionContextCorrupted" }, + { 0x230402, "IncorrectSaveDataExtensionContextMagicCode" }, + { 0x230602, "InvalidSaveDataExtensionContextState" }, + { 0x230802, "DifferentSaveDataExtensionContextParameter" }, + { 0x230A02, "InvalidSaveDataExtensionContextParameter" }, + { 0x231602, "IntegritySaveDataCorrupted" }, + { 0x231802, "InvalidIntegritySaveDataHashSize" }, + { 0x231C02, "InvalidIntegritySaveDataControlAreaSize" }, + { 0x231E02, "IntegritySaveDataControlAreaVerificationFailed" }, + { 0x232002, "IntegritySaveDataMasterSignatureVerificationFailed" }, + { 0x232202, "IncorrectIntegritySaveDataMagicCode" }, + { 0x232A02, "NcaCorrupted" }, + { 0x233802, "NcaBaseStorageOutOfRangeA" }, + { 0x233A02, "NcaBaseStorageOutOfRangeB" }, + { 0x233C02, "NcaBaseStorageOutOfRangeC" }, + { 0x233E02, "NcaFileSystemCorrupted" }, + { 0x234002, "InvalidNcaFileSystemType" }, + { 0x234202, "InvalidAcidFileSize" }, + { 0x234402, "InvalidAcidSize" }, + { 0x234602, "InvalidAcid" }, + { 0x234802, "AcidVerificationFailed" }, + { 0x234A02, "InvalidNcaSignature" }, + { 0x234C02, "NcaHeaderSignature1VerificationFailed" }, + { 0x234E02, "NcaHeaderSignature2VerificationFailed" }, + { 0x235002, "NcaFsHeaderHashVerificationFailed" }, + { 0x235202, "InvalidNcaKeyIndex" }, + { 0x235402, "InvalidNcaFsHeaderHashType" }, + { 0x235602, "InvalidNcaFsHeaderEncryptionType" }, + { 0x235802, "InvalidNcaPatchInfoIndirectSize" }, + { 0x235A02, "InvalidNcaPatchInfoAesCtrExSize" }, + { 0x235C02, "InvalidNcaPatchInfoAesCtrExOffset" }, + { 0x235E02, "InvalidNcaId" }, + { 0x236002, "InvalidNcaHeader" }, + { 0x236202, "InvalidNcaFsHeader" }, + { 0x236402, "InvalidNcaPatchInfoIndirectOffset" }, + { 0x236602, "NcaHierarchicalSha256StorageCorrupted" }, + { 0x236802, "InvalidHierarchicalSha256BlockSize" }, + { 0x236A02, "InvalidHierarchicalSha256LayerCount" }, + { 0x236C02, "HierarchicalSha256BaseStorageTooLarge" }, + { 0x236E02, "HierarchicalSha256HashVerificationFailed" }, + { 0x237A02, "InvalidHierarchicalIntegrityVerificationLayerCount" }, + { 0x237C02, "NcaIndirectStorageOutOfRange" }, + { 0x237E02, "InvalidNcaHeader1SignatureKeyGeneration" }, + { 0x238202, "InvalidNspdVerificationData" }, + { 0x238402, "MissingNspdVerificationData" }, + { 0x238602, "NcaInvalidCompressionInfo" }, + { 0x23F202, "IntegrityVerificationStorageCorrupted" }, + { 0x23F402, "IncorrectIntegrityVerificationMagicCode" }, + { 0x23F602, "InvalidZeroHash" }, + { 0x23F802, "NonRealDataVerificationFailed" }, + { 0x240602, "RealDataVerificationFailed" }, + { 0x240802, "ClearedRealDataVerificationFailed" }, + { 0x240A02, "UnclearedRealDataVerificationFailed" }, + { 0x244202, "PartitionFileSystemCorrupted" }, + { 0x244402, "InvalidSha256PartitionHashTarget" }, + { 0x244602, "Sha256PartitionHashVerificationFailed" }, + { 0x244802, "PartitionSignatureVerificationFailed" }, + { 0x244A02, "Sha256PartitionSignatureVerificationFailed" }, + { 0x244C02, "InvalidPartitionEntryOffset" }, + { 0x244E02, "InvalidSha256PartitionMetaDataSize" }, + { 0x246A02, "BuiltInStorageCorrupted" }, + { 0x246C02, "GptHeaderSignatureVerificationFailed" }, + { 0x247002, "GptHeaderInvalidPartitionSize" }, + { 0x249202, "FatFileSystemCorrupted" }, + { 0x249602, "InvalidFatFormat" }, + { 0x249802, "InvalidFatFileNumber" }, + { 0x249A02, "ExFatUnavailable" }, + { 0x249C02, "InvalidFatFormatBisUser" }, + { 0x249E02, "InvalidFatFormatBisSystem" }, + { 0x24A002, "InvalidFatFormatBisSafe" }, + { 0x24A202, "InvalidFatFormatBisCalibration" }, + { 0x24A402, "InvalidFatFormatSd" }, + { 0x24BA02, "HostFileSystemCorrupted" }, + { 0x24BC02, "HostEntryCorrupted" }, + { 0x24BE02, "HostFileDataCorrupted" }, + { 0x24C002, "HostFileCorrupted" }, + { 0x24C202, "InvalidHostHandle" }, + { 0x24E202, "DatabaseCorrupted" }, + { 0x24E402, "InvalidAllocationTableBlock" }, + { 0x24E602, "InvalidKeyValueListElementIndex" }, + { 0x24E802, "InvalidAllocationTableChainEntry" }, + { 0x24EA02, "InvalidAllocationTableOffset" }, + { 0x24EC02, "InvalidAllocationTableBlockCount" }, + { 0x24EE02, "InvalidKeyValueListEntryIndex" }, + { 0x24F002, "InvalidBitmapIndex" }, + { 0x250A02, "AesXtsFileSystemCorrupted" }, + { 0x250C02, "AesXtsFileSystemFileHeaderSizeCorruptedOnFileOpen" }, + { 0x250E02, "AesXtsFileSystemFileHeaderCorruptedOnFileOpen" }, + { 0x251002, "AesXtsFileSystemFileNoHeaderOnFileOpen" }, + { 0x251202, "AesXtsFileSystemFileSizeCorruptedOnFileOpen" }, + { 0x251402, "AesXtsFileSystemFileSizeCorruptedOnFileSetSize" }, + { 0x251602, "AesXtsFileSystemFileHeaderCorruptedOnRename" }, + { 0x251802, "AesXtsFileSystemFileHeaderCorruptedOnFileSetSize" }, + { 0x253202, "SaveDataTransferDataCorrupted" }, + { 0x253402, "SaveDataTransferTokenMacVerificationFailed" }, + { 0x253602, "SaveDataTransferTokenSignatureVerificationFailed" }, + { 0x253802, "SaveDataTransferTokenChallengeVerificationFailed" }, + { 0x253A02, "SaveDataTransferImportMacVerificationFailed" }, + { 0x253C02, "SaveDataTransferInitialDataMacVerificationFailed" }, + { 0x253E02, "SaveDataTransferInitialDataVersionVerificationFailed" }, + { 0x254602, "SignedSystemPartitionDataCorrupted" }, + { 0x254802, "SignedSystemPartitionInvalidSize" }, + { 0x254A02, "SignedSystemPartitionSignatureVerificationFailed" }, + { 0x254C02, "SignedSystemPartitionHashVerificationFailed" }, + { 0x254E02, "SignedSystemPartitionPackage2HashVerificationFailed" }, + { 0x255002, "SignedSystemPartitionInvalidAppendHashCount" }, + { 0x255A02, "GameCardLogoDataCorrupted" }, + { 0x256202, "SimulatedDeviceDataCorrupted" }, + { 0x256C02, "MultiCommitContextCorrupted" }, + { 0x256E02, "InvalidMultiCommitContextVersion" }, + { 0x257002, "InvalidMultiCommitContextState" }, + { 0x258402, "ConcatenationFsInvalidInternalFileCount" }, + { 0x259602, "ZeroBitmapFileCorrupted" }, + { 0x259802, "IncompleteBlockInZeroBitmapHashStorageFile" }, + { 0x271002, "Unexpected" }, + { 0x271202, "FatFsUnexpected" }, + { 0x271402, "FatFsUnclassified" }, + { 0x271602, "FatFsStorageStateMissmatch" }, + { 0x274002, "FatFsTooManyFilesOpenedS" }, + { 0x274202, "FatFsTooManyFilesOpenedU" }, + { 0x274402, "FatFsNotAFile" }, + { 0x274802, "FatFsLockError" }, + { 0x274A02, "FatFsInternalError" }, + { 0x277E02, "FatFsModuleSafeError" }, + { 0x27EC02, "FatFsUnexpectedSystemError" }, + { 0x280002, "FatFsFormatUnexpected" }, + { 0x280202, "FatFsFormatUnsupportedSize" }, + { 0x280402, "FatFsFormatInvalidBpb" }, + { 0x280602, "FatFsFormatInvalidParameter" }, + { 0x280802, "FatFsFormatIllegalSectorsA" }, + { 0x280A02, "FatFsFormatIllegalSectorsB" }, + { 0x280C02, "FatFsFormatIllegalSectorsC" }, + { 0x280E02, "FatFsFormatIllegalSectorsD" }, + { 0x281602, "FatFsWriteVerifyError" }, + { 0x296A02, "UnexpectedInMountTableA" }, + { 0x296C02, "UnexpectedInJournalIntegritySaveDataFileSystemA" }, + { 0x296E02, "UnexpectedInJournalIntegritySaveDataFileSystemB" }, + { 0x297002, "UnexpectedInJournalIntegritySaveDataFileSystemC" }, + { 0x297202, "UnexpectedInLocalFileSystemA" }, + { 0x297402, "UnexpectedInLocalFileSystemB" }, + { 0x297602, "UnexpectedInLocalFileSystemC" }, + { 0x297802, "UnexpectedInLocalFileSystemD" }, + { 0x297A02, "UnexpectedInLocalFileSystemE" }, + { 0x297C02, "UnexpectedInLocalFileSystemF" }, + { 0x297E02, "UnexpectedInPathToolA" }, + { 0x298002, "UnexpectedInPathOnExecutionDirectoryA" }, + { 0x298202, "UnexpectedInPathOnExecutionDirectoryB" }, + { 0x298402, "UnexpectedInPathOnExecutionDirectoryC" }, + { 0x298602, "UnexpectedInAesCtrStorageA" }, + { 0x298802, "UnexpectedInAesXtsStorageA" }, + { 0x298A02, "UnexpectedInSaveDataInternalStorageFileSystemA" }, + { 0x298C02, "UnexpectedInSaveDataInternalStorageFileSystemB" }, + { 0x298E02, "UnexpectedInMountUtilityA" }, + { 0x299002, "UnexpectedInNcaFileSystemServiceImplA" }, + { 0x299202, "UnexpectedInRamDiskFileSystemA" }, + { 0x299402, "UnexpectedInBisWiperA" }, + { 0x299602, "UnexpectedInBisWiperB" }, + { 0x299802, "UnexpectedInCompressedStorageA" }, + { 0x299A02, "UnexpectedInCompressedStorageB" }, + { 0x299C02, "UnexpectedInCompressedStorageC" }, + { 0x299E02, "UnexpectedInCompressedStorageD" }, + { 0x29A002, "UnexpectedInPathA" }, + { 0x2EE002, "PreconditionViolation" }, + { 0x2EE202, "InvalidArgument" }, + { 0x2EE402, "InvalidPath" }, + { 0x2EE602, "TooLongPath" }, + { 0x2EE802, "InvalidCharacter" }, + { 0x2EEA02, "InvalidPathFormat" }, + { 0x2EEC02, "DirectoryUnobtainable" }, + { 0x2EEE02, "NotNormalized" }, + { 0x2F1C02, "InvalidPathForOperation" }, + { 0x2F1E02, "DirectoryUndeletable" }, + { 0x2F2002, "DirectoryUnrenamable" }, + { 0x2F2202, "IncompatiblePath" }, + { 0x2F2402, "RenameToOtherFileSystem" }, + { 0x2F5A02, "InvalidOffset" }, + { 0x2F5C02, "InvalidSize" }, + { 0x2F5E02, "NullptrArgument" }, + { 0x2F6002, "InvalidAlignment" }, + { 0x2F6202, "InvalidMountName" }, + { 0x2F6402, "ExtensionSizeTooLarge" }, + { 0x2F6602, "ExtensionSizeInvalid" }, + { 0x2F6802, "InvalidHandle" }, + { 0x2F6A02, "CacheStorageSizeTooLarge" }, + { 0x2F6C02, "CacheStorageIndexTooLarge" }, + { 0x2F6E02, "InvalidCommitNameCount" }, + { 0x2F7002, "InvalidModeForFileOpen" }, + { 0x2F7202, "InvalidFileSize" }, + { 0x2F7402, "InvalidModeForDirectoryOpen" }, + { 0x2F7602, "InvalidCommitOption" }, + { 0x2F8002, "InvalidEnumValue" }, + { 0x2F8202, "InvalidSaveDataState" }, + { 0x2F8402, "InvalidSaveDataSpaceId" }, + { 0x2FAA02, "GameCardLogoDataTooLarge" }, + { 0x2FAC02, "FileDataCacheMemorySizeTooSmall" }, + { 0x307002, "InvalidOperationForOpenMode" }, + { 0x307202, "FileExtensionWithoutOpenModeAllowAppend" }, + { 0x307402, "ReadUnpermitted" }, + { 0x307602, "WriteUnpermitted" }, + { 0x313802, "UnsupportedOperation" }, + { 0x313A02, "UnsupportedCommitTarget" }, + { 0x313C02, "UnsupportedSetSizeForNotResizableSubStorage" }, + { 0x313E02, "UnsupportedSetSizeForResizableSubStorage" }, + { 0x314002, "UnsupportedSetSizeForMemoryStorage" }, + { 0x314202, "UnsupportedOperateRangeForMemoryStorage" }, + { 0x314402, "UnsupportedOperateRangeForFileStorage" }, + { 0x314602, "UnsupportedOperateRangeForFileHandleStorage" }, + { 0x314802, "UnsupportedOperateRangeForSwitchStorage" }, + { 0x314A02, "UnsupportedOperateRangeForStorageServiceObjectAdapter" }, + { 0x314C02, "UnsupportedWriteForAesCtrCounterExtendedStorage" }, + { 0x314E02, "UnsupportedSetSizeForAesCtrCounterExtendedStorage" }, + { 0x315002, "UnsupportedOperateRangeForAesCtrCounterExtendedStorage" }, + { 0x315202, "UnsupportedWriteForAesCtrStorageExternal" }, + { 0x315402, "UnsupportedSetSizeForAesCtrStorageExternal" }, + { 0x315602, "UnsupportedSetSizeForAesCtrStorage" }, + { 0x315802, "UnsupportedSetSizeForHierarchicalIntegrityVerificationStorage" }, + { 0x315A02, "UnsupportedOperateRangeForHierarchicalIntegrityVerificationStorage" }, + { 0x315C02, "UnsupportedSetSizeForIntegrityVerificationStorage" }, + { 0x315E02, "UnsupportedOperateRangeForWritableIntegrityVerificationStorage" }, + { 0x316002, "UnsupportedOperateRangeForIntegrityVerificationStorage" }, + { 0x316202, "UnsupportedSetSizeForBlockCacheBufferedStorage" }, + { 0x316402, "UnsupportedOperateRangeForWritableBlockCacheBufferedStorage" }, + { 0x316602, "UnsupportedOperateRangeForBlockCacheBufferedStorage" }, + { 0x316802, "UnsupportedWriteForIndirectStorage" }, + { 0x316A02, "UnsupportedSetSizeForIndirectStorage" }, + { 0x316C02, "UnsupportedOperateRangeForIndirectStorage" }, + { 0x316E02, "UnsupportedWriteForZeroStorage" }, + { 0x317002, "UnsupportedSetSizeForZeroStorage" }, + { 0x317202, "UnsupportedSetSizeForHierarchicalSha256Storage" }, + { 0x317402, "UnsupportedWriteForReadOnlyBlockCacheStorage" }, + { 0x317602, "UnsupportedSetSizeForReadOnlyBlockCacheStorage" }, + { 0x317802, "UnsupportedSetSizeForIntegrityRomFsStorage" }, + { 0x317A02, "UnsupportedSetSizeForDuplexStorage" }, + { 0x317C02, "UnsupportedOperateRangeForDuplexStorage" }, + { 0x317E02, "UnsupportedSetSizeForHierarchicalDuplexStorage" }, + { 0x318002, "UnsupportedGetSizeForRemapStorage" }, + { 0x318202, "UnsupportedSetSizeForRemapStorage" }, + { 0x318402, "UnsupportedOperateRangeForRemapStorage" }, + { 0x318602, "UnsupportedSetSizeForIntegritySaveDataStorage" }, + { 0x318802, "UnsupportedOperateRangeForIntegritySaveDataStorage" }, + { 0x318A02, "UnsupportedSetSizeForJournalIntegritySaveDataStorage" }, + { 0x318C02, "UnsupportedOperateRangeForJournalIntegritySaveDataStorage" }, + { 0x318E02, "UnsupportedGetSizeForJournalStorage" }, + { 0x319002, "UnsupportedSetSizeForJournalStorage" }, + { 0x319202, "UnsupportedOperateRangeForJournalStorage" }, + { 0x319402, "UnsupportedSetSizeForUnionStorage" }, + { 0x319602, "UnsupportedSetSizeForAllocationTableStorage" }, + { 0x319802, "UnsupportedReadForWriteOnlyGameCardStorage" }, + { 0x319A02, "UnsupportedSetSizeForWriteOnlyGameCardStorage" }, + { 0x319C02, "UnsupportedWriteForReadOnlyGameCardStorage" }, + { 0x319E02, "UnsupportedSetSizeForReadOnlyGameCardStorage" }, + { 0x31A002, "UnsupportedOperateRangeForReadOnlyGameCardStorage" }, + { 0x31A202, "UnsupportedSetSizeForSdmmcStorage" }, + { 0x31A402, "UnsupportedOperateRangeForSdmmcStorage" }, + { 0x31A602, "UnsupportedOperateRangeForFatFile" }, + { 0x31A802, "UnsupportedOperateRangeForStorageFile" }, + { 0x31AA02, "UnsupportedSetSizeForInternalStorageConcatenationFile" }, + { 0x31AC02, "UnsupportedOperateRangeForInternalStorageConcatenationFile" }, + { 0x31AE02, "UnsupportedQueryEntryForConcatenationFileSystem" }, + { 0x31B002, "UnsupportedOperateRangeForConcatenationFile" }, + { 0x31B202, "UnsupportedSetSizeForZeroBitmapFile" }, + { 0x31B402, "UnsupportedOperateRangeForFileServiceObjectAdapter" }, + { 0x31B602, "UnsupportedOperateRangeForAesXtsFile" }, + { 0x31B802, "UnsupportedWriteForRomFsFileSystem" }, + { 0x31BA02, "UnsupportedCommitProvisionallyForRomFsFileSystem" }, + { 0x31BC02, "UnsupportedGetTotalSpaceSizeForRomFsFileSystem" }, + { 0x31BE02, "UnsupportedWriteForRomFsFile" }, + { 0x31C002, "UnsupportedOperateRangeForRomFsFile" }, + { 0x31C202, "UnsupportedWriteForReadOnlyFileSystem" }, + { 0x31C402, "UnsupportedCommitProvisionallyForReadOnlyFileSystem" }, + { 0x31C602, "UnsupportedGetTotalSpaceSizeForReadOnlyFileSystem" }, + { 0x31C802, "UnsupportedWriteForReadOnlyFile" }, + { 0x31CA02, "UnsupportedOperateRangeForReadOnlyFile" }, + { 0x31CC02, "UnsupportedWriteForPartitionFileSystem" }, + { 0x31CE02, "UnsupportedCommitProvisionallyForPartitionFileSystem" }, + { 0x31D002, "UnsupportedWriteForPartitionFile" }, + { 0x31D202, "UnsupportedOperateRangeForPartitionFile" }, + { 0x31D402, "UnsupportedOperateRangeForTmFileSystemFile" }, + { 0x31D602, "UnsupportedWriteForSaveDataInternalStorageFileSystem" }, + { 0x31DC02, "UnsupportedCommitProvisionallyForApplicationTemporaryFileSystem" }, + { 0x31DE02, "UnsupportedCommitProvisionallyForSaveDataFileSystem" }, + { 0x31E002, "UnsupportedCommitProvisionallyForDirectorySaveDataFileSystem" }, + { 0x31E202, "UnsupportedWriteForZeroBitmapHashStorageFile" }, + { 0x31E402, "UnsupportedSetSizeForZeroBitmapHashStorageFile" }, + { 0x31E602, "UnsupportedWriteForCompressedStorage" }, + { 0x31E802, "UnsupportedOperateRangeForCompressedStorage" }, + { 0x31F602, "UnsupportedRollbackOnlyModifiedForApplicationTemporaryFileSystem" }, + { 0x31F802, "UnsupportedRollbackOnlyModifiedForDirectorySaveDataFileSystem" }, + { 0x31FA02, "UnsupportedOperateRangeForRegionSwitchStorage" }, + { 0x320002, "PermissionDenied" }, + { 0x320602, "HostFileSystemOperationDisabled" }, + { 0x326402, "PortAcceptableCountLimited" }, + { 0x326802, "NcaExternalKeyInconsistent" }, + { 0x326C02, "NeedFlush" }, + { 0x326E02, "FileNotClosed" }, + { 0x327002, "DirectoryNotClosed" }, + { 0x327202, "WriteModeFileNotClosed" }, + { 0x327402, "AllocatorAlreadyRegistered" }, + { 0x327602, "DefaultAllocatorAlreadyUsed" }, + { 0x327802, "GameCardLogoDataSizeInvalid" }, + { 0x327A02, "AllocatorAlignmentViolation" }, + { 0x327C02, "GlobalFileDataCacheAlreadyEnabled" }, + { 0x327E02, "MultiCommitHasOverlappingTargets" }, + { 0x328002, "MultiCommitAlreadyInProgress" }, + { 0x328202, "UserNotExist" }, + { 0x328402, "DefaultGlobalFileDataCacheEnabled" }, + { 0x328602, "SaveDataRootPathUnavailable" }, + { 0x339002, "NotFound" }, + { 0x339402, "FileNotFound" }, + { 0x339602, "DirectoryNotFound" }, + { 0x339802, "DatabaseKeyNotFound" }, + { 0x339A02, "ProgramInfoNotFound" }, + { 0x339C02, "ProgramIndexNotFound" }, + { 0x345802, "OutOfResource" }, + { 0x346202, "BufferAllocationFailed" }, + { 0x346402, "MappingTableFull" }, + { 0x346602, "AllocationTableFull" }, + { 0x346A02, "OpenCountLimit" }, + { 0x346C02, "MultiCommitFileSystemLimit" }, + { 0x352002, "MappingFailed" }, + { 0x353602, "MapFull" }, + { 0x35E802, "BadState" }, + { 0x35EC02, "NotInitialized" }, + { 0x35EE02, "BisProxyInvalidated" }, + { 0x35F002, "NcaDigestInconsistent" }, + { 0x35F202, "NotMounted" }, + { 0x35F402, "SaveDataExtending" }, + { 0x35F602, "SaveDataToExpandIsProvisionallyCommitted" }, + { 0x36B402, "SaveDataTransferV2KeySeedPackageMacVerificationFailed" }, + { 0x36B602, "SaveDataTransferV2KeySeedPackageSignatureVerificationFailed" }, + { 0x36B802, "SaveDataTransferV2KeySeedPackageChallengeVerificationFailed" }, + { 0x36BA02, "SaveDataTransferV2ImportDataVerificationFailed" }, + { 0x36BC02, "SaveDataTransferV2InitialDataGcmMacVerificationFailed" }, + { 0x36C202, "SaveDataTransferV2InitialDataMacVerificationFailed" }, + { 0x36C402, "SaveDataTransferV2ImportDataDecompressionFailed" }, + { 0x36C602, "SaveDataTransferV2PortContextMacVerificationFailed" }, + { 0x36EE02, "SaveDataPorterInvalidated" }, + { 0x36F002, "SaveDataDivisionExporterChunkExportIncomplete" }, + { 0x36F202, "SaveDataDivisionImporterChunkImportIncomplete" }, + { 0x36F402, "SaveDataPorterInitialDataVersionVerificationFailed" }, + { 0x36F602, "SaveDataChunkDecryptorGcmStreamVersionVerificationFailed" }, + { 0x36F802, "SaveDataPorterSaveDataModified" }, + { 0x36FA02, "SaveDataPorterVersionUnsupported" }, + { 0x36FC02, "SaveDataTransferV2SecondarySaveCorrupted" }, + { 0x372C02, "SaveDataTransferForSaveDataRepairKeyPackageMacVerificationFailed" }, + { 0x372E02, "SaveDataTransferForSaveDataRepairKeyPackageSignatureVerificationFailed" }, + { 0x373002, "SaveDataTransferForSaveDataRepairKeyPackageChallengeVerificationFailed" }, + { 0x373202, "SaveDataTransferForSaveDataRepairUnsupportedKeyGeneration" }, + { 0x373402, "SaveDataTransferForSaveDataRepairInitialDataMacVerificationFailed" }, + { 0x373A02, "SaveDataTransferForSaveDataRepairIncorrectInitialData" }, + { 0x373C02, "SaveDataTransferForSaveDataRepairInconsistentInitialData" }, + { 0x373E02, "SaveDataTransferForSaveDataRepairInitialDataIncorrectUserId" }, + { 0x377802, "RamDiskCorrupted" }, + { 0x377A02, "RamDiskVerifiedStorageVerificationFailed" }, + { 0x378E02, "RamDiskSaveDataCoreFileSystemCorrupted" }, + { 0x379002, "IncorrectRamDiskSaveDataFileSystemMagicCode" }, + { 0x379202, "InvalidRamDiskSaveDataFileReadOffset" }, + { 0x379402, "InvalidRamDiskSaveDataCoreDataStorageSize" }, + { 0x37A202, "RamDiskDatabaseCorrupted" }, + { 0x37A402, "InvalidRamDiskAllocationTableBlock" }, + { 0x37A602, "InvalidRamDiskKeyValueListElementIndex" }, + { 0x37A802, "InvalidRamDiskAllocationTableChainEntry" }, + { 0x37AA02, "InvalidRamDiskAllocationTableOffset" }, + { 0x37AC02, "InvalidRamDiskAllocationTableBlockCount" }, + { 0x37AE02, "InvalidRamDiskKeyValueListEntryIndex" }, + { 0x37CC02, "SaveDataTransferForRepairInitialDataMacVerificationFailed" }, + { 0x3DB802, "Unknown" }, + { 0x3DBA02, "DbmNotFound" }, + { 0x3DBC02, "DbmKeyNotFound" }, + { 0x3DBE02, "DbmFileNotFound" }, + { 0x3DC002, "DbmDirectoryNotFound" }, + { 0x3DC402, "DbmAlreadyExists" }, + { 0x3DC602, "DbmKeyFull" }, + { 0x3DC802, "DbmDirectoryEntryFull" }, + { 0x3DCA02, "DbmFileEntryFull" }, + { 0x3DCC02, "DbmFindFinished" }, + { 0x3DCE02, "DbmFindKeyFinished" }, + { 0x3DD002, "DbmIterationFinished" }, + { 0x3DD402, "DbmInvalidOperation" }, + { 0x3DD602, "DbmInvalidPathFormat" }, + { 0x3DD802, "DbmDirectoryNameTooLong" }, + { 0x3DDA02, "DbmFileNameTooLong" }, + { 0x803, "Busy" }, + { 0x1003, "OutOfMemory" }, + { 0x1203, "OutOfResource" }, + { 0x1803, "OutOfVirtualAddressSpace" }, + { 0x1A03, "ResourceLimit" }, + { 0x3E803, "OutOfHandles" }, + { 0x3EA03, "InvalidHandle" }, + { 0x3EC03, "InvalidCurrentMemoryState" }, + { 0x3EE03, "InvalidTransferMemoryState" }, + { 0x3F003, "InvalidTransferMemorySize" }, + { 0x3F203, "OutOfTransferMemory" }, + { 0x3F403, "OutOfAddressSpace" }, + { 0x3FC03, "SessionClosedForReceive" }, + { 0x3FE03, "SessionClosedForReply" }, + { 0x40003, "ReceiveListBroken" }, + { 0x1204, "InvalidHandle" }, + { 0xFA204, "InvalidArgument" }, + { 0xFA604, "InvalidServerHandle" }, + { 0xFBC04, "InvalidSize" }, + { 0xFCA04, "Cancelled" }, + { 0xFCE04, "Completed" }, + { 0x106E04, "InvalidTask" }, + { 0x205, "InvalidContentStorageBase" }, + { 0x405, "PlaceHolderAlreadyExists" }, + { 0x605, "PlaceHolderNotFound" }, + { 0x805, "ContentAlreadyExists" }, + { 0xA05, "ContentNotFound" }, + { 0xE05, "ContentMetaNotFound" }, + { 0x1005, "AllocationFailed" }, + { 0x1805, "UnknownStorage" }, + { 0xC805, "InvalidContentStorage" }, + { 0xDC05, "InvalidContentMetaDatabase" }, + { 0x10405, "InvalidPackageFormat" }, + { 0x11805, "InvalidContentHash" }, + { 0x14005, "InvalidInstallTaskState" }, + { 0x15405, "InvalidPlaceHolderFile" }, + { 0x16805, "BufferInsufficient" }, + { 0x17C05, "WriteToReadOnlyContentStorage" }, + { 0x19005, "NotEnoughInstallSpace" }, + { 0x1A405, "SystemUpdateNotFoundInPackage" }, + { 0x1B805, "ContentInfoNotFound" }, + { 0x1DA05, "DeltaNotFound" }, + { 0x1E005, "InvalidContentMetaKey" }, + { 0x1F405, "ContentStorageNotActive" }, + { 0x1F605, "GameCardContentStorageNotActive" }, + { 0x1F805, "BuiltInSystemContentStorageNotActive" }, + { 0x1FA05, "BuiltInUserContentStorageNotActive" }, + { 0x1FC05, "SdCardContentStorageNotActive" }, + { 0x20405, "UnknownContentStorageNotActive" }, + { 0x20805, "ContentMetaDatabaseNotActive" }, + { 0x20A05, "GameCardContentMetaDatabaseNotActive" }, + { 0x20C05, "BuiltInSystemContentMetaDatabaseNotActive" }, + { 0x20E05, "BuiltInUserContentMetaDatabaseNotActive" }, + { 0x21005, "SdCardContentMetaDatabaseNotActive" }, + { 0x21805, "UnknownContentMetaDatabaseNotActive" }, + { 0x23005, "IgnorableInstallTicketFailure" }, + { 0x24405, "InstallTaskCancelled" }, + { 0x24605, "CreatePlaceHolderCancelled" }, + { 0x24805, "WritePlaceHolderCancelled" }, + { 0x26C05, "ContentStorageBaseNotFound" }, + { 0x29405, "ListPartiallyNotCommitted" }, + { 0x2D005, "UnexpectedContentMetaPrepared" }, + { 0x2F805, "InvalidFirmwareVariation" }, + { 0x3FEA05, "InvalidArgument" }, + { 0x3FEC05, "InvalidOffset" }, + { 0x206, "EndOfQuery" }, + { 0x406, "InvalidCurrentMemory" }, + { 0x606, "NotSingleRegion" }, + { 0x806, "InvalidMemoryState" }, + { 0xA06, "OutOfMemory" }, + { 0xC06, "OutOfResource" }, + { 0xE06, "NotSupported" }, + { 0x1006, "InvalidHandle" }, + { 0x7FE06, "InternalError" }, + { 0x208, "ResolverNotFound" }, + { 0x408, "ProgramNotFound" }, + { 0x608, "DataNotFound" }, + { 0x808, "UnknownResolver" }, + { 0xA08, "ApplicationNotFound" }, + { 0xC08, "HtmlDocumentNotFound" }, + { 0xE08, "AddOnContentNotFound" }, + { 0x1008, "ControlNotFound" }, + { 0x1208, "LegalInformationNotFound" }, + { 0x1408, "DebugProgramNotFound" }, + { 0xB408, "TooManyRegisteredPaths" }, + { 0x209, "TooLongArgument" }, + { 0x409, "TooManyArguments" }, + { 0x609, "TooLargeMeta" }, + { 0x809, "InvalidMeta" }, + { 0xA09, "InvalidNso" }, + { 0xC09, "InvalidPath" }, + { 0xE09, "TooManyProcesses" }, + { 0x1009, "NotPinned" }, + { 0x1209, "InvalidProgramId" }, + { 0x1409, "InvalidVersion" }, + { 0x1609, "InvalidAcidSignature" }, + { 0x1809, "InvalidNcaSignature" }, + { 0x6609, "InsufficientAddressSpace" }, + { 0x6809, "InvalidNro" }, + { 0x6A09, "InvalidNrr" }, + { 0x6C09, "InvalidSignature" }, + { 0x6E09, "InsufficientNroRegistrations" }, + { 0x7009, "InsufficientNrrRegistrations" }, + { 0x7209, "NroAlreadyLoaded" }, + { 0xA209, "InvalidAddress" }, + { 0xA409, "InvalidSize" }, + { 0xA809, "NotLoaded" }, + { 0xAA09, "NotRegistered" }, + { 0xAC09, "InvalidSession" }, + { 0xAE09, "InvalidProcess" }, + { 0xC809, "UnknownCapability" }, + { 0xCE09, "InvalidCapabilityKernelFlags" }, + { 0xD009, "InvalidCapabilitySyscallMask" }, + { 0xD409, "InvalidCapabilityMapRange" }, + { 0xD609, "InvalidCapabilityMapPage" }, + { 0xDE09, "InvalidCapabilityInterruptPair" }, + { 0xE209, "InvalidCapabilityApplicationType" }, + { 0xE409, "InvalidCapabilityKernelVersion" }, + { 0xE609, "InvalidCapabilityHandleTable" }, + { 0xE809, "InvalidCapabilityDebugFlags" }, + { 0x19009, "InternalError" }, + { 0x20A, "NotSupported" }, + { 0x60A, "PreconditionViolation" }, + { 0x140A, "MemoryAllocationFailed" }, + { 0x160A, "CmifProxyAllocationFailed" }, + { 0x1940A, "InvalidCmifHeaderSize" }, + { 0x1A60A, "InvalidCmifInHeader" }, + { 0x1A80A, "InvalidCmifOutHeader" }, + { 0x1BA0A, "UnknownMethodId" }, + { 0x1CE0A, "InvalidInRawSize" }, + { 0x1D00A, "InvalidOutRawSize" }, + { 0x1D60A, "InvalidInObjectCount" }, + { 0x1D80A, "InvalidOutObjectCount" }, + { 0x1DE0A, "InvalidInObject" }, + { 0x20A0A, "TargetObjectNotFound" }, + { 0x25A0A, "OutOfDomainEntry" }, + { 0x6400A, "RequestContextChanged" }, + { 0x6420A, "RequestInvalidated" }, + { 0x6440A, "RequestInvalidatedByUser" }, + { 0x6560A, "RequestDeferred" }, + { 0x6580A, "RequestDeferredByUser" }, + { 0x20B, "NotSupported" }, + { 0xC80B, "OutOfResource" }, + { 0xCC0B, "OutOfSessionMemory" }, + { 0x1060B, "OutOfSessions" }, + { 0x11A0B, "InsufficientPointerTransferBuffer" }, + { 0x1900B, "OutOfDomains" }, + { 0x2580B, "CommunicationError" }, + { 0x25A0B, "SessionClosed" }, + { 0x3240B, "InvalidRequestSize" }, + { 0x3260B, "UnknownCommandType" }, + { 0x3480B, "InvalidCmifRequest" }, + { 0x3D60B, "TargetNotDomain" }, + { 0x3D80B, "DomainObjectNotFound" }, + { 0x20C, "Unknown" }, + { 0x20D, "Unknown" }, + { 0x40D, "DebuggingDisabled" }, + { 0x20F, "ProcessNotFound" }, + { 0x40F, "AlreadyStarted" }, + { 0x60F, "NotTerminated" }, + { 0x80F, "DebugHookInUse" }, + { 0xA0F, "ApplicationRunning" }, + { 0xC0F, "InvalidSize" }, + { 0xB410, "Canceled" }, + { 0xDC10, "OutOfMaxRunningTask" }, + { 0x21C10, "CardUpdateNotSetup" }, + { 0x23010, "CardUpdateNotPrepared" }, + { 0x24410, "CardUpdateAlreadySetup" }, + { 0x39810, "PrepareCardUpdateAlreadyRequested" }, + { 0x212, "ConnectionFailure" }, + { 0x412, "NotFound" }, + { 0x612, "NotEnoughBuffer" }, + { 0xCA12, "Cancelled" }, + { 0x7FE12, "" }, + { 0xFA212, "" }, + { 0xFA612, "InvalidTaskId" }, + { 0xFB612, "InvalidSize" }, + { 0xFCA12, "TaskCancelled" }, + { 0xFCC12, "TaskNotCompleted" }, + { 0xFCE12, "TaskQueueNotAvailable" }, + { 0x106A12, "" }, + { 0x106C12, "OutOfRpcTask" }, + { 0x109612, "InvalidCategory" }, + { 0x214, "OutOfKeyResource" }, + { 0x414, "KeyNotFound" }, + { 0x814, "AllocationFailed" }, + { 0xA14, "InvalidKeyValue" }, + { 0xC14, "BufferInsufficient" }, + { 0x1014, "InvalidFileSystemState" }, + { 0x1214, "NotCreated" }, + { 0x215, "OutOfProcesses" }, + { 0x415, "InvalidClient" }, + { 0x615, "OutOfSessions" }, + { 0x815, "AlreadyRegistered" }, + { 0xA15, "OutOfServices" }, + { 0xC15, "InvalidServiceName" }, + { 0xE15, "NotRegistered" }, + { 0x1015, "NotAllowed" }, + { 0x1215, "TooLargeAccessControl" }, + { 0x216, "RoError" }, + { 0x416, "OutOfAddressSpace" }, + { 0x616, "AlreadyLoaded" }, + { 0x816, "InvalidNro" }, + { 0xC16, "InvalidNrr" }, + { 0xE16, "TooManyNro" }, + { 0x1016, "TooManyNrr" }, + { 0x1216, "NotAuthorized" }, + { 0x1416, "InvalidNrrKind" }, + { 0x7FE16, "InternalError" }, + { 0x80216, "InvalidAddress" }, + { 0x80416, "InvalidSize" }, + { 0x80816, "NotLoaded" }, + { 0x80A16, "NotRegistered" }, + { 0x80C16, "InvalidSession" }, + { 0x80E16, "InvalidProcess" }, + { 0x218, "NoDevice" }, + { 0x418, "NotActivated" }, + { 0x618, "DeviceRemoved" }, + { 0x818, "NotAwakened" }, + { 0x4018, "CommunicationError" }, + { 0x4218, "CommunicationNotAttained" }, + { 0x4418, "ResponseIndexError" }, + { 0x4618, "ResponseEndBitError" }, + { 0x4818, "ResponseCrcError" }, + { 0x4A18, "ResponseTimeoutError" }, + { 0x4C18, "DataEndBitError" }, + { 0x4E18, "DataCrcError" }, + { 0x5018, "DataTimeoutError" }, + { 0x5218, "AutoCommandResponseIndexError" }, + { 0x5418, "AutoCommandResponseEndBitError" }, + { 0x5618, "AutoCommandResponseCrcError" }, + { 0x5818, "AutoCommandResponseTimeoutError" }, + { 0x5A18, "CommandCompleteSoftwareTimeout" }, + { 0x5C18, "TransferCompleteSoftwareTimeout" }, + { 0x6018, "DeviceStatusHasError" }, + { 0x6218, "DeviceStatusAddressOutOfRange" }, + { 0x6418, "DeviceStatusAddressMisaligned" }, + { 0x6618, "DeviceStatusBlockLenError" }, + { 0x6818, "DeviceStatusEraseSeqError" }, + { 0x6A18, "DeviceStatusEraseParam" }, + { 0x6C18, "DeviceStatusWpViolation" }, + { 0x6E18, "DeviceStatusLockUnlockFailed" }, + { 0x7018, "DeviceStatusComCrcError" }, + { 0x7218, "DeviceStatusIllegalCommand" }, + { 0x7418, "DeviceStatusDeviceEccFailed" }, + { 0x7618, "DeviceStatusCcError" }, + { 0x7818, "DeviceStatusError" }, + { 0x7A18, "DeviceStatusCidCsdOverwrite" }, + { 0x7C18, "DeviceStatusWpEraseSkip" }, + { 0x7E18, "DeviceStatusEraseReset" }, + { 0x8018, "DeviceStatusSwitchError" }, + { 0x9018, "UnexpectedDeviceState" }, + { 0x9218, "UnexpectedDeviceCsdValue" }, + { 0x9418, "AbortTransactionSoftwareTimeout" }, + { 0x9618, "CommandInhibitCmdSoftwareTimeout" }, + { 0x9818, "CommandInhibitDatSoftwareTimeout" }, + { 0x9A18, "BusySoftwareTimeout" }, + { 0x9C18, "IssueTuningCommandSoftwareTimeout" }, + { 0x9E18, "TuningFailed" }, + { 0xA018, "MmcInitializationSoftwareTimeout" }, + { 0xA218, "MmcNotSupportExtendedCsd" }, + { 0xA418, "UnexpectedMmcExtendedCsdValue" }, + { 0xA618, "MmcEraseSoftwareTimeout" }, + { 0xA818, "SdCardValidationError" }, + { 0xAA18, "SdCardInitializationSoftwareTimeout" }, + { 0xAC18, "SdCardGetValidRcaSoftwareTimeout" }, + { 0xAE18, "UnexpectedSdCardAcmdDisabled" }, + { 0xB018, "SdCardNotSupportSwitchFunctionStatus" }, + { 0xB218, "UnexpectedSdCardSwitchFunctionStatus" }, + { 0xB418, "SdCardNotSupportAccessMode" }, + { 0xB618, "SdCardNot4BitBusWidthAtUhsIMode" }, + { 0xB818, "SdCardNotSupportSdr104AndSdr50" }, + { 0xBA18, "SdCardCannotSwitchAccessMode" }, + { 0xBC18, "SdCardFailedSwitchAccessMode" }, + { 0xBE18, "SdCardUnacceptableCurrentConsumption" }, + { 0xC018, "SdCardNotReadyToVoltageSwitch" }, + { 0xC218, "SdCardNotCompleteVoltageSwitch" }, + { 0x10018, "HostControllerUnexpected" }, + { 0x10218, "InternalClockStableSoftwareTimeout" }, + { 0x10418, "SdHostStandardUnknownAutoCmdError" }, + { 0x10618, "SdHostStandardUnknownError" }, + { 0x10818, "SdmmcDllCalibrationSoftwareTimeout" }, + { 0x10A18, "SdmmcDllApplicationSoftwareTimeout" }, + { 0x10C18, "SdHostStandardFailSwitchTo18V" }, + { 0x10E18, "DriveStrengthCalibrationNotCompleted" }, + { 0x11018, "DriveStrengthCalibrationSoftwareTimeout" }, + { 0x11218, "SdmmcCompShortToGnd" }, + { 0x11418, "SdmmcCompOpen" }, + { 0x14018, "InternalError" }, + { 0x14218, "NoWaitedInterrupt" }, + { 0x14418, "WaitInterruptSoftwareTimeout" }, + { 0x18018, "AbortCommandIssued" }, + { 0x19018, "NotSupported" }, + { 0x19218, "NotImplemented" }, + { 0x1A, "SecureMonitorError" }, + { 0x21A, "SecureMonitorNotImplemented" }, + { 0x41A, "SecureMonitorInvalidArgument" }, + { 0x61A, "SecureMonitorBusy" }, + { 0x81A, "SecureMonitorNoAsyncOperation" }, + { 0xA1A, "SecureMonitorInvalidAsyncOperation" }, + { 0xC1A, "SecureMonitorNotPermitted" }, + { 0xE1A, "SecureMonitorNotInitialized" }, + { 0xC81A, "InvalidSize" }, + { 0xCA1A, "UnknownSecureMonitorError" }, + { 0xCC1A, "DecryptionFailed" }, + { 0xD01A, "OutOfKeySlots" }, + { 0xD21A, "InvalidKeySlot" }, + { 0xD41A, "BootReasonAlreadySet" }, + { 0xD61A, "BootReasonNotSet" }, + { 0xD81A, "InvalidArgument" }, + { 0x21B, "InsufficientProvidedMemory" }, + { 0x21D, "ConnectionFailure" }, + { 0x61D, "UnknownDriverType" }, + { 0xA1D, "NonBlockingReceiveFailed" }, + { 0x101D, "ChannelWaitCancelled" }, + { 0x121D, "ChannelAlreadyExist" }, + { 0x141D, "ChannelNotExist" }, + { 0x12E1D, "OutOfChannel" }, + { 0x1301D, "OutOfTask" }, + { 0x1901D, "InvalidChannelState" }, + { 0x1921D, "InvalidChannelStateDisconnected" }, + { 0x7D01D, "InternalError" }, + { 0x7D21D, "Overflow" }, + { 0x7D41D, "OutOfMemory" }, + { 0x7D61D, "InvalidArgument" }, + { 0x7D81D, "ProtocolError" }, + { 0x7DA1D, "Cancelled" }, + { 0x8981D, "MuxError" }, + { 0x89A1D, "ChannelBufferOverflow" }, + { 0x89C1D, "ChannelBufferHasNotEnoughData" }, + { 0x89E1D, "ChannelVersionNotMatched" }, + { 0x8A01D, "ChannelStateTransitionError" }, + { 0x8A41D, "ChannelReceiveBufferEmpty" }, + { 0x8A61D, "ChannelSequenceIdNotMatched" }, + { 0x8A81D, "ChannelCannotDiscard" }, + { 0x9601D, "DriverError" }, + { 0x9621D, "DriverOpened" }, + { 0xA281D, "SocketDriverError" }, + { 0xA2A1D, "SocketSocketExemptError" }, + { 0xA2C1D, "SocketBindError" }, + { 0xA301D, "SocketListenError" }, + { 0xA321D, "SocketAcceptError" }, + { 0xA341D, "SocketReceiveError" }, + { 0xA361D, "SocketSendError" }, + { 0xA381D, "SocketReceiveFromError" }, + { 0xA3A1D, "SocketSendToError" }, + { 0xA3C1D, "SocketSetSockOptError" }, + { 0xA3E1D, "SocketGetSockNameError" }, + { 0xAF01D, "UsbDriverError" }, + { 0xAF21D, "UsbDriverUnknownError" }, + { 0xAF41D, "UsbDriverBusyError" }, + { 0xAF61D, "UsbDriverReceiveError" }, + { 0xAF81D, "UsbDriverSendError" }, + { 0xFA01D, "HtcctrlError" }, + { 0xFA21D, "HtcctrlStateTransitionNotAllowed" }, + { 0xFA41D, "HtcctrlReceiveUnexpectedPacket" }, + { 0x21E, "OutOfResource" }, + { 0x41E, "NotSupported" }, + { 0x61E, "InvalidArgument" }, + { 0x81E, "PermissionDenied" }, + { 0xA1E, "AccessModeDenied" }, + { 0xC1E, "DeviceCodeNotFound" }, + { 0x61F, "InvalidArgument" }, + { 0xC81F, "ConnectionFailure" }, + { 0xCA1F, "HtclowChannelClosed" }, + { 0xDC1F, "UnexpectedResponse" }, + { 0xDE1F, "UnexpectedResponseProtocolId" }, + { 0xE01F, "UnexpectedResponseProtocolVersion" }, + { 0xE21F, "UnexpectedResponsePacketCategory" }, + { 0xE41F, "UnexpectedResponsePacketType" }, + { 0xE61F, "UnexpectedResponseBodySize" }, + { 0xE81F, "UnexpectedResponseBody" }, + { 0x1901F, "InternalError" }, + { 0x1921F, "InvalidSize" }, + { 0x1A61F, "UnknownError" }, + { 0x1A81F, "UnsupportedProtocolVersion" }, + { 0x1AA1F, "InvalidRequest" }, + { 0x1AC1F, "InvalidHandle" }, + { 0x1AE1F, "OutOfHandle" }, + { 0x265, "NoAck" }, + { 0x465, "BusBusy" }, + { 0x665, "CommandListFull" }, + { 0xA65, "UnknownDevice" }, + { 0x1FA65, "Timeout" }, + { 0x266, "AlreadyBound" }, + { 0x466, "AlreadyOpen" }, + { 0x666, "DeviceNotFound" }, + { 0x866, "InvalidArgument" }, + { 0xC66, "NotOpen" }, + { 0x1669, "SettingsItemNotFound" }, + { 0xC869, "InternalError" }, + { 0xCA69, "SettingsItemKeyAllocationFailed" }, + { 0xCC69, "SettingsItemValueAllocationFailed" }, + { 0x19069, "InvalidArgument" }, + { 0x19269, "SettingsNameNull" }, + { 0x19469, "SettingsItemKeyNull" }, + { 0x19669, "SettingsItemValueNull" }, + { 0x19869, "SettingsItemKeyBufferNull" }, + { 0x19A69, "SettingsItemValueBufferNull" }, + { 0x1BA69, "SettingsNameEmpty" }, + { 0x1BC69, "SettingsItemKeyEmpty" }, + { 0x1E269, "SettingsNameTooLong" }, + { 0x1E469, "SettingsItemKeyTooLong" }, + { 0x20A69, "SettingsNameInvalidFormat" }, + { 0x20C69, "SettingsItemKeyInvalidFormat" }, + { 0x20E69, "SettingsItemValueInvalidFormat" }, + { 0x48869, "CalibrationDataError" }, + { 0x48A69, "CalibrationDataFileSystemCorrupted" }, + { 0x48C69, "CalibrationDataCrcError" }, + { 0x48E69, "CalibrationDataShaError" }, + { 0x272, "OperationFailed" }, + { 0xC72, "NotSupported" }, + { 0xE72, "NotFound" }, + { 0x74, "NotInitialized" }, + { 0x274, "NoCapability" }, + { 0xCC74, "OffsetInvalid" }, + { 0xCE74, "UninitializedClock" }, + { 0x19074, "NotComparable" }, + { 0x19274, "Overflowed" }, + { 0x64274, "OutOfMemory" }, + { 0x70874, "InvalidArgument" }, + { 0x70A74, "InvalidPointer" }, + { 0x70C74, "OutOfRange" }, + { 0x70E74, "InvalidTimeZoneBinary" }, + { 0x7BA74, "NotFound" }, + { 0x7BC74, "NotImplemented" }, + { 0x27A, "InvalidArgument" }, + { 0x47A, "NotFound" }, + { 0x67A, "TargetLocked" }, + { 0x87A, "TargetAlreadyMounted" }, + { 0xA7A, "TargetNotMounted" }, + { 0xC7A, "AlreadyOpen" }, + { 0xE7A, "NotOpen" }, + { 0x107A, "InternetRequestDenied" }, + { 0x127A, "ServiceOpenLimitReached" }, + { 0x147A, "SaveDataNotFound" }, + { 0x3E7A, "NetworkServiceAccountNotAvailable" }, + { 0xA07A, "PassphrasePathNotFound" }, + { 0xA27A, "DataVerificationFailed" }, + { 0xB47A, "PermissionDenied" }, + { 0xB67A, "AllocationFailed" }, + { 0xC47A, "InvalidOperation" }, + { 0x1987A, "InvalidDeliveryCacheStorageFile" }, + { 0x19A7A, "StorageOpenLimitReached" }, + { 0x7B, "SslService" }, + { 0x7C, "Cancelled" }, + { 0x27C, "CancelledByUser" }, + { 0xC87C, "UserNotExist" }, + { 0x1907C, "NetworkServiceAccountUnavailable" }, + { 0x35C7C, "TokenCacheUnavailable" }, + { 0x17707C, "NetworkCommunicationError" }, + { 0x2085, "IllegalRequest" }, + { 0x8C89, "HttpConnectionCanceled" }, + { 0x48A, "AlreadyInitialized" }, + { 0x68A, "NotInitialized" }, + { 0x8C, "NotInitialized" }, + { 0x28C, "AlreadyInitialized" }, + { 0xC88C, "InvalidParameter" }, + { 0xCE8C, "AlignmentError" }, + { 0x1928C, "OperationDenied" }, + { 0x1948C, "MemAllocFailure" }, + { 0x19C8C, "ResourceBusy" }, + { 0x19E8C, "InternalStateError" }, + { 0x3228C, "TransactionError" }, + { 0x3328C, "Interrupted" }, + { 0x293, "NotInitialized" }, + { 0x493, "AlreadyInitialized" }, + { 0x693, "OutOfArraySpace" }, + { 0x893, "OutOfFieldSpace" }, + { 0xA93, "OutOfMemory" }, + { 0xC93, "NotSupported" }, + { 0xE93, "InvalidArgument" }, + { 0x1093, "NotFound" }, + { 0x1293, "FieldCategoryMismatch" }, + { 0x1493, "FieldTypeMismatch" }, + { 0x1693, "AlreadyExists" }, + { 0x1893, "CorruptJournal" }, + { 0x1A93, "CategoryNotFound" }, + { 0x1C93, "RequiredContextMissing" }, + { 0x1E93, "RequiredFieldMissing" }, + { 0x2093, "FormatterError" }, + { 0x2293, "InvalidPowerState" }, + { 0x2493, "ArrayFieldTooLarge" }, + { 0x2693, "AlreadyOwned" }, + { 0x49E, "BootImagePackageNotFound" }, + { 0x69E, "InvalidBootImagePackage" }, + { 0x89E, "TooSmallWorkBuffer" }, + { 0xA9E, "NotAlignedWorkBuffer" }, + { 0xC9E, "NeedsRepairBootImages" }, + { 0x2A2, "ApplicationAborted" }, + { 0x4A2, "SystemModuleAborted" }, + { 0x2A3, "AllocationFailed" }, + { 0x4A3, "NullGraphicsBuffer" }, + { 0x6A3, "AlreadyThrown" }, + { 0x8A3, "TooManyEvents" }, + { 0xAA3, "InRepairWithoutVolHeld" }, + { 0xCA3, "InRepairWithoutTimeReviserCartridge" }, + { 0xA8, "UndefinedInstruction" }, + { 0x2A8, "InstructionAbort" }, + { 0x4A8, "DataAbort" }, + { 0x6A8, "AlignmentFault" }, + { 0x8A8, "DebuggerAttached" }, + { 0xAA8, "BreakPoint" }, + { 0xCA8, "UserBreak" }, + { 0xEA8, "DebuggerBreak" }, + { 0x10A8, "UndefinedSystemCall" }, + { 0x12A8, "MemorySystemError" }, + { 0xC6A8, "IncompleteReport" }, + { 0x2B7, "CannotDebug" }, + { 0x4B7, "AlreadyAttached" }, + { 0x6B7, "Cancelled" }, + { 0x4BD, "InvalidArgument" }, + { 0x2C6, "NotSupported" }, + { 0x4C6, "InvalidArgument" }, + { 0x6C6, "NotAvailable" }, + { 0xCAC6, "CalibrationDataCrcError" }, + { 0x118CA, "Invalid" }, + { 0x4B2CA, "DualConnected" }, + { 0x4B4CA, "SameJoyTypeConnected" }, + { 0x4B6CA, "ColorNotAvailable" }, + { 0x4B8CA, "ControllerNotConnected" }, + { 0x183ACA, "Canceled" }, + { 0x183CCA, "NotSupportedNpadStyle" }, + { 0x1900CA, "ControllerFirmwareUpdateError" }, + { 0x1902CA, "ControllerFirmwareUpdateFailed" }, + { 0x4CC, "UnknownCommand" }, + { 0x8CC, "OutOfResource" }, + { 0xECC, "NoSocket" }, + { 0xDCCD, "IrsensorUnavailable" }, + { 0xDECD, "IrsensorUnsupported" }, + { 0xF0CD, "IrsensorNotReady" }, + { 0xF4CD, "IrsensorDeviceError" }, + { 0x4CE, "AlbumError" }, + { 0x6CE, "AlbumWorkMemoryError" }, + { 0xECE, "AlbumAlreadyOpened" }, + { 0x10CE, "AlbumOutOfRange" }, + { 0x14CE, "AlbumInvalidFileId" }, + { 0x16CE, "AlbumInvalidApplicationId" }, + { 0x18CE, "AlbumInvalidTimestamp" }, + { 0x1ACE, "AlbumInvalidStorage" }, + { 0x1CCE, "AlbumInvalidFileContents" }, + { 0x2ACE, "AlbumIsNotMounted" }, + { 0x2CCE, "AlbumIsFull" }, + { 0x2ECE, "AlbumFileNotFound" }, + { 0x30CE, "AlbumInvalidFileData" }, + { 0x32CE, "AlbumFileCountLimit" }, + { 0x34CE, "AlbumFileNoThumbnail" }, + { 0x3CCE, "AlbumReadBufferShortage" }, + { 0xB4CE, "AlbumFileSystemError" }, + { 0xBCCE, "AlbumAccessCorrupted" }, + { 0xC0CE, "AlbumDestinationAccessCorrupted" }, + { 0x640CE, "ControlError" }, + { 0x668CE, "ControlResourceLimit" }, + { 0x66CCE, "ControlNotOpened" }, + { 0x7FECE, "NotSupported" }, + { 0x800CE, "InternalError" }, + { 0x974CE, "InternalJpegEncoderError" }, + { 0x978CE, "InternalJpegWorkMemoryShortage" }, + { 0xA28CE, "InternalFileDataVerificationError" }, + { 0xA2ACE, "InternalFileDataVerificationEmptyFileData" }, + { 0xA2CCE, "InternalFileDataVerificationExifExtractionFailed" }, + { 0xA2ECE, "InternalFileDataVerificationExifAnalyzationFailed" }, + { 0xA30CE, "InternalFileDataVerificationDateTimeExtractionFailed" }, + { 0xA32CE, "InternalFileDataVerificationInvalidDateTimeLength" }, + { 0xA34CE, "InternalFileDataVerificationInconsistentDateTime" }, + { 0xA36CE, "InternalFileDataVerificationMakerNoteExtractionFailed" }, + { 0xA38CE, "InternalFileDataVerificationInconsistentApplicationId" }, + { 0xA3ACE, "InternalFileDataVerificationInconsistentSignature" }, + { 0xA3CCE, "InternalFileDataVerificationUnsupportedOrientation" }, + { 0xA3ECE, "InternalFileDataVerificationInvalidDataDimension" }, + { 0xA40CE, "InternalFileDataVerificationInconsistentOrientation" }, + { 0xAF0CE, "InternalAlbumLimitationError" }, + { 0xAF2CE, "InternalAlbumLimitationFileCountLimit" }, + { 0xBB8CE, "InternalSignatureError" }, + { 0xBBACE, "InternalSignatureExifExtractionFailed" }, + { 0xBBCCE, "InternalSignatureMakerNoteExtractionFailed" }, + { 0xD48CE, "InternalAlbumSessionError" }, + { 0xD4ACE, "InternalAlbumLimitationSessionCountLimit" }, + { 0xED8CE, "InternalAlbumTemporaryFileError" }, + { 0xEDACE, "InternalAlbumTemporaryFileCountLimit" }, + { 0xEDCCE, "InternalAlbumTemporaryFileCreateError" }, + { 0xEDECE, "InternalAlbumTemporaryFileCreateRetryCountLimit" }, + { 0xEE0CE, "InternalAlbumTemporaryFileOpenError" }, + { 0xEE2CE, "InternalAlbumTemporaryFileGetFileSizeError" }, + { 0xEE4CE, "InternalAlbumTemporaryFileSetFileSizeError" }, + { 0xEE6CE, "InternalAlbumTemporaryFileReadFileError" }, + { 0xEE8CE, "InternalAlbumTemporaryFileWriteFileError" }, + { 0x2E4, "NotImplemented" }, + { 0x4E4, "NotAvailable" }, + { 0x6E4, "ApplicationNotRunning" }, + { 0x8E4, "BufferNotEnough" }, + { 0xAE4, "ApplicationContentNotFound" }, + { 0xCE4, "ContentMetaNotFound" }, + { 0xEE4, "OutOfMemory" }, + { 0x3AC, "InvalidArgument" }, + { 0x5AC, "NullArgument" }, + { 0x7AC, "ArgumentOutOfRange" }, + { 0x9AC, "BufferTooSmall" }, + { 0x67AC, "ServiceNotInitialized" }, + { 0xCBAC, "NotImplemented" }, + { 0x7D1AC, "InvalidData" }, + { 0x7D3AC, "InvalidInitialProcessData" }, + { 0x7D5AC, "InvalidKip" }, + { 0x7D7AC, "InvalidKipFileSize" }, + { 0x7D9AC, "InvalidKipMagic" }, + { 0x7DBAC, "InvalidKipSegmentSize" }, + { 0x7DDAC, "KipSegmentDecompressionFailed" }, + { 0x7E5AC, "InvalidIni" }, + { 0x7E7AC, "InvalidIniFileSize" }, + { 0x7E9AC, "InvalidIniMagic" }, + { 0x7EBAC, "InvalidIniProcessCount" }, + { 0x7F9AC, "InvalidPackage2" }, + { 0x7FBAC, "InvalidPackage2HeaderSignature" }, + { 0x7FDAC, "InvalidPackage2MetaSizeA" }, + { 0x7FFAC, "InvalidPackage2MetaSizeB" }, + { 0x801AC, "InvalidPackage2MetaKeyGeneration" }, + { 0x803AC, "InvalidPackage2MetaMagic" }, + { 0x805AC, "InvalidPackage2MetaEntryPointAlignment" }, + { 0x807AC, "InvalidPackage2MetaPayloadAlignment" }, + { 0x809AC, "InvalidPackage2MetaPayloadSizeAlignment" }, + { 0x80BAC, "InvalidPackage2MetaTotalSize" }, + { 0x80DAC, "InvalidPackage2MetaPayloadSize" }, + { 0x80FAC, "InvalidPackage2MetaPayloadsOverlap" }, + { 0x811AC, "InvalidPackage2MetaEntryPointNotFound" }, + { 0x813AC, "InvalidPackage2PayloadCorrupted" }, + { 0x821AC, "InvalidPackage1" }, + { 0x823AC, "InvalidPackage1SectionSize" }, + { 0x825AC, "InvalidPackage1MarikoBodySize" }, + { 0x827AC, "InvalidPackage1Pk11Size" } + }; + + public static bool TryGet(int errorCode, out string name) + { + return _names.TryGetValue(errorCode, out name); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj b/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj new file mode 100644 index 000000000..d04c5a9b6 --- /dev/null +++ b/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj @@ -0,0 +1,11 @@ + + + + net7.0 + + + + + + + diff --git a/Ryujinx.Horizon.Common/ThreadTerminatedException.cs b/Ryujinx.Horizon.Common/ThreadTerminatedException.cs new file mode 100644 index 000000000..c86cb05fa --- /dev/null +++ b/Ryujinx.Horizon.Common/ThreadTerminatedException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Ryujinx.Horizon.Common +{ + public class ThreadTerminatedException : Exception + { + public ThreadTerminatedException() : base("The thread has been terminated.") + { + } + + public ThreadTerminatedException(string message) : base(message) + { + } + + public ThreadTerminatedException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Ryujinx.Horizon.Generators/CodeGenerator.cs b/Ryujinx.Horizon.Generators/CodeGenerator.cs index 80a33c66c..3a479eb74 100644 --- a/Ryujinx.Horizon.Generators/CodeGenerator.cs +++ b/Ryujinx.Horizon.Generators/CodeGenerator.cs @@ -24,10 +24,10 @@ namespace Ryujinx.Horizon.Generators IncreaseIndentation(); } - public void LeaveScope() + public void LeaveScope(string suffix = "") { DecreaseIndentation(); - AppendLine("}"); + AppendLine($"}}{suffix}"); } public void IncreaseIndentation() diff --git a/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs b/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs new file mode 100644 index 000000000..b859f1f37 --- /dev/null +++ b/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Horizon.Generators.Hipc +{ + enum CommandArgType : byte + { + Invalid, + + Buffer, + InArgument, + InCopyHandle, + InMoveHandle, + InObject, + OutArgument, + OutCopyHandle, + OutMoveHandle, + OutObject, + ProcessId + } +} diff --git a/Ryujinx.Horizon.Generators/Hipc/CommandInterface.cs b/Ryujinx.Horizon.Generators/Hipc/CommandInterface.cs new file mode 100644 index 000000000..2ee192820 --- /dev/null +++ b/Ryujinx.Horizon.Generators/Hipc/CommandInterface.cs @@ -0,0 +1,17 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Generators.Hipc +{ + class CommandInterface + { + public ClassDeclarationSyntax ClassDeclarationSyntax { get; } + public List CommandImplementations { get; } + + public CommandInterface(ClassDeclarationSyntax classDeclarationSyntax) + { + ClassDeclarationSyntax = classDeclarationSyntax; + CommandImplementations = new List(); + } + } +} diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs new file mode 100644 index 000000000..a66d57a3b --- /dev/null +++ b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -0,0 +1,749 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Horizon.Generators.Hipc +{ + [Generator] + class HipcGenerator : ISourceGenerator + { + private const string ArgVariablePrefix = "arg"; + private const string ResultVariableName = "result"; + private const string IsBufferMapAliasVariableName = "isBufferMapAlias"; + private const string InObjectsVariableName = "inObjects"; + private const string OutObjectsVariableName = "outObjects"; + private const string ResponseVariableName = "response"; + private const string OutRawDataVariableName = "outRawData"; + + private const string TypeSystemReadOnlySpan = "System.ReadOnlySpan"; + private const string TypeSystemSpan = "System.Span"; + private const string TypeStructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute"; + + public const string CommandAttributeName = "CmifCommandAttribute"; + + private const string TypeResult = "Ryujinx.Horizon.Common.Result"; + private const string TypeBufferAttribute = "Ryujinx.Horizon.Sdk.Sf.BufferAttribute"; + private const string TypeCopyHandleAttribute = "Ryujinx.Horizon.Sdk.Sf.CopyHandleAttribute"; + private const string TypeMoveHandleAttribute = "Ryujinx.Horizon.Sdk.Sf.MoveHandleAttribute"; + private const string TypeClientProcessIdAttribute = "Ryujinx.Horizon.Sdk.Sf.ClientProcessIdAttribute"; + private const string TypeCommandAttribute = "Ryujinx.Horizon.Sdk.Sf." + CommandAttributeName; + private const string TypeIServiceObject = "Ryujinx.Horizon.Sdk.Sf.IServiceObject"; + + private enum Modifier + { + None, + Ref, + Out, + In + } + + private struct OutParameter + { + public readonly string Name; + public readonly string TypeName; + public readonly int Index; + public readonly CommandArgType Type; + + public OutParameter(string name, string typeName, int index, CommandArgType type) + { + Name = name; + TypeName = typeName; + Index = index; + Type = type; + } + } + + public void Execute(GeneratorExecutionContext context) + { + HipcSyntaxReceiver syntaxReceiver = (HipcSyntaxReceiver)context.SyntaxReceiver; + + foreach (var commandInterface in syntaxReceiver.CommandInterfaces) + { + if (!NeedsIServiceObjectImplementation(context.Compilation, commandInterface.ClassDeclarationSyntax)) + { + continue; + } + + CodeGenerator generator = new CodeGenerator(); + string className = commandInterface.ClassDeclarationSyntax.Identifier.ToString(); + + generator.AppendLine("using Ryujinx.Horizon.Common;"); + generator.AppendLine("using Ryujinx.Horizon.Sdk.Sf;"); + generator.AppendLine("using Ryujinx.Horizon.Sdk.Sf.Cmif;"); + generator.AppendLine("using Ryujinx.Horizon.Sdk.Sf.Hipc;"); + generator.AppendLine("using System;"); + generator.AppendLine("using System.Collections.Generic;"); + generator.AppendLine("using System.Runtime.CompilerServices;"); + generator.AppendLine("using System.Runtime.InteropServices;"); + generator.AppendLine(); + generator.EnterScope($"namespace {GetNamespaceName(commandInterface.ClassDeclarationSyntax)}"); + generator.EnterScope($"partial class {className}"); + + GenerateMethodTable(generator, context.Compilation, commandInterface); + + foreach (var method in commandInterface.CommandImplementations) + { + generator.AppendLine(); + + GenerateMethod(generator, context.Compilation, method); + } + + generator.LeaveScope(); + generator.LeaveScope(); + + context.AddSource($"{className}.g.cs", generator.ToString()); + } + } + + private static string GetNamespaceName(SyntaxNode syntaxNode) + { + while (syntaxNode != null && !(syntaxNode is NamespaceDeclarationSyntax)) + { + syntaxNode = syntaxNode.Parent; + } + + if (syntaxNode == null) + { + return string.Empty; + } + + return ((NamespaceDeclarationSyntax)syntaxNode).Name.ToString(); + } + + private static void GenerateMethodTable(CodeGenerator generator, Compilation compilation, CommandInterface commandInterface) + { + generator.EnterScope($"public IReadOnlyDictionary GetCommandHandlers()"); + generator.EnterScope($"return new Dictionary()"); + + foreach (var method in commandInterface.CommandImplementations) + { + foreach (var commandId in GetAttributeAguments(compilation, method, TypeCommandAttribute, 0)) + { + string[] args = new string[method.ParameterList.Parameters.Count]; + + int index = 0; + + foreach (var parameter in method.ParameterList.Parameters) + { + string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); + CommandArgType argType = GetCommandArgType(compilation, parameter); + + string arg; + + if (argType == CommandArgType.Buffer) + { + string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0); + string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1); + + if (bufferFixedSize != null) + { + arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})"; + } + else + { + arg = $"new CommandArg({bufferFlags})"; + } + } + else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument) + { + string alignment = GetTypeAlignmentExpression(compilation, parameter.Type); + + arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})"; + } + else + { + arg = $"new CommandArg(CommandArgType.{argType})"; + } + + args[index++] = arg; + } + + generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)}) }},"); + } + } + + generator.LeaveScope(";"); + generator.LeaveScope(); + } + + private static IEnumerable GetAttributeAguments(Compilation compilation, SyntaxNode syntaxNode, string attributeName, int argIndex) + { + ISymbol symbol = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode); + + foreach (var attribute in symbol.GetAttributes()) + { + if (attribute.AttributeClass.ToDisplayString() == attributeName && (uint)argIndex < (uint)attribute.ConstructorArguments.Length) + { + yield return attribute.ConstructorArguments[argIndex].ToCSharpString(); + } + } + } + + private static string GetFirstAttributeAgument(Compilation compilation, SyntaxNode syntaxNode, string attributeName, int argIndex) + { + return GetAttributeAguments(compilation, syntaxNode, attributeName, argIndex).FirstOrDefault(); + } + + private static void GenerateMethod(CodeGenerator generator, Compilation compilation, MethodDeclarationSyntax method) + { + int inObjectsCount = 0; + int outObjectsCount = 0; + int buffersCount = 0; + + foreach (var parameter in method.ParameterList.Parameters) + { + if (IsObject(compilation, parameter)) + { + if (IsIn(parameter)) + { + inObjectsCount++; + } + else + { + outObjectsCount++; + } + } + else if (IsBuffer(compilation, parameter)) + { + buffersCount++; + } + } + + generator.EnterScope($"private Result {method.Identifier.Text}(" + + "ref ServiceDispatchContext context, " + + "HipcCommandProcessor processor, " + + "ServerMessageRuntimeMetadata runtimeMetadata, " + + "ReadOnlySpan inRawData, " + + "ref Span outHeader)"); + + bool returnsResult = method.ReturnType != null && GetCanonicalTypeName(compilation, method.ReturnType) == TypeResult; + + if (returnsResult || buffersCount != 0 || inObjectsCount != 0) + { + generator.AppendLine($"Result {ResultVariableName};"); + + if (buffersCount != 0) + { + generator.AppendLine($"bool[] {IsBufferMapAliasVariableName} = new bool[{method.ParameterList.Parameters.Count}];"); + generator.AppendLine(); + + generator.AppendLine($"{ResultVariableName} = processor.ProcessBuffers(ref context, {IsBufferMapAliasVariableName}, runtimeMetadata);"); + generator.EnterScope($"if ({ResultVariableName}.IsFailure)"); + generator.AppendLine($"return {ResultVariableName};"); + generator.LeaveScope(); + } + + generator.AppendLine(); + } + + List outParameters = new List(); + + string[] args = new string[method.ParameterList.Parameters.Count]; + + if (inObjectsCount != 0) + { + generator.AppendLine($"var {InObjectsVariableName} = new IServiceObject[{inObjectsCount}];"); + generator.AppendLine(); + + generator.AppendLine($"{ResultVariableName} = processor.GetInObjects(context.Processor, {InObjectsVariableName});"); + generator.EnterScope($"if ({ResultVariableName}.IsFailure)"); + generator.AppendLine($"return {ResultVariableName};"); + generator.LeaveScope(); + generator.AppendLine(); + } + + if (outObjectsCount != 0) + { + generator.AppendLine($"var {OutObjectsVariableName} = new IServiceObject[{outObjectsCount}];"); + } + + int index = 0; + int inCopyHandleIndex = 0; + int inMoveHandleIndex = 0; + int inObjectIndex = 0; + + foreach (var parameter in method.ParameterList.Parameters) + { + string name = parameter.Identifier.Text; + string argName = GetPrefixedArgName(name); + string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); + CommandArgType argType = GetCommandArgType(compilation, parameter); + Modifier modifier = GetModifier(parameter); + bool isNonSpanBuffer = false; + + if (modifier == Modifier.Out) + { + if (IsNonSpanOutBuffer(compilation, parameter)) + { + generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}));"); + + argName = $"out {GenerateSpanCastElement0(canonicalTypeName, $"{argName}.Memory.Span")}"; + } + else + { + outParameters.Add(new OutParameter(argName, canonicalTypeName, index, argType)); + + argName = $"out {canonicalTypeName} {argName}"; + } + } + else + { + string value = $"default({canonicalTypeName})"; + + switch (argType) + { + case CommandArgType.InArgument: + value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({index}))"; + break; + case CommandArgType.InCopyHandle: + value = $"CommandSerialization.DeserializeCopyHandle(ref context, {inCopyHandleIndex++})"; + break; + case CommandArgType.InMoveHandle: + value = $"CommandSerialization.DeserializeMoveHandle(ref context, {inMoveHandleIndex++})"; + break; + case CommandArgType.ProcessId: + value = "CommandSerialization.DeserializeClientProcessId(ref context)"; + break; + case CommandArgType.InObject: + value = $"{InObjectsVariableName}[{inObjectIndex++}]"; + break; + case CommandArgType.Buffer: + if (IsReadOnlySpan(compilation, parameter)) + { + string spanGenericTypeName = GetCanonicalTypeNameOfGenericArgument(compilation, parameter.Type, 0); + value = GenerateSpanCast(spanGenericTypeName, $"CommandSerialization.GetReadOnlySpan(processor.GetBufferRange({index}))"); + } + else if (IsSpan(compilation, parameter)) + { + value = $"CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}))"; + } + else + { + value = $"CommandSerialization.GetRef<{canonicalTypeName}>(processor.GetBufferRange({index}))"; + isNonSpanBuffer = true; + } + break; + } + + if (IsSpan(compilation, parameter)) + { + generator.AppendLine($"using var {argName} = {value};"); + + string spanGenericTypeName = GetCanonicalTypeNameOfGenericArgument(compilation, parameter.Type, 0); + argName = GenerateSpanCast(spanGenericTypeName, $"{argName}.Memory.Span"); ; + } + else if (isNonSpanBuffer) + { + generator.AppendLine($"ref var {argName} = ref {value};"); + } + else if (argType == CommandArgType.InObject) + { + generator.EnterScope($"if (!({value} is {canonicalTypeName} {argName}))"); + generator.AppendLine("return SfResult.InvalidInObject;"); + generator.LeaveScope(); + } + else + { + generator.AppendLine($"var {argName} = {value};"); + } + } + + if (modifier == Modifier.Ref) + { + argName = $"ref {argName}"; + } + else if (modifier == Modifier.In) + { + argName = $"in {argName}"; + } + + args[index++] = argName; + } + + if (args.Length - outParameters.Count > 0) + { + generator.AppendLine(); + } + + if (returnsResult) + { + generator.AppendLine($"{ResultVariableName} = {method.Identifier.Text}({string.Join(", ", args)});"); + generator.AppendLine(); + + generator.AppendLine($"Span {OutRawDataVariableName};"); + generator.AppendLine(); + + generator.EnterScope($"if ({ResultVariableName}.IsFailure)"); + generator.AppendLine($"context.Processor.PrepareForErrorReply(ref context, out {OutRawDataVariableName}, runtimeMetadata);"); + generator.AppendLine($"CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref {OutRawDataVariableName});"); + generator.AppendLine($"return {ResultVariableName};"); + generator.LeaveScope(); + } + else + { + generator.AppendLine($"{method.Identifier.Text}({string.Join(", ", args)});"); + + generator.AppendLine(); + generator.AppendLine($"Span {OutRawDataVariableName};"); + } + + generator.AppendLine(); + + generator.AppendLine($"var {ResponseVariableName} = context.Processor.PrepareForReply(ref context, out {OutRawDataVariableName}, runtimeMetadata);"); + generator.AppendLine($"CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref {OutRawDataVariableName});"); + generator.AppendLine(); + + generator.EnterScope($"if ({OutRawDataVariableName}.Length < processor.OutRawDataSize)"); + generator.AppendLine("return SfResult.InvalidOutRawSize;"); + generator.LeaveScope(); + + if (outParameters.Count != 0) + { + generator.AppendLine(); + + int outCopyHandleIndex = 0; + int outMoveHandleIndex = outObjectsCount; + int outObjectIndex = 0; + + for (int outIndex = 0; outIndex < outParameters.Count; outIndex++) + { + OutParameter outParameter = outParameters[outIndex]; + + switch (outParameter.Type) + { + case CommandArgType.OutArgument: + generator.AppendLine($"CommandSerialization.SerializeArg<{outParameter.TypeName}>({OutRawDataVariableName}, processor.GetOutArgOffset({outParameter.Index}), {outParameter.Name});"); + break; + case CommandArgType.OutCopyHandle: + generator.AppendLine($"CommandSerialization.SerializeCopyHandle({ResponseVariableName}, {outCopyHandleIndex++}, {outParameter.Name});"); + break; + case CommandArgType.OutMoveHandle: + generator.AppendLine($"CommandSerialization.SerializeMoveHandle({ResponseVariableName}, {outMoveHandleIndex++}, {outParameter.Name});"); + break; + case CommandArgType.OutObject: + generator.AppendLine($"{OutObjectsVariableName}[{outObjectIndex++}] = {outParameter.Name};"); + break; + } + } + } + + generator.AppendLine(); + + if (outObjectsCount != 0 || buffersCount != 0) + { + if (outObjectsCount != 0) + { + generator.AppendLine($"processor.SetOutObjects(ref context, {ResponseVariableName}, {OutObjectsVariableName});"); + } + + if (buffersCount != 0) + { + generator.AppendLine($"processor.SetOutBuffers({ResponseVariableName}, {IsBufferMapAliasVariableName});"); + } + + generator.AppendLine(); + } + + generator.AppendLine("return Result.Success;"); + generator.LeaveScope(); + } + + private static string GetPrefixedArgName(string name) + { + return ArgVariablePrefix + name[0].ToString().ToUpperInvariant() + name.Substring(1); + } + + private static string GetCanonicalTypeNameOfGenericArgument(Compilation compilation, SyntaxNode syntaxNode, int argIndex) + { + if (syntaxNode is GenericNameSyntax genericNameSyntax) + { + if ((uint)argIndex < (uint)genericNameSyntax.TypeArgumentList.Arguments.Count) + { + return GetCanonicalTypeNameWithGenericArguments(compilation, genericNameSyntax.TypeArgumentList.Arguments[argIndex]); + } + } + + return GetCanonicalTypeName(compilation, syntaxNode); + } + + private static string GetCanonicalTypeNameWithGenericArguments(Compilation compilation, SyntaxNode syntaxNode) + { + TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); + + return typeInfo.Type.ToDisplayString(); + } + + private static string GetCanonicalTypeName(Compilation compilation, SyntaxNode syntaxNode) + { + TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); + string typeName = typeInfo.Type.ToDisplayString(); + + int genericArgsStartIndex = typeName.IndexOf('<'); + if (genericArgsStartIndex >= 0) + { + return typeName.Substring(0, genericArgsStartIndex); + } + + return typeName; + } + + private static SpecialType GetSpecialTypeName(Compilation compilation, SyntaxNode syntaxNode) + { + TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); + + return typeInfo.Type.SpecialType; + } + + private static string GetTypeAlignmentExpression(Compilation compilation, SyntaxNode syntaxNode) + { + TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); + + // Since there's no way to get the alignment for a arbitrary type here, let's assume that all + // "special" types are primitive types aligned to their own length. + // Otherwise, assume that the type is a custom struct, that either defines an explicit alignment + // or has an alignment of 1 which is the lowest possible value. + if (typeInfo.Type.SpecialType == SpecialType.None) + { + string pack = GetTypeFirstNamedAttributeAgument(compilation, syntaxNode, TypeStructLayoutAttribute, "Pack"); + + return pack ?? "1"; + } + else + { + return $"Unsafe.SizeOf<{typeInfo.Type.ToDisplayString()}>()"; + } + } + + private static string GetTypeFirstNamedAttributeAgument(Compilation compilation, SyntaxNode syntaxNode, string attributeName, string argName) + { + ISymbol symbol = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode).Type; + + foreach (var attribute in symbol.GetAttributes()) + { + if (attribute.AttributeClass.ToDisplayString() == attributeName) + { + foreach (var kv in attribute.NamedArguments) + { + if (kv.Key == argName) + { + return kv.Value.ToCSharpString(); + } + } + } + } + + return null; + } + + private static CommandArgType GetCommandArgType(Compilation compilation, ParameterSyntax parameter) + { + CommandArgType type = CommandArgType.Invalid; + + if (IsIn(parameter)) + { + if (IsArgument(compilation, parameter)) + { + type = CommandArgType.InArgument; + } + else if (IsBuffer(compilation, parameter)) + { + type = CommandArgType.Buffer; + } + else if (IsCopyHandle(compilation, parameter)) + { + type = CommandArgType.InCopyHandle; + } + else if (IsMoveHandle(compilation, parameter)) + { + type = CommandArgType.InMoveHandle; + } + else if (IsObject(compilation, parameter)) + { + type = CommandArgType.InObject; + } + else if (IsProcessId(compilation, parameter)) + { + type = CommandArgType.ProcessId; + } + } + else if (IsOut(parameter)) + { + if (IsArgument(compilation, parameter)) + { + type = CommandArgType.OutArgument; + } + else if (IsNonSpanOutBuffer(compilation, parameter)) + { + type = CommandArgType.Buffer; + } + else if (IsCopyHandle(compilation, parameter)) + { + type = CommandArgType.OutCopyHandle; + } + else if (IsMoveHandle(compilation, parameter)) + { + type = CommandArgType.OutMoveHandle; + } + else if (IsObject(compilation, parameter)) + { + type = CommandArgType.OutObject; + } + } + + return type; + } + + private static bool IsArgument(Compilation compilation,ParameterSyntax parameter) + { + return !IsBuffer(compilation, parameter) && + !IsHandle(compilation, parameter) && + !IsObject(compilation, parameter) && + !IsProcessId(compilation, parameter) && + IsUnmanagedType(compilation, parameter.Type); + } + + private static bool IsBuffer(Compilation compilation, ParameterSyntax parameter) + { + return HasAttribute(compilation, parameter, TypeBufferAttribute) && + IsValidTypeForBuffer(compilation, parameter); + } + + private static bool IsNonSpanOutBuffer(Compilation compilation, ParameterSyntax parameter) + { + return HasAttribute(compilation, parameter, TypeBufferAttribute) && + IsUnmanagedType(compilation, parameter.Type); + } + + private static bool IsValidTypeForBuffer(Compilation compilation, ParameterSyntax parameter) + { + return IsReadOnlySpan(compilation, parameter) || + IsSpan(compilation, parameter) || + IsUnmanagedType(compilation, parameter.Type); + } + + private static bool IsUnmanagedType(Compilation compilation, SyntaxNode syntaxNode) + { + TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); + + return typeInfo.Type.IsUnmanagedType; + } + + private static bool IsReadOnlySpan(Compilation compilation, ParameterSyntax parameter) + { + return GetCanonicalTypeName(compilation, parameter.Type) == TypeSystemReadOnlySpan; + } + + private static bool IsSpan(Compilation compilation, ParameterSyntax parameter) + { + return GetCanonicalTypeName(compilation, parameter.Type) == TypeSystemSpan; + } + + private static bool IsHandle(Compilation compilation, ParameterSyntax parameter) + { + return IsCopyHandle(compilation, parameter) || IsMoveHandle(compilation, parameter); + } + + private static bool IsCopyHandle(Compilation compilation, ParameterSyntax parameter) + { + return HasAttribute(compilation, parameter, TypeCopyHandleAttribute) && + GetSpecialTypeName(compilation, parameter.Type) == SpecialType.System_Int32; + } + + private static bool IsMoveHandle(Compilation compilation, ParameterSyntax parameter) + { + return HasAttribute(compilation, parameter, TypeMoveHandleAttribute) && + GetSpecialTypeName(compilation, parameter.Type) == SpecialType.System_Int32; + } + + private static bool IsObject(Compilation compilation, ParameterSyntax parameter) + { + SyntaxNode syntaxNode = parameter.Type; + TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); + + return typeInfo.Type.ToDisplayString() == TypeIServiceObject || + typeInfo.Type.AllInterfaces.Any(x => x.ToDisplayString() == TypeIServiceObject); + } + + private static bool IsProcessId(Compilation compilation, ParameterSyntax parameter) + { + return HasAttribute(compilation, parameter, TypeClientProcessIdAttribute) && + GetSpecialTypeName(compilation, parameter.Type) == SpecialType.System_UInt64; + } + + private static bool IsIn(ParameterSyntax parameter) + { + return !IsOut(parameter); + } + + private static bool IsOut(ParameterSyntax parameter) + { + return parameter.Modifiers.Any(SyntaxKind.OutKeyword); + } + + private static Modifier GetModifier(ParameterSyntax parameter) + { + foreach (SyntaxToken syntaxToken in parameter.Modifiers) + { + if (syntaxToken.IsKind(SyntaxKind.RefKeyword)) + { + return Modifier.Ref; + } + else if (syntaxToken.IsKind(SyntaxKind.OutKeyword)) + { + return Modifier.Out; + } + else if (syntaxToken.IsKind(SyntaxKind.InKeyword)) + { + return Modifier.In; + } + } + + return Modifier.None; + } + + private static string GenerateSpanCastElement0(string targetType, string input) + { + return $"{GenerateSpanCast(targetType, input)}[0]"; + } + + private static string GenerateSpanCast(string targetType, string input) + { + return $"MemoryMarshal.Cast({input})"; + } + + private static bool HasAttribute(Compilation compilation, ParameterSyntax parameterSyntax, string fullAttributeName) + { + foreach (var attributeList in parameterSyntax.AttributeLists) + { + foreach (var attribute in attributeList.Attributes) + { + if (GetCanonicalTypeName(compilation, attribute) == fullAttributeName) + { + return true; + } + } + } + + return false; + } + + private static bool NeedsIServiceObjectImplementation(Compilation compilation, ClassDeclarationSyntax classDeclarationSyntax) + { + ITypeSymbol type = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree).GetDeclaredSymbol(classDeclarationSyntax); + var serviceObjectInterface = type.AllInterfaces.FirstOrDefault(x => x.ToDisplayString() == TypeIServiceObject); + var interfaceMember = serviceObjectInterface?.GetMembers().FirstOrDefault(x => x.Name == "GetCommandHandlers"); + + // Return true only if the class implements IServiceObject but does not actually implement the method + // that the interface defines, since this is the only case we want to handle, if the method already exists + // we have nothing to do. + return serviceObjectInterface != null && type.FindImplementationForInterfaceMember(interfaceMember) == null; + } + + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => new HipcSyntaxReceiver()); + } + } +} diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcSyntaxReceiver.cs b/Ryujinx.Horizon.Generators/Hipc/HipcSyntaxReceiver.cs new file mode 100644 index 000000000..4b998dbe2 --- /dev/null +++ b/Ryujinx.Horizon.Generators/Hipc/HipcSyntaxReceiver.cs @@ -0,0 +1,58 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Horizon.Generators.Hipc +{ + class HipcSyntaxReceiver : ISyntaxReceiver + { + public List CommandInterfaces { get; } + + public HipcSyntaxReceiver() + { + CommandInterfaces = new List(); + } + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is ClassDeclarationSyntax classDeclaration) + { + if (!classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword) || classDeclaration.BaseList == null) + { + return; + } + + CommandInterface commandInterface = new CommandInterface(classDeclaration); + + foreach (var memberDeclaration in classDeclaration.Members) + { + if (memberDeclaration is MethodDeclarationSyntax methodDeclaration) + { + VisitMethod(commandInterface, methodDeclaration); + } + } + + CommandInterfaces.Add(commandInterface); + } + } + + private void VisitMethod(CommandInterface commandInterface, MethodDeclarationSyntax methodDeclaration) + { + string attributeName = HipcGenerator.CommandAttributeName.Replace("Attribute", string.Empty); + + if (methodDeclaration.AttributeLists.Count != 0) + { + foreach (var attributeList in methodDeclaration.AttributeLists) + { + if (attributeList.Attributes.Any(x => x.Name.ToString().Contains(attributeName))) + { + commandInterface.CommandImplementations.Add(methodDeclaration); + break; + } + } + } + } + } +} diff --git a/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs b/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs new file mode 100644 index 000000000..80a33c66c --- /dev/null +++ b/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs @@ -0,0 +1,58 @@ +using System.Text; + +namespace Ryujinx.Horizon.Generators +{ + class CodeGenerator + { + private const string Indent = " "; + private readonly StringBuilder _sb; + private string _currentIndent; + + public CodeGenerator() + { + _sb = new StringBuilder(); + } + + public void EnterScope(string header = null) + { + if (header != null) + { + AppendLine(header); + } + + AppendLine("{"); + IncreaseIndentation(); + } + + public void LeaveScope() + { + DecreaseIndentation(); + AppendLine("}"); + } + + public void IncreaseIndentation() + { + _currentIndent += Indent; + } + + public void DecreaseIndentation() + { + _currentIndent = _currentIndent.Substring(0, _currentIndent.Length - Indent.Length); + } + + public void AppendLine() + { + _sb.AppendLine(); + } + + public void AppendLine(string text) + { + _sb.AppendLine(_currentIndent + text); + } + + public override string ToString() + { + return _sb.ToString(); + } + } +} diff --git a/Ryujinx.Horizon.Generators/Kernel/SyscallGenerator.cs b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs similarity index 93% rename from Ryujinx.Horizon.Generators/Kernel/SyscallGenerator.cs rename to Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs index 2562cd46c..f2a877030 100644 --- a/Ryujinx.Horizon.Generators/Kernel/SyscallGenerator.cs +++ b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs @@ -25,15 +25,17 @@ namespace Ryujinx.Horizon.Generators.Kernel private const string TypeSystemUInt64 = "System.UInt64"; private const string NamespaceKernel = "Ryujinx.HLE.HOS.Kernel"; + private const string NamespaceHorizonCommon = "Ryujinx.Horizon.Common"; private const string TypeSvcAttribute = NamespaceKernel + ".SupervisorCall.SvcAttribute"; private const string TypePointerSizedAttribute = NamespaceKernel + ".SupervisorCall.PointerSizedAttribute"; + private const string TypeResultName = "Result"; private const string TypeKernelResultName = "KernelResult"; - private const string TypeKernelResult = NamespaceKernel + ".Common." + TypeKernelResultName; + private const string TypeResult = NamespaceHorizonCommon + "." + TypeResultName; private const string TypeExecutionContext = "IExecutionContext"; private static readonly string[] _expectedResults = new string[] { - $"{TypeKernelResultName}.Success", + $"{TypeResultName}.Success", $"{TypeKernelResultName}.TimedOut", $"{TypeKernelResultName}.Cancelled", $"{TypeKernelResultName}.PortRemoteClosed", @@ -133,6 +135,7 @@ namespace Ryujinx.Horizon.Generators.Kernel generator.AppendLine($"using {NamespaceKernel}.Memory;"); generator.AppendLine($"using {NamespaceKernel}.Process;"); generator.AppendLine($"using {NamespaceKernel}.Threading;"); + generator.AppendLine($"using {NamespaceHorizonCommon};"); generator.AppendLine("using System;"); generator.AppendLine(); generator.EnterScope($"namespace {ClassNamespace}"); @@ -183,7 +186,7 @@ namespace Ryujinx.Horizon.Generators.Kernel private static void GenerateResultCheckHelper(CodeGenerator generator) { - generator.EnterScope($"private static bool {ResultCheckHelperName}({TypeKernelResultName} {ResultVariableName})"); + generator.EnterScope($"private static bool {ResultCheckHelperName}({TypeResultName} {ResultVariableName})"); string[] expectedChecks = new string[_expectedResults.Length]; @@ -266,19 +269,25 @@ namespace Ryujinx.Horizon.Generators.Kernel GenerateLogPrintBeforeCall(generator, method.Identifier.Text, logInArgs); - string returnTypeName = method.ReturnType.ToString(); string argsList = string.Join(", ", args); int returnRegisterIndex = 0; string result = null; string canonicalReturnTypeName = null; - if (returnTypeName != "void") + if (method.ReturnType.ToString() != "void") { generator.AppendLine($"var {ResultVariableName} = syscall.{method.Identifier.Text}({argsList});"); - generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){ResultVariableName});"); - canonicalReturnTypeName = GetCanonicalTypeName(compilation, method.ReturnType); + if (canonicalReturnTypeName == TypeResult) + { + generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){ResultVariableName}.ErrorCode);"); + } + else + { + generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){ResultVariableName});"); + } + if (Is64BitInteger(canonicalReturnTypeName)) { generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint)({ResultVariableName} >> 32));"); @@ -358,8 +367,17 @@ namespace Ryujinx.Horizon.Generators.Kernel if (method.ReturnType.ToString() != "void") { generator.AppendLine($"var {ResultVariableName} = syscall.{method.Identifier.Text}({argsList});"); - generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){ResultVariableName});"); canonicalReturnTypeName = GetCanonicalTypeName(compilation, method.ReturnType); + + if (canonicalReturnTypeName == TypeResult) + { + generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){ResultVariableName}.ErrorCode);"); + } + else + { + generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){ResultVariableName});"); + } + result = GetFormattedLogValue(ResultVariableName, canonicalReturnTypeName); } else @@ -433,7 +451,7 @@ namespace Ryujinx.Horizon.Generators.Kernel log += $" = {result}"; } - if (canonicalResultTypeName == TypeKernelResult) + if (canonicalResultTypeName == TypeResult) { generator.EnterScope($"if ({ResultCheckHelperName}({ResultVariableName}))"); GenerateLogPrint(generator, "Trace", "KernelSvc", log); diff --git a/Ryujinx.Horizon.Generators/Kernel/SyscallSyntaxReceiver.cs b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs similarity index 100% rename from Ryujinx.Horizon.Generators/Kernel/SyscallSyntaxReceiver.cs rename to Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs diff --git a/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj b/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj new file mode 100644 index 000000000..67fab2d55 --- /dev/null +++ b/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/Ryujinx.Horizon/HeapAllocator.cs b/Ryujinx.Horizon/HeapAllocator.cs new file mode 100644 index 000000000..867c96770 --- /dev/null +++ b/Ryujinx.Horizon/HeapAllocator.cs @@ -0,0 +1,143 @@ +using Ryujinx.Common; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Ryujinx.Horizon +{ + class HeapAllocator + { + private const ulong InvalidAddress = ulong.MaxValue; + + private struct Range : IComparable + { + public ulong Offset { get; } + public ulong Size { get; } + + public Range(ulong offset, ulong size) + { + Offset = offset; + Size = size; + } + + public int CompareTo(Range other) + { + return Offset.CompareTo(other.Offset); + } + } + + private readonly List _freeRanges; + private ulong _currentHeapSize; + + public HeapAllocator() + { + _freeRanges = new List(); + _currentHeapSize = 0; + } + + public ulong Allocate(ulong size, ulong alignment = 1UL) + { + ulong address = AllocateImpl(size, alignment); + + if (address == InvalidAddress) + { + ExpandHeap(size + alignment - 1UL); + + address = AllocateImpl(size, alignment); + + Debug.Assert(address != InvalidAddress); + } + + return address; + } + + private void ExpandHeap(ulong expansionSize) + { + ulong oldHeapSize = _currentHeapSize; + ulong newHeapSize = BitUtils.AlignUp(oldHeapSize + expansionSize, 0x200000UL); + + _currentHeapSize = newHeapSize; + + HorizonStatic.Syscall.SetHeapSize(out ulong heapAddress, newHeapSize).AbortOnFailure(); + + Free(heapAddress + oldHeapSize, newHeapSize - oldHeapSize); + } + + private ulong AllocateImpl(ulong size, ulong alignment) + { + for (int i = 0; i < _freeRanges.Count; i++) + { + var range = _freeRanges[i]; + + ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment); + ulong sizeDelta = alignedOffset - range.Offset; + ulong usableSize = range.Size - sizeDelta; + + if (sizeDelta < range.Size && usableSize >= size) + { + _freeRanges.RemoveAt(i); + + if (sizeDelta != 0) + { + InsertFreeRange(range.Offset, sizeDelta); + } + + ulong endOffset = range.Offset + range.Size; + ulong remainingSize = endOffset - (alignedOffset + size); + if (remainingSize != 0) + { + InsertFreeRange(endOffset - remainingSize, remainingSize); + } + + return alignedOffset; + } + } + + return InvalidAddress; + } + + public void Free(ulong offset, ulong size) + { + InsertFreeRangeComingled(offset, size); + } + + private void InsertFreeRange(ulong offset, ulong size) + { + var range = new Range(offset, size); + int index = _freeRanges.BinarySearch(range); + if (index < 0) + { + index = ~index; + } + + _freeRanges.Insert(index, range); + } + + private void InsertFreeRangeComingled(ulong offset, ulong size) + { + ulong endOffset = offset + size; + var range = new Range(offset, size); + int index = _freeRanges.BinarySearch(range); + if (index < 0) + { + index = ~index; + } + + if (index < _freeRanges.Count && _freeRanges[index].Offset == endOffset) + { + endOffset = _freeRanges[index].Offset + _freeRanges[index].Size; + _freeRanges.RemoveAt(index); + } + + if (index > 0 && _freeRanges[index - 1].Offset + _freeRanges[index - 1].Size == offset) + { + offset = _freeRanges[index - 1].Offset; + _freeRanges.RemoveAt(--index); + } + + range = new Range(offset, endOffset - offset); + + _freeRanges.Insert(index, range); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/HorizonOptions.cs b/Ryujinx.Horizon/HorizonOptions.cs new file mode 100644 index 000000000..b1567c6a4 --- /dev/null +++ b/Ryujinx.Horizon/HorizonOptions.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Horizon +{ + public struct HorizonOptions + { + public bool IgnoreMissingServices { get; } + + public HorizonOptions(bool ignoreMissingServices) + { + IgnoreMissingServices = ignoreMissingServices; + } + } +} diff --git a/Ryujinx.Horizon/HorizonStatic.cs b/Ryujinx.Horizon/HorizonStatic.cs new file mode 100644 index 000000000..1e483cd44 --- /dev/null +++ b/Ryujinx.Horizon/HorizonStatic.cs @@ -0,0 +1,44 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Horizon +{ + static class HorizonStatic + { + [ThreadStatic] + private static HorizonOptions _options; + + [ThreadStatic] + private static ISyscallApi _syscall; + + [ThreadStatic] + private static IVirtualMemoryManager _addressSpace; + + [ThreadStatic] + private static IThreadContext _threadContext; + + [ThreadStatic] + private static int _threadHandle; + + public static HorizonOptions Options => _options; + public static ISyscallApi Syscall => _syscall; + public static IVirtualMemoryManager AddressSpace => _addressSpace; + public static IThreadContext ThreadContext => _threadContext; + public static int CurrentThreadHandle => _threadHandle; + + public static void Register( + HorizonOptions options, + ISyscallApi syscallApi, + IVirtualMemoryManager addressSpace, + IThreadContext threadContext, + int threadHandle) + { + _options = options; + _syscall = syscallApi; + _addressSpace = addressSpace; + _threadContext = threadContext; + _threadHandle = threadHandle; + } + } +} diff --git a/Ryujinx.Horizon/IService.cs b/Ryujinx.Horizon/IService.cs new file mode 100644 index 000000000..67c12cef6 --- /dev/null +++ b/Ryujinx.Horizon/IService.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Horizon +{ + interface IService + { + abstract static void Main(); + } +} diff --git a/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/Ryujinx.Horizon/LogManager/LmIpcServer.cs new file mode 100644 index 000000000..7b757fe98 --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmIpcServer.cs @@ -0,0 +1,54 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm; + +namespace Ryujinx.Horizon.LogManager +{ + class LmIpcServer + { + private const int LogMaxSessionsCount = 42; + + private const int PointerBufferSize = 0x400; + private const int MaxDomains = 31; + private const int MaxDomainObjects = 61; + + private const int MaxPortsCount = 1; + + private static readonly ManagerOptions _logManagerOptions = new ManagerOptions( + PointerBufferSize, + MaxDomains, + MaxDomainObjects, + false); + + private static readonly ServiceName _logServiceName = ServiceName.Encode("lm"); + + private SmApi _sm; + private ServerManager _serverManager; + + private LmLog _logServiceObject; + + public void Initialize() + { + HeapAllocator allocator = new HeapAllocator(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount); + + _logServiceObject = new LmLog(); + + _serverManager.RegisterObjectForServer(_logServiceObject, _logServiceName, LogMaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +} diff --git a/Ryujinx.Horizon/LogManager/LmLog.cs b/Ryujinx.Horizon/LogManager/LmLog.cs new file mode 100644 index 000000000..772465c45 --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmLog.cs @@ -0,0 +1,19 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Lm; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.LogManager +{ + partial class LmLog : IServiceObject + { + public LogDestination LogDestination { get; set; } = LogDestination.TargetManager; + + [CmifCommand(0)] + public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong clientProcessId) + { + logger = new LmLogger(this, clientProcessId); + + return Result.Success; + } + } +} diff --git a/Ryujinx.Horizon/LogManager/LmLogger.cs b/Ryujinx.Horizon/LogManager/LmLogger.cs new file mode 100644 index 000000000..461776cd8 --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmLogger.cs @@ -0,0 +1,139 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Lm; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.Horizon.LogManager +{ + partial class LmLogger : IServiceObject + { + private readonly LmLog _log; + private readonly ulong _clientProcessId; + + public LmLogger(LmLog log, ulong clientProcessId) + { + _log = log; + _clientProcessId = clientProcessId; + } + + [CmifCommand(0)] + public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span message) + { + if (!SetProcessId(message, _clientProcessId)) + { + return Result.Success; + } + + Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(message)); + + return Result.Success; + } + + [CmifCommand(1)] + public Result SetDestination(LogDestination destination) + { + _log.LogDestination = destination; + + return Result.Success; + } + + private static bool SetProcessId(Span message, ulong processId) + { + ref LogPacketHeader header = ref MemoryMarshal.Cast(message)[0]; + + uint expectedMessageSize = (uint)Unsafe.SizeOf() + header.PayloadSize; + + if (expectedMessageSize != (uint)message.Length) + { + Logger.Warning?.Print(LogClass.ServiceLm, $"Invalid message size (expected 0x{expectedMessageSize:X} but got 0x{message.Length:X})."); + + return false; + } + + header.ProcessId = processId; + + return true; + } + + private static string LogImpl(ReadOnlySpan message) + { + SpanReader reader = new SpanReader(message); + + LogPacketHeader header = reader.Read(); + + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($"Guest Log:\n Log level: {header.Severity}"); + + while (reader.Length > 0) + { + int type = ReadUleb128(ref reader); + int size = ReadUleb128(ref reader); + + LogDataChunkKey field = (LogDataChunkKey)type; + + string fieldStr = string.Empty; + + if (field == LogDataChunkKey.Start) + { + reader.Skip(size); + + continue; + } + else if (field == LogDataChunkKey.Stop) + { + break; + } + else if (field == LogDataChunkKey.Line) + { + fieldStr = $"{field}: {reader.Read()}"; + } + else if (field == LogDataChunkKey.DropCount) + { + fieldStr = $"{field}: {reader.Read()}"; + } + else if (field == LogDataChunkKey.Time) + { + fieldStr = $"{field}: {reader.Read()}s"; + } + else if (field < LogDataChunkKey.Count) + { + fieldStr = $"{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; + } + else + { + fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; + } + + sb.AppendLine($" {fieldStr}"); + } + + return sb.ToString(); + } + + private static int ReadUleb128(ref SpanReader reader) + { + int result = 0; + int count = 0; + + byte encoded; + + do + { + encoded = reader.Read(); + + result += (encoded & 0x7F) << (7 * count); + + count++; + } while ((encoded & 0x80) != 0); + + return result; + } + } +} diff --git a/Ryujinx.Horizon/LogManager/LmMain.cs b/Ryujinx.Horizon/LogManager/LmMain.cs new file mode 100644 index 000000000..8c0262ac6 --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmMain.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Horizon.LogManager +{ + class LmMain : IService + { + public static void Main() + { + LmIpcServer ipcServer = new LmIpcServer(); + + ipcServer.Initialize(); + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} diff --git a/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/Ryujinx.Horizon/Ryujinx.Horizon.csproj new file mode 100644 index 000000000..e4591c6f9 --- /dev/null +++ b/Ryujinx.Horizon/Ryujinx.Horizon.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + + + + + + + + + + diff --git a/Ryujinx.Horizon/Sdk/DebugUtil.cs b/Ryujinx.Horizon/Sdk/DebugUtil.cs new file mode 100644 index 000000000..f56a50ec5 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/DebugUtil.cs @@ -0,0 +1,12 @@ +using System.Diagnostics; + +namespace Ryujinx.Horizon.Sdk +{ + static class DebugUtil + { + public static void Assert(bool condition) + { + Debug.Assert(condition); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs b/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs new file mode 100644 index 000000000..72acf7896 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Horizon.Sdk.Diag +{ + enum LogSeverity : byte + { + Trace = 0, + Info = 1, + Warn = 2, + Error = 3, + Fatal = 4 + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs b/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs new file mode 100644 index 000000000..90756ece2 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Horizon.Sdk.Lm +{ + enum LogDataChunkKey + { + Start = 0, + Stop = 1, + Message = 2, + Line = 3, + Filename = 4, + Function = 5, + Module = 6, + Thread = 7, + DropCount = 8, + Time = 9, + ProgramName = 10, + + Count + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs b/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs new file mode 100644 index 000000000..8b08548d6 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Lm +{ + [Flags] + enum LogDestination + { + TargetManager = 1 << 0, + Uart = 1 << 1, + UartIfSleep = 1 << 2, + + All = 0xffff + } +} diff --git a/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs b/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs new file mode 100644 index 000000000..75d9f40b9 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Lm +{ + [Flags] + enum LogPacketFlags : byte + { + IsHead = 1 << 0, + IsTail = 1 << 1, + IsLittleEndian = 1 << 2 + } +} diff --git a/Ryujinx.Horizon/Sdk/Lm/LogPacketHeader.cs b/Ryujinx.Horizon/Sdk/Lm/LogPacketHeader.cs new file mode 100644 index 000000000..022ba8dad --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/LogPacketHeader.cs @@ -0,0 +1,15 @@ +using Ryujinx.Horizon.Sdk.Diag; + +namespace Ryujinx.Horizon.Sdk.Lm +{ + struct LogPacketHeader + { + public ulong ProcessId; + public ulong ThreadId; + public LogPacketFlags Flags; + public byte Padding; + public LogSeverity Severity; + public byte Verbosity; + public uint PayloadSize; + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Event.cs b/Ryujinx.Horizon/Sdk/OsTypes/Event.cs new file mode 100644 index 000000000..79d7408e3 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/Event.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + class Event : IDisposable + { + private EventType _event; + + public object EventLock => _event.Lock; + public LinkedList MultiWaitHolders => _event.MultiWaitHolders; + + public Event(EventClearMode clearMode) + { + Os.InitializeEvent(out _event, signaled: false, clearMode); + } + + public TriBool IsSignaledThreadUnsafe() + { + return _event.Signaled ? TriBool.True : TriBool.False; + } + + public void Wait() + { + Os.WaitEvent(ref _event); + } + + public bool TryWait() + { + return Os.TryWaitEvent(ref _event); + } + + public bool TimedWait(TimeSpan timeout) + { + return Os.TimedWaitEvent(ref _event, timeout); + } + + public void Signal() + { + Os.SignalEvent(ref _event); + } + + public void Clear() + { + Os.ClearEvent(ref _event); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Os.FinalizeEvent(ref _event); + } + } + + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs b/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs new file mode 100644 index 000000000..b500e6b3c --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + enum EventClearMode + { + ManualClear, + AutoClear + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs new file mode 100644 index 000000000..b4b1a275c --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + struct EventType + { + public LinkedList MultiWaitHolders; + public bool Signaled; + public bool InitiallySignaled; + public EventClearMode ClearMode; + public InitializationState State; + public ulong BroadcastCounter; + public object Lock; + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs new file mode 100644 index 000000000..62b5bf066 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs @@ -0,0 +1,89 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.OsTypes.Impl +{ + static class InterProcessEvent + { + public static Result Create(ref InterProcessEventType ipEvent, EventClearMode clearMode) + { + Result result = InterProcessEventImpl.Create(out int writableHandle, out int readableHandle); + + if (result != Result.Success) + { + return result; + } + + ipEvent = new InterProcessEventType( + clearMode == EventClearMode.AutoClear, + true, + true, + readableHandle, + writableHandle); + + return Result.Success; + } + + public static void Destroy(ref InterProcessEventType ipEvent) + { + ipEvent.State = InitializationState.NotInitialized; + + if (ipEvent.ReadableHandleManaged) + { + if (ipEvent.ReadableHandle != 0) + { + InterProcessEventImpl.Close(ipEvent.ReadableHandle); + } + ipEvent.ReadableHandleManaged = false; + } + + if (ipEvent.WritableHandleManaged) + { + if (ipEvent.WritableHandle != 0) + { + InterProcessEventImpl.Close(ipEvent.WritableHandle); + } + ipEvent.WritableHandleManaged = false; + } + } + + public static int DetachReadableHandle(ref InterProcessEventType ipEvent) + { + int handle = ipEvent.ReadableHandle; + + ipEvent.ReadableHandle = 0; + ipEvent.ReadableHandleManaged = false; + + return handle; + } + + public static int DetachWritableHandle(ref InterProcessEventType ipEvent) + { + int handle = ipEvent.WritableHandle; + + ipEvent.WritableHandle = 0; + ipEvent.WritableHandleManaged = false; + + return handle; + } + + public static int GetReadableHandle(ref InterProcessEventType ipEvent) + { + return ipEvent.ReadableHandle; + } + + public static int GetWritableHandle(ref InterProcessEventType ipEvent) + { + return ipEvent.WritableHandle; + } + + public static void Signal(ref InterProcessEventType ipEvent) + { + InterProcessEventImpl.Signal(ipEvent.WritableHandle); + } + + public static void Clear(ref InterProcessEventType ipEvent) + { + InterProcessEventImpl.Clear(ipEvent.ReadableHandle == 0 ? ipEvent.WritableHandle : ipEvent.ReadableHandle); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs new file mode 100644 index 000000000..a8aeacc92 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs @@ -0,0 +1,136 @@ +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.Horizon.Sdk.OsTypes.Impl +{ + static class InterProcessEventImpl + { + public static Result Create(out int writableHandle, out int readableHandle) + { + Result result = HorizonStatic.Syscall.CreateEvent(out writableHandle, out readableHandle); + + if (result == KernelResult.OutOfResource) + { + return OsResult.OutOfResource; + } + + result.AbortOnFailure(); + + return Result.Success; + } + + public static void Close(int handle) + { + if (handle != 0) + { + HorizonStatic.Syscall.CloseHandle(handle).AbortOnFailure(); + } + } + + public static void Signal(int handle) + { + HorizonStatic.Syscall.SignalEvent(handle).AbortOnFailure(); + } + + public static void Clear(int handle) + { + HorizonStatic.Syscall.ClearEvent(handle).AbortOnFailure(); + } + + public static void Wait(int handle, bool autoClear) + { + Span handles = stackalloc int[1]; + + handles[0] = handle; + + while (true) + { + Result result = HorizonStatic.Syscall.WaitSynchronization(out _, handles, -1L); + + if (result == Result.Success) + { + if (autoClear) + { + result = HorizonStatic.Syscall.ResetSignal(handle); + + if (result == KernelResult.InvalidState) + { + continue; + } + + result.AbortOnFailure(); + } + + return; + } + + result.AbortUnless(KernelResult.Cancelled); + } + } + + public static bool TryWait(int handle, bool autoClear) + { + if (autoClear) + { + return HorizonStatic.Syscall.ResetSignal(handle) == Result.Success; + } + + Span handles = stackalloc int[1]; + + handles[0] = handle; + + while (true) + { + Result result = HorizonStatic.Syscall.WaitSynchronization(out _, handles, 0); + + if (result == Result.Success) + { + return true; + } + else if (result == KernelResult.TimedOut) + { + return false; + } + + result.AbortUnless(KernelResult.Cancelled); + } + } + + public static bool TimedWait(int handle, bool autoClear, TimeSpan timeout) + { + Span handles = stackalloc int[1]; + + handles[0] = handle; + + long timeoutNs = timeout.Milliseconds * 1000000L; + + while (true) + { + Result result = HorizonStatic.Syscall.WaitSynchronization(out _, handles, timeoutNs); + + if (result == Result.Success) + { + if (autoClear) + { + result = HorizonStatic.Syscall.ResetSignal(handle); + + if (result == KernelResult.InvalidState) + { + continue; + } + + result.AbortOnFailure(); + } + + return true; + } + else if (result == KernelResult.TimedOut) + { + return false; + } + + result.AbortUnless(KernelResult.Cancelled); + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs new file mode 100644 index 000000000..fd45792d7 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -0,0 +1,250 @@ +using Ryujinx.Common; +using Ryujinx.Horizon.Common; +using System.Collections.Generic; +using System; + +namespace Ryujinx.Horizon.Sdk.OsTypes.Impl +{ + class MultiWaitImpl + { + private const int WaitTimedOut = -1; + private const int WaitCancelled = -2; + private const int WaitInvalid = -3; + + private readonly List _multiWaits; + + private object _lock; + + private int _waitingThreadHandle; + + private MultiWaitHolderBase _signaledHolder; + + public long CurrentTime { get; private set; } + + public MultiWaitImpl() + { + _multiWaits = new List(); + + _lock = new object(); + } + + public void LinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder) + { + _multiWaits.Add(multiWaitHolder); + } + + public void UnlinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder) + { + _multiWaits.Remove(multiWaitHolder); + } + + public void MoveAllFrom(MultiWaitImpl other) + { + foreach (MultiWaitHolderBase multiWait in other._multiWaits) + { + multiWait.SetMultiWait(this); + } + + _multiWaits.AddRange(other._multiWaits); + + other._multiWaits.Clear(); + } + + public MultiWaitHolderBase WaitAnyImpl(bool infinite, long timeout) + { + _signaledHolder = null; + _waitingThreadHandle = Os.GetCurrentThreadHandle(); + + MultiWaitHolderBase result = LinkHoldersToObjectList(); + + lock (_lock) + { + if (_signaledHolder != null) + { + result = _signaledHolder; + } + } + + if (result == null) + { + result = WaitAnyHandleImpl(infinite, timeout); + } + + UnlinkHoldersFromObjectsList(); + _waitingThreadHandle = 0; + + return result; + } + + private MultiWaitHolderBase WaitAnyHandleImpl(bool infinite, long timeout) + { + Span objectHandles = new int[64]; + + Span objects = new MultiWaitHolderBase[64]; + + int count = FillObjectsArray(objectHandles, objects); + + long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000; + + while (true) + { + CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000; + + MultiWaitHolderBase minTimeoutObject = RecalcMultiWaitTimeout(endTime, out long minTimeout); + + int index; + + if (count == 0 && minTimeout == 0) + { + index = WaitTimedOut; + } + else + { + index = WaitSynchronization(objectHandles.Slice(0, count), minTimeout); + + DebugUtil.Assert(index != WaitInvalid); + } + + switch (index) + { + case WaitTimedOut: + if (minTimeoutObject != null) + { + CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000; + + if (minTimeoutObject.Signaled == TriBool.True) + { + lock (_lock) + { + _signaledHolder = minTimeoutObject; + + return _signaledHolder; + } + } + } + else + { + return null; + } + break; + case WaitCancelled: + lock (_lock) + { + if (_signaledHolder != null) + { + return _signaledHolder; + } + } + break; + default: + lock (_lock) + { + _signaledHolder = objects[index]; + + return _signaledHolder; + } + } + } + } + + private int FillObjectsArray(Span handles, Span objects) + { + int count = 0; + + foreach (MultiWaitHolderBase holder in _multiWaits) + { + int handle = holder.Handle; + + if (handle != 0) + { + handles[count] = handle; + objects[count] = holder; + + count++; + } + } + + return count; + } + + private MultiWaitHolderBase RecalcMultiWaitTimeout(long endTime, out long minTimeout) + { + MultiWaitHolderBase minTimeHolder = null; + + long minTime = endTime; + + foreach (MultiWaitHolder holder in _multiWaits) + { + long currentTime = holder.GetAbsoluteTimeToWakeup(); + + if ((ulong)currentTime < (ulong)minTime) + { + minTimeHolder = holder; + + minTime = currentTime; + } + } + + minTimeout = (ulong)minTime < (ulong)CurrentTime ? 0 : minTime - CurrentTime; + + return minTimeHolder; + } + + private static int WaitSynchronization(ReadOnlySpan handles, long timeout) + { + Result result = HorizonStatic.Syscall.WaitSynchronization(out int index, handles, timeout); + + if (result == KernelResult.TimedOut) + { + return WaitTimedOut; + } + else if (result == KernelResult.Cancelled) + { + return WaitCancelled; + } + else + { + result.AbortOnFailure(); + } + + return index; + } + + public void NotifyAndWakeUpThread(MultiWaitHolderBase holder) + { + lock (_lock) + { + if (_signaledHolder == null) + { + _signaledHolder = holder; + HorizonStatic.Syscall.CancelSynchronization(_waitingThreadHandle).AbortOnFailure(); + } + } + } + + private MultiWaitHolderBase LinkHoldersToObjectList() + { + MultiWaitHolderBase signaledHolder = null; + + foreach (MultiWaitHolderBase holder in _multiWaits) + { + TriBool isSignaled = holder.LinkToObjectList(); + + if (signaledHolder == null && isSignaled == TriBool.True) + { + signaledHolder = holder; + } + } + + return signaledHolder; + } + + private void UnlinkHoldersFromObjectsList() + { + foreach (MultiWaitHolderBase holder in _multiWaits) + { + holder.UnlinkFromObjectList(); + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs b/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs new file mode 100644 index 000000000..45ffd2587 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + enum InitializationState : byte + { + NotInitialized, + Initialized + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs new file mode 100644 index 000000000..5f6824fea --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + struct InterProcessEventType + { + public readonly bool AutoClear; + public InitializationState State; + public bool ReadableHandleManaged; + public bool WritableHandleManaged; + public int ReadableHandle; + public int WritableHandle; + + public InterProcessEventType( + bool autoClear, + bool readableHandleManaged, + bool writableHandleManaged, + int readableHandle, + int writableHandle) + { + AutoClear = autoClear; + State = InitializationState.Initialized; + ReadableHandleManaged = readableHandleManaged; + WritableHandleManaged = writableHandleManaged; + ReadableHandle = readableHandle; + WritableHandle = writableHandle; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs new file mode 100644 index 000000000..5a91f6c36 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs @@ -0,0 +1,43 @@ +using Ryujinx.Horizon.Sdk.OsTypes.Impl; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + class MultiWait + { + private readonly MultiWaitImpl _impl; + + public MultiWait() + { + _impl = new MultiWaitImpl(); + } + + public void LinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder) + { + DebugUtil.Assert(!multiWaitHolder.IsLinked); + + _impl.LinkMultiWaitHolder(multiWaitHolder); + + multiWaitHolder.SetMultiWait(_impl); + } + + public void MoveAllFrom(MultiWait other) + { + _impl.MoveAllFrom(other._impl); + } + + public MultiWaitHolder WaitAny() + { + return (MultiWaitHolder)_impl.WaitAnyImpl(true, -1L); + } + + public MultiWaitHolder TryWaitAny() + { + return (MultiWaitHolder)_impl.WaitAnyImpl(false, 0); + } + + public MultiWaitHolder TimedWaitAny(long timeout) + { + return (MultiWaitHolder)_impl.WaitAnyImpl(false, timeout); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs new file mode 100644 index 000000000..a24b19064 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + class MultiWaitHolder : MultiWaitHolderBase + { + public object UserData { get; set; } + + public void UnlinkFromMultiWaitHolder() + { + DebugUtil.Assert(IsLinked); + + MultiWait.UnlinkMultiWaitHolder(this); + + SetMultiWait(null); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs new file mode 100644 index 000000000..018305ba6 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs @@ -0,0 +1,39 @@ +using Ryujinx.Horizon.Sdk.OsTypes.Impl; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + class MultiWaitHolderBase + { + protected MultiWaitImpl MultiWait; + + public bool IsLinked => MultiWait != null; + + public virtual TriBool Signaled => TriBool.False; + + public virtual int Handle => 0; + + public void SetMultiWait(MultiWaitImpl multiWait) + { + MultiWait = multiWait; + } + + public MultiWaitImpl GetMultiWait() + { + return MultiWait; + } + + public virtual TriBool LinkToObjectList() + { + return TriBool.Undefined; + } + + public virtual void UnlinkFromObjectList() + { + } + + public virtual long GetAbsoluteTimeToWakeup() + { + return long.MaxValue; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs new file mode 100644 index 000000000..37ac22f0b --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + class MultiWaitHolderOfEvent : MultiWaitHolder + { + private Event _event; + private LinkedListNode _node; + + public override TriBool Signaled + { + get + { + lock (_event.EventLock) + { + return _event.IsSignaledThreadUnsafe(); + } + } + } + + public MultiWaitHolderOfEvent(Event evnt) + { + _event = evnt; + } + + public override TriBool LinkToObjectList() + { + lock (_event.EventLock) + { + _node = _event.MultiWaitHolders.AddLast(this); + + return _event.IsSignaledThreadUnsafe(); + } + } + + public override void UnlinkFromObjectList() + { + lock (_event.EventLock) + { + _event.MultiWaitHolders.Remove(_node); + _node = null; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs new file mode 100644 index 000000000..6fc5c75b9 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + class MultiWaitHolderOfHandle : MultiWaitHolder + { + private int _handle; + + public override int Handle => _handle; + + public MultiWaitHolderOfHandle(int handle) + { + _handle = handle; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs new file mode 100644 index 000000000..cc7e84836 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + static partial class Os + { + public static void InitializeEvent(out EventType evnt, bool signaled, EventClearMode clearMode) + { + evnt = new EventType + { + MultiWaitHolders = new LinkedList(), + Signaled = signaled, + InitiallySignaled = signaled, + ClearMode = clearMode, + State = InitializationState.Initialized, + Lock = new object() + }; + } + + public static void FinalizeEvent(ref EventType evnt) + { + evnt.State = InitializationState.NotInitialized; + } + + public static void WaitEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + ulong currentCounter = evnt.BroadcastCounter; + + while (!evnt.Signaled) + { + if (currentCounter != evnt.BroadcastCounter) + { + break; + } + + Monitor.Wait(evnt.Lock); + } + + if (evnt.ClearMode == EventClearMode.AutoClear) + { + evnt.Signaled = false; + } + } + } + + public static bool TryWaitEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + bool signaled = evnt.Signaled; + + if (evnt.ClearMode == EventClearMode.AutoClear) + { + evnt.Signaled = false; + } + + return signaled; + } + } + + public static bool TimedWaitEvent(ref EventType evnt, TimeSpan timeout) + { + lock (evnt.Lock) + { + ulong currentCounter = evnt.BroadcastCounter; + + while (!evnt.Signaled) + { + if (currentCounter != evnt.BroadcastCounter) + { + break; + } + + bool wasSignaledInTime = Monitor.Wait(evnt.Lock, timeout); + if (!wasSignaledInTime) + { + return false; + } + } + + if (evnt.ClearMode == EventClearMode.AutoClear) + { + evnt.Signaled = false; + } + } + + return true; + } + + public static void SignalEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + if (evnt.Signaled) + { + return; + } + + evnt.Signaled = true; + + if (evnt.ClearMode == EventClearMode.ManualClear) + { + evnt.BroadcastCounter++; + Monitor.PulseAll(evnt.Lock); + } + else + { + Monitor.Pulse(evnt.Lock); + } + + foreach (MultiWaitHolderBase holder in evnt.MultiWaitHolders) + { + holder.GetMultiWait().NotifyAndWakeUpThread(holder); + } + } + } + + public static void ClearEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + evnt.Signaled = false; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs new file mode 100644 index 000000000..827de2313 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + static partial class Os + { + public static void FinalizeMultiWaitHolder(MultiWaitHolderBase holder) + { + DebugUtil.Assert(!holder.IsLinked); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs new file mode 100644 index 000000000..6a6d9bf23 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs @@ -0,0 +1,33 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + static partial class Os + { + private const int SelfProcessHandle = (0x1ffff << 15) | 1; + + public static int GetCurrentProcessHandle() + { + return SelfProcessHandle; + } + + public static ulong GetCurrentProcessId() + { + return GetProcessId(GetCurrentProcessHandle()); + } + + private static ulong GetProcessId(int handle) + { + Result result = TryGetProcessId(handle, out ulong pid); + + result.AbortOnFailure(); + + return pid; + } + + private static Result TryGetProcessId(int handle, out ulong pid) + { + return HorizonStatic.Syscall.GetProcessId(out pid, handle); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs new file mode 100644 index 000000000..86dcd1fad --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs @@ -0,0 +1,11 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + static class OsResult + { + private const int ModuleId = 3; + + public static Result OutOfResource => new Result(ModuleId, 9); + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs new file mode 100644 index 000000000..061d7a3cd --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs @@ -0,0 +1,85 @@ +using Ryujinx.Horizon.Sdk.OsTypes.Impl; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + static partial class Os + { + public static Result CreateSystemEvent(out SystemEventType sysEvent, EventClearMode clearMode, bool interProcess) + { + sysEvent = new SystemEventType(); + + if (interProcess) + { + Result result = InterProcessEvent.Create(ref sysEvent.InterProcessEvent, clearMode); + + if (result != Result.Success) + { + return result; + } + + sysEvent.State = SystemEventType.InitializationState.InitializedAsInterProcess; + } + else + { + throw new NotImplementedException(); + } + + return Result.Success; + } + + public static void DestroySystemEvent(ref SystemEventType sysEvent) + { + var oldState = sysEvent.State; + sysEvent.State = SystemEventType.InitializationState.NotInitialized; + + switch (oldState) + { + case SystemEventType.InitializationState.InitializedAsInterProcess: + InterProcessEvent.Destroy(ref sysEvent.InterProcessEvent); + break; + } + } + + public static int DetachReadableHandleOfSystemEvent(ref SystemEventType sysEvent) + { + return InterProcessEvent.DetachReadableHandle(ref sysEvent.InterProcessEvent); + } + + public static int DetachWritableHandleOfSystemEvent(ref SystemEventType sysEvent) + { + return InterProcessEvent.DetachWritableHandle(ref sysEvent.InterProcessEvent); + } + + public static int GetReadableHandleOfSystemEvent(ref SystemEventType sysEvent) + { + return InterProcessEvent.GetReadableHandle(ref sysEvent.InterProcessEvent); + } + + public static int GetWritableHandleOfSystemEvent(ref SystemEventType sysEvent) + { + return InterProcessEvent.GetWritableHandle(ref sysEvent.InterProcessEvent); + } + + public static void SignalSystemEvent(ref SystemEventType sysEvent) + { + switch (sysEvent.State) + { + case SystemEventType.InitializationState.InitializedAsInterProcess: + InterProcessEvent.Signal(ref sysEvent.InterProcessEvent); + break; + } + } + + public static void ClearSystemEvent(ref SystemEventType sysEvent) + { + switch (sysEvent.State) + { + case SystemEventType.InitializationState.InitializedAsInterProcess: + InterProcessEvent.Clear(ref sysEvent.InterProcessEvent); + break; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs new file mode 100644 index 000000000..2037cd7fe --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + static partial class Os + { + public static int GetCurrentThreadHandle() + { + return HorizonStatic.CurrentThreadHandle; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs new file mode 100644 index 000000000..338493d23 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + struct SystemEventType + { + public enum InitializationState : byte + { + NotInitialized, + InitializedAsEvent, + InitializedAsInterProcess + } + + public InterProcessEventType InterProcessEvent; + public InitializationState State; + + public bool NotInitialized => State == InitializationState.NotInitialized; + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs b/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs new file mode 100644 index 000000000..7debd9e27 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + enum TriBool + { + False, + True, + Undefined + } +} diff --git a/Ryujinx.Horizon/Sdk/ServiceUtil.cs b/Ryujinx.Horizon/Sdk/ServiceUtil.cs new file mode 100644 index 000000000..413ac1f6b --- /dev/null +++ b/Ryujinx.Horizon/Sdk/ServiceUtil.cs @@ -0,0 +1,38 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk +{ + static class ServiceUtil + { + public static Result SendRequest(out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan data) + { + ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress; + int tlsSize = Api.TlsMessageBufferSize; + + using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize)) + { + CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat() + { + DataSize = data.Length, + RequestId = requestId, + SendPid = sendPid + }); + + data.CopyTo(request.Data); + } + + Result result = HorizonStatic.Syscall.SendSyncRequest(sessionHandle); + + if (result.IsFailure) + { + response = default; + return result; + } + + return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs new file mode 100644 index 000000000..882115013 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct CmifDomainInHeader + { + public CmifDomainRequestType Type; + public byte ObjectsCount; + public ushort DataSize; + public int ObjectId; + public uint Padding; + public uint Token; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs new file mode 100644 index 000000000..2086d24c1 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct CmifDomainOutHeader + { +#pragma warning disable CS0649 + public uint ObjectsCount; + public uint Padding; + public uint Padding2; + public uint Padding3; +#pragma warning restore CS0649 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs new file mode 100644 index 000000000..b913db94e --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + enum CmifDomainRequestType : byte + { + Invalid = 0, + SendMessage = 1, + Close = 2 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs new file mode 100644 index 000000000..55b859fca --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct CmifInHeader + { + public uint Magic; + public uint Version; + public uint CommandId; + public uint Token; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs new file mode 100644 index 000000000..781452e30 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs @@ -0,0 +1,128 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + static class CmifMessage + { + public const uint CmifInHeaderMagic = 0x49434653; // SFCI + public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO + + public static CmifRequest CreateRequest(Span output, CmifRequestFormat format) + { + int totalSize = 16; + + if (format.ObjectId != 0) + { + totalSize += Unsafe.SizeOf() + format.ObjectsCount * sizeof(int); + } + + totalSize += Unsafe.SizeOf() + format.DataSize; + totalSize = (totalSize + 1) & ~1; + int outPointerSizeTableOffset = totalSize; + int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount; + totalSize += sizeof(ushort) * outPointerSizeTableSize; + int rawDataSizeInWords = (totalSize + sizeof(uint) - 1) / sizeof(uint); + + CmifRequest request = new CmifRequest(); + + request.Hipc = HipcMessage.WriteMessage(output, new HipcMetadata() + { + Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request, + SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount, + SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount, + ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount, + ExchangeBuffersCount = format.InOutBuffersCount, + DataWordsCount = rawDataSizeInWords, + ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount, + SendPid = format.SendPid, + CopyHandlesCount = format.HandlesCount, + MoveHandlesCount = 0 + }); + + Span data = request.Hipc.DataWords; + + if (format.ObjectId != 0) + { + ref CmifDomainInHeader domainHeader = ref MemoryMarshal.Cast(data)[0]; + + int payloadSize = Unsafe.SizeOf() + format.DataSize; + + domainHeader = new CmifDomainInHeader() + { + Type = CmifDomainRequestType.SendMessage, + ObjectsCount = (byte)format.ObjectsCount, + DataSize = (ushort)payloadSize, + ObjectId = format.ObjectId, + Padding = 0, + Token = format.Context + }; + + data = data.Slice(Unsafe.SizeOf() / sizeof(uint)); + + request.Objects = data.Slice((payloadSize + sizeof(uint) - 1) / sizeof(uint)); + } + + ref CmifInHeader header = ref MemoryMarshal.Cast(data)[0]; + + header = new CmifInHeader() + { + Magic = CmifInHeaderMagic, + Version = format.Context != 0 ? 1u : 0u, + CommandId = format.RequestId, + Token = format.ObjectId != 0 ? 0u : format.Context + }; + + request.Data = MemoryMarshal.Cast(data).Slice(Unsafe.SizeOf()); + + int paddingSizeBefore = (rawDataSizeInWords - request.Hipc.DataWords.Length) * sizeof(uint); + + Span outPointerTable = MemoryMarshal.Cast(request.Hipc.DataWords).Slice(outPointerSizeTableOffset - paddingSizeBefore); + request.OutPointerSizes = MemoryMarshal.Cast(outPointerTable); + request.ServerPointerSize = format.ServerPointerSize; + + return request; + } + + public static Result ParseResponse(out CmifResponse response, Span input, bool isDomain, int size) + { + HipcMessage responseMessage = new HipcMessage(input); + + Span data = MemoryMarshal.Cast(responseMessage.Data.DataWords); + Span objects = Span.Empty; + + if (isDomain) + { + data = data.Slice(Unsafe.SizeOf()); + objects = MemoryMarshal.Cast(data.Slice(Unsafe.SizeOf() + size)); + } + + CmifOutHeader header = MemoryMarshal.Cast(data)[0]; + + if (header.Magic != CmifOutHeaderMagic) + { + response = default; + return SfResult.InvalidOutHeader; + } + + if (header.Result.IsFailure) + { + response = default; + return header.Result; + } + + response = new CmifResponse() + { + Data = data.Slice(Unsafe.SizeOf()), + Objects = objects, + CopyHandles = responseMessage.Data.CopyHandles, + MoveHandles = responseMessage.Data.MoveHandles + }; + + return Result.Success; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs new file mode 100644 index 000000000..2828cde5e --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs @@ -0,0 +1,14 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct CmifOutHeader + { +#pragma warning disable CS0649 + public uint Magic; + public uint Version; + public Result Result; + public uint Token; +#pragma warning restore CS0649 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs new file mode 100644 index 000000000..80772ad33 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs @@ -0,0 +1,14 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + ref struct CmifRequest + { + public HipcMessageData Hipc; + public Span Data; + public Span OutPointerSizes; + public Span Objects; + public int ServerPointerSize; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs new file mode 100644 index 000000000..d11545786 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct CmifRequestFormat + { +#pragma warning disable CS0649 + public int ObjectId; + public uint RequestId; + public uint Context; + public int DataSize; + public int ServerPointerSize; + public int InAutoBuffersCount; + public int OutAutoBuffersCount; + public int InBuffersCount; + public int OutBuffersCount; + public int InOutBuffersCount; + public int InPointersCount; + public int OutPointersCount; + public int OutFixedPointersCount; + public int ObjectsCount; + public int HandlesCount; + public bool SendPid; +#pragma warning restore CS0649 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs new file mode 100644 index 000000000..d1d8dc9c5 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + ref struct CmifResponse + { + public ReadOnlySpan Data; + public ReadOnlySpan Objects; + public ReadOnlySpan CopyHandles; + public ReadOnlySpan MoveHandles; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs new file mode 100644 index 000000000..b3b05864e --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + enum CommandType + { + Invalid = 0, + LegacyRequest = 1, + Close = 2, + LegacyControl = 3, + Request = 4, + Control = 5, + RequestWithContext = 6, + ControlWithContext = 7 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs new file mode 100644 index 000000000..148396876 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + abstract partial class DomainServiceObject : ServerDomainBase, IServiceObject + { + public abstract ServerDomainBase GetServerDomain(); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs new file mode 100644 index 000000000..bcf311b2e --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs @@ -0,0 +1,75 @@ +using Ryujinx.Horizon.Common; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + class DomainServiceObjectDispatchTable : ServiceDispatchTableBase + { + public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan inRawData) + { + return ProcessMessageImpl(ref context, ((DomainServiceObject)context.ServiceObject).GetServerDomain(), inRawData); + } + + private Result ProcessMessageImpl(ref ServiceDispatchContext context, ServerDomainBase domain, ReadOnlySpan inRawData) + { + if (inRawData.Length < Unsafe.SizeOf()) + { + return SfResult.InvalidHeaderSize; + } + + var inHeader = MemoryMarshal.Cast(inRawData)[0]; + + ReadOnlySpan inDomainRawData = inRawData.Slice(Unsafe.SizeOf()); + + int targetObjectId = inHeader.ObjectId; + + switch (inHeader.Type) + { + case CmifDomainRequestType.SendMessage: + var targetObject = domain.GetObject(targetObjectId); + if (targetObject == null) + { + return SfResult.TargetNotFound; + } + + if (inHeader.DataSize + inHeader.ObjectsCount * sizeof(int) > inDomainRawData.Length) + { + return SfResult.InvalidHeaderSize; + } + + ReadOnlySpan inMessageRawData = inDomainRawData.Slice(0, inHeader.DataSize); + + if (inHeader.ObjectsCount > DomainServiceObjectProcessor.MaximumObjects) + { + return SfResult.InvalidInObjectsCount; + } + + int[] inObjectIds = new int[inHeader.ObjectsCount]; + + var domainProcessor = new DomainServiceObjectProcessor(domain, inObjectIds); + + if (context.Processor == null) + { + context.Processor = domainProcessor; + } + else + { + context.Processor.SetImplementationProcessor(domainProcessor); + } + + context.ServiceObject = targetObject.ServiceObject; + + return targetObject.ProcessMessage(ref context, inMessageRawData); + + case CmifDomainRequestType.Close: + domain.UnregisterObject(targetObjectId); + return Result.Success; + + default: + return SfResult.InvalidInHeader; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs new file mode 100644 index 000000000..92d86196a --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs @@ -0,0 +1,140 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + class DomainServiceObjectProcessor : ServerMessageProcessor + { + public const int MaximumObjects = 8; + + private ServerMessageProcessor _implProcessor; + private readonly ServerDomainBase _domain; + private int _outObjectIdsOffset; + private readonly int[] _inObjectIds; + private readonly int[] _reservedObjectIds; + private ServerMessageRuntimeMetadata _implMetadata; + + private int InObjectsCount => _inObjectIds.Length; + private int OutObjectsCount => _implMetadata.OutObjectsCount; + private int ImplOutHeadersSize => _implMetadata.OutHeadersSize; + private int ImplOutDataTotalSize => _implMetadata.OutDataSize + _implMetadata.OutHeadersSize; + + public DomainServiceObjectProcessor(ServerDomainBase domain, int[] inObjectIds) + { + _domain = domain; + _inObjectIds = inObjectIds; + _reservedObjectIds = new int[MaximumObjects]; + } + + public override void SetImplementationProcessor(ServerMessageProcessor impl) + { + if (_implProcessor == null) + { + _implProcessor = impl; + } + else + { + _implProcessor.SetImplementationProcessor(impl); + } + + _implMetadata = _implProcessor.GetRuntimeMetadata(); + } + + public override ServerMessageRuntimeMetadata GetRuntimeMetadata() + { + var runtimeMetadata = _implProcessor.GetRuntimeMetadata(); + + return new ServerMessageRuntimeMetadata( + (ushort)(runtimeMetadata.InDataSize + runtimeMetadata.InObjectsCount * sizeof(int)), + (ushort)(runtimeMetadata.OutDataSize + runtimeMetadata.OutObjectsCount * sizeof(int)), + (byte)(runtimeMetadata.InHeadersSize + Unsafe.SizeOf()), + (byte)(runtimeMetadata.OutHeadersSize + Unsafe.SizeOf()), + 0, + 0); + } + + public override Result PrepareForProcess(ref ServiceDispatchContext context, ServerMessageRuntimeMetadata runtimeMetadata) + { + if (_implMetadata.InObjectsCount != InObjectsCount) + { + return SfResult.InvalidInObjectsCount; + } + + Result result = _domain.ReserveIds(new Span(_reservedObjectIds).Slice(0, OutObjectsCount)); + + if (result.IsFailure) + { + return result; + } + + return _implProcessor.PrepareForProcess(ref context, runtimeMetadata); + } + + public override Result GetInObjects(Span inObjects) + { + for (int i = 0; i < InObjectsCount; i++) + { + inObjects[i] = _domain.GetObject(_inObjectIds[i]); + } + + return Result.Success; + } + + public override HipcMessageData PrepareForReply(scoped ref ServiceDispatchContext context, out Span outRawData, ServerMessageRuntimeMetadata runtimeMetadata) + { + var response = _implProcessor.PrepareForReply(ref context, out outRawData, runtimeMetadata); + + int outHeaderSize = Unsafe.SizeOf(); + int implOutDataTotalSize = ImplOutDataTotalSize; + + DebugUtil.Assert(outHeaderSize + implOutDataTotalSize + OutObjectsCount * sizeof(int) <= outRawData.Length); + + outRawData = outRawData.Slice(outHeaderSize); + _outObjectIdsOffset = (response.DataWords.Length * sizeof(uint) - outRawData.Length) + implOutDataTotalSize; + + return response; + } + + public override void PrepareForErrorReply(scoped ref ServiceDispatchContext context, out Span outRawData, ServerMessageRuntimeMetadata runtimeMetadata) + { + _implProcessor.PrepareForErrorReply(ref context, out outRawData, runtimeMetadata); + + int outHeaderSize = Unsafe.SizeOf(); + int implOutDataTotalSize = ImplOutDataTotalSize; + + DebugUtil.Assert(outHeaderSize + implOutDataTotalSize <= outRawData.Length); + + outRawData = outRawData.Slice(outHeaderSize); + + _domain.UnreserveIds(new Span(_reservedObjectIds).Slice(0, OutObjectsCount)); + } + + public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span outObjects) + { + int outObjectsCount = OutObjectsCount; + Span objectIds = _reservedObjectIds; + + for (int i = 0; i < outObjectsCount; i++) + { + if (outObjects[i] == null) + { + _domain.UnreserveIds(objectIds.Slice(i, 1)); + objectIds[i] = 0; + continue; + } + + _domain.RegisterObject(objectIds[i], outObjects[i]); + } + + Span outObjectIds = MemoryMarshal.Cast(MemoryMarshal.Cast(response.DataWords).Slice(_outObjectIdsOffset)); + + for (int i = 0; i < outObjectsCount; i++) + { + outObjectIds[i] = objectIds[i]; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs new file mode 100644 index 000000000..0f3b259af --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs @@ -0,0 +1,52 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct HandlesToClose + { + private int _handle0; + private int _handle1; + private int _handle2; + private int _handle3; + private int _handle4; + private int _handle5; + private int _handle6; + private int _handle7; + + public int Count; + + public int this[int index] + { + get + { + return index switch + { + 0 => _handle0, + 1 => _handle1, + 2 => _handle2, + 3 => _handle3, + 4 => _handle4, + 5 => _handle5, + 6 => _handle6, + 7 => _handle7, + _ => throw new IndexOutOfRangeException() + }; + } + set + { + switch (index) + { + case 0: _handle0 = value; break; + case 1: _handle1 = value; break; + case 2: _handle2 = value; break; + case 3: _handle3 = value; break; + case 4: _handle4 = value; break; + case 5: _handle5 = value; break; + case 6: _handle6 = value; break; + case 7: _handle7 = value; break; + default: throw new IndexOutOfRangeException(); + } + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs new file mode 100644 index 000000000..ddb6943f3 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + class InlineContext + { + public static int Set(int newContext) + { + // TODO: Implement (will require FS changes???) + return newContext; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs new file mode 100644 index 000000000..5af00077a --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct PointerAndSize + { + public static PointerAndSize Empty => new PointerAndSize(0UL, 0UL); + + public ulong Address { get; } + public ulong Size { get; } + public bool IsEmpty => Size == 0UL; + + public PointerAndSize(ulong address, ulong size) + { + Address = address; + Size = size; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs new file mode 100644 index 000000000..eabe544f4 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs @@ -0,0 +1,19 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct ScopedInlineContextChange : IDisposable + { + private readonly int _previousContext; + + public ScopedInlineContextChange(int newContext) + { + _previousContext = InlineContext.Set(newContext); + } + + public void Dispose() + { + InlineContext.Set(_previousContext); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs new file mode 100644 index 000000000..f38fa0303 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs @@ -0,0 +1,15 @@ +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + abstract class ServerDomainBase + { + public abstract Result ReserveIds(Span outIds); + public abstract void UnreserveIds(ReadOnlySpan ids); + public abstract void RegisterObject(int id, ServiceObjectHolder obj); + + public abstract ServiceObjectHolder UnregisterObject(int id); + public abstract ServiceObjectHolder GetObject(int id); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs new file mode 100644 index 000000000..62ee27380 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs @@ -0,0 +1,246 @@ +using Ryujinx.Horizon.Common; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + class ServerDomainManager + { + private class EntryManager + { + public class Entry + { + public int Id { get; } + public Domain Owner { get; set; } + public ServiceObjectHolder Obj { get; set; } + public LinkedListNode Node { get; set; } + + public Entry(int id) + { + Id = id; + } + } + + private readonly LinkedList _freeList; + private readonly Entry[] _entries; + + public EntryManager(int count) + { + _freeList = new LinkedList(); + _entries = new Entry[count]; + + for (int i = 0; i < count; i++) + { + _freeList.AddLast(_entries[i] = new Entry(i + 1)); + } + } + + public Entry AllocateEntry() + { + lock (_freeList) + { + if (_freeList.Count == 0) + { + return null; + } + + var entry = _freeList.First.Value; + _freeList.RemoveFirst(); + return entry; + } + } + + public void FreeEntry(Entry entry) + { + lock (_freeList) + { + DebugUtil.Assert(entry.Owner == null); + DebugUtil.Assert(entry.Obj == null); + _freeList.AddFirst(entry); + } + } + + public Entry GetEntry(int id) + { + if (id == 0) + { + return null; + } + + int index = id - 1; + + if ((uint)index >= (uint)_entries.Length) + { + return null; + } + + return _entries[index]; + } + } + + private class Domain : DomainServiceObject, IDisposable + { + private readonly ServerDomainManager _manager; + private readonly LinkedList _entries; + + public Domain(ServerDomainManager manager) + { + _manager = manager; + _entries = new LinkedList(); + } + + public override ServiceObjectHolder GetObject(int id) + { + var entry = _manager._entryManager.GetEntry(id); + if (entry == null) + { + return null; + } + + lock (_manager._entryOwnerLock) + { + if (entry.Owner != this) + { + return null; + } + } + + return entry.Obj.Clone(); + } + + public override ServerDomainBase GetServerDomain() + { + return this; + } + + public override void RegisterObject(int id, ServiceObjectHolder obj) + { + var entry = _manager._entryManager.GetEntry(id); + DebugUtil.Assert(entry != null); + + lock (_manager._entryOwnerLock) + { + DebugUtil.Assert(entry.Owner == null); + entry.Owner = this; + entry.Node = _entries.AddLast(entry); + } + + entry.Obj = obj; + } + + public override Result ReserveIds(Span outIds) + { + for (int i = 0; i < outIds.Length; i++) + { + var entry = _manager._entryManager.AllocateEntry(); + if (entry == null) + { + return SfResult.OutOfDomainEntries; + } + + DebugUtil.Assert(entry.Owner == null); + + outIds[i] = entry.Id; + } + + return Result.Success; + } + + public override ServiceObjectHolder UnregisterObject(int id) + { + var entry = _manager._entryManager.GetEntry(id); + if (entry == null) + { + return null; + } + + ServiceObjectHolder obj; + + lock (_manager._entryOwnerLock) + { + if (entry.Owner != this) + { + return null; + } + + entry.Owner = null; + obj = entry.Obj; + entry.Obj = null; + _entries.Remove(entry.Node); + entry.Node = null; + } + + _manager._entryManager.FreeEntry(entry); + + return obj; + } + + public override void UnreserveIds(ReadOnlySpan ids) + { + for (int i = 0; i < ids.Length; i++) + { + var entry = _manager._entryManager.GetEntry(ids[i]); + + DebugUtil.Assert(entry != null); + DebugUtil.Assert(entry.Owner == null); + + _manager._entryManager.FreeEntry(entry); + } + } + + public void Dispose() + { + foreach (var entry in _entries) + { + if (entry.Obj.ServiceObject is IDisposable disposableObj) + { + disposableObj.Dispose(); + } + } + + _manager.FreeDomain(this); + } + } + + private readonly EntryManager _entryManager; + private readonly object _entryOwnerLock; + private readonly HashSet _domains; + private int _maxDomains; + + public ServerDomainManager(int entryCount, int maxDomains) + { + _entryManager = new EntryManager(entryCount); + _entryOwnerLock = new object(); + _domains = new HashSet(); + _maxDomains = maxDomains; + } + + public DomainServiceObject AllocateDomainServiceObject() + { + lock (_domains) + { + if (_domains.Count == _maxDomains) + { + return null; + } + + var domain = new Domain(this); + _domains.Add(domain); + return domain; + } + } + + public static void DestroyDomainServiceObject(DomainServiceObject obj) + { + ((Domain)obj).Dispose(); + } + + private void FreeDomain(Domain domain) + { + lock (_domains) + { + _domains.Remove(domain); + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs new file mode 100644 index 000000000..e76502381 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs @@ -0,0 +1,18 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + abstract class ServerMessageProcessor + { + public abstract void SetImplementationProcessor(ServerMessageProcessor impl); + public abstract ServerMessageRuntimeMetadata GetRuntimeMetadata(); + + public abstract Result PrepareForProcess(scoped ref ServiceDispatchContext context, ServerMessageRuntimeMetadata runtimeMetadata); + public abstract Result GetInObjects(Span inObjects); + public abstract HipcMessageData PrepareForReply(scoped ref ServiceDispatchContext context, out Span outRawData, ServerMessageRuntimeMetadata runtimeMetadata); + public abstract void PrepareForErrorReply(scoped ref ServiceDispatchContext context, out Span outRawData, ServerMessageRuntimeMetadata runtimeMetadata); + public abstract void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span outObjects); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs new file mode 100644 index 000000000..18a404302 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct ServerMessageRuntimeMetadata + { + public ushort InDataSize { get; } + public ushort OutDataSize { get; } + public byte InHeadersSize { get; } + public byte OutHeadersSize { get; } + public byte InObjectsCount { get; } + public byte OutObjectsCount { get; } + public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10; + + public ServerMessageRuntimeMetadata( + ushort inDataSize, + ushort outDataSize, + byte inHeadersSize, + byte outHeadersSize, + byte inObjectsCount, + byte outObjectsCount) + { + InDataSize = inDataSize; + OutDataSize = outDataSize; + InHeadersSize = inHeadersSize; + OutHeadersSize = outHeadersSize; + InObjectsCount = inObjectsCount; + OutObjectsCount = outObjectsCount; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs new file mode 100644 index 000000000..3339a1a60 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs @@ -0,0 +1,18 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + ref struct ServiceDispatchContext + { + public IServiceObject ServiceObject; + public ServerSessionManager Manager; + public ServerSession Session; + public ServerMessageProcessor Processor; + public HandlesToClose HandlesToClose; + public PointerAndSize PointerBuffer; + public ReadOnlySpan InMessageBuffer; + public Span OutMessageBuffer; + public HipcMessage Request; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs new file mode 100644 index 000000000..7fbd8eb84 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + struct ServiceDispatchMeta + { + public ServiceDispatchTableBase DispatchTable { get; } + + public ServiceDispatchMeta(ServiceDispatchTableBase dispatchTable) + { + DispatchTable = dispatchTable; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs new file mode 100644 index 000000000..145c17839 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs @@ -0,0 +1,33 @@ +using Ryujinx.Horizon.Common; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + class ServiceDispatchTable : ServiceDispatchTableBase + { + private readonly string _objectName; + private readonly IReadOnlyDictionary _entries; + + public ServiceDispatchTable(string objectName, IReadOnlyDictionary entries) + { + _objectName = objectName; + _entries = entries; + } + + public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan inRawData) + { + return ProcessMessageImpl(ref context, inRawData, _entries, _objectName); + } + + public static ServiceDispatchTableBase Create(IServiceObject instance) + { + if (instance is DomainServiceObject) + { + return new DomainServiceObjectDispatchTable(); + } + + return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers()); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs new file mode 100644 index 000000000..a0e28ca8f --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs @@ -0,0 +1,90 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + abstract class ServiceDispatchTableBase + { + private const uint MaxCmifVersion = 1; + + public abstract Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan inRawData); + + protected Result ProcessMessageImpl(ref ServiceDispatchContext context, ReadOnlySpan inRawData, IReadOnlyDictionary entries, string objectName) + { + if (inRawData.Length < Unsafe.SizeOf()) + { + Logger.Warning?.Print(LogClass.KernelIpc, $"Request message size 0x{inRawData.Length:X} is invalid"); + + return SfResult.InvalidHeaderSize; + } + + CmifInHeader inHeader = MemoryMarshal.Cast(inRawData)[0]; + + if (inHeader.Magic != CmifMessage.CmifInHeaderMagic || inHeader.Version > MaxCmifVersion) + { + Logger.Warning?.Print(LogClass.KernelIpc, $"Request message header magic value 0x{inHeader.Magic:X} is invalid"); + + return SfResult.InvalidInHeader; + } + + ReadOnlySpan inMessageRawData = inRawData[Unsafe.SizeOf()..]; + uint commandId = inHeader.CommandId; + + var outHeader = Span.Empty; + + if (!entries.TryGetValue((int)commandId, out var commandHandler)) + { + Logger.Warning?.Print(LogClass.KernelIpc, $"{objectName} command ID 0x{commandId:X} is not implemented"); + + if (HorizonStatic.Options.IgnoreMissingServices) + { + // If ignore missing services is enabled, just pretend that everything is fine. + var response = PrepareForStubReply(ref context, out Span outRawData); + CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData); + outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success }; + + return Result.Success; + } + + return SfResult.UnknownCommandId; + } + + Logger.Trace?.Print(LogClass.KernelIpc, $"{objectName}.{commandHandler.MethodName} called"); + + Result commandResult = commandHandler.Invoke(ref outHeader, ref context, inMessageRawData); + + if (commandResult.Module == SfResult.ModuleId || + commandResult.Module == HipcResult.ModuleId) + { + Logger.Warning?.Print(LogClass.KernelIpc, $"{commandHandler.MethodName} returned error {commandResult}"); + } + + if (SfResult.RequestContextChanged(commandResult)) + { + return commandResult; + } + + if (outHeader.IsEmpty) + { + commandResult.AbortOnSuccess(); + return commandResult; + } + + outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = commandResult }; + + return Result.Success; + } + + private static HipcMessageData PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span outRawData) + { + var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0); + outRawData = MemoryMarshal.Cast(response.DataWords); + return response; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs new file mode 100644 index 000000000..6e87e3405 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs @@ -0,0 +1,34 @@ +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Cmif +{ + class ServiceObjectHolder + { + public IServiceObject ServiceObject { get; } + + private readonly ServiceDispatchMeta _dispatchMeta; + + public ServiceObjectHolder(ServiceObjectHolder objectHolder) + { + ServiceObject = objectHolder.ServiceObject; + _dispatchMeta = objectHolder._dispatchMeta; + } + + public ServiceObjectHolder(IServiceObject serviceImpl) + { + ServiceObject = serviceImpl; + _dispatchMeta = new ServiceDispatchMeta(ServiceDispatchTable.Create(serviceImpl)); + } + + public ServiceObjectHolder Clone() + { + return new ServiceObjectHolder(this); + } + + public Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan inRawData) + { + return _dispatchMeta.DispatchTable.ProcessMessage(ref context, inRawData); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/CmifCommandAttribute.cs b/Ryujinx.Horizon/Sdk/Sf/CmifCommandAttribute.cs new file mode 100644 index 000000000..51a7b5976 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/CmifCommandAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + class CmifCommandAttribute : Attribute + { + public uint CommandId { get; } + + public CmifCommandAttribute(uint commandId) + { + CommandId = commandId; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs new file mode 100644 index 000000000..8f367b4e0 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs @@ -0,0 +1,56 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + enum CommandArgType : byte + { + Invalid, + + Buffer, + InArgument, + InCopyHandle, + InMoveHandle, + InObject, + OutArgument, + OutCopyHandle, + OutMoveHandle, + OutObject, + ProcessId + } + + struct CommandArg + { + public CommandArgType Type { get; } + public HipcBufferFlags BufferFlags { get; } + public ushort BufferFixedSize { get; } + public int ArgSize { get; } + public int ArgAlignment { get; } + + public CommandArg(CommandArgType type) + { + Type = type; + BufferFlags = default; + BufferFixedSize = 0; + ArgSize = 0; + ArgAlignment = 0; + } + + public CommandArg(CommandArgType type, int argSize, int argAlignment) + { + Type = type; + BufferFlags = default; + BufferFixedSize = 0; + ArgSize = argSize; + ArgAlignment = argAlignment; + } + + public CommandArg(HipcBufferFlags flags, ushort fixedSize = 0) + { + Type = CommandArgType.Buffer; + BufferFlags = flags; + BufferFixedSize = fixedSize; + ArgSize = 0; + ArgAlignment = 0; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs new file mode 100644 index 000000000..5b7c302f2 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs @@ -0,0 +1,38 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + [AttributeUsage(AttributeTargets.Parameter)] + class BufferAttribute : Attribute + { + public HipcBufferFlags Flags { get; } + public ushort FixedSize { get; } + + public BufferAttribute(HipcBufferFlags flags) + { + Flags = flags; + } + + public BufferAttribute(HipcBufferFlags flags, ushort fixedSize) + { + Flags = flags | HipcBufferFlags.FixedSize; + FixedSize = fixedSize; + } + } + + [AttributeUsage(AttributeTargets.Parameter)] + class ClientProcessIdAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Parameter)] + class CopyHandleAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Parameter)] + class MoveHandleAttribute : Attribute + { + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs new file mode 100644 index 000000000..ae42a8ef5 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs @@ -0,0 +1,57 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + class CommandHandler + { + public delegate Result MethodInvoke( + ref ServiceDispatchContext context, + HipcCommandProcessor processor, + ServerMessageRuntimeMetadata runtimeMetadata, + ReadOnlySpan inRawData, + ref Span outHeader); + + private readonly MethodInvoke _invoke; + private readonly HipcCommandProcessor _processor; + + public string MethodName => _invoke.Method.Name; + + public CommandHandler(MethodInvoke invoke, params CommandArg[] args) + { + _invoke = invoke; + _processor = new HipcCommandProcessor(args); + } + + public Result Invoke(ref Span outHeader, ref ServiceDispatchContext context, ReadOnlySpan inRawData) + { + if (context.Processor == null) + { + context.Processor = _processor; + } + else + { + context.Processor.SetImplementationProcessor(_processor); + } + + var runtimeMetadata = context.Processor.GetRuntimeMetadata(); + Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); + + if (result.IsFailure) + { + return result; + } + + return _invoke(ref context, _processor, runtimeMetadata, inRawData, ref outHeader); + } + + public static void GetCmifOutHeaderPointer(ref Span outHeader, ref Span outRawData) + { + outHeader = MemoryMarshal.Cast(outRawData).Slice(0, 1); + outRawData = outRawData.Slice(Unsafe.SizeOf()); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs new file mode 100644 index 000000000..9a3a511ad --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs @@ -0,0 +1,68 @@ +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Memory; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + static class CommandSerialization + { + public static ReadOnlySpan GetReadOnlySpan(PointerAndSize bufferRange) + { + return HorizonStatic.AddressSpace.GetSpan(bufferRange.Address, checked((int)bufferRange.Size)); + } + + public static WritableRegion GetWritableRegion(PointerAndSize bufferRange) + { + return HorizonStatic.AddressSpace.GetWritableRegion(bufferRange.Address, checked((int)bufferRange.Size)); + } + + public static ref T GetRef(PointerAndSize bufferRange) where T : unmanaged + { + var writableRegion = GetWritableRegion(bufferRange); + return ref MemoryMarshal.Cast(writableRegion.Memory.Span)[0]; + } + + public static object DeserializeArg(ref ServiceDispatchContext context, ReadOnlySpan inRawData, int offset) where T : unmanaged + { + return MemoryMarshal.Cast(inRawData.Slice(offset, Unsafe.SizeOf()))[0]; + } + + public static T DeserializeArg(ReadOnlySpan inRawData, int offset) where T : unmanaged + { + return MemoryMarshal.Cast(inRawData.Slice(offset, Unsafe.SizeOf()))[0]; + } + + public static ulong DeserializeClientProcessId(ref ServiceDispatchContext context) + { + return context.Request.Pid; + } + + public static int DeserializeCopyHandle(ref ServiceDispatchContext context, int index) + { + return context.Request.Data.CopyHandles[index]; + } + + public static int DeserializeMoveHandle(ref ServiceDispatchContext context, int index) + { + return context.Request.Data.MoveHandles[index]; + } + + public static void SerializeArg(Span outRawData, int offset, T value) where T : unmanaged + { + MemoryMarshal.Cast(outRawData.Slice(offset, Unsafe.SizeOf()))[0] = (T)value; + } + + public static void SerializeCopyHandle(HipcMessageData response, int index, int value) + { + response.CopyHandles[index] = value; + } + + public static void SerializeMoveHandle(HipcMessageData response, int index, int value) + { + response.MoveHandles[index] = value; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs new file mode 100644 index 000000000..deac524c7 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs @@ -0,0 +1,89 @@ +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + static class Api + { + public const int TlsMessageBufferSize = 0x100; + + public static Result Receive(out ReceiveResult recvResult, int sessionHandle, Span messageBuffer) + { + Result result = ReceiveImpl(sessionHandle, messageBuffer); + + if (result == KernelResult.PortRemoteClosed) + { + recvResult = ReceiveResult.Closed; + + return Result.Success; + } + else if (result == KernelResult.ReceiveListBroken) + { + recvResult = ReceiveResult.NeedsRetry; + + return Result.Success; + } + + recvResult = ReceiveResult.Success; + + return result; + } + + private static Result ReceiveImpl(int sessionHandle, Span messageBuffer) + { + Span handles = stackalloc int[1]; + + handles[0] = sessionHandle; + + var tlsSpan = HorizonStatic.AddressSpace.GetSpan(HorizonStatic.ThreadContext.TlsAddress, TlsMessageBufferSize); + + if (messageBuffer == tlsSpan) + { + return HorizonStatic.Syscall.ReplyAndReceive(out _, handles, 0, -1L); + } + else + { + throw new NotImplementedException(); + } + } + + public static Result Reply(int sessionHandle, ReadOnlySpan messageBuffer) + { + Result result = ReplyImpl(sessionHandle, messageBuffer); + + result.AbortUnless(KernelResult.TimedOut, KernelResult.PortRemoteClosed); + + return Result.Success; + } + + private static Result ReplyImpl(int sessionHandle, ReadOnlySpan messageBuffer) + { + Span handles = stackalloc int[1]; + + handles[0] = sessionHandle; + + var tlsSpan = HorizonStatic.AddressSpace.GetSpan(HorizonStatic.ThreadContext.TlsAddress, TlsMessageBufferSize); + + if (messageBuffer == tlsSpan) + { + return HorizonStatic.Syscall.ReplyAndReceive(out _, handles, sessionHandle, 0); + } + else + { + throw new NotImplementedException(); + } + } + + public static Result CreateSession(out int serverHandle, out int clientHandle) + { + Result result = HorizonStatic.Syscall.CreateSession(out serverHandle, out clientHandle, false, null); + + if (result == KernelResult.OutOfResource) + { + return HipcResult.OutOfSessions; + } + + return result; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs new file mode 100644 index 000000000..cdb50b578 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs @@ -0,0 +1,65 @@ +using Ryujinx.Common.Utilities; +using Ryujinx.Horizon.Sdk.Sf.Cmif; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct Header + { + private uint _word0; + private uint _word1; + + public CommandType Type + { + get => (CommandType)_word0.Extract(0, 16); + set => _word0 = _word0.Insert(0, 16, (uint)value); + } + + public int SendStaticsCount + { + get => (int)_word0.Extract(16, 4); + set => _word0 = _word0.Insert(16, 4, (uint)value); + } + + public int SendBuffersCount + { + get => (int)_word0.Extract(20, 4); + set => _word0 = _word0.Insert(20, 4, (uint)value); + } + + public int ReceiveBuffersCount + { + get => (int)_word0.Extract(24, 4); + set => _word0 = _word0.Insert(24, 4, (uint)value); + } + + public int ExchangeBuffersCount + { + get => (int)_word0.Extract(28, 4); + set => _word0 = _word0.Insert(28, 4, (uint)value); + } + + public int DataWordsCount + { + get => (int)_word1.Extract(0, 10); + set => _word1 = _word1.Insert(0, 10, (uint)value); + } + + public int ReceiveStaticMode + { + get => (int)_word1.Extract(10, 4); + set => _word1 = _word1.Insert(10, 4, (uint)value); + } + + public int ReceiveListOffset + { + get => (int)_word1.Extract(20, 11); + set => _word1 = _word1.Insert(20, 11, (uint)value); + } + + public bool HasSpecialHeader + { + get => _word1.Extract(31); + set => _word1 = _word1.Insert(31, value); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs new file mode 100644 index 000000000..7778d5bca --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcBufferDescriptor + { +#pragma warning disable CS0649 + private uint _sizeLow; + private uint _addressLow; + private uint _word2; +#pragma warning restore CS0649 + + public ulong Address => _addressLow | (((ulong)_word2 << 4) & 0xf00000000UL) | (((ulong)_word2 << 34) & 0x7000000000UL); + public ulong Size => _sizeLow | ((ulong)_word2 << 8) & 0xf00000000UL; + public HipcBufferMode Mode => (HipcBufferMode)(_word2 & 3); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs new file mode 100644 index 000000000..594af2c84 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + [Flags] + enum HipcBufferFlags : byte + { + In = 1 << 0, + Out = 1 << 1, + MapAlias = 1 << 2, + Pointer = 1 << 3, + FixedSize = 1 << 4, + AutoSelect = 1 << 5, + MapTransferAllowsNonSecure = 1 << 6, + MapTransferAllowsNonDevice = 1 << 7 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs new file mode 100644 index 000000000..4ef6374ba --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + enum HipcBufferMode + { + Normal = 0, + NonSecure = 1, + Invalid = 2, + NonDevice = 3 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs new file mode 100644 index 000000000..ea2ec6505 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs @@ -0,0 +1,115 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + partial class HipcManager : IServiceObject + { + private readonly ServerDomainSessionManager _manager; + private readonly ServerSession _session; + + public HipcManager(ServerDomainSessionManager manager, ServerSession session) + { + _manager = manager; + _session = session; + } + + [CmifCommand(0)] + public Result ConvertCurrentObjectToDomain(out int objectId) + { + objectId = 0; + + var domain = _manager.Domain.AllocateDomainServiceObject(); + if (domain == null) + { + return HipcResult.OutOfDomains; + } + + bool succeeded = false; + + try + { + Span objectIds = stackalloc int[1]; + + Result result = domain.ReserveIds(objectIds); + + if (result.IsFailure) + { + return result; + } + + objectId = objectIds[0]; + succeeded = true; + } + finally + { + if (!succeeded) + { + ServerDomainManager.DestroyDomainServiceObject(domain); + } + } + + domain.RegisterObject(objectId, _session.ServiceObjectHolder); + _session.ServiceObjectHolder = new ServiceObjectHolder(domain); + + return Result.Success; + } + + [CmifCommand(1)] + public Result CopyFromCurrentDomain([MoveHandle] out int clientHandle, int objectId) + { + clientHandle = 0; + + if (!(_session.ServiceObjectHolder.ServiceObject is DomainServiceObject domain)) + { + return HipcResult.TargetNotDomain; + } + + var obj = domain.GetObject(objectId); + if (obj == null) + { + return HipcResult.DomainObjectNotFound; + } + + Api.CreateSession(out int serverHandle, out clientHandle).AbortOnFailure(); + _manager.RegisterSession(serverHandle, obj).AbortOnFailure(); + + return Result.Success; + } + + [CmifCommand(2)] + public Result CloneCurrentObject([MoveHandle] out int clientHandle) + { + return CloneCurrentObjectImpl(out clientHandle, _manager); + } + + [CmifCommand(3)] + public void QueryPointerBufferSize(out ushort size) + { + size = (ushort)_session.PointerBuffer.Size; + } + + [CmifCommand(4)] + public Result CloneCurrentObjectEx([MoveHandle] out int clientHandle, uint tag) + { + return CloneCurrentObjectImpl(out clientHandle, _manager.GetSessionManagerByTag(tag)); + } + + private Result CloneCurrentObjectImpl(out int clientHandle, ServerSessionManager manager) + { + clientHandle = 0; + + var clone = _session.ServiceObjectHolder.Clone(); + if (clone == null) + { + return HipcResult.DomainObjectNotFound; + } + + Api.CreateSession(out int serverHandle, out clientHandle).AbortOnFailure(); + manager.RegisterSession(serverHandle, clone).AbortOnFailure(); + + return Result.Success; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs new file mode 100644 index 000000000..3017f4041 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs @@ -0,0 +1,222 @@ +using Ryujinx.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + ref struct HipcMessage + { + public const int AutoReceiveStatic = byte.MaxValue; + + public HipcMetadata Meta; + public HipcMessageData Data; + public ulong Pid; + + public HipcMessage(Span data) + { + int initialLength = data.Length; + + Header header = MemoryMarshal.Cast(data)[0]; + + data = data.Slice(Unsafe.SizeOf
    ()); + + int receiveStaticsCount = 0; + ulong pid = 0; + + if (header.ReceiveStaticMode != 0) + { + if (header.ReceiveStaticMode == 2) + { + receiveStaticsCount = AutoReceiveStatic; + } + else if (header.ReceiveStaticMode > 2) + { + receiveStaticsCount = header.ReceiveStaticMode - 2; + } + } + + SpecialHeader specialHeader = default; + + if (header.HasSpecialHeader) + { + specialHeader = MemoryMarshal.Cast(data)[0]; + + data = data.Slice(Unsafe.SizeOf()); + + if (specialHeader.SendPid) + { + pid = MemoryMarshal.Cast(data)[0]; + + data = data.Slice(sizeof(ulong)); + } + } + + Meta = new HipcMetadata() + { + Type = (int)header.Type, + SendStaticsCount = header.SendStaticsCount, + SendBuffersCount = header.SendBuffersCount, + ReceiveBuffersCount = header.ReceiveBuffersCount, + ExchangeBuffersCount = header.ExchangeBuffersCount, + DataWordsCount = header.DataWordsCount, + ReceiveStaticsCount = receiveStaticsCount, + SendPid = specialHeader.SendPid, + CopyHandlesCount = specialHeader.CopyHandlesCount, + MoveHandlesCount = specialHeader.MoveHandlesCount + }; + + Data = CreateMessageData(Meta, data, initialLength); + Pid = pid; + } + + public static HipcMessageData WriteResponse( + Span destination, + int sendStaticCount, + int dataWordsCount, + int copyHandlesCount, + int moveHandlesCount) + { + return WriteMessage(destination, new HipcMetadata() + { + SendStaticsCount = sendStaticCount, + DataWordsCount = dataWordsCount, + CopyHandlesCount = copyHandlesCount, + MoveHandlesCount = moveHandlesCount + }); + } + + public static HipcMessageData WriteMessage(Span destination, HipcMetadata meta) + { + int initialLength = destination.Length; + + bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0; + + MemoryMarshal.Cast(destination)[0] = new Header() + { + Type = (CommandType)meta.Type, + SendStaticsCount = meta.SendStaticsCount, + SendBuffersCount = meta.SendBuffersCount, + ReceiveBuffersCount = meta.ReceiveBuffersCount, + ExchangeBuffersCount = meta.ExchangeBuffersCount, + DataWordsCount = meta.DataWordsCount, + ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0, + HasSpecialHeader = hasSpecialHeader + }; + + destination = destination.Slice(Unsafe.SizeOf
    ()); + + if (hasSpecialHeader) + { + MemoryMarshal.Cast(destination)[0] = new SpecialHeader() + { + SendPid = meta.SendPid, + CopyHandlesCount = meta.CopyHandlesCount, + MoveHandlesCount = meta.MoveHandlesCount + }; + + destination = destination.Slice(Unsafe.SizeOf()); + + if (meta.SendPid) + { + destination = destination.Slice(sizeof(ulong)); + } + } + + return CreateMessageData(meta, destination, initialLength); + } + + private static HipcMessageData CreateMessageData(HipcMetadata meta, Span data, int initialLength) + { + Span copyHandles = Span.Empty; + + if (meta.CopyHandlesCount != 0) + { + copyHandles = MemoryMarshal.Cast(data).Slice(0, meta.CopyHandlesCount); + + data = data.Slice(meta.CopyHandlesCount * sizeof(int)); + } + + Span moveHandles = Span.Empty; + + if (meta.MoveHandlesCount != 0) + { + moveHandles = MemoryMarshal.Cast(data).Slice(0, meta.MoveHandlesCount); + + data = data.Slice(meta.MoveHandlesCount * sizeof(int)); + } + + Span sendStatics = Span.Empty; + + if (meta.SendStaticsCount != 0) + { + sendStatics = MemoryMarshal.Cast(data).Slice(0, meta.SendStaticsCount); + + data = data.Slice(meta.SendStaticsCount * Unsafe.SizeOf()); + } + + Span sendBuffers = Span.Empty; + + if (meta.SendBuffersCount != 0) + { + sendBuffers = MemoryMarshal.Cast(data).Slice(0, meta.SendBuffersCount); + + data = data.Slice(meta.SendBuffersCount * Unsafe.SizeOf()); + } + + Span receiveBuffers = Span.Empty; + + if (meta.ReceiveBuffersCount != 0) + { + receiveBuffers = MemoryMarshal.Cast(data).Slice(0, meta.ReceiveBuffersCount); + + data = data.Slice(meta.ReceiveBuffersCount * Unsafe.SizeOf()); + } + + Span exchangeBuffers = Span.Empty; + + if (meta.ExchangeBuffersCount != 0) + { + exchangeBuffers = MemoryMarshal.Cast(data).Slice(0, meta.ExchangeBuffersCount); + + data = data.Slice(meta.ExchangeBuffersCount * Unsafe.SizeOf()); + } + + Span dataWords = Span.Empty; + + if (meta.DataWordsCount != 0) + { + int dataOffset = initialLength - data.Length; + int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10); + + int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); + + dataWords = MemoryMarshal.Cast(data).Slice(padding, meta.DataWordsCount - padding); + + data = data.Slice(meta.DataWordsCount * sizeof(uint)); + } + + Span receiveList = Span.Empty; + + if (meta.ReceiveStaticsCount != 0) + { + int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount; + + receiveList = MemoryMarshal.Cast(data).Slice(0, receiveListSize); + } + + return new HipcMessageData() + { + SendStatics = sendStatics, + SendBuffers = sendBuffers, + ReceiveBuffers = receiveBuffers, + ExchangeBuffers = exchangeBuffers, + DataWords = dataWords, + ReceiveList = receiveList, + CopyHandles = copyHandles, + MoveHandles = moveHandles + }; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs new file mode 100644 index 000000000..c83c422ca --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + ref struct HipcMessageData + { + public Span SendStatics; + public Span SendBuffers; + public Span ReceiveBuffers; + public Span ExchangeBuffers; + public Span DataWords; + public Span ReceiveList; + public Span CopyHandles; + public Span MoveHandles; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs new file mode 100644 index 000000000..fe13137a6 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcMetadata + { + public int Type; + public int SendStaticsCount; + public int SendBuffersCount; + public int ReceiveBuffersCount; + public int ExchangeBuffersCount; + public int DataWordsCount; + public int ReceiveStaticsCount; + public bool SendPid; + public int CopyHandlesCount; + public int MoveHandlesCount; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs new file mode 100644 index 000000000..94bf09685 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcReceiveListEntry + { + private uint _addressLow; + private uint _word1; + + public HipcReceiveListEntry(ulong address, ulong size) + { + _addressLow = (uint)address; + _word1 = (ushort)(address >> 32) | (uint)(size << 16); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs new file mode 100644 index 000000000..ef989a986 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs @@ -0,0 +1,22 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + static class HipcResult + { + public const int ModuleId = 11; + + public static Result OutOfSessionMemory => new Result(ModuleId, 102); + public static Result OutOfSessions => new Result(ModuleId, 131); + public static Result PointerBufferTooSmall => new Result(ModuleId, 141); + public static Result OutOfDomains => new Result(ModuleId, 200); + + public static Result InvalidRequestSize => new Result(ModuleId, 402); + public static Result UnknownCommandType => new Result(ModuleId, 403); + + public static Result InvalidCmifRequest => new Result(ModuleId, 420); + + public static Result TargetNotDomain => new Result(ModuleId, 491); + public static Result DomainObjectNotFound => new Result(ModuleId, 492); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs new file mode 100644 index 000000000..5cebf47c8 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcStaticDescriptor + { + private readonly ulong _data; + + public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32); + public ushort Size => (ushort)(_data >> 16); + public int ReceiveIndex => (int)(_data & 0xf); + + public HipcStaticDescriptor(ulong address, ushort size, int receiveIndex) + { + ulong data = (uint)(receiveIndex & 0xf) | ((uint)size << 16); + + data |= address << 32; + data |= (address >> 20) & 0xf000; + data |= (address >> 30) & 0xffc0; + + _data = data; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs new file mode 100644 index 000000000..e087cb223 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct ManagerOptions + { + public static ManagerOptions Default => new ManagerOptions(0, 0, 0, false); + + public int PointerBufferSize { get; } + public int MaxDomains { get; } + public int MaxDomainObjects { get; } + public bool CanDeferInvokeRequest { get; } + + public ManagerOptions(int pointerBufferSize, int maxDomains, int maxDomainObjects, bool canDeferInvokeRequest) + { + PointerBufferSize = pointerBufferSize; + MaxDomains = maxDomains; + MaxDomainObjects = maxDomainObjects; + CanDeferInvokeRequest = canDeferInvokeRequest; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs new file mode 100644 index 000000000..7c380a011 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + enum ReceiveResult + { + Success, + Closed, + NeedsRetry + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs new file mode 100644 index 000000000..923f2d52d --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs @@ -0,0 +1,36 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class Server : MultiWaitHolderOfHandle + { + public int PortIndex { get; } + public int PortHandle { get; } + public ServiceName Name { get; } + public bool Managed { get; } + public ServiceObjectHolder StaticObject { get; } + + public Server( + int portIndex, + int portHandle, + ServiceName name, + bool managed, + ServiceObjectHolder staticHoder) : base(portHandle) + { + PortHandle = portHandle; + Name = name; + Managed = managed; + + if (staticHoder != null) + { + StaticObject = staticHoder; + } + else + { + PortIndex = portIndex; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs new file mode 100644 index 000000000..d920a6591 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs @@ -0,0 +1,23 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerDomainSessionManager : ServerSessionManager + { + public ServerDomainManager Domain { get; } + + public ServerDomainSessionManager(int entryCount, int maxDomains) + { + Domain = new ServerDomainManager(entryCount, maxDomains); + } + + protected override Result DispatchManagerRequest(ServerSession session, Span inMessage, Span outMessage) + { + HipcManager hipcManager = new HipcManager(this, session); + + return DispatchRequest(new ServiceObjectHolder(hipcManager), session, inMessage, outMessage); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs new file mode 100644 index 000000000..5bb2de25d --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs @@ -0,0 +1,198 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerManager : ServerManagerBase, IDisposable + { + private readonly SmApi _sm; + private readonly int _pointerBufferSize; + private readonly bool _canDeferInvokeRequest; + private readonly int _maxSessions; + + private ulong _pointerBuffersBaseAddress; + private ulong _savedMessagesBaseAddress; + + private readonly object _resourceLock; + private readonly ulong[] _sessionAllocationBitmap; + private readonly HashSet _sessions; + private readonly HashSet _servers; + + public ServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(sm, options) + { + _sm = sm; + _pointerBufferSize = options.PointerBufferSize; + _canDeferInvokeRequest = options.CanDeferInvokeRequest; + _maxSessions = maxSessions; + + if (allocator != null) + { + _pointerBuffersBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)options.PointerBufferSize); + + if (options.CanDeferInvokeRequest) + { + _savedMessagesBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)Api.TlsMessageBufferSize); + } + } + + _resourceLock = new object(); + _sessionAllocationBitmap = new ulong[(maxSessions + 63) / 64]; + _sessions = new HashSet(); + _servers = new HashSet(); + } + + private PointerAndSize GetObjectBySessionIndex(ServerSession session, ulong baseAddress, ulong size) + { + return new PointerAndSize(baseAddress + (ulong)session.SessionIndex * size, size); + } + + protected override ServerSession AllocateSession(int sessionHandle, ServiceObjectHolder obj) + { + int sessionIndex = -1; + + lock (_resourceLock) + { + if (_sessions.Count >= _maxSessions) + { + return null; + } + + for (int i = 0; i <_sessionAllocationBitmap.Length; i++) + { + ref ulong mask = ref _sessionAllocationBitmap[i]; + + if (mask != ulong.MaxValue) + { + int bit = BitOperations.TrailingZeroCount(~mask); + sessionIndex = i * 64 + bit; + mask |= 1UL << bit; + + break; + } + } + + if (sessionIndex == -1) + { + return null; + } + + ServerSession session = new ServerSession(sessionIndex, sessionHandle, obj); + + _sessions.Add(session); + + return session; + } + } + + protected override void FreeSession(ServerSession session) + { + if (session.ServiceObjectHolder.ServiceObject is IDisposable disposableObj) + { + disposableObj.Dispose(); + } + + lock (_resourceLock) + { + _sessionAllocationBitmap[session.SessionIndex / 64] &= ~(1UL << (session.SessionIndex & 63)); + _sessions.Remove(session); + } + } + + protected override Server AllocateServer( + int portIndex, + int portHandle, + ServiceName name, + bool managed, + ServiceObjectHolder staticHoder) + { + lock (_resourceLock) + { + Server server = new Server(portIndex, portHandle, name, managed, staticHoder); + + _servers.Add(server); + + return server; + } + } + + protected override void DestroyServer(Server server) + { + lock (_resourceLock) + { + server.UnlinkFromMultiWaitHolder(); + Os.FinalizeMultiWaitHolder(server); + + if (server.Managed) + { + // We should AbortOnFailure, but sometimes SM is already gone when this is called, + // so let's just ignore potential errors. + _sm.UnregisterService(server.Name); + + HorizonStatic.Syscall.CloseHandle(server.PortHandle); + } + + _servers.Remove(server); + } + } + + protected override PointerAndSize GetSessionPointerBuffer(ServerSession session) + { + if (_pointerBufferSize > 0) + { + return GetObjectBySessionIndex(session, _pointerBuffersBaseAddress, (ulong)_pointerBufferSize); + } + else + { + return PointerAndSize.Empty; + } + } + + protected override PointerAndSize GetSessionSavedMessageBuffer(ServerSession session) + { + if (_canDeferInvokeRequest) + { + return GetObjectBySessionIndex(session, _savedMessagesBaseAddress, Api.TlsMessageBufferSize); + } + else + { + return PointerAndSize.Empty; + } + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (_resourceLock) + { + ServerSession[] sessionsToClose = new ServerSession[_sessions.Count]; + + _sessions.CopyTo(sessionsToClose); + + foreach (ServerSession session in sessionsToClose) + { + CloseSessionImpl(session); + } + + Server[] serversToClose = new Server[_servers.Count]; + + _servers.CopyTo(serversToClose); + + foreach (Server server in serversToClose) + { + DestroyServer(server); + } + } + } + } + + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs new file mode 100644 index 000000000..68cae6bc4 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -0,0 +1,307 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerManagerBase : ServerDomainSessionManager + { + private readonly SmApi _sm; + + private bool _canDeferInvokeRequest; + + private readonly MultiWait _multiWait; + private readonly MultiWait _waitList; + + private readonly object _multiWaitSelectionLock; + private readonly object _waitListLock; + + private readonly Event _requestStopEvent; + private readonly Event _notifyEvent; + + private readonly MultiWaitHolderBase _requestStopEventHolder; + private readonly MultiWaitHolderBase _notifyEventHolder; + + private enum UserDataTag + { + Server = 1, + Session = 2 + } + + public ServerManagerBase(SmApi sm, ManagerOptions options) : base(options.MaxDomainObjects, options.MaxDomains) + { + _sm = sm; + _canDeferInvokeRequest = options.CanDeferInvokeRequest; + + _multiWait = new MultiWait(); + _waitList = new MultiWait(); + + _multiWaitSelectionLock = new object(); + _waitListLock = new object(); + + _requestStopEvent = new Event(EventClearMode.ManualClear); + _notifyEvent = new Event(EventClearMode.ManualClear); + + _requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent); + _multiWait.LinkMultiWaitHolder(_requestStopEventHolder); + _notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent); + _multiWait.LinkMultiWaitHolder(_notifyEventHolder); + } + + public void RegisterObjectForServer(IServiceObject staticObject, int portHandle) + { + RegisterServerImpl(0, new ServiceObjectHolder(staticObject), portHandle); + } + + public Result RegisterObjectForServer(IServiceObject staticObject, ServiceName name, int maxSessions) + { + return RegisterServerImpl(0, new ServiceObjectHolder(staticObject), name, maxSessions); + } + + public void RegisterServer(int portIndex, int portHandle) + { + RegisterServerImpl(portIndex, null, portHandle); + } + + public Result RegisterServer(int portIndex, ServiceName name, int maxSessions) + { + return RegisterServerImpl(portIndex, null, name, maxSessions); + } + + private void RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, int portHandle) + { + Server server = AllocateServer(portIndex, portHandle, ServiceName.Invalid, managed: false, staticHolder); + RegisterServerImpl(server); + } + + private Result RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, ServiceName name, int maxSessions) + { + Result result = _sm.RegisterService(out int portHandle, name, maxSessions, isLight: false); + + if (result.IsFailure) + { + return result; + } + + Server server = AllocateServer(portIndex, portHandle, name, managed: true, staticHolder); + RegisterServerImpl(server); + + return Result.Success; + } + + private void RegisterServerImpl(Server server) + { + server.UserData = UserDataTag.Server; + + _multiWait.LinkMultiWaitHolder(server); + } + + protected virtual Result OnNeedsToAccept(int portIndex, Server server) + { + throw new NotSupportedException(); + } + + public void ServiceRequests() + { + while (WaitAndProcessRequestsImpl()); + } + + public void WaitAndProcessRequests() + { + WaitAndProcessRequestsImpl(); + } + + private bool WaitAndProcessRequestsImpl() + { + try + { + MultiWaitHolder multiWait = WaitSignaled(); + + if (multiWait == null) + { + return false; + } + + DebugUtil.Assert(Process(multiWait).IsSuccess); + + return HorizonStatic.ThreadContext.Running; + } + catch (ThreadTerminatedException) + { + return false; + } + } + + private MultiWaitHolder WaitSignaled() + { + lock (_multiWaitSelectionLock) + { + while (true) + { + ProcessWaitList(); + + MultiWaitHolder selected = _multiWait.WaitAny(); + + if (selected == _requestStopEventHolder) + { + return null; + } + else if (selected == _notifyEventHolder) + { + _notifyEvent.Clear(); + } + else + { + selected.UnlinkFromMultiWaitHolder(); + + return selected; + } + } + } + } + + public void ResumeProcessing() + { + _requestStopEvent.Clear(); + } + + public void RequestStopProcessing() + { + _requestStopEvent.Signal(); + } + + protected override void RegisterSessionToWaitList(ServerSession session) + { + session.HasReceived = false; + session.UserData = UserDataTag.Session; + RegisterToWaitList(session); + } + + private void RegisterToWaitList(MultiWaitHolder holder) + { + lock (_waitListLock) + { + _waitList.LinkMultiWaitHolder(holder); + _notifyEvent.Signal(); + } + } + + private void ProcessWaitList() + { + lock (_waitListLock) + { + _multiWait.MoveAllFrom(_waitList); + } + } + + private Result Process(MultiWaitHolder holder) + { + switch ((UserDataTag)holder.UserData) + { + case UserDataTag.Server: + return ProcessForServer(holder); + case UserDataTag.Session: + return ProcessForSession(holder); + default: + throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()); + } + } + + private Result ProcessForServer(MultiWaitHolder holder) + { + DebugUtil.Assert((UserDataTag)holder.UserData == UserDataTag.Server); + + Server server = (Server)holder; + + try + { + if (server.StaticObject != null) + { + return AcceptSession(server.PortHandle, server.StaticObject.Clone()); + } + else + { + return OnNeedsToAccept(server.PortIndex, server); + } + } + finally + { + RegisterToWaitList(server); + } + } + + private Result ProcessForSession(MultiWaitHolder holder) + { + DebugUtil.Assert((UserDataTag)holder.UserData == UserDataTag.Session); + + ServerSession session = (ServerSession)holder; + + using var tlsMessage = HorizonStatic.AddressSpace.GetWritableRegion(HorizonStatic.ThreadContext.TlsAddress, Api.TlsMessageBufferSize); + + Result result; + + if (_canDeferInvokeRequest) + { + // If the request is deferred, we save the message on a temporary buffer to process it later. + using var savedMessage = HorizonStatic.AddressSpace.GetWritableRegion(session.SavedMessage.Address, (int)session.SavedMessage.Size); + + DebugUtil.Assert(tlsMessage.Memory.Length == savedMessage.Memory.Length); + + if (!session.HasReceived) + { + result = ReceiveRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure) + { + return result; + } + + session.HasReceived = true; + tlsMessage.Memory.Span.CopyTo(savedMessage.Memory.Span); + } + else + { + savedMessage.Memory.Span.CopyTo(tlsMessage.Memory.Span); + } + + result = ProcessRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure && !SfResult.Invalidated(result)) + { + return result; + } + } + else + { + if (!session.HasReceived) + { + result = ReceiveRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure) + { + return result; + } + + session.HasReceived = true; + } + + result = ProcessRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure) + { + // Those results are not valid because the service does not support deferral. + if (SfResult.RequestDeferred(result) || SfResult.Invalidated(result)) + { + result.AbortOnFailure(); + } + + return result; + } + } + + return Result.Success; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs new file mode 100644 index 000000000..eb98fefd0 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs @@ -0,0 +1,23 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerSession : MultiWaitHolderOfHandle + { + public ServiceObjectHolder ServiceObjectHolder { get; set; } + public PointerAndSize PointerBuffer { get; set; } + public PointerAndSize SavedMessage { get; set; } + public int SessionIndex { get; } + public int SessionHandle { get; } + public bool IsClosed { get; set; } + public bool HasReceived { get; set; } + + public ServerSession(int index, int handle, ServiceObjectHolder obj) : base(handle) + { + ServiceObjectHolder = obj; + SessionIndex = index; + SessionHandle = handle; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs new file mode 100644 index 000000000..e85892f26 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs @@ -0,0 +1,335 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerSessionManager + { + public Result AcceptSession(int portHandle, ServiceObjectHolder obj) + { + return AcceptSession(out _, portHandle, obj); + } + + private Result AcceptSession(out ServerSession session, int portHandle, ServiceObjectHolder obj) + { + return AcceptSessionImpl(out session, portHandle, obj); + } + + private Result AcceptSessionImpl(out ServerSession session, int portHandle, ServiceObjectHolder obj) + { + session = null; + + Result result = HorizonStatic.Syscall.AcceptSession(out int sessionHandle, portHandle); + + if (result.IsFailure) + { + return result; + } + + bool succeeded = false; + + try + { + result = RegisterSessionImpl(out session, sessionHandle, obj); + + if (result.IsFailure) + { + return result; + } + + succeeded = true; + } + finally + { + if (!succeeded) + { + HorizonStatic.Syscall.CloseHandle(sessionHandle); + } + } + + return Result.Success; + } + + public Result RegisterSession(int sessionHandle, ServiceObjectHolder obj) + { + return RegisterSession(out _, sessionHandle, obj); + } + + public Result RegisterSession(out ServerSession session, int sessionHandle, ServiceObjectHolder obj) + { + return RegisterSessionImpl(out session, sessionHandle, obj); + } + + private Result RegisterSessionImpl(out ServerSession session, int sessionHandle, ServiceObjectHolder obj) + { + Result result = CreateSessionImpl(out session, sessionHandle, obj); + + if (result.IsFailure) + { + return result; + } + + session.PointerBuffer = GetSessionPointerBuffer(session); + session.SavedMessage = GetSessionSavedMessageBuffer(session); + + RegisterSessionToWaitList(session); + return Result.Success; + } + + protected virtual void RegisterSessionToWaitList(ServerSession session) + { + throw new NotSupportedException(); + } + + private Result CreateSessionImpl(out ServerSession session, int sessionHandle, ServiceObjectHolder obj) + { + session = AllocateSession(sessionHandle, obj); + + if (session == null) + { + return HipcResult.OutOfSessionMemory; + } + + return Result.Success; + } + + protected virtual ServerSession AllocateSession(int sessionHandle, ServiceObjectHolder obj) + { + throw new NotSupportedException(); + } + + protected virtual void FreeSession(ServerSession session) + { + throw new NotSupportedException(); + } + + protected virtual Server AllocateServer( + int portIndex, + int portHandle, + ServiceName name, + bool managed, + ServiceObjectHolder staticHoder) + { + throw new NotSupportedException(); + } + + protected virtual void DestroyServer(Server server) + { + throw new NotSupportedException(); + } + + protected virtual PointerAndSize GetSessionPointerBuffer(ServerSession session) + { + throw new NotSupportedException(); + } + + protected virtual PointerAndSize GetSessionSavedMessageBuffer(ServerSession session) + { + throw new NotSupportedException(); + } + + private void DestroySession(ServerSession session) + { + FreeSession(session); + } + + protected void CloseSessionImpl(ServerSession session) + { + int sessionHandle = session.Handle; + Os.FinalizeMultiWaitHolder(session); + DestroySession(session); + HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure(); + } + + private static CommandType GetCmifCommandType(ReadOnlySpan message) + { + return MemoryMarshal.Cast(message)[0].Type; + } + + public Result ProcessRequest(ServerSession session, Span message) + { + if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close) + { + CloseSessionImpl(session); + return Result.Success; + } + else + { + Result result = ProcessRequestImpl(session, message, message); + + if (result.IsSuccess) + { + RegisterSessionToWaitList(session); + return Result.Success; + } + else if (SfResult.RequestContextChanged(result)) + { + return result; + } + else + { + Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}"); + + CloseSessionImpl(session); + return Result.Success; + } + } + } + + private Result ProcessRequestImpl(ServerSession session, Span inMessage, Span outMessage) + { + CommandType commandType = GetCmifCommandType(inMessage); + + using var _ = new ScopedInlineContextChange(GetInlineContext(commandType, inMessage)); + + switch (commandType) + { + case CommandType.Request: + case CommandType.RequestWithContext: + return DispatchRequest(session.ServiceObjectHolder, session, inMessage, outMessage); + case CommandType.Control: + case CommandType.ControlWithContext: + return DispatchManagerRequest(session, inMessage, outMessage); + default: + return HipcResult.UnknownCommandType; + } + } + + private static int GetInlineContext(CommandType commandType, ReadOnlySpan inMessage) + { + switch (commandType) + { + case CommandType.RequestWithContext: + case CommandType.ControlWithContext: + if (inMessage.Length >= 0x10) + { + return MemoryMarshal.Cast(inMessage)[3]; + } + break; + } + + return 0; + } + + protected Result ReceiveRequest(ServerSession session, Span message) + { + return ReceiveRequestImpl(session, message); + } + + private Result ReceiveRequestImpl(ServerSession session, Span message) + { + PointerAndSize pointerBuffer = session.PointerBuffer; + + while (true) + { + if (pointerBuffer.Address != 0) + { + HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata() + { + Type = (int)CommandType.Invalid, + ReceiveStaticsCount = HipcMessage.AutoReceiveStatic + }); + + messageData.ReceiveList[0] = new HipcReceiveListEntry(pointerBuffer.Address, pointerBuffer.Size); + } + else + { + MemoryMarshal.Cast(message)[0] = new Header() + { + Type = CommandType.Invalid + }; + } + + Result result = Api.Receive(out ReceiveResult recvResult, session.Handle, message); + + if (result.IsFailure) + { + return result; + } + + switch (recvResult) + { + case ReceiveResult.Success: + session.IsClosed = false; + return Result.Success; + case ReceiveResult.Closed: + session.IsClosed = true; + return Result.Success; + } + } + } + + protected virtual Result DispatchManagerRequest(ServerSession session, Span inMessage, Span outMessage) + { + return SfResult.NotSupported; + } + + protected virtual Result DispatchRequest( + ServiceObjectHolder objectHolder, + ServerSession session, + Span inMessage, + Span outMessage) + { + HipcMessage request; + + try + { + request = new HipcMessage(inMessage); + } + catch (ArgumentOutOfRangeException) + { + return HipcResult.InvalidRequestSize; + } + + var dispatchCtx = new ServiceDispatchContext() + { + ServiceObject = objectHolder.ServiceObject, + Manager = this, + Session = session, + HandlesToClose = new HandlesToClose(), + PointerBuffer = session.PointerBuffer, + InMessageBuffer = inMessage, + OutMessageBuffer = outMessage, + Request = request + }; + + ReadOnlySpan inRawData = MemoryMarshal.Cast(dispatchCtx.Request.Data.DataWords); + + int inRawSize = dispatchCtx.Request.Meta.DataWordsCount * sizeof(uint); + + if (inRawSize < 0x10) + { + return HipcResult.InvalidRequestSize; + } + + Result result = objectHolder.ProcessMessage(ref dispatchCtx, inRawData); + + if (result.IsFailure) + { + return result; + } + + result = Api.Reply(session.SessionHandle, outMessage); + + ref var handlesToClose = ref dispatchCtx.HandlesToClose; + + for (int i = 0; i < handlesToClose.Count; i++) + { + HorizonStatic.Syscall.CloseHandle(handlesToClose[i]).AbortOnFailure(); + } + + return result; + } + + public ServerSessionManager GetSessionManagerByTag(uint tag) + { + // Official FW does not do anything with the tag currently. + return this; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs new file mode 100644 index 000000000..8b747626d --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs @@ -0,0 +1,27 @@ +using Ryujinx.Common.Utilities; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct SpecialHeader + { + private uint _word; + + public bool SendPid + { + get => _word.Extract(0); + set => _word = _word.Insert(0, value); + } + + public int CopyHandlesCount + { + get => (int)_word.Extract(1, 4); + set => _word = _word.Insert(1, 4, (uint)value); + } + + public int MoveHandlesCount + { + get => (int)_word.Extract(5, 4); + set => _word = _word.Insert(5, 4, (uint)value); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs new file mode 100644 index 000000000..53202edee --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs @@ -0,0 +1,421 @@ +using Ryujinx.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + class HipcCommandProcessor : ServerMessageProcessor + { + private readonly CommandArg[] _args; + + private readonly int[] _inOffsets; + private readonly int[] _outOffsets; + private readonly PointerAndSize[] _bufferRanges; + + private readonly bool _hasInProcessIdHolder; + private readonly int _inObjectsCount; + private readonly int _outObjectsCount; + private readonly int _inMapAliasBuffersCount; + private readonly int _outMapAliasBuffersCount; + private readonly int _inPointerBuffersCount; + private readonly int _outPointerBuffersCount; + private readonly int _outFixedSizePointerBuffersCount; + private readonly int _inMoveHandlesCount; + private readonly int _inCopyHandlesCount; + private readonly int _outMoveHandlesCount; + private readonly int _outCopyHandlesCount; + + public int FunctionArgumentsCount => _args.Length; + + public int InRawDataSize => BitUtils.AlignUp(_inOffsets[^1], sizeof(ushort)); + public int OutRawDataSize => BitUtils.AlignUp(_outOffsets[^1], sizeof(uint)); + + private int OutUnfixedSizePointerBuffersCount => _outPointerBuffersCount - _outFixedSizePointerBuffersCount; + + public HipcCommandProcessor(CommandArg[] args) + { + _args = args; + + for (int i = 0; i < args.Length; i++) + { + var argInfo = args[i]; + + switch (argInfo.Type) + { + case CommandArgType.Buffer: + var flags = argInfo.BufferFlags; + + if (flags.HasFlag(HipcBufferFlags.In)) + { + if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + { + _inMapAliasBuffersCount++; + _inPointerBuffersCount++; + } + else if (flags.HasFlag(HipcBufferFlags.MapAlias)) + { + _inMapAliasBuffersCount++; + } + else if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + _inPointerBuffersCount++; + } + } + else + { + bool autoSelect = flags.HasFlag(HipcBufferFlags.AutoSelect); + if (autoSelect || flags.HasFlag(HipcBufferFlags.Pointer)) + { + _outPointerBuffersCount++; + + if (flags.HasFlag(HipcBufferFlags.FixedSize)) + { + _outFixedSizePointerBuffersCount++; + } + } + + if (autoSelect || flags.HasFlag(HipcBufferFlags.MapAlias)) + { + _outMapAliasBuffersCount++; + } + } + break; + case CommandArgType.InCopyHandle: + _inCopyHandlesCount++; + break; + case CommandArgType.InMoveHandle: + _inMoveHandlesCount++; + break; + case CommandArgType.InObject: + _inObjectsCount++; + break; + case CommandArgType.ProcessId: + _hasInProcessIdHolder = true; + break; + case CommandArgType.OutCopyHandle: + _outCopyHandlesCount++; + break; + case CommandArgType.OutMoveHandle: + _outMoveHandlesCount++; + break; + case CommandArgType.OutObject: + _outObjectsCount++; + break; + } + } + + _inOffsets = RawDataOffsetCalculator.Calculate(args.Where(x => x.Type == CommandArgType.InArgument).ToArray()); + _outOffsets = RawDataOffsetCalculator.Calculate(args.Where(x => x.Type == CommandArgType.OutArgument).ToArray()); + _bufferRanges = new PointerAndSize[args.Length]; + } + + public int GetInArgOffset(int argIndex) + { + return _inOffsets[argIndex]; + } + + public int GetOutArgOffset(int argIndex) + { + return _outOffsets[argIndex]; + } + + public PointerAndSize GetBufferRange(int argIndex) + { + return _bufferRanges[argIndex]; + } + + public Result ProcessBuffers(ref ServiceDispatchContext context, bool[] isBufferMapAlias, ServerMessageRuntimeMetadata runtimeMetadata) + { + bool mapAliasBuffersValid = true; + + ulong pointerBufferTail = context.PointerBuffer.Address; + ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size; + + int sendMapAliasIndex = 0; + int recvMapAliasIndex = 0; + int sendPointerIndex = 0; + int unfixedRecvPointerIndex = 0; + + for (int i = 0; i < _args.Length; i++) + { + if (_args[i].Type != CommandArgType.Buffer) + { + continue; + } + + var flags = _args[i].BufferFlags; + bool isMapAlias; + + if (flags.HasFlag(HipcBufferFlags.MapAlias)) + { + isMapAlias = true; + } + else if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + isMapAlias = false; + } + else /* if (flags.HasFlag(HipcBufferFlags.HipcAutoSelect)) */ + { + var descriptor = flags.HasFlag(HipcBufferFlags.In) + ? context.Request.Data.SendBuffers[sendMapAliasIndex] + : context.Request.Data.ReceiveBuffers[recvMapAliasIndex]; + + isMapAlias = descriptor.Address != 0UL; + } + + isBufferMapAlias[i] = isMapAlias; + + if (isMapAlias) + { + var descriptor = flags.HasFlag(HipcBufferFlags.In) + ? context.Request.Data.SendBuffers[sendMapAliasIndex++] + : context.Request.Data.ReceiveBuffers[recvMapAliasIndex++]; + + _bufferRanges[i] = new PointerAndSize(descriptor.Address, descriptor.Size); + + if (!IsMapTransferModeValid(flags, descriptor.Mode)) + { + mapAliasBuffersValid = false; + } + } + else + { + if (flags.HasFlag(HipcBufferFlags.In)) + { + var descriptor = context.Request.Data.SendStatics[sendPointerIndex++]; + ulong address = descriptor.Address; + ulong size = descriptor.Size; + _bufferRanges[i] = new PointerAndSize(address, size); + + if (size != 0) + { + pointerBufferTail = Math.Max(pointerBufferTail, address + size); + } + } + else /* if (flags.HasFlag(HipcBufferFlags.Out)) */ + { + ulong size; + + if (flags.HasFlag(HipcBufferFlags.FixedSize)) + { + size = _args[i].BufferFixedSize; + } + else + { + var data = MemoryMarshal.Cast(context.Request.Data.DataWords); + var recvPointerSizes = MemoryMarshal.Cast(data.Slice(runtimeMetadata.UnfixedOutPointerSizeOffset)); + size = recvPointerSizes[unfixedRecvPointerIndex++]; + } + + pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL); + _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); + } + } + } + + if (!mapAliasBuffersValid) + { + return HipcResult.InvalidCmifRequest; + } + + if (_outPointerBuffersCount != 0 && pointerBufferTail > pointerBufferHead) + { + return HipcResult.PointerBufferTooSmall; + } + + return Result.Success; + } + + private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode) + { + if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure)) + { + return mode == HipcBufferMode.NonSecure; + } + else if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice)) + { + return mode == HipcBufferMode.NonDevice; + } + else + { + return mode == HipcBufferMode.Normal; + } + } + + public void SetOutBuffers(HipcMessageData response, bool[] isBufferMapAlias) + { + int recvPointerIndex = 0; + + for (int i = 0; i < _args.Length; i++) + { + if (_args[i].Type != CommandArgType.Buffer) + { + continue; + } + + var flags = _args[i].BufferFlags; + if (flags.HasFlag(HipcBufferFlags.Out)) + { + var buffer = _bufferRanges[i]; + + if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); + } + else if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + { + if (!isBufferMapAlias[i]) + { + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); + } + else + { + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(0UL, 0, recvPointerIndex); + } + } + + recvPointerIndex++; + } + } + } + + public override void SetImplementationProcessor(ServerMessageProcessor impl) + { + // We don't need to do anything here as this should be always the last processor to be called. + } + + public override ServerMessageRuntimeMetadata GetRuntimeMetadata() + { + return new ServerMessageRuntimeMetadata( + (ushort)InRawDataSize, + (ushort)OutRawDataSize, + (byte)Unsafe.SizeOf(), + (byte)Unsafe.SizeOf(), + (byte)_inObjectsCount, + (byte)_outObjectsCount); + } + + public override Result PrepareForProcess(ref ServiceDispatchContext context, ServerMessageRuntimeMetadata runtimeMetadata) + { + ref var meta = ref context.Request.Meta; + bool requestValid = true; + requestValid &= meta.SendPid == _hasInProcessIdHolder; + requestValid &= meta.SendStaticsCount == _inPointerBuffersCount; + requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount; + requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount; + requestValid &= meta.ExchangeBuffersCount == 0; + requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount; + requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount; + + int rawSizeInBytes = meta.DataWordsCount * sizeof(uint); + int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint)); + requestValid &= rawSizeInBytes >= commandRawSize; + + return requestValid ? Result.Success : HipcResult.InvalidCmifRequest; + } + + public Result GetInObjects(ServerMessageProcessor processor, Span objects) + { + if (objects.Length == 0) + { + return Result.Success; + } + + ServiceObjectHolder[] inObjects = new ServiceObjectHolder[objects.Length]; + Result result = processor.GetInObjects(inObjects); + + if (result.IsFailure) + { + return result; + } + + int inObjectIndex = 0; + + for (int i = 0; i < _args.Length; i++) + { + if (_args[i].Type == CommandArgType.InObject) + { + int index = inObjectIndex++; + var inObject = inObjects[index]; + + objects[index] = inObject?.ServiceObject; + } + } + + return Result.Success; + } + + public override Result GetInObjects(Span inObjects) + { + return SfResult.NotSupported; + } + + public override HipcMessageData PrepareForReply(scoped ref ServiceDispatchContext context, out Span outRawData, ServerMessageRuntimeMetadata runtimeMetadata) + { + int rawDataSize = OutRawDataSize + runtimeMetadata.OutHeadersSize; + var response = HipcMessage.WriteResponse( + context.OutMessageBuffer, + _outPointerBuffersCount, + (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint), + _outCopyHandlesCount, + _outMoveHandlesCount + runtimeMetadata.OutObjectsCount); + outRawData = MemoryMarshal.Cast(response.DataWords); + return response; + } + + public override void PrepareForErrorReply(scoped ref ServiceDispatchContext context, out Span outRawData, ServerMessageRuntimeMetadata runtimeMetadata) + { + int rawDataSize = runtimeMetadata.OutHeadersSize; + var response = HipcMessage.WriteResponse( + context.OutMessageBuffer, + 0, + (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint), + 0, + 0); + outRawData = MemoryMarshal.Cast(response.DataWords); + } + + public void SetOutObjects(ref ServiceDispatchContext context, HipcMessageData response, Span objects) + { + if (objects.Length == 0) + { + return; + } + + ServiceObjectHolder[] outObjects = new ServiceObjectHolder[objects.Length]; + + for (int i = 0; i < objects.Length; i++) + { + outObjects[i] = objects[i] != null ? new ServiceObjectHolder(objects[i]) : null; + } + + context.Processor.SetOutObjects(ref context, response, outObjects); + } + + public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span outObjects) + { + for (int index = 0; index < _outObjectsCount; index++) + { + SetOutObjectImpl(index, response, context.Manager, outObjects[index]); + } + } + + private void SetOutObjectImpl(int index, HipcMessageData response, ServerSessionManager manager, ServiceObjectHolder obj) + { + if (obj == null) + { + response.MoveHandles[index] = 0; + return; + } + + Api.CreateSession(out int serverHandle, out int clientHandle).AbortOnFailure(); + manager.RegisterSession(serverHandle, obj).AbortOnFailure(); + response.MoveHandles[index] = clientHandle; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/IServiceObject.cs b/Ryujinx.Horizon/Sdk/Sf/IServiceObject.cs new file mode 100644 index 000000000..afa57fef9 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/IServiceObject.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + interface IServiceObject + { + IReadOnlyDictionary GetCommandHandlers(); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs new file mode 100644 index 000000000..982f454f0 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs @@ -0,0 +1,51 @@ +using Ryujinx.Common; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + static class RawDataOffsetCalculator + { + public static int[] Calculate(CommandArg[] args) + { + int[] offsets = new int[args.Length + 1]; + + if (args.Length != 0) + { + int argsCount = args.Length; + + int[] sizes = new int[argsCount]; + int[] aligns = new int[argsCount]; + int[] map = new int[argsCount]; + + for (int i = 0; i < argsCount; i++) + { + sizes[i] = args[i].ArgSize; + aligns[i] = args[i].ArgAlignment; + map[i] = i; + } + + for (int i = 1; i < argsCount; i++) + { + for (int j = i; j > 0 && aligns[map[j - 1]] > aligns[map[j]]; j--) + { + var temp = map[j - 1]; + map[j - 1] = map[j]; + map[j] = temp; + } + } + + int offset = 0; + + foreach (int i in map) + { + offset = BitUtils.AlignUp(offset, aligns[i]); + offsets[i] = offset; + offset += sizes[i]; + } + + offsets[argsCount] = offset; + } + + return offsets; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs new file mode 100644 index 000000000..6aa11ba5f --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs @@ -0,0 +1,31 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + static class SfResult + { + public const int ModuleId = 10; + + public static Result NotSupported => new Result(ModuleId, 1); + public static Result InvalidHeaderSize => new Result(ModuleId, 202); + public static Result InvalidInHeader => new Result(ModuleId, 211); + public static Result InvalidOutHeader => new Result(ModuleId, 212); + public static Result UnknownCommandId => new Result(ModuleId, 221); + public static Result InvalidOutRawSize => new Result(ModuleId, 232); + public static Result InvalidInObjectsCount => new Result(ModuleId, 235); + public static Result InvalidOutObjectsCount => new Result(ModuleId, 236); + public static Result InvalidInObject => new Result(ModuleId, 239); + + public static Result TargetNotFound => new Result(ModuleId, 261); + + public static Result OutOfDomainEntries => new Result(ModuleId, 301); + + public static Result InvalidatedByUser => new Result(ModuleId, 802); + public static Result RequestDeferredByUser => new Result(ModuleId, 812); + + public static bool RequestContextChanged(Result result) => result.InRange(800, 899); + public static bool Invalidated(Result result) => result.InRange(801, 809); + + public static bool RequestDeferred(Result result) => result.InRange(811, 819); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs b/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs new file mode 100644 index 000000000..dbb300785 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs @@ -0,0 +1,98 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sm +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ServiceName + { + public static ServiceName Invalid { get; } = new ServiceName(0); + + public bool IsInvalid => Packed == 0; + + public int Length => sizeof(ulong); + + public ulong Packed { get; } + + public byte this[int index] + { + get + { + if ((uint)index >= sizeof(ulong)) + { + throw new IndexOutOfRangeException(); + } + + return (byte)(Packed >> (index * 8)); + } + } + + private ServiceName(ulong packed) + { + Packed = packed; + } + + public static ServiceName Encode(string name) + { + ulong packed = 0; + + for (int index = 0; index < sizeof(ulong); index++) + { + if (index < name.Length) + { + packed |= (ulong)(byte)name[index] << (index * 8); + } + else + { + break; + } + } + + return new ServiceName(packed); + } + + public override bool Equals(object obj) + { + return obj is ServiceName serviceName && serviceName.Equals(this); + } + + public bool Equals(ServiceName other) + { + return other.Packed == Packed; + } + + public override int GetHashCode() + { + return Packed.GetHashCode(); + } + + public static bool operator ==(ServiceName lhs, ServiceName rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(ServiceName lhs, ServiceName rhs) + { + return !lhs.Equals(rhs); + } + + public override string ToString() + { + string name = string.Empty; + + for (int index = 0; index < sizeof(ulong); index++) + { + byte character = (byte)(Packed >> (index * 8)); + + if (character == 0) + { + break; + } + + name += (char)character; + } + + return name; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs new file mode 100644 index 000000000..e4b0eea1c --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs @@ -0,0 +1,107 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; + +namespace Ryujinx.Horizon.Sdk.Sm +{ + class SmApi + { + private int _portHandle; + + public Result Initialize() + { + Result result = HorizonStatic.Syscall.ConnectToNamedPort(out int portHandle, "sm:"); + + while (result == KernelResult.NotFound) + { + HorizonStatic.Syscall.SleepThread(50000000L); + result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, "sm:"); + } + + if (result.IsFailure) + { + return result; + } + + _portHandle = portHandle; + + return RegisterClient(); + } + + private Result RegisterClient() + { + Span data = stackalloc byte[8]; + + SpanWriter writer = new SpanWriter(data); + + writer.Write(0UL); + + return ServiceUtil.SendRequest(out _, _portHandle, 0, sendPid: true, data); + } + + public Result GetServiceHandle(out int handle, ServiceName name) + { + Span data = stackalloc byte[8]; + + SpanWriter writer = new SpanWriter(data); + + writer.Write(name); + + Result result = ServiceUtil.SendRequest(out CmifResponse response, _portHandle, 1, sendPid: false, data); + + if (result.IsFailure) + { + handle = 0; + return result; + } + + handle = response.MoveHandles[0]; + return Result.Success; + } + + public Result RegisterService(out int handle, ServiceName name, int maxSessions, bool isLight) + { + Span data = stackalloc byte[16]; + + SpanWriter writer = new SpanWriter(data); + + writer.Write(name); + writer.Write(isLight ? 1 : 0); + writer.Write(maxSessions); + + Result result = ServiceUtil.SendRequest(out CmifResponse response, _portHandle, 2, sendPid: false, data); + + if (result.IsFailure) + { + handle = 0; + return result; + } + + handle = response.MoveHandles[0]; + return Result.Success; + } + + public Result UnregisterService(ServiceName name) + { + Span data = stackalloc byte[8]; + + SpanWriter writer = new SpanWriter(data); + + writer.Write(name); + + return ServiceUtil.SendRequest(out _, _portHandle, 3, sendPid: false, data); + } + + public Result DetachClient() + { + Span data = stackalloc byte[8]; + + SpanWriter writer = new SpanWriter(data); + + writer.Write(0UL); + + return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data); + } + } +} diff --git a/Ryujinx.Horizon/ServiceEntry.cs b/Ryujinx.Horizon/ServiceEntry.cs new file mode 100644 index 000000000..3fea46c19 --- /dev/null +++ b/Ryujinx.Horizon/ServiceEntry.cs @@ -0,0 +1,25 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Horizon +{ + public struct ServiceEntry + { + private readonly Action _entrypoint; + private readonly HorizonOptions _options; + + internal ServiceEntry(Action entrypoint, HorizonOptions options) + { + _entrypoint = entrypoint; + _options = options; + } + + public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext) + { + HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext, (int)threadContext.GetX(1)); + + _entrypoint(); + } + } +} diff --git a/Ryujinx.Horizon/ServiceTable.cs b/Ryujinx.Horizon/ServiceTable.cs new file mode 100644 index 000000000..933b6a59b --- /dev/null +++ b/Ryujinx.Horizon/ServiceTable.cs @@ -0,0 +1,22 @@ +using Ryujinx.Horizon.LogManager; +using System.Collections.Generic; + +namespace Ryujinx.Horizon +{ + public static class ServiceTable + { + public static IEnumerable GetServices(HorizonOptions options) + { + List entries = new List(); + + void RegisterService() where T : IService + { + entries.Add(new ServiceEntry(T.Main, options)); + } + + RegisterService(); + + return entries; + } + } +} diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs new file mode 100644 index 000000000..fed420aa8 --- /dev/null +++ b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs @@ -0,0 +1,20 @@ +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sm.Impl +{ + struct ServiceInfo + { + public ServiceName Name; + public ulong OwnerProcessId; + public int PortHandle; + + public void Free() + { + HorizonStatic.Syscall.CloseHandle(PortHandle); + + Name = ServiceName.Invalid; + OwnerProcessId = 0L; + PortHandle = 0; + } + } +} diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs new file mode 100644 index 000000000..cdf2d17f1 --- /dev/null +++ b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs @@ -0,0 +1,197 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sm.Impl +{ + class ServiceManager + { + private const int MaxServicesCount = 256; + + private readonly ServiceInfo[] _services; + + public ServiceManager() + { + _services = new ServiceInfo[MaxServicesCount]; + } + + public Result GetService(out int handle, ulong processId, ServiceName name) + { + handle = 0; + Result result = ValidateServiceName(name); + + if (result.IsFailure) + { + return result; + } + + // TODO: Validation with GetProcessInfo etc. + + int serviceIndex = GetServiceInfo(name); + + if (serviceIndex < 0) + { + return SfResult.RequestDeferredByUser; + } + + result = GetServiceImpl(out handle, ref _services[serviceIndex]); + + if (result == KernelResult.SessionCountExceeded) + { + return SmResult.OutOfSessions; + } + + return result; + } + + private Result GetServiceImpl(out int handle, ref ServiceInfo serviceInfo) + { + return HorizonStatic.Syscall.ConnectToPort(out handle, serviceInfo.PortHandle); + } + + public Result RegisterService(out int handle, ulong processId, ServiceName name, int maxSessions, bool isLight) + { + handle = 0; + Result result = ValidateServiceName(name); + + if (result.IsFailure) + { + return result; + } + + // TODO: Validation with GetProcessInfo etc. + + if (HasServiceInfo(name)) + { + return SmResult.AlreadyRegistered; + } + + return RegisterServiceImpl(out handle, processId, name, maxSessions, isLight); + } + + public Result RegisterServiceForSelf(out int handle, ServiceName name, int maxSessions) + { + return RegisterServiceImpl(out handle, Os.GetCurrentProcessId(), name, maxSessions, false); + } + + private Result RegisterServiceImpl(out int handle, ulong processId, ServiceName name, int maxSessions, bool isLight) + { + handle = 0; + + Result result = ValidateServiceName(name); + + if (!result.IsSuccess) + { + return result; + } + + if (HasServiceInfo(name)) + { + return SmResult.AlreadyRegistered; + } + + int freeServiceIndex = GetFreeService(); + + if (freeServiceIndex < 0) + { + return SmResult.OutOfServices; + } + + ref ServiceInfo freeService = ref _services[freeServiceIndex]; + + result = HorizonStatic.Syscall.CreatePort(out handle, out int clientPort, maxSessions, isLight, null); + + if (!result.IsSuccess) + { + return result; + } + + freeService.PortHandle = clientPort; + freeService.Name = name; + freeService.OwnerProcessId = processId; + + return Result.Success; + } + + public Result UnregisterService(ulong processId, ServiceName name) + { + Result result = ValidateServiceName(name); + + if (result.IsFailure) + { + return result; + } + + // TODO: Validation with GetProcessInfo etc. + + int serviceIndex = GetServiceInfo(name); + + if (serviceIndex < 0) + { + return SmResult.NotRegistered; + } + + ref var serviceInfo = ref _services[serviceIndex]; + + if (serviceInfo.OwnerProcessId != processId) + { + return SmResult.NotAllowed; + } + + serviceInfo.Free(); + return Result.Success; + } + + private static Result ValidateServiceName(ServiceName name) + { + if (name[0] == 0) + { + return SmResult.InvalidServiceName; + } + + int nameLength = 1; + + for (; nameLength < name.Length; nameLength++) + { + if (name[nameLength] == 0) + { + break; + } + } + + while (nameLength < name.Length) + { + if (name[nameLength++] != 0) + { + return SmResult.InvalidServiceName; + } + } + + return Result.Success; + } + + private bool HasServiceInfo(ServiceName name) + { + return GetServiceInfo(name) != -1; + } + + private int GetFreeService() + { + return GetServiceInfo(ServiceName.Invalid); + } + + private int GetServiceInfo(ServiceName name) + { + for (int index = 0; index < MaxServicesCount; index++) + { + if (_services[index].Name == name) + { + return index; + } + } + + return -1; + } + } +} diff --git a/Ryujinx.Horizon/Sm/ManagerService.cs b/Ryujinx.Horizon/Sm/ManagerService.cs new file mode 100644 index 000000000..1719dcfd5 --- /dev/null +++ b/Ryujinx.Horizon/Sm/ManagerService.cs @@ -0,0 +1,8 @@ +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sm +{ + partial class ManagerService : IServiceObject + { + } +} diff --git a/Ryujinx.Horizon/Sm/SmMain.cs b/Ryujinx.Horizon/Sm/SmMain.cs new file mode 100644 index 000000000..8c37bece5 --- /dev/null +++ b/Ryujinx.Horizon/Sm/SmMain.cs @@ -0,0 +1,30 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm.Impl; + +namespace Ryujinx.Horizon.Sm +{ + public class SmMain + { + private enum PortIndex + { + User, + Manager + } + + private const int MaxPortsCount = 2; + + private readonly ServerManager _serverManager = new ServerManager(null, null, MaxPortsCount, ManagerOptions.Default, 0); + private readonly ServiceManager _serviceManager = new ServiceManager(); + + public void Main() + { + HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", 64).AbortOnFailure(); + + _serverManager.RegisterServer((int)PortIndex.User, smHandle); + _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), 1).AbortOnFailure(); + _serverManager.RegisterServer((int)PortIndex.Manager, smmHandle); + _serverManager.ServiceRequests(); + } + } +} diff --git a/Ryujinx.Horizon/Sm/SmResult.cs b/Ryujinx.Horizon/Sm/SmResult.cs new file mode 100644 index 000000000..3063445dc --- /dev/null +++ b/Ryujinx.Horizon/Sm/SmResult.cs @@ -0,0 +1,19 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sm +{ + static class SmResult + { + private const int ModuleId = 21; + + public static Result OutOfProcess => new Result(ModuleId, 1); + public static Result InvalidClient => new Result(ModuleId, 2); + public static Result OutOfSessions => new Result(ModuleId, 3); + public static Result AlreadyRegistered => new Result(ModuleId, 4); + public static Result OutOfServices => new Result(ModuleId, 5); + public static Result InvalidServiceName => new Result(ModuleId, 6); + public static Result NotRegistered => new Result(ModuleId, 7); + public static Result NotAllowed => new Result(ModuleId, 8); + public static Result TooLargeAccessControl => new Result(ModuleId, 9); + } +} diff --git a/Ryujinx.Horizon/Sm/UserService.cs b/Ryujinx.Horizon/Sm/UserService.cs new file mode 100644 index 000000000..d3b4537bf --- /dev/null +++ b/Ryujinx.Horizon/Sm/UserService.cs @@ -0,0 +1,66 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm.Impl; + +namespace Ryujinx.Horizon.Sm +{ + partial class UserService : IServiceObject + { + private readonly ServiceManager _serviceManager; + + private ulong _clientProcessId; + private bool _initialized; + + public UserService(ServiceManager serviceManager) + { + _serviceManager = serviceManager; + } + + [CmifCommand(0)] + public Result Initialize([ClientProcessId] ulong clientProcessId) + { + _clientProcessId = clientProcessId; + _initialized = true; + + return Result.Success; + } + + [CmifCommand(1)] + public Result GetService([MoveHandle] out int handle, ServiceName name) + { + if (!_initialized) + { + handle = 0; + + return SmResult.InvalidClient; + } + + return _serviceManager.GetService(out handle, _clientProcessId, name); + } + + [CmifCommand(2)] + public Result RegisterService([MoveHandle] out int handle, ServiceName name, int maxSessions, bool isLight) + { + if (!_initialized) + { + handle = 0; + + return SmResult.InvalidClient; + } + + return _serviceManager.RegisterService(out handle, _clientProcessId, name, maxSessions, isLight); + } + + [CmifCommand(3)] + public Result UnregisterService(ServiceName name) + { + if (!_initialized) + { + return SmResult.InvalidClient; + } + + return _serviceManager.UnregisterService(_clientProcessId, name); + } + } +} diff --git a/Ryujinx.sln b/Ryujinx.sln index 1eaf006c3..007252fd1 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -79,7 +79,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "Ryujinx.Horizon\Ryujinx.Horizon.csproj", "{AF34127A-3A92-43E5-8496-14960A50B1F1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -227,10 +231,18 @@ Global {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.Build.0 = Release|Any CPU - {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.Build.0 = Release|Any CPU + {77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77F96ECE-4952-42DB-A528-DED25572A573}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77F96ECE-4952-42DB-A528-DED25572A573}.Release|Any CPU.Build.0 = Release|Any CPU + {AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|Any CPU.Build.0 = Release|Any CPU + {7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From fc4b7cba2c083b3920f2d74e0cb4b08cf7a5a278 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 4 Jan 2023 20:01:44 -0300 Subject: [PATCH 237/737] Make PPTC state non-static (#4157) * Make PPTC state non-static * DiskCacheLoadState can be null --- ARMeilleure/Translation/ArmEmitterContext.cs | 7 +- ARMeilleure/Translation/PTC/IPtcLoadState.cs | 10 ++ ARMeilleure/Translation/PTC/Ptc.cs | 136 +++++++++--------- ARMeilleure/Translation/PTC/PtcProfiler.cs | 74 +++++----- ARMeilleure/Translation/Translator.cs | 46 ++++-- Ryujinx.Ava/AppHost.cs | 7 +- Ryujinx.Ava/Program.cs | 7 - .../UI/ViewModels/MainWindowViewModel.cs | 25 ++-- Ryujinx.Cpu/ICpuContext.cs | 22 +++ Ryujinx.Cpu/IDiskCacheState.cs | 20 +++ Ryujinx.Cpu/Jit/JitCpuContext.cs | 12 ++ Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs | 38 +++++ Ryujinx.Cpu/LoadState.cs | 12 ++ Ryujinx.HLE/HOS/ApplicationLoader.cs | 55 ++++--- Ryujinx.HLE/HOS/ArmProcessContext.cs | 23 ++- Ryujinx.HLE/HOS/ArmProcessContextFactory.cs | 33 ++++- Ryujinx.HLE/HOS/ProgramLoader.cs | 90 ++++++++---- Ryujinx.Headless.SDL2/Program.cs | 14 +- Ryujinx/Program.cs | 7 - Ryujinx/Ui/MainWindow.cs | 21 ++- Ryujinx/Ui/RendererWidgetBase.cs | 5 +- 21 files changed, 434 insertions(+), 230 deletions(-) create mode 100644 ARMeilleure/Translation/PTC/IPtcLoadState.cs create mode 100644 Ryujinx.Cpu/IDiskCacheState.cs create mode 100644 Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs create mode 100644 Ryujinx.Cpu/LoadState.cs diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/ARMeilleure/Translation/ArmEmitterContext.cs index 33355daec..48254de4e 100644 --- a/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/ARMeilleure/Translation/ArmEmitterContext.cs @@ -6,7 +6,6 @@ using ARMeilleure.Instructions; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Memory; using ARMeilleure.State; -using ARMeilleure.Translation.PTC; using System; using System.Collections.Generic; using System.Reflection; @@ -44,14 +43,13 @@ namespace ARMeilleure.Translation public IMemoryManager Memory { get; } - public bool HasPtc { get; } - public EntryTable CountTable { get; } public AddressTable FunctionTable { get; } public TranslatorStubs Stubs { get; } public ulong EntryAddress { get; } public bool HighCq { get; } + public bool HasPtc { get; } public Aarch32Mode Mode { get; } private int _ifThenBlockStateIndex = 0; @@ -66,15 +64,16 @@ namespace ARMeilleure.Translation TranslatorStubs stubs, ulong entryAddress, bool highCq, + bool hasPtc, Aarch32Mode mode) { - HasPtc = Ptc.State != PtcState.Disabled; Memory = memory; CountTable = countTable; FunctionTable = funcTable; Stubs = stubs; EntryAddress = entryAddress; HighCq = highCq; + HasPtc = hasPtc; Mode = mode; _labels = new Dictionary(); diff --git a/ARMeilleure/Translation/PTC/IPtcLoadState.cs b/ARMeilleure/Translation/PTC/IPtcLoadState.cs new file mode 100644 index 000000000..1b11ac0b5 --- /dev/null +++ b/ARMeilleure/Translation/PTC/IPtcLoadState.cs @@ -0,0 +1,10 @@ +using System; + +namespace ARMeilleure.Translation.PTC +{ + public interface IPtcLoadState + { + event Action PtcStateChanged; + void Continue(); + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index c2d358c1e..f99d6e516 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -22,7 +22,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter; namespace ARMeilleure.Translation.PTC { - public static class Ptc + class Ptc : IPtcLoadState { private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; @@ -35,45 +35,49 @@ namespace ARMeilleure.Translation.PTC private const string TitleIdTextDefault = "0000000000000000"; private const string DisplayVersionDefault = "0"; - internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1); - internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2); - internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3); + public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1); + public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2); + public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3); private const byte FillingByte = 0x00; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; + public PtcProfiler Profiler { get; } + // Carriers. - private static MemoryStream _infosStream; - private static List _codesList; - private static MemoryStream _relocsStream; - private static MemoryStream _unwindInfosStream; + private MemoryStream _infosStream; + private List _codesList; + private MemoryStream _relocsStream; + private MemoryStream _unwindInfosStream; - private static readonly ulong _outerHeaderMagic; - private static readonly ulong _innerHeaderMagic; + private readonly ulong _outerHeaderMagic; + private readonly ulong _innerHeaderMagic; - private static readonly ManualResetEvent _waitEvent; + private readonly ManualResetEvent _waitEvent; - private static readonly object _lock; + private readonly object _lock; - private static bool _disposed; + private bool _disposed; - internal static string TitleIdText { get; private set; } - internal static string DisplayVersion { get; private set; } + public string TitleIdText { get; private set; } + public string DisplayVersion { get; private set; } - private static MemoryManagerMode _memoryMode; + private MemoryManagerType _memoryMode; - internal static string CachePathActual { get; private set; } - internal static string CachePathBackup { get; private set; } + public string CachePathActual { get; private set; } + public string CachePathBackup { get; private set; } - internal static PtcState State { get; private set; } + public PtcState State { get; private set; } // Progress reporting helpers. - private static volatile int _translateCount; - private static volatile int _translateTotalCount; - public static event Action PtcStateChanged; + private volatile int _translateCount; + private volatile int _translateTotalCount; + public event Action PtcStateChanged; - static Ptc() + public Ptc() { + Profiler = new PtcProfiler(this); + InitializeCarriers(); _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); @@ -94,12 +98,12 @@ namespace ARMeilleure.Translation.PTC Disable(); } - public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode) + public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode) { Wait(); - PtcProfiler.Wait(); - PtcProfiler.ClearEntries(); + Profiler.Wait(); + Profiler.ClearEntries(); Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled})."); @@ -137,12 +141,12 @@ namespace ARMeilleure.Translation.PTC CachePathBackup = Path.Combine(workPathBackup, DisplayVersion); PreLoad(); - PtcProfiler.PreLoad(); + Profiler.PreLoad(); Enable(); } - private static void InitializeCarriers() + private void InitializeCarriers() { _infosStream = new MemoryStream(); _codesList = new List(); @@ -150,7 +154,7 @@ namespace ARMeilleure.Translation.PTC _unwindInfosStream = new MemoryStream(); } - private static void DisposeCarriers() + private void DisposeCarriers() { _infosStream.Dispose(); _codesList.Clear(); @@ -158,12 +162,12 @@ namespace ARMeilleure.Translation.PTC _unwindInfosStream.Dispose(); } - private static bool AreCarriersEmpty() + private bool AreCarriersEmpty() { return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L; } - private static void ResetCarriersIfNeeded() + private void ResetCarriersIfNeeded() { if (AreCarriersEmpty()) { @@ -175,7 +179,7 @@ namespace ARMeilleure.Translation.PTC InitializeCarriers(); } - private static void PreLoad() + private void PreLoad() { string fileNameActual = string.Concat(CachePathActual, ".cache"); string fileNameBackup = string.Concat(CachePathBackup, ".cache"); @@ -199,7 +203,7 @@ namespace ARMeilleure.Translation.PTC } } - private static unsafe bool Load(string fileName, bool isBackup) + private unsafe bool Load(string fileName, bool isBackup) { using (FileStream compressedStream = new(fileName, FileMode.Open)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) @@ -376,12 +380,12 @@ namespace ARMeilleure.Translation.PTC return true; } - private static void InvalidateCompressedStream(FileStream compressedStream) + private void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); } - private static void PreSave() + private void PreSave() { _waitEvent.Reset(); @@ -409,7 +413,7 @@ namespace ARMeilleure.Translation.PTC _waitEvent.Set(); } - private static unsafe void Save(string fileName) + private unsafe void Save(string fileName) { int translatedFuncsCount; @@ -517,7 +521,7 @@ namespace ARMeilleure.Translation.PTC } } - internal static void LoadTranslations(Translator translator) + public void LoadTranslations(Translator translator) { if (AreCarriersEmpty()) { @@ -550,7 +554,7 @@ namespace ARMeilleure.Translation.PTC bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize); - if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) + if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) { infoEntry.Stubbed = true; infoEntry.CodeLength = 0; @@ -601,38 +605,38 @@ namespace ARMeilleure.Translation.PTC Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded"); } - private static int GetEntriesCount() + private int GetEntriesCount() { return _codesList.Count; } [Conditional("DEBUG")] - private static void SkipCode(int index, int codeLength) + private void SkipCode(int index, int codeLength) { Debug.Assert(_codesList[index].Length == 0); Debug.Assert(codeLength == 0); } - private static void SkipReloc(int relocEntriesCount) + private void SkipReloc(int relocEntriesCount) { _relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current); } - private static void SkipUnwindInfo(BinaryReader unwindInfosReader) + private void SkipUnwindInfo(BinaryReader unwindInfosReader) { int pushEntriesLength = unwindInfosReader.ReadInt32(); _unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current); } - private static byte[] ReadCode(int index, int codeLength) + private byte[] ReadCode(int index, int codeLength) { Debug.Assert(_codesList[index].Length == codeLength); return _codesList[index]; } - private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) + private RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) { RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount]; @@ -648,7 +652,7 @@ namespace ARMeilleure.Translation.PTC return relocEntries; } - private static void PatchCode(Translator translator, Span code, RelocEntry[] relocEntries, out Counter callCounter) + private void PatchCode(Translator translator, Span code, RelocEntry[] relocEntries, out Counter callCounter) { callCounter = null; @@ -702,7 +706,7 @@ namespace ARMeilleure.Translation.PTC } } - private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) + private UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) { int pushEntriesLength = unwindInfosReader.ReadInt32(); @@ -723,7 +727,7 @@ namespace ARMeilleure.Translation.PTC return new UnwindInfo(pushEntries, prologueSize); } - private static TranslatedFunction FastTranslate( + private TranslatedFunction FastTranslate( byte[] code, Counter callCounter, ulong guestSize, @@ -736,19 +740,19 @@ namespace ARMeilleure.Translation.PTC return new TranslatedFunction(gFunc, callCounter, guestSize, highCq); } - private static void UpdateInfo(InfoEntry infoEntry) + private void UpdateInfo(InfoEntry infoEntry) { _infosStream.Seek(-Unsafe.SizeOf(), SeekOrigin.Current); SerializeStructure(_infosStream, infoEntry); } - private static void StubCode(int index) + private void StubCode(int index) { _codesList[index] = Array.Empty(); } - private static void StubReloc(int relocEntriesCount) + private void StubReloc(int relocEntriesCount) { for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++) { @@ -756,7 +760,7 @@ namespace ARMeilleure.Translation.PTC } } - private static void StubUnwindInfo(BinaryReader unwindInfosReader) + private void StubUnwindInfo(BinaryReader unwindInfosReader) { int pushEntriesLength = unwindInfosReader.ReadInt32(); @@ -766,9 +770,9 @@ namespace ARMeilleure.Translation.PTC } } - internal static void MakeAndSaveTranslations(Translator translator) + public void MakeAndSaveTranslations(Translator translator) { - var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions); + var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions); _translateCount = 0; _translateTotalCount = profiledFuncsToTranslate.Count; @@ -811,7 +815,7 @@ namespace ARMeilleure.Translation.PTC { ulong address = item.address; - Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); + Debug.Assert(Profiler.IsAddressInStaticCodeRange(address)); TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq); @@ -861,7 +865,7 @@ namespace ARMeilleure.Translation.PTC preSaveThread.Start(); } - private static void ReportProgress(object state) + private void ReportProgress(object state) { const int refreshRate = 50; // ms. @@ -882,12 +886,12 @@ namespace ARMeilleure.Translation.PTC while (!endEvent.WaitOne(refreshRate)); } - internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) + public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) { return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize)))); } - internal static void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc) + public void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc) { lock (_lock) { @@ -936,12 +940,12 @@ namespace ARMeilleure.Translation.PTC } } - private static void WriteCode(ReadOnlySpan code) + private void WriteCode(ReadOnlySpan code) { _codesList.Add(code.ToArray()); } - internal static bool GetEndianness() + public static bool GetEndianness() { return BitConverter.IsLittleEndian; } @@ -955,7 +959,7 @@ namespace ARMeilleure.Translation.PTC (uint)HardwareCapabilities.FeatureInfo7Ecx); } - private static byte GetMemoryManagerMode() + private byte GetMemoryManagerMode() { return (byte)_memoryMode; } @@ -1050,12 +1054,12 @@ namespace ARMeilleure.Translation.PTC public int RelocEntriesCount; } - private static void Enable() + private void Enable() { State = PtcState.Enabled; } - public static void Continue() + public void Continue() { if (State == PtcState.Enabled) { @@ -1063,7 +1067,7 @@ namespace ARMeilleure.Translation.PTC } } - public static void Close() + public void Close() { if (State == PtcState.Enabled || State == PtcState.Continuing) @@ -1072,17 +1076,17 @@ namespace ARMeilleure.Translation.PTC } } - internal static void Disable() + public void Disable() { State = PtcState.Disabled; } - private static void Wait() + private void Wait() { _waitEvent.WaitOne(); } - public static void Dispose() + public void Dispose() { if (!_disposed) { diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index bb70da8d0..0d5546283 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -16,7 +16,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter; namespace ARMeilleure.Translation.PTC { - public static class PtcProfiler + class PtcProfiler { private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; @@ -26,27 +26,31 @@ namespace ARMeilleure.Translation.PTC private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; - private static readonly System.Timers.Timer _timer; + private readonly Ptc _ptc; - private static readonly ulong _outerHeaderMagic; + private readonly System.Timers.Timer _timer; - private static readonly ManualResetEvent _waitEvent; + private readonly ulong _outerHeaderMagic; - private static readonly object _lock; + private readonly ManualResetEvent _waitEvent; - private static bool _disposed; + private readonly object _lock; - private static Hash128 _lastHash; + private bool _disposed; - internal static Dictionary ProfiledFuncs { get; private set; } + private Hash128 _lastHash; - internal static bool Enabled { get; private set; } + public Dictionary ProfiledFuncs { get; private set; } - public static ulong StaticCodeStart { internal get; set; } - public static ulong StaticCodeSize { internal get; set; } + public bool Enabled { get; private set; } - static PtcProfiler() + public ulong StaticCodeStart { get; set; } + public ulong StaticCodeSize { get; set; } + + public PtcProfiler(Ptc ptc) { + _ptc = ptc; + _timer = new System.Timers.Timer((double)SaveInterval * 1000d); _timer.Elapsed += PreSave; @@ -63,7 +67,7 @@ namespace ARMeilleure.Translation.PTC Enabled = false; } - internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq) + public void AddEntry(ulong address, ExecutionMode mode, bool highCq) { if (IsAddressInStaticCodeRange(address)) { @@ -76,7 +80,7 @@ namespace ARMeilleure.Translation.PTC } } - internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq) + public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq) { if (IsAddressInStaticCodeRange(address)) { @@ -91,12 +95,12 @@ namespace ARMeilleure.Translation.PTC } } - internal static bool IsAddressInStaticCodeRange(ulong address) + public bool IsAddressInStaticCodeRange(ulong address) { return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize; } - internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache funcs) + public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache funcs) { var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>(); @@ -111,18 +115,18 @@ namespace ARMeilleure.Translation.PTC return profiledFuncsToTranslate; } - internal static void ClearEntries() + public void ClearEntries() { ProfiledFuncs.Clear(); ProfiledFuncs.TrimExcess(); } - internal static void PreLoad() + public void PreLoad() { _lastHash = default; - string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); - string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); + string fileNameActual = string.Concat(_ptc.CachePathActual, ".info"); + string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info"); FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoBackup = new FileInfo(fileNameBackup); @@ -143,7 +147,7 @@ namespace ARMeilleure.Translation.PTC } } - private static bool Load(string fileName, bool isBackup) + private bool Load(string fileName, bool isBackup) { using (FileStream compressedStream = new(fileName, FileMode.Open)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) @@ -228,22 +232,22 @@ namespace ARMeilleure.Translation.PTC return DeserializeDictionary(stream, (stream) => DeserializeStructure(stream)); } - private static ReadOnlySpan GetReadOnlySpan(MemoryStream memoryStream) + private ReadOnlySpan GetReadOnlySpan(MemoryStream memoryStream) { return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position); } - private static void InvalidateCompressedStream(FileStream compressedStream) + private void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); } - private static void PreSave(object source, System.Timers.ElapsedEventArgs e) + private void PreSave(object source, System.Timers.ElapsedEventArgs e) { _waitEvent.Reset(); - string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); - string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); + string fileNameActual = string.Concat(_ptc.CachePathActual, ".info"); + string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info"); FileInfo fileInfoActual = new FileInfo(fileNameActual); @@ -257,7 +261,7 @@ namespace ARMeilleure.Translation.PTC _waitEvent.Set(); } - private static void Save(string fileName) + private void Save(string fileName) { int profiledFuncsCount; @@ -329,7 +333,7 @@ namespace ARMeilleure.Translation.PTC } } - private static void Serialize(Stream stream, Dictionary profiledFuncs) + private void Serialize(Stream stream, Dictionary profiledFuncs) { SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); } @@ -361,7 +365,7 @@ namespace ARMeilleure.Translation.PTC } [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)] - internal struct FuncProfile + public struct FuncProfile { public ExecutionMode Mode; public bool HighCq; @@ -373,10 +377,10 @@ namespace ARMeilleure.Translation.PTC } } - internal static void Start() + public void Start() { - if (Ptc.State == PtcState.Enabled || - Ptc.State == PtcState.Continuing) + if (_ptc.State == PtcState.Enabled || + _ptc.State == PtcState.Continuing) { Enabled = true; @@ -384,7 +388,7 @@ namespace ARMeilleure.Translation.PTC } } - public static void Stop() + public void Stop() { Enabled = false; @@ -394,12 +398,12 @@ namespace ARMeilleure.Translation.PTC } } - internal static void Wait() + public void Wait() { _waitEvent.WaitOne(); } - public static void Dispose() + public void Dispose() { if (!_disposed) { diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 2edbe4011..77ccdaeab 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -44,6 +44,8 @@ namespace ARMeilleure.Translation private readonly IJitMemoryAllocator _allocator; private readonly ConcurrentQueue> _oldFuncs; + private readonly Ptc _ptc; + internal TranslatorCache Functions { get; } internal AddressTable FunctionTable { get; } internal EntryTable CountTable { get; } @@ -63,6 +65,8 @@ namespace ARMeilleure.Translation _oldFuncs = new ConcurrentQueue>(); + _ptc = new Ptc(); + Queue = new TranslatorQueue(); JitCache.Initialize(allocator); @@ -80,22 +84,37 @@ namespace ARMeilleure.Translation } } + public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + { + _ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type); + return _ptc; + } + + public void PrepareCodeRange(ulong address, ulong size) + { + if (_ptc.Profiler.StaticCodeSize == 0) + { + _ptc.Profiler.StaticCodeStart = address; + _ptc.Profiler.StaticCodeSize = size; + } + } + public void Execute(State.ExecutionContext context, ulong address) { if (Interlocked.Increment(ref _threadCount) == 1) { IsReadyForTranslation.WaitOne(); - if (Ptc.State == PtcState.Enabled) + if (_ptc.State == PtcState.Enabled) { Debug.Assert(Functions.Count == 0); - Ptc.LoadTranslations(this); - Ptc.MakeAndSaveTranslations(this); + _ptc.LoadTranslations(this); + _ptc.MakeAndSaveTranslations(this); } - PtcProfiler.Start(); + _ptc.Profiler.Start(); - Ptc.Disable(); + _ptc.Disable(); // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht // etc). All threads are normal priority except from the last, which just fills as much of the last core @@ -148,6 +167,12 @@ namespace ARMeilleure.Translation Stubs.Dispose(); FunctionTable.Dispose(); CountTable.Dispose(); + + _ptc.Close(); + _ptc.Profiler.Stop(); + + _ptc.Dispose(); + _ptc.Profiler.Dispose(); } } @@ -189,9 +214,9 @@ namespace ARMeilleure.Translation func = oldFunc; } - if (PtcProfiler.Enabled) + if (_ptc.Profiler.Enabled) { - PtcProfiler.AddEntry(address, mode, highCq: false); + _ptc.Profiler.AddEntry(address, mode, highCq: false); } RegisterFunction(address, func); @@ -217,6 +242,7 @@ namespace ARMeilleure.Translation Stubs, address, highCq, + _ptc.State != PtcState.Disabled, mode: Aarch32Mode.User); Logger.StartPass(PassName.Decoding); @@ -262,7 +288,7 @@ namespace ARMeilleure.Translation { Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize); - Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); + _ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); } GuestFunction func = compiledFunc.Map(); @@ -284,9 +310,9 @@ namespace ARMeilleure.Translation return func; }); - if (PtcProfiler.Enabled) + if (_ptc.Profiler.Enabled) { - PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true); + _ptc.Profiler.UpdateEntry(request.Address, request.Mode, highCq: true); } RegisterFunction(request.Address, func); diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 0baa94c3b..2cf53ef69 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using Avalonia.Input; using Avalonia.Threading; using LibHac.Tools.FsSystem; @@ -280,7 +279,7 @@ namespace Ryujinx.Ava _parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); - _parent.ViewModel.HandleShaderProgress(Device); + _parent.ViewModel.SetUiProgressHandlers(Device); Renderer.SizeChanged += Window_SizeChanged; @@ -357,8 +356,6 @@ namespace Ryujinx.Ava DisplaySleep.Restore(); - Ptc.Close(); - PtcProfiler.Stop(); NpadManager.Dispose(); TouchScreenManager.Dispose(); Device.Dispose(); @@ -949,7 +946,7 @@ namespace Ryujinx.Ava if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen) { - Ptc.Continue(); + Device.Application.DiskCacheLoadState?.Cancel(); } }); } diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 142d7820b..836801c88 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using Avalonia; using Avalonia.Threading; using Ryujinx.Ava.UI.Windows; @@ -197,9 +196,6 @@ namespace Ryujinx.Ava private static void ProcessUnhandledException(Exception ex, bool isTerminating) { - Ptc.Close(); - PtcProfiler.Stop(); - string message = $"Unhandled exception caught: {ex}"; Logger.Error?.PrintMsg(LogClass.Application, message); @@ -219,9 +215,6 @@ namespace Ryujinx.Ava { DiscordIntegrationModule.Exit(); - Ptc.Dispose(); - PtcProfiler.Dispose(); - Logger.Shutdown(); } } diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 878af3f81..953f8562c 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using Avalonia; using Avalonia.Controls; using Avalonia.Input; @@ -18,6 +17,7 @@ using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Cpu; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; @@ -107,9 +107,6 @@ namespace Ryujinx.Ava.UI.ViewModels { ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; - - Ptc.PtcStateChanged -= ProgressHandler; - Ptc.PtcStateChanged += ProgressHandler; } public string SearchText @@ -436,7 +433,7 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); } } - + public bool ShowMenuAndStatusBar { get => _showMenuAndStatusBar; @@ -745,8 +742,14 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void HandleShaderProgress(Switch emulationContext) + public void SetUiProgressHandlers(Switch emulationContext) { + if (emulationContext.Application.DiskCacheLoadState != null) + { + emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; + emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + } + emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; } @@ -1033,16 +1036,16 @@ namespace Ryujinx.Ava.UI.ViewModels switch (state) { - case PtcLoadingState ptcState: + case LoadState ptcState: CacheLoadStatus = $"{current} / {total}"; switch (ptcState) { - case PtcLoadingState.Start: - case PtcLoadingState.Loading: + case LoadState.Unloaded: + case LoadState.Loading: LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingPPTC]; IsLoadingIndeterminate = false; break; - case PtcLoadingState.Loaded: + case LoadState.Loaded: LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); IsLoadingIndeterminate = true; CacheLoadStatus = ""; @@ -1166,7 +1169,7 @@ namespace Ryujinx.Ava.UI.ViewModels DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1")); // FIXME: Found a way to reproduce the bold effect on the title name (fork?). - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], + UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionMessage], selection.TitleName), LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], diff --git a/Ryujinx.Cpu/ICpuContext.cs b/Ryujinx.Cpu/ICpuContext.cs index 4a73a8338..80916d1ca 100644 --- a/Ryujinx.Cpu/ICpuContext.cs +++ b/Ryujinx.Cpu/ICpuContext.cs @@ -35,5 +35,27 @@ namespace Ryujinx.Cpu /// Address of the region to be invalidated /// Size of the region to be invalidated void InvalidateCacheRegion(ulong address, ulong size); + + /// + /// Loads cached code from disk for a given application. + /// + /// + /// If the execution engine is recompiling guest code, this can be used to load cached code from disk. + /// + /// Title ID of the application in padded hex form + /// Version of the application + /// True if the cache should be loaded from disk if it exists, false otherwise + /// Disk cache load progress reporter and manager + IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled); + + /// + /// Indicates that code has been loaded into guest memory, and that it might be executed in the future. + /// + /// + /// Some execution engines might use this information to cache recompiled code on disk or to ensure it can be executed. + /// + /// CPU virtual address where the code starts + /// Size of the code range in bytes + void PrepareCodeRange(ulong address, ulong size); } } diff --git a/Ryujinx.Cpu/IDiskCacheState.cs b/Ryujinx.Cpu/IDiskCacheState.cs new file mode 100644 index 000000000..61bbdf924 --- /dev/null +++ b/Ryujinx.Cpu/IDiskCacheState.cs @@ -0,0 +1,20 @@ +using System; + +namespace Ryujinx.Cpu +{ + /// + /// Disk cache load state report and management interface. + /// + public interface IDiskCacheLoadState + { + /// + /// Event used to report the cache load progress. + /// + event Action StateChanged; + + /// + /// Cancels the disk cache load process. + /// + void Cancel(); + } +} diff --git a/Ryujinx.Cpu/Jit/JitCpuContext.cs b/Ryujinx.Cpu/Jit/JitCpuContext.cs index d6892ea75..02465a0b3 100644 --- a/Ryujinx.Cpu/Jit/JitCpuContext.cs +++ b/Ryujinx.Cpu/Jit/JitCpuContext.cs @@ -37,5 +37,17 @@ namespace Ryujinx.Cpu.Jit { _translator.InvalidateJitCacheRegion(address, size); } + + /// + public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + { + return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled)); + } + + /// + public void PrepareCodeRange(ulong address, ulong size) + { + _translator.PrepareCodeRange(address, size); + } } } diff --git a/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs b/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs new file mode 100644 index 000000000..7a4b670b3 --- /dev/null +++ b/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs @@ -0,0 +1,38 @@ +using ARMeilleure.Translation.PTC; +using System; + +namespace Ryujinx.Cpu.Jit +{ + public class JitDiskCacheLoadState : IDiskCacheLoadState + { + /// + public event Action StateChanged; + + private readonly IPtcLoadState _loadState; + + public JitDiskCacheLoadState(IPtcLoadState loadState) + { + loadState.PtcStateChanged += LoadStateChanged; + _loadState = loadState; + } + + private void LoadStateChanged(PtcLoadingState newState, int current, int total) + { + LoadState state = newState switch + { + PtcLoadingState.Start => LoadState.Unloaded, + PtcLoadingState.Loading => LoadState.Loading, + PtcLoadingState.Loaded => LoadState.Loaded, + _ => throw new ArgumentException($"Invalid load state \"{newState}\".") + }; + + StateChanged?.Invoke(state, current, total); + } + + /// + public void Cancel() + { + _loadState.Continue(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/LoadState.cs b/Ryujinx.Cpu/LoadState.cs new file mode 100644 index 000000000..1f2e1ae8c --- /dev/null +++ b/Ryujinx.Cpu/LoadState.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Cpu +{ + /// + /// Load state. + /// + public enum LoadState + { + Unloaded, + Loading, + Loaded + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 61fcd0c35..06281b497 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using LibHac; using LibHac.Account; using LibHac.Common; @@ -14,8 +13,8 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Cpu; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.Memory; using System; @@ -67,6 +66,8 @@ namespace Ryujinx.HLE.HOS public string TitleIdText => TitleId.ToString("x16"); + public IDiskCacheLoadState DiskCacheLoadState { get; private set; } + public ApplicationLoader(Switch device) { _device = device; @@ -94,7 +95,7 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId)); } - LoadExeFs(codeFs, metaData); + LoadExeFs(codeFs, string.Empty, metaData); } public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) @@ -302,12 +303,6 @@ namespace Ryujinx.HLE.HOS public void LoadServiceNca(string ncaFile) { - // Disable PPTC here as it does not support multiple processes running. - // TODO: This should be eventually removed and it should stop using global state and - // instead manage the cache per process. - Ptc.Close(); - PtcProfiler.Stop(); - FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); @@ -369,16 +364,12 @@ namespace Ryujinx.HLE.HOS // Collect the nsos, ignoring ones that aren't used. NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); - MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode; - - if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) - { - memoryManagerMode = MemoryManagerMode.SoftwarePageTable; - } + string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString; + bool usePtc = _device.System.EnablePtc; metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false); - ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs); + ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false); + ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16"); bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0; @@ -477,9 +468,11 @@ namespace Ryujinx.HLE.HOS _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + string displayVersion = string.Empty; + if (controlNca != null) { - ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref _displayVersion); + ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion); } else { @@ -493,9 +486,11 @@ namespace Ryujinx.HLE.HOS string dummyTitleName = ""; BlitStruct dummyControl = new BlitStruct(1); - ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref _displayVersion); + ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion); } + _displayVersion = displayVersion; + if (dataStorage == null) { Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); @@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId & ~0xFul)); } - LoadExeFs(codeFs, metaData); + LoadExeFs(codeFs, displayVersion, metaData); Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); } @@ -584,7 +579,7 @@ namespace Ryujinx.HLE.HOS } } - private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null, bool isHomebrew = false) + private void LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) { if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) { @@ -649,23 +644,23 @@ namespace Ryujinx.HLE.HOS memoryManagerMode = MemoryManagerMode.SoftwarePageTable; } - Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode); - // We allow it for nx-hbloader because it can be used to launch homebrew. bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew; metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit); - ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: programs); + ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit); + ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); + DiskCacheLoadState = result.DiskCacheLoadState; + + _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); } public void LoadProgram(string filePath) { MetaLoader metaData = GetDefaultNpdm(); metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: true); + ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true); bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; @@ -761,9 +756,11 @@ namespace Ryujinx.HLE.HOS Graphics.Gpu.GraphicsConfig.TitleId = null; _device.Gpu.HostInitalized.Set(); - ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: executable); + ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable); - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); + DiskCacheLoadState = result.DiskCacheLoadState; + + _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); } private MetaLoader GetDefaultNpdm() diff --git a/Ryujinx.HLE/HOS/ArmProcessContext.cs b/Ryujinx.HLE/HOS/ArmProcessContext.cs index 072df0b61..6338edc1e 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContext.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContext.cs @@ -6,7 +6,17 @@ using Ryujinx.Memory; namespace Ryujinx.HLE.HOS { - class ArmProcessContext : IProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager + interface IArmProcessContext : IProcessContext + { + IDiskCacheLoadState Initialize( + string titleIdText, + string displayVersion, + bool diskCacheEnabled, + ulong codeAddress, + ulong codeSize); + } + + class ArmProcessContext : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager { private readonly ulong _pid; private readonly GpuContext _gpuContext; @@ -40,6 +50,17 @@ namespace Ryujinx.HLE.HOS _cpuContext.Execute(context, codeAddress); } + public IDiskCacheLoadState Initialize( + string titleIdText, + string displayVersion, + bool diskCacheEnabled, + ulong codeAddress, + ulong codeSize) + { + _cpuContext.PrepareCodeRange(codeAddress, codeSize); + return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled); + } + public void InvalidateCacheRegion(ulong address, ulong size) { _cpuContext.InvalidateCacheRegion(address, size); diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 7d1c4e1d6..5ecaf38e9 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -13,11 +13,30 @@ namespace Ryujinx.HLE.HOS { private readonly ICpuEngine _cpuEngine; private readonly GpuContext _gpu; + private readonly string _titleIdText; + private readonly string _displayVersion; + private readonly bool _diskCacheEnabled; + private readonly ulong _codeAddress; + private readonly ulong _codeSize; - public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu) + public IDiskCacheLoadState DiskCacheLoadState { get; private set; } + + public ArmProcessContextFactory( + ICpuEngine cpuEngine, + GpuContext gpu, + string titleIdText, + string displayVersion, + bool diskCacheEnabled, + ulong codeAddress, + ulong codeSize) { _cpuEngine = cpuEngine; _gpu = gpu; + _titleIdText = titleIdText; + _displayVersion = displayVersion; + _diskCacheEnabled = diskCacheEnabled; + _codeAddress = codeAddress; + _codeSize = codeSize; } public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit) @@ -29,21 +48,29 @@ namespace Ryujinx.HLE.HOS mode = MemoryManagerMode.SoftwarePageTable; } + IArmProcessContext processContext; + switch (mode) { case MemoryManagerMode.SoftwarePageTable: var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); - return new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManager, for64Bit); + processContext = new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManager, for64Bit); + break; case MemoryManagerMode.HostMapped: case MemoryManagerMode.HostMappedUnsafe: bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler); - return new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); + processContext = new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); + break; default: throw new ArgumentOutOfRangeException(); } + + DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize); + + return processContext; } } } diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index b422fef75..09e1ac31c 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -1,9 +1,9 @@ -using ARMeilleure.Translation.PTC; using LibHac.Loader; using LibHac.Ncm; using LibHac.Util; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; @@ -21,16 +21,40 @@ namespace Ryujinx.HLE.HOS { public string Name; public ulong ProgramId; - public bool AllowCodeMemoryForJit; + public readonly string TitleIdText; + public readonly string DisplayVersion; + public readonly bool DiskCacheEnabled; + public readonly bool AllowCodeMemoryForJit; - public ProgramInfo(in Npdm npdm, bool allowCodeMemoryForJit) + public ProgramInfo(in Npdm npdm, string displayVersion, bool diskCacheEnabled, bool allowCodeMemoryForJit) { + ulong programId = npdm.Aci.Value.ProgramId.Value; + Name = StringUtils.Utf8ZToString(npdm.Meta.Value.ProgramName); - ProgramId = npdm.Aci.Value.ProgramId.Value; + ProgramId = programId; + TitleIdText = programId.ToString("x16"); + DisplayVersion = displayVersion; + DiskCacheEnabled = diskCacheEnabled; AllowCodeMemoryForJit = allowCodeMemoryForJit; } } + struct ProgramLoadResult + { + public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null); + + public readonly bool Success; + public readonly ProcessTamperInfo TamperInfo; + public readonly IDiskCacheLoadState DiskCacheLoadState; + + public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState) + { + Success = success; + TamperInfo = tamperInfo; + DiskCacheLoadState = diskCacheLoadState; + } + } + static class ProgramLoader { private const bool AslrEnabled = true; @@ -102,7 +126,14 @@ namespace Ryujinx.HLE.HOS KProcess process = new KProcess(context); - var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu); + var processContextFactory = new ArmProcessContextFactory( + context.Device.System.CpuEngine, + context.Device.Gpu, + string.Empty, + string.Empty, + false, + codeAddress, + codeSize); result = process.InitializeKip( creationInfo, @@ -144,9 +175,8 @@ namespace Ryujinx.HLE.HOS return true; } - public static bool LoadNsos( + public static ProgramLoadResult LoadNsos( KernelContext context, - out ProcessTamperInfo tamperInfo, MetaLoader metaData, ProgramInfo programInfo, byte[] arguments = null, @@ -156,8 +186,7 @@ namespace Ryujinx.HLE.HOS if (rc.IsFailure()) { - tamperInfo = null; - return false; + return ProgramLoadResult.Failed; } ref readonly var meta = ref npdm.Meta.Value; @@ -212,9 +241,6 @@ namespace Ryujinx.HLE.HOS } } - PtcProfiler.StaticCodeStart = codeStart; - PtcProfiler.StaticCodeSize = (ulong)codeSize; - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); int personalMmHeapPagesCount = (int)(meta.SystemResourceSize / KPageTableBase.PageSize); @@ -263,9 +289,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } KProcess process = new KProcess(context, programInfo.AllowCodeMemoryForJit); @@ -276,12 +300,17 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } - var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu); + var processContextFactory = new ArmProcessContextFactory( + context.Device.System.CpuEngine, + context.Device.Gpu, + programInfo.TitleIdText, + programInfo.DisplayVersion, + programInfo.DiskCacheEnabled, + codeStart, + codeSize); result = process.Initialize( creationInfo, @@ -294,9 +323,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } for (int index = 0; index < executables.Length; index++) @@ -309,9 +336,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } } @@ -323,9 +348,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } context.Processes.TryAdd(process.Pid, process); @@ -333,10 +356,15 @@ namespace Ryujinx.HLE.HOS // Keep the build ids because the tamper machine uses them to know which process to associate a // tamper to and also keep the starting address of each executable inside a process because some // memory modifications are relative to this address. - tamperInfo = new ProcessTamperInfo(process, buildIds, nsoBase, process.MemoryManager.HeapRegionStart, - process.MemoryManager.AliasRegionStart, process.MemoryManager.CodeRegionStart); + ProcessTamperInfo tamperInfo = new ProcessTamperInfo( + process, + buildIds, + nsoBase, + process.MemoryManager.HeapRegionStart, + process.MemoryManager.AliasRegionStart, + process.MemoryManager.CodeRegionStart); - return true; + return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState); } private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 4a2ba99de..b0c29e561 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using CommandLine; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; @@ -12,6 +11,7 @@ using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; using Ryujinx.Common.Utilities; +using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; @@ -447,8 +447,11 @@ namespace Ryujinx.Headless.SDL2 private static void SetupProgressHandler() { - Ptc.PtcStateChanged -= ProgressHandler; - Ptc.PtcStateChanged += ProgressHandler; + if (_emulationContext.Application.DiskCacheLoadState != null) + { + _emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; + _emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + } _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; @@ -460,7 +463,7 @@ namespace Ryujinx.Headless.SDL2 switch (state) { - case PtcLoadingState ptcState: + case LoadState ptcState: label = $"PTC : {current}/{total}"; break; case ShaderCacheState shaderCacheState: @@ -563,9 +566,6 @@ namespace Ryujinx.Headless.SDL2 _window.Execute(); - Ptc.Close(); - PtcProfiler.Stop(); - _emulationContext.Dispose(); _window.Dispose(); diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index e27c4ae94..787a8ad5f 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using Gtk; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -308,9 +307,6 @@ namespace Ryujinx private static void ProcessUnhandledException(Exception ex, bool isTerminating) { - Ptc.Close(); - PtcProfiler.Stop(); - string message = $"Unhandled exception caught: {ex}"; Logger.Error?.PrintMsg(LogClass.Application, message); @@ -330,9 +326,6 @@ namespace Ryujinx { DiscordIntegrationModule.Exit(); - Ptc.Dispose(); - PtcProfiler.Dispose(); - Logger.Shutdown(); } } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 0e7e4d625..495f66519 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using Gtk; using LibHac.Common; using LibHac.Common.Keys; @@ -16,6 +15,7 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; +using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.OpenGL; @@ -46,7 +46,6 @@ using System.Threading; using System.Threading.Tasks; using GUI = Gtk.Builder.ObjectAttribute; -using PtcLoadingState = ARMeilleure.Translation.PTC.PtcLoadingState; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; namespace Ryujinx.Ui @@ -588,8 +587,11 @@ namespace Ryujinx.Ui private void SetupProgressUiHandlers() { - Ptc.PtcStateChanged -= ProgressHandler; - Ptc.PtcStateChanged += ProgressHandler; + if (_emulationContext.Application.DiskCacheLoadState != null) + { + _emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; + _emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + } _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; @@ -602,8 +604,8 @@ namespace Ryujinx.Ui switch (state) { - case PtcLoadingState ptcState: - visible = ptcState != PtcLoadingState.Loaded; + case LoadState ptcState: + visible = ptcState != LoadState.Loaded; label = $"PTC : {current}/{total}"; break; case ShaderCacheLoadingState shaderCacheState: @@ -705,8 +707,6 @@ namespace Ryujinx.Ui UpdateGraphicsConfig(); - SetupProgressUiHandlers(); - SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); bool isDirectory = Directory.Exists(path); @@ -841,6 +841,8 @@ namespace Ryujinx.Ui return; } + SetupProgressUiHandlers(); + _currentEmulatedGamePath = path; _deviceExitStatus.Reset(); @@ -967,9 +969,6 @@ namespace Ryujinx.Ui RendererWidget.Start(); - Ptc.Close(); - PtcProfiler.Stop(); - _emulationContext.Dispose(); _deviceExitStatus.Set(); diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 9dabe8173..8db023bec 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using Gdk; using Gtk; using Ryujinx.Common; @@ -519,7 +518,7 @@ namespace Ryujinx.Ui _gpuCancellationTokenSource.Cancel(); _isStopped = true; - + if (_isActive) { _isActive = false; @@ -585,7 +584,7 @@ namespace Ryujinx.Ui { if (!ParentWindow.State.HasFlag(WindowState.Fullscreen)) { - Ptc.Continue(); + Device.Application.DiskCacheLoadState?.Cancel(); } } }); From 8f2b7b5b8ed4c9671f75ec758a554d765905e080 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 5 Jan 2023 00:20:47 +0100 Subject: [PATCH 238/737] Readd Ryujinx.Ui.LocaleGenerator removed in #4188 --- Ryujinx.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Ryujinx.sln b/Ryujinx.sln index 007252fd1..d99c5b636 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -79,6 +79,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "Ryujinx.Horizon\Ryujinx.Horizon.csproj", "{AF34127A-3A92-43E5-8496-14960A50B1F1}" From d6b86a6629aefdfea9b7e7d1dea92bba6b4e1481 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 5 Jan 2023 00:23:17 +0100 Subject: [PATCH 239/737] Readd Ryujinx.Ui.LocaleGenerator removed in #4188 (again) --- Ryujinx.sln | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Ryujinx.sln b/Ryujinx.sln index d99c5b636..12657bf9e 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -233,6 +233,10 @@ Global {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.Build.0 = Release|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.Build.0 = Release|Any CPU {77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.Build.0 = Debug|Any CPU {77F96ECE-4952-42DB-A528-DED25572A573}.Release|Any CPU.ActiveCfg = Release|Any CPU From 86392455338e5e1fc65b25a3136d866228e28e40 Mon Sep 17 00:00:00 2001 From: Mary Date: Thu, 5 Jan 2023 01:55:27 +0100 Subject: [PATCH 240/737] hle: Add safety measure around overflow in ScheduleFutureInvocation Fix crash on Linux since 08831eecf77cedd3c4192ebab5a9c485fb15d51e. --- Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs index 4eb736f2c..020048f4e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs @@ -44,7 +44,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout) { - long timePoint = PerformanceCounter.ElapsedTicks + ConvertNanosecondsToHostTicks(timeout); + long startTime = PerformanceCounter.ElapsedTicks; + long timePoint = startTime + ConvertNanosecondsToHostTicks(timeout); + + if (timePoint < startTime) + { + timePoint = long.MaxValue; + } lock (_context.CriticalSection.Lock) { From e876c43ce9b96fbb349b881ac7905b8a4748a50a Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 5 Jan 2023 04:30:55 +0100 Subject: [PATCH 241/737] Misc; Remove duplicated entries and clean locale csproj (#4209) --- Directory.Packages.props | 4 +--- .../Ryujinx.Ui.LocaleGenerator.csproj | 11 ++++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 84d83d0d9..7fa742366 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -50,7 +50,5 @@ - - - + \ No newline at end of file diff --git a/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj b/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj index 0960455d5..b7fe19544 100644 --- a/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj +++ b/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj @@ -1,12 +1,9 @@  - netstandard2.0 - enable - true - Generated - true - latest + netstandard2.0 + enable + latest @@ -14,7 +11,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 7f27aabbd0501b32e4918384c0a50fd0b7f357fe Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Fri, 6 Jan 2023 16:52:25 +0000 Subject: [PATCH 242/737] chore: Update Ryujinx.SDL2-CS to 2.26.1 (#4199) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7fa742366..61b63f3f5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,7 +34,7 @@ - + From 38519f3b9ade223983f7529f9b4f4b857c60f42b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Fri, 6 Jan 2023 18:35:21 -0500 Subject: [PATCH 243/737] Ava GUI: `SettingsWindow` Refactor (#4177) * Fix redundancies * Add back elses * Settings Refactor * Fix Disposal functions * Use `ReflectionBinding` instead of redundant funcs * Ac_K suggestions * Update Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs Co-authored-by: Ac_K * Update locale keys * Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Update Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Update Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Use block-scoped namespaces * Fix typo * Make `TimeZone` internal again Co-authored-by: Ac_K Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- .../UI/ViewModels/SettingsViewModel.cs | 104 +-- .../UI/Views/Settings/SettingsAudioView.axaml | 81 ++ .../Views/Settings/SettingsAudioView.axaml.cs | 12 + .../UI/Views/Settings/SettingsCPUView.axaml | 72 ++ .../Views/Settings/SettingsCPUView.axaml.cs | 12 + .../Views/Settings/SettingsGraphicsView.axaml | 218 +++++ .../Settings/SettingsGraphicsView.axaml.cs | 12 + .../Views/Settings/SettingsHotkeysView.axaml | 104 +++ .../Settings/SettingsHotkeysView.axaml.cs | 81 ++ .../UI/Views/Settings/SettingsInputView.axaml | 46 + .../Views/Settings/SettingsInputView.axaml.cs | 17 + .../Views/Settings/SettingsLoggingView.axaml | 118 +++ .../Settings/SettingsLoggingView.axaml.cs | 12 + .../Views/Settings/SettingsNetworkView.axaml | 35 + .../Settings/SettingsNetworkView.axaml.cs | 12 + .../Views/Settings/SettingsSystemView.axaml | 195 ++++ .../Settings/SettingsSystemView.axaml.cs | 52 ++ .../UI/Views/Settings/SettingsUIView.axaml | 156 ++++ .../UI/Views/Settings/SettingsUIView.axaml.cs | 82 ++ Ryujinx.Ava/UI/Windows/SettingsWindow.axaml | 884 +----------------- .../UI/Windows/SettingsWindow.axaml.cs | 214 ++--- 21 files changed, 1422 insertions(+), 1097 deletions(-) create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 5570800ae..d11b01f1b 100644 --- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -1,4 +1,3 @@ -using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Threading; @@ -8,8 +7,6 @@ using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Configuration; @@ -19,7 +16,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Time.TimeZone; -using Ryujinx.Input; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; using System; @@ -30,11 +26,10 @@ using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; namespace Ryujinx.Ava.UI.ViewModels { - internal class SettingsViewModel : BaseModel + public class SettingsViewModel : BaseModel { private readonly VirtualFileSystem _virtualFileSystem; private readonly ContentManager _contentManager; - private readonly StyleableWindow _owner; private TimeZoneContentManager _timeZoneContentManager; private readonly List _validTzRegions; @@ -44,10 +39,14 @@ namespace Ryujinx.Ava.UI.ViewModels private int _graphicsBackendMultithreadingIndex; private float _volume; private bool _isVulkanAvailable = true; - private bool _directoryChanged = false; - private List _gpuIds = new List(); + private bool _directoryChanged; + private List _gpuIds = new(); private KeyboardHotkeys _keyboardHotkeys; private int _graphicsBackendIndex; + private string _customThemePath; + + public event Action CloseWindow; + public event Action SaveSettingsEvent; public int ResolutionScale { @@ -67,19 +66,16 @@ namespace Ryujinx.Ava.UI.ViewModels { _graphicsBackendMultithreadingIndex = value; - if (_owner != null) + if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value) { - if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value) + Dispatcher.UIThread.Post(async () => { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage], - "", - "", - LocaleManager.Instance[LocaleKeys.InputDialogOk], - LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]); - }); - } + await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage], + "", + "", + LocaleManager.Instance[LocaleKeys.InputDialogOk], + LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]); + }); } OnPropertyChanged(); @@ -120,12 +116,12 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); } } - + public bool IsMacOS { get => OperatingSystem.IsMacOS(); } - + public bool EnableDiscordIntegration { get; set; } public bool CheckUpdatesOnStart { get; set; } public bool ShowConfirmExit { get; set; } @@ -160,7 +156,20 @@ namespace Ryujinx.Ava.UI.ViewModels public string TimeZone { get; set; } public string ShaderDumpPath { get; set; } - public string CustomThemePath { get; set; } + + public string CustomThemePath + { + get + { + return _customThemePath; + } + set + { + _customThemePath = value; + + OnPropertyChanged(); + } + } public int Language { get; set; } public int Region { get; set; } @@ -191,7 +200,7 @@ namespace Ryujinx.Ava.UI.ViewModels { _volume = value; - ConfigurationState.Instance.System.AudioVolume.Value = (float)(_volume / 100); + ConfigurationState.Instance.System.AudioVolume.Value = _volume / 100; OnPropertyChanged(); } @@ -199,7 +208,7 @@ namespace Ryujinx.Ava.UI.ViewModels public DateTimeOffset DateOffset { get; set; } public TimeSpan TimeOffset { get; set; } - public AvaloniaList TimeZones { get; set; } + private AvaloniaList TimeZones { get; set; } public AvaloniaList GameDirectories { get; set; } public ObservableCollection AvailableGpus { get; set; } @@ -214,17 +223,13 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public IGamepadDriver AvaloniaKeyboardDriver { get; } - - public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager, StyleableWindow owner) : this() + public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() { _virtualFileSystem = virtualFileSystem; _contentManager = contentManager; - _owner = owner; if (Program.PreviewerDetached) { LoadTimeZones(); - AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner); } } @@ -251,10 +256,10 @@ namespace Ryujinx.Ava.UI.ViewModels IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported; } - private unsafe void LoadAvailableGpus() + private void LoadAvailableGpus() { _gpuIds = new List(); - List names = new List(); + List names = new(); var devices = VulkanRenderer.GetPhysicalDevices(); if (devices.Length == 0) @@ -272,7 +277,7 @@ namespace Ryujinx.Ava.UI.ViewModels } AvailableGpus.Clear(); - AvailableGpus.AddRange(names.Select(x => new ComboBoxItem() { Content = x })); + AvailableGpus.AddRange(names.Select(x => new ComboBoxItem { Content = x })); } public void LoadTimeZones() @@ -302,25 +307,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public async void BrowseTheme() - { - var dialog = new OpenFileDialog() - { - Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle], - AllowMultiple = false - }; - - dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] }); - - var file = await dialog.ShowAsync(_owner); - - if (file != null && file.Length > 0) - { - CustomThemePath = file[0]; - OnPropertyChanged(nameof(CustomThemePath)); - } - } - public void LoadCurrentConfiguration() { ConfigurationState config = ConfigurationState.Instance; @@ -477,16 +463,8 @@ namespace Ryujinx.Ava.UI.ViewModels config.ToFileFormat().SaveConfig(Program.ConfigurationPath); MainWindow.UpdateGraphicsConfig(); - - if (_owner is SettingsWindow owner) - { - owner.ControllerSettings?.SaveCurrentProfile(); - } - - if (_owner.Owner is MainWindow window && _directoryChanged) - { - window.ViewModel.LoadApplications(); - } + + SaveSettingsEvent?.Invoke(); _directoryChanged = false; } @@ -504,13 +482,13 @@ namespace Ryujinx.Ava.UI.ViewModels public void OkButton() { SaveSettings(); - _owner.Close(); + CloseWindow?.Invoke(); } public void CancelButton() { RevertIfNotSaved(); - _owner.Close(); + CloseWindow?.Invoke(); } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml new file mode 100644 index 000000000..352833534 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs new file mode 100644 index 000000000..026c7fdf8 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsAudioView : UserControl + { + public SettingsAudioView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml new file mode 100644 index 000000000..a0cf34529 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs new file mode 100644 index 000000000..5c5b60793 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsCPUView : UserControl + { + public SettingsCPUView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml new file mode 100644 index 000000000..1f65155a2 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs new file mode 100644 index 000000000..8fe08552e --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsGraphicsView : UserControl + { + public SettingsGraphicsView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml new file mode 100644 index 000000000..361125bfe --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs new file mode 100644 index 000000000..702f73e87 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs @@ -0,0 +1,81 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using Ryujinx.Ava.Input; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Input; +using Ryujinx.Input.Assigner; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsHotkeysView : UserControl + { + private ButtonKeyAssigner _currentAssigner; + private IGamepadDriver AvaloniaKeyboardDriver; + + public SettingsHotkeysView() + { + InitializeComponent(); + AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this); + } + + private void MouseClick(object sender, PointerPressedEventArgs e) + { + bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; + + _currentAssigner?.Cancel(shouldUnbind); + + PointerPressed -= MouseClick; + } + + private void Button_Checked(object sender, RoutedEventArgs e) + { + if (sender is ToggleButton button) + { + if (_currentAssigner != null && button == _currentAssigner.ToggledButton) + { + return; + } + + if (_currentAssigner == null && button.IsChecked != null && (bool)button.IsChecked) + { + _currentAssigner = new ButtonKeyAssigner(button); + + FocusManager.Instance?.Focus(this, NavigationMethod.Pointer); + + PointerPressed += MouseClick; + + var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]); + IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard); + + _currentAssigner.GetInputAndAssign(assigner); + } + else + { + if (_currentAssigner != null) + { + ToggleButton oldButton = _currentAssigner.ToggledButton; + + _currentAssigner.Cancel(); + _currentAssigner = null; + + button.IsChecked = false; + } + } + } + } + + private void Button_Unchecked(object sender, RoutedEventArgs e) + { + _currentAssigner?.Cancel(); + _currentAssigner = null; + } + + public void Dispose() + { + _currentAssigner?.Cancel(); + _currentAssigner = null; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml new file mode 100644 index 000000000..1c774bdad --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs new file mode 100644 index 000000000..0c2105ec4 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs @@ -0,0 +1,17 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsInputView : UserControl + { + public SettingsInputView() + { + InitializeComponent(); + } + + public void Dispose() + { + ControllerSettings.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml new file mode 100644 index 000000000..2163dcdac --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs new file mode 100644 index 000000000..2ec476ac6 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsLoggingView : UserControl + { + public SettingsLoggingView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml new file mode 100644 index 000000000..8efd367dd --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs new file mode 100644 index 000000000..d7407b9d5 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsNetworkView : UserControl + { + public SettingsNetworkView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml new file mode 100644 index 000000000..ddcca39cb --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs b/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs new file mode 100644 index 000000000..d2ea59deb --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs @@ -0,0 +1,52 @@ +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Ryujinx.Ava.UI.ViewModels; +using System; +using System.Linq; +using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsSystemView : UserControl + { + public SettingsViewModel ViewModel; + + public SettingsSystemView() + { + InitializeComponent(); + + FuncMultiValueConverter converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim()); + MultiBinding tzMultiBinding = new() { Converter = converter }; + + tzMultiBinding.Bindings.Add(new Binding("UtcDifference")); + tzMultiBinding.Bindings.Add(new Binding("Location")); + tzMultiBinding.Bindings.Add(new Binding("Abbreviation")); + + TimeZoneBox.ValueMemberBinding = tzMultiBinding; + } + + private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (e.AddedItems != null && e.AddedItems.Count > 0) + { + if (e.AddedItems[0] is TimeZone timeZone) + { + e.Handled = true; + + ViewModel.ValidateAndSetTimeZone(timeZone.Location); + } + } + } + + private void TimeZoneBox_OnTextChanged(object sender, EventArgs e) + { + if (sender is AutoCompleteBox box && box.SelectedItem is TimeZone timeZone) + { + { + ViewModel.ValidateAndSetTimeZone(timeZone.Location); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml new file mode 100644 index 000000000..61b6c4335 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + Grid.Row="0" + Spacing="10" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch"> + + + + + + + + + + + + + + + + + + - - - - - - + - - + BorderBrush="{DynamicResource ThemeControlBorderColor}" + BorderThickness="1,0,0,0" + Margin="20 0"/> - - - - - - - - - - - + FontWeight="Bold" + FontSize="15" + Text="{locale:Locale AboutRyujinxAboutTitle}" /> - + FontSize="10" + TextWrapping="Wrap" + Text="{locale:Locale AboutRyujinxAboutContent}" /> + + + + + + + + + + + + - + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs index 99280b870..5dbbbcdd7 100644 --- a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs @@ -1,38 +1,48 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Threading; +using Avalonia.Styling; +using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Common.Utilities; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ui.Common.Helper; -using System.Net.Http; -using System.Net.NetworkInformation; using System.Threading.Tasks; +using Button = Avalonia.Controls.Button; namespace Ryujinx.Ava.UI.Windows { - public partial class AboutWindow : StyleableWindow + public partial class AboutWindow : UserControl { public AboutWindow() { - if (Program.PreviewerDetached) - { - Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.MenuBarHelpAbout]; - } - - Version = Program.Version; - - DataContext = this; + DataContext = new AboutWindowViewModel(); InitializeComponent(); - - _ = DownloadPatronsJson(); } - public string Supporters { get; set; } - public string Version { get; set; } + public static async Task Show() + { + var content = new AboutWindow(); - public string Developers => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD«"); + ContentDialog contentDialog = new() + { + PrimaryButtonText = "", + SecondaryButtonText = "", + CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], + Content = content + }; + + 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 contentDialog.ShowAsync(); + } private void Button_OnClick(object sender, RoutedEventArgs e) { @@ -42,31 +52,6 @@ namespace Ryujinx.Ava.UI.Windows } } - private async Task DownloadPatronsJson() - { - if (!NetworkInterface.GetIsNetworkAvailable()) - { - Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError]; - - return; - } - - HttpClient httpClient = new(); - - try - { - string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - - Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString)); - } - catch - { - Supporters = LocaleManager.Instance[LocaleKeys.ApiError]; - } - - await Dispatcher.UIThread.InvokeAsync(() => SupportersTextBlock.Text = Supporters); - } - private void AmiiboLabel_OnPointerPressed(object sender, PointerPressedEventArgs e) { if (sender is TextBlock) diff --git a/Ryujinx.Ui.Common/Resources/Logo_Discord.png b/Ryujinx.Ui.Common/Resources/Logo_Discord.png deleted file mode 100644 index 9eabebf8d8ae578f3ad7e69d8de6071f55e6a504..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8022 zcmZ{pRZtwjx~&Hb5?n$E4uc1G4KlzC7Tnz>A=uy~Sc2Q&FgOGl+zIZQ;1b;3-Qi~M zy0>nfhg0?ay{gyh_0|tv)nTeC@;F%UumAu64g?HR|EClG84T2aYhtYS;6FjNky4fd z0IFhMKboQa+tXNp)s+DNZ$<#XKNtYG|7ZE{0sv4h0ASw~01!z507#wSO=@EQ0!$~c zo+|+Gn&>}+1W1Fw{bwSZE6RfaFCj6%+W`P7LkLJp({uhP1JjpScBZ4mhY|DFDlZT< z6x@YF>k_o6rYoiL0UzW{fREoPd*dpFD+|JfLUr�?fw#Gyz(Yj$5x#7`bDeOfZol zjf2ASH?tZ>Mn==FjFyb?sfc?hA^7Ld!sjRBR)qHz6%%r`Q?_9Nobw`xPP-@Ka<#L{ z2|IqNZM%qyba*zlZP8Qck|>0hTU8%)&XD^pq9_F9A)R}6+S5x=q>J=6s;HQibzD4F z^H-H-W|OAovxHn2F%YSfVC*AupBAI)y$g~$0{3g8nzL?Z3NaG4{43BbPtZhjWN5#? z+bOK%4Ka%>oLV9FJRZPVkfuhm%AkY$jI)Eq*s2&xj8?ch-mQq&eo$#R4(quqLulZK zVT7Z_U>Y;tE}egP0}_KVok`IahrZVeAfnN_Yo=06VAngJ<%R&K;DPnz9me+q;0|ulgkhBN0#EpjNZbsC*f}YGWv`iQ?fPk-ES~RFo3W)Nm}#GFLf?V zzVvD-bvZgRHU3a0UBq2=C?IMoy^>cq&>QoH%VCZt@TEl&23 z&95`U^SM#LGGhOq+$wL7J}JVG!Q5{w$zU|f;V(Cf9r!cTc-qIA-|WxV#2`l`oo9pK zNTu9^eHHGvm#Dd__yWwwAu(@r!#k(GOyn41s&i|4p96LDf}xP5@f!NM!z9VN$VYP# z8BOj2xo&7pPSJ5xqtaB21>Dk$_)HL0;$a-F#w zsf_Ra{*b0oZ*pUs#Q~88p-B=qWGSWN04j4|#Q~M~vOryiI8}z2;=x21A=P{|+iTXP zGD~Xa&3TyYC*ZX+cX=RHl!7|F1+~S}UM3--c%ksT!q#Cr?g%z|Xt9;+OvW)QiVxvM zK98cRRiG*w+R-2Vywf2N+uagT5<`L_KgDSunn@OP#oDTU^$50qk+AAEAv848aS7hL;5lkGut53G&G!}?n3uG7jVZsF5su;)Uva5DP! z+swrEfcI0{^*%A>MwECCjzM=0GqV(zrm|2GuD#N?PiyI!t;Nj^_0O<`XGIBI-;pSU zV7X%92nktCuc{l*)#Qb0hwT)^4YsA;R=7;RVjN-@nZf@q_!ZQ%I4P`zqvP3t@*kA6Aa{9%rvzUoHu zoE4hvZhPNH`Ag_IU5MG2%Q58yUaj0XK+uNHmoMy2t?Nd|r87%V#<|jmqEHO_iE0=Bbqp%#0r0 z+U=0{hk?1g@rBB^@;i^wwPdH3p2(>=vXYUy!=ym;xywE|eB!x2}UxS}w6K-b-@az${fcz!$!cRj@WvBut z_|@*}h9xQ`l%6t*ScN2 zXaZrn-1$D7AAY#eKa`CoW&NlE=TK0-n{h7Xpa5JvEt*W;T&^guj++zu6KXBgQ;f$B z9P-V>`A=2P;`t>g7HyquHHLk%j4y`f{d$>gV;CN_PN%eM`WlC~FI>PO4KXJ1UPRwb z1lT`aHZ-tjxSiO}#GD-6-%%+5X$VaMrVC_2iZJb@S|T(lgIH%&5Wa2Z4o+DxK@BtdEK7ctFR5l7SO z^&M}}%gW;GiLxQ2kuBRxmu%N_CKQ-B{XN20(2n(q^c6hR-?4Z9CH`We6muVT6_chc zP-gJvb@!aUd4a;!!=JD9@7>rhjx0{|lFPp=z5H=C0hY>~`A!cfJ^s~kuI~pL$OYH& z*-g4ZRyK43_c`^q*SjDwNaPPvJ z*nX2&lNA(mtJ+7l{0^4ZIMY;8`z&LCxisaWqnRhATyk+gTG0CyY427Anc(g)jo*ot zD#le)-_lx4LCQ2=F~zB?bJ`r$$V(K(YP~DZfTE zX1Oc$ch3bIq1P{e^i$p8gz#_`NdQ*t*gB=+8yZuT zZl&$51!zYOf@GL0bw) zQm95;+9~kws@_KDMy&R+3?_OjB>~MFM_Dl*TD^O>W;~f8iAN)W$09`4YOBAds{Tc1 zUw(pVzKV}11}w4LiKwjzIVR_sO)ZhcTcROtVi#}sa zw0~A2l)n&gJ_-GM2EhG(l!}cUt(FEsbT}9kbLtsJ(Do*VV4C8ErN3Q*Ioqy(H=~Z~~=sZe>@|HnWtmg4I8^g zI$I|7P05!;>uQYTP>YWlMMD^1@n~?NoIXh)X14sFuqZC>?DkRbjeNX&}~YZkBvhp*3feRVY4EI+EPorxSZ z5T)p@GAi#`0*Yv8X+JMC9N{J$0)%u7{mJaoU>BPlx)=^$ItV;80~g|%3A=onlNv>a zr)!k40=FRBW;-qzB=T+?scRaAc92eXLeUDbdrX?U>axpHK~5z-yU$Ix`1$n32^4dI z`9cPRD%2t=e}n*Ad^6pp9u(?A)iyD>?iB=WGm#?3z4>(N`j{OMT&rSLpma<<4WI|8 z&`t`ER@kj8F7(BKRh?d~lv#tXxr*;T2br$hDdKpx9ajoKbA{tbWAe*|y>^qP%Dh#> zKl2j|`_svX=e`bpZVYH-c04PtB8>9hl|+;Hc-_8S?S z7xR+l<$UjzLZtR-tC3dYFG6O;>j)HCI_h(Go!Ww{6#NDPH0*T`+Vf%P>b{ z+Gq^XW~MfxA5Oi@i`+FMti;5sK53^rnsto{wizLOUJOLJNEdl%t!=(2N2B?Po|eBK z`l$?ogzSv>?l>@&yxxf`^Y9$EAE>{TI=Xd>n$J+oaYQqoK|c1xoxF{2HrUE%fSZ=Y z$PYym04RHB4_0g(;g#^WOesnL26auE_-oG&9~bmb=Q# z&KJ-9m@h`g7fI4#C6D$zL#Wc=*E$eG^sAK7*{h6X)zP!iAt;rV6 z0D-3RHT%#np~P}n<1b*1WQ1lwVOp5jdBhosy}RC6GuKec9iIYhXY=mO>nK({mYcLb zOe!HYMk|ktY7b=m`kr5l+RJ{V$12T7%!3oXa0Dl07DLzt2mcTt6X$TWB2;olM_Mck z=aEcvo*yDzOzjGSjt;Wo?Okb-M!xd?jIBcAT5ag?a<2EM(;8|(QD1iw>~KVE!bI`M z&T+HT0hkg$a`}wj`x;4=(@!jEB+SrWufOlGZFoKqLAce>f*UPQ6-$Cv7g~!K;4iK=4XMUG zYJstiE2W&k5I55%L{gkQmxFGq1u`C<(@v%|VUR@rl^7jQ>E<&8EEUg6oa7V`v`5!Sn?l9hRFJ z(7Y;%uru$|gK}t3i=yfVCr!Lz7=d+{LbObw;DD&VhqK@5bY>b?PQ5upP;&#~Jy7i` z07bx@*IcQ;0e9XF3frbY+xtahp8yZTBDY$&pD5R!>_nfw!w<$vf&Ioi+;!`k4>R!g=v@%c$~& zZiM{`+F2^lCoZOU47Pvs3xN&s6Je*86PCj+Jp`7Y-76VI#7K)?89+I$`fWz@ZGX5Q z{Oz4LV)beB-4uRJi1*P{{ga}z>20psmHgQ>5|^b4W%X_OHpf9YmUNLuK5m$1PPG(9Jo~@pu zgsq(b-QN;~PYN6BX`aw~W-EP8&5Oh)Q^ObIAwor-RVvc{6SZCS;Hvf5_UoxI8$djC zcFACB4b<3QReskF+ZVcM=&LBI;B5Qq^|Jr@BqN6_^|NVi1990jgj+;Qy&znI_o{LqxE2k7_YC%vF@d?bZu<)_!6+hsY&#hmjq z4w)+2t4m+{=P6$6LpzmWx?{htC&R^5*yRQ9BtIMOOo}ZiT>RCFjj{AiaNikpz0Sky zs_=!i><`M?iM8_*}le>clrzM*0Isxb|EWo~SIe^Nu)C5XAj@KLyg>)4`v8>J{U3+FZwoCU(Fsy0yZGl@>#NUG)?{J?+=uwljvm5R+&O zn-fPET55tcRg37`&lTGXC@HGRB>DsUY%&aufFFBgv}WJCT-ko4Ik)|rX8qAH>i7fG z5u?5`mubynWFCsMwgUm-7`sP3P;l|;0s8^sY&mB;mUDQMkTq$sTx9_ z6yLy%SY~<}ALphd(fFw5D78y0rs?=bQ3WwEdnJ?f`<$3ACgcViBZKRrtHtJB4jx+` zmYn<(%5);Hy#V#?9}^LGr&-D?vP)+hx35O>rNQXyw|JfSADMk?5e7=d&X~z7l=pBZ zb1bdECJhC{erl0p6Hx}?r#EpIvUOLU6J0gnaR>Hik-r%?7yFHqv(K*T-ki|tvU%5i zT~wxur~N9f2jlG@a8notgJ~(?u(YbN_$5vRksgzdrGy%O%KXcDM*hzNRcCoO8=tz zfkbhWexXoYj2z;eZB4FSkd@{{62)v0Npc&?9X zW7IT#u7#L0HTNI9sDpEcc@+Y!I??Qr%HR>ocMWBW#t2JrwNfurA5*kv9nFjSOw^;e z-fTqD&cH@TOpvJC<&=@zal9M!k;+T!Y^!m1Eb}a8DyE#83bQ49H()lQvM5O$37qit zoChyAm}j?8!2|?I*o-9@!zj8ptztg~V_L+v z%sDx+H=wd5+6t`>get*WrkzwbVne;qO4D0#2s4T_0NeSmpO+!M1#^Ne%CG@XaW3ty zC-P?;79rc~Ww`H`U{H@pYv$2O^%P6)0fNnI*;+_-{0$!Eg$KCx-R5FN!=Y)DpcG-z zra&KF%&llX> zSx=PeFVwK*^@+s&D+70+QyZB^Gmz5U0;XPjcW zo?Q`UGu5*m6YbK=S72lUzNNt8&5QWSr60@%-!}(W@A7{c^6}Z$UV=*()P>J$J_O z?Z%{FHjhZ%vsbl-IIt`KcmMM5(c<@ziL^DBd+EJ;l}!n5`reN=%20!xMQ-NQJciio z!ImE3yTpWS<9to3yGLS6!;@OK<-bQ;M9#{RH!6)YI;JvlYd?!FsQK$+1`pKZ%f+n%RQKGXkX)cmy*QB=$vR zX{wzO5Di&kN8^0;u}%?pHl}E*6FacIv?}fr4<09PVhjepp1EIPk#MRk-Dsr0dWlOV zH6XEE1z`bH8g0dYyRP@|upL2R>vpJ^r;t`c4713=99?DHlTv^Mi~~gu>tMGGos-?G zWSwho*sYtDS+9CG_;1?}3_$jv8;3p5ry&T9a#~B^#79W#qR$1RmS!%h%}hp=j;cF# zrQNu*#E($Tuk-JfDa>wIf3atm&C=PQh(=fc(S$>7PWpuRt^M#~u9#*rr_}jhhs8RL zj}ZEd=v}GH0+q);tVT_~toz<#|Jd!whkbLP%g6aTN;v;sfs|Sd8n}E!-O~^e7}g(m zH;vsmHfGz(EbKFIfj8su9wgZ-mV&IG5$jPs*t;8;GidVCFDnX@auH>m(5ON+t3DJa ziCh->niLe}&IqA`U)ocy5!=D`!<9nFkLZFn=fWxwmkDK3MVBxh(T_DX{LHo!fxKX< z&V0}JN#+Gi0o9*yGL`EvA=x|)|LpGkd%eU}%Ed*-*D)tG-idlOV?8upsPU3;2sggM z$0n+$*`$h6n$)nxdTe!ulufy=W{hYRl$r9EVDImcc+5c`w#WYbv7ZxJ zQ}T>I$O_0vWf>uf_ON8SLWSbv5ufMp&7wI?@eG5`Rl)5X9hP}T{lZJH!Bef7ps2);NjxoX6NE#=jPSq<`?1S s5#i-yy45d;T2E^$>IW+fU@Fx++5%bEFq( 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 B;Q{~v literal 0 HcmV?d00001 diff --git a/Ryujinx.Ui.Common/Resources/Logo_GitHub.png b/Ryujinx.Ui.Common/Resources/Logo_GitHub.png deleted file mode 100644 index 55f4d4e6393f695f7eda0a03328fcdb477f55ade..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15822 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 aTPyGXA3*9>SNjhD{QEp5`%T&`=zjoODg>ke diff --git a/Ryujinx.Ui.Common/Resources/Logo_GitHub_Dark.png b/Ryujinx.Ui.Common/Resources/Logo_GitHub_Dark.png new file mode 100644 index 0000000000000000000000000000000000000000..50b81752278d084ba9d449fff25f4051df162b0f GIT binary patch literal 4837 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Ui.Common/Resources/Logo_GitHub_Light.png b/Ryujinx.Ui.Common/Resources/Logo_GitHub_Light.png new file mode 100644 index 0000000000000000000000000000000000000000..95bc742bb5a4064fd98b7a67dbc3bd4d17013938 GIT binary patch literal 5166 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 WLB{WW`a*sH1Q_U;YS(ExJ^3FEU3|{~ literal 0 HcmV?d00001 diff --git a/Ryujinx.Ui.Common/Resources/Logo_Patreon.png b/Ryujinx.Ui.Common/Resources/Logo_Patreon.png deleted file mode 100644 index ba2da7975c1b92edb2e26f768cebc378243cefb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8134 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

    )y_roEpUu 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 Bh%x{G literal 0 HcmV?d00001 diff --git a/Ryujinx.Ui.Common/Resources/Logo_Patreon_Light.png b/Ryujinx.Ui.Common/Resources/Logo_Patreon_Light.png new file mode 100644 index 0000000000000000000000000000000000000000..44da0ac45c70dd752afaf84736c841834e3c955c GIT binary patch literal 29395 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 S90Mi?q3zc8RuvX>=Klk?^^WrZ literal 0 HcmV?d00001 diff --git a/Ryujinx.Ui.Common/Resources/Logo_Twitter_Light.png b/Ryujinx.Ui.Common/Resources/Logo_Twitter_Light.png new file mode 100644 index 0000000000000000000000000000000000000000..040ca16995674eb360877c7e9cfb2e5d797e7e8f GIT binary patch literal 19901 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 e4P?8Kwqc& - - - - + + + + + + + + diff --git a/Ryujinx/Ui/Windows/AboutWindow.Designer.cs b/Ryujinx/Ui/Windows/AboutWindow.Designer.cs index 8117cf36b..fa1a06578 100644 --- a/Ryujinx/Ui/Windows/AboutWindow.Designer.cs +++ b/Ryujinx/Ui/Windows/AboutWindow.Designer.cs @@ -206,7 +206,7 @@ namespace Ryujinx.Ui.Windows // // _patreonLogo // - _patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Patreon.png", 30, 30)) + _patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png", 30, 30)) { Margin = 10 }; @@ -236,7 +236,7 @@ namespace Ryujinx.Ui.Windows // // _githubLogo // - _githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_GitHub.png", 30, 30)) + _githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png", 30, 30)) { Margin = 10 }; @@ -266,7 +266,7 @@ namespace Ryujinx.Ui.Windows // // _discordLogo // - _discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Discord.png", 30, 30)) + _discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png", 30, 30)) { Margin = 10 }; @@ -296,7 +296,7 @@ namespace Ryujinx.Ui.Windows // // _twitterLogo // - _twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Twitter.png", 30, 30)) + _twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png", 30, 30)) { Margin = 10 }; From cbaa845f5d51fe61a46d1a57950aa76a0a976d3e Mon Sep 17 00:00:00 2001 From: RMED24 <81475204+RMED24@users.noreply.github.com> Date: Sat, 7 Jan 2023 08:06:13 +0000 Subject: [PATCH 246/737] Include a start.sh file with correct launch options (#4013) * Include reference to start.sh to be bundled * Add start.sh * Fix silly mistake I made on windows-x64 * ... I cannot read properly * Make same changes for avalonia csproj * Remove notice from start.sh Co-authored-by: Mary-nyan * Update Ryujinx/Ryujinx.csproj Co-authored-by: Mary-nyan * Update Ryujinx.Ava/Ryujinx.Ava.csproj Co-authored-by: Mary-nyan * Update distribution/linux/start.sh Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Update distribution/linux/start.sh Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Update Ryujinx.Ava/Ryujinx.Ava.csproj Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Update Ryujinx.csproj * Update Ryujinx.Ava.csproj * Rename start.sh to Ryujinx.sh * Update Ryujinx.csproj * Update Ryujinx.Ava.csproj * Update Ryujinx.Ava.csproj * Update Ryujinx.Ava/Ryujinx.Ava.csproj Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Add `GDK_BACKEND` variable * Update Ryujinx.Ava.csproj * Update Program.cs * Update Program.cs * Update Ryujinx.sh * Update Program.cs * linux: Register mime types on launch * Add DOTNET_EnableAlternateStackCheck=1 to desktop file * linux: Add exclusion for RegisterMimeTypes for flathub builds * Update logo path * Cleanup Ryujinx.sh * Fix typo in ReleaseInformation * gha: Fix permissions for linux release binaries * ava: Rename output assembly to Ryujinx * Update mime database after installing new types Wait until logging is available before registering mime types * Copy mime types to output directory Co-authored-by: Mary-nyan Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- .github/workflows/release.yml | 25 ++++++--- Ryujinx.Ava/Modules/Updater/Updater.cs | 8 +-- Ryujinx.Ava/Program.cs | 54 ++++++++++++++++++- Ryujinx.Ava/Ryujinx.Ava.csproj | 11 ++++ .../UI/ViewModels/MainWindowViewModel.cs | 2 +- .../Logging/Targets/FileLogTarget.cs | 2 +- ...eInformations.cs => ReleaseInformation.cs} | 4 +- Ryujinx.Headless.SDL2/Program.cs | 4 +- Ryujinx.SDL2.Common/SDL2Driver.cs | 2 +- .../Configuration/LoggerModule.cs | 2 +- Ryujinx/Modules/Updater/Updater.cs | 6 +-- Ryujinx/Program.cs | 53 +++++++++++++++++- Ryujinx/Ryujinx.csproj | 10 ++++ Ryujinx/Ui/MainWindow.cs | 2 +- .../{ryujinx.desktop => Ryujinx.desktop} | 8 +-- distribution/linux/Ryujinx.sh | 5 ++ .../{ryujinx-mime.xml => mime/Ryujinx.xml} | 10 ++++ .../{linux/ryujinx-logo.svg => misc/Logo.svg} | 0 18 files changed, 177 insertions(+), 31 deletions(-) rename Ryujinx.Common/{ReleaseInformations.cs => ReleaseInformation.cs} (97%) rename distribution/linux/{ryujinx.desktop => Ryujinx.desktop} (78%) create mode 100644 distribution/linux/Ryujinx.sh rename distribution/linux/{ryujinx-mime.xml => mime/Ryujinx.xml} (62%) rename distribution/{linux/ryujinx-logo.svg => misc/Logo.svg} (100%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7bcd57417..3a4b4e205 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,11 +38,11 @@ jobs: shell: bash - name: Configure for release run: | - sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformations.cs - sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformations.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformations.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformations.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformations.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformation.cs shell: bash - name: Create output dir run: "mkdir release_output" @@ -75,15 +75,24 @@ jobs: - name: Packing Linux builds run: | pushd publish_linux - tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish + tar --exclude "publish/Ryujinx" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish + python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx" + gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz + rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar popd pushd publish_linux_sdl2_headless - tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish + tar --exclude "publish/Ryujinx.Headless.SDL2" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish + python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2" + gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz + rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar popd pushd publish_linux_ava - tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish + tar --exclude "publish/Ryujinx" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish + python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx" + gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz + rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar popd shell: bash diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index 7bf147fe4..bc4760bae 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -95,7 +95,7 @@ namespace Ryujinx.Modules { using (HttpClient jsonClient = ConstructHttpClient()) { - string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformations.ReleaseChannelOwner}/{ReleaseInformations.ReleaseChannelRepo}/releases/latest"; + string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); JObject jsonRoot = JObject.Parse(fetchedJson); @@ -625,7 +625,7 @@ namespace Ryujinx.Modules return false; } - if (Program.Version.Contains("dirty") || !ReleaseInformations.IsValid()) + if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid()) { if (showWarnings) { @@ -640,7 +640,7 @@ namespace Ryujinx.Modules #else if (showWarnings) { - if (ReleaseInformations.IsFlatHubBuild()) + if (ReleaseInformation.IsFlatHubBuild()) { ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]); } @@ -711,4 +711,4 @@ namespace Ryujinx.Modules } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 836801c88..5d88466e8 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -13,8 +13,10 @@ using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; using System; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading.Tasks; namespace Ryujinx.Ava @@ -32,9 +34,51 @@ namespace Ryujinx.Ava private const uint MB_ICONWARNING = 0x30; + [SupportedOSPlatform("linux")] + static void RegisterMimeTypes() + { + if (ReleaseInformation.IsFlatHubBuild()) + { + return; + } + + string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); + + if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml"))) + { + string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml"); + using Process mimeProcess = new(); + + mimeProcess.StartInfo.FileName = "xdg-mime"; + mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}"; + + mimeProcess.Start(); + mimeProcess.WaitForExit(); + + if (mimeProcess.ExitCode != 0) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}"); + return; + } + + using Process updateMimeProcess = new(); + + updateMimeProcess.StartInfo.FileName = "update-mime-database"; + updateMimeProcess.StartInfo.Arguments = mimeDbPath; + + updateMimeProcess.Start(); + updateMimeProcess.WaitForExit(); + + if (updateMimeProcess.ExitCode != 0) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}"); + } + } + } + public static void Main(string[] args) { - Version = ReleaseInformations.GetVersion(); + Version = ReleaseInformation.GetVersion(); if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) { @@ -93,6 +137,12 @@ namespace Ryujinx.Ava // Initialize the logger system. LoggerModule.Initialize(); + // Register mime types on linux. + if (OperatingSystem.IsLinux()) + { + RegisterMimeTypes(); + } + // Initialize Discord integration. DiscordIntegrationModule.Initialize(); @@ -218,4 +268,4 @@ namespace Ryujinx.Ava Logger.Shutdown(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 3e3bdc8c7..e63360d47 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -4,6 +4,7 @@ win10-x64;osx-x64;linux-x64 Exe true + Ryujinx 1.0.0-dirty $(DefineConstants);$(ExtraDefineConstants) Ryujinx.Ava @@ -76,6 +77,16 @@ + + + Always + + + Always + mime\Ryujinx.xml + + + Designer diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 888e227c0..514a8bb35 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -940,7 +940,7 @@ namespace Ryujinx.Ava.UI.ViewModels public static void OpenLogsFolder() { - string logPath = Path.Combine(ReleaseInformations.GetBaseApplicationDirectory(), "Logs"); + string logPath = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "Logs"); new DirectoryInfo(logPath).Create(); diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs index 401fe91ba..24dd6d179 100644 --- a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs +++ b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Common.Logging files[i].Delete(); } - string version = ReleaseInformations.GetVersion(); + string version = ReleaseInformation.GetVersion(); // Get path for the current time path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.log"); diff --git a/Ryujinx.Common/ReleaseInformations.cs b/Ryujinx.Common/ReleaseInformation.cs similarity index 97% rename from Ryujinx.Common/ReleaseInformations.cs rename to Ryujinx.Common/ReleaseInformation.cs index 35890406e..d0e013282 100644 --- a/Ryujinx.Common/ReleaseInformations.cs +++ b/Ryujinx.Common/ReleaseInformation.cs @@ -5,7 +5,7 @@ using System.Reflection; namespace Ryujinx.Common { // DO NOT EDIT, filled by CI - public static class ReleaseInformations + public static class ReleaseInformation { private const string FlatHubChannelOwner = "flathub"; @@ -50,4 +50,4 @@ namespace Ryujinx.Common return AppDomain.CurrentDomain.BaseDirectory; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index b0c29e561..84363e1fb 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -59,7 +59,7 @@ namespace Ryujinx.Headless.SDL2 static void Main(string[] args) { - Version = ReleaseInformations.GetVersion(); + Version = ReleaseInformation.GetVersion(); Console.Title = $"Ryujinx Console {Version} (Headless SDL2)"; @@ -419,7 +419,7 @@ namespace Ryujinx.Headless.SDL2 if ((bool)option.EnableFileLog) { Logger.AddTarget(new AsyncLogTargetWrapper( - new FileLogTarget(ReleaseInformations.GetBaseApplicationDirectory(), "file"), + new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"), 1000, AsyncLogTargetOverflowAction.Block )); diff --git a/Ryujinx.SDL2.Common/SDL2Driver.cs b/Ryujinx.SDL2.Common/SDL2Driver.cs index 7aa2d5840..970e287de 100644 --- a/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -91,7 +91,7 @@ namespace Ryujinx.SDL2.Common SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE); - string gamepadDbPath = Path.Combine(ReleaseInformations.GetBaseApplicationDirectory(), "SDL_GameControllerDB.txt"); + string gamepadDbPath = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "SDL_GameControllerDB.txt"); if (File.Exists(gamepadDbPath)) { diff --git a/Ryujinx.Ui.Common/Configuration/LoggerModule.cs b/Ryujinx.Ui.Common/Configuration/LoggerModule.cs index 174f056d8..f4712213e 100644 --- a/Ryujinx.Ui.Common/Configuration/LoggerModule.cs +++ b/Ryujinx.Ui.Common/Configuration/LoggerModule.cs @@ -80,7 +80,7 @@ namespace Ryujinx.Ui.Common.Configuration if (e.NewValue) { Logger.AddTarget(new AsyncLogTargetWrapper( - new FileLogTarget(ReleaseInformations.GetBaseApplicationDirectory(), "file"), + new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"), 1000, AsyncLogTargetOverflowAction.Block )); diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs index 0a1cb53bc..2a25e78f5 100644 --- a/Ryujinx/Modules/Updater/Updater.cs +++ b/Ryujinx/Modules/Updater/Updater.cs @@ -103,7 +103,7 @@ namespace Ryujinx.Modules { using (HttpClient jsonClient = ConstructHttpClient()) { - string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformations.ReleaseChannelOwner}/{ReleaseInformations.ReleaseChannelRepo}/releases/latest"; + string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; // Fetch latest build information string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); @@ -556,7 +556,7 @@ namespace Ryujinx.Modules return false; } - if (Program.Version.Contains("dirty") || !ReleaseInformations.IsValid()) + if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid()) { if (showWarnings) { @@ -570,7 +570,7 @@ namespace Ryujinx.Modules #else if (showWarnings) { - if (ReleaseInformations.IsFlatHubBuild()) + if (ReleaseInformation.IsFlatHubBuild()) { GtkDialog.CreateWarningDialog("Updater Disabled!", "Please update Ryujinx via FlatHub."); } diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 787a8ad5f..b1fedaade 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading.Tasks; namespace Ryujinx @@ -72,9 +73,51 @@ namespace Ryujinx } } + [SupportedOSPlatform("linux")] + static void RegisterMimeTypes() + { + if (ReleaseInformation.IsFlatHubBuild()) + { + return; + } + + string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); + + if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml"))) + { + string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml"); + using Process mimeProcess = new(); + + mimeProcess.StartInfo.FileName = "xdg-mime"; + mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}"; + + mimeProcess.Start(); + mimeProcess.WaitForExit(); + + if (mimeProcess.ExitCode != 0) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}"); + return; + } + + using Process updateMimeProcess = new(); + + updateMimeProcess.StartInfo.FileName = "update-mime-database"; + updateMimeProcess.StartInfo.Arguments = mimeDbPath; + + updateMimeProcess.Start(); + updateMimeProcess.WaitForExit(); + + if (updateMimeProcess.ExitCode != 0) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}"); + } + } + } + static void Main(string[] args) { - Version = ReleaseInformations.GetVersion(); + Version = ReleaseInformation.GetVersion(); if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) { @@ -101,6 +144,8 @@ namespace Ryujinx if (OperatingSystem.IsLinux()) { XInitThreads(); + Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); + setenv("GDK_BACKEND", "x11", 1); } if (OperatingSystem.IsMacOS()) @@ -144,6 +189,12 @@ namespace Ryujinx // Initialize the logger system. LoggerModule.Initialize(); + // Register mime types on linux. + if (OperatingSystem.IsLinux()) + { + RegisterMimeTypes(); + } + // Initialize Discord integration. DiscordIntegrationModule.Initialize(); diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index d8176ee35..6aae6296f 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -62,6 +62,16 @@ + + + Always + + + Always + mime\Ryujinx.xml + + + false diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 495f66519..688e2f522 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -1294,7 +1294,7 @@ namespace Ryujinx.Ui private void OpenLogsFolder_Pressed(object sender, EventArgs args) { - string logPath = System.IO.Path.Combine(ReleaseInformations.GetBaseApplicationDirectory(), "Logs"); + string logPath = System.IO.Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "Logs"); new DirectoryInfo(logPath).Create(); diff --git a/distribution/linux/ryujinx.desktop b/distribution/linux/Ryujinx.desktop similarity index 78% rename from distribution/linux/ryujinx.desktop rename to distribution/linux/Ryujinx.desktop index dbfc43e41..19cc5d6cc 100644 --- a/distribution/linux/ryujinx.desktop +++ b/distribution/linux/Ryujinx.desktop @@ -1,13 +1,13 @@ [Desktop Entry] Version=1.0 Name=Ryujinx -Comment=A Nintendo Switch Emulator Type=Application +Icon=Ryujinx +Exec=env DOTNET_EnableAlternateStackCheck=1 Ryujinx %f +Comment=A Nintendo Switch Emulator GenericName=Nintendo Switch Emulator -Icon=ryujinx Terminal=false -Exec=Ryujinx %f -Categories=Game;Emulator;GTK; +Categories=Game;Emulator; MimeType=application/x-nx-nca;application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci; Keywords=Switch;Nintendo;Emulator; StartupWMClass=Ryujinx diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh new file mode 100644 index 000000000..57a75b1d1 --- /dev/null +++ b/distribution/linux/Ryujinx.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +SCRIPT_DIR=$(dirname $(realpath $0)) + +env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/Ryujinx" "$@" diff --git a/distribution/linux/ryujinx-mime.xml b/distribution/linux/mime/Ryujinx.xml similarity index 62% rename from distribution/linux/ryujinx-mime.xml rename to distribution/linux/mime/Ryujinx.xml index 6ec35c848..bd9df0edf 100644 --- a/distribution/linux/ryujinx-mime.xml +++ b/distribution/linux/mime/Ryujinx.xml @@ -2,22 +2,32 @@ Nintendo Content Archive + NCA + Nintendo Relocatable Object + NRO + Nintendo Shared Object + NSO + Nintendo Submission Package + NSP + Nintendo Switch Cartridge + XCI + diff --git a/distribution/linux/ryujinx-logo.svg b/distribution/misc/Logo.svg similarity index 100% rename from distribution/linux/ryujinx-logo.svg rename to distribution/misc/Logo.svg From b9f2a96595b2721836d2b73e005640f9c3bfaf23 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Sat, 7 Jan 2023 11:24:21 +0000 Subject: [PATCH 247/737] ava: Fix regression caused by #4013 (#4222) Avalonia seems to not like when the artifact doesns't match the root namespace... Address that by moving the binary to "Ryujinx" like we do on macOS build. --- .github/workflows/build.yml | 6 ++++++ .github/workflows/release.yml | 4 +++- Ryujinx.Ava/Ryujinx.Ava.csproj | 1 - 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ac19f717a..4586902ce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,6 +69,12 @@ jobs: - name: Publish Ryujinx.Ava run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true if: github.event_name == 'pull_request' + - name: Rename Avalonia (Windows) + run: mv ./publish_ava/Ryujinx.Ava.exe ./publish_ava/Ryujinx.exe + if: runner.os == 'Windows' && github.event_name == 'pull_request' + - name: Rename Avalonia (Unix) + run: mv ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx + if: runner.os != 'Windows' && github.event_name == 'pull_request' - name: Upload Ryujinx artifact uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a4b4e205..29f0c5cf9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,11 +46,13 @@ jobs: shell: bash - name: Create output dir run: "mkdir release_output" + - name: Publish Windows run: | dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true + mv ./publish_windows_ava/publish/Ryujinx.Ava.exe ./publish_windows_ava/publish/Ryujinx.exe - name: Packing Windows builds run: | pushd publish_windows @@ -71,7 +73,7 @@ jobs: dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true - + mv ./publish_linux_ava/publish/Ryujinx.Ava ./publish_linux_ava/publish/Ryujinx - name: Packing Linux builds run: | pushd publish_linux diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index e63360d47..5c001c672 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -4,7 +4,6 @@ win10-x64;osx-x64;linux-x64 Exe true - Ryujinx 1.0.0-dirty $(DefineConstants);$(ExtraDefineConstants) Ryujinx.Ava From 3ffceab1fb220c13f5982de599d788f2e3e7cc47 Mon Sep 17 00:00:00 2001 From: merry Date: Sat, 7 Jan 2023 16:01:53 +0000 Subject: [PATCH 248/737] MainWindow: Vertically center SearchBox TextPresenter (#4223) --- Ryujinx.Ava/UI/Windows/MainWindow.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/Ryujinx.Ava/UI/Windows/MainWindow.axaml index 1eb422796..63d939a8e 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml @@ -343,6 +343,7 @@ Margin="5,0,5,0" HorizontalAlignment="Right" VerticalAlignment="Center" + VerticalContentAlignment="Center" DockPanel.Dock="Right" KeyUp="SearchBox_OnKeyUp" Text="{Binding SearchText}" From 550747eac6c0f6da14070c8b6d208bde6f1d1eb9 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sun, 8 Jan 2023 13:13:39 +0100 Subject: [PATCH 249/737] Horizon: Impl Prepo, Fixes bugs, Clean things (#4220) * Horizon: Impl Prepo, Fixes bugs, Clean things * remove ToArray() * resultCode > status * Remove old services * Addresses gdkchan's comments and more cleanup * Addresses Gdkchan's feedback 2 * Reorganize services, make sure service are loaded before guest Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com> * Create interfaces for lm and sm Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com> --- Ryujinx.HLE/HOS/Horizon.cs | 3 + Ryujinx.HLE/HOS/ProgramLoader.cs | 2 + Ryujinx.HLE/HOS/Services/Lm/ILogService.cs | 19 -- .../HOS/Services/Lm/LogService/ILogger.cs | 109 --------- .../Lm/LogService/Types/LmLogField.cs | 18 -- .../Lm/LogService/Types/LmLogLevel.cs | 11 - .../HOS/Services/Prepo/IPrepoService.cs | 182 --------------- Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs | 15 -- Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 2 +- .../Hipc/HipcGenerator.cs | 57 +++-- Ryujinx.Horizon/HeapAllocator.cs | 12 +- Ryujinx.Horizon/HorizonOptions.cs | 6 +- Ryujinx.Horizon/HorizonStatic.cs | 26 +-- Ryujinx.Horizon/IService.cs | 2 +- .../LogManager/{ => Ipc}/LmLogger.cs | 37 ++- .../{LmLog.cs => Ipc/LogService.cs} | 11 +- Ryujinx.Horizon/LogManager/LmIpcServer.cs | 31 +-- Ryujinx.Horizon/LogManager/LmMain.cs | 9 +- Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs | 218 ++++++++++++++++++ Ryujinx.Horizon/Prepo/PrepoIpcServer.cs | 49 ++++ Ryujinx.Horizon/Prepo/PrepoMain.cs | 17 ++ Ryujinx.Horizon/Prepo/PrepoResult.cs | 15 ++ Ryujinx.Horizon/Prepo/PrepoServerManager.cs | 30 +++ Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs | 12 + .../Types/PrepoServicePermissionLevel.cs | 5 +- Ryujinx.Horizon/Ryujinx.Horizon.csproj | 4 + Ryujinx.Horizon/Sdk/Account/Uid.cs | 62 +++++ Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs | 12 + Ryujinx.Horizon/Sdk/Lm/ILogService.cs | 11 + Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs | 20 ++ Ryujinx.Horizon/Sdk/ServiceUtil.cs | 9 +- .../Sdk/Sf/Cmif/CmifDomainInHeader.cs | 10 +- .../Sdk/Sf/Cmif/CmifDomainRequestType.cs | 4 +- Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs | 81 ++++--- Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs | 6 +- Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs | 8 +- .../Sdk/Sf/Cmif/CmifRequestFormat.cs | 26 +-- Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs | 4 +- Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs | 12 +- .../Cmif/DomainServiceObjectDispatchTable.cs | 4 +- .../Sf/Cmif/DomainServiceObjectProcessor.cs | 10 +- Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs | 4 +- .../Sf/Cmif/ServerMessageRuntimeMetadata.cs | 33 +-- .../Sdk/Sf/Cmif/ServiceDispatchContext.cs | 18 +- .../Sdk/Sf/Cmif/ServiceDispatchTable.cs | 4 +- .../Sdk/Sf/Cmif/ServiceDispatchTableBase.cs | 16 +- Ryujinx.Horizon/Sdk/Sf/CommandArg.cs | 36 +-- .../Sdk/Sf/CommandArgAttributes.cs | 8 +- Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs | 22 +- .../Sdk/Sf/CommandSerialization.cs | 3 +- .../Sdk/Sf/Hipc/HipcBufferFlags.cs | 12 +- Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs | 4 +- Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs | 4 +- Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs | 122 +++++----- .../Sdk/Sf/Hipc/HipcMessageData.cs | 8 +- Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs | 18 +- .../Sdk/Sf/Hipc/HipcReceiveListEntry.cs | 2 +- Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs | 21 +- .../Sdk/Sf/Hipc/HipcStaticDescriptor.cs | 8 +- Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs | 16 +- Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs | 20 +- .../Sdk/Sf/Hipc/ServerDomainSessionManager.cs | 2 +- Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs | 4 +- .../Sdk/Sf/Hipc/ServerManagerBase.cs | 33 +-- Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs | 16 +- .../Sdk/Sf/Hipc/ServerSessionManager.cs | 41 ++-- .../Sdk/Sf/HipcCommandProcessor.cs | 38 +-- .../Sdk/Sf/RawDataOffsetCalculator.cs | 18 +- Ryujinx.Horizon/Sdk/Sf/SfResult.cs | 36 ++- Ryujinx.Horizon/Sdk/Sm/IManagerService.cs | 8 + Ryujinx.Horizon/Sdk/Sm/IUserService.cs | 13 ++ Ryujinx.Horizon/Sdk/Sm/SmApi.cs | 22 +- Ryujinx.Horizon/ServiceEntry.cs | 16 +- Ryujinx.Horizon/ServiceTable.cs | 46 +++- Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs | 10 +- Ryujinx.Horizon/Sm/Impl/ServiceManager.cs | 9 +- Ryujinx.Horizon/Sm/Ipc/ManagerService.cs | 8 + Ryujinx.Horizon/Sm/{ => Ipc}/UserService.cs | 10 +- Ryujinx.Horizon/Sm/ManagerService.cs | 8 - Ryujinx.Horizon/Sm/SmMain.cs | 30 +-- Ryujinx.Horizon/Sm/SmResult.cs | 20 +- Ryujinx.Horizon/Sm/SmServerManager.cs | 30 +++ Ryujinx.Horizon/Sm/Types/SmPortIndex.cs | 8 + 83 files changed, 1106 insertions(+), 880 deletions(-) delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/ILogService.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs rename Ryujinx.Horizon/LogManager/{ => Ipc}/LmLogger.cs (81%) rename Ryujinx.Horizon/LogManager/{LmLog.cs => Ipc/LogService.cs} (55%) create mode 100644 Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs create mode 100644 Ryujinx.Horizon/Prepo/PrepoIpcServer.cs create mode 100644 Ryujinx.Horizon/Prepo/PrepoMain.cs create mode 100644 Ryujinx.Horizon/Prepo/PrepoResult.cs create mode 100644 Ryujinx.Horizon/Prepo/PrepoServerManager.cs create mode 100644 Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs rename {Ryujinx.HLE/HOS/Services => Ryujinx.Horizon}/Prepo/Types/PrepoServicePermissionLevel.cs (52%) create mode 100644 Ryujinx.Horizon/Sdk/Account/Uid.cs create mode 100644 Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs create mode 100644 Ryujinx.Horizon/Sdk/Lm/ILogService.cs create mode 100644 Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs create mode 100644 Ryujinx.Horizon/Sdk/Sm/IManagerService.cs create mode 100644 Ryujinx.Horizon/Sdk/Sm/IUserService.cs create mode 100644 Ryujinx.Horizon/Sm/Ipc/ManagerService.cs rename Ryujinx.Horizon/Sm/{ => Ipc}/UserService.cs (91%) delete mode 100644 Ryujinx.Horizon/Sm/ManagerService.cs create mode 100644 Ryujinx.Horizon/Sm/SmServerManager.cs create mode 100644 Ryujinx.Horizon/Sm/Types/SmPortIndex.cs diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index a8b5be33d..ca3f8103e 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -123,6 +123,8 @@ namespace Ryujinx.HLE.HOS internal LibHacHorizonManager LibHacHorizonManager { get; private set; } + internal ServiceTable ServiceTable { get; private set; } + public bool IsPaused { get; private set; } public Horizon(Switch device) @@ -326,6 +328,7 @@ namespace Ryujinx.HLE.HOS private void StartNewServices() { + ServiceTable = new ServiceTable(); var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices)); foreach (var service in services) diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 09e1ac31c..b6a39a20a 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -182,6 +182,8 @@ namespace Ryujinx.HLE.HOS byte[] arguments = null, params IExecutable[] executables) { + context.Device.System.ServiceTable.WaitServicesReady(); + LibHac.Result rc = metaData.GetNpdm(out var npdm); if (rc.IsFailure()) diff --git a/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs b/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs deleted file mode 100644 index 0976431b2..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.HLE.HOS.Services.Lm.LogService; - -namespace Ryujinx.HLE.HOS.Services.Lm -{ - [Service("lm")] - class ILogService : IpcService - { - public ILogService(ServiceCtx context) { } - - [CommandHipc(0)] - // Initialize(u64, pid) -> object - public ResultCode Initialize(ServiceCtx context) - { - MakeObject(context, new ILogger()); - - return ResultCode.Success; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs deleted file mode 100644 index 3181668f7..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Ryujinx.Common.Logging; -using System.IO; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Lm.LogService -{ - class ILogger : IpcService - { - public ILogger() { } - - [CommandHipc(0)] - // Log(buffer) - public ResultCode Log(ServiceCtx context) - { - Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(context)); - - return ResultCode.Success; - } - - private string LogImpl(ServiceCtx context) - { - (ulong bufPos, ulong bufSize) = context.Request.GetBufferType0x21(); - - byte[] logBuffer = new byte[bufSize]; - - context.Memory.Read(bufPos, logBuffer); - - using MemoryStream ms = new MemoryStream(logBuffer); - - BinaryReader reader = new BinaryReader(ms); - - long pid = reader.ReadInt64(); - long threadContext = reader.ReadInt64(); - short flags = reader.ReadInt16(); - byte level = reader.ReadByte(); - byte verbosity = reader.ReadByte(); - int payloadLength = reader.ReadInt32(); - - StringBuilder sb = new StringBuilder(); - - sb.AppendLine($"Guest Log:\n Log level: {(LmLogLevel)level}"); - - while (ms.Position < ms.Length) - { - int type = ReadEncodedInt(reader); - int size = ReadEncodedInt(reader); - - LmLogField field = (LmLogField)type; - - string fieldStr = string.Empty; - - if (field == LmLogField.Start) - { - reader.ReadBytes(size); - - continue; - } - else if (field == LmLogField.Stop) - { - break; - } - else if (field == LmLogField.Line) - { - fieldStr = $"{field}: {reader.ReadInt32()}"; - } - else if (field == LmLogField.DropCount) - { - fieldStr = $"{field}: {reader.ReadInt64()}"; - } - else if (field == LmLogField.Time) - { - fieldStr = $"{field}: {reader.ReadInt64()}s"; - } - else if (field < LmLogField.Count) - { - fieldStr = $"{field}: '{Encoding.UTF8.GetString(reader.ReadBytes(size)).TrimEnd()}'"; - } - else - { - fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.ReadBytes(size)).TrimEnd()}'"; - } - - sb.AppendLine($" {fieldStr}"); - } - - return sb.ToString(); - } - - private static int ReadEncodedInt(BinaryReader reader) - { - int result = 0; - int position = 0; - - byte encoded; - - do - { - encoded = reader.ReadByte(); - - result += (encoded & 0x7F) << (7 * position); - - position++; - - } while ((encoded & 0x80) != 0); - - return result; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs deleted file mode 100644 index 3f93e1676..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Lm.LogService -{ - enum LmLogField - { - Start = 0, - Stop = 1, - Message = 2, - Line = 3, - Filename = 4, - Function = 5, - Module = 6, - Thread = 7, - DropCount = 8, - Time = 9, - ProgramName = 10, - Count - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs deleted file mode 100644 index ee1a8396a..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Lm.LogService -{ - enum LmLogLevel - { - Trace, - Info, - Warning, - Error, - Critical - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs deleted file mode 100644 index 019626f1b..000000000 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ /dev/null @@ -1,182 +0,0 @@ -using MsgPack; -using MsgPack.Serialization; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.Utilities; -using System; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Prepo -{ - [Service("prepo:a", PrepoServicePermissionLevel.Admin)] // 1.0.0-5.1.0 - [Service("prepo:a2", PrepoServicePermissionLevel.Admin)] // 6.0.0+ - [Service("prepo:m", PrepoServicePermissionLevel.Manager)] - [Service("prepo:u", PrepoServicePermissionLevel.User)] - [Service("prepo:s", PrepoServicePermissionLevel.System)] - class IPrepoService : IpcService - { - private PrepoServicePermissionLevel _permission; - private ulong _systemSessionId; - - public IPrepoService(ServiceCtx context, PrepoServicePermissionLevel permission) - { - _permission = permission; - } - - [CommandHipc(10100)] // 1.0.0-5.1.0 - [CommandHipc(10102)] // 6.0.0-9.2.0 - [CommandHipc(10104)] // 10.0.0+ - // SaveReport(u64, pid, buffer, buffer) - public ResultCode SaveReport(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.User) == 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: false); - } - - [CommandHipc(10101)] // 1.0.0-5.1.0 - [CommandHipc(10103)] // 6.0.0-9.2.0 - [CommandHipc(10105)] // 10.0.0+ - // SaveReportWithUser(nn::account::Uid, u64, pid, buffer, buffer) - public ResultCode SaveReportWithUser(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.User) == 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: true); - } - - [CommandHipc(10200)] - // RequestImmediateTransmission() - public ResultCode RequestImmediateTransmission(ServiceCtx context) - { - // It signals an event of nn::prepo::detail::service::core::TransmissionStatusManager that requests the transmission of the report. - // Since we don't use reports it's fine to do nothing. - - return ResultCode.Success; - } - - [CommandHipc(10300)] - // GetTransmissionStatus() -> u32 - public ResultCode GetTransmissionStatus(ServiceCtx context) - { - // It returns the transmission result of nn::prepo::detail::service::core::TransmissionStatusManager. - // Since we don't use reports it's fine to return ResultCode.Success. - context.ResponseData.Write((int)ResultCode.Success); - - return ResultCode.Success; - } - - [CommandHipc(10400)] // 9.0.0+ - // GetSystemSessionId() -> u64 - public ResultCode GetSystemSessionId(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.User) == 0) - { - return ResultCode.PermissionDenied; - } - - if (_systemSessionId == 0) - { - byte[] randomBuffer = new byte[8]; - - Random.Shared.NextBytes(randomBuffer); - - _systemSessionId = BitConverter.ToUInt64(randomBuffer, 0); - } - - context.ResponseData.Write(_systemSessionId); - - return ResultCode.Success; - } - - [CommandHipc(20100)] - // SaveSystemReport(u64, pid, buffer, buffer) - public ResultCode SaveSystemReport(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.System) != 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: false); - } - - [CommandHipc(20101)] - // SaveSystemReportWithUser(nn::account::Uid, u64, pid, buffer, buffer) - public ResultCode SaveSystemReportWithUser(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.System) != 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: true); - } - - private ResultCode ProcessReport(ServiceCtx context, bool withUserID) - { - UserId userId = withUserID ? context.RequestData.ReadStruct() : new UserId(); - string gameRoom = StringUtils.ReadUtf8String(context); - - if (withUserID) - { - if (userId.IsNull) - { - return ResultCode.InvalidArgument; - } - } - - if (gameRoom == string.Empty) - { - return ResultCode.InvalidState; - } - - ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; - - if (inputSize == 0) - { - return ResultCode.InvalidBufferSize; - } - - byte[] inputBuffer = new byte[inputSize]; - - context.Memory.Read(inputPosition, inputBuffer); - - Logger.Info?.Print(LogClass.ServicePrepo, ReadReportBuffer(inputBuffer, gameRoom, userId)); - - return ResultCode.Success; - } - - private string ReadReportBuffer(byte[] buffer, string room, UserId userId) - { - StringBuilder builder = new StringBuilder(); - MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(buffer); - - builder.AppendLine(); - builder.AppendLine("PlayReport log:"); - - if (!userId.IsNull) - { - builder.AppendLine($" UserId: {userId}"); - } - - builder.AppendLine($" Room: {room}"); - builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}"); - - return builder.ToString(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs deleted file mode 100644 index 3199e2705..000000000 --- a/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Prepo -{ - enum ResultCode - { - ModuleId = 129, - ErrorCodeShift = 9, - - Success = 0, - - InvalidArgument = (1 << ErrorCodeShift) | ModuleId, - InvalidState = (5 << ErrorCodeShift) | ModuleId, - InvalidBufferSize = (9 << ErrorCodeShift) | ModuleId, - PermissionDenied = (90 << ErrorCodeShift) | ModuleId - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 86031a707..73652da4b 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm return ResultCode.InvalidName; } - Logger.Info?.Print(LogClass.ServiceSm, $"Register \"{name}\"."); + Logger.Debug?.Print(LogClass.ServiceSm, $"Register \"{name}\"."); KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, null); diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs index a66d57a3b..332e04d17 100644 --- a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs +++ b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -123,44 +123,51 @@ namespace Ryujinx.Horizon.Generators.Hipc { string[] args = new string[method.ParameterList.Parameters.Count]; - int index = 0; - - foreach (var parameter in method.ParameterList.Parameters) + if (args.Length == 0) { - string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); - CommandArgType argType = GetCommandArgType(compilation, parameter); + generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, Array.Empty()) }},"); + } + else + { + int index = 0; - string arg; - - if (argType == CommandArgType.Buffer) + foreach (var parameter in method.ParameterList.Parameters) { - string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0); - string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1); + string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); + CommandArgType argType = GetCommandArgType(compilation, parameter); - if (bufferFixedSize != null) + string arg; + + if (argType == CommandArgType.Buffer) { - arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})"; + string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0); + string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1); + + if (bufferFixedSize != null) + { + arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})"; + } + else + { + arg = $"new CommandArg({bufferFlags})"; + } + } + else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument) + { + string alignment = GetTypeAlignmentExpression(compilation, parameter.Type); + + arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})"; } else { - arg = $"new CommandArg({bufferFlags})"; + arg = $"new CommandArg(CommandArgType.{argType})"; } - } - else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument) - { - string alignment = GetTypeAlignmentExpression(compilation, parameter.Type); - arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})"; - } - else - { - arg = $"new CommandArg(CommandArgType.{argType})"; + args[index++] = arg; } - args[index++] = arg; + generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)}) }},"); } - - generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)}) }},"); } } diff --git a/Ryujinx.Horizon/HeapAllocator.cs b/Ryujinx.Horizon/HeapAllocator.cs index 867c96770..40ff14d09 100644 --- a/Ryujinx.Horizon/HeapAllocator.cs +++ b/Ryujinx.Horizon/HeapAllocator.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Horizon private struct Range : IComparable { public ulong Offset { get; } - public ulong Size { get; } + public ulong Size { get; } public Range(ulong offset, ulong size) { Offset = offset; - Size = size; + Size = size; } public int CompareTo(Range other) @@ -31,7 +31,7 @@ namespace Ryujinx.Horizon public HeapAllocator() { - _freeRanges = new List(); + _freeRanges = new List(); _currentHeapSize = 0; } @@ -70,8 +70,8 @@ namespace Ryujinx.Horizon var range = _freeRanges[i]; ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment); - ulong sizeDelta = alignedOffset - range.Offset; - ulong usableSize = range.Size - sizeDelta; + ulong sizeDelta = alignedOffset - range.Offset; + ulong usableSize = range.Size - sizeDelta; if (sizeDelta < range.Size && usableSize >= size) { @@ -82,7 +82,7 @@ namespace Ryujinx.Horizon InsertFreeRange(range.Offset, sizeDelta); } - ulong endOffset = range.Offset + range.Size; + ulong endOffset = range.Offset + range.Size; ulong remainingSize = endOffset - (alignedOffset + size); if (remainingSize != 0) { diff --git a/Ryujinx.Horizon/HorizonOptions.cs b/Ryujinx.Horizon/HorizonOptions.cs index b1567c6a4..6d580e8b9 100644 --- a/Ryujinx.Horizon/HorizonOptions.cs +++ b/Ryujinx.Horizon/HorizonOptions.cs @@ -2,11 +2,13 @@ namespace Ryujinx.Horizon { public struct HorizonOptions { - public bool IgnoreMissingServices { get; } + public bool IgnoreMissingServices { get; } + public bool ThrowOnInvalidCommandIds { get; } public HorizonOptions(bool ignoreMissingServices) { - IgnoreMissingServices = ignoreMissingServices; + IgnoreMissingServices = ignoreMissingServices; + ThrowOnInvalidCommandIds = true; } } } diff --git a/Ryujinx.Horizon/HorizonStatic.cs b/Ryujinx.Horizon/HorizonStatic.cs index 1e483cd44..e372df699 100644 --- a/Ryujinx.Horizon/HorizonStatic.cs +++ b/Ryujinx.Horizon/HorizonStatic.cs @@ -21,24 +21,24 @@ namespace Ryujinx.Horizon [ThreadStatic] private static int _threadHandle; - public static HorizonOptions Options => _options; - public static ISyscallApi Syscall => _syscall; - public static IVirtualMemoryManager AddressSpace => _addressSpace; - public static IThreadContext ThreadContext => _threadContext; - public static int CurrentThreadHandle => _threadHandle; + public static HorizonOptions Options => _options; + public static ISyscallApi Syscall => _syscall; + public static IVirtualMemoryManager AddressSpace => _addressSpace; + public static IThreadContext ThreadContext => _threadContext; + public static int CurrentThreadHandle => _threadHandle; public static void Register( - HorizonOptions options, - ISyscallApi syscallApi, + HorizonOptions options, + ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, - IThreadContext threadContext, - int threadHandle) + IThreadContext threadContext, + int threadHandle) { - _options = options; - _syscall = syscallApi; - _addressSpace = addressSpace; + _options = options; + _syscall = syscallApi; + _addressSpace = addressSpace; _threadContext = threadContext; - _threadHandle = threadHandle; + _threadHandle = threadHandle; } } } diff --git a/Ryujinx.Horizon/IService.cs b/Ryujinx.Horizon/IService.cs index 67c12cef6..c0bc0bcb7 100644 --- a/Ryujinx.Horizon/IService.cs +++ b/Ryujinx.Horizon/IService.cs @@ -2,6 +2,6 @@ { interface IService { - abstract static void Main(); + abstract static void Main(ServiceTable serviceTable); } } diff --git a/Ryujinx.Horizon/LogManager/LmLogger.cs b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs similarity index 81% rename from Ryujinx.Horizon/LogManager/LmLogger.cs rename to Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs index 461776cd8..002a59824 100644 --- a/Ryujinx.Horizon/LogManager/LmLogger.cs +++ b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -9,23 +9,23 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -namespace Ryujinx.Horizon.LogManager +namespace Ryujinx.Horizon.LogManager.Ipc { - partial class LmLogger : IServiceObject + partial class LmLogger : ILmLogger { - private readonly LmLog _log; - private readonly ulong _clientProcessId; + private readonly LogService _log; + private readonly ulong _pid; - public LmLogger(LmLog log, ulong clientProcessId) + public LmLogger(LogService log, ulong pid) { _log = log; - _clientProcessId = clientProcessId; + _pid = pid; } [CmifCommand(0)] public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span message) { - if (!SetProcessId(message, _clientProcessId)) + if (!SetProcessId(message, _pid)) { return Result.Success; } @@ -35,7 +35,7 @@ namespace Ryujinx.Horizon.LogManager return Result.Success; } - [CmifCommand(1)] + [CmifCommand(1)] // 3.0.0+ public Result SetDestination(LogDestination destination) { _log.LogDestination = destination; @@ -48,7 +48,6 @@ namespace Ryujinx.Horizon.LogManager ref LogPacketHeader header = ref MemoryMarshal.Cast(message)[0]; uint expectedMessageSize = (uint)Unsafe.SizeOf() + header.PayloadSize; - if (expectedMessageSize != (uint)message.Length) { Logger.Warning?.Print(LogClass.ServiceLm, $"Invalid message size (expected 0x{expectedMessageSize:X} but got 0x{message.Length:X})."); @@ -63,13 +62,11 @@ namespace Ryujinx.Horizon.LogManager private static string LogImpl(ReadOnlySpan message) { - SpanReader reader = new SpanReader(message); + SpanReader reader = new(message); + LogPacketHeader header = reader.Read(); + StringBuilder builder = new(); - LogPacketHeader header = reader.Read(); - - StringBuilder sb = new StringBuilder(); - - sb.AppendLine($"Guest Log:\n Log level: {header.Severity}"); + builder.AppendLine($"Guest Log:\n Log level: {header.Severity}"); while (reader.Length > 0) { @@ -78,7 +75,7 @@ namespace Ryujinx.Horizon.LogManager LogDataChunkKey field = (LogDataChunkKey)type; - string fieldStr = string.Empty; + string fieldStr; if (field == LogDataChunkKey.Start) { @@ -111,16 +108,16 @@ namespace Ryujinx.Horizon.LogManager fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; } - sb.AppendLine($" {fieldStr}"); + builder.AppendLine($" {fieldStr}"); } - return sb.ToString(); + return builder.ToString(); } private static int ReadUleb128(ref SpanReader reader) { int result = 0; - int count = 0; + int count = 0; byte encoded; @@ -136,4 +133,4 @@ namespace Ryujinx.Horizon.LogManager return result; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/LogManager/LmLog.cs b/Ryujinx.Horizon/LogManager/Ipc/LogService.cs similarity index 55% rename from Ryujinx.Horizon/LogManager/LmLog.cs rename to Ryujinx.Horizon/LogManager/Ipc/LogService.cs index 772465c45..6899739e3 100644 --- a/Ryujinx.Horizon/LogManager/LmLog.cs +++ b/Ryujinx.Horizon/LogManager/Ipc/LogService.cs @@ -2,18 +2,19 @@ using Ryujinx.Horizon.Sdk.Lm; using Ryujinx.Horizon.Sdk.Sf; -namespace Ryujinx.Horizon.LogManager +namespace Ryujinx.Horizon.LogManager.Ipc { - partial class LmLog : IServiceObject + partial class LogService : ILogService { public LogDestination LogDestination { get; set; } = LogDestination.TargetManager; [CmifCommand(0)] - public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong clientProcessId) + public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong pid) { - logger = new LmLogger(this, clientProcessId); + // NOTE: Internal name is Logger, but we rename it LmLogger to avoid name clash with Ryujinx.Common.Logging logger. + logger = new LmLogger(this, pid); return Result.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/Ryujinx.Horizon/LogManager/LmIpcServer.cs index 7b757fe98..71b844a2b 100644 --- a/Ryujinx.Horizon/LogManager/LmIpcServer.cs +++ b/Ryujinx.Horizon/LogManager/LmIpcServer.cs @@ -1,6 +1,6 @@ -using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.LogManager.Ipc; +using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; -using Ryujinx.Horizon.Sm; namespace Ryujinx.Horizon.LogManager { @@ -9,36 +9,25 @@ namespace Ryujinx.Horizon.LogManager private const int LogMaxSessionsCount = 42; private const int PointerBufferSize = 0x400; - private const int MaxDomains = 31; - private const int MaxDomainObjects = 61; + private const int MaxDomains = 31; + private const int MaxDomainObjects = 61; + private const int MaxPortsCount = 1; - private const int MaxPortsCount = 1; + private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); - private static readonly ManagerOptions _logManagerOptions = new ManagerOptions( - PointerBufferSize, - MaxDomains, - MaxDomainObjects, - false); - - private static readonly ServiceName _logServiceName = ServiceName.Encode("lm"); - - private SmApi _sm; + private SmApi _sm; private ServerManager _serverManager; - private LmLog _logServiceObject; - public void Initialize() { - HeapAllocator allocator = new HeapAllocator(); + HeapAllocator allocator = new(); _sm = new SmApi(); _sm.Initialize().AbortOnFailure(); _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount); - _logServiceObject = new LmLog(); - - _serverManager.RegisterObjectForServer(_logServiceObject, _logServiceName, LogMaxSessionsCount); + _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), LogMaxSessionsCount); } public void ServiceRequests() @@ -51,4 +40,4 @@ namespace Ryujinx.Horizon.LogManager _serverManager.Dispose(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/LogManager/LmMain.cs b/Ryujinx.Horizon/LogManager/LmMain.cs index 8c0262ac6..bbe96d4c9 100644 --- a/Ryujinx.Horizon/LogManager/LmMain.cs +++ b/Ryujinx.Horizon/LogManager/LmMain.cs @@ -2,13 +2,16 @@ { class LmMain : IService { - public static void Main() + public static void Main(ServiceTable serviceTable) { - LmIpcServer ipcServer = new LmIpcServer(); + LmIpcServer ipcServer = new(); ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + ipcServer.ServiceRequests(); ipcServer.Shutdown(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs new file mode 100644 index 000000000..672bba4ea --- /dev/null +++ b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs @@ -0,0 +1,218 @@ +using MsgPack; +using MsgPack.Serialization; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Sdk.Account; +using Ryujinx.Horizon.Sdk.Prepo; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Text; + +namespace Ryujinx.Horizon.Prepo.Ipc +{ + partial class PrepoService : IPrepoService + { + enum PlayReportKind + { + Normal, + System + } + + private readonly PrepoServicePermissionLevel _permissionLevel; + private ulong _systemSessionId; + + private bool _immediateTransmissionEnabled = false; + private bool _userAgreementCheckEnabled = true; + + public PrepoService(PrepoServicePermissionLevel permissionLevel) + { + _permissionLevel = permissionLevel; + } + + [CmifCommand(10100)] // 1.0.0-5.1.0 + [CmifCommand(10102)] // 6.0.0-9.2.0 + [CmifCommand(10104)] // 10.0.0+ + public Result SaveReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0) + { + return PrepoResult.PermissionDenied; + } + + ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, Uid.Null); + + return Result.Success; + } + + [CmifCommand(10101)] // 1.0.0-5.1.0 + [CmifCommand(10103)] // 6.0.0-9.2.0 + [CmifCommand(10105)] // 10.0.0+ + public Result SaveReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0) + { + return PrepoResult.PermissionDenied; + } + + ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, userId, true); + + return Result.Success; + } + + [CmifCommand(10200)] + public Result RequestImmediateTransmission() + { + _immediateTransmissionEnabled = true; + + // It signals an event of nn::prepo::detail::service::core::TransmissionStatusManager that requests the transmission of the report. + // Since we don't use reports, it's fine to do nothing. + + return Result.Success; + } + + [CmifCommand(10300)] + public Result GetTransmissionStatus(out int status) + { + status = 0; + + if (_immediateTransmissionEnabled && _userAgreementCheckEnabled) + { + status = 1; + } + + return Result.Success; + } + + [CmifCommand(10400)] // 9.0.0+ + public Result GetSystemSessionId(out ulong systemSessionId) + { + systemSessionId = default; + + if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0) + { + return PrepoResult.PermissionDenied; + } + + if (_systemSessionId == 0) + { + _systemSessionId = (ulong)Random.Shared.NextInt64(); + } + + systemSessionId = _systemSessionId; + + return Result.Success; + } + + [CmifCommand(20100)] + public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) + { + return PrepoResult.PermissionDenied; + } + + return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, Uid.Null); + } + + [CmifCommand(20101)] + public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) + { + return PrepoResult.PermissionDenied; + } + + return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, userId, true); + } + + [CmifCommand(40100)] // 2.0.0+ + public Result IsUserAgreementCheckEnabled(out bool enabled) + { + enabled = false; + + if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System) + { + enabled = _userAgreementCheckEnabled; + + // If "enabled" is false, it sets some internal fields to 0. + // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and returns the contained bool. + // We can return the private bool instead, we don't care about the agreement since we don't send reports. + + return Result.Success; + } + + return PrepoResult.PermissionDenied; + } + + [CmifCommand(40101)] // 2.0.0+ + public Result SetUserAgreementCheckEnabled(bool enabled) + { + if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System) + { + _userAgreementCheckEnabled = enabled; + + // If "enabled" is false, it sets some internal fields to 0. + // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and stores the "enabled" value. + // We can store in the private bool instead, we don't care about the agreement since we don't send reports. + + return Result.Success; + } + + return PrepoResult.PermissionDenied; + } + + private static Result ProcessPlayReport(PlayReportKind playReportKind, ulong pid, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, Uid userId, bool withUserId = false) + { + if (withUserId) + { + if (userId.IsNull) + { + return PrepoResult.InvalidArgument; + } + } + + if (gameRoomBuffer.Length > 31) + { + return PrepoResult.InvalidArgument; + } + + string gameRoom = Encoding.UTF8.GetString(gameRoomBuffer).TrimEnd(); + + if (gameRoom == string.Empty) + { + return PrepoResult.InvalidState; + } + + if (reportBuffer.Length == 0) + { + return PrepoResult.InvalidBufferSize; + } + + // NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned. + // Reports are stored internally and an event is signaled to transmit them. + + StringBuilder builder = new(); + MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray()); + + builder.AppendLine(); + builder.AppendLine("PlayReport log:"); + builder.AppendLine($" Kind: {playReportKind}"); + builder.AppendLine($" Pid: {pid}"); + + if (!userId.IsNull) + { + builder.AppendLine($" UserId: {userId}"); + } + + builder.AppendLine($" Room: {gameRoom}"); + builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}"); + + Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString()); + + return Result.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs new file mode 100644 index 000000000..ba19ff6f6 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs @@ -0,0 +1,49 @@ +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Prepo +{ + class PrepoIpcServer + { + private const int PrepoMaxSessionsCount = 12; + private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6; + + private const int PointerBufferSize = 0x3800; + private const int MaxDomains = 64; + private const int MaxDomainObjects = 16; + private const int MaxPortsCount = 6; + + private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + private SmApi _sm; + private PrepoServerManager _serverManager; + + public void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, PrepoTotalMaxSessionsCount); + + _serverManager.RegisterServer((int)PrepoPortIndex.Admin, ServiceName.Encode("prepo:a"), PrepoMaxSessionsCount); // 1.0.0-5.1.0 + _serverManager.RegisterServer((int)PrepoPortIndex.Admin2, ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+ + _serverManager.RegisterServer((int)PrepoPortIndex.Manager, ServiceName.Encode("prepo:m"), PrepoMaxSessionsCount); + _serverManager.RegisterServer((int)PrepoPortIndex.User, ServiceName.Encode("prepo:u"), PrepoMaxSessionsCount); + _serverManager.RegisterServer((int)PrepoPortIndex.System, ServiceName.Encode("prepo:s"), PrepoMaxSessionsCount); + _serverManager.RegisterServer((int)PrepoPortIndex.Debug, ServiceName.Encode("prepo:d"), PrepoMaxSessionsCount); // 1.0.0 + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoMain.cs b/Ryujinx.Horizon/Prepo/PrepoMain.cs new file mode 100644 index 000000000..5ff0f53d9 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoMain.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Horizon.Prepo +{ + class PrepoMain : IService + { + public static void Main(ServiceTable serviceTable) + { + PrepoIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoResult.cs b/Ryujinx.Horizon/Prepo/PrepoResult.cs new file mode 100644 index 000000000..12255e3d8 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoResult.cs @@ -0,0 +1,15 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Prepo +{ + static class PrepoResult + { + private const int ModuleId = 129; + + public static Result InvalidArgument => new(ModuleId, 1); + public static Result InvalidState => new(ModuleId, 5); + public static Result InvalidBufferSize => new(ModuleId, 9); + public static Result PermissionDenied => new(ModuleId, 90); + public static Result InvalidPid => new(ModuleId, 101); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoServerManager.cs b/Ryujinx.Horizon/Prepo/PrepoServerManager.cs new file mode 100644 index 000000000..55e4ff7db --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoServerManager.cs @@ -0,0 +1,30 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Prepo.Ipc; +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using System; + +namespace Ryujinx.Horizon.Prepo +{ + class PrepoServerManager : ServerManager + { + public PrepoServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) + { + } + + protected override Result OnNeedsToAccept(int portIndex, Server server) + { + return (PrepoPortIndex)portIndex switch + { + PrepoPortIndex.Admin => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)), + PrepoPortIndex.Admin2 => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)), + PrepoPortIndex.Manager => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Manager)), + PrepoPortIndex.User => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.User)), + PrepoPortIndex.System => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.System)), + PrepoPortIndex.Debug => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Debug)), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs b/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs new file mode 100644 index 000000000..f4d6b8773 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Horizon.Prepo.Types +{ + enum PrepoPortIndex + { + Admin, + Admin2, + Manager, + User, + System, + Debug + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs b/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs similarity index 52% rename from Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs rename to Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs index 35774707d..8214f4b9a 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs +++ b/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs @@ -1,10 +1,11 @@ -namespace Ryujinx.HLE.HOS.Services.Prepo +namespace Ryujinx.Horizon.Prepo.Types { enum PrepoServicePermissionLevel { Admin = -1, User = 1, System = 2, - Manager = 6 + Manager = 6, + Debug = unchecked((int)0x80000006) } } \ No newline at end of file diff --git a/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/Ryujinx.Horizon/Ryujinx.Horizon.csproj index e4591c6f9..0139c367f 100644 --- a/Ryujinx.Horizon/Ryujinx.Horizon.csproj +++ b/Ryujinx.Horizon/Ryujinx.Horizon.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/Ryujinx.Horizon/Sdk/Account/Uid.cs b/Ryujinx.Horizon/Sdk/Account/Uid.cs new file mode 100644 index 000000000..a0a399781 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Account/Uid.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Account +{ + [StructLayout(LayoutKind.Sequential)] + public readonly record struct Uid + { + public readonly long High; + public readonly long Low; + + public bool IsNull => (Low | High) == 0; + + public static Uid Null => new(0, 0); + + public Uid(long low, long high) + { + Low = low; + High = high; + } + + public Uid(byte[] bytes) + { + High = BitConverter.ToInt64(bytes, 0); + Low = BitConverter.ToInt64(bytes, 8); + } + + public Uid(string hex) + { + if (hex == null || hex.Length != 32 || !hex.All("0123456789abcdefABCDEF".Contains)) + { + throw new ArgumentException("Invalid Hex value!", nameof(hex)); + } + + Low = Convert.ToInt64(hex[16..], 16); + High = Convert.ToInt64(hex[..16], 16); + } + + public void Write(BinaryWriter binaryWriter) + { + binaryWriter.Write(High); + binaryWriter.Write(Low); + } + + public override string ToString() + { + return High.ToString("x16") + Low.ToString("x16"); + } + + public LibHac.Account.Uid ToLibHacUid() + { + return new LibHac.Account.Uid((ulong)High, (ulong)Low); + } + + public UInt128 ToUInt128() + { + return new UInt128((ulong)High, (ulong)Low); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs b/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs new file mode 100644 index 000000000..bb5770cbc --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Lm +{ + interface ILmLogger : IServiceObject + { + Result Log(Span message); + Result SetDestination(LogDestination destination); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Lm/ILogService.cs b/Ryujinx.Horizon/Sdk/Lm/ILogService.cs new file mode 100644 index 000000000..ad6c8455a --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/ILogService.cs @@ -0,0 +1,11 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.LogManager.Ipc; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Lm +{ + interface ILogService : IServiceObject + { + Result OpenLogger(out LmLogger logger, ulong pid); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs new file mode 100644 index 000000000..f61ee61bd --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs @@ -0,0 +1,20 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Account; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Prepo +{ + interface IPrepoService : IServiceObject + { + Result SaveReport(ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result SaveReportWithUser(Uid userId, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result RequestImmediateTransmission(); + Result GetTransmissionStatus(out int status); + Result GetSystemSessionId(out ulong systemSessionId); + Result SaveSystemReport(ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result IsUserAgreementCheckEnabled(out bool enabled); + Result SetUserAgreementCheckEnabled(bool enabled); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/ServiceUtil.cs b/Ryujinx.Horizon/Sdk/ServiceUtil.cs index 413ac1f6b..fe6fcce15 100644 --- a/Ryujinx.Horizon/Sdk/ServiceUtil.cs +++ b/Ryujinx.Horizon/Sdk/ServiceUtil.cs @@ -10,15 +10,15 @@ namespace Ryujinx.Horizon.Sdk public static Result SendRequest(out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan data) { ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress; - int tlsSize = Api.TlsMessageBufferSize; + int tlsSize = Api.TlsMessageBufferSize; using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize)) { CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat() { - DataSize = data.Length, + DataSize = data.Length, RequestId = requestId, - SendPid = sendPid + SendPid = sendPid }); data.CopyTo(request.Data); @@ -29,10 +29,11 @@ namespace Ryujinx.Horizon.Sdk if (result.IsFailure) { response = default; + return result; } return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs index 882115013..beaff613f 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs @@ -3,10 +3,10 @@ struct CmifDomainInHeader { public CmifDomainRequestType Type; - public byte ObjectsCount; - public ushort DataSize; - public int ObjectId; - public uint Padding; - public uint Token; + public byte ObjectsCount; + public ushort DataSize; + public int ObjectId; + public uint Padding; + public uint Token; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs index b913db94e..1a02e0825 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs @@ -2,8 +2,8 @@ { enum CmifDomainRequestType : byte { - Invalid = 0, + Invalid = 0, SendMessage = 1, - Close = 2 + Close = 2 } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs index 781452e30..0d23d33bb 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { static class CmifMessage { - public const uint CmifInHeaderMagic = 0x49434653; // SFCI + public const uint CmifInHeaderMagic = 0x49434653; // SFCI public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO public static CmifRequest CreateRequest(Span output, CmifRequestFormat format) @@ -21,27 +21,31 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif } totalSize += Unsafe.SizeOf() + format.DataSize; - totalSize = (totalSize + 1) & ~1; + totalSize = (totalSize + 1) & ~1; + int outPointerSizeTableOffset = totalSize; - int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount; + int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount; + totalSize += sizeof(ushort) * outPointerSizeTableSize; + int rawDataSizeInWords = (totalSize + sizeof(uint) - 1) / sizeof(uint); - CmifRequest request = new CmifRequest(); - - request.Hipc = HipcMessage.WriteMessage(output, new HipcMetadata() + CmifRequest request = new() { - Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request, - SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount, - SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount, - ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount, - ExchangeBuffersCount = format.InOutBuffersCount, - DataWordsCount = rawDataSizeInWords, - ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount, - SendPid = format.SendPid, - CopyHandlesCount = format.HandlesCount, - MoveHandlesCount = 0 - }); + Hipc = HipcMessage.WriteMessage(output, new HipcMetadata() + { + Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request, + SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount, + SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount, + ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount, + ExchangeBuffersCount = format.InOutBuffersCount, + DataWordsCount = rawDataSizeInWords, + ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount, + SendPid = format.SendPid, + CopyHandlesCount = format.HandlesCount, + MoveHandlesCount = 0 + }) + }; Span data = request.Hipc.DataWords; @@ -53,35 +57,36 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif domainHeader = new CmifDomainInHeader() { - Type = CmifDomainRequestType.SendMessage, + Type = CmifDomainRequestType.SendMessage, ObjectsCount = (byte)format.ObjectsCount, - DataSize = (ushort)payloadSize, - ObjectId = format.ObjectId, - Padding = 0, - Token = format.Context + DataSize = (ushort)payloadSize, + ObjectId = format.ObjectId, + Padding = 0, + Token = format.Context }; - data = data.Slice(Unsafe.SizeOf() / sizeof(uint)); + data = data[(Unsafe.SizeOf() / sizeof(uint))..]; - request.Objects = data.Slice((payloadSize + sizeof(uint) - 1) / sizeof(uint)); + request.Objects = data[((payloadSize + sizeof(uint) - 1) / sizeof(uint))..]; } ref CmifInHeader header = ref MemoryMarshal.Cast(data)[0]; header = new CmifInHeader() { - Magic = CmifInHeaderMagic, - Version = format.Context != 0 ? 1u : 0u, + Magic = CmifInHeaderMagic, + Version = format.Context != 0 ? 1u : 0u, CommandId = format.RequestId, - Token = format.ObjectId != 0 ? 0u : format.Context + Token = format.ObjectId != 0 ? 0u : format.Context }; - request.Data = MemoryMarshal.Cast(data).Slice(Unsafe.SizeOf()); + request.Data = MemoryMarshal.Cast(data)[Unsafe.SizeOf()..]; int paddingSizeBefore = (rawDataSizeInWords - request.Hipc.DataWords.Length) * sizeof(uint); - Span outPointerTable = MemoryMarshal.Cast(request.Hipc.DataWords).Slice(outPointerSizeTableOffset - paddingSizeBefore); - request.OutPointerSizes = MemoryMarshal.Cast(outPointerTable); + Span outPointerTable = MemoryMarshal.Cast(request.Hipc.DataWords)[(outPointerSizeTableOffset - paddingSizeBefore)..]; + + request.OutPointerSizes = MemoryMarshal.Cast(outPointerTable); request.ServerPointerSize = format.ServerPointerSize; return request; @@ -89,15 +94,15 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public static Result ParseResponse(out CmifResponse response, Span input, bool isDomain, int size) { - HipcMessage responseMessage = new HipcMessage(input); + HipcMessage responseMessage = new(input); - Span data = MemoryMarshal.Cast(responseMessage.Data.DataWords); + Span data = MemoryMarshal.Cast(responseMessage.Data.DataWords); Span objects = Span.Empty; if (isDomain) { - data = data.Slice(Unsafe.SizeOf()); - objects = MemoryMarshal.Cast(data.Slice(Unsafe.SizeOf() + size)); + data = data[Unsafe.SizeOf()..]; + objects = MemoryMarshal.Cast(data[(Unsafe.SizeOf() + size)..]); } CmifOutHeader header = MemoryMarshal.Cast(data)[0]; @@ -105,19 +110,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif if (header.Magic != CmifOutHeaderMagic) { response = default; + return SfResult.InvalidOutHeader; } if (header.Result.IsFailure) { response = default; + return header.Result; } response = new CmifResponse() { - Data = data.Slice(Unsafe.SizeOf()), - Objects = objects, + Data = data[Unsafe.SizeOf()..], + Objects = objects, CopyHandles = responseMessage.Data.CopyHandles, MoveHandles = responseMessage.Data.MoveHandles }; @@ -125,4 +132,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return Result.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs index 2828cde5e..00b9d2bda 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs @@ -5,10 +5,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif struct CmifOutHeader { #pragma warning disable CS0649 - public uint Magic; - public uint Version; + public uint Magic; + public uint Version; public Result Result; - public uint Token; + public uint Token; #pragma warning restore CS0649 } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs index 80772ad33..e44a84ec1 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs @@ -6,9 +6,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif ref struct CmifRequest { public HipcMessageData Hipc; - public Span Data; - public Span OutPointerSizes; - public Span Objects; - public int ServerPointerSize; + public Span Data; + public Span OutPointerSizes; + public Span Objects; + public int ServerPointerSize; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs index d11545786..592f11f42 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs @@ -3,21 +3,21 @@ struct CmifRequestFormat { #pragma warning disable CS0649 - public int ObjectId; + public int ObjectId; public uint RequestId; public uint Context; - public int DataSize; - public int ServerPointerSize; - public int InAutoBuffersCount; - public int OutAutoBuffersCount; - public int InBuffersCount; - public int OutBuffersCount; - public int InOutBuffersCount; - public int InPointersCount; - public int OutPointersCount; - public int OutFixedPointersCount; - public int ObjectsCount; - public int HandlesCount; + public int DataSize; + public int ServerPointerSize; + public int InAutoBuffersCount; + public int OutAutoBuffersCount; + public int InBuffersCount; + public int OutBuffersCount; + public int InOutBuffersCount; + public int InPointersCount; + public int OutPointersCount; + public int OutFixedPointersCount; + public int ObjectsCount; + public int HandlesCount; public bool SendPid; #pragma warning restore CS0649 } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs index d1d8dc9c5..2ff31eb67 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { public ReadOnlySpan Data; public ReadOnlySpan Objects; - public ReadOnlySpan CopyHandles; - public ReadOnlySpan MoveHandles; + public ReadOnlySpan CopyHandles; + public ReadOnlySpan MoveHandles; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs index b3b05864e..82c0648b6 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs @@ -2,12 +2,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { enum CommandType { - Invalid = 0, - LegacyRequest = 1, - Close = 2, - LegacyControl = 3, - Request = 4, - Control = 5, + Invalid = 0, + LegacyRequest = 1, + Close = 2, + LegacyControl = 3, + Request = 4, + Control = 5, RequestWithContext = 6, ControlWithContext = 7 } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs index bcf311b2e..b0b4498d9 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif var inHeader = MemoryMarshal.Cast(inRawData)[0]; - ReadOnlySpan inDomainRawData = inRawData.Slice(Unsafe.SizeOf()); + ReadOnlySpan inDomainRawData = inRawData[Unsafe.SizeOf()..]; int targetObjectId = inHeader.ObjectId; @@ -39,7 +39,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return SfResult.InvalidHeaderSize; } - ReadOnlySpan inMessageRawData = inDomainRawData.Slice(0, inHeader.DataSize); + ReadOnlySpan inMessageRawData = inDomainRawData[..inHeader.DataSize]; if (inHeader.ObjectsCount > DomainServiceObjectProcessor.MaximumObjects) { diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs index 92d86196a..796b8a789 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return SfResult.InvalidInObjectsCount; } - Result result = _domain.ReserveIds(new Span(_reservedObjectIds).Slice(0, OutObjectsCount)); + Result result = _domain.ReserveIds(new Span(_reservedObjectIds)[..OutObjectsCount]); if (result.IsFailure) { @@ -92,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif DebugUtil.Assert(outHeaderSize + implOutDataTotalSize + OutObjectsCount * sizeof(int) <= outRawData.Length); - outRawData = outRawData.Slice(outHeaderSize); + outRawData = outRawData[outHeaderSize..]; _outObjectIdsOffset = (response.DataWords.Length * sizeof(uint) - outRawData.Length) + implOutDataTotalSize; return response; @@ -107,9 +107,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif DebugUtil.Assert(outHeaderSize + implOutDataTotalSize <= outRawData.Length); - outRawData = outRawData.Slice(outHeaderSize); + outRawData = outRawData[outHeaderSize..]; - _domain.UnreserveIds(new Span(_reservedObjectIds).Slice(0, OutObjectsCount)); + _domain.UnreserveIds(new Span(_reservedObjectIds)[..OutObjectsCount]); } public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span outObjects) @@ -129,7 +129,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif _domain.RegisterObject(objectIds[i], outObjects[i]); } - Span outObjectIds = MemoryMarshal.Cast(MemoryMarshal.Cast(response.DataWords).Slice(_outObjectIdsOffset)); + Span outObjectIds = MemoryMarshal.Cast(MemoryMarshal.Cast(response.DataWords)[_outObjectIdsOffset..]); for (int i = 0; i < outObjectsCount; i++) { diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs index 5af00077a..ad0e18244 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { struct PointerAndSize { - public static PointerAndSize Empty => new PointerAndSize(0UL, 0UL); + public static PointerAndSize Empty => new(0UL, 0UL); public ulong Address { get; } public ulong Size { get; } @@ -11,7 +11,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public PointerAndSize(ulong address, ulong size) { Address = address; - Size = size; + Size = size; } } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs index 18a404302..6a92e8d55 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs @@ -2,28 +2,29 @@ { struct ServerMessageRuntimeMetadata { - public ushort InDataSize { get; } - public ushort OutDataSize { get; } - public byte InHeadersSize { get; } - public byte OutHeadersSize { get; } - public byte InObjectsCount { get; } - public byte OutObjectsCount { get; } + public ushort InDataSize { get; } + public ushort OutDataSize { get; } + public byte InHeadersSize { get; } + public byte OutHeadersSize { get; } + public byte InObjectsCount { get; } + public byte OutObjectsCount { get; } + public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10; public ServerMessageRuntimeMetadata( ushort inDataSize, ushort outDataSize, - byte inHeadersSize, - byte outHeadersSize, - byte inObjectsCount, - byte outObjectsCount) + byte inHeadersSize, + byte outHeadersSize, + byte inObjectsCount, + byte outObjectsCount) { - InDataSize = inDataSize; - OutDataSize = outDataSize; - InHeadersSize = inHeadersSize; - OutHeadersSize = outHeadersSize; - InObjectsCount = inObjectsCount; + InDataSize = inDataSize; + OutDataSize = outDataSize; + InHeadersSize = inHeadersSize; + OutHeadersSize = outHeadersSize; + InObjectsCount = inObjectsCount; OutObjectsCount = outObjectsCount; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs index 3339a1a60..31be810d3 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs @@ -5,14 +5,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { ref struct ServiceDispatchContext { - public IServiceObject ServiceObject; - public ServerSessionManager Manager; - public ServerSession Session; + public IServiceObject ServiceObject; + public ServerSessionManager Manager; + public ServerSession Session; public ServerMessageProcessor Processor; - public HandlesToClose HandlesToClose; - public PointerAndSize PointerBuffer; - public ReadOnlySpan InMessageBuffer; - public Span OutMessageBuffer; - public HipcMessage Request; + public HandlesToClose HandlesToClose; + public PointerAndSize PointerBuffer; + public ReadOnlySpan InMessageBuffer; + public Span OutMessageBuffer; + public HipcMessage Request; } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs index 145c17839..21b342dff 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public ServiceDispatchTable(string objectName, IReadOnlyDictionary entries) { _objectName = objectName; - _entries = entries; + _entries = entries; } public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan inRawData) @@ -30,4 +30,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers()); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs index a0e28ca8f..816000675 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs @@ -39,17 +39,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif if (!entries.TryGetValue((int)commandId, out var commandHandler)) { - Logger.Warning?.Print(LogClass.KernelIpc, $"{objectName} command ID 0x{commandId:X} is not implemented"); - if (HorizonStatic.Options.IgnoreMissingServices) { // If ignore missing services is enabled, just pretend that everything is fine. - var response = PrepareForStubReply(ref context, out Span outRawData); + PrepareForStubReply(ref context, out Span outRawData); CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData); outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success }; + Logger.Warning?.Print(LogClass.Service, $"Missing service {objectName} (command ID: {commandId}) ignored"); + return Result.Success; } + else if (HorizonStatic.Options.ThrowOnInvalidCommandIds) + { + throw new NotImplementedException($"{objectName} command ID: {commandId} is not implemented"); + } return SfResult.UnknownCommandId; } @@ -72,6 +76,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif if (outHeader.IsEmpty) { commandResult.AbortOnSuccess(); + return commandResult; } @@ -80,11 +85,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return Result.Success; } - private static HipcMessageData PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span outRawData) + private static void PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span outRawData) { var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0); outRawData = MemoryMarshal.Cast(response.DataWords); - return response; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs index 8f367b4e0..47aedde96 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs @@ -20,37 +20,37 @@ namespace Ryujinx.Horizon.Sdk.Sf struct CommandArg { - public CommandArgType Type { get; } - public HipcBufferFlags BufferFlags { get; } - public ushort BufferFixedSize { get; } - public int ArgSize { get; } - public int ArgAlignment { get; } + public CommandArgType Type { get; } + public HipcBufferFlags BufferFlags { get; } + public ushort BufferFixedSize { get; } + public int ArgSize { get; } + public int ArgAlignment { get; } public CommandArg(CommandArgType type) { - Type = type; - BufferFlags = default; + Type = type; + BufferFlags = default; BufferFixedSize = 0; - ArgSize = 0; - ArgAlignment = 0; + ArgSize = 0; + ArgAlignment = 0; } public CommandArg(CommandArgType type, int argSize, int argAlignment) { - Type = type; - BufferFlags = default; + Type = type; + BufferFlags = default; BufferFixedSize = 0; - ArgSize = argSize; - ArgAlignment = argAlignment; + ArgSize = argSize; + ArgAlignment = argAlignment; } public CommandArg(HipcBufferFlags flags, ushort fixedSize = 0) { - Type = CommandArgType.Buffer; - BufferFlags = flags; + Type = CommandArgType.Buffer; + BufferFlags = flags; BufferFixedSize = fixedSize; - ArgSize = 0; - ArgAlignment = 0; + ArgSize = 0; + ArgAlignment = 0; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs index 5b7c302f2..294c7d58d 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Horizon.Sdk.Sf [AttributeUsage(AttributeTargets.Parameter)] class BufferAttribute : Attribute { - public HipcBufferFlags Flags { get; } - public ushort FixedSize { get; } + public HipcBufferFlags Flags { get; } + public ushort FixedSize { get; } public BufferAttribute(HipcBufferFlags flags) { @@ -16,7 +16,7 @@ namespace Ryujinx.Horizon.Sdk.Sf public BufferAttribute(HipcBufferFlags flags, ushort fixedSize) { - Flags = flags | HipcBufferFlags.FixedSize; + Flags = flags | HipcBufferFlags.FixedSize; FixedSize = fixedSize; } } @@ -35,4 +35,4 @@ namespace Ryujinx.Horizon.Sdk.Sf class MoveHandleAttribute : Attribute { } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs index ae42a8ef5..fe079d47d 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs @@ -9,20 +9,20 @@ namespace Ryujinx.Horizon.Sdk.Sf class CommandHandler { public delegate Result MethodInvoke( - ref ServiceDispatchContext context, - HipcCommandProcessor processor, + ref ServiceDispatchContext context, + HipcCommandProcessor processor, ServerMessageRuntimeMetadata runtimeMetadata, - ReadOnlySpan inRawData, - ref Span outHeader); + ReadOnlySpan inRawData, + ref Span outHeader); - private readonly MethodInvoke _invoke; + private readonly MethodInvoke _invoke; private readonly HipcCommandProcessor _processor; public string MethodName => _invoke.Method.Name; public CommandHandler(MethodInvoke invoke, params CommandArg[] args) { - _invoke = invoke; + _invoke = invoke; _processor = new HipcCommandProcessor(args); } @@ -37,8 +37,8 @@ namespace Ryujinx.Horizon.Sdk.Sf context.Processor.SetImplementationProcessor(_processor); } - var runtimeMetadata = context.Processor.GetRuntimeMetadata(); - Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); + var runtimeMetadata = context.Processor.GetRuntimeMetadata(); + Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); if (result.IsFailure) { @@ -50,8 +50,8 @@ namespace Ryujinx.Horizon.Sdk.Sf public static void GetCmifOutHeaderPointer(ref Span outHeader, ref Span outRawData) { - outHeader = MemoryMarshal.Cast(outRawData).Slice(0, 1); - outRawData = outRawData.Slice(Unsafe.SizeOf()); + outHeader = MemoryMarshal.Cast(outRawData)[..1]; + outRawData = outRawData[Unsafe.SizeOf()..]; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs index 9a3a511ad..feb2d666f 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs @@ -22,6 +22,7 @@ namespace Ryujinx.Horizon.Sdk.Sf public static ref T GetRef(PointerAndSize bufferRange) where T : unmanaged { var writableRegion = GetWritableRegion(bufferRange); + return ref MemoryMarshal.Cast(writableRegion.Memory.Span)[0]; } @@ -65,4 +66,4 @@ namespace Ryujinx.Horizon.Sdk.Sf response.MoveHandles[index] = value; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs index 594af2c84..269ab4fe5 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs @@ -5,12 +5,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc [Flags] enum HipcBufferFlags : byte { - In = 1 << 0, - Out = 1 << 1, - MapAlias = 1 << 2, - Pointer = 1 << 3, - FixedSize = 1 << 4, - AutoSelect = 1 << 5, + In = 1 << 0, + Out = 1 << 1, + MapAlias = 1 << 2, + Pointer = 1 << 3, + FixedSize = 1 << 4, + AutoSelect = 1 << 5, MapTransferAllowsNonSecure = 1 << 6, MapTransferAllowsNonDevice = 1 << 7 } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs index 4ef6374ba..b1e67253c 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs @@ -2,9 +2,9 @@ { enum HipcBufferMode { - Normal = 0, + Normal = 0, NonSecure = 1, - Invalid = 2, + Invalid = 2, NonDevice = 3 } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs index ea2ec6505..7541e2941 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { clientHandle = 0; - if (!(_session.ServiceObjectHolder.ServiceObject is DomainServiceObject domain)) + if (_session.ServiceObjectHolder.ServiceObject is not DomainServiceObject domain) { return HipcResult.TargetNotDomain; } @@ -112,4 +112,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return Result.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs index 3017f4041..6500d6cf7 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs @@ -10,9 +10,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { public const int AutoReceiveStatic = byte.MaxValue; - public HipcMetadata Meta; + public HipcMetadata Meta; public HipcMessageData Data; - public ulong Pid; + public ulong Pid; public HipcMessage(Span data) { @@ -20,10 +20,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc Header header = MemoryMarshal.Cast(data)[0]; - data = data.Slice(Unsafe.SizeOf

    ()); + data = data[Unsafe.SizeOf
    ()..]; - int receiveStaticsCount = 0; - ulong pid = 0; + int receiveStaticsCount = 0; + ulong pid = 0; if (header.ReceiveStaticMode != 0) { @@ -42,46 +42,44 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (header.HasSpecialHeader) { specialHeader = MemoryMarshal.Cast(data)[0]; - - data = data.Slice(Unsafe.SizeOf()); + data = data[Unsafe.SizeOf()..]; if (specialHeader.SendPid) { - pid = MemoryMarshal.Cast(data)[0]; - - data = data.Slice(sizeof(ulong)); + pid = MemoryMarshal.Cast(data)[0]; + data = data[sizeof(ulong)..]; } } Meta = new HipcMetadata() { - Type = (int)header.Type, - SendStaticsCount = header.SendStaticsCount, - SendBuffersCount = header.SendBuffersCount, - ReceiveBuffersCount = header.ReceiveBuffersCount, + Type = (int)header.Type, + SendStaticsCount = header.SendStaticsCount, + SendBuffersCount = header.SendBuffersCount, + ReceiveBuffersCount = header.ReceiveBuffersCount, ExchangeBuffersCount = header.ExchangeBuffersCount, - DataWordsCount = header.DataWordsCount, - ReceiveStaticsCount = receiveStaticsCount, - SendPid = specialHeader.SendPid, - CopyHandlesCount = specialHeader.CopyHandlesCount, - MoveHandlesCount = specialHeader.MoveHandlesCount + DataWordsCount = header.DataWordsCount, + ReceiveStaticsCount = receiveStaticsCount, + SendPid = specialHeader.SendPid, + CopyHandlesCount = specialHeader.CopyHandlesCount, + MoveHandlesCount = specialHeader.MoveHandlesCount }; Data = CreateMessageData(Meta, data, initialLength); - Pid = pid; + Pid = pid; } public static HipcMessageData WriteResponse( Span destination, - int sendStaticCount, - int dataWordsCount, - int copyHandlesCount, - int moveHandlesCount) + int sendStaticCount, + int dataWordsCount, + int copyHandlesCount, + int moveHandlesCount) { return WriteMessage(destination, new HipcMetadata() { SendStaticsCount = sendStaticCount, - DataWordsCount = dataWordsCount, + DataWordsCount = dataWordsCount, CopyHandlesCount = copyHandlesCount, MoveHandlesCount = moveHandlesCount }); @@ -89,38 +87,37 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public static HipcMessageData WriteMessage(Span destination, HipcMetadata meta) { - int initialLength = destination.Length; - + int initialLength = destination.Length; bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0; MemoryMarshal.Cast(destination)[0] = new Header() { - Type = (CommandType)meta.Type, - SendStaticsCount = meta.SendStaticsCount, - SendBuffersCount = meta.SendBuffersCount, - ReceiveBuffersCount = meta.ReceiveBuffersCount, + Type = (CommandType)meta.Type, + SendStaticsCount = meta.SendStaticsCount, + SendBuffersCount = meta.SendBuffersCount, + ReceiveBuffersCount = meta.ReceiveBuffersCount, ExchangeBuffersCount = meta.ExchangeBuffersCount, - DataWordsCount = meta.DataWordsCount, - ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0, - HasSpecialHeader = hasSpecialHeader + DataWordsCount = meta.DataWordsCount, + ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0, + HasSpecialHeader = hasSpecialHeader }; - destination = destination.Slice(Unsafe.SizeOf
    ()); + destination = destination[Unsafe.SizeOf
    ()..]; if (hasSpecialHeader) { MemoryMarshal.Cast(destination)[0] = new SpecialHeader() { - SendPid = meta.SendPid, + SendPid = meta.SendPid, CopyHandlesCount = meta.CopyHandlesCount, MoveHandlesCount = meta.MoveHandlesCount }; - destination = destination.Slice(Unsafe.SizeOf()); + destination = destination[Unsafe.SizeOf()..]; if (meta.SendPid) { - destination = destination.Slice(sizeof(ulong)); + destination = destination[sizeof(ulong)..]; } } @@ -133,68 +130,67 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (meta.CopyHandlesCount != 0) { - copyHandles = MemoryMarshal.Cast(data).Slice(0, meta.CopyHandlesCount); + copyHandles = MemoryMarshal.Cast(data)[..meta.CopyHandlesCount]; - data = data.Slice(meta.CopyHandlesCount * sizeof(int)); + data = data[(meta.CopyHandlesCount * sizeof(int))..]; } Span moveHandles = Span.Empty; if (meta.MoveHandlesCount != 0) { - moveHandles = MemoryMarshal.Cast(data).Slice(0, meta.MoveHandlesCount); + moveHandles = MemoryMarshal.Cast(data)[..meta.MoveHandlesCount]; - data = data.Slice(meta.MoveHandlesCount * sizeof(int)); + data = data[(meta.MoveHandlesCount * sizeof(int))..]; } Span sendStatics = Span.Empty; if (meta.SendStaticsCount != 0) { - sendStatics = MemoryMarshal.Cast(data).Slice(0, meta.SendStaticsCount); + sendStatics = MemoryMarshal.Cast(data)[..meta.SendStaticsCount]; - data = data.Slice(meta.SendStaticsCount * Unsafe.SizeOf()); + data = data[(meta.SendStaticsCount * Unsafe.SizeOf())..]; } Span sendBuffers = Span.Empty; if (meta.SendBuffersCount != 0) { - sendBuffers = MemoryMarshal.Cast(data).Slice(0, meta.SendBuffersCount); + sendBuffers = MemoryMarshal.Cast(data)[..meta.SendBuffersCount]; - data = data.Slice(meta.SendBuffersCount * Unsafe.SizeOf()); + data = data[(meta.SendBuffersCount * Unsafe.SizeOf())..]; } Span receiveBuffers = Span.Empty; if (meta.ReceiveBuffersCount != 0) { - receiveBuffers = MemoryMarshal.Cast(data).Slice(0, meta.ReceiveBuffersCount); + receiveBuffers = MemoryMarshal.Cast(data)[..meta.ReceiveBuffersCount]; - data = data.Slice(meta.ReceiveBuffersCount * Unsafe.SizeOf()); + data = data[(meta.ReceiveBuffersCount * Unsafe.SizeOf())..]; } Span exchangeBuffers = Span.Empty; if (meta.ExchangeBuffersCount != 0) { - exchangeBuffers = MemoryMarshal.Cast(data).Slice(0, meta.ExchangeBuffersCount); + exchangeBuffers = MemoryMarshal.Cast(data)[..meta.ExchangeBuffersCount]; - data = data.Slice(meta.ExchangeBuffersCount * Unsafe.SizeOf()); + data = data[(meta.ExchangeBuffersCount * Unsafe.SizeOf())..]; } Span dataWords = Span.Empty; if (meta.DataWordsCount != 0) { - int dataOffset = initialLength - data.Length; + int dataOffset = initialLength - data.Length; int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10); + int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); - int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); + dataWords = MemoryMarshal.Cast(data)[padding..meta.DataWordsCount]; - dataWords = MemoryMarshal.Cast(data).Slice(padding, meta.DataWordsCount - padding); - - data = data.Slice(meta.DataWordsCount * sizeof(uint)); + data = data[(meta.DataWordsCount * sizeof(uint))..]; } Span receiveList = Span.Empty; @@ -203,19 +199,19 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount; - receiveList = MemoryMarshal.Cast(data).Slice(0, receiveListSize); + receiveList = MemoryMarshal.Cast(data)[..receiveListSize]; } return new HipcMessageData() { - SendStatics = sendStatics, - SendBuffers = sendBuffers, - ReceiveBuffers = receiveBuffers, + SendStatics = sendStatics, + SendBuffers = sendBuffers, + ReceiveBuffers = receiveBuffers, ExchangeBuffers = exchangeBuffers, - DataWords = dataWords, - ReceiveList = receiveList, - CopyHandles = copyHandles, - MoveHandles = moveHandles + DataWords = dataWords, + ReceiveList = receiveList, + CopyHandles = copyHandles, + MoveHandles = moveHandles }; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs index c83c422ca..154b8f079 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs @@ -8,9 +8,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public Span SendBuffers; public Span ReceiveBuffers; public Span ExchangeBuffers; - public Span DataWords; + public Span DataWords; public Span ReceiveList; - public Span CopyHandles; - public Span MoveHandles; + public Span CopyHandles; + public Span MoveHandles; } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs index fe13137a6..10abc4006 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs @@ -2,15 +2,15 @@ { struct HipcMetadata { - public int Type; - public int SendStaticsCount; - public int SendBuffersCount; - public int ReceiveBuffersCount; - public int ExchangeBuffersCount; - public int DataWordsCount; - public int ReceiveStaticsCount; + public int Type; + public int SendStaticsCount; + public int SendBuffersCount; + public int ReceiveBuffersCount; + public int ExchangeBuffersCount; + public int DataWordsCount; + public int ReceiveStaticsCount; public bool SendPid; - public int CopyHandlesCount; - public int MoveHandlesCount; + public int CopyHandlesCount; + public int MoveHandlesCount; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs index 94bf09685..56cf16fb0 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs @@ -8,7 +8,7 @@ public HipcReceiveListEntry(ulong address, ulong size) { _addressLow = (uint)address; - _word1 = (ushort)(address >> 32) | (uint)(size << 16); + _word1 = (ushort)(address >> 32) | (uint)(size << 16); } } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs index ef989a986..3b483be81 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs @@ -6,17 +6,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { public const int ModuleId = 11; - public static Result OutOfSessionMemory => new Result(ModuleId, 102); - public static Result OutOfSessions => new Result(ModuleId, 131); - public static Result PointerBufferTooSmall => new Result(ModuleId, 141); - public static Result OutOfDomains => new Result(ModuleId, 200); - - public static Result InvalidRequestSize => new Result(ModuleId, 402); - public static Result UnknownCommandType => new Result(ModuleId, 403); - - public static Result InvalidCmifRequest => new Result(ModuleId, 420); - - public static Result TargetNotDomain => new Result(ModuleId, 491); - public static Result DomainObjectNotFound => new Result(ModuleId, 492); + public static Result OutOfSessionMemory => new(ModuleId, 102); + public static Result OutOfSessions => new(ModuleId, 131); + public static Result PointerBufferTooSmall => new(ModuleId, 141); + public static Result OutOfDomains => new(ModuleId, 200); + public static Result InvalidRequestSize => new(ModuleId, 402); + public static Result UnknownCommandType => new(ModuleId, 403); + public static Result InvalidCmifRequest => new(ModuleId, 420); + public static Result TargetNotDomain => new(ModuleId, 491); + public static Result DomainObjectNotFound => new(ModuleId, 492); } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs index 5cebf47c8..103820a6b 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs @@ -4,9 +4,9 @@ { private readonly ulong _data; - public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32); - public ushort Size => (ushort)(_data >> 16); - public int ReceiveIndex => (int)(_data & 0xf); + public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32); + public ushort Size => (ushort)(_data >> 16); + public int ReceiveIndex => (int)(_data & 0xf); public HipcStaticDescriptor(ulong address, ushort size, int receiveIndex) { @@ -19,4 +19,4 @@ _data = data; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs index e087cb223..b99d63c5e 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs @@ -2,19 +2,19 @@ { struct ManagerOptions { - public static ManagerOptions Default => new ManagerOptions(0, 0, 0, false); + public static ManagerOptions Default => new(0, 0, 0, false); - public int PointerBufferSize { get; } - public int MaxDomains { get; } - public int MaxDomainObjects { get; } + public int PointerBufferSize { get; } + public int MaxDomains { get; } + public int MaxDomainObjects { get; } public bool CanDeferInvokeRequest { get; } public ManagerOptions(int pointerBufferSize, int maxDomains, int maxDomainObjects, bool canDeferInvokeRequest) { - PointerBufferSize = pointerBufferSize; - MaxDomains = maxDomains; - MaxDomainObjects = maxDomainObjects; + PointerBufferSize = pointerBufferSize; + MaxDomains = maxDomains; + MaxDomainObjects = maxDomainObjects; CanDeferInvokeRequest = canDeferInvokeRequest; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs index 923f2d52d..bbbab8985 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs @@ -6,22 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { class Server : MultiWaitHolderOfHandle { - public int PortIndex { get; } - public int PortHandle { get; } - public ServiceName Name { get; } - public bool Managed { get; } + public int PortIndex { get; } + public int PortHandle { get; } + public ServiceName Name { get; } + public bool Managed { get; } public ServiceObjectHolder StaticObject { get; } public Server( - int portIndex, - int portHandle, - ServiceName name, - bool managed, + int portIndex, + int portHandle, + ServiceName name, + bool managed, ServiceObjectHolder staticHoder) : base(portHandle) { PortHandle = portHandle; - Name = name; - Managed = managed; + Name = name; + Managed = managed; if (staticHoder != null) { diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs index d920a6591..dda775397 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected override Result DispatchManagerRequest(ServerSession session, Span inMessage, Span outMessage) { - HipcManager hipcManager = new HipcManager(this, session); + HipcManager hipcManager = new(this, session); return DispatchRequest(new ServiceObjectHolder(hipcManager), session, inMessage, outMessage); } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs index 5bb2de25d..2ca9ceea2 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs @@ -80,7 +80,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return null; } - ServerSession session = new ServerSession(sessionIndex, sessionHandle, obj); + ServerSession session = new(sessionIndex, sessionHandle, obj); _sessions.Add(session); @@ -111,7 +111,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { lock (_resourceLock) { - Server server = new Server(portIndex, portHandle, name, managed, staticHoder); + Server server = new(portIndex, portHandle, name, managed, staticHoder); _servers.Add(server); diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs index 68cae6bc4..9d21290d8 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private enum UserDataTag { - Server = 1, + Server = 1, Session = 2 } @@ -36,16 +36,17 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc _canDeferInvokeRequest = options.CanDeferInvokeRequest; _multiWait = new MultiWait(); - _waitList = new MultiWait(); + _waitList = new MultiWait(); _multiWaitSelectionLock = new object(); - _waitListLock = new object(); + _waitListLock = new object(); _requestStopEvent = new Event(EventClearMode.ManualClear); - _notifyEvent = new Event(EventClearMode.ManualClear); + _notifyEvent = new Event(EventClearMode.ManualClear); _requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent); _multiWait.LinkMultiWaitHolder(_requestStopEventHolder); + _notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent); _multiWait.LinkMultiWaitHolder(_notifyEventHolder); } @@ -73,6 +74,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private void RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, int portHandle) { Server server = AllocateServer(portIndex, portHandle, ServiceName.Invalid, managed: false, staticHolder); + RegisterServerImpl(server); } @@ -86,6 +88,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } Server server = AllocateServer(portIndex, portHandle, name, managed: true, staticHolder); + RegisterServerImpl(server); return Result.Success; @@ -103,6 +106,11 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc throw new NotSupportedException(); } + protected Result AcceptImpl(Server server, IServiceObject obj) + { + return AcceptSession(server.PortHandle, new ServiceObjectHolder(obj)); + } + public void ServiceRequests() { while (WaitAndProcessRequestsImpl()); @@ -175,7 +183,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected override void RegisterSessionToWaitList(ServerSession session) { session.HasReceived = false; - session.UserData = UserDataTag.Session; + session.UserData = UserDataTag.Session; + RegisterToWaitList(session); } @@ -198,15 +207,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private Result Process(MultiWaitHolder holder) { - switch ((UserDataTag)holder.UserData) + return (UserDataTag)holder.UserData switch { - case UserDataTag.Server: - return ProcessForServer(holder); - case UserDataTag.Session: - return ProcessForSession(holder); - default: - throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()); - } + UserDataTag.Server => ProcessForServer(holder), + UserDataTag.Session => ProcessForSession(holder), + _ => throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()) + }; } private Result ProcessForServer(MultiWaitHolder holder) @@ -259,6 +265,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } session.HasReceived = true; + tlsMessage.Memory.Span.CopyTo(savedMessage.Memory.Span); } else diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs index eb98fefd0..a17300823 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs @@ -6,18 +6,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc class ServerSession : MultiWaitHolderOfHandle { public ServiceObjectHolder ServiceObjectHolder { get; set; } - public PointerAndSize PointerBuffer { get; set; } - public PointerAndSize SavedMessage { get; set; } - public int SessionIndex { get; } - public int SessionHandle { get; } - public bool IsClosed { get; set; } - public bool HasReceived { get; set; } + public PointerAndSize PointerBuffer { get; set; } + public PointerAndSize SavedMessage { get; set; } + public int SessionIndex { get; } + public int SessionHandle { get; } + public bool IsClosed { get; set; } + public bool HasReceived { get; set; } public ServerSession(int index, int handle, ServiceObjectHolder obj) : base(handle) { ServiceObjectHolder = obj; - SessionIndex = index; - SessionHandle = handle; + SessionIndex = index; + SessionHandle = handle; } } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs index e85892f26..6d3950813 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs @@ -75,9 +75,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } session.PointerBuffer = GetSessionPointerBuffer(session); - session.SavedMessage = GetSessionSavedMessageBuffer(session); + session.SavedMessage = GetSessionSavedMessageBuffer(session); RegisterSessionToWaitList(session); + return Result.Success; } @@ -109,10 +110,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } protected virtual Server AllocateServer( - int portIndex, - int portHandle, - ServiceName name, - bool managed, + int portIndex, + int portHandle, + ServiceName name, + bool managed, ServiceObjectHolder staticHoder) { throw new NotSupportedException(); @@ -141,6 +142,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected void CloseSessionImpl(ServerSession session) { int sessionHandle = session.Handle; + Os.FinalizeMultiWaitHolder(session); DestroySession(session); HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure(); @@ -156,6 +158,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close) { CloseSessionImpl(session); + return Result.Success; } else @@ -165,6 +168,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (result.IsSuccess) { RegisterSessionToWaitList(session); + return Result.Success; } else if (SfResult.RequestContextChanged(result)) @@ -176,6 +180,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}"); CloseSessionImpl(session); + return Result.Success; } } @@ -197,8 +202,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return DispatchManagerRequest(session, inMessage, outMessage); default: return HipcResult.UnknownCommandType; - } } + } private static int GetInlineContext(CommandType commandType, ReadOnlySpan inMessage) { @@ -231,7 +236,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata() { - Type = (int)CommandType.Invalid, + Type = (int)CommandType.Invalid, ReceiveStaticsCount = HipcMessage.AutoReceiveStatic }); @@ -271,9 +276,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected virtual Result DispatchRequest( ServiceObjectHolder objectHolder, - ServerSession session, - Span inMessage, - Span outMessage) + ServerSession session, + Span inMessage, + Span outMessage) { HipcMessage request; @@ -288,14 +293,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc var dispatchCtx = new ServiceDispatchContext() { - ServiceObject = objectHolder.ServiceObject, - Manager = this, - Session = session, - HandlesToClose = new HandlesToClose(), - PointerBuffer = session.PointerBuffer, - InMessageBuffer = inMessage, + ServiceObject = objectHolder.ServiceObject, + Manager = this, + Session = session, + HandlesToClose = new HandlesToClose(), + PointerBuffer = session.PointerBuffer, + InMessageBuffer = inMessage, OutMessageBuffer = outMessage, - Request = request + Request = request }; ReadOnlySpan inRawData = MemoryMarshal.Cast(dispatchCtx.Request.Data.DataWords); @@ -332,4 +337,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return this; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs index 53202edee..6bba49ae0 100644 --- a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs +++ b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs @@ -136,9 +136,9 @@ namespace Ryujinx.Horizon.Sdk.Sf ulong pointerBufferTail = context.PointerBuffer.Address; ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size; - int sendMapAliasIndex = 0; - int recvMapAliasIndex = 0; - int sendPointerIndex = 0; + int sendMapAliasIndex = 0; + int recvMapAliasIndex = 0; + int sendPointerIndex = 0; int unfixedRecvPointerIndex = 0; for (int i = 0; i < _args.Length; i++) @@ -188,8 +188,9 @@ namespace Ryujinx.Horizon.Sdk.Sf if (flags.HasFlag(HipcBufferFlags.In)) { var descriptor = context.Request.Data.SendStatics[sendPointerIndex++]; - ulong address = descriptor.Address; - ulong size = descriptor.Size; + ulong address = descriptor.Address; + ulong size = descriptor.Size; + _bufferRanges[i] = new PointerAndSize(address, size); if (size != 0) @@ -207,13 +208,14 @@ namespace Ryujinx.Horizon.Sdk.Sf } else { - var data = MemoryMarshal.Cast(context.Request.Data.DataWords); - var recvPointerSizes = MemoryMarshal.Cast(data.Slice(runtimeMetadata.UnfixedOutPointerSizeOffset)); + var data = MemoryMarshal.Cast(context.Request.Data.DataWords); + var recvPointerSizes = MemoryMarshal.Cast(data[runtimeMetadata.UnfixedOutPointerSizeOffset..]); + size = recvPointerSizes[unfixedRecvPointerIndex++]; } pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL); - _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); + _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); } } } @@ -304,16 +306,17 @@ namespace Ryujinx.Horizon.Sdk.Sf { ref var meta = ref context.Request.Meta; bool requestValid = true; - requestValid &= meta.SendPid == _hasInProcessIdHolder; - requestValid &= meta.SendStaticsCount == _inPointerBuffersCount; - requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount; - requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount; + requestValid &= meta.SendPid == _hasInProcessIdHolder; + requestValid &= meta.SendStaticsCount == _inPointerBuffersCount; + requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount; + requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount; requestValid &= meta.ExchangeBuffersCount == 0; - requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount; - requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount; + requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount; + requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount; int rawSizeInBytes = meta.DataWordsCount * sizeof(uint); int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint)); + requestValid &= rawSizeInBytes >= commandRawSize; return requestValid ? Result.Success : HipcResult.InvalidCmifRequest; @@ -340,7 +343,7 @@ namespace Ryujinx.Horizon.Sdk.Sf { if (_args[i].Type == CommandArgType.InObject) { - int index = inObjectIndex++; + int index = inObjectIndex++; var inObject = inObjects[index]; objects[index] = inObject?.ServiceObject; @@ -365,6 +368,7 @@ namespace Ryujinx.Horizon.Sdk.Sf _outCopyHandlesCount, _outMoveHandlesCount + runtimeMetadata.OutObjectsCount); outRawData = MemoryMarshal.Cast(response.DataWords); + return response; } @@ -377,6 +381,7 @@ namespace Ryujinx.Horizon.Sdk.Sf (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint), 0, 0); + outRawData = MemoryMarshal.Cast(response.DataWords); } @@ -410,6 +415,7 @@ namespace Ryujinx.Horizon.Sdk.Sf if (obj == null) { response.MoveHandles[index] = 0; + return; } @@ -418,4 +424,4 @@ namespace Ryujinx.Horizon.Sdk.Sf response.MoveHandles[index] = clientHandle; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs index 982f454f0..10e4f9094 100644 --- a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs +++ b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs @@ -12,24 +12,22 @@ namespace Ryujinx.Horizon.Sdk.Sf { int argsCount = args.Length; - int[] sizes = new int[argsCount]; + int[] sizes = new int[argsCount]; int[] aligns = new int[argsCount]; - int[] map = new int[argsCount]; + int[] map = new int[argsCount]; for (int i = 0; i < argsCount; i++) { - sizes[i] = args[i].ArgSize; + sizes[i] = args[i].ArgSize; aligns[i] = args[i].ArgAlignment; - map[i] = i; + map[i] = i; } for (int i = 1; i < argsCount; i++) { for (int j = i; j > 0 && aligns[map[j - 1]] > aligns[map[j]]; j--) { - var temp = map[j - 1]; - map[j - 1] = map[j]; - map[j] = temp; + (map[j], map[j - 1]) = (map[j - 1], map[j]); } } @@ -37,9 +35,9 @@ namespace Ryujinx.Horizon.Sdk.Sf foreach (int i in map) { - offset = BitUtils.AlignUp(offset, aligns[i]); + offset = BitUtils.AlignUp(offset, aligns[i]); offsets[i] = offset; - offset += sizes[i]; + offset += sizes[i]; } offsets[argsCount] = offset; @@ -48,4 +46,4 @@ namespace Ryujinx.Horizon.Sdk.Sf return offsets; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs index 6aa11ba5f..72502d17e 100644 --- a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs +++ b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs @@ -6,26 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf { public const int ModuleId = 10; - public static Result NotSupported => new Result(ModuleId, 1); - public static Result InvalidHeaderSize => new Result(ModuleId, 202); - public static Result InvalidInHeader => new Result(ModuleId, 211); - public static Result InvalidOutHeader => new Result(ModuleId, 212); - public static Result UnknownCommandId => new Result(ModuleId, 221); - public static Result InvalidOutRawSize => new Result(ModuleId, 232); - public static Result InvalidInObjectsCount => new Result(ModuleId, 235); - public static Result InvalidOutObjectsCount => new Result(ModuleId, 236); - public static Result InvalidInObject => new Result(ModuleId, 239); - - public static Result TargetNotFound => new Result(ModuleId, 261); - - public static Result OutOfDomainEntries => new Result(ModuleId, 301); - - public static Result InvalidatedByUser => new Result(ModuleId, 802); - public static Result RequestDeferredByUser => new Result(ModuleId, 812); + public static Result NotSupported => new(ModuleId, 1); + public static Result InvalidHeaderSize => new(ModuleId, 202); + public static Result InvalidInHeader => new(ModuleId, 211); + public static Result InvalidOutHeader => new(ModuleId, 212); + public static Result UnknownCommandId => new(ModuleId, 221); + public static Result InvalidOutRawSize => new(ModuleId, 232); + public static Result InvalidInObjectsCount => new(ModuleId, 235); + public static Result InvalidOutObjectsCount => new(ModuleId, 236); + public static Result InvalidInObject => new(ModuleId, 239); + public static Result TargetNotFound => new(ModuleId, 261); + public static Result OutOfDomainEntries => new(ModuleId, 301); + public static Result InvalidatedByUser => new(ModuleId, 802); + public static Result RequestDeferredByUser => new(ModuleId, 812); public static bool RequestContextChanged(Result result) => result.InRange(800, 899); - public static bool Invalidated(Result result) => result.InRange(801, 809); - - public static bool RequestDeferred(Result result) => result.InRange(811, 819); + public static bool Invalidated(Result result) => result.InRange(801, 809); + public static bool RequestDeferred(Result result) => result.InRange(811, 819); } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs b/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs new file mode 100644 index 000000000..644285834 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs @@ -0,0 +1,8 @@ +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Sm +{ + interface IManagerService : IServiceObject + { + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sm/IUserService.cs b/Ryujinx.Horizon/Sdk/Sm/IUserService.cs new file mode 100644 index 000000000..ad9bc9d7b --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sm/IUserService.cs @@ -0,0 +1,13 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Sm +{ + interface IUserService : IServiceObject + { + Result Initialize(ulong clientProcessId); + Result GetService(out int handle, ServiceName name); + Result RegisterService(out int handle, ServiceName name, int maxSessions, bool isLight); + Result UnregisterService(ServiceName name); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs index e4b0eea1c..533e68d9c 100644 --- a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs +++ b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs @@ -7,16 +7,18 @@ namespace Ryujinx.Horizon.Sdk.Sm { class SmApi { + private const string SmName = "sm:"; + private int _portHandle; public Result Initialize() { - Result result = HorizonStatic.Syscall.ConnectToNamedPort(out int portHandle, "sm:"); + Result result = HorizonStatic.Syscall.ConnectToNamedPort(out int portHandle, SmName); while (result == KernelResult.NotFound) { HorizonStatic.Syscall.SleepThread(50000000L); - result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, "sm:"); + result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, SmName); } if (result.IsFailure) @@ -33,7 +35,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(0UL); @@ -44,7 +46,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(name); @@ -53,10 +55,12 @@ namespace Ryujinx.Horizon.Sdk.Sm if (result.IsFailure) { handle = 0; + return result; } handle = response.MoveHandles[0]; + return Result.Success; } @@ -64,7 +68,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[16]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(name); writer.Write(isLight ? 1 : 0); @@ -75,10 +79,12 @@ namespace Ryujinx.Horizon.Sdk.Sm if (result.IsFailure) { handle = 0; + return result; } handle = response.MoveHandles[0]; + return Result.Success; } @@ -86,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(name); @@ -97,11 +103,11 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(0UL); return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/ServiceEntry.cs b/Ryujinx.Horizon/ServiceEntry.cs index 3fea46c19..06152d9ff 100644 --- a/Ryujinx.Horizon/ServiceEntry.cs +++ b/Ryujinx.Horizon/ServiceEntry.cs @@ -6,20 +6,22 @@ namespace Ryujinx.Horizon { public struct ServiceEntry { - private readonly Action _entrypoint; - private readonly HorizonOptions _options; + private readonly Action _entrypoint; + private readonly ServiceTable _serviceTable; + private readonly HorizonOptions _options; - internal ServiceEntry(Action entrypoint, HorizonOptions options) + internal ServiceEntry(Action entrypoint, ServiceTable serviceTable, HorizonOptions options) { - _entrypoint = entrypoint; - _options = options; + _entrypoint = entrypoint; + _serviceTable = serviceTable; + _options = options; } public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext) { HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext, (int)threadContext.GetX(1)); - _entrypoint(); + _entrypoint(_serviceTable); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/ServiceTable.cs b/Ryujinx.Horizon/ServiceTable.cs index 933b6a59b..2edc6ea10 100644 --- a/Ryujinx.Horizon/ServiceTable.cs +++ b/Ryujinx.Horizon/ServiceTable.cs @@ -1,22 +1,58 @@ using Ryujinx.Horizon.LogManager; +using Ryujinx.Horizon.Prepo; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.Horizon { - public static class ServiceTable + public class ServiceTable { - public static IEnumerable GetServices(HorizonOptions options) + private int _readyServices; + private int _totalServices; + + private readonly ManualResetEvent _servicesReadyEvent = new(false); + + public IEnumerable GetServices(HorizonOptions options) { - List entries = new List(); + List entries = new(); void RegisterService() where T : IService { - entries.Add(new ServiceEntry(T.Main, options)); + entries.Add(new ServiceEntry(T.Main, this, options)); } RegisterService(); + RegisterService(); + + _totalServices = entries.Count; return entries; } + + internal void SignalServiceReady() + { + if (Interlocked.Increment(ref _readyServices) == _totalServices) + { + _servicesReadyEvent.Set(); + } + } + + public void WaitServicesReady() + { + _servicesReadyEvent.WaitOne(); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _servicesReadyEvent.Dispose(); + } + } + + public void Dispose() + { + Dispose(true); + } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs index fed420aa8..50c18a2c9 100644 --- a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs +++ b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs @@ -5,16 +5,16 @@ namespace Ryujinx.Horizon.Sm.Impl struct ServiceInfo { public ServiceName Name; - public ulong OwnerProcessId; - public int PortHandle; + public ulong OwnerProcessId; + public int PortHandle; public void Free() { HorizonStatic.Syscall.CloseHandle(PortHandle); - Name = ServiceName.Invalid; + Name = ServiceName.Invalid; OwnerProcessId = 0L; - PortHandle = 0; + PortHandle = 0; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs index cdf2d17f1..44a1ec46e 100644 --- a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs +++ b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs @@ -107,8 +107,8 @@ namespace Ryujinx.Horizon.Sm.Impl return result; } - freeService.PortHandle = clientPort; - freeService.Name = name; + freeService.PortHandle = clientPort; + freeService.Name = name; freeService.OwnerProcessId = processId; return Result.Success; @@ -126,20 +126,19 @@ namespace Ryujinx.Horizon.Sm.Impl // TODO: Validation with GetProcessInfo etc. int serviceIndex = GetServiceInfo(name); - if (serviceIndex < 0) { return SmResult.NotRegistered; } ref var serviceInfo = ref _services[serviceIndex]; - if (serviceInfo.OwnerProcessId != processId) { return SmResult.NotAllowed; } serviceInfo.Free(); + return Result.Success; } @@ -194,4 +193,4 @@ namespace Ryujinx.Horizon.Sm.Impl return -1; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs b/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs new file mode 100644 index 000000000..c7dcddc91 --- /dev/null +++ b/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs @@ -0,0 +1,8 @@ +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sm.Ipc +{ + partial class ManagerService : IManagerService + { + } +} diff --git a/Ryujinx.Horizon/Sm/UserService.cs b/Ryujinx.Horizon/Sm/Ipc/UserService.cs similarity index 91% rename from Ryujinx.Horizon/Sm/UserService.cs rename to Ryujinx.Horizon/Sm/Ipc/UserService.cs index d3b4537bf..d093913a9 100644 --- a/Ryujinx.Horizon/Sm/UserService.cs +++ b/Ryujinx.Horizon/Sm/Ipc/UserService.cs @@ -3,14 +3,14 @@ using Ryujinx.Horizon.Sdk.Sf; using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sm.Impl; -namespace Ryujinx.Horizon.Sm +namespace Ryujinx.Horizon.Sm.Ipc { - partial class UserService : IServiceObject + partial class UserService : IUserService { private readonly ServiceManager _serviceManager; private ulong _clientProcessId; - private bool _initialized; + private bool _initialized; public UserService(ServiceManager serviceManager) { @@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sm public Result Initialize([ClientProcessId] ulong clientProcessId) { _clientProcessId = clientProcessId; - _initialized = true; + _initialized = true; return Result.Success; } @@ -63,4 +63,4 @@ namespace Ryujinx.Horizon.Sm return _serviceManager.UnregisterService(_clientProcessId, name); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/ManagerService.cs b/Ryujinx.Horizon/Sm/ManagerService.cs deleted file mode 100644 index 1719dcfd5..000000000 --- a/Ryujinx.Horizon/Sm/ManagerService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Ryujinx.Horizon.Sdk.Sf; - -namespace Ryujinx.Horizon.Sm -{ - partial class ManagerService : IServiceObject - { - } -} diff --git a/Ryujinx.Horizon/Sm/SmMain.cs b/Ryujinx.Horizon/Sm/SmMain.cs index 8c37bece5..5656d464f 100644 --- a/Ryujinx.Horizon/Sm/SmMain.cs +++ b/Ryujinx.Horizon/Sm/SmMain.cs @@ -1,30 +1,34 @@ -using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Prepo; +using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sm.Impl; +using Ryujinx.Horizon.Sm.Types; namespace Ryujinx.Horizon.Sm { public class SmMain { - private enum PortIndex - { - User, - Manager - } + private const int SmMaxSessionsCount = 64; + private const int SmmMaxSessionsCount = 1; + private const int SmTotalMaxSessionsCount = SmMaxSessionsCount + SmmMaxSessionsCount; private const int MaxPortsCount = 2; - private readonly ServerManager _serverManager = new ServerManager(null, null, MaxPortsCount, ManagerOptions.Default, 0); - private readonly ServiceManager _serviceManager = new ServiceManager(); + private SmServerManager _serverManager; + + private readonly ServiceManager _serviceManager = new(); public void Main() { - HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", 64).AbortOnFailure(); + HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", SmMaxSessionsCount).AbortOnFailure(); - _serverManager.RegisterServer((int)PortIndex.User, smHandle); - _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), 1).AbortOnFailure(); - _serverManager.RegisterServer((int)PortIndex.Manager, smmHandle); + _serverManager = new SmServerManager(_serviceManager, null, null, MaxPortsCount, ManagerOptions.Default, SmTotalMaxSessionsCount); + + _serverManager.RegisterServer((int)SmPortIndex.User, smHandle); + _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), SmmMaxSessionsCount).AbortOnFailure(); + _serverManager.RegisterServer((int)SmPortIndex.Manager, smmHandle); _serverManager.ServiceRequests(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/SmResult.cs b/Ryujinx.Horizon/Sm/SmResult.cs index 3063445dc..2d503a4f8 100644 --- a/Ryujinx.Horizon/Sm/SmResult.cs +++ b/Ryujinx.Horizon/Sm/SmResult.cs @@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.Sm { private const int ModuleId = 21; - public static Result OutOfProcess => new Result(ModuleId, 1); - public static Result InvalidClient => new Result(ModuleId, 2); - public static Result OutOfSessions => new Result(ModuleId, 3); - public static Result AlreadyRegistered => new Result(ModuleId, 4); - public static Result OutOfServices => new Result(ModuleId, 5); - public static Result InvalidServiceName => new Result(ModuleId, 6); - public static Result NotRegistered => new Result(ModuleId, 7); - public static Result NotAllowed => new Result(ModuleId, 8); - public static Result TooLargeAccessControl => new Result(ModuleId, 9); + public static Result OutOfProcess => new(ModuleId, 1); + public static Result InvalidClient => new(ModuleId, 2); + public static Result OutOfSessions => new(ModuleId, 3); + public static Result AlreadyRegistered => new(ModuleId, 4); + public static Result OutOfServices => new(ModuleId, 5); + public static Result InvalidServiceName => new(ModuleId, 6); + public static Result NotRegistered => new(ModuleId, 7); + public static Result NotAllowed => new(ModuleId, 8); + public static Result TooLargeAccessControl => new(ModuleId, 9); } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/SmServerManager.cs b/Ryujinx.Horizon/Sm/SmServerManager.cs new file mode 100644 index 000000000..dc8dc5b67 --- /dev/null +++ b/Ryujinx.Horizon/Sm/SmServerManager.cs @@ -0,0 +1,30 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm.Impl; +using Ryujinx.Horizon.Sm.Ipc; +using Ryujinx.Horizon.Sm.Types; +using System; + +namespace Ryujinx.Horizon.Sm +{ + class SmServerManager : ServerManager + { + private readonly ServiceManager _serviceManager; + + public SmServerManager(ServiceManager serviceManager, HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) + { + _serviceManager = serviceManager; + } + + protected override Result OnNeedsToAccept(int portIndex, Server server) + { + return (SmPortIndex)portIndex switch + { + SmPortIndex.User => AcceptImpl(server, new UserService(_serviceManager)), + SmPortIndex.Manager => AcceptImpl(server, new ManagerService()), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs b/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs new file mode 100644 index 000000000..5325558b8 --- /dev/null +++ b/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Horizon.Sm.Types +{ + enum SmPortIndex + { + User, + Manager + } +} \ No newline at end of file From eeb2af9953f48479c3a902664f31634e6a2148be Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Sun, 8 Jan 2023 12:46:25 -0500 Subject: [PATCH 250/737] Ava GUI: `MainWindow` Refactor (#4178) * Fix redundancies * Add back elses * `MainWindow` Refactor * Switch commands to `ReflectionBinding` Not required in Ava 11 * Update Ryujinx.Ava/AppHost.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/AppHost.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/AppHost.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/AppHost.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/AppHost.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/AppHost.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs Co-authored-by: Ac_K * Resolve issues * Remove Ava 11 Fix * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: gdkchan * Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs Co-authored-by: gdkchan * Fix whitespace + other suggestions * Move Vsync colours to `Styles.xaml` * Remove catch all * Use `switch` instead of `if` * Update locale keys * Use block-scoped namespaces * Fix improper Ava api usage then * Static PTC * Fix `GridItemSelectorSize` with `ShowNames` * Update for new About Window * Add back search fix Co-authored-by: Ac_K Co-authored-by: gdkchan --- Ryujinx.Ava/AppHost.cs | 205 ++- Ryujinx.Ava/Assets/Styles/Styles.xaml | 2 + Ryujinx.Ava/Input/AvaloniaMouseDriver.cs | 10 +- Ryujinx.Ava/Modules/Updater/Updater.cs | 4 +- Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs | 4 +- .../Applet/AvaloniaDynamicTextInputHandler.cs | 2 +- Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs | 4 +- .../ViewModels/ControllerSettingsViewModel.cs | 8 +- .../UI/ViewModels/MainWindowViewModel.cs | 1594 +++++++++++------ .../UI/Views/Main/MainMenuBarView.axaml | 216 +++ .../UI/Views/Main/MainMenuBarView.axaml.cs | 146 ++ .../UI/Views/Main/MainStatusBarView.axaml | 232 +++ .../UI/Views/Main/MainStatusBarView.axaml.cs | 52 + .../UI/Views/Main/MainViewControls.axaml | 176 ++ .../UI/Views/Main/MainViewControls.axaml.cs | 54 + Ryujinx.Ava/UI/Windows/MainWindow.axaml | 606 +------ Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 535 ++---- 17 files changed, 2153 insertions(+), 1697 deletions(-) create mode 100644 Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml create mode 100644 Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml create mode 100644 Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 2cf53ef69..5a0910e87 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -1,4 +1,6 @@ using ARMeilleure.Translation; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Threading; using LibHac.Tools.FsSystem; @@ -13,6 +15,7 @@ using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -42,7 +45,7 @@ using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; - +using Image = SixLabors.ImageSharp.Image; using InputManager = Ryujinx.Input.HLE.InputManager; using Key = Ryujinx.Input.Key; using MouseButton = Ryujinx.Input.MouseButton; @@ -67,7 +70,7 @@ namespace Ryujinx.Ava private readonly AccountManager _accountManager; private readonly UserChannelPersistence _userChannelPersistence; private readonly InputManager _inputManager; - private readonly MainWindow _parent; + private readonly MainWindowViewModel _viewModel; private readonly IKeyboard _keyboardInterface; private readonly GraphicsDebugLevel _glLogLevel; @@ -110,6 +113,7 @@ namespace Ryujinx.Ava public bool ScreenshotRequested { get; set; } private object _lockObject = new(); + private TopLevel _topLevel; public AppHost( RendererHost renderer, @@ -119,9 +123,10 @@ namespace Ryujinx.Ava ContentManager contentManager, AccountManager accountManager, UserChannelPersistence userChannelPersistence, - MainWindow parent) + MainWindowViewModel viewmodel, + TopLevel topLevel) { - _parent = parent; + _viewModel = viewmodel; _inputManager = inputManager; _accountManager = accountManager; _userChannelPersistence = userChannelPersistence; @@ -129,7 +134,8 @@ namespace Ryujinx.Ava _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; _lastCursorMoveTime = Stopwatch.GetTimestamp(); _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; - _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_parent, renderer)); + _topLevel = topLevel; + _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); NpadManager = _inputManager.CreateNpadManager(); @@ -144,15 +150,15 @@ namespace Ryujinx.Ava if (ApplicationPath.StartsWith("@SystemContent")) { - ApplicationPath = _parent.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); + ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); _isFirmwareTitle = true; } ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorState_Changed; - _parent.PointerLeave += Parent_PointerLeft; - _parent.PointerMoved += Parent_PointerMoved; + _topLevel.PointerLeave += TopLevel_PointerLeave; + _topLevel.PointerMoved += TopLevel_PointerMoved; ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; @@ -162,25 +168,27 @@ namespace Ryujinx.Ava _gpuCancellationTokenSource = new CancellationTokenSource(); } - private void Parent_PointerMoved(object sender, PointerEventArgs e) + private void TopLevel_PointerMoved(object sender, PointerEventArgs e) { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - var p = e.GetCurrentPoint(_parent).Position; - var r = _parent.InputHitTest(p); - _isMouseInRenderer = r == Renderer; + if (sender is Control visual) + { + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + var point = e.GetCurrentPoint(visual).Position; + _isMouseInRenderer = Equals(visual.InputHitTest(point), Renderer); + } } - private void Parent_PointerLeft(object sender, PointerEventArgs e) + private void TopLevel_PointerLeave(object sender, PointerEventArgs e) { _isMouseInRenderer = false; - _parent.Cursor = Cursor.Default; + _viewModel.Cursor = Cursor.Default; } private void SetRendererWindowSize(Size size) { if (_renderer != null) { - double scale = _parent.PlatformImpl.RenderScaling; + double scale = _topLevel.PlatformImpl.RenderScaling; _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); } } @@ -256,7 +264,7 @@ namespace Ryujinx.Ava NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); TouchScreenManager.Initialize(Device); - _parent.ViewModel.IsGameRunning = true; + _viewModel.IsGameRunning = true; string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty @@ -276,10 +284,10 @@ namespace Ryujinx.Ava Dispatcher.UIThread.InvokeAsync(() => { - _parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; + _viewModel.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); - _parent.ViewModel.SetUiProgressHandlers(Device); + _viewModel.SetUIProgressHandlers(Device); Renderer.SizeChanged += Window_SizeChanged; @@ -287,7 +295,7 @@ namespace Ryujinx.Ava _renderingThread.Start(); - _parent.ViewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; + _viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; MainLoop(); @@ -321,7 +329,7 @@ namespace Ryujinx.Ava Dispatcher.UIThread.Post(() => { var value = e.NewValue; - _parent.ViewModel.Volume = e.NewValue; + _viewModel.Volume = e.NewValue; }); } @@ -369,7 +377,7 @@ namespace Ryujinx.Ava { if (Device.Application != null) { - _parent.UpdateGameMetadata(Device.Application.TitleIdText); + _viewModel.UpdateGameMetadata(Device.Application.TitleIdText); } ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; @@ -377,6 +385,9 @@ namespace Ryujinx.Ava ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; + _topLevel.PointerLeave -= TopLevel_PointerLeave; + _topLevel.PointerMoved -= TopLevel_PointerMoved; + _gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Dispose(); @@ -410,7 +421,7 @@ namespace Ryujinx.Ava } else { - _parent.Cursor = Cursor.Default; + _viewModel.Cursor = Cursor.Default; } }); } @@ -422,57 +433,65 @@ namespace Ryujinx.Ava SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) + if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) { - if (userError == UserError.NoFirmware) { - UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], - string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - ""); - - if (result != UserResult.Yes) + if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) { - await UserErrorDialog.ShowUserErrorDialog(userError, _parent); + if (userError == UserError.NoFirmware) + { + UserResult result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], + firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + ""); + + if (result != UserResult.Yes) + { + await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); + Device.Dispose(); + + return false; + } + } + + if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) + { + await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); + Device.Dispose(); + + return false; + } + + // Tell the user that we installed a firmware for them. + if (userError == UserError.NoFirmware) + { + firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); + + _viewModel.RefreshFirmwareStatus(); + + await ContentDialogHelper.CreateInfoDialog( + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], + firmwareVersion.VersionString), + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], + firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogOk], + "", + LocaleManager.Instance[LocaleKeys.RyujinxInfo]); + } + } + else + { + await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); Device.Dispose(); return false; } } - - if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) - { - await UserErrorDialog.ShowUserErrorDialog(userError, _parent); - Device.Dispose(); - - return false; - } - - // Tell the user that we installed a firmware for them. - if (userError == UserError.NoFirmware) - { - firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - - _parent.RefreshFirmwareStatus(); - - await ContentDialogHelper.CreateInfoDialog( - string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString), - string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogOk], - "", - LocaleManager.Instance[LocaleKeys.RyujinxInfo]); - } - } - else - { - await UserErrorDialog.ShowUserErrorDialog(userError, _parent); - Device.Dispose(); - - return false; } } @@ -567,7 +586,7 @@ namespace Ryujinx.Ava DiscordIntegrationModule.SwitchToPlayingState(Device.Application.TitleIdText, Device.Application.TitleName); - _parent.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata => + _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow.ToString(); }); @@ -578,13 +597,13 @@ namespace Ryujinx.Ava internal void Resume() { Device?.System.TogglePauseEmulation(false); - _parent.ViewModel.IsPaused = false; + _viewModel.IsPaused = false; } internal void Pause() { Device?.System.TogglePauseEmulation(true); - _parent.ViewModel.IsPaused = true; + _viewModel.IsPaused = true; } private void InitializeSwitchInstance() @@ -632,7 +651,7 @@ namespace Ryujinx.Ava Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; - MainWindow.SaveConfig(); + MainWindowViewModel.SaveConfig(); deviceDriver = new OpenALHardwareDeviceDriver(); } @@ -645,7 +664,7 @@ namespace Ryujinx.Ava Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; - MainWindow.SaveConfig(); + MainWindowViewModel.SaveConfig(); deviceDriver = new SoundIoHardwareDeviceDriver(); } @@ -671,7 +690,7 @@ namespace Ryujinx.Ava Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; - MainWindow.SaveConfig(); + MainWindowViewModel.SaveConfig(); deviceDriver = new SDL2HardwareDeviceDriver(); } @@ -684,7 +703,7 @@ namespace Ryujinx.Ava Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; - MainWindow.SaveConfig(); + MainWindowViewModel.SaveConfig(); deviceDriver = new OpenALHardwareDeviceDriver(); } @@ -710,7 +729,7 @@ namespace Ryujinx.Ava Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; - MainWindow.SaveConfig(); + MainWindowViewModel.SaveConfig(); deviceDriver = new SDL2HardwareDeviceDriver(); } @@ -723,7 +742,7 @@ namespace Ryujinx.Ava Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; - MainWindow.SaveConfig(); + MainWindowViewModel.SaveConfig(); deviceDriver = new SoundIoHardwareDeviceDriver(); } @@ -740,14 +759,14 @@ namespace Ryujinx.Ava IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(VirtualFileSystem, - _parent.LibHacHorizonManager, + _viewModel.LibHacHorizonManager, ContentManager, _accountManager, _userChannelPersistence, renderer, deviceDriver, memoryConfiguration, - _parent.UiHandler, + _viewModel.UiHandler, (SystemLanguage)ConfigurationState.Instance.System.Language.Value, (RegionCode)ConfigurationState.Instance.System.Region.Value, ConfigurationState.Instance.Graphics.EnableVsync, @@ -788,14 +807,14 @@ namespace Ryujinx.Ava { Dispatcher.UIThread.InvokeAsync(() => { - if (_parent.ViewModel.StartGamesInFullscreen) + if (_viewModel.StartGamesInFullscreen) { - _parent.WindowState = WindowState.FullScreen; + _viewModel.WindowState = WindowState.FullScreen; } - if (_parent.WindowState == WindowState.FullScreen) + if (_viewModel.WindowState == WindowState.FullScreen) { - _parent.ViewModel.ShowMenuAndStatusBar = false; + _viewModel.ShowMenuAndStatusBar = false; } }); @@ -819,7 +838,7 @@ namespace Ryujinx.Ava Width = (int)Renderer.Bounds.Width; Height = (int)Renderer.Bounds.Height; - _renderer.Window.SetSize((int)(Width * _parent.PlatformImpl.RenderScaling), (int)(Height * _parent.PlatformImpl.RenderScaling)); + _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling)); _chrono.Start(); @@ -847,7 +866,7 @@ namespace Ryujinx.Ava if (!_renderingStarted) { _renderingStarted = true; - _parent.SwitchToGameControl(); + _viewModel.SwitchToRenderer(false); } Device.PresentFrame(() => Renderer?.SwapBuffers()); @@ -914,7 +933,7 @@ namespace Ryujinx.Ava { Dispatcher.UIThread.Post(() => { - _parent.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default; + _viewModel.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default; }); } else @@ -925,7 +944,7 @@ namespace Ryujinx.Ava Dispatcher.UIThread.Post(() => { - _parent.Cursor = cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency ? InvisibleCursor : Cursor.Default; + _viewModel.Cursor = cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency ? InvisibleCursor : Cursor.Default; }); } } @@ -938,13 +957,13 @@ namespace Ryujinx.Ava return false; } - if (_parent.IsActive) + if (_viewModel.IsActive) { Dispatcher.UIThread.Post(() => { HandleScreenState(); - if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen) + if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) { Device.Application.DiskCacheLoadState?.Cancel(); } @@ -953,7 +972,7 @@ namespace Ryujinx.Ava NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - if (_parent.IsActive) + if (_viewModel.IsActive) { KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); @@ -969,10 +988,10 @@ namespace Ryujinx.Ava ScreenshotRequested = true; break; case KeyboardHotkeyState.ShowUi: - _parent.ViewModel.ShowMenuAndStatusBar = true; + _viewModel.ShowMenuAndStatusBar = true; break; case KeyboardHotkeyState.Pause: - if (_parent.ViewModel.IsPaused) + if (_viewModel.IsPaused) { Resume(); } @@ -991,7 +1010,7 @@ namespace Ryujinx.Ava Device.SetVolume(0); } - _parent.ViewModel.Volume = Device.GetVolume(); + _viewModel.Volume = Device.GetVolume(); break; case KeyboardHotkeyState.ResScaleUp: GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; @@ -1004,13 +1023,13 @@ namespace Ryujinx.Ava _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); Device.SetVolume(_newVolume); - _parent.ViewModel.Volume = Device.GetVolume(); + _viewModel.Volume = Device.GetVolume(); break; case KeyboardHotkeyState.VolumeDown: _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); Device.SetVolume(_newVolume); - _parent.ViewModel.Volume = Device.GetVolume(); + _viewModel.Volume = Device.GetVolume(); break; case KeyboardHotkeyState.None: (_keyboardInterface as AvaloniaKeyboard).Clear(); @@ -1030,7 +1049,7 @@ namespace Ryujinx.Ava // Touchscreen bool hasTouch = false; - if (_parent.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) + if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) { hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); } diff --git a/Ryujinx.Ava/Assets/Styles/Styles.xaml b/Ryujinx.Ava/Assets/Styles/Styles.xaml index de965a2a0..c5e760e81 100644 --- a/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -269,6 +269,8 @@ #FF00FABB #FF2D2D2D #FF505050 + #FF2EEAC9 + #FFFF4554 15 8 10 diff --git a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs index eb58752ce..b0b6cdf05 100644 --- a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs +++ b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs @@ -11,10 +11,10 @@ namespace Ryujinx.Ava.Input { internal class AvaloniaMouseDriver : IGamepadDriver { - private Control _widget; - private bool _isDisposed; - private Size _size; - private readonly Window _window; + private Control _widget; + private bool _isDisposed; + private Size _size; + private readonly TopLevel _window; public bool[] PressedButtons { get; } public Vector2 CurrentPosition { get; private set; } @@ -23,7 +23,7 @@ namespace Ryujinx.Ava.Input public string DriverName => "AvaloniaMouseDriver"; public ReadOnlySpan GamepadsIds => new[] { "0" }; - public AvaloniaMouseDriver(Window window, Control parent) + public AvaloniaMouseDriver(TopLevel window, Control parent) { _widget = parent; _window = window; diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index bc4760bae..a7399407f 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Modules } Running = true; - mainWindow.CanUpdate = false; + mainWindow.ViewModel.CanUpdate = false; // Detect current platform if (OperatingSystem.IsMacOS()) @@ -182,7 +182,7 @@ namespace Ryujinx.Modules } Running = false; - mainWindow.CanUpdate = true; + mainWindow.ViewModel.CanUpdate = true; return; } diff --git a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs index f4d9bc806..0809cb4f8 100644 --- a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs +++ b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs @@ -144,9 +144,9 @@ namespace Ryujinx.Ava.UI.Applet public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value) { device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); - if (_parent.AppHost != null) + if (_parent.ViewModel.AppHost != null) { - _parent.AppHost.Stop(); + _parent.ViewModel.AppHost.Stop(); } } diff --git a/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs index 314746e76..bae4762eb 100644 --- a/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs +++ b/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Ava.UI.Applet Dispatcher.UIThread.Post(() => { _hiddenTextBox.Clear(); - _parent.RendererControl.Focus(); + _parent.ViewModel.RendererControl.Focus(); _parent = null; }); diff --git a/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs b/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs index fe5e27210..77c7a2d2c 100644 --- a/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs +++ b/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs @@ -13,8 +13,8 @@ namespace Ryujinx.Ava.UI.Applet DefaultBackgroundColor = BrushToThemeColor(parent.Background); DefaultForegroundColor = BrushToThemeColor(parent.Foreground); DefaultBorderColor = BrushToThemeColor(parent.BorderBrush); - SelectionBackgroundColor = BrushToThemeColor(parent.SearchBox.SelectionBrush); - SelectionForegroundColor = BrushToThemeColor(parent.SearchBox.SelectionForegroundBrush); + SelectionBackgroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionBrush); + SelectionForegroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionForegroundBrush); } public string FontFamily { get; } diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index 6a2afad1f..82a75788d 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -244,9 +244,9 @@ namespace Ryujinx.Ava.UI.ViewModels _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - if (_mainWindow.AppHost != null) + if (_mainWindow.ViewModel.AppHost != null) { - _mainWindow.AppHost.NpadManager.BlockInputUpdates(); + _mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates(); } _isLoaded = false; @@ -862,7 +862,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - _mainWindow.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); // Atomically replace and signal input change. // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event. @@ -891,7 +891,7 @@ namespace Ryujinx.Ava.UI.ViewModels _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; - _mainWindow.AppHost?.NpadManager.UnblockInputUpdates(); + _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); SelectedGamepad?.Dispose(); diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 514a8bb35..9571af477 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1,5 +1,5 @@ -using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Media; using Avalonia.Threading; @@ -7,12 +7,12 @@ using DynamicData; using DynamicData.Binding; using LibHac.Fs; using LibHac.FsSystem; -using LibHac.Ncm; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -21,11 +21,13 @@ using Ryujinx.Cpu; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; -using Ryujinx.Modules; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.Ui; using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; +using SixLabors.ImageSharp.PixelFormats; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -35,14 +37,14 @@ using System.Threading; using System.Threading.Tasks; using Path = System.IO.Path; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; +using UserId = LibHac.Fs.UserId; namespace Ryujinx.Ava.UI.ViewModels { - internal class MainWindowViewModel : BaseModel + public class MainWindowViewModel : BaseModel { private const int HotKeyPressDelayMs = 500; - private readonly MainWindow _owner; private ObservableCollection _applications; private string _aspectStatusText; @@ -57,7 +59,7 @@ namespace Ryujinx.Ava.UI.ViewModels private string _gpuStatusText; private bool _isAmiiboRequested; private bool _isGameRunning; - private bool _isLoading; + private bool _isFullScreen; private int _progressMaximum; private int _progressValue; private long _lastFullscreenToggle = Environment.TickCount64; @@ -76,15 +78,29 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _isLoadingIndeterminate = true; private bool _showAll; private string _lastScannedAmiiboId; + private bool _statusBarVisible; private ReadOnlyObservableCollection _appsObservableList; - public ApplicationLibrary ApplicationLibrary => _owner.ApplicationLibrary; - public string TitleName { get; internal set; } + private string _showUiKey = "F4"; + private string _pauseKey = "F5"; + private string _screenshotKey = "F8"; + private float _volume; + private string _backendText; - public MainWindowViewModel(MainWindow owner) : this() - { - _owner = owner; - } + public ApplicationData ListSelectedApplication; + public ApplicationData GridSelectedApplication; + private bool _canUpdate; + private Cursor _cursor; + private string _title; + private string _currentEmulatedGamePath; + private AutoResetEvent _rendererWaitEvent; + private WindowState _windowState; + private bool _isActive; + + public event Action ReloadGameList; + + private string TitleName { get; set; } + internal AppHost AppHost { get; set; } public MainWindowViewModel() { @@ -95,6 +111,8 @@ namespace Ryujinx.Ava.UI.ViewModels .Sort(GetComparer()) .Bind(out _appsObservableList).AsObservableList(); + _rendererWaitEvent = new AutoResetEvent(false); + if (Program.PreviewerDetached) { LoadConfigurableHotKeys(); @@ -103,12 +121,37 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void Initialize() + public void Initialize( + ContentManager contentManager, + ApplicationLibrary applicationLibrary, + VirtualFileSystem virtualFileSystem, + AccountManager accountManager, + Ryujinx.Input.HLE.InputManager inputManager, + UserChannelPersistence userChannelPersistence, + LibHacHorizonManager libHacHorizonManager, + IHostUiHandler uiHandler, + Action showLoading, + Action switchToGameControl, + Action setMainContent, + TopLevel topLevel) { - ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; - ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; + ContentManager = contentManager; + ApplicationLibrary = applicationLibrary; + VirtualFileSystem = virtualFileSystem; + AccountManager = accountManager; + InputManager = inputManager; + UserChannelPersistence = userChannelPersistence; + LibHacHorizonManager = libHacHorizonManager; + UiHandler = uiHandler; + + ShowLoading = showLoading; + SwitchToGameControl = switchToGameControl; + SetMainContent = setMainContent; + TopLevel = topLevel; } +#region Properties + public string SearchText { get => _searchText; @@ -130,6 +173,27 @@ namespace Ryujinx.Ava.UI.ViewModels _searchTimer = null; } + public bool CanUpdate + { + get => _canUpdate; + set + { + _canUpdate = value; + + OnPropertyChanged(); + } + } + + public Cursor Cursor + { + get => _cursor; + set + { + _cursor = value; + OnPropertyChanged(); + } + } + public ReadOnlyObservableCollection AppsObservableList { get => _appsObservableList; @@ -152,6 +216,28 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public long LastFullscreenToggle + { + get => _lastFullscreenToggle; + set + { + _lastFullscreenToggle = value; + + OnPropertyChanged(); + } + } + + public bool StatusBarVisible + { + get => _statusBarVisible && EnableNonGameRunningControls; + set + { + _statusBarVisible = value; + + OnPropertyChanged(); + } + } + public bool EnableNonGameRunningControls => !IsGameRunning; public bool ShowFirmwareStatus => !ShowLoadProgress; @@ -170,6 +256,7 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); OnPropertyChanged(nameof(EnableNonGameRunningControls)); + OnPropertyChanged(nameof(StatusBarVisible)); OnPropertyChanged(nameof(ShowFirmwareStatus)); } } @@ -208,11 +295,38 @@ namespace Ryujinx.Ava.UI.ViewModels } } - private string _showUikey = "F4"; - private string _pauseKey = "F5"; - private string _screenshotkey = "F8"; - private float _volume; - private string _backendText; + public bool IsFullScreen + { + get => _isFullScreen; + set + { + _isFullScreen = value; + + OnPropertyChanged(); + } + } + + public bool ShowAll + { + get => _showAll; + set + { + _showAll = value; + + OnPropertyChanged(); + } + } + + public string LastScannedAmiiboId + { + get => _lastScannedAmiiboId; + set + { + _lastScannedAmiiboId = value; + + OnPropertyChanged(); + } + } public ApplicationData SelectedApplication { @@ -220,9 +334,9 @@ namespace Ryujinx.Ava.UI.ViewModels { return Glyph switch { - Glyph.List => _owner.GameList.SelectedApplication, - Glyph.Grid => _owner.GameGrid.SelectedApplication, - _ => null, + Glyph.List => ListSelectedApplication, + Glyph.Grid => GridSelectedApplication, + _ => null, }; } } @@ -414,7 +528,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (_isGameRunning) { - _owner.AppHost.Device.SetVolume(_volume); + AppHost.Device.SetVolume(_volume); } OnPropertyChanged(nameof(VolumeStatusText)); @@ -456,6 +570,18 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public bool IsActive + { + get => _isActive; + set + { + _isActive = value; + + OnPropertyChanged(); + } + } + + public bool ShowContent { get => _showContent; @@ -478,6 +604,17 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public WindowState WindowState + { + get => _windowState; + internal set + { + _windowState = value; + + OnPropertyChanged(); + } + } + public bool IsGrid => Glyph == Glyph.Grid; public bool IsList => Glyph == Glyph.List; @@ -495,44 +632,6 @@ namespace Ryujinx.Ava.UI.ViewModels RefreshView(); } - private IComparer GetComparer() - { - return SortMode switch - { - ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending), - ApplicationSort.FileSize => IsAscending ? SortExpressionComparer.Ascending(app => app.FileSizeBytes) - : SortExpressionComparer.Descending(app => app.FileSizeBytes), - ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer.Ascending(app => app.TimePlayedNum) - : SortExpressionComparer.Descending(app => app.TimePlayedNum), - ApplicationSort.Title => IsAscending ? SortExpressionComparer.Ascending(app => app.TitleName) - : SortExpressionComparer.Descending(app => app.TitleName), - ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer.Ascending(app => app.Favorite) - : SortExpressionComparer.Descending(app => app.Favorite), - ApplicationSort.Developer => IsAscending ? SortExpressionComparer.Ascending(app => app.Developer) - : SortExpressionComparer.Descending(app => app.Developer), - ApplicationSort.FileType => IsAscending ? SortExpressionComparer.Ascending(app => app.FileExtension) - : SortExpressionComparer.Descending(app => app.FileExtension), - ApplicationSort.Path => IsAscending ? SortExpressionComparer.Ascending(app => app.Path) - : SortExpressionComparer.Descending(app => app.Path), - _ => null, - }; - } - - private void RefreshView() - { - RefreshGrid(); - } - - private void RefreshGrid() - { - Applications.ToObservableChangeSet() - .Filter(Filter) - .Sort(GetComparer()) - .Bind(out _appsObservableList).AsObservableList(); - - OnPropertyChanged(nameof(AppsObservableList)); - } - public bool StartGamesInFullscreen { get => ConfigurationState.Instance.Ui.StartFullscreen; @@ -559,6 +658,17 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public string Title + { + get => _title; + set + { + _title = value; + + OnPropertyChanged(); + } + } + public bool ShowConsoleVisible { get => ConsoleHelper.SetConsoleWindowStateSupported; @@ -598,6 +708,7 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); OnPropertyChanged(nameof(GridSizeScale)); + OnPropertyChanged(nameof(GridItemSelectorSize)); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } @@ -617,14 +728,70 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite; - public bool IsSortedByTitle => SortMode == ApplicationSort.Title; - public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer; - public bool IsSortedByLastPlayed => SortMode == ApplicationSort.LastPlayed; - public bool IsSortedByTimePlayed => SortMode == ApplicationSort.TotalTimePlayed; - public bool IsSortedByType => SortMode == ApplicationSort.FileType; - public bool IsSortedBySize => SortMode == ApplicationSort.FileSize; - public bool IsSortedByPath => SortMode == ApplicationSort.Path; + public int ListItemSelectorSize + { + get + { + switch (ConfigurationState.Instance.Ui.GridSize) + { + case 1: + return 78; + case 2: + return 100; + case 3: + return 120; + case 4: + return 140; + default: + return 16; + } + } + } + + public int GridItemSelectorSize + { + get + { + switch (ConfigurationState.Instance.Ui.GridSize) + { + case 1: + return 120; + case 2: + return ShowNames ? 210 : 150; + case 3: + return ShowNames ? 240 : 180; + case 4: + return ShowNames ? 280 : 220; + default: + return 16; + } + } + } + + public int GridSizeScale + { + get => ConfigurationState.Instance.Ui.GridSize; + set + { + ConfigurationState.Instance.Ui.GridSize.Value = value; + + if (value < 2) + { + ShowNames = false; + } + + OnPropertyChanged(); + OnPropertyChanged(nameof(IsGridSmall)); + OnPropertyChanged(nameof(IsGridMedium)); + OnPropertyChanged(nameof(IsGridLarge)); + OnPropertyChanged(nameof(IsGridHuge)); + OnPropertyChanged(nameof(ListItemSelectorSize)); + OnPropertyChanged(nameof(GridItemSelectorSize)); + OnPropertyChanged(nameof(ShowNames)); + + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + } + } public string SortName { @@ -632,15 +799,15 @@ namespace Ryujinx.Ava.UI.ViewModels { return SortMode switch { - ApplicationSort.Title => LocaleManager.Instance[LocaleKeys.GameListHeaderApplication], - ApplicationSort.Developer => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper], - ApplicationSort.LastPlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderLastPlayed], + ApplicationSort.Title => LocaleManager.Instance[LocaleKeys.GameListHeaderApplication], + ApplicationSort.Developer => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper], + ApplicationSort.LastPlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderLastPlayed], ApplicationSort.TotalTimePlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderTimePlayed], - ApplicationSort.FileType => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension], - ApplicationSort.FileSize => LocaleManager.Instance[LocaleKeys.GameListHeaderFileSize], - ApplicationSort.Path => LocaleManager.Instance[LocaleKeys.GameListHeaderPath], - ApplicationSort.Favorite => LocaleManager.Instance[LocaleKeys.CommonFavorite], - _ => string.Empty, + ApplicationSort.FileType => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension], + ApplicationSort.FileSize => LocaleManager.Instance[LocaleKeys.GameListHeaderFileSize], + ApplicationSort.Path => LocaleManager.Instance[LocaleKeys.GameListHeaderPath], + ApplicationSort.Favorite => LocaleManager.Instance[LocaleKeys.CommonFavorite], + _ => string.Empty, }; } } @@ -662,9 +829,10 @@ namespace Ryujinx.Ava.UI.ViewModels public KeyGesture ShowUiKey { - get => KeyGesture.Parse(_showUikey); set + get => KeyGesture.Parse(_showUiKey); + set { - _showUikey = value.ToString(); + _showUiKey = value.ToString(); OnPropertyChanged(); } @@ -672,9 +840,10 @@ namespace Ryujinx.Ava.UI.ViewModels public KeyGesture ScreenshotKey { - get => KeyGesture.Parse(_screenshotkey); set + get => KeyGesture.Parse(_screenshotKey); + set { - _screenshotkey = value.ToString(); + _screenshotKey = value.ToString(); OnPropertyChanged(); } @@ -690,68 +859,73 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool IsGridSmall => ConfigurationState.Instance.Ui.GridSize == 1; + public ContentManager ContentManager { get; private set; } + public ApplicationLibrary ApplicationLibrary { get; private set; } + public VirtualFileSystem VirtualFileSystem { get; private set; } + public AccountManager AccountManager { get; private set; } + public Ryujinx.Input.HLE.InputManager InputManager { get; private set; } + public UserChannelPersistence UserChannelPersistence { get; private set; } + public Action ShowLoading { get; private set; } + public Action SwitchToGameControl { get; private set; } + public Action SetMainContent { get; private set; } + public TopLevel TopLevel { get; private set; } + public RendererHost RendererControl { get; private set; } + public bool IsClosing { get; set; } + public LibHacHorizonManager LibHacHorizonManager { get; internal set; } + public IHostUiHandler UiHandler { get; internal set; } + public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite; + public bool IsSortedByTitle => SortMode == ApplicationSort.Title; + public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer; + public bool IsSortedByLastPlayed => SortMode == ApplicationSort.LastPlayed; + public bool IsSortedByTimePlayed => SortMode == ApplicationSort.TotalTimePlayed; + public bool IsSortedByType => SortMode == ApplicationSort.FileType; + public bool IsSortedBySize => SortMode == ApplicationSort.FileSize; + public bool IsSortedByPath => SortMode == ApplicationSort.Path; + public bool IsGridSmall => ConfigurationState.Instance.Ui.GridSize == 1; public bool IsGridMedium => ConfigurationState.Instance.Ui.GridSize == 2; - public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; - public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; + public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; + public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; - public int GridSizeScale +#endregion + +#region PrivateMethods + + private IComparer GetComparer() { - get => ConfigurationState.Instance.Ui.GridSize; - set + return SortMode switch { - ConfigurationState.Instance.Ui.GridSize.Value = value; - - if (value < 2) - { - ShowNames = false; - } - - OnPropertyChanged(); - OnPropertyChanged(nameof(IsGridSmall)); - OnPropertyChanged(nameof(IsGridMedium)); - OnPropertyChanged(nameof(IsGridLarge)); - OnPropertyChanged(nameof(IsGridHuge)); - OnPropertyChanged(nameof(ShowNames)); - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } + ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending), + ApplicationSort.FileSize => IsAscending ? SortExpressionComparer.Ascending(app => app.FileSizeBytes) + : SortExpressionComparer.Descending(app => app.FileSizeBytes), + ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer.Ascending(app => app.TimePlayedNum) + : SortExpressionComparer.Descending(app => app.TimePlayedNum), + ApplicationSort.Title => IsAscending ? SortExpressionComparer.Ascending(app => app.TitleName) + : SortExpressionComparer.Descending(app => app.TitleName), + ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer.Ascending(app => app.Favorite) + : SortExpressionComparer.Descending(app => app.Favorite), + ApplicationSort.Developer => IsAscending ? SortExpressionComparer.Ascending(app => app.Developer) + : SortExpressionComparer.Descending(app => app.Developer), + ApplicationSort.FileType => IsAscending ? SortExpressionComparer.Ascending(app => app.FileExtension) + : SortExpressionComparer.Descending(app => app.FileExtension), + ApplicationSort.Path => IsAscending ? SortExpressionComparer.Ascending(app => app.Path) + : SortExpressionComparer.Descending(app => app.Path), + _ => null, + }; } - public async void OpenAmiiboWindow() + private void RefreshView() { - if (!_isAmiiboRequested) - { - return; - } - - if (_owner.AppHost.Device.System.SearchingForAmiibo(out int deviceId)) - { - string titleId = _owner.AppHost.Device.Application.TitleIdText.ToUpper(); - AmiiboWindow window = new(_showAll, _lastScannedAmiiboId, titleId); - - await window.ShowDialog(_owner); - - if (window.IsScanned) - { - _showAll = window.ViewModel.ShowAllAmiibo; - _lastScannedAmiiboId = window.ScannedAmiibo.GetId(); - - _owner.AppHost.Device.System.ScanAmiibo(deviceId, _lastScannedAmiiboId, window.ViewModel.UseRandomUuid); - } - } + RefreshGrid(); } - public void SetUiProgressHandlers(Switch emulationContext) + private void RefreshGrid() { - if (emulationContext.Application.DiskCacheLoadState != null) - { - emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; - emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; - } + Applications.ToObservableChangeSet() + .Filter(Filter) + .Sort(GetComparer()) + .Bind(out _appsObservableList).AsObservableList(); - emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; - emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; + OnPropertyChanged(nameof(AppsObservableList)); } private bool Filter(object arg) @@ -764,148 +938,314 @@ namespace Ryujinx.Ava.UI.ViewModels return false; } - private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e) + private async Task HandleFirmwareInstallation(string filename) { - AddApplication(e.AppData); - } - - private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e) - { - StatusBarProgressValue = e.NumAppsLoaded; - StatusBarProgressMaximum = e.NumAppsFound; - - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, StatusBarProgressValue, StatusBarProgressMaximum); - - Dispatcher.UIThread.Post(() => + try { - if (e.NumAppsFound == 0) + SystemVersion firmwareVersion = ContentManager.VerifyFirmwarePackage(filename); + + if (firmwareVersion == null) { - _owner.LoadProgressBar.IsVisible = false; + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage], filename)); + + return; } - if (e.NumAppsLoaded == e.NumAppsFound) + string dialogTitle = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle], firmwareVersion.VersionString); + + SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion(); + + string dialogMessage = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage], firmwareVersion.VersionString); + + if (currentVersion != null) { - _owner.LoadProgressBar.IsVisible = false; + dialogMessage += string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage], currentVersion.VersionString); } - }); - } - public void AddApplication(ApplicationData applicationData) - { - Dispatcher.UIThread.InvokeAsync(() => - { - Applications.Add(applicationData); - }); - } + dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage]; - public async void LoadApplications() - { - await Dispatcher.UIThread.InvokeAsync(() => - { - Applications.Clear(); + UserResult result = await ContentDialogHelper.CreateConfirmationDialog( + dialogTitle, + dialogMessage, + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - _owner.LoadProgressBar.IsVisible = true; - StatusBarProgressMaximum = 0; - StatusBarProgressValue = 0; + UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]); - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); - }); + if (result == UserResult.Yes) + { + Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}"); - ReloadGameList(); - } + Thread thread = new(() => + { + Dispatcher.UIThread.InvokeAsync(delegate + { + waitingDialog.Show(); + }); - private void ReloadGameList() - { - if (_isLoading) - { - return; + try + { + ContentManager.InstallFirmware(filename); + + Dispatcher.UIThread.InvokeAsync(async delegate + { + waitingDialog.Close(); + + string message = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage], firmwareVersion.VersionString); + + await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]); + + Logger.Info?.Print(LogClass.Application, message); + + // Purge Applet Cache. + + DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); + + if (miiEditorCacheFolder.Exists) + { + miiEditorCacheFolder.Delete(true); + } + }); + } + catch (Exception ex) + { + Dispatcher.UIThread.InvokeAsync(async () => + { + waitingDialog.Close(); + + await ContentDialogHelper.CreateErrorDialog(ex.Message); + }); + } + finally + { + RefreshFirmwareStatus(); + } + }) { Name = "GUI.FirmwareInstallerThread" }; + + thread.Start(); + } } - - _isLoading = true; - - Thread thread = new(() => + catch (LibHac.Common.Keys.MissingKeyException ex) { - ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language); - - _isLoading = false; - }) - { Name = "GUI.AppListLoadThread", Priority = ThreadPriority.AboveNormal }; - - thread.Start(); - } - - public async void OpenFile() - { - OpenFileDialog dialog = new() - { - Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle] - }; - - dialog.Filters.Add(new FileDialogFilter - { - Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats], - Extensions = + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - "nsp", - "pfs0", - "xci", - "nca", - "nro", - "nso" + Logger.Error?.Print(LogClass.Application, ex.ToString()); + + async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, (desktop.MainWindow as MainWindow)); + + Dispatcher.UIThread.Post(Action); } - }); - - dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); - - string[] files = await dialog.ShowAsync(_owner); - - if (files != null && files.Length > 0) + } + catch (Exception ex) { - _owner.LoadApplication(files[0]); + await ContentDialogHelper.CreateErrorDialog(ex.Message); } } - public async void OpenFolder() + private void ProgressHandler(T state, int current, int total) where T : Enum { - OpenFolderDialog dialog = new() + Dispatcher.UIThread.Post((() => { - Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle] - }; + ProgressMaximum = total; + ProgressValue = current; - string folder = await dialog.ShowAsync(_owner); + switch (state) + { + case LoadState ptcState: + CacheLoadStatus = $"{current} / {total}"; + switch (ptcState) + { + case LoadState.Unloaded: + case LoadState.Loading: + LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingPPTC]; + IsLoadingIndeterminate = false; + break; + case LoadState.Loaded: + LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); + IsLoadingIndeterminate = true; + CacheLoadStatus = ""; + break; + } + break; + case ShaderCacheLoadingState shaderCacheState: + CacheLoadStatus = $"{current} / {total}"; + switch (shaderCacheState) + { + case ShaderCacheLoadingState.Start: + case ShaderCacheLoadingState.Loading: + LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders]; + IsLoadingIndeterminate = false; + break; + case ShaderCacheLoadingState.Loaded: + LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); + IsLoadingIndeterminate = true; + CacheLoadStatus = ""; + break; + } + break; + default: + throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}"); + } + })); + } - if (!string.IsNullOrWhiteSpace(folder) && Directory.Exists(folder)) + private void OpenSaveDirectory(in SaveDataFilter filter, ApplicationData data, ulong titleId) + { + ApplicationHelper.OpenSaveDir(in filter, titleId, data.ControlHolder, data.TitleName); + } + + private async void ExtractLogo() + { + var selection = SelectedApplication; + if (selection != null) { - _owner.LoadApplication(folder); + await ApplicationHelper.ExtractSection(NcaSectionType.Logo, selection.Path); } } + private async void ExtractRomFs() + { + var selection = SelectedApplication; + if (selection != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Data, selection.Path); + } + } + + private async void ExtractExeFs() + { + var selection = SelectedApplication; + if (selection != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Code, selection.Path); + } + } + + private void PrepareLoadScreen() + { + using MemoryStream stream = new(SelectedIcon); + using var gameIconBmp = SixLabors.ImageSharp.Image.Load(stream); + + var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel(); + + const float colorMultiple = 0.5f; + + Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B); + Color progressBgColor = Color.FromRgb( + (byte)(dominantColor.R * colorMultiple), + (byte)(dominantColor.G * colorMultiple), + (byte)(dominantColor.B * colorMultiple)); + + ProgressBarForegroundColor = new SolidColorBrush(progressFgColor); + ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor); + } + + private void InitializeGame() + { + RendererControl.RendererInitialized += GlRenderer_Created; + + AppHost.StatusUpdatedEvent += Update_StatusBar; + AppHost.AppExit += AppHost_AppExit; + + _rendererWaitEvent.WaitOne(); + + AppHost?.Start(); + + AppHost?.DisposeContext(); + } + + private void HandleRelaunch() + { + if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart) + { + UserChannelPersistence.ShouldRestart = false; + + Dispatcher.UIThread.Post(() => + { + LoadApplication(_currentEmulatedGamePath); + }); + } + else + { + // Otherwise, clear state. + UserChannelPersistence = new UserChannelPersistence(); + _currentEmulatedGamePath = null; + } + } + + private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) + { + if (ShowMenuAndStatusBar && !ShowLoadProgress) + { + Dispatcher.UIThread.InvokeAsync(() => + { + Avalonia.Application.Current.Styles.TryGetResource(args.VSyncEnabled + ? "VsyncEnabled" + : "VsyncDisabled", out object color); + + if (color is not null) + { + VsyncColor = new SolidColorBrush((Color)color); + } + + DockedStatusText = args.DockedMode; + AspectRatioStatusText = args.AspectRatio; + GameStatusText = args.GameStatus; + VolumeStatusText = args.VolumeStatus; + FifoStatusText = args.FifoStatus; + GpuNameText = args.GpuName; + BackendText = args.GpuBackend; + + ShowStatusSeparator = true; + }); + } + } + + private void GlRenderer_Created(object sender, EventArgs e) + { + ShowLoading(false); + + _rendererWaitEvent.Set(); + } + +#endregion + +#region PublicMethods + + public void SetUIProgressHandlers(Switch emulationContext) + { + if (emulationContext.Application.DiskCacheLoadState != null) + { + emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; + emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + } + + emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; + emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; + } + public void LoadConfigurableHotKeys() { if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey)) { - ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None); + ShowUiKey = new KeyGesture(showUiKey); } if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) { - ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None); + ScreenshotKey = new KeyGesture(screenshotKey); } if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) { - PauseKey = new KeyGesture(pauseKey, KeyModifiers.None); + PauseKey = new KeyGesture(pauseKey); } } public void TakeScreenshot() { - _owner.AppHost.ScreenshotRequested = true; + AppHost.ScreenshotRequested = true; } public void HideUi() @@ -923,13 +1263,36 @@ namespace Ryujinx.Ava.UI.ViewModels Glyph = Glyph.Grid; } - public void OpenMiiApplet() + public async void InstallFirmwareFromFile() { - string contentPath = _owner.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); - - if (!string.IsNullOrWhiteSpace(contentPath)) + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - _owner.LoadApplication(contentPath, false, "Mii Applet"); + OpenFileDialog dialog = new() { AllowMultiple = false }; + dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); + + string[] file = await dialog.ShowAsync(desktop.MainWindow); + + if (file != null && file.Length > 0) + { + await HandleFirmwareInstallation(file[0]); + } + } + } + + public async void InstallFirmwareFromFolder() + { + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + OpenFolderDialog dialog = new(); + + string folder = await dialog.ShowAsync(desktop.MainWindow); + + if (!string.IsNullOrEmpty(folder)) + { + await HandleFirmwareInstallation(folder); + } } } @@ -947,39 +1310,6 @@ namespace Ryujinx.Ava.UI.ViewModels OpenHelper.OpenFolder(logPath); } - public void ToggleFullscreen() - { - if (Environment.TickCount64 - _lastFullscreenToggle < HotKeyPressDelayMs) - { - return; - } - - _lastFullscreenToggle = Environment.TickCount64; - - if (_owner.WindowState == WindowState.FullScreen) - { - _owner.WindowState = WindowState.Normal; - - if (IsGameRunning) - { - ShowMenuAndStatusBar = true; - } - } - else - { - _owner.WindowState = WindowState.FullScreen; - - if (IsGameRunning) - { - ShowMenuAndStatusBar = false; - } - } - - OnPropertyChanged(nameof(IsFullScreen)); - } - - public bool IsFullScreen => _owner.WindowState == WindowState.FullScreen; - public void ToggleDockMode() { if (IsGameRunning) @@ -990,7 +1320,7 @@ namespace Ryujinx.Ava.UI.ViewModels public async void ExitCurrentState() { - if (_owner.WindowState == WindowState.FullScreen) + if (WindowState == WindowState.FullScreen) { ToggleFullscreen(); } @@ -998,146 +1328,19 @@ namespace Ryujinx.Ava.UI.ViewModels { await Task.Delay(100); - _owner.AppHost?.ShowExitPrompt(); + AppHost?.ShowExitPrompt(); } } - public async void OpenSettings() - { - _owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager); - - await _owner.SettingsWindow.ShowDialog(_owner); - - LoadConfigurableHotKeys(); - } - - public async void ManageProfiles() - { - await NavigationDialogHost.Show(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem, _owner.LibHacHorizonManager.RyujinxClient); - } - - public async void OpenAboutWindow() - { - await AboutWindow.Show(); - } - public void ChangeLanguage(object obj) { LocaleManager.Instance.LoadDefaultLanguage(); LocaleManager.Instance.LoadLanguage((string)obj); } - private void ProgressHandler(T state, int current, int total) where T : Enum + public async void ManageProfiles() { - try - { - ProgressMaximum = total; - ProgressValue = current; - - switch (state) - { - case LoadState ptcState: - CacheLoadStatus = $"{current} / {total}"; - switch (ptcState) - { - case LoadState.Unloaded: - case LoadState.Loading: - LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingPPTC]; - IsLoadingIndeterminate = false; - break; - case LoadState.Loaded: - LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); - IsLoadingIndeterminate = true; - CacheLoadStatus = ""; - break; - } - break; - case ShaderCacheLoadingState shaderCacheState: - CacheLoadStatus = $"{current} / {total}"; - switch (shaderCacheState) - { - case ShaderCacheLoadingState.Start: - case ShaderCacheLoadingState.Loading: - LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders]; - IsLoadingIndeterminate = false; - break; - case ShaderCacheLoadingState.Loaded: - LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); - IsLoadingIndeterminate = true; - CacheLoadStatus = ""; - break; - } - break; - default: - throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}"); - } - } - catch (Exception) { } - } - - public void OpenUserSaveDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - Task.Run(() => - { - if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); - }); - - return; - } - - UserId userId = new((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low); - SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default); - OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); - }); - } - } - - public void ToggleFavorite() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - selection.Favorite = !selection.Favorite; - - ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata => - { - appMetadata.Favorite = selection.Favorite; - }); - - RefreshView(); - } - } - - public void OpenModsDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId); - - OpenHelper.OpenFolder(titleModsPath); - } - } - - public void OpenSdModsDirectory() - { - ApplicationData selection = SelectedApplication; - - if (selection != null) - { - string sdModsBasePath = _owner.VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId); - - OpenHelper.OpenFolder(titleModsPath); - } + await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient); } public void OpenPtcDirectory() @@ -1145,8 +1348,8 @@ namespace Ryujinx.Ava.UI.ViewModels ApplicationData selection = SelectedApplication; if (selection != null) { - string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu"); - string mainPath = Path.Combine(ptcDir, "0"); + string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu"); + string mainPath = Path.Combine(ptcDir, "0"); string backupPath = Path.Combine(ptcDir, "1"); if (!Directory.Exists(ptcDir)) @@ -1165,7 +1368,7 @@ namespace Ryujinx.Ava.UI.ViewModels ApplicationData selection = SelectedApplication; if (selection != null) { - DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0")); + DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0")); DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1")); // FIXME: Found a way to reproduce the bold effect on the title name (fork?). @@ -1222,7 +1425,7 @@ namespace Ryujinx.Ava.UI.ViewModels public void SimulateWakeUpMessage() { - _owner.AppHost.Device.System.SimulateWakeUpMessage(); + AppHost.Device.System.SimulateWakeUpMessage(); } public async void PurgeShaderCache() @@ -1240,7 +1443,7 @@ namespace Ryujinx.Ava.UI.ViewModels LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); List oldCacheDirectories = new(); - List newCacheFiles = new(); + List newCacheFiles = new(); if (shaderCacheDir.Exists) { @@ -1278,57 +1481,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public async void CheckForUpdates() - { - if (Updater.CanUpdate(true, _owner)) - { - await Updater.BeginParse(_owner, true); - } - } - - public async void OpenTitleUpdateManager() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - await new TitleUpdateWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner); - } - } - - public async void OpenDownloadableContentManager() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - await new DownloadableContentManagerWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner); - } - } - - public async void OpenCheatManager() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - await new CheatWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner); - } - } - - public async void OpenCheatManagerForCurrentApp() - { - if (!IsGameRunning) - { - return; - } - - ApplicationLoader application = _owner.AppHost.Device.Application; - if (application != null) - { - await new CheatWindow(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(_owner); - - _owner.AppHost.Device.EnableCheats(); - } - } - public void OpenDeviceSaveDirectory() { ApplicationData selection = SelectedApplication; @@ -1338,10 +1490,12 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) { - Dispatcher.UIThread.Post(async () => + async void Action() { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); - }); + } + + Dispatcher.UIThread.Post(Action); return; } @@ -1361,10 +1515,12 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) { - Dispatcher.UIThread.Post(async () => + async void Action() { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); - }); + } + + Dispatcher.UIThread.Post(Action); return; } @@ -1375,169 +1531,421 @@ namespace Ryujinx.Ava.UI.ViewModels } } - private void OpenSaveDirectory(in SaveDataFilter filter, ApplicationData data, ulong titleId) + public void ToggleFavorite() { - ApplicationHelper.OpenSaveDir(in filter, titleId, data.ControlHolder, data.TitleName); - } - - private async void ExtractLogo() - { - var selection = SelectedApplication; + ApplicationData selection = SelectedApplication; if (selection != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Logo, selection.Path); - } - } + selection.Favorite = !selection.Favorite; - private async void ExtractRomFs() - { - var selection = SelectedApplication; - if (selection != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Data, selection.Path); - } - } - - private async void ExtractExeFs() - { - var selection = SelectedApplication; - if (selection != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Code, selection.Path); - } - } - - public void CloseWindow() - { - _owner.Close(); - } - - private async Task HandleFirmwareInstallation(string filename) - { - try - { - SystemVersion firmwareVersion = _owner.ContentManager.VerifyFirmwarePackage(filename); - - if (firmwareVersion == null) + ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage], filename)); + appMetadata.Favorite = selection.Favorite; + }); + + RefreshView(); + } + } + + public void OpenUserSaveDirectory() + { + ApplicationData selection = SelectedApplication; + if (selection != null) + { + Task.Run(() => + { + if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) + { + async void Action() + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); + } + + Dispatcher.UIThread.Post(Action); + + return; + } + + UserId userId = new((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low); + SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default); + OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); + }); + } + } + + public void OpenModsDirectory() + { + ApplicationData selection = SelectedApplication; + if (selection != null) + { + string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); + string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId); + + OpenHelper.OpenFolder(titleModsPath); + } + } + + public void OpenSdModsDirectory() + { + ApplicationData selection = SelectedApplication; + + if (selection != null) + { + string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); + string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId); + + OpenHelper.OpenFolder(titleModsPath); + } + } + + public async void OpenTitleUpdateManager() + { + ApplicationData selection = SelectedApplication; + if (selection != null) + { + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + await new TitleUpdateWindow(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(desktop.MainWindow); + } + } + } + + public async void OpenDownloadableContentManager() + { + ApplicationData selection = SelectedApplication; + if (selection != null) + { + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(desktop.MainWindow); + } + } + } + + public async void OpenCheatManager() + { + ApplicationData selection = SelectedApplication; + if (selection != null) + { + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + await new CheatWindow(VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(desktop.MainWindow); + } + } + } + + public async void LoadApplications() + { + await Dispatcher.UIThread.InvokeAsync(() => + { + Applications.Clear(); + + StatusBarVisible = true; + StatusBarProgressMaximum = 0; + StatusBarProgressValue = 0; + + LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); + }); + + ReloadGameList?.Invoke(); + } + + public async void OpenFile() + { + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + OpenFileDialog dialog = new() + { + Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle] + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats], + Extensions = + { + "nsp", + "pfs0", + "xci", + "nca", + "nro", + "nso" + } + }); + + dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); + + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null && files.Length > 0) + { + LoadApplication(files[0]); + } + } + } + + public async void OpenFolder() + { + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + OpenFolderDialog dialog = new() + { + Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle] + }; + + string folder = await dialog.ShowAsync(desktop.MainWindow); + + if (!string.IsNullOrWhiteSpace(folder) && Directory.Exists(folder)) + { + LoadApplication(folder); + } + } + } + + public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "") + { + if (AppHost != null) + { + await ContentDialogHelper.CreateInfoDialog( + LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage], + LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage], + LocaleManager.Instance[LocaleKeys.InputDialogOk], + "", + LocaleManager.Instance[LocaleKeys.RyujinxInfo]); + + return; + } + +#if RELEASE + await PerformanceCheck(); +#endif + + Logger.RestartTime(); + + if (SelectedIcon == null) + { + SelectedIcon = ApplicationLibrary.GetApplicationIcon(path); + } + + PrepareLoadScreen(); + + RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel); + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl) + { + RendererControl.CreateOpenGL(); + } + else + { + RendererControl.CreateVulkan(); + } + + AppHost = new AppHost( + RendererControl, + InputManager, + path, + VirtualFileSystem, + ContentManager, + AccountManager, + UserChannelPersistence, + this, + TopLevel); + + async void Action() + { + if (!await AppHost.LoadGuestApplication()) + { + AppHost.DisposeContext(); + AppHost = null; return; } - string dialogTitle = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle], firmwareVersion.VersionString); + CanUpdate = false; + LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName; + TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; - SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion(); + SwitchToRenderer(startFullscreen); - string dialogMessage = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage], firmwareVersion.VersionString); + _currentEmulatedGamePath = path; - if (currentVersion != null) + Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" }; + gameThread.Start(); + } + + Dispatcher.UIThread.Post(Action); + } + + public void SwitchToRenderer(bool startFullscreen) + { + Dispatcher.UIThread.Post(() => + { + SwitchToGameControl(startFullscreen); + + SetMainContent(RendererControl); + + RendererControl.Focus(); + }); + } + + public void UpdateGameMetadata(string titleId) + { + ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => + { + if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) { - dialogMessage += string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage], currentVersion.VersionString); - } + double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage]; + appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + } + }); + } + + public void RefreshFirmwareStatus() + { + SystemVersion version = null; + try + { + version = ContentManager.GetCurrentFirmwareVersion(); + } + catch (Exception) { } + + bool hasApplet = false; + + if (version != null) + { + LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, + version.VersionString); + + hasApplet = version.Major > 3; + } + else + { + LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0"); + } + + IsAppletMenuActive = hasApplet; + } + + public void AppHost_AppExit(object sender, EventArgs e) + { + if (IsClosing) + { + return; + } + + IsGameRunning = false; + + Dispatcher.UIThread.InvokeAsync(() => + { + ShowMenuAndStatusBar = true; + ShowContent = true; + ShowLoadProgress = false; + IsLoadingIndeterminate = false; + CanUpdate = true; + Cursor = Cursor.Default; + + SetMainContent(null); + + AppHost = null; + + HandleRelaunch(); + }); + + RendererControl.RendererInitialized -= GlRenderer_Created; + RendererControl = null; + + SelectedIcon = null; + + Dispatcher.UIThread.InvokeAsync(() => + { + Title = $"Ryujinx {Program.Version}"; + }); + } + + public void ToggleFullscreen() + { + if (Environment.TickCount64 - LastFullscreenToggle < HotKeyPressDelayMs) + { + return; + } + + LastFullscreenToggle = Environment.TickCount64; + + if (WindowState == WindowState.FullScreen) + { + WindowState = WindowState.Normal; + + if (IsGameRunning) + { + ShowMenuAndStatusBar = true; + } + } + else + { + WindowState = WindowState.FullScreen; + + if (IsGameRunning) + { + ShowMenuAndStatusBar = false; + } + } + + IsFullScreen = WindowState == WindowState.FullScreen; + } + + public static void SaveConfig() + { + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + } + + public static async Task PerformanceCheck() + { + if (ConfigurationState.Instance.Logger.EnableTrace.Value) + { + string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledMessage]; + string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage]; UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - dialogTitle, - dialogMessage, + mainMessage, + secondaryMessage, LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]); - - if (result == UserResult.Yes) + if (result != UserResult.Yes) { - Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}"); + ConfigurationState.Instance.Logger.EnableTrace.Value = false; - Thread thread = new(() => - { - Dispatcher.UIThread.InvokeAsync(delegate - { - waitingDialog.Show(); - }); - - try - { - _owner.ContentManager.InstallFirmware(filename); - - Dispatcher.UIThread.InvokeAsync(async delegate - { - waitingDialog.Close(); - - string message = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage], firmwareVersion.VersionString); - - await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]); - - Logger.Info?.Print(LogClass.Application, message); - - // Purge Applet Cache. - - DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); - - if (miiEditorCacheFolder.Exists) - { - miiEditorCacheFolder.Delete(true); - } - }); - } - catch (Exception ex) - { - Dispatcher.UIThread.InvokeAsync(async () => - { - waitingDialog.Close(); - - await ContentDialogHelper.CreateErrorDialog(ex.Message); - }); - } - finally - { - _owner.RefreshFirmwareStatus(); - } - }); - - thread.Name = "GUI.FirmwareInstallerThread"; - thread.Start(); + SaveConfig(); } } - catch (LibHac.Common.Keys.MissingKeyException ex) - { - Logger.Error?.Print(LogClass.Application, ex.ToString()); - Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, _owner)); - } - catch (Exception ex) + if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) { - await ContentDialogHelper.CreateErrorDialog(ex.Message); + string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledMessage]; + string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage]; + + UserResult result = await ContentDialogHelper.CreateConfirmationDialog( + mainMessage, + secondaryMessage, + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result != UserResult.Yes) + { + ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = ""; + + SaveConfig(); + } } } - public async void InstallFirmwareFromFile() - { - OpenFileDialog dialog = new() { AllowMultiple = false }; - dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); - - string[] file = await dialog.ShowAsync(_owner); - - if (file != null && file.Length > 0) - { - await HandleFirmwareInstallation(file[0]); - } - } - - public async void InstallFirmwareFromFolder() - { - OpenFolderDialog dialog = new(); - - string folder = await dialog.ShowAsync(_owner); - - if (!string.IsNullOrWhiteSpace(folder)) - { - await HandleFirmwareInstallation(folder); - } - } +#endregion } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml new file mode 100644 index 000000000..0d0ae1193 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs new file mode 100644 index 000000000..8c28abffe --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -0,0 +1,146 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using System.Threading.Tasks; +using LibHac.FsSystem; +using LibHac.Ncm; +using Ryujinx.HLE.HOS; +using Ryujinx.Modules; + +namespace Ryujinx.Ava.UI.Views.Main +{ + public partial class MainMenuBarView : UserControl + { + public MainWindow Window { get; private set; } + public MainWindowViewModel ViewModel { get; private set; } + + public MainMenuBarView() + { + InitializeComponent(); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + if (this.VisualRoot is MainWindow window) + { + Window = window; + } + + ViewModel = Window.ViewModel; + DataContext = ViewModel; + } + + private async void StopEmulation_Click(object sender, RoutedEventArgs e) + { + await Task.Run(() => + { + Window.ViewModel.AppHost?.ShowExitPrompt(); + }); + } + + private async void PauseEmulation_Click(object sender, RoutedEventArgs e) + { + await Task.Run(() => + { + Window.ViewModel.AppHost?.Pause(); + }); + } + + private async void ResumeEmulation_Click(object sender, RoutedEventArgs e) + { + await Task.Run(() => + { + Window.ViewModel.AppHost?.Resume(); + }); + } + + public async void OpenSettings(object sender, RoutedEventArgs e) + { + Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager); + + await Window.SettingsWindow.ShowDialog(Window); + + ViewModel.LoadConfigurableHotKeys(); + } + + public void OpenMiiApplet(object sender, RoutedEventArgs e) + { + string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); + + if (!string.IsNullOrEmpty(contentPath)) + { + ViewModel.LoadApplication(contentPath, false, "Mii Applet"); + } + } + + public async void OpenAmiiboWindow(object sender, RoutedEventArgs e) + { + if (!ViewModel.IsAmiiboRequested) + { + return; + } + + if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId)) + { + string titleId = ViewModel.AppHost.Device.Application.TitleIdText.ToUpper(); + AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId); + + await window.ShowDialog(Window); + + if (window.IsScanned) + { + ViewModel.ShowAll = window.ViewModel.ShowAllAmiibo; + ViewModel.LastScannedAmiiboId = window.ScannedAmiibo.GetId(); + + ViewModel.AppHost.Device.System.ScanAmiibo(deviceId, ViewModel.LastScannedAmiiboId, window.ViewModel.UseRandomUuid); + } + } + } + + public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e) + { + if (!ViewModel.IsGameRunning) + { + return; + } + + ApplicationLoader application = ViewModel.AppHost.Device.Application; + if (application != null) + { + await new CheatWindow(Window.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(Window); + + ViewModel.AppHost.Device.EnableCheats(); + } + } + + private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + if (sender is MenuItem) + { + ViewModel.IsAmiiboRequested = Window.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _); + } + } + + public async void CheckForUpdates(object sender, RoutedEventArgs e) + { + if (Updater.CanUpdate(true, Window)) + { + await Updater.BeginParse(Window, true); + } + } + + public async void OpenAboutWindow(object sender, RoutedEventArgs e) + { + await AboutWindow.Show(); + } + + public void CloseWindow(object sender, RoutedEventArgs e) + { + Window.Close(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml b/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml new file mode 100644 index 000000000..167056954 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs new file mode 100644 index 000000000..d1050dddf --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs @@ -0,0 +1,52 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Ryujinx.Ava.UI.Windows; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Ui.Common.Configuration; +using System; + +namespace Ryujinx.Ava.UI.Views.Main +{ + public partial class MainStatusBarView : UserControl + { + public MainWindow Window; + + public MainStatusBarView() + { + InitializeComponent(); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + if (this.VisualRoot is MainWindow window) + { + Window = window; + } + + DataContext = Window.ViewModel; + } + + private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e) + { + Window.ViewModel.AppHost.Device.EnableDeviceVsync = !Window.ViewModel.AppHost.Device.EnableDeviceVsync; + + Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {Window.ViewModel.AppHost.Device.EnableDeviceVsync}"); + } + + private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e) + { + ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; + } + + private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e) + { + AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value; + + ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml b/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml new file mode 100644 index 000000000..e83a65046 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs new file mode 100644 index 000000000..841d59dec --- /dev/null +++ b/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs @@ -0,0 +1,54 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using System; + +namespace Ryujinx.Ava.UI.Views.Main +{ + public partial class MainViewControls : UserControl + { + public MainWindowViewModel ViewModel; + + public MainViewControls() + { + InitializeComponent(); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + if (this.VisualRoot is MainWindow window) + { + ViewModel = window.ViewModel; + } + + DataContext = ViewModel; + } + + public void Sort_Checked(object sender, RoutedEventArgs args) + { + if (sender is RadioButton button) + { + ViewModel.Sort(Enum.Parse(button.Tag.ToString())); + } + } + + public void Order_Checked(object sender, RoutedEventArgs args) + { + if (sender is RadioButton button) + { + ViewModel.Sort(button.Tag.ToString() != "Descending"); + } + } + + private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) + { + ViewModel.SearchText = SearchBox.Text; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/Ryujinx.Ava/UI/Windows/MainWindow.axaml index 63d939a8e..c3d34e75d 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml @@ -2,15 +2,16 @@ x:Class="Ryujinx.Ava.UI.Windows.MainWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" - xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" - Title="Ryujinx" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" + xmlns:main="clr-namespace:Ryujinx.Ava.UI.Views.Main" + Cursor="{Binding Cursor}" + Title="{Binding Title}" + WindowState="{Binding WindowState}" Width="1280" Height="777" MinWidth="1092" @@ -66,206 +67,8 @@ VerticalAlignment="Stretch" IsVisible="{Binding ShowMenuAndStatusBar}" Orientation="Vertical"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + TextWrapping="Wrap" + MaxWidth="500" /> + TextAlignment="Left" + MaxWidth="500" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index b2d822c3d..3c4534c54 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -1,19 +1,14 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Media; using Avalonia.Threading; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Applet; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.FileSystem; @@ -25,11 +20,9 @@ using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; -using SixLabors.ImageSharp.PixelFormats; using System; using System.ComponentModel; using System.IO; -using System.Threading; using System.Threading.Tasks; using InputManager = Ryujinx.Input.HLE.InputManager; @@ -38,19 +31,14 @@ namespace Ryujinx.Ava.UI.Windows public partial class MainWindow : StyleableWindow { internal static MainWindowViewModel MainWindowViewModel { get; private set; } - private bool _canUpdate; - private bool _isClosing; - private bool _isLoading; - private Control _mainViewContent; + private bool _isLoading; private UserChannelPersistence _userChannelPersistence; private static bool _deferLoad; private static string _launchPath; private static bool _startFullscreen; - private string _currentEmulatedGamePath; internal readonly AvaHostUiHandler UiHandler; - private AutoResetEvent _rendererWaitEvent; public VirtualFileSystem VirtualFileSystem { get; private set; } public ContentManager ContentManager { get; private set; } @@ -58,30 +46,17 @@ namespace Ryujinx.Ava.UI.Windows public LibHacHorizonManager LibHacHorizonManager { get; private set; } - internal AppHost AppHost { get; private set; } public InputManager InputManager { get; private set; } - internal RendererHost RendererControl { get; private set; } internal MainWindowViewModel ViewModel { get; private set; } public SettingsWindow SettingsWindow { get; set; } - public bool CanUpdate - { - get => _canUpdate; - set - { - _canUpdate = value; - - Dispatcher.UIThread.InvokeAsync(() => UpdateMenuItem.IsEnabled = _canUpdate); - } - } - public static bool ShowKeyErrorOnLoad { get; set; } public ApplicationLibrary ApplicationLibrary { get; set; } public MainWindow() { - ViewModel = new MainWindowViewModel(this); + ViewModel = new MainWindowViewModel(); MainWindowViewModel = ViewModel; @@ -92,10 +67,10 @@ namespace Ryujinx.Ava.UI.Windows UiHandler = new AvaHostUiHandler(this); - Title = $"Ryujinx {Program.Version}"; + ViewModel.Title = $"Ryujinx {Program.Version}"; // NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point. - double barHeight = MenuBar.MinHeight + StatusBar.MinHeight; + double barHeight = MenuBar.MinHeight + StatusBarView.StatusBar.MinHeight; Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight; Width /= Program.WindowScaleFactor; @@ -103,14 +78,37 @@ namespace Ryujinx.Ava.UI.Windows { Initialize(); - ViewModel.Initialize(); - InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); + ViewModel.Initialize( + ContentManager, + ApplicationLibrary, + VirtualFileSystem, + AccountManager, + InputManager, + _userChannelPersistence, + LibHacHorizonManager, + UiHandler, + ShowLoading, + SwitchToGameControl, + SetMainContent, + this); + + ViewModel.RefreshFirmwareStatus(); + LoadGameList(); + + this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged); } - _rendererWaitEvent = new AutoResetEvent(false); + ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; + ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; + ViewModel.ReloadGameList += ReloadGameList; + } + + private void IsActiveChanged(bool obj) + { + ViewModel.IsActive = obj; } public void LoadGameList() @@ -122,45 +120,51 @@ namespace Ryujinx.Ava.UI.Windows _isLoading = true; - ViewModel.LoadApplications(); + LoadApplications(); _isLoading = false; } - private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) - { - if (ViewModel.ShowMenuAndStatusBar && !ViewModel.ShowLoadProgress) - { - Dispatcher.UIThread.InvokeAsync(() => - { - if (args.VSyncEnabled) - { - ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ff2eeac9")); - } - else - { - ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ffff4554")); - } - - ViewModel.DockedStatusText = args.DockedMode; - ViewModel.AspectRatioStatusText = args.AspectRatio; - ViewModel.GameStatusText = args.GameStatus; - ViewModel.VolumeStatusText = args.VolumeStatus; - ViewModel.FifoStatusText = args.FifoStatus; - ViewModel.GpuNameText = args.GpuName; - ViewModel.BackendText = args.GpuBackend; - - ViewModel.ShowStatusSeparator = true; - }); - } - } - protected override void HandleScalingChanged(double scale) { Program.DesktopScaleFactor = scale; base.HandleScalingChanged(scale); } + public void AddApplication(ApplicationData applicationData) + { + Dispatcher.UIThread.InvokeAsync(() => + { + ViewModel.Applications.Add(applicationData); + }); + } + + private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e) + { + AddApplication(e.AppData); + } + + private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e) + { + LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound); + + Dispatcher.UIThread.Post(() => + { + ViewModel.StatusBarProgressValue = e.NumAppsLoaded; + ViewModel.StatusBarProgressMaximum = e.NumAppsFound; + + if (e.NumAppsFound == 0) + { + StatusBarView.LoadProgressBar.IsVisible = false; + } + + if (e.NumAppsLoaded == e.NumAppsFound) + { + StatusBarView.LoadProgressBar.IsVisible = false; + } + }); + } + public void Application_Opened(object sender, ApplicationOpenedEventArgs args) { if (args.Application != null) @@ -169,50 +173,12 @@ namespace Ryujinx.Ava.UI.Windows string path = new FileInfo(args.Application.Path).FullName; - LoadApplication(path); + ViewModel.LoadApplication(path); } args.Handled = true; } - public async Task PerformanceCheck() - { - if (ConfigurationState.Instance.Logger.EnableTrace.Value) - { - string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledMessage]; - string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage]; - - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage, - LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], - LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - - if (result != UserResult.Yes) - { - ConfigurationState.Instance.Logger.EnableTrace.Value = false; - - SaveConfig(); - } - } - - if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) - { - string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledMessage]; - string secondaryMessage = - LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage]; - - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage, - LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], - LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - - if (result != UserResult.Yes) - { - ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = ""; - - SaveConfig(); - } - } - } - internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg) { _deferLoad = true; @@ -220,109 +186,6 @@ namespace Ryujinx.Ava.UI.Windows _startFullscreen = startFullscreenArg; } -#pragma warning disable CS1998 - public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "") -#pragma warning restore CS1998 - { - if (AppHost != null) - { - await ContentDialogHelper.CreateInfoDialog( - LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage], - LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage], - LocaleManager.Instance[LocaleKeys.InputDialogOk], - "", - LocaleManager.Instance[LocaleKeys.RyujinxInfo]); - - return; - } - -#if RELEASE - await PerformanceCheck(); -#endif - - Logger.RestartTime(); - - if (ViewModel.SelectedIcon == null) - { - ViewModel.SelectedIcon = ApplicationLibrary.GetApplicationIcon(path); - } - - PrepareLoadScreen(); - - _mainViewContent = MainContent.Content as Control; - - RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel); - if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl) - { - RendererControl.CreateOpenGL(); - } - else - { - RendererControl.CreateVulkan(); - } - - AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this); - - Dispatcher.UIThread.Post(async () => - { - if (!await AppHost.LoadGuestApplication()) - { - AppHost.DisposeContext(); - AppHost = null; - - return; - } - - CanUpdate = false; - ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName; - ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; - - SwitchToGameControl(startFullscreen); - - _currentEmulatedGamePath = path; - - Thread gameThread = new(InitializeGame) - { - Name = "GUI.WindowThread" - }; - gameThread.Start(); - }); - } - - private void InitializeGame() - { - RendererControl.RendererInitialized += GlRenderer_Created; - - AppHost.StatusUpdatedEvent += Update_StatusBar; - AppHost.AppExit += AppHost_AppExit; - - _rendererWaitEvent.WaitOne(); - - AppHost?.Start(); - - AppHost.DisposeContext(); - } - - - private void HandleRelaunch() - { - if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart) - { - _userChannelPersistence.ShouldRestart = false; - - Dispatcher.UIThread.Post(() => - { - LoadApplication(_currentEmulatedGamePath); - }); - } - else - { - // otherwise, clear state. - _userChannelPersistence = new UserChannelPersistence(); - _currentEmulatedGamePath = null; - } - } - public void SwitchToGameControl(bool startFullscreen = false) { ViewModel.ShowLoadProgress = false; @@ -331,14 +194,10 @@ namespace Ryujinx.Ava.UI.Windows Dispatcher.UIThread.InvokeAsync(() => { - MainContent.Content = RendererControl; - - if (startFullscreen && WindowState != WindowState.FullScreen) + if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen) { ViewModel.ToggleFullscreen(); } - - RendererControl.Focus(); }); } @@ -350,71 +209,16 @@ namespace Ryujinx.Ava.UI.Windows Dispatcher.UIThread.InvokeAsync(() => { - if (startFullscreen && WindowState != WindowState.FullScreen) + if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen) { ViewModel.ToggleFullscreen(); } }); } - private void GlRenderer_Created(object sender, EventArgs e) - { - ShowLoading(); - - _rendererWaitEvent.Set(); - } - - private void AppHost_AppExit(object sender, EventArgs e) - { - if (_isClosing) - { - return; - } - - ViewModel.IsGameRunning = false; - - Dispatcher.UIThread.InvokeAsync(() => - { - ViewModel.ShowMenuAndStatusBar = true; - ViewModel.ShowContent = true; - ViewModel.ShowLoadProgress = false; - ViewModel.IsLoadingIndeterminate = false; - CanUpdate = true; - Cursor = Cursor.Default; - - if (MainContent.Content != _mainViewContent) - { - MainContent.Content = _mainViewContent; - } - - AppHost = null; - - HandleRelaunch(); - }); - - RendererControl.RendererInitialized -= GlRenderer_Created; - RendererControl = null; - - ViewModel.SelectedIcon = null; - - Dispatcher.UIThread.InvokeAsync(() => - { - Title = $"Ryujinx {Program.Version}"; - }); - } - - public void Sort_Checked(object sender, RoutedEventArgs args) - { - if (sender is RadioButton button) - { - var sort = Enum.Parse(button.Tag.ToString()); - ViewModel.Sort(sort); - } - } - protected override void HandleWindowStateChanged(WindowState state) { - WindowState = state; + ViewModel.WindowState = state; if (state != WindowState.Minimized) { @@ -422,15 +226,6 @@ namespace Ryujinx.Ava.UI.Windows } } - public void Order_Checked(object sender, RoutedEventArgs args) - { - if (sender is RadioButton button) - { - var tag = button.Tag.ToString(); - ViewModel.Sort(tag != "Descending"); - } - } - private void Initialize() { _userChannelPersistence = new UserChannelPersistence(); @@ -457,8 +252,6 @@ namespace Ryujinx.Ava.UI.Windows VirtualFileSystem.ReloadKeySet(); ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this); - - RefreshFirmwareStatus(); } protected void CheckLaunchState() @@ -475,7 +268,7 @@ namespace Ryujinx.Ava.UI.Windows { _deferLoad = false; - LoadApplication(_launchPath, _startFullscreen); + ViewModel.LoadApplication(_launchPath, _startFullscreen); } if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false, this)) @@ -487,35 +280,9 @@ namespace Ryujinx.Ava.UI.Windows } } - public void RefreshFirmwareStatus() - { - SystemVersion version = null; - try - { - version = ContentManager.GetCurrentFirmwareVersion(); - } - catch (Exception) { } - - bool hasApplet = false; - - if (version != null) - { - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, - version.VersionString); - - hasApplet = version.Major > 3; - } - else - { - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0"); - } - - ViewModel.IsAppletMenuActive = hasApplet; - } - private void Load() { - VolumeStatus.Click += VolumeStatus_CheckedChanged; + StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged; GameGrid.ApplicationOpened += Application_Opened; @@ -535,6 +302,19 @@ namespace Ryujinx.Ava.UI.Windows CheckLaunchState(); } + private void SetMainContent(Control content = null) + { + if (content == null) + { + content = GameLibrary; + } + + if (MainContent.Content != content) + { + MainContent.Content = content; + } + } + public static void UpdateGraphicsConfig() { GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale; @@ -553,99 +333,6 @@ namespace Ryujinx.Ava.UI.Windows HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); } - public static void SaveConfig() - { - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } - - public void UpdateGameMetadata(string titleId) - { - ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => - { - if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) - { - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - - appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); - } - }); - } - - private void PrepareLoadScreen() - { - using MemoryStream stream = new MemoryStream(ViewModel.SelectedIcon); - using var gameIconBmp = SixLabors.ImageSharp.Image.Load(stream); - - var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel(); - - const int ColorDivisor = 4; - - Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B); - Color progressBgColor = Color.FromRgb( - (byte)(dominantColor.R / ColorDivisor), - (byte)(dominantColor.G / ColorDivisor), - (byte)(dominantColor.B / ColorDivisor)); - - ViewModel.ProgressBarForegroundColor = new SolidColorBrush(progressFgColor); - ViewModel.ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor); - } - - private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) - { - ViewModel.SearchText = SearchBox.Text; - } - - private async void StopEmulation_Click(object sender, RoutedEventArgs e) - { - if (AppHost != null) - { - await AppHost.ShowExitPrompt(); - } - } - - private async void PauseEmulation_Click(object sender, RoutedEventArgs e) - { - await Task.Run(() => - { - AppHost?.Pause(); - }); - } - - private async void ResumeEmulation_Click(object sender, RoutedEventArgs e) - { - await Task.Run(() => - { - AppHost?.Resume(); - }); - } - - private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) - { - if (sender is MenuItem) - { - ViewModel.IsAmiiboRequested = AppHost.Device.System.SearchingForAmiibo(out _); - } - } - - private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e) - { - AppHost.Device.EnableDeviceVsync = !AppHost.Device.EnableDeviceVsync; - - Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {AppHost.Device.EnableDeviceVsync}"); - } - - private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; - } - - private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e) - { - AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value; - - ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; - } - private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e) { var volumeSplitButton = sender as ToggleSplitButton; @@ -653,20 +340,20 @@ namespace Ryujinx.Ava.UI.Windows { if (!volumeSplitButton.IsChecked) { - AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume); + ViewModel.AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume); } else { - AppHost.Device.SetVolume(0); + ViewModel.AppHost.Device.SetVolume(0); } - ViewModel.Volume = AppHost.Device.GetVolume(); + ViewModel.Volume = ViewModel.AppHost.Device.GetVolume(); } } protected override void OnClosing(CancelEventArgs e) { - if (!_isClosing && AppHost != null && ConfigurationState.Instance.ShowConfirmExit) + if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit) { e.Cancel = true; @@ -675,14 +362,14 @@ namespace Ryujinx.Ava.UI.Windows return; } - _isClosing = true; + ViewModel.IsClosing = true; - if (AppHost != null) + if (ViewModel.AppHost != null) { - AppHost.AppExit -= AppHost_AppExit; - AppHost.AppExit += (sender, e) => + ViewModel.AppHost.AppExit -= ViewModel.AppHost_AppExit; + ViewModel.AppHost.AppExit += (sender, e) => { - AppHost = null; + ViewModel.AppHost = null; Dispatcher.UIThread.Post(() => { @@ -691,7 +378,7 @@ namespace Ryujinx.Ava.UI.Windows Close(); }); }; - AppHost?.Stop(); + ViewModel.AppHost?.Stop(); e.Cancel = true; @@ -709,13 +396,43 @@ namespace Ryujinx.Ava.UI.Windows { Dispatcher.UIThread.InvokeAsync(async () => { - _isClosing = await ContentDialogHelper.CreateExitDialog(); + ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog(); - if (_isClosing) + if (ViewModel.IsClosing) { Close(); } }); } + + public async void LoadApplications() + { + await Dispatcher.UIThread.InvokeAsync(() => + { + ViewModel.Applications.Clear(); + + StatusBarView.LoadProgressBar.IsVisible = true; + ViewModel.StatusBarProgressMaximum = 0; + ViewModel.StatusBarProgressValue = 0; + + LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); + }); + + ReloadGameList(); + } + + private void ReloadGameList() + { + if (_isLoading) + { + return; + } + + _isLoading = true; + + ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language); + + _isLoading = false; + } } } \ No newline at end of file From 6f60e102a29aa04fff5c6bcdec37213b2d8ac646 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 8 Jan 2023 15:34:49 -0300 Subject: [PATCH 251/737] HIPC: Fix reply possibly also receiving one request (#4232) --- Ryujinx.Horizon.Common/Result.cs | 8 -------- Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs | 8 ++------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/Ryujinx.Horizon.Common/Result.cs b/Ryujinx.Horizon.Common/Result.cs index ac8436888..28056310f 100644 --- a/Ryujinx.Horizon.Common/Result.cs +++ b/Ryujinx.Horizon.Common/Result.cs @@ -100,14 +100,6 @@ namespace Ryujinx.Horizon.Common } } - public void AbortOnFailureUnless(Result result, Result result2) - { - if (this != Success && this != result && this != result2) - { - ThrowInvalidResult(); - } - } - private void ThrowInvalidResult() { throw new InvalidResultException(this); diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs index 822a4b4a6..33c42825f 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs @@ -51,22 +51,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { Result result = ReplyImpl(sessionHandle, messageBuffer); - result.AbortOnFailureUnless(KernelResult.TimedOut, KernelResult.PortRemoteClosed); + result.AbortUnless(KernelResult.TimedOut, KernelResult.PortRemoteClosed); return Result.Success; } private static Result ReplyImpl(int sessionHandle, ReadOnlySpan messageBuffer) { - Span handles = stackalloc int[1]; - - handles[0] = sessionHandle; - var tlsSpan = HorizonStatic.AddressSpace.GetSpan(HorizonStatic.ThreadContext.TlsAddress, TlsMessageBufferSize); if (messageBuffer == tlsSpan) { - return HorizonStatic.Syscall.ReplyAndReceive(out _, handles, sessionHandle, 0); + return HorizonStatic.Syscall.ReplyAndReceive(out _, ReadOnlySpan.Empty, sessionHandle, 0); } else { From ee6e682ab426fb9d1b15e0df73d9c1e26c4cc4b3 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Sun, 8 Jan 2023 21:36:11 -0500 Subject: [PATCH 252/737] Fix selection bar (#4236) --- Ryujinx.Ava/UI/Controls/GameGridView.axaml | 3 +++ Ryujinx.Ava/UI/Controls/GameListView.axaml | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml b/Ryujinx.Ava/UI/Controls/GameGridView.axaml index 1c4d7638e..02e8f0f8f 100644 --- a/Ryujinx.Ava/UI/Controls/GameGridView.axaml +++ b/Ryujinx.Ava/UI/Controls/GameGridView.axaml @@ -139,6 +139,9 @@ + diff --git a/Ryujinx.Ava/UI/Controls/GameListView.axaml b/Ryujinx.Ava/UI/Controls/GameListView.axaml index d886ecbea..018b06098 100644 --- a/Ryujinx.Ava/UI/Controls/GameListView.axaml +++ b/Ryujinx.Ava/UI/Controls/GameListView.axaml @@ -137,12 +137,12 @@ - + From 492056abf6c97696075f16101009aa463c319832 Mon Sep 17 00:00:00 2001 From: merry Date: Mon, 9 Jan 2023 03:32:20 +0000 Subject: [PATCH 253/737] Ava: Make Avalonia use our logging system (#4231) * Ava: Make Avalonia use our logging system * LoggerAdapter: Address review comments * Update Ryujinx.Common/Logging/LogClass.cs Co-authored-by: Ac_K --- Ryujinx.Ava/Helper/LoggerAdapter.cs | 111 ++++++++++++++++++++++++++++ Ryujinx.Ava/Program.cs | 6 +- Ryujinx.Common/Logging/LogClass.cs | 3 +- 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 Ryujinx.Ava/Helper/LoggerAdapter.cs diff --git a/Ryujinx.Ava/Helper/LoggerAdapter.cs b/Ryujinx.Ava/Helper/LoggerAdapter.cs new file mode 100644 index 000000000..c8f3fea14 --- /dev/null +++ b/Ryujinx.Ava/Helper/LoggerAdapter.cs @@ -0,0 +1,111 @@ +using Avalonia.Utilities; +using System; +using System.Text; + +namespace Ryujinx.Ava.UI.Helper +{ + using AvaLogger = Avalonia.Logging.Logger; + using AvaLogLevel = Avalonia.Logging.LogEventLevel; + using RyuLogger = Ryujinx.Common.Logging.Logger; + using RyuLogClass = Ryujinx.Common.Logging.LogClass; + + internal class LoggerAdapter : Avalonia.Logging.ILogSink + { + public static void Register() + { + AvaLogger.Sink = new LoggerAdapter(); + } + + private static RyuLogger.Log? GetLog(AvaLogLevel level) + { + return level switch + { + AvaLogLevel.Verbose => RyuLogger.Trace, + AvaLogLevel.Debug => RyuLogger.Debug, + AvaLogLevel.Information => RyuLogger.Info, + AvaLogLevel.Warning => RyuLogger.Warning, + AvaLogLevel.Error => RyuLogger.Error, + AvaLogLevel.Fatal => RyuLogger.Notice, + _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) + }; + } + + public bool IsEnabled(AvaLogLevel level, string area) + { + return GetLog(level) != null; + } + + public void Log(AvaLogLevel level, string area, object source, string messageTemplate) + { + GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, null)); + } + + public void Log(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0) + { + GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0 })); + } + + public void Log(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0, propertyValue1 })); + } + + public void Log(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0, propertyValue1, propertyValue2 })); + } + + public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues) + { + GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, propertyValues)); + } + + private static string Format(string area, string template, object source, object[] v) + { + var result = new StringBuilder(); + var r = new CharacterReader(template.AsSpan()); + var i = 0; + + result.Append('['); + result.Append(area); + result.Append("] "); + + while (!r.End) + { + var c = r.Take(); + + if (c != '{') + { + result.Append(c); + } + else + { + if (r.Peek != '{') + { + result.Append('\''); + result.Append(i < v.Length ? v[i++] : null); + result.Append('\''); + r.TakeUntil('}'); + r.Take(); + } + else + { + result.Append('{'); + r.Take(); + } + } + } + + if (source != null) + { + result.Append(" ("); + result.Append(source.GetType().Name); + result.Append(" #"); + result.Append(source.GetHashCode()); + result.Append(')'); + } + + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 5d88466e8..1dc1a7bce 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Threading; +using Ryujinx.Ava.UI.Helper; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -89,6 +90,8 @@ namespace Ryujinx.Ava Initialize(args); + LoggerAdapter.Register(); + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } @@ -110,8 +113,7 @@ namespace Ryujinx.Ava AllowEglInitialization = false, CompositionBackdropCornerRadius = 8.0f, }) - .UseSkia() - .LogToTrace(); + .UseSkia(); } private static void Initialize(string[] args) diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 2e936fc72..7e53c972b 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -7,9 +7,9 @@ namespace Ryujinx.Common.Logging AudioRenderer, Configuration, Cpu, - Font, Emulation, FFmpeg, + Font, Gpu, Hid, Host1x, @@ -66,6 +66,7 @@ namespace Ryujinx.Common.Logging ServiceVi, SurfaceFlinger, TamperMachine, + Ui, Vic } } \ No newline at end of file From 610eecc1c1fec2203fff1ebd71cd10798fbcc05a Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 9 Jan 2023 04:37:20 +0100 Subject: [PATCH 254/737] ava: Fixes regressions from refactoring (#4237) * ava: Fix regressions from #4178 * Remove duplicated code * real fix for right click menu Co-Authored-By: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> * Remove ContentDialogOverlay Co-authored-by: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> --- Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs | 4 +- Ryujinx.Ava/UI/Controls/GameListView.axaml.cs | 4 +- Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs | 333 +++++++------ .../UI/ViewModels/MainWindowViewModel.cs | 5 +- .../UI/Views/Main/MainMenuBarView.axaml.cs | 5 +- .../UI/Views/Main/MainStatusBarView.axaml.cs | 2 +- .../UI/Views/Main/MainViewControls.axaml.cs | 2 +- Ryujinx.Ava/UI/Windows/AboutWindow.axaml | 460 +++++++++--------- Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs | 11 +- 9 files changed, 427 insertions(+), 399 deletions(-) diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs b/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs index 9965f7509..531b54357 100644 --- a/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs @@ -38,9 +38,9 @@ namespace Ryujinx.Ava.UI.Controls { if (sender is ListBox listBox) { - var selected = listBox.SelectedItem as ApplicationData; + _selectedApplication = listBox.SelectedItem as ApplicationData; - _selectedApplication = selected; + (DataContext as MainWindowViewModel).GridSelectedApplication = _selectedApplication; } } diff --git a/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs b/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs index 01e35990d..bded1dec7 100644 --- a/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs @@ -38,9 +38,9 @@ namespace Ryujinx.Ava.UI.Controls { if (sender is ListBox listBox) { - var selected = listBox.SelectedItem as ApplicationData; + _selectedApplication = listBox.SelectedItem as ApplicationData; - _selectedApplication = selected; + (DataContext as MainWindowViewModel).ListSelectedApplication = _selectedApplication; } } diff --git a/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index a098a8eae..f4334b5f6 100644 --- a/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -19,7 +19,56 @@ namespace Ryujinx.Ava.UI.Helpers { private static bool _isChoiceDialogOpen; - private async static Task ShowContentDialog( + public async static Task ShowContentDialog( + string title, + object content, + string primaryButton, + string secondaryButton, + string closeButton, + UserResult primaryButtonResult = UserResult.Ok, + ManualResetEvent deferResetEvent = null, + Func doWhileDeferred = null, + TypedEventHandler deferCloseAction = null) + { + UserResult result = UserResult.None; + + ContentDialog contentDialog = new() + { + Title = title, + PrimaryButtonText = primaryButton, + SecondaryButtonText = secondaryButton, + CloseButtonText = closeButton, + Content = content + }; + + contentDialog.PrimaryButtonCommand = MiniCommand.Create(() => + { + result = primaryButtonResult; + }); + + contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => + { + result = UserResult.No; + contentDialog.PrimaryButtonClick -= deferCloseAction; + }); + + contentDialog.CloseButtonCommand = MiniCommand.Create(() => + { + result = UserResult.Cancel; + contentDialog.PrimaryButtonClick -= deferCloseAction; + }); + + if (deferResetEvent != null) + { + contentDialog.PrimaryButtonClick += deferCloseAction; + } + + await ShowAsync(contentDialog); + + return result; + } + + private async static Task ShowTextDialog( string title, string primaryText, string secondaryText, @@ -32,119 +81,9 @@ namespace Ryujinx.Ava.UI.Helpers Func doWhileDeferred = null, TypedEventHandler deferCloseAction = null) { - UserResult result = UserResult.None; + Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol); - bool useOverlay = false; - Window mainWindow = null; - - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al) - { - foreach (var item in al.Windows) - { - if (item.IsActive && item is MainWindow window && window.ViewModel.IsGameRunning) - { - mainWindow = window; - useOverlay = true; - break; - } - } - } - - ContentDialog contentDialog = null; - ContentDialogOverlayWindow overlay = null; - - if (useOverlay) - { - overlay = new ContentDialogOverlayWindow() - { - Height = mainWindow.Bounds.Height, - Width = mainWindow.Bounds.Width, - Position = mainWindow.PointToScreen(new Point()) - }; - - mainWindow.PositionChanged += OverlayOnPositionChanged; - - void OverlayOnPositionChanged(object sender, PixelPointEventArgs e) - { - overlay.Position = mainWindow.PointToScreen(new Point()); - } - - contentDialog = overlay.ContentDialog; - - bool opened = false; - - overlay.Opened += OverlayOnActivated; - - async void OverlayOnActivated(object sender, EventArgs e) - { - if (opened) - { - return; - } - - opened = true; - - overlay.Position = mainWindow.PointToScreen(new Point()); - - await ShowDialog(); - } - - await overlay.ShowDialog(mainWindow); - } - else - { - contentDialog = new ContentDialog(); - - await ShowDialog(); - } - - async Task ShowDialog() - { - contentDialog.Title = title; - contentDialog.PrimaryButtonText = primaryButton; - contentDialog.SecondaryButtonText = secondaryButton; - contentDialog.CloseButtonText = closeButton; - contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol); - - contentDialog.PrimaryButtonCommand = MiniCommand.Create(() => - { - result = primaryButtonResult; - }); - contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => - { - result = UserResult.No; - contentDialog.PrimaryButtonClick -= deferCloseAction; - }); - contentDialog.CloseButtonCommand = MiniCommand.Create(() => - { - result = UserResult.Cancel; - contentDialog.PrimaryButtonClick -= deferCloseAction; - }); - - if (deferResetEvent != null) - { - contentDialog.PrimaryButtonClick += deferCloseAction; - } - - if (useOverlay) - { - await contentDialog.ShowAsync(overlay, ContentDialogPlacement.Popup); - - overlay!.Close(); - } - else - { - await contentDialog.ShowAsync(ContentDialogPlacement.Popup); - } - } - - if (useOverlay) - { - overlay.Content = null; - overlay.Close(); - } - - return result; + return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction); } public async static Task ShowDeferredContentDialog( @@ -162,7 +101,7 @@ namespace Ryujinx.Ava.UI.Helpers bool startedDeferring = false; UserResult result = UserResult.None; - return await ShowContentDialog( + return await ShowTextDialog( title, primaryText, secondaryText, @@ -192,8 +131,7 @@ namespace Ryujinx.Ava.UI.Helpers sender.PrimaryButtonClick -= DeferClose; -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - Task.Run(() => + _ = Task.Run(() => { deferResetEvent.WaitOne(); @@ -202,7 +140,6 @@ namespace Ryujinx.Ava.UI.Helpers deferral.Complete(); }); }); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed if (doWhileDeferred != null) { @@ -213,34 +150,42 @@ namespace Ryujinx.Ava.UI.Helpers } } - private static Grid CreateDialogTextContent(string primaryText, string secondaryText, int symbol) + private static Grid CreateTextDialogContent(string primaryText, string secondaryText, int symbol) { - Grid content = new Grid(); - content.RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() }; - content.ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() }; + Grid content = new() + { + RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() }, + ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() }, - content.MinHeight = 80; + MinHeight = 80 + }; + + SymbolIcon icon = new() + { + Symbol = (Symbol)symbol, + Margin = new Thickness(10), + FontSize = 40, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center + }; - SymbolIcon icon = new SymbolIcon { Symbol = (Symbol)symbol, Margin = new Thickness(10) }; - icon.FontSize = 40; - icon.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center; Grid.SetColumn(icon, 0); Grid.SetRowSpan(icon, 2); Grid.SetRow(icon, 0); - TextBlock primaryLabel = new TextBlock() + TextBlock primaryLabel = new() { - Text = primaryText, - Margin = new Thickness(5), + Text = primaryText, + Margin = new Thickness(5), TextWrapping = TextWrapping.Wrap, - MaxWidth = 450 + MaxWidth = 450 }; - TextBlock secondaryLabel = new TextBlock() + + TextBlock secondaryLabel = new() { - Text = secondaryText, - Margin = new Thickness(5), + Text = secondaryText, + Margin = new Thickness(5), TextWrapping = TextWrapping.Wrap, - MaxWidth = 450 + MaxWidth = 450 }; Grid.SetColumn(primaryLabel, 1); @@ -262,7 +207,7 @@ namespace Ryujinx.Ava.UI.Helpers string closeButton, string title) { - return await ShowContentDialog( + return await ShowTextDialog( title, primary, secondaryText, @@ -280,7 +225,7 @@ namespace Ryujinx.Ava.UI.Helpers string title, UserResult primaryButtonResult = UserResult.Yes) { - return await ShowContentDialog( + return await ShowTextDialog( string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title, primaryText, secondaryText, @@ -298,7 +243,7 @@ namespace Ryujinx.Ava.UI.Helpers internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText) { - await ShowContentDialog( + await ShowTextDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle], primary, secondaryText, @@ -310,7 +255,7 @@ namespace Ryujinx.Ava.UI.Helpers internal static async Task CreateWarningDialog(string primary, string secondaryText) { - await ShowContentDialog( + await ShowTextDialog( LocaleManager.Instance[LocaleKeys.DialogWarningTitle], primary, secondaryText, @@ -324,7 +269,7 @@ namespace Ryujinx.Ava.UI.Helpers { Logger.Error?.Print(LogClass.Application, errorMessage); - await ShowContentDialog( + await ShowTextDialog( LocaleManager.Instance[LocaleKeys.DialogErrorTitle], LocaleManager.Instance[LocaleKeys.DialogErrorMessage], errorMessage, @@ -343,16 +288,15 @@ namespace Ryujinx.Ava.UI.Helpers _isChoiceDialogOpen = true; - UserResult response = - await ShowContentDialog( - title, - primary, - secondaryText, - LocaleManager.Instance[LocaleKeys.InputDialogYes], - "", - LocaleManager.Instance[LocaleKeys.InputDialogNo], - (int)Symbol.Help, - UserResult.Yes); + UserResult response = await ShowTextDialog( + title, + primary, + secondaryText, + LocaleManager.Instance[LocaleKeys.InputDialogYes], + "", + LocaleManager.Instance[LocaleKeys.InputDialogNo], + (int)Symbol.Help, + UserResult.Yes); _isChoiceDialogOpen = false; @@ -396,5 +340,98 @@ namespace Ryujinx.Ava.UI.Helpers return string.Empty; } + + public static async Task ShowAsync(ContentDialog contentDialog) + { + ContentDialogResult result; + + ContentDialogOverlayWindow contentDialogOverlayWindow = null; + + Window parent = GetMainWindow(); + + if (parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning) + { + contentDialogOverlayWindow = new() + { + Height = parent.Bounds.Height, + Width = parent.Bounds.Width, + Position = parent.PointToScreen(new Point()), + ShowInTaskbar = false + }; + + parent.PositionChanged += OverlayOnPositionChanged; + + void OverlayOnPositionChanged(object sender, PixelPointEventArgs e) + { + contentDialogOverlayWindow.Position = parent.PointToScreen(new Point()); + } + + contentDialogOverlayWindow.ContentDialog = contentDialog; + + bool opened = false; + + contentDialogOverlayWindow.Opened += OverlayOnActivated; + + async void OverlayOnActivated(object sender, EventArgs e) + { + if (opened) + { + return; + } + + opened = true; + + contentDialogOverlayWindow.Position = parent.PointToScreen(new Point()); + + result = await ShowDialog(); + } + + result = await contentDialogOverlayWindow.ShowDialog(parent); + } + else + { + result = await ShowDialog(); + } + + async Task ShowDialog() + { + if (contentDialogOverlayWindow is not null) + { + result = await contentDialog.ShowAsync(contentDialogOverlayWindow); + + contentDialogOverlayWindow!.Close(); + } + else + { + result = await contentDialog.ShowAsync(); + } + + return result; + } + + if (contentDialogOverlayWindow is not null) + { + contentDialogOverlayWindow.Content = null; + contentDialogOverlayWindow.Close(); + } + + return result; + } + + private static Window GetMainWindow() + { + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al) + { + foreach (Window item in al.Windows) + { + if (item.IsActive && item is MainWindow window) + { + return window; + } + } + } + + return null; + } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 9571af477..073c4902d 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -87,8 +87,6 @@ namespace Ryujinx.Ava.UI.ViewModels private float _volume; private string _backendText; - public ApplicationData ListSelectedApplication; - public ApplicationData GridSelectedApplication; private bool _canUpdate; private Cursor _cursor; private string _title; @@ -97,6 +95,9 @@ namespace Ryujinx.Ava.UI.ViewModels private WindowState _windowState; private bool _isActive; + public ApplicationData ListSelectedApplication; + public ApplicationData GridSelectedApplication; + public event Action ReloadGameList; private string TitleName { get; set; } diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 8c28abffe..788f47a1a 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -36,10 +36,7 @@ namespace Ryujinx.Ava.UI.Views.Main private async void StopEmulation_Click(object sender, RoutedEventArgs e) { - await Task.Run(() => - { - Window.ViewModel.AppHost?.ShowExitPrompt(); - }); + await Window.ViewModel.AppHost?.ShowExitPrompt(); } private async void PauseEmulation_Click(object sender, RoutedEventArgs e) diff --git a/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs index d1050dddf..473de0ee2 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Main { base.OnAttachedToVisualTree(e); - if (this.VisualRoot is MainWindow window) + if (VisualRoot is MainWindow window) { Window = window; } diff --git a/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs index 841d59dec..fd7578939 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Main { base.OnAttachedToVisualTree(e); - if (this.VisualRoot is MainWindow window) + if (VisualRoot is MainWindow window) { ViewModel = window.ViewModel; } diff --git a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml b/Ryujinx.Ava/UI/Windows/AboutWindow.axaml index f446890cc..7bd3e20d3 100644 --- a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/AboutWindow.axaml @@ -1,253 +1,247 @@ - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs index 5dbbbcdd7..36a28605f 100644 --- a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Interactivity; using Avalonia.Styling; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ui.Common.Helper; using System.Threading.Tasks; @@ -22,14 +23,12 @@ namespace Ryujinx.Ava.UI.Windows public static async Task Show() { - var content = new AboutWindow(); - ContentDialog contentDialog = new() { - PrimaryButtonText = "", + PrimaryButtonText = "", SecondaryButtonText = "", - CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], - Content = content + CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], + Content = new AboutWindow() }; Style closeButton = new(x => x.Name("CloseButton")); @@ -41,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows contentDialog.Styles.Add(closeButton); contentDialog.Styles.Add(closeButtonParent); - await contentDialog.ShowAsync(); + await ContentDialogHelper.ShowAsync(contentDialog); } private void Button_OnClick(object sender, RoutedEventArgs e) From 51b3953cfc3b440aa0b7b1b06d33626426e54556 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 9 Jan 2023 04:55:37 +0100 Subject: [PATCH 255/737] [Headless] Add missing arguments & Fix typos (#4193) * headless: Fix typos in command line options * Remove nullable from command line options Add EnableMacroHLE option Add HideCursorOnIdle option * headless: Adjust enable-ptc help text * headless: Use switch statement instead of if-else chain * headless: Improve formatting for long constructors * headless: Remove discards from SDL_ShowCursor() * headless: Add window icon * Fix hiding cursor on idle At least on Wayland, SDL2 doesn't produce any mouse motion events. * Add new command line args: BaseDataDir and UserProfile * headless: Read icon from embedded resource * headless: Skip SetWindowIcon() on Windows if dll isn't present * headless: Fix division by zero * headless: Fix command line options not working correctly * headless: Fix crash when viewing command line options * headless: Load window icon bmp from memory * Add comment to the workaround for SDL_LoadBMP_RW * headless: Enable logging to file by default * headless: Add 3 options for --hide-cursor Replaces --disable-hide-cursor-on-idle --- Ryujinx.Headless.SDL2/HideCursor.cs | 9 ++ Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs | 11 ++- Ryujinx.Headless.SDL2/Options.cs | 82 +++++++++------- Ryujinx.Headless.SDL2/Program.cs | 90 +++++++++--------- .../Ryujinx.Headless.SDL2.csproj | 4 + Ryujinx.Headless.SDL2/Ryujinx.bmp | Bin 0 -> 1122442 bytes Ryujinx.Headless.SDL2/SDL2MouseDriver.cs | 87 ++++++++++++++--- Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs | 10 +- Ryujinx.Headless.SDL2/WindowBase.cs | 55 ++++++++++- Ryujinx.Input/HLE/NpadManager.cs | 2 +- 10 files changed, 248 insertions(+), 102 deletions(-) create mode 100644 Ryujinx.Headless.SDL2/HideCursor.cs create mode 100644 Ryujinx.Headless.SDL2/Ryujinx.bmp diff --git a/Ryujinx.Headless.SDL2/HideCursor.cs b/Ryujinx.Headless.SDL2/HideCursor.cs new file mode 100644 index 000000000..2dc0bd6ab --- /dev/null +++ b/Ryujinx.Headless.SDL2/HideCursor.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Headless.SDL2 +{ + public enum HideCursor + { + Never, + OnIdle, + Always + } +} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs index d1d0872b3..69b0f42fb 100644 --- a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs @@ -5,7 +5,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.OpenGL; using Ryujinx.Input.HLE; using System; - using static SDL2.SDL; namespace Ryujinx.Headless.SDL2.OpenGL @@ -103,7 +102,13 @@ namespace Ryujinx.Headless.SDL2.OpenGL private GraphicsDebugLevel _glLogLevel; private SDL2OpenGLContext _openGLContext; - public OpenGLWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse) + public OpenGLWindow( + InputManager inputManager, + GraphicsDebugLevel glLogLevel, + AspectRatio aspectRatio, + bool enableMouse, + HideCursor hideCursor) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor) { _glLogLevel = glLogLevel; } @@ -161,4 +166,4 @@ namespace Ryujinx.Headless.SDL2.OpenGL SDL_GL_SwapWindow(WindowHandle); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/Options.cs b/Ryujinx.Headless.SDL2/Options.cs index 209ce2288..49233bcea 100644 --- a/Ryujinx.Headless.SDL2/Options.cs +++ b/Ryujinx.Headless.SDL2/Options.cs @@ -6,6 +6,14 @@ namespace Ryujinx.Headless.SDL2 { public class Options { + // General + + [Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")] + public string BaseDataDir { get; set; } + + [Option("profile", Required = false, HelpText = "Set the user profile to launch the game with.")] + public string UserProfile { get; set; } + // Input [Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")] @@ -23,7 +31,7 @@ namespace Ryujinx.Headless.SDL2 [Option("input-profile-5", Required = false, HelpText = "Set the input profile in use for Player 5.")] public string InputProfile5Name { get; set; } - [Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 5.")] + [Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 6.")] public string InputProfile6Name { get; set; } [Option("input-profile-7", Required = false, HelpText = "Set the input profile in use for Player 7.")] @@ -63,42 +71,45 @@ namespace Ryujinx.Headless.SDL2 public string InputIdHandheld { get; set; } [Option("enable-keyboard", Required = false, Default = false, HelpText = "Enable or disable keyboard support (Independent from controllers binding).")] - public bool? EnableKeyboard { get; set; } + public bool EnableKeyboard { get; set; } [Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")] - public bool? EnableMouse { get; set; } + public bool EnableMouse { get; set; } + + [Option("hide-cursor", Required = false, Default = HideCursor.OnIdle, HelpText = "Change when the cursor gets hidden.")] + public HideCursor HideCursor { get; set; } [Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")] - public bool? ListInputProfiles { get; set; } + public bool ListInputProfiles { get; set; } [Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")] public bool ListInputIds { get; set; } // System - [Option("enable-ptc", Required = false, Default = true, HelpText = "Enables profiled translation cache persistency.")] - public bool? EnablePtc { get; set; } + [Option("disable-ptc", Required = false, HelpText = "Disables profiled persistent translation cache.")] + public bool DisablePtc { get; set; } [Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")] - public bool? EnableInternetAccess { get; set; } + public bool EnableInternetAccess { get; set; } - [Option("enable-fs-integrity-checks", Required = false, Default = true, HelpText = "Enables integrity checks on Game content files.")] - public bool? EnableFsIntegrityChecks { get; set; } + [Option("disable-fs-integrity-checks", Required = false, HelpText = "Disables integrity checks on Game content files.")] + public bool DisableFsIntegrityChecks { get; set; } [Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")] public int FsGlobalAccessLogMode { get; set; } - [Option("enable-vsync", Required = false, Default = true, HelpText = "Enables Vertical Sync.")] - public bool? EnableVsync { get; set; } + [Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")] + public bool DisableVsync { get; set; } - [Option("enable-shader-cache", Required = false, Default = true, HelpText = "Enables Shader cache.")] - public bool? EnableShaderCache { get; set; } + [Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")] + public bool DisableShaderCache { get; set; } [Option("enable-texture-recompression", Required = false, Default = false, HelpText = "Enables Texture recompression.")] - public bool? EnableTextureRecompression { get; set; } + public bool EnableTextureRecompression { get; set; } - [Option("enable-docked-mode", Required = false, Default = true, HelpText = "Enables Docked Mode.")] - public bool? EnableDockedMode { get; set; } + [Option("disable-docked-mode", Required = false, HelpText = "Disables Docked Mode.")] + public bool DisableDockedMode { get; set; } [Option("system-language", Required = false, Default = SystemLanguage.AmericanEnglish, HelpText = "Change System Language.")] public SystemLanguage SystemLanguage { get; set; } @@ -120,32 +131,32 @@ namespace Ryujinx.Headless.SDL2 // Logging - [Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")] - public bool? EnableFileLog { get; set; } + [Option("disable-file-logging", Required = false, Default = false, HelpText = "Disables logging to a file on disk.")] + public bool DisableFileLog { get; set; } [Option("enable-debug-logs", Required = false, Default = false, HelpText = "Enables printing debug log messages.")] - public bool? LoggingEnableDebug { get; set; } + public bool LoggingEnableDebug { get; set; } - [Option("enable-stub-logs", Required = false, Default = true, HelpText = "Enables printing stub log messages.")] - public bool? LoggingEnableStub { get; set; } + [Option("disable-stub-logs", Required = false, HelpText = "Disables printing stub log messages.")] + public bool LoggingDisableStub { get; set; } - [Option("enable-info-logs", Required = false, Default = true, HelpText = "Enables printing info log messages.")] - public bool? LoggingEnableInfo { get; set; } + [Option("disable-info-logs", Required = false, HelpText = "Disables printing info log messages.")] + public bool LoggingDisableInfo { get; set; } - [Option("enable-warning-logs", Required = false, Default = true, HelpText = "Enables printing warning log messages.")] - public bool? LoggingEnableWarning { get; set; } + [Option("disable-warning-logs", Required = false, HelpText = "Disables printing warning log messages.")] + public bool LoggingDisableWarning { get; set; } - [Option("enable-error-logs", Required = false, Default = true, HelpText = "Enables printing error log messages.")] - public bool? LoggingEnableError { get; set; } + [Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")] + public bool LoggingEnableError { get; set; } [Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")] - public bool? LoggingEnableTrace { get; set; } + public bool LoggingEnableTrace { get; set; } - [Option("enable-guest-logs", Required = false, Default = true, HelpText = "Enables printing guest log messages.")] - public bool? LoggingEnableGuest { get; set; } + [Option("disable-guest-logs", Required = false, HelpText = "Disables printing guest log messages.")] + public bool LoggingDisableGuest { get; set; } [Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")] - public bool? LoggingEnableFsAccessLog { get; set; } + public bool LoggingEnableFsAccessLog { get; set; } [Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")] public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; } @@ -164,6 +175,9 @@ namespace Ryujinx.Headless.SDL2 [Option("backend-threading", Required = false, Default = BackendThreading.Auto, HelpText = "Whether or not backend threading is enabled. The \"Auto\" setting will determine whether threading should be enabled at runtime.")] public BackendThreading BackendThreading { get; set; } + [Option("disable-macro-hle", Required= false, HelpText = "Disables high-level emulation of Macro code. Leaving this enabled improves performance but may cause graphical glitches in some games.")] + public bool DisableMacroHLE { get; set; } + [Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")] public string GraphicsShadersDumpPath { get; set; } @@ -176,14 +190,14 @@ namespace Ryujinx.Headless.SDL2 // Hacks [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")] - public bool? ExpandRam { get; set; } + public bool ExpandRam { get; set; } [Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] - public bool? IgnoreMissingServices { get; set; } + public bool IgnoreMissingServices { get; set; } // Values [Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)] public string InputPath { get; set; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 84363e1fb..6ea3a98d7 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -33,7 +33,6 @@ using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading; - using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; using Key = Ryujinx.Common.Configuration.Hid.Key; @@ -63,20 +62,6 @@ namespace Ryujinx.Headless.SDL2 Console.Title = $"Ryujinx Console {Version} (Headless SDL2)"; - AppDataManager.Initialize(null); - - _virtualFileSystem = VirtualFileSystem.CreateInstance(); - _libHacHorizonManager = new LibHacHorizonManager(); - - _libHacHorizonManager.InitializeFsServer(_virtualFileSystem); - _libHacHorizonManager.InitializeArpServer(); - _libHacHorizonManager.InitializeBcatServer(); - _libHacHorizonManager.InitializeSystemClients(); - - _contentManager = new ContentManager(_virtualFileSystem); - _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient); - _userChannelPersistence = new UserChannelPersistence(); - if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) { AutoResetEvent invoked = new AutoResetEvent(false); @@ -97,15 +82,9 @@ namespace Ryujinx.Headless.SDL2 }; } - _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); - - GraphicsConfig.EnableShaderCache = true; - Parser.Default.ParseArguments(args) - .WithParsed(options => Load(options)) + .WithParsed(Load) .WithNotParsed(errors => errors.Output()); - - _inputManager.Dispose(); } private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) @@ -343,6 +322,24 @@ namespace Ryujinx.Headless.SDL2 static void Load(Options option) { + AppDataManager.Initialize(option.BaseDataDir); + + _virtualFileSystem = VirtualFileSystem.CreateInstance(); + _libHacHorizonManager = new LibHacHorizonManager(); + + _libHacHorizonManager.InitializeFsServer(_virtualFileSystem); + _libHacHorizonManager.InitializeArpServer(); + _libHacHorizonManager.InitializeBcatServer(); + _libHacHorizonManager.InitializeSystemClients(); + + _contentManager = new ContentManager(_virtualFileSystem); + _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile); + _userChannelPersistence = new UserChannelPersistence(); + + _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); + + GraphicsConfig.EnableShaderCache = true; + IGamepad gamepad; if (option.ListInputIds) @@ -378,8 +375,8 @@ namespace Ryujinx.Headless.SDL2 } _inputConfiguration = new List(); - _enableKeyboard = (bool)option.EnableKeyboard; - _enableMouse = (bool)option.EnableMouse; + _enableKeyboard = option.EnableKeyboard; + _enableMouse = option.EnableMouse; void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) { @@ -407,16 +404,16 @@ namespace Ryujinx.Headless.SDL2 } // Setup logging level - Logger.SetEnable(LogLevel.Debug, (bool)option.LoggingEnableDebug); - Logger.SetEnable(LogLevel.Stub, (bool)option.LoggingEnableStub); - Logger.SetEnable(LogLevel.Info, (bool)option.LoggingEnableInfo); - Logger.SetEnable(LogLevel.Warning, (bool)option.LoggingEnableWarning); - Logger.SetEnable(LogLevel.Error, (bool)option.LoggingEnableError); - Logger.SetEnable(LogLevel.Trace, (bool)option.LoggingEnableTrace); - Logger.SetEnable(LogLevel.Guest, (bool)option.LoggingEnableGuest); - Logger.SetEnable(LogLevel.AccessLog, (bool)option.LoggingEnableFsAccessLog); + Logger.SetEnable(LogLevel.Debug, option.LoggingEnableDebug); + Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub); + Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo); + Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning); + Logger.SetEnable(LogLevel.Error, option.LoggingEnableError); + Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace); + Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest); + Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog); - if ((bool)option.EnableFileLog) + if (!option.DisableFileLog) { Logger.AddTarget(new AsyncLogTargetWrapper( new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"), @@ -426,11 +423,12 @@ namespace Ryujinx.Headless.SDL2 } // Setup graphics configuration - GraphicsConfig.EnableShaderCache = (bool)option.EnableShaderCache; - GraphicsConfig.EnableTextureRecompression = (bool)option.EnableTextureRecompression; + GraphicsConfig.EnableShaderCache = !option.DisableShaderCache; + GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression; GraphicsConfig.ResScale = option.ResScale; GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy; GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; + GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; while (true) { @@ -443,6 +441,8 @@ namespace Ryujinx.Headless.SDL2 _userChannelPersistence.ShouldRestart = false; } + + _inputManager.Dispose(); } private static void SetupProgressHandler() @@ -479,8 +479,8 @@ namespace Ryujinx.Headless.SDL2 private static WindowBase CreateWindow(Options options) { return options.GraphicsBackend == GraphicsBackend.Vulkan - ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse) - : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse); + ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor) + : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor); } private static IRenderer CreateRenderer(Options options, WindowBase window) @@ -533,20 +533,20 @@ namespace Ryujinx.Headless.SDL2 _userChannelPersistence, renderer, new SDL2HardwareDeviceDriver(), - (bool)options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, + options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, window, options.SystemLanguage, options.SystemRegion, - (bool)options.EnableVsync, - (bool)options.EnableDockedMode, - (bool)options.EnablePtc, - (bool)options.EnableInternetAccess, - (bool)options.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + !options.DisableVsync, + !options.DisableDockedMode, + !options.DisablePtc, + options.EnableInternetAccess, + !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, options.FsGlobalAccessLogMode, options.SystemTimeOffset, options.SystemTimeZone, options.MemoryManagerMode, - (bool)options.IgnoreMissingServices, + options.IgnoreMissingServices, options.AspectRatio, options.AudioVolume); @@ -649,7 +649,7 @@ namespace Ryujinx.Headless.SDL2 } else { - Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); + Logger.Warning?.Print(LogClass.Application, $"Couldn't load '{options.InputPath}'. Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); _emulationContext.Dispose(); diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index 44a162051..81ef53fe7 100644 --- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -42,6 +42,10 @@ + + + + false diff --git a/Ryujinx.Headless.SDL2/Ryujinx.bmp b/Ryujinx.Headless.SDL2/Ryujinx.bmp new file mode 100644 index 0000000000000000000000000000000000000000..413f3b21bb8af18fa2e47aa2468df02546dd359a GIT binary patch literal 1122442 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
    ;ISff6)c9rVC*GVXglT5c>a_lbvi0m&as& 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 XF#!7i9{=I{<8i5;p 0 && rawButton <= (int)MouseButton.Count) + CheckIdle(); + } + + private void CheckIdle() + { + if (_hideCursor != HideCursor.OnIdle) + { + return; + } + + long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; + + if (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) + { + if (!_isHidden) { - PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN; - - CurrentPosition = new Vector2(evnt.button.x, evnt.button.y); + SDL_ShowCursor(SDL_DISABLE); + _isHidden = true; } } - else if (evnt.type == SDL_EventType.SDL_MOUSEMOTION) + else { - CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y); + if (_isHidden) + { + SDL_ShowCursor(SDL_ENABLE); + _isHidden = false; + } } - else if (evnt.type == SDL_EventType.SDL_MOUSEWHEEL) + } + + public void Update(SDL_Event evnt) + { + switch (evnt.type) { - Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y); + case SDL_EventType.SDL_MOUSEBUTTONDOWN: + case SDL_EventType.SDL_MOUSEBUTTONUP: + uint rawButton = evnt.button.button; + + if (rawButton > 0 && rawButton <= (int)MouseButton.Count) + { + PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN; + + CurrentPosition = new Vector2(evnt.button.x, evnt.button.y); + } + + break; + + // NOTE: On Linux using Wayland mouse motion events won't be received at all. + case SDL_EventType.SDL_MOUSEMOTION: + CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y); + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + + break; + + case SDL_EventType.SDL_MOUSEWHEEL: + Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y); + + break; } } @@ -100,4 +161,4 @@ namespace Ryujinx.Headless.SDL2 _isDisposed = true; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs index 183233397..172b7685a 100644 --- a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs @@ -12,7 +12,13 @@ namespace Ryujinx.Headless.SDL2.Vulkan { private GraphicsDebugLevel _glLogLevel; - public VulkanWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse) + public VulkanWindow( + InputManager inputManager, + GraphicsDebugLevel glLogLevel, + AspectRatio aspectRatio, + bool enableMouse, + HideCursor hideCursor) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor) { _glLogLevel = glLogLevel; } @@ -95,4 +101,4 @@ namespace Ryujinx.Headless.SDL2.Vulkan protected override void SwapBuffers() { } } -} +} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/Ryujinx.Headless.SDL2/WindowBase.cs index 88b0d5733..db6c8ec4d 100644 --- a/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/Ryujinx.Headless.SDL2/WindowBase.cs @@ -14,13 +14,16 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; using System.Threading; using static SDL2.SDL; using Switch = Ryujinx.HLE.Switch; namespace Ryujinx.Headless.SDL2 { - abstract class WindowBase : IHostUiHandler, IDisposable + abstract partial class WindowBase : IHostUiHandler, IDisposable { protected const int DefaultWidth = 1280; protected const int DefaultHeight = 720; @@ -29,6 +32,10 @@ namespace Ryujinx.Headless.SDL2 private static ConcurrentQueue MainThreadActions = new ConcurrentQueue(); + [LibraryImport("SDL2")] + // TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly + private static partial IntPtr SDL_LoadBMP_RW(IntPtr src, int freesrc); + public static void QueueMainThreadAction(Action action) { MainThreadActions.Enqueue(action); @@ -66,9 +73,14 @@ namespace Ryujinx.Headless.SDL2 private AspectRatio _aspectRatio; private bool _enableMouse; - public WindowBase(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) + public WindowBase( + InputManager inputManager, + GraphicsDebugLevel glLogLevel, + AspectRatio aspectRatio, + bool enableMouse, + HideCursor hideCursor) { - MouseDriver = new SDL2MouseDriver(); + MouseDriver = new SDL2MouseDriver(hideCursor); _inputManager = inputManager; _inputManager.SetMouseDriver(MouseDriver); NpadManager = _inputManager.CreateNpadManager(); @@ -103,6 +115,34 @@ namespace Ryujinx.Headless.SDL2 TouchScreenManager.Initialize(device); } + private void SetWindowIcon() + { + Stream iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.Headless.SDL2.Ryujinx.bmp"); + byte[] iconBytes = new byte[iconStream!.Length]; + + if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length) + { + Logger.Error?.Print(LogClass.Application, "Failed to read icon to byte array."); + iconStream.Close(); + + return; + } + + iconStream.Close(); + + unsafe + { + fixed (byte* iconPtr = iconBytes) + { + IntPtr rwOpsStruct = SDL_RWFromConstMem((IntPtr)iconPtr, iconBytes.Length); + IntPtr iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1); + + SDL_SetWindowIcon(WindowHandle, iconHandle); + SDL_FreeSurface(iconHandle); + } + } + } + private void InitializeWindow() { string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty @@ -127,6 +167,8 @@ namespace Ryujinx.Headless.SDL2 throw new Exception(errorMessage); } + SetWindowIcon(); + _windowId = SDL_GetWindowID(WindowHandle); SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent); @@ -146,9 +188,11 @@ namespace Ryujinx.Headless.SDL2 Renderer?.Window.SetSize(Width, Height); MouseDriver.SetClientSize(Width, Height); break; + case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE: Exit(); break; + default: break; } @@ -331,6 +375,9 @@ namespace Ryujinx.Headless.SDL2 Device.Hid.DebugPad.Update(); + // TODO: Replace this with MouseDriver.CheckIdle() when mouse motion events are received on every supported platform. + MouseDriver.UpdatePosition(); + return true; } @@ -451,4 +498,4 @@ namespace Ryujinx.Headless.SDL2 } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Input/HLE/NpadManager.cs b/Ryujinx.Input/HLE/NpadManager.cs index 34e05687f..5290ecbb7 100644 --- a/Ryujinx.Input/HLE/NpadManager.cs +++ b/Ryujinx.Input/HLE/NpadManager.cs @@ -163,7 +163,7 @@ namespace Ryujinx.Input.HLE ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); } - public void Update(float aspectRatio = 0) + public void Update(float aspectRatio = 1) { lock (_lock) { From 3a4eeb77fe23b60c2b3e0f921325315f72fb53fd Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 9 Jan 2023 19:02:41 +0100 Subject: [PATCH 256/737] headless: Change window icon size to 48x48 (#4247) --- Ryujinx.Headless.SDL2/Ryujinx.bmp | Bin 1122442 -> 9354 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Ryujinx.Headless.SDL2/Ryujinx.bmp b/Ryujinx.Headless.SDL2/Ryujinx.bmp index 413f3b21bb8af18fa2e47aa2468df02546dd359a..1daa7ce9406ae1ad56b4e697582c3e746a52da7e 100644 GIT binary patch literal 9354 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
      ;ISff6)c9rVC*GVXglT5c>a_lbvi0m&as& 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 XF#!7i9{=I{<8i5;p Date: Mon, 9 Jan 2023 18:58:29 -0300 Subject: [PATCH 257/737] Replace tabs with spaces across the project (#4244) * Replace tabs with spaces across the project * Include AXAML files too --- Ryujinx.Ava/UI/Windows/AvatarWindow.axaml | 8 +- .../HOS/Services/Settings/NxSettings.cs | 3412 ++++++++--------- .../Ryujinx.Headless.SDL2.csproj | 22 +- 3 files changed, 1721 insertions(+), 1721 deletions(-) diff --git a/Ryujinx.Ava/UI/Windows/AvatarWindow.axaml b/Ryujinx.Ava/UI/Windows/AvatarWindow.axaml index c90ce0221..1d30fff58 100644 --- a/Ryujinx.Ava/UI/Windows/AvatarWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/AvatarWindow.axaml @@ -1,4 +1,4 @@ - - - - + + + diff --git a/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs b/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs index ca3853e8b..e5f218a64 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs @@ -3,1710 +3,1710 @@ using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Settings { static class NxSettings - { - // Generated automatically from a Switch 3.0 config file (Tid: 0100000000000818). - public static Dictionary Settings = new Dictionary - { - { "account!na_required_for_network_service", true }, - { "account.daemon!background_awaking_periodicity", 10800 }, - { "account.daemon!schedule_periodicity", 3600 }, - { "account.daemon!profile_sync_interval", 18000 }, - { "account.daemon!na_info_refresh_interval", 46800 }, - { "am.display!transition_layer_enabled", true }, - { "am.gpu!gpu_scheduling_enabled", true }, - { "am.gpu!gpu_scheduling_frame_time_us", 116666 }, - { "am.gpu!gpu_scheduling_fg_app_us", 116166 }, - { "am.gpu!gpu_scheduling_bg_app_us", 104500 }, - { "am.gpu!gpu_scheduling_oa_us", 500 }, - { "am.gpu!gpu_scheduling_fg_sa_us", 11666 }, - { "am.gpu!gpu_scheduling_bg_sa_us", 0 }, - { "am.gpu!gpu_scheduling_fg_la_us", 11666 }, - { "am.gpu!gpu_scheduling_partial_fg_la_us", 2000 }, - { "am.gpu!gpu_scheduling_bg_la_us", 0 }, - { "audio!audren_log_enabled", false }, - { "audio!audout_log_enabled", false }, - { "audio!audin_log_enabled", false }, - { "audio!hwopus_log_enabled", false }, - { "audio!adsp_log_enabled", false }, - { "audio!suspend_for_debugger_enabled", false }, - { "audio!uac_speaker_enabled", false }, - { "bgtc!enable_halfawake", 1 }, - { "bgtc!enable_battery_saver", 1 }, - { "bgtc!leaving_halfawake_margin", 3 }, - { "bgtc!battery_threshold_save", 20 }, - { "bgtc!battery_threshold_stop", 20 }, - { "bgtc!minimum_interval_normal", 1800 }, - { "bgtc!minimum_interval_save", 86400 }, - { "boot!force_maintenance", false }, - { "capsrv!screenshot_layerstack", "screenshot" }, - { "capsrv!enable_album_screenshot_filedata_verification", true }, - { "devmenu!enable_application_update", true }, - { "devmenu!enable_exhibition_mode", false }, - { "eclct!analytics_override", false }, - { "eclct!analytics_pollperiod", 86400 }, - { "err!applet_auto_close", false }, - { "friends!background_processing", true }, - { "htc!disconnection_emulation", false }, - { "idle!dim_level_percent_lcd", 10 }, - { "idle!dim_level_percent_tv", 70 }, - { "lbl!force_disable_als", false }, - { "lm!enable_sd_card_logging", false }, - { "lm!sd_card_log_output_directory", "NxBinLogs" }, - { "mii!is_db_test_mode_enabled", false }, - { "news!system_version", 2 }, - { "nfp!not_locked_tag", true }, - { "nfp!play_report", false }, - { "nifm!is_communication_control_enabled_for_test", false }, - { "nifm!connection_test_timeout", 45000 }, - { "nifm!apply_config_timeout", 30000 }, - { "nifm!ethernet_adapter_standby_time", 10000 }, - { "nim.install!prefer_delta_evenif_inefficient", false }, - { "nim.install!apply_delta_stress_storage", 0 }, - { "ns.notification!retry_interval", 60 }, - { "ns.notification!enable_network_update", true }, - { "ns.notification!enable_download_task_list", true }, - { "ns.notification!enable_version_list", true }, - { "ns.notification!enable_random_wait", true }, - { "ns.notification!debug_waiting_limit", 0 }, - { "ns.notification!enable_request_on_cold_boot", true }, - { "ns.sdcard!mount_sdcard", true }, - { "ns.sdcard!compare_sdcard", 0 }, - { "ns.gamecard!mount_gamecard_result_value", 0 }, - { "ns.gamecard!try_gamecard_access_result_value", 0 }, - { "nv!00008600", "" }, - { "nv!0007b25e", "" }, - { "nv!0083e1", "" }, - { "nv!01621887", "" }, - { "nv!03134743", "" }, - { "nv!0356afd0", "" }, - { "nv!0356afd1", "" }, - { "nv!0356afd2", "" }, - { "nv!0356afd3", "" }, - { "nv!094313", "" }, - { "nv!0x04dc09", "" }, - { "nv!0x111133", "" }, - { "nv!0x1aa483", "" }, - { "nv!0x1cb1cf", "" }, - { "nv!0x1cb1d0", "" }, - { "nv!0x1e3221", "" }, - { "nv!0x300fc8", "" }, - { "nv!0x301fc8", "" }, - { "nv!0x302fc8", "" }, - { "nv!0x3eec59", "" }, - { "nv!0x46b3ed", "" }, - { "nv!0x523dc0", "" }, - { "nv!0x523dc1", "" }, - { "nv!0x523dc2", "" }, - { "nv!0x523dc3", "" }, - { "nv!0x523dc4", "" }, - { "nv!0x523dc5", "" }, - { "nv!0x523dc6", "" }, - { "nv!0x523dd0", "" }, - { "nv!0x523dd1", "" }, - { "nv!0x523dd3", "" }, - { "nv!0x5344bb", "" }, - { "nv!0x555237", "" }, - { "nv!0x58a234", "" }, - { "nv!0x7b4428", "" }, - { "nv!0x923dc0", "" }, - { "nv!0x923dc1", "" }, - { "nv!0x923dc2", "" }, - { "nv!0x923dc3", "" }, - { "nv!0x923dc4", "" }, - { "nv!0x923dd3", "" }, - { "nv!0x9abdc5", "" }, - { "nv!0x9abdc6", "" }, - { "nv!0xaaa36c", "" }, - { "nv!0xb09da0", "" }, - { "nv!0xb09da1", "" }, - { "nv!0xb09da2", "" }, - { "nv!0xb09da3", "" }, - { "nv!0xb09da4", "" }, - { "nv!0xb09da5", "" }, - { "nv!0xb0b348", "" }, - { "nv!0xb0b349", "" }, - { "nv!0xbb558f", "" }, - { "nv!0xbd10fb", "" }, - { "nv!0xc32ad3", "" }, - { "nv!0xce2348", "" }, - { "nv!0xcfd81f", "" }, - { "nv!0xe0036b", "" }, - { "nv!0xe01f2d", "" }, - { "nv!0xe17212", "" }, - { "nv!0xeae966", "" }, - { "nv!0xed4f82", "" }, - { "nv!0xf12335", "" }, - { "nv!0xf12336", "" }, - { "nv!10261989", "" }, - { "nv!1042d483", "" }, - { "nv!10572898", "" }, - { "nv!115631", "" }, - { "nv!12950094", "" }, - { "nv!1314f311", "" }, - { "nv!1314f312", "" }, - { "nv!13279512", "" }, - { "nv!13813496", "" }, - { "nv!14507179", "" }, - { "nv!15694569", "" }, - { "nv!16936964", "" }, - { "nv!17aa230c", "" }, - { "nv!182054", "" }, - { "nv!18273275", "" }, - { "nv!18273276", "" }, - { "nv!1854d03b", "" }, - { "nv!18add00d", "" }, - { "nv!19156670", "" }, - { "nv!19286545", "" }, - { "nv!1a298e9f", "" }, - { "nv!1acf43fe", "" }, - { "nv!1bda43fe", "" }, - { "nv!1c3b92", "" }, - { "nv!21509920", "" }, - { "nv!215323457", "" }, - { "nv!2165ad", "" }, - { "nv!2165ae", "" }, - { "nv!21be9c", "" }, - { "nv!233264316", "" }, - { "nv!234557580", "" }, - { "nv!23cd0e", "" }, - { "nv!24189123", "" }, - { "nv!2443266", "" }, - { "nv!25025519", "" }, - { "nv!255e39", "" }, - { "nv!2583364", "" }, - { "nv!2888c1", "" }, - { "nv!28ca3e", "" }, - { "nv!29871243", "" }, - { "nv!2a1f64", "" }, - { "nv!2dc432", "" }, - { "nv!2de437", "" }, - { "nv!2f3bb89c", "" }, - { "nv!2fd652", "" }, - { "nv!3001ac", "" }, - { "nv!31298772", "" }, - { "nv!313233", "" }, - { "nv!31f7d603", "" }, - { "nv!320ce4", "" }, - { "nv!32153248", "" }, - { "nv!32153249", "" }, - { "nv!335bca", "" }, - { "nv!342abb", "" }, - { "nv!34dfe6", "" }, - { "nv!34dfe7", "" }, - { "nv!34dfe8", "" }, - { "nv!34dfe9", "" }, - { "nv!35201578", "" }, - { "nv!359278", "" }, - { "nv!37f53a", "" }, - { "nv!38144972", "" }, - { "nv!38542646", "" }, - { "nv!3b74c9", "" }, - { "nv!3c136f", "" }, - { "nv!3cf72823", "" }, - { "nv!3d7af029", "" }, - { "nv!3ff34782", "" }, - { "nv!4129618", "" }, - { "nv!4189fac3", "" }, - { "nv!420bd4", "" }, - { "nv!42a699", "" }, - { "nv!441369", "" }, - { "nv!4458713e", "" }, - { "nv!4554b6", "" }, - { "nv!457425", "" }, - { "nv!4603b207", "" }, - { "nv!46574957", "" }, - { "nv!46574958", "" }, - { "nv!46813529", "" }, - { "nv!46f1e13d", "" }, - { "nv!47534c43", "" }, - { "nv!48550336", "" }, - { "nv!48576893", "" }, - { "nv!48576894", "" }, - { "nv!4889ac02", "" }, - { "nv!49005740", "" }, - { "nv!49867584", "" }, - { "nv!49960973", "" }, - { "nv!4a5341", "" }, - { "nv!4f4e48", "" }, - { "nv!4f8a0a", "" }, - { "nv!50299698", "" }, - { "nv!50299699", "" }, - { "nv!50361291", "" }, - { "nv!5242ae", "" }, - { "nv!53d30c", "" }, - { "nv!56347a", "" }, - { "nv!563a95f1", "" }, - { "nv!573823", "" }, - { "nv!58027529", "" }, - { "nv!5d2d63", "" }, - { "nv!5f7e3b", "" }, - { "nv!60461793", "" }, - { "nv!60d355", "" }, - { "nv!616627aa", "" }, - { "nv!62317182", "" }, - { "nv!6253fa2e", "" }, - { "nv!64100768", "" }, - { "nv!64100769", "" }, - { "nv!64100770", "" }, - { "nv!647395", "" }, - { "nv!66543234", "" }, - { "nv!67674763", "" }, - { "nv!67739784", "" }, - { "nv!68fb9c", "" }, - { "nv!69801276", "" }, - { "nv!6af9fa2f", "" }, - { "nv!6af9fa3f", "" }, - { "nv!6af9fa4f", "" }, - { "nv!6bd8c7", "" }, - { "nv!6c7691", "" }, - { "nv!6d4296ce", "" }, - { "nv!6dd7e7", "" }, - { "nv!6dd7e8", "" }, - { "nv!6fe11ec1", "" }, - { "nv!716511763", "" }, - { "nv!72504593", "" }, - { "nv!73304097", "" }, - { "nv!73314098", "" }, - { "nv!74095213", "" }, - { "nv!74095213a", "" }, - { "nv!74095213b", "" }, - { "nv!74095214", "" }, - { "nv!748f9649", "" }, - { "nv!75494732", "" }, - { "nv!78452832", "" }, - { "nv!784561", "" }, - { "nv!78e16b9c", "" }, - { "nv!79251225", "" }, - { "nv!7c128b", "" }, - { "nv!7ccd93", "" }, - { "nv!7df8d1", "" }, - { "nv!800c2310", "" }, - { "nv!80546710", "" }, - { "nv!80772310", "" }, - { "nv!808ee280", "" }, - { "nv!81131154", "" }, - { "nv!81274457", "" }, - { "nv!8292291f", "" }, - { "nv!83498426", "" }, - { "nv!84993794", "" }, - { "nv!84995585", "" }, - { "nv!84a0a0", "" }, - { "nv!852142", "" }, - { "nv!85612309", "" }, - { "nv!85612310", "" }, - { "nv!85612311", "" }, - { "nv!85612312", "" }, - { "nv!8623ff27", "" }, - { "nv!87364952", "" }, - { "nv!87f6275666", "" }, - { "nv!886748", "" }, - { "nv!89894423", "" }, - { "nv!8ad8a75", "" }, - { "nv!8ad8ad00", "" }, - { "nv!8bb815", "" }, - { "nv!8bb817", "" }, - { "nv!8bb818", "" }, - { "nv!8bb819", "" }, - { "nv!8e640cd1", "" }, - { "nv!8f34971a", "" }, - { "nv!8f773984", "" }, - { "nv!8f7a7d", "" }, - { "nv!902486209", "" }, - { "nv!90482571", "" }, - { "nv!91214835", "" }, - { "nv!912848290", "" }, - { "nv!915e56", "" }, - { "nv!92179063", "" }, - { "nv!92179064", "" }, - { "nv!92179065", "" }, - { "nv!92179066", "" }, - { "nv!92350358", "" }, - { "nv!92809063", "" }, - { "nv!92809064", "" }, - { "nv!92809065", "" }, - { "nv!92809066", "" }, - { "nv!92920143", "" }, - { "nv!93a89b12", "" }, - { "nv!93a89c0b", "" }, - { "nv!94812574", "" }, - { "nv!95282304", "" }, - { "nv!95394027", "" }, - { "nv!959b1f", "" }, - { "nv!9638af", "" }, - { "nv!96fd59", "" }, - { "nv!97f6275666", "" }, - { "nv!97f6275667", "" }, - { "nv!97f6275668", "" }, - { "nv!97f6275669", "" }, - { "nv!97f627566a", "" }, - { "nv!97f627566b", "" }, - { "nv!97f627566d", "" }, - { "nv!97f627566e", "" }, - { "nv!97f627566f", "" }, - { "nv!97f6275670", "" }, - { "nv!97f6275671", "" }, - { "nv!97f727566e", "" }, - { "nv!98480775", "" }, - { "nv!98480776", "" }, - { "nv!98480777", "" }, - { "nv!992431", "" }, - { "nv!9aa29065", "" }, - { "nv!9af32c", "" }, - { "nv!9af32d", "" }, - { "nv!9af32e", "" }, - { "nv!9c108b71", "" }, - { "nv!9f279065", "" }, - { "nv!a01bc728", "" }, - { "nv!a13b46c80", "" }, - { "nv!a22eb0", "" }, - { "nv!a2fb451e", "" }, - { "nv!a3456abe", "" }, - { "nv!a7044887", "" }, - { "nv!a7149200", "" }, - { "nv!a766215670", "" }, - { "nv!aac_drc_boost", "" }, - { "nv!aac_drc_cut", "" }, - { "nv!aac_drc_enc_target_level", "" }, - { "nv!aac_drc_heavy", "" }, - { "nv!aac_drc_reference_level", "" }, - { "nv!aalinegamma", "" }, - { "nv!aalinetweaks", "" }, - { "nv!ab34ee01", "" }, - { "nv!ab34ee02", "" }, - { "nv!ab34ee03", "" }, - { "nv!ac0274", "" }, - { "nv!af73c63e", "" }, - { "nv!af73c63f", "" }, - { "nv!af9927", "" }, - { "nv!afoverride", "" }, - { "nv!allocdeviceevents", "" }, - { "nv!applicationkey", "" }, - { "nv!appreturnonlybasicglsltype", "" }, - { "nv!app_softimage", "" }, - { "nv!app_supportbits2", "" }, - { "nv!assumetextureismipmappedatcreation", "" }, - { "nv!b1fb0f01", "" }, - { "nv!b3edd5", "" }, - { "nv!b40d9e03d", "" }, - { "nv!b7f6275666", "" }, - { "nv!b812c1", "" }, - { "nv!ba14ba1a", "" }, - { "nv!ba14ba1b", "" }, - { "nv!bd7559", "" }, - { "nv!bd755a", "" }, - { "nv!bd755c", "" }, - { "nv!bd755d", "" }, - { "nv!be58bb", "" }, - { "nv!be92cb", "" }, - { "nv!beefcba3", "" }, - { "nv!beefcba4", "" }, - { "nv!c023777f", "" }, - { "nv!c09dc8", "" }, - { "nv!c0d340", "" }, - { "nv!c2ff374c", "" }, - { "nv!c5e9d7a3", "" }, - { "nv!c5e9d7a4", "" }, - { "nv!c5e9d7b4", "" }, - { "nv!c618f9", "" }, - { "nv!ca345840", "" }, - { "nv!cachedisable", "" }, - { "nv!cast.on", "" }, - { "nv!cde", "" }, - { "nv!channelpriorityoverride", "" }, - { "nv!cleardatastorevidmem", "" }, - { "nv!cmdbufmemoryspaceenables", "" }, - { "nv!cmdbufminwords", "" }, - { "nv!cmdbufsizewords", "" }, - { "nv!conformantblitframebufferscissor", "" }, - { "nv!conformantincompletetextures", "" }, - { "nv!copybuffermethod", "" }, - { "nv!cubemapaniso", "" }, - { "nv!cubemapfiltering", "" }, - { "nv!d0e9a4d7", "" }, - { "nv!d13733f12", "" }, - { "nv!d1b399", "" }, - { "nv!d2983c32", "" }, - { "nv!d2983c33", "" }, - { "nv!d2e71b", "" }, - { "nv!d377dc", "" }, - { "nv!d377dd", "" }, - { "nv!d489f4", "" }, - { "nv!d4bce1", "" }, - { "nv!d518cb", "" }, - { "nv!d518cd", "" }, - { "nv!d518ce", "" }, - { "nv!d518d0", "" }, - { "nv!d518d1", "" }, - { "nv!d518d2", "" }, - { "nv!d518d3", "" }, - { "nv!d518d4", "" }, - { "nv!d518d5", "" }, - { "nv!d59eda", "" }, - { "nv!d83cbd", "" }, - { "nv!d8e777", "" }, - { "nv!debug_level", "" }, - { "nv!debug_mask", "" }, - { "nv!debug_options", "" }, - { "nv!devshmpageableallocations", "" }, - { "nv!df1f9812", "" }, - { "nv!df783c", "" }, - { "nv!diagenable", "" }, - { "nv!disallowcemask", "" }, - { "nv!disallowz16", "" }, - { "nv!dlmemoryspaceenables", "" }, - { "nv!e0bfec", "" }, - { "nv!e433456d", "" }, - { "nv!e435563f", "" }, - { "nv!e4cd9c", "" }, - { "nv!e5c972", "" }, - { "nv!e639ef", "" }, - { "nv!e802af", "" }, - { "nv!eae964", "" }, - { "nv!earlytexturehwallocation", "" }, - { "nv!eb92a3", "" }, - { "nv!ebca56", "" }, - { "nv!enable-noaud", "" }, - { "nv!enable-noavs", "" }, - { "nv!enable-prof", "" }, - { "nv!enable-sxesmode", "" }, - { "nv!enable-ulld", "" }, - { "nv!expert_detail_level", "" }, - { "nv!expert_output_mask", "" }, - { "nv!expert_report_mask", "" }, - { "nv!extensionstringnvarch", "" }, - { "nv!extensionstringversion", "" }, - { "nv!f00f1938", "" }, - { "nv!f10736", "" }, - { "nv!f1846870", "" }, - { "nv!f33bc370", "" }, - { "nv!f392a874", "" }, - { "nv!f49ae8", "" }, - { "nv!fa345cce", "" }, - { "nv!fa35cc4", "" }, - { "nv!faa14a", "" }, - { "nv!faf8a723", "" }, - { "nv!fastgs", "" }, - { "nv!fbf4ac45", "" }, - { "nv!fbo_blit_ignore_srgb", "" }, - { "nv!fc64c7", "" }, - { "nv!ff54ec97", "" }, - { "nv!ff54ec98", "" }, - { "nv!forceexitprocessdetach", "" }, - { "nv!forcerequestedesversion", "" }, - { "nv!__gl_", "" }, - { "nv!__gl_00008600", "" }, - { "nv!__gl_0007b25e", "" }, - { "nv!__gl_0083e1", "" }, - { "nv!__gl_01621887", "" }, - { "nv!__gl_03134743", "" }, - { "nv!__gl_0356afd0", "" }, - { "nv!__gl_0356afd1", "" }, - { "nv!__gl_0356afd2", "" }, - { "nv!__gl_0356afd3", "" }, - { "nv!__gl_094313", "" }, - { "nv!__gl_0x04dc09", "" }, - { "nv!__gl_0x111133", "" }, - { "nv!__gl_0x1aa483", "" }, - { "nv!__gl_0x1cb1cf", "" }, - { "nv!__gl_0x1cb1d0", "" }, - { "nv!__gl_0x1e3221", "" }, - { "nv!__gl_0x300fc8", "" }, - { "nv!__gl_0x301fc8", "" }, - { "nv!__gl_0x302fc8", "" }, - { "nv!__gl_0x3eec59", "" }, - { "nv!__gl_0x46b3ed", "" }, - { "nv!__gl_0x523dc0", "" }, - { "nv!__gl_0x523dc1", "" }, - { "nv!__gl_0x523dc2", "" }, - { "nv!__gl_0x523dc3", "" }, - { "nv!__gl_0x523dc4", "" }, - { "nv!__gl_0x523dc5", "" }, - { "nv!__gl_0x523dc6", "" }, - { "nv!__gl_0x523dd0", "" }, - { "nv!__gl_0x523dd1", "" }, - { "nv!__gl_0x523dd3", "" }, - { "nv!__gl_0x5344bb", "" }, - { "nv!__gl_0x555237", "" }, - { "nv!__gl_0x58a234", "" }, - { "nv!__gl_0x7b4428", "" }, - { "nv!__gl_0x923dc0", "" }, - { "nv!__gl_0x923dc1", "" }, - { "nv!__gl_0x923dc2", "" }, - { "nv!__gl_0x923dc3", "" }, - { "nv!__gl_0x923dc4", "" }, - { "nv!__gl_0x923dd3", "" }, - { "nv!__gl_0x9abdc5", "" }, - { "nv!__gl_0x9abdc6", "" }, - { "nv!__gl_0xaaa36c", "" }, - { "nv!__gl_0xb09da0", "" }, - { "nv!__gl_0xb09da1", "" }, - { "nv!__gl_0xb09da2", "" }, - { "nv!__gl_0xb09da3", "" }, - { "nv!__gl_0xb09da4", "" }, - { "nv!__gl_0xb09da5", "" }, - { "nv!__gl_0xb0b348", "" }, - { "nv!__gl_0xb0b349", "" }, - { "nv!__gl_0xbb558f", "" }, - { "nv!__gl_0xbd10fb", "" }, - { "nv!__gl_0xc32ad3", "" }, - { "nv!__gl_0xce2348", "" }, - { "nv!__gl_0xcfd81f", "" }, - { "nv!__gl_0xe0036b", "" }, - { "nv!__gl_0xe01f2d", "" }, - { "nv!__gl_0xe17212", "" }, - { "nv!__gl_0xeae966", "" }, - { "nv!__gl_0xed4f82", "" }, - { "nv!__gl_0xf12335", "" }, - { "nv!__gl_0xf12336", "" }, - { "nv!__gl_10261989", "" }, - { "nv!__gl_1042d483", "" }, - { "nv!__gl_10572898", "" }, - { "nv!__gl_115631", "" }, - { "nv!__gl_12950094", "" }, - { "nv!__gl_1314f311", "" }, - { "nv!__gl_1314f312", "" }, - { "nv!__gl_13279512", "" }, - { "nv!__gl_13813496", "" }, - { "nv!__gl_14507179", "" }, - { "nv!__gl_15694569", "" }, - { "nv!__gl_16936964", "" }, - { "nv!__gl_17aa230c", "" }, - { "nv!__gl_182054", "" }, - { "nv!__gl_18273275", "" }, - { "nv!__gl_18273276", "" }, - { "nv!__gl_1854d03b", "" }, - { "nv!__gl_18add00d", "" }, - { "nv!__gl_19156670", "" }, - { "nv!__gl_19286545", "" }, - { "nv!__gl_1a298e9f", "" }, - { "nv!__gl_1acf43fe", "" }, - { "nv!__gl_1bda43fe", "" }, - { "nv!__gl_1c3b92", "" }, - { "nv!__gl_21509920", "" }, - { "nv!__gl_215323457", "" }, - { "nv!__gl_2165ad", "" }, - { "nv!__gl_2165ae", "" }, - { "nv!__gl_21be9c", "" }, - { "nv!__gl_233264316", "" }, - { "nv!__gl_234557580", "" }, - { "nv!__gl_23cd0e", "" }, - { "nv!__gl_24189123", "" }, - { "nv!__gl_2443266", "" }, - { "nv!__gl_25025519", "" }, - { "nv!__gl_255e39", "" }, - { "nv!__gl_2583364", "" }, - { "nv!__gl_2888c1", "" }, - { "nv!__gl_28ca3e", "" }, - { "nv!__gl_29871243", "" }, - { "nv!__gl_2a1f64", "" }, - { "nv!__gl_2dc432", "" }, - { "nv!__gl_2de437", "" }, - { "nv!__gl_2f3bb89c", "" }, - { "nv!__gl_2fd652", "" }, - { "nv!__gl_3001ac", "" }, - { "nv!__gl_31298772", "" }, - { "nv!__gl_313233", "" }, - { "nv!__gl_31f7d603", "" }, - { "nv!__gl_320ce4", "" }, - { "nv!__gl_32153248", "" }, - { "nv!__gl_32153249", "" }, - { "nv!__gl_335bca", "" }, - { "nv!__gl_342abb", "" }, - { "nv!__gl_34dfe6", "" }, - { "nv!__gl_34dfe7", "" }, - { "nv!__gl_34dfe8", "" }, - { "nv!__gl_34dfe9", "" }, - { "nv!__gl_35201578", "" }, - { "nv!__gl_359278", "" }, - { "nv!__gl_37f53a", "" }, - { "nv!__gl_38144972", "" }, - { "nv!__gl_38542646", "" }, - { "nv!__gl_3b74c9", "" }, - { "nv!__gl_3c136f", "" }, - { "nv!__gl_3cf72823", "" }, - { "nv!__gl_3d7af029", "" }, - { "nv!__gl_3ff34782", "" }, - { "nv!__gl_4129618", "" }, - { "nv!__gl_4189fac3", "" }, - { "nv!__gl_420bd4", "" }, - { "nv!__gl_42a699", "" }, - { "nv!__gl_441369", "" }, - { "nv!__gl_4458713e", "" }, - { "nv!__gl_4554b6", "" }, - { "nv!__gl_457425", "" }, - { "nv!__gl_4603b207", "" }, - { "nv!__gl_46574957", "" }, - { "nv!__gl_46574958", "" }, - { "nv!__gl_46813529", "" }, - { "nv!__gl_46f1e13d", "" }, - { "nv!__gl_47534c43", "" }, - { "nv!__gl_48550336", "" }, - { "nv!__gl_48576893", "" }, - { "nv!__gl_48576894", "" }, - { "nv!__gl_4889ac02", "" }, - { "nv!__gl_49005740", "" }, - { "nv!__gl_49867584", "" }, - { "nv!__gl_49960973", "" }, - { "nv!__gl_4a5341", "" }, - { "nv!__gl_4f4e48", "" }, - { "nv!__gl_4f8a0a", "" }, - { "nv!__gl_50299698", "" }, - { "nv!__gl_50299699", "" }, - { "nv!__gl_50361291", "" }, - { "nv!__gl_5242ae", "" }, - { "nv!__gl_53d30c", "" }, - { "nv!__gl_56347a", "" }, - { "nv!__gl_563a95f1", "" }, - { "nv!__gl_573823", "" }, - { "nv!__gl_58027529", "" }, - { "nv!__gl_5d2d63", "" }, - { "nv!__gl_5f7e3b", "" }, - { "nv!__gl_60461793", "" }, - { "nv!__gl_60d355", "" }, - { "nv!__gl_616627aa", "" }, - { "nv!__gl_62317182", "" }, - { "nv!__gl_6253fa2e", "" }, - { "nv!__gl_64100768", "" }, - { "nv!__gl_64100769", "" }, - { "nv!__gl_64100770", "" }, - { "nv!__gl_647395", "" }, - { "nv!__gl_66543234", "" }, - { "nv!__gl_67674763", "" }, - { "nv!__gl_67739784", "" }, - { "nv!__gl_68fb9c", "" }, - { "nv!__gl_69801276", "" }, - { "nv!__gl_6af9fa2f", "" }, - { "nv!__gl_6af9fa3f", "" }, - { "nv!__gl_6af9fa4f", "" }, - { "nv!__gl_6bd8c7", "" }, - { "nv!__gl_6c7691", "" }, - { "nv!__gl_6d4296ce", "" }, - { "nv!__gl_6dd7e7", "" }, - { "nv!__gl_6dd7e8", "" }, - { "nv!__gl_6fe11ec1", "" }, - { "nv!__gl_716511763", "" }, - { "nv!__gl_72504593", "" }, - { "nv!__gl_73304097", "" }, - { "nv!__gl_73314098", "" }, - { "nv!__gl_74095213", "" }, - { "nv!__gl_74095213a", "" }, - { "nv!__gl_74095213b", "" }, - { "nv!__gl_74095214", "" }, - { "nv!__gl_748f9649", "" }, - { "nv!__gl_75494732", "" }, - { "nv!__gl_78452832", "" }, - { "nv!__gl_784561", "" }, - { "nv!__gl_78e16b9c", "" }, - { "nv!__gl_79251225", "" }, - { "nv!__gl_7c128b", "" }, - { "nv!__gl_7ccd93", "" }, - { "nv!__gl_7df8d1", "" }, - { "nv!__gl_800c2310", "" }, - { "nv!__gl_80546710", "" }, - { "nv!__gl_80772310", "" }, - { "nv!__gl_808ee280", "" }, - { "nv!__gl_81131154", "" }, - { "nv!__gl_81274457", "" }, - { "nv!__gl_8292291f", "" }, - { "nv!__gl_83498426", "" }, - { "nv!__gl_84993794", "" }, - { "nv!__gl_84995585", "" }, - { "nv!__gl_84a0a0", "" }, - { "nv!__gl_852142", "" }, - { "nv!__gl_85612309", "" }, - { "nv!__gl_85612310", "" }, - { "nv!__gl_85612311", "" }, - { "nv!__gl_85612312", "" }, - { "nv!__gl_8623ff27", "" }, - { "nv!__gl_87364952", "" }, - { "nv!__gl_87f6275666", "" }, - { "nv!__gl_886748", "" }, - { "nv!__gl_89894423", "" }, - { "nv!__gl_8ad8a75", "" }, - { "nv!__gl_8ad8ad00", "" }, - { "nv!__gl_8bb815", "" }, - { "nv!__gl_8bb817", "" }, - { "nv!__gl_8bb818", "" }, - { "nv!__gl_8bb819", "" }, - { "nv!__gl_8e640cd1", "" }, - { "nv!__gl_8f34971a", "" }, - { "nv!__gl_8f773984", "" }, - { "nv!__gl_8f7a7d", "" }, - { "nv!__gl_902486209", "" }, - { "nv!__gl_90482571", "" }, - { "nv!__gl_91214835", "" }, - { "nv!__gl_912848290", "" }, - { "nv!__gl_915e56", "" }, - { "nv!__gl_92179063", "" }, - { "nv!__gl_92179064", "" }, - { "nv!__gl_92179065", "" }, - { "nv!__gl_92179066", "" }, - { "nv!__gl_92350358", "" }, - { "nv!__gl_92809063", "" }, - { "nv!__gl_92809064", "" }, - { "nv!__gl_92809065", "" }, - { "nv!__gl_92809066", "" }, - { "nv!__gl_92920143", "" }, - { "nv!__gl_93a89b12", "" }, - { "nv!__gl_93a89c0b", "" }, - { "nv!__gl_94812574", "" }, - { "nv!__gl_95282304", "" }, - { "nv!__gl_95394027", "" }, - { "nv!__gl_959b1f", "" }, - { "nv!__gl_9638af", "" }, - { "nv!__gl_96fd59", "" }, - { "nv!__gl_97f6275666", "" }, - { "nv!__gl_97f6275667", "" }, - { "nv!__gl_97f6275668", "" }, - { "nv!__gl_97f6275669", "" }, - { "nv!__gl_97f627566a", "" }, - { "nv!__gl_97f627566b", "" }, - { "nv!__gl_97f627566d", "" }, - { "nv!__gl_97f627566e", "" }, - { "nv!__gl_97f627566f", "" }, - { "nv!__gl_97f6275670", "" }, - { "nv!__gl_97f6275671", "" }, - { "nv!__gl_97f727566e", "" }, - { "nv!__gl_98480775", "" }, - { "nv!__gl_98480776", "" }, - { "nv!__gl_98480777", "" }, - { "nv!__gl_992431", "" }, - { "nv!__gl_9aa29065", "" }, - { "nv!__gl_9af32c", "" }, - { "nv!__gl_9af32d", "" }, - { "nv!__gl_9af32e", "" }, - { "nv!__gl_9c108b71", "" }, - { "nv!__gl_9f279065", "" }, - { "nv!__gl_a01bc728", "" }, - { "nv!__gl_a13b46c80", "" }, - { "nv!__gl_a22eb0", "" }, - { "nv!__gl_a2fb451e", "" }, - { "nv!__gl_a3456abe", "" }, - { "nv!__gl_a7044887", "" }, - { "nv!__gl_a7149200", "" }, - { "nv!__gl_a766215670", "" }, - { "nv!__gl_aalinegamma", "" }, - { "nv!__gl_aalinetweaks", "" }, - { "nv!__gl_ab34ee01", "" }, - { "nv!__gl_ab34ee02", "" }, - { "nv!__gl_ab34ee03", "" }, - { "nv!__gl_ac0274", "" }, - { "nv!__gl_af73c63e", "" }, - { "nv!__gl_af73c63f", "" }, - { "nv!__gl_af9927", "" }, - { "nv!__gl_afoverride", "" }, - { "nv!__gl_allocdeviceevents", "" }, - { "nv!__gl_applicationkey", "" }, - { "nv!__gl_appreturnonlybasicglsltype", "" }, - { "nv!__gl_app_softimage", "" }, - { "nv!__gl_app_supportbits2", "" }, - { "nv!__gl_assumetextureismipmappedatcreation", "" }, - { "nv!__gl_b1fb0f01", "" }, - { "nv!__gl_b3edd5", "" }, - { "nv!__gl_b40d9e03d", "" }, - { "nv!__gl_b7f6275666", "" }, - { "nv!__gl_b812c1", "" }, - { "nv!__gl_ba14ba1a", "" }, - { "nv!__gl_ba14ba1b", "" }, - { "nv!__gl_bd7559", "" }, - { "nv!__gl_bd755a", "" }, - { "nv!__gl_bd755c", "" }, - { "nv!__gl_bd755d", "" }, - { "nv!__gl_be58bb", "" }, - { "nv!__gl_be92cb", "" }, - { "nv!__gl_beefcba3", "" }, - { "nv!__gl_beefcba4", "" }, - { "nv!__gl_c023777f", "" }, - { "nv!__gl_c09dc8", "" }, - { "nv!__gl_c0d340", "" }, - { "nv!__gl_c2ff374c", "" }, - { "nv!__gl_c5e9d7a3", "" }, - { "nv!__gl_c5e9d7a4", "" }, - { "nv!__gl_c5e9d7b4", "" }, - { "nv!__gl_c618f9", "" }, - { "nv!__gl_ca345840", "" }, - { "nv!__gl_cachedisable", "" }, - { "nv!__gl_channelpriorityoverride", "" }, - { "nv!__gl_cleardatastorevidmem", "" }, - { "nv!__gl_cmdbufmemoryspaceenables", "" }, - { "nv!__gl_cmdbufminwords", "" }, - { "nv!__gl_cmdbufsizewords", "" }, - { "nv!__gl_conformantblitframebufferscissor", "" }, - { "nv!__gl_conformantincompletetextures", "" }, - { "nv!__gl_copybuffermethod", "" }, - { "nv!__gl_cubemapaniso", "" }, - { "nv!__gl_cubemapfiltering", "" }, - { "nv!__gl_d0e9a4d7", "" }, - { "nv!__gl_d13733f12", "" }, - { "nv!__gl_d1b399", "" }, - { "nv!__gl_d2983c32", "" }, - { "nv!__gl_d2983c33", "" }, - { "nv!__gl_d2e71b", "" }, - { "nv!__gl_d377dc", "" }, - { "nv!__gl_d377dd", "" }, - { "nv!__gl_d489f4", "" }, - { "nv!__gl_d4bce1", "" }, - { "nv!__gl_d518cb", "" }, - { "nv!__gl_d518cd", "" }, - { "nv!__gl_d518ce", "" }, - { "nv!__gl_d518d0", "" }, - { "nv!__gl_d518d1", "" }, - { "nv!__gl_d518d2", "" }, - { "nv!__gl_d518d3", "" }, - { "nv!__gl_d518d4", "" }, - { "nv!__gl_d518d5", "" }, - { "nv!__gl_d59eda", "" }, - { "nv!__gl_d83cbd", "" }, - { "nv!__gl_d8e777", "" }, - { "nv!__gl_debug_level", "" }, - { "nv!__gl_debug_mask", "" }, - { "nv!__gl_debug_options", "" }, - { "nv!__gl_devshmpageableallocations", "" }, - { "nv!__gl_df1f9812", "" }, - { "nv!__gl_df783c", "" }, - { "nv!__gl_diagenable", "" }, - { "nv!__gl_disallowcemask", "" }, - { "nv!__gl_disallowz16", "" }, - { "nv!__gl_dlmemoryspaceenables", "" }, - { "nv!__gl_e0bfec", "" }, - { "nv!__gl_e433456d", "" }, - { "nv!__gl_e435563f", "" }, - { "nv!__gl_e4cd9c", "" }, - { "nv!__gl_e5c972", "" }, - { "nv!__gl_e639ef", "" }, - { "nv!__gl_e802af", "" }, - { "nv!__gl_eae964", "" }, - { "nv!__gl_earlytexturehwallocation", "" }, - { "nv!__gl_eb92a3", "" }, - { "nv!__gl_ebca56", "" }, - { "nv!__gl_expert_detail_level", "" }, - { "nv!__gl_expert_output_mask", "" }, - { "nv!__gl_expert_report_mask", "" }, - { "nv!__gl_extensionstringnvarch", "" }, - { "nv!__gl_extensionstringversion", "" }, - { "nv!__gl_f00f1938", "" }, - { "nv!__gl_f10736", "" }, - { "nv!__gl_f1846870", "" }, - { "nv!__gl_f33bc370", "" }, - { "nv!__gl_f392a874", "" }, - { "nv!__gl_f49ae8", "" }, - { "nv!__gl_fa345cce", "" }, - { "nv!__gl_fa35cc4", "" }, - { "nv!__gl_faa14a", "" }, - { "nv!__gl_faf8a723", "" }, - { "nv!__gl_fastgs", "" }, - { "nv!__gl_fbf4ac45", "" }, - { "nv!__gl_fbo_blit_ignore_srgb", "" }, - { "nv!__gl_fc64c7", "" }, - { "nv!__gl_ff54ec97", "" }, - { "nv!__gl_ff54ec98", "" }, - { "nv!__gl_forceexitprocessdetach", "" }, - { "nv!__gl_forcerequestedesversion", "" }, - { "nv!__gl_glsynctovblank", "" }, - { "nv!__gl_gvitimeoutcontrol", "" }, - { "nv!__gl_hcctrl", "" }, - { "nv!__gl_hwstate_per_ctx", "" }, - { "nv!__gl_machinecachelimit", "" }, - { "nv!__gl_maxframesallowed", "" }, - { "nv!__gl_memmgrcachedalloclimit", "" }, - { "nv!__gl_memmgrcachedalloclimitratio", "" }, - { "nv!__gl_memmgrsysheapalloclimit", "" }, - { "nv!__gl_memmgrsysheapalloclimitratio", "" }, - { "nv!__gl_memmgrvidheapalloclimit", "" }, - { "nv!__gl_mosaic_clip_to_subdev", "" }, - { "nv!__gl_mosaic_clip_to_subdev_h_overlap", "" }, - { "nv!__gl_mosaic_clip_to_subdev_v_overlap", "" }, - { "nv!__gl_overlaymergeblittimerms", "" }, - { "nv!__gl_perfmon_mode", "" }, - { "nv!__gl_pixbar_mode", "" }, - { "nv!__gl_qualityenhancements", "" }, - { "nv!__gl_r27s18q28", "" }, - { "nv!__gl_r2d7c1d8", "" }, - { "nv!__gl_renderer", "" }, - { "nv!__gl_renderqualityflags", "" }, - { "nv!__gl_s3tcquality", "" }, - { "nv!__gl_shaderatomics", "" }, - { "nv!__gl_shadercacheinitsize", "" }, - { "nv!__gl_shader_disk_cache_path", "" }, - { "nv!__gl_shader_disk_cache_read_only", "" }, - { "nv!__gl_shaderobjects", "" }, - { "nv!__gl_shaderportabilitywarnings", "" }, - { "nv!__gl_shaderwarningsaserrors", "" }, - { "nv!__gl_skiptexturehostcopies", "" }, - { "nv!__glslc_debug_level", "" }, - { "nv!__glslc_debug_mask", "" }, - { "nv!__glslc_debug_options", "" }, - { "nv!__glslc_debug_filename", "" }, - { "nv!__gl_sli_dli_control", "" }, - { "nv!__gl_sparsetexture", "" }, - { "nv!__gl_spinlooptimeout", "" }, - { "nv!__gl_sync_to_vblank", "" }, - { "nv!glsynctovblank", "" }, - { "nv!__gl_sysheapreuseratio", "" }, - { "nv!__gl_sysmemtexturepromotion", "" }, - { "nv!__gl_targetflushcount", "" }, - { "nv!__gl_tearingfreeswappresent", "" }, - { "nv!__gl_texclampbehavior", "" }, - { "nv!__gl_texlodbias", "" }, - { "nv!__gl_texmemoryspaceenables", "" }, - { "nv!__gl_textureprecache", "" }, - { "nv!__gl_threadcontrol", "" }, - { "nv!__gl_threadcontrol2", "" }, - { "nv!__gl_usegvievents", "" }, - { "nv!__gl_vbomemoryspaceenables", "" }, - { "nv!__gl_vertexlimit", "" }, - { "nv!__gl_vidheapreuseratio", "" }, - { "nv!__gl_vpipe", "" }, - { "nv!__gl_vpipeformatbloatlimit", "" }, - { "nv!__gl_wglmessageboxonabort", "" }, - { "nv!__gl_writeinfolog", "" }, - { "nv!__gl_writeprogramobjectassembly", "" }, - { "nv!__gl_writeprogramobjectsource", "" }, - { "nv!__gl_xnvadapterpresent", "" }, - { "nv!__gl_yield", "" }, - { "nv!__gl_yieldfunction", "" }, - { "nv!__gl_yieldfunctionfast", "" }, - { "nv!__gl_yieldfunctionslow", "" }, - { "nv!__gl_yieldfunctionwaitfordcqueue", "" }, - { "nv!__gl_yieldfunctionwaitforframe", "" }, - { "nv!__gl_yieldfunctionwaitforgpu", "" }, - { "nv!__gl_zbctableaddhysteresis", "" }, - { "nv!gpu_debug_mode", "" }, - { "nv!gpu_stay_on", "" }, - { "nv!gpu_timeout_ms_max", "" }, - { "nv!gvitimeoutcontrol", "" }, - { "nv!hcctrl", "" }, - { "nv!hwstate_per_ctx", "" }, - { "nv!libandroid_enable_log", "" }, - { "nv!machinecachelimit", "" }, - { "nv!maxframesallowed", "" }, - { "nv!media.aac_51_output_enabled", "" }, - { "nv!memmgrcachedalloclimit", "" }, - { "nv!memmgrcachedalloclimitratio", "" }, - { "nv!memmgrsysheapalloclimit", "" }, - { "nv!memmgrsysheapalloclimitratio", "" }, - { "nv!memmgrvidheapalloclimit", "" }, - { "nv!mosaic_clip_to_subdev", "" }, - { "nv!mosaic_clip_to_subdev_h_overlap", "" }, - { "nv!mosaic_clip_to_subdev_v_overlap", "" }, - { "nv!nvblit.dump", "" }, - { "nv!nvblit.profile", "" }, - { "nv!nvblit.twod", "" }, - { "nv!nvblit.vic", "" }, - { "nv!nvddk_vic_prevent_use", "" }, - { "nv!nv_decompression", "" }, - { "nv!nvdisp_bl_ctrl", "0" }, - { "nv!nvdisp_debug_mask", "" }, - { "nv!nvdisp_enable_ts", "0" }, - { "nv!nvhdcp_timeout_ms", "12000" }, - { "nv!nvhdcp_max_retries", "5" }, - { "nv!nv_emc_dvfs_test", "" }, - { "nv!nv_emc_init_rate_hz", "" }, - { "nv!nv_gmmu_va_page_split", "" }, - { "nv!nv_gmmu_va_range", "" }, - { "nv!nvhost_debug_mask", "" }, - { "nv!nvidia.hwc.dump_config", "" }, - { "nv!nvidia.hwc.dump_layerlist", "" }, - { "nv!nvidia.hwc.dump_windows", "" }, - { "nv!nvidia.hwc.enable_disp_trans", "" }, - { "nv!nvidia.hwc.ftrace_enable", "" }, - { "nv!nvidia.hwc.hdcp_enable", "" }, - { "nv!nvidia.hwc.hidden_window_mask0", "" }, - { "nv!nvidia.hwc.hidden_window_mask1", "" }, - { "nv!nvidia.hwc.immediate_modeset", "" }, - { "nv!nvidia.hwc.imp_enable", "" }, - { "nv!nvidia.hwc.no_egl", "" }, - { "nv!nvidia.hwc.no_scratchblit", "" }, - { "nv!nvidia.hwc.no_vic", "" }, - { "nv!nvidia.hwc.null_display", "" }, - { "nv!nvidia.hwc.scan_props", "" }, - { "nv!nvidia.hwc.swap_interval", "" }, - { "nv!nvidia.hwc.war_1515812", "0" }, - { "nv!nvmap_debug_mask", "" }, - { "nv!nv_memory_profiler", "" }, - { "nv!nvnflinger_enable_log", "" }, - { "nv!nvnflinger_flip_policy", "" }, - { "nv!nvnflinger_hotplug_autoswitch", "0" }, - { "nv!nvnflinger_prefer_primary_layer", "0" }, - { "nv!nvnflinger_service_priority", "" }, - { "nv!nvnflinger_service_threads", "" }, - { "nv!nvnflinger_swap_interval", "" }, - { "nv!nvnflinger_track_perf", "" }, - { "nv!nvnflinger_virtualdisplay_policy", "60hz" }, - { "nv!nvn_no_vsync_capability", false }, - { "nv!nvn_through_opengl", "" }, - { "nv!nv_pllcx_always_on", "" }, - { "nv!nv_pllcx_safe_div", "" }, - { "nv!nvrm_gpu_channel_interleave", "" }, - { "nv!nvrm_gpu_channel_priority", "" }, - { "nv!nvrm_gpu_channel_timeslice", "" }, - { "nv!nvrm_gpu_default_device_index", "" }, - { "nv!nvrm_gpu_dummy", "" }, - { "nv!nvrm_gpu_help", "" }, - { "nv!nvrm_gpu_nvgpu_disable", "" }, - { "nv!nvrm_gpu_nvgpu_do_nfa_partial_map", "" }, - { "nv!nvrm_gpu_nvgpu_ecc_overrides", "" }, - { "nv!nvrm_gpu_nvgpu_no_as_get_va_regions", "" }, - { "nv!nvrm_gpu_nvgpu_no_channel_abort", "" }, - { "nv!nvrm_gpu_nvgpu_no_cyclestats", "" }, - { "nv!nvrm_gpu_nvgpu_no_fixed", "" }, - { "nv!nvrm_gpu_nvgpu_no_gpu_characteristics", "" }, - { "nv!nvrm_gpu_nvgpu_no_ioctl_mutex", "" }, - { "nv!nvrm_gpu_nvgpu_no_map_buffer_ex", "" }, - { "nv!nvrm_gpu_nvgpu_no_robustness", "" }, - { "nv!nvrm_gpu_nvgpu_no_sparse", "" }, - { "nv!nvrm_gpu_nvgpu_no_syncpoints", "" }, - { "nv!nvrm_gpu_nvgpu_no_tsg", "" }, - { "nv!nvrm_gpu_nvgpu_no_zbc", "" }, - { "nv!nvrm_gpu_nvgpu_no_zcull", "" }, - { "nv!nvrm_gpu_nvgpu_wrap_channels_in_tsgs", "" }, - { "nv!nvrm_gpu_prevent_use", "" }, - { "nv!nvrm_gpu_trace", "" }, - { "nv!nvsched_debug_mask", "" }, - { "nv!nvsched_force_enable", "" }, - { "nv!nvsched_force_log", "" }, - { "nv!nv_usb_plls_hw_ctrl", "" }, - { "nv!nv_winsys", "" }, - { "nv!nvwsi_dump", "" }, - { "nv!nvwsi_fill", "" }, - { "nv!ogl_", "" }, - { "nv!ogl_0356afd0", "" }, - { "nv!ogl_0356afd1", "" }, - { "nv!ogl_0356afd2", "" }, - { "nv!ogl_0356afd3", "" }, - { "nv!ogl_0x923dc0", "" }, - { "nv!ogl_0x923dc1", "" }, - { "nv!ogl_0x923dc2", "" }, - { "nv!ogl_0x923dc3", "" }, - { "nv!ogl_0x923dc4", "" }, - { "nv!ogl_0x923dd3", "" }, - { "nv!ogl_0x9abdc5", "" }, - { "nv!ogl_0x9abdc6", "" }, - { "nv!ogl_0xbd10fb", "" }, - { "nv!ogl_0xce2348", "" }, - { "nv!ogl_10261989", "" }, - { "nv!ogl_1042d483", "" }, - { "nv!ogl_10572898", "" }, - { "nv!ogl_115631", "" }, - { "nv!ogl_12950094", "" }, - { "nv!ogl_1314f311", "" }, - { "nv!ogl_1314f312", "" }, - { "nv!ogl_13279512", "" }, - { "nv!ogl_13813496", "" }, - { "nv!ogl_14507179", "" }, - { "nv!ogl_15694569", "" }, - { "nv!ogl_16936964", "" }, - { "nv!ogl_17aa230c", "" }, - { "nv!ogl_182054", "" }, - { "nv!ogl_18273275", "" }, - { "nv!ogl_18273276", "" }, - { "nv!ogl_1854d03b", "" }, - { "nv!ogl_18add00d", "" }, - { "nv!ogl_19156670", "" }, - { "nv!ogl_19286545", "" }, - { "nv!ogl_1a298e9f", "" }, - { "nv!ogl_1acf43fe", "" }, - { "nv!ogl_1bda43fe", "" }, - { "nv!ogl_1c3b92", "" }, - { "nv!ogl_21509920", "" }, - { "nv!ogl_215323457", "" }, - { "nv!ogl_2165ad", "" }, - { "nv!ogl_2165ae", "" }, - { "nv!ogl_21be9c", "" }, - { "nv!ogl_233264316", "" }, - { "nv!ogl_234557580", "" }, - { "nv!ogl_23cd0e", "" }, - { "nv!ogl_24189123", "" }, - { "nv!ogl_2443266", "" }, - { "nv!ogl_25025519", "" }, - { "nv!ogl_255e39", "" }, - { "nv!ogl_2583364", "" }, - { "nv!ogl_2888c1", "" }, - { "nv!ogl_28ca3e", "" }, - { "nv!ogl_29871243", "" }, - { "nv!ogl_2a1f64", "" }, - { "nv!ogl_2dc432", "" }, - { "nv!ogl_2de437", "" }, - { "nv!ogl_2f3bb89c", "" }, - { "nv!ogl_2fd652", "" }, - { "nv!ogl_3001ac", "" }, - { "nv!ogl_31298772", "" }, - { "nv!ogl_313233", "" }, - { "nv!ogl_31f7d603", "" }, - { "nv!ogl_320ce4", "" }, - { "nv!ogl_32153248", "" }, - { "nv!ogl_32153249", "" }, - { "nv!ogl_335bca", "" }, - { "nv!ogl_342abb", "" }, - { "nv!ogl_34dfe6", "" }, - { "nv!ogl_34dfe7", "" }, - { "nv!ogl_34dfe8", "" }, - { "nv!ogl_34dfe9", "" }, - { "nv!ogl_35201578", "" }, - { "nv!ogl_359278", "" }, - { "nv!ogl_37f53a", "" }, - { "nv!ogl_38144972", "" }, - { "nv!ogl_38542646", "" }, - { "nv!ogl_3b74c9", "" }, - { "nv!ogl_3c136f", "" }, - { "nv!ogl_3cf72823", "" }, - { "nv!ogl_3d7af029", "" }, - { "nv!ogl_3ff34782", "" }, - { "nv!ogl_4129618", "" }, - { "nv!ogl_4189fac3", "" }, - { "nv!ogl_420bd4", "" }, - { "nv!ogl_42a699", "" }, - { "nv!ogl_441369", "" }, - { "nv!ogl_4458713e", "" }, - { "nv!ogl_4554b6", "" }, - { "nv!ogl_457425", "" }, - { "nv!ogl_4603b207", "" }, - { "nv!ogl_46574957", "" }, - { "nv!ogl_46574958", "" }, - { "nv!ogl_46813529", "" }, - { "nv!ogl_46f1e13d", "" }, - { "nv!ogl_47534c43", "" }, - { "nv!ogl_48550336", "" }, - { "nv!ogl_48576893", "" }, - { "nv!ogl_48576894", "" }, - { "nv!ogl_4889ac02", "" }, - { "nv!ogl_49005740", "" }, - { "nv!ogl_49867584", "" }, - { "nv!ogl_49960973", "" }, - { "nv!ogl_4a5341", "" }, - { "nv!ogl_4f4e48", "" }, - { "nv!ogl_4f8a0a", "" }, - { "nv!ogl_50299698", "" }, - { "nv!ogl_50299699", "" }, - { "nv!ogl_50361291", "" }, - { "nv!ogl_5242ae", "" }, - { "nv!ogl_53d30c", "" }, - { "nv!ogl_56347a", "" }, - { "nv!ogl_563a95f1", "" }, - { "nv!ogl_573823", "" }, - { "nv!ogl_58027529", "" }, - { "nv!ogl_5d2d63", "" }, - { "nv!ogl_5f7e3b", "" }, - { "nv!ogl_60461793", "" }, - { "nv!ogl_60d355", "" }, - { "nv!ogl_616627aa", "" }, - { "nv!ogl_62317182", "" }, - { "nv!ogl_6253fa2e", "" }, - { "nv!ogl_64100768", "" }, - { "nv!ogl_64100769", "" }, - { "nv!ogl_64100770", "" }, - { "nv!ogl_647395", "" }, - { "nv!ogl_66543234", "" }, - { "nv!ogl_67674763", "" }, - { "nv!ogl_67739784", "" }, - { "nv!ogl_68fb9c", "" }, - { "nv!ogl_69801276", "" }, - { "nv!ogl_6af9fa2f", "" }, - { "nv!ogl_6af9fa3f", "" }, - { "nv!ogl_6af9fa4f", "" }, - { "nv!ogl_6bd8c7", "" }, - { "nv!ogl_6c7691", "" }, - { "nv!ogl_6d4296ce", "" }, - { "nv!ogl_6dd7e7", "" }, - { "nv!ogl_6dd7e8", "" }, - { "nv!ogl_6fe11ec1", "" }, - { "nv!ogl_716511763", "" }, - { "nv!ogl_72504593", "" }, - { "nv!ogl_73304097", "" }, - { "nv!ogl_73314098", "" }, - { "nv!ogl_74095213", "" }, - { "nv!ogl_74095213a", "" }, - { "nv!ogl_74095213b", "" }, - { "nv!ogl_74095214", "" }, - { "nv!ogl_748f9649", "" }, - { "nv!ogl_75494732", "" }, - { "nv!ogl_78452832", "" }, - { "nv!ogl_784561", "" }, - { "nv!ogl_78e16b9c", "" }, - { "nv!ogl_79251225", "" }, - { "nv!ogl_7c128b", "" }, - { "nv!ogl_7ccd93", "" }, - { "nv!ogl_7df8d1", "" }, - { "nv!ogl_800c2310", "" }, - { "nv!ogl_80546710", "" }, - { "nv!ogl_80772310", "" }, - { "nv!ogl_808ee280", "" }, - { "nv!ogl_81131154", "" }, - { "nv!ogl_81274457", "" }, - { "nv!ogl_8292291f", "" }, - { "nv!ogl_83498426", "" }, - { "nv!ogl_84993794", "" }, - { "nv!ogl_84995585", "" }, - { "nv!ogl_84a0a0", "" }, - { "nv!ogl_852142", "" }, - { "nv!ogl_85612309", "" }, - { "nv!ogl_85612310", "" }, - { "nv!ogl_85612311", "" }, - { "nv!ogl_85612312", "" }, - { "nv!ogl_8623ff27", "" }, - { "nv!ogl_87364952", "" }, - { "nv!ogl_87f6275666", "" }, - { "nv!ogl_886748", "" }, - { "nv!ogl_89894423", "" }, - { "nv!ogl_8ad8a75", "" }, - { "nv!ogl_8ad8ad00", "" }, - { "nv!ogl_8bb815", "" }, - { "nv!ogl_8bb817", "" }, - { "nv!ogl_8bb818", "" }, - { "nv!ogl_8bb819", "" }, - { "nv!ogl_8e640cd1", "" }, - { "nv!ogl_8f34971a", "" }, - { "nv!ogl_8f773984", "" }, - { "nv!ogl_8f7a7d", "" }, - { "nv!ogl_902486209", "" }, - { "nv!ogl_90482571", "" }, - { "nv!ogl_91214835", "" }, - { "nv!ogl_912848290", "" }, - { "nv!ogl_915e56", "" }, - { "nv!ogl_92179063", "" }, - { "nv!ogl_92179064", "" }, - { "nv!ogl_92179065", "" }, - { "nv!ogl_92179066", "" }, - { "nv!ogl_92350358", "" }, - { "nv!ogl_92809063", "" }, - { "nv!ogl_92809064", "" }, - { "nv!ogl_92809065", "" }, - { "nv!ogl_92809066", "" }, - { "nv!ogl_92920143", "" }, - { "nv!ogl_93a89b12", "" }, - { "nv!ogl_93a89c0b", "" }, - { "nv!ogl_94812574", "" }, - { "nv!ogl_95282304", "" }, - { "nv!ogl_95394027", "" }, - { "nv!ogl_959b1f", "" }, - { "nv!ogl_9638af", "" }, - { "nv!ogl_96fd59", "" }, - { "nv!ogl_97f6275666", "" }, - { "nv!ogl_97f6275667", "" }, - { "nv!ogl_97f6275668", "" }, - { "nv!ogl_97f6275669", "" }, - { "nv!ogl_97f627566a", "" }, - { "nv!ogl_97f627566b", "" }, - { "nv!ogl_97f627566d", "" }, - { "nv!ogl_97f627566e", "" }, - { "nv!ogl_97f627566f", "" }, - { "nv!ogl_97f6275670", "" }, - { "nv!ogl_97f6275671", "" }, - { "nv!ogl_97f727566e", "" }, - { "nv!ogl_98480775", "" }, - { "nv!ogl_98480776", "" }, - { "nv!ogl_98480777", "" }, - { "nv!ogl_992431", "" }, - { "nv!ogl_9aa29065", "" }, - { "nv!ogl_9af32c", "" }, - { "nv!ogl_9af32d", "" }, - { "nv!ogl_9af32e", "" }, - { "nv!ogl_9c108b71", "" }, - { "nv!ogl_9f279065", "" }, - { "nv!ogl_a01bc728", "" }, - { "nv!ogl_a13b46c80", "" }, - { "nv!ogl_a22eb0", "" }, - { "nv!ogl_a2fb451e", "" }, - { "nv!ogl_a3456abe", "" }, - { "nv!ogl_a7044887", "" }, - { "nv!ogl_a7149200", "" }, - { "nv!ogl_a766215670", "" }, - { "nv!ogl_aalinegamma", "" }, - { "nv!ogl_aalinetweaks", "" }, - { "nv!ogl_ab34ee01", "" }, - { "nv!ogl_ab34ee02", "" }, - { "nv!ogl_ab34ee03", "" }, - { "nv!ogl_ac0274", "" }, - { "nv!ogl_af73c63e", "" }, - { "nv!ogl_af73c63f", "" }, - { "nv!ogl_af9927", "" }, - { "nv!ogl_afoverride", "" }, - { "nv!ogl_allocdeviceevents", "" }, - { "nv!ogl_applicationkey", "" }, - { "nv!ogl_appreturnonlybasicglsltype", "" }, - { "nv!ogl_app_softimage", "" }, - { "nv!ogl_app_supportbits2", "" }, - { "nv!ogl_assumetextureismipmappedatcreation", "" }, - { "nv!ogl_b1fb0f01", "" }, - { "nv!ogl_b3edd5", "" }, - { "nv!ogl_b40d9e03d", "" }, - { "nv!ogl_b7f6275666", "" }, - { "nv!ogl_b812c1", "" }, - { "nv!ogl_ba14ba1a", "" }, - { "nv!ogl_ba14ba1b", "" }, - { "nv!ogl_bd7559", "" }, - { "nv!ogl_bd755a", "" }, - { "nv!ogl_bd755c", "" }, - { "nv!ogl_bd755d", "" }, - { "nv!ogl_be58bb", "" }, - { "nv!ogl_be92cb", "" }, - { "nv!ogl_beefcba3", "" }, - { "nv!ogl_beefcba4", "" }, - { "nv!ogl_c023777f", "" }, - { "nv!ogl_c09dc8", "" }, - { "nv!ogl_c0d340", "" }, - { "nv!ogl_c2ff374c", "" }, - { "nv!ogl_c5e9d7a3", "" }, - { "nv!ogl_c5e9d7a4", "" }, - { "nv!ogl_c5e9d7b4", "" }, - { "nv!ogl_c618f9", "" }, - { "nv!ogl_ca345840", "" }, - { "nv!ogl_cachedisable", "" }, - { "nv!ogl_channelpriorityoverride", "" }, - { "nv!ogl_cleardatastorevidmem", "" }, - { "nv!ogl_cmdbufmemoryspaceenables", "" }, - { "nv!ogl_cmdbufminwords", "" }, - { "nv!ogl_cmdbufsizewords", "" }, - { "nv!ogl_conformantblitframebufferscissor", "" }, - { "nv!ogl_conformantincompletetextures", "" }, - { "nv!ogl_copybuffermethod", "" }, - { "nv!ogl_cubemapaniso", "" }, - { "nv!ogl_cubemapfiltering", "" }, - { "nv!ogl_d0e9a4d7", "" }, - { "nv!ogl_d13733f12", "" }, - { "nv!ogl_d1b399", "" }, - { "nv!ogl_d2983c32", "" }, - { "nv!ogl_d2983c33", "" }, - { "nv!ogl_d2e71b", "" }, - { "nv!ogl_d377dc", "" }, - { "nv!ogl_d377dd", "" }, - { "nv!ogl_d489f4", "" }, - { "nv!ogl_d4bce1", "" }, - { "nv!ogl_d518cb", "" }, - { "nv!ogl_d518cd", "" }, - { "nv!ogl_d518ce", "" }, - { "nv!ogl_d518d0", "" }, - { "nv!ogl_d518d1", "" }, - { "nv!ogl_d518d2", "" }, - { "nv!ogl_d518d3", "" }, - { "nv!ogl_d518d4", "" }, - { "nv!ogl_d518d5", "" }, - { "nv!ogl_d59eda", "" }, - { "nv!ogl_d83cbd", "" }, - { "nv!ogl_d8e777", "" }, - { "nv!ogl_debug_level", "" }, - { "nv!ogl_debug_mask", "" }, - { "nv!ogl_debug_options", "" }, - { "nv!ogl_devshmpageableallocations", "" }, - { "nv!ogl_df1f9812", "" }, - { "nv!ogl_df783c", "" }, - { "nv!ogl_diagenable", "" }, - { "nv!ogl_disallowcemask", "" }, - { "nv!ogl_disallowz16", "" }, - { "nv!ogl_dlmemoryspaceenables", "" }, - { "nv!ogl_e0bfec", "" }, - { "nv!ogl_e433456d", "" }, - { "nv!ogl_e435563f", "" }, - { "nv!ogl_e4cd9c", "" }, - { "nv!ogl_e5c972", "" }, - { "nv!ogl_e639ef", "" }, - { "nv!ogl_e802af", "" }, - { "nv!ogl_eae964", "" }, - { "nv!ogl_earlytexturehwallocation", "" }, - { "nv!ogl_eb92a3", "" }, - { "nv!ogl_ebca56", "" }, - { "nv!ogl_expert_detail_level", "" }, - { "nv!ogl_expert_output_mask", "" }, - { "nv!ogl_expert_report_mask", "" }, - { "nv!ogl_extensionstringnvarch", "" }, - { "nv!ogl_extensionstringversion", "" }, - { "nv!ogl_f00f1938", "" }, - { "nv!ogl_f10736", "" }, - { "nv!ogl_f1846870", "" }, - { "nv!ogl_f33bc370", "" }, - { "nv!ogl_f392a874", "" }, - { "nv!ogl_f49ae8", "" }, - { "nv!ogl_fa345cce", "" }, - { "nv!ogl_fa35cc4", "" }, - { "nv!ogl_faa14a", "" }, - { "nv!ogl_faf8a723", "" }, - { "nv!ogl_fastgs", "" }, - { "nv!ogl_fbf4ac45", "" }, - { "nv!ogl_fbo_blit_ignore_srgb", "" }, - { "nv!ogl_fc64c7", "" }, - { "nv!ogl_ff54ec97", "" }, - { "nv!ogl_ff54ec98", "" }, - { "nv!ogl_forceexitprocessdetach", "" }, - { "nv!ogl_forcerequestedesversion", "" }, - { "nv!ogl_glsynctovblank", "" }, - { "nv!ogl_gvitimeoutcontrol", "" }, - { "nv!ogl_hcctrl", "" }, - { "nv!ogl_hwstate_per_ctx", "" }, - { "nv!ogl_machinecachelimit", "" }, - { "nv!ogl_maxframesallowed", "" }, - { "nv!ogl_memmgrcachedalloclimit", "" }, - { "nv!ogl_memmgrcachedalloclimitratio", "" }, - { "nv!ogl_memmgrsysheapalloclimit", "" }, - { "nv!ogl_memmgrsysheapalloclimitratio", "" }, - { "nv!ogl_memmgrvidheapalloclimit", "" }, - { "nv!ogl_mosaic_clip_to_subdev", "" }, - { "nv!ogl_mosaic_clip_to_subdev_h_overlap", "" }, - { "nv!ogl_mosaic_clip_to_subdev_v_overlap", "" }, - { "nv!ogl_overlaymergeblittimerms", "" }, - { "nv!ogl_perfmon_mode", "" }, - { "nv!ogl_pixbar_mode", "" }, - { "nv!ogl_qualityenhancements", "" }, - { "nv!ogl_r27s18q28", "" }, - { "nv!ogl_r2d7c1d8", "" }, - { "nv!ogl_renderer", "" }, - { "nv!ogl_renderqualityflags", "" }, - { "nv!ogl_s3tcquality", "" }, - { "nv!ogl_shaderatomics", "" }, - { "nv!ogl_shadercacheinitsize", "" }, - { "nv!ogl_shader_disk_cache_path", "" }, - { "nv!ogl_shader_disk_cache_read_only", "" }, - { "nv!ogl_shaderobjects", "" }, - { "nv!ogl_shaderportabilitywarnings", "" }, - { "nv!ogl_shaderwarningsaserrors", "" }, - { "nv!ogl_skiptexturehostcopies", "" }, - { "nv!ogl_sli_dli_control", "" }, - { "nv!ogl_sparsetexture", "" }, - { "nv!ogl_spinlooptimeout", "" }, - { "nv!ogl_sync_to_vblank", "" }, - { "nv!ogl_sysheapreuseratio", "" }, - { "nv!ogl_sysmemtexturepromotion", "" }, - { "nv!ogl_targetflushcount", "" }, - { "nv!ogl_tearingfreeswappresent", "" }, - { "nv!ogl_texclampbehavior", "" }, - { "nv!ogl_texlodbias", "" }, - { "nv!ogl_texmemoryspaceenables", "" }, - { "nv!ogl_textureprecache", "" }, - { "nv!ogl_threadcontrol", "" }, - { "nv!ogl_threadcontrol2", "" }, - { "nv!ogl_usegvievents", "" }, - { "nv!ogl_vbomemoryspaceenables", "" }, - { "nv!ogl_vertexlimit", "" }, - { "nv!ogl_vidheapreuseratio", "" }, - { "nv!ogl_vpipe", "" }, - { "nv!ogl_vpipeformatbloatlimit", "" }, - { "nv!ogl_wglmessageboxonabort", "" }, - { "nv!ogl_writeinfolog", "" }, - { "nv!ogl_writeprogramobjectassembly", "" }, - { "nv!ogl_writeprogramobjectsource", "" }, - { "nv!ogl_xnvadapterpresent", "" }, - { "nv!ogl_yield", "" }, - { "nv!ogl_yieldfunction", "" }, - { "nv!ogl_yieldfunctionfast", "" }, - { "nv!ogl_yieldfunctionslow", "" }, - { "nv!ogl_yieldfunctionwaitfordcqueue", "" }, - { "nv!ogl_yieldfunctionwaitforframe", "" }, - { "nv!ogl_yieldfunctionwaitforgpu", "" }, - { "nv!ogl_zbctableaddhysteresis", "" }, - { "nv!overlaymergeblittimerms", "" }, - { "nv!perfmon_mode", "" }, - { "nv!persist.sys.display.resolution", "" }, - { "nv!persist.tegra.composite.fallb", "" }, - { "nv!persist.tegra.composite.policy", "" }, - { "nv!persist.tegra.composite.range", "" }, - { "nv!persist.tegra.compositor", "" }, - { "nv!persist.tegra.compositor.virt", "" }, - { "nv!persist.tegra.compression", "" }, - { "nv!persist.tegra.cursor.enable", "" }, - { "nv!persist.tegra.didim.enable", "" }, - { "nv!persist.tegra.didim.normal", "" }, - { "nv!persist.tegra.didim.video", "" }, - { "nv!persist.tegra.disp.heads", "" }, - { "nv!persist.tegra.gamma_correction", "" }, - { "nv!persist.tegra.gpu_mapping_cache", "" }, - { "nv!persist.tegra.grlayout", "" }, - { "nv!persist.tegra.hdmi.2020.10", "" }, - { "nv!persist.tegra.hdmi.2020.fake", "" }, - { "nv!persist.tegra.hdmi.2020.force", "" }, - { "nv!persist.tegra.hdmi.autorotate", "" }, - { "nv!persist.tegra.hdmi.hdr.fake", "" }, - { "nv!persist.tegra.hdmi.ignore_ratio", "" }, - { "nv!persist.tegra.hdmi.limit.clock", "" }, - { "nv!persist.tegra.hdmi.only_16_9", "" }, - { "nv!persist.tegra.hdmi.range", "" }, - { "nv!persist.tegra.hdmi.resolution", "" }, - { "nv!persist.tegra.hdmi.underscan", "" }, - { "nv!persist.tegra.hdmi.yuv.422", "" }, - { "nv!persist.tegra.hdmi.yuv.444", "" }, - { "nv!persist.tegra.hdmi.yuv.enable", "" }, - { "nv!persist.tegra.hdmi.yuv.force", "" }, - { "nv!persist.tegra.hwc.nvdc", "" }, - { "nv!persist.tegra.idle.minimum_fps", "" }, - { "nv!persist.tegra.panel.rotation", "" }, - { "nv!persist.tegra.scan_props", "" }, - { "nv!persist.tegra.stb.mode", "" }, - { "nv!persist.tegra.zbc_override", "" }, - { "nv!pixbar_mode", "" }, - { "nv!qualityenhancements", "" }, - { "nv!r27s18q28", "" }, - { "nv!r2d7c1d8", "" }, - { "nv!renderer", "" }, - { "nv!renderqualityflags", "" }, - { "nv!rmos_debug_mask", "" }, - { "nv!rmos_set_production_mode", "" }, - { "nv!s3tcquality", "" }, - { "nv!shaderatomics", "" }, - { "nv!shadercacheinitsize", "" }, - { "nv!shader_disk_cache_path", "" }, - { "nv!shader_disk_cache_read_only", "" }, - { "nv!shaderobjects", "" }, - { "nv!shaderportabilitywarnings", "" }, - { "nv!shaderwarningsaserrors", "" }, - { "nv!skiptexturehostcopies", "" }, - { "nv!sli_dli_control", "" }, - { "nv!sparsetexture", "" }, - { "nv!spinlooptimeout", "" }, - { "nv!sync_to_vblank", "" }, - { "nv!sysheapreuseratio", "" }, - { "nv!sysmemtexturepromotion", "" }, - { "nv!targetflushcount", "" }, - { "nv!tearingfreeswappresent", "" }, - { "nv!tegra.refresh", "" }, - { "nv!texclampbehavior", "" }, - { "nv!texlodbias", "" }, - { "nv!texmemoryspaceenables", "" }, - { "nv!textureprecache", "" }, - { "nv!threadcontrol", "" }, - { "nv!threadcontrol2", "" }, - { "nv!tvmr.avp.logs", "" }, - { "nv!tvmr.buffer.logs", "" }, - { "nv!tvmr.dec.prof", "" }, - { "nv!tvmr.deint.logs", "" }, - { "nv!tvmr.dfs.logs", "" }, - { "nv!tvmr.ffprof.logs", "" }, - { "nv!tvmr.game.stream", "" }, - { "nv!tvmr.general.logs", "" }, - { "nv!tvmr.input.dump", "" }, - { "nv!tvmr.seeking.logs", "" }, - { "nv!tvmr.ts_pulldown", "" }, - { "nv!usegvievents", "" }, - { "nv!vbomemoryspaceenables", "" }, - { "nv!vcc_debug_ip", "" }, - { "nv!vcc_verbose_level", "" }, - { "nv!vertexlimit", "" }, - { "nv!viccomposer.filter", "" }, - { "nv!videostats-enable", "" }, - { "nv!vidheapreuseratio", "" }, - { "nv!vpipe", "" }, - { "nv!vpipeformatbloatlimit", "" }, - { "nv!wglmessageboxonabort", "" }, - { "nv!writeinfolog", "" }, - { "nv!writeprogramobjectassembly", "" }, - { "nv!writeprogramobjectsource", "" }, - { "nv!xnvadapterpresent", "" }, - { "nv!yield", "" }, - { "nv!yieldfunction", "" }, - { "nv!yieldfunctionfast", "" }, - { "nv!yieldfunctionslow", "" }, - { "nv!yieldfunctionwaitfordcqueue", "" }, - { "nv!yieldfunctionwaitforframe", "" }, - { "nv!yieldfunctionwaitforgpu", "" }, - { "nv!zbctableaddhysteresis", "" }, - { "pcm!enable", true }, - { "pctl!intermittent_task_interval_seconds", 21600 }, - { "prepo!devmenu_prepo_page_view", false }, - { "prepo!background_processing", true }, - { "prepo!transmission_interval_min", 10 }, - { "prepo!transmission_retry_interval", 3600 }, - { "psm!evaluation_log_enabled", false }, - { "snap_shot_dump!auto_dump", false }, - { "snap_shot_dump!output_dir", "%USERPROFILE%/Documents/Nintendo/NXDMP" }, - { "snap_shot_dump!full_dump", false }, - { "systemconfig!field_testing", false }, - { "systemconfig!exhivision", false }, - { "systempowerstate!always_reboot", false }, - { "systempowerstate!power_state_message_emulation_trigger_time", 0 }, - { "systempowerstate!power_state_message_to_emulate", 0 }, - { "target_manager!device_name", "" }, - { "vulnerability!needs_update_vulnerability_policy", 0 }, - { "apm!performance_mode_policy", "auto" }, - { "apm!sdev_throttling_enabled", true }, - { "apm!sdev_throttling_additional_delay_us", 16000 }, - { "apm!battery_draining_enabled", false }, - { "apm!sdev_cpu_overclock_enabled", false }, - { "bcat!production_mode", true }, - { "bpc!enable_quasi_off", true }, - { "bsp0!usb", "UDS" }, - { "bsp0!tm_transport", "USB" }, - { "bluetooth_debug!skip_boot", false }, - { "contents_delivery!enable_debug_api", false }, - { "eupld!upload_enabled", true }, - { "fatal!transition_to_fatal", true }, - { "fatal!show_extra_info", false }, - { "gpu_core_dump!auto_dump", false }, - { "hid_debug!enables_debugpad", false }, - { "hid_debug!manages_devices", true }, - { "hid_debug!emulate_future_device", false }, - { "hid_debug!emulate_firmware_update_failure", false }, - { "hid_debug!emulate_mcu_hardware_error", false }, - { "hid_debug!firmware_update_failure_emulation_mode", 0 }, - { "jit_debug!enable_jit_debug", false }, - { "npns!background_processing", true }, - { "npns!logmanager_redirection", true }, - { "npns!sleep_processing_timeout", 30 }, - { "npns!sleep_periodic_interval", 10800 }, - { "npns!sleep_max_try_count", 5 }, - { "npns!test_mode", false }, - { "ns.applet!overlay_applet_id", "0x010000000000100c" }, - { "ns.applet!system_applet_id", "0x0100000000001000" }, - { "ns.applet!shop_applet_id", "0x010000000000100b" }, - { "ns.autoboot!enabled", true }, - { "ns.pseudodeviceid!reset_pseudo_device_id", false }, - { "nsd!environment_identifier", "lp1" }, - { "nsd!test_mode", false }, - { "ntc!is_autonomic_correction_enabled", true }, - { "ntc!autonomic_correction_interval_seconds", 432000 }, - { "ntc!autonomic_correction_failed_retry_interval_seconds", 1800 }, - { "ntc!autonomic_correction_immediate_try_count_max", 4 }, - { "ntc!autonomic_correction_immediate_try_interval_milliseconds", 5000 }, - { "nv!nv_graphics_firmware_memory_margin", false }, - { "omm!operation_mode_policy", "auto" }, - { "omm!sleep_fade_in_ms", 50 }, - { "omm!sleep_fade_out_ms", 100 }, - { "omm!charging_sign_ms", 3000 }, - { "omm!low_battery_sign_ms", 3000 }, - { "omm!sign_fade_in_ms", 0 }, - { "omm!sign_fade_out_ms", 400 }, - { "omm!sign_wait_layer_visible_ms", 100 }, - { "omm!startup_fade_in_ms", 200 }, - { "omm!startup_fade_out_ms", 400 }, - { "omm!backlight_off_ms_on_handheld_switch", 150 }, - { "omm!sleep_on_ac_ok_boot", true }, - { "pdm!save_playlog", true }, - { "productinfo!product_name", "Nintendo Switch" }, - { "productinfo!cec_osd_name", "NintendoSwitch" }, - { "ro!ease_nro_restriction", false }, - { "settings_debug!is_debug_mode_enabled", false }, - { "systemreport!enabled", true }, - { "systemsleep!enter_sleep", true }, - { "systemsleep!enter_sc7", true }, - { "systemsleep!keep_vdd_core", true }, - { "systemsleep!disable_tma_sleep", false }, - { "systemsleep!disable_auto_sleep", false }, - { "systemsleep!override_auto_sleep_time", 0 }, - { "systemsleep!sleep_pending_time_ms", 15000 }, - { "systemsleep!hush_time_after_brief_power_button_press_ms", 1000 }, - { "systemsleep!transition_timeout_sec", 60 }, - { "systemsleep!dummy_event_auto_wake", false }, - { "systemupdate!debug_id", "0x0000000000000000" }, - { "systemupdate!debug_version", 0 }, - { "systemupdate!bgnup_retry_seconds", 60 }, - { "systemupdate!enable_background_download_stress_testing", false }, - { "systemupdate!debug_id_for_content_delivery", "0x0000000000000000" }, - { "systemupdate!debug_version_for_content_delivery", 0 }, - { "systemupdate!assumed_system_applet_version", 0 }, - { "tc!iir_filter_gain_soc", 100 }, - { "tc!iir_filter_gain_pcb", 100 }, - { "tc!tskin_soc_coefficients_handheld", "[5464, 174190]" }, - { "tc!tskin_soc_coefficients_console", "[6182, 112480]" }, - { "tc!tskin_pcb_coefficients_handheld", "[5464, 174190]" }, - { "tc!tskin_pcb_coefficients_console", "[6182, 112480]" }, - { "tc!tskin_select", "both" }, - { "tc!tskin_rate_table_handheld", "[[-1000000, 40000, 0, 0], [36000, 43000, 51, 51], [43000, 48000, 51, 102], [48000, 53000, 102, 153], [53000, 1000000, 153, 153], [48000, 1000000, 153, 153]]" }, - { "tc!tskin_rate_table_console", "[[-1000000, 43000, 51, 51], [43000, 53000, 51, 153], [53000, 58000, 153, 255], [58000, 1000000, 255, 255]]" }, - { "tc!rate_select", "both" }, - { "tc!log_enabled", false }, - { "tc!sleep_enabled", true }, - { "time!standard_steady_clock_test_offset_minutes", 0 }, - { "time!standard_steady_clock_rtc_update_interval_minutes", 5 }, - { "time!standard_network_clock_sufficient_accuracy_minutes", 43200 }, - { "time!standard_user_clock_initial_year", 2019 }, - { "usb!usb30_force_enabled", false }, - { "wlan_debug!skip_wlan_boot", false } - }; - } + { + // Generated automatically from a Switch 3.0 config file (Tid: 0100000000000818). + public static Dictionary Settings = new Dictionary + { + { "account!na_required_for_network_service", true }, + { "account.daemon!background_awaking_periodicity", 10800 }, + { "account.daemon!schedule_periodicity", 3600 }, + { "account.daemon!profile_sync_interval", 18000 }, + { "account.daemon!na_info_refresh_interval", 46800 }, + { "am.display!transition_layer_enabled", true }, + { "am.gpu!gpu_scheduling_enabled", true }, + { "am.gpu!gpu_scheduling_frame_time_us", 116666 }, + { "am.gpu!gpu_scheduling_fg_app_us", 116166 }, + { "am.gpu!gpu_scheduling_bg_app_us", 104500 }, + { "am.gpu!gpu_scheduling_oa_us", 500 }, + { "am.gpu!gpu_scheduling_fg_sa_us", 11666 }, + { "am.gpu!gpu_scheduling_bg_sa_us", 0 }, + { "am.gpu!gpu_scheduling_fg_la_us", 11666 }, + { "am.gpu!gpu_scheduling_partial_fg_la_us", 2000 }, + { "am.gpu!gpu_scheduling_bg_la_us", 0 }, + { "audio!audren_log_enabled", false }, + { "audio!audout_log_enabled", false }, + { "audio!audin_log_enabled", false }, + { "audio!hwopus_log_enabled", false }, + { "audio!adsp_log_enabled", false }, + { "audio!suspend_for_debugger_enabled", false }, + { "audio!uac_speaker_enabled", false }, + { "bgtc!enable_halfawake", 1 }, + { "bgtc!enable_battery_saver", 1 }, + { "bgtc!leaving_halfawake_margin", 3 }, + { "bgtc!battery_threshold_save", 20 }, + { "bgtc!battery_threshold_stop", 20 }, + { "bgtc!minimum_interval_normal", 1800 }, + { "bgtc!minimum_interval_save", 86400 }, + { "boot!force_maintenance", false }, + { "capsrv!screenshot_layerstack", "screenshot" }, + { "capsrv!enable_album_screenshot_filedata_verification", true }, + { "devmenu!enable_application_update", true }, + { "devmenu!enable_exhibition_mode", false }, + { "eclct!analytics_override", false }, + { "eclct!analytics_pollperiod", 86400 }, + { "err!applet_auto_close", false }, + { "friends!background_processing", true }, + { "htc!disconnection_emulation", false }, + { "idle!dim_level_percent_lcd", 10 }, + { "idle!dim_level_percent_tv", 70 }, + { "lbl!force_disable_als", false }, + { "lm!enable_sd_card_logging", false }, + { "lm!sd_card_log_output_directory", "NxBinLogs" }, + { "mii!is_db_test_mode_enabled", false }, + { "news!system_version", 2 }, + { "nfp!not_locked_tag", true }, + { "nfp!play_report", false }, + { "nifm!is_communication_control_enabled_for_test", false }, + { "nifm!connection_test_timeout", 45000 }, + { "nifm!apply_config_timeout", 30000 }, + { "nifm!ethernet_adapter_standby_time", 10000 }, + { "nim.install!prefer_delta_evenif_inefficient", false }, + { "nim.install!apply_delta_stress_storage", 0 }, + { "ns.notification!retry_interval", 60 }, + { "ns.notification!enable_network_update", true }, + { "ns.notification!enable_download_task_list", true }, + { "ns.notification!enable_version_list", true }, + { "ns.notification!enable_random_wait", true }, + { "ns.notification!debug_waiting_limit", 0 }, + { "ns.notification!enable_request_on_cold_boot", true }, + { "ns.sdcard!mount_sdcard", true }, + { "ns.sdcard!compare_sdcard", 0 }, + { "ns.gamecard!mount_gamecard_result_value", 0 }, + { "ns.gamecard!try_gamecard_access_result_value", 0 }, + { "nv!00008600", "" }, + { "nv!0007b25e", "" }, + { "nv!0083e1", "" }, + { "nv!01621887", "" }, + { "nv!03134743", "" }, + { "nv!0356afd0", "" }, + { "nv!0356afd1", "" }, + { "nv!0356afd2", "" }, + { "nv!0356afd3", "" }, + { "nv!094313", "" }, + { "nv!0x04dc09", "" }, + { "nv!0x111133", "" }, + { "nv!0x1aa483", "" }, + { "nv!0x1cb1cf", "" }, + { "nv!0x1cb1d0", "" }, + { "nv!0x1e3221", "" }, + { "nv!0x300fc8", "" }, + { "nv!0x301fc8", "" }, + { "nv!0x302fc8", "" }, + { "nv!0x3eec59", "" }, + { "nv!0x46b3ed", "" }, + { "nv!0x523dc0", "" }, + { "nv!0x523dc1", "" }, + { "nv!0x523dc2", "" }, + { "nv!0x523dc3", "" }, + { "nv!0x523dc4", "" }, + { "nv!0x523dc5", "" }, + { "nv!0x523dc6", "" }, + { "nv!0x523dd0", "" }, + { "nv!0x523dd1", "" }, + { "nv!0x523dd3", "" }, + { "nv!0x5344bb", "" }, + { "nv!0x555237", "" }, + { "nv!0x58a234", "" }, + { "nv!0x7b4428", "" }, + { "nv!0x923dc0", "" }, + { "nv!0x923dc1", "" }, + { "nv!0x923dc2", "" }, + { "nv!0x923dc3", "" }, + { "nv!0x923dc4", "" }, + { "nv!0x923dd3", "" }, + { "nv!0x9abdc5", "" }, + { "nv!0x9abdc6", "" }, + { "nv!0xaaa36c", "" }, + { "nv!0xb09da0", "" }, + { "nv!0xb09da1", "" }, + { "nv!0xb09da2", "" }, + { "nv!0xb09da3", "" }, + { "nv!0xb09da4", "" }, + { "nv!0xb09da5", "" }, + { "nv!0xb0b348", "" }, + { "nv!0xb0b349", "" }, + { "nv!0xbb558f", "" }, + { "nv!0xbd10fb", "" }, + { "nv!0xc32ad3", "" }, + { "nv!0xce2348", "" }, + { "nv!0xcfd81f", "" }, + { "nv!0xe0036b", "" }, + { "nv!0xe01f2d", "" }, + { "nv!0xe17212", "" }, + { "nv!0xeae966", "" }, + { "nv!0xed4f82", "" }, + { "nv!0xf12335", "" }, + { "nv!0xf12336", "" }, + { "nv!10261989", "" }, + { "nv!1042d483", "" }, + { "nv!10572898", "" }, + { "nv!115631", "" }, + { "nv!12950094", "" }, + { "nv!1314f311", "" }, + { "nv!1314f312", "" }, + { "nv!13279512", "" }, + { "nv!13813496", "" }, + { "nv!14507179", "" }, + { "nv!15694569", "" }, + { "nv!16936964", "" }, + { "nv!17aa230c", "" }, + { "nv!182054", "" }, + { "nv!18273275", "" }, + { "nv!18273276", "" }, + { "nv!1854d03b", "" }, + { "nv!18add00d", "" }, + { "nv!19156670", "" }, + { "nv!19286545", "" }, + { "nv!1a298e9f", "" }, + { "nv!1acf43fe", "" }, + { "nv!1bda43fe", "" }, + { "nv!1c3b92", "" }, + { "nv!21509920", "" }, + { "nv!215323457", "" }, + { "nv!2165ad", "" }, + { "nv!2165ae", "" }, + { "nv!21be9c", "" }, + { "nv!233264316", "" }, + { "nv!234557580", "" }, + { "nv!23cd0e", "" }, + { "nv!24189123", "" }, + { "nv!2443266", "" }, + { "nv!25025519", "" }, + { "nv!255e39", "" }, + { "nv!2583364", "" }, + { "nv!2888c1", "" }, + { "nv!28ca3e", "" }, + { "nv!29871243", "" }, + { "nv!2a1f64", "" }, + { "nv!2dc432", "" }, + { "nv!2de437", "" }, + { "nv!2f3bb89c", "" }, + { "nv!2fd652", "" }, + { "nv!3001ac", "" }, + { "nv!31298772", "" }, + { "nv!313233", "" }, + { "nv!31f7d603", "" }, + { "nv!320ce4", "" }, + { "nv!32153248", "" }, + { "nv!32153249", "" }, + { "nv!335bca", "" }, + { "nv!342abb", "" }, + { "nv!34dfe6", "" }, + { "nv!34dfe7", "" }, + { "nv!34dfe8", "" }, + { "nv!34dfe9", "" }, + { "nv!35201578", "" }, + { "nv!359278", "" }, + { "nv!37f53a", "" }, + { "nv!38144972", "" }, + { "nv!38542646", "" }, + { "nv!3b74c9", "" }, + { "nv!3c136f", "" }, + { "nv!3cf72823", "" }, + { "nv!3d7af029", "" }, + { "nv!3ff34782", "" }, + { "nv!4129618", "" }, + { "nv!4189fac3", "" }, + { "nv!420bd4", "" }, + { "nv!42a699", "" }, + { "nv!441369", "" }, + { "nv!4458713e", "" }, + { "nv!4554b6", "" }, + { "nv!457425", "" }, + { "nv!4603b207", "" }, + { "nv!46574957", "" }, + { "nv!46574958", "" }, + { "nv!46813529", "" }, + { "nv!46f1e13d", "" }, + { "nv!47534c43", "" }, + { "nv!48550336", "" }, + { "nv!48576893", "" }, + { "nv!48576894", "" }, + { "nv!4889ac02", "" }, + { "nv!49005740", "" }, + { "nv!49867584", "" }, + { "nv!49960973", "" }, + { "nv!4a5341", "" }, + { "nv!4f4e48", "" }, + { "nv!4f8a0a", "" }, + { "nv!50299698", "" }, + { "nv!50299699", "" }, + { "nv!50361291", "" }, + { "nv!5242ae", "" }, + { "nv!53d30c", "" }, + { "nv!56347a", "" }, + { "nv!563a95f1", "" }, + { "nv!573823", "" }, + { "nv!58027529", "" }, + { "nv!5d2d63", "" }, + { "nv!5f7e3b", "" }, + { "nv!60461793", "" }, + { "nv!60d355", "" }, + { "nv!616627aa", "" }, + { "nv!62317182", "" }, + { "nv!6253fa2e", "" }, + { "nv!64100768", "" }, + { "nv!64100769", "" }, + { "nv!64100770", "" }, + { "nv!647395", "" }, + { "nv!66543234", "" }, + { "nv!67674763", "" }, + { "nv!67739784", "" }, + { "nv!68fb9c", "" }, + { "nv!69801276", "" }, + { "nv!6af9fa2f", "" }, + { "nv!6af9fa3f", "" }, + { "nv!6af9fa4f", "" }, + { "nv!6bd8c7", "" }, + { "nv!6c7691", "" }, + { "nv!6d4296ce", "" }, + { "nv!6dd7e7", "" }, + { "nv!6dd7e8", "" }, + { "nv!6fe11ec1", "" }, + { "nv!716511763", "" }, + { "nv!72504593", "" }, + { "nv!73304097", "" }, + { "nv!73314098", "" }, + { "nv!74095213", "" }, + { "nv!74095213a", "" }, + { "nv!74095213b", "" }, + { "nv!74095214", "" }, + { "nv!748f9649", "" }, + { "nv!75494732", "" }, + { "nv!78452832", "" }, + { "nv!784561", "" }, + { "nv!78e16b9c", "" }, + { "nv!79251225", "" }, + { "nv!7c128b", "" }, + { "nv!7ccd93", "" }, + { "nv!7df8d1", "" }, + { "nv!800c2310", "" }, + { "nv!80546710", "" }, + { "nv!80772310", "" }, + { "nv!808ee280", "" }, + { "nv!81131154", "" }, + { "nv!81274457", "" }, + { "nv!8292291f", "" }, + { "nv!83498426", "" }, + { "nv!84993794", "" }, + { "nv!84995585", "" }, + { "nv!84a0a0", "" }, + { "nv!852142", "" }, + { "nv!85612309", "" }, + { "nv!85612310", "" }, + { "nv!85612311", "" }, + { "nv!85612312", "" }, + { "nv!8623ff27", "" }, + { "nv!87364952", "" }, + { "nv!87f6275666", "" }, + { "nv!886748", "" }, + { "nv!89894423", "" }, + { "nv!8ad8a75", "" }, + { "nv!8ad8ad00", "" }, + { "nv!8bb815", "" }, + { "nv!8bb817", "" }, + { "nv!8bb818", "" }, + { "nv!8bb819", "" }, + { "nv!8e640cd1", "" }, + { "nv!8f34971a", "" }, + { "nv!8f773984", "" }, + { "nv!8f7a7d", "" }, + { "nv!902486209", "" }, + { "nv!90482571", "" }, + { "nv!91214835", "" }, + { "nv!912848290", "" }, + { "nv!915e56", "" }, + { "nv!92179063", "" }, + { "nv!92179064", "" }, + { "nv!92179065", "" }, + { "nv!92179066", "" }, + { "nv!92350358", "" }, + { "nv!92809063", "" }, + { "nv!92809064", "" }, + { "nv!92809065", "" }, + { "nv!92809066", "" }, + { "nv!92920143", "" }, + { "nv!93a89b12", "" }, + { "nv!93a89c0b", "" }, + { "nv!94812574", "" }, + { "nv!95282304", "" }, + { "nv!95394027", "" }, + { "nv!959b1f", "" }, + { "nv!9638af", "" }, + { "nv!96fd59", "" }, + { "nv!97f6275666", "" }, + { "nv!97f6275667", "" }, + { "nv!97f6275668", "" }, + { "nv!97f6275669", "" }, + { "nv!97f627566a", "" }, + { "nv!97f627566b", "" }, + { "nv!97f627566d", "" }, + { "nv!97f627566e", "" }, + { "nv!97f627566f", "" }, + { "nv!97f6275670", "" }, + { "nv!97f6275671", "" }, + { "nv!97f727566e", "" }, + { "nv!98480775", "" }, + { "nv!98480776", "" }, + { "nv!98480777", "" }, + { "nv!992431", "" }, + { "nv!9aa29065", "" }, + { "nv!9af32c", "" }, + { "nv!9af32d", "" }, + { "nv!9af32e", "" }, + { "nv!9c108b71", "" }, + { "nv!9f279065", "" }, + { "nv!a01bc728", "" }, + { "nv!a13b46c80", "" }, + { "nv!a22eb0", "" }, + { "nv!a2fb451e", "" }, + { "nv!a3456abe", "" }, + { "nv!a7044887", "" }, + { "nv!a7149200", "" }, + { "nv!a766215670", "" }, + { "nv!aac_drc_boost", "" }, + { "nv!aac_drc_cut", "" }, + { "nv!aac_drc_enc_target_level", "" }, + { "nv!aac_drc_heavy", "" }, + { "nv!aac_drc_reference_level", "" }, + { "nv!aalinegamma", "" }, + { "nv!aalinetweaks", "" }, + { "nv!ab34ee01", "" }, + { "nv!ab34ee02", "" }, + { "nv!ab34ee03", "" }, + { "nv!ac0274", "" }, + { "nv!af73c63e", "" }, + { "nv!af73c63f", "" }, + { "nv!af9927", "" }, + { "nv!afoverride", "" }, + { "nv!allocdeviceevents", "" }, + { "nv!applicationkey", "" }, + { "nv!appreturnonlybasicglsltype", "" }, + { "nv!app_softimage", "" }, + { "nv!app_supportbits2", "" }, + { "nv!assumetextureismipmappedatcreation", "" }, + { "nv!b1fb0f01", "" }, + { "nv!b3edd5", "" }, + { "nv!b40d9e03d", "" }, + { "nv!b7f6275666", "" }, + { "nv!b812c1", "" }, + { "nv!ba14ba1a", "" }, + { "nv!ba14ba1b", "" }, + { "nv!bd7559", "" }, + { "nv!bd755a", "" }, + { "nv!bd755c", "" }, + { "nv!bd755d", "" }, + { "nv!be58bb", "" }, + { "nv!be92cb", "" }, + { "nv!beefcba3", "" }, + { "nv!beefcba4", "" }, + { "nv!c023777f", "" }, + { "nv!c09dc8", "" }, + { "nv!c0d340", "" }, + { "nv!c2ff374c", "" }, + { "nv!c5e9d7a3", "" }, + { "nv!c5e9d7a4", "" }, + { "nv!c5e9d7b4", "" }, + { "nv!c618f9", "" }, + { "nv!ca345840", "" }, + { "nv!cachedisable", "" }, + { "nv!cast.on", "" }, + { "nv!cde", "" }, + { "nv!channelpriorityoverride", "" }, + { "nv!cleardatastorevidmem", "" }, + { "nv!cmdbufmemoryspaceenables", "" }, + { "nv!cmdbufminwords", "" }, + { "nv!cmdbufsizewords", "" }, + { "nv!conformantblitframebufferscissor", "" }, + { "nv!conformantincompletetextures", "" }, + { "nv!copybuffermethod", "" }, + { "nv!cubemapaniso", "" }, + { "nv!cubemapfiltering", "" }, + { "nv!d0e9a4d7", "" }, + { "nv!d13733f12", "" }, + { "nv!d1b399", "" }, + { "nv!d2983c32", "" }, + { "nv!d2983c33", "" }, + { "nv!d2e71b", "" }, + { "nv!d377dc", "" }, + { "nv!d377dd", "" }, + { "nv!d489f4", "" }, + { "nv!d4bce1", "" }, + { "nv!d518cb", "" }, + { "nv!d518cd", "" }, + { "nv!d518ce", "" }, + { "nv!d518d0", "" }, + { "nv!d518d1", "" }, + { "nv!d518d2", "" }, + { "nv!d518d3", "" }, + { "nv!d518d4", "" }, + { "nv!d518d5", "" }, + { "nv!d59eda", "" }, + { "nv!d83cbd", "" }, + { "nv!d8e777", "" }, + { "nv!debug_level", "" }, + { "nv!debug_mask", "" }, + { "nv!debug_options", "" }, + { "nv!devshmpageableallocations", "" }, + { "nv!df1f9812", "" }, + { "nv!df783c", "" }, + { "nv!diagenable", "" }, + { "nv!disallowcemask", "" }, + { "nv!disallowz16", "" }, + { "nv!dlmemoryspaceenables", "" }, + { "nv!e0bfec", "" }, + { "nv!e433456d", "" }, + { "nv!e435563f", "" }, + { "nv!e4cd9c", "" }, + { "nv!e5c972", "" }, + { "nv!e639ef", "" }, + { "nv!e802af", "" }, + { "nv!eae964", "" }, + { "nv!earlytexturehwallocation", "" }, + { "nv!eb92a3", "" }, + { "nv!ebca56", "" }, + { "nv!enable-noaud", "" }, + { "nv!enable-noavs", "" }, + { "nv!enable-prof", "" }, + { "nv!enable-sxesmode", "" }, + { "nv!enable-ulld", "" }, + { "nv!expert_detail_level", "" }, + { "nv!expert_output_mask", "" }, + { "nv!expert_report_mask", "" }, + { "nv!extensionstringnvarch", "" }, + { "nv!extensionstringversion", "" }, + { "nv!f00f1938", "" }, + { "nv!f10736", "" }, + { "nv!f1846870", "" }, + { "nv!f33bc370", "" }, + { "nv!f392a874", "" }, + { "nv!f49ae8", "" }, + { "nv!fa345cce", "" }, + { "nv!fa35cc4", "" }, + { "nv!faa14a", "" }, + { "nv!faf8a723", "" }, + { "nv!fastgs", "" }, + { "nv!fbf4ac45", "" }, + { "nv!fbo_blit_ignore_srgb", "" }, + { "nv!fc64c7", "" }, + { "nv!ff54ec97", "" }, + { "nv!ff54ec98", "" }, + { "nv!forceexitprocessdetach", "" }, + { "nv!forcerequestedesversion", "" }, + { "nv!__gl_", "" }, + { "nv!__gl_00008600", "" }, + { "nv!__gl_0007b25e", "" }, + { "nv!__gl_0083e1", "" }, + { "nv!__gl_01621887", "" }, + { "nv!__gl_03134743", "" }, + { "nv!__gl_0356afd0", "" }, + { "nv!__gl_0356afd1", "" }, + { "nv!__gl_0356afd2", "" }, + { "nv!__gl_0356afd3", "" }, + { "nv!__gl_094313", "" }, + { "nv!__gl_0x04dc09", "" }, + { "nv!__gl_0x111133", "" }, + { "nv!__gl_0x1aa483", "" }, + { "nv!__gl_0x1cb1cf", "" }, + { "nv!__gl_0x1cb1d0", "" }, + { "nv!__gl_0x1e3221", "" }, + { "nv!__gl_0x300fc8", "" }, + { "nv!__gl_0x301fc8", "" }, + { "nv!__gl_0x302fc8", "" }, + { "nv!__gl_0x3eec59", "" }, + { "nv!__gl_0x46b3ed", "" }, + { "nv!__gl_0x523dc0", "" }, + { "nv!__gl_0x523dc1", "" }, + { "nv!__gl_0x523dc2", "" }, + { "nv!__gl_0x523dc3", "" }, + { "nv!__gl_0x523dc4", "" }, + { "nv!__gl_0x523dc5", "" }, + { "nv!__gl_0x523dc6", "" }, + { "nv!__gl_0x523dd0", "" }, + { "nv!__gl_0x523dd1", "" }, + { "nv!__gl_0x523dd3", "" }, + { "nv!__gl_0x5344bb", "" }, + { "nv!__gl_0x555237", "" }, + { "nv!__gl_0x58a234", "" }, + { "nv!__gl_0x7b4428", "" }, + { "nv!__gl_0x923dc0", "" }, + { "nv!__gl_0x923dc1", "" }, + { "nv!__gl_0x923dc2", "" }, + { "nv!__gl_0x923dc3", "" }, + { "nv!__gl_0x923dc4", "" }, + { "nv!__gl_0x923dd3", "" }, + { "nv!__gl_0x9abdc5", "" }, + { "nv!__gl_0x9abdc6", "" }, + { "nv!__gl_0xaaa36c", "" }, + { "nv!__gl_0xb09da0", "" }, + { "nv!__gl_0xb09da1", "" }, + { "nv!__gl_0xb09da2", "" }, + { "nv!__gl_0xb09da3", "" }, + { "nv!__gl_0xb09da4", "" }, + { "nv!__gl_0xb09da5", "" }, + { "nv!__gl_0xb0b348", "" }, + { "nv!__gl_0xb0b349", "" }, + { "nv!__gl_0xbb558f", "" }, + { "nv!__gl_0xbd10fb", "" }, + { "nv!__gl_0xc32ad3", "" }, + { "nv!__gl_0xce2348", "" }, + { "nv!__gl_0xcfd81f", "" }, + { "nv!__gl_0xe0036b", "" }, + { "nv!__gl_0xe01f2d", "" }, + { "nv!__gl_0xe17212", "" }, + { "nv!__gl_0xeae966", "" }, + { "nv!__gl_0xed4f82", "" }, + { "nv!__gl_0xf12335", "" }, + { "nv!__gl_0xf12336", "" }, + { "nv!__gl_10261989", "" }, + { "nv!__gl_1042d483", "" }, + { "nv!__gl_10572898", "" }, + { "nv!__gl_115631", "" }, + { "nv!__gl_12950094", "" }, + { "nv!__gl_1314f311", "" }, + { "nv!__gl_1314f312", "" }, + { "nv!__gl_13279512", "" }, + { "nv!__gl_13813496", "" }, + { "nv!__gl_14507179", "" }, + { "nv!__gl_15694569", "" }, + { "nv!__gl_16936964", "" }, + { "nv!__gl_17aa230c", "" }, + { "nv!__gl_182054", "" }, + { "nv!__gl_18273275", "" }, + { "nv!__gl_18273276", "" }, + { "nv!__gl_1854d03b", "" }, + { "nv!__gl_18add00d", "" }, + { "nv!__gl_19156670", "" }, + { "nv!__gl_19286545", "" }, + { "nv!__gl_1a298e9f", "" }, + { "nv!__gl_1acf43fe", "" }, + { "nv!__gl_1bda43fe", "" }, + { "nv!__gl_1c3b92", "" }, + { "nv!__gl_21509920", "" }, + { "nv!__gl_215323457", "" }, + { "nv!__gl_2165ad", "" }, + { "nv!__gl_2165ae", "" }, + { "nv!__gl_21be9c", "" }, + { "nv!__gl_233264316", "" }, + { "nv!__gl_234557580", "" }, + { "nv!__gl_23cd0e", "" }, + { "nv!__gl_24189123", "" }, + { "nv!__gl_2443266", "" }, + { "nv!__gl_25025519", "" }, + { "nv!__gl_255e39", "" }, + { "nv!__gl_2583364", "" }, + { "nv!__gl_2888c1", "" }, + { "nv!__gl_28ca3e", "" }, + { "nv!__gl_29871243", "" }, + { "nv!__gl_2a1f64", "" }, + { "nv!__gl_2dc432", "" }, + { "nv!__gl_2de437", "" }, + { "nv!__gl_2f3bb89c", "" }, + { "nv!__gl_2fd652", "" }, + { "nv!__gl_3001ac", "" }, + { "nv!__gl_31298772", "" }, + { "nv!__gl_313233", "" }, + { "nv!__gl_31f7d603", "" }, + { "nv!__gl_320ce4", "" }, + { "nv!__gl_32153248", "" }, + { "nv!__gl_32153249", "" }, + { "nv!__gl_335bca", "" }, + { "nv!__gl_342abb", "" }, + { "nv!__gl_34dfe6", "" }, + { "nv!__gl_34dfe7", "" }, + { "nv!__gl_34dfe8", "" }, + { "nv!__gl_34dfe9", "" }, + { "nv!__gl_35201578", "" }, + { "nv!__gl_359278", "" }, + { "nv!__gl_37f53a", "" }, + { "nv!__gl_38144972", "" }, + { "nv!__gl_38542646", "" }, + { "nv!__gl_3b74c9", "" }, + { "nv!__gl_3c136f", "" }, + { "nv!__gl_3cf72823", "" }, + { "nv!__gl_3d7af029", "" }, + { "nv!__gl_3ff34782", "" }, + { "nv!__gl_4129618", "" }, + { "nv!__gl_4189fac3", "" }, + { "nv!__gl_420bd4", "" }, + { "nv!__gl_42a699", "" }, + { "nv!__gl_441369", "" }, + { "nv!__gl_4458713e", "" }, + { "nv!__gl_4554b6", "" }, + { "nv!__gl_457425", "" }, + { "nv!__gl_4603b207", "" }, + { "nv!__gl_46574957", "" }, + { "nv!__gl_46574958", "" }, + { "nv!__gl_46813529", "" }, + { "nv!__gl_46f1e13d", "" }, + { "nv!__gl_47534c43", "" }, + { "nv!__gl_48550336", "" }, + { "nv!__gl_48576893", "" }, + { "nv!__gl_48576894", "" }, + { "nv!__gl_4889ac02", "" }, + { "nv!__gl_49005740", "" }, + { "nv!__gl_49867584", "" }, + { "nv!__gl_49960973", "" }, + { "nv!__gl_4a5341", "" }, + { "nv!__gl_4f4e48", "" }, + { "nv!__gl_4f8a0a", "" }, + { "nv!__gl_50299698", "" }, + { "nv!__gl_50299699", "" }, + { "nv!__gl_50361291", "" }, + { "nv!__gl_5242ae", "" }, + { "nv!__gl_53d30c", "" }, + { "nv!__gl_56347a", "" }, + { "nv!__gl_563a95f1", "" }, + { "nv!__gl_573823", "" }, + { "nv!__gl_58027529", "" }, + { "nv!__gl_5d2d63", "" }, + { "nv!__gl_5f7e3b", "" }, + { "nv!__gl_60461793", "" }, + { "nv!__gl_60d355", "" }, + { "nv!__gl_616627aa", "" }, + { "nv!__gl_62317182", "" }, + { "nv!__gl_6253fa2e", "" }, + { "nv!__gl_64100768", "" }, + { "nv!__gl_64100769", "" }, + { "nv!__gl_64100770", "" }, + { "nv!__gl_647395", "" }, + { "nv!__gl_66543234", "" }, + { "nv!__gl_67674763", "" }, + { "nv!__gl_67739784", "" }, + { "nv!__gl_68fb9c", "" }, + { "nv!__gl_69801276", "" }, + { "nv!__gl_6af9fa2f", "" }, + { "nv!__gl_6af9fa3f", "" }, + { "nv!__gl_6af9fa4f", "" }, + { "nv!__gl_6bd8c7", "" }, + { "nv!__gl_6c7691", "" }, + { "nv!__gl_6d4296ce", "" }, + { "nv!__gl_6dd7e7", "" }, + { "nv!__gl_6dd7e8", "" }, + { "nv!__gl_6fe11ec1", "" }, + { "nv!__gl_716511763", "" }, + { "nv!__gl_72504593", "" }, + { "nv!__gl_73304097", "" }, + { "nv!__gl_73314098", "" }, + { "nv!__gl_74095213", "" }, + { "nv!__gl_74095213a", "" }, + { "nv!__gl_74095213b", "" }, + { "nv!__gl_74095214", "" }, + { "nv!__gl_748f9649", "" }, + { "nv!__gl_75494732", "" }, + { "nv!__gl_78452832", "" }, + { "nv!__gl_784561", "" }, + { "nv!__gl_78e16b9c", "" }, + { "nv!__gl_79251225", "" }, + { "nv!__gl_7c128b", "" }, + { "nv!__gl_7ccd93", "" }, + { "nv!__gl_7df8d1", "" }, + { "nv!__gl_800c2310", "" }, + { "nv!__gl_80546710", "" }, + { "nv!__gl_80772310", "" }, + { "nv!__gl_808ee280", "" }, + { "nv!__gl_81131154", "" }, + { "nv!__gl_81274457", "" }, + { "nv!__gl_8292291f", "" }, + { "nv!__gl_83498426", "" }, + { "nv!__gl_84993794", "" }, + { "nv!__gl_84995585", "" }, + { "nv!__gl_84a0a0", "" }, + { "nv!__gl_852142", "" }, + { "nv!__gl_85612309", "" }, + { "nv!__gl_85612310", "" }, + { "nv!__gl_85612311", "" }, + { "nv!__gl_85612312", "" }, + { "nv!__gl_8623ff27", "" }, + { "nv!__gl_87364952", "" }, + { "nv!__gl_87f6275666", "" }, + { "nv!__gl_886748", "" }, + { "nv!__gl_89894423", "" }, + { "nv!__gl_8ad8a75", "" }, + { "nv!__gl_8ad8ad00", "" }, + { "nv!__gl_8bb815", "" }, + { "nv!__gl_8bb817", "" }, + { "nv!__gl_8bb818", "" }, + { "nv!__gl_8bb819", "" }, + { "nv!__gl_8e640cd1", "" }, + { "nv!__gl_8f34971a", "" }, + { "nv!__gl_8f773984", "" }, + { "nv!__gl_8f7a7d", "" }, + { "nv!__gl_902486209", "" }, + { "nv!__gl_90482571", "" }, + { "nv!__gl_91214835", "" }, + { "nv!__gl_912848290", "" }, + { "nv!__gl_915e56", "" }, + { "nv!__gl_92179063", "" }, + { "nv!__gl_92179064", "" }, + { "nv!__gl_92179065", "" }, + { "nv!__gl_92179066", "" }, + { "nv!__gl_92350358", "" }, + { "nv!__gl_92809063", "" }, + { "nv!__gl_92809064", "" }, + { "nv!__gl_92809065", "" }, + { "nv!__gl_92809066", "" }, + { "nv!__gl_92920143", "" }, + { "nv!__gl_93a89b12", "" }, + { "nv!__gl_93a89c0b", "" }, + { "nv!__gl_94812574", "" }, + { "nv!__gl_95282304", "" }, + { "nv!__gl_95394027", "" }, + { "nv!__gl_959b1f", "" }, + { "nv!__gl_9638af", "" }, + { "nv!__gl_96fd59", "" }, + { "nv!__gl_97f6275666", "" }, + { "nv!__gl_97f6275667", "" }, + { "nv!__gl_97f6275668", "" }, + { "nv!__gl_97f6275669", "" }, + { "nv!__gl_97f627566a", "" }, + { "nv!__gl_97f627566b", "" }, + { "nv!__gl_97f627566d", "" }, + { "nv!__gl_97f627566e", "" }, + { "nv!__gl_97f627566f", "" }, + { "nv!__gl_97f6275670", "" }, + { "nv!__gl_97f6275671", "" }, + { "nv!__gl_97f727566e", "" }, + { "nv!__gl_98480775", "" }, + { "nv!__gl_98480776", "" }, + { "nv!__gl_98480777", "" }, + { "nv!__gl_992431", "" }, + { "nv!__gl_9aa29065", "" }, + { "nv!__gl_9af32c", "" }, + { "nv!__gl_9af32d", "" }, + { "nv!__gl_9af32e", "" }, + { "nv!__gl_9c108b71", "" }, + { "nv!__gl_9f279065", "" }, + { "nv!__gl_a01bc728", "" }, + { "nv!__gl_a13b46c80", "" }, + { "nv!__gl_a22eb0", "" }, + { "nv!__gl_a2fb451e", "" }, + { "nv!__gl_a3456abe", "" }, + { "nv!__gl_a7044887", "" }, + { "nv!__gl_a7149200", "" }, + { "nv!__gl_a766215670", "" }, + { "nv!__gl_aalinegamma", "" }, + { "nv!__gl_aalinetweaks", "" }, + { "nv!__gl_ab34ee01", "" }, + { "nv!__gl_ab34ee02", "" }, + { "nv!__gl_ab34ee03", "" }, + { "nv!__gl_ac0274", "" }, + { "nv!__gl_af73c63e", "" }, + { "nv!__gl_af73c63f", "" }, + { "nv!__gl_af9927", "" }, + { "nv!__gl_afoverride", "" }, + { "nv!__gl_allocdeviceevents", "" }, + { "nv!__gl_applicationkey", "" }, + { "nv!__gl_appreturnonlybasicglsltype", "" }, + { "nv!__gl_app_softimage", "" }, + { "nv!__gl_app_supportbits2", "" }, + { "nv!__gl_assumetextureismipmappedatcreation", "" }, + { "nv!__gl_b1fb0f01", "" }, + { "nv!__gl_b3edd5", "" }, + { "nv!__gl_b40d9e03d", "" }, + { "nv!__gl_b7f6275666", "" }, + { "nv!__gl_b812c1", "" }, + { "nv!__gl_ba14ba1a", "" }, + { "nv!__gl_ba14ba1b", "" }, + { "nv!__gl_bd7559", "" }, + { "nv!__gl_bd755a", "" }, + { "nv!__gl_bd755c", "" }, + { "nv!__gl_bd755d", "" }, + { "nv!__gl_be58bb", "" }, + { "nv!__gl_be92cb", "" }, + { "nv!__gl_beefcba3", "" }, + { "nv!__gl_beefcba4", "" }, + { "nv!__gl_c023777f", "" }, + { "nv!__gl_c09dc8", "" }, + { "nv!__gl_c0d340", "" }, + { "nv!__gl_c2ff374c", "" }, + { "nv!__gl_c5e9d7a3", "" }, + { "nv!__gl_c5e9d7a4", "" }, + { "nv!__gl_c5e9d7b4", "" }, + { "nv!__gl_c618f9", "" }, + { "nv!__gl_ca345840", "" }, + { "nv!__gl_cachedisable", "" }, + { "nv!__gl_channelpriorityoverride", "" }, + { "nv!__gl_cleardatastorevidmem", "" }, + { "nv!__gl_cmdbufmemoryspaceenables", "" }, + { "nv!__gl_cmdbufminwords", "" }, + { "nv!__gl_cmdbufsizewords", "" }, + { "nv!__gl_conformantblitframebufferscissor", "" }, + { "nv!__gl_conformantincompletetextures", "" }, + { "nv!__gl_copybuffermethod", "" }, + { "nv!__gl_cubemapaniso", "" }, + { "nv!__gl_cubemapfiltering", "" }, + { "nv!__gl_d0e9a4d7", "" }, + { "nv!__gl_d13733f12", "" }, + { "nv!__gl_d1b399", "" }, + { "nv!__gl_d2983c32", "" }, + { "nv!__gl_d2983c33", "" }, + { "nv!__gl_d2e71b", "" }, + { "nv!__gl_d377dc", "" }, + { "nv!__gl_d377dd", "" }, + { "nv!__gl_d489f4", "" }, + { "nv!__gl_d4bce1", "" }, + { "nv!__gl_d518cb", "" }, + { "nv!__gl_d518cd", "" }, + { "nv!__gl_d518ce", "" }, + { "nv!__gl_d518d0", "" }, + { "nv!__gl_d518d1", "" }, + { "nv!__gl_d518d2", "" }, + { "nv!__gl_d518d3", "" }, + { "nv!__gl_d518d4", "" }, + { "nv!__gl_d518d5", "" }, + { "nv!__gl_d59eda", "" }, + { "nv!__gl_d83cbd", "" }, + { "nv!__gl_d8e777", "" }, + { "nv!__gl_debug_level", "" }, + { "nv!__gl_debug_mask", "" }, + { "nv!__gl_debug_options", "" }, + { "nv!__gl_devshmpageableallocations", "" }, + { "nv!__gl_df1f9812", "" }, + { "nv!__gl_df783c", "" }, + { "nv!__gl_diagenable", "" }, + { "nv!__gl_disallowcemask", "" }, + { "nv!__gl_disallowz16", "" }, + { "nv!__gl_dlmemoryspaceenables", "" }, + { "nv!__gl_e0bfec", "" }, + { "nv!__gl_e433456d", "" }, + { "nv!__gl_e435563f", "" }, + { "nv!__gl_e4cd9c", "" }, + { "nv!__gl_e5c972", "" }, + { "nv!__gl_e639ef", "" }, + { "nv!__gl_e802af", "" }, + { "nv!__gl_eae964", "" }, + { "nv!__gl_earlytexturehwallocation", "" }, + { "nv!__gl_eb92a3", "" }, + { "nv!__gl_ebca56", "" }, + { "nv!__gl_expert_detail_level", "" }, + { "nv!__gl_expert_output_mask", "" }, + { "nv!__gl_expert_report_mask", "" }, + { "nv!__gl_extensionstringnvarch", "" }, + { "nv!__gl_extensionstringversion", "" }, + { "nv!__gl_f00f1938", "" }, + { "nv!__gl_f10736", "" }, + { "nv!__gl_f1846870", "" }, + { "nv!__gl_f33bc370", "" }, + { "nv!__gl_f392a874", "" }, + { "nv!__gl_f49ae8", "" }, + { "nv!__gl_fa345cce", "" }, + { "nv!__gl_fa35cc4", "" }, + { "nv!__gl_faa14a", "" }, + { "nv!__gl_faf8a723", "" }, + { "nv!__gl_fastgs", "" }, + { "nv!__gl_fbf4ac45", "" }, + { "nv!__gl_fbo_blit_ignore_srgb", "" }, + { "nv!__gl_fc64c7", "" }, + { "nv!__gl_ff54ec97", "" }, + { "nv!__gl_ff54ec98", "" }, + { "nv!__gl_forceexitprocessdetach", "" }, + { "nv!__gl_forcerequestedesversion", "" }, + { "nv!__gl_glsynctovblank", "" }, + { "nv!__gl_gvitimeoutcontrol", "" }, + { "nv!__gl_hcctrl", "" }, + { "nv!__gl_hwstate_per_ctx", "" }, + { "nv!__gl_machinecachelimit", "" }, + { "nv!__gl_maxframesallowed", "" }, + { "nv!__gl_memmgrcachedalloclimit", "" }, + { "nv!__gl_memmgrcachedalloclimitratio", "" }, + { "nv!__gl_memmgrsysheapalloclimit", "" }, + { "nv!__gl_memmgrsysheapalloclimitratio", "" }, + { "nv!__gl_memmgrvidheapalloclimit", "" }, + { "nv!__gl_mosaic_clip_to_subdev", "" }, + { "nv!__gl_mosaic_clip_to_subdev_h_overlap", "" }, + { "nv!__gl_mosaic_clip_to_subdev_v_overlap", "" }, + { "nv!__gl_overlaymergeblittimerms", "" }, + { "nv!__gl_perfmon_mode", "" }, + { "nv!__gl_pixbar_mode", "" }, + { "nv!__gl_qualityenhancements", "" }, + { "nv!__gl_r27s18q28", "" }, + { "nv!__gl_r2d7c1d8", "" }, + { "nv!__gl_renderer", "" }, + { "nv!__gl_renderqualityflags", "" }, + { "nv!__gl_s3tcquality", "" }, + { "nv!__gl_shaderatomics", "" }, + { "nv!__gl_shadercacheinitsize", "" }, + { "nv!__gl_shader_disk_cache_path", "" }, + { "nv!__gl_shader_disk_cache_read_only", "" }, + { "nv!__gl_shaderobjects", "" }, + { "nv!__gl_shaderportabilitywarnings", "" }, + { "nv!__gl_shaderwarningsaserrors", "" }, + { "nv!__gl_skiptexturehostcopies", "" }, + { "nv!__glslc_debug_level", "" }, + { "nv!__glslc_debug_mask", "" }, + { "nv!__glslc_debug_options", "" }, + { "nv!__glslc_debug_filename", "" }, + { "nv!__gl_sli_dli_control", "" }, + { "nv!__gl_sparsetexture", "" }, + { "nv!__gl_spinlooptimeout", "" }, + { "nv!__gl_sync_to_vblank", "" }, + { "nv!glsynctovblank", "" }, + { "nv!__gl_sysheapreuseratio", "" }, + { "nv!__gl_sysmemtexturepromotion", "" }, + { "nv!__gl_targetflushcount", "" }, + { "nv!__gl_tearingfreeswappresent", "" }, + { "nv!__gl_texclampbehavior", "" }, + { "nv!__gl_texlodbias", "" }, + { "nv!__gl_texmemoryspaceenables", "" }, + { "nv!__gl_textureprecache", "" }, + { "nv!__gl_threadcontrol", "" }, + { "nv!__gl_threadcontrol2", "" }, + { "nv!__gl_usegvievents", "" }, + { "nv!__gl_vbomemoryspaceenables", "" }, + { "nv!__gl_vertexlimit", "" }, + { "nv!__gl_vidheapreuseratio", "" }, + { "nv!__gl_vpipe", "" }, + { "nv!__gl_vpipeformatbloatlimit", "" }, + { "nv!__gl_wglmessageboxonabort", "" }, + { "nv!__gl_writeinfolog", "" }, + { "nv!__gl_writeprogramobjectassembly", "" }, + { "nv!__gl_writeprogramobjectsource", "" }, + { "nv!__gl_xnvadapterpresent", "" }, + { "nv!__gl_yield", "" }, + { "nv!__gl_yieldfunction", "" }, + { "nv!__gl_yieldfunctionfast", "" }, + { "nv!__gl_yieldfunctionslow", "" }, + { "nv!__gl_yieldfunctionwaitfordcqueue", "" }, + { "nv!__gl_yieldfunctionwaitforframe", "" }, + { "nv!__gl_yieldfunctionwaitforgpu", "" }, + { "nv!__gl_zbctableaddhysteresis", "" }, + { "nv!gpu_debug_mode", "" }, + { "nv!gpu_stay_on", "" }, + { "nv!gpu_timeout_ms_max", "" }, + { "nv!gvitimeoutcontrol", "" }, + { "nv!hcctrl", "" }, + { "nv!hwstate_per_ctx", "" }, + { "nv!libandroid_enable_log", "" }, + { "nv!machinecachelimit", "" }, + { "nv!maxframesallowed", "" }, + { "nv!media.aac_51_output_enabled", "" }, + { "nv!memmgrcachedalloclimit", "" }, + { "nv!memmgrcachedalloclimitratio", "" }, + { "nv!memmgrsysheapalloclimit", "" }, + { "nv!memmgrsysheapalloclimitratio", "" }, + { "nv!memmgrvidheapalloclimit", "" }, + { "nv!mosaic_clip_to_subdev", "" }, + { "nv!mosaic_clip_to_subdev_h_overlap", "" }, + { "nv!mosaic_clip_to_subdev_v_overlap", "" }, + { "nv!nvblit.dump", "" }, + { "nv!nvblit.profile", "" }, + { "nv!nvblit.twod", "" }, + { "nv!nvblit.vic", "" }, + { "nv!nvddk_vic_prevent_use", "" }, + { "nv!nv_decompression", "" }, + { "nv!nvdisp_bl_ctrl", "0" }, + { "nv!nvdisp_debug_mask", "" }, + { "nv!nvdisp_enable_ts", "0" }, + { "nv!nvhdcp_timeout_ms", "12000" }, + { "nv!nvhdcp_max_retries", "5" }, + { "nv!nv_emc_dvfs_test", "" }, + { "nv!nv_emc_init_rate_hz", "" }, + { "nv!nv_gmmu_va_page_split", "" }, + { "nv!nv_gmmu_va_range", "" }, + { "nv!nvhost_debug_mask", "" }, + { "nv!nvidia.hwc.dump_config", "" }, + { "nv!nvidia.hwc.dump_layerlist", "" }, + { "nv!nvidia.hwc.dump_windows", "" }, + { "nv!nvidia.hwc.enable_disp_trans", "" }, + { "nv!nvidia.hwc.ftrace_enable", "" }, + { "nv!nvidia.hwc.hdcp_enable", "" }, + { "nv!nvidia.hwc.hidden_window_mask0", "" }, + { "nv!nvidia.hwc.hidden_window_mask1", "" }, + { "nv!nvidia.hwc.immediate_modeset", "" }, + { "nv!nvidia.hwc.imp_enable", "" }, + { "nv!nvidia.hwc.no_egl", "" }, + { "nv!nvidia.hwc.no_scratchblit", "" }, + { "nv!nvidia.hwc.no_vic", "" }, + { "nv!nvidia.hwc.null_display", "" }, + { "nv!nvidia.hwc.scan_props", "" }, + { "nv!nvidia.hwc.swap_interval", "" }, + { "nv!nvidia.hwc.war_1515812", "0" }, + { "nv!nvmap_debug_mask", "" }, + { "nv!nv_memory_profiler", "" }, + { "nv!nvnflinger_enable_log", "" }, + { "nv!nvnflinger_flip_policy", "" }, + { "nv!nvnflinger_hotplug_autoswitch", "0" }, + { "nv!nvnflinger_prefer_primary_layer", "0" }, + { "nv!nvnflinger_service_priority", "" }, + { "nv!nvnflinger_service_threads", "" }, + { "nv!nvnflinger_swap_interval", "" }, + { "nv!nvnflinger_track_perf", "" }, + { "nv!nvnflinger_virtualdisplay_policy", "60hz" }, + { "nv!nvn_no_vsync_capability", false }, + { "nv!nvn_through_opengl", "" }, + { "nv!nv_pllcx_always_on", "" }, + { "nv!nv_pllcx_safe_div", "" }, + { "nv!nvrm_gpu_channel_interleave", "" }, + { "nv!nvrm_gpu_channel_priority", "" }, + { "nv!nvrm_gpu_channel_timeslice", "" }, + { "nv!nvrm_gpu_default_device_index", "" }, + { "nv!nvrm_gpu_dummy", "" }, + { "nv!nvrm_gpu_help", "" }, + { "nv!nvrm_gpu_nvgpu_disable", "" }, + { "nv!nvrm_gpu_nvgpu_do_nfa_partial_map", "" }, + { "nv!nvrm_gpu_nvgpu_ecc_overrides", "" }, + { "nv!nvrm_gpu_nvgpu_no_as_get_va_regions", "" }, + { "nv!nvrm_gpu_nvgpu_no_channel_abort", "" }, + { "nv!nvrm_gpu_nvgpu_no_cyclestats", "" }, + { "nv!nvrm_gpu_nvgpu_no_fixed", "" }, + { "nv!nvrm_gpu_nvgpu_no_gpu_characteristics", "" }, + { "nv!nvrm_gpu_nvgpu_no_ioctl_mutex", "" }, + { "nv!nvrm_gpu_nvgpu_no_map_buffer_ex", "" }, + { "nv!nvrm_gpu_nvgpu_no_robustness", "" }, + { "nv!nvrm_gpu_nvgpu_no_sparse", "" }, + { "nv!nvrm_gpu_nvgpu_no_syncpoints", "" }, + { "nv!nvrm_gpu_nvgpu_no_tsg", "" }, + { "nv!nvrm_gpu_nvgpu_no_zbc", "" }, + { "nv!nvrm_gpu_nvgpu_no_zcull", "" }, + { "nv!nvrm_gpu_nvgpu_wrap_channels_in_tsgs", "" }, + { "nv!nvrm_gpu_prevent_use", "" }, + { "nv!nvrm_gpu_trace", "" }, + { "nv!nvsched_debug_mask", "" }, + { "nv!nvsched_force_enable", "" }, + { "nv!nvsched_force_log", "" }, + { "nv!nv_usb_plls_hw_ctrl", "" }, + { "nv!nv_winsys", "" }, + { "nv!nvwsi_dump", "" }, + { "nv!nvwsi_fill", "" }, + { "nv!ogl_", "" }, + { "nv!ogl_0356afd0", "" }, + { "nv!ogl_0356afd1", "" }, + { "nv!ogl_0356afd2", "" }, + { "nv!ogl_0356afd3", "" }, + { "nv!ogl_0x923dc0", "" }, + { "nv!ogl_0x923dc1", "" }, + { "nv!ogl_0x923dc2", "" }, + { "nv!ogl_0x923dc3", "" }, + { "nv!ogl_0x923dc4", "" }, + { "nv!ogl_0x923dd3", "" }, + { "nv!ogl_0x9abdc5", "" }, + { "nv!ogl_0x9abdc6", "" }, + { "nv!ogl_0xbd10fb", "" }, + { "nv!ogl_0xce2348", "" }, + { "nv!ogl_10261989", "" }, + { "nv!ogl_1042d483", "" }, + { "nv!ogl_10572898", "" }, + { "nv!ogl_115631", "" }, + { "nv!ogl_12950094", "" }, + { "nv!ogl_1314f311", "" }, + { "nv!ogl_1314f312", "" }, + { "nv!ogl_13279512", "" }, + { "nv!ogl_13813496", "" }, + { "nv!ogl_14507179", "" }, + { "nv!ogl_15694569", "" }, + { "nv!ogl_16936964", "" }, + { "nv!ogl_17aa230c", "" }, + { "nv!ogl_182054", "" }, + { "nv!ogl_18273275", "" }, + { "nv!ogl_18273276", "" }, + { "nv!ogl_1854d03b", "" }, + { "nv!ogl_18add00d", "" }, + { "nv!ogl_19156670", "" }, + { "nv!ogl_19286545", "" }, + { "nv!ogl_1a298e9f", "" }, + { "nv!ogl_1acf43fe", "" }, + { "nv!ogl_1bda43fe", "" }, + { "nv!ogl_1c3b92", "" }, + { "nv!ogl_21509920", "" }, + { "nv!ogl_215323457", "" }, + { "nv!ogl_2165ad", "" }, + { "nv!ogl_2165ae", "" }, + { "nv!ogl_21be9c", "" }, + { "nv!ogl_233264316", "" }, + { "nv!ogl_234557580", "" }, + { "nv!ogl_23cd0e", "" }, + { "nv!ogl_24189123", "" }, + { "nv!ogl_2443266", "" }, + { "nv!ogl_25025519", "" }, + { "nv!ogl_255e39", "" }, + { "nv!ogl_2583364", "" }, + { "nv!ogl_2888c1", "" }, + { "nv!ogl_28ca3e", "" }, + { "nv!ogl_29871243", "" }, + { "nv!ogl_2a1f64", "" }, + { "nv!ogl_2dc432", "" }, + { "nv!ogl_2de437", "" }, + { "nv!ogl_2f3bb89c", "" }, + { "nv!ogl_2fd652", "" }, + { "nv!ogl_3001ac", "" }, + { "nv!ogl_31298772", "" }, + { "nv!ogl_313233", "" }, + { "nv!ogl_31f7d603", "" }, + { "nv!ogl_320ce4", "" }, + { "nv!ogl_32153248", "" }, + { "nv!ogl_32153249", "" }, + { "nv!ogl_335bca", "" }, + { "nv!ogl_342abb", "" }, + { "nv!ogl_34dfe6", "" }, + { "nv!ogl_34dfe7", "" }, + { "nv!ogl_34dfe8", "" }, + { "nv!ogl_34dfe9", "" }, + { "nv!ogl_35201578", "" }, + { "nv!ogl_359278", "" }, + { "nv!ogl_37f53a", "" }, + { "nv!ogl_38144972", "" }, + { "nv!ogl_38542646", "" }, + { "nv!ogl_3b74c9", "" }, + { "nv!ogl_3c136f", "" }, + { "nv!ogl_3cf72823", "" }, + { "nv!ogl_3d7af029", "" }, + { "nv!ogl_3ff34782", "" }, + { "nv!ogl_4129618", "" }, + { "nv!ogl_4189fac3", "" }, + { "nv!ogl_420bd4", "" }, + { "nv!ogl_42a699", "" }, + { "nv!ogl_441369", "" }, + { "nv!ogl_4458713e", "" }, + { "nv!ogl_4554b6", "" }, + { "nv!ogl_457425", "" }, + { "nv!ogl_4603b207", "" }, + { "nv!ogl_46574957", "" }, + { "nv!ogl_46574958", "" }, + { "nv!ogl_46813529", "" }, + { "nv!ogl_46f1e13d", "" }, + { "nv!ogl_47534c43", "" }, + { "nv!ogl_48550336", "" }, + { "nv!ogl_48576893", "" }, + { "nv!ogl_48576894", "" }, + { "nv!ogl_4889ac02", "" }, + { "nv!ogl_49005740", "" }, + { "nv!ogl_49867584", "" }, + { "nv!ogl_49960973", "" }, + { "nv!ogl_4a5341", "" }, + { "nv!ogl_4f4e48", "" }, + { "nv!ogl_4f8a0a", "" }, + { "nv!ogl_50299698", "" }, + { "nv!ogl_50299699", "" }, + { "nv!ogl_50361291", "" }, + { "nv!ogl_5242ae", "" }, + { "nv!ogl_53d30c", "" }, + { "nv!ogl_56347a", "" }, + { "nv!ogl_563a95f1", "" }, + { "nv!ogl_573823", "" }, + { "nv!ogl_58027529", "" }, + { "nv!ogl_5d2d63", "" }, + { "nv!ogl_5f7e3b", "" }, + { "nv!ogl_60461793", "" }, + { "nv!ogl_60d355", "" }, + { "nv!ogl_616627aa", "" }, + { "nv!ogl_62317182", "" }, + { "nv!ogl_6253fa2e", "" }, + { "nv!ogl_64100768", "" }, + { "nv!ogl_64100769", "" }, + { "nv!ogl_64100770", "" }, + { "nv!ogl_647395", "" }, + { "nv!ogl_66543234", "" }, + { "nv!ogl_67674763", "" }, + { "nv!ogl_67739784", "" }, + { "nv!ogl_68fb9c", "" }, + { "nv!ogl_69801276", "" }, + { "nv!ogl_6af9fa2f", "" }, + { "nv!ogl_6af9fa3f", "" }, + { "nv!ogl_6af9fa4f", "" }, + { "nv!ogl_6bd8c7", "" }, + { "nv!ogl_6c7691", "" }, + { "nv!ogl_6d4296ce", "" }, + { "nv!ogl_6dd7e7", "" }, + { "nv!ogl_6dd7e8", "" }, + { "nv!ogl_6fe11ec1", "" }, + { "nv!ogl_716511763", "" }, + { "nv!ogl_72504593", "" }, + { "nv!ogl_73304097", "" }, + { "nv!ogl_73314098", "" }, + { "nv!ogl_74095213", "" }, + { "nv!ogl_74095213a", "" }, + { "nv!ogl_74095213b", "" }, + { "nv!ogl_74095214", "" }, + { "nv!ogl_748f9649", "" }, + { "nv!ogl_75494732", "" }, + { "nv!ogl_78452832", "" }, + { "nv!ogl_784561", "" }, + { "nv!ogl_78e16b9c", "" }, + { "nv!ogl_79251225", "" }, + { "nv!ogl_7c128b", "" }, + { "nv!ogl_7ccd93", "" }, + { "nv!ogl_7df8d1", "" }, + { "nv!ogl_800c2310", "" }, + { "nv!ogl_80546710", "" }, + { "nv!ogl_80772310", "" }, + { "nv!ogl_808ee280", "" }, + { "nv!ogl_81131154", "" }, + { "nv!ogl_81274457", "" }, + { "nv!ogl_8292291f", "" }, + { "nv!ogl_83498426", "" }, + { "nv!ogl_84993794", "" }, + { "nv!ogl_84995585", "" }, + { "nv!ogl_84a0a0", "" }, + { "nv!ogl_852142", "" }, + { "nv!ogl_85612309", "" }, + { "nv!ogl_85612310", "" }, + { "nv!ogl_85612311", "" }, + { "nv!ogl_85612312", "" }, + { "nv!ogl_8623ff27", "" }, + { "nv!ogl_87364952", "" }, + { "nv!ogl_87f6275666", "" }, + { "nv!ogl_886748", "" }, + { "nv!ogl_89894423", "" }, + { "nv!ogl_8ad8a75", "" }, + { "nv!ogl_8ad8ad00", "" }, + { "nv!ogl_8bb815", "" }, + { "nv!ogl_8bb817", "" }, + { "nv!ogl_8bb818", "" }, + { "nv!ogl_8bb819", "" }, + { "nv!ogl_8e640cd1", "" }, + { "nv!ogl_8f34971a", "" }, + { "nv!ogl_8f773984", "" }, + { "nv!ogl_8f7a7d", "" }, + { "nv!ogl_902486209", "" }, + { "nv!ogl_90482571", "" }, + { "nv!ogl_91214835", "" }, + { "nv!ogl_912848290", "" }, + { "nv!ogl_915e56", "" }, + { "nv!ogl_92179063", "" }, + { "nv!ogl_92179064", "" }, + { "nv!ogl_92179065", "" }, + { "nv!ogl_92179066", "" }, + { "nv!ogl_92350358", "" }, + { "nv!ogl_92809063", "" }, + { "nv!ogl_92809064", "" }, + { "nv!ogl_92809065", "" }, + { "nv!ogl_92809066", "" }, + { "nv!ogl_92920143", "" }, + { "nv!ogl_93a89b12", "" }, + { "nv!ogl_93a89c0b", "" }, + { "nv!ogl_94812574", "" }, + { "nv!ogl_95282304", "" }, + { "nv!ogl_95394027", "" }, + { "nv!ogl_959b1f", "" }, + { "nv!ogl_9638af", "" }, + { "nv!ogl_96fd59", "" }, + { "nv!ogl_97f6275666", "" }, + { "nv!ogl_97f6275667", "" }, + { "nv!ogl_97f6275668", "" }, + { "nv!ogl_97f6275669", "" }, + { "nv!ogl_97f627566a", "" }, + { "nv!ogl_97f627566b", "" }, + { "nv!ogl_97f627566d", "" }, + { "nv!ogl_97f627566e", "" }, + { "nv!ogl_97f627566f", "" }, + { "nv!ogl_97f6275670", "" }, + { "nv!ogl_97f6275671", "" }, + { "nv!ogl_97f727566e", "" }, + { "nv!ogl_98480775", "" }, + { "nv!ogl_98480776", "" }, + { "nv!ogl_98480777", "" }, + { "nv!ogl_992431", "" }, + { "nv!ogl_9aa29065", "" }, + { "nv!ogl_9af32c", "" }, + { "nv!ogl_9af32d", "" }, + { "nv!ogl_9af32e", "" }, + { "nv!ogl_9c108b71", "" }, + { "nv!ogl_9f279065", "" }, + { "nv!ogl_a01bc728", "" }, + { "nv!ogl_a13b46c80", "" }, + { "nv!ogl_a22eb0", "" }, + { "nv!ogl_a2fb451e", "" }, + { "nv!ogl_a3456abe", "" }, + { "nv!ogl_a7044887", "" }, + { "nv!ogl_a7149200", "" }, + { "nv!ogl_a766215670", "" }, + { "nv!ogl_aalinegamma", "" }, + { "nv!ogl_aalinetweaks", "" }, + { "nv!ogl_ab34ee01", "" }, + { "nv!ogl_ab34ee02", "" }, + { "nv!ogl_ab34ee03", "" }, + { "nv!ogl_ac0274", "" }, + { "nv!ogl_af73c63e", "" }, + { "nv!ogl_af73c63f", "" }, + { "nv!ogl_af9927", "" }, + { "nv!ogl_afoverride", "" }, + { "nv!ogl_allocdeviceevents", "" }, + { "nv!ogl_applicationkey", "" }, + { "nv!ogl_appreturnonlybasicglsltype", "" }, + { "nv!ogl_app_softimage", "" }, + { "nv!ogl_app_supportbits2", "" }, + { "nv!ogl_assumetextureismipmappedatcreation", "" }, + { "nv!ogl_b1fb0f01", "" }, + { "nv!ogl_b3edd5", "" }, + { "nv!ogl_b40d9e03d", "" }, + { "nv!ogl_b7f6275666", "" }, + { "nv!ogl_b812c1", "" }, + { "nv!ogl_ba14ba1a", "" }, + { "nv!ogl_ba14ba1b", "" }, + { "nv!ogl_bd7559", "" }, + { "nv!ogl_bd755a", "" }, + { "nv!ogl_bd755c", "" }, + { "nv!ogl_bd755d", "" }, + { "nv!ogl_be58bb", "" }, + { "nv!ogl_be92cb", "" }, + { "nv!ogl_beefcba3", "" }, + { "nv!ogl_beefcba4", "" }, + { "nv!ogl_c023777f", "" }, + { "nv!ogl_c09dc8", "" }, + { "nv!ogl_c0d340", "" }, + { "nv!ogl_c2ff374c", "" }, + { "nv!ogl_c5e9d7a3", "" }, + { "nv!ogl_c5e9d7a4", "" }, + { "nv!ogl_c5e9d7b4", "" }, + { "nv!ogl_c618f9", "" }, + { "nv!ogl_ca345840", "" }, + { "nv!ogl_cachedisable", "" }, + { "nv!ogl_channelpriorityoverride", "" }, + { "nv!ogl_cleardatastorevidmem", "" }, + { "nv!ogl_cmdbufmemoryspaceenables", "" }, + { "nv!ogl_cmdbufminwords", "" }, + { "nv!ogl_cmdbufsizewords", "" }, + { "nv!ogl_conformantblitframebufferscissor", "" }, + { "nv!ogl_conformantincompletetextures", "" }, + { "nv!ogl_copybuffermethod", "" }, + { "nv!ogl_cubemapaniso", "" }, + { "nv!ogl_cubemapfiltering", "" }, + { "nv!ogl_d0e9a4d7", "" }, + { "nv!ogl_d13733f12", "" }, + { "nv!ogl_d1b399", "" }, + { "nv!ogl_d2983c32", "" }, + { "nv!ogl_d2983c33", "" }, + { "nv!ogl_d2e71b", "" }, + { "nv!ogl_d377dc", "" }, + { "nv!ogl_d377dd", "" }, + { "nv!ogl_d489f4", "" }, + { "nv!ogl_d4bce1", "" }, + { "nv!ogl_d518cb", "" }, + { "nv!ogl_d518cd", "" }, + { "nv!ogl_d518ce", "" }, + { "nv!ogl_d518d0", "" }, + { "nv!ogl_d518d1", "" }, + { "nv!ogl_d518d2", "" }, + { "nv!ogl_d518d3", "" }, + { "nv!ogl_d518d4", "" }, + { "nv!ogl_d518d5", "" }, + { "nv!ogl_d59eda", "" }, + { "nv!ogl_d83cbd", "" }, + { "nv!ogl_d8e777", "" }, + { "nv!ogl_debug_level", "" }, + { "nv!ogl_debug_mask", "" }, + { "nv!ogl_debug_options", "" }, + { "nv!ogl_devshmpageableallocations", "" }, + { "nv!ogl_df1f9812", "" }, + { "nv!ogl_df783c", "" }, + { "nv!ogl_diagenable", "" }, + { "nv!ogl_disallowcemask", "" }, + { "nv!ogl_disallowz16", "" }, + { "nv!ogl_dlmemoryspaceenables", "" }, + { "nv!ogl_e0bfec", "" }, + { "nv!ogl_e433456d", "" }, + { "nv!ogl_e435563f", "" }, + { "nv!ogl_e4cd9c", "" }, + { "nv!ogl_e5c972", "" }, + { "nv!ogl_e639ef", "" }, + { "nv!ogl_e802af", "" }, + { "nv!ogl_eae964", "" }, + { "nv!ogl_earlytexturehwallocation", "" }, + { "nv!ogl_eb92a3", "" }, + { "nv!ogl_ebca56", "" }, + { "nv!ogl_expert_detail_level", "" }, + { "nv!ogl_expert_output_mask", "" }, + { "nv!ogl_expert_report_mask", "" }, + { "nv!ogl_extensionstringnvarch", "" }, + { "nv!ogl_extensionstringversion", "" }, + { "nv!ogl_f00f1938", "" }, + { "nv!ogl_f10736", "" }, + { "nv!ogl_f1846870", "" }, + { "nv!ogl_f33bc370", "" }, + { "nv!ogl_f392a874", "" }, + { "nv!ogl_f49ae8", "" }, + { "nv!ogl_fa345cce", "" }, + { "nv!ogl_fa35cc4", "" }, + { "nv!ogl_faa14a", "" }, + { "nv!ogl_faf8a723", "" }, + { "nv!ogl_fastgs", "" }, + { "nv!ogl_fbf4ac45", "" }, + { "nv!ogl_fbo_blit_ignore_srgb", "" }, + { "nv!ogl_fc64c7", "" }, + { "nv!ogl_ff54ec97", "" }, + { "nv!ogl_ff54ec98", "" }, + { "nv!ogl_forceexitprocessdetach", "" }, + { "nv!ogl_forcerequestedesversion", "" }, + { "nv!ogl_glsynctovblank", "" }, + { "nv!ogl_gvitimeoutcontrol", "" }, + { "nv!ogl_hcctrl", "" }, + { "nv!ogl_hwstate_per_ctx", "" }, + { "nv!ogl_machinecachelimit", "" }, + { "nv!ogl_maxframesallowed", "" }, + { "nv!ogl_memmgrcachedalloclimit", "" }, + { "nv!ogl_memmgrcachedalloclimitratio", "" }, + { "nv!ogl_memmgrsysheapalloclimit", "" }, + { "nv!ogl_memmgrsysheapalloclimitratio", "" }, + { "nv!ogl_memmgrvidheapalloclimit", "" }, + { "nv!ogl_mosaic_clip_to_subdev", "" }, + { "nv!ogl_mosaic_clip_to_subdev_h_overlap", "" }, + { "nv!ogl_mosaic_clip_to_subdev_v_overlap", "" }, + { "nv!ogl_overlaymergeblittimerms", "" }, + { "nv!ogl_perfmon_mode", "" }, + { "nv!ogl_pixbar_mode", "" }, + { "nv!ogl_qualityenhancements", "" }, + { "nv!ogl_r27s18q28", "" }, + { "nv!ogl_r2d7c1d8", "" }, + { "nv!ogl_renderer", "" }, + { "nv!ogl_renderqualityflags", "" }, + { "nv!ogl_s3tcquality", "" }, + { "nv!ogl_shaderatomics", "" }, + { "nv!ogl_shadercacheinitsize", "" }, + { "nv!ogl_shader_disk_cache_path", "" }, + { "nv!ogl_shader_disk_cache_read_only", "" }, + { "nv!ogl_shaderobjects", "" }, + { "nv!ogl_shaderportabilitywarnings", "" }, + { "nv!ogl_shaderwarningsaserrors", "" }, + { "nv!ogl_skiptexturehostcopies", "" }, + { "nv!ogl_sli_dli_control", "" }, + { "nv!ogl_sparsetexture", "" }, + { "nv!ogl_spinlooptimeout", "" }, + { "nv!ogl_sync_to_vblank", "" }, + { "nv!ogl_sysheapreuseratio", "" }, + { "nv!ogl_sysmemtexturepromotion", "" }, + { "nv!ogl_targetflushcount", "" }, + { "nv!ogl_tearingfreeswappresent", "" }, + { "nv!ogl_texclampbehavior", "" }, + { "nv!ogl_texlodbias", "" }, + { "nv!ogl_texmemoryspaceenables", "" }, + { "nv!ogl_textureprecache", "" }, + { "nv!ogl_threadcontrol", "" }, + { "nv!ogl_threadcontrol2", "" }, + { "nv!ogl_usegvievents", "" }, + { "nv!ogl_vbomemoryspaceenables", "" }, + { "nv!ogl_vertexlimit", "" }, + { "nv!ogl_vidheapreuseratio", "" }, + { "nv!ogl_vpipe", "" }, + { "nv!ogl_vpipeformatbloatlimit", "" }, + { "nv!ogl_wglmessageboxonabort", "" }, + { "nv!ogl_writeinfolog", "" }, + { "nv!ogl_writeprogramobjectassembly", "" }, + { "nv!ogl_writeprogramobjectsource", "" }, + { "nv!ogl_xnvadapterpresent", "" }, + { "nv!ogl_yield", "" }, + { "nv!ogl_yieldfunction", "" }, + { "nv!ogl_yieldfunctionfast", "" }, + { "nv!ogl_yieldfunctionslow", "" }, + { "nv!ogl_yieldfunctionwaitfordcqueue", "" }, + { "nv!ogl_yieldfunctionwaitforframe", "" }, + { "nv!ogl_yieldfunctionwaitforgpu", "" }, + { "nv!ogl_zbctableaddhysteresis", "" }, + { "nv!overlaymergeblittimerms", "" }, + { "nv!perfmon_mode", "" }, + { "nv!persist.sys.display.resolution", "" }, + { "nv!persist.tegra.composite.fallb", "" }, + { "nv!persist.tegra.composite.policy", "" }, + { "nv!persist.tegra.composite.range", "" }, + { "nv!persist.tegra.compositor", "" }, + { "nv!persist.tegra.compositor.virt", "" }, + { "nv!persist.tegra.compression", "" }, + { "nv!persist.tegra.cursor.enable", "" }, + { "nv!persist.tegra.didim.enable", "" }, + { "nv!persist.tegra.didim.normal", "" }, + { "nv!persist.tegra.didim.video", "" }, + { "nv!persist.tegra.disp.heads", "" }, + { "nv!persist.tegra.gamma_correction", "" }, + { "nv!persist.tegra.gpu_mapping_cache", "" }, + { "nv!persist.tegra.grlayout", "" }, + { "nv!persist.tegra.hdmi.2020.10", "" }, + { "nv!persist.tegra.hdmi.2020.fake", "" }, + { "nv!persist.tegra.hdmi.2020.force", "" }, + { "nv!persist.tegra.hdmi.autorotate", "" }, + { "nv!persist.tegra.hdmi.hdr.fake", "" }, + { "nv!persist.tegra.hdmi.ignore_ratio", "" }, + { "nv!persist.tegra.hdmi.limit.clock", "" }, + { "nv!persist.tegra.hdmi.only_16_9", "" }, + { "nv!persist.tegra.hdmi.range", "" }, + { "nv!persist.tegra.hdmi.resolution", "" }, + { "nv!persist.tegra.hdmi.underscan", "" }, + { "nv!persist.tegra.hdmi.yuv.422", "" }, + { "nv!persist.tegra.hdmi.yuv.444", "" }, + { "nv!persist.tegra.hdmi.yuv.enable", "" }, + { "nv!persist.tegra.hdmi.yuv.force", "" }, + { "nv!persist.tegra.hwc.nvdc", "" }, + { "nv!persist.tegra.idle.minimum_fps", "" }, + { "nv!persist.tegra.panel.rotation", "" }, + { "nv!persist.tegra.scan_props", "" }, + { "nv!persist.tegra.stb.mode", "" }, + { "nv!persist.tegra.zbc_override", "" }, + { "nv!pixbar_mode", "" }, + { "nv!qualityenhancements", "" }, + { "nv!r27s18q28", "" }, + { "nv!r2d7c1d8", "" }, + { "nv!renderer", "" }, + { "nv!renderqualityflags", "" }, + { "nv!rmos_debug_mask", "" }, + { "nv!rmos_set_production_mode", "" }, + { "nv!s3tcquality", "" }, + { "nv!shaderatomics", "" }, + { "nv!shadercacheinitsize", "" }, + { "nv!shader_disk_cache_path", "" }, + { "nv!shader_disk_cache_read_only", "" }, + { "nv!shaderobjects", "" }, + { "nv!shaderportabilitywarnings", "" }, + { "nv!shaderwarningsaserrors", "" }, + { "nv!skiptexturehostcopies", "" }, + { "nv!sli_dli_control", "" }, + { "nv!sparsetexture", "" }, + { "nv!spinlooptimeout", "" }, + { "nv!sync_to_vblank", "" }, + { "nv!sysheapreuseratio", "" }, + { "nv!sysmemtexturepromotion", "" }, + { "nv!targetflushcount", "" }, + { "nv!tearingfreeswappresent", "" }, + { "nv!tegra.refresh", "" }, + { "nv!texclampbehavior", "" }, + { "nv!texlodbias", "" }, + { "nv!texmemoryspaceenables", "" }, + { "nv!textureprecache", "" }, + { "nv!threadcontrol", "" }, + { "nv!threadcontrol2", "" }, + { "nv!tvmr.avp.logs", "" }, + { "nv!tvmr.buffer.logs", "" }, + { "nv!tvmr.dec.prof", "" }, + { "nv!tvmr.deint.logs", "" }, + { "nv!tvmr.dfs.logs", "" }, + { "nv!tvmr.ffprof.logs", "" }, + { "nv!tvmr.game.stream", "" }, + { "nv!tvmr.general.logs", "" }, + { "nv!tvmr.input.dump", "" }, + { "nv!tvmr.seeking.logs", "" }, + { "nv!tvmr.ts_pulldown", "" }, + { "nv!usegvievents", "" }, + { "nv!vbomemoryspaceenables", "" }, + { "nv!vcc_debug_ip", "" }, + { "nv!vcc_verbose_level", "" }, + { "nv!vertexlimit", "" }, + { "nv!viccomposer.filter", "" }, + { "nv!videostats-enable", "" }, + { "nv!vidheapreuseratio", "" }, + { "nv!vpipe", "" }, + { "nv!vpipeformatbloatlimit", "" }, + { "nv!wglmessageboxonabort", "" }, + { "nv!writeinfolog", "" }, + { "nv!writeprogramobjectassembly", "" }, + { "nv!writeprogramobjectsource", "" }, + { "nv!xnvadapterpresent", "" }, + { "nv!yield", "" }, + { "nv!yieldfunction", "" }, + { "nv!yieldfunctionfast", "" }, + { "nv!yieldfunctionslow", "" }, + { "nv!yieldfunctionwaitfordcqueue", "" }, + { "nv!yieldfunctionwaitforframe", "" }, + { "nv!yieldfunctionwaitforgpu", "" }, + { "nv!zbctableaddhysteresis", "" }, + { "pcm!enable", true }, + { "pctl!intermittent_task_interval_seconds", 21600 }, + { "prepo!devmenu_prepo_page_view", false }, + { "prepo!background_processing", true }, + { "prepo!transmission_interval_min", 10 }, + { "prepo!transmission_retry_interval", 3600 }, + { "psm!evaluation_log_enabled", false }, + { "snap_shot_dump!auto_dump", false }, + { "snap_shot_dump!output_dir", "%USERPROFILE%/Documents/Nintendo/NXDMP" }, + { "snap_shot_dump!full_dump", false }, + { "systemconfig!field_testing", false }, + { "systemconfig!exhivision", false }, + { "systempowerstate!always_reboot", false }, + { "systempowerstate!power_state_message_emulation_trigger_time", 0 }, + { "systempowerstate!power_state_message_to_emulate", 0 }, + { "target_manager!device_name", "" }, + { "vulnerability!needs_update_vulnerability_policy", 0 }, + { "apm!performance_mode_policy", "auto" }, + { "apm!sdev_throttling_enabled", true }, + { "apm!sdev_throttling_additional_delay_us", 16000 }, + { "apm!battery_draining_enabled", false }, + { "apm!sdev_cpu_overclock_enabled", false }, + { "bcat!production_mode", true }, + { "bpc!enable_quasi_off", true }, + { "bsp0!usb", "UDS" }, + { "bsp0!tm_transport", "USB" }, + { "bluetooth_debug!skip_boot", false }, + { "contents_delivery!enable_debug_api", false }, + { "eupld!upload_enabled", true }, + { "fatal!transition_to_fatal", true }, + { "fatal!show_extra_info", false }, + { "gpu_core_dump!auto_dump", false }, + { "hid_debug!enables_debugpad", false }, + { "hid_debug!manages_devices", true }, + { "hid_debug!emulate_future_device", false }, + { "hid_debug!emulate_firmware_update_failure", false }, + { "hid_debug!emulate_mcu_hardware_error", false }, + { "hid_debug!firmware_update_failure_emulation_mode", 0 }, + { "jit_debug!enable_jit_debug", false }, + { "npns!background_processing", true }, + { "npns!logmanager_redirection", true }, + { "npns!sleep_processing_timeout", 30 }, + { "npns!sleep_periodic_interval", 10800 }, + { "npns!sleep_max_try_count", 5 }, + { "npns!test_mode", false }, + { "ns.applet!overlay_applet_id", "0x010000000000100c" }, + { "ns.applet!system_applet_id", "0x0100000000001000" }, + { "ns.applet!shop_applet_id", "0x010000000000100b" }, + { "ns.autoboot!enabled", true }, + { "ns.pseudodeviceid!reset_pseudo_device_id", false }, + { "nsd!environment_identifier", "lp1" }, + { "nsd!test_mode", false }, + { "ntc!is_autonomic_correction_enabled", true }, + { "ntc!autonomic_correction_interval_seconds", 432000 }, + { "ntc!autonomic_correction_failed_retry_interval_seconds", 1800 }, + { "ntc!autonomic_correction_immediate_try_count_max", 4 }, + { "ntc!autonomic_correction_immediate_try_interval_milliseconds", 5000 }, + { "nv!nv_graphics_firmware_memory_margin", false }, + { "omm!operation_mode_policy", "auto" }, + { "omm!sleep_fade_in_ms", 50 }, + { "omm!sleep_fade_out_ms", 100 }, + { "omm!charging_sign_ms", 3000 }, + { "omm!low_battery_sign_ms", 3000 }, + { "omm!sign_fade_in_ms", 0 }, + { "omm!sign_fade_out_ms", 400 }, + { "omm!sign_wait_layer_visible_ms", 100 }, + { "omm!startup_fade_in_ms", 200 }, + { "omm!startup_fade_out_ms", 400 }, + { "omm!backlight_off_ms_on_handheld_switch", 150 }, + { "omm!sleep_on_ac_ok_boot", true }, + { "pdm!save_playlog", true }, + { "productinfo!product_name", "Nintendo Switch" }, + { "productinfo!cec_osd_name", "NintendoSwitch" }, + { "ro!ease_nro_restriction", false }, + { "settings_debug!is_debug_mode_enabled", false }, + { "systemreport!enabled", true }, + { "systemsleep!enter_sleep", true }, + { "systemsleep!enter_sc7", true }, + { "systemsleep!keep_vdd_core", true }, + { "systemsleep!disable_tma_sleep", false }, + { "systemsleep!disable_auto_sleep", false }, + { "systemsleep!override_auto_sleep_time", 0 }, + { "systemsleep!sleep_pending_time_ms", 15000 }, + { "systemsleep!hush_time_after_brief_power_button_press_ms", 1000 }, + { "systemsleep!transition_timeout_sec", 60 }, + { "systemsleep!dummy_event_auto_wake", false }, + { "systemupdate!debug_id", "0x0000000000000000" }, + { "systemupdate!debug_version", 0 }, + { "systemupdate!bgnup_retry_seconds", 60 }, + { "systemupdate!enable_background_download_stress_testing", false }, + { "systemupdate!debug_id_for_content_delivery", "0x0000000000000000" }, + { "systemupdate!debug_version_for_content_delivery", 0 }, + { "systemupdate!assumed_system_applet_version", 0 }, + { "tc!iir_filter_gain_soc", 100 }, + { "tc!iir_filter_gain_pcb", 100 }, + { "tc!tskin_soc_coefficients_handheld", "[5464, 174190]" }, + { "tc!tskin_soc_coefficients_console", "[6182, 112480]" }, + { "tc!tskin_pcb_coefficients_handheld", "[5464, 174190]" }, + { "tc!tskin_pcb_coefficients_console", "[6182, 112480]" }, + { "tc!tskin_select", "both" }, + { "tc!tskin_rate_table_handheld", "[[-1000000, 40000, 0, 0], [36000, 43000, 51, 51], [43000, 48000, 51, 102], [48000, 53000, 102, 153], [53000, 1000000, 153, 153], [48000, 1000000, 153, 153]]" }, + { "tc!tskin_rate_table_console", "[[-1000000, 43000, 51, 51], [43000, 53000, 51, 153], [53000, 58000, 153, 255], [58000, 1000000, 255, 255]]" }, + { "tc!rate_select", "both" }, + { "tc!log_enabled", false }, + { "tc!sleep_enabled", true }, + { "time!standard_steady_clock_test_offset_minutes", 0 }, + { "time!standard_steady_clock_rtc_update_interval_minutes", 5 }, + { "time!standard_network_clock_sufficient_accuracy_minutes", 43200 }, + { "time!standard_user_clock_initial_year", 2019 }, + { "usb!usb30_force_enabled", false }, + { "wlan_debug!skip_wlan_boot", false } + }; + } } \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index 81ef53fe7..307ca9e40 100644 --- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -31,16 +31,16 @@ - - - Always - THIRDPARTY.md - - - Always - LICENSE.txt - - + + + Always + THIRDPARTY.md + + + Always + LICENSE.txt + + From 8734ea9dd4e67c971a078f4f10c16e40476ece52 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 9 Jan 2023 22:58:51 +0100 Subject: [PATCH 258/737] Linux: Add Avalonia detection to Ryujinx.sh (#4224) * Revert "ava: Fix regression caused by #4013 (#4222)" This reverts commit b9f2a96595b2721836d2b73e005640f9c3bfaf23. * linux: Detect Ryujinx.Ava and don't rename the Ryujinx.Ava assembly --- .github/workflows/build.yml | 6 ------ .github/workflows/release.yml | 4 +--- distribution/linux/Ryujinx.sh | 7 ++++++- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4586902ce..ac19f717a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,12 +69,6 @@ jobs: - name: Publish Ryujinx.Ava run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true if: github.event_name == 'pull_request' - - name: Rename Avalonia (Windows) - run: mv ./publish_ava/Ryujinx.Ava.exe ./publish_ava/Ryujinx.exe - if: runner.os == 'Windows' && github.event_name == 'pull_request' - - name: Rename Avalonia (Unix) - run: mv ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx - if: runner.os != 'Windows' && github.event_name == 'pull_request' - name: Upload Ryujinx artifact uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29f0c5cf9..3a4b4e205 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,13 +46,11 @@ jobs: shell: bash - name: Create output dir run: "mkdir release_output" - - name: Publish Windows run: | dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true - mv ./publish_windows_ava/publish/Ryujinx.Ava.exe ./publish_windows_ava/publish/Ryujinx.exe - name: Packing Windows builds run: | pushd publish_windows @@ -73,7 +71,7 @@ jobs: dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true - mv ./publish_linux_ava/publish/Ryujinx.Ava ./publish_linux_ava/publish/Ryujinx + - name: Packing Linux builds run: | pushd publish_linux diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh index 57a75b1d1..09a4188cd 100644 --- a/distribution/linux/Ryujinx.sh +++ b/distribution/linux/Ryujinx.sh @@ -1,5 +1,10 @@ #!/bin/sh SCRIPT_DIR=$(dirname $(realpath $0)) +RYUJINX_BIN="Ryujinx" -env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/Ryujinx" "$@" +if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then + RYUJINX_BIN="Ryujinx.Ava" +fi + +env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/$RYUJINX_BIN" "$@" From 8c720783f50051b28188a330f87d3b74bbe26cc7 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 10 Jan 2023 00:45:46 +0100 Subject: [PATCH 259/737] linux: Fix packaging step for CI & Add Ryujinx.Headless.SDL2 to Ryujinx.sh (#4249) --- .github/workflows/release.yml | 11 +++++++---- Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj | 6 ++++++ distribution/linux/Ryujinx.sh | 4 ++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a4b4e205..9258ff488 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,22 +75,25 @@ jobs: - name: Packing Linux builds run: | pushd publish_linux - tar --exclude "publish/Ryujinx" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish + tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx" + python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh" gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar popd pushd publish_linux_sdl2_headless - tar --exclude "publish/Ryujinx.Headless.SDL2" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish + tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2" + python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh" gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar popd pushd publish_linux_ava - tar --exclude "publish/Ryujinx" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish - python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx" + tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish + python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava" + python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh" gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar popd diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index 307ca9e40..ebb9c94c3 100644 --- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -42,6 +42,12 @@ + + + Always + + + diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh index 09a4188cd..ccb8d65e1 100644 --- a/distribution/linux/Ryujinx.sh +++ b/distribution/linux/Ryujinx.sh @@ -7,4 +7,8 @@ if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then RYUJINX_BIN="Ryujinx.Ava" fi +if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then + RYUJINX_BIN="Ryujinx.Headless.SDL2" +fi + env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/$RYUJINX_BIN" "$@" From e4413542b2199894453c8ba6dc67a86ee5add7bd Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 10 Jan 2023 00:59:23 +0100 Subject: [PATCH 260/737] Add command line arguments to override docked mode (#4239) * Add command line args for docked mode * Apply suggestions from code review Co-authored-by: Ac_K Co-authored-by: Ac_K --- Ryujinx.Ava/Program.cs | 8 +++++++- Ryujinx.Ui.Common/Helper/CommandLineState.cs | 7 +++++++ Ryujinx/Program.cs | 8 +++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 1dc1a7bce..010aff514 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -6,8 +6,8 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; using Ryujinx.Common.SystemInfo; +using Ryujinx.Common.SystemInterop; using Ryujinx.Modules; using Ryujinx.SDL2.Common; using Ryujinx.Ui.Common; @@ -227,6 +227,12 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; } } + + // Check if docked mode was overriden. + if (CommandLineState.OverrideDockedMode.HasValue) + { + ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; + } } private static void PrintSystemInfo() diff --git a/Ryujinx.Ui.Common/Helper/CommandLineState.cs b/Ryujinx.Ui.Common/Helper/CommandLineState.cs index cda4af4ed..8ca7fba18 100644 --- a/Ryujinx.Ui.Common/Helper/CommandLineState.cs +++ b/Ryujinx.Ui.Common/Helper/CommandLineState.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Ui.Common.Helper { public static string[] Arguments { get; private set; } + public static bool? OverrideDockedMode { get; private set; } public static string OverrideGraphicsBackend { get; private set; } public static string BaseDirPathArg { get; private set; } public static string Profile { get; private set; } @@ -69,6 +70,12 @@ namespace Ryujinx.Ui.Common.Helper OverrideGraphicsBackend = args[++i]; break; + case "--docked-mode": + OverrideDockedMode = true; + break; + case "--handheld-mode": + OverrideDockedMode = false; + break; default: LaunchPathArg = arg; break; diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index b1fedaade..56352a4ce 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -3,8 +3,8 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; using Ryujinx.Common.SystemInfo; +using Ryujinx.Common.SystemInterop; using Ryujinx.Modules; using Ryujinx.SDL2.Common; using Ryujinx.Ui; @@ -271,6 +271,12 @@ namespace Ryujinx } } + // Check if docked mode was overriden. + if (CommandLineState.OverrideDockedMode.HasValue) + { + ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; + } + // Logging system information. PrintSystemInfo(); From 3e455a90a1137bad4abad5d5a4ca15476bb088e8 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:22:25 +0100 Subject: [PATCH 261/737] Ava: Add missing null check to ContentDialogHelper.ShowAsync() (#4248) * ava: Add missing null check to ContentDialogHelper.ShowAsync() * Replace "is not" with != operator Co-authored-by: gdkchan Co-authored-by: gdkchan --- Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index f4334b5f6..8f0c670ea 100644 --- a/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -349,7 +349,7 @@ namespace Ryujinx.Ava.UI.Helpers Window parent = GetMainWindow(); - if (parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning) + if (parent != null && parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning) { contentDialogOverlayWindow = new() { From a16854e55a2e81b9a3765656e2010bf7e3a79f24 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Tue, 10 Jan 2023 18:45:55 +0100 Subject: [PATCH 262/737] ava: Cleanup AppHost (#4240) * ava: Cleanup AppHost This PR cleaned up the AppHost file a bit (adding the infamous extra spaces to improve readability), resorting private vars, remove useless vars, and improve the code here and there, like the AudioBackend check. Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com> * Remove 'renderer" * Revert currentTime * revert if condition Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com> --- .../CompatLayerHardwareDeviceDriver.cs | 2 + .../Dummy/DummyHardwareDeviceDriver.cs | 2 + .../Integration/IHardwareDeviceDriver.cs | 2 + Ryujinx.Ava/AppHost.cs | 433 ++++++++---------- 4 files changed, 189 insertions(+), 250 deletions(-) diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs index fbdfe18f2..1eedfa660 100644 --- a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs +++ b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs @@ -15,6 +15,8 @@ namespace Ryujinx.Audio.Backends.CompatLayer { private IHardwareDeviceDriver _realDriver; + public static bool IsSupported => true; + public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice) { _realDriver = realDevice; diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs index eecc95f55..641640f0e 100644 --- a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs +++ b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs @@ -12,6 +12,8 @@ namespace Ryujinx.Audio.Backends.Dummy private ManualResetEvent _updateRequiredEvent; private ManualResetEvent _pauseEvent; + public static bool IsSupported => true; + public DummyHardwareDeviceDriver() { _updateRequiredEvent = new ManualResetEvent(false); diff --git a/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs b/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs index 11de9f237..4ed179519 100644 --- a/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs +++ b/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs @@ -26,6 +26,8 @@ namespace Ryujinx.Audio.Integration bool SupportsSampleFormat(SampleFormat sampleFormat); bool SupportsChannelCount(uint channelCount); + static abstract bool IsSupported { get; } + IHardwareDeviceDriver GetRealDeviceDriver() { return this; diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 5a0910e87..65f84c494 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -41,6 +41,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SPB.Graphics.Vulkan; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; @@ -57,95 +58,96 @@ namespace Ryujinx.Ava { internal class AppHost { - private const int CursorHideIdleTime = 8; // Hide Cursor seconds + private const int CursorHideIdleTime = 8; // Hide Cursor seconds. private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. - private const int TargetFps = 60; + private const int TargetFps = 60; + private const float VolumeDelta = 0.05f; - private const float VolumeDelta = 0.05f; + private static readonly Cursor InvisibleCursor = new(StandardCursorType.None); - private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); - - private readonly long _ticksPerFrame; + private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; - private readonly AccountManager _accountManager; + private long _ticks; + + private readonly AccountManager _accountManager; private readonly UserChannelPersistence _userChannelPersistence; - private readonly InputManager _inputManager; + private readonly InputManager _inputManager; + private readonly MainWindowViewModel _viewModel; - private readonly IKeyboard _keyboardInterface; + private readonly IKeyboard _keyboardInterface; + private readonly TopLevel _topLevel; + private readonly GraphicsDebugLevel _glLogLevel; + private float _newVolume; + private KeyboardHotkeyState _prevHotkeyState; private bool _hideCursorOnIdle; + private long _lastCursorMoveTime; + private bool _isCursorInRenderer; + private bool _isStopped; private bool _isActive; - private long _lastCursorMoveTime; - private float _newVolume; - private long _ticks = 0; - - private KeyboardHotkeyState _prevHotkeyState; - - private IRenderer _renderer; - private readonly Thread _renderingThread; - - private bool _isMouseInRenderer; private bool _renderingStarted; - private bool _dialogShown; + private IRenderer _renderer; + private readonly Thread _renderingThread; + private readonly CancellationTokenSource _gpuCancellationTokenSource; private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - private readonly CancellationTokenSource _gpuCancellationTokenSource; + private bool _dialogShown; + private readonly bool _isFirmwareTitle; + + private readonly object _lockObject = new(); public event EventHandler AppExit; public event EventHandler StatusUpdatedEvent; - public RendererHost Renderer { get; } - public VirtualFileSystem VirtualFileSystem { get; } - public ContentManager ContentManager { get; } - public Switch Device { get; set; } - public NpadManager NpadManager { get; } + public RendererHost Renderer { get; } + public VirtualFileSystem VirtualFileSystem { get; } + public ContentManager ContentManager { get; } + public NpadManager NpadManager { get; } public TouchScreenManager TouchScreenManager { get; } + public Switch Device { get; set; } - public int Width { get; private set; } - public int Height { get; private set; } - public string ApplicationPath { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + public string ApplicationPath { get; private set; } + public bool ScreenshotRequested { get; set; } - private bool _isFirmwareTitle; - - public bool ScreenshotRequested { get; set; } - - private object _lockObject = new(); - private TopLevel _topLevel; public AppHost( - RendererHost renderer, - InputManager inputManager, - string applicationPath, - VirtualFileSystem virtualFileSystem, - ContentManager contentManager, - AccountManager accountManager, + RendererHost renderer, + InputManager inputManager, + string applicationPath, + VirtualFileSystem virtualFileSystem, + ContentManager contentManager, + AccountManager accountManager, UserChannelPersistence userChannelPersistence, - MainWindowViewModel viewmodel, - TopLevel topLevel) + MainWindowViewModel viewmodel, + TopLevel topLevel) { - _viewModel = viewmodel; - _inputManager = inputManager; - _accountManager = accountManager; + _viewModel = viewmodel; + _inputManager = inputManager; + _accountManager = accountManager; _userChannelPersistence = userChannelPersistence; - _renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" }; - _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; - _topLevel = topLevel; + _renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" }; + _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; + _topLevel = topLevel; + _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); + _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - NpadManager = _inputManager.CreateNpadManager(); + NpadManager = _inputManager.CreateNpadManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager(); - Renderer = renderer; - ApplicationPath = applicationPath; - VirtualFileSystem = virtualFileSystem; - ContentManager = contentManager; + Renderer = renderer; + ApplicationPath = applicationPath; + VirtualFileSystem = virtualFileSystem; + ContentManager = contentManager; - _chrono = new Stopwatch(); + _chrono = new Stopwatch(); _ticksPerFrame = Stopwatch.Frequency / TargetFps; if (ApplicationPath.StartsWith("@SystemContent")) @@ -161,9 +163,9 @@ namespace Ryujinx.Ava _topLevel.PointerMoved += TopLevel_PointerMoved; ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; _gpuCancellationTokenSource = new CancellationTokenSource(); } @@ -173,15 +175,17 @@ namespace Ryujinx.Ava if (sender is Control visual) { _lastCursorMoveTime = Stopwatch.GetTimestamp(); + var point = e.GetCurrentPoint(visual).Position; - _isMouseInRenderer = Equals(visual.InputHitTest(point), Renderer); + + _isCursorInRenderer = Equals(visual.InputHitTest(point), Renderer); } } private void TopLevel_PointerLeave(object sender, PointerEventArgs e) { - _isMouseInRenderer = false; - _viewModel.Cursor = Cursor.Default; + _isCursorInRenderer = false; + _viewModel.Cursor = Cursor.Default; } private void SetRendererWindowSize(Size size) @@ -189,6 +193,7 @@ namespace Ryujinx.Ava if (_renderer != null) { double scale = _topLevel.PlatformImpl.RenderScaling; + _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); } } @@ -201,12 +206,13 @@ namespace Ryujinx.Ava { lock (_lockObject) { - var currentTime = DateTime.Now; - string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; + DateTime currentTime = DateTime.Now; + string filename = $"ryujinx_capture_{currentTime}-{currentTime:D2}-{currentTime:D2}_{currentTime:D2}-{currentTime:D2}-{currentTime:D2}.png"; + string directory = AppDataManager.Mode switch { AppDataManager.LaunchMode.Portable => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") + _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") }; string path = Path.Combine(directory, filename); @@ -266,21 +272,10 @@ namespace Ryujinx.Ava _viewModel.IsGameRunning = true; - string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) - ? string.Empty - : $" - {Device.Application.TitleName}"; - - string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) - ? string.Empty - : $" v{Device.Application.DisplayVersion}"; - - string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) - ? string.Empty - : $" ({Device.Application.TitleIdText.ToUpper()})"; - - string titleArchSection = Device.Application.TitleIs64Bit - ? " (64-bit)" - : " (32-bit)"; + string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty : $" - {Device.Application.TitleName}"; + string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty : $" v{Device.Application.DisplayVersion}"; + string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty : $" ({Device.Application.TitleIdText.ToUpper()})"; + string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)"; Dispatcher.UIThread.InvokeAsync(() => { @@ -326,9 +321,9 @@ namespace Ryujinx.Ava private void UpdateAudioVolumeState(object sender, ReactiveEventArgs e) { Device?.SetVolume(e.NewValue); + Dispatcher.UIThread.Post(() => { - var value = e.NewValue; _viewModel.Volume = e.NewValue; }); } @@ -348,7 +343,7 @@ namespace Ryujinx.Ava } _isStopped = true; - _isActive = false; + _isActive = false; } public void DisposeContext() @@ -381,9 +376,9 @@ namespace Ryujinx.Ava } ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; _topLevel.PointerLeave -= TopLevel_PointerLeave; _topLevel.PointerMoved -= TopLevel_PointerMoved; @@ -597,19 +592,23 @@ namespace Ryujinx.Ava internal void Resume() { Device?.System.TogglePauseEmulation(false); + _viewModel.IsPaused = false; } internal void Pause() { Device?.System.TogglePauseEmulation(true); + _viewModel.IsPaused = true; } private void InitializeSwitchInstance() { + // Initialize KeySet. VirtualFileSystem.ReloadKeySet(); + // Initialize Renderer. IRenderer renderer; if (Renderer.IsVulkan) @@ -623,12 +622,9 @@ namespace Ryujinx.Ava renderer = new OpenGLRenderer(); } - IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver(); - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; var isGALthreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - if (isGALthreaded) { renderer = new ThreadedRenderer(renderer); @@ -636,159 +632,104 @@ namespace Ryujinx.Ava Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALthreaded}"); - if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SDL2) - { - if (SDL2HardwareDeviceDriver.IsSupported) - { - deviceDriver = new SDL2HardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL."); - - if (OpenALHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; - MainWindowViewModel.SaveConfig(); - - deviceDriver = new OpenALHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SoundIO."); - - if (SoundIoHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; - MainWindowViewModel.SaveConfig(); - - deviceDriver = new SoundIoHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out."); - } - } - } - } - else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo) - { - if (SoundIoHardwareDeviceDriver.IsSupported) - { - deviceDriver = new SoundIoHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, trying to fall back to SDL2."); - - if (SDL2HardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; - MainWindowViewModel.SaveConfig(); - - deviceDriver = new SDL2HardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL."); - - if (OpenALHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; - MainWindowViewModel.SaveConfig(); - - deviceDriver = new OpenALHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, falling back to dummy audio out."); - } - } - } - } - else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.OpenAl) - { - if (OpenALHardwareDeviceDriver.IsSupported) - { - deviceDriver = new OpenALHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SDL2."); - - if (SDL2HardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; - MainWindowViewModel.SaveConfig(); - - deviceDriver = new SDL2HardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to SoundIO."); - - if (SoundIoHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; - MainWindowViewModel.SaveConfig(); - - deviceDriver = new SoundIoHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out."); - } - } - } - } - + // Initialize Configuration. var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB; - IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; - - HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(VirtualFileSystem, - _viewModel.LibHacHorizonManager, - ContentManager, - _accountManager, - _userChannelPersistence, - renderer, - deviceDriver, - memoryConfiguration, - _viewModel.UiHandler, - (SystemLanguage)ConfigurationState.Instance.System.Language.Value, - (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, - ConfigurationState.Instance.System.EnableDockedMode, - ConfigurationState.Instance.System.EnablePtc, - ConfigurationState.Instance.System.EnableInternetAccess, - fsIntegrityCheckLevel, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - ConfigurationState.Instance.System.SystemTimeOffset, - ConfigurationState.Instance.System.TimeZone, - ConfigurationState.Instance.System.MemoryManagerMode, - ConfigurationState.Instance.System.IgnoreMissingServices, - ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume); + HLE.HLEConfiguration configuration = new(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.EnableVsync, + 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.SystemTimeOffset, + ConfigurationState.Instance.System.TimeZone, + ConfigurationState.Instance.System.MemoryManagerMode, + ConfigurationState.Instance.System.IgnoreMissingServices, + ConfigurationState.Instance.Graphics.AspectRatio, + ConfigurationState.Instance.System.AudioVolume); Device = new Switch(configuration); } + private static IHardwareDeviceDriver InitializeAudio() + { + var availableBackends = new List() + { + AudioBackend.SDL2, + AudioBackend.SoundIo, + AudioBackend.OpenAl, + AudioBackend.Dummy + }; + + AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value; + + for (int i = 0; i < availableBackends.Count; i++) + { + if (availableBackends[i] == preferredBackend) + { + availableBackends.RemoveAt(i); + availableBackends.Insert(0, preferredBackend); + break; + } + } + + static IHardwareDeviceDriver InitializeAudioBackend(AudioBackend backend, AudioBackend nextBackend) where T : IHardwareDeviceDriver, new() + { + if (T.IsSupported) + { + return new T(); + } + else + { + Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); + + return null; + } + } + + IHardwareDeviceDriver deviceDriver = null; + + for (int i = 0; i < availableBackends.Count; i++) + { + AudioBackend currentBackend = availableBackends[i]; + AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy; + + deviceDriver = currentBackend switch + { + AudioBackend.SDL2 => InitializeAudioBackend(AudioBackend.SDL2, nextBackend), + AudioBackend.SoundIo => InitializeAudioBackend(AudioBackend.SoundIo, nextBackend), + AudioBackend.OpenAl => InitializeAudioBackend(AudioBackend.OpenAl, nextBackend), + _ => new DummyHardwareDeviceDriver() + }; + + if (deviceDriver != null) + { + ConfigurationState.Instance.System.AudioBackend.Value = currentBackend; + break; + } + } + + MainWindowViewModel.SaveConfig(); + + return deviceDriver; + } + private void Window_SizeChanged(object sender, Size e) { - Width = (int)e.Width; + Width = (int)e.Width; Height = (int)e.Height; + SetRendererWindowSize(e); } @@ -798,7 +739,7 @@ namespace Ryujinx.Ava { UpdateFrame(); - // Polling becomes expensive if it's not slept + // Polling becomes expensive if it's not slept. Thread.Sleep(1); } } @@ -818,14 +759,7 @@ namespace Ryujinx.Ava } }); - IRenderer renderer = Device.Gpu.Renderer; - - if (renderer is ThreadedRenderer tr) - { - renderer = tr.BaseRenderer; - } - - _renderer = renderer; + _renderer = Device.Gpu.Renderer is ThreadedRenderer tr ? tr.BaseRenderer : Device.Gpu.Renderer; _renderer.ScreenCaptured += Renderer_ScreenCaptured; @@ -884,13 +818,12 @@ namespace Ryujinx.Ava public void UpdateStatus() { - // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued + // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; - float scale = GraphicsConfig.ResScale; - - if (scale != 1) + + if (GraphicsConfig.ResScale != 1) { - dockedMode += $" ({scale}x)"; + dockedMode += $" ({GraphicsConfig.ResScale}x)"; } StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( @@ -907,7 +840,6 @@ namespace Ryujinx.Ava public async Task ShowExitPrompt() { bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; - if (!shouldExit) { if (_dialogShown) @@ -916,6 +848,7 @@ namespace Ryujinx.Ava } _dialogShown = true; + shouldExit = await ContentDialogHelper.CreateStopEmulationDialog(); _dialogShown = false; @@ -933,7 +866,7 @@ namespace Ryujinx.Ava { Dispatcher.UIThread.Post(() => { - _viewModel.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default; + _viewModel.Cursor = _isCursorInRenderer ? InvisibleCursor : Cursor.Default; }); } else @@ -1046,7 +979,7 @@ namespace Ryujinx.Ava } } - // Touchscreen + // Touchscreen. bool hasTouch = false; if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) From 7210c17c5e474addded6ef825036ab10da607a35 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Tue, 10 Jan 2023 19:00:14 +0100 Subject: [PATCH 263/737] misc: Enforce LF (#4253) Because we are building everything on Windows for release at the moment, git default line ending to CRLF causing issues when packing the Ryujinx.sh script. This addresses this by enforcing all files to use LF via .gitattributes. --- .gitattributes | 61 +------------------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/.gitattributes b/.gitattributes index 1ff0c4230..e39a7f135 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,63 +1,4 @@ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain +* text=auto eol=lf From 600f86dc7b93b24c85f4ad22d2f6a5f2f3556213 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Tue, 10 Jan 2023 13:15:15 -0500 Subject: [PATCH 264/737] Fix context menu locales (#4242) --- Ryujinx.Ava/Assets/Locales/de_DE.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/el_GR.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/en_US.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/es_ES.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/fr_FR.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/it_IT.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/ja_JP.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/ko_KR.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/pl_PL.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/pt_BR.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/ru_RU.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/tr_TR.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/uk_UA.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/zh_CN.json | 10 +++++----- Ryujinx.Ava/Assets/Locales/zh_TW.json | 10 +++++----- Ryujinx.Ava/UI/Controls/GameGridView.axaml | 8 ++++---- Ryujinx.Ava/UI/Controls/GameListView.axaml | 8 ++++---- 17 files changed, 83 insertions(+), 83 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/de_DE.json b/Ryujinx.Ava/Assets/Locales/de_DE.json index dc84afa8e..adda79caa 100644 --- a/Ryujinx.Ava/Assets/Locales/de_DE.json +++ b/Ryujinx.Ava/Assets/Locales/de_DE.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Pfad", "GameListContextMenuOpenUserSaveDirectory": "Spielstand-Verzeichnis öffnen", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den Benutzer-Spielstand beinhaltet", - "GameListContextMenuOpenUserDeviceDirectory": "Benutzer-Geräte-Verzeichnis öffnen", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Öffnet das Verzeichnis, welches den Geräte-Spielstände beinhaltet", - "GameListContextMenuOpenUserBcatDirectory": "Benutzer-BCAT-Vezeichnis öffnen", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Öffnet das Verzeichnis, welches den BCAT Cache des Spiels beinhaltet", + "GameListContextMenuOpenDeviceSaveDirectory": "Benutzer-Geräte-Verzeichnis öffnen", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den Geräte-Spielstände beinhaltet", + "GameListContextMenuOpenBcatSaveDirectory": "Benutzer-BCAT-Vezeichnis öffnen", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den BCAT Cache des Spiels beinhaltet", "GameListContextMenuManageTitleUpdates": "Verwalten von Spiel Updates", "GameListContextMenuManageTitleUpdatesToolTip": "Öffnet den Spiel-Update-Manager", "GameListContextMenuManageDlc": "Verwalten von DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Konto wiederherstellen", "Recover": "Wiederherstellen", "UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/el_GR.json b/Ryujinx.Ava/Assets/Locales/el_GR.json index a05c8aed2..aef474ac9 100644 --- a/Ryujinx.Ava/Assets/Locales/el_GR.json +++ b/Ryujinx.Ava/Assets/Locales/el_GR.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Τοποθεσία", "GameListContextMenuOpenUserSaveDirectory": "Άνοιγμα Τοποθεσίας Αποθήκευσης Χρήστη", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Χρήστη της εφαρμογής", - "GameListContextMenuOpenUserDeviceDirectory": "Άνοιγμα Τοποθεσίας Συσκευής Χρήστη", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Συσκευής της εφαρμογής", - "GameListContextMenuOpenUserBcatDirectory": "Άνοιγμα Τοποθεσίας BCAT", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση BCAT της εφαρμογής", + "GameListContextMenuOpenDeviceSaveDirectory": "Άνοιγμα Τοποθεσίας Συσκευής Χρήστη", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Συσκευής της εφαρμογής", + "GameListContextMenuOpenBcatSaveDirectory": "Άνοιγμα Τοποθεσίας BCAT", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση BCAT της εφαρμογής", "GameListContextMenuManageTitleUpdates": "Διαχείριση Ενημερώσεων Παιχνιδιού", "GameListContextMenuManageTitleUpdatesToolTip": "Ανοίγει το παράθυρο διαχείρισης Ενημερώσεων Παιχνιδιού", "GameListContextMenuManageDlc": "Διαχείριση DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading": "Saves were found for the following accounts" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index a0507e7f1..afd3f3932 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Path", "GameListContextMenuOpenUserSaveDirectory": "Open User Save Directory", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Opens the directory which contains Application's User Save", - "GameListContextMenuOpenUserDeviceDirectory": "Open User Device Directory", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Opens the directory which contains Application's Device Save", - "GameListContextMenuOpenUserBcatDirectory": "Open User BCAT Directory", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Opens the directory which contains Application's BCAT Save", + "GameListContextMenuOpenDeviceSaveDirectory": "Open Device Save Directory", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Opens the directory which contains Application's Device Save", + "GameListContextMenuOpenBcatSaveDirectory": "Open BCAT Save Directory", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Opens the directory which contains Application's BCAT Save", "GameListContextMenuManageTitleUpdates": "Manage Title Updates", "GameListContextMenuManageTitleUpdatesToolTip": "Opens the Title Update management window", "GameListContextMenuManageDlc": "Manage DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading" : "Saves were found for the following accounts" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/es_ES.json b/Ryujinx.Ava/Assets/Locales/es_ES.json index c45aa1fbb..8275cdd88 100644 --- a/Ryujinx.Ava/Assets/Locales/es_ES.json +++ b/Ryujinx.Ava/Assets/Locales/es_ES.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Directorio", "GameListContextMenuOpenUserSaveDirectory": "Abrir carpeta de guardado de este usuario", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del usuario para esta aplicación", - "GameListContextMenuOpenUserDeviceDirectory": "Abrir carpeta de guardado del sistema para el usuario actual", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del sistema para esta aplicación", - "GameListContextMenuOpenUserBcatDirectory": "Abrir carpeta de guardado BCAT del usuario", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Abrir la carpeta que contiene el guardado BCAT de esta aplicación", + "GameListContextMenuOpenDeviceSaveDirectory": "Abrir carpeta de guardado del sistema para el usuario actual", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del sistema para esta aplicación", + "GameListContextMenuOpenBcatSaveDirectory": "Abrir carpeta de guardado BCAT del usuario", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Abrir la carpeta que contiene el guardado BCAT de esta aplicación", "GameListContextMenuManageTitleUpdates": "Gestionar actualizaciones del juego", "GameListContextMenuManageTitleUpdatesToolTip": "Abrir la ventana de gestión de actualizaciones de esta aplicación", "GameListContextMenuManageDlc": "Gestionar DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading": "Saves were found for the following accounts" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/fr_FR.json b/Ryujinx.Ava/Assets/Locales/fr_FR.json index 735c0db06..634a996c0 100644 --- a/Ryujinx.Ava/Assets/Locales/fr_FR.json +++ b/Ryujinx.Ava/Assets/Locales/fr_FR.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Chemin", "GameListContextMenuOpenUserSaveDirectory": "Ouvrir le dossier de sauvegarde utilisateur", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde utilisateur du jeu", - "GameListContextMenuOpenUserDeviceDirectory": "Ouvrir le dossier de sauvegarde console", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde console du jeu", - "GameListContextMenuOpenUserBcatDirectory": "Ouvrir le dossier de sauvegarde BCAT", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde BCAT du jeu", + "GameListContextMenuOpenDeviceSaveDirectory": "Ouvrir le dossier de sauvegarde console", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde console du jeu", + "GameListContextMenuOpenBcatSaveDirectory": "Ouvrir le dossier de sauvegarde BCAT", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde BCAT du jeu", "GameListContextMenuManageTitleUpdates": "Gérer les mises à jour", "GameListContextMenuManageTitleUpdatesToolTip": "Ouvre la fenêtre de gestion des mises à jour", "GameListContextMenuManageDlc": "Gérer les DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Récupérer les comptes perdus", "Recover": "Récupérer", "UserProfilesRecoverHeading": "Des sauvegardes ont été trouvées pour les comptes suivants" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/it_IT.json b/Ryujinx.Ava/Assets/Locales/it_IT.json index be1721f29..b53dbf370 100644 --- a/Ryujinx.Ava/Assets/Locales/it_IT.json +++ b/Ryujinx.Ava/Assets/Locales/it_IT.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Percorso", "GameListContextMenuOpenUserSaveDirectory": "Apri la cartella salvataggi dell'utente", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Apre la cartella che contiene i salvataggi di gioco dell'utente", - "GameListContextMenuOpenUserDeviceDirectory": "Apri la cartella dispositivo dell'utente", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Apre la cartella che contiene i salvataggi di gioco del dispositivo", - "GameListContextMenuOpenUserBcatDirectory": "Apri la cartella BCAT dell'utente", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Apre la cartella che contiene i salvataggi BCAT dell'applicazione", + "GameListContextMenuOpenDeviceSaveDirectory": "Apri la cartella dispositivo dell'utente", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Apre la cartella che contiene i salvataggi di gioco del dispositivo", + "GameListContextMenuOpenBcatSaveDirectory": "Apri la cartella BCAT dell'utente", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Apre la cartella che contiene i salvataggi BCAT dell'applicazione", "GameListContextMenuManageTitleUpdates": "Gestisci aggiornamenti del gioco", "GameListContextMenuManageTitleUpdatesToolTip": "Apre la finestra di gestione aggiornamenti del gioco", "GameListContextMenuManageDlc": "Gestici DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Recupera il tuo account", "Recover": "Recupera", "UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/ja_JP.json b/Ryujinx.Ava/Assets/Locales/ja_JP.json index 52356197f..c779056ba 100644 --- a/Ryujinx.Ava/Assets/Locales/ja_JP.json +++ b/Ryujinx.Ava/Assets/Locales/ja_JP.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "パス", "GameListContextMenuOpenUserSaveDirectory": "セーブディレクトリを開く", "GameListContextMenuOpenUserSaveDirectoryToolTip": "アプリケーションのユーザセーブデータを格納するディレクトリを開きます", - "GameListContextMenuOpenUserDeviceDirectory": "デバイスディレクトリを開く", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "アプリケーションのデバイスセーブデータを格納するディレクトリを開きます", - "GameListContextMenuOpenUserBcatDirectory": "BCATディレクトリを開く", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "アプリケーションの BCAT セーブデータを格納するディレクトリを開きます", + "GameListContextMenuOpenDeviceSaveDirectory": "デバイスディレクトリを開く", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "アプリケーションのデバイスセーブデータを格納するディレクトリを開きます", + "GameListContextMenuOpenBcatSaveDirectory": "BCATディレクトリを開く", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "アプリケーションの BCAT セーブデータを格納するディレクトリを開きます", "GameListContextMenuManageTitleUpdates": "アップデートを管理", "GameListContextMenuManageTitleUpdatesToolTip": "タイトルのアップデート管理ウインドウを開きます", "GameListContextMenuManageDlc": "DLCを管理", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "アカウントの復旧", "Recover": "復旧", "UserProfilesRecoverHeading": "以下のアカウントのセーブデータが見つかりました" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/ko_KR.json b/Ryujinx.Ava/Assets/Locales/ko_KR.json index 2d95c17ee..0efafc322 100644 --- a/Ryujinx.Ava/Assets/Locales/ko_KR.json +++ b/Ryujinx.Ava/Assets/Locales/ko_KR.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "경로", "GameListContextMenuOpenUserSaveDirectory": "사용자 저장 디렉토리 열기\n", "GameListContextMenuOpenUserSaveDirectoryToolTip": "응용 프로그램의 사용자 저장이 포함된 디렉토리 열기\n", - "GameListContextMenuOpenUserDeviceDirectory": "사용자 장치 디렉토리 열기", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "응용 프로그램의 장치 저장이 포함된 디렉토리 열기\n", - "GameListContextMenuOpenUserBcatDirectory": "사용자의 BCAT 디렉토리 열기\n", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "응용 프로그램의 BCAT 저장이 포함된 디렉토리 열기\n", + "GameListContextMenuOpenDeviceSaveDirectory": "사용자 장치 디렉토리 열기", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "응용 프로그램의 장치 저장이 포함된 디렉토리 열기\n", + "GameListContextMenuOpenBcatSaveDirectory": "사용자의 BCAT 디렉토리 열기\n", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "응용 프로그램의 BCAT 저장이 포함된 디렉토리 열기\n", "GameListContextMenuManageTitleUpdates": "타이틀 업데이트 관리\n", "GameListContextMenuManageTitleUpdatesToolTip": "타이틀 업데이트 관리 창 열기", "GameListContextMenuManageDlc": "DLC 관리", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "잃어버린 계정 복구", "Recover": "복구", "UserProfilesRecoverHeading": "다음 계정에 대한 저장 발견" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/Ryujinx.Ava/Assets/Locales/pl_PL.json index 0ebdde8a3..020f54b83 100644 --- a/Ryujinx.Ava/Assets/Locales/pl_PL.json +++ b/Ryujinx.Ava/Assets/Locales/pl_PL.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Ścieżka", "GameListContextMenuOpenUserSaveDirectory": "Otwórz Katalog Zapisów Użytkownika", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Otwiera katalog, który zawiera Zapis Użytkownika Aplikacji", - "GameListContextMenuOpenUserDeviceDirectory": "Otwórz Katalog Urządzeń Użytkownika", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Otwiera katalog, który zawiera Zapis Urządzenia Aplikacji", - "GameListContextMenuOpenUserBcatDirectory": "Otwórz Katalog BCAT Użytkownika", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Otwiera katalog, który zawiera Zapis BCAT Aplikacji", + "GameListContextMenuOpenDeviceSaveDirectory": "Otwórz Katalog Urządzeń Użytkownika", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Otwiera katalog, który zawiera Zapis Urządzenia Aplikacji", + "GameListContextMenuOpenBcatSaveDirectory": "Otwórz Katalog BCAT Użytkownika", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Otwiera katalog, który zawiera Zapis BCAT Aplikacji", "GameListContextMenuManageTitleUpdates": "Zarządzaj Aktualizacjami Tytułów", "GameListContextMenuManageTitleUpdatesToolTip": "Otwiera okno zarządzania Aktualizacjami Tytułu", "GameListContextMenuManageDlc": "Zarządzaj DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Odzyskaj Utracone Konta", "Recover": "Odzyskaj", "UserProfilesRecoverHeading": "Znaleziono zapisy dla następujących kont" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json index 6fa946bc9..6a377b0d6 100644 --- a/Ryujinx.Ava/Assets/Locales/pt_BR.json +++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Caminho", "GameListContextMenuOpenUserSaveDirectory": "Abrir diretório de saves do usuário", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre o diretório que contém jogos salvos para o usuário atual", - "GameListContextMenuOpenUserDeviceDirectory": "Abrir diretório de saves de dispositivo do usuário", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Abre o diretório que contém saves do dispositivo para o usuário atual", - "GameListContextMenuOpenUserBcatDirectory": "Abrir diretório de saves BCAT do usuário", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Abre o diretório que contém saves BCAT para o usuário atual", + "GameListContextMenuOpenDeviceSaveDirectory": "Abrir diretório de saves de dispositivo do usuário", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Abre o diretório que contém saves do dispositivo para o usuário atual", + "GameListContextMenuOpenBcatSaveDirectory": "Abrir diretório de saves BCAT do usuário", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Abre o diretório que contém saves BCAT para o usuário atual", "GameListContextMenuManageTitleUpdates": "Gerenciar atualizações do jogo", "GameListContextMenuManageTitleUpdatesToolTip": "Abre a janela de gerenciamento de atualizações", "GameListContextMenuManageDlc": "Gerenciar DLCs", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Recuperar contas perdidas", "Recover": "Recuperar", "UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/ru_RU.json b/Ryujinx.Ava/Assets/Locales/ru_RU.json index 5bace0c76..b5eadd6bb 100644 --- a/Ryujinx.Ava/Assets/Locales/ru_RU.json +++ b/Ryujinx.Ava/Assets/Locales/ru_RU.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Путь", "GameListContextMenuOpenUserSaveDirectory": "Открыть каталог сохранений пользователя", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Открывает каталог, содержащий пользовательское сохранение приложения", - "GameListContextMenuOpenUserDeviceDirectory": "Открыть каталог пользовательских устройств", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Открывает каталог, содержащий сохранение устройства приложения", - "GameListContextMenuOpenUserBcatDirectory": "Открыть каталог пользователей BCAT", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Открывает каталог, содержащий BCAT сохранения приложения.", + "GameListContextMenuOpenDeviceSaveDirectory": "Открыть каталог пользовательских устройств", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Открывает каталог, содержащий сохранение устройства приложения", + "GameListContextMenuOpenBcatSaveDirectory": "Открыть каталог пользователей BCAT", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Открывает каталог, содержащий BCAT сохранения приложения.", "GameListContextMenuManageTitleUpdates": "Управление обновлениями заголовков", "GameListContextMenuManageTitleUpdatesToolTip": "Открывает окно управления обновлением заголовков", "GameListContextMenuManageDlc": "Управление DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading": "Saves were found for the following accounts" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/tr_TR.json b/Ryujinx.Ava/Assets/Locales/tr_TR.json index 9e8d9c697..623ad34ae 100644 --- a/Ryujinx.Ava/Assets/Locales/tr_TR.json +++ b/Ryujinx.Ava/Assets/Locales/tr_TR.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Yol", "GameListContextMenuOpenUserSaveDirectory": "Kullanıcı Kayıt Dosyası Dizinini Aç", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Uygulamanın Kullanıcı Kaydı'nın bulunduğu dizini açar", - "GameListContextMenuOpenUserDeviceDirectory": "Kullanıcı Cihaz Dizinini Aç", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Uygulamanın Kullanıcı Cihaz Kaydı'nın bulunduğu dizini açar", - "GameListContextMenuOpenUserBcatDirectory": "Kullanıcı BCAT Dizinini Aç", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Uygulamanın Kullanıcı BCAT Kaydı'nın bulunduğu dizini açar", + "GameListContextMenuOpenDeviceSaveDirectory": "Kullanıcı Cihaz Dizinini Aç", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Uygulamanın Kullanıcı Cihaz Kaydı'nın bulunduğu dizini açar", + "GameListContextMenuOpenBcatSaveDirectory": "Kullanıcı BCAT Dizinini Aç", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Uygulamanın Kullanıcı BCAT Kaydı'nın bulunduğu dizini açar", "GameListContextMenuManageTitleUpdates": "Oyun Güncellemelerini Yönet", "GameListContextMenuManageTitleUpdatesToolTip": "Oyun Güncelleme Yönetim Penceresini Açar", "GameListContextMenuManageDlc": "DLC'leri Yönet", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Kayıp Hesapları Kurtar", "Recover": "Kurtar", "UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/uk_UA.json b/Ryujinx.Ava/Assets/Locales/uk_UA.json index f502986e2..e28d47c28 100644 --- a/Ryujinx.Ava/Assets/Locales/uk_UA.json +++ b/Ryujinx.Ava/Assets/Locales/uk_UA.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "Шлях", "GameListContextMenuOpenUserSaveDirectory": "Відкрити каталог збереження користувача", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Відкриває каталог, який містить збереження користувача програми", - "GameListContextMenuOpenUserDeviceDirectory": "Відкрити каталог пристроїв користувача", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "Відкриває каталог, який містить збереження пристрою програми", - "GameListContextMenuOpenUserBcatDirectory": "Відкрити каталог користувача BCAT", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "Відкриває каталог, який містить BCAT-збереження програми", + "GameListContextMenuOpenDeviceSaveDirectory": "Відкрити каталог пристроїв користувача", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Відкриває каталог, який містить збереження пристрою програми", + "GameListContextMenuOpenBcatSaveDirectory": "Відкрити каталог користувача BCAT", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Відкриває каталог, який містить BCAT-збереження програми", "GameListContextMenuManageTitleUpdates": "Керування оновленнями заголовків", "GameListContextMenuManageTitleUpdatesToolTip": "Відкриває вікно керування оновленням заголовка", "GameListContextMenuManageDlc": "Керування DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "Відновлення втрачених облікових записів", "Recover": "Відновити", "UserProfilesRecoverHeading": "Знайдено збереження для наступних облікових записів" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/zh_CN.json b/Ryujinx.Ava/Assets/Locales/zh_CN.json index cb4c4dde0..1fcfe8dab 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_CN.json +++ b/Ryujinx.Ava/Assets/Locales/zh_CN.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "路径", "GameListContextMenuOpenUserSaveDirectory": "打开应用存档目录", "GameListContextMenuOpenUserSaveDirectoryToolTip": "打开储存游戏存档的目录", - "GameListContextMenuOpenUserDeviceDirectory": "打开应用系统目录", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "打开包含游戏系统设置的目录", - "GameListContextMenuOpenUserBcatDirectory": "打开 BCAT 目录", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "打开包含游戏 BCAT 数据的目录", + "GameListContextMenuOpenDeviceSaveDirectory": "打开应用系统目录", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "打开包含游戏系统设置的目录", + "GameListContextMenuOpenBcatSaveDirectory": "打开 BCAT 目录", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "打开包含游戏 BCAT 数据的目录", "GameListContextMenuManageTitleUpdates": "管理游戏更新", "GameListContextMenuManageTitleUpdatesToolTip": "打开更新管理器", "GameListContextMenuManageDlc": "管理 DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "恢复丢失的账户", "Recover": "恢复", "UserProfilesRecoverHeading": "找到了这些用户的存档数据" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/Assets/Locales/zh_TW.json b/Ryujinx.Ava/Assets/Locales/zh_TW.json index 5c98c1c9b..fcc16d78a 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -41,10 +41,10 @@ "GameListHeaderPath": "路徑", "GameListContextMenuOpenUserSaveDirectory": "開啟使用者存檔資料夾", "GameListContextMenuOpenUserSaveDirectoryToolTip": "開啟儲存遊戲存檔的資料夾", - "GameListContextMenuOpenUserDeviceDirectory": "開啟系統資料夾", - "GameListContextMenuOpenUserDeviceDirectoryToolTip": "開啟包含遊戲系統設定的資料夾", - "GameListContextMenuOpenUserBcatDirectory": "開啟 BCAT 資料夾", - "GameListContextMenuOpenUserBcatDirectoryToolTip": "開啟包含遊戲 BCAT 資料的資料夾", + "GameListContextMenuOpenDeviceSaveDirectory": "開啟系統資料夾", + "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "開啟包含遊戲系統設定的資料夾", + "GameListContextMenuOpenBcatSaveDirectory": "開啟 BCAT 資料夾", + "GameListContextMenuOpenBcatSaveDirectoryToolTip": "開啟包含遊戲 BCAT 資料的資料夾", "GameListContextMenuManageTitleUpdates": "管理遊戲更新", "GameListContextMenuManageTitleUpdatesToolTip": "開啟更新管理視窗", "GameListContextMenuManageDlc": "管理 DLC", @@ -610,4 +610,4 @@ "UserProfilesRecoverLostAccounts": "恢復遺失的帳號", "Recover": "恢復", "UserProfilesRecoverHeading": "在以下帳號找到了一些遊戲存檔" -} +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml b/Ryujinx.Ava/UI/Controls/GameGridView.axaml index 02e8f0f8f..c757f066c 100644 --- a/Ryujinx.Ava/UI/Controls/GameGridView.axaml +++ b/Ryujinx.Ava/UI/Controls/GameGridView.axaml @@ -26,12 +26,12 @@ ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> + Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" /> + Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" /> + Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" /> + Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" /> Date: Tue, 10 Jan 2023 21:49:46 +0000 Subject: [PATCH 265/737] Set LSApplicationCategoryType to games (#4257) https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype Makes it auto-add to the macOS Launchpad games folder --- distribution/macos/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist index c3e1e08e4..6c3f7717c 100644 --- a/distribution/macos/Info.plist +++ b/distribution/macos/Info.plist @@ -40,6 +40,8 @@ NSHumanReadableCopyright Copyright © 2018 - 2022 Ryujinx Team and Contributors. + LSApplicationCategoryType + public.app-category.games LSMinimumSystemVersion 11.0 From 5e0f8e873857ce3ca3f532aff0936beb28e412c8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 10 Jan 2023 19:16:59 -0300 Subject: [PATCH 266/737] Implement JIT Arm64 backend (#4114) * Implement JIT Arm64 backend * PPTC version bump * Address some feedback from Arm64 JIT PR * Address even more PR feedback * Remove unused IsPageAligned function * Sync Qc flag before calls * Fix comment and remove unused enum * Address riperiperi PR feedback * Delete Breakpoint IR instruction that was only implemented for Arm64 --- ARMeilleure/ARMeilleure.csproj | 7 + ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs | 270 +++ ARMeilleure/CodeGen/Arm64/ArmCondition.cs | 47 + ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs | 14 + ARMeilleure/CodeGen/Arm64/ArmShiftType.cs | 11 + ARMeilleure/CodeGen/Arm64/Assembler.cs | 1160 ++++++++++++ .../CodeGen/Arm64/CallingConvention.cs | 96 + ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs | 173 ++ ARMeilleure/CodeGen/Arm64/CodeGenContext.cs | 286 +++ ARMeilleure/CodeGen/Arm64/CodeGenerator.cs | 1576 +++++++++++++++++ .../CodeGen/Arm64/CodeGeneratorIntrinsic.cs | 662 +++++++ ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs | 14 + ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs | 461 +++++ ARMeilleure/CodeGen/Arm64/IntrinsicType.cs | 59 + ARMeilleure/CodeGen/Arm64/PreAllocator.cs | 940 ++++++++++ .../CodeGen/Optimizations/ConstantFolding.cs | 41 + .../CodeGen/Optimizations/Optimizer.cs | 28 +- .../RegisterAllocators/LinearScanAllocator.cs | 75 +- .../RegisterAllocators/LiveInterval.cs | 4 +- .../RegisterAllocators/RegisterMasks.cs | 5 +- ARMeilleure/CodeGen/X86/CodeGenerator.cs | 4 +- ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 2 - .../Instructions/InstEmitSimdArithmetic.cs | 1516 +++++++++++++--- .../Instructions/InstEmitSimdArithmetic32.cs | 229 ++- ARMeilleure/Instructions/InstEmitSimdCmp.cs | 18 +- ARMeilleure/Instructions/InstEmitSimdCmp32.cs | 66 +- ARMeilleure/Instructions/InstEmitSimdCvt.cs | 294 ++- ARMeilleure/Instructions/InstEmitSimdCvt32.cs | 103 +- .../Instructions/InstEmitSimdHelper32Arm64.cs | 366 ++++ .../Instructions/InstEmitSimdHelperArm64.cs | 720 ++++++++ .../Instructions/InstEmitSimdLogical.cs | 54 +- .../Instructions/InstEmitSimdLogical32.cs | 54 +- .../Instructions/InstEmitSimdMove32.cs | 58 +- ARMeilleure/Instructions/InstEmitSimdShift.cs | 517 +++++- ARMeilleure/Instructions/InstEmitSystem.cs | 4 + .../IntermediateRepresentation/Intrinsic.cs | 456 ++++- .../IntermediateRepresentation/Multiplier.cs | 3 +- .../IntermediateRepresentation/Operand.cs | 14 + .../IntermediateRepresentation/OperandType.cs | 14 + ARMeilleure/Memory/ReservedRegion.cs | 2 +- ARMeilleure/Native/JitSupportDarwin.cs | 13 + .../libs/libarmeilleure-jitsupport.dylib | Bin 0 -> 33564 bytes ARMeilleure/Native/macos_jit_support/Makefile | 8 + .../Native/macos_jit_support/support.c | 14 + ARMeilleure/Optimizations.cs | 5 + ARMeilleure/Signal/NativeSignalHandler.cs | 50 +- ARMeilleure/Signal/TestMethods.cs | 8 +- .../Signal/UnixSignalHandlerRegistration.cs | 47 +- ARMeilleure/Translation/ArmEmitterContext.cs | 49 + ARMeilleure/Translation/Cache/JitCache.cs | 26 +- .../Translation/Cache/JitCacheInvalidation.cs | 79 + ARMeilleure/Translation/Compiler.cs | 19 +- ARMeilleure/Translation/PTC/Ptc.cs | 2 +- ARMeilleure/Translation/Translator.cs | 21 +- ARMeilleure/Translation/TranslatorStubs.cs | 6 +- Ryujinx.Cpu/Jit/JitMemoryAllocator.cs | 2 +- Ryujinx.Memory/MemoryAllocationFlags.cs | 14 +- Ryujinx.Memory/MemoryBlock.cs | 70 +- Ryujinx.Memory/MemoryManagement.cs | 12 +- Ryujinx.Memory/MemoryManagementUnix.cs | 31 +- Ryujinx.Memory/MemoryManagerUnixHelper.cs | 9 +- 61 files changed, 10266 insertions(+), 642 deletions(-) create mode 100644 ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs create mode 100644 ARMeilleure/CodeGen/Arm64/ArmCondition.cs create mode 100644 ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs create mode 100644 ARMeilleure/CodeGen/Arm64/ArmShiftType.cs create mode 100644 ARMeilleure/CodeGen/Arm64/Assembler.cs create mode 100644 ARMeilleure/CodeGen/Arm64/CallingConvention.cs create mode 100644 ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs create mode 100644 ARMeilleure/CodeGen/Arm64/CodeGenContext.cs create mode 100644 ARMeilleure/CodeGen/Arm64/CodeGenerator.cs create mode 100644 ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs create mode 100644 ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs create mode 100644 ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs create mode 100644 ARMeilleure/CodeGen/Arm64/IntrinsicType.cs create mode 100644 ARMeilleure/CodeGen/Arm64/PreAllocator.cs create mode 100644 ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs create mode 100644 ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs create mode 100644 ARMeilleure/Native/JitSupportDarwin.cs create mode 100644 ARMeilleure/Native/libs/libarmeilleure-jitsupport.dylib create mode 100644 ARMeilleure/Native/macos_jit_support/Makefile create mode 100644 ARMeilleure/Native/macos_jit_support/support.c create mode 100644 ARMeilleure/Translation/Cache/JitCacheInvalidation.cs diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj index bb3f47219..58fd04b38 100644 --- a/ARMeilleure/ARMeilleure.csproj +++ b/ARMeilleure/ARMeilleure.csproj @@ -9,4 +9,11 @@ + + + PreserveNewest + libarmeilleure-jitsupport.dylib + + + diff --git a/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs b/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs new file mode 100644 index 000000000..fdd4d0241 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs @@ -0,0 +1,270 @@ +using ARMeilleure.CodeGen.Optimizations; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System.Collections.Generic; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; +using static ARMeilleure.IntermediateRepresentation.Operation.Factory; + +namespace ARMeilleure.CodeGen.Arm64 +{ + static class Arm64Optimizer + { + private const int MaxConstantUses = 10000; + + public static void RunPass(ControlFlowGraph cfg) + { + var constants = new Dictionary(); + + Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source) + { + // If the constant has many uses, we also force a new constant mov to be added, in order + // to avoid overflow of the counts field (that is limited to 16 bits). + if (!constants.TryGetValue(source.Value, out var constant) || constant.UsesCount > MaxConstantUses) + { + constant = Local(source.Type); + + Operation copyOp = Operation(Instruction.Copy, constant, source); + + block.Operations.AddBefore(operation, copyOp); + + constants[source.Value] = constant; + } + + return constant; + } + + for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) + { + constants.Clear(); + + Operation nextNode; + + for (Operation node = block.Operations.First; node != default; node = nextNode) + { + nextNode = node.ListNext; + + // Insert copies for constants that can't fit on a 32-bit immediate. + // Doing this early unblocks a few optimizations. + if (node.Instruction == Instruction.Add) + { + Operand src1 = node.GetSource(0); + Operand src2 = node.GetSource(1); + + if (src1.Kind == OperandKind.Constant && (src1.Relocatable || ConstTooLong(src1, OperandType.I32))) + { + node.SetSource(0, GetConstantCopy(block, node, src1)); + } + + if (src2.Kind == OperandKind.Constant && (src2.Relocatable || ConstTooLong(src2, OperandType.I32))) + { + node.SetSource(1, GetConstantCopy(block, node, src2)); + } + } + + // Try to fold something like: + // lsl x1, x1, #2 + // add x0, x0, x1 + // ldr x0, [x0] + // add x2, x2, #16 + // ldr x2, [x2] + // Into: + // ldr x0, [x0, x1, lsl #2] + // ldr x2, [x2, #16] + if (IsMemoryLoadOrStore(node.Instruction)) + { + OperandType type; + + if (node.Destination != default) + { + type = node.Destination.Type; + } + else + { + type = node.GetSource(1).Type; + } + + Operand memOp = GetMemoryOperandOrNull(node.GetSource(0), type); + + if (memOp != default) + { + node.SetSource(0, memOp); + } + } + } + } + + Optimizer.RemoveUnusedNodes(cfg); + } + + private static Operand GetMemoryOperandOrNull(Operand addr, OperandType type) + { + Operand baseOp = addr; + + // First we check if the address is the result of a local X with immediate + // addition. If that is the case, then the baseOp is X, and the memory operand immediate + // becomes the addition immediate. Otherwise baseOp keeps being the address. + int imm = GetConstOp(ref baseOp, type); + if (imm != 0) + { + return MemoryOp(type, baseOp, default, Multiplier.x1, imm); + } + + // Now we check if the baseOp is the result of a local Y with a local Z addition. + // If that is the case, we now set baseOp to Y and indexOp to Z. We further check + // if Z is the result of a left shift of local W by a value == 0 or == Log2(AccessSize), + // if that is the case, we set indexOp to W and adjust the scale value of the memory operand + // to match that of the left shift. + // There is one missed case, which is the address being a shift result, but this is + // probably not worth optimizing as it should never happen. + (Operand indexOp, Multiplier scale) = GetIndexOp(ref baseOp, type); + + // If baseOp is still equal to address, then there's nothing that can be optimized. + if (baseOp == addr) + { + return default; + } + + return MemoryOp(type, baseOp, indexOp, scale, 0); + } + + private static int GetConstOp(ref Operand baseOp, OperandType accessType) + { + Operation operation = GetAsgOpWithInst(baseOp, Instruction.Add); + + if (operation == default) + { + return 0; + } + + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + Operand constOp; + Operand otherOp; + + if (src1.Kind == OperandKind.Constant && src2.Kind == OperandKind.LocalVariable) + { + constOp = src1; + otherOp = src2; + } + else if (src1.Kind == OperandKind.LocalVariable && src2.Kind == OperandKind.Constant) + { + constOp = src2; + otherOp = src1; + } + else + { + return 0; + } + + // If we have addition by a constant that we can't encode on the instruction, + // then we can't optimize it further. + if (ConstTooLong(constOp, accessType)) + { + return 0; + } + + baseOp = otherOp; + + return constOp.AsInt32(); + } + + private static (Operand, Multiplier) GetIndexOp(ref Operand baseOp, OperandType accessType) + { + Operand indexOp = default; + + Multiplier scale = Multiplier.x1; + + Operation addOp = GetAsgOpWithInst(baseOp, Instruction.Add); + + if (addOp == default) + { + return (indexOp, scale); + } + + Operand src1 = addOp.GetSource(0); + Operand src2 = addOp.GetSource(1); + + if (src1.Kind != OperandKind.LocalVariable || src2.Kind != OperandKind.LocalVariable) + { + return (indexOp, scale); + } + + baseOp = src1; + indexOp = src2; + + Operation shlOp = GetAsgOpWithInst(src1, Instruction.ShiftLeft); + + bool indexOnSrc2 = false; + + if (shlOp == default) + { + shlOp = GetAsgOpWithInst(src2, Instruction.ShiftLeft); + + indexOnSrc2 = true; + } + + if (shlOp != default) + { + Operand shSrc = shlOp.GetSource(0); + Operand shift = shlOp.GetSource(1); + + int maxShift = Assembler.GetScaleForType(accessType); + + if (shSrc.Kind == OperandKind.LocalVariable && + shift.Kind == OperandKind.Constant && + (shift.Value == 0 || shift.Value == (ulong)maxShift)) + { + scale = shift.Value switch + { + 1 => Multiplier.x2, + 2 => Multiplier.x4, + 3 => Multiplier.x8, + 4 => Multiplier.x16, + _ => Multiplier.x1 + }; + + baseOp = indexOnSrc2 ? src1 : src2; + indexOp = shSrc; + } + } + + return (indexOp, scale); + } + + private static Operation GetAsgOpWithInst(Operand op, Instruction inst) + { + // If we have multiple assignments, folding is not safe + // as the value may be different depending on the + // control flow path. + if (op.AssignmentsCount != 1) + { + return default; + } + + Operation asgOp = op.Assignments[0]; + + if (asgOp.Instruction != inst) + { + return default; + } + + return asgOp; + } + + private static bool IsMemoryLoadOrStore(Instruction inst) + { + return inst == Instruction.Load || inst == Instruction.Store; + } + + private static bool ConstTooLong(Operand constOp, OperandType accessType) + { + if ((uint)constOp.Value != constOp.Value) + { + return true; + } + + return !CodeGenCommon.ConstFitsOnUImm12(constOp.AsInt32(), accessType); + } + } +} diff --git a/ARMeilleure/CodeGen/Arm64/ArmCondition.cs b/ARMeilleure/CodeGen/Arm64/ArmCondition.cs new file mode 100644 index 000000000..db27a8104 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/ArmCondition.cs @@ -0,0 +1,47 @@ +using ARMeilleure.IntermediateRepresentation; +using System; + +namespace ARMeilleure.CodeGen.Arm64 +{ + enum ArmCondition + { + Eq = 0, + Ne = 1, + GeUn = 2, + LtUn = 3, + Mi = 4, + Pl = 5, + Vs = 6, + Vc = 7, + GtUn = 8, + LeUn = 9, + Ge = 10, + Lt = 11, + Gt = 12, + Le = 13, + Al = 14, + Nv = 15 + } + + static class ComparisonArm64Extensions + { + public static ArmCondition ToArmCondition(this Comparison comp) + { + return comp switch + { + Comparison.Equal => ArmCondition.Eq, + Comparison.NotEqual => ArmCondition.Ne, + Comparison.Greater => ArmCondition.Gt, + Comparison.LessOrEqual => ArmCondition.Le, + Comparison.GreaterUI => ArmCondition.GtUn, + Comparison.LessOrEqualUI => ArmCondition.LeUn, + Comparison.GreaterOrEqual => ArmCondition.Ge, + Comparison.Less => ArmCondition.Lt, + Comparison.GreaterOrEqualUI => ArmCondition.GeUn, + Comparison.LessUI => ArmCondition.LtUn, + + _ => throw new ArgumentException(null, nameof(comp)) + }; + } + } +} diff --git a/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs b/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs new file mode 100644 index 000000000..062a6d0b7 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs @@ -0,0 +1,14 @@ +namespace ARMeilleure.CodeGen.Arm64 +{ + enum ArmExtensionType + { + Uxtb = 0, + Uxth = 1, + Uxtw = 2, + Uxtx = 3, + Sxtb = 4, + Sxth = 5, + Sxtw = 6, + Sxtx = 7 + } +} diff --git a/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs b/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs new file mode 100644 index 000000000..d223a1464 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs @@ -0,0 +1,11 @@ + +namespace ARMeilleure.CodeGen.Arm64 +{ + enum ArmShiftType + { + Lsl = 0, + Lsr = 1, + Asr = 2, + Ror = 3 + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/Assembler.cs b/ARMeilleure/CodeGen/Arm64/Assembler.cs new file mode 100644 index 000000000..0ec0be7cb --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/Assembler.cs @@ -0,0 +1,1160 @@ +using ARMeilleure.IntermediateRepresentation; +using System; +using System.Diagnostics; +using System.IO; +using static ARMeilleure.IntermediateRepresentation.Operand; + +namespace ARMeilleure.CodeGen.Arm64 +{ + class Assembler + { + public const uint SfFlag = 1u << 31; + + private const int SpRegister = 31; + private const int ZrRegister = 31; + + private readonly Stream _stream; + + public Assembler(Stream stream) + { + _stream = stream; + } + + public void Add(Operand rd, Operand rn, Operand rm, ArmExtensionType extensionType, int shiftAmount = 0) + { + WriteInstructionAuto(0x0b200000u, rd, rn, rm, extensionType, shiftAmount); + } + + public void Add(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0, bool immForm = false) + { + WriteInstructionAuto(0x11000000u, 0x0b000000u, rd, rn, rm, shiftType, shiftAmount, immForm); + } + + public void And(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + WriteInstructionBitwiseAuto(0x12000000u, 0x0a000000u, rd, rn, rm, shiftType, shiftAmount); + } + + public void Ands(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + WriteInstructionBitwiseAuto(0x72000000u, 0x6a000000u, rd, rn, rm, shiftType, shiftAmount); + } + + public void Asr(Operand rd, Operand rn, Operand rm) + { + if (rm.Kind == OperandKind.Constant) + { + int shift = rm.AsInt32(); + int mask = rd.Type == OperandType.I64 ? 63 : 31; + shift &= mask; + Sbfm(rd, rn, shift, mask); + } + else + { + Asrv(rd, rn, rm); + } + } + + public void Asrv(Operand rd, Operand rn, Operand rm) + { + WriteInstructionBitwiseAuto(0x1ac02800u, rd, rn, rm); + } + + public void B(int imm) + { + WriteUInt32(0x14000000u | EncodeSImm26_2(imm)); + } + + public void B(ArmCondition condition, int imm) + { + WriteUInt32(0x54000000u | (uint)condition | (EncodeSImm19_2(imm) << 5)); + } + + public void Blr(Operand rn) + { + WriteUInt32(0xd63f0000u | (EncodeReg(rn) << 5)); + } + + public void Br(Operand rn) + { + WriteUInt32(0xd61f0000u | (EncodeReg(rn) << 5)); + } + + public void Brk() + { + WriteUInt32(0xd4200000u); + } + + public void Cbz(Operand rt, int imm) + { + WriteInstructionAuto(0x34000000u | (EncodeSImm19_2(imm) << 5), rt); + } + + public void Cbnz(Operand rt, int imm) + { + WriteInstructionAuto(0x35000000u | (EncodeSImm19_2(imm) << 5), rt); + } + + public void Clrex(int crm = 15) + { + WriteUInt32(0xd503305fu | (EncodeUImm4(crm) << 8)); + } + + public void Clz(Operand rd, Operand rn) + { + WriteInstructionAuto(0x5ac01000u, rd, rn); + } + + public void CmeqVector(Operand rd, Operand rn, Operand rm, int size, bool q = true) + { + Debug.Assert((uint)size < 4); + WriteSimdInstruction(0x2e208c00u | ((uint)size << 22), rd, rn, rm, q); + } + + public void Cmp(Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + Subs(Factory.Register(ZrRegister, RegisterType.Integer, rn.Type), rn, rm, shiftType, shiftAmount); + } + + public void Csel(Operand rd, Operand rn, Operand rm, ArmCondition condition) + { + WriteInstructionBitwiseAuto(0x1a800000u | ((uint)condition << 12), rd, rn, rm); + } + + public void Cset(Operand rd, ArmCondition condition) + { + var zr = Factory.Register(ZrRegister, RegisterType.Integer, rd.Type); + Csinc(rd, zr, zr, (ArmCondition)((int)condition ^ 1)); + } + + public void Csinc(Operand rd, Operand rn, Operand rm, ArmCondition condition) + { + WriteInstructionBitwiseAuto(0x1a800400u | ((uint)condition << 12), rd, rn, rm); + } + + public void Dmb(uint option) + { + WriteUInt32(0xd50330bfu | (option << 8)); + } + + public void DupScalar(Operand rd, Operand rn, int index, int size) + { + WriteInstruction(0x5e000400u | (EncodeIndexSizeImm5(index, size) << 16), rd, rn); + } + + public void Eor(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + WriteInstructionBitwiseAuto(0x52000000u, 0x4a000000u, rd, rn, rm, shiftType, shiftAmount); + } + + public void EorVector(Operand rd, Operand rn, Operand rm, bool q = true) + { + WriteSimdInstruction(0x2e201c00u, rd, rn, rm, q); + } + + public void Extr(Operand rd, Operand rn, Operand rm, int imms) + { + uint n = rd.Type == OperandType.I64 ? 1u << 22 : 0u; + WriteInstructionBitwiseAuto(0x13800000u | n | (EncodeUImm6(imms) << 10), rd, rn, rm); + } + + public void FaddScalar(Operand rd, Operand rn, Operand rm) + { + WriteFPInstructionAuto(0x1e202800u, rd, rn, rm); + } + + public void FcvtScalar(Operand rd, Operand rn) + { + uint instruction = 0x1e224000u | (rd.Type == OperandType.FP64 ? 1u << 15 : 1u << 22); + WriteUInt32(instruction | EncodeReg(rd) | (EncodeReg(rn) << 5)); + } + + public void FdivScalar(Operand rd, Operand rn, Operand rm) + { + WriteFPInstructionAuto(0x1e201800u, rd, rn, rm); + } + + public void Fmov(Operand rd, Operand rn) + { + WriteFPInstructionAuto(0x1e204000u, rd, rn); + } + + public void Fmov(Operand rd, Operand rn, bool topHalf) + { + Debug.Assert(rd.Type.IsInteger() != rn.Type.IsInteger()); + Debug.Assert(rd.Type == OperandType.I64 || rn.Type == OperandType.I64 || !topHalf); + + uint opcode = rd.Type.IsInteger() ? 0b110u : 0b111u; + + uint rmode = topHalf ? 1u << 19 : 0u; + uint ftype = rd.Type == OperandType.FP64 || rn.Type == OperandType.FP64 ? 1u << 22 : 0u; + uint sf = rd.Type == OperandType.I64 || rn.Type == OperandType.I64 ? SfFlag : 0u; + + WriteUInt32(0x1e260000u | (opcode << 16) | rmode | ftype | sf | EncodeReg(rd) | (EncodeReg(rn) << 5)); + } + + public void FmulScalar(Operand rd, Operand rn, Operand rm) + { + WriteFPInstructionAuto(0x1e200800u, rd, rn, rm); + } + + public void FnegScalar(Operand rd, Operand rn) + { + WriteFPInstructionAuto(0x1e214000u, rd, rn); + } + + public void FsubScalar(Operand rd, Operand rn, Operand rm) + { + WriteFPInstructionAuto(0x1e203800u, rd, rn, rm); + } + + public void Ins(Operand rd, Operand rn, int index, int size) + { + WriteInstruction(0x4e001c00u | (EncodeIndexSizeImm5(index, size) << 16), rd, rn); + } + + public void Ins(Operand rd, Operand rn, int srcIndex, int dstIndex, int size) + { + uint imm4 = (uint)srcIndex << size; + Debug.Assert((uint)srcIndex < (16u >> size)); + WriteInstruction(0x6e000400u | (imm4 << 11) | (EncodeIndexSizeImm5(dstIndex, size) << 16), rd, rn); + } + + public void Ldaxp(Operand rt, Operand rt2, Operand rn) + { + WriteInstruction(0x887f8000u | ((rt.Type == OperandType.I64 ? 3u : 2u) << 30), rt, rn, rt2); + } + + public void Ldaxr(Operand rt, Operand rn) + { + WriteInstruction(0x085ffc00u | ((rt.Type == OperandType.I64 ? 3u : 2u) << 30), rt, rn); + } + + public void Ldaxrb(Operand rt, Operand rn) + { + WriteInstruction(0x085ffc00u, rt, rn); + } + + public void Ldaxrh(Operand rt, Operand rn) + { + WriteInstruction(0x085ffc00u | (1u << 30), rt, rn); + } + + public void LdpRiPost(Operand rt, Operand rt2, Operand rn, int imm) + { + uint instruction = GetLdpStpInstruction(0x28c00000u, 0x2cc00000u, imm, rt.Type); + WriteInstruction(instruction, rt, rn, rt2); + } + + public void LdpRiPre(Operand rt, Operand rt2, Operand rn, int imm) + { + uint instruction = GetLdpStpInstruction(0x29c00000u, 0x2dc00000u, imm, rt.Type); + WriteInstruction(instruction, rt, rn, rt2); + } + + public void LdpRiUn(Operand rt, Operand rt2, Operand rn, int imm) + { + uint instruction = GetLdpStpInstruction(0x29400000u, 0x2d400000u, imm, rt.Type); + WriteInstruction(instruction, rt, rn, rt2); + } + + public void Ldr(Operand rt, Operand rn) + { + if (rn.Kind == OperandKind.Memory) + { + MemoryOperand memOp = rn.GetMemory(); + + if (memOp.Index != default) + { + Debug.Assert(memOp.Displacement == 0); + Debug.Assert(memOp.Scale == Multiplier.x1 || (int)memOp.Scale == GetScaleForType(rt.Type)); + LdrRr(rt, memOp.BaseAddress, memOp.Index, ArmExtensionType.Uxtx, memOp.Scale != Multiplier.x1); + } + else + { + LdrRiUn(rt, memOp.BaseAddress, memOp.Displacement); + } + } + else + { + LdrRiUn(rt, rn, 0); + } + } + + public void LdrLit(Operand rt, int offset) + { + uint instruction = 0x18000000u | (EncodeSImm19_2(offset) << 5); + + if (rt.Type == OperandType.I64) + { + instruction |= 1u << 30; + } + + WriteInstruction(instruction, rt); + } + + public void LdrRiPost(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb8400400u, 0x3c400400u, rt.Type) | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void LdrRiPre(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb8400c00u, 0x3c400c00u, rt.Type) | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void LdrRiUn(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb9400000u, 0x3d400000u, rt.Type) | (EncodeUImm12(imm, rt.Type) << 10); + WriteInstruction(instruction, rt, rn); + } + + public void LdrRr(Operand rt, Operand rn, Operand rm, ArmExtensionType extensionType, bool shift) + { + uint instruction = GetLdrStrInstruction(0xb8600800u, 0x3ce00800u, rt.Type); + WriteInstructionLdrStrAuto(instruction, rt, rn, rm, extensionType, shift); + } + + public void LdrbRiPost(Operand rt, Operand rn, int imm) + { + uint instruction = 0x38400400u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void LdrbRiPre(Operand rt, Operand rn, int imm) + { + uint instruction = 0x38400c00u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void LdrbRiUn(Operand rt, Operand rn, int imm) + { + uint instruction = 0x39400000u | (EncodeUImm12(imm, 0) << 10); + WriteInstruction(instruction, rt, rn); + } + + public void LdrhRiPost(Operand rt, Operand rn, int imm) + { + uint instruction = 0x78400400u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void LdrhRiPre(Operand rt, Operand rn, int imm) + { + uint instruction = 0x78400c00u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void LdrhRiUn(Operand rt, Operand rn, int imm) + { + uint instruction = 0x79400000u | (EncodeUImm12(imm, 1) << 10); + WriteInstruction(instruction, rt, rn); + } + + public void Ldur(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb8400000u, 0x3c400000u, rt.Type) | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void Lsl(Operand rd, Operand rn, Operand rm) + { + if (rm.Kind == OperandKind.Constant) + { + int shift = rm.AsInt32(); + int mask = rd.Type == OperandType.I64 ? 63 : 31; + shift &= mask; + Ubfm(rd, rn, -shift & mask, mask - shift); + } + else + { + Lslv(rd, rn, rm); + } + } + + public void Lslv(Operand rd, Operand rn, Operand rm) + { + WriteInstructionBitwiseAuto(0x1ac02000u, rd, rn, rm); + } + + public void Lsr(Operand rd, Operand rn, Operand rm) + { + if (rm.Kind == OperandKind.Constant) + { + int shift = rm.AsInt32(); + int mask = rd.Type == OperandType.I64 ? 63 : 31; + shift &= mask; + Ubfm(rd, rn, shift, mask); + } + else + { + Lsrv(rd, rn, rm); + } + } + + public void Lsrv(Operand rd, Operand rn, Operand rm) + { + WriteInstructionBitwiseAuto(0x1ac02400u, rd, rn, rm); + } + + public void Madd(Operand rd, Operand rn, Operand rm, Operand ra) + { + WriteInstructionAuto(0x1b000000u, rd, rn, rm, ra); + } + + public void Mul(Operand rd, Operand rn, Operand rm) + { + Madd(rd, rn, rm, Factory.Register(ZrRegister, RegisterType.Integer, rd.Type)); + } + + public void Mov(Operand rd, Operand rn) + { + if (rd.Type.IsInteger()) + { + Orr(rd, Factory.Register(ZrRegister, RegisterType.Integer, rd.Type), rn); + } + else + { + OrrVector(rd, rn, rn); + } + } + + public void MovSp(Operand rd, Operand rn) + { + if (rd.GetRegister().Index == SpRegister || + rn.GetRegister().Index == SpRegister) + { + Add(rd, rn, Factory.Const(rd.Type, 0), immForm: true); + } + else + { + Mov(rd, rn); + } + } + + public void Mov(Operand rd, int imm) + { + Movz(rd, imm, 0); + } + + public void Movz(Operand rd, int imm, int hw) + { + Debug.Assert((hw & (rd.Type == OperandType.I64 ? 3 : 1)) == hw); + WriteInstructionAuto(0x52800000u | (EncodeUImm16(imm) << 5) | ((uint)hw << 21), rd); + } + + public void Movk(Operand rd, int imm, int hw) + { + Debug.Assert((hw & (rd.Type == OperandType.I64 ? 3 : 1)) == hw); + WriteInstructionAuto(0x72800000u | (EncodeUImm16(imm) << 5) | ((uint)hw << 21), rd); + } + + public void Mrs(Operand rt, uint o0, uint op1, uint crn, uint crm, uint op2) + { + uint instruction = 0xd5300000u; + + instruction |= (op2 & 7) << 5; + instruction |= (crm & 15) << 8; + instruction |= (crn & 15) << 12; + instruction |= (op1 & 7) << 16; + instruction |= (o0 & 1) << 19; + + WriteInstruction(instruction, rt); + } + + public void Mvn(Operand rd, Operand rn, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + Orn(rd, Factory.Register(ZrRegister, RegisterType.Integer, rd.Type), rn, shiftType, shiftAmount); + } + + public void Neg(Operand rd, Operand rn, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + Sub(rd, Factory.Register(ZrRegister, RegisterType.Integer, rd.Type), rn, shiftType, shiftAmount); + } + + public void Orn(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + WriteInstructionBitwiseAuto(0x2a200000u, rd, rn, rm, shiftType, shiftAmount); + } + + public void Orr(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + WriteInstructionBitwiseAuto(0x32000000u, 0x2a000000u, rd, rn, rm, shiftType, shiftAmount); + } + + public void OrrVector(Operand rd, Operand rn, Operand rm, bool q = true) + { + WriteSimdInstruction(0x0ea01c00u, rd, rn, rm, q); + } + + public void Ret(Operand rn) + { + WriteUInt32(0xd65f0000u | (EncodeReg(rn) << 5)); + } + + public void Rev(Operand rd, Operand rn) + { + uint opc0 = rd.Type == OperandType.I64 ? 1u << 10 : 0u; + WriteInstructionAuto(0x5ac00800u | opc0, rd, rn); + } + + public void Ror(Operand rd, Operand rn, Operand rm) + { + if (rm.Kind == OperandKind.Constant) + { + int shift = rm.AsInt32(); + int mask = rd.Type == OperandType.I64 ? 63 : 31; + shift &= mask; + Extr(rd, rn, rn, shift); + } + else + { + Rorv(rd, rn, rm); + } + } + + public void Rorv(Operand rd, Operand rn, Operand rm) + { + WriteInstructionBitwiseAuto(0x1ac02c00u, rd, rn, rm); + } + + public void Sbfm(Operand rd, Operand rn, int immr, int imms) + { + uint n = rd.Type == OperandType.I64 ? 1u << 22 : 0u; + WriteInstructionAuto(0x13000000u | n | (EncodeUImm6(imms) << 10) | (EncodeUImm6(immr) << 16), rd, rn); + } + + public void ScvtfScalar(Operand rd, Operand rn) + { + uint instruction = 0x1e220000u; + + if (rn.Type == OperandType.I64) + { + instruction |= SfFlag; + } + + WriteFPInstructionAuto(instruction, rd, rn); + } + + public void Sdiv(Operand rd, Operand rn, Operand rm) + { + WriteInstructionRm16Auto(0x1ac00c00u, rd, rn, rm); + } + + public void Smulh(Operand rd, Operand rn, Operand rm) + { + WriteInstructionRm16(0x9b407c00u, rd, rn, rm); + } + + public void Stlxp(Operand rt, Operand rt2, Operand rn, Operand rs) + { + WriteInstruction(0x88208000u | ((rt.Type == OperandType.I64 ? 3u : 2u) << 30), rt, rn, rs, rt2); + } + + public void Stlxr(Operand rt, Operand rn, Operand rs) + { + WriteInstructionRm16(0x0800fc00u | ((rt.Type == OperandType.I64 ? 3u : 2u) << 30), rt, rn, rs); + } + + public void Stlxrb(Operand rt, Operand rn, Operand rs) + { + WriteInstructionRm16(0x0800fc00u, rt, rn, rs); + } + + public void Stlxrh(Operand rt, Operand rn, Operand rs) + { + WriteInstructionRm16(0x0800fc00u | (1u << 30), rt, rn, rs); + } + + public void StpRiPost(Operand rt, Operand rt2, Operand rn, int imm) + { + uint instruction = GetLdpStpInstruction(0x28800000u, 0x2c800000u, imm, rt.Type); + WriteInstruction(instruction, rt, rn, rt2); + } + + public void StpRiPre(Operand rt, Operand rt2, Operand rn, int imm) + { + uint instruction = GetLdpStpInstruction(0x29800000u, 0x2d800000u, imm, rt.Type); + WriteInstruction(instruction, rt, rn, rt2); + } + + public void StpRiUn(Operand rt, Operand rt2, Operand rn, int imm) + { + uint instruction = GetLdpStpInstruction(0x29000000u, 0x2d000000u, imm, rt.Type); + WriteInstruction(instruction, rt, rn, rt2); + } + + public void Str(Operand rt, Operand rn) + { + if (rn.Kind == OperandKind.Memory) + { + MemoryOperand memOp = rn.GetMemory(); + + if (memOp.Index != default) + { + Debug.Assert(memOp.Displacement == 0); + Debug.Assert(memOp.Scale == Multiplier.x1 || (int)memOp.Scale == GetScaleForType(rt.Type)); + StrRr(rt, memOp.BaseAddress, memOp.Index, ArmExtensionType.Uxtx, memOp.Scale != Multiplier.x1); + } + else + { + StrRiUn(rt, memOp.BaseAddress, memOp.Displacement); + } + } + else + { + StrRiUn(rt, rn, 0); + } + } + + public void StrRiPost(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb8000400u, 0x3c000400u, rt.Type) | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void StrRiPre(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb8000c00u, 0x3c000c00u, rt.Type) | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void StrRiUn(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb9000000u, 0x3d000000u, rt.Type) | (EncodeUImm12(imm, rt.Type) << 10); + WriteInstruction(instruction, rt, rn); + } + + public void StrRr(Operand rt, Operand rn, Operand rm, ArmExtensionType extensionType, bool shift) + { + uint instruction = GetLdrStrInstruction(0xb8200800u, 0x3ca00800u, rt.Type); + WriteInstructionLdrStrAuto(instruction, rt, rn, rm, extensionType, shift); + } + + public void StrbRiPost(Operand rt, Operand rn, int imm) + { + uint instruction = 0x38000400u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void StrbRiPre(Operand rt, Operand rn, int imm) + { + uint instruction = 0x38000c00u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void StrbRiUn(Operand rt, Operand rn, int imm) + { + uint instruction = 0x39000000u | (EncodeUImm12(imm, 0) << 10); + WriteInstruction(instruction, rt, rn); + } + + public void StrhRiPost(Operand rt, Operand rn, int imm) + { + uint instruction = 0x78000400u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void StrhRiPre(Operand rt, Operand rn, int imm) + { + uint instruction = 0x78000c00u | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void StrhRiUn(Operand rt, Operand rn, int imm) + { + uint instruction = 0x79000000u | (EncodeUImm12(imm, 1) << 10); + WriteInstruction(instruction, rt, rn); + } + + public void Stur(Operand rt, Operand rn, int imm) + { + uint instruction = GetLdrStrInstruction(0xb8000000u, 0x3c000000u, rt.Type) | (EncodeSImm9(imm) << 12); + WriteInstruction(instruction, rt, rn); + } + + public void Sub(Operand rd, Operand rn, Operand rm, ArmExtensionType extensionType, int shiftAmount = 0) + { + WriteInstructionAuto(0x4b200000u, rd, rn, rm, extensionType, shiftAmount); + } + + public void Sub(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + WriteInstructionAuto(0x51000000u, 0x4b000000u, rd, rn, rm, shiftType, shiftAmount); + } + + public void Subs(Operand rd, Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + WriteInstructionAuto(0x71000000u, 0x6b000000u, rd, rn, rm, shiftType, shiftAmount); + } + + public void Sxtb(Operand rd, Operand rn) + { + Sbfm(rd, rn, 0, 7); + } + + public void Sxth(Operand rd, Operand rn) + { + Sbfm(rd, rn, 0, 15); + } + + public void Sxtw(Operand rd, Operand rn) + { + Sbfm(rd, rn, 0, 31); + } + + public void Tst(Operand rn, Operand rm, ArmShiftType shiftType = ArmShiftType.Lsl, int shiftAmount = 0) + { + Ands(Factory.Register(ZrRegister, RegisterType.Integer, rn.Type), rn, rm, shiftType, shiftAmount); + } + + public void Ubfm(Operand rd, Operand rn, int immr, int imms) + { + uint n = rd.Type == OperandType.I64 ? 1u << 22 : 0u; + WriteInstructionAuto(0x53000000u | n | (EncodeUImm6(imms) << 10) | (EncodeUImm6(immr) << 16), rd, rn); + } + + public void UcvtfScalar(Operand rd, Operand rn) + { + uint instruction = 0x1e230000u; + + if (rn.Type == OperandType.I64) + { + instruction |= SfFlag; + } + + WriteFPInstructionAuto(instruction, rd, rn); + } + + public void Udiv(Operand rd, Operand rn, Operand rm) + { + WriteInstructionRm16Auto(0x1ac00800u, rd, rn, rm); + } + + public void Umov(Operand rd, Operand rn, int index, int size) + { + uint q = size == 3 ? 1u << 30 : 0u; + WriteInstruction(0x0e003c00u | (EncodeIndexSizeImm5(index, size) << 16) | q, rd, rn); + } + + public void Umulh(Operand rd, Operand rn, Operand rm) + { + WriteInstructionRm16(0x9bc07c00u, rd, rn, rm); + } + + public void Uxtb(Operand rd, Operand rn) + { + Ubfm(rd, rn, 0, 7); + } + + public void Uxth(Operand rd, Operand rn) + { + Ubfm(rd, rn, 0, 15); + } + + private void WriteInstructionAuto( + uint instI, + uint instR, + Operand rd, + Operand rn, + Operand rm, + ArmShiftType shiftType = ArmShiftType.Lsl, + int shiftAmount = 0, + bool immForm = false) + { + if (rm.Kind == OperandKind.Constant && (rm.Value != 0 || immForm)) + { + Debug.Assert(shiftAmount == 0); + int imm = rm.AsInt32(); + Debug.Assert((uint)imm == rm.Value); + if (imm != 0 && (imm & 0xfff) == 0) + { + instI |= 1 << 22; // sh flag + imm >>= 12; + } + WriteInstructionAuto(instI | (EncodeUImm12(imm, 0) << 10), rd, rn); + } + else + { + instR |= EncodeUImm6(shiftAmount) << 10; + instR |= (uint)shiftType << 22; + + WriteInstructionRm16Auto(instR, rd, rn, rm); + } + } + + private void WriteInstructionAuto( + uint instruction, + Operand rd, + Operand rn, + Operand rm, + ArmExtensionType extensionType, + int shiftAmount = 0) + { + Debug.Assert((uint)shiftAmount <= 4); + + instruction |= (uint)shiftAmount << 10; + instruction |= (uint)extensionType << 13; + + WriteInstructionRm16Auto(instruction, rd, rn, rm); + } + + private void WriteInstructionBitwiseAuto( + uint instI, + uint instR, + Operand rd, + Operand rn, + Operand rm, + ArmShiftType shiftType = ArmShiftType.Lsl, + int shiftAmount = 0) + { + if (rm.Kind == OperandKind.Constant && rm.Value != 0) + { + Debug.Assert(shiftAmount == 0); + bool canEncode = CodeGenCommon.TryEncodeBitMask(rm, out int immN, out int immS, out int immR); + Debug.Assert(canEncode); + uint instruction = instI | ((uint)immS << 10) | ((uint)immR << 16) | ((uint)immN << 22); + + WriteInstructionAuto(instruction, rd, rn); + } + else + { + WriteInstructionBitwiseAuto(instR, rd, rn, rm, shiftType, shiftAmount); + } + } + + private void WriteInstructionBitwiseAuto( + uint instruction, + Operand rd, + Operand rn, + Operand rm, + ArmShiftType shiftType = ArmShiftType.Lsl, + int shiftAmount = 0) + { + if (rd.Type == OperandType.I64) + { + instruction |= SfFlag; + } + + instruction |= EncodeUImm6(shiftAmount) << 10; + instruction |= (uint)shiftType << 22; + + WriteInstructionRm16(instruction, rd, rn, rm); + } + + private void WriteInstructionLdrStrAuto( + uint instruction, + Operand rd, + Operand rn, + Operand rm, + ArmExtensionType extensionType, + bool shift) + { + if (shift) + { + instruction |= 1u << 12; + } + + instruction |= (uint)extensionType << 13; + + if (rd.Type == OperandType.I64) + { + instruction |= 1u << 30; + } + + WriteInstructionRm16(instruction, rd, rn, rm); + } + + private void WriteInstructionAuto(uint instruction, Operand rd) + { + if (rd.Type == OperandType.I64) + { + instruction |= SfFlag; + } + + WriteInstruction(instruction, rd); + } + + public void WriteInstructionAuto(uint instruction, Operand rd, Operand rn) + { + if (rd.Type == OperandType.I64) + { + instruction |= SfFlag; + } + + WriteInstruction(instruction, rd, rn); + } + + private void WriteInstructionAuto(uint instruction, Operand rd, Operand rn, Operand rm, Operand ra) + { + if (rd.Type == OperandType.I64) + { + instruction |= SfFlag; + } + + WriteInstruction(instruction, rd, rn, rm, ra); + } + + public void WriteInstruction(uint instruction, Operand rd) + { + WriteUInt32(instruction | EncodeReg(rd)); + } + + public void WriteInstruction(uint instruction, Operand rd, Operand rn) + { + WriteUInt32(instruction | EncodeReg(rd) | (EncodeReg(rn) << 5)); + } + + public void WriteInstruction(uint instruction, Operand rd, Operand rn, Operand rm) + { + WriteUInt32(instruction | EncodeReg(rd) | (EncodeReg(rn) << 5) | (EncodeReg(rm) << 10)); + } + + public void WriteInstruction(uint instruction, Operand rd, Operand rn, Operand rm, Operand ra) + { + WriteUInt32(instruction | EncodeReg(rd) | (EncodeReg(rn) << 5) | (EncodeReg(ra) << 10) | (EncodeReg(rm) << 16)); + } + + private void WriteFPInstructionAuto(uint instruction, Operand rd, Operand rn) + { + if (rd.Type == OperandType.FP64) + { + instruction |= 1u << 22; + } + + WriteUInt32(instruction | EncodeReg(rd) | (EncodeReg(rn) << 5)); + } + + private void WriteFPInstructionAuto(uint instruction, Operand rd, Operand rn, Operand rm) + { + if (rd.Type == OperandType.FP64) + { + instruction |= 1u << 22; + } + + WriteInstructionRm16(instruction, rd, rn, rm); + } + + private void WriteSimdInstruction(uint instruction, Operand rd, Operand rn, Operand rm, bool q = true) + { + if (q) + { + instruction |= 1u << 30; + } + + WriteInstructionRm16(instruction, rd, rn, rm); + } + + private void WriteInstructionRm16Auto(uint instruction, Operand rd, Operand rn, Operand rm) + { + if (rd.Type == OperandType.I64) + { + instruction |= SfFlag; + } + + WriteInstructionRm16(instruction, rd, rn, rm); + } + + public void WriteInstructionRm16(uint instruction, Operand rd, Operand rn, Operand rm) + { + WriteUInt32(instruction | EncodeReg(rd) | (EncodeReg(rn) << 5) | (EncodeReg(rm) << 16)); + } + + public void WriteInstructionRm16NoRet(uint instruction, Operand rn, Operand rm) + { + WriteUInt32(instruction | (EncodeReg(rn) << 5) | (EncodeReg(rm) << 16)); + } + + private static uint GetLdpStpInstruction(uint intInst, uint vecInst, int imm, OperandType type) + { + uint instruction; + int scale; + + if (type.IsInteger()) + { + instruction = intInst; + + if (type == OperandType.I64) + { + instruction |= SfFlag; + scale = 3; + } + else + { + scale = 2; + } + } + else + { + int opc = type switch + { + OperandType.FP32 => 0, + OperandType.FP64 => 1, + _ => 2 + }; + + instruction = vecInst | ((uint)opc << 30); + scale = 2 + opc; + } + + instruction |= (EncodeSImm7(imm, scale) << 15); + + return instruction; + } + + private static uint GetLdrStrInstruction(uint intInst, uint vecInst, OperandType type) + { + uint instruction; + + if (type.IsInteger()) + { + instruction = intInst; + + if (type == OperandType.I64) + { + instruction |= 1 << 30; + } + } + else + { + instruction = vecInst; + + if (type == OperandType.V128) + { + instruction |= 1u << 23; + } + else + { + instruction |= type == OperandType.FP32 ? 2u << 30 : 3u << 30; + } + } + + return instruction; + } + + private static uint EncodeIndexSizeImm5(int index, int size) + { + Debug.Assert((uint)size < 4); + Debug.Assert((uint)index < (16u >> size), $"Invalid index {index} and size {size} combination."); + return ((uint)index << (size + 1)) | (1u << size); + } + + private static uint EncodeSImm7(int value, int scale) + { + uint imm = (uint)(value >> scale) & 0x7f; + Debug.Assert(((int)imm << 25) >> (25 - scale) == value, $"Failed to encode constant 0x{value:X} with scale {scale}."); + return imm; + } + + private static uint EncodeSImm9(int value) + { + uint imm = (uint)value & 0x1ff; + Debug.Assert(((int)imm << 23) >> 23 == value, $"Failed to encode constant 0x{value:X}."); + return imm; + } + + private static uint EncodeSImm19_2(int value) + { + uint imm = (uint)(value >> 2) & 0x7ffff; + Debug.Assert(((int)imm << 13) >> 11 == value, $"Failed to encode constant 0x{value:X}."); + return imm; + } + + private static uint EncodeSImm26_2(int value) + { + uint imm = (uint)(value >> 2) & 0x3ffffff; + Debug.Assert(((int)imm << 6) >> 4 == value, $"Failed to encode constant 0x{value:X}."); + return imm; + } + + private static uint EncodeUImm4(int value) + { + uint imm = (uint)value & 0xf; + Debug.Assert((int)imm == value, $"Failed to encode constant 0x{value:X}."); + return imm; + } + + private static uint EncodeUImm6(int value) + { + uint imm = (uint)value & 0x3f; + Debug.Assert((int)imm == value, $"Failed to encode constant 0x{value:X}."); + return imm; + } + + private static uint EncodeUImm12(int value, OperandType type) + { + return EncodeUImm12(value, GetScaleForType(type)); + } + + private static uint EncodeUImm12(int value, int scale) + { + uint imm = (uint)(value >> scale) & 0xfff; + Debug.Assert((int)imm << scale == value, $"Failed to encode constant 0x{value:X} with scale {scale}."); + return imm; + } + + private static uint EncodeUImm16(int value) + { + uint imm = (uint)value & 0xffff; + Debug.Assert((int)imm == value, $"Failed to encode constant 0x{value:X}."); + return imm; + } + + private static uint EncodeReg(Operand reg) + { + if (reg.Kind == OperandKind.Constant && reg.Value == 0) + { + return ZrRegister; + } + + uint regIndex = (uint)reg.GetRegister().Index; + Debug.Assert(reg.Kind == OperandKind.Register); + Debug.Assert(regIndex < 32); + return regIndex; + } + + public static int GetScaleForType(OperandType type) + { + return type switch + { + OperandType.I32 => 2, + OperandType.I64 => 3, + OperandType.FP32 => 2, + OperandType.FP64 => 3, + OperandType.V128 => 4, + _ => throw new ArgumentException($"Invalid type {type}.") + }; + } + + private void WriteInt16(short value) + { + WriteUInt16((ushort)value); + } + + private void WriteInt32(int value) + { + WriteUInt32((uint)value); + } + + private void WriteByte(byte value) + { + _stream.WriteByte(value); + } + + private void WriteUInt16(ushort value) + { + _stream.WriteByte((byte)(value >> 0)); + _stream.WriteByte((byte)(value >> 8)); + } + + private void WriteUInt32(uint value) + { + _stream.WriteByte((byte)(value >> 0)); + _stream.WriteByte((byte)(value >> 8)); + _stream.WriteByte((byte)(value >> 16)); + _stream.WriteByte((byte)(value >> 24)); + } + } +} diff --git a/ARMeilleure/CodeGen/Arm64/CallingConvention.cs b/ARMeilleure/CodeGen/Arm64/CallingConvention.cs new file mode 100644 index 000000000..fda8d7867 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/CallingConvention.cs @@ -0,0 +1,96 @@ +using System; + +namespace ARMeilleure.CodeGen.Arm64 +{ + static class CallingConvention + { + private const int RegistersMask = unchecked((int)0xffffffff); + + // Some of those register have specific roles and can't be used as general purpose registers. + // X18 - Reserved for platform specific usage. + // X29 - Frame pointer. + // X30 - Return address. + // X31 - Not an actual register, in some cases maps to SP, and in others to ZR. + private const int ReservedRegsMask = (1 << CodeGenCommon.ReservedRegister) | (1 << 18) | (1 << 29) | (1 << 30) | (1 << 31); + + public static int GetIntAvailableRegisters() + { + return RegistersMask & ~ReservedRegsMask; + } + + public static int GetVecAvailableRegisters() + { + return RegistersMask; + } + + public static int GetIntCallerSavedRegisters() + { + return (GetIntCalleeSavedRegisters() ^ RegistersMask) & ~ReservedRegsMask; + } + + public static int GetFpCallerSavedRegisters() + { + return GetFpCalleeSavedRegisters() ^ RegistersMask; + } + + public static int GetVecCallerSavedRegisters() + { + return GetVecCalleeSavedRegisters() ^ RegistersMask; + } + + public static int GetIntCalleeSavedRegisters() + { + return 0x1ff80000; // X19 to X28 + } + + public static int GetFpCalleeSavedRegisters() + { + return 0xff00; // D8 to D15 + } + + public static int GetVecCalleeSavedRegisters() + { + return 0; + } + + public static int GetArgumentsOnRegsCount() + { + return 8; + } + + public static int GetIntArgumentRegister(int index) + { + if ((uint)index < (uint)GetArgumentsOnRegsCount()) + { + return index; + } + + throw new ArgumentOutOfRangeException(nameof(index)); + } + + public static int GetVecArgumentRegister(int index) + { + if ((uint)index < (uint)GetArgumentsOnRegsCount()) + { + return index; + } + + throw new ArgumentOutOfRangeException(nameof(index)); + } + + public static int GetIntReturnRegister() + { + return 0; + } + + public static int GetIntReturnRegisterHigh() + { + return 1; + } + + public static int GetVecReturnRegister() + { + return 0; + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs b/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs new file mode 100644 index 000000000..e67d2fdb7 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs @@ -0,0 +1,173 @@ +using ARMeilleure.IntermediateRepresentation; +using System; +using System.Numerics; + +namespace ARMeilleure.CodeGen.Arm64 +{ + static class CodeGenCommon + { + public const int TcAddressRegister = 8; + public const int ReservedRegister = 17; + + public static bool ConstFitsOnSImm7(int value, int scale) + { + return (((value >> scale) << 25) >> (25 - scale)) == value; + } + + public static bool ConstFitsOnSImm9(int value) + { + return ((value << 23) >> 23) == value; + } + + public static bool ConstFitsOnUImm12(int value) + { + return (value & 0xfff) == value; + } + + public static bool ConstFitsOnUImm12(int value, OperandType type) + { + int scale = Assembler.GetScaleForType(type); + return (((value >> scale) & 0xfff) << scale) == value; + } + + public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR) + { + ulong value = operand.Value; + + if (operand.Type == OperandType.I32) + { + value |= value << 32; + } + + return TryEncodeBitMask(value, out immN, out immS, out immR); + } + + public static bool TryEncodeBitMask(ulong value, out int immN, out int immS, out int immR) + { + // Some special values also can't be encoded: + // 0 can't be encoded because we need to subtract 1 from onesCount (which would became negative if 0). + // A value with all bits set can't be encoded because it is reserved according to the spec, because: + // Any value AND all ones will be equal itself, so it's effectively a no-op. + // Any value OR all ones will be equal all ones, so one can just use MOV. + // Any value XOR all ones will be equal its inverse, so one can just use MVN. + if (value == ulong.MaxValue) + { + immN = 0; + immS = 0; + immR = 0; + + return false; + } + + int bitLength = CountSequence(value); + + if ((value >> bitLength) != 0) + { + bitLength += CountSequence(value >> bitLength); + } + + int bitLengthLog2 = BitOperations.Log2((uint)bitLength); + int bitLengthPow2 = 1 << bitLengthLog2; + + if (bitLengthPow2 < bitLength) + { + bitLengthLog2++; + bitLengthPow2 <<= 1; + } + + int selectedESize = 64; + int repetitions = 1; + int onesCount = BitOperations.PopCount(value); + + if (bitLengthPow2 < 64 && (value >> bitLengthPow2) != 0) + { + for (int eSizeLog2 = bitLengthLog2; eSizeLog2 < 6; eSizeLog2++) + { + bool match = true; + int eSize = 1 << eSizeLog2; + ulong mask = (1UL << eSize) - 1; + ulong eValue = value & mask; + + for (int e = 1; e < 64 / eSize; e++) + { + if (((value >> (e * eSize)) & mask) != eValue) + { + match = false; + break; + } + } + + if (match) + { + selectedESize = eSize; + repetitions = 64 / eSize; + onesCount = BitOperations.PopCount(eValue); + break; + } + } + } + + // Find rotation. We have two cases, one where the highest bit is 0 + // and one where it is 1. + // If it's 1, we just need to count the number of 1 bits on the MSB to find the right rotation. + // If it's 0, we just need to count the number of 0 bits on the LSB to find the left rotation, + // then we can convert it to the right rotation shift by subtracting the value from the element size. + int rotation; + long vHigh = (long)(value << (64 - selectedESize)); + if (vHigh < 0) + { + rotation = BitOperations.LeadingZeroCount(~(ulong)vHigh); + } + else + { + rotation = (selectedESize - BitOperations.TrailingZeroCount(value)) & (selectedESize - 1); + } + + // Reconstruct value and see if it matches. If not, we can't encode. + ulong reconstructed = onesCount == 64 ? ulong.MaxValue : RotateRight((1UL << onesCount) - 1, rotation, selectedESize); + + for (int bit = 32; bit >= selectedESize; bit >>= 1) + { + reconstructed |= reconstructed << bit; + } + + if (reconstructed != value || onesCount == 0) + { + immN = 0; + immS = 0; + immR = 0; + + return false; + } + + immR = rotation; + + // immN indicates that there are no repetitions. + // The MSB of immS indicates the amount of repetitions, and the LSB the number of bits set. + if (repetitions == 1) + { + immN = 1; + immS = 0; + } + else + { + immN = 0; + immS = (0xf80 >> BitOperations.Log2((uint)repetitions)) & 0x3f; + } + + immS |= onesCount - 1; + + return true; + } + + private static int CountSequence(ulong value) + { + return BitOperations.TrailingZeroCount(value) + BitOperations.TrailingZeroCount(~value); + } + + private static ulong RotateRight(ulong bits, int shift, int size) + { + return (bits >> shift) | ((bits << (size - shift)) & (size == 64 ? ulong.MaxValue : (1UL << size) - 1)); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs new file mode 100644 index 000000000..1ddde0c19 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs @@ -0,0 +1,286 @@ +using ARMeilleure.CodeGen.Linking; +using ARMeilleure.CodeGen.RegisterAllocators; +using ARMeilleure.IntermediateRepresentation; +using System; +using System.Collections.Generic; +using System.IO; + +namespace ARMeilleure.CodeGen.Arm64 +{ + class CodeGenContext + { + private const int BccInstLength = 4; + private const int CbnzInstLength = 4; + private const int LdrLitInstLength = 4; + + private Stream _stream; + + public int StreamOffset => (int)_stream.Length; + + public AllocationResult AllocResult { get; } + + public Assembler Assembler { get; } + + public BasicBlock CurrBlock { get; private set; } + + public bool HasCall { get; } + + public int CallArgsRegionSize { get; } + public int FpLrSaveRegionSize { get; } + + private readonly Dictionary _visitedBlocks; + private readonly Dictionary> _pendingBranches; + + private struct ConstantPoolEntry + { + public readonly int Offset; + public readonly Symbol Symbol; + public readonly List<(Operand, int)> LdrOffsets; + + public ConstantPoolEntry(int offset, Symbol symbol) + { + Offset = offset; + Symbol = symbol; + LdrOffsets = new List<(Operand, int)>(); + } + } + + private readonly Dictionary _constantPool; + + private bool _constantPoolWritten; + private long _constantPoolOffset; + + private ArmCondition _jNearCondition; + private Operand _jNearValue; + + private long _jNearPosition; + + private readonly bool _relocatable; + + public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable) + { + _stream = new MemoryStream(); + + AllocResult = allocResult; + + Assembler = new Assembler(_stream); + + bool hasCall = maxCallArgs >= 0; + + HasCall = hasCall; + + if (maxCallArgs < 0) + { + maxCallArgs = 0; + } + + CallArgsRegionSize = maxCallArgs * 16; + FpLrSaveRegionSize = hasCall ? 16 : 0; + + _visitedBlocks = new Dictionary(); + _pendingBranches = new Dictionary>(); + _constantPool = new Dictionary(); + + _relocatable = relocatable; + } + + public void EnterBlock(BasicBlock block) + { + CurrBlock = block; + + long target = _stream.Position; + + if (_pendingBranches.TryGetValue(block, out var list)) + { + foreach (var tuple in list) + { + _stream.Seek(tuple.BranchPos, SeekOrigin.Begin); + WriteBranch(tuple.Condition, target); + } + + _stream.Seek(target, SeekOrigin.Begin); + _pendingBranches.Remove(block); + } + + _visitedBlocks.Add(block, target); + } + + public void JumpTo(BasicBlock target) + { + JumpTo(ArmCondition.Al, target); + } + + public void JumpTo(ArmCondition condition, BasicBlock target) + { + if (_visitedBlocks.TryGetValue(target, out long offset)) + { + WriteBranch(condition, offset); + } + else + { + if (!_pendingBranches.TryGetValue(target, out var list)) + { + list = new List<(ArmCondition, long)>(); + _pendingBranches.Add(target, list); + } + + list.Add((condition, _stream.Position)); + + _stream.Seek(BccInstLength, SeekOrigin.Current); + } + } + + private void WriteBranch(ArmCondition condition, long to) + { + int imm = checked((int)(to - _stream.Position)); + + if (condition != ArmCondition.Al) + { + Assembler.B(condition, imm); + } + else + { + Assembler.B(imm); + } + } + + public void JumpToNear(ArmCondition condition) + { + _jNearCondition = condition; + _jNearPosition = _stream.Position; + + _stream.Seek(BccInstLength, SeekOrigin.Current); + } + + public void JumpToNearIfNotZero(Operand value) + { + _jNearValue = value; + _jNearPosition = _stream.Position; + + _stream.Seek(CbnzInstLength, SeekOrigin.Current); + } + + public void JumpHere() + { + long currentPosition = _stream.Position; + long offset = currentPosition - _jNearPosition; + + _stream.Seek(_jNearPosition, SeekOrigin.Begin); + + if (_jNearValue != default) + { + Assembler.Cbnz(_jNearValue, checked((int)offset)); + _jNearValue = default; + } + else + { + Assembler.B(_jNearCondition, checked((int)offset)); + } + + _stream.Seek(currentPosition, SeekOrigin.Begin); + } + + public void ReserveRelocatableConstant(Operand rt, Symbol symbol, ulong value) + { + if (!_constantPool.TryGetValue(value, out ConstantPoolEntry cpe)) + { + cpe = new ConstantPoolEntry(_constantPool.Count * sizeof(ulong), symbol); + _constantPool.Add(value, cpe); + } + + cpe.LdrOffsets.Add((rt, (int)_stream.Position)); + _stream.Seek(LdrLitInstLength, SeekOrigin.Current); + } + + private long WriteConstantPool() + { + if (_constantPoolWritten) + { + return _constantPoolOffset; + } + + long constantPoolBaseOffset = _stream.Position; + + foreach (ulong value in _constantPool.Keys) + { + WriteUInt64(value); + } + + foreach (ConstantPoolEntry cpe in _constantPool.Values) + { + foreach ((Operand rt, int ldrOffset) in cpe.LdrOffsets) + { + _stream.Seek(ldrOffset, SeekOrigin.Begin); + + int absoluteOffset = checked((int)(constantPoolBaseOffset + cpe.Offset)); + int pcRelativeOffset = absoluteOffset - ldrOffset; + + Assembler.LdrLit(rt, pcRelativeOffset); + } + } + + _stream.Seek(constantPoolBaseOffset + _constantPool.Count * sizeof(ulong), SeekOrigin.Begin); + + _constantPoolOffset = constantPoolBaseOffset; + _constantPoolWritten = true; + + return constantPoolBaseOffset; + } + + public (byte[], RelocInfo) GetCode() + { + long constantPoolBaseOffset = WriteConstantPool(); + + byte[] code = new byte[_stream.Length]; + + long originalPosition = _stream.Position; + + _stream.Seek(0, SeekOrigin.Begin); + _stream.Read(code, 0, code.Length); + _stream.Seek(originalPosition, SeekOrigin.Begin); + + RelocInfo relocInfo; + + if (_relocatable) + { + RelocEntry[] relocs = new RelocEntry[_constantPool.Count]; + + int index = 0; + + foreach (ConstantPoolEntry cpe in _constantPool.Values) + { + if (cpe.Symbol.Type != SymbolType.None) + { + int absoluteOffset = checked((int)(constantPoolBaseOffset + cpe.Offset)); + relocs[index++] = new RelocEntry(absoluteOffset, cpe.Symbol); + } + } + + if (index != relocs.Length) + { + Array.Resize(ref relocs, index); + } + + relocInfo = new RelocInfo(relocs); + } + else + { + relocInfo = new RelocInfo(new RelocEntry[0]); + } + + return (code, relocInfo); + } + + private void WriteUInt64(ulong value) + { + _stream.WriteByte((byte)(value >> 0)); + _stream.WriteByte((byte)(value >> 8)); + _stream.WriteByte((byte)(value >> 16)); + _stream.WriteByte((byte)(value >> 24)); + _stream.WriteByte((byte)(value >> 32)); + _stream.WriteByte((byte)(value >> 40)); + _stream.WriteByte((byte)(value >> 48)); + _stream.WriteByte((byte)(value >> 56)); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs b/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs new file mode 100644 index 000000000..704aa45ac --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs @@ -0,0 +1,1576 @@ +using ARMeilleure.CodeGen.Linking; +using ARMeilleure.CodeGen.Optimizations; +using ARMeilleure.CodeGen.RegisterAllocators; +using ARMeilleure.CodeGen.Unwinding; +using ARMeilleure.Common; +using ARMeilleure.Diagnostics; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Numerics; + +using static ARMeilleure.IntermediateRepresentation.Operand; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; + +namespace ARMeilleure.CodeGen.Arm64 +{ + static class CodeGenerator + { + private const int DWordScale = 3; + + private const int RegistersCount = 32; + + private const int FpRegister = 29; + private const int LrRegister = 30; + private const int SpRegister = 31; + private const int ZrRegister = 31; + + private enum AccessSize + { + Byte, + Hword, + Auto + } + + private static Action[] _instTable; + + static CodeGenerator() + { + _instTable = new Action[EnumUtils.GetCount(typeof(Instruction))]; + + Add(Instruction.Add, GenerateAdd); + Add(Instruction.BitwiseAnd, GenerateBitwiseAnd); + Add(Instruction.BitwiseExclusiveOr, GenerateBitwiseExclusiveOr); + Add(Instruction.BitwiseNot, GenerateBitwiseNot); + Add(Instruction.BitwiseOr, GenerateBitwiseOr); + Add(Instruction.BranchIf, GenerateBranchIf); + Add(Instruction.ByteSwap, GenerateByteSwap); + Add(Instruction.Call, GenerateCall); + //Add(Instruction.Clobber, GenerateClobber); + Add(Instruction.Compare, GenerateCompare); + Add(Instruction.CompareAndSwap, GenerateCompareAndSwap); + Add(Instruction.CompareAndSwap16, GenerateCompareAndSwap16); + Add(Instruction.CompareAndSwap8, GenerateCompareAndSwap8); + Add(Instruction.ConditionalSelect, GenerateConditionalSelect); + Add(Instruction.ConvertI64ToI32, GenerateConvertI64ToI32); + Add(Instruction.ConvertToFP, GenerateConvertToFP); + Add(Instruction.ConvertToFPUI, GenerateConvertToFPUI); + Add(Instruction.Copy, GenerateCopy); + Add(Instruction.CountLeadingZeros, GenerateCountLeadingZeros); + Add(Instruction.Divide, GenerateDivide); + Add(Instruction.DivideUI, GenerateDivideUI); + Add(Instruction.Fill, GenerateFill); + Add(Instruction.Load, GenerateLoad); + Add(Instruction.Load16, GenerateLoad16); + Add(Instruction.Load8, GenerateLoad8); + Add(Instruction.MemoryBarrier, GenerateMemoryBarrier); + Add(Instruction.Multiply, GenerateMultiply); + Add(Instruction.Multiply64HighSI, GenerateMultiply64HighSI); + Add(Instruction.Multiply64HighUI, GenerateMultiply64HighUI); + Add(Instruction.Negate, GenerateNegate); + Add(Instruction.Return, GenerateReturn); + Add(Instruction.RotateRight, GenerateRotateRight); + Add(Instruction.ShiftLeft, GenerateShiftLeft); + Add(Instruction.ShiftRightSI, GenerateShiftRightSI); + Add(Instruction.ShiftRightUI, GenerateShiftRightUI); + Add(Instruction.SignExtend16, GenerateSignExtend16); + Add(Instruction.SignExtend32, GenerateSignExtend32); + Add(Instruction.SignExtend8, GenerateSignExtend8); + Add(Instruction.Spill, GenerateSpill); + Add(Instruction.SpillArg, GenerateSpillArg); + Add(Instruction.StackAlloc, GenerateStackAlloc); + Add(Instruction.Store, GenerateStore); + Add(Instruction.Store16, GenerateStore16); + Add(Instruction.Store8, GenerateStore8); + Add(Instruction.Subtract, GenerateSubtract); + Add(Instruction.Tailcall, GenerateTailcall); + Add(Instruction.VectorCreateScalar, GenerateVectorCreateScalar); + Add(Instruction.VectorExtract, GenerateVectorExtract); + Add(Instruction.VectorExtract16, GenerateVectorExtract16); + Add(Instruction.VectorExtract8, GenerateVectorExtract8); + Add(Instruction.VectorInsert, GenerateVectorInsert); + Add(Instruction.VectorInsert16, GenerateVectorInsert16); + Add(Instruction.VectorInsert8, GenerateVectorInsert8); + Add(Instruction.VectorOne, GenerateVectorOne); + Add(Instruction.VectorZero, GenerateVectorZero); + Add(Instruction.VectorZeroUpper64, GenerateVectorZeroUpper64); + Add(Instruction.VectorZeroUpper96, GenerateVectorZeroUpper96); + Add(Instruction.ZeroExtend16, GenerateZeroExtend16); + Add(Instruction.ZeroExtend32, GenerateZeroExtend32); + Add(Instruction.ZeroExtend8, GenerateZeroExtend8); + + static void Add(Instruction inst, Action func) + { + _instTable[(int)inst] = func; + } + } + + public static CompiledFunction Generate(CompilerContext cctx) + { + ControlFlowGraph cfg = cctx.Cfg; + + Logger.StartPass(PassName.Optimization); + + if (cctx.Options.HasFlag(CompilerOptions.Optimize)) + { + if (cctx.Options.HasFlag(CompilerOptions.SsaForm)) + { + Optimizer.RunPass(cfg); + } + + BlockPlacement.RunPass(cfg); + } + + Arm64Optimizer.RunPass(cfg); + + Logger.EndPass(PassName.Optimization, cfg); + + Logger.StartPass(PassName.PreAllocation); + + StackAllocator stackAlloc = new(); + + PreAllocator.RunPass(cctx, stackAlloc, out int maxCallArgs); + + Logger.EndPass(PassName.PreAllocation, cfg); + + Logger.StartPass(PassName.RegisterAllocation); + + if (cctx.Options.HasFlag(CompilerOptions.SsaForm)) + { + Ssa.Deconstruct(cfg); + } + + IRegisterAllocator regAlloc; + + if (cctx.Options.HasFlag(CompilerOptions.Lsra)) + { + regAlloc = new LinearScanAllocator(); + } + else + { + regAlloc = new HybridAllocator(); + } + + RegisterMasks regMasks = new( + CallingConvention.GetIntAvailableRegisters(), + CallingConvention.GetVecAvailableRegisters(), + CallingConvention.GetIntCallerSavedRegisters(), + CallingConvention.GetVecCallerSavedRegisters(), + CallingConvention.GetIntCalleeSavedRegisters(), + CallingConvention.GetVecCalleeSavedRegisters(), + RegistersCount); + + AllocationResult allocResult = regAlloc.RunPass(cfg, stackAlloc, regMasks); + + Logger.EndPass(PassName.RegisterAllocation, cfg); + + Logger.StartPass(PassName.CodeGeneration); + + //Console.Error.WriteLine(IRDumper.GetDump(cfg)); + + bool relocatable = (cctx.Options & CompilerOptions.Relocatable) != 0; + + CodeGenContext context = new(allocResult, maxCallArgs, cfg.Blocks.Count, relocatable); + + UnwindInfo unwindInfo = WritePrologue(context); + + for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) + { + context.EnterBlock(block); + + for (Operation node = block.Operations.First; node != default;) + { + node = GenerateOperation(context, node); + } + + if (block.SuccessorsCount == 0) + { + // The only blocks which can have 0 successors are exit blocks. + Operation last = block.Operations.Last; + + Debug.Assert(last.Instruction == Instruction.Tailcall || + last.Instruction == Instruction.Return); + } + else + { + BasicBlock succ = block.GetSuccessor(0); + + if (succ != block.ListNext) + { + context.JumpTo(succ); + } + } + } + + (byte[] code, RelocInfo relocInfo) = context.GetCode(); + + Logger.EndPass(PassName.CodeGeneration); + + return new CompiledFunction(code, unwindInfo, relocInfo); + } + + private static Operation GenerateOperation(CodeGenContext context, Operation operation) + { + if (operation.Instruction == Instruction.Extended) + { + CodeGeneratorIntrinsic.GenerateOperation(context, operation); + } + else + { + if (IsLoadOrStore(operation) && + operation.ListNext != default && + operation.ListNext.Instruction == operation.Instruction && + TryPairMemoryOp(context, operation, operation.ListNext)) + { + // Skip next operation if we managed to pair them. + return operation.ListNext.ListNext; + } + + Action func = _instTable[(int)operation.Instruction]; + + if (func != null) + { + func(context, operation); + } + else + { + throw new ArgumentException($"Invalid instruction \"{operation.Instruction}\"."); + } + } + + return operation.ListNext; + } + + private static void GenerateAdd(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + // ValidateBinOp(dest, src1, src2); + + if (dest.Type.IsInteger()) + { + context.Assembler.Add(dest, src1, src2); + } + else + { + context.Assembler.FaddScalar(dest, src1, src2); + } + } + + private static void GenerateBitwiseAnd(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + ValidateBinOp(dest, src1, src2); + + Debug.Assert(dest.Type.IsInteger()); + + context.Assembler.And(dest, src1, src2); + } + + private static void GenerateBitwiseExclusiveOr(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + ValidateBinOp(dest, src1, src2); + + if (dest.Type.IsInteger()) + { + context.Assembler.Eor(dest, src1, src2); + } + else + { + context.Assembler.EorVector(dest, src1, src2); + } + } + + private static void GenerateBitwiseNot(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + ValidateUnOp(dest, source); + + Debug.Assert(dest.Type.IsInteger()); + + context.Assembler.Mvn(dest, source); + } + + private static void GenerateBitwiseOr(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + ValidateBinOp(dest, src1, src2); + + Debug.Assert(dest.Type.IsInteger()); + + context.Assembler.Orr(dest, src1, src2); + } + + private static void GenerateBranchIf(CodeGenContext context, Operation operation) + { + Operand comp = operation.GetSource(2); + + Debug.Assert(comp.Kind == OperandKind.Constant); + + var cond = ((Comparison)comp.AsInt32()).ToArmCondition(); + + GenerateCompareCommon(context, operation); + + context.JumpTo(cond, context.CurrBlock.GetSuccessor(1)); + } + + private static void GenerateByteSwap(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + ValidateUnOp(dest, source); + + Debug.Assert(dest.Type.IsInteger()); + + context.Assembler.Rev(dest, source); + } + + private static void GenerateCall(CodeGenContext context, Operation operation) + { + context.Assembler.Blr(operation.GetSource(0)); + } + + private static void GenerateCompare(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand comp = operation.GetSource(2); + + Debug.Assert(dest.Type == OperandType.I32); + Debug.Assert(comp.Kind == OperandKind.Constant); + + var cond = ((Comparison)comp.AsInt32()).ToArmCondition(); + + GenerateCompareCommon(context, operation); + + context.Assembler.Cset(dest, cond); + } + + private static void GenerateCompareAndSwap(CodeGenContext context, Operation operation) + { + if (operation.SourcesCount == 5) // CompareAndSwap128 has 5 sources, compared to CompareAndSwap64/32's 3. + { + Operand actualLow = operation.GetDestination(0); + Operand actualHigh = operation.GetDestination(1); + Operand temp0 = operation.GetDestination(2); + Operand temp1 = operation.GetDestination(3); + Operand address = operation.GetSource(0); + Operand expectedLow = operation.GetSource(1); + Operand expectedHigh = operation.GetSource(2); + Operand desiredLow = operation.GetSource(3); + Operand desiredHigh = operation.GetSource(4); + + GenerateAtomicDcas( + context, + address, + expectedLow, + expectedHigh, + desiredLow, + desiredHigh, + actualLow, + actualHigh, + temp0, + temp1); + } + else + { + Operand actual = operation.GetDestination(0); + Operand result = operation.GetDestination(1); + Operand address = operation.GetSource(0); + Operand expected = operation.GetSource(1); + Operand desired = operation.GetSource(2); + + GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Auto); + } + } + + private static void GenerateCompareAndSwap16(CodeGenContext context, Operation operation) + { + Operand actual = operation.GetDestination(0); + Operand result = operation.GetDestination(1); + Operand address = operation.GetSource(0); + Operand expected = operation.GetSource(1); + Operand desired = operation.GetSource(2); + + GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Hword); + } + + private static void GenerateCompareAndSwap8(CodeGenContext context, Operation operation) + { + Operand actual = operation.GetDestination(0); + Operand result = operation.GetDestination(1); + Operand address = operation.GetSource(0); + Operand expected = operation.GetSource(1); + Operand desired = operation.GetSource(2); + + GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Byte); + } + + private static void GenerateCompareCommon(CodeGenContext context, Operation operation) + { + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + EnsureSameType(src1, src2); + + Debug.Assert(src1.Type.IsInteger()); + + context.Assembler.Cmp(src1, src2); + } + + private static void GenerateConditionalSelect(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + Operand src3 = operation.GetSource(2); + + EnsureSameType(dest, src2, src3); + + Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(src1.Type == OperandType.I32); + + context.Assembler.Cmp (src1, Const(src1.Type, 0)); + context.Assembler.Csel(dest, src2, src3, ArmCondition.Ne); + } + + private static void GenerateConvertI64ToI32(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type == OperandType.I32 && source.Type == OperandType.I64); + + context.Assembler.Mov(dest, Register(source, OperandType.I32)); + } + + private static void GenerateConvertToFP(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64); + Debug.Assert(dest.Type != source.Type); + Debug.Assert(source.Type != OperandType.V128); + + if (source.Type.IsInteger()) + { + context.Assembler.ScvtfScalar(dest, source); + } + else + { + context.Assembler.FcvtScalar(dest, source); + } + } + + private static void GenerateConvertToFPUI(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64); + Debug.Assert(dest.Type != source.Type); + Debug.Assert(source.Type.IsInteger()); + + context.Assembler.UcvtfScalar(dest, source); + } + + private static void GenerateCopy(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + EnsureSameType(dest, source); + + Debug.Assert(dest.Type.IsInteger() || source.Kind != OperandKind.Constant); + + // Moves to the same register are useless. + if (dest.Kind == source.Kind && dest.Value == source.Value) + { + return; + } + + if (dest.Kind == OperandKind.Register && source.Kind == OperandKind.Constant) + { + if (source.Relocatable) + { + context.ReserveRelocatableConstant(dest, source.Symbol, source.Value); + } + else + { + GenerateConstantCopy(context, dest, source.Value); + } + } + else + { + context.Assembler.Mov(dest, source); + } + } + + private static void GenerateCountLeadingZeros(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + EnsureSameType(dest, source); + + Debug.Assert(dest.Type.IsInteger()); + + context.Assembler.Clz(dest, source); + } + + private static void GenerateDivide(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand dividend = operation.GetSource(0); + Operand divisor = operation.GetSource(1); + + ValidateBinOp(dest, dividend, divisor); + + if (dest.Type.IsInteger()) + { + context.Assembler.Sdiv(dest, dividend, divisor); + } + else + { + context.Assembler.FdivScalar(dest, dividend, divisor); + } + } + + private static void GenerateDivideUI(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand dividend = operation.GetSource(0); + Operand divisor = operation.GetSource(1); + + ValidateBinOp(dest, dividend, divisor); + + context.Assembler.Udiv(dest, dividend, divisor); + } + + private static void GenerateLoad(CodeGenContext context, Operation operation) + { + Operand value = operation.Destination; + Operand address = operation.GetSource(0); + + context.Assembler.Ldr(value, address); + } + + private static void GenerateLoad16(CodeGenContext context, Operation operation) + { + Operand value = operation.Destination; + Operand address = operation.GetSource(0); + + Debug.Assert(value.Type.IsInteger()); + + context.Assembler.LdrhRiUn(value, address, 0); + } + + private static void GenerateLoad8(CodeGenContext context, Operation operation) + { + Operand value = operation.Destination; + Operand address = operation.GetSource(0); + + Debug.Assert(value.Type.IsInteger()); + + context.Assembler.LdrbRiUn(value, address, 0); + } + + private static void GenerateMemoryBarrier(CodeGenContext context, Operation operation) + { + context.Assembler.Dmb(0xf); + } + + private static void GenerateMultiply(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + EnsureSameType(dest, src1, src2); + + if (dest.Type.IsInteger()) + { + context.Assembler.Mul(dest, src1, src2); + } + else + { + context.Assembler.FmulScalar(dest, src1, src2); + } + } + + private static void GenerateMultiply64HighSI(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + EnsureSameType(dest, src1, src2); + + Debug.Assert(dest.Type == OperandType.I64); + + context.Assembler.Smulh(dest, src1, src2); + } + + private static void GenerateMultiply64HighUI(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + EnsureSameType(dest, src1, src2); + + Debug.Assert(dest.Type == OperandType.I64); + + context.Assembler.Umulh(dest, src1, src2); + } + + private static void GenerateNegate(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + ValidateUnOp(dest, source); + + if (dest.Type.IsInteger()) + { + context.Assembler.Neg(dest, source); + } + else + { + context.Assembler.FnegScalar(dest, source); + } + } + + private static void GenerateLoad(CodeGenContext context, Operand value, Operand address, int offset) + { + if (CodeGenCommon.ConstFitsOnUImm12(offset, value.Type)) + { + context.Assembler.LdrRiUn(value, address, offset); + } + else if (CodeGenCommon.ConstFitsOnSImm9(offset)) + { + context.Assembler.Ldur(value, address, offset); + } + else + { + Operand tempAddress = Register(CodeGenCommon.ReservedRegister); + GenerateConstantCopy(context, tempAddress, (ulong)offset); + context.Assembler.Add(tempAddress, address, tempAddress, ArmExtensionType.Uxtx); // Address might be SP and must be the first input. + context.Assembler.LdrRiUn(value, tempAddress, 0); + } + } + + private static void GenerateReturn(CodeGenContext context, Operation operation) + { + WriteEpilogue(context); + + context.Assembler.Ret(Register(LrRegister)); + } + + private static void GenerateRotateRight(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + ValidateShift(dest, src1, src2); + + context.Assembler.Ror(dest, src1, src2); + } + + private static void GenerateShiftLeft(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + ValidateShift(dest, src1, src2); + + context.Assembler.Lsl(dest, src1, src2); + } + + private static void GenerateShiftRightSI(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + ValidateShift(dest, src1, src2); + + context.Assembler.Asr(dest, src1, src2); + } + + private static void GenerateShiftRightUI(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + ValidateShift(dest, src1, src2); + + context.Assembler.Lsr(dest, src1, src2); + } + + private static void GenerateSignExtend16(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + + context.Assembler.Sxth(dest, source); + } + + private static void GenerateSignExtend32(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + + context.Assembler.Sxtw(dest, source); + } + + private static void GenerateSignExtend8(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + + context.Assembler.Sxtb(dest, source); + } + + private static void GenerateFill(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand offset = operation.GetSource(0); + + Debug.Assert(offset.Kind == OperandKind.Constant); + + int offs = offset.AsInt32() + context.CallArgsRegionSize + context.FpLrSaveRegionSize; + + GenerateLoad(context, dest, Register(SpRegister), offs); + } + + private static void GenerateStore(CodeGenContext context, Operand value, Operand address, int offset) + { + if (CodeGenCommon.ConstFitsOnUImm12(offset, value.Type)) + { + context.Assembler.StrRiUn(value, address, offset); + } + else if (CodeGenCommon.ConstFitsOnSImm9(offset)) + { + context.Assembler.Stur(value, address, offset); + } + else + { + Operand tempAddress = Register(CodeGenCommon.ReservedRegister); + GenerateConstantCopy(context, tempAddress, (ulong)offset); + context.Assembler.Add(tempAddress, address, tempAddress, ArmExtensionType.Uxtx); // Address might be SP and must be the first input. + context.Assembler.StrRiUn(value, tempAddress, 0); + } + } + + private static void GenerateSpill(CodeGenContext context, Operation operation) + { + GenerateSpill(context, operation, context.CallArgsRegionSize + context.FpLrSaveRegionSize); + } + + private static void GenerateSpillArg(CodeGenContext context, Operation operation) + { + GenerateSpill(context, operation, 0); + } + + private static void GenerateStackAlloc(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand offset = operation.GetSource(0); + + Debug.Assert(offset.Kind == OperandKind.Constant); + + int offs = offset.AsInt32() + context.CallArgsRegionSize + context.FpLrSaveRegionSize; + + context.Assembler.Add(dest, Register(SpRegister), Const(dest.Type, offs)); + } + + private static void GenerateStore(CodeGenContext context, Operation operation) + { + Operand value = operation.GetSource(1); + Operand address = operation.GetSource(0); + + context.Assembler.Str(value, address); + } + + private static void GenerateStore16(CodeGenContext context, Operation operation) + { + Operand value = operation.GetSource(1); + Operand address = operation.GetSource(0); + + Debug.Assert(value.Type.IsInteger()); + + context.Assembler.StrhRiUn(value, address, 0); + } + + private static void GenerateStore8(CodeGenContext context, Operation operation) + { + Operand value = operation.GetSource(1); + Operand address = operation.GetSource(0); + + Debug.Assert(value.Type.IsInteger()); + + context.Assembler.StrbRiUn(value, address, 0); + } + + private static void GenerateSpill(CodeGenContext context, Operation operation, int baseOffset) + { + Operand offset = operation.GetSource(0); + Operand source = operation.GetSource(1); + + Debug.Assert(offset.Kind == OperandKind.Constant); + + int offs = offset.AsInt32() + baseOffset; + + GenerateStore(context, source, Register(SpRegister), offs); + } + + private static void GenerateSubtract(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + // ValidateBinOp(dest, src1, src2); + + if (dest.Type.IsInteger()) + { + context.Assembler.Sub(dest, src1, src2); + } + else + { + context.Assembler.FsubScalar(dest, src1, src2); + } + } + + private static void GenerateTailcall(CodeGenContext context, Operation operation) + { + WriteEpilogue(context); + + context.Assembler.Br(operation.GetSource(0)); + } + + private static void GenerateVectorCreateScalar(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + if (dest != default) + { + Debug.Assert(!dest.Type.IsInteger() && source.Type.IsInteger()); + + OperandType destType = source.Type == OperandType.I64 ? OperandType.FP64 : OperandType.FP32; + + context.Assembler.Fmov(Register(dest, destType), source, topHalf: false); + } + } + + private static void GenerateVectorExtract(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; // Value + Operand src1 = operation.GetSource(0); // Vector + Operand src2 = operation.GetSource(1); // Index + + Debug.Assert(src1.Type == OperandType.V128); + Debug.Assert(src2.Kind == OperandKind.Constant); + + byte index = src2.AsByte(); + + Debug.Assert(index < OperandType.V128.GetSizeInBytes() / dest.Type.GetSizeInBytes()); + + if (dest.Type.IsInteger()) + { + context.Assembler.Umov(dest, src1, index, dest.Type == OperandType.I64 ? 3 : 2); + } + else + { + context.Assembler.DupScalar(dest, src1, index, dest.Type == OperandType.FP64 ? 3 : 2); + } + } + + private static void GenerateVectorExtract16(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; // Value + Operand src1 = operation.GetSource(0); // Vector + Operand src2 = operation.GetSource(1); // Index + + Debug.Assert(src1.Type == OperandType.V128); + Debug.Assert(src2.Kind == OperandKind.Constant); + + byte index = src2.AsByte(); + + Debug.Assert(index < 8); + + context.Assembler.Umov(dest, src1, index, 1); + } + + private static void GenerateVectorExtract8(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; // Value + Operand src1 = operation.GetSource(0); // Vector + Operand src2 = operation.GetSource(1); // Index + + Debug.Assert(src1.Type == OperandType.V128); + Debug.Assert(src2.Kind == OperandKind.Constant); + + byte index = src2.AsByte(); + + Debug.Assert(index < 16); + + context.Assembler.Umov(dest, src1, index, 0); + } + + private static void GenerateVectorInsert(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); // Vector + Operand src2 = operation.GetSource(1); // Value + Operand src3 = operation.GetSource(2); // Index + + EnsureSameReg(dest, src1); + + Debug.Assert(src1.Type == OperandType.V128); + Debug.Assert(src3.Kind == OperandKind.Constant); + + byte index = src3.AsByte(); + + if (src2.Type.IsInteger()) + { + context.Assembler.Ins(dest, src2, index, src2.Type == OperandType.I64 ? 3 : 2); + } + else + { + context.Assembler.Ins(dest, src2, 0, index, src2.Type == OperandType.FP64 ? 3 : 2); + } + } + + private static void GenerateVectorInsert16(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); // Vector + Operand src2 = operation.GetSource(1); // Value + Operand src3 = operation.GetSource(2); // Index + + EnsureSameReg(dest, src1); + + Debug.Assert(src1.Type == OperandType.V128); + Debug.Assert(src3.Kind == OperandKind.Constant); + + byte index = src3.AsByte(); + + context.Assembler.Ins(dest, src2, index, 1); + } + + private static void GenerateVectorInsert8(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); // Vector + Operand src2 = operation.GetSource(1); // Value + Operand src3 = operation.GetSource(2); // Index + + EnsureSameReg(dest, src1); + + Debug.Assert(src1.Type == OperandType.V128); + Debug.Assert(src3.Kind == OperandKind.Constant); + + byte index = src3.AsByte(); + + context.Assembler.Ins(dest, src2, index, 0); + } + + private static void GenerateVectorOne(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + + Debug.Assert(!dest.Type.IsInteger()); + + context.Assembler.CmeqVector(dest, dest, dest, 2); + } + + private static void GenerateVectorZero(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + + Debug.Assert(!dest.Type.IsInteger()); + + context.Assembler.EorVector(dest, dest, dest); + } + + private static void GenerateVectorZeroUpper64(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128); + + context.Assembler.Fmov(Register(dest, OperandType.FP64), Register(source, OperandType.FP64)); + } + + private static void GenerateVectorZeroUpper96(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128); + + context.Assembler.Fmov(Register(dest, OperandType.FP32), Register(source, OperandType.FP32)); + } + + private static void GenerateZeroExtend16(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + + context.Assembler.Uxth(dest, source); + } + + private static void GenerateZeroExtend32(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + + // We can eliminate the move if source is already 32-bit and the registers are the same. + if (dest.Value == source.Value && source.Type == OperandType.I32) + { + return; + } + + context.Assembler.Mov(Register(dest.GetRegister().Index, OperandType.I32), source); + } + + private static void GenerateZeroExtend8(CodeGenContext context, Operation operation) + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); + + Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); + + context.Assembler.Uxtb(dest, source); + } + + private static UnwindInfo WritePrologue(CodeGenContext context) + { + List pushEntries = new List(); + + Operand rsp = Register(SpRegister); + + int intMask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.IntUsedRegisters; + int vecMask = CallingConvention.GetFpCalleeSavedRegisters() & context.AllocResult.VecUsedRegisters; + + int intCalleeSavedRegsCount = BitOperations.PopCount((uint)intMask); + int vecCalleeSavedRegsCount = BitOperations.PopCount((uint)vecMask); + + int calleeSaveRegionSize = Align16(intCalleeSavedRegsCount * 8 + vecCalleeSavedRegsCount * 8); + + int offset = 0; + + WritePrologueCalleeSavesPreIndexed(context, pushEntries, ref intMask, ref offset, calleeSaveRegionSize, OperandType.I64); + WritePrologueCalleeSavesPreIndexed(context, pushEntries, ref vecMask, ref offset, calleeSaveRegionSize, OperandType.FP64); + + int localSize = Align16(context.AllocResult.SpillRegionSize + context.FpLrSaveRegionSize); + int outArgsSize = context.CallArgsRegionSize; + + if (CodeGenCommon.ConstFitsOnSImm7(localSize, DWordScale)) + { + if (context.HasCall) + { + context.Assembler.StpRiPre(Register(FpRegister), Register(LrRegister), rsp, -localSize); + context.Assembler.MovSp(Register(FpRegister), rsp); + } + + if (outArgsSize != 0) + { + context.Assembler.Sub(rsp, rsp, Const(OperandType.I64, outArgsSize)); + } + } + else + { + int frameSize = localSize + outArgsSize; + if (frameSize != 0) + { + if (CodeGenCommon.ConstFitsOnUImm12(frameSize)) + { + context.Assembler.Sub(rsp, rsp, Const(OperandType.I64, frameSize)); + } + else + { + Operand tempSize = Register(CodeGenCommon.ReservedRegister); + GenerateConstantCopy(context, tempSize, (ulong)frameSize); + context.Assembler.Sub(rsp, rsp, tempSize, ArmExtensionType.Uxtx); + } + } + + context.Assembler.StpRiUn(Register(FpRegister), Register(LrRegister), rsp, outArgsSize); + + if (outArgsSize != 0) + { + context.Assembler.Add(Register(FpRegister), Register(SpRegister), Const(OperandType.I64, outArgsSize)); + } + else + { + context.Assembler.MovSp(Register(FpRegister), Register(SpRegister)); + } + } + + return new UnwindInfo(pushEntries.ToArray(), context.StreamOffset); + } + + private static void WritePrologueCalleeSavesPreIndexed( + CodeGenContext context, + List pushEntries, + ref int mask, + ref int offset, + int calleeSaveRegionSize, + OperandType type) + { + if ((BitOperations.PopCount((uint)mask) & 1) != 0) + { + int reg = BitOperations.TrailingZeroCount(mask); + + pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.PushReg, context.StreamOffset, regIndex: reg)); + + mask &= ~(1 << reg); + + if (offset != 0) + { + context.Assembler.StrRiUn(Register(reg, type), Register(SpRegister), offset); + } + else + { + context.Assembler.StrRiPre(Register(reg, type), Register(SpRegister), -calleeSaveRegionSize); + } + + offset += type.GetSizeInBytes(); + } + + while (mask != 0) + { + int reg = BitOperations.TrailingZeroCount(mask); + + pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.PushReg, context.StreamOffset, regIndex: reg)); + + mask &= ~(1 << reg); + + int reg2 = BitOperations.TrailingZeroCount(mask); + + pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.PushReg, context.StreamOffset, regIndex: reg2)); + + mask &= ~(1 << reg2); + + if (offset != 0) + { + context.Assembler.StpRiUn(Register(reg, type), Register(reg2, type), Register(SpRegister), offset); + } + else + { + context.Assembler.StpRiPre(Register(reg, type), Register(reg2, type), Register(SpRegister), -calleeSaveRegionSize); + } + + offset += type.GetSizeInBytes() * 2; + } + } + + private static void WriteEpilogue(CodeGenContext context) + { + Operand rsp = Register(SpRegister); + + int localSize = Align16(context.AllocResult.SpillRegionSize + context.FpLrSaveRegionSize); + int outArgsSize = context.CallArgsRegionSize; + + if (CodeGenCommon.ConstFitsOnSImm7(localSize, DWordScale)) + { + if (outArgsSize != 0) + { + context.Assembler.Add(rsp, rsp, Const(OperandType.I64, outArgsSize)); + } + + if (context.HasCall) + { + context.Assembler.LdpRiPost(Register(FpRegister), Register(LrRegister), rsp, localSize); + } + } + else + { + if (context.HasCall) + { + context.Assembler.LdpRiUn(Register(FpRegister), Register(LrRegister), rsp, outArgsSize); + } + + int frameSize = localSize + outArgsSize; + if (frameSize != 0) + { + if (CodeGenCommon.ConstFitsOnUImm12(frameSize)) + { + context.Assembler.Add(rsp, rsp, Const(OperandType.I64, frameSize)); + } + else + { + Operand tempSize = Register(CodeGenCommon.ReservedRegister); + GenerateConstantCopy(context, tempSize, (ulong)frameSize); + context.Assembler.Add(rsp, rsp, tempSize, ArmExtensionType.Uxtx); + } + } + } + + int intMask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.IntUsedRegisters; + int vecMask = CallingConvention.GetFpCalleeSavedRegisters() & context.AllocResult.VecUsedRegisters; + + int intCalleeSavedRegsCount = BitOperations.PopCount((uint)intMask); + int vecCalleeSavedRegsCount = BitOperations.PopCount((uint)vecMask); + + int offset = intCalleeSavedRegsCount * 8 + vecCalleeSavedRegsCount * 8; + int calleeSaveRegionSize = Align16(offset); + + WriteEpilogueCalleeSavesPostIndexed(context, ref vecMask, ref offset, calleeSaveRegionSize, OperandType.FP64); + WriteEpilogueCalleeSavesPostIndexed(context, ref intMask, ref offset, calleeSaveRegionSize, OperandType.I64); + } + + private static void WriteEpilogueCalleeSavesPostIndexed( + CodeGenContext context, + ref int mask, + ref int offset, + int calleeSaveRegionSize, + OperandType type) + { + while (mask != 0) + { + int reg = BitUtils.HighestBitSet(mask); + + mask &= ~(1 << reg); + + if (mask != 0) + { + int reg2 = BitUtils.HighestBitSet(mask); + + mask &= ~(1 << reg2); + + offset -= type.GetSizeInBytes() * 2; + + if (offset != 0) + { + context.Assembler.LdpRiUn(Register(reg2, type), Register(reg, type), Register(SpRegister), offset); + } + else + { + context.Assembler.LdpRiPost(Register(reg2, type), Register(reg, type), Register(SpRegister), calleeSaveRegionSize); + } + } + else + { + offset -= type.GetSizeInBytes(); + + if (offset != 0) + { + context.Assembler.LdrRiUn(Register(reg, type), Register(SpRegister), offset); + } + else + { + context.Assembler.LdrRiPost(Register(reg, type), Register(SpRegister), calleeSaveRegionSize); + } + } + } + } + + private static void GenerateConstantCopy(CodeGenContext context, Operand dest, ulong value) + { + if (value != 0) + { + int hw = 0; + bool first = true; + + while (value != 0) + { + int valueLow = (ushort)value; + if (valueLow != 0) + { + if (first) + { + context.Assembler.Movz(dest, valueLow, hw); + first = false; + } + else + { + context.Assembler.Movk(dest, valueLow, hw); + } + } + + hw++; + value >>= 16; + } + } + else + { + context.Assembler.Mov(dest, Register(ZrRegister, dest.Type)); + } + } + + private static void GenerateAtomicCas( + CodeGenContext context, + Operand address, + Operand expected, + Operand desired, + Operand actual, + Operand result, + AccessSize accessSize) + { + int startOffset = context.StreamOffset; + + switch (accessSize) + { + case AccessSize.Byte: + context.Assembler.Ldaxrb(actual, address); + break; + case AccessSize.Hword: + context.Assembler.Ldaxrh(actual, address); + break; + default: + context.Assembler.Ldaxr(actual, address); + break; + } + + context.Assembler.Cmp(actual, expected); + + context.JumpToNear(ArmCondition.Ne); + + switch (accessSize) + { + case AccessSize.Byte: + context.Assembler.Stlxrb(desired, address, result); + break; + case AccessSize.Hword: + context.Assembler.Stlxrh(desired, address, result); + break; + default: + context.Assembler.Stlxr(desired, address, result); + break; + } + + context.Assembler.Cbnz(result, startOffset - context.StreamOffset); // Retry if store failed. + + context.JumpHere(); + + context.Assembler.Clrex(); + } + + private static void GenerateAtomicDcas( + CodeGenContext context, + Operand address, + Operand expectedLow, + Operand expectedHigh, + Operand desiredLow, + Operand desiredHigh, + Operand actualLow, + Operand actualHigh, + Operand temp0, + Operand temp1) + { + int startOffset = context.StreamOffset; + + context.Assembler.Ldaxp(actualLow, actualHigh, address); + context.Assembler.Eor(temp0, actualHigh, expectedHigh); + context.Assembler.Eor(temp1, actualLow, expectedLow); + context.Assembler.Orr(temp0, temp1, temp0); + + context.JumpToNearIfNotZero(temp0); + + Operand result = Register(temp0, OperandType.I32); + + context.Assembler.Stlxp(desiredLow, desiredHigh, address, result); + context.Assembler.Cbnz(result, startOffset - context.StreamOffset); // Retry if store failed. + + context.JumpHere(); + + context.Assembler.Clrex(); + } + + private static bool TryPairMemoryOp(CodeGenContext context, Operation currentOp, Operation nextOp) + { + if (!TryGetMemOpBaseAndOffset(currentOp, out Operand op1Base, out int op1Offset)) + { + return false; + } + + if (!TryGetMemOpBaseAndOffset(nextOp, out Operand op2Base, out int op2Offset)) + { + return false; + } + + if (op1Base != op2Base) + { + return false; + } + + OperandType valueType = GetMemOpValueType(currentOp); + + if (valueType != GetMemOpValueType(nextOp) || op1Offset + valueType.GetSizeInBytes() != op2Offset) + { + return false; + } + + if (!CodeGenCommon.ConstFitsOnSImm7(op1Offset, valueType.GetSizeInBytesLog2())) + { + return false; + } + + if (currentOp.Instruction == Instruction.Load) + { + context.Assembler.LdpRiUn(currentOp.Destination, nextOp.Destination, op1Base, op1Offset); + } + else if (currentOp.Instruction == Instruction.Store) + { + context.Assembler.StpRiUn(currentOp.GetSource(1), nextOp.GetSource(1), op1Base, op1Offset); + } + else + { + return false; + } + + return true; + } + + private static bool IsLoadOrStore(Operation operation) + { + return operation.Instruction == Instruction.Load || operation.Instruction == Instruction.Store; + } + + private static OperandType GetMemOpValueType(Operation operation) + { + if (operation.Destination != default) + { + return operation.Destination.Type; + } + + return operation.GetSource(1).Type; + } + + private static bool TryGetMemOpBaseAndOffset(Operation operation, out Operand baseAddress, out int offset) + { + baseAddress = default; + offset = 0; + Operand address = operation.GetSource(0); + + if (address.Kind != OperandKind.Memory) + { + return false; + } + + MemoryOperand memOp = address.GetMemory(); + Operand baseOp = memOp.BaseAddress; + + if (baseOp == default) + { + baseOp = memOp.Index; + + if (baseOp == default || memOp.Scale != Multiplier.x1) + { + return false; + } + } + if (memOp.Index != default) + { + return false; + } + + baseAddress = memOp.BaseAddress; + offset = memOp.Displacement; + + return true; + } + + private static Operand Register(Operand operand, OperandType type = OperandType.I64) + { + return Register(operand.GetRegister().Index, type); + } + + private static Operand Register(int register, OperandType type = OperandType.I64) + { + return Factory.Register(register, RegisterType.Integer, type); + } + + private static int Align16(int value) + { + return (value + 0xf) & ~0xf; + } + + [Conditional("DEBUG")] + private static void ValidateUnOp(Operand dest, Operand source) + { + // Destination and source aren't forced to be equals + // EnsureSameReg (dest, source); + EnsureSameType(dest, source); + } + + [Conditional("DEBUG")] + private static void ValidateBinOp(Operand dest, Operand src1, Operand src2) + { + // Destination and source aren't forced to be equals + // EnsureSameReg (dest, src1); + EnsureSameType(dest, src1, src2); + } + + [Conditional("DEBUG")] + private static void ValidateShift(Operand dest, Operand src1, Operand src2) + { + // Destination and source aren't forced to be equals + // EnsureSameReg (dest, src1); + EnsureSameType(dest, src1); + + Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32); + } + + private static void EnsureSameReg(Operand op1, Operand op2) + { + Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory); + Debug.Assert(op1.Kind == op2.Kind); + Debug.Assert(op1.Value == op2.Value); + } + + private static void EnsureSameType(Operand op1, Operand op2) + { + Debug.Assert(op1.Type == op2.Type); + } + + private static void EnsureSameType(Operand op1, Operand op2, Operand op3) + { + Debug.Assert(op1.Type == op2.Type); + Debug.Assert(op1.Type == op3.Type); + } + + private static void EnsureSameType(Operand op1, Operand op2, Operand op3, Operand op4) + { + Debug.Assert(op1.Type == op2.Type); + Debug.Assert(op1.Type == op3.Type); + Debug.Assert(op1.Type == op4.Type); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs b/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs new file mode 100644 index 000000000..aaa00bb65 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs @@ -0,0 +1,662 @@ +using ARMeilleure.IntermediateRepresentation; +using System; +using System.Diagnostics; + +namespace ARMeilleure.CodeGen.Arm64 +{ + static class CodeGeneratorIntrinsic + { + public static void GenerateOperation(CodeGenContext context, Operation operation) + { + Intrinsic intrin = operation.Intrinsic; + + IntrinsicInfo info = IntrinsicTable.GetInfo(intrin & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask)); + + switch (info.Type) + { + case IntrinsicType.ScalarUnary: + GenerateVectorUnary( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0)); + break; + case IntrinsicType.ScalarUnaryByElem: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorUnaryByElem( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(1).AsInt32(), + operation.Destination, + operation.GetSource(0)); + break; + case IntrinsicType.ScalarBinary: + GenerateVectorBinary( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.ScalarBinaryFPByElem: + Debug.Assert(operation.GetSource(2).Kind == OperandKind.Constant); + + GenerateVectorBinaryFPByElem( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(2).AsInt32(), + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.ScalarBinaryRd: + GenerateVectorUnary( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1)); + break; + case IntrinsicType.ScalarBinaryShl: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorBinaryShlImm( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + (uint)operation.GetSource(1).AsInt32()); + break; + case IntrinsicType.ScalarBinaryShr: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorBinaryShrImm( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + (uint)operation.GetSource(1).AsInt32()); + break; + case IntrinsicType.ScalarFPCompare: + GenerateScalarFPCompare( + context, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.ScalarFPConvFixed: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorBinaryShrImm( + context, + 0, + ((uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift) + 2u, + info.Inst, + operation.Destination, + operation.GetSource(0), + (uint)operation.GetSource(1).AsInt32()); + break; + case IntrinsicType.ScalarFPConvFixedGpr: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateScalarFPConvGpr( + context, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + (uint)operation.GetSource(1).AsInt32()); + break; + case IntrinsicType.ScalarFPConvGpr: + GenerateScalarFPConvGpr( + context, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0)); + break; + case IntrinsicType.ScalarTernary: + GenerateScalarTernary( + context, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1), + operation.GetSource(2), + operation.GetSource(0)); + break; + case IntrinsicType.ScalarTernaryFPRdByElem: + Debug.Assert(operation.GetSource(3).Kind == OperandKind.Constant); + + GenerateVectorBinaryFPByElem( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(3).AsInt32(), + operation.Destination, + operation.GetSource(1), + operation.GetSource(2)); + break; + case IntrinsicType.ScalarTernaryShlRd: + Debug.Assert(operation.GetSource(2).Kind == OperandKind.Constant); + + GenerateVectorBinaryShlImm( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1), + (uint)operation.GetSource(2).AsInt32()); + break; + case IntrinsicType.ScalarTernaryShrRd: + Debug.Assert(operation.GetSource(2).Kind == OperandKind.Constant); + + GenerateVectorBinaryShrImm( + context, + 0, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1), + (uint)operation.GetSource(2).AsInt32()); + break; + + case IntrinsicType.VectorUnary: + GenerateVectorUnary( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0)); + break; + case IntrinsicType.VectorUnaryByElem: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorUnaryByElem( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(1).AsInt32(), + operation.Destination, + operation.GetSource(0)); + break; + case IntrinsicType.VectorBinary: + GenerateVectorBinary( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.VectorBinaryBitwise: + GenerateVectorBinary( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.VectorBinaryByElem: + Debug.Assert(operation.GetSource(2).Kind == OperandKind.Constant); + + GenerateVectorBinaryByElem( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(2).AsInt32(), + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.VectorBinaryFPByElem: + Debug.Assert(operation.GetSource(2).Kind == OperandKind.Constant); + + GenerateVectorBinaryFPByElem( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(2).AsInt32(), + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.VectorBinaryRd: + GenerateVectorUnary( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1)); + break; + case IntrinsicType.VectorBinaryShl: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorBinaryShlImm( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + (uint)operation.GetSource(1).AsInt32()); + break; + case IntrinsicType.VectorBinaryShr: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorBinaryShrImm( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(0), + (uint)operation.GetSource(1).AsInt32()); + break; + case IntrinsicType.VectorFPConvFixed: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + + GenerateVectorBinaryShrImm( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + ((uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift) + 2u, + info.Inst, + operation.Destination, + operation.GetSource(0), + (uint)operation.GetSource(1).AsInt32()); + break; + case IntrinsicType.VectorInsertByElem: + Debug.Assert(operation.GetSource(1).Kind == OperandKind.Constant); + Debug.Assert(operation.GetSource(3).Kind == OperandKind.Constant); + + GenerateVectorInsertByElem( + context, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(3).AsInt32(), + (uint)operation.GetSource(1).AsInt32(), + operation.Destination, + operation.GetSource(2)); + break; + case IntrinsicType.VectorLookupTable: + Debug.Assert((uint)(operation.SourcesCount - 2) <= 3); + + for (int i = 1; i < operation.SourcesCount - 1; i++) + { + Register currReg = operation.GetSource(i).GetRegister(); + Register prevReg = operation.GetSource(i - 1).GetRegister(); + + Debug.Assert(prevReg.Index + 1 == currReg.Index && currReg.Type == RegisterType.Vector); + } + + GenerateVectorBinary( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + info.Inst | ((uint)(operation.SourcesCount - 2) << 13), + operation.Destination, + operation.GetSource(0), + operation.GetSource(operation.SourcesCount - 1)); + break; + case IntrinsicType.VectorTernaryFPRdByElem: + Debug.Assert(operation.GetSource(3).Kind == OperandKind.Constant); + + GenerateVectorBinaryFPByElem( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(3).AsInt32(), + operation.Destination, + operation.GetSource(1), + operation.GetSource(2)); + break; + case IntrinsicType.VectorTernaryRd: + GenerateVectorBinary( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1), + operation.GetSource(2)); + break; + case IntrinsicType.VectorTernaryRdBitwise: + GenerateVectorBinary( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + info.Inst, + operation.Destination, + operation.GetSource(1), + operation.GetSource(2)); + break; + case IntrinsicType.VectorTernaryRdByElem: + Debug.Assert(operation.GetSource(3).Kind == OperandKind.Constant); + + GenerateVectorBinaryByElem( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + (uint)operation.GetSource(3).AsInt32(), + operation.Destination, + operation.GetSource(1), + operation.GetSource(2)); + break; + case IntrinsicType.VectorTernaryShlRd: + Debug.Assert(operation.GetSource(2).Kind == OperandKind.Constant); + + GenerateVectorBinaryShlImm( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1), + (uint)operation.GetSource(2).AsInt32()); + break; + case IntrinsicType.VectorTernaryShrRd: + Debug.Assert(operation.GetSource(2).Kind == OperandKind.Constant); + + GenerateVectorBinaryShrImm( + context, + (uint)(intrin & Intrinsic.Arm64VTypeMask) >> (int)Intrinsic.Arm64VTypeShift, + (uint)(intrin & Intrinsic.Arm64VSizeMask) >> (int)Intrinsic.Arm64VSizeShift, + info.Inst, + operation.Destination, + operation.GetSource(1), + (uint)operation.GetSource(2).AsInt32()); + break; + + case IntrinsicType.GetRegister: + context.Assembler.WriteInstruction(info.Inst, operation.Destination); + break; + case IntrinsicType.SetRegister: + context.Assembler.WriteInstruction(info.Inst, operation.GetSource(0)); + break; + + default: + throw new NotImplementedException(info.Type.ToString()); + } + } + + private static void GenerateScalarFPCompare( + CodeGenContext context, + uint sz, + uint instruction, + Operand dest, + Operand rn, + Operand rm) + { + instruction |= (sz << 22); + + if (rm.Kind == OperandKind.Constant && rm.Value == 0) + { + instruction |= 0b1000; + rm = rn; + } + + context.Assembler.WriteInstructionRm16NoRet(instruction, rn, rm); + context.Assembler.Mrs(dest, 1, 3, 4, 2, 0); + } + + private static void GenerateScalarFPConvGpr( + CodeGenContext context, + uint sz, + uint instruction, + Operand rd, + Operand rn) + { + instruction |= (sz << 22); + + if (rd.Type.IsInteger()) + { + context.Assembler.WriteInstructionAuto(instruction, rd, rn); + } + else + { + if (rn.Type == OperandType.I64) + { + instruction |= Assembler.SfFlag; + } + + context.Assembler.WriteInstruction(instruction, rd, rn); + } + } + + private static void GenerateScalarFPConvGpr( + CodeGenContext context, + uint sz, + uint instruction, + Operand rd, + Operand rn, + uint fBits) + { + Debug.Assert(fBits <= 64); + + instruction |= (sz << 22); + instruction |= (64 - fBits) << 10; + + if (rd.Type.IsInteger()) + { + Debug.Assert(rd.Type != OperandType.I32 || fBits <= 32); + + context.Assembler.WriteInstructionAuto(instruction, rd, rn); + } + else + { + if (rn.Type == OperandType.I64) + { + instruction |= Assembler.SfFlag; + } + else + { + Debug.Assert(fBits <= 32); + } + + context.Assembler.WriteInstruction(instruction, rd, rn); + } + + } + + private static void GenerateScalarTernary( + CodeGenContext context, + uint sz, + uint instruction, + Operand rd, + Operand rn, + Operand rm, + Operand ra) + { + instruction |= (sz << 22); + + context.Assembler.WriteInstruction(instruction, rd, rn, rm, ra); + } + + private static void GenerateVectorUnary( + CodeGenContext context, + uint q, + uint sz, + uint instruction, + Operand rd, + Operand rn) + { + instruction |= (q << 30) | (sz << 22); + + context.Assembler.WriteInstruction(instruction, rd, rn); + } + + private static void GenerateVectorUnaryByElem( + CodeGenContext context, + uint q, + uint sz, + uint instruction, + uint srcIndex, + Operand rd, + Operand rn) + { + uint imm5 = (srcIndex << ((int)sz + 1)) | (1u << (int)sz); + + instruction |= (q << 30) | (imm5 << 16); + + context.Assembler.WriteInstruction(instruction, rd, rn); + } + + private static void GenerateVectorBinary( + CodeGenContext context, + uint q, + uint instruction, + Operand rd, + Operand rn, + Operand rm) + { + instruction |= (q << 30); + + context.Assembler.WriteInstructionRm16(instruction, rd, rn, rm); + } + + private static void GenerateVectorBinary( + CodeGenContext context, + uint q, + uint sz, + uint instruction, + Operand rd, + Operand rn, + Operand rm) + { + instruction |= (q << 30) | (sz << 22); + + context.Assembler.WriteInstructionRm16(instruction, rd, rn, rm); + } + + private static void GenerateVectorBinaryByElem( + CodeGenContext context, + uint q, + uint size, + uint instruction, + uint srcIndex, + Operand rd, + Operand rn, + Operand rm) + { + instruction |= (q << 30) | (size << 22); + + if (size == 2) + { + instruction |= ((srcIndex & 1) << 21) | ((srcIndex & 2) << 10); + } + else + { + instruction |= ((srcIndex & 3) << 20) | ((srcIndex & 4) << 9); + } + + context.Assembler.WriteInstructionRm16(instruction, rd, rn, rm); + } + + private static void GenerateVectorBinaryFPByElem( + CodeGenContext context, + uint q, + uint sz, + uint instruction, + uint srcIndex, + Operand rd, + Operand rn, + Operand rm) + { + instruction |= (q << 30) | (sz << 22); + + if (sz != 0) + { + instruction |= (srcIndex & 1) << 11; + } + else + { + instruction |= ((srcIndex & 1) << 21) | ((srcIndex & 2) << 10); + } + + context.Assembler.WriteInstructionRm16(instruction, rd, rn, rm); + } + + private static void GenerateVectorBinaryShlImm( + CodeGenContext context, + uint q, + uint sz, + uint instruction, + Operand rd, + Operand rn, + uint shift) + { + instruction |= (q << 30); + + Debug.Assert(shift >= 0 && shift < (8u << (int)sz)); + + uint imm = (8u << (int)sz) | (shift & (0x3fu >> (int)(3 - sz))); + + instruction |= (imm << 16); + + context.Assembler.WriteInstruction(instruction, rd, rn); + } + + private static void GenerateVectorBinaryShrImm( + CodeGenContext context, + uint q, + uint sz, + uint instruction, + Operand rd, + Operand rn, + uint shift) + { + instruction |= (q << 30); + + Debug.Assert(shift > 0 && shift <= (8u << (int)sz)); + + uint imm = (8u << (int)sz) | ((8u << (int)sz) - shift); + + instruction |= (imm << 16); + + context.Assembler.WriteInstruction(instruction, rd, rn); + } + + private static void GenerateVectorInsertByElem( + CodeGenContext context, + uint sz, + uint instruction, + uint srcIndex, + uint dstIndex, + Operand rd, + Operand rn) + { + uint imm4 = srcIndex << (int)sz; + uint imm5 = (dstIndex << ((int)sz + 1)) | (1u << (int)sz); + + instruction |= imm4 << 11; + instruction |= imm5 << 16; + + context.Assembler.WriteInstruction(instruction, rd, rn); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs b/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs new file mode 100644 index 000000000..8695db903 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs @@ -0,0 +1,14 @@ +namespace ARMeilleure.CodeGen.Arm64 +{ + struct IntrinsicInfo + { + public uint Inst { get; } + public IntrinsicType Type { get; } + + public IntrinsicInfo(uint inst, IntrinsicType type) + { + Inst = inst; + Type = type; + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs b/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs new file mode 100644 index 000000000..53ef152e5 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs @@ -0,0 +1,461 @@ +using ARMeilleure.Common; +using ARMeilleure.IntermediateRepresentation; + +namespace ARMeilleure.CodeGen.Arm64 +{ + static class IntrinsicTable + { + private static IntrinsicInfo[] _intrinTable; + + static IntrinsicTable() + { + _intrinTable = new IntrinsicInfo[EnumUtils.GetCount(typeof(Intrinsic))]; + + Add(Intrinsic.Arm64AbsS, new IntrinsicInfo(0x5e20b800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64AbsV, new IntrinsicInfo(0x0e20b800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64AddhnV, new IntrinsicInfo(0x0e204000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64AddpS, new IntrinsicInfo(0x5e31b800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64AddpV, new IntrinsicInfo(0x0e20bc00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64AddvV, new IntrinsicInfo(0x0e31b800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64AddS, new IntrinsicInfo(0x5e208400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64AddV, new IntrinsicInfo(0x0e208400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64AesdV, new IntrinsicInfo(0x4e285800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64AeseV, new IntrinsicInfo(0x4e284800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64AesimcV, new IntrinsicInfo(0x4e287800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64AesmcV, new IntrinsicInfo(0x4e286800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64AndV, new IntrinsicInfo(0x0e201c00u, IntrinsicType.VectorBinaryBitwise)); + Add(Intrinsic.Arm64BicVi, new IntrinsicInfo(0x2f001400u, IntrinsicType.VectorBinaryBitwiseImm)); + Add(Intrinsic.Arm64BicV, new IntrinsicInfo(0x0e601c00u, IntrinsicType.VectorBinaryBitwise)); + Add(Intrinsic.Arm64BifV, new IntrinsicInfo(0x2ee01c00u, IntrinsicType.VectorTernaryRdBitwise)); + Add(Intrinsic.Arm64BitV, new IntrinsicInfo(0x2ea01c00u, IntrinsicType.VectorTernaryRdBitwise)); + Add(Intrinsic.Arm64BslV, new IntrinsicInfo(0x2e601c00u, IntrinsicType.VectorTernaryRdBitwise)); + Add(Intrinsic.Arm64ClsV, new IntrinsicInfo(0x0e204800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64ClzV, new IntrinsicInfo(0x2e204800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64CmeqS, new IntrinsicInfo(0x7e208c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64CmeqV, new IntrinsicInfo(0x2e208c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64CmeqSz, new IntrinsicInfo(0x5e209800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64CmeqVz, new IntrinsicInfo(0x0e209800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64CmgeS, new IntrinsicInfo(0x5e203c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64CmgeV, new IntrinsicInfo(0x0e203c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64CmgeSz, new IntrinsicInfo(0x7e208800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64CmgeVz, new IntrinsicInfo(0x2e208800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64CmgtS, new IntrinsicInfo(0x5e203400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64CmgtV, new IntrinsicInfo(0x0e203400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64CmgtSz, new IntrinsicInfo(0x5e208800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64CmgtVz, new IntrinsicInfo(0x0e208800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64CmhiS, new IntrinsicInfo(0x7e203400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64CmhiV, new IntrinsicInfo(0x2e203400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64CmhsS, new IntrinsicInfo(0x7e203c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64CmhsV, new IntrinsicInfo(0x2e203c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64CmleSz, new IntrinsicInfo(0x7e209800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64CmleVz, new IntrinsicInfo(0x2e209800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64CmltSz, new IntrinsicInfo(0x5e20a800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64CmltVz, new IntrinsicInfo(0x0e20a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64CmtstS, new IntrinsicInfo(0x5e208c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64CmtstV, new IntrinsicInfo(0x0e208c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64CntV, new IntrinsicInfo(0x0e205800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64DupSe, new IntrinsicInfo(0x5e000400u, IntrinsicType.ScalarUnaryByElem)); + Add(Intrinsic.Arm64DupVe, new IntrinsicInfo(0x0e000400u, IntrinsicType.VectorUnaryByElem)); + Add(Intrinsic.Arm64DupGp, new IntrinsicInfo(0x0e000c00u, IntrinsicType.VectorUnaryByElem)); + Add(Intrinsic.Arm64EorV, new IntrinsicInfo(0x2e201c00u, IntrinsicType.VectorBinaryBitwise)); + Add(Intrinsic.Arm64ExtV, new IntrinsicInfo(0x2e000000u, IntrinsicType.VectorExt)); + Add(Intrinsic.Arm64FabdS, new IntrinsicInfo(0x7ea0d400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FabdV, new IntrinsicInfo(0x2ea0d400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FabsV, new IntrinsicInfo(0x0ea0f800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FabsS, new IntrinsicInfo(0x1e20c000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FacgeS, new IntrinsicInfo(0x7e20ec00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FacgeV, new IntrinsicInfo(0x2e20ec00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FacgtS, new IntrinsicInfo(0x7ea0ec00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FacgtV, new IntrinsicInfo(0x2ea0ec00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FaddpS, new IntrinsicInfo(0x7e30d800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FaddpV, new IntrinsicInfo(0x2e20d400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FaddV, new IntrinsicInfo(0x0e20d400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FaddS, new IntrinsicInfo(0x1e202800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FccmpeS, new IntrinsicInfo(0x1e200410u, IntrinsicType.ScalarFPCompareCond)); + Add(Intrinsic.Arm64FccmpS, new IntrinsicInfo(0x1e200400u, IntrinsicType.ScalarFPCompareCond)); + Add(Intrinsic.Arm64FcmeqS, new IntrinsicInfo(0x5e20e400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FcmeqV, new IntrinsicInfo(0x0e20e400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FcmeqSz, new IntrinsicInfo(0x5ea0d800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcmeqVz, new IntrinsicInfo(0x0ea0d800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcmgeS, new IntrinsicInfo(0x7e20e400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FcmgeV, new IntrinsicInfo(0x2e20e400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FcmgeSz, new IntrinsicInfo(0x7ea0c800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcmgeVz, new IntrinsicInfo(0x2ea0c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcmgtS, new IntrinsicInfo(0x7ea0e400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FcmgtV, new IntrinsicInfo(0x2ea0e400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FcmgtSz, new IntrinsicInfo(0x5ea0c800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcmgtVz, new IntrinsicInfo(0x0ea0c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcmleSz, new IntrinsicInfo(0x7ea0d800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcmleVz, new IntrinsicInfo(0x2ea0d800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcmltSz, new IntrinsicInfo(0x5ea0e800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcmltVz, new IntrinsicInfo(0x0ea0e800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcmpeS, new IntrinsicInfo(0x1e202010u, IntrinsicType.ScalarFPCompare)); + Add(Intrinsic.Arm64FcmpS, new IntrinsicInfo(0x1e202000u, IntrinsicType.ScalarFPCompare)); + Add(Intrinsic.Arm64FcselS, new IntrinsicInfo(0x1e200c00u, IntrinsicType.ScalarFcsel)); + Add(Intrinsic.Arm64FcvtasS, new IntrinsicInfo(0x5e21c800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtasV, new IntrinsicInfo(0x0e21c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtasGp, new IntrinsicInfo(0x1e240000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtauS, new IntrinsicInfo(0x7e21c800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtauV, new IntrinsicInfo(0x2e21c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtauGp, new IntrinsicInfo(0x1e250000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtlV, new IntrinsicInfo(0x0e217800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtmsS, new IntrinsicInfo(0x5e21b800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtmsV, new IntrinsicInfo(0x0e21b800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtmsGp, new IntrinsicInfo(0x1e300000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtmuS, new IntrinsicInfo(0x7e21b800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtmuV, new IntrinsicInfo(0x2e21b800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtmuGp, new IntrinsicInfo(0x1e310000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtnsS, new IntrinsicInfo(0x5e21a800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtnsV, new IntrinsicInfo(0x0e21a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtnsGp, new IntrinsicInfo(0x1e200000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtnuS, new IntrinsicInfo(0x7e21a800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtnuV, new IntrinsicInfo(0x2e21a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtnuGp, new IntrinsicInfo(0x1e210000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtnV, new IntrinsicInfo(0x0e216800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64FcvtpsS, new IntrinsicInfo(0x5ea1a800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtpsV, new IntrinsicInfo(0x0ea1a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtpsGp, new IntrinsicInfo(0x1e280000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtpuS, new IntrinsicInfo(0x7ea1a800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtpuV, new IntrinsicInfo(0x2ea1a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtpuGp, new IntrinsicInfo(0x1e290000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtxnS, new IntrinsicInfo(0x7e216800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtxnV, new IntrinsicInfo(0x2e216800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtzsSFixed, new IntrinsicInfo(0x5f00fc00u, IntrinsicType.ScalarFPConvFixed)); + Add(Intrinsic.Arm64FcvtzsVFixed, new IntrinsicInfo(0x0f00fc00u, IntrinsicType.VectorFPConvFixed)); + Add(Intrinsic.Arm64FcvtzsS, new IntrinsicInfo(0x5ea1b800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtzsV, new IntrinsicInfo(0x0ea1b800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtzsGpFixed, new IntrinsicInfo(0x1e180000u, IntrinsicType.ScalarFPConvFixedGpr)); + Add(Intrinsic.Arm64FcvtzsGp, new IntrinsicInfo(0x1e380000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtzuSFixed, new IntrinsicInfo(0x7f00fc00u, IntrinsicType.ScalarFPConvFixed)); + Add(Intrinsic.Arm64FcvtzuVFixed, new IntrinsicInfo(0x2f00fc00u, IntrinsicType.VectorFPConvFixed)); + Add(Intrinsic.Arm64FcvtzuS, new IntrinsicInfo(0x7ea1b800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FcvtzuV, new IntrinsicInfo(0x2ea1b800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FcvtzuGpFixed, new IntrinsicInfo(0x1e190000u, IntrinsicType.ScalarFPConvFixedGpr)); + Add(Intrinsic.Arm64FcvtzuGp, new IntrinsicInfo(0x1e390000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FcvtS, new IntrinsicInfo(0x1e224000u, IntrinsicType.ScalarFPConv)); + Add(Intrinsic.Arm64FdivV, new IntrinsicInfo(0x2e20fc00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FdivS, new IntrinsicInfo(0x1e201800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FmaddS, new IntrinsicInfo(0x1f000000u, IntrinsicType.ScalarTernary)); + Add(Intrinsic.Arm64FmaxnmpS, new IntrinsicInfo(0x7e30c800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FmaxnmpV, new IntrinsicInfo(0x2e20c400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FmaxnmvV, new IntrinsicInfo(0x2e30c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FmaxnmV, new IntrinsicInfo(0x0e20c400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FmaxnmS, new IntrinsicInfo(0x1e206800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FmaxpS, new IntrinsicInfo(0x7e30f800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FmaxpV, new IntrinsicInfo(0x2e20f400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FmaxvV, new IntrinsicInfo(0x2e30f800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FmaxV, new IntrinsicInfo(0x0e20f400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FmaxS, new IntrinsicInfo(0x1e204800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FminnmpS, new IntrinsicInfo(0x7eb0c800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FminnmpV, new IntrinsicInfo(0x2ea0c400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FminnmvV, new IntrinsicInfo(0x2eb0c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FminnmV, new IntrinsicInfo(0x0ea0c400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FminnmS, new IntrinsicInfo(0x1e207800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FminpS, new IntrinsicInfo(0x7eb0f800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FminpV, new IntrinsicInfo(0x2ea0f400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FminvV, new IntrinsicInfo(0x2eb0f800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FminV, new IntrinsicInfo(0x0ea0f400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FminS, new IntrinsicInfo(0x1e205800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FmlaSe, new IntrinsicInfo(0x5f801000u, IntrinsicType.ScalarTernaryFPRdByElem)); + Add(Intrinsic.Arm64FmlaVe, new IntrinsicInfo(0x0f801000u, IntrinsicType.VectorTernaryFPRdByElem)); + Add(Intrinsic.Arm64FmlaV, new IntrinsicInfo(0x0e20cc00u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64FmlsSe, new IntrinsicInfo(0x5f805000u, IntrinsicType.ScalarTernaryFPRdByElem)); + Add(Intrinsic.Arm64FmlsVe, new IntrinsicInfo(0x0f805000u, IntrinsicType.VectorTernaryFPRdByElem)); + Add(Intrinsic.Arm64FmlsV, new IntrinsicInfo(0x0ea0cc00u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64FmovVi, new IntrinsicInfo(0x0f00f400u, IntrinsicType.VectorFmovi)); + Add(Intrinsic.Arm64FmovS, new IntrinsicInfo(0x1e204000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FmovGp, new IntrinsicInfo(0x1e260000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64FmovSi, new IntrinsicInfo(0x1e201000u, IntrinsicType.ScalarFmovi)); + Add(Intrinsic.Arm64FmsubS, new IntrinsicInfo(0x1f008000u, IntrinsicType.ScalarTernary)); + Add(Intrinsic.Arm64FmulxSe, new IntrinsicInfo(0x7f809000u, IntrinsicType.ScalarBinaryFPByElem)); + Add(Intrinsic.Arm64FmulxVe, new IntrinsicInfo(0x2f809000u, IntrinsicType.VectorBinaryFPByElem)); + Add(Intrinsic.Arm64FmulxS, new IntrinsicInfo(0x5e20dc00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FmulxV, new IntrinsicInfo(0x0e20dc00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FmulSe, new IntrinsicInfo(0x5f809000u, IntrinsicType.ScalarBinaryFPByElem)); + Add(Intrinsic.Arm64FmulVe, new IntrinsicInfo(0x0f809000u, IntrinsicType.VectorBinaryFPByElem)); + Add(Intrinsic.Arm64FmulV, new IntrinsicInfo(0x2e20dc00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FmulS, new IntrinsicInfo(0x1e200800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FnegV, new IntrinsicInfo(0x2ea0f800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FnegS, new IntrinsicInfo(0x1e214000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FnmaddS, new IntrinsicInfo(0x1f200000u, IntrinsicType.ScalarTernary)); + Add(Intrinsic.Arm64FnmsubS, new IntrinsicInfo(0x1f208000u, IntrinsicType.ScalarTernary)); + Add(Intrinsic.Arm64FnmulS, new IntrinsicInfo(0x1e208800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FrecpeS, new IntrinsicInfo(0x5ea1d800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrecpeV, new IntrinsicInfo(0x0ea1d800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrecpsS, new IntrinsicInfo(0x5e20fc00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FrecpsV, new IntrinsicInfo(0x0e20fc00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FrecpxS, new IntrinsicInfo(0x5ea1f800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrintaV, new IntrinsicInfo(0x2e218800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrintaS, new IntrinsicInfo(0x1e264000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrintiV, new IntrinsicInfo(0x2ea19800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrintiS, new IntrinsicInfo(0x1e27c000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrintmV, new IntrinsicInfo(0x0e219800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrintmS, new IntrinsicInfo(0x1e254000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrintnV, new IntrinsicInfo(0x0e218800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrintnS, new IntrinsicInfo(0x1e244000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrintpV, new IntrinsicInfo(0x0ea18800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrintpS, new IntrinsicInfo(0x1e24c000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrintxV, new IntrinsicInfo(0x2e219800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrintxS, new IntrinsicInfo(0x1e274000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrintzV, new IntrinsicInfo(0x0ea19800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrintzS, new IntrinsicInfo(0x1e25c000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrsqrteS, new IntrinsicInfo(0x7ea1d800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FrsqrteV, new IntrinsicInfo(0x2ea1d800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FrsqrtsS, new IntrinsicInfo(0x5ea0fc00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64FrsqrtsV, new IntrinsicInfo(0x0ea0fc00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FsqrtV, new IntrinsicInfo(0x2ea1f800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64FsqrtS, new IntrinsicInfo(0x1e21c000u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64FsubV, new IntrinsicInfo(0x0ea0d400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64FsubS, new IntrinsicInfo(0x1e203800u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64InsVe, new IntrinsicInfo(0x6e000400u, IntrinsicType.VectorInsertByElem)); + Add(Intrinsic.Arm64InsGp, new IntrinsicInfo(0x4e001c00u, IntrinsicType.ScalarUnaryByElem)); + Add(Intrinsic.Arm64Ld1rV, new IntrinsicInfo(0x0d40c000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld1Vms, new IntrinsicInfo(0x0c402000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld1Vss, new IntrinsicInfo(0x0d400000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64Ld2rV, new IntrinsicInfo(0x0d60c000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld2Vms, new IntrinsicInfo(0x0c408000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld2Vss, new IntrinsicInfo(0x0d600000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64Ld3rV, new IntrinsicInfo(0x0d40e000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld3Vms, new IntrinsicInfo(0x0c404000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld3Vss, new IntrinsicInfo(0x0d402000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64Ld4rV, new IntrinsicInfo(0x0d60e000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld4Vms, new IntrinsicInfo(0x0c400000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64Ld4Vss, new IntrinsicInfo(0x0d602000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64MlaVe, new IntrinsicInfo(0x2f000000u, IntrinsicType.VectorTernaryRdByElem)); + Add(Intrinsic.Arm64MlaV, new IntrinsicInfo(0x0e209400u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem)); + Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi)); + Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister)); + Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister)); + Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64MulV, new IntrinsicInfo(0x0e209c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64MvniV, new IntrinsicInfo(0x2f000400u, IntrinsicType.VectorMvni)); + Add(Intrinsic.Arm64NegS, new IntrinsicInfo(0x7e20b800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64NegV, new IntrinsicInfo(0x2e20b800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64NotV, new IntrinsicInfo(0x2e205800u, IntrinsicType.VectorUnaryBitwise)); + Add(Intrinsic.Arm64OrnV, new IntrinsicInfo(0x0ee01c00u, IntrinsicType.VectorBinaryBitwise)); + Add(Intrinsic.Arm64OrrVi, new IntrinsicInfo(0x0f001400u, IntrinsicType.VectorBinaryBitwiseImm)); + Add(Intrinsic.Arm64OrrV, new IntrinsicInfo(0x0ea01c00u, IntrinsicType.VectorBinaryBitwise)); + Add(Intrinsic.Arm64PmullV, new IntrinsicInfo(0x0e20e000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64PmulV, new IntrinsicInfo(0x2e209c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64RaddhnV, new IntrinsicInfo(0x2e204000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64RbitV, new IntrinsicInfo(0x2e605800u, IntrinsicType.VectorUnaryBitwise)); + Add(Intrinsic.Arm64Rev16V, new IntrinsicInfo(0x0e201800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64Rev32V, new IntrinsicInfo(0x2e200800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64Rev64V, new IntrinsicInfo(0x0e200800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64RshrnV, new IntrinsicInfo(0x0f008c00u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64RsubhnV, new IntrinsicInfo(0x2e206000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64SabalV, new IntrinsicInfo(0x0e205000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64SabaV, new IntrinsicInfo(0x0e207c00u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64SabdlV, new IntrinsicInfo(0x0e207000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SabdV, new IntrinsicInfo(0x0e207400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SadalpV, new IntrinsicInfo(0x0e206800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64SaddlpV, new IntrinsicInfo(0x0e202800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64SaddlvV, new IntrinsicInfo(0x0e303800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64SaddlV, new IntrinsicInfo(0x0e200000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SaddwV, new IntrinsicInfo(0x0e201000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64ScvtfSFixed, new IntrinsicInfo(0x5f00e400u, IntrinsicType.ScalarFPConvFixed)); + Add(Intrinsic.Arm64ScvtfVFixed, new IntrinsicInfo(0x0f00e400u, IntrinsicType.VectorFPConvFixed)); + Add(Intrinsic.Arm64ScvtfS, new IntrinsicInfo(0x5e21d800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64ScvtfV, new IntrinsicInfo(0x0e21d800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64ScvtfGpFixed, new IntrinsicInfo(0x1e020000u, IntrinsicType.ScalarFPConvFixedGpr)); + Add(Intrinsic.Arm64ScvtfGp, new IntrinsicInfo(0x1e220000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64Sha1cV, new IntrinsicInfo(0x5e000000u, IntrinsicType.Vector128Binary)); + Add(Intrinsic.Arm64Sha1hV, new IntrinsicInfo(0x5e280800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64Sha1mV, new IntrinsicInfo(0x5e002000u, IntrinsicType.Vector128Binary)); + Add(Intrinsic.Arm64Sha1pV, new IntrinsicInfo(0x5e001000u, IntrinsicType.Vector128Binary)); + Add(Intrinsic.Arm64Sha1su0V, new IntrinsicInfo(0x5e003000u, IntrinsicType.Vector128Binary)); + Add(Intrinsic.Arm64Sha1su1V, new IntrinsicInfo(0x5e281800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64Sha256h2V, new IntrinsicInfo(0x5e005000u, IntrinsicType.Vector128Binary)); + Add(Intrinsic.Arm64Sha256hV, new IntrinsicInfo(0x5e004000u, IntrinsicType.Vector128Binary)); + Add(Intrinsic.Arm64Sha256su0V, new IntrinsicInfo(0x5e282800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64Sha256su1V, new IntrinsicInfo(0x5e006000u, IntrinsicType.Vector128Binary)); + Add(Intrinsic.Arm64ShaddV, new IntrinsicInfo(0x0e200400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64ShllV, new IntrinsicInfo(0x2e213800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64ShlS, new IntrinsicInfo(0x5f005400u, IntrinsicType.ScalarBinaryShl)); + Add(Intrinsic.Arm64ShlV, new IntrinsicInfo(0x0f005400u, IntrinsicType.VectorBinaryShl)); + Add(Intrinsic.Arm64ShrnV, new IntrinsicInfo(0x0f008400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64ShsubV, new IntrinsicInfo(0x0e202400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SliS, new IntrinsicInfo(0x7f005400u, IntrinsicType.ScalarTernaryShlRd)); + Add(Intrinsic.Arm64SliV, new IntrinsicInfo(0x2f005400u, IntrinsicType.VectorTernaryShlRd)); + Add(Intrinsic.Arm64SmaxpV, new IntrinsicInfo(0x0e20a400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SmaxvV, new IntrinsicInfo(0x0e30a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64SmaxV, new IntrinsicInfo(0x0e206400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SminpV, new IntrinsicInfo(0x0e20ac00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SminvV, new IntrinsicInfo(0x0e31a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64SminV, new IntrinsicInfo(0x0e206c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SmlalVe, new IntrinsicInfo(0x0f002000u, IntrinsicType.VectorTernaryRdByElem)); + Add(Intrinsic.Arm64SmlalV, new IntrinsicInfo(0x0e208000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64SmlslVe, new IntrinsicInfo(0x0f006000u, IntrinsicType.VectorTernaryRdByElem)); + Add(Intrinsic.Arm64SmlslV, new IntrinsicInfo(0x0e20a000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64SmovV, new IntrinsicInfo(0x0e002c00u, IntrinsicType.VectorUnaryByElem)); + Add(Intrinsic.Arm64SmullVe, new IntrinsicInfo(0x0f00a000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64SmullV, new IntrinsicInfo(0x0e20c000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqabsS, new IntrinsicInfo(0x5e207800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64SqabsV, new IntrinsicInfo(0x0e207800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64SqaddS, new IntrinsicInfo(0x5e200c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqaddV, new IntrinsicInfo(0x0e200c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqdmlalSe, new IntrinsicInfo(0x5f003000u, IntrinsicType.ScalarBinaryByElem)); + Add(Intrinsic.Arm64SqdmlalVe, new IntrinsicInfo(0x0f003000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64SqdmlalS, new IntrinsicInfo(0x5e209000u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqdmlalV, new IntrinsicInfo(0x0e209000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqdmlslSe, new IntrinsicInfo(0x5f007000u, IntrinsicType.ScalarBinaryByElem)); + Add(Intrinsic.Arm64SqdmlslVe, new IntrinsicInfo(0x0f007000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64SqdmlslS, new IntrinsicInfo(0x5e20b000u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqdmlslV, new IntrinsicInfo(0x0e20b000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqdmulhSe, new IntrinsicInfo(0x5f00c000u, IntrinsicType.ScalarBinaryByElem)); + Add(Intrinsic.Arm64SqdmulhVe, new IntrinsicInfo(0x0f00c000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64SqdmulhS, new IntrinsicInfo(0x5e20b400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqdmulhV, new IntrinsicInfo(0x0e20b400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqdmullSe, new IntrinsicInfo(0x5f00b000u, IntrinsicType.ScalarBinaryByElem)); + Add(Intrinsic.Arm64SqdmullVe, new IntrinsicInfo(0x0f00b000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64SqdmullS, new IntrinsicInfo(0x5e20d000u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqdmullV, new IntrinsicInfo(0x0e20d000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqnegS, new IntrinsicInfo(0x7e207800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64SqnegV, new IntrinsicInfo(0x2e207800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64SqrdmulhSe, new IntrinsicInfo(0x5f00d000u, IntrinsicType.ScalarBinaryByElem)); + Add(Intrinsic.Arm64SqrdmulhVe, new IntrinsicInfo(0x0f00d000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64SqrdmulhS, new IntrinsicInfo(0x7e20b400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqrdmulhV, new IntrinsicInfo(0x2e20b400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqrshlS, new IntrinsicInfo(0x5e205c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqrshlV, new IntrinsicInfo(0x0e205c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqrshrnS, new IntrinsicInfo(0x5f009c00u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64SqrshrnV, new IntrinsicInfo(0x0f009c00u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64SqrshrunS, new IntrinsicInfo(0x7f008c00u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64SqrshrunV, new IntrinsicInfo(0x2f008c00u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64SqshluS, new IntrinsicInfo(0x7f006400u, IntrinsicType.ScalarBinaryShl)); + Add(Intrinsic.Arm64SqshluV, new IntrinsicInfo(0x2f006400u, IntrinsicType.VectorBinaryShl)); + Add(Intrinsic.Arm64SqshlSi, new IntrinsicInfo(0x5f007400u, IntrinsicType.ScalarBinaryShl)); + Add(Intrinsic.Arm64SqshlVi, new IntrinsicInfo(0x0f007400u, IntrinsicType.VectorBinaryShl)); + Add(Intrinsic.Arm64SqshlS, new IntrinsicInfo(0x5e204c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqshlV, new IntrinsicInfo(0x0e204c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqshrnS, new IntrinsicInfo(0x5f009400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64SqshrnV, new IntrinsicInfo(0x0f009400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64SqshrunS, new IntrinsicInfo(0x7f008400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64SqshrunV, new IntrinsicInfo(0x2f008400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64SqsubS, new IntrinsicInfo(0x5e202c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SqsubV, new IntrinsicInfo(0x0e202c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SqxtnS, new IntrinsicInfo(0x5e214800u, IntrinsicType.ScalarBinaryRd)); + Add(Intrinsic.Arm64SqxtnV, new IntrinsicInfo(0x0e214800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64SqxtunS, new IntrinsicInfo(0x7e212800u, IntrinsicType.ScalarBinaryRd)); + Add(Intrinsic.Arm64SqxtunV, new IntrinsicInfo(0x2e212800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64SrhaddV, new IntrinsicInfo(0x0e201400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SriS, new IntrinsicInfo(0x7f004400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64SriV, new IntrinsicInfo(0x2f004400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64SrshlS, new IntrinsicInfo(0x5e205400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SrshlV, new IntrinsicInfo(0x0e205400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SrshrS, new IntrinsicInfo(0x5f002400u, IntrinsicType.ScalarBinaryShr)); + Add(Intrinsic.Arm64SrshrV, new IntrinsicInfo(0x0f002400u, IntrinsicType.VectorBinaryShr)); + Add(Intrinsic.Arm64SrsraS, new IntrinsicInfo(0x5f003400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64SrsraV, new IntrinsicInfo(0x0f003400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64SshllV, new IntrinsicInfo(0x0f00a400u, IntrinsicType.VectorBinaryShl)); + Add(Intrinsic.Arm64SshlS, new IntrinsicInfo(0x5e204400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SshlV, new IntrinsicInfo(0x0e204400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SshrS, new IntrinsicInfo(0x5f000400u, IntrinsicType.ScalarBinaryShr)); + Add(Intrinsic.Arm64SshrV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorBinaryShr)); + Add(Intrinsic.Arm64SsraS, new IntrinsicInfo(0x5f001400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64SsraV, new IntrinsicInfo(0x0f001400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64SsublV, new IntrinsicInfo(0x0e202000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SsubwV, new IntrinsicInfo(0x0e203000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64St1Vms, new IntrinsicInfo(0x0c002000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64St1Vss, new IntrinsicInfo(0x0d000000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64St2Vms, new IntrinsicInfo(0x0c008000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64St2Vss, new IntrinsicInfo(0x0d200000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64St3Vms, new IntrinsicInfo(0x0c004000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64St3Vss, new IntrinsicInfo(0x0d002000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64St4Vms, new IntrinsicInfo(0x0c000000u, IntrinsicType.VectorLdSt)); + Add(Intrinsic.Arm64St4Vss, new IntrinsicInfo(0x0d202000u, IntrinsicType.VectorLdStSs)); + Add(Intrinsic.Arm64SubhnV, new IntrinsicInfo(0x0e206000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64SubS, new IntrinsicInfo(0x7e208400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64SubV, new IntrinsicInfo(0x2e208400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64SuqaddS, new IntrinsicInfo(0x5e203800u, IntrinsicType.ScalarBinaryRd)); + Add(Intrinsic.Arm64SuqaddV, new IntrinsicInfo(0x0e203800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64TblV, new IntrinsicInfo(0x0e000000u, IntrinsicType.VectorLookupTable)); + Add(Intrinsic.Arm64TbxV, new IntrinsicInfo(0x0e001000u, IntrinsicType.VectorLookupTable)); + Add(Intrinsic.Arm64Trn1V, new IntrinsicInfo(0x0e002800u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64Trn2V, new IntrinsicInfo(0x0e006800u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UabalV, new IntrinsicInfo(0x2e205000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64UabaV, new IntrinsicInfo(0x2e207c00u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64UabdlV, new IntrinsicInfo(0x2e207000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UabdV, new IntrinsicInfo(0x2e207400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UadalpV, new IntrinsicInfo(0x2e206800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64UaddlpV, new IntrinsicInfo(0x2e202800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64UaddlvV, new IntrinsicInfo(0x2e303800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64UaddlV, new IntrinsicInfo(0x2e200000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UaddwV, new IntrinsicInfo(0x2e201000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UcvtfSFixed, new IntrinsicInfo(0x7f00e400u, IntrinsicType.ScalarFPConvFixed)); + Add(Intrinsic.Arm64UcvtfVFixed, new IntrinsicInfo(0x2f00e400u, IntrinsicType.VectorFPConvFixed)); + Add(Intrinsic.Arm64UcvtfS, new IntrinsicInfo(0x7e21d800u, IntrinsicType.ScalarUnary)); + Add(Intrinsic.Arm64UcvtfV, new IntrinsicInfo(0x2e21d800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64UcvtfGpFixed, new IntrinsicInfo(0x1e030000u, IntrinsicType.ScalarFPConvFixedGpr)); + Add(Intrinsic.Arm64UcvtfGp, new IntrinsicInfo(0x1e230000u, IntrinsicType.ScalarFPConvGpr)); + Add(Intrinsic.Arm64UhaddV, new IntrinsicInfo(0x2e200400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UhsubV, new IntrinsicInfo(0x2e202400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UmaxpV, new IntrinsicInfo(0x2e20a400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UmaxvV, new IntrinsicInfo(0x2e30a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64UmaxV, new IntrinsicInfo(0x2e206400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UminpV, new IntrinsicInfo(0x2e20ac00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UminvV, new IntrinsicInfo(0x2e31a800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64UminV, new IntrinsicInfo(0x2e206c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UmlalVe, new IntrinsicInfo(0x2f002000u, IntrinsicType.VectorTernaryRdByElem)); + Add(Intrinsic.Arm64UmlalV, new IntrinsicInfo(0x2e208000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64UmlslVe, new IntrinsicInfo(0x2f006000u, IntrinsicType.VectorTernaryRdByElem)); + Add(Intrinsic.Arm64UmlslV, new IntrinsicInfo(0x2e20a000u, IntrinsicType.VectorTernaryRd)); + Add(Intrinsic.Arm64UmovV, new IntrinsicInfo(0x0e003c00u, IntrinsicType.VectorUnaryByElem)); + Add(Intrinsic.Arm64UmullVe, new IntrinsicInfo(0x2f00a000u, IntrinsicType.VectorBinaryByElem)); + Add(Intrinsic.Arm64UmullV, new IntrinsicInfo(0x2e20c000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UqaddS, new IntrinsicInfo(0x7e200c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64UqaddV, new IntrinsicInfo(0x2e200c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UqrshlS, new IntrinsicInfo(0x7e205c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64UqrshlV, new IntrinsicInfo(0x2e205c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UqrshrnS, new IntrinsicInfo(0x7f009c00u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64UqrshrnV, new IntrinsicInfo(0x2f009c00u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64UqshlSi, new IntrinsicInfo(0x7f007400u, IntrinsicType.ScalarBinaryShl)); + Add(Intrinsic.Arm64UqshlVi, new IntrinsicInfo(0x2f007400u, IntrinsicType.VectorBinaryShl)); + Add(Intrinsic.Arm64UqshlS, new IntrinsicInfo(0x7e204c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64UqshlV, new IntrinsicInfo(0x2e204c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UqshrnS, new IntrinsicInfo(0x7f009400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64UqshrnV, new IntrinsicInfo(0x2f009400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64UqsubS, new IntrinsicInfo(0x7e202c00u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64UqsubV, new IntrinsicInfo(0x2e202c00u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UqxtnS, new IntrinsicInfo(0x7e214800u, IntrinsicType.ScalarBinaryRd)); + Add(Intrinsic.Arm64UqxtnV, new IntrinsicInfo(0x2e214800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64UrecpeV, new IntrinsicInfo(0x0ea1c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64UrhaddV, new IntrinsicInfo(0x2e201400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UrshlS, new IntrinsicInfo(0x7e205400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64UrshlV, new IntrinsicInfo(0x2e205400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UrshrS, new IntrinsicInfo(0x7f002400u, IntrinsicType.ScalarBinaryShr)); + Add(Intrinsic.Arm64UrshrV, new IntrinsicInfo(0x2f002400u, IntrinsicType.VectorBinaryShr)); + Add(Intrinsic.Arm64UrsqrteV, new IntrinsicInfo(0x2ea1c800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64UrsraS, new IntrinsicInfo(0x7f003400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64UrsraV, new IntrinsicInfo(0x2f003400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64UshllV, new IntrinsicInfo(0x2f00a400u, IntrinsicType.VectorBinaryShl)); + Add(Intrinsic.Arm64UshlS, new IntrinsicInfo(0x7e204400u, IntrinsicType.ScalarBinary)); + Add(Intrinsic.Arm64UshlV, new IntrinsicInfo(0x2e204400u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UshrS, new IntrinsicInfo(0x7f000400u, IntrinsicType.ScalarBinaryShr)); + Add(Intrinsic.Arm64UshrV, new IntrinsicInfo(0x2f000400u, IntrinsicType.VectorBinaryShr)); + Add(Intrinsic.Arm64UsqaddS, new IntrinsicInfo(0x7e203800u, IntrinsicType.ScalarBinaryRd)); + Add(Intrinsic.Arm64UsqaddV, new IntrinsicInfo(0x2e203800u, IntrinsicType.VectorBinaryRd)); + Add(Intrinsic.Arm64UsraS, new IntrinsicInfo(0x7f001400u, IntrinsicType.ScalarTernaryShrRd)); + Add(Intrinsic.Arm64UsraV, new IntrinsicInfo(0x2f001400u, IntrinsicType.VectorTernaryShrRd)); + Add(Intrinsic.Arm64UsublV, new IntrinsicInfo(0x2e202000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64UsubwV, new IntrinsicInfo(0x2e203000u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64Uzp1V, new IntrinsicInfo(0x0e001800u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64Uzp2V, new IntrinsicInfo(0x0e005800u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64XtnV, new IntrinsicInfo(0x0e212800u, IntrinsicType.VectorUnary)); + Add(Intrinsic.Arm64Zip1V, new IntrinsicInfo(0x0e003800u, IntrinsicType.VectorBinary)); + Add(Intrinsic.Arm64Zip2V, new IntrinsicInfo(0x0e007800u, IntrinsicType.VectorBinary)); + } + + private static void Add(Intrinsic intrin, IntrinsicInfo info) + { + _intrinTable[(int)intrin] = info; + } + + public static IntrinsicInfo GetInfo(Intrinsic intrin) + { + return _intrinTable[(int)intrin]; + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs b/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs new file mode 100644 index 000000000..800eca93c --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs @@ -0,0 +1,59 @@ +namespace ARMeilleure.CodeGen.Arm64 +{ + enum IntrinsicType + { + ScalarUnary, + ScalarUnaryByElem, + ScalarBinary, + ScalarBinaryByElem, + ScalarBinaryFPByElem, + ScalarBinaryRd, + ScalarBinaryShl, + ScalarBinaryShr, + ScalarFcsel, + ScalarFmovi, + ScalarFPCompare, + ScalarFPCompareCond, + ScalarFPConv, + ScalarFPConvFixed, + ScalarFPConvFixedGpr, + ScalarFPConvGpr, + ScalarTernary, + ScalarTernaryFPRdByElem, + ScalarTernaryShlRd, + ScalarTernaryShrRd, + + VectorUnary, + VectorUnaryBitwise, + VectorUnaryByElem, + VectorBinary, + VectorBinaryBitwise, + VectorBinaryBitwiseImm, + VectorBinaryByElem, + VectorBinaryFPByElem, + VectorBinaryRd, + VectorBinaryShl, + VectorBinaryShr, + VectorExt, + VectorFmovi, + VectorFPConvFixed, + VectorInsertByElem, + VectorLdSt, + VectorLdStSs, + VectorLookupTable, + VectorMovi, + VectorMvni, + VectorTernaryFPRdByElem, + VectorTernaryRd, + VectorTernaryRdBitwise, + VectorTernaryRdByElem, + VectorTernaryShlRd, + VectorTernaryShrRd, + + Vector128Unary, + Vector128Binary, + + GetRegister, + SetRegister + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/PreAllocator.cs b/ARMeilleure/CodeGen/Arm64/PreAllocator.cs new file mode 100644 index 000000000..a7f073946 --- /dev/null +++ b/ARMeilleure/CodeGen/Arm64/PreAllocator.cs @@ -0,0 +1,940 @@ +using ARMeilleure.CodeGen.RegisterAllocators; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; +using static ARMeilleure.IntermediateRepresentation.Operation.Factory; + +namespace ARMeilleure.CodeGen.Arm64 +{ + class PreAllocator + { + private class ConstantDict + { + private readonly Dictionary<(ulong, OperandType), Operand> _constants; + + public ConstantDict() + { + _constants = new Dictionary<(ulong, OperandType), Operand>(); + } + + public void Add(ulong value, OperandType type, Operand local) + { + _constants.Add((value, type), local); + } + + public bool TryGetValue(ulong value, OperandType type, out Operand local) + { + return _constants.TryGetValue((value, type), out local); + } + } + + public static void RunPass(CompilerContext cctx, StackAllocator stackAlloc, out int maxCallArgs) + { + maxCallArgs = -1; + + Span buffer = default; + + Operand[] preservedArgs = new Operand[CallingConvention.GetArgumentsOnRegsCount()]; + + for (BasicBlock block = cctx.Cfg.Blocks.First; block != null; block = block.ListNext) + { + ConstantDict constants = new ConstantDict(); + + Operation nextNode; + + for (Operation node = block.Operations.First; node != default; node = nextNode) + { + nextNode = node.ListNext; + + if (node.Instruction == Instruction.Phi) + { + continue; + } + + HandleConstantRegCopy(constants, block.Operations, node); + HandleDestructiveRegCopy(block.Operations, node); + + switch (node.Instruction) + { + case Instruction.Call: + // Get the maximum number of arguments used on a call. + // On windows, when a struct is returned from the call, + // we also need to pass the pointer where the struct + // should be written on the first argument. + int argsCount = node.SourcesCount - 1; + + if (node.Destination != default && node.Destination.Type == OperandType.V128) + { + argsCount++; + } + + if (maxCallArgs < argsCount) + { + maxCallArgs = argsCount; + } + + // Copy values to registers expected by the function + // being called, as mandated by the ABI. + HandleCall(constants, block.Operations, node); + break; + case Instruction.CompareAndSwap: + case Instruction.CompareAndSwap16: + case Instruction.CompareAndSwap8: + nextNode = HandleCompareAndSwap(block.Operations, node); + break; + case Instruction.LoadArgument: + nextNode = HandleLoadArgument(cctx, ref buffer, block.Operations, preservedArgs, node); + break; + case Instruction.Return: + HandleReturn(block.Operations, node); + break; + case Instruction.Tailcall: + HandleTailcall(constants, block.Operations, stackAlloc, node, node); + break; + } + } + } + } + + private static void HandleConstantRegCopy(ConstantDict constants, IntrusiveList nodes, Operation node) + { + if (node.SourcesCount == 0 || IsIntrinsicWithConst(node)) + { + return; + } + + Instruction inst = node.Instruction; + + Operand src1 = node.GetSource(0); + Operand src2; + + if (src1.Kind == OperandKind.Constant) + { + if (!src1.Type.IsInteger()) + { + // Handle non-integer types (FP32, FP64 and V128). + // For instructions without an immediate operand, we do the following: + // - Insert a copy with the constant value (as integer) to a GPR. + // - Insert a copy from the GPR to a XMM register. + // - Replace the constant use with the XMM register. + src1 = AddFloatConstantCopy(constants, nodes, node, src1); + + node.SetSource(0, src1); + } + else if (!HasConstSrc1(node, src1.Value)) + { + // Handle integer types. + // Most ALU instructions accepts a 32-bits immediate on the second operand. + // We need to ensure the following: + // - If the constant is on operand 1, we need to move it. + // -- But first, we try to swap operand 1 and 2 if the instruction is commutative. + // -- Doing so may allow us to encode the constant as operand 2 and avoid a copy. + // - If the constant is on operand 2, we check if the instruction supports it, + // if not, we also add a copy. 64-bits constants are usually not supported. + if (IsCommutative(node)) + { + src2 = node.GetSource(1); + + Operand temp = src1; + + src1 = src2; + src2 = temp; + + node.SetSource(0, src1); + node.SetSource(1, src2); + } + + if (src1.Kind == OperandKind.Constant) + { + src1 = AddIntConstantCopy(constants, nodes, node, src1); + + node.SetSource(0, src1); + } + } + } + + if (node.SourcesCount < 2) + { + return; + } + + src2 = node.GetSource(1); + + if (src2.Kind == OperandKind.Constant) + { + if (!src2.Type.IsInteger()) + { + src2 = AddFloatConstantCopy(constants, nodes, node, src2); + + node.SetSource(1, src2); + } + else if (!HasConstSrc2(inst, src2)) + { + src2 = AddIntConstantCopy(constants, nodes, node, src2); + + node.SetSource(1, src2); + } + } + + if (node.SourcesCount < 3 || + node.Instruction == Instruction.BranchIf || + node.Instruction == Instruction.Compare || + node.Instruction == Instruction.VectorInsert || + node.Instruction == Instruction.VectorInsert16 || + node.Instruction == Instruction.VectorInsert8) + { + return; + } + + for (int srcIndex = 2; srcIndex < node.SourcesCount; srcIndex++) + { + Operand src = node.GetSource(srcIndex); + + if (src.Kind == OperandKind.Constant) + { + if (!src.Type.IsInteger()) + { + src = AddFloatConstantCopy(constants, nodes, node, src); + + node.SetSource(srcIndex, src); + } + else + { + src = AddIntConstantCopy(constants, nodes, node, src); + + node.SetSource(srcIndex, src); + } + } + } + } + + private static void HandleDestructiveRegCopy(IntrusiveList nodes, Operation node) + { + if (node.Destination == default || node.SourcesCount == 0) + { + return; + } + + Operand dest = node.Destination; + Operand src1 = node.GetSource(0); + + if (IsSameOperandDestSrc1(node) && src1.Kind == OperandKind.LocalVariable) + { + bool useNewLocal = false; + + for (int srcIndex = 1; srcIndex < node.SourcesCount; srcIndex++) + { + if (node.GetSource(srcIndex) == dest) + { + useNewLocal = true; + + break; + } + } + + if (useNewLocal) + { + // Dest is being used as some source already, we need to use a new + // local to store the temporary value, otherwise the value on dest + // local would be overwritten. + Operand temp = Local(dest.Type); + + nodes.AddBefore(node, Operation(Instruction.Copy, temp, src1)); + + node.SetSource(0, temp); + + nodes.AddAfter(node, Operation(Instruction.Copy, dest, temp)); + + node.Destination = temp; + } + else + { + nodes.AddBefore(node, Operation(Instruction.Copy, dest, src1)); + + node.SetSource(0, dest); + } + } + } + + private static void HandleCall(ConstantDict constants, IntrusiveList nodes, Operation node) + { + Operation operation = node; + + Operand dest = operation.Destination; + + List sources = new List + { + operation.GetSource(0) + }; + + int argsCount = operation.SourcesCount - 1; + + int intMax = CallingConvention.GetArgumentsOnRegsCount(); + int vecMax = CallingConvention.GetArgumentsOnRegsCount(); + + int intCount = 0; + int vecCount = 0; + + int stackOffset = 0; + + for (int index = 0; index < argsCount; index++) + { + Operand source = operation.GetSource(index + 1); + + bool passOnReg; + + if (source.Type.IsInteger()) + { + passOnReg = intCount < intMax; + } + else if (source.Type == OperandType.V128) + { + passOnReg = intCount + 1 < intMax; + } + else + { + passOnReg = vecCount < vecMax; + } + + if (source.Type == OperandType.V128 && passOnReg) + { + // V128 is a struct, we pass each half on a GPR if possible. + Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); + + continue; + } + + if (passOnReg) + { + Operand argReg = source.Type.IsInteger() + ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) + : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); + + Operation copyOp = Operation(Instruction.Copy, argReg, source); + + HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp)); + + sources.Add(argReg); + } + else + { + Operand offset = Const(stackOffset); + + Operation spillOp = Operation(Instruction.SpillArg, default, offset, source); + + HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, spillOp)); + + stackOffset += source.Type.GetSizeInBytes(); + } + } + + if (dest != default) + { + if (dest.Type == OperandType.V128) + { + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); + + node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, retLReg)); + nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, retHReg, Const(1))); + + operation.Destination = default; + } + else + { + Operand retReg = dest.Type.IsInteger() + ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) + : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); + + Operation copyOp = Operation(Instruction.Copy, dest, retReg); + + nodes.AddAfter(node, copyOp); + + operation.Destination = retReg; + } + } + + operation.SetSources(sources.ToArray()); + } + + private static void HandleTailcall( + ConstantDict constants, + IntrusiveList nodes, + StackAllocator stackAlloc, + Operation node, + Operation operation) + { + List sources = new List + { + operation.GetSource(0) + }; + + int argsCount = operation.SourcesCount - 1; + + int intMax = CallingConvention.GetArgumentsOnRegsCount(); + int vecMax = CallingConvention.GetArgumentsOnRegsCount(); + + int intCount = 0; + int vecCount = 0; + + // Handle arguments passed on registers. + for (int index = 0; index < argsCount; index++) + { + Operand source = operation.GetSource(1 + index); + + bool passOnReg; + + if (source.Type.IsInteger()) + { + passOnReg = intCount + 1 < intMax; + } + else + { + passOnReg = vecCount < vecMax; + } + + if (source.Type == OperandType.V128 && passOnReg) + { + // V128 is a struct, we pass each half on a GPR if possible. + Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); + + continue; + } + + if (passOnReg) + { + Operand argReg = source.Type.IsInteger() + ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) + : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); + + Operation copyOp = Operation(Instruction.Copy, argReg, source); + + HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp)); + + sources.Add(argReg); + } + else + { + throw new NotImplementedException("Spilling is not currently supported for tail calls. (too many arguments)"); + } + } + + // The target address must be on the return registers, since we + // don't return anything and it is guaranteed to not be a + // callee saved register (which would be trashed on the epilogue). + Operand tcAddress = Gpr(CodeGenCommon.TcAddressRegister, OperandType.I64); + + Operation addrCopyOp = Operation(Instruction.Copy, tcAddress, operation.GetSource(0)); + + nodes.AddBefore(node, addrCopyOp); + + sources[0] = tcAddress; + + operation.SetSources(sources.ToArray()); + } + + private static Operation HandleCompareAndSwap(IntrusiveList nodes, Operation node) + { + Operand expected = node.GetSource(1); + + if (expected.Type == OperandType.V128) + { + Operand dest = node.Destination; + Operand expectedLow = Local(OperandType.I64); + Operand expectedHigh = Local(OperandType.I64); + Operand desiredLow = Local(OperandType.I64); + Operand desiredHigh = Local(OperandType.I64); + Operand actualLow = Local(OperandType.I64); + Operand actualHigh = Local(OperandType.I64); + + Operand address = node.GetSource(0); + Operand desired = node.GetSource(2); + + void SplitOperand(Operand source, Operand low, Operand high) + { + nodes.AddBefore(node, Operation(Instruction.VectorExtract, low, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, high, source, Const(1))); + } + + SplitOperand(expected, expectedLow, expectedHigh); + SplitOperand(desired, desiredLow, desiredHigh); + + Operation operation = node; + + // Update the sources and destinations with split 64-bit halfs of the whole 128-bit values. + // We also need a additional registers that will be used to store temporary information. + operation.SetDestinations(new[] { actualLow, actualHigh, Local(OperandType.I64), Local(OperandType.I64) }); + operation.SetSources(new[] { address, expectedLow, expectedHigh, desiredLow, desiredHigh }); + + // Add some dummy uses of the input operands, as the CAS operation will be a loop, + // so they can't be used as destination operand. + for (int i = 0; i < operation.SourcesCount; i++) + { + Operand src = operation.GetSource(i); + node = nodes.AddAfter(node, Operation(Instruction.Copy, src, src)); + } + + // Assemble the vector with the 64-bit values at the given memory location. + node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, actualLow)); + node = nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, actualHigh, Const(1))); + } + else + { + // We need a additional register where the store result will be written to. + node.SetDestinations(new[] { node.Destination, Local(OperandType.I32) }); + + // Add some dummy uses of the input operands, as the CAS operation will be a loop, + // so they can't be used as destination operand. + Operation operation = node; + + for (int i = 0; i < operation.SourcesCount; i++) + { + Operand src = operation.GetSource(i); + node = nodes.AddAfter(node, Operation(Instruction.Copy, src, src)); + } + } + + return node.ListNext; + } + + private static void HandleReturn(IntrusiveList nodes, Operation node) + { + if (node.SourcesCount == 0) + { + return; + } + + Operand source = node.GetSource(0); + + if (source.Type == OperandType.V128) + { + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); + + nodes.AddBefore(node, Operation(Instruction.VectorExtract, retLReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, retHReg, source, Const(1))); + } + else + { + Operand retReg = source.Type.IsInteger() + ? Gpr(CallingConvention.GetIntReturnRegister(), source.Type) + : Xmm(CallingConvention.GetVecReturnRegister(), source.Type); + + Operation retCopyOp = Operation(Instruction.Copy, retReg, source); + + nodes.AddBefore(node, retCopyOp); + } + } + + private static Operation HandleLoadArgument( + CompilerContext cctx, + ref Span buffer, + IntrusiveList nodes, + Operand[] preservedArgs, + Operation node) + { + Operand source = node.GetSource(0); + + Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind."); + + int index = source.AsInt32(); + + int intCount = 0; + int vecCount = 0; + + for (int cIndex = 0; cIndex < index; cIndex++) + { + OperandType argType = cctx.FuncArgTypes[cIndex]; + + if (argType.IsInteger()) + { + intCount++; + } + else if (argType == OperandType.V128) + { + intCount += 2; + } + else + { + vecCount++; + } + } + + bool passOnReg; + + if (source.Type.IsInteger()) + { + passOnReg = intCount < CallingConvention.GetArgumentsOnRegsCount(); + } + else if (source.Type == OperandType.V128) + { + passOnReg = intCount + 1 < CallingConvention.GetArgumentsOnRegsCount(); + } + else + { + passOnReg = vecCount < CallingConvention.GetArgumentsOnRegsCount(); + } + + if (passOnReg) + { + Operand dest = node.Destination; + + if (preservedArgs[index] == default) + { + if (dest.Type == OperandType.V128) + { + // V128 is a struct, we pass each half on a GPR if possible. + Operand pArg = Local(OperandType.V128); + + Operand argLReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount), OperandType.I64); + Operand argHReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount + 1), OperandType.I64); + + Operation copyL = Operation(Instruction.VectorCreateScalar, pArg, argLReg); + Operation copyH = Operation(Instruction.VectorInsert, pArg, pArg, argHReg, Const(1)); + + cctx.Cfg.Entry.Operations.AddFirst(copyH); + cctx.Cfg.Entry.Operations.AddFirst(copyL); + + preservedArgs[index] = pArg; + } + else + { + Operand pArg = Local(dest.Type); + + Operand argReg = dest.Type.IsInteger() + ? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type) + : Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type); + + Operation copyOp = Operation(Instruction.Copy, pArg, argReg); + + cctx.Cfg.Entry.Operations.AddFirst(copyOp); + + preservedArgs[index] = pArg; + } + } + + Operation nextNode; + + if (dest.AssignmentsCount == 1) + { + // Let's propagate the argument if we can to avoid copies. + Propagate(ref buffer, dest, preservedArgs[index]); + nextNode = node.ListNext; + } + else + { + Operation argCopyOp = Operation(Instruction.Copy, dest, preservedArgs[index]); + nextNode = nodes.AddBefore(node, argCopyOp); + } + + Delete(nodes, node); + return nextNode; + } + else + { + // TODO: Pass on stack. + return node; + } + } + + private static void Propagate(ref Span buffer, Operand dest, Operand value) + { + ReadOnlySpan uses = dest.GetUses(ref buffer); + + foreach (Operation use in uses) + { + for (int srcIndex = 0; srcIndex < use.SourcesCount; srcIndex++) + { + Operand useSrc = use.GetSource(srcIndex); + + if (useSrc == dest) + { + use.SetSource(srcIndex, value); + } + else if (useSrc.Kind == OperandKind.Memory) + { + MemoryOperand memoryOp = useSrc.GetMemory(); + + Operand baseAddr = memoryOp.BaseAddress; + Operand index = memoryOp.Index; + bool changed = false; + + if (baseAddr == dest) + { + baseAddr = value; + changed = true; + } + + if (index == dest) + { + index = value; + changed = true; + } + + if (changed) + { + use.SetSource(srcIndex, MemoryOp( + useSrc.Type, + baseAddr, + index, + memoryOp.Scale, + memoryOp.Displacement)); + } + } + } + } + } + + private static Operand AddFloatConstantCopy( + ConstantDict constants, + IntrusiveList nodes, + Operation node, + Operand source) + { + Operand temp = Local(source.Type); + + Operand intConst = AddIntConstantCopy(constants, nodes, node, GetIntConst(source)); + + Operation copyOp = Operation(Instruction.VectorCreateScalar, temp, intConst); + + nodes.AddBefore(node, copyOp); + + return temp; + } + + private static Operand AddIntConstantCopy( + ConstantDict constants, + IntrusiveList nodes, + Operation node, + Operand source) + { + if (constants.TryGetValue(source.Value, source.Type, out Operand temp)) + { + return temp; + } + + temp = Local(source.Type); + + Operation copyOp = Operation(Instruction.Copy, temp, source); + + nodes.AddBefore(node, copyOp); + + constants.Add(source.Value, source.Type, temp); + + return temp; + } + + private static Operand GetIntConst(Operand value) + { + if (value.Type == OperandType.FP32) + { + return Const(value.AsInt32()); + } + else if (value.Type == OperandType.FP64) + { + return Const(value.AsInt64()); + } + + return value; + } + + private static void Delete(IntrusiveList nodes, Operation node) + { + node.Destination = default; + + for (int index = 0; index < node.SourcesCount; index++) + { + node.SetSource(index, default); + } + + nodes.Remove(node); + } + + private static Operand Gpr(int register, OperandType type) + { + return Register(register, RegisterType.Integer, type); + } + + private static Operand Xmm(int register, OperandType type) + { + return Register(register, RegisterType.Vector, type); + } + + private static bool IsSameOperandDestSrc1(Operation operation) + { + switch (operation.Instruction) + { + case Instruction.Extended: + return IsSameOperandDestSrc1(operation.Intrinsic); + case Instruction.VectorInsert: + case Instruction.VectorInsert16: + case Instruction.VectorInsert8: + return true; + } + + return false; + } + + private static bool IsSameOperandDestSrc1(Intrinsic intrinsic) + { + IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask)); + + return info.Type == IntrinsicType.ScalarBinaryRd || + info.Type == IntrinsicType.ScalarTernaryFPRdByElem || + info.Type == IntrinsicType.ScalarTernaryShlRd || + info.Type == IntrinsicType.ScalarTernaryShrRd || + info.Type == IntrinsicType.VectorBinaryRd || + info.Type == IntrinsicType.VectorInsertByElem || + info.Type == IntrinsicType.VectorTernaryRd || + info.Type == IntrinsicType.VectorTernaryRdBitwise || + info.Type == IntrinsicType.VectorTernaryFPRdByElem || + info.Type == IntrinsicType.VectorTernaryRdByElem || + info.Type == IntrinsicType.VectorTernaryShlRd || + info.Type == IntrinsicType.VectorTernaryShrRd; + } + + private static bool HasConstSrc1(Operation node, ulong value) + { + switch (node.Instruction) + { + case Instruction.Add: + case Instruction.BranchIf: + case Instruction.Compare: + case Instruction.Subtract: + // The immediate encoding of those instructions does not allow Rn to be + // XZR (it will be SP instead), so we can't allow a Rn constant in this case. + return value == 0 && NotConstOrConst0(node.GetSource(1)); + case Instruction.BitwiseAnd: + case Instruction.BitwiseExclusiveOr: + case Instruction.BitwiseNot: + case Instruction.BitwiseOr: + case Instruction.ByteSwap: + case Instruction.CountLeadingZeros: + case Instruction.Multiply: + case Instruction.Negate: + case Instruction.RotateRight: + case Instruction.ShiftLeft: + case Instruction.ShiftRightSI: + case Instruction.ShiftRightUI: + return value == 0; + case Instruction.Copy: + case Instruction.LoadArgument: + case Instruction.Spill: + case Instruction.SpillArg: + return true; + case Instruction.Extended: + return value == 0; + } + + return false; + } + + private static bool NotConstOrConst0(Operand operand) + { + return operand.Kind != OperandKind.Constant || operand.Value == 0; + } + + private static bool HasConstSrc2(Instruction inst, Operand operand) + { + ulong value = operand.Value; + + switch (inst) + { + case Instruction.Add: + case Instruction.BranchIf: + case Instruction.Compare: + case Instruction.Subtract: + return ConstFitsOnUImm12Sh(value); + case Instruction.BitwiseAnd: + case Instruction.BitwiseExclusiveOr: + case Instruction.BitwiseOr: + return value == 0 || CodeGenCommon.TryEncodeBitMask(operand, out _, out _, out _); + case Instruction.Multiply: + case Instruction.Store: + case Instruction.Store16: + case Instruction.Store8: + return value == 0; + case Instruction.RotateRight: + case Instruction.ShiftLeft: + case Instruction.ShiftRightSI: + case Instruction.ShiftRightUI: + case Instruction.VectorExtract: + case Instruction.VectorExtract16: + case Instruction.VectorExtract8: + return true; + case Instruction.Extended: + // TODO: Check if actual intrinsic is supposed to have consts here? + // Right now we only hit this case for fixed-point int <-> FP conversion instructions. + return true; + } + + return false; + } + + private static bool IsCommutative(Operation operation) + { + switch (operation.Instruction) + { + case Instruction.Add: + case Instruction.BitwiseAnd: + case Instruction.BitwiseExclusiveOr: + case Instruction.BitwiseOr: + case Instruction.Multiply: + return true; + + case Instruction.BranchIf: + case Instruction.Compare: + { + Operand comp = operation.GetSource(2); + + Debug.Assert(comp.Kind == OperandKind.Constant); + + var compType = (Comparison)comp.AsInt32(); + + return compType == Comparison.Equal || compType == Comparison.NotEqual; + } + } + + return false; + } + + private static bool ConstFitsOnUImm12Sh(ulong value) + { + return (value & ~0xfffUL) == 0 || (value & ~0xfff000UL) == 0; + } + + private static bool IsIntrinsicWithConst(Operation operation) + { + bool isIntrinsic = IsIntrinsic(operation.Instruction); + + if (isIntrinsic) + { + Intrinsic intrinsic = operation.Intrinsic; + IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask)); + + // Those have integer inputs that don't support consts. + return info.Type != IntrinsicType.ScalarFPConvGpr && + info.Type != IntrinsicType.ScalarFPConvFixedGpr && + info.Type != IntrinsicType.SetRegister; + } + + return false; + } + + private static bool IsIntrinsic(Instruction inst) + { + return inst == Instruction.Extended; + } + } +} diff --git a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs b/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs index 0423c2559..c5a22a537 100644 --- a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs +++ b/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs @@ -90,6 +90,47 @@ namespace ARMeilleure.CodeGen.Optimizations } break; + case Instruction.Compare: + if (type == OperandType.I32 && + operation.GetSource(0).Type == type && + operation.GetSource(1).Type == type) + { + switch ((Comparison)operation.GetSource(2).Value) + { + case Comparison.Equal: + EvaluateBinaryI32(operation, (x, y) => x == y ? 1 : 0); + break; + case Comparison.NotEqual: + EvaluateBinaryI32(operation, (x, y) => x != y ? 1 : 0); + break; + case Comparison.Greater: + EvaluateBinaryI32(operation, (x, y) => x > y ? 1 : 0); + break; + case Comparison.LessOrEqual: + EvaluateBinaryI32(operation, (x, y) => x <= y ? 1 : 0); + break; + case Comparison.GreaterUI: + EvaluateBinaryI32(operation, (x, y) => (uint)x > (uint)y ? 1 : 0); + break; + case Comparison.LessOrEqualUI: + EvaluateBinaryI32(operation, (x, y) => (uint)x <= (uint)y ? 1 : 0); + break; + case Comparison.GreaterOrEqual: + EvaluateBinaryI32(operation, (x, y) => x >= y ? 1 : 0); + break; + case Comparison.Less: + EvaluateBinaryI32(operation, (x, y) => x < y ? 1 : 0); + break; + case Comparison.GreaterOrEqualUI: + EvaluateBinaryI32(operation, (x, y) => (uint)x >= (uint)y ? 1 : 0); + break; + case Comparison.LessUI: + EvaluateBinaryI32(operation, (x, y) => (uint)x < (uint)y ? 1 : 0); + break; + } + } + break; + case Instruction.Copy: if (type == OperandType.I32) { diff --git a/ARMeilleure/CodeGen/Optimizations/Optimizer.cs b/ARMeilleure/CodeGen/Optimizations/Optimizer.cs index 919e996bd..a45bb4551 100644 --- a/ARMeilleure/CodeGen/Optimizations/Optimizer.cs +++ b/ARMeilleure/CodeGen/Optimizations/Optimizer.cs @@ -44,8 +44,8 @@ namespace ARMeilleure.CodeGen.Optimizations ConstantFolding.RunPass(node); Simplification.RunPass(node); - if (DestIsLocalVar(node)) - { + if (DestIsSingleLocalVar(node)) + { if (IsPropagableCompare(node)) { modified |= PropagateCompare(ref buffer, node); @@ -99,20 +99,6 @@ namespace ARMeilleure.CodeGen.Optimizations while (modified); } - private static Span GetUses(ref Span buffer, Operand operand) - { - ReadOnlySpan uses = operand.Uses; - - if (buffer.Length < uses.Length) - { - buffer = Allocators.Default.AllocateSpan((uint)uses.Length); - } - - uses.CopyTo(buffer); - - return buffer.Slice(0, uses.Length); - } - private static bool PropagateCompare(ref Span buffer, Operation compOp) { // Try to propagate Compare operations into their BranchIf uses, when these BranchIf uses are in the form @@ -160,7 +146,7 @@ namespace ARMeilleure.CodeGen.Optimizations Comparison compType = (Comparison)comp.AsInt32(); - Span uses = GetUses(ref buffer, dest); + Span uses = dest.GetUses(ref buffer); foreach (Operation use in uses) { @@ -199,7 +185,7 @@ namespace ARMeilleure.CodeGen.Optimizations Operand dest = copyOp.Destination; Operand source = copyOp.GetSource(0); - Span uses = GetUses(ref buffer, dest); + Span uses = dest.GetUses(ref buffer); foreach (Operation use in uses) { @@ -231,12 +217,12 @@ namespace ARMeilleure.CodeGen.Optimizations private static bool IsUnused(Operation node) { - return DestIsLocalVar(node) && node.Destination.UsesCount == 0 && !HasSideEffects(node); + return DestIsSingleLocalVar(node) && node.Destination.UsesCount == 0 && !HasSideEffects(node); } - private static bool DestIsLocalVar(Operation node) + private static bool DestIsSingleLocalVar(Operation node) { - return node.Destination != default && node.Destination.Kind == OperandKind.LocalVariable; + return node.DestinationsCount == 1 && node.Destination.Kind == OperandKind.LocalVariable; } private static bool HasSideEffects(Operation node) diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index d8a40365b..6ea62c28b 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -17,8 +17,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private const int InstructionGap = 2; private const int InstructionGapMask = InstructionGap - 1; - private const int RegistersCount = 16; - private HashSet _blockEdges; private LiveRange[] _blockRanges; private BitMap[] _blockLiveIn; @@ -59,7 +57,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators void PopulateFreePositions(RegisterType type, out int[] positions, out int count) { - positions = new int[RegistersCount]; + positions = new int[masks.RegistersCount]; count = BitOperations.PopCount((uint)masks.GetAvailableRegisters(type)); int mask = masks.GetAvailableRegisters(type); @@ -115,7 +113,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators StackAllocator stackAlloc, RegisterMasks regMasks) { - NumberLocals(cfg); + NumberLocals(cfg, regMasks.RegistersCount); var context = new AllocationContext(stackAlloc, regMasks, _intervals.Count); @@ -134,22 +132,25 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { context.Active.Set(index); - if (current.Register.Type == RegisterType.Integer) + if (current.IsFixedAndUsed) { - context.IntUsedRegisters |= 1 << current.Register.Index; - } - else /* if (interval.Register.Type == RegisterType.Vector) */ - { - context.VecUsedRegisters |= 1 << current.Register.Index; + if (current.Register.Type == RegisterType.Integer) + { + context.IntUsedRegisters |= 1 << current.Register.Index; + } + else /* if (interval.Register.Type == RegisterType.Vector) */ + { + context.VecUsedRegisters |= 1 << current.Register.Index; + } } continue; } - AllocateInterval(context, current, index); + AllocateInterval(context, current, index, regMasks.RegistersCount); } - for (int index = RegistersCount * 2; index < _intervals.Count; index++) + for (int index = regMasks.RegistersCount * 2; index < _intervals.Count; index++) { if (!_intervals[index].IsSpilled) { @@ -163,7 +164,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return new AllocationResult(context.IntUsedRegisters, context.VecUsedRegisters, context.StackAlloc.TotalSize); } - private void AllocateInterval(AllocationContext context, LiveInterval current, int cIndex) + private void AllocateInterval(AllocationContext context, LiveInterval current, int cIndex, int registersCount) { // Check active intervals that already ended. foreach (int iIndex in context.Active) @@ -199,17 +200,17 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } - if (!TryAllocateRegWithoutSpill(context, current, cIndex)) + if (!TryAllocateRegWithoutSpill(context, current, cIndex, registersCount)) { - AllocateRegWithSpill(context, current, cIndex); + AllocateRegWithSpill(context, current, cIndex, registersCount); } } - private bool TryAllocateRegWithoutSpill(AllocationContext context, LiveInterval current, int cIndex) + private bool TryAllocateRegWithoutSpill(AllocationContext context, LiveInterval current, int cIndex, int registersCount) { RegisterType regType = current.Local.Type.ToRegisterType(); - Span freePositions = stackalloc int[RegistersCount]; + Span freePositions = stackalloc int[registersCount]; context.GetFreePositions(regType, freePositions, out int freePositionsCount); @@ -278,7 +279,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { Debug.Assert(splitChild.GetStart() > current.GetStart(), "Split interval has an invalid start position."); - InsertInterval(splitChild); + InsertInterval(splitChild, registersCount); } else { @@ -302,12 +303,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return true; } - private void AllocateRegWithSpill(AllocationContext context, LiveInterval current, int cIndex) + private void AllocateRegWithSpill(AllocationContext context, LiveInterval current, int cIndex, int registersCount) { RegisterType regType = current.Local.Type.ToRegisterType(); - Span usePositions = stackalloc int[RegistersCount]; - Span blockedPositions = stackalloc int[RegistersCount]; + Span usePositions = stackalloc int[registersCount]; + Span blockedPositions = stackalloc int[registersCount]; context.GetFreePositions(regType, usePositions, out _); context.GetFreePositions(regType, blockedPositions, out _); @@ -386,7 +387,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators Debug.Assert(splitChild.GetStart() > current.GetStart(), "Split interval has an invalid start position."); - InsertInterval(splitChild); + InsertInterval(splitChild, registersCount); Spill(context, current); } @@ -396,7 +397,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators // so we only need to split the intervals using the selected register. current.Register = new Register(selectedReg, regType); - SplitAndSpillOverlappingIntervals(context, current); + SplitAndSpillOverlappingIntervals(context, current, registersCount); context.Active.Set(cIndex); } @@ -417,14 +418,14 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { Debug.Assert(splitChild.GetStart() > current.GetStart(), "Split interval has an invalid start position."); - InsertInterval(splitChild); + InsertInterval(splitChild, registersCount); } else { Spill(context, splitChild); } - SplitAndSpillOverlappingIntervals(context, current); + SplitAndSpillOverlappingIntervals(context, current, registersCount); context.Active.Set(cIndex); } @@ -460,7 +461,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return selected; } - private void SplitAndSpillOverlappingIntervals(AllocationContext context, LiveInterval current) + private void SplitAndSpillOverlappingIntervals(AllocationContext context, LiveInterval current, int registersCount) { foreach (int iIndex in context.Active) { @@ -468,7 +469,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (!interval.IsFixed && interval.Register == current.Register) { - SplitAndSpillOverlappingInterval(context, current, interval); + SplitAndSpillOverlappingInterval(context, current, interval, registersCount); context.Active.Clear(iIndex); } @@ -480,7 +481,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (!interval.IsFixed && interval.Register == current.Register && interval.Overlaps(current)) { - SplitAndSpillOverlappingInterval(context, current, interval); + SplitAndSpillOverlappingInterval(context, current, interval, registersCount); context.Inactive.Clear(iIndex); } @@ -490,7 +491,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void SplitAndSpillOverlappingInterval( AllocationContext context, LiveInterval current, - LiveInterval interval) + LiveInterval interval, + int registersCount) { // If there's a next use after the start of the current interval, // we need to split the spilled interval twice, and re-insert it @@ -522,7 +524,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators splitChild = right; } - InsertInterval(splitChild); + InsertInterval(splitChild, registersCount); } else { @@ -530,13 +532,13 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } - private void InsertInterval(LiveInterval interval) + private void InsertInterval(LiveInterval interval, int registersCount) { Debug.Assert(interval.UsesCount != 0, "Trying to insert a interval without uses."); Debug.Assert(!interval.IsEmpty, "Trying to insert a empty interval."); Debug.Assert(!interval.IsSpilled, "Trying to insert a spilled interval."); - int startIndex = RegistersCount * 2; + int startIndex = registersCount * 2; int insertIndex = _intervals.BinarySearch(startIndex, _intervals.Count - startIndex, interval, null); @@ -790,12 +792,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return _operationNodes[position / InstructionGap]; } - private void NumberLocals(ControlFlowGraph cfg) + private void NumberLocals(ControlFlowGraph cfg, int registersCount) { _operationNodes = new List<(IntrusiveList, Operation)>(); _intervals = new List(); - for (int index = 0; index < RegistersCount; index++) + for (int index = 0; index < registersCount; index++) { _intervals.Add(new LiveInterval(new Register(index, RegisterType.Integer))); _intervals.Add(new LiveInterval(new Register(index, RegisterType.Vector))); @@ -1041,6 +1043,11 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { LiveInterval interval = _intervals[GetOperandId(dest)]; + if (interval.IsFixed) + { + interval.IsFixedAndUsed = true; + } + interval.SetStart(operationPos + 1); interval.AddUsePosition(operationPos + 1); } diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs b/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs index 77ad95416..d739ad281 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs @@ -27,6 +27,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public Register Register; public bool IsFixed; + public bool IsFixedAndUsed; } private readonly Data* _data; @@ -44,6 +45,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public ref int SpillOffset => ref _data->SpillOffset; public bool IsFixed => _data->IsFixed; + public ref bool IsFixedAndUsed => ref _data->IsFixedAndUsed; public bool IsEmpty => FirstRange == default; public bool IsSplit => Children.Count != 0; public bool IsSpilled => SpillOffset != -1; @@ -114,7 +116,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } else { - FirstRange = new LiveRange(position, position + 1); + FirstRange = new LiveRange(position, position + 1); End = position + 1; } } diff --git a/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs b/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs index 5b11aac20..bc948f95f 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs @@ -11,6 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public int VecCallerSavedRegisters { get; } public int IntCalleeSavedRegisters { get; } public int VecCalleeSavedRegisters { get; } + public int RegistersCount { get; } public RegisterMasks( int intAvailableRegisters, @@ -18,7 +19,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators int intCallerSavedRegisters, int vecCallerSavedRegisters, int intCalleeSavedRegisters, - int vecCalleeSavedRegisters) + int vecCalleeSavedRegisters, + int registersCount) { IntAvailableRegisters = intAvailableRegisters; VecAvailableRegisters = vecAvailableRegisters; @@ -26,6 +28,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators VecCallerSavedRegisters = vecCallerSavedRegisters; IntCalleeSavedRegisters = intCalleeSavedRegisters; VecCalleeSavedRegisters = vecCalleeSavedRegisters; + RegistersCount = registersCount; } public int GetAvailableRegisters(RegisterType type) diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index e589da140..8b5a3fc57 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -16,6 +16,7 @@ namespace ARMeilleure.CodeGen.X86 { static class CodeGenerator { + private const int RegistersCount = 16; private const int PageSize = 0x1000; private const int StackGuardSize = 0x2000; @@ -143,7 +144,8 @@ namespace ARMeilleure.CodeGen.X86 CallingConvention.GetIntCallerSavedRegisters(), CallingConvention.GetVecCallerSavedRegisters(), CallingConvention.GetIntCalleeSavedRegisters(), - CallingConvention.GetVecCalleeSavedRegisters()); + CallingConvention.GetVecCalleeSavedRegisters(), + RegistersCount); AllocationResult allocResult = regAlloc.RunPass(cfg, stackAlloc, regMasks); diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs index 6407a9a7b..8c909ac13 100644 --- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs +++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs @@ -5,8 +5,6 @@ namespace ARMeilleure.CodeGen.X86 { static class IntrinsicTable { - private const int BadOp = 0; - private static IntrinsicInfo[] _intrinTable; static IntrinsicTable() diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs index b91c522ec..3e65db23d 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs @@ -21,22 +21,47 @@ namespace ARMeilleure.Instructions { public static void Abs_S(ArmEmitterContext context) { - EmitScalarUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOp(context, Intrinsic.Arm64AbsS); + } + else + { + EmitScalarUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + } } public static void Abs_V(ArmEmitterContext context) { - EmitVectorUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64AbsV); + } + else + { + EmitVectorUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + } } public static void Add_S(ArmEmitterContext context) { - EmitScalarBinaryOpZx(context, (op1, op2) => context.Add(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOp(context, Intrinsic.Arm64AddS); + } + else + { + EmitScalarBinaryOpZx(context, (op1, op2) => context.Add(op1, op2)); + } } public static void Add_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64AddV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -62,24 +87,42 @@ namespace ARMeilleure.Instructions public static void Addhn_V(ArmEmitterContext context) { - EmitHighNarrow(context, (op1, op2) => context.Add(op1, op2), round: false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64AddhnV); + } + else + { + EmitHighNarrow(context, (op1, op2) => context.Add(op1, op2), round: false); + } } public static void Addp_S(ArmEmitterContext context) { - OpCodeSimd op = (OpCodeSimd)context.CurrOp; + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOp(context, Intrinsic.Arm64AddpS); + } + else + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; - Operand ne0 = EmitVectorExtractZx(context, op.Rn, 0, op.Size); - Operand ne1 = EmitVectorExtractZx(context, op.Rn, 1, op.Size); + Operand ne0 = EmitVectorExtractZx(context, op.Rn, 0, op.Size); + Operand ne1 = EmitVectorExtractZx(context, op.Rn, 1, op.Size); - Operand res = context.Add(ne0, ne1); + Operand res = context.Add(ne0, ne1); - context.Copy(GetVec(op.Rd), EmitVectorInsert(context, context.VectorZero(), res, 0, op.Size)); + context.Copy(GetVec(op.Rd), EmitVectorInsert(context, context.VectorZero(), res, 0, op.Size)); + } } public static void Addp_V(ArmEmitterContext context) { - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64AddpV); + } + else if (Optimizations.UseSsse3) { EmitSsse3VectorPairwiseOp(context, X86PaddInstruction); } @@ -91,68 +134,89 @@ namespace ARMeilleure.Instructions public static void Addv_V(ArmEmitterContext context) { - EmitVectorAcrossVectorOpZx(context, (op1, op2) => context.Add(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64AddvV); + } + else + { + EmitVectorAcrossVectorOpZx(context, (op1, op2) => context.Add(op1, op2)); + } } public static void Cls_V(ArmEmitterContext context) { - OpCodeSimd op = (OpCodeSimd)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.GetBytesCount() >> op.Size; - - int eSize = 8 << op.Size; - - for (int index = 0; index < elems; index++) + if (Optimizations.UseAdvSimd) { - Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); - - Operand de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)), ne, Const(eSize)); - - res = EmitVectorInsert(context, res, de, index, op.Size); - } - - context.Copy(GetVec(op.Rd), res); - } - - public static void Clz_V(ArmEmitterContext context) - { - OpCodeSimd op = (OpCodeSimd)context.CurrOp; - - int eSize = 8 << op.Size; - - Operand res = eSize switch { - 8 => Clz_V_I8 (context, GetVec(op.Rn)), - 16 => Clz_V_I16(context, GetVec(op.Rn)), - 32 => Clz_V_I32(context, GetVec(op.Rn)), - _ => default - }; - - if (res != default) - { - if (op.RegisterSize == RegisterSize.Simd64) - { - res = context.VectorZeroUpper64(res); - } + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64ClsV); } else { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand res = context.VectorZero(); + int elems = op.GetBytesCount() >> op.Size; - res = context.VectorZero(); + int eSize = 8 << op.Size; for (int index = 0; index < elems; index++) { Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); - Operand de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)), ne, Const(eSize)); + Operand de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)), ne, Const(eSize)); res = EmitVectorInsert(context, res, de, index, op.Size); } - } - context.Copy(GetVec(op.Rd), res); + context.Copy(GetVec(op.Rd), res); + } + } + + public static void Clz_V(ArmEmitterContext context) + { + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64ClzV); + } + else + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + int eSize = 8 << op.Size; + + Operand res = eSize switch { + 8 => Clz_V_I8 (context, GetVec(op.Rn)), + 16 => Clz_V_I16(context, GetVec(op.Rn)), + 32 => Clz_V_I32(context, GetVec(op.Rn)), + _ => default + }; + + if (res != default) + { + if (op.RegisterSize == RegisterSize.Simd64) + { + res = context.VectorZeroUpper64(res); + } + } + else + { + int elems = op.GetBytesCount() >> op.Size; + + res = context.VectorZero(); + + for (int index = 0; index < elems; index++) + { + Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); + + Operand de = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)), ne, Const(eSize)); + + res = EmitVectorInsert(context, res, de, index, op.Size); + } + } + + context.Copy(GetVec(op.Rd), res); + } } private static Operand Clz_V_I8(ArmEmitterContext context, Operand arg) @@ -271,36 +335,47 @@ namespace ARMeilleure.Instructions public static void Cnt_V(ArmEmitterContext context) { - OpCodeSimd op = (OpCodeSimd)context.CurrOp; - - Operand res = context.VectorZero(); - - int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; - - for (int index = 0; index < elems; index++) + if (Optimizations.UseAdvSimd) { - Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0); - - Operand de; - - if (Optimizations.UsePopCnt) - { - de = context.AddIntrinsicLong(Intrinsic.X86Popcnt, ne); - } - else - { - de = EmitCountSetBits8(context, ne); - } - - res = EmitVectorInsert(context, res, de, index, 0); + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64CntV); } + else + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; - context.Copy(GetVec(op.Rd), res); + Operand res = context.VectorZero(); + + int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; + + for (int index = 0; index < elems; index++) + { + Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0); + + Operand de; + + if (Optimizations.UsePopCnt) + { + de = context.AddIntrinsicLong(Intrinsic.X86Popcnt, ne); + } + else + { + de = EmitCountSetBits8(context, ne); + } + + res = EmitVectorInsert(context, res, de, index, 0); + } + + context.Copy(GetVec(op.Rd), res); + } } public static void Fabd_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FabdS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -336,7 +411,11 @@ namespace ARMeilleure.Instructions public static void Fabd_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FabdV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -377,7 +456,11 @@ namespace ARMeilleure.Instructions public static void Fabs_S(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FabsS); + } + else if (Optimizations.UseSse2) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -405,7 +488,11 @@ namespace ARMeilleure.Instructions public static void Fabs_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FabsV); + } + else if (Optimizations.UseSse2) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -440,7 +527,11 @@ namespace ARMeilleure.Instructions public static void Fadd_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FaddS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF(context, Intrinsic.X86Addss, Intrinsic.X86Addsd); } @@ -459,7 +550,11 @@ namespace ARMeilleure.Instructions public static void Fadd_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FaddV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF(context, Intrinsic.X86Addps, Intrinsic.X86Addpd); } @@ -478,7 +573,11 @@ namespace ARMeilleure.Instructions public static void Faddp_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse3) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FaddpS); + } + else if (Optimizations.FastFP && Optimizations.UseSse3) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -506,7 +605,11 @@ namespace ARMeilleure.Instructions public static void Faddp_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FaddpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorPairwiseOpF(context, (op1, op2) => { @@ -534,7 +637,11 @@ namespace ARMeilleure.Instructions public static void Fdiv_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FdivS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF(context, Intrinsic.X86Divss, Intrinsic.X86Divsd); } @@ -553,7 +660,11 @@ namespace ARMeilleure.Instructions public static void Fdiv_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FdivV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF(context, Intrinsic.X86Divps, Intrinsic.X86Divpd); } @@ -572,7 +683,11 @@ namespace ARMeilleure.Instructions public static void Fmadd_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarTernaryOpF(context, Intrinsic.Arm64FmaddS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -607,7 +722,11 @@ namespace ARMeilleure.Instructions public static void Fmax_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FmaxS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { @@ -628,7 +747,11 @@ namespace ARMeilleure.Instructions public static void Fmax_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FmaxV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { @@ -649,7 +772,11 @@ namespace ARMeilleure.Instructions public static void Fmaxnm_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FmaxnmS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF(context, isMaxNum: true, scalar: true); } @@ -664,7 +791,11 @@ namespace ARMeilleure.Instructions public static void Fmaxnm_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FmaxnmV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF(context, isMaxNum: true, scalar: false); } @@ -679,7 +810,11 @@ namespace ARMeilleure.Instructions public static void Fmaxnmp_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FmaxnmpS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2ScalarPairwiseOpF(context, (op1, op2) => { @@ -697,7 +832,11 @@ namespace ARMeilleure.Instructions public static void Fmaxnmp_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FmaxnmpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorPairwiseOpF(context, (op1, op2) => { @@ -715,7 +854,11 @@ namespace ARMeilleure.Instructions public static void Fmaxnmv_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FmaxnmvV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorAcrossVectorOpF(context, (op1, op2) => { @@ -733,7 +876,11 @@ namespace ARMeilleure.Instructions public static void Fmaxp_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FmaxpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorPairwiseOpF(context, (op1, op2) => { @@ -757,7 +904,11 @@ namespace ARMeilleure.Instructions public static void Fmaxv_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FmaxvV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorAcrossVectorOpF(context, (op1, op2) => { @@ -781,7 +932,11 @@ namespace ARMeilleure.Instructions public static void Fmin_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FminS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { @@ -802,7 +957,11 @@ namespace ARMeilleure.Instructions public static void Fmin_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FminV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { @@ -823,7 +982,11 @@ namespace ARMeilleure.Instructions public static void Fminnm_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FminnmS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF(context, isMaxNum: false, scalar: true); } @@ -838,7 +1001,11 @@ namespace ARMeilleure.Instructions public static void Fminnm_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FminnmV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF(context, isMaxNum: false, scalar: false); } @@ -853,7 +1020,11 @@ namespace ARMeilleure.Instructions public static void Fminnmp_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FminnmpS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2ScalarPairwiseOpF(context, (op1, op2) => { @@ -871,7 +1042,11 @@ namespace ARMeilleure.Instructions public static void Fminnmp_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FminnmpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorPairwiseOpF(context, (op1, op2) => { @@ -889,7 +1064,11 @@ namespace ARMeilleure.Instructions public static void Fminnmv_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FminnmvV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorAcrossVectorOpF(context, (op1, op2) => { @@ -907,7 +1086,11 @@ namespace ARMeilleure.Instructions public static void Fminp_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FminpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorPairwiseOpF(context, (op1, op2) => { @@ -931,7 +1114,11 @@ namespace ARMeilleure.Instructions public static void Fminv_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FminvV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse2VectorAcrossVectorOpF(context, (op1, op2) => { @@ -955,15 +1142,26 @@ namespace ARMeilleure.Instructions public static void Fmla_Se(ArmEmitterContext context) // Fused. { - EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe); + } + else + { + EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => + { + return context.Add(op1, context.Multiply(op2, op3)); + }); + } } public static void Fmla_V(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpFRd(context, Intrinsic.Arm64FmlaV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1006,7 +1204,11 @@ namespace ARMeilleure.Instructions public static void Fmla_Ve(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaVe); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; @@ -1055,15 +1257,26 @@ namespace ARMeilleure.Instructions public static void Fmls_Se(ArmEmitterContext context) // Fused. { - EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Subtract(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe); + } + else + { + EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => + { + return context.Subtract(op1, context.Multiply(op2, op3)); + }); + } } public static void Fmls_V(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpFRd(context, Intrinsic.Arm64FmlsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1106,7 +1319,11 @@ namespace ARMeilleure.Instructions public static void Fmls_Ve(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsVe); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; @@ -1155,7 +1372,11 @@ namespace ARMeilleure.Instructions public static void Fmsub_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarTernaryOpF(context, Intrinsic.Arm64FmsubS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1190,7 +1411,11 @@ namespace ARMeilleure.Instructions public static void Fmul_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FmulS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd); } @@ -1209,12 +1434,23 @@ namespace ARMeilleure.Instructions public static void Fmul_Se(ArmEmitterContext context) { - EmitScalarBinaryOpByElemF(context, (op1, op2) => context.Multiply(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpFByElem(context, Intrinsic.Arm64FmulSe); + } + else + { + EmitScalarBinaryOpByElemF(context, (op1, op2) => context.Multiply(op1, op2)); + } } public static void Fmul_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FmulV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF(context, Intrinsic.X86Mulps, Intrinsic.X86Mulpd); } @@ -1233,7 +1469,11 @@ namespace ARMeilleure.Instructions public static void Fmul_Ve(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpFByElem(context, Intrinsic.Arm64FmulVe); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; @@ -1283,39 +1523,71 @@ namespace ARMeilleure.Instructions public static void Fmulx_S(ArmEmitterContext context) { - EmitScalarBinaryOpF(context, (op1, op2) => + if (Optimizations.UseAdvSimd) { - return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); - }); + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FmulxS); + } + else + { + EmitScalarBinaryOpF(context, (op1, op2) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); + }); + } } public static void Fmulx_Se(ArmEmitterContext context) { - EmitScalarBinaryOpByElemF(context, (op1, op2) => + if (Optimizations.UseAdvSimd) { - return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); - }); + InstEmitSimdHelperArm64.EmitScalarBinaryOpFByElem(context, Intrinsic.Arm64FmulxSe); + } + else + { + EmitScalarBinaryOpByElemF(context, (op1, op2) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); + }); + } } public static void Fmulx_V(ArmEmitterContext context) { - EmitVectorBinaryOpF(context, (op1, op2) => + if (Optimizations.UseAdvSimd) { - return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); - }); + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FmulxV); + } + else + { + EmitVectorBinaryOpF(context, (op1, op2) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); + }); + } } public static void Fmulx_Ve(ArmEmitterContext context) { - EmitVectorBinaryOpByElemF(context, (op1, op2) => + if (Optimizations.UseAdvSimd) { - return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); - }); + InstEmitSimdHelperArm64.EmitVectorBinaryOpFByElem(context, Intrinsic.Arm64FmulxVe); + } + else + { + EmitVectorBinaryOpByElemF(context, (op1, op2) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX), op1, op2); + }); + } } public static void Fneg_S(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FnegS); + } + else if (Optimizations.UseSse2) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -1344,7 +1616,11 @@ namespace ARMeilleure.Instructions public static void Fneg_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FnegV); + } + else if (Optimizations.UseSse2) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -1380,7 +1656,11 @@ namespace ARMeilleure.Instructions public static void Fnmadd_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarTernaryOpF(context, Intrinsic.Arm64FnmaddS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1423,7 +1703,11 @@ namespace ARMeilleure.Instructions public static void Fnmsub_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarTernaryOpF(context, Intrinsic.Arm64FnmsubS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1466,7 +1750,14 @@ namespace ARMeilleure.Instructions public static void Fnmul_S(ArmEmitterContext context) { - EmitScalarBinaryOpF(context, (op1, op2) => context.Negate(context.Multiply(op1, op2))); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FnmulS); + } + else + { + EmitScalarBinaryOpF(context, (op1, op2) => context.Negate(context.Multiply(op1, op2))); + } } public static void Frecpe_S(ArmEmitterContext context) @@ -1475,7 +1766,11 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrecpeS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) { Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rcpss, GetVec(op.Rn)), scalar: true); @@ -1496,7 +1791,11 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrecpeV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) { Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rcpps, GetVec(op.Rn)), scalar: false); @@ -1518,7 +1817,11 @@ namespace ARMeilleure.Instructions public static void Frecps_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FrecpsS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1561,7 +1864,11 @@ namespace ARMeilleure.Instructions public static void Frecps_V(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FrecpsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1609,15 +1916,26 @@ namespace ARMeilleure.Instructions public static void Frecpx_S(ArmEmitterContext context) { - EmitScalarUnaryOpF(context, (op1) => + if (Optimizations.UseAdvSimd) { - return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecpX), op1); - }); + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FrecpxS); + } + else + { + EmitScalarUnaryOpF(context, (op1) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecpX), op1); + }); + } } public static void Frinta_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintaS); + } + else if (Optimizations.UseSse41) { EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearestAway); } @@ -1632,7 +1950,11 @@ namespace ARMeilleure.Instructions public static void Frinta_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintaV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearestAway); } @@ -1647,23 +1969,41 @@ namespace ARMeilleure.Instructions public static void Frinti_S(ArmEmitterContext context) { - EmitScalarUnaryOpF(context, (op1) => + if (Optimizations.UseAdvSimd) { - return EmitRoundByRMode(context, op1); - }); + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintiS); + } + else + { + EmitScalarUnaryOpF(context, (op1) => + { + return EmitRoundByRMode(context, op1); + }); + } } public static void Frinti_V(ArmEmitterContext context) { - EmitVectorUnaryOpF(context, (op1) => + if (Optimizations.UseAdvSimd) { - return EmitRoundByRMode(context, op1); - }); + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintiV); + } + else + { + EmitVectorUnaryOpF(context, (op1) => + { + return EmitRoundByRMode(context, op1); + }); + } } public static void Frintm_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintmS); + } + else if (Optimizations.UseSse41) { EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsMinusInfinity); } @@ -1678,7 +2018,11 @@ namespace ARMeilleure.Instructions public static void Frintm_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintmV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsMinusInfinity); } @@ -1693,7 +2037,11 @@ namespace ARMeilleure.Instructions public static void Frintn_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintnS); + } + else if (Optimizations.UseSse41) { EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearest); } @@ -1708,7 +2056,11 @@ namespace ARMeilleure.Instructions public static void Frintn_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintnV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearest); } @@ -1723,7 +2075,11 @@ namespace ARMeilleure.Instructions public static void Frintp_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintpS); + } + else if (Optimizations.UseSse41) { EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsPlusInfinity); } @@ -1738,7 +2094,11 @@ namespace ARMeilleure.Instructions public static void Frintp_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintpV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsPlusInfinity); } @@ -1753,6 +2113,7 @@ namespace ARMeilleure.Instructions public static void Frintx_S(ArmEmitterContext context) { + // TODO Arm64: Fast path. Should we set host FPCR? EmitScalarUnaryOpF(context, (op1) => { return EmitRoundByRMode(context, op1); @@ -1761,6 +2122,7 @@ namespace ARMeilleure.Instructions public static void Frintx_V(ArmEmitterContext context) { + // TODO Arm64: Fast path. Should we set host FPCR? EmitVectorUnaryOpF(context, (op1) => { return EmitRoundByRMode(context, op1); @@ -1769,7 +2131,11 @@ namespace ARMeilleure.Instructions public static void Frintz_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintzS); + } + else if (Optimizations.UseSse41) { EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsZero); } @@ -1784,7 +2150,11 @@ namespace ARMeilleure.Instructions public static void Frintz_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintzV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsZero); } @@ -1803,7 +2173,11 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrsqrteS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) { Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtss, GetVec(op.Rn)), scalar: true); @@ -1824,7 +2198,11 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrsqrteV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0) { Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtps, GetVec(op.Rn)), scalar: false); @@ -1846,7 +2224,11 @@ namespace ARMeilleure.Instructions public static void Frsqrts_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FrsqrtsS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1895,7 +2277,11 @@ namespace ARMeilleure.Instructions public static void Frsqrts_V(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FrsqrtsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -1949,7 +2335,11 @@ namespace ARMeilleure.Instructions public static void Fsqrt_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FsqrtS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarUnaryOpF(context, Intrinsic.X86Sqrtss, Intrinsic.X86Sqrtsd); } @@ -1964,7 +2354,11 @@ namespace ARMeilleure.Instructions public static void Fsqrt_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FsqrtV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorUnaryOpF(context, Intrinsic.X86Sqrtps, Intrinsic.X86Sqrtpd); } @@ -1979,7 +2373,11 @@ namespace ARMeilleure.Instructions public static void Fsub_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOpF(context, Intrinsic.Arm64FsubS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF(context, Intrinsic.X86Subss, Intrinsic.X86Subsd); } @@ -1998,7 +2396,11 @@ namespace ARMeilleure.Instructions public static void Fsub_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpF(context, Intrinsic.Arm64FsubV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF(context, Intrinsic.X86Subps, Intrinsic.X86Subpd); } @@ -2017,7 +2419,11 @@ namespace ARMeilleure.Instructions public static void Mla_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64MlaV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorMul_AddSub(context, AddSub.Add); } @@ -2032,15 +2438,26 @@ namespace ARMeilleure.Instructions public static void Mla_Ve(ArmEmitterContext context) { - EmitVectorTernaryOpByElemZx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRdByElem(context, Intrinsic.Arm64MlaVe); + } + else + { + EmitVectorTernaryOpByElemZx(context, (op1, op2, op3) => + { + return context.Add(op1, context.Multiply(op2, op3)); + }); + } } public static void Mls_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64MlsV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorMul_AddSub(context, AddSub.Subtract); } @@ -2055,15 +2472,26 @@ namespace ARMeilleure.Instructions public static void Mls_Ve(ArmEmitterContext context) { - EmitVectorTernaryOpByElemZx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Subtract(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRdByElem(context, Intrinsic.Arm64MlsVe); + } + else + { + EmitVectorTernaryOpByElemZx(context, (op1, op2, op3) => + { + return context.Subtract(op1, context.Multiply(op2, op3)); + }); + } } public static void Mul_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64MulV); + } + else if (Optimizations.UseSse41) { EmitSse41VectorMul_AddSub(context, AddSub.None); } @@ -2075,17 +2503,35 @@ namespace ARMeilleure.Instructions public static void Mul_Ve(ArmEmitterContext context) { - EmitVectorBinaryOpByElemZx(context, (op1, op2) => context.Multiply(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpByElem(context, Intrinsic.Arm64MulVe); + } + else + { + EmitVectorBinaryOpByElemZx(context, (op1, op2) => context.Multiply(op1, op2)); + } } public static void Neg_S(ArmEmitterContext context) { - EmitScalarUnaryOpSx(context, (op1) => context.Negate(op1)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOp(context, Intrinsic.Arm64NegS); + } + else + { + EmitScalarUnaryOpSx(context, (op1) => context.Negate(op1)); + } } public static void Neg_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64NegV); + } + else if (Optimizations.UseSse2) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -2110,7 +2556,11 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UsePclmulqdq && op.Size == 3) + if (Optimizations.UseAdvSimd && false) // Not supported by all Arm CPUs. + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64PmullV); + } + else if (Optimizations.UsePclmulqdq && op.Size == 3) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -2214,33 +2664,65 @@ namespace ARMeilleure.Instructions public static void Raddhn_V(ArmEmitterContext context) { - EmitHighNarrow(context, (op1, op2) => context.Add(op1, op2), round: true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64RaddhnV); + } + else + { + EmitHighNarrow(context, (op1, op2) => context.Add(op1, op2), round: true); + } } public static void Rsubhn_V(ArmEmitterContext context) { - EmitHighNarrow(context, (op1, op2) => context.Subtract(op1, op2), round: true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64RsubhnV); + } + else + { + EmitHighNarrow(context, (op1, op2) => context.Subtract(op1, op2), round: true); + } } public static void Saba_V(ArmEmitterContext context) { - EmitVectorTernaryOpSx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64SabaV); + } + else + { + EmitVectorTernaryOpSx(context, (op1, op2, op3) => + { + return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); + }); + } } public static void Sabal_V(ArmEmitterContext context) { - EmitVectorWidenRnRmTernaryOpSx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64SabalV); + } + else + { + EmitVectorWidenRnRmTernaryOpSx(context, (op1, op2, op3) => + { + return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); + }); + } } public static void Sabd_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SabdV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2262,7 +2744,11 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse41 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SabdlV); + } + else if (Optimizations.UseSse41 && op.Size < 2) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -2293,12 +2779,23 @@ namespace ARMeilleure.Instructions public static void Sadalp_V(ArmEmitterContext context) { - EmitAddLongPairwise(context, signed: true, accumulate: true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpRd(context, Intrinsic.Arm64SadalpV); + } + else + { + EmitAddLongPairwise(context, signed: true, accumulate: true); + } } public static void Saddl_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SaddlV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2328,17 +2825,35 @@ namespace ARMeilleure.Instructions public static void Saddlp_V(ArmEmitterContext context) { - EmitAddLongPairwise(context, signed: true, accumulate: false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64SaddlpV); + } + else + { + EmitAddLongPairwise(context, signed: true, accumulate: false); + } } public static void Saddlv_V(ArmEmitterContext context) { - EmitVectorLongAcrossVectorOpSx(context, (op1, op2) => context.Add(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64SaddlvV); + } + else + { + EmitVectorLongAcrossVectorOpSx(context, (op1, op2) => context.Add(op1, op2)); + } } public static void Saddw_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SaddwV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2368,7 +2883,11 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64ShaddV); + } + else if (Optimizations.UseSse2 && op.Size > 0) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -2404,7 +2923,11 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse2 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64ShsubV); + } + else if (Optimizations.UseSse2 && op.Size < 2) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -2442,7 +2965,11 @@ namespace ARMeilleure.Instructions public static void Smax_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SmaxV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2468,7 +2995,11 @@ namespace ARMeilleure.Instructions public static void Smaxp_V(ArmEmitterContext context) { - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SmaxpV); + } + else if (Optimizations.UseSsse3) { EmitSsse3VectorPairwiseOp(context, X86PmaxsInstruction); } @@ -2480,12 +3011,23 @@ namespace ARMeilleure.Instructions public static void Smaxv_V(ArmEmitterContext context) { - EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64SmaxvV); + } + else + { + EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: true)); + } } public static void Smin_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SminV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2511,7 +3053,11 @@ namespace ARMeilleure.Instructions public static void Sminp_V(ArmEmitterContext context) { - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SminpV); + } + else if (Optimizations.UseSsse3) { EmitSsse3VectorPairwiseOp(context, X86PminsInstruction); } @@ -2523,14 +3069,25 @@ namespace ARMeilleure.Instructions public static void Sminv_V(ArmEmitterContext context) { - EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64SminvV); + } + else + { + EmitVectorAcrossVectorOpSx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: true)); + } } public static void Smlal_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse41 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64SmlalV); + } + else if (Optimizations.UseSse41 && op.Size < 2) { Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); @@ -2566,17 +3123,28 @@ namespace ARMeilleure.Instructions public static void Smlal_Ve(ArmEmitterContext context) { - EmitVectorWidenTernaryOpByElemSx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRdByElem(context, Intrinsic.Arm64SmlalVe); + } + else + { + EmitVectorWidenTernaryOpByElemSx(context, (op1, op2, op3) => + { + return context.Add(op1, context.Multiply(op2, op3)); + }); + } } public static void Smlsl_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse41 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64SmlslV); + } + else if (Optimizations.UseSse41 && op.Size < 2) { Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); @@ -2612,117 +3180,268 @@ namespace ARMeilleure.Instructions public static void Smlsl_Ve(ArmEmitterContext context) { - EmitVectorWidenTernaryOpByElemSx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Subtract(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRdByElem(context, Intrinsic.Arm64SmlslVe); + } + else + { + EmitVectorWidenTernaryOpByElemSx(context, (op1, op2, op3) => + { + return context.Subtract(op1, context.Multiply(op2, op3)); + }); + } } public static void Smull_V(ArmEmitterContext context) { - EmitVectorWidenRnRmBinaryOpSx(context, (op1, op2) => context.Multiply(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SmullV); + } + else + { + EmitVectorWidenRnRmBinaryOpSx(context, (op1, op2) => context.Multiply(op1, op2)); + } } public static void Smull_Ve(ArmEmitterContext context) { - EmitVectorWidenBinaryOpByElemSx(context, (op1, op2) => context.Multiply(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpByElem(context, Intrinsic.Arm64SmullVe); + } + else + { + EmitVectorWidenBinaryOpByElemSx(context, (op1, op2) => context.Multiply(op1, op2)); + } } public static void Sqabs_S(ArmEmitterContext context) { - EmitScalarSaturatingUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingUnaryOp(context, Intrinsic.Arm64SqabsS); + } + else + { + EmitScalarSaturatingUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + } } public static void Sqabs_V(ArmEmitterContext context) { - EmitVectorSaturatingUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingUnaryOp(context, Intrinsic.Arm64SqabsV); + } + else + { + EmitVectorSaturatingUnaryOpSx(context, (op1) => EmitAbs(context, op1)); + } } public static void Sqadd_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpSx(context, flags: SaturatingFlags.Add); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOp(context, Intrinsic.Arm64SqaddS); + } + else + { + EmitScalarSaturatingBinaryOpSx(context, flags: SaturatingFlags.Add); + } } public static void Sqadd_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpSx(context, flags: SaturatingFlags.Add); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64SqaddV); + } + else + { + EmitVectorSaturatingBinaryOpSx(context, flags: SaturatingFlags.Add); + } } public static void Sqdmulh_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: false)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOp(context, Intrinsic.Arm64SqdmulhS); + } + else + { + EmitScalarSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: false)); + } } public static void Sqdmulh_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: false)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64SqdmulhV); + } + else + { + EmitVectorSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: false)); + } } public static void Sqdmulh_Ve(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpByElemSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: false)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOpByElem(context, Intrinsic.Arm64SqdmulhVe); + } + else + { + EmitVectorSaturatingBinaryOpByElemSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: false)); + } } public static void Sqneg_S(ArmEmitterContext context) { - EmitScalarSaturatingUnaryOpSx(context, (op1) => context.Negate(op1)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingUnaryOp(context, Intrinsic.Arm64SqnegS); + } + else + { + EmitScalarSaturatingUnaryOpSx(context, (op1) => context.Negate(op1)); + } } public static void Sqneg_V(ArmEmitterContext context) { - EmitVectorSaturatingUnaryOpSx(context, (op1) => context.Negate(op1)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingUnaryOp(context, Intrinsic.Arm64SqnegV); + } + else + { + EmitVectorSaturatingUnaryOpSx(context, (op1) => context.Negate(op1)); + } } public static void Sqrdmulh_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: true)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOp(context, Intrinsic.Arm64SqrdmulhS); + } + else + { + EmitScalarSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: true)); + } } public static void Sqrdmulh_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: true)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64SqrdmulhV); + } + else + { + EmitVectorSaturatingBinaryOpSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: true)); + } } public static void Sqrdmulh_Ve(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpByElemSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: true)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOpByElem(context, Intrinsic.Arm64SqrdmulhVe); + } + else + { + EmitVectorSaturatingBinaryOpByElemSx(context, (op1, op2) => EmitDoublingMultiplyHighHalf(context, op1, op2, round: true)); + } } public static void Sqsub_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpSx(context, flags: SaturatingFlags.Sub); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOp(context, Intrinsic.Arm64SqsubS); + } + else + { + EmitScalarSaturatingBinaryOpSx(context, flags: SaturatingFlags.Sub); + } } public static void Sqsub_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpSx(context, flags: SaturatingFlags.Sub); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64SqsubV); + } + else + { + EmitVectorSaturatingBinaryOpSx(context, flags: SaturatingFlags.Sub); + } } public static void Sqxtn_S(ArmEmitterContext context) { - EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxSx); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOpRd(context, Intrinsic.Arm64SqxtnS); + } + else + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxSx); + } } public static void Sqxtn_V(ArmEmitterContext context) { - EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxSx); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOpRd(context, Intrinsic.Arm64SqxtnV); + } + else + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxSx); + } } public static void Sqxtun_S(ArmEmitterContext context) { - EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxZx); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOpRd(context, Intrinsic.Arm64SqxtunS); + } + else + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxZx); + } } public static void Sqxtun_V(ArmEmitterContext context) { - EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxZx); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOpRd(context, Intrinsic.Arm64SqxtunV); + } + else + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxZx); + } } public static void Srhadd_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse2 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SrhaddV); + } + else if (Optimizations.UseSse2 && op.Size < 2) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -2764,7 +3483,11 @@ namespace ARMeilleure.Instructions public static void Ssubl_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SsublV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2794,7 +3517,11 @@ namespace ARMeilleure.Instructions public static void Ssubw_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SsubwV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2822,12 +3549,23 @@ namespace ARMeilleure.Instructions public static void Sub_S(ArmEmitterContext context) { - EmitScalarBinaryOpZx(context, (op1, op2) => context.Subtract(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOp(context, Intrinsic.Arm64SubS); + } + else + { + EmitScalarBinaryOpZx(context, (op1, op2) => context.Subtract(op1, op2)); + } } public static void Sub_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SubV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2853,38 +3591,77 @@ namespace ARMeilleure.Instructions public static void Subhn_V(ArmEmitterContext context) { - EmitHighNarrow(context, (op1, op2) => context.Subtract(op1, op2), round: false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64SubhnV); + } + else + { + EmitHighNarrow(context, (op1, op2) => context.Subtract(op1, op2), round: false); + } } public static void Suqadd_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpSx(context, flags: SaturatingFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOpRd(context, Intrinsic.Arm64SuqaddS); + } + else + { + EmitScalarSaturatingBinaryOpSx(context, flags: SaturatingFlags.Accumulate); + } } public static void Suqadd_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpSx(context, flags: SaturatingFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOpRd(context, Intrinsic.Arm64SuqaddV); + } + else + { + EmitVectorSaturatingBinaryOpSx(context, flags: SaturatingFlags.Accumulate); + } } public static void Uaba_V(ArmEmitterContext context) { - EmitVectorTernaryOpZx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64UabaV); + } + else + { + EmitVectorTernaryOpZx(context, (op1, op2, op3) => + { + return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); + }); + } } public static void Uabal_V(ArmEmitterContext context) { - EmitVectorWidenRnRmTernaryOpZx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64UabalV); + } + else + { + EmitVectorWidenRnRmTernaryOpZx(context, (op1, op2, op3) => + { + return context.Add(op1, EmitAbs(context, context.Subtract(op2, op3))); + }); + } } public static void Uabd_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UabdV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2906,7 +3683,11 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse41 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UabdlV); + } + else if (Optimizations.UseSse41 && op.Size < 2) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -2937,12 +3718,23 @@ namespace ARMeilleure.Instructions public static void Uadalp_V(ArmEmitterContext context) { - EmitAddLongPairwise(context, signed: false, accumulate: true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpRd(context, Intrinsic.Arm64UadalpV); + } + else + { + EmitAddLongPairwise(context, signed: false, accumulate: true); + } } public static void Uaddl_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UaddlV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -2972,17 +3764,35 @@ namespace ARMeilleure.Instructions public static void Uaddlp_V(ArmEmitterContext context) { - EmitAddLongPairwise(context, signed: false, accumulate: false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64UaddlpV); + } + else + { + EmitAddLongPairwise(context, signed: false, accumulate: false); + } } public static void Uaddlv_V(ArmEmitterContext context) { - EmitVectorLongAcrossVectorOpZx(context, (op1, op2) => context.Add(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64UaddlvV); + } + else + { + EmitVectorLongAcrossVectorOpZx(context, (op1, op2) => context.Add(op1, op2)); + } } public static void Uaddw_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UaddwV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -3012,7 +3822,11 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UhaddV); + } + else if (Optimizations.UseSse2 && op.Size > 0) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -3048,7 +3862,11 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse2 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UhsubV); + } + else if (Optimizations.UseSse2 && op.Size < 2) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -3079,7 +3897,11 @@ namespace ARMeilleure.Instructions public static void Umax_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UmaxV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -3105,7 +3927,11 @@ namespace ARMeilleure.Instructions public static void Umaxp_V(ArmEmitterContext context) { - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UmaxpV); + } + else if (Optimizations.UseSsse3) { EmitSsse3VectorPairwiseOp(context, X86PmaxuInstruction); } @@ -3117,12 +3943,23 @@ namespace ARMeilleure.Instructions public static void Umaxv_V(ArmEmitterContext context) { - EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64UmaxvV); + } + else + { + EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMax64Op(context, op1, op2, signed: false)); + } } public static void Umin_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UminV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -3148,7 +3985,11 @@ namespace ARMeilleure.Instructions public static void Uminp_V(ArmEmitterContext context) { - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UminpV); + } + else if (Optimizations.UseSsse3) { EmitSsse3VectorPairwiseOp(context, X86PminuInstruction); } @@ -3160,14 +4001,25 @@ namespace ARMeilleure.Instructions public static void Uminv_V(ArmEmitterContext context) { - EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64UminvV); + } + else + { + EmitVectorAcrossVectorOpZx(context, (op1, op2) => EmitMin64Op(context, op1, op2, signed: false)); + } } public static void Umlal_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse41 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64UmlalV); + } + else if (Optimizations.UseSse41 && op.Size < 2) { Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); @@ -3203,17 +4055,28 @@ namespace ARMeilleure.Instructions public static void Umlal_Ve(ArmEmitterContext context) { - EmitVectorWidenTernaryOpByElemZx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Add(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRdByElem(context, Intrinsic.Arm64UmlalVe); + } + else + { + EmitVectorWidenTernaryOpByElemZx(context, (op1, op2, op3) => + { + return context.Add(op1, context.Multiply(op2, op3)); + }); + } } public static void Umlsl_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse41 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64UmlslV); + } + else if (Optimizations.UseSse41 && op.Size < 2) { Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); @@ -3249,57 +4112,124 @@ namespace ARMeilleure.Instructions public static void Umlsl_Ve(ArmEmitterContext context) { - EmitVectorWidenTernaryOpByElemZx(context, (op1, op2, op3) => + if (Optimizations.UseAdvSimd) { - return context.Subtract(op1, context.Multiply(op2, op3)); - }); + InstEmitSimdHelperArm64.EmitVectorTernaryOpRdByElem(context, Intrinsic.Arm64UmlslVe); + } + else + { + EmitVectorWidenTernaryOpByElemZx(context, (op1, op2, op3) => + { + return context.Subtract(op1, context.Multiply(op2, op3)); + }); + } } public static void Umull_V(ArmEmitterContext context) { - EmitVectorWidenRnRmBinaryOpZx(context, (op1, op2) => context.Multiply(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UmullV); + } + else + { + EmitVectorWidenRnRmBinaryOpZx(context, (op1, op2) => context.Multiply(op1, op2)); + } } public static void Umull_Ve(ArmEmitterContext context) { - EmitVectorWidenBinaryOpByElemZx(context, (op1, op2) => context.Multiply(op1, op2)); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpByElem(context, Intrinsic.Arm64UmullVe); + } + else + { + EmitVectorWidenBinaryOpByElemZx(context, (op1, op2) => context.Multiply(op1, op2)); + } } public static void Uqadd_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOp(context, Intrinsic.Arm64UqaddS); + } + else + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add); + } } public static void Uqadd_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Add); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64UqaddV); + } + else + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Add); + } } public static void Uqsub_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOp(context, Intrinsic.Arm64UqsubS); + } + else + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + } } public static void Uqsub_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64UqsubV); + } + else + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + } } public static void Uqxtn_S(ArmEmitterContext context) { - EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarZxZx); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOpRd(context, Intrinsic.Arm64UqxtnS); + } + else + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarZxZx); + } } public static void Uqxtn_V(ArmEmitterContext context) { - EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorZxZx); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOpRd(context, Intrinsic.Arm64UqxtnV); + } + else + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorZxZx); + } } public static void Urhadd_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - if (Optimizations.UseSse2 && op.Size < 2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UrhaddV); + } + else if (Optimizations.UseSse2 && op.Size < 2) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); @@ -3330,17 +4260,35 @@ namespace ARMeilleure.Instructions public static void Usqadd_S(ArmEmitterContext context) { - EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarSaturatingBinaryOpRd(context, Intrinsic.Arm64UsqaddS); + } + else + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + } } public static void Usqadd_V(ArmEmitterContext context) { - EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOpRd(context, Intrinsic.Arm64UsqaddV); + } + else + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + } } public static void Usubl_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UsublV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -3370,7 +4318,11 @@ namespace ARMeilleure.Instructions public static void Usubw_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UsubwV); + } + else if (Optimizations.UseSse41) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs index 79b376e95..a9994e412 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs @@ -2,6 +2,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; + using static ARMeilleure.Instructions.InstEmitFlowHelper; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -30,7 +31,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FabsS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarUnaryOpSimd32(context, (m) => { @@ -49,7 +54,11 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FabsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorUnaryOpSimd32(context, (m) => { @@ -76,7 +85,11 @@ namespace ARMeilleure.Instructions public static void Vadd_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarBinaryOpF32(context, Intrinsic.Arm64FaddS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF32(context, Intrinsic.X86Addss, Intrinsic.X86Addsd); } @@ -92,7 +105,11 @@ namespace ARMeilleure.Instructions public static void Vadd_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FaddV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF32(context, Intrinsic.X86Addps, Intrinsic.X86Addpd); } @@ -280,7 +297,11 @@ namespace ARMeilleure.Instructions public static void Vfma_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseFma) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FmaddS); + } + else if (Optimizations.FastFP && Optimizations.UseFma) { EmitScalarTernaryOpF32(context, Intrinsic.X86Vfmadd231ss, Intrinsic.X86Vfmadd231sd); } @@ -299,7 +320,11 @@ namespace ARMeilleure.Instructions public static void Vfma_V(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseFma) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorTernaryOpF32(context, Intrinsic.Arm64FmlaV); + } + else if (Optimizations.FastFP && Optimizations.UseFma) { EmitVectorTernaryOpF32(context, Intrinsic.X86Vfmadd231ps); } @@ -314,7 +339,11 @@ namespace ARMeilleure.Instructions public static void Vfms_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseFma) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FmsubS); + } + else if (Optimizations.FastFP && Optimizations.UseFma) { EmitScalarTernaryOpF32(context, Intrinsic.X86Vfnmadd231ss, Intrinsic.X86Vfnmadd231sd); } @@ -333,7 +362,11 @@ namespace ARMeilleure.Instructions public static void Vfms_V(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseFma) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorTernaryOpF32(context, Intrinsic.Arm64FmlsV); + } + else if (Optimizations.FastFP && Optimizations.UseFma) { EmitVectorTernaryOpF32(context, Intrinsic.X86Vfnmadd231ps); } @@ -348,7 +381,11 @@ namespace ARMeilleure.Instructions public static void Vfnma_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseFma) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FnmaddS); + } + else if (Optimizations.FastFP && Optimizations.UseFma) { EmitScalarTernaryOpF32(context, Intrinsic.X86Vfnmsub231ss, Intrinsic.X86Vfnmsub231sd); } @@ -367,7 +404,11 @@ namespace ARMeilleure.Instructions public static void Vfnms_S(ArmEmitterContext context) // Fused. { - if (Optimizations.FastFP && Optimizations.UseFma) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FnmsubS); + } + else if (Optimizations.FastFP && Optimizations.UseFma) { EmitScalarTernaryOpF32(context, Intrinsic.X86Vfmsub231ss, Intrinsic.X86Vfmsub231sd); } @@ -419,7 +460,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; - if (Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FnegS); + } + else if (Optimizations.UseSse2) { EmitScalarUnaryOpSimd32(context, (m) => { @@ -445,7 +490,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; - if (Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarBinaryOpF32(context, Intrinsic.Arm64FnmulS); + } + else if (Optimizations.UseSse2) { EmitScalarBinaryOpSimd32(context, (n, m) => { @@ -473,7 +522,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FnmaddS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarTernaryOpF32(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd, Intrinsic.X86Subss, Intrinsic.X86Subsd, isNegD: true); } @@ -498,7 +551,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FnmsubS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarTernaryOpF32(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd, Intrinsic.X86Addss, Intrinsic.X86Addsd, isNegD: true); } @@ -525,7 +582,11 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FnegV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorUnaryOpSimd32(context, (m) => { @@ -554,7 +615,11 @@ namespace ARMeilleure.Instructions public static void Vdiv_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarBinaryOpF32(context, Intrinsic.Arm64FdivS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF32(context, Intrinsic.X86Divss, Intrinsic.X86Divsd); } @@ -573,7 +638,11 @@ namespace ARMeilleure.Instructions public static void Vmaxnm_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarBinaryOpF32(context, Intrinsic.Arm64FmaxnmS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF32(context, true, true); } @@ -585,7 +654,11 @@ namespace ARMeilleure.Instructions public static void Vmaxnm_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FmaxnmV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF32(context, true, false); } @@ -597,7 +670,11 @@ namespace ARMeilleure.Instructions public static void Vminnm_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarBinaryOpF32(context, Intrinsic.Arm64FminnmS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF32(context, false, true); } @@ -609,7 +686,11 @@ namespace ARMeilleure.Instructions public static void Vminnm_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse41) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FminnmV); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) { EmitSse41MaxMinNumOpF32(context, false, false); } @@ -621,7 +702,11 @@ namespace ARMeilleure.Instructions public static void Vmax_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FmaxV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF32(context, Intrinsic.X86Maxps, Intrinsic.X86Maxpd); } @@ -664,7 +749,11 @@ namespace ARMeilleure.Instructions public static void Vmin_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FminV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF32(context, Intrinsic.X86Minps, Intrinsic.X86Minpd); } @@ -707,7 +796,11 @@ namespace ARMeilleure.Instructions public static void Vmla_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FmaddS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarTernaryOpF32(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd, Intrinsic.X86Addss, Intrinsic.X86Addsd); } @@ -730,7 +823,11 @@ namespace ARMeilleure.Instructions public static void Vmla_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorTernaryOpF32(context, Intrinsic.Arm64FmlaV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorTernaryOpF32(context, Intrinsic.X86Mulps, Intrinsic.X86Mulpd, Intrinsic.X86Addps, Intrinsic.X86Addpd); } @@ -786,7 +883,11 @@ namespace ARMeilleure.Instructions public static void Vmls_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarTernaryOpF32(context, Intrinsic.Arm64FmlsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarTernaryOpF32(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd, Intrinsic.X86Subss, Intrinsic.X86Subsd); } @@ -809,7 +910,11 @@ namespace ARMeilleure.Instructions public static void Vmls_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorTernaryOpF32(context, Intrinsic.Arm64FmlsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorTernaryOpF32(context, Intrinsic.X86Mulps, Intrinsic.X86Mulpd, Intrinsic.X86Subps, Intrinsic.X86Subpd); } @@ -865,7 +970,11 @@ namespace ARMeilleure.Instructions public static void Vmul_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarBinaryOpF32(context, Intrinsic.Arm64FmulS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF32(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd); } @@ -884,7 +993,11 @@ namespace ARMeilleure.Instructions public static void Vmul_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FmulV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF32(context, Intrinsic.X86Mulps, Intrinsic.X86Mulpd); } @@ -975,7 +1088,11 @@ namespace ARMeilleure.Instructions public static void Vpadd_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorPairwiseOpF32(context, Intrinsic.Arm64FaddpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitSse2VectorPairwiseOpF32(context, Intrinsic.X86Addps); } @@ -1008,7 +1125,11 @@ namespace ARMeilleure.Instructions public static void Vpmax_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorPairwiseOpF32(context, Intrinsic.Arm64FmaxpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitSse2VectorPairwiseOpF32(context, Intrinsic.X86Maxps); } @@ -1038,7 +1159,11 @@ namespace ARMeilleure.Instructions public static void Vpmin_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorPairwiseOpF32(context, Intrinsic.Arm64FminpV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitSse2VectorPairwiseOpF32(context, Intrinsic.X86Minps); } @@ -1217,7 +1342,11 @@ namespace ARMeilleure.Instructions { int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse2 && sizeF == 0) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrecpeV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2 && sizeF == 0) { EmitVectorUnaryOpF32(context, Intrinsic.X86Rcpps, 0); } @@ -1237,7 +1366,11 @@ namespace ARMeilleure.Instructions public static void Vrecps(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FrecpsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; bool single = (op.Size & 1) == 0; @@ -1304,7 +1437,11 @@ namespace ARMeilleure.Instructions { int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse2 && sizeF == 0) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrsqrteV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2 && sizeF == 0) { EmitVectorUnaryOpF32(context, Intrinsic.X86Rsqrtps, 0); } @@ -1324,7 +1461,11 @@ namespace ARMeilleure.Instructions public static void Vrsqrts(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FrsqrtsV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; bool single = (op.Size & 1) == 0; @@ -1393,7 +1534,11 @@ namespace ARMeilleure.Instructions public static void Vsqrt_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FsqrtS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarUnaryOpF32(context, Intrinsic.X86Sqrtss, Intrinsic.X86Sqrtsd); } @@ -1408,7 +1553,11 @@ namespace ARMeilleure.Instructions public static void Vsub_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarBinaryOpF32(context, Intrinsic.Arm64FsubS); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitScalarBinaryOpF32(context, Intrinsic.X86Subss, Intrinsic.X86Subsd); } @@ -1420,7 +1569,11 @@ namespace ARMeilleure.Instructions public static void Vsub_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpF32(context, Intrinsic.Arm64FsubV); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitVectorBinaryOpF32(context, Intrinsic.X86Subps, Intrinsic.X86Subpd); } diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/ARMeilleure/Instructions/InstEmitSimdCmp.cs index 71055155c..c32b64ba1 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCmp.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCmp.cs @@ -466,12 +466,26 @@ namespace ARMeilleure.Instructions public static void Fcmp_S(ArmEmitterContext context) { - EmitFcmpOrFcmpe(context, signalNaNs: false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitFcmpOrFcmpe(context, signalNaNs: false); + } + else + { + EmitFcmpOrFcmpe(context, signalNaNs: false); + } } public static void Fcmpe_S(ArmEmitterContext context) { - EmitFcmpOrFcmpe(context, signalNaNs: true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitFcmpOrFcmpe(context, signalNaNs: true); + } + else + { + EmitFcmpOrFcmpe(context, signalNaNs: true); + } } private static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs) diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp32.cs b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs index 339d32939..a990e057d 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCmp32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs @@ -17,7 +17,11 @@ namespace ARMeilleure.Instructions { public static void Vceq_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.Equal, false); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, false); } @@ -38,7 +42,11 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.Equal, true); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, true); } @@ -55,7 +63,11 @@ namespace ARMeilleure.Instructions public static void Vcge_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseAvx) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false); + } + else if (Optimizations.FastFP && Optimizations.UseAvx) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false); } @@ -78,7 +90,11 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseAvx) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true); + } + else if (Optimizations.FastFP && Optimizations.UseAvx) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true); } @@ -95,7 +111,11 @@ namespace ARMeilleure.Instructions public static void Vcgt_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseAvx) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThan, false); + } + else if (Optimizations.FastFP && Optimizations.UseAvx) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, false); } @@ -118,7 +138,11 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseAvx) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThan, true); + } + else if (Optimizations.FastFP && Optimizations.UseAvx) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, true); } @@ -139,7 +163,11 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.LessThanOrEqual, true); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThanOrEqual, true); } @@ -160,7 +188,11 @@ namespace ARMeilleure.Instructions if (op.F) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.LessThan, true); + } + else if (Optimizations.FastFP && Optimizations.UseSse2) { EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThan, true); } @@ -247,12 +279,26 @@ namespace ARMeilleure.Instructions public static void Vcmp(ArmEmitterContext context) { - EmitVcmpOrVcmpe(context, false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVcmpOrVcmpe(context, false); + } + else + { + EmitVcmpOrVcmpe(context, false); + } } public static void Vcmpe(ArmEmitterContext context) { - EmitVcmpOrVcmpe(context, true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVcmpOrVcmpe(context, true); + } + else + { + EmitVcmpOrVcmpe(context, true); + } } private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs) diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt.cs b/ARMeilleure/Instructions/InstEmitSimdCvt.cs index 7f61cad41..652ad397c 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt.cs @@ -164,7 +164,11 @@ namespace ARMeilleure.Instructions public static void Fcvtas_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtasGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvts_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false); } @@ -176,7 +180,11 @@ namespace ARMeilleure.Instructions public static void Fcvtas_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FcvtasS); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: true); } @@ -188,7 +196,11 @@ namespace ARMeilleure.Instructions public static void Fcvtas_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtasS); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: false); } @@ -200,7 +212,11 @@ namespace ARMeilleure.Instructions public static void Fcvtau_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtauGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvtu_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false); } @@ -212,7 +228,11 @@ namespace ARMeilleure.Instructions public static void Fcvtau_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FcvtauS); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: true); } @@ -224,7 +244,11 @@ namespace ARMeilleure.Instructions public static void Fcvtau_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtauV); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: false); } @@ -240,7 +264,11 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; - if (Optimizations.UseSse2 && sizeF == 1) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtlV); + } + else if (Optimizations.UseSse2 && sizeF == 1) { Operand n = GetVec(op.Rn); @@ -296,7 +324,11 @@ namespace ARMeilleure.Instructions public static void Fcvtms_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtmsGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvts_Gp(context, FPRoundingMode.TowardsMinusInfinity, isFixed: false); } @@ -308,7 +340,11 @@ namespace ARMeilleure.Instructions public static void Fcvtms_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtmsV); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsMinusInfinity, scalar: false); } @@ -320,7 +356,11 @@ namespace ARMeilleure.Instructions public static void Fcvtmu_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtmuGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvtu_Gp(context, FPRoundingMode.TowardsMinusInfinity, isFixed: false); } @@ -336,7 +376,11 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; - if (Optimizations.UseSse2 && sizeF == 1) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOpFRd(context, Intrinsic.Arm64FcvtnV); + } + else if (Optimizations.UseSse2 && sizeF == 1) { Operand d = GetVec(op.Rd); @@ -405,7 +449,11 @@ namespace ARMeilleure.Instructions public static void Fcvtns_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtnsGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvts_Gp(context, FPRoundingMode.ToNearest, isFixed: false); } @@ -417,7 +465,11 @@ namespace ARMeilleure.Instructions public static void Fcvtns_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FcvtnsS); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearest, scalar: true); } @@ -429,7 +481,11 @@ namespace ARMeilleure.Instructions public static void Fcvtns_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtnsV); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearest, scalar: false); } @@ -441,7 +497,11 @@ namespace ARMeilleure.Instructions public static void Fcvtnu_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FcvtnuS); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearest, scalar: true); } @@ -453,7 +513,11 @@ namespace ARMeilleure.Instructions public static void Fcvtnu_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtnuV); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearest, scalar: false); } @@ -465,7 +529,11 @@ namespace ARMeilleure.Instructions public static void Fcvtps_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtpsGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvts_Gp(context, FPRoundingMode.TowardsPlusInfinity, isFixed: false); } @@ -477,7 +545,11 @@ namespace ARMeilleure.Instructions public static void Fcvtpu_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtpuGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvtu_Gp(context, FPRoundingMode.TowardsPlusInfinity, isFixed: false); } @@ -489,7 +561,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzs_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtzsGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvts_Gp(context, FPRoundingMode.TowardsZero, isFixed: false); } @@ -501,7 +577,13 @@ namespace ARMeilleure.Instructions public static void Fcvtzs_Gp_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; + + InstEmitSimdHelperArm64.EmitScalarConvertBinaryOpFToGp(context, Intrinsic.Arm64FcvtzsGpFixed, op.FBits); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvts_Gp(context, FPRoundingMode.TowardsZero, isFixed: true); } @@ -513,7 +595,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzs_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FcvtzsS); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: true); } @@ -525,7 +611,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzs_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtzsV); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: false); } @@ -537,7 +627,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzs_V_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorConvertBinaryOpF(context, Intrinsic.Arm64FcvtzsVFixed, GetFBits(context)); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtsOpF(context, FPRoundingMode.TowardsZero, scalar: false); } @@ -549,7 +643,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzu_Gp(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFToGp(context, Intrinsic.Arm64FcvtzuGp); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvtu_Gp(context, FPRoundingMode.TowardsZero, isFixed: false); } @@ -561,7 +659,13 @@ namespace ARMeilleure.Instructions public static void Fcvtzu_Gp_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; + + InstEmitSimdHelperArm64.EmitScalarConvertBinaryOpFToGp(context, Intrinsic.Arm64FcvtzuGpFixed, op.FBits); + } + else if (Optimizations.UseSse41) { EmitSse41Fcvtu_Gp(context, FPRoundingMode.TowardsZero, isFixed: true); } @@ -573,7 +677,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzu_S(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FcvtzuS); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: true); } @@ -585,7 +693,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzu_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FcvtzuV); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: false); } @@ -597,7 +709,11 @@ namespace ARMeilleure.Instructions public static void Fcvtzu_V_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorConvertBinaryOpF(context, Intrinsic.Arm64FcvtzuVFixed, GetFBits(context)); + } + else if (Optimizations.UseSse41) { EmitSse41FcvtuOpF(context, FPRoundingMode.TowardsZero, scalar: false); } @@ -609,41 +725,59 @@ namespace ARMeilleure.Instructions public static void Scvtf_Gp(ArmEmitterContext context) { - OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; - - Operand res = GetIntOrZR(context, op.Rn); - - if (op.RegisterSize == RegisterSize.Int32) + if (Optimizations.UseAdvSimd) { - res = context.SignExtend32(OperandType.I64, res); + InstEmitSimdHelperArm64.EmitScalarUnaryOpFFromGp(context, Intrinsic.Arm64ScvtfGp); } + else + { + OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; - res = EmitFPConvert(context, res, op.Size, signed: true); + Operand res = GetIntOrZR(context, op.Rn); - context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + if (op.RegisterSize == RegisterSize.Int32) + { + res = context.SignExtend32(OperandType.I64, res); + } + + res = EmitFPConvert(context, res, op.Size, signed: true); + + context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + } } public static void Scvtf_Gp_Fixed(ArmEmitterContext context) { OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; - Operand res = GetIntOrZR(context, op.Rn); - - if (op.RegisterSize == RegisterSize.Int32) + if (Optimizations.UseAdvSimd) { - res = context.SignExtend32(OperandType.I64, res); + InstEmitSimdHelperArm64.EmitScalarConvertBinaryOpFFromGp(context, Intrinsic.Arm64ScvtfGpFixed, op.FBits); } + else + { + Operand res = GetIntOrZR(context, op.Rn); - res = EmitFPConvert(context, res, op.Size, signed: true); + if (op.RegisterSize == RegisterSize.Int32) + { + res = context.SignExtend32(OperandType.I64, res); + } - res = EmitI2fFBitsMul(context, res, op.FBits); + res = EmitFPConvert(context, res, op.Size, signed: true); - context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + res = EmitI2fFBitsMul(context, res, op.FBits); + + context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + } } public static void Scvtf_S(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64ScvtfS); + } + else if (Optimizations.UseSse2) { EmitSse2ScvtfOp(context, scalar: true); } @@ -655,7 +789,11 @@ namespace ARMeilleure.Instructions public static void Scvtf_S_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarConvertBinaryOpF(context, Intrinsic.Arm64ScvtfSFixed, GetFBits(context)); + } + else if (Optimizations.UseSse2) { EmitSse2ScvtfOp(context, scalar: true); } @@ -667,7 +805,11 @@ namespace ARMeilleure.Instructions public static void Scvtf_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64ScvtfV); + } + else if (Optimizations.UseSse2) { EmitSse2ScvtfOp(context, scalar: false); } @@ -679,7 +821,11 @@ namespace ARMeilleure.Instructions public static void Scvtf_V_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorConvertBinaryOpF(context, Intrinsic.Arm64ScvtfVFixed, GetFBits(context)); + } + else if (Optimizations.UseSse2) { EmitSse2ScvtfOp(context, scalar: false); } @@ -691,31 +837,49 @@ namespace ARMeilleure.Instructions public static void Ucvtf_Gp(ArmEmitterContext context) { - OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpFFromGp(context, Intrinsic.Arm64UcvtfGp); + } + else + { + OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; - Operand res = GetIntOrZR(context, op.Rn); + Operand res = GetIntOrZR(context, op.Rn); - res = EmitFPConvert(context, res, op.Size, signed: false); + res = EmitFPConvert(context, res, op.Size, signed: false); - context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + } } public static void Ucvtf_Gp_Fixed(ArmEmitterContext context) { OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; - Operand res = GetIntOrZR(context, op.Rn); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarConvertBinaryOpFFromGp(context, Intrinsic.Arm64UcvtfGpFixed, op.FBits); + } + else + { + Operand res = GetIntOrZR(context, op.Rn); - res = EmitFPConvert(context, res, op.Size, signed: false); + res = EmitFPConvert(context, res, op.Size, signed: false); - res = EmitI2fFBitsMul(context, res, op.FBits); + res = EmitI2fFBitsMul(context, res, op.FBits); - context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); + } } public static void Ucvtf_S(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64UcvtfS); + } + else if (Optimizations.UseSse2) { EmitSse2UcvtfOp(context, scalar: true); } @@ -727,7 +891,11 @@ namespace ARMeilleure.Instructions public static void Ucvtf_S_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarConvertBinaryOpF(context, Intrinsic.Arm64UcvtfSFixed, GetFBits(context)); + } + else if (Optimizations.UseSse2) { EmitSse2UcvtfOp(context, scalar: true); } @@ -739,7 +907,11 @@ namespace ARMeilleure.Instructions public static void Ucvtf_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64UcvtfV); + } + else if (Optimizations.UseSse2) { EmitSse2UcvtfOp(context, scalar: false); } @@ -751,7 +923,11 @@ namespace ARMeilleure.Instructions public static void Ucvtf_V_Fixed(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorConvertBinaryOpF(context, Intrinsic.Arm64UcvtfVFixed, GetFBits(context)); + } + else if (Optimizations.UseSse2) { EmitSse2UcvtfOp(context, scalar: false); } diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index f3f239589..5fdc3b5ad 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -59,7 +59,11 @@ namespace ARMeilleure.Instructions if (toInteger) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuV : Intrinsic.Arm64FcvtzsV); + } + else if (Optimizations.UseSse41) { EmitSse41ConvertVector32(context, FPRoundingMode.TowardsZero, !unsigned); } @@ -153,7 +157,28 @@ namespace ARMeilleure.Instructions bool unsigned = (op.Opc2 & 1) == 0; bool roundWithFpscr = op.Opc != 1; - if (!roundWithFpscr && Optimizations.UseSse41) + if (!roundWithFpscr && Optimizations.UseAdvSimd) + { + bool doubleSize = floatSize == OperandType.FP64; + + if (doubleSize) + { + Operand m = GetVecA32(op.Vm >> 1); + + Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, doubleSize); + + Intrinsic inst = (unsigned ? Intrinsic.Arm64FcvtzuGp : Intrinsic.Arm64FcvtzsGp) | Intrinsic.Arm64VDouble; + + Operand asInteger = context.AddIntrinsicInt(inst, toConvert); + + InsertScalar(context, op.Vd, asInteger); + } + else + { + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuS : Intrinsic.Arm64FcvtzsS); + } + } + else if (!roundWithFpscr && Optimizations.UseSse41) { EmitSse41ConvertInt32(context, FPRoundingMode.TowardsZero, !unsigned); } @@ -231,7 +256,34 @@ namespace ARMeilleure.Instructions bool unsigned = op.Opc == 0; int rm = op.Opc2 & 3; - if (Optimizations.UseSse41) + Intrinsic inst; + + if (Optimizations.UseAdvSimd) + { + if (unsigned) + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtauS, + 0b01 => Intrinsic.Arm64FcvtnuS, + 0b10 => Intrinsic.Arm64FcvtpuS, + 0b11 => Intrinsic.Arm64FcvtmuS, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + else + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtasS, + 0b01 => Intrinsic.Arm64FcvtnsS, + 0b10 => Intrinsic.Arm64FcvtpsS, + 0b11 => Intrinsic.Arm64FcvtmsS, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst); + } + else if (Optimizations.UseSse41) { EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned); } @@ -338,7 +390,19 @@ namespace ARMeilleure.Instructions int rm = op.Opc2 & 3; - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + Intrinsic inst = rm switch { + 0b00 => Intrinsic.Arm64FrintaS, + 0b01 => Intrinsic.Arm64FrintnS, + 0b10 => Intrinsic.Arm64FrintpS, + 0b11 => Intrinsic.Arm64FrintmS, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst); + } + else if (Optimizations.UseSse41) { EmitScalarUnaryOpSimd32(context, (m) => { @@ -382,12 +446,9 @@ namespace ARMeilleure.Instructions // VRINTA (vector). public static void Vrinta_V(ArmEmitterContext context) { - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) { - EmitVectorUnaryOpSimd32(context, (m) => - { - return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: false); - }); + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintaS); } else { @@ -398,7 +459,11 @@ namespace ARMeilleure.Instructions // VRINTM (vector). public static void Vrintm_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintmS); + } + else if (Optimizations.UseSse2) { EmitVectorUnaryOpSimd32(context, (m) => { @@ -414,7 +479,11 @@ namespace ARMeilleure.Instructions // VRINTN (vector). public static void Vrintn_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintnS); + } + else if (Optimizations.UseSse2) { EmitVectorUnaryOpSimd32(context, (m) => { @@ -430,7 +499,11 @@ namespace ARMeilleure.Instructions // VRINTP (vector). public static void Vrintp_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintpS); + } + else if (Optimizations.UseSse2) { EmitVectorUnaryOpSimd32(context, (m) => { @@ -448,7 +521,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintzS); + } + else if (Optimizations.UseSse2) { EmitScalarUnaryOpSimd32(context, (m) => { diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs b/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs new file mode 100644 index 000000000..98236be6d --- /dev/null +++ b/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs @@ -0,0 +1,366 @@ + +using ARMeilleure.Decoders; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.State; +using ARMeilleure.Translation; +using System; +using System.Diagnostics; + +using static ARMeilleure.Instructions.InstEmitHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; + +namespace ARMeilleure.Instructions +{ + using Func1I = Func; + using Func2I = Func; + using Func3I = Func; + + static class InstEmitSimdHelper32Arm64 + { + // Intrinsic Helpers + + public static Operand EmitMoveDoubleWordToSide(ArmEmitterContext context, Operand input, int originalV, int targetV) + { + Debug.Assert(input.Type == OperandType.V128); + + int originalSide = originalV & 1; + int targetSide = targetV & 1; + + if (originalSide == targetSide) + { + return input; + } + + Intrinsic vType = Intrinsic.Arm64VDWord | Intrinsic.Arm64V128; + + if (targetSide == 1) + { + return context.AddIntrinsic(Intrinsic.Arm64DupVe | vType, input, Const(OperandType.I32, 0)); // Low to high. + } + else + { + return context.AddIntrinsic(Intrinsic.Arm64DupVe | vType, input, Const(OperandType.I32, 1)); // High to low. + } + } + + public static Operand EmitDoubleWordInsert(ArmEmitterContext context, Operand target, Operand value, int targetV) + { + Debug.Assert(target.Type == OperandType.V128 && value.Type == OperandType.V128); + + int targetSide = targetV & 1; + Operand idx = Const(targetSide); + + return context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, target, idx, value, idx); + } + + public static Operand EmitScalarInsert(ArmEmitterContext context, Operand target, Operand value, int reg, bool doubleWidth) + { + Debug.Assert(target.Type == OperandType.V128 && value.Type == OperandType.V128); + + // Insert from index 0 in value to index in target. + int index = reg & (doubleWidth ? 1 : 3); + + if (doubleWidth) + { + return context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, target, Const(index), value, Const(0)); + } + else + { + return context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VWord, target, Const(index), value, Const(0)); + } + } + + public static Operand EmitExtractScalar(ArmEmitterContext context, Operand target, int reg, bool doubleWidth) + { + int index = reg & (doubleWidth ? 1 : 3); + if (index == 0) return target; // Element is already at index 0, so just return the vector directly. + + if (doubleWidth) + { + return context.AddIntrinsic(Intrinsic.Arm64DupSe | Intrinsic.Arm64VDWord, target, Const(1)); // Extract high (index 1). + } + else + { + return context.AddIntrinsic(Intrinsic.Arm64DupSe | Intrinsic.Arm64VWord, target, Const(index)); // Extract element at index. + } + } + + // Vector Operand Templates + + public static void EmitVectorUnaryOpSimd32(ArmEmitterContext context, Func1I vectorFunc) + { + OpCode32Simd op = (OpCode32Simd)context.CurrOp; + + Operand m = GetVecA32(op.Qm); + Operand d = GetVecA32(op.Qd); + + if (!op.Q) // Register swap: move relevant doubleword to destination side. + { + m = EmitMoveDoubleWordToSide(context, m, op.Vm, op.Vd); + } + + Operand res = vectorFunc(m); + + if (!op.Q) // Register insert. + { + res = EmitDoubleWordInsert(context, d, res, op.Vd); + } + + context.Copy(d, res); + } + + public static void EmitVectorUnaryOpF32(ArmEmitterContext context, Intrinsic inst) + { + OpCode32Simd op = (OpCode32Simd)context.CurrOp; + + inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitVectorUnaryOpSimd32(context, (m) => context.AddIntrinsic(inst, m)); + } + + public static void EmitVectorBinaryOpSimd32(ArmEmitterContext context, Func2I vectorFunc, int side = -1) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + Operand n = GetVecA32(op.Qn); + Operand m = GetVecA32(op.Qm); + Operand d = GetVecA32(op.Qd); + + if (side == -1) + { + side = op.Vd; + } + + if (!op.Q) // Register swap: move relevant doubleword to destination side. + { + n = EmitMoveDoubleWordToSide(context, n, op.Vn, side); + m = EmitMoveDoubleWordToSide(context, m, op.Vm, side); + } + + Operand res = vectorFunc(n, m); + + if (!op.Q) // Register insert. + { + if (side != op.Vd) + { + res = EmitMoveDoubleWordToSide(context, res, side, op.Vd); + } + res = EmitDoubleWordInsert(context, d, res, op.Vd); + } + + context.Copy(d, res); + } + + public static void EmitVectorBinaryOpF32(ArmEmitterContext context, Intrinsic inst) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m)); + } + + public static void EmitVectorTernaryOpSimd32(ArmEmitterContext context, Func3I vectorFunc) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + Operand n = GetVecA32(op.Qn); + Operand m = GetVecA32(op.Qm); + Operand d = GetVecA32(op.Qd); + Operand initialD = d; + + if (!op.Q) // Register swap: move relevant doubleword to destination side. + { + n = EmitMoveDoubleWordToSide(context, n, op.Vn, op.Vd); + m = EmitMoveDoubleWordToSide(context, m, op.Vm, op.Vd); + } + + Operand res = vectorFunc(d, n, m); + + if (!op.Q) // Register insert. + { + res = EmitDoubleWordInsert(context, initialD, res, op.Vd); + } + + context.Copy(initialD, res); + } + + public static void EmitVectorTernaryOpF32(ArmEmitterContext context, Intrinsic inst) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(inst, d, n, m)); + } + + public static void EmitScalarUnaryOpSimd32(ArmEmitterContext context, Func1I scalarFunc) + { + OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; + + bool doubleSize = (op.Size & 1) != 0; + int shift = doubleSize ? 1 : 2; + Operand m = GetVecA32(op.Vm >> shift); + Operand d = GetVecA32(op.Vd >> shift); + + m = EmitExtractScalar(context, m, op.Vm, doubleSize); + + Operand res = scalarFunc(m); + + // Insert scalar into vector. + res = EmitScalarInsert(context, d, res, op.Vd, doubleSize); + + context.Copy(d, res); + } + + public static void EmitScalarUnaryOpF32(ArmEmitterContext context, Intrinsic inst) + { + OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; + + inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitScalarUnaryOpSimd32(context, (m) => (inst == 0) ? m : context.AddIntrinsic(inst, m)); + } + + public static void EmitScalarBinaryOpSimd32(ArmEmitterContext context, Func2I scalarFunc) + { + OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; + + bool doubleSize = (op.Size & 1) != 0; + int shift = doubleSize ? 1 : 2; + Operand n = GetVecA32(op.Vn >> shift); + Operand m = GetVecA32(op.Vm >> shift); + Operand d = GetVecA32(op.Vd >> shift); + + n = EmitExtractScalar(context, n, op.Vn, doubleSize); + m = EmitExtractScalar(context, m, op.Vm, doubleSize); + + Operand res = scalarFunc(n, m); + + // Insert scalar into vector. + res = EmitScalarInsert(context, d, res, op.Vd, doubleSize); + + context.Copy(d, res); + } + + public static void EmitScalarBinaryOpF32(ArmEmitterContext context, Intrinsic inst) + { + OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; + + inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitScalarBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m)); + } + + public static void EmitScalarTernaryOpSimd32(ArmEmitterContext context, Func3I scalarFunc) + { + OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; + + bool doubleSize = (op.Size & 1) != 0; + int shift = doubleSize ? 1 : 2; + Operand n = GetVecA32(op.Vn >> shift); + Operand m = GetVecA32(op.Vm >> shift); + Operand d = GetVecA32(op.Vd >> shift); + Operand initialD = d; + + n = EmitExtractScalar(context, n, op.Vn, doubleSize); + m = EmitExtractScalar(context, m, op.Vm, doubleSize); + d = EmitExtractScalar(context, d, op.Vd, doubleSize); + + Operand res = scalarFunc(d, n, m); + + // Insert scalar into vector. + res = EmitScalarInsert(context, initialD, res, op.Vd, doubleSize); + + context.Copy(initialD, res); + } + + public static void EmitScalarTernaryOpF32(ArmEmitterContext context, Intrinsic inst) + { + OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; + + inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitScalarTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(inst, d, n, m)); + } + + // Pairwise + + public static void EmitVectorPairwiseOpF32(ArmEmitterContext context, Intrinsic inst32) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + inst32 |= Intrinsic.Arm64V64 | Intrinsic.Arm64VFloat; + EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst32, n, m), 0); + } + + public static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs) + { + OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; + + bool cmpWithZero = (op.Opc & 2) != 0; + + Intrinsic inst = signalNaNs ? Intrinsic.Arm64FcmpeS : Intrinsic.Arm64FcmpS; + inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + + bool doubleSize = (op.Size & 1) != 0; + int shift = doubleSize ? 1 : 2; + Operand n = GetVecA32(op.Vd >> shift); + Operand m = GetVecA32(op.Vm >> shift); + + n = EmitExtractScalar(context, n, op.Vd, doubleSize); + m = cmpWithZero ? Const(0) : EmitExtractScalar(context, m, op.Vm, doubleSize); + + Operand nzcv = context.AddIntrinsicInt(inst, n, m); + + Operand one = Const(1); + + SetFpFlag(context, FPState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(28)), one)); + SetFpFlag(context, FPState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(29)), one)); + SetFpFlag(context, FPState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(30)), one)); + SetFpFlag(context, FPState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(31)), one)); + } + + public static void EmitCmpOpF32(ArmEmitterContext context, CmpCondition cond, bool zero) + { + OpCode32Simd op = (OpCode32Simd)context.CurrOp; + + int sizeF = op.Size & 1; + + Intrinsic inst; + if (zero) + { + inst = cond switch + { + CmpCondition.Equal => Intrinsic.Arm64FcmeqVz, + CmpCondition.GreaterThan => Intrinsic.Arm64FcmgtVz, + CmpCondition.GreaterThanOrEqual => Intrinsic.Arm64FcmgeVz, + CmpCondition.LessThan => Intrinsic.Arm64FcmltVz, + CmpCondition.LessThanOrEqual => Intrinsic.Arm64FcmleVz, + _ => throw new InvalidOperationException() + }; + } + else { + inst = cond switch + { + CmpCondition.Equal => Intrinsic.Arm64FcmeqV, + CmpCondition.GreaterThan => Intrinsic.Arm64FcmgtV, + CmpCondition.GreaterThanOrEqual => Intrinsic.Arm64FcmgeV, + _ => throw new InvalidOperationException() + }; + } + + inst |= (sizeF != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + + if (zero) + { + EmitVectorUnaryOpSimd32(context, (m) => + { + return context.AddIntrinsic(inst, m); + }); + } + else + { + EmitVectorBinaryOpSimd32(context, (n, m) => + { + return context.AddIntrinsic(inst, n, m); + }); + } + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs b/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs new file mode 100644 index 000000000..f0d242ae2 --- /dev/null +++ b/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs @@ -0,0 +1,720 @@ +using ARMeilleure.Decoders; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.State; +using ARMeilleure.Translation; + +using static ARMeilleure.Instructions.InstEmitHelper; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; + +namespace ARMeilleure.Instructions +{ + static class InstEmitSimdHelperArm64 + { + public static void EmitScalarUnaryOpF(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n)); + } + + public static void EmitScalarUnaryOpFFromGp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; + + Operand n = GetIntOrZR(context, op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n)); + } + + public static void EmitScalarUnaryOpFToGp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp; + + Operand n = GetVec(op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + SetIntOrZR(context, op.Rd, op.RegisterSize == RegisterSize.Int32 + ? context.AddIntrinsicInt (inst, n) + : context.AddIntrinsicLong(inst, n)); + } + + public static void EmitScalarBinaryOpF(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m)); + } + + public static void EmitScalarBinaryOpFByElem(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m, Const(op.Index))); + } + + public static void EmitScalarTernaryOpF(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + Operand a = GetVec(op.Ra); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, a, n, m)); + } + + public static void EmitScalarTernaryOpFRdByElem(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(d, context.AddIntrinsic(inst, d, n, m, Const(op.Index))); + } + + public static void EmitScalarUnaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n)); + } + + public static void EmitScalarBinaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m)); + } + + public static void EmitScalarBinaryOpRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n)); + } + + public static void EmitScalarTernaryOpRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + context.Copy(d, context.AddIntrinsic(inst, d, n, m)); + } + + public static void EmitScalarShiftBinaryOp(ArmEmitterContext context, Intrinsic inst, int shift) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(shift))); + } + + public static void EmitScalarShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift))); + } + + public static void EmitScalarSaturatingShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift))); + + context.SetPendingQcFlagSync(); + } + + public static void EmitScalarSaturatingUnaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + Operand result = context.AddIntrinsic(inst, n); + + context.Copy(GetVec(op.Rd), result); + + context.SetPendingQcFlagSync(); + } + + public static void EmitScalarSaturatingBinaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + Operand result = context.AddIntrinsic(inst, n, m); + + context.Copy(GetVec(op.Rd), result); + + context.SetPendingQcFlagSync(); + } + + public static void EmitScalarSaturatingBinaryOpRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + Operand result = context.AddIntrinsic(inst, d, n); + + context.Copy(GetVec(op.Rd), result); + + context.SetPendingQcFlagSync(); + } + + public static void EmitScalarConvertBinaryOpF(ArmEmitterContext context, Intrinsic inst, int fBits) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(fBits))); + } + + public static void EmitScalarConvertBinaryOpFFromGp(ArmEmitterContext context, Intrinsic inst, int fBits) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetIntOrZR(context, op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(fBits))); + } + + public static void EmitScalarConvertBinaryOpFToGp(ArmEmitterContext context, Intrinsic inst, int fBits) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + SetIntOrZR(context, op.Rd, op.RegisterSize == RegisterSize.Int32 + ? context.AddIntrinsicInt (inst, n, Const(fBits)) + : context.AddIntrinsicLong(inst, n, Const(fBits))); + } + + public static void EmitVectorUnaryOpF(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n)); + } + + public static void EmitVectorBinaryOpF(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m)); + } + + public static void EmitVectorBinaryOpFRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n)); + } + + public static void EmitVectorBinaryOpFByElem(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m, Const(op.Index))); + } + + public static void EmitVectorTernaryOpFRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(d, context.AddIntrinsic(inst, d, n, m)); + } + + public static void EmitVectorTernaryOpFRdByElem(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(d, context.AddIntrinsic(inst, d, n, m, Const(op.Index))); + } + + public static void EmitVectorUnaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n)); + } + + public static void EmitVectorBinaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m)); + } + + public static void EmitVectorBinaryOpRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n)); + } + + public static void EmitVectorBinaryOpByElem(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdRegElem op = (OpCodeSimdRegElem)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m, Const(op.Index))); + } + + public static void EmitVectorTernaryOpRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(d, context.AddIntrinsic(inst, d, n, m)); + } + + public static void EmitVectorTernaryOpRdByElem(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdRegElem op = (OpCodeSimdRegElem)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(d, context.AddIntrinsic(inst, d, n, m, Const(op.Index))); + } + + public static void EmitVectorShiftBinaryOp(ArmEmitterContext context, Intrinsic inst, int shift) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(shift))); + } + + public static void EmitVectorShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift))); + } + + public static void EmitVectorSaturatingShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift))); + + context.SetPendingQcFlagSync(); + } + + public static void EmitVectorSaturatingUnaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + Operand result = context.AddIntrinsic(inst, n); + + context.Copy(GetVec(op.Rd), result); + + context.SetPendingQcFlagSync(); + } + + public static void EmitVectorSaturatingBinaryOp(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + Operand result = context.AddIntrinsic(inst, n, m); + + context.Copy(GetVec(op.Rd), result); + + context.SetPendingQcFlagSync(); + } + + public static void EmitVectorSaturatingBinaryOpRd(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + Operand result = context.AddIntrinsic(inst, d, n); + + context.Copy(GetVec(op.Rd), result); + + context.SetPendingQcFlagSync(); + } + + public static void EmitVectorSaturatingBinaryOpByElem(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdRegElem op = (OpCodeSimdRegElem)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + Operand result = context.AddIntrinsic(inst, n, m, Const(op.Index)); + + context.Copy(GetVec(op.Rd), result); + + context.SetPendingQcFlagSync(); + } + + public static void EmitVectorConvertBinaryOpF(ArmEmitterContext context, Intrinsic inst, int fBits) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(fBits))); + } + + public static void EmitVectorLookupTable(ArmEmitterContext context, Intrinsic inst) + { + OpCodeSimdTbl op = (OpCodeSimdTbl)context.CurrOp; + + Operand[] operands = new Operand[op.Size + 1]; + + operands[op.Size] = GetVec(op.Rm); + + for (int index = 0; index < op.Size; index++) + { + operands[index] = GetVec((op.Rn + index) & 0x1F); + } + + if (op.RegisterSize == RegisterSize.Simd128) + { + inst |= Intrinsic.Arm64V128; + } + + context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, operands)); + } + + public static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false; + + Intrinsic inst = signalNaNs ? Intrinsic.Arm64FcmpeS : Intrinsic.Arm64FcmpS; + + if ((op.Size & 1) != 0) + { + inst |= Intrinsic.Arm64VDouble; + } + + Operand n = GetVec(op.Rn); + Operand m = cmpWithZero ? Const(0) : GetVec(op.Rm); + + Operand nzcv = context.AddIntrinsicInt(inst, n, m); + + Operand one = Const(1); + + SetFlag(context, PState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(28)), one)); + SetFlag(context, PState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(29)), one)); + SetFlag(context, PState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(30)), one)); + SetFlag(context, PState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(31)), one)); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical.cs b/ARMeilleure/Instructions/InstEmitSimdLogical.cs index 624ae841d..8ca815801 100644 --- a/ARMeilleure/Instructions/InstEmitSimdLogical.cs +++ b/ARMeilleure/Instructions/InstEmitSimdLogical.cs @@ -14,7 +14,11 @@ namespace ARMeilleure.Instructions { public static void And_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64AndV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -38,7 +42,11 @@ namespace ARMeilleure.Instructions public static void Bic_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64BicV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -98,12 +106,26 @@ namespace ARMeilleure.Instructions public static void Bif_V(ArmEmitterContext context) { - EmitBifBit(context, notRm: true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64BifV); + } + else + { + EmitBifBit(context, notRm: true); + } } public static void Bit_V(ArmEmitterContext context) { - EmitBifBit(context, notRm: false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64BitV); + } + else + { + EmitBifBit(context, notRm: false); + } } private static void EmitBifBit(ArmEmitterContext context, bool notRm) @@ -167,7 +189,11 @@ namespace ARMeilleure.Instructions public static void Bsl_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64BslV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -200,7 +226,11 @@ namespace ARMeilleure.Instructions public static void Eor_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64EorV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -249,7 +279,11 @@ namespace ARMeilleure.Instructions public static void Orn_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrnV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -280,7 +314,11 @@ namespace ARMeilleure.Instructions public static void Orr_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrrV); + } + else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical32.cs b/ARMeilleure/Instructions/InstEmitSimdLogical32.cs index dd686d4dd..c2a04778b 100644 --- a/ARMeilleure/Instructions/InstEmitSimdLogical32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdLogical32.cs @@ -13,7 +13,11 @@ namespace ARMeilleure.Instructions { public static void Vand_I(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64AndV | Intrinsic.Arm64V128, n, m)); + } + else if (Optimizations.UseSse2) { EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pand, n, m)); } @@ -25,7 +29,11 @@ namespace ARMeilleure.Instructions public static void Vbic_I(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64BicV | Intrinsic.Arm64V128, n, m)); + } + else if (Optimizations.UseSse2) { EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pandn, m, n)); } @@ -73,17 +81,35 @@ namespace ARMeilleure.Instructions public static void Vbif(ArmEmitterContext context) { - EmitBifBit(context, true); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(Intrinsic.Arm64BifV | Intrinsic.Arm64V128, d, n, m)); + } + else + { + EmitBifBit(context, true); + } } public static void Vbit(ArmEmitterContext context) { - EmitBifBit(context, false); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(Intrinsic.Arm64BitV | Intrinsic.Arm64V128, d, n, m)); + } + else + { + EmitBifBit(context, false); + } } public static void Vbsl(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(Intrinsic.Arm64BslV | Intrinsic.Arm64V128, d, n, m)); + } + else if (Optimizations.UseSse2) { EmitVectorTernaryOpSimd32(context, (d, n, m) => { @@ -105,7 +131,11 @@ namespace ARMeilleure.Instructions public static void Veor_I(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64EorV | Intrinsic.Arm64V128, n, m)); + } + else if (Optimizations.UseSse2) { EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pxor, n, m)); } @@ -117,7 +147,11 @@ namespace ARMeilleure.Instructions public static void Vorn_I(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrnV | Intrinsic.Arm64V128, n, m)); + } + else if (Optimizations.UseSse2) { Operand mask = context.VectorOne(); @@ -135,7 +169,11 @@ namespace ARMeilleure.Instructions public static void Vorr_I(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrrV | Intrinsic.Arm64V128, n, m)); + } + else if (Optimizations.UseSse2) { EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Por, n, m)); } diff --git a/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/ARMeilleure/Instructions/InstEmitSimdMove32.cs index 7da180fc9..17100eb9c 100644 --- a/ARMeilleure/Instructions/InstEmitSimdMove32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdMove32.cs @@ -392,7 +392,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp; - if (Optimizations.UseSse2) + if (Optimizations.UseAdvSimd) + { + EmitVectorZipUzpOpSimd32(context, Intrinsic.Arm64Zip1V, Intrinsic.Arm64Zip2V); + } + else if (Optimizations.UseSse2) { EmitVectorShuffleOpSimd32(context, (m, d) => { @@ -461,7 +465,11 @@ namespace ARMeilleure.Instructions { OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp; - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + EmitVectorZipUzpOpSimd32(context, Intrinsic.Arm64Uzp1V, Intrinsic.Arm64Uzp2V); + } + else if (Optimizations.UseSsse3) { EmitVectorShuffleOpSimd32(context, (m, d) => { @@ -559,6 +567,52 @@ namespace ARMeilleure.Instructions } } + private static void EmitVectorZipUzpOpSimd32(ArmEmitterContext context, Intrinsic inst1, Intrinsic inst2) + { + OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp; + + bool overlap = op.Qm == op.Qd; + + Operand d = GetVecA32(op.Qd); + Operand m = GetVecA32(op.Qm); + + Operand dPart = d; + Operand mPart = m; + + if (!op.Q) // Register swap: move relevant doubleword to destination side. + { + dPart = InstEmitSimdHelper32Arm64.EmitMoveDoubleWordToSide(context, d, op.Vd, 0); + mPart = InstEmitSimdHelper32Arm64.EmitMoveDoubleWordToSide(context, m, op.Vm, 0); + } + + Intrinsic vSize = op.Q ? Intrinsic.Arm64V128 : Intrinsic.Arm64V64; + + vSize |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift); + + Operand resD = context.AddIntrinsic(inst1 | vSize, dPart, mPart); + Operand resM = context.AddIntrinsic(inst2 | vSize, dPart, mPart); + + if (!op.Q) // Register insert. + { + resD = context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, d, Const(op.Vd & 1), resD, Const(0)); + + if (overlap) + { + resD = context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, resD, Const(op.Vm & 1), resM, Const(0)); + } + else + { + resM = context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, m, Const(op.Vm & 1), resM, Const(0)); + } + } + + context.Copy(d, resD); + if (!overlap) + { + context.Copy(m, resM); + } + } + private static void EmitVectorShuffleOpSimd32(ArmEmitterContext context, Func shuffleFunc) { OpCode32Simd op = (OpCode32Simd)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitSimdShift.cs b/ARMeilleure/Instructions/InstEmitSimdShift.cs index cf3b51bd6..19e41119b 100644 --- a/ARMeilleure/Instructions/InstEmitSimdShift.cs +++ b/ARMeilleure/Instructions/InstEmitSimdShift.cs @@ -26,7 +26,15 @@ namespace ARMeilleure.Instructions public static void Rshrn_V(ArmEmitterContext context) { - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64RshrnV, shift); + } + else if (Optimizations.UseSsse3) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; @@ -80,7 +88,14 @@ namespace ARMeilleure.Instructions int shift = GetImmShl(op); - EmitScalarUnaryOpZx(context, (op1) => context.ShiftLeft(op1, Const(shift))); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarShiftBinaryOp(context, Intrinsic.Arm64ShlS, shift); + } + else + { + EmitScalarUnaryOpZx(context, (op1) => context.ShiftLeft(op1, Const(shift))); + } } public static void Shl_V(ArmEmitterContext context) @@ -90,7 +105,11 @@ namespace ARMeilleure.Instructions int shift = GetImmShl(op); int eSize = 8 << op.Size; - if (shift >= eSize) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorShiftBinaryOp(context, Intrinsic.Arm64ShlV, shift); + } + else if (shift >= eSize) { if ((op.RegisterSize == RegisterSize.Simd64)) { @@ -143,7 +162,11 @@ namespace ARMeilleure.Instructions int shift = 8 << op.Size; - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorUnaryOp(context, Intrinsic.Arm64ShllV); + } + else if (Optimizations.UseSse41) { Operand n = GetVec(op.Rn); @@ -170,7 +193,15 @@ namespace ARMeilleure.Instructions public static void Shrn_V(ArmEmitterContext context) { - if (Optimizations.UseSsse3) + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64ShrnV, shift); + } + else if (Optimizations.UseSsse3) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; @@ -205,89 +236,259 @@ namespace ARMeilleure.Instructions public static void Sli_S(ArmEmitterContext context) { - EmitSli(context, scalar: true); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShl(op); + + InstEmitSimdHelperArm64.EmitScalarShiftTernaryOpRd(context, Intrinsic.Arm64SliS, shift); + } + else + { + EmitSli(context, scalar: true); + } } public static void Sli_V(ArmEmitterContext context) { - EmitSli(context, scalar: false); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShl(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64SliV, shift); + } + else + { + EmitSli(context, scalar: false); + } } public static void Sqrshl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Round | ShlRegFlags.Saturating); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64SqrshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Round | ShlRegFlags.Saturating); + } } public static void Sqrshrn_S(ArmEmitterContext context) { - EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqrshrnS, shift); + } + else + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + } } public static void Sqrshrn_V(ArmEmitterContext context) { - EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqrshrnV, shift); + } + else + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + } } public static void Sqrshrun_S(ArmEmitterContext context) { - EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqrshrunS, shift); + } + else + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + } } public static void Sqrshrun_V(ArmEmitterContext context) { - EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqrshrunV, shift); + } + else + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + } } public static void Sqshl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Saturating); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64SqshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Saturating); + } } public static void Sqshrn_S(ArmEmitterContext context) { - EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqshrnS, shift); + } + else + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + } } public static void Sqshrn_V(ArmEmitterContext context) { - EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqshrnV, shift); + } + else + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + } } public static void Sqshrun_S(ArmEmitterContext context) { - EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqshrunS, shift); + } + else + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + } } public static void Sqshrun_V(ArmEmitterContext context) { - EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64SqshrunV, shift); + } + else + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + } } public static void Sri_S(ArmEmitterContext context) { - EmitSri(context, scalar: true); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftTernaryOpRd(context, Intrinsic.Arm64SriS, shift); + } + else + { + EmitSri(context, scalar: true); + } } public static void Sri_V(ArmEmitterContext context) { - EmitSri(context, scalar: false); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64SriV, shift); + } + else + { + EmitSri(context, scalar: false); + } } public static void Srshl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Round); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SrshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Signed | ShlRegFlags.Round); + } } public static void Srshr_S(ArmEmitterContext context) { - EmitScalarShrImmOpSx(context, ShrImmFlags.Round); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftBinaryOp(context, Intrinsic.Arm64SrshrS, shift); + } + else + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Round); + } } public static void Srshr_V(ArmEmitterContext context) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) + if (Optimizations.UseAdvSimd) + { + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftBinaryOp(context, Intrinsic.Arm64SrshrV, shift); + } + else if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) { int shift = GetImmShr(op); int eSize = 8 << op.Size; @@ -325,14 +526,31 @@ namespace ARMeilleure.Instructions public static void Srsra_S(ArmEmitterContext context) { - EmitScalarShrImmOpSx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftTernaryOpRd(context, Intrinsic.Arm64SrsraS, shift); + } + else + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } } public static void Srsra_V(ArmEmitterContext context) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) + if (Optimizations.UseAdvSimd) + { + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64SrsraV, shift); + } + else if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) { int shift = GetImmShr(op); int eSize = 8 << op.Size; @@ -372,12 +590,26 @@ namespace ARMeilleure.Instructions public static void Sshl_S(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Scalar | ShlRegFlags.Signed); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOp(context, Intrinsic.Arm64SshlS); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Scalar | ShlRegFlags.Signed); + } } public static void Sshl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Signed); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64SshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Signed); + } } public static void Sshll_V(ArmEmitterContext context) @@ -386,7 +618,11 @@ namespace ARMeilleure.Instructions int shift = GetImmShl(op); - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorShiftBinaryOp(context, Intrinsic.Arm64SshllV, shift); + } + else if (Optimizations.UseSse41) { Operand n = GetVec(op.Rn); @@ -416,7 +652,18 @@ namespace ARMeilleure.Instructions public static void Sshr_S(ArmEmitterContext context) { - EmitShrImmOp(context, ShrImmFlags.ScalarSx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftBinaryOp(context, Intrinsic.Arm64SshrS, shift); + } + else + { + EmitShrImmOp(context, ShrImmFlags.ScalarSx); + } } public static void Sshr_V(ArmEmitterContext context) @@ -425,7 +672,11 @@ namespace ARMeilleure.Instructions int shift = GetImmShr(op); - if (Optimizations.UseGfni && op.Size == 0) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorShiftBinaryOp(context, Intrinsic.Arm64SshrV, shift); + } + else if (Optimizations.UseGfni && op.Size == 0) { Operand n = GetVec(op.Rn); @@ -478,14 +729,31 @@ namespace ARMeilleure.Instructions public static void Ssra_S(ArmEmitterContext context) { - EmitScalarShrImmOpSx(context, ShrImmFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftTernaryOpRd(context, Intrinsic.Arm64SsraS, shift); + } + else + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Accumulate); + } } public static void Ssra_V(ArmEmitterContext context) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) + if (Optimizations.UseAdvSimd) + { + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64SsraV, shift); + } + else if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3) { int shift = GetImmShr(op); @@ -515,49 +783,131 @@ namespace ARMeilleure.Instructions public static void Uqrshl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Round | ShlRegFlags.Saturating); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64UqrshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Round | ShlRegFlags.Saturating); + } } public static void Uqrshrn_S(ArmEmitterContext context) { - EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64UqrshrnS, shift); + } + else + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + } } public static void Uqrshrn_V(ArmEmitterContext context) { - EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64UqrshrnV, shift); + } + else + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + } } public static void Uqshl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Saturating); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorSaturatingBinaryOp(context, Intrinsic.Arm64UqshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Saturating); + } } public static void Uqshrn_S(ArmEmitterContext context) { - EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64UqshrnS, shift); + } + else + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + } } public static void Uqshrn_V(ArmEmitterContext context) { - EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorSaturatingShiftTernaryOpRd(context, Intrinsic.Arm64UqshrnV, shift); + } + else + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + } } public static void Urshl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Round); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UrshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Round); + } } public static void Urshr_S(ArmEmitterContext context) { - EmitScalarShrImmOpZx(context, ShrImmFlags.Round); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftBinaryOp(context, Intrinsic.Arm64UrshrS, shift); + } + else + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Round); + } } public static void Urshr_V(ArmEmitterContext context) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0) + if (Optimizations.UseAdvSimd) + { + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftBinaryOp(context, Intrinsic.Arm64UrshrV, shift); + } + else if (Optimizations.UseSse2 && op.Size > 0) { int shift = GetImmShr(op); int eSize = 8 << op.Size; @@ -593,14 +943,31 @@ namespace ARMeilleure.Instructions public static void Ursra_S(ArmEmitterContext context) { - EmitScalarShrImmOpZx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftTernaryOpRd(context, Intrinsic.Arm64UrsraS, shift); + } + else + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } } public static void Ursra_V(ArmEmitterContext context) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0) + if (Optimizations.UseAdvSimd) + { + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64UrsraV, shift); + } + else if (Optimizations.UseSse2 && op.Size > 0) { int shift = GetImmShr(op); int eSize = 8 << op.Size; @@ -638,12 +1005,26 @@ namespace ARMeilleure.Instructions public static void Ushl_S(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.Scalar); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarBinaryOp(context, Intrinsic.Arm64UshlS); + } + else + { + EmitShlRegOp(context, ShlRegFlags.Scalar); + } } public static void Ushl_V(ArmEmitterContext context) { - EmitShlRegOp(context, ShlRegFlags.None); + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64UshlV); + } + else + { + EmitShlRegOp(context, ShlRegFlags.None); + } } public static void Ushll_V(ArmEmitterContext context) @@ -652,7 +1033,11 @@ namespace ARMeilleure.Instructions int shift = GetImmShl(op); - if (Optimizations.UseSse41) + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitVectorShiftBinaryOp(context, Intrinsic.Arm64UshllV, shift); + } + else if (Optimizations.UseSse41) { Operand n = GetVec(op.Rn); @@ -682,14 +1067,31 @@ namespace ARMeilleure.Instructions public static void Ushr_S(ArmEmitterContext context) { - EmitShrImmOp(context, ShrImmFlags.ScalarZx); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftBinaryOp(context, Intrinsic.Arm64UshrS, shift); + } + else + { + EmitShrImmOp(context, ShrImmFlags.ScalarZx); + } } public static void Ushr_V(ArmEmitterContext context) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0) + if (Optimizations.UseAdvSimd) + { + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftBinaryOp(context, Intrinsic.Arm64UshrV, shift); + } + else if (Optimizations.UseSse2 && op.Size > 0) { int shift = GetImmShr(op); @@ -714,14 +1116,31 @@ namespace ARMeilleure.Instructions public static void Usra_S(ArmEmitterContext context) { - EmitScalarShrImmOpZx(context, ShrImmFlags.Accumulate); + if (Optimizations.UseAdvSimd) + { + OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; + + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitScalarShiftTernaryOpRd(context, Intrinsic.Arm64UsraS, shift); + } + else + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Accumulate); + } } public static void Usra_V(ArmEmitterContext context) { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - if (Optimizations.UseSse2 && op.Size > 0) + if (Optimizations.UseAdvSimd) + { + int shift = GetImmShr(op); + + InstEmitSimdHelperArm64.EmitVectorShiftTernaryOpRd(context, Intrinsic.Arm64UsraV, shift); + } + else if (Optimizations.UseSse2 && op.Size > 0) { int shift = GetImmShr(op); diff --git a/ARMeilleure/Instructions/InstEmitSystem.cs b/ARMeilleure/Instructions/InstEmitSystem.cs index cc32228c3..1345bbf10 100644 --- a/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/ARMeilleure/Instructions/InstEmitSystem.cs @@ -150,6 +150,8 @@ namespace ARMeilleure.Instructions { OpCodeSystem op = (OpCodeSystem)context.CurrOp; + context.SyncQcFlag(); + Operand fpsr = Const(0); for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++) @@ -196,6 +198,8 @@ namespace ARMeilleure.Instructions { OpCodeSystem op = (OpCodeSystem)context.CurrOp; + context.ClearQcFlagIfModified(); + Operand fpsr = GetIntOrZR(context, op.Rt); fpsr = context.ConvertI64ToI32(fpsr); diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index bc1285be2..a665e4b7a 100644 --- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -2,6 +2,8 @@ namespace ARMeilleure.IntermediateRepresentation { enum Intrinsic : ushort { + // X86 (SSE and AVX) + X86Addpd, X86Addps, X86Addsd, @@ -172,6 +174,458 @@ namespace ARMeilleure.IntermediateRepresentation X86Vfnmsub231sd, X86Vfnmsub231ss, X86Xorpd, - X86Xorps + X86Xorps, + + // Arm64 (FP and Advanced SIMD) + + Arm64AbsS, + Arm64AbsV, + Arm64AddhnV, + Arm64AddpS, + Arm64AddpV, + Arm64AddvV, + Arm64AddS, + Arm64AddV, + Arm64AesdV, + Arm64AeseV, + Arm64AesimcV, + Arm64AesmcV, + Arm64AndV, + Arm64BicVi, + Arm64BicV, + Arm64BifV, + Arm64BitV, + Arm64BslV, + Arm64ClsV, + Arm64ClzV, + Arm64CmeqS, + Arm64CmeqV, + Arm64CmeqSz, + Arm64CmeqVz, + Arm64CmgeS, + Arm64CmgeV, + Arm64CmgeSz, + Arm64CmgeVz, + Arm64CmgtS, + Arm64CmgtV, + Arm64CmgtSz, + Arm64CmgtVz, + Arm64CmhiS, + Arm64CmhiV, + Arm64CmhsS, + Arm64CmhsV, + Arm64CmleSz, + Arm64CmleVz, + Arm64CmltSz, + Arm64CmltVz, + Arm64CmtstS, + Arm64CmtstV, + Arm64CntV, + Arm64DupSe, + Arm64DupVe, + Arm64DupGp, + Arm64EorV, + Arm64ExtV, + Arm64FabdS, + Arm64FabdV, + Arm64FabsV, + Arm64FabsS, + Arm64FacgeS, + Arm64FacgeV, + Arm64FacgtS, + Arm64FacgtV, + Arm64FaddpS, + Arm64FaddpV, + Arm64FaddV, + Arm64FaddS, + Arm64FccmpeS, + Arm64FccmpS, + Arm64FcmeqS, + Arm64FcmeqV, + Arm64FcmeqSz, + Arm64FcmeqVz, + Arm64FcmgeS, + Arm64FcmgeV, + Arm64FcmgeSz, + Arm64FcmgeVz, + Arm64FcmgtS, + Arm64FcmgtV, + Arm64FcmgtSz, + Arm64FcmgtVz, + Arm64FcmleSz, + Arm64FcmleVz, + Arm64FcmltSz, + Arm64FcmltVz, + Arm64FcmpeS, + Arm64FcmpS, + Arm64FcselS, + Arm64FcvtasS, + Arm64FcvtasV, + Arm64FcvtasGp, + Arm64FcvtauS, + Arm64FcvtauV, + Arm64FcvtauGp, + Arm64FcvtlV, + Arm64FcvtmsS, + Arm64FcvtmsV, + Arm64FcvtmsGp, + Arm64FcvtmuS, + Arm64FcvtmuV, + Arm64FcvtmuGp, + Arm64FcvtnsS, + Arm64FcvtnsV, + Arm64FcvtnsGp, + Arm64FcvtnuS, + Arm64FcvtnuV, + Arm64FcvtnuGp, + Arm64FcvtnV, + Arm64FcvtpsS, + Arm64FcvtpsV, + Arm64FcvtpsGp, + Arm64FcvtpuS, + Arm64FcvtpuV, + Arm64FcvtpuGp, + Arm64FcvtxnS, + Arm64FcvtxnV, + Arm64FcvtzsSFixed, + Arm64FcvtzsVFixed, + Arm64FcvtzsS, + Arm64FcvtzsV, + Arm64FcvtzsGpFixed, + Arm64FcvtzsGp, + Arm64FcvtzuSFixed, + Arm64FcvtzuVFixed, + Arm64FcvtzuS, + Arm64FcvtzuV, + Arm64FcvtzuGpFixed, + Arm64FcvtzuGp, + Arm64FcvtS, + Arm64FdivV, + Arm64FdivS, + Arm64FmaddS, + Arm64FmaxnmpS, + Arm64FmaxnmpV, + Arm64FmaxnmvV, + Arm64FmaxnmV, + Arm64FmaxnmS, + Arm64FmaxpS, + Arm64FmaxpV, + Arm64FmaxvV, + Arm64FmaxV, + Arm64FmaxS, + Arm64FminnmpS, + Arm64FminnmpV, + Arm64FminnmvV, + Arm64FminnmV, + Arm64FminnmS, + Arm64FminpS, + Arm64FminpV, + Arm64FminvV, + Arm64FminV, + Arm64FminS, + Arm64FmlaSe, + Arm64FmlaVe, + Arm64FmlaV, + Arm64FmlsSe, + Arm64FmlsVe, + Arm64FmlsV, + Arm64FmovVi, + Arm64FmovS, + Arm64FmovGp, + Arm64FmovSi, + Arm64FmsubS, + Arm64FmulxSe, + Arm64FmulxVe, + Arm64FmulxS, + Arm64FmulxV, + Arm64FmulSe, + Arm64FmulVe, + Arm64FmulV, + Arm64FmulS, + Arm64FnegV, + Arm64FnegS, + Arm64FnmaddS, + Arm64FnmsubS, + Arm64FnmulS, + Arm64FrecpeS, + Arm64FrecpeV, + Arm64FrecpsS, + Arm64FrecpsV, + Arm64FrecpxS, + Arm64FrintaV, + Arm64FrintaS, + Arm64FrintiV, + Arm64FrintiS, + Arm64FrintmV, + Arm64FrintmS, + Arm64FrintnV, + Arm64FrintnS, + Arm64FrintpV, + Arm64FrintpS, + Arm64FrintxV, + Arm64FrintxS, + Arm64FrintzV, + Arm64FrintzS, + Arm64FrsqrteS, + Arm64FrsqrteV, + Arm64FrsqrtsS, + Arm64FrsqrtsV, + Arm64FsqrtV, + Arm64FsqrtS, + Arm64FsubV, + Arm64FsubS, + Arm64InsVe, + Arm64InsGp, + Arm64Ld1rV, + Arm64Ld1Vms, + Arm64Ld1Vss, + Arm64Ld2rV, + Arm64Ld2Vms, + Arm64Ld2Vss, + Arm64Ld3rV, + Arm64Ld3Vms, + Arm64Ld3Vss, + Arm64Ld4rV, + Arm64Ld4Vms, + Arm64Ld4Vss, + Arm64MlaVe, + Arm64MlaV, + Arm64MlsVe, + Arm64MlsV, + Arm64MoviV, + Arm64MrsFpsr, + Arm64MsrFpsr, + Arm64MulVe, + Arm64MulV, + Arm64MvniV, + Arm64NegS, + Arm64NegV, + Arm64NotV, + Arm64OrnV, + Arm64OrrVi, + Arm64OrrV, + Arm64PmullV, + Arm64PmulV, + Arm64RaddhnV, + Arm64RbitV, + Arm64Rev16V, + Arm64Rev32V, + Arm64Rev64V, + Arm64RshrnV, + Arm64RsubhnV, + Arm64SabalV, + Arm64SabaV, + Arm64SabdlV, + Arm64SabdV, + Arm64SadalpV, + Arm64SaddlpV, + Arm64SaddlvV, + Arm64SaddlV, + Arm64SaddwV, + Arm64ScvtfSFixed, + Arm64ScvtfVFixed, + Arm64ScvtfS, + Arm64ScvtfV, + Arm64ScvtfGpFixed, + Arm64ScvtfGp, + Arm64Sha1cV, + Arm64Sha1hV, + Arm64Sha1mV, + Arm64Sha1pV, + Arm64Sha1su0V, + Arm64Sha1su1V, + Arm64Sha256h2V, + Arm64Sha256hV, + Arm64Sha256su0V, + Arm64Sha256su1V, + Arm64ShaddV, + Arm64ShllV, + Arm64ShlS, + Arm64ShlV, + Arm64ShrnV, + Arm64ShsubV, + Arm64SliS, + Arm64SliV, + Arm64SmaxpV, + Arm64SmaxvV, + Arm64SmaxV, + Arm64SminpV, + Arm64SminvV, + Arm64SminV, + Arm64SmlalVe, + Arm64SmlalV, + Arm64SmlslVe, + Arm64SmlslV, + Arm64SmovV, + Arm64SmullVe, + Arm64SmullV, + Arm64SqabsS, + Arm64SqabsV, + Arm64SqaddS, + Arm64SqaddV, + Arm64SqdmlalSe, + Arm64SqdmlalVe, + Arm64SqdmlalS, + Arm64SqdmlalV, + Arm64SqdmlslSe, + Arm64SqdmlslVe, + Arm64SqdmlslS, + Arm64SqdmlslV, + Arm64SqdmulhSe, + Arm64SqdmulhVe, + Arm64SqdmulhS, + Arm64SqdmulhV, + Arm64SqdmullSe, + Arm64SqdmullVe, + Arm64SqdmullS, + Arm64SqdmullV, + Arm64SqnegS, + Arm64SqnegV, + Arm64SqrdmulhSe, + Arm64SqrdmulhVe, + Arm64SqrdmulhS, + Arm64SqrdmulhV, + Arm64SqrshlS, + Arm64SqrshlV, + Arm64SqrshrnS, + Arm64SqrshrnV, + Arm64SqrshrunS, + Arm64SqrshrunV, + Arm64SqshluS, + Arm64SqshluV, + Arm64SqshlSi, + Arm64SqshlVi, + Arm64SqshlS, + Arm64SqshlV, + Arm64SqshrnS, + Arm64SqshrnV, + Arm64SqshrunS, + Arm64SqshrunV, + Arm64SqsubS, + Arm64SqsubV, + Arm64SqxtnS, + Arm64SqxtnV, + Arm64SqxtunS, + Arm64SqxtunV, + Arm64SrhaddV, + Arm64SriS, + Arm64SriV, + Arm64SrshlS, + Arm64SrshlV, + Arm64SrshrS, + Arm64SrshrV, + Arm64SrsraS, + Arm64SrsraV, + Arm64SshllV, + Arm64SshlS, + Arm64SshlV, + Arm64SshrS, + Arm64SshrV, + Arm64SsraS, + Arm64SsraV, + Arm64SsublV, + Arm64SsubwV, + Arm64St1Vms, + Arm64St1Vss, + Arm64St2Vms, + Arm64St2Vss, + Arm64St3Vms, + Arm64St3Vss, + Arm64St4Vms, + Arm64St4Vss, + Arm64SubhnV, + Arm64SubS, + Arm64SubV, + Arm64SuqaddS, + Arm64SuqaddV, + Arm64TblV, + Arm64TbxV, + Arm64Trn1V, + Arm64Trn2V, + Arm64UabalV, + Arm64UabaV, + Arm64UabdlV, + Arm64UabdV, + Arm64UadalpV, + Arm64UaddlpV, + Arm64UaddlvV, + Arm64UaddlV, + Arm64UaddwV, + Arm64UcvtfSFixed, + Arm64UcvtfVFixed, + Arm64UcvtfS, + Arm64UcvtfV, + Arm64UcvtfGpFixed, + Arm64UcvtfGp, + Arm64UhaddV, + Arm64UhsubV, + Arm64UmaxpV, + Arm64UmaxvV, + Arm64UmaxV, + Arm64UminpV, + Arm64UminvV, + Arm64UminV, + Arm64UmlalVe, + Arm64UmlalV, + Arm64UmlslVe, + Arm64UmlslV, + Arm64UmovV, + Arm64UmullVe, + Arm64UmullV, + Arm64UqaddS, + Arm64UqaddV, + Arm64UqrshlS, + Arm64UqrshlV, + Arm64UqrshrnS, + Arm64UqrshrnV, + Arm64UqshlSi, + Arm64UqshlVi, + Arm64UqshlS, + Arm64UqshlV, + Arm64UqshrnS, + Arm64UqshrnV, + Arm64UqsubS, + Arm64UqsubV, + Arm64UqxtnS, + Arm64UqxtnV, + Arm64UrecpeV, + Arm64UrhaddV, + Arm64UrshlS, + Arm64UrshlV, + Arm64UrshrS, + Arm64UrshrV, + Arm64UrsqrteV, + Arm64UrsraS, + Arm64UrsraV, + Arm64UshllV, + Arm64UshlS, + Arm64UshlV, + Arm64UshrS, + Arm64UshrV, + Arm64UsqaddS, + Arm64UsqaddV, + Arm64UsraS, + Arm64UsraV, + Arm64UsublV, + Arm64UsubwV, + Arm64Uzp1V, + Arm64Uzp2V, + Arm64XtnV, + Arm64Zip1V, + Arm64Zip2V, + + Arm64VTypeShift = 13, + Arm64VTypeMask = 1 << Arm64VTypeShift, + Arm64V64 = 0 << Arm64VTypeShift, + Arm64V128 = 1 << Arm64VTypeShift, + + Arm64VSizeShift = 14, + Arm64VSizeMask = 3 << Arm64VSizeShift, + Arm64VFloat = 0 << Arm64VSizeShift, + Arm64VDouble = 1 << Arm64VSizeShift, + Arm64VByte = 0 << Arm64VSizeShift, + Arm64VHWord = 1 << Arm64VSizeShift, + Arm64VWord = 2 << Arm64VSizeShift, + Arm64VDWord = 3 << Arm64VSizeShift } } \ No newline at end of file diff --git a/ARMeilleure/IntermediateRepresentation/Multiplier.cs b/ARMeilleure/IntermediateRepresentation/Multiplier.cs index 23582072b..d6bc7d994 100644 --- a/ARMeilleure/IntermediateRepresentation/Multiplier.cs +++ b/ARMeilleure/IntermediateRepresentation/Multiplier.cs @@ -5,6 +5,7 @@ namespace ARMeilleure.IntermediateRepresentation x1 = 0, x2 = 1, x4 = 2, - x8 = 3 + x8 = 3, + x16 = 4 } } \ No newline at end of file diff --git a/ARMeilleure/IntermediateRepresentation/Operand.cs b/ARMeilleure/IntermediateRepresentation/Operand.cs index 896d3420c..9e8de3ba4 100644 --- a/ARMeilleure/IntermediateRepresentation/Operand.cs +++ b/ARMeilleure/IntermediateRepresentation/Operand.cs @@ -259,6 +259,20 @@ namespace ARMeilleure.IntermediateRepresentation } } + public Span GetUses(ref Span buffer) + { + ReadOnlySpan uses = Uses; + + if (buffer.Length < uses.Length) + { + buffer = Allocators.Default.AllocateSpan((uint)uses.Length); + } + + uses.CopyTo(buffer); + + return buffer.Slice(0, uses.Length); + } + private static void New(ref T* data, ref ushort count, ref ushort capacity, ushort initialCapacity) where T : unmanaged { count = 0; diff --git a/ARMeilleure/IntermediateRepresentation/OperandType.cs b/ARMeilleure/IntermediateRepresentation/OperandType.cs index bfdf5130c..81b22cf56 100644 --- a/ARMeilleure/IntermediateRepresentation/OperandType.cs +++ b/ARMeilleure/IntermediateRepresentation/OperandType.cs @@ -47,5 +47,19 @@ namespace ARMeilleure.IntermediateRepresentation throw new InvalidOperationException($"Invalid operand type \"{type}\"."); } + + public static int GetSizeInBytesLog2(this OperandType type) + { + switch (type) + { + case OperandType.FP32: return 2; + case OperandType.FP64: return 3; + case OperandType.I32: return 2; + case OperandType.I64: return 3; + case OperandType.V128: return 4; + } + + throw new InvalidOperationException($"Invalid operand type \"{type}\"."); + } } } \ No newline at end of file diff --git a/ARMeilleure/Memory/ReservedRegion.cs b/ARMeilleure/Memory/ReservedRegion.cs index d63491089..2197afad9 100644 --- a/ARMeilleure/Memory/ReservedRegion.cs +++ b/ARMeilleure/Memory/ReservedRegion.cs @@ -4,7 +4,7 @@ namespace ARMeilleure.Memory { class ReservedRegion { - private const int DefaultGranularity = 65536; // Mapping granularity in Windows. + public const int DefaultGranularity = 65536; // Mapping granularity in Windows. public IJitMemoryBlock Block { get; } diff --git a/ARMeilleure/Native/JitSupportDarwin.cs b/ARMeilleure/Native/JitSupportDarwin.cs new file mode 100644 index 000000000..7d6a8634a --- /dev/null +++ b/ARMeilleure/Native/JitSupportDarwin.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace ARMeilleure.Native +{ + [SupportedOSPlatform("macos")] + public static partial class JitSupportDarwin + { + [LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")] + public static partial void Copy(IntPtr dst, IntPtr src, ulong n); + } +} diff --git a/ARMeilleure/Native/libs/libarmeilleure-jitsupport.dylib b/ARMeilleure/Native/libs/libarmeilleure-jitsupport.dylib new file mode 100644 index 0000000000000000000000000000000000000000..c65b0a4efb797fdc0280c4edf5557d16142d2347 GIT binary patch literal 33564 zcmeI*UuYaf90%~ZyJ zO&aP&snmZ+Az~CoK}GM37{rHAh>Z$b`chIsQK7{8pb6+hZ5u5{{rzU=uG@pO4+WpT zLuO}xGkd??{p{v(c8;Chj`_Oy9;CdltNY%Ltz9MXJauw0)mD_W z8}*O*delpzhp(cxjVhlv*gt4*PiBWKdvMUoWv%92w&&&?CzJJBQo}SRZl}!Z&C2^e zn{+%uag1`YDd(Kc^CPGIF(DGvUP<+Ss=>nN&zdgNl z=Qkg}^UIA^)4uO-qrMnb&MO4J9kqdTd~Mu9J^p_NkNZcoQOmjf_hAFQ_94-lbF-~< z!(4lZUC$b7*`f*fXQGKJ=b6q6QA6!U9rJy2xt=PIJ9Pb%{>5-N3Irek0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P$##AOHafKmY;|fB*y_a1nvC{WFErJ)y$#UZb%5j5zp5*eIS3hl*#yqDWJlAD}tB z;wp+`)W5piUsqh-yM=PZ3upVk{NM^9>ghLAQN#R87#qG-`_+N{ z)&4$D32xNa6>ND9GS8i-dDg1FqUWu1%=4sqHgifpXF**~k3C~Nx^EU#5%Z-=T zR;TX}sKV=<3zJqXE+S2zPsEg7tA!o2Ro~6cJ#O?QBL9wlIy$odhnW{T zpS$(_OO2-@&sIHjc;uuv-t@-Yk2Sm8&4;#~uwOrL$KMBE?&xfPB7611tEVD`>4tb+ z%i`xp$BxWReOTYM?%jr_rBw^he>Q)q{Fgg^=#hIy;-`+RYb|b!ytugdTIP52&n34v O_VfJwTcb0Z+W!MamVHnF literal 0 HcmV?d00001 diff --git a/ARMeilleure/Native/macos_jit_support/Makefile b/ARMeilleure/Native/macos_jit_support/Makefile new file mode 100644 index 000000000..d6da35d52 --- /dev/null +++ b/ARMeilleure/Native/macos_jit_support/Makefile @@ -0,0 +1,8 @@ +NAME = libarmeilleure-jitsupport.dylib + +all: ${NAME} + +${NAME}: + clang -O3 -dynamiclib support.c -o ${NAME} +clean: + rm -f ${NAME} diff --git a/ARMeilleure/Native/macos_jit_support/support.c b/ARMeilleure/Native/macos_jit_support/support.c new file mode 100644 index 000000000..1b13d9066 --- /dev/null +++ b/ARMeilleure/Native/macos_jit_support/support.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include + +void armeilleure_jit_memcpy(void *dst, const void *src, size_t n) { + pthread_jit_write_protect_np(0); + memcpy(dst, src, n); + pthread_jit_write_protect_np(1); + + // Ensure that the instruction cache for this range is invalidated. + sys_icache_invalidate(dst, n); +} diff --git a/ARMeilleure/Optimizations.cs b/ARMeilleure/Optimizations.cs index 97defd9a9..0810d96c9 100644 --- a/ARMeilleure/Optimizations.cs +++ b/ARMeilleure/Optimizations.cs @@ -1,4 +1,5 @@ using ARMeilleure.CodeGen.X86; +using System.Runtime.Intrinsics.Arm; namespace ARMeilleure { @@ -9,6 +10,8 @@ namespace ARMeilleure public static bool AllowLcqInFunctionTable { get; set; } = true; public static bool UseUnmanagedDispatchLoop { get; set; } = true; + public static bool UseAdvSimdIfAvailable { get; set; } = true; + public static bool UseSseIfAvailable { get; set; } = true; public static bool UseSse2IfAvailable { get; set; } = true; public static bool UseSse3IfAvailable { get; set; } = true; @@ -30,6 +33,8 @@ namespace ARMeilleure set => HardwareCapabilities.ForceLegacySse = value; } + internal static bool UseAdvSimd => UseAdvSimdIfAvailable && AdvSimd.IsSupported; + internal static bool UseSse => UseSseIfAvailable && HardwareCapabilities.SupportsSse; internal static bool UseSse2 => UseSse2IfAvailable && HardwareCapabilities.SupportsSse2; internal static bool UseSse3 => UseSse3IfAvailable && HardwareCapabilities.SupportsSse3; diff --git a/ARMeilleure/Signal/NativeSignalHandler.cs b/ARMeilleure/Signal/NativeSignalHandler.cs index 0257f4403..da02f76a8 100644 --- a/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/ARMeilleure/Signal/NativeSignalHandler.cs @@ -1,5 +1,7 @@ using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Memory; using ARMeilleure.Translation; +using ARMeilleure.Translation.Cache; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -69,8 +71,8 @@ namespace ARMeilleure.Signal private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005; - private const ulong PageSize = 0x1000; - private const ulong PageMask = PageSize - 1; + private static ulong _pageSize = GetPageSize(); + private static ulong _pageMask = _pageSize - 1; private static IntPtr _handlerConfig; private static IntPtr _signalHandlerPtr; @@ -79,6 +81,19 @@ namespace ARMeilleure.Signal private static readonly object _lock = new object(); private static bool _initialized; + private static ulong GetPageSize() + { + // TODO: This needs to be based on the current memory manager configuration. + if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return 1UL << 14; + } + else + { + return 1UL << 12; + } + } + static NativeSignalHandler() { _handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf()); @@ -87,7 +102,12 @@ namespace ARMeilleure.Signal config = new SignalHandlerConfig(); } - public static void InitializeSignalHandler() + public static void InitializeJitCache(IJitMemoryAllocator allocator) + { + JitCache.Initialize(allocator); + } + + public static void InitializeSignalHandler(Func customSignalHandlerFactory = null) { if (_initialized) return; @@ -95,10 +115,9 @@ namespace ARMeilleure.Signal { if (_initialized) return; - bool unix = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); ref SignalHandlerConfig config = ref GetConfigRef(); - if (unix) + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { // Unix siginfo struct locations. // NOTE: These are incredibly likely to be different between kernel version and architectures. @@ -108,7 +127,13 @@ namespace ARMeilleure.Signal _signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig)); - SigAction old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); + if (customSignalHandlerFactory != null) + { + _signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr); + } + + var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); + config.UnixOldSigaction = (nuint)(ulong)old.sa_handler; config.UnixOldSigaction3Arg = old.sa_flags & 4; } @@ -119,6 +144,11 @@ namespace ARMeilleure.Signal _signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateWindowsSignalHandler(_handlerConfig)); + if (customSignalHandlerFactory != null) + { + _signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr); + } + _signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); } @@ -197,7 +227,7 @@ namespace ARMeilleure.Signal // Only call tracking if in range. context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold); - Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~PageMask)); + Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~_pageMask)); // Call the tracking action, with the pointer's relative offset to the base address. Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20)); @@ -208,7 +238,7 @@ namespace ARMeilleure.Signal // Tracking action should be non-null to call it, otherwise assume false return. context.BranchIfFalse(skipActionLabel, trackingActionPtr); - Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(PageSize), isWrite, Const(0)); + Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite, Const(0)); context.Copy(inRegionLocal, result); context.MarkLabel(skipActionLabel); @@ -278,7 +308,7 @@ namespace ARMeilleure.Signal OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq).Map(); + return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr) @@ -332,7 +362,7 @@ namespace ARMeilleure.Signal OperandType[] argTypes = new OperandType[] { OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq).Map(); + return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } } } diff --git a/ARMeilleure/Signal/TestMethods.cs b/ARMeilleure/Signal/TestMethods.cs index 2d7cef166..e2ecad242 100644 --- a/ARMeilleure/Signal/TestMethods.cs +++ b/ARMeilleure/Signal/TestMethods.cs @@ -1,7 +1,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; - +using System.Runtime.InteropServices; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Signal @@ -32,7 +32,7 @@ namespace ARMeilleure.Signal OperandType[] argTypes = new OperandType[] { OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq).Map(); + return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(IntPtr structPtr) @@ -49,7 +49,7 @@ namespace ARMeilleure.Signal OperandType[] argTypes = new OperandType[] { OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq).Map(); + return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop() @@ -78,7 +78,7 @@ namespace ARMeilleure.Signal OperandType[] argTypes = new OperandType[] { OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq).Map(); + return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } } } diff --git a/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs b/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs index 945a01dae..22009240b 100644 --- a/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs +++ b/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs @@ -3,23 +3,23 @@ using System.Runtime.InteropServices; namespace ARMeilleure.Signal { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - unsafe struct SigSet - { - fixed long sa_mask[16]; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct SigAction - { - public IntPtr sa_handler; - public SigSet sa_mask; - public int sa_flags; - public IntPtr sa_restorer; - } - static partial class UnixSignalHandlerRegistration { + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct SigSet + { + fixed long sa_mask[16]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SigAction + { + public IntPtr sa_handler; + public SigSet sa_mask; + public int sa_flags; + public IntPtr sa_restorer; + } + private const int SIGSEGV = 11; private const int SIGBUS = 10; private const int SA_SIGINFO = 0x00000004; @@ -27,9 +27,24 @@ namespace ARMeilleure.Signal [LibraryImport("libc", SetLastError = true)] private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction); + [LibraryImport("libc", SetLastError = true)] + private static partial int sigaction(int signum, IntPtr sigAction, out SigAction oldAction); + [LibraryImport("libc", SetLastError = true)] private static partial int sigemptyset(ref SigSet set); + public static SigAction GetSegfaultExceptionHandler() + { + int result = sigaction(SIGSEGV, IntPtr.Zero, out SigAction old); + + if (result != 0) + { + throw new InvalidOperationException($"Could not get SIGSEGV sigaction. Error: {result}"); + } + + return old; + } + public static SigAction RegisterExceptionHandler(IntPtr action) { SigAction sig = new SigAction @@ -49,7 +64,7 @@ namespace ARMeilleure.Signal if (OperatingSystem.IsMacOS()) { - result = sigaction(SIGBUS, ref sig, out SigAction oldb); + result = sigaction(SIGBUS, ref sig, out _); if (result != 0) { diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/ARMeilleure/Translation/ArmEmitterContext.cs index 48254de4e..238f85082 100644 --- a/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/ARMeilleure/Translation/ArmEmitterContext.cs @@ -39,6 +39,8 @@ namespace ARMeilleure.Translation } } + private bool _pendingQcFlagSync; + public OpCode CurrOp { get; set; } public IMemoryManager Memory { get; } @@ -81,6 +83,8 @@ namespace ARMeilleure.Translation public override Operand Call(MethodInfo info, params Operand[] callArgs) { + SyncQcFlag(); + if (!HasPtc) { return base.Call(info, callArgs); @@ -139,6 +143,51 @@ namespace ARMeilleure.Translation _optOpLastFlagSet = null; } + public void SetPendingQcFlagSync() + { + _pendingQcFlagSync = true; + } + + public void SyncQcFlag() + { + if (_pendingQcFlagSync) + { + if (Optimizations.UseAdvSimd) + { + Operand fpsr = AddIntrinsicInt(Intrinsic.Arm64MrsFpsr); + + uint qcFlagMask = (uint)FPSR.Qc; + + Operand qcClearLabel = Label(); + + BranchIfFalse(qcClearLabel, BitwiseAnd(fpsr, Const(qcFlagMask))); + + AddIntrinsicNoRet(Intrinsic.Arm64MsrFpsr, Const(0)); + InstEmitHelper.SetFpFlag(this, FPState.QcFlag, Const(1)); + + MarkLabel(qcClearLabel); + } + + _pendingQcFlagSync = false; + } + } + + public void ClearQcFlag() + { + if (Optimizations.UseAdvSimd) + { + AddIntrinsicNoRet(Intrinsic.Arm64MsrFpsr, Const(0)); + } + } + + public void ClearQcFlagIfModified() + { + if (_pendingQcFlagSync && Optimizations.UseAdvSimd) + { + AddIntrinsicNoRet(Intrinsic.Arm64MsrFpsr, Const(0)); + } + } + public Operand TryGetComparisonResult(Condition condition) { if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet) diff --git a/ARMeilleure/Translation/Cache/JitCache.cs b/ARMeilleure/Translation/Cache/JitCache.cs index 24affa34e..f496a8e9c 100644 --- a/ARMeilleure/Translation/Cache/JitCache.cs +++ b/ARMeilleure/Translation/Cache/JitCache.cs @@ -1,6 +1,7 @@ using ARMeilleure.CodeGen; using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.Memory; +using ARMeilleure.Native; using System; using System.Collections.Generic; using System.Diagnostics; @@ -17,6 +18,7 @@ namespace ARMeilleure.Translation.Cache private const int CacheSize = 2047 * 1024 * 1024; private static ReservedRegion _jitRegion; + private static JitCacheInvalidation _jitCacheInvalidator; private static CacheMemoryAllocator _cacheAllocator; @@ -25,8 +27,6 @@ namespace ARMeilleure.Translation.Cache private static readonly object _lock = new object(); private static bool _initialized; - public static IntPtr Base => _jitRegion.Pointer; - public static void Initialize(IJitMemoryAllocator allocator) { if (_initialized) return; @@ -36,6 +36,7 @@ namespace ARMeilleure.Translation.Cache if (_initialized) return; _jitRegion = new ReservedRegion(allocator, CacheSize); + _jitCacheInvalidator = new JitCacheInvalidation(allocator); _cacheAllocator = new CacheMemoryAllocator(CacheSize); @@ -60,11 +61,24 @@ namespace ARMeilleure.Translation.Cache IntPtr funcPtr = _jitRegion.Pointer + funcOffset; - ReprotectAsWritable(funcOffset, code.Length); + if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + unsafe + { + fixed (byte *codePtr = code) + { + JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length); + } + } + } + else + { + ReprotectAsWritable(funcOffset, code.Length); + Marshal.Copy(code, 0, funcPtr, code.Length); + ReprotectAsExecutable(funcOffset, code.Length); - Marshal.Copy(code, 0, funcPtr, code.Length); - - ReprotectAsExecutable(funcOffset, code.Length); + _jitCacheInvalidator.Invalidate(funcPtr, (ulong)code.Length); + } Add(funcOffset, code.Length, func.UnwindInfo); diff --git a/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs b/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs new file mode 100644 index 000000000..ec2ae73bb --- /dev/null +++ b/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs @@ -0,0 +1,79 @@ +using ARMeilleure.Memory; +using System; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Translation.Cache +{ + class JitCacheInvalidation + { + private static int[] _invalidationCode = new int[] + { + unchecked((int)0xd53b0022), // mrs x2, ctr_el0 + unchecked((int)0xd3504c44), // ubfx x4, x2, #16, #4 + unchecked((int)0x52800083), // mov w3, #0x4 + unchecked((int)0x12000c45), // and w5, w2, #0xf + unchecked((int)0x1ac42064), // lsl w4, w3, w4 + unchecked((int)0x51000482), // sub w2, w4, #0x1 + unchecked((int)0x8a220002), // bic x2, x0, x2 + unchecked((int)0x1ac52063), // lsl w3, w3, w5 + unchecked((int)0xeb01005f), // cmp x2, x1 + unchecked((int)0x93407c84), // sxtw x4, w4 + unchecked((int)0x540000a2), // b.cs 3c + unchecked((int)0xd50b7b22), // dc cvau, x2 + unchecked((int)0x8b040042), // add x2, x2, x4 + unchecked((int)0xeb02003f), // cmp x1, x2 + unchecked((int)0x54ffffa8), // b.hi 2c + unchecked((int)0xd5033b9f), // dsb ish + unchecked((int)0x51000462), // sub w2, w3, #0x1 + unchecked((int)0x93407c63), // sxtw x3, w3 + unchecked((int)0x8a220000), // bic x0, x0, x2 + unchecked((int)0xeb00003f), // cmp x1, x0 + unchecked((int)0x540000a9), // b.ls 64 + unchecked((int)0xd50b7520), // ic ivau, x0 + unchecked((int)0x8b030000), // add x0, x0, x3 + unchecked((int)0xeb00003f), // cmp x1, x0 + unchecked((int)0x54ffffa8), // b.hi 54 + unchecked((int)0xd5033b9f), // dsb ish + unchecked((int)0xd5033fdf), // isb + unchecked((int)0xd65f03c0), // ret + }; + + private delegate void InvalidateCache(ulong start, ulong end); + + private InvalidateCache _invalidateCache; + private ReservedRegion _invalidateCacheCodeRegion; + + private readonly bool _needsInvalidation; + + public JitCacheInvalidation(IJitMemoryAllocator allocator) + { + // On macOS, a different path is used to write to the JIT cache, which does the invalidation. + if (!OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + ulong size = (ulong)_invalidationCode.Length * sizeof(int); + ulong mask = (ulong)ReservedRegion.DefaultGranularity - 1; + + size = (size + mask) & ~mask; + + _invalidateCacheCodeRegion = new ReservedRegion(allocator, size); + _invalidateCacheCodeRegion.ExpandIfNeeded(size); + + Marshal.Copy(_invalidationCode, 0, _invalidateCacheCodeRegion.Pointer, _invalidationCode.Length); + + _invalidateCacheCodeRegion.Block.MapAsRx(0, size); + + _invalidateCache = Marshal.GetDelegateForFunctionPointer(_invalidateCacheCodeRegion.Pointer); + + _needsInvalidation = true; + } + } + + public void Invalidate(IntPtr basePointer, ulong size) + { + if (_needsInvalidation) + { + _invalidateCache((ulong)basePointer, (ulong)basePointer + size); + } + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/Compiler.cs b/ARMeilleure/Translation/Compiler.cs index 817bd487e..d4aa5cd96 100644 --- a/ARMeilleure/Translation/Compiler.cs +++ b/ARMeilleure/Translation/Compiler.cs @@ -1,8 +1,9 @@ using ARMeilleure.CodeGen; using ARMeilleure.CodeGen.Optimizations; -using ARMeilleure.CodeGen.X86; using ARMeilleure.Diagnostics; using ARMeilleure.IntermediateRepresentation; +using System; +using System.Runtime.InteropServices; namespace ARMeilleure.Translation { @@ -12,7 +13,8 @@ namespace ARMeilleure.Translation ControlFlowGraph cfg, OperandType[] argTypes, OperandType retType, - CompilerOptions options) + CompilerOptions options, + Architecture target) { CompilerContext cctx = new(cfg, argTypes, retType, options); @@ -49,7 +51,18 @@ namespace ARMeilleure.Translation Logger.EndPass(PassName.RegisterToLocal, cfg); } - return CodeGenerator.Generate(cctx); + if (target == Architecture.X64) + { + return CodeGen.X86.CodeGenerator.Generate(cctx); + } + else if (target == Architecture.Arm64) + { + return CodeGen.Arm64.CodeGenerator.Generate(cctx); + } + else + { + throw new NotImplementedException(target.ToString()); + } } } } \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index f99d6e516..6f57e1883 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 4159; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 4114; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 77ccdaeab..75c4df23e 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -282,7 +283,7 @@ namespace ARMeilleure.Translation options |= CompilerOptions.Relocatable; } - CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options); + CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options, RuntimeInformation.ProcessArchitecture); if (context.HasPtc && !singleStep) { @@ -359,9 +360,14 @@ namespace ARMeilleure.Translation } } - if (block.Address == context.EntryAddress && !context.HighCq) + if (block.Address == context.EntryAddress) { - EmitRejitCheck(context, out counter); + if (!context.HighCq) + { + EmitRejitCheck(context, out counter); + } + + context.ClearQcFlag(); } context.CurrBlock = block; @@ -386,9 +392,14 @@ namespace ARMeilleure.Translation bool isLastOp = opcIndex == block.OpCodes.Count - 1; - if (isLastOp && block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address) + if (isLastOp) { - EmitSynchronization(context); + context.SyncQcFlag(); + + if (block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address) + { + EmitSynchronization(context); + } } Operand lblPredicateSkip = default; diff --git a/ARMeilleure/Translation/TranslatorStubs.cs b/ARMeilleure/Translation/TranslatorStubs.cs index 67d2bba8e..6ed84de80 100644 --- a/ARMeilleure/Translation/TranslatorStubs.cs +++ b/ARMeilleure/Translation/TranslatorStubs.cs @@ -171,7 +171,7 @@ namespace ARMeilleure.Translation var retType = OperandType.I64; var argTypes = new[] { OperandType.I64 }; - var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map(); + var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); return Marshal.GetFunctionPointerForDelegate(func); } @@ -197,7 +197,7 @@ namespace ARMeilleure.Translation var retType = OperandType.I64; var argTypes = new[] { OperandType.I64 }; - var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map(); + var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); return Marshal.GetFunctionPointerForDelegate(func); } @@ -235,7 +235,7 @@ namespace ARMeilleure.Translation var retType = OperandType.None; var argTypes = new[] { OperandType.I64, OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map(); + return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } } } diff --git a/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs b/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs index 27bb09ccb..0cf35c17b 100644 --- a/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs +++ b/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Cpu.Jit public class JitMemoryAllocator : IJitMemoryAllocator { public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None); - public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve); + public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Jit); } } diff --git a/Ryujinx.Memory/MemoryAllocationFlags.cs b/Ryujinx.Memory/MemoryAllocationFlags.cs index 313f33e5f..6f0ef1aa9 100644 --- a/Ryujinx.Memory/MemoryAllocationFlags.cs +++ b/Ryujinx.Memory/MemoryAllocationFlags.cs @@ -35,6 +35,18 @@ namespace Ryujinx.Memory /// Indicates that the memory block should support mapping views of a mirrorable memory block. /// The block that is to have their views mapped should be created with the flag. ///
    - ViewCompatible = 1 << 3 + ViewCompatible = 1 << 3, + + /// + /// If used with the flag, indicates that the memory block will only be used as + /// backing storage and will never be accessed directly, so the memory for the block will not be mapped. + /// + NoMap = 1 << 4, + + /// + /// Indicates that the memory will be used to store JIT generated code. + /// On some platforms, this requires special flags to be passed that will allow the memory to be executable. + /// + Jit = 1 << 5 } } diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 6b9d852de..e1f19c27a 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Concurrent; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Memory @@ -13,10 +13,9 @@ namespace Ryujinx.Memory private readonly bool _usesSharedMemory; private readonly bool _isMirror; private readonly bool _viewCompatible; + private readonly bool _forJit; private IntPtr _sharedMemory; private IntPtr _pointer; - private ConcurrentDictionary _viewStorages; - private int _viewCount; /// /// Pointer to the memory block data. @@ -40,24 +39,27 @@ namespace Ryujinx.Memory if (flags.HasFlag(MemoryAllocationFlags.Mirrorable)) { _sharedMemory = MemoryManagement.CreateSharedMemory(size, flags.HasFlag(MemoryAllocationFlags.Reserve)); - _pointer = MemoryManagement.MapSharedMemory(_sharedMemory, size); + + if (!flags.HasFlag(MemoryAllocationFlags.NoMap)) + { + _pointer = MemoryManagement.MapSharedMemory(_sharedMemory, size); + } + _usesSharedMemory = true; } else if (flags.HasFlag(MemoryAllocationFlags.Reserve)) { _viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible); - _pointer = MemoryManagement.Reserve(size, _viewCompatible); + _forJit = flags.HasFlag(MemoryAllocationFlags.Jit); + _pointer = MemoryManagement.Reserve(size, _forJit, _viewCompatible); } else { - _pointer = MemoryManagement.Allocate(size); + _forJit = flags.HasFlag(MemoryAllocationFlags.Jit); + _pointer = MemoryManagement.Allocate(size, _forJit); } Size = size; - - _viewStorages = new ConcurrentDictionary(); - _viewStorages.TryAdd(this, 0); - _viewCount = 1; } /// @@ -104,7 +106,7 @@ namespace Ryujinx.Memory /// Throw when either or are out of range public bool Commit(ulong offset, ulong size) { - return MemoryManagement.Commit(GetPointerInternal(offset, size), size); + return MemoryManagement.Commit(GetPointerInternal(offset, size), size, _forJit); } /// @@ -138,11 +140,6 @@ namespace Ryujinx.Memory throw new ArgumentException("The source memory block is not mirrorable, and thus cannot be mapped on the current block."); } - if (_viewStorages.TryAdd(srcBlock, 0)) - { - srcBlock.IncrementViewCount(); - } - MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, this); } @@ -403,33 +400,16 @@ namespace Ryujinx.Memory { MemoryManagement.Free(ptr, Size); } - - foreach (MemoryBlock viewStorage in _viewStorages.Keys) - { - viewStorage.DecrementViewCount(); - } - - _viewStorages.Clear(); } - } - /// - /// Increments the number of views that uses this memory block as storage. - /// - private void IncrementViewCount() - { - Interlocked.Increment(ref _viewCount); - } - - /// - /// Decrements the number of views that uses this memory block as storage. - /// - private void DecrementViewCount() - { - if (Interlocked.Decrement(ref _viewCount) == 0 && _sharedMemory != IntPtr.Zero && !_isMirror) + if (!_isMirror) { - MemoryManagement.DestroySharedMemory(_sharedMemory); - _sharedMemory = IntPtr.Zero; + IntPtr sharedMemory = Interlocked.Exchange(ref _sharedMemory, IntPtr.Zero); + + if (sharedMemory != IntPtr.Zero) + { + MemoryManagement.DestroySharedMemory(sharedMemory); + } } } @@ -453,6 +433,16 @@ namespace Ryujinx.Memory return true; } + public static ulong GetPageSize() + { + if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return 1UL << 14; + } + + return 1UL << 12; + } + private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); } } diff --git a/Ryujinx.Memory/MemoryManagement.cs b/Ryujinx.Memory/MemoryManagement.cs index 7c042eba3..c4b5ac4c9 100644 --- a/Ryujinx.Memory/MemoryManagement.cs +++ b/Ryujinx.Memory/MemoryManagement.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Memory { public static class MemoryManagement { - public static IntPtr Allocate(ulong size) + public static IntPtr Allocate(ulong size, bool forJit) { if (OperatingSystem.IsWindows()) { @@ -12,7 +12,7 @@ namespace Ryujinx.Memory } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - return MemoryManagementUnix.Allocate(size); + return MemoryManagementUnix.Allocate(size, forJit); } else { @@ -20,7 +20,7 @@ namespace Ryujinx.Memory } } - public static IntPtr Reserve(ulong size, bool viewCompatible) + public static IntPtr Reserve(ulong size, bool forJit, bool viewCompatible) { if (OperatingSystem.IsWindows()) { @@ -28,7 +28,7 @@ namespace Ryujinx.Memory } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - return MemoryManagementUnix.Reserve(size); + return MemoryManagementUnix.Reserve(size, forJit); } else { @@ -36,7 +36,7 @@ namespace Ryujinx.Memory } } - public static bool Commit(IntPtr address, ulong size) + public static bool Commit(IntPtr address, ulong size, bool forJit) { if (OperatingSystem.IsWindows()) { @@ -44,7 +44,7 @@ namespace Ryujinx.Memory } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - return MemoryManagementUnix.Commit(address, size); + return MemoryManagementUnix.Commit(address, size, forJit); } else { diff --git a/Ryujinx.Memory/MemoryManagementUnix.cs b/Ryujinx.Memory/MemoryManagementUnix.cs index df3fcea91..affcff92b 100644 --- a/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/Ryujinx.Memory/MemoryManagementUnix.cs @@ -13,17 +13,17 @@ namespace Ryujinx.Memory { private static readonly ConcurrentDictionary _allocations = new ConcurrentDictionary(); - public static IntPtr Allocate(ulong size) + public static IntPtr Allocate(ulong size, bool forJit) { - return AllocateInternal(size, MmapProts.PROT_READ | MmapProts.PROT_WRITE); + return AllocateInternal(size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, forJit); } - public static IntPtr Reserve(ulong size) + public static IntPtr Reserve(ulong size, bool forJit) { - return AllocateInternal(size, MmapProts.PROT_NONE); + return AllocateInternal(size, MmapProts.PROT_NONE, forJit); } - private static IntPtr AllocateInternal(ulong size, MmapProts prot, bool shared = false) + private static IntPtr AllocateInternal(ulong size, MmapProts prot, bool forJit, bool shared = false) { MmapFlags flags = MmapFlags.MAP_ANONYMOUS; @@ -41,6 +41,16 @@ namespace Ryujinx.Memory flags |= MmapFlags.MAP_NORESERVE; } + if (OperatingSystem.IsMacOSVersionAtLeast(10, 14) && forJit) + { + flags |= MmapFlags.MAP_JIT_DARWIN; + + if (prot == (MmapProts.PROT_READ | MmapProts.PROT_WRITE)) + { + prot |= MmapProts.PROT_EXEC; + } + } + IntPtr ptr = mmap(IntPtr.Zero, size, prot, flags, -1, 0); if (ptr == new IntPtr(-1L)) @@ -57,9 +67,16 @@ namespace Ryujinx.Memory return ptr; } - public static bool Commit(IntPtr address, ulong size) + public static bool Commit(IntPtr address, ulong size, bool forJit) { - return mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) == 0; + MmapProts prot = MmapProts.PROT_READ | MmapProts.PROT_WRITE; + + if (OperatingSystem.IsMacOSVersionAtLeast(10, 14) && forJit) + { + prot |= MmapProts.PROT_EXEC; + } + + return mprotect(address, size, prot) == 0; } public static bool Decommit(IntPtr address, ulong size) diff --git a/Ryujinx.Memory/MemoryManagerUnixHelper.cs b/Ryujinx.Memory/MemoryManagerUnixHelper.cs index 87a81a79b..204f1ca4d 100644 --- a/Ryujinx.Memory/MemoryManagerUnixHelper.cs +++ b/Ryujinx.Memory/MemoryManagerUnixHelper.cs @@ -22,7 +22,8 @@ namespace Ryujinx.Memory MAP_ANONYMOUS = 4, MAP_NORESERVE = 8, MAP_FIXED = 16, - MAP_UNLOCKED = 32 + MAP_UNLOCKED = 32, + MAP_JIT_DARWIN = 0x800 } [Flags] @@ -45,7 +46,6 @@ namespace Ryujinx.Memory private const int MAP_UNLOCKED_LINUX_GENERIC = 0x80000; private const int MAP_NORESERVE_DARWIN = 0x40; - private const int MAP_JIT_DARWIN = 0x800; private const int MAP_ANONYMOUS_DARWIN = 0x1000; public const int MADV_DONTNEED = 4; @@ -151,10 +151,9 @@ namespace Ryujinx.Memory } } - if (OperatingSystem.IsMacOSVersionAtLeast(10, 14)) + if (flags.HasFlag(MmapFlags.MAP_JIT_DARWIN) && OperatingSystem.IsMacOSVersionAtLeast(10, 14)) { - // Only to be used with the Hardened Runtime. - // result |= MAP_JIT_DARWIN; + result |= (int)MmapFlags.MAP_JIT_DARWIN; } return result; From 2355c2af62282ff36e56106495c09ed5cfcc3672 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Wed, 11 Jan 2023 01:29:22 +0100 Subject: [PATCH 267/737] ava: Generate Locale menu automatically (#4243) Currently in `MenuMainBarView.axaml` we list all available languages and hardcode the language name with the language key. It's a bit bad beause if we want to add a new language, we have to edit the `csproj` and the `axaml` with the translated language name and the language code. I've put all translations in their respective locale files, add code into `MainMenuBarView` constructor to generate the menu automatically. Now we just have to edit the `csproj` if we want to add a new language. --- Ryujinx.Ava/Assets/Locales/de_DE.json | 3 +- Ryujinx.Ava/Assets/Locales/el_GR.json | 3 +- Ryujinx.Ava/Assets/Locales/en_US.json | 4 +- Ryujinx.Ava/Assets/Locales/es_ES.json | 3 +- Ryujinx.Ava/Assets/Locales/fr_FR.json | 3 +- Ryujinx.Ava/Assets/Locales/it_IT.json | 3 +- Ryujinx.Ava/Assets/Locales/ja_JP.json | 3 +- Ryujinx.Ava/Assets/Locales/ko_KR.json | 3 +- Ryujinx.Ava/Assets/Locales/pl_PL.json | 3 +- Ryujinx.Ava/Assets/Locales/pt_BR.json | 3 +- Ryujinx.Ava/Assets/Locales/ru_RU.json | 3 +- Ryujinx.Ava/Assets/Locales/tr_TR.json | 3 +- Ryujinx.Ava/Assets/Locales/uk_UA.json | 3 +- Ryujinx.Ava/Assets/Locales/zh_CN.json | 3 +- Ryujinx.Ava/Assets/Locales/zh_TW.json | 3 +- Ryujinx.Ava/Common/Locale/LocaleManager.cs | 1 - Ryujinx.Ava/Ryujinx.Ava.csproj | 2 + .../UI/Views/Main/MainMenuBarView.axaml | 58 +------------------ .../UI/Views/Main/MainMenuBarView.axaml.cs | 49 ++++++++++++++-- Ryujinx.Common/Utilities/EmbeddedResources.cs | 8 +++ 20 files changed, 87 insertions(+), 77 deletions(-) diff --git a/Ryujinx.Ava/Assets/Locales/de_DE.json b/Ryujinx.Ava/Assets/Locales/de_DE.json index adda79caa..671f369e7 100644 --- a/Ryujinx.Ava/Assets/Locales/de_DE.json +++ b/Ryujinx.Ava/Assets/Locales/de_DE.json @@ -1,4 +1,5 @@ { + "Language": "Deutsch", "MenuBarFileOpenApplet": "Applet öffnen", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii Editor Applet im Standalone Modus", "SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Konto wiederherstellen", "Recover": "Wiederherstellen", "UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/el_GR.json b/Ryujinx.Ava/Assets/Locales/el_GR.json index aef474ac9..5cd7a5540 100644 --- a/Ryujinx.Ava/Assets/Locales/el_GR.json +++ b/Ryujinx.Ava/Assets/Locales/el_GR.json @@ -1,4 +1,5 @@ { + "Language": "Ελληνικά", "MenuBarFileOpenApplet": "Άνοιγμα Applet", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία", "SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading": "Saves were found for the following accounts" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index afd3f3932..46203463a 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -1,4 +1,5 @@ { + "Language": "English (US)", "MenuBarFileOpenApplet": "Open Applet", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode", "SettingsTabInputDirectMouseAccess": "Direct Mouse Access", @@ -610,4 +611,5 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading" : "Saves were found for the following accounts" -} \ No newline at end of file +} + diff --git a/Ryujinx.Ava/Assets/Locales/es_ES.json b/Ryujinx.Ava/Assets/Locales/es_ES.json index 8275cdd88..1922318d0 100644 --- a/Ryujinx.Ava/Assets/Locales/es_ES.json +++ b/Ryujinx.Ava/Assets/Locales/es_ES.json @@ -1,4 +1,5 @@ { + "Language": "Español (ES)", "MenuBarFileOpenApplet": "Abrir applet", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo", "SettingsTabInputDirectMouseAccess": "Acceso directo al ratón", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading": "Saves were found for the following accounts" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/fr_FR.json b/Ryujinx.Ava/Assets/Locales/fr_FR.json index 634a996c0..938d0cc77 100644 --- a/Ryujinx.Ava/Assets/Locales/fr_FR.json +++ b/Ryujinx.Ava/Assets/Locales/fr_FR.json @@ -1,4 +1,5 @@ { + "Language": "Français", "MenuBarFileOpenApplet": "Ouvrir Applet", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Ouvrir l'Applet Mii Editor en mode Standalone", "SettingsTabInputDirectMouseAccess": "Accès direct à la souris", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Récupérer les comptes perdus", "Recover": "Récupérer", "UserProfilesRecoverHeading": "Des sauvegardes ont été trouvées pour les comptes suivants" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/it_IT.json b/Ryujinx.Ava/Assets/Locales/it_IT.json index b53dbf370..fbdd4046d 100644 --- a/Ryujinx.Ava/Assets/Locales/it_IT.json +++ b/Ryujinx.Ava/Assets/Locales/it_IT.json @@ -1,4 +1,5 @@ { + "Language": "Italiano", "MenuBarFileOpenApplet": "Apri Applet", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Apri l'applet Mii Editor in modalità Standalone", "SettingsTabInputDirectMouseAccess": "Accesso diretto al mouse", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Recupera il tuo account", "Recover": "Recupera", "UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/ja_JP.json b/Ryujinx.Ava/Assets/Locales/ja_JP.json index c779056ba..c88477f96 100644 --- a/Ryujinx.Ava/Assets/Locales/ja_JP.json +++ b/Ryujinx.Ava/Assets/Locales/ja_JP.json @@ -1,4 +1,5 @@ { + "Language": "日本語", "MenuBarFileOpenApplet": "アプレットを開く", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "スタンドアロンモードで Mii エディタアプレットを開きます", "SettingsTabInputDirectMouseAccess": "マウス直接アクセス", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "アカウントの復旧", "Recover": "復旧", "UserProfilesRecoverHeading": "以下のアカウントのセーブデータが見つかりました" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/ko_KR.json b/Ryujinx.Ava/Assets/Locales/ko_KR.json index 0efafc322..5379efa9c 100644 --- a/Ryujinx.Ava/Assets/Locales/ko_KR.json +++ b/Ryujinx.Ava/Assets/Locales/ko_KR.json @@ -1,4 +1,5 @@ { + "Language": "한국어", "MenuBarFileOpenApplet": "애플릿 열기", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "독립 실행형 모드에서 Mii 편집기 애플릿 열기", "SettingsTabInputDirectMouseAccess": "직접 마우스 접속", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "잃어버린 계정 복구", "Recover": "복구", "UserProfilesRecoverHeading": "다음 계정에 대한 저장 발견" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/Ryujinx.Ava/Assets/Locales/pl_PL.json index 020f54b83..3c1b541ed 100644 --- a/Ryujinx.Ava/Assets/Locales/pl_PL.json +++ b/Ryujinx.Ava/Assets/Locales/pl_PL.json @@ -1,4 +1,5 @@ { + "Language": "Polski", "MenuBarFileOpenApplet": "Otwórz Aplet", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Otwórz aplet Mii Editor w trybie Indywidualnym", "SettingsTabInputDirectMouseAccess": "Bezpośredni Dostęp do Myszy", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Odzyskaj Utracone Konta", "Recover": "Odzyskaj", "UserProfilesRecoverHeading": "Znaleziono zapisy dla następujących kont" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json index 6a377b0d6..036b0a4bf 100644 --- a/Ryujinx.Ava/Assets/Locales/pt_BR.json +++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json @@ -1,4 +1,5 @@ { + "Language": "Português (BR)", "MenuBarFileOpenApplet": "Abrir Applet", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abrir editor Mii em modo avulso", "SettingsTabInputDirectMouseAccess": "Acesso direto ao mouse", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Recuperar contas perdidas", "Recover": "Recuperar", "UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/ru_RU.json b/Ryujinx.Ava/Assets/Locales/ru_RU.json index b5eadd6bb..b3ad82be7 100644 --- a/Ryujinx.Ava/Assets/Locales/ru_RU.json +++ b/Ryujinx.Ava/Assets/Locales/ru_RU.json @@ -1,4 +1,5 @@ { + "Language": "Русский", "MenuBarFileOpenApplet": "Открыть апплет", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Открыть апплет Mii Editor в автономном режиме.", "SettingsTabInputDirectMouseAccess": "Прямой доступ с помощью мыши", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", "UserProfilesRecoverHeading": "Saves were found for the following accounts" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/tr_TR.json b/Ryujinx.Ava/Assets/Locales/tr_TR.json index 623ad34ae..ae14cdaf3 100644 --- a/Ryujinx.Ava/Assets/Locales/tr_TR.json +++ b/Ryujinx.Ava/Assets/Locales/tr_TR.json @@ -1,4 +1,5 @@ { + "Language": "Türkçe", "MenuBarFileOpenApplet": "Applet'i Aç", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Mii Editör Applet'ini Bağımsız Mod'da Aç", "SettingsTabInputDirectMouseAccess": "Doğrudan Mouse Erişimi", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Kayıp Hesapları Kurtar", "Recover": "Kurtar", "UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/uk_UA.json b/Ryujinx.Ava/Assets/Locales/uk_UA.json index e28d47c28..01d433da6 100644 --- a/Ryujinx.Ava/Assets/Locales/uk_UA.json +++ b/Ryujinx.Ava/Assets/Locales/uk_UA.json @@ -1,4 +1,5 @@ { + "Language": "Yкраїнська", "MenuBarFileOpenApplet": "Відкрити аплет", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Відкрийте аплет Mii Editor в автономному режимі", "SettingsTabInputDirectMouseAccess": "Прямий доступ мишею", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "Відновлення втрачених облікових записів", "Recover": "Відновити", "UserProfilesRecoverHeading": "Знайдено збереження для наступних облікових записів" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/zh_CN.json b/Ryujinx.Ava/Assets/Locales/zh_CN.json index 1fcfe8dab..cd76951a7 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_CN.json +++ b/Ryujinx.Ava/Assets/Locales/zh_CN.json @@ -1,4 +1,5 @@ { + "Language": "简体中文", "MenuBarFileOpenApplet": "打开小程序", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "打开独立的 Mii 小程序", "SettingsTabInputDirectMouseAccess": "直通鼠标操作", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "恢复丢失的账户", "Recover": "恢复", "UserProfilesRecoverHeading": "找到了这些用户的存档数据" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Assets/Locales/zh_TW.json b/Ryujinx.Ava/Assets/Locales/zh_TW.json index fcc16d78a..963c0a834 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -1,4 +1,5 @@ { + "Language": "繁體中文", "MenuBarFileOpenApplet": "打開小程式", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "打開獨立的 Mii 小程式", "SettingsTabInputDirectMouseAccess": "直通滑鼠操作", @@ -610,4 +611,4 @@ "UserProfilesRecoverLostAccounts": "恢復遺失的帳號", "Recover": "恢復", "UserProfilesRecoverHeading": "在以下帳號找到了一些遊戲存檔" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index c2251f851..5bcaa437e 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; -using System.Text.Json; namespace Ryujinx.Ava.Common.Locale { diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 5c001c672..996817b9d 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -145,6 +145,7 @@ + @@ -165,6 +166,7 @@ + diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml index 0d0ae1193..bd26561a7 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml @@ -77,63 +77,7 @@ - - - - - - - - - - - - - - - + menuItems = new(); + + string localePath = "Ryujinx.Ava/Assets/Locales"; + string localeExt = ".json"; + + string[] localesPath = EmbeddedResources.GetAllAvailableResources(localePath, localeExt); + + Array.Sort(localesPath); + + foreach (string locale in localesPath) + { + string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last(); + string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}"); + var strings = JsonHelper.Deserialize>(languageJson); + + if (!strings.TryGetValue("Language", out string languageName)) + { + languageName = languageCode; + } + + MenuItem menuItem = new() + { + Header = languageName, + Command = MiniCommand.Create(() => + { + ViewModel.ChangeLanguage(languageCode); + }) + }; + + menuItems.Add(menuItem); + } + + ChangeLanguageMenuItem.Items = menuItems.ToArray(); } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - if (this.VisualRoot is MainWindow window) + if (VisualRoot is MainWindow window) { Window = window; } diff --git a/Ryujinx.Common/Utilities/EmbeddedResources.cs b/Ryujinx.Common/Utilities/EmbeddedResources.cs index 286d2c5ce..e7c8d7d70 100644 --- a/Ryujinx.Common/Utilities/EmbeddedResources.cs +++ b/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -127,6 +128,13 @@ namespace Ryujinx.Common return stream; } + public static string[] GetAllAvailableResources(string path, string ext = "") + { + return ResolveManifestPath(path).Item1.GetManifestResourceNames() + .Where(r => r.EndsWith(ext)) + .ToArray(); + } + private static (Assembly, string) ResolveManifestPath(string filename) { var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries); From 94a64f2aea3225d83a2aa1e61ed8d4bf8be49e5c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 10 Jan 2023 22:53:56 -0300 Subject: [PATCH 268/737] Remove textures from cache on unmap if not mapped and modified (#4211) --- Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs | 26 +++++++++++++++++++ Ryujinx.Graphics.Gpu/Image/Texture.cs | 7 +++++ Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 13 ++++++++++ Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 26 +++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index 277f39b98..4a1615f04 100644 --- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image @@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image private const int MaxCapacity = 2048; private readonly LinkedList _textures; + private readonly ConcurrentQueue _deferredRemovals; /// /// Creates a new instance of the automatic deletion cache. @@ -20,6 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Image public AutoDeleteCache() { _textures = new LinkedList(); + _deferredRemovals = new ConcurrentQueue(); } /// @@ -56,6 +59,14 @@ namespace Ryujinx.Graphics.Gpu.Image oldestTexture.CacheNode = null; } + + if (_deferredRemovals.Count > 0) + { + while (_deferredRemovals.TryDequeue(out Texture textureToRemove)) + { + Remove(textureToRemove, false); + } + } } /// @@ -84,6 +95,12 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Removes a texture from the cache. + /// + /// The texture to be removed from the cache + /// True to remove the texture if it was on the cache + /// True if the texture was found and removed, false otherwise public bool Remove(Texture texture, bool flush) { if (texture.CacheNode == null) @@ -104,6 +121,15 @@ namespace Ryujinx.Graphics.Gpu.Image return texture.DecrementReferenceCount(); } + /// + /// Queues removal of a texture from the cache in a thread safe way. + /// + /// The texture to be removed from the cache + public void RemoveDeferred(Texture texture) + { + _deferredRemovals.Enqueue(texture); + } + public IEnumerator GetEnumerator() { return _textures.GetEnumerator(); diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 0995314d0..f0c31be6f 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1676,6 +1676,13 @@ namespace Ryujinx.Graphics.Gpu.Image } RemoveFromPools(true); + + // We only want to remove if there's no mapped region of the texture that was modified by the GPU, + // otherwise we could lose data. + if (!Group.AnyModified(this)) + { + _physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this); + } } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 16bfc6931..c020f4c82 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -1165,6 +1165,19 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Queues the removal of a texture from the auto delete cache. + /// + /// + /// This function is thread safe and can be called from any thread. + /// The texture will be deleted on the next time the cache is used. + /// + /// The texture to be removed + public void QueueAutoDeleteCacheRemoval(Texture texture) + { + _cache.RemoveDeferred(texture); + } + /// /// Disposes all textures and samplers in the cache. /// It's an error to use the texture cache after disposal. diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index ca54dc2f2..cd17564a0 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -434,6 +434,32 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Checks if a texture was modified by the GPU. + /// + /// The texture to be checked + /// True if any region of the texture was modified by the GPU, false otherwise + public bool AnyModified(Texture texture) + { + bool anyModified = false; + + EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => + { + for (int i = 0; i < regionCount; i++) + { + TextureGroupHandle group = _handles[baseHandle + i]; + + if (group.Modified) + { + anyModified = true; + break; + } + } + }); + + return anyModified; + } + /// /// Flush modified ranges for a given texture. /// From cee667b491f87c48546f348bad8c6f16cdf6d628 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Wed, 11 Jan 2023 05:39:25 +0100 Subject: [PATCH 269/737] Ava: Fixes Update count in heading (#4265) --- Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 29dc5351b..848c5587f 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Ava.UI.Windows private void PrintHeading() { - Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], _titleUpdates.Count, _titleName, _titleId.ToString("X16")); + Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], _titleUpdates.Count - 1, _titleName, _titleId.ToString("X16")); } private void LoadUpdates() From 934b5a64e5638ae5228acb52faf48efadefdea8d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Wed, 11 Jan 2023 00:20:19 -0500 Subject: [PATCH 270/737] Ava GUI: User Profile Manager + Other Fixes (#4166) * Fix redundancies * Add back elses * Loading Screen fixes * Redesign User Profile Manager - Backported long selection bar in Grid/List view not working - Backported UserSelector is jank * Fix SelectionIndicator * Fix DataType * Fix SaveManager bug * Remove debug log * Load saves on UIThread * Reduce UI thread blocking * Fix locale keys * Use block namespaces * Fix close button width * Make UserProfile ordering consistent * Alphabetical order * Adjust layout, remove green circle for blue selector * Fix some inconsistencies * Fix no inital selected profile * Adjust appearance of edit button * Adjust SaveManager * Remove redundant warning dialog * Make firmware avatar selector clearer * View redesign again :hero_depressed: * Consistency adjustments * Adjust margins * Make `UserProfileImageSelector` consistent * Make `UserFirmwareAvatarSelector` consistent * Fix long grid view selector * Switch case * Remove long selection bar Handled in #4178 * Consistency * Started dialog titles * Fixes * Remaining titles * Update Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml Co-authored-by: Mary-nyan * Fix build * Hide UserRecoverer if no LostProfiles are found * UserEditor Avatar Placeholder * Watermark + locale adjustment * Border radius * Remove unnecessary styles * Fix firmware avatar image order * Cleanup `ColorPickerButton` * Make `UserId` copy/paste able * Make `FirmwareAvatarSelector` 6 images wide * Make selection bar better * Unsaved changes dialogue * Fix indentation * Remove extra check * Address suggestions * Reorganise - Remove unused views - Rename views to match convention - Fix weird namespacing * Update Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml Co-authored-by: Ac_K * UserRecovererView empty placeholder * Update Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Models/UserProfile.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs Co-authored-by: Ac_K * Remove AddModel * Update Ryujinx.Ava/Assets/Locales/en_US.json Co-authored-by: Ac_K * Fix bug Co-authored-by: Mary-nyan Co-authored-by: Ac_K --- Ryujinx.Ava/Assets/Locales/de_DE.json | 2 +- Ryujinx.Ava/Assets/Locales/el_GR.json | 2 +- Ryujinx.Ava/Assets/Locales/en_US.json | 18 +- Ryujinx.Ava/Assets/Locales/es_ES.json | 2 +- Ryujinx.Ava/Assets/Locales/fr_FR.json | 2 +- Ryujinx.Ava/Assets/Locales/ja_JP.json | 2 +- Ryujinx.Ava/Assets/Locales/pl_PL.json | 2 +- Ryujinx.Ava/Assets/Locales/pt_BR.json | 2 +- Ryujinx.Ava/Assets/Locales/ru_RU.json | 2 +- Ryujinx.Ava/Assets/Locales/tr_TR.json | 2 +- Ryujinx.Ava/Assets/Locales/zh_TW.json | 2 +- Ryujinx.Ava/Assets/Styles/Styles.xaml | 32 +++ Ryujinx.Ava/Program.cs | 2 +- Ryujinx.Ava/Ryujinx.Ava.csproj | 12 + Ryujinx.Ava/UI/Controls/GameGridView.axaml | 24 -- Ryujinx.Ava/UI/Controls/GameListView.axaml | 29 --- .../UI/Controls/NavigationDialogHost.axaml | 3 +- .../UI/Controls/NavigationDialogHost.axaml.cs | 139 ++++++++++- .../ProfileImageSelectionDialog.axaml | 57 ----- Ryujinx.Ava/UI/Controls/SaveManager.axaml | 175 ------------- Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs | 160 ------------ Ryujinx.Ava/UI/Controls/UserRecoverer.axaml | 72 ------ .../UI/Controls/UserRecoverer.axaml.cs | 44 ---- Ryujinx.Ava/UI/Controls/UserSelector.axaml | 145 ----------- Ryujinx.Ava/UI/Controls/UserSelector.axaml.cs | 77 ------ Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs | 5 +- .../{Helper => UI/Helpers}/LoggerAdapter.cs | 2 +- .../{Helper => UI/Helpers}/MetalHelper.cs | 2 +- Ryujinx.Ava/UI/Models/ProfileImageModel.cs | 20 +- Ryujinx.Ava/UI/Models/SaveModel.cs | 24 -- Ryujinx.Ava/UI/Models/TempProfile.cs | 9 +- Ryujinx.Ava/UI/Models/UserProfile.cs | 42 +++- .../UserFirmwareAvatarSelectorViewModel.cs | 230 ++++++++++++++++++ .../UserProfileImageSelectorViewModel.cs | 18 ++ .../UI/ViewModels/UserProfileViewModel.cs | 200 +-------------- .../UI/ViewModels/UserSaveManagerViewModel.cs | 123 ++++++++++ .../User/UserEditorView.axaml} | 109 ++++++--- .../User/UserEditorView.axaml.cs} | 81 ++++-- .../User/UserFirmwareAvatarSelectorView.axaml | 114 +++++++++ .../UserFirmwareAvatarSelectorView.axaml.cs} | 35 ++- .../User/UserProfileImageSelectorView.axaml | 63 +++++ .../UserProfileImageSelectorView.axaml.cs} | 43 +++- .../UI/Views/User/UserRecovererView.axaml | 83 +++++++ .../UI/Views/User/UserRecovererView.axaml.cs | 51 ++++ .../UI/Views/User/UserSaveManagerView.axaml | 199 +++++++++++++++ .../Views/User/UserSaveManagerView.axaml.cs | 148 +++++++++++ .../UI/Views/User/UserSelectorView.axaml | 165 +++++++++++++ .../UI/Views/User/UserSelectorView.axaml.cs | 128 ++++++++++ Ryujinx.Ava/UI/Windows/AvatarWindow.axaml | 54 ---- 49 files changed, 1787 insertions(+), 1170 deletions(-) delete mode 100644 Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml delete mode 100644 Ryujinx.Ava/UI/Controls/SaveManager.axaml delete mode 100644 Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs delete mode 100644 Ryujinx.Ava/UI/Controls/UserRecoverer.axaml delete mode 100644 Ryujinx.Ava/UI/Controls/UserRecoverer.axaml.cs delete mode 100644 Ryujinx.Ava/UI/Controls/UserSelector.axaml delete mode 100644 Ryujinx.Ava/UI/Controls/UserSelector.axaml.cs rename Ryujinx.Ava/{Helper => UI/Helpers}/LoggerAdapter.cs (99%) rename Ryujinx.Ava/{Helper => UI/Helpers}/MetalHelper.cs (99%) create mode 100644 Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs create mode 100644 Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs create mode 100644 Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs rename Ryujinx.Ava/UI/{Controls/UserEditor.axaml => Views/User/UserEditorView.axaml} (52%) rename Ryujinx.Ava/UI/{Controls/UserEditor.axaml.cs => Views/User/UserEditorView.axaml.cs} (52%) create mode 100644 Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml rename Ryujinx.Ava/UI/{Windows/AvatarWindow.axaml.cs => Views/User/UserFirmwareAvatarSelectorView.axaml.cs} (55%) create mode 100644 Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml rename Ryujinx.Ava/UI/{Controls/ProfileImageSelectionDialog.axaml.cs => Views/User/UserProfileImageSelectorView.axaml.cs} (69%) create mode 100644 Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml create mode 100644 Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml create mode 100644 Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs create mode 100644 Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml create mode 100644 Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs delete mode 100644 Ryujinx.Ava/UI/Windows/AvatarWindow.axaml diff --git a/Ryujinx.Ava/Assets/Locales/de_DE.json b/Ryujinx.Ava/Assets/Locales/de_DE.json index 671f369e7..4d656bc99 100644 --- a/Ryujinx.Ava/Assets/Locales/de_DE.json +++ b/Ryujinx.Ava/Assets/Locales/de_DE.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Profilbild ändern", "UserProfilesAvailableUserProfiles": "Verfügbare Profile:", "UserProfilesAddNewProfile": "Neues Profil", - "UserProfilesDeleteSelectedProfile": "Profil löschen", + "UserProfilesDelete": "Löschen", "UserProfilesClose": "Schließen", "ProfileImageSelectionTitle": "Auswahl des Profilbildes", "ProfileImageSelectionHeader": "Wähle ein Profilbild aus", diff --git a/Ryujinx.Ava/Assets/Locales/el_GR.json b/Ryujinx.Ava/Assets/Locales/el_GR.json index 5cd7a5540..ca3be8b9a 100644 --- a/Ryujinx.Ava/Assets/Locales/el_GR.json +++ b/Ryujinx.Ava/Assets/Locales/el_GR.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Αλλαγή Εικόνας Προφίλ", "UserProfilesAvailableUserProfiles": "Διαθέσιμα Προφίλ Χρηστών:", "UserProfilesAddNewProfile": "Προσθήκη Νέου Προφίλ", - "UserProfilesDeleteSelectedProfile": "Διαγραφή Επιλεγμένου Προφίλ", + "UserProfilesDelete": "Διαγράφω", "UserProfilesClose": "Κλείσιμο", "ProfileImageSelectionTitle": "Επιλογή Εικόνας Προφίλ", "ProfileImageSelectionHeader": "Επιλέξτε μία Εικόνα Προφίλ", diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 46203463a..0c767871b 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -260,8 +260,9 @@ "UserProfilesChangeProfileImage": "Change Profile Image", "UserProfilesAvailableUserProfiles": "Available User Profiles:", "UserProfilesAddNewProfile": "Create Profile", - "UserProfilesDeleteSelectedProfile": "Delete Selected", + "UserProfilesDelete": "Delete", "UserProfilesClose": "Close", + "ProfileNameSelectionWatermark": "Choose a nickname", "ProfileImageSelectionTitle": "Profile Image Selection", "ProfileImageSelectionHeader": "Choose a profile Image", "ProfileImageSelectionNote": "You may import a custom profile image, or select an avatar from system firmware", @@ -273,7 +274,7 @@ "InputDialogAddNewProfileTitle": "Choose the Profile Name", "InputDialogAddNewProfileHeader": "Please Enter a Profile Name", "InputDialogAddNewProfileSubtext": "(Max Length: {0})", - "AvatarChoose": "Choose", + "AvatarChoose": "Choose Avatar", "AvatarSetBackgroundColor": "Set Background Color", "AvatarClose": "Close", "ControllerSettingsLoadProfileToolTip": "Load Profile", @@ -368,6 +369,9 @@ "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "System version {0} successfully installed.", "DialogUserProfileDeletionWarningMessage": "There would be no other profiles to be opened if selected profile is deleted", "DialogUserProfileDeletionConfirmMessage": "Do you want to delete the selected profile", + "DialogUserProfileUnsavedChangesTitle": "Warning - Unsaved Changes", + "DialogUserProfileUnsavedChangesMessage": "You have made changes to this user profile that have not been saved.", + "DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?", "DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.", "DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?", "DialogDlcLoadNcaErrorMessage": "{0}. Errored File: {1}", @@ -584,7 +588,7 @@ "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:", "UserProfilesName": "Name:", - "UserProfilesUserId": "User Id:", + "UserProfilesUserId": "User ID:", "SettingsTabGraphicsBackend": "Graphics Backend", "SettingsTabGraphicsBackendTooltip": "Graphics Backend to use", "SettingsEnableTextureRecompression": "Enable Texture Recompression", @@ -603,13 +607,15 @@ "UserProfilesManageSaves": "Manage Saves", "DeleteUserSave": "Do you want to delete user save for this game?", "IrreversibleActionNote": "This action is not reversible.", - "SaveManagerHeading": "Manage Saves for {0}", + "SaveManagerHeading": "Manage Saves for {0} ({1})", "SaveManagerTitle": "Save Manager", "Name": "Name", "Size": "Size", "Search": "Search", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", - "UserProfilesRecoverHeading" : "Saves were found for the following accounts" + "UserProfilesRecoverHeading" : "Saves were found for the following accounts", + "UserProfilesRecoverEmptyList": "No profiles to recover", + "UserEditorTitle" : "Edit User", + "UserEditorTitleCreate" : "Create User" } - diff --git a/Ryujinx.Ava/Assets/Locales/es_ES.json b/Ryujinx.Ava/Assets/Locales/es_ES.json index 1922318d0..660d62a1e 100644 --- a/Ryujinx.Ava/Assets/Locales/es_ES.json +++ b/Ryujinx.Ava/Assets/Locales/es_ES.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Cambiar imagen de perfil", "UserProfilesAvailableUserProfiles": "Perfiles de usuario disponibles:", "UserProfilesAddNewProfile": "Añadir nuevo perfil", - "UserProfilesDeleteSelectedProfile": "Eliminar perfil seleccionado", + "UserProfilesDelete": "Eliminar", "UserProfilesClose": "Cerrar", "ProfileImageSelectionTitle": "Selección de imagen de perfil", "ProfileImageSelectionHeader": "Elige una imagen de perfil", diff --git a/Ryujinx.Ava/Assets/Locales/fr_FR.json b/Ryujinx.Ava/Assets/Locales/fr_FR.json index 938d0cc77..71f32c6ee 100644 --- a/Ryujinx.Ava/Assets/Locales/fr_FR.json +++ b/Ryujinx.Ava/Assets/Locales/fr_FR.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Changer l'image du profil", "UserProfilesAvailableUserProfiles": "Profils utilisateurs disponible:", "UserProfilesAddNewProfile": "Ajouter un nouveau profil", - "UserProfilesDeleteSelectedProfile": "Supprimer le profil sélectionné", + "UserProfilesDelete": "Supprimer", "UserProfilesClose": "Fermer", "ProfileImageSelectionTitle": "Sélection de l'image du profil", "ProfileImageSelectionHeader": "Choisir l'image du profil", diff --git a/Ryujinx.Ava/Assets/Locales/ja_JP.json b/Ryujinx.Ava/Assets/Locales/ja_JP.json index c88477f96..b1e0a43bf 100644 --- a/Ryujinx.Ava/Assets/Locales/ja_JP.json +++ b/Ryujinx.Ava/Assets/Locales/ja_JP.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "プロファイル画像を変更", "UserProfilesAvailableUserProfiles": "利用可能なユーザプロファイル:", "UserProfilesAddNewProfile": "プロファイルを作成", - "UserProfilesDeleteSelectedProfile": "削除", + "UserProfilesDelete": "削除", "UserProfilesClose": "閉じる", "ProfileImageSelectionTitle": "プロファイル画像選択", "ProfileImageSelectionHeader": "プロファイル画像を選択", diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/Ryujinx.Ava/Assets/Locales/pl_PL.json index 3c1b541ed..0cc0b4f91 100644 --- a/Ryujinx.Ava/Assets/Locales/pl_PL.json +++ b/Ryujinx.Ava/Assets/Locales/pl_PL.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Zmień Obraz Profilu", "UserProfilesAvailableUserProfiles": "Dostępne Profile Użytkowników:", "UserProfilesAddNewProfile": "Utwórz Profil", - "UserProfilesDeleteSelectedProfile": "Usuń Zaznaczone", + "UserProfilesDelete": "Usuwać", "UserProfilesClose": "Zamknij", "ProfileImageSelectionTitle": "Wybór Obrazu Profilu", "ProfileImageSelectionHeader": "Wybierz zdjęcie profilowe", diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json index 036b0a4bf..ded6cf95f 100644 --- a/Ryujinx.Ava/Assets/Locales/pt_BR.json +++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Mudar imagem de perfil", "UserProfilesAvailableUserProfiles": "Perfis de usuário disponíveis:", "UserProfilesAddNewProfile": "Adicionar novo perfil", - "UserProfilesDeleteSelectedProfile": "Apagar perfil selecionado", + "UserProfilesDelete": "Apagar", "UserProfilesClose": "Fechar", "ProfileImageSelectionTitle": "Seleção da imagem de perfil", "ProfileImageSelectionHeader": "Escolha uma imagem de perfil", diff --git a/Ryujinx.Ava/Assets/Locales/ru_RU.json b/Ryujinx.Ava/Assets/Locales/ru_RU.json index b3ad82be7..7b25f4554 100644 --- a/Ryujinx.Ava/Assets/Locales/ru_RU.json +++ b/Ryujinx.Ava/Assets/Locales/ru_RU.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Изменить изображение профиля", "UserProfilesAvailableUserProfiles": "Доступные профили пользователей:", "UserProfilesAddNewProfile": "Добавить новый профиль", - "UserProfilesDeleteSelectedProfile": "Удалить выбранный профиль", + "UserProfilesDelete": "Удалить", "UserProfilesClose": "Закрыть", "ProfileImageSelectionTitle": "Выбор изображения профиля", "ProfileImageSelectionHeader": "Выберите изображение профиля", diff --git a/Ryujinx.Ava/Assets/Locales/tr_TR.json b/Ryujinx.Ava/Assets/Locales/tr_TR.json index ae14cdaf3..f277713ba 100644 --- a/Ryujinx.Ava/Assets/Locales/tr_TR.json +++ b/Ryujinx.Ava/Assets/Locales/tr_TR.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "Profil Resmini Değiştir", "UserProfilesAvailableUserProfiles": "Mevcut Kullanıcı Profilleri:", "UserProfilesAddNewProfile": "Yeni Profil Ekle", - "UserProfilesDeleteSelectedProfile": "Seçili Profili Sil", + "UserProfilesDelete": "Sil", "UserProfilesClose": "Kapat", "ProfileImageSelectionTitle": "Profil Resmi Seçimi", "ProfileImageSelectionHeader": "Profil Resmi Seç", diff --git a/Ryujinx.Ava/Assets/Locales/zh_TW.json b/Ryujinx.Ava/Assets/Locales/zh_TW.json index 963c0a834..e68329957 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -260,7 +260,7 @@ "UserProfilesChangeProfileImage": "更換頭貼", "UserProfilesAvailableUserProfiles": "現有的帳號:", "UserProfilesAddNewProfile": "建立帳號", - "UserProfilesDeleteSelectedProfile": "刪除選擇的帳號", + "UserProfilesDelete": "刪除", "UserProfilesClose": "關閉", "ProfileImageSelectionTitle": "頭貼選擇", "ProfileImageSelectionHeader": "選擇合適的頭貼圖片", diff --git a/Ryujinx.Ava/Assets/Styles/Styles.xaml b/Ryujinx.Ava/Assets/Styles/Styles.xaml index c5e760e81..fc4e9ddd6 100644 --- a/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -179,6 +179,9 @@ + @@ -234,6 +237,35 @@ + + + diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 010aff514..46e135a9b 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -1,6 +1,6 @@ using Avalonia; using Avalonia.Threading; -using Ryujinx.Ava.UI.Helper; +using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 996817b9d..88b60d0ba 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -130,6 +130,18 @@ GameListView.axaml Code + + UserEditor.axaml + Code + + + UserRecoverer.axaml + Code + + + UserSelector.axaml + Code + diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml b/Ryujinx.Ava/UI/Controls/GameGridView.axaml index c757f066c..862bc6d30 100644 --- a/Ryujinx.Ava/UI/Controls/GameGridView.axaml +++ b/Ryujinx.Ava/UI/Controls/GameGridView.axaml @@ -112,32 +112,8 @@ - - - - diff --git a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml index 90720478d..bf34b303a 100644 --- a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml +++ b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml @@ -12,5 +12,6 @@ + x:Name="ContentFrame"> + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs index 0c3002675..6911a4d4c 100644 --- a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs @@ -1,13 +1,25 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Styling; +using Avalonia.Threading; +using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using LibHac; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Shim; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Views.User; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Account.Acc; using System; using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.Controls { @@ -31,14 +43,14 @@ namespace Ryujinx.Ava.UI.Controls ContentManager = contentManager; VirtualFileSystem = virtualFileSystem; HorizonClient = horizonClient; - ViewModel = new UserProfileViewModel(this); - + ViewModel = new UserProfileViewModel(); + LoadProfiles(); if (contentManager.GetCurrentFirmwareVersion() != null) { Task.Run(() => { - AvatarProfileViewModel.PreloadAvatars(contentManager, virtualFileSystem); + UserFirmwareAvatarSelectorViewModel.PreloadAvatars(contentManager, virtualFileSystem); }); } InitializeComponent(); @@ -51,7 +63,7 @@ namespace Ryujinx.Ava.UI.Controls ContentFrame.GoBack(); } - ViewModel.LoadProfiles(); + LoadProfiles(); } public void Navigate(Type sourcePageType, object parameter) @@ -68,7 +80,7 @@ namespace Ryujinx.Ava.UI.Controls Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle], PrimaryButtonText = "", SecondaryButtonText = "", - CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], + CloseButtonText = "", Content = content, Padding = new Thickness(0) }; @@ -78,6 +90,11 @@ namespace Ryujinx.Ava.UI.Controls content.ViewModel.Dispose(); }; + Style footer = new(x => x.Name("DialogSpace").Child().OfType()); + footer.Setters.Add(new Setter(IsVisibleProperty, false)); + + contentDialog.Styles.Add(footer); + await contentDialog.ShowAsync(); } @@ -85,7 +102,117 @@ namespace Ryujinx.Ava.UI.Controls { base.OnAttachedToVisualTree(e); - Navigate(typeof(UserSelector), this); + Navigate(typeof(UserSelectorViews), this); + } + + public void LoadProfiles() + { + ViewModel.Profiles.Clear(); + ViewModel.LostProfiles.Clear(); + + var profiles = AccountManager.GetAllUsers().OrderBy(x => x.Name); + + foreach (var profile in profiles) + { + ViewModel.Profiles.Add(new UserProfile(profile, this)); + } + + var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account, default, saveDataId: default, index: default); + + using var saveDataIterator = new UniqueRef(); + + HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); + + Span saveDataInfo = stackalloc SaveDataInfo[10]; + + HashSet lostAccounts = new(); + + while (true) + { + saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure(); + + if (readCount == 0) + { + break; + } + + for (int i = 0; i < readCount; i++) + { + var save = saveDataInfo[i]; + var id = new HLE.HOS.Services.Account.Acc.UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High); + if (ViewModel.Profiles.Cast().FirstOrDefault( x=> x.UserId == id) == null) + { + lostAccounts.Add(id); + } + } + } + + foreach(var account in lostAccounts) + { + ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this)); + } + + ViewModel.Profiles.Add(new BaseModel()); + } + + public async void DeleteUser(UserProfile userProfile) + { + var lastUserId = AccountManager.LastOpenedUser.UserId; + + if (userProfile.UserId == lastUserId) + { + // If we are deleting the currently open profile, then we must open something else before deleting. + var profile = ViewModel.Profiles.Cast().FirstOrDefault(x => x.UserId != lastUserId); + + if (profile == null) + { + async void Action() + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]); + } + + Dispatcher.UIThread.Post(Action); + + return; + } + + AccountManager.OpenUser(profile.UserId); + } + + var result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionConfirmMessage], + "", + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + ""); + + if (result == UserResult.Yes) + { + GoBack(); + AccountManager.DeleteUser(userProfile.UserId); + } + + LoadProfiles(); + } + + public void AddUser() + { + Navigate(typeof(UserEditorView), (this, (UserProfile)null, true)); + } + + public void EditUser(UserProfile userProfile) + { + Navigate(typeof(UserEditorView), (this, userProfile, false)); + } + + public void RecoverLostAccounts() + { + Navigate(typeof(UserRecovererView), this); + } + + public void ManageSaves() + { + Navigate(typeof(UserSaveManagerView), (this, AccountManager, HorizonClient, VirtualFileSystem)); } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml b/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml deleted file mode 100644 index 56f8152ae..000000000 --- a/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/SaveManager.axaml b/Ryujinx.Ava/UI/Controls/SaveManager.axaml deleted file mode 100644 index 64674b65b..000000000 --- a/Ryujinx.Ava/UI/Controls/SaveManager.axaml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs b/Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs deleted file mode 100644 index 9910481c5..000000000 --- a/Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Avalonia.Controls; -using DynamicData; -using DynamicData.Binding; -using LibHac; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Shim; -using Ryujinx.Ava.Common; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; -using Ryujinx.HLE.FileSystem; -using Ryujinx.Ui.App.Common; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Threading.Tasks; -using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; - -namespace Ryujinx.Ava.UI.Controls -{ - public partial class SaveManager : UserControl - { - private readonly UserProfile _userProfile; - private readonly HorizonClient _horizonClient; - private readonly VirtualFileSystem _virtualFileSystem; - private int _sortIndex; - private int _orderIndex; - private ObservableCollection _view = new ObservableCollection(); - private string _search; - - public ObservableCollection Saves { get; set; } = new ObservableCollection(); - - public ObservableCollection View - { - get => _view; - set => _view = value; - } - - public int SortIndex - { - get => _sortIndex; - set - { - _sortIndex = value; - Sort(); - } - } - - public int OrderIndex - { - get => _orderIndex; - set - { - _orderIndex = value; - Sort(); - } - } - - public string Search - { - get => _search; - set - { - _search = value; - Sort(); - } - } - - public SaveManager() - { - InitializeComponent(); - } - - public SaveManager(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem) - { - _userProfile = userProfile; - _horizonClient = horizonClient; - _virtualFileSystem = virtualFileSystem; - InitializeComponent(); - - DataContext = this; - - Task.Run(LoadSaves); - } - - public void LoadSaves() - { - Saves.Clear(); - var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account, - new UserId((ulong)_userProfile.UserId.High, (ulong)_userProfile.UserId.Low), saveDataId: default, index: default); - - using var saveDataIterator = new UniqueRef(); - - _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); - - Span saveDataInfo = stackalloc SaveDataInfo[10]; - - while (true) - { - saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure(); - - if (readCount == 0) - { - break; - } - - for (int i = 0; i < readCount; i++) - { - var save = saveDataInfo[i]; - if (save.ProgramId.Value != 0) - { - var saveModel = new SaveModel(save, _horizonClient, _virtualFileSystem); - Saves.Add(saveModel); - saveModel.DeleteAction = () => { Saves.Remove(saveModel); }; - } - - Sort(); - } - } - } - - private void Sort() - { - Saves.AsObservableChangeSet() - .Filter(Filter) - .Sort(GetComparer()) - .Bind(out var view).AsObservableList(); - - _view.Clear(); - _view.AddRange(view); - } - - private IComparer GetComparer() - { - switch (SortIndex) - { - case 0: - return OrderIndex == 0 - ? SortExpressionComparer.Ascending(save => save.Title) - : SortExpressionComparer.Descending(save => save.Title); - case 1: - return OrderIndex == 0 - ? SortExpressionComparer.Ascending(save => save.Size) - : SortExpressionComparer.Descending(save => save.Size); - default: - return null; - } - } - - private bool Filter(object arg) - { - if (arg is SaveModel save) - { - return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower()); - } - - return false; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/UserRecoverer.axaml b/Ryujinx.Ava/UI/Controls/UserRecoverer.axaml deleted file mode 100644 index 69f3d36a2..000000000 --- a/Ryujinx.Ava/UI/Controls/UserRecoverer.axaml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs b/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs similarity index 69% rename from Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs rename to Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs index 46a2f5079..18f76f805 100644 --- a/Ryujinx.Ava/UI/Controls/ProfileImageSelectionDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs @@ -4,25 +4,26 @@ using Avalonia.VisualTree; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.Windows; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.HLE.FileSystem; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; using System.IO; using Image = SixLabors.ImageSharp.Image; -namespace Ryujinx.Ava.UI.Controls +namespace Ryujinx.Ava.UI.Views.User { - public partial class ProfileImageSelectionDialog : UserControl + public partial class UserProfileImageSelectorView : UserControl { private ContentManager _contentManager; private NavigationDialogHost _parent; private TempProfile _profile; - public bool FirmwareFound => _contentManager.GetCurrentFirmwareVersion() != null; + internal UserProfileImageSelectorViewModel ViewModel { get; private set; } - public ProfileImageSelectionDialog() + public UserProfileImageSelectorView() { InitializeComponent(); AddHandler(Frame.NavigatedToEvent, (s, e) => @@ -40,13 +41,23 @@ namespace Ryujinx.Ava.UI.Controls case NavigationMode.New: (_parent, _profile) = ((NavigationDialogHost, TempProfile))arg.Parameter; _contentManager = _parent.ContentManager; + + ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - {LocaleManager.Instance[LocaleKeys.ProfileImageSelectionHeader]}"; + + if (Program.PreviewerDetached) + { + DataContext = ViewModel = new UserProfileImageSelectorViewModel(); + ViewModel.FirmwareFound = _contentManager.GetCurrentFirmwareVersion() != null; + } + break; case NavigationMode.Back: - _parent.GoBack(); + if (_profile.Image != null) + { + _parent.GoBack(); + } break; } - - DataContext = this; } } @@ -73,17 +84,25 @@ namespace Ryujinx.Ava.UI.Controls string imageFile = image[0]; _profile.Image = ProcessProfileImage(File.ReadAllBytes(imageFile)); - } - _parent.GoBack(); + if (_profile.Image != null) + { + _parent.GoBack(); + } + } } } + private void GoBack(object sender, RoutedEventArgs e) + { + _parent.GoBack(); + } + private void SelectFirmwareImage_OnClick(object sender, RoutedEventArgs e) { - if (FirmwareFound) + if (ViewModel.FirmwareFound) { - _parent.Navigate(typeof(AvatarWindow), (_parent, _profile)); + _parent.Navigate(typeof(UserFirmwareAvatarSelectorView), (_parent, _profile)); } } diff --git a/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml b/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml new file mode 100644 index 000000000..62b5e1840 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs b/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs new file mode 100644 index 000000000..0c53e53d7 --- /dev/null +++ b/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs @@ -0,0 +1,51 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using FluentAvalonia.UI.Controls; +using FluentAvalonia.UI.Navigation; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; + +namespace Ryujinx.Ava.UI.Views.User +{ + public partial class UserRecovererView : UserControl + { + private NavigationDialogHost _parent; + + public UserRecovererView() + { + InitializeComponent(); + AddHandler(Frame.NavigatedToEvent, (s, e) => + { + NavigatedTo(e); + }, RoutingStrategies.Direct); + } + + private void NavigatedTo(NavigationEventArgs arg) + { + if (Program.PreviewerDetached) + { + switch (arg.NavigationMode) + { + case NavigationMode.New: + var parent = (NavigationDialogHost)arg.Parameter; + + _parent = parent; + + ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - {LocaleManager.Instance[LocaleKeys.UserProfilesRecoverHeading]}"; + + break; + } + } + } + + private void GoBack(object sender, RoutedEventArgs e) + { + _parent?.GoBack(); + } + + private void Recover(object sender, RoutedEventArgs e) + { + _parent?.RecoverLostAccounts(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml new file mode 100644 index 000000000..cdf74d52f --- /dev/null +++ b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs new file mode 100644 index 000000000..9d955326f --- /dev/null +++ b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs @@ -0,0 +1,148 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Threading; +using FluentAvalonia.UI.Controls; +using FluentAvalonia.UI.Navigation; +using LibHac; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Shim; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using UserId = LibHac.Fs.UserId; + +namespace Ryujinx.Ava.UI.Views.User +{ + public partial class UserSaveManagerView : UserControl + { + internal UserSaveManagerViewModel ViewModel { get; private set; } + + private AccountManager _accountManager; + private HorizonClient _horizonClient; + private VirtualFileSystem _virtualFileSystem; + private NavigationDialogHost _parent; + + public UserSaveManagerView() + { + InitializeComponent(); + AddHandler(Frame.NavigatedToEvent, (s, e) => + { + NavigatedTo(e); + }, RoutingStrategies.Direct); + } + + private void NavigatedTo(NavigationEventArgs arg) + { + if (Program.PreviewerDetached) + { + switch (arg.NavigationMode) + { + case NavigationMode.New: + var args = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter; + _accountManager = args.accountManager; + _horizonClient = args.client; + _virtualFileSystem = args.virtualFileSystem; + + _parent = args.parent; + break; + } + + DataContext = ViewModel = new UserSaveManagerViewModel(_accountManager); + ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - {ViewModel.SaveManagerHeading}"; + + Task.Run(LoadSaves); + } + } + + public void LoadSaves() + { + ViewModel.Saves.Clear(); + var saves = new ObservableCollection(); + var saveDataFilter = SaveDataFilter.Make( + programId: default, + saveType: SaveDataType.Account, + new UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low), + saveDataId: default, + index: default); + + using var saveDataIterator = new UniqueRef(); + + _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); + + Span saveDataInfo = stackalloc SaveDataInfo[10]; + + while (true) + { + saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure(); + + if (readCount == 0) + { + break; + } + + for (int i = 0; i < readCount; i++) + { + var save = saveDataInfo[i]; + if (save.ProgramId.Value != 0) + { + var saveModel = new SaveModel(save, _horizonClient, _virtualFileSystem); + saves.Add(saveModel); + } + } + } + + Dispatcher.UIThread.Post(() => + { + ViewModel.Saves = saves; + ViewModel.Sort(); + }); + } + + private void GoBack(object sender, RoutedEventArgs e) + { + _parent?.GoBack(); + } + + private void OpenLocation(object sender, RoutedEventArgs e) + { + if (sender is Avalonia.Controls.Button button) + { + if (button.DataContext is SaveModel saveModel) + { + ApplicationHelper.OpenSaveDir(saveModel.SaveId); + } + } + } + + private async void Delete(object sender, RoutedEventArgs e) + { + if (sender is Avalonia.Controls.Button button) + { + if (button.DataContext is SaveModel saveModel) + { + var result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DeleteUserSave], + LocaleManager.Instance[LocaleKeys.IrreversibleActionNote], + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], ""); + + if (result == UserResult.Yes) + { + _horizonClient.Fs.DeleteSaveData(SaveDataSpaceId.User, saveModel.SaveId); + } + + ViewModel.Saves.Remove(saveModel); + ViewModel.Views.Remove(saveModel); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml b/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml new file mode 100644 index 000000000..9a6ba054e --- /dev/null +++ b/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + internal class SpecializationStateUpdater { + private readonly GpuContext _context; private GpuChannelGraphicsState _graphics; private GpuChannelPoolState _pool; @@ -18,6 +20,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private bool _changed; + /// + /// Creates a new instance of the specialization state updater class. + /// + /// GPU context + public SpecializationStateUpdater(GpuContext context) + { + _context = context; + } + /// /// Signal that the specialization state has changed. /// @@ -232,6 +243,42 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } } + /// + /// Updates the type of the outputs produced by the fragment shader based on the current render target state. + /// + /// The render target control register + /// The color attachment state + public void SetFragmentOutputTypes(RtControl rtControl, ref Array8 state) + { + bool changed = false; + int count = rtControl.UnpackCount(); + + for (int index = 0; index < Constants.TotalRenderTargets; index++) + { + int rtIndex = rtControl.UnpackPermutationIndex(index); + + var colorState = state[rtIndex]; + + if (index < count && StateUpdater.IsRtEnabled(colorState)) + { + Format format = colorState.Format.Convert().Format; + + AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float; + + if (type != _graphics.FragmentOutputTypes[index]) + { + _graphics.FragmentOutputTypes[index] = type; + changed = true; + } + } + } + + if (changed && _context.Capabilities.NeedsFragmentOutputSpecialization) + { + Signal(); + } + } + /// /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs index 3ed5607a4..7c7309676 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs @@ -138,6 +138,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _dirtyMask = ulong.MaxValue >> ((sizeof(ulong) * 8) - _callbacks.Length); } + /// + /// Check if the given register group is dirty without clearing it. + /// + /// Index of the group to check + /// True if dirty, false otherwise + public bool IsDirty(int groupIndex) + { + return (_dirtyMask & (1UL << groupIndex)) != 0; + } + /// /// Check all the groups specified by for modification, and update if modified. /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 64fa1735f..9b59009cf 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public const int ScissorStateIndex = 16; public const int VertexBufferStateIndex = 0; public const int PrimitiveRestartStateIndex = 12; + public const int RenderTargetStateIndex = 27; private readonly GpuContext _context; private readonly GpuChannel _channel; @@ -264,6 +265,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _prevTfEnable = false; } + if (_updateTracker.IsDirty(RenderTargetStateIndex)) + { + UpdateRenderTargetSpecialization(); + } + _updateTracker.Update(ulong.MaxValue); // If any state that the shader depends on changed, @@ -526,12 +532,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } } + /// + /// Updates specialization state based on render target state. + /// + public void UpdateRenderTargetSpecialization() + { + _currentSpecState.SetFragmentOutputTypes(_state.State.RtControl, ref _state.State.RtColorState); + } + /// /// Checks if a render target color buffer is used. /// /// Color buffer information /// True if the specified buffer is enabled/used, false otherwise - private static bool IsRtEnabled(RtColorState colorState) + internal static bool IsRtEnabled(RtColorState colorState) { // Colors are disabled by writing 0 to the format. return colorState.Format != 0 && colorState.WidthOrStride != 0; @@ -893,7 +907,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}."); - format = Format.R32G32B32A32Float; + format = vertexAttrib.UnpackType() switch + { + VertexAttribType.Sint => Format.R32G32B32A32Sint, + VertexAttribType.Uint => Format.R32G32B32A32Uint, + _ => Format.R32G32B32A32Float + }; } vertexAttribs[index] = new VertexAttribDescriptor( diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index a38c09875..19eb8b46e 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false); - var spec = new SpecializationStateUpdater(); + var spec = new SpecializationStateUpdater(context); var drawState = new DrawState(); _drawManager = new DrawManager(context, channel, _state, drawState, spec); diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs index 57c632dac..fe8587629 100644 --- a/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs @@ -1,4 +1,5 @@ -using Ryujinx.Graphics.Gpu.Engine.GPFifo; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Memory; using System; @@ -31,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu /// internal MemoryManager MemoryManager => _memoryManager; + /// + /// Host hardware capabilities from the GPU context. + /// + internal ref Capabilities Capabilities => ref _context.Capabilities; + /// /// Creates a new instance of a GPU channel. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index c567c2c06..97173c964 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -107,6 +107,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.AttributeTypes[location]; } + /// + public AttributeType QueryFragmentOutputType(int location) + { + return _oldSpecState.GraphicsState.FragmentOutputTypes[location]; + } + /// public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 28ea430cd..05631a210 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -113,6 +113,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.AttributeTypes[location]; } + /// + public AttributeType QueryFragmentOutputType(int location) + { + return _state.GraphicsState.FragmentOutputTypes[location]; + } + /// public int QueryComputeLocalSizeX() => _state.ComputeState.LocalSizeX; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 33f06b6e9..d36ffd70f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -112,6 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader }; } + public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision; + public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug; public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index e5e486260..70ac50170 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -87,6 +87,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public bool HasUnalignedStorageBuffer; + /// + /// Type of the fragment shader outputs. + /// + public Array8 FragmentOutputTypes; + /// /// Creates a new GPU graphics state. /// @@ -105,6 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Type of the vertex attributes consumed by the shader /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0 /// Indicates that any storage buffer use is unaligned + /// Type of the fragment shader outputs public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -120,7 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Shader float alphaTestReference, ref Array32 attributeTypes, bool hasConstantBufferDrawParameters, - bool hasUnalignedStorageBuffer) + bool hasUnalignedStorageBuffer, + ref Array8 fragmentOutputTypes) { EarlyZForce = earlyZForce; Topology = topology; @@ -137,6 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Shader AttributeTypes = attributeTypes; HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; + FragmentOutputTypes = fragmentOutputTypes; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index b0d77d8ae..a4bf81363 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -530,6 +530,11 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + if (channel.Capabilities.NeedsFragmentOutputSpecialization && !graphicsState.FragmentOutputTypes.AsSpan().SequenceEqual(GraphicsState.FragmentOutputTypes.AsSpan())) + { + return false; + } + return Matches(channel, ref poolState, checkTextures, isCompute: false); } diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 1733c6f21..30ed942d3 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.OpenGL vendorName: GpuVendor, hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows, hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows, + needsFragmentOutputSpecialization: false, + reduceShaderPrecision: false, supportsAstcCompression: HwCapabilities.SupportsAstcCompression, supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc, supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc, diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 4da21cb72..996d312b7 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -346,12 +346,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string name = context.OperandManager.DeclareLocal(decl); - context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); + context.AppendLine(GetVarTypeName(context, decl.VarType) + " " + name + ";"); } } - public static string GetVarTypeName(AggregateType type, bool precise = true) + public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool precise = true) { + if (context.Config.GpuAccessor.QueryHostReducedPrecision()) + { + precise = false; + } + return type switch { AggregateType.Void => "void", @@ -666,7 +671,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - context.AppendLine($"layout (location = {attr}) out vec4 {name};"); + string type = context.Config.Stage != ShaderStage.Fragment ? "vec4" : + context.Config.GpuAccessor.QueryFragmentOutputType(attr) switch + { + AttributeType.Sint => "ivec4", + AttributeType.Uint => "uvec4", + _ => "vec4" + }; + + if (context.Config.GpuAccessor.QueryHostReducedPrecision() && context.Config.Stage == ShaderStage.Vertex && attr == 0) + { + context.AppendLine($"layout (location = {attr}) invariant out {type} {name};"); + } + else + { + context.AppendLine($"layout (location = {attr}) out {type} {name};"); + } } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 90826a154..907275583 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { for (int i = 1; i < info.Functions.Count; i++) { - context.AppendLine($"{GetFunctionSignature(info.Functions[i])};"); + context.AppendLine($"{GetFunctionSignature(context, info.Functions[i])};"); } context.AppendLine(); @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { context.CurrentFunction = function; - context.AppendLine(GetFunctionSignature(function, funcName)); + context.AppendLine(GetFunctionSignature(context, function, funcName)); context.EnterScope(); Declarations.DeclareLocals(context, function); @@ -54,23 +54,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.LeaveScope(); } - private static string GetFunctionSignature(StructuredFunction function, string funcName = null) + private static string GetFunctionSignature(CodeGenContext context, StructuredFunction function, string funcName = null) { string[] args = new string[function.InArguments.Length + function.OutArguments.Length]; for (int i = 0; i < function.InArguments.Length; i++) { - args[i] = $"{Declarations.GetVarTypeName(function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; + args[i] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; } for (int i = 0; i < function.OutArguments.Length; i++) { int j = i + function.InArguments.Length; - args[j] = $"out {Declarations.GetVarTypeName(function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; + args[j] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; } - return $"{Declarations.GetVarTypeName(function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; + return $"{Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; } private static void PrintBlock(CodeGenContext context, AstBlock block) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index f667d0808..3dbd73b27 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if ((outputType & AggregateType.ElementCountMask) != 0) { - return $"{Declarations.GetVarTypeName(outputType, precise: false)}({imageConst})"; + return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({imageConst})"; } return imageConst; @@ -513,7 +513,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if ((outputType & AggregateType.ElementCountMask) != 0) { - return $"{Declarations.GetVarTypeName(outputType, precise: false)}({scalarValue})"; + return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({scalarValue})"; } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 3da72b402..fab1667ce 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -577,6 +577,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.Decorate(spvVar, Decoration.Patch); } + if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment) + { + context.Decorate(spvVar, Decoration.Invariant); + } + context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index a02c4c22a..61abf3341 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -2194,13 +2194,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (operation.Inst.HasFlag(Instruction.FP64)) { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2)); - context.Decorate(result, Decoration.NoContraction); + + if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + { + context.Decorate(result, Decoration.NoContraction); + } + return new OperationResult(AggregateType.FP64, result); } else if (operation.Inst.HasFlag(Instruction.FP32)) { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2)); - context.Decorate(result, Decoration.NoContraction); + + if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + { + context.Decorate(result, Decoration.NoContraction); + } + return new OperationResult(AggregateType.FP32, result); } else @@ -2255,13 +2265,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (operation.Inst.HasFlag(Instruction.FP64)) { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3)); - context.Decorate(result, Decoration.NoContraction); + + if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + { + context.Decorate(result, Decoration.NoContraction); + } + return new OperationResult(AggregateType.FP64, result); } else if (operation.Inst.HasFlag(Instruction.FP32)) { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3)); - context.Decorate(result, Decoration.NoContraction); + + if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + { + context.Decorate(result, Decoration.NoContraction); + } + return new OperationResult(AggregateType.FP32, result); } else diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 337bd314a..55df8dc31 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -114,6 +114,16 @@ namespace Ryujinx.Graphics.Shader return index; } + /// + /// Queries output type for fragment shaders. + /// + /// Location of the framgent output + /// Output location + AttributeType QueryFragmentOutputType(int location) + { + return AttributeType.Float; + } + /// /// Queries Local Size X for compute shaders. /// @@ -186,6 +196,15 @@ namespace Ryujinx.Graphics.Shader return false; } + /// + /// Queries host about whether to reduce precision to improve performance. + /// + /// True if precision is limited to vertex position, false otherwise + bool QueryHostReducedPrecision() + { + return false; + } + /// /// Queries host about the presence of the FrontFacing built-in variable bug. /// diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index fa1ae17f6..b671429a8 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -128,7 +128,15 @@ namespace Ryujinx.Graphics.Shader.Translation } else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) { - return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | AggregateType.FP32, false); + int location = (value - AttributeConsts.FragmentOutputColorBase) / 16; + var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch + { + AttributeType.Sint => AggregateType.S32, + AttributeType.Uint => AggregateType.U32, + _ => AggregateType.FP32 + }; + + return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false); } else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY) { diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 1b99a609c..751ef5ebf 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -140,6 +140,25 @@ namespace Ryujinx.Graphics.Vulkan return _attachments[index]; } + public ComponentType GetAttachmentComponentType(int index) + { + if (_colors != null && (uint)index < _colors.Length) + { + var format = _colors[index].Info.Format; + + if (format.IsSint()) + { + return ComponentType.SignedInteger; + } + else if (format.IsUint()) + { + return ComponentType.UnsignedInteger; + } + } + + return ComponentType.Float; + } + public bool IsValidColorAttachment(int bindIndex) { return (uint)bindIndex < Constants.MaxRenderTargets && (_validColorAttachments & (1u << bindIndex)) != 0; diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 31acfc9b6..0a4d365f2 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -1,7 +1,20 @@ using Silk.NET.Vulkan; +using System; namespace Ryujinx.Graphics.Vulkan { + [Flags] + enum PortabilitySubsetFlags + { + None = 0, + + VertexBufferAlignment4B = 1, + NoTriangleFans = 1 << 1, + NoPointMode = 1 << 2, + No3DImageView = 1 << 3, + NoLodBias = 1 << 4 + } + readonly struct HardwareCapabilities { public readonly bool SupportsIndexTypeUint8; @@ -23,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly uint MaxSubgroupSize; public readonly ShaderStageFlags RequiredSubgroupSizeStages; public readonly SampleCountFlags SupportedSampleCounts; + public readonly PortabilitySubsetFlags PortabilitySubset; public HardwareCapabilities( bool supportsIndexTypeUint8, @@ -43,7 +57,8 @@ namespace Ryujinx.Graphics.Vulkan uint minSubgroupSize, uint maxSubgroupSize, ShaderStageFlags requiredSubgroupSizeStages, - SampleCountFlags supportedSampleCounts) + SampleCountFlags supportedSampleCounts, + PortabilitySubsetFlags portabilitySubset) { SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsCustomBorderColor = supportsCustomBorderColor; @@ -64,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan MaxSubgroupSize = maxSubgroupSize; RequiredSubgroupSizeStages = requiredSubgroupSizeStages; SupportedSampleCounts = supportedSampleCounts; + PortabilitySubset = portabilitySubset; } } } diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 14a636151..223bcc71e 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -9,6 +9,13 @@ using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan { + enum ComponentType + { + Float, + SignedInteger, + UnsignedInteger + } + class HelperShader : IDisposable { private const int UniformBufferAlignment = 256; @@ -18,7 +25,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly ISampler _samplerNearest; private readonly IProgram _programColorBlit; private readonly IProgram _programColorBlitClearAlpha; - private readonly IProgram _programColorClear; + private readonly IProgram _programColorClearF; + private readonly IProgram _programColorClearSI; + private readonly IProgram _programColorClearUI; private readonly IProgram _programStrideChange; private readonly IProgram _programConvertIndexBuffer; private readonly IProgram _programConvertIndirectData; @@ -63,10 +72,22 @@ namespace Ryujinx.Graphics.Vulkan Array.Empty(), Array.Empty()); - _programColorClear = gd.CreateProgramWithMinimalLayout(new[] + _programColorClearF = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programColorClearSI = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); var strideChangeBindings = new ShaderBindings( @@ -242,6 +263,7 @@ namespace Ryujinx.Graphics.Vulkan int dstWidth, int dstHeight, VkFormat dstFormat, + ComponentType type, Rectangle scissor) { const int ClearColorBufferSize = 16; @@ -273,7 +295,22 @@ namespace Ryujinx.Graphics.Vulkan scissors[0] = scissor; - _pipeline.SetProgram(_programColorClear); + IProgram program; + + if (type == ComponentType.SignedInteger) + { + program = _programColorClearSI; + } + else if (type == ComponentType.UnsignedInteger) + { + program = _programColorClearUI; + } + else + { + program = _programColorClearF; + } + + _pipeline.SetProgram(program); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); _pipeline.SetRenderTargetColorMasks(new uint[] { componentMask }); _pipeline.SetViewports(viewports, false); @@ -948,7 +985,9 @@ namespace Ryujinx.Graphics.Vulkan { _programColorBlitClearAlpha.Dispose(); _programColorBlit.Dispose(); - _programColorClear.Dispose(); + _programColorClearF.Dispose(); + _programColorClearSI.Dispose(); + _programColorClearUI.Dispose(); _programStrideChange.Dispose(); _programConvertIndexBuffer.Dispose(); _programConvertIndirectData.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs new file mode 100644 index 000000000..4fbae86e7 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs @@ -0,0 +1,104 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan.MoltenVK +{ + enum MVKConfigLogLevel : int + { + None = 0, + Error = 1, + Warning = 2, + Info = 3, + Debug = 4 + } + + enum MVKConfigTraceVulkanCalls : int + { + None = 0, + Enter = 1, + EnterExit = 2, + Duration = 3 + } + + enum MVKConfigAutoGPUCaptureScope : int + { + None = 0, + Device = 1, + Frame = 2 + } + + [Flags] + enum MVKConfigAdvertiseExtensions : int + { + All = 0x00000001, + MoltenVK = 0x00000002, + WSI = 0x00000004, + Portability = 0x00000008 + } + + enum MVKVkSemaphoreSupportStyle : int + { + MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE = 0, + MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS_WHERE_SAFE = 1, + MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS = 2, + MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_CALLBACK = 3, + MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_MAX_ENUM = 0x7FFFFFFF + } + + readonly struct Bool32 + { + uint Value { get; } + + public Bool32(uint value) + { + Value = value; + } + + public Bool32(bool value) + { + Value = value ? 1u : 0u; + } + + public static implicit operator bool(Bool32 val) => val.Value == 1; + public static implicit operator Bool32(bool val) => new Bool32(val); + } + + [StructLayout(LayoutKind.Sequential)] + struct MVKConfiguration + { + public Bool32 DebugMode; + public Bool32 ShaderConversionFlipVertexY; + public Bool32 SynchronousQueueSubmits; + public Bool32 PrefillMetalCommandBuffers; + public uint MaxActiveMetalCommandBuffersPerQueue; + public Bool32 SupportLargeQueryPools; + public Bool32 PresentWithCommandBuffer; + public Bool32 SwapchainMagFilterUseNearest; + public ulong MetalCompileTimeout; + public Bool32 PerformanceTracking; + public uint PerformanceLoggingFrameCount; + public Bool32 DisplayWatermark; + public Bool32 SpecializedQueueFamilies; + public Bool32 SwitchSystemGPU; + public Bool32 FullImageViewSwizzle; + public uint DefaultGPUCaptureScopeQueueFamilyIndex; + public uint DefaultGPUCaptureScopeQueueIndex; + public Bool32 FastMathEnabled; + public MVKConfigLogLevel LogLevel; + public MVKConfigTraceVulkanCalls TraceVulkanCalls; + public Bool32 ForceLowPowerGPU; + public Bool32 SemaphoreUseMTLFence; + public MVKVkSemaphoreSupportStyle SemaphoreSupportStyle; + public MVKConfigAutoGPUCaptureScope AutoGPUCaptureScope; + public IntPtr AutoGPUCaptureOutputFilepath; + public Bool32 Texture1DAs2D; + public Bool32 PreallocateDescriptors; + public Bool32 UseCommandPooling; + public Bool32 UseMTLHeap; + public Bool32 LogActivityPerformanceInline; + public uint ApiVersionToAdvertise; + public MVKConfigAdvertiseExtensions AdvertiseExtensions; + public Bool32 ResumeLostDevice; + public Bool32 UseMetalArgumentBuffers; + } +} diff --git a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs new file mode 100644 index 000000000..ca2fbfb94 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -0,0 +1,31 @@ +using Silk.NET.Vulkan; +using System; +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan.MoltenVK +{ + [SupportedOSPlatform("macos")] + public static partial class MVKInitialization + { + [LibraryImport("libMoltenVK.dylib")] + private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize); + + [LibraryImport("libMoltenVK.dylib")] + private static partial Result vkSetMoltenVKConfigurationMVK(IntPtr unusedInstance, in MVKConfiguration config, in IntPtr configSize); + + public static void Initialize() + { + var configSize = (IntPtr)Marshal.SizeOf(); + + vkGetMoltenVKConfigurationMVK(IntPtr.Zero, out MVKConfiguration config, configSize); + + config.UseMetalArgumentBuffers = true; + + config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE; + config.SynchronousQueueSubmits = false; + + vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index dfcb32d40..1c0c836bb 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -50,6 +51,11 @@ namespace Ryujinx.Graphics.Vulkan private Auto _renderPass; private int _writtenAttachmentCount; + private bool _framebufferUsingColorWriteMask; + + private ITexture[] _preMaskColors; + private ITexture _preMaskDepthStencil; + private readonly DescriptorSetUpdater _descriptorSetUpdater; private IndexBufferState _indexBuffer; @@ -905,22 +911,35 @@ namespace Ryujinx.Graphics.Vulkan } } - SignalStateChange(); - - if (writtenAttachments != _writtenAttachmentCount) + if (_framebufferUsingColorWriteMask) { - SignalAttachmentChange(); - _writtenAttachmentCount = writtenAttachments; + SetRenderTargetsInternal(_preMaskColors, _preMaskDepthStencil, true); } + else + { + SignalStateChange(); + + if (writtenAttachments != _writtenAttachmentCount) + { + SignalAttachmentChange(); + _writtenAttachmentCount = writtenAttachments; + } + } + } + + private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) + { + FramebufferParams?.UpdateModifications(); + CreateFramebuffer(colors, depthStencil, filterWriteMasked); + CreateRenderPass(); + SignalStateChange(); + SignalAttachmentChange(); } public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) { - FramebufferParams?.UpdateModifications(); - CreateFramebuffer(colors, depthStencil); - CreateRenderPass(); - SignalStateChange(); - SignalAttachmentChange(); + _framebufferUsingColorWriteMask = false; + SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR); } public void SetRenderTargetScale(float scale) @@ -1102,7 +1121,7 @@ namespace Ryujinx.Graphics.Vulkan int vbSize = vertexBuffer.Buffer.Size; - if (Gd.Vendor == Vendor.Amd && vertexBuffer.Stride > 0) + if (Gd.Vendor == Vendor.Amd && !Gd.IsMoltenVk && vertexBuffer.Stride > 0) { // AMD has a bug where if offset + stride * count is greater than // the size, then the last attribute will have the wrong value. @@ -1119,7 +1138,8 @@ namespace Ryujinx.Graphics.Vulkan buffer.Dispose(); - if ((vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) + if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) && + (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) { buffer = new VertexBufferState( vb, @@ -1259,8 +1279,62 @@ namespace Ryujinx.Graphics.Vulkan _currentPipelineHandle = 0; } - private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil) + private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) { + if (filterWriteMasked) + { + // 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. + + void maskOut() + { + if (!_framebufferUsingColorWriteMask) + { + _preMaskColors = colors.ToArray(); + _preMaskDepthStencil = depthStencil; + } + + // If true, then the framebuffer must be recreated when the mask changes. + _framebufferUsingColorWriteMask = true; + } + + // Look for textures that are masked out. + + for (int i = 0; i < colors.Length; i++) + { + if (colors[i] == null) + { + continue; + } + + ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; + + 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. + ref var vkBlend2 = ref _newState.Internal.ColorBlendAttachmentState[j]; + if (vkBlend.ColorWriteMask == 0) + { + colors[i] = null; + maskOut(); + } + else if (vkBlend2.ColorWriteMask == 0) + { + colors[j] = null; + maskOut(); + } + } + } + } + } + FramebufferParams = new FramebufferParams(Device, colors, depthStencil); UpdatePipelineAttachmentFormats(); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 2256c5422..e4bf4fffa 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -79,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan (int)FramebufferParams.Width, (int)FramebufferParams.Height, FramebufferParams.AttachmentFormats[index], + FramebufferParams.GetAttachmentComponentType(index), ClearScissor); } else diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorClearFragmentShaderSource.frag rename to Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag new file mode 100644 index 000000000..4254f4f86 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag @@ -0,0 +1,9 @@ +#version 450 core + +layout (location = 0) in vec4 clear_colour; +layout (location = 0) out ivec4 colour; + +void main() +{ + colour = floatBitsToInt(clear_colour); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag new file mode 100644 index 000000000..08a6b8648 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag @@ -0,0 +1,9 @@ +#version 450 core + +layout (location = 0) in vec4 clear_colour; +layout (location = 0) out uvec4 colour; + +void main() +{ + colour = floatBitsToUint(clear_colour); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index 992f1e692..667e5a8b4 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x3C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorClearFragmentShaderSource = new byte[] + public static readonly byte[] ColorClearFFragmentShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, @@ -459,6 +459,68 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x0C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + public static readonly byte[] ColorClearSIFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, + 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61, + 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + + public static readonly byte[] ColorClearUIFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, + 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61, + 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + public static readonly byte[] ColorClearVertexShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00, diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index f4feecbcd..28fabb4fa 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Vulkan flags |= ImageCreateFlags.CreateCubeCompatibleBit; } - if (type == ImageType.Type3D) + if (type == ImageType.Type3D && !gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView)) { flags |= ImageCreateFlags.Create2DArrayCompatibleBit; } diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index c58c9fc5d..a9e1ed361 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -94,8 +94,14 @@ namespace Ryujinx.Graphics.Vulkan var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers); var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers); - unsafe Auto CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType) + unsafe Auto CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags = 0) { + var usage = new ImageViewUsageCreateInfo() + { + SType = StructureType.ImageViewUsageCreateInfo, + Usage = usageFlags + }; + var imageCreateInfo = new ImageViewCreateInfo() { SType = StructureType.ImageViewCreateInfo, @@ -103,7 +109,8 @@ namespace Ryujinx.Graphics.Vulkan ViewType = viewType, Format = format, Components = cm, - SubresourceRange = sr + SubresourceRange = sr, + PNext = usageFlags == 0 ? null : &usage }; gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError(); @@ -124,9 +131,21 @@ namespace Ryujinx.Graphics.Vulkan // Framebuffer attachments also require 3D textures to be bound as 2D array. if (info.Target == Target.Texture3D) { - subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth); + if (gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView)) + { + if (levels == 1 && (info.Format.IsRtColorCompatible() || info.Format.IsDepthOrStencil())) + { + subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, 1); - _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray); + _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2D, ImageUsageFlags.ColorAttachmentBit); + } + } + else + { + subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth); + + _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray); + } } Valid = true; @@ -353,7 +372,7 @@ namespace Ryujinx.Graphics.Vulkan } if (VulkanConfiguration.UseSlowSafeBlitOnAmd && - _gd.Vendor == Vendor.Amd && + (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk) && src.Info.Target == Target.Texture2D && dst.Info.Target == Target.Texture2D && !dst.Info.Format.IsDepthOrStencil()) diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/Ryujinx.Graphics.Vulkan/Vendor.cs index 7a6e0e600..087d6e9dc 100644 --- a/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -5,9 +5,12 @@ namespace Ryujinx.Graphics.Vulkan enum Vendor { Amd, + ImgTec, Intel, Nvidia, + ARM, Qualcomm, + Apple, Unknown } @@ -21,7 +24,10 @@ namespace Ryujinx.Graphics.Vulkan return id switch { 0x1002 => Vendor.Amd, + 0x1010 => Vendor.ImgTec, + 0x106B => Vendor.Apple, 0x10DE => Vendor.Nvidia, + 0x13B5 => Vendor.ARM, 0x8086 => Vendor.Intel, 0x5143 => Vendor.Qualcomm, _ => Vendor.Unknown @@ -34,6 +40,7 @@ namespace Ryujinx.Graphics.Vulkan { 0x1002 => "AMD", 0x1010 => "ImgTec", + 0x106B => "Apple", 0x10DE => "NVIDIA", 0x13B5 => "ARM", 0x1AE0 => "Google", diff --git a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index 661bb7747..7a0220108 100644 --- a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -82,9 +82,9 @@ namespace Ryujinx.Graphics.Vulkan } _buffer = autoBuffer; - } - state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; + state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride; + } return; } diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 68462825f..fe9462aa3 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -389,6 +389,18 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &featuresCustomBorderColorSupported; } + PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + { + SType = StructureType.PhysicalDeviceRobustness2FeaturesExt + }; + + if (supportedExtensions.Contains("VK_EXT_robustness2")) + { + supportedFeaturesRobustness2.PNext = features2.PNext; + + features2.PNext = &supportedFeaturesRobustness2; + } + api.GetPhysicalDeviceFeatures2(physicalDevice, &features2); var supportedFeatures = features2.Features; @@ -428,14 +440,17 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresTransformFeedback; - var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + if (supportedExtensions.Contains("VK_EXT_robustness2")) { - SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, - PNext = pExtendedFeatures, - NullDescriptor = true - }; + var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + { + SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, + PNext = pExtendedFeatures, + NullDescriptor = supportedFeaturesRobustness2.NullDescriptor + }; - pExtendedFeatures = &featuresRobustness2; + pExtendedFeatures = &featuresRobustness2; + } var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT() { diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 5c77cb001..f1922efed 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -3,6 +3,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; +using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Graphics.Vulkan.Queries; using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.EXT; @@ -77,6 +78,8 @@ namespace Ryujinx.Graphics.Vulkan internal bool IsAmdWindows { get; private set; } internal bool IsIntelWindows { get; private set; } internal bool IsAmdGcn { get; private set; } + internal bool IsMoltenVk { get; private set; } + internal bool IsTBDR { get; private set; } public string GpuVendor { get; private set; } public string GpuRenderer { get; private set; } public string GpuVersion { get; private set; } @@ -93,6 +96,14 @@ namespace Ryujinx.Graphics.Vulkan Shaders = new HashSet(); Textures = new HashSet(); Samplers = new HashSet(); + + if (OperatingSystem.IsMacOS()) + { + MVKInitialization.Initialize(); + + // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. + IsMoltenVk = true; + } } private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex) @@ -161,7 +172,10 @@ namespace Ryujinx.Graphics.Vulkan properties2.PNext = &propertiesTransformFeedback; } - Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2); + PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new PhysicalDevicePortabilitySubsetPropertiesKHR() + { + SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr + }; PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() { @@ -183,6 +197,11 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt }; + PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR() + { + SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr + }; + if (supportedExtensions.Contains("VK_EXT_robustness2")) { features2.PNext = &featuresRobustness2; @@ -200,8 +219,31 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &featuresCustomBorderColor; } + bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset"); + + if (usePortability) + { + propertiesPortabilitySubset.PNext = properties2.PNext; + properties2.PNext = &propertiesPortabilitySubset; + + featuresPortabilitySubset.PNext = features2.PNext; + features2.PNext = &featuresPortabilitySubset; + } + + Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2); Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); + var portabilityFlags = PortabilitySubsetFlags.None; + + if (usePortability) + { + portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0; + portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans; + portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode; + portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView; + portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias; + } + bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") && featuresCustomBorderColor.CustomBorderColors && featuresCustomBorderColor.CustomBorderColorWithoutFormat; @@ -224,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), features2.Features.MultiViewport, - featuresRobustness2.NullDescriptor, + featuresRobustness2.NullDescriptor || IsMoltenVk, supportedExtensions.Contains(KhrPushDescriptor.ExtensionName), supportsTransformFeedback, propertiesTransformFeedback.TransformFeedbackQueries, @@ -232,7 +274,8 @@ namespace Ryujinx.Graphics.Vulkan propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, - supportedSampleCounts); + supportedSampleCounts, + portabilityFlags); MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount); @@ -413,6 +456,36 @@ namespace Ryujinx.Graphics.Vulkan bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, GAL.Format.R4G4B4A4Unorm); + bool supportsAstcFormats = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, + GAL.Format.Astc4x4Unorm, + GAL.Format.Astc5x4Unorm, + GAL.Format.Astc5x5Unorm, + GAL.Format.Astc6x5Unorm, + GAL.Format.Astc6x6Unorm, + GAL.Format.Astc8x5Unorm, + GAL.Format.Astc8x6Unorm, + GAL.Format.Astc8x8Unorm, + GAL.Format.Astc10x5Unorm, + GAL.Format.Astc10x6Unorm, + GAL.Format.Astc10x8Unorm, + GAL.Format.Astc10x10Unorm, + GAL.Format.Astc12x10Unorm, + GAL.Format.Astc12x12Unorm, + GAL.Format.Astc4x4Srgb, + GAL.Format.Astc5x4Srgb, + GAL.Format.Astc5x5Srgb, + GAL.Format.Astc6x5Srgb, + GAL.Format.Astc6x6Srgb, + GAL.Format.Astc8x5Srgb, + GAL.Format.Astc8x6Srgb, + GAL.Format.Astc8x8Srgb, + GAL.Format.Astc10x5Srgb, + GAL.Format.Astc10x6Srgb, + GAL.Format.Astc10x8Srgb, + GAL.Format.Astc10x10Srgb, + GAL.Format.Astc12x10Srgb, + GAL.Format.Astc12x12Srgb); + PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features() { SType = StructureType.PhysicalDeviceVulkan12Features @@ -434,7 +507,9 @@ namespace Ryujinx.Graphics.Vulkan GpuVendor, hasFrontFacingBug: IsIntelWindows, hasVectorIndexingBug: Vendor == Vendor.Qualcomm, - supportsAstcCompression: features2.Features.TextureCompressionAstcLdr, + needsFragmentOutputSpecialization: IsMoltenVk, + reduceShaderPrecision: IsMoltenVk, + supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats, supportsBc123Compression: supportsBc123CompressionFormat, supportsBc45Compression: supportsBc45CompressionFormat, supportsBc67Compression: supportsBc67CompressionFormat, @@ -515,12 +590,13 @@ namespace Ryujinx.Graphics.Vulkan IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec; GpuVendor = vendorName; GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}"; - IsAmdGcn = Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer); + IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer); Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); } @@ -531,6 +607,7 @@ namespace Ryujinx.Graphics.Vulkan { GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles, GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip, + GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans) ? GAL.PrimitiveTopology.Triangles : topology, _ => topology }; } @@ -540,6 +617,7 @@ namespace Ryujinx.Graphics.Vulkan return topology switch { GAL.PrimitiveTopology.Quads => true, + GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans), _ => false }; } @@ -553,7 +631,13 @@ namespace Ryujinx.Graphics.Vulkan public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) { - if (Vendor != Vendor.Nvidia) + if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B)) + { + alignment = 4; + + return true; + } + else if (Vendor != Vendor.Nvidia) { // Vulkan requires that vertex attributes are globally aligned by their component size, // so buffer strides that don't divide by the largest scalar element are invalid. From 4d2c8e2a442d2859a75c6c9162a192a0a9a221be Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 13 Jan 2023 01:50:14 +0100 Subject: [PATCH 280/737] Prepo: Fix SaveSystemReport* IPC definitions (#4278) * Prepo: Fix SaveSystemReport IPC definitions * Follow original code * Fix args index in HipcGenerator * Addresses feedback * oops --- .../Hipc/HipcGenerator.cs | 6 ++- Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs | 29 +++++++---- Ryujinx.Horizon/Sdk/Account/Uid.cs | 2 +- Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs | 52 +++++++++++++++++++ Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs | 4 +- Ryujinx.Horizon/Sdk/Sm/ServiceName.cs | 2 +- 6 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs index 332e04d17..8f4c37f70 100644 --- a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs +++ b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -267,6 +267,8 @@ namespace Ryujinx.Horizon.Generators.Hipc } int index = 0; + int inArgIndex = 0; + int outArgIndex = 0; int inCopyHandleIndex = 0; int inMoveHandleIndex = 0; int inObjectIndex = 0; @@ -284,7 +286,7 @@ namespace Ryujinx.Horizon.Generators.Hipc { if (IsNonSpanOutBuffer(compilation, parameter)) { - generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}));"); + generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({outArgIndex++}));"); argName = $"out {GenerateSpanCastElement0(canonicalTypeName, $"{argName}.Memory.Span")}"; } @@ -302,7 +304,7 @@ namespace Ryujinx.Horizon.Generators.Hipc switch (argType) { case CommandArgType.InArgument: - value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({index}))"; + value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({inArgIndex++}))"; break; case CommandArgType.InCopyHandle: value = $"CommandSerialization.DeserializeCopyHandle(ref context, {inCopyHandleIndex++})"; diff --git a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs index 672bba4ea..e157fa562 100644 --- a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs +++ b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc return PrepoResult.PermissionDenied; } - ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, Uid.Null); + ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, Uid.Null); return Result.Success; } @@ -57,7 +57,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc return PrepoResult.PermissionDenied; } - ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, userId, true); + ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, userId, true); return Result.Success; } @@ -107,25 +107,25 @@ namespace Ryujinx.Horizon.Prepo.Ipc } [CmifCommand(20100)] - public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer) { if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) { return PrepoResult.PermissionDenied; } - return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, Uid.Null); + return ProcessPlayReport(PlayReportKind.System, gameRoomBuffer, reportBuffer, 0, Uid.Null, false, applicationId); } [CmifCommand(20101)] - public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer) { if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) { return PrepoResult.PermissionDenied; } - return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, userId, true); + return ProcessPlayReport(PlayReportKind.System, gameRoomBuffer, reportBuffer, 0, userId, true, applicationId); } [CmifCommand(40100)] // 2.0.0+ @@ -164,7 +164,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc return PrepoResult.PermissionDenied; } - private static Result ProcessPlayReport(PlayReportKind playReportKind, ulong pid, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, Uid userId, bool withUserId = false) + private static Result ProcessPlayReport(PlayReportKind playReportKind, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid, Uid userId, bool withUserId = false, Sdk.Ncm.ApplicationId applicationId = default) { if (withUserId) { @@ -191,16 +191,23 @@ namespace Ryujinx.Horizon.Prepo.Ipc return PrepoResult.InvalidBufferSize; } - // NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned. - // Reports are stored internally and an event is signaled to transmit them. - StringBuilder builder = new(); MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray()); builder.AppendLine(); builder.AppendLine("PlayReport log:"); builder.AppendLine($" Kind: {playReportKind}"); - builder.AppendLine($" Pid: {pid}"); + + // NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned. + // Reports are stored internally and an event is signaled to transmit them. + if (pid != 0) + { + builder.AppendLine($" Pid: {pid}"); + } + else + { + builder.AppendLine($" ApplicationId: {applicationId}"); + } if (!userId.IsNull) { diff --git a/Ryujinx.Horizon/Sdk/Account/Uid.cs b/Ryujinx.Horizon/Sdk/Account/Uid.cs index a0a399781..5aad0463f 100644 --- a/Ryujinx.Horizon/Sdk/Account/Uid.cs +++ b/Ryujinx.Horizon/Sdk/Account/Uid.cs @@ -6,7 +6,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.Horizon.Sdk.Account { [StructLayout(LayoutKind.Sequential)] - public readonly record struct Uid + readonly record struct Uid { public readonly long High; public readonly long Low; diff --git a/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs b/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs new file mode 100644 index 000000000..37b4cbfbc --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs @@ -0,0 +1,52 @@ +namespace Ryujinx.Horizon.Sdk.Ncm +{ + readonly struct ApplicationId + { + public readonly ulong Id; + + public static int Length => sizeof(ulong); + + public static ApplicationId First => new(0x0100000000010000); + + public static ApplicationId Last => new(0x01FFFFFFFFFFFFFF); + + public static ApplicationId Invalid => new(0); + + public bool IsValid => Id >= First.Id && Id <= Last.Id; + + public ApplicationId(ulong id) + { + Id = id; + } + + public override bool Equals(object obj) + { + return obj is ApplicationId applicationId && applicationId.Equals(this); + } + + public bool Equals(ApplicationId other) + { + return other.Id == Id; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + public static bool operator ==(ApplicationId lhs, ApplicationId rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(ApplicationId lhs, ApplicationId rhs) + { + return !lhs.Equals(rhs); + } + + public override string ToString() + { + return $"0x{Id:x}"; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs index f61ee61bd..042cb4007 100644 --- a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs +++ b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs @@ -12,8 +12,8 @@ namespace Ryujinx.Horizon.Sdk.Prepo Result RequestImmediateTransmission(); Result GetTransmissionStatus(out int status); Result GetSystemSessionId(out ulong systemSessionId); - Result SaveSystemReport(ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); - Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result SaveSystemReport(ReadOnlySpan gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan reportBuffer); + Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan reportBuffer); Result IsUserAgreementCheckEnabled(out bool enabled); Result SetUserAgreementCheckEnabled(bool enabled); } diff --git a/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs b/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs index dbb300785..9b7fae3f5 100644 --- a/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs +++ b/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { public static ServiceName Invalid { get; } = new ServiceName(0); - public bool IsInvalid => Packed == 0; + public bool IsValid => Packed != 0; public int Length => sizeof(ulong); From dca5b14493e730960ed5cd67906278ecea969b3a Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 13 Jan 2023 02:09:48 -0300 Subject: [PATCH 281/737] Relax Vulkan requirements (#4228) --- Ryujinx.Graphics.Vulkan/BufferManager.cs | 22 ++++++- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 3 +- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 33 ++++++++-- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 3 +- Ryujinx.Graphics.Vulkan/Vendor.cs | 3 + .../VulkanInitialization.cs | 66 +++++++++++++------ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 6 +- Ryujinx.Graphics.Vulkan/Window.cs | 22 +++++-- 8 files changed, 121 insertions(+), 37 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index f32403712..9c50e6ff3 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan MemoryPropertyFlags.HostCoherentBit | MemoryPropertyFlags.HostCachedBit; + // Some drivers don't expose a "HostCached" memory type, + // so we need those alternative flags for the allocation to succeed there. + private const MemoryPropertyFlags DefaultBufferMemoryAltFlags = + MemoryPropertyFlags.HostVisibleBit | + MemoryPropertyFlags.HostCoherentBit; + private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = MemoryPropertyFlags.DeviceLocalBit; @@ -94,9 +100,21 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); - var allocateFlags = deviceLocal ? DeviceLocalBufferMemoryFlags : DefaultBufferMemoryFlags; + MemoryPropertyFlags allocateFlags; + MemoryPropertyFlags allocateFlagsAlt; - var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags); + if (deviceLocal) + { + allocateFlags = DeviceLocalBufferMemoryFlags; + allocateFlagsAlt = DeviceLocalBufferMemoryFlags; + } + else + { + allocateFlags = DefaultBufferMemoryFlags; + allocateFlagsAlt = DefaultBufferMemoryAltFlags; + } + + var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); if (allocation.Memory.Handle == 0UL) { diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 751ef5ebf..84ac0b051 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan public int[] AttachmentIndices { get; } public int AttachmentsCount { get; } - public int MaxColorAttachmentIndex { get; } + public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1; public bool HasDepthStencil { get; } public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); @@ -67,7 +67,6 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = new uint[count]; AttachmentFormats = new VkFormat[count]; AttachmentIndices = new int[count]; - MaxColorAttachmentIndex = colors.Length - 1; uint width = uint.MaxValue; uint height = uint.MaxValue; diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index eea4e60b3..83c0a3243 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -27,7 +27,16 @@ namespace Ryujinx.Graphics.Vulkan MemoryRequirements requirements, MemoryPropertyFlags flags = 0) { - int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags); + return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); + } + + public MemoryAllocation AllocateDeviceMemory( + PhysicalDevice physicalDevice, + MemoryRequirements requirements, + MemoryPropertyFlags flags, + MemoryPropertyFlags alternativeFlags) + { + int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); if (memoryTypeIndex < 0) { return default; @@ -56,21 +65,35 @@ namespace Ryujinx.Graphics.Vulkan return newBl.Allocate(size, alignment, map); } - private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, MemoryPropertyFlags flags) + private static int FindSuitableMemoryTypeIndex( + Vk api, + PhysicalDevice physicalDevice, + uint memoryTypeBits, + MemoryPropertyFlags flags, + MemoryPropertyFlags alternativeFlags) { + int bestCandidateIndex = -1; + api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); for (int i = 0; i < properties.MemoryTypeCount; i++) { var type = properties.MemoryTypes[i]; - if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) + if ((memoryTypeBits & (1 << i)) != 0) { - return i; + if (type.PropertyFlags.HasFlag(flags)) + { + return i; + } + else if (type.PropertyFlags.HasFlag(alternativeFlags)) + { + bestCandidateIndex = i; + } } } - return -1; + return bestCandidateIndex; } public void Dispose() diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 1c0c836bb..c4a9f2df5 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1344,8 +1344,7 @@ namespace Ryujinx.Graphics.Vulkan var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); - int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex + (FramebufferParams.HasDepthStencil ? 1 : 0); - for (int i = FramebufferParams.AttachmentFormats.Length; i <= maxAttachmentIndex; i++) + for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) { dstAttachmentFormats[i] = 0; } diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/Ryujinx.Graphics.Vulkan/Vendor.cs index 087d6e9dc..5e0290c0a 100644 --- a/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Vulkan Intel, Nvidia, ARM, + Broadcom, Qualcomm, Apple, Unknown @@ -28,6 +29,7 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => Vendor.Apple, 0x10DE => Vendor.Nvidia, 0x13B5 => Vendor.ARM, + 0x14E4 => Vendor.Broadcom, 0x8086 => Vendor.Intel, 0x5143 => Vendor.Qualcomm, _ => Vendor.Unknown @@ -43,6 +45,7 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => "Apple", 0x10DE => "NVIDIA", 0x13B5 => "ARM", + 0x14E4 => "Broadcom", 0x1AE0 => "Google", 0x5143 => "Qualcomm", 0x8086 => "Intel", diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index fe9462aa3..d9dcc0b25 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -162,7 +162,6 @@ namespace Ryujinx.Graphics.Vulkan if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) { Logger.Error?.Print(LogClass.Gpu, msg); - //throw new Exception(msg); } else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) { @@ -379,14 +378,34 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceFeatures2 }; - PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT() + PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new PhysicalDeviceVulkan11Features() { - SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt + SType = StructureType.PhysicalDeviceVulkan11Features, + PNext = features2.PNext + }; + + features2.PNext = &supportedFeaturesVk11; + + PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() + { + SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, + PNext = features2.PNext }; if (supportedExtensions.Contains("VK_EXT_custom_border_color")) { - features2.PNext = &featuresCustomBorderColorSupported; + features2.PNext = &supportedFeaturesCustomBorderColor; + } + + PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + { + SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, + PNext = features2.PNext + }; + + if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + { + features2.PNext = &supportedFeaturesTransformFeedback; } PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() @@ -408,41 +427,48 @@ namespace Ryujinx.Graphics.Vulkan var features = new PhysicalDeviceFeatures() { DepthBiasClamp = true, - DepthClamp = true, - DualSrcBlend = true, + DepthClamp = supportedFeatures.DepthClamp, + DualSrcBlend = supportedFeatures.DualSrcBlend, FragmentStoresAndAtomics = true, GeometryShader = supportedFeatures.GeometryShader, ImageCubeArray = true, IndependentBlend = true, LogicOp = supportedFeatures.LogicOp, - MultiViewport = true, + MultiViewport = supportedFeatures.MultiViewport, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, SamplerAnisotropy = true, ShaderClipDistance = true, ShaderFloat64 = supportedFeatures.ShaderFloat64, - ShaderImageGatherExtended = true, + ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, // ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true, - TessellationShader = true, + TessellationShader = supportedFeatures.TessellationShader, VertexPipelineStoresAndAtomics = true, RobustBufferAccess = useRobustBufferAccess }; void* pExtendedFeatures = null; - var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() - { - SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, - PNext = pExtendedFeatures, - TransformFeedback = true - }; + PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; - pExtendedFeatures = &featuresTransformFeedback; + if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + { + featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + { + SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, + PNext = pExtendedFeatures, + TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback + }; + + pExtendedFeatures = &featuresTransformFeedback; + } + + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; if (supportedExtensions.Contains("VK_EXT_robustness2")) { - var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() { SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, PNext = pExtendedFeatures, @@ -465,7 +491,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceVulkan11Features, PNext = pExtendedFeatures, - ShaderDrawParameters = true + ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters }; pExtendedFeatures = &featuresVk11; @@ -526,8 +552,8 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; if (supportedExtensions.Contains("VK_EXT_custom_border_color") && - featuresCustomBorderColorSupported.CustomBorderColors && - featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat) + supportedFeaturesCustomBorderColor.CustomBorderColors && + supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) { featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() { diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index f1922efed..029ad4b96 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -590,7 +590,11 @@ namespace Ryujinx.Graphics.Vulkan IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec; + IsTBDR = IsMoltenVk || + Vendor == Vendor.Qualcomm || + Vendor == Vendor.ARM || + Vendor == Vendor.Broadcom || + Vendor == Vendor.ImgTec; GpuVendor = vendorName; GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index d37dd7e96..27ebb0de6 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Vulkan ImageSharingMode = SharingMode.Exclusive, ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, - CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, + CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha), PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), Clipped = true, OldSwapchain = oldSwapchain @@ -182,6 +182,22 @@ namespace Ryujinx.Graphics.Vulkan return availableFormats[0]; } + private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags) + { + if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr)) + { + return CompositeAlphaFlagsKHR.OpaqueBitKhr; + } + else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr)) + { + return CompositeAlphaFlagsKHR.PreMultipliedBitKhr; + } + else + { + return CompositeAlphaFlagsKHR.InheritBitKhr; + } + } + private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) { if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) @@ -192,10 +208,6 @@ namespace Ryujinx.Graphics.Vulkan { return PresentModeKHR.MailboxKhr; } - else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr)) - { - return PresentModeKHR.FifoKhr; - } else { return PresentModeKHR.FifoKhr; From 85faa9d8fa1f8d5b1a80f3506717186f7a7e26c9 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 13 Jan 2023 07:04:59 +0100 Subject: [PATCH 282/737] Revert "Relax Vulkan requirements (#4228)" (#4279) This reverts commit dca5b14493e730960ed5cd67906278ecea969b3a. --- Ryujinx.Graphics.Vulkan/BufferManager.cs | 22 +------ Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 3 +- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 33 ++-------- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 3 +- Ryujinx.Graphics.Vulkan/Vendor.cs | 3 - .../VulkanInitialization.cs | 64 ++++++------------- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 6 +- Ryujinx.Graphics.Vulkan/Window.cs | 22 ++----- 8 files changed, 36 insertions(+), 120 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index 9c50e6ff3..f32403712 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -14,12 +14,6 @@ namespace Ryujinx.Graphics.Vulkan MemoryPropertyFlags.HostCoherentBit | MemoryPropertyFlags.HostCachedBit; - // Some drivers don't expose a "HostCached" memory type, - // so we need those alternative flags for the allocation to succeed there. - private const MemoryPropertyFlags DefaultBufferMemoryAltFlags = - MemoryPropertyFlags.HostVisibleBit | - MemoryPropertyFlags.HostCoherentBit; - private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = MemoryPropertyFlags.DeviceLocalBit; @@ -100,21 +94,9 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); - MemoryPropertyFlags allocateFlags; - MemoryPropertyFlags allocateFlagsAlt; + var allocateFlags = deviceLocal ? DeviceLocalBufferMemoryFlags : DefaultBufferMemoryFlags; - if (deviceLocal) - { - allocateFlags = DeviceLocalBufferMemoryFlags; - allocateFlagsAlt = DeviceLocalBufferMemoryFlags; - } - else - { - allocateFlags = DefaultBufferMemoryFlags; - allocateFlagsAlt = DefaultBufferMemoryAltFlags; - } - - var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); + var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags); if (allocation.Memory.Handle == 0UL) { diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 84ac0b051..751ef5ebf 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan public int[] AttachmentIndices { get; } public int AttachmentsCount { get; } - public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1; + public int MaxColorAttachmentIndex { get; } public bool HasDepthStencil { get; } public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); @@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = new uint[count]; AttachmentFormats = new VkFormat[count]; AttachmentIndices = new int[count]; + MaxColorAttachmentIndex = colors.Length - 1; uint width = uint.MaxValue; uint height = uint.MaxValue; diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 83c0a3243..eea4e60b3 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -27,16 +27,7 @@ namespace Ryujinx.Graphics.Vulkan MemoryRequirements requirements, MemoryPropertyFlags flags = 0) { - return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); - } - - public MemoryAllocation AllocateDeviceMemory( - PhysicalDevice physicalDevice, - MemoryRequirements requirements, - MemoryPropertyFlags flags, - MemoryPropertyFlags alternativeFlags) - { - int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); + int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags); if (memoryTypeIndex < 0) { return default; @@ -65,35 +56,21 @@ namespace Ryujinx.Graphics.Vulkan return newBl.Allocate(size, alignment, map); } - private static int FindSuitableMemoryTypeIndex( - Vk api, - PhysicalDevice physicalDevice, - uint memoryTypeBits, - MemoryPropertyFlags flags, - MemoryPropertyFlags alternativeFlags) + private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, MemoryPropertyFlags flags) { - int bestCandidateIndex = -1; - api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); for (int i = 0; i < properties.MemoryTypeCount; i++) { var type = properties.MemoryTypes[i]; - if ((memoryTypeBits & (1 << i)) != 0) + if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) { - if (type.PropertyFlags.HasFlag(flags)) - { - return i; - } - else if (type.PropertyFlags.HasFlag(alternativeFlags)) - { - bestCandidateIndex = i; - } + return i; } } - return bestCandidateIndex; + return -1; } public void Dispose() diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c4a9f2df5..1c0c836bb 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1344,7 +1344,8 @@ namespace Ryujinx.Graphics.Vulkan var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); - for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) + int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex + (FramebufferParams.HasDepthStencil ? 1 : 0); + for (int i = FramebufferParams.AttachmentFormats.Length; i <= maxAttachmentIndex; i++) { dstAttachmentFormats[i] = 0; } diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/Ryujinx.Graphics.Vulkan/Vendor.cs index 5e0290c0a..087d6e9dc 100644 --- a/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Vulkan Intel, Nvidia, ARM, - Broadcom, Qualcomm, Apple, Unknown @@ -29,7 +28,6 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => Vendor.Apple, 0x10DE => Vendor.Nvidia, 0x13B5 => Vendor.ARM, - 0x14E4 => Vendor.Broadcom, 0x8086 => Vendor.Intel, 0x5143 => Vendor.Qualcomm, _ => Vendor.Unknown @@ -45,7 +43,6 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => "Apple", 0x10DE => "NVIDIA", 0x13B5 => "ARM", - 0x14E4 => "Broadcom", 0x1AE0 => "Google", 0x5143 => "Qualcomm", 0x8086 => "Intel", diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index d9dcc0b25..fe9462aa3 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -162,6 +162,7 @@ namespace Ryujinx.Graphics.Vulkan if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) { Logger.Error?.Print(LogClass.Gpu, msg); + //throw new Exception(msg); } else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) { @@ -378,34 +379,14 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceFeatures2 }; - PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new PhysicalDeviceVulkan11Features() + PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT() { - SType = StructureType.PhysicalDeviceVulkan11Features, - PNext = features2.PNext - }; - - features2.PNext = &supportedFeaturesVk11; - - PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() - { - SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, - PNext = features2.PNext + SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt }; if (supportedExtensions.Contains("VK_EXT_custom_border_color")) { - features2.PNext = &supportedFeaturesCustomBorderColor; - } - - PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() - { - SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, - PNext = features2.PNext - }; - - if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) - { - features2.PNext = &supportedFeaturesTransformFeedback; + features2.PNext = &featuresCustomBorderColorSupported; } PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() @@ -427,48 +408,41 @@ namespace Ryujinx.Graphics.Vulkan var features = new PhysicalDeviceFeatures() { DepthBiasClamp = true, - DepthClamp = supportedFeatures.DepthClamp, - DualSrcBlend = supportedFeatures.DualSrcBlend, + DepthClamp = true, + DualSrcBlend = true, FragmentStoresAndAtomics = true, GeometryShader = supportedFeatures.GeometryShader, ImageCubeArray = true, IndependentBlend = true, LogicOp = supportedFeatures.LogicOp, - MultiViewport = supportedFeatures.MultiViewport, + MultiViewport = true, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, SamplerAnisotropy = true, ShaderClipDistance = true, ShaderFloat64 = supportedFeatures.ShaderFloat64, - ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, + ShaderImageGatherExtended = true, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, // ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true, - TessellationShader = supportedFeatures.TessellationShader, + TessellationShader = true, VertexPipelineStoresAndAtomics = true, RobustBufferAccess = useRobustBufferAccess }; void* pExtendedFeatures = null; - PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; - - if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() { - featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() - { - SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, - PNext = pExtendedFeatures, - TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback - }; + SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, + PNext = pExtendedFeatures, + TransformFeedback = true + }; - pExtendedFeatures = &featuresTransformFeedback; - } - - PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; + pExtendedFeatures = &featuresTransformFeedback; if (supportedExtensions.Contains("VK_EXT_robustness2")) { - featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() { SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, PNext = pExtendedFeatures, @@ -491,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceVulkan11Features, PNext = pExtendedFeatures, - ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters + ShaderDrawParameters = true }; pExtendedFeatures = &featuresVk11; @@ -552,8 +526,8 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; if (supportedExtensions.Contains("VK_EXT_custom_border_color") && - supportedFeaturesCustomBorderColor.CustomBorderColors && - supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) + featuresCustomBorderColorSupported.CustomBorderColors && + featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat) { featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() { diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 029ad4b96..f1922efed 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -590,11 +590,7 @@ namespace Ryujinx.Graphics.Vulkan IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsTBDR = IsMoltenVk || - Vendor == Vendor.Qualcomm || - Vendor == Vendor.ARM || - Vendor == Vendor.Broadcom || - Vendor == Vendor.ImgTec; + IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec; GpuVendor = vendorName; GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 27ebb0de6..d37dd7e96 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Vulkan ImageSharingMode = SharingMode.Exclusive, ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, - CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha), + CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), Clipped = true, OldSwapchain = oldSwapchain @@ -182,22 +182,6 @@ namespace Ryujinx.Graphics.Vulkan return availableFormats[0]; } - private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags) - { - if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr)) - { - return CompositeAlphaFlagsKHR.OpaqueBitKhr; - } - else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr)) - { - return CompositeAlphaFlagsKHR.PreMultipliedBitKhr; - } - else - { - return CompositeAlphaFlagsKHR.InheritBitKhr; - } - } - private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) { if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) @@ -208,6 +192,10 @@ namespace Ryujinx.Graphics.Vulkan { return PresentModeKHR.MailboxKhr; } + else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr)) + { + return PresentModeKHR.FifoKhr; + } else { return PresentModeKHR.FifoKhr; From 08ab47c6c0cbdb56bc1ab17be4323a35b2163fa9 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 13 Jan 2023 07:56:41 +0100 Subject: [PATCH 283/737] Update Program.cs --- Ryujinx.Ava/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 46e135a9b..e64b6921e 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -28,7 +28,7 @@ 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 bool PreviewerDetached { get; private set; } + public static bool PreviewerDetached { get; private set; } [LibraryImport("user32.dll", SetLastError = true)] public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); @@ -276,4 +276,4 @@ namespace Ryujinx.Ava Logger.Shutdown(); } } -} \ No newline at end of file +} From 070136b3f7a000e092eef46780091be29c0bc0bf Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 13 Jan 2023 23:46:45 -0300 Subject: [PATCH 284/737] Fix texture modified on CPU from GPU thread after being modified on GPU not being updated (#4284) --- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index cd17564a0..a6e0616cc 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1431,10 +1431,10 @@ namespace Ryujinx.Graphics.Gpu.Image return; } + handle.Sync(_context); + _context.Renderer.BackgroundContextAction(() => { - handle.Sync(_context); - Storage.SignalModifiedDirty(); lock (handle.Overlaps) From cd3a15aea514234153d0afcaaed0009086492bd9 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 14 Jan 2023 00:16:06 -0300 Subject: [PATCH 285/737] Fix NRE when MemoryUnmappedHandler is called for a destroyed channel (#4285) --- Ryujinx.Graphics.Gpu/GpuChannel.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs index fe8587629..43fa8484b 100644 --- a/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs @@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Gpu // Since the memory manager changed, make sure we will get pools from addresses of the new memory manager. TextureManager.ReloadPools(); - MemoryManager.Physical.BufferCache.QueuePrune(); + memoryManager.Physical.BufferCache.QueuePrune(); } /// @@ -84,7 +84,9 @@ namespace Ryujinx.Graphics.Gpu private void MemoryUnmappedHandler(object sender, UnmapEventArgs e) { TextureManager.ReloadPools(); - MemoryManager.Physical.BufferCache.QueuePrune(); + + var memoryManager = Volatile.Read(ref _memoryManager); + memoryManager?.Physical.BufferCache.QueuePrune(); } /// From 93df366b2c1ddb073410b8ce95c8225f52a49f33 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 14 Jan 2023 11:23:57 -0300 Subject: [PATCH 286/737] Fix texture flush from CPU WaitSync regression on OpenGL (#4289) --- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 13 +++++++- .../Image/TextureGroupHandle.cs | 31 +++++++------------ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index a6e0616cc..c167dc0d3 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1431,10 +1431,21 @@ namespace Ryujinx.Graphics.Gpu.Image return; } - handle.Sync(_context); + bool isGpuThread = _context.IsGpuThread(); + + if (isGpuThread) + { + // No need to wait if we're on the GPU thread, we can just clear the modified flag immediately. + handle.Modified = false; + } _context.Renderer.BackgroundContextAction(() => { + if (!isGpuThread) + { + handle.Sync(_context); + } + Storage.SignalModifiedDirty(); lock (handle.Overlaps) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index 34b59cffe..1b83cb558 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// A tracking handle for a texture group, which represents a range of views in a storage texture. /// Retains a list of overlapping texture views, a modified flag, and tracking for each /// CPU VA range that the views cover. - /// Also tracks copy dependencies for the handle - references to other handles that must be kept + /// Also tracks copy dependencies for the handle - references to other handles that must be kept /// in sync with this one before use. /// class TextureGroupHandle : IDisposable @@ -232,32 +232,23 @@ namespace Ryujinx.Graphics.Gpu.Image /// The GPU context used to wait for sync public void Sync(GpuContext context) { - bool needsSync = !context.IsGpuThread(); + ulong registeredSync = _registeredSync; + long diff = (long)(context.SyncNumber - registeredSync); - if (needsSync) + if (diff > 0) { - ulong registeredSync = _registeredSync; - long diff = (long)(context.SyncNumber - registeredSync); + context.Renderer.WaitSync(registeredSync); - if (diff > 0) + if ((long)(_modifiedSync - registeredSync) > 0) { - context.Renderer.WaitSync(registeredSync); - - if ((long)(_modifiedSync - registeredSync) > 0) - { - // Flush the data in a previous state. Do not remove the modified flag - it will be removed to ignore following writes. - return; - } - - Modified = false; + // Flush the data in a previous state. Do not remove the modified flag - it will be removed to ignore following writes. + return; } - - // If the difference is <= 0, no data is not ready yet. Flush any data we can without waiting or removing modified flag. - } - else - { + Modified = false; } + + // If the difference is <= 0, no data is not ready yet. Flush any data we can without waiting or removing modified flag. } /// From b402b4e7f6463c42ac5afcfaee45592e75851dc3 Mon Sep 17 00:00:00 2001 From: gnisman Date: Sat, 14 Jan 2023 20:37:04 +0200 Subject: [PATCH 287/737] Change GetPageSize to use Environment.SystemPageSize (#4291) * Change GetPageSize to use Environment.SystemPageSize * Fix PR comment --- Ryujinx.Memory/MemoryBlock.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index e1f19c27a..2df7ea9bd 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -435,12 +435,7 @@ namespace Ryujinx.Memory public static ulong GetPageSize() { - if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) - { - return 1UL << 14; - } - - return 1UL << 12; + return (ulong)Environment.SystemPageSize; } private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); From 8071c8c8c044ee56bc7578a4ba3178d2d03733db Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sun, 15 Jan 2023 01:05:44 +0100 Subject: [PATCH 288/737] Ava UI: Fixes "Hide Cursor on Idle" for Windows (#4266) * Ava: Fixes "Hide Cursor on Idle" for Windows * Add check in MouseDriver and reduce the time of idling * Fix linux error * Change idle time everywhere for consistencies --- Ryujinx.Ava/AppHost.cs | 128 +++++++++++-------- Ryujinx.Ava/Input/AvaloniaMouseDriver.cs | 32 ++++- Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs | 7 +- Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs | 16 +++ Ryujinx.Headless.SDL2/SDL2MouseDriver.cs | 2 +- Ryujinx/Ui/RendererWidgetBase.cs | 2 +- 6 files changed, 125 insertions(+), 62 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 65f84c494..5c4f5bd8b 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -44,8 +44,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; +using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; using Image = SixLabors.ImageSharp.Image; using InputManager = Ryujinx.Input.HLE.InputManager; using Key = Ryujinx.Input.Key; @@ -58,12 +60,14 @@ namespace Ryujinx.Ava { internal class AppHost { - private const int CursorHideIdleTime = 8; // Hide Cursor seconds. + private const int CursorHideIdleTime = 5; // Hide Cursor seconds. private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const int TargetFps = 60; private const float VolumeDelta = 0.05f; private static readonly Cursor InvisibleCursor = new(StandardCursorType.None); + private readonly IntPtr InvisibleCursorWin; + private readonly IntPtr DefaultCursorWin; private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; @@ -81,7 +85,6 @@ namespace Ryujinx.Ava private float _newVolume; private KeyboardHotkeyState _prevHotkeyState; - private bool _hideCursorOnIdle; private long _lastCursorMoveTime; private bool _isCursorInRenderer; @@ -131,7 +134,6 @@ namespace Ryujinx.Ava _accountManager = accountManager; _userChannelPersistence = userChannelPersistence; _renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" }; - _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; _lastCursorMoveTime = Stopwatch.GetTimestamp(); _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; _topLevel = topLevel; @@ -159,9 +161,14 @@ namespace Ryujinx.Ava ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorState_Changed; - _topLevel.PointerLeave += TopLevel_PointerLeave; _topLevel.PointerMoved += TopLevel_PointerMoved; + if (OperatingSystem.IsWindows()) + { + InvisibleCursorWin = CreateEmptyCursor(); + DefaultCursorWin = CreateArrowCursor(); + } + ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; @@ -172,20 +179,47 @@ namespace Ryujinx.Ava private void TopLevel_PointerMoved(object sender, PointerEventArgs e) { - if (sender is Control visual) + if (sender is MainWindow window) { _lastCursorMoveTime = Stopwatch.GetTimestamp(); - var point = e.GetCurrentPoint(visual).Position; + if ((Renderer.Content as EmbeddedWindow).TransformedBounds != null) + { + var point = e.GetCurrentPoint(window).Position; + var bounds = (Renderer.Content as EmbeddedWindow).TransformedBounds.Value.Clip; - _isCursorInRenderer = Equals(visual.InputHitTest(point), Renderer); + _isCursorInRenderer = point.X >= bounds.X && + point.X <= bounds.Width + bounds.X && + point.Y >= bounds.Y && + point.Y <= bounds.Height + bounds.Y; + } } } - private void TopLevel_PointerLeave(object sender, PointerEventArgs e) + private void ShowCursor() { - _isCursorInRenderer = false; - _viewModel.Cursor = Cursor.Default; + Dispatcher.UIThread.Post(() => + { + _viewModel.Cursor = Cursor.Default; + + if (OperatingSystem.IsWindows()) + { + SetCursor(DefaultCursorWin); + } + }); + } + + private void HideCursor() + { + Dispatcher.UIThread.Post(() => + { + _viewModel.Cursor = InvisibleCursor; + + if (OperatingSystem.IsWindows()) + { + SetCursor(InvisibleCursorWin); + } + }); } private void SetRendererWindowSize(Size size) @@ -380,7 +414,6 @@ namespace Ryujinx.Ava ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; - _topLevel.PointerLeave -= TopLevel_PointerLeave; _topLevel.PointerMoved -= TopLevel_PointerMoved; _gpuCancellationTokenSource.Cancel(); @@ -406,19 +439,10 @@ namespace Ryujinx.Ava private void HideCursorState_Changed(object sender, ReactiveEventArgs state) { - Dispatcher.UIThread.InvokeAsync(delegate + if (state.NewValue) { - _hideCursorOnIdle = state.NewValue; - - if (_hideCursorOnIdle) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - else - { - _viewModel.Cursor = Cursor.Default; - } - }); + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + } } public async Task LoadGuestApplication() @@ -860,29 +884,6 @@ namespace Ryujinx.Ava } } - private void HandleScreenState() - { - if (ConfigurationState.Instance.Hid.EnableMouse) - { - Dispatcher.UIThread.Post(() => - { - _viewModel.Cursor = _isCursorInRenderer ? InvisibleCursor : Cursor.Default; - }); - } - else - { - if (_hideCursorOnIdle) - { - long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; - - Dispatcher.UIThread.Post(() => - { - _viewModel.Cursor = cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency ? InvisibleCursor : Cursor.Default; - }); - } - } - } - private bool UpdateFrame() { if (!_isActive) @@ -890,23 +891,44 @@ namespace Ryujinx.Ava return false; } + NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); + if (_viewModel.IsActive) { + if (ConfigurationState.Instance.Hid.EnableMouse) + { + if (_isCursorInRenderer) + { + HideCursor(); + } + else + { + ShowCursor(); + } + } + else + { + if (ConfigurationState.Instance.HideCursorOnIdle) + { + if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) + { + HideCursor(); + } + else + { + ShowCursor(); + } + } + } + Dispatcher.UIThread.Post(() => { - HandleScreenState(); - if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) { Device.Application.DiskCacheLoadState?.Cancel(); } }); - } - NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - - if (_viewModel.IsActive) - { KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); if (currentHotkeyState != _prevHotkeyState) diff --git a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs index b0b6cdf05..b3e1a21a1 100644 --- a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs +++ b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; +using FluentAvalonia.Core; using Ryujinx.Input; using System; using System.Numerics; @@ -69,12 +70,22 @@ namespace Ryujinx.Ava.Input private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args) { - PressedButtons[(int)args.InitialPressMouseButton - 1] = false; + int button = (int)args.InitialPressMouseButton - 1; + + if (PressedButtons.Count() >= button) + { + PressedButtons[button] = false; + } } private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args) { - PressedButtons[(int)args.GetCurrentPoint(_widget).Properties.PointerUpdateKind] = true; + int button = (int)args.GetCurrentPoint(_widget).Properties.PointerUpdateKind; + + if (PressedButtons.Count() >= button) + { + PressedButtons[button] = true; + } } private void Parent_PointerMovedEvent(object o, PointerEventArgs args) @@ -86,12 +97,18 @@ namespace Ryujinx.Ava.Input public void SetMousePressed(MouseButton button) { - PressedButtons[(int)button] = true; + if (PressedButtons.Count() >= (int)button) + { + PressedButtons[(int)button] = true; + } } public void SetMouseReleased(MouseButton button) { - PressedButtons[(int)button] = false; + if (PressedButtons.Count() >= (int)button) + { + PressedButtons[(int)button] = false; + } } public void SetPosition(double x, double y) @@ -101,7 +118,12 @@ namespace Ryujinx.Ava.Input public bool IsButtonPressed(MouseButton button) { - return PressedButtons[(int)button]; + if (PressedButtons.Count() >= (int)button) + { + return PressedButtons[(int)button]; + } + + return false; } public Size GetClientSize() diff --git a/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs index 8247a89b5..67ab80aa7 100644 --- a/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs +++ b/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs @@ -34,6 +34,8 @@ namespace Ryujinx.Ava.UI.Helpers { WindowHandle = IntPtr.Zero; X11Display = IntPtr.Zero; + NsView = IntPtr.Zero; + MetalLayer = IntPtr.Zero; } public EmbeddedWindow() @@ -42,7 +44,7 @@ namespace Ryujinx.Ava.UI.Helpers stateObserverable.Subscribe(StateChanged); - this.Initialized += NativeEmbeddedWindow_Initialized; + Initialized += NativeEmbeddedWindow_Initialized; } public virtual void OnWindowCreated() { } @@ -127,7 +129,7 @@ namespace Ryujinx.Ava.UI.Helpers lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), style = ClassStyles.CS_OWNDC, lpszClassName = Marshal.StringToHGlobalUni(_className), - hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW) + hCursor = CreateArrowCursor() }; var atom = RegisterClassEx(ref wndClassEx); @@ -198,6 +200,7 @@ namespace Ryujinx.Ava.UI.Helpers KeyModifiers.None)); break; } + return DefWindowProc(hWnd, msg, wParam, lParam); } diff --git a/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs b/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs index 1e6e3c3bd..03d3a49f3 100644 --- a/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs +++ b/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs @@ -70,6 +70,22 @@ namespace Ryujinx.Ava.UI.Helpers } } + public static IntPtr CreateEmptyCursor() + { + return CreateCursor(IntPtr.Zero, 0, 0, 1, 1, new byte[] { 0xFF }, new byte[] { 0x00 }); + } + + public static IntPtr CreateArrowCursor() + { + return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW); + } + + [LibraryImport("user32.dll")] + public static partial IntPtr SetCursor(IntPtr handle); + + [LibraryImport("user32.dll")] + public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvANDPlane, byte[] pvXORPlane); + [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")] public static partial ushort RegisterClassEx(ref WNDCLASSEX param); diff --git a/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs b/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs index bdf428cc4..8c3412ff9 100644 --- a/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs +++ b/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Headless.SDL2 { class SDL2MouseDriver : IGamepadDriver { - private const int CursorHideIdleTime = 8; // seconds + private const int CursorHideIdleTime = 5; // seconds private bool _isDisposed; private HideCursor _hideCursor; diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 8db023bec..4bf2a70ff 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -68,7 +68,7 @@ namespace Ryujinx.Ui private readonly CancellationTokenSource _gpuCancellationTokenSource; // Hide Cursor - const int CursorHideIdleTime = 8; // seconds + const int CursorHideIdleTime = 5; // seconds private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor); private long _lastCursorMoveTime; private bool _hideCursorOnIdle; From 41bba5310a5324f54fa5c0200aff2bf697ced000 Mon Sep 17 00:00:00 2001 From: merry Date: Sun, 15 Jan 2023 04:20:49 +0000 Subject: [PATCH 289/737] Audren: Implement polyphase upsampler (#4256) * Audren: Implement polyphase upsampler * prefer shifting to modulo * prefer MathF * fix nits * rm ResampleForUpsampler * oop * Array20 * nits --- .../Renderer/Dsp/Command/UpsampleCommand.cs | 12 +- Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs | 47 ----- Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs | 175 ++++++++++++++++++ .../Server/Upsampler/UpsamplerBufferState.cs | 14 ++ .../Server/Upsampler/UpsamplerState.cs | 5 + 5 files changed, 201 insertions(+), 52 deletions(-) create mode 100644 Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs create mode 100644 Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs index 1617a6421..0870d59ce 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs @@ -40,6 +40,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command info.InputBufferIndices[i] = (ushort)(bufferOffset + inputBufferOffset[i]); } + if (info.BufferStates?.Length != (int)inputCount) + { + // Keep state if possible. + info.BufferStates = new UpsamplerBufferState[(int)inputCount]; + } + UpsamplerInfo = info; } @@ -50,8 +56,6 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public void Process(CommandList context) { - float ratio = (float)InputSampleRate / Constants.TargetSampleRate; - uint bufferCount = Math.Min(BufferCount, UpsamplerInfo.SourceSampleCount); for (int i = 0; i < bufferCount; i++) @@ -59,9 +63,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Span inputBuffer = context.GetBuffer(UpsamplerInfo.InputBufferIndices[i]); Span outputBuffer = GetBuffer(UpsamplerInfo.InputBufferIndices[i], (int)UpsamplerInfo.SampleCount); - float fraction = 0.0f; - - ResamplerHelper.ResampleForUpsampler(outputBuffer, inputBuffer, ratio, ref fraction, (int)(InputSampleCount / ratio)); + UpsamplerHelper.Upsample(outputBuffer, inputBuffer, (int)UpsamplerInfo.SampleCount, (int)InputSampleCount, ref UpsamplerInfo.BufferStates[i]); } } } diff --git a/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs b/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs index 4de2e078a..b46a33fe0 100644 --- a/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs +++ b/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs @@ -579,52 +579,5 @@ namespace Ryujinx.Audio.Renderer.Dsp fraction -= (int)fraction; } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ResampleForUpsampler(Span outputBuffer, ReadOnlySpan inputBuffer, float ratio, ref float fraction, int sampleCount) - { - // Currently a simple cubic interpolation, assuming duplicated values at edges. - // TODO: Discover and use algorithm that the switch uses. - - int inputBufferIndex = 0; - int maxIndex = inputBuffer.Length - 1; - int cubicEnd = inputBuffer.Length - 3; - - for (int i = 0; i < sampleCount; i++) - { - float s0, s1, s2, s3; - - s1 = inputBuffer[inputBufferIndex]; - - if (inputBufferIndex == 0 || inputBufferIndex > cubicEnd) - { - // Clamp interplation values at the ends of the input buffer. - s0 = inputBuffer[Math.Max(0, inputBufferIndex - 1)]; - s2 = inputBuffer[Math.Min(maxIndex, inputBufferIndex + 1)]; - s3 = inputBuffer[Math.Min(maxIndex, inputBufferIndex + 2)]; - } - else - { - s0 = inputBuffer[inputBufferIndex - 1]; - s2 = inputBuffer[inputBufferIndex + 1]; - s3 = inputBuffer[inputBufferIndex + 2]; - } - - float a = s3 - s2 - s0 + s1; - float b = s0 - s1 - a; - float c = s2 - s0; - float d = s1; - - float f2 = fraction * fraction; - float f3 = f2 * fraction; - - outputBuffer[i] = a * f3 + b * f2 + c * fraction + d; - - fraction += ratio; - inputBufferIndex += (int)MathF.Truncate(fraction); - - fraction -= (int)fraction; - } - } } } \ No newline at end of file diff --git a/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs b/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs new file mode 100644 index 000000000..847acec2e --- /dev/null +++ b/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs @@ -0,0 +1,175 @@ +using Ryujinx.Audio.Renderer.Server.Upsampler; +using Ryujinx.Common.Memory; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Audio.Renderer.Dsp +{ + public class UpsamplerHelper + { + private const int HistoryLength = UpsamplerBufferState.HistoryLength; + private const int FilterBankLength = 20; + // Bank0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + private const int Bank0CenterIndex = 9; + private static readonly Array20 Bank1 = PrecomputeFilterBank(1.0f / 6.0f); + private static readonly Array20 Bank2 = PrecomputeFilterBank(2.0f / 6.0f); + private static readonly Array20 Bank3 = PrecomputeFilterBank(3.0f / 6.0f); + private static readonly Array20 Bank4 = PrecomputeFilterBank(4.0f / 6.0f); + private static readonly Array20 Bank5 = PrecomputeFilterBank(5.0f / 6.0f); + + private static Array20 PrecomputeFilterBank(float offset) + { + float Sinc(float x) + { + if (x == 0) + { + return 1.0f; + } + return (MathF.Sin(MathF.PI * x) / (MathF.PI * x)); + } + + float BlackmanWindow(float x) + { + const float a = 0.18f; + const float a0 = 0.5f - 0.5f * a; + const float a1 = -0.5f; + const float a2 = 0.5f * a; + return a0 + a1 * MathF.Cos(2 * MathF.PI * x) + a2 * MathF.Cos(4 * MathF.PI * x); + } + + Array20 result = new Array20(); + + for (int i = 0; i < FilterBankLength; i++) + { + float x = (Bank0CenterIndex - i) + offset; + result[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f); + } + + return result; + } + + // Polyphase upsampling algorithm + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Upsample(Span outputBuffer, ReadOnlySpan inputBuffer, int outputSampleCount, int inputSampleCount, ref UpsamplerBufferState state) + { + if (!state.Initialized) + { + state.Scale = inputSampleCount switch + { + 40 => 6.0f, + 80 => 3.0f, + 160 => 1.5f, + _ => throw new ArgumentOutOfRangeException() + }; + state.Initialized = true; + } + + if (outputSampleCount == 0) + { + return; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + float DoFilterBank(ref UpsamplerBufferState state, in Array20 bank) + { + float result = 0.0f; + + Debug.Assert(state.History.Length == HistoryLength); + Debug.Assert(bank.Length == FilterBankLength); + for (int j = 0; j < FilterBankLength; j++) + { + result += bank[j] * state.History[j]; + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void NextInput(ref UpsamplerBufferState state, float input) + { + state.History.AsSpan().Slice(1).CopyTo(state.History.AsSpan()); + state.History[HistoryLength - 1] = input; + } + + int inputBufferIndex = 0; + + switch (state.Scale) + { + case 6.0f: + for (int i = 0; i < outputSampleCount; i++) + { + switch (state.Phase) + { + case 0: + NextInput(ref state, inputBuffer[inputBufferIndex++]); + outputBuffer[i] = state.History[Bank0CenterIndex]; + break; + case 1: + outputBuffer[i] = DoFilterBank(ref state, Bank1); + break; + case 2: + outputBuffer[i] = DoFilterBank(ref state, Bank2); + break; + case 3: + outputBuffer[i] = DoFilterBank(ref state, Bank3); + break; + case 4: + outputBuffer[i] = DoFilterBank(ref state, Bank4); + break; + case 5: + outputBuffer[i] = DoFilterBank(ref state, Bank5); + break; + } + + state.Phase = (state.Phase + 1) % 6; + } + break; + case 3.0f: + for (int i = 0; i < outputSampleCount; i++) + { + switch (state.Phase) + { + case 0: + NextInput(ref state, inputBuffer[inputBufferIndex++]); + outputBuffer[i] = state.History[Bank0CenterIndex]; + break; + case 1: + outputBuffer[i] = DoFilterBank(ref state, Bank2); + break; + case 2: + outputBuffer[i] = DoFilterBank(ref state, Bank4); + break; + } + + state.Phase = (state.Phase + 1) % 3; + } + break; + case 1.5f: + // Upsample by 3 then decimate by 2. + for (int i = 0; i < outputSampleCount; i++) + { + switch (state.Phase) + { + case 0: + NextInput(ref state, inputBuffer[inputBufferIndex++]); + outputBuffer[i] = state.History[Bank0CenterIndex]; + break; + case 1: + outputBuffer[i] = DoFilterBank(ref state, Bank4); + break; + case 2: + NextInput(ref state, inputBuffer[inputBufferIndex++]); + outputBuffer[i] = DoFilterBank(ref state, Bank2); + break; + } + + state.Phase = (state.Phase + 1) % 3; + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs new file mode 100644 index 000000000..a45fa8e5b --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs @@ -0,0 +1,14 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.Audio.Renderer.Server.Upsampler +{ + public struct UpsamplerBufferState + { + public const int HistoryLength = 20; + + public float Scale; + public Array20 History; + public bool Initialized; + public int Phase; + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs index 065e4838c..e508f35b4 100644 --- a/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs +++ b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs @@ -37,6 +37,11 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler /// public ushort[] InputBufferIndices; + /// + /// State of each input buffer index kept across invocations of the upsampler. + /// + public UpsamplerBufferState[] BufferStates; + /// /// Create a new . /// From 719dc97bbd321e98083f47267feb01db769e5fa6 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Sun, 15 Jan 2023 06:11:52 -0500 Subject: [PATCH 290/737] Ava UI: `TitleUpdateWindow` Refactor (#4276) * Start Refactor * Dialogue opens * Changes * Switch to ListBox * Fix bugs and stuff * Fix spacing * Implement OpenLocation * Change icon * Color * Color * Remove background * Make no update the same height * Fix height and smooth scroll * Height * Fix update selection * Make window smaller * Add back remove all button * Make selection more obvious * Hide selection bar on SaveManager * Fix autoscroll * Fix no update not staying selected * Better file opener * Fix * Revert that * Update Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs Co-authored-by: Ac_K * Log warning * Update Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs Co-authored-by: Ac_K Co-authored-by: Ac_K --- Ryujinx.Ava/Assets/Locales/en_US.json | 4 +- Ryujinx.Ava/UI/Models/TitleUpdateModel.cs | 12 +- .../UI/ViewModels/MainWindowViewModel.cs | 8 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 226 ++++++++++++++ .../UI/Views/User/UserSaveManagerView.axaml | 4 + .../UI/Windows/TitleUpdateWindow.axaml | 172 ++++++----- .../UI/Windows/TitleUpdateWindow.axaml.cs | 275 ++++-------------- Ryujinx.Ui.Common/Helper/OpenHelper.cs | 68 ++++- 8 files changed, 455 insertions(+), 314 deletions(-) create mode 100644 Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 24f44deeb..ba5af264b 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -524,7 +524,7 @@ "UserErrorUndefinedDescription": "An undefined error occured! This shouldn't happen, please contact a dev!", "OpenSetupGuideMessage": "Open the Setup Guide", "NoUpdate": "No Update", - "TitleUpdateVersionLabel": "Version {0} - {1}", + "TitleUpdateVersionLabel": "Version {0}", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Confirmation", "FileDialogAllTypes": "All types", @@ -585,7 +585,7 @@ "UserProfilesSetProfileImage": "Set Profile Image", "UserProfileEmptyNameError": "Name is required", "UserProfileNoImageError": "Profile image must be set", - "GameUpdateWindowHeading": "{0} Update(s) available for {1} ({2})", + "GameUpdateWindowHeading": "Manage Updates for {0} ({1})", "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:", "UserProfilesName": "Name:", diff --git a/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs b/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs index c3ba62301..c57b3a26a 100644 --- a/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs +++ b/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs @@ -3,23 +3,17 @@ using Ryujinx.Ava.Common.Locale; namespace Ryujinx.Ava.UI.Models { - internal class TitleUpdateModel + public class TitleUpdateModel { - public bool IsEnabled { get; set; } - public bool IsNoUpdate { get; } public ApplicationControlProperty Control { get; } public string Path { get; } - public string Label => IsNoUpdate - ? LocaleManager.Instance[LocaleKeys.NoUpdate] - : string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString(), - Path); + public string Label => string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString()); - public TitleUpdateModel(ApplicationControlProperty control, string path, bool isNoUpdate = false) + public TitleUpdateModel(ApplicationControlProperty control, string path) { Control = control; Path = path; - IsNoUpdate = isNoUpdate; } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f86cda21a..295402155 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1601,13 +1601,9 @@ namespace Ryujinx.Ava.UI.ViewModels public async void OpenTitleUpdateManager() { - ApplicationData selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - await new TitleUpdateWindow(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(desktop.MainWindow); - } + await TitleUpdateWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName); } } diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs new file mode 100644 index 000000000..131ebd25b --- /dev/null +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -0,0 +1,226 @@ +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Threading; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Ns; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SpanHelpers = LibHac.Common.SpanHelpers; +using Path = System.IO.Path; + +namespace Ryujinx.Ava.UI.ViewModels; + +public class TitleUpdateViewModel : BaseModel +{ + public TitleUpdateMetadata _titleUpdateWindowData; + public readonly string _titleUpdateJsonPath; + private VirtualFileSystem _virtualFileSystem { get; } + private ulong _titleId { get; } + private string _titleName { get; } + + private AvaloniaList _titleUpdates = new(); + private AvaloniaList _views = new(); + private object _selectedUpdate; + + public AvaloniaList TitleUpdates + { + get => _titleUpdates; + set + { + _titleUpdates = value; + OnPropertyChanged(); + } + } + + public AvaloniaList Views + { + get => _views; + set + { + _views = value; + OnPropertyChanged(); + } + } + + public object SelectedUpdate + { + get => _selectedUpdate; + set + { + _selectedUpdate = value; + OnPropertyChanged(); + } + } + + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + _virtualFileSystem = virtualFileSystem; + + _titleId = titleId; + _titleName = titleName; + + _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); + + try + { + _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath); + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); + + _titleUpdateWindowData = new TitleUpdateMetadata + { + Selected = "", + Paths = new List() + }; + } + + LoadUpdates(); + } + + private void LoadUpdates() + { + foreach (string path in _titleUpdateWindowData.Paths) + { + AddUpdate(path); + } + + TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); + + SelectedUpdate = selected; + + SortUpdates(); + } + + public void SortUpdates() + { + var list = TitleUpdates.ToList(); + + list.Sort((first, second) => + { + if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) + { + return -1; + } + else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) + { + return 1; + } + + return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; + }); + + Views.Clear(); + Views.Add(new BaseModel()); + Views.AddRange(list); + + if (SelectedUpdate == null) + { + SelectedUpdate = Views[0]; + } + else if (!TitleUpdates.Contains(SelectedUpdate)) + { + if (Views.Count > 1) + { + SelectedUpdate = Views[1]; + } + else + { + SelectedUpdate = Views[0]; + } + } + } + + private void AddUpdate(string path) + { + if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) + { + using FileStream file = new(path, FileMode.Open, FileAccess.Read); + + try + { + (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + + if (controlNca != null && patchNca != null) + { + ApplicationControlProperty controlData = new(); + + using UniqueRef nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + TitleUpdates.Add(new TitleUpdateModel(controlData, path)); + } + else + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); + }); + } + } + catch (Exception ex) + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path)); + }); + } + } + } + + public void RemoveUpdate(TitleUpdateModel update) + { + TitleUpdates.Remove(update); + + SortUpdates(); + } + + public async void Add() + { + OpenFileDialog dialog = new() + { + Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], + AllowMultiple = true + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = "NSP", + Extensions = { "nsp" } + }); + + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null) + { + foreach (string file in files) + { + AddUpdate(file); + } + } + } + + SortUpdates(); + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml index b4f2e1014..ec931dd96 100644 --- a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml +++ b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml @@ -107,6 +107,7 @@ VerticalAlignment="Stretch"> @@ -116,6 +117,9 @@ + diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml index 5a69be9b0..e98580386 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml @@ -1,115 +1,135 @@ - - + - - - - - - - - - - - + - - - + + - - + + - \ No newline at end of file + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 848c5587f..9d8b9a7b9 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -1,271 +1,116 @@ -using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Threading; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Ns; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; +using Avalonia.Interactivity; +using Avalonia.Styling; +using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; -using Ryujinx.Common.Configuration; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using System; -using System.Collections.Generic; +using Ryujinx.Ui.Common.Helper; using System.IO; -using System.Linq; using System.Text; -using Path = System.IO.Path; -using SpanHelpers = LibHac.Common.SpanHelpers; +using System.Threading.Tasks; +using Button = Avalonia.Controls.Button; namespace Ryujinx.Ava.UI.Windows { - public partial class TitleUpdateWindow : StyleableWindow + public partial class TitleUpdateWindow : UserControl { - private readonly string _titleUpdateJsonPath; - private TitleUpdateMetadata _titleUpdateWindowData; - - private VirtualFileSystem _virtualFileSystem { get; } - private AvaloniaList _titleUpdates { get; set; } - - private ulong _titleId { get; } - private string _titleName { get; } + public TitleUpdateViewModel ViewModel; public TitleUpdateWindow() { DataContext = this; InitializeComponent(); - - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.UpdateWindowTitle]} - {_titleName} ({_titleId:X16})"; } public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - _virtualFileSystem = virtualFileSystem; - _titleUpdates = new AvaloniaList(); - - _titleId = titleId; - _titleName = titleName; - - _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); - - try - { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath); - } - catch - { - _titleUpdateWindowData = new TitleUpdateMetadata - { - Selected = "", - Paths = new List() - }; - } - - DataContext = this; + DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId, titleName); InitializeComponent(); - - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.UpdateWindowTitle]} - {_titleName} ({_titleId:X16})"; - - LoadUpdates(); - PrintHeading(); } - private void PrintHeading() + public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], _titleUpdates.Count - 1, _titleName, _titleId.ToString("X16")); - } - - private void LoadUpdates() - { - _titleUpdates.Add(new TitleUpdateModel(default, string.Empty, true)); - - foreach (string path in _titleUpdateWindowData.Paths) + ContentDialog contentDialog = new() { - AddUpdate(path); - } - - if (_titleUpdateWindowData.Selected == "") - { - _titleUpdates[0].IsEnabled = true; - } - else - { - TitleUpdateModel selected = _titleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected); - List enabled = _titleUpdates.Where(x => x.IsEnabled).ToList(); - - foreach (TitleUpdateModel update in enabled) - { - update.IsEnabled = false; - } - - if (selected != null) - { - selected.IsEnabled = true; - } - } - - SortUpdates(); - } - - private void AddUpdate(string path) - { - if (File.Exists(path) && !_titleUpdates.Any(x => x.Path == path)) - { - using FileStream file = new(path, FileMode.Open, FileAccess.Read); - - try - { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); - - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new(); - - using UniqueRef nacpFile = new(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - _titleUpdates.Add(new TitleUpdateModel(controlData, path)); - - foreach (var update in _titleUpdates) - { - update.IsEnabled = false; - } - - _titleUpdates.Last().IsEnabled = true; - } - else - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); - }); - } - } - catch (Exception ex) - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path)); - }); - } - } - } - - private void RemoveUpdates(bool removeSelectedOnly = false) - { - if (removeSelectedOnly) - { - _titleUpdates.RemoveAll(_titleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList()); - } - else - { - _titleUpdates.RemoveAll(_titleUpdates.Where(x => !x.IsNoUpdate).ToList()); - } - - _titleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true; - - SortUpdates(); - PrintHeading(); - } - - public void RemoveSelected() - { - RemoveUpdates(true); - } - - public void RemoveAll() - { - RemoveUpdates(); - } - - public async void Add() - { - OpenFileDialog dialog = new() - { - Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], - AllowMultiple = true + PrimaryButtonText = "", + SecondaryButtonText = "", + CloseButtonText = "", + Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName), + Title = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], titleName, titleId.ToString("X16")) }; - dialog.Filters.Add(new FileDialogFilter - { - Name = "NSP", - Extensions = { "nsp" } - }); + Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); + bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); - string[] files = await dialog.ShowAsync(this); + contentDialog.Styles.Add(bottomBorder); - if (files != null) - { - foreach (string file in files) - { - AddUpdate(file); - } - } - - SortUpdates(); - PrintHeading(); + await ContentDialogHelper.ShowAsync(contentDialog); } - private void SortUpdates() + private void Close(object sender, RoutedEventArgs e) { - var list = _titleUpdates.ToList(); - - list.Sort((first, second) => - { - if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) - { - return -1; - } - else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) - { - return 1; - } - - return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; - }); - - _titleUpdates.Clear(); - _titleUpdates.AddRange(list); + ((ContentDialog)Parent).Hide(); } - public void Save() + public void Save(object sender, RoutedEventArgs e) { - _titleUpdateWindowData.Paths.Clear(); + ViewModel._titleUpdateWindowData.Paths.Clear(); - _titleUpdateWindowData.Selected = ""; + ViewModel._titleUpdateWindowData.Selected = ""; - foreach (TitleUpdateModel update in _titleUpdates) + foreach (TitleUpdateModel update in ViewModel.TitleUpdates) { - _titleUpdateWindowData.Paths.Add(update.Path); + ViewModel._titleUpdateWindowData.Paths.Add(update.Path); - if (update.IsEnabled) + if (update == ViewModel.SelectedUpdate) { - _titleUpdateWindowData.Selected = update.Path; + ViewModel._titleUpdateWindowData.Selected = update.Path; } } - using (FileStream titleUpdateJsonStream = File.Create(_titleUpdateJsonPath, 4096, FileOptions.WriteThrough)) + using (FileStream titleUpdateJsonStream = File.Create(ViewModel._titleUpdateJsonPath, 4096, FileOptions.WriteThrough)) { - titleUpdateJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); + titleUpdateJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(ViewModel._titleUpdateWindowData, true))); } - if (Owner is MainWindow window) + if (VisualRoot is MainWindow window) { window.ViewModel.LoadApplications(); } - Close(); + ((ContentDialog)Parent).Hide(); + } + + private void OpenLocation(object sender, RoutedEventArgs e) + { + if (sender is Button button) + { + if (button.DataContext is TitleUpdateModel model) + { + OpenHelper.LocateFile(model.Path); + } + } + } + + private void RemoveUpdate(object sender, RoutedEventArgs e) + { + if (sender is Button button) + { + ViewModel.RemoveUpdate((TitleUpdateModel)button.DataContext); + } + } + + private void RemoveAll(object sender, RoutedEventArgs e) + { + ViewModel.TitleUpdates.Clear(); + + ViewModel.SortUpdates(); } } } \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Helper/OpenHelper.cs b/Ryujinx.Ui.Common/Helper/OpenHelper.cs index eaaa73924..355348921 100644 --- a/Ryujinx.Ui.Common/Helper/OpenHelper.cs +++ b/Ryujinx.Ui.Common/Helper/OpenHelper.cs @@ -1,19 +1,75 @@ using Ryujinx.Common.Logging; using System; using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; namespace Ryujinx.Ui.Common.Helper { - public static class OpenHelper + public static partial class OpenHelper { + [LibraryImport("shell32.dll", SetLastError = true)] + public static partial int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr apidl, uint dwFlags); + + [LibraryImport("shell32.dll", SetLastError = true)] + public static partial void ILFree(IntPtr pidlList); + + [LibraryImport("shell32.dll", SetLastError = true)] + public static partial IntPtr ILCreateFromPathW([MarshalAs(UnmanagedType.LPWStr)] string pszPath); + public static void OpenFolder(string path) { - Process.Start(new ProcessStartInfo + if (Directory.Exists(path)) { - FileName = path, - UseShellExecute = true, - Verb = "open" - }); + Process.Start(new ProcessStartInfo + { + FileName = path, + UseShellExecute = true, + Verb = "open" + }); + } + else + { + Logger.Notice.Print(LogClass.Application, $"Directory \"{path}\" doesn't exist!"); + } + } + + public static void LocateFile(string path) + { + if (File.Exists(path)) + { + if (OperatingSystem.IsWindows()) + { + IntPtr pidlList = ILCreateFromPathW(path); + if (pidlList != IntPtr.Zero) + { + try + { + Marshal.ThrowExceptionForHR(SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0)); + } + finally + { + ILFree(pidlList); + } + } + } + else if (OperatingSystem.IsMacOS()) + { + Process.Start("open", $"-R \"{path}\""); + } + else if (OperatingSystem.IsLinux()) + { + Process.Start("dbus-send", $"--session --print-reply --dest=org.freedesktop.FileManager1 --type=method_call /org/freedesktop/FileManager1 org.freedesktop.FileManager1.ShowItems array:string:\"file://{path}\" string:\"\""); + } + else + { + OpenFolder(Path.GetDirectoryName(path)); + } + } + else + { + Logger.Notice.Print(LogClass.Application, $"File \"{path}\" doesn't exist!"); + } } public static void OpenUrl(string url) From 139a93040741ec78a4ee13035eab1b1b705bdcf8 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Sun, 15 Jan 2023 16:16:24 -0500 Subject: [PATCH 291/737] Implement missing service calls in `pm` (#4210) * Implement `GetTitleId` Fixes #2516 * Null check + Proper result code * Better comment * Implement `GetApplicationProcessId` * Add TODOs * Update Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs Co-authored-by: Ac_K * Update Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs Co-authored-by: Ac_K * Remove new function from KernelStatic Co-authored-by: Ac_K --- Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 5 ++--- .../HOS/Services/Pm/IDebugMonitorInterface.cs | 18 +++++++++++++++ .../HOS/Services/Pm/IInformationInterface.cs | 22 ++++++++++++++++++- Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs | 17 ++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs index 18cf212a9..21d7d90c4 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -1,5 +1,4 @@ -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; @@ -71,4 +70,4 @@ namespace Ryujinx.HLE.HOS.Kernel return null; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs index c9c6354df..8d4934fab 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs @@ -10,6 +10,24 @@ namespace Ryujinx.HLE.HOS.Services.Pm { public IDebugMonitorInterface(ServiceCtx context) { } + [CommandHipc(4)] + // GetProgramId() -> sf::Out out_process_id + public ResultCode GetApplicationProcessId(ServiceCtx context) + { + // TODO: Not correct as it shouldn't be directly using kernel objects here + foreach (KProcess process in context.Device.System.KernelContext.Processes.Values) + { + if (process.IsApplication) + { + context.ResponseData.Write(process.Pid); + + return ResultCode.Success; + } + } + + return ResultCode.ProcessNotFound; + } + [CommandHipc(65000)] // AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::OutCopyHandle out_process_handle, sf::Out out_loc, sf::Out out_status public ResultCode GetProcessInfo(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs index 0be85c4f6..e3ce6d2af 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs @@ -1,8 +1,28 @@ -namespace Ryujinx.HLE.HOS.Services.Pm +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Kernel.Process; + +namespace Ryujinx.HLE.HOS.Services.Pm { [Service("pm:info")] class IInformationInterface : IpcService { public IInformationInterface(ServiceCtx context) { } + + [CommandHipc(0)] + // GetProgramId(os::ProcessId process_id) -> sf::Out out + public ResultCode GetProgramId(ServiceCtx context) + { + ulong pid = context.RequestData.ReadUInt64(); + + // TODO: Not correct as it shouldn't be directly using kernel objects here + if (context.Device.System.KernelContext.Processes.TryGetValue(pid, out KProcess process)) + { + context.ResponseData.Write(process.TitleId); + + return ResultCode.Success; + } + + return ResultCode.ProcessNotFound; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs new file mode 100644 index 000000000..92b5925e4 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.HLE.HOS.Services.Pm +{ + enum ResultCode + { + ModuleId = 15, + ErrorCodeShift = 9, + + Success = 0, + + ProcessNotFound = (1 << ErrorCodeShift) | ModuleId, + AlreadyStarted = (2 << ErrorCodeShift) | ModuleId, + NotTerminated = (3 << ErrorCodeShift) | ModuleId, + DebugHookInUse = (4 << ErrorCodeShift) | ModuleId, + ApplicationRunning = (5 << ErrorCodeShift) | ModuleId, + InvalidSize = (6 << ErrorCodeShift) | ModuleId, + } +} \ No newline at end of file From 065c4e520d1c8d9fc7de2097839976f9934ef8d0 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 15 Jan 2023 19:12:52 -0300 Subject: [PATCH 292/737] Specify image view usage flags on Vulkan (#4283) * Specify image view usage flags on Vulkan * PR feedback --- Ryujinx.Graphics.Vulkan/TextureStorage.cs | 37 ++++++++++++++--------- Ryujinx.Graphics.Vulkan/TextureView.cs | 11 ++++--- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 28fabb4fa..92209997d 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -79,21 +79,7 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = DefaultUsageFlags; - - if (info.Format.IsDepthOrStencil()) - { - usage |= ImageUsageFlags.DepthStencilAttachmentBit; - } - else if (info.Format.IsRtColorCompatible()) - { - usage |= ImageUsageFlags.ColorAttachmentBit; - } - - if (info.Format.IsImageCompatible()) - { - usage |= ImageUsageFlags.StorageBit; - } + var usage = GetImageUsageFromFormat(info.Format); var flags = ImageCreateFlags.CreateMutableFormatBit; @@ -306,6 +292,27 @@ namespace Ryujinx.Graphics.Vulkan } } + public static ImageUsageFlags GetImageUsageFromFormat(GAL.Format format) + { + var usage = DefaultUsageFlags; + + if (format.IsDepthOrStencil()) + { + usage |= ImageUsageFlags.DepthStencilAttachmentBit; + } + else if (format.IsRtColorCompatible()) + { + usage |= ImageUsageFlags.ColorAttachmentBit; + } + + if (format.IsImageCompatible()) + { + usage |= ImageUsageFlags.StorageBit; + } + + return usage; + } + public static SampleCountFlags ConvertToSampleCountFlags(SampleCountFlags supportedSampleCounts, uint samples) { if (samples == 0 || samples > (uint)SampleCountFlags.Count64Bit) diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index a9e1ed361..67f207219 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -54,6 +54,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); + var usage = TextureStorage.GetImageUsageFromFormat(info.Format); var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); @@ -94,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers); var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers); - unsafe Auto CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags = 0) + unsafe Auto CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags) { var usage = new ImageViewUsageCreateInfo() { @@ -110,14 +111,14 @@ namespace Ryujinx.Graphics.Vulkan Format = format, Components = cm, SubresourceRange = sr, - PNext = usageFlags == 0 ? null : &usage + PNext = &usage }; gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError(); return new Auto(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage()); } - _imageView = CreateImageView(componentMapping, subresourceRange, type); + _imageView = CreateImageView(componentMapping, subresourceRange, type, ImageUsageFlags.SampledBit); // Framebuffer attachments and storage images requires a identity component mapping. var identityComponentMapping = new ComponentMapping( @@ -126,7 +127,7 @@ namespace Ryujinx.Graphics.Vulkan ComponentSwizzle.B, ComponentSwizzle.A); - _imageViewIdentity = CreateImageView(identityComponentMapping, subresourceRangeDepth, type); + _imageViewIdentity = CreateImageView(identityComponentMapping, subresourceRangeDepth, type, usage); // Framebuffer attachments also require 3D textures to be bound as 2D array. if (info.Target == Target.Texture3D) @@ -144,7 +145,7 @@ namespace Ryujinx.Graphics.Vulkan { subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth); - _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray); + _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray, usage); } } From 64263c5218271f2e217aa593b4e47a59fe097835 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 16 Jan 2023 00:11:16 +0100 Subject: [PATCH 293/737] UI: Fix applications times (#4294) * Fix applications times * Add spaces * Fix TimeString formatting --- Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 714 ++++++++++---------- 1 file changed, 349 insertions(+), 365 deletions(-) diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index b1a8026e5..951516c08 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -38,9 +38,9 @@ namespace Ryujinx.Ui.App.Common private readonly byte[] _nroIcon; private readonly byte[] _nsoIcon; - private VirtualFileSystem _virtualFileSystem; - private Language _desiredTitleLanguage; - private CancellationTokenSource _cancellationToken; + private readonly VirtualFileSystem _virtualFileSystem; + private Language _desiredTitleLanguage; + private CancellationTokenSource _cancellationToken; public ApplicationLibrary(VirtualFileSystem virtualFileSystem) { @@ -53,7 +53,7 @@ namespace Ryujinx.Ui.App.Common _nsoIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NSO.png"); } - private byte[] GetResourceBytes(string resourceName) + private static byte[] GetResourceBytes(string resourceName) { Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName); byte[] resourceByteArray = new byte[resourceStream.Length]; @@ -68,9 +68,9 @@ namespace Ryujinx.Ui.App.Common _cancellationToken?.Cancel(); } - public void ReadControlData(IFileSystem controlFs, Span outProperty) + public static void ReadControlData(IFileSystem controlFs, Span outProperty) { - using var controlFile = new UniqueRef(); + using UniqueRef controlFile = new(); controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); @@ -86,7 +86,7 @@ namespace Ryujinx.Ui.App.Common _cancellationToken = new CancellationTokenSource(); // Builds the applications list with paths to found applications - List applications = new List(); + List applications = new(); try { @@ -143,251 +143,246 @@ namespace Ryujinx.Ui.App.Common string version = "0"; byte[] applicationIcon = null; - BlitStruct controlHolder = new BlitStruct(1); + BlitStruct controlHolder = new(1); try { string extension = Path.GetExtension(applicationPath).ToLower(); - using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) + using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read); + + if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") { - if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") + try { - try + PartitionFileSystem pfs; + + bool isExeFs = false; + + if (extension == ".xci") { - PartitionFileSystem pfs; + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - bool isExeFs = false; + pfs = xci.OpenPartition(XciPartitionType.Secure); + } + else + { + pfs = new PartitionFileSystem(file.AsStorage()); - if (extension == ".xci") + // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application. + bool hasMainNca = false; + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) { - Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); - - pfs = xci.OpenPartition(XciPartitionType.Secure); - } - else - { - pfs = new PartitionFileSystem(file.AsStorage()); - - // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application. - bool hasMainNca = false; - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) + if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca") { - if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca") + using UniqueRef ncaFile = new(); + + pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + // Some main NCAs don't have a data partition, so check if the partition exists before opening it + if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) { - using var ncaFile = new UniqueRef(); + hasMainNca = true; - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - // Some main NCAs don't have a data partition, so check if the partition exists before opening it - if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) - { - hasMainNca = true; - - break; - } - } - else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") - { - isExeFs = true; + break; } } - - if (!hasMainNca && !isExeFs) + else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") { - numApplicationsFound--; - - continue; + isExeFs = true; } } - if (isExeFs) - { - applicationIcon = _nspIcon; - - using var npdmFile = new UniqueRef(); - - Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); - - if (ResultFs.PathNotFound.Includes(result)) - { - Npdm npdm = new Npdm(npdmFile.Get.AsStream()); - - titleName = npdm.TitleName; - titleId = npdm.Aci0.TitleId.ToString("x16"); - } - } - else - { - GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); - - // Check if there is an update available. - if (IsUpdateApplied(titleId, out IFileSystem updatedControlFs)) - { - // Replace the original ControlFs by the updated one. - controlFs = updatedControlFs; - } - - ReadControlData(controlFs, controlHolder.ByteSpan); - - GetGameInformation(ref controlHolder.Value, out titleName, out _, out developer, out version); - - // Read the icon from the ControlFS and store it as a byte array - try - { - using var icon = new UniqueRef(); - - controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using (MemoryStream stream = new MemoryStream()) - { - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); - } - } - catch (HorizonResultException) - { - foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) - { - if (entry.Name == "control.nacp") - { - continue; - } - - using var icon = new UniqueRef(); - - controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using (MemoryStream stream = new MemoryStream()) - { - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); - } - - if (applicationIcon != null) - { - break; - } - } - - if (applicationIcon == null) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - } - } - } - } - catch (MissingKeyException exception) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - - Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); - } - catch (InvalidDataException) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - - Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}"); - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); - - numApplicationsFound--; - - continue; - } - } - else if (extension == ".nro") - { - BinaryReader reader = new BinaryReader(file); - - byte[] Read(long position, int size) - { - file.Seek(position, SeekOrigin.Begin); - - return reader.ReadBytes(size); - } - - try - { - file.Seek(24, SeekOrigin.Begin); - - int assetOffset = reader.ReadInt32(); - - if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") - { - byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); - - long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); - long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); - - ulong nacpOffset = reader.ReadUInt64(); - ulong nacpSize = reader.ReadUInt64(); - - // Reads and stores game icon as byte array - applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); - - // Read the NACP data - Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); - - GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version); - } - else - { - applicationIcon = _nroIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); - } - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); - - numApplicationsFound--; - - continue; - } - } - else if (extension == ".nca") - { - try - { - Nca nca = new Nca(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage()); - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) + if (!hasMainNca && !isExeFs) { numApplicationsFound--; continue; } } - catch (InvalidDataException) - { - Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}"); - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); + if (isExeFs) + { + applicationIcon = _nspIcon; + + using UniqueRef npdmFile = new(); + + Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); + + if (ResultFs.PathNotFound.Includes(result)) + { + Npdm npdm = new(npdmFile.Get.AsStream()); + + titleName = npdm.TitleName; + titleId = npdm.Aci0.TitleId.ToString("x16"); + } + } + else + { + GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); + + // Check if there is an update available. + if (IsUpdateApplied(titleId, out IFileSystem updatedControlFs)) + { + // Replace the original ControlFs by the updated one. + controlFs = updatedControlFs; + } + + ReadControlData(controlFs, controlHolder.ByteSpan); + + GetGameInformation(ref controlHolder.Value, out titleName, out _, out developer, out version); + + // Read the icon from the ControlFS and store it as a byte array + try + { + using UniqueRef icon = new(); + + controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + using MemoryStream stream = new(); + + icon.Get.AsStream().CopyTo(stream); + applicationIcon = stream.ToArray(); + } + catch (HorizonResultException) + { + foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) + { + if (entry.Name == "control.nacp") + { + continue; + } + + using var icon = new UniqueRef(); + + controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + using MemoryStream stream = new(); + + icon.Get.AsStream().CopyTo(stream); + applicationIcon = stream.ToArray(); + + + if (applicationIcon != null) + { + break; + } + } + + applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon; + } + } + } + catch (MissingKeyException exception) + { + applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; + + Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); + } + catch (InvalidDataException) + { + applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; + + Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}"); + } + catch (Exception exception) + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); + + numApplicationsFound--; + + continue; + } + } + else if (extension == ".nro") + { + BinaryReader reader = new(file); + + byte[] Read(long position, int size) + { + file.Seek(position, SeekOrigin.Begin); + + return reader.ReadBytes(size); + } + + try + { + file.Seek(24, SeekOrigin.Begin); + + int assetOffset = reader.ReadInt32(); + + if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") + { + byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); + + long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); + long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); + + ulong nacpOffset = reader.ReadUInt64(); + ulong nacpSize = reader.ReadUInt64(); + + // Reads and stores game icon as byte array + applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); + + // Read the NACP data + Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); + + GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version); + } + else + { + applicationIcon = _nroIcon; + titleName = Path.GetFileNameWithoutExtension(applicationPath); + } + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); + + numApplicationsFound--; + + continue; + } + } + else if (extension == ".nca") + { + try + { + Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage()); + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) + { numApplicationsFound--; continue; } - - applicationIcon = _ncaIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); } - // If its an NSO we just set defaults - else if (extension == ".nso") + catch (InvalidDataException) { - applicationIcon = _nsoIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); + Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}"); } + catch + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); + + numApplicationsFound--; + + continue; + } + + applicationIcon = _ncaIcon; + titleName = Path.GetFileNameWithoutExtension(applicationPath); + } + // If its an NSO we just set defaults + else if (extension == ".nso") + { + applicationIcon = _nsoIcon; + titleName = Path.GetFileNameWithoutExtension(applicationPath); } } catch (IOException exception) @@ -404,14 +399,21 @@ namespace Ryujinx.Ui.App.Common appMetadata.Title = titleName; }); - if (appMetadata.LastPlayed != "Never" && !DateTime.TryParse(appMetadata.LastPlayed, out _)) - { - Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); + if (appMetadata.LastPlayed != "Never") + { + if (!DateTime.TryParse(appMetadata.LastPlayed, out _)) + { + Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); - appMetadata.LastPlayed = "Never"; + appMetadata.LastPlayed = "Never"; + } + else + { + appMetadata.LastPlayed = appMetadata.LastPlayed[..^3]; + } } - ApplicationData data = new ApplicationData + ApplicationData data = new() { Favorite = appMetadata.Favorite, Icon = applicationIcon, @@ -419,7 +421,7 @@ namespace Ryujinx.Ui.App.Common TitleId = titleId, Developer = developer, Version = version, - TimePlayed = ConvertSecondsToReadableString(appMetadata.TimePlayed), + TimePlayed = ConvertSecondsToFormattedString(appMetadata.TimePlayed), TimePlayedNum = appMetadata.TimePlayed, LastPlayed = appMetadata.LastPlayed, FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1), @@ -488,10 +490,9 @@ namespace Ryujinx.Ui.App.Common appMetadata = new ApplicationMetadata(); - using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) - { - JsonHelper.Serialize(stream, appMetadata, true); - } + using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); + + JsonHelper.Serialize(stream, appMetadata, true); } try @@ -509,10 +510,9 @@ namespace Ryujinx.Ui.App.Common { modifyFunction(appMetadata); - using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) - { - JsonHelper.Serialize(stream, appMetadata, true); - } + using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); + + JsonHelper.Serialize(stream, appMetadata, true); } return appMetadata; @@ -529,192 +529,177 @@ namespace Ryujinx.Ui.App.Common { string extension = Path.GetExtension(applicationPath).ToLower(); - using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) + using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read); + + if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") { - if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") + try { - try + PartitionFileSystem pfs; + + bool isExeFs = false; + + if (extension == ".xci") { - PartitionFileSystem pfs; + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - bool isExeFs = false; + pfs = xci.OpenPartition(XciPartitionType.Secure); + } + else + { + pfs = new PartitionFileSystem(file.AsStorage()); - if (extension == ".xci") + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) { - Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - - pfs = xci.OpenPartition(XciPartitionType.Secure); - } - else - { - pfs = new PartitionFileSystem(file.AsStorage()); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) + if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") { - if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") - { - isExeFs = true; - } + isExeFs = true; } } + } - if (isExeFs) + if (isExeFs) + { + applicationIcon = _nspIcon; + } + else + { + // Store the ControlFS in variable called controlFs + GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _); + + // Read the icon from the ControlFS and store it as a byte array + try { - applicationIcon = _nspIcon; + using var icon = new UniqueRef(); + + controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + using MemoryStream stream = new(); + + icon.Get.AsStream().CopyTo(stream); + applicationIcon = stream.ToArray(); } - else + catch (HorizonResultException) { - // Store the ControlFS in variable called controlFs - GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _); - - // Read the icon from the ControlFS and store it as a byte array - try + foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) { + if (entry.Name == "control.nacp") + { + continue; + } + using var icon = new UniqueRef(); - controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = new()) { icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } - } - catch (HorizonResultException) - { - foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) + + if (applicationIcon != null) { - if (entry.Name == "control.nacp") - { - continue; - } - - using var icon = new UniqueRef(); - - controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using (MemoryStream stream = new MemoryStream()) - { - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); - } - - if (applicationIcon != null) - { - break; - } - } - - if (applicationIcon == null) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; + break; } } + + applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon; } } - catch (MissingKeyException) - { - applicationIcon = extension == ".xci" - ? _xciIcon - : _nspIcon; - } - catch (InvalidDataException) - { - applicationIcon = extension == ".xci" - ? _xciIcon - : _nspIcon; - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, - $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); - } } - else if (extension == ".nro") + catch (MissingKeyException) { - BinaryReader reader = new(file); - - byte[] Read(long position, int size) - { - file.Seek(position, SeekOrigin.Begin); - - return reader.ReadBytes(size); - } - - try - { - file.Seek(24, SeekOrigin.Begin); - - int assetOffset = reader.ReadInt32(); - - if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") - { - byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); - - long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); - long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); - - // Reads and stores game icon as byte array - applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); - } - else - { - applicationIcon = _nroIcon; - } - } - catch - { - Logger.Warning?.Print(LogClass.Application, - $"The file encountered was not of a valid type. Errored File: {applicationPath}"); - } + applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; } - else if (extension == ".nca") + catch (InvalidDataException) { - applicationIcon = _ncaIcon; + applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; } - // If its an NSO we just set defaults - else if (extension == ".nso") + catch (Exception exception) { - applicationIcon = _nsoIcon; + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); } } + else if (extension == ".nro") + { + BinaryReader reader = new(file); + + byte[] Read(long position, int size) + { + file.Seek(position, SeekOrigin.Begin); + + return reader.ReadBytes(size); + } + + try + { + file.Seek(24, SeekOrigin.Begin); + + int assetOffset = reader.ReadInt32(); + + if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") + { + byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); + + long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); + long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); + + // Reads and stores game icon as byte array + applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); + } + else + { + applicationIcon = _nroIcon; + } + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); + } + } + else if (extension == ".nca") + { + applicationIcon = _ncaIcon; + } + // If its an NSO we just set defaults + else if (extension == ".nso") + { + applicationIcon = _nsoIcon; + } } } catch(Exception) { - Logger.Warning?.Print(LogClass.Application, - $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}"); + Logger.Warning?.Print(LogClass.Application, $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}"); } return applicationIcon ?? _ncaIcon; } - private string ConvertSecondsToReadableString(double seconds) + private static string ConvertSecondsToFormattedString(double seconds) { - const int secondsPerMinute = 60; - const int secondsPerHour = secondsPerMinute * 60; - const int secondsPerDay = secondsPerHour * 24; + System.TimeSpan time = System.TimeSpan.FromSeconds(seconds); - string readableString; - - if (seconds < secondsPerMinute) + string timeString; + if (time.Days != 0) { - readableString = $"{seconds} seconds"; + timeString = $"{time.Days}d {time.Hours:D2}h {time.Minutes:D2}m"; } - else if (seconds < secondsPerHour) + else if (time.Hours != 0) { - readableString = $"{Math.Round(seconds / secondsPerMinute, 0, MidpointRounding.AwayFromZero)} minutes"; + timeString = $"{time.Hours:D2}h {time.Minutes:D2}m"; } - else if (seconds < secondsPerDay) + else if (time.Minutes != 0) { - readableString = $"{Math.Round(seconds / secondsPerHour, 1, MidpointRounding.AwayFromZero)} hours"; + timeString = $"{time.Minutes:D2}m"; } else { - readableString = $"{Math.Round(seconds / secondsPerDay, 1, MidpointRounding.AwayFromZero)} days"; + timeString = "Never"; } - return readableString; + return timeString; } private void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version) @@ -797,8 +782,7 @@ namespace Ryujinx.Ui.App.Common } catch (InvalidDataException) { - Logger.Warning?.Print(LogClass.Application, - $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {updatePath}"); + Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {updatePath}"); } catch (MissingKeyException exception) { From 784cf9d5947d60d146e518a7913220155362396b Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 16 Jan 2023 01:14:01 +0100 Subject: [PATCH 294/737] Ava UI: `Renderer` refactoring (#4297) * Ava UI: `Renderer` refactoring * Fix Vulkan CreateSurface --- Ryujinx.Ava/AppHost.cs | 46 ++-- .../Applet/AvaloniaDynamicTextInputHandler.cs | 2 +- Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs | 127 --------- Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs | 16 -- Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs | 16 -- Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs | 235 ---------------- Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs | 25 -- .../UI/Helpers/VulkanEmbeddedWindow.cs | 52 ---- Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 258 ++++++++++++++++++ .../EmbeddedWindowOpenGL.cs} | 37 +-- .../UI/Renderer/EmbeddedWindowVulkan.cs | 42 +++ .../OpenTKBindingsContext.cs} | 6 +- .../{Controls => Renderer}/RendererHost.axaml | 4 +- Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs | 69 +++++ .../{Helpers => Renderer}/SPBOpenGLContext.cs | 12 +- .../UI/ViewModels/MainWindowViewModel.cs | 27 +- 16 files changed, 434 insertions(+), 540 deletions(-) delete mode 100644 Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs delete mode 100644 Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs delete mode 100644 Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs delete mode 100644 Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs delete mode 100644 Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs delete mode 100644 Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs create mode 100644 Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs rename Ryujinx.Ava/UI/{Helpers/OpenGLEmbeddedWindow.cs => Renderer/EmbeddedWindowOpenGL.cs} (60%) create mode 100644 Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs rename Ryujinx.Ava/UI/{Helpers/OpenToolkitBindingsContext.cs => Renderer/OpenTKBindingsContext.cs} (62%) rename Ryujinx.Ava/UI/{Controls => Renderer}/RendererHost.axaml (84%) create mode 100644 Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs rename Ryujinx.Ava/UI/{Helpers => Renderer}/SPBOpenGLContext.cs (73%) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 5c4f5bd8b..067be5c01 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -12,9 +12,9 @@ using Ryujinx.Audio.Integration; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; @@ -44,7 +44,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; @@ -66,8 +65,8 @@ namespace Ryujinx.Ava private const float VolumeDelta = 0.05f; private static readonly Cursor InvisibleCursor = new(StandardCursorType.None); - private readonly IntPtr InvisibleCursorWin; - private readonly IntPtr DefaultCursorWin; + private readonly IntPtr InvisibleCursorWin; + private readonly IntPtr DefaultCursorWin; private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; @@ -80,6 +79,7 @@ namespace Ryujinx.Ava private readonly MainWindowViewModel _viewModel; private readonly IKeyboard _keyboardInterface; private readonly TopLevel _topLevel; + public RendererHost _rendererHost; private readonly GraphicsDebugLevel _glLogLevel; private float _newVolume; @@ -105,7 +105,6 @@ namespace Ryujinx.Ava public event EventHandler AppExit; public event EventHandler StatusUpdatedEvent; - public RendererHost Renderer { get; } public VirtualFileSystem VirtualFileSystem { get; } public ContentManager ContentManager { get; } public NpadManager NpadManager { get; } @@ -117,7 +116,6 @@ namespace Ryujinx.Ava public string ApplicationPath { get; private set; } public bool ScreenshotRequested { get; set; } - public AppHost( RendererHost renderer, InputManager inputManager, @@ -144,11 +142,12 @@ namespace Ryujinx.Ava NpadManager = _inputManager.CreateNpadManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager(); - Renderer = renderer; ApplicationPath = applicationPath; VirtualFileSystem = virtualFileSystem; ContentManager = contentManager; + _rendererHost = renderer; + _chrono = new Stopwatch(); _ticksPerFrame = Stopwatch.Frequency / TargetFps; @@ -183,10 +182,10 @@ namespace Ryujinx.Ava { _lastCursorMoveTime = Stopwatch.GetTimestamp(); - if ((Renderer.Content as EmbeddedWindow).TransformedBounds != null) + if (_rendererHost.EmbeddedWindow.TransformedBounds != null) { var point = e.GetCurrentPoint(window).Position; - var bounds = (Renderer.Content as EmbeddedWindow).TransformedBounds.Value.Clip; + var bounds = _rendererHost.EmbeddedWindow.TransformedBounds.Value.Clip; _isCursorInRenderer = point.X >= bounds.X && point.X <= bounds.Width + bounds.X && @@ -318,7 +317,7 @@ namespace Ryujinx.Ava _viewModel.SetUIProgressHandlers(Device); - Renderer.SizeChanged += Window_SizeChanged; + _rendererHost.SizeChanged += Window_SizeChanged; _isActive = true; @@ -430,11 +429,11 @@ namespace Ryujinx.Ava _windowsMultimediaTimerResolution = null; } - Renderer?.MakeCurrent(); + (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(); Device.DisposeGpu(); - Renderer?.MakeCurrent(null); + (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null); } private void HideCursorState_Changed(object sender, ReactiveEventArgs state) @@ -635,11 +634,12 @@ namespace Ryujinx.Ava // Initialize Renderer. IRenderer renderer; - if (Renderer.IsVulkan) + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) { - string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - - renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); + renderer = new VulkanRenderer( + (_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, + VulkanHelper.GetRequiredInstanceExtensions, + ConfigurationState.Instance.Graphics.PreferredGpu.Value); } else { @@ -787,14 +787,12 @@ namespace Ryujinx.Ava _renderer.ScreenCaptured += Renderer_ScreenCaptured; - (_renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(Renderer.GetContext())); - - Renderer.MakeCurrent(); + (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); Device.Gpu.Renderer.Initialize(_glLogLevel); - Width = (int)Renderer.Bounds.Width; - Height = (int)Renderer.Bounds.Height; + Width = (int)_rendererHost.Bounds.Width; + Height = (int)_rendererHost.Bounds.Height; _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling)); @@ -827,7 +825,7 @@ namespace Ryujinx.Ava _viewModel.SwitchToRenderer(false); } - Device.PresentFrame(() => Renderer?.SwapBuffers()); + Device.PresentFrame(() => (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); } if (_ticks >= _ticksPerFrame) @@ -837,7 +835,7 @@ namespace Ryujinx.Ava } }); - Renderer?.MakeCurrent(null); + (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null); } public void UpdateStatus() @@ -853,7 +851,7 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( Device.EnableDeviceVsync, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", - Renderer.IsVulkan ? "Vulkan" : "OpenGL", + ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan ? "Vulkan" : "OpenGL", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", diff --git a/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs index bae4762eb..2dd65e362 100644 --- a/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs +++ b/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Ava.UI.Applet Dispatcher.UIThread.Post(() => { _hiddenTextBox.Clear(); - _parent.ViewModel.RendererControl.Focus(); + _parent.ViewModel.RendererHostControl.Focus(); _parent = null; }); diff --git a/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs b/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs deleted file mode 100644 index 97058fa49..000000000 --- a/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Common.Configuration; -using Silk.NET.Vulkan; -using SPB.Graphics.OpenGL; -using SPB.Windowing; -using System; - -namespace Ryujinx.Ava.UI.Controls -{ - public partial class RendererHost : UserControl, IDisposable - { - private readonly GraphicsDebugLevel _graphicsDebugLevel; - private EmbeddedWindow _currentWindow; - - public bool IsVulkan { get; private set; } - - public RendererHost(GraphicsDebugLevel graphicsDebugLevel) - { - _graphicsDebugLevel = graphicsDebugLevel; - InitializeComponent(); - } - - public RendererHost() - { - InitializeComponent(); - } - - public void CreateOpenGL() - { - Dispose(); - - _currentWindow = new OpenGLEmbeddedWindow(3, 3, _graphicsDebugLevel); - Initialize(); - - IsVulkan = false; - } - - private void Initialize() - { - _currentWindow.WindowCreated += CurrentWindow_WindowCreated; - _currentWindow.SizeChanged += CurrentWindow_SizeChanged; - Content = _currentWindow; - } - - public void CreateVulkan() - { - Dispose(); - - _currentWindow = new VulkanEmbeddedWindow(); - Initialize(); - - IsVulkan = true; - } - - public OpenGLContextBase GetContext() - { - if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) - { - return openGlEmbeddedWindow.Context; - } - - return null; - } - - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnDetachedFromVisualTree(e); - - Dispose(); - } - - private void CurrentWindow_SizeChanged(object sender, Size e) - { - SizeChanged?.Invoke(sender, e); - } - - private void CurrentWindow_WindowCreated(object sender, IntPtr e) - { - RendererInitialized?.Invoke(this, EventArgs.Empty); - } - - public void MakeCurrent() - { - if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) - { - openGlEmbeddedWindow.MakeCurrent(); - } - } - - public void MakeCurrent(SwappableNativeWindowBase window) - { - if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) - { - openGlEmbeddedWindow.MakeCurrent(window); - } - } - - public void SwapBuffers() - { - if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow) - { - openGlEmbeddedWindow.SwapBuffers(); - } - } - - public event EventHandler RendererInitialized; - public event Action SizeChanged; - public void Dispose() - { - if (_currentWindow != null) - { - _currentWindow.WindowCreated -= CurrentWindow_WindowCreated; - _currentWindow.SizeChanged -= CurrentWindow_SizeChanged; - } - } - - public SurfaceKHR CreateVulkanSurface(Instance instance, Vk api) - { - return (_currentWindow is VulkanEmbeddedWindow vulkanEmbeddedWindow) - ? vulkanEmbeddedWindow.CreateSurface(instance) - : default; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs b/Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs deleted file mode 100644 index 6b696ba73..000000000 --- a/Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using SPB.Graphics; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Ava.UI.Helpers -{ - [SupportedOSPlatform("linux")] - internal class AvaloniaGlxContext : SPB.Platform.GLX.GLXOpenGLContext - { - public AvaloniaGlxContext(IntPtr handle) - : base(FramebufferFormat.Default, 0, 0, 0, false, null) - { - ContextHandle = handle; - } - } -} diff --git a/Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs b/Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs deleted file mode 100644 index b63a973a1..000000000 --- a/Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using SPB.Graphics; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Ava.UI.Helpers -{ - [SupportedOSPlatform("windows")] - internal class AvaloniaWglContext : SPB.Platform.WGL.WGLOpenGLContext - { - public AvaloniaWglContext(IntPtr handle) - : base(FramebufferFormat.Default, 0, 0, 0, false, null) - { - ContextHandle = handle; - } - } -} diff --git a/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs deleted file mode 100644 index 67ab80aa7..000000000 --- a/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs +++ /dev/null @@ -1,235 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Platform; -using SPB.Graphics; -using SPB.Platform; -using SPB.Platform.GLX; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Threading.Tasks; -using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; - -namespace Ryujinx.Ava.UI.Helpers -{ - public class EmbeddedWindow : NativeControlHost - { - private WindowProc _wndProcDelegate; - private string _className; - - protected GLXWindow X11Window { get; set; } - protected IntPtr WindowHandle { get; set; } - protected IntPtr X11Display { get; set; } - protected IntPtr NsView { get; set; } - protected IntPtr MetalLayer { get; set; } - - private UpdateBoundsCallbackDelegate _updateBoundsCallback; - - public event EventHandler WindowCreated; - public event EventHandler SizeChanged; - - protected virtual void OnWindowDestroyed() { } - protected virtual void OnWindowDestroying() - { - WindowHandle = IntPtr.Zero; - X11Display = IntPtr.Zero; - NsView = IntPtr.Zero; - MetalLayer = IntPtr.Zero; - } - - public EmbeddedWindow() - { - var stateObserverable = this.GetObservable(BoundsProperty); - - stateObserverable.Subscribe(StateChanged); - - Initialized += NativeEmbeddedWindow_Initialized; - } - - public virtual void OnWindowCreated() { } - - private void NativeEmbeddedWindow_Initialized(object sender, EventArgs e) - { - OnWindowCreated(); - - Task.Run(() => - { - WindowCreated?.Invoke(this, WindowHandle); - }); - } - - private void StateChanged(Rect rect) - { - SizeChanged?.Invoke(this, rect.Size); - _updateBoundsCallback?.Invoke(rect); - } - - protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) - { - if (OperatingSystem.IsLinux()) - { - return CreateLinux(parent); - } - else if (OperatingSystem.IsWindows()) - { - return CreateWin32(parent); - } - else if (OperatingSystem.IsMacOS()) - { - return CreateMacOs(parent); - } - - return base.CreateNativeControlCore(parent); - } - - protected override void DestroyNativeControlCore(IPlatformHandle control) - { - OnWindowDestroying(); - - if (OperatingSystem.IsLinux()) - { - DestroyLinux(); - } - else if (OperatingSystem.IsWindows()) - { - DestroyWin32(control); - } - else if (OperatingSystem.IsMacOS()) - { - DestroyMacOS(); - } - else - { - base.DestroyNativeControlCore(control); - } - - OnWindowDestroyed(); - } - - [SupportedOSPlatform("linux")] - protected virtual IPlatformHandle CreateLinux(IPlatformHandle parent) - { - X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow; - WindowHandle = X11Window.WindowHandle.RawHandle; - X11Display = X11Window.DisplayHandle.RawHandle; - - return new PlatformHandle(WindowHandle, "X11"); - } - - [SupportedOSPlatform("windows")] - IPlatformHandle CreateWin32(IPlatformHandle parent) - { - _className = "NativeWindow-" + Guid.NewGuid(); - _wndProcDelegate = WndProc; - var wndClassEx = new WNDCLASSEX - { - cbSize = Marshal.SizeOf(), - hInstance = GetModuleHandle(null), - lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), - style = ClassStyles.CS_OWNDC, - lpszClassName = Marshal.StringToHGlobalUni(_className), - hCursor = CreateArrowCursor() - }; - - var atom = RegisterClassEx(ref wndClassEx); - - var handle = CreateWindowEx( - 0, - _className, - "NativeWindow", - WindowStyles.WS_CHILD, - 0, - 0, - 640, - 480, - parent.Handle, - IntPtr.Zero, - IntPtr.Zero, - IntPtr.Zero); - - WindowHandle = handle; - - Marshal.FreeHGlobal(wndClassEx.lpszClassName); - - return new PlatformHandle(WindowHandle, "HWND"); - } - - [SupportedOSPlatform("windows")] - IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam) - { - var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF); - var root = VisualRoot as Window; - bool isLeft = false; - switch (msg) - { - case WindowsMessages.LBUTTONDOWN: - case WindowsMessages.RBUTTONDOWN: - isLeft = msg == WindowsMessages.LBUTTONDOWN; - this.RaiseEvent(new PointerPressedEventArgs( - this, - new Pointer(0, PointerType.Mouse, true), - root, - this.TranslatePoint(point, root).Value, - (ulong)Environment.TickCount64, - new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed), - KeyModifiers.None)); - break; - case WindowsMessages.LBUTTONUP: - case WindowsMessages.RBUTTONUP: - isLeft = msg == WindowsMessages.LBUTTONUP; - this.RaiseEvent(new PointerReleasedEventArgs( - this, - new Pointer(0, PointerType.Mouse, true), - root, - this.TranslatePoint(point, root).Value, - (ulong)Environment.TickCount64, - new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased), - KeyModifiers.None, - isLeft ? MouseButton.Left : MouseButton.Right)); - break; - case WindowsMessages.MOUSEMOVE: - this.RaiseEvent(new PointerEventArgs( - PointerMovedEvent, - this, - new Pointer(0, PointerType.Mouse, true), - root, - this.TranslatePoint(point, root).Value, - (ulong)Environment.TickCount64, - new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other), - KeyModifiers.None)); - break; - } - - return DefWindowProc(hWnd, msg, wParam, lParam); - } - - [SupportedOSPlatform("macos")] - IPlatformHandle CreateMacOs(IPlatformHandle parent) - { - MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback); - - NsView = nsView; - - return new PlatformHandle(nsView, "NSView"); - } - - void DestroyLinux() - { - X11Window?.Dispose(); - } - - [SupportedOSPlatform("windows")] - void DestroyWin32(IPlatformHandle handle) - { - DestroyWindow(handle.Handle); - UnregisterClass(_className, GetModuleHandle(null)); - } - - [SupportedOSPlatform("macos")] - void DestroyMacOS() - { - MetalHelper.DestroyMetalLayer(NsView, MetalLayer); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs b/Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs deleted file mode 100644 index e69774c30..000000000 --- a/Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Avalonia.OpenGL; -using SPB.Graphics.OpenGL; -using System; - -namespace Ryujinx.Ava.UI.Helpers -{ - internal static class IGlContextExtension - { - public static OpenGLContextBase AsOpenGLContextBase(this IGlContext context) - { - var handle = (IntPtr)context.GetType().GetProperty("Handle").GetValue(context); - - if (OperatingSystem.IsWindows()) - { - return new AvaloniaWglContext(handle); - } - else if (OperatingSystem.IsLinux()) - { - return new AvaloniaGlxContext(handle); - } - - return null; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs b/Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs deleted file mode 100644 index 6581610b6..000000000 --- a/Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Avalonia.Platform; -using Silk.NET.Vulkan; -using SPB.Graphics.Vulkan; -using SPB.Platform.GLX; -using SPB.Platform.Metal; -using SPB.Platform.Win32; -using SPB.Platform.X11; -using SPB.Windowing; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Ava.UI.Helpers -{ - public class VulkanEmbeddedWindow : EmbeddedWindow - { - private NativeWindowBase _window; - - [SupportedOSPlatform("linux")] - protected override IPlatformHandle CreateLinux(IPlatformHandle parent) - { - X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(parent.Handle)); - WindowHandle = X11Window.WindowHandle.RawHandle; - X11Display = X11Window.DisplayHandle.RawHandle; - - X11Window.Hide(); - - return new PlatformHandle(WindowHandle, "X11"); - } - - public SurfaceKHR CreateSurface(Instance instance) - { - if (OperatingSystem.IsWindows()) - { - _window = new SimpleWin32Window(new NativeHandle(WindowHandle)); - } - else if (OperatingSystem.IsLinux()) - { - _window = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle)); - } - else if (OperatingSystem.IsMacOS()) - { - _window = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer)); - } - else - { - throw new PlatformNotSupportedException(); - } - - return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, _window)); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs new file mode 100644 index 000000000..21d9d12e2 --- /dev/null +++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -0,0 +1,258 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Platform; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Common.Configuration; +using Ryujinx.Ui.Common.Configuration; +using SPB.Graphics; +using SPB.Platform; +using SPB.Platform.GLX; +using SPB.Platform.X11; +using SPB.Windowing; +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; + +namespace Ryujinx.Ava.UI.Renderer +{ + public class EmbeddedWindow : NativeControlHost + { + private WindowProc _wndProcDelegate; + private string _className; + + protected GLXWindow X11Window { get; set; } + + protected IntPtr WindowHandle { get; set; } + protected IntPtr X11Display { get; set; } + protected IntPtr NsView { get; set; } + protected IntPtr MetalLayer { get; set; } + + private UpdateBoundsCallbackDelegate _updateBoundsCallback; + + public event EventHandler WindowCreated; + public event EventHandler SizeChanged; + + public EmbeddedWindow() + { + this.GetObservable(BoundsProperty).Subscribe(StateChanged); + + Initialized += OnNativeEmbeddedWindowCreated; + } + + public virtual void OnWindowCreated() { } + + protected virtual void OnWindowDestroyed() { } + + protected virtual void OnWindowDestroying() + { + WindowHandle = IntPtr.Zero; + X11Display = IntPtr.Zero; + NsView = IntPtr.Zero; + MetalLayer = IntPtr.Zero; + } + + private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e) + { + OnWindowCreated(); + + Task.Run(() => + { + WindowCreated?.Invoke(this, WindowHandle); + }); + } + + private void StateChanged(Rect rect) + { + SizeChanged?.Invoke(this, rect.Size); + _updateBoundsCallback?.Invoke(rect); + } + + protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle control) + { + if (OperatingSystem.IsLinux()) + { + return CreateLinux(control); + } + else if (OperatingSystem.IsWindows()) + { + return CreateWin32(control); + } + else if (OperatingSystem.IsMacOS()) + { + return CreateMacOs(); + } + + return base.CreateNativeControlCore(control); + } + + protected override void DestroyNativeControlCore(IPlatformHandle control) + { + OnWindowDestroying(); + + if (OperatingSystem.IsLinux()) + { + DestroyLinux(); + } + else if (OperatingSystem.IsWindows()) + { + DestroyWin32(control); + } + else if (OperatingSystem.IsMacOS()) + { + DestroyMacOS(); + } + else + { + base.DestroyNativeControlCore(control); + } + + OnWindowDestroyed(); + } + + [SupportedOSPlatform("linux")] + protected virtual IPlatformHandle CreateLinux(IPlatformHandle control) + { + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) + { + X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle)); + } + else + { + X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow; + } + + WindowHandle = X11Window.WindowHandle.RawHandle; + X11Display = X11Window.DisplayHandle.RawHandle; + + return new PlatformHandle(WindowHandle, "X11"); + } + + [SupportedOSPlatform("windows")] + IPlatformHandle CreateWin32(IPlatformHandle control) + { + _className = "NativeWindow-" + Guid.NewGuid(); + + _wndProcDelegate = delegate (IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam) + { + if (VisualRoot != null) + { + Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value; + Pointer pointer = new(0, PointerType.Mouse, true); + + switch (msg) + { + case WindowsMessages.LBUTTONDOWN: + case WindowsMessages.RBUTTONDOWN: + { + bool isLeft = msg == WindowsMessages.LBUTTONDOWN; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed); + + var evnt = new PointerPressedEventArgs( + this, + pointer, + VisualRoot, + rootVisualPosition, + (ulong)Environment.TickCount64, + properties, + KeyModifiers.None); + + RaiseEvent(evnt); + + break; + } + case WindowsMessages.LBUTTONUP: + case WindowsMessages.RBUTTONUP: + { + bool isLeft = msg == WindowsMessages.LBUTTONUP; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased); + + var evnt = new PointerReleasedEventArgs( + this, + pointer, + VisualRoot, + rootVisualPosition, + (ulong)Environment.TickCount64, + properties, + KeyModifiers.None, + isLeft ? MouseButton.Left : MouseButton.Right); + + RaiseEvent(evnt); + + break; + } + case WindowsMessages.MOUSEMOVE: + { + var evnt = new PointerEventArgs( + PointerMovedEvent, + this, + pointer, + VisualRoot, + rootVisualPosition, + (ulong)Environment.TickCount64, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other), + KeyModifiers.None); + + RaiseEvent(evnt); + + break; + } + } + } + + return DefWindowProc(hWnd, msg, wParam, lParam); + }; + + WNDCLASSEX wndClassEx = new() + { + cbSize = Marshal.SizeOf(), + hInstance = GetModuleHandle(null), + lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), + style = ClassStyles.CS_OWNDC, + lpszClassName = Marshal.StringToHGlobalUni(_className), + hCursor = CreateArrowCursor() + }; + + RegisterClassEx(ref wndClassEx); + + WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + Marshal.FreeHGlobal(wndClassEx.lpszClassName); + + return new PlatformHandle(WindowHandle, "HWND"); + } + + [SupportedOSPlatform("macos")] + IPlatformHandle CreateMacOs() + { + MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback); + + NsView = nsView; + + return new PlatformHandle(nsView, "NSView"); + } + + [SupportedOSPlatform("Linux")] + void DestroyLinux() + { + X11Window?.Dispose(); + } + + [SupportedOSPlatform("windows")] + void DestroyWin32(IPlatformHandle handle) + { + DestroyWindow(handle.Handle); + UnregisterClass(_className, GetModuleHandle(null)); + } + + [SupportedOSPlatform("macos")] + void DestroyMacOS() + { + MetalHelper.DestroyMetalLayer(NsView, MetalLayer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Helpers/OpenGLEmbeddedWindow.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs similarity index 60% rename from Ryujinx.Ava/UI/Helpers/OpenGLEmbeddedWindow.cs rename to Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs index db77f66bf..305e891a1 100644 --- a/Ryujinx.Ava/UI/Helpers/OpenGLEmbeddedWindow.cs +++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs @@ -1,5 +1,8 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Configuration; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL; +using Ryujinx.Ui.Common.Configuration; using SPB.Graphics; using SPB.Graphics.OpenGL; using SPB.Platform; @@ -7,26 +10,20 @@ using SPB.Platform.WGL; using SPB.Windowing; using System; -namespace Ryujinx.Ava.UI.Helpers +namespace Ryujinx.Ava.UI.Renderer { - public class OpenGLEmbeddedWindow : EmbeddedWindow + public class EmbeddedWindowOpenGL : EmbeddedWindow { - private readonly int _major; - private readonly int _minor; - private readonly GraphicsDebugLevel _graphicsDebugLevel; private SwappableNativeWindowBase _window; + public OpenGLContextBase Context { get; set; } - public OpenGLEmbeddedWindow(int major, int minor, GraphicsDebugLevel graphicsDebugLevel) - { - _major = major; - _minor = minor; - _graphicsDebugLevel = graphicsDebugLevel; - } + public EmbeddedWindowOpenGL() { } protected override void OnWindowDestroying() { Context.Dispose(); + base.OnWindowDestroying(); } @@ -48,19 +45,20 @@ namespace Ryujinx.Ava.UI.Helpers } var flags = OpenGLContextFlags.Compat; - if (_graphicsDebugLevel != GraphicsDebugLevel.None) + if (ConfigurationState.Instance.Logger.GraphicsDebugLevel != GraphicsDebugLevel.None) { flags |= OpenGLContextFlags.Debug; } - Context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, _major, _minor, flags); + var graphicsMode = Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default; + + Context = PlatformHelper.CreateOpenGLContext(graphicsMode, 3, 3, flags); Context.Initialize(_window); Context.MakeCurrent(_window); - var bindingsContext = new OpenToolkitBindingsContext(Context.GetProcAddress); + GL.LoadBindings(new OpenTKBindingsContext(Context.GetProcAddress)); - GL.LoadBindings(bindingsContext); Context.MakeCurrent(null); } @@ -76,7 +74,14 @@ namespace Ryujinx.Ava.UI.Helpers public void SwapBuffers() { - _window.SwapBuffers(); + _window?.SwapBuffers(); + } + + public void InitializeBackgroundContext(IRenderer renderer) + { + (renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(Context)); + + MakeCurrent(); } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs new file mode 100644 index 000000000..0b3eb9e30 --- /dev/null +++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs @@ -0,0 +1,42 @@ +using Silk.NET.Vulkan; +using SPB.Graphics.Vulkan; +using SPB.Platform.Metal; +using SPB.Platform.Win32; +using SPB.Platform.X11; +using SPB.Windowing; +using System; + +namespace Ryujinx.Ava.UI.Renderer +{ + public class EmbeddedWindowVulkan : EmbeddedWindow + { + public SurfaceKHR CreateSurface(Instance instance) + { + NativeWindowBase nativeWindowBase; + + if (OperatingSystem.IsWindows()) + { + nativeWindowBase = new SimpleWin32Window(new NativeHandle(WindowHandle)); + } + else if (OperatingSystem.IsLinux()) + { + nativeWindowBase = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle)); + } + else if (OperatingSystem.IsMacOS()) + { + nativeWindowBase = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer)); + } + else + { + throw new PlatformNotSupportedException(); + } + + return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, nativeWindowBase)); + } + + public SurfaceKHR CreateSurface(Instance instance, Vk api) + { + return CreateSurface(instance); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Helpers/OpenToolkitBindingsContext.cs b/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs similarity index 62% rename from Ryujinx.Ava/UI/Helpers/OpenToolkitBindingsContext.cs rename to Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs index efb703bab..a2ec02b25 100644 --- a/Ryujinx.Ava/UI/Helpers/OpenToolkitBindingsContext.cs +++ b/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs @@ -1,13 +1,13 @@ using OpenTK; using System; -namespace Ryujinx.Ava.UI.Helpers +namespace Ryujinx.Ava.UI.Renderer { - internal class OpenToolkitBindingsContext : IBindingsContext + internal class OpenTKBindingsContext : IBindingsContext { private readonly Func _getProcAddress; - public OpenToolkitBindingsContext(Func getProcAddress) + public OpenTKBindingsContext(Func getProcAddress) { _getProcAddress = getProcAddress; } diff --git a/Ryujinx.Ava/UI/Controls/RendererHost.axaml b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml similarity index 84% rename from Ryujinx.Ava/UI/Controls/RendererHost.axaml rename to Ryujinx.Ava/UI/Renderer/RendererHost.axaml index 1cc557f06..bb96b10d2 100644 --- a/Ryujinx.Ava/UI/Controls/RendererHost.axaml +++ b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml @@ -6,6 +6,6 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Ryujinx.Ava.UI.Controls.RendererHost" + x:Class="Ryujinx.Ava.UI.Renderer.RendererHost" Focusable="True"> - + \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs new file mode 100644 index 000000000..16a46df42 --- /dev/null +++ b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs @@ -0,0 +1,69 @@ +using Avalonia; +using Avalonia.Controls; +using Ryujinx.Common.Configuration; +using Ryujinx.Ui.Common.Configuration; +using Silk.NET.Vulkan; +using System; + +namespace Ryujinx.Ava.UI.Renderer +{ + public partial class RendererHost : UserControl, IDisposable + { + public EmbeddedWindow EmbeddedWindow; + + public event EventHandler WindowCreated; + public event Action SizeChanged; + + public RendererHost() + { + InitializeComponent(); + + Dispose(); + + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl) + { + EmbeddedWindow = new EmbeddedWindowOpenGL(); + } + else + { + EmbeddedWindow = new EmbeddedWindowVulkan(); + } + + Initialize(); + } + + private void Initialize() + { + EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated; + EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged; + + Content = EmbeddedWindow; + } + + public void Dispose() + { + if (EmbeddedWindow != null) + { + EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated; + EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged; + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + Dispose(); + } + + private void CurrentWindow_SizeChanged(object sender, Size e) + { + SizeChanged?.Invoke(sender, e); + } + + private void CurrentWindow_WindowCreated(object sender, IntPtr e) + { + WindowCreated?.Invoke(this, EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Helpers/SPBOpenGLContext.cs b/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs similarity index 73% rename from Ryujinx.Ava/UI/Helpers/SPBOpenGLContext.cs rename to Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs index 21f206c83..e090f14c7 100644 --- a/Ryujinx.Ava/UI/Helpers/SPBOpenGLContext.cs +++ b/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs @@ -5,17 +5,17 @@ using SPB.Graphics.OpenGL; using SPB.Platform; using SPB.Windowing; -namespace Ryujinx.Ava.UI.Helpers +namespace Ryujinx.Ava.UI.Renderer { class SPBOpenGLContext : IOpenGLContext { - private OpenGLContextBase _context; - private NativeWindowBase _window; + private readonly OpenGLContextBase _context; + private readonly NativeWindowBase _window; private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window) { _context = context; - _window = window; + _window = window; } public void Dispose() @@ -32,12 +32,12 @@ namespace Ryujinx.Ava.UI.Helpers public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext) { OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext); - NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); + NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); context.Initialize(window); context.MakeCurrent(window); - GL.LoadBindings(new OpenToolkitBindingsContext(context.GetProcAddress)); + GL.LoadBindings(new OpenTKBindingsContext(context.GetProcAddress)); context.MakeCurrent(null); diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 295402155..a02b64295 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -13,6 +13,7 @@ using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -870,7 +871,7 @@ namespace Ryujinx.Ava.UI.ViewModels public Action SwitchToGameControl { get; private set; } public Action SetMainContent { get; private set; } public TopLevel TopLevel { get; private set; } - public RendererHost RendererControl { get; private set; } + public RendererHost RendererHostControl { get; private set; } public bool IsClosing { get; set; } public LibHacHorizonManager LibHacHorizonManager { get; internal set; } public IHostUiHandler UiHandler { get; internal set; } @@ -1144,7 +1145,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void InitializeGame() { - RendererControl.RendererInitialized += GlRenderer_Created; + RendererHostControl.WindowCreated += RendererHost_Created; AppHost.StatusUpdatedEvent += Update_StatusBar; AppHost.AppExit += AppHost_AppExit; @@ -1203,7 +1204,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - private void GlRenderer_Created(object sender, EventArgs e) + private void RendererHost_Created(object sender, EventArgs e) { ShowLoading(false); @@ -1731,18 +1732,10 @@ namespace Ryujinx.Ava.UI.ViewModels PrepareLoadScreen(); - RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel); - if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl) - { - RendererControl.CreateOpenGL(); - } - else - { - RendererControl.CreateVulkan(); - } + RendererHostControl = new RendererHost(); AppHost = new AppHost( - RendererControl, + RendererHostControl, InputManager, path, VirtualFileSystem, @@ -1783,9 +1776,9 @@ namespace Ryujinx.Ava.UI.ViewModels { SwitchToGameControl(startFullscreen); - SetMainContent(RendererControl); + SetMainContent(RendererHostControl); - RendererControl.Focus(); + RendererHostControl.Focus(); }); } @@ -1853,8 +1846,8 @@ namespace Ryujinx.Ava.UI.ViewModels HandleRelaunch(); }); - RendererControl.RendererInitialized -= GlRenderer_Created; - RendererControl = null; + RendererHostControl.WindowCreated -= RendererHost_Created; + RendererHostControl = null; SelectedIcon = null; From 1faff14e73408872dd034114bfc2c3f1044f8d94 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 16 Jan 2023 03:09:52 +0100 Subject: [PATCH 295/737] UI: Fixes GTK sorting regression of #4294 --- Ryujinx/Ui/Helper/SortHelper.cs | 80 ++++++++++----------------------- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/Ryujinx/Ui/Helper/SortHelper.cs b/Ryujinx/Ui/Helper/SortHelper.cs index 4def89323..0c0eefd2c 100644 --- a/Ryujinx/Ui/Helper/SortHelper.cs +++ b/Ryujinx/Ui/Helper/SortHelper.cs @@ -7,65 +7,33 @@ namespace Ryujinx.Ui.Helper { public static int TimePlayedSort(ITreeModel model, TreeIter a, TreeIter b) { - string aValue = model.GetValue(a, 5).ToString(); - string bValue = model.GetValue(b, 5).ToString(); - float aFloat; - float bFloat; + static string ReverseFormat(string time) + { + if (time == "Never") + { + return "00"; + } - if (aValue.Length > 7 && aValue[^7..] == "minutes") - { - aValue = aValue.Replace("minutes", ""); - aFloat = (float.Parse(aValue) * 60); - } - else if (aValue.Length > 5 && aValue[^5..] == "hours") - { - aValue = aValue.Replace("hours", ""); - aFloat = (float.Parse(aValue) * 3600); - } - else if (aValue.Length > 4 && aValue[^4..] == "days") - { - aValue = aValue.Replace("days", ""); - aFloat = (float.Parse(aValue) * 86400); - } - else - { - aValue = aValue.Replace("seconds", ""); - aFloat = float.Parse(aValue); + var numbers = time.Split(new char[] { 'd', 'h', 'm' }); + + time = time.Replace(" ", "").Replace("d", ".").Replace("h", ":").Replace("m", ""); + + if (numbers.Length == 2) + { + return $"00.00:{time}"; + } + else if (numbers.Length == 3) + { + return $"00.{time}"; + } + + return time; } - if (bValue.Length > 7 && bValue[^7..] == "minutes") - { - bValue = bValue.Replace("minutes", ""); - bFloat = (float.Parse(bValue) * 60); - } - else if (bValue.Length > 5 && bValue[^5..] == "hours") - { - bValue = bValue.Replace("hours", ""); - bFloat = (float.Parse(bValue) * 3600); - } - else if (bValue.Length > 4 && bValue[^4..] == "days") - { - bValue = bValue.Replace("days", ""); - bFloat = (float.Parse(bValue) * 86400); - } - else - { - bValue = bValue[0..^8]; - bFloat = float.Parse(bValue); - } + string aValue = ReverseFormat(model.GetValue(a, 5).ToString()); + string bValue = ReverseFormat(model.GetValue(b, 5).ToString()); - if (aFloat > bFloat) - { - return -1; - } - else if (bFloat > aFloat) - { - return 1; - } - else - { - return 0; - } + return TimeSpan.Compare(TimeSpan.Parse(aValue), TimeSpan.Parse(bValue)); } public static int LastPlayedSort(ITreeModel model, TreeIter a, TreeIter b) @@ -123,4 +91,4 @@ namespace Ryujinx.Ui.Helper } } } -} \ No newline at end of file +} From e68650237db2d5fd0fb78d9e21378d139338246f Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 16 Jan 2023 03:59:41 +0100 Subject: [PATCH 296/737] Ava: Fix Linux Vulkan renderer regression (#4303) * ava: Fix Linux Vulkan renderer staying transparent * ava: Minor Renderer cleanup * Don't supress potential NRE warning Co-authored-by: Ac_K Co-authored-by: Ac_K --- Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 7 ++++--- Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs | 7 +++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs index 21d9d12e2..6cacfef49 100644 --- a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs +++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Ava.UI.Renderer } else if (OperatingSystem.IsMacOS()) { - return CreateMacOs(); + return CreateMacOS(); } return base.CreateNativeControlCore(control); @@ -113,11 +113,12 @@ namespace Ryujinx.Ava.UI.Renderer } [SupportedOSPlatform("linux")] - protected virtual IPlatformHandle CreateLinux(IPlatformHandle control) + private IPlatformHandle CreateLinux(IPlatformHandle control) { if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) { X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle)); + X11Window.Hide(); } else { @@ -227,7 +228,7 @@ namespace Ryujinx.Ava.UI.Renderer } [SupportedOSPlatform("macos")] - IPlatformHandle CreateMacOs() + IPlatformHandle CreateMacOS() { MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback); diff --git a/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs index 16a46df42..ee10282db 100644 --- a/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs +++ b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs @@ -2,14 +2,13 @@ using Avalonia; using Avalonia.Controls; using Ryujinx.Common.Configuration; using Ryujinx.Ui.Common.Configuration; -using Silk.NET.Vulkan; using System; namespace Ryujinx.Ava.UI.Renderer { public partial class RendererHost : UserControl, IDisposable { - public EmbeddedWindow EmbeddedWindow; + public readonly EmbeddedWindow EmbeddedWindow; public event EventHandler WindowCreated; public event Action SizeChanged; @@ -18,8 +17,6 @@ namespace Ryujinx.Ava.UI.Renderer { InitializeComponent(); - Dispose(); - if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl) { EmbeddedWindow = new EmbeddedWindowOpenGL(); @@ -47,6 +44,8 @@ namespace Ryujinx.Ava.UI.Renderer EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated; EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged; } + + GC.SuppressFinalize(this); } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) From f0e27a23a5a4c834cc846e415c9cce0597e8d6c5 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Tue, 17 Jan 2023 03:39:46 +0000 Subject: [PATCH 297/737] Add short duration texture cache (#3754) * Add short duration texture cache This texture cache takes textures that lose their last pool reference and keeps them alive until the next frame, or until an incompatible overlap removes it. This is done since under certain circumstances, a texture's reference can be wiped from a pool despite it still being in use - though typically the reference will return when rendering the next frame. While this may slightly increase texture memory usage when quickly going through a bunch of temporary textures, it's still bounded due to the overlap removal rule. This greatly increases performance in Hyrule Warriors: Age of Calamity. It may positively affect some UE4 games which dip framerate severely under certain circumstances. * Small optimization * Don't forget this. * Add short cache dictionary * Address feedback * Address some feedback --- Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs | 111 ++++++++++++++++++ Ryujinx.Graphics.Gpu/Image/Pool.cs | 12 +- Ryujinx.Graphics.Gpu/Image/Texture.cs | 26 ++++ Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 37 ++++++ .../Image/TextureDescriptor.cs | 22 +++- Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 29 +++-- Ryujinx.Graphics.Gpu/Window.cs | 2 + 7 files changed, 228 insertions(+), 11 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index 4a1615f04..379eb7159 100644 --- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -4,6 +4,28 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image { + /// + /// An entry on the short duration texture cache. + /// + class ShortTextureCacheEntry + { + public readonly TextureDescriptor Descriptor; + public readonly int InvalidatedSequence; + public readonly Texture Texture; + + /// + /// Create a new entry on the short duration texture cache. + /// + /// Last descriptor that referenced the texture + /// The texture + public ShortTextureCacheEntry(TextureDescriptor descriptor, Texture texture) + { + Descriptor = descriptor; + InvalidatedSequence = texture.InvalidatedSequence; + Texture = texture; + } + } + /// /// A texture cache that automatically removes older textures that are not used for some time. /// The cache works with a rotated list with a fixed size. When new textures are added, the @@ -16,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly LinkedList _textures; private readonly ConcurrentQueue _deferredRemovals; + private HashSet _shortCacheBuilder; + private HashSet _shortCache; + + private Dictionary _shortCacheLookup; + /// /// Creates a new instance of the automatic deletion cache. /// @@ -23,6 +50,11 @@ namespace Ryujinx.Graphics.Gpu.Image { _textures = new LinkedList(); _deferredRemovals = new ConcurrentQueue(); + + _shortCacheBuilder = new HashSet(); + _shortCache = new HashSet(); + + _shortCacheLookup = new Dictionary(); } /// @@ -130,6 +162,85 @@ namespace Ryujinx.Graphics.Gpu.Image _deferredRemovals.Enqueue(texture); } + /// + /// Attempt to find a texture on the short duration cache. + /// + /// The texture descriptor + /// The texture if found, null otherwise + public Texture FindShortCache(in TextureDescriptor descriptor) + { + if (_shortCacheLookup.Count > 0 && _shortCacheLookup.TryGetValue(descriptor, out var entry)) + { + if (entry.InvalidatedSequence == entry.Texture.InvalidatedSequence) + { + return entry.Texture; + } + else + { + _shortCacheLookup.Remove(descriptor); + } + } + + return null; + } + + /// + /// Removes a texture from the short duration cache. + /// + /// Texture to remove from the short cache + public void RemoveShortCache(Texture texture) + { + bool removed = _shortCache.Remove(texture.ShortCacheEntry); + removed |= _shortCacheBuilder.Remove(texture.ShortCacheEntry); + + if (removed) + { + texture.DecrementReferenceCount(); + + _shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor); + texture.ShortCacheEntry = null; + } + } + + /// + /// Adds a texture to the short duration cache. + /// It starts in the builder set, and it is moved into the deletion set on next process. + /// + /// Texture to add to the short cache + /// Last used texture descriptor + public void AddShortCache(Texture texture, ref TextureDescriptor descriptor) + { + var entry = new ShortTextureCacheEntry(descriptor, texture); + + _shortCacheBuilder.Add(entry); + _shortCacheLookup.Add(entry.Descriptor, entry); + + texture.ShortCacheEntry = entry; + + texture.IncrementReferenceCount(); + } + + /// + /// Delete textures from the short duration cache. + /// Moves the builder set to be deleted on next process. + /// + public void ProcessShortCache() + { + HashSet toRemove = _shortCache; + + foreach (var entry in toRemove) + { + entry.Texture.DecrementReferenceCount(); + + _shortCacheLookup.Remove(entry.Descriptor); + entry.Texture.ShortCacheEntry = null; + } + + toRemove.Clear(); + _shortCache = _shortCacheBuilder; + _shortCacheBuilder = toRemove; + } + public IEnumerator GetEnumerator() { return _textures.GetEnumerator(); diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs index ddd698077..ee4c051f4 100644 --- a/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -91,7 +91,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// A reference to the descriptor public ref readonly T2 GetDescriptorRef(int id) { - return ref MemoryMarshal.Cast(PhysicalMemory.GetSpan(Address + (ulong)id * DescriptorSize, DescriptorSize))[0]; + return ref GetDescriptorRefAddress(Address + (ulong)id * DescriptorSize); + } + + /// + /// Gets a reference to the descriptor for a given address. + /// + /// Address of the descriptor + /// A reference to the descriptor + public ref readonly T2 GetDescriptorRefAddress(ulong address) + { + return ref MemoryMarshal.Cast(PhysicalMemory.GetSpan(address, DescriptorSize))[0]; } /// diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index f0c31be6f..cfe577566 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -138,6 +138,10 @@ namespace Ryujinx.Graphics.Gpu.Image public LinkedListNode CacheNode { get; set; } /// + /// Entry for this texture in the short duration cache, if present. + /// + public ShortTextureCacheEntry ShortCacheEntry { get; set; } + /// Physical memory ranges where the texture data is located. /// public MultiRange Range { get; private set; } @@ -1555,6 +1559,20 @@ namespace Ryujinx.Graphics.Gpu.Image _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id }); } _referenceCount++; + + if (ShortCacheEntry != null) + { + _physicalMemory.TextureCache.RemoveShortCache(this); + } + } + + /// + /// Indicates that the texture has one reference left, and will delete on reference decrement. + /// + /// True if there is one reference remaining, false otherwise + public bool HasOneReference() + { + return _referenceCount == 1; } /// @@ -1624,6 +1642,14 @@ namespace Ryujinx.Graphics.Gpu.Image _poolOwners.Clear(); } + if (ShortCacheEntry != null && _context.IsGpuThread()) + { + // If this is called from another thread (unmapped), the short cache will + // have to remove this texture on a future tick. + + _physicalMemory.TextureCache.RemoveShortCache(this); + } + InvalidatedSequence++; } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index c020f4c82..49adecdca 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -894,6 +894,16 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Attempt to find a texture on the short duration cache. + /// + /// The texture descriptor + /// The texture if found, null otherwise + public Texture FindShortCache(in TextureDescriptor descriptor) + { + return _cache.FindShortCache(descriptor); + } + /// /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null. /// @@ -1178,6 +1188,33 @@ namespace Ryujinx.Graphics.Gpu.Image _cache.RemoveDeferred(texture); } + /// + /// Adds a texture to the short duration cache. This typically keeps it alive for two ticks. + /// + /// Texture to add to the short cache + /// Last used texture descriptor + public void AddShortCache(Texture texture, ref TextureDescriptor descriptor) + { + _cache.AddShortCache(texture, ref descriptor); + } + + /// + /// Removes a texture from the short duration cache. + /// + /// Texture to remove from the short cache + public void RemoveShortCache(Texture texture) + { + _cache.RemoveShortCache(texture); + } + + /// + /// Ticks periodic elements of the texture cache. + /// + public void Tick() + { + _cache.ProcessShortCache(); + } + /// /// Disposes all textures and samplers in the cache. /// It's an error to use the texture cache after disposal. diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs index 52cc8ee01..3e35f8d2c 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs @@ -1,3 +1,4 @@ +using System; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; @@ -6,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Maxwell texture descriptor, as stored on the GPU texture pool memory region. /// - struct TextureDescriptor : ITextureDescriptor + struct TextureDescriptor : ITextureDescriptor, IEquatable { #pragma warning disable CS0649 public uint Word0; @@ -249,5 +250,24 @@ namespace Ryujinx.Graphics.Gpu.Image { return Unsafe.As>(ref this).Equals(Unsafe.As>(ref other)); } + + /// + /// Check if two descriptors are equal. + /// + /// The descriptor to compare against + /// True if they are equal, false otherwise + public bool Equals(TextureDescriptor other) + { + return Equals(ref other); + } + + /// + /// Gets a hash code for this descriptor. + /// + /// The hash code for this descriptor. + public override int GetHashCode() + { + return Unsafe.As>(ref this).GetHashCode(); + } } } diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 4d2544e27..fc99fc997 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -52,16 +52,21 @@ namespace Ryujinx.Graphics.Gpu.Image if (texture == null) { - TextureInfo info = GetInfo(descriptor, out int layerSize); + texture = PhysicalMemory.TextureCache.FindShortCache(descriptor); - ProcessDereferenceQueue(); - - texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize); - - // If this happens, then the texture address is invalid, we can't add it to the cache. if (texture == null) { - return ref descriptor; + TextureInfo info = GetInfo(descriptor, out int layerSize); + + ProcessDereferenceQueue(); + + texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize); + + // If this happens, then the texture address is invalid, we can't add it to the cache. + if (texture == null) + { + return ref descriptor; + } } texture.IncrementReferenceCount(this, id); @@ -208,15 +213,21 @@ namespace Ryujinx.Graphics.Gpu.Image if (texture != null) { - TextureDescriptor descriptor = PhysicalMemory.Read(address); + ref TextureDescriptor cachedDescriptor = ref DescriptorCache[id]; + ref readonly TextureDescriptor descriptor = ref GetDescriptorRefAddress(address); // If the descriptors are the same, the texture is the same, // we don't need to remove as it was not modified. Just continue. - if (descriptor.Equals(ref DescriptorCache[id])) + if (descriptor.Equals(ref cachedDescriptor)) { continue; } + if (texture.HasOneReference()) + { + _channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor); + } + texture.DecrementReferenceCount(this, id); Items[id] = null; diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs index c116d9466..90f8e40f9 100644 --- a/Ryujinx.Graphics.Gpu/Window.cs +++ b/Ryujinx.Graphics.Gpu/Window.cs @@ -204,6 +204,8 @@ namespace Ryujinx.Graphics.Gpu Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range); + pt.Cache.Tick(); + texture.SynchronizeMemory(); ImageCrop crop = pt.Crop; From 43a83a401ea8101bf6d001fe6fe188e1c106245e Mon Sep 17 00:00:00 2001 From: Ac_K Date: Tue, 17 Jan 2023 04:57:21 +0100 Subject: [PATCH 298/737] Ava UI: Readd some infos to the GameList (#4302) --- Ryujinx.Ava/UI/Controls/GameListView.axaml | 53 ++++++++++++++++------ 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/Ryujinx.Ava/UI/Controls/GameListView.axaml b/Ryujinx.Ava/UI/Controls/GameListView.axaml index bb4e37b01..2ba4a204d 100644 --- a/Ryujinx.Ava/UI/Controls/GameListView.axaml +++ b/Ryujinx.Ava/UI/Controls/GameListView.axaml @@ -3,14 +3,14 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" - xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" d:DesignHeight="450" d:DesignWidth="800" - mc:Ignorable="d" - Focusable="True"> + Focusable="True" + mc:Ignorable="d"> @@ -130,7 +130,8 @@ - + + - + + + + + + + + Spacing="5"> - Date: Tue, 17 Jan 2023 01:13:24 -0300 Subject: [PATCH 299/737] Implement support for page sizes > 4KB (#4252) * Implement support for page sizes > 4KB * Check and work around more alignment issues * Was not meant to change this * Use MemoryBlock.GetPageSize() value for signal handler code * Do not take the path for private allocations if host supports 4KB pages * Add Flags attribute on MemoryMapFlags * Fix dirty region size with 16kb pages Would accidentally report a size that was too high (generally 16k instead of 4k, uploading 4x as much data) Co-authored-by: riperiperi --- ARMeilleure/Memory/IJitMemoryAllocator.cs | 2 + ARMeilleure/Signal/NativeSignalHandler.cs | 24 +- ARMeilleure/Translation/Translator.cs | 2 +- Ryujinx.Cpu/AddressSpace.cs | 470 ++++++++++++++++++ Ryujinx.Cpu/Jit/JitMemoryAllocator.cs | 2 + Ryujinx.Cpu/Jit/MemoryManager.cs | 43 +- Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs | 63 ++- Ryujinx.Cpu/PrivateMemoryAllocation.cs | 41 ++ Ryujinx.Cpu/PrivateMemoryAllocator.cs | 268 ++++++++++ Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 8 + Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 17 +- Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 5 + Ryujinx.HLE/HOS/Kernel/KernelContext.cs | 13 +- .../HOS/Kernel/Memory/KMemoryRegionManager.cs | 2 +- Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs | 56 ++- .../HOS/Kernel/Memory/KPageTableBase.cs | 70 ++- .../HOS/Kernel/Memory/KSharedMemory.cs | 13 +- .../HOS/Kernel/Memory/SharedMemoryStorage.cs | 2 +- .../MockVirtualMemoryManager.cs | 14 +- Ryujinx.Memory/AddressSpaceManager.cs | 137 +++-- Ryujinx.Memory/IVirtualMemoryManager.cs | 30 +- Ryujinx.Memory/MemoryBlock.cs | 2 +- Ryujinx.Memory/MemoryMapFlags.cs | 23 + Ryujinx.Memory/Range/HostMemoryRange.cs | 71 +++ Ryujinx.Memory/Tracking/MemoryTracking.cs | 10 +- Ryujinx.Memory/Tracking/MultiRegionHandle.cs | 21 +- Ryujinx.Memory/Tracking/RegionHandle.cs | 27 +- Ryujinx.Tests/Cpu/CpuTest.cs | 2 +- Ryujinx.Tests/Cpu/CpuTest32.cs | 2 +- 29 files changed, 1294 insertions(+), 146 deletions(-) create mode 100644 Ryujinx.Cpu/AddressSpace.cs create mode 100644 Ryujinx.Cpu/PrivateMemoryAllocation.cs create mode 100644 Ryujinx.Cpu/PrivateMemoryAllocator.cs create mode 100644 Ryujinx.Memory/MemoryMapFlags.cs create mode 100644 Ryujinx.Memory/Range/HostMemoryRange.cs diff --git a/ARMeilleure/Memory/IJitMemoryAllocator.cs b/ARMeilleure/Memory/IJitMemoryAllocator.cs index 5745a4bfe..19b696b0a 100644 --- a/ARMeilleure/Memory/IJitMemoryAllocator.cs +++ b/ARMeilleure/Memory/IJitMemoryAllocator.cs @@ -4,5 +4,7 @@ { IJitMemoryBlock Allocate(ulong size); IJitMemoryBlock Reserve(ulong size); + + ulong GetPageSize(); } } diff --git a/ARMeilleure/Signal/NativeSignalHandler.cs b/ARMeilleure/Signal/NativeSignalHandler.cs index da02f76a8..e8dc6ddaf 100644 --- a/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/ARMeilleure/Signal/NativeSignalHandler.cs @@ -71,8 +71,8 @@ namespace ARMeilleure.Signal private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005; - private static ulong _pageSize = GetPageSize(); - private static ulong _pageMask = _pageSize - 1; + private static ulong _pageSize; + private static ulong _pageMask; private static IntPtr _handlerConfig; private static IntPtr _signalHandlerPtr; @@ -81,19 +81,6 @@ namespace ARMeilleure.Signal private static readonly object _lock = new object(); private static bool _initialized; - private static ulong GetPageSize() - { - // TODO: This needs to be based on the current memory manager configuration. - if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) - { - return 1UL << 14; - } - else - { - return 1UL << 12; - } - } - static NativeSignalHandler() { _handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf()); @@ -102,12 +89,12 @@ namespace ARMeilleure.Signal config = new SignalHandlerConfig(); } - public static void InitializeJitCache(IJitMemoryAllocator allocator) + public static void Initialize(IJitMemoryAllocator allocator) { JitCache.Initialize(allocator); } - public static void InitializeSignalHandler(Func customSignalHandlerFactory = null) + public static void InitializeSignalHandler(ulong pageSize, Func customSignalHandlerFactory = null) { if (_initialized) return; @@ -115,6 +102,9 @@ namespace ARMeilleure.Signal { if (_initialized) return; + _pageSize = pageSize; + _pageMask = pageSize - 1; + ref SignalHandlerConfig config = ref GetConfigRef(); if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 75c4df23e..cbf6baa00 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -81,7 +81,7 @@ namespace ARMeilleure.Translation if (memory.Type.IsHostMapped()) { - NativeSignalHandler.InitializeSignalHandler(); + NativeSignalHandler.InitializeSignalHandler(allocator.GetPageSize()); } } diff --git a/Ryujinx.Cpu/AddressSpace.cs b/Ryujinx.Cpu/AddressSpace.cs new file mode 100644 index 000000000..cea3b56d2 --- /dev/null +++ b/Ryujinx.Cpu/AddressSpace.cs @@ -0,0 +1,470 @@ +using Ryujinx.Common; +using Ryujinx.Common.Collections; +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Cpu +{ + class AddressSpace : IDisposable + { + private const ulong PageSize = 0x1000; + + private const int DefaultBlockAlignment = 1 << 20; + + private enum MappingType : byte + { + None, + Private, + Shared + } + + private class Mapping : IntrusiveRedBlackTreeNode, IComparable + { + public ulong Address { get; private set; } + public ulong Size { get; private set; } + public ulong EndAddress => Address + Size; + public MappingType Type { get; private set; } + + public Mapping(ulong address, ulong size, MappingType type) + { + Address = address; + Size = size; + Type = type; + } + + public Mapping Split(ulong splitAddress) + { + ulong leftSize = splitAddress - Address; + ulong rightSize = EndAddress - splitAddress; + + Mapping left = new Mapping(Address, leftSize, Type); + + Address = splitAddress; + Size = rightSize; + + return left; + } + + public void UpdateState(MappingType newType) + { + Type = newType; + } + + public void Extend(ulong sizeDelta) + { + Size += sizeDelta; + } + + public int CompareTo(Mapping other) + { + if (Address < other.Address) + { + return -1; + } + else if (Address <= other.EndAddress - 1UL) + { + return 0; + } + else + { + return 1; + } + } + } + + private class PrivateMapping : IntrusiveRedBlackTreeNode, IComparable + { + public ulong Address { get; private set; } + public ulong Size { get; private set; } + public ulong EndAddress => Address + Size; + public PrivateMemoryAllocation PrivateAllocation { get; private set; } + + public PrivateMapping(ulong address, ulong size, PrivateMemoryAllocation privateAllocation) + { + Address = address; + Size = size; + PrivateAllocation = privateAllocation; + } + + public PrivateMapping Split(ulong splitAddress) + { + ulong leftSize = splitAddress - Address; + ulong rightSize = EndAddress - splitAddress; + + (var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize); + + PrivateMapping left = new PrivateMapping(Address, leftSize, leftAllocation); + + Address = splitAddress; + Size = rightSize; + + return left; + } + + public void Map(MemoryBlock baseBlock, MemoryBlock mirrorBlock, PrivateMemoryAllocation newAllocation) + { + baseBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size); + mirrorBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size); + PrivateAllocation = newAllocation; + } + + public void Unmap(MemoryBlock baseBlock, MemoryBlock mirrorBlock) + { + if (PrivateAllocation.IsValid) + { + baseBlock.UnmapView(PrivateAllocation.Memory, Address, Size); + mirrorBlock.UnmapView(PrivateAllocation.Memory, Address, Size); + PrivateAllocation.Dispose(); + } + + PrivateAllocation = default; + } + + public void Extend(ulong sizeDelta) + { + Size += sizeDelta; + } + + public int CompareTo(PrivateMapping other) + { + if (Address < other.Address) + { + return -1; + } + else if (Address <= other.EndAddress - 1UL) + { + return 0; + } + else + { + return 1; + } + } + } + + private readonly MemoryBlock _backingMemory; + private readonly PrivateMemoryAllocator _privateMemoryAllocator; + private readonly IntrusiveRedBlackTree _mappingTree; + private readonly IntrusiveRedBlackTree _privateTree; + + private readonly object _treeLock; + + private readonly bool _supports4KBPages; + + public MemoryBlock Base { get; } + public MemoryBlock Mirror { get; } + + public AddressSpace(MemoryBlock backingMemory, ulong asSize, bool supports4KBPages) + { + if (!supports4KBPages) + { + _privateMemoryAllocator = new PrivateMemoryAllocator(DefaultBlockAlignment, MemoryAllocationFlags.Mirrorable | MemoryAllocationFlags.NoMap); + _mappingTree = new IntrusiveRedBlackTree(); + _privateTree = new IntrusiveRedBlackTree(); + _treeLock = new object(); + + _mappingTree.Add(new Mapping(0UL, asSize, MappingType.None)); + _privateTree.Add(new PrivateMapping(0UL, asSize, default)); + } + + _backingMemory = backingMemory; + _supports4KBPages = supports4KBPages; + + MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; + + Base = new MemoryBlock(asSize, asFlags); + Mirror = new MemoryBlock(asSize, asFlags); + } + + public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) + { + if (_supports4KBPages) + { + Base.MapView(_backingMemory, pa, va, size); + Mirror.MapView(_backingMemory, pa, va, size); + + return; + } + + lock (_treeLock) + { + ulong alignment = MemoryBlock.GetPageSize(); + bool isAligned = ((va | pa | size) & (alignment - 1)) == 0; + + if (flags.HasFlag(MemoryMapFlags.Private) && !isAligned) + { + Update(va, pa, size, MappingType.Private); + } + else + { + // The update method assumes that shared mappings are already aligned. + + if (!flags.HasFlag(MemoryMapFlags.Private)) + { + if ((va & (alignment - 1)) != (pa & (alignment - 1))) + { + throw new InvalidMemoryRegionException($"Virtual address 0x{va:X} and physical address 0x{pa:X} are misaligned and can't be aligned."); + } + + ulong endAddress = va + size; + va = BitUtils.AlignDown(va, alignment); + pa = BitUtils.AlignDown(pa, alignment); + size = BitUtils.AlignUp(endAddress, alignment) - va; + } + + Update(va, pa, size, MappingType.Shared); + } + } + } + + public void Unmap(ulong va, ulong size) + { + if (_supports4KBPages) + { + Base.UnmapView(_backingMemory, va, size); + Mirror.UnmapView(_backingMemory, va, size); + + return; + } + + lock (_treeLock) + { + Update(va, 0UL, size, MappingType.None); + } + } + + private void Update(ulong va, ulong pa, ulong size, MappingType type) + { + Mapping map = _mappingTree.GetNode(new Mapping(va, 1UL, MappingType.None)); + + Update(map, va, pa, size, type); + } + + private Mapping Update(Mapping map, ulong va, ulong pa, ulong size, MappingType type) + { + ulong endAddress = va + size; + + for (; map != null; map = map.Successor) + { + if (map.Address < va) + { + _mappingTree.Add(map.Split(va)); + } + + if (map.EndAddress > endAddress) + { + Mapping newMap = map.Split(endAddress); + _mappingTree.Add(newMap); + map = newMap; + } + + switch (type) + { + case MappingType.None: + if (map.Type == MappingType.Shared) + { + ulong startOffset = map.Address - va; + ulong mapVa = va + startOffset; + ulong mapSize = Math.Min(size - startOffset, map.Size); + ulong mapEndAddress = mapVa + mapSize; + ulong alignment = MemoryBlock.GetPageSize(); + + mapVa = BitUtils.AlignDown(mapVa, alignment); + mapEndAddress = BitUtils.AlignUp(mapEndAddress, alignment); + + mapSize = mapEndAddress - mapVa; + + Base.UnmapView(_backingMemory, mapVa, mapSize); + Mirror.UnmapView(_backingMemory, mapVa, mapSize); + } + else + { + UnmapPrivate(va, size); + } + break; + case MappingType.Private: + if (map.Type == MappingType.Shared) + { + throw new InvalidMemoryRegionException($"Private mapping request at 0x{va:X} with size 0x{size:X} overlaps shared mapping at 0x{map.Address:X} with size 0x{map.Size:X}."); + } + else + { + MapPrivate(va, size); + } + break; + case MappingType.Shared: + if (map.Type != MappingType.None) + { + throw new InvalidMemoryRegionException($"Shared mapping request at 0x{va:X} with size 0x{size:X} overlaps mapping at 0x{map.Address:X} with size 0x{map.Size:X}."); + } + else + { + ulong startOffset = map.Address - va; + ulong mapPa = pa + startOffset; + ulong mapVa = va + startOffset; + ulong mapSize = Math.Min(size - startOffset, map.Size); + + Base.MapView(_backingMemory, mapPa, mapVa, mapSize); + Mirror.MapView(_backingMemory, mapPa, mapVa, mapSize); + } + break; + } + + map.UpdateState(type); + map = TryCoalesce(map); + + if (map.EndAddress >= endAddress) + { + break; + } + } + + return map; + } + + private Mapping TryCoalesce(Mapping map) + { + Mapping previousMap = map.Predecessor; + Mapping nextMap = map.Successor; + + if (previousMap != null && CanCoalesce(previousMap, map)) + { + previousMap.Extend(map.Size); + _mappingTree.Remove(map); + map = previousMap; + } + + if (nextMap != null && CanCoalesce(map, nextMap)) + { + map.Extend(nextMap.Size); + _mappingTree.Remove(nextMap); + } + + return map; + } + + private static bool CanCoalesce(Mapping left, Mapping right) + { + return left.Type == right.Type; + } + + private void MapPrivate(ulong va, ulong size) + { + ulong endAddress = va + size; + + ulong alignment = MemoryBlock.GetPageSize(); + + // Expand the range outwards based on page size to ensure that at least the requested region is mapped. + ulong vaAligned = BitUtils.AlignDown(va, alignment); + ulong endAddressAligned = BitUtils.AlignUp(endAddress, alignment); + + ulong sizeAligned = endAddressAligned - vaAligned; + + PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default)); + + for (; map != null; map = map.Successor) + { + if (!map.PrivateAllocation.IsValid) + { + if (map.Address < vaAligned) + { + _privateTree.Add(map.Split(vaAligned)); + } + + if (map.EndAddress > endAddressAligned) + { + PrivateMapping newMap = map.Split(endAddressAligned); + _privateTree.Add(newMap); + map = newMap; + } + + map.Map(Base, Mirror, _privateMemoryAllocator.Allocate(map.Size, MemoryBlock.GetPageSize())); + } + + if (map.EndAddress >= endAddressAligned) + { + break; + } + } + } + + private void UnmapPrivate(ulong va, ulong size) + { + ulong endAddress = va + size; + + ulong alignment = MemoryBlock.GetPageSize(); + + // Shrink the range inwards based on page size to ensure we won't unmap memory that might be still in use. + ulong vaAligned = BitUtils.AlignUp(va, alignment); + ulong endAddressAligned = BitUtils.AlignDown(endAddress, alignment); + + if (endAddressAligned <= vaAligned) + { + return; + } + + ulong alignedSize = endAddressAligned - vaAligned; + + PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default)); + + for (; map != null; map = map.Successor) + { + if (map.PrivateAllocation.IsValid) + { + if (map.Address < vaAligned) + { + _privateTree.Add(map.Split(vaAligned)); + } + + if (map.EndAddress > endAddressAligned) + { + PrivateMapping newMap = map.Split(endAddressAligned); + _privateTree.Add(newMap); + map = newMap; + } + + map.Unmap(Base, Mirror); + map = TryCoalesce(map); + } + + if (map.EndAddress >= endAddressAligned) + { + break; + } + } + } + + private PrivateMapping TryCoalesce(PrivateMapping map) + { + PrivateMapping previousMap = map.Predecessor; + PrivateMapping nextMap = map.Successor; + + if (previousMap != null && CanCoalesce(previousMap, map)) + { + previousMap.Extend(map.Size); + _privateTree.Remove(map); + map = previousMap; + } + + if (nextMap != null && CanCoalesce(map, nextMap)) + { + map.Extend(nextMap.Size); + _privateTree.Remove(nextMap); + } + + return map; + } + + private static bool CanCoalesce(PrivateMapping left, PrivateMapping right) + { + return !left.PrivateAllocation.IsValid && !right.PrivateAllocation.IsValid; + } + + public void Dispose() + { + _privateMemoryAllocator.Dispose(); + Base.Dispose(); + Mirror.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs b/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs index 0cf35c17b..4aa78d06c 100644 --- a/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs +++ b/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs @@ -7,5 +7,7 @@ namespace Ryujinx.Cpu.Jit { public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None); public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Jit); + + public ulong GetPageSize() => MemoryBlock.GetPageSize(); } } diff --git a/Ryujinx.Cpu/Jit/MemoryManager.cs b/Ryujinx.Cpu/Jit/MemoryManager.cs index 21c50d51f..014d843b5 100644 --- a/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -28,6 +28,9 @@ namespace Ryujinx.Cpu.Jit private readonly MemoryBlock _backingMemory; private readonly InvalidAccessHandler _invalidAccessHandler; + /// + public bool Supports4KBPages => true; + /// /// Address space width in bits. /// @@ -76,7 +79,7 @@ namespace Ryujinx.Cpu.Jit } /// - public void Map(ulong va, ulong pa, ulong size) + public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) { AssertValidAddressAndSize(va, size); @@ -91,9 +94,16 @@ namespace Ryujinx.Cpu.Jit pa += PageSize; remainingSize -= PageSize; } + Tracking.Map(oVa, size); } + /// + public void MapForeign(ulong va, nuint hostPointer, ulong size) + { + throw new NotSupportedException(); + } + /// public void Unmap(ulong va, ulong size) { @@ -378,6 +388,32 @@ namespace Ryujinx.Cpu.Jit return true; } + /// + public IEnumerable GetHostRegions(ulong va, ulong size) + { + if (size == 0) + { + return Enumerable.Empty(); + } + + var guestRegions = GetPhysicalRegionsImpl(va, size); + if (guestRegions == null) + { + return null; + } + + var regions = new HostMemoryRange[guestRegions.Count]; + + for (int i = 0; i < regions.Length; i++) + { + var guestRegion = guestRegions[i]; + IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); + regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); + } + + return regions; + } + /// public IEnumerable GetPhysicalRegions(ulong va, ulong size) { @@ -386,6 +422,11 @@ namespace Ryujinx.Cpu.Jit return Enumerable.Empty(); } + return GetPhysicalRegionsImpl(va, size); + } + + private List GetPhysicalRegionsImpl(ulong va, ulong size) + { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { return null; diff --git a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index c4e59db9f..856b6b9b0 100644 --- a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -5,6 +5,7 @@ using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -37,20 +38,21 @@ namespace Ryujinx.Cpu.Jit private readonly InvalidAccessHandler _invalidAccessHandler; private readonly bool _unsafeMode; - private readonly MemoryBlock _addressSpace; - private readonly MemoryBlock _addressSpaceMirror; + private readonly AddressSpace _addressSpace; private readonly ulong _addressSpaceSize; - private readonly MemoryBlock _backingMemory; private readonly PageTable _pageTable; private readonly MemoryEhMeilleure _memoryEh; private readonly ulong[] _pageBitmap; + /// + public bool Supports4KBPages => MemoryBlock.GetPageSize() == PageSize; + public int AddressSpaceBits { get; } - public IntPtr PageTablePointer => _addressSpace.Pointer; + public IntPtr PageTablePointer => _addressSpace.Base.Pointer; public MemoryManagerType Type => _unsafeMode ? MemoryManagerType.HostMappedUnsafe : MemoryManagerType.HostMapped; @@ -67,7 +69,6 @@ namespace Ryujinx.Cpu.Jit /// Optional function to handle invalid memory accesses public MemoryManagerHostMapped(MemoryBlock backingMemory, ulong addressSpaceSize, bool unsafeMode, InvalidAccessHandler invalidAccessHandler = null) { - _backingMemory = backingMemory; _pageTable = new PageTable(); _invalidAccessHandler = invalidAccessHandler; _unsafeMode = unsafeMode; @@ -86,13 +87,10 @@ namespace Ryujinx.Cpu.Jit _pageBitmap = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))]; - MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; + _addressSpace = new AddressSpace(backingMemory, asSize, Supports4KBPages); - _addressSpace = new MemoryBlock(asSize, asFlags); - _addressSpaceMirror = new MemoryBlock(asSize, asFlags); - - Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler); - _memoryEh = new MemoryEhMeilleure(_addressSpace, _addressSpaceMirror, Tracking); + Tracking = new MemoryTracking(this, (int)MemoryBlock.GetPageSize(), invalidAccessHandler); + _memoryEh = new MemoryEhMeilleure(_addressSpace.Base, _addressSpace.Mirror, Tracking); } /// @@ -145,18 +143,23 @@ namespace Ryujinx.Cpu.Jit } /// - public void Map(ulong va, ulong pa, ulong size) + public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) { AssertValidAddressAndSize(va, size); - _addressSpace.MapView(_backingMemory, pa, va, size); - _addressSpaceMirror.MapView(_backingMemory, pa, va, size); + _addressSpace.Map(va, pa, size, flags); AddMapping(va, size); PtMap(va, pa, size); Tracking.Map(va, size); } + /// + public void MapForeign(ulong va, nuint hostPointer, ulong size) + { + throw new NotSupportedException(); + } + /// public void Unmap(ulong va, ulong size) { @@ -167,8 +170,7 @@ namespace Ryujinx.Cpu.Jit RemoveMapping(va, size); PtUnmap(va, size); - _addressSpace.UnmapView(_backingMemory, va, size); - _addressSpaceMirror.UnmapView(_backingMemory, va, size); + _addressSpace.Unmap(va, size); } private void PtMap(ulong va, ulong pa, ulong size) @@ -201,7 +203,7 @@ namespace Ryujinx.Cpu.Jit { AssertMapped(va, (ulong)Unsafe.SizeOf()); - return _addressSpaceMirror.Read(va); + return _addressSpace.Mirror.Read(va); } catch (InvalidMemoryRegionException) { @@ -241,7 +243,7 @@ namespace Ryujinx.Cpu.Jit { AssertMapped(va, (ulong)data.Length); - _addressSpaceMirror.Read(va, data); + _addressSpace.Mirror.Read(va, data); } catch (InvalidMemoryRegionException) { @@ -260,7 +262,7 @@ namespace Ryujinx.Cpu.Jit { SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), write: true); - _addressSpaceMirror.Write(va, value); + _addressSpace.Mirror.Write(va, value); } catch (InvalidMemoryRegionException) { @@ -278,7 +280,7 @@ namespace Ryujinx.Cpu.Jit { SignalMemoryTracking(va, (ulong)data.Length, write: true); - _addressSpaceMirror.Write(va, data); + _addressSpace.Mirror.Write(va, data); } catch (InvalidMemoryRegionException) { @@ -296,7 +298,7 @@ namespace Ryujinx.Cpu.Jit { AssertMapped(va, (ulong)data.Length); - _addressSpaceMirror.Write(va, data); + _addressSpace.Mirror.Write(va, data); } catch (InvalidMemoryRegionException) { @@ -314,7 +316,7 @@ namespace Ryujinx.Cpu.Jit { SignalMemoryTracking(va, (ulong)data.Length, false); - Span target = _addressSpaceMirror.GetSpan(va, data.Length); + Span target = _addressSpace.Mirror.GetSpan(va, data.Length); bool changed = !data.SequenceEqual(target); if (changed) @@ -347,7 +349,7 @@ namespace Ryujinx.Cpu.Jit AssertMapped(va, (ulong)size); } - return _addressSpaceMirror.GetSpan(va, size); + return _addressSpace.Mirror.GetSpan(va, size); } /// @@ -362,7 +364,7 @@ namespace Ryujinx.Cpu.Jit AssertMapped(va, (ulong)size); } - return _addressSpaceMirror.GetWritableRegion(va, size); + return _addressSpace.Mirror.GetWritableRegion(va, size); } /// @@ -370,7 +372,7 @@ namespace Ryujinx.Cpu.Jit { SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), true); - return ref _addressSpaceMirror.GetRef(va); + return ref _addressSpace.Mirror.GetRef(va); } /// @@ -454,6 +456,14 @@ namespace Ryujinx.Cpu.Jit return true; } + /// + public IEnumerable GetHostRegions(ulong va, ulong size) + { + AssertValidAddressAndSize(va, size); + + return Enumerable.Repeat(new HostMemoryRange((nuint)(ulong)_addressSpace.Mirror.GetPointer(va, size), size), 1); + } + /// public IEnumerable GetPhysicalRegions(ulong va, ulong size) { @@ -692,7 +702,7 @@ namespace Ryujinx.Cpu.Jit _ => MemoryPermission.None }; - _addressSpace.Reprotect(va, size, protection, false); + _addressSpace.Base.Reprotect(va, size, protection, false); } /// @@ -799,7 +809,6 @@ namespace Ryujinx.Cpu.Jit protected override void Destroy() { _addressSpace.Dispose(); - _addressSpaceMirror.Dispose(); _memoryEh.Dispose(); } diff --git a/Ryujinx.Cpu/PrivateMemoryAllocation.cs b/Ryujinx.Cpu/PrivateMemoryAllocation.cs new file mode 100644 index 000000000..1327880e2 --- /dev/null +++ b/Ryujinx.Cpu/PrivateMemoryAllocation.cs @@ -0,0 +1,41 @@ +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Cpu +{ + struct PrivateMemoryAllocation : IDisposable + { + private readonly PrivateMemoryAllocator _owner; + private readonly PrivateMemoryAllocator.Block _block; + + public bool IsValid => _owner != null; + public MemoryBlock Memory => _block?.Memory; + public ulong Offset { get; } + public ulong Size { get; } + + public PrivateMemoryAllocation( + PrivateMemoryAllocator owner, + PrivateMemoryAllocator.Block block, + ulong offset, + ulong size) + { + _owner = owner; + _block = block; + Offset = offset; + Size = size; + } + + public (PrivateMemoryAllocation, PrivateMemoryAllocation) Split(ulong splitOffset) + { + PrivateMemoryAllocation left = new PrivateMemoryAllocation(_owner, _block, Offset, splitOffset); + PrivateMemoryAllocation right = new PrivateMemoryAllocation(_owner, _block, Offset + splitOffset, Size - splitOffset); + + return (left, right); + } + + public void Dispose() + { + _owner.Free(_block, Offset, Size); + } + } +} diff --git a/Ryujinx.Cpu/PrivateMemoryAllocator.cs b/Ryujinx.Cpu/PrivateMemoryAllocator.cs new file mode 100644 index 000000000..cbf1f1d9c --- /dev/null +++ b/Ryujinx.Cpu/PrivateMemoryAllocator.cs @@ -0,0 +1,268 @@ +using Ryujinx.Common; +using Ryujinx.Memory; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Ryujinx.Cpu +{ + class PrivateMemoryAllocator : PrivateMemoryAllocatorImpl + { + public const ulong InvalidOffset = ulong.MaxValue; + + public class Block : IComparable + { + public MemoryBlock Memory { get; private set; } + public ulong Size { get; } + + private struct Range : IComparable + { + public ulong Offset { get; } + public ulong Size { get; } + + public Range(ulong offset, ulong size) + { + Offset = offset; + Size = size; + } + + public int CompareTo(Range other) + { + return Offset.CompareTo(other.Offset); + } + } + + private readonly List _freeRanges; + + public Block(MemoryBlock memory, ulong size) + { + Memory = memory; + Size = size; + _freeRanges = new List + { + new Range(0, size) + }; + } + + public ulong Allocate(ulong size, ulong alignment) + { + for (int i = 0; i < _freeRanges.Count; i++) + { + var range = _freeRanges[i]; + + ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment); + ulong sizeDelta = alignedOffset - range.Offset; + ulong usableSize = range.Size - sizeDelta; + + if (sizeDelta < range.Size && usableSize >= size) + { + _freeRanges.RemoveAt(i); + + if (sizeDelta != 0) + { + InsertFreeRange(range.Offset, sizeDelta); + } + + ulong endOffset = range.Offset + range.Size; + ulong remainingSize = endOffset - (alignedOffset + size); + if (remainingSize != 0) + { + InsertFreeRange(endOffset - remainingSize, remainingSize); + } + + return alignedOffset; + } + } + + return InvalidOffset; + } + + public void Free(ulong offset, ulong size) + { + InsertFreeRangeComingled(offset, size); + } + + private void InsertFreeRange(ulong offset, ulong size) + { + var range = new Range(offset, size); + int index = _freeRanges.BinarySearch(range); + if (index < 0) + { + index = ~index; + } + + _freeRanges.Insert(index, range); + } + + private void InsertFreeRangeComingled(ulong offset, ulong size) + { + ulong endOffset = offset + size; + var range = new Range(offset, size); + int index = _freeRanges.BinarySearch(range); + if (index < 0) + { + index = ~index; + } + + if (index < _freeRanges.Count && _freeRanges[index].Offset == endOffset) + { + endOffset = _freeRanges[index].Offset + _freeRanges[index].Size; + _freeRanges.RemoveAt(index); + } + + if (index > 0 && _freeRanges[index - 1].Offset + _freeRanges[index - 1].Size == offset) + { + offset = _freeRanges[index - 1].Offset; + _freeRanges.RemoveAt(--index); + } + + range = new Range(offset, endOffset - offset); + + _freeRanges.Insert(index, range); + } + + public bool IsTotallyFree() + { + if (_freeRanges.Count == 1 && _freeRanges[0].Size == Size) + { + Debug.Assert(_freeRanges[0].Offset == 0); + return true; + } + + return false; + } + + public int CompareTo(Block other) + { + return Size.CompareTo(other.Size); + } + + public virtual void Destroy() + { + Memory.Dispose(); + } + } + + public PrivateMemoryAllocator(int blockAlignment, MemoryAllocationFlags allocationFlags) : base(blockAlignment, allocationFlags) + { + } + + public PrivateMemoryAllocation Allocate(ulong size, ulong alignment) + { + var allocation = Allocate(size, alignment, CreateBlock); + + return new PrivateMemoryAllocation(this, allocation.Block, allocation.Offset, allocation.Size); + } + + private Block CreateBlock(MemoryBlock memory, ulong size) + { + return new Block(memory, size); + } + } + + class PrivateMemoryAllocatorImpl : IDisposable where T : PrivateMemoryAllocator.Block + { + private const ulong InvalidOffset = ulong.MaxValue; + + public struct Allocation + { + public T Block { get; } + public ulong Offset { get; } + public ulong Size { get; } + + public Allocation(T block, ulong offset, ulong size) + { + Block = block; + Offset = offset; + Size = size; + } + } + + private readonly List _blocks; + + private readonly int _blockAlignment; + private readonly MemoryAllocationFlags _allocationFlags; + + public PrivateMemoryAllocatorImpl(int blockAlignment, MemoryAllocationFlags allocationFlags) + { + _blocks = new List(); + _blockAlignment = blockAlignment; + _allocationFlags = allocationFlags; + } + + protected Allocation Allocate(ulong size, ulong alignment, Func createBlock) + { + // Ensure we have a sane alignment value. + if ((ulong)(int)alignment != alignment || (int)alignment <= 0) + { + throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}."); + } + + for (int i = 0; i < _blocks.Count; i++) + { + var block = _blocks[i]; + + if (block.Size >= size) + { + ulong offset = block.Allocate(size, alignment); + if (offset != InvalidOffset) + { + return new Allocation(block, offset, size); + } + } + } + + ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment); + + var memory = new MemoryBlock(blockAlignedSize, _allocationFlags); + var newBlock = createBlock(memory, blockAlignedSize); + + InsertBlock(newBlock); + + ulong newBlockOffset = newBlock.Allocate(size, alignment); + Debug.Assert(newBlockOffset != InvalidOffset); + + return new Allocation(newBlock, newBlockOffset, size); + } + + public void Free(PrivateMemoryAllocator.Block block, ulong offset, ulong size) + { + block.Free(offset, size); + + if (block.IsTotallyFree()) + { + for (int i = 0; i < _blocks.Count; i++) + { + if (_blocks[i] == block) + { + _blocks.RemoveAt(i); + break; + } + } + + block.Destroy(); + } + } + + private void InsertBlock(T block) + { + int index = _blocks.BinarySearch(block); + if (index < 0) + { + index = ~index; + } + + _blocks.Insert(index, block); + } + + public void Dispose() + { + for (int i = 0; i < _blocks.Count; i++) + { + _blocks[i].Destroy(); + } + + _blocks.Clear(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index c167dc0d3..896e11a5d 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1420,6 +1420,14 @@ namespace Ryujinx.Graphics.Gpu.Image /// The size of the flushing memory access public void FlushAction(TextureGroupHandle handle, ulong address, ulong size) { + // If the page size is larger than 4KB, we will have a lot of false positives for flushing. + // Let's avoid flushing textures that are unlikely to be read from CPU to improve performance + // on those platforms. + if (!_physicalMemory.Supports4KBPages && !Storage.Info.IsLinear && !_context.IsGpuThread()) + { + return; + } + // There is a small gap here where the action is removed but _actionRegistered is still 1. // In this case it will skip registering the action, but here we are already handling it, // so there shouldn't be any issue as it's the same handler for all actions. diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 842249f34..a624386ed 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -470,19 +470,16 @@ namespace Ryujinx.Graphics.Gpu.Memory return false; } - if (address < Address) + ulong maxAddress = Math.Max(address, Address); + ulong minEndAddress = Math.Min(address + size, Address + Size); + + if (maxAddress >= minEndAddress) { - address = Address; + // Access doesn't overlap. + return false; } - ulong maxSize = Address + Size - address; - - if (size > maxSize) - { - size = maxSize; - } - - ForceDirty(address, size); + ForceDirty(maxAddress, minEndAddress - maxAddress); return true; } diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index 051838f1f..c1fc0c5cd 100644 --- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -21,6 +21,11 @@ namespace Ryujinx.Graphics.Gpu.Memory private IVirtualMemoryManagerTracked _cpuMemory; private int _referenceCount; + /// + /// Indicates whenever the memory manager supports 4KB pages. + /// + public bool Supports4KBPages => _cpuMemory.Supports4KBPages; + /// /// In-memory shader cache. /// diff --git a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs index 6c58e1972..ccc5c0f0b 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs @@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Kernel KernelConstants.UserSlabHeapItemSize, KernelConstants.UserSlabHeapSize); - memory.Commit(KernelConstants.UserSlabHeapBase - DramMemoryMap.DramBase, KernelConstants.UserSlabHeapSize); + CommitMemory(KernelConstants.UserSlabHeapBase - DramMemoryMap.DramBase, KernelConstants.UserSlabHeapSize); CriticalSection = new KCriticalSection(this); Schedulers = new KScheduler[KScheduler.CpuCoresCount]; @@ -119,6 +119,17 @@ namespace Ryujinx.HLE.HOS.Kernel new Thread(PreemptionThreadStart) { Name = "HLE.PreemptionThread" }.Start(); } + public void CommitMemory(ulong address, ulong size) + { + ulong alignment = MemoryBlock.GetPageSize(); + ulong endAddress = address + size; + + address &= ~(alignment - 1); + endAddress = (endAddress + (alignment - 1)) & ~(alignment - 1); + + Memory.Commit(address, endAddress - address); + } + public ulong NewThreadUid() { return Interlocked.Increment(ref _threadUid) - 1; diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs index 5e6273b86..4596b15d5 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (address != 0) { IncrementPagesReferenceCount(address, pagesCount); - context.Memory.Commit(address - DramMemoryMap.DramBase, pagesCount * KPageTableBase.PageSize); + context.CommitMemory(address - DramMemoryMap.DramBase, pagesCount * KPageTableBase.PageSize); } return address; diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs index 9b7c99ba1..28e9f90aa 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs @@ -1,6 +1,8 @@ using Ryujinx.Horizon.Common; using Ryujinx.Memory; +using Ryujinx.Memory.Range; using System; +using System.Collections.Generic; using System.Diagnostics; namespace Ryujinx.HLE.HOS.Kernel.Memory @@ -9,11 +11,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { private readonly IVirtualMemoryManager _cpuMemory; + protected override bool Supports4KBPages => _cpuMemory.Supports4KBPages; + public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context) { _cpuMemory = cpuMemory; } + /// + protected override IEnumerable GetHostRegions(ulong va, ulong size) + { + return _cpuMemory.GetHostRegions(va, size); + } + /// protected override void GetPhysicalRegions(ulong va, ulong size, KPageList pageList) { @@ -43,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - result = MapPages(dst, pageList, newDstPermission, false, 0); + result = MapPages(dst, pageList, newDstPermission, MemoryMapFlags.Private, false, 0); if (result != Result.Success) { @@ -81,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (result != Result.Success) { - Result mapResult = MapPages(dst, dstPageList, oldDstPermission, false, 0); + Result mapResult = MapPages(dst, dstPageList, oldDstPermission, MemoryMapFlags.Private, false, 0); Debug.Assert(mapResult == Result.Success); } @@ -89,13 +99,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } /// - protected override Result MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages, byte fillValue) + protected override Result MapPages( + ulong dstVa, + ulong pagesCount, + ulong srcPa, + KMemoryPermission permission, + MemoryMapFlags flags, + bool shouldFillPages, + byte fillValue) { ulong size = pagesCount * PageSize; - Context.Memory.Commit(srcPa - DramMemoryMap.DramBase, size); + Context.CommitMemory(srcPa - DramMemoryMap.DramBase, size); - _cpuMemory.Map(dstVa, srcPa - DramMemoryMap.DramBase, size); + _cpuMemory.Map(dstVa, srcPa - DramMemoryMap.DramBase, size, flags); if (DramMemoryMap.IsHeapPhysicalAddress(srcPa)) { @@ -111,7 +128,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } /// - protected override Result MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages, byte fillValue) + protected override Result MapPages( + ulong address, + KPageList pageList, + KMemoryPermission permission, + MemoryMapFlags flags, + bool shouldFillPages, + byte fillValue) { using var scopedPageList = new KScopedPageList(Context.MemoryManager, pageList); @@ -122,9 +145,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong addr = pageNode.Address - DramMemoryMap.DramBase; ulong size = pageNode.PagesCount * PageSize; - Context.Memory.Commit(addr, size); + Context.CommitMemory(addr, size); - _cpuMemory.Map(currentVa, addr, size); + _cpuMemory.Map(currentVa, addr, size, flags); if (shouldFillPages) { @@ -139,6 +162,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return Result.Success; } + /// + protected override Result MapForeign(IEnumerable regions, ulong va, ulong size) + { + ulong offset = 0; + + foreach (var region in regions) + { + _cpuMemory.MapForeign(va + offset, region.Address, region.Size); + + offset += region.Size; + } + + return Result.Success; + } + /// protected override Result Unmap(ulong address, ulong pagesCount) { @@ -188,4 +226,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _cpuMemory.Write(va, data); } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index e19e22c87..bd7d5725b 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -1,6 +1,8 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Memory; +using Ryujinx.Memory.Range; using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; @@ -29,6 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private const int MaxBlocksNeededForInsertion = 2; protected readonly KernelContext Context; + protected virtual bool Supports4KBPages => true; public ulong AddrSpaceStart { get; private set; } public ulong AddrSpaceEnd { get; private set; } @@ -366,7 +369,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.OutOfResource; } - Result result = MapPages(address, pageList, permission); + Result result = MapPages(address, pageList, permission, MemoryMapFlags.None); if (result == Result.Success) { @@ -502,7 +505,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (paIsValid) { - result = MapPages(address, pagesCount, srcPa, permission); + result = MapPages(address, pagesCount, srcPa, permission, MemoryMapFlags.Private); } else { @@ -565,7 +568,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory using var _ = new OnScopeExit(() => pageList.DecrementPagesReferenceCount(Context.MemoryManager)); - return MapPages(address, pageList, permission); + return MapPages(address, pageList, permission, MemoryMapFlags.Private); } public Result MapProcessCodeMemory(ulong dst, ulong src, ulong size) @@ -746,7 +749,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidMemState; } - result = MapPages(_currentHeapAddr, pageList, KMemoryPermission.ReadAndWrite, true, (byte)_heapFillValue); + result = MapPages(_currentHeapAddr, pageList, KMemoryPermission.ReadAndWrite, MemoryMapFlags.Private, true, (byte)_heapFillValue); if (result != Result.Success) { @@ -1334,7 +1337,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong currentPagesCount = Math.Min(srcPaPages, dstVaPages); - MapPages(dstVa, currentPagesCount, srcPa, KMemoryPermission.ReadAndWrite); + MapPages(dstVa, currentPagesCount, srcPa, KMemoryPermission.ReadAndWrite, MemoryMapFlags.Private); dstVa += currentPagesCount * PageSize; srcPa += currentPagesCount * PageSize; @@ -1878,7 +1881,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Context.Memory.Fill(GetDramAddressFromPa(firstPageFillAddress), unusedSizeAfter, (byte)_ipcFillValue); } - Result result = MapPages(currentVa, 1, dstFirstPagePa, permission); + Result result = MapPages(currentVa, 1, dstFirstPagePa, permission, MemoryMapFlags.Private); if (result != Result.Success) { @@ -1894,10 +1897,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong alignedSize = endAddrTruncated - addressRounded; - KPageList pageList = new KPageList(); - srcPageTable.GetPhysicalRegions(addressRounded, alignedSize, pageList); + Result result; - Result result = MapPages(currentVa, pageList, permission); + if (srcPageTable.Supports4KBPages) + { + KPageList pageList = new KPageList(); + srcPageTable.GetPhysicalRegions(addressRounded, alignedSize, pageList); + + result = MapPages(currentVa, pageList, permission, MemoryMapFlags.None); + } + else + { + result = MapForeign(srcPageTable.GetHostRegions(addressRounded, alignedSize), currentVa, alignedSize); + } if (result != Result.Success) { @@ -1932,7 +1944,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Context.Memory.Fill(GetDramAddressFromPa(lastPageFillAddr), unusedSizeAfter, (byte)_ipcFillValue); - Result result = MapPages(currentVa, 1, dstLastPagePa, permission); + Result result = MapPages(currentVa, 1, dstLastPagePa, permission, MemoryMapFlags.Private); if (result != Result.Success) { @@ -2884,6 +2896,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return StackRegionStart > address || address + size - 1 > StackRegionEnd - 1; } + /// + /// Gets the host regions that make up the given virtual address region. + /// If any part of the virtual region is unmapped, null is returned. + /// + /// Virtual address of the range + /// Size of the range + /// The host regions + /// Throw for unhandled invalid or unmapped memory accesses + protected abstract IEnumerable GetHostRegions(ulong va, ulong size); + /// /// Gets the physical regions that make up the given virtual address region. /// If any part of the virtual region is unmapped, null is returned. @@ -2936,10 +2958,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Number of pages to map /// Physical address where the pages should be mapped. May be ignored if aliasing is not supported /// Permission of the region to be mapped + /// Flags controlling the memory map operation /// Indicate if the pages should be filled with the value /// The value used to fill pages when is set to true /// Result of the mapping operation - protected abstract Result MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); + protected abstract Result MapPages( + ulong dstVa, + ulong pagesCount, + ulong srcPa, + KMemoryPermission permission, + MemoryMapFlags flags, + bool shouldFillPages = false, + byte fillValue = 0); /// /// Maps a region of memory into the specified physical memory region. @@ -2947,10 +2977,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Destination virtual address that should be mapped /// List of physical memory pages where the pages should be mapped. May be ignored if aliasing is not supported /// Permission of the region to be mapped + /// Flags controlling the memory map operation /// Indicate if the pages should be filled with the value /// The value used to fill pages when is set to true /// Result of the mapping operation - protected abstract Result MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); + protected abstract Result MapPages( + ulong address, + KPageList pageList, + KMemoryPermission permission, + MemoryMapFlags flags, + bool shouldFillPages = false, + byte fillValue = 0); + + /// + /// Maps pages into an arbitrary host memory location. + /// + /// Host regions to be mapped into the specified virtual memory region + /// Destination virtual address of the range on this page table + /// Size of the range + /// Result of the mapping operation + protected abstract Result MapForeign(IEnumerable regions, ulong va, ulong size); /// /// Unmaps a region of memory that was previously mapped with one of the page mapping methods. diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs index 2dbaf3cd8..5ec3cd724 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.Horizon.Common; +using Ryujinx.Memory; namespace Ryujinx.HLE.HOS.Kernel.Memory { @@ -48,7 +49,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidPermission; } - return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission); + // On platforms with page size > 4 KB, this can fail due to the address not being page aligned, + // we can return an error to force the application to retry with a different address. + + try + { + return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission); + } + catch (InvalidMemoryRegionException) + { + return KernelResult.InvalidMemState; + } } public Result UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process) diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs b/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs index 167e0aa90..c68b73695 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong address = pageNode.Address - DramMemoryMap.DramBase; ulong size = pageNode.PagesCount * KPageTableBase.PageSize; - context.Memory.Commit(address, size); + context.CommitMemory(address, size); } } diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 6c4422829..06eb4729e 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -6,6 +6,8 @@ namespace Ryujinx.Memory.Tests { public class MockVirtualMemoryManager : IVirtualMemoryManager { + public bool Supports4KBPages => true; + public bool NoMappings = false; public event Action OnProtect; @@ -14,7 +16,12 @@ namespace Ryujinx.Memory.Tests { } - public void Map(ulong va, ulong pa, ulong size) + public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) + { + throw new NotImplementedException(); + } + + public void MapForeign(ulong va, nuint hostAddress, ulong size) { throw new NotImplementedException(); } @@ -64,6 +71,11 @@ namespace Ryujinx.Memory.Tests throw new NotImplementedException(); } + IEnumerable IVirtualMemoryManager.GetHostRegions(ulong va, ulong size) + { + throw new NotImplementedException(); + } + IEnumerable IVirtualMemoryManager.GetPhysicalRegions(ulong va, ulong size) { return NoMappings ? new MemoryRange[0] : new MemoryRange[] { new MemoryRange(va, size) }; diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs index ffe880bf8..b532ce5e0 100644 --- a/Ryujinx.Memory/AddressSpaceManager.cs +++ b/Ryujinx.Memory/AddressSpaceManager.cs @@ -13,9 +13,12 @@ namespace Ryujinx.Memory /// public sealed class AddressSpaceManager : IVirtualMemoryManager, IWritableBlock { - public const int PageBits = PageTable.PageBits; - public const int PageSize = PageTable.PageSize; - public const int PageMask = PageTable.PageMask; + public const int PageBits = PageTable.PageBits; + public const int PageSize = PageTable.PageSize; + public const int PageMask = PageTable.PageMask; + + /// + public bool Supports4KBPages => true; /// /// Address space width in bits. @@ -25,7 +28,7 @@ namespace Ryujinx.Memory private readonly ulong _addressSpaceSize; private readonly MemoryBlock _backingMemory; - private readonly PageTable _pageTable; + private readonly PageTable _pageTable; /// /// Creates a new instance of the memory manager. @@ -46,17 +49,17 @@ namespace Ryujinx.Memory AddressSpaceBits = asBits; _addressSpaceSize = asSize; _backingMemory = backingMemory; - _pageTable = new PageTable(); + _pageTable = new PageTable(); } /// - public void Map(ulong va, ulong pa, ulong size) + public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) { AssertValidAddressAndSize(va, size); while (size != 0) { - _pageTable.Map(va, pa); + _pageTable.Map(va, (nuint)(ulong)_backingMemory.GetPointer(pa, PageSize)); va += PageSize; pa += PageSize; @@ -64,6 +67,21 @@ namespace Ryujinx.Memory } } + /// + public void MapForeign(ulong va, nuint hostPointer, ulong size) + { + AssertValidAddressAndSize(va, size); + + while (size != 0) + { + _pageTable.Map(va, hostPointer); + + va += PageSize; + hostPointer += PageSize; + size -= PageSize; + } + } + /// public void Unmap(ulong va, ulong size) { @@ -108,7 +126,7 @@ namespace Ryujinx.Memory if (IsContiguousAndMapped(va, data.Length)) { - data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); + data.CopyTo(GetHostSpanContiguous(va, data.Length)); } else { @@ -116,22 +134,18 @@ namespace Ryujinx.Memory if ((va & PageMask) != 0) { - ulong pa = GetPhysicalAddressInternal(va); - size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); + data.Slice(0, size).CopyTo(GetHostSpanContiguous(va, size)); offset += size; } for (; offset < data.Length; offset += size) { - ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); - size = Math.Min(data.Length - offset, PageSize); - data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); + data.Slice(offset, size).CopyTo(GetHostSpanContiguous(va + (ulong)offset, size)); } } } @@ -154,7 +168,7 @@ namespace Ryujinx.Memory if (IsContiguousAndMapped(va, size)) { - return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); + return GetHostSpanContiguous(va, size); } else { @@ -176,7 +190,7 @@ namespace Ryujinx.Memory if (IsContiguousAndMapped(va, size)) { - return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); + return new WritableRegion(null, va, new NativeMemoryManager((byte*)GetHostAddress(va), size).Memory); } else { @@ -189,14 +203,14 @@ namespace Ryujinx.Memory } /// - public ref T GetRef(ulong va) where T : unmanaged + public unsafe ref T GetRef(ulong va) where T : unmanaged { if (!IsContiguous(va, Unsafe.SizeOf())) { ThrowMemoryNotContiguous(); } - return ref _backingMemory.GetRef(GetPhysicalAddressInternal(va)); + return ref *(T*)GetHostAddress(va); } /// @@ -210,7 +224,7 @@ namespace Ryujinx.Memory return (int)(vaSpan / PageSize); } - private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); + private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); @@ -232,7 +246,7 @@ namespace Ryujinx.Memory return false; } - if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize)) + if (GetHostAddress(va) + PageSize != GetHostAddress(va + PageSize)) { return false; } @@ -243,6 +257,17 @@ namespace Ryujinx.Memory return true; } + /// + public IEnumerable GetHostRegions(ulong va, ulong size) + { + if (size == 0) + { + return Enumerable.Empty(); + } + + return GetHostRegionsImpl(va, size); + } + /// public IEnumerable GetPhysicalRegions(ulong va, ulong size) { @@ -251,6 +276,39 @@ namespace Ryujinx.Memory return Enumerable.Empty(); } + var hostRegions = GetHostRegionsImpl(va, size); + if (hostRegions == null) + { + return null; + } + + var regions = new MemoryRange[hostRegions.Count]; + + ulong backingStart = (ulong)_backingMemory.Pointer; + ulong backingEnd = backingStart + _backingMemory.Size; + + int count = 0; + + for (int i = 0; i < regions.Length; i++) + { + var hostRegion = hostRegions[i]; + + if ((ulong)hostRegion.Address >= backingStart && (ulong)hostRegion.Address < backingEnd) + { + regions[count++] = new MemoryRange((ulong)hostRegion.Address - backingStart, hostRegion.Size); + } + } + + if (count != regions.Length) + { + return new ArraySegment(regions, 0, count); + } + + return regions; + } + + private List GetHostRegionsImpl(ulong va, ulong size) + { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { return null; @@ -258,9 +316,9 @@ namespace Ryujinx.Memory int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); + var regions = new List(); - ulong regionStart = GetPhysicalAddressInternal(va); + nuint regionStart = GetHostAddress(va); ulong regionSize = PageSize; for (int page = 0; page < pages - 1; page++) @@ -270,12 +328,12 @@ namespace Ryujinx.Memory return null; } - ulong newPa = GetPhysicalAddressInternal(va + PageSize); + nuint newHostAddress = GetHostAddress(va + PageSize); - if (GetPhysicalAddressInternal(va) + PageSize != newPa) + if (GetHostAddress(va) + PageSize != newHostAddress) { - regions.Add(new MemoryRange(regionStart, regionSize)); - regionStart = newPa; + regions.Add(new HostMemoryRange(regionStart, regionSize)); + regionStart = newHostAddress; regionSize = 0; } @@ -283,7 +341,7 @@ namespace Ryujinx.Memory regionSize += PageSize; } - regions.Add(new MemoryRange(regionStart, regionSize)); + regions.Add(new HostMemoryRange(regionStart, regionSize)); return regions; } @@ -301,22 +359,18 @@ namespace Ryujinx.Memory if ((va & PageMask) != 0) { - ulong pa = GetPhysicalAddressInternal(va); - size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); + GetHostSpanContiguous(va, size).CopyTo(data.Slice(0, size)); offset += size; } for (; offset < data.Length; offset += size) { - ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); - size = Math.Min(data.Length - offset, PageSize); - _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); + GetHostSpanContiguous(va + (ulong)offset, size).CopyTo(data.Slice(offset, size)); } } @@ -391,22 +445,23 @@ namespace Ryujinx.Memory } } - private ulong GetPhysicalAddressInternal(ulong va) + private unsafe Span GetHostSpanContiguous(ulong va, int size) { - return _pageTable.Read(va) + (va & PageMask); + return new Span((void*)GetHostAddress(va), size); } - /// - /// Reprotect a region of virtual memory for tracking. Sets software protection bits. - /// - /// Virtual address base - /// Size of the region to protect - /// Memory protection to set + private nuint GetHostAddress(ulong va) + { + return _pageTable.Read(va) + (nuint)(va & PageMask); + } + + /// public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection) { throw new NotImplementedException(); } + /// public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) { // Only the ARM Memory Manager has tracking for now. diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs index c8a74f665..390371ad2 100644 --- a/Ryujinx.Memory/IVirtualMemoryManager.cs +++ b/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -6,6 +6,12 @@ namespace Ryujinx.Memory { public interface IVirtualMemoryManager { + /// + /// Indicates whenever the memory manager supports aliasing pages at 4KB granularity. + /// + /// True if 4KB pages are supported by the memory manager, false otherwise + bool Supports4KBPages { get; } + /// /// Maps a virtual memory range into a physical memory range. /// @@ -15,7 +21,20 @@ namespace Ryujinx.Memory /// Virtual memory address /// Physical memory address where the region should be mapped to /// Size to be mapped - void Map(ulong va, ulong pa, ulong size); + /// Flags controlling memory mapping + void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags); + + /// + /// Maps a virtual memory range into an arbitrary host memory range. + /// + /// + /// Addresses and size must be page aligned. + /// Not all memory managers supports this feature. + /// + /// Virtual memory address + /// Host pointer where the virtual region should be mapped + /// Size to be mapped + void MapForeign(ulong va, nuint hostPointer, ulong size); /// /// Unmaps a previously mapped range of virtual memory. @@ -115,6 +134,15 @@ namespace Ryujinx.Memory /// Throw if the specified memory region is not contiguous in physical memory ref T GetRef(ulong va) where T : unmanaged; + /// + /// Gets the host regions that make up the given virtual address region. + /// If any part of the virtual region is unmapped, null is returned. + /// + /// Virtual address of the range + /// Size of the range + /// Array of host regions + IEnumerable GetHostRegions(ulong va, ulong size); + /// /// Gets the physical regions that make up the given virtual address region. /// If any part of the virtual region is unmapped, null is returned. diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 2df7ea9bd..885ef4569 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -440,4 +440,4 @@ namespace Ryujinx.Memory private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); } -} +} \ No newline at end of file diff --git a/Ryujinx.Memory/MemoryMapFlags.cs b/Ryujinx.Memory/MemoryMapFlags.cs new file mode 100644 index 000000000..b4c74c8c9 --- /dev/null +++ b/Ryujinx.Memory/MemoryMapFlags.cs @@ -0,0 +1,23 @@ +using System; + +namespace Ryujinx.Memory +{ + /// + /// Flags that indicate how the host memory should be mapped. + /// + [Flags] + public enum MemoryMapFlags + { + /// + /// No mapping flags. + /// + None = 0, + + /// + /// Indicates that the implementation is free to ignore the specified backing memory offset + /// and allocate its own private storage for the mapping. + /// This allows some mappings that would otherwise fail due to host platform restrictions to succeed. + /// + Private = 1 << 0 + } +} diff --git a/Ryujinx.Memory/Range/HostMemoryRange.cs b/Ryujinx.Memory/Range/HostMemoryRange.cs new file mode 100644 index 000000000..79c649d85 --- /dev/null +++ b/Ryujinx.Memory/Range/HostMemoryRange.cs @@ -0,0 +1,71 @@ +using System; + +namespace Ryujinx.Memory.Range +{ + /// + /// Range of memory composed of an address and size. + /// + public struct HostMemoryRange : IEquatable + { + /// + /// An empty memory range, with a null address and zero size. + /// + public static HostMemoryRange Empty => new HostMemoryRange(0, 0); + + /// + /// Start address of the range. + /// + public nuint Address { get; } + + /// + /// Size of the range in bytes. + /// + public ulong Size { get; } + + /// + /// Address where the range ends (exclusive). + /// + public nuint EndAddress => Address + (nuint)Size; + + /// + /// Creates a new memory range with the specified address and size. + /// + /// Start address + /// Size in bytes + public HostMemoryRange(nuint address, ulong size) + { + Address = address; + Size = size; + } + + /// + /// Checks if the range overlaps with another. + /// + /// The other range to check for overlap + /// True if the ranges overlap, false otherwise + public bool OverlapsWith(HostMemoryRange other) + { + nuint thisAddress = Address; + nuint thisEndAddress = EndAddress; + nuint otherAddress = other.Address; + nuint otherEndAddress = other.EndAddress; + + return thisAddress < otherEndAddress && otherAddress < thisEndAddress; + } + + public override bool Equals(object obj) + { + return obj is HostMemoryRange other && Equals(other); + } + + public bool Equals(HostMemoryRange other) + { + return Address == other.Address && Size == other.Size; + } + + public override int GetHashCode() + { + return HashCode.Combine(Address, Size); + } + } +} diff --git a/Ryujinx.Memory/Tracking/MemoryTracking.cs b/Ryujinx.Memory/Tracking/MemoryTracking.cs index 9aa7c7ff3..9a35cfb6c 100644 --- a/Ryujinx.Memory/Tracking/MemoryTracking.cs +++ b/Ryujinx.Memory/Tracking/MemoryTracking.cs @@ -139,8 +139,6 @@ namespace Ryujinx.Memory.Tracking /// The memory tracking handle public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity) { - (address, size) = PageAlign(address, size); - return new MultiRegionHandle(this, address, size, handles, granularity); } @@ -166,11 +164,11 @@ namespace Ryujinx.Memory.Tracking /// The memory tracking handle public RegionHandle BeginTracking(ulong address, ulong size) { - (address, size) = PageAlign(address, size); + var (paAddress, paSize) = PageAlign(address, size); lock (TrackingLock) { - RegionHandle handle = new RegionHandle(this, address, size, _memoryManager.IsRangeMapped(address, size)); + RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, _memoryManager.IsRangeMapped(address, size)); return handle; } @@ -186,11 +184,11 @@ namespace Ryujinx.Memory.Tracking /// The memory tracking handle internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit) { - (address, size) = PageAlign(address, size); + var (paAddress, paSize) = PageAlign(address, size); lock (TrackingLock) { - RegionHandle handle = new RegionHandle(this, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size)); + RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size)); return handle; } diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs index 6cbea7f31..6ea2b7845 100644 --- a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Memory.Tracking internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable handles, ulong granularity) { - _handles = new RegionHandle[size / granularity]; + _handles = new RegionHandle[(size + granularity - 1) / granularity]; Granularity = granularity; _dirtyBitmap = new ConcurrentBitmap(_handles.Length, true); @@ -50,7 +50,7 @@ namespace Ryujinx.Memory.Tracking foreach (RegionHandle handle in handles) { - int startIndex = (int)((handle.Address - address) / granularity); + int startIndex = (int)((handle.RealAddress - address) / granularity); // Fill any gap left before this handle. while (i < startIndex) @@ -72,7 +72,7 @@ namespace Ryujinx.Memory.Tracking } else { - int endIndex = (int)((handle.EndAddress - address) / granularity); + int endIndex = (int)((handle.RealEndAddress - address) / granularity); while (i < endIndex) { @@ -171,12 +171,13 @@ namespace Ryujinx.Memory.Tracking modifiedAction(rgStart, rgSize); rgSize = 0; } - rgStart = handle.Address; + + rgStart = handle.RealAddress; } if (handle.Dirty) { - rgSize += handle.Size; + rgSize += handle.RealSize; handle.Reprotect(); } @@ -191,7 +192,7 @@ namespace Ryujinx.Memory.Tracking int startHandle = (int)((address - Address) / Granularity); int lastHandle = (int)((address + (size - 1) - Address) / Granularity); - ulong rgStart = _handles[startHandle].Address; + ulong rgStart = Address + (ulong)startHandle * Granularity; if (startHandle == lastHandle) { @@ -200,7 +201,7 @@ namespace Ryujinx.Memory.Tracking if (handle.Dirty) { handle.Reprotect(); - modifiedAction(rgStart, handle.Size); + modifiedAction(rgStart, handle.RealSize); } return; @@ -273,10 +274,10 @@ namespace Ryujinx.Memory.Tracking modifiedAction(rgStart, rgSize); rgSize = 0; } - rgStart = handle.Address; + rgStart = handle.RealAddress; } - rgSize += handle.Size; + rgSize += handle.RealSize; handle.Reprotect(false, (checkMasks[index] & bitValue) == 0); checkMasks[index] &= ~bitValue; @@ -320,7 +321,7 @@ namespace Ryujinx.Memory.Tracking { handle.Reprotect(); - modifiedAction(rgStart, handle.Size); + modifiedAction(rgStart, handle.RealSize); } } diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs index 86c77abc3..580f94a51 100644 --- a/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -42,6 +42,10 @@ namespace Ryujinx.Memory.Tracking public ulong Size { get; } public ulong EndAddress { get; } + public ulong RealAddress { get; } + public ulong RealSize { get; } + public ulong RealEndAddress { get; } + internal IMultiRegionHandle Parent { get; set; } private event Action _onDirty; @@ -89,10 +93,12 @@ namespace Ryujinx.Memory.Tracking /// Tracking object for the target memory block /// Virtual address of the region to track /// Size of the region to track + /// The real, unaligned address of the handle + /// The real, unaligned size of the handle /// The bitmap the dirty flag for this handle is stored in /// The bit index representing the dirty flag for this handle /// True if the region handle starts mapped - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ConcurrentBitmap bitmap, int bit, bool mapped = true) + internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, ConcurrentBitmap bitmap, int bit, bool mapped = true) { Bitmap = bitmap; DirtyBit = bit; @@ -104,6 +110,10 @@ namespace Ryujinx.Memory.Tracking Size = size; EndAddress = address + size; + RealAddress = realAddress; + RealSize = realSize; + RealEndAddress = realAddress + realSize; + _tracking = tracking; _regions = tracking.GetVirtualRegionsForHandle(address, size); foreach (var region in _regions) @@ -119,16 +129,23 @@ namespace Ryujinx.Memory.Tracking /// Tracking object for the target memory block /// Virtual address of the region to track /// Size of the region to track + /// The real, unaligned address of the handle + /// The real, unaligned size of the handle /// True if the region handle starts mapped - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true) + internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, bool mapped = true) { Bitmap = new ConcurrentBitmap(1, mapped); Unmapped = !mapped; + Address = address; Size = size; EndAddress = address + size; + RealAddress = realAddress; + RealSize = realSize; + RealEndAddress = realAddress + realSize; + _tracking = tracking; _regions = tracking.GetVirtualRegionsForHandle(address, size); foreach (var region in _regions) @@ -199,6 +216,10 @@ namespace Ryujinx.Memory.Tracking if (_preAction != null) { + // Limit the range to within this handle. + ulong maxAddress = Math.Max(address, RealAddress); + ulong minEndAddress = Math.Min(address + size, RealAddress + RealSize); + // Copy the handles list in case it changes when we're out of the lock. if (handleIterable is List) { @@ -212,7 +233,7 @@ namespace Ryujinx.Memory.Tracking { lock (_preActionLock) { - _preAction?.Invoke(address, size); + _preAction?.Invoke(maxAddress, minEndAddress - maxAddress); // The action is removed after it returns, to ensure that the null check above succeeds when // it's still in progress rather than continuing and possibly missing a required data flush. diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index cafed37da..b64f74668 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Tests.Cpu _ram = new MemoryBlock(Size * 2); _memory = new MemoryManager(_ram, 1ul << 16); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, 0, Size * 2); + _memory.Map(CodeBaseAddress, 0, Size * 2, MemoryMapFlags.Private); _context = CpuContext.CreateExecutionContext(); Translator.IsReadyForTranslation.Set(); diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs index 53fea943d..46ae3c771 100644 --- a/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Tests.Cpu _ram = new MemoryBlock(Size * 2); _memory = new MemoryManager(_ram, 1ul << 16); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, 0, Size * 2); + _memory.Map(CodeBaseAddress, 0, Size * 2, MemoryMapFlags.Private); _context = CpuContext.CreateExecutionContext(); _context.IsAarch32 = true; From cff9046fc7765768ed38e4442bcb5d18446874f9 Mon Sep 17 00:00:00 2001 From: merry Date: Tue, 17 Jan 2023 04:32:08 +0000 Subject: [PATCH 300/737] ConfigurationState: Default to Vulkan on macOS (#4299) --- Ryujinx.Ui.Common/Configuration/ConfigurationState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index ae183fea7..e64c69ad6 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -617,7 +617,7 @@ namespace Ryujinx.Ui.Common.Configuration Graphics.ResScaleCustom.Value = 1.0f; Graphics.MaxAnisotropy.Value = -1.0f; Graphics.AspectRatio.Value = AspectRatio.Fixed16x9; - Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; + Graphics.GraphicsBackend.Value = OperatingSystem.IsMacOS() ? GraphicsBackend.Vulkan : GraphicsBackend.OpenGl; Graphics.PreferredGpu.Value = ""; Graphics.ShadersDumpPath.Value = ""; Logger.EnableDebug.Value = false; From 410be95ab68ff1246eb24d6157c18d4c8c2dcf5a Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:50:39 +0100 Subject: [PATCH 301/737] Fix NRE when disposing AddressSpace with 4KB pages support (#4307) --- Ryujinx.Cpu/AddressSpace.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Cpu/AddressSpace.cs b/Ryujinx.Cpu/AddressSpace.cs index cea3b56d2..9dc32426c 100644 --- a/Ryujinx.Cpu/AddressSpace.cs +++ b/Ryujinx.Cpu/AddressSpace.cs @@ -462,7 +462,7 @@ namespace Ryujinx.Cpu public void Dispose() { - _privateMemoryAllocator.Dispose(); + _privateMemoryAllocator?.Dispose(); Base.Dispose(); Mirror.Dispose(); } From f449895e6d8af90f727de6590fd6120038c73986 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Wed, 18 Jan 2023 14:50:42 +0100 Subject: [PATCH 302/737] HOS: Load RomFs by pid (#4301) We currently loading only one RomFs at a time, which could be wrong if one day we want to load more than one guest at time. This PR fixes that by loading romfs by pid. --- Ryujinx.HLE/FileSystem/VirtualFileSystem.cs | 38 +++++++++++++++---- Ryujinx.HLE/HOS/ApplicationLoader.cs | 30 +++++++++------ Ryujinx.HLE/HOS/ProgramLoader.cs | 8 ++-- .../HOS/Services/Fs/IFileSystemProxy.cs | 9 +++-- 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index c958c6e8a..95a2fcdac 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -16,6 +16,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS; using System; using System.Buffers.Text; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; @@ -35,7 +36,8 @@ namespace Ryujinx.HLE.FileSystem public EmulatedGameCard GameCard { get; private set; } public EmulatedSdCard SdCard { get; private set; } public ModLoader ModLoader { get; private set; } - public Stream RomFs { get; private set; } + + private readonly ConcurrentDictionary _romFsByPid; private static bool _isInitialized = false; @@ -55,17 +57,34 @@ namespace Ryujinx.HLE.FileSystem { ReloadKeySet(); ModLoader = new ModLoader(); // Should only be created once + _romFsByPid = new ConcurrentDictionary(); } - public void LoadRomFs(string fileName) + public void LoadRomFs(ulong pid, string fileName) { - RomFs = new FileStream(fileName, FileMode.Open, FileAccess.Read); + var romfsStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); + + _romFsByPid.AddOrUpdate(pid, romfsStream, (pid, oldStream) => + { + oldStream.Close(); + + return romfsStream; + }); } - public void SetRomFs(Stream romfsStream) + public void SetRomFs(ulong pid, Stream romfsStream) { - RomFs?.Close(); - RomFs = romfsStream; + _romFsByPid.AddOrUpdate(pid, romfsStream, (pid, oldStream) => + { + oldStream.Close(); + + return romfsStream; + }); + } + + public Stream GetRomFs(ulong pid) + { + return _romFsByPid[pid]; } public string GetFullPath(string basePath, string fileName) @@ -583,7 +602,12 @@ namespace Ryujinx.HLE.FileSystem { if (disposing) { - RomFs?.Dispose(); + foreach (var stream in _romFsByPid.Values) + { + stream.Close(); + } + + _romFsByPid.Clear(); } } } diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 06281b497..67e0a9c7b 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -76,11 +76,6 @@ namespace Ryujinx.HLE.HOS public void LoadCart(string exeFsDir, string romFsFile = null) { - if (romFsFile != null) - { - _device.Configuration.VirtualFileSystem.LoadRomFs(romFsFile); - } - LocalFileSystem codeFs = new LocalFileSystem(exeFsDir); MetaLoader metaData = ReadNpdm(codeFs); @@ -95,7 +90,12 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId)); } - LoadExeFs(codeFs, string.Empty, metaData); + ulong pid = LoadExeFs(codeFs, string.Empty, metaData); + + if (romFsFile != null) + { + _device.Configuration.VirtualFileSystem.LoadRomFs(pid, romFsFile); + } } public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) @@ -491,6 +491,8 @@ namespace Ryujinx.HLE.HOS _displayVersion = displayVersion; + ulong pid = LoadExeFs(codeFs, displayVersion, metaData); + if (dataStorage == null) { Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); @@ -499,7 +501,7 @@ namespace Ryujinx.HLE.HOS { IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage); - _device.Configuration.VirtualFileSystem.SetRomFs(newStorage.AsStream(FileAccess.Read)); + _device.Configuration.VirtualFileSystem.SetRomFs(pid, newStorage.AsStream(FileAccess.Read)); } // Don't create save data for system programs. @@ -510,8 +512,6 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId & ~0xFul)); } - LoadExeFs(codeFs, displayVersion, metaData); - Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); } @@ -579,7 +579,7 @@ namespace Ryujinx.HLE.HOS } } - private void LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) + private ulong LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) { if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) { @@ -654,6 +654,8 @@ namespace Ryujinx.HLE.HOS DiskCacheLoadState = result.DiskCacheLoadState; _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); + + return result.ProcessId; } public void LoadProgram(string filePath) @@ -665,6 +667,7 @@ namespace Ryujinx.HLE.HOS bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; IExecutable executable; + Stream romfsStream = null; if (isNro) { @@ -697,7 +700,7 @@ namespace Ryujinx.HLE.HOS if (romfsSize != 0) { - _device.Configuration.VirtualFileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset)); + romfsStream = new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset); } if (nacpSize != 0) @@ -758,6 +761,11 @@ namespace Ryujinx.HLE.HOS ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable); + if (romfsStream != null) + { + _device.Configuration.VirtualFileSystem.SetRomFs(result.ProcessId, romfsStream); + } + DiskCacheLoadState = result.DiskCacheLoadState; _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index b6a39a20a..695f46721 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -41,17 +41,19 @@ namespace Ryujinx.HLE.HOS struct ProgramLoadResult { - public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null); + public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null, 0); public readonly bool Success; public readonly ProcessTamperInfo TamperInfo; public readonly IDiskCacheLoadState DiskCacheLoadState; + public readonly ulong ProcessId; - public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState) + public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState, ulong pid) { Success = success; TamperInfo = tamperInfo; DiskCacheLoadState = diskCacheLoadState; + ProcessId = pid; } } @@ -366,7 +368,7 @@ namespace Ryujinx.HLE.HOS process.MemoryManager.AliasRegionStart, process.MemoryManager.CodeRegionStart); - return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState); + return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState, process.Pid); } private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index d812e3741..a4bc62540 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -27,6 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs class IFileSystemProxy : DisposableIpcService { private SharedRef _baseFileSystemProxy; + private ulong _pid; public IFileSystemProxy(ServiceCtx context) : base(context.Device.System.FsServer) { @@ -38,6 +39,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs // SetCurrentProcess(u64, pid) public ResultCode SetCurrentProcess(ServiceCtx context) { + _pid = context.Request.HandleDesc.PId; + return ResultCode.Success; } @@ -702,7 +705,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // OpenDataStorageByCurrentProcess() -> object dataStorage public ResultCode OpenDataStorageByCurrentProcess(ServiceCtx context) { - var storage = context.Device.FileSystem.RomFs.AsStorage(true); + var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef(storage); using var sfStorage = new SharedRef(new StorageInterfaceAdapter(ref sharedStorage.Ref())); @@ -791,7 +794,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // OpenPatchDataStorageByCurrentProcess() -> object public ResultCode OpenPatchDataStorageByCurrentProcess(ServiceCtx context) { - var storage = context.Device.FileSystem.RomFs.AsStorage(true); + var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef(storage); using var sfStorage = new SharedRef(new StorageInterfaceAdapter(ref sharedStorage.Ref())); @@ -811,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex})."); } - var storage = context.Device.FileSystem.RomFs.AsStorage(true); + var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef(storage); using var sfStorage = new SharedRef(new StorageInterfaceAdapter(ref sharedStorage.Ref())); From ae4324032a48ee08a808354673f47536e76759d0 Mon Sep 17 00:00:00 2001 From: Andrey Sukharev Date: Thu, 19 Jan 2023 01:25:16 +0300 Subject: [PATCH 303/737] Optimize string memory usage. Use Spans and StringBuilders where possible (#3933) * Optimize string memory usage. Use ReadOnlySpan and StringBuilder where possible. * Fix copypaste error * Code generator review fixes * Use if statement instead of switch * Code style fixes Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Another code style fix * Styling fix Co-authored-by: Mary-nyan * Styling fix Co-authored-by: gdkchan Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Co-authored-by: Mary-nyan Co-authored-by: gdkchan --- ARMeilleure/Decoders/OpCodeTable.cs | 12 +++--- ARMeilleure/Translation/PTC/Ptc.cs | 8 ++-- ARMeilleure/Translation/PTC/PtcProfiler.cs | 8 ++-- .../ViewModels/ControllerSettingsViewModel.cs | 2 +- Ryujinx.Common/Utilities/UInt128Utils.cs | 3 +- .../Glsl/Instructions/InstGenMemory.cs | 40 ++++++++++++------- .../Glsl/Instructions/InstGenPacking.cs | 3 +- Ryujinx.Graphics.Shader/Decoders/InstTable.cs | 11 ++--- Ryujinx.HLE/FileSystem/VirtualFileSystem.cs | 4 +- .../SoftwareKeyboard/InlineResponses.cs | 2 +- .../SoftwareKeyboardRendererBase.cs | 39 ++++++++++++------ .../Demangler/Ast/IntegerLiteral.cs | 3 +- .../HOS/Diagnostics/Demangler/Demangler.cs | 26 ++++++------ .../HOS/Services/Account/Acc/Types/UserId.cs | 5 ++- .../HOS/Services/Sockets/Nsd/IManager.cs | 3 +- Ryujinx.Horizon.Generators/CodeGenerator.cs | 15 ++++--- .../Kernel/SyscallGenerator.cs | 2 +- Ryujinx/Ui/Windows/AmiiboWindow.cs | 11 ++--- Ryujinx/Ui/Windows/ControllerWindow.cs | 2 +- 19 files changed, 118 insertions(+), 81 deletions(-) diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index 3f24986c5..54abb1418 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -1339,7 +1339,7 @@ namespace ARMeilleure.Decoders private static void SetT32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) { - string reversedEncoding = encoding.Substring(16) + encoding.Substring(0, 16); + string reversedEncoding = $"{encoding.AsSpan(16)}{encoding.AsSpan(0, 16)}"; MakeOp reversedMakeOp = (inst, address, opCode) => makeOp(inst, address, (int)BitOperations.RotateRight((uint)opCode, 16)); @@ -1353,7 +1353,7 @@ namespace ARMeilleure.Decoders string thumbEncoding = encoding; if (thumbEncoding.StartsWith("<<<<")) { - thumbEncoding = "1110" + thumbEncoding.Substring(4); + thumbEncoding = $"1110{thumbEncoding.AsSpan(4)}"; } SetT32(thumbEncoding, name, emitter, makeOpT32); } @@ -1365,19 +1365,19 @@ namespace ARMeilleure.Decoders string thumbEncoding = encoding; if (thumbEncoding.StartsWith("11110100")) { - thumbEncoding = "11111001" + encoding.Substring(8); + thumbEncoding = $"11111001{encoding.AsSpan(8)}"; } else if (thumbEncoding.StartsWith("1111001x")) { - thumbEncoding = "111x1111" + encoding.Substring(8); + thumbEncoding = $"111x1111{encoding.AsSpan(8)}"; } else if (thumbEncoding.StartsWith("11110010")) { - thumbEncoding = "11101111" + encoding.Substring(8); + thumbEncoding = $"11101111{encoding.AsSpan(8)}"; } else if (thumbEncoding.StartsWith("11110011")) { - thumbEncoding = "11111111" + encoding.Substring(8); + thumbEncoding = $"11111111{encoding.AsSpan(8)}"; } else { diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index e5b4623d2..e5e0b2a54 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -183,8 +183,8 @@ namespace ARMeilleure.Translation.PTC private void PreLoad() { - string fileNameActual = string.Concat(CachePathActual, ".cache"); - string fileNameBackup = string.Concat(CachePathBackup, ".cache"); + string fileNameActual = $"{CachePathActual}.cache"; + string fileNameBackup = $"{CachePathBackup}.cache"; FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoBackup = new FileInfo(fileNameBackup); @@ -400,8 +400,8 @@ namespace ARMeilleure.Translation.PTC try { - string fileNameActual = string.Concat(CachePathActual, ".cache"); - string fileNameBackup = string.Concat(CachePathBackup, ".cache"); + string fileNameActual = $"{CachePathActual}.cache"; + string fileNameBackup = $"{CachePathBackup}.cache"; FileInfo fileInfoActual = new FileInfo(fileNameActual); diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index 0d5546283..030ccff5f 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -125,8 +125,8 @@ namespace ARMeilleure.Translation.PTC { _lastHash = default; - string fileNameActual = string.Concat(_ptc.CachePathActual, ".info"); - string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info"); + string fileNameActual = $"{_ptc.CachePathActual}.info"; + string fileNameBackup = $"{_ptc.CachePathBackup}.info"; FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoBackup = new FileInfo(fileNameBackup); @@ -246,8 +246,8 @@ namespace ARMeilleure.Translation.PTC { _waitEvent.Reset(); - string fileNameActual = string.Concat(_ptc.CachePathActual, ".info"); - string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info"); + string fileNameActual = $"{_ptc.CachePathActual}.info"; + string fileNameBackup = $"{_ptc.CachePathBackup}.info"; FileInfo fileInfoActual = new FileInfo(fileNameActual); diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index 82a75788d..5d5ca5f83 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -435,7 +435,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (str.Length > MaxSize) { - return str.Substring(0, MaxSize - Ellipsis.Length) + Ellipsis; + return $"{str.AsSpan(0, MaxSize - Ellipsis.Length)}{Ellipsis}"; } return str; diff --git a/Ryujinx.Common/Utilities/UInt128Utils.cs b/Ryujinx.Common/Utilities/UInt128Utils.cs index 8cc437d17..af8521b4e 100644 --- a/Ryujinx.Common/Utilities/UInt128Utils.cs +++ b/Ryujinx.Common/Utilities/UInt128Utils.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; namespace Ryujinx.Common.Utilities { @@ -6,7 +7,7 @@ namespace Ryujinx.Common.Utilities { public static UInt128 FromHex(string hex) { - return new UInt128((ulong)Convert.ToInt64(hex.Substring(0, 16), 16), (ulong)Convert.ToInt64(hex.Substring(16), 16)); + return new UInt128(ulong.Parse(hex.AsSpan(0, 16), NumberStyles.HexNumber), ulong.Parse(hex.AsSpan(16), NumberStyles.HexNumber)); } public static UInt128 CreateRandom() diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 3dbd73b27..263eada6f 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -2,6 +2,7 @@ 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.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -44,11 +45,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; - string texCall; + var texCallBuilder = new StringBuilder(); if (texOp.Inst == Instruction.ImageAtomic) { - texCall = (texOp.Flags & TextureFlags.AtomicMask) switch { + texCallBuilder.Append((texOp.Flags & TextureFlags.AtomicMask) switch { TextureFlags.Add => "imageAtomicAdd", TextureFlags.Minimum => "imageAtomicMin", TextureFlags.Maximum => "imageAtomicMax", @@ -60,11 +61,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions TextureFlags.Swap => "imageAtomicExchange", TextureFlags.CAS => "imageAtomicCompSwap", _ => "imageAtomicAdd", - }; + }); } else { - texCall = texOp.Inst == Instruction.ImageLoad ? "imageLoad" : "imageStore"; + texCallBuilder.Append(texOp.Inst == Instruction.ImageLoad ? "imageLoad" : "imageStore"); } int srcIndex = isBindless ? 1 : 0; @@ -83,7 +84,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); - texCall += "(" + imageName; + texCallBuilder.Append('('); + texCallBuilder.Append(imageName); int coordsCount = texOp.Type.GetDimensions(); @@ -91,7 +93,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions void Append(string str) { - texCall += ", " + str; + texCallBuilder.Append(", "); + texCallBuilder.Append(str); } string ApplyScaling(string vector) @@ -107,11 +110,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (pCount == 3 && isArray) { // The array index is not scaled, just x and y. - vector = "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + scaleIndex + "), (" + vector + ").z)"; + vector = $"ivec3(Helper_TexelFetchScale(({vector}).xy, {scaleIndex}), ({vector}).z)"; } else if (pCount == 2 && !isArray) { - vector = "Helper_TexelFetchScale(" + vector + ", " + scaleIndex + ")"; + vector = $"Helper_TexelFetchScale({vector}, {scaleIndex})"; } } @@ -127,7 +130,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions elems[index] = Src(AggregateType.S32); } - Append(ApplyScaling("ivec" + pCount + "(" + string.Join(", ", elems) + ")")); + Append(ApplyScaling($"ivec{pCount}({string.Join(", ", elems)})")); } else { @@ -164,7 +167,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions _ => string.Empty }; - Append(prefix + "vec4(" + string.Join(", ", cElems) + ")"); + Append($"{prefix}vec4({string.Join(", ", cElems)})"); } if (texOp.Inst == Instruction.ImageAtomic) @@ -185,19 +188,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Append(value); - texCall += ")"; + texCallBuilder.Append(')'); if (type != AggregateType.S32) { - texCall = "int(" + texCall + ")"; + texCallBuilder + .Insert(0, "int(") + .Append(')'); } } else { - texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMaskMultiDest(texOp.Index) : ""); + texCallBuilder.Append(')'); + + if (texOp.Inst == Instruction.ImageLoad) + { + texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); + } } - return texCall; + return texCallBuilder.ToString(); } public static string LoadAttribute(CodeGenContext context, AstOperation operation) @@ -827,7 +837,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions private static string GetMask(int index) { - return '.' + "rgba".Substring(index, 1); + return $".{"rgba".AsSpan(index, 1)}"; } private static string GetMaskMultiDest(int mask) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs index ecb90c1e0..5a888e9c5 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.StructuredIr; +using System; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -49,7 +50,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions private static string GetMask(int index) { - return '.' + "xy".Substring(index, 1); + return $".{"xy".AsSpan(index, 1)}"; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/InstTable.cs b/Ryujinx.Graphics.Shader/Decoders/InstTable.cs index 911f15816..eaa77930b 100644 --- a/Ryujinx.Graphics.Shader/Decoders/InstTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/InstTable.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.Instructions; +using System; namespace Ryujinx.Graphics.Shader.Decoders { @@ -329,18 +330,18 @@ namespace Ryujinx.Graphics.Shader.Decoders private static void Add(string encoding, InstName name, InstEmitter emitter, InstProps props = InstProps.None) { - encoding = encoding.Substring(0, EncodingBits); + ReadOnlySpan encodingPart = encoding.AsSpan(0, EncodingBits); - int bit = encoding.Length - 1; + int bit = encodingPart.Length - 1; int value = 0; int xMask = 0; int xBits = 0; - int[] xPos = new int[encoding.Length]; + int[] xPos = new int[encodingPart.Length]; - for (int index = 0; index < encoding.Length; index++, bit--) + for (int index = 0; index < encodingPart.Length; index++, bit--) { - char chr = encoding[index]; + char chr = encodingPart[index]; if (chr == '1') { diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 95a2fcdac..0b91d3a2d 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -141,8 +141,8 @@ namespace Ryujinx.HLE.FileSystem return $"{rawPath}:/"; } - string basePath = rawPath.Substring(0, firstSeparatorOffset); - string fileName = rawPath.Substring(firstSeparatorOffset + 1); + var basePath = rawPath.AsSpan(0, firstSeparatorOffset); + var fileName = rawPath.AsSpan(firstSeparatorOffset + 1); return $"{basePath}:/{fileName}"; } diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs index d48227a0f..c3e45d469 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard for (int maxStr = text.Length; maxStr >= 0; maxStr--) { // This loop will probably will run only once. - bytes = encoding.GetBytes(text.Substring(0, maxStr)); + bytes = encoding.GetBytes(text, 0, maxStr); if (bytes.Length <= maxSize) { break; diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index 8216a65ee..71835e2da 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -292,20 +292,35 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard _logoPosition = new Point(logoPositionX, logoPositionY); } - - private RectangleF MeasureString(string text, Font font) + private static RectangleF MeasureString(string text, Font font) { RendererOptions options = new RendererOptions(font); - FontRectangle rectangle = TextMeasurer.Measure(text == "" ? " " : text, options); if (text == "") { - return new RectangleF(0, rectangle.Y, 0, rectangle.Height); + FontRectangle emptyRectangle = TextMeasurer.Measure(" ", options); + + return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height); } - else + + FontRectangle rectangle = TextMeasurer.Measure(text, options); + + return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); + } + + private static RectangleF MeasureString(ReadOnlySpan text, Font font) + { + RendererOptions options = new RendererOptions(font); + + if (text == "") { - return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); + FontRectangle emptyRectangle = TextMeasurer.Measure(" ", options); + return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height); } + + FontRectangle rectangle = TextMeasurer.Measure(text, options); + + return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUiState state) @@ -354,8 +369,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard cursorBrush = _selectionBoxBrush; cursorPen = _selectionBoxPen; - string textUntilBegin = state.InputText.Substring(0, state.CursorBegin); - string textUntilEnd = state.InputText.Substring(0, state.CursorEnd); + ReadOnlySpan textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin); + ReadOnlySpan textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd); var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont); var selectionEndRectangle = MeasureString(textUntilEnd , _inputTextFont); @@ -374,9 +389,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { // Show the blinking cursor. - int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); - string textUntilCursor = state.InputText.Substring(0, cursorBegin); - var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); + int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); + ReadOnlySpan textUntilCursor = state.InputText.AsSpan(0, cursorBegin); + var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); cursorVisible = true; cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; @@ -387,7 +402,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard if (state.CursorBegin < state.InputText.Length) { - textUntilCursor = state.InputText.Substring(0, cursorBegin + 1); + textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1); cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; } diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs index 951faa554..ea048d768 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs @@ -1,4 +1,5 @@ using System.IO; +using System; namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { @@ -25,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast if (_literalValue[0] == 'n') { writer.Write("-"); - writer.Write(_literalValue.Substring(1)); + writer.Write(_literalValue.AsSpan(1)); } else { diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs index be5b75392..a6618eca4 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -32,9 +32,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private bool ConsumeIf(string toConsume) { - string mangledPart = Mangled.Substring(_position); + var mangledPart = Mangled.AsSpan(_position); - if (mangledPart.StartsWith(toConsume)) + if (mangledPart.StartsWith(toConsume.AsSpan())) { _position += toConsume.Length; @@ -44,14 +44,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return false; } - private string PeekString(int offset = 0, int length = 1) + private ReadOnlySpan PeekString(int offset = 0, int length = 1) { if (_position + offset >= length) { return null; } - return Mangled.Substring(_position + offset, length); + return Mangled.AsSpan(_position + offset, length); } private char Peek(int offset = 0) @@ -101,8 +101,8 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private int ParseSeqId() { - string part = Mangled.Substring(_position); - int seqIdLen = 0; + ReadOnlySpan part = Mangled.AsSpan(_position); + int seqIdLen = 0; for (; seqIdLen < part.Length; seqIdLen++) { @@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler _position += seqIdLen; - return FromBase36(part.Substring(0, seqIdLen)); + return FromBase36(new string(part[..seqIdLen])); } // ::= S _ @@ -900,8 +900,8 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private int ParsePositiveNumber() { - string part = Mangled.Substring(_position); - int numberLength = 0; + ReadOnlySpan part = Mangled.AsSpan(_position); + int numberLength = 0; for (; numberLength < part.Length; numberLength++) { @@ -918,7 +918,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return -1; } - return int.Parse(part.AsSpan(0, numberLength)); + return int.Parse(part[..numberLength]); } private string ParseNumber(bool isSigned = false) @@ -933,8 +933,8 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; } - string part = Mangled.Substring(_position); - int numberLength = 0; + ReadOnlySpan part = Mangled.AsSpan(_position); + int numberLength = 0; for (; numberLength < part.Length; numberLength++) { @@ -946,7 +946,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler _position += numberLength; - return part.Substring(0, numberLength); + return new string(part[..numberLength]); } // ::= diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs index 1793067d0..e5577a94b 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs @@ -1,5 +1,6 @@ using LibHac.Account; using System; +using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -35,8 +36,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc throw new ArgumentException("Invalid Hex value!", nameof(hex)); } - Low = Convert.ToInt64(hex.Substring(16), 16); - High = Convert.ToInt64(hex.Substring(0, 16), 16); + Low = long.Parse(hex.AsSpan(16), NumberStyles.HexNumber); + High = long.Parse(hex.AsSpan(0, 16), NumberStyles.HexNumber); } public void Write(BinaryWriter binaryWriter) diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs index 14bdd476c..b098e2edc 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs @@ -4,6 +4,7 @@ using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Services.Settings; using Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager; using Ryujinx.HLE.HOS.Services.Sockets.Nsd.Types; +using System; using System.Text; namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd @@ -370,7 +371,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return result; } - byte environmentType = identifier.Substring(0, 2) switch + byte environmentType = identifier.AsSpan(0, 2) switch { "lp" => (byte)ApplicationServerEnvironmentType.Lp, "sd" => (byte)ApplicationServerEnvironmentType.Sd, diff --git a/Ryujinx.Horizon.Generators/CodeGenerator.cs b/Ryujinx.Horizon.Generators/CodeGenerator.cs index 3a479eb74..29e1c75c8 100644 --- a/Ryujinx.Horizon.Generators/CodeGenerator.cs +++ b/Ryujinx.Horizon.Generators/CodeGenerator.cs @@ -4,9 +4,10 @@ namespace Ryujinx.Horizon.Generators { class CodeGenerator { - private const string Indent = " "; + private const int IndentLength = 4; + private readonly StringBuilder _sb; - private string _currentIndent; + private int _currentIndentCount; public CodeGenerator() { @@ -32,12 +33,15 @@ namespace Ryujinx.Horizon.Generators public void IncreaseIndentation() { - _currentIndent += Indent; + _currentIndentCount++; } public void DecreaseIndentation() { - _currentIndent = _currentIndent.Substring(0, _currentIndent.Length - Indent.Length); + if (_currentIndentCount - 1 >= 0) + { + _currentIndentCount--; + } } public void AppendLine() @@ -47,7 +51,8 @@ namespace Ryujinx.Horizon.Generators public void AppendLine(string text) { - _sb.AppendLine(_currentIndent + text); + _sb.Append(' ', IndentLength * _currentIndentCount); + _sb.AppendLine(text); } public override string ToString() diff --git a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs index f2a877030..8bc0800c1 100644 --- a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs +++ b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs @@ -417,7 +417,7 @@ namespace Ryujinx.Horizon.Generators.Kernel private static string GetPrefixedArgName(string name) { - return ArgVariablePrefix + name[0].ToString().ToUpperInvariant() + name.Substring(1); + return ArgVariablePrefix + char.ToUpperInvariant(name[0]) + name.Substring(1); } private static string GetCanonicalTypeName(Compilation compilation, SyntaxNode syntaxNode) diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.cs b/Ryujinx/Ui/Windows/AmiiboWindow.cs index a8b021238..9140a14e9 100644 --- a/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -344,7 +344,7 @@ namespace Ryujinx.Ui.Windows string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Head + amiibo.Tail == _amiiboCharsComboBox.ActiveId).Image; - string usageString = ""; + var usageStringBuilder = new StringBuilder(); for (int i = 0; i < _amiiboList.Count; i++) { @@ -358,19 +358,20 @@ namespace Ryujinx.Ui.Windows { foreach (AmiiboApiUsage usageItem in item.AmiiboUsage) { - usageString += Environment.NewLine + $"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"; + usageStringBuilder.Append(Environment.NewLine); + usageStringBuilder.Append($"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"); writable = usageItem.Write; } } } - if (usageString.Length == 0) + if (usageStringBuilder.Length == 0) { - usageString = "Unknown."; + usageStringBuilder.Append("Unknown."); } - _gameUsageLabel.Text = $"Usage{(writable ? " (Writable)" : "")} : {usageString}"; + _gameUsageLabel.Text = $"Usage{(writable ? " (Writable)" : "")} : {usageStringBuilder}"; } } diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs index 002f8fe22..8c3a43c85 100644 --- a/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -246,7 +246,7 @@ namespace Ryujinx.Ui.Windows if (str.Length > MaxSize) { - return str.Substring(0, MaxSize - ShrinkChars.Length) + ShrinkChars; + return $"{str.AsSpan(0, MaxSize - ShrinkChars.Length)}{ShrinkChars}"; } return str; From 36d53819a473d195f9efd1253b2421e8670f6c84 Mon Sep 17 00:00:00 2001 From: merry Date: Thu, 19 Jan 2023 00:13:17 +0000 Subject: [PATCH 304/737] NativeSignalHandler: Fix write flag (#4306) * NativeSignalHandler: Fix write flag * address comments --- ARMeilleure/Signal/NativeSignalHandler.cs | 86 ++++++++++++++++++++--- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/ARMeilleure/Signal/NativeSignalHandler.cs b/ARMeilleure/Signal/NativeSignalHandler.cs index e8dc6ddaf..77eabe267 100644 --- a/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/ARMeilleure/Signal/NativeSignalHandler.cs @@ -109,12 +109,6 @@ namespace ARMeilleure.Signal if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - // Unix siginfo struct locations. - // NOTE: These are incredibly likely to be different between kernel version and architectures. - - config.StructAddressOffset = OperatingSystem.IsMacOS() ? 24 : 16; // si_addr - config.StructWriteOffset = 8; // si_code - _signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig)); if (customSignalHandlerFactory != null) @@ -251,18 +245,88 @@ namespace ARMeilleure.Signal return context.Copy(inRegionLocal); } + private static Operand GenerateUnixFaultAddress(EmitterContext context, Operand sigInfoPtr) + { + ulong structAddressOffset = OperatingSystem.IsMacOS() ? 24ul : 16ul; // si_addr + return context.Load(OperandType.I64, context.Add(sigInfoPtr, Const(structAddressOffset))); + } + + private static Operand GenerateUnixWriteFlag(EmitterContext context, Operand ucontextPtr) + { + if (OperatingSystem.IsMacOS()) + { + const ulong mcontextOffset = 48; // uc_mcontext + Operand ctxPtr = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(mcontextOffset))); + + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + const ulong esrOffset = 8; // __es.__esr + Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(esrOffset))); + return context.BitwiseAnd(esr, Const(0x40ul)); + } + + if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + { + const ulong errOffset = 4; // __es.__err + Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(errOffset))); + return context.BitwiseAnd(err, Const(2ul)); + } + } + else if (OperatingSystem.IsLinux()) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + Operand auxPtr = context.AllocateLocal(OperandType.I64); + + Operand loopLabel = Label(); + Operand successLabel = Label(); + + const ulong auxOffset = 464; // uc_mcontext.__reserved + const uint esrMagic = 0x45535201; + + context.Copy(auxPtr, context.Add(ucontextPtr, Const(auxOffset))); + + context.MarkLabel(loopLabel); + + // _aarch64_ctx::magic + Operand magic = context.Load(OperandType.I32, auxPtr); + // _aarch64_ctx::size + Operand size = context.Load(OperandType.I32, context.Add(auxPtr, Const(4ul))); + + context.BranchIf(successLabel, magic, Const(esrMagic), Comparison.Equal); + + context.Copy(auxPtr, context.Add(auxPtr, context.ZeroExtend32(OperandType.I64, size))); + + context.Branch(loopLabel); + + context.MarkLabel(successLabel); + + // esr_context::esr + Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul))); + return context.BitwiseAnd(esr, Const(0x40ul)); + } + + if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + { + const int errOffset = 192; // uc_mcontext.gregs[REG_ERR] + Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(errOffset))); + return context.BitwiseAnd(err, Const(2ul)); + } + } + + throw new PlatformNotSupportedException(); + } + private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr) { EmitterContext context = new EmitterContext(); // (int sig, SigInfo* sigInfo, void* ucontext) Operand sigInfoPtr = context.LoadArgument(OperandType.I64, 1); + Operand ucontextPtr = context.LoadArgument(OperandType.I64, 2); - Operand structAddressOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructAddressOffset)); - Operand structWriteOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructWriteOffset)); - - Operand faultAddress = context.Load(OperandType.I64, context.Add(sigInfoPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset))); - Operand writeFlag = context.Load(OperandType.I64, context.Add(sigInfoPtr, context.ZeroExtend32(OperandType.I64, structWriteOffset))); + Operand faultAddress = GenerateUnixFaultAddress(context, sigInfoPtr); + Operand writeFlag = GenerateUnixWriteFlag(context, ucontextPtr); Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1. From de3134adbec9e35eba08dbb835c38bc305d2c150 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 19 Jan 2023 00:30:42 +0000 Subject: [PATCH 305/737] Vulkan: Explicitly enable precise occlusion queries (#4292) The only guarantee of the occlusion query type in Vulkan is that it will be zero when no samples pass, and non-zero when any samples pass. Of course, most GPUs implement this by just placing the # of samples in the result and calling it a day. However, this lax restriction means that GPUs could just report a boolean (1/0) or report a value after one is recorded, but before all samples have been counted. MoltenVK falls in the first category - by default it only reports 1/0 for occlusion queries. Thankfully, there is a feature and flag that you can use to force compatible drivers to provide a "precise" query result, that being the real # of samples passed. Should fix ink collision in Splatoon 2/3 on MoltenVK. --- Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs | 3 +++ Ryujinx.Graphics.Vulkan/PipelineFull.cs | 4 ++-- Ryujinx.Graphics.Vulkan/VulkanInitialization.cs | 1 + Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 0a4d365f2..8685d3442 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsMultiView; public readonly bool SupportsNullDescriptors; + public readonly bool SupportsPreciseOcclusionQueries; public readonly bool SupportsPushDescriptors; public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedbackQueries; @@ -53,6 +54,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsPushDescriptors, bool supportsTransformFeedback, bool supportsTransformFeedbackQueries, + bool supportsPreciseOcclusionQueries, bool supportsGeometryShader, uint minSubgroupSize, uint maxSubgroupSize, @@ -74,6 +76,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsPushDescriptors = supportsPushDescriptors; SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries; + SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; SupportsGeometryShader = supportsGeometryShader; MinSubgroupSize = minSubgroupSize; MaxSubgroupSize = maxSubgroupSize; diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index e4bf4fffa..5b4f4a6e9 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -235,7 +235,7 @@ namespace Ryujinx.Graphics.Vulkan foreach (var queryPool in _activeQueries) { Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1); - Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, 0); + Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0); } Restore(); @@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Vulkan } } - Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, 0); + Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0); _activeQueries.Add(pool); } diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index fe9462aa3..00baad103 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -416,6 +416,7 @@ namespace Ryujinx.Graphics.Vulkan IndependentBlend = true, LogicOp = supportedFeatures.LogicOp, MultiViewport = true, + OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, SamplerAnisotropy = true, ShaderClipDistance = true, diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index f1922efed..bec7b8475 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -270,6 +270,7 @@ namespace Ryujinx.Graphics.Vulkan supportedExtensions.Contains(KhrPushDescriptor.ExtensionName), supportsTransformFeedback, propertiesTransformFeedback.TransformFeedbackQueries, + features2.Features.OcclusionQueryPrecise, supportedFeatures.GeometryShader, propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, From bb89e36fd851e78787a3c81de0c6b757f170bea6 Mon Sep 17 00:00:00 2001 From: Fliperworld Date: Thu, 19 Jan 2023 21:31:25 -0300 Subject: [PATCH 306/737] Vulkan: Destroy old swapchain on swapchain recreation (#3889) * Destroy old swapchain on swapchain recreation * vkDeviceWaitIdle before DestroySwapchain * Update Ryujinx.Graphics.Vulkan/Window.cs Co-authored-by: gdkchan * Avoid unsafe code on RecreateSwapchain() * Destroying old Swapchain on a queue. * Cleanup and fix on destroying old Swapchain. * Update Ryujinx.Graphics.Vulkan/Window.cs Co-authored-by: gdkchan * Update Ryujinx.Graphics.Vulkan/Window.cs Co-authored-by: gdkchan * Update Ryujinx.Graphics.Vulkan/Window.cs Co-authored-by: gdkchan * Update Window.cs Done. Co-authored-by: gdkchan --- Ryujinx.Graphics.Vulkan/Window.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index d37dd7e96..edc7d7160 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.KHR; using System; using System.Linq; using VkFormat = Silk.NET.Vulkan.Format; @@ -49,13 +50,19 @@ namespace Ryujinx.Graphics.Vulkan private void RecreateSwapchain() { + var oldSwapchain = _swapchain; + int imageCount = _swapchainImageViews.Length; _vsyncModeChanged = false; - for (int i = 0; i < _swapchainImageViews.Length; i++) + for (int i = 0; i < imageCount; i++) { _swapchainImageViews[i].Dispose(); } + // Destroy old Swapchain. + _gd.Api.DeviceWaitIdle(_device); + _gd.SwapchainApi.DestroySwapchain(_device, oldSwapchain, Span.Empty); + CreateSwapchain(); } @@ -115,8 +122,7 @@ namespace Ryujinx.Graphics.Vulkan PreTransform = capabilities.CurrentTransform, CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), - Clipped = true, - OldSwapchain = oldSwapchain + Clipped = true }; _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError(); From eb2cc159fa3632f19188b49908ac87625a0ae3cc Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 20 Jan 2023 21:30:21 +0100 Subject: [PATCH 307/737] Ava UI: Fixes and cleanup Updater (#4269) * ava: Fixes and cleanup Updater * _updateSuccessful --- Ryujinx.Ava/Modules/Updater/Updater.cs | 528 ++++++++++-------- .../UI/ViewModels/MainWindowViewModel.cs | 5 +- .../UI/Views/Main/MainMenuBarView.axaml.cs | 2 +- Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 2 +- 4 files changed, 284 insertions(+), 253 deletions(-) diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index a7399407f..39683187e 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -7,9 +7,7 @@ using ICSharpCode.SharpZipLib.Zip; using Newtonsoft.Json.Linq; using Ryujinx.Ava; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Ui.Common.Helper; @@ -32,31 +30,29 @@ namespace Ryujinx.Modules internal static class Updater { private const string GitHubApiURL = "https://api.github.com"; - internal static bool Running; - private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; - private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); + private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; + private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); - private static readonly int ConnectionCount = 4; + private static readonly int ConnectionCount = 4; private static string _buildVer; private static string _platformExt; private static string _buildUrl; - private static long _buildSize; + private static long _buildSize; + private static bool _updateSuccessful; + private static bool _running; private static readonly string[] WindowsDependencyDirs = Array.Empty(); - public static bool UpdateSuccessful { get; private set; } - - public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) + public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate) { - if (Running) + if (_running) { return; } - Running = true; - mainWindow.ViewModel.CanUpdate = false; + _running = true; // Detect current platform if (OperatingSystem.IsMacOS()) @@ -82,77 +78,87 @@ namespace Ryujinx.Modules catch { Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); + Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); }); + _running = false; + return; } // Get latest version number from GitHub API try { - using (HttpClient jsonClient = ConstructHttpClient()) + using HttpClient jsonClient = ConstructHttpClient(); + + string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); + JObject jsonRoot = JObject.Parse(fetchedJson); + JToken assets = jsonRoot["assets"]; + + _buildVer = (string)jsonRoot["name"]; + + foreach (JToken asset in assets) { - string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + string assetName = (string)asset["name"]; + string assetState = (string)asset["state"]; + string downloadURL = (string)asset["browser_download_url"]; - string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - JObject jsonRoot = JObject.Parse(fetchedJson); - JToken assets = jsonRoot["assets"]; - - _buildVer = (string)jsonRoot["name"]; - - foreach (JToken asset in assets) + if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt)) { - string assetName = (string)asset["name"]; - string assetState = (string)asset["state"]; - string downloadURL = (string)asset["browser_download_url"]; + _buildUrl = downloadURL; - if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt)) + if (assetState != "uploaded") { - _buildUrl = downloadURL; - - if (assetState != "uploaded") + if (showVersionUpToDate) { - if (showVersionUpToDate) + Dispatcher.UIThread.Post(async () => { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); - }); - } - - return; + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); + }); } - break; - } - } + _running = false; - // If build not done, assume no new update are availaible. - if (_buildUrl == null) + return; + } + + break; + } + } + + // If build not done, assume no new update are availaible. + if (_buildUrl == null) + { + if (showVersionUpToDate) { - if (showVersionUpToDate) + Dispatcher.UIThread.Post(async () => { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); - }); - } - - return; + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); + }); } + + _running = false; + + return; } } catch (Exception exception) { Logger.Error?.Print(LogClass.Application, exception.Message); + Dispatcher.UIThread.Post(async () => { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]); }); + _running = false; + return; } @@ -163,11 +169,16 @@ namespace Ryujinx.Modules catch { Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!"); + Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); }); + _running = false; + return; } @@ -181,8 +192,7 @@ namespace Ryujinx.Modules }); } - Running = false; - mainWindow.ViewModel.CanUpdate = true; + _running = false; return; } @@ -210,7 +220,8 @@ namespace Ryujinx.Modules Dispatcher.UIThread.Post(async () => { // Show a message asking the user if they want to update - var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog( + LocaleManager.Instance[LocaleKeys.RyujinxUpdater], LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage], $"{Program.Version} -> {newVersion}"); @@ -218,12 +229,16 @@ namespace Ryujinx.Modules { UpdateRyujinx(mainWindow, _buildUrl); } + else + { + _running = false; + } }); } private static HttpClient ConstructHttpClient() { - HttpClient result = new HttpClient(); + HttpClient result = new(); // Required by GitHub to interract with APIs. result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); @@ -233,7 +248,7 @@ namespace Ryujinx.Modules public static async void UpdateRyujinx(Window parent, string downloadUrl) { - UpdateSuccessful = false; + _updateSuccessful = false; // Empty update dir, although it shouldn't ever have anything inside it if (Directory.Exists(UpdateDir)) @@ -245,17 +260,16 @@ namespace Ryujinx.Modules string updateFile = Path.Combine(UpdateDir, "update.bin"); - var taskDialog = new TaskDialog() + TaskDialog taskDialog = new() { - Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater], - SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], - IconSource = new SymbolIconSource { Symbol = Symbol.Download }, - Buttons = { }, - ShowProgressBar = true + Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], + IconSource = new SymbolIconSource { Symbol = Symbol.Download }, + Buttons = { }, + ShowProgressBar = true, + XamlRoot = parent }; - taskDialog.XamlRoot = parent; - taskDialog.Opened += (s, e) => { if (_buildSize >= 0) @@ -270,7 +284,7 @@ namespace Ryujinx.Modules await taskDialog.ShowAsync(true); - if (UpdateSuccessful) + if (_updateSuccessful) { var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], @@ -279,7 +293,7 @@ namespace Ryujinx.Modules if (shouldRestart) { string ryuName = Path.GetFileName(Environment.ProcessPath); - string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); + string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); if (!Path.Exists(ryuExe)) { @@ -298,15 +312,15 @@ namespace Ryujinx.Modules private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile) { // Multi-Threaded Updater - long chunkSize = _buildSize / ConnectionCount; + long chunkSize = _buildSize / ConnectionCount; long remainderChunk = _buildSize % ConnectionCount; - int completedRequests = 0; - int totalProgressPercentage = 0; - int[] progressPercentage = new int[ConnectionCount]; + int completedRequests = 0; + int totalProgressPercentage = 0; + int[] progressPercentage = new int[ConnectionCount]; - List list = new List(ConnectionCount); - List webClients = new List(ConnectionCount); + List list = new(ConnectionCount); + List webClients = new(ConnectionCount); for (int i = 0; i < ConnectionCount; i++) { @@ -317,133 +331,129 @@ namespace Ryujinx.Modules { #pragma warning disable SYSLIB0014 // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. - using (WebClient client = new WebClient()) + using WebClient client = new(); #pragma warning restore SYSLIB0014 + + webClients.Add(client); + + if (i == ConnectionCount - 1) { - webClients.Add(client); + client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); + } + else + { + client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); + } - if (i == ConnectionCount - 1) + client.DownloadProgressChanged += (_, args) => + { + int index = (int)args.UserState; + + Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); + Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); + Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); + + taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal); + }; + + client.DownloadDataCompleted += (_, args) => + { + int index = (int)args.UserState; + + if (args.Cancelled) { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); - } - else - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); - } + webClients[index].Dispose(); - client.DownloadProgressChanged += (_, args) => - { - int index = (int)args.UserState; - - Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); - Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); - Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - - taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal); - }; - - client.DownloadDataCompleted += (_, args) => - { - int index = (int)args.UserState; - - if (args.Cancelled) - { - webClients[index].Dispose(); - - taskDialog.Hide(); - - return; - } - - list[index] = args.Result; - Interlocked.Increment(ref completedRequests); - - if (Equals(completedRequests, ConnectionCount)) - { - byte[] mergedFileBytes = new byte[_buildSize]; - for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) - { - Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); - destinationOffset += list[connectionIndex].Length; - } - - File.WriteAllBytes(updateFile, mergedFileBytes); - - try - { - InstallUpdate(taskDialog, updateFile); - } - catch (Exception e) - { - Logger.Warning?.Print(LogClass.Application, e.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); - - return; - } - } - }; - - try - { - client.DownloadDataAsync(new Uri(downloadUrl), i); - } - catch (WebException ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - for (int j = 0; j < webClients.Count; j++) - { - webClients[j].CancelAsync(); - } - - DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); + taskDialog.Hide(); return; } + + list[index] = args.Result; + Interlocked.Increment(ref completedRequests); + + if (Equals(completedRequests, ConnectionCount)) + { + byte[] mergedFileBytes = new byte[_buildSize]; + for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) + { + Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); + destinationOffset += list[connectionIndex].Length; + } + + File.WriteAllBytes(updateFile, mergedFileBytes); + + try + { + InstallUpdate(taskDialog, updateFile); + } + catch (Exception e) + { + Logger.Warning?.Print(LogClass.Application, e.Message); + Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); + + DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); + + return; + } + } + }; + + try + { + client.DownloadDataAsync(new Uri(downloadUrl), i); + } + catch (WebException ex) + { + Logger.Warning?.Print(LogClass.Application, ex.Message); + Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); + + for (int j = 0; j < webClients.Count; j++) + { + webClients[j].CancelAsync(); + } + + DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); + + return; } } } private static void DoUpdateWithSingleThreadWorker(TaskDialog taskDialog, string downloadUrl, string updateFile) { - using (HttpClient client = new HttpClient()) + using HttpClient client = new(); + // We do not want to timeout while downloading + client.Timeout = TimeSpan.FromDays(1); + + using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) + using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) { - // We do not want to timeout while downloading - client.Timeout = TimeSpan.FromDays(1); + using Stream updateFileStream = File.Open(updateFile, FileMode.Create); - using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) - using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) + long totalBytes = response.Content.Headers.ContentLength.Value; + long byteWritten = 0; + + byte[] buffer = new byte[32 * 1024]; + + while (true) { - using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) + int readSize = remoteFileStream.Read(buffer); + + if (readSize == 0) { - long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; - - byte[] buffer = new byte[32 * 1024]; - - while (true) - { - int readSize = remoteFileStream.Read(buffer); - - if (readSize == 0) - { - break; - } - - byteWritten += readSize; - - taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal); - - updateFileStream.Write(buffer, 0, readSize); - } + break; } - } - InstallUpdate(taskDialog, updateFile); + byteWritten += readSize; + + taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal); + + updateFileStream.Write(buffer, 0, readSize); + } } + + InstallUpdate(taskDialog, updateFile); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -454,8 +464,11 @@ namespace Ryujinx.Modules private static void DoUpdateWithSingleThread(TaskDialog taskDialog, string downloadUrl, string updateFile) { - Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile)); - worker.Name = "Updater.SingleThreadWorker"; + Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile)) + { + Name = "Updater.SingleThreadWorker" + }; + worker.Start(); } @@ -483,72 +496,70 @@ namespace Ryujinx.Modules if (OperatingSystem.IsLinux()) { - using (Stream inStream = File.OpenRead(updateFile)) - using (Stream gzipStream = new GZipInputStream(inStream)) - using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII)) + using Stream inStream = File.OpenRead(updateFile); + using GZipInputStream gzipStream = new(inStream); + using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); + + await Task.Run(() => { - await Task.Run(() => + TarEntry tarEntry; + while ((tarEntry = tarStream.GetNextEntry()) != null) { - TarEntry tarEntry; - while ((tarEntry = tarStream.GetNextEntry()) != null) + if (tarEntry.IsDirectory) continue; + + string outPath = Path.Combine(UpdateDir, tarEntry.Name); + + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + + using (FileStream outStream = File.OpenWrite(outPath)) { - if (tarEntry.IsDirectory) continue; - - string outPath = Path.Combine(UpdateDir, tarEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using (FileStream outStream = File.OpenWrite(outPath)) - { - tarStream.CopyEntryContents(outStream); - } - - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); - - TarEntry entry = tarEntry; - - Dispatcher.UIThread.Post(() => - { - taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal); - }); + tarStream.CopyEntryContents(outStream); } - }); - taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal); - } + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); + + TarEntry entry = tarEntry; + + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal); + }); + } + }); + + taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal); } else { - using (Stream inStream = File.OpenRead(updateFile)) - using (ZipFile zipFile = new ZipFile(inStream)) + using Stream inStream = File.OpenRead(updateFile); + using ZipFile zipFile = new(inStream); + + await Task.Run(() => { - await Task.Run(() => + double count = 0; + foreach (ZipEntry zipEntry in zipFile) { - double count = 0; - foreach (ZipEntry zipEntry in zipFile) + count++; + if (zipEntry.IsDirectory) continue; + + string outPath = Path.Combine(UpdateDir, zipEntry.Name); + + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + + using (Stream zipStream = zipFile.GetInputStream(zipEntry)) + using (FileStream outStream = File.OpenWrite(outPath)) { - count++; - if (zipEntry.IsDirectory) continue; - - string outPath = Path.Combine(UpdateDir, zipEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using (Stream zipStream = zipFile.GetInputStream(zipEntry)) - using (FileStream outStream = File.OpenWrite(outPath)) - { - zipStream.CopyTo(outStream); - } - - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); - - Dispatcher.UIThread.Post(() => - { - taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); - }); + zipStream.CopyTo(outStream); } - }); - } + + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); + + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); + }); + } + }); } // Delete downloaded zip @@ -594,21 +605,24 @@ namespace Ryujinx.Modules SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx")); - UpdateSuccessful = true; + _updateSuccessful = true; taskDialog.Hide(); } -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - public static bool CanUpdate(bool showWarnings, StyleableWindow parent) + public static bool CanUpdate(bool showWarnings) { #if !DISABLE_UPDATER if (RuntimeInformation.OSArchitecture != Architecture.X64) { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage], - LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]); + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]); + }); } return false; @@ -618,8 +632,12 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage], - LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]); + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]); + }); } return false; @@ -629,8 +647,12 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage], - LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); + }); } return false; @@ -642,18 +664,27 @@ namespace Ryujinx.Modules { if (ReleaseInformation.IsFlatHubBuild()) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]); + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], + LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]); + }); } else { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], + LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); + }); } } return false; #endif } -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed // NOTE: This method should always reflect the latest build layout.s private static IEnumerable EnumerateFilesToDelete() @@ -677,7 +708,7 @@ namespace Ryujinx.Modules private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog) { - var total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length; + int total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length; foreach (string directory in Directory.GetDirectories(root)) { string dirName = Path.GetFileName(directory); @@ -694,6 +725,7 @@ namespace Ryujinx.Modules foreach (string file in Directory.GetFiles(root)) { count++; + File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true); Dispatcher.UIThread.InvokeAsync(() => diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index a02b64295..443e0d180 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels private float _volume; private string _backendText; - private bool _canUpdate; + private bool _canUpdate = true; private Cursor _cursor; private string _title; private string _currentEmulatedGamePath; @@ -177,11 +177,10 @@ namespace Ryujinx.Ava.UI.ViewModels public bool CanUpdate { - get => _canUpdate; + get => _canUpdate && EnableNonGameRunningControls && Modules.Updater.CanUpdate(false); set { _canUpdate = value; - OnPropertyChanged(); } } diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 31dbb1b77..172ab6fcd 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -165,7 +165,7 @@ namespace Ryujinx.Ava.UI.Views.Main public async void CheckForUpdates(object sender, RoutedEventArgs e) { - if (Updater.CanUpdate(true, Window)) + if (Updater.CanUpdate(true)) { await Updater.BeginParse(Window, true); } diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index 3c4534c54..6864e6432 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -271,7 +271,7 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.LoadApplication(_launchPath, _startFullscreen); } - if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false, this)) + if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) { Updater.BeginParse(this, false).ContinueWith(task => { From 009e6bcd1b4d55f511fc947d2d029f3f68b50d5d Mon Sep 17 00:00:00 2001 From: merry Date: Fri, 20 Jan 2023 20:46:13 +0000 Subject: [PATCH 308/737] Audio: Implement PCM24 output (#4321) --- .../CompatLayerHardwareDeviceDriver.cs | 7 ++-- .../CompatLayerHardwareDeviceSession.cs | 7 ++-- Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs | 33 +++++++++++++------ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs index 1eedfa660..22919f1e1 100644 --- a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs +++ b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs @@ -75,9 +75,12 @@ namespace Ryujinx.Audio.Backends.CompatLayer return SampleFormat.PcmFloat; } - // TODO: Implement PCM24 conversion. + if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt24)) + { + return SampleFormat.PcmInt24; + } - // If nothing is truly supported, attempt PCM8 at the cost of loosing quality. + // If nothing is truly supported, attempt PCM8 at the cost of losing quality. if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt8)) { return SampleFormat.PcmInt8; diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs index ca6090fe9..f22a7a690 100644 --- a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs +++ b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs @@ -58,10 +58,13 @@ namespace Ryujinx.Audio.Backends.CompatLayer switch (realSampleFormat) { case SampleFormat.PcmInt8: - PcmHelper.Convert(MemoryMarshal.Cast(convertedSamples), samples); + PcmHelper.ConvertSampleToPcm8(MemoryMarshal.Cast(convertedSamples), samples); + break; + case SampleFormat.PcmInt24: + PcmHelper.ConvertSampleToPcm24(convertedSamples, samples); break; case SampleFormat.PcmInt32: - PcmHelper.Convert(MemoryMarshal.Cast(convertedSamples), samples); + PcmHelper.ConvertSampleToPcm32(MemoryMarshal.Cast(convertedSamples), samples); break; case SampleFormat.PcmFloat: PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast(convertedSamples), samples); diff --git a/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs b/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs index 1459e3a0f..0233a8d71 100644 --- a/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs +++ b/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs @@ -37,19 +37,32 @@ namespace Ryujinx.Audio.Renderer.Dsp } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TOutput ConvertSample(TInput value) where TInput: INumber, IMinMaxValue where TOutput : INumber, IMinMaxValue - { - TInput conversionRate = TInput.CreateSaturating(TOutput.MaxValue / TOutput.CreateSaturating(TInput.MaxValue)); - - return TOutput.CreateSaturating(value * conversionRate); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert(Span output, ReadOnlySpan input) where TInput : INumber, IMinMaxValue where TOutput : INumber, IMinMaxValue + public static void ConvertSampleToPcm8(Span output, ReadOnlySpan input) { for (int i = 0; i < input.Length; i++) { - output[i] = ConvertSample(input[i]); + // Output most significant byte + output[i] = (sbyte)(input[i] >> 8); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ConvertSampleToPcm24(Span output, ReadOnlySpan input) + { + for (int i = 0; i < input.Length; i++) + { + output[i * 3 + 2] = (byte)(input[i] >> 8); + output[i * 3 + 1] = (byte)(input[i] & 0xff); + output[i * 3 + 0] = 0; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ConvertSampleToPcm32(Span output, ReadOnlySpan input) + { + for (int i = 0; i < input.Length; i++) + { + output[i] = ((int)input[i]) << 16; } } From a76eaf9a9ab042e05841d1369c2e466a4f63a48f Mon Sep 17 00:00:00 2001 From: gnisman Date: Fri, 20 Jan 2023 23:18:01 +0200 Subject: [PATCH 309/737] Ava UI: Add Control+Cmd+F HotKey for Mac OS (#4317) * Ava UI: Add Control+Cmd+F HotKey for Mac OS * fix aligned * Remove comment from code --- Ryujinx.Ava/UI/Windows/MainWindow.axaml | 1 + Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/Ryujinx.Ava/UI/Windows/MainWindow.axaml index c3d34e75d..08b99cf53 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml @@ -43,6 +43,7 @@ + diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index 6864e6432..dcd89ac0a 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -327,10 +327,11 @@ namespace Ryujinx.Ava.UI.Windows public void LoadHotKeys() { - HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); - HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11)); - HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); - HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); + HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); + HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11)); + HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta)); + HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); + HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); } private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e) From dd7a924596ff5925baa8b7f3ec85ceda8cb1cd8a Mon Sep 17 00:00:00 2001 From: Phi <7350221+PhiZero@users.noreply.github.com> Date: Sat, 21 Jan 2023 01:36:57 +0100 Subject: [PATCH 310/737] Catch Profile.json parse to prevent crash on launch (#3393) * Catch Profile.json parse to prevent crash on launch * Update Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs Co-authored-by: Ac_K Co-authored-by: PhiZero Co-authored-by: Ac_K --- .../Account/Acc/AccountSaveDataManager.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs index 44ef3f335..3bd0e2da4 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -1,5 +1,7 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; +using Ryujinx.Common.Logging; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -43,16 +45,25 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc if (File.Exists(_profilesJsonPath)) { - ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath); - - foreach (var profile in profilesJson.Profiles) + try { - UserProfile addedProfile = new UserProfile(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp); + ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath); - profiles.AddOrUpdate(profile.UserId, addedProfile, (key, old) => addedProfile); + foreach (var profile in profilesJson.Profiles) + { + UserProfile addedProfile = new UserProfile(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp); + + profiles.AddOrUpdate(profile.UserId, addedProfile, (key, old) => addedProfile); + } + + LastOpened = new UserId(profilesJson.LastOpened); } + catch (Exception e) + { + Logger.Error?.Print(LogClass.Application, $"Failed to parse {_profilesJsonPath}: {e.Message} Loading default profile!"); - LastOpened = new UserId(profilesJson.LastOpened); + LastOpened = AccountManager.DefaultUserId; + } } else { From 8474d52778d6bc45146b59a1fc921e6702f4b96a Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sat, 21 Jan 2023 02:06:19 +0100 Subject: [PATCH 311/737] Ava UI: Fix `string.Format` issues in Locale (#4305) * Ava UI: Fix `string.Format` issues in Locale * LoacLanguage everytime now * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * fix UpdateAndGetDynamicValue Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- Ryujinx.Ava/AppHost.cs | 9 +- Ryujinx.Ava/Assets/Locales/de_DE.json | 2 +- Ryujinx.Ava/Assets/Locales/el_GR.json | 2 +- Ryujinx.Ava/Assets/Locales/en_US.json | 2 +- Ryujinx.Ava/Assets/Locales/es_ES.json | 2 +- Ryujinx.Ava/Assets/Locales/fr_FR.json | 2 +- Ryujinx.Ava/Assets/Locales/it_IT.json | 2 +- Ryujinx.Ava/Assets/Locales/ja_JP.json | 2 +- Ryujinx.Ava/Assets/Locales/ko_KR.json | 2 +- Ryujinx.Ava/Assets/Locales/pl_PL.json | 2 +- Ryujinx.Ava/Assets/Locales/pt_BR.json | 2 +- Ryujinx.Ava/Assets/Locales/ru_RU.json | 2 +- Ryujinx.Ava/Assets/Locales/tr_TR.json | 2 +- Ryujinx.Ava/Assets/Locales/uk_UA.json | 2 +- Ryujinx.Ava/Assets/Locales/zh_CN.json | 2 +- Ryujinx.Ava/Assets/Locales/zh_TW.json | 2 +- Ryujinx.Ava/Common/ApplicationHelper.cs | 7 +- Ryujinx.Ava/Common/Locale/LocaleExtension.cs | 2 +- Ryujinx.Ava/Common/Locale/LocaleManager.cs | 84 +++++++++++++------ Ryujinx.Ava/Modules/Updater/Updater.cs | 2 +- Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs | 25 +++--- .../UI/Applet/SwkbdAppletDialog.axaml.cs | 6 +- Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs | 4 +- Ryujinx.Ava/UI/Models/TitleUpdateModel.cs | 2 +- .../UI/ViewModels/AboutWindowViewModel.cs | 5 +- .../ViewModels/ControllerSettingsViewModel.cs | 4 +- .../UI/ViewModels/MainWindowViewModel.cs | 56 ++++++++----- .../UI/ViewModels/TitleUpdateViewModel.cs | 2 +- .../UI/ViewModels/UserSaveManagerViewModel.cs | 3 +- Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs | 2 +- .../DownloadableContentManagerWindow.axaml.cs | 4 +- Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 4 +- .../UI/Windows/TitleUpdateWindow.axaml.cs | 2 +- 33 files changed, 144 insertions(+), 109 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 067be5c01..fdeee2cc8 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -462,8 +462,7 @@ namespace Ryujinx.Ava { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], - string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], - firmwareVersion.VersionString), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString), LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], ""); @@ -493,10 +492,8 @@ namespace Ryujinx.Ava _viewModel.RefreshFirmwareStatus(); await ContentDialogHelper.CreateInfoDialog( - string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], - firmwareVersion.VersionString), - string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], - firmwareVersion.VersionString), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString), LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]); diff --git a/Ryujinx.Ava/Assets/Locales/de_DE.json b/Ryujinx.Ava/Assets/Locales/de_DE.json index 4d656bc99..8bf00d7ed 100644 --- a/Ryujinx.Ava/Assets/Locales/de_DE.json +++ b/Ryujinx.Ava/Assets/Locales/de_DE.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Möchtest du das ausgewählte Profil löschen?", "DialogControllerSettingsModifiedConfirmMessage": "Die aktuellen Controller-Einstellungen wurden aktualisiert.", "DialogControllerSettingsModifiedConfirmSubMessage": "Controller-Einstellungen speichern?", - "DialogDlcLoadNcaErrorMessage": "{0}. Fehlerhafte Datei: {1}", + "DialogLoadNcaErrorMessage": "{0}. Fehlerhafte Datei: {1}", "DialogDlcNoDlcErrorMessage": "Die angegebene Datei enthält keinen DLC für den ausgewählten Titel!", "DialogPerformanceCheckLoggingEnabledMessage": "Es wurde die Debug Protokollierung aktiviert", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Um eine optimale Leistung zu erzielen, wird empfohlen, die Debug Protokollierung zu deaktivieren. Debug Protokollierung jetzt deaktivieren?", diff --git a/Ryujinx.Ava/Assets/Locales/el_GR.json b/Ryujinx.Ava/Assets/Locales/el_GR.json index ca3be8b9a..de8897368 100644 --- a/Ryujinx.Ava/Assets/Locales/el_GR.json +++ b/Ryujinx.Ava/Assets/Locales/el_GR.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Θέλετε να διαγράψετε το επιλεγμένο προφίλ", "DialogControllerSettingsModifiedConfirmMessage": "Οι τρέχουσες ρυθμίσεις χειρισμού έχουν ενημερωθεί.", "DialogControllerSettingsModifiedConfirmSubMessage": "Θέλετε να αποθηκεύσετε;", - "DialogDlcLoadNcaErrorMessage": "{0}. Σφάλμα Αρχείου: {1}", + "DialogLoadNcaErrorMessage": "{0}. Σφάλμα Αρχείου: {1}", "DialogDlcNoDlcErrorMessage": "Το αρχείο δεν περιέχει DLC για τον επιλεγμένο τίτλο!", "DialogPerformanceCheckLoggingEnabledMessage": "Έχετε ενεργοποιημένη την καταγραφή εντοπισμού σφαλμάτων, η οποία έχει σχεδιαστεί για χρήση μόνο από προγραμματιστές.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Για βέλτιστη απόδοση, συνιστάται η απενεργοποίηση καταγραφής εντοπισμού σφαλμάτων. Θέλετε να απενεργοποιήσετε την καταγραφή τώρα;", diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index ba5af264b..3d5096759 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -375,7 +375,7 @@ "DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?", "DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.", "DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?", - "DialogDlcLoadNcaErrorMessage": "{0}. Errored File: {1}", + "DialogLoadNcaErrorMessage": "{0}. Errored File: {1}", "DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!", "DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?", diff --git a/Ryujinx.Ava/Assets/Locales/es_ES.json b/Ryujinx.Ava/Assets/Locales/es_ES.json index 660d62a1e..13b05fc04 100644 --- a/Ryujinx.Ava/Assets/Locales/es_ES.json +++ b/Ryujinx.Ava/Assets/Locales/es_ES.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "¿Quieres eliminar el perfil seleccionado?", "DialogControllerSettingsModifiedConfirmMessage": "Se ha actualizado la configuración del mando actual.", "DialogControllerSettingsModifiedConfirmSubMessage": "¿Guardar cambios?", - "DialogDlcLoadNcaErrorMessage": "{0}. Archivo con error: {1}", + "DialogLoadNcaErrorMessage": "{0}. Archivo con error: {1}", "DialogDlcNoDlcErrorMessage": "¡Ese archivo no contiene contenido descargable para el título seleccionado!", "DialogPerformanceCheckLoggingEnabledMessage": "Has habilitado los registros debug, diseñados solo para uso de los desarrolladores.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para un rendimiento óptimo, se recomienda deshabilitar los registros debug. ¿Quieres deshabilitarlos ahora?", diff --git a/Ryujinx.Ava/Assets/Locales/fr_FR.json b/Ryujinx.Ava/Assets/Locales/fr_FR.json index 71f32c6ee..fad46751a 100644 --- a/Ryujinx.Ava/Assets/Locales/fr_FR.json +++ b/Ryujinx.Ava/Assets/Locales/fr_FR.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Voulez-vous supprimer le profil sélectionné ?", "DialogControllerSettingsModifiedConfirmMessage": "Les paramètres actuels du contrôleur ont été mis à jour.", "DialogControllerSettingsModifiedConfirmSubMessage": "Voulez-vous sauvegarder?", - "DialogDlcLoadNcaErrorMessage": "{0}. Fichier erroné : {1}", + "DialogLoadNcaErrorMessage": "{0}. Fichier erroné : {1}", "DialogDlcNoDlcErrorMessage": "Le fichier spécifié ne contient pas de DLC pour le titre sélectionné !", "DialogPerformanceCheckLoggingEnabledMessage": "Vous avez activé la journalisation des traces, conçue pour être utilisée uniquement par les développeurs.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Pour des performances optimales, il est recommandé de désactiver la journalisation des traces. Souhaitez-vous désactiver la journalisation des traces maintenant ?", diff --git a/Ryujinx.Ava/Assets/Locales/it_IT.json b/Ryujinx.Ava/Assets/Locales/it_IT.json index fbdd4046d..0ae027496 100644 --- a/Ryujinx.Ava/Assets/Locales/it_IT.json +++ b/Ryujinx.Ava/Assets/Locales/it_IT.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Vuoi eliminare il profilo selezionato?", "DialogControllerSettingsModifiedConfirmMessage": "Le attuali impostazioni del controller sono state aggiornate.", "DialogControllerSettingsModifiedConfirmSubMessage": "Vuoi salvare?", - "DialogDlcLoadNcaErrorMessage": "{0}. File errato: {1}", + "DialogLoadNcaErrorMessage": "{0}. File errato: {1}", "DialogDlcNoDlcErrorMessage": "Il file specificato non contiene un DLC per il titolo selezionato!", "DialogPerformanceCheckLoggingEnabledMessage": "Hai abilitato il trace logging, che è progettato per essere usato solo dagli sviluppatori.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Per prestazioni ottimali, si raccomanda di disabilitare il trace logging. Vuoi disabilitare il debug logging adesso?", diff --git a/Ryujinx.Ava/Assets/Locales/ja_JP.json b/Ryujinx.Ava/Assets/Locales/ja_JP.json index b1e0a43bf..581443f55 100644 --- a/Ryujinx.Ava/Assets/Locales/ja_JP.json +++ b/Ryujinx.Ava/Assets/Locales/ja_JP.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "選択されたプロファイルを削除しますか", "DialogControllerSettingsModifiedConfirmMessage": "現在のコントローラ設定が更新されました.", "DialogControllerSettingsModifiedConfirmSubMessage": "セーブしますか?", - "DialogDlcLoadNcaErrorMessage": "{0}. エラー発生ファイル: {1}", + "DialogLoadNcaErrorMessage": "{0}. エラー発生ファイル: {1}", "DialogDlcNoDlcErrorMessage": "選択されたファイルはこのタイトル用の DLC ではありません!", "DialogPerformanceCheckLoggingEnabledMessage": "トレースロギングを有効にします. これは開発者のみに有用な機能です.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "パフォーマンス最適化のためには,トレースロギングを無効にすることを推奨します. トレースロギングを無効にしてよろしいですか?", diff --git a/Ryujinx.Ava/Assets/Locales/ko_KR.json b/Ryujinx.Ava/Assets/Locales/ko_KR.json index 5379efa9c..adf7f61e3 100644 --- a/Ryujinx.Ava/Assets/Locales/ko_KR.json +++ b/Ryujinx.Ava/Assets/Locales/ko_KR.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "선택한 프로파일을 삭제하겠습니까?", "DialogControllerSettingsModifiedConfirmMessage": "현재 컨트롤러 설정이 업데이트되었습니다.", "DialogControllerSettingsModifiedConfirmSubMessage": "저장하겠습니까?", - "DialogDlcLoadNcaErrorMessage": "{0}입니다. 오류 발생 파일: {1}", + "DialogLoadNcaErrorMessage": "{0}입니다. 오류 발생 파일: {1}", "DialogDlcNoDlcErrorMessage": "지정된 파일에 선택한 타이틀에 대한 DLC가 포함되어 있지 않습니다!", "DialogPerformanceCheckLoggingEnabledMessage": "개발자만 사용하도록 설계된 추적 로깅이 활성화되어 있습니다.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "최적의 성능을 위해 추적 로깅을 비활성화하는 것이 좋습니다. 지금 추적 로깅을 비활성화하겠습니까?", diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/Ryujinx.Ava/Assets/Locales/pl_PL.json index 0cc0b4f91..077ccc2d5 100644 --- a/Ryujinx.Ava/Assets/Locales/pl_PL.json +++ b/Ryujinx.Ava/Assets/Locales/pl_PL.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Czy chcesz usunąć wybrany profil", "DialogControllerSettingsModifiedConfirmMessage": "Aktualne ustawienia kontrolera zostały zaktualizowane.", "DialogControllerSettingsModifiedConfirmSubMessage": "Czy chcesz zapisać?", - "DialogDlcLoadNcaErrorMessage": "{0}. Błędny Plik: {1}", + "DialogLoadNcaErrorMessage": "{0}. Błędny Plik: {1}", "DialogDlcNoDlcErrorMessage": "Określony plik nie zawiera DLC dla wybranego tytułu!", "DialogPerformanceCheckLoggingEnabledMessage": "Masz włączone rejestrowanie śledzenia, które jest przeznaczone tylko dla programistów.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Aby uzyskać optymalną wydajność, zaleca się wyłączenie rejestrowania śledzenia. Czy chcesz teraz wyłączyć rejestrowanie śledzenia?", diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json index ded6cf95f..effe8c028 100644 --- a/Ryujinx.Ava/Assets/Locales/pt_BR.json +++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Deseja deletar o perfil selecionado", "DialogControllerSettingsModifiedConfirmMessage": "As configurações de controle atuais foram atualizadas.", "DialogControllerSettingsModifiedConfirmSubMessage": "Deseja salvar?", - "DialogDlcLoadNcaErrorMessage": "{0}. Arquivo com erro: {1}", + "DialogLoadNcaErrorMessage": "{0}. Arquivo com erro: {1}", "DialogDlcNoDlcErrorMessage": "O arquivo especificado não contém DLCs para o título selecionado!", "DialogPerformanceCheckLoggingEnabledMessage": "Os logs de depuração estão ativos, esse recurso é feito para ser usado apenas por desenvolvedores.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para melhor performance, é recomendável desabilitar os logs de depuração. Gostaria de desabilitar os logs de depuração agora?", diff --git a/Ryujinx.Ava/Assets/Locales/ru_RU.json b/Ryujinx.Ava/Assets/Locales/ru_RU.json index 7b25f4554..ea60bd918 100644 --- a/Ryujinx.Ava/Assets/Locales/ru_RU.json +++ b/Ryujinx.Ava/Assets/Locales/ru_RU.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Вы хотите удалить выбранный профиль", "DialogControllerSettingsModifiedConfirmMessage": "Текущие настройки контроллера обновлены.", "DialogControllerSettingsModifiedConfirmSubMessage": "Вы хотите сохранить?", - "DialogDlcLoadNcaErrorMessage": "{0}. Файл с ошибкой: {1}", + "DialogLoadNcaErrorMessage": "{0}. Файл с ошибкой: {1}", "DialogDlcNoDlcErrorMessage": "Указанный файл не содержит DLC для выбранной игры!", "DialogPerformanceCheckLoggingEnabledMessage": "У вас включено ведение журнала отладки, предназначенное только для разработчиков.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальной производительности рекомендуется отключить ведение журнала отладки. Вы хотите отключить ведение журнала отладки сейчас?", diff --git a/Ryujinx.Ava/Assets/Locales/tr_TR.json b/Ryujinx.Ava/Assets/Locales/tr_TR.json index f277713ba..decc3cfd6 100644 --- a/Ryujinx.Ava/Assets/Locales/tr_TR.json +++ b/Ryujinx.Ava/Assets/Locales/tr_TR.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Seçilen profili silmek istiyor musunuz", "DialogControllerSettingsModifiedConfirmMessage": "Güncel kontrolcü seçenekleri güncellendi.", "DialogControllerSettingsModifiedConfirmSubMessage": "Kaydetmek istiyor musunuz?", - "DialogDlcLoadNcaErrorMessage": "{0}. Hatalı Dosya: {1}", + "DialogLoadNcaErrorMessage": "{0}. Hatalı Dosya: {1}", "DialogDlcNoDlcErrorMessage": "Belirtilen dosya seçilen oyun için DLC içermiyor!", "DialogPerformanceCheckLoggingEnabledMessage": "Sadece geliştiriler için dizayn edilen Trace Loglama seçeneği etkin.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "En iyi performans için trace loglama'nın devre dışı bırakılması tavsiye edilir. Trace loglama seçeneğini şimdi devre dışı bırakmak ister misiniz?", diff --git a/Ryujinx.Ava/Assets/Locales/uk_UA.json b/Ryujinx.Ava/Assets/Locales/uk_UA.json index 01d433da6..111337a42 100644 --- a/Ryujinx.Ava/Assets/Locales/uk_UA.json +++ b/Ryujinx.Ava/Assets/Locales/uk_UA.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "Ви хочете видалити вибраний профіль", "DialogControllerSettingsModifiedConfirmMessage": "Поточні налаштування контролера оновлено.", "DialogControllerSettingsModifiedConfirmSubMessage": "Ви хочете зберегти?", - "DialogDlcLoadNcaErrorMessage": "{0}. Файл з помилкою: {1}", + "DialogLoadNcaErrorMessage": "{0}. Файл з помилкою: {1}", "DialogDlcNoDlcErrorMessage": "Зазначений файл не містить DLC для вибраного заголовку!", "DialogPerformanceCheckLoggingEnabledMessage": "Ви увімкнули журнал налагодження, призначений лише для розробників.", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальної продуктивності рекомендується вимкнути ведення журналу налагодження. Ви хочете вимкнути ведення журналу налагодження зараз?", diff --git a/Ryujinx.Ava/Assets/Locales/zh_CN.json b/Ryujinx.Ava/Assets/Locales/zh_CN.json index cd76951a7..25dc3cbaf 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_CN.json +++ b/Ryujinx.Ava/Assets/Locales/zh_CN.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "是否删除选择的账户", "DialogControllerSettingsModifiedConfirmMessage": "目前的输入预设已更新", "DialogControllerSettingsModifiedConfirmSubMessage": "要保存吗?", - "DialogDlcLoadNcaErrorMessage": "{0}. 错误的文件: {1}", + "DialogLoadNcaErrorMessage": "{0}. 错误的文件: {1}", "DialogDlcNoDlcErrorMessage": "选择的文件不包含所选游戏的 DLC!", "DialogPerformanceCheckLoggingEnabledMessage": "您启用了跟踪日志,仅供开发人员使用。", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "为了获得最佳性能,建议禁用跟踪日志记录。您是否要立即禁用?", diff --git a/Ryujinx.Ava/Assets/Locales/zh_TW.json b/Ryujinx.Ava/Assets/Locales/zh_TW.json index e68329957..940282a0f 100644 --- a/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -370,7 +370,7 @@ "DialogUserProfileDeletionConfirmMessage": "是否刪除選擇的帳號", "DialogControllerSettingsModifiedConfirmMessage": "目前的輸入預設已更新", "DialogControllerSettingsModifiedConfirmSubMessage": "要儲存嗎?", - "DialogDlcLoadNcaErrorMessage": "{0}. 錯誤的檔案: {1}", + "DialogLoadNcaErrorMessage": "{0}. 錯誤的檔案: {1}", "DialogDlcNoDlcErrorMessage": "選擇的檔案不包含所選遊戲的 DLC!", "DialogPerformanceCheckLoggingEnabledMessage": "您啟用了跟蹤日誌,僅供開發人員使用。", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "為了獲得最佳效能,建議停用跟蹤日誌記錄。您是否要立即停用?", diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 8e5bdaec7..2dc3d37ff 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -80,8 +80,7 @@ namespace Ryujinx.Ava.Common { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog( - string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageCreateSaveErrorMessage], result.ToStringWithName())); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageCreateSaveErrorMessage, result.ToStringWithName())); }); return false; @@ -100,7 +99,7 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageFindSaveErrorMessage], result.ToStringWithName())); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageFindSaveErrorMessage, result.ToStringWithName())); }); return false; @@ -164,7 +163,7 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - string.Format(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMessage], ncaSectionType, Path.GetFileName(titleFilePath)), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)), "", "", LocaleManager.Instance[LocaleKeys.InputDialogCancel], diff --git a/Ryujinx.Ava/Common/Locale/LocaleExtension.cs b/Ryujinx.Ava/Common/Locale/LocaleExtension.cs index 6c54becdc..b82c405de 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleExtension.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleExtension.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Ava.Common.Locale ReflectionBindingExtension binding = new($"[{keyToUse}]") { - Mode = BindingMode.OneWay, + Mode = BindingMode.OneWay, Source = LocaleManager.Instance }; diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index 5bcaa437e..1374bfee1 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -13,56 +13,87 @@ namespace Ryujinx.Ava.Common.Locale { private const string DefaultLanguageCode = "en_US"; - private Dictionary _localeStrings; - private ConcurrentDictionary _dynamicValues; + private Dictionary _localeStrings; + private Dictionary _localeDefaultStrings; + private readonly ConcurrentDictionary _dynamicValues; public static LocaleManager Instance { get; } = new LocaleManager(); - public Dictionary LocaleStrings { get => _localeStrings; set => _localeStrings = value; } - public LocaleManager() { - _localeStrings = new Dictionary(); - _dynamicValues = new ConcurrentDictionary(); + _localeStrings = new Dictionary(); + _localeDefaultStrings = new Dictionary(); + _dynamicValues = new ConcurrentDictionary(); Load(); } public void Load() { + // Load the system Language Code. string localeLanguageCode = CultureInfo.CurrentCulture.Name.Replace('-', '_'); + // If the view is loaded with the UI Previewer detached, then override it with the saved one or default. if (Program.PreviewerDetached) { if (!string.IsNullOrEmpty(ConfigurationState.Instance.Ui.LanguageCode.Value)) { localeLanguageCode = ConfigurationState.Instance.Ui.LanguageCode.Value; } + else + { + localeLanguageCode = DefaultLanguageCode; + } } - // Load english first, if the target language translation is incomplete, we default to english. + // Load en_US as default, if the target language translation is incomplete. LoadDefaultLanguage(); - if (localeLanguageCode != DefaultLanguageCode) - { - LoadLanguage(localeLanguageCode); - } + LoadLanguage(localeLanguageCode); } public string this[LocaleKeys key] { get { + // Check if the locale contains the key. if (_localeStrings.TryGetValue(key, out string value)) { + // Check if the localized string needs to be formatted. if (_dynamicValues.TryGetValue(key, out var dynamicValue)) { - return string.Format(value, dynamicValue); + try + { + return string.Format(value, dynamicValue); + } + catch (Exception) + { + // If formatting failed use the default text instead. + if (_localeDefaultStrings.TryGetValue(key, out value)) + { + try + { + return string.Format(value, dynamicValue); + } + catch (Exception) + { + // If formatting the default text failed return the key. + return key.ToString(); + } + } + } } return value; } + // If the locale doesn't contain the key return the default one. + if (_localeDefaultStrings.TryGetValue(key, out string defaultValue)) + { + return defaultValue; + } + + // If the locale text doesn't exist return the key. return key.ToString(); } set @@ -73,42 +104,43 @@ namespace Ryujinx.Ava.Common.Locale } } - public void UpdateDynamicValue(LocaleKeys key, params object[] values) + public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values) { _dynamicValues[key] = values; OnPropertyChanged("Item"); + + return this[key]; } - public void LoadDefaultLanguage() + private void LoadDefaultLanguage() { - LoadLanguage(DefaultLanguageCode); + _localeDefaultStrings = LoadJsonLanguage(); } public void LoadLanguage(string languageCode) { - string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); - - if (languageJson == null) + foreach (var item in LoadJsonLanguage(languageCode)) { - return; + this[item.Key] = item.Value; } + } - var strings = JsonHelper.Deserialize>(languageJson); + private Dictionary LoadJsonLanguage(string languageCode = DefaultLanguageCode) + { + var localeStrings = new Dictionary(); + string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); + var strings = JsonHelper.Deserialize>(languageJson); foreach (var item in strings) { if (Enum.TryParse(item.Key, out var key)) { - this[key] = item.Value; + localeStrings[key] = item.Value; } } - if (Program.PreviewerDetached) - { - ConfigurationState.Instance.Ui.LanguageCode.Value = languageCode; - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } + return localeStrings; } } } \ No newline at end of file diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index 39683187e..62dc17729 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -588,7 +588,7 @@ namespace Ryujinx.Modules } catch { - Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance[LocaleKeys.UpdaterRenameFailed], file)); + Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file)); } } diff --git a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs index 0809cb4f8..a49077f21 100644 --- a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs +++ b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs @@ -29,17 +29,12 @@ namespace Ryujinx.Ava.UI.Applet public bool DisplayMessageDialog(ControllerAppletUiArgs args) { - string playerCount = args.PlayerCountMin == args.PlayerCountMax - ? args.PlayerCountMin.ToString() - : $"{args.PlayerCountMin}-{args.PlayerCountMax}"; - - LocaleKeys key = args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange; - - string message = string.Format(LocaleManager.Instance[key], - playerCount, - args.SupportedStyles, - string.Join(", ", args.SupportedPlayers), - args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : ""); + string message = LocaleManager.Instance.UpdateAndGetDynamicValue( + args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange, + args.PlayerCountMin == args.PlayerCountMax ? args.PlayerCountMin.ToString() : $"{args.PlayerCountMin}-{args.PlayerCountMax}", + args.SupportedStyles, + string.Join(", ", args.SupportedPlayers), + args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : ""); return DisplayMessageDialog(LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle], message); } @@ -92,7 +87,7 @@ namespace Ryujinx.Ava.UI.Applet } catch (Exception ex) { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageDialogErrorExceptionMessage], ex)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex)); dialogCloseEvent.Set(); } @@ -126,7 +121,8 @@ namespace Ryujinx.Ava.UI.Applet catch (Exception ex) { error = true; - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage], ex)); + + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex)); } finally { @@ -181,7 +177,8 @@ namespace Ryujinx.Ava.UI.Applet catch (Exception ex) { dialogCloseEvent.Set(); - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogErrorAppletErrorExceptionMessage], ex)); + + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex)); } }); diff --git a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index 78e6f237e..78c62a9a7 100644 --- a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -139,14 +139,16 @@ namespace Ryujinx.Ava.UI.Controls else if (_inputMin > 0 && _inputMax == int.MaxValue) { Error.IsVisible = true; - Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinCharacters], _inputMin); + + Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin); _checkLength = length => _inputMin <= length; } else { Error.IsVisible = true; - Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinRangeCharacters], _inputMin, _inputMax); + + Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax); _checkLength = length => _inputMin <= length && length <= _inputMax; } diff --git a/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs b/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs index 06ec8e302..4ed629ff1 100644 --- a/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs +++ b/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs @@ -76,11 +76,11 @@ namespace Ryujinx.Ava.UI.Helpers string setupButtonLabel = isInSetupGuide ? LocaleManager.Instance[LocaleKeys.OpenSetupGuideMessage] : ""; var result = await ContentDialogHelper.CreateInfoDialog( - string.Format(LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogMessage], errorCode, GetErrorTitle(error)), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogMessage, errorCode, GetErrorTitle(error)), GetErrorDescription(error) + (isInSetupGuide ? LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogInfoMessage] : ""), setupButtonLabel, LocaleManager.Instance[LocaleKeys.InputDialogOk], - string.Format(LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogTitle], errorCode)); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogTitle, errorCode)); if (result == UserResult.Ok) { diff --git a/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs b/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs index c57b3a26a..80476a433 100644 --- a/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs +++ b/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Ava.UI.Models public ApplicationControlProperty Control { get; } public string Path { get; } - public string Label => string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString()); + public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleUpdateVersionLabel, Control.DisplayVersionString.ToString()); public TitleUpdateModel(ApplicationControlProperty control, string path) { diff --git a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs index 5faad33fb..872c1a37f 100644 --- a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs @@ -81,10 +81,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public string Developers - { - get => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz"); - } + public string Developers => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.AboutPageDeveloperListMore, "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz"); public AboutWindowViewModel() { diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index 5d5ca5f83..f63fc3491 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -3,6 +3,8 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Svg.Skia; using Avalonia.Threading; +using LibHac.Bcat; +using LibHac.Tools.Fs; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Controls; @@ -717,7 +719,7 @@ namespace Ryujinx.Ava.UI.ViewModels { Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system."); - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileErrorMessage], ProfileName)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogProfileInvalidProfileErrorMessage, ProfileName)); return; } diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 443e0d180..8bd146ed8 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -5,8 +5,10 @@ using Avalonia.Media; using Avalonia.Threading; using DynamicData; using DynamicData.Binding; +using LibHac.Bcat; using LibHac.Fs; using LibHac.FsSystem; +using LibHac.Tools.Fs; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; @@ -947,20 +949,18 @@ namespace Ryujinx.Ava.UI.ViewModels if (firmwareVersion == null) { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage], filename)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage, filename)); return; } - string dialogTitle = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle], firmwareVersion.VersionString); + string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle, firmwareVersion.VersionString); + string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage, firmwareVersion.VersionString); SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion(); - - string dialogMessage = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage], firmwareVersion.VersionString); - if (currentVersion != null) { - dialogMessage += string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage], currentVersion.VersionString); + dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage, currentVersion.VersionString); } dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage]; @@ -993,7 +993,7 @@ namespace Ryujinx.Ava.UI.ViewModels { waitingDialog.Close(); - string message = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage], firmwareVersion.VersionString); + string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, firmwareVersion.VersionString); await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]); @@ -1063,7 +1063,7 @@ namespace Ryujinx.Ava.UI.ViewModels IsLoadingIndeterminate = false; break; case LoadState.Loaded: - LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName); IsLoadingIndeterminate = true; CacheLoadStatus = ""; break; @@ -1079,7 +1079,7 @@ namespace Ryujinx.Ava.UI.ViewModels IsLoadingIndeterminate = false; break; case ShaderCacheLoadingState.Loaded: - LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName); IsLoadingIndeterminate = true; CacheLoadStatus = ""; break; @@ -1333,10 +1333,15 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void ChangeLanguage(object obj) + public void ChangeLanguage(object languageCode) { - LocaleManager.Instance.LoadDefaultLanguage(); - LocaleManager.Instance.LoadLanguage((string)obj); + LocaleManager.Instance.LoadLanguage((string)languageCode); + + if (Program.PreviewerDetached) + { + ConfigurationState.Instance.Ui.LanguageCode.Value = (string)languageCode; + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + } } public async void ManageProfiles() @@ -1374,7 +1379,7 @@ namespace Ryujinx.Ava.UI.ViewModels // FIXME: Found a way to reproduce the bold effect on the title name (fork?). UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], - string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionMessage], selection.TitleName), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, selection.TitleName), LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); @@ -1401,7 +1406,7 @@ namespace Ryujinx.Ava.UI.ViewModels } catch (Exception e) { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], file.Name, e)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, e)); } } } @@ -1438,7 +1443,7 @@ namespace Ryujinx.Ava.UI.ViewModels // FIXME: Found a way to reproduce the bold effect on the title name (fork?). UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], - string.Format(LocaleManager.Instance[LocaleKeys.DialogShaderDeletionMessage], selection.TitleName), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, selection.TitleName), LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); @@ -1463,7 +1468,7 @@ namespace Ryujinx.Ava.UI.ViewModels } catch (Exception e) { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], directory.Name, e)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, e)); } } } @@ -1476,7 +1481,7 @@ namespace Ryujinx.Ava.UI.ViewModels } catch (Exception e) { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.ShaderCachePurgeError], file.Name, e)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, e)); } } } @@ -1641,7 +1646,7 @@ namespace Ryujinx.Ava.UI.ViewModels StatusBarProgressMaximum = 0; StatusBarProgressValue = 0; - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); }); ReloadGameList?.Invoke(); @@ -1755,8 +1760,14 @@ namespace Ryujinx.Ava.UI.ViewModels } CanUpdate = false; - LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName; - TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; + + LoadHeading = TitleName = titleName; + + if (string.IsNullOrWhiteSpace(titleName)) + { + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Application.TitleName); + TitleName = AppHost.Device.Application.TitleName; + } SwitchToRenderer(startFullscreen); @@ -1807,14 +1818,13 @@ namespace Ryujinx.Ava.UI.ViewModels if (version != null) { - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, - version.VersionString); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, version.VersionString); hasApplet = version.Major > 3; } else { - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0"); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0"); } IsAppletMenuActive = hasApplet; diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index 131ebd25b..1bac94245 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -181,7 +181,7 @@ public class TitleUpdateViewModel : BaseModel { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)); }); } } diff --git a/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs index dad74230f..097634a80 100644 --- a/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs @@ -17,8 +17,7 @@ namespace Ryujinx.Ava.UI.ViewModels private ObservableCollection _views = new(); private AccountManager _accountManager; - public string SaveManagerHeading => - string.Format(LocaleManager.Instance[LocaleKeys.SaveManagerHeading], _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); + public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); public int SortIndex { diff --git a/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index b27d20d37..cb939763b 100644 --- a/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Ava.UI.Windows { LoadedCheats = new AvaloniaList(); - Heading = string.Format(LocaleManager.Instance[LocaleKeys.CheatWindowHeading], titleName, titleId.ToUpper()); + Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper()); InitializeComponent(); diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs index fa9d70e22..47216c489 100644 --- a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs @@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Windows private void PrintHeading() { - Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], _downloadableContents.Count, _titleName, _titleId.ToString("X16")); + Heading.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DlcWindowHeading, _downloadableContents.Count, _titleName, _titleId.ToString("X16")); } private void LoadDownloadableContents() @@ -133,7 +133,7 @@ namespace Ryujinx.Ava.UI.Windows { Dispatcher.UIThread.InvokeAsync(async () => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, containerPath)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, containerPath)); }); } diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index dcd89ac0a..921dfbb11 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -146,7 +146,7 @@ namespace Ryujinx.Ava.UI.Windows private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e) { - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound); Dispatcher.UIThread.Post(() => { @@ -416,7 +416,7 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.StatusBarProgressMaximum = 0; ViewModel.StatusBarProgressValue = 0; - LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); }); ReloadGameList(); diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 9d8b9a7b9..41370e669 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Windows SecondaryButtonText = "", CloseButtonText = "", Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName), - Title = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], titleName, titleId.ToString("X16")) + Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")) }; Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); From a47824f96101a1c1e63b7622f0c4e61ba6345a98 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sat, 21 Jan 2023 02:57:37 +0100 Subject: [PATCH 312/737] Ava UI: Add Notifications and Cleanup (#4275) * Ava UI: Add Notifications and Cleanup * Revert notifications on ErrorDialog * remove unused code from game list views * Fix cast --- Ryujinx.Ava/Common/ApplicationHelper.cs | 240 ++++++++---------- Ryujinx.Ava/UI/Controls/GameGridView.axaml | 5 +- Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs | 50 +--- Ryujinx.Ava/UI/Controls/GameListView.axaml | 5 +- Ryujinx.Ava/UI/Controls/GameListView.axaml.cs | 50 +--- Ryujinx.Ava/UI/Helpers/NotificationHelper.cs | 65 +++++ .../UI/ViewModels/MainWindowViewModel.cs | 182 +++++-------- Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 2 + 8 files changed, 270 insertions(+), 329 deletions(-) create mode 100644 Ryujinx.Ava/UI/Helpers/NotificationHelper.cs diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 2dc3d37ff..0b8bd8da1 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Controls.Notifications; using Avalonia.Threading; using LibHac; using LibHac.Account; @@ -12,7 +13,6 @@ 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.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Logging; @@ -44,14 +44,11 @@ namespace Ryujinx.Ava.Common _accountManager = accountManager; } - private static bool TryFindSaveData(string titleName, ulong titleId, - BlitStruct controlHolder, in SaveDataFilter filter, out ulong saveDataId) + private static bool TryFindSaveData(string titleName, ulong titleId, BlitStruct controlHolder, in SaveDataFilter filter, out ulong saveDataId) { saveDataId = default; - Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, - SaveDataSpaceId.User, in filter); - + Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, in filter); if (ResultFs.TargetNotFound.Includes(result)) { ref ApplicationControlProperty control = ref controlHolder.Value; @@ -68,17 +65,15 @@ namespace Ryujinx.Ava.Common control.UserAccountSaveDataSize = 0x4000; control.UserAccountSaveDataJournalSize = 0x4000; - Logger.Warning?.Print(LogClass.Application, - "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); + Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } - Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); + Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); - if (result.IsFailure()) { - Dispatcher.UIThread.Post(async () => + Dispatcher.UIThread.InvokeAsync(async () => { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageCreateSaveErrorMessage, result.ToStringWithName())); }); @@ -97,7 +92,7 @@ namespace Ryujinx.Ava.Common return true; } - Dispatcher.UIThread.Post(async () => + Dispatcher.UIThread.InvokeAsync(async () => { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageFindSaveErrorMessage, result.ToStringWithName())); }); @@ -105,8 +100,7 @@ namespace Ryujinx.Ava.Common return false; } - public static void OpenSaveDir(in SaveDataFilter saveDataFilter, ulong titleId, - BlitStruct controlData, string titleName) + public static void OpenSaveDir(in SaveDataFilter saveDataFilter, ulong titleId, BlitStruct controlData, string titleName) { if (!TryFindSaveData(titleName, titleId, controlData, in saveDataFilter, out ulong saveDataId)) { @@ -147,14 +141,15 @@ namespace Ryujinx.Ava.Common } } - public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, - int programIndex = 0) + public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0) { - OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] }; + OpenFolderDialog folderDialog = new() + { + Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] + }; - string destination = await folderDialog.ShowAsync(_owner); - - var cancellationToken = new CancellationTokenSource(); + string destination = await folderDialog.ShowAsync(_owner); + var cancellationToken = new CancellationTokenSource(); if (!string.IsNullOrWhiteSpace(destination)) { @@ -174,133 +169,122 @@ namespace Ryujinx.Ava.Common cancellationToken.Cancel(); } }); + + using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); - Thread.Sleep(1000); + Nca mainNca = null; + Nca patchNca = null; - using (FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read)) + string extension = Path.GetExtension(titleFilePath).ToLower(); + if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") { - Nca mainNca = null; - Nca patchNca = null; + PartitionFileSystem pfs; - string extension = Path.GetExtension(titleFilePath).ToLower(); - - if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") + if (extension == ".xci") { - PartitionFileSystem pfs; + pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure); + } + else + { + pfs = new PartitionFileSystem(file.AsStorage()); + } - if (extension == ".xci") + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef(); + + pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); + if (nca.Header.ContentType == NcaContentType.Program) { - Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - - pfs = xci.OpenPartition(XciPartitionType.Secure); - } - else - { - pfs = new PartitionFileSystem(file.AsStorage()); - } - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef(); - - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); - - if (nca.Header.ContentType == NcaContentType.Program) + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { - int dataIndex = - Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } + patchNca = nca; + } + else + { + mainNca = nca; } } } - else if (extension == ".nca") - { - mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); - } + } + else if (extension == ".nca") + { + mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); + } - if (mainNca == null) + if (mainNca == null) + { + Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file"); + + Dispatcher.UIThread.InvokeAsync(async () => { - Logger.Error?.Print(LogClass.Application, - "Extraction failure. The main NCA was not present in the selected file"); - Dispatcher.UIThread.InvokeAsync(async () => + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); + }); + + return; + } + + (Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); + if (updatePatchNca != null) + { + patchNca = updatePatchNca; + } + + int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); + + try + { + IFileSystem ncaFileSystem = patchNca != null + ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) + : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); + + FileSystemClient fsClient = _horizonClient.Fs; + + string source = DateTime.Now.ToFileTime().ToString()[10..]; + string output = DateTime.Now.ToFileTime().ToString()[10..]; + + using var uniqueSourceFs = new UniqueRef(ncaFileSystem); + using var uniqueOutputFs = new UniqueRef(new LocalFileSystem(destination)); + + fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref()); + fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref()); + + (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token); + + if (!canceled) + { + if (resultCode.Value.IsFailure()) { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); - }); - return; - } + Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); - (Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, - mainNca.Header.TitleId.ToString("x16"), programIndex, out _); - if (updatePatchNca != null) - { - patchNca = updatePatchNca; - } - - int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); - - try - { - IFileSystem ncaFileSystem = patchNca != null - ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) - : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); - - FileSystemClient fsClient = _horizonClient.Fs; - - string source = DateTime.Now.ToFileTime().ToString()[10..]; - string output = DateTime.Now.ToFileTime().ToString()[10..]; - - using var uniqueSourceFs = new UniqueRef(ncaFileSystem); - using var uniqueOutputFs = new UniqueRef(new LocalFileSystem(destination)); - - fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref()); - fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref()); - - (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token); - - if (!canceled) - { - if (resultCode.Value.IsFailure()) + Dispatcher.UIThread.InvokeAsync(async () => { - Logger.Error?.Print(LogClass.Application, - $"LibHac returned error code: {resultCode.Value.ErrorCode}"); - Dispatcher.UIThread.InvokeAsync(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); - }); - } - else if (resultCode.Value.IsSuccess()) - { - Dispatcher.UIThread.InvokeAsync(async () => - { - await ContentDialogHelper.CreateInfoDialog( - LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage], - "", - LocaleManager.Instance[LocaleKeys.InputDialogOk], - "", - LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]); - }); - } + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); + }); } - - fsClient.Unmount(source.ToU8Span()); - fsClient.Unmount(output.ToU8Span()); - } - catch (ArgumentException ex) - { - Dispatcher.UIThread.InvokeAsync(async () => + else if (resultCode.Value.IsSuccess()) { - await ContentDialogHelper.CreateErrorDialog(ex.Message); - }); + NotificationHelper.Show( + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle], + $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}", + NotificationType.Information); + } } + + fsClient.Unmount(source.ToU8Span()); + fsClient.Unmount(output.ToU8Span()); + } + catch (ArgumentException ex) + { + Logger.Error?.Print(LogClass.Application, $"{ex.Message}"); + + Dispatcher.UIThread.InvokeAsync(async () => + { + await ContentDialogHelper.CreateErrorDialog(ex.Message); + }); } }); diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml b/Ryujinx.Ava/UI/Controls/GameGridView.axaml index 862bc6d30..32cabfaa8 100644 --- a/Ryujinx.Ava/UI/Controls/GameGridView.axaml +++ b/Ryujinx.Ava/UI/Controls/GameGridView.axaml @@ -14,7 +14,7 @@ Focusable="True"> - + diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs b/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs index 531b54357..aa76b7c95 100644 --- a/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs @@ -1,9 +1,7 @@ -using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; -using LibHac.Common; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ui.App.Common; @@ -13,16 +11,25 @@ namespace Ryujinx.Ava.UI.Controls { public partial class GameGridView : UserControl { - private ApplicationData _selectedApplication; public static readonly RoutedEvent ApplicationOpenedEvent = RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); public event EventHandler ApplicationOpened { - add { AddHandler(ApplicationOpenedEvent, value); } + add { AddHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); } } + public GameGridView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + public void GameList_DoubleTapped(object sender, RoutedEventArgs args) { if (sender is ListBox listBox) @@ -38,46 +45,13 @@ namespace Ryujinx.Ava.UI.Controls { if (sender is ListBox listBox) { - _selectedApplication = listBox.SelectedItem as ApplicationData; - - (DataContext as MainWindowViewModel).GridSelectedApplication = _selectedApplication; + (DataContext as MainWindowViewModel).GridSelectedApplication = listBox.SelectedItem as ApplicationData; } } - public ApplicationData SelectedApplication => _selectedApplication; - - public GameGridView() - { - InitializeComponent(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) { (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; } - - private void MenuBase_OnMenuOpened(object sender, EventArgs e) - { - var selection = SelectedApplication; - - if (selection != null) - { - if (sender is ContextMenu menu) - { - bool canHaveUserSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.UserAccountSaveDataSize > 0; - bool canHaveDeviceSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.DeviceSaveDataSize > 0; - bool canHaveBcatSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; - - ((menu.Items as AvaloniaList)[2] as MenuItem).IsEnabled = canHaveUserSave; - ((menu.Items as AvaloniaList)[3] as MenuItem).IsEnabled = canHaveDeviceSave; - ((menu.Items as AvaloniaList)[4] as MenuItem).IsEnabled = canHaveBcatSave; - } - } - } } } diff --git a/Ryujinx.Ava/UI/Controls/GameListView.axaml b/Ryujinx.Ava/UI/Controls/GameListView.axaml index 2ba4a204d..c13eaae80 100644 --- a/Ryujinx.Ava/UI/Controls/GameListView.axaml +++ b/Ryujinx.Ava/UI/Controls/GameListView.axaml @@ -13,7 +13,7 @@ mc:Ignorable="d"> - + diff --git a/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs b/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs index bded1dec7..a64497097 100644 --- a/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs @@ -1,9 +1,7 @@ -using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; -using LibHac.Common; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ui.App.Common; @@ -13,16 +11,25 @@ namespace Ryujinx.Ava.UI.Controls { public partial class GameListView : UserControl { - private ApplicationData _selectedApplication; public static readonly RoutedEvent ApplicationOpenedEvent = RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); public event EventHandler ApplicationOpened { - add { AddHandler(ApplicationOpenedEvent, value); } + add { AddHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); } } + public GameListView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + public void GameList_DoubleTapped(object sender, RoutedEventArgs args) { if (sender is ListBox listBox) @@ -38,46 +45,13 @@ namespace Ryujinx.Ava.UI.Controls { if (sender is ListBox listBox) { - _selectedApplication = listBox.SelectedItem as ApplicationData; - - (DataContext as MainWindowViewModel).ListSelectedApplication = _selectedApplication; + (DataContext as MainWindowViewModel).ListSelectedApplication = listBox.SelectedItem as ApplicationData; } } - public ApplicationData SelectedApplication => _selectedApplication; - - public GameListView() - { - InitializeComponent(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) { (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; } - - private void MenuBase_OnMenuOpened(object sender, EventArgs e) - { - var selection = SelectedApplication; - - if (selection != null) - { - if (sender is ContextMenu menu) - { - bool canHaveUserSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.UserAccountSaveDataSize > 0; - bool canHaveDeviceSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.DeviceSaveDataSize > 0; - bool canHaveBcatSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; - - ((menu.Items as AvaloniaList)[2] as MenuItem).IsEnabled = canHaveUserSave; - ((menu.Items as AvaloniaList)[3] as MenuItem).IsEnabled = canHaveDeviceSave; - ((menu.Items as AvaloniaList)[4] as MenuItem).IsEnabled = canHaveBcatSave; - } - } - } } } diff --git a/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs b/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs new file mode 100644 index 000000000..7e2afb8bd --- /dev/null +++ b/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs @@ -0,0 +1,65 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Notifications; +using Avalonia.Threading; +using Ryujinx.Ava.Common.Locale; +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Ryujinx.Ava.UI.Helpers +{ + public static class NotificationHelper + { + private const int MaxNotifications = 4; + private const int NotificationDelayInMs = 5000; + + private static WindowNotificationManager _notificationManager; + + private static readonly ManualResetEvent _templateAppliedEvent = new(false); + private static readonly BlockingCollection _notifications = new(); + + public static void SetNotificationManager(Window host) + { + _notificationManager = new WindowNotificationManager(host) + { + Position = NotificationPosition.BottomRight, + MaxItems = MaxNotifications, + Margin = new Thickness(0, 0, 15, 40) + }; + + _notificationManager.TemplateApplied += (sender, args) => + { + _templateAppliedEvent.Set(); + }; + + Task.Run(async () => + { + _templateAppliedEvent.WaitOne(); + + foreach (var notification in _notifications.GetConsumingEnumerable()) + { + Dispatcher.UIThread.Post(() => + { + _notificationManager.Show(notification); + }); + + await Task.Delay(NotificationDelayInMs / MaxNotifications); + } + }); + } + + public static void Show(string title, string text, NotificationType type, bool waitingExit = false, Action onClick = null, Action onClose = null) + { + var delay = waitingExit ? TimeSpan.FromMilliseconds(0) : TimeSpan.FromMilliseconds(NotificationDelayInMs); + + _notifications.Add(new Notification(title, text, type, delay, onClick, onClose)); + } + + public static void ShowError(string message) + { + Show(LocaleManager.Instance[LocaleKeys.DialogErrorTitle], $"{LocaleManager.Instance[LocaleKeys.DialogErrorMessage]}\n\n{message}", NotificationType.Error); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 8bd146ed8..6fea1844f 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -5,7 +5,7 @@ using Avalonia.Media; using Avalonia.Threading; using DynamicData; using DynamicData.Binding; -using LibHac.Bcat; +using LibHac.Common; using LibHac.Fs; using LibHac.FsSystem; using LibHac.Tools.Fs; @@ -344,6 +344,12 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public bool EnabledUserSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; + + public bool EnabledDeviceSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; + + public bool EnabledBcatSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; + public string LoadHeading { get => _loadHeading; @@ -735,19 +741,14 @@ namespace Ryujinx.Ava.UI.ViewModels { get { - switch (ConfigurationState.Instance.Ui.GridSize) + return ConfigurationState.Instance.Ui.GridSize.Value switch { - case 1: - return 78; - case 2: - return 100; - case 3: - return 120; - case 4: - return 140; - default: - return 16; - } + 1 => 78, + 2 => 100, + 3 => 120, + 4 => 140, + _ => 16, + }; } } @@ -755,19 +756,14 @@ namespace Ryujinx.Ava.UI.ViewModels { get { - switch (ConfigurationState.Instance.Ui.GridSize) + return ConfigurationState.Instance.Ui.GridSize.Value switch { - case 1: - return 120; - case 2: - return ShowNames ? 210 : 150; - case 3: - return ShowNames ? 240 : 180; - case 4: - return ShowNames ? 280 : 220; - default: - return 16; - } + 1 => 120, + 2 => ShowNames ? 210 : 150, + 3 => ShowNames ? 240 : 180, + 4 => ShowNames ? 280 : 220, + _ => 16, + }; } } @@ -1091,35 +1087,27 @@ namespace Ryujinx.Ava.UI.ViewModels })); } - private void OpenSaveDirectory(in SaveDataFilter filter, ApplicationData data, ulong titleId) - { - ApplicationHelper.OpenSaveDir(in filter, titleId, data.ControlHolder, data.TitleName); - } - private async void ExtractLogo() { - var selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Logo, selection.Path); + await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName); } } private async void ExtractRomFs() { - var selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Data, selection.Path); + await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName); } } private async void ExtractExeFs() { - var selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Code, selection.Path); + await ApplicationHelper.ExtractSection(NcaSectionType.Code, SelectedApplication.Path, SelectedApplication.TitleName); } } @@ -1487,56 +1475,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void OpenDeviceSaveDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - Task.Run(() => - { - if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) - { - async void Action() - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); - } - - Dispatcher.UIThread.Post(Action); - - return; - } - - var saveDataFilter = SaveDataFilter.Make(titleIdNumber, SaveDataType.Device, userId: default, saveDataId: default, index: default); - OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); - }); - } - } - - public void OpenBcatSaveDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - Task.Run(() => - { - if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) - { - async void Action() - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); - } - - Dispatcher.UIThread.Post(Action); - - return; - } - - var saveDataFilter = SaveDataFilter.Make(titleIdNumber, SaveDataType.Bcat, userId: default, saveDataId: default, index: default); - OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); - }); - } - } - public void ToggleFavorite() { ApplicationData selection = SelectedApplication; @@ -1555,37 +1493,45 @@ namespace Ryujinx.Ava.UI.ViewModels public void OpenUserSaveDirectory() { - ApplicationData selection = SelectedApplication; - if (selection != null) + OpenSaveDirectory(SaveDataType.Account, userId: new UserId((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low)); + } + + public void OpenDeviceSaveDirectory() + { + OpenSaveDirectory(SaveDataType.Device, userId: default); + } + + public void OpenBcatSaveDirectory() + { + OpenSaveDirectory(SaveDataType.Bcat, userId: default); + } + + private void OpenSaveDirectory(SaveDataType saveDataType, UserId userId) + { + if (SelectedApplication != null) { - Task.Run(() => + if (!ulong.TryParse(SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) { - if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) + Dispatcher.UIThread.InvokeAsync(async () => { - async void Action() - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); - } + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); + }); - Dispatcher.UIThread.Post(Action); + return; + } - return; - } + var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default); - UserId userId = new((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low); - SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default); - OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); - }); + ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, SelectedApplication.ControlHolder, SelectedApplication.TitleName); } } public void OpenModsDirectory() { - ApplicationData selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId); + string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -1593,12 +1539,10 @@ namespace Ryujinx.Ava.UI.ViewModels public void OpenSdModsDirectory() { - ApplicationData selection = SelectedApplication; - - if (selection != null) + if (SelectedApplication != null) { string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId); + string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -1614,25 +1558,17 @@ namespace Ryujinx.Ava.UI.ViewModels public async void OpenDownloadableContentManager() { - ApplicationData selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(desktop.MainWindow); - } + await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName).ShowDialog(TopLevel as Window); } } public async void OpenCheatManager() { - ApplicationData selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - await new CheatWindow(VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(desktop.MainWindow); - } + await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window); } } diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index 921dfbb11..81e055063 100644 --- a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -104,6 +104,8 @@ namespace Ryujinx.Ava.UI.Windows ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; ViewModel.ReloadGameList += ReloadGameList; + + NotificationHelper.SetNotificationManager(this); } private void IsActiveChanged(bool obj) From 2747f125918951673b298964889276ded5de935d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Jan 2023 05:24:57 +0100 Subject: [PATCH 313/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.25.1 to 6.26.0 (#4322) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.25.1 to 6.26.0. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 61b63f3f5..52cc1235b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + From 6adf15e479b684cad7a783e7a1a056be087fdc02 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 21 Jan 2023 12:18:05 -0300 Subject: [PATCH 314/737] Implement CSET and CSETP shader instructions (#4318) * Implement CSET and CSETP shader instructions * Shader cache version bump * Fix CC.HI --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Decoders/InstDecoders.cs | 1 + .../Instructions/InstEmit.cs | 7 -- .../Instructions/InstEmitConditionCode.cs | 87 +++++++++++++++++++ .../Instructions/InstEmitFlowControl.cs | 18 +--- .../Instructions/InstEmitIntegerComparison.cs | 17 ---- 6 files changed, 90 insertions(+), 42 deletions(-) create mode 100644 Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 2622ea3e8..9f436502f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3939; + private const uint CodeGenVersion = 4318; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs index 98a436407..0c22ddc05 100644 --- a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs +++ b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs @@ -91,6 +91,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Neu = 13, Geu = 14, T = 15, + Off = 16, Lo = 17, Sff = 18, Ls = 19, diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs index c242963a6..3a9e658aa 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs @@ -54,13 +54,6 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Config.GpuAccessor.Log("Shader instruction Cctlt is not implemented."); } - public static void Cset(EmitterContext context) - { - InstCset op = context.GetOp(); - - context.Config.GpuAccessor.Log("Shader instruction Cset is not implemented."); - } - public static void Cs2r(EmitterContext context) { InstCs2r op = context.GetOp(); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs new file mode 100644 index 000000000..74ac76029 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs @@ -0,0 +1,87 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Cset(EmitterContext context) + { + InstCset op = context.GetOp(); + + Operand res = GetCondition(context, op.Ccc); + Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv); + + res = GetPredLogicalOp(context, op.Bop, res, srcPred); + + Operand dest = GetDest(op.Dest); + + if (op.BVal) + { + context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0))); + } + else + { + context.Copy(dest, res); + } + + // TODO: CC. + } + + public static void Csetp(EmitterContext context) + { + InstCsetp op = context.GetOp(); + + Operand p0Res = GetCondition(context, op.Ccc); + Operand p1Res = context.BitwiseNot(p0Res); + Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv); + + p0Res = GetPredLogicalOp(context, op.Bop, p0Res, srcPred); + p1Res = GetPredLogicalOp(context, op.Bop, p1Res, srcPred); + + context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res); + context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res); + + // TODO: CC. + } + + private static Operand GetCondition(EmitterContext context, Ccc cond, int defaultCond = IrConsts.True) + { + return cond switch + { + Ccc.F => Const(IrConsts.False), + Ccc.Lt => context.BitwiseExclusiveOr(context.BitwiseAnd(GetNF(), context.BitwiseNot(GetZF())), GetVF()), + Ccc.Eq => context.BitwiseAnd(context.BitwiseNot(GetNF()), GetZF()), + Ccc.Le => context.BitwiseExclusiveOr(GetNF(), context.BitwiseOr(GetZF(), GetVF())), + Ccc.Gt => context.BitwiseNot(context.BitwiseOr(context.BitwiseExclusiveOr(GetNF(), GetVF()), GetZF())), + Ccc.Ne => context.BitwiseNot(GetZF()), + Ccc.Ge => context.BitwiseNot(context.BitwiseExclusiveOr(GetNF(), GetVF())), + Ccc.Num => context.BitwiseNot(context.BitwiseAnd(GetNF(), GetZF())), + Ccc.Nan => context.BitwiseAnd(GetNF(), GetZF()), + Ccc.Ltu => context.BitwiseExclusiveOr(GetNF(), GetVF()), + Ccc.Equ => GetZF(), + Ccc.Leu => context.BitwiseOr(context.BitwiseExclusiveOr(GetNF(), GetVF()), GetZF()), + Ccc.Gtu => context.BitwiseExclusiveOr(context.BitwiseNot(GetNF()), context.BitwiseOr(GetVF(), GetZF())), + Ccc.Neu => context.BitwiseOr(GetNF(), context.BitwiseNot(GetZF())), + Ccc.Geu => context.BitwiseExclusiveOr(context.BitwiseOr(context.BitwiseNot(GetNF()), GetZF()), GetVF()), + Ccc.T => Const(IrConsts.True), + Ccc.Off => context.BitwiseNot(GetVF()), + Ccc.Lo => context.BitwiseNot(GetCF()), + Ccc.Sff => context.BitwiseNot(GetNF()), + Ccc.Ls => context.BitwiseOr(GetZF(), context.BitwiseNot(GetCF())), + Ccc.Hi => context.BitwiseAnd(GetCF(), context.BitwiseNot(GetZF())), + Ccc.Sft => GetNF(), + Ccc.Hs => GetCF(), + Ccc.Oft => GetVF(), + Ccc.Rle => context.BitwiseOr(GetNF(), GetZF()), + Ccc.Rgt => context.BitwiseNot(context.BitwiseOr(GetNF(), GetZF())), + _ => Const(defaultCond) + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs index f1dd279c8..91c232303 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs @@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - // TODO: Support CC here aswell (condition). + // TODO: Support CC here as well (condition). foreach (SyncTarget target in targets.Values) { PushOpInfo pushOpInfo = target.PushOpInfo; @@ -318,21 +318,5 @@ namespace Ryujinx.Graphics.Shader.Instructions context.BranchIfTrue(label, pred); } } - - private static Operand GetCondition(EmitterContext context, Ccc cond, int defaultCond = IrConsts.True) - { - // TODO: More condition codes, figure out how they work. - switch (cond) - { - case Ccc.Eq: - case Ccc.Equ: - return GetZF(); - case Ccc.Ne: - case Ccc.Neu: - return context.BitwiseNot(GetZF()); - } - - return Const(defaultCond); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs index ddd90f8e8..dcdb189fb 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs @@ -11,23 +11,6 @@ namespace Ryujinx.Graphics.Shader.Instructions { static partial class InstEmit { - public static void Csetp(EmitterContext context) - { - InstCsetp op = context.GetOp(); - - // TODO: Implement that properly. - - Operand p0Res = Const(IrConsts.True); - Operand p1Res = context.BitwiseNot(p0Res); - Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv); - - p0Res = GetPredLogicalOp(context, op.Bop, p0Res, srcPred); - p1Res = GetPredLogicalOp(context, op.Bop, p1Res, srcPred); - - context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res); - context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res); - } - public static void IcmpR(EmitterContext context) { InstIcmpR op = context.GetOp(); From 423dbc88883d46d11293156e3320b08ba7a90797 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 21 Jan 2023 19:49:55 +0000 Subject: [PATCH 315/737] Use volatile read/writes for GAL threading (#4327) --- Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 58058be2c..74326f1d2 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -135,7 +135,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading // The other thread can only increase the command count. // We can assume that if it is above 0, it will stay there or get higher. - while (_commandCount > 0 && Volatile.Read(ref _interruptAction) == null) + while (Volatile.Read(ref _commandCount) > 0 && Volatile.Read(ref _interruptAction) == null) { int commandPtr = _consumerPtr; @@ -169,7 +169,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading internal ref T New() where T : struct { - while (_producerPtr == (_consumerPtr + QueueCount - 1) % QueueCount) + while (_producerPtr == (Volatile.Read(ref _consumerPtr) + QueueCount - 1) % QueueCount) { // If incrementing the producer pointer would overflow, we need to wait. // _consumerPtr can only move forward, so there's no race to worry about here. From 7b7f62c776361d83bdb0a4035b7ebdce832941be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Jan 2023 20:04:39 +0000 Subject: [PATCH 316/737] nuget: bump Microsoft.CodeAnalysis.Analyzers from 3.3.3 to 3.3.4 (#4310) * nuget: bump Microsoft.CodeAnalysis.Analyzers from 3.3.3 to 3.3.4 Bumps [Microsoft.CodeAnalysis.Analyzers](https://github.com/dotnet/roslyn-analyzers) from 3.3.3 to 3.3.4. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/main/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v3.3.3...v3.3.4) --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.Analyzers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Fixes warning Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ac_K --- Directory.Packages.props | 2 +- Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj | 1 + .../Ryujinx.Horizon.Kernel.Generators.csproj | 1 + Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 52cc1235b..d60960fbd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -20,7 +20,7 @@ - + diff --git a/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj b/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj index 67fab2d55..d58803993 100644 --- a/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj +++ b/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj @@ -2,6 +2,7 @@ netstandard2.0 + true diff --git a/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj b/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj index 67fab2d55..d58803993 100644 --- a/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj +++ b/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj @@ -2,6 +2,7 @@ netstandard2.0 + true diff --git a/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj b/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj index b7fe19544..05cbc7644 100644 --- a/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj +++ b/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj @@ -4,6 +4,7 @@ netstandard2.0 enable latest + true From 7fea26e97e74e7ec0a5fa27921aa40c31b2c1dd9 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 21 Jan 2023 21:07:43 -0300 Subject: [PATCH 317/737] Remove use of reflection on GAL multithreading (#4287) * Introduce new IGALCommand interface and use it * Remove use of reflection on GAL multithreading * Unmanaged constraint --- .../Multithreading/CommandHelper.cs | 292 ++++++------------ .../Multithreading/Commands/BarrierCommand.cs | 2 +- .../Commands/BeginTransformFeedbackCommand.cs | 2 +- .../Commands/Buffer/BufferDisposeCommand.cs | 2 +- .../Commands/Buffer/BufferGetDataCommand.cs | 2 +- .../Commands/Buffer/BufferSetDataCommand.cs | 2 +- .../Commands/ClearBufferCommand.cs | 2 +- .../Commands/ClearRenderTargetColorCommand.cs | 2 +- .../ClearRenderTargetDepthStencilCommand.cs | 2 +- .../Commands/CommandBufferBarrierCommand.cs | 2 +- .../Commands/CopyBufferCommand.cs | 2 +- .../CounterEventDisposeCommand.cs | 2 +- .../CounterEvent/CounterEventFlushCommand.cs | 2 +- .../Commands/DispatchComputeCommand.cs | 2 +- .../Multithreading/Commands/DrawCommand.cs | 2 +- .../Commands/DrawIndexedCommand.cs | 2 +- .../Commands/DrawIndexedIndirectCommand.cs | 2 +- .../DrawIndexedIndirectCountCommand.cs | 2 +- .../Commands/DrawIndirectCommand.cs | 2 +- .../Commands/DrawIndirectCountCommand.cs | 2 +- .../Commands/DrawTextureCommand.cs | 2 +- .../EndHostConditionalRenderingCommand.cs | 4 +- .../Commands/EndTransformFeedbackCommand.cs | 2 +- .../Multithreading/Commands/IGALCommand.cs | 5 + .../Program/ProgramCheckLinkCommand.cs | 2 +- .../Commands/Program/ProgramDisposeCommand.cs | 2 +- .../Program/ProgramGetBinaryCommand.cs | 2 +- .../Commands/Renderer/ActionCommand.cs | 2 +- .../Commands/Renderer/CreateBufferCommand.cs | 2 +- .../Commands/Renderer/CreateProgramCommand.cs | 2 +- .../Commands/Renderer/CreateSamplerCommand.cs | 2 +- .../Commands/Renderer/CreateSyncCommand.cs | 2 +- .../Commands/Renderer/CreateTextureCommand.cs | 2 +- .../Renderer/GetCapabilitiesCommand.cs | 2 +- .../Commands/Renderer/PreFrameCommand.cs | 2 +- .../Commands/Renderer/ReportCounterCommand.cs | 2 +- .../Commands/Renderer/ResetCounterCommand.cs | 2 +- .../Renderer/UpdateCountersCommand.cs | 2 +- .../Commands/Sampler/SamplerDisposeCommand.cs | 2 +- .../Commands/SetAlphaTestCommand.cs | 2 +- .../Commands/SetBlendStateCommand.cs | 2 +- .../Commands/SetDepthBiasCommand.cs | 2 +- .../Commands/SetDepthClampCommand.cs | 2 +- .../Commands/SetDepthModeCommand.cs | 2 +- .../Commands/SetDepthTestCommand.cs | 2 +- .../Commands/SetFaceCullingCommand.cs | 2 +- .../Commands/SetFrontFaceCommand.cs | 2 +- .../Commands/SetImageCommand.cs | 2 +- .../Commands/SetIndexBufferCommand.cs | 2 +- .../Commands/SetLineParametersCommand.cs | 2 +- .../Commands/SetLogicOpStateCommand.cs | 2 +- .../Commands/SetMultisampleStateCommand.cs | 2 +- .../Commands/SetPatchParametersCommand.cs | 2 +- .../Commands/SetPointParametersCommand.cs | 2 +- .../Commands/SetPolygonModeCommand.cs | 2 +- .../Commands/SetPrimitiveRestartCommand.cs | 2 +- .../Commands/SetPrimitiveTopologyCommand.cs | 2 +- .../Commands/SetProgramCommand.cs | 2 +- .../Commands/SetRasterizerDiscardCommand.cs | 2 +- .../SetRenderTargetColorMasksCommand.cs | 2 +- .../Commands/SetRenderTargetScaleCommand.cs | 2 +- .../Commands/SetRenderTargetsCommand.cs | 2 +- .../Commands/SetScissorsCommand.cs | 2 +- .../Commands/SetStencilTestCommand.cs | 2 +- .../Commands/SetStorageBuffersCommand.cs | 2 +- .../Commands/SetTextureAndSamplerCommand.cs | 2 +- .../SetTransformFeedbackBuffersCommand.cs | 2 +- .../Commands/SetUniformBuffersCommand.cs | 2 +- .../Commands/SetUserClipDistanceCommand.cs | 2 +- .../Commands/SetVertexAttribsCommand.cs | 2 +- .../Commands/SetVertexBuffersCommand.cs | 2 +- .../Commands/SetViewportsCommand.cs | 2 +- .../Commands/Texture/TextureCopyToCommand.cs | 2 +- .../Texture/TextureCopyToScaledCommand.cs | 2 +- .../Texture/TextureCopyToSliceCommand.cs | 2 +- .../Texture/TextureCreateViewCommand.cs | 2 +- .../Commands/Texture/TextureGetDataCommand.cs | 2 +- .../Texture/TextureGetDataSliceCommand.cs | 2 +- .../Commands/Texture/TextureReleaseCommand.cs | 2 +- .../Commands/Texture/TextureSetDataCommand.cs | 2 +- .../Texture/TextureSetDataSliceCommand.cs | 2 +- .../TextureSetDataSliceRegionCommand.cs | 2 +- .../Texture/TextureSetStorageCommand.cs | 2 +- .../Commands/TextureBarrierCommand.cs | 2 +- .../Commands/TextureBarrierTiledCommand.cs | 2 +- .../TryHostConditionalRenderingCommand.cs | 2 +- ...TryHostConditionalRenderingFlushCommand.cs | 2 +- .../Commands/UpdateRenderScaleCommand.cs | 2 +- .../Commands/Window/WindowPresentCommand.cs | 2 +- 89 files changed, 192 insertions(+), 281 deletions(-) diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs index 497693296..48873491f 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -7,9 +7,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler; using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; using System; -using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -30,207 +28,115 @@ namespace Ryujinx.Graphics.GAL.Multithreading public static int GetMaxCommandSize() { - Assembly assembly = typeof(CommandHelper).Assembly; - - IEnumerable commands = assembly.GetTypes().Where(type => typeof(IGALCommand).IsAssignableFrom(type) && type.IsValueType); - - int maxSize = commands.Max(command => - { - MethodInfo method = typeof(Unsafe).GetMethod(nameof(Unsafe.SizeOf)); - MethodInfo generic = method.MakeGenericMethod(command); - int size = (int)generic.Invoke(null, null); - - return size; - }); - - InitLookup(); - - return maxSize + 1; // 1 byte reserved for command size. + return InitLookup() + 1; // 1 byte reserved for command size. } - private static void InitLookup() + private static int InitLookup() { - _lookup[(int)CommandType.Action] = (memory, threaded, renderer) => - ActionCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CreateBuffer] = (memory, threaded, renderer) => - CreateBufferCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CreateProgram] = (memory, threaded, renderer) => - CreateProgramCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CreateSampler] = (memory, threaded, renderer) => - CreateSamplerCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CreateSync] = (memory, threaded, renderer) => - CreateSyncCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CreateTexture] = (memory, threaded, renderer) => - CreateTextureCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.GetCapabilities] = (memory, threaded, renderer) => - GetCapabilitiesCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.PreFrame] = (memory, threaded, renderer) => - PreFrameCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.ReportCounter] = (memory, threaded, renderer) => - ReportCounterCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.ResetCounter] = (memory, threaded, renderer) => - ResetCounterCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.UpdateCounters] = (memory, threaded, renderer) => - UpdateCountersCommand.Run(ref GetCommand(memory), threaded, renderer); + int maxCommandSize = 0; - _lookup[(int)CommandType.BufferDispose] = (memory, threaded, renderer) => - BufferDisposeCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.BufferGetData] = (memory, threaded, renderer) => - BufferGetDataCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.BufferSetData] = (memory, threaded, renderer) => - BufferSetDataCommand.Run(ref GetCommand(memory), threaded, renderer); + void Register(CommandType commandType) where T : unmanaged, IGALCommand, IGALCommand + { + maxCommandSize = Math.Max(maxCommandSize, Unsafe.SizeOf()); + _lookup[(int)commandType] = (memory, threaded, renderer) => T.Run(ref GetCommand(memory), threaded, renderer); + } - _lookup[(int)CommandType.CounterEventDispose] = (memory, threaded, renderer) => - CounterEventDisposeCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CounterEventFlush] = (memory, threaded, renderer) => - CounterEventFlushCommand.Run(ref GetCommand(memory), threaded, renderer); + Register(CommandType.Action); + Register(CommandType.CreateBuffer); + Register(CommandType.CreateProgram); + Register(CommandType.CreateSampler); + Register(CommandType.CreateSync); + Register(CommandType.CreateTexture); + Register(CommandType.GetCapabilities); + Register(CommandType.PreFrame); + Register(CommandType.ReportCounter); + Register(CommandType.ResetCounter); + Register(CommandType.UpdateCounters); - _lookup[(int)CommandType.ProgramDispose] = (memory, threaded, renderer) => - ProgramDisposeCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.ProgramGetBinary] = (memory, threaded, renderer) => - ProgramGetBinaryCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.ProgramCheckLink] = (memory, threaded, renderer) => - ProgramCheckLinkCommand.Run(ref GetCommand(memory), threaded, renderer); + Register(CommandType.BufferDispose); + Register(CommandType.BufferGetData); + Register(CommandType.BufferSetData); - _lookup[(int)CommandType.SamplerDispose] = (memory, threaded, renderer) => - SamplerDisposeCommand.Run(ref GetCommand(memory), threaded, renderer); + Register(CommandType.CounterEventDispose); + Register(CommandType.CounterEventFlush); - _lookup[(int)CommandType.TextureCopyTo] = (memory, threaded, renderer) => - TextureCopyToCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureCopyToScaled] = (memory, threaded, renderer) => - TextureCopyToScaledCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureCopyToSlice] = (memory, threaded, renderer) => - TextureCopyToSliceCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureCreateView] = (memory, threaded, renderer) => - TextureCreateViewCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureGetData] = (memory, threaded, renderer) => - TextureGetDataCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureGetDataSlice] = (memory, threaded, renderer) => - TextureGetDataSliceCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureRelease] = (memory, threaded, renderer) => - TextureReleaseCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureSetData] = (memory, threaded, renderer) => - TextureSetDataCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureSetDataSlice] = (memory, threaded, renderer) => - TextureSetDataSliceCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureSetDataSliceRegion] = (memory, threaded, renderer) => - TextureSetDataSliceRegionCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureSetStorage] = (memory, threaded, renderer) => - TextureSetStorageCommand.Run(ref GetCommand(memory), threaded, renderer); + Register(CommandType.ProgramDispose); + Register(CommandType.ProgramGetBinary); + Register(CommandType.ProgramCheckLink); - _lookup[(int)CommandType.WindowPresent] = (memory, threaded, renderer) => - WindowPresentCommand.Run(ref GetCommand(memory), threaded, renderer); + Register(CommandType.SamplerDispose); - _lookup[(int)CommandType.Barrier] = (memory, threaded, renderer) => - BarrierCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.BeginTransformFeedback] = (memory, threaded, renderer) => - BeginTransformFeedbackCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.ClearBuffer] = (memory, threaded, renderer) => - ClearBufferCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.ClearRenderTargetColor] = (memory, threaded, renderer) => - ClearRenderTargetColorCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.ClearRenderTargetDepthStencil] = (memory, threaded, renderer) => - ClearRenderTargetDepthStencilCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CommandBufferBarrier] = (memory, threaded, renderer) => - CommandBufferBarrierCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.CopyBuffer] = (memory, threaded, renderer) => - CopyBufferCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.DispatchCompute] = (memory, threaded, renderer) => - DispatchComputeCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.Draw] = (memory, threaded, renderer) => - DrawCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.DrawIndexed] = (memory, threaded, renderer) => - DrawIndexedCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.DrawIndexedIndirect] = (memory, threaded, renderer) => - DrawIndexedIndirectCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.DrawIndexedIndirectCount] = (memory, threaded, renderer) => - DrawIndexedIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.DrawIndirect] = (memory, threaded, renderer) => - DrawIndirectCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.DrawIndirectCount] = (memory, threaded, renderer) => - DrawIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.DrawTexture] = (memory, threaded, renderer) => - DrawTextureCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.EndHostConditionalRendering] = (memory, threaded, renderer) => - EndHostConditionalRenderingCommand.Run(renderer); - _lookup[(int)CommandType.EndTransformFeedback] = (memory, threaded, renderer) => - EndTransformFeedbackCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetAlphaTest] = (memory, threaded, renderer) => - SetAlphaTestCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetBlendState] = (memory, threaded, renderer) => - SetBlendStateCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetDepthBias] = (memory, threaded, renderer) => - SetDepthBiasCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetDepthClamp] = (memory, threaded, renderer) => - SetDepthClampCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetDepthMode] = (memory, threaded, renderer) => - SetDepthModeCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetDepthTest] = (memory, threaded, renderer) => - SetDepthTestCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetFaceCulling] = (memory, threaded, renderer) => - SetFaceCullingCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetFrontFace] = (memory, threaded, renderer) => - SetFrontFaceCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetStorageBuffers] = (memory, threaded, renderer) => - SetStorageBuffersCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetTransformFeedbackBuffers] = (memory, threaded, renderer) => - SetTransformFeedbackBuffersCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetUniformBuffers] = (memory, threaded, renderer) => - SetUniformBuffersCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetImage] = (memory, threaded, renderer) => - SetImageCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetIndexBuffer] = (memory, threaded, renderer) => - SetIndexBufferCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetLineParameters] = (memory, threaded, renderer) => - SetLineParametersCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetLogicOpState] = (memory, threaded, renderer) => - SetLogicOpStateCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetMultisampleState] = (memory, threaded, renderer) => - SetMultisampleStateCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetPatchParameters] = (memory, threaded, renderer) => - SetPatchParametersCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetPointParameters] = (memory, threaded, renderer) => - SetPointParametersCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetPolygonMode] = (memory, threaded, renderer) => - SetPolygonModeCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetPrimitiveRestart] = (memory, threaded, renderer) => - SetPrimitiveRestartCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetPrimitiveTopology] = (memory, threaded, renderer) => - SetPrimitiveTopologyCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetProgram] = (memory, threaded, renderer) => - SetProgramCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetRasterizerDiscard] = (memory, threaded, renderer) => - SetRasterizerDiscardCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetRenderTargetColorMasks] = (memory, threaded, renderer) => - SetRenderTargetColorMasksCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetRenderTargetScale] = (memory, threaded, renderer) => - SetRenderTargetScaleCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetRenderTargets] = (memory, threaded, renderer) => - SetRenderTargetsCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetScissor] = (memory, threaded, renderer) => - SetScissorsCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetStencilTest] = (memory, threaded, renderer) => - SetStencilTestCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetTextureAndSampler] = (memory, threaded, renderer) => - SetTextureAndSamplerCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetUserClipDistance] = (memory, threaded, renderer) => - SetUserClipDistanceCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetVertexAttribs] = (memory, threaded, renderer) => - SetVertexAttribsCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetVertexBuffers] = (memory, threaded, renderer) => - SetVertexBuffersCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.SetViewports] = (memory, threaded, renderer) => - SetViewportsCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureBarrier] = (memory, threaded, renderer) => - TextureBarrierCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TextureBarrierTiled] = (memory, threaded, renderer) => - TextureBarrierTiledCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TryHostConditionalRendering] = (memory, threaded, renderer) => - TryHostConditionalRenderingCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.TryHostConditionalRenderingFlush] = (memory, threaded, renderer) => - TryHostConditionalRenderingFlushCommand.Run(ref GetCommand(memory), threaded, renderer); - _lookup[(int)CommandType.UpdateRenderScale] = (memory, threaded, renderer) => - UpdateRenderScaleCommand.Run(ref GetCommand(memory), threaded, renderer); + Register(CommandType.TextureCopyTo); + Register(CommandType.TextureCopyToScaled); + Register(CommandType.TextureCopyToSlice); + Register(CommandType.TextureCreateView); + Register(CommandType.TextureGetData); + Register(CommandType.TextureGetDataSlice); + Register(CommandType.TextureRelease); + Register(CommandType.TextureSetData); + Register(CommandType.TextureSetDataSlice); + Register(CommandType.TextureSetDataSliceRegion); + Register(CommandType.TextureSetStorage); + + Register(CommandType.WindowPresent); + + Register(CommandType.Barrier); + Register(CommandType.BeginTransformFeedback); + Register(CommandType.ClearBuffer); + Register(CommandType.ClearRenderTargetColor); + Register(CommandType.ClearRenderTargetDepthStencil); + Register(CommandType.CommandBufferBarrier); + Register(CommandType.CopyBuffer); + Register(CommandType.DispatchCompute); + Register(CommandType.Draw); + Register(CommandType.DrawIndexed); + Register(CommandType.DrawIndexedIndirect); + Register(CommandType.DrawIndexedIndirectCount); + Register(CommandType.DrawIndirect); + Register(CommandType.DrawIndirectCount); + Register(CommandType.DrawTexture); + Register(CommandType.EndHostConditionalRendering); + Register(CommandType.EndTransformFeedback); + Register(CommandType.SetAlphaTest); + Register(CommandType.SetBlendState); + Register(CommandType.SetDepthBias); + Register(CommandType.SetDepthClamp); + Register(CommandType.SetDepthMode); + Register(CommandType.SetDepthTest); + Register(CommandType.SetFaceCulling); + Register(CommandType.SetFrontFace); + Register(CommandType.SetStorageBuffers); + Register(CommandType.SetTransformFeedbackBuffers); + Register(CommandType.SetUniformBuffers); + Register(CommandType.SetImage); + Register(CommandType.SetIndexBuffer); + Register(CommandType.SetLineParameters); + Register(CommandType.SetLogicOpState); + Register(CommandType.SetMultisampleState); + Register(CommandType.SetPatchParameters); + Register(CommandType.SetPointParameters); + Register(CommandType.SetPolygonMode); + Register(CommandType.SetPrimitiveRestart); + Register(CommandType.SetPrimitiveTopology); + Register(CommandType.SetProgram); + Register(CommandType.SetRasterizerDiscard); + Register(CommandType.SetRenderTargetColorMasks); + Register(CommandType.SetRenderTargetScale); + Register(CommandType.SetRenderTargets); + Register(CommandType.SetScissor); + Register(CommandType.SetStencilTest); + Register(CommandType.SetTextureAndSampler); + Register(CommandType.SetUserClipDistance); + Register(CommandType.SetVertexAttribs); + Register(CommandType.SetVertexBuffers); + Register(CommandType.SetViewports); + Register(CommandType.TextureBarrier); + Register(CommandType.TextureBarrierTiled); + Register(CommandType.TryHostConditionalRendering); + Register(CommandType.TryHostConditionalRenderingFlush); + Register(CommandType.UpdateRenderScale); + + return maxCommandSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs index f187c3c2f..4f8e1b088 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct BarrierCommand : IGALCommand + struct BarrierCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.Barrier; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs index ea547d8b0..500326352 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct BeginTransformFeedbackCommand : IGALCommand + struct BeginTransformFeedbackCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.BeginTransformFeedback; private PrimitiveTopology _topology; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs index 68167be0a..5be42fff6 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer { - struct BufferDisposeCommand : IGALCommand + struct BufferDisposeCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.BufferDispose; private BufferHandle _buffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs index 786ed87c5..d3a255e7b 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer { - struct BufferGetDataCommand : IGALCommand + struct BufferGetDataCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.BufferGetData; private BufferHandle _buffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs index 6f39898ec..dcb8c2f21 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer { - struct BufferSetDataCommand : IGALCommand + struct BufferSetDataCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.BufferSetData; private BufferHandle _buffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs index 2b194b46a..1d70460ab 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct ClearBufferCommand : IGALCommand + struct ClearBufferCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ClearBuffer; private BufferHandle _destination; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs index 00a5128a2..f8c2bdfe6 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct ClearRenderTargetColorCommand : IGALCommand + struct ClearRenderTargetColorCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ClearRenderTargetColor; private int _index; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs index c9ebad215..ca86673ed 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct ClearRenderTargetDepthStencilCommand : IGALCommand + struct ClearRenderTargetDepthStencilCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; private int _layer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs index 8c8286485..ad3ab0f84 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct CommandBufferBarrierCommand : IGALCommand + struct CommandBufferBarrierCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CommandBufferBarrier; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs index e8f80d981..43111bce6 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct CopyBufferCommand : IGALCommand + struct CopyBufferCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CopyBuffer; private BufferHandle _source; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs index ae634e6ad..e5250212e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent { - struct CounterEventDisposeCommand : IGALCommand + struct CounterEventDisposeCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CounterEventDispose; private TableRef _event; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs index e4ff4c180..608cf8f9d 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent { - struct CounterEventFlushCommand : IGALCommand + struct CounterEventFlushCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CounterEventFlush; private TableRef _event; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs index 26c880626..29568837c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DispatchComputeCommand : IGALCommand + struct DispatchComputeCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.DispatchCompute; private int _groupsX; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs index ff27303ac..804eaa497 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DrawIndexedCommand : IGALCommand + struct DrawIndexedCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.DrawIndexed; private int _indexCount; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs index fc84819a1..1b28afcd3 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DrawCommand : IGALCommand + struct DrawCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.Draw; private int _vertexCount; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs index 3a47e9621..521b2f0c2 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DrawIndexedIndirectCommand : IGALCommand + struct DrawIndexedIndirectCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.DrawIndexedIndirect; private BufferRange _indirectBuffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs index 79d9792e9..6bdf376d0 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DrawIndexedIndirectCountCommand : IGALCommand + struct DrawIndexedIndirectCountCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.DrawIndexedIndirectCount; private BufferRange _indirectBuffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs index 73414e441..e1947084c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DrawIndirectCommand : IGALCommand + struct DrawIndirectCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.DrawIndirect; private BufferRange _indirectBuffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs index 96f60f4aa..ef56ffb26 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DrawIndirectCountCommand : IGALCommand + struct DrawIndirectCountCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.DrawIndirectCount; private BufferRange _indirectBuffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs index 41a852bb3..b3e9c4b5b 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct DrawTextureCommand : IGALCommand + struct DrawTextureCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.DrawTexture; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs index e0edd9ab9..877af23bd 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs @@ -1,10 +1,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct EndHostConditionalRenderingCommand : IGALCommand + struct EndHostConditionalRenderingCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.EndHostConditionalRendering; - public static void Run(IRenderer renderer) + public static void Run(ref EndHostConditionalRenderingCommand command, ThreadedRenderer threaded, IRenderer renderer) { renderer.Pipeline.EndHostConditionalRendering(); } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs index 561996e31..33df325fd 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct EndTransformFeedbackCommand : IGALCommand + struct EndTransformFeedbackCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.EndTransformFeedback; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs index 5fb04c803..ea831c8d4 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs @@ -4,4 +4,9 @@ { CommandType CommandType { get; } } + + interface IGALCommand where T : IGALCommand + { + abstract static void Run(ref T command, ThreadedRenderer threaded, IRenderer renderer); + } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs index 7ae887f41..f36624240 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program { - struct ProgramCheckLinkCommand : IGALCommand + struct ProgramCheckLinkCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ProgramCheckLink; private TableRef _program; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs index e614c392d..d1ec42985 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program { - struct ProgramDisposeCommand : IGALCommand + struct ProgramDisposeCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ProgramDispose; private TableRef _program; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs index 92c0a6d68..16963245f 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program { - struct ProgramGetBinaryCommand : IGALCommand + struct ProgramGetBinaryCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ProgramGetBinary; private TableRef _program; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs index 07e55c969..41987da1e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct ActionCommand : IGALCommand + struct ActionCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.Action; private TableRef _action; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs index a96b3cef4..4f01dea27 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct CreateBufferCommand : IGALCommand + struct CreateBufferCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CreateBuffer; private BufferHandle _threadedHandle; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs index e24505e58..19563e124 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct CreateProgramCommand : IGALCommand + struct CreateProgramCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CreateProgram; private TableRef _request; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs index bca98cfb8..6ab862d44 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct CreateSamplerCommand : IGALCommand + struct CreateSamplerCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CreateSampler; private TableRef _sampler; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs index 66f5cf062..32afb051e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct CreateSyncCommand : IGALCommand + struct CreateSyncCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CreateSync; private ulong _id; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs index f92401250..0347ded46 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct CreateTextureCommand : IGALCommand + struct CreateTextureCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.CreateTexture; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs index 102ed9daf..4111dcfd4 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct GetCapabilitiesCommand : IGALCommand + struct GetCapabilitiesCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.GetCapabilities; private TableRef> _result; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs index 1048dc9e6..820908f39 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct PreFrameCommand : IGALCommand + struct PreFrameCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.PreFrame; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs index d477f2355..4b0210cbf 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct ReportCounterCommand : IGALCommand + struct ReportCounterCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ReportCounter; private TableRef _event; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs index 2835bf316..3d7960417 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct ResetCounterCommand : IGALCommand + struct ResetCounterCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.ResetCounter; private CounterType _type; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs index f28bf080b..c7076c0ec 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { - struct UpdateCountersCommand : IGALCommand + struct UpdateCountersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.UpdateCounters; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs index 8f4dfb7ee..9485e9a10 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler { - struct SamplerDisposeCommand : IGALCommand + struct SamplerDisposeCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SamplerDispose; private TableRef _sampler; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs index 893793870..a96879ffa 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetAlphaTestCommand : IGALCommand + struct SetAlphaTestCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetAlphaTest; private bool _enable; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs index 6cc4894e7..68e48da57 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetBlendStateCommand : IGALCommand + struct SetBlendStateCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetBlendState; private int _index; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs index 352242a3a..eb8d4a72a 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetDepthBiasCommand : IGALCommand + struct SetDepthBiasCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetDepthBias; private PolygonModeMask _enables; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs index 21c8f3e62..15159cb44 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetDepthClampCommand : IGALCommand + struct SetDepthClampCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetDepthClamp; private bool _clamp; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs index 28c36be8b..3e1691641 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetDepthModeCommand : IGALCommand + struct SetDepthModeCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetDepthMode; private DepthMode _mode; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs index 585d3e8b5..2abaeb787 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetDepthTestCommand : IGALCommand + struct SetDepthTestCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetDepthTest; private DepthTestDescriptor _depthTest; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs index 2a2b41caf..54311e95c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetFaceCullingCommand : IGALCommand + struct SetFaceCullingCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetFaceCulling; private bool _enable; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs index a415237f9..e4d7b8147 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetFrontFaceCommand : IGALCommand + struct SetFrontFaceCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetFrontFace; private FrontFace _frontFace; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs index 4223a6212..7836acd7e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetImageCommand : IGALCommand + struct SetImageCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetImage; private int _binding; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs index 753e21f9b..ded44c552 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetIndexBufferCommand : IGALCommand + struct SetIndexBufferCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetIndexBuffer; private BufferRange _buffer; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs index 7fd2e5b1f..683319323 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetLineParametersCommand : IGALCommand + struct SetLineParametersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetLineParameters; private float _width; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs index 253ef1388..2d7fc1698 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetLogicOpStateCommand : IGALCommand + struct SetLogicOpStateCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetLogicOpState; private bool _enable; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs index f981c6ceb..f7b4969a5 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetMultisampleStateCommand : IGALCommand + struct SetMultisampleStateCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetMultisampleState; private MultisampleDescriptor _multisample; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs index d67cfc692..815bc3c22 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetPatchParametersCommand : IGALCommand + struct SetPatchParametersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetPatchParameters; private int _vertices; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs index 37833a0e1..e3fad0f82 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetPointParametersCommand : IGALCommand + struct SetPointParametersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetPointParameters; private float _size; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs index 6de78f045..ea2f838b9 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetPolygonModeCommand : IGALCommand + struct SetPolygonModeCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetPolygonMode; private PolygonMode _frontMode; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs index e5f6ecf38..26b88b01d 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetPrimitiveRestartCommand : IGALCommand + struct SetPrimitiveRestartCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetPrimitiveRestart; private bool _enable; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs index 0bf292605..062c4e57c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetPrimitiveTopologyCommand : IGALCommand + struct SetPrimitiveTopologyCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetPrimitiveTopology; private PrimitiveTopology _topology; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs index c35d9c1f4..fa2e9a8a5 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetProgramCommand : IGALCommand + struct SetProgramCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetProgram; private TableRef _program; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs index 4f92ce99e..d2095a4f1 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetRasterizerDiscardCommand : IGALCommand + struct SetRasterizerDiscardCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetRasterizerDiscard; private bool _discard; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs index 1e75ddb87..c247ff3af 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetRenderTargetColorMasksCommand : IGALCommand + struct SetRenderTargetColorMasksCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetRenderTargetColorMasks; private SpanRef _componentMask; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs index a97a63db4..7cb5ec114 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetRenderTargetScaleCommand : IGALCommand + struct SetRenderTargetScaleCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetRenderTargetScale; private float _scale; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs index 30f798ddb..0b175a72e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs @@ -4,7 +4,7 @@ using System.Linq; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetRenderTargetsCommand : IGALCommand + struct SetRenderTargetsCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetRenderTargets; private TableRef _colors; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs index 6966df6d5..985d775e0 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetScissorsCommand : IGALCommand + struct SetScissorsCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetScissor; private SpanRef> _scissors; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs index cc5db4df4..41bff97e3 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetStencilTestCommand : IGALCommand + struct SetStencilTestCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetStencilTest; private StencilTestDescriptor _stencilTest; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs index 610603caa..6ecb0989b 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetStorageBuffersCommand : IGALCommand + struct SetStorageBuffersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetStorageBuffers; private SpanRef _buffers; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs index 7ef58c3d0..5e8e08544 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs @@ -4,7 +4,7 @@ using Ryujinx.Graphics.Shader; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetTextureAndSamplerCommand : IGALCommand + struct SetTextureAndSamplerCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetTextureAndSampler; private ShaderStage _stage; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs index 5125447c5..e0d4ef2d2 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetTransformFeedbackBuffersCommand : IGALCommand + struct SetTransformFeedbackBuffersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetTransformFeedbackBuffers; private SpanRef _buffers; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs index e4abb403a..9e93db9ee 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetUniformBuffersCommand : IGALCommand + struct SetUniformBuffersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetUniformBuffers; private SpanRef _buffers; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs index f0f057794..4336ce49f 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetUserClipDistanceCommand : IGALCommand + struct SetUserClipDistanceCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetUserClipDistance; private int _index; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs index cbc313e9f..e442c72d1 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetVertexAttribsCommand : IGALCommand + struct SetVertexAttribsCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetVertexAttribs; private SpanRef _vertexAttribs; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs index b7d46d08c..585da2a4a 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetVertexBuffersCommand : IGALCommand + struct SetVertexBuffersCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetVertexBuffers; private SpanRef _vertexBuffers; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs index e1b8c7d4f..c18bd811e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct SetViewportsCommand : IGALCommand + struct SetViewportsCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.SetViewports; private SpanRef _viewports; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs index 112c1fd1d..02d0b6396 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureCopyToCommand : IGALCommand + struct TextureCopyToCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureCopyTo; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs index 11843361d..6b83d3f80 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureCopyToScaledCommand : IGALCommand + struct TextureCopyToScaledCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureCopyToScaled; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs index 363edb005..2a340a704 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureCopyToSliceCommand : IGALCommand + struct TextureCopyToSliceCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureCopyToSlice; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs index 7c385407b..09e9ca2f7 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureCreateViewCommand : IGALCommand + struct TextureCreateViewCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureCreateView; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs index 9e7d0c64f..1f519ccd8 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureGetDataCommand : IGALCommand + struct TextureGetDataCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureGetData; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs index 207e5784c..5ac05971a 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureGetDataSliceCommand : IGALCommand + struct TextureGetDataSliceCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureGetDataSlice; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs index 591b2214c..61486e091 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureReleaseCommand : IGALCommand + struct TextureReleaseCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureRelease; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs index a8a6d274a..cfbaffd3e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureSetDataCommand : IGALCommand + struct TextureSetDataCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureSetData; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs index 0179ff110..a7126f617 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureSetDataSliceCommand : IGALCommand + struct TextureSetDataSliceCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureSetDataSlice; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs index b4285592f..4df83e084 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureSetDataSliceRegionCommand : IGALCommand + struct TextureSetDataSliceRegionCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureSetDataSliceRegion; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs index f86a9c44a..2a1943a9c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { - struct TextureSetStorageCommand : IGALCommand + struct TextureSetStorageCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureSetStorage; private TableRef _texture; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs index b0b46021d..ce1a83a78 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct TextureBarrierCommand : IGALCommand + struct TextureBarrierCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureBarrier; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs index f92abe5b3..c65ffe2ec 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct TextureBarrierTiledCommand : IGALCommand + struct TextureBarrierTiledCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TextureBarrierTiled; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs index 65e1748de..9124ca1f0 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct TryHostConditionalRenderingCommand : IGALCommand + struct TryHostConditionalRenderingCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TryHostConditionalRendering; private TableRef _value; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs index 29eb8dd49..a5d076400 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs @@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL.Multithreading.Resources; namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct TryHostConditionalRenderingFlushCommand : IGALCommand + struct TryHostConditionalRenderingFlushCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.TryHostConditionalRenderingFlush; private TableRef _value; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs index ca869b01e..ebe141508 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { - struct UpdateRenderScaleCommand : IGALCommand + struct UpdateRenderScaleCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.UpdateRenderScale; private SpanRef _scales; diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs index c4f3b553a..6a24cd35b 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Window { - struct WindowPresentCommand : IGALCommand + struct WindowPresentCommand : IGALCommand, IGALCommand { public CommandType CommandType => CommandType.WindowPresent; private TableRef _texture; From c14844d12c199894ba3ad75ff48802ad09f2b498 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sun, 22 Jan 2023 01:42:55 +0100 Subject: [PATCH 318/737] Ava UI: Various Fixes (#4326) * Ava UI: Various Fixes * use WriteAllBytes --- Ryujinx.Ava/AppHost.cs | 4 +- Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 113 ++++++++++-------- .../UI/ViewModels/TitleUpdateViewModel.cs | 26 +++- .../UI/Windows/TitleUpdateWindow.axaml.cs | 19 +-- 4 files changed, 88 insertions(+), 74 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index fdeee2cc8..ad33d08c8 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -231,7 +231,7 @@ namespace Ryujinx.Ava } } - private unsafe void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) + private void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) { if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0) { @@ -240,7 +240,7 @@ namespace Ryujinx.Ava lock (_lockObject) { DateTime currentTime = DateTime.Now; - string filename = $"ryujinx_capture_{currentTime}-{currentTime:D2}-{currentTime:D2}_{currentTime:D2}-{currentTime:D2}-{currentTime:D2}.png"; + string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; string directory = AppDataManager.Mode switch { diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs index 6cacfef49..532f4dc27 100644 --- a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs +++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -140,68 +140,75 @@ namespace Ryujinx.Ava.UI.Renderer { if (VisualRoot != null) { - Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value; - Pointer pointer = new(0, PointerType.Mouse, true); - - switch (msg) + if (msg == WindowsMessages.LBUTTONDOWN || + msg == WindowsMessages.RBUTTONDOWN || + msg == WindowsMessages.LBUTTONUP || + msg == WindowsMessages.RBUTTONUP || + msg == WindowsMessages.MOUSEMOVE) { - case WindowsMessages.LBUTTONDOWN: - case WindowsMessages.RBUTTONDOWN: - { - bool isLeft = msg == WindowsMessages.LBUTTONDOWN; - RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; - PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed); + Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value; + Pointer pointer = new(0, PointerType.Mouse, true); - var evnt = new PointerPressedEventArgs( - this, - pointer, - VisualRoot, - rootVisualPosition, - (ulong)Environment.TickCount64, - properties, - KeyModifiers.None); + switch (msg) + { + case WindowsMessages.LBUTTONDOWN: + case WindowsMessages.RBUTTONDOWN: + { + bool isLeft = msg == WindowsMessages.LBUTTONDOWN; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed); - RaiseEvent(evnt); + var evnt = new PointerPressedEventArgs( + this, + pointer, + VisualRoot, + rootVisualPosition, + (ulong)Environment.TickCount64, + properties, + KeyModifiers.None); - break; - } - case WindowsMessages.LBUTTONUP: - case WindowsMessages.RBUTTONUP: - { - bool isLeft = msg == WindowsMessages.LBUTTONUP; - RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; - PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased); + RaiseEvent(evnt); - var evnt = new PointerReleasedEventArgs( - this, - pointer, - VisualRoot, - rootVisualPosition, - (ulong)Environment.TickCount64, - properties, - KeyModifiers.None, - isLeft ? MouseButton.Left : MouseButton.Right); + break; + } + case WindowsMessages.LBUTTONUP: + case WindowsMessages.RBUTTONUP: + { + bool isLeft = msg == WindowsMessages.LBUTTONUP; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased); - RaiseEvent(evnt); + var evnt = new PointerReleasedEventArgs( + this, + pointer, + VisualRoot, + rootVisualPosition, + (ulong)Environment.TickCount64, + properties, + KeyModifiers.None, + isLeft ? MouseButton.Left : MouseButton.Right); - break; - } - case WindowsMessages.MOUSEMOVE: - { - var evnt = new PointerEventArgs( - PointerMovedEvent, - this, - pointer, - VisualRoot, - rootVisualPosition, - (ulong)Environment.TickCount64, - new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other), - KeyModifiers.None); + RaiseEvent(evnt); - RaiseEvent(evnt); + break; + } + case WindowsMessages.MOUSEMOVE: + { + var evnt = new PointerEventArgs( + PointerMovedEvent, + this, + pointer, + VisualRoot, + rootVisualPosition, + (ulong)Environment.TickCount64, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other), + KeyModifiers.None); - break; - } + RaiseEvent(evnt); + + break; + } + } } } diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index 1bac94245..3d0b20f7e 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -21,8 +21,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using SpanHelpers = LibHac.Common.SpanHelpers; +using System.Text; using Path = System.IO.Path; +using SpanHelpers = LibHac.Common.SpanHelpers; namespace Ryujinx.Ava.UI.ViewModels; @@ -90,6 +91,8 @@ public class TitleUpdateViewModel : BaseModel Selected = "", Paths = new List() }; + + Save(); } LoadUpdates(); @@ -102,6 +105,9 @@ public class TitleUpdateViewModel : BaseModel AddUpdate(path); } + // NOTE: Save the list again to remove leftovers. + Save(); + TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); SelectedUpdate = selected; @@ -223,4 +229,22 @@ public class TitleUpdateViewModel : BaseModel SortUpdates(); } + + public void Save() + { + _titleUpdateWindowData.Paths.Clear(); + _titleUpdateWindowData.Selected = ""; + + foreach (TitleUpdateModel update in TitleUpdates) + { + _titleUpdateWindowData.Paths.Add(update.Path); + + if (update == SelectedUpdate) + { + _titleUpdateWindowData.Selected = update.Path; + } + } + + File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); + } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 41370e669..1b50c46f3 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -60,24 +60,7 @@ namespace Ryujinx.Ava.UI.Windows public void Save(object sender, RoutedEventArgs e) { - ViewModel._titleUpdateWindowData.Paths.Clear(); - - ViewModel._titleUpdateWindowData.Selected = ""; - - foreach (TitleUpdateModel update in ViewModel.TitleUpdates) - { - ViewModel._titleUpdateWindowData.Paths.Add(update.Path); - - if (update == ViewModel.SelectedUpdate) - { - ViewModel._titleUpdateWindowData.Selected = update.Path; - } - } - - using (FileStream titleUpdateJsonStream = File.Create(ViewModel._titleUpdateJsonPath, 4096, FileOptions.WriteThrough)) - { - titleUpdateJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(ViewModel._titleUpdateWindowData, true))); - } + ViewModel.Save(); if (VisualRoot is MainWindow window) { From e3d0ccf8d5226189fa93741a2c63787b76defea6 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 22 Jan 2023 02:03:30 +0000 Subject: [PATCH 319/737] Allow setting texture data from 1x to fix some textures resetting randomly (#2860) * Allow setting texture data from 1x to fix some textures resetting randomly Expected targets: - Deltarune 1+2 - Crash Team Racing - Those new pokemon games idk * Allow scaling of MSAA textures, propagate scale on copy. * Fix Rebase Oops * Automatic disable * A bit more aggressive * Without the debug log * Actually decrement the score when writing. --- Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs | 5 ++ Ryujinx.Graphics.Gpu/Image/Texture.cs | 68 ++++++++++++++++--- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 2 +- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs index d2b6bec37..4ce53e786 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs @@ -349,6 +349,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod return; } + if (srcTexture.Info.Samples > 1 || dstTexture.Info.Samples > 1) + { + srcTexture.PropagateScale(dstTexture); + } + float scale = srcTexture.ScaleFactor; float dstScale = dstTexture.ScaleFactor; diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index cfe577566..5ed9b2a07 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -25,6 +25,12 @@ namespace Ryujinx.Graphics.Gpu.Image // This method uses much more memory so we want to avoid it if possible. private const int ByteComparisonSwitchThreshold = 4; + // Tuning for blacklisting textures from scaling when their data is updated from CPU. + // Each write adds the weight, each GPU modification subtracts 1. + // Exceeding the threshold blacklists the texture. + private const int ScaledSetWeight = 10; + private const int ScaledSetThreshold = 30; + private const int MinLevelsForForceAnisotropy = 5; private struct TexturePoolOwner @@ -122,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Image private Target _arrayViewTarget; private ITexture _flushHostTexture; + private ITexture _setHostTexture; + private int _scaledSetScore; private Texture _viewStorage; @@ -518,6 +526,25 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Registers when a texture has had its data set after being scaled, and + /// determines if it should be blacklisted from scaling to improve performance. + /// + /// True if setting data for a scaled texture is allowed, false if the texture has been blacklisted + private bool AllowScaledSetData() + { + _scaledSetScore += ScaledSetWeight; + + if (_scaledSetScore >= ScaledSetThreshold) + { + BlacklistScale(); + + return false; + } + + return true; + } + /// /// Blacklists this texture from being scaled. Resets its scale to 1 if needed. /// @@ -554,9 +581,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale. /// /// Scale factor + /// True if the data should be copied to the texture, false otherwise /// Texture to use instead of creating one /// A host texture containing a scaled version of this texture - private ITexture GetScaledHostTexture(float scale, ITexture storage = null) + private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null) { if (storage == null) { @@ -564,7 +592,10 @@ namespace Ryujinx.Graphics.Gpu.Image storage = _context.Renderer.CreateTexture(createInfo, scale); } - HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true); + if (copy) + { + HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true); + } return storage; } @@ -595,7 +626,7 @@ namespace Ryujinx.Graphics.Gpu.Image ScaleFactor = scale; - ITexture newStorage = GetScaledHostTexture(ScaleFactor); + ITexture newStorage = GetScaledHostTexture(ScaleFactor, true); Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}"); @@ -692,11 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// public void SynchronizeFull() { - if (_hasData) - { - BlacklistScale(); - } - ReadOnlySpan data = _physicalMemory.GetSpan(Range); // If the host does not support ASTC compression, we need to do the decompression. @@ -723,7 +749,19 @@ namespace Ryujinx.Graphics.Gpu.Image SpanOrArray result = ConvertToHostCompatibleFormat(data); - HostTexture.SetData(result); + if (ScaleFactor != 1f && AllowScaledSetData()) + { + // If needed, create a texture to load from 1x scale. + ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture); + + texture.SetData(result); + + texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true); + } + else + { + HostTexture.SetData(result); + } _hasData = true; } @@ -1056,7 +1094,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (ScaleFactor != 1f) { // If needed, create a texture to flush back to host at 1x scale. - texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture); + texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture); } return texture; @@ -1456,6 +1494,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// public void SignalModified() { + _scaledSetScore = Math.Max(0, _scaledSetScore - 1); + if (_modifiedStale || Group.HasCopyDependencies) { _modifiedStale = false; @@ -1472,6 +1512,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if the texture has been bound, false if it has been unbound public void SignalModifying(bool bound) { + if (bound) + { + _scaledSetScore = Math.Max(0, _scaledSetScore - 1); + } + if (_modifiedStale || Group.HasCopyDependencies) { _modifiedStale = false; @@ -1685,6 +1730,9 @@ namespace Ryujinx.Graphics.Gpu.Image _flushHostTexture?.Release(); _flushHostTexture = null; + + _setHostTexture?.Release(); + _setHostTexture = null; } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 49adecdca..9802a3dcc 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if eligible private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale) { - if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed) + if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray || info.Target == Target.Texture2DMultisample) && !info.FormatInfo.IsCompressed) { return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired; } From 32a1cd83fd2e98d6f6da0b6c0b43c3af1323fca4 Mon Sep 17 00:00:00 2001 From: merry Date: Sun, 22 Jan 2023 10:21:52 +0000 Subject: [PATCH 320/737] AvaloniaKeyboardDriver: Swallow TextInput events to avoid bell (#4320) --- Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs index b107898e4..edcdb52fd 100644 --- a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs +++ b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs @@ -1,5 +1,6 @@ using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Interactivity; using Ryujinx.Ava.Common.Locale; using Ryujinx.Input; using System; @@ -30,6 +31,7 @@ namespace Ryujinx.Ava.Input _control.KeyDown += OnKeyPress; _control.KeyUp += OnKeyRelease; _control.TextInput += Control_TextInput; + _control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble); } private void Control_TextInput(object sender, TextInputEventArgs e) @@ -37,6 +39,12 @@ namespace Ryujinx.Ava.Input TextInput?.Invoke(this, e.Text); } + private void Control_LastChanceTextInput(object sender, TextInputEventArgs e) + { + // Swallow event + e.Handled = true; + } + public event Action OnGamepadConnected { add { } From 4f293f8cbec33e8edce81ad4980bd532a2464c05 Mon Sep 17 00:00:00 2001 From: merry Date: Sun, 22 Jan 2023 14:15:49 +0000 Subject: [PATCH 321/737] Arm64: Simplify TryEncodeBitMask and use for constants (#4328) * Arm64: Simplify TryEncodeBitMask * CodeGenerator: Use TryEncodeBitMask in GenerateConstantCopy * Ptc: Bump version --- ARMeilleure/ARMeilleure.csproj | 6 + ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs | 120 +++---------------- ARMeilleure/CodeGen/Arm64/CodeGenerator.cs | 14 ++- ARMeilleure/Translation/PTC/Ptc.cs | 2 +- Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs | 46 +++++++ 5 files changed, 81 insertions(+), 107 deletions(-) create mode 100644 Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj index 58fd04b38..1c2135ed5 100644 --- a/ARMeilleure/ARMeilleure.csproj +++ b/ARMeilleure/ARMeilleure.csproj @@ -16,4 +16,10 @@ + + + <_Parameter1>Ryujinx.Tests + + + diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs b/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs index e67d2fdb7..8d1e597ba 100644 --- a/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs +++ b/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs @@ -1,5 +1,4 @@ using ARMeilleure.IntermediateRepresentation; -using System; using System.Numerics; namespace ARMeilleure.CodeGen.Arm64 @@ -32,9 +31,12 @@ namespace ARMeilleure.CodeGen.Arm64 public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR) { - ulong value = operand.Value; + return TryEncodeBitMask(operand.Type, operand.Value, out immN, out immS, out immR); + } - if (operand.Type == OperandType.I32) + public static bool TryEncodeBitMask(OperandType type, ulong value, out int immN, out int immS, out int immR) + { + if (type == OperandType.I32) { value |= value << 32; } @@ -50,7 +52,7 @@ namespace ARMeilleure.CodeGen.Arm64 // Any value AND all ones will be equal itself, so it's effectively a no-op. // Any value OR all ones will be equal all ones, so one can just use MOV. // Any value XOR all ones will be equal its inverse, so one can just use MVN. - if (value == ulong.MaxValue) + if (value == 0 || value == ulong.MaxValue) { immN = 0; immS = 0; @@ -59,79 +61,18 @@ namespace ARMeilleure.CodeGen.Arm64 return false; } - int bitLength = CountSequence(value); + // Normalize value, rotating it such that the LSB is 1: Ensures we get a complete element that has not + // been cut-in-half across the word boundary. + int rotation = BitOperations.TrailingZeroCount(value & (value + 1)); + ulong rotatedValue = ulong.RotateRight(value, rotation); - if ((value >> bitLength) != 0) - { - bitLength += CountSequence(value >> bitLength); - } + // Now that we have a complete element in the LSB with the LSB = 1, determine size and number of ones + // in element. + int elementSize = BitOperations.TrailingZeroCount(rotatedValue & (rotatedValue + 1)); + int onesInElement = BitOperations.TrailingZeroCount(~rotatedValue); - int bitLengthLog2 = BitOperations.Log2((uint)bitLength); - int bitLengthPow2 = 1 << bitLengthLog2; - - if (bitLengthPow2 < bitLength) - { - bitLengthLog2++; - bitLengthPow2 <<= 1; - } - - int selectedESize = 64; - int repetitions = 1; - int onesCount = BitOperations.PopCount(value); - - if (bitLengthPow2 < 64 && (value >> bitLengthPow2) != 0) - { - for (int eSizeLog2 = bitLengthLog2; eSizeLog2 < 6; eSizeLog2++) - { - bool match = true; - int eSize = 1 << eSizeLog2; - ulong mask = (1UL << eSize) - 1; - ulong eValue = value & mask; - - for (int e = 1; e < 64 / eSize; e++) - { - if (((value >> (e * eSize)) & mask) != eValue) - { - match = false; - break; - } - } - - if (match) - { - selectedESize = eSize; - repetitions = 64 / eSize; - onesCount = BitOperations.PopCount(eValue); - break; - } - } - } - - // Find rotation. We have two cases, one where the highest bit is 0 - // and one where it is 1. - // If it's 1, we just need to count the number of 1 bits on the MSB to find the right rotation. - // If it's 0, we just need to count the number of 0 bits on the LSB to find the left rotation, - // then we can convert it to the right rotation shift by subtracting the value from the element size. - int rotation; - long vHigh = (long)(value << (64 - selectedESize)); - if (vHigh < 0) - { - rotation = BitOperations.LeadingZeroCount(~(ulong)vHigh); - } - else - { - rotation = (selectedESize - BitOperations.TrailingZeroCount(value)) & (selectedESize - 1); - } - - // Reconstruct value and see if it matches. If not, we can't encode. - ulong reconstructed = onesCount == 64 ? ulong.MaxValue : RotateRight((1UL << onesCount) - 1, rotation, selectedESize); - - for (int bit = 32; bit >= selectedESize; bit >>= 1) - { - reconstructed |= reconstructed << bit; - } - - if (reconstructed != value || onesCount == 0) + // Check the value is repeating; also ensures element size is a power of two. + if (ulong.RotateRight(value, elementSize) != value) { immN = 0; immS = 0; @@ -140,34 +81,11 @@ namespace ARMeilleure.CodeGen.Arm64 return false; } - immR = rotation; - - // immN indicates that there are no repetitions. - // The MSB of immS indicates the amount of repetitions, and the LSB the number of bits set. - if (repetitions == 1) - { - immN = 1; - immS = 0; - } - else - { - immN = 0; - immS = (0xf80 >> BitOperations.Log2((uint)repetitions)) & 0x3f; - } - - immS |= onesCount - 1; + immN = (elementSize >> 6) & 1; + immS = (((~elementSize + 1) << 1) | (onesInElement - 1)) & 0x3f; + immR = (elementSize - rotation) & (elementSize - 1); return true; } - - private static int CountSequence(ulong value) - { - return BitOperations.TrailingZeroCount(value) + BitOperations.TrailingZeroCount(~value); - } - - private static ulong RotateRight(ulong bits, int shift, int size) - { - return (bits >> shift) | ((bits << (size - shift)) & (size == 64 ? ulong.MaxValue : (1UL << size) - 1)); - } } } \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs b/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs index 704aa45ac..fc4fa976e 100644 --- a/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs @@ -1303,7 +1303,15 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateConstantCopy(CodeGenContext context, Operand dest, ulong value) { - if (value != 0) + if (value == 0) + { + context.Assembler.Mov(dest, Register(ZrRegister, dest.Type)); + } + else if (CodeGenCommon.TryEncodeBitMask(dest.Type, value, out _, out _, out _)) + { + context.Assembler.Orr(dest, Register(ZrRegister, dest.Type), Const(dest.Type, (long)value)); + } + else { int hw = 0; bool first = true; @@ -1328,10 +1336,6 @@ namespace ARMeilleure.CodeGen.Arm64 value >>= 16; } } - else - { - context.Assembler.Mov(dest, Register(ZrRegister, dest.Type)); - } } private static void GenerateAtomicCas( diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index e5e0b2a54..aeb5868c9 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 4272; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 4328; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs b/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs new file mode 100644 index 000000000..e16361bbc --- /dev/null +++ b/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs @@ -0,0 +1,46 @@ +using ARMeilleure.CodeGen.Arm64; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + public class Arm64CodeGenCommonTests + { + public struct TestCase + { + public ulong Value; + public bool Valid; + public int ImmN; + public int ImmS; + public int ImmR; + } + + public static readonly TestCase[] TestCases = + { + new() { Value = 0, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 }, + new() { Value = 0x970977f35f848714, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 }, + new() { Value = 0xffffffffffffffff, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 }, + new() { Value = 0x5555555555555555, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 0 }, + new() { Value = 0xaaaaaaaaaaaaaaaa, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 1 }, + new() { Value = 0x6666666666666666, Valid = true, ImmN = 0, ImmS = 0x39, ImmR = 3 }, + new() { Value = 0x1c1c1c1c1c1c1c1c, Valid = true, ImmN = 0, ImmS = 0x32, ImmR = 6 }, + new() { Value = 0x0f0f0f0f0f0f0f0f, Valid = true, ImmN = 0, ImmS = 0x33, ImmR = 0 }, + new() { Value = 0xf1f1f1f1f1f1f1f1, Valid = true, ImmN = 0, ImmS = 0x34, ImmR = 4 }, + new() { Value = 0xe7e7e7e7e7e7e7e7, Valid = true, ImmN = 0, ImmS = 0x35, ImmR = 3 }, + new() { Value = 0xc001c001c001c001, Valid = true, ImmN = 0, ImmS = 0x22, ImmR = 2 }, + new() { Value = 0x0000038000000380, Valid = true, ImmN = 0, ImmS = 0x02, ImmR = 25 }, + new() { Value = 0xffff8fffffff8fff, Valid = true, ImmN = 0, ImmS = 0x1c, ImmR = 17 }, + new() { Value = 0x000000000ffff800, Valid = true, ImmN = 1, ImmS = 0x10, ImmR = 53 }, + }; + + [Test] + public void BitImmTests([ValueSource(nameof(TestCases))] TestCase test) + { + bool valid = CodeGenCommon.TryEncodeBitMask(test.Value, out int immN, out int immS, out int immR); + + Assert.That(valid, Is.EqualTo(test.Valid)); + Assert.That(immN, Is.EqualTo(test.ImmN)); + Assert.That(immS, Is.EqualTo(test.ImmS)); + Assert.That(immR, Is.EqualTo(test.ImmR)); + } + } +} \ No newline at end of file From dc30d948520ad3bc76c8911a40d554906c9c5170 Mon Sep 17 00:00:00 2001 From: gnisman Date: Sun, 22 Jan 2023 18:04:33 +0200 Subject: [PATCH 322/737] Handle parsing of corrupt Config.json and prevent crash on launch (#4309) * Handle parsing of corrupt Config.json and prevent crash on launch * Implement a cleaner solution to handle empty json object --- Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 52ca71538..2ebf65ac1 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -340,7 +340,7 @@ namespace Ryujinx.Ui.Common.Configuration { configurationFileFormat = JsonHelper.DeserializeFromFile(path); - return true; + return configurationFileFormat.Version != 0; } catch { From ad6ff6ce99619bb320cc7517768bca603f11a75c Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 22 Jan 2023 18:39:00 +0100 Subject: [PATCH 323/737] GUI: Add option to register file types (#4250) * Add FileAssociationHelper.cs * Add register file types option to gtk * Add register file types option to avalonia * Add Windows support to FileAssociationHelper.cs * linux: Add uninstall support for file types * Ignore .glade~ backup files * Rename Register/Unregister methods * gtk: Add manage file types submenu * ava: Add manage file types submenu * windows: Add uninstall support for file types * Don't invert uninstall condition (formatting change) Co-authored-by: gdkchan * Add IsTypesRegisteredWindows & Fix Windows install function * Add AreMimeTypesRegisteredLinux() * Fix wrong indention Co-authored-by: AcK77 Co-authored-by: gdkchan --- .gitignore | 3 + Ryujinx.Ava/Assets/Locales/en_US.json | 9 +- Ryujinx.Ava/Program.cs | 50 -- .../UI/ViewModels/MainWindowViewModel.cs | 5 + .../UI/Views/Main/MainMenuBarView.axaml | 9 +- .../UI/Views/Main/MainMenuBarView.axaml.cs | 28 ++ .../Helper/FileAssociationHelper.cs | 198 ++++++++ Ryujinx/Program.cs | 49 -- Ryujinx/Ui/MainWindow.cs | 28 +- Ryujinx/Ui/MainWindow.glade | 435 ++++++++++-------- 10 files changed, 506 insertions(+), 308 deletions(-) create mode 100644 Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs diff --git a/.gitignore b/.gitignore index c24f90e34..37b419d07 100644 --- a/.gitignore +++ b/.gitignore @@ -170,3 +170,6 @@ launchSettings.json # NetCore Publishing Profiles PublishProfiles/ + +# Glade backup files +*.glade~ diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 3d5096759..9bc6b5817 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -26,6 +26,9 @@ "MenuBarToolsInstallFirmware": "Install Firmware", "MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP", "MenuBarFileToolsInstallFirmwareFromDirectory": "Install a firmware from a directory", + "MenuBarToolsManageFileTypes": "Manage file types", + "MenuBarToolsInstallFileTypes": "Install file types", + "MenuBarToolsUninstallFileTypes": "Uninstall file types", "MenuBarHelp": "Help", "MenuBarHelpCheckForUpdates": "Check for Updates", "MenuBarHelpAbout": "About", @@ -339,6 +342,10 @@ "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\\nThe emulator will now start.", "DialogFirmwareNoFirmwareInstalledMessage": "No Firmware Installed", "DialogFirmwareInstalledMessage": "Firmware {0} was installed", + "DialogInstallFileTypesSuccessMessage": "Successfully installed file types!", + "DialogInstallFileTypesErrorMessage": "Failed to install file types.", + "DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!", + "DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.", "DialogOpenSettingsWindowLabel": "Open Settings Window", "DialogControllerAppletTitle": "Controller Applet", "DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}", @@ -619,4 +626,4 @@ "UserProfilesRecoverEmptyList": "No profiles to recover", "UserEditorTitle" : "Edit User", "UserEditorTitleCreate" : "Create User" -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index e64b6921e..7f35c62a4 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -14,10 +14,8 @@ using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; using System; -using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Threading.Tasks; namespace Ryujinx.Ava @@ -35,48 +33,6 @@ namespace Ryujinx.Ava private const uint MB_ICONWARNING = 0x30; - [SupportedOSPlatform("linux")] - static void RegisterMimeTypes() - { - if (ReleaseInformation.IsFlatHubBuild()) - { - return; - } - - string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); - - if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml"))) - { - string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml"); - using Process mimeProcess = new(); - - mimeProcess.StartInfo.FileName = "xdg-mime"; - mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}"; - - mimeProcess.Start(); - mimeProcess.WaitForExit(); - - if (mimeProcess.ExitCode != 0) - { - Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}"); - return; - } - - using Process updateMimeProcess = new(); - - updateMimeProcess.StartInfo.FileName = "update-mime-database"; - updateMimeProcess.StartInfo.Arguments = mimeDbPath; - - updateMimeProcess.Start(); - updateMimeProcess.WaitForExit(); - - if (updateMimeProcess.ExitCode != 0) - { - Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}"); - } - } - } - public static void Main(string[] args) { Version = ReleaseInformation.GetVersion(); @@ -139,12 +95,6 @@ namespace Ryujinx.Ava // Initialize the logger system. LoggerModule.Initialize(); - // Register mime types on linux. - if (OperatingSystem.IsLinux()) - { - RegisterMimeTypes(); - } - // Initialize Discord integration. DiscordIntegrationModule.Initialize(); diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 6fea1844f..a4ccac2da 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -683,6 +683,11 @@ namespace Ryujinx.Ava.UI.ViewModels get => ConsoleHelper.SetConsoleWindowStateSupported; } + public bool ManageFileTypesVisible + { + get => FileAssociationHelper.IsTypeAssociationSupported; + } + public ObservableCollection Applications { get => _applications; diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml index bd26561a7..b1d768ead 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml @@ -77,8 +77,7 @@ - - + + + + + - \ No newline at end of file + diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 172ab6fcd..11ecd0fc9 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Interactivity; using LibHac.FsSystem; using LibHac.Ncm; +using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; @@ -10,6 +11,7 @@ using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS; using Ryujinx.Modules; +using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; using System.IO; @@ -163,6 +165,32 @@ namespace Ryujinx.Ava.UI.Views.Main } } + private async void InstallFileTypes_Click(object sender, RoutedEventArgs e) + { + if (FileAssociationHelper.Install()) + { + await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], + string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty); + } + else + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]); + } + } + + private async void UninstallFileTypes_Click(object sender, RoutedEventArgs e) + { + if (FileAssociationHelper.Uninstall()) + { + await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], + string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty); + } + else + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]); + } + } + public async void CheckForUpdates(object sender, RoutedEventArgs e) { if (Updater.CanUpdate(true)) diff --git a/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs b/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs new file mode 100644 index 000000000..4f4b25245 --- /dev/null +++ b/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs @@ -0,0 +1,198 @@ +using Microsoft.Win32; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Ui.Common.Helper +{ + public static partial class FileAssociationHelper + { + private static string[] _fileExtensions = new string[] { ".nca", ".nro", ".nso", ".nsp", ".xci" }; + + [SupportedOSPlatform("linux")] + private static string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); + + private const int SHCNE_ASSOCCHANGED = 0x8000000; + private const int SHCNF_FLUSH = 0x1000; + + [LibraryImport("shell32.dll", SetLastError = true)] + public static partial void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2); + + public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild(); + + [SupportedOSPlatform("linux")] + private static bool AreMimeTypesRegisteredLinux() => File.Exists(Path.Combine(_mimeDbPath, "packages", "Ryujinx.xml")); + + [SupportedOSPlatform("linux")] + private static bool InstallLinuxMimeTypes(bool uninstall = false) + { + string installKeyword = uninstall ? "uninstall" : "install"; + + if (!AreMimeTypesRegisteredLinux()) + { + string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml"); + string additionalArgs = !uninstall ? "--novendor" : ""; + + using Process mimeProcess = new(); + + mimeProcess.StartInfo.FileName = "xdg-mime"; + mimeProcess.StartInfo.Arguments = $"{installKeyword} {additionalArgs} --mode user {mimeTypesFile}"; + + mimeProcess.Start(); + mimeProcess.WaitForExit(); + + if (mimeProcess.ExitCode != 0) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Unable to {installKeyword} mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}"); + + return false; + } + + using Process updateMimeProcess = new(); + + updateMimeProcess.StartInfo.FileName = "update-mime-database"; + updateMimeProcess.StartInfo.Arguments = _mimeDbPath; + + updateMimeProcess.Start(); + updateMimeProcess.WaitForExit(); + + if (updateMimeProcess.ExitCode != 0) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}"); + } + } + + return true; + } + + [SupportedOSPlatform("windows")] + private static bool AreMimeTypesRegisteredWindows() + { + static bool CheckRegistering(string ext) + { + RegistryKey key = Registry.CurrentUser.OpenSubKey(@$"Software\Classes\{ext}"); + + if (key is null) + { + return false; + } + + key.OpenSubKey(@"shell\open\command"); + + string keyValue = (string)key.GetValue(""); + + return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName)); + } + + bool registered = false; + + foreach (string ext in _fileExtensions) + { + registered |= CheckRegistering(ext); + } + + return registered; + } + + [SupportedOSPlatform("windows")] + private static bool InstallWindowsMimeTypes(bool uninstall = false) + { + static bool RegisterExtension(string ext, bool uninstall = false) + { + string keyString = @$"Software\Classes\{ext}"; + + if (uninstall) + { + if (!AreMimeTypesRegisteredWindows()) + { + return false; + } + + Registry.CurrentUser.DeleteSubKeyTree(keyString); + } + else + { + RegistryKey key = Registry.CurrentUser.CreateSubKey(keyString); + if (key is null) + { + return false; + } + + key.CreateSubKey(@"shell\open\command"); + + key.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\""); + key.Close(); + } + + // Notify Explorer the file association has been changed. + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero); + + return true; + } + + bool registered = false; + + foreach (string ext in _fileExtensions) + { + registered |= RegisterExtension(ext, uninstall); + } + + return registered; + } + + public static bool AreMimeTypesRegistered() + { + if (OperatingSystem.IsLinux()) + { + return AreMimeTypesRegisteredLinux(); + } + + if (OperatingSystem.IsWindows()) + { + return AreMimeTypesRegisteredWindows(); + } + + // TODO: Add macOS support. + + return false; + } + + public static bool Install() + { + if (OperatingSystem.IsLinux()) + { + return InstallLinuxMimeTypes(); + } + + if (OperatingSystem.IsWindows()) + { + return InstallWindowsMimeTypes(); + } + + // TODO: Add macOS support. + + return false; + } + + public static bool Uninstall() + { + if (OperatingSystem.IsLinux()) + { + return InstallLinuxMimeTypes(true); + } + + if (OperatingSystem.IsWindows()) + { + return InstallWindowsMimeTypes(true); + } + + // TODO: Add macOS support. + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 56352a4ce..ace8b87f9 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -18,7 +18,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Threading.Tasks; namespace Ryujinx @@ -73,48 +72,6 @@ namespace Ryujinx } } - [SupportedOSPlatform("linux")] - static void RegisterMimeTypes() - { - if (ReleaseInformation.IsFlatHubBuild()) - { - return; - } - - string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); - - if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml"))) - { - string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml"); - using Process mimeProcess = new(); - - mimeProcess.StartInfo.FileName = "xdg-mime"; - mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}"; - - mimeProcess.Start(); - mimeProcess.WaitForExit(); - - if (mimeProcess.ExitCode != 0) - { - Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}"); - return; - } - - using Process updateMimeProcess = new(); - - updateMimeProcess.StartInfo.FileName = "update-mime-database"; - updateMimeProcess.StartInfo.Arguments = mimeDbPath; - - updateMimeProcess.Start(); - updateMimeProcess.WaitForExit(); - - if (updateMimeProcess.ExitCode != 0) - { - Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}"); - } - } - } - static void Main(string[] args) { Version = ReleaseInformation.GetVersion(); @@ -189,12 +146,6 @@ namespace Ryujinx // Initialize the logger system. LoggerModule.Initialize(); - // Register mime types on linux. - if (OperatingSystem.IsLinux()) - { - RegisterMimeTypes(); - } - // Initialize Discord integration. DiscordIntegrationModule.Initialize(); diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 688e2f522..53a97fb9f 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -44,7 +44,6 @@ using System.IO; using System.Reflection; using System.Threading; using System.Threading.Tasks; - using GUI = Gtk.Builder.ObjectAttribute; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; @@ -109,6 +108,7 @@ namespace Ryujinx.Ui [GUI] CheckMenuItem _favToggle; [GUI] MenuItem _firmwareInstallDirectory; [GUI] MenuItem _firmwareInstallFile; + [GUI] MenuItem _fileTypesSubMenu; [GUI] Label _fifoStatus; [GUI] CheckMenuItem _iconToggle; [GUI] CheckMenuItem _developerToggle; @@ -220,6 +220,8 @@ namespace Ryujinx.Ui _pauseEmulation.Sensitive = false; _resumeEmulation.Sensitive = false; + _fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported; + if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true; @@ -1500,6 +1502,30 @@ namespace Ryujinx.Ui }); } + private void InstallFileTypes_Pressed(object sender, EventArgs e) + { + if (FileAssociationHelper.Install()) + { + GtkDialog.CreateInfoDialog("Install file types", "File types successfully installed!"); + } + else + { + GtkDialog.CreateErrorDialog("Failed to install file types."); + } + } + + private void UninstallFileTypes_Pressed(object sender, EventArgs e) + { + if (FileAssociationHelper.Uninstall()) + { + GtkDialog.CreateInfoDialog("Uninstall file types", "File types successfully uninstalled!"); + } + else + { + GtkDialog.CreateErrorDialog("Failed to uninstall file types."); + } + } + private void HandleRelaunch() { if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart) diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade index 405bc43fc..8ffbb97e5 100644 --- a/Ryujinx/Ui/MainWindow.glade +++ b/Ryujinx/Ui/MainWindow.glade @@ -1,67 +1,67 @@ - + - False + False Ryujinx - center + center True - False + False vertical True - False + False True - False + False File - True + True True - False + False True - False - Open a file explorer to choose a Switch compatible file to load + False + Open a file explorer to choose a Switch compatible file to load Load Application from File - True + True True - False - Open a file explorer to choose a Switch compatible, unpacked application to load + False + Open a file explorer to choose a Switch compatible, unpacked application to load Load Unpacked Game - True + True True - False + False Load Applet - True + True True - False + False True - False - Open Mii Editor Applet in Standalone mode + False + Open Mii Editor Applet in Standalone mode Mii Editor - True + True @@ -72,42 +72,42 @@ True - False + False True - False - Open Ryujinx filesystem folder + False + Open Ryujinx filesystem folder Open Ryujinx Folder - True + True True - False - Opens the folder where logs are written to. + False + Opens the folder where logs are written to. Open Logs Folder - True + True True - False + False True - False - Exit Ryujinx + False + Exit Ryujinx Exit - True + True @@ -118,144 +118,144 @@ True - False + False Options - True + True True - False + False True - False + False Enter Fullscreen - True + True True - False + False Start Games in Fullscreen Mode - True + True True - False + False Show Log Console - True + True True - False + False True - False - Select which GUI columns to enable + False + Select which GUI columns to enable Enable GUI Columns - True + True True - False + False True - False - Enable or Disable Favorite Games Column in the game list + False + Enable or Disable Favorite Games Column in the game list Enable Favorite Games Column - True + True True - False - Enable or Disable Icon Column in the game list + False + Enable or Disable Icon Column in the game list Enable Icon Column - True + True True - False - Enable or Disable Title Name/ID Column in the game list + False + Enable or Disable Title Name/ID Column in the game list Enable Title Name/ID Column - True + True True - False - Enable or Disable Developer Column in the game list + False + Enable or Disable Developer Column in the game list Enable Developer Column - True + True True - False - Enable or Disable Version Column in the game list + False + Enable or Disable Version Column in the game list Enable Version Column - True + True True - False - Enable or Disable Time Played Column in the game list + False + Enable or Disable Time Played Column in the game list Enable Time Played Column - True + True True - False - Enable or Disable Last Played Column in the game list + False + Enable or Disable Last Played Column in the game list Enable Last Played Column - True + True True - False - Enable or Disable file extension column in the game list + False + Enable or Disable file extension column in the game list Enable File Ext Column - True + True True - False - Enable or Disable File Size Column in the game list + False + Enable or Disable File Size Column in the game list Enable File Size Column - True + True True - False - Enable or Disable Path Column in the game list + False + Enable or Disable Path Column in the game list Enable Path Column - True + True @@ -265,26 +265,26 @@ True - False + False True - False - Open settings window + False + Open settings window Settings - True + True True - False - Open User Profiles Manager window + False + Open User Profiles Manager window Manage User Profiles - True + True @@ -295,74 +295,74 @@ True - False + False Actions - True + True True - False + False True - False - Pause emulation + False + Pause emulation Pause Emulation - True + True True - False - Resume emulation + False + Resume emulation Resume Emulation - True + True True - False - Stop emulation of the current game and return to game selection + False + Stop emulation of the current game and return to game selection Stop Emulation - True + True True - False + False True - False - Simulate a Wake-up Message + False + Simulate a Wake-up Message Simulate Wake-up Message - True + True True - False - Scan an Amiibo + False + Scan an Amiibo Scan an Amiibo - True + True True - False - Take a screenshot + False + Take a screenshot Take Screenshot @@ -370,16 +370,16 @@ True - False + False Hide UI (SHOWUIKEY to show) - True + True True - False + False Manage Cheats @@ -391,38 +391,38 @@ True - False + False Tools - True + True True - False + False True - False + False Install Firmware - True + True True - False + False True - False + False Install a firmware from XCI or ZIP - True + True True - False + False Install a firmware from a directory - True + True @@ -430,6 +430,36 @@ + + + True + False + Manage file types + True + + + True + False + + + True + False + Install file types + + + + + + True + False + Uninstall file types + + + + + + + @@ -437,36 +467,36 @@ True - False + False Help - True + True True - False + False True - False - Check for updates to Ryujinx + False + Check for updates to Ryujinx Check for Updates - True + True True - False + False True - False - Open about window + False + Open about window About - True + True @@ -484,24 +514,24 @@ True - False + False vertical True - False + False vertical True - True - in + True + in True - True + True True - True + True @@ -524,24 +554,24 @@ - 19 + 19 True - False + False True - False + False True - False - 5 + False + 5 RefreshList True - False + False gtk-refresh @@ -555,11 +585,11 @@ True - False - 10 - 5 - 2 - 2 + False + 10 + 5 + 2 + 2 0/0 Games Loaded @@ -570,13 +600,13 @@ - 200 + 200 True - False + False start - 10 - 5 - 6 + 10 + 5 + 6 True @@ -594,19 +624,19 @@ True - False + False True - False + False True - False + False start - 5 - 5 + 5 + 5 VSync @@ -620,7 +650,7 @@ True - False + False False @@ -631,15 +661,15 @@ True - False + False True - False + False start - 5 - 5 + 5 + 5 @@ -652,7 +682,7 @@ True - False + False False @@ -663,15 +693,15 @@ True - False + False True - False + False start - 5 - 5 + 5 + 5 @@ -684,7 +714,7 @@ True - False + False False @@ -695,15 +725,15 @@ True - False + False True - False + False start - 5 - 5 + 5 + 5 @@ -716,7 +746,7 @@ True - False + False False @@ -727,10 +757,10 @@ True - False + False start - 5 - 5 + 5 + 5 False @@ -741,7 +771,7 @@ True - False + False False @@ -752,10 +782,10 @@ True - False + False start - 5 - 5 + 5 + 5 False @@ -766,7 +796,7 @@ True - False + False False @@ -777,10 +807,10 @@ True - False + False start - 5 - 5 + 5 + 5 False @@ -791,7 +821,7 @@ True - False + False False @@ -802,10 +832,10 @@ True - False + False start - 5 - 5 + 5 + 5 True @@ -823,12 +853,12 @@ True - False - 5 + False + 5 True - False + False System Version @@ -839,16 +869,16 @@ - 50 + 50 True - False - 5 - 5 + False + 5 + 5 False True - end + end 1 @@ -856,15 +886,15 @@ False True - end + end 4 - False - 5 - 5 + False + 5 + 5 0/0 @@ -875,11 +905,11 @@ - 200 - False - 5 - 5 - 6 + 200 + False + 5 + 5 + 6 False @@ -903,8 +933,5 @@ - - - From 2fd819613ffcede43562b333602d17fa79c9751d Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 23 Jan 2023 19:20:40 -0300 Subject: [PATCH 324/737] SPIR-V: Change BitfieldExtract and BitfieldInsert for SPIRV-Cross (#4336) * SPIR-V: Change BitfieldExtract and BitfieldInsert types to make Metal MSL compiler happy * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Spirv/Instructions.cs | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 9f436502f..dcd0eb709 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4318; + private const uint CodeGenVersion = 4336; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 61abf3341..14d6ab52a 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -261,17 +261,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateBitfieldExtractS32(CodeGenContext context, AstOperation operation) { - return GenerateTernaryS32(context, operation, context.Delegates.BitFieldSExtract); + return GenerateBitfieldExtractS32(context, operation, context.Delegates.BitFieldSExtract); } private static OperationResult GenerateBitfieldExtractU32(CodeGenContext context, AstOperation operation) { - return GenerateTernaryS32(context, operation, context.Delegates.BitFieldUExtract); + return GenerateTernaryU32(context, operation, context.Delegates.BitFieldUExtract); } private static OperationResult GenerateBitfieldInsert(CodeGenContext context, AstOperation operation) { - return GenerateQuaternaryS32(context, operation, context.Delegates.BitFieldInsert); + return GenerateBitfieldInsert(context, operation, context.Delegates.BitFieldInsert); } private static OperationResult GenerateBitfieldReverse(CodeGenContext context, AstOperation operation) @@ -2290,22 +2290,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - private static OperationResult GenerateTernaryS32( - CodeGenContext context, - AstOperation operation, - Func emitS) - { - var src1 = operation.GetSource(0); - var src2 = operation.GetSource(1); - var src3 = operation.GetSource(2); - - return new OperationResult(AggregateType.S32, emitS( - context.TypeS32(), - context.GetS32(src1), - context.GetS32(src2), - context.GetS32(src3))); - } - private static OperationResult GenerateTernaryU32( CodeGenContext context, AstOperation operation, @@ -2322,7 +2306,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.GetU32(src3))); } - private static OperationResult GenerateQuaternaryS32( + private static OperationResult GenerateBitfieldExtractS32( + CodeGenContext context, + AstOperation operation, + Func emitS) + { + var src1 = operation.GetSource(0); + var src2 = operation.GetSource(1); + var src3 = operation.GetSource(2); + + return new OperationResult(AggregateType.S32, emitS( + context.TypeS32(), + context.GetS32(src1), + context.GetU32(src2), + context.GetU32(src3))); + } + + private static OperationResult GenerateBitfieldInsert( CodeGenContext context, AstOperation operation, Func emitS) @@ -2332,12 +2332,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var src3 = operation.GetSource(2); var src4 = operation.GetSource(3); - return new OperationResult(AggregateType.S32, emitS( - context.TypeS32(), - context.GetS32(src1), - context.GetS32(src2), - context.GetS32(src3), - context.GetS32(src4))); + return new OperationResult(AggregateType.U32, emitS( + context.TypeU32(), + context.GetU32(src1), + context.GetU32(src2), + context.GetU32(src3), + context.GetU32(src4))); } } } From a1a4771ac1de95f2410c7fb8dfaf4a5986e5ebc6 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 23 Jan 2023 19:37:53 -0300 Subject: [PATCH 325/737] Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer (#4337) * Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer * Rename FuncPtr to FuncPointer --- ARMeilleure/CodeGen/CompiledFunction.cs | 16 ++++++++++++++-- ARMeilleure/Instructions/NativeInterface.cs | 2 +- ARMeilleure/Translation/PTC/Ptc.cs | 4 ++-- ARMeilleure/Translation/TranslatedFunction.cs | 7 +++---- ARMeilleure/Translation/Translator.cs | 12 ++++++------ 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/ARMeilleure/CodeGen/CompiledFunction.cs b/ARMeilleure/CodeGen/CompiledFunction.cs index ab5e88ebb..0560bf2e9 100644 --- a/ARMeilleure/CodeGen/CompiledFunction.cs +++ b/ARMeilleure/CodeGen/CompiledFunction.cs @@ -48,9 +48,21 @@ namespace ARMeilleure.CodeGen /// A delegate of type pointing to the mapped function public T Map() { - IntPtr codePtr = JitCache.Map(this); + return MapWithPointer(out _); + } - return Marshal.GetDelegateForFunctionPointer(codePtr); + /// + /// Maps the onto the and returns a delegate of type + /// pointing to the mapped function. + /// + /// Type of delegate + /// Pointer to the function code in memory + /// A delegate of type pointing to the mapped function + public T MapWithPointer(out IntPtr codePointer) + { + codePointer = JitCache.Map(this); + + return Marshal.GetDelegateForFunctionPointer(codePointer); } } } \ No newline at end of file diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs index 2ac748a9a..57964cc8d 100644 --- a/ARMeilleure/Instructions/NativeInterface.cs +++ b/ARMeilleure/Instructions/NativeInterface.cs @@ -191,7 +191,7 @@ namespace ARMeilleure.Instructions { TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); - return (ulong)function.FuncPtr.ToInt64(); + return (ulong)function.FuncPointer.ToInt64(); } public static void InvalidateCacheLine(ulong address) diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index aeb5868c9..de2294b24 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -745,9 +745,9 @@ namespace ARMeilleure.Translation.PTC bool highCq) { var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty); - var gFunc = cFunc.Map(); + var gFunc = cFunc.MapWithPointer(out IntPtr gFuncPointer); - return new TranslatedFunction(gFunc, callCounter, guestSize, highCq); + return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq); } private void UpdateInfo(InfoEntry infoEntry) diff --git a/ARMeilleure/Translation/TranslatedFunction.cs b/ARMeilleure/Translation/TranslatedFunction.cs index 04dd769c1..71eec08ac 100644 --- a/ARMeilleure/Translation/TranslatedFunction.cs +++ b/ARMeilleure/Translation/TranslatedFunction.cs @@ -1,6 +1,5 @@ using ARMeilleure.Common; using System; -using System.Runtime.InteropServices; namespace ARMeilleure.Translation { @@ -8,18 +7,18 @@ namespace ARMeilleure.Translation { private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected. + public IntPtr FuncPointer { get; } public Counter CallCounter { get; } public ulong GuestSize { get; } public bool HighCq { get; } - public IntPtr FuncPtr { get; } - public TranslatedFunction(GuestFunction func, Counter callCounter, ulong guestSize, bool highCq) + public TranslatedFunction(GuestFunction func, IntPtr funcPointer, Counter callCounter, ulong guestSize, bool highCq) { _func = func; + FuncPointer = funcPointer; CallCounter = callCounter; GuestSize = guestSize; HighCq = highCq; - FuncPtr = Marshal.GetFunctionPointerForDelegate(func); } public ulong Execute(State.ExecutionContext context) diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index cbf6baa00..0c05b2b49 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -211,7 +211,7 @@ namespace ARMeilleure.Translation if (oldFunc != func) { - JitCache.Unmap(func.FuncPtr); + JitCache.Unmap(func.FuncPointer); func = oldFunc; } @@ -230,7 +230,7 @@ namespace ARMeilleure.Translation { if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq)) { - Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPtr); + Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPointer); } } @@ -292,11 +292,11 @@ namespace ARMeilleure.Translation _ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); } - GuestFunction func = compiledFunc.Map(); + GuestFunction func = compiledFunc.MapWithPointer(out IntPtr funcPointer); Allocators.ResetAll(); - return new TranslatedFunction(func, counter, funcSize, highCq); + return new TranslatedFunction(func, funcPointer, counter, funcSize, highCq); } private void BackgroundTranslate() @@ -537,7 +537,7 @@ namespace ARMeilleure.Translation foreach (var func in functions) { - JitCache.Unmap(func.FuncPtr); + JitCache.Unmap(func.FuncPointer); func.CallCounter?.Dispose(); } @@ -546,7 +546,7 @@ namespace ARMeilleure.Translation while (_oldFuncs.TryDequeue(out var kv)) { - JitCache.Unmap(kv.Value.FuncPtr); + JitCache.Unmap(kv.Value.FuncPointer); kv.Value.CallCounter?.Dispose(); } From e7cf4e6eaf528aa72e27f6ba86259c00813bc776 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Tue, 24 Jan 2023 16:32:56 +0000 Subject: [PATCH 326/737] Vulkan: Reset queries on same command buffer (#4329) * Reset queries on same command buffer Vulkan seems to complain when the queries are reset on another command buffer. No idea why, the spec really could be written better in this regard. This fixes complaints, and hopefully any implementations that care extensively about them. This change _guesses_ how many queries need to be reset and resets as many as possible at the same time to avoid splitting render passes. If it resets too many queries, we didn't waste too much time - if it runs out of resets it will batch reset 10 more. The number of queries reset is the maximum number of queries in the last 3 frames. This has been worked into the AutoFlushCounter so that it only resets up to 32 if it is yet to force a command buffer submission in this attachment. This is only done for samples passed queries right now, as they have by far the most resets. * Address Feedback --- Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs | 32 +++++++++++++++++++ Ryujinx.Graphics.Vulkan/PipelineBase.cs | 2 +- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 30 ++++------------- .../Queries/BufferedQuery.cs | 16 ++++++---- .../Queries/CounterQueue.cs | 23 ++++++++++++- .../Queries/CounterQueueEvent.cs | 2 +- Ryujinx.Graphics.Vulkan/Queries/Counters.cs | 13 ++++++++ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 10 ++++++ Ryujinx.Graphics.Vulkan/Window.cs | 2 ++ 9 files changed, 97 insertions(+), 33 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs index 4ba689ccb..2a4cbd52e 100644 --- a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs +++ b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; namespace Ryujinx.Graphics.Vulkan { @@ -16,6 +17,10 @@ namespace Ryujinx.Graphics.Vulkan private bool _hasPendingQuery; private int _queryCount; + private int[] _queryCountHistory = new int[3]; + private int _queryCountHistoryIndex; + private int _remainingQueries; + public void RegisterFlush(ulong drawCount) { _lastFlush = Stopwatch.GetTimestamp(); @@ -27,6 +32,9 @@ namespace Ryujinx.Graphics.Vulkan public bool RegisterPendingQuery() { _hasPendingQuery = true; + _remainingQueries--; + + _queryCountHistory[_queryCountHistoryIndex]++; // Interrupt render passes to flush queries, so that early results arrive sooner. if (++_queryCount == InitialQueryCountForFlush) @@ -37,6 +45,21 @@ namespace Ryujinx.Graphics.Vulkan return false; } + public int GetRemainingQueries() + { + if (_remainingQueries <= 0) + { + _remainingQueries = 16; + } + + if (_queryCount < InitialQueryCountForFlush) + { + return Math.Min(InitialQueryCountForFlush - _queryCount, _remainingQueries); + } + + return _remainingQueries; + } + public bool ShouldFlushQuery() { return _hasPendingQuery; @@ -69,5 +92,14 @@ namespace Ryujinx.Graphics.Vulkan return now > _lastFlush + flushTimeout; } + + public void Present() + { + _queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3; + + _remainingQueries = _queryCountHistory.Max() + 10; + + _queryCountHistory[_queryCountHistoryIndex] = 0; + } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 1c0c836bb..c53acfe10 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan protected readonly Device Device; public readonly PipelineCache PipelineCache; - protected readonly AutoFlushCounter AutoFlush; + public readonly AutoFlushCounter AutoFlush; protected PipelineDynamicState DynamicState; private PipelineState _newState; diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 5b4f4a6e9..94fd24416 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -14,7 +14,6 @@ namespace Ryujinx.Graphics.Vulkan private CounterQueueEvent _activeConditionalRender; private readonly List _pendingQueryCopies; - private readonly List _pendingQueryResets; private ulong _byteWeight; @@ -22,7 +21,6 @@ namespace Ryujinx.Graphics.Vulkan { _activeQueries = new List(); _pendingQueryCopies = new(); - _pendingQueryResets = new List(); CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; } @@ -34,16 +32,6 @@ namespace Ryujinx.Graphics.Vulkan query.PoolCopy(Cbs); } - lock (_pendingQueryResets) - { - foreach (var query in _pendingQueryResets) - { - query.PoolReset(CommandBuffer); - } - - _pendingQueryResets.Clear(); - } - _pendingQueryCopies.Clear(); } @@ -238,10 +226,12 @@ namespace Ryujinx.Graphics.Vulkan Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0); } + Gd.ResetCounterPool(); + Restore(); } - public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset) + public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool) { if (needsReset) { @@ -249,9 +239,11 @@ namespace Ryujinx.Graphics.Vulkan Gd.Api.CmdResetQueryPool(CommandBuffer, pool, 0, 1); - lock (_pendingQueryResets) + if (fromSamplePool) { - _pendingQueryResets.Remove(query); // Might be present on here. + // Try reset some additional queries in advance. + + Gd.ResetFutureCounters(CommandBuffer, AutoFlush.GetRemainingQueries()); } } @@ -267,14 +259,6 @@ namespace Ryujinx.Graphics.Vulkan _activeQueries.Remove(pool); } - public void ResetQuery(BufferedQuery query) - { - lock (_pendingQueryResets) - { - _pendingQueryResets.Add(query); - } - } - public void CopyQueryResults(BufferedQuery query) { _pendingQueryCopies.Add(query); diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index 4cf258eb7..a1a5eb279 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -18,7 +18,6 @@ namespace Ryujinx.Graphics.Vulkan.Queries private readonly PipelineFull _pipeline; private QueryPool _queryPool; - private bool _isReset; private readonly BufferHolder _buffer; private readonly IntPtr _bufferMap; @@ -27,6 +26,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private bool _isSupported; private long _defaultValue; + private int? _resetSequence; public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit) { @@ -92,16 +92,17 @@ namespace Ryujinx.Graphics.Vulkan.Queries public void Reset() { End(false); - Begin(); + Begin(null); } - public void Begin() + public void Begin(int? resetSequence) { if (_isSupported) { - _pipeline.BeginQuery(this, _queryPool, !_isReset); + bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value; + _pipeline.BeginQuery(this, _queryPool, needsReset, _type == CounterType.SamplesPassed && resetSequence != null); } - _isReset = false; + _resetSequence = null; } public unsafe void End(bool withResult) @@ -162,13 +163,14 @@ namespace Ryujinx.Graphics.Vulkan.Queries return data; } - public void PoolReset(CommandBuffer cmd) + public void PoolReset(CommandBuffer cmd, int resetSequence) { if (_isSupported) { _api.CmdResetQueryPool(cmd, _queryPool, 0, 1); } - _isReset = true; + + _resetSequence = resetSequence; } public void PoolCopy(CommandBufferScoped cbs) diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index 7ee3c15ae..c47f95eab 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -3,6 +3,7 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Threading; +using System.Linq; namespace Ryujinx.Graphics.Vulkan.Queries { @@ -32,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries private Thread _consumerThread; + public int ResetSequence { get; private set; } + internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type) { _gd = gd; @@ -53,6 +56,24 @@ namespace Ryujinx.Graphics.Vulkan.Queries _consumerThread.Start(); } + public void ResetCounterPool() + { + ResetSequence++; + } + + public void ResetFutureCounters(CommandBuffer cmd, int count) + { + // Pre-emptively reset queries to avoid render pass splitting. + lock (_queryPool) + { + count = Math.Min(count, _queryPool.Count); + for (int i = 0; i < count; i++) + { + _queryPool.ElementAt(i).PoolReset(cmd, ResetSequence); + } + } + } + private void EventConsumer() { while (!Disposed) @@ -106,7 +127,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries { lock (_lock) { - _pipeline.ResetQuery(query); + // The query will be reset when it dequeues. _queryPool.Enqueue(query); } } diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs b/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs index 241fe1ee0..6b780ba34 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries DrawIndex = drawIndex; - _counter.Begin(); + _counter.Begin(_queue.ResetSequence); } public Auto GetBuffer() diff --git a/Ryujinx.Graphics.Vulkan/Queries/Counters.cs b/Ryujinx.Graphics.Vulkan/Queries/Counters.cs index 63581e427..7113d0601 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/Counters.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/Counters.cs @@ -24,6 +24,19 @@ namespace Ryujinx.Graphics.Vulkan.Queries } } + public void ResetCounterPool() + { + foreach (var queue in _counterQueues) + { + queue.ResetCounterPool(); + } + } + + public void ResetFutureCounters(CommandBuffer cmd, int count) + { + _counterQueues[(int)CounterType.SamplesPassed].ResetFutureCounters(cmd, count); + } + public CounterQueueEvent QueueReport(CounterType type, EventHandler resultHandler, bool hostReserved) { return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved); diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index bec7b8475..f53f79400 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -679,6 +679,16 @@ namespace Ryujinx.Graphics.Vulkan _counters.Update(); } + public void ResetCounterPool() + { + _counters.ResetCounterPool(); + } + + public void ResetFutureCounters(CommandBuffer cmd, int count) + { + _counters?.ResetFutureCounters(cmd, count); + } + public void BackgroundContextAction(Action action, bool alwaysBackground = false) { action(); diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index edc7d7160..7412ad90d 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -225,6 +225,8 @@ namespace Ryujinx.Graphics.Vulkan public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { + _gd.PipelineInternal.AutoFlush.Present(); + uint nextImage = 0; while (true) From 296c4a3d012b1203b1b7e58cdc34c334159e1599 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 26 Jan 2023 18:34:35 -0300 Subject: [PATCH 327/737] Relax Vulkan requirements (#4282) * Relax Vulkan requirements * Fix MaxColorAttachmentIndex * Fix ColorBlendAttachmentStateCount value mismatch for background pipelines * Change query capability check to check for pipeline statistics query rather than geometry shader support --- Ryujinx.Graphics.Vulkan/BufferManager.cs | 22 ++++++- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 5 +- .../HardwareCapabilities.cs | 5 +- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 33 ++++++++-- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 5 +- Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 18 ++--- .../Queries/BufferedQuery.cs | 2 +- Ryujinx.Graphics.Vulkan/Vendor.cs | 3 + .../VulkanInitialization.cs | 66 +++++++++++++------ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 11 +++- Ryujinx.Graphics.Vulkan/Window.cs | 22 +++++-- 11 files changed, 139 insertions(+), 53 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index f32403712..9c50e6ff3 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan MemoryPropertyFlags.HostCoherentBit | MemoryPropertyFlags.HostCachedBit; + // Some drivers don't expose a "HostCached" memory type, + // so we need those alternative flags for the allocation to succeed there. + private const MemoryPropertyFlags DefaultBufferMemoryAltFlags = + MemoryPropertyFlags.HostVisibleBit | + MemoryPropertyFlags.HostCoherentBit; + private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = MemoryPropertyFlags.DeviceLocalBit; @@ -94,9 +100,21 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); - var allocateFlags = deviceLocal ? DeviceLocalBufferMemoryFlags : DefaultBufferMemoryFlags; + MemoryPropertyFlags allocateFlags; + MemoryPropertyFlags allocateFlagsAlt; - var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags); + if (deviceLocal) + { + allocateFlags = DeviceLocalBufferMemoryFlags; + allocateFlagsAlt = DeviceLocalBufferMemoryFlags; + } + else + { + allocateFlags = DefaultBufferMemoryFlags; + allocateFlagsAlt = DefaultBufferMemoryAltFlags; + } + + var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); if (allocation.Memory.Handle == 0UL) { diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 751ef5ebf..ff8d423b8 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan public int[] AttachmentIndices { get; } public int AttachmentsCount { get; } - public int MaxColorAttachmentIndex { get; } + public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1; public bool HasDepthStencil { get; } public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); @@ -66,8 +66,7 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = new uint[count]; AttachmentFormats = new VkFormat[count]; - AttachmentIndices = new int[count]; - MaxColorAttachmentIndex = colors.Length - 1; + AttachmentIndices = new int[colorsCount]; uint width = uint.MaxValue; uint height = uint.MaxValue; diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 8685d3442..376f39608 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -28,10 +28,11 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsMultiView; public readonly bool SupportsNullDescriptors; - public readonly bool SupportsPreciseOcclusionQueries; public readonly bool SupportsPushDescriptors; public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedbackQueries; + public readonly bool SupportsPreciseOcclusionQueries; + public readonly bool SupportsPipelineStatisticsQuery; public readonly bool SupportsGeometryShader; public readonly uint MinSubgroupSize; public readonly uint MaxSubgroupSize; @@ -55,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsTransformFeedback, bool supportsTransformFeedbackQueries, bool supportsPreciseOcclusionQueries, + bool supportsPipelineStatisticsQuery, bool supportsGeometryShader, uint minSubgroupSize, uint maxSubgroupSize, @@ -77,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries; SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; + SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery; SupportsGeometryShader = supportsGeometryShader; MinSubgroupSize = minSubgroupSize; MaxSubgroupSize = maxSubgroupSize; diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index eea4e60b3..83c0a3243 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -27,7 +27,16 @@ namespace Ryujinx.Graphics.Vulkan MemoryRequirements requirements, MemoryPropertyFlags flags = 0) { - int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags); + return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); + } + + public MemoryAllocation AllocateDeviceMemory( + PhysicalDevice physicalDevice, + MemoryRequirements requirements, + MemoryPropertyFlags flags, + MemoryPropertyFlags alternativeFlags) + { + int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); if (memoryTypeIndex < 0) { return default; @@ -56,21 +65,35 @@ namespace Ryujinx.Graphics.Vulkan return newBl.Allocate(size, alignment, map); } - private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, MemoryPropertyFlags flags) + private static int FindSuitableMemoryTypeIndex( + Vk api, + PhysicalDevice physicalDevice, + uint memoryTypeBits, + MemoryPropertyFlags flags, + MemoryPropertyFlags alternativeFlags) { + int bestCandidateIndex = -1; + api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); for (int i = 0; i < properties.MemoryTypeCount; i++) { var type = properties.MemoryTypes[i]; - if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) + if ((memoryTypeBits & (1 << i)) != 0) { - return i; + if (type.PropertyFlags.HasFlag(flags)) + { + return i; + } + else if (type.PropertyFlags.HasFlag(alternativeFlags)) + { + bestCandidateIndex = i; + } } } - return -1; + return bestCandidateIndex; } public void Dispose() diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c53acfe10..43dccf86e 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1344,8 +1344,7 @@ namespace Ryujinx.Graphics.Vulkan var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); - int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex + (FramebufferParams.HasDepthStencil ? 1 : 0); - for (int i = FramebufferParams.AttachmentFormats.Length; i <= maxAttachmentIndex; i++) + for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) { dstAttachmentFormats[i] = 0; } @@ -1376,8 +1375,6 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < FramebufferParams.AttachmentsCount; i++) { - int bindIndex = FramebufferParams.AttachmentIndices[i]; - attachmentDescs[i] = new AttachmentDescription( 0, FramebufferParams.AttachmentFormats[i], diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 3e85ec36a..26d34e54e 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -27,14 +27,11 @@ namespace Ryujinx.Graphics.Vulkan int attachmentCount = 0; int colorCount = 0; - int maxColorAttachmentIndex = 0; for (int i = 0; i < state.AttachmentEnable.Length; i++) { if (state.AttachmentEnable[i]) { - maxColorAttachmentIndex = i; - attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); attachmentIndices[attachmentCount++] = i; @@ -270,7 +267,7 @@ namespace Ryujinx.Graphics.Vulkan // NOTE: Viewports, Scissors are dynamic. - for (int i = 0; i < 8; i++) + for (int i = 0; i < Constants.MaxRenderTargets; i++) { var blend = state.BlendDescriptors[i]; @@ -293,21 +290,24 @@ namespace Ryujinx.Graphics.Vulkan } } - int maxAttachmentIndex = 0; - for (int i = 0; i < 8; i++) + int attachmentCount = 0; + int maxColorAttachmentIndex = -1; + + for (int i = 0; i < Constants.MaxRenderTargets; i++) { if (state.AttachmentEnable[i]) { - pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); + pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); + maxColorAttachmentIndex = i; } } if (state.DepthStencilEnable) { - pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); + pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); } - pipeline.ColorBlendAttachmentStateCount = 8; + pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); return pipeline; diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index a1a5eb279..abb43eb56 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -67,8 +67,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries return type switch { CounterType.SamplesPassed => true, + CounterType.PrimitivesGenerated => gd.Capabilities.SupportsPipelineStatisticsQuery, CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries, - CounterType.PrimitivesGenerated => gd.Capabilities.SupportsGeometryShader, _ => false }; } diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/Ryujinx.Graphics.Vulkan/Vendor.cs index 087d6e9dc..5e0290c0a 100644 --- a/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Vulkan Intel, Nvidia, ARM, + Broadcom, Qualcomm, Apple, Unknown @@ -28,6 +29,7 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => Vendor.Apple, 0x10DE => Vendor.Nvidia, 0x13B5 => Vendor.ARM, + 0x14E4 => Vendor.Broadcom, 0x8086 => Vendor.Intel, 0x5143 => Vendor.Qualcomm, _ => Vendor.Unknown @@ -43,6 +45,7 @@ namespace Ryujinx.Graphics.Vulkan 0x106B => "Apple", 0x10DE => "NVIDIA", 0x13B5 => "ARM", + 0x14E4 => "Broadcom", 0x1AE0 => "Google", 0x5143 => "Qualcomm", 0x8086 => "Intel", diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 00baad103..f38fc8ab8 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -162,7 +162,6 @@ namespace Ryujinx.Graphics.Vulkan if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) { Logger.Error?.Print(LogClass.Gpu, msg); - //throw new Exception(msg); } else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) { @@ -379,14 +378,34 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceFeatures2 }; - PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT() + PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new PhysicalDeviceVulkan11Features() { - SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt + SType = StructureType.PhysicalDeviceVulkan11Features, + PNext = features2.PNext + }; + + features2.PNext = &supportedFeaturesVk11; + + PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() + { + SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, + PNext = features2.PNext }; if (supportedExtensions.Contains("VK_EXT_custom_border_color")) { - features2.PNext = &featuresCustomBorderColorSupported; + features2.PNext = &supportedFeaturesCustomBorderColor; + } + + PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + { + SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, + PNext = features2.PNext + }; + + if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + { + features2.PNext = &supportedFeaturesTransformFeedback; } PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() @@ -408,42 +427,49 @@ namespace Ryujinx.Graphics.Vulkan var features = new PhysicalDeviceFeatures() { DepthBiasClamp = true, - DepthClamp = true, - DualSrcBlend = true, + DepthClamp = supportedFeatures.DepthClamp, + DualSrcBlend = supportedFeatures.DualSrcBlend, FragmentStoresAndAtomics = true, GeometryShader = supportedFeatures.GeometryShader, ImageCubeArray = true, IndependentBlend = true, LogicOp = supportedFeatures.LogicOp, - MultiViewport = true, OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, + MultiViewport = supportedFeatures.MultiViewport, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, SamplerAnisotropy = true, ShaderClipDistance = true, ShaderFloat64 = supportedFeatures.ShaderFloat64, - ShaderImageGatherExtended = true, + ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, // ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true, - TessellationShader = true, + TessellationShader = supportedFeatures.TessellationShader, VertexPipelineStoresAndAtomics = true, RobustBufferAccess = useRobustBufferAccess }; void* pExtendedFeatures = null; - var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() - { - SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, - PNext = pExtendedFeatures, - TransformFeedback = true - }; + PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; - pExtendedFeatures = &featuresTransformFeedback; + if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + { + featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + { + SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, + PNext = pExtendedFeatures, + TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback + }; + + pExtendedFeatures = &featuresTransformFeedback; + } + + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; if (supportedExtensions.Contains("VK_EXT_robustness2")) { - var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() { SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, PNext = pExtendedFeatures, @@ -466,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceVulkan11Features, PNext = pExtendedFeatures, - ShaderDrawParameters = true + ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters }; pExtendedFeatures = &featuresVk11; @@ -527,8 +553,8 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; if (supportedExtensions.Contains("VK_EXT_custom_border_color") && - featuresCustomBorderColorSupported.CustomBorderColors && - featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat) + supportedFeaturesCustomBorderColor.CustomBorderColors && + supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) { featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() { diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index f53f79400..085a7e930 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -271,6 +271,7 @@ namespace Ryujinx.Graphics.Vulkan supportsTransformFeedback, propertiesTransformFeedback.TransformFeedbackQueries, features2.Features.OcclusionQueryPrecise, + supportedFeatures.PipelineStatisticsQuery, supportedFeatures.GeometryShader, propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, @@ -589,9 +590,13 @@ namespace Ryujinx.Graphics.Vulkan Vendor = VendorUtils.FromId(properties.VendorID); - IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec; + IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows(); + IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows(); + IsTBDR = IsMoltenVk || + Vendor == Vendor.Qualcomm || + Vendor == Vendor.ARM || + Vendor == Vendor.Broadcom || + Vendor == Vendor.ImgTec; GpuVendor = vendorName; GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 7412ad90d..9f731e0e6 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -120,7 +120,7 @@ namespace Ryujinx.Graphics.Vulkan ImageSharingMode = SharingMode.Exclusive, ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, - CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, + CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha), PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), Clipped = true }; @@ -188,6 +188,22 @@ namespace Ryujinx.Graphics.Vulkan return availableFormats[0]; } + private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags) + { + if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr)) + { + return CompositeAlphaFlagsKHR.OpaqueBitKhr; + } + else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr)) + { + return CompositeAlphaFlagsKHR.PreMultipliedBitKhr; + } + else + { + return CompositeAlphaFlagsKHR.InheritBitKhr; + } + } + private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) { if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) @@ -198,10 +214,6 @@ namespace Ryujinx.Graphics.Vulkan { return PresentModeKHR.MailboxKhr; } - else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr)) - { - return PresentModeKHR.FifoKhr; - } else { return PresentModeKHR.FifoKhr; From c7f9962ddee502030e790a0bfc0f949eaf5910c7 Mon Sep 17 00:00:00 2001 From: Piplup <100526773+piplup55@users.noreply.github.com> Date: Fri, 27 Jan 2023 18:23:55 +0000 Subject: [PATCH 328/737] converts the templates into forms (#4068) * Create bug_report.yml * Update bug_report.yml * Update bug_report.yml * Create feature_request.yml * Update feature_request.yml * Update feature_request.yml * Update feature_request.yml * Update feature_request.yml * a * Update missing_cpu_instruction.yml * Update missing_cpu_instruction.yml * Update missing_cpu_instruction.yml * Update missing_cpu_instruction.yml * b * addressed some of the feedback * forget the label * added missing text inputs * formatting changes * dropdown menu added dropdown menu for os, idk if we will keep this * addressed feedback addressed the long overdue feedback, sorry about that * added markdowns everything should be addressed now i hope * game version optional made game version optional after further feedback * feature request checkbox --- .github/ISSUE_TEMPLATE/bug_report.md | 43 ----------- .github/ISSUE_TEMPLATE/bug_report.yml | 75 +++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 34 --------- .github/ISSUE_TEMPLATE/feature_request.yml | 36 +++++++++ .../ISSUE_TEMPLATE/missing_cpu_instruction.md | 34 --------- .../missing_cpu_instruction.yml | 31 ++++++++ .../ISSUE_TEMPLATE/missing_service_call.md | 35 --------- .../ISSUE_TEMPLATE/missing_service_call.yml | 31 ++++++++ 8 files changed, 173 insertions(+), 146 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 .github/ISSUE_TEMPLATE/missing_cpu_instruction.md create mode 100644 .github/ISSUE_TEMPLATE/missing_cpu_instruction.yml delete mode 100644 .github/ISSUE_TEMPLATE/missing_service_call.md create mode 100644 .github/ISSUE_TEMPLATE/missing_service_call.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 9c0b18c54..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -name: Bug Report -about: Something doesn't work correctly in Ryujinx. Game-specific issues should be posted at https://github.com/Ryujinx/Ryujinx-Games-List instead, unless it is a provable regression. -#assignees: ---- - -## Bug Report - -[ If any section does not apply, replace its contents with "N/A". ]
    -[ Lines between [ ] (square brackets) should be removed before posting. ] - -### What's the issue you encountered? - -[ Describe the issue in detail and what you were doing beforehand. ]
    -[ Did you make any changes related to Ryujinx itself? ]
    -[ If so, make sure to include details relating to what exactly you changed. ] - -### How can the issue be reproduced? - -[ Include a detailed step by step process for recreating your issue. ] - -### Log file - -[ Logs files can be found under ``Logs`` folder in Ryujinx program folder. ]
    -[ If you don't include a crash report in instances of crash related issues, we will ask you one to provide one. ] - -### Environment? - -- Ryujinx version: 1.0.X
    -[ Replace X's with the Ryujinx version at time of crash. ] -- Game version: X.X.X
    -[ Replace X's with the game version at time of crash. ] -- System Specs: - - OS: *(e.g. Windows 10)* - - CPU: *(e.g. i7-6700)* - - GPU: *(e.g. NVIDIA RTX 2070)* - - RAM: *(e.g. 16GiB)* -- Applied Mods : [ Yes (Which ones) / No ] - -### Additional context? - -Additional info about your environment:
    -[ Any other information relevant to your issue. ] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..a7560fc69 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,75 @@ +name: Bug Report +description: File a bug report +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: issue + attributes: + label: Description of Issue + description: What's the issue you encountered? + validations: + required: true + - type: textarea + id: repro + attributes: + label: Reproduction Steps + description: How can the issue be reproduced? + validations: + required: true + - type: textarea + id: log + attributes: + label: Log File + description: A log file will help our developers to better diagnose and fix the issue. + placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area + validations: + required: true + - type: input + id: os + attributes: + label: OS + placeholder: "Example: Windows 10" + validations: + required: true + - type: input + id: ryujinx-version + attributes: + label: Ryujinx version + placeholder: | + - *(e.g. 1.0.470)* + validations: + required: true + - type: input + id: game-version + attributes: + label: Game version + placeholder: | + - *(e.g. 1.1.1)* + validations: + required: false + - type: textarea + id: environment + attributes: + label: Environment? + value: | + - ##### CPU: *(e.g. i7-6700)* + - ##### GPU: *(e.g. NVIDIA RTX 2070)* + - ##### RAM: *(e.g. 16GB)* + - Applied Mods: [ Yes (Which ones) / No ] + validations: + required: true + - type: textarea + id: additional-context + attributes: + label: Additional context? + description: | + - Additional info about your environment: + - Any other information relevant to your issue. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index f1855f269..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Feature Request -about: Suggest a new feature for Ryujinx. -#assignees: ---- - -## Feature Request - -[ If any section does not apply, replace its contents with "N/A". ]
    -[ If you do not have the information needed for a section, replace its contents with "Unknown". ]
    -[ Lines between [ ] (square brackets) are to be removed before posting. ]
    - -[ Please search for existing [feature requests](https://github.com/Ryujinx/Ryujinx/issues) before you make your own request. ]
    -[ Duplicate requests will be marked as such and you will be referred to the original request. ] - -### What feature are you suggesting? -#### Overview: -- [ Include the basic, high-level concepts for this feature here. ] - -#### Smaller Details: -- [ These may include specific methods of implementation etc. ] - -#### Nature of Request: -[ Remove all that do not apply to your request. ] -- Addition - - [ Ex: Addition of certain original features or features from other community projects. ] - - [ If you are suggesting porting features or including features from other projects, include what license they are distributed under and what, if any libraries those project use. ] -- Change -- Removal - - [Ex: Removal of certain features or implementation due to a specific issue/bug or because of low quality code, etc.] - -### Why would this feature be useful? -[ If this is a feature for an end-user, how does it benefit the end-user? ]
    -[ If this feature is for developers, what does it add to Ryujinx that did not already exist? ] diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..f755812f6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest a new feature for Ryujinx. +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the feature you are requesting. + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: overview + attributes: + label: Overview + description: Include the basic, high-level concepts for this feature here. + validations: + required: true + - type: textarea + id: details + attributes: + label: Smaller Details + description: These may include specific methods of implementation etc. + validations: + required: true + - type: textarea + id: request + attributes: + label: Nature of Request + validations: + required: true + - type: textarea + id: feature + attributes: + label: Why would this feature be useful? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/missing_cpu_instruction.md b/.github/ISSUE_TEMPLATE/missing_cpu_instruction.md deleted file mode 100644 index abfda1159..000000000 --- a/.github/ISSUE_TEMPLATE/missing_cpu_instruction.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Missing CPU Instruction -about: CPU Instruction is missing in Ryujinx. -#assignees: ---- - -## Missing CPU Instruction - -[ If any section does not apply, replace its contents with "N/A". ]
    -[ If you do not have the information needed for a section, replace its contents with "Unknown". ]
    -[ Lines between [ ] (square brackets) are to be removed before posting. ] - -[ Please search for existing [missing CPU instruction](https://github.com/Ryujinx/Ryujinx/issues) before you make your own issue. ]
    -[ See the following [issue](https://github.com/Ryujinx/Ryujinx/issues/1405) as an example ]
    -[ Duplicate issue will be marked as such and you will be referred to the original request. ] - -### What CPU instruction is missing? - -Requires the *INSTRUCTION* instruction.
    -[ Replace *INSTRUCTION* by the instruction name, e.g. VADDL.U16 ] - -``` -* -``` -[ Add the undefined instruction error message in the above code block ] - -### Instruction name -``` -* -``` -[ Include the name from [armconverter.com](https://armconverter.com/?disasm) or [shell-storm.org](http://shell-storm.org/online/Online-Assembler-and-Disassembler/?arch=arm64&endianness=big&dis_with_raw=True&dis_with_ins=True) in the above code block ] - -### Required by: -[ Add our (games list database)[https://github.com/Ryujinx/Ryujinx-Games-List/issues] links of games who require this instruction ] diff --git a/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml b/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml new file mode 100644 index 000000000..237e61e91 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml @@ -0,0 +1,31 @@ +name: Missing CPU Instruction +description: CPU Instruction is missing in Ryujinx. +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search for existing missing CPU instruction + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: instruction + attributes: + label: CPU instruction + description: What CPU instruction is missing? + validations: + required: true + - type: textarea + id: name + attributes: + label: Instruction name + description: Include the name from [armconverter.com](https://armconverter.com/?disasm) or [shell-storm.org](http://shell-storm.org/online/Online-Assembler-and-Disassembler/?arch=arm64&endianness=big&dis_with_raw=True&dis_with_ins=True) in the above code block + validations: + required: true + - type: textarea + id: required + attributes: + label: Required by + description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/missing_service_call.md b/.github/ISSUE_TEMPLATE/missing_service_call.md deleted file mode 100644 index d221add83..000000000 --- a/.github/ISSUE_TEMPLATE/missing_service_call.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Missing Service Call -about: Service call is missing in Ryujinx. -#assignees: ---- - -## Missing Service Call - -[ If any section does not apply, replace its contents with "N/A". ]
    -[ If you do not have the information needed for a section, replace its contents with "Unknown". ]
    -[ Lines between [ ] (square brackets) are to be removed before posting. ] - -[ Please search for existing [missing service call](https://github.com/Ryujinx/Ryujinx/issues) before you make your own issue. ]
    -[ See the following [issue](https://github.com/Ryujinx/Ryujinx/issues/1431) as an example ]
    -[ Duplicate issue will be marked as such and you will be referred to the original request. ] - -### What service call is missing? - -*SERVICE* *INTERFACE*: *NUMBER* (*NAME*) is not implemented.
    -[ Replace *SERVICE* by the service name, e.g. appletAE ]
    -[ Replace *INTERFACE* by the interface name, e.g. IAllSystemAppletProxiesService ]
    -[ Replace *NUMBER* by the call number, e.g. 100 ]
    -[ Replace *NAME* by the call name, e.g. OpenSystemAppletProxy ]
    -[ e.g. appletAE IAllSystemAppletProxiesService: 100 (OpenSystemAppletProxy) ] - -[ Add related links to the specific call from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) ] - -### Service description -``` -* -``` -[ Include the description/explanation from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) in the above code block ] - -### Required by: -[ Add our (games list database)[https://github.com/Ryujinx/Ryujinx-Games-List/issues] links of games who require this call ] diff --git a/.github/ISSUE_TEMPLATE/missing_service_call.yml b/.github/ISSUE_TEMPLATE/missing_service_call.yml new file mode 100644 index 000000000..72a521173 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/missing_service_call.yml @@ -0,0 +1,31 @@ +name: Missing Service Call +description: Service call is missing in Ryujinx. +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search for an [existing missing service call issue](https://github.com/Ryujinx/Ryujinx/issues) first. + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: instruction + attributes: + label: Service Call + description: What service call is missing? + validations: + required: true + - type: textarea + id: name + attributes: + label: Service description + description: Include the description/explanation from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) in the above code block + validations: + required: true + - type: textarea + id: required + attributes: + label: Required by + description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this service. + validations: + required: true \ No newline at end of file From a53cfdab78c382677eb826bd5bedb58b3b838796 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 29 Jan 2023 08:37:52 -0300 Subject: [PATCH 329/737] Initial Apple Hypervisor based CPU emulation (#4332) * Initial Apple Hypervisor based CPU emulation implementation * Add UseHypervisor Setting * Add basic MacOS support to Avalonia * Fix initialization * Fix GTK build * Fix/silence warnings * Change exceptions to asserts on HvAddressSpaceRange * Replace DllImport with LibraryImport * Fix LibraryImport * Remove unneeded usings * Revert outdated change * Set DiskCacheLoadState when using hypervisor too * Fix HvExecutionContext PC value * Address PR feedback * Use existing entitlements.xml file on distribution folder --------- Co-authored-by: riperiperi --- Ryujinx.Ava/AppHost.cs | 7 +- Ryujinx.Ava/Assets/Locales/en_US.json | 4 +- Ryujinx.Ava/Ryujinx.Ava.csproj | 7 +- .../UI/ViewModels/SettingsViewModel.cs | 14 +- .../UI/Views/Settings/SettingsCPUView.axaml | 10 +- Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs | 27 + Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs | 47 + .../AppleHv/DummyDiskCacheLoadState.cs | 17 + Ryujinx.Cpu/AppleHv/HvAddressSpace.cs | 129 +++ Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs | 370 +++++++ Ryujinx.Cpu/AppleHv/HvApi.cs | 320 ++++++ Ryujinx.Cpu/AppleHv/HvCpuContext.cs | 47 + Ryujinx.Cpu/AppleHv/HvEngine.cs | 20 + Ryujinx.Cpu/AppleHv/HvExecutionContext.cs | 284 ++++++ .../AppleHv/HvExecutionContextShadow.cs | 59 ++ Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs | 196 ++++ Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs | 34 + .../AppleHv/HvMemoryBlockAllocation.cs | 34 + Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs | 59 ++ Ryujinx.Cpu/AppleHv/HvMemoryManager.cs | 947 ++++++++++++++++++ Ryujinx.Cpu/AppleHv/HvVcpu.cs | 25 + Ryujinx.Cpu/AppleHv/HvVcpuPool.cs | 103 ++ Ryujinx.Cpu/AppleHv/HvVm.cs | 68 ++ Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs | 46 + Ryujinx.HLE/HLEConfiguration.cs | 9 +- Ryujinx.HLE/HOS/ArmProcessContextFactory.cs | 59 +- Ryujinx.HLE/HOS/Horizon.cs | 3 - Ryujinx.HLE/HOS/ProgramLoader.cs | 4 +- Ryujinx.Headless.SDL2/Options.cs | 3 + Ryujinx.Headless.SDL2/Program.cs | 3 +- .../Configuration/ConfigurationFileFormat.cs | 7 +- .../Configuration/ConfigurationState.cs | 19 +- Ryujinx/Ui/MainWindow.cs | 3 +- 33 files changed, 2939 insertions(+), 45 deletions(-) create mode 100644 Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs create mode 100644 Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs create mode 100644 Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvAddressSpace.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvApi.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvCpuContext.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvEngine.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvExecutionContext.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvMemoryManager.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvVcpu.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvVcpuPool.cs create mode 100644 Ryujinx.Cpu/AppleHv/HvVm.cs create mode 100644 Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index ad33d08c8..6146a7d9e 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -241,7 +241,7 @@ namespace Ryujinx.Ava { DateTime currentTime = DateTime.Now; string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; - + string directory = AppDataManager.Mode switch { AppDataManager.LaunchMode.Portable => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), @@ -678,7 +678,8 @@ namespace Ryujinx.Ava ConfigurationState.Instance.System.MemoryManagerMode, ConfigurationState.Instance.System.IgnoreMissingServices, ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume); + ConfigurationState.Instance.System.AudioVolume, + ConfigurationState.Instance.System.UseHypervisor); Device = new Switch(configuration); } @@ -839,7 +840,7 @@ namespace Ryujinx.Ava { // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; - + if (GraphicsConfig.ResScale != 1) { dockedMode += $" ({GraphicsConfig.ResScale}x)"; diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index 9bc6b5817..b7d1e02bf 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -7,6 +7,7 @@ "SettingsTabSystemMemoryManagerModeSoftware": "Software", "SettingsTabSystemMemoryManagerModeHost": "Host (fast)", "SettingsTabSystemMemoryManagerModeHostUnchecked": "Host Unchecked (fastest, unsafe)", + "SettingsTabSystemUseHypervisor": "Use Hypervisor", "MenuBarFile": "_File", "MenuBarFileOpenFromFile": "_Load Application From File", "MenuBarFileOpenUnpacked": "Load _Unpacked Game", @@ -457,6 +458,7 @@ "MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.", "MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", "MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", + "UseHypervisorTooltip": "Use Hypervisor instead of JIT. Greatly improves performance when available, but can be unstable in its current state.", "DRamTooltip": "Utilizes an alternative MemoryMode layout to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.", "GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", @@ -507,7 +509,7 @@ "SettingsTabNetwork": "Network", "SettingsTabNetworkConnection": "Network Connection", "SettingsTabCpuCache": "CPU Cache", - "SettingsTabCpuMemory": "CPU Memory", + "SettingsTabCpuMemory": "CPU Mode", "DialogUpdaterFlatpakNotSupportedMessage": "Please update Ryujinx via FlatHub.", "UpdaterDisabledWarningTitle": "Updater Disabled!", "GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 88b60d0ba..61a2bcb7e 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -1,4 +1,4 @@ - + net7.0 win10-x64;osx-x64;linux-x64 @@ -6,11 +6,16 @@ true 1.0.0-dirty $(DefineConstants);$(ExtraDefineConstants) + - Ryujinx.Ava Ryujinx.ico true + + + + true true diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index e6a0111e8..36b37b0f5 100644 --- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Runtime.InteropServices; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; namespace Ryujinx.Ava.UI.ViewModels @@ -59,6 +60,7 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(nameof(IsCustomResolutionScaleActive)); } } + public int GraphicsBackendMultithreadingIndex { get => _graphicsBackendMultithreadingIndex; @@ -106,6 +108,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); + public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; + public bool DirectoryChanged { get => _directoryChanged; @@ -117,10 +121,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool IsMacOS - { - get => OperatingSystem.IsMacOS(); - } + public bool IsMacOS => OperatingSystem.IsMacOS(); public bool EnableDiscordIntegration { get; set; } public bool CheckUpdatesOnStart { get; set; } @@ -153,6 +154,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableCustomTheme { get; set; } public bool IsCustomResolutionScaleActive => _resolutionScale == 4; public bool IsVulkanSelected => GraphicsBackendIndex == 0; + public bool UseHypervisor { get; set; } public string TimeZone { get; set; } public string ShaderDumpPath { get; set; } @@ -349,6 +351,7 @@ namespace Ryujinx.Ava.UI.ViewModels // CPU EnablePptc = config.System.EnablePtc; MemoryMode = (int)config.System.MemoryManagerMode.Value; + UseHypervisor = config.System.UseHypervisor; // Graphics GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; @@ -369,7 +372,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Network EnableInternetAccess = config.System.EnableInternetAccess; - + // Logging EnableFileLog = config.Logger.EnableFileLog; EnableStub = config.Logger.EnableStub; @@ -432,6 +435,7 @@ namespace Ryujinx.Ava.UI.ViewModels // CPU config.System.EnablePtc.Value = EnablePptc; config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode; + config.System.UseHypervisor.Value = UseHypervisor; // Graphics config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex; diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml index a0cf34529..e98b963c1 100644 --- a/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml @@ -1,4 +1,4 @@ - + + + - \ No newline at end of file + diff --git a/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs b/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs new file mode 100644 index 000000000..95e674327 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.Cpu.AppleHv.Arm +{ + enum ApFlags : ulong + { + ApShift = 6, + PxnShift = 53, + UxnShift = 54, + + UserExecuteKernelReadWriteExecute = (0UL << (int)ApShift), + UserReadWriteExecuteKernelReadWrite = (1UL << (int)ApShift), + UserExecuteKernelReadExecute = (2UL << (int)ApShift), + UserReadExecuteKernelReadExecute = (3UL << (int)ApShift), + + UserExecuteKernelReadWrite = (1UL << (int)PxnShift) | (0UL << (int)ApShift), + UserExecuteKernelRead = (1UL << (int)PxnShift) | (2UL << (int)ApShift), + UserReadExecuteKernelRead = (1UL << (int)PxnShift) | (3UL << (int)ApShift), + + UserNoneKernelReadWriteExecute = (1UL << (int)UxnShift) | (0UL << (int)ApShift), + UserReadWriteKernelReadWrite = (1UL << (int)UxnShift) | (1UL << (int)ApShift), + UserNoneKernelReadExecute = (1UL << (int)UxnShift) | (2UL << (int)ApShift), + UserReadKernelReadExecute = (1UL << (int)UxnShift) | (3UL << (int)ApShift), + + UserNoneKernelReadWrite = (1UL << (int)PxnShift) | (1UL << (int)UxnShift) | (0UL << (int)ApShift), + UserNoneKernelRead = (1UL << (int)PxnShift) | (1UL << (int)UxnShift) | (2UL << (int)ApShift), + UserReadKernelRead = (1UL << (int)PxnShift) | (1UL << (int)UxnShift) | (3UL << (int)ApShift) + } +} diff --git a/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs b/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs new file mode 100644 index 000000000..18152f254 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs @@ -0,0 +1,47 @@ +namespace Ryujinx.Cpu.AppleHv.Arm +{ + enum ExceptionClass + { + Unknown = 0b000000, + TrappedWfeWfiWfetWfit = 0b000001, + TrappedMcrMrcCp15 = 0b000011, + TrappedMcrrMrrcCp15 = 0b000100, + TrappedMcrMrcCp14 = 0b000101, + TrappedLdcStc = 0b000110, + TrappedSveFpSimd = 0b000111, + TrappedVmrs = 0b001000, + TrappedPAuth = 0b001001, + TrappedLd64bSt64bSt64bvSt64bv0 = 0b001010, + TrappedMrrcCp14 = 0b001100, + IllegalExecutionState = 0b001110, + SvcAarch32 = 0b010001, + HvcAarch32 = 0b010010, + SmcAarch32 = 0b010011, + SvcAarch64 = 0b010101, + HvcAarch64 = 0b010110, + SmcAarch64 = 0b010111, + TrappedMsrMrsSystem = 0b011000, + TrappedSve = 0b011001, + TrappedEretEretaaEretab = 0b011010, + PointerAuthenticationFailure = 0b011100, + ImplementationDefinedEl3 = 0b011111, + InstructionAbortLowerEl = 0b100000, + InstructionAbortSameEl = 0b100001, + PcAlignmentFault = 0b100010, + DataAbortLowerEl = 0b100100, + DataAbortSameEl = 0b100101, + SpAlignmentFault = 0b100110, + TrappedFpExceptionAarch32 = 0b101000, + TrappedFpExceptionAarch64 = 0b101100, + SErrorInterrupt = 0b101111, + BreakpointLowerEl = 0b110000, + BreakpointSameEl = 0b110001, + SoftwareStepLowerEl = 0b110010, + SoftwareStepSameEl = 0b110011, + WatchpointLowerEl = 0b110100, + WatchpointSameEl = 0b110101, + BkptAarch32 = 0b111000, + VectorCatchAarch32 = 0b111010, + BrkAarch64 = 0b111100 + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs b/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs new file mode 100644 index 000000000..6a692e740 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + public class DummyDiskCacheLoadState : IDiskCacheLoadState + { +#pragma warning disable CS0067 + /// + public event Action StateChanged; +#pragma warning restore CS0067 + + /// + public void Cancel() + { + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs b/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs new file mode 100644 index 000000000..78f4c4640 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs @@ -0,0 +1,129 @@ +using Ryujinx.Cpu.AppleHv.Arm; +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvAddressSpace : IDisposable + { + private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39)); + private const ulong KernelRegionCodeOffset = 0UL; + private const ulong KernelRegionCodeSize = 0x2000UL; + private const ulong KernelRegionTlbiEretOffset = KernelRegionCodeOffset + 0x1000UL; + private const ulong KernelRegionEretOffset = KernelRegionTlbiEretOffset + 4UL; + + public const ulong KernelRegionEretAddress = KernelRegionBase + KernelRegionEretOffset; + public const ulong KernelRegionTlbiEretAddress = KernelRegionBase + KernelRegionTlbiEretOffset; + + private const ulong AllocationGranule = 1UL << 14; + + private readonly ulong _asBase; + private readonly ulong _asSize; + private readonly ulong _backingSize; + + private readonly HvAddressSpaceRange _userRange; + private readonly HvAddressSpaceRange _kernelRange; + + private MemoryBlock _kernelCodeBlock; + + public HvAddressSpace(MemoryBlock backingMemory, ulong asSize) + { + (_asBase, var ipaAllocator) = HvVm.CreateAddressSpace(backingMemory); + _asSize = asSize; + _backingSize = backingMemory.Size; + + _userRange = new HvAddressSpaceRange(ipaAllocator); + _kernelRange = new HvAddressSpaceRange(ipaAllocator); + + _kernelCodeBlock = new MemoryBlock(AllocationGranule); + + InitializeKernelCode(ipaAllocator); + } + + private void InitializeKernelCode(HvIpaAllocator ipaAllocator) + { + // Write exception handlers. + for (ulong offset = 0; offset < 0x800; offset += 0x80) + { + // Offsets: + // 0x0: Synchronous + // 0x80: IRQ + // 0x100: FIQ + // 0x180: SError + _kernelCodeBlock.Write(KernelRegionCodeOffset + offset, 0xD41FFFE2u); // HVC #0xFFFF + _kernelCodeBlock.Write(KernelRegionCodeOffset + offset + 4, 0xD69F03E0u); // ERET + } + + _kernelCodeBlock.Write(KernelRegionTlbiEretOffset, 0xD508831Fu); // TLBI VMALLE1IS + _kernelCodeBlock.Write(KernelRegionEretOffset, 0xD69F03E0u); // ERET + + ulong kernelCodePa = ipaAllocator.Allocate(AllocationGranule); + HvApi.hv_vm_map((ulong)_kernelCodeBlock.Pointer, kernelCodePa, AllocationGranule, hv_memory_flags_t.HV_MEMORY_READ | hv_memory_flags_t.HV_MEMORY_EXEC).ThrowOnError(); + + _kernelRange.Map(KernelRegionCodeOffset, kernelCodePa, KernelRegionCodeSize, ApFlags.UserNoneKernelReadExecute); + } + + public void InitializeMmu(ulong vcpu) + { + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_VBAR_EL1, KernelRegionBase + KernelRegionCodeOffset); + + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TTBR0_EL1, _userRange.GetIpaBase()); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TTBR1_EL1, _kernelRange.GetIpaBase()); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_MAIR_EL1, 0xffUL); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TCR_EL1, 0x00000011B5193519UL); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_SCTLR_EL1, 0x0000000034D5D925UL); + } + + public bool GetAndClearUserTlbInvalidationPending() + { + return _userRange.GetAndClearTlbInvalidationPending(); + } + + public void MapUser(ulong va, ulong pa, ulong size, MemoryPermission permission) + { + pa += _asBase; + + lock (_userRange) + { + _userRange.Map(va, pa, size, GetApFlags(permission)); + } + } + + public void UnmapUser(ulong va, ulong size) + { + lock (_userRange) + { + _userRange.Unmap(va, size); + } + } + + public void ReprotectUser(ulong va, ulong size, MemoryPermission permission) + { + lock (_userRange) + { + _userRange.Reprotect(va, size, GetApFlags(permission)); + } + } + + private static ApFlags GetApFlags(MemoryPermission permission) + { + return permission switch + { + MemoryPermission.None => ApFlags.UserNoneKernelRead, + MemoryPermission.Execute => ApFlags.UserExecuteKernelRead, + MemoryPermission.Read => ApFlags.UserReadKernelRead, + MemoryPermission.ReadAndWrite => ApFlags.UserReadWriteKernelReadWrite, + MemoryPermission.ReadAndExecute => ApFlags.UserReadExecuteKernelRead, + MemoryPermission.ReadWriteExecute => ApFlags.UserReadWriteExecuteKernelReadWrite, + _ => throw new ArgumentException($"Permission \"{permission}\" is invalid.") + }; + } + + public void Dispose() + { + _userRange.Dispose(); + _kernelRange.Dispose(); + HvVm.DestroyAddressSpace(_asBase, _backingSize); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs b/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs new file mode 100644 index 000000000..ca30bb681 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs @@ -0,0 +1,370 @@ +using Ryujinx.Cpu.AppleHv.Arm; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvAddressSpaceRange : IDisposable + { + private const ulong AllocationGranule = 1UL << 14; + + private const ulong AttributesMask = (0x3ffUL << 2) | (0x3fffUL << 50); + + private const ulong BaseAttributes = (1UL << 10) | (3UL << 8); // Access flag set, inner shareable. + + private const int LevelBits = 9; + private const int LevelCount = 1 << LevelBits; + private const int LevelMask = LevelCount - 1; + private const int PageBits = 12; + private const int PageSize = 1 << PageBits; + private const int PageMask = PageSize - 1; + private const int AllLevelsMask = PageMask | (LevelMask << PageBits) | (LevelMask << (PageBits + LevelBits)); + + private class PtLevel + { + public ulong Address => Allocation.Ipa + Allocation.Offset; + public int EntriesCount; + public readonly HvMemoryBlockAllocation Allocation; + public readonly PtLevel[] Next; + + public PtLevel(HvMemoryBlockAllocator blockAllocator, int count, bool hasNext) + { + ulong size = (ulong)count * sizeof(ulong); + Allocation = blockAllocator.Allocate(size, PageSize); + + AsSpan().Fill(0UL); + + if (hasNext) + { + Next = new PtLevel[count]; + } + } + + public unsafe Span AsSpan() + { + return MemoryMarshal.Cast(Allocation.Memory.GetSpan(Allocation.Offset, (int)Allocation.Size)); + } + } + + private PtLevel _level0; + + private int _tlbInvalidationPending; + + private readonly HvIpaAllocator _ipaAllocator; + private readonly HvMemoryBlockAllocator _blockAllocator; + + public HvAddressSpaceRange(HvIpaAllocator ipaAllocator) + { + _ipaAllocator = ipaAllocator; + _blockAllocator = new HvMemoryBlockAllocator(ipaAllocator, (int)AllocationGranule); + } + + public ulong GetIpaBase() + { + return EnsureLevel0().Address; + } + + public bool GetAndClearTlbInvalidationPending() + { + return Interlocked.Exchange(ref _tlbInvalidationPending, 0) != 0; + } + + public void Map(ulong va, ulong pa, ulong size, ApFlags accessPermission) + { + MapImpl(va, pa, size, (ulong)accessPermission | BaseAttributes); + } + + public void Unmap(ulong va, ulong size) + { + UnmapImpl(EnsureLevel0(), 0, va, size); + Interlocked.Exchange(ref _tlbInvalidationPending, 1); + } + + public void Reprotect(ulong va, ulong size, ApFlags accessPermission) + { + UpdateAttributes(va, size, (ulong)accessPermission | BaseAttributes); + } + + private void MapImpl(ulong va, ulong pa, ulong size, ulong attr) + { + PtLevel level0 = EnsureLevel0(); + + ulong endVa = va + size; + + while (va < endVa) + { + (ulong mapSize, int depth) = GetMapSizeAndDepth(va, pa, endVa); + + PtLevel currentLevel = level0; + + for (int i = 0; i < depth; i++) + { + int l = (int)(va >> (PageBits + (2 - i) * LevelBits)) & LevelMask; + EnsureTable(currentLevel, l, i == 0); + currentLevel = currentLevel.Next[l]; + } + + (ulong blockSize, int blockShift) = GetBlockSizeAndShift(depth); + + for (ulong i = 0; i < mapSize; i += blockSize) + { + if ((va >> blockShift) << blockShift != va || + (pa >> blockShift) << blockShift != pa) + { + Debug.Fail($"Block size 0x{blockSize:X} (log2: {blockShift}) is invalid for VA 0x{va:X} or PA 0x{pa:X}."); + } + + WriteBlock(currentLevel, (int)(va >> blockShift) & LevelMask, depth, pa, attr); + + va += blockSize; + pa += blockSize; + } + } + } + + private void UnmapImpl(PtLevel level, int depth, ulong va, ulong size) + { + ulong endVa = (va + size + PageMask) & ~((ulong)PageMask); + va &= ~((ulong)PageMask); + + (ulong blockSize, int blockShift) = GetBlockSizeAndShift(depth); + + while (va < endVa) + { + ulong nextEntryVa = GetNextAddress(va, blockSize); + ulong chunckSize = Math.Min(endVa - va, nextEntryVa - va); + + int l = (int)(va >> (PageBits + (2 - depth) * LevelBits)) & LevelMask; + + PtLevel nextTable = level.Next != null ? level.Next[l] : null; + + if (nextTable != null) + { + // Entry is a table, visit it and update attributes as required. + UnmapImpl(nextTable, depth + 1, va, chunckSize); + } + else if (chunckSize != blockSize) + { + // Entry is a block but is not aligned, we need to turn it into a table. + ref ulong pte = ref level.AsSpan()[l]; + nextTable = CreateTable(pte, depth + 1); + level.Next[l] = nextTable; + + // Now that we have a table, we can handle it like the first case. + UnmapImpl(nextTable, depth + 1, va, chunckSize); + + // Update PTE to point to the new table. + pte = (nextTable.Address & ~(ulong)PageMask) | 3UL; + } + + // If entry is a block, or if entry is a table but it is empty, we can remove it. + if (nextTable == null || nextTable.EntriesCount == 0) + { + // Entry is a block and is fully aligned, so we can just set it to 0. + if (nextTable != null) + { + nextTable.Allocation.Dispose(); + level.Next[l] = null; + } + + level.AsSpan()[l] = 0UL; + level.EntriesCount--; + ValidateEntriesCount(level.EntriesCount); + } + + va += chunckSize; + } + } + + private void UpdateAttributes(ulong va, ulong size, ulong newAttr) + { + UpdateAttributes(EnsureLevel0(), 0, va, size, newAttr); + + Interlocked.Exchange(ref _tlbInvalidationPending, 1); + } + + private void UpdateAttributes(PtLevel level, int depth, ulong va, ulong size, ulong newAttr) + { + ulong endVa = (va + size + PageSize - 1) & ~((ulong)PageSize - 1); + va &= ~((ulong)PageSize - 1); + + (ulong blockSize, int blockShift) = GetBlockSizeAndShift(depth); + + while (va < endVa) + { + ulong nextEntryVa = GetNextAddress(va, blockSize); + ulong chunckSize = Math.Min(endVa - va, nextEntryVa - va); + + int l = (int)(va >> (PageBits + (2 - depth) * LevelBits)) & LevelMask; + + ref ulong pte = ref level.AsSpan()[l]; + + // First check if the region is mapped. + if ((pte & 3) != 0) + { + PtLevel nextTable = level.Next != null ? level.Next[l] : null; + + if (nextTable != null) + { + // Entry is a table, visit it and update attributes as required. + UpdateAttributes(nextTable, depth + 1, va, chunckSize, newAttr); + } + else if (chunckSize != blockSize) + { + // Entry is a block but is not aligned, we need to turn it into a table. + nextTable = CreateTable(pte, depth + 1); + level.Next[l] = nextTable; + + // Now that we have a table, we can handle it like the first case. + UpdateAttributes(nextTable, depth + 1, va, chunckSize, newAttr); + + // Update PTE to point to the new table. + pte = (nextTable.Address & ~(ulong)PageMask) | 3UL; + } + else + { + // Entry is a block and is fully aligned, so we can just update the attributes. + // Update PTE with the new attributes. + pte = (pte & ~AttributesMask) | newAttr; + } + } + + va += chunckSize; + } + } + + private PtLevel CreateTable(ulong pte, int depth) + { + pte &= ~3UL; + pte |= (depth == 2 ? 3UL : 1UL); + + PtLevel level = new PtLevel(_blockAllocator, LevelCount, depth < 2); + Span currentLevel = level.AsSpan(); + + (ulong blockSize, int blockShift) = GetBlockSizeAndShift(depth); + + // Fill in the blocks. + for (int i = 0; i < LevelCount; i++) + { + ulong offset = (ulong)i << blockShift; + currentLevel[i] = pte + offset; + } + + level.EntriesCount = LevelCount; + + return level; + } + + private static (ulong, int) GetBlockSizeAndShift(int depth) + { + int blockShift = PageBits + (2 - depth) * LevelBits; + ulong blockSize = 1UL << blockShift; + + return (blockSize, blockShift); + } + + private static (ulong, int) GetMapSizeAndDepth(ulong va, ulong pa, ulong endVa) + { + // Both virtual and physical addresses must be aligned to the block size. + ulong combinedAddress = va | pa; + + ulong l0Alignment = 1UL << (PageBits + LevelBits * 2); + ulong l1Alignment = 1UL << (PageBits + LevelBits); + + if ((combinedAddress & (l0Alignment - 1)) == 0 && AlignDown(endVa, l0Alignment) > va) + { + return (AlignDown(endVa, l0Alignment) - va, 0); + } + else if ((combinedAddress & (l1Alignment - 1)) == 0 && AlignDown(endVa, l1Alignment) > va) + { + ulong nextOrderVa = GetNextAddress(va, l0Alignment); + + if (nextOrderVa <= endVa) + { + return (nextOrderVa - va, 1); + } + else + { + return (AlignDown(endVa, l1Alignment) - va, 1); + } + } + else + { + ulong nextOrderVa = GetNextAddress(va, l1Alignment); + + if (nextOrderVa <= endVa) + { + return (nextOrderVa - va, 2); + } + else + { + return (endVa - va, 2); + } + } + } + + private static ulong AlignDown(ulong va, ulong alignment) + { + return va & ~(alignment - 1); + } + + private static ulong GetNextAddress(ulong va, ulong alignment) + { + return (va + alignment) & ~(alignment - 1); + } + + private PtLevel EnsureLevel0() + { + PtLevel level0 = _level0; + + if (level0 == null) + { + level0 = new PtLevel(_blockAllocator, LevelCount, true); + _level0 = level0; + } + + return level0; + } + + private void EnsureTable(PtLevel level, int index, bool hasNext) + { + Span currentTable = level.AsSpan(); + + if ((currentTable[index] & 1) == 0) + { + PtLevel nextLevel = new PtLevel(_blockAllocator, LevelCount, hasNext); + + currentTable[index] = (nextLevel.Address & ~(ulong)PageMask) | 3UL; + level.Next[index] = nextLevel; + level.EntriesCount++; + ValidateEntriesCount(level.EntriesCount); + } + else if (level.Next[index] == null) + { + Debug.Fail($"Index {index} is block, expected a table."); + } + } + + private void WriteBlock(PtLevel level, int index, int depth, ulong pa, ulong attr) + { + Span currentTable = level.AsSpan(); + + currentTable[index] = (pa & ~((ulong)AllLevelsMask >> (depth * LevelBits))) | (depth == 2 ? 3UL : 1UL) | attr; + + level.EntriesCount++; + ValidateEntriesCount(level.EntriesCount); + } + + private static void ValidateEntriesCount(int count) + { + Debug.Assert(count >= 0 && count <= LevelCount, $"Entries count {count} is invalid."); + } + + public void Dispose() + { + _blockAllocator.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvApi.cs b/Ryujinx.Cpu/AppleHv/HvApi.cs new file mode 100644 index 000000000..d7628bb52 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvApi.cs @@ -0,0 +1,320 @@ +using ARMeilleure.State; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Cpu.AppleHv +{ + struct hv_vcpu_exit_exception_t + { +#pragma warning disable CS0649 + public ulong syndrome; + public ulong virtual_address; + public ulong physical_address; +#pragma warning restore CS0649 + } + + struct hv_vcpu_exit_t + { +#pragma warning disable CS0649 + public uint reason; + public hv_vcpu_exit_exception_t exception; +#pragma warning restore CS0649 + } + + enum hv_reg_t : uint + { + HV_REG_X0, + HV_REG_X1, + HV_REG_X2, + HV_REG_X3, + HV_REG_X4, + HV_REG_X5, + HV_REG_X6, + HV_REG_X7, + HV_REG_X8, + HV_REG_X9, + HV_REG_X10, + HV_REG_X11, + HV_REG_X12, + HV_REG_X13, + HV_REG_X14, + HV_REG_X15, + HV_REG_X16, + HV_REG_X17, + HV_REG_X18, + HV_REG_X19, + HV_REG_X20, + HV_REG_X21, + HV_REG_X22, + HV_REG_X23, + HV_REG_X24, + HV_REG_X25, + HV_REG_X26, + HV_REG_X27, + HV_REG_X28, + HV_REG_X29, + HV_REG_FP = HV_REG_X29, + HV_REG_X30, + HV_REG_LR = HV_REG_X30, + HV_REG_PC, + HV_REG_FPCR, + HV_REG_FPSR, + HV_REG_CPSR, + } + + enum hv_simd_fp_reg_t : uint + { + HV_SIMD_FP_REG_Q0, + HV_SIMD_FP_REG_Q1, + HV_SIMD_FP_REG_Q2, + HV_SIMD_FP_REG_Q3, + HV_SIMD_FP_REG_Q4, + HV_SIMD_FP_REG_Q5, + HV_SIMD_FP_REG_Q6, + HV_SIMD_FP_REG_Q7, + HV_SIMD_FP_REG_Q8, + HV_SIMD_FP_REG_Q9, + HV_SIMD_FP_REG_Q10, + HV_SIMD_FP_REG_Q11, + HV_SIMD_FP_REG_Q12, + HV_SIMD_FP_REG_Q13, + HV_SIMD_FP_REG_Q14, + HV_SIMD_FP_REG_Q15, + HV_SIMD_FP_REG_Q16, + HV_SIMD_FP_REG_Q17, + HV_SIMD_FP_REG_Q18, + HV_SIMD_FP_REG_Q19, + HV_SIMD_FP_REG_Q20, + HV_SIMD_FP_REG_Q21, + HV_SIMD_FP_REG_Q22, + HV_SIMD_FP_REG_Q23, + HV_SIMD_FP_REG_Q24, + HV_SIMD_FP_REG_Q25, + HV_SIMD_FP_REG_Q26, + HV_SIMD_FP_REG_Q27, + HV_SIMD_FP_REG_Q28, + HV_SIMD_FP_REG_Q29, + HV_SIMD_FP_REG_Q30, + HV_SIMD_FP_REG_Q31, + } + + enum hv_sys_reg_t : ushort + { + HV_SYS_REG_DBGBVR0_EL1 = 0x8004, + HV_SYS_REG_DBGBCR0_EL1 = 0x8005, + HV_SYS_REG_DBGWVR0_EL1 = 0x8006, + HV_SYS_REG_DBGWCR0_EL1 = 0x8007, + HV_SYS_REG_DBGBVR1_EL1 = 0x800c, + HV_SYS_REG_DBGBCR1_EL1 = 0x800d, + HV_SYS_REG_DBGWVR1_EL1 = 0x800e, + HV_SYS_REG_DBGWCR1_EL1 = 0x800f, + HV_SYS_REG_MDCCINT_EL1 = 0x8010, + HV_SYS_REG_MDSCR_EL1 = 0x8012, + HV_SYS_REG_DBGBVR2_EL1 = 0x8014, + HV_SYS_REG_DBGBCR2_EL1 = 0x8015, + HV_SYS_REG_DBGWVR2_EL1 = 0x8016, + HV_SYS_REG_DBGWCR2_EL1 = 0x8017, + HV_SYS_REG_DBGBVR3_EL1 = 0x801c, + HV_SYS_REG_DBGBCR3_EL1 = 0x801d, + HV_SYS_REG_DBGWVR3_EL1 = 0x801e, + HV_SYS_REG_DBGWCR3_EL1 = 0x801f, + HV_SYS_REG_DBGBVR4_EL1 = 0x8024, + HV_SYS_REG_DBGBCR4_EL1 = 0x8025, + HV_SYS_REG_DBGWVR4_EL1 = 0x8026, + HV_SYS_REG_DBGWCR4_EL1 = 0x8027, + HV_SYS_REG_DBGBVR5_EL1 = 0x802c, + HV_SYS_REG_DBGBCR5_EL1 = 0x802d, + HV_SYS_REG_DBGWVR5_EL1 = 0x802e, + HV_SYS_REG_DBGWCR5_EL1 = 0x802f, + HV_SYS_REG_DBGBVR6_EL1 = 0x8034, + HV_SYS_REG_DBGBCR6_EL1 = 0x8035, + HV_SYS_REG_DBGWVR6_EL1 = 0x8036, + HV_SYS_REG_DBGWCR6_EL1 = 0x8037, + HV_SYS_REG_DBGBVR7_EL1 = 0x803c, + HV_SYS_REG_DBGBCR7_EL1 = 0x803d, + HV_SYS_REG_DBGWVR7_EL1 = 0x803e, + HV_SYS_REG_DBGWCR7_EL1 = 0x803f, + HV_SYS_REG_DBGBVR8_EL1 = 0x8044, + HV_SYS_REG_DBGBCR8_EL1 = 0x8045, + HV_SYS_REG_DBGWVR8_EL1 = 0x8046, + HV_SYS_REG_DBGWCR8_EL1 = 0x8047, + HV_SYS_REG_DBGBVR9_EL1 = 0x804c, + HV_SYS_REG_DBGBCR9_EL1 = 0x804d, + HV_SYS_REG_DBGWVR9_EL1 = 0x804e, + HV_SYS_REG_DBGWCR9_EL1 = 0x804f, + HV_SYS_REG_DBGBVR10_EL1 = 0x8054, + HV_SYS_REG_DBGBCR10_EL1 = 0x8055, + HV_SYS_REG_DBGWVR10_EL1 = 0x8056, + HV_SYS_REG_DBGWCR10_EL1 = 0x8057, + HV_SYS_REG_DBGBVR11_EL1 = 0x805c, + HV_SYS_REG_DBGBCR11_EL1 = 0x805d, + HV_SYS_REG_DBGWVR11_EL1 = 0x805e, + HV_SYS_REG_DBGWCR11_EL1 = 0x805f, + HV_SYS_REG_DBGBVR12_EL1 = 0x8064, + HV_SYS_REG_DBGBCR12_EL1 = 0x8065, + HV_SYS_REG_DBGWVR12_EL1 = 0x8066, + HV_SYS_REG_DBGWCR12_EL1 = 0x8067, + HV_SYS_REG_DBGBVR13_EL1 = 0x806c, + HV_SYS_REG_DBGBCR13_EL1 = 0x806d, + HV_SYS_REG_DBGWVR13_EL1 = 0x806e, + HV_SYS_REG_DBGWCR13_EL1 = 0x806f, + HV_SYS_REG_DBGBVR14_EL1 = 0x8074, + HV_SYS_REG_DBGBCR14_EL1 = 0x8075, + HV_SYS_REG_DBGWVR14_EL1 = 0x8076, + HV_SYS_REG_DBGWCR14_EL1 = 0x8077, + HV_SYS_REG_DBGBVR15_EL1 = 0x807c, + HV_SYS_REG_DBGBCR15_EL1 = 0x807d, + HV_SYS_REG_DBGWVR15_EL1 = 0x807e, + HV_SYS_REG_DBGWCR15_EL1 = 0x807f, + HV_SYS_REG_MIDR_EL1 = 0xc000, + HV_SYS_REG_MPIDR_EL1 = 0xc005, + HV_SYS_REG_ID_AA64PFR0_EL1 = 0xc020, + HV_SYS_REG_ID_AA64PFR1_EL1 = 0xc021, + HV_SYS_REG_ID_AA64DFR0_EL1 = 0xc028, + HV_SYS_REG_ID_AA64DFR1_EL1 = 0xc029, + HV_SYS_REG_ID_AA64ISAR0_EL1 = 0xc030, + HV_SYS_REG_ID_AA64ISAR1_EL1 = 0xc031, + HV_SYS_REG_ID_AA64MMFR0_EL1 = 0xc038, + HV_SYS_REG_ID_AA64MMFR1_EL1 = 0xc039, + HV_SYS_REG_ID_AA64MMFR2_EL1 = 0xc03a, + HV_SYS_REG_SCTLR_EL1 = 0xc080, + HV_SYS_REG_CPACR_EL1 = 0xc082, + HV_SYS_REG_TTBR0_EL1 = 0xc100, + HV_SYS_REG_TTBR1_EL1 = 0xc101, + HV_SYS_REG_TCR_EL1 = 0xc102, + HV_SYS_REG_APIAKEYLO_EL1 = 0xc108, + HV_SYS_REG_APIAKEYHI_EL1 = 0xc109, + HV_SYS_REG_APIBKEYLO_EL1 = 0xc10a, + HV_SYS_REG_APIBKEYHI_EL1 = 0xc10b, + HV_SYS_REG_APDAKEYLO_EL1 = 0xc110, + HV_SYS_REG_APDAKEYHI_EL1 = 0xc111, + HV_SYS_REG_APDBKEYLO_EL1 = 0xc112, + HV_SYS_REG_APDBKEYHI_EL1 = 0xc113, + HV_SYS_REG_APGAKEYLO_EL1 = 0xc118, + HV_SYS_REG_APGAKEYHI_EL1 = 0xc119, + HV_SYS_REG_SPSR_EL1 = 0xc200, + HV_SYS_REG_ELR_EL1 = 0xc201, + HV_SYS_REG_SP_EL0 = 0xc208, + HV_SYS_REG_AFSR0_EL1 = 0xc288, + HV_SYS_REG_AFSR1_EL1 = 0xc289, + HV_SYS_REG_ESR_EL1 = 0xc290, + HV_SYS_REG_FAR_EL1 = 0xc300, + HV_SYS_REG_PAR_EL1 = 0xc3a0, + HV_SYS_REG_MAIR_EL1 = 0xc510, + HV_SYS_REG_AMAIR_EL1 = 0xc518, + HV_SYS_REG_VBAR_EL1 = 0xc600, + HV_SYS_REG_CONTEXTIDR_EL1 = 0xc681, + HV_SYS_REG_TPIDR_EL1 = 0xc684, + HV_SYS_REG_CNTKCTL_EL1 = 0xc708, + HV_SYS_REG_CSSELR_EL1 = 0xd000, + HV_SYS_REG_TPIDR_EL0 = 0xde82, + HV_SYS_REG_TPIDRRO_EL0 = 0xde83, + HV_SYS_REG_CNTV_CTL_EL0 = 0xdf19, + HV_SYS_REG_CNTV_CVAL_EL0 = 0xdf1a, + HV_SYS_REG_SP_EL1 = 0xe208, + } + + enum hv_memory_flags_t : ulong + { + HV_MEMORY_READ = 1UL << 0, + HV_MEMORY_WRITE = 1UL << 1, + HV_MEMORY_EXEC = 1UL << 2 + } + + enum hv_result_t : uint + { + HV_SUCCESS = 0, + HV_ERROR = 0xfae94001, + HV_BUSY = 0xfae94002, + HV_BAD_ARGUMENT = 0xfae94003, + HV_NO_RESOURCES = 0xfae94005, + HV_NO_DEVICE = 0xfae94006, + HV_DENIED = 0xfae94007, + HV_UNSUPPORTED = 0xfae9400f + } + + enum hv_interrupt_type_t : uint + { + HV_INTERRUPT_TYPE_IRQ, + HV_INTERRUPT_TYPE_FIQ + } + + struct hv_simd_fp_uchar16_t + { + public ulong Low; + public ulong High; + } + + static class HvResultExtensions + { + public static void ThrowOnError(this hv_result_t result) + { + if (result != hv_result_t.HV_SUCCESS) + { + throw new Exception($"Unexpected result \"{result}\"."); + } + } + } + + static partial class HvApi + { + public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor"; + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vm_get_max_vcpu_count(out uint max_vcpu_count); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vm_create(IntPtr config); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vm_destroy(); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vm_map(ulong addr, ulong ipa, ulong size, hv_memory_flags_t flags); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vm_unmap(ulong ipa, ulong size); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vm_protect(ulong ipa, ulong size, hv_memory_flags_t flags); + + [LibraryImport(LibraryName, SetLastError = true)] + public unsafe static partial hv_result_t hv_vcpu_create(out ulong vcpu, ref hv_vcpu_exit_t* exit, IntPtr config); + + [LibraryImport(LibraryName, SetLastError = true)] + public unsafe static partial hv_result_t hv_vcpu_destroy(ulong vcpu); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_run(ulong vcpu); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpus_exit(ref ulong vcpus, uint vcpu_count); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_set_vtimer_mask(ulong vcpu, [MarshalAs(UnmanagedType.Bool)] bool vtimer_is_masked); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_get_reg(ulong vcpu, hv_reg_t reg, out ulong value); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_set_reg(ulong vcpu, hv_reg_t reg, ulong value); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_get_simd_fp_reg(ulong vcpu, hv_simd_fp_reg_t reg, out hv_simd_fp_uchar16_t value); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_set_simd_fp_reg(ulong vcpu, hv_simd_fp_reg_t reg, hv_simd_fp_uchar16_t value); // DO NOT USE DIRECTLY! + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_get_sys_reg(ulong vcpu, hv_sys_reg_t reg, out ulong value); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_set_sys_reg(ulong vcpu, hv_sys_reg_t reg, ulong value); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_get_pending_interrupt(ulong vcpu, hv_interrupt_type_t type, [MarshalAs(UnmanagedType.Bool)] out bool pending); + + [LibraryImport(LibraryName, SetLastError = true)] + public static partial hv_result_t hv_vcpu_set_pending_interrupt(ulong vcpu, hv_interrupt_type_t type, [MarshalAs(UnmanagedType.Bool)] bool pending); + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvCpuContext.cs b/Ryujinx.Cpu/AppleHv/HvCpuContext.cs new file mode 100644 index 000000000..de782d541 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvCpuContext.cs @@ -0,0 +1,47 @@ +using ARMeilleure.Memory; +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvCpuContext : ICpuContext + { + private readonly ITickSource _tickSource; + private readonly HvMemoryManager _memoryManager; + + public HvCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) + { + _tickSource = tickSource; + _memoryManager = (HvMemoryManager)memory; + } + + private void UnmapHandler(ulong address, ulong size) + { + } + + /// + public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks) + { + return new HvExecutionContext(_tickSource, exceptionCallbacks); + } + + /// + public void Execute(IExecutionContext context, ulong address) + { + ((HvExecutionContext)context).Execute(_memoryManager, address); + } + + /// + public void InvalidateCacheRegion(ulong address, ulong size) + { + } + + public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + { + return new DummyDiskCacheLoadState(); + } + + public void PrepareCodeRange(ulong address, ulong size) + { + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvEngine.cs b/Ryujinx.Cpu/AppleHv/HvEngine.cs new file mode 100644 index 000000000..7ad99cb9c --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvEngine.cs @@ -0,0 +1,20 @@ +using ARMeilleure.Memory; + +namespace Ryujinx.Cpu.AppleHv +{ + public class HvEngine : ICpuEngine + { + private readonly ITickSource _tickSource; + + public HvEngine(ITickSource tickSource) + { + _tickSource = tickSource; + } + + /// + public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit) + { + return new HvCpuContext(_tickSource, memoryManager, for64Bit); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs new file mode 100644 index 000000000..dc1f6f6da --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs @@ -0,0 +1,284 @@ +using ARMeilleure.State; +using Ryujinx.Cpu.AppleHv.Arm; +using Ryujinx.Memory.Tracking; +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvExecutionContext : IExecutionContext + { + /// + public ulong Pc => _impl.ElrEl1; + + /// + public long TpidrEl0 + { + get => _impl.TpidrEl0; + set => _impl.TpidrEl0 = value; + } + + /// + public long TpidrroEl0 + { + get => _impl.TpidrroEl0; + set => _impl.TpidrroEl0 = value; + } + + /// + public uint Pstate + { + get => _impl.Pstate; + set => _impl.Pstate = value; + } + + /// + public uint Fpcr + { + get => _impl.Fpcr; + set => _impl.Fpcr = value; + } + + /// + public uint Fpsr + { + get => _impl.Fpsr; + set => _impl.Fpsr = value; + } + + /// + public bool IsAarch32 + { + get => false; + set + { + if (value) + { + throw new NotSupportedException(); + } + } + } + + /// + public bool Running { get; private set; } + + private readonly ICounter _counter; + private readonly IHvExecutionContext _shadowContext; + private IHvExecutionContext _impl; + + private readonly ExceptionCallbacks _exceptionCallbacks; + + public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks) + { + _counter = counter; + _shadowContext = new HvExecutionContextShadow(); + _impl = _shadowContext; + _exceptionCallbacks = exceptionCallbacks; + Running = true; + } + + /// + public ulong GetX(int index) => _impl.GetX(index); + + /// + public void SetX(int index, ulong value) => _impl.SetX(index, value); + + /// + public V128 GetV(int index) => _impl.GetV(index); + + /// + public void SetV(int index, V128 value) => _impl.SetV(index, value); + + private void InterruptHandler() + { + _exceptionCallbacks.InterruptCallback?.Invoke(this); + } + + private void BreakHandler(ulong address, int imm) + { + _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm); + } + + private void SupervisorCallHandler(ulong address, int imm) + { + _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm); + } + + private void UndefinedHandler(ulong address, int opCode) + { + _exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode); + } + + /// + public void RequestInterrupt() + { + _impl.RequestInterrupt(); + } + + /// + public void StopRunning() + { + Running = false; + RequestInterrupt(); + } + + public unsafe void Execute(HvMemoryManager memoryManager, ulong address) + { + HvVcpu vcpu = HvVcpuPool.Instance.Create(memoryManager.AddressSpace, _shadowContext, SwapContext); + + HvApi.hv_vcpu_set_reg(vcpu.Handle, hv_reg_t.HV_REG_PC, address).ThrowOnError(); + + while (Running) + { + HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError(); + + uint reason = vcpu.ExitInfo->reason; + + if (reason == 1) + { + uint hvEsr = (uint)vcpu.ExitInfo->exception.syndrome; + ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26); + + if (hvEc != ExceptionClass.HvcAarch64) + { + throw new Exception($"Unhandled exception from guest kernel with ESR 0x{hvEsr:X} ({hvEc})."); + } + + address = SynchronousException(memoryManager, ref vcpu); + HvApi.hv_vcpu_set_reg(vcpu.Handle, hv_reg_t.HV_REG_PC, address).ThrowOnError(); + } + else if (reason == 0) + { + if (_impl.GetAndClearInterruptRequested()) + { + ReturnToPool(vcpu); + InterruptHandler(); + vcpu = RentFromPool(memoryManager.AddressSpace, vcpu); + } + } + else + { + throw new Exception($"Unhandled exit reason {reason}."); + } + } + + HvVcpuPool.Instance.Destroy(vcpu, SwapContext); + } + + private ulong SynchronousException(HvMemoryManager memoryManager, ref HvVcpu vcpu) + { + ulong vcpuHandle = vcpu.Handle; + + HvApi.hv_vcpu_get_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, out ulong elr).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_ESR_EL1, out ulong esr).ThrowOnError(); + + ExceptionClass ec = (ExceptionClass)((uint)esr >> 26); + + switch (ec) + { + case ExceptionClass.DataAbortLowerEl: + DataAbort(memoryManager.Tracking, vcpuHandle, (uint)esr); + break; + case ExceptionClass.TrappedMsrMrsSystem: + InstructionTrap((uint)esr); + HvApi.hv_vcpu_set_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, elr + 4UL).ThrowOnError(); + break; + case ExceptionClass.SvcAarch64: + ReturnToPool(vcpu); + ushort id = (ushort)esr; + SupervisorCallHandler(elr - 4UL, id); + vcpu = RentFromPool(memoryManager.AddressSpace, vcpu); + break; + default: + throw new Exception($"Unhandled guest exception {ec}."); + } + + // Make sure we will continue running at EL0. + if (memoryManager.AddressSpace.GetAndClearUserTlbInvalidationPending()) + { + // TODO: Invalidate only the range that was modified? + return HvAddressSpace.KernelRegionTlbiEretAddress; + } + else + { + return HvAddressSpace.KernelRegionEretAddress; + } + } + + private void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr) + { + bool write = (esr & (1u << 6)) != 0; + bool farValid = (esr & (1u << 10)) == 0; + int accessSizeLog2 = (int)((esr >> 22) & 3); + + if (farValid) + { + HvApi.hv_vcpu_get_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_FAR_EL1, out ulong far).ThrowOnError(); + + ulong size = 1UL << accessSizeLog2; + + if (!tracking.VirtualMemoryEvent(far, size, write)) + { + string rw = write ? "write" : "read"; + throw new Exception($"Unhandled invalid memory access at VA 0x{far:X} with size 0x{size:X} ({rw})."); + } + } + else + { + throw new Exception($"Unhandled invalid memory access at unknown VA with ESR 0x{esr:X}."); + } + } + + private void InstructionTrap(uint esr) + { + bool read = (esr & 1) != 0; + uint rt = (esr >> 5) & 0x1f; + + if (read) + { + // Op0 Op2 Op1 CRn 00000 CRm + switch ((esr >> 1) & 0x1ffe0f) + { + case 0b11_000_011_1110_00000_0000: // CNTFRQ_EL0 + WriteRt(rt, _counter.Frequency); + break; + case 0b11_001_011_1110_00000_0000: // CNTPCT_EL0 + WriteRt(rt, _counter.Counter); + break; + default: + throw new Exception($"Unhandled system register read with ESR 0x{esr:X}"); + } + } + else + { + throw new Exception($"Unhandled system register write with ESR 0x{esr:X}"); + } + } + + private void WriteRt(uint rt, ulong value) + { + if (rt < 31) + { + SetX((int)rt, value); + } + } + + private void ReturnToPool(HvVcpu vcpu) + { + HvVcpuPool.Instance.Return(vcpu, SwapContext); + } + + private HvVcpu RentFromPool(HvAddressSpace addressSpace, HvVcpu vcpu) + { + return HvVcpuPool.Instance.Rent(addressSpace, _shadowContext, vcpu, SwapContext); + } + + private void SwapContext(IHvExecutionContext newContext) + { + _impl = newContext; + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs b/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs new file mode 100644 index 000000000..c088ebdcf --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs @@ -0,0 +1,59 @@ +using ARMeilleure.State; + +namespace Ryujinx.Cpu.AppleHv +{ + unsafe class HvExecutionContextShadow : IHvExecutionContext + { + public ulong Pc { get; set; } + public ulong ElrEl1 { get; set; } + public ulong EsrEl1 { get; set; } + + public long TpidrEl0 { get; set; } + public long TpidrroEl0 { get; set; } + + public uint Pstate { get; set; } + + public uint Fpcr { get; set; } + public uint Fpsr { get; set; } + + public bool IsAarch32 { get; set; } + + private readonly ulong[] _x; + private readonly V128[] _v; + + public HvExecutionContextShadow() + { + _x = new ulong[32]; + _v = new V128[32]; + } + + public ulong GetX(int index) + { + return _x[index]; + } + + public void SetX(int index, ulong value) + { + _x[index] = value; + } + + public V128 GetV(int index) + { + return _v[index]; + } + + public void SetV(int index, V128 value) + { + _v[index] = value; + } + + public void RequestInterrupt() + { + } + + public bool GetAndClearInterruptRequested() + { + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs new file mode 100644 index 000000000..4f6ebefa7 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs @@ -0,0 +1,196 @@ +using ARMeilleure.State; +using Ryujinx.Memory; +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvExecutionContextVcpu : IHvExecutionContext + { + private static MemoryBlock _setSimdFpRegFuncMem; + private delegate hv_result_t SetSimdFpReg(ulong vcpu, hv_simd_fp_reg_t reg, in V128 value, IntPtr funcPtr); + private static SetSimdFpReg _setSimdFpReg; + private static IntPtr _setSimdFpRegNativePtr; + + static HvExecutionContextVcpu() + { + // .NET does not support passing vectors by value, so we need to pass a pointer and use a native + // function to load the value into a vector register. + _setSimdFpRegFuncMem = new MemoryBlock(MemoryBlock.GetPageSize()); + _setSimdFpRegFuncMem.Write(0, 0x3DC00040u); // LDR Q0, [X2] + _setSimdFpRegFuncMem.Write(4, 0xD61F0060u); // BR X3 + _setSimdFpRegFuncMem.Reprotect(0, _setSimdFpRegFuncMem.Size, MemoryPermission.ReadAndExecute); + + _setSimdFpReg = Marshal.GetDelegateForFunctionPointer(_setSimdFpRegFuncMem.Pointer); + + if (NativeLibrary.TryLoad(HvApi.LibraryName, out IntPtr hvLibHandle)) + { + _setSimdFpRegNativePtr = NativeLibrary.GetExport(hvLibHandle, nameof(HvApi.hv_vcpu_set_simd_fp_reg)); + } + } + + public ulong Pc + { + get + { + HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_PC, out ulong pc).ThrowOnError(); + return pc; + } + set + { + HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_PC, value).ThrowOnError(); + } + } + + public ulong ElrEl1 + { + get + { + HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, out ulong elr).ThrowOnError(); + return elr; + } + set + { + HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, value).ThrowOnError(); + } + } + + public ulong EsrEl1 + { + get + { + HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ESR_EL1, out ulong esr).ThrowOnError(); + return esr; + } + set + { + HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ESR_EL1, value).ThrowOnError(); + } + } + + public long TpidrEl0 + { + get + { + HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDR_EL0, out ulong tpidrEl0).ThrowOnError(); + return (long)tpidrEl0; + } + set + { + HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDR_EL0, (ulong)value).ThrowOnError(); + } + } + + public long TpidrroEl0 + { + get + { + HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDRRO_EL0, out ulong tpidrroEl0).ThrowOnError(); + return (long)tpidrroEl0; + } + set + { + HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDRRO_EL0, (ulong)value).ThrowOnError(); + } + } + + public uint Pstate + { + get + { + HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_CPSR, out ulong cpsr).ThrowOnError(); + return (uint)cpsr; + } + set + { + HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_CPSR, (ulong)value).ThrowOnError(); + } + } + + public uint Fpcr + { + get + { + HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_FPCR, out ulong fpcr).ThrowOnError(); + return (uint)fpcr; + } + set + { + HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_FPCR, (ulong)value).ThrowOnError(); + } + } + + public uint Fpsr + { + get + { + HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_FPSR, out ulong fpsr).ThrowOnError(); + return (uint)fpsr; + } + set + { + HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_FPSR, (ulong)value).ThrowOnError(); + } + } + + private ulong _vcpu; + private int _interruptRequested; + + public HvExecutionContextVcpu(ulong vcpu) + { + _vcpu = vcpu; + } + + public ulong GetX(int index) + { + if (index == 31) + { + HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_SP_EL0, out ulong value).ThrowOnError(); + return value; + } + else + { + HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_X0 + (uint)index, out ulong value).ThrowOnError(); + return value; + } + } + + public void SetX(int index, ulong value) + { + if (index == 31) + { + HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_SP_EL0, value).ThrowOnError(); + } + else + { + HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_X0 + (uint)index, value).ThrowOnError(); + } + } + + public V128 GetV(int index) + { + HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, hv_simd_fp_reg_t.HV_SIMD_FP_REG_Q0 + (uint)index, out hv_simd_fp_uchar16_t value).ThrowOnError(); + return new V128(value.Low, value.High); + } + + public void SetV(int index, V128 value) + { + _setSimdFpReg(_vcpu, hv_simd_fp_reg_t.HV_SIMD_FP_REG_Q0 + (uint)index, value, _setSimdFpRegNativePtr).ThrowOnError(); + } + + public void RequestInterrupt() + { + if (Interlocked.Exchange(ref _interruptRequested, 1) == 0) + { + ulong vcpu = _vcpu; + HvApi.hv_vcpus_exit(ref vcpu, 1); + } + } + + public bool GetAndClearInterruptRequested() + { + return Interlocked.Exchange(ref _interruptRequested, 0) != 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs b/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs new file mode 100644 index 000000000..7eefe130b --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs @@ -0,0 +1,34 @@ +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvIpaAllocator + { + private const ulong AllocationGranule = 1UL << 14; + private const ulong IpaRegionSize = 1UL << 35; + + private readonly PrivateMemoryAllocator.Block _block; + + public HvIpaAllocator() + { + _block = new PrivateMemoryAllocator.Block(null, IpaRegionSize); + } + + public ulong Allocate(ulong size, ulong alignment = AllocationGranule) + { + ulong offset = _block.Allocate(size, alignment); + + if (offset == PrivateMemoryAllocator.InvalidOffset) + { + throw new InvalidOperationException($"No enough free IPA memory to allocate 0x{size:X} bytes with alignment 0x{alignment:X}."); + } + + return offset; + } + + public void Free(ulong offset, ulong size) + { + _block.Free(offset, size); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs b/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs new file mode 100644 index 000000000..94289d1c3 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs @@ -0,0 +1,34 @@ +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + struct HvMemoryBlockAllocation : IDisposable + { + private readonly HvMemoryBlockAllocator _owner; + private readonly HvMemoryBlockAllocator.Block _block; + + public bool IsValid => _owner != null; + public MemoryBlock Memory => _block.Memory; + public ulong Ipa => _block.Ipa; + public ulong Offset { get; } + public ulong Size { get; } + + public HvMemoryBlockAllocation( + HvMemoryBlockAllocator owner, + HvMemoryBlockAllocator.Block block, + ulong offset, + ulong size) + { + _owner = owner; + _block = block; + Offset = offset; + Size = size; + } + + public void Dispose() + { + _owner.Free(_block, Offset, Size); + } + } +} diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs b/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs new file mode 100644 index 000000000..24c3a969c --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs @@ -0,0 +1,59 @@ +using Ryujinx.Memory; +using System.Collections.Generic; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl + { + private const ulong InvalidOffset = ulong.MaxValue; + + public class Block : PrivateMemoryAllocator.Block + { + private readonly HvIpaAllocator _ipaAllocator; + public ulong Ipa { get; } + + public Block(HvIpaAllocator ipaAllocator, MemoryBlock memory, ulong size) : base(memory, size) + { + _ipaAllocator = ipaAllocator; + + lock (ipaAllocator) + { + Ipa = ipaAllocator.Allocate(size); + } + + HvApi.hv_vm_map((ulong)Memory.Pointer, Ipa, size, hv_memory_flags_t.HV_MEMORY_READ | hv_memory_flags_t.HV_MEMORY_WRITE).ThrowOnError(); + } + + public override void Destroy() + { + HvApi.hv_vm_unmap(Ipa, Size).ThrowOnError(); + + lock (_ipaAllocator) + { + _ipaAllocator.Free(Ipa, Size); + } + + base.Destroy(); + } + } + + private readonly HvIpaAllocator _ipaAllocator; + + public HvMemoryBlockAllocator(HvIpaAllocator ipaAllocator, int blockAlignment) : base(blockAlignment, MemoryAllocationFlags.None) + { + _ipaAllocator = ipaAllocator; + } + + public unsafe HvMemoryBlockAllocation Allocate(ulong size, ulong alignment) + { + var allocation = Allocate(size, alignment, CreateBlock); + + return new HvMemoryBlockAllocation(this, allocation.Block, allocation.Offset, allocation.Size); + } + + private Block CreateBlock(MemoryBlock memory, ulong size) + { + return new Block(_ipaAllocator, memory, size); + } + } +} diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs new file mode 100644 index 000000000..222dcae1b --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -0,0 +1,947 @@ +using ARMeilleure.Memory; +using Ryujinx.Cpu.Tracking; +using Ryujinx.Memory; +using Ryujinx.Memory.Range; +using Ryujinx.Memory.Tracking; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Cpu.AppleHv +{ + /// + /// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table. + /// + public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock + { + public const int PageBits = 12; + public const int PageSize = 1 << PageBits; + public const int PageMask = PageSize - 1; + + public const int PageToPteShift = 5; // 32 pages (2 bits each) in one ulong page table entry. + public const ulong BlockMappedMask = 0x5555555555555555; // First bit of each table entry set. + + private enum HostMappedPtBits : ulong + { + Unmapped = 0, + Mapped, + WriteTracked, + ReadWriteTracked, + + MappedReplicated = 0x5555555555555555, + WriteTrackedReplicated = 0xaaaaaaaaaaaaaaaa, + ReadWriteTrackedReplicated = ulong.MaxValue + } + + private readonly InvalidAccessHandler _invalidAccessHandler; + + private readonly ulong _addressSpaceSize; + + private readonly HvAddressSpace _addressSpace; + + internal HvAddressSpace AddressSpace => _addressSpace; + + private readonly MemoryBlock _backingMemory; + private readonly PageTable _pageTable; + + private readonly ulong[] _pageBitmap; + + public bool Supports4KBPages => true; + + public int AddressSpaceBits { get; } + + public IntPtr PageTablePointer => IntPtr.Zero; + + public MemoryManagerType Type => MemoryManagerType.SoftwarePageTable; + + public MemoryTracking Tracking { get; } + + public event Action UnmapEvent; + + /// + /// Creates a new instance of the Hypervisor memory manager. + /// + /// Physical backing memory where virtual memory will be mapped to + /// Size of the address space + /// Optional function to handle invalid memory accesses + public HvMemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null) + { + _backingMemory = backingMemory; + _pageTable = new PageTable(); + _invalidAccessHandler = invalidAccessHandler; + _addressSpaceSize = addressSpaceSize; + + ulong asSize = PageSize; + int asBits = PageBits; + + while (asSize < addressSpaceSize) + { + asSize <<= 1; + asBits++; + } + + _addressSpace = new HvAddressSpace(backingMemory, asSize); + + AddressSpaceBits = asBits; + + _pageBitmap = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))]; + Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler); + } + + /// + /// Checks if the virtual address is part of the addressable space. + /// + /// Virtual address + /// True if the virtual address is part of the addressable space + private bool ValidateAddress(ulong va) + { + return va < _addressSpaceSize; + } + + /// + /// Checks if the combination of virtual address and size is part of the addressable space. + /// + /// Virtual address of the range + /// Size of the range in bytes + /// True if the combination of virtual address and size is part of the addressable space + private bool ValidateAddressAndSize(ulong va, ulong size) + { + ulong endVa = va + size; + return endVa >= va && endVa >= size && endVa <= _addressSpaceSize; + } + + /// + /// Ensures the combination of virtual address and size is part of the addressable space. + /// + /// Virtual address of the range + /// Size of the range in bytes + /// Throw when the memory region specified outside the addressable space + private void AssertValidAddressAndSize(ulong va, ulong size) + { + if (!ValidateAddressAndSize(va, size)) + { + throw new InvalidMemoryRegionException($"va=0x{va:X16}, size=0x{size:X16}"); + } + } + + /// + /// Ensures the combination of virtual address and size is part of the addressable space and fully mapped. + /// + /// Virtual address of the range + /// Size of the range in bytes + private void AssertMapped(ulong va, ulong size) + { + if (!ValidateAddressAndSize(va, size) || !IsRangeMappedImpl(va, size)) + { + throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}"); + } + } + + /// + public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) + { + AssertValidAddressAndSize(va, size); + + PtMap(va, pa, size); + _addressSpace.MapUser(va, pa, size, MemoryPermission.ReadWriteExecute); + AddMapping(va, size); + + Tracking.Map(va, size); + } + + private void PtMap(ulong va, ulong pa, ulong size) + { + while (size != 0) + { + _pageTable.Map(va, pa); + + va += PageSize; + pa += PageSize; + size -= PageSize; + } + } + + /// + public void MapForeign(ulong va, nuint hostPointer, ulong size) + { + throw new NotSupportedException(); + } + + /// + public void Unmap(ulong va, ulong size) + { + AssertValidAddressAndSize(va, size); + + UnmapEvent?.Invoke(va, size); + Tracking.Unmap(va, size); + + RemoveMapping(va, size); + _addressSpace.UnmapUser(va, size); + PtUnmap(va, size); + } + + private void PtUnmap(ulong va, ulong size) + { + while (size != 0) + { + _pageTable.Unmap(va); + + va += PageSize; + size -= PageSize; + } + } + + /// + public T Read(ulong va) where T : unmanaged + { + return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0]; + } + + /// + public T ReadTracked(ulong va) where T : unmanaged + { + try + { + SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false); + + return Read(va); + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; + } + + return default; + } + } + + /// + public void Read(ulong va, Span data) + { + ReadImpl(va, data); + } + + /// + public void Write(ulong va, T value) where T : unmanaged + { + Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1))); + } + + /// + public void Write(ulong va, ReadOnlySpan data) + { + if (data.Length == 0) + { + return; + } + + SignalMemoryTracking(va, (ulong)data.Length, true); + + WriteImpl(va, data); + } + + /// + public void WriteUntracked(ulong va, ReadOnlySpan data) + { + if (data.Length == 0) + { + return; + } + + WriteImpl(va, data); + } + + /// + public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data) + { + if (data.Length == 0) + { + return false; + } + + SignalMemoryTracking(va, (ulong)data.Length, false); + + if (IsContiguousAndMapped(va, data.Length)) + { + var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length); + + bool changed = !data.SequenceEqual(target); + + if (changed) + { + data.CopyTo(target); + } + + return changed; + } + else + { + WriteImpl(va, data); + + return true; + } + } + + private void WriteImpl(ulong va, ReadOnlySpan data) + { + try + { + AssertValidAddressAndSize(va, (ulong)data.Length); + + if (IsContiguousAndMapped(va, data.Length)) + { + data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); + } + else + { + int offset = 0, size; + + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressChecked(va); + + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + + data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressChecked(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); + + data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); + } + } + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; + } + } + } + + /// + public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) + { + if (size == 0) + { + return ReadOnlySpan.Empty; + } + + if (tracked) + { + SignalMemoryTracking(va, (ulong)size, false); + } + + if (IsContiguousAndMapped(va, size)) + { + return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); + } + else + { + Span data = new byte[size]; + + ReadImpl(va, data); + + return data; + } + } + + /// + public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false) + { + if (size == 0) + { + return new WritableRegion(null, va, Memory.Empty); + } + + if (tracked) + { + SignalMemoryTracking(va, (ulong)size, true); + } + + if (IsContiguousAndMapped(va, size)) + { + return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); + } + else + { + Memory memory = new byte[size]; + + ReadImpl(va, memory.Span); + + return new WritableRegion(this, va, memory); + } + } + + /// + public ref T GetRef(ulong va) where T : unmanaged + { + if (!IsContiguous(va, Unsafe.SizeOf())) + { + ThrowMemoryNotContiguous(); + } + + SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), true); + + return ref _backingMemory.GetRef(GetPhysicalAddressChecked(va)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsMapped(ulong va) + { + return ValidateAddress(va) && IsMappedImpl(va); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsMappedImpl(ulong va) + { + ulong page = va >> PageBits; + + int bit = (int)((page & 31) << 1); + + int pageIndex = (int)(page >> PageToPteShift); + ref ulong pageRef = ref _pageBitmap[pageIndex]; + + ulong pte = Volatile.Read(ref pageRef); + + return ((pte >> bit) & 3) != 0; + } + + /// + public bool IsRangeMapped(ulong va, ulong size) + { + AssertValidAddressAndSize(va, size); + + return IsRangeMappedImpl(va, size); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void GetPageBlockRange(ulong pageStart, ulong pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex) + { + startMask = ulong.MaxValue << ((int)(pageStart & 31) << 1); + endMask = ulong.MaxValue >> (64 - ((int)(pageEnd & 31) << 1)); + + pageIndex = (int)(pageStart >> PageToPteShift); + pageEndIndex = (int)((pageEnd - 1) >> PageToPteShift); + } + + private bool IsRangeMappedImpl(ulong va, ulong size) + { + int pages = GetPagesCount(va, size, out _); + + if (pages == 1) + { + return IsMappedImpl(va); + } + + ulong pageStart = va >> PageBits; + ulong pageEnd = pageStart + (ulong)pages; + + GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); + + // Check if either bit in each 2 bit page entry is set. + // OR the block with itself shifted down by 1, and check the first bit of each entry. + + ulong mask = BlockMappedMask & startMask; + + while (pageIndex <= pageEndIndex) + { + if (pageIndex == pageEndIndex) + { + mask &= endMask; + } + + ref ulong pageRef = ref _pageBitmap[pageIndex++]; + ulong pte = Volatile.Read(ref pageRef); + + pte |= pte >> 1; + if ((pte & mask) != mask) + { + return false; + } + + mask = BlockMappedMask; + } + + return true; + } + + private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsContiguous(ulong va, int size) + { + if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size)) + { + return false; + } + + int pages = GetPagesCount(va, (uint)size, out va); + + for (int page = 0; page < pages - 1; page++) + { + if (!ValidateAddress(va + PageSize)) + { + return false; + } + + if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize)) + { + return false; + } + + va += PageSize; + } + + return true; + } + + /// + public IEnumerable GetHostRegions(ulong va, ulong size) + { + if (size == 0) + { + return Enumerable.Empty(); + } + + var guestRegions = GetPhysicalRegionsImpl(va, size); + if (guestRegions == null) + { + return null; + } + + var regions = new HostMemoryRange[guestRegions.Count]; + + for (int i = 0; i < regions.Length; i++) + { + var guestRegion = guestRegions[i]; + IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); + regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); + } + + return regions; + } + + /// + public IEnumerable GetPhysicalRegions(ulong va, ulong size) + { + if (size == 0) + { + return Enumerable.Empty(); + } + + return GetPhysicalRegionsImpl(va, size); + } + + private List GetPhysicalRegionsImpl(ulong va, ulong size) + { + if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) + { + return null; + } + + int pages = GetPagesCount(va, (uint)size, out va); + + var regions = new List(); + + ulong regionStart = GetPhysicalAddressInternal(va); + ulong regionSize = PageSize; + + for (int page = 0; page < pages - 1; page++) + { + if (!ValidateAddress(va + PageSize)) + { + return null; + } + + ulong newPa = GetPhysicalAddressInternal(va + PageSize); + + if (GetPhysicalAddressInternal(va) + PageSize != newPa) + { + regions.Add(new MemoryRange(regionStart, regionSize)); + regionStart = newPa; + regionSize = 0; + } + + va += PageSize; + regionSize += PageSize; + } + + regions.Add(new MemoryRange(regionStart, regionSize)); + + return regions; + } + + private void ReadImpl(ulong va, Span data) + { + if (data.Length == 0) + { + return; + } + + try + { + AssertValidAddressAndSize(va, (ulong)data.Length); + + int offset = 0, size; + + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressChecked(va); + + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressChecked(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); + + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); + } + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; + } + } + } + + /// + /// + /// This function also validates that the given range is both valid and mapped, and will throw if it is not. + /// + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + { + AssertValidAddressAndSize(va, size); + + if (precise) + { + Tracking.VirtualMemoryEvent(va, size, write, precise: true); + return; + } + + // Software table, used for managed memory tracking. + + int pages = GetPagesCount(va, size, out _); + ulong pageStart = va >> PageBits; + + if (pages == 1) + { + ulong tag = (ulong)(write ? HostMappedPtBits.WriteTracked : HostMappedPtBits.ReadWriteTracked); + + int bit = (int)((pageStart & 31) << 1); + + int pageIndex = (int)(pageStart >> PageToPteShift); + ref ulong pageRef = ref _pageBitmap[pageIndex]; + + ulong pte = Volatile.Read(ref pageRef); + ulong state = ((pte >> bit) & 3); + + if (state >= tag) + { + Tracking.VirtualMemoryEvent(va, size, write); + return; + } + else if (state == 0) + { + ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}"); + } + } + else + { + ulong pageEnd = pageStart + (ulong)pages; + + GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); + + ulong mask = startMask; + + ulong anyTrackingTag = (ulong)HostMappedPtBits.WriteTrackedReplicated; + + while (pageIndex <= pageEndIndex) + { + if (pageIndex == pageEndIndex) + { + mask &= endMask; + } + + ref ulong pageRef = ref _pageBitmap[pageIndex++]; + + ulong pte = Volatile.Read(ref pageRef); + ulong mappedMask = mask & BlockMappedMask; + + ulong mappedPte = pte | (pte >> 1); + if ((mappedPte & mappedMask) != mappedMask) + { + ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}"); + } + + pte &= mask; + if ((pte & anyTrackingTag) != 0) // Search for any tracking. + { + // Writes trigger any tracking. + // Only trigger tracking from reads if both bits are set on any page. + if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) + { + Tracking.VirtualMemoryEvent(va, size, write); + break; + } + } + + mask = ulong.MaxValue; + } + } + } + + /// + /// Computes the number of pages in a virtual address range. + /// + /// Virtual address of the range + /// Size of the range + /// The virtual address of the beginning of the first page + /// This function does not differentiate between allocated and unallocated pages. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetPagesCount(ulong va, ulong size, out ulong startVa) + { + // WARNING: Always check if ulong does not overflow during the operations. + startVa = va & ~(ulong)PageMask; + ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask; + + return (int)(vaSpan / PageSize); + } + + /// + public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection) + { + // Protection is inverted on software pages, since the default value is 0. + protection = (~protection) & MemoryPermission.ReadAndWrite; + + int pages = GetPagesCount(va, size, out va); + ulong pageStart = va >> PageBits; + + if (pages == 1) + { + ulong protTag = protection switch + { + MemoryPermission.None => (ulong)HostMappedPtBits.Mapped, + MemoryPermission.Write => (ulong)HostMappedPtBits.WriteTracked, + _ => (ulong)HostMappedPtBits.ReadWriteTracked, + }; + + int bit = (int)((pageStart & 31) << 1); + + ulong tagMask = 3UL << bit; + ulong invTagMask = ~tagMask; + + ulong tag = protTag << bit; + + int pageIndex = (int)(pageStart >> PageToPteShift); + ref ulong pageRef = ref _pageBitmap[pageIndex]; + + ulong pte; + + do + { + pte = Volatile.Read(ref pageRef); + } + while ((pte & tagMask) != 0 && Interlocked.CompareExchange(ref pageRef, (pte & invTagMask) | tag, pte) != pte); + } + else + { + ulong pageEnd = pageStart + (ulong)pages; + + GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); + + ulong mask = startMask; + + ulong protTag = protection switch + { + MemoryPermission.None => (ulong)HostMappedPtBits.MappedReplicated, + MemoryPermission.Write => (ulong)HostMappedPtBits.WriteTrackedReplicated, + _ => (ulong)HostMappedPtBits.ReadWriteTrackedReplicated, + }; + + while (pageIndex <= pageEndIndex) + { + if (pageIndex == pageEndIndex) + { + mask &= endMask; + } + + ref ulong pageRef = ref _pageBitmap[pageIndex++]; + + ulong pte; + ulong mappedMask; + + // Change the protection of all 2 bit entries that are mapped. + do + { + pte = Volatile.Read(ref pageRef); + + mappedMask = pte | (pte >> 1); + mappedMask |= (mappedMask & BlockMappedMask) << 1; + mappedMask &= mask; // Only update mapped pages within the given range. + } + while (Interlocked.CompareExchange(ref pageRef, (pte & (~mappedMask)) | (protTag & mappedMask), pte) != pte); + + mask = ulong.MaxValue; + } + } + + protection = protection switch + { + MemoryPermission.None => MemoryPermission.ReadAndWrite, + MemoryPermission.Write => MemoryPermission.Read, + _ => MemoryPermission.None + }; + + _addressSpace.ReprotectUser(va, size, protection); + } + + /// + public CpuRegionHandle BeginTracking(ulong address, ulong size) + { + return new CpuRegionHandle(Tracking.BeginTracking(address, size)); + } + + /// + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity) + { + return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); + } + + /// + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + { + return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); + } + + /// + /// Adds the given address mapping to the page table. + /// + /// Virtual memory address + /// Size to be mapped + private void AddMapping(ulong va, ulong size) + { + int pages = GetPagesCount(va, size, out _); + ulong pageStart = va >> PageBits; + ulong pageEnd = pageStart + (ulong)pages; + + GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); + + ulong mask = startMask; + + while (pageIndex <= pageEndIndex) + { + if (pageIndex == pageEndIndex) + { + mask &= endMask; + } + + ref ulong pageRef = ref _pageBitmap[pageIndex++]; + + ulong pte; + ulong mappedMask; + + // Map all 2-bit entries that are unmapped. + do + { + pte = Volatile.Read(ref pageRef); + + mappedMask = pte | (pte >> 1); + mappedMask |= (mappedMask & BlockMappedMask) << 1; + mappedMask |= ~mask; // Treat everything outside the range as mapped, thus unchanged. + } + while (Interlocked.CompareExchange(ref pageRef, (pte & mappedMask) | (BlockMappedMask & (~mappedMask)), pte) != pte); + + mask = ulong.MaxValue; + } + } + + /// + /// Removes the given address mapping from the page table. + /// + /// Virtual memory address + /// Size to be unmapped + private void RemoveMapping(ulong va, ulong size) + { + int pages = GetPagesCount(va, size, out _); + ulong pageStart = va >> PageBits; + ulong pageEnd = pageStart + (ulong)pages; + + GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); + + startMask = ~startMask; + endMask = ~endMask; + + ulong mask = startMask; + + while (pageIndex <= pageEndIndex) + { + if (pageIndex == pageEndIndex) + { + mask |= endMask; + } + + ref ulong pageRef = ref _pageBitmap[pageIndex++]; + ulong pte; + + do + { + pte = Volatile.Read(ref pageRef); + } + while (Interlocked.CompareExchange(ref pageRef, pte & mask, pte) != pte); + + mask = 0; + } + } + + private ulong GetPhysicalAddressChecked(ulong va) + { + if (!IsMapped(va)) + { + ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}"); + } + + return GetPhysicalAddressInternal(va); + } + + private ulong GetPhysicalAddressInternal(ulong va) + { + return _pageTable.Read(va) + (va & PageMask); + } + + /// + /// Disposes of resources used by the memory manager. + /// + protected override void Destroy() + { + _addressSpace.Dispose(); + } + + private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message); + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvVcpu.cs b/Ryujinx.Cpu/AppleHv/HvVcpu.cs new file mode 100644 index 000000000..484a9fe82 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvVcpu.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Cpu.AppleHv +{ + unsafe class HvVcpu + { + public readonly ulong Handle; + public readonly hv_vcpu_exit_t* ExitInfo; + public readonly IHvExecutionContext ShadowContext; + public readonly IHvExecutionContext NativeContext; + public readonly bool IsEphemeral; + + public HvVcpu( + ulong handle, + hv_vcpu_exit_t* exitInfo, + IHvExecutionContext shadowContext, + IHvExecutionContext nativeContext, + bool isEphemeral) + { + Handle = handle; + ExitInfo = exitInfo; + ShadowContext = shadowContext; + NativeContext = nativeContext; + IsEphemeral = isEphemeral; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs b/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs new file mode 100644 index 000000000..cb1944fe7 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs @@ -0,0 +1,103 @@ +using System; +using System.Threading; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvVcpuPool + { + // Since there's a limit on the number of VCPUs we can create, + // and we assign one VCPU per guest thread, we need to ensure + // there are enough VCPUs available for at least the maximum number of active guest threads. + // To do that, we always destroy and re-create VCPUs that are above a given limit. + // Those VCPUs are called "ephemeral" here because they are not kept for long. + // + // In the future, we might want to consider a smarter approach that only makes + // VCPUs for threads that are not running frequently "ephemeral", but this is + // complicated because VCPUs can only be destroyed by the same thread that created them. + + private const int MaxActiveVcpus = 4; + + public static readonly HvVcpuPool Instance = new HvVcpuPool(); + + private int _totalVcpus; + private int _maxVcpus; + + public HvVcpuPool() + { + HvApi.hv_vm_get_max_vcpu_count(out uint maxVcpuCount).ThrowOnError(); + _maxVcpus = (int)maxVcpuCount; + } + + public HvVcpu Create(HvAddressSpace addressSpace, IHvExecutionContext shadowContext, Action swapContext) + { + HvVcpu vcpu = CreateNew(addressSpace, shadowContext); + vcpu.NativeContext.Load(shadowContext); + swapContext(vcpu.NativeContext); + return vcpu; + } + + public void Destroy(HvVcpu vcpu, Action swapContext) + { + vcpu.ShadowContext.Load(vcpu.NativeContext); + swapContext(vcpu.ShadowContext); + DestroyVcpu(vcpu); + } + + public void Return(HvVcpu vcpu, Action swapContext) + { + if (vcpu.IsEphemeral) + { + Destroy(vcpu, swapContext); + } + } + + public HvVcpu Rent(HvAddressSpace addressSpace, IHvExecutionContext shadowContext, HvVcpu vcpu, Action swapContext) + { + if (vcpu.IsEphemeral) + { + return Create(addressSpace, shadowContext, swapContext); + } + else + { + return vcpu; + } + } + + private unsafe HvVcpu CreateNew(HvAddressSpace addressSpace, IHvExecutionContext shadowContext) + { + int newCount = IncrementVcpuCount(); + bool isEphemeral = newCount > _maxVcpus - MaxActiveVcpus; + + // Create VCPU. + hv_vcpu_exit_t* exitInfo = null; + HvApi.hv_vcpu_create(out ulong vcpuHandle, ref exitInfo, IntPtr.Zero).ThrowOnError(); + + // Enable FP and SIMD instructions. + HvApi.hv_vcpu_set_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_CPACR_EL1, 0b11 << 20).ThrowOnError(); + + addressSpace.InitializeMmu(vcpuHandle); + + HvExecutionContextVcpu nativeContext = new HvExecutionContextVcpu(vcpuHandle); + + HvVcpu vcpu = new HvVcpu(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral); + + return vcpu; + } + + private void DestroyVcpu(HvVcpu vcpu) + { + HvApi.hv_vcpu_destroy(vcpu.Handle).ThrowOnError(); + DecrementVcpuCount(); + } + + private int IncrementVcpuCount() + { + return Interlocked.Increment(ref _totalVcpus); + } + + private void DecrementVcpuCount() + { + Interlocked.Decrement(ref _totalVcpus); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/HvVm.cs b/Ryujinx.Cpu/AppleHv/HvVm.cs new file mode 100644 index 000000000..d91abff9a --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvVm.cs @@ -0,0 +1,68 @@ +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + static class HvVm + { + // This alignment allows us to use larger blocks on the page table. + private const ulong AsIpaAlignment = 1UL << 30; + + private static int _addressSpaces; + private static HvIpaAllocator _ipaAllocator; + private static object _lock = new object(); + + public static (ulong, HvIpaAllocator) CreateAddressSpace(MemoryBlock block) + { + HvIpaAllocator ipaAllocator; + + lock (_lock) + { + if (++_addressSpaces == 1) + { + HvApi.hv_vm_create(IntPtr.Zero).ThrowOnError(); + _ipaAllocator = ipaAllocator = new HvIpaAllocator(); + } + else + { + ipaAllocator = _ipaAllocator; + } + } + + ulong baseAddress; + + lock (ipaAllocator) + { + baseAddress = ipaAllocator.Allocate(block.Size, AsIpaAlignment); + } + + var rwx = hv_memory_flags_t.HV_MEMORY_READ | hv_memory_flags_t.HV_MEMORY_WRITE | hv_memory_flags_t.HV_MEMORY_EXEC; + + HvApi.hv_vm_map((ulong)block.Pointer, baseAddress, block.Size, rwx).ThrowOnError(); + + return (baseAddress, ipaAllocator); + } + + public static void DestroyAddressSpace(ulong address, ulong size) + { + HvApi.hv_vm_unmap(address, size); + + HvIpaAllocator ipaAllocator; + + lock (_lock) + { + if (--_addressSpaces == 0) + { + HvApi.hv_vm_destroy().ThrowOnError(); + } + + ipaAllocator = _ipaAllocator; + } + + lock (ipaAllocator) + { + ipaAllocator.Free(address, size); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs b/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs new file mode 100644 index 000000000..adf2dd997 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs @@ -0,0 +1,46 @@ +using ARMeilleure.State; + +namespace Ryujinx.Cpu.AppleHv +{ + public interface IHvExecutionContext + { + ulong Pc { get; set; } + ulong ElrEl1 { get; set; } + ulong EsrEl1 { get; set; } + + long TpidrEl0 { get; set; } + long TpidrroEl0 { get; set; } + + uint Pstate { get; set; } + + uint Fpcr { get; set; } + uint Fpsr { get; set; } + + ulong GetX(int index); + void SetX(int index, ulong value); + + V128 GetV(int index); + void SetV(int index, V128 value); + + public void Load(IHvExecutionContext context) + { + Pc = context.Pc; + ElrEl1 = context.ElrEl1; + EsrEl1 = context.EsrEl1; + TpidrEl0 = context.TpidrEl0; + TpidrroEl0 = context.TpidrroEl0; + Pstate = context.Pstate; + Fpcr = context.Fpcr; + Fpsr = context.Fpsr; + + for (int i = 0; i < 32; i++) + { + SetX(i, context.GetX(i)); + SetV(i, context.GetV(i)); + } + } + + void RequestInterrupt(); + bool GetAndClearInterruptRequested(); + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HLEConfiguration.cs b/Ryujinx.HLE/HLEConfiguration.cs index 8fd02a962..e21157f9b 100644 --- a/Ryujinx.HLE/HLEConfiguration.cs +++ b/Ryujinx.HLE/HLEConfiguration.cs @@ -148,6 +148,11 @@ namespace Ryujinx.HLE /// public float AudioVolume { get; set; } + /// + /// Use Hypervisor over JIT if available. + /// + internal readonly bool UseHypervisor; + /// /// An action called when HLE force a refresh of output after docked mode changed. /// @@ -175,7 +180,8 @@ namespace Ryujinx.HLE MemoryManagerMode memoryManagerMode, bool ignoreMissingServices, AspectRatio aspectRatio, - float audioVolume) + float audioVolume, + bool useHypervisor) { VirtualFileSystem = virtualFileSystem; LibHacHorizonManager = libHacHorizonManager; @@ -200,6 +206,7 @@ namespace Ryujinx.HLE IgnoreMissingServices = ignoreMissingServices; AspectRatio = aspectRatio; AudioVolume = audioVolume; + UseHypervisor = useHypervisor; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 5ecaf38e9..1b0d66ac6 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -1,17 +1,19 @@ using Ryujinx.Common.Configuration; using Ryujinx.Cpu; +using Ryujinx.Cpu.AppleHv; using Ryujinx.Cpu.Jit; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.Memory; using System; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS { class ArmProcessContextFactory : IProcessContextFactory { - private readonly ICpuEngine _cpuEngine; + private readonly ITickSource _tickSource; private readonly GpuContext _gpu; private readonly string _titleIdText; private readonly string _displayVersion; @@ -22,7 +24,7 @@ namespace Ryujinx.HLE.HOS public IDiskCacheLoadState DiskCacheLoadState { get; private set; } public ArmProcessContextFactory( - ICpuEngine cpuEngine, + ITickSource tickSource, GpuContext gpu, string titleIdText, string displayVersion, @@ -30,7 +32,7 @@ namespace Ryujinx.HLE.HOS ulong codeAddress, ulong codeSize) { - _cpuEngine = cpuEngine; + _tickSource = tickSource; _gpu = gpu; _titleIdText = titleIdText; _displayVersion = displayVersion; @@ -41,31 +43,42 @@ namespace Ryujinx.HLE.HOS public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit) { - MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode; - - if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) - { - mode = MemoryManagerMode.SoftwarePageTable; - } - IArmProcessContext processContext; - switch (mode) + if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && for64Bit && context.Device.Configuration.UseHypervisor) { - case MemoryManagerMode.SoftwarePageTable: - var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); - processContext = new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManager, for64Bit); - break; + var cpuEngine = new HvEngine(_tickSource); + var memoryManager = new HvMemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); + processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManager, for64Bit); + } + else + { + MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode; - case MemoryManagerMode.HostMapped: - case MemoryManagerMode.HostMappedUnsafe: - bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; - var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler); - processContext = new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); - break; + if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) + { + mode = MemoryManagerMode.SoftwarePageTable; + } - default: - throw new ArgumentOutOfRangeException(); + var cpuEngine = new JitEngine(_tickSource); + + switch (mode) + { + case MemoryManagerMode.SoftwarePageTable: + var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); + processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManager, for64Bit); + break; + + case MemoryManagerMode.HostMapped: + case MemoryManagerMode.HostMappedUnsafe: + bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; + var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler); + processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); + break; + + default: + throw new ArgumentOutOfRangeException(); + } } DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize); diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index ca3f8103e..9908cbba8 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -12,7 +12,6 @@ using Ryujinx.Audio.Renderer.Device; using Ryujinx.Audio.Renderer.Server; using Ryujinx.Common.Utilities; using Ryujinx.Cpu; -using Ryujinx.Cpu.Jit; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Memory; @@ -61,7 +60,6 @@ namespace Ryujinx.HLE.HOS internal Switch Device { get; private set; } internal ITickSource TickSource { get; } - internal ICpuEngine CpuEngine { get; } internal SurfaceFlinger SurfaceFlinger { get; private set; } internal AudioManager AudioManager { get; private set; } @@ -130,7 +128,6 @@ namespace Ryujinx.HLE.HOS public Horizon(Switch device) { TickSource = new TickSource(KernelConstants.CounterFrequency); - CpuEngine = new JitEngine(TickSource); KernelContext = new KernelContext( TickSource, diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 695f46721..1f6fd96d7 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS KProcess process = new KProcess(context); var processContextFactory = new ArmProcessContextFactory( - context.Device.System.CpuEngine, + context.Device.System.TickSource, context.Device.Gpu, string.Empty, string.Empty, @@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS } var processContextFactory = new ArmProcessContextFactory( - context.Device.System.CpuEngine, + context.Device.System.TickSource, context.Device.Gpu, programInfo.TitleIdText, programInfo.DisplayVersion, diff --git a/Ryujinx.Headless.SDL2/Options.cs b/Ryujinx.Headless.SDL2/Options.cs index 49233bcea..5138a0535 100644 --- a/Ryujinx.Headless.SDL2/Options.cs +++ b/Ryujinx.Headless.SDL2/Options.cs @@ -129,6 +129,9 @@ namespace Ryujinx.Headless.SDL2 [Option("audio-volume", Required = false, Default = 1.0f, HelpText ="The audio level (0 to 1).")] public float AudioVolume { get; set; } + [Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")] + public bool UseHypervisor { get; set; } + // Logging [Option("disable-file-logging", Required = false, Default = false, HelpText = "Disables logging to a file on disk.")] diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 6ea3a98d7..f618e38d6 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -548,7 +548,8 @@ namespace Ryujinx.Headless.SDL2 options.MemoryManagerMode, options.IgnoreMissingServices, options.AspectRatio, - options.AudioVolume); + options.AudioVolume, + options.UseHypervisor); return new Switch(configuration); } diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 2ebf65ac1..226b5933b 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 42; + public const int CurrentVersion = 43; /// /// Version of the configuration file format @@ -330,6 +330,11 @@ namespace Ryujinx.Ui.Common.Configuration /// public string PreferredGpu { get; set; } + /// + /// Uses Hypervisor over JIT if available + /// + public bool UseHypervisor { get; set; } + /// /// Loads a configuration file from disk /// diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index e64c69ad6..f193b1570 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -301,6 +301,11 @@ namespace Ryujinx.Ui.Common.Configuration /// public ReactiveObject IgnoreMissingServices { get; private set; } + /// + /// Uses Hypervisor over JIT if available + /// + public ReactiveObject UseHypervisor { get; private set; } + public SystemSection() { Language = new ReactiveObject(); @@ -327,6 +332,8 @@ namespace Ryujinx.Ui.Common.Configuration IgnoreMissingServices.Event += static (sender, e) => LogValueChange(sender, e, nameof(IgnoreMissingServices)); AudioVolume = new ReactiveObject(); AudioVolume.Event += static (sender, e) => LogValueChange(sender, e, nameof(AudioVolume)); + UseHypervisor = new ReactiveObject(); + UseHypervisor.Event += static (sender, e) => LogValueChange(sender, e, nameof(UseHypervisor)); } } @@ -566,6 +573,7 @@ namespace Ryujinx.Ui.Common.Configuration MemoryManagerMode = System.MemoryManagerMode, ExpandRam = System.ExpandRam, IgnoreMissingServices = System.IgnoreMissingServices, + UseHypervisor = System.UseHypervisor, GuiColumns = new GuiColumns { FavColumn = Ui.GuiColumns.FavColumn, @@ -652,6 +660,7 @@ namespace Ryujinx.Ui.Common.Configuration System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe; System.ExpandRam.Value = false; System.IgnoreMissingServices.Value = false; + System.UseHypervisor.Value = true; Ui.GuiColumns.FavColumn.Value = true; Ui.GuiColumns.IconColumn.Value = true; Ui.GuiColumns.AppColumn.Value = true; @@ -1192,6 +1201,13 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.EnableMacroHLE = true; } + if (configurationFileFormat.Version < 43) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 43."); + + configurationFileFormat.UseHypervisor = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1233,6 +1249,7 @@ namespace Ryujinx.Ui.Common.Configuration System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode; System.ExpandRam.Value = configurationFileFormat.ExpandRam; System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices; + System.UseHypervisor.Value = configurationFileFormat.UseHypervisor; Ui.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn; Ui.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn; Ui.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn; @@ -1292,4 +1309,4 @@ namespace Ryujinx.Ui.Common.Configuration Instance = new ConfigurationState(); } } -} +} \ No newline at end of file diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 53a97fb9f..5051fb5f6 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -577,7 +577,8 @@ namespace Ryujinx.Ui ConfigurationState.Instance.System.MemoryManagerMode, ConfigurationState.Instance.System.IgnoreMissingServices, ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume); + ConfigurationState.Instance.System.AudioVolume, + ConfigurationState.Instance.System.UseHypervisor); _emulationContext = new HLE.Switch(configuration); } From 9044cb38d19194bb29595ac09cad8857364650b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 01:58:25 +0100 Subject: [PATCH 330/737] nuget: bump SharpZipLib from 1.4.1 to 1.4.2 (#4353) Bumps [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) from 1.4.1 to 1.4.2. - [Release notes](https://github.com/icsharpcode/SharpZipLib/releases) - [Changelog](https://github.com/icsharpcode/SharpZipLib/blob/master/docs/Changes.txt) - [Commits](https://github.com/icsharpcode/SharpZipLib/compare/v1.4.1...v1.4.2) --- updated-dependencies: - dependency-name: SharpZipLib dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d60960fbd..8f7788dc6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -36,7 +36,7 @@ - + From 780627e7b0bc6137e72674d146e121a6def0fe13 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 1 Feb 2023 12:52:36 -0300 Subject: [PATCH 331/737] Implement Account LoadOpenContext (#4359) * Implement Account LoadOpenContext * Formatting --- .../Account/Acc/IAccountServiceForApplication.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 059aba17d..1b412d74e 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return resultCode; } - + [CommandHipc(110)] // StoreSaveDataThumbnail(nn::account::Uid, buffer) public ResultCode StoreSaveDataThumbnail(ServiceCtx context) @@ -153,10 +153,17 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc } [CommandHipc(130)] // 5.0.0+ - // LoadOpenContext(nn::account::Uid) + // LoadOpenContext(nn::account::Uid) -> object public ResultCode LoadOpenContext(ServiceCtx context) { - Logger.Stub?.PrintStub(LogClass.ServiceAcc); + ResultCode resultCode = _applicationServiceServer.CheckUserId(context, out UserId userId); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + MakeObject(context, new IManagerForApplication(userId)); return ResultCode.Success; } From 43081c16c48d73dfd585d951281fcbd60bebccbc Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 5 Feb 2023 18:52:57 -0300 Subject: [PATCH 332/737] Insert bitcast for assignment of fragment integer outputs on GLSL (#4369) * Insert bitcast for assignment of fragment integer outputs on GLSL * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index dcd0eb709..1f6dab893 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4336; + private const uint CodeGenVersion = 4369; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 080f1708b..ce1ab50e0 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -485,6 +485,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location); + return type.ToAggregateType(); + } + else if (context.Config.Stage == ShaderStage.Fragment && isAsgDest && + operand.Value >= AttributeConsts.FragmentOutputColorBase && + operand.Value < AttributeConsts.FragmentOutputColorEnd) + { + int location = (operand.Value - AttributeConsts.FragmentOutputColorBase) / 16; + + AttributeType type = context.Config.GpuAccessor.QueryFragmentOutputType(location); + return type.ToAggregateType(); } } From 7528f945361a52a0f1b3cd604ae8c203a9670111 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 6 Feb 2023 00:19:31 -0300 Subject: [PATCH 333/737] Implement safe depth-stencil blit using stencil export extension (#4356) * Implement safe depth-stencil blit using stencil export extension * Delete depth-stencil blit with buffer path --- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 4 +- .../HardwareCapabilities.cs | 3 + Ryujinx.Graphics.Vulkan/HelperShader.cs | 216 +++++++++++++++++- .../DepthBlitFragmentShaderSource.frag | 10 + .../Shaders/ShaderBinaries.cs | 90 ++++++++ .../StencilBlitFragmentShaderSource.frag | 12 + Ryujinx.Graphics.Vulkan/TextureView.cs | 204 +---------------- .../VulkanInitialization.cs | 1 + Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + Ryujinx.Graphics.Vulkan/Window.cs | 2 +- 10 files changed, 330 insertions(+), 213 deletions(-) create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index ff8d423b8..0a1cdcce3 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan { _device = device; _attachments = new[] { view }; - _validColorAttachments = 1u; + _validColorAttachments = isDepthStencil ? 0u : 1u; Width = width; Height = height; @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = new[] { samples }; AttachmentFormats = new[] { format }; - AttachmentIndices = new[] { 0 }; + AttachmentIndices = isDepthStencil ? Array.Empty() : new[] { 0 }; AttachmentsCount = 1; diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 376f39608..82fcaea10 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsSubgroupSizeControl; public readonly bool SupportsShaderInt8; + public readonly bool SupportsShaderStencilExport; public readonly bool SupportsConditionalRendering; public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsMultiView; @@ -48,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsGeometryShaderPassthrough, bool supportsSubgroupSizeControl, bool supportsShaderInt8, + bool supportsShaderStencilExport, bool supportsConditionalRendering, bool supportsExtendedDynamicState, bool supportsMultiView, @@ -71,6 +73,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsSubgroupSizeControl = supportsSubgroupSizeControl; SupportsShaderInt8 = supportsShaderInt8; + SupportsShaderStencilExport = supportsShaderStencilExport; SupportsConditionalRendering = supportsConditionalRendering; SupportsExtendedDynamicState = supportsExtendedDynamicState; SupportsMultiView = supportsMultiView; diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 223bcc71e..658516e6d 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -33,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programConvertIndirectData; private readonly IProgram _programColorCopyToNonMs; private readonly IProgram _programColorDrawToMs; + private readonly IProgram _programDepthBlit; + private readonly IProgram _programStencilBlit; public HelperShader(VulkanRenderer gd, Device device) { @@ -42,13 +44,13 @@ namespace Ryujinx.Graphics.Vulkan _samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); _samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); - var colorBlitVertexBindings = new ShaderBindings( + var blitVertexBindings = new ShaderBindings( new[] { 1 }, Array.Empty(), Array.Empty(), Array.Empty()); - var colorBlitFragmentBindings = new ShaderBindings( + var blitFragmentBindings = new ShaderBindings( Array.Empty(), Array.Empty(), new[] { 0 }, @@ -56,14 +58,14 @@ namespace Ryujinx.Graphics.Vulkan _programColorBlit = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); var colorClearFragmentBindings = new ShaderBindings( @@ -74,19 +76,19 @@ namespace Ryujinx.Graphics.Vulkan _programColorClearF = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); _programColorClearSI = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); @@ -151,6 +153,21 @@ namespace Ryujinx.Graphics.Vulkan { new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv), }); + + _programDepthBlit = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + if (gd.Capabilities.SupportsShaderStencilExport) + { + _programStencilBlit = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + } } public void Blit( @@ -162,6 +179,7 @@ namespace Ryujinx.Graphics.Vulkan VkFormat dstFormat, Extents2D srcRegion, Extents2D dstRegion, + bool isDepthOrStencil, bool linearFilter, bool clearAlpha = false) { @@ -169,10 +187,17 @@ namespace Ryujinx.Graphics.Vulkan using var cbs = gd.CommandBufferPool.Rent(); - Blit(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha); + if (isDepthOrStencil) + { + BlitDepthStencil(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion); + } + else + { + BlitColor(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha); + } } - public void Blit( + public void BlitColor( VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, @@ -255,6 +280,173 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.Delete(bufferHandle); } + private void BlitDepthStencil( + VulkanRenderer gd, + CommandBufferScoped cbs, + TextureView src, + Auto dst, + int dstWidth, + int dstHeight, + VkFormat dstFormat, + Extents2D srcRegion, + Extents2D dstRegion) + { + _pipeline.SetCommandBuffer(cbs); + + const int RegionBufferSize = 16; + + Span region = stackalloc float[RegionBufferSize / sizeof(float)]; + + region[0] = (float)srcRegion.X1 / src.Width; + region[1] = (float)srcRegion.X2 / src.Width; + region[2] = (float)srcRegion.Y1 / src.Height; + region[3] = (float)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]); + } + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false); + + gd.BufferManager.SetData(bufferHandle, 0, region); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); + + Span viewports = stackalloc GAL.Viewport[1]; + + var rect = new Rectangle( + 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 GAL.Viewport( + rect, + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + Span> scissors = stackalloc Rectangle[1]; + + scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); + + _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + + var aspectFlags = src.Info.Format.ConvertAspectFlags(); + + if (aspectFlags.HasFlag(ImageAspectFlags.DepthBit)) + { + var depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth); + + BlitDepthStencilDraw(depthTexture, isDepth: true); + + if (depthTexture != src) + { + depthTexture.Release(); + } + } + + if (aspectFlags.HasFlag(ImageAspectFlags.StencilBit) && _programStencilBlit != null) + { + var stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil); + + BlitDepthStencilDraw(stencilTexture, isDepth: false); + + if (stencilTexture != src) + { + stencilTexture.Release(); + } + } + + _pipeline.Finish(gd, cbs); + + gd.BufferManager.Delete(bufferHandle); + } + + private static TextureView CreateDepthOrStencilView(TextureView depthStencilTexture, DepthStencilMode depthStencilMode) + { + if (depthStencilTexture.Info.DepthStencilMode == depthStencilMode) + { + return depthStencilTexture; + } + + return (TextureView)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(TextureView src, bool isDepth) + { + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); + + if (isDepth) + { + _pipeline.SetProgram(_programDepthBlit); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + } + else + { + _pipeline.SetProgram(_programStencilBlit); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); + } + + _pipeline.Draw(4, 1, 0, 0); + + if (isDepth) + { + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always)); + } + else + { + _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); + } + } + + private static StencilTestDescriptor CreateStencilTestDescriptor(bool enabled) + { + return new StencilTestDescriptor( + enabled, + GAL.CompareOp.Always, + GAL.StencilOp.Replace, + GAL.StencilOp.Replace, + GAL.StencilOp.Replace, + 0, + 0xff, + 0xff, + GAL.CompareOp.Always, + GAL.StencilOp.Replace, + GAL.StencilOp.Replace, + GAL.StencilOp.Replace, + 0, + 0xff, + 0xff); + } + public void Clear( VulkanRenderer gd, Auto dst, @@ -993,6 +1185,8 @@ namespace Ryujinx.Graphics.Vulkan _programConvertIndirectData.Dispose(); _programColorCopyToNonMs.Dispose(); _programColorDrawToMs.Dispose(); + _programDepthBlit.Dispose(); + _programStencilBlit?.Dispose(); _samplerNearest.Dispose(); _samplerLinear.Dispose(); _pipeline.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag new file mode 100644 index 000000000..55b7be137 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag @@ -0,0 +1,10 @@ +#version 450 core + +layout (binding = 0, set = 2) uniform sampler2D texDepth; + +layout (location = 0) in vec2 tex_coord; + +void main() +{ + gl_FragDepth = texture(texDepth, tex_coord).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index 667e5a8b4..5a2acf221 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -1459,5 +1459,95 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x3B, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + + public static readonly byte[] DepthBlitFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x44, + 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + + public static readonly byte[] StencilBlitFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, + 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, + 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x13, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, + 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x53, + 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x53, 0x74, 0x65, 0x6E, 0x63, + 0x69, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, + 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag new file mode 100644 index 000000000..1919269be --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag @@ -0,0 +1,12 @@ +#version 450 core + +#extension GL_ARB_shader_stencil_export : require + +layout (binding = 0, set = 2) uniform isampler2D texStencil; + +layout (location = 0) in vec2 tex_coord; + +void main() +{ + gl_FragStencilRefARB = texture(texStencil, tex_coord).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index 67f207219..3ee196756 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -362,21 +362,16 @@ namespace Ryujinx.Graphics.Vulkan levels, linearFilter); - return; - } - else if (srcFormat == GAL.Format.D32FloatS8Uint && srcFormat == dstFormat && SupportsBlitFromD32FS8ToD32FAndS8()) - { - BlitDepthStencilWithBuffer(_gd, cbs, src, dst, srcRegion, dstRegion); - return; } } + bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + if (VulkanConfiguration.UseSlowSafeBlitOnAmd && (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk) && src.Info.Target == Target.Texture2D && - dst.Info.Target == Target.Texture2D && - !dst.Info.Format.IsDepthOrStencil()) + dst.Info.Target == Target.Texture2D) { _gd.HelperShader.Blit( _gd, @@ -387,6 +382,7 @@ namespace Ryujinx.Graphics.Vulkan dst.VkFormat, srcRegion, dstRegion, + isDepthOrStencil, linearFilter); return; @@ -395,7 +391,7 @@ namespace Ryujinx.Graphics.Vulkan Auto srcImage; Auto dstImage; - if (dst.Info.Format.IsDepthOrStencil()) + if (isDepthOrStencil) { srcImage = src.Storage.CreateAliasedColorForDepthStorageUnsafe(srcFormat).GetImage(); dstImage = dst.Storage.CreateAliasedColorForDepthStorageUnsafe(dstFormat).GetImage(); @@ -426,189 +422,6 @@ namespace Ryujinx.Graphics.Vulkan ImageAspectFlags.ColorBit); } - private static void BlitDepthStencilWithBuffer( - VulkanRenderer gd, - CommandBufferScoped cbs, - TextureView src, - TextureView dst, - Extents2D srcRegion, - Extents2D dstRegion) - { - int drBaseX = Math.Min(dstRegion.X1, dstRegion.X2); - int drBaseY = Math.Min(dstRegion.Y1, dstRegion.Y2); - int drWidth = Math.Abs(dstRegion.X2 - dstRegion.X1); - int drHeight = Math.Abs(dstRegion.Y2 - dstRegion.Y1); - - var drOriginZero = new Extents2D( - dstRegion.X1 - drBaseX, - dstRegion.Y1 - drBaseY, - dstRegion.X2 - drBaseX, - dstRegion.Y2 - drBaseY); - - var d32SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.D32Float, 4); - var d32DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.D32Float, 4, drWidth, drHeight); - var s8SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.S8Uint, 1); - var s8DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.S8Uint, 1, drWidth, drHeight); - - using var d32SrcStorage = gd.CreateTextureStorage(d32SrcStorageInfo, src.Storage.ScaleFactor); - using var d32DstStorage = gd.CreateTextureStorage(d32DstStorageInfo, dst.Storage.ScaleFactor); - using var s8SrcStorage = gd.CreateTextureStorage(s8SrcStorageInfo, src.Storage.ScaleFactor); - using var s8DstStorage = gd.CreateTextureStorage(s8DstStorageInfo, dst.Storage.ScaleFactor); - - void SlowBlit(TextureStorage srcTemp, TextureStorage dstTemp, ImageAspectFlags aspectFlags) - { - int levels = Math.Min(src.Info.Levels, dst.Info.Levels); - - int srcSize = 0; - int dstSize = 0; - - for (int l = 0; l < levels; l++) - { - srcSize += srcTemp.Info.GetMipSize2D(l); - dstSize += dstTemp.Info.GetMipSize2D(l); - } - - using var srcTempBuffer = gd.BufferManager.Create(gd, srcSize, deviceLocal: true); - using var dstTempBuffer = gd.BufferManager.Create(gd, dstSize, deviceLocal: true); - - src.Storage.CopyFromOrToBuffer( - cbs.CommandBuffer, - srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, - src.GetImage().Get(cbs).Value, - srcSize, - to: true, - 0, - 0, - src.FirstLayer, - src.FirstLevel, - 1, - levels, - true, - aspectFlags, - false); - - BufferHolder.InsertBufferBarrier( - gd, - cbs.CommandBuffer, - srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - 0, - srcSize); - - srcTemp.CopyFromOrToBuffer( - cbs.CommandBuffer, - srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, - srcTemp.GetImage().Get(cbs).Value, - srcSize, - to: false, - 0, - 0, - 0, - 0, - 1, - levels, - true, - aspectFlags, - false); - - InsertImageBarrier( - gd.Api, - cbs.CommandBuffer, - srcTemp.GetImage().Get(cbs).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - aspectFlags, - 0, - 0, - 1, - levels); - - TextureCopy.Blit( - gd.Api, - cbs.CommandBuffer, - srcTemp.GetImage().Get(cbs).Value, - dstTemp.GetImage().Get(cbs).Value, - srcTemp.Info, - dstTemp.Info, - srcRegion, - drOriginZero, - 0, - 0, - 0, - 0, - 1, - levels, - false, - aspectFlags, - aspectFlags); - - InsertImageBarrier( - gd.Api, - cbs.CommandBuffer, - dstTemp.GetImage().Get(cbs).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - aspectFlags, - 0, - 0, - 1, - levels); - - dstTemp.CopyFromOrToBuffer( - cbs.CommandBuffer, - dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, - dstTemp.GetImage().Get(cbs).Value, - dstSize, - to: true, - 0, - 0, - 0, - 0, - 1, - levels, - true, - aspectFlags, - false); - - BufferHolder.InsertBufferBarrier( - gd, - cbs.CommandBuffer, - dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - 0, - dstSize); - - dst.Storage.CopyFromOrToBuffer( - cbs.CommandBuffer, - dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, - dst.GetImage().Get(cbs).Value, - dstSize, - to: false, - drBaseX, - drBaseY, - dst.FirstLayer, - dst.FirstLevel, - 1, - levels, - true, - aspectFlags, - false); - } - - SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.DepthBit); - SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.StencilBit); - } - public static unsafe void InsertImageBarrier( Vk api, CommandBuffer commandBuffer, @@ -649,13 +462,6 @@ namespace Ryujinx.Graphics.Vulkan memoryBarrier); } - private bool SupportsBlitFromD32FS8ToD32FAndS8() - { - var formatFeatureFlags = FormatFeatureFlags.BlitSrcBit | FormatFeatureFlags.BlitDstBit; - return _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.D32Float) && - _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.S8Uint); - } - public TextureView GetView(GAL.Format format) { if (format == Info.Format) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index f38fc8ab8..ab5a0acfb 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_fragment_shader_interlock", "VK_EXT_index_type_uint8", "VK_EXT_robustness2", + "VK_EXT_shader_stencil_export", "VK_KHR_shader_float16_int8", "VK_EXT_shader_subgroup_ballot", "VK_EXT_subgroup_size_control", diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 085a7e930..92dec7a1a 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -263,6 +263,7 @@ namespace Ryujinx.Graphics.Vulkan supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), supportedExtensions.Contains("VK_EXT_subgroup_size_control"), featuresShaderInt8.ShaderInt8, + supportedExtensions.Contains("VK_EXT_shader_stencil_export"), supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), features2.Features.MultiViewport, diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 9f731e0e6..dc4dc6be9 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -335,7 +335,7 @@ namespace Ryujinx.Graphics.Vulkan int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; - _gd.HelperShader.Blit( + _gd.HelperShader.BlitColor( _gd, cbs, view, From cb250162cb47536120a963acdc2672d09e147edf Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 6 Feb 2023 23:38:54 -0300 Subject: [PATCH 334/737] Accelerate NVDEC VIC surface read/write and colorspace conversion with Arm64 HW intrinsics (#4351) * Accelerate NVDEC VIC surface read/write and colorspace conversion with Arm64 HW intrinsics * Improve ReadNv12 x86 SSE path --- Ryujinx.Graphics.Vic/Blender.cs | 121 +++++++++++- Ryujinx.Graphics.Vic/Image/SurfaceReader.cs | 109 ++++++++++- Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs | 203 ++++++++++++++++++++ 3 files changed, 420 insertions(+), 13 deletions(-) diff --git a/Ryujinx.Graphics.Vic/Blender.cs b/Ryujinx.Graphics.Vic/Blender.cs index b6ca35ae8..e49b59032 100644 --- a/Ryujinx.Graphics.Vic/Blender.cs +++ b/Ryujinx.Graphics.Vic/Blender.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace Ryujinx.Graphics.Vic @@ -17,10 +18,18 @@ namespace Ryujinx.Graphics.Vic int x2 = Math.Min(src.Width, x1 + targetRect.Width); int y2 = Math.Min(src.Height, y1 + targetRect.Height); - if (Sse41.IsSupported && ((x1 | x2) & 3) == 0) + if (((x1 | x2) & 3) == 0) { - BlendOneSse41(dst, src, ref slot, x1, y1, x2, y2); - return; + if (Sse41.IsSupported) + { + BlendOneSse41(dst, src, ref slot, x1, y1, x2, y2); + return; + } + else if (AdvSimd.IsSupported) + { + BlendOneAdvSimd(dst, src, ref slot, x1, y1, x2, y2); + return; + } } for (int y = y1; y < y2; y++) @@ -105,6 +114,84 @@ namespace Ryujinx.Graphics.Vic } } + private unsafe static void BlendOneAdvSimd(Surface dst, Surface src, ref SlotStruct slot, int x1, int y1, int x2, int y2) + { + Debug.Assert(((x1 | x2) & 3) == 0); + + ref MatrixStruct mtx = ref slot.ColorMatrixStruct; + + Vector128 col1 = Vector128.Create(mtx.MatrixCoeff00, mtx.MatrixCoeff10, mtx.MatrixCoeff20, 0); + Vector128 col2 = Vector128.Create(mtx.MatrixCoeff01, mtx.MatrixCoeff11, mtx.MatrixCoeff21, 0); + Vector128 col3 = Vector128.Create(mtx.MatrixCoeff02, mtx.MatrixCoeff12, mtx.MatrixCoeff22, 0); + Vector128 col4 = Vector128.Create(mtx.MatrixCoeff03, mtx.MatrixCoeff13, mtx.MatrixCoeff23, 0); + + Vector128 rShift = Vector128.Create(-mtx.MatrixRShift); + Vector128 selMask = Vector128.Create(0, 0, 0, -1); + Vector128 clMin = Vector128.Create((ushort)slot.SlotConfig.SoftClampLow); + Vector128 clMax = Vector128.Create((ushort)slot.SlotConfig.SoftClampHigh); + + fixed (Pixel* srcPtr = src.Data, dstPtr = dst.Data) + { + Pixel* ip = srcPtr; + Pixel* op = dstPtr; + + if (mtx.MatrixEnable) + { + for (int y = y1; y < y2; y++, ip += src.Width, op += dst.Width) + { + for (int x = x1; x < x2; x += 4) + { + Vector128 pixel12 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x)); + Vector128 pixel34 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x + 2)); + + Vector128 pixel1 = AdvSimd.ZeroExtendWideningLower(pixel12.GetLower()); + Vector128 pixel2 = AdvSimd.ZeroExtendWideningUpper(pixel12); + Vector128 pixel3 = AdvSimd.ZeroExtendWideningLower(pixel34.GetLower()); + Vector128 pixel4 = AdvSimd.ZeroExtendWideningUpper(pixel34); + + Vector128 t1 = MatrixMultiplyAdvSimd(pixel1.AsInt32(), col1, col2, col3, col4, rShift, selMask); + Vector128 t2 = MatrixMultiplyAdvSimd(pixel2.AsInt32(), col1, col2, col3, col4, rShift, selMask); + Vector128 t3 = MatrixMultiplyAdvSimd(pixel3.AsInt32(), col1, col2, col3, col4, rShift, selMask); + Vector128 t4 = MatrixMultiplyAdvSimd(pixel4.AsInt32(), col1, col2, col3, col4, rShift, selMask); + + Vector64 lower1 = AdvSimd.ExtractNarrowingSaturateUnsignedLower(t1); + Vector64 lower3 = AdvSimd.ExtractNarrowingSaturateUnsignedLower(t3); + + pixel12 = AdvSimd.ExtractNarrowingSaturateUnsignedUpper(lower1, t2); + pixel34 = AdvSimd.ExtractNarrowingSaturateUnsignedUpper(lower3, t4); + + pixel12 = AdvSimd.Min(pixel12, clMax); + pixel34 = AdvSimd.Min(pixel34, clMax); + pixel12 = AdvSimd.Max(pixel12, clMin); + pixel34 = AdvSimd.Max(pixel34, clMin); + + AdvSimd.Store((ushort*)(op + (uint)x + 0), pixel12); + AdvSimd.Store((ushort*)(op + (uint)x + 2), pixel34); + } + } + } + else + { + for (int y = y1; y < y2; y++, ip += src.Width, op += dst.Width) + { + for (int x = x1; x < x2; x += 4) + { + Vector128 pixel12 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x)); + Vector128 pixel34 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x + 2)); + + pixel12 = AdvSimd.Min(pixel12, clMax); + pixel34 = AdvSimd.Min(pixel34, clMax); + pixel12 = AdvSimd.Max(pixel12, clMin); + pixel34 = AdvSimd.Max(pixel34, clMin); + + AdvSimd.Store((ushort*)(op + (uint)x + 0), pixel12); + AdvSimd.Store((ushort*)(op + (uint)x + 2), pixel34); + } + } + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void MatrixMultiply(ref MatrixStruct mtx, int x, int y, int z, out int r, out int g, out int b) { @@ -159,5 +246,33 @@ namespace Ryujinx.Graphics.Vic return res; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 MatrixMultiplyAdvSimd( + Vector128 pixel, + Vector128 col1, + Vector128 col2, + Vector128 col3, + Vector128 col4, + Vector128 rShift, + Vector128 selectMask) + { + Vector128 x = AdvSimd.DuplicateSelectedScalarToVector128(pixel, 0); + Vector128 y = AdvSimd.DuplicateSelectedScalarToVector128(pixel, 1); + Vector128 z = AdvSimd.DuplicateSelectedScalarToVector128(pixel, 2); + + col1 = AdvSimd.Multiply(col1, x); + col2 = AdvSimd.Multiply(col2, y); + col3 = AdvSimd.Multiply(col3, z); + + Vector128 res = AdvSimd.Add(col3, AdvSimd.Add(col1, col2)); + + res = AdvSimd.ShiftArithmetic(res, rShift); + res = AdvSimd.Add(res, col4); + res = AdvSimd.ShiftRightArithmetic(res, 8); + res = AdvSimd.BitwiseSelect(selectMask, pixel, res); + + return res; + } } } diff --git a/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs b/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs index d9717bf85..10fd9d8d3 100644 --- a/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs +++ b/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Vic.Types; using System; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using static Ryujinx.Graphics.Vic.Image.SurfaceCommon; @@ -54,7 +55,7 @@ namespace Ryujinx.Graphics.Vic.Image (byte)4, (byte)6, (byte)7, (byte)5, (byte)8, (byte)10, (byte)11, (byte)9, (byte)12, (byte)14, (byte)15, (byte)13); - Vector128 alphaMask = Vector128.Create(0xffUL << 48).AsInt16(); + Vector128 alphaMask = Vector128.Create(0xff << 24).AsInt16(); int yStrideGap = yStride - width; int uvStrideGap = uvStride - input.UvWidth; @@ -95,6 +96,11 @@ namespace Ryujinx.Graphics.Vic.Image rgba2 = Ssse3.Shuffle(rgba2.AsByte(), shufMask).AsInt16(); rgba3 = Ssse3.Shuffle(rgba3.AsByte(), shufMask).AsInt16(); + rgba0 = Sse2.Or(rgba0, alphaMask); + rgba1 = Sse2.Or(rgba1, alphaMask); + rgba2 = Sse2.Or(rgba2, alphaMask); + rgba3 = Sse2.Or(rgba3, alphaMask); + Vector128 rgba16_0 = Sse41.ConvertToVector128Int16(rgba0.AsByte()); Vector128 rgba16_1 = Sse41.ConvertToVector128Int16(HighToLow(rgba0.AsByte())); Vector128 rgba16_2 = Sse41.ConvertToVector128Int16(rgba1.AsByte()); @@ -104,15 +110,6 @@ namespace Ryujinx.Graphics.Vic.Image Vector128 rgba16_6 = Sse41.ConvertToVector128Int16(rgba3.AsByte()); Vector128 rgba16_7 = Sse41.ConvertToVector128Int16(HighToLow(rgba3.AsByte())); - rgba16_0 = Sse2.Or(rgba16_0, alphaMask); - rgba16_1 = Sse2.Or(rgba16_1, alphaMask); - rgba16_2 = Sse2.Or(rgba16_2, alphaMask); - rgba16_3 = Sse2.Or(rgba16_3, alphaMask); - rgba16_4 = Sse2.Or(rgba16_4, alphaMask); - rgba16_5 = Sse2.Or(rgba16_5, alphaMask); - rgba16_6 = Sse2.Or(rgba16_6, alphaMask); - rgba16_7 = Sse2.Or(rgba16_7, alphaMask); - rgba16_0 = Sse2.ShiftLeftLogical(rgba16_0, 2); rgba16_1 = Sse2.ShiftLeftLogical(rgba16_1, 2); rgba16_2 = Sse2.ShiftLeftLogical(rgba16_2, 2); @@ -149,6 +146,98 @@ namespace Ryujinx.Graphics.Vic.Image } } } + else if (AdvSimd.Arm64.IsSupported) + { + Vector128 alphaMask = Vector128.Create(0xffu << 24).AsInt32(); + + int yStrideGap = yStride - width; + int uvStrideGap = uvStride - input.UvWidth; + + int widthTrunc = width & ~0xf; + + fixed (Pixel* dstPtr = output.Data) + { + Pixel* op = dstPtr; + + fixed (byte* src0Ptr = input.Buffer0, src1Ptr = input.Buffer1) + { + byte* i0p = src0Ptr; + + for (int y = 0; y < height; y++) + { + byte* i1p = src1Ptr + (y >> 1) * uvStride; + + int x = 0; + + for (; x < widthTrunc; x += 16, i0p += 16, i1p += 16) + { + Vector128 ya = AdvSimd.LoadVector128(i0p); + Vector128 uv = AdvSimd.LoadVector128(i1p); + + Vector128 ya0 = AdvSimd.ZeroExtendWideningLower(ya.GetLower()).AsInt16(); + Vector128 ya1 = AdvSimd.ZeroExtendWideningUpper(ya).AsInt16(); + + Vector128 uv0 = AdvSimd.Arm64.ZipLow(uv.AsInt16(), uv.AsInt16()); + Vector128 uv1 = AdvSimd.Arm64.ZipHigh(uv.AsInt16(), uv.AsInt16()); + + ya0 = AdvSimd.ShiftLeftLogical(ya0, 8); + ya1 = AdvSimd.ShiftLeftLogical(ya1, 8); + + Vector128 rgba0 = AdvSimd.Arm64.ZipLow(ya0, uv0); + Vector128 rgba1 = AdvSimd.Arm64.ZipHigh(ya0, uv0); + Vector128 rgba2 = AdvSimd.Arm64.ZipLow(ya1, uv1); + Vector128 rgba3 = AdvSimd.Arm64.ZipHigh(ya1, uv1); + + rgba0 = AdvSimd.ShiftRightLogicalAdd(alphaMask, rgba0.AsInt32(), 8).AsInt16(); + rgba1 = AdvSimd.ShiftRightLogicalAdd(alphaMask, rgba1.AsInt32(), 8).AsInt16(); + rgba2 = AdvSimd.ShiftRightLogicalAdd(alphaMask, rgba2.AsInt32(), 8).AsInt16(); + rgba3 = AdvSimd.ShiftRightLogicalAdd(alphaMask, rgba3.AsInt32(), 8).AsInt16(); + + Vector128 rgba16_0 = AdvSimd.ZeroExtendWideningLower(rgba0.AsByte().GetLower()).AsInt16(); + Vector128 rgba16_1 = AdvSimd.ZeroExtendWideningUpper(rgba0.AsByte()).AsInt16(); + Vector128 rgba16_2 = AdvSimd.ZeroExtendWideningLower(rgba1.AsByte().GetLower()).AsInt16(); + Vector128 rgba16_3 = AdvSimd.ZeroExtendWideningUpper(rgba1.AsByte()).AsInt16(); + Vector128 rgba16_4 = AdvSimd.ZeroExtendWideningLower(rgba2.AsByte().GetLower()).AsInt16(); + Vector128 rgba16_5 = AdvSimd.ZeroExtendWideningUpper(rgba2.AsByte()).AsInt16(); + Vector128 rgba16_6 = AdvSimd.ZeroExtendWideningLower(rgba3.AsByte().GetLower()).AsInt16(); + Vector128 rgba16_7 = AdvSimd.ZeroExtendWideningUpper(rgba3.AsByte()).AsInt16(); + + rgba16_0 = AdvSimd.ShiftLeftLogical(rgba16_0, 2); + rgba16_1 = AdvSimd.ShiftLeftLogical(rgba16_1, 2); + rgba16_2 = AdvSimd.ShiftLeftLogical(rgba16_2, 2); + rgba16_3 = AdvSimd.ShiftLeftLogical(rgba16_3, 2); + rgba16_4 = AdvSimd.ShiftLeftLogical(rgba16_4, 2); + rgba16_5 = AdvSimd.ShiftLeftLogical(rgba16_5, 2); + rgba16_6 = AdvSimd.ShiftLeftLogical(rgba16_6, 2); + rgba16_7 = AdvSimd.ShiftLeftLogical(rgba16_7, 2); + + AdvSimd.Store((short*)(op + (uint)x + 0), rgba16_0); + AdvSimd.Store((short*)(op + (uint)x + 2), rgba16_1); + AdvSimd.Store((short*)(op + (uint)x + 4), rgba16_2); + AdvSimd.Store((short*)(op + (uint)x + 6), rgba16_3); + AdvSimd.Store((short*)(op + (uint)x + 8), rgba16_4); + AdvSimd.Store((short*)(op + (uint)x + 10), rgba16_5); + AdvSimd.Store((short*)(op + (uint)x + 12), rgba16_6); + AdvSimd.Store((short*)(op + (uint)x + 14), rgba16_7); + } + + for (; x < width; x++, i1p += (x & 1) * 2) + { + Pixel* px = op + (uint)x; + + px->R = Upsample(*i0p++); + px->G = Upsample(*i1p); + px->B = Upsample(*(i1p + 1)); + px->A = 0x3ff; + } + + op += width; + i0p += yStrideGap; + i1p += uvStrideGap; + } + } + } + } else { for (int y = 0; y < height; y++) diff --git a/Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs b/Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs index 297a04b6d..37d261f9e 100644 --- a/Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs +++ b/Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Vic.Types; using System; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using static Ryujinx.Graphics.Vic.Image.SurfaceCommon; @@ -93,6 +94,64 @@ namespace Ryujinx.Graphics.Vic.Image } } } + else if (AdvSimd.IsSupported) + { + int widthTrunc = width & ~7; + int strideGap = stride - width * 4; + + fixed (Pixel* srcPtr = input.Data) + { + Pixel* ip = srcPtr; + + fixed (byte* dstPtr = dst) + { + byte* op = dstPtr; + + for (int y = 0; y < height; y++, ip += input.Width) + { + int x = 0; + + for (; x < widthTrunc; x += 8) + { + Vector128 pixel12 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x)); + Vector128 pixel34 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x + 2)); + Vector128 pixel56 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x + 4)); + Vector128 pixel78 = AdvSimd.LoadVector128((ushort*)(ip + (uint)x + 6)); + + pixel12 = AdvSimd.ShiftRightLogical(pixel12, 2); + pixel34 = AdvSimd.ShiftRightLogical(pixel34, 2); + pixel56 = AdvSimd.ShiftRightLogical(pixel56, 2); + pixel78 = AdvSimd.ShiftRightLogical(pixel78, 2); + + Vector64 lower12 = AdvSimd.ExtractNarrowingLower(pixel12.AsUInt16()); + Vector64 lower56 = AdvSimd.ExtractNarrowingLower(pixel56.AsUInt16()); + + Vector128 pixel1234 = AdvSimd.ExtractNarrowingUpper(lower12, pixel34.AsUInt16()); + Vector128 pixel5678 = AdvSimd.ExtractNarrowingUpper(lower56, pixel78.AsUInt16()); + + AdvSimd.Store(op + 0x00, pixel1234); + AdvSimd.Store(op + 0x10, pixel5678); + + op += 0x20; + } + + for (; x < width; x++) + { + Pixel* px = ip + (uint)x; + + *(op + 0) = Downsample(px->R); + *(op + 1) = Downsample(px->G); + *(op + 2) = Downsample(px->B); + *(op + 3) = Downsample(px->A); + + op += 4; + } + + op += strideGap; + } + } + } + } else { for (int y = 0; y < height; y++) @@ -302,6 +361,87 @@ namespace Ryujinx.Graphics.Vic.Image } } } + else if (AdvSimd.IsSupported) + { + Vector128 mask = Vector128.Create(0xffffUL).AsUInt16(); + + int widthTrunc = width & ~0xf; + int strideGap = yStride - width; + + fixed (Pixel* srcPtr = input.Data) + { + Pixel* ip = srcPtr; + + fixed (byte* dstPtr = dstY) + { + byte* op = dstPtr; + + for (int y = 0; y < height; y++, ip += input.Width) + { + int x = 0; + + for (; x < widthTrunc; x += 16) + { + byte* baseOffset = (byte*)(ip + (ulong)(uint)x); + + Vector128 pixelp1 = AdvSimd.LoadVector128((ushort*)baseOffset); + Vector128 pixelp2 = AdvSimd.LoadVector128((ushort*)(baseOffset + 0x10)); + Vector128 pixelp3 = AdvSimd.LoadVector128((ushort*)(baseOffset + 0x20)); + Vector128 pixelp4 = AdvSimd.LoadVector128((ushort*)(baseOffset + 0x30)); + Vector128 pixelp5 = AdvSimd.LoadVector128((ushort*)(baseOffset + 0x40)); + Vector128 pixelp6 = AdvSimd.LoadVector128((ushort*)(baseOffset + 0x50)); + Vector128 pixelp7 = AdvSimd.LoadVector128((ushort*)(baseOffset + 0x60)); + Vector128 pixelp8 = AdvSimd.LoadVector128((ushort*)(baseOffset + 0x70)); + + pixelp1 = AdvSimd.And(pixelp1, mask); + pixelp2 = AdvSimd.And(pixelp2, mask); + pixelp3 = AdvSimd.And(pixelp3, mask); + pixelp4 = AdvSimd.And(pixelp4, mask); + pixelp5 = AdvSimd.And(pixelp5, mask); + pixelp6 = AdvSimd.And(pixelp6, mask); + pixelp7 = AdvSimd.And(pixelp7, mask); + pixelp8 = AdvSimd.And(pixelp8, mask); + + Vector64 lowerp1 = AdvSimd.ExtractNarrowingLower(pixelp1.AsUInt32()); + Vector64 lowerp3 = AdvSimd.ExtractNarrowingLower(pixelp3.AsUInt32()); + Vector64 lowerp5 = AdvSimd.ExtractNarrowingLower(pixelp5.AsUInt32()); + Vector64 lowerp7 = AdvSimd.ExtractNarrowingLower(pixelp7.AsUInt32()); + + Vector128 pixelq1 = AdvSimd.ExtractNarrowingUpper(lowerp1, pixelp2.AsUInt32()); + Vector128 pixelq2 = AdvSimd.ExtractNarrowingUpper(lowerp3, pixelp4.AsUInt32()); + Vector128 pixelq3 = AdvSimd.ExtractNarrowingUpper(lowerp5, pixelp6.AsUInt32()); + Vector128 pixelq4 = AdvSimd.ExtractNarrowingUpper(lowerp7, pixelp8.AsUInt32()); + + Vector64 lowerq1 = AdvSimd.ExtractNarrowingLower(pixelq1.AsUInt32()); + Vector64 lowerq3 = AdvSimd.ExtractNarrowingLower(pixelq3.AsUInt32()); + + pixelq1 = AdvSimd.ExtractNarrowingUpper(lowerq1, pixelq2.AsUInt32()); + pixelq2 = AdvSimd.ExtractNarrowingUpper(lowerq3, pixelq4.AsUInt32()); + + pixelq1 = AdvSimd.ShiftRightLogical(pixelq1, 2); + pixelq2 = AdvSimd.ShiftRightLogical(pixelq2, 2); + + Vector64 pixelLower = AdvSimd.ExtractNarrowingLower(pixelq1.AsUInt16()); + + Vector128 pixel = AdvSimd.ExtractNarrowingUpper(pixelLower, pixelq2.AsUInt16()); + + AdvSimd.Store(op, pixel); + + op += 0x10; + } + + for (; x < width; x++) + { + Pixel* px = ip + (uint)x; + + *op++ = Downsample(px->R); + } + + op += strideGap; + } + } + } + } else { for (int y = 0; y < height; y++) @@ -392,6 +532,69 @@ namespace Ryujinx.Graphics.Vic.Image } } } + else if (AdvSimd.Arm64.IsSupported) + { + int widthTrunc = uvWidth & ~7; + int strideGap = uvStride - uvWidth * 2; + + fixed (Pixel* srcPtr = input.Data) + { + Pixel* ip = srcPtr; + + fixed (byte* dstPtr = dstUv) + { + byte* op = dstPtr; + + for (int y = 0; y < uvHeight; y++, ip += input.Width * 2) + { + int x = 0; + + for (; x < widthTrunc; x += 8) + { + byte* baseOffset = (byte*)ip + (ulong)(uint)x * 16; + + Vector128 pixel1 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x02)); + Vector128 pixel2 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x12)); + Vector128 pixel3 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x22)); + Vector128 pixel4 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x32)); + Vector128 pixel5 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x42)); + Vector128 pixel6 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x52)); + Vector128 pixel7 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x62)); + Vector128 pixel8 = AdvSimd.LoadAndReplicateToVector128((uint*)(baseOffset + 0x72)); + + Vector128 pixel12 = AdvSimd.Arm64.ZipLow(pixel1, pixel2); + Vector128 pixel34 = AdvSimd.Arm64.ZipLow(pixel3, pixel4); + Vector128 pixel56 = AdvSimd.Arm64.ZipLow(pixel5, pixel6); + Vector128 pixel78 = AdvSimd.Arm64.ZipLow(pixel7, pixel8); + + Vector128 pixel1234 = AdvSimd.Arm64.ZipLow(pixel12.AsUInt64(), pixel34.AsUInt64()); + Vector128 pixel5678 = AdvSimd.Arm64.ZipLow(pixel56.AsUInt64(), pixel78.AsUInt64()); + + pixel1234 = AdvSimd.ShiftRightLogical(pixel1234, 2); + pixel5678 = AdvSimd.ShiftRightLogical(pixel5678, 2); + + Vector64 pixelLower = AdvSimd.ExtractNarrowingLower(pixel1234.AsUInt16()); + + Vector128 pixel = AdvSimd.ExtractNarrowingUpper(pixelLower, pixel5678.AsUInt16()); + + AdvSimd.Store(op, pixel); + + op += 0x10; + } + + for (; x < uvWidth; x++) + { + Pixel* px = ip + (uint)(x << 1); + + *op++ = Downsample(px->G); + *op++ = Downsample(px->B); + } + + op += strideGap; + } + } + } + } else { for (int y = 0; y < uvHeight; y++) From f8beeeb7d3dca796d05a2ab1d7a8f1af3b7e3fb5 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 7 Feb 2023 13:55:59 -0300 Subject: [PATCH 335/737] Support safe blit on non-2D textures (#4374) * Support safe blit on non-2D textures (except multisample) * Change safe blit with different levels and layers to match CmdBlitImage path * Remove now unused variables * Multisample safe blit support --- Ryujinx.Graphics.Vulkan/HelperShader.cs | 285 ++++++++++++++---- .../ColorBlitMsFragmentShaderSource.frag | 11 + .../DepthBlitMsFragmentShaderSource.frag | 10 + .../Shaders/ShaderBinaries.cs | 176 +++++++++++ .../StencilBlitMsFragmentShaderSource.frag | 12 + Ryujinx.Graphics.Vulkan/TextureView.cs | 14 +- Ryujinx.Graphics.Vulkan/Window.cs | 2 + 7 files changed, 436 insertions(+), 74 deletions(-) create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 658516e6d..b8c21fe8e 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly ISampler _samplerLinear; private readonly ISampler _samplerNearest; private readonly IProgram _programColorBlit; + private readonly IProgram _programColorBlitMs; private readonly IProgram _programColorBlitClearAlpha; private readonly IProgram _programColorClearF; private readonly IProgram _programColorClearSI; @@ -34,7 +35,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programColorCopyToNonMs; private readonly IProgram _programColorDrawToMs; private readonly IProgram _programDepthBlit; + private readonly IProgram _programDepthBlitMs; private readonly IProgram _programStencilBlit; + private readonly IProgram _programStencilBlitMs; public HelperShader(VulkanRenderer gd, Device device) { @@ -62,6 +65,12 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); + _programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), @@ -160,6 +169,12 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); + _programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + if (gd.Capabilities.SupportsShaderStencilExport) { _programStencilBlit = gd.CreateProgramWithMinimalLayout(new[] @@ -167,18 +182,23 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); + + _programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); } } public void Blit( VulkanRenderer gd, TextureView src, - Auto dst, - int dstWidth, - int dstHeight, - VkFormat dstFormat, + TextureView dst, Extents2D srcRegion, Extents2D dstRegion, + int layers, + int levels, bool isDepthOrStencil, bool linearFilter, bool clearAlpha = false) @@ -187,13 +207,137 @@ namespace Ryujinx.Graphics.Vulkan using var cbs = gd.CommandBufferPool.Rent(); - if (isDepthOrStencil) + var dstFormat = dst.VkFormat; + var dstSamples = dst.Info.Samples; + + for (int l = 0; l < levels; l++) { - BlitDepthStencil(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion); + int srcWidth = Math.Max(1, src.Width >> l); + int srcHeight = Math.Max(1, src.Height >> l); + + int dstWidth = Math.Max(1, dst.Width >> l); + int dstHeight = Math.Max(1, dst.Height >> l); + + var mipSrcRegion = new Extents2D( + srcRegion.X1 >> l, + srcRegion.Y1 >> l, + srcRegion.X2 >> l, + srcRegion.Y2 >> l); + + var mipDstRegion = new Extents2D( + dstRegion.X1 >> l, + dstRegion.Y1 >> l, + dstRegion.X2 >> l, + dstRegion.Y2 >> l); + + for (int z = 0; z < layers; z++) + { + var srcView = Create2DLayerView(src, z, l); + var dstView = Create2DLayerView(dst, z, l); + + if (isDepthOrStencil) + { + BlitDepthStencil( + gd, + cbs, + srcView, + dst.GetImageViewForAttachment(), + dstWidth, + dstHeight, + dstSamples, + dstFormat, + mipSrcRegion, + mipDstRegion); + } + else + { + BlitColor( + gd, + cbs, + srcView, + dst.GetImageViewForAttachment(), + dstWidth, + dstHeight, + dstSamples, + dstFormat, + false, + mipSrcRegion, + mipDstRegion, + linearFilter, + clearAlpha); + } + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } } - else + } + + public void CopyColor( + VulkanRenderer gd, + CommandBufferScoped cbs, + TextureView src, + TextureView dst, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel, + int depth, + int levels) + { + for (int l = 0; l < levels; l++) { - BlitColor(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha); + int mipSrcLevel = srcLevel + l; + int mipDstLevel = dstLevel + l; + + int srcWidth = Math.Max(1, src.Width >> mipSrcLevel); + int srcHeight = Math.Max(1, src.Height >> mipSrcLevel); + + int dstWidth = Math.Max(1, dst.Width >> mipDstLevel); + int dstHeight = Math.Max(1, dst.Height >> mipDstLevel); + + var extents = new Extents2D( + 0, + 0, + Math.Min(srcWidth, dstWidth), + Math.Min(srcHeight, dstHeight)); + + for (int z = 0; z < depth; z++) + { + var srcView = Create2DLayerView(src, srcLayer + z, mipSrcLevel); + var dstView = Create2DLayerView(dst, dstLayer + z, mipDstLevel); + + BlitColor( + gd, + cbs, + srcView, + dstView.GetImageViewForAttachment(), + dstView.Width, + dstView.Height, + dstView.Info.Samples, + dstView.VkFormat, + dstView.Info.Format.IsDepthOrStencil(), + extents, + extents, + false); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } } } @@ -204,7 +348,9 @@ namespace Ryujinx.Graphics.Vulkan Auto dst, int dstWidth, int dstHeight, + int dstSamples, VkFormat dstFormat, + bool dstIsDepthOrStencil, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter, @@ -262,8 +408,25 @@ namespace Ryujinx.Graphics.Vulkan scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - _pipeline.SetProgram(clearAlpha ? _programColorBlitClearAlpha : _programColorBlit); - _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); + if (dstIsDepthOrStencil) + { + _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + } + else if (src.Info.Target.IsMultisample()) + { + _pipeline.SetProgram(_programColorBlitMs); + } + else if (clearAlpha) + { + _pipeline.SetProgram(_programColorBlitClearAlpha); + } + else + { + _pipeline.SetProgram(_programColorBlit); + } + + _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, dstIsDepthOrStencil, dstFormat); _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); _pipeline.SetScissors(scissors); @@ -275,6 +438,12 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetViewports(viewports, false); _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); + + if (dstIsDepthOrStencil) + { + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always)); + } + _pipeline.Finish(gd, cbs); gd.BufferManager.Delete(bufferHandle); @@ -287,6 +456,7 @@ namespace Ryujinx.Graphics.Vulkan Auto dst, int dstWidth, int dstHeight, + int dstSamples, VkFormat dstFormat, Extents2D srcRegion, Extents2D dstRegion) @@ -339,7 +509,7 @@ namespace Ryujinx.Graphics.Vulkan scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat); + _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat); _pipeline.SetScissors(scissors); _pipeline.SetViewports(viewports, false); _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); @@ -406,12 +576,12 @@ namespace Ryujinx.Graphics.Vulkan if (isDepth) { - _pipeline.SetProgram(_programDepthBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); } else { - _pipeline.SetProgram(_programStencilBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); } @@ -795,33 +965,25 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); - if (src.Info.Target == Target.Texture2DMultisampleArray || - dst.Info.Target == Target.Texture2DMultisampleArray) + for (int z = 0; z < depth; z++) { - for (int z = 0; z < depth; z++) - { - var srcView = Create2DLayerView(src, srcLayer + z, format); - var dstView = Create2DLayerView(dst, dstLayer + z); - - _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); - _pipeline.SetImage(0, dstView, format); - - _pipeline.DispatchCompute(dispatchX, dispatchY, 1); - - srcView.Release(); - dstView.Release(); - } - } - else - { - var srcView = Create2DLayerView(src, srcLayer, format); + var srcView = Create2DLayerView(src, srcLayer + z, 0, format); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); - _pipeline.SetImage(0, dst, format); + _pipeline.SetImage(0, dstView, format); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); - srcView.Release(); + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } } gd.BufferManager.Delete(bufferHandle); @@ -906,36 +1068,14 @@ namespace Ryujinx.Graphics.Vulkan var format = GetFormat(src.Info.BytesPerPixel); var vkFormat = FormatTable.GetFormat(format); - if (src.Info.Target == Target.Texture2DMultisampleArray || - dst.Info.Target == Target.Texture2DMultisampleArray) + for (int z = 0; z < depth; z++) { - for (int z = 0; z < depth; z++) - { - var srcView = Create2DLayerView(src, srcLayer + z, format); - var dstView = Create2DLayerView(dst, dstLayer + z); - - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null); - _pipeline.SetRenderTarget( - ((TextureView)dstView).GetView(format).GetImageViewForAttachment(), - (uint)dst.Width, - (uint)dst.Height, - (uint)samples, - false, - vkFormat); - - _pipeline.Draw(4, 1, 0, 0); - - srcView.Release(); - dstView.Release(); - } - } - else - { - var srcView = Create2DLayerView(src, srcLayer, format); + var srcView = Create2DLayerView(src, srcLayer + z, 0, format); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null); _pipeline.SetRenderTarget( - dst.GetView(format).GetImageViewForAttachment(), + ((TextureView)dstView).GetView(format).GetImageViewForAttachment(), (uint)dst.Width, (uint)dst.Height, (uint)samples, @@ -944,7 +1084,15 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.Draw(4, 1, 0, 0); - srcView.Release(); + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } } gd.BufferManager.Delete(bufferHandle); @@ -1001,14 +1149,18 @@ namespace Ryujinx.Graphics.Vulkan return (samplesInXLog2, samplesInYLog2); } - private static ITexture Create2DLayerView(TextureView from, int layer, GAL.Format? format = null) + private static TextureView Create2DLayerView(TextureView from, int layer, int level, GAL.Format? format = null) { + if (from.Info.Target == Target.Texture2D && level == 0 && (format == null || format.Value == from.Info.Format)) + { + return from; + } + var target = from.Info.Target switch { Target.Texture1DArray => Target.Texture1D, - Target.Texture2DArray => Target.Texture2D, Target.Texture2DMultisampleArray => Target.Texture2DMultisample, - _ => from.Info.Target + _ => Target.Texture2D }; var info = new TextureCreateInfo( @@ -1028,7 +1180,7 @@ namespace Ryujinx.Graphics.Vulkan from.Info.SwizzleB, from.Info.SwizzleA); - return from.CreateView(info, layer, 0); + return from.CreateViewImpl(info, layer, level); } private static GAL.Format GetFormat(int bytesPerPixel) @@ -1177,6 +1329,7 @@ namespace Ryujinx.Graphics.Vulkan { _programColorBlitClearAlpha.Dispose(); _programColorBlit.Dispose(); + _programColorBlitMs.Dispose(); _programColorClearF.Dispose(); _programColorClearSI.Dispose(); _programColorClearUI.Dispose(); @@ -1186,7 +1339,9 @@ namespace Ryujinx.Graphics.Vulkan _programColorCopyToNonMs.Dispose(); _programColorDrawToMs.Dispose(); _programDepthBlit.Dispose(); + _programDepthBlitMs.Dispose(); _programStencilBlit?.Dispose(); + _programStencilBlitMs?.Dispose(); _samplerNearest.Dispose(); _samplerLinear.Dispose(); _pipeline.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag new file mode 100644 index 000000000..71145e029 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag @@ -0,0 +1,11 @@ +#version 450 core + +layout (binding = 0, set = 2) uniform sampler2DMS tex; + +layout (location = 0) in vec2 tex_coord; +layout (location = 0) out vec4 colour; + +void main() +{ + colour = texelFetch(tex, ivec2(tex_coord * vec2(textureSize(tex).xy)), gl_SampleID); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag new file mode 100644 index 000000000..c93c7e7fe --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag @@ -0,0 +1,10 @@ +#version 450 core + +layout (binding = 0, set = 2) uniform sampler2DMS texDepth; + +layout (location = 0) in vec2 tex_coord; + +void main() +{ + gl_FragDepth = texelFetch(texDepth, ivec2(tex_coord * vec2(textureSize(texDepth).xy)), gl_SampleID).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index 5a2acf221..c9df894bc 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -329,6 +329,61 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + public static readonly byte[] ColorBlitMsFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, + 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + public static readonly byte[] ColorBlitVertexShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3F, 0x00, 0x00, 0x00, @@ -1502,6 +1557,64 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + public static readonly byte[] DepthBlitMsFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, + 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, + 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x53, + 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x6F, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + public static readonly byte[] StencilBlitFragmentShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, @@ -1549,5 +1662,68 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + + public static readonly byte[] StencilBlitMsFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, + 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, + 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xA3, 0x13, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, + 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, + 0x72, 0x61, 0x67, 0x53, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x53, + 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag new file mode 100644 index 000000000..7e26672a8 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag @@ -0,0 +1,12 @@ +#version 450 core + +#extension GL_ARB_shader_stencil_export : require + +layout (binding = 0, set = 2) uniform isampler2DMS texStencil; + +layout (location = 0) in vec2 tex_coord; + +void main() +{ + gl_FragStencilRefARB = texelFetch(texStencil, ivec2(tex_coord * vec2(textureSize(texStencil).xy)), gl_SampleID).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index 3ee196756..e58b743df 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -368,20 +368,16 @@ namespace Ryujinx.Graphics.Vulkan bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - if (VulkanConfiguration.UseSlowSafeBlitOnAmd && - (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk) && - src.Info.Target == Target.Texture2D && - dst.Info.Target == Target.Texture2D) + if (VulkanConfiguration.UseSlowSafeBlitOnAmd && (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk)) { _gd.HelperShader.Blit( _gd, src, - dst.GetIdentityImageView(), - dst.Width, - dst.Height, - dst.VkFormat, + dst, srcRegion, dstRegion, + layers, + levels, isDepthOrStencil, linearFilter); @@ -501,7 +497,7 @@ namespace Ryujinx.Graphics.Vulkan return CreateViewImpl(info, firstLayer, firstLevel); } - private TextureView CreateViewImpl(TextureCreateInfo info, int firstLayer, int firstLevel) + public TextureView CreateViewImpl(TextureCreateInfo info, int firstLayer, int firstLevel) { return new TextureView(_gd, _device, info, Storage, FirstLayer + firstLayer, FirstLevel + firstLevel); } diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index dc4dc6be9..a90a824df 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -342,7 +342,9 @@ namespace Ryujinx.Graphics.Vulkan _swapchainImageViews[nextImage], _width, _height, + 1, _format, + false, new Extents2D(srcX0, srcY0, srcX1, srcY1), new Extents2D(dstX0, dstY1, dstX1, dstY0), true, From 59755818ef79f494d87c4e9d2b8372b54b38cb9d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 8 Feb 2023 01:28:53 +0100 Subject: [PATCH 336/737] Add ChangeVSyncMode() call to Avalonia render loop (#4379) --- Ryujinx.Ava/AppHost.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 6146a7d9e..242c84e7f 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -53,7 +53,6 @@ using Key = Ryujinx.Input.Key; using MouseButton = Ryujinx.Input.MouseButton; using Size = Avalonia.Size; using Switch = Ryujinx.HLE.Switch; -using WindowState = Avalonia.Controls.WindowState; namespace Ryujinx.Ava { @@ -766,7 +765,7 @@ namespace Ryujinx.Ava } } - private unsafe void RenderLoop() + private void RenderLoop() { Dispatcher.UIThread.InvokeAsync(() => { @@ -802,6 +801,8 @@ namespace Ryujinx.Ava Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Translator.IsReadyForTranslation.Set(); + _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + while (_isActive) { _ticks += _chrono.ElapsedTicks; From 96cf242bcf168b9f9e6a1e27200529466217f396 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 8 Feb 2023 04:48:09 -0300 Subject: [PATCH 337/737] Handle mismatching texture size with copy dependencies (#4364) * Handle mismatching texture size with copy dependencies * Create copy and render textures with the minimum possible size * Only align width for comparisons, assume that height is always exact * Fix IsExactMatch size check * Allow sampler and copy textures to match textures with larger width * Delete texture ChangeSize related code * Move AdjustSize to TextureInfo and give it a better name, adjust usages * Fix GetMinimumWidthInGob when minimumWidth > width * Only update render targets that are actually cleared for clear Avoids creating textures with incorrect sizes * Delete UpdateRenderTargetState method that is not needed anymore Clears now only ever sets the render targets that will be cleared rather than all of them --- .../Engine/Threed/DrawManager.cs | 32 +-- .../Engine/Threed/RenderTargetUpdateFlags.cs | 41 ++++ .../Engine/Threed/StateUpdater.cs | 15 +- .../Engine/Threed/ThreedClass.cs | 7 +- Ryujinx.Graphics.Gpu/Image/Texture.cs | 143 ++----------- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 192 +++++------------- .../Image/TextureCompatibility.cs | 126 +++++------- Ryujinx.Graphics.Gpu/Image/TextureInfo.cs | 85 ++++++++ Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 16 -- Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 17 +- .../Image/TextureSearchFlags.cs | 1 - Ryujinx.Graphics.Gpu/Window.cs | 2 +- 12 files changed, 271 insertions(+), 406 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 0f249512b..61f227d93 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -725,10 +725,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return; } + bool clearDepth = (argument & 1) != 0; + bool clearStencil = (argument & 2) != 0; + uint componentMask = (uint)((argument >> 2) & 0xf); int index = (argument >> 6) & 0xf; int layer = (argument >> 10) & 0x3ff; - engine.UpdateRenderTargetState(useControl: false, layered: layer != 0 || layerCount > 1, singleUse: index); + RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor; + + if (layer != 0 || layerCount > 1) + { + updateFlags |= RenderTargetUpdateFlags.Layered; + } + + if (clearDepth || clearStencil) + { + updateFlags |= RenderTargetUpdateFlags.UpdateDepthStencil; + } + + engine.UpdateRenderTargetState(updateFlags, singleUse: componentMask != 0 ? index : -1); // If there is a mismatch on the host clip region and the one explicitly defined by the guest // on the screen scissor state, then we need to force only one texture to be bound to avoid @@ -788,18 +803,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.SetScissors(scissors); } - if (clipMismatch) - { - _channel.TextureManager.UpdateRenderTarget(index); - } - else - { - _channel.TextureManager.UpdateRenderTargets(); - } - - bool clearDepth = (argument & 1) != 0; - bool clearStencil = (argument & 2) != 0; - uint componentMask = (uint)((argument >> 2) & 0xf); + _channel.TextureManager.UpdateRenderTargets(); if (componentMask != 0) { @@ -841,7 +845,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed engine.UpdateScissorState(); } - engine.UpdateRenderTargetState(useControl: true); + engine.UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll); if (renderEnable == ConditionalRenderEnabled.Host) { diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs new file mode 100644 index 000000000..cf2e818ce --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs @@ -0,0 +1,41 @@ +using System; + +namespace Ryujinx.Graphics.Gpu.Engine.Threed +{ + /// + /// Flags indicating how the render targets should be updated. + /// + [Flags] + enum RenderTargetUpdateFlags + { + /// + /// No flags. + /// + None = 0, + + /// + /// Get render target index from the control register. + /// + UseControl = 1 << 0, + + /// + /// Indicates that all render targets are 2D array textures. + /// + Layered = 1 << 1, + + /// + /// Indicates that only a single color target will be used. + /// + SingleColor = 1 << 2, + + /// + /// Indicates that the depth-stencil target will be used. + /// + UpdateDepthStencil = 1 << 3, + + /// + /// Default update flags for draw. + /// + UpdateAll = UseControl | UpdateDepthStencil + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 9b59009cf..9b58e0148 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -402,20 +402,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// private void UpdateRenderTargetState() { - UpdateRenderTargetState(true); + UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll); } /// /// Updates render targets (color and depth-stencil buffers) based on current render target state. /// - /// Use draw buffers information from render target control register - /// Indicates if the texture is layered + /// Flags indicating which render targets should be updated and how /// If this is not -1, it indicates that only the given indexed target will be used. - public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1) + public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1) { var memoryManager = _channel.MemoryManager; var rtControl = _state.State.RtControl; + bool useControl = updateFlags.HasFlag(RenderTargetUpdateFlags.UseControl); + bool layered = updateFlags.HasFlag(RenderTargetUpdateFlags.Layered); + bool singleColor = updateFlags.HasFlag(RenderTargetUpdateFlags.SingleColor); + int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets; var msaaMode = _state.State.RtMsaaMode; @@ -438,7 +441,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed var colorState = _state.State.RtColorState[rtIndex]; - if (index >= count || !IsRtEnabled(colorState)) + if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse)) { changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null); @@ -478,7 +481,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Image.Texture depthStencil = null; - if (dsEnable) + if (dsEnable && updateFlags.HasFlag(RenderTargetUpdateFlags.UpdateDepthStencil)) { var dsState = _state.State.RtDepthStencilState; var dsSize = _state.State.RtDepthStencilSize; diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 19eb8b46e..9a447a0bd 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -139,12 +139,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// /// Updates render targets (color and depth-stencil buffers) based on current render target state. /// - /// Use draw buffers information from render target control register - /// Indicates if the texture is layered + /// Flags indicating which render targets should be updated and how /// If this is not -1, it indicates that only the given indexed target will be used. - public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1) + public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1) { - _stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse); + _stateUpdater.UpdateRenderTargetState(updateFlags, singleUse); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 5ed9b2a07..352a828d1 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; @@ -89,12 +88,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// public TextureGroup Group { get; private set; } - /// - /// Set when a texture has been changed size. This indicates that it may need to be - /// changed again when obtained as a sampler. - /// - public bool ChangedSize { get; private set; } - /// /// Set when a texture's GPU VA has ever been partially or fully unmapped. /// This indicates that the range must be fully checked when matching the texture. @@ -410,122 +403,6 @@ namespace Ryujinx.Graphics.Gpu.Image Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo); } - /// - /// Changes the texture size. - /// - /// - /// This operation may also change the size of all mipmap levels, including from the parent - /// and other possible child textures, to ensure that all sizes are consistent. - /// - /// The new texture width - /// The new texture height - /// The new texture depth (for 3D textures) or layers (for layered textures) - public void ChangeSize(int width, int height, int depthOrLayers) - { - int blockWidth = Info.FormatInfo.BlockWidth; - int blockHeight = Info.FormatInfo.BlockHeight; - - width <<= FirstLevel; - height <<= FirstLevel; - - if (Target == Target.Texture3D) - { - depthOrLayers <<= FirstLevel; - } - else - { - depthOrLayers = _viewStorage.Info.DepthOrLayers; - } - - _viewStorage.RecreateStorageOrView(width, height, blockWidth, blockHeight, depthOrLayers); - - foreach (Texture view in _viewStorage._views) - { - int viewWidth = Math.Max(1, width >> view.FirstLevel); - int viewHeight = Math.Max(1, height >> view.FirstLevel); - - int viewDepthOrLayers; - - if (view.Info.Target == Target.Texture3D) - { - viewDepthOrLayers = Math.Max(1, depthOrLayers >> view.FirstLevel); - } - else - { - viewDepthOrLayers = view.Info.DepthOrLayers; - } - - view.RecreateStorageOrView(viewWidth, viewHeight, blockWidth, blockHeight, viewDepthOrLayers); - } - } - - /// - /// Recreates the texture storage (or view, in the case of child textures) of this texture. - /// This allows recreating the texture with a new size. - /// A copy is automatically performed from the old to the new texture. - /// - /// The new texture width - /// The new texture height - /// The block width related to the given width - /// The block height related to the given height - /// The new texture depth (for 3D textures) or layers (for layered textures) - private void RecreateStorageOrView(int width, int height, int blockWidth, int blockHeight, int depthOrLayers) - { - RecreateStorageOrView( - BitUtils.DivRoundUp(width * Info.FormatInfo.BlockWidth, blockWidth), - BitUtils.DivRoundUp(height * Info.FormatInfo.BlockHeight, blockHeight), - depthOrLayers); - } - - /// - /// Recreates the texture storage (or view, in the case of child textures) of this texture. - /// This allows recreating the texture with a new size. - /// A copy is automatically performed from the old to the new texture. - /// - /// The new texture width - /// The new texture height - /// The new texture depth (for 3D textures) or layers (for layered textures) - private void RecreateStorageOrView(int width, int height, int depthOrLayers) - { - ChangedSize = true; - - SetInfo(new TextureInfo( - Info.GpuAddress, - width, - height, - depthOrLayers, - Info.Levels, - Info.SamplesInX, - Info.SamplesInY, - Info.Stride, - Info.IsLinear, - Info.GobBlocksInY, - Info.GobBlocksInZ, - Info.GobBlocksInTileX, - Info.Target, - Info.FormatInfo, - Info.DepthStencilMode, - Info.SwizzleR, - Info.SwizzleG, - Info.SwizzleB, - Info.SwizzleA)); - - TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor); - - if (_viewStorage != this) - { - ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, FirstLayer, FirstLevel)); - } - else - { - ITexture newStorage = _context.Renderer.CreateTexture(createInfo, ScaleFactor); - - HostTexture.CopyTo(newStorage, 0, 0); - - ReplaceStorage(newStorage); - } - } - /// /// Registers when a texture has had its data set after being scaled, and /// determines if it should be blacklisted from scaling to improve performance. @@ -1215,7 +1092,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// A value indicating how well this texture matches the given info public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags) { - TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0); + bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0; + + TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0); if (matchQuality == TextureMatchQuality.NoMatch) { @@ -1227,12 +1106,12 @@ namespace Ryujinx.Graphics.Gpu.Image return TextureMatchQuality.NoMatch; } - if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0, FirstLevel)) + if (!TextureCompatibility.SizeMatches(Info, info, forSampler)) { return TextureMatchQuality.NoMatch; } - if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0) + if ((flags & TextureSearchFlags.ForSampler) != 0) { if (!TextureCompatibility.SamplerParamsMatches(Info, info)) { @@ -1262,12 +1141,20 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Texture view information /// Texture view physical memory ranges + /// Indicates if the texture sizes must be exactly equal, or width is allowed to differ /// Layer size on the given texture /// Host GPU capabilities /// Texture view initial layer on this texture /// Texture view first mipmap level on this texture /// The level of compatiblilty a view with the given parameters created from this texture has - public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel) + public TextureViewCompatibility IsViewCompatible( + TextureInfo info, + MultiRange range, + bool exactSize, + int layerSize, + Capabilities caps, + out int firstLayer, + out int firstLevel) { TextureViewCompatibility result = TextureViewCompatibility.Full; @@ -1317,7 +1204,7 @@ namespace Ryujinx.Graphics.Gpu.Image return TextureViewCompatibility.LayoutIncompatible; } - result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel)); + result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel)); return result; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 9802a3dcc..1d5b1851f 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -210,8 +210,8 @@ namespace Ryujinx.Graphics.Gpu.Image ulong offset, FormatInfo formatInfo, bool shouldCreate, - bool preferScaling = true, - Size? sizeHint = null) + bool preferScaling, + Size sizeHint) { int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY(); int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ(); @@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureInfo info = new TextureInfo( copyTexture.Address.Pack() + offset, - width, + GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout), copyTexture.Height, copyTexture.Depth, 1, @@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Image flags |= TextureSearchFlags.NoCreate; } - Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint); + Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0); texture?.SynchronizeMemory(); @@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureInfo info = new TextureInfo( colorState.Address.Pack(), - width, + GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear), colorState.Height, colorState.Depth, 1, @@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Image int layerSize = !isLinear ? colorState.LayerSize * 4 : 0; - Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize, sizeHint); + Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize); texture?.SynchronizeMemory(); @@ -395,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureInfo info = new TextureInfo( dsState.Address.Pack(), - size.Width, + GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false), size.Height, size.Depth, 1, @@ -409,13 +409,41 @@ namespace Ryujinx.Graphics.Gpu.Image target, formatInfo); - Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint); + Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4); texture?.SynchronizeMemory(); return texture; } + /// + /// For block linear textures, gets the minimum width of the texture + /// that would still have the same number of GOBs per row as the original width. + /// + /// The possibly aligned texture width + /// The minimum width that the texture may have without losing data + /// Bytes per pixel of the texture format + /// True if the texture is linear, false for block linear + /// The minimum width of the texture with the same amount of GOBs per row + private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPerPixel, bool isLinear) + { + if (isLinear || (uint)minimumWidth >= (uint)width) + { + return width; + } + + // Calculate the minimum possible that would not cause data loss + // and would be still within the same GOB (aligned size would be the same). + // This is useful for render and copy operations, where we don't know the + // exact width of the texture, but it doesn't matter, as long the texture is + // at least as large as the region being rendered or copied. + + int alignment = 64 / bytesPerPixel; + int widthAligned = BitUtils.AlignUp(width, alignment); + + return Math.Clamp(widthAligned - alignment + 1, minimumWidth, widthAligned); + } + /// /// Tries to find an existing texture, or create a new one if not found. /// @@ -423,7 +451,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture search flags, defines texture comparison rules /// Texture information of the texture to be found or created /// Size in bytes of a single texture layer - /// A hint indicating the minimum used size for the texture /// Optional ranges of physical memory where the texture data is located /// The texture public Texture FindOrCreateTexture( @@ -431,7 +458,6 @@ namespace Ryujinx.Graphics.Gpu.Image TextureSearchFlags flags, TextureInfo info, int layerSize = 0, - Size? sizeHint = null, MultiRange? range = null) { bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0; @@ -512,8 +538,6 @@ namespace Ryujinx.Graphics.Gpu.Image if (texture != null) { - ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint); - texture.SynchronizeMemory(); return texture; @@ -568,6 +592,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible( info, range.Value, + isSamplerTexture, sizeInfo.LayerSize, _context.Capabilities, out int firstLayer, @@ -598,17 +623,15 @@ namespace Ryujinx.Graphics.Gpu.Image if (oInfo.Compatibility == TextureViewCompatibility.Full) { - TextureInfo adjInfo = AdjustSizes(overlap, info, oInfo.FirstLevel); - if (!isSamplerTexture) { - info = adjInfo; + // If this is not a sampler texture, the size might be different from the requested size, + // so we need to make sure the texture information has the correct size for this base texture, + // before creating the view. + info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel); } - texture = overlap.CreateView(adjInfo, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); - - ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint); - + texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); texture.SynchronizeMemory(); break; } @@ -682,6 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureViewCompatibility compatibility = texture.IsViewCompatible( overlap.Info, overlap.Range, + exactSize: true, overlap.LayerSize, _context.Capabilities, out int firstLayer, @@ -792,7 +816,11 @@ namespace Ryujinx.Graphics.Gpu.Image continue; } - TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel); + // Note: If we allow different sizes for those overlaps, + // we need to make sure that the "info" has the correct size for the parent texture here. + // Since this is not allowed right now, we don't need to do it. + + TextureInfo overlapInfo = overlap.Info; if (texture.ScaleFactor != overlap.ScaleFactor) { @@ -856,44 +884,6 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } - /// - /// Changes a texture's size to match the desired size for samplers, - /// or increases a texture's size to fit the region indicated by a size hint. - /// - /// The desired texture info - /// The texture to resize - /// True if the texture will be used for a sampler, false otherwise - /// A hint indicating the minimum used size for the texture - private void ChangeSizeIfNeeded(TextureInfo info, Texture texture, bool isSamplerTexture, Size? sizeHint) - { - if (isSamplerTexture) - { - // If this is used for sampling, the size must match, - // otherwise the shader would sample garbage data. - // To fix that, we create a new texture with the correct - // size, and copy the data from the old one to the new one. - - if (!TextureCompatibility.SizeMatches(texture.Info, info)) - { - texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers); - } - } - else if (sizeHint != null) - { - // A size hint indicates that data will be used within that range, at least. - // If the texture is smaller than the size hint, it must be enlarged to meet it. - // The maximum size is provided by the requested info, which generally has an aligned size. - - int width = Math.Max(texture.Info.Width, Math.Min(sizeHint.Value.Width, info.Width)); - int height = Math.Max(texture.Info.Height, Math.Min(sizeHint.Value.Height, info.Height)); - - if (texture.Info.Width != width || texture.Info.Height != height) - { - texture.ChangeSize(width, height, info.DepthOrLayers); - } - } - } - /// /// Attempt to find a texture on the short duration cache. /// @@ -1000,92 +990,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Adjusts the size of the texture information for a given mipmap level, - /// based on the size of a parent texture. - /// - /// The parent texture - /// The texture information to be adjusted - /// The first level of the texture view - /// The adjusted texture information with the new size - private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel) - { - // When the texture is used as view of another texture, we must - // ensure that the sizes are valid, otherwise data uploads would fail - // (and the size wouldn't match the real size used on the host API). - // Given a parent texture from where the view is created, we have the - // following rules: - // - The view size must be equal to the parent size, divided by (2 ^ l), - // where l is the first mipmap level of the view. The division result must - // be rounded down, and the result must be clamped to 1. - // - If the parent format is compressed, and the view format isn't, the - // view size is calculated as above, but the width and height of the - // view must be also divided by the compressed format block width and height. - // - If the parent format is not compressed, and the view is, the view - // size is calculated as described on the first point, but the width and height - // of the view must be also multiplied by the block width and height. - int width = Math.Max(1, parent.Info.Width >> firstLevel); - int height = Math.Max(1, parent.Info.Height >> firstLevel); - - if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed) - { - width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth); - height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight); - } - else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed) - { - width *= info.FormatInfo.BlockWidth; - height *= info.FormatInfo.BlockHeight; - } - - int depthOrLayers; - - if (info.Target == Target.Texture3D) - { - depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel); - } - else - { - depthOrLayers = info.DepthOrLayers; - } - - // 2D and 2D multisample textures are not considered compatible. - // This specific case is required for copies, where the source texture might be multisample. - // In this case, we inherit the parent texture multisample state. - Target target = info.Target; - int samplesInX = info.SamplesInX; - int samplesInY = info.SamplesInY; - - if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample) - { - target = Target.Texture2DMultisample; - samplesInX = parent.Info.SamplesInX; - samplesInY = parent.Info.SamplesInY; - } - - return new TextureInfo( - info.GpuAddress, - width, - height, - depthOrLayers, - info.Levels, - samplesInX, - samplesInY, - info.Stride, - info.IsLinear, - info.GobBlocksInY, - info.GobBlocksInZ, - info.GobBlocksInTileX, - target, - info.FormatInfo, - info.DepthStencilMode, - info.SwizzleR, - info.SwizzleG, - info.SwizzleB, - info.SwizzleA); - } - - /// /// Gets a texture creation information from texture information. /// This can be used to create new host textures. diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 7ec4c7ac2..e8061951b 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -380,42 +380,37 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Texture information of the texture view /// Texture information of the texture view to match against + /// Indicates if the sizes must be exactly equal /// Mipmap level of the texture view in relation to this texture /// The view compatibility level of the view sizes - public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, int level) + public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact, int level) { - Size size = GetAlignedSize(lhs, level); + Size lhsAlignedSize = GetAlignedSize(lhs, level); + Size rhsAlignedSize = GetAlignedSize(rhs); - Size otherSize = GetAlignedSize(rhs); + Size lhsSize = GetSizeInBlocks(lhs, level); + Size rhsSize = GetSizeInBlocks(rhs); TextureViewCompatibility result = TextureViewCompatibility.Full; // For copies, we can copy a subset of the 3D texture slices, // so the depth may be different in this case. - if (rhs.Target == Target.Texture3D && size.Depth != otherSize.Depth) + if (rhs.Target == Target.Texture3D && lhsSize.Depth != rhsSize.Depth) { result = TextureViewCompatibility.CopyOnly; } - if (size.Width == otherSize.Width && size.Height == otherSize.Height) + // Some APIs align the width for copy and render target textures, + // so the width may not match in this case for different uses of the same texture. + // To account for this, we compare the aligned width here. + // We expect height to always match exactly, if the texture is the same. + if (lhsAlignedSize.Width == rhsAlignedSize.Width && lhsSize.Height == rhsSize.Height) { - if (level > 0 && result == TextureViewCompatibility.Full) - { - // A resize should not change the aligned size of the largest mip. - // If it would, then create a copy dependency rather than a full view. - - Size mip0SizeLhs = GetAlignedSize(lhs); - Size mip0SizeRhs = GetLargestAlignedSize(rhs, level); - - if (mip0SizeLhs.Width != mip0SizeRhs.Width || mip0SizeLhs.Height != mip0SizeRhs.Height) - { - result = TextureViewCompatibility.CopyOnly; - } - } - - return result; + return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width + ? TextureViewCompatibility.CopyOnly + : result; } - else if (lhs.IsLinear && rhs.IsLinear) + else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height) { // Copy between linear textures with matching stride. int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment); @@ -454,57 +449,33 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Texture information to compare /// Texture information to compare with - /// True if the size matches, false otherwise - public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs) - { - return SizeMatches(lhs, rhs, alignSizes: false); - } - - /// - /// Checks if the texture sizes of the supplied texture informations match the given level - /// - /// Texture information to compare - /// Texture information to compare with - /// Mipmap level of this texture to compare with - /// True if the size matches with the level, false otherwise - public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, int level) - { - return Math.Max(1, lhs.Width >> level) == rhs.Width && - Math.Max(1, lhs.Height >> level) == rhs.Height && - Math.Max(1, lhs.GetDepth() >> level) == rhs.GetDepth(); - } - - /// - /// Checks if the texture sizes of the supplied texture informations match. - /// - /// Texture information to compare - /// Texture information to compare with - /// True to align the sizes according to the texture layout for comparison - /// Mip level of the lhs texture. Aligned sizes are compared for the largest mip + /// Indicates if the size must be exactly equal between the textures, or if is allowed to be larger /// True if the sizes matches, false otherwise - public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool alignSizes, int lhsLevel = 0) + public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact) { if (lhs.GetLayers() != rhs.GetLayers()) { return false; } - bool isTextureBuffer = lhs.Target == Target.TextureBuffer || rhs.Target == Target.TextureBuffer; + Size lhsSize = GetSizeInBlocks(lhs); + Size rhsSize = GetSizeInBlocks(rhs); - if (alignSizes && !isTextureBuffer) + if (exact || lhs.IsLinear || rhs.IsLinear) { - Size size0 = GetLargestAlignedSize(lhs, lhsLevel); - Size size1 = GetLargestAlignedSize(rhs, lhsLevel); - - return size0.Width == size1.Width && - size0.Height == size1.Height && - size0.Depth == size1.Depth; + return lhsSize.Width == rhsSize.Width && + lhsSize.Height == rhsSize.Height && + lhsSize.Depth == rhsSize.Depth; } else { - return lhs.Width == rhs.Width && - lhs.Height == rhs.Height && - lhs.GetDepth() == rhs.GetDepth(); + Size lhsAlignedSize = GetAlignedSize(lhs); + Size rhsAlignedSize = GetAlignedSize(rhs); + + return lhsAlignedSize.Width == rhsAlignedSize.Width && + lhsSize.Width >= rhsSize.Width && + lhsSize.Height == rhsSize.Height && + lhsSize.Depth == rhsSize.Depth; } } @@ -543,22 +514,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Gets the aligned sizes of the specified texture information, shifted to the largest mip from a given level. - /// The alignment depends on the texture layout and format bytes per pixel. - /// - /// Texture information to calculate the aligned size from - /// Mipmap level for texture views. Shifts the aligned size to represent the largest mip level - /// The aligned texture size of the largest mip level - public static Size GetLargestAlignedSize(TextureInfo info, int level) - { - int width = info.Width << level; - int height = info.Height << level; - int depth = info.GetDepth() << level; - - return GetAlignedSize(info, width, height, depth); - } - /// /// Gets the aligned sizes of the specified texture information. /// The alignment depends on the texture layout and format bytes per pixel. @@ -575,6 +530,25 @@ namespace Ryujinx.Graphics.Gpu.Image return GetAlignedSize(info, width, height, depth); } + /// + /// Gets the size in blocks for the given texture information. + /// For non-compressed formats, that's the same as the regular size. + /// + /// Texture information to calculate the aligned size from + /// Mipmap level for texture views + /// The texture size in blocks + public static Size GetSizeInBlocks(TextureInfo info, int level = 0) + { + int width = Math.Max(1, info.Width >> level); + int height = Math.Max(1, info.Height >> level); + int depth = Math.Max(1, info.GetDepth() >> level); + + return new Size( + BitUtils.DivRoundUp(width, info.FormatInfo.BlockWidth), + BitUtils.DivRoundUp(height, info.FormatInfo.BlockHeight), + depth); + } + /// /// Check if it's possible to create a view with the layout of the second texture information from the first. /// The layout information is composed of the Stride for linear textures, or GOB block size diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs index 65cc698b0..a7ee12bcc 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs @@ -1,5 +1,7 @@ +using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Texture; +using System; namespace Ryujinx.Graphics.Gpu.Image { @@ -292,5 +294,88 @@ namespace Ryujinx.Graphics.Gpu.Image layerSize); } } + + /// + /// Creates texture information for a given mipmap level of the specified parent texture and this information. + /// + /// The parent texture + /// The first level of the texture view + /// The adjusted texture information with the new size + public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel) + { + // When the texture is used as view of another texture, we must + // ensure that the sizes are valid, otherwise data uploads would fail + // (and the size wouldn't match the real size used on the host API). + // Given a parent texture from where the view is created, we have the + // following rules: + // - The view size must be equal to the parent size, divided by (2 ^ l), + // where l is the first mipmap level of the view. The division result must + // be rounded down, and the result must be clamped to 1. + // - If the parent format is compressed, and the view format isn't, the + // view size is calculated as above, but the width and height of the + // view must be also divided by the compressed format block width and height. + // - If the parent format is not compressed, and the view is, the view + // size is calculated as described on the first point, but the width and height + // of the view must be also multiplied by the block width and height. + int width = Math.Max(1, parent.Info.Width >> firstLevel); + int height = Math.Max(1, parent.Info.Height >> firstLevel); + + if (parent.Info.FormatInfo.IsCompressed && !FormatInfo.IsCompressed) + { + width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth); + height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight); + } + else if (!parent.Info.FormatInfo.IsCompressed && FormatInfo.IsCompressed) + { + width *= FormatInfo.BlockWidth; + height *= FormatInfo.BlockHeight; + } + + int depthOrLayers; + + if (Target == Target.Texture3D) + { + depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel); + } + else + { + depthOrLayers = DepthOrLayers; + } + + // 2D and 2D multisample textures are not considered compatible. + // This specific case is required for copies, where the source texture might be multisample. + // In this case, we inherit the parent texture multisample state. + Target target = Target; + int samplesInX = SamplesInX; + int samplesInY = SamplesInY; + + if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample) + { + target = Target.Texture2DMultisample; + samplesInX = parent.Info.SamplesInX; + samplesInY = parent.Info.SamplesInY; + } + + return new TextureInfo( + GpuAddress, + width, + height, + depthOrLayers, + Levels, + samplesInX, + samplesInY, + Stride, + IsLinear, + GobBlocksInY, + GobBlocksInZ, + GobBlocksInTileX, + target, + FormatInfo, + DepthStencilMode, + SwizzleR, + SwizzleG, + SwizzleB, + SwizzleA); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 083de64c5..266f62856 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -437,22 +437,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Update host framebuffer attachments based on currently bound render target buffers. - /// - /// - /// All attachments other than will be unbound. - /// - /// Index of the render target color to be updated - public void UpdateRenderTarget(int index) - { - new Span(_rtHostColors).Fill(null); - _rtHostColors[index] = _rtColors[index]?.HostTexture; - _rtHostDs = null; - - _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, null); - } - /// /// Update host framebuffer attachments based on currently bound render target buffers. /// diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index fc99fc997..0348ca014 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -77,22 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - if (texture.ChangedSize) - { - // Texture changed size at one point - it may be a different size than the sampler expects. - // This can be triggered when the size is changed by a size hint on copy or draw, but the texture has been sampled before. - - int baseLevel = descriptor.UnpackBaseLevel(); - int width = Math.Max(1, descriptor.UnpackWidth() >> baseLevel); - int height = Math.Max(1, descriptor.UnpackHeight() >> baseLevel); - - if (texture.Info.Width != width || texture.Info.Height != height) - { - texture.ChangeSize(width, height, texture.Info.DepthOrLayers); - } - } - - // Memory is automatically synchronized on texture creation. + // On the path above (texture not yet in the pool), memory is automatically synchronized on texture creation. texture.SynchronizeMemory(); } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs index aea7b167e..890bf1736 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs @@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Image enum TextureSearchFlags { None = 0, - Strict = 1 << 0, ForSampler = 1 << 1, ForCopy = 1 << 2, WithUpscale = 1 << 3, diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs index 90f8e40f9..06d0fddf4 100644 --- a/Ryujinx.Graphics.Gpu/Window.cs +++ b/Ryujinx.Graphics.Gpu/Window.cs @@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu { pt.AcquireCallback(_context, pt.UserObj); - Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range); + Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, pt.Range); pt.Cache.Tick(); From 26bf13a65d6689601593a8050970d6835fd9dfe2 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 8 Feb 2023 10:19:43 -0300 Subject: [PATCH 338/737] Limit texture cache based on total texture size (#4350) * Limit texture cache based on total texture size * Formatting --- Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs | 78 ++++++++++--------- Ryujinx.Graphics.Gpu/Image/Texture.cs | 7 -- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 13 ---- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 26 ------- 4 files changed, 40 insertions(+), 84 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index 379eb7159..a0b9f57bd 100644 --- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -33,10 +33,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// class AutoDeleteCache : IEnumerable { + private const int MinCountForDeletion = 32; private const int MaxCapacity = 2048; + private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB; private readonly LinkedList _textures; - private readonly ConcurrentQueue _deferredRemovals; + private ulong _totalSize; private HashSet _shortCacheBuilder; private HashSet _shortCache; @@ -49,7 +51,6 @@ namespace Ryujinx.Graphics.Gpu.Image public AutoDeleteCache() { _textures = new LinkedList(); - _deferredRemovals = new ConcurrentQueue(); _shortCacheBuilder = new HashSet(); _shortCache = new HashSet(); @@ -67,37 +68,15 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture to be added to the cache public void Add(Texture texture) { - texture.IncrementReferenceCount(); + _totalSize += texture.Size; + texture.IncrementReferenceCount(); texture.CacheNode = _textures.AddLast(texture); - if (_textures.Count > MaxCapacity) + if (_textures.Count > MaxCapacity || + (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)) { - Texture oldestTexture = _textures.First.Value; - - if (!oldestTexture.CheckModified(false)) - { - // The texture must be flushed if it falls out of the auto delete cache. - // Flushes out of the auto delete cache do not trigger write tracking, - // as it is expected that other overlapping textures exist that have more up-to-date contents. - - oldestTexture.Group.SynchronizeDependents(oldestTexture); - oldestTexture.FlushModified(false); - } - - _textures.RemoveFirst(); - - oldestTexture.DecrementReferenceCount(); - - oldestTexture.CacheNode = null; - } - - if (_deferredRemovals.Count > 0) - { - while (_deferredRemovals.TryDequeue(out Texture textureToRemove)) - { - Remove(textureToRemove, false); - } + RemoveLeastUsedTexture(); } } @@ -120,6 +99,11 @@ namespace Ryujinx.Graphics.Gpu.Image texture.CacheNode = _textures.AddLast(texture); } + + if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion) + { + RemoveLeastUsedTexture(); + } } else { @@ -127,6 +111,31 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Removes the least used texture from the cache. + /// + private void RemoveLeastUsedTexture() + { + Texture oldestTexture = _textures.First.Value; + + _totalSize -= oldestTexture.Size; + + if (!oldestTexture.CheckModified(false)) + { + // The texture must be flushed if it falls out of the auto delete cache. + // Flushes out of the auto delete cache do not trigger write tracking, + // as it is expected that other overlapping textures exist that have more up-to-date contents. + + oldestTexture.Group.SynchronizeDependents(oldestTexture); + oldestTexture.FlushModified(false); + } + + _textures.RemoveFirst(); + + oldestTexture.DecrementReferenceCount(); + oldestTexture.CacheNode = null; + } + /// /// Removes a texture from the cache. /// @@ -148,20 +157,13 @@ namespace Ryujinx.Graphics.Gpu.Image _textures.Remove(texture.CacheNode); + _totalSize -= texture.Size; + texture.CacheNode = null; return texture.DecrementReferenceCount(); } - /// - /// Queues removal of a texture from the cache in a thread safe way. - /// - /// The texture to be removed from the cache - public void RemoveDeferred(Texture texture) - { - _deferredRemovals.Enqueue(texture); - } - /// /// Attempt to find a texture on the short duration cache. /// diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 352a828d1..6c0de5367 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1637,13 +1637,6 @@ namespace Ryujinx.Graphics.Gpu.Image } RemoveFromPools(true); - - // We only want to remove if there's no mapped region of the texture that was modified by the GPU, - // otherwise we could lose data. - if (!Group.AnyModified(this)) - { - _physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this); - } } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 1d5b1851f..27bec786f 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -1079,19 +1079,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Queues the removal of a texture from the auto delete cache. - /// - /// - /// This function is thread safe and can be called from any thread. - /// The texture will be deleted on the next time the cache is used. - /// - /// The texture to be removed - public void QueueAutoDeleteCacheRemoval(Texture texture) - { - _cache.RemoveDeferred(texture); - } - /// /// Adds a texture to the short duration cache. This typically keeps it alive for two ticks. /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 896e11a5d..942fa2f87 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -434,32 +434,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Checks if a texture was modified by the GPU. - /// - /// The texture to be checked - /// True if any region of the texture was modified by the GPU, false otherwise - public bool AnyModified(Texture texture) - { - bool anyModified = false; - - EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => - { - for (int i = 0; i < regionCount; i++) - { - TextureGroupHandle group = _handles[baseHandle + i]; - - if (group.Modified) - { - anyModified = true; - break; - } - } - }); - - return anyModified; - } - /// /// Flush modified ranges for a given texture. /// From f6d5499a16a45347c08d444d76b1355c74194301 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 8 Feb 2023 10:34:22 -0300 Subject: [PATCH 339/737] Fix some Vulkan validation errors (#4357) --- Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 7 +++-- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 28 +++++++++++++------ .../Queries/BufferedQuery.cs | 3 +- Ryujinx.Graphics.Vulkan/TextureView.cs | 2 +- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 26d34e54e..5c9193fa6 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan int attachmentCount = 0; int colorCount = 0; + int maxColorAttachmentIndex = -1; for (int i = 0; i < state.AttachmentEnable.Length; i++) { @@ -36,6 +37,7 @@ namespace Ryujinx.Graphics.Vulkan attachmentIndices[attachmentCount++] = i; colorCount++; + maxColorAttachmentIndex = i; } } @@ -73,12 +75,11 @@ namespace Ryujinx.Graphics.Vulkan if (colorAttachmentsCount != 0) { - int maxAttachmentIndex = Constants.MaxRenderTargets - 1; - subpass.ColorAttachmentCount = (uint)maxAttachmentIndex + 1; + subpass.ColorAttachmentCount = (uint)maxColorAttachmentIndex + 1; subpass.PColorAttachments = &attachmentReferences[0]; // Fill with VK_ATTACHMENT_UNUSED to cover any gaps. - for (int i = 0; i <= maxAttachmentIndex; i++) + for (int i = 0; i <= maxColorAttachmentIndex; i++) { subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 94fd24416..d8e8cdfb1 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan { private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB - private readonly List _activeQueries; + private readonly List<(QueryPool, bool)> _activeQueries; private CounterQueueEvent _activeConditionalRender; private readonly List _pendingQueryCopies; @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device) { - _activeQueries = new List(); + _activeQueries = new List<(QueryPool, bool)>(); _pendingQueryCopies = new(); CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; @@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Vulkan AutoFlush.RegisterFlush(DrawCount); EndRenderPass(); - foreach (var queryPool in _activeQueries) + foreach ((var queryPool, _) in _activeQueries) { Gd.Api.CmdEndQuery(CommandBuffer, queryPool, 0); } @@ -220,10 +220,12 @@ namespace Ryujinx.Graphics.Vulkan // Restore per-command buffer state. - foreach (var queryPool in _activeQueries) + foreach ((var queryPool, var isOcclusion) in _activeQueries) { + bool isPrecise = Gd.Capabilities.SupportsPreciseOcclusionQueries && isOcclusion; + Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1); - Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0); + Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, isPrecise ? QueryControlFlags.PreciseBit : 0); } Gd.ResetCounterPool(); @@ -231,7 +233,7 @@ namespace Ryujinx.Graphics.Vulkan Restore(); } - public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool) + public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool isOcclusion, bool fromSamplePool) { if (needsReset) { @@ -247,16 +249,24 @@ namespace Ryujinx.Graphics.Vulkan } } - Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0); + bool isPrecise = Gd.Capabilities.SupportsPreciseOcclusionQueries && isOcclusion; + Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, isPrecise ? QueryControlFlags.PreciseBit : 0); - _activeQueries.Add(pool); + _activeQueries.Add((pool, isOcclusion)); } public void EndQuery(QueryPool pool) { Gd.Api.CmdEndQuery(CommandBuffer, pool, 0); - _activeQueries.Remove(pool); + for (int i = 0; i < _activeQueries.Count; i++) + { + if (_activeQueries[i].Item1.Handle == pool.Handle) + { + _activeQueries.RemoveAt(i); + break; + } + } } public void CopyQueryResults(BufferedQuery query) diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index abb43eb56..29efd8e74 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -100,7 +100,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries if (_isSupported) { bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value; - _pipeline.BeginQuery(this, _queryPool, needsReset, _type == CounterType.SamplesPassed && resetSequence != null); + bool isOcclusion = _type == CounterType.SamplesPassed; + _pipeline.BeginQuery(this, _queryPool, needsReset, isOcclusion, isOcclusion && resetSequence != null); } _resetSequence = null; } diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index e58b743df..d60ce39b0 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.Vulkan var componentMapping = new ComponentMapping(swizzleR, swizzleG, swizzleB, swizzleA); var aspectFlags = info.Format.ConvertAspectFlags(info.DepthStencilMode); - var aspectFlagsDepth = info.Format.ConvertAspectFlags(DepthStencilMode.Depth); + var aspectFlagsDepth = info.Format.ConvertAspectFlags(); var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers); var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers); From 99fc4fa61b0eb4c02eacdf7428ae14721349dece Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Wed, 8 Feb 2023 14:54:58 +0100 Subject: [PATCH 340/737] Replace BitConverter.ToString(bytes).Replace("-", "") with Convert.ToHexString(bytes) (#4382) --- Ryujinx.HLE/FileSystem/ContentManager.cs | 2 +- Ryujinx.HLE/HOS/ModLoader.cs | 4 ++-- .../Services/Account/Acc/AccountService/ManagerServer.cs | 6 +++--- Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Ryujinx.HLE/FileSystem/ContentManager.cs b/Ryujinx.HLE/FileSystem/ContentManager.cs index 9ae619adb..9f0f3a4ae 100644 --- a/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -229,7 +229,7 @@ namespace Ryujinx.HLE.FileSystem continue; } - string ncaId = BitConverter.ToString(cnmt.ContentEntries[0].NcaId).Replace("-", "").ToLower(); + string ncaId = Convert.ToHexString(cnmt.ContentEntries[0].NcaId).ToLower(); AddAocItem(cnmt.TitleId, containerPath, $"{ncaId}.nca", true); } diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs index 3b2695176..b6c9973f0 100644 --- a/Ryujinx.HLE/HOS/ModLoader.cs +++ b/Ryujinx.HLE/HOS/ModLoader.cs @@ -696,8 +696,8 @@ namespace Ryujinx.HLE.HOS var buildIds = programs.Select(p => p switch { - NsoExecutable nso => BitConverter.ToString(nso.BuildId.ItemsRo.ToArray()).Replace("-", "").TrimEnd('0'), - NroExecutable nro => BitConverter.ToString(nro.Header.BuildId).Replace("-", "").TrimEnd('0'), + NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()).TrimEnd('0'), + NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'), _ => string.Empty }).ToList(); diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs index 011accd28..972403118 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs @@ -51,11 +51,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService var payload = new JwtPayload { - { "sub", BitConverter.ToString(rawUserId).Replace("-", "").ToLower() }, + { "sub", Convert.ToHexString(rawUserId).ToLower() }, { "aud", "ed9e2f05d286f7b8" }, - { "di", BitConverter.ToString(deviceId).Replace("-", "").ToLower() }, + { "di", Convert.ToHexString(deviceId).ToLower() }, { "sn", "XAW10000000000" }, - { "bs:did", BitConverter.ToString(deviceAccountId).Replace("-", "").ToLower() }, + { "bs:did", Convert.ToHexString(deviceAccountId).ToLower() }, { "iss", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com" }, { "typ", "id_token" }, { "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, diff --git a/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs index 522de8e03..6320fe284 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs @@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps }; // NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead. - string hash = BitConverter.ToString(SHA256.HashData(BitConverter.GetBytes(titleId))).Replace("-", "").Remove(0x20); + string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId))).Remove(0x20); string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00")); string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash); From 618c8edc795b22128fa53448bd7e33d62e53719e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 22:51:07 +0100 Subject: [PATCH 341/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.26.0 to 6.26.1 (#4384) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.26.0 to 6.26.1. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.26.0...v6.26.1) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 8f7788dc6..cde8974df 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + From 40c94160976e2f9efcd858eec726649f2c7372db Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 9 Feb 2023 01:52:43 +0100 Subject: [PATCH 342/737] Misc: Update issues form (#4383) --- .github/ISSUE_TEMPLATE/bug_report.yml | 53 +++++++++++++------ .github/ISSUE_TEMPLATE/feature_request.yml | 8 +-- .../missing_cpu_instruction.yml | 9 +--- .../ISSUE_TEMPLATE/missing_service_call.yml | 8 +-- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a7560fc69..4994debbb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,13 +1,8 @@ name: Bug Report description: File a bug report +title: "[Bug] " +labels: bug body: - - type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search to see if an issue already exists for the bug you encountered. - options: - - label: I have searched the existing issues - required: true - type: textarea id: issue attributes: @@ -53,17 +48,43 @@ body: - *(e.g. 1.1.1)* validations: required: false - - type: textarea - id: environment + - type: input + id: cpu attributes: - label: Environment? - value: | - - ##### CPU: *(e.g. i7-6700)* - - ##### GPU: *(e.g. NVIDIA RTX 2070)* - - ##### RAM: *(e.g. 16GB)* - - Applied Mods: [ Yes (Which ones) / No ] + label: CPU + placeholder: | + - *(e.g. i7-6700)* validations: - required: true + required: false + - type: input + id: gpu + attributes: + label: GPU + placeholder: | + - *(e.g. NVIDIA RTX 2070)* + validations: + required: false + - type: input + id: ram + attributes: + label: RAM + placeholder: | + - *(e.g. 16GB)* + validations: + required: false + - type: checkboxes + attributes: + label: Applied Mods? + options: + - label: Yes + required: false + - type: textarea + id: mods + attributes: + label: List of applied mods + placeholder: You can list applied mods here. + validations: + required: false - type: textarea id: additional-context attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index f755812f6..a9a5b504a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,13 +1,7 @@ name: Feature Request description: Suggest a new feature for Ryujinx. +title: "[Feature Request] <title>" body: - - type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search to see if an issue already exists for the feature you are requesting. - options: - - label: I have searched the existing issues - required: true - type: textarea id: overview attributes: diff --git a/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml b/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml index 237e61e91..10e3bad37 100644 --- a/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml +++ b/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml @@ -1,13 +1,8 @@ name: Missing CPU Instruction description: CPU Instruction is missing in Ryujinx. +title: "[CPU] <title>" +labels: [cpu, not-implemented] body: - - type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search for existing missing CPU instruction - options: - - label: I have searched the existing issues - required: true - type: textarea id: instruction attributes: diff --git a/.github/ISSUE_TEMPLATE/missing_service_call.yml b/.github/ISSUE_TEMPLATE/missing_service_call.yml index 72a521173..48d46d57f 100644 --- a/.github/ISSUE_TEMPLATE/missing_service_call.yml +++ b/.github/ISSUE_TEMPLATE/missing_service_call.yml @@ -1,13 +1,7 @@ name: Missing Service Call description: Service call is missing in Ryujinx. +labels: not-implemented body: - - type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search for an [existing missing service call issue](https://github.com/Ryujinx/Ryujinx/issues) first. - options: - - label: I have searched the existing issues - required: true - type: textarea id: instruction attributes: From f614d2c435bbb4ea6df89d0c174c3acb920d74b1 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Thu, 9 Feb 2023 02:02:00 +0100 Subject: [PATCH 343/737] bug_report.yml hotfix --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 4994debbb..f65f32540 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -76,7 +76,7 @@ body: attributes: label: Applied Mods? options: - - label: Yes + - label: "Yes" required: false - type: textarea id: mods @@ -93,4 +93,4 @@ body: - Additional info about your environment: - Any other information relevant to your issue. validations: - required: false \ No newline at end of file + required: false From b3f0978869c7b621bf1c62ea6a8fc51f1e3a5c24 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Thu, 9 Feb 2023 01:03:41 +0000 Subject: [PATCH 344/737] Vulkan: Flush command buffers for queries less aggressively (#4387) The AutoFlushCounter would flush command buffers on any attachment change (write mask or bindings change) if there was a pending query. This is to get query results as soon as possible for draw skips, but it's assuming that a full occlusion query _pass_ happened, that we want to flush it's data before getting onto draws, rather than the queries being randomly interspersed throughout a pass that also draws. Xenoblade 2 repeatedly switches between performing a samples passed query and outputting to a render target on each draw, and flips the write mask to do so. Flushing the command buffer every 2 draws isn't ideal, so it's best that we only do this if the pattern matches the large block style of occlusion query. This change makes this flush only happen after a few consecutive query reports. "Consecutive" is interrupted by attachment changes or command buffer flush. This doesn't really solve the issue where it resets more queries than it uses, it just stops the game doing it as often. I'm not sure of the best way to do that. The cost of resetting could probably be reduced by using query pools with more than one element and resetting in bulk. --- Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs | 11 +++++++++-- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs index 2a4cbd52e..953316a6c 100644 --- a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs +++ b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs @@ -10,11 +10,13 @@ namespace Ryujinx.Graphics.Vulkan private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; private const int MinDrawCountForFlush = 10; + private const int MinConsecutiveQueryForFlush = 10; private const int InitialQueryCountForFlush = 32; private long _lastFlush; private ulong _lastDrawCount; private bool _hasPendingQuery; + private int _consecutiveQueries; private int _queryCount; private int[] _queryCountHistory = new int[3]; @@ -27,11 +29,13 @@ namespace Ryujinx.Graphics.Vulkan _lastDrawCount = drawCount; _hasPendingQuery = false; + _consecutiveQueries = 0; } public bool RegisterPendingQuery() { _hasPendingQuery = true; + _consecutiveQueries++; _remainingQueries--; _queryCountHistory[_queryCountHistoryIndex]++; @@ -65,15 +69,18 @@ namespace Ryujinx.Graphics.Vulkan return _hasPendingQuery; } - public bool ShouldFlush(ulong drawCount) + public bool ShouldFlushAttachmentChange(ulong drawCount) { _queryCount = 0; - if (_hasPendingQuery) + // Flush when there's an attachment change out of a large block of queries. + if (_consecutiveQueries > MinConsecutiveQueryForFlush) { return true; } + _consecutiveQueries = 0; + long draws = (long)(drawCount - _lastDrawCount); if (draws < MinDrawCountForFlush) diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index d8e8cdfb1..6c026a07f 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -281,7 +281,7 @@ namespace Ryujinx.Graphics.Vulkan protected override void SignalAttachmentChange() { - if (AutoFlush.ShouldFlush(DrawCount)) + if (AutoFlush.ShouldFlushAttachmentChange(DrawCount)) { FlushCommandsImpl(); } From ec8d4f3af5d951776ab9d494ca1c5cae7809c08f Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 9 Feb 2023 02:24:32 +0100 Subject: [PATCH 345/737] Replace unicorn bindings with Nuget package (#4378) * Replace unicorn bindings with Nuget package * Use nameof for ValueSource args * Remove redundant code from test projects * Fix wrong values for EmuStart() Add notes to address this later again * Improve formatting * Fix formatting/alignment issues --- Directory.Packages.props | 1 + Ryujinx.Tests.Unicorn/MemoryPermission.cs | 15 +- Ryujinx.Tests.Unicorn/Native/Const/Arch.cs | 20 - Ryujinx.Tests.Unicorn/Native/Const/Arm.cs | 200 --- Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs | 341 ----- Ryujinx.Tests.Unicorn/Native/Const/Common.cs | 44 - Ryujinx.Tests.Unicorn/Native/Const/Error.cs | 31 - Ryujinx.Tests.Unicorn/Native/Const/Hook.cs | 33 - Ryujinx.Tests.Unicorn/Native/Const/Memory.cs | 19 - Ryujinx.Tests.Unicorn/Native/Const/Mode.cs | 35 - .../Native/Const/Permission.cs | 14 - Ryujinx.Tests.Unicorn/Native/Const/TCG.cs | 12 - Ryujinx.Tests.Unicorn/Native/Interface.cs | 101 -- .../Native/UnicornMemoryRegion.cs | 13 - .../Ryujinx.Tests.Unicorn.csproj | 4 + Ryujinx.Tests.Unicorn/UnicornAArch32.cs | 197 ++- Ryujinx.Tests.Unicorn/UnicornAArch64.cs | 249 ++-- Ryujinx.Tests.Unicorn/UnicornException.cs | 24 - Ryujinx.Tests.Unicorn/libs/README.md | 20 - .../libs/linux/libunicorn.so | Bin 4500288 -> 0 bytes .../libs/windows/unicorn.dll | Bin 2178048 -> 0 bytes .../unicorn_const_generator.py | 199 --- Ryujinx.Tests/Cpu/CpuTest.cs | 90 +- Ryujinx.Tests/Cpu/CpuTest32.cs | 93 +- Ryujinx.Tests/Cpu/CpuTestAlu.cs | 27 +- Ryujinx.Tests/Cpu/CpuTestAlu32.cs | 28 +- Ryujinx.Tests/Cpu/CpuTestAluBinary.cs | 60 +- Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs | 4 +- Ryujinx.Tests/Cpu/CpuTestAluImm.cs | 110 +- Ryujinx.Tests/Cpu/CpuTestAluImm32.cs | 7 +- Ryujinx.Tests/Cpu/CpuTestAluRs.cs | 225 ++-- Ryujinx.Tests/Cpu/CpuTestAluRs32.cs | 18 +- Ryujinx.Tests/Cpu/CpuTestAluRx.cs | 115 +- Ryujinx.Tests/Cpu/CpuTestBf32.cs | 28 +- Ryujinx.Tests/Cpu/CpuTestBfm.cs | 40 +- Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs | 20 +- Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs | 19 +- Ryujinx.Tests/Cpu/CpuTestCsel.cs | 35 +- Ryujinx.Tests/Cpu/CpuTestMisc.cs | 29 +- Ryujinx.Tests/Cpu/CpuTestMisc32.cs | 6 +- Ryujinx.Tests/Cpu/CpuTestMov.cs | 15 +- Ryujinx.Tests/Cpu/CpuTestMul.cs | 60 +- Ryujinx.Tests/Cpu/CpuTestMul32.cs | 32 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 784 ++++++------ Ryujinx.Tests/Cpu/CpuTestSimd32.cs | 32 +- Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs | 141 +-- Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs | 32 +- Ryujinx.Tests/Cpu/CpuTestSimdExt.cs | 26 +- Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs | 34 +- Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs | 9 +- Ryujinx.Tests/Cpu/CpuTestSimdImm.cs | 77 +- Ryujinx.Tests/Cpu/CpuTestSimdIns.cs | 160 ++- Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs | 36 +- Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs | 19 +- Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs | 8 +- Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 1116 ++++++++--------- Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs | 101 +- Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs | 55 +- Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs | 78 +- Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs | 316 +++-- Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs | 71 +- Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs | 110 +- Ryujinx.Tests/Cpu/CpuTestSystem.cs | 10 +- Ryujinx.Tests/Cpu/CpuTestThumb.cs | 4 +- 64 files changed, 2276 insertions(+), 3576 deletions(-) delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Arch.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Arm.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Common.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Error.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Hook.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Memory.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Mode.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/Permission.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Const/TCG.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/Interface.cs delete mode 100644 Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs delete mode 100644 Ryujinx.Tests.Unicorn/UnicornException.cs delete mode 100644 Ryujinx.Tests.Unicorn/libs/README.md delete mode 100644 Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so delete mode 100644 Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll delete mode 100644 Ryujinx.Tests.Unicorn/unicorn_const_generator.py diff --git a/Directory.Packages.props b/Directory.Packages.props index cde8974df..1b0b906f8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -49,6 +49,7 @@ <PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> + <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-9c9356d" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> </ItemGroup> </Project> \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/MemoryPermission.cs b/Ryujinx.Tests.Unicorn/MemoryPermission.cs index a14c4e9cf..044b3176b 100644 --- a/Ryujinx.Tests.Unicorn/MemoryPermission.cs +++ b/Ryujinx.Tests.Unicorn/MemoryPermission.cs @@ -1,11 +1,14 @@ +using System; + namespace Ryujinx.Tests.Unicorn { + [Flags] public enum MemoryPermission { - NONE = 0, - READ = 1, - WRITE = 2, - EXEC = 4, - ALL = 7, + None = 0, + Read = 1, + Write = 2, + Exec = 4, + All = 7, } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Arch.cs b/Ryujinx.Tests.Unicorn/Native/Const/Arch.cs deleted file mode 100644 index f614d091b..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Arch.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Arch - { - ARM = 1, - ARM64 = 2, - MIPS = 3, - X86 = 4, - PPC = 5, - SPARC = 6, - M68K = 7, - RISCV = 8, - S390X = 9, - TRICORE = 10, - MAX = 11, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Arm.cs b/Ryujinx.Tests.Unicorn/Native/Const/Arm.cs deleted file mode 100644 index 4b7b3d6f3..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Arm.cs +++ /dev/null @@ -1,200 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Arm - { - - // ARM CPU - - CPU_ARM_926 = 0, - CPU_ARM_946 = 1, - CPU_ARM_1026 = 2, - CPU_ARM_1136_R2 = 3, - CPU_ARM_1136 = 4, - CPU_ARM_1176 = 5, - CPU_ARM_11MPCORE = 6, - CPU_ARM_CORTEX_M0 = 7, - CPU_ARM_CORTEX_M3 = 8, - CPU_ARM_CORTEX_M4 = 9, - CPU_ARM_CORTEX_M7 = 10, - CPU_ARM_CORTEX_M33 = 11, - CPU_ARM_CORTEX_R5 = 12, - CPU_ARM_CORTEX_R5F = 13, - CPU_ARM_CORTEX_A7 = 14, - CPU_ARM_CORTEX_A8 = 15, - CPU_ARM_CORTEX_A9 = 16, - CPU_ARM_CORTEX_A15 = 17, - CPU_ARM_TI925T = 18, - CPU_ARM_SA1100 = 19, - CPU_ARM_SA1110 = 20, - CPU_ARM_PXA250 = 21, - CPU_ARM_PXA255 = 22, - CPU_ARM_PXA260 = 23, - CPU_ARM_PXA261 = 24, - CPU_ARM_PXA262 = 25, - CPU_ARM_PXA270 = 26, - CPU_ARM_PXA270A0 = 27, - CPU_ARM_PXA270A1 = 28, - CPU_ARM_PXA270B0 = 29, - CPU_ARM_PXA270B1 = 30, - CPU_ARM_PXA270C0 = 31, - CPU_ARM_PXA270C5 = 32, - CPU_ARM_MAX = 33, - CPU_ARM_ENDING = 34, - - // ARM registers - - REG_INVALID = 0, - REG_APSR = 1, - REG_APSR_NZCV = 2, - REG_CPSR = 3, - REG_FPEXC = 4, - REG_FPINST = 5, - REG_FPSCR = 6, - REG_FPSCR_NZCV = 7, - REG_FPSID = 8, - REG_ITSTATE = 9, - REG_LR = 10, - REG_PC = 11, - REG_SP = 12, - REG_SPSR = 13, - REG_D0 = 14, - REG_D1 = 15, - REG_D2 = 16, - REG_D3 = 17, - REG_D4 = 18, - REG_D5 = 19, - REG_D6 = 20, - REG_D7 = 21, - REG_D8 = 22, - REG_D9 = 23, - REG_D10 = 24, - REG_D11 = 25, - REG_D12 = 26, - REG_D13 = 27, - REG_D14 = 28, - REG_D15 = 29, - REG_D16 = 30, - REG_D17 = 31, - REG_D18 = 32, - REG_D19 = 33, - REG_D20 = 34, - REG_D21 = 35, - REG_D22 = 36, - REG_D23 = 37, - REG_D24 = 38, - REG_D25 = 39, - REG_D26 = 40, - REG_D27 = 41, - REG_D28 = 42, - REG_D29 = 43, - REG_D30 = 44, - REG_D31 = 45, - REG_FPINST2 = 46, - REG_MVFR0 = 47, - REG_MVFR1 = 48, - REG_MVFR2 = 49, - REG_Q0 = 50, - REG_Q1 = 51, - REG_Q2 = 52, - REG_Q3 = 53, - REG_Q4 = 54, - REG_Q5 = 55, - REG_Q6 = 56, - REG_Q7 = 57, - REG_Q8 = 58, - REG_Q9 = 59, - REG_Q10 = 60, - REG_Q11 = 61, - REG_Q12 = 62, - REG_Q13 = 63, - REG_Q14 = 64, - REG_Q15 = 65, - REG_R0 = 66, - REG_R1 = 67, - REG_R2 = 68, - REG_R3 = 69, - REG_R4 = 70, - REG_R5 = 71, - REG_R6 = 72, - REG_R7 = 73, - REG_R8 = 74, - REG_R9 = 75, - REG_R10 = 76, - REG_R11 = 77, - REG_R12 = 78, - REG_S0 = 79, - REG_S1 = 80, - REG_S2 = 81, - REG_S3 = 82, - REG_S4 = 83, - REG_S5 = 84, - REG_S6 = 85, - REG_S7 = 86, - REG_S8 = 87, - REG_S9 = 88, - REG_S10 = 89, - REG_S11 = 90, - REG_S12 = 91, - REG_S13 = 92, - REG_S14 = 93, - REG_S15 = 94, - REG_S16 = 95, - REG_S17 = 96, - REG_S18 = 97, - REG_S19 = 98, - REG_S20 = 99, - REG_S21 = 100, - REG_S22 = 101, - REG_S23 = 102, - REG_S24 = 103, - REG_S25 = 104, - REG_S26 = 105, - REG_S27 = 106, - REG_S28 = 107, - REG_S29 = 108, - REG_S30 = 109, - REG_S31 = 110, - REG_C1_C0_2 = 111, - REG_C13_C0_2 = 112, - REG_C13_C0_3 = 113, - REG_IPSR = 114, - REG_MSP = 115, - REG_PSP = 116, - REG_CONTROL = 117, - REG_IAPSR = 118, - REG_EAPSR = 119, - REG_XPSR = 120, - REG_EPSR = 121, - REG_IEPSR = 122, - REG_PRIMASK = 123, - REG_BASEPRI = 124, - REG_BASEPRI_MAX = 125, - REG_FAULTMASK = 126, - REG_APSR_NZCVQ = 127, - REG_APSR_G = 128, - REG_APSR_NZCVQG = 129, - REG_IAPSR_NZCVQ = 130, - REG_IAPSR_G = 131, - REG_IAPSR_NZCVQG = 132, - REG_EAPSR_NZCVQ = 133, - REG_EAPSR_G = 134, - REG_EAPSR_NZCVQG = 135, - REG_XPSR_NZCVQ = 136, - REG_XPSR_G = 137, - REG_XPSR_NZCVQG = 138, - REG_CP_REG = 139, - REG_ENDING = 140, - - // alias registers - REG_R13 = 12, - REG_R14 = 10, - REG_R15 = 11, - REG_SB = 75, - REG_SL = 76, - REG_FP = 77, - REG_IP = 78, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs b/Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs deleted file mode 100644 index 11344557b..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs +++ /dev/null @@ -1,341 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Arm64 - { - - // ARM64 CPU - - CPU_ARM64_A57 = 0, - CPU_ARM64_A53 = 1, - CPU_ARM64_A72 = 2, - CPU_ARM64_MAX = 3, - CPU_ARM64_ENDING = 4, - - // ARM64 registers - - REG_INVALID = 0, - REG_X29 = 1, - REG_X30 = 2, - REG_NZCV = 3, - REG_SP = 4, - REG_WSP = 5, - REG_WZR = 6, - REG_XZR = 7, - REG_B0 = 8, - REG_B1 = 9, - REG_B2 = 10, - REG_B3 = 11, - REG_B4 = 12, - REG_B5 = 13, - REG_B6 = 14, - REG_B7 = 15, - REG_B8 = 16, - REG_B9 = 17, - REG_B10 = 18, - REG_B11 = 19, - REG_B12 = 20, - REG_B13 = 21, - REG_B14 = 22, - REG_B15 = 23, - REG_B16 = 24, - REG_B17 = 25, - REG_B18 = 26, - REG_B19 = 27, - REG_B20 = 28, - REG_B21 = 29, - REG_B22 = 30, - REG_B23 = 31, - REG_B24 = 32, - REG_B25 = 33, - REG_B26 = 34, - REG_B27 = 35, - REG_B28 = 36, - REG_B29 = 37, - REG_B30 = 38, - REG_B31 = 39, - REG_D0 = 40, - REG_D1 = 41, - REG_D2 = 42, - REG_D3 = 43, - REG_D4 = 44, - REG_D5 = 45, - REG_D6 = 46, - REG_D7 = 47, - REG_D8 = 48, - REG_D9 = 49, - REG_D10 = 50, - REG_D11 = 51, - REG_D12 = 52, - REG_D13 = 53, - REG_D14 = 54, - REG_D15 = 55, - REG_D16 = 56, - REG_D17 = 57, - REG_D18 = 58, - REG_D19 = 59, - REG_D20 = 60, - REG_D21 = 61, - REG_D22 = 62, - REG_D23 = 63, - REG_D24 = 64, - REG_D25 = 65, - REG_D26 = 66, - REG_D27 = 67, - REG_D28 = 68, - REG_D29 = 69, - REG_D30 = 70, - REG_D31 = 71, - REG_H0 = 72, - REG_H1 = 73, - REG_H2 = 74, - REG_H3 = 75, - REG_H4 = 76, - REG_H5 = 77, - REG_H6 = 78, - REG_H7 = 79, - REG_H8 = 80, - REG_H9 = 81, - REG_H10 = 82, - REG_H11 = 83, - REG_H12 = 84, - REG_H13 = 85, - REG_H14 = 86, - REG_H15 = 87, - REG_H16 = 88, - REG_H17 = 89, - REG_H18 = 90, - REG_H19 = 91, - REG_H20 = 92, - REG_H21 = 93, - REG_H22 = 94, - REG_H23 = 95, - REG_H24 = 96, - REG_H25 = 97, - REG_H26 = 98, - REG_H27 = 99, - REG_H28 = 100, - REG_H29 = 101, - REG_H30 = 102, - REG_H31 = 103, - REG_Q0 = 104, - REG_Q1 = 105, - REG_Q2 = 106, - REG_Q3 = 107, - REG_Q4 = 108, - REG_Q5 = 109, - REG_Q6 = 110, - REG_Q7 = 111, - REG_Q8 = 112, - REG_Q9 = 113, - REG_Q10 = 114, - REG_Q11 = 115, - REG_Q12 = 116, - REG_Q13 = 117, - REG_Q14 = 118, - REG_Q15 = 119, - REG_Q16 = 120, - REG_Q17 = 121, - REG_Q18 = 122, - REG_Q19 = 123, - REG_Q20 = 124, - REG_Q21 = 125, - REG_Q22 = 126, - REG_Q23 = 127, - REG_Q24 = 128, - REG_Q25 = 129, - REG_Q26 = 130, - REG_Q27 = 131, - REG_Q28 = 132, - REG_Q29 = 133, - REG_Q30 = 134, - REG_Q31 = 135, - REG_S0 = 136, - REG_S1 = 137, - REG_S2 = 138, - REG_S3 = 139, - REG_S4 = 140, - REG_S5 = 141, - REG_S6 = 142, - REG_S7 = 143, - REG_S8 = 144, - REG_S9 = 145, - REG_S10 = 146, - REG_S11 = 147, - REG_S12 = 148, - REG_S13 = 149, - REG_S14 = 150, - REG_S15 = 151, - REG_S16 = 152, - REG_S17 = 153, - REG_S18 = 154, - REG_S19 = 155, - REG_S20 = 156, - REG_S21 = 157, - REG_S22 = 158, - REG_S23 = 159, - REG_S24 = 160, - REG_S25 = 161, - REG_S26 = 162, - REG_S27 = 163, - REG_S28 = 164, - REG_S29 = 165, - REG_S30 = 166, - REG_S31 = 167, - REG_W0 = 168, - REG_W1 = 169, - REG_W2 = 170, - REG_W3 = 171, - REG_W4 = 172, - REG_W5 = 173, - REG_W6 = 174, - REG_W7 = 175, - REG_W8 = 176, - REG_W9 = 177, - REG_W10 = 178, - REG_W11 = 179, - REG_W12 = 180, - REG_W13 = 181, - REG_W14 = 182, - REG_W15 = 183, - REG_W16 = 184, - REG_W17 = 185, - REG_W18 = 186, - REG_W19 = 187, - REG_W20 = 188, - REG_W21 = 189, - REG_W22 = 190, - REG_W23 = 191, - REG_W24 = 192, - REG_W25 = 193, - REG_W26 = 194, - REG_W27 = 195, - REG_W28 = 196, - REG_W29 = 197, - REG_W30 = 198, - REG_X0 = 199, - REG_X1 = 200, - REG_X2 = 201, - REG_X3 = 202, - REG_X4 = 203, - REG_X5 = 204, - REG_X6 = 205, - REG_X7 = 206, - REG_X8 = 207, - REG_X9 = 208, - REG_X10 = 209, - REG_X11 = 210, - REG_X12 = 211, - REG_X13 = 212, - REG_X14 = 213, - REG_X15 = 214, - REG_X16 = 215, - REG_X17 = 216, - REG_X18 = 217, - REG_X19 = 218, - REG_X20 = 219, - REG_X21 = 220, - REG_X22 = 221, - REG_X23 = 222, - REG_X24 = 223, - REG_X25 = 224, - REG_X26 = 225, - REG_X27 = 226, - REG_X28 = 227, - REG_V0 = 228, - REG_V1 = 229, - REG_V2 = 230, - REG_V3 = 231, - REG_V4 = 232, - REG_V5 = 233, - REG_V6 = 234, - REG_V7 = 235, - REG_V8 = 236, - REG_V9 = 237, - REG_V10 = 238, - REG_V11 = 239, - REG_V12 = 240, - REG_V13 = 241, - REG_V14 = 242, - REG_V15 = 243, - REG_V16 = 244, - REG_V17 = 245, - REG_V18 = 246, - REG_V19 = 247, - REG_V20 = 248, - REG_V21 = 249, - REG_V22 = 250, - REG_V23 = 251, - REG_V24 = 252, - REG_V25 = 253, - REG_V26 = 254, - REG_V27 = 255, - REG_V28 = 256, - REG_V29 = 257, - REG_V30 = 258, - REG_V31 = 259, - - // pseudo registers - REG_PC = 260, - REG_CPACR_EL1 = 261, - - // thread registers, depreciated, use UC_ARM64_REG_CP_REG instead - REG_TPIDR_EL0 = 262, - REG_TPIDRRO_EL0 = 263, - REG_TPIDR_EL1 = 264, - REG_PSTATE = 265, - - // exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead - REG_ELR_EL0 = 266, - REG_ELR_EL1 = 267, - REG_ELR_EL2 = 268, - REG_ELR_EL3 = 269, - - // stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead - REG_SP_EL0 = 270, - REG_SP_EL1 = 271, - REG_SP_EL2 = 272, - REG_SP_EL3 = 273, - - // other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead - REG_TTBR0_EL1 = 274, - REG_TTBR1_EL1 = 275, - REG_ESR_EL0 = 276, - REG_ESR_EL1 = 277, - REG_ESR_EL2 = 278, - REG_ESR_EL3 = 279, - REG_FAR_EL0 = 280, - REG_FAR_EL1 = 281, - REG_FAR_EL2 = 282, - REG_FAR_EL3 = 283, - REG_PAR_EL1 = 284, - REG_MAIR_EL1 = 285, - REG_VBAR_EL0 = 286, - REG_VBAR_EL1 = 287, - REG_VBAR_EL2 = 288, - REG_VBAR_EL3 = 289, - REG_CP_REG = 290, - - // floating point control and status registers - REG_FPCR = 291, - REG_FPSR = 292, - REG_ENDING = 293, - - // alias registers - REG_IP0 = 215, - REG_IP1 = 216, - REG_FP = 1, - REG_LR = 2, - - // ARM64 instructions - - INS_INVALID = 0, - INS_MRS = 1, - INS_MSR = 2, - INS_SYS = 3, - INS_SYSL = 4, - INS_ENDING = 5, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Common.cs b/Ryujinx.Tests.Unicorn/Native/Const/Common.cs deleted file mode 100644 index e4b59a489..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Common.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Common - { - API_MAJOR = 2, - - API_MINOR = 0, - - API_PATCH = 0, - API_EXTRA = 255, - VERSION_MAJOR = 2, - - VERSION_MINOR = 0, - - VERSION_PATCH = 0, - VERSION_EXTRA = 255, - SECOND_SCALE = 1000000, - MILISECOND_SCALE = 1000, - QUERY_MODE = 1, - QUERY_PAGE_SIZE = 2, - QUERY_ARCH = 3, - QUERY_TIMEOUT = 4, - - CTL_IO_NONE = 0, - CTL_IO_WRITE = 1, - CTL_IO_READ = 2, - CTL_IO_READ_WRITE = 3, - - CTL_UC_MODE = 0, - CTL_UC_PAGE_SIZE = 1, - CTL_UC_ARCH = 2, - CTL_UC_TIMEOUT = 3, - CTL_UC_USE_EXITS = 4, - CTL_UC_EXITS_CNT = 5, - CTL_UC_EXITS = 6, - CTL_CPU_MODEL = 7, - CTL_TB_REQUEST_CACHE = 8, - CTL_TB_REMOVE_CACHE = 9, - CTL_TB_FLUSH = 10, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Error.cs b/Ryujinx.Tests.Unicorn/Native/Const/Error.cs deleted file mode 100644 index 9cedb0fcc..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Error.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Error - { - OK = 0, - NOMEM = 1, - ARCH = 2, - HANDLE = 3, - MODE = 4, - VERSION = 5, - READ_UNMAPPED = 6, - WRITE_UNMAPPED = 7, - FETCH_UNMAPPED = 8, - HOOK = 9, - INSN_INVALID = 10, - MAP = 11, - WRITE_PROT = 12, - READ_PROT = 13, - FETCH_PROT = 14, - ARG = 15, - READ_UNALIGNED = 16, - WRITE_UNALIGNED = 17, - FETCH_UNALIGNED = 18, - HOOK_EXIST = 19, - RESOURCE = 20, - EXCEPTION = 21, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Hook.cs b/Ryujinx.Tests.Unicorn/Native/Const/Hook.cs deleted file mode 100644 index a6b9dca6e..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Hook.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Hook - { - INTR = 1, - INSN = 2, - CODE = 4, - BLOCK = 8, - MEM_READ_UNMAPPED = 16, - MEM_WRITE_UNMAPPED = 32, - MEM_FETCH_UNMAPPED = 64, - MEM_READ_PROT = 128, - MEM_WRITE_PROT = 256, - MEM_FETCH_PROT = 512, - MEM_READ = 1024, - MEM_WRITE = 2048, - MEM_FETCH = 4096, - MEM_READ_AFTER = 8192, - INSN_INVALID = 16384, - EDGE_GENERATED = 32768, - TCG_OPCODE = 65536, - MEM_UNMAPPED = 112, - MEM_PROT = 896, - MEM_READ_INVALID = 144, - MEM_WRITE_INVALID = 288, - MEM_FETCH_INVALID = 576, - MEM_INVALID = 1008, - MEM_VALID = 7168, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Memory.cs b/Ryujinx.Tests.Unicorn/Native/Const/Memory.cs deleted file mode 100644 index a7d60e611..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Memory.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Memory - { - READ = 16, - WRITE = 17, - FETCH = 18, - READ_UNMAPPED = 19, - WRITE_UNMAPPED = 20, - FETCH_UNMAPPED = 21, - WRITE_PROT = 22, - READ_PROT = 23, - FETCH_PROT = 24, - READ_AFTER = 25, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Mode.cs b/Ryujinx.Tests.Unicorn/Native/Const/Mode.cs deleted file mode 100644 index 804d01a97..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Mode.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Mode - { - LITTLE_ENDIAN = 0, - BIG_ENDIAN = 1073741824, - ARM = 0, - THUMB = 16, - MCLASS = 32, - V8 = 64, - ARMBE8 = 1024, - ARM926 = 128, - ARM946 = 256, - ARM1176 = 512, - MICRO = 16, - MIPS3 = 32, - MIPS32R6 = 64, - MIPS32 = 4, - MIPS64 = 8, - MODE_16 = 2, - MODE_32 = 4, - MODE_64 = 8, - PPC32 = 4, - PPC64 = 8, - QPX = 16, - SPARC32 = 4, - SPARC64 = 8, - V9 = 16, - RISCV32 = 4, - RISCV64 = 8, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/Permission.cs b/Ryujinx.Tests.Unicorn/Native/Const/Permission.cs deleted file mode 100644 index 19ddc4f27..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/Permission.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum Permission - { - NONE = 0, - READ = 1, - WRITE = 2, - EXEC = 4, - ALL = 7, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Const/TCG.cs b/Ryujinx.Tests.Unicorn/Native/Const/TCG.cs deleted file mode 100644 index f38785db7..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Const/TCG.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - -// ReSharper disable InconsistentNaming -namespace Ryujinx.Tests.Unicorn.Native.Const -{ - public enum TCG - { - OP_SUB = 0, - OP_FLAG_CMP = 1, - OP_FLAG_DIRECT = 2, - } -} diff --git a/Ryujinx.Tests.Unicorn/Native/Interface.cs b/Ryujinx.Tests.Unicorn/Native/Interface.cs deleted file mode 100644 index 4e34ee8b3..000000000 --- a/Ryujinx.Tests.Unicorn/Native/Interface.cs +++ /dev/null @@ -1,101 +0,0 @@ -using Ryujinx.Tests.Unicorn.Native.Const; -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Tests.Unicorn.Native -{ - public static partial class Interface - { - public static bool IsUnicornAvailable { get; private set; } = true; - - private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) - { - if (libraryName == "unicorn") - { - string loadPath = $"{Path.GetDirectoryName(assembly.Location)}/"; - loadPath += OperatingSystem.IsWindows() ? $"{libraryName}.dll" : $"lib{libraryName}.so"; - - if (!NativeLibrary.TryLoad(loadPath, out IntPtr libraryPtr)) - { - IsUnicornAvailable = false; - Console.Error.WriteLine($"ERROR: Could not find unicorn at: {loadPath}"); - } - - return libraryPtr; - } - - // Otherwise, fallback to default import resolver. - return IntPtr.Zero; - } - - static Interface() - { - NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), ImportResolver); - } - - public static void Checked(Error error) - { - if (error != Error.OK) - { - throw new UnicornException(error); - } - } - - public static void MarshalArrayOf<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(IntPtr input, int length, out T[] output) - { - int size = Marshal.SizeOf<T>(); - - output = new T[length]; - - for (int i = 0; i < length; i++) - { - IntPtr item = new IntPtr(input.ToInt64() + i * size); - - output[i] = Marshal.PtrToStructure<T>(item); - } - } - - [LibraryImport("unicorn")] - public static partial uint uc_version(out uint major, out uint minor); - - [LibraryImport("unicorn")] - public static partial Error uc_open(Arch arch, Mode mode, out IntPtr uc); - - [LibraryImport("unicorn")] - public static partial Error uc_close(IntPtr uc); - - [LibraryImport("unicorn")] - public static partial IntPtr uc_strerror(Error err); - - [LibraryImport("unicorn")] - public static partial Error uc_reg_write(IntPtr uc, int regid, byte[] value); - - [LibraryImport("unicorn")] - public static partial Error uc_reg_read(IntPtr uc, int regid, byte[] value); - - [LibraryImport("unicorn")] - public static partial Error uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size); - - [LibraryImport("unicorn")] - public static partial Error uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size); - - [LibraryImport("unicorn")] - public static partial Error uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count); - - [LibraryImport("unicorn")] - public static partial Error uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms); - - [LibraryImport("unicorn")] - public static partial Error uc_mem_unmap(IntPtr uc, ulong address, ulong size); - - [LibraryImport("unicorn")] - public static partial Error uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms); - - [LibraryImport("unicorn")] - public static partial Error uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count); - } -} \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs deleted file mode 100644 index 7ee34a74a..000000000 --- a/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Tests.Unicorn.Native -{ - [StructLayout(LayoutKind.Sequential)] - public struct UnicornMemoryRegion - { - public UInt64 begin; // begin address of the region (inclusive) - public UInt64 end; // end address of the region (inclusive) - public UInt32 perms; // memory permissions of the region - } -} diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj index b3ee86d37..d925546fe 100644 --- a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj +++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj @@ -10,4 +10,8 @@ <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup> + <ItemGroup> + <PackageReference Include="UnicornEngine.Unicorn" /> + </ItemGroup> + </Project> diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs index 3b8c1699e..a095e6641 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs @@ -1,62 +1,45 @@ -using Ryujinx.Tests.Unicorn.Native; -using Ryujinx.Tests.Unicorn.Native.Const; -using System; +using System; +using UnicornEngine.Const; namespace Ryujinx.Tests.Unicorn { public class UnicornAArch32 : IDisposable { - internal readonly IntPtr uc; - private bool _isDisposed = false; + internal readonly UnicornEngine.Unicorn uc; + private bool _isDisposed; - public IndexedProperty<int, uint> R - { - get - { - return new IndexedProperty<int, uint>( - (int i) => GetX(i), - (int i, uint value) => SetX(i, value)); - } - } + public IndexedProperty<int, uint> R => new(GetX, SetX); - public IndexedProperty<int, SimdValue> Q - { - get - { - return new IndexedProperty<int, SimdValue>( - (int i) => GetQ(i), - (int i, SimdValue value) => SetQ(i, value)); - } - } + public IndexedProperty<int, SimdValue> Q => new(GetQ, SetQ); public uint LR { - get => GetRegister(Arm.REG_LR); - set => SetRegister(Arm.REG_LR, value); + get => GetRegister(Arm.UC_ARM_REG_LR); + set => SetRegister(Arm.UC_ARM_REG_LR, value); } public uint SP { - get => GetRegister(Arm.REG_SP); - set => SetRegister(Arm.REG_SP, value); + get => GetRegister(Arm.UC_ARM_REG_SP); + set => SetRegister(Arm.UC_ARM_REG_SP, value); } public uint PC { - get => GetRegister(Arm.REG_PC) & 0xfffffffeu; - set => SetRegister(Arm.REG_PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u)); + get => GetRegister(Arm.UC_ARM_REG_PC) & 0xfffffffeu; + set => SetRegister(Arm.UC_ARM_REG_PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u)); } public uint CPSR { - get => GetRegister(Arm.REG_CPSR); - set => SetRegister(Arm.REG_CPSR, value); + get => GetRegister(Arm.UC_ARM_REG_CPSR); + set => SetRegister(Arm.UC_ARM_REG_CPSR, value); } public int Fpscr { - get => (int)GetRegister(Arm.REG_FPSCR) | ((int)GetRegister(Arm.REG_FPSCR_NZCV)); - set => SetRegister(Arm.REG_FPSCR, (uint)value); + get => (int)GetRegister(Arm.UC_ARM_REG_FPSCR) | ((int)GetRegister(Arm.UC_ARM_REG_FPSCR_NZCV)); + set => SetRegister(Arm.UC_ARM_REG_FPSCR, (uint)value); } public bool QFlag @@ -95,16 +78,16 @@ namespace Ryujinx.Tests.Unicorn set { CPSR = (CPSR & ~0x00000020u) | (value ? 0x00000020u : 0u); - SetRegister(Arm.REG_PC, (GetRegister(Arm.REG_PC) & 0xfffffffeu) | (value ? 1u : 0u)); + SetRegister(Arm.UC_ARM_REG_PC, (GetRegister(Arm.UC_ARM_REG_PC) & 0xfffffffeu) | (value ? 1u : 0u)); } } public UnicornAArch32() { - Interface.Checked(Interface.uc_open(Arch.ARM, Mode.LITTLE_ENDIAN, out uc)); + uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM, Common.UC_MODE_LITTLE_ENDIAN); - SetRegister(Arm.REG_C1_C0_2, GetRegister(Arm.REG_C1_C0_2) | 0xf00000); - SetRegister(Arm.REG_FPEXC, 0x40000000); + SetRegister(Arm.UC_ARM_REG_C1_C0_2, GetRegister(Arm.UC_ARM_REG_C1_C0_2) | 0xf00000); + SetRegister(Arm.UC_ARM_REG_FPEXC, 0x40000000); } ~UnicornAArch32() @@ -122,14 +105,15 @@ namespace Ryujinx.Tests.Unicorn { if (!_isDisposed) { - Interface.Checked(Interface.uc_close(uc)); + uc.Close(); _isDisposed = true; } } public void RunForCount(ulong count) { - Interface.Checked(Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count)); + // FIXME: untilAddr should be 0xFFFFFFFFFFFFFFFFu + uc.EmuStart(this.PC, -1, 0, (long)count); } public void Step() @@ -137,44 +121,44 @@ namespace Ryujinx.Tests.Unicorn RunForCount(1); } - private static Arm[] XRegisters = new Arm[16] + private static int[] XRegisters = { - Arm.REG_R0, - Arm.REG_R1, - Arm.REG_R2, - Arm.REG_R3, - Arm.REG_R4, - Arm.REG_R5, - Arm.REG_R6, - Arm.REG_R7, - Arm.REG_R8, - Arm.REG_R9, - Arm.REG_R10, - Arm.REG_R11, - Arm.REG_R12, - Arm.REG_R13, - Arm.REG_R14, - Arm.REG_R15, + Arm.UC_ARM_REG_R0, + Arm.UC_ARM_REG_R1, + Arm.UC_ARM_REG_R2, + Arm.UC_ARM_REG_R3, + Arm.UC_ARM_REG_R4, + Arm.UC_ARM_REG_R5, + Arm.UC_ARM_REG_R6, + Arm.UC_ARM_REG_R7, + Arm.UC_ARM_REG_R8, + Arm.UC_ARM_REG_R9, + Arm.UC_ARM_REG_R10, + Arm.UC_ARM_REG_R11, + Arm.UC_ARM_REG_R12, + Arm.UC_ARM_REG_R13, + Arm.UC_ARM_REG_R14, + Arm.UC_ARM_REG_R15, }; - private static Arm[] QRegisters = new Arm[16] + private static int[] QRegisters = { - Arm.REG_Q0, - Arm.REG_Q1, - Arm.REG_Q2, - Arm.REG_Q3, - Arm.REG_Q4, - Arm.REG_Q5, - Arm.REG_Q6, - Arm.REG_Q7, - Arm.REG_Q8, - Arm.REG_Q9, - Arm.REG_Q10, - Arm.REG_Q11, - Arm.REG_Q12, - Arm.REG_Q13, - Arm.REG_Q14, - Arm.REG_Q15 + Arm.UC_ARM_REG_Q0, + Arm.UC_ARM_REG_Q1, + Arm.UC_ARM_REG_Q2, + Arm.UC_ARM_REG_Q3, + Arm.UC_ARM_REG_Q4, + Arm.UC_ARM_REG_Q5, + Arm.UC_ARM_REG_Q6, + Arm.UC_ARM_REG_Q7, + Arm.UC_ARM_REG_Q8, + Arm.UC_ARM_REG_Q9, + Arm.UC_ARM_REG_Q10, + Arm.UC_ARM_REG_Q11, + Arm.UC_ARM_REG_Q12, + Arm.UC_ARM_REG_Q13, + Arm.UC_ARM_REG_Q14, + Arm.UC_ARM_REG_Q15 }; public uint GetX(int index) @@ -205,7 +189,7 @@ namespace Ryujinx.Tests.Unicorn } // Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead. - return GetVector((Arm)((int)Arm.REG_D0 + index * 2)); + return GetVector(Arm.UC_ARM_REG_D0 + index * 2); } public void SetQ(int index, SimdValue value) @@ -215,96 +199,85 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - SetVector((Arm)((int)Arm.REG_D0 + index * 2), value); + SetVector(Arm.UC_ARM_REG_D0 + index * 2, value); } - public uint GetRegister(Arm register) + public uint GetRegister(int register) { byte[] data = new byte[4]; - Interface.Checked(Interface.uc_reg_read(uc, (int)register, data)); + uc.RegRead(register, data); - return (uint)BitConverter.ToInt32(data, 0); + return BitConverter.ToUInt32(data, 0); } - public void SetRegister(Arm register, uint value) + public void SetRegister(int register, uint value) { byte[] data = BitConverter.GetBytes(value); - Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); + uc.RegWrite(register, data); } - public SimdValue GetVector(Arm register) + public SimdValue GetVector(int register) { byte[] data = new byte[8]; - Interface.Checked(Interface.uc_reg_read(uc, (int)register, data)); + uc.RegRead(register, data); ulong lo = BitConverter.ToUInt64(data, 0); - Interface.Checked(Interface.uc_reg_read(uc, (int)register + 1, data)); + uc.RegRead(register + 1, data); ulong hi = BitConverter.ToUInt64(data, 0); return new SimdValue(lo, hi); } - private void SetVector(Arm register, SimdValue value) + private void SetVector(int register, SimdValue value) { byte[] data = BitConverter.GetBytes(value.GetUInt64(0)); - Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); + uc.RegWrite(register, data); data = BitConverter.GetBytes(value.GetUInt64(1)); - Interface.Checked(Interface.uc_reg_write(uc, (int)register + 1, data)); + uc.RegWrite(register + 1, data); } public byte[] MemoryRead(ulong address, ulong size) { byte[] value = new byte[size]; - Interface.Checked(Interface.uc_mem_read(uc, address, value, size)); + uc.MemRead((long)address, value); return value; } public byte MemoryRead8(ulong address) => MemoryRead(address, 1)[0]; - public UInt16 MemoryRead16(ulong address) => (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0); - public UInt32 MemoryRead32(ulong address) => (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0); - public UInt64 MemoryRead64(ulong address) => (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0); + public ushort MemoryRead16(ulong address) => BitConverter.ToUInt16(MemoryRead(address, 2), 0); + public uint MemoryRead32(ulong address) => BitConverter.ToUInt32(MemoryRead(address, 4), 0); + public ulong MemoryRead64(ulong address) => BitConverter.ToUInt64(MemoryRead(address, 8), 0); public void MemoryWrite(ulong address, byte[] value) { - Interface.Checked(Interface.uc_mem_write(uc, address, value, (ulong)value.Length)); + uc.MemWrite((long)address, value); } - public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new byte[] { value }); - public void MemoryWrite16(ulong address, Int16 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite16(ulong address, UInt16 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite32(ulong address, Int32 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite32(ulong address, UInt32 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite64(ulong address, Int64 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite64(ulong address, UInt64 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new[] { value }); + public void MemoryWrite16(ulong address, short value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite16(ulong address, ushort value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite32(ulong address, int value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite32(ulong address, uint value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite64(ulong address, long value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite64(ulong address, ulong value) => MemoryWrite(address, BitConverter.GetBytes(value)); public void MemoryMap(ulong address, ulong size, MemoryPermission permissions) { - Interface.Checked(Interface.uc_mem_map(uc, address, size, (uint)permissions)); + uc.MemMap((long)address, (long)size, (int)permissions); } public void MemoryUnmap(ulong address, ulong size) { - Interface.Checked(Interface.uc_mem_unmap(uc, address, size)); + uc.MemUnmap((long)address, (long)size); } public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions) { - Interface.Checked(Interface.uc_mem_protect(uc, address, size, (uint)permissions)); - } - - public static bool IsAvailable() - { - try - { - Interface.uc_version(out _, out _); - } - catch (DllNotFoundException) { } - - return Interface.IsUnicornAvailable; + uc.MemProtect((long)address, (long)size, (int)permissions); } } } \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs index 1784e7dff..16dfd93bd 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs @@ -1,68 +1,51 @@ -using Ryujinx.Tests.Unicorn.Native; -using Ryujinx.Tests.Unicorn.Native.Const; using System; +using UnicornEngine.Const; namespace Ryujinx.Tests.Unicorn { public class UnicornAArch64 : IDisposable { - internal readonly IntPtr uc; - private bool _isDisposed = false; + internal readonly UnicornEngine.Unicorn uc; + private bool _isDisposed; - public IndexedProperty<int, ulong> X - { - get - { - return new IndexedProperty<int, ulong>( - (int i) => GetX(i), - (int i, ulong value) => SetX(i, value)); - } - } + public IndexedProperty<int, ulong> X => new(GetX, SetX); - public IndexedProperty<int, SimdValue> Q - { - get - { - return new IndexedProperty<int, SimdValue>( - (int i) => GetQ(i), - (int i, SimdValue value) => SetQ(i, value)); - } - } + public IndexedProperty<int, SimdValue> Q => new(GetQ, SetQ); public ulong LR { - get => GetRegister(Arm64.REG_LR); - set => SetRegister(Arm64.REG_LR, value); + get => GetRegister(Arm64.UC_ARM64_REG_LR); + set => SetRegister(Arm64.UC_ARM64_REG_LR, value); } public ulong SP { - get => GetRegister(Arm64.REG_SP); - set => SetRegister(Arm64.REG_SP, value); + get => GetRegister(Arm64.UC_ARM64_REG_SP); + set => SetRegister(Arm64.UC_ARM64_REG_SP, value); } public ulong PC { - get => GetRegister(Arm64.REG_PC); - set => SetRegister(Arm64.REG_PC, value); + get => GetRegister(Arm64.UC_ARM64_REG_PC); + set => SetRegister(Arm64.UC_ARM64_REG_PC, value); } public uint Pstate { - get => (uint)GetRegister(Arm64.REG_PSTATE); - set => SetRegister(Arm64.REG_PSTATE, (uint)value); + get => (uint)GetRegister(Arm64.UC_ARM64_REG_PSTATE); + set => SetRegister(Arm64.UC_ARM64_REG_PSTATE, value); } public int Fpcr { - get => (int)GetRegister(Arm64.REG_FPCR); - set => SetRegister(Arm64.REG_FPCR, (uint)value); + get => (int)GetRegister(Arm64.UC_ARM64_REG_FPCR); + set => SetRegister(Arm64.UC_ARM64_REG_FPCR, (uint)value); } public int Fpsr { - get => (int)GetRegister(Arm64.REG_FPSR); - set => SetRegister(Arm64.REG_FPSR, (uint)value); + get => (int)GetRegister(Arm64.UC_ARM64_REG_FPSR); + set => SetRegister(Arm64.UC_ARM64_REG_FPSR, (uint)value); } public bool OverflowFlag @@ -91,9 +74,9 @@ namespace Ryujinx.Tests.Unicorn public UnicornAArch64() { - Interface.Checked(Interface.uc_open(Arch.ARM64, Mode.LITTLE_ENDIAN, out uc)); + uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM64, Common.UC_MODE_LITTLE_ENDIAN); - SetRegister(Arm64.REG_CPACR_EL1, 0x00300000); + SetRegister(Arm64.UC_ARM64_REG_CPACR_EL1, 0x00300000); } ~UnicornAArch64() @@ -111,14 +94,15 @@ namespace Ryujinx.Tests.Unicorn { if (!_isDisposed) { - Interface.Checked(Interface.uc_close(uc)); + uc.Close(); _isDisposed = true; } } public void RunForCount(ulong count) { - Interface.Checked(Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count)); + // FIXME: untilAddr should be 0xFFFFFFFFFFFFFFFFul + uc.EmuStart((long)this.PC, -1, 0, (long)count); } public void Step() @@ -126,75 +110,75 @@ namespace Ryujinx.Tests.Unicorn RunForCount(1); } - private static Arm64[] XRegisters = new Arm64[31] + private static int[] XRegisters = { - Arm64.REG_X0, - Arm64.REG_X1, - Arm64.REG_X2, - Arm64.REG_X3, - Arm64.REG_X4, - Arm64.REG_X5, - Arm64.REG_X6, - Arm64.REG_X7, - Arm64.REG_X8, - Arm64.REG_X9, - Arm64.REG_X10, - Arm64.REG_X11, - Arm64.REG_X12, - Arm64.REG_X13, - Arm64.REG_X14, - Arm64.REG_X15, - Arm64.REG_X16, - Arm64.REG_X17, - Arm64.REG_X18, - Arm64.REG_X19, - Arm64.REG_X20, - Arm64.REG_X21, - Arm64.REG_X22, - Arm64.REG_X23, - Arm64.REG_X24, - Arm64.REG_X25, - Arm64.REG_X26, - Arm64.REG_X27, - Arm64.REG_X28, - Arm64.REG_X29, - Arm64.REG_X30, + Arm64.UC_ARM64_REG_X0, + Arm64.UC_ARM64_REG_X1, + Arm64.UC_ARM64_REG_X2, + Arm64.UC_ARM64_REG_X3, + Arm64.UC_ARM64_REG_X4, + Arm64.UC_ARM64_REG_X5, + Arm64.UC_ARM64_REG_X6, + Arm64.UC_ARM64_REG_X7, + Arm64.UC_ARM64_REG_X8, + Arm64.UC_ARM64_REG_X9, + Arm64.UC_ARM64_REG_X10, + Arm64.UC_ARM64_REG_X11, + Arm64.UC_ARM64_REG_X12, + Arm64.UC_ARM64_REG_X13, + Arm64.UC_ARM64_REG_X14, + Arm64.UC_ARM64_REG_X15, + Arm64.UC_ARM64_REG_X16, + Arm64.UC_ARM64_REG_X17, + Arm64.UC_ARM64_REG_X18, + Arm64.UC_ARM64_REG_X19, + Arm64.UC_ARM64_REG_X20, + Arm64.UC_ARM64_REG_X21, + Arm64.UC_ARM64_REG_X22, + Arm64.UC_ARM64_REG_X23, + Arm64.UC_ARM64_REG_X24, + Arm64.UC_ARM64_REG_X25, + Arm64.UC_ARM64_REG_X26, + Arm64.UC_ARM64_REG_X27, + Arm64.UC_ARM64_REG_X28, + Arm64.UC_ARM64_REG_X29, + Arm64.UC_ARM64_REG_X30, }; - private static Arm64[] QRegisters = new Arm64[32] + private static int[] QRegisters = { - Arm64.REG_Q0, - Arm64.REG_Q1, - Arm64.REG_Q2, - Arm64.REG_Q3, - Arm64.REG_Q4, - Arm64.REG_Q5, - Arm64.REG_Q6, - Arm64.REG_Q7, - Arm64.REG_Q8, - Arm64.REG_Q9, - Arm64.REG_Q10, - Arm64.REG_Q11, - Arm64.REG_Q12, - Arm64.REG_Q13, - Arm64.REG_Q14, - Arm64.REG_Q15, - Arm64.REG_Q16, - Arm64.REG_Q17, - Arm64.REG_Q18, - Arm64.REG_Q19, - Arm64.REG_Q20, - Arm64.REG_Q21, - Arm64.REG_Q22, - Arm64.REG_Q23, - Arm64.REG_Q24, - Arm64.REG_Q25, - Arm64.REG_Q26, - Arm64.REG_Q27, - Arm64.REG_Q28, - Arm64.REG_Q29, - Arm64.REG_Q30, - Arm64.REG_Q31, + Arm64.UC_ARM64_REG_Q0, + Arm64.UC_ARM64_REG_Q1, + Arm64.UC_ARM64_REG_Q2, + Arm64.UC_ARM64_REG_Q3, + Arm64.UC_ARM64_REG_Q4, + Arm64.UC_ARM64_REG_Q5, + Arm64.UC_ARM64_REG_Q6, + Arm64.UC_ARM64_REG_Q7, + Arm64.UC_ARM64_REG_Q8, + Arm64.UC_ARM64_REG_Q9, + Arm64.UC_ARM64_REG_Q10, + Arm64.UC_ARM64_REG_Q11, + Arm64.UC_ARM64_REG_Q12, + Arm64.UC_ARM64_REG_Q13, + Arm64.UC_ARM64_REG_Q14, + Arm64.UC_ARM64_REG_Q15, + Arm64.UC_ARM64_REG_Q16, + Arm64.UC_ARM64_REG_Q17, + Arm64.UC_ARM64_REG_Q18, + Arm64.UC_ARM64_REG_Q19, + Arm64.UC_ARM64_REG_Q20, + Arm64.UC_ARM64_REG_Q21, + Arm64.UC_ARM64_REG_Q22, + Arm64.UC_ARM64_REG_Q23, + Arm64.UC_ARM64_REG_Q24, + Arm64.UC_ARM64_REG_Q25, + Arm64.UC_ARM64_REG_Q26, + Arm64.UC_ARM64_REG_Q27, + Arm64.UC_ARM64_REG_Q28, + Arm64.UC_ARM64_REG_Q29, + Arm64.UC_ARM64_REG_Q30, + Arm64.UC_ARM64_REG_Q31, }; public ulong GetX(int index) @@ -237,89 +221,78 @@ namespace Ryujinx.Tests.Unicorn SetVector(QRegisters[index], value); } - private ulong GetRegister(Arm64 register) + private ulong GetRegister(int register) { byte[] data = new byte[8]; - Interface.Checked(Interface.uc_reg_read(uc, (int)register, data)); + uc.RegRead(register, data); - return (ulong)BitConverter.ToInt64(data, 0); + return BitConverter.ToUInt64(data, 0); } - private void SetRegister(Arm64 register, ulong value) + private void SetRegister(int register, ulong value) { byte[] data = BitConverter.GetBytes(value); - Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); + uc.RegWrite(register, data); } - private SimdValue GetVector(Arm64 register) + private SimdValue GetVector(int register) { byte[] data = new byte[16]; - Interface.Checked(Interface.uc_reg_read(uc, (int)register, data)); + uc.RegRead(register, data); return new SimdValue(data); } - private void SetVector(Arm64 register, SimdValue value) + private void SetVector(int register, SimdValue value) { byte[] data = value.ToArray(); - Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); + uc.RegWrite(register, data); } public byte[] MemoryRead(ulong address, ulong size) { byte[] value = new byte[size]; - Interface.Checked(Interface.uc_mem_read(uc, address, value, size)); + uc.MemRead((long)address, value); return value; } public byte MemoryRead8 (ulong address) => MemoryRead(address, 1)[0]; - public UInt16 MemoryRead16(ulong address) => (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0); - public UInt32 MemoryRead32(ulong address) => (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0); - public UInt64 MemoryRead64(ulong address) => (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0); + public ushort MemoryRead16(ulong address) => BitConverter.ToUInt16(MemoryRead(address, 2), 0); + public uint MemoryRead32(ulong address) => BitConverter.ToUInt32(MemoryRead(address, 4), 0); + public ulong MemoryRead64(ulong address) => BitConverter.ToUInt64(MemoryRead(address, 8), 0); public void MemoryWrite(ulong address, byte[] value) { - Interface.Checked(Interface.uc_mem_write(uc, address, value, (ulong)value.Length)); + uc.MemWrite((long)address, value); } - public void MemoryWrite8 (ulong address, byte value) => MemoryWrite(address, new byte[]{value}); - public void MemoryWrite16(ulong address, Int16 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite16(ulong address, UInt16 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite32(ulong address, Int32 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite32(ulong address, UInt32 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite64(ulong address, Int64 value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite64(ulong address, UInt64 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite8 (ulong address, byte value) => MemoryWrite(address, new[]{ value }); + public void MemoryWrite16(ulong address, short value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite16(ulong address, ushort value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite32(ulong address, int value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite32(ulong address, uint value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite64(ulong address, long value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite64(ulong address, ulong value) => MemoryWrite(address, BitConverter.GetBytes(value)); public void MemoryMap(ulong address, ulong size, MemoryPermission permissions) { - Interface.Checked(Interface.uc_mem_map(uc, address, size, (uint)permissions)); + uc.MemMap((long)address, (long)size, (int)permissions); } public void MemoryUnmap(ulong address, ulong size) { - Interface.Checked(Interface.uc_mem_unmap(uc, address, size)); + uc.MemUnmap((long)address, (long)size); } public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions) { - Interface.Checked(Interface.uc_mem_protect(uc, address, size, (uint)permissions)); - } - - public static bool IsAvailable() - { - try - { - Interface.uc_version(out _, out _); - } - catch (DllNotFoundException) { } - - return Interface.IsUnicornAvailable; + uc.MemProtect((long)address, (long)size, (int)permissions); } } } \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/UnicornException.cs b/Ryujinx.Tests.Unicorn/UnicornException.cs deleted file mode 100644 index b5c5f980e..000000000 --- a/Ryujinx.Tests.Unicorn/UnicornException.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Ryujinx.Tests.Unicorn.Native.Const; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Tests.Unicorn -{ - public class UnicornException : Exception - { - public readonly Error Error; - - internal UnicornException(Error error) - { - Error = error; - } - - public override string Message - { - get - { - return Marshal.PtrToStringAnsi(Native.Interface.uc_strerror(Error)); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/libs/README.md b/Ryujinx.Tests.Unicorn/libs/README.md deleted file mode 100644 index d05291e5c..000000000 --- a/Ryujinx.Tests.Unicorn/libs/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Unicorn - -Unicorn is a CPU simulator with bindings in many languages, including -C#/.NET. -It is used by the Ryujinx test suite for comparative testing with its built-in -CPU simulator, Armeilleure. - -## Windows - -On Windows, Unicorn is shipped as a pre-compiled dynamic library (`.dll`), licenced under the GPLv2. - -The source code for `windows/unicorn.dll` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698 - -## Linux - -On Windows, Unicorn is shipped as a pre-compiled shared object (`.so`), licenced under the GPLv2. - -The source code for `linux/unicorn.so` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698 - -See https://github.com/Ryujinx/Ryujinx/pull/1433 for details. diff --git a/Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so b/Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so deleted file mode 100644 index 8d0948af99546040326cf2da088ba0a062d21f1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4500288 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­+o_;|HhkDGn9+mFYi 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 z�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 =?{% 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ɣ*b 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 qjo%!8y^W#7rt&nRL37Z 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%0A_uRYXSXE=q(g;*CUbP4e+wjBN8wyF#+j?qlR# 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%0A`KO85&i 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Ξ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%0AX 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%0D-|r>-RgKx@b*k#@xEHE%I}3*9<K&^ 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%0AFyvV4i~j2oxF;!f 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�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 N<BwKkngm~|C@lMm{2E@8TY_;~X3 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 diff --git a/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll b/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll deleted file mode 100644 index 1c84586e410a0169002b19897cc7613336b7a62f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2178048 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≧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�?tYx6)r=o?Y8+UBe%DYkC8QR2P~* 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�pT2$DrtC+0N@}L|FD)}oJf)>+5BO<H&&<e3 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 |PF%bQz9 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¥l;uPlTvx>&KcmjZ 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 Tc?xweX8iu;R3-n>|NqZF&SDnX diff --git a/Ryujinx.Tests.Unicorn/unicorn_const_generator.py b/Ryujinx.Tests.Unicorn/unicorn_const_generator.py deleted file mode 100644 index 813485fd2..000000000 --- a/Ryujinx.Tests.Unicorn/unicorn_const_generator.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python3 -# Unicorn Engine -# By Dang Hoang Vu, 2013 -# Modified for Ryujinx from: https://github.com/unicorn-engine/unicorn/blob/6c1cbef6ac505d355033aef1176b684d02e1eb3a/bindings/const_generator.py -from __future__ import print_function -import sys, re, os - -include = [ 'arm.h', 'arm64.h', 'unicorn.h' ] -split_common = [ 'ARCH', 'MODE', 'ERR', 'MEM', 'TCG', 'HOOK', 'PROT' ] - -template = { - 'dotnet': { - 'header': "// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT\n\n// ReSharper disable InconsistentNaming\nnamespace Ryujinx.Tests.Unicorn.Native.Const\n{\n public enum %s\n {\n", - 'footer': " }\n}\n", - 'line_format': ' %s = %s,\n', - 'out_file': os.path.join(os.path.dirname(__file__), 'Native', 'Const', '%s.cs'), - # prefixes for constant filenames of all archs - case sensitive - 'arm.h': 'Arm', - 'arm64.h': 'Arm64', - 'unicorn.h': 'Common', - # prefixes for filenames of split_common values - case sensitive - 'ARCH': 'Arch', - 'MODE': 'Mode', - 'ERR': 'Error', - 'MEM': 'Memory', - 'TCG': 'TCG', - 'HOOK': 'Hook', - 'PROT': 'Permission', - 'comment_open': ' //', - 'comment_close': '', - } -} - -# markup for comments to be added to autogen files -MARKUP = '//>' - -def gen(unicorn_repo_path): - global include - include_dir = os.path.join(unicorn_repo_path, 'include', 'unicorn') - templ = template["dotnet"] - for target in include: - prefix = templ[target] - outfile = open(templ['out_file'] %(prefix), 'wb') # open as binary prevents windows newlines - outfile.write((templ['header'] % (prefix)).encode("utf-8")) - if target == 'unicorn.h': - prefix = '' - for cat in split_common: - with open(templ['out_file'] %(templ[cat]), 'wb') as file: - file.write((templ['header'] %(templ[cat])).encode("utf-8")) - with open(os.path.join(include_dir, target)) as f: - lines = f.readlines() - - previous = {} - count = 0 - skip = 0 - in_comment = False - - for lno, line in enumerate(lines): - if "/*" in line: - in_comment = True - if "*/" in line: - in_comment = False - if in_comment: - continue - if skip > 0: - # Due to clang-format, values may come up in the next line - skip -= 1 - continue - line = line.strip() - - if line.startswith(MARKUP): # markup for comments - outfile.write(("\n%s%s%s\n" %(templ['comment_open'], \ - line.replace(MARKUP, ''), templ['comment_close'])).encode("utf-8")) - continue - - if line == '' or line.startswith('//'): - continue - - tmp = line.strip().split(',') - if len(tmp) >= 2 and tmp[0] != "#define" and not tmp[0].startswith("UC_"): - continue - for t in tmp: - t = t.strip() - if not t or t.startswith('//'): continue - f = re.split('\s+', t) - - # parse #define UC_TARGET (num) - define = False - if f[0] == '#define' and len(f) >= 3: - define = True - f.pop(0) - f.insert(1, '=') - if f[0].startswith("UC_" + prefix.upper()) or f[0].startswith("UC_CPU"): - if len(f) > 1 and f[1] not in ('//', '='): - print("WARNING: Unable to convert %s" % f) - print(" Line =", line) - continue - elif len(f) > 1 and f[1] == '=': - # Like: - # UC_A = - # (1 << 2) - # #define UC_B \ - # (UC_A | UC_C) - # Let's search the next line - if len(f) == 2: - if lno == len(lines) - 1: - print("WARNING: Unable to convert %s" % f) - print(" Line =", line) - continue - skip += 1 - next_line = lines[lno + 1] - next_line_tmp = next_line.strip().split(",") - rhs = next_line_tmp[0] - elif f[-1] == "\\": - idx = 0 - rhs = "" - while True: - idx += 1 - if lno + idx == len(lines): - print("WARNING: Unable to convert %s" % f) - print(" Line =", line) - continue - skip += 1 - next_line = lines[lno + idx] - next_line_f = re.split('\s+', next_line.strip()) - if next_line_f[-1] == "\\": - rhs += "".join(next_line_f[:-1]) - else: - rhs += next_line.strip() - break - else: - rhs = ''.join(f[2:]) - else: - rhs = str(count) - - - lhs = f[0].strip() - #print(f'lhs: {lhs} rhs: {rhs} f:{f}') - # evaluate bitshifts in constants e.g. "UC_X86 = 1 << 1" - match = re.match(r'(?P<rhs>\s*\d+\s*<<\s*\d+\s*)', rhs) - if match: - rhs = str(eval(match.group(1))) - else: - # evaluate references to other constants e.g. "UC_ARM_REG_X = UC_ARM_REG_SP" - match = re.match(r'^([^\d]\w+)$', rhs) - if match: - rhs = previous[match.group(1)] - - if not rhs.isdigit(): - for k, v in previous.items(): - rhs = re.sub(r'\b%s\b' % k, v, rhs) - rhs = str(eval(rhs)) - - lhs_strip = re.sub(r'^UC_', '', lhs) - count = int(rhs) + 1 - - if target == "unicorn.h": - matched_cat = False - for cat in split_common: - if lhs_strip.startswith(f"{cat}_"): - with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file: - cat_lhs_strip = lhs_strip - if not lhs_strip.lstrip(f"{cat}_").isnumeric(): - cat_lhs_strip = lhs_strip.replace(f"{cat}_", "", 1) - cat_file.write( - (templ['line_format'] % (cat_lhs_strip, rhs)).encode("utf-8")) - matched_cat = True - break - if matched_cat: - previous[lhs] = str(rhs) - continue - - if (count == 1): - outfile.write(("\n").encode("utf-8")) - - if lhs_strip.startswith(f"{prefix.upper()}_") and not lhs_strip.replace(f"{prefix.upper()}_", "", 1).isnumeric(): - lhs_strip = lhs_strip.replace(f"{prefix.upper()}_", "", 1) - - outfile.write((templ['line_format'] % (lhs_strip, rhs)).encode("utf-8")) - previous[lhs] = str(rhs) - - outfile.write((templ['footer']).encode("utf-8")) - outfile.close() - - if target == "unicorn.h": - for cat in split_common: - with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file: - cat_file.write(templ['footer'].encode('utf-8')) - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage:", sys.argv[0], " <path to unicorn repo>") - sys.exit(1) - unicorn_repo_path = sys.argv[1] - if os.path.isdir(unicorn_repo_path): - print("Generating constants for dotnet") - gen(unicorn_repo_path) - else: - print("Couldn't find unicorn repo at:", unicorn_repo_path) diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index b64f74668..979b313b0 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -6,7 +6,6 @@ using Ryujinx.Cpu.Jit; using Ryujinx.Memory; using Ryujinx.Tests.Unicorn; using System; - using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission; namespace Ryujinx.Tests.Cpu @@ -33,18 +32,10 @@ namespace Ryujinx.Tests.Cpu private CpuContext _cpuContext; - private static bool _unicornAvailable; private UnicornAArch64 _unicornEmu; private bool _usingMemory; - [OneTimeSetUp] - public void OneTimeSetup() - { - _unicornAvailable = UnicornAArch64.IsAvailable(); - Assume.That(_unicornAvailable, "Unicorn is not available"); - } - [SetUp] public void Setup() { @@ -65,23 +56,17 @@ namespace Ryujinx.Tests.Cpu Optimizations.AllowLcqInFunctionTable = false; Optimizations.UseUnmanagedDispatchLoop = false; - if (_unicornAvailable) - { - _unicornEmu = new UnicornAArch64(); - _unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.READ | MemoryPermission.EXEC); - _unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.READ | MemoryPermission.WRITE); - _unicornEmu.PC = CodeBaseAddress; - } + _unicornEmu = new UnicornAArch64(); + _unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Exec); + _unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Write); + _unicornEmu.PC = CodeBaseAddress; } [TearDown] public void Teardown() { - if (_unicornAvailable) - { - _unicornEmu.Dispose(); - _unicornEmu = null; - } + _unicornEmu.Dispose(); + _unicornEmu = null; _memory.DecrementReferenceCount(); _context.Dispose(); @@ -105,10 +90,7 @@ namespace Ryujinx.Tests.Cpu { _memory.Write(_currAddress, opcode); - if (_unicornAvailable) - { - _unicornEmu.MemoryWrite32(_currAddress, opcode); - } + _unicornEmu.MemoryWrite32(_currAddress, opcode); _currAddress += 4; } @@ -158,38 +140,35 @@ namespace Ryujinx.Tests.Cpu _context.Fpcr = (FPCR)fpcr; _context.Fpsr = (FPSR)fpsr; - if (_unicornAvailable) - { - _unicornEmu.X[0] = x0; - _unicornEmu.X[1] = x1; - _unicornEmu.X[2] = x2; - _unicornEmu.X[3] = x3; - _unicornEmu.SP = x31; + _unicornEmu.X[0] = x0; + _unicornEmu.X[1] = x1; + _unicornEmu.X[2] = x2; + _unicornEmu.X[3] = x3; + _unicornEmu.SP = x31; - _unicornEmu.Q[0] = V128ToSimdValue(v0); - _unicornEmu.Q[1] = V128ToSimdValue(v1); - _unicornEmu.Q[2] = V128ToSimdValue(v2); - _unicornEmu.Q[3] = V128ToSimdValue(v3); - _unicornEmu.Q[4] = V128ToSimdValue(v4); - _unicornEmu.Q[5] = V128ToSimdValue(v5); - _unicornEmu.Q[30] = V128ToSimdValue(v30); - _unicornEmu.Q[31] = V128ToSimdValue(v31); + _unicornEmu.Q[0] = V128ToSimdValue(v0); + _unicornEmu.Q[1] = V128ToSimdValue(v1); + _unicornEmu.Q[2] = V128ToSimdValue(v2); + _unicornEmu.Q[3] = V128ToSimdValue(v3); + _unicornEmu.Q[4] = V128ToSimdValue(v4); + _unicornEmu.Q[5] = V128ToSimdValue(v5); + _unicornEmu.Q[30] = V128ToSimdValue(v30); + _unicornEmu.Q[31] = V128ToSimdValue(v31); - _unicornEmu.OverflowFlag = overflow; - _unicornEmu.CarryFlag = carry; - _unicornEmu.ZeroFlag = zero; - _unicornEmu.NegativeFlag = negative; + _unicornEmu.OverflowFlag = overflow; + _unicornEmu.CarryFlag = carry; + _unicornEmu.ZeroFlag = zero; + _unicornEmu.NegativeFlag = negative; - _unicornEmu.Fpcr = fpcr; - _unicornEmu.Fpsr = fpsr; - } + _unicornEmu.Fpcr = fpcr; + _unicornEmu.Fpsr = fpsr; } protected void ExecuteOpcodes(bool runUnicorn = true) { _cpuContext.Execute(_context, CodeBaseAddress); - if (_unicornAvailable && runUnicorn) + if (runUnicorn) { _unicornEmu.RunForCount((_currAddress - CodeBaseAddress - 4) / 4); } @@ -239,10 +218,7 @@ namespace Ryujinx.Tests.Cpu { _memory.Write(DataBaseAddress + offset, data); - if (_unicornAvailable) - { - _unicornEmu.MemoryWrite(DataBaseAddress + offset, data); - } + _unicornEmu.MemoryWrite(DataBaseAddress + offset, data); _usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too. } @@ -251,10 +227,7 @@ namespace Ryujinx.Tests.Cpu { _memory.Write(DataBaseAddress + offset, data); - if (_unicornAvailable) - { - _unicornEmu.MemoryWrite8(DataBaseAddress + offset, data); - } + _unicornEmu.MemoryWrite8(DataBaseAddress + offset, data); _usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too. } @@ -331,11 +304,6 @@ namespace Ryujinx.Tests.Cpu FpSkips fpSkips = FpSkips.None, FpTolerances fpTolerances = FpTolerances.None) { - if (!_unicornAvailable) - { - return; - } - if (IgnoreAllExcept_FpsrQc) { fpsrMask &= Fpsr.Qc; diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs index 46ae3c771..47dc9f8a8 100644 --- a/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -6,7 +6,6 @@ using Ryujinx.Cpu.Jit; using Ryujinx.Memory; using Ryujinx.Tests.Unicorn; using System; - using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission; namespace Ryujinx.Tests.Cpu @@ -27,19 +26,10 @@ namespace Ryujinx.Tests.Cpu private ExecutionContext _context; private CpuContext _cpuContext; - - private static bool _unicornAvailable; private UnicornAArch32 _unicornEmu; private bool _usingMemory; - [OneTimeSetUp] - public void OneTimeSetup() - { - _unicornAvailable = UnicornAArch32.IsAvailable(); - Assume.That(_unicornAvailable, "Unicorn is not available"); - } - [SetUp] public void Setup() { @@ -61,23 +51,17 @@ namespace Ryujinx.Tests.Cpu Optimizations.AllowLcqInFunctionTable = false; Optimizations.UseUnmanagedDispatchLoop = false; - if (_unicornAvailable) - { - _unicornEmu = new UnicornAArch32(); - _unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.READ | MemoryPermission.EXEC); - _unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.READ | MemoryPermission.WRITE); - _unicornEmu.PC = CodeBaseAddress; - } + _unicornEmu = new UnicornAArch32(); + _unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Exec); + _unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Write); + _unicornEmu.PC = CodeBaseAddress; } [TearDown] public void Teardown() { - if (_unicornAvailable) - { - _unicornEmu.Dispose(); - _unicornEmu = null; - } + _unicornEmu.Dispose(); + _unicornEmu = null; _memory.DecrementReferenceCount(); _context.Dispose(); @@ -101,10 +85,7 @@ namespace Ryujinx.Tests.Cpu { _memory.Write(_currAddress, opcode); - if (_unicornAvailable) - { - _unicornEmu.MemoryWrite32(_currAddress, opcode); - } + _unicornEmu.MemoryWrite32(_currAddress, opcode); _currAddress += 4; } @@ -113,10 +94,7 @@ namespace Ryujinx.Tests.Cpu { _memory.Write(_currAddress, opcode); - if (_unicornAvailable) - { - _unicornEmu.MemoryWrite16(_currAddress, opcode); - } + _unicornEmu.MemoryWrite16(_currAddress, opcode); _currAddress += 2; } @@ -169,40 +147,37 @@ namespace Ryujinx.Tests.Cpu _context.SetPstateFlag(PState.TFlag, thumb); - if (_unicornAvailable) - { - _unicornEmu.R[0] = r0; - _unicornEmu.R[1] = r1; - _unicornEmu.R[2] = r2; - _unicornEmu.R[3] = r3; - _unicornEmu.SP = sp; + _unicornEmu.R[0] = r0; + _unicornEmu.R[1] = r1; + _unicornEmu.R[2] = r2; + _unicornEmu.R[3] = r3; + _unicornEmu.SP = sp; - _unicornEmu.Q[0] = V128ToSimdValue(v0); - _unicornEmu.Q[1] = V128ToSimdValue(v1); - _unicornEmu.Q[2] = V128ToSimdValue(v2); - _unicornEmu.Q[3] = V128ToSimdValue(v3); - _unicornEmu.Q[4] = V128ToSimdValue(v4); - _unicornEmu.Q[5] = V128ToSimdValue(v5); - _unicornEmu.Q[14] = V128ToSimdValue(v14); - _unicornEmu.Q[15] = V128ToSimdValue(v15); + _unicornEmu.Q[0] = V128ToSimdValue(v0); + _unicornEmu.Q[1] = V128ToSimdValue(v1); + _unicornEmu.Q[2] = V128ToSimdValue(v2); + _unicornEmu.Q[3] = V128ToSimdValue(v3); + _unicornEmu.Q[4] = V128ToSimdValue(v4); + _unicornEmu.Q[5] = V128ToSimdValue(v5); + _unicornEmu.Q[14] = V128ToSimdValue(v14); + _unicornEmu.Q[15] = V128ToSimdValue(v15); - _unicornEmu.QFlag = saturation; - _unicornEmu.OverflowFlag = overflow; - _unicornEmu.CarryFlag = carry; - _unicornEmu.ZeroFlag = zero; - _unicornEmu.NegativeFlag = negative; + _unicornEmu.QFlag = saturation; + _unicornEmu.OverflowFlag = overflow; + _unicornEmu.CarryFlag = carry; + _unicornEmu.ZeroFlag = zero; + _unicornEmu.NegativeFlag = negative; - _unicornEmu.Fpscr = fpscr; + _unicornEmu.Fpscr = fpscr; - _unicornEmu.ThumbFlag = thumb; - } + _unicornEmu.ThumbFlag = thumb; } protected void ExecuteOpcodes(bool runUnicorn = true) { _cpuContext.Execute(_context, CodeBaseAddress); - if (_unicornAvailable && runUnicorn) + if (runUnicorn) { _unicornEmu.RunForCount((_currAddress - CodeBaseAddress - 4) / 4); } @@ -322,10 +297,7 @@ namespace Ryujinx.Tests.Cpu { _memory.Write(DataBaseAddress + offset, data); - if (_unicornAvailable) - { - _unicornEmu.MemoryWrite(DataBaseAddress + offset, data); - } + _unicornEmu.MemoryWrite(DataBaseAddress + offset, data); _usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too. } @@ -407,11 +379,6 @@ namespace Ryujinx.Tests.Cpu FpSkips fpSkips = FpSkips.None, FpTolerances fpTolerances = FpTolerances.None) { - if (!_unicornAvailable) - { - return; - } - if (fpSkips != FpSkips.None) { ManageFpSkips(fpSkips); diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs index 0c4aa3b0e..7318d9793 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAlu.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -1,7 +1,6 @@ #define Alu using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -91,12 +90,10 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; - [Test, Pairwise, Description("CLS <Xd>, <Xn>")] public void Cls_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_GenLeadingSignsX_")] [Random(RndCnt)] ulong xn) + [ValueSource(nameof(_GenLeadingSignsX_))] ulong xn) { uint opcode = 0xDAC01400; // CLS X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -111,7 +108,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLS <Wd>, <Wn>")] public void Cls_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_GenLeadingSignsW_")] [Random(RndCnt)] uint wn) + [ValueSource(nameof(_GenLeadingSignsW_))] uint wn) { uint opcode = 0x5AC01400; // CLS W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -126,7 +123,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ <Xd>, <Xn>")] public void Clz_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_GenLeadingZerosX_")] [Random(RndCnt)] ulong xn) + [ValueSource(nameof(_GenLeadingZerosX_))] ulong xn) { uint opcode = 0xDAC01000; // CLZ X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -141,7 +138,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ <Wd>, <Wn>")] public void Clz_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_GenLeadingZerosW_")] [Random(RndCnt)] uint wn) + [ValueSource(nameof(_GenLeadingZerosW_))] uint wn) { uint opcode = 0x5AC01000; // CLZ W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -157,7 +154,7 @@ namespace Ryujinx.Tests.Cpu public void Rbit_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn) { uint opcode = 0xDAC00000; // RBIT X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -173,7 +170,7 @@ namespace Ryujinx.Tests.Cpu public void Rbit_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + 0x80000000u, 0xFFFFFFFFu)] uint wn) { uint opcode = 0x5AC00000; // RBIT W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -189,7 +186,7 @@ namespace Ryujinx.Tests.Cpu public void Rev16_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn) { uint opcode = 0xDAC00400; // REV16 X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -205,7 +202,7 @@ namespace Ryujinx.Tests.Cpu public void Rev16_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + 0x80000000u, 0xFFFFFFFFu)] uint wn) { uint opcode = 0x5AC00400; // REV16 W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -221,7 +218,7 @@ namespace Ryujinx.Tests.Cpu public void Rev32_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn) { uint opcode = 0xDAC00800; // REV32 X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -237,7 +234,7 @@ namespace Ryujinx.Tests.Cpu public void Rev32_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + 0x80000000u, 0xFFFFFFFFu)] uint wn) { uint opcode = 0x5AC00800; // REV W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -253,7 +250,7 @@ namespace Ryujinx.Tests.Cpu public void Rev64_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn) { uint opcode = 0xDAC00C00; // REV64 X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -266,4 +263,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu32.cs b/Ryujinx.Tests/Cpu/CpuTestAlu32.cs index c7537cd9b..0d009e90e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAlu32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAlu32.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _SU_H_AddSub_8_() { - return new uint[] + return new[] { 0xe6100f90u, // SADD8 R0, R0, R0 0xe6100ff0u, // SSUB8 R0, R0, R0 @@ -27,7 +27,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Ssat_Usat_() { - return new uint[] + return new[] { 0xe6a00010u, // SSAT R0, #1, R0, LSL #0 0xe6a00050u, // SSAT R0, #1, R0, ASR #32 @@ -38,7 +38,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Ssat16_Usat16_() { - return new uint[] + return new[] { 0xe6a00f30u, // SSAT16 R0, #1, R0 0xe6e00f30u, // USAT16 R0, #0, R0 @@ -47,7 +47,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Lsr_Lsl_Asr_Ror_() { - return new uint[] + return new[] { 0xe1b00030u, // LSRS R0, R0, R0 0xe1b00010u, // LSLS R0, R0, R0 @@ -63,7 +63,7 @@ namespace Ryujinx.Tests.Cpu public void Rbit_32bit([Values(0u, 0xdu)] uint rd, [Values(1u, 0xdu)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + 0x80000000u, 0xFFFFFFFFu)] uint wn) { uint opcode = 0xe6ff0f30u; // RBIT R0, R0 opcode |= ((rm & 15) << 0) | ((rd & 15) << 12); @@ -76,10 +76,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Lsr_Lsl_Asr_Ror([ValueSource("_Lsr_Lsl_Asr_Ror_")] uint opcode, + public void Lsr_Lsl_Asr_Ror([ValueSource(nameof(_Lsr_Lsl_Asr_Ror_))] uint opcode, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint shiftValue, - [Range(0, 31)] [Values(32, 256, 768, -1, -23)] int shiftAmount) + 0x80000000u, 0xFFFFFFFFu)] uint shiftValue, + [Range(0, 31)] int shiftAmount) { uint rd = 0; uint rm = 1; @@ -130,13 +130,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Ssat_Usat([ValueSource("_Ssat_Usat_")] uint opcode, + public void Ssat_Usat([ValueSource(nameof(_Ssat_Usat_))] uint opcode, [Values(0u, 0xdu)] uint rd, [Values(1u, 0xdu)] uint rn, [Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint sat, [Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint shift, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + 0x80000000u, 0xFFFFFFFFu)] uint wn) { opcode |= ((rn & 15) << 0) | ((shift & 31) << 7) | ((rd & 15) << 12) | ((sat & 31) << 16); @@ -148,12 +148,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Ssat16_Usat16([ValueSource("_Ssat16_Usat16_")] uint opcode, + public void Ssat16_Usat16([ValueSource(nameof(_Ssat16_Usat16_))] uint opcode, [Values(0u, 0xdu)] uint rd, [Values(1u, 0xdu)] uint rn, [Values(0u, 7u, 8u, 0xfu)] uint sat, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + 0x80000000u, 0xFFFFFFFFu)] uint wn) { opcode |= ((rn & 15) << 0) | ((rd & 15) << 12) | ((sat & 15) << 16); @@ -165,7 +165,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_H_AddSub_8([ValueSource("_SU_H_AddSub_8_")] uint opcode, + public void SU_H_AddSub_8([ValueSource(nameof(_SU_H_AddSub_8_))] uint opcode, [Values(0u, 0xdu)] uint rd, [Values(1u)] uint rm, [Values(2u)] uint rn, @@ -206,4 +206,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs b/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs index eb94b8450..0265e5230 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Tests.Cpu // - xor 0 // Only includes non-C variant, as the other can be tested with unicorn. - return new CrcTest[] + return new[] { new CrcTest(0x00000000u, 0x00_00_00_00_00_00_00_00u, false, 0x00000000, 0x00000000, 0x00000000, 0x00000000), new CrcTest(0x00000000u, 0x7f_ff_ff_ff_ff_ff_ff_ffu, false, 0x2d02ef8d, 0xbe2612ff, 0xdebb20e3, 0xa9de8355), @@ -53,14 +53,12 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; - [Test, Combinatorial] public void Crc32_b_h_w_x([Values(0u)] uint rd, [Values(1u)] uint rn, [Values(2u)] uint rm, [Range(0u, 3u)] uint size, - [ValueSource("_CRC32_Test_Values_")] CrcTest test) + [ValueSource(nameof(_CRC32_Test_Values_))] CrcTest test) { uint opcode = 0x1AC04000; // CRC32B W0, W0, W0 @@ -85,11 +83,11 @@ namespace Ryujinx.Tests.Cpu public void Crc32x([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((ulong)0x00_00_00_00_00_00_00_00, (ulong)0x7F_FF_FF_FF_FF_FF_FF_FF, - (ulong)0x80_00_00_00_00_00_00_00, - (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong xm) + 0x80_00_00_00_00_00_00_00, + 0xFF_FF_FF_FF_FF_FF_FF_FF)] ulong xm) { uint opcode = 0x9AC04C00; // CRC32X W0, W0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -105,9 +103,9 @@ namespace Ryujinx.Tests.Cpu public void Crc32w([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF, - (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint wm) + 0x80_00_00_00, 0xFF_FF_FF_FF)] uint wm) { uint opcode = 0x1AC04800; // CRC32W W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -123,9 +121,9 @@ namespace Ryujinx.Tests.Cpu public void Crc32h([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((ushort)0x00_00, (ushort)0x7F_FF, - (ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort wm) + (ushort)0x80_00, (ushort)0xFF_FF)] ushort wm) { uint opcode = 0x1AC04400; // CRC32H W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -141,9 +139,9 @@ namespace Ryujinx.Tests.Cpu public void Crc32b([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm) + (byte)0x80, (byte)0xFF)] byte wm) { uint opcode = 0x1AC04000; // CRC32B W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -159,11 +157,11 @@ namespace Ryujinx.Tests.Cpu public void Crc32cx([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((ulong)0x00_00_00_00_00_00_00_00, (ulong)0x7F_FF_FF_FF_FF_FF_FF_FF, - (ulong)0x80_00_00_00_00_00_00_00, - (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong xm) + 0x80_00_00_00_00_00_00_00, + 0xFF_FF_FF_FF_FF_FF_FF_FF)] ulong xm) { uint opcode = 0x9AC05C00; // CRC32CX W0, W0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -179,9 +177,9 @@ namespace Ryujinx.Tests.Cpu public void Crc32cw([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF, - (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint wm) + 0x80_00_00_00, 0xFF_FF_FF_FF)] uint wm) { uint opcode = 0x1AC05800; // CRC32CW W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -197,9 +195,9 @@ namespace Ryujinx.Tests.Cpu public void Crc32ch([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((ushort)0x00_00, (ushort)0x7F_FF, - (ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort wm) + (ushort)0x80_00, (ushort)0xFF_FF)] ushort wm) { uint opcode = 0x1AC05400; // CRC32CH W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -215,9 +213,9 @@ namespace Ryujinx.Tests.Cpu public void Crc32cb([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0x00000000u, 0xFFFFFFFFu)] uint wn, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm) + (byte)0x80, (byte)0xFF)] byte wm) { uint opcode = 0x1AC05000; // CRC32CB W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -234,9 +232,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9AC00C00; // SDIV X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -253,9 +251,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { uint opcode = 0x1AC00C00; // SDIV W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -272,9 +270,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9AC00800; // UDIV X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -291,9 +289,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { uint opcode = 0x1AC00800; // UDIV W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -306,4 +304,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs b/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs index c402c4baa..d92a95224 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Tests.Cpu // - bytes in order of increasing significance // - xor 0 - return new CrcTest32[] + return new[] { new CrcTest32(0x00000000u, 0x00_00_00_00u, false, 0x00000000, 0x00000000, 0x00000000), new CrcTest32(0x00000000u, 0x7f_ff_ff_ffu, false, 0x2d02ef8d, 0xbe2612ff, 0x3303a3c3), @@ -70,7 +70,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u)] uint rn, [Values(2u)] uint rm, [Range(0u, 2u)] uint size, - [ValueSource("_CRC32_Test_Values_")] CrcTest32 test) + [ValueSource(nameof(_CRC32_Test_Values_))] CrcTest32 test) { // Unicorn does not yet support 32bit crc instructions, so test against a known table of results/values. diff --git a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs index 9551ce2ce..c97ef9ed0 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs @@ -8,17 +8,13 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestAluImm : CpuTest { #if AluImm - private const int RndCnt = 2; - private const int RndCntImm = 2; - private const int RndCntImms = 2; - private const int RndCntImmr = 2; [Test, Pairwise, Description("ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}")] public void Add_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0x91000000; // ADD X0, X0, #0, LSL #0 @@ -41,8 +37,8 @@ namespace Ryujinx.Tests.Cpu public void Add_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0x11000000; // ADD W0, W0, #0, LSL #0 @@ -65,8 +61,8 @@ namespace Ryujinx.Tests.Cpu public void Adds_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0xB1000000; // ADDS X0, X0, #0, LSL #0 @@ -89,8 +85,8 @@ namespace Ryujinx.Tests.Cpu public void Adds_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0x31000000; // ADDS W0, W0, #0, LSL #0 @@ -113,9 +109,9 @@ namespace Ryujinx.Tests.Cpu public void And_N1_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // <imm> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u, 32u, 62u)] uint imms, // <imm> + [Values(0u, 31u, 32u, 63u)] uint immr) // <imm> { uint opcode = 0x92400000; // AND X0, X0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -132,9 +128,9 @@ namespace Ryujinx.Tests.Cpu public void And_N0_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0x92000000; // AND X0, X0, #0x100000001 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -151,9 +147,9 @@ namespace Ryujinx.Tests.Cpu public void And_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0x12000000; // AND W0, W0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -170,9 +166,9 @@ namespace Ryujinx.Tests.Cpu public void Ands_N1_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // <imm> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u, 32u, 62u)] uint imms, // <imm> + [Values(0u, 31u, 32u, 63u)] uint immr) // <imm> { uint opcode = 0xF2400000; // ANDS X0, X0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -189,9 +185,9 @@ namespace Ryujinx.Tests.Cpu public void Ands_N0_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0xF2000000; // ANDS X0, X0, #0x100000001 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -208,9 +204,9 @@ namespace Ryujinx.Tests.Cpu public void Ands_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0x72000000; // ANDS W0, W0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -227,9 +223,9 @@ namespace Ryujinx.Tests.Cpu public void Eor_N1_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // <imm> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u, 32u, 62u)] uint imms, // <imm> + [Values(0u, 31u, 32u, 63u)] uint immr) // <imm> { uint opcode = 0xD2400000; // EOR X0, X0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -246,9 +242,9 @@ namespace Ryujinx.Tests.Cpu public void Eor_N0_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0xD2000000; // EOR X0, X0, #0x100000001 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -265,9 +261,9 @@ namespace Ryujinx.Tests.Cpu public void Eor_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0x52000000; // EOR W0, W0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -284,9 +280,9 @@ namespace Ryujinx.Tests.Cpu public void Orr_N1_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // <imm> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u, 32u, 62u)] uint imms, // <imm> + [Values(0u, 31u, 32u, 63u)] uint immr) // <imm> { uint opcode = 0xB2400000; // ORR X0, X0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -303,9 +299,9 @@ namespace Ryujinx.Tests.Cpu public void Orr_N0_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0xB2000000; // ORR X0, X0, #0x100000001 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -322,9 +318,9 @@ namespace Ryujinx.Tests.Cpu public void Orr_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // <imm> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // <imm> + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 30u)] uint imms, // <imm> + [Values(0u, 15u, 16u, 31u)] uint immr) // <imm> { uint opcode = 0x32000000; // ORR W0, W0, #0x1 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -341,8 +337,8 @@ namespace Ryujinx.Tests.Cpu public void Sub_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0xD1000000; // SUB X0, X0, #0, LSL #0 @@ -365,8 +361,8 @@ namespace Ryujinx.Tests.Cpu public void Sub_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0x51000000; // SUB W0, W0, #0, LSL #0 @@ -389,8 +385,8 @@ namespace Ryujinx.Tests.Cpu public void Subs_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0xF1000000; // SUBS X0, X0, #0, LSL #0 @@ -413,8 +409,8 @@ namespace Ryujinx.Tests.Cpu public void Subs_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12> { uint opcode = 0x71000000; // SUBS W0, W0, #0, LSL #0 @@ -434,4 +430,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs b/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs index 440642c38..cc12f3871 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _opcodes() { - return new uint[] + return new[] { 0xe2a00000u, // ADC R0, R0, #0 0xe2b00000u, // ADCS R0, R0, #0 @@ -33,10 +33,9 @@ namespace Ryujinx.Tests.Cpu #endregion private const int RndCnt = 2; - private const int RndCntAmount = 2; [Test, Pairwise] - public void TestCpuTestAluImm32([ValueSource("_opcodes")] uint opcode, + public void TestCpuTestAluImm32([ValueSource(nameof(_opcodes))] uint opcode, [Values(0u, 13u)] uint rd, [Values(1u, 13u)] uint rn, [Random(RndCnt)] uint imm, @@ -53,4 +52,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs index 418dd56d2..20e0e396e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs @@ -8,18 +8,15 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestAluRs : CpuTest { #if AluRs - private const int RndCnt = 2; - private const int RndCntAmount = 2; - private const int RndCntLsb = 2; [Test, Pairwise, Description("ADC <Xd>, <Xn>, <Xm>")] public void Adc_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values] bool carryIn) { uint opcode = 0x9A000000; // ADC X0, X0, X0 @@ -37,9 +34,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values] bool carryIn) { uint opcode = 0x1A000000; // ADC W0, W0, W0 @@ -57,9 +54,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values] bool carryIn) { uint opcode = 0xBA000000; // ADCS X0, X0, X0 @@ -77,9 +74,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values] bool carryIn) { uint opcode = 0x3A000000; // ADCS W0, W0, W0 @@ -97,11 +94,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0x8B000000; // ADD X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -119,11 +116,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x0B000000; // ADD W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -141,11 +138,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xAB000000; // ADDS X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -163,11 +160,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x2B000000; // ADDS W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -185,11 +182,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0x8A000000; // AND X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -207,11 +204,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x0A000000; // AND W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -229,11 +226,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xEA000000; // ANDS X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -251,11 +248,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x6A000000; // ANDS W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -273,9 +270,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9AC02800; // ASRV X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -292,9 +289,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { uint opcode = 0x1AC02800; // ASRV W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -311,11 +308,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0x8A200000; // BIC X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -333,11 +330,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x0A200000; // BIC W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -355,11 +352,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xEA200000; // BICS X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -377,11 +374,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x6A200000; // BICS W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -399,11 +396,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xCA200000; // EON X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -421,11 +418,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x4A200000; // EON W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -443,11 +440,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xCA000000; // EOR X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -465,11 +462,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x4A000000; // EOR W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -487,10 +484,10 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntLsb)] uint lsb) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, + [Values(0u, 31u, 32u, 63u)] uint lsb) { uint opcode = 0x93C00000; // EXTR X0, X0, X0, #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -508,10 +505,10 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntLsb)] uint lsb) + 0x80000000u, 0xFFFFFFFFu)] uint wm, + [Values(0u, 15u, 16u, 31u)] uint lsb) { uint opcode = 0x13800000; // EXTR W0, W0, W0, #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -529,9 +526,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9AC02000; // LSLV X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -548,9 +545,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { uint opcode = 0x1AC02000; // LSLV W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -567,9 +564,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9AC02400; // LSRV X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -586,9 +583,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { uint opcode = 0x1AC02400; // LSRV W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -605,11 +602,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xAA200000; // ORN X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -627,11 +624,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x2A200000; // ORN W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -649,11 +646,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xAA000000; // ORR X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -671,11 +668,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x2A000000; // ORR W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -693,9 +690,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9AC02C00; // RORV X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -712,9 +709,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { uint opcode = 0x1AC02C00; // RORV W0, W0, W0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -731,9 +728,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values] bool carryIn) { uint opcode = 0xDA000000; // SBC X0, X0, X0 @@ -751,9 +748,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values] bool carryIn) { uint opcode = 0x5A000000; // SBC W0, W0, W0 @@ -771,9 +768,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values] bool carryIn) { uint opcode = 0xFA000000; // SBCS X0, X0, X0 @@ -791,9 +788,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values] bool carryIn) { uint opcode = 0x7A000000; // SBCS W0, W0, W0 @@ -811,11 +808,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xCB000000; // SUB X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -833,11 +830,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x4B000000; // SUB W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -855,11 +852,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) + [Values(0u, 31u, 32u, 63u)] uint amount) { uint opcode = 0xEB000000; // SUBS X0, X0, X0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -877,11 +874,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { uint opcode = 0x6B000000; // SUBS W0, W0, W0, LSL #0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -895,4 +892,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs b/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs index 25b2c9687..d241aac48 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _Add_Adds_Rsb_Rsbs_() { - return new uint[] + return new[] { 0xe0800000u, // ADD R0, R0, R0, LSL #0 0xe0900000u, // ADDS R0, R0, R0, LSL #0 @@ -23,7 +23,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Adc_Adcs_Rsc_Rscs_Sbc_Sbcs_() { - return new uint[] + return new[] { 0xe0a00000u, // ADC R0, R0, R0 0xe0b00000u, // ADCS R0, R0, R0 @@ -35,8 +35,6 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; - private const int RndCntAmount = 2; [Test, Pairwise] public void Adc_Adcs_Rsc_Rscs_Sbc_Sbcs([ValueSource("_Adc_Adcs_Rsc_Rscs_Sbc_Sbcs_")] uint opcode, @@ -44,9 +42,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 13u)] uint rn, [Values(2u, 13u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values] bool carryIn) { opcode |= ((rm & 15) << 0) | ((rn & 15) << 16) | ((rd & 15) << 12); @@ -64,11 +62,11 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 13u)] uint rn, [Values(2u, 13u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR> - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) + [Values(0u, 15u, 16u, 31u)] uint amount) { opcode |= ((rm & 15) << 0) | ((rn & 15) << 16) | ((rd & 15) << 12); opcode |= ((shift & 3) << 5) | ((amount & 31) << 7); @@ -81,4 +79,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs index 357a96ab9..d51e76209 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs @@ -8,16 +8,15 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestAluRx : CpuTest { #if AluRx - private const int RndCnt = 2; [Test, Pairwise, Description("ADD <Xd|SP>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")] public void Add_X_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, + 0x8000000000000000, 0xFFFFFFFFFFFFFFFF)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { @@ -44,9 +43,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -74,9 +73,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -104,9 +103,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -134,9 +133,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -164,9 +163,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -194,9 +193,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -224,9 +223,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, + 0x8000000000000000, 0xFFFFFFFFFFFFFFFF)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { @@ -244,9 +243,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -265,9 +264,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -286,9 +285,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -307,9 +306,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -328,9 +327,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -349,9 +348,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -370,9 +369,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, + 0x8000000000000000, 0xFFFFFFFFFFFFFFFF)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { @@ -399,9 +398,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -429,9 +428,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -459,9 +458,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -489,9 +488,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -519,9 +518,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -549,9 +548,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -579,9 +578,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, + 0x8000000000000000, 0xFFFFFFFFFFFFFFFF)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { @@ -599,9 +598,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -620,9 +619,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -641,9 +640,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, // <UXTB, UXTH, UXTW, 0b100u, 0b101u, 0b110u)] uint extend, // SXTB, SXTH, SXTW> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -662,9 +661,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, + 0x80000000, 0xFFFFFFFF)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -683,9 +682,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, + (ushort)0x8000, (ushort)0xFFFF)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -704,9 +703,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, + 0x80000000u, 0xFFFFFFFFu)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, + (byte)0x80, (byte)0xFF)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // <UXTB, UXTH, LSL|UXTW, UXTX, 0b100u, 0b101u, 0b110u, 0b111u)] uint extend, // SXTB, SXTH, SXTW, SXTX> [Values(0u, 1u, 2u, 3u, 4u)] uint amount) @@ -721,4 +720,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestBf32.cs b/Ryujinx.Tests/Cpu/CpuTestBf32.cs index 66b8fc062..871e7649f 100644 --- a/Ryujinx.Tests/Cpu/CpuTestBf32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestBf32.cs @@ -10,15 +10,13 @@ namespace Ryujinx.Tests.Cpu { #if Bf32 private const int RndCnt = 2; - private const int RndCntImmr = 2; - private const int RndCntImms = 2; [Test, Pairwise, Description("BFC <Rd>, #<lsb>, #<width>")] public void Bfc([Values(0u, 0xdu)] uint rd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wd, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint msb) + 0x80000000u, 0xFFFFFFFFu)] uint wd, + [Values(0u, 15u, 16u, 31u)] uint lsb, + [Values(0u, 15u, 16u, 31u)] uint msb) { msb = Math.Max(lsb, msb); // Don't test unpredictable for now. uint opcode = 0xe7c0001fu; // BFC R0, #0, #1 @@ -37,9 +35,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0xdu)] uint rn, [Random(RndCnt)] uint wd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint msb) + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 31u)] uint lsb, + [Values(0u, 15u, 16u, 31u)] uint msb) { msb = Math.Max(lsb, msb); // Don't test unpredictable for now. uint opcode = 0xe7c00010u; // BFI R0, R0, #0, #1 @@ -59,9 +57,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0xdu)] uint rn, [Random(RndCnt)] uint wd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint widthm1) + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 31u)] uint lsb, + [Values(0u, 15u, 16u, 31u)] uint widthm1) { if (lsb + widthm1 > 31) { @@ -84,9 +82,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0xdu)] uint rn, [Random(RndCnt)] uint wd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint widthm1) + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 31u)] uint lsb, + [Values(0u, 15u, 16u, 31u)] uint widthm1) { if (lsb + widthm1 > 31) { @@ -105,4 +103,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestBfm.cs b/Ryujinx.Tests/Cpu/CpuTestBfm.cs index 24f69036e..c169ee41c 100644 --- a/Ryujinx.Tests/Cpu/CpuTestBfm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestBfm.cs @@ -9,17 +9,15 @@ namespace Ryujinx.Tests.Cpu { #if Bfm private const int RndCnt = 2; - private const int RndCntImmr = 2; - private const int RndCntImms = 2; [Test, Pairwise, Description("BFM <Xd>, <Xn>, #<immr>, #<imms>")] public void Bfm_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Random(RndCnt)] ulong xd, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr, - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImms)] uint imms) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u, 32u, 63u)] uint immr, + [Values(0u, 31u, 32u, 63u)] uint imms) { uint opcode = 0xB3400000; // BFM X0, X0, #0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -37,9 +35,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Random(RndCnt)] uint wd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint imms) + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 31u)] uint immr, + [Values(0u, 15u, 16u, 31u)] uint imms) { uint opcode = 0x33000000; // BFM W0, W0, #0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -56,9 +54,9 @@ namespace Ryujinx.Tests.Cpu public void Sbfm_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr, - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImms)] uint imms) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u, 32u, 63u)] uint immr, + [Values(0u, 31u, 32u, 63u)] uint imms) { uint opcode = 0x93400000; // SBFM X0, X0, #0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -75,9 +73,9 @@ namespace Ryujinx.Tests.Cpu public void Sbfm_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint imms) + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 31u)] uint immr, + [Values(0u, 15u, 16u, 31u)] uint imms) { uint opcode = 0x13000000; // SBFM W0, W0, #0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -94,9 +92,9 @@ namespace Ryujinx.Tests.Cpu public void Ubfm_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr, - [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImms)] uint imms) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u, 32u, 63u)] uint immr, + [Values(0u, 31u, 32u, 63u)] uint imms) { uint opcode = 0xD3400000; // UBFM X0, X0, #0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -113,9 +111,9 @@ namespace Ryujinx.Tests.Cpu public void Ubfm_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr, - [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint imms) + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 15u, 16u, 31u)] uint immr, + [Values(0u, 15u, 16u, 31u)] uint imms) { uint opcode = 0x53000000; // UBFM W0, W0, #0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -129,4 +127,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs index a2c734494..2c431fb23 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs @@ -8,15 +8,13 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestCcmpImm : CpuTest { #if CcmpImm - private const int RndCnt = 2; - private const int RndCntImm = 2; private const int RndCntNzcv = 2; [Test, Pairwise, Description("CCMN <Xn>, #<imm>, #<nzcv>, <cond>")] public void Ccmn_64bit([Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -37,8 +35,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CCMN <Wn>, #<imm>, #<nzcv>, <cond>")] public void Ccmn_32bit([Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 31u)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -59,8 +57,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CCMP <Xn>, #<imm>, #<nzcv>, <cond>")] public void Ccmp_64bit([Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 31u)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -81,8 +79,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CCMP <Wn>, #<imm>, #<nzcv>, <cond>")] public void Ccmp_32bit([Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 31u)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -101,4 +99,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs index 8cf5268eb..1021de930 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs @@ -8,16 +8,15 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestCcmpReg : CpuTest { #if CcmpReg - private const int RndCnt = 2; private const int RndCntNzcv = 2; [Test, Pairwise, Description("CCMN <Xn>, <Xm>, #<nzcv>, <cond>")] public void Ccmn_64bit([Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -39,9 +38,9 @@ namespace Ryujinx.Tests.Cpu public void Ccmn_32bit([Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -63,9 +62,9 @@ namespace Ryujinx.Tests.Cpu public void Ccmp_64bit([Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -87,9 +86,9 @@ namespace Ryujinx.Tests.Cpu public void Ccmp_32bit([Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -108,4 +107,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestCsel.cs b/Ryujinx.Tests/Cpu/CpuTestCsel.cs index 9764c2b78..379cdfd8e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCsel.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCsel.cs @@ -8,16 +8,15 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestCsel : CpuTest { #if Csel - private const int RndCnt = 2; [Test, Pairwise, Description("CSEL <Xd>, <Xn>, <Xm>, <cond>")] public void Csel_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -39,9 +38,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -63,9 +62,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -87,9 +86,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -111,9 +110,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -135,9 +134,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -159,9 +158,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -183,9 +182,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -203,4 +202,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/Ryujinx.Tests/Cpu/CpuTestMisc.cs index 9c067f4ed..b643a1021 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMisc.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -1,9 +1,7 @@ #define Misc using ARMeilleure.State; - using NUnit.Framework; - using System; using System.Collections.Generic; @@ -59,7 +57,6 @@ namespace Ryujinx.Tests.Cpu #endregion private const int RndCnt = 2; - private const int RndCntImm = 2; private static readonly bool NoZeros = false; private static readonly bool NoInfs = false; @@ -68,8 +65,8 @@ namespace Ryujinx.Tests.Cpu #region "AluImm & Csel" [Test, Pairwise] public void Adds_Csinc_64bit([Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift, // <LSL #0, LSL #12> [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -93,8 +90,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Adds_Csinc_32bit([Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift, // <LSL #0, LSL #12> [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -118,8 +115,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Subs_Csinc_64bit([Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift, // <LSL #0, LSL #12> [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -143,8 +140,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Subs_Csinc_32bit([Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, - [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + 0x80000000u, 0xFFFFFFFFu)] uint wn, + [Values(0u, 4095u)] uint imm, [Values(0b00u, 0b01u)] uint shift, // <LSL #0, LSL #12> [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -412,9 +409,9 @@ namespace Ryujinx.Tests.Cpu [Explicit] [Test, Pairwise] - public void Misc4([ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b, - [ValueSource("_1S_F_")] ulong c, + public void Misc4([ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b, + [ValueSource(nameof(_1S_F_))] ulong c, [Values(0ul, 1ul, 2ul, 3ul)] ulong displacement) { if (!BitConverter.IsLittleEndian) @@ -460,7 +457,7 @@ namespace Ryujinx.Tests.Cpu [Explicit] [Test] - public void Misc5([ValueSource("_1S_F_")] ulong a) + public void Misc5([ValueSource(nameof(_1S_F_))] ulong a) { SetContext( v0: MakeVectorE0E1(a, TestContext.CurrentContext.Random.NextULong()), @@ -482,4 +479,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc32.cs b/Ryujinx.Tests/Cpu/CpuTestMisc32.cs index 1090a1a91..e61150132 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMisc32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMisc32.cs @@ -1,9 +1,7 @@ #define Misc32 using ARMeilleure.State; - using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -64,8 +62,8 @@ namespace Ryujinx.Tests.Cpu private static readonly bool NoNaNs = false; [Test, Pairwise] - public void Vmsr_Vcmp_Vmrs([ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b, + public void Vmsr_Vcmp_Vmrs([ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b, [Values] bool mode1, [Values] bool mode2, [Values] bool mode3) diff --git a/Ryujinx.Tests/Cpu/CpuTestMov.cs b/Ryujinx.Tests/Cpu/CpuTestMov.cs index fa51c0725..c437560a2 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMov.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMov.cs @@ -9,12 +9,11 @@ namespace Ryujinx.Tests.Cpu { #if Mov private const int RndCnt = 2; - private const int RndCntImm = 2; [Test, Pairwise, Description("MOVK <Xd>, #<imm>{, LSL #<shift>}")] public void Movk_64bit([Values(0u, 31u)] uint rd, [Random(RndCnt)] ulong xd, - [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, + [Values(0u, 65535u)] uint imm, [Values(0u, 16u, 32u, 48u)] uint shift) { uint opcode = 0xF2800000; // MOVK X0, #0, LSL #0 @@ -31,7 +30,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("MOVK <Wd>, #<imm>{, LSL #<shift>}")] public void Movk_32bit([Values(0u, 31u)] uint rd, [Random(RndCnt)] uint wd, - [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, + [Values(0u, 65535u)] uint imm, [Values(0u, 16u)] uint shift) { uint opcode = 0x72800000; // MOVK W0, #0, LSL #0 @@ -47,7 +46,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("MOVN <Xd>, #<imm>{, LSL #<shift>}")] public void Movn_64bit([Values(0u, 31u)] uint rd, - [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, + [Values(0u, 65535u)] uint imm, [Values(0u, 16u, 32u, 48u)] uint shift) { uint opcode = 0x92800000; // MOVN X0, #0, LSL #0 @@ -63,7 +62,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("MOVN <Wd>, #<imm>{, LSL #<shift>}")] public void Movn_32bit([Values(0u, 31u)] uint rd, - [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, + [Values(0u, 65535u)] uint imm, [Values(0u, 16u)] uint shift) { uint opcode = 0x12800000; // MOVN W0, #0, LSL #0 @@ -79,7 +78,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("MOVZ <Xd>, #<imm>{, LSL #<shift>}")] public void Movz_64bit([Values(0u, 31u)] uint rd, - [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, + [Values(0u, 65535u)] uint imm, [Values(0u, 16u, 32u, 48u)] uint shift) { uint opcode = 0xD2800000; // MOVZ X0, #0, LSL #0 @@ -95,7 +94,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("MOVZ <Wd>, #<imm>{, LSL #<shift>}")] public void Movz_32bit([Values(0u, 31u)] uint rd, - [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, + [Values(0u, 65535u)] uint imm, [Values(0u, 16u)] uint shift) { uint opcode = 0x52800000; // MOVZ W0, #0, LSL #0 @@ -110,4 +109,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestMul.cs b/Ryujinx.Tests/Cpu/CpuTestMul.cs index 4ad7cf110..c94bcbdb6 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMul.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMul.cs @@ -8,19 +8,17 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestMul : CpuTest { #if Mul - private const int RndCnt = 2; - [Test, Pairwise, Description("MADD <Xd>, <Xn>, <Xm>, <Xa>")] public void Madd_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xa) { uint opcode = 0x9B000000; // MADD X0, X0, X0, X0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -38,11 +36,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa) + 0x80000000u, 0xFFFFFFFFu)] uint wa) { uint opcode = 0x1B000000; // MADD W0, W0, W0, W0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -60,11 +58,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xa) { uint opcode = 0x9B008000; // MSUB X0, X0, X0, X0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -82,11 +80,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa) + 0x80000000u, 0xFFFFFFFFu)] uint wa) { uint opcode = 0x1B008000; // MSUB W0, W0, W0, W0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -104,11 +102,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xa) { uint opcode = 0x9B200000; // SMADDL X0, W0, W0, X0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -126,11 +124,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xa) { uint opcode = 0x9BA00000; // UMADDL X0, W0, W0, X0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -148,11 +146,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xa) { uint opcode = 0x9B208000; // SMSUBL X0, W0, W0, X0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -170,11 +168,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 31u)] uint rm, [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xa) { uint opcode = 0x9BA08000; // UMSUBL X0, W0, W0, X0 opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -191,9 +189,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9B407C00; // SMULH X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -210,9 +208,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 31u)] uint rn, [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm) { uint opcode = 0x9BC07C00; // UMULH X0, X0, X0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -225,4 +223,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestMul32.cs b/Ryujinx.Tests/Cpu/CpuTestMul32.cs index daa6d4607..0743e913c 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMul32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMul32.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _Smlabb_Smlabt_Smlatb_Smlatt_() { - return new uint[] + return new[] { 0xe1000080u, // SMLABB R0, R0, R0, R0 0xe10000C0u, // SMLABT R0, R0, R0, R0 @@ -23,7 +23,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Smlawb_Smlawt_() { - return new uint[] + return new[] { 0xe1200080u, // SMLAWB R0, R0, R0, R0 0xe12000C0u, // SMLAWT R0, R0, R0, R0 @@ -32,7 +32,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Smulbb_Smulbt_Smultb_Smultt_() { - return new uint[] + return new[] { 0xe1600080u, // SMULBB R0, R0, R0 0xe16000C0u, // SMULBT R0, R0, R0 @@ -43,7 +43,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Smulwb_Smulwt_() { - return new uint[] + return new[] { 0xe12000a0u, // SMULWB R0, R0, R0 0xe12000e0u, // SMULWT R0, R0, R0 @@ -51,8 +51,6 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; - [Test, Pairwise, Description("SMLA<x><y> <Rd>, <Rn>, <Rm>, <Ra>")] public void Smla___32bit([ValueSource("_Smlabb_Smlabt_Smlatb_Smlatt_")] uint opcode, [Values(0u, 0xdu)] uint rn, @@ -60,11 +58,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 0xdu)] uint ra, [Values(3u, 0xdu)] uint rd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa) + 0x80000000u, 0xFFFFFFFFu)] uint wa) { opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((ra & 15) << 12) | ((rd & 15) << 16); @@ -82,11 +80,11 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 0xdu)] uint ra, [Values(3u, 0xdu)] uint rd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + 0x80000000u, 0xFFFFFFFFu)] uint wm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa) + 0x80000000u, 0xFFFFFFFFu)] uint wa) { opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((ra & 15) << 12) | ((rd & 15) << 16); @@ -103,9 +101,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0xdu)] uint rm, [Values(2u, 0xdu)] uint rd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((rd & 15) << 16); @@ -122,9 +120,9 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0xdu)] uint rm, [Values(2u, 0xdu)] uint rd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + 0x80000000u, 0xFFFFFFFFu)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) + 0x80000000u, 0xFFFFFFFFu)] uint wm) { opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((rd & 15) << 16); @@ -136,4 +134,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 04110ec37..7c68c0fa1 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -1,9 +1,7 @@ #define Simd using ARMeilleure.State; - using NUnit.Framework; - using System; using System.Collections.Generic; @@ -47,8 +45,8 @@ namespace Ryujinx.Tests.Cpu private static byte GenLeadingZeros8(int cnt) // 0 <= cnt <= 8 { - if (cnt == 8) return (byte)0; - if (cnt == 7) return (byte)1; + if (cnt == 8) return 0; + if (cnt == 7) return 1; byte rnd = TestContext.CurrentContext.Random.NextByte(); sbyte mask = sbyte.MinValue; @@ -58,8 +56,8 @@ namespace Ryujinx.Tests.Cpu private static ushort GenLeadingZeros16(int cnt) // 0 <= cnt <= 16 { - if (cnt == 16) return (ushort)0; - if (cnt == 15) return (ushort)1; + if (cnt == 16) return 0; + if (cnt == 15) return 1; ushort rnd = TestContext.CurrentContext.Random.NextUShort(); short mask = short.MinValue; @@ -82,96 +80,96 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _1B1H1S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x000000000000007Ful, - 0x0000000000000080ul, 0x00000000000000FFul, - 0x0000000000007FFFul, 0x0000000000008000ul, - 0x000000000000FFFFul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul, - 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, - 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x000000000000007Ful, + 0x0000000000000080ul, 0x00000000000000FFul, + 0x0000000000007FFFul, 0x0000000000008000ul, + 0x000000000000FFFFul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, + 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _1H1S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul, - 0x0000000000008000ul, 0x000000000000FFFFul, - 0x000000007FFFFFFFul, 0x0000000080000000ul, - 0x00000000FFFFFFFFul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul, + 0x000000007FFFFFFFul, 0x0000000080000000ul, + 0x00000000FFFFFFFFul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _1S_() { - return new ulong[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul }; } private static ulong[] _2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H2S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H2S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static uint[] _W_() { - return new uint[] { 0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu }; } private static ulong[] _X_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<ulong> _1H_F_() @@ -347,10 +345,8 @@ namespace Ryujinx.Tests.Cpu { ulong grbg = TestContext.CurrentContext.Random.NextUInt(); - ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( - (float)((int)TestContext.CurrentContext.Random.NextUInt())); - ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( - (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd1 = (uint)BitConverter.SingleToInt32Bits((int)TestContext.CurrentContext.Random.NextUInt()); + ulong rnd2 = (uint)BitConverter.SingleToInt32Bits(TestContext.CurrentContext.Random.NextUInt()); ulong rnd3 = GenNormalS(); ulong rnd4 = GenSubnormalS(); @@ -451,9 +447,9 @@ namespace Ryujinx.Tests.Cpu for (int cnt = 1; cnt <= RndCnt; cnt++) { ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( - (float)((int)TestContext.CurrentContext.Random.NextUInt())); + (int)TestContext.CurrentContext.Random.NextUInt()); ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( - (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + TestContext.CurrentContext.Random.NextUInt()); ulong rnd3 = GenNormalS(); ulong rnd4 = GenSubnormalS(); @@ -554,9 +550,9 @@ namespace Ryujinx.Tests.Cpu for (int cnt = 1; cnt <= RndCnt; cnt++) { ulong rnd1 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((long)TestContext.CurrentContext.Random.NextULong())); + (long)TestContext.CurrentContext.Random.NextULong()); ulong rnd2 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((ulong)TestContext.CurrentContext.Random.NextULong())); + TestContext.CurrentContext.Random.NextULong()); ulong rnd3 = GenNormalD(); ulong rnd4 = GenSubnormalD(); @@ -651,7 +647,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _SU_Add_Max_Min_V_V_8BB_4HH_() { - return new uint[] + return new[] { 0x0E31B800u, // ADDV B0, V0.8B 0x0E30A800u, // SMAXV B0, V0.8B @@ -663,7 +659,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Add_Max_Min_V_V_16BB_8HH_4SS_() { - return new uint[] + return new[] { 0x4E31B800u, // ADDV B0, V0.16B 0x4E30A800u, // SMAXV B0, V0.16B @@ -675,7 +671,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Abs_Neg_Recpx_Sqrt_S_S_() { - return new uint[] + return new[] { 0x1E20C020u, // FABS S0, S1 0x1E214020u, // FNEG S0, S1 @@ -686,7 +682,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Abs_Neg_Recpx_Sqrt_S_D_() { - return new uint[] + return new[] { 0x1E60C020u, // FABS D0, D1 0x1E614020u, // FNEG D0, D1 @@ -697,7 +693,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Abs_Neg_Sqrt_V_2S_4S_() { - return new uint[] + return new[] { 0x0EA0F800u, // FABS V0.2S, V0.2S 0x2EA0F800u, // FNEG V0.2S, V0.2S @@ -707,7 +703,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Abs_Neg_Sqrt_V_2D_() { - return new uint[] + return new[] { 0x4EE0F800u, // FABS V0.2D, V0.2D 0x6EE0F800u, // FNEG V0.2D, V0.2D @@ -717,7 +713,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Add_Max_Min_Nm_P_S_2SS_() { - return new uint[] + return new[] { 0x7E30D820u, // FADDP S0, V1.2S 0x7E30C820u, // FMAXNMP S0, V1.2S @@ -727,7 +723,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Add_Max_Min_Nm_P_S_2DD_() { - return new uint[] + return new[] { 0x7E70D820u, // FADDP D0, V1.2D 0x7E70C820u, // FMAXNMP D0, V1.2D @@ -737,7 +733,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cm_EqGeGtLeLt_S_S_() { - return new uint[] + return new[] { 0x5EA0D820u, // FCMEQ S0, S1, #0.0 0x7EA0C820u, // FCMGE S0, S1, #0.0 @@ -749,7 +745,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cm_EqGeGtLeLt_S_D_() { - return new uint[] + return new[] { 0x5EE0D820u, // FCMEQ D0, D1, #0.0 0x7EE0C820u, // FCMGE D0, D1, #0.0 @@ -761,7 +757,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cm_EqGeGtLeLt_V_2S_4S_() { - return new uint[] + return new[] { 0x0EA0D800u, // FCMEQ V0.2S, V0.2S, #0.0 0x2EA0C800u, // FCMGE V0.2S, V0.2S, #0.0 @@ -773,7 +769,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cm_EqGeGtLeLt_V_2D_() { - return new uint[] + return new[] { 0x4EE0D800u, // FCMEQ V0.2D, V0.2D, #0.0 0x6EE0C800u, // FCMGE V0.2D, V0.2D, #0.0 @@ -785,7 +781,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cmp_Cmpe_S_S_() { - return new uint[] + return new[] { 0x1E202028u, // FCMP S1, #0.0 0x1E202038u // FCMPE S1, #0.0 @@ -794,7 +790,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cmp_Cmpe_S_D_() { - return new uint[] + return new[] { 0x1E602028u, // FCMP D1, #0.0 0x1E602038u // FCMPE D1, #0.0 @@ -803,7 +799,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_S_SD_() { - return new uint[] + return new[] { 0x1E22C020u // FCVT D0, S1 }; @@ -811,7 +807,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_S_DS_() { - return new uint[] + return new[] { 0x1E624020u // FCVT S0, D1 }; @@ -819,7 +815,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_S_SH_() { - return new uint[] + return new[] { 0x1E23C020u // FCVT H0, S1 }; @@ -827,7 +823,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_S_DH_() { - return new uint[] + return new[] { 0x1E63C020u // FCVT H0, D1 }; @@ -835,7 +831,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_S_HS_() { - return new uint[] + return new[] { 0x1EE24020u // FCVT S0, H1 }; @@ -843,7 +839,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_S_HD_() { - return new uint[] + return new[] { 0x1EE2C020u // FCVT D0, H1 }; @@ -851,7 +847,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_ANZ_SU_S_S_() { - return new uint[] + return new[] { 0x5E21C820u, // FCVTAS S0, S1 0x7E21C820u, // FCVTAU S0, S1 @@ -864,7 +860,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_ANZ_SU_S_D_() { - return new uint[] + return new[] { 0x5E61C820u, // FCVTAS D0, D1 0x7E61C820u, // FCVTAU D0, D1 @@ -877,7 +873,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_ANZ_SU_V_2S_4S_() { - return new uint[] + return new[] { 0x0E21C800u, // FCVTAS V0.2S, V0.2S 0x2E21C800u, // FCVTAU V0.2S, V0.2S @@ -891,7 +887,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_ANZ_SU_V_2D_() { - return new uint[] + return new[] { 0x4E61C800u, // FCVTAS V0.2D, V0.2D 0x6E61C800u, // FCVTAU V0.2D, V0.2D @@ -905,7 +901,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvtl_V_4H4S_8H4S_() { - return new uint[] + return new[] { 0x0E217800u // FCVTL V0.4S, V0.4H }; @@ -913,7 +909,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvtl_V_2S2D_4S2D_() { - return new uint[] + return new[] { 0x0E617800u // FCVTL V0.2D, V0.2S }; @@ -921,7 +917,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvtn_V_4S4H_4S8H_() { - return new uint[] + return new[] { 0x0E216800u // FCVTN V0.4H, V0.4S }; @@ -929,7 +925,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvtn_V_2D2S_2D4S_() { - return new uint[] + return new[] { 0x0E616800u // FCVTN V0.2S, V0.2D }; @@ -937,7 +933,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Max_Min_Nm_V_V_4SS_() { - return new uint[] + return new[] { 0x6E30C800u, // FMAXNMV S0, V0.4S 0x6E30F800u, // FMAXV S0, V0.4S @@ -948,7 +944,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Ftoi_SW_() { - return new uint[] + return new[] { 0x1E260000u // FMOV W0, S0 }; @@ -956,7 +952,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Ftoi_DX_() { - return new uint[] + return new[] { 0x9E660000u // FMOV X0, D0 }; @@ -964,7 +960,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Ftoi1_DX_() { - return new uint[] + return new[] { 0x9EAE0000u // FMOV X0, V0.D[1] }; @@ -972,7 +968,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Itof_WS_() { - return new uint[] + return new[] { 0x1E270000u // FMOV S0, W0 }; @@ -980,7 +976,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Itof_XD_() { - return new uint[] + return new[] { 0x9E670000u // FMOV D0, X0 }; @@ -988,7 +984,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Itof1_XD_() { - return new uint[] + return new[] { 0x9EAF0000u // FMOV V0.D[1], X0 }; @@ -996,7 +992,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_S_S_() { - return new uint[] + return new[] { 0x1E204020u // FMOV S0, S1 }; @@ -1004,7 +1000,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_S_D_() { - return new uint[] + return new[] { 0x1E604020u // FMOV D0, D1 }; @@ -1012,7 +1008,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recpe_Rsqrte_S_S_() { - return new uint[] + return new[] { 0x5EA1D820u, // FRECPE S0, S1 0x7EA1D820u // FRSQRTE S0, S1 @@ -1021,7 +1017,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recpe_Rsqrte_S_D_() { - return new uint[] + return new[] { 0x5EE1D820u, // FRECPE D0, D1 0x7EE1D820u // FRSQRTE D0, D1 @@ -1030,7 +1026,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recpe_Rsqrte_V_2S_4S_() { - return new uint[] + return new[] { 0x0EA1D800u, // FRECPE V0.2S, V0.2S 0x2EA1D800u // FRSQRTE V0.2S, V0.2S @@ -1039,7 +1035,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recpe_Rsqrte_V_2D_() { - return new uint[] + return new[] { 0x4EE1D800u, // FRECPE V0.2D, V0.2D 0x6EE1D800u // FRSQRTE V0.2D, V0.2D @@ -1048,7 +1044,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_AMNPZ_S_S_() { - return new uint[] + return new[] { 0x1E264020u, // FRINTA S0, S1 0x1E254020u, // FRINTM S0, S1 @@ -1060,7 +1056,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_AMNPZ_S_D_() { - return new uint[] + return new[] { 0x1E664020u, // FRINTA D0, D1 0x1E654020u, // FRINTM D0, D1 @@ -1072,7 +1068,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_AMNPZ_V_2S_4S_() { - return new uint[] + return new[] { 0x2E218800u, // FRINTA V0.2S, V0.2S 0x0E219800u, // FRINTM V0.2S, V0.2S @@ -1084,7 +1080,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_AMNPZ_V_2D_() { - return new uint[] + return new[] { 0x6E618800u, // FRINTA V0.2D, V0.2D 0x4E619800u, // FRINTM V0.2D, V0.2D @@ -1096,7 +1092,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_IX_S_S_() { - return new uint[] + return new[] { 0x1E27C020u, // FRINTI S0, S1 0x1E274020u // FRINTX S0, S1 @@ -1105,7 +1101,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_IX_S_D_() { - return new uint[] + return new[] { 0x1E67C020u, // FRINTI D0, D1 0x1E674020u // FRINTX D0, D1 @@ -1114,7 +1110,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_IX_V_2S_4S_() { - return new uint[] + return new[] { 0x2EA19800u, // FRINTI V0.2S, V0.2S 0x2E219800u // FRINTX V0.2S, V0.2S @@ -1123,7 +1119,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Rint_IX_V_2D_() { - return new uint[] + return new[] { 0x6EE19800u, // FRINTI V0.2D, V0.2D 0x6E619800u // FRINTX V0.2D, V0.2D @@ -1132,7 +1128,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Addl_V_V_8BH_4HS_() { - return new uint[] + return new[] { 0x0E303800u, // SADDLV H0, V0.8B 0x2E303800u // UADDLV H0, V0.8B @@ -1141,7 +1137,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Addl_V_V_16BH_8HS_4SD_() { - return new uint[] + return new[] { 0x4E303800u, // SADDLV H0, V0.16B 0x6E303800u // UADDLV H0, V0.16B @@ -1150,7 +1146,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_S_S_() { - return new uint[] + return new[] { 0x5E21D820u, // SCVTF S0, S1 0x7E21D820u // UCVTF S0, S1 @@ -1159,7 +1155,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_S_D_() { - return new uint[] + return new[] { 0x5E61D820u, // SCVTF D0, D1 0x7E61D820u // UCVTF D0, D1 @@ -1168,7 +1164,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_V_2S_4S_() { - return new uint[] + return new[] { 0x0E21D800u, // SCVTF V0.2S, V0.2S 0x2E21D800u // UCVTF V0.2S, V0.2S @@ -1177,7 +1173,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_V_2D_() { - return new uint[] + return new[] { 0x4E61D800u, // SCVTF V0.2D, V0.2D 0x6E61D800u // UCVTF V0.2D, V0.2D @@ -1186,7 +1182,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Sha1h_Sha1su1_V_() { - return new uint[] + return new[] { 0x5E280800u, // SHA1H S0, S0 0x5E281800u // SHA1SU1 V0.4S, V0.4S @@ -1195,7 +1191,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Sha256su0_V_() { - return new uint[] + return new[] { 0x5E282800u // SHA256SU0 V0.4S, V0.4S }; @@ -1211,8 +1207,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("ABS <V><d>, <V><n>")] public void Abs_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x5EE0B800; // ABS D0, D0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1228,8 +1224,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")] public void Abs_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E20B800; // ABS V0.8B, V0.8B @@ -1247,8 +1243,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")] public void Abs_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E20B800; // ABS V0.16B, V0.16B @@ -1266,8 +1262,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("ADDP <V><d>, <Vn>.<T>")] public void Addp_S_2DD([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x5EF1B800; // ADDP D0, V0.2D opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1281,11 +1277,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Add_Max_Min_V_V_8BB_4HH([ValueSource("_SU_Add_Max_Min_V_V_8BB_4HH_")] uint opcodes, + public void SU_Add_Max_Min_V_V_8BB_4HH([ValueSource(nameof(_SU_Add_Max_Min_V_V_8BB_4HH_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H_))] ulong z, + [ValueSource(nameof(_8B4H_))] ulong a, [Values(0b00u, 0b01u)] uint size) // <8BB, 4HH> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1300,11 +1296,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Add_Max_Min_V_V_16BB_8HH_4SS([ValueSource("_SU_Add_Max_Min_V_V_16BB_8HH_4SS_")] uint opcodes, + public void SU_Add_Max_Min_V_V_16BB_8HH_4SS([ValueSource(nameof(_SU_Add_Max_Min_V_V_16BB_8HH_4SS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16BB, 8HH, 4SS> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1321,8 +1317,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")] public void Cls_V_8B_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenLeadingSigns8B_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenLeadingSigns8B_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint opcode = 0x0E204800; // CLS V0.8B, V0.8B @@ -1340,8 +1336,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")] public void Cls_V_4H_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenLeadingSigns4H_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenLeadingSigns4H_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { uint opcode = 0x0E604800; // CLS V0.4H, V0.4H @@ -1359,8 +1355,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")] public void Cls_V_2S_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenLeadingSigns2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_2S_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenLeadingSigns2S_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint opcode = 0x0EA04800; // CLS V0.2S, V0.2S @@ -1378,8 +1374,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")] public void Clz_V_8B_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenLeadingZeros8B_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenLeadingZeros8B_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint opcode = 0x2E204800; // CLZ V0.8B, V0.8B @@ -1397,8 +1393,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")] public void Clz_V_4H_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenLeadingZeros4H_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenLeadingZeros4H_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { uint opcode = 0x2E604800; // CLZ V0.4H, V0.4H @@ -1416,8 +1412,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")] public void Clz_V_2S_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenLeadingZeros2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_2S_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenLeadingZeros2S_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint opcode = 0x2EA04800; // CLZ V0.2S, V0.2S @@ -1435,8 +1431,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMEQ <V><d>, <V><n>, #0")] public void Cmeq_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x5EE09800; // CMEQ D0, D0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1452,8 +1448,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, #0")] public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E209800; // CMEQ V0.8B, V0.8B, #0 @@ -1471,8 +1467,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, #0")] public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E209800; // CMEQ V0.16B, V0.16B, #0 @@ -1490,8 +1486,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMGE <V><d>, <V><n>, #0")] public void Cmge_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x7EE08800; // CMGE D0, D0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1507,8 +1503,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, #0")] public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E208800; // CMGE V0.8B, V0.8B, #0 @@ -1526,8 +1522,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, #0")] public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E208800; // CMGE V0.16B, V0.16B, #0 @@ -1545,8 +1541,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMGT <V><d>, <V><n>, #0")] public void Cmgt_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x5EE08800; // CMGT D0, D0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1562,8 +1558,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, #0")] public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E208800; // CMGT V0.8B, V0.8B, #0 @@ -1581,8 +1577,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, #0")] public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E208800; // CMGT V0.16B, V0.16B, #0 @@ -1600,8 +1596,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMLE <V><d>, <V><n>, #0")] public void Cmle_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x7EE09800; // CMLE D0, D0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1617,8 +1613,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMLE <Vd>.<T>, <Vn>.<T>, #0")] public void Cmle_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E209800; // CMLE V0.8B, V0.8B, #0 @@ -1636,8 +1632,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMLE <Vd>.<T>, <Vn>.<T>, #0")] public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E209800; // CMLE V0.16B, V0.16B, #0 @@ -1655,8 +1651,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMLT <V><d>, <V><n>, #0")] public void Cmlt_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x5EE0A800; // CMLT D0, D0, #0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1672,8 +1668,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMLT <Vd>.<T>, <Vn>.<T>, #0")] public void Cmlt_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E20A800; // CMLT V0.8B, V0.8B, #0 @@ -1691,8 +1687,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CMLT <Vd>.<T>, <Vn>.<T>, #0")] public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E20A800; // CMLT V0.16B, V0.16B, #0 @@ -1710,8 +1706,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CNT <Vd>.<T>, <Vn>.<T>")] public void Cnt_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenPopCnt8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenPopCnt8B_))] ulong a) { uint opcode = 0x0E205800; // CNT V0.8B, V0.8B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1727,8 +1723,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CNT <Vd>.<T>, <Vn>.<T>")] public void Cnt_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_GenPopCnt8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_GenPopCnt8B_))] ulong a) { uint opcode = 0x4E205800; // CNT V0.16B, V0.16B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1742,8 +1738,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abs_Neg_Recpx_Sqrt_S_S([ValueSource("_F_Abs_Neg_Recpx_Sqrt_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a) + public void F_Abs_Neg_Recpx_Sqrt_S_S([ValueSource(nameof(_F_Abs_Neg_Recpx_Sqrt_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1760,8 +1756,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abs_Neg_Recpx_Sqrt_S_D([ValueSource("_F_Abs_Neg_Recpx_Sqrt_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a) + public void F_Abs_Neg_Recpx_Sqrt_S_D([ValueSource(nameof(_F_Abs_Neg_Recpx_Sqrt_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1778,11 +1774,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abs_Neg_Sqrt_V_2S_4S([ValueSource("_F_Abs_Neg_Sqrt_V_2S_4S_")] uint opcodes, + public void F_Abs_Neg_Sqrt_V_2S_4S([ValueSource(nameof(_F_Abs_Neg_Sqrt_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1802,11 +1798,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abs_Neg_Sqrt_V_2D([ValueSource("_F_Abs_Neg_Sqrt_V_2D_")] uint opcodes, + public void F_Abs_Neg_Sqrt_V_2D([ValueSource(nameof(_F_Abs_Neg_Sqrt_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1824,8 +1820,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Add_Max_Min_Nm_P_S_2SS([ValueSource("_F_Add_Max_Min_Nm_P_S_2SS_")] uint opcodes, - [ValueSource("_2S_F_")] ulong a) + public void F_Add_Max_Min_Nm_P_S_2SS([ValueSource(nameof(_F_Add_Max_Min_Nm_P_S_2SS_))] uint opcodes, + [ValueSource(nameof(_2S_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -1843,9 +1839,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Add_Max_Min_Nm_P_S_2DD([ValueSource("_F_Add_Max_Min_Nm_P_S_2DD_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a0, - [ValueSource("_1D_F_")] ulong a1) + public void F_Add_Max_Min_Nm_P_S_2DD([ValueSource(nameof(_F_Add_Max_Min_Nm_P_S_2DD_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a0, + [ValueSource(nameof(_1D_F_))] ulong a1) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -1863,8 +1859,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cm_EqGeGtLeLt_S_S([ValueSource("_F_Cm_EqGeGtLeLt_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a) + public void F_Cm_EqGeGtLeLt_S_S([ValueSource(nameof(_F_Cm_EqGeGtLeLt_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1880,8 +1876,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cm_EqGeGtLeLt_S_D([ValueSource("_F_Cm_EqGeGtLeLt_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a) + public void F_Cm_EqGeGtLeLt_S_D([ValueSource(nameof(_F_Cm_EqGeGtLeLt_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1897,11 +1893,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cm_EqGeGtLeLt_V_2S_4S([ValueSource("_F_Cm_EqGeGtLeLt_V_2S_4S_")] uint opcodes, + public void F_Cm_EqGeGtLeLt_V_2S_4S([ValueSource(nameof(_F_Cm_EqGeGtLeLt_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1920,11 +1916,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cm_EqGeGtLeLt_V_2D([ValueSource("_F_Cm_EqGeGtLeLt_V_2D_")] uint opcodes, + public void F_Cm_EqGeGtLeLt_V_2D([ValueSource(nameof(_F_Cm_EqGeGtLeLt_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1941,8 +1937,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cmp_Cmpe_S_S([ValueSource("_F_Cmp_Cmpe_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a) + public void F_Cmp_Cmpe_S_S([ValueSource(nameof(_F_Cmp_Cmpe_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a) { V128 v1 = MakeVectorE0(a); @@ -1957,8 +1953,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cmp_Cmpe_S_D([ValueSource("_F_Cmp_Cmpe_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a) + public void F_Cmp_Cmpe_S_D([ValueSource(nameof(_F_Cmp_Cmpe_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a) { V128 v1 = MakeVectorE0(a); @@ -1973,8 +1969,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_S_SD([ValueSource("_F_Cvt_S_SD_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a) + public void F_Cvt_S_SD([ValueSource(nameof(_F_Cvt_S_SD_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1986,8 +1982,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_S_DS([ValueSource("_F_Cvt_S_DS_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a) + public void F_Cvt_S_DS([ValueSource(nameof(_F_Cvt_S_DS_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1999,8 +1995,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Unicorn seems to default all rounding modes to RMode.Rn. - public void F_Cvt_S_SH([ValueSource("_F_Cvt_S_SH_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, + public void F_Cvt_S_SH([ValueSource(nameof(_F_Cvt_S_SH_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -2015,8 +2011,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_S_DH([ValueSource("_F_Cvt_S_DH_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, + public void F_Cvt_S_DH([ValueSource(nameof(_F_Cvt_S_DH_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -2031,8 +2027,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_S_HS([ValueSource("_F_Cvt_S_HS_")] uint opcodes, - [ValueSource("_1H_F_")] ulong a) + public void F_Cvt_S_HS([ValueSource(nameof(_F_Cvt_S_HS_))] uint opcodes, + [ValueSource(nameof(_1H_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -2044,8 +2040,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_S_HD([ValueSource("_F_Cvt_S_HD_")] uint opcodes, - [ValueSource("_1H_F_")] ulong a) + public void F_Cvt_S_HD([ValueSource(nameof(_F_Cvt_S_HD_))] uint opcodes, + [ValueSource(nameof(_1H_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -2057,8 +2053,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_ANZ_SU_S_S([ValueSource("_F_Cvt_ANZ_SU_S_S_")] uint opcodes, - [ValueSource("_1S_F_W_")] ulong a) + public void F_Cvt_ANZ_SU_S_S([ValueSource(nameof(_F_Cvt_ANZ_SU_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_W_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -2070,8 +2066,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_ANZ_SU_S_D([ValueSource("_F_Cvt_ANZ_SU_S_D_")] uint opcodes, - [ValueSource("_1D_F_X_")] ulong a) + public void F_Cvt_ANZ_SU_S_D([ValueSource(nameof(_F_Cvt_ANZ_SU_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_X_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -2083,11 +2079,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_ANZ_SU_V_2S_4S([ValueSource("_F_Cvt_ANZ_SU_V_2S_4S_")] uint opcodes, + public void F_Cvt_ANZ_SU_V_2S_4S([ValueSource(nameof(_F_Cvt_ANZ_SU_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_W_")] ulong z, - [ValueSource("_2S_F_W_")] ulong a, + [ValueSource(nameof(_2S_F_W_))] ulong z, + [ValueSource(nameof(_2S_F_W_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2102,11 +2098,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_ANZ_SU_V_2D([ValueSource("_F_Cvt_ANZ_SU_V_2D_")] uint opcodes, + public void F_Cvt_ANZ_SU_V_2D([ValueSource(nameof(_F_Cvt_ANZ_SU_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_X_")] ulong z, - [ValueSource("_1D_F_X_")] ulong a) + [ValueSource(nameof(_1D_F_X_))] ulong z, + [ValueSource(nameof(_1D_F_X_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2119,11 +2115,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvtl_V_4H4S_8H4S([ValueSource("_F_Cvtl_V_4H4S_8H4S_")] uint opcodes, + public void F_Cvtl_V_4H4S_8H4S([ValueSource(nameof(_F_Cvtl_V_4H4S_8H4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_F_")] ulong z, - [ValueSource("_4H_F_")] ulong a, + [ValueSource(nameof(_4H_F_))] ulong z, + [ValueSource(nameof(_4H_F_))] ulong a, [Values(0b0u, 0b1u)] uint q, // <4H4S, 8H4S> [Values(RMode.Rn)] RMode rMode) { @@ -2146,11 +2142,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvtl_V_2S2D_4S2D([ValueSource("_F_Cvtl_V_2S2D_4S2D_")] uint opcodes, + public void F_Cvtl_V_2S2D_4S2D([ValueSource(nameof(_F_Cvtl_V_2S2D_4S2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S2D, 4S2D> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2165,11 +2161,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Unicorn seems to default all rounding modes to RMode.Rn. - public void F_Cvtn_V_4S4H_4S8H([ValueSource("_F_Cvtn_V_4S4H_4S8H_")] uint opcodes, + public void F_Cvtn_V_4S4H_4S8H([ValueSource(nameof(_F_Cvtn_V_4S4H_4S8H_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, [Values(0b0u, 0b1u)] uint q, // <4S4H, 4S8H> [Values(RMode.Rn)] RMode rMode) { @@ -2192,11 +2188,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvtn_V_2D2S_2D4S([ValueSource("_F_Cvtn_V_2D2S_2D4S_")] uint opcodes, + public void F_Cvtn_V_2D2S_2D4S([ValueSource(nameof(_F_Cvtn_V_2D2S_2D4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2D2S, 2D4S> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2211,11 +2207,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Max_Min_Nm_V_V_4SS([ValueSource("_F_Max_Min_Nm_V_V_4SS_")] uint opcodes, + public void F_Max_Min_Nm_V_V_4SS([ValueSource(nameof(_F_Max_Min_Nm_V_V_4SS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a) + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2233,10 +2229,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Ftoi_SW([ValueSource("_F_Mov_Ftoi_SW_")] uint opcodes, + public void F_Mov_Ftoi_SW([ValueSource(nameof(_F_Mov_Ftoi_SW_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1S_F_")] ulong a) + [ValueSource(nameof(_1S_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2250,10 +2246,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Ftoi_DX([ValueSource("_F_Mov_Ftoi_DX_")] uint opcodes, + public void F_Mov_Ftoi_DX([ValueSource(nameof(_F_Mov_Ftoi_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1D_F_")] ulong a) + [ValueSource(nameof(_1D_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2266,10 +2262,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Ftoi1_DX([ValueSource("_F_Mov_Ftoi1_DX_")] uint opcodes, + public void F_Mov_Ftoi1_DX([ValueSource(nameof(_F_Mov_Ftoi1_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1D_F_")] ulong a) + [ValueSource(nameof(_1D_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2282,10 +2278,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Itof_WS([ValueSource("_F_Mov_Itof_WS_")] uint opcodes, + public void F_Mov_Itof_WS([ValueSource(nameof(_F_Mov_Itof_WS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_W_")] [Random(RndCnt)] uint wn) + [ValueSource(nameof(_W_))] uint wn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2299,10 +2295,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Itof_XD([ValueSource("_F_Mov_Itof_XD_")] uint opcodes, + public void F_Mov_Itof_XD([ValueSource(nameof(_F_Mov_Itof_XD_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2316,10 +2312,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Itof1_XD([ValueSource("_F_Mov_Itof1_XD_")] uint opcodes, + public void F_Mov_Itof1_XD([ValueSource(nameof(_F_Mov_Itof1_XD_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2333,8 +2329,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_S_S([ValueSource("_F_Mov_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a) + public void F_Mov_S_S([ValueSource(nameof(_F_Mov_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -2346,8 +2342,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_S_D([ValueSource("_F_Mov_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a) + public void F_Mov_S_D([ValueSource(nameof(_F_Mov_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -2359,8 +2355,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Recpe_Rsqrte_S_S([ValueSource("_F_Recpe_Rsqrte_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, + public void F_Recpe_Rsqrte_S_S([ValueSource(nameof(_F_Recpe_Rsqrte_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -2379,8 +2375,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Recpe_Rsqrte_S_D([ValueSource("_F_Recpe_Rsqrte_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, + public void F_Recpe_Rsqrte_S_D([ValueSource(nameof(_F_Recpe_Rsqrte_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -2399,11 +2395,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Recpe_Rsqrte_V_2S_4S([ValueSource("_F_Recpe_Rsqrte_V_2S_4S_")] uint opcodes, + public void F_Recpe_Rsqrte_V_2S_4S([ValueSource(nameof(_F_Recpe_Rsqrte_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, [Values(0b0u, 0b1u)] uint q, // <2S, 4S> [Values(RMode.Rn)] RMode rMode) { @@ -2425,11 +2421,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Recpe_Rsqrte_V_2D([ValueSource("_F_Recpe_Rsqrte_V_2D_")] uint opcodes, + public void F_Recpe_Rsqrte_V_2D([ValueSource(nameof(_F_Recpe_Rsqrte_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2449,8 +2445,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_AMNPZ_S_S([ValueSource("_F_Rint_AMNPZ_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a) + public void F_Rint_AMNPZ_S_S([ValueSource(nameof(_F_Rint_AMNPZ_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -2462,8 +2458,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_AMNPZ_S_D([ValueSource("_F_Rint_AMNPZ_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a) + public void F_Rint_AMNPZ_S_D([ValueSource(nameof(_F_Rint_AMNPZ_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -2475,11 +2471,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_AMNPZ_V_2S_4S([ValueSource("_F_Rint_AMNPZ_V_2S_4S_")] uint opcodes, + public void F_Rint_AMNPZ_V_2S_4S([ValueSource(nameof(_F_Rint_AMNPZ_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2494,11 +2490,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_AMNPZ_V_2D([ValueSource("_F_Rint_AMNPZ_V_2D_")] uint opcodes, + public void F_Rint_AMNPZ_V_2D([ValueSource(nameof(_F_Rint_AMNPZ_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2511,8 +2507,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_IX_S_S([ValueSource("_F_Rint_IX_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, + public void F_Rint_IX_S_S([ValueSource(nameof(_F_Rint_IX_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, [Values] RMode rMode) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -2527,8 +2523,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_IX_S_D([ValueSource("_F_Rint_IX_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, + public void F_Rint_IX_S_D([ValueSource(nameof(_F_Rint_IX_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, [Values] RMode rMode) { ulong z = TestContext.CurrentContext.Random.NextULong(); @@ -2543,11 +2539,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_IX_V_2S_4S([ValueSource("_F_Rint_IX_V_2S_4S_")] uint opcodes, + public void F_Rint_IX_V_2S_4S([ValueSource(nameof(_F_Rint_IX_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, [Values(0b0u, 0b1u)] uint q, // <2S, 4S> [Values] RMode rMode) { @@ -2565,11 +2561,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Rint_IX_V_2D([ValueSource("_F_Rint_IX_V_2D_")] uint opcodes, + public void F_Rint_IX_V_2D([ValueSource(nameof(_F_Rint_IX_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, [Values] RMode rMode) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2587,8 +2583,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("NEG <V><d>, <V><n>")] public void Neg_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { uint opcode = 0x7EE0B800; // NEG D0, D0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2604,8 +2600,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")] public void Neg_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E20B800; // NEG V0.8B, V0.8B @@ -2623,8 +2619,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")] public void Neg_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E20B800; // NEG V0.16B, V0.16B @@ -2642,8 +2638,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("NOT <Vd>.<T>, <Vn>.<T>")] public void Not_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a) { uint opcode = 0x2E205800; // NOT V0.8B, V0.8B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2659,8 +2655,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("NOT <Vd>.<T>, <Vn>.<T>")] public void Not_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a) { uint opcode = 0x6E205800; // NOT V0.16B, V0.16B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2676,8 +2672,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("RBIT <Vd>.<T>, <Vn>.<T>")] public void Rbit_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a) { uint opcode = 0x2E605800; // RBIT V0.8B, V0.8B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2693,8 +2689,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("RBIT <Vd>.<T>, <Vn>.<T>")] public void Rbit_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a) { uint opcode = 0x6E605800; // RBIT V0.16B, V0.16B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2710,8 +2706,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("REV16 <Vd>.<T>, <Vn>.<T>")] public void Rev16_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a) { uint opcode = 0x0E201800; // REV16 V0.8B, V0.8B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2727,8 +2723,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("REV16 <Vd>.<T>, <Vn>.<T>")] public void Rev16_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a) { uint opcode = 0x4E201800; // REV16 V0.16B, V0.16B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2744,8 +2740,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("REV32 <Vd>.<T>, <Vn>.<T>")] public void Rev32_V_8B_4H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H_))] ulong z, + [ValueSource(nameof(_8B4H_))] ulong a, [Values(0b00u, 0b01u)] uint size) // <8B, 4H> { uint opcode = 0x2E200800; // REV32 V0.8B, V0.8B @@ -2763,8 +2759,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("REV32 <Vd>.<T>, <Vn>.<T>")] public void Rev32_V_16B_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H_))] ulong z, + [ValueSource(nameof(_8B4H_))] ulong a, [Values(0b00u, 0b01u)] uint size) // <16B, 8H> { uint opcode = 0x6E200800; // REV32 V0.16B, V0.16B @@ -2782,8 +2778,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("REV64 <Vd>.<T>, <Vn>.<T>")] public void Rev64_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E200800; // REV64 V0.8B, V0.8B @@ -2801,8 +2797,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("REV64 <Vd>.<T>, <Vn>.<T>")] public void Rev64_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x4E200800; // REV64 V0.16B, V0.16B @@ -2820,8 +2816,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SADALP <Vd>.<Ta>, <Vn>.<Tb>")] public void Sadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { uint opcode = 0x0E206800; // SADALP V0.4H, V0.8B @@ -2839,8 +2835,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SADALP <Vd>.<Ta>, <Vn>.<Tb>")] public void Sadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x4E206800; // SADALP V0.8H, V0.16B @@ -2858,8 +2854,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SADDLP <Vd>.<Ta>, <Vn>.<Tb>")] public void Saddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { uint opcode = 0x0E202800; // SADDLP V0.4H, V0.8B @@ -2877,8 +2873,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SADDLP <Vd>.<Ta>, <Vn>.<Tb>")] public void Saddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x4E202800; // SADDLP V0.8H, V0.16B @@ -2894,11 +2890,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Addl_V_V_8BH_4HS([ValueSource("_SU_Addl_V_V_8BH_4HS_")] uint opcodes, + public void SU_Addl_V_V_8BH_4HS([ValueSource(nameof(_SU_Addl_V_V_8BH_4HS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H_))] ulong z, + [ValueSource(nameof(_8B4H_))] ulong a, [Values(0b00u, 0b01u)] uint size) // <8BH, 4HS> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2913,11 +2909,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Addl_V_V_16BH_8HS_4SD([ValueSource("_SU_Addl_V_V_16BH_8HS_4SD_")] uint opcodes, + public void SU_Addl_V_V_16BH_8HS_4SD([ValueSource(nameof(_SU_Addl_V_V_16BH_8HS_4SD_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16BH, 8HS, 4SD> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2932,8 +2928,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_S_S([ValueSource("_SU_Cvt_F_S_S_")] uint opcodes, - [ValueSource("_1S_")] [Random(RndCnt)] ulong a) + public void SU_Cvt_F_S_S([ValueSource(nameof(_SU_Cvt_F_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -2945,8 +2941,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_S_D([ValueSource("_SU_Cvt_F_S_D_")] uint opcodes, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + public void SU_Cvt_F_S_D([ValueSource(nameof(_SU_Cvt_F_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_))] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -2958,11 +2954,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_V_2S_4S([ValueSource("_SU_Cvt_F_V_2S_4S_")] uint opcodes, + public void SU_Cvt_F_V_2S_4S([ValueSource(nameof(_SU_Cvt_F_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2977,11 +2973,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_V_2D([ValueSource("_SU_Cvt_F_V_2D_")] uint opcodes, + public void SU_Cvt_F_V_2D([ValueSource(nameof(_SU_Cvt_F_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2994,7 +2990,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Sha1h_Sha1su1_V([ValueSource("_Sha1h_Sha1su1_V_")] uint opcodes, + public void Sha1h_Sha1su1_V([ValueSource(nameof(_Sha1h_Sha1su1_V_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, @@ -3011,7 +3007,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Sha256su0_V([ValueSource("_Sha256su0_V_")] uint opcodes, + public void Sha256su0_V([ValueSource(nameof(_Sha256su0_V_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, @@ -3030,8 +3026,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SHLL{2} <Vd>.<Ta>, <Vn>.<Tb>, #<shift>")] public void Shll_V([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size, // <shift: 8, 16, 32> [Values(0b0u, 0b1u)] uint q) { @@ -3051,8 +3047,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQABS <V><d>, <V><n>")] public void Sqabs_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> { uint opcode = 0x5E207800; // SQABS B0, B0 @@ -3070,8 +3066,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQABS <Vd>.<T>, <Vn>.<T>")] public void Sqabs_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E207800; // SQABS V0.8B, V0.8B @@ -3089,8 +3085,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQABS <Vd>.<T>, <Vn>.<T>")] public void Sqabs_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E207800; // SQABS V0.16B, V0.16B @@ -3108,8 +3104,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQNEG <V><d>, <V><n>")] public void Sqneg_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> { uint opcode = 0x7E207800; // SQNEG B0, B0 @@ -3127,8 +3123,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQNEG <Vd>.<T>, <Vn>.<T>")] public void Sqneg_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E207800; // SQNEG V0.8B, V0.8B @@ -3146,8 +3142,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQNEG <Vd>.<T>, <Vn>.<T>")] public void Sqneg_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E207800; // SQNEG V0.16B, V0.16B @@ -3165,8 +3161,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQXTN <Vb><d>, <Va><n>")] public void Sqxtn_S_HB_SH_DS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1H1S1D_))] ulong z, + [ValueSource(nameof(_1H1S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <HB, SH, DS> { uint opcode = 0x5E214800; // SQXTN B0, H0 @@ -3184,8 +3180,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x0E214800; // SQXTN V0.8B, V0.8H @@ -3203,8 +3199,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x4E214800; // SQXTN2 V0.16B, V0.8H @@ -3222,8 +3218,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQXTUN <Vb><d>, <Va><n>")] public void Sqxtun_S_HB_SH_DS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1H1S1D_))] ulong z, + [ValueSource(nameof(_1H1S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <HB, SH, DS> { uint opcode = 0x7E212800; // SQXTUN B0, H0 @@ -3241,8 +3237,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQXTUN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x2E212800; // SQXTUN V0.8B, V0.8H @@ -3260,8 +3256,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SQXTUN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x6E212800; // SQXTUN2 V0.16B, V0.8H @@ -3279,8 +3275,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SUQADD <V><d>, <V><n>")] public void Suqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> { uint opcode = 0x5E203800; // SUQADD B0, B0 @@ -3298,8 +3294,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SUQADD <Vd>.<T>, <Vn>.<T>")] public void Suqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E203800; // SUQADD V0.8B, V0.8B @@ -3317,8 +3313,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SUQADD <Vd>.<T>, <Vn>.<T>")] public void Suqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E203800; // SUQADD V0.16B, V0.16B @@ -3336,8 +3332,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UADALP <Vd>.<Ta>, <Vn>.<Tb>")] public void Uadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { uint opcode = 0x2E206800; // UADALP V0.4H, V0.8B @@ -3355,8 +3351,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UADALP <Vd>.<Ta>, <Vn>.<Tb>")] public void Uadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x6E206800; // UADALP V0.8H, V0.16B @@ -3374,8 +3370,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UADDLP <Vd>.<Ta>, <Vn>.<Tb>")] public void Uaddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { uint opcode = 0x2E202800; // UADDLP V0.4H, V0.8B @@ -3393,8 +3389,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UADDLP <Vd>.<Ta>, <Vn>.<Tb>")] public void Uaddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x6E202800; // UADDLP V0.8H, V0.16B @@ -3412,8 +3408,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UQXTN <Vb><d>, <Va><n>")] public void Uqxtn_S_HB_SH_DS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1H1S1D_))] ulong z, + [ValueSource(nameof(_1H1S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <HB, SH, DS> { uint opcode = 0x7E214800; // UQXTN B0, H0 @@ -3431,8 +3427,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x2E214800; // UQXTN V0.8B, V0.8H @@ -3450,8 +3446,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x6E214800; // UQXTN2 V0.16B, V0.8H @@ -3469,8 +3465,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("USQADD <V><d>, <V><n>")] public void Usqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> { uint opcode = 0x7E203800; // USQADD B0, B0 @@ -3488,8 +3484,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("USQADD <Vd>.<T>, <Vn>.<T>")] public void Usqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E203800; // USQADD V0.8B, V0.8B @@ -3507,8 +3503,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("USQADD <Vd>.<T>, <Vn>.<T>")] public void Usqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E203800; // USQADD V0.16B, V0.16B @@ -3526,8 +3522,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("XTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x0E212800; // XTN V0.8B, V0.8H @@ -3545,8 +3541,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("XTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x4E212800; // XTN2 V0.16B, V0.8H @@ -3562,4 +3558,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd32.cs b/Ryujinx.Tests/Cpu/CpuTestSimd32.cs index 34173cd7d..42bc2ac55 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd32.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _Vabs_Vneg_Vpaddl_I_() { - return new uint[] + return new[] { 0xf3b10300u, // VABS.S8 D0, D0 0xf3b10380u, // VNEG.S8 D0, D0 @@ -24,7 +24,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vabs_Vneg_F_() { - return new uint[] + return new[] { 0xf3b90700u, // VABS.F32 D0, D0 0xf3b90780u // VNEG.F32 D0, D0 @@ -35,10 +35,10 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _8B4H2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<ulong> _1S_F_() @@ -211,11 +211,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vabs_Vneg_Vpaddl_V_I([ValueSource("_Vabs_Vneg_Vpaddl_I_")] uint opcode, + public void Vabs_Vneg_Vpaddl_V_I([ValueSource(nameof(_Vabs_Vneg_Vpaddl_I_))] uint opcode, [Range(0u, 3u)] uint rd, [Range(0u, 3u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0u, 1u, 2u)] uint size, // <S8, S16, S32> [Values] bool q) { @@ -241,11 +241,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vabs_Vneg_V_F32([ValueSource("_Vabs_Vneg_F_")] uint opcode, + public void Vabs_Vneg_V_F32([ValueSource(nameof(_Vabs_Vneg_F_))] uint opcode, [Range(0u, 3u)] uint rd, [Range(0u, 3u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong b, [Values] bool q) { if (q) @@ -270,7 +270,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VCNT.8 D0, D0 | VCNT.8 Q0, Q0")] public void Vcnt([Values(0u, 1u)] uint rd, [Values(0u, 1u)] uint rm, - [ValueSource(nameof(_GenPopCnt8B_))] [Random(RndCnt)] ulong d0, + [ValueSource(nameof(_GenPopCnt8B_))] ulong d0, [Values] bool q) { ulong d1 = ~d0; // It's expensive to have a second generator. @@ -298,8 +298,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Vmovn_V([Range(0u, 3u)] uint rd, [Range(0u, 3u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0u, 1u, 2u, 3u)] uint op, [Values(0u, 1u, 2u)] uint size) // <S8, S16, S32> { @@ -322,4 +322,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs index c73fd0235..dbb9410cd 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs @@ -1,9 +1,7 @@ #define SimdCvt using ARMeilleure.State; - using NUnit.Framework; - using System; using System.Collections.Generic; @@ -17,14 +15,14 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static uint[] _W_() { - return new uint[] { 0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu }; } private static ulong[] _X_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<ulong> _1S_F_WX_() @@ -89,13 +87,13 @@ namespace Ryujinx.Tests.Cpu ulong grbg = TestContext.CurrentContext.Random.NextUInt(); ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( - (float)((int)TestContext.CurrentContext.Random.NextUInt())); + (int)TestContext.CurrentContext.Random.NextUInt()); ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( - (float)((long)TestContext.CurrentContext.Random.NextULong())); + (long)TestContext.CurrentContext.Random.NextULong()); ulong rnd3 = (uint)BitConverter.SingleToInt32Bits( - (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + TestContext.CurrentContext.Random.NextUInt()); ulong rnd4 = (uint)BitConverter.SingleToInt32Bits( - (float)((ulong)TestContext.CurrentContext.Random.NextULong())); + TestContext.CurrentContext.Random.NextULong()); ulong rnd5 = GenNormalS(); ulong rnd6 = GenSubnormalS(); @@ -170,13 +168,13 @@ namespace Ryujinx.Tests.Cpu for (int cnt = 1; cnt <= RndCnt; cnt++) { ulong rnd1 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((int)TestContext.CurrentContext.Random.NextUInt())); + (int)TestContext.CurrentContext.Random.NextUInt()); ulong rnd2 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((long)TestContext.CurrentContext.Random.NextULong())); + (long)TestContext.CurrentContext.Random.NextULong()); ulong rnd3 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((uint)TestContext.CurrentContext.Random.NextUInt())); + TestContext.CurrentContext.Random.NextUInt()); ulong rnd4 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((ulong)TestContext.CurrentContext.Random.NextULong())); + TestContext.CurrentContext.Random.NextULong()); ulong rnd5 = GenNormalD(); ulong rnd6 = GenSubnormalD(); @@ -195,7 +193,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _F_Cvt_AMPZ_SU_Gp_SW_() { - return new uint[] + return new[] { 0x1E240000u, // FCVTAS W0, S0 0x1E250000u, // FCVTAU W0, S0 @@ -211,7 +209,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_AMPZ_SU_Gp_SX_() { - return new uint[] + return new[] { 0x9E240000u, // FCVTAS X0, S0 0x9E250000u, // FCVTAU X0, S0 @@ -227,7 +225,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_AMPZ_SU_Gp_DW_() { - return new uint[] + return new[] { 0x1E640000u, // FCVTAS W0, D0 0x1E650000u, // FCVTAU W0, D0 @@ -243,7 +241,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_AMPZ_SU_Gp_DX_() { - return new uint[] + return new[] { 0x9E640000u, // FCVTAS X0, D0 0x9E650000u, // FCVTAU X0, D0 @@ -259,7 +257,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_Z_SU_Gp_Fixed_SW_() { - return new uint[] + return new[] { 0x1E188000u, // FCVTZS W0, S0, #32 0x1E198000u // FCVTZU W0, S0, #32 @@ -268,7 +266,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_Z_SU_Gp_Fixed_SX_() { - return new uint[] + return new[] { 0x9E180000u, // FCVTZS X0, S0, #64 0x9E190000u // FCVTZU X0, S0, #64 @@ -277,7 +275,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_Z_SU_Gp_Fixed_DW_() { - return new uint[] + return new[] { 0x1E588000u, // FCVTZS W0, D0, #32 0x1E598000u // FCVTZU W0, D0, #32 @@ -286,7 +284,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_Z_SU_Gp_Fixed_DX_() { - return new uint[] + return new[] { 0x9E580000u, // FCVTZS X0, D0, #64 0x9E590000u // FCVTZU X0, D0, #64 @@ -295,7 +293,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_WS_() { - return new uint[] + return new[] { 0x1E220000u, // SCVTF S0, W0 0x1E230000u // UCVTF S0, W0 @@ -304,7 +302,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_WD_() { - return new uint[] + return new[] { 0x1E620000u, // SCVTF D0, W0 0x1E630000u // UCVTF D0, W0 @@ -313,7 +311,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_XS_() { - return new uint[] + return new[] { 0x9E220000u, // SCVTF S0, X0 0x9E230000u // UCVTF S0, X0 @@ -322,7 +320,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_XD_() { - return new uint[] + return new[] { 0x9E620000u, // SCVTF D0, X0 0x9E630000u // UCVTF D0, X0 @@ -331,7 +329,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_Fixed_WS_() { - return new uint[] + return new[] { 0x1E028000u, // SCVTF S0, W0, #32 0x1E038000u // UCVTF S0, W0, #32 @@ -340,7 +338,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_Fixed_WD_() { - return new uint[] + return new[] { 0x1E428000u, // SCVTF D0, W0, #32 0x1E438000u // UCVTF D0, W0, #32 @@ -349,7 +347,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_Fixed_XS_() { - return new uint[] + return new[] { 0x9E020000u, // SCVTF S0, X0, #64 0x9E030000u // UCVTF S0, X0, #64 @@ -358,7 +356,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_Gp_Fixed_XD_() { - return new uint[] + return new[] { 0x9E420000u, // SCVTF D0, X0, #64 0x9E430000u // UCVTF D0, X0, #64 @@ -367,17 +365,16 @@ namespace Ryujinx.Tests.Cpu #endregion private const int RndCnt = 2; - private const int RndCntFBits = 2; private static readonly bool NoZeros = false; private static readonly bool NoInfs = false; private static readonly bool NoNaNs = false; [Test, Pairwise] [Explicit] - public void F_Cvt_AMPZ_SU_Gp_SW([ValueSource("_F_Cvt_AMPZ_SU_Gp_SW_")] uint opcodes, + public void F_Cvt_AMPZ_SU_Gp_SW([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_SW_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1S_F_WX_")] ulong a) + [ValueSource(nameof(_1S_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -391,10 +388,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_AMPZ_SU_Gp_SX([ValueSource("_F_Cvt_AMPZ_SU_Gp_SX_")] uint opcodes, + public void F_Cvt_AMPZ_SU_Gp_SX([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_SX_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1S_F_WX_")] ulong a) + [ValueSource(nameof(_1S_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -407,10 +404,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_AMPZ_SU_Gp_DW([ValueSource("_F_Cvt_AMPZ_SU_Gp_DW_")] uint opcodes, + public void F_Cvt_AMPZ_SU_Gp_DW([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_DW_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1D_F_WX_")] ulong a) + [ValueSource(nameof(_1D_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -424,10 +421,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_AMPZ_SU_Gp_DX([ValueSource("_F_Cvt_AMPZ_SU_Gp_DX_")] uint opcodes, + public void F_Cvt_AMPZ_SU_Gp_DX([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1D_F_WX_")] ulong a) + [ValueSource(nameof(_1D_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -440,11 +437,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_Z_SU_Gp_Fixed_SW([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_SW_")] uint opcodes, + public void F_Cvt_Z_SU_Gp_Fixed_SW([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_SW_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1S_F_WX_")] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_1S_F_WX_))] ulong a, + [Values(1u, 32u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -461,11 +458,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_Z_SU_Gp_Fixed_SX([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_SX_")] uint opcodes, + public void F_Cvt_Z_SU_Gp_Fixed_SX([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_SX_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1S_F_WX_")] ulong a, - [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_1S_F_WX_))] ulong a, + [Values(1u, 64u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -481,11 +478,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_Z_SU_Gp_Fixed_DW([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_DW_")] uint opcodes, + public void F_Cvt_Z_SU_Gp_Fixed_DW([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_DW_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1D_F_WX_")] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_1D_F_WX_))] ulong a, + [Values(1u, 32u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -502,11 +499,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_Z_SU_Gp_Fixed_DX([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_DX_")] uint opcodes, + public void F_Cvt_Z_SU_Gp_Fixed_DX([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1D_F_WX_")] ulong a, - [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_1D_F_WX_))] ulong a, + [Values(1u, 64u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -522,10 +519,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_WS([ValueSource("_SU_Cvt_F_Gp_WS_")] uint opcodes, + public void SU_Cvt_F_Gp_WS([ValueSource(nameof(_SU_Cvt_F_Gp_WS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_W_")] [Random(RndCnt)] uint wn) + [ValueSource(nameof(_W_))] uint wn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -539,10 +536,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_WD([ValueSource("_SU_Cvt_F_Gp_WD_")] uint opcodes, + public void SU_Cvt_F_Gp_WD([ValueSource(nameof(_SU_Cvt_F_Gp_WD_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_W_")] [Random(RndCnt)] uint wn) + [ValueSource(nameof(_W_))] uint wn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -556,10 +553,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_XS([ValueSource("_SU_Cvt_F_Gp_XS_")] uint opcodes, + public void SU_Cvt_F_Gp_XS([ValueSource(nameof(_SU_Cvt_F_Gp_XS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -573,10 +570,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_XD([ValueSource("_SU_Cvt_F_Gp_XD_")] uint opcodes, + public void SU_Cvt_F_Gp_XD([ValueSource(nameof(_SU_Cvt_F_Gp_XD_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -590,11 +587,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_Fixed_WS([ValueSource("_SU_Cvt_F_Gp_Fixed_WS_")] uint opcodes, + public void SU_Cvt_F_Gp_Fixed_WS([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_WS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_W_")] [Random(RndCnt)] uint wn, - [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_W_))] uint wn, + [Values(1u, 32u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -611,11 +608,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_Fixed_WD([ValueSource("_SU_Cvt_F_Gp_Fixed_WD_")] uint opcodes, + public void SU_Cvt_F_Gp_Fixed_WD([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_WD_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_W_")] [Random(RndCnt)] uint wn, - [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_W_))] uint wn, + [Values(1u, 32u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -632,11 +629,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_Fixed_XS([ValueSource("_SU_Cvt_F_Gp_Fixed_XS_")] uint opcodes, + public void SU_Cvt_F_Gp_Fixed_XS([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_XS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn, - [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_X_))] ulong xn, + [Values(1u, 64u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -653,11 +650,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_Gp_Fixed_XD([ValueSource("_SU_Cvt_F_Gp_Fixed_XD_")] uint opcodes, + public void SU_Cvt_F_Gp_Fixed_XD([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_XD_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn, - [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_X_))] ulong xn, + [Values(1u, 64u)] uint fBits) { uint scale = (64u - fBits) & 0x3Fu; @@ -674,4 +671,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs index 0c90d0bad..aa68cded3 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _Vrint_AMNP_V_F32_() { - return new uint[] + return new[] { 0xf3ba0500u, // VRINTA.F32 Q0, Q0 0xf3ba0680u, // VRINTM.F32 Q0, Q0 @@ -28,8 +28,8 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static uint[] _1S_() { - return new uint[] { 0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu }; } private static IEnumerable<ulong> _1S_F_() @@ -219,10 +219,10 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VCVT.F32.<dt> <Sd>, <Sm>")] public void Vcvt_I32_F32([Values(0u, 1u, 2u, 3u)] uint rd, [Values(0u, 1u, 2u, 3u)] uint rm, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3, + [ValueSource(nameof(_1S_))] uint s0, + [ValueSource(nameof(_1S_))] uint s1, + [ValueSource(nameof(_1S_))] uint s2, + [ValueSource(nameof(_1S_))] uint s3, [Values] bool unsigned, // <U32, S32> [Values(RMode.Rn)] RMode rMode) { @@ -249,10 +249,10 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VCVT.F64.<dt> <Dd>, <Sm>")] public void Vcvt_I32_F64([Values(0u, 1u)] uint rd, [Values(0u, 1u, 2u, 3u)] uint rm, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3, + [ValueSource(nameof(_1S_))] uint s0, + [ValueSource(nameof(_1S_))] uint s1, + [ValueSource(nameof(_1S_))] uint s2, + [ValueSource(nameof(_1S_))] uint s3, [Values] bool unsigned, // <U32, S32> [Values(RMode.Rn)] RMode rMode) { @@ -344,10 +344,10 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VCVT<top>.F16.F32 <Sd>, <Dm>")] public void Vcvt_F32_F16([Values(0u, 1u, 2u, 3u)] uint rd, [Values(0u, 1u, 2u, 3u)] uint rm, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2, - [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3, + [ValueSource(nameof(_1S_))] uint s0, + [ValueSource(nameof(_1S_))] uint s1, + [ValueSource(nameof(_1S_))] uint s2, + [ValueSource(nameof(_1S_))] uint s3, [Values] bool top) { uint opcode = 0xeeb30a40; // VCVTB.F16.F32 S0, D0 @@ -428,4 +428,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs index 0ab40cad2..dae09a160 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs @@ -1,7 +1,6 @@ #define SimdExt using ARMeilleure.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu @@ -14,22 +13,19 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource" private static ulong[] _8B_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } #endregion - private const int RndCnt = 2; - private const int RndCntIndex = 2; - [Test, Pairwise, Description("EXT <Vd>.8B, <Vn>.8B, <Vm>.8B, #<index>")] public void Ext_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b, + [Values(0u, 7u)] uint index) { uint imm4 = index & 0x7u; @@ -50,10 +46,10 @@ namespace Ryujinx.Tests.Cpu public void Ext_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b, + [Values(0u, 15u)] uint index) { uint imm4 = index & 0xFu; @@ -71,4 +67,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs b/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs index 825a1c78c..bf13236e9 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs @@ -1,9 +1,7 @@ #define SimdFcond using ARMeilleure.State; - using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -101,7 +99,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _F_Ccmp_Ccmpe_S_S_() { - return new uint[] + return new[] { 0x1E220420u, // FCCMP S1, S2, #0, EQ 0x1E220430u // FCCMPE S1, S2, #0, EQ @@ -110,7 +108,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Ccmp_Ccmpe_S_D_() { - return new uint[] + return new[] { 0x1E620420u, // FCCMP D1, D2, #0, EQ 0x1E620430u // FCCMPE D1, D2, #0, EQ @@ -119,7 +117,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Csel_S_S_() { - return new uint[] + return new[] { 0x1E220C20u // FCSEL S0, S1, S2, EQ }; @@ -127,7 +125,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Csel_S_D_() { - return new uint[] + return new[] { 0x1E620C20u // FCSEL D0, D1, D2, EQ }; @@ -142,9 +140,9 @@ namespace Ryujinx.Tests.Cpu private static readonly bool NoNaNs = false; [Test, Pairwise] [Explicit] - public void F_Ccmp_Ccmpe_S_S([ValueSource("_F_Ccmp_Ccmpe_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b, + public void F_Ccmp_Ccmpe_S_S([ValueSource(nameof(_F_Ccmp_Ccmpe_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -167,9 +165,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Ccmp_Ccmpe_S_D([ValueSource("_F_Ccmp_Ccmpe_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b, + public void F_Ccmp_Ccmpe_S_D([ValueSource(nameof(_F_Ccmp_Ccmpe_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, @@ -192,9 +190,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Csel_S_S([ValueSource("_F_Csel_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b, + public void F_Csel_S_S([ValueSource(nameof(_F_Csel_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, @@ -213,9 +211,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Csel_S_D([ValueSource("_F_Csel_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b, + public void F_Csel_S_D([ValueSource(nameof(_F_Csel_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // <EQ, NE, CS/HS, CC/LO, 0b0100u, 0b0101u, 0b0110u, 0b0111u, // MI, PL, VS, VC, 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs b/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs index 534dba57d..aa6e0c944 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs @@ -1,7 +1,6 @@ #define SimdFmov using ARMeilleure.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu @@ -14,7 +13,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource" private static uint[] _F_Mov_Si_S_() { - return new uint[] + return new[] { 0x1E201000u // FMOV S0, #2.0 }; @@ -22,7 +21,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Si_D_() { - return new uint[] + return new[] { 0x1E601000u // FMOV D0, #2.0 }; @@ -30,7 +29,7 @@ namespace Ryujinx.Tests.Cpu #endregion [Test, Pairwise] [Explicit] - public void F_Mov_Si_S([ValueSource("_F_Mov_Si_S_")] uint opcodes, + public void F_Mov_Si_S([ValueSource(nameof(_F_Mov_Si_S_))] uint opcodes, [Range(0u, 255u, 1u)] uint imm8) { opcodes |= ((imm8 & 0xFFu) << 13); @@ -44,7 +43,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Si_D([ValueSource("_F_Mov_Si_D_")] uint opcodes, + public void F_Mov_Si_D([ValueSource(nameof(_F_Mov_Si_D_))] uint opcodes, [Range(0u, 255u, 1u)] uint imm8) { opcodes |= ((imm8 & 0xFFu) << 13); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs b/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs index 1ea74a112..d946b4330 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs @@ -1,9 +1,7 @@ #define SimdImm using ARMeilleure.State; - using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -50,14 +48,14 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<byte> _8BIT_IMM_() @@ -94,7 +92,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _Bic_Orr_Vi_16bit_() { - return new uint[] + return new[] { 0x2F009400u, // BIC V0.4H, #0 0x0F009400u // ORR V0.4H, #0 @@ -103,7 +101,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Bic_Orr_Vi_32bit_() { - return new uint[] + return new[] { 0x2F001400u, // BIC V0.2S, #0 0x0F001400u // ORR V0.2S, #0 @@ -112,7 +110,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Vi_2S_() { - return new uint[] + return new[] { 0x0F00F400u // FMOV V0.2S, #2.0 }; @@ -120,7 +118,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Vi_4S_() { - return new uint[] + return new[] { 0x4F00F400u // FMOV V0.4S, #2.0 }; @@ -128,7 +126,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mov_Vi_2D_() { - return new uint[] + return new[] { 0x6F00F400u // FMOV V0.2D, #2.0 }; @@ -136,7 +134,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Movi_V_8bit_() { - return new uint[] + return new[] { 0x0F00E400u // MOVI V0.8B, #0 }; @@ -144,7 +142,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Movi_Mvni_V_16bit_shifted_imm_() { - return new uint[] + return new[] { 0x0F008400u, // MOVI V0.4H, #0 0x2F008400u // MVNI V0.4H, #0 @@ -153,7 +151,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Movi_Mvni_V_32bit_shifted_imm_() { - return new uint[] + return new[] { 0x0F000400u, // MOVI V0.2S, #0 0x2F000400u // MVNI V0.2S, #0 @@ -162,7 +160,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Movi_Mvni_V_32bit_shifting_ones_() { - return new uint[] + return new[] { 0x0F00C400u, // MOVI V0.2S, #0, MSL #8 0x2F00C400u // MVNI V0.2S, #0, MSL #8 @@ -171,7 +169,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Movi_V_64bit_scalar_() { - return new uint[] + return new[] { 0x2F00E400u // MOVI D0, #0 }; @@ -179,21 +177,20 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Movi_V_64bit_vector_() { - return new uint[] + return new[] { 0x6F00E400u // MOVI V0.2D, #0 }; } #endregion - private const int RndCnt = 2; private const int RndCntImm8 = 2; private const int RndCntImm64 = 2; [Test, Pairwise] - public void Bic_Orr_Vi_16bit([ValueSource("_Bic_Orr_Vi_16bit_")] uint opcodes, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_8BIT_IMM_")] byte imm8, + public void Bic_Orr_Vi_16bit([ValueSource(nameof(_Bic_Orr_Vi_16bit_))] uint opcodes, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_8BIT_IMM_))] byte imm8, [Values(0b0u, 0b1u)] uint amount, // <0, 8> [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { @@ -212,9 +209,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Bic_Orr_Vi_32bit([ValueSource("_Bic_Orr_Vi_32bit_")] uint opcodes, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8BIT_IMM_")] byte imm8, + public void Bic_Orr_Vi_32bit([ValueSource(nameof(_Bic_Orr_Vi_32bit_))] uint opcodes, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_8BIT_IMM_))] byte imm8, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint amount, // <0, 8, 16, 24> [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -233,7 +230,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Vi_2S([ValueSource("_F_Mov_Vi_2S_")] uint opcodes, + public void F_Mov_Vi_2S([ValueSource(nameof(_F_Mov_Vi_2S_))] uint opcodes, [Range(0u, 255u, 1u)] uint abcdefgh) { uint abc = (abcdefgh & 0xE0u) >> 5; @@ -250,7 +247,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Vi_4S([ValueSource("_F_Mov_Vi_4S_")] uint opcodes, + public void F_Mov_Vi_4S([ValueSource(nameof(_F_Mov_Vi_4S_))] uint opcodes, [Range(0u, 255u, 1u)] uint abcdefgh) { uint abc = (abcdefgh & 0xE0u) >> 5; @@ -264,7 +261,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mov_Vi_2D([ValueSource("_F_Mov_Vi_2D_")] uint opcodes, + public void F_Mov_Vi_2D([ValueSource(nameof(_F_Mov_Vi_2D_))] uint opcodes, [Range(0u, 255u, 1u)] uint abcdefgh) { uint abc = (abcdefgh & 0xE0u) >> 5; @@ -278,8 +275,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Movi_V_8bit([ValueSource("_Movi_V_8bit_")] uint opcodes, - [ValueSource("_8BIT_IMM_")] byte imm8, + public void Movi_V_8bit([ValueSource(nameof(_Movi_V_8bit_))] uint opcodes, + [ValueSource(nameof(_8BIT_IMM_))] byte imm8, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint abc = (imm8 & 0xE0u) >> 5; @@ -297,8 +294,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Movi_Mvni_V_16bit_shifted_imm([ValueSource("_Movi_Mvni_V_16bit_shifted_imm_")] uint opcodes, - [ValueSource("_8BIT_IMM_")] byte imm8, + public void Movi_Mvni_V_16bit_shifted_imm([ValueSource(nameof(_Movi_Mvni_V_16bit_shifted_imm_))] uint opcodes, + [ValueSource(nameof(_8BIT_IMM_))] byte imm8, [Values(0b0u, 0b1u)] uint amount, // <0, 8> [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { @@ -318,8 +315,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Movi_Mvni_V_32bit_shifted_imm([ValueSource("_Movi_Mvni_V_32bit_shifted_imm_")] uint opcodes, - [ValueSource("_8BIT_IMM_")] byte imm8, + public void Movi_Mvni_V_32bit_shifted_imm([ValueSource(nameof(_Movi_Mvni_V_32bit_shifted_imm_))] uint opcodes, + [ValueSource(nameof(_8BIT_IMM_))] byte imm8, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint amount, // <0, 8, 16, 24> [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -339,8 +336,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Movi_Mvni_V_32bit_shifting_ones([ValueSource("_Movi_Mvni_V_32bit_shifting_ones_")] uint opcodes, - [ValueSource("_8BIT_IMM_")] byte imm8, + public void Movi_Mvni_V_32bit_shifting_ones([ValueSource(nameof(_Movi_Mvni_V_32bit_shifting_ones_))] uint opcodes, + [ValueSource(nameof(_8BIT_IMM_))] byte imm8, [Values(0b0u, 0b1u)] uint amount, // <8, 16> [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -360,8 +357,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Movi_V_64bit_scalar([ValueSource("_Movi_V_64bit_scalar_")] uint opcodes, - [ValueSource("_64BIT_IMM_")] ulong imm) + public void Movi_V_64bit_scalar([ValueSource(nameof(_Movi_V_64bit_scalar_))] uint opcodes, + [ValueSource(nameof(_64BIT_IMM_))] ulong imm) { byte imm8 = ShrinkImm64(imm); @@ -379,8 +376,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Movi_V_64bit_vector([ValueSource("_Movi_V_64bit_vector_")] uint opcodes, - [ValueSource("_64BIT_IMM_")] ulong imm) + public void Movi_V_64bit_vector([ValueSource(nameof(_Movi_V_64bit_vector_))] uint opcodes, + [ValueSource(nameof(_64BIT_IMM_))] ulong imm) { byte imm8 = ShrinkImm64(imm); @@ -395,4 +392,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs index 031ed0f2c..e1e81a000 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs @@ -1,7 +1,6 @@ #define SimdIns using ARMeilleure.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu @@ -14,66 +13,63 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource" private static ulong[] _1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static uint[] _W_() { - return new uint[] { 0x00000000u, 0x0000007Fu, - 0x00000080u, 0x000000FFu, - 0x00007FFFu, 0x00008000u, - 0x0000FFFFu, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { 0x00000000u, 0x0000007Fu, + 0x00000080u, 0x000000FFu, + 0x00007FFFu, 0x00008000u, + 0x0000FFFFu, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu }; } private static ulong[] _X_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } #endregion - private const int RndCnt = 2; - private const int RndCntIndex = 2; - [Test, Pairwise, Description("DUP <Vd>.<T>, W<n>")] public void Dup_Gp_W([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_W_")] [Random(RndCnt)] uint wn, + [ValueSource(nameof(_W_))] uint wn, [Values(0, 1, 2)] int size, // Q0: <8B, 4H, 2S> [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 8H, 4S> { @@ -96,7 +92,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("DUP <Vd>.<T>, X<n>")] public void Dup_Gp_X([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + [ValueSource(nameof(_X_))] ulong xn) { uint opcode = 0x4E080C00; // DUP V0.2D, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -111,8 +107,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP B0, V1.B[<index>]")] - public void Dup_S_B([ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) + public void Dup_S_B([ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 15u)] uint index) { const int size = 0; @@ -131,8 +127,8 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP H0, V1.H[<index>]")] - public void Dup_S_H([ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) + public void Dup_S_H([ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 7u)] uint index) { const int size = 1; @@ -151,7 +147,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP S0, V1.S[<index>]")] - public void Dup_S_S([ValueSource("_2S_")] [Random(RndCnt)] ulong a, + public void Dup_S_S([ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { const int size = 2; @@ -171,7 +167,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP D0, V1.D[<index>]")] - public void Dup_S_D([ValueSource("_1D_")] [Random(RndCnt)] ulong a, + public void Dup_S_D([ValueSource(nameof(_1D_))] ulong a, [Values(0u, 1u)] uint index) { const int size = 3; @@ -193,9 +189,9 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.B[<index>]")] public void Dup_V_8B_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 15u)] uint index, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { const int size = 0; @@ -218,9 +214,9 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.H[<index>]")] public void Dup_V_4H_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 7u)] uint index, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { const int size = 1; @@ -243,8 +239,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.S[<index>]")] public void Dup_V_2S_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -268,8 +264,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.D[<index>]")] public void Dup_V_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, [Values(0u, 1u)] uint index, [Values(0b1u)] uint q) // <2D> { @@ -293,9 +289,9 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.B[<index>], W<n>")] public void Ins_Gp_WB([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_W_")] [Random(RndCnt)] uint wn, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_W_))] uint wn, + [Values(0u, 15u)] uint index) { const int size = 0; @@ -316,9 +312,9 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.H[<index>], W<n>")] public void Ins_Gp_WH([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_W_")] [Random(RndCnt)] uint wn, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_W_))] uint wn, + [Values(0u, 7u)] uint index) { const int size = 1; @@ -339,8 +335,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.S[<index>], W<n>")] public void Ins_Gp_WS([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_W_")] [Random(RndCnt)] uint wn, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_W_))] uint wn, [Values(0u, 1u, 2u, 3u)] uint index) { const int size = 2; @@ -362,8 +358,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.D[<index>], X<n>")] public void Ins_Gp_XD([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_X_")] [Random(RndCnt)] ulong xn, + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_X_))] ulong xn, [Values(0u, 1u)] uint index) { const int size = 3; @@ -385,10 +381,10 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.B[<index1>], <Vn>.B[<index2>]")] public void Ins_V_BB([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint dstIndex, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint srcIndex) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 15u)] uint dstIndex, + [Values(0u, 15u)] uint srcIndex) { const int size = 0; @@ -411,10 +407,10 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.H[<index1>], <Vn>.H[<index2>]")] public void Ins_V_HH([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint dstIndex, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint srcIndex) + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 7u)] uint dstIndex, + [Values(0u, 7u)] uint srcIndex) { const int size = 1; @@ -437,8 +433,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.S[<index1>], <Vn>.S[<index2>]")] public void Ins_V_SS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint dstIndex, [Values(0u, 1u, 2u, 3u)] uint srcIndex) { @@ -463,8 +459,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("INS <Vd>.D[<index1>], <Vn>.D[<index2>]")] public void Ins_V_DD([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, [Values(0u, 1u)] uint dstIndex, [Values(0u, 1u)] uint srcIndex) { @@ -489,8 +485,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Wd>, <Vn>.B[<index>]")] public void Smov_S_BW([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) + [ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 15u)] uint index) { const int size = 0; @@ -512,8 +508,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Wd>, <Vn>.H[<index>]")] public void Smov_S_HW([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) + [ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 7u)] uint index) { const int size = 1; @@ -535,8 +531,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Xd>, <Vn>.B[<index>]")] public void Smov_S_BX([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) + [ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 15u)] uint index) { const int size = 0; @@ -557,8 +553,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Xd>, <Vn>.H[<index>]")] public void Smov_S_HX([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) + [ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 7u)] uint index) { const int size = 1; @@ -579,7 +575,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Xd>, <Vn>.S[<index>]")] public void Smov_S_SX([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { const int size = 2; @@ -601,8 +597,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Wd>, <Vn>.B[<index>]")] public void Umov_S_BW([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) + [ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 15u)] uint index) { const int size = 0; @@ -624,8 +620,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Wd>, <Vn>.H[<index>]")] public void Umov_S_HW([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) + [ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 7u)] uint index) { const int size = 1; @@ -647,7 +643,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Wd>, <Vn>.S[<index>]")] public void Umov_S_SW([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { const int size = 2; @@ -670,7 +666,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Xd>, <Vn>.D[<index>]")] public void Umov_S_DX([Values(0u, 31u)] uint rd, [Values(1u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_1D_))] ulong a, [Values(0u, 1u)] uint index) { const int size = 3; @@ -690,4 +686,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs index e55574fff..2cc1a0943 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs @@ -13,17 +13,17 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _8B4H2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } #endregion #region "ValueSource (Opcodes)" private static uint[] _Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I_() { - return new uint[] + return new[] { 0xf2100110u, // VBIC D0, D0, D0 0xf3300110u, // VBIF D0, D0, D0 @@ -38,7 +38,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vbic_Vorr_II_() { - return new uint[] + return new[] { 0xf2800130u, // VBIC.I32 D0, #0 (A1) 0xf2800930u, // VBIC.I16 D0, #0 (A2) @@ -48,16 +48,14 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; - [Test, Pairwise] - public void Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I([ValueSource("_Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I_")] uint opcode, + public void Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I([ValueSource(nameof(_Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I_))] uint opcode, [Range(0u, 5u)] uint rd, [Range(0u, 5u)] uint rn, [Range(0u, 5u)] uint rm, - [Values(ulong.MinValue, ulong.MaxValue)] [Random(RndCnt)] ulong z, - [Values(ulong.MinValue, ulong.MaxValue)] [Random(RndCnt)] ulong a, - [Values(ulong.MinValue, ulong.MaxValue)] [Random(RndCnt)] ulong b, + [Values(ulong.MinValue, ulong.MaxValue)] ulong z, + [Values(ulong.MinValue, ulong.MaxValue)] ulong a, + [Values(ulong.MinValue, ulong.MaxValue)] ulong b, [Values] bool q) { if (q) @@ -83,10 +81,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vbic_Vorr_II([ValueSource("_Vbic_Vorr_II_")] uint opcode, + public void Vbic_Vorr_II([ValueSource(nameof(_Vbic_Vorr_II_))] uint opcode, [Values(0u, 1u)] uint rd, - [Values(ulong.MinValue, ulong.MaxValue)] [Random(RndCnt)] ulong z, - [Values(byte.MinValue, byte.MaxValue)] [Random(RndCnt)] byte imm, + [Values(ulong.MinValue, ulong.MaxValue)] ulong z, + [Values(byte.MinValue, byte.MaxValue)] byte imm, [Values(0u, 1u, 2u, 3u)] uint cMode, [Values] bool q) { @@ -119,9 +117,9 @@ namespace Ryujinx.Tests.Cpu public void Vtst([Range(0u, 5u)] uint rd, [Range(0u, 5u)] uint rn, [Range(0u, 5u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0u, 1u, 2u)] uint size, [Values] bool q) { @@ -152,4 +150,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs index b14fdcd55..2f9504cbf 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs @@ -10,7 +10,6 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestSimdMemory32 : CpuTest32 { #if SimdMemory32 - private const int RndCntImm = 2; private uint[] _ldStModes = { @@ -41,7 +40,7 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 7u)] uint index, [Range(0u, 3u)] uint n, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint offset) + [Values(0x0u)] uint offset) { var data = GenerateVectorSequence(0x1000); SetWorkingMemory(0, data); @@ -71,7 +70,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 3u)] uint n, [Range(0u, 2u)] uint size, [Values] bool t, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint offset) + [Values(0x0u)] uint offset) { var data = GenerateVectorSequence(0x1000); SetWorkingMemory(0, data); @@ -97,7 +96,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 13u, 15u)] uint rm, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 10u)] uint mode, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint offset) + [Values(0x0u)] uint offset) { var data = GenerateVectorSequence(0x1000); SetWorkingMemory(0, data); @@ -127,7 +126,7 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 7u)] uint index, [Range(0u, 3u)] uint n, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint offset) + [Values(0x0u)] uint offset) { var data = GenerateVectorSequence(0x1000); SetWorkingMemory(0, data); @@ -158,7 +157,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 13u, 15u)] uint rm, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 10u)] uint mode, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint offset) + [Values(0x0u)] uint offset) { var data = GenerateVectorSequence(0x1000); SetWorkingMemory(0, data); @@ -187,7 +186,7 @@ namespace Ryujinx.Tests.Cpu public void Vldm([Values(0u, 13u)] uint rn, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 2u)] uint mode, - [Values(0x1u, 0x32u)] [Random(2u, 31u, RndCntImm)] uint regs, + [Values(0x1u, 0x32u)] uint regs, [Values] bool single) { var data = GenerateVectorSequence(0x1000); @@ -235,7 +234,7 @@ namespace Ryujinx.Tests.Cpu public void Vldr([Values(2u, 3u)] uint size, // FP16 is not supported for now [Values(0u)] uint rn, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint sd, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint imm, + [Values(0x0u)] uint imm, [Values] bool sub) { var data = GenerateVectorSequence(0x1000); @@ -270,7 +269,7 @@ namespace Ryujinx.Tests.Cpu public void Vstr([Values(2u, 3u)] uint size, // FP16 is not supported for now [Values(0u)] uint rn, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint sd, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint imm, + [Values(0x0u)] uint imm, [Values] bool sub) { var data = GenerateVectorSequence(0x1000); @@ -329,4 +328,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs index bb2536432..399b058f2 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VMOV.I<size> <Dd/Qd>, #<imm>")] public void Movi_V([Range(0u, 10u)] uint variant, [Values(0u, 1u, 2u, 3u)] uint vd, - [Values(0x0u)] [Random(1u, 0xffu, RndCntImm)] uint imm, + [Values(0x0u)] uint imm, [Values] bool q) { uint[] variants = @@ -62,7 +62,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VMOV.F<size> <Sd>, #<imm>")] public void Movi_S([Range(2u, 3u)] uint size, [Values(0u, 1u, 2u, 3u)] uint vd, - [Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint imm) + [Values(0x0u)] uint imm) { uint opcode = 0xeeb00800u; opcode |= (size & 3) << 8; @@ -292,7 +292,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VMVN.I<size> <Dd/Qd>, #<imm>")] public void Mvni_V([Range(0u, 7u)] uint variant, [Values(0u, 1u, 2u, 3u)] uint vd, - [Values(0x0u)] [Random(1u, 0xffu, RndCntImm)] uint imm, + [Values(0x0u)] uint imm, [Values] bool q) { uint[] variants = @@ -596,4 +596,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 0daeb1d10..1f2a6bc81 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -1,9 +1,7 @@ #define SimdReg using ARMeilleure.State; - using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -16,72 +14,72 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _1B1H1S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x000000000000007Ful, - 0x0000000000000080ul, 0x00000000000000FFul, - 0x0000000000007FFFul, 0x0000000000008000ul, - 0x000000000000FFFFul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul, - 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, - 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x000000000000007Ful, + 0x0000000000000080ul, 0x00000000000000FFul, + 0x0000000000007FFFul, 0x0000000000008000ul, + 0x000000000000FFFFul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, + 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _1H1S_() { - return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul, - 0x0000000000008000ul, 0x000000000000FFFFul, - 0x000000007FFFFFFFul, 0x0000000080000000ul, - 0x00000000FFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul, + 0x000000007FFFFFFFul, 0x0000000080000000ul, + 0x00000000FFFFFFFFul }; } private static ulong[] _4H2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H2S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H2S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<ulong> _1S_F_() @@ -212,7 +210,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S_() { - return new uint[] + return new[] { 0x7EA2D420u, // FABD S0, S1, S2 0x1E222820u, // FADD S0, S1, S2 @@ -226,7 +224,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_D_() { - return new uint[] + return new[] { 0x7EE2D420u, // FABD D0, D1, D2 0x1E622820u, // FADD D0, D1, D2 @@ -240,7 +238,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2S_4S_() { - return new uint[] + return new[] { 0x2EA0D400u, // FABD V0.2S, V0.2S, V0.2S 0x0E20D400u, // FADD V0.2S, V0.2S, V0.2S @@ -254,7 +252,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2D_() { - return new uint[] + return new[] { 0x6EE0D400u, // FABD V0.2D, V0.2D, V0.2D 0x4E60D400u, // FADD V0.2D, V0.2D, V0.2D @@ -268,7 +266,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_AcCm_EqGeGt_S_S_() { - return new uint[] + return new[] { 0x7E22EC20u, // FACGE S0, S1, S2 0x7EA2EC20u, // FACGT S0, S1, S2 @@ -280,7 +278,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_AcCm_EqGeGt_S_D_() { - return new uint[] + return new[] { 0x7E62EC20u, // FACGE D0, D1, D2 0x7EE2EC20u, // FACGT D0, D1, D2 @@ -292,7 +290,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_AcCm_EqGeGt_V_2S_4S_() { - return new uint[] + return new[] { 0x2E20EC00u, // FACGE V0.2S, V0.2S, V0.2S 0x2EA0EC00u, // FACGT V0.2S, V0.2S, V0.2S @@ -304,7 +302,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_AcCm_EqGeGt_V_2D_() { - return new uint[] + return new[] { 0x6E60EC00u, // FACGE V0.2D, V0.2D, V0.2D 0x6EE0EC00u, // FACGT V0.2D, V0.2D, V0.2D @@ -316,7 +314,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cmp_Cmpe_S_S_() { - return new uint[] + return new[] { 0x1E222020u, // FCMP S1, S2 0x1E222030u // FCMPE S1, S2 @@ -325,7 +323,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cmp_Cmpe_S_D_() { - return new uint[] + return new[] { 0x1E622020u, // FCMP D1, D2 0x1E622030u // FCMPE D1, D2 @@ -334,7 +332,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Madd_Msub_Nmadd_Nmsub_S_S_() { - return new uint[] + return new[] { 0x1F020C20u, // FMADD S0, S1, S2, S3 0x1F028C20u, // FMSUB S0, S1, S2, S3 @@ -345,7 +343,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Madd_Msub_Nmadd_Nmsub_S_D_() { - return new uint[] + return new[] { 0x1F420C20u, // FMADD D0, D1, D2, D3 0x1F428C20u, // FMSUB D0, D1, D2, D3 @@ -356,7 +354,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Max_Min_Nm_S_S_() { - return new uint[] + return new[] { 0x1E224820u, // FMAX S0, S1, S2 0x1E226820u, // FMAXNM S0, S1, S2 @@ -367,7 +365,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Max_Min_Nm_S_D_() { - return new uint[] + return new[] { 0x1E624820u, // FMAX D0, D1, D2 0x1E626820u, // FMAXNM D0, D1, D2 @@ -378,7 +376,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Max_Min_Nm_P_V_2S_4S_() { - return new uint[] + return new[] { 0x0E20F400u, // FMAX V0.2S, V0.2S, V0.2S 0x0E20C400u, // FMAXNM V0.2S, V0.2S, V0.2S @@ -393,7 +391,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Max_Min_Nm_P_V_2D_() { - return new uint[] + return new[] { 0x4E60F400u, // FMAX V0.2D, V0.2D, V0.2D 0x4E60C400u, // FMAXNM V0.2D, V0.2D, V0.2D @@ -408,7 +406,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mla_Mls_V_2S_4S_() { - return new uint[] + return new[] { 0x0E20CC00u, // FMLA V0.2S, V0.2S, V0.2S 0x0EA0CC00u // FMLS V0.2S, V0.2S, V0.2S @@ -417,7 +415,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mla_Mls_V_2D_() { - return new uint[] + return new[] { 0x4E60CC00u, // FMLA V0.2D, V0.2D, V0.2D 0x4EE0CC00u // FMLS V0.2D, V0.2D, V0.2D @@ -426,7 +424,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recps_Rsqrts_S_S_() { - return new uint[] + return new[] { 0x5E22FC20u, // FRECPS S0, S1, S2 0x5EA2FC20u // FRSQRTS S0, S1, S2 @@ -435,7 +433,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recps_Rsqrts_S_D_() { - return new uint[] + return new[] { 0x5E62FC20u, // FRECPS D0, D1, D2 0x5EE2FC20u // FRSQRTS D0, D1, D2 @@ -444,7 +442,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recps_Rsqrts_V_2S_4S_() { - return new uint[] + return new[] { 0x0E20FC00u, // FRECPS V0.2S, V0.2S, V0.2S 0x0EA0FC00u // FRSQRTS V0.2S, V0.2S, V0.2S @@ -453,7 +451,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Recps_Rsqrts_V_2D_() { - return new uint[] + return new[] { 0x4E60FC00u, // FRECPS V0.2D, V0.2D, V0.2D 0x4EE0FC00u // FRSQRTS V0.2D, V0.2D, V0.2D @@ -462,7 +460,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Mla_Mls_Mul_V_8B_4H_2S_() { - return new uint[] + return new[] { 0x0E209400u, // MLA V0.8B, V0.8B, V0.8B 0x2E209400u, // MLS V0.8B, V0.8B, V0.8B @@ -472,7 +470,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Mla_Mls_Mul_V_16B_8H_4S_() { - return new uint[] + return new[] { 0x4E209400u, // MLA V0.16B, V0.16B, V0.16B 0x6E209400u, // MLS V0.16B, V0.16B, V0.16B @@ -482,7 +480,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Sha1c_Sha1m_Sha1p_Sha1su0_V_() { - return new uint[] + return new[] { 0x5E000000u, // SHA1C Q0, S0, V0.4S 0x5E002000u, // SHA1M Q0, S0, V0.4S @@ -493,7 +491,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Sha256h_Sha256h2_Sha256su1_V_() { - return new uint[] + return new[] { 0x5E004000u, // SHA256H Q0, Q0, V0.4S 0x5E005000u, // SHA256H2 Q0, Q0, V0.4S @@ -503,7 +501,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Max_Min_P_V_() { - return new uint[] + return new[] { 0x0E206400u, // SMAX V0.8B, V0.8B, V0.8B 0x0E20A400u, // SMAXP V0.8B, V0.8B, V0.8B @@ -518,7 +516,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Mlal_Mlsl_Mull_V_8B8H_4H4S_2S2D_() { - return new uint[] + return new[] { 0x0E208000u, // SMLAL V0.8H, V0.8B, V0.8B 0x0E20A000u, // SMLSL V0.8H, V0.8B, V0.8B @@ -531,7 +529,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Mlal_Mlsl_Mull_V_16B8H_8H4S_4S2D_() { - return new uint[] + return new[] { 0x4E208000u, // SMLAL2 V0.8H, V0.16B, V0.16B 0x4E20A000u, // SMLSL2 V0.8H, V0.16B, V0.16B @@ -544,7 +542,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShlReg_S_D_() { - return new uint[] + return new[] { 0x5EE04400u, // SSHL D0, D0, D0 0x7EE04400u // USHL D0, D0, D0 @@ -553,7 +551,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShlReg_V_8B_4H_2S_() { - return new uint[] + return new[] { 0x0E205C00u, // SQRSHL V0.8B, V0.8B, V0.8B 0x0E204C00u, // SQSHL V0.8B, V0.8B, V0.8B @@ -568,7 +566,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShlReg_V_16B_8H_4S_2D_() { - return new uint[] + return new[] { 0x4E205C00u, // SQRSHL V0.16B, V0.16B, V0.16B 0x4E204C00u, // SQSHL V0.16B, V0.16B, V0.16B @@ -592,9 +590,9 @@ namespace Ryujinx.Tests.Cpu public void Add_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x5EE08400; // ADD D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -612,9 +610,9 @@ namespace Ryujinx.Tests.Cpu public void Add_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E208400; // ADD V0.8B, V0.8B, V0.8B @@ -634,9 +632,9 @@ namespace Ryujinx.Tests.Cpu public void Add_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E208400; // ADD V0.16B, V0.16B, V0.16B @@ -656,9 +654,9 @@ namespace Ryujinx.Tests.Cpu public void Addhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x0E204000; // ADDHN V0.8B, V0.8H, V0.8H @@ -678,9 +676,9 @@ namespace Ryujinx.Tests.Cpu public void Addhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x4E204000; // ADDHN2 V0.16B, V0.8H, V0.8H @@ -700,9 +698,9 @@ namespace Ryujinx.Tests.Cpu public void Addp_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E20BC00; // ADDP V0.8B, V0.8B, V0.8B @@ -722,9 +720,9 @@ namespace Ryujinx.Tests.Cpu public void Addp_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E20BC00; // ADDP V0.16B, V0.16B, V0.16B @@ -744,9 +742,9 @@ namespace Ryujinx.Tests.Cpu public void And_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x0E201C00; // AND V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -764,9 +762,9 @@ namespace Ryujinx.Tests.Cpu public void And_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x4E201C00; // AND V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -784,9 +782,9 @@ namespace Ryujinx.Tests.Cpu public void Bic_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x0E601C00; // BIC V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -804,9 +802,9 @@ namespace Ryujinx.Tests.Cpu public void Bic_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x4E601C00; // BIC V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -824,9 +822,9 @@ namespace Ryujinx.Tests.Cpu public void Bif_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x2EE01C00; // BIF V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -844,9 +842,9 @@ namespace Ryujinx.Tests.Cpu public void Bif_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x6EE01C00; // BIF V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -864,9 +862,9 @@ namespace Ryujinx.Tests.Cpu public void Bit_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x2EA01C00; // BIT V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -884,9 +882,9 @@ namespace Ryujinx.Tests.Cpu public void Bit_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x6EA01C00; // BIT V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -904,9 +902,9 @@ namespace Ryujinx.Tests.Cpu public void Bsl_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x2E601C00; // BSL V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -924,9 +922,9 @@ namespace Ryujinx.Tests.Cpu public void Bsl_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x6E601C00; // BSL V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -944,9 +942,9 @@ namespace Ryujinx.Tests.Cpu public void Cmeq_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x7EE08C00; // CMEQ D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -964,9 +962,9 @@ namespace Ryujinx.Tests.Cpu public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E208C00; // CMEQ V0.8B, V0.8B, V0.8B @@ -986,9 +984,9 @@ namespace Ryujinx.Tests.Cpu public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E208C00; // CMEQ V0.16B, V0.16B, V0.16B @@ -1008,9 +1006,9 @@ namespace Ryujinx.Tests.Cpu public void Cmge_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x5EE03C00; // CMGE D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1028,9 +1026,9 @@ namespace Ryujinx.Tests.Cpu public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E203C00; // CMGE V0.8B, V0.8B, V0.8B @@ -1050,9 +1048,9 @@ namespace Ryujinx.Tests.Cpu public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E203C00; // CMGE V0.16B, V0.16B, V0.16B @@ -1072,9 +1070,9 @@ namespace Ryujinx.Tests.Cpu public void Cmgt_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x5EE03400; // CMGT D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1092,9 +1090,9 @@ namespace Ryujinx.Tests.Cpu public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E203400; // CMGT V0.8B, V0.8B, V0.8B @@ -1114,9 +1112,9 @@ namespace Ryujinx.Tests.Cpu public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E203400; // CMGT V0.16B, V0.16B, V0.16B @@ -1136,9 +1134,9 @@ namespace Ryujinx.Tests.Cpu public void Cmhi_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x7EE03400; // CMHI D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1156,9 +1154,9 @@ namespace Ryujinx.Tests.Cpu public void Cmhi_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E203400; // CMHI V0.8B, V0.8B, V0.8B @@ -1178,9 +1176,9 @@ namespace Ryujinx.Tests.Cpu public void Cmhi_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E203400; // CMHI V0.16B, V0.16B, V0.16B @@ -1200,9 +1198,9 @@ namespace Ryujinx.Tests.Cpu public void Cmhs_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x7EE03C00; // CMHS D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1220,9 +1218,9 @@ namespace Ryujinx.Tests.Cpu public void Cmhs_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E203C00; // CMHS V0.8B, V0.8B, V0.8B @@ -1242,9 +1240,9 @@ namespace Ryujinx.Tests.Cpu public void Cmhs_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E203C00; // CMHS V0.16B, V0.16B, V0.16B @@ -1264,9 +1262,9 @@ namespace Ryujinx.Tests.Cpu public void Cmtst_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x5EE08C00; // CMTST D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1284,9 +1282,9 @@ namespace Ryujinx.Tests.Cpu public void Cmtst_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E208C00; // CMTST V0.8B, V0.8B, V0.8B @@ -1306,9 +1304,9 @@ namespace Ryujinx.Tests.Cpu public void Cmtst_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E208C00; // CMTST V0.16B, V0.16B, V0.16B @@ -1328,9 +1326,9 @@ namespace Ryujinx.Tests.Cpu public void Eor_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x2E201C00; // EOR V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1348,9 +1346,9 @@ namespace Ryujinx.Tests.Cpu public void Eor_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x6E201C00; // EOR V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1365,9 +1363,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S([ValueSource("_F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b) + public void F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1385,9 +1383,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_D([ValueSource("_F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + public void F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_D([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1405,13 +1403,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2S_4S([ValueSource("_F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2S_4S_")] uint opcodes, + public void F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2S_4S([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1432,13 +1430,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2D([ValueSource("_F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2D_")] uint opcodes, + public void F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2D([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1457,9 +1455,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_AcCm_EqGeGt_S_S([ValueSource("_F_AcCm_EqGeGt_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b) + public void F_AcCm_EqGeGt_S_S([ValueSource(nameof(_F_AcCm_EqGeGt_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1476,9 +1474,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_AcCm_EqGeGt_S_D([ValueSource("_F_AcCm_EqGeGt_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + public void F_AcCm_EqGeGt_S_D([ValueSource(nameof(_F_AcCm_EqGeGt_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1495,13 +1493,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_AcCm_EqGeGt_V_2S_4S([ValueSource("_F_AcCm_EqGeGt_V_2S_4S_")] uint opcodes, + public void F_AcCm_EqGeGt_V_2S_4S([ValueSource(nameof(_F_AcCm_EqGeGt_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1521,13 +1519,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_AcCm_EqGeGt_V_2D([ValueSource("_F_AcCm_EqGeGt_V_2D_")] uint opcodes, + public void F_AcCm_EqGeGt_V_2D([ValueSource(nameof(_F_AcCm_EqGeGt_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1545,9 +1543,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cmp_Cmpe_S_S([ValueSource("_F_Cmp_Cmpe_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b) + public void F_Cmp_Cmpe_S_S([ValueSource(nameof(_F_Cmp_Cmpe_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b) { V128 v1 = MakeVectorE0(a); V128 v2 = MakeVectorE0(b); @@ -1563,9 +1561,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cmp_Cmpe_S_D([ValueSource("_F_Cmp_Cmpe_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + public void F_Cmp_Cmpe_S_D([ValueSource(nameof(_F_Cmp_Cmpe_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { V128 v1 = MakeVectorE0(a); V128 v2 = MakeVectorE0(b); @@ -1581,10 +1579,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Madd_Msub_Nmadd_Nmsub_S_S([ValueSource("_F_Madd_Msub_Nmadd_Nmsub_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b, - [ValueSource("_1S_F_")] ulong c) + public void F_Madd_Msub_Nmadd_Nmsub_S_S([ValueSource(nameof(_F_Madd_Msub_Nmadd_Nmsub_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b, + [ValueSource(nameof(_1S_F_))] ulong c) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1603,10 +1601,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Madd_Msub_Nmadd_Nmsub_S_D([ValueSource("_F_Madd_Msub_Nmadd_Nmsub_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b, - [ValueSource("_1D_F_")] ulong c) + public void F_Madd_Msub_Nmadd_Nmsub_S_D([ValueSource(nameof(_F_Madd_Msub_Nmadd_Nmsub_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b, + [ValueSource(nameof(_1D_F_))] ulong c) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1625,9 +1623,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Max_Min_Nm_S_S([ValueSource("_F_Max_Min_Nm_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b) + public void F_Max_Min_Nm_S_S([ValueSource(nameof(_F_Max_Min_Nm_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1645,9 +1643,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Max_Min_Nm_S_D([ValueSource("_F_Max_Min_Nm_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + public void F_Max_Min_Nm_S_D([ValueSource(nameof(_F_Max_Min_Nm_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1665,13 +1663,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Max_Min_Nm_P_V_2S_4S([ValueSource("_F_Max_Min_Nm_P_V_2S_4S_")] uint opcodes, + public void F_Max_Min_Nm_P_V_2S_4S([ValueSource(nameof(_F_Max_Min_Nm_P_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1692,13 +1690,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Max_Min_Nm_P_V_2D([ValueSource("_F_Max_Min_Nm_P_V_2D_")] uint opcodes, + public void F_Max_Min_Nm_P_V_2D([ValueSource(nameof(_F_Max_Min_Nm_P_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1717,13 +1715,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Mla_Mls_V_2S_4S([ValueSource("_F_Mla_Mls_V_2S_4S_")] uint opcodes, + public void F_Mla_Mls_V_2S_4S([ValueSource(nameof(_F_Mla_Mls_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1744,13 +1742,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Mla_Mls_V_2D([ValueSource("_F_Mla_Mls_V_2D_")] uint opcodes, + public void F_Mla_Mls_V_2D([ValueSource(nameof(_F_Mla_Mls_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1769,9 +1767,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Recps_Rsqrts_S_S([ValueSource("_F_Recps_Rsqrts_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b) + public void F_Recps_Rsqrts_S_S([ValueSource(nameof(_F_Recps_Rsqrts_S_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); @@ -1789,9 +1787,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Recps_Rsqrts_S_D([ValueSource("_F_Recps_Rsqrts_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + public void F_Recps_Rsqrts_S_D([ValueSource(nameof(_F_Recps_Rsqrts_S_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); @@ -1809,13 +1807,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Recps_Rsqrts_V_2S_4S([ValueSource("_F_Recps_Rsqrts_V_2S_4S_")] uint opcodes, + public void F_Recps_Rsqrts_V_2S_4S([ValueSource(nameof(_F_Recps_Rsqrts_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1836,13 +1834,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Recps_Rsqrts_V_2D([ValueSource("_F_Recps_Rsqrts_V_2D_")] uint opcodes, + public void F_Recps_Rsqrts_V_2D([ValueSource(nameof(_F_Recps_Rsqrts_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b) + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b) { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1861,13 +1859,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Mla_Mls_Mul_V_8B_4H_2S([ValueSource("_Mla_Mls_Mul_V_8B_4H_2S_")] uint opcodes, + public void Mla_Mls_Mul_V_8B_4H_2S([ValueSource(nameof(_Mla_Mls_Mul_V_8B_4H_2S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1883,13 +1881,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Mla_Mls_Mul_V_16B_8H_4S([ValueSource("_Mla_Mls_Mul_V_16B_8H_4S_")] uint opcodes, + public void Mla_Mls_Mul_V_16B_8H_4S([ValueSource(nameof(_Mla_Mls_Mul_V_16B_8H_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1908,9 +1906,9 @@ namespace Ryujinx.Tests.Cpu public void Orn_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x0EE01C00; // ORN V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1928,9 +1926,9 @@ namespace Ryujinx.Tests.Cpu public void Orn_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x4EE01C00; // ORN V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1948,9 +1946,9 @@ namespace Ryujinx.Tests.Cpu public void Orr_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x0EA01C00; // ORR V0.8B, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1968,9 +1966,9 @@ namespace Ryujinx.Tests.Cpu public void Orr_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [ValueSource(nameof(_8B_))] ulong b) { uint opcode = 0x4EA01C00; // ORR V0.16B, V0.16B, V0.16B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1988,12 +1986,12 @@ namespace Ryujinx.Tests.Cpu public void Pmull_V([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong z0, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong z1, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong a0, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong a1, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong b0, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong b1, + [ValueSource(nameof(_8B1D_))] ulong z0, + [ValueSource(nameof(_8B1D_))] ulong z1, + [ValueSource(nameof(_8B1D_))] ulong a0, + [ValueSource(nameof(_8B1D_))] ulong a1, + [ValueSource(nameof(_8B1D_))] ulong b0, + [ValueSource(nameof(_8B1D_))] ulong b1, [Values(0b00u, 0b11u)] uint size, // Q0: <8B, 1D> => <8H, 1Q> [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 2D> => <8H, 1Q> { @@ -2015,9 +2013,9 @@ namespace Ryujinx.Tests.Cpu public void Raddhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x2E204000; // RADDHN V0.8B, V0.8H, V0.8H @@ -2037,9 +2035,9 @@ namespace Ryujinx.Tests.Cpu public void Raddhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x6E204000; // RADDHN2 V0.16B, V0.8H, V0.8H @@ -2059,9 +2057,9 @@ namespace Ryujinx.Tests.Cpu public void Rsubhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x2E206000; // RSUBHN V0.8B, V0.8H, V0.8H @@ -2081,9 +2079,9 @@ namespace Ryujinx.Tests.Cpu public void Rsubhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x6E206000; // RSUBHN2 V0.16B, V0.8H, V0.8H @@ -2103,9 +2101,9 @@ namespace Ryujinx.Tests.Cpu public void Saba_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E207C00; // SABA V0.8B, V0.8B, V0.8B @@ -2125,9 +2123,9 @@ namespace Ryujinx.Tests.Cpu public void Saba_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x4E207C00; // SABA V0.16B, V0.16B, V0.16B @@ -2147,9 +2145,9 @@ namespace Ryujinx.Tests.Cpu public void Sabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x0E205000; // SABAL V0.8H, V0.8B, V0.8B @@ -2169,9 +2167,9 @@ namespace Ryujinx.Tests.Cpu public void Sabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x4E205000; // SABAL2 V0.8H, V0.16B, V0.16B @@ -2191,9 +2189,9 @@ namespace Ryujinx.Tests.Cpu public void Sabd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E207400; // SABD V0.8B, V0.8B, V0.8B @@ -2213,9 +2211,9 @@ namespace Ryujinx.Tests.Cpu public void Sabd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x4E207400; // SABD V0.16B, V0.16B, V0.16B @@ -2235,9 +2233,9 @@ namespace Ryujinx.Tests.Cpu public void Sabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x0E207000; // SABDL V0.8H, V0.8B, V0.8B @@ -2257,9 +2255,9 @@ namespace Ryujinx.Tests.Cpu public void Sabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x4E207000; // SABDL2 V0.8H, V0.16B, V0.16B @@ -2279,9 +2277,9 @@ namespace Ryujinx.Tests.Cpu public void Saddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x0E200000; // SADDL V0.8H, V0.8B, V0.8B @@ -2301,9 +2299,9 @@ namespace Ryujinx.Tests.Cpu public void Saddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x4E200000; // SADDL2 V0.8H, V0.16B, V0.16B @@ -2323,9 +2321,9 @@ namespace Ryujinx.Tests.Cpu public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x0E201000; // SADDW V0.8H, V0.8H, V0.8B @@ -2345,9 +2343,9 @@ namespace Ryujinx.Tests.Cpu public void Saddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x4E201000; // SADDW2 V0.8H, V0.8H, V0.16B @@ -2364,7 +2362,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Sha1c_Sha1m_Sha1p_Sha1su0_V([ValueSource("_Sha1c_Sha1m_Sha1p_Sha1su0_V_")] uint opcodes, + public void Sha1c_Sha1m_Sha1p_Sha1su0_V([ValueSource(nameof(_Sha1c_Sha1m_Sha1p_Sha1su0_V_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, @@ -2384,7 +2382,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Sha256h_Sha256h2_Sha256su1_V([ValueSource("_Sha256h_Sha256h2_Sha256su1_V_")] uint opcodes, + public void Sha256h_Sha256h2_Sha256su1_V([ValueSource(nameof(_Sha256h_Sha256h2_Sha256su1_V_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, @@ -2407,9 +2405,9 @@ namespace Ryujinx.Tests.Cpu public void Shadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E200400; // SHADD V0.8B, V0.8B, V0.8B @@ -2429,9 +2427,9 @@ namespace Ryujinx.Tests.Cpu public void Shadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x4E200400; // SHADD V0.16B, V0.16B, V0.16B @@ -2451,9 +2449,9 @@ namespace Ryujinx.Tests.Cpu public void Shsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E202400; // SHSUB V0.8B, V0.8B, V0.8B @@ -2473,9 +2471,9 @@ namespace Ryujinx.Tests.Cpu public void Shsub_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x4E202400; // SHSUB V0.16B, V0.16B, V0.16B @@ -2492,13 +2490,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Max_Min_P_V([ValueSource("_SU_Max_Min_P_V_")] uint opcodes, + public void SU_Max_Min_P_V([ValueSource(nameof(_SU_Max_Min_P_V_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size, // Q0: <8B, 4H, 2S> [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 8H, 4S> { @@ -2516,13 +2514,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Mlal_Mlsl_Mull_V_8B8H_4H4S_2S2D([ValueSource("_SU_Mlal_Mlsl_Mull_V_8B8H_4H4S_2S2D_")] uint opcodes, + public void SU_Mlal_Mlsl_Mull_V_8B8H_4H4S_2S2D([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_V_8B8H_4H4S_2S2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2538,13 +2536,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Mlal_Mlsl_Mull_V_16B8H_8H4S_4S2D([ValueSource("_SU_Mlal_Mlsl_Mull_V_16B8H_8H4S_4S2D_")] uint opcodes, + public void SU_Mlal_Mlsl_Mull_V_16B8H_8H4S_4S2D([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_V_16B8H_8H4S_4S2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2563,9 +2561,9 @@ namespace Ryujinx.Tests.Cpu public void Sqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <b, H, S, D> { uint opcode = 0x5E200C00; // SQADD B0, B0, B0 @@ -2585,9 +2583,9 @@ namespace Ryujinx.Tests.Cpu public void Sqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E200C00; // SQADD V0.8B, V0.8B, V0.8B @@ -2607,9 +2605,9 @@ namespace Ryujinx.Tests.Cpu public void Sqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E200C00; // SQADD V0.16B, V0.16B, V0.16B @@ -2629,9 +2627,9 @@ namespace Ryujinx.Tests.Cpu public void Sqdmulh_S_H_S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong z, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong a, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_1H1S_))] ulong z, + [ValueSource(nameof(_1H1S_))] ulong a, + [ValueSource(nameof(_1H1S_))] ulong b, [Values(0b01u, 0b10u)] uint size) // <H, S> { uint opcode = 0x5E20B400; // SQDMULH B0, B0, B0 (RESERVED) @@ -2651,9 +2649,9 @@ namespace Ryujinx.Tests.Cpu public void Sqdmulh_V_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S_))] ulong z, + [ValueSource(nameof(_4H2S_))] ulong a, + [ValueSource(nameof(_4H2S_))] ulong b, [Values(0b01u, 0b10u)] uint size) // <4H, 2S> { uint opcode = 0x0E20B400; // SQDMULH V0.8B, V0.8B, V0.8B (RESERVED) @@ -2673,9 +2671,9 @@ namespace Ryujinx.Tests.Cpu public void Sqdmulh_V_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S_))] ulong z, + [ValueSource(nameof(_4H2S_))] ulong a, + [ValueSource(nameof(_4H2S_))] ulong b, [Values(0b01u, 0b10u)] uint size) // <8H, 4S> { uint opcode = 0x4E20B400; // SQDMULH V0.16B, V0.16B, V0.16B (RESERVED) @@ -2695,9 +2693,9 @@ namespace Ryujinx.Tests.Cpu public void Sqrdmulh_S_H_S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong z, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong a, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_1H1S_))] ulong z, + [ValueSource(nameof(_1H1S_))] ulong a, + [ValueSource(nameof(_1H1S_))] ulong b, [Values(0b01u, 0b10u)] uint size) // <H, S> { uint opcode = 0x7E20B400; // SQRDMULH B0, B0, B0 (RESERVED) @@ -2717,9 +2715,9 @@ namespace Ryujinx.Tests.Cpu public void Sqrdmulh_V_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S_))] ulong z, + [ValueSource(nameof(_4H2S_))] ulong a, + [ValueSource(nameof(_4H2S_))] ulong b, [Values(0b01u, 0b10u)] uint size) // <4H, 2S> { uint opcode = 0x2E20B400; // SQRDMULH V0.8B, V0.8B, V0.8B (RESERVED) @@ -2739,9 +2737,9 @@ namespace Ryujinx.Tests.Cpu public void Sqrdmulh_V_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S_))] ulong z, + [ValueSource(nameof(_4H2S_))] ulong a, + [ValueSource(nameof(_4H2S_))] ulong b, [Values(0b01u, 0b10u)] uint size) // <8H, 4S> { uint opcode = 0x6E20B400; // SQRDMULH V0.16B, V0.16B, V0.16B (RESERVED) @@ -2761,9 +2759,9 @@ namespace Ryujinx.Tests.Cpu public void Sqsub_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <b, H, S, D> { uint opcode = 0x5E202C00; // SQSUB B0, B0, B0 @@ -2783,9 +2781,9 @@ namespace Ryujinx.Tests.Cpu public void Sqsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E202C00; // SQSUB V0.8B, V0.8B, V0.8B @@ -2805,9 +2803,9 @@ namespace Ryujinx.Tests.Cpu public void Sqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E202C00; // SQSUB V0.16B, V0.16B, V0.16B @@ -2827,9 +2825,9 @@ namespace Ryujinx.Tests.Cpu public void Srhadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E201400; // SRHADD V0.8B, V0.8B, V0.8B @@ -2849,9 +2847,9 @@ namespace Ryujinx.Tests.Cpu public void Srhadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x4E201400; // SRHADD V0.16B, V0.16B, V0.16B @@ -2868,13 +2866,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShlReg_S_D([ValueSource("_ShlReg_S_D_")] uint opcodes, + public void ShlReg_S_D([ValueSource(nameof(_ShlReg_S_D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(0ul, 255ul, RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2888,13 +2886,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShlReg_V_8B_4H_2S([ValueSource("_ShlReg_V_8B_4H_2S_")] uint opcodes, + public void ShlReg_V_8B_4H_2S([ValueSource(nameof(_ShlReg_V_8B_4H_2S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(0ul, 255ul, RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2910,13 +2908,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShlReg_V_16B_8H_4S_2D([ValueSource("_ShlReg_V_16B_8H_4S_2D_")] uint opcodes, + public void ShlReg_V_16B_8H_4S_2D([ValueSource(nameof(_ShlReg_V_16B_8H_4S_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(0ul, 255ul, RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2935,9 +2933,9 @@ namespace Ryujinx.Tests.Cpu public void Ssubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x0E202000; // SSUBL V0.8H, V0.8B, V0.8B @@ -2957,9 +2955,9 @@ namespace Ryujinx.Tests.Cpu public void Ssubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x4E202000; // SSUBL2 V0.8H, V0.16B, V0.16B @@ -2979,9 +2977,9 @@ namespace Ryujinx.Tests.Cpu public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x0E203000; // SSUBW V0.8H, V0.8H, V0.8B @@ -3001,9 +2999,9 @@ namespace Ryujinx.Tests.Cpu public void Ssubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x4E203000; // SSUBW2 V0.8H, V0.8H, V0.16B @@ -3023,9 +3021,9 @@ namespace Ryujinx.Tests.Cpu public void Sub_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [ValueSource(nameof(_1D_))] ulong b) { uint opcode = 0x7EE08400; // SUB D0, D0, D0 opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -3043,9 +3041,9 @@ namespace Ryujinx.Tests.Cpu public void Sub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E208400; // SUB V0.8B, V0.8B, V0.8B @@ -3065,9 +3063,9 @@ namespace Ryujinx.Tests.Cpu public void Sub_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E208400; // SUB V0.16B, V0.16B, V0.16B @@ -3087,9 +3085,9 @@ namespace Ryujinx.Tests.Cpu public void Subhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { uint opcode = 0x0E206000; // SUBHN V0.8B, V0.8H, V0.8H @@ -3109,9 +3107,9 @@ namespace Ryujinx.Tests.Cpu public void Subhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] ulong a, + [ValueSource(nameof(_4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { uint opcode = 0x4E206000; // SUBHN2 V0.16B, V0.8H, V0.8H @@ -3131,9 +3129,9 @@ namespace Ryujinx.Tests.Cpu public void Trn1_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E002800; // TRN1 V0.8B, V0.8B, V0.8B @@ -3153,9 +3151,9 @@ namespace Ryujinx.Tests.Cpu public void Trn1_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E002800; // TRN1 V0.16B, V0.16B, V0.16B @@ -3175,9 +3173,9 @@ namespace Ryujinx.Tests.Cpu public void Trn2_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E006800; // TRN2 V0.8B, V0.8B, V0.8B @@ -3197,9 +3195,9 @@ namespace Ryujinx.Tests.Cpu public void Trn2_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E006800; // TRN2 V0.16B, V0.16B, V0.16B @@ -3219,9 +3217,9 @@ namespace Ryujinx.Tests.Cpu public void Uaba_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E207C00; // UABA V0.8B, V0.8B, V0.8B @@ -3241,9 +3239,9 @@ namespace Ryujinx.Tests.Cpu public void Uaba_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x6E207C00; // UABA V0.16B, V0.16B, V0.16B @@ -3263,9 +3261,9 @@ namespace Ryujinx.Tests.Cpu public void Uabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x2E205000; // UABAL V0.8H, V0.8B, V0.8B @@ -3285,9 +3283,9 @@ namespace Ryujinx.Tests.Cpu public void Uabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x6E205000; // UABAL2 V0.8H, V0.16B, V0.16B @@ -3307,9 +3305,9 @@ namespace Ryujinx.Tests.Cpu public void Uabd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E207400; // UABD V0.8B, V0.8B, V0.8B @@ -3329,9 +3327,9 @@ namespace Ryujinx.Tests.Cpu public void Uabd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x6E207400; // UABD V0.16B, V0.16B, V0.16B @@ -3351,9 +3349,9 @@ namespace Ryujinx.Tests.Cpu public void Uabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x2E207000; // UABDL V0.8H, V0.8B, V0.8B @@ -3373,9 +3371,9 @@ namespace Ryujinx.Tests.Cpu public void Uabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x6E207000; // UABDL2 V0.8H, V0.16B, V0.16B @@ -3395,9 +3393,9 @@ namespace Ryujinx.Tests.Cpu public void Uaddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x2E200000; // UADDL V0.8H, V0.8B, V0.8B @@ -3417,9 +3415,9 @@ namespace Ryujinx.Tests.Cpu public void Uaddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x6E200000; // UADDL2 V0.8H, V0.16B, V0.16B @@ -3439,9 +3437,9 @@ namespace Ryujinx.Tests.Cpu public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x2E201000; // UADDW V0.8H, V0.8H, V0.8B @@ -3461,9 +3459,9 @@ namespace Ryujinx.Tests.Cpu public void Uaddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x6E201000; // UADDW2 V0.8H, V0.8H, V0.16B @@ -3483,9 +3481,9 @@ namespace Ryujinx.Tests.Cpu public void Uhadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E200400; // UHADD V0.8B, V0.8B, V0.8B @@ -3505,9 +3503,9 @@ namespace Ryujinx.Tests.Cpu public void Uhadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x6E200400; // UHADD V0.16B, V0.16B, V0.16B @@ -3527,9 +3525,9 @@ namespace Ryujinx.Tests.Cpu public void Uhsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E202400; // UHSUB V0.8B, V0.8B, V0.8B @@ -3549,9 +3547,9 @@ namespace Ryujinx.Tests.Cpu public void Uhsub_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x6E202400; // UHSUB V0.16B, V0.16B, V0.16B @@ -3571,9 +3569,9 @@ namespace Ryujinx.Tests.Cpu public void Uqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <b, H, S, D> { uint opcode = 0x7E200C00; // UQADD B0, B0, B0 @@ -3593,9 +3591,9 @@ namespace Ryujinx.Tests.Cpu public void Uqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E200C00; // UQADD V0.8B, V0.8B, V0.8B @@ -3615,9 +3613,9 @@ namespace Ryujinx.Tests.Cpu public void Uqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E200C00; // UQADD V0.16B, V0.16B, V0.16B @@ -3637,9 +3635,9 @@ namespace Ryujinx.Tests.Cpu public void Uqsub_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_1B1H1S1D_))] ulong z, + [ValueSource(nameof(_1B1H1S1D_))] ulong a, + [ValueSource(nameof(_1B1H1S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <b, H, S, D> { uint opcode = 0x7E202C00; // UQSUB B0, B0, B0 @@ -3659,9 +3657,9 @@ namespace Ryujinx.Tests.Cpu public void Uqsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E202C00; // UQSUB V0.8B, V0.8B, V0.8B @@ -3681,9 +3679,9 @@ namespace Ryujinx.Tests.Cpu public void Uqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x6E202C00; // UQSUB V0.16B, V0.16B, V0.16B @@ -3703,9 +3701,9 @@ namespace Ryujinx.Tests.Cpu public void Urhadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x2E201400; // URHADD V0.8B, V0.8B, V0.8B @@ -3725,9 +3723,9 @@ namespace Ryujinx.Tests.Cpu public void Urhadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { uint opcode = 0x6E201400; // URHADD V0.16B, V0.16B, V0.16B @@ -3747,9 +3745,9 @@ namespace Ryujinx.Tests.Cpu public void Usubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { uint opcode = 0x2E202000; // USUBL V0.8H, V0.8B, V0.8B @@ -3769,9 +3767,9 @@ namespace Ryujinx.Tests.Cpu public void Usubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { uint opcode = 0x6E202000; // USUBL2 V0.8H, V0.16B, V0.16B @@ -3791,9 +3789,9 @@ namespace Ryujinx.Tests.Cpu public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x2E203000; // USUBW V0.8H, V0.8H, V0.8B @@ -3813,9 +3811,9 @@ namespace Ryujinx.Tests.Cpu public void Usubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x6E203000; // USUBW2 V0.8H, V0.8H, V0.16B @@ -3835,9 +3833,9 @@ namespace Ryujinx.Tests.Cpu public void Uzp1_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E001800; // UZP1 V0.8B, V0.8B, V0.8B @@ -3857,9 +3855,9 @@ namespace Ryujinx.Tests.Cpu public void Uzp1_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E001800; // UZP1 V0.16B, V0.16B, V0.16B @@ -3879,9 +3877,9 @@ namespace Ryujinx.Tests.Cpu public void Uzp2_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E005800; // UZP2 V0.8B, V0.8B, V0.8B @@ -3901,9 +3899,9 @@ namespace Ryujinx.Tests.Cpu public void Uzp2_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E005800; // UZP2 V0.16B, V0.16B, V0.16B @@ -3923,9 +3921,9 @@ namespace Ryujinx.Tests.Cpu public void Zip1_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E003800; // ZIP1 V0.8B, V0.8B, V0.8B @@ -3945,9 +3943,9 @@ namespace Ryujinx.Tests.Cpu public void Zip1_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E003800; // ZIP1 V0.16B, V0.16B, V0.16B @@ -3967,9 +3965,9 @@ namespace Ryujinx.Tests.Cpu public void Zip2_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S_))] ulong z, + [ValueSource(nameof(_8B4H2S_))] ulong a, + [ValueSource(nameof(_8B4H2S_))] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { uint opcode = 0x0E007800; // ZIP2 V0.8B, V0.8B, V0.8B @@ -3989,9 +3987,9 @@ namespace Ryujinx.Tests.Cpu public void Zip2_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { uint opcode = 0x4E007800; // ZIP2 V0.16B, V0.16B, V0.16B @@ -4008,4 +4006,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs index 93fc658aa..b19137a4b 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _V_Add_Sub_Long_Wide_I_() { - return new uint[] + return new[] { 0xf2800000u, // VADDL.S8 Q0, D0, D0 0xf2800100u, // VADDW.S8 Q0, Q0, D0 @@ -25,7 +25,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vfma_Vfms_Vfnma_Vfnms_S_F32_() { - return new uint[] + return new[] { 0xEEA00A00u, // VFMA. F32 S0, S0, S0 0xEEA00A40u, // VFMS. F32 S0, S0, S0 @@ -36,7 +36,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vfma_Vfms_Vfnma_Vfnms_S_F64_() { - return new uint[] + return new[] { 0xEEA00B00u, // VFMA. F64 D0, D0, D0 0xEEA00B40u, // VFMS. F64 D0, D0, D0 @@ -47,7 +47,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vfma_Vfms_V_F32_() { - return new uint[] + return new[] { 0xF2000C10u, // VFMA.F32 D0, D0, D0 0xF2200C10u // VFMS.F32 D0, D0, D0 @@ -56,7 +56,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vmla_Vmls_Vnmla_Vnmls_S_F32_() { - return new uint[] + return new[] { 0xEE000A00u, // VMLA. F32 S0, S0, S0 0xEE000A40u, // VMLS. F32 S0, S0, S0 @@ -67,7 +67,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vmla_Vmls_Vnmla_Vnmls_S_F64_() { - return new uint[] + return new[] { 0xEE000B00u, // VMLA. F64 D0, D0, D0 0xEE000B40u, // VMLS. F64 D0, D0, D0 @@ -78,7 +78,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vmlal_Vmlsl_V_I_() { - return new uint[] + return new[] { 0xf2800800u, // VMLAL.S8 Q0, D0, D0 0xf2800a00u // VMLSL.S8 Q0, D0, D0 @@ -87,7 +87,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vp_Add_Max_Min_F_() { - return new uint[] + return new[] { 0xf3000d00u, // VPADD.F32 D0, D0, D0 0xf3000f00u, // VPMAX.F32 D0, D0, D0 @@ -97,7 +97,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vp_Add_I_() { - return new uint[] + return new[] { 0xf2000b10u // VPADD.I8 D0, D0, D0 }; @@ -105,7 +105,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _V_Pmax_Pmin_Rhadd_I_() { - return new uint[] + return new[] { 0xf2000a00u, // VPMAX .S8 D0, D0, D0 0xf2000a10u, // VPMIN .S8 D0, D0, D0 @@ -115,7 +115,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vq_Add_Sub_I_() { - return new uint[] + return new[] { 0xf2000050u, // VQADD.S8 Q0, Q0, Q0 0xf2000250u // VQSUB.S8 Q0, Q0, Q0 @@ -126,18 +126,18 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _8B1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B4H2S1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<ulong> _1S_F_() @@ -378,12 +378,12 @@ namespace Ryujinx.Tests.Cpu public void Vadd_F32([Values(0u)] uint rd, [Values(0u, 1u)] uint rn, [Values(0u, 2u)] uint rm, - [ValueSource("_2S_F_")] ulong z0, - [ValueSource("_2S_F_")] ulong z1, - [ValueSource("_2S_F_")] ulong a0, - [ValueSource("_2S_F_")] ulong a1, - [ValueSource("_2S_F_")] ulong b0, - [ValueSource("_2S_F_")] ulong b1, + [ValueSource(nameof(_2S_F_))] ulong z0, + [ValueSource(nameof(_2S_F_))] ulong z1, + [ValueSource(nameof(_2S_F_))] ulong a0, + [ValueSource(nameof(_2S_F_))] ulong a1, + [ValueSource(nameof(_2S_F_))] ulong b0, + [ValueSource(nameof(_2S_F_))] ulong b1, [Values] bool q) { uint opcode = 0xf2000d00u; // VADD.F32 D0, D0, D0 @@ -409,13 +409,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void V_Add_Sub_Long_Wide_I([ValueSource("_V_Add_Sub_Long_Wide_I_")] uint opcode, + public void V_Add_Sub_Long_Wide_I([ValueSource(nameof(_V_Add_Sub_Long_Wide_I_))] uint opcode, [Range(0u, 5u)] uint rd, [Range(0u, 5u)] uint rn, [Range(0u, 5u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0u, 1u, 2u)] uint size, // <SU8, SU16, SU32> [Values] bool u) // <S, U> { @@ -444,8 +444,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VCMP.f<size> Vd, Vm")] public void Vcmp([Values(2u, 3u)] uint size, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_1S_F_")] ulong b, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_1S_F_))] ulong b, [Values] bool e) { uint opcode = 0xeeb40840u; @@ -666,8 +666,8 @@ namespace Ryujinx.Tests.Cpu public void Vmull_I_P8_P64([Values(0u, 1u)] uint rd, [Values(0u, 1u)] uint rn, [Values(0u, 1u)] uint rm, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong d0, - [ValueSource(nameof(_8B1D_))] [Random(RndCnt)] ulong d1, + [ValueSource(nameof(_8B1D_))] ulong d0, + [ValueSource(nameof(_8B1D_))] ulong d1, [Values(0u/*, 2u*/)] uint size) // <P8, P64> { /*if (size == 2u) @@ -734,22 +734,21 @@ namespace Ryujinx.Tests.Cpu [Explicit] [Test, Pairwise] - public void Vp_Add_Max_Min_F([ValueSource("_Vp_Add_Max_Min_F_")] uint opcode, + public void Vp_Add_Max_Min_F([ValueSource(nameof(_Vp_Add_Max_Min_F_))] uint opcode, [Values(0u)] uint rd, [Range(0u, 7u)] uint rn, [Range(0u, 7u)] uint rm, - [ValueSource("_2S_F_")] ulong z0, - [ValueSource("_2S_F_")] ulong z1, - [ValueSource("_2S_F_")] ulong a0, - [ValueSource("_2S_F_")] ulong a1, - [ValueSource("_2S_F_")] ulong b0, - [ValueSource("_2S_F_")] ulong b1) + [ValueSource(nameof(_2S_F_))] ulong z0, + [ValueSource(nameof(_2S_F_))] ulong z1, + [ValueSource(nameof(_2S_F_))] ulong a0, + [ValueSource(nameof(_2S_F_))] ulong a1, + [ValueSource(nameof(_2S_F_))] ulong b0, + [ValueSource(nameof(_2S_F_))] ulong b1) { opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - var rnd = TestContext.CurrentContext.Random; V128 v0 = MakeVectorE0E1(z0, z1); V128 v1 = MakeVectorE0E1(a0, a1); V128 v2 = MakeVectorE0E1(b0, b1); @@ -760,7 +759,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vp_Add_I([ValueSource("_Vp_Add_I_")] uint opcode, + public void Vp_Add_I([ValueSource(nameof(_Vp_Add_I_))] uint opcode, [Values(0u)] uint rd, [Range(0u, 5u)] uint rn, [Range(0u, 5u)] uint rm, @@ -785,7 +784,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void V_Pmax_Pmin_Rhadd_I([ValueSource("_V_Pmax_Pmin_Rhadd_I_")] uint opcode, + public void V_Pmax_Pmin_Rhadd_I([ValueSource(nameof(_V_Pmax_Pmin_Rhadd_I_))] uint opcode, [Values(0u)] uint rd, [Range(0u, 5u)] uint rn, [Range(0u, 5u)] uint rm, @@ -816,13 +815,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vq_Add_Sub_I([ValueSource("_Vq_Add_Sub_I_")] uint opcode, + public void Vq_Add_Sub_I([ValueSource(nameof(_Vq_Add_Sub_I_))] uint opcode, [Range(0u, 5u)] uint rd, [Range(0u, 5u)] uint rn, [Range(0u, 5u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(0u, 1u, 2u)] uint size, // <SU8, SU16, SU32> [Values] bool u) // <S, U> { @@ -854,9 +853,9 @@ namespace Ryujinx.Tests.Cpu public void Vqdmulh_I([Range(0u, 5u)] uint rd, [Range(0u, 5u)] uint rn, [Range(0u, 5u)] uint rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_8B4H2S1D_))] ulong z, + [ValueSource(nameof(_8B4H2S1D_))] ulong a, + [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(1u, 2u)] uint size) // <S16, S32> { rd >>= 1; rd <<= 1; @@ -881,4 +880,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs index 5d0a8f3f9..7f5f6d17c 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs @@ -1,7 +1,6 @@ #define SimdRegElem using ARMeilleure.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu @@ -14,21 +13,21 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } #endregion #region "ValueSource (Opcodes)" private static uint[] _Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_4H_8H_() { - return new uint[] + return new[] { 0x2F400000u, // MLA V0.4H, V0.4H, V0.H[0] 0x2F404000u, // MLS V0.4H, V0.4H, V0.H[0] @@ -40,7 +39,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_2S_4S_() { - return new uint[] + return new[] { 0x2F800000u, // MLA V0.2S, V0.2S, V0.S[0] 0x2F804000u, // MLS V0.2S, V0.2S, V0.S[0] @@ -52,7 +51,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_() { - return new uint[] + return new[] { 0x0F402000u, // SMLAL V0.4S, V0.4H, V0.H[0] 0x0F406000u, // SMLSL V0.4S, V0.4H, V0.H[0] @@ -65,7 +64,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_() { - return new uint[] + return new[] { 0x0F802000u, // SMLAL V0.2D, V0.2S, V0.S[0] 0x0F806000u, // SMLSL V0.2D, V0.2S, V0.S[0] @@ -77,18 +76,16 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; - private const int RndCntIndex = 2; [Test, Pairwise] public void Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_4H_8H([ValueSource(nameof(_Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_4H_8H_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource(nameof(_4H_))] [Random(RndCnt)] ulong z, - [ValueSource(nameof(_4H_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_4H_))] [Random(RndCnt)] ulong b, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [ValueSource(nameof(_4H_))] ulong b, + [Values(0u, 7u)] uint index, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { uint h = (index >> 2) & 1; @@ -110,12 +107,12 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_2S_4S([ValueSource(nameof(_Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource(nameof(_2S_))] [Random(RndCnt)] ulong z, - [ValueSource(nameof(_2S_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [ValueSource(nameof(_2S_))] ulong b, [Values(0u, 1u, 2u, 3u)] uint index, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -136,14 +133,14 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_")] uint opcodes, + public void SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [ValueSource("_4H_")] [Random(RndCnt)] ulong b, - [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [ValueSource(nameof(_4H_))] ulong b, + [Values(0u, 7u)] uint index, [Values(0b0u, 0b1u)] uint q) // <4H4S, 8H4S> { uint h = (index >> 2) & 1; @@ -164,13 +161,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_")] uint opcodes, + public void SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [ValueSource("_2S_")] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [ValueSource(nameof(_2S_))] ulong b, [Values(0u, 1u, 2u, 3u)] uint index, [Values(0b0u, 0b1u)] uint q) // <2S2D, 4S2D> { @@ -191,4 +188,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs b/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs index 38197fd5f..31b236041 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs @@ -1,9 +1,7 @@ #define SimdRegElemF using ARMeilleure.State; - using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -142,7 +140,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _F_Mla_Mls_Se_S_() { - return new uint[] + return new[] { 0x5F821020u, // FMLA S0, S1, V2.S[0] 0x5F825020u // FMLS S0, S1, V2.S[0] @@ -151,7 +149,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mla_Mls_Se_D_() { - return new uint[] + return new[] { 0x5FC21020u, // FMLA D0, D1, V2.D[0] 0x5FC25020u // FMLS D0, D1, V2.D[0] @@ -160,7 +158,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mla_Mls_Ve_2S_4S_() { - return new uint[] + return new[] { 0x0F801000u, // FMLA V0.2S, V0.2S, V0.S[0] 0x0F805000u // FMLS V0.2S, V0.2S, V0.S[0] @@ -169,7 +167,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mla_Mls_Ve_2D_() { - return new uint[] + return new[] { 0x4FC01000u, // FMLA V0.2D, V0.2D, V0.D[0] 0x4FC05000u // FMLS V0.2D, V0.2D, V0.D[0] @@ -178,7 +176,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mul_Mulx_Se_S_() { - return new uint[] + return new[] { 0x5F829020u, // FMUL S0, S1, V2.S[0] 0x7F829020u // FMULX S0, S1, V2.S[0] @@ -187,7 +185,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mul_Mulx_Se_D_() { - return new uint[] + return new[] { 0x5FC29020u, // FMUL D0, D1, V2.D[0] 0x7FC29020u // FMULX D0, D1, V2.D[0] @@ -196,7 +194,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mul_Mulx_Ve_2S_4S_() { - return new uint[] + return new[] { 0x0F809000u, // FMUL V0.2S, V0.2S, V0.S[0] 0x2F809000u // FMULX V0.2S, V0.2S, V0.S[0] @@ -205,7 +203,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Mul_Mulx_Ve_2D_() { - return new uint[] + return new[] { 0x4FC09000u, // FMUL V0.2D, V0.2D, V0.D[0] 0x6FC09000u // FMULX V0.2D, V0.2D, V0.D[0] @@ -220,10 +218,10 @@ namespace Ryujinx.Tests.Cpu private static readonly bool NoNaNs = false; [Test, Pairwise] [Explicit] // Fused. - public void F_Mla_Mls_Se_S([ValueSource("_F_Mla_Mls_Se_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong z, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + public void F_Mla_Mls_Se_S([ValueSource(nameof(_F_Mla_Mls_Se_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong z, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0u, 1u, 2u, 3u)] uint index) { uint h = (index >> 1) & 1; @@ -246,10 +244,10 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Mla_Mls_Se_D([ValueSource("_F_Mla_Mls_Se_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b, + public void F_Mla_Mls_Se_D([ValueSource(nameof(_F_Mla_Mls_Se_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b, [Values(0u, 1u)] uint index) { uint h = index & 1; @@ -271,13 +269,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Mla_Mls_Ve_2S_4S([ValueSource("_F_Mla_Mls_Ve_2S_4S_")] uint opcodes, + public void F_Mla_Mls_Ve_2S_4S([ValueSource(nameof(_F_Mla_Mls_Ve_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0u, 1u, 2u, 3u)] uint index, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -303,13 +301,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] // Fused. - public void F_Mla_Mls_Ve_2D([ValueSource("_F_Mla_Mls_Ve_2D_")] uint opcodes, + public void F_Mla_Mls_Ve_2D([ValueSource(nameof(_F_Mla_Mls_Ve_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b, + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b, [Values(0u, 1u)] uint index) { uint h = index & 1; @@ -332,9 +330,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mul_Mulx_Se_S([ValueSource("_F_Mul_Mulx_Se_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + public void F_Mul_Mulx_Se_S([ValueSource(nameof(_F_Mul_Mulx_Se_S_))] uint opcodes, + [ValueSource(nameof(_1S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0u, 1u, 2u, 3u)] uint index) { uint h = (index >> 1) & 1; @@ -358,9 +356,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mul_Mulx_Se_D([ValueSource("_F_Mul_Mulx_Se_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b, + public void F_Mul_Mulx_Se_D([ValueSource(nameof(_F_Mul_Mulx_Se_D_))] uint opcodes, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b, [Values(0u, 1u)] uint index) { uint h = index & 1; @@ -383,13 +381,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mul_Mulx_Ve_2S_4S([ValueSource("_F_Mul_Mulx_Ve_2S_4S_")] uint opcodes, + public void F_Mul_Mulx_Ve_2S_4S([ValueSource(nameof(_F_Mul_Mulx_Ve_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, - [ValueSource("_2S_F_")] ulong b, + [ValueSource(nameof(_2S_F_))] ulong z, + [ValueSource(nameof(_2S_F_))] ulong a, + [ValueSource(nameof(_2S_F_))] ulong b, [Values(0u, 1u, 2u, 3u)] uint index, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -415,13 +413,13 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Mul_Mulx_Ve_2D([ValueSource("_F_Mul_Mulx_Ve_2D_")] uint opcodes, + public void F_Mul_Mulx_Ve_2D([ValueSource(nameof(_F_Mul_Mulx_Ve_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a, - [ValueSource("_1D_F_")] ulong b, + [ValueSource(nameof(_1D_F_))] ulong z, + [ValueSource(nameof(_1D_F_))] ulong a, + [ValueSource(nameof(_1D_F_))] ulong b, [Values(0u, 1u)] uint index) { uint h = index & 1; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs b/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs index 6c28a92da..4a49814ef 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs @@ -1,9 +1,7 @@ #define SimdShImm using ARMeilleure.State; - using NUnit.Framework; - using System; using System.Collections.Generic; @@ -17,38 +15,38 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _1H_() { - return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul, - 0x0000000000008000ul, 0x000000000000FFFFul }; + return new[] { 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul }; } private static ulong[] _1S_() { - return new ulong[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul }; } private static ulong[] _2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<ulong> _2S_F_W_() @@ -97,10 +95,8 @@ namespace Ryujinx.Tests.Cpu for (int cnt = 1; cnt <= RndCnt; cnt++) { - ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( - (float)((int)TestContext.CurrentContext.Random.NextUInt())); - ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( - (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd1 = (uint)BitConverter.SingleToInt32Bits((int)TestContext.CurrentContext.Random.NextUInt()); + ulong rnd2 = (uint)BitConverter.SingleToInt32Bits(TestContext.CurrentContext.Random.NextUInt()); ulong rnd3 = GenNormalS(); ulong rnd4 = GenSubnormalS(); @@ -160,9 +156,9 @@ namespace Ryujinx.Tests.Cpu for (int cnt = 1; cnt <= RndCnt; cnt++) { ulong rnd1 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((long)TestContext.CurrentContext.Random.NextULong())); + (long)TestContext.CurrentContext.Random.NextULong()); ulong rnd2 = (ulong)BitConverter.DoubleToInt64Bits( - (double)((ulong)TestContext.CurrentContext.Random.NextULong())); + TestContext.CurrentContext.Random.NextULong()); ulong rnd3 = GenNormalD(); ulong rnd4 = GenSubnormalD(); @@ -179,7 +175,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _F_Cvt_Z_SU_V_Fixed_2S_4S_() { - return new uint[] + return new[] { 0x0F20FC00u, // FCVTZS V0.2S, V0.2S, #32 0x2F20FC00u // FCVTZU V0.2S, V0.2S, #32 @@ -188,7 +184,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _F_Cvt_Z_SU_V_Fixed_2D_() { - return new uint[] + return new[] { 0x4F40FC00u, // FCVTZS V0.2D, V0.2D, #64 0x6F40FC00u // FCVTZU V0.2D, V0.2D, #64 @@ -197,7 +193,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_S_Fixed_S_() { - return new uint[] + return new[] { 0x5F20E420u, // SCVTF S0, S1, #32 0x7F20E420u // UCVTF S0, S1, #32 @@ -206,7 +202,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_S_Fixed_D_() { - return new uint[] + return new[] { 0x5F40E420u, // SCVTF D0, D1, #64 0x7F40E420u // UCVTF D0, D1, #64 @@ -215,7 +211,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_V_Fixed_2S_4S_() { - return new uint[] + return new[] { 0x0F20E400u, // SCVTF V0.2S, V0.2S, #32 0x2F20E400u // UCVTF V0.2S, V0.2S, #32 @@ -224,7 +220,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Cvt_F_V_Fixed_2D_() { - return new uint[] + return new[] { 0x4F40E400u, // SCVTF V0.2D, V0.2D, #64 0x6F40E400u // UCVTF V0.2D, V0.2D, #64 @@ -233,7 +229,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Shl_Sli_S_D_() { - return new uint[] + return new[] { 0x5F405400u, // SHL D0, D0, #0 0x7F405400u // SLI D0, D0, #0 @@ -242,7 +238,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Shl_Sli_V_8B_16B_() { - return new uint[] + return new[] { 0x0F085400u, // SHL V0.8B, V0.8B, #0 0x2F085400u // SLI V0.8B, V0.8B, #0 @@ -251,7 +247,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Shl_Sli_V_4H_8H_() { - return new uint[] + return new[] { 0x0F105400u, // SHL V0.4H, V0.4H, #0 0x2F105400u // SLI V0.4H, V0.4H, #0 @@ -260,7 +256,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Shl_Sli_V_2S_4S_() { - return new uint[] + return new[] { 0x0F205400u, // SHL V0.2S, V0.2S, #0 0x2F205400u // SLI V0.2S, V0.2S, #0 @@ -269,7 +265,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Shl_Sli_V_2D_() { - return new uint[] + return new[] { 0x4F405400u, // SHL V0.2D, V0.2D, #0 0x6F405400u // SLI V0.2D, V0.2D, #0 @@ -278,7 +274,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Shll_V_8B8H_16B8H_() { - return new uint[] + return new[] { 0x0F08A400u, // SSHLL V0.8H, V0.8B, #0 0x2F08A400u // USHLL V0.8H, V0.8B, #0 @@ -287,7 +283,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Shll_V_4H4S_8H4S_() { - return new uint[] + return new[] { 0x0F10A400u, // SSHLL V0.4S, V0.4H, #0 0x2F10A400u // USHLL V0.4S, V0.4H, #0 @@ -296,7 +292,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _SU_Shll_V_2S2D_4S2D_() { - return new uint[] + return new[] { 0x0F20A400u, // SSHLL V0.2D, V0.2S, #0 0x2F20A400u // USHLL V0.2D, V0.2S, #0 @@ -305,7 +301,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImm_Sri_S_D_() { - return new uint[] + return new[] { 0x7F404400u, // SRI D0, D0, #64 0x5F402400u, // SRSHR D0, D0, #64 @@ -321,7 +317,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImm_Sri_V_8B_16B_() { - return new uint[] + return new[] { 0x2F084400u, // SRI V0.8B, V0.8B, #8 0x0F082400u, // SRSHR V0.8B, V0.8B, #8 @@ -337,7 +333,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImm_Sri_V_4H_8H_() { - return new uint[] + return new[] { 0x2F104400u, // SRI V0.4H, V0.4H, #16 0x0F102400u, // SRSHR V0.4H, V0.4H, #16 @@ -353,7 +349,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImm_Sri_V_2S_4S_() { - return new uint[] + return new[] { 0x2F204400u, // SRI V0.2S, V0.2S, #32 0x0F202400u, // SRSHR V0.2S, V0.2S, #32 @@ -369,7 +365,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImm_Sri_V_2D_() { - return new uint[] + return new[] { 0x6F404400u, // SRI V0.2D, V0.2D, #64 0x4F402400u, // SRSHR V0.2D, V0.2D, #64 @@ -385,7 +381,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmNarrow_V_8H8B_8H16B_() { - return new uint[] + return new[] { 0x0F088C00u, // RSHRN V0.8B, V0.8H, #8 0x0F088400u // SHRN V0.8B, V0.8H, #8 @@ -394,7 +390,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmNarrow_V_4S4H_4S8H_() { - return new uint[] + return new[] { 0x0F108C00u, // RSHRN V0.4H, V0.4S, #16 0x0F108400u // SHRN V0.4H, V0.4S, #16 @@ -403,7 +399,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmNarrow_V_2D2S_2D4S_() { - return new uint[] + return new[] { 0x0F208C00u, // RSHRN V0.2S, V0.2D, #32 0x0F208400u // SHRN V0.2S, V0.2D, #32 @@ -412,7 +408,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmSaturatingNarrow_S_HB_() { - return new uint[] + return new[] { 0x5F089C00u, // SQRSHRN B0, H0, #8 0x7F089C00u, // UQRSHRN B0, H0, #8 @@ -425,7 +421,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmSaturatingNarrow_S_SH_() { - return new uint[] + return new[] { 0x5F109C00u, // SQRSHRN H0, S0, #16 0x7F109C00u, // UQRSHRN H0, S0, #16 @@ -438,7 +434,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmSaturatingNarrow_S_DS_() { - return new uint[] + return new[] { 0x5F209C00u, // SQRSHRN S0, D0, #32 0x7F209C00u, // UQRSHRN S0, D0, #32 @@ -451,7 +447,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmSaturatingNarrow_V_8H8B_8H16B_() { - return new uint[] + return new[] { 0x0F089C00u, // SQRSHRN V0.8B, V0.8H, #8 0x2F089C00u, // UQRSHRN V0.8B, V0.8H, #8 @@ -464,7 +460,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmSaturatingNarrow_V_4S4H_4S8H_() { - return new uint[] + return new[] { 0x0F109C00u, // SQRSHRN V0.4H, V0.4S, #16 0x2F109C00u, // UQRSHRN V0.4H, V0.4S, #16 @@ -477,7 +473,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ShrImmSaturatingNarrow_V_2D2S_2D4S_() { - return new uint[] + return new[] { 0x0F209C00u, // SQRSHRN V0.2S, V0.2D, #32 0x2F209C00u, // UQRSHRN V0.2S, V0.2D, #32 @@ -490,20 +486,18 @@ namespace Ryujinx.Tests.Cpu #endregion private const int RndCnt = 2; - private const int RndCntFBits = 2; - private const int RndCntShift = 2; private static readonly bool NoZeros = false; private static readonly bool NoInfs = false; private static readonly bool NoNaNs = false; [Test, Pairwise] [Explicit] - public void F_Cvt_Z_SU_V_Fixed_2S_4S([ValueSource("_F_Cvt_Z_SU_V_Fixed_2S_4S_")] uint opcodes, + public void F_Cvt_Z_SU_V_Fixed_2S_4S([ValueSource(nameof(_F_Cvt_Z_SU_V_Fixed_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_W_")] ulong z, - [ValueSource("_2S_F_W_")] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits, + [ValueSource(nameof(_2S_F_W_))] ulong z, + [ValueSource(nameof(_2S_F_W_))] ulong a, + [Values(1u, 32u)] uint fBits, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint immHb = (64 - fBits) & 0x7F; @@ -521,12 +515,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void F_Cvt_Z_SU_V_Fixed_2D([ValueSource("_F_Cvt_Z_SU_V_Fixed_2D_")] uint opcodes, + public void F_Cvt_Z_SU_V_Fixed_2D([ValueSource(nameof(_F_Cvt_Z_SU_V_Fixed_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_X_")] ulong z, - [ValueSource("_1D_F_X_")] ulong a, - [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_1D_F_X_))] ulong z, + [ValueSource(nameof(_1D_F_X_))] ulong a, + [Values(1u, 64u)] uint fBits) { uint immHb = (128 - fBits) & 0x7F; @@ -542,9 +536,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_S_Fixed_S([ValueSource("_SU_Cvt_F_S_Fixed_S_")] uint opcodes, - [ValueSource("_1S_")] [Random(RndCnt)] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + public void SU_Cvt_F_S_Fixed_S([ValueSource(nameof(_SU_Cvt_F_S_Fixed_S_))] uint opcodes, + [ValueSource(nameof(_1S_))] ulong a, + [Values(1u, 32u)] uint fBits) { uint immHb = (64 - fBits) & 0x7F; @@ -560,9 +554,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_S_Fixed_D([ValueSource("_SU_Cvt_F_S_Fixed_D_")] uint opcodes, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + public void SU_Cvt_F_S_Fixed_D([ValueSource(nameof(_SU_Cvt_F_S_Fixed_D_))] uint opcodes, + [ValueSource(nameof(_1D_))] ulong a, + [Values(1u, 64u)] uint fBits) { uint immHb = (128 - fBits) & 0x7F; @@ -578,12 +572,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_V_Fixed_2S_4S([ValueSource("_SU_Cvt_F_V_Fixed_2S_4S_")] uint opcodes, + public void SU_Cvt_F_V_Fixed_2S_4S([ValueSource(nameof(_SU_Cvt_F_V_Fixed_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [Values(1u, 32u)] uint fBits, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint immHb = (64 - fBits) & 0x7F; @@ -601,12 +595,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] [Explicit] - public void SU_Cvt_F_V_Fixed_2D([ValueSource("_SU_Cvt_F_V_Fixed_2D_")] uint opcodes, + public void SU_Cvt_F_V_Fixed_2D([ValueSource(nameof(_SU_Cvt_F_V_Fixed_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(1u, 64u)] uint fBits) { uint immHb = (128 - fBits) & 0x7F; @@ -622,12 +616,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Shl_Sli_S_D([ValueSource("_Shl_Sli_S_D_")] uint opcodes, + public void Shl_Sli_S_D([ValueSource(nameof(_Shl_Sli_S_D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(0u, 63u)] [Random(1u, 62u, RndCntShift)] uint shift) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(0u, 63u)] uint shift) { uint immHb = (64 + shift) & 0x7F; @@ -643,12 +637,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Shl_Sli_V_8B_16B([ValueSource("_Shl_Sli_V_8B_16B_")] uint opcodes, + public void Shl_Sli_V_8B_16B([ValueSource(nameof(_Shl_Sli_V_8B_16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntShift)] uint shift, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 7u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint immHb = (8 + shift) & 0x7F; @@ -666,12 +660,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Shl_Sli_V_4H_8H([ValueSource("_Shl_Sli_V_4H_8H_")] uint opcodes, + public void Shl_Sli_V_4H_8H([ValueSource(nameof(_Shl_Sli_V_4H_8H_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntShift)] uint shift, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 15u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { uint immHb = (16 + shift) & 0x7F; @@ -689,12 +683,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Shl_Sli_V_2S_4S([ValueSource("_Shl_Sli_V_2S_4S_")] uint opcodes, + public void Shl_Sli_V_2S_4S([ValueSource(nameof(_Shl_Sli_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Values(0u, 31u)] [Random(1u, 30u, RndCntShift)] uint shift, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [Values(0u, 31u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint immHb = (32 + shift) & 0x7F; @@ -712,12 +706,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Shl_Sli_V_2D([ValueSource("_Shl_Sli_V_2D_")] uint opcodes, + public void Shl_Sli_V_2D([ValueSource(nameof(_Shl_Sli_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(0u, 63u)] [Random(1u, 62u, RndCntShift)] uint shift) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(0u, 63u)] uint shift) { uint immHb = (64 + shift) & 0x7F; @@ -733,12 +727,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Shll_V_8B8H_16B8H([ValueSource("_SU_Shll_V_8B8H_16B8H_")] uint opcodes, + public void SU_Shll_V_8B8H_16B8H([ValueSource(nameof(_SU_Shll_V_8B8H_16B8H_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(0u, 7u)] [Random(1u, 6u, RndCntShift)] uint shift, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [Values(0u, 7u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8B8H, 16B8H> { uint immHb = (8 + shift) & 0x7F; @@ -756,12 +750,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Shll_V_4H4S_8H4S([ValueSource("_SU_Shll_V_4H4S_8H4S_")] uint opcodes, + public void SU_Shll_V_4H4S_8H4S([ValueSource(nameof(_SU_Shll_V_4H4S_8H4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(0u, 15u)] [Random(1u, 14u, RndCntShift)] uint shift, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [Values(0u, 15u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4H4S, 8H4S> { uint immHb = (16 + shift) & 0x7F; @@ -779,12 +773,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_Shll_V_2S2D_4S2D([ValueSource("_SU_Shll_V_2S2D_4S2D_")] uint opcodes, + public void SU_Shll_V_2S2D_4S2D([ValueSource(nameof(_SU_Shll_V_2S2D_4S2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Values(0u, 31u)] [Random(1u, 30u, RndCntShift)] uint shift, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [Values(0u, 31u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2S2D, 4S2D> { uint immHb = (32 + shift) & 0x7F; @@ -802,12 +796,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImm_Sri_S_D([ValueSource("_ShrImm_Sri_S_D_")] uint opcodes, + public void ShrImm_Sri_S_D([ValueSource(nameof(_ShrImm_Sri_S_D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(1u, 64u)] [Random(2u, 63u, RndCntShift)] uint shift) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(1u, 64u)] uint shift) { uint immHb = (128 - shift) & 0x7F; @@ -823,12 +817,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImm_Sri_V_8B_16B([ValueSource("_ShrImm_Sri_V_8B_16B_")] uint opcodes, + public void ShrImm_Sri_V_8B_16B([ValueSource(nameof(_ShrImm_Sri_V_8B_16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong a, + [Values(1u, 8u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint immHb = (16 - shift) & 0x7F; @@ -846,12 +840,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImm_Sri_V_4H_8H([ValueSource("_ShrImm_Sri_V_4H_8H_")] uint opcodes, + public void ShrImm_Sri_V_4H_8H([ValueSource(nameof(_ShrImm_Sri_V_4H_8H_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [Values(1u, 16u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { uint immHb = (32 - shift) & 0x7F; @@ -869,12 +863,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImm_Sri_V_2S_4S([ValueSource("_ShrImm_Sri_V_2S_4S_")] uint opcodes, + public void ShrImm_Sri_V_2S_4S([ValueSource(nameof(_ShrImm_Sri_V_2S_4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [Values(1u, 32u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint immHb = (64 - shift) & 0x7F; @@ -892,12 +886,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImm_Sri_V_2D([ValueSource("_ShrImm_Sri_V_2D_")] uint opcodes, + public void ShrImm_Sri_V_2D([ValueSource(nameof(_ShrImm_Sri_V_2D_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(1u, 64u)] [Random(2u, 63u, RndCntShift)] uint shift) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(1u, 64u)] uint shift) { uint immHb = (128 - shift) & 0x7F; @@ -913,12 +907,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmNarrow_V_8H8B_8H16B([ValueSource("_ShrImmNarrow_V_8H8B_8H16B_")] uint opcodes, + public void ShrImmNarrow_V_8H8B_8H16B([ValueSource(nameof(_ShrImmNarrow_V_8H8B_8H16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [Values(1u, 8u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8H8B, 8H16B> { uint immHb = (16 - shift) & 0x7F; @@ -936,12 +930,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmNarrow_V_4S4H_4S8H([ValueSource("_ShrImmNarrow_V_4S4H_4S8H_")] uint opcodes, + public void ShrImmNarrow_V_4S4H_4S8H([ValueSource(nameof(_ShrImmNarrow_V_4S4H_4S8H_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [Values(1u, 16u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4S4H, 4S8H> { uint immHb = (32 - shift) & 0x7F; @@ -959,12 +953,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmNarrow_V_2D2S_2D4S([ValueSource("_ShrImmNarrow_V_2D2S_2D4S_")] uint opcodes, + public void ShrImmNarrow_V_2D2S_2D4S([ValueSource(nameof(_ShrImmNarrow_V_2D2S_2D4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift, + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(1u, 32u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2D2S, 2D4S> { uint immHb = (64 - shift) & 0x7F; @@ -982,12 +976,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmSaturatingNarrow_S_HB([ValueSource("_ShrImmSaturatingNarrow_S_HB_")] uint opcodes, + public void ShrImmSaturatingNarrow_S_HB([ValueSource(nameof(_ShrImmSaturatingNarrow_S_HB_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1H_")] [Random(RndCnt)] ulong z, - [ValueSource("_1H_")] [Random(RndCnt)] ulong a, - [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift) + [ValueSource(nameof(_1H_))] ulong z, + [ValueSource(nameof(_1H_))] ulong a, + [Values(1u, 8u)] uint shift) { uint immHb = (16 - shift) & 0x7F; @@ -1003,12 +997,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmSaturatingNarrow_S_SH([ValueSource("_ShrImmSaturatingNarrow_S_SH_")] uint opcodes, + public void ShrImmSaturatingNarrow_S_SH([ValueSource(nameof(_ShrImmSaturatingNarrow_S_SH_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1S_")] [Random(RndCnt)] ulong z, - [ValueSource("_1S_")] [Random(RndCnt)] ulong a, - [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift) + [ValueSource(nameof(_1S_))] ulong z, + [ValueSource(nameof(_1S_))] ulong a, + [Values(1u, 16u)] uint shift) { uint immHb = (32 - shift) & 0x7F; @@ -1024,12 +1018,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmSaturatingNarrow_S_DS([ValueSource("_ShrImmSaturatingNarrow_S_DS_")] uint opcodes, + public void ShrImmSaturatingNarrow_S_DS([ValueSource(nameof(_ShrImmSaturatingNarrow_S_DS_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift) + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(1u, 32u)] uint shift) { uint immHb = (64 - shift) & 0x7F; @@ -1045,12 +1039,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmSaturatingNarrow_V_8H8B_8H16B([ValueSource("_ShrImmSaturatingNarrow_V_8H8B_8H16B_")] uint opcodes, + public void ShrImmSaturatingNarrow_V_8H8B_8H16B([ValueSource(nameof(_ShrImmSaturatingNarrow_V_8H8B_8H16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong a, + [Values(1u, 8u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8H8B, 8H16B> { uint immHb = (16 - shift) & 0x7F; @@ -1068,12 +1062,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmSaturatingNarrow_V_4S4H_4S8H([ValueSource("_ShrImmSaturatingNarrow_V_4S4H_4S8H_")] uint opcodes, + public void ShrImmSaturatingNarrow_V_4S4H_4S8H([ValueSource(nameof(_ShrImmSaturatingNarrow_V_4S4H_4S8H_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong a, + [Values(1u, 16u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4S4H, 4S8H> { uint immHb = (32 - shift) & 0x7F; @@ -1091,12 +1085,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ShrImmSaturatingNarrow_V_2D2S_2D4S([ValueSource("_ShrImmSaturatingNarrow_V_2D2S_2D4S_")] uint opcodes, + public void ShrImmSaturatingNarrow_V_2D2S_2D4S([ValueSource(nameof(_ShrImmSaturatingNarrow_V_2D2S_2D4S_))] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift, + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong a, + [Values(1u, 32u)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2D2S, 2D4S> { uint immHb = (64 - shift) & 0x7F; @@ -1114,4 +1108,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs index 45481f854..e7fad89f9 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs @@ -13,33 +13,30 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _1D_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _2S_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _4H_() { - return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; } private static ulong[] _8B_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } #endregion #region "ValueSource (Opcodes)" private static uint[] _Vshr_Imm_SU8_() { - return new uint[] + return new[] { 0xf2880010u, // VSHR.S8 D0, D0, #8 0xf2880110u, // VSRA.S8 D0, D0, #8 @@ -50,7 +47,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vshr_Imm_SU16_() { - return new uint[] + return new[] { 0xf2900010u, // VSHR.S16 D0, D0, #16 0xf2900110u, // VSRA.S16 D0, D0, #16 @@ -61,7 +58,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vshr_Imm_SU32_() { - return new uint[] + return new[] { 0xf2a00010u, // VSHR.S32 D0, D0, #32 0xf2a00110u, // VSRA.S32 D0, D0, #32 @@ -72,7 +69,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vshr_Imm_SU64_() { - return new uint[] + return new[] { 0xf2800190u, // VSRA.S64 D0, D0, #64 0xf2800290u, // VRSHR.S64 D0, D0, #64 @@ -82,7 +79,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vqshrn_Vqrshrn_Vrshrn_Imm_() { - return new uint[] + return new[] { 0xf2800910u, // VORR.I16 D0, #0 (immediate value changes it into QSHRN) 0xf2800950u, // VORR.I16 Q0, #0 (immediate value changes it into QRSHRN) @@ -92,7 +89,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _Vqshrun_Vqrshrun_Imm_() { - return new uint[] + return new[] { 0xf3800810u, // VMOV.I16 D0, #0x80 (immediate value changes it into QSHRUN) 0xf3800850u // VMOV.I16 Q0, #0x80 (immediate value changes it into QRSHRUN) @@ -104,12 +101,12 @@ namespace Ryujinx.Tests.Cpu private const int RndCntShiftImm = 2; [Test, Pairwise] - public void Vshr_Imm_SU8([ValueSource("_Vshr_Imm_SU8_")] uint opcode, + public void Vshr_Imm_SU8([ValueSource(nameof(_Vshr_Imm_SU8_))] uint opcode, [Range(0u, 3u)] uint rd, [Range(0u, 3u)] uint rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong b, - [Values(1u, 8u)] [Random(2u, 7u, RndCntShiftImm)] uint shiftImm, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong b, + [Values(1u, 8u)] uint shiftImm, [Values] bool u, [Values] bool q) { @@ -119,12 +116,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vshr_Imm_SU16([ValueSource("_Vshr_Imm_SU16_")] uint opcode, + public void Vshr_Imm_SU16([ValueSource(nameof(_Vshr_Imm_SU16_))] uint opcode, [Range(0u, 3u)] uint rd, [Range(0u, 3u)] uint rm, - [ValueSource("_4H_")] [Random(RndCnt)] ulong z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong b, - [Values(1u, 16u)] [Random(2u, 15u, RndCntShiftImm)] uint shiftImm, + [ValueSource(nameof(_4H_))] ulong z, + [ValueSource(nameof(_4H_))] ulong b, + [Values(1u, 16u)] uint shiftImm, [Values] bool u, [Values] bool q) { @@ -134,12 +131,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vshr_Imm_SU32([ValueSource("_Vshr_Imm_SU32_")] uint opcode, + public void Vshr_Imm_SU32([ValueSource(nameof(_Vshr_Imm_SU32_))] uint opcode, [Range(0u, 3u)] uint rd, [Range(0u, 3u)] uint rm, - [ValueSource("_2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong b, - [Values(1u, 32u)] [Random(2u, 31u, RndCntShiftImm)] uint shiftImm, + [ValueSource(nameof(_2S_))] ulong z, + [ValueSource(nameof(_2S_))] ulong b, + [Values(1u, 32u)] uint shiftImm, [Values] bool u, [Values] bool q) { @@ -149,12 +146,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vshr_Imm_SU64([ValueSource("_Vshr_Imm_SU64_")] uint opcode, + public void Vshr_Imm_SU64([ValueSource(nameof(_Vshr_Imm_SU64_))] uint opcode, [Range(0u, 3u)] uint rd, [Range(0u, 3u)] uint rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong b, - [Values(1u, 64u)] [Random(2u, 63u, RndCntShiftImm)] uint shiftImm, + [ValueSource(nameof(_1D_))] ulong z, + [ValueSource(nameof(_1D_))] ulong b, + [Values(1u, 64u)] uint shiftImm, [Values] bool u, [Values] bool q) { @@ -195,7 +192,7 @@ namespace Ryujinx.Tests.Cpu public void Vshl_Imm([Values(0u)] uint rd, [Values(2u, 0u)] uint rm, [Values(0u, 1u, 2u, 3u)] uint size, - [Random(RndCntShiftImm)] [Values(0u)] uint shiftImm, + [Random(RndCntShiftImm)] uint shiftImm, [Random(RndCnt)] ulong z, [Random(RndCnt)] ulong a, [Random(RndCnt)] ulong b, @@ -229,7 +226,7 @@ namespace Ryujinx.Tests.Cpu public void Vshrn_Imm([Values(0u, 1u)] uint rd, [Values(2u, 0u)] uint rm, [Values(0u, 1u, 2u)] uint size, - [Random(RndCntShiftImm)] [Values(0u)] uint shiftImm, + [Random(RndCntShiftImm)] uint shiftImm, [Random(RndCnt)] ulong z, [Random(RndCnt)] ulong a, [Random(RndCnt)] ulong b) @@ -253,11 +250,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vqshrn_Vqrshrn_Vrshrn_Imm([ValueSource("_Vqshrn_Vqrshrn_Vrshrn_Imm_")] uint opcode, + public void Vqshrn_Vqrshrn_Vrshrn_Imm([ValueSource(nameof(_Vqshrn_Vqrshrn_Vrshrn_Imm_))] uint opcode, [Values(0u, 1u)] uint rd, [Values(2u, 0u)] uint rm, [Values(0u, 1u, 2u)] uint size, - [Random(RndCntShiftImm)] [Values(0u)] uint shiftImm, + [Random(RndCntShiftImm)] uint shiftImm, [Random(RndCnt)] ulong z, [Random(RndCnt)] ulong a, [Random(RndCnt)] ulong b, @@ -287,11 +284,11 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Vqshrun_Vqrshrun_Imm([ValueSource("_Vqshrun_Vqrshrun_Imm_")] uint opcode, + public void Vqshrun_Vqrshrun_Imm([ValueSource(nameof(_Vqshrun_Vqrshrun_Imm_))] uint opcode, [Values(0u, 1u)] uint rd, [Values(2u, 0u)] uint rm, [Values(0u, 1u, 2u)] uint size, - [Random(RndCntShiftImm)] [Values(0u)] uint shiftImm, + [Random(RndCntShiftImm)] uint shiftImm, [Random(RndCnt)] ulong z, [Random(RndCnt)] ulong a, [Random(RndCnt)] ulong b) @@ -315,4 +312,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs b/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs index 0daeab618..7bafc195e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs @@ -1,9 +1,7 @@ #define SimdTbl using ARMeilleure.State; - using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -16,17 +14,17 @@ namespace Ryujinx.Tests.Cpu #region "Helper methods" private static ulong GenIdxsForTbls(int regs) { - const byte idxInRngMin = (byte)0; - byte idxInRngMax = (byte)((16 * regs) - 1); - byte idxOutRngMin = (byte) (16 * regs); - const byte idxOutRngMax = (byte)255; + const byte idxInRngMin = 0; + byte idxInRngMax = (byte)((16 * regs) - 1); + byte idxOutRngMin = (byte) (16 * regs); + const byte idxOutRngMax = 255; ulong idxs = 0ul; for (int cnt = 1; cnt <= 8; cnt++) { - ulong idxInRng = (ulong)TestContext.CurrentContext.Random.NextByte(idxInRngMin, idxInRngMax); - ulong idxOutRng = (ulong)TestContext.CurrentContext.Random.NextByte(idxOutRngMin, idxOutRngMax); + ulong idxInRng = TestContext.CurrentContext.Random.NextByte(idxInRngMin, idxInRngMax); + ulong idxOutRng = TestContext.CurrentContext.Random.NextByte(idxOutRngMin, idxOutRngMax); ulong idx = TestContext.CurrentContext.Random.NextBool() ? idxInRng : idxOutRng; @@ -40,8 +38,8 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Types)" private static ulong[] _8B_() { - return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } private static IEnumerable<ulong> _GenIdxsForTbl1_() @@ -100,7 +98,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _SingleRegisterTable_V_8B_16B_() { - return new uint[] + return new[] { 0x0E000000u, // TBL V0.8B, { V0.16B }, V0.8B 0x0E001000u // TBX V0.8B, { V0.16B }, V0.8B @@ -109,7 +107,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _TwoRegisterTable_V_8B_16B_() { - return new uint[] + return new[] { 0x0E002000u, // TBL V0.8B, { V0.16B, V1.16B }, V0.8B 0x0E003000u // TBX V0.8B, { V0.16B, V1.16B }, V0.8B @@ -118,7 +116,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _ThreeRegisterTable_V_8B_16B_() { - return new uint[] + return new[] { 0x0E004000u, // TBL V0.8B, { V0.16B, V1.16B, V2.16B }, V0.8B 0x0E005000u // TBX V0.8B, { V0.16B, V1.16B, V2.16B }, V0.8B @@ -127,7 +125,7 @@ namespace Ryujinx.Tests.Cpu private static uint[] _FourRegisterTable_V_8B_16B_() { - return new uint[] + return new[] { 0x0E006000u, // TBL V0.8B, { V0.16B, V1.16B, V2.16B, V3.16B }, V0.8B 0x0E006000u // TBX V0.8B, { V0.16B, V1.16B, V2.16B, V3.16B }, V0.8B @@ -135,18 +133,16 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCntDest = 2; - private const int RndCntTbls = 2; private const int RndCntIdxs = 2; [Test, Pairwise] - public void SingleRegisterTable_V_8B_16B([ValueSource("_SingleRegisterTable_V_8B_16B_")] uint opcodes, + public void SingleRegisterTable_V_8B_16B([ValueSource(nameof(_SingleRegisterTable_V_8B_16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u)] uint rn, [Values(2u)] uint rm, - [ValueSource("_8B_")] [Random(RndCntDest)] ulong z, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, - [ValueSource("_GenIdxsForTbl1_")] ulong indexes, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong table0, + [ValueSource(nameof(_GenIdxsForTbl1_))] ulong indexes, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -162,14 +158,14 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void TwoRegisterTable_V_8B_16B([ValueSource("_TwoRegisterTable_V_8B_16B_")] uint opcodes, + public void TwoRegisterTable_V_8B_16B([ValueSource(nameof(_TwoRegisterTable_V_8B_16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u)] uint rn, [Values(3u)] uint rm, - [ValueSource("_8B_")] [Random(RndCntDest)] ulong z, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, - [ValueSource("_GenIdxsForTbl2_")] ulong indexes, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong table0, + [ValueSource(nameof(_8B_))] ulong table1, + [ValueSource(nameof(_GenIdxsForTbl2_))] ulong indexes, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -186,14 +182,14 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Mod_TwoRegisterTable_V_8B_16B([ValueSource("_TwoRegisterTable_V_8B_16B_")] uint opcodes, + public void Mod_TwoRegisterTable_V_8B_16B([ValueSource(nameof(_TwoRegisterTable_V_8B_16B_))] uint opcodes, [Values(30u, 1u)] uint rd, [Values(31u)] uint rn, [Values(1u, 30u)] uint rm, - [ValueSource("_8B_")] [Random(RndCntDest)] ulong z, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, - [ValueSource("_GenIdxsForTbl2_")] ulong indexes, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong table0, + [ValueSource(nameof(_8B_))] ulong table1, + [ValueSource(nameof(_GenIdxsForTbl2_))] ulong indexes, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -210,15 +206,15 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void ThreeRegisterTable_V_8B_16B([ValueSource("_ThreeRegisterTable_V_8B_16B_")] uint opcodes, + public void ThreeRegisterTable_V_8B_16B([ValueSource(nameof(_ThreeRegisterTable_V_8B_16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u)] uint rn, [Values(4u)] uint rm, - [ValueSource("_8B_")] [Random(RndCntDest)] ulong z, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, - [ValueSource("_GenIdxsForTbl3_")] ulong indexes, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong table0, + [ValueSource(nameof(_8B_))] ulong table1, + [ValueSource(nameof(_8B_))] ulong table2, + [ValueSource(nameof(_GenIdxsForTbl3_))] ulong indexes, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -236,15 +232,15 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Mod_ThreeRegisterTable_V_8B_16B([ValueSource("_ThreeRegisterTable_V_8B_16B_")] uint opcodes, + public void Mod_ThreeRegisterTable_V_8B_16B([ValueSource(nameof(_ThreeRegisterTable_V_8B_16B_))] uint opcodes, [Values(30u, 2u)] uint rd, [Values(31u)] uint rn, [Values(2u, 30u)] uint rm, - [ValueSource("_8B_")] [Random(RndCntDest)] ulong z, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, - [ValueSource("_GenIdxsForTbl3_")] ulong indexes, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong table0, + [ValueSource(nameof(_8B_))] ulong table1, + [ValueSource(nameof(_8B_))] ulong table2, + [ValueSource(nameof(_GenIdxsForTbl3_))] ulong indexes, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -262,16 +258,16 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void FourRegisterTable_V_8B_16B([ValueSource("_FourRegisterTable_V_8B_16B_")] uint opcodes, + public void FourRegisterTable_V_8B_16B([ValueSource(nameof(_FourRegisterTable_V_8B_16B_))] uint opcodes, [Values(0u)] uint rd, [Values(1u)] uint rn, [Values(5u)] uint rm, - [ValueSource("_8B_")] [Random(RndCntDest)] ulong z, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table3, - [ValueSource("_GenIdxsForTbl4_")] ulong indexes, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong table0, + [ValueSource(nameof(_8B_))] ulong table1, + [ValueSource(nameof(_8B_))] ulong table2, + [ValueSource(nameof(_8B_))] ulong table3, + [ValueSource(nameof(_GenIdxsForTbl4_))] ulong indexes, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -290,16 +286,16 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Mod_FourRegisterTable_V_8B_16B([ValueSource("_FourRegisterTable_V_8B_16B_")] uint opcodes, + public void Mod_FourRegisterTable_V_8B_16B([ValueSource(nameof(_FourRegisterTable_V_8B_16B_))] uint opcodes, [Values(30u, 3u)] uint rd, [Values(31u)] uint rn, [Values(3u, 30u)] uint rm, - [ValueSource("_8B_")] [Random(RndCntDest)] ulong z, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, - [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table3, - [ValueSource("_GenIdxsForTbl4_")] ulong indexes, + [ValueSource(nameof(_8B_))] ulong z, + [ValueSource(nameof(_8B_))] ulong table0, + [ValueSource(nameof(_8B_))] ulong table1, + [ValueSource(nameof(_8B_))] ulong table2, + [ValueSource(nameof(_8B_))] ulong table3, + [ValueSource(nameof(_GenIdxsForTbl4_))] ulong indexes, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -318,4 +314,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSystem.cs b/Ryujinx.Tests/Cpu/CpuTestSystem.cs index 02b1f1bd3..cb8d9ce14 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSystem.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSystem.cs @@ -1,9 +1,7 @@ #define System using ARMeilleure.State; - using NUnit.Framework; - using System.Collections.Generic; namespace Ryujinx.Tests.Cpu @@ -40,7 +38,7 @@ namespace Ryujinx.Tests.Cpu #region "ValueSource (Opcodes)" private static uint[] _MrsMsr_Nzcv_() { - return new uint[] + return new[] { 0xD53B4200u, // MRS X0, NZCV 0xD51B4200u // MSR NZCV, X0 @@ -48,12 +46,10 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; - [Test, Pairwise] public void MrsMsr_Nzcv([ValueSource("_MrsMsr_Nzcv_")] uint opcodes, [Values(0u, 1u, 31u)] uint rt, - [ValueSource("_GenNzcv_")] [Random(RndCnt)] ulong xt) + [ValueSource("_GenNzcv_")] ulong xt) { opcodes |= (rt & 31) << 0; @@ -70,4 +66,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestThumb.cs b/Ryujinx.Tests/Cpu/CpuTestThumb.cs index da7c6e92d..b740f524a 100644 --- a/Ryujinx.Tests/Cpu/CpuTestThumb.cs +++ b/Ryujinx.Tests/Cpu/CpuTestThumb.cs @@ -142,10 +142,10 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetContext().GetX(1), Is.EqualTo(w1 ^ w2)); break; case 2: - Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? 0 : (uint)(w1 << (int)shift))); + Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? 0 : w1 << (int)shift)); break; case 3: - Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? 0 : (uint)(w1 >> (int)shift))); + Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? 0 : w1 >> (int)shift)); break; case 4: Assert.That(GetContext().GetX(1), Is.EqualTo(shift >= 32 ? (uint)((int)w1 >> 31) : (uint)((int)w1 >> (int)shift))); From f1943fd0b65b74f164eec1f47a586a463fd4352a Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Thu, 9 Feb 2023 02:50:18 +0000 Subject: [PATCH 346/737] Log shader compile errors with Warning level (#2617) * Log shader compile errors with Warning level These are infrequent enough that I think it's worth dumping any errors into the log. They also keep causing graphical glitches, and the only indication that anything went wrong is a debug log that is never enabled. * Add maximum length for shader log --- Ryujinx.Graphics.OpenGL/Program.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs index 0cc722e6c..a6009108a 100644 --- a/Ryujinx.Graphics.OpenGL/Program.cs +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.OpenGL { class Program : IProgram { + private const int MaxShaderLogLength = 2048; + public int Handle { get; private set; } public bool IsLinked @@ -115,9 +117,16 @@ namespace Ryujinx.Graphics.OpenGL if (status == 0) { - // Use GL.GetProgramInfoLog(Handle), it may be too long to print on the log. _status = ProgramLinkStatus.Failure; - Logger.Debug?.Print(LogClass.Gpu, "Shader linking failed."); + + string log = GL.GetProgramInfoLog(Handle); + + if (log.Length > MaxShaderLogLength) + { + log = log.Substring(0, MaxShaderLogLength) + "..."; + } + + Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{log}"); } else { From 7bae440d3a5f2ed9ca7f93d8e39d6e2935926d41 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Wed, 8 Feb 2023 22:08:15 -0500 Subject: [PATCH 347/737] `ObjectiveC` Helper Class (#4286) * `NativeMacOS` Helper Class * Corrections * Make CFString IDisposable * Fix `openURL:` * `dealloc` metal layer * Remove releases * Use NSString * Update Ryujinx.Ui.Common/Helper/NativeMacOS.cs Co-authored-by: merry <git@mary.rs> * Programatically select updates in Finder * Address feedback * Feedback * Ptr * Fix whoopsie * Ack suggestions * Update Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * GDK Suggestions --------- Co-authored-by: merry <git@mary.rs> Co-authored-by: gdkchan <gab.dark.100@gmail.com> --- Ryujinx.Ava/UI/Helpers/MetalHelper.cs | 127 ---------------------- Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 28 ++++- Ryujinx.Ui.Common/Helper/ObjectiveC.cs | 97 +++++++++++++++++ Ryujinx.Ui.Common/Helper/OpenHelper.cs | 21 +++- 4 files changed, 141 insertions(+), 132 deletions(-) delete mode 100644 Ryujinx.Ava/UI/Helpers/MetalHelper.cs create mode 100644 Ryujinx.Ui.Common/Helper/ObjectiveC.cs diff --git a/Ryujinx.Ava/UI/Helpers/MetalHelper.cs b/Ryujinx.Ava/UI/Helpers/MetalHelper.cs deleted file mode 100644 index 5eb8660a1..000000000 --- a/Ryujinx.Ava/UI/Helpers/MetalHelper.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Runtime.Versioning; -using System.Runtime.InteropServices; -using Avalonia; - -namespace Ryujinx.Ava.UI.Helpers -{ - public delegate void UpdateBoundsCallbackDelegate(Rect rect); - - [SupportedOSPlatform("macos")] - static partial class MetalHelper - { - private const string LibObjCImport = "/usr/lib/libobjc.A.dylib"; - - private struct Selector - { - public readonly IntPtr NativePtr; - - public unsafe Selector(string value) - { - int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); - byte* data = stackalloc byte[size]; - - fixed (char* pValue = value) - { - System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); - } - - NativePtr = sel_registerName(data); - } - - public static implicit operator Selector(string value) => new Selector(value); - } - - private static unsafe IntPtr GetClass(string value) - { - int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); - byte* data = stackalloc byte[size]; - - fixed (char* pValue = value) - { - System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); - } - - return objc_getClass(data); - } - - private struct NSPoint - { - public double X; - public double Y; - - public NSPoint(double x, double y) - { - X = x; - Y = y; - } - } - - private struct NSRect - { - public NSPoint Pos; - public NSPoint Size; - - public NSRect(double x, double y, double width, double height) - { - Pos = new NSPoint(x, y); - Size = new NSPoint(width, height); - } - } - - public static IntPtr GetMetalLayer(out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds) - { - // Create a new CAMetalLayer. - IntPtr layerClass = GetClass("CAMetalLayer"); - IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc"); - objc_msgSend(metalLayer, "init"); - - // Create a child NSView to render into. - IntPtr nsViewClass = GetClass("NSView"); - IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc"); - objc_msgSend(child, "init", new NSRect(0, 0, 0, 0)); - - // Make its renderer our metal layer. - objc_msgSend(child, "setWantsLayer:", (byte)1); - objc_msgSend(child, "setLayer:", metalLayer); - objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor); - - // Ensure the scale factor is up to date. - updateBounds = (Rect rect) => { - objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor); - }; - - nsView = child; - return metalLayer; - } - - public static void DestroyMetalLayer(IntPtr nsView, IntPtr metalLayer) - { - // TODO - } - - [LibraryImport(LibObjCImport)] - private static unsafe partial IntPtr sel_registerName(byte* data); - - [LibraryImport(LibObjCImport)] - private static unsafe partial IntPtr objc_getClass(byte* data); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); - - [LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")] - private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs index 532f4dc27..a5c8b0031 100644 --- a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs +++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -2,9 +2,9 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Platform; -using Ryujinx.Ava.UI.Helpers; using Ryujinx.Common.Configuration; using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Helper; using SPB.Graphics; using SPB.Platform; using SPB.Platform.GLX; @@ -30,6 +30,7 @@ namespace Ryujinx.Ava.UI.Renderer protected IntPtr NsView { get; set; } protected IntPtr MetalLayer { get; set; } + public delegate void UpdateBoundsCallbackDelegate(Rect rect); private UpdateBoundsCallbackDelegate _updateBoundsCallback; public event EventHandler<IntPtr> WindowCreated; @@ -237,8 +238,29 @@ namespace Ryujinx.Ava.UI.Renderer [SupportedOSPlatform("macos")] IPlatformHandle CreateMacOS() { - MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback); + // Create a new CAMetalLayer. + IntPtr layerClass = ObjectiveC.objc_getClass("CAMetalLayer"); + IntPtr metalLayer = ObjectiveC.IntPtr_objc_msgSend(layerClass, "alloc"); + ObjectiveC.objc_msgSend(metalLayer, "init"); + // Create a child NSView to render into. + IntPtr nsViewClass = ObjectiveC.objc_getClass("NSView"); + IntPtr child = ObjectiveC.IntPtr_objc_msgSend(nsViewClass, "alloc"); + ObjectiveC.objc_msgSend(child, "init", new ObjectiveC.NSRect(0, 0, 0, 0)); + + // Make its renderer our metal layer. + ObjectiveC.objc_msgSend(child, "setWantsLayer:", 1); + ObjectiveC.objc_msgSend(child, "setLayer:", metalLayer); + ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor); + + // Ensure the scale factor is up to date. + _updateBoundsCallback = rect => + { + ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor); + }; + + IntPtr nsView = child; + MetalLayer = metalLayer; NsView = nsView; return new PlatformHandle(nsView, "NSView"); @@ -260,7 +282,7 @@ namespace Ryujinx.Ava.UI.Renderer [SupportedOSPlatform("macos")] void DestroyMacOS() { - MetalHelper.DestroyMetalLayer(NsView, MetalLayer); + // TODO } } } \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Helper/ObjectiveC.cs b/Ryujinx.Ui.Common/Helper/ObjectiveC.cs new file mode 100644 index 000000000..234f7597a --- /dev/null +++ b/Ryujinx.Ui.Common/Helper/ObjectiveC.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Text; + +namespace Ryujinx.Ui.Common.Helper +{ + [SupportedOSPlatform("macos")] + public static partial class ObjectiveC + { + private const string ObjCRuntime = "/usr/lib/libobjc.A.dylib"; + + [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] + private static unsafe partial IntPtr sel_getUid(string name); + + [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] + public static partial IntPtr objc_getClass(string name); + + [LibraryImport(ObjCRuntime)] + public static partial void objc_msgSend(IntPtr receiver, Selector selector); + + [LibraryImport(ObjCRuntime)] + public static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); + + [LibraryImport(ObjCRuntime)] + public static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); + + [LibraryImport(ObjCRuntime)] + public static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); + + [LibraryImport(ObjCRuntime)] + public static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend", StringMarshalling = StringMarshalling.Utf8)] + public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, string param); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool bool_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); + + public struct Selector + { + public readonly IntPtr SelPtr; + + public unsafe Selector(string name) + { + SelPtr = sel_getUid(name); + } + + public static implicit operator Selector(string value) => new(value); + } + + public struct NSString + { + public readonly IntPtr StrPtr; + + public NSString(string aString) + { + IntPtr nsString = objc_getClass("NSString"); + StrPtr = IntPtr_objc_msgSend(nsString, "stringWithUTF8String:", aString); + } + + public static implicit operator IntPtr(NSString nsString) => nsString.StrPtr; + } + + public readonly struct NSPoint + { + public readonly double X; + public readonly double Y; + + public NSPoint(double x, double y) + { + X = x; + Y = y; + } + } + + public readonly struct NSRect + { + public readonly NSPoint Pos; + public readonly NSPoint Size; + + public NSRect(double x, double y, double width, double height) + { + Pos = new NSPoint(x, y); + Size = new NSPoint(width, height); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Helper/OpenHelper.cs b/Ryujinx.Ui.Common/Helper/OpenHelper.cs index 355348921..5b2e86635 100644 --- a/Ryujinx.Ui.Common/Helper/OpenHelper.cs +++ b/Ryujinx.Ui.Common/Helper/OpenHelper.cs @@ -55,7 +55,17 @@ namespace Ryujinx.Ui.Common.Helper } else if (OperatingSystem.IsMacOS()) { - Process.Start("open", $"-R \"{path}\""); + ObjectiveC.NSString nsStringPath = new(path); + IntPtr nsUrl = ObjectiveC.objc_getClass("NSURL"); + var urlPtr = ObjectiveC.IntPtr_objc_msgSend(nsUrl, "fileURLWithPath:", nsStringPath); + + IntPtr nsArray = ObjectiveC.objc_getClass("NSArray"); + IntPtr urlArray = ObjectiveC.IntPtr_objc_msgSend(nsArray, "arrayWithObject:", urlPtr); + + IntPtr nsWorkspace = ObjectiveC.objc_getClass("NSWorkspace"); + IntPtr sharedWorkspace = ObjectiveC.IntPtr_objc_msgSend(nsWorkspace, "sharedWorkspace"); + + ObjectiveC.objc_msgSend(sharedWorkspace, "activateFileViewerSelectingURLs:", urlArray); } else if (OperatingSystem.IsLinux()) { @@ -84,7 +94,14 @@ namespace Ryujinx.Ui.Common.Helper } else if (OperatingSystem.IsMacOS()) { - Process.Start("open", url); + ObjectiveC.NSString nsStringPath = new(url); + IntPtr nsUrl = ObjectiveC.objc_getClass("NSURL"); + var urlPtr = ObjectiveC.IntPtr_objc_msgSend(nsUrl, "URLWithString:", nsStringPath); + + IntPtr nsWorkspace = ObjectiveC.objc_getClass("NSWorkspace"); + IntPtr sharedWorkspace = ObjectiveC.IntPtr_objc_msgSend(nsWorkspace, "sharedWorkspace"); + + ObjectiveC.bool_objc_msgSend(sharedWorkspace, "openURL:", urlPtr); } else { From 5f38086f9494a4ffbcb4b0ce4b7727ad8ac18b3e Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 9 Feb 2023 00:48:25 -0300 Subject: [PATCH 348/737] Fix SPIR-V when all inputs/outputs are indexed (#4389) --- .../CodeGen/Spirv/Declarations.cs | 128 +++++++++--------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index fab1667ce..5108d8619 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -397,6 +397,31 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) { bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing); + + if (iaIndexing && !perPatch) + { + var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); + + if (context.Config.Stage == ShaderStage.Geometry) + { + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices)); + } + + var spvType = context.TypePointer(StorageClass.Input, attrType); + var spvVar = context.Variable(spvType, StorageClass.Input); + + if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) + { + context.Decorate(spvVar, Decoration.PassthroughNV); + } + + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); + + context.AddGlobalVariable(spvVar); + context.InputsArray = spvVar; + } + var inputs = perPatch ? info.InputsPerPatch : info.Inputs; foreach (int attr in inputs) @@ -410,60 +435,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (iaIndexing && isUserAttr && !perPatch) { - if (context.InputsArray == null) - { - var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); - - if (context.Config.Stage == ShaderStage.Geometry) - { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices)); - } - - var spvType = context.TypePointer(StorageClass.Input, attrType); - var spvVar = context.Variable(spvType, StorageClass.Input); - - if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) - { - context.Decorate(spvVar, Decoration.PassthroughNV); - } - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); - - context.AddGlobalVariable(spvVar); - context.InputsArray = spvVar; - } + continue; } - else + + PixelImap iq = PixelImap.Unused; + + if (context.Config.Stage == ShaderStage.Fragment) { - PixelImap iq = PixelImap.Unused; - - if (context.Config.Stage == ShaderStage.Fragment) + if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) { - if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) - { - iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); - } - else - { - AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false); - AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask; + iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); + } + else + { + AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false); + AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask; - if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32)) - { - iq = PixelImap.Constant; - } + if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32)) + { + iq = PixelImap.Constant; } } - - DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq); } + + DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq); } } private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) { bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing); + + if (oaIndexing && !perPatch) + { + var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); + + if (context.Config.Stage == ShaderStage.TessellationControl) + { + attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + } + + var spvType = context.TypePointer(StorageClass.Output, attrType); + var spvVar = context.Variable(spvType, StorageClass.Output); + + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); + + context.AddGlobalVariable(spvVar); + context.OutputsArray = spvVar; + } + var outputs = perPatch ? info.OutputsPerPatch : info.Outputs; foreach (int attr in outputs) @@ -477,29 +498,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (oaIndexing && isUserAttr && !perPatch) { - if (context.OutputsArray == null) - { - var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); - - if (context.Config.Stage == ShaderStage.TessellationControl) - { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); - } - - var spvType = context.TypePointer(StorageClass.Output, attrType); - var spvVar = context.Variable(spvType, StorageClass.Output); - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); - - context.AddGlobalVariable(spvVar); - context.OutputsArray = spvVar; - } - } - else - { - DeclareOutputAttribute(context, attr, perPatch); + continue; } + + DeclareOutputAttribute(context, attr, perPatch); } if (context.Config.Stage == ShaderStage.Vertex) From 61b1ce252f11e8f8e31080faee60d0a9d99cb67f Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 10 Feb 2023 11:47:59 -0300 Subject: [PATCH 349/737] Allow partially mapped textures with unmapped start (#4394) --- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 7 +++++ Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs | 32 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 27bec786f..f18de6075 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -474,6 +474,13 @@ namespace Ryujinx.Graphics.Gpu.Image { address = memoryManager.Translate(info.GpuAddress); + // If the start address is unmapped, let's try to find a page of memory that is mapped. + if (address == MemoryManager.PteUnmapped) + { + address = memoryManager.TranslateFirstMapped(info.GpuAddress, (ulong)info.CalculateSizeInfo(layerSize).TotalSize); + } + + // If address is still invalid, the texture is fully unmapped, so it has no data, just return null. if (address == MemoryManager.PteUnmapped) { return null; diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 0ac6160d9..b0f7e7992 100644 --- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -583,6 +583,38 @@ namespace Ryujinx.Graphics.Gpu.Memory return UnpackPaFromPte(pte) + (va & PageMask); } + /// <summary> + /// Translates a GPU virtual address to a CPU virtual address on the first mapped page of memory + /// on the specified region. + /// If no page is mapped on the specified region, <see cref="PteUnmapped"/> is returned. + /// </summary> + /// <param name="va">GPU virtual address to be translated</param> + /// <param name="size">Size of the range to be translated</param> + /// <returns>CPU virtual address, or <see cref="PteUnmapped"/> if unmapped</returns> + public ulong TranslateFirstMapped(ulong va, ulong size) + { + if (!ValidateAddress(va)) + { + return PteUnmapped; + } + + ulong endVa = va + size; + + ulong pte = GetPte(va); + + for (; va < endVa && pte == PteUnmapped; va += PageSize - (va & PageMask)) + { + pte = GetPte(va); + } + + if (pte == PteUnmapped) + { + return PteUnmapped; + } + + return UnpackPaFromPte(pte) + (va & PageMask); + } + /// <summary> /// Gets the kind of a given memory page. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling. From 1dcd44b94fcba10e9f78f8352557d46ea84b80ab Mon Sep 17 00:00:00 2001 From: Logan Stromberg <loganstromberg@gmail.com> Date: Fri, 10 Feb 2023 07:37:20 -0800 Subject: [PATCH 350/737] Treat NpadIdType < 0 as invalid. Filter invalid SupportedPlayers inside IHidServer.SetSupportedNpadIdType(). (#4377) Co-authored-by: Logan Stromberg <lostromb@microsoft.com> --- Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs | 4 +++- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs b/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs index 65a69bb77..b98f60658 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs @@ -38,7 +38,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer public static bool IsValidNpadIdType(NpadIdType npadIdType) { - return npadIdType <= NpadIdType.Player8 || npadIdType == NpadIdType.Handheld || npadIdType == NpadIdType.Unknown; + return (npadIdType >= NpadIdType.Player1 && npadIdType <= NpadIdType.Player8) || + npadIdType == NpadIdType.Handheld || + npadIdType == NpadIdType.Unknown; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index d347a3bde..266fc04fb 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -722,7 +722,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid for (int i = 0; i < supportedPlayerIds.Length; ++i) { - if (supportedPlayerIds[i] >= 0) + if (HidUtils.IsValidNpadIdType(supportedPlayerIds[i])) { context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i])); } @@ -1101,7 +1101,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey) { - if (npadIdType >= (NpadIdType.Player8 + 1) && npadIdType != NpadIdType.Handheld && npadIdType != NpadIdType.Unknown) + if (!HidUtils.IsValidNpadIdType(npadIdType)) { return ResultCode.InvalidNpadIdType; } From e4f68592c3a6e51414e5f78eef096f21bf735eb1 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sun, 12 Feb 2023 09:30:26 +0000 Subject: [PATCH 351/737] Fix partial updates for textures. (#4401) I was forcing some types of texture to partially update when investigating performance with games that stream in data, and noticed that partially loading texture data was really broken on both backends. Fixes Vulkan texture set by getting the correct expected size for the texture. Fixes partial upload on both backends for both Texture 2D Array and Cubemap using the wrong offset and uploading to the first layer/level for a handle. 3D might also be affected. This might fix textures randomly having incorrect data in games that render to it - jumbled in the case of OpenGL, and outdated/black in the case of Vulkan. This case typically happens in UE4 games. --- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 9 ++++----- Ryujinx.Graphics.Vulkan/TextureView.cs | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 942fa2f87..1040b394a 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -336,24 +336,23 @@ namespace Ryujinx.Graphics.Gpu.Image if (_loadNeeded[baseHandle + i]) { var info = GetHandleInformation(baseHandle + i); - int offsetIndex = info.Index; // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views. for (int layer = 0; layer < info.Layers; layer++) { for (int level = 0; level < info.Levels; level++) { + int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level); + int offset = _allOffsets[offsetIndex]; int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size); int size = endOffset - offset; ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size)); - SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true); + SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); - Storage.SetData(result, info.BaseLayer, info.BaseLevel); - - offsetIndex++; + Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level); } } } diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index d60ce39b0..aa050c015 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -712,7 +712,7 @@ namespace Ryujinx.Graphics.Vulkan for (int level = 0; level < levels; level++) { - int mipSize = GetBufferDataLength(Info.GetMipSize(dstLevel + level)); + int mipSize = GetBufferDataLength(Info.GetMipSize2D(dstLevel + level) * dstLayers); int endOffset = offset + mipSize; From 052b23c83c3a58afdd63c3d5a7655be482f1c739 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 13 Feb 2023 21:32:20 +0100 Subject: [PATCH 352/737] vulkan: Do not call vkCmdSetViewport when viewportCount is 0 (#4406) This fix validation error "VUID-vkCmdSetViewport-viewportCount-arraylength". --- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 8 +++----- Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 43dccf86e..02b1c3896 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -650,9 +650,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.DepthWriteEnable = oldDepthWriteEnable; _newState.Topology = oldTopology; - DynamicState.Viewports = oldViewports; - DynamicState.ViewportsCount = (int)oldViewportsCount; - DynamicState.SetViewportsDirty(); + DynamicState.SetViewports(ref oldViewports, oldViewportsCount); _newState.ViewportsCount = oldViewportsCount; SignalStateChange(); @@ -1183,6 +1181,8 @@ namespace Ryujinx.Graphics.Vulkan return Math.Clamp(value, 0f, 1f); } + DynamicState.ViewportsCount = (uint)count; + for (int i = 0; i < count; i++) { var viewport = viewports[i]; @@ -1196,8 +1196,6 @@ namespace Ryujinx.Graphics.Vulkan Clamp(viewport.DepthFar))); } - DynamicState.ViewportsCount = count; - float disableTransformF = disableTransform ? 1.0f : 0.0f; if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform) { diff --git a/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index b4d6e95c7..42ea022a4 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan private Array4<float> _blendConstants; - public int ViewportsCount; + public uint ViewportsCount; public Array16<Viewport> Viewports; private enum DirtyFlags @@ -88,9 +88,15 @@ namespace Ryujinx.Graphics.Vulkan _dirty |= DirtyFlags.Viewport; } - public void SetViewportsDirty() + public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount) { - _dirty |= DirtyFlags.Viewport; + Viewports = viewports; + ViewportsCount = viewportsCount; + + if (ViewportsCount != 0) + { + _dirty |= DirtyFlags.Viewport; + } } public void ForceAllDirty() @@ -155,7 +161,10 @@ namespace Ryujinx.Graphics.Vulkan private void RecordViewport(Vk api, CommandBuffer commandBuffer) { - api.CmdSetViewport(commandBuffer, 0, (uint)ViewportsCount, Viewports.AsSpan()); + if (ViewportsCount != 0) + { + api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan()); + } } } } From fe9c49949a1329bc964ab10ff2a97abd5507ef6a Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 13 Feb 2023 23:04:55 +0100 Subject: [PATCH 353/737] vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level (#4408) * vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level This ensure we don't end up trying to initialize with anything currently incompatible. * Address riperiperi's comment --- .../VulkanInitialization.cs | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index ab5a0acfb..b8b48f6ca 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -14,6 +14,9 @@ namespace Ryujinx.Graphics.Vulkan public unsafe static class VulkanInitialization { private const uint InvalidIndex = uint.MaxValue; + private static uint MinimalVulkanVersion = Vk.Version11.Value; + private static uint MinimalInstanceVulkanVersion = Vk.Version12.Value; + private static uint MaximumVulkanVersion = Vk.Version12.Value; private const string AppName = "Ryujinx.Graphics.Vulkan"; private const int QueuesCount = 2; @@ -99,7 +102,7 @@ namespace Ryujinx.Graphics.Vulkan ApplicationVersion = 1, PEngineName = (byte*)appName, EngineVersion = 1, - ApiVersion = Vk.Version12.Value + ApiVersion = MaximumVulkanVersion }; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; @@ -224,7 +227,7 @@ namespace Ryujinx.Graphics.Vulkan ApplicationVersion = 1, PEngineName = (byte*)appName, EngineVersion = 1, - ApiVersion = Vk.Version12.Value + ApiVersion = MaximumVulkanVersion }; var instanceCreateInfo = new InstanceCreateInfo @@ -239,6 +242,27 @@ namespace Ryujinx.Graphics.Vulkan api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); + // We ensure that vkEnumerateInstanceVersion is present (added in 1.1). + // If the instance doesn't support it, no device is going to be 1.1 compatible. + if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero) + { + api.DestroyInstance(instance, null); + + return Array.Empty<DeviceInfo>(); + } + + // We currently assume that the instance is compatible with Vulkan 1.2 + // TODO: Remove this once we relax our initialization codepaths. + uint instanceApiVerison = 0; + api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError(); + + if (instanceApiVerison < MinimalInstanceVulkanVersion) + { + api.DestroyInstance(instance, null); + + return Array.Empty<DeviceInfo>(); + } + Marshal.FreeHGlobal(appName); uint physicalDeviceCount; @@ -259,6 +283,11 @@ namespace Ryujinx.Graphics.Vulkan var physicalDevice = physicalDevices[i]; api.GetPhysicalDeviceProperties(physicalDevice, out var properties); + if (properties.ApiVersion < MinimalVulkanVersion) + { + continue; + } + devices[i] = new DeviceInfo( StringFromIdPair(properties.VendorID, properties.DeviceID), VendorUtils.GetNameFromId(properties.VendorID), From ed7a0474c6b126d885e6689abc46264100ec8de0 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Tue, 14 Feb 2023 15:58:57 +0100 Subject: [PATCH 354/737] Infra: Issues template cleanup (#4421) * Infra: Issues template cleanup * applied --- .github/ISSUE_TEMPLATE/bug_report.yml | 34 +++++++------------ .github/ISSUE_TEMPLATE/feature_request.yml | 6 ++-- .../missing_cpu_instruction.yml | 2 +- .../ISSUE_TEMPLATE/missing_service_call.yml | 2 +- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f65f32540..68be1f5e0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,26 +1,27 @@ name: Bug Report description: File a bug report -title: "[Bug] <title>" +title: "[Bug]" labels: bug body: - type: textarea id: issue attributes: - label: Description of Issue + label: Description of the issue description: What's the issue you encountered? validations: required: true - type: textarea id: repro attributes: - label: Reproduction Steps + label: Reproduction steps description: How can the issue be reproduced? + placeholder: Describe each step as precisely as possible validations: required: true - type: textarea id: log attributes: - label: Log File + label: Log file description: A log file will help our developers to better diagnose and fix the issue. placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area validations: @@ -29,55 +30,44 @@ body: id: os attributes: label: OS - placeholder: "Example: Windows 10" + placeholder: "e.g. Windows 10" validations: required: true - type: input id: ryujinx-version attributes: label: Ryujinx version - placeholder: | - - *(e.g. 1.0.470)* + placeholder: "e.g. 1.0.470" validations: required: true - type: input id: game-version attributes: label: Game version - placeholder: | - - *(e.g. 1.1.1)* + placeholder: "e.g. 1.1.1" validations: required: false - type: input id: cpu attributes: label: CPU - placeholder: | - - *(e.g. i7-6700)* + placeholder: "e.g. i7-6700" validations: required: false - type: input id: gpu attributes: label: GPU - placeholder: | - - *(e.g. NVIDIA RTX 2070)* + placeholder: "e.g. NVIDIA RTX 2070" validations: required: false - type: input id: ram attributes: label: RAM - placeholder: | - - *(e.g. 16GB)* + placeholder: "e.g. 16GB" validations: required: false - - type: checkboxes - attributes: - label: Applied Mods? - options: - - label: "Yes" - required: false - type: textarea id: mods attributes: @@ -93,4 +83,4 @@ body: - Additional info about your environment: - Any other information relevant to your issue. validations: - required: false + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index a9a5b504a..383bbb151 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,6 +1,6 @@ name: Feature Request description: Suggest a new feature for Ryujinx. -title: "[Feature Request] <title>" +title: "[Feature Request]" body: - type: textarea id: overview @@ -12,14 +12,14 @@ body: - type: textarea id: details attributes: - label: Smaller Details + label: Smaller details description: These may include specific methods of implementation etc. validations: required: true - type: textarea id: request attributes: - label: Nature of Request + label: Nature of request validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml b/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml index 10e3bad37..d815ddfd9 100644 --- a/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml +++ b/.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml @@ -1,6 +1,6 @@ name: Missing CPU Instruction description: CPU Instruction is missing in Ryujinx. -title: "[CPU] <title>" +title: "[CPU]" labels: [cpu, not-implemented] body: - type: textarea diff --git a/.github/ISSUE_TEMPLATE/missing_service_call.yml b/.github/ISSUE_TEMPLATE/missing_service_call.yml index 48d46d57f..80aae533b 100644 --- a/.github/ISSUE_TEMPLATE/missing_service_call.yml +++ b/.github/ISSUE_TEMPLATE/missing_service_call.yml @@ -5,7 +5,7 @@ body: - type: textarea id: instruction attributes: - label: Service Call + label: Service call description: What service call is missing? validations: required: true From 32450d45de7889318e0f289fc52b3fffc62edf60 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 15 Feb 2023 07:50:26 +0100 Subject: [PATCH 355/737] vulkan: Clean up MemoryAllocator (#4418) This started as an attempt to remove vkGetPhysicalDeviceMemoryProperties in FindSuitableMemoryTypeIndex (As this could have some overhead and shouldn't change at runtime) and turned in a little bigger cleanup. --- Ryujinx.Graphics.Vulkan/BufferManager.cs | 6 ++--- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 26 ++++++++++------------ Ryujinx.Graphics.Vulkan/TextureStorage.cs | 5 ++--- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 6 ++--- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index 9c50e6ff3..49fdd75d6 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -39,7 +39,6 @@ namespace Ryujinx.Graphics.Vulkan BufferUsageFlags.VertexBufferBit | BufferUsageFlags.TransformFeedbackBufferBitExt; - private readonly PhysicalDevice _physicalDevice; private readonly Device _device; private readonly IdList<BufferHolder> _buffers; @@ -48,9 +47,8 @@ namespace Ryujinx.Graphics.Vulkan public StagingBuffer StagingBuffer { get; } - public BufferManager(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device) + public BufferManager(VulkanRenderer gd, Device device) { - _physicalDevice = physicalDevice; _device = device; _buffers = new IdList<BufferHolder>(); StagingBuffer = new StagingBuffer(gd, this); @@ -114,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan allocateFlagsAlt = DefaultBufferMemoryAltFlags; } - var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); + var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt); if (allocation.Memory.Handle == 0UL) { diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 83c0a3243..e4dcd916e 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -9,34 +9,36 @@ namespace Ryujinx.Graphics.Vulkan private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private readonly Vk _api; + private readonly PhysicalDevice _physicalDevice; private readonly Device _device; private readonly List<MemoryAllocatorBlockList> _blockLists; + private readonly int _blockAlignment; + private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties; - private int _blockAlignment; - - public MemoryAllocator(Vk api, Device device, uint maxMemoryAllocationCount) + public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount) { _api = api; + _physicalDevice = physicalDevice; _device = device; _blockLists = new List<MemoryAllocatorBlockList>(); _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount); + + _api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties); } public MemoryAllocation AllocateDeviceMemory( - PhysicalDevice physicalDevice, MemoryRequirements requirements, MemoryPropertyFlags flags = 0) { - return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); + return AllocateDeviceMemory(requirements, flags, flags); } public MemoryAllocation AllocateDeviceMemory( - PhysicalDevice physicalDevice, MemoryRequirements requirements, MemoryPropertyFlags flags, MemoryPropertyFlags alternativeFlags) { - int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); + int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags); if (memoryTypeIndex < 0) { return default; @@ -65,20 +67,16 @@ namespace Ryujinx.Graphics.Vulkan return newBl.Allocate(size, alignment, map); } - private static int FindSuitableMemoryTypeIndex( - Vk api, - PhysicalDevice physicalDevice, + private int FindSuitableMemoryTypeIndex( uint memoryTypeBits, MemoryPropertyFlags flags, MemoryPropertyFlags alternativeFlags) { int bestCandidateIndex = -1; - api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); - - for (int i = 0; i < properties.MemoryTypeCount; i++) + for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++) { - var type = properties.MemoryTypes[i]; + var type = _physicalDeviceMemoryProperties.MemoryTypes[i]; if ((memoryTypeBits & (1 << i)) != 0) { diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 92209997d..03a47a091 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -55,7 +55,6 @@ namespace Ryujinx.Graphics.Vulkan public unsafe TextureStorage( VulkanRenderer gd, - PhysicalDevice physicalDevice, Device device, TextureCreateInfo info, float scaleFactor, @@ -118,7 +117,7 @@ namespace Ryujinx.Graphics.Vulkan if (foreignAllocation == null) { gd.Api.GetImageMemoryRequirements(device, _image, out var requirements); - var allocation = gd.MemoryAllocator.AllocateDeviceMemory(physicalDevice, requirements, DefaultImageMemoryFlags); + var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, DefaultImageMemoryFlags); if (allocation.Memory.Handle == 0UL) { @@ -173,7 +172,7 @@ namespace Ryujinx.Graphics.Vulkan var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel); - storage = new TextureStorage(_gd, default, _device, info, ScaleFactor, _allocationAuto); + storage = new TextureStorage(_gd, _device, info, ScaleFactor, _allocationAuto); _aliasedStorages.Add(format, storage); } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 92dec7a1a..595e033cb 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -280,7 +280,7 @@ namespace Ryujinx.Graphics.Vulkan supportedSampleCounts, portabilityFlags); - MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount); + MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); @@ -290,7 +290,7 @@ namespace Ryujinx.Graphics.Vulkan BackgroundResources = new BackgroundResources(this, _device); - BufferManager = new BufferManager(this, _physicalDevice, _device); + BufferManager = new BufferManager(this, _device); _syncManager = new SyncManager(this, _device); _pipeline = new PipelineFull(this, _device); @@ -388,7 +388,7 @@ namespace Ryujinx.Graphics.Vulkan internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale) { - return new TextureStorage(this, _physicalDevice, _device, info, scale); + return new TextureStorage(this, _device, info, scale); } public void DeleteBuffer(BufferHandle buffer) From 17078ad929f9942d2b03ede00b30867aeab924de Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 15 Feb 2023 09:41:48 +0100 Subject: [PATCH 356/737] vulkan: Respect VK_KHR_portability_subset vertex stride alignment (#4419) * vulkan: Respect VK_KHR_portability_subset vertex stride alignment We were hardcoding alignment to 4, but by specs it can be any values that is a power of 2. This also enable VK_KHR_portability_subset if present as per specs requirements. * address gdkchan's comment * Make NeedsVertexBufferAlignment internal --- Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs | 14 ++++++++------ Ryujinx.Graphics.Vulkan/PipelineBase.cs | 5 +++-- Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 5 +++-- Ryujinx.Graphics.Vulkan/VulkanInitialization.cs | 3 ++- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 13 ++++++++----- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 82fcaea10..1ed2b0ccc 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -8,11 +8,10 @@ namespace Ryujinx.Graphics.Vulkan { None = 0, - VertexBufferAlignment4B = 1, - NoTriangleFans = 1 << 1, - NoPointMode = 1 << 2, - No3DImageView = 1 << 3, - NoLodBias = 1 << 4 + NoTriangleFans = 1, + NoPointMode = 1 << 1, + No3DImageView = 1 << 2, + NoLodBias = 1 << 3 } readonly struct HardwareCapabilities @@ -40,6 +39,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly ShaderStageFlags RequiredSubgroupSizeStages; public readonly SampleCountFlags SupportedSampleCounts; public readonly PortabilitySubsetFlags PortabilitySubset; + public readonly uint VertexBufferAlignment; public HardwareCapabilities( bool supportsIndexTypeUint8, @@ -64,7 +64,8 @@ namespace Ryujinx.Graphics.Vulkan uint maxSubgroupSize, ShaderStageFlags requiredSubgroupSizeStages, SampleCountFlags supportedSampleCounts, - PortabilitySubsetFlags portabilitySubset) + PortabilitySubsetFlags portabilitySubset, + uint vertexBufferAlignment) { SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsCustomBorderColor = supportsCustomBorderColor; @@ -89,6 +90,7 @@ namespace Ryujinx.Graphics.Vulkan RequiredSubgroupSizeStages = requiredSubgroupSizeStages; SupportedSampleCounts = supportedSampleCounts; PortabilitySubset = portabilitySubset; + VertexBufferAlignment = vertexBufferAlignment; } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 02b1c3896..8ed39ee26 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1,4 +1,5 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; @@ -1136,7 +1137,7 @@ namespace Ryujinx.Graphics.Vulkan buffer.Dispose(); - if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) && + if (Gd.Capabilities.VertexBufferAlignment < 2 && (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) { buffer = new VertexBufferState( diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 5c9193fa6..da480d9f5 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -1,4 +1,5 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; @@ -253,7 +254,7 @@ namespace Ryujinx.Graphics.Vulkan if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment)) { - alignedStride = (vertexBuffer.Stride + (alignment - 1)) & -alignment; + alignedStride = BitUtils.AlignUp(vertexBuffer.Stride, alignment); } // TODO: Support divisor > 1 diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index b8b48f6ca..4401f032d 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -36,7 +36,8 @@ namespace Ryujinx.Graphics.Vulkan "VK_KHR_shader_float16_int8", "VK_EXT_shader_subgroup_ballot", "VK_EXT_subgroup_size_control", - "VK_NV_geometry_shader_passthrough" + "VK_NV_geometry_shader_passthrough", + "VK_KHR_portability_subset", // By spec, we should enable this if present. }; public static string[] RequiredExtensions { get; } = new string[] diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 595e033cb..a7b4b41a7 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -234,10 +234,12 @@ namespace Ryujinx.Graphics.Vulkan Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); var portabilityFlags = PortabilitySubsetFlags.None; + uint vertexBufferAlignment = 1; if (usePortability) { - portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0; + vertexBufferAlignment = propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment; + portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans; portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode; portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView; @@ -278,7 +280,8 @@ namespace Ryujinx.Graphics.Vulkan propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, supportedSampleCounts, - portabilityFlags); + portabilityFlags, + vertexBufferAlignment); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); @@ -636,11 +639,11 @@ namespace Ryujinx.Graphics.Vulkan PrintGpuInformation(); } - public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) + internal bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) { - if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B)) + if (Capabilities.VertexBufferAlignment > 1) { - alignment = 4; + alignment = (int)Capabilities.VertexBufferAlignment; return true; } From a5a9b9bc8b64184cd4c342dea39fed5c2c058a72 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 15 Feb 2023 23:36:35 +0100 Subject: [PATCH 357/737] GUI: Small Updater refactor & Set correct permissions on Linux when extracting files (#4315) * ava: Refactor Updater.cs Fix typos Remove unused usings Rename variables to follow naming scheme * ava: Set file permissions when extracting update files * gtk: Apply the same refactor to Updater.cs * updater: Replace assert with if statement * updater: Remove await usings again --- Ryujinx.Ava/Modules/Updater/Updater.cs | 71 ++--- Ryujinx/Modules/Updater/Updater.cs | 372 ++++++++++++------------- 2 files changed, 203 insertions(+), 240 deletions(-) diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index 62dc17729..b476bb85b 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -132,8 +132,8 @@ namespace Ryujinx.Modules } } - // If build not done, assume no new update are availaible. - if (_buildUrl == null) + // If build not done, assume no new update are available. + if (_buildUrl is null) { if (showVersionUpToDate) { @@ -240,13 +240,13 @@ namespace Ryujinx.Modules { HttpClient result = new(); - // Required by GitHub to interract with APIs. + // Required by GitHub to interact with APIs. result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); return result; } - public static async void UpdateRyujinx(Window parent, string downloadUrl) + private static async void UpdateRyujinx(Window parent, string downloadUrl) { _updateSuccessful = false; @@ -300,8 +300,6 @@ namespace Ryujinx.Modules ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); } - SetFileExecutable(ryuExe); - Process.Start(ryuExe, CommandLineState.Arguments); Environment.Exit(0); @@ -408,9 +406,9 @@ namespace Ryujinx.Modules Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - for (int j = 0; j < webClients.Count; j++) + foreach (WebClient webClient in webClients) { - webClients[j].CancelAsync(); + webClient.CancelAsync(); } DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); @@ -472,22 +470,6 @@ namespace Ryujinx.Modules worker.Start(); } - private static void SetFileExecutable(string path) - { - const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute | - UnixFileMode.UserWrite | - UnixFileMode.UserRead | - UnixFileMode.GroupRead | - UnixFileMode.GroupWrite | - UnixFileMode.OtherRead | - UnixFileMode.OtherWrite; - - if (!OperatingSystem.IsWindows() && File.Exists(path)) - { - File.SetUnixFileMode(path, ExecutableFileMode); - } - } - private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) { // Extract Update @@ -503,27 +485,30 @@ namespace Ryujinx.Modules await Task.Run(() => { TarEntry tarEntry; - while ((tarEntry = tarStream.GetNextEntry()) != null) + + if (!OperatingSystem.IsWindows()) { - if (tarEntry.IsDirectory) continue; - - string outPath = Path.Combine(UpdateDir, tarEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using (FileStream outStream = File.OpenWrite(outPath)) + while ((tarEntry = tarStream.GetNextEntry()) is not null) { - tarStream.CopyEntryContents(outStream); + if (tarEntry.IsDirectory) continue; + + string outPath = Path.Combine(UpdateDir, tarEntry.Name); + + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + + using (FileStream outStream = File.OpenWrite(outPath)) + { + tarStream.CopyEntryContents(outStream); + } + + File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); + + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal); + }); } - - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); - - TarEntry entry = tarEntry; - - Dispatcher.UIThread.Post(() => - { - taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal); - }); } }); @@ -603,8 +588,6 @@ namespace Ryujinx.Modules Directory.Delete(UpdateDir, true); - SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx")); - _updateSuccessful = true; taskDialog.Hide(); diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs index 2a25e78f5..5ad5924e8 100644 --- a/Ryujinx/Modules/Updater/Updater.cs +++ b/Ryujinx/Modules/Updater/Updater.cs @@ -9,6 +9,7 @@ using Ryujinx.Ui; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -23,20 +24,20 @@ namespace Ryujinx.Modules { public static class Updater { + private const string GitHubApiURL = "https://api.github.com"; + private const int ConnectionCount = 4; + internal static bool Running; private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); - private static readonly int ConnectionCount = 4; private static string _buildVer; private static string _platformExt; private static string _buildUrl; private static long _buildSize; - private const string GitHubApiURL = "https://api.github.com"; - // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; @@ -44,7 +45,7 @@ namespace Ryujinx.Modules { HttpClient result = new HttpClient(); - // Required by GitHub to interract with APIs. + // Required by GitHub to interact with APIs. result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); return result; @@ -101,50 +102,48 @@ namespace Ryujinx.Modules // Get latest version number from GitHub API try { - using (HttpClient jsonClient = ConstructHttpClient()) + using HttpClient jsonClient = ConstructHttpClient(); + string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + + // Fetch latest build information + string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); + JObject jsonRoot = JObject.Parse(fetchedJson); + JToken assets = jsonRoot["assets"]; + + _buildVer = (string)jsonRoot["name"]; + + foreach (JToken asset in assets) { - string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + string assetName = (string)asset["name"]; + string assetState = (string)asset["state"]; + string downloadURL = (string)asset["browser_download_url"]; - // Fetch latest build information - string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - JObject jsonRoot = JObject.Parse(fetchedJson); - JToken assets = jsonRoot["assets"]; - - _buildVer = (string)jsonRoot["name"]; - - foreach (JToken asset in assets) + if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) { - string assetName = (string)asset["name"]; - string assetState = (string)asset["state"]; - string downloadURL = (string)asset["browser_download_url"]; + _buildUrl = downloadURL; - if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) + if (assetState != "uploaded") { - _buildUrl = downloadURL; - - if (assetState != "uploaded") + if (showVersionUpToDate) { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; + GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } - break; + return; } - } - if (_buildUrl == null) + break; + } + } + + if (_buildUrl == null) + { + if (showVersionUpToDate) { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; + GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } + + return; } } catch (Exception exception) @@ -247,160 +246,142 @@ namespace Ryujinx.Modules for (int i = 0; i < ConnectionCount; i++) { - list.Add(new byte[0]); + list.Add(Array.Empty<byte>()); } for (int i = 0; i < ConnectionCount; i++) { #pragma warning disable SYSLIB0014 // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. - using (WebClient client = new WebClient()) + using WebClient client = new WebClient(); #pragma warning restore SYSLIB0014 + webClients.Add(client); + + if (i == ConnectionCount - 1) { - webClients.Add(client); + client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); + } + else + { + client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); + } - if (i == ConnectionCount - 1) + client.DownloadProgressChanged += (_, args) => + { + int index = (int)args.UserState; + + Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); + Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); + Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); + + updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; + }; + + client.DownloadDataCompleted += (_, args) => + { + int index = (int)args.UserState; + + if (args.Cancelled) { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); - } - else - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); - } - - client.DownloadProgressChanged += (_, args) => - { - int index = (int)args.UserState; - - Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); - Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); - Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - - updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; - }; - - client.DownloadDataCompleted += (_, args) => - { - int index = (int)args.UserState; - - if (args.Cancelled) - { - webClients[index].Dispose(); - - return; - } - - list[index] = args.Result; - Interlocked.Increment(ref completedRequests); - - if (Equals(completedRequests, ConnectionCount)) - { - byte[] mergedFileBytes = new byte[_buildSize]; - for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) - { - Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); - destinationOffset += list[connectionIndex].Length; - } - - File.WriteAllBytes(updateFile, mergedFileBytes); - - try - { - InstallUpdate(updateDialog, updateFile); - } - catch (Exception e) - { - Logger.Warning?.Print(LogClass.Application, e.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - - return; - } - } - }; - - try - { - client.DownloadDataAsync(new Uri(downloadUrl), i); - } - catch (WebException ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - for (int j = 0; j < webClients.Count; j++) - { - webClients[j].CancelAsync(); - } - - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + webClients[index].Dispose(); return; } + + list[index] = args.Result; + Interlocked.Increment(ref completedRequests); + + if (Equals(completedRequests, ConnectionCount)) + { + byte[] mergedFileBytes = new byte[_buildSize]; + for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) + { + Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); + destinationOffset += list[connectionIndex].Length; + } + + File.WriteAllBytes(updateFile, mergedFileBytes); + + try + { + InstallUpdate(updateDialog, updateFile); + } + catch (Exception e) + { + Logger.Warning?.Print(LogClass.Application, e.Message); + Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); + + DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + + return; + } + } + }; + + try + { + client.DownloadDataAsync(new Uri(downloadUrl), i); + } + catch (WebException ex) + { + Logger.Warning?.Print(LogClass.Application, ex.Message); + Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); + + foreach (WebClient webClient in webClients) + { + webClient.CancelAsync(); + } + + DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + + return; } } } private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) { - using (HttpClient client = new HttpClient()) + using HttpClient client = new HttpClient(); + // We do not want to timeout while downloading + client.Timeout = TimeSpan.FromDays(1); + + using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) + using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) { - // We do not want to timeout while downloading - client.Timeout = TimeSpan.FromDays(1); - - using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) - using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) + using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) { - using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) + long totalBytes = response.Content.Headers.ContentLength.Value; + long byteWritten = 0; + + byte[] buffer = new byte[32 * 1024]; + + while (true) { - long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; + int readSize = remoteFileStream.Read(buffer); - byte[] buffer = new byte[32 * 1024]; - - while (true) + if (readSize == 0) { - int readSize = remoteFileStream.Read(buffer); - - if (readSize == 0) - { - break; - } - - byteWritten += readSize; - - updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; - updateFileStream.Write(buffer, 0, readSize); + break; } + + byteWritten += readSize; + + updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; + updateFileStream.Write(buffer, 0, readSize); } } - - InstallUpdate(updateDialog, updateFile); } + + InstallUpdate(updateDialog, updateFile); } private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) { - Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)); - worker.Name = "Updater.SingleThreadWorker"; - worker.Start(); - } - - private static void SetFileExecutable(string path) - { - const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute | - UnixFileMode.UserWrite | - UnixFileMode.UserRead | - UnixFileMode.GroupRead | - UnixFileMode.GroupWrite | - UnixFileMode.OtherRead | - UnixFileMode.OtherWrite; - - if (!OperatingSystem.IsWindows() && File.Exists(path)) + Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)) { - File.SetUnixFileMode(path, ExecutableFileMode); - } + Name = "Updater.SingleThreadWorker" + }; + worker.Start(); } private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) @@ -411,15 +392,17 @@ namespace Ryujinx.Modules if (OperatingSystem.IsLinux()) { - using (Stream inStream = File.OpenRead(updateFile)) - using (Stream gzipStream = new GZipInputStream(inStream)) - using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII)) - { - updateDialog.ProgressBar.MaxValue = inStream.Length; + using Stream inStream = File.OpenRead(updateFile); + using Stream gzipStream = new GZipInputStream(inStream); + using TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII); + updateDialog.ProgressBar.MaxValue = inStream.Length; - await Task.Run(() => + await Task.Run(() => + { + TarEntry tarEntry; + + if (!OperatingSystem.IsWindows()) { - TarEntry tarEntry; while ((tarEntry = tarStream.GetNextEntry()) != null) { if (tarEntry.IsDirectory) continue; @@ -433,6 +416,7 @@ namespace Ryujinx.Modules tarStream.CopyEntryContents(outStream); } + File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); TarEntry entry = tarEntry; @@ -442,43 +426,41 @@ namespace Ryujinx.Modules updateDialog.ProgressBar.Value += entry.Size; }); } - }); + } + }); - updateDialog.ProgressBar.Value = inStream.Length; - } + updateDialog.ProgressBar.Value = inStream.Length; } else { - using (Stream inStream = File.OpenRead(updateFile)) - using (ZipFile zipFile = new ZipFile(inStream)) + using Stream inStream = File.OpenRead(updateFile); + using ZipFile zipFile = new ZipFile(inStream); + updateDialog.ProgressBar.MaxValue = zipFile.Count; + + await Task.Run(() => { - updateDialog.ProgressBar.MaxValue = zipFile.Count; - - await Task.Run(() => + foreach (ZipEntry zipEntry in zipFile) { - foreach (ZipEntry zipEntry in zipFile) + if (zipEntry.IsDirectory) continue; + + string outPath = Path.Combine(UpdateDir, zipEntry.Name); + + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + + using (Stream zipStream = zipFile.GetInputStream(zipEntry)) + using (FileStream outStream = File.OpenWrite(outPath)) { - if (zipEntry.IsDirectory) continue; - - string outPath = Path.Combine(UpdateDir, zipEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using (Stream zipStream = zipFile.GetInputStream(zipEntry)) - using (FileStream outStream = File.OpenWrite(outPath)) - { - zipStream.CopyTo(outStream); - } - - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); - - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value++; - }); + zipStream.CopyTo(outStream); } - }); - } + + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); + + Application.Invoke(delegate + { + updateDialog.ProgressBar.Value++; + }); + } + }); } // Delete downloaded zip @@ -522,8 +504,6 @@ namespace Ryujinx.Modules Directory.Delete(UpdateDir, true); - SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx")); - updateDialog.MainText.Text = "Update Complete!"; updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; updateDialog.Modal = true; @@ -640,4 +620,4 @@ namespace Ryujinx.Modules } } } -} +} \ No newline at end of file From a707842e14dde468781270198ae63757ca3c2716 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 16 Feb 2023 11:16:31 -0300 Subject: [PATCH 358/737] Validate dimensions before creating texture (#4430) --- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index f18de6075..261d06038 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -477,7 +477,23 @@ namespace Ryujinx.Graphics.Gpu.Image // If the start address is unmapped, let's try to find a page of memory that is mapped. if (address == MemoryManager.PteUnmapped) { - address = memoryManager.TranslateFirstMapped(info.GpuAddress, (ulong)info.CalculateSizeInfo(layerSize).TotalSize); + // Make sure that the dimensions are valid before calculating the texture size. + if (info.Width < 1 || info.Height < 1 || info.Levels < 1) + { + return null; + } + + if ((info.Target == Target.Texture3D || + info.Target == Target.Texture2DArray || + info.Target == Target.Texture2DMultisampleArray || + info.Target == Target.CubemapArray) && info.DepthOrLayers < 1) + { + return null; + } + + ulong dataSize = (ulong)info.CalculateSizeInfo(layerSize).TotalSize; + + address = memoryManager.TranslateFirstMapped(info.GpuAddress, dataSize); } // If address is still invalid, the texture is fully unmapped, so it has no data, just return null. From efb135b74c9c0ff1de2dfd7d2a431bd23185ca66 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 16 Feb 2023 18:28:49 -0300 Subject: [PATCH 359/737] Clear CPU side data on GPU buffer clears (#4125) * Clear CPU side data on GPU buffer clears * Implement tracked fill operation that can signal other resource types except buffer * Fix tests, add missing XML doc * PR feedback --- ARMeilleure/Memory/IMemoryManager.cs | 3 +- ARMeilleure/Signal/NativeSignalHandler.cs | 2 +- Ryujinx.Cpu/AppleHv/HvMemoryManager.cs | 20 +++++----- Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs | 9 +++-- Ryujinx.Cpu/Jit/MemoryManager.cs | 18 ++++----- Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs | 20 +++++----- Ryujinx.Cpu/MemoryEhMeilleure.cs | 2 +- Ryujinx.Graphics.Gpu/Image/Pool.cs | 2 +- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 4 +- Ryujinx.Graphics.Gpu/Memory/BufferCache.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 37 ++++++++++++++---- Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs | 13 +++++++ .../MockVirtualMemoryManager.cs | 2 +- .../MultiRegionTrackingTests.cs | 16 ++++---- Ryujinx.Memory.Tests/TrackingTests.cs | 24 ++++++------ Ryujinx.Memory/AddressSpaceManager.cs | 2 +- Ryujinx.Memory/IVirtualMemoryManager.cs | 3 +- Ryujinx.Memory/Tracking/AbstractRegion.cs | 8 ++-- Ryujinx.Memory/Tracking/MemoryTracking.cs | 38 +++++++++++-------- Ryujinx.Memory/Tracking/MultiRegionHandle.cs | 14 +++++-- Ryujinx.Memory/Tracking/RegionHandle.cs | 24 ++++++++++-- .../Tracking/SmartMultiRegionHandle.cs | 12 +++--- Ryujinx.Memory/Tracking/VirtualRegion.cs | 16 ++++++-- Ryujinx.Tests/Memory/MockMemoryManager.cs | 2 +- 25 files changed, 188 insertions(+), 107 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/ARMeilleure/Memory/IMemoryManager.cs index c4ea70d17..5eb1fadd6 100644 --- a/ARMeilleure/Memory/IMemoryManager.cs +++ b/ARMeilleure/Memory/IMemoryManager.cs @@ -71,6 +71,7 @@ namespace ARMeilleure.Memory /// <param name="size">Size of the region</param> /// <param name="write">True if the region was written, false if read</param> /// <param name="precise">True if the access is precise, false otherwise</param> - void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); + /// <param name="exemptId">Optional ID of the handles that should not be signalled</param> + void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null); } } \ No newline at end of file diff --git a/ARMeilleure/Signal/NativeSignalHandler.cs b/ARMeilleure/Signal/NativeSignalHandler.cs index 77eabe267..cddeb8174 100644 --- a/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/ARMeilleure/Signal/NativeSignalHandler.cs @@ -222,7 +222,7 @@ namespace ARMeilleure.Signal // Tracking action should be non-null to call it, otherwise assume false return. context.BranchIfFalse(skipActionLabel, trackingActionPtr); - Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite, Const(0)); + Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite); context.Copy(inRegionLocal, result); context.MarkLabel(skipActionLabel); diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs index 222dcae1b..437e02aea 100644 --- a/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs +++ b/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -634,13 +634,13 @@ namespace Ryujinx.Cpu.AppleHv /// <remarks> /// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// </remarks> - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { AssertValidAddressAndSize(va, size); if (precise) { - Tracking.VirtualMemoryEvent(va, size, write, precise: true); + Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId); return; } @@ -663,7 +663,7 @@ namespace Ryujinx.Cpu.AppleHv if (state >= tag) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); return; } else if (state == 0) @@ -706,7 +706,7 @@ namespace Ryujinx.Cpu.AppleHv // Only trigger tracking from reads if both bits are set on any page. if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); break; } } @@ -822,21 +822,21 @@ namespace Ryujinx.Cpu.AppleHv } /// <inheritdoc/> - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size)); + return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); } /// <inheritdoc/> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); + return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); } /// <inheritdoc/> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); + return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); } /// <summary> diff --git a/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs b/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs index 8004d39bc..92d3c76ca 100644 --- a/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs +++ b/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs @@ -28,8 +28,9 @@ namespace Ryujinx.Cpu /// </summary> /// <param name="address">CPU virtual address of the region</param> /// <param name="size">Size of the region</param> + /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - CpuRegionHandle BeginTracking(ulong address, ulong size); + CpuRegionHandle BeginTracking(ulong address, ulong size, int id); /// <summary> /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. @@ -38,8 +39,9 @@ namespace Ryujinx.Cpu /// <param name="size">Size of the region</param> /// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param> /// <param name="granularity">Desired granularity of write tracking</param> + /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity); + CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id); /// <summary> /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. @@ -47,7 +49,8 @@ namespace Ryujinx.Cpu /// <param name="address">CPU virtual address of the region</param> /// <param name="size">Size of the region</param> /// <param name="granularity">Desired granularity of write tracking</param> + /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity); + CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id); } } diff --git a/Ryujinx.Cpu/Jit/MemoryManager.cs b/Ryujinx.Cpu/Jit/MemoryManager.cs index 014d843b5..8542d53e2 100644 --- a/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -629,31 +629,31 @@ namespace Ryujinx.Cpu.Jit } /// <inheritdoc/> - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size)); + return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); } /// <inheritdoc/> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); + return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); } /// <inheritdoc/> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); + return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); } /// <inheritdoc/> - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { AssertValidAddressAndSize(va, size); if (precise) { - Tracking.VirtualMemoryEvent(va, size, write, precise: true); + Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId); return; } @@ -676,7 +676,7 @@ namespace Ryujinx.Cpu.Jit if ((pte & tag) != 0) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); break; } diff --git a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 856b6b9b0..090740abe 100644 --- a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -518,13 +518,13 @@ namespace Ryujinx.Cpu.Jit /// <remarks> /// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// </remarks> - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { AssertValidAddressAndSize(va, size); if (precise) { - Tracking.VirtualMemoryEvent(va, size, write, precise: true); + Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId); return; } @@ -547,7 +547,7 @@ namespace Ryujinx.Cpu.Jit if (state >= tag) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); return; } else if (state == 0) @@ -590,7 +590,7 @@ namespace Ryujinx.Cpu.Jit // Only trigger tracking from reads if both bits are set on any page. if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); break; } } @@ -706,21 +706,21 @@ namespace Ryujinx.Cpu.Jit } /// <inheritdoc/> - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size)); + return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); } /// <inheritdoc/> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); + return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); } /// <inheritdoc/> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); + return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); } /// <summary> diff --git a/Ryujinx.Cpu/MemoryEhMeilleure.cs b/Ryujinx.Cpu/MemoryEhMeilleure.cs index 806ef8113..0b434ea74 100644 --- a/Ryujinx.Cpu/MemoryEhMeilleure.cs +++ b/Ryujinx.Cpu/MemoryEhMeilleure.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Cpu { public class MemoryEhMeilleure : IDisposable { - private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write, bool precise = false); + private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write); private readonly MemoryTracking _tracking; private readonly TrackingEventDelegate _trackingEvent; diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs index ee4c051f4..3e557c0bd 100644 --- a/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Image Address = address; Size = size; - _memoryTracking = physicalMemory.BeginGranularTracking(address, size); + _memoryTracking = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Pool); _memoryTracking.RegisterPreciseAction(address, size, PreciseAction); _modifiedDelegate = RegionModified; } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 1040b394a..12a640e15 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -854,7 +854,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>A CpuRegionHandle covering the given range</returns> private CpuRegionHandle GenerateHandle(ulong address, ulong size) { - return _physicalMemory.BeginTracking(address, size); + return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index a624386ed..3778cd824 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -105,13 +105,13 @@ namespace Ryujinx.Graphics.Gpu.Memory if (_useGranular) { - _memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles); + _memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Buffer, baseHandles); _memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction); } else { - _memoryTracking = physicalMemory.BeginTracking(address, size); + _memoryTracking = physicalMemory.BeginTracking(address, size, ResourceKind.Buffer); if (baseHandles != null) { diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 00f590831..a5a9b75e9 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value); - buffer.SignalModified(address, size); + memoryManager.Physical.FillTrackedResource(address, size, value, ResourceKind.Buffer); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index c1fc0c5cd..bd33383e5 100644 --- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -7,6 +7,7 @@ using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Graphics.Gpu.Memory @@ -295,23 +296,41 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// <summary> + /// Fills the specified memory region with a 32-bit integer value. + /// </summary> + /// <param name="address">CPU virtual address of the region</param> + /// <param name="size">Size of the region</param> + /// <param name="value">Value to fill the region with</param> + /// <param name="kind">Kind of the resource being filled, which will not be signalled as CPU modified</param> + public void FillTrackedResource(ulong address, ulong size, uint value, ResourceKind kind) + { + _cpuMemory.SignalMemoryTracking(address, size, write: true, precise: true, (int)kind); + + using WritableRegion region = _cpuMemory.GetWritableRegion(address, (int)size); + + MemoryMarshal.Cast<byte, uint>(region.Memory.Span).Fill(value); + } + /// <summary> /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// </summary> /// <param name="address">CPU virtual address of the region</param> /// <param name="size">Size of the region</param> + /// <param name="kind">Kind of the resource being tracked</param> /// <returns>The memory tracking handle</returns> - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind) { - return _cpuMemory.BeginTracking(address, size); + return _cpuMemory.BeginTracking(address, size, (int)kind); } /// <summary> /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// </summary> /// <param name="range">Ranges of physical memory where the data is located</param> + /// <param name="kind">Kind of the resource being tracked</param> /// <returns>The memory tracking handle</returns> - public GpuRegionHandle BeginTracking(MultiRange range) + public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind) { var cpuRegionHandles = new CpuRegionHandle[range.Count]; int count = 0; @@ -321,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Memory var currentRange = range.GetSubRange(i); if (currentRange.Address != MemoryManager.PteUnmapped) { - cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size); + cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size, (int)kind); } } @@ -338,12 +357,13 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </summary> /// <param name="address">CPU virtual address of the region</param> /// <param name="size">Size of the region</param> + /// <param name="kind">Kind of the resource being tracked</param> /// <param name="handles">Handles to inherit state from or reuse</param> /// <param name="granularity">Desired granularity of write tracking</param> /// <returns>The memory tracking handle</returns> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096) { - return _cpuMemory.BeginGranularTracking(address, size, handles, granularity); + return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind); } /// <summary> @@ -351,11 +371,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </summary> /// <param name="address">CPU virtual address of the region</param> /// <param name="size">Size of the region</param> + /// <param name="kind">Kind of the resource being tracked</param> /// <param name="granularity">Desired granularity of write tracking</param> /// <returns>The memory tracking handle</returns> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity = 4096) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096) { - return _cpuMemory.BeginSmartGranularTracking(address, size, granularity); + return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs b/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs new file mode 100644 index 000000000..55d697b81 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + /// <summary> + /// Kind of a GPU resource. + /// </summary> + enum ResourceKind + { + None, + Buffer, + Texture, + Pool + } +} diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 06eb4729e..6729f4a36 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -96,7 +96,7 @@ namespace Ryujinx.Memory.Tests throw new NotImplementedException(); } - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { throw new NotImplementedException(); } diff --git a/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs b/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs index c607464d2..38cb49216 100644 --- a/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs +++ b/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs @@ -34,8 +34,8 @@ namespace Ryujinx.Memory.Tests private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity) { return smart ? - _tracking.BeginSmartGranularTracking(address, size, granularity) : - (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity); + _tracking.BeginSmartGranularTracking(address, size, granularity, 0) : + (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity, 0); } private void RandomOrder(Random random, List<int> indices, Action<int> action) @@ -216,7 +216,7 @@ namespace Ryujinx.Memory.Tests { int region = regionSizes[i]; handle.QueryModified(address, (ulong)(PageSize * region), (address, size) => { }); - + // There should be a gap between regions, // So that they don't combine and we can see the full effects. address += (ulong)(PageSize * (region + 1)); @@ -294,7 +294,7 @@ namespace Ryujinx.Memory.Tests bool[] actionsTriggered = new bool[3]; - MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); + MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0); PreparePages(granular, 3, PageSize * 3); // Write to the second handle in the multiregion. @@ -307,7 +307,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < 3; i++) { - singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize); + singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize, 0); singlePages[i].Reprotect(); } @@ -321,7 +321,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < 3; i++) { - doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2); + doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2, 0); doublePages[i].Reprotect(); } @@ -340,7 +340,7 @@ namespace Ryujinx.Memory.Tests doublePages }; - MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize); + MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize, 0); bool[] expectedDirty = new bool[] { @@ -405,7 +405,7 @@ namespace Ryujinx.Memory.Tests { bool actionTriggered = false; - MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); + MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0); PreparePages(granular, 3, PageSize * 3); // Add a precise action to the second and third handle in the multiregion. diff --git a/Ryujinx.Memory.Tests/TrackingTests.cs b/Ryujinx.Memory.Tests/TrackingTests.cs index b0c39ab04..eb679804c 100644 --- a/Ryujinx.Memory.Tests/TrackingTests.cs +++ b/Ryujinx.Memory.Tests/TrackingTests.cs @@ -44,7 +44,7 @@ namespace Ryujinx.Memory.Tests [Test] public void SingleRegion() { - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); (ulong address, ulong size)? readTrackingTriggered = null; handle.RegisterAction((address, size) => { @@ -97,7 +97,7 @@ namespace Ryujinx.Memory.Tests [Test] public void OverlappingRegions() { - RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16); + RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16, 0); allHandle.Reprotect(); (ulong address, ulong size)? readTrackingTriggeredAll = null; @@ -116,7 +116,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < 16; i++) { - containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); + containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0); containedHandles[i].Reprotect(); } @@ -163,7 +163,7 @@ namespace Ryujinx.Memory.Tests ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize; ulong alignedSize = alignedEnd - alignedStart; - RegionHandle handle = _tracking.BeginTracking(address, size); + RegionHandle handle = _tracking.BeginTracking(address, size, 0); // Anywhere inside the pages the region is contained on should trigger. @@ -207,7 +207,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < handles.Length; i++) { - handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); + handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0); handles[i].Reprotect(); } @@ -263,7 +263,7 @@ namespace Ryujinx.Memory.Tests Random random = new Random(randSeed + 512); while (Stopwatch.GetTimestamp() < finishedTime) { - RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536)); + RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536), 0); handle.Dispose(); @@ -295,7 +295,7 @@ namespace Ryujinx.Memory.Tests // Read actions should only be triggered once for each registration. // The implementation should use an interlocked exchange to make sure other threads can't get the action. - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); int triggeredCount = 0; int registeredCount = 0; @@ -359,7 +359,7 @@ namespace Ryujinx.Memory.Tests { // Ensure that disposed handles correctly remove their virtual and physical regions. - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); handle.Reprotect(); Assert.AreEqual(1, _tracking.GetRegionCount()); @@ -372,8 +372,8 @@ namespace Ryujinx.Memory.Tests // We expect there to be three regions after creating both, one for the small region and two covering the big one around it. // Regions are always split to avoid overlapping, which is why there are three instead of two. - RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize); - RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4); + RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize, 0); + RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4, 0); Assert.AreEqual(3, _tracking.GetRegionCount()); @@ -398,7 +398,7 @@ namespace Ryujinx.Memory.Tests protection = newProtection; }; - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); // After creating the handle, there is no protection yet. Assert.AreEqual(MemoryPermission.ReadAndWrite, protection); @@ -453,7 +453,7 @@ namespace Ryujinx.Memory.Tests [Test] public void PreciseAction() { - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); (ulong address, ulong size, bool write)? preciseTriggered = null; handle.RegisterPreciseAction((address, size, write) => diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs index b532ce5e0..ac89fca6d 100644 --- a/Ryujinx.Memory/AddressSpaceManager.cs +++ b/Ryujinx.Memory/AddressSpaceManager.cs @@ -462,7 +462,7 @@ namespace Ryujinx.Memory } /// <inheritdoc/> - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { // Only the ARM Memory Manager has tracking for now. } diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs index 390371ad2..e1851d48b 100644 --- a/Ryujinx.Memory/IVirtualMemoryManager.cs +++ b/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -175,7 +175,8 @@ namespace Ryujinx.Memory /// <param name="size">Size of the region</param> /// <param name="write">True if the region was written, false if read</param> /// <param name="precise">True if the access is precise, false otherwise</param> - void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); + /// <param name="exemptId">Optional ID of the handles that should not be signalled</param> + void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null); /// <summary> /// Reprotect a region of virtual memory for tracking. diff --git a/Ryujinx.Memory/Tracking/AbstractRegion.cs b/Ryujinx.Memory/Tracking/AbstractRegion.cs index a3c3990ea..bd4c8ab5c 100644 --- a/Ryujinx.Memory/Tracking/AbstractRegion.cs +++ b/Ryujinx.Memory/Tracking/AbstractRegion.cs @@ -50,7 +50,8 @@ namespace Ryujinx.Memory.Tracking /// <param name="address">Address accessed</param> /// <param name="size">Size of the region affected in bytes</param> /// <param name="write">Whether the region was written to or read</param> - public abstract void Signal(ulong address, ulong size, bool write); + /// <param name="exemptId">Optional ID of the handles that should not be signalled</param> + public abstract void Signal(ulong address, ulong size, bool write, int? exemptId); /// <summary> /// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained. @@ -58,10 +59,11 @@ namespace Ryujinx.Memory.Tracking /// <param name="address">Address accessed</param> /// <param name="size">Size of the region affected in bytes</param> /// <param name="write">Whether the region was written to or read</param> - public abstract void SignalPrecise(ulong address, ulong size, bool write); + /// <param name="exemptId">Optional ID of the handles that should not be signalled</param> + public abstract void SignalPrecise(ulong address, ulong size, bool write, int? exemptId); /// <summary> - /// Split this region into two, around the specified address. + /// Split this region into two, around the specified address. /// This region is updated to end at the split address, and a new region is created to represent past that point. /// </summary> /// <param name="splitAddress">Address to split the region around</param> diff --git a/Ryujinx.Memory/Tracking/MemoryTracking.cs b/Ryujinx.Memory/Tracking/MemoryTracking.cs index 9a35cfb6c..bf1e0ad34 100644 --- a/Ryujinx.Memory/Tracking/MemoryTracking.cs +++ b/Ryujinx.Memory/Tracking/MemoryTracking.cs @@ -136,10 +136,11 @@ namespace Ryujinx.Memory.Tracking /// <param name="size">Size of the region</param> /// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param> /// <param name="granularity">Desired granularity of write tracking</param> + /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) { - return new MultiRegionHandle(this, address, size, handles, granularity); + return new MultiRegionHandle(this, address, size, handles, granularity, id); } /// <summary> @@ -148,12 +149,13 @@ namespace Ryujinx.Memory.Tracking /// <param name="address">CPU virtual address of the region</param> /// <param name="size">Size of the region</param> /// <param name="granularity">Desired granularity of write tracking</param> + /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { (address, size) = PageAlign(address, size); - return new SmartMultiRegionHandle(this, address, size, granularity); + return new SmartMultiRegionHandle(this, address, size, granularity, id); } /// <summary> @@ -161,14 +163,16 @@ namespace Ryujinx.Memory.Tracking /// </summary> /// <param name="address">CPU virtual address of the region</param> /// <param name="size">Size of the region</param> + /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - public RegionHandle BeginTracking(ulong address, ulong size) + public RegionHandle BeginTracking(ulong address, ulong size, int id) { var (paAddress, paSize) = PageAlign(address, size); lock (TrackingLock) { - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, _memoryManager.IsRangeMapped(address, size)); + bool mapped = _memoryManager.IsRangeMapped(address, size); + RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, id, mapped); return handle; } @@ -181,28 +185,31 @@ namespace Ryujinx.Memory.Tracking /// <param name="size">Size of the region</param> /// <param name="bitmap">The bitmap owning the dirty flag for this handle</param> /// <param name="bit">The bit of this handle within the dirty flag</param> + /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit) + internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit, int id) { var (paAddress, paSize) = PageAlign(address, size); lock (TrackingLock) { - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size)); + bool mapped = _memoryManager.IsRangeMapped(address, size); + RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, id, mapped); return handle; } } /// <summary> - /// Signal that a virtual memory event happened at the given location (one byte). + /// Signal that a virtual memory event happened at the given location. /// </summary> /// <param name="address">Virtual address accessed</param> - /// <param name="write">Whether the address was written to or read</param> + /// <param name="size">Size of the region affected in bytes</param> + /// <param name="write">Whether the region was written to or read</param> /// <returns>True if the event triggered any tracking regions, false otherwise</returns> - public bool VirtualMemoryEventTracking(ulong address, bool write) + public bool VirtualMemoryEvent(ulong address, ulong size, bool write) { - return VirtualMemoryEvent(address, 1, write); + return VirtualMemoryEvent(address, size, write, precise: false, null); } /// <summary> @@ -214,8 +221,9 @@ namespace Ryujinx.Memory.Tracking /// <param name="size">Size of the region affected in bytes</param> /// <param name="write">Whether the region was written to or read</param> /// <param name="precise">True if the access is precise, false otherwise</param> + /// <param name="exemptId">Optional ID that of the handles that should not be signalled</param> /// <returns>True if the event triggered any tracking regions, false otherwise</returns> - public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise = false) + public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise, int? exemptId = null) { // Look up the virtual region using the region list. // Signal up the chain to relevant handles. @@ -250,11 +258,11 @@ namespace Ryujinx.Memory.Tracking if (precise) { - region.SignalPrecise(address, size, write); + region.SignalPrecise(address, size, write, exemptId); } else { - region.Signal(address, size, write); + region.Signal(address, size, write, exemptId); } } } diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs index 6ea2b7845..68fc5e759 100644 --- a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs @@ -30,7 +30,13 @@ namespace Ryujinx.Memory.Tracking public bool Dirty { get; private set; } = true; - internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) + internal MultiRegionHandle( + MemoryTracking tracking, + ulong address, + ulong size, + IEnumerable<IRegionHandle> handles, + ulong granularity, + int id) { _handles = new RegionHandle[(size + granularity - 1) / granularity]; Granularity = granularity; @@ -55,7 +61,7 @@ namespace Ryujinx.Memory.Tracking // Fill any gap left before this handle. while (i < startIndex) { - RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); + RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); fillHandle.Parent = this; _handles[i++] = fillHandle; } @@ -76,7 +82,7 @@ namespace Ryujinx.Memory.Tracking while (i < endIndex) { - RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); + RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); splitHandle.Parent = this; splitHandle.Reprotect(handle.Dirty); @@ -99,7 +105,7 @@ namespace Ryujinx.Memory.Tracking // Fill any remaining space with new handles. while (i < _handles.Length) { - RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); + RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); handle.Parent = this; _handles[i++] = handle; } diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs index 580f94a51..7a59f9f25 100644 --- a/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -15,12 +15,12 @@ namespace Ryujinx.Memory.Tracking /// If more than this number of checks have been performed on a dirty flag since its last reprotect, /// then it is dirtied infrequently. /// </summary> - private static int CheckCountForInfrequent = 3; + private const int CheckCountForInfrequent = 3; /// <summary> /// Number of frequent dirty/consume in a row to make this handle volatile. /// </summary> - private static int VolatileThreshold = 5; + private const int VolatileThreshold = 5; public bool Dirty { @@ -35,6 +35,7 @@ namespace Ryujinx.Memory.Tracking } internal int SequenceNumber { get; set; } + internal int Id { get; } public bool Unmapped { get; private set; } @@ -97,14 +98,26 @@ namespace Ryujinx.Memory.Tracking /// <param name="realSize">The real, unaligned size of the handle</param> /// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param> /// <param name="bit">The bit index representing the dirty flag for this handle</param> + /// <param name="id">Handle ID</param> /// <param name="mapped">True if the region handle starts mapped</param> - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, ConcurrentBitmap bitmap, int bit, bool mapped = true) + internal RegionHandle( + MemoryTracking tracking, + ulong address, + ulong size, + ulong realAddress, + ulong realSize, + ConcurrentBitmap bitmap, + int bit, + int id, + bool mapped = true) { Bitmap = bitmap; DirtyBit = bit; Dirty = mapped; + Id = id; + Unmapped = !mapped; Address = address; Size = size; @@ -131,11 +144,14 @@ namespace Ryujinx.Memory.Tracking /// <param name="size">Size of the region to track</param> /// <param name="realAddress">The real, unaligned address of the handle</param> /// <param name="realSize">The real, unaligned size of the handle</param> + /// <param name="id">Handle ID</param> /// <param name="mapped">True if the region handle starts mapped</param> - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, bool mapped = true) + internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, int id, bool mapped = true) { Bitmap = new ConcurrentBitmap(1, mapped); + Id = id; + Unmapped = !mapped; Address = address; diff --git a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs b/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs index 47fe72e5b..4acddefaf 100644 --- a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs +++ b/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs @@ -18,10 +18,11 @@ namespace Ryujinx.Memory.Tracking private readonly ulong _granularity; private readonly ulong _size; private MemoryTracking _tracking; + private readonly int _id; public bool Dirty { get; private set; } = true; - internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity) + internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity, int id) { // For this multi-region handle, the handle list starts empty. // As regions are queried, they are added to the _handles array at their start index. @@ -34,6 +35,7 @@ namespace Ryujinx.Memory.Tracking _address = address; _size = size; + _id = id; } public void SignalWrite() @@ -102,7 +104,7 @@ namespace Ryujinx.Memory.Tracking RegionSignal signal = handle.PreAction; handle.Dispose(); - RegionHandle splitLow = _tracking.BeginTracking(address, size); + RegionHandle splitLow = _tracking.BeginTracking(address, size, _id); splitLow.Parent = this; if (signal != null) { @@ -110,7 +112,7 @@ namespace Ryujinx.Memory.Tracking } _handles[handleIndex] = splitLow; - RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size); + RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size, _id); splitHigh.Parent = this; if (signal != null) { @@ -145,7 +147,7 @@ namespace Ryujinx.Memory.Tracking if (handle != null) { // Fill up to the found handle. - handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle)); + handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle), _id); handle.Parent = this; _handles[startHandle] = handle; return; @@ -153,7 +155,7 @@ namespace Ryujinx.Memory.Tracking } // Can fill the whole range. - _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle)); + _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle), _id); _handles[startHandle].Parent = this; } diff --git a/Ryujinx.Memory/Tracking/VirtualRegion.cs b/Ryujinx.Memory/Tracking/VirtualRegion.cs index 57a0344ac..9651426b3 100644 --- a/Ryujinx.Memory/Tracking/VirtualRegion.cs +++ b/Ryujinx.Memory/Tracking/VirtualRegion.cs @@ -19,19 +19,24 @@ namespace Ryujinx.Memory.Tracking _tracking = tracking; } - public override void Signal(ulong address, ulong size, bool write) + /// <inheritdoc/> + public override void Signal(ulong address, ulong size, bool write, int? exemptId) { IList<RegionHandle> handles = Handles; for (int i = 0; i < handles.Count; i++) { - handles[i].Signal(address, size, write, ref handles); + if (exemptId == null || handles[i].Id != exemptId.Value) + { + handles[i].Signal(address, size, write, ref handles); + } } UpdateProtection(); } - public override void SignalPrecise(ulong address, ulong size, bool write) + /// <inheritdoc/> + public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId) { IList<RegionHandle> handles = Handles; @@ -39,7 +44,10 @@ namespace Ryujinx.Memory.Tracking for (int i = 0; i < handles.Count; i++) { - allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles); + if (exemptId == null || handles[i].Id != exemptId.Value) + { + allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles); + } } // Only update protection if a regular signal handler was called. diff --git a/Ryujinx.Tests/Memory/MockMemoryManager.cs b/Ryujinx.Tests/Memory/MockMemoryManager.cs index 3f7692636..eeecf419f 100644 --- a/Ryujinx.Tests/Memory/MockMemoryManager.cs +++ b/Ryujinx.Tests/Memory/MockMemoryManager.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Tests.Memory throw new NotImplementedException(); } - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { throw new NotImplementedException(); } From 6bf460e1041b969a453dc40ee6fb83164739bf9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Feb 2023 02:35:02 +0100 Subject: [PATCH 360/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.26.1 to 6.27.0 (#4441) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.26.1 to 6.27.0. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1b0b906f8..6f5ed3d71 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> - <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.26.1" /> + <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" /> <PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> <PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> From 7aa430f1a51fd793971992b4454540975222b848 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 19 Feb 2023 22:37:37 -0300 Subject: [PATCH 361/737] Add support for advanced blend (part 1/2) (#2801) * Add blend microcode registers * Add advanced blend support using host extension * Remove debug message * Use pre-generated table for blend functions * XML docs * Rename AdvancedBlendMode to AdvancedBlendOp for consistency * Remove redundant code * Fix some advanced blend related issues on Vulkan * Formatting --- .../AdvancedBlendDescriptor.cs | 16 + Ryujinx.Graphics.GAL/AdvancedBlendOp.cs | 52 + Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs | 9 + Ryujinx.Graphics.GAL/Capabilities.cs | 3 + Ryujinx.Graphics.GAL/IPipeline.cs | 1 + .../Multithreading/CommandHelper.cs | 1 + .../Multithreading/CommandType.cs | 1 + .../Commands/SetBlendStateAdvancedCommand.cs | 18 + .../Multithreading/ThreadedPipeline.cs | 6 + .../Threed/Blender/AdvancedBlendFunctions.cs | 4226 +++++++++++++++++ .../Threed/Blender/AdvancedBlendManager.cs | 115 + .../Blender/AdvancedBlendPreGenTable.cs | 273 ++ .../Threed/Blender/AdvancedBlendUcode.cs | 126 + .../Engine/Threed/Blender/UcodeAssembler.cs | 305 ++ .../Engine/Threed/StateUpdater.cs | 28 +- .../Engine/Threed/ThreedClass.cs | 25 +- .../Engine/Threed/ThreedClassState.cs | 72 +- Ryujinx.Graphics.OpenGL/EnumConversion.cs | 120 + Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 2 + Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 1 + Ryujinx.Graphics.OpenGL/Pipeline.cs | 19 + Ryujinx.Graphics.Vulkan/EnumConversion.cs | 65 + .../HardwareCapabilities.cs | 12 + Ryujinx.Graphics.Vulkan/PipelineBase.cs | 54 +- Ryujinx.Graphics.Vulkan/PipelineState.cs | 42 + .../VulkanInitialization.cs | 1 + Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 26 +- 27 files changed, 5605 insertions(+), 14 deletions(-) create mode 100644 Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs create mode 100644 Ryujinx.Graphics.GAL/AdvancedBlendOp.cs create mode 100644 Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs create mode 100644 Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs diff --git a/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs b/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs new file mode 100644 index 000000000..1f1f7c3f1 --- /dev/null +++ b/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Graphics.GAL +{ + public struct AdvancedBlendDescriptor + { + public AdvancedBlendOp Op { get; } + public AdvancedBlendOverlap Overlap { get; } + public bool SrcPreMultiplied { get; } + + public AdvancedBlendDescriptor(AdvancedBlendOp op, AdvancedBlendOverlap overlap, bool srcPreMultiplied) + { + Op = op; + Overlap = overlap; + SrcPreMultiplied = srcPreMultiplied; + } + } +} diff --git a/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs b/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs new file mode 100644 index 000000000..4140bf497 --- /dev/null +++ b/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs @@ -0,0 +1,52 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum AdvancedBlendOp + { + Zero, + Src, + Dst, + SrcOver, + DstOver, + SrcIn, + DstIn, + SrcOut, + DstOut, + SrcAtop, + DstAtop, + Xor, + Plus, + PlusClamped, + PlusClampedAlpha, + PlusDarker, + Multiply, + Screen, + Overlay, + Darken, + Lighten, + ColorDodge, + ColorBurn, + HardLight, + SoftLight, + Difference, + Minus, + MinusClamped, + Exclusion, + Contrast, + Invert, + InvertRGB, + InvertOvg, + LinearDodge, + LinearBurn, + VividLight, + LinearLight, + PinLight, + HardMix, + Red, + Green, + Blue, + HslHue, + HslSaturation, + HslColor, + HslLuminosity + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs b/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs new file mode 100644 index 000000000..d4feb2b30 --- /dev/null +++ b/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum AdvancedBlendOverlap + { + Uncorrelated, + Disjoint, + Conjoint + } +} diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index 7a1f44b6b..a24139eba 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsR4G4B4A4Format; public readonly bool SupportsSnormBufferTextureFormat; public readonly bool Supports5BitComponentFormat; + public readonly bool SupportsBlendEquationAdvanced; public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsGeometryShaderPassthrough; @@ -64,6 +65,7 @@ namespace Ryujinx.Graphics.GAL bool supportsR4G4B4A4Format, bool supportsSnormBufferTextureFormat, bool supports5BitComponentFormat, + bool supportsBlendEquationAdvanced, bool supportsFragmentShaderInterlock, bool supportsFragmentShaderOrderingIntel, bool supportsGeometryShaderPassthrough, @@ -102,6 +104,7 @@ namespace Ryujinx.Graphics.GAL SupportsR4G4B4A4Format = supportsR4G4B4A4Format; SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat; Supports5BitComponentFormat = supports5BitComponentFormat; + SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 26d019eb4..0a362081c 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.GAL void SetAlphaTest(bool enable, float reference, CompareOp op); + void SetBlendState(AdvancedBlendDescriptor blend); void SetBlendState(int index, BlendDescriptor blend); void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs index 48873491f..063b7edf9 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -98,6 +98,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register<EndHostConditionalRenderingCommand>(CommandType.EndHostConditionalRendering); Register<EndTransformFeedbackCommand>(CommandType.EndTransformFeedback); Register<SetAlphaTestCommand>(CommandType.SetAlphaTest); + Register<SetBlendStateAdvancedCommand>(CommandType.SetBlendStateAdvanced); Register<SetBlendStateCommand>(CommandType.SetBlendState); Register<SetDepthBiasCommand>(CommandType.SetDepthBias); Register<SetDepthClampCommand>(CommandType.SetDepthClamp); diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs index c199ff34c..61e729b44 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -60,6 +60,7 @@ EndHostConditionalRendering, EndTransformFeedback, SetAlphaTest, + SetBlendStateAdvanced, SetBlendState, SetDepthBias, SetDepthClamp, diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs new file mode 100644 index 000000000..2ec10a503 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetBlendStateAdvancedCommand : IGALCommand, IGALCommand<SetBlendStateAdvancedCommand> + { + public CommandType CommandType => CommandType.SetBlendStateAdvanced; + private AdvancedBlendDescriptor _blend; + + public void Set(AdvancedBlendDescriptor blend) + { + _blend = blend; + } + + public static void Run(ref SetBlendStateAdvancedCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetBlendState(command._blend); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index ba120867c..1bdc9cf48 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -131,6 +131,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } + public void SetBlendState(AdvancedBlendDescriptor blend) + { + _renderer.New<SetBlendStateAdvancedCommand>().Set(blend); + _renderer.QueueCommand(); + } + public void SetBlendState(int index, BlendDescriptor blend) { _renderer.New<SetBlendStateCommand>().Set(index, blend); diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs new file mode 100644 index 000000000..a40b9cc47 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs @@ -0,0 +1,4226 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender +{ + static class AdvancedBlendFunctions + { + public static readonly AdvancedBlendUcode[] Table = new AdvancedBlendUcode[] + { + new AdvancedBlendUcode(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedPlusClampedPremul), + new AdvancedBlendUcode(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedPlusClampedAlphaPremul), + new AdvancedBlendUcode(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedPlusDarkerPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedMultiplyPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedScreenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedOverlayPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedDarkenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedLightenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedColorDodgePremul), + new AdvancedBlendUcode(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedColorBurnPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedHardLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedSoftLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedDifferencePremul), + new AdvancedBlendUcode(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedMinusPremul), + new AdvancedBlendUcode(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedMinusClampedPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedExclusionPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedContrastPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedInvertPremul), + new AdvancedBlendUcode(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedInvertRGBPremul), + new AdvancedBlendUcode(AdvancedBlendOp.InvertOvg, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedInvertOvgPremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedLinearDodgePremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedLinearBurnPremul), + new AdvancedBlendUcode(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedVividLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedLinearLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedPinLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedHardMixPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedRedPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedGreenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedBluePremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedHslHuePremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedHslSaturationPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedHslColorPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, true, GenUncorrelatedHslLuminosityPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, true, GenDisjointSrcPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Disjoint, true, GenDisjointDstPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, true, GenDisjointSrcOverPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, true, GenDisjointDstOverPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, true, GenDisjointSrcInPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Disjoint, true, GenDisjointDstInPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, true, GenDisjointSrcOutPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Disjoint, true, GenDisjointDstOutPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, true, GenDisjointSrcAtopPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, true, GenDisjointDstAtopPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, true, GenDisjointXorPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, true, GenDisjointPlusPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, true, GenDisjointMultiplyPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, true, GenDisjointScreenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, true, GenDisjointOverlayPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, true, GenDisjointDarkenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, true, GenDisjointLightenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, true, GenDisjointColorDodgePremul), + new AdvancedBlendUcode(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, true, GenDisjointColorBurnPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, true, GenDisjointHardLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, true, GenDisjointSoftLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, true, GenDisjointDifferencePremul), + new AdvancedBlendUcode(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, true, GenDisjointExclusionPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Disjoint, true, GenDisjointInvertPremul), + new AdvancedBlendUcode(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, true, GenDisjointInvertRGBPremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, true, GenDisjointLinearDodgePremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, true, GenDisjointLinearBurnPremul), + new AdvancedBlendUcode(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, true, GenDisjointVividLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, true, GenDisjointLinearLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, true, GenDisjointPinLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, true, GenDisjointHardMixPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, true, GenDisjointHslHuePremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, true, GenDisjointHslSaturationPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, true, GenDisjointHslColorPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, true, GenDisjointHslLuminosityPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, true, GenConjointSrcPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Conjoint, true, GenConjointDstPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, true, GenConjointSrcOverPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, true, GenConjointDstOverPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, true, GenConjointSrcInPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Conjoint, true, GenConjointDstInPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, true, GenConjointSrcOutPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Conjoint, true, GenConjointDstOutPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, true, GenConjointSrcAtopPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, true, GenConjointDstAtopPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, true, GenConjointXorPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, true, GenConjointMultiplyPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, true, GenConjointScreenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, true, GenConjointOverlayPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, true, GenConjointDarkenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, true, GenConjointLightenPremul), + new AdvancedBlendUcode(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, true, GenConjointColorDodgePremul), + new AdvancedBlendUcode(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, true, GenConjointColorBurnPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, true, GenConjointHardLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, true, GenConjointSoftLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, true, GenConjointDifferencePremul), + new AdvancedBlendUcode(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, true, GenConjointExclusionPremul), + new AdvancedBlendUcode(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Conjoint, true, GenConjointInvertPremul), + new AdvancedBlendUcode(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, true, GenConjointInvertRGBPremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, true, GenConjointLinearDodgePremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, true, GenConjointLinearBurnPremul), + new AdvancedBlendUcode(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, true, GenConjointVividLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, true, GenConjointLinearLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, true, GenConjointPinLightPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, true, GenConjointHardMixPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, true, GenConjointHslHuePremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, true, GenConjointHslSaturationPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, true, GenConjointHslColorPremul), + new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, true, GenConjointHslLuminosityPremul), + new AdvancedBlendUcode(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedDstOver), + new AdvancedBlendUcode(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedSrcIn), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedSrcOut), + new AdvancedBlendUcode(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedSrcAtop), + new AdvancedBlendUcode(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedDstAtop), + new AdvancedBlendUcode(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedXor), + new AdvancedBlendUcode(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedPlusClamped), + new AdvancedBlendUcode(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedPlusClampedAlpha), + new AdvancedBlendUcode(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedPlusDarker), + new AdvancedBlendUcode(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedMultiply), + new AdvancedBlendUcode(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedScreen), + new AdvancedBlendUcode(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedOverlay), + new AdvancedBlendUcode(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedDarken), + new AdvancedBlendUcode(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedLighten), + new AdvancedBlendUcode(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedColorDodge), + new AdvancedBlendUcode(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedColorBurn), + new AdvancedBlendUcode(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedHardLight), + new AdvancedBlendUcode(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedSoftLight), + new AdvancedBlendUcode(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedDifference), + new AdvancedBlendUcode(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedMinus), + new AdvancedBlendUcode(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedMinusClamped), + new AdvancedBlendUcode(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedExclusion), + new AdvancedBlendUcode(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedContrast), + new AdvancedBlendUcode(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedInvertRGB), + new AdvancedBlendUcode(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedLinearDodge), + new AdvancedBlendUcode(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedLinearBurn), + new AdvancedBlendUcode(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedVividLight), + new AdvancedBlendUcode(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedLinearLight), + new AdvancedBlendUcode(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedPinLight), + new AdvancedBlendUcode(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedHardMix), + new AdvancedBlendUcode(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedRed), + new AdvancedBlendUcode(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedGreen), + new AdvancedBlendUcode(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedBlue), + new AdvancedBlendUcode(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedHslHue), + new AdvancedBlendUcode(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedHslSaturation), + new AdvancedBlendUcode(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedHslColor), + new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, false, GenUncorrelatedHslLuminosity), + new AdvancedBlendUcode(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, false, GenDisjointSrc), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, false, GenDisjointSrcOver), + new AdvancedBlendUcode(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, false, GenDisjointDstOver), + new AdvancedBlendUcode(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, false, GenDisjointSrcIn), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, false, GenDisjointSrcOut), + new AdvancedBlendUcode(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, false, GenDisjointSrcAtop), + new AdvancedBlendUcode(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, false, GenDisjointDstAtop), + new AdvancedBlendUcode(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, false, GenDisjointXor), + new AdvancedBlendUcode(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, false, GenDisjointPlus), + new AdvancedBlendUcode(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, false, GenDisjointMultiply), + new AdvancedBlendUcode(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, false, GenDisjointScreen), + new AdvancedBlendUcode(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, false, GenDisjointOverlay), + new AdvancedBlendUcode(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, false, GenDisjointDarken), + new AdvancedBlendUcode(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, false, GenDisjointLighten), + new AdvancedBlendUcode(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, false, GenDisjointColorDodge), + new AdvancedBlendUcode(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, false, GenDisjointColorBurn), + new AdvancedBlendUcode(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, false, GenDisjointHardLight), + new AdvancedBlendUcode(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, false, GenDisjointSoftLight), + new AdvancedBlendUcode(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, false, GenDisjointDifference), + new AdvancedBlendUcode(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, false, GenDisjointExclusion), + new AdvancedBlendUcode(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, false, GenDisjointInvertRGB), + new AdvancedBlendUcode(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, false, GenDisjointLinearDodge), + new AdvancedBlendUcode(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, false, GenDisjointLinearBurn), + new AdvancedBlendUcode(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, false, GenDisjointVividLight), + new AdvancedBlendUcode(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, false, GenDisjointLinearLight), + new AdvancedBlendUcode(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, false, GenDisjointPinLight), + new AdvancedBlendUcode(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, false, GenDisjointHardMix), + new AdvancedBlendUcode(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, false, GenDisjointHslHue), + new AdvancedBlendUcode(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, false, GenDisjointHslSaturation), + new AdvancedBlendUcode(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, false, GenDisjointHslColor), + new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, false, GenDisjointHslLuminosity), + new AdvancedBlendUcode(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, false, GenConjointSrc), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, false, GenConjointSrcOver), + new AdvancedBlendUcode(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, false, GenConjointDstOver), + new AdvancedBlendUcode(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, false, GenConjointSrcIn), + new AdvancedBlendUcode(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, false, GenConjointSrcOut), + new AdvancedBlendUcode(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, false, GenConjointSrcAtop), + new AdvancedBlendUcode(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, false, GenConjointDstAtop), + new AdvancedBlendUcode(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, false, GenConjointXor), + new AdvancedBlendUcode(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, false, GenConjointMultiply), + new AdvancedBlendUcode(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, false, GenConjointScreen), + new AdvancedBlendUcode(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, false, GenConjointOverlay), + new AdvancedBlendUcode(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, false, GenConjointDarken), + new AdvancedBlendUcode(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, false, GenConjointLighten), + new AdvancedBlendUcode(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, false, GenConjointColorDodge), + new AdvancedBlendUcode(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, false, GenConjointColorBurn), + new AdvancedBlendUcode(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, false, GenConjointHardLight), + new AdvancedBlendUcode(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, false, GenConjointSoftLight), + new AdvancedBlendUcode(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, false, GenConjointDifference), + new AdvancedBlendUcode(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, false, GenConjointExclusion), + new AdvancedBlendUcode(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, false, GenConjointInvertRGB), + new AdvancedBlendUcode(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, false, GenConjointLinearDodge), + new AdvancedBlendUcode(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, false, GenConjointLinearBurn), + new AdvancedBlendUcode(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, false, GenConjointVividLight), + new AdvancedBlendUcode(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, false, GenConjointLinearLight), + new AdvancedBlendUcode(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, false, GenConjointPinLight), + new AdvancedBlendUcode(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, false, GenConjointHardMix), + new AdvancedBlendUcode(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, false, GenConjointHslHue), + new AdvancedBlendUcode(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, false, GenConjointHslSaturation), + new AdvancedBlendUcode(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, false, GenConjointHslColor), + new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, false, GenConjointHslLuminosity) + }; + + public static string GenTable() + { + // This can be used to generate the table on AdvancedBlendPreGenTable. + + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($"private static Dictionary<Hash128, AdvancedBlendEntry> _entries = new()"); + sb.AppendLine("{"); + + foreach (var entry in Table) + { + Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast<uint, byte>(entry.Code)); + + string[] constants = new string[entry.Constants != null ? entry.Constants.Length : 0]; + + for (int i = 0; i < constants.Length; i++) + { + RgbFloat rgb = entry.Constants[i]; + + constants[i] = string.Format(CultureInfo.InvariantCulture, "new " + nameof(RgbFloat) + "({0}f, {1}f, {2}f)", rgb.R, rgb.G, rgb.B); + } + + string constantList = constants.Length > 0 ? $"new[] {{ {string.Join(", ", constants)} }}" : $"Array.Empty<{nameof(RgbFloat)}>()"; + + static string EnumValue(string name, object value) + { + if (value.ToString() == "0") + { + return "0"; + } + + return $"{name}.{value}"; + } + + string alpha = $"new {nameof(FixedFunctionAlpha)}({EnumValue(nameof(BlendUcodeEnable), entry.Alpha.Enable)}, {EnumValue(nameof(BlendOp), entry.Alpha.AlphaOp)}, {EnumValue(nameof(BlendFactor), entry.Alpha.AlphaSrcFactor)}, {EnumValue(nameof(BlendFactor), entry.Alpha.AlphaDstFactor)})"; + + sb.AppendLine($" {{ new Hash128(0x{hash.Low:X16}, 0x{hash.High:X16}), new AdvancedBlendEntry({nameof(AdvancedBlendOp)}.{entry.Op}, {nameof(AdvancedBlendOverlap)}.{entry.Overlap}, {(entry.SrcPreMultiplied ? "true" : "false")}, {constantList}, {alpha}) }},"); + } + + sb.AppendLine("};"); + + return sb.ToString(); + } + + private static FixedFunctionAlpha GenUncorrelatedPlusClampedPremul(ref UcodeAssembler asm) + { + asm.Add(CC.T, Dest.PBR, OpBD.DstRGB, OpBD.SrcRGB); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedPlusClampedAlphaPremul(ref UcodeAssembler asm) + { + asm.Add(CC.T, Dest.Temp0, OpBD.DstRGB, OpBD.SrcRGB); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedPlusDarkerPremul(ref UcodeAssembler asm) + { + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.T, Dest.PBR, OpBD.PBR, OpBD.SrcRGB); + asm.Add(CC.T, Dest.PBR, OpBD.PBR, OpBD.DstRGB); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.SrcAAA); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.DstAAA); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedMultiplyPremul(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstRGB); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedScreenPremul(ref UcodeAssembler asm) + { + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.DstRGB); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedOverlayPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp2, OpBD.Temp1, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedDarkenPremul(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.SrcAAA); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLightenPremul(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.SrcAAA); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedColorDodgePremul(ref UcodeAssembler asm) + { + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.SrcRGB); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.SrcAAA); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.DstRGB); + asm.Min(CC.GT, Dest.PBR, OpAC.DstAAA, OpBD.PBR); + asm.Mul(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.SrcAAA); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.DstAAA); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.DstRGB, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedColorBurnPremul(ref UcodeAssembler asm) + { + asm.Mmsub(CC.T, Dest.Temp0, OpAC.DstAAA, OpBD.SrcAAA, OpAC.SrcAAA, OpBD.DstRGB); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcRGB); + asm.Mul(CC.T, Dest.PBR, OpAC.Temp0, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.SrcAAA, OpBD.DstAAA, OpAC.SrcAAA, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.DstAAA, OpBD.DstRGB); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHardLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.Temp2, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp2, OpBD.Temp1, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedSoftLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(4, 0.25f, 0.25f, 0.25f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(0, 0.2605f, 0.2605f, 0.2605f); + asm.Mul(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(1, -0.7817f, -0.7817f, -0.7817f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(2, 0.3022f, 0.3022f, 0.3022f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(3, 0.2192f, 0.2192f, 0.2192f); + asm.Add(CC.GT, Dest.Temp0, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(5, 16f, 16f, 16f); + asm.Mul(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(6, 12f, 12f, 12f); + asm.Mmsub(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(7, 3f, 3f, 3f); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mmsub(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.ConstantOne, OpAC.Temp1, OpBD.Temp1); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedDifferencePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.Temp0, OpBD.Temp2, OpBD.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedMinusPremul(ref UcodeAssembler asm) + { + asm.Sub(CC.T, Dest.Temp0, OpBD.DstRGB, OpBD.SrcRGB); + return new FixedFunctionAlpha(BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedMinusClampedPremul(ref UcodeAssembler asm) + { + asm.Sub(CC.T, Dest.PBR, OpBD.DstRGB, OpBD.SrcRGB); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantZero); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedExclusionPremul(ref UcodeAssembler asm) + { + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.DstRGB); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.DstRGB); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedContrastPremul(ref UcodeAssembler asm) + { + asm.SetConstant(0, 2f, 2f, 2f); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.ConstantRGB, OpAC.DstAAA, OpBD.ConstantOne); + asm.Mmsub(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.SrcAAA, OpBD.ConstantOne); + asm.Mul(CC.T, Dest.PBR, OpAC.Temp0, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.PBR, OpBD.DstAAA); + asm.SetConstant(1, 0.5f, 0.5f, 0.5f); + asm.Mul(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantRGB); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedInvertPremul(ref UcodeAssembler asm) + { + asm.Mmsub(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA, OpAC.SrcAAA, OpBD.DstRGB); + asm.Madd(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.OneMinusSrcAAA, OpAC.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedInvertRGBPremul(ref UcodeAssembler asm) + { + asm.Mmsub(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.DstAAA, OpAC.SrcRGB, OpBD.DstRGB); + asm.Madd(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.OneMinusSrcAAA, OpAC.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedInvertOvgPremul(ref UcodeAssembler asm) + { + asm.Sub(CC.T, Dest.PBR, OpBD.ConstantOne, OpBD.DstRGB); + asm.Mmadd(CC.T, Dest.Temp0, OpAC.SrcAAA, OpBD.PBR, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLinearDodgePremul(ref UcodeAssembler asm) + { + asm.Mmadd(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLinearBurnPremul(ref UcodeAssembler asm) + { + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Mmadd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedVividLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantRGB); + asm.Sub(CC.GE, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Add(CC.GE, Dest.PBR, OpBD.PBR, OpBD.PBR); + asm.Rcp(CC.GE, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GE, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GE, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Rcp(CC.LT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.LT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantOne); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLinearLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 2f, 2f, 2f); + asm.Madd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedPinLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.Temp0, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Add(CC.LE, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Min(CC.LE, Dest.Temp0, OpAC.PBR, OpBD.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHardMixPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mul(CC.LT, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedRedPremul(ref UcodeAssembler asm) + { + asm.Mov(CC.T, Dest.Temp0, OpBD.DstRGB); + asm.Mov(CC.T, Dest.Temp0.R, OpBD.SrcRGB); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedGreenPremul(ref UcodeAssembler asm) + { + asm.Mov(CC.T, Dest.Temp0, OpBD.DstRGB); + asm.Mov(CC.T, Dest.Temp0.G, OpBD.SrcRGB); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedBluePremul(ref UcodeAssembler asm) + { + asm.Mov(CC.T, Dest.Temp0, OpBD.DstRGB); + asm.Mov(CC.T, Dest.Temp0.B, OpBD.SrcRGB); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslHuePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp2, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp2.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp2); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslSaturationPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.PBR); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp1, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.Temp1.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp1); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslColorPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp2, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp2, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslLuminosityPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp2, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp2.BBB, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Add(CC.T, Dest.Temp1, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp1, OpBD.Temp2); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp1, OpBD.Temp2, OpAC.Temp2, OpBD.Temp2); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp2); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.SrcRGB, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenDisjointSrcPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenDisjointDstPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.Temp1, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointSrcOverPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp2); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDstOverPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp1); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSrcInPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Sub(CC.T, Dest.Temp1.RToA, OpBD.DstAAA, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDstInPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.Temp1, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Sub(CC.T, Dest.Temp1.RToA, OpBD.DstAAA, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSrcOutPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDstOutPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSrcAtopPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointDstAtopPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.Temp1, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenDisjointXorPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + asm.Min(CC.T, Dest.Temp1, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Add(CC.T, Dest.Temp1.RToA, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointPlusPremul(ref UcodeAssembler asm) + { + asm.Add(CC.T, Dest.Temp0, OpBD.DstRGB, OpBD.SrcRGB); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointMultiplyPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointScreenPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointOverlayPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp2, OpBD.Temp1, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDarkenPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointLightenPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointColorDodgePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.ConstantOne, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.Temp0); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp1, OpBD.ConstantZero); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantZero); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointColorBurnPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.Temp2, OpBD.ConstantZero); + asm.Rcp(CC.GT, Dest.PBR, OpAC.Temp2); + asm.Mmsub(CC.GT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.ConstantOne, OpBD.Temp1); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHardLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.Temp2, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp2, OpBD.Temp1, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSoftLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(4, 0.25f, 0.25f, 0.25f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(0, 0.2605f, 0.2605f, 0.2605f); + asm.Mul(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(1, -0.7817f, -0.7817f, -0.7817f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(2, 0.3022f, 0.3022f, 0.3022f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(3, 0.2192f, 0.2192f, 0.2192f); + asm.Add(CC.GT, Dest.Temp0, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(5, 16f, 16f, 16f); + asm.Mul(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(6, 12f, 12f, 12f); + asm.Mmsub(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(7, 3f, 3f, 3f); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mmsub(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.ConstantOne, OpAC.Temp1, OpBD.Temp1); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDifferencePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.Temp0, OpBD.Temp2, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointExclusionPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.Temp1); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointInvertPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp0, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointInvertRGBPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.ConstantOne, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp0, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointLinearDodgePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointLinearBurnPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointVividLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantRGB); + asm.Sub(CC.GE, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Add(CC.GE, Dest.PBR, OpBD.PBR, OpBD.PBR); + asm.Rcp(CC.GE, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GE, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GE, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Rcp(CC.LT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.LT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantOne); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointLinearLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 2f, 2f, 2f); + asm.Madd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointPinLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.Temp0, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Add(CC.LE, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Min(CC.LE, Dest.Temp0, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHardMixPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mul(CC.LT, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslHuePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp2, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp2.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp2); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslSaturationPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.PBR); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp1, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.Temp1.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp1); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslColorPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp2, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp2, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslLuminosityPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp2, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp2.BBB, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Add(CC.T, Dest.Temp1, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp1, OpBD.Temp2); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp1, OpBD.Temp2, OpAC.Temp2, OpBD.Temp2); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp2); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.Temp2, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenConjointSrcPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenConjointDstPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointSrcOverPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp2, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDstOverPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointSrcInPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDstInPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointSrcOutPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantZero); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenConjointDstOutPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantZero); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenConjointSrcAtopPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDstAtopPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenConjointXorPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + asm.Sub(CC.T, Dest.Temp1.CC, OpBD.DstAAA, OpBD.SrcAAA); + asm.Sub(CC.LT, Dest.Temp1, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mov(CC.T, Dest.Temp1.RToA, OpBD.Temp1); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenConjointMultiplyPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointScreenPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointOverlayPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp2, OpBD.Temp1, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDarkenPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLightenPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointColorDodgePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.ConstantOne, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.Temp0); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp1, OpBD.ConstantZero); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointColorBurnPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.Temp2, OpBD.ConstantZero); + asm.Rcp(CC.GT, Dest.PBR, OpAC.Temp2); + asm.Mmsub(CC.GT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.ConstantOne, OpBD.Temp1); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHardLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.Temp2, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp2, OpBD.Temp1, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointSoftLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(4, 0.25f, 0.25f, 0.25f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(0, 0.2605f, 0.2605f, 0.2605f); + asm.Mul(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(1, -0.7817f, -0.7817f, -0.7817f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(2, 0.3022f, 0.3022f, 0.3022f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(3, 0.2192f, 0.2192f, 0.2192f); + asm.Add(CC.GT, Dest.Temp0, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(5, 16f, 16f, 16f); + asm.Mul(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(6, 12f, 12f, 12f); + asm.Mmsub(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(7, 3f, 3f, 3f); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mmsub(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.ConstantOne, OpAC.Temp1, OpBD.Temp1); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDifferencePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.Temp0, OpBD.Temp2, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointExclusionPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.Temp1); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointInvertPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointInvertRGBPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.ConstantOne, OpAC.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLinearDodgePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLinearBurnPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointVividLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantRGB); + asm.Sub(CC.GE, Dest.PBR, OpBD.ConstantOne, OpBD.Temp2); + asm.Add(CC.GE, Dest.PBR, OpBD.PBR, OpBD.PBR); + asm.Rcp(CC.GE, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GE, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GE, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Rcp(CC.LT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.LT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantOne); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLinearLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 2f, 2f, 2f); + asm.Madd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointPinLightPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Sub(CC.T, Dest.Temp0, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Add(CC.LE, Dest.PBR, OpBD.Temp2, OpBD.Temp2); + asm.Min(CC.LE, Dest.Temp0, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHardMixPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mul(CC.LT, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslHuePremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp2, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp2.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp2); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslSaturationPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.PBR); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp1, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.GT, Dest.Temp1.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp1); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslColorPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp2, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp2, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslLuminosityPremul(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp2, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp2.BBB, OpAC.Temp2, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Add(CC.T, Dest.Temp1, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp1, OpBD.Temp2); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp1, OpBD.Temp2, OpAC.Temp2, OpBD.Temp2); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp2); + asm.Rcp(CC.T, Dest.PBR, OpAC.SrcAAA); + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.PBR); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp2, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedDstOver(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedSrcIn(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.PBR, OpBD.DstAAA); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.DstAlphaGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenUncorrelatedSrcOut(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.PBR, OpBD.OneMinusDstAAA); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenUncorrelatedSrcAtop(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.PBR, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.OneMinusSrcAAA, OpAC.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedDstAtop(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.SrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenUncorrelatedXor(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.PBR, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.OneMinusSrcAAA, OpAC.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedPlusClamped(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Add(CC.T, Dest.PBR, OpBD.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedPlusClampedAlpha(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedPlusDarker(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.T, Dest.PBR, OpBD.PBR, OpBD.Temp2); + asm.Add(CC.T, Dest.PBR, OpBD.PBR, OpBD.DstRGB); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.SrcAAA); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.DstAAA); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedMultiply(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.PBR, OpBD.DstRGB); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedScreen(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.PBR, OpAC.PBR, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.DstRGB); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedOverlay(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.SrcRGB, OpBD.Temp1, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedDarken(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.PBR, OpBD.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.SrcAAA); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLighten(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.PBR, OpBD.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.SrcAAA); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedColorDodge(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.PBR); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.SrcAAA); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.DstRGB); + asm.Min(CC.GT, Dest.PBR, OpAC.DstAAA, OpBD.PBR); + asm.Mul(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.SrcAAA); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.DstAAA); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.DstRGB, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedColorBurn(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.DstAAA, OpBD.SrcAAA, OpAC.SrcAAA, OpBD.DstRGB); + asm.Rcp(CC.T, Dest.PBR, OpAC.Temp2); + asm.Mul(CC.T, Dest.PBR, OpAC.Temp0, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.SrcAAA, OpBD.DstAAA, OpAC.SrcAAA, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp2, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.DstAAA, OpBD.DstRGB); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHardLight(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.SrcRGB, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.SrcRGB, OpBD.Temp1, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedSoftLight(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(4, 0.25f, 0.25f, 0.25f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(0, 0.2605f, 0.2605f, 0.2605f); + asm.Mul(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(1, -0.7817f, -0.7817f, -0.7817f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(2, 0.3022f, 0.3022f, 0.3022f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(3, 0.2192f, 0.2192f, 0.2192f); + asm.Add(CC.GT, Dest.Temp0, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(5, 16f, 16f, 16f); + asm.Mul(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(6, 12f, 12f, 12f); + asm.Mmsub(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(7, 3f, 3f, 3f); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mmsub(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.ConstantOne, OpAC.Temp1, OpBD.Temp1); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedDifference(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.SrcRGB); + asm.Sub(CC.LT, Dest.Temp0, OpBD.SrcRGB, OpBD.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedMinus(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Sub(CC.T, Dest.Temp0, OpBD.DstRGB, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedMinusClamped(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Sub(CC.T, Dest.PBR, OpBD.DstRGB, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantZero); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenUncorrelatedExclusion(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.PBR, OpAC.PBR, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.DstRGB); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.Temp2, OpBD.DstRGB); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedContrast(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.SetConstant(0, 2f, 2f, 2f); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.ConstantRGB, OpAC.DstAAA, OpBD.ConstantOne); + asm.Mmsub(CC.T, Dest.PBR, OpAC.Temp2, OpBD.ConstantRGB, OpAC.SrcAAA, OpBD.ConstantOne); + asm.Mul(CC.T, Dest.PBR, OpAC.Temp0, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.PBR, OpBD.DstAAA); + asm.SetConstant(1, 0.5f, 0.5f, 0.5f); + asm.Mul(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantRGB); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedInvertRGB(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.DstAAA, OpAC.PBR, OpBD.DstRGB); + asm.Madd(CC.T, Dest.Temp0, OpAC.DstRGB, OpBD.OneMinusSrcAAA, OpAC.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLinearDodge(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLinearBurn(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.PBR, OpAC.PBR, OpBD.DstAAA, OpAC.DstRGB, OpBD.SrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Mmadd(CC.T, Dest.PBR, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.Temp0, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedVividLight(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantRGB); + asm.Sub(CC.GE, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Add(CC.GE, Dest.PBR, OpBD.PBR, OpBD.PBR); + asm.Rcp(CC.GE, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GE, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GE, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.LT, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Rcp(CC.LT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.LT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantOne); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedLinearLight(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 2f, 2f, 2f); + asm.Madd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedPinLight(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.Temp0, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Add(CC.LE, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Min(CC.LE, Dest.Temp0, OpAC.PBR, OpBD.Temp1); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHardMix(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mul(CC.LT, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.Temp2, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedRed(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mov(CC.T, Dest.Temp0, OpBD.DstRGB); + asm.Mov(CC.T, Dest.Temp0.R, OpBD.Temp2); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedGreen(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mov(CC.T, Dest.Temp0, OpBD.DstRGB); + asm.Mov(CC.T, Dest.Temp0.G, OpBD.Temp2); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedBlue(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.Temp2, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mov(CC.T, Dest.Temp0, OpBD.DstRGB); + asm.Mov(CC.T, Dest.Temp0.B, OpBD.Temp2); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslHue(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.SrcRGB, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp2.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp2); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.PBR, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslSaturation(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.PBR); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp1, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.Temp1.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp1); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.PBR, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslColor(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.SrcRGB, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.SrcRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.PBR, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenUncorrelatedHslLuminosity(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.SrcRGB, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp2.BBB, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Add(CC.T, Dest.Temp1, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp1, OpBD.Temp2); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp1, OpBD.Temp2, OpAC.Temp2, OpBD.Temp2); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp2); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Mmadd(CC.T, Dest.Temp1, OpAC.PBR, OpBD.OneMinusDstAAA, OpAC.DstRGB, OpBD.OneMinusSrcAAA); + asm.Mul(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.DstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl); + } + + private static FixedFunctionAlpha GenDisjointSrc(ref UcodeAssembler asm) + { + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenDisjointSrcOver(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.SrcRGB); + asm.Madd(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDstOver(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp1); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSrcIn(ref UcodeAssembler asm) + { + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Sub(CC.T, Dest.Temp1.RToA, OpBD.DstAAA, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSrcOut(ref UcodeAssembler asm) + { + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSrcAtop(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointDstAtop(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.Temp1, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenDisjointXor(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + asm.Min(CC.T, Dest.Temp1, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Add(CC.T, Dest.Temp1.RToA, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointPlus(ref UcodeAssembler asm) + { + asm.Mul(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.SrcAAA); + asm.Add(CC.T, Dest.Temp0, OpBD.DstRGB, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointMultiply(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointScreen(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointOverlay(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.SrcRGB, OpBD.Temp1, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDarken(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointLighten(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointColorDodge(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Rcp(CC.GT, Dest.PBR, OpAC.Temp0); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp1, OpBD.ConstantZero); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantZero); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointColorBurn(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.SrcRGB, OpBD.ConstantZero); + asm.Rcp(CC.GT, Dest.PBR, OpAC.SrcRGB); + asm.Mmsub(CC.GT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.ConstantOne, OpBD.Temp1); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHardLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.SrcRGB, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.SrcRGB, OpBD.Temp1, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointSoftLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(4, 0.25f, 0.25f, 0.25f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(0, 0.2605f, 0.2605f, 0.2605f); + asm.Mul(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(1, -0.7817f, -0.7817f, -0.7817f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(2, 0.3022f, 0.3022f, 0.3022f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(3, 0.2192f, 0.2192f, 0.2192f); + asm.Add(CC.GT, Dest.Temp0, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(5, 16f, 16f, 16f); + asm.Mul(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(6, 12f, 12f, 12f); + asm.Mmsub(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(7, 3f, 3f, 3f); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mmsub(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.ConstantOne, OpAC.Temp1, OpBD.Temp1); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointDifference(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.SrcRGB); + asm.Sub(CC.LT, Dest.Temp0, OpBD.SrcRGB, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointExclusion(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.Temp1); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointInvertRGB(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.Temp0, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenDisjointLinearDodge(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointLinearBurn(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointVividLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantRGB); + asm.Sub(CC.GE, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Add(CC.GE, Dest.PBR, OpBD.PBR, OpBD.PBR); + asm.Rcp(CC.GE, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GE, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GE, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.LT, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Rcp(CC.LT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.LT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantOne); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointLinearLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 2f, 2f, 2f); + asm.Madd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointPinLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.Temp0, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Add(CC.LE, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Min(CC.LE, Dest.Temp0, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHardMix(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mul(CC.LT, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslHue(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.SrcRGB, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp2.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp2); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslSaturation(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.PBR); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp1, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.Temp1.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp1); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslColor(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.SrcRGB, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.SrcRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenDisjointHslLuminosity(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.SrcRGB, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp2.BBB, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Add(CC.T, Dest.Temp1, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp1, OpBD.Temp2); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp1, OpBD.Temp2, OpAC.Temp2, OpBD.Temp2); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp2); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.OneMinusSrcAAA); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.Temp1, OpAC.PBR, OpBD.Temp0); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.SrcAAA, OpBD.OneMinusDstAAA); + asm.Madd(CC.T, Dest.Temp0, OpAC.PBR, OpBD.SrcRGB, OpAC.Temp0); + asm.Add(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Min(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenConjointSrc(ref UcodeAssembler asm) + { + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenConjointSrcOver(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.SrcRGB, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDstOver(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp1, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointSrcIn(ref UcodeAssembler asm) + { + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointSrcOut(ref UcodeAssembler asm) + { + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.Temp1.RToA, OpAC.PBR, OpBD.ConstantZero); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenConjointSrcAtop(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDstAtop(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl); + } + + private static FixedFunctionAlpha GenConjointXor(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.SrcAAA, OpBD.DstAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + asm.Sub(CC.T, Dest.Temp1.CC, OpBD.DstAAA, OpBD.SrcAAA); + asm.Sub(CC.LT, Dest.Temp1, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mov(CC.T, Dest.Temp1.RToA, OpBD.Temp1); + asm.Mov(CC.T, Dest.Temp0, OpBD.Temp0); + return FixedFunctionAlpha.Disabled; + } + + private static FixedFunctionAlpha GenConjointMultiply(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mul(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointScreen(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointOverlay(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.SrcRGB, OpBD.Temp1, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDarken(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLighten(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointColorDodge(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Rcp(CC.GT, Dest.PBR, OpAC.Temp0); + asm.Mul(CC.GT, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.Temp1, OpBD.ConstantZero); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointColorBurn(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.SrcRGB, OpBD.ConstantZero); + asm.Rcp(CC.GT, Dest.PBR, OpAC.SrcRGB); + asm.Mmsub(CC.GT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Max(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.ConstantOne, OpBD.Temp1); + asm.Mov(CC.LE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHardLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.SrcRGB, OpBD.ConstantRGB); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.SrcRGB, OpBD.Temp1, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.GT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointSoftLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(4, 0.25f, 0.25f, 0.25f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(0, 0.2605f, 0.2605f, 0.2605f); + asm.Mul(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(1, -0.7817f, -0.7817f, -0.7817f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(2, 0.3022f, 0.3022f, 0.3022f); + asm.Mmadd(CC.GT, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(3, 0.2192f, 0.2192f, 0.2192f); + asm.Add(CC.GT, Dest.Temp0, OpBD.PBR, OpBD.ConstantRGB); + asm.SetConstant(5, 16f, 16f, 16f); + asm.Mul(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(6, 12f, 12f, 12f); + asm.Mmsub(CC.LE, Dest.PBR, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.SetConstant(7, 3f, 3f, 3f); + asm.Mmadd(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mmsub(CC.LE, Dest.Temp0, OpAC.Temp1, OpBD.ConstantOne, OpAC.Temp1, OpBD.Temp1); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointDifference(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.SrcRGB); + asm.Sub(CC.LT, Dest.Temp0, OpBD.SrcRGB, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointExclusion(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.Temp1); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointInvertRGB(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mmsub(CC.T, Dest.Temp0, OpAC.SrcRGB, OpBD.ConstantOne, OpAC.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.PBR, OpAC.DstAAA, OpBD.SrcAAA); + asm.Mul(CC.T, Dest.Temp0, OpAC.Temp0, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Madd(CC.T, Dest.Temp0, OpAC.Temp1, OpBD.PBR, OpAC.Temp0); + return new FixedFunctionAlpha(BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLinearDodge(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLinearBurn(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointVividLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.5f, 0.5f, 0.5f); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantRGB); + asm.Sub(CC.GE, Dest.PBR, OpBD.ConstantOne, OpBD.SrcRGB); + asm.Add(CC.GE, Dest.PBR, OpBD.PBR, OpBD.PBR); + asm.Rcp(CC.GE, Dest.PBR, OpAC.PBR); + asm.Mul(CC.GE, Dest.PBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GE, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Add(CC.LT, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Rcp(CC.LT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.LT, Dest.PBR, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.LT, Dest.Temp0, OpBD.ConstantOne, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantZero); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcRGB, OpBD.ConstantOne); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointLinearLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 2f, 2f, 2f); + asm.Madd(CC.T, Dest.PBR, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Max(CC.T, Dest.PBR, OpAC.PBR, OpBD.ConstantZero); + asm.Min(CC.T, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointPinLight(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.Temp0, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.ConstantZero); + asm.Add(CC.LE, Dest.PBR, OpBD.SrcRGB, OpBD.SrcRGB); + asm.Min(CC.LE, Dest.Temp0, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHardMix(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Add(CC.T, Dest.PBR, OpBD.SrcRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Mul(CC.LT, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Mov(CC.GE, Dest.Temp0, OpBD.ConstantOne); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslHue(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.SrcRGB, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.Temp2.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp2); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslSaturation(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.PBR); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.Temp0.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.Temp0.CC, OpBD.PBR, OpBD.Temp0); + asm.Rcp(CC.GT, Dest.Temp0, OpAC.Temp0); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.Temp1, OpAC.Temp0, OpBD.PBR); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Min(CC.GT, Dest.Temp1.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mov(CC.GT, Dest.PBR.GBR, OpBD.SrcRGB); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Max(CC.GT, Dest.PBR.GBR, OpAC.PBR, OpBD.SrcRGB); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp0, OpBD.Temp1); + asm.Mul(CC.LE, Dest.Temp0, OpAC.SrcAAA, OpBD.ConstantZero); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp0, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp0, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslColor(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.PBR, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp1.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.SrcRGB, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Add(CC.T, Dest.Temp2, OpBD.SrcRGB, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp1); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp2, OpBD.Temp1); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp1); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp2); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp1, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp2, OpBD.Temp1, OpAC.Temp1, OpBD.Temp1); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp1); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + + private static FixedFunctionAlpha GenConjointHslLuminosity(ref UcodeAssembler asm) + { + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.SetConstant(0, 0.3f, 0.59f, 0.11f); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.SrcRGB, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.Temp2.BBB, OpAC.SrcRGB, OpBD.ConstantRGB, OpAC.PBR); + asm.Mul(CC.T, Dest.PBR.RRR, OpAC.Temp1, OpBD.ConstantRGB); + asm.Madd(CC.T, Dest.PBR.GGG, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Madd(CC.T, Dest.PBR.BBB, OpAC.Temp1, OpBD.ConstantRGB, OpAC.PBR); + asm.Sub(CC.T, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Add(CC.T, Dest.Temp1, OpBD.Temp1, OpBD.PBR); + asm.Mov(CC.T, Dest.Temp0, OpBD.PBR); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Max(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.PBR, OpBD.ConstantOne); + asm.Add(CC.GT, Dest.PBR, OpBD.PBR, OpBD.ConstantOne); + asm.Sub(CC.GT, Dest.PBR, OpBD.PBR, OpBD.Temp2); + asm.Rcp(CC.GT, Dest.PBR, OpAC.PBR); + asm.Mmsub(CC.GT, Dest.Temp0, OpAC.PBR, OpBD.ConstantOne, OpAC.PBR, OpBD.Temp2); + asm.Sub(CC.GT, Dest.PBR, OpBD.Temp1, OpBD.Temp2); + asm.Madd(CC.GT, Dest.Temp0, OpAC.Temp0, OpBD.PBR, OpAC.Temp2); + asm.Mov(CC.T, Dest.PBR.GBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR, OpAC.PBR, OpBD.Temp1); + asm.Min(CC.T, Dest.PBR.GBR.CC, OpAC.PBR, OpBD.Temp1); + asm.Sub(CC.LT, Dest.PBR, OpBD.Temp2, OpBD.PBR); + asm.Rcp(CC.LT, Dest.Temp0, OpAC.PBR); + asm.Mmsub(CC.LT, Dest.PBR, OpAC.Temp1, OpBD.Temp2, OpAC.Temp2, OpBD.Temp2); + asm.Madd(CC.LT, Dest.Temp0, OpAC.PBR, OpBD.Temp0, OpAC.Temp2); + asm.Rcp(CC.T, Dest.PBR, OpAC.DstAAA); + asm.Mul(CC.T, Dest.Temp1, OpAC.DstRGB, OpBD.PBR); + asm.Sub(CC.T, Dest.PBR.CC, OpBD.SrcAAA, OpBD.DstAAA); + asm.Mmadd(CC.GE, Dest.Temp0, OpAC.Temp0, OpBD.DstAAA, OpAC.SrcRGB, OpBD.PBR); + asm.Sub(CC.LT, Dest.PBR, OpBD.DstAAA, OpBD.SrcAAA); + asm.Mmadd(CC.LT, Dest.Temp0, OpAC.Temp0, OpBD.SrcAAA, OpAC.Temp1, OpBD.PBR); + return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs new file mode 100644 index 000000000..8072c6af2 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs @@ -0,0 +1,115 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender +{ + /// <summary> + /// Advanced blend manager. + /// </summary> + class AdvancedBlendManager + { + private const int InstructionRamSize = 128; + private const int InstructionRamSizeMask = InstructionRamSize - 1; + + private readonly DeviceStateWithShadow<ThreedClassState> _state; + + private readonly uint[] _code; + private int _ip; + + /// <summary> + /// Creates a new instance of the advanced blend manager. + /// </summary> + /// <param name="state">GPU state of the channel owning this manager</param> + public AdvancedBlendManager(DeviceStateWithShadow<ThreedClassState> state) + { + _state = state; + _code = new uint[InstructionRamSize]; + } + + /// <summary> + /// Sets the start offset of the blend microcode in memory. + /// </summary> + /// <param name="argument">Method call argument</param> + public void LoadBlendUcodeStart(int argument) + { + _ip = argument; + } + + /// <summary> + /// Pushes one word of blend microcode. + /// </summary> + /// <param name="argument">Method call argument</param> + public void LoadBlendUcodeInstruction(int argument) + { + _code[_ip++ & InstructionRamSizeMask] = (uint)argument; + } + + /// <summary> + /// Tries to identify the current advanced blend function being used, + /// given the current state and microcode that was uploaded. + /// </summary> + /// <param name="descriptor">Advanced blend descriptor</param> + /// <returns>True if the function was found, false otherwise</returns> + public bool TryGetAdvancedBlend(out AdvancedBlendDescriptor descriptor) + { + Span<uint> currentCode = new Span<uint>(_code); + byte codeLength = (byte)_state.State.BlendUcodeSize; + + if (currentCode.Length > codeLength) + { + currentCode = currentCode.Slice(0, codeLength); + } + + Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast<uint, byte>(currentCode)); + + descriptor = default; + + if (!AdvancedBlendPreGenTable.Entries.TryGetValue(hash, out var entry)) + { + return false; + } + + if (entry.Constants != null) + { + bool constantsMatch = true; + + for (int i = 0; i < entry.Constants.Length; i++) + { + RgbFloat constant = entry.Constants[i]; + RgbHalf constant2 = _state.State.BlendUcodeConstants[i]; + + if ((Half)constant.R != constant2.UnpackR() || + (Half)constant.G != constant2.UnpackG() || + (Half)constant.B != constant2.UnpackB()) + { + constantsMatch = false; + break; + } + } + + if (!constantsMatch) + { + return false; + } + } + + if (entry.Alpha.Enable != _state.State.BlendUcodeEnable) + { + return false; + } + + if (entry.Alpha.Enable == BlendUcodeEnable.EnableRGBA && + (entry.Alpha.AlphaOp != _state.State.BlendStateCommon.AlphaOp || + entry.Alpha.AlphaSrcFactor != _state.State.BlendStateCommon.AlphaSrcFactor || + entry.Alpha.AlphaDstFactor != _state.State.BlendStateCommon.AlphaDstFactor)) + { + return false; + } + + descriptor = new AdvancedBlendDescriptor(entry.Op, entry.Overlap, entry.SrcPreMultiplied); + return true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs new file mode 100644 index 000000000..d35d8abf4 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs @@ -0,0 +1,273 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender +{ + /// <summary> + /// Advanced blend function entry. + /// </summary> + struct AdvancedBlendEntry + { + /// <summary> + /// Advanced blend operation. + /// </summary> + public AdvancedBlendOp Op { get; } + + /// <summary> + /// Advanced blend overlap mode. + /// </summary> + public AdvancedBlendOverlap Overlap { get; } + + /// <summary> + /// Whenever the source input is pre-multiplied. + /// </summary> + public bool SrcPreMultiplied { get; } + + /// <summary> + /// Constants used by the microcode. + /// </summary> + public RgbFloat[] Constants { get; } + + /// <summary> + /// Fixed function alpha state. + /// </summary> + public FixedFunctionAlpha Alpha { get; } + + /// <summary> + /// Creates a new advanced blend function entry. + /// </summary> + /// <param name="op">Advanced blend operation</param> + /// <param name="overlap">Advanced blend overlap mode</param> + /// <param name="srcPreMultiplied">Whenever the source input is pre-multiplied</param> + /// <param name="constants">Constants used by the microcode</param> + /// <param name="alpha">Fixed function alpha state</param> + public AdvancedBlendEntry( + AdvancedBlendOp op, + AdvancedBlendOverlap overlap, + bool srcPreMultiplied, + RgbFloat[] constants, + FixedFunctionAlpha alpha) + { + Op = op; + Overlap = overlap; + SrcPreMultiplied = srcPreMultiplied; + Constants = constants; + Alpha = alpha; + } + } + + /// <summary> + /// Pre-generated hash table with advanced blend functions used by the driver. + /// </summary> + static class AdvancedBlendPreGenTable + { + /// <summary> + /// Advanced blend functions dictionary. + /// </summary> + public static readonly IReadOnlyDictionary<Hash128, AdvancedBlendEntry> Entries = new Dictionary<Hash128, AdvancedBlendEntry>() + { + { new Hash128(0x19ECF57B83DE31F7, 0x5BAE759246F264C0), new AdvancedBlendEntry(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xDE1B14A356A1A9ED, 0x59D803593C607C1D), new AdvancedBlendEntry(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x1A3C3A6D32DEC368, 0xBCAE519EC6AAA045), new AdvancedBlendEntry(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x6FD380261A63B240, 0x17C3B335DBB9E3DB), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x1D39164823D3A2D1, 0xC45350959CE1C8FB), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x18DF09FF53B129FE, 0xC02EDA33C36019F6), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x5973E583271EBF06, 0x711497D75D1272E0), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x4759E0E5DA54D5E8, 0x1FDD57C0C38AFA1F), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x337684D43CCE97FA, 0x0139E30CC529E1C9), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xDA59E85D8428992D, 0x1D3D7C64C9EF0132), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x9455B949298CE805, 0xE73D3301518BE98A), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xBDD3B4DEDBE336AA, 0xBFA4DCD50D535DEE), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x22D4E970A028649A, 0x4F3FCB055FCED965), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xA346A91311D72114, 0x151A27A3FB0A1904), new AdvancedBlendEntry(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x8A307241061FACD6, 0xA39D1826440B8EE7), new AdvancedBlendEntry(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xB3BE569485EFFFE0, 0x0BA4E269B3CFB165), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x36FCA3277DC11822, 0x2BC0F6CAC2029672), new AdvancedBlendEntry(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(2f, 2f, 2f), new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x4A6226AF2DE9BD7F, 0xEB890D7DA716F73A), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0xF364CAA94E160FEB, 0xBF364512C72A3797), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x6BF791AB4AC19C87, 0x6FA17A994EA0FCDE), new AdvancedBlendEntry(AdvancedBlendOp.InvertOvg, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x053C75A0AE0BB222, 0x03C791FEEB59754C), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x25762AB40B6CBDE9, 0x595E9A968AC4F01C), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xC2D05E2DBE16955D, 0xB8659C7A3FCFA7CE), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x223F220B8F74CBFB, 0xD3DD19D7C39209A5), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xD0DAE57A9F1FE78A, 0x353796BCFB8CE30B), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x601C8CBEC07FF8FF, 0xB8E22882360E8695), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x3A55B7B78C76A7A8, 0x206F503B2D9FFEAA), new AdvancedBlendEntry(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x80BC65C7831388E5, 0xC652457B2C766AEC), new AdvancedBlendEntry(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x3D3A912E5833EE13, 0x307895951349EE33), new AdvancedBlendEntry(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x289105BE92E81803, 0xFD8F1F03D15C53B4), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x007AE3BD140764EB, 0x0EE05A0D2E80BBAE), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x77F7EE0DB3FDDB96, 0xDEA47C881306DB3E), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x66F4E9A7D73CA157, 0x1486058A177DB11C), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x593E9F331612D618, 0x9D217BEFA4EB919A), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x0A5194C5E6891106, 0xDD8EC6586106557C), new AdvancedBlendEntry(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x8D77173D5E06E916, 0x06AB190E7D10F4D4), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x655B4EBC148981DA, 0x455999EF2B9BD28A), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x98F5437D5F518929, 0xBFF4A6E83183DB63), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x6ADDEFE3B9CEF2FD, 0xB6F6272AFECB1AAB), new AdvancedBlendEntry(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x80953F0953BF05B1, 0xD59ABFAA34F8196F), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xA401D9AA2A39C121, 0xFC0C8005C22AD7E3), new AdvancedBlendEntry(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x06274FB7CA9CDD22, 0x6CE8188B1A9AB6EF), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x0B079BE7F7F70817, 0xB72E7736CA51E321), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x66215C99403CEDDE, 0x900B733D62204C48), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x12DEF2AD900CAD6C, 0x58CF5CC3004910DF), new AdvancedBlendEntry(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x272BA3A49F64DAE4, 0xAC70B96C00A99EAF), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x206C34AAA7D3F545, 0xDA4B30CACAA483A0), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x3D93494920D257BE, 0xDCC573BE1F5F4449), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x0D7417D80191107B, 0xEAF40547827E005F), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xEC1B03E8C883F9C9, 0x2D3CA044C58C01B4), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x58A19A0135D68B31, 0x82F35B97AED068E5), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x20489F9AB36CC0E3, 0x20499874219E35EE), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xBB176935E5EE05BF, 0x95B26D4D30EA7A14), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x5FF9393C908ACFED, 0x068B0BD875773ABF), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x03181F8711C9802C, 0x6B02C7C6B224FE7B), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x2EE2209021F6B977, 0xF3AFA1491B8B89FC), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xD8BA4DD2EDE4DC9E, 0x01006114977CF715), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0xD156B99835A2D8ED, 0x2D0BEE9E135EA7A7), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x20CE8C898ED4BE27, 0x1514900B6F5E8F66), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xCDE5F743820BA2D9, 0x917845FE2ECB083D), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xEB03DF4A0C1D14CD, 0xBAE2E831C6E8FFE4), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x1DC9E49AABC779AC, 0x4053A1441EB713D3), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xFBDEF776248F7B3E, 0xE05EEFD65AC47CB7), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x415A1A48E03AA6E7, 0x046D7EE33CA46B9A), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x59A6901EC9BB2041, 0x2F3E19CE5EEC3EBE), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x044B2B6E105221DA, 0x3089BBC033F994AF), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x374A5A24AA8E6CC5, 0x29930FAA6215FA2B), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x30CD0F7AF0CF26F9, 0x06CCA6744DE7DCF5), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x1A6C9A1F6FE494A5, 0xA0CFAF77617E54DD), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x081AF6DAAB1C8717, 0xBFEDCE59AE3DC9AC), new AdvancedBlendEntry(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x3518E44573AB68BA, 0xC96EE71AF9F8F546), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xF89E81FE8D73C96F, 0x4583A04577A0F21C), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xDF4026421CB61119, 0x14115A1F5139AFC7), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x91A20262C3E3A695, 0x0B3A102BFCDC6B1C), new AdvancedBlendEntry(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x44F4C7CCFEB9EBFA, 0xF68394E6D56E5C2F), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xB89F17C7021E9760, 0x430357EE0F7188EF), new AdvancedBlendEntry(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xDA2D20EA4242B8A0, 0x0D1EC05B72E3838F), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x855DFEE1208D11B9, 0x77C6E3DDCFE30B85), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x9B3808439683FD58, 0x123DCBE4705AB25E), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xA42CF045C248A00A, 0x0C6C63C24EA0B0C1), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x320A83B6D00C8059, 0x796EDAB3EB7314BC), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x45253AC9ABFFC613, 0x8F92EA70195FB573), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x1A5D263B588274B6, 0x167D305F6C794179), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x709C1A837FE966AC, 0x75D8CE49E8A78EDB), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x8265C26F85E4145F, 0x932E6CCBF37CB600), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x3F252B3FEF983F27, 0x9370D7EEFEFA1A9E), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x66A334A4AEA41078, 0xCB52254E1E395231), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xFDD05C53B25F0035, 0xB7E3ECEE166C222F), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x25D932A77FFED81A, 0xA50D797B0FCA94E8), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x4A953B6F5F7D341C, 0xDC05CFB50DDB5DC1), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x838CB660C4F41F6D, 0x9E7D958697543495), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x4DF6EC1348A8F797, 0xA128E0CD69DB5A64), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x178CDFAB9A015295, 0x2BF40EA72E596D57), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x338FC99050E56AFD, 0x2AF41CF82BE602BF), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x62E02ED60D1E978E, 0xBF726B3E68C11E4D), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xFBAF92DD4C101502, 0x7AF2EDA6596B819D), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x0EF1241F65D4B50A, 0xE8D85DFA6AEDDB84), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x77FE024B5C9D4A18, 0xF19D48A932F6860F), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x9C88CBFA2E09D857, 0x0A0361704CBEEE1D), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x5B94127FA190E640, 0x8D1FEFF837A91268), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xB9C9105B7E063DDB, 0xF6A70E1D511B96FD), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xF0751AAE332B3ED1, 0xC40146F5C83C2533), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x579EB12F595F75AD, 0x151BF0504703B81B), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xF9CA152C03AC8C62, 0x1581336205E5CF47), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.DstAlphaGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x98ACD8BB5E195D0F, 0x91F937672BE899F0), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.ZeroGl)) }, + { new Hash128(0xBF97F10FC301F44C, 0x75721789F0D48548), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x1B982263B8B08A10, 0x3350C76E2E1B27DF), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0xFF20AC79F64EDED8, 0xAF9025B2D97B9273), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x9FFD986600FB112F, 0x384FDDF4E060139A), new AdvancedBlendEntry(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x0425E40B5B8B3B52, 0x5880CBED7CAB631C), new AdvancedBlendEntry(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x16DAC8593F28623A, 0x233DBC82325B8AED), new AdvancedBlendEntry(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xB37E5F234B9F0948, 0xD5F957A2ECD98FD6), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xCA0FDADD1D20DBE3, 0x1A5C15CCBF1AC538), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x1C48304D73A9DF3A, 0x891DB93FA36E3450), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x53200F2279B7FA39, 0x051C2462EBF6789C), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xB88BFB80714DCD5C, 0xEBD6938D744E6A41), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xE33DC2A25FC1A976, 0x08B3DBB1F3027D45), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xCE97E71615370316, 0xE131AE49D3A4D62B), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xE059FD265149B256, 0x94AF817AC348F61F), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x16D31333D477E231, 0x9A98AAC84F72CC62), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x47FC3B0776366D3C, 0xE96D9BD83B277874), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x7230401E3FEA1F3B, 0xF0D15F05D3D1E309), new AdvancedBlendEntry(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x188212F9303742F5, 0x100C51CB96E03591), new AdvancedBlendEntry(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x52B755D296B44DC5, 0x4003B87275625973), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xD873ED973ADF7EAD, 0x73E68B57D92034E7), new AdvancedBlendEntry(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(2f, 2f, 2f), new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x471F9FA34B945ACB, 0x10524D1410B3C402), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x99F569454EA0EF32, 0x6FC70A8B3A07DC8B), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x5AD55F950067AC7E, 0x4BA60A4FBABDD0AC), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x03FF2C858C9C4C5B, 0xE95AE7F561FB60E9), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x6DC0E510C7BCF9D2, 0xAE805D7CECDCB5C1), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x44832332CED5C054, 0x2F8D5536C085B30A), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x4AB4D387618AC51F, 0x495B46E0555F4B32), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x99282B49405A01A8, 0xD6FA93F864F24A8E), new AdvancedBlendEntry(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x37B30C1064FBD23E, 0x5D068366F42317C2), new AdvancedBlendEntry(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x760FAE9D59E04BC2, 0xA40AD483EA01435E), new AdvancedBlendEntry(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0xE786950FD9D1C6EF, 0xF9FDD5AF6451D239), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x052458BB4788B0CA, 0x8AC58FDCA1F45EF5), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x6AFC3837D1D31920, 0xB9D49C2FE49642C6), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0xAFC2911949317E01, 0xD5B63636F5CB3422), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) }, + { new Hash128(0x13B46DF507CC2C53, 0x86DE26517E6BF0A7), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x5C372442474BE410, 0x79ECD3C0C496EF2E), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x74AAB45DBF5336E9, 0x01BFC4E181DAD442), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x43239E282A36C85C, 0x36FB65560E46AD0F), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x1A3BA8A7583B8F7A, 0xE64E41D548033180), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x32BBB9859E9B565D, 0x3D5CE94FE55F18B5), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0xD947A0766AE3C0FC, 0x391E5D53E86F4ED6), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0xBD9A7C08BDFD8CE6, 0x905407634901355E), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x8395475BCB0D7A8C, 0x48AF5DD501D44A70), new AdvancedBlendEntry(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x80AAC23FEBD4A3E5, 0xEA8C70F0B4DE52DE), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x2F3AD1B0F1B3FD09, 0xC0EBC784BFAB8EA3), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x52B54032F2F70BFF, 0xC941D6FDED674765), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xCA7B86F72EC6A99B, 0x55868A131AFE359E), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x377919B60BD133CA, 0x0FD611627664EF40), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x9D4A0C5EE1153887, 0x7B869EBA218C589B), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x311F2A858545D123, 0xB4D09C802480AD62), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xCF78AA6A83AFA689, 0x9DC48B0C2182A3E1), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xC3018CD6F1CF62D1, 0x016E32DD9087B1BB), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x9CB62CE0E956EE29, 0x0FB67F503E60B3AD), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x3589A13C16EF3BFA, 0x15B29BFC91F3BDFB), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x3502CA5FB7529917, 0xFA51BFD0D1688071), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x62ADC25AD6D0A923, 0x76CB6D238276D3A3), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x09FDEB1116A9D52C, 0x85BB8627CD5C2733), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x0709FED1B65E18EB, 0x5BC3AA4D99EC19CF), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xB18D28AE5DE4C723, 0xE820AA2B75C9C02E), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x6743C51621497480, 0x4B164E40858834AE), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x63D1E181E34A2944, 0x1AE292C9D9F12819), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x079523298250BFF6, 0xC0C793510603CDB5), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x4C9D0A973C805EA6, 0xD1FF59AD5156B93C), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x1E914678F3057BCD, 0xD503AE389C12D229), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0x9FDBADE5556C5311, 0x03F0CBC798FC5C94), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xE39451534635403C, 0x606CC1CA1F452388), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x1D39F0F0A1008AA6, 0xBFDF2B97E6C3F125), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xDB81BED30D5BDBEA, 0xAF0B2856EB93AD2C), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x83F69CCF1D0A79B6, 0x70D31332797430AC), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x7B87F807AB7A8F5C, 0x1241A2A01FB31771), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xF557172E20D5272D, 0xC1961F8C7A5D2820), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0xA8476B3944DBBC9B, 0x84A2F6AF97B15FDF), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) }, + { new Hash128(0x3259602B55414DA3, 0x72AACCC00B5A9D10), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) }, + { new Hash128(0xC0CB8C10F36EDCD6, 0x8C2D088AD8191E1C), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x81806C451C6255EF, 0x5AA8AC9A08941A15), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xE55A6537F4568198, 0xCA8735390B799B19), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x5C044BA14536DDA3, 0xBCE0123ED7D510EC), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x6788346C405BE130, 0x372A4BB199C01F9F), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x510EDC2A34E2856B, 0xE1727A407E294254), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x4B7BE01BD398C7A8, 0x5BFF79BC00672C18), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x213B43845540CFEC, 0xDA857411CF1CCFCE), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x765AFA6732E783F1, 0x8F1CABF1BC78A014), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xA4A5DE1CC06F6CB1, 0xA0634A0011001709), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x81F32BD8816EA796, 0x697EE86683165170), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xB870C209EAA5F092, 0xAF5FD923909CAA1F), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) }, + { new Hash128(0x3649A9F5C936FB83, 0xDD7C834897AA182A), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xD72A2B1097A5995C, 0x3D41B2763A913654), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x551E212B9F6C454A, 0xB0DFA05BEB3C37FA), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x681B5A313B7416BF, 0xCB1CBAEEB4D81500), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x9343A18BD4B16777, 0xEDB4AC1C8972C3A4), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xC960BF6D8519DE28, 0x78D8557FD405D119), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x65A7B01FDC73A46C, 0x297E096ED5CC4D8A), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0xD9C99BA4A6CDC13B, 0x3CFF0ACEDC2EE150), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x6BC00DA6EB922BD1, 0x5FD4C11F2A685234), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + { new Hash128(0x8652300E32D93050, 0x9460E7B449132371), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, + }; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs new file mode 100644 index 000000000..f06b4bf74 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs @@ -0,0 +1,126 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender +{ + /// <summary> + /// Fixed function alpha state used for a advanced blend function. + /// </summary> + struct FixedFunctionAlpha + { + /// <summary> + /// Fixed function alpha state with alpha blending disabled. + /// </summary> + public static FixedFunctionAlpha Disabled => new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, default, default, default); + + /// <summary> + /// Individual enable bits for the RGB and alpha components. + /// </summary> + public BlendUcodeEnable Enable { get; } + + /// <summary> + /// Alpha blend operation. + /// </summary> + public BlendOp AlphaOp { get; } + + /// <summary> + /// Value multiplied with the blend source operand. + /// </summary> + public BlendFactor AlphaSrcFactor { get; } + + /// <summary> + /// Value multiplied with the blend destination operand. + /// </summary> + public BlendFactor AlphaDstFactor { get; } + + /// <summary> + /// Creates a new blend fixed function alpha state. + /// </summary> + /// <param name="enable">Individual enable bits for the RGB and alpha components</param> + /// <param name="alphaOp">Alpha blend operation</param> + /// <param name="alphaSrc">Value multiplied with the blend source operand</param> + /// <param name="alphaDst">Value multiplied with the blend destination operand</param> + public FixedFunctionAlpha(BlendUcodeEnable enable, BlendOp alphaOp, BlendFactor alphaSrc, BlendFactor alphaDst) + { + Enable = enable; + AlphaOp = alphaOp; + AlphaSrcFactor = alphaSrc; + AlphaDstFactor = alphaDst; + } + + /// <summary> + /// Creates a new blend fixed function alpha state. + /// </summary> + /// <param name="alphaOp">Alpha blend operation</param> + /// <param name="alphaSrc">Value multiplied with the blend source operand</param> + /// <param name="alphaDst">Value multiplied with the blend destination operand</param> + public FixedFunctionAlpha(BlendOp alphaOp, BlendFactor alphaSrc, BlendFactor alphaDst) : this(BlendUcodeEnable.EnableRGB, alphaOp, alphaSrc, alphaDst) + { + } + } + + /// <summary> + /// Blend microcode assembly function delegate. + /// </summary> + /// <param name="asm">Assembler</param> + /// <returns>Fixed function alpha state for the microcode</returns> + delegate FixedFunctionAlpha GenUcodeFunc(ref UcodeAssembler asm); + + /// <summary> + /// Advanced blend microcode state. + /// </summary> + struct AdvancedBlendUcode + { + /// <summary> + /// Advanced blend operation. + /// </summary> + public AdvancedBlendOp Op { get; } + + /// <summary> + /// Advanced blend overlap mode. + /// </summary> + public AdvancedBlendOverlap Overlap { get; } + + /// <summary> + /// Whenever the source input is pre-multiplied. + /// </summary> + public bool SrcPreMultiplied { get; } + + /// <summary> + /// Fixed function alpha state. + /// </summary> + public FixedFunctionAlpha Alpha { get; } + + /// <summary> + /// Microcode. + /// </summary> + public uint[] Code { get; } + + /// <summary> + /// Constants used by the microcode. + /// </summary> + public RgbFloat[] Constants { get; } + + /// <summary> + /// Creates a new advanced blend state. + /// </summary> + /// <param name="op">Advanced blend operation</param> + /// <param name="overlap">Advanced blend overlap mode</param> + /// <param name="srcPreMultiplied">Whenever the source input is pre-multiplied</param> + /// <param name="genFunc">Function that will generate the advanced blend microcode</param> + public AdvancedBlendUcode( + AdvancedBlendOp op, + AdvancedBlendOverlap overlap, + bool srcPreMultiplied, + GenUcodeFunc genFunc) + { + Op = op; + Overlap = overlap; + SrcPreMultiplied = srcPreMultiplied; + + UcodeAssembler asm = new UcodeAssembler(); + Alpha = genFunc(ref asm); + Code = asm.GetCode(); + Constants = asm.GetConstants(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs new file mode 100644 index 000000000..f854787e0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender +{ + /// <summary> + /// Blend microcode instruction. + /// </summary> + enum Instruction + { + Mmadd = 0, + Mmsub = 1, + Min = 2, + Max = 3, + Rcp = 4, + Add = 5, + Sub = 6 + } + + /// <summary> + /// Blend microcode condition code. + /// </summary> + enum CC + { + F = 0, + T = 1, + EQ = 2, + NE = 3, + LT = 4, + LE = 5, + GT = 6, + GE = 7 + } + + /// <summary> + /// Blend microcode opend B or D value. + /// </summary> + enum OpBD + { + ConstantZero = 0x0, + ConstantOne = 0x1, + SrcRGB = 0x2, + SrcAAA = 0x3, + OneMinusSrcAAA = 0x4, + DstRGB = 0x5, + DstAAA = 0x6, + OneMinusDstAAA = 0x7, + Temp0 = 0x9, + Temp1 = 0xa, + Temp2 = 0xb, + PBR = 0xc, + ConstantRGB = 0xd + } + + /// <summary> + /// Blend microcode operand A or C value. + /// </summary> + enum OpAC + { + SrcRGB = 0, + DstRGB = 1, + SrcAAA = 2, + DstAAA = 3, + Temp0 = 4, + Temp1 = 5, + Temp2 = 6, + PBR = 7 + } + + /// <summary> + /// Blend microcode destination operand. + /// </summary> + enum OpDst + { + Temp0 = 0, + Temp1 = 1, + Temp2 = 2, + PBR = 3 + } + + /// <summary> + /// Blend microcode input swizzle. + /// </summary> + enum Swizzle + { + RGB = 0, + GBR = 1, + RRR = 2, + GGG = 3, + BBB = 4, + RToA = 5 + } + + /// <summary> + /// Blend microcode output components. + /// </summary> + enum WriteMask + { + RGB = 0, + R = 1, + G = 2, + B = 3 + } + + /// <summary> + /// Floating-point RGB color values. + /// </summary> + struct RgbFloat + { + /// <summary> + /// Red component value. + /// </summary> + public float R { get; } + + /// <summary> + /// Green component value. + /// </summary> + public float G { get; } + + /// <summary> + /// Blue component value. + /// </summary> + public float B { get; } + + /// <summary> + /// Creates a new floating-point RGB value. + /// </summary> + /// <param name="r">Red component value</param> + /// <param name="g">Green component value</param> + /// <param name="b">Blue component value</param> + public RgbFloat(float r, float g, float b) + { + R = r; + G = g; + B = b; + } + } + + /// <summary> + /// Blend microcode destination operand, including swizzle, write mask and condition code update flag. + /// </summary> + struct Dest + { + public static Dest Temp0 => new Dest(OpDst.Temp0, Swizzle.RGB, WriteMask.RGB, false); + public static Dest Temp1 => new Dest(OpDst.Temp1, Swizzle.RGB, WriteMask.RGB, false); + public static Dest Temp2 => new Dest(OpDst.Temp2, Swizzle.RGB, WriteMask.RGB, false); + public static Dest PBR => new Dest(OpDst.PBR, Swizzle.RGB, WriteMask.RGB, false); + + public Dest GBR => new Dest(Dst, Swizzle.GBR, WriteMask, WriteCC); + public Dest RRR => new Dest(Dst, Swizzle.RRR, WriteMask, WriteCC); + public Dest GGG => new Dest(Dst, Swizzle.GGG, WriteMask, WriteCC); + public Dest BBB => new Dest(Dst, Swizzle.BBB, WriteMask, WriteCC); + public Dest RToA => new Dest(Dst, Swizzle.RToA, WriteMask, WriteCC); + + public Dest R => new Dest(Dst, Swizzle, WriteMask.R, WriteCC); + public Dest G => new Dest(Dst, Swizzle, WriteMask.G, WriteCC); + public Dest B => new Dest(Dst, Swizzle, WriteMask.B, WriteCC); + + public Dest CC => new Dest(Dst, Swizzle, WriteMask, true); + + public OpDst Dst { get; } + public Swizzle Swizzle { get; } + public WriteMask WriteMask { get; } + public bool WriteCC { get; } + + /// <summary> + /// Creates a new blend microcode destination operand. + /// </summary> + /// <param name="dst">Operand</param> + /// <param name="swizzle">Swizzle</param> + /// <param name="writeMask">Write maks</param> + /// <param name="writeCC">Indicates if condition codes should be updated</param> + public Dest(OpDst dst, Swizzle swizzle, WriteMask writeMask, bool writeCC) + { + Dst = dst; + Swizzle = swizzle; + WriteMask = writeMask; + WriteCC = writeCC; + } + } + + /// <summary> + /// Blend microcode operaiton. + /// </summary> + struct UcodeOp + { + public readonly uint Word; + + /// <summary> + /// Creates a new blend microcode operation. + /// </summary> + /// <param name="cc">Condition code that controls whenever the operation is executed or not</param> + /// <param name="inst">Instruction</param> + /// <param name="constIndex">Index on the constant table of the constant used by any constant operand</param> + /// <param name="dest">Destination operand</param> + /// <param name="srcA">First input operand</param> + /// <param name="srcB">Second input operand</param> + /// <param name="srcC">Third input operand</param> + /// <param name="srcD">Fourth input operand</param> + public UcodeOp(CC cc, Instruction inst, int constIndex, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) + { + Word = (uint)cc | + ((uint)inst << 3) | + ((uint)constIndex << 6) | + ((uint)srcA << 9) | + ((uint)srcB << 12) | + ((uint)srcC << 16) | + ((uint)srcD << 19) | + ((uint)dest.Swizzle << 23) | + ((uint)dest.WriteMask << 26) | + ((uint)dest.Dst << 28) | + (dest.WriteCC ? (1u << 31) : 0); + } + } + + /// <summary> + /// Blend microcode assembler. + /// </summary> + struct UcodeAssembler + { + private List<uint> _code; + private RgbFloat[] _constants; + private int _constantIndex; + + public void Mul(CC cc, Dest dest, OpAC srcA, OpBD srcB) + { + Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero); + } + + public void Madd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC) + { + Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, OpBD.ConstantOne); + } + + public void Mmadd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) + { + Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, srcD); + } + + public void Mmsub(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) + { + Assemble(cc, Instruction.Mmsub, dest, srcA, srcB, srcC, srcD); + } + + public void Min(CC cc, Dest dest, OpAC srcA, OpBD srcB) + { + Assemble(cc, Instruction.Min, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero); + } + + public void Max(CC cc, Dest dest, OpAC srcA, OpBD srcB) + { + Assemble(cc, Instruction.Max, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero); + } + + public void Rcp(CC cc, Dest dest, OpAC srcA) + { + Assemble(cc, Instruction.Rcp, dest, srcA, OpBD.ConstantZero, OpAC.SrcRGB, OpBD.ConstantZero); + } + + public void Mov(CC cc, Dest dest, OpBD srcB) + { + Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, OpBD.ConstantZero); + } + + public void Add(CC cc, Dest dest, OpBD srcB, OpBD srcD) + { + Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD); + } + + public void Sub(CC cc, Dest dest, OpBD srcB, OpBD srcD) + { + Assemble(cc, Instruction.Sub, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD); + } + + private void Assemble(CC cc, Instruction inst, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) + { + (_code ??= new List<uint>()).Add(new UcodeOp(cc, inst, _constantIndex, dest, srcA, srcB, srcC, srcD).Word); + } + + public void SetConstant(int index, float r, float g, float b) + { + if (_constants == null) + { + _constants = new RgbFloat[index + 1]; + } + else if (_constants.Length <= index) + { + Array.Resize(ref _constants, index + 1); + } + + _constants[index] = new RgbFloat(r, g, b); + _constantIndex = index; + } + + public uint[] GetCode() + { + return _code?.ToArray(); + } + + public RgbFloat[] GetConstants() + { + return _constants; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 9b58e0148..ecfd763f6 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Engine.Threed.Blender; using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Shader; @@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private readonly GpuChannel _channel; private readonly DeviceStateWithShadow<ThreedClassState> _state; private readonly DrawState _drawState; + private readonly AdvancedBlendManager _blendManager; private readonly StateUpdateTracker<ThreedClassState> _updateTracker; @@ -55,13 +57,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// <param name="channel">GPU channel</param> /// <param name="state">3D engine state</param> /// <param name="drawState">Draw state</param> + /// <param name="blendManager">Advanced blend manager</param> /// <param name="spec">Specialization state updater</param> - public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState, SpecializationStateUpdater spec) + public StateUpdater( + GpuContext context, + GpuChannel channel, + DeviceStateWithShadow<ThreedClassState> state, + DrawState drawState, + AdvancedBlendManager blendManager, + SpecializationStateUpdater spec) { _context = context; _channel = channel; _state = state; _drawState = drawState; + _blendManager = blendManager; _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages]; _currentSpecState = spec; @@ -84,6 +94,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)), new StateUpdateCallbackEntry(UpdateBlendState, + nameof(ThreedClassState.BlendUcodeEnable), + nameof(ThreedClassState.BlendUcodeSize), nameof(ThreedClassState.BlendIndependent), nameof(ThreedClassState.BlendConstant), nameof(ThreedClassState.BlendStateCommon), @@ -1154,6 +1166,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> private void UpdateBlendState() { + if (_state.State.BlendUcodeEnable != BlendUcodeEnable.Disabled) + { + if (_context.Capabilities.SupportsBlendEquationAdvanced && _blendManager.TryGetAdvancedBlend(out var blendDescriptor)) + { + // Try to HLE it using advanced blend on the host if we can. + _context.Renderer.Pipeline.SetBlendState(blendDescriptor); + return; + } + else + { + // TODO: Blend emulation fallback. + } + } + bool blendIndependent = _state.State.BlendIndependent; ColorF blendConstant = _state.State.BlendConstant; diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 9a447a0bd..caeee18e5 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; +using Ryujinx.Graphics.Gpu.Engine.Threed.Blender; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -18,6 +19,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private readonly DeviceStateWithShadow<ThreedClassState> _state; private readonly InlineToMemoryClass _i2mClass; + private readonly AdvancedBlendManager _blendManager; private readonly DrawManager _drawManager; private readonly SemaphoreUpdater _semaphoreUpdater; private readonly ConstantBufferUpdater _cbUpdater; @@ -40,6 +42,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { nameof(ThreedClassState.InvalidateSamplerCacheNoWfi), new RwCallback(InvalidateSamplerCacheNoWfi, null) }, { nameof(ThreedClassState.InvalidateTextureHeaderCacheNoWfi), new RwCallback(InvalidateTextureHeaderCacheNoWfi, null) }, { nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) }, + { nameof(ThreedClassState.LoadBlendUcodeStart), new RwCallback(LoadBlendUcodeStart, null) }, + { nameof(ThreedClassState.LoadBlendUcodeInstruction), new RwCallback(LoadBlendUcodeInstruction, null) }, { nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) }, { nameof(ThreedClassState.DrawTextureSrcY), new RwCallback(DrawTexture, null) }, { nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceFirst), new RwCallback(DrawVertexArrayBeginEndInstanceFirst, null) }, @@ -75,9 +79,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed var drawState = new DrawState(); _drawManager = new DrawManager(context, channel, _state, drawState, spec); + _blendManager = new AdvancedBlendManager(_state); _semaphoreUpdater = new SemaphoreUpdater(context, channel, _state); _cbUpdater = new ConstantBufferUpdater(channel, _state); - _stateUpdater = new StateUpdater(context, channel, _state, drawState, spec); + _stateUpdater = new StateUpdater(context, channel, _state, drawState, _blendManager, spec); // This defaults to "always", even without any register write. // Reads just return 0, regardless of what was set there. @@ -283,6 +288,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.TextureBarrier(); } + /// <summary> + /// Sets the start offset of the blend microcode in memory. + /// </summary> + /// <param name="argument">Method call argument</param> + private void LoadBlendUcodeStart(int argument) + { + _blendManager.LoadBlendUcodeStart(argument); + } + + /// <summary> + /// Pushes one word of blend microcode. + /// </summary> + /// <param name="argument">Method call argument</param> + private void LoadBlendUcodeInstruction(int argument) + { + _blendManager.LoadBlendUcodeInstruction(argument); + } + /// <summary> /// Issues a texture barrier. /// This waits until previous texture writes from the GPU to finish, before diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index 1498e27ba..8f26f38ff 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; using System; +using System.Runtime.CompilerServices; namespace Ryujinx.Graphics.Gpu.Engine.Threed { @@ -214,6 +215,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed #pragma warning restore CS0649 } + /// <summary> + /// Indicates whenever the blend microcode processes RGB and alpha components. + /// </summary> + enum BlendUcodeEnable + { + Disabled = 0, + EnableRGB = 1, + EnableAlpha = 2, + EnableRGBA = 3 + } + /// <summary> /// Scissor state. /// </summary> @@ -434,6 +446,49 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed TriangleRastFlip = 1 << 4 } + /// <summary> + /// RGB color components packed as 16-bit float values. + /// </summary> + struct RgbHalf + { +#pragma warning disable CS0649 + public uint R; + public uint G; + public uint B; + public uint Padding; +#pragma warning restore CS0649 + + /// <summary> + /// Unpacks the red color component as a 16-bit float value. + /// </summary> + /// <returns>The component value</returns> + public Half UnpackR() + { + ushort value = (ushort)R; + return Unsafe.As<ushort, Half>(ref value); + } + + /// <summary> + /// Unpacks the green color component as a 16-bit float value. + /// </summary> + /// <returns>The component value</returns> + public Half UnpackG() + { + ushort value = (ushort)G; + return Unsafe.As<ushort, Half>(ref value); + } + + /// <summary> + /// Unpacks the blue color component as a 16-bit float value. + /// </summary> + /// <returns>The component value</returns> + public Half UnpackB() + { + ushort value = (ushort)B; + return Unsafe.As<ushort, Half>(ref value); + } + } + /// <summary> /// Condition for conditional rendering. /// </summary> @@ -752,7 +807,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public Boolean32 EarlyZForce; public fixed uint Reserved214[45]; public uint SyncpointAction; - public fixed uint Reserved2CC[21]; + public fixed uint Reserved2CC[10]; + public uint BlendUcodeNormalizedDst; + public fixed uint Reserved2F8[10]; public TessMode TessMode; public Array4<float> TessOuterLevel; public Array2<float> TessInnerLevel; @@ -781,11 +838,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public fixed uint ReservedDB8[2]; public DepthBiasState DepthBiasState; public int PatchVertices; - public fixed uint ReservedDD0[4]; + public BlendUcodeEnable BlendUcodeEnable; + public uint BlendUcodeSize; + public fixed uint ReservedDD8[2]; public uint TextureBarrier; public uint WatchdogTimer; public Boolean32 PrimitiveRestartDrawArrays; - public fixed uint ReservedDEC[5]; + public uint ReservedDEC; + public uint LoadBlendUcodeStart; + public uint LoadBlendUcodeInstruction; + public fixed uint ReservedDF8[2]; public Array16<ScissorState> ScissorState; public fixed uint ReservedF00[21]; public StencilBackMasks StencilBackMasks; @@ -850,7 +912,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public fixed uint Reserved142C[2]; public uint FirstVertex; public uint FirstInstance; - public fixed uint Reserved143C[53]; + public fixed uint Reserved143C[17]; + public Array8<RgbHalf> BlendUcodeConstants; + public fixed uint Reserved1500[4]; public uint ClipDistanceEnable; public uint Reserved1514; public float PointSize; diff --git a/Ryujinx.Graphics.OpenGL/EnumConversion.cs b/Ryujinx.Graphics.OpenGL/EnumConversion.cs index f262c584c..c9a1eaa93 100644 --- a/Ryujinx.Graphics.OpenGL/EnumConversion.cs +++ b/Ryujinx.Graphics.OpenGL/EnumConversion.cs @@ -34,6 +34,126 @@ namespace Ryujinx.Graphics.OpenGL return TextureWrapMode.Clamp; } + public static NvBlendEquationAdvanced Convert(this AdvancedBlendOp op) + { + switch (op) + { + case AdvancedBlendOp.Zero: + return NvBlendEquationAdvanced.Zero; + case AdvancedBlendOp.Src: + return NvBlendEquationAdvanced.SrcNv; + case AdvancedBlendOp.Dst: + return NvBlendEquationAdvanced.DstNv; + case AdvancedBlendOp.SrcOver: + return NvBlendEquationAdvanced.SrcOverNv; + case AdvancedBlendOp.DstOver: + return NvBlendEquationAdvanced.DstOverNv; + case AdvancedBlendOp.SrcIn: + return NvBlendEquationAdvanced.SrcInNv; + case AdvancedBlendOp.DstIn: + return NvBlendEquationAdvanced.DstInNv; + case AdvancedBlendOp.SrcOut: + return NvBlendEquationAdvanced.SrcOutNv; + case AdvancedBlendOp.DstOut: + return NvBlendEquationAdvanced.DstOutNv; + case AdvancedBlendOp.SrcAtop: + return NvBlendEquationAdvanced.SrcAtopNv; + case AdvancedBlendOp.DstAtop: + return NvBlendEquationAdvanced.DstAtopNv; + case AdvancedBlendOp.Xor: + return NvBlendEquationAdvanced.XorNv; + case AdvancedBlendOp.Plus: + return NvBlendEquationAdvanced.PlusNv; + case AdvancedBlendOp.PlusClamped: + return NvBlendEquationAdvanced.PlusClampedNv; + case AdvancedBlendOp.PlusClampedAlpha: + return NvBlendEquationAdvanced.PlusClampedAlphaNv; + case AdvancedBlendOp.PlusDarker: + return NvBlendEquationAdvanced.PlusDarkerNv; + case AdvancedBlendOp.Multiply: + return NvBlendEquationAdvanced.MultiplyNv; + case AdvancedBlendOp.Screen: + return NvBlendEquationAdvanced.ScreenNv; + case AdvancedBlendOp.Overlay: + return NvBlendEquationAdvanced.OverlayNv; + case AdvancedBlendOp.Darken: + return NvBlendEquationAdvanced.DarkenNv; + case AdvancedBlendOp.Lighten: + return NvBlendEquationAdvanced.LightenNv; + case AdvancedBlendOp.ColorDodge: + return NvBlendEquationAdvanced.ColordodgeNv; + case AdvancedBlendOp.ColorBurn: + return NvBlendEquationAdvanced.ColorburnNv; + case AdvancedBlendOp.HardLight: + return NvBlendEquationAdvanced.HardlightNv; + case AdvancedBlendOp.SoftLight: + return NvBlendEquationAdvanced.SoftlightNv; + case AdvancedBlendOp.Difference: + return NvBlendEquationAdvanced.DifferenceNv; + case AdvancedBlendOp.Minus: + return NvBlendEquationAdvanced.MinusNv; + case AdvancedBlendOp.MinusClamped: + return NvBlendEquationAdvanced.MinusClampedNv; + case AdvancedBlendOp.Exclusion: + return NvBlendEquationAdvanced.ExclusionNv; + case AdvancedBlendOp.Contrast: + return NvBlendEquationAdvanced.ContrastNv; + case AdvancedBlendOp.Invert: + return NvBlendEquationAdvanced.Invert; + case AdvancedBlendOp.InvertRGB: + return NvBlendEquationAdvanced.InvertRgbNv; + case AdvancedBlendOp.InvertOvg: + return NvBlendEquationAdvanced.InvertOvgNv; + case AdvancedBlendOp.LinearDodge: + return NvBlendEquationAdvanced.LineardodgeNv; + case AdvancedBlendOp.LinearBurn: + return NvBlendEquationAdvanced.LinearburnNv; + case AdvancedBlendOp.VividLight: + return NvBlendEquationAdvanced.VividlightNv; + case AdvancedBlendOp.LinearLight: + return NvBlendEquationAdvanced.LinearlightNv; + case AdvancedBlendOp.PinLight: + return NvBlendEquationAdvanced.PinlightNv; + case AdvancedBlendOp.HardMix: + return NvBlendEquationAdvanced.HardmixNv; + case AdvancedBlendOp.Red: + return NvBlendEquationAdvanced.RedNv; + case AdvancedBlendOp.Green: + return NvBlendEquationAdvanced.GreenNv; + case AdvancedBlendOp.Blue: + return NvBlendEquationAdvanced.BlueNv; + case AdvancedBlendOp.HslHue: + return NvBlendEquationAdvanced.HslHueNv; + case AdvancedBlendOp.HslSaturation: + return NvBlendEquationAdvanced.HslSaturationNv; + case AdvancedBlendOp.HslColor: + return NvBlendEquationAdvanced.HslColorNv; + case AdvancedBlendOp.HslLuminosity: + return NvBlendEquationAdvanced.HslLuminosityNv; + } + + Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(AdvancedBlendOp)} enum value: {op}."); + + return NvBlendEquationAdvanced.Zero; + } + + public static All Convert(this AdvancedBlendOverlap overlap) + { + switch (overlap) + { + case AdvancedBlendOverlap.Uncorrelated: + return All.UncorrelatedNv; + case AdvancedBlendOverlap.Disjoint: + return All.DisjointNv; + case AdvancedBlendOverlap.Conjoint: + return All.ConjointNv; + } + + Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(AdvancedBlendOverlap)} enum value: {overlap}."); + + return All.UncorrelatedNv; + } + public static All Convert(this BlendFactor factor) { switch (factor) diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index 8caf11dd5..846465260 100644 --- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.OpenGL { private static readonly Lazy<bool> _supportsAlphaToCoverageDitherControl = new Lazy<bool>(() => HasExtension("GL_NV_alpha_to_coverage_dither_control")); private static readonly Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); + private static readonly Lazy<bool> _supportsBlendEquationAdvanced = new Lazy<bool>(() => HasExtension("GL_NV_blend_equation_advanced")); private static readonly Lazy<bool> _supportsDrawTexture = new Lazy<bool>(() => HasExtension("GL_NV_draw_texture")); private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new Lazy<bool>(() => HasExtension("GL_ARB_fragment_shader_interlock")); private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new Lazy<bool>(() => HasExtension("GL_INTEL_fragment_shader_ordering")); @@ -51,6 +52,7 @@ namespace Ryujinx.Graphics.OpenGL public static bool SupportsAlphaToCoverageDitherControl => _supportsAlphaToCoverageDitherControl.Value; public static bool SupportsAstcCompression => _supportsAstcCompression.Value; + public static bool SupportsBlendEquationAdvanced => _supportsBlendEquationAdvanced.Value; public static bool SupportsDrawTexture => _supportsDrawTexture.Value; public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value; public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value; diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 30ed942d3..722c4b4da 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -119,6 +119,7 @@ namespace Ryujinx.Graphics.OpenGL supportsR4G4B4A4Format: true, supportsSnormBufferTextureFormat: false, supports5BitComponentFormat: true, + supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced, supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 8bcaf4c77..970feea0c 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -59,6 +59,7 @@ namespace Ryujinx.Graphics.OpenGL private uint _fragmentOutputMap; private uint _componentMasks; private uint _currentComponentMasks; + private bool _advancedBlendEnable; private uint _scissorEnables; @@ -784,8 +785,26 @@ namespace Ryujinx.Graphics.OpenGL GL.Enable(EnableCap.AlphaTest); } + public void SetBlendState(AdvancedBlendDescriptor blend) + { + if (HwCapabilities.SupportsBlendEquationAdvanced) + { + GL.BlendEquation((BlendEquationMode)blend.Op.Convert()); + GL.NV.BlendParameter(NvBlendEquationAdvanced.BlendOverlapNv, (int)blend.Overlap.Convert()); + GL.NV.BlendParameter(NvBlendEquationAdvanced.BlendPremultipliedSrcNv, blend.SrcPreMultiplied ? 1 : 0); + GL.Enable(EnableCap.Blend); + _advancedBlendEnable = true; + } + } + public void SetBlendState(int index, BlendDescriptor blend) { + if (_advancedBlendEnable) + { + GL.Disable(EnableCap.Blend); + _advancedBlendEnable = false; + } + if (!blend.Enable) { GL.Disable(IndexedEnableCap.Blend, index); diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 0164ef85c..6c273b050 100644 --- a/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -79,6 +79,60 @@ namespace Ryujinx.Graphics.Vulkan }; } + public static Silk.NET.Vulkan.BlendOp Convert(this GAL.AdvancedBlendOp op) + { + return op switch + { + GAL.AdvancedBlendOp.Zero => Silk.NET.Vulkan.BlendOp.ZeroExt, + GAL.AdvancedBlendOp.Src => Silk.NET.Vulkan.BlendOp.SrcExt, + GAL.AdvancedBlendOp.Dst => Silk.NET.Vulkan.BlendOp.DstExt, + GAL.AdvancedBlendOp.SrcOver => Silk.NET.Vulkan.BlendOp.SrcOverExt, + GAL.AdvancedBlendOp.DstOver => Silk.NET.Vulkan.BlendOp.DstOverExt, + GAL.AdvancedBlendOp.SrcIn => Silk.NET.Vulkan.BlendOp.SrcInExt, + GAL.AdvancedBlendOp.DstIn => Silk.NET.Vulkan.BlendOp.DstInExt, + GAL.AdvancedBlendOp.SrcOut => Silk.NET.Vulkan.BlendOp.SrcOutExt, + GAL.AdvancedBlendOp.DstOut => Silk.NET.Vulkan.BlendOp.DstOutExt, + GAL.AdvancedBlendOp.SrcAtop => Silk.NET.Vulkan.BlendOp.SrcAtopExt, + GAL.AdvancedBlendOp.DstAtop => Silk.NET.Vulkan.BlendOp.DstAtopExt, + GAL.AdvancedBlendOp.Xor => Silk.NET.Vulkan.BlendOp.XorExt, + GAL.AdvancedBlendOp.Plus => Silk.NET.Vulkan.BlendOp.PlusExt, + GAL.AdvancedBlendOp.PlusClamped => Silk.NET.Vulkan.BlendOp.PlusClampedExt, + GAL.AdvancedBlendOp.PlusClampedAlpha => Silk.NET.Vulkan.BlendOp.PlusClampedAlphaExt, + GAL.AdvancedBlendOp.PlusDarker => Silk.NET.Vulkan.BlendOp.PlusDarkerExt, + GAL.AdvancedBlendOp.Multiply => Silk.NET.Vulkan.BlendOp.MultiplyExt, + GAL.AdvancedBlendOp.Screen => Silk.NET.Vulkan.BlendOp.ScreenExt, + GAL.AdvancedBlendOp.Overlay => Silk.NET.Vulkan.BlendOp.OverlayExt, + GAL.AdvancedBlendOp.Darken => Silk.NET.Vulkan.BlendOp.DarkenExt, + GAL.AdvancedBlendOp.Lighten => Silk.NET.Vulkan.BlendOp.LightenExt, + GAL.AdvancedBlendOp.ColorDodge => Silk.NET.Vulkan.BlendOp.ColordodgeExt, + GAL.AdvancedBlendOp.ColorBurn => Silk.NET.Vulkan.BlendOp.ColorburnExt, + GAL.AdvancedBlendOp.HardLight => Silk.NET.Vulkan.BlendOp.HardlightExt, + GAL.AdvancedBlendOp.SoftLight => Silk.NET.Vulkan.BlendOp.SoftlightExt, + GAL.AdvancedBlendOp.Difference => Silk.NET.Vulkan.BlendOp.DifferenceExt, + GAL.AdvancedBlendOp.Minus => Silk.NET.Vulkan.BlendOp.MinusExt, + GAL.AdvancedBlendOp.MinusClamped => Silk.NET.Vulkan.BlendOp.MinusClampedExt, + GAL.AdvancedBlendOp.Exclusion => Silk.NET.Vulkan.BlendOp.ExclusionExt, + GAL.AdvancedBlendOp.Contrast => Silk.NET.Vulkan.BlendOp.ContrastExt, + GAL.AdvancedBlendOp.Invert => Silk.NET.Vulkan.BlendOp.InvertExt, + GAL.AdvancedBlendOp.InvertRGB => Silk.NET.Vulkan.BlendOp.InvertRgbExt, + GAL.AdvancedBlendOp.InvertOvg => Silk.NET.Vulkan.BlendOp.InvertOvgExt, + GAL.AdvancedBlendOp.LinearDodge => Silk.NET.Vulkan.BlendOp.LineardodgeExt, + GAL.AdvancedBlendOp.LinearBurn => Silk.NET.Vulkan.BlendOp.LinearburnExt, + GAL.AdvancedBlendOp.VividLight => Silk.NET.Vulkan.BlendOp.VividlightExt, + GAL.AdvancedBlendOp.LinearLight => Silk.NET.Vulkan.BlendOp.LinearlightExt, + GAL.AdvancedBlendOp.PinLight => Silk.NET.Vulkan.BlendOp.PinlightExt, + GAL.AdvancedBlendOp.HardMix => Silk.NET.Vulkan.BlendOp.HardmixExt, + GAL.AdvancedBlendOp.Red => Silk.NET.Vulkan.BlendOp.RedExt, + GAL.AdvancedBlendOp.Green => Silk.NET.Vulkan.BlendOp.GreenExt, + GAL.AdvancedBlendOp.Blue => Silk.NET.Vulkan.BlendOp.BlueExt, + GAL.AdvancedBlendOp.HslHue => Silk.NET.Vulkan.BlendOp.HslHueExt, + GAL.AdvancedBlendOp.HslSaturation => Silk.NET.Vulkan.BlendOp.HslSaturationExt, + GAL.AdvancedBlendOp.HslColor => Silk.NET.Vulkan.BlendOp.HslColorExt, + GAL.AdvancedBlendOp.HslLuminosity => Silk.NET.Vulkan.BlendOp.HslLuminosityExt, + _ => LogInvalidAndReturn(op, nameof(GAL.AdvancedBlendOp), Silk.NET.Vulkan.BlendOp.Add) + }; + } + public static Silk.NET.Vulkan.BlendOp Convert(this GAL.BlendOp op) { return op switch @@ -92,6 +146,17 @@ namespace Ryujinx.Graphics.Vulkan }; } + public static Silk.NET.Vulkan.BlendOverlapEXT Convert(this GAL.AdvancedBlendOverlap overlap) + { + return overlap switch + { + GAL.AdvancedBlendOverlap.Uncorrelated => Silk.NET.Vulkan.BlendOverlapEXT.UncorrelatedExt, + GAL.AdvancedBlendOverlap.Disjoint => Silk.NET.Vulkan.BlendOverlapEXT.DisjointExt, + GAL.AdvancedBlendOverlap.Conjoint => Silk.NET.Vulkan.BlendOverlapEXT.ConjointExt, + _ => LogInvalidAndReturn(overlap, nameof(GAL.AdvancedBlendOverlap), Silk.NET.Vulkan.BlendOverlapEXT.UncorrelatedExt) + }; + } + public static Silk.NET.Vulkan.CompareOp Convert(this GAL.CompareOp op) { return op switch diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 1ed2b0ccc..4512d375f 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -18,6 +18,10 @@ namespace Ryujinx.Graphics.Vulkan { public readonly bool SupportsIndexTypeUint8; public readonly bool SupportsCustomBorderColor; + public readonly bool SupportsBlendEquationAdvanced; + public readonly bool SupportsBlendEquationAdvancedCorrelatedOverlap; + public readonly bool SupportsBlendEquationAdvancedNonPreMultipliedSrcColor; + public readonly bool SupportsBlendEquationAdvancedNonPreMultipliedDstColor; public readonly bool SupportsIndirectParameters; public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsGeometryShaderPassthrough; @@ -44,6 +48,10 @@ namespace Ryujinx.Graphics.Vulkan public HardwareCapabilities( bool supportsIndexTypeUint8, bool supportsCustomBorderColor, + bool supportsBlendEquationAdvanced, + bool supportsBlendEquationAdvancedCorrelatedOverlap, + bool supportsBlendEquationAdvancedNonPreMultipliedSrcColor, + bool supportsBlendEquationAdvancedNonPreMultipliedDstColor, bool supportsIndirectParameters, bool supportsFragmentShaderInterlock, bool supportsGeometryShaderPassthrough, @@ -69,6 +77,10 @@ namespace Ryujinx.Graphics.Vulkan { SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsCustomBorderColor = supportsCustomBorderColor; + SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced; + SupportsBlendEquationAdvancedCorrelatedOverlap = supportsBlendEquationAdvancedCorrelatedOverlap; + SupportsBlendEquationAdvancedNonPreMultipliedSrcColor = supportsBlendEquationAdvancedNonPreMultipliedSrcColor; + SupportsBlendEquationAdvancedNonPreMultipliedDstColor = supportsBlendEquationAdvancedNonPreMultipliedDstColor; SupportsIndirectParameters = supportsIndirectParameters; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 8ed39ee26..f779305db 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -112,11 +112,9 @@ namespace Ryujinx.Graphics.Vulkan var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f }; new Span<Vector4<float>>(_renderScale).Fill(defaultScale); - _newState.Initialize(); - _newState.LineWidth = 1f; - _newState.SamplesCount = 1; + _storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets]; - _storedBlend = new PipelineColorBlendAttachmentState[8]; + _newState.Initialize(); } public void Initialize() @@ -676,6 +674,49 @@ namespace Ryujinx.Graphics.Vulkan // to avoid creating one version of the shader per reference value used. } + public void SetBlendState(AdvancedBlendDescriptor blend) + { + for (int index = 0; index < Constants.MaxRenderTargets; index++) + { + ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; + + if (index == 0) + { + var blendOp = blend.Op.Convert(); + + vkBlend = new PipelineColorBlendAttachmentState( + blendEnable: true, + colorBlendOp: blendOp, + alphaBlendOp: blendOp, + colorWriteMask: vkBlend.ColorWriteMask); + + if (Gd.Capabilities.SupportsBlendEquationAdvancedNonPreMultipliedSrcColor) + { + _newState.AdvancedBlendSrcPreMultiplied = blend.SrcPreMultiplied; + } + + if (Gd.Capabilities.SupportsBlendEquationAdvancedCorrelatedOverlap) + { + _newState.AdvancedBlendOverlap = blend.Overlap.Convert(); + } + } + else + { + vkBlend = new PipelineColorBlendAttachmentState( + colorWriteMask: vkBlend.ColorWriteMask); + } + + if (vkBlend.ColorWriteMask == 0) + { + _storedBlend[index] = vkBlend; + + vkBlend = new PipelineColorBlendAttachmentState(); + } + } + + SignalStateChange(); + } + public void SetBlendState(int index, BlendDescriptor blend) { ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; @@ -709,6 +750,11 @@ namespace Ryujinx.Graphics.Vulkan blend.BlendConstant.Blue, blend.BlendConstant.Alpha); + // Reset advanced blend state back defaults to the cache to help the pipeline cache. + _newState.AdvancedBlendSrcPreMultiplied = true; + _newState.AdvancedBlendDstPreMultiplied = true; + _newState.AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt; + SignalStateChange(); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineState.cs b/Ryujinx.Graphics.Vulkan/PipelineState.cs index 00b154a06..0d5494766 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -285,6 +285,24 @@ namespace Ryujinx.Graphics.Vulkan set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); } + public bool AdvancedBlendSrcPreMultiplied + { + get => ((Internal.Id9 >> 2) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2); + } + + public bool AdvancedBlendDstPreMultiplied + { + get => ((Internal.Id9 >> 3) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3); + } + + public BlendOverlapEXT AdvancedBlendOverlap + { + get => (BlendOverlapEXT)((Internal.Id9 >> 4) & 0x3); + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); + } + public NativeArray<PipelineShaderStageCreateInfo> Stages; public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes; public PipelineLayout PipelineLayout; @@ -303,6 +321,13 @@ namespace Ryujinx.Graphics.Vulkan RequiredSubgroupSize = RequiredSubgroupSize }; } + + AdvancedBlendSrcPreMultiplied = true; + AdvancedBlendDstPreMultiplied = true; + AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt; + + LineWidth = 1f; + SamplesCount = 1; } public unsafe Auto<DisposablePipeline> CreateComputePipeline( @@ -486,6 +511,23 @@ namespace Ryujinx.Graphics.Vulkan PAttachments = pColorBlendAttachmentState }; + PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState; + + if (!AdvancedBlendSrcPreMultiplied || + !AdvancedBlendDstPreMultiplied || + AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt) + { + colorBlendAdvancedState = new PipelineColorBlendAdvancedStateCreateInfoEXT() + { + SType = StructureType.PipelineColorBlendAdvancedStateCreateInfoExt, + SrcPremultiplied = AdvancedBlendSrcPreMultiplied, + DstPremultiplied = AdvancedBlendDstPreMultiplied, + BlendOverlap = AdvancedBlendOverlap + }; + + colorBlendState.PNext = &colorBlendAdvancedState; + } + bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState; int dynamicStatesCount = supportsExtDynamicState ? 9 : 8; diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 4401f032d..353b219ac 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan ExtTransformFeedback.ExtensionName, KhrDrawIndirectCount.ExtensionName, KhrPushDescriptor.ExtensionName, + "VK_EXT_blend_operation_advanced", "VK_EXT_custom_border_color", "VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV. "VK_EXT_fragment_shader_interlock", diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index a7b4b41a7..4c7c731be 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -149,6 +149,19 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceProperties2 }; + PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new PhysicalDeviceBlendOperationAdvancedPropertiesEXT() + { + SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt + }; + + bool supportsBlendOperationAdvanced = supportedExtensions.Contains("VK_EXT_blend_operation_advanced"); + + if (supportsBlendOperationAdvanced) + { + propertiesBlendOperationAdvanced.PNext = properties2.PNext; + properties2.PNext = &propertiesBlendOperationAdvanced; + } + PhysicalDeviceSubgroupSizeControlPropertiesEXT propertiesSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlPropertiesEXT() { SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt @@ -246,9 +259,9 @@ namespace Ryujinx.Graphics.Vulkan portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias; } - bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") && - featuresCustomBorderColor.CustomBorderColors && - featuresCustomBorderColor.CustomBorderColorWithoutFormat; + bool supportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color") && + featuresCustomBorderColor.CustomBorderColors && + featuresCustomBorderColor.CustomBorderColorWithoutFormat; ref var properties = ref properties2.Properties; @@ -259,7 +272,11 @@ namespace Ryujinx.Graphics.Vulkan Capabilities = new HardwareCapabilities( supportedExtensions.Contains("VK_EXT_index_type_uint8"), - customBorderColorSupported, + supportsCustomBorderColor, + supportsBlendOperationAdvanced, + propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap, + propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor, + propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor, supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"), supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), @@ -526,6 +543,7 @@ namespace Ryujinx.Graphics.Vulkan supportsR4G4B4A4Format: supportsR4G4B4A4Format, supportsSnormBufferTextureFormat: true, supports5BitComponentFormat: supports5BitComponentFormat, + supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced, supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderOrderingIntel: false, supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, From 58d7a1fe9747f673b0c0399581730616681f015c Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 21 Feb 2023 06:40:23 -0300 Subject: [PATCH 362/737] Mark texture as modified and sync on I2M fast path (#4449) --- .../Engine/InlineToMemory/InlineToMemoryClass.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs index f6effe2ed..e1d7e9407 100644 --- a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs @@ -197,7 +197,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory if (target != null) { + target.SynchronizeMemory(); target.SetData(data, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount)); + target.SignalModified(); return; } From fc43aecbbd37a83ebd03f8cfe8fbc033ce2bda7d Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Tue, 21 Feb 2023 09:53:38 +0000 Subject: [PATCH 363/737] Memory: Faster Split for NonOverlappingRangeList (#4451) I noticed that in Xenoblade 2, the game can end up spending a lot of time adding and removing tracking handles. One of the main causes of this is actually splitting existing handles, which does the following: - Remove existing handle from list - Update existing handle to end at split address, create new handle starting at split address - Add updated handle (left) to list - Add new handle (right) to list This costs 1 deletion and 2 insertions. When there are more handles, this gets a lot more expensive, as insertions are done by copying all values to the right, and deletions by copying values to the left. This PR simply allows it to look up the handle being split, and replace its entry with the new end address without insertion or deletion. This makes a split only cost one insertion and a binary search lookup (very cheap). This isn't all of the cost on Xenoblade 2, but it does significantly reduce it. There might be something else to this - we could find a way to reduce the handle count for the game (merging on deletion? buffer deletion?), we could use a different structure for virtual regions, as the current one is optimal for buffer lookups which nearly always read, memory tracking has more of a balance between read/write. That's for a later date though, this was an easy improvment. --- .../Range/NonOverlappingRangeList.cs | 4 +- Ryujinx.Memory/Range/RangeList.cs | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Memory/Range/NonOverlappingRangeList.cs b/Ryujinx.Memory/Range/NonOverlappingRangeList.cs index 9a8f84dd6..60b2b3784 100644 --- a/Ryujinx.Memory/Range/NonOverlappingRangeList.cs +++ b/Ryujinx.Memory/Range/NonOverlappingRangeList.cs @@ -97,10 +97,8 @@ namespace Ryujinx.Memory.Range /// <returns>The new region (high part)</returns> private T Split(T region, ulong splitAddress) { - Remove(region); - T newRegion = (T)region.Split(splitAddress); - Add(region); + Update(region); Add(newRegion); return newRegion; } diff --git a/Ryujinx.Memory/Range/RangeList.cs b/Ryujinx.Memory/Range/RangeList.cs index 7278e7eb4..469195973 100644 --- a/Ryujinx.Memory/Range/RangeList.cs +++ b/Ryujinx.Memory/Range/RangeList.cs @@ -67,6 +67,43 @@ namespace Ryujinx.Memory.Range Insert(index, new RangeItem<T>(item)); } + /// <summary> + /// Updates an item's end address on the list. Address must be the same. + /// </summary> + /// <param name="item">The item to be updated</param> + /// <returns>True if the item was located and updated, false otherwise</returns> + public bool Update(T item) + { + int index = BinarySearch(item.Address); + + if (index >= 0) + { + while (index > 0 && _items[index - 1].Address == item.Address) + { + index--; + } + + while (index < Count) + { + if (_items[index].Value.Equals(item)) + { + _items[index] = new RangeItem<T>(item); + + return true; + } + + if (_items[index].Address > item.Address) + { + break; + } + + index++; + } + } + + return false; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Insert(int index, RangeItem<T> item) { From edfd4d70c0f38d41c6ebb31508127b14727017bd Mon Sep 17 00:00:00 2001 From: Logan Stromberg <loganstromberg@gmail.com> Date: Tue, 21 Feb 2023 02:44:57 -0800 Subject: [PATCH 364/737] Use SIMD acceleration for audio upsampler (#4410) * Use SIMD acceleration for audio upsampler filter kernel for a moderate speedup * Address formatting. Implement AVX2 fast path for high quality resampling in ResamplerHelper * now really, are we really getting the benefit of inlining 50+ line methods? * adding unit tests for resampler + upsampler. The upsampler ones fail for some reason * Fixing upsampler test. Apparently this algo only works at specific ratios --------- Co-authored-by: Logan Stromberg <lostromb@microsoft.com> --- Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs | 183 ++++++++++-------- Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs | 23 ++- .../Audio/Renderer/Dsp/ResamplerTests.cs | 93 +++++++++ .../Audio/Renderer/Dsp/UpsamplerTests.cs | 64 ++++++ 4 files changed, 279 insertions(+), 84 deletions(-) create mode 100644 Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs create mode 100644 Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs b/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs index b46a33fe0..7873c4d27 100644 --- a/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs +++ b/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -380,7 +381,6 @@ namespace Ryujinx.Audio.Renderer.Dsp return _normalCurveLut2F; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void ResampleDefaultQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount, bool needPitch) { ReadOnlySpan<float> parameters = GetDefaultParameter(ratio); @@ -394,35 +394,33 @@ namespace Ryujinx.Audio.Renderer.Dsp if (ratio == 1f) { fixed (short* pInput = inputBuffer) + fixed (float* pOutput = outputBuffer, pParameters = parameters) { - fixed (float* pOutput = outputBuffer, pParameters = parameters) + Vector128<float> parameter = Sse.LoadVector128(pParameters); + + for (; i < (sampleCount & ~3); i += 4) { - Vector128<float> parameter = Sse.LoadVector128(pParameters); + Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + (uint)i); + Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 1); + Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 2); + Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 3); - for (; i < (sampleCount & ~3); i += 4) - { - Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + (uint)i); - Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 1); - Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 2); - Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 3); + Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0); + Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1); + Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2); + Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3); - Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0); - Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1); - Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2); - Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3); + Vector128<float> mix0 = Sse.Multiply(input0, parameter); + Vector128<float> mix1 = Sse.Multiply(input1, parameter); + Vector128<float> mix2 = Sse.Multiply(input2, parameter); + Vector128<float> mix3 = Sse.Multiply(input3, parameter); - Vector128<float> mix0 = Sse.Multiply(input0, parameter); - Vector128<float> mix1 = Sse.Multiply(input1, parameter); - Vector128<float> mix2 = Sse.Multiply(input2, parameter); - Vector128<float> mix3 = Sse.Multiply(input3, parameter); + Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1); + Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3); - Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1); - Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3); + Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23); - Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23); - - Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123)); - } + Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123)); } } @@ -431,62 +429,60 @@ namespace Ryujinx.Audio.Renderer.Dsp else { fixed (short* pInput = inputBuffer) + fixed (float* pOutput = outputBuffer, pParameters = parameters) { - fixed (float* pOutput = outputBuffer, pParameters = parameters) + for (; i < (sampleCount & ~3); i += 4) { - for (; i < (sampleCount & ~3); i += 4) - { - uint baseIndex0 = (uint)(fraction * 128) * 4; - uint inputIndex0 = (uint)inputBufferIndex; + uint baseIndex0 = (uint)(fraction * 128) * 4; + uint inputIndex0 = (uint)inputBufferIndex; - fraction += ratio; + fraction += ratio; - uint baseIndex1 = ((uint)(fraction * 128) & 127) * 4; - uint inputIndex1 = (uint)inputBufferIndex + (uint)fraction; + uint baseIndex1 = ((uint)(fraction * 128) & 127) * 4; + uint inputIndex1 = (uint)inputBufferIndex + (uint)fraction; - fraction += ratio; + fraction += ratio; - uint baseIndex2 = ((uint)(fraction * 128) & 127) * 4; - uint inputIndex2 = (uint)inputBufferIndex + (uint)fraction; + uint baseIndex2 = ((uint)(fraction * 128) & 127) * 4; + uint inputIndex2 = (uint)inputBufferIndex + (uint)fraction; - fraction += ratio; + fraction += ratio; - uint baseIndex3 = ((uint)(fraction * 128) & 127) * 4; - uint inputIndex3 = (uint)inputBufferIndex + (uint)fraction; + uint baseIndex3 = ((uint)(fraction * 128) & 127) * 4; + uint inputIndex3 = (uint)inputBufferIndex + (uint)fraction; - fraction += ratio; - inputBufferIndex += (int)fraction; + fraction += ratio; + inputBufferIndex += (int)fraction; - // Only keep lower part (safe as fraction isn't supposed to be negative) - fraction -= (int)fraction; + // Only keep lower part (safe as fraction isn't supposed to be negative) + fraction -= (int)fraction; - Vector128<float> parameter0 = Sse.LoadVector128(pParameters + baseIndex0); - Vector128<float> parameter1 = Sse.LoadVector128(pParameters + baseIndex1); - Vector128<float> parameter2 = Sse.LoadVector128(pParameters + baseIndex2); - Vector128<float> parameter3 = Sse.LoadVector128(pParameters + baseIndex3); + Vector128<float> parameter0 = Sse.LoadVector128(pParameters + baseIndex0); + Vector128<float> parameter1 = Sse.LoadVector128(pParameters + baseIndex1); + Vector128<float> parameter2 = Sse.LoadVector128(pParameters + baseIndex2); + Vector128<float> parameter3 = Sse.LoadVector128(pParameters + baseIndex3); - Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + inputIndex0); - Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + inputIndex1); - Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + inputIndex2); - Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + inputIndex3); + Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + inputIndex0); + Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + inputIndex1); + Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + inputIndex2); + Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + inputIndex3); - Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0); - Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1); - Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2); - Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3); + Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0); + Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1); + Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2); + Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3); - Vector128<float> mix0 = Sse.Multiply(input0, parameter0); - Vector128<float> mix1 = Sse.Multiply(input1, parameter1); - Vector128<float> mix2 = Sse.Multiply(input2, parameter2); - Vector128<float> mix3 = Sse.Multiply(input3, parameter3); + Vector128<float> mix0 = Sse.Multiply(input0, parameter0); + Vector128<float> mix1 = Sse.Multiply(input1, parameter1); + Vector128<float> mix2 = Sse.Multiply(input2, parameter2); + Vector128<float> mix3 = Sse.Multiply(input3, parameter3); - Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1); - Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3); + Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1); + Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3); - Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23); + Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23); - Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123)); - } + Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123)); } } } @@ -526,34 +522,59 @@ namespace Ryujinx.Audio.Renderer.Dsp return _highCurveLut2F; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ResampleHighQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount) + private static unsafe void ResampleHighQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount) { ReadOnlySpan<float> parameters = GetHighParameter(ratio); int inputBufferIndex = 0; - // TODO: fast path - - for (int i = 0; i < sampleCount; i++) + if (Avx2.IsSupported) { - int baseIndex = (int)(fraction * 128) * 8; - ReadOnlySpan<float> parameter = parameters.Slice(baseIndex, 8); - ReadOnlySpan<short> currentInput = inputBuffer.Slice(inputBufferIndex, 8); + // Fast path; assumes 256-bit vectors for simplicity because the filter is 8 taps + fixed (short* pInput = inputBuffer) + fixed (float* pParameters = parameters) + { + for (int i = 0; i < sampleCount; i++) + { + int baseIndex = (int)(fraction * 128) * 8; - outputBuffer[i] = (float)Math.Round(currentInput[0] * parameter[0] + - currentInput[1] * parameter[1] + - currentInput[2] * parameter[2] + - currentInput[3] * parameter[3] + - currentInput[4] * parameter[4] + - currentInput[5] * parameter[5] + - currentInput[6] * parameter[6] + - currentInput[7] * parameter[7]); + Vector256<int> intInput = Avx2.ConvertToVector256Int32(pInput + inputBufferIndex); + Vector256<float> floatInput = Avx.ConvertToVector256Single(intInput); + Vector256<float> parameter = Avx.LoadVector256(pParameters + baseIndex); + Vector256<float> dp = Avx.DotProduct(floatInput, parameter, control: 0xFF); - fraction += ratio; - inputBufferIndex += (int)MathF.Truncate(fraction); + // avx2 does an 8-element dot product piecewise so we have to sum up 2 intermediate results + outputBuffer[i] = (float)Math.Round(dp[0] + dp[4]); - fraction -= (int)fraction; + fraction += ratio; + inputBufferIndex += (int)MathF.Truncate(fraction); + + fraction -= (int)fraction; + } + } + } + else + { + for (int i = 0; i < sampleCount; i++) + { + int baseIndex = (int)(fraction * 128) * 8; + ReadOnlySpan<float> parameter = parameters.Slice(baseIndex, 8); + ReadOnlySpan<short> currentInput = inputBuffer.Slice(inputBufferIndex, 8); + + outputBuffer[i] = (float)Math.Round(currentInput[0] * parameter[0] + + currentInput[1] * parameter[1] + + currentInput[2] * parameter[2] + + currentInput[3] * parameter[3] + + currentInput[4] * parameter[4] + + currentInput[5] * parameter[5] + + currentInput[6] * parameter[6] + + currentInput[7] * parameter[7]); + + fraction += ratio; + inputBufferIndex += (int)MathF.Truncate(fraction); + + fraction -= (int)fraction; + } } } diff --git a/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs b/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs index 847acec2e..6cdab5a7b 100644 --- a/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs +++ b/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs @@ -2,6 +2,7 @@ using Ryujinx.Audio.Renderer.Server.Upsampler; using Ryujinx.Common.Memory; using System; using System.Diagnostics; +using System.Numerics; using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Dsp @@ -70,16 +71,32 @@ namespace Ryujinx.Audio.Renderer.Dsp return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] float DoFilterBank(ref UpsamplerBufferState state, in Array20<float> bank) { float result = 0.0f; Debug.Assert(state.History.Length == HistoryLength); Debug.Assert(bank.Length == FilterBankLength); - for (int j = 0; j < FilterBankLength; j++) + + int curIdx = 0; + if (Vector.IsHardwareAccelerated) { - result += bank[j] * state.History[j]; + // Do SIMD-accelerated block operations where possible. + // Only about a 2x speedup since filter bank length is short + int stopIdx = FilterBankLength - (FilterBankLength % Vector<float>.Count); + while (curIdx < stopIdx) + { + result += Vector.Dot( + new Vector<float>(bank.AsSpan().Slice(curIdx, Vector<float>.Count)), + new Vector<float>(state.History.AsSpan().Slice(curIdx, Vector<float>.Count))); + curIdx += Vector<float>.Count; + } + } + + while (curIdx < FilterBankLength) + { + result += bank[curIdx] * state.History[curIdx]; + curIdx++; } return result; diff --git a/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs b/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs new file mode 100644 index 000000000..364837ee0 --- /dev/null +++ b/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs @@ -0,0 +1,93 @@ +using NUnit.Framework; +using Ryujinx.Audio.Renderer.Dsp; +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Server.Upsampler; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Ryujinx.Tests.Audio.Renderer.Dsp +{ + class ResamplerTests + { + [Test] + [TestCase(VoiceInParameter.SampleRateConversionQuality.Low)] + [TestCase(VoiceInParameter.SampleRateConversionQuality.Default)] + [TestCase(VoiceInParameter.SampleRateConversionQuality.High)] + public void TestResamplerConsistencyUpsampling(VoiceInParameter.SampleRateConversionQuality quality) + { + DoResamplingTest(44100, 48000, quality); + } + + [Test] + [TestCase(VoiceInParameter.SampleRateConversionQuality.Low)] + [TestCase(VoiceInParameter.SampleRateConversionQuality.Default)] + [TestCase(VoiceInParameter.SampleRateConversionQuality.High)] + public void TestResamplerConsistencyDownsampling(VoiceInParameter.SampleRateConversionQuality quality) + { + DoResamplingTest(48000, 44100, quality); + } + + /// <summary> + /// Generates a 1-second sine wave sample at input rate, resamples it to output rate, and + /// ensures that it resampled at the expected rate with no discontinuities + /// </summary> + /// <param name="inputRate">The input sample rate to test</param> + /// <param name="outputRate">The output sample rate to test</param> + /// <param name="quality">The resampler quality to use</param> + private static void DoResamplingTest(int inputRate, int outputRate, VoiceInParameter.SampleRateConversionQuality quality) + { + float inputSampleRate = (float)inputRate; + float outputSampleRate = (float)outputRate; + int inputSampleCount = inputRate; + int outputSampleCount = outputRate; + short[] inputBuffer = new short[inputSampleCount + 100]; // add some safety buffer at the end + float[] outputBuffer = new float[outputSampleCount + 100]; + for (int sample = 0; sample < inputBuffer.Length; sample++) + { + // 440 hz sine wave with amplitude = 0.5f at input sample rate + inputBuffer[sample] = (short)(32767 * MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f); + } + + float fraction = 0; + + ResamplerHelper.Resample( + outputBuffer.AsSpan(), + inputBuffer.AsSpan(), + inputSampleRate / outputSampleRate, + ref fraction, + outputSampleCount, + quality, + false); + + float[] expectedOutput = new float[outputSampleCount]; + float sumDifference = 0; + int delay = quality switch + { + VoiceInParameter.SampleRateConversionQuality.High => 3, + VoiceInParameter.SampleRateConversionQuality.Default => 1, + _ => 0 + }; + + for (int sample = 0; sample < outputSampleCount; sample++) + { + outputBuffer[sample] /= 32767; + // 440 hz sine wave with amplitude = 0.5f at output sample rate + expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample + delay) * MathF.PI * 2f) * 0.5f; + float thisDelta = Math.Abs(expectedOutput[sample] - outputBuffer[sample]); + + // Ensure no discontinuities + Assert.IsTrue(thisDelta < 0.1f); + sumDifference += thisDelta; + } + + sumDifference = sumDifference / (float)outputSampleCount; + // Expect the output to be 99% similar to the expected resampled sine wave + Assert.IsTrue(sumDifference < 0.01f); + } + } +} diff --git a/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs b/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs new file mode 100644 index 000000000..2018752b3 --- /dev/null +++ b/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs @@ -0,0 +1,64 @@ +using NUnit.Framework; +using Ryujinx.Audio.Renderer.Dsp; +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Server.Upsampler; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Ryujinx.Tests.Audio.Renderer.Dsp +{ + class UpsamplerTests + { + [Test] + public void TestUpsamplerConsistency() + { + UpsamplerBufferState bufferState = new UpsamplerBufferState(); + int inputBlockSize = 160; + int numInputSamples = 32000; + int numOutputSamples = 48000; + float inputSampleRate = numInputSamples; + float outputSampleRate = numOutputSamples; + float[] inputBuffer = new float[numInputSamples + 100]; + float[] outputBuffer = new float[numOutputSamples + 100]; + for (int sample = 0; sample < inputBuffer.Length; sample++) + { + // 440 hz sine wave with amplitude = 0.5f at input sample rate + inputBuffer[sample] = MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f; + } + + int inputIdx = 0; + int outputIdx = 0; + while (inputIdx + inputBlockSize < numInputSamples) + { + int outputBufLength = (int)Math.Round((float)(inputIdx + inputBlockSize) * outputSampleRate / inputSampleRate) - outputIdx; + UpsamplerHelper.Upsample( + outputBuffer.AsSpan(outputIdx), + inputBuffer.AsSpan(inputIdx), + outputBufLength, + inputBlockSize, + ref bufferState); + + inputIdx += inputBlockSize; + outputIdx += outputBufLength; + } + + float[] expectedOutput = new float[numOutputSamples]; + float sumDifference = 0; + for (int sample = 0; sample < numOutputSamples; sample++) + { + // 440 hz sine wave with amplitude = 0.5f at output sample rate with an offset of 15 + expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample - 15) * MathF.PI * 2f) * 0.5f; + sumDifference += Math.Abs(expectedOutput[sample] - outputBuffer[sample]); + } + + sumDifference = sumDifference / (float)expectedOutput.Length; + // Expect the output to be 98% similar to the expected resampled sine wave + Assert.IsTrue(sumDifference < 0.02f); + } + } +} From e54f9dc4b42e0c4091875989df24710956bf2e10 Mon Sep 17 00:00:00 2001 From: Andrew Glaze <andrew.glaze76@gmail.com> Date: Tue, 21 Feb 2023 06:14:31 -0500 Subject: [PATCH 365/737] Move Ryujinx Folder from ~/.config to ~/Library/Application Support on macOS (#4296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move Ryujinx folder to Application Support on macOS * Create a symlink to preserve back compat Co-authored-by: Ac_K <Acoustik666@gmail.com> * Remove extra whitespace * Don’t create a symlink * Update Ryujinx.Common/Configuration/AppDataManager.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Revert "Don’t create a symlink" This reverts commit 31752fe8aba1deb32e75f949001ffb74a1e0f674. --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../Configuration/AppDataManager.cs | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Common/Configuration/AppDataManager.cs b/Ryujinx.Common/Configuration/AppDataManager.cs index 42b76453b..d6e778430 100644 --- a/Ryujinx.Common/Configuration/AppDataManager.cs +++ b/Ryujinx.Common/Configuration/AppDataManager.cs @@ -45,7 +45,15 @@ namespace Ryujinx.Common.Configuration public static void Initialize(string baseDirPath) { - string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + string appDataPath; + if (OperatingSystem.IsMacOS()) + { + appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support"); + } + else + { + appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + } if (appDataPath.Length == 0) { @@ -81,6 +89,21 @@ namespace Ryujinx.Common.Configuration BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths + // NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found + // and a Ryujinx folder does not already exist in Application Support. + // Also creates a symlink from `~/.config/Ryujinx` to `~/Library/Application Support/Ryujinx` to preserve backwards compatibility. + // This should be removed in the future. + if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile) + { + string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir); + if (Path.Exists(oldConfigPath) && !Path.Exists(BaseDirPath)) + { + CopyDirectory(oldConfigPath, BaseDirPath); + Directory.Delete(oldConfigPath, true); + Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath); + } + } + SetupBasePaths(); } @@ -92,6 +115,34 @@ namespace Ryujinx.Common.Configuration Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir)); } + private static void CopyDirectory(string sourceDir, string destinationDir) + { + var dir = new DirectoryInfo(sourceDir); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); + } + + DirectoryInfo[] subDirs = dir.GetDirectories(); + Directory.CreateDirectory(destinationDir); + + foreach (FileInfo file in dir.GetFiles()) + { + if (file.Name == ".DS_Store") + { + continue; + } + + file.CopyTo(Path.Combine(destinationDir, file.Name)); + } + + foreach (DirectoryInfo subDir in subDirs) + { + CopyDirectory(subDir.FullName, Path.Combine(destinationDir, subDir.Name)); + } + } + public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName; public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName; } From 1f1e2a7f03aad988cb04045eee18a360a807d13f Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Tue, 21 Feb 2023 22:38:34 +0100 Subject: [PATCH 366/737] misc: changes base application directory behaviour (#4460) This allows changing base application directory behavior at build time via FORCE_EXTERNAL_BASE_DIR. This is intended to be used by nixpkgs and flathub builds. I also added the missing patch for macOS that we have on macos1 to avoid invalidating code signature. --- Ryujinx.Common/ReleaseInformation.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Common/ReleaseInformation.cs b/Ryujinx.Common/ReleaseInformation.cs index d0e013282..601c05b17 100644 --- a/Ryujinx.Common/ReleaseInformation.cs +++ b/Ryujinx.Common/ReleaseInformation.cs @@ -40,14 +40,21 @@ namespace Ryujinx.Common } } +#if FORCE_EXTERNAL_BASE_DIR public static string GetBaseApplicationDirectory() { - if (IsFlatHubBuild()) + return AppDataManager.BaseDirPath; + } +#else + public static string GetBaseApplicationDirectory() + { + if (IsFlatHubBuild() || OperatingSystem.IsMacOS()) { return AppDataManager.BaseDirPath; } return AppDomain.CurrentDomain.BaseDirectory; } +#endif } } \ No newline at end of file From c3a5716a95ea93cba9488189fb36d594db5083bc Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 21 Feb 2023 19:21:57 -0300 Subject: [PATCH 367/737] Add copy dependency for some incompatible texture formats (#4380) * Add copy dependency for some incompatible texture formats * Simplify compatibility check --- .../Image/TextureCompatibility.cs | 103 ++++--- .../Image/TextureCopyIncompatible.cs | 252 +++++++++++++++++ Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 10 + Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 2 + Ryujinx.Graphics.Vulkan/HelperShader.cs | 158 ++++++++++- ...olorCopyShorteningComputeShaderSource.comp | 36 +++ .../ColorCopyWideningComputeShaderSource.comp | 31 +++ .../Shaders/ShaderBinaries.cs | 259 ++++++++++++++++++ Ryujinx.Graphics.Vulkan/TextureView.cs | 10 + 9 files changed, 814 insertions(+), 47 deletions(-) create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index e8061951b..4b84333df 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -214,41 +214,6 @@ namespace Ryujinx.Graphics.Gpu.Image return true; } - /// <summary> - /// Checks if two formats are compatible, according to the host API copy format compatibility rules. - /// </summary> - /// <param name="lhsFormat">First comparand</param> - /// <param name="rhsFormat">Second comparand</param> - /// <param name="caps">Host GPU capabilities</param> - /// <returns>True if the formats are compatible, false otherwise</returns> - public static bool FormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) - { - FormatInfo lhsFormat = lhs.FormatInfo; - FormatInfo rhsFormat = rhs.FormatInfo; - - if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil()) - { - return lhsFormat.Format == rhsFormat.Format; - } - - if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps)) - { - return lhsFormat.Format == rhsFormat.Format; - } - - if (lhsFormat.IsCompressed && rhsFormat.IsCompressed) - { - FormatClass lhsClass = GetFormatClass(lhsFormat.Format); - FormatClass rhsClass = GetFormatClass(rhsFormat.Format); - - return lhsClass == rhsClass; - } - else - { - return lhsFormat.BytesPerPixel == rhsFormat.BytesPerPixel; - } - } - /// <summary> /// Checks if the texture format matches with the specified texture information. /// </summary> @@ -391,6 +356,13 @@ namespace Ryujinx.Graphics.Gpu.Image Size lhsSize = GetSizeInBlocks(lhs, level); Size rhsSize = GetSizeInBlocks(rhs); + bool alignedWidthMatches = lhsAlignedSize.Width == rhsAlignedSize.Width; + + if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo)) + { + alignedWidthMatches = lhsSize.Width * lhs.FormatInfo.BytesPerPixel == rhsSize.Width * rhs.FormatInfo.BytesPerPixel; + } + TextureViewCompatibility result = TextureViewCompatibility.Full; // For copies, we can copy a subset of the 3D texture slices, @@ -404,7 +376,7 @@ namespace Ryujinx.Graphics.Gpu.Image // so the width may not match in this case for different uses of the same texture. // To account for this, we compare the aligned width here. // We expect height to always match exactly, if the texture is the same. - if (lhsAlignedSize.Width == rhsAlignedSize.Width && lhsSize.Height == rhsSize.Height) + if (alignedWidthMatches && lhsSize.Height == rhsSize.Height) { return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width ? TextureViewCompatibility.CopyOnly @@ -659,21 +631,62 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>The view compatibility level of the texture formats</returns> public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) { - if (FormatCompatible(lhs, rhs, caps)) + FormatInfo lhsFormat = lhs.FormatInfo; + FormatInfo rhsFormat = rhs.FormatInfo; + + if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil()) { - if (lhs.FormatInfo.IsCompressed != rhs.FormatInfo.IsCompressed) - { - return TextureViewCompatibility.CopyOnly; - } - else - { - return TextureViewCompatibility.Full; - } + return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; + } + + if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps)) + { + return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; + } + + if (lhsFormat.IsCompressed && rhsFormat.IsCompressed) + { + FormatClass lhsClass = GetFormatClass(lhsFormat.Format); + FormatClass rhsClass = GetFormatClass(rhsFormat.Format); + + return lhsClass == rhsClass ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; + } + else if (lhsFormat.BytesPerPixel == rhsFormat.BytesPerPixel) + { + return lhs.FormatInfo.IsCompressed == rhs.FormatInfo.IsCompressed + ? TextureViewCompatibility.Full + : TextureViewCompatibility.CopyOnly; + } + else if (IsIncompatibleFormatAliasingAllowed(lhsFormat, rhsFormat)) + { + return TextureViewCompatibility.CopyOnly; } return TextureViewCompatibility.Incompatible; } + /// <summary> + /// Checks if aliasing of two formats that would normally be considered incompatible be allowed, + /// using copy dependencies. + /// </summary> + /// <param name="lhsFormat">Format information of the first texture</param + /// <param name="rhsFormat">Format information of the second texture</param> + /// <returns>True if aliasing should be allowed, false otherwise</returns> + private static bool IsIncompatibleFormatAliasingAllowed(FormatInfo lhsFormat, FormatInfo rhsFormat) + { + // Some games will try to alias textures with incompatible foramts, with different BPP (bytes per pixel). + // We allow that in some cases as long Width * BPP is equal on both textures. + // This is very conservative right now as we want to avoid copies as much as possible, + // so we only consider the formats we have seen being aliased. + + if (rhsFormat.BytesPerPixel < lhsFormat.BytesPerPixel) + { + (lhsFormat, rhsFormat) = (rhsFormat, lhsFormat); + } + + return lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm; + } + /// <summary> /// Check if the target of the first texture view information is compatible with the target of the second texture view information. /// This follows the host API target compatibility rules. diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs new file mode 100644 index 000000000..c8fbfbc6a --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs @@ -0,0 +1,252 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Numerics; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class TextureCopyIncompatible + { + private const string ComputeShaderShortening = @"#version 450 core + +layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src; +layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst; + +layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; + +void main() +{ + uvec2 coords = gl_GlobalInvocationID.xy; + ivec2 imageSz = imageSize(src); + + if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) + { + return; + } + + uint coordsShifted = coords.x << $RATIO_LOG2$; + + uvec2 dstCoords0 = uvec2(coordsShifted, coords.y); + uvec2 dstCoords1 = uvec2(coordsShifted + 1, coords.y); + uvec2 dstCoords2 = uvec2(coordsShifted + 2, coords.y); + uvec2 dstCoords3 = uvec2(coordsShifted + 3, coords.y); + + uvec4 rgba = imageLoad(src, ivec2(coords)); + + imageStore(dst, ivec2(dstCoords0), rgba.rrrr); + imageStore(dst, ivec2(dstCoords1), rgba.gggg); + imageStore(dst, ivec2(dstCoords2), rgba.bbbb); + imageStore(dst, ivec2(dstCoords3), rgba.aaaa); +}"; + + private const string ComputeShaderWidening = @"#version 450 core + +layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src; +layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst; + +layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; + +void main() +{ + uvec2 coords = gl_GlobalInvocationID.xy; + ivec2 imageSz = imageSize(dst); + + if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) + { + return; + } + + uvec2 srcCoords = uvec2(coords.x << $RATIO_LOG2$, coords.y); + + uint r = imageLoad(src, ivec2(srcCoords) + ivec2(0, 0)).r; + uint g = imageLoad(src, ivec2(srcCoords) + ivec2(1, 0)).r; + uint b = imageLoad(src, ivec2(srcCoords) + ivec2(2, 0)).r; + uint a = imageLoad(src, ivec2(srcCoords) + ivec2(3, 0)).r; + + imageStore(dst, ivec2(coords), uvec4(r, g, b, a)); +}"; + + private readonly OpenGLRenderer _renderer; + private readonly Dictionary<int, int> _shorteningProgramHandles; + private readonly Dictionary<int, int> _wideningProgramHandles; + + public TextureCopyIncompatible(OpenGLRenderer renderer) + { + _renderer = renderer; + _shorteningProgramHandles = new Dictionary<int, int>(); + _wideningProgramHandles = new Dictionary<int, int>(); + } + + public void CopyIncompatibleFormats(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int depth, int levels) + { + TextureCreateInfo srcInfo = src.Info; + TextureCreateInfo dstInfo = dst.Info; + + int srcBpp = src.Info.BytesPerPixel; + int dstBpp = dst.Info.BytesPerPixel; + + // Calculate ideal component size, given our constraints: + // - Component size must not exceed bytes per pixel of source and destination image formats. + // - Maximum component size is 4 (R32). + int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4); + + int srcComponentsCount = srcBpp / componentSize; + int dstComponentsCount = dstBpp / componentSize; + + var srcFormat = GetFormat(componentSize, srcComponentsCount); + var dstFormat = GetFormat(componentSize, dstComponentsCount); + + GL.UseProgram(srcBpp < dstBpp + ? GetWideningShader(componentSize, srcComponentsCount, dstComponentsCount) + : GetShorteningShader(componentSize, srcComponentsCount, dstComponentsCount)); + + for (int l = 0; l < levels; l++) + { + int srcWidth = Math.Max(1, src.Info.Width >> l); + int srcHeight = Math.Max(1, src.Info.Height >> l); + + int dstWidth = Math.Max(1, dst.Info.Width >> l); + int dstHeight = Math.Max(1, dst.Info.Height >> l); + + int width = Math.Min(srcWidth, dstWidth); + int height = Math.Min(srcHeight, dstHeight); + + for (int z = 0; z < depth; z++) + { + GL.BindImageTexture(0, src.Handle, srcLevel + l, false, srcLayer + z, TextureAccess.ReadOnly, srcFormat); + GL.BindImageTexture(1, dst.Handle, dstLevel + l, false, dstLayer + z, TextureAccess.WriteOnly, dstFormat); + + GL.DispatchCompute((width + 31) / 32, (height + 31) / 32, 1); + } + } + + Pipeline pipeline = (Pipeline)_renderer.Pipeline; + + pipeline.RestoreProgram(); + pipeline.RestoreImages1And2(); + } + + private static SizedInternalFormat GetFormat(int componentSize, int componentsCount) + { + if (componentSize == 1) + { + return componentsCount switch + { + 1 => SizedInternalFormat.R8ui, + 2 => SizedInternalFormat.Rg8ui, + 4 => SizedInternalFormat.Rgba8ui, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else if (componentSize == 2) + { + return componentsCount switch + { + 1 => SizedInternalFormat.R16ui, + 2 => SizedInternalFormat.Rg16ui, + 4 => SizedInternalFormat.Rgba16ui, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else if (componentSize == 4) + { + return componentsCount switch + { + 1 => SizedInternalFormat.R32ui, + 2 => SizedInternalFormat.Rg32ui, + 4 => SizedInternalFormat.Rgba32ui, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else + { + throw new ArgumentException($"Invalid component size {componentSize}."); + } + } + + private int GetShorteningShader(int componentSize, int srcComponentsCount, int dstComponentsCount) + { + return GetShader(ComputeShaderShortening, _shorteningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount); + } + + private int GetWideningShader(int componentSize, int srcComponentsCount, int dstComponentsCount) + { + return GetShader(ComputeShaderWidening, _wideningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount); + } + + private int GetShader( + string code, + Dictionary<int, int> programHandles, + int componentSize, + int srcComponentsCount, + int dstComponentsCount) + { + int componentSizeLog2 = BitOperations.Log2((uint)componentSize); + + int srcIndex = componentSizeLog2 + BitOperations.Log2((uint)srcComponentsCount) * 3; + int dstIndex = componentSizeLog2 + BitOperations.Log2((uint)dstComponentsCount) * 3; + + int key = srcIndex | (dstIndex << 8); + + if (!programHandles.TryGetValue(key, out int programHandle)) + { + int csHandle = GL.CreateShader(ShaderType.ComputeShader); + + string[] formatTable = new[] { "r8ui", "r16ui", "r32ui", "rg8ui", "rg16ui", "rg32ui", "rgba8ui", "rgba16ui", "rgba32ui" }; + + string srcFormat = formatTable[srcIndex]; + string dstFormat = formatTable[dstIndex]; + + int srcBpp = srcComponentsCount * componentSize; + int dstBpp = dstComponentsCount * componentSize; + + int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp; + int ratioLog2 = BitOperations.Log2((uint)ratio); + + GL.ShaderSource(csHandle, code + .Replace("$SRC_FORMAT$", srcFormat) + .Replace("$DST_FORMAT$", dstFormat) + .Replace("$RATIO_LOG2$", ratioLog2.ToString(CultureInfo.InvariantCulture))); + + GL.CompileShader(csHandle); + + programHandle = GL.CreateProgram(); + + GL.AttachShader(programHandle, csHandle); + GL.LinkProgram(programHandle); + GL.DetachShader(programHandle, csHandle); + GL.DeleteShader(csHandle); + + GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status); + + if (status == 0) + { + throw new Exception(GL.GetProgramInfoLog(programHandle)); + } + + programHandles.Add(key, programHandle); + } + + return programHandle; + } + + public void Dispose() + { + foreach (int handle in _shorteningProgramHandles.Values) + { + GL.DeleteProgram(handle); + } + + _shorteningProgramHandles.Clear(); + + foreach (int handle in _wideningProgramHandles.Values) + { + GL.DeleteProgram(handle); + } + + _wideningProgramHandles.Clear(); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 68cd2d30f..44df441f7 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -127,6 +127,12 @@ namespace Ryujinx.Graphics.OpenGL.Image int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers); } + else if (destinationView.Info.BytesPerPixel != Info.BytesPerPixel) + { + int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); + int levels = Math.Min(Info.Levels, destinationView.Info.Levels - firstLevel); + _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, 0, firstLayer, 0, firstLevel, layers, levels); + } else { _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); @@ -145,6 +151,10 @@ namespace Ryujinx.Graphics.OpenGL.Image { _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1); } + else if (destinationView.Info.BytesPerPixel != Info.BytesPerPixel) + { + _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } else { _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 722c4b4da..efbd17c1b 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.OpenGL private TextureCopy _textureCopy; private TextureCopy _backgroundTextureCopy; internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy; + internal TextureCopyIncompatible TextureCopyIncompatible { get; } internal TextureCopyMS TextureCopyMS { get; } private Sync _sync; @@ -49,6 +50,7 @@ namespace Ryujinx.Graphics.OpenGL _window = new Window(this); _textureCopy = new TextureCopy(this); _backgroundTextureCopy = new TextureCopy(this); + TextureCopyIncompatible = new TextureCopyIncompatible(this); TextureCopyMS = new TextureCopyMS(this); _sync = new Sync(); PersistentBuffers = new PersistentBuffers(); diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index b8c21fe8e..c67389aa4 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Vulkan.Shaders; using Silk.NET.Vulkan; using System; using System.Collections.Generic; +using System.Numerics; using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan @@ -32,7 +33,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programStrideChange; private readonly IProgram _programConvertIndexBuffer; private readonly IProgram _programConvertIndirectData; + private readonly IProgram _programColorCopyShortening; private readonly IProgram _programColorCopyToNonMs; + private readonly IProgram _programColorCopyWidening; private readonly IProgram _programColorDrawToMs; private readonly IProgram _programDepthBlit; private readonly IProgram _programDepthBlitMs; @@ -112,15 +115,25 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv), }); - var colorCopyToNonMsBindings = new ShaderBindings( + var colorCopyBindings = new ShaderBindings( new[] { 0 }, Array.Empty<int>(), new[] { 0 }, new[] { 0 }); + _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyToNonMsBindings, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + + _programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), }); var colorDrawToMsVertexBindings = new ShaderBindings( @@ -922,6 +935,107 @@ namespace Ryujinx.Graphics.Vulkan convertedCount * outputIndexSize); } + public void CopyIncompatibleFormats( + VulkanRenderer gd, + CommandBufferScoped cbs, + TextureView src, + TextureView dst, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel, + int depth, + int levels) + { + const int ParamsBufferSize = 4; + + Span<int> shaderParams = stackalloc int[sizeof(int)]; + + int srcBpp = src.Info.BytesPerPixel; + int dstBpp = dst.Info.BytesPerPixel; + + int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp; + + shaderParams[0] = BitOperations.Log2((uint)ratio); + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false); + + gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + src.GetImage().Get(cbs).Value, + TextureStorage.DefaultAccessMask, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.ComputeShaderBit, + ImageAspectFlags.ColorBit, + src.FirstLayer + srcLayer, + src.FirstLevel + srcLevel, + depth, + levels); + + _pipeline.SetCommandBuffer(cbs); + + _pipeline.SetProgram(srcBpp < dstBpp ? _programColorCopyWidening : _programColorCopyShortening); + + // Calculate ideal component size, given our constraints: + // - Component size must not exceed bytes per pixel of source and destination image formats. + // - Maximum component size is 4 (R32). + int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4); + + var srcFormat = GetFormat(componentSize, srcBpp / componentSize); + var dstFormat = GetFormat(componentSize, dstBpp / componentSize); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + + for (int l = 0; l < levels; l++) + { + for (int z = 0; z < depth; z++) + { + var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat); + var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l); + + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); + _pipeline.SetImage(0, dstView, dstFormat); + + int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; + int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32; + + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } + } + + gd.BufferManager.Delete(bufferHandle); + + _pipeline.Finish(gd, cbs); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + dst.GetImage().Get(cbs).Value, + AccessFlags.ShaderWriteBit, + TextureStorage.DefaultAccessMask, + PipelineStageFlags.ComputeShaderBit, + PipelineStageFlags.AllCommandsBit, + ImageAspectFlags.ColorBit, + dst.FirstLayer + dstLayer, + dst.FirstLevel + dstLevel, + depth, + levels); + } + public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth) { const int ParamsBufferSize = 16; @@ -1196,6 +1310,44 @@ namespace Ryujinx.Graphics.Vulkan }; } + private static GAL.Format GetFormat(int componentSize, int componentsCount) + { + if (componentSize == 1) + { + return componentsCount switch + { + 1 => GAL.Format.R8Uint, + 2 => GAL.Format.R8G8Uint, + 4 => GAL.Format.R8G8B8A8Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else if (componentSize == 2) + { + return componentsCount switch + { + 1 => GAL.Format.R16Uint, + 2 => GAL.Format.R16G16Uint, + 4 => GAL.Format.R16G16B16A16Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else if (componentSize == 4) + { + return componentsCount switch + { + 1 => GAL.Format.R32Uint, + 2 => GAL.Format.R32G32Uint, + 4 => GAL.Format.R32G32B32A32Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else + { + throw new ArgumentException($"Invalid component size {componentSize}."); + } + } + public void ConvertIndexBufferIndirect( VulkanRenderer gd, CommandBufferScoped cbs, @@ -1336,7 +1488,9 @@ namespace Ryujinx.Graphics.Vulkan _programStrideChange.Dispose(); _programConvertIndexBuffer.Dispose(); _programConvertIndirectData.Dispose(); + _programColorCopyShortening.Dispose(); _programColorCopyToNonMs.Dispose(); + _programColorCopyWidening.Dispose(); _programColorDrawToMs.Dispose(); _programDepthBlit.Dispose(); _programDepthBlitMs.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp b/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp new file mode 100644 index 000000000..78cc1cc6f --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp @@ -0,0 +1,36 @@ +#version 450 core + +layout (std140, binding = 0) uniform ratio_in +{ + int ratio; +}; + +layout (set = 2, binding = 0) uniform usampler2D src; +layout (set = 3, binding = 0) writeonly uniform uimage2D dst; + +layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; + +void main() +{ + uvec2 coords = gl_GlobalInvocationID.xy; + ivec2 textureSz = textureSize(src, 0); + + if (int(coords.x) >= textureSz.x || int(coords.y) >= textureSz.y) + { + return; + } + + uint coordsShifted = coords.x << ratio; + + uvec2 dstCoords0 = uvec2(coordsShifted, coords.y); + uvec2 dstCoords1 = uvec2(coordsShifted + 1, coords.y); + uvec2 dstCoords2 = uvec2(coordsShifted + 2, coords.y); + uvec2 dstCoords3 = uvec2(coordsShifted + 3, coords.y); + + uvec4 rgba = texelFetch(src, ivec2(coords), 0); + + imageStore(dst, ivec2(dstCoords0), rgba.rrrr); + imageStore(dst, ivec2(dstCoords1), rgba.gggg); + imageStore(dst, ivec2(dstCoords2), rgba.bbbb); + imageStore(dst, ivec2(dstCoords3), rgba.aaaa); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp b/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp new file mode 100644 index 000000000..a9be454fa --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp @@ -0,0 +1,31 @@ +#version 450 core + +layout (std140, binding = 0) uniform ratio_in +{ + int ratio; +}; + +layout (set = 2, binding = 0) uniform usampler2D src; +layout (set = 3, binding = 0) writeonly uniform uimage2D dst; + +layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; + +void main() +{ + uvec2 coords = gl_GlobalInvocationID.xy; + ivec2 imageSz = imageSize(dst); + + if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) + { + return; + } + + uvec2 srcCoords = uvec2(coords.x << ratio, coords.y); + + uint r = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(0, 0)).r; + uint g = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(1, 0)).r; + uint b = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(2, 0)).r; + uint a = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(3, 0)).r; + + imageStore(dst, ivec2(coords), uvec4(r, g, b, a)); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index c9df894bc..7fd047a23 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -669,6 +669,138 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x35, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + public static readonly byte[] ColorCopyShorteningComputeShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x73, 0x72, 0x63, 0x00, 0x05, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, + 0x6F, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x76, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xF7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x67, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x77, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x4E, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x56, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x61, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x6A, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x5D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x77, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x77, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + public static readonly byte[] ColorCopyToNonMsComputeShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x86, 0x00, 0x00, 0x00, @@ -801,6 +933,133 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x84, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + public static readonly byte[] ColorCopyWideningComputeShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x64, 0x73, 0x74, 0x00, 0x05, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, + 0x6F, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x73, 0x72, 0x63, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xF7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x71, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4A, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x6D, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x66, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6D, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + public static readonly byte[] ColorDrawToMsVertexShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x2E, 0x00, 0x00, 0x00, diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index aa050c015..264ecf5db 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -199,6 +199,12 @@ namespace Ryujinx.Graphics.Vulkan 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 { TextureCopy.Copy( @@ -244,6 +250,10 @@ namespace Ryujinx.Graphics.Vulkan { _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 { TextureCopy.Copy( From 1f8d66db7c91a3242629edca84f4df9c17a832ef Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 22 Feb 2023 09:13:50 +0100 Subject: [PATCH 368/737] Ava: Fix Updater crashing on Linux (#4457) --- Ryujinx.Ava/Modules/Updater/Updater.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index b476bb85b..511e273e5 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -506,6 +506,11 @@ namespace Ryujinx.Modules Dispatcher.UIThread.Post(() => { + if (tarEntry is null) + { + return; + } + taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal); }); } From f1eef29409e393e2470557cec65268f925099880 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:30:53 +0100 Subject: [PATCH 369/737] nuget: bump UnicornEngine.Unicorn (#4459) Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-9c9356d to 2.0.2-rc1-a913199. - [Release notes](https://github.com/unicorn-engine/unicorn/releases) - [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog) - [Commits](https://github.com/unicorn-engine/unicorn/commits) --- updated-dependencies: - dependency-name: UnicornEngine.Unicorn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6f5ed3d71..9ddf8a63d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -49,7 +49,7 @@ <PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> - <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-9c9356d" /> + <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-a913199" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> </ItemGroup> </Project> \ No newline at end of file From c308f09722fdcd46b71c7f0892ebeb31a6345b3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:08:25 +0100 Subject: [PATCH 370/737] nuget: bump Microsoft.NET.Test.Sdk from 17.4.1 to 17.5.0 (#4458) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.1 to 17.5.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.4.1...v17.5.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 9ddf8a63d..528dc4b4a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ <PackageVersion Include="LibHac" Version="0.17.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" /> - <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> <PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" /> From f07ae7d53f511af38d9735c8a0aecc1cce6dc12b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Wed, 22 Feb 2023 15:58:32 -0500 Subject: [PATCH 371/737] Fix Title Update Manager not selecting right update (#4452) --- Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index 3d0b20f7e..f330006e3 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -105,13 +105,13 @@ public class TitleUpdateViewModel : BaseModel AddUpdate(path); } - // NOTE: Save the list again to remove leftovers. - Save(); - TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); SelectedUpdate = selected; + // NOTE: Save the list again to remove leftovers. + Save(); + SortUpdates(); } From 095ad923ad24c44e51ee6cee60edd50ee470fd71 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 23 Feb 2023 06:08:54 -0300 Subject: [PATCH 372/737] Account for multisample when calculating render target size hint (#4467) --- Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index ecfd763f6..d7d197adb 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -439,7 +439,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int samplesInY = msaaMode.SamplesInY(); var scissor = _state.State.ScreenScissorState; - Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1); + Size sizeHint = new Size((scissor.X + scissor.Width) * samplesInX, (scissor.Y + scissor.Height) * samplesInY, 1); int clipRegionWidth = int.MaxValue; int clipRegionHeight = int.MaxValue; From 58207685c0dcda07d18f5f538629c775e2a714b8 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sat, 25 Feb 2023 05:26:39 -0500 Subject: [PATCH 373/737] Perform bounds checking before list indexer to avoid frequent exceptions (#4438) * Perform bounds checking before list indexer to avoid frequent ArgumentOutOfRangeExceptions * do a single compare after casting id and .Count to uint --- Ryujinx.Graphics.Vulkan/IdList.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/IdList.cs b/Ryujinx.Graphics.Vulkan/IdList.cs index d5a87a058..5c0623c3f 100644 --- a/Ryujinx.Graphics.Vulkan/IdList.cs +++ b/Ryujinx.Graphics.Vulkan/IdList.cs @@ -80,8 +80,16 @@ namespace Ryujinx.Graphics.Vulkan try { - value = _list[id]; - return value != null; + if ((uint)id < (uint)_list.Count) + { + value = _list[id]; + return value != null; + } + else + { + value = null; + return false; + } } catch (ArgumentOutOfRangeException) { From cedd2007451c046a1276556bacb4e19333b11557 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 25 Feb 2023 07:39:51 -0300 Subject: [PATCH 374/737] Move gl_Layer to vertex shader if geometry is not supported (#4368) * Set gl_Layer on vertex shader if it's set on the geometry shader and it does nothing else * Shader cache version bump * PR feedback * Fix typo --- Ryujinx.Graphics.GAL/Capabilities.cs | 3 + .../Shader/DiskCache/DiskCacheHostStorage.cs | 4 +- .../DiskCache/ParallelDiskCacheLoader.cs | 5 + .../Shader/GpuAccessorBase.cs | 2 + Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 38 +++++ Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 1 + Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 ++ .../ShaderIdentification.cs | 8 + Ryujinx.Graphics.Shader/ShaderProgramInfo.cs | 6 + .../Translation/EmitterContext.cs | 7 + .../Translation/ShaderConfig.cs | 22 ++- .../Translation/ShaderIdentifier.cs | 145 ++++++++++++++++++ .../Translation/Translator.cs | 4 +- .../Translation/TranslatorContext.cs | 10 ++ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 15 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 Ryujinx.Graphics.Shader/ShaderIdentification.cs create mode 100644 Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index a24139eba..7822da211 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsBlendEquationAdvanced; public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderOrderingIntel; + public readonly bool SupportsGeometryShader; public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsImageLoadFormatted; public readonly bool SupportsLayerVertexTessellation; @@ -68,6 +69,7 @@ namespace Ryujinx.Graphics.GAL bool supportsBlendEquationAdvanced, bool supportsFragmentShaderInterlock, bool supportsFragmentShaderOrderingIntel, + bool supportsGeometryShader, bool supportsGeometryShaderPassthrough, bool supportsImageLoadFormatted, bool supportsLayerVertexTessellation, @@ -107,6 +109,7 @@ namespace Ryujinx.Graphics.GAL SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; + SupportsGeometryShader = supportsGeometryShader; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsImageLoadFormatted = supportsImageLoadFormatted; SupportsLayerVertexTessellation = supportsLayerVertexTessellation; diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 1f6dab893..edc5a8a08 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4369; + private const uint CodeGenVersion = 4368; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; @@ -774,6 +774,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache sBuffers, textures, images, + ShaderIdentification.None, + 0, dataInfo.Stage, dataInfo.UsesInstanceId, dataInfo.UsesDrawParameters, diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 722e66b36..77fb3ca4b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -633,6 +633,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } } + if (!_context.Capabilities.SupportsGeometryShader) + { + ShaderCache.TryRemoveGeometryStage(translatorContexts); + } + CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length]; List<ShaderProgram> translatedStages = new List<ShaderProgram>(); diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index d36ffd70f..1402f146b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -126,6 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel; + public bool QueryHostSupportsGeometryShader() => _context.Capabilities.SupportsGeometryShader; + public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough; public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 5c045d9ba..11f7085d3 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -353,6 +353,11 @@ namespace Ryujinx.Graphics.Gpu.Shader } } + if (!_context.Capabilities.SupportsGeometryShader) + { + TryRemoveGeometryStage(translatorContexts); + } + CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; List<ShaderSource> shaderSources = new List<ShaderSource>(); @@ -421,6 +426,39 @@ namespace Ryujinx.Graphics.Gpu.Shader return gpShaders; } + /// <summary> + /// Tries to eliminate the geometry stage from the array of translator contexts. + /// </summary> + /// <param name="translatorContexts">Array of translator contexts</param> + public static void TryRemoveGeometryStage(TranslatorContext[] translatorContexts) + { + if (translatorContexts[4] != null) + { + // We have a geometry shader, but geometry shaders are not supported. + // Try to eliminate the geometry shader. + + ShaderProgramInfo info = translatorContexts[4].Translate().Info; + + if (info.Identification == ShaderIdentification.GeometryLayerPassthrough) + { + // We managed to identify that this geometry shader is only used to set the output Layer value, + // we can set the Layer on the previous stage instead (usually the vertex stage) and eliminate it. + + for (int i = 3; i >= 1; i--) + { + if (translatorContexts[i] != null) + { + translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute); + translatorContexts[i].SetLastInVertexPipeline(translatorContexts[5] != null); + break; + } + } + + translatorContexts[4] = null; + } + } + } + /// <summary> /// Creates a shader source for use with the backend from a translated shader program. /// </summary> diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index efbd17c1b..9490684cd 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -124,6 +124,7 @@ namespace Ryujinx.Graphics.OpenGL supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced, supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, + supportsGeometryShader: true, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted, supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 55df8dc31..f364437c7 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -259,6 +259,15 @@ namespace Ryujinx.Graphics.Shader return false; } + /// <summary> + /// Queries host GPU geometry shader support. + /// </summary> + /// <returns>True if the GPU and driver supports geometry shaders, false otherwise</returns> + bool QueryHostSupportsGeometryShader() + { + return true; + } + /// <summary> /// Queries host GPU geometry shader passthrough support. /// </summary> diff --git a/Ryujinx.Graphics.Shader/ShaderIdentification.cs b/Ryujinx.Graphics.Shader/ShaderIdentification.cs new file mode 100644 index 000000000..3f0157626 --- /dev/null +++ b/Ryujinx.Graphics.Shader/ShaderIdentification.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Shader +{ + public enum ShaderIdentification + { + None, + GeometryLayerPassthrough + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index bb75b10ae..30f0ffaa2 100644 --- a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Shader public ReadOnlyCollection<TextureDescriptor> Textures { get; } public ReadOnlyCollection<TextureDescriptor> Images { get; } + public ShaderIdentification Identification { get; } + public int GpLayerInputAttribute { get; } public ShaderStage Stage { get; } public bool UsesInstanceId { get; } public bool UsesDrawParameters { get; } @@ -22,6 +24,8 @@ namespace Ryujinx.Graphics.Shader BufferDescriptor[] sBuffers, TextureDescriptor[] textures, TextureDescriptor[] images, + ShaderIdentification identification, + int gpLayerInputAttribute, ShaderStage stage, bool usesInstanceId, bool usesDrawParameters, @@ -34,6 +38,8 @@ namespace Ryujinx.Graphics.Shader Textures = Array.AsReadOnly(textures); Images = Array.AsReadOnly(images); + Identification = identification; + GpLayerInputAttribute = gpLayerInputAttribute; Stage = stage; UsesInstanceId = usesInstanceId; UsesDrawParameters = usesDrawParameters; diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index ad55c0109..8f33cceda 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -241,6 +241,13 @@ namespace Ryujinx.Graphics.Shader.Translation this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW)); } + + if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute) + { + Config.SetUsedFeature(FeatureFlags.RtLayer); + + this.Copy(Attribute(AttributeConsts.Layer), Attribute(Config.GpLayerInputAttribute | AttributeConsts.LoadOutputMask)); + } } public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal) diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index a79ef6f57..2caa8f638 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -20,6 +20,8 @@ namespace Ryujinx.Graphics.Shader.Translation public bool LastInPipeline { get; private set; } public bool LastInVertexPipeline { get; private set; } + public bool HasLayerInputAttribute { get; private set; } + public int GpLayerInputAttribute { get; private set; } public int ThreadsPerInputPrimitive { get; } public OutputTopology OutputTopology { get; } @@ -245,6 +247,22 @@ namespace Ryujinx.Graphics.Shader.Translation LayerOutputAttribute = attr; } + public void SetGeometryShaderLayerInputAttribute(int attr) + { + HasLayerInputAttribute = true; + GpLayerInputAttribute = attr; + } + + public void SetLastInVertexPipeline(bool hasFragment) + { + if (!hasFragment) + { + LastInPipeline = true; + } + + LastInVertexPipeline = true; + } + public void SetInputUserAttributeFixedFunc(int index) { UsedInputAttributes |= 1 << index; @@ -706,13 +724,15 @@ namespace Ryujinx.Graphics.Shader.Translation return FindDescriptorIndex(GetImageDescriptors(), texOp); } - public ShaderProgramInfo CreateProgramInfo() + public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None) { return new ShaderProgramInfo( GetConstantBufferDescriptors(), GetStorageBufferDescriptors(), GetTextureDescriptors(), GetImageDescriptors(), + identification, + GpLayerInputAttribute, Stage, UsedFeatures.HasFlag(FeatureFlags.InstanceId), UsedFeatures.HasFlag(FeatureFlags.DrawParameters), diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs new file mode 100644 index 000000000..206718f2a --- /dev/null +++ b/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -0,0 +1,145 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + static class ShaderIdentifier + { + public static ShaderIdentification Identify(Function[] functions, ShaderConfig config) + { + if (config.Stage == ShaderStage.Geometry && + config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles && + !config.GpuAccessor.QueryHostSupportsGeometryShader() && + IsLayerPassthroughGeometryShader(functions, out int layerInputAttr)) + { + config.SetGeometryShaderLayerInputAttribute(layerInputAttr); + + return ShaderIdentification.GeometryLayerPassthrough; + } + + return ShaderIdentification.None; + } + + private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr) + { + bool writesLayer = false; + layerInputAttr = 0; + + if (functions.Length != 1) + { + return false; + } + + int verticesCount = 0; + int totalVerticesCount = 0; + + foreach (BasicBlock block in functions[0].Blocks) + { + // We are not expecting loops or any complex control flow here, so fail in those cases. + if (block.Branch != null && block.Branch.Index <= block.Index) + { + return false; + } + + foreach (INode node in block.Operations) + { + if (!(node is Operation operation)) + { + continue; + } + + if (IsResourceWrite(operation.Inst)) + { + return false; + } + + if (operation.Inst == Instruction.StoreAttribute) + { + return false; + } + + if (operation.Inst == Instruction.Copy && operation.Dest.Type == OperandType.Attribute) + { + Operand src = operation.GetSource(0); + + if (src.Type == OperandType.LocalVariable && src.AsgOp is Operation asgOp && asgOp.Inst == Instruction.LoadAttribute) + { + src = Attribute(asgOp.GetSource(0).Value); + } + + if (src.Type == OperandType.Attribute) + { + if (operation.Dest.Value == AttributeConsts.Layer) + { + if ((src.Value & AttributeConsts.LoadOutputMask) != 0) + { + return false; + } + + writesLayer = true; + layerInputAttr = src.Value; + } + else if (src.Value != operation.Dest.Value) + { + return false; + } + } + else if (src.Type == OperandType.Constant) + { + int dstComponent = (operation.Dest.Value >> 2) & 3; + float expectedValue = dstComponent == 3 ? 1f : 0f; + + if (src.AsFloat() != expectedValue) + { + return false; + } + } + else + { + return false; + } + } + else if (operation.Inst == Instruction.EmitVertex) + { + verticesCount++; + } + else if (operation.Inst == Instruction.EndPrimitive) + { + totalVerticesCount += verticesCount; + verticesCount = 0; + } + } + } + + return totalVerticesCount + verticesCount == 3 && writesLayer; + } + + private static bool IsResourceWrite(Instruction inst) + { + switch (inst) + { + case Instruction.AtomicAdd: + case Instruction.AtomicAnd: + case Instruction.AtomicCompareAndSwap: + case Instruction.AtomicMaxS32: + case Instruction.AtomicMaxU32: + case Instruction.AtomicMinS32: + case Instruction.AtomicMinU32: + case Instruction.AtomicOr: + case Instruction.AtomicSwap: + case Instruction.AtomicXor: + case Instruction.ImageAtomic: + case Instruction.ImageStore: + case Instruction.StoreGlobal: + case Instruction.StoreGlobal16: + case Instruction.StoreGlobal8: + case Instruction.StoreStorage: + case Instruction.StoreStorage16: + case Instruction.StoreStorage8: + return true; + } + + return false; + } + } +} diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 3fb586cbb..6a1230458 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -77,9 +77,11 @@ namespace Ryujinx.Graphics.Shader.Translation funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); } + var identification = ShaderIdentifier.Identify(funcs, config); + var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); - var info = config.CreateProgramInfo(); + var info = config.CreateProgramInfo(identification); return config.Options.TargetLanguage switch { diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 127f84a67..3b88fdbab 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -138,6 +138,16 @@ namespace Ryujinx.Graphics.Shader.Translation _config.MergeFromtNextStage(nextStage._config); } + public void SetGeometryShaderLayerInputAttribute(int attr) + { + _config.SetGeometryShaderLayerInputAttribute(attr); + } + + public void SetLastInVertexPipeline(bool hasFragment) + { + _config.SetLastInVertexPipeline(hasFragment); + } + public ShaderProgram Translate(TranslatorContext other = null) { FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _); diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 4c7c731be..6b6352571 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -546,6 +546,7 @@ namespace Ryujinx.Graphics.Vulkan supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced, supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderOrderingIntel: false, + supportsGeometryShader: Capabilities.SupportsGeometryShader, supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat, supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer, From f7c2e867f4e0c9067c0c88f58b5df4cef6ee4399 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 25 Feb 2023 11:55:57 +0100 Subject: [PATCH 375/737] chore: Update OpenTK to 4.7.7 (#4478) --- Directory.Packages.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 528dc4b4a..ae05ff54c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -26,10 +26,10 @@ <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> <PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" /> - <PackageVersion Include="OpenTK.Core" Version="4.7.5" /> - <PackageVersion Include="OpenTK.Graphics" Version="4.7.5" /> - <PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" /> - <PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" /> + <PackageVersion Include="OpenTK.Core" Version="4.7.7" /> + <PackageVersion Include="OpenTK.Graphics" Version="4.7.7" /> + <PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" /> + <PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" /> <PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> From f663a5cd38e0ac0191f5859ed5bc25f5a7a9a907 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 25 Feb 2023 12:30:48 +0100 Subject: [PATCH 376/737] macos: Add updater support (#4464) This is a very basic updater but should be enough for now. --------- Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- Ryujinx.Ava/Modules/Updater/Updater.cs | 257 ++++++++++++--------- distribution/macos/create_app_bundle.sh | 1 + distribution/macos/create_macos_release.sh | 2 +- distribution/macos/updater.sh | 39 ++++ 4 files changed, 191 insertions(+), 108 deletions(-) create mode 100755 distribution/macos/updater.sh diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index 511e273e5..e89abd1da 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -21,6 +21,7 @@ using System.Net.Http; using System.Net.NetworkInformation; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -57,7 +58,7 @@ namespace Ryujinx.Modules // Detect current platform if (OperatingSystem.IsMacOS()) { - _platformExt = "osx_x64.zip"; + _platformExt = "macos_universal.app.tar.gz"; } else if (OperatingSystem.IsWindows()) { @@ -286,22 +287,40 @@ namespace Ryujinx.Modules if (_updateSuccessful) { - var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], - LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], - LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]); + bool shouldRestart = true; + + if (!OperatingSystem.IsMacOS()) + { + shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]); + } if (shouldRestart) { + List<string> arguments = CommandLineState.Arguments.ToList(); string ryuName = Path.GetFileName(Environment.ProcessPath); - string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); + string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; + string executablePath = Path.Combine(executableDirectory, ryuName); - if (!Path.Exists(ryuExe)) + if (!Path.Exists(executablePath)) { - ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); + executablePath = Path.Combine(executableDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); } - Process.Start(ryuExe, CommandLineState.Arguments); + // On macOS we perform the update at relaunch. + if (OperatingSystem.IsMacOS()) + { + string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); + string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app"); + string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); + string currentPid = Process.GetCurrentProcess().Id.ToString(); + executablePath = "/bin/bash"; + arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid }); + } + + Process.Start(executablePath, arguments); Environment.Exit(0); } } @@ -381,6 +400,15 @@ namespace Ryujinx.Modules File.WriteAllBytes(updateFile, mergedFileBytes); + // On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution. + if (OperatingSystem.IsMacOS()) + { + using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile })) + { + xattrProcess.WaitForExit(); + } + } + try { InstallUpdate(taskDialog, updateFile); @@ -470,87 +498,98 @@ namespace Ryujinx.Modules worker.Start(); } + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath) + { + using Stream inStream = File.OpenRead(archivePath); + using GZipInputStream gzipStream = new(inStream); + using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); + + TarEntry tarEntry; + + while ((tarEntry = tarStream.GetNextEntry()) is not null) + { + if (tarEntry.IsDirectory) + { + continue; + } + + string outPath = Path.Combine(outputDirectoryPath, tarEntry.Name); + + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + + using (FileStream outStream = File.OpenWrite(outPath)) + { + tarStream.CopyEntryContents(outStream); + } + + File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); + + Dispatcher.UIThread.Post(() => + { + if (tarEntry is null) + { + return; + } + + taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal); + }); + } + } + + private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath) + { + using Stream inStream = File.OpenRead(archivePath); + using ZipFile zipFile = new(inStream); + + double count = 0; + foreach (ZipEntry zipEntry in zipFile) + { + count++; + if (zipEntry.IsDirectory) continue; + + string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name); + + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + + using (Stream zipStream = zipFile.GetInputStream(zipEntry)) + using (FileStream outStream = File.OpenWrite(outPath)) + { + zipStream.CopyTo(outStream); + } + + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); + + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); + }); + } + } + private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) { // Extract Update taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); - if (OperatingSystem.IsLinux()) + await Task.Run(() => { - using Stream inStream = File.OpenRead(updateFile); - using GZipInputStream gzipStream = new(inStream); - using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); - - await Task.Run(() => + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - TarEntry tarEntry; - - if (!OperatingSystem.IsWindows()) - { - while ((tarEntry = tarStream.GetNextEntry()) is not null) - { - if (tarEntry.IsDirectory) continue; - - string outPath = Path.Combine(UpdateDir, tarEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using (FileStream outStream = File.OpenWrite(outPath)) - { - tarStream.CopyEntryContents(outStream); - } - - File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); - - Dispatcher.UIThread.Post(() => - { - if (tarEntry is null) - { - return; - } - - taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal); - }); - } - } - }); - - taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal); - } - else - { - using Stream inStream = File.OpenRead(updateFile); - using ZipFile zipFile = new(inStream); - - await Task.Run(() => + ExtractTarGzipFile(taskDialog, updateFile, UpdateDir); + } + else if (OperatingSystem.IsWindows()) { - double count = 0; - foreach (ZipEntry zipEntry in zipFile) - { - count++; - if (zipEntry.IsDirectory) continue; - - string outPath = Path.Combine(UpdateDir, zipEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using (Stream zipStream = zipFile.GetInputStream(zipEntry)) - using (FileStream outStream = File.OpenWrite(outPath)) - { - zipStream.CopyTo(outStream); - } - - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); - - Dispatcher.UIThread.Post(() => - { - taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); - }); - } - }); - } + ExtractZipFile(taskDialog, updateFile, UpdateDir); + } + else + { + throw new NotSupportedException(); + } + }); // Delete downloaded zip File.Delete(updateFile); @@ -560,38 +599,42 @@ namespace Ryujinx.Modules taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); - // Replace old files - await Task.Run(() => + // NOTE: On macOS, replacement is delayed to the restart phase. + if (!OperatingSystem.IsMacOS()) { - double count = 0; - foreach (string file in allFiles) + // Replace old files + await Task.Run(() => { - count++; - try + double count = 0; + foreach (string file in allFiles) { - File.Move(file, file + ".ryuold"); - - Dispatcher.UIThread.Post(() => + count++; + try { - taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal); - }); - } - catch - { - Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file)); - } - } + File.Move(file, file + ".ryuold"); - Dispatcher.UIThread.Post(() => - { - taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles]; - taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal); + }); + } + catch + { + Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file)); + } + } + + Dispatcher.UIThread.Post(() => + { + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles]; + taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); + }); + + MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog); }); - MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog); - }); - - Directory.Delete(UpdateDir, true); + Directory.Delete(UpdateDir, true); + } _updateSuccessful = true; @@ -601,7 +644,7 @@ namespace Ryujinx.Modules public static bool CanUpdate(bool showWarnings) { #if !DISABLE_UPDATER - if (RuntimeInformation.OSArchitecture != Architecture.X64) + if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS()) { if (showWarnings) { @@ -674,7 +717,7 @@ namespace Ryujinx.Modules #endif } - // NOTE: This method should always reflect the latest build layout.s + // NOTE: This method should always reflect the latest build layout. private static IEnumerable<string> EnumerateFilesToDelete() { var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh index 8076303cb..b62f3491e 100755 --- a/distribution/macos/create_app_bundle.sh +++ b/distribution/macos/create_app_bundle.sh @@ -24,6 +24,7 @@ cp $PUBLISH_DIRECTORY/*.dylib $APP_BUNDLE_DIRECTORY/Contents/Frameworks # Then resources cp Info.plist $APP_BUNDLE_DIRECTORY/Contents cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns +cp updater.sh $APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo diff --git a/distribution/macos/create_macos_release.sh b/distribution/macos/create_macos_release.sh index 545baf20e..d979ec8f0 100755 --- a/distribution/macos/create_macos_release.sh +++ b/distribution/macos/create_macos_release.sh @@ -27,7 +27,7 @@ EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx rm -rf $TEMP_DIRECTORY mkdir -p $TEMP_DIRECTORY -DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID -p:ExtraDefineConstants=DISABLE_UPDATER --self-contained true" +DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true" dotnet restore dotnet build -c Release Ryujinx.Ava diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh new file mode 100755 index 000000000..b60ac34df --- /dev/null +++ b/distribution/macos/updater.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +INSTALL_DIRECTORY=$1 +NEW_APP_DIRECTORY=$2 +APP_PID=$3 +APP_ARGUMENTS="${@:4}" + +error_handler() { + local lineno="$1" + + script=""" + set alertTitle to \"Ryujinx - Updater error\" + set alertMessage to \"An error occurred during Ryujinx update (updater.sh:$lineno)\n\nPlease download the update manually from our website if the problem persists.\" + display dialog alertMessage with icon caution with title alertTitle buttons {\"Open Download Page\", \"Exit\"} + set the button_pressed to the button returned of the result + + if the button_pressed is \"Open Download Page\" then + open location \"https://ryujinx.org/download\" + end if + """ + + osascript -e "$script" + exit 1 +} + +trap 'error_handler ${LINENO}' ERR + +# Wait for Ryujinx to exit +# NOTE: in case no fds are open, lsof could be returning with a process still living. +# We wait 1s and assume the process stopped after that +lsof -p $APP_PID +r 1 &>/dev/null +sleep 1 + +# Now replace and reopen. +rm -rf "$INSTALL_DIRECTORY" +mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY" +open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS" From e691622f0a118d550a7891896e40b0d9ab39fb60 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 25 Feb 2023 13:29:30 +0100 Subject: [PATCH 377/737] misc: Add missing DefineConstants definition in Ryujinx.Common Fix flathub and nixpkgs build hopefully now. --- Ryujinx.Common/Ryujinx.Common.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj index ca5de76a6..c307f524e 100644 --- a/Ryujinx.Common/Ryujinx.Common.csproj +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -3,6 +3,7 @@ <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants> </PropertyGroup> <ItemGroup> From 9b1cc2cec6135602efc5dc5afa45ed3db261eb42 Mon Sep 17 00:00:00 2001 From: merry <git@mary.rs> Date: Sat, 25 Feb 2023 15:07:23 +0000 Subject: [PATCH 378/737] Logging: Redirect StdErr into logging system (#4427) * Logging: Redirect StdErr into logging system * Remove Mono.Unix * Apply suggestions from code review Co-authored-by: riperiperi <rhy3756547@hotmail.com> * Address comments --------- Co-authored-by: Mary <thog@protonmail.com> Co-authored-by: riperiperi <rhy3756547@hotmail.com> Co-authored-by: Mary <mary@mary.zone> --- Directory.Packages.props | 2 +- Ryujinx.Common/Logging/Logger.cs | 15 +- Ryujinx.Common/SystemInterop/StdErrAdapter.cs | 93 +++++++++++ Ryujinx.Common/SystemInterop/UnixStream.cs | 155 ++++++++++++++++++ 4 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 Ryujinx.Common/SystemInterop/StdErrAdapter.cs create mode 100644 Ryujinx.Common/SystemInterop/UnixStream.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index ae05ff54c..35c98e5a3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -52,4 +52,4 @@ <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-a913199" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> </ItemGroup> -</Project> \ No newline at end of file +</Project> diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs index c1abdba9b..4d48dd48d 100644 --- a/Ryujinx.Common/Logging/Logger.cs +++ b/Ryujinx.Common/Logging/Logger.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.SystemInterop; using System; using System.Collections.Generic; using System.Diagnostics; @@ -14,6 +15,8 @@ namespace Ryujinx.Common.Logging private static readonly List<ILogTarget> m_LogTargets; + private static readonly StdErrAdapter _stdErrAdapter; + public static event EventHandler<LogEventArgs> Updated; public readonly struct Log @@ -77,7 +80,13 @@ namespace Ryujinx.Common.Logging { Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message), data)); } - } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PrintRawMsg(string message) + { + Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, message)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string FormatMessage(LogClass Class, string Caller, string Message) => $"{Class} {Caller}: {Message}"; @@ -119,6 +128,8 @@ namespace Ryujinx.Common.Logging Warning = new Log(LogLevel.Warning); Info = new Log(LogLevel.Info); Trace = new Log(LogLevel.Trace); + + _stdErrAdapter = new StdErrAdapter(); } public static void RestartTime() @@ -164,6 +175,8 @@ namespace Ryujinx.Common.Logging { Updated = null; + _stdErrAdapter.Dispose(); + foreach (var target in m_LogTargets) { target.Dispose(); diff --git a/Ryujinx.Common/SystemInterop/StdErrAdapter.cs b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs new file mode 100644 index 000000000..12e240ad3 --- /dev/null +++ b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Runtime.Versioning; +using System.Threading; +using Ryujinx.Common.Logging; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.SystemInterop +{ + public partial class StdErrAdapter : IDisposable + { + private bool _disposable = false; + private UnixStream _pipeReader; + private UnixStream _pipeWriter; + private Thread _worker; + + public StdErrAdapter() + { + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + RegisterPosix(); + } + } + + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private void RegisterPosix() + { + const int stdErrFileno = 2; + + (int readFd, int writeFd) = MakePipe(); + dup2(writeFd, stdErrFileno); + + _pipeReader = new UnixStream(readFd); + _pipeWriter = new UnixStream(writeFd); + + _worker = new Thread(EventWorker); + _disposable = true; + _worker.Start(); + } + + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private void EventWorker() + { + TextReader reader = new StreamReader(_pipeReader); + string line; + while ((line = reader.ReadLine()) != null) + { + Logger.Error?.PrintRawMsg(line); + } + } + + private void Dispose(bool disposing) + { + if (_disposable) + { + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + _pipeReader?.Close(); + _pipeWriter?.Close(); + } + + _disposable = false; + } + } + + public void Dispose() + { + Dispose(true); + } + + [LibraryImport("libc", SetLastError = true)] + private static partial int dup2(int fd, int fd2); + + [LibraryImport("libc", SetLastError = true)] + private static unsafe partial int pipe(int* pipefd); + + private static unsafe (int, int) MakePipe() + { + int *pipefd = stackalloc int[2]; + + if (pipe(pipefd) == 0) + { + return (pipefd[0], pipefd[1]); + } + else + { + throw new(); + } + } + } +} diff --git a/Ryujinx.Common/SystemInterop/UnixStream.cs b/Ryujinx.Common/SystemInterop/UnixStream.cs new file mode 100644 index 000000000..1d6449974 --- /dev/null +++ b/Ryujinx.Common/SystemInterop/UnixStream.cs @@ -0,0 +1,155 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Common.SystemInterop +{ + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + public partial class UnixStream : Stream, IDisposable + { + private const int InvalidFd = -1; + + private int _fd; + + [LibraryImport("libc", SetLastError = true)] + private static partial long read(int fd, IntPtr buf, ulong count); + + [LibraryImport("libc", SetLastError = true)] + private static partial long write(int fd, IntPtr buf, ulong count); + + [LibraryImport("libc", SetLastError = true)] + private static partial int close(int fd); + + public UnixStream(int fd) + { + if (InvalidFd == fd) + { + throw new ArgumentException("Invalid file descriptor"); + } + + _fd = fd; + + CanRead = read(fd, IntPtr.Zero, 0) != -1; + CanWrite = write(fd, IntPtr.Zero, 0) != -1; + } + + ~UnixStream() + { + Close(); + } + + public override bool CanRead { get; } + public override bool CanWrite { get; } + public override bool CanSeek => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override void Flush() + { + } + + public override unsafe int Read([In, Out] byte[] buffer, int offset, int count) + { + if (offset < 0 || offset > (buffer.Length - count) || count < 0) + { + throw new ArgumentOutOfRangeException(); + } + + if (buffer.Length == 0) + { + return 0; + } + + long r = 0; + fixed (byte* buf = &buffer[offset]) + { + do + { + r = read(_fd, (IntPtr)buf, (ulong)count); + } while (ShouldRetry(r)); + } + + return (int)r; + } + + public override unsafe void Write(byte[] buffer, int offset, int count) + { + if (offset < 0 || offset > (buffer.Length - count) || count < 0) + { + throw new ArgumentOutOfRangeException(); + } + + if (buffer.Length == 0) + { + return; + } + + fixed (byte* buf = &buffer[offset]) + { + long r = 0; + do { + r = write(_fd, (IntPtr)buf, (ulong)count); + } while (ShouldRetry(r)); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Close() + { + if (_fd == InvalidFd) + { + return; + } + + Flush(); + + int r; + do { + r = close(_fd); + } while (ShouldRetry(r)); + + _fd = InvalidFd; + } + + void IDisposable.Dispose() + { + Close(); + } + + private bool ShouldRetry(long r) + { + if (r == -1) + { + const int eintr = 4; + + int errno = Marshal.GetLastPInvokeError(); + + if (errno == eintr) + { + return true; + } + + throw new SystemException($"Operation failed with error 0x{errno:X}"); + } + + return false; + } + } +} From 5d85468302dd21a93ac141abfb7b8749b938dc9a Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 26 Feb 2023 19:19:00 -0300 Subject: [PATCH 379/737] Vulkan: Support list topology primitive restart (#4483) --- .../HardwareCapabilities.cs | 6 +++++ Ryujinx.Graphics.Vulkan/PipelineState.cs | 21 +++++++++++---- .../VulkanInitialization.cs | 27 +++++++++++++++++++ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 13 +++++++++ 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 4512d375f..a45c2409b 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -33,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsMultiView; public readonly bool SupportsNullDescriptors; public readonly bool SupportsPushDescriptors; + public readonly bool SupportsPrimitiveTopologyListRestart; + public readonly bool SupportsPrimitiveTopologyPatchListRestart; public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedbackQueries; public readonly bool SupportsPreciseOcclusionQueries; @@ -63,6 +65,8 @@ namespace Ryujinx.Graphics.Vulkan bool supportsMultiView, bool supportsNullDescriptors, bool supportsPushDescriptors, + bool supportsPrimitiveTopologyListRestart, + bool supportsPrimitiveTopologyPatchListRestart, bool supportsTransformFeedback, bool supportsTransformFeedbackQueries, bool supportsPreciseOcclusionQueries, @@ -92,6 +96,8 @@ namespace Ryujinx.Graphics.Vulkan SupportsMultiView = supportsMultiView; SupportsNullDescriptors = supportsNullDescriptors; SupportsPushDescriptors = supportsPushDescriptors; + SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart; + SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart; SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries; SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; diff --git a/Ryujinx.Graphics.Vulkan/PipelineState.cs b/Ryujinx.Graphics.Vulkan/PipelineState.cs index 0d5494766..dccc8ce68 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -417,11 +417,22 @@ namespace Ryujinx.Graphics.Vulkan bool primitiveRestartEnable = PrimitiveRestartEnable; - primitiveRestartEnable &= Topology == PrimitiveTopology.LineStrip || - Topology == PrimitiveTopology.TriangleStrip || - Topology == PrimitiveTopology.TriangleFan || - Topology == PrimitiveTopology.LineStripWithAdjacency || - Topology == PrimitiveTopology.TriangleStripWithAdjacency; + bool topologySupportsRestart; + + if (gd.Capabilities.SupportsPrimitiveTopologyListRestart) + { + topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart || Topology != PrimitiveTopology.PatchList; + } + else + { + topologySupportsRestart = Topology == PrimitiveTopology.LineStrip || + Topology == PrimitiveTopology.TriangleStrip || + Topology == PrimitiveTopology.TriangleFan || + Topology == PrimitiveTopology.LineStripWithAdjacency || + Topology == PrimitiveTopology.TriangleStripWithAdjacency; + } + + primitiveRestartEnable &= topologySupportsRestart; var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo() { diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 353b219ac..ba3b5ef65 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV. "VK_EXT_fragment_shader_interlock", "VK_EXT_index_type_uint8", + "VK_EXT_primitive_topology_list_restart", "VK_EXT_robustness2", "VK_EXT_shader_stencil_export", "VK_KHR_shader_float16_int8", @@ -429,6 +430,17 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesCustomBorderColor; } + PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() + { + SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, + PNext = features2.PNext + }; + + if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) + { + features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart; + } + PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() { SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, @@ -497,6 +509,21 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresTransformFeedback; } + PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart; + + if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) + { + featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() + { + SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, + PNext = pExtendedFeatures, + PrimitiveTopologyListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, + PrimitiveTopologyPatchListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart + }; + + pExtendedFeatures = &featuresPrimitiveTopologyListRestart; + } + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; if (supportedExtensions.Contains("VK_EXT_robustness2")) diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 6b6352571..8d4e54c4b 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -195,6 +195,11 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceFeatures2 }; + PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() + { + SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt + }; + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() { SType = StructureType.PhysicalDeviceRobustness2FeaturesExt @@ -215,8 +220,14 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr }; + if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) + { + features2.PNext = &featuresPrimitiveTopologyListRestart; + } + if (supportedExtensions.Contains("VK_EXT_robustness2")) { + featuresRobustness2.PNext = features2.PNext; features2.PNext = &featuresRobustness2; } @@ -288,6 +299,8 @@ namespace Ryujinx.Graphics.Vulkan features2.Features.MultiViewport, featuresRobustness2.NullDescriptor || IsMoltenVk, supportedExtensions.Contains(KhrPushDescriptor.ExtensionName), + featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, + featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, supportsTransformFeedback, propertiesTransformFeedback.TransformFeedbackQueries, features2.Features.OcclusionQueryPrecise, From 80b497213981512e9ba1a629bcd5e2c519d2e566 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen <emmausssss@gmail.com> Date: Mon, 27 Feb 2023 21:11:55 +0000 Subject: [PATCH 380/737] Add Support for Post Processing Effects (#3616) * Add Post Processing Effects * fix events and shader issues * fix gtk upscale slider value * fix bgra games * don't swap swizzle if already swapped * restore opengl texture state after effects run * addressed review * use single pipeline for smaa and fsr * call finish on all pipelines * addressed review * attempt fix file case * attempt fixing file case * fix filter level tick frequency * adjust filter slider margins * replace fxaa shaders with original shader * addressed review --- Ryujinx.Ava/AppHost.cs | 28 + Ryujinx.Ava/Assets/Locales/en_US.json | 10 + .../UI/ViewModels/SettingsViewModel.cs | 32 + .../Views/Settings/SettingsGraphicsView.axaml | 78 + Ryujinx.Common/Configuration/AntiAliasing.cs | 12 + Ryujinx.Common/Configuration/ScalingFilter.cs | 9 + Ryujinx.Graphics.GAL/AntiAliasing.cs | 12 + Ryujinx.Graphics.GAL/IWindow.cs | 4 + .../Multithreading/ThreadedWindow.cs | 6 + Ryujinx.Graphics.GAL/UpscaleType.cs | 9 + .../Effects/FsrScalingFilter.cs | 177 + .../Effects/FxaaPostProcessingEffect.cs | 81 + .../Effects/IPostProcessingEffect.cs | 11 + .../Effects/IScalingFilter.cs | 18 + .../Effects/ShaderHelper.cs | 40 + .../Effects/Shaders/ffx_a.h | 2656 +++++++++++ .../Effects/Shaders/ffx_fsr1.h | 1199 +++++ .../Effects/Shaders/fsr_scaling.glsl | 88 + .../Effects/Shaders/fsr_sharpening.glsl | 37 + .../Effects/Shaders/fxaa.glsl | 1174 +++++ .../Effects/Shaders/smaa.hlsl | 1361 ++++++ .../Effects/Shaders/smaa_blend.glsl | 26 + .../Effects/Shaders/smaa_edge.glsl | 24 + .../Effects/Shaders/smaa_neighbour.glsl | 26 + .../Effects/SmaaPostProcessingEffect.cs | 261 ++ .../Effects/Textures/SmaaAreaTexture.bin | Bin 0 -> 179200 bytes .../Effects/Textures/SmaaSearchTexture.bin | Bin 0 -> 1024 bytes .../Ryujinx.Graphics.OpenGL.csproj | 14 + Ryujinx.Graphics.OpenGL/Window.cs | 215 +- .../DescriptorSetUpdater.cs | 7 + .../Effects/FsrScalingFilter.cs | 208 + .../Effects/FxaaPostProcessingEffect.cs | 127 + .../Effects/IPostProcessingEffect.cs | 10 + .../Effects/IScalingFilter.cs | 20 + .../Effects/Shaders/FsrScaling.glsl | 3945 +++++++++++++++++ .../Effects/Shaders/FsrScaling.spv | Bin 0 -> 44672 bytes .../Effects/Shaders/FsrSharpening.glsl | 3904 ++++++++++++++++ .../Effects/Shaders/FsrSharpening.spv | Bin 0 -> 20472 bytes .../Effects/Shaders/Fxaa.glsl | 1177 +++++ .../Effects/Shaders/Fxaa.spv | Bin 0 -> 25012 bytes .../Effects/Shaders/SmaaBlend.glsl | 1404 ++++++ .../Effects/Shaders/SmaaBlend.spv | Bin 0 -> 33728 bytes .../Effects/Shaders/SmaaEdge.glsl | 1402 ++++++ .../Effects/Shaders/SmaaEdge.spv | Bin 0 -> 8464 bytes .../Effects/Shaders/SmaaNeighbour.glsl | 1403 ++++++ .../Effects/Shaders/SmaaNeighbour.spv | Bin 0 -> 8328 bytes .../Effects/SmaaConstants.cs | 15 + .../Effects/SmaaPostProcessingEffect.cs | 314 ++ .../Effects/Textures/SmaaAreaTexture.bin | Bin 0 -> 179200 bytes .../Effects/Textures/SmaaSearchTexture.bin | Bin 0 -> 1024 bytes Ryujinx.Graphics.Vulkan/NativeArray.cs | 7 +- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 27 + .../Ryujinx.Graphics.Vulkan.csproj | 11 + Ryujinx.Graphics.Vulkan/Window.cs | 167 +- Ryujinx.Graphics.Vulkan/WindowBase.cs | 3 + .../Configuration/ConfigurationFileFormat.cs | 17 +- .../Configuration/ConfigurationState.cs | 41 + Ryujinx/Ui/RendererWidgetBase.cs | 28 + Ryujinx/Ui/Windows/SettingsWindow.cs | 12 + Ryujinx/Ui/Windows/SettingsWindow.glade | 123 +- 60 files changed, 21954 insertions(+), 26 deletions(-) create mode 100644 Ryujinx.Common/Configuration/AntiAliasing.cs create mode 100644 Ryujinx.Common/Configuration/ScalingFilter.cs create mode 100644 Ryujinx.Graphics.GAL/AntiAliasing.cs create mode 100644 Ryujinx.Graphics.GAL/UpscaleType.cs create mode 100644 Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs create mode 100644 Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs create mode 100644 Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs create mode 100644 Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs create mode 100644 Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl create mode 100644 Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin create mode 100644 Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin create mode 100644 Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs create mode 100644 Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs create mode 100644 Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs create mode 100644 Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv create mode 100644 Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs create mode 100644 Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin create mode 100644 Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 242c84e7f..eb22b39e9 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -171,6 +171,11 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; _gpuCancellationTokenSource = new CancellationTokenSource(); } @@ -193,6 +198,17 @@ namespace Ryujinx.Ava } } } + private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e) + { + _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + } + + private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e) + { + _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + } private void ShowCursor() { @@ -345,6 +361,11 @@ namespace Ryujinx.Ava } } + private void UpdateAntiAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e) + { + _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); + } + private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e) { Device?.System.ChangeDockedModeState(e.NewValue); @@ -411,6 +432,9 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; _topLevel.PointerMoved -= TopLevel_PointerMoved; @@ -788,6 +812,10 @@ namespace Ryujinx.Ava Device.Gpu.Renderer.Initialize(_glLogLevel); + _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); + _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + Width = (int)_rendererHost.Bounds.Width; Height = (int)_rendererHost.Bounds.Height; diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index b7d1e02bf..db8d24241 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -626,6 +626,16 @@ "Recover": "Recover", "UserProfilesRecoverHeading" : "Saves were found for the following accounts", "UserProfilesRecoverEmptyList": "No profiles to recover", + "GraphicsAATooltip": "Applies anti-aliasing to the game render", + "GraphicsAALabel": "Anti-Aliasing:", + "GraphicsScalingFilterLabel": "Scaling Filter:", + "GraphicsScalingFilterTooltip": "Enables Framebuffer Scaling", + "GraphicsScalingFilterLevelLabel": "Level", + "GraphicsScalingFilterLevelTooltip": "Set Scaling Filter Level", + "SmaaLow": "SMAA Low", + "SmaaMedium": "SMAA Medium", + "SmaaHigh": "SMAA High", + "SmaaUltra": "SMAA Ultra", "UserEditorTitle" : "Edit User", "UserEditorTitleCreate" : "Create User" } diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 36b37b0f5..7045c9ed3 100644 --- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -45,6 +45,8 @@ namespace Ryujinx.Ava.UI.ViewModels private KeyboardHotkeys _keyboardHotkeys; private int _graphicsBackendIndex; private string _customThemePath; + private int _scalingFilter; + private int _scalingFilterLevel; public event Action CloseWindow; public event Action SaveSettingsEvent; @@ -153,6 +155,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsSDL2Enabled { get; set; } public bool EnableCustomTheme { get; set; } public bool IsCustomResolutionScaleActive => _resolutionScale == 4; + public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr; + public bool IsVulkanSelected => GraphicsBackendIndex == 0; public bool UseHypervisor { get; set; } @@ -179,6 +183,18 @@ namespace Ryujinx.Ava.UI.ViewModels public int AudioBackend { get; set; } public int MaxAnisotropy { get; set; } public int AspectRatio { get; set; } + public int AntiAliasingEffect { get; set; } + public string ScalingFilterLevelText => ScalingFilterLevel.ToString("0"); + public int ScalingFilterLevel + { + get => _scalingFilterLevel; + set + { + _scalingFilterLevel = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(ScalingFilterLevelText)); + } + } public int OpenglDebugLevel { get; set; } public int MemoryMode { get; set; } public int BaseStyleIndex { get; set; } @@ -192,6 +208,16 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(nameof(IsVulkanSelected)); } } + public int ScalingFilter + { + get => _scalingFilter; + set + { + _scalingFilter = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(IsScalingFilterActive)); + } + } public int PreferredGpuIndex { get; set; } @@ -365,6 +391,9 @@ namespace Ryujinx.Ava.UI.ViewModels AspectRatio = (int)config.Graphics.AspectRatio.Value; GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value; ShaderDumpPath = config.Graphics.ShadersDumpPath; + AntiAliasingEffect = (int)config.Graphics.AntiAliasing.Value; + ScalingFilter = (int)config.Graphics.ScalingFilter.Value; + ScalingFilterLevel = config.Graphics.ScalingFilterLevel.Value; // Audio AudioBackend = (int)config.System.AudioBackend.Value; @@ -447,6 +476,9 @@ namespace Ryujinx.Ava.UI.ViewModels config.Graphics.ResScaleCustom.Value = CustomResolutionScale; config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy); config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio; + config.Graphics.AntiAliasing.Value = (AntiAliasing)AntiAliasingEffect; + config.Graphics.ScalingFilter.Value = (ScalingFilter)ScalingFilter; + config.Graphics.ScalingFilterLevel.Value = ScalingFilterLevel; if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex) { diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml index fb30fb7f4..8e4122f38 100644 --- a/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml @@ -7,6 +7,7 @@ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + Design.Width="1000" mc:Ignorable="d" x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> @@ -111,6 +112,83 @@ Minimum="0.1" Value="{Binding CustomResolutionScale}" /> </StackPanel> + <StackPanel + HorizontalAlignment="Stretch" + Orientation="Vertical" + Spacing="10"> + <StackPanel Orientation="Horizontal"> + <TextBlock VerticalAlignment="Center" + ToolTip.Tip="{locale:Locale GraphicsAATooltip}" + Text="{locale:Locale GraphicsAALabel}" + Width="250" /> + <ComboBox Width="350" + HorizontalContentAlignment="Left" + ToolTip.Tip="{locale:Locale GraphicsAATooltip}" + SelectedIndex="{Binding AntiAliasingEffect}"> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="FXAA" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SmaaLow}" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SmaaMedium}" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SmaaHigh}" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SmaaUltra}" /> + </ComboBoxItem> + </ComboBox> + </StackPanel> + </StackPanel> + <StackPanel + HorizontalAlignment="Stretch" + Orientation="Vertical" + Spacing="10"> + <StackPanel Orientation="Horizontal"> + <TextBlock VerticalAlignment="Center" + ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}" + Text="{locale:Locale GraphicsScalingFilterLabel}" + Width="250" /> + <ComboBox Width="350" + HorizontalContentAlignment="Left" + ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}" + SelectedIndex="{Binding ScalingFilter}"> + <ComboBoxItem> + <TextBlock Text="Bilinear" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="Nearest" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="FSR" /> + </ComboBoxItem> + </ComboBox> + <Slider Value="{Binding ScalingFilterLevel}" + ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}" + MinWidth="150" + Margin="10,-3,0,0" + Height="32" + Padding="0,-5" + IsVisible="{Binding IsScalingFilterActive}" + TickFrequency="1" + IsSnapToTickEnabled="True" + LargeChange="10" + SmallChange="1" + VerticalAlignment="Center" + Minimum="0" + Maximum="100" /> + <TextBlock Margin="5,0" + Width="40" + IsVisible="{Binding IsScalingFilterActive}" + Text="{Binding ScalingFilterLevelText}"/> + </StackPanel> + </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock VerticalAlignment="Center" ToolTip.Tip="{locale:Locale AnisotropyTooltip}" diff --git a/Ryujinx.Common/Configuration/AntiAliasing.cs b/Ryujinx.Common/Configuration/AntiAliasing.cs new file mode 100644 index 000000000..6543598c7 --- /dev/null +++ b/Ryujinx.Common/Configuration/AntiAliasing.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Common.Configuration +{ + public enum AntiAliasing + { + None, + Fxaa, + SmaaLow, + SmaaMedium, + SmaaHigh, + SmaaUltra + } +} diff --git a/Ryujinx.Common/Configuration/ScalingFilter.cs b/Ryujinx.Common/Configuration/ScalingFilter.cs new file mode 100644 index 000000000..2095b89b1 --- /dev/null +++ b/Ryujinx.Common/Configuration/ScalingFilter.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Common.Configuration +{ + public enum ScalingFilter + { + Bilinear, + Nearest, + Fsr + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/AntiAliasing.cs b/Ryujinx.Graphics.GAL/AntiAliasing.cs new file mode 100644 index 000000000..d4e5754d8 --- /dev/null +++ b/Ryujinx.Graphics.GAL/AntiAliasing.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum AntiAliasing + { + None, + Fxaa, + SmaaLow, + SmaaMedium, + SmaaHigh, + SmaaUltra + } +} diff --git a/Ryujinx.Graphics.GAL/IWindow.cs b/Ryujinx.Graphics.GAL/IWindow.cs index a9bbbc5e0..1221d685a 100644 --- a/Ryujinx.Graphics.GAL/IWindow.cs +++ b/Ryujinx.Graphics.GAL/IWindow.cs @@ -9,5 +9,9 @@ namespace Ryujinx.Graphics.GAL void SetSize(int width, int height); void ChangeVSyncMode(bool vsyncEnabled); + + void SetAntiAliasing(AntiAliasing antialiasing); + void SetScalingFilter(ScalingFilter type); + void SetScalingFilterLevel(float level); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs index c4b62a25d..a647d37eb 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -32,5 +32,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading } public void ChangeVSyncMode(bool vsyncEnabled) { } + + public void SetAntiAliasing(AntiAliasing effect) { } + + public void SetScalingFilter(ScalingFilter type) { } + + public void SetScalingFilterLevel(float level) { } } } diff --git a/Ryujinx.Graphics.GAL/UpscaleType.cs b/Ryujinx.Graphics.GAL/UpscaleType.cs new file mode 100644 index 000000000..442b65f24 --- /dev/null +++ b/Ryujinx.Graphics.GAL/UpscaleType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum ScalingFilter + { + Bilinear, + Nearest, + Fsr + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs b/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs new file mode 100644 index 000000000..16678bb7b --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs @@ -0,0 +1,177 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Image; +using System; +using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper; + +namespace Ryujinx.Graphics.OpenGL.Effects +{ + internal class FsrScalingFilter : IScalingFilter + { + private readonly OpenGLRenderer _renderer; + private int _inputUniform; + private int _outputUniform; + private int _sharpeningUniform; + private int _srcX0Uniform; + private int _srcX1Uniform; + private int _srcY0Uniform; + private int _scalingShaderProgram; + private int _sharpeningShaderProgram; + private float _scale = 1; + private int _srcY1Uniform; + private int _dstX0Uniform; + private int _dstX1Uniform; + private int _dstY0Uniform; + private int _dstY1Uniform; + private int _scaleXUniform; + private int _scaleYUniform; + private TextureStorage _intermediaryTexture; + + public float Level + { + get => _scale; + set + { + _scale = MathF.Max(0.01f, value); + } + } + + public FsrScalingFilter(OpenGLRenderer renderer, IPostProcessingEffect filter) + { + Initialize(); + + _renderer = renderer; + } + + public void Dispose() + { + if (_scalingShaderProgram != 0) + { + GL.DeleteProgram(_scalingShaderProgram); + GL.DeleteProgram(_sharpeningShaderProgram); + } + + _intermediaryTexture?.Dispose(); + } + + private void Initialize() + { + var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl"); + var sharpeningShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl"); + var fsrA = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h"); + var fsr1 = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h"); + + scalingShader = scalingShader.Replace("#include \"ffx_a.h\"", fsrA); + scalingShader = scalingShader.Replace("#include \"ffx_fsr1.h\"", fsr1); + sharpeningShader = sharpeningShader.Replace("#include \"ffx_a.h\"", fsrA); + sharpeningShader = sharpeningShader.Replace("#include \"ffx_fsr1.h\"", fsr1); + + _scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader); + _sharpeningShaderProgram = CompileProgram(sharpeningShader, ShaderType.ComputeShader); + + _inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source"); + _outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput"); + _sharpeningUniform = GL.GetUniformLocation(_sharpeningShaderProgram, "sharpening"); + + _srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0"); + _srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1"); + _srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0"); + _srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1"); + _dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0"); + _dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1"); + _dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0"); + _dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1"); + _scaleXUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleX"); + _scaleYUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleY"); + } + + public void Run( + TextureView view, + TextureView destinationTexture, + int width, + int height, + Extents2D source, + Extents2D destination) + { + if (_intermediaryTexture == null || _intermediaryTexture.Info.Width != width || _intermediaryTexture.Info.Height != height) + { + _intermediaryTexture?.Dispose(); + var originalInfo = view.Info; + var info = new TextureCreateInfo(width, + height, + originalInfo.Depth, + originalInfo.Levels, + originalInfo.Samples, + originalInfo.BlockWidth, + originalInfo.BlockHeight, + originalInfo.BytesPerPixel, + originalInfo.Format, + originalInfo.DepthStencilMode, + originalInfo.Target, + originalInfo.SwizzleR, + originalInfo.SwizzleG, + originalInfo.SwizzleB, + originalInfo.SwizzleA); + + _intermediaryTexture = new TextureStorage(_renderer, info, view.ScaleFactor); + _intermediaryTexture.CreateDefaultView(); + } + + var textureView = _intermediaryTexture.CreateView(_intermediaryTexture.Info, 0, 0) as TextureView; + + int previousProgram = GL.GetInteger(GetPName.CurrentProgram); + int previousUnit = GL.GetInteger(GetPName.ActiveTexture); + GL.ActiveTexture(TextureUnit.Texture0); + int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D); + + GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); + + int threadGroupWorkRegionDim = 16; + int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + // Scaling pass + float srcWidth = Math.Abs(source.X2 - source.X1); + float srcHeight = Math.Abs(source.Y2 - source.Y1); + float scaleX = srcWidth / view.Width; + float scaleY = srcHeight / view.Height; + GL.UseProgram(_scalingShaderProgram); + view.Bind(0); + GL.Uniform1(_inputUniform, 0); + GL.Uniform1(_outputUniform, 0); + GL.Uniform1(_srcX0Uniform, (float)source.X1); + GL.Uniform1(_srcX1Uniform, (float)source.X2); + GL.Uniform1(_srcY0Uniform, (float)source.Y1); + GL.Uniform1(_srcY1Uniform, (float)source.Y2); + GL.Uniform1(_dstX0Uniform, (float)destination.X1); + GL.Uniform1(_dstX1Uniform, (float)destination.X2); + GL.Uniform1(_dstY0Uniform, (float)destination.Y1); + GL.Uniform1(_dstY1Uniform, (float)destination.Y2); + GL.Uniform1(_scaleXUniform, scaleX); + GL.Uniform1(_scaleYUniform, scaleY); + GL.DispatchCompute(dispatchX, dispatchY, 1); + + GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); + + // Sharpening Pass + GL.UseProgram(_sharpeningShaderProgram); + GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); + textureView.Bind(0); + GL.Uniform1(_inputUniform, 0); + GL.Uniform1(_outputUniform, 0); + GL.Uniform1(_sharpeningUniform, 1.5f - (Level * 0.01f * 1.5f)); + GL.DispatchCompute(dispatchX, dispatchY, 1); + + GL.UseProgram(previousProgram); + GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); + + (_renderer.Pipeline as Pipeline).RestoreImages1And2(); + + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding); + + GL.ActiveTexture((TextureUnit)previousUnit); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs b/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs new file mode 100644 index 000000000..3a2d685b7 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs @@ -0,0 +1,81 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common; +using Ryujinx.Graphics.OpenGL.Image; + +namespace Ryujinx.Graphics.OpenGL.Effects +{ + internal class FxaaPostProcessingEffect : IPostProcessingEffect + { + private readonly OpenGLRenderer _renderer; + private int _resolutionUniform; + private int _inputUniform; + private int _outputUniform; + private int _shaderProgram; + private TextureStorage _textureStorage; + + public FxaaPostProcessingEffect(OpenGLRenderer renderer) + { + Initialize(); + + _renderer = renderer; + } + + public void Dispose() + { + if (_shaderProgram != 0) + { + GL.DeleteProgram(_shaderProgram); + _textureStorage?.Dispose(); + } + } + + private void Initialize() + { + _shaderProgram = ShaderHelper.CompileProgram(EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl"), ShaderType.ComputeShader); + + _resolutionUniform = GL.GetUniformLocation(_shaderProgram, "invResolution"); + _inputUniform = GL.GetUniformLocation(_shaderProgram, "inputTexture"); + _outputUniform = GL.GetUniformLocation(_shaderProgram, "imgOutput"); + } + + public TextureView Run(TextureView view, int width, int height) + { + if (_textureStorage == null || _textureStorage.Info.Width != view.Width || _textureStorage.Info.Height != view.Height) + { + _textureStorage?.Dispose(); + _textureStorage = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _textureStorage.CreateDefaultView(); + } + + var textureView = _textureStorage.CreateView(view.Info, 0, 0) as TextureView; + + int previousProgram = GL.GetInteger(GetPName.CurrentProgram); + int previousUnit = GL.GetInteger(GetPName.ActiveTexture); + GL.ActiveTexture(TextureUnit.Texture0); + int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D); + + GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); + GL.UseProgram(_shaderProgram); + + var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); + var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); + + view.Bind(0); + GL.Uniform1(_inputUniform, 0); + GL.Uniform1(_outputUniform, 0); + GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height); + GL.DispatchCompute(dispatchX, dispatchY, 1); + GL.UseProgram(previousProgram); + GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); + + (_renderer.Pipeline as Pipeline).RestoreImages1And2(); + + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding); + + GL.ActiveTexture((TextureUnit)previousUnit); + + return textureView; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs b/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs new file mode 100644 index 000000000..7a045a021 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.OpenGL.Image; +using System; + +namespace Ryujinx.Graphics.OpenGL.Effects +{ + internal interface IPostProcessingEffect : IDisposable + { + const int LocalGroupSize = 64; + TextureView Run(TextureView view, int width, int height); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs b/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs new file mode 100644 index 000000000..e1e1b2c1d --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs @@ -0,0 +1,18 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Image; +using System; + +namespace Ryujinx.Graphics.OpenGL.Effects +{ + internal interface IScalingFilter : IDisposable + { + float Level { get; set; } + void Run( + TextureView view, + TextureView destinationTexture, + int width, + int height, + Extents2D source, + Extents2D destination); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs b/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs new file mode 100644 index 000000000..72c5a98f5 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs @@ -0,0 +1,40 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Effects +{ + internal static class ShaderHelper + { + public static int CompileProgram(string shaderCode, ShaderType shaderType) + { + var shader = GL.CreateShader(shaderType); + GL.ShaderSource(shader, shaderCode); + GL.CompileShader(shader); + + var program = GL.CreateProgram(); + GL.AttachShader(program, shader); + GL.LinkProgram(program); + + GL.DetachShader(program, shader); + GL.DeleteShader(shader); + + return program; + } + + public static int CompileProgram(string[] shaders, ShaderType shaderType) + { + var shader = GL.CreateShader(shaderType); + GL.ShaderSource(shader, shaders.Length, shaders, (int[])null); + GL.CompileShader(shader); + + var program = GL.CreateProgram(); + GL.AttachShader(program, shader); + GL.LinkProgram(program); + + GL.DetachShader(program, shader); + GL.DeleteShader(shader); + + return program; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h b/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h new file mode 100644 index 000000000..d04bff55c --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h @@ -0,0 +1,2656 @@ +//============================================================================================================================== +// +// [A] SHADER PORTABILITY 1.20210629 +// +//============================================================================================================================== +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// MIT LICENSE +// =========== +// Copyright (c) 2014 Michal Drobot (for concepts used in "FLOAT APPROXIMATIONS"). +// ----------- +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// ----------- +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +// Software. +// ----------- +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// Common central point for high-level shading language and C portability for various shader headers. +//------------------------------------------------------------------------------------------------------------------------------ +// DEFINES +// ======= +// A_CPU ..... Include the CPU related code. +// A_GPU ..... Include the GPU related code. +// A_GLSL .... Using GLSL. +// A_HLSL .... Using HLSL. +// A_HLSL_6_2 Using HLSL 6.2 with new 'uint16_t' and related types (requires '-enable-16bit-types'). +// A_NO_16_BIT_CAST Don't use instructions that are not availabe in SPIR-V (needed for running A_HLSL_6_2 on Vulkan) +// A_GCC ..... Using a GCC compatible compiler (else assume MSVC compatible compiler by default). +// ======= +// A_BYTE .... Support 8-bit integer. +// A_HALF .... Support 16-bit integer and floating point. +// A_LONG .... Support 64-bit integer. +// A_DUBL .... Support 64-bit floating point. +// ======= +// A_WAVE .... Support wave-wide operations. +//------------------------------------------------------------------------------------------------------------------------------ +// To get #include "ffx_a.h" working in GLSL use '#extension GL_GOOGLE_include_directive:require'. +//------------------------------------------------------------------------------------------------------------------------------ +// SIMPLIFIED TYPE SYSTEM +// ====================== +// - All ints will be unsigned with exception of when signed is required. +// - Type naming simplified and shortened "A<type><#components>", +// - H = 16-bit float (half) +// - F = 32-bit float (float) +// - D = 64-bit float (double) +// - P = 1-bit integer (predicate, not using bool because 'B' is used for byte) +// - B = 8-bit integer (byte) +// - W = 16-bit integer (word) +// - U = 32-bit integer (unsigned) +// - L = 64-bit integer (long) +// - Using "AS<type><#components>" for signed when required. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure 'ALerp*(a,b,m)' does 'b*m+(-a*m+a)' (2 ops). +//------------------------------------------------------------------------------------------------------------------------------ +// CHANGE LOG +// ========== +// 20200914 - Expanded wave ops and prx code. +// 20200713 - Added [ZOL] section, fixed serious bugs in sRGB and Rec.709 color conversion code, etc. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COMMON +//============================================================================================================================== +#define A_2PI 6.28318530718 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// CPU +// +// +//============================================================================================================================== +#ifdef A_CPU + // Supporting user defined overrides. + #ifndef A_RESTRICT + #define A_RESTRICT __restrict + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifndef A_STATIC + #define A_STATIC static + #endif +//------------------------------------------------------------------------------------------------------------------------------ + // Same types across CPU and GPU. + // Predicate uses 32-bit integer (C friendly bool). + typedef uint32_t AP1; + typedef float AF1; + typedef double AD1; + typedef uint8_t AB1; + typedef uint16_t AW1; + typedef uint32_t AU1; + typedef uint64_t AL1; + typedef int8_t ASB1; + typedef int16_t ASW1; + typedef int32_t ASU1; + typedef int64_t ASL1; +//------------------------------------------------------------------------------------------------------------------------------ + #define AD1_(a) ((AD1)(a)) + #define AF1_(a) ((AF1)(a)) + #define AL1_(a) ((AL1)(a)) + #define AU1_(a) ((AU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1_(a) ((ASL1)(a)) + #define ASU1_(a) ((ASU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AU1 AU1_AF1(AF1 a){union{AF1 f;AU1 u;}bits;bits.f=a;return bits.u;} +//------------------------------------------------------------------------------------------------------------------------------ + #define A_TRUE 1 + #define A_FALSE 0 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// CPU/GPU PORTING +// +//------------------------------------------------------------------------------------------------------------------------------ +// Get CPU and GPU to share all setup code, without duplicate code paths. +// This uses a lower-case prefix for special vector constructs. +// - In C restrict pointers are used. +// - In the shading language, in/inout/out arguments are used. +// This depends on the ability to access a vector value in both languages via array syntax (aka color[2]). +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD1 *A_RESTRICT + #define retAD3 AD1 *A_RESTRICT + #define retAD4 AD1 *A_RESTRICT + #define retAF2 AF1 *A_RESTRICT + #define retAF3 AF1 *A_RESTRICT + #define retAF4 AF1 *A_RESTRICT + #define retAL2 AL1 *A_RESTRICT + #define retAL3 AL1 *A_RESTRICT + #define retAL4 AL1 *A_RESTRICT + #define retAU2 AU1 *A_RESTRICT + #define retAU3 AU1 *A_RESTRICT + #define retAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 AD1 *A_RESTRICT + #define inAD3 AD1 *A_RESTRICT + #define inAD4 AD1 *A_RESTRICT + #define inAF2 AF1 *A_RESTRICT + #define inAF3 AF1 *A_RESTRICT + #define inAF4 AF1 *A_RESTRICT + #define inAL2 AL1 *A_RESTRICT + #define inAL3 AL1 *A_RESTRICT + #define inAL4 AL1 *A_RESTRICT + #define inAU2 AU1 *A_RESTRICT + #define inAU3 AU1 *A_RESTRICT + #define inAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 AD1 *A_RESTRICT + #define inoutAD3 AD1 *A_RESTRICT + #define inoutAD4 AD1 *A_RESTRICT + #define inoutAF2 AF1 *A_RESTRICT + #define inoutAF3 AF1 *A_RESTRICT + #define inoutAF4 AF1 *A_RESTRICT + #define inoutAL2 AL1 *A_RESTRICT + #define inoutAL3 AL1 *A_RESTRICT + #define inoutAL4 AL1 *A_RESTRICT + #define inoutAU2 AU1 *A_RESTRICT + #define inoutAU3 AU1 *A_RESTRICT + #define inoutAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 AD1 *A_RESTRICT + #define outAD3 AD1 *A_RESTRICT + #define outAD4 AD1 *A_RESTRICT + #define outAF2 AF1 *A_RESTRICT + #define outAF3 AF1 *A_RESTRICT + #define outAF4 AF1 *A_RESTRICT + #define outAL2 AL1 *A_RESTRICT + #define outAL3 AL1 *A_RESTRICT + #define outAL4 AL1 *A_RESTRICT + #define outAU2 AU1 *A_RESTRICT + #define outAU3 AU1 *A_RESTRICT + #define outAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD1 x[2] + #define varAD3(x) AD1 x[3] + #define varAD4(x) AD1 x[4] + #define varAF2(x) AF1 x[2] + #define varAF3(x) AF1 x[3] + #define varAF4(x) AF1 x[4] + #define varAL2(x) AL1 x[2] + #define varAL3(x) AL1 x[3] + #define varAL4(x) AL1 x[4] + #define varAU2(x) AU1 x[2] + #define varAU3(x) AU1 x[3] + #define varAU4(x) AU1 x[4] +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) {x,y} + #define initAD3(x,y,z) {x,y,z} + #define initAD4(x,y,z,w) {x,y,z,w} + #define initAF2(x,y) {x,y} + #define initAF3(x,y,z) {x,y,z} + #define initAF4(x,y,z,w) {x,y,z,w} + #define initAL2(x,y) {x,y} + #define initAL3(x,y,z) {x,y,z} + #define initAL4(x,y,z,w) {x,y,z,w} + #define initAU2(x,y) {x,y} + #define initAU3(x,y,z) {x,y,z} + #define initAU4(x,y,z,w) {x,y,z,w} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Replace transcendentals with manual versions. +//============================================================================================================================== + #ifdef A_GCC + A_STATIC AD1 AAbsD1(AD1 a){return __builtin_fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return __builtin_fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(__builtin_abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(__builtin_llabs(ASL1_(a)));} + #else + A_STATIC AD1 AAbsD1(AD1 a){return fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(labs((long)ASL1_(a)));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ACosD1(AD1 a){return __builtin_cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return __builtin_cosf(a);} + #else + A_STATIC AD1 ACosD1(AD1 a){return cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return cosf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ADotD2(inAD2 a,inAD2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AD1 ADotD3(inAD3 a,inAD3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AD1 ADotD4(inAD4 a,inAD4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} + A_STATIC AF1 ADotF2(inAF2 a,inAF2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AF1 ADotF3(inAF3 a,inAF3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AF1 ADotF4(inAF4 a,inAF4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AExp2D1(AD1 a){return __builtin_exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return __builtin_exp2f(a);} + #else + A_STATIC AD1 AExp2D1(AD1 a){return exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return exp2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AFloorD1(AD1 a){return __builtin_floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return __builtin_floorf(a);} + #else + A_STATIC AD1 AFloorD1(AD1 a){return floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return floorf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ALerpD1(AD1 a,AD1 b,AD1 c){return b*c+(-a*c+a);} + A_STATIC AF1 ALerpF1(AF1 a,AF1 b,AF1 c){return b*c+(-a*c+a);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ALog2D1(AD1 a){return __builtin_log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return __builtin_log2f(a);} + #else + A_STATIC AD1 ALog2D1(AD1 a){return log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return log2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMaxD1(AD1 a,AD1 b){return a>b?a:b;} + A_STATIC AF1 AMaxF1(AF1 a,AF1 b){return a>b?a:b;} + A_STATIC AL1 AMaxL1(AL1 a,AL1 b){return a>b?a:b;} + A_STATIC AU1 AMaxU1(AU1 a,AU1 b){return a>b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + // These follow the convention that A integer types don't have signage, until they are operated on. + A_STATIC AL1 AMaxSL1(AL1 a,AL1 b){return (ASL1_(a)>ASL1_(b))?a:b;} + A_STATIC AU1 AMaxSU1(AU1 a,AU1 b){return (ASU1_(a)>ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMinD1(AD1 a,AD1 b){return a<b?a:b;} + A_STATIC AF1 AMinF1(AF1 a,AF1 b){return a<b?a:b;} + A_STATIC AL1 AMinL1(AL1 a,AL1 b){return a<b?a:b;} + A_STATIC AU1 AMinU1(AU1 a,AU1 b){return a<b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AL1 AMinSL1(AL1 a,AL1 b){return (ASL1_(a)<ASL1_(b))?a:b;} + A_STATIC AU1 AMinSU1(AU1 a,AU1 b){return (ASU1_(a)<ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARcpD1(AD1 a){return 1.0/a;} + A_STATIC AF1 ARcpF1(AF1 a){return 1.0f/a;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AL1 AShrSL1(AL1 a,AL1 b){return AL1_(ASL1_(a)>>ASL1_(b));} + A_STATIC AU1 AShrSU1(AU1 a,AU1 b){return AU1_(ASU1_(a)>>ASU1_(b));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASinD1(AD1 a){return __builtin_sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return __builtin_sinf(a);} + #else + A_STATIC AD1 ASinD1(AD1 a){return sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return sinf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASqrtD1(AD1 a){return __builtin_sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return __builtin_sqrtf(a);} + #else + A_STATIC AD1 ASqrtD1(AD1 a){return sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return sqrtf(a);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + A_STATIC AD1 AClampD1(AD1 x,AD1 n,AD1 m){return AMaxD1(n,AMinD1(x,m));} + A_STATIC AF1 AClampF1(AF1 x,AF1 n,AF1 m){return AMaxF1(n,AMinF1(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AFractD1(AD1 a){return a-AFloorD1(a);} + A_STATIC AF1 AFractF1(AF1 a){return a-AFloorF1(a);} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 APowD1(AD1 a,AD1 b){return AExp2D1(b*ALog2D1(a));} + A_STATIC AF1 APowF1(AF1 a,AF1 b){return AExp2F1(b*ALog2F1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARsqD1(AD1 a){return ARcpD1(ASqrtD1(a));} + A_STATIC AF1 ARsqF1(AF1 a){return ARcpF1(ASqrtF1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ASatD1(AD1 a){return AMinD1(1.0,AMaxD1(0.0,a));} + A_STATIC AF1 ASatF1(AF1 a){return AMinF1(1.0f,AMaxF1(0.0f,a));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + A_STATIC retAD2 opAAbsD2(outAD2 d,inAD2 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);return d;} + A_STATIC retAD3 opAAbsD3(outAD3 d,inAD3 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);return d;} + A_STATIC retAD4 opAAbsD4(outAD4 d,inAD4 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);d[3]=AAbsD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAbsF2(outAF2 d,inAF2 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);return d;} + A_STATIC retAF3 opAAbsF3(outAF3 d,inAF3 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);return d;} + A_STATIC retAF4 opAAbsF4(outAF4 d,inAF4 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);d[3]=AAbsF1(a[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opACpyD2(outAD2 d,inAD2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAD3 opACpyD3(outAD3 d,inAD3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAD4 opACpyD4(outAD4 d,inAD4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opACpyF2(outAF2 d,inAF2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAF3 opACpyF3(outAF3 d,inAF3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAF4 opACpyF4(outAF4 d,inAF4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);return d;} + A_STATIC retAD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);return d;} + A_STATIC retAD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);d[3]=ALerpD1(a[3],b[3],c[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);return d;} + A_STATIC retAF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);return d;} + A_STATIC retAF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);d[3]=ALerpF1(a[3],b[3],c[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);return d;} + A_STATIC retAD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);return d;} + A_STATIC retAD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);d[3]=ALerpD1(a[3],b[3],c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);return d;} + A_STATIC retAF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);return d;} + A_STATIC retAF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);d[3]=ALerpF1(a[3],b[3],c);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);d[3]=AMaxD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);d[3]=AMaxF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);d[3]=AMinD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);d[3]=AMinF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opANegD2(outAD2 d,inAD2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAD3 opANegD3(outAD3 d,inAD3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAD4 opANegD4(outAD4 d,inAD4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opANegF2(outAF2 d,inAF2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAF3 opANegF3(outAF3 d,inAF3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAF4 opANegF4(outAF4 d,inAF4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opARcpD2(outAD2 d,inAD2 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);return d;} + A_STATIC retAD3 opARcpD3(outAD3 d,inAD3 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);return d;} + A_STATIC retAD4 opARcpD4(outAD4 d,inAD4 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);d[3]=ARcpD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opARcpF2(outAF2 d,inAF2 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);return d;} + A_STATIC retAF3 opARcpF3(outAF3 d,inAF3 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);return d;} + A_STATIC retAF4 opARcpF4(outAF4 d,inAF4 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);d[3]=ARcpF1(a[3]);return d;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF FLOAT PACKING +//============================================================================================================================== + // Convert float to half (in lower 16-bits of output). + // Same fast technique as documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf + // Supports denormals. + // Conversion rules are to make computations possibly "safer" on the GPU, + // -INF & -NaN -> -65504 + // +INF & +NaN -> +65504 + A_STATIC AU1 AU1_AH1_AF1(AF1 f){ + static AW1 base[512]={ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100, + 0x0200,0x0400,0x0800,0x0c00,0x1000,0x1400,0x1800,0x1c00,0x2000,0x2400,0x2800,0x2c00,0x3000,0x3400,0x3800,0x3c00, + 0x4000,0x4400,0x4800,0x4c00,0x5000,0x5400,0x5800,0x5c00,0x6000,0x6400,0x6800,0x6c00,0x7000,0x7400,0x7800,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8001,0x8002,0x8004,0x8008,0x8010,0x8020,0x8040,0x8080,0x8100, + 0x8200,0x8400,0x8800,0x8c00,0x9000,0x9400,0x9800,0x9c00,0xa000,0xa400,0xa800,0xac00,0xb000,0xb400,0xb800,0xbc00, + 0xc000,0xc400,0xc800,0xcc00,0xd000,0xd400,0xd800,0xdc00,0xe000,0xe400,0xe800,0xec00,0xf000,0xf400,0xf800,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff}; + static AB1 shift[512]={ + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}; + union{AF1 f;AU1 u;}bits;bits.f=f;AU1 u=bits.u;AU1 i=u>>23;return (AU1)(base[i])+((u&0x7fffff)>>shift[i]);} +//------------------------------------------------------------------------------------------------------------------------------ + // Used to output packed constant. + A_STATIC AU1 AU1_AH2_AF2(inAF2 a){return AU1_AH1_AF1(a[0])+(AU1_AH1_AF1(a[1])<<16);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GLSL +// +// +//============================================================================================================================== +#if defined(A_GLSL) && defined(A_GPU) + #ifndef A_SKIP_EXT + #ifdef A_HALF + #extension GL_EXT_shader_16bit_storage:require + #extension GL_EXT_shader_explicit_arithmetic_types:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_LONG + #extension GL_ARB_gpu_shader_int64:require + #extension GL_NV_shader_atomic_int64:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_WAVE + #extension GL_KHR_shader_subgroup_arithmetic:require + #extension GL_KHR_shader_subgroup_ballot:require + #extension GL_KHR_shader_subgroup_quad:require + #extension GL_KHR_shader_subgroup_shuffle:require + #endif + #endif +//============================================================================================================================== + #define AP1 bool + #define AP2 bvec2 + #define AP3 bvec3 + #define AP4 bvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 vec2 + #define AF3 vec3 + #define AF4 vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uvec2 + #define AU3 uvec3 + #define AU4 uvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 ivec2 + #define ASU3 ivec3 + #define ASU4 ivec4 +//============================================================================================================================== + #define AF1_AU1(x) uintBitsToFloat(AU1(x)) + #define AF2_AU2(x) uintBitsToFloat(AU2(x)) + #define AF3_AU3(x) uintBitsToFloat(AU3(x)) + #define AF4_AU4(x) uintBitsToFloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) floatBitsToUint(AF1(x)) + #define AU2_AF2(x) floatBitsToUint(AF2(x)) + #define AU3_AF3(x) floatBitsToUint(AF3(x)) + #define AU4_AF4(x) floatBitsToUint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return packHalf2x16(AF2(a,0.0));} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2_AF2 packHalf2x16 + #define AU1_AW2Unorm_AF2 packUnorm2x16 + #define AU1_AB4Unorm_AF4 packUnorm4x8 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF2_AH2_AU1 unpackHalf2x16 + #define AF2_AW2Unorm_AU1 unpackUnorm2x16 + #define AF4_AB4Unorm_AU1 unpackUnorm4x8 +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){return bitfieldExtract(src,ASU1(off),ASU1(bits));} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + // Proxy for V_BFI_B32 where the 'mask' is set as 'bits', 'mask=(1<<bits)-1', and 'bits' needs to be an immediate. + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){return bitfieldInsert(src,ins,0,ASU1(bits));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MED3_F32. + AF1 AClampF1(AF1 x,AF1 n,AF1 m){return clamp(x,n,m);} + AF2 AClampF2(AF2 x,AF2 n,AF2 m){return clamp(x,n,m);} + AF3 AClampF3(AF3 x,AF3 n,AF3 m){return clamp(x,n,m);} + AF4 AClampF4(AF4 x,AF4 n,AF4 m){return clamp(x,n,m);} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F32 (note DX frac() is different). + AF1 AFractF1(AF1 x){return fract(x);} + AF2 AFractF2(AF2 x){return fract(x);} + AF3 AFractF3(AF3 x){return fract(x);} + AF4 AFractF4(AF4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ALerpF1(AF1 x,AF1 y,AF1 a){return mix(x,y,a);} + AF2 ALerpF2(AF2 x,AF2 y,AF2 a){return mix(x,y,a);} + AF3 ALerpF3(AF3 x,AF3 y,AF3 a){return mix(x,y,a);} + AF4 ALerpF4(AF4 x,AF4 y,AF4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MAX3_F32. + AF1 AMax3F1(AF1 x,AF1 y,AF1 z){return max(x,max(y,z));} + AF2 AMax3F2(AF2 x,AF2 y,AF2 z){return max(x,max(y,z));} + AF3 AMax3F3(AF3 x,AF3 y,AF3 z){return max(x,max(y,z));} + AF4 AMax3F4(AF4 x,AF4 y,AF4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3SU1(AU1 x,AU1 y,AU1 z){return AU1(max(ASU1(x),max(ASU1(y),ASU1(z))));} + AU2 AMax3SU2(AU2 x,AU2 y,AU2 z){return AU2(max(ASU2(x),max(ASU2(y),ASU2(z))));} + AU3 AMax3SU3(AU3 x,AU3 y,AU3 z){return AU3(max(ASU3(x),max(ASU3(y),ASU3(z))));} + AU4 AMax3SU4(AU4 x,AU4 y,AU4 z){return AU4(max(ASU4(x),max(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3U1(AU1 x,AU1 y,AU1 z){return max(x,max(y,z));} + AU2 AMax3U2(AU2 x,AU2 y,AU2 z){return max(x,max(y,z));} + AU3 AMax3U3(AU3 x,AU3 y,AU3 z){return max(x,max(y,z));} + AU4 AMax3U4(AU4 x,AU4 y,AU4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMaxSU1(AU1 a,AU1 b){return AU1(max(ASU1(a),ASU1(b)));} + AU2 AMaxSU2(AU2 a,AU2 b){return AU2(max(ASU2(a),ASU2(b)));} + AU3 AMaxSU3(AU3 a,AU3 b){return AU3(max(ASU3(a),ASU3(b)));} + AU4 AMaxSU4(AU4 a,AU4 b){return AU4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Clamp has an easier pattern match for med3 when some ordering is known. + // V_MED3_F32. + AF1 AMed3F1(AF1 x,AF1 y,AF1 z){return max(min(x,y),min(max(x,y),z));} + AF2 AMed3F2(AF2 x,AF2 y,AF2 z){return max(min(x,y),min(max(x,y),z));} + AF3 AMed3F3(AF3 x,AF3 y,AF3 z){return max(min(x,y),min(max(x,y),z));} + AF4 AMed3F4(AF4 x,AF4 y,AF4 z){return max(min(x,y),min(max(x,y),z));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MIN3_F32. + AF1 AMin3F1(AF1 x,AF1 y,AF1 z){return min(x,min(y,z));} + AF2 AMin3F2(AF2 x,AF2 y,AF2 z){return min(x,min(y,z));} + AF3 AMin3F3(AF3 x,AF3 y,AF3 z){return min(x,min(y,z));} + AF4 AMin3F4(AF4 x,AF4 y,AF4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3SU1(AU1 x,AU1 y,AU1 z){return AU1(min(ASU1(x),min(ASU1(y),ASU1(z))));} + AU2 AMin3SU2(AU2 x,AU2 y,AU2 z){return AU2(min(ASU2(x),min(ASU2(y),ASU2(z))));} + AU3 AMin3SU3(AU3 x,AU3 y,AU3 z){return AU3(min(ASU3(x),min(ASU3(y),ASU3(z))));} + AU4 AMin3SU4(AU4 x,AU4 y,AU4 z){return AU4(min(ASU4(x),min(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3U1(AU1 x,AU1 y,AU1 z){return min(x,min(y,z));} + AU2 AMin3U2(AU2 x,AU2 y,AU2 z){return min(x,min(y,z));} + AU3 AMin3U3(AU3 x,AU3 y,AU3 z){return min(x,min(y,z));} + AU4 AMin3U4(AU4 x,AU4 y,AU4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMinSU1(AU1 a,AU1 b){return AU1(min(ASU1(a),ASU1(b)));} + AU2 AMinSU2(AU2 a,AU2 b){return AU2(min(ASU2(a),ASU2(b)));} + AU3 AMinSU3(AU3 a,AU3 b){return AU3(min(ASU3(a),ASU3(b)));} + AU4 AMinSU4(AU4 a,AU4 b){return AU4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Normalized trig. Valid input domain is {-256 to +256}. No GLSL compiler intrinsic exists to map to this currently. + // V_COS_F32. + AF1 ANCosF1(AF1 x){return cos(x*AF1_(A_2PI));} + AF2 ANCosF2(AF2 x){return cos(x*AF2_(A_2PI));} + AF3 ANCosF3(AF3 x){return cos(x*AF3_(A_2PI));} + AF4 ANCosF4(AF4 x){return cos(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + // Normalized trig. Valid input domain is {-256 to +256}. No GLSL compiler intrinsic exists to map to this currently. + // V_SIN_F32. + AF1 ANSinF1(AF1 x){return sin(x*AF1_(A_2PI));} + AF2 ANSinF2(AF2 x){return sin(x*AF2_(A_2PI));} + AF3 ANSinF3(AF3 x){return sin(x*AF3_(A_2PI));} + AF4 ANSinF4(AF4 x){return sin(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARcpF1(AF1 x){return AF1_(1.0)/x;} + AF2 ARcpF2(AF2 x){return AF2_(1.0)/x;} + AF3 ARcpF3(AF3 x){return AF3_(1.0)/x;} + AF4 ARcpF4(AF4 x){return AF4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARsqF1(AF1 x){return AF1_(1.0)/sqrt(x);} + AF2 ARsqF2(AF2 x){return AF2_(1.0)/sqrt(x);} + AF3 ARsqF3(AF3 x){return AF3_(1.0)/sqrt(x);} + AF4 ARsqF4(AF4 x){return AF4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASatF1(AF1 x){return clamp(x,AF1_(0.0),AF1_(1.0));} + AF2 ASatF2(AF2 x){return clamp(x,AF2_(0.0),AF2_(1.0));} + AF3 ASatF3(AF3 x){return clamp(x,AF3_(0.0),AF3_(1.0));} + AF4 ASatF4(AF4 x){return clamp(x,AF4_(0.0),AF4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AShrSU1(AU1 a,AU1 b){return AU1(ASU1(a)>>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #define AB1 uint8_t + #define AB2 u8vec2 + #define AB3 u8vec3 + #define AB4 u8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASB1 int8_t + #define ASB2 i8vec2 + #define ASB3 i8vec3 + #define ASB4 i8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + AB1 AB1_x(AB1 a){return AB1(a);} + AB2 AB2_x(AB1 a){return AB2(a,a);} + AB3 AB3_x(AB1 a){return AB3(a,a,a);} + AB4 AB4_x(AB1 a){return AB4(a,a,a,a);} + #define AB1_(a) AB1_x(AB1(a)) + #define AB2_(a) AB2_x(AB1(a)) + #define AB3_(a) AB3_x(AB1(a)) + #define AB4_(a) AB4_x(AB1(a)) + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 float16_t + #define AH2 f16vec2 + #define AH3 f16vec3 + #define AH4 f16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 u16vec2 + #define AW3 u16vec3 + #define AW4 u16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 i16vec2 + #define ASW3 i16vec3 + #define ASW4 i16vec4 +//============================================================================================================================== + #define AH2_AU1(x) unpackFloat2x16(AU1(x)) + AH4 AH4_AU2_x(AU2 x){return AH4(unpackFloat2x16(x.x),unpackFloat2x16(x.y));} + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) unpackUint2x16(AU1(x)) + #define AW4_AU2(x) unpackUint4x16(pack64(AU2(x))) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2(x) packFloat2x16(AH2(x)) + AU2 AU2_AH4_x(AH4 x){return AU2(packFloat2x16(x.xy),packFloat2x16(x.zw));} + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) packUint2x16(AW2(x)) + #define AU2_AW4(x) unpack32(packUint4x16(AW4(x))) +//============================================================================================================================== + #define AW1_AH1(x) halfBitsToUint16(AH1(x)) + #define AW2_AH2(x) halfBitsToUint16(AH2(x)) + #define AW3_AH3(x) halfBitsToUint16(AH3(x)) + #define AW4_AH4(x) halfBitsToUint16(AH4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AH1_AW1(x) uint16BitsToHalf(AW1(x)) + #define AH2_AW2(x) uint16BitsToHalf(AW2(x)) + #define AH3_AW3(x) uint16BitsToHalf(AW3(x)) + #define AH4_AW4(x) uint16BitsToHalf(AW4(x)) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return clamp(x,n,m);} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return clamp(x,n,m);} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return clamp(x,n,m);} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return clamp(x,n,m);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFractH1(AH1 x){return fract(x);} + AH2 AFractH2(AH2 x){return fract(x);} + AH3 AFractH3(AH3 x){return fract(x);} + AH4 AFractH4(AH4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return mix(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return mix(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return mix(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of max3. + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of min3. + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return AH1_(1.0)/x;} + AH2 ARcpH2(AH2 x){return AH2_(1.0)/x;} + AH3 ARcpH3(AH3 x){return AH3_(1.0)/x;} + AH4 ARcpH4(AH4 x){return AH4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return AH1_(1.0)/sqrt(x);} + AH2 ARsqH2(AH2 x){return AH2_(1.0)/sqrt(x);} + AH3 ARsqH3(AH3 x){return AH3_(1.0)/sqrt(x);} + AH4 ARsqH4(AH4 x){return AH4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return clamp(x,AH1_(0.0),AH1_(1.0));} + AH2 ASatH2(AH2 x){return clamp(x,AH2_(0.0),AH2_(1.0));} + AH3 ASatH3(AH3 x){return clamp(x,AH3_(0.0),AH3_(1.0));} + AH4 ASatH4(AH4 x){return clamp(x,AH4_(0.0),AH4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 dvec2 + #define AD3 dvec3 + #define AD4 dvec4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 x){return fract(x);} + AD2 AFractD2(AD2 x){return fract(x);} + AD3 AFractD3(AD3 x){return fract(x);} + AD4 AFractD4(AD4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return mix(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return mix(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return mix(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return AD1_(1.0)/x;} + AD2 ARcpD2(AD2 x){return AD2_(1.0)/x;} + AD3 ARcpD3(AD3 x){return AD3_(1.0)/x;} + AD4 ARcpD4(AD4 x){return AD4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return AD1_(1.0)/sqrt(x);} + AD2 ARsqD2(AD2 x){return AD2_(1.0)/sqrt(x);} + AD3 ARsqD3(AD3 x){return AD3_(1.0)/sqrt(x);} + AD4 ARsqD4(AD4 x){return AD4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return clamp(x,AD1_(0.0),AD1_(1.0));} + AD2 ASatD2(AD2 x){return clamp(x,AD2_(0.0),AD2_(1.0));} + AD3 ASatD3(AD3 x){return clamp(x,AD3_(0.0),AD3_(1.0));} + AD4 ASatD4(AD4 x){return clamp(x,AD4_(0.0),AD4_(1.0));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #define AL1 uint64_t + #define AL2 u64vec2 + #define AL3 u64vec3 + #define AL4 u64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1 int64_t + #define ASL2 i64vec2 + #define ASL3 i64vec3 + #define ASL4 i64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AL1_AU2(x) packUint2x32(AU2(x)) + #define AU2_AL1(x) unpackUint2x32(AL1(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AL1_x(AL1 a){return AL1(a);} + AL2 AL2_x(AL1 a){return AL2(a,a);} + AL3 AL3_x(AL1 a){return AL3(a,a,a);} + AL4 AL4_x(AL1 a){return AL4(a,a,a,a);} + #define AL1_(a) AL1_x(AL1(a)) + #define AL2_(a) AL2_x(AL1(a)) + #define AL3_(a) AL3_x(AL1(a)) + #define AL4_(a) AL4_x(AL1(a)) +//============================================================================================================================== + AL1 AAbsSL1(AL1 a){return AL1(abs(ASL1(a)));} + AL2 AAbsSL2(AL2 a){return AL2(abs(ASL2(a)));} + AL3 AAbsSL3(AL3 a){return AL3(abs(ASL3(a)));} + AL4 AAbsSL4(AL4 a){return AL4(abs(ASL4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMaxSL1(AL1 a,AL1 b){return AL1(max(ASU1(a),ASU1(b)));} + AL2 AMaxSL2(AL2 a,AL2 b){return AL2(max(ASU2(a),ASU2(b)));} + AL3 AMaxSL3(AL3 a,AL3 b){return AL3(max(ASU3(a),ASU3(b)));} + AL4 AMaxSL4(AL4 a,AL4 b){return AL4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMinSL1(AL1 a,AL1 b){return AL1(min(ASU1(a),ASU1(b)));} + AL2 AMinSL2(AL2 a,AL2 b){return AL2(min(ASU2(a),ASU2(b)));} + AL3 AMinSL3(AL3 a,AL3 b){return AL3(min(ASU3(a),ASU3(b)));} + AL4 AMinSL4(AL4 a,AL4 b){return AL4(min(ASU4(a),ASU4(b)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// WAVE OPERATIONS +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return subgroupShuffleXor(v,x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return subgroupShuffleXor(v,x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return subgroupShuffleXor(v,x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return subgroupShuffleXor(v,x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return subgroupShuffleXor(v,x);} + AU2 AWaveXorU2(AU2 v,AU1 x){return subgroupShuffleXor(v,x);} + AU3 AWaveXorU3(AU3 v,AU1 x){return subgroupShuffleXor(v,x);} + AU4 AWaveXorU4(AU4 v,AU1 x){return subgroupShuffleXor(v,x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(subgroupShuffleXor(AU1_AH2(v),x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(subgroupShuffleXor(AU2_AH4(v),x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(subgroupShuffleXor(AU1_AW2(v),x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU2(subgroupShuffleXor(AU2_AW4(v),x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// HLSL +// +// +//============================================================================================================================== +#if defined(A_HLSL) && defined(A_GPU) + #ifdef A_HLSL_6_2 + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float32_t + #define AF2 float32_t2 + #define AF3 float32_t3 + #define AF4 float32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint32_t + #define AU2 uint32_t2 + #define AU3 uint32_t3 + #define AU4 uint32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int32_t + #define ASU2 int32_t2 + #define ASU3 int32_t3 + #define ASU4 int32_t4 + #else + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 float2 + #define AF3 float3 + #define AF4 float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uint2 + #define AU3 uint3 + #define AU4 uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 int2 + #define ASU3 int3 + #define ASU4 int4 + #endif +//============================================================================================================================== + #define AF1_AU1(x) asfloat(AU1(x)) + #define AF2_AU2(x) asfloat(AU2(x)) + #define AF3_AU3(x) asfloat(AU3(x)) + #define AF4_AU4(x) asfloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) asuint(AF1(x)) + #define AU2_AF2(x) asuint(AF2(x)) + #define AU3_AF3(x) asuint(AF3(x)) + #define AU4_AF4(x) asuint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return f32tof16(a);} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_AF2_x(AF2 a){return f32tof16(a.x)|(f32tof16(a.y)<<16);} + #define AU1_AH2_AF2(a) AU1_AH2_AF2_x(AF2(a)) + #define AU1_AB4Unorm_AF4(x) D3DCOLORtoUBYTE4(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AF2 AF2_AH2_AU1_x(AU1 x){return AF2(f16tof32(x&0xFFFF),f16tof32(x>>16));} + #define AF2_AH2_AU1(x) AF2_AH2_AU1_x(AU1(x)) +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){AU1 mask=(1u<<bits)-1;return (src>>off)&mask;} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){AU1 mask=(1u<<bits)-1;return (ins&mask)|(src&(~mask));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AClampF1(AF1 x,AF1 n,AF1 m){return max(n,min(x,m));} + AF2 AClampF2(AF2 x,AF2 n,AF2 m){return max(n,min(x,m));} + AF3 AClampF3(AF3 x,AF3 n,AF3 m){return max(n,min(x,m));} + AF4 AClampF4(AF4 x,AF4 n,AF4 m){return max(n,min(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFractF1(AF1 x){return x-floor(x);} + AF2 AFractF2(AF2 x){return x-floor(x);} + AF3 AFractF3(AF3 x){return x-floor(x);} + AF4 AFractF4(AF4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ALerpF1(AF1 x,AF1 y,AF1 a){return lerp(x,y,a);} + AF2 ALerpF2(AF2 x,AF2 y,AF2 a){return lerp(x,y,a);} + AF3 ALerpF3(AF3 x,AF3 y,AF3 a){return lerp(x,y,a);} + AF4 ALerpF4(AF4 x,AF4 y,AF4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMax3F1(AF1 x,AF1 y,AF1 z){return max(x,max(y,z));} + AF2 AMax3F2(AF2 x,AF2 y,AF2 z){return max(x,max(y,z));} + AF3 AMax3F3(AF3 x,AF3 y,AF3 z){return max(x,max(y,z));} + AF4 AMax3F4(AF4 x,AF4 y,AF4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3SU1(AU1 x,AU1 y,AU1 z){return AU1(max(ASU1(x),max(ASU1(y),ASU1(z))));} + AU2 AMax3SU2(AU2 x,AU2 y,AU2 z){return AU2(max(ASU2(x),max(ASU2(y),ASU2(z))));} + AU3 AMax3SU3(AU3 x,AU3 y,AU3 z){return AU3(max(ASU3(x),max(ASU3(y),ASU3(z))));} + AU4 AMax3SU4(AU4 x,AU4 y,AU4 z){return AU4(max(ASU4(x),max(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3U1(AU1 x,AU1 y,AU1 z){return max(x,max(y,z));} + AU2 AMax3U2(AU2 x,AU2 y,AU2 z){return max(x,max(y,z));} + AU3 AMax3U3(AU3 x,AU3 y,AU3 z){return max(x,max(y,z));} + AU4 AMax3U4(AU4 x,AU4 y,AU4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMaxSU1(AU1 a,AU1 b){return AU1(max(ASU1(a),ASU1(b)));} + AU2 AMaxSU2(AU2 a,AU2 b){return AU2(max(ASU2(a),ASU2(b)));} + AU3 AMaxSU3(AU3 a,AU3 b){return AU3(max(ASU3(a),ASU3(b)));} + AU4 AMaxSU4(AU4 a,AU4 b){return AU4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMed3F1(AF1 x,AF1 y,AF1 z){return max(min(x,y),min(max(x,y),z));} + AF2 AMed3F2(AF2 x,AF2 y,AF2 z){return max(min(x,y),min(max(x,y),z));} + AF3 AMed3F3(AF3 x,AF3 y,AF3 z){return max(min(x,y),min(max(x,y),z));} + AF4 AMed3F4(AF4 x,AF4 y,AF4 z){return max(min(x,y),min(max(x,y),z));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMin3F1(AF1 x,AF1 y,AF1 z){return min(x,min(y,z));} + AF2 AMin3F2(AF2 x,AF2 y,AF2 z){return min(x,min(y,z));} + AF3 AMin3F3(AF3 x,AF3 y,AF3 z){return min(x,min(y,z));} + AF4 AMin3F4(AF4 x,AF4 y,AF4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3SU1(AU1 x,AU1 y,AU1 z){return AU1(min(ASU1(x),min(ASU1(y),ASU1(z))));} + AU2 AMin3SU2(AU2 x,AU2 y,AU2 z){return AU2(min(ASU2(x),min(ASU2(y),ASU2(z))));} + AU3 AMin3SU3(AU3 x,AU3 y,AU3 z){return AU3(min(ASU3(x),min(ASU3(y),ASU3(z))));} + AU4 AMin3SU4(AU4 x,AU4 y,AU4 z){return AU4(min(ASU4(x),min(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3U1(AU1 x,AU1 y,AU1 z){return min(x,min(y,z));} + AU2 AMin3U2(AU2 x,AU2 y,AU2 z){return min(x,min(y,z));} + AU3 AMin3U3(AU3 x,AU3 y,AU3 z){return min(x,min(y,z));} + AU4 AMin3U4(AU4 x,AU4 y,AU4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMinSU1(AU1 a,AU1 b){return AU1(min(ASU1(a),ASU1(b)));} + AU2 AMinSU2(AU2 a,AU2 b){return AU2(min(ASU2(a),ASU2(b)));} + AU3 AMinSU3(AU3 a,AU3 b){return AU3(min(ASU3(a),ASU3(b)));} + AU4 AMinSU4(AU4 a,AU4 b){return AU4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ANCosF1(AF1 x){return cos(x*AF1_(A_2PI));} + AF2 ANCosF2(AF2 x){return cos(x*AF2_(A_2PI));} + AF3 ANCosF3(AF3 x){return cos(x*AF3_(A_2PI));} + AF4 ANCosF4(AF4 x){return cos(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ANSinF1(AF1 x){return sin(x*AF1_(A_2PI));} + AF2 ANSinF2(AF2 x){return sin(x*AF2_(A_2PI));} + AF3 ANSinF3(AF3 x){return sin(x*AF3_(A_2PI));} + AF4 ANSinF4(AF4 x){return sin(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARcpF1(AF1 x){return rcp(x);} + AF2 ARcpF2(AF2 x){return rcp(x);} + AF3 ARcpF3(AF3 x){return rcp(x);} + AF4 ARcpF4(AF4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARsqF1(AF1 x){return rsqrt(x);} + AF2 ARsqF2(AF2 x){return rsqrt(x);} + AF3 ARsqF3(AF3 x){return rsqrt(x);} + AF4 ARsqF4(AF4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASatF1(AF1 x){return saturate(x);} + AF2 ASatF2(AF2 x){return saturate(x);} + AF3 ASatF3(AF3 x){return saturate(x);} + AF4 ASatF4(AF4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AShrSU1(AU1 a,AU1 b){return AU1(ASU1(a)>>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define AH1 float16_t + #define AH2 float16_t2 + #define AH3 float16_t3 + #define AH4 float16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 uint16_t2 + #define AW3 uint16_t3 + #define AW4 uint16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 int16_t2 + #define ASW3 int16_t3 + #define ASW4 int16_t4 + #else + #define AH1 min16float + #define AH2 min16float2 + #define AH3 min16float3 + #define AH4 min16float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 min16uint + #define AW2 min16uint2 + #define AW3 min16uint3 + #define AW4 min16uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 min16int + #define ASW2 min16int2 + #define ASW3 min16int3 + #define ASW4 min16int4 + #endif +//============================================================================================================================== + // Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). + // Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ + AH2 AH2_AU1_x(AU1 x){AF2 t=f16tof32(AU2(x&0xFFFF,x>>16));return AH2(t);} + AH4 AH4_AU2_x(AU2 x){return AH4(AH2_AU1_x(x.x),AH2_AU1_x(x.y));} + AW2 AW2_AU1_x(AU1 x){AU2 t=AU2(x&0xFFFF,x>>16);return AW2(t);} + AW4 AW4_AU2_x(AU2 x){return AW4(AW2_AU1_x(x.x),AW2_AU1_x(x.y));} + #define AH2_AU1(x) AH2_AU1_x(AU1(x)) + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) AW2_AU1_x(AU1(x)) + #define AW4_AU2(x) AW4_AU2_x(AU2(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_x(AH2 x){return f32tof16(x.x)+(f32tof16(x.y)<<16);} + AU2 AU2_AH4_x(AH4 x){return AU2(AU1_AH2_x(x.xy),AU1_AH2_x(x.zw));} + AU1 AU1_AW2_x(AW2 x){return AU1(x.x)+(AU1(x.y)<<16);} + AU2 AU2_AW4_x(AW4 x){return AU2(AU1_AW2_x(x.xy),AU1_AW2_x(x.zw));} + #define AU1_AH2(x) AU1_AH2_x(AH2(x)) + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) AU1_AW2_x(AW2(x)) + #define AU2_AW4(x) AU2_AW4_x(AW4(x)) +//============================================================================================================================== + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AW1_AH1(x) asuint16(x) + #define AW2_AH2(x) asuint16(x) + #define AW3_AH3(x) asuint16(x) + #define AW4_AH4(x) asuint16(x) + #else + #define AW1_AH1(a) AW1(f32tof16(AF1(a))) + #define AW2_AH2(a) AW2(AW1_AH1((a).x),AW1_AH1((a).y)) + #define AW3_AH3(a) AW3(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z)) + #define AW4_AH4(a) AW4(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z),AW1_AH1((a).w)) + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AH1_AW1(x) asfloat16(x) + #define AH2_AW2(x) asfloat16(x) + #define AH3_AW3(x) asfloat16(x) + #define AH4_AW4(x) asfloat16(x) + #else + #define AH1_AW1(a) AH1(f16tof32(AU1(a))) + #define AH2_AW2(a) AH2(AH1_AW1((a).x),AH1_AW1((a).y)) + #define AH3_AW3(a) AH3(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z)) + #define AH4_AW4(a) AH4(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z),AH1_AW1((a).w)) + #endif +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return max(n,min(x,m));} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return max(n,min(x,m));} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return max(n,min(x,m));} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return max(n,min(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F16 (note DX frac() is different). + AH1 AFractH1(AH1 x){return x-floor(x);} + AH2 AFractH2(AH2 x){return x-floor(x);} + AH3 AFractH3(AH3 x){return x-floor(x);} + AH4 AFractH4(AH4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return lerp(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return lerp(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return lerp(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return rcp(x);} + AH2 ARcpH2(AH2 x){return rcp(x);} + AH3 ARcpH3(AH3 x){return rcp(x);} + AH4 ARcpH4(AH4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return rsqrt(x);} + AH2 ARsqH2(AH2 x){return rsqrt(x);} + AH3 ARsqH3(AH3 x){return rsqrt(x);} + AH4 ARsqH4(AH4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return saturate(x);} + AH2 ASatH2(AH2 x){return saturate(x);} + AH3 ASatH3(AH3 x){return saturate(x);} + AH4 ASatH4(AH4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #ifdef A_HLSL_6_2 + #define AD1 float64_t + #define AD2 float64_t2 + #define AD3 float64_t3 + #define AD4 float64_t4 + #else + #define AD1 double + #define AD2 double2 + #define AD3 double3 + #define AD4 double4 + #endif +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 a){return a-floor(a);} + AD2 AFractD2(AD2 a){return a-floor(a);} + AD3 AFractD3(AD3 a){return a-floor(a);} + AD4 AFractD4(AD4 a){return a-floor(a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return lerp(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return lerp(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return lerp(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return rcp(x);} + AD2 ARcpD2(AD2 x){return rcp(x);} + AD3 ARcpD3(AD3 x){return rcp(x);} + AD4 ARcpD4(AD4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return rsqrt(x);} + AD2 ARsqD2(AD2 x){return rsqrt(x);} + AD3 ARsqD3(AD3 x){return rsqrt(x);} + AD4 ARsqD4(AD4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return saturate(x);} + AD2 ASatD2(AD2 x){return saturate(x);} + AD3 ASatD3(AD3 x){return saturate(x);} + AD4 ASatD4(AD4 x){return saturate(x);} + #endif +//============================================================================================================================== +// HLSL WAVE +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU2 AWaveXorU1(AU2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU3 AWaveXorU1(AU3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU4 AWaveXorU1(AU4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(WaveReadLaneAt(AU1_AH2(v),WaveGetLaneIndex()^x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(WaveReadLaneAt(AU2_AH4(v),WaveGetLaneIndex()^x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(WaveReadLaneAt(AU1_AW2(v),WaveGetLaneIndex()^x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU1(WaveReadLaneAt(AU1_AW4(v),WaveGetLaneIndex()^x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU COMMON +// +// +//============================================================================================================================== +#ifdef A_GPU + // Negative and positive infinity. + #define A_INFP_F AF1_AU1(0x7f800000u) + #define A_INFN_F AF1_AU1(0xff800000u) +//------------------------------------------------------------------------------------------------------------------------------ + // Copy sign from 's' to positive 'd'. + AF1 ACpySgnF1(AF1 d,AF1 s){return AF1_AU1(AU1_AF1(d)|(AU1_AF1(s)&AU1_(0x80000000u)));} + AF2 ACpySgnF2(AF2 d,AF2 s){return AF2_AU2(AU2_AF2(d)|(AU2_AF2(s)&AU2_(0x80000000u)));} + AF3 ACpySgnF3(AF3 d,AF3 s){return AF3_AU3(AU3_AF3(d)|(AU3_AF3(s)&AU3_(0x80000000u)));} + AF4 ACpySgnF4(AF4 d,AF4 s){return AF4_AU4(AU4_AF4(d)|(AU4_AF4(s)&AU4_(0x80000000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Single operation to return (useful to create a mask to use in lerp for branch free logic), + // m=NaN := 0 + // m>=0 := 0 + // m<0 := 1 + // Uses the following useful floating point logic, + // saturate(+a*(-INF)==-INF) := 0 + // saturate( 0*(-INF)== NaN) := 0 + // saturate(-a*(-INF)==+INF) := 1 + AF1 ASignedF1(AF1 m){return ASatF1(m*AF1_(A_INFN_F));} + AF2 ASignedF2(AF2 m){return ASatF2(m*AF2_(A_INFN_F));} + AF3 ASignedF3(AF3 m){return ASatF3(m*AF3_(A_INFN_F));} + AF4 ASignedF4(AF4 m){return ASatF4(m*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AGtZeroF1(AF1 m){return ASatF1(m*AF1_(A_INFP_F));} + AF2 AGtZeroF2(AF2 m){return ASatF2(m*AF2_(A_INFP_F));} + AF3 AGtZeroF3(AF3 m){return ASatF3(m*AF3_(A_INFP_F));} + AF4 AGtZeroF4(AF4 m){return ASatF4(m*AF4_(A_INFP_F));} +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define A_INFP_H AH1_AW1((uint16_t)0x7c00u) + #define A_INFN_H AH1_AW1((uint16_t)0xfc00u) + #else + #define A_INFP_H AH1_AW1(0x7c00u) + #define A_INFN_H AH1_AW1(0xfc00u) + #endif + +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ACpySgnH1(AH1 d,AH1 s){return AH1_AW1(AW1_AH1(d)|(AW1_AH1(s)&AW1_(0x8000u)));} + AH2 ACpySgnH2(AH2 d,AH2 s){return AH2_AW2(AW2_AH2(d)|(AW2_AH2(s)&AW2_(0x8000u)));} + AH3 ACpySgnH3(AH3 d,AH3 s){return AH3_AW3(AW3_AH3(d)|(AW3_AH3(s)&AW3_(0x8000u)));} + AH4 ACpySgnH4(AH4 d,AH4 s){return AH4_AW4(AW4_AH4(d)|(AW4_AH4(s)&AW4_(0x8000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASignedH1(AH1 m){return ASatH1(m*AH1_(A_INFN_H));} + AH2 ASignedH2(AH2 m){return ASatH2(m*AH2_(A_INFN_H));} + AH3 ASignedH3(AH3 m){return ASatH3(m*AH3_(A_INFN_H));} + AH4 ASignedH4(AH4 m){return ASatH4(m*AH4_(A_INFN_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AGtZeroH1(AH1 m){return ASatH1(m*AH1_(A_INFP_H));} + AH2 AGtZeroH2(AH2 m){return ASatH2(m*AH2_(A_INFP_H));} + AH3 AGtZeroH3(AH3 m){return ASatH3(m*AH3_(A_INFP_H));} + AH4 AGtZeroH4(AH4 m){return ASatH4(m*AH4_(A_INFP_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [FIS] FLOAT INTEGER SORTABLE +//------------------------------------------------------------------------------------------------------------------------------ +// Float to integer sortable. +// - If sign bit=0, flip the sign bit (positives). +// - If sign bit=1, flip all bits (negatives). +// Integer sortable to float. +// - If sign bit=1, flip the sign bit (positives). +// - If sign bit=0, flip all bits (negatives). +// Has nice side effects. +// - Larger integers are more positive values. +// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). +// Burns 3 ops for conversion {shift,or,xor}. +//============================================================================================================================== + AU1 AFisToU1(AU1 x){return x^(( AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} + AU1 AFisFromU1(AU1 x){return x^((~AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + // Just adjust high 16-bit value (useful when upper part of 32-bit word is a 16-bit float value). + AU1 AFisToHiU1(AU1 x){return x^(( AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} + AU1 AFisFromHiU1(AU1 x){return x^((~AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AW1 AFisToW1(AW1 x){return x^(( AShrSW1(x,AW1_(15)))|AW1_(0x8000));} + AW1 AFisFromW1(AW1 x){return x^((~AShrSW1(x,AW1_(15)))|AW1_(0x8000));} +//------------------------------------------------------------------------------------------------------------------------------ + AW2 AFisToW2(AW2 x){return x^(( AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + AW2 AFisFromW2(AW2 x){return x^((~AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [PERM] V_PERM_B32 +//------------------------------------------------------------------------------------------------------------------------------ +// Support for V_PERM_B32 started in the 3rd generation of GCN. +//------------------------------------------------------------------------------------------------------------------------------ +// yyyyxxxx - The 'i' input. +// 76543210 +// ======== +// HGFEDCBA - Naming on permutation. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure compiler optimizes this. +//============================================================================================================================== + #ifdef A_HALF + AU1 APerm0E0A(AU2 i){return((i.x )&0xffu)|((i.y<<16)&0xff0000u);} + AU1 APerm0F0B(AU2 i){return((i.x>> 8)&0xffu)|((i.y<< 8)&0xff0000u);} + AU1 APerm0G0C(AU2 i){return((i.x>>16)&0xffu)|((i.y )&0xff0000u);} + AU1 APerm0H0D(AU2 i){return((i.x>>24)&0xffu)|((i.y>> 8)&0xff0000u);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermHGFA(AU2 i){return((i.x )&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGFC(AU2 i){return((i.x>>16)&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGAE(AU2 i){return((i.x<< 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHGCE(AU2 i){return((i.x>> 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHAFE(AU2 i){return((i.x<<16)&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermHCFE(AU2 i){return((i.x )&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermAGFE(AU2 i){return((i.x<<24)&0xff000000u)|(i.y&0x00ffffffu);} + AU1 APermCGFE(AU2 i){return((i.x<< 8)&0xff000000u)|(i.y&0x00ffffffu);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermGCEA(AU2 i){return((i.x)&0x00ff00ffu)|((i.y<<8)&0xff00ff00u);} + AU1 APermGECA(AU2 i){return(((i.x)&0xffu)|((i.x>>8)&0xff00u)|((i.y<<16)&0xff0000u)|((i.y<<8)&0xff000000u));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BUC] BYTE UNSIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Designed to use the optimal conversion, enables the scaling to possibly be factored into other computation. +// Works on a range of {0 to A_BUC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// OPCODE NOTES +// ============ +// GCN does not do UNORM or SNORM for bytes in opcodes. +// - V_CVT_F32_UBYTE{0,1,2,3} - Unsigned byte to float. +// - V_CVT_PKACC_U8_F32 - Float to unsigned byte (does bit-field insert into 32-bit integer). +// V_PERM_B32 does byte packing with ability to zero fill bytes as well. +// - Can pull out byte values from two sources, and zero fill upper 8-bits of packed hi and lo. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U1() - Designed for V_CVT_F32_UBYTE* and V_CVT_PKACCUM_U8_F32 ops. +// ==== ===== +// 0 : 0 +// 1 : 1 +// ... +// 255 : 255 +// : 256 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : 0 +// 1 : 1/512 +// 2 : 1/256 +// ... +// 64 : 1/8 +// 128 : 1/4 +// 255 : 255/512 +// : 1/2 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMAL IMPLEMENTATIONS ON AMD ARCHITECTURES +// ============================================ +// r=ABuc0FromU1(i) +// V_CVT_F32_UBYTE0 r,i +// -------------------------------------------- +// r=ABuc0ToU1(d,i) +// V_CVT_PKACCUM_U8_F32 r,i,0,d +// -------------------------------------------- +// d=ABuc0FromU2(i) +// Where 'k0' is an SGPR with 0x0E0A +// Where 'k1' is an SGPR with {32768.0} packed into the lower 16-bits +// V_PERM_B32 d,i.x,i.y,k0 +// V_PK_FMA_F16 d,d,k1.x,0 +// -------------------------------------------- +// r=ABuc0ToU2(d,i) +// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +// Where 'k1' is an SGPR with 0x???? +// Where 'k2' is an SGPR with 0x???? +// V_PK_FMA_F16 i,i,k0.x,0 +// V_PERM_B32 r.x,i,i,k1 +// V_PERM_B32 r.y,i,i,k2 +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BUC_32 (255.0) + #define A_BUC_16 (255.0/512.0) +//============================================================================================================================== + #if 1 + // Designed to be one V_CVT_PKACCUM_U8_F32. + // The extra min is required to pattern match to V_CVT_PKACCUM_U8_F32. + AU1 ABuc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i),255u) )&(0x000000ffu));} + AU1 ABuc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i),255u)<< 8)&(0x0000ff00u));} + AU1 ABuc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i),255u)<<16)&(0x00ff0000u));} + AU1 ABuc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed to be one V_CVT_F32_UBYTE*. + AF1 ABuc0FromU1(AU1 i){return AF1((i )&255u);} + AF1 ABuc1FromU1(AU1 i){return AF1((i>> 8)&255u);} + AF1 ABuc2FromU1(AU1 i){return AF1((i>>16)&255u);} + AF1 ABuc3FromU1(AU1 i){return AF1((i>>24)&255u);} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABuc01ToW2(AH2 x,AH2 y){x*=AH2_(1.0/32768.0);y*=AH2_(1.0/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 3 ops to do SOA to AOS and conversion. + AU2 ABuc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABuc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABuc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABuc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 2 ops to do both AOS to SOA, and conversion. + AH2 ABuc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0);} + AH2 ABuc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0);} + AH2 ABuc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0);} + AH2 ABuc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BSC] BYTE SIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Similar to [BUC]. +// Works on a range of {-/+ A_BSC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// ENCODING (without zero-based encoding) +// ======== +// 0 = unused (can be used to mean something else) +// 1 = lowest value +// 128 = exact zero center (zero based encoding +// 255 = highest value +//------------------------------------------------------------------------------------------------------------------------------ +// Zero-based [Zb] flips the MSB bit of the byte (making 128 "exact zero" actually zero). +// This is useful if there is a desire for cleared values to decode as zero. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABsc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : -127/512 (unused) +// 1 : -126/512 +// 2 : -125/512 +// ... +// 128 : 0 +// ... +// 255 : 127/512 +// : 1/4 (just outside the encoding range) +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BSC_32 (127.0) + #define A_BSC_16 (127.0/512.0) +//============================================================================================================================== + #if 1 + AU1 ABsc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i+128.0),255u) )&(0x000000ffu));} + AU1 ABsc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i+128.0),255u)<< 8)&(0x0000ff00u));} + AU1 ABsc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i+128.0),255u)<<16)&(0x00ff0000u));} + AU1 ABsc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i+128.0),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABsc0ToZbU1(AU1 d,AF1 i){return ((d&0xffffff00u)|((min(AU1(trunc(i)+128.0),255u) )&(0x000000ffu)))^0x00000080u;} + AU1 ABsc1ToZbU1(AU1 d,AF1 i){return ((d&0xffff00ffu)|((min(AU1(trunc(i)+128.0),255u)<< 8)&(0x0000ff00u)))^0x00008000u;} + AU1 ABsc2ToZbU1(AU1 d,AF1 i){return ((d&0xff00ffffu)|((min(AU1(trunc(i)+128.0),255u)<<16)&(0x00ff0000u)))^0x00800000u;} + AU1 ABsc3ToZbU1(AU1 d,AF1 i){return ((d&0x00ffffffu)|((min(AU1(trunc(i)+128.0),255u)<<24)&(0xff000000u)))^0x80000000u;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromU1(AU1 i){return AF1((i )&255u)-128.0;} + AF1 ABsc1FromU1(AU1 i){return AF1((i>> 8)&255u)-128.0;} + AF1 ABsc2FromU1(AU1 i){return AF1((i>>16)&255u)-128.0;} + AF1 ABsc3FromU1(AU1 i){return AF1((i>>24)&255u)-128.0;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromZbU1(AU1 i){return AF1(((i )&255u)^0x80u)-128.0;} + AF1 ABsc1FromZbU1(AU1 i){return AF1(((i>> 8)&255u)^0x80u)-128.0;} + AF1 ABsc2FromZbU1(AU1 i){return AF1(((i>>16)&255u)^0x80u)-128.0;} + AF1 ABsc3FromZbU1(AU1 i){return AF1(((i>>24)&255u)^0x80u)-128.0;} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABsc01ToW2(AH2 x,AH2 y){x=x*AH2_(1.0/32768.0)+AH2_(0.25/32768.0);y=y*AH2_(1.0/32768.0)+AH2_(0.25/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0)-AH2_(0.25);} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These support only positive inputs. +// Did not see value yet in specialization for range. +// Using quick testing, ended up mostly getting the same "best" approximation for various ranges. +// With hardware that can co-execute transcendentals, the value in approximations could be less than expected. +// However from a latency perspective, if execution of a transcendental is 4 clk, with no packed support, -> 8 clk total. +// And co-execution would require a compiler interleaving a lot of independent work for packed usage. +//------------------------------------------------------------------------------------------------------------------------------ +// The one Newton Raphson iteration form of rsq() was skipped (requires 6 ops total). +// Same with sqrt(), as this could be x*rsq() (7 ops). +//============================================================================================================================== + #ifdef A_HALF + // Minimize squared error across full positive range, 2 ops. + // The 0x1de2 based approximation maps {0 to 1} input maps to < 1 output. + AH1 APrxLoSqrtH1(AH1 a){return AH1_AW1((AW1_AH1(a)>>AW1_(1))+AW1_(0x1de2));} + AH2 APrxLoSqrtH2(AH2 a){return AH2_AW2((AW2_AH2(a)>>AW2_(1))+AW2_(0x1de2));} + AH3 APrxLoSqrtH3(AH3 a){return AH3_AW3((AW3_AH3(a)>>AW3_(1))+AW3_(0x1de2));} + AH4 APrxLoSqrtH4(AH4 a){return AH4_AW4((AW4_AH4(a)>>AW4_(1))+AW4_(0x1de2));} +//------------------------------------------------------------------------------------------------------------------------------ + // Lower precision estimation, 1 op. + // Minimize squared error across {smallest normal to 16384.0}. + AH1 APrxLoRcpH1(AH1 a){return AH1_AW1(AW1_(0x7784)-AW1_AH1(a));} + AH2 APrxLoRcpH2(AH2 a){return AH2_AW2(AW2_(0x7784)-AW2_AH2(a));} + AH3 APrxLoRcpH3(AH3 a){return AH3_AW3(AW3_(0x7784)-AW3_AH3(a));} + AH4 APrxLoRcpH4(AH4 a){return AH4_AW4(AW4_(0x7784)-AW4_AH4(a));} +//------------------------------------------------------------------------------------------------------------------------------ + // Medium precision estimation, one Newton Raphson iteration, 3 ops. + AH1 APrxMedRcpH1(AH1 a){AH1 b=AH1_AW1(AW1_(0x778d)-AW1_AH1(a));return b*(-b*a+AH1_(2.0));} + AH2 APrxMedRcpH2(AH2 a){AH2 b=AH2_AW2(AW2_(0x778d)-AW2_AH2(a));return b*(-b*a+AH2_(2.0));} + AH3 APrxMedRcpH3(AH3 a){AH3 b=AH3_AW3(AW3_(0x778d)-AW3_AH3(a));return b*(-b*a+AH3_(2.0));} + AH4 APrxMedRcpH4(AH4 a){AH4 b=AH4_AW4(AW4_(0x778d)-AW4_AH4(a));return b*(-b*a+AH4_(2.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // Minimize squared error across {smallest normal to 16384.0}, 2 ops. + AH1 APrxLoRsqH1(AH1 a){return AH1_AW1(AW1_(0x59a3)-(AW1_AH1(a)>>AW1_(1)));} + AH2 APrxLoRsqH2(AH2 a){return AH2_AW2(AW2_(0x59a3)-(AW2_AH2(a)>>AW2_(1)));} + AH3 APrxLoRsqH3(AH3 a){return AH3_AW3(AW3_(0x59a3)-(AW3_AH3(a)>>AW3_(1)));} + AH4 APrxLoRsqH4(AH4 a){return AH4_AW4(AW4_(0x59a3)-(AW4_AH4(a)>>AW4_(1)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// FLOAT APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// Michal Drobot has an excellent presentation on these: "Low Level Optimizations For GCN", +// - Idea dates back to SGI, then to Quake 3, etc. +// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +// - sqrt(x)=rsqrt(x)*x +// - rcp(x)=rsqrt(x)*rsqrt(x) for positive x +// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +//------------------------------------------------------------------------------------------------------------------------------ +// These below are from perhaps less complete searching for optimal. +// Used FP16 normal range for testing with +4096 32-bit step size for sampling error. +// So these match up well with the half approximations. +//============================================================================================================================== + AF1 APrxLoSqrtF1(AF1 a){return AF1_AU1((AU1_AF1(a)>>AU1_(1))+AU1_(0x1fbc4639));} + AF1 APrxLoRcpF1(AF1 a){return AF1_AU1(AU1_(0x7ef07ebb)-AU1_AF1(a));} + AF1 APrxMedRcpF1(AF1 a){AF1 b=AF1_AU1(AU1_(0x7ef19fff)-AU1_AF1(a));return b*(-b*a+AF1_(2.0));} + AF1 APrxLoRsqF1(AF1 a){return AF1_AU1(AU1_(0x5f347d74)-(AU1_AF1(a)>>AU1_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxLoSqrtF2(AF2 a){return AF2_AU2((AU2_AF2(a)>>AU2_(1))+AU2_(0x1fbc4639));} + AF2 APrxLoRcpF2(AF2 a){return AF2_AU2(AU2_(0x7ef07ebb)-AU2_AF2(a));} + AF2 APrxMedRcpF2(AF2 a){AF2 b=AF2_AU2(AU2_(0x7ef19fff)-AU2_AF2(a));return b*(-b*a+AF2_(2.0));} + AF2 APrxLoRsqF2(AF2 a){return AF2_AU2(AU2_(0x5f347d74)-(AU2_AF2(a)>>AU2_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxLoSqrtF3(AF3 a){return AF3_AU3((AU3_AF3(a)>>AU3_(1))+AU3_(0x1fbc4639));} + AF3 APrxLoRcpF3(AF3 a){return AF3_AU3(AU3_(0x7ef07ebb)-AU3_AF3(a));} + AF3 APrxMedRcpF3(AF3 a){AF3 b=AF3_AU3(AU3_(0x7ef19fff)-AU3_AF3(a));return b*(-b*a+AF3_(2.0));} + AF3 APrxLoRsqF3(AF3 a){return AF3_AU3(AU3_(0x5f347d74)-(AU3_AF3(a)>>AU3_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxLoSqrtF4(AF4 a){return AF4_AU4((AU4_AF4(a)>>AU4_(1))+AU4_(0x1fbc4639));} + AF4 APrxLoRcpF4(AF4 a){return AF4_AU4(AU4_(0x7ef07ebb)-AU4_AF4(a));} + AF4 APrxMedRcpF4(AF4 a){AF4 b=AF4_AU4(AU4_(0x7ef19fff)-AU4_AF4(a));return b*(-b*a+AF4_(2.0));} + AF4 APrxLoRsqF4(AF4 a){return AF4_AU4(AU4_(0x5f347d74)-(AU4_AF4(a)>>AU4_(1)));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PQ APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// PQ is very close to x^(1/8). The functions below Use the fast float approximation method to do +// PQ<~>Gamma2 (4th power and fast 4th root) and PQ<~>Linear (8th power and fast 8th root). Maximum error is ~0.2%. +//============================================================================================================================== +// Helpers + AF1 Quart(AF1 a) { a = a * a; return a * a;} + AF1 Oct(AF1 a) { a = a * a; a = a * a; return a * a; } + AF2 Quart(AF2 a) { a = a * a; return a * a; } + AF2 Oct(AF2 a) { a = a * a; a = a * a; return a * a; } + AF3 Quart(AF3 a) { a = a * a; return a * a; } + AF3 Oct(AF3 a) { a = a * a; a = a * a; return a * a; } + AF4 Quart(AF4 a) { a = a * a; return a * a; } + AF4 Oct(AF4 a) { a = a * a; a = a * a; return a * a; } + //------------------------------------------------------------------------------------------------------------------------------ + AF1 APrxPQToGamma2(AF1 a) { return Quart(a); } + AF1 APrxPQToLinear(AF1 a) { return Oct(a); } + AF1 APrxLoGamma2ToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); } + AF1 APrxMedGamma2ToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); AF1 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF1 APrxHighGamma2ToPQ(AF1 a) { return sqrt(sqrt(a)); } + AF1 APrxLoLinearToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); } + AF1 APrxMedLinearToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); AF1 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF1 APrxHighLinearToPQ(AF1 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxPQToGamma2(AF2 a) { return Quart(a); } + AF2 APrxPQToLinear(AF2 a) { return Oct(a); } + AF2 APrxLoGamma2ToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); } + AF2 APrxMedGamma2ToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); AF2 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF2 APrxHighGamma2ToPQ(AF2 a) { return sqrt(sqrt(a)); } + AF2 APrxLoLinearToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); } + AF2 APrxMedLinearToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); AF2 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF2 APrxHighLinearToPQ(AF2 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxPQToGamma2(AF3 a) { return Quart(a); } + AF3 APrxPQToLinear(AF3 a) { return Oct(a); } + AF3 APrxLoGamma2ToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); } + AF3 APrxMedGamma2ToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); AF3 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF3 APrxHighGamma2ToPQ(AF3 a) { return sqrt(sqrt(a)); } + AF3 APrxLoLinearToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); } + AF3 APrxMedLinearToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); AF3 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF3 APrxHighLinearToPQ(AF3 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxPQToGamma2(AF4 a) { return Quart(a); } + AF4 APrxPQToLinear(AF4 a) { return Oct(a); } + AF4 APrxLoGamma2ToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); } + AF4 APrxMedGamma2ToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); AF4 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF4 APrxHighGamma2ToPQ(AF4 a) { return sqrt(sqrt(a)); } + AF4 APrxLoLinearToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); } + AF4 APrxMedLinearToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); AF4 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF4 APrxHighLinearToPQ(AF4 a) { return sqrt(sqrt(sqrt(a))); } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PARABOLIC SIN & COS +//------------------------------------------------------------------------------------------------------------------------------ +// Approximate answers to transcendental questions. +//------------------------------------------------------------------------------------------------------------------------------ +//============================================================================================================================== + #if 1 + // Valid input range is {-1 to 1} representing {0 to 2 pi}. + // Output range is {-1/4 to 1/4} representing {-1 to 1}. + AF1 APSinF1(AF1 x){return x*abs(x)-x;} // MAD. + AF2 APSinF2(AF2 x){return x*abs(x)-x;} + AF1 APCosF1(AF1 x){x=AFractF1(x*AF1_(0.5)+AF1_(0.75));x=x*AF1_(2.0)-AF1_(1.0);return APSinF1(x);} // 3x MAD, FRACT + AF2 APCosF2(AF2 x){x=AFractF2(x*AF2_(0.5)+AF2_(0.75));x=x*AF2_(2.0)-AF2_(1.0);return APSinF2(x);} + AF2 APSinCosF1(AF1 x){AF1 y=AFractF1(x*AF1_(0.5)+AF1_(0.75));y=y*AF1_(2.0)-AF1_(1.0);return APSinF2(AF2(x,y));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + // For a packed {sin,cos} pair, + // - Native takes 16 clocks and 4 issue slots (no packed transcendentals). + // - Parabolic takes 8 clocks and 8 issue slots (only fract is non-packed). + AH1 APSinH1(AH1 x){return x*abs(x)-x;} + AH2 APSinH2(AH2 x){return x*abs(x)-x;} // AND,FMA + AH1 APCosH1(AH1 x){x=AFractH1(x*AH1_(0.5)+AH1_(0.75));x=x*AH1_(2.0)-AH1_(1.0);return APSinH1(x);} + AH2 APCosH2(AH2 x){x=AFractH2(x*AH2_(0.5)+AH2_(0.75));x=x*AH2_(2.0)-AH2_(1.0);return APSinH2(x);} // 3x FMA, 2xFRACT, AND + AH2 APSinCosH1(AH1 x){AH1 y=AFractH1(x*AH1_(0.5)+AH1_(0.75));y=y*AH1_(2.0)-AH1_(1.0);return APSinH2(AH2(x,y));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [ZOL] ZERO ONE LOGIC +//------------------------------------------------------------------------------------------------------------------------------ +// Conditional free logic designed for easy 16-bit packing, and backwards porting to 32-bit. +//------------------------------------------------------------------------------------------------------------------------------ +// 0 := false +// 1 := true +//------------------------------------------------------------------------------------------------------------------------------ +// AndNot(x,y) -> !(x&y) .... One op. +// AndOr(x,y,z) -> (x&y)|z ... One op. +// GtZero(x) -> x>0.0 ..... One op. +// Sel(x,y,z) -> x?y:z ..... Two ops, has no precision loss. +// Signed(x) -> x<0.0 ..... One op. +// ZeroPass(x,y) -> x?0:y ..... Two ops, 'y' is a pass through safe for aliasing as integer. +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMIZATION NOTES +// ================== +// - On Vega to use 2 constants in a packed op, pass in as one AW2 or one AH2 'k.xy' and use as 'k.xx' and 'k.yy'. +// For example 'a.xy*k.xx+k.yy'. +//============================================================================================================================== + #if 1 + AU1 AZolAndU1(AU1 x,AU1 y){return min(x,y);} + AU2 AZolAndU2(AU2 x,AU2 y){return min(x,y);} + AU3 AZolAndU3(AU3 x,AU3 y){return min(x,y);} + AU4 AZolAndU4(AU4 x,AU4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolNotU1(AU1 x){return x^AU1_(1);} + AU2 AZolNotU2(AU2 x){return x^AU2_(1);} + AU3 AZolNotU3(AU3 x){return x^AU3_(1);} + AU4 AZolNotU4(AU4 x){return x^AU4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolOrU1(AU1 x,AU1 y){return max(x,y);} + AU2 AZolOrU2(AU2 x,AU2 y){return max(x,y);} + AU3 AZolOrU3(AU3 x,AU3 y){return max(x,y);} + AU4 AZolOrU4(AU4 x,AU4 y){return max(x,y);} +//============================================================================================================================== + AU1 AZolF1ToU1(AF1 x){return AU1(x);} + AU2 AZolF2ToU2(AF2 x){return AU2(x);} + AU3 AZolF3ToU3(AF3 x){return AU3(x);} + AU4 AZolF4ToU4(AF4 x){return AU4(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // 2 ops, denormals don't work in 32-bit on PC (and if they are enabled, OMOD is disabled). + AU1 AZolNotF1ToU1(AF1 x){return AU1(AF1_(1.0)-x);} + AU2 AZolNotF2ToU2(AF2 x){return AU2(AF2_(1.0)-x);} + AU3 AZolNotF3ToU3(AF3 x){return AU3(AF3_(1.0)-x);} + AU4 AZolNotF4ToU4(AF4 x){return AU4(AF4_(1.0)-x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolU1ToF1(AU1 x){return AF1(x);} + AF2 AZolU2ToF2(AU2 x){return AF2(x);} + AF3 AZolU3ToF3(AU3 x){return AF3(x);} + AF4 AZolU4ToF4(AU4 x){return AF4(x);} +//============================================================================================================================== + AF1 AZolAndF1(AF1 x,AF1 y){return min(x,y);} + AF2 AZolAndF2(AF2 x,AF2 y){return min(x,y);} + AF3 AZolAndF3(AF3 x,AF3 y){return min(x,y);} + AF4 AZolAndF4(AF4 x,AF4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASolAndNotF1(AF1 x,AF1 y){return (-x)*y+AF1_(1.0);} + AF2 ASolAndNotF2(AF2 x,AF2 y){return (-x)*y+AF2_(1.0);} + AF3 ASolAndNotF3(AF3 x,AF3 y){return (-x)*y+AF3_(1.0);} + AF4 ASolAndNotF4(AF4 x,AF4 y){return (-x)*y+AF4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolAndOrF1(AF1 x,AF1 y,AF1 z){return ASatF1(x*y+z);} + AF2 AZolAndOrF2(AF2 x,AF2 y,AF2 z){return ASatF2(x*y+z);} + AF3 AZolAndOrF3(AF3 x,AF3 y,AF3 z){return ASatF3(x*y+z);} + AF4 AZolAndOrF4(AF4 x,AF4 y,AF4 z){return ASatF4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolGtZeroF1(AF1 x){return ASatF1(x*AF1_(A_INFP_F));} + AF2 AZolGtZeroF2(AF2 x){return ASatF2(x*AF2_(A_INFP_F));} + AF3 AZolGtZeroF3(AF3 x){return ASatF3(x*AF3_(A_INFP_F));} + AF4 AZolGtZeroF4(AF4 x){return ASatF4(x*AF4_(A_INFP_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolNotF1(AF1 x){return AF1_(1.0)-x;} + AF2 AZolNotF2(AF2 x){return AF2_(1.0)-x;} + AF3 AZolNotF3(AF3 x){return AF3_(1.0)-x;} + AF4 AZolNotF4(AF4 x){return AF4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolOrF1(AF1 x,AF1 y){return max(x,y);} + AF2 AZolOrF2(AF2 x,AF2 y){return max(x,y);} + AF3 AZolOrF3(AF3 x,AF3 y){return max(x,y);} + AF4 AZolOrF4(AF4 x,AF4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSelF1(AF1 x,AF1 y,AF1 z){AF1 r=(-x)*z+z;return x*y+r;} + AF2 AZolSelF2(AF2 x,AF2 y,AF2 z){AF2 r=(-x)*z+z;return x*y+r;} + AF3 AZolSelF3(AF3 x,AF3 y,AF3 z){AF3 r=(-x)*z+z;return x*y+r;} + AF4 AZolSelF4(AF4 x,AF4 y,AF4 z){AF4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSignedF1(AF1 x){return ASatF1(x*AF1_(A_INFN_F));} + AF2 AZolSignedF2(AF2 x){return ASatF2(x*AF2_(A_INFN_F));} + AF3 AZolSignedF3(AF3 x){return ASatF3(x*AF3_(A_INFN_F));} + AF4 AZolSignedF4(AF4 x){return ASatF4(x*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolZeroPassF1(AF1 x,AF1 y){return AF1_AU1((AU1_AF1(x)!=AU1_(0))?AU1_(0):AU1_AF1(y));} + AF2 AZolZeroPassF2(AF2 x,AF2 y){return AF2_AU2((AU2_AF2(x)!=AU2_(0))?AU2_(0):AU2_AF2(y));} + AF3 AZolZeroPassF3(AF3 x,AF3 y){return AF3_AU3((AU3_AF3(x)!=AU3_(0))?AU3_(0):AU3_AF3(y));} + AF4 AZolZeroPassF4(AF4 x,AF4 y){return AF4_AU4((AU4_AF4(x)!=AU4_(0))?AU4_(0):AU4_AF4(y));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AW1 AZolAndW1(AW1 x,AW1 y){return min(x,y);} + AW2 AZolAndW2(AW2 x,AW2 y){return min(x,y);} + AW3 AZolAndW3(AW3 x,AW3 y){return min(x,y);} + AW4 AZolAndW4(AW4 x,AW4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolNotW1(AW1 x){return x^AW1_(1);} + AW2 AZolNotW2(AW2 x){return x^AW2_(1);} + AW3 AZolNotW3(AW3 x){return x^AW3_(1);} + AW4 AZolNotW4(AW4 x){return x^AW4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolOrW1(AW1 x,AW1 y){return max(x,y);} + AW2 AZolOrW2(AW2 x,AW2 y){return max(x,y);} + AW3 AZolOrW3(AW3 x,AW3 y){return max(x,y);} + AW4 AZolOrW4(AW4 x,AW4 y){return max(x,y);} +//============================================================================================================================== + // Uses denormal trick. + AW1 AZolH1ToW1(AH1 x){return AW1_AH1(x*AH1_AW1(AW1_(1)));} + AW2 AZolH2ToW2(AH2 x){return AW2_AH2(x*AH2_AW2(AW2_(1)));} + AW3 AZolH3ToW3(AH3 x){return AW3_AH3(x*AH3_AW3(AW3_(1)));} + AW4 AZolH4ToW4(AH4 x){return AW4_AH4(x*AH4_AW4(AW4_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + // AMD arch lacks a packed conversion opcode. + AH1 AZolW1ToH1(AW1 x){return AH1_AW1(x*AW1_AH1(AH1_(1.0)));} + AH2 AZolW2ToH2(AW2 x){return AH2_AW2(x*AW2_AH2(AH2_(1.0)));} + AH3 AZolW1ToH3(AW3 x){return AH3_AW3(x*AW3_AH3(AH3_(1.0)));} + AH4 AZolW2ToH4(AW4 x){return AH4_AW4(x*AW4_AH4(AH4_(1.0)));} +//============================================================================================================================== + AH1 AZolAndH1(AH1 x,AH1 y){return min(x,y);} + AH2 AZolAndH2(AH2 x,AH2 y){return min(x,y);} + AH3 AZolAndH3(AH3 x,AH3 y){return min(x,y);} + AH4 AZolAndH4(AH4 x,AH4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASolAndNotH1(AH1 x,AH1 y){return (-x)*y+AH1_(1.0);} + AH2 ASolAndNotH2(AH2 x,AH2 y){return (-x)*y+AH2_(1.0);} + AH3 ASolAndNotH3(AH3 x,AH3 y){return (-x)*y+AH3_(1.0);} + AH4 ASolAndNotH4(AH4 x,AH4 y){return (-x)*y+AH4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolAndOrH1(AH1 x,AH1 y,AH1 z){return ASatH1(x*y+z);} + AH2 AZolAndOrH2(AH2 x,AH2 y,AH2 z){return ASatH2(x*y+z);} + AH3 AZolAndOrH3(AH3 x,AH3 y,AH3 z){return ASatH3(x*y+z);} + AH4 AZolAndOrH4(AH4 x,AH4 y,AH4 z){return ASatH4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolGtZeroH1(AH1 x){return ASatH1(x*AH1_(A_INFP_H));} + AH2 AZolGtZeroH2(AH2 x){return ASatH2(x*AH2_(A_INFP_H));} + AH3 AZolGtZeroH3(AH3 x){return ASatH3(x*AH3_(A_INFP_H));} + AH4 AZolGtZeroH4(AH4 x){return ASatH4(x*AH4_(A_INFP_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolNotH1(AH1 x){return AH1_(1.0)-x;} + AH2 AZolNotH2(AH2 x){return AH2_(1.0)-x;} + AH3 AZolNotH3(AH3 x){return AH3_(1.0)-x;} + AH4 AZolNotH4(AH4 x){return AH4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolOrH1(AH1 x,AH1 y){return max(x,y);} + AH2 AZolOrH2(AH2 x,AH2 y){return max(x,y);} + AH3 AZolOrH3(AH3 x,AH3 y){return max(x,y);} + AH4 AZolOrH4(AH4 x,AH4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSelH1(AH1 x,AH1 y,AH1 z){AH1 r=(-x)*z+z;return x*y+r;} + AH2 AZolSelH2(AH2 x,AH2 y,AH2 z){AH2 r=(-x)*z+z;return x*y+r;} + AH3 AZolSelH3(AH3 x,AH3 y,AH3 z){AH3 r=(-x)*z+z;return x*y+r;} + AH4 AZolSelH4(AH4 x,AH4 y,AH4 z){AH4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSignedH1(AH1 x){return ASatH1(x*AH1_(A_INFN_H));} + AH2 AZolSignedH2(AH2 x){return ASatH2(x*AH2_(A_INFN_H));} + AH3 AZolSignedH3(AH3 x){return ASatH3(x*AH3_(A_INFN_H));} + AH4 AZolSignedH4(AH4 x){return ASatH4(x*AH4_(A_INFN_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COLOR CONVERSIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These are all linear to/from some other space (where 'linear' has been shortened out of the function name). +// So 'ToGamma' is 'LinearToGamma', and 'FromGamma' is 'LinearFromGamma'. +// These are branch free implementations. +// The AToSrgbF1() function is useful for stores for compute shaders for GPUs without hardware linear->sRGB store conversion. +//------------------------------------------------------------------------------------------------------------------------------ +// TRANSFER FUNCTIONS +// ================== +// 709 ..... Rec709 used for some HDTVs +// Gamma ... Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native +// Pq ...... PQ native for HDR10 +// Srgb .... The sRGB output, typical of PC displays, useful for 10-bit output, or storing to 8-bit UNORM without SRGB type +// Two ..... Gamma 2.0, fastest conversion (useful for intermediate pass approximations) +// Three ... Gamma 3.0, less fast, but good for HDR. +//------------------------------------------------------------------------------------------------------------------------------ +// KEEPING TO SPEC +// =============== +// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +// Also there is a slight step in the transition regions. +// Precision of the coefficients in the spec being the likely cause. +// Main usage case of the sRGB code is to do the linear->sRGB converstion in a compute shader before store. +// This is to work around lack of hardware (typically only ROP does the conversion for free). +// To "correct" the linear segment, would be to introduce error, because hardware decode of sRGB->linear is fixed (and free). +// So this header keeps with the spec. +// For linear->sRGB transforms, the linear segment in some respects reduces error, because rounding in that region is linear. +// Rounding in the curved region in hardware (and fast software code) introduces error due to rounding in non-linear. +//------------------------------------------------------------------------------------------------------------------------------ +// FOR PQ +// ====== +// Both input and output is {0.0-1.0}, and where output 1.0 represents 10000.0 cd/m^2. +// All constants are only specified to FP32 precision. +// External PQ source reference, +// - https://github.com/ampas/aces-dev/blob/master/transforms/ctl/utilities/ACESlib.Utilities_Color.a1.0.1.ctl +//------------------------------------------------------------------------------------------------------------------------------ +// PACKED VERSIONS +// =============== +// These are the A*H2() functions. +// There is no PQ functions as FP16 seemed to not have enough precision for the conversion. +// The remaining functions are "good enough" for 8-bit, and maybe 10-bit if not concerned about a few 1-bit errors. +// Precision is lowest in the 709 conversion, higher in sRGB, higher still in Two and Gamma (when using 2.2 at least). +//------------------------------------------------------------------------------------------------------------------------------ +// NOTES +// ===== +// Could be faster for PQ conversions to be in ALU or a texture lookup depending on usage case. +//============================================================================================================================== + #if 1 + AF1 ATo709F1(AF1 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 ATo709F2(AF2 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 ATo709F3(AF3 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + // Note 'rcpX' is '1/x', where the 'x' is what would be used in AFromGamma(). + AF1 AToGammaF1(AF1 c,AF1 rcpX){return pow(c,AF1_(rcpX));} + AF2 AToGammaF2(AF2 c,AF1 rcpX){return pow(c,AF2_(rcpX));} + AF3 AToGammaF3(AF3 c,AF1 rcpX){return pow(c,AF3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToPqF1(AF1 x){AF1 p=pow(x,AF1_(0.159302)); + return pow((AF1_(0.835938)+AF1_(18.8516)*p)/(AF1_(1.0)+AF1_(18.6875)*p),AF1_(78.8438));} + AF2 AToPqF1(AF2 x){AF2 p=pow(x,AF2_(0.159302)); + return pow((AF2_(0.835938)+AF2_(18.8516)*p)/(AF2_(1.0)+AF2_(18.6875)*p),AF2_(78.8438));} + AF3 AToPqF1(AF3 x){AF3 p=pow(x,AF3_(0.159302)); + return pow((AF3_(0.835938)+AF3_(18.8516)*p)/(AF3_(1.0)+AF3_(18.6875)*p),AF3_(78.8438));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToSrgbF1(AF1 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 AToSrgbF2(AF2 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 AToSrgbF3(AF3 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToTwoF1(AF1 c){return sqrt(c);} + AF2 AToTwoF2(AF2 c){return sqrt(c);} + AF3 AToTwoF3(AF3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToThreeF1(AF1 c){return pow(c,AF1_(1.0/3.0));} + AF2 AToThreeF2(AF2 c){return pow(c,AF2_(1.0/3.0));} + AF3 AToThreeF3(AF3 c){return pow(c,AF3_(1.0/3.0));} + #endif +//============================================================================================================================== + #if 1 + // Unfortunately median won't work here. + AF1 AFrom709F1(AF1 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFrom709F2(AF2 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFrom709F3(AF3 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromGammaF1(AF1 c,AF1 x){return pow(c,AF1_(x));} + AF2 AFromGammaF2(AF2 c,AF1 x){return pow(c,AF2_(x));} + AF3 AFromGammaF3(AF3 c,AF1 x){return pow(c,AF3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromPqF1(AF1 x){AF1 p=pow(x,AF1_(0.0126833)); + return pow(ASatF1(p-AF1_(0.835938))/(AF1_(18.8516)-AF1_(18.6875)*p),AF1_(6.27739));} + AF2 AFromPqF1(AF2 x){AF2 p=pow(x,AF2_(0.0126833)); + return pow(ASatF2(p-AF2_(0.835938))/(AF2_(18.8516)-AF2_(18.6875)*p),AF2_(6.27739));} + AF3 AFromPqF1(AF3 x){AF3 p=pow(x,AF3_(0.0126833)); + return pow(ASatF3(p-AF3_(0.835938))/(AF3_(18.8516)-AF3_(18.6875)*p),AF3_(6.27739));} +//------------------------------------------------------------------------------------------------------------------------------ + // Unfortunately median won't work here. + AF1 AFromSrgbF1(AF1 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFromSrgbF2(AF2 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFromSrgbF3(AF3 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromTwoF1(AF1 c){return c*c;} + AF2 AFromTwoF2(AF2 c){return c*c;} + AF3 AFromTwoF3(AF3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromThreeF1(AF1 c){return c*c*c;} + AF2 AFromThreeF2(AF2 c){return c*c*c;} + AF3 AFromThreeF3(AF3 c){return c*c*c;} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 ATo709H1(AH1 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 ATo709H2(AH2 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 ATo709H3(AH3 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToGammaH1(AH1 c,AH1 rcpX){return pow(c,AH1_(rcpX));} + AH2 AToGammaH2(AH2 c,AH1 rcpX){return pow(c,AH2_(rcpX));} + AH3 AToGammaH3(AH3 c,AH1 rcpX){return pow(c,AH3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToSrgbH1(AH1 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 AToSrgbH2(AH2 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 AToSrgbH3(AH3 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToTwoH1(AH1 c){return sqrt(c);} + AH2 AToTwoH2(AH2 c){return sqrt(c);} + AH3 AToTwoH3(AH3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToThreeF1(AH1 c){return pow(c,AH1_(1.0/3.0));} + AH2 AToThreeF2(AH2 c){return pow(c,AH2_(1.0/3.0));} + AH3 AToThreeF3(AH3 c){return pow(c,AH3_(1.0/3.0));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 AFrom709H1(AH1 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AFrom709H2(AH2 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AFrom709H3(AH3 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromGammaH1(AH1 c,AH1 x){return pow(c,AH1_(x));} + AH2 AFromGammaH2(AH2 c,AH1 x){return pow(c,AH2_(x));} + AH3 AFromGammaH3(AH3 c,AH1 x){return pow(c,AH3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AHromSrgbF1(AH1 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AHromSrgbF2(AH2 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AHromSrgbF3(AH3 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromTwoH1(AH1 c){return c*c;} + AH2 AFromTwoH2(AH2 c){return c*c;} + AH3 AFromTwoH3(AH3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromThreeH1(AH1 c){return c*c*c;} + AH2 AFromThreeH2(AH2 c){return c*c*c;} + AH3 AFromThreeH3(AH3 c){return c*c*c;} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CS REMAP +//============================================================================================================================== + // Simple remap 64x1 to 8x8 with rotated 2x2 pixel quads in quad linear. + // 543210 + // ====== + // ..xxx. + // yy...y + AU2 ARmp8x8(AU1 a){return AU2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} +//============================================================================================================================== + // More complex remap 64x1 to 8x8 which is necessary for 2D wave reductions. + // 543210 + // ====== + // .xx..x + // y..yy. + // Details, + // LANE TO 8x8 MAPPING + // =================== + // 00 01 08 09 10 11 18 19 + // 02 03 0a 0b 12 13 1a 1b + // 04 05 0c 0d 14 15 1c 1d + // 06 07 0e 0f 16 17 1e 1f + // 20 21 28 29 30 31 38 39 + // 22 23 2a 2b 32 33 3a 3b + // 24 25 2c 2d 34 35 3c 3d + // 26 27 2e 2f 36 37 3e 3f + AU2 ARmpRed8x8(AU1 a){return AU2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} +//============================================================================================================================== + #ifdef A_HALF + AW2 ARmp8x8H(AU1 a){return AW2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} + AW2 ARmpRed8x8H(AU1 a){return AW2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} + #endif +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// REFERENCE +// +//------------------------------------------------------------------------------------------------------------------------------ +// IEEE FLOAT RULES +// ================ +// - saturate(NaN)=0, saturate(-INF)=0, saturate(+INF)=1 +// - {+/-}0 * {+/-}INF = NaN +// - -INF + (+INF) = NaN +// - {+/-}0 / {+/-}0 = NaN +// - {+/-}INF / {+/-}INF = NaN +// - a<(-0) := sqrt(a) = NaN (a=-0.0 won't NaN) +// - 0 == -0 +// - 4/0 = +INF +// - 4/-0 = -INF +// - 4+INF = +INF +// - 4-INF = -INF +// - 4*(+INF) = +INF +// - 4*(-INF) = -INF +// - -4*(+INF) = -INF +// - sqrt(+INF) = +INF +//------------------------------------------------------------------------------------------------------------------------------ +// FP16 ENCODING +// ============= +// fedcba9876543210 +// ---------------- +// ......mmmmmmmmmm 10-bit mantissa (encodes 11-bit 0.5 to 1.0 except for denormals) +// .eeeee.......... 5-bit exponent +// .00000.......... denormals +// .00001.......... -14 exponent +// .11110.......... 15 exponent +// .111110000000000 infinity +// .11111nnnnnnnnnn NaN with n!=0 +// s............... sign +//------------------------------------------------------------------------------------------------------------------------------ +// FP16/INT16 ALIASING DENORMAL +// ============================ +// 11-bit unsigned integers alias with half float denormal/normal values, +// 1 = 2^(-24) = 1/16777216 ....................... first denormal value +// 2 = 2^(-23) +// ... +// 1023 = 2^(-14)*(1-2^(-10)) = 2^(-14)*(1-1/1024) ... last denormal value +// 1024 = 2^(-14) = 1/16384 .......................... first normal value that still maps to integers +// 2047 .............................................. last normal value that still maps to integers +// Scaling limits, +// 2^15 = 32768 ...................................... largest power of 2 scaling +// Largest pow2 conversion mapping is at *32768, +// 1 : 2^(-9) = 1/512 +// 2 : 1/256 +// 4 : 1/128 +// 8 : 1/64 +// 16 : 1/32 +// 32 : 1/16 +// 64 : 1/8 +// 128 : 1/4 +// 256 : 1/2 +// 512 : 1 +// 1024 : 2 +// 2047 : a little less than 4 +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU/CPU PORTABILITY +// +// +//------------------------------------------------------------------------------------------------------------------------------ +// This is the GPU implementation. +// See the CPU implementation for docs. +//============================================================================================================================== +#ifdef A_GPU + #define A_TRUE true + #define A_FALSE false + #define A_STATIC +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD2 + #define retAD3 AD3 + #define retAD4 AD4 + #define retAF2 AF2 + #define retAF3 AF3 + #define retAF4 AF4 + #define retAL2 AL2 + #define retAL3 AL3 + #define retAL4 AL4 + #define retAU2 AU2 + #define retAU3 AU3 + #define retAU4 AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 in AD2 + #define inAD3 in AD3 + #define inAD4 in AD4 + #define inAF2 in AF2 + #define inAF3 in AF3 + #define inAF4 in AF4 + #define inAL2 in AL2 + #define inAL3 in AL3 + #define inAL4 in AL4 + #define inAU2 in AU2 + #define inAU3 in AU3 + #define inAU4 in AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 inout AD2 + #define inoutAD3 inout AD3 + #define inoutAD4 inout AD4 + #define inoutAF2 inout AF2 + #define inoutAF3 inout AF3 + #define inoutAF4 inout AF4 + #define inoutAL2 inout AL2 + #define inoutAL3 inout AL3 + #define inoutAL4 inout AL4 + #define inoutAU2 inout AU2 + #define inoutAU3 inout AU3 + #define inoutAU4 inout AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 out AD2 + #define outAD3 out AD3 + #define outAD4 out AD4 + #define outAF2 out AF2 + #define outAF3 out AF3 + #define outAF4 out AF4 + #define outAL2 out AL2 + #define outAL3 out AL3 + #define outAL4 out AL4 + #define outAU2 out AU2 + #define outAU3 out AU3 + #define outAU4 out AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD2 x + #define varAD3(x) AD3 x + #define varAD4(x) AD4 x + #define varAF2(x) AF2 x + #define varAF3(x) AF3 x + #define varAF4(x) AF4 x + #define varAL2(x) AL2 x + #define varAL3(x) AL3 x + #define varAL4(x) AL4 x + #define varAU2(x) AU2 x + #define varAU3(x) AU3 x + #define varAU4(x) AU4 x +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) AD2(x,y) + #define initAD3(x,y,z) AD3(x,y,z) + #define initAD4(x,y,z,w) AD4(x,y,z,w) + #define initAF2(x,y) AF2(x,y) + #define initAF3(x,y,z) AF3(x,y,z) + #define initAF4(x,y,z,w) AF4(x,y,z,w) + #define initAL2(x,y) AL2(x,y) + #define initAL3(x,y,z) AL3(x,y,z) + #define initAL4(x,y,z,w) AL4(x,y,z,w) + #define initAU2(x,y) AU2(x,y) + #define initAU3(x,y,z) AU3(x,y,z) + #define initAU4(x,y,z,w) AU4(x,y,z,w) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//============================================================================================================================== + #define AAbsD1(a) abs(AD1(a)) + #define AAbsF1(a) abs(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ACosD1(a) cos(AD1(a)) + #define ACosF1(a) cos(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ADotD2(a,b) dot(AD2(a),AD2(b)) + #define ADotD3(a,b) dot(AD3(a),AD3(b)) + #define ADotD4(a,b) dot(AD4(a),AD4(b)) + #define ADotF2(a,b) dot(AF2(a),AF2(b)) + #define ADotF3(a,b) dot(AF3(a),AF3(b)) + #define ADotF4(a,b) dot(AF4(a),AF4(b)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AExp2D1(a) exp2(AD1(a)) + #define AExp2F1(a) exp2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AFloorD1(a) floor(AD1(a)) + #define AFloorF1(a) floor(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ALog2D1(a) log2(AD1(a)) + #define ALog2F1(a) log2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMaxD1(a,b) max(a,b) + #define AMaxF1(a,b) max(a,b) + #define AMaxL1(a,b) max(a,b) + #define AMaxU1(a,b) max(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMinD1(a,b) min(a,b) + #define AMinF1(a,b) min(a,b) + #define AMinL1(a,b) min(a,b) + #define AMinU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASinD1(a) sin(AD1(a)) + #define ASinF1(a) sin(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASqrtD1(a) sqrt(AD1(a)) + #define ASqrtF1(a) sqrt(AF1(a)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + #define APowD1(a,b) pow(AD1(a),AF1(b)) + #define APowF1(a,b) pow(AF1(a),AF1(b)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + #ifdef A_DUBL + AD2 opAAbsD2(outAD2 d,inAD2 a){d=abs(a);return d;} + AD3 opAAbsD3(outAD3 d,inAD3 a){d=abs(a);return d;} + AD4 opAAbsD4(outAD4 d,inAD4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d=a+b;return d;} + AD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d=a+b;return d;} + AD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d=a+AD2_(b);return d;} + AD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d=a+AD3_(b);return d;} + AD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d=a+AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opACpyD2(outAD2 d,inAD2 a){d=a;return d;} + AD3 opACpyD3(outAD3 d,inAD3 a){d=a;return d;} + AD4 opACpyD4(outAD4 d,inAD4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d=ALerpD2(a,b,c);return d;} + AD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d=ALerpD3(a,b,c);return d;} + AD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d=ALerpD4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d=ALerpD2(a,b,AD2_(c));return d;} + AD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d=ALerpD3(a,b,AD3_(c));return d;} + AD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d=ALerpD4(a,b,AD4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d=max(a,b);return d;} + AD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d=max(a,b);return d;} + AD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d=min(a,b);return d;} + AD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d=min(a,b);return d;} + AD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d=a*b;return d;} + AD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d=a*b;return d;} + AD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d=a*AD2_(b);return d;} + AD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d=a*AD3_(b);return d;} + AD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d=a*AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opANegD2(outAD2 d,inAD2 a){d=-a;return d;} + AD3 opANegD3(outAD3 d,inAD3 a){d=-a;return d;} + AD4 opANegD4(outAD4 d,inAD4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opARcpD2(outAD2 d,inAD2 a){d=ARcpD2(a);return d;} + AD3 opARcpD3(outAD3 d,inAD3 a){d=ARcpD3(a);return d;} + AD4 opARcpD4(outAD4 d,inAD4 a){d=ARcpD4(a);return d;} + #endif +//============================================================================================================================== + AF2 opAAbsF2(outAF2 d,inAF2 a){d=abs(a);return d;} + AF3 opAAbsF3(outAF3 d,inAF3 a){d=abs(a);return d;} + AF4 opAAbsF4(outAF4 d,inAF4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d=a+b;return d;} + AF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d=a+b;return d;} + AF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d=a+AF2_(b);return d;} + AF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d=a+AF3_(b);return d;} + AF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d=a+AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opACpyF2(outAF2 d,inAF2 a){d=a;return d;} + AF3 opACpyF3(outAF3 d,inAF3 a){d=a;return d;} + AF4 opACpyF4(outAF4 d,inAF4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d=ALerpF2(a,b,c);return d;} + AF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d=ALerpF3(a,b,c);return d;} + AF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d=ALerpF4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d=ALerpF2(a,b,AF2_(c));return d;} + AF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d=ALerpF3(a,b,AF3_(c));return d;} + AF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d=ALerpF4(a,b,AF4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d=max(a,b);return d;} + AF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d=max(a,b);return d;} + AF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d=min(a,b);return d;} + AF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d=min(a,b);return d;} + AF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d=a*b;return d;} + AF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d=a*b;return d;} + AF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d=a*AF2_(b);return d;} + AF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d=a*AF3_(b);return d;} + AF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d=a*AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opANegF2(outAF2 d,inAF2 a){d=-a;return d;} + AF3 opANegF3(outAF3 d,inAF3 a){d=-a;return d;} + AF4 opANegF4(outAF4 d,inAF4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opARcpF2(outAF2 d,inAF2 a){d=ARcpF2(a);return d;} + AF3 opARcpF3(outAF3 d,inAF3 a){d=ARcpF3(a);return d;} + AF4 opARcpF4(outAF4 d,inAF4 a){d=ARcpF4(a);return d;} +#endif diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h b/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h new file mode 100644 index 000000000..4e0b3d548 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h @@ -0,0 +1,1199 @@ +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// AMD FidelityFX SUPER RESOLUTION [FSR 1] ::: SPATIAL SCALING & EXTRAS - v1.20210629 +// +// +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// FSR is a collection of algorithms relating to generating a higher resolution image. +// This specific header focuses on single-image non-temporal image scaling, and related tools. +// +// The core functions are EASU and RCAS: +// [EASU] Edge Adaptive Spatial Upsampling ....... 1x to 4x area range spatial scaling, clamped adaptive elliptical filter. +// [RCAS] Robust Contrast Adaptive Sharpening .... A non-scaling variation on CAS. +// RCAS needs to be applied after EASU as a separate pass. +// +// Optional utility functions are: +// [LFGA] Linear Film Grain Applicator ........... Tool to apply film grain after scaling. +// [SRTM] Simple Reversible Tone-Mapper .......... Linear HDR {0 to FP16_MAX} to {0 to 1} and back. +// [TEPD] Temporal Energy Preserving Dither ...... Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// See each individual sub-section for inline documentation. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FUNCTION PERMUTATIONS +// ===================== +// *F() ..... Single item computation with 32-bit. +// *H() ..... Single item computation with 16-bit, with packing (aka two 16-bit ops in parallel) when possible. +// *Hx2() ... Processing two items in parallel with 16-bit, easier packing. +// Not all interfaces in this file have a *Hx2() form. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING +// +//------------------------------------------------------------------------------------------------------------------------------ +// EASU provides a high quality spatial-only scaling at relatively low cost. +// Meaning EASU is appropiate for laptops and other low-end GPUs. +// Quality from 1x to 4x area scaling is good. +//------------------------------------------------------------------------------------------------------------------------------ +// The scalar uses a modified fast approximation to the standard lanczos(size=2) kernel. +// EASU runs in a single pass, so it applies a directionally and anisotropically adaptive radial lanczos. +// This is also kept as simple as possible to have minimum runtime. +//------------------------------------------------------------------------------------------------------------------------------ +// The lanzcos filter has negative lobes, so by itself it will introduce ringing. +// To remove all ringing, the algorithm uses the nearest 2x2 input texels as a neighborhood, +// and limits output to the minimum and maximum of that neighborhood. +//------------------------------------------------------------------------------------------------------------------------------ +// Input image requirements: +// +// Color needs to be encoded as 3 channel[red, green, blue](e.g.XYZ not supported) +// Each channel needs to be in the range[0, 1] +// Any color primaries are supported +// Display / tonemapping curve needs to be as if presenting to sRGB display or similar(e.g.Gamma 2.0) +// There should be no banding in the input +// There should be no high amplitude noise in the input +// There should be no noise in the input that is not at input pixel granularity +// For performance purposes, use 32bpp formats +//------------------------------------------------------------------------------------------------------------------------------ +// Best to apply EASU at the end of the frame after tonemapping +// but before film grain or composite of the UI. +//------------------------------------------------------------------------------------------------------------------------------ +// Example of including this header for D3D HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan GLSL : +// +// #define A_GPU 1 +// #define A_GLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HLSL_6_2 1 +// #define A_NO_16_BIT_CAST 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of declaring the required input callbacks for GLSL : +// The callbacks need to gather4 for each color channel using the specified texture coordinate 'p'. +// EASU uses gather4 to reduce position computation logic and for free Arrays of Structures to Structures of Arrays conversion. +// +// AH4 FsrEasuRH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,0));} +// AH4 FsrEasuGH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,1));} +// AH4 FsrEasuBH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,2));} +// ... +// The FsrEasuCon function needs to be called from the CPU or GPU to set up constants. +// The difference in viewport and input image size is there to support Dynamic Resolution Scaling. +// To use FsrEasuCon() on the CPU, define A_CPU before including ffx_a and ffx_fsr1. +// Including a GPU example here, the 'con0' through 'con3' values would be stored out to a constant buffer. +// AU4 con0,con1,con2,con3; +// FsrEasuCon(con0,con1,con2,con3, +// 1920.0,1080.0, // Viewport size (top left aligned) in the input image which is to be scaled. +// 3840.0,2160.0, // The size of the input image. +// 2560.0,1440.0); // The output resolution. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrEasuCon( +outAU4 con0, +outAU4 con1, +outAU4 con2, +outAU4 con3, +// This the rendered image resolution being upscaled +AF1 inputViewportInPixelsX, +AF1 inputViewportInPixelsY, +// This is the resolution of the resource containing the input image (useful for dynamic resolution) +AF1 inputSizeInPixelsX, +AF1 inputSizeInPixelsY, +// This is the display resolution which the input image gets upscaled to +AF1 outputSizeInPixelsX, +AF1 outputSizeInPixelsY){ + // Output integer position to a pixel position in viewport. + con0[0]=AU1_AF1(inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)); + con0[1]=AU1_AF1(inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)); + con0[2]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)-AF1_(0.5)); + con0[3]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)-AF1_(0.5)); + // Viewport pixel position to normalized image space. + // This is used to get upper-left of 'F' tap. + con1[0]=AU1_AF1(ARcpF1(inputSizeInPixelsX)); + con1[1]=AU1_AF1(ARcpF1(inputSizeInPixelsY)); + // Centers of gather4, first offset from upper-left of 'F'. + // +---+---+ + // | | | + // +--(0)--+ + // | b | c | + // +---F---+---+---+ + // | e | f | g | h | + // +--(1)--+--(2)--+ + // | i | j | k | l | + // +---+---+---+---+ + // | n | o | + // +--(3)--+ + // | | | + // +---+---+ + con1[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con1[3]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsY)); + // These are from (0) instead of 'F'. + con2[0]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsX)); + con2[1]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con2[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con2[3]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con3[0]=AU1_AF1(AF1_( 0.0)*ARcpF1(inputSizeInPixelsX)); + con3[1]=AU1_AF1(AF1_( 4.0)*ARcpF1(inputSizeInPixelsY)); + con3[2]=con3[3]=0;} + +//If the an offset into the input image resource +A_STATIC void FsrEasuConOffset( + outAU4 con0, + outAU4 con1, + outAU4 con2, + outAU4 con3, + // This the rendered image resolution being upscaled + AF1 inputViewportInPixelsX, + AF1 inputViewportInPixelsY, + // This is the resolution of the resource containing the input image (useful for dynamic resolution) + AF1 inputSizeInPixelsX, + AF1 inputSizeInPixelsY, + // This is the display resolution which the input image gets upscaled to + AF1 outputSizeInPixelsX, + AF1 outputSizeInPixelsY, + // This is the input image offset into the resource containing it (useful for dynamic resolution) + AF1 inputOffsetInPixelsX, + AF1 inputOffsetInPixelsY) { + FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY, inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY); + con0[2] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsX * ARcpF1(outputSizeInPixelsX) - AF1_(0.5) + inputOffsetInPixelsX); + con0[3] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsY * ARcpF1(outputSizeInPixelsY) - AF1_(0.5) + inputOffsetInPixelsY); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_EASU_F) + // Input callback prototypes, need to be implemented by calling shader + AF4 FsrEasuRF(AF2 p); + AF4 FsrEasuGF(AF2 p); + AF4 FsrEasuBF(AF2 p); +//------------------------------------------------------------------------------------------------------------------------------ + // Filtering for a given tap for the scalar. + void FsrEasuTapF( + inout AF3 aC, // Accumulated color, with negative lobe. + inout AF1 aW, // Accumulated weight. + AF2 off, // Pixel offset from resolve position to tap. + AF2 dir, // Gradient direction. + AF2 len, // Length. + AF1 lob, // Negative lobe strength. + AF1 clp, // Clipping point. + AF3 c){ // Tap color. + // Rotate offset by direction. + AF2 v; + v.x=(off.x*( dir.x))+(off.y*dir.y); + v.y=(off.x*(-dir.y))+(off.y*dir.x); + // Anisotropy. + v*=len; + // Compute distance^2. + AF1 d2=v.x*v.x+v.y*v.y; + // Limit to the window as at corner, 2 taps can easily be outside. + d2=min(d2,clp); + // Approximation of lancos2 without sin() or rcp(), or sqrt() to get x. + // (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2 + // |_______________________________________| |_______________| + // base window + // The general form of the 'base' is, + // (a*(b*x^2-1)^2-(a-1)) + // Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe. + AF1 wB=AF1_(2.0/5.0)*d2+AF1_(-1.0); + AF1 wA=lob*d2+AF1_(-1.0); + wB*=wB; + wA*=wA; + wB=AF1_(25.0/16.0)*wB+AF1_(-(25.0/16.0-1.0)); + AF1 w=wB*wA; + // Do weighted average. + aC+=c*w;aW+=w;} +//------------------------------------------------------------------------------------------------------------------------------ + // Accumulate direction and length. + void FsrEasuSetF( + inout AF2 dir, + inout AF1 len, + AF2 pp, + AP1 biS,AP1 biT,AP1 biU,AP1 biV, + AF1 lA,AF1 lB,AF1 lC,AF1 lD,AF1 lE){ + // Compute bilinear weight, branches factor out as predicates are compiler time immediates. + // s t + // u v + AF1 w = AF1_(0.0); + if(biS)w=(AF1_(1.0)-pp.x)*(AF1_(1.0)-pp.y); + if(biT)w= pp.x *(AF1_(1.0)-pp.y); + if(biU)w=(AF1_(1.0)-pp.x)* pp.y ; + if(biV)w= pp.x * pp.y ; + // Direction is the '+' diff. + // a + // b c d + // e + // Then takes magnitude from abs average of both sides of 'c'. + // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms. + AF1 dc=lD-lC; + AF1 cb=lC-lB; + AF1 lenX=max(abs(dc),abs(cb)); + lenX=APrxLoRcpF1(lenX); + AF1 dirX=lD-lB; + dir.x+=dirX*w; + lenX=ASatF1(abs(dirX)*lenX); + lenX*=lenX; + len+=lenX*w; + // Repeat for the y axis. + AF1 ec=lE-lC; + AF1 ca=lC-lA; + AF1 lenY=max(abs(ec),abs(ca)); + lenY=APrxLoRcpF1(lenY); + AF1 dirY=lE-lA; + dir.y+=dirY*w; + lenY=ASatF1(abs(dirY)*lenY); + lenY*=lenY; + len+=lenY*w;} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrEasuF( + out AF3 pix, + AU2 ip, // Integer pixel position in output. + AU4 con0, // Constants generated by FsrEasuCon(). + AU4 con1, + AU4 con2, + AU4 con3){ +//------------------------------------------------------------------------------------------------------------------------------ + // Get position of 'f'. + AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw); + AF2 fp=floor(pp); + pp-=fp; +//------------------------------------------------------------------------------------------------------------------------------ + // 12-tap kernel. + // b c + // e f g h + // i j k l + // n o + // Gather 4 ordering. + // a b + // r g + // For packed FP16, need either {rg} or {ab} so using the following setup for gather in all versions, + // a b <- unused (z) + // r g + // a b a b + // r g r g + // a b + // r g <- unused (z) + // Allowing dead-code removal to remove the 'z's. + AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw); + // These are from p0 to avoid pulling two constants on pre-Navi hardware. + AF2 p1=p0+AF2_AU2(con2.xy); + AF2 p2=p0+AF2_AU2(con2.zw); + AF2 p3=p0+AF2_AU2(con3.xy); + AF4 bczzR=FsrEasuRF(p0); + AF4 bczzG=FsrEasuGF(p0); + AF4 bczzB=FsrEasuBF(p0); + AF4 ijfeR=FsrEasuRF(p1); + AF4 ijfeG=FsrEasuGF(p1); + AF4 ijfeB=FsrEasuBF(p1); + AF4 klhgR=FsrEasuRF(p2); + AF4 klhgG=FsrEasuGF(p2); + AF4 klhgB=FsrEasuBF(p2); + AF4 zzonR=FsrEasuRF(p3); + AF4 zzonG=FsrEasuGF(p3); + AF4 zzonB=FsrEasuBF(p3); +//------------------------------------------------------------------------------------------------------------------------------ + // Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD). + AF4 bczzL=bczzB*AF4_(0.5)+(bczzR*AF4_(0.5)+bczzG); + AF4 ijfeL=ijfeB*AF4_(0.5)+(ijfeR*AF4_(0.5)+ijfeG); + AF4 klhgL=klhgB*AF4_(0.5)+(klhgR*AF4_(0.5)+klhgG); + AF4 zzonL=zzonB*AF4_(0.5)+(zzonR*AF4_(0.5)+zzonG); + // Rename. + AF1 bL=bczzL.x; + AF1 cL=bczzL.y; + AF1 iL=ijfeL.x; + AF1 jL=ijfeL.y; + AF1 fL=ijfeL.z; + AF1 eL=ijfeL.w; + AF1 kL=klhgL.x; + AF1 lL=klhgL.y; + AF1 hL=klhgL.z; + AF1 gL=klhgL.w; + AF1 oL=zzonL.z; + AF1 nL=zzonL.w; + // Accumulate for bilinear interpolation. + AF2 dir=AF2_(0.0); + AF1 len=AF1_(0.0); + FsrEasuSetF(dir,len,pp,true, false,false,false,bL,eL,fL,gL,jL); + FsrEasuSetF(dir,len,pp,false,true ,false,false,cL,fL,gL,hL,kL); + FsrEasuSetF(dir,len,pp,false,false,true ,false,fL,iL,jL,kL,nL); + FsrEasuSetF(dir,len,pp,false,false,false,true ,gL,jL,kL,lL,oL); +//------------------------------------------------------------------------------------------------------------------------------ + // Normalize with approximation, and cleanup close to zero. + AF2 dir2=dir*dir; + AF1 dirR=dir2.x+dir2.y; + AP1 zro=dirR<AF1_(1.0/32768.0); + dirR=APrxLoRsqF1(dirR); + dirR=zro?AF1_(1.0):dirR; + dir.x=zro?AF1_(1.0):dir.x; + dir*=AF2_(dirR); + // Transform from {0 to 2} to {0 to 1} range, and shape with square. + len=len*AF1_(0.5); + len*=len; + // Stretch kernel {1.0 vert|horz, to sqrt(2.0) on diagonal}. + AF1 stretch=(dir.x*dir.x+dir.y*dir.y)*APrxLoRcpF1(max(abs(dir.x),abs(dir.y))); + // Anisotropic length after rotation, + // x := 1.0 lerp to 'stretch' on edges + // y := 1.0 lerp to 2x on edges + AF2 len2=AF2(AF1_(1.0)+(stretch-AF1_(1.0))*len,AF1_(1.0)+AF1_(-0.5)*len); + // Based on the amount of 'edge', + // the window shifts from +/-{sqrt(2.0) to slightly beyond 2.0}. + AF1 lob=AF1_(0.5)+AF1_((1.0/4.0-0.04)-0.5)*len; + // Set distance^2 clipping point to the end of the adjustable window. + AF1 clp=APrxLoRcpF1(lob); +//------------------------------------------------------------------------------------------------------------------------------ + // Accumulation mixed with min/max of 4 nearest. + // b c + // e f g h + // i j k l + // n o + AF3 min4=min(AMin3F3(AF3(ijfeR.z,ijfeG.z,ijfeB.z),AF3(klhgR.w,klhgG.w,klhgB.w),AF3(ijfeR.y,ijfeG.y,ijfeB.y)), + AF3(klhgR.x,klhgG.x,klhgB.x)); + AF3 max4=max(AMax3F3(AF3(ijfeR.z,ijfeG.z,ijfeB.z),AF3(klhgR.w,klhgG.w,klhgB.w),AF3(ijfeR.y,ijfeG.y,ijfeB.y)), + AF3(klhgR.x,klhgG.x,klhgB.x)); + // Accumulation. + AF3 aC=AF3_(0.0); + AF1 aW=AF1_(0.0); + FsrEasuTapF(aC,aW,AF2( 0.0,-1.0)-pp,dir,len2,lob,clp,AF3(bczzR.x,bczzG.x,bczzB.x)); // b + FsrEasuTapF(aC,aW,AF2( 1.0,-1.0)-pp,dir,len2,lob,clp,AF3(bczzR.y,bczzG.y,bczzB.y)); // c + FsrEasuTapF(aC,aW,AF2(-1.0, 1.0)-pp,dir,len2,lob,clp,AF3(ijfeR.x,ijfeG.x,ijfeB.x)); // i + FsrEasuTapF(aC,aW,AF2( 0.0, 1.0)-pp,dir,len2,lob,clp,AF3(ijfeR.y,ijfeG.y,ijfeB.y)); // j + FsrEasuTapF(aC,aW,AF2( 0.0, 0.0)-pp,dir,len2,lob,clp,AF3(ijfeR.z,ijfeG.z,ijfeB.z)); // f + FsrEasuTapF(aC,aW,AF2(-1.0, 0.0)-pp,dir,len2,lob,clp,AF3(ijfeR.w,ijfeG.w,ijfeB.w)); // e + FsrEasuTapF(aC,aW,AF2( 1.0, 1.0)-pp,dir,len2,lob,clp,AF3(klhgR.x,klhgG.x,klhgB.x)); // k + FsrEasuTapF(aC,aW,AF2( 2.0, 1.0)-pp,dir,len2,lob,clp,AF3(klhgR.y,klhgG.y,klhgB.y)); // l + FsrEasuTapF(aC,aW,AF2( 2.0, 0.0)-pp,dir,len2,lob,clp,AF3(klhgR.z,klhgG.z,klhgB.z)); // h + FsrEasuTapF(aC,aW,AF2( 1.0, 0.0)-pp,dir,len2,lob,clp,AF3(klhgR.w,klhgG.w,klhgB.w)); // g + FsrEasuTapF(aC,aW,AF2( 1.0, 2.0)-pp,dir,len2,lob,clp,AF3(zzonR.z,zzonG.z,zzonB.z)); // o + FsrEasuTapF(aC,aW,AF2( 0.0, 2.0)-pp,dir,len2,lob,clp,AF3(zzonR.w,zzonG.w,zzonB.w)); // n +//------------------------------------------------------------------------------------------------------------------------------ + // Normalize and dering. + pix=min(max4,max(min4,aC*AF3_(ARcpF1(aW))));} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_EASU_H) +// Input callback prototypes, need to be implemented by calling shader + AH4 FsrEasuRH(AF2 p); + AH4 FsrEasuGH(AF2 p); + AH4 FsrEasuBH(AF2 p); +//------------------------------------------------------------------------------------------------------------------------------ + // This runs 2 taps in parallel. + void FsrEasuTapH( + inout AH2 aCR,inout AH2 aCG,inout AH2 aCB, + inout AH2 aW, + AH2 offX,AH2 offY, + AH2 dir, + AH2 len, + AH1 lob, + AH1 clp, + AH2 cR,AH2 cG,AH2 cB){ + AH2 vX,vY; + vX=offX* dir.xx +offY*dir.yy; + vY=offX*(-dir.yy)+offY*dir.xx; + vX*=len.x;vY*=len.y; + AH2 d2=vX*vX+vY*vY; + d2=min(d2,AH2_(clp)); + AH2 wB=AH2_(2.0/5.0)*d2+AH2_(-1.0); + AH2 wA=AH2_(lob)*d2+AH2_(-1.0); + wB*=wB; + wA*=wA; + wB=AH2_(25.0/16.0)*wB+AH2_(-(25.0/16.0-1.0)); + AH2 w=wB*wA; + aCR+=cR*w;aCG+=cG*w;aCB+=cB*w;aW+=w;} +//------------------------------------------------------------------------------------------------------------------------------ + // This runs 2 taps in parallel. + void FsrEasuSetH( + inout AH2 dirPX,inout AH2 dirPY, + inout AH2 lenP, + AH2 pp, + AP1 biST,AP1 biUV, + AH2 lA,AH2 lB,AH2 lC,AH2 lD,AH2 lE){ + AH2 w = AH2_(0.0); + if(biST)w=(AH2(1.0,0.0)+AH2(-pp.x,pp.x))*AH2_(AH1_(1.0)-pp.y); + if(biUV)w=(AH2(1.0,0.0)+AH2(-pp.x,pp.x))*AH2_( pp.y); + // ABS is not free in the packed FP16 path. + AH2 dc=lD-lC; + AH2 cb=lC-lB; + AH2 lenX=max(abs(dc),abs(cb)); + lenX=ARcpH2(lenX); + AH2 dirX=lD-lB; + dirPX+=dirX*w; + lenX=ASatH2(abs(dirX)*lenX); + lenX*=lenX; + lenP+=lenX*w; + AH2 ec=lE-lC; + AH2 ca=lC-lA; + AH2 lenY=max(abs(ec),abs(ca)); + lenY=ARcpH2(lenY); + AH2 dirY=lE-lA; + dirPY+=dirY*w; + lenY=ASatH2(abs(dirY)*lenY); + lenY*=lenY; + lenP+=lenY*w;} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrEasuH( + out AH3 pix, + AU2 ip, + AU4 con0, + AU4 con1, + AU4 con2, + AU4 con3){ +//------------------------------------------------------------------------------------------------------------------------------ + AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw); + AF2 fp=floor(pp); + pp-=fp; + AH2 ppp=AH2(pp); +//------------------------------------------------------------------------------------------------------------------------------ + AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw); + AF2 p1=p0+AF2_AU2(con2.xy); + AF2 p2=p0+AF2_AU2(con2.zw); + AF2 p3=p0+AF2_AU2(con3.xy); + AH4 bczzR=FsrEasuRH(p0); + AH4 bczzG=FsrEasuGH(p0); + AH4 bczzB=FsrEasuBH(p0); + AH4 ijfeR=FsrEasuRH(p1); + AH4 ijfeG=FsrEasuGH(p1); + AH4 ijfeB=FsrEasuBH(p1); + AH4 klhgR=FsrEasuRH(p2); + AH4 klhgG=FsrEasuGH(p2); + AH4 klhgB=FsrEasuBH(p2); + AH4 zzonR=FsrEasuRH(p3); + AH4 zzonG=FsrEasuGH(p3); + AH4 zzonB=FsrEasuBH(p3); +//------------------------------------------------------------------------------------------------------------------------------ + AH4 bczzL=bczzB*AH4_(0.5)+(bczzR*AH4_(0.5)+bczzG); + AH4 ijfeL=ijfeB*AH4_(0.5)+(ijfeR*AH4_(0.5)+ijfeG); + AH4 klhgL=klhgB*AH4_(0.5)+(klhgR*AH4_(0.5)+klhgG); + AH4 zzonL=zzonB*AH4_(0.5)+(zzonR*AH4_(0.5)+zzonG); + AH1 bL=bczzL.x; + AH1 cL=bczzL.y; + AH1 iL=ijfeL.x; + AH1 jL=ijfeL.y; + AH1 fL=ijfeL.z; + AH1 eL=ijfeL.w; + AH1 kL=klhgL.x; + AH1 lL=klhgL.y; + AH1 hL=klhgL.z; + AH1 gL=klhgL.w; + AH1 oL=zzonL.z; + AH1 nL=zzonL.w; + // This part is different, accumulating 2 taps in parallel. + AH2 dirPX=AH2_(0.0); + AH2 dirPY=AH2_(0.0); + AH2 lenP=AH2_(0.0); + FsrEasuSetH(dirPX,dirPY,lenP,ppp,true, false,AH2(bL,cL),AH2(eL,fL),AH2(fL,gL),AH2(gL,hL),AH2(jL,kL)); + FsrEasuSetH(dirPX,dirPY,lenP,ppp,false,true ,AH2(fL,gL),AH2(iL,jL),AH2(jL,kL),AH2(kL,lL),AH2(nL,oL)); + AH2 dir=AH2(dirPX.r+dirPX.g,dirPY.r+dirPY.g); + AH1 len=lenP.r+lenP.g; +//------------------------------------------------------------------------------------------------------------------------------ + AH2 dir2=dir*dir; + AH1 dirR=dir2.x+dir2.y; + AP1 zro=dirR<AH1_(1.0/32768.0); + dirR=APrxLoRsqH1(dirR); + dirR=zro?AH1_(1.0):dirR; + dir.x=zro?AH1_(1.0):dir.x; + dir*=AH2_(dirR); + len=len*AH1_(0.5); + len*=len; + AH1 stretch=(dir.x*dir.x+dir.y*dir.y)*APrxLoRcpH1(max(abs(dir.x),abs(dir.y))); + AH2 len2=AH2(AH1_(1.0)+(stretch-AH1_(1.0))*len,AH1_(1.0)+AH1_(-0.5)*len); + AH1 lob=AH1_(0.5)+AH1_((1.0/4.0-0.04)-0.5)*len; + AH1 clp=APrxLoRcpH1(lob); +//------------------------------------------------------------------------------------------------------------------------------ + // FP16 is different, using packed trick to do min and max in same operation. + AH2 bothR=max(max(AH2(-ijfeR.z,ijfeR.z),AH2(-klhgR.w,klhgR.w)),max(AH2(-ijfeR.y,ijfeR.y),AH2(-klhgR.x,klhgR.x))); + AH2 bothG=max(max(AH2(-ijfeG.z,ijfeG.z),AH2(-klhgG.w,klhgG.w)),max(AH2(-ijfeG.y,ijfeG.y),AH2(-klhgG.x,klhgG.x))); + AH2 bothB=max(max(AH2(-ijfeB.z,ijfeB.z),AH2(-klhgB.w,klhgB.w)),max(AH2(-ijfeB.y,ijfeB.y),AH2(-klhgB.x,klhgB.x))); + // This part is different for FP16, working pairs of taps at a time. + AH2 pR=AH2_(0.0); + AH2 pG=AH2_(0.0); + AH2 pB=AH2_(0.0); + AH2 pW=AH2_(0.0); + FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0, 1.0)-ppp.xx,AH2(-1.0,-1.0)-ppp.yy,dir,len2,lob,clp,bczzR.xy,bczzG.xy,bczzB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2(-1.0, 0.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,ijfeR.xy,ijfeG.xy,ijfeB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0,-1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,ijfeR.zw,ijfeG.zw,ijfeB.zw); + FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 2.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,klhgR.xy,klhgG.xy,klhgB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2( 2.0, 1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,klhgR.zw,klhgG.zw,klhgB.zw); + FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 0.0)-ppp.xx,AH2( 2.0, 2.0)-ppp.yy,dir,len2,lob,clp,zzonR.zw,zzonG.zw,zzonB.zw); + AH3 aC=AH3(pR.x+pR.y,pG.x+pG.y,pB.x+pB.y); + AH1 aW=pW.x+pW.y; +//------------------------------------------------------------------------------------------------------------------------------ + // Slightly different for FP16 version due to combined min and max. + pix=min(AH3(bothR.y,bothG.y,bothB.y),max(-AH3(bothR.x,bothG.x,bothB.x),aC*AH3_(ARcpH1(aW))));} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING +// +//------------------------------------------------------------------------------------------------------------------------------ +// CAS uses a simplified mechanism to convert local contrast into a variable amount of sharpness. +// RCAS uses a more exact mechanism, solving for the maximum local sharpness possible before clipping. +// RCAS also has a built in process to limit sharpening of what it detects as possible noise. +// RCAS sharper does not support scaling, as it should be applied after EASU scaling. +// Pass EASU output straight into RCAS, no color conversions necessary. +//------------------------------------------------------------------------------------------------------------------------------ +// RCAS is based on the following logic. +// RCAS uses a 5 tap filter in a cross pattern (same as CAS), +// w n +// w 1 w for taps w m e +// w s +// Where 'w' is the negative lobe weight. +// output = (w*(n+e+w+s)+m)/(4*w+1) +// RCAS solves for 'w' by seeing where the signal might clip out of the {0 to 1} input range, +// 0 == (w*(n+e+w+s)+m)/(4*w+1) -> w = -m/(n+e+w+s) +// 1 == (w*(n+e+w+s)+m)/(4*w+1) -> w = (1-m)/(n+e+w+s-4*1) +// Then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. +// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. +// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps. +// As well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation. +// This stabilizes RCAS. +// RCAS does a simple highpass which is normalized against the local contrast then shaped, +// 0.25 +// 0.25 -1 0.25 +// 0.25 +// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges. +// +// GLSL example for the required callbacks : +// +// AH4 FsrRcasLoadH(ASW2 p){return AH4(imageLoad(imgSrc,ASU2(p)));} +// void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b) +// { +// //do any simple input color conversions here or leave empty if none needed +// } +// +// FsrRcasCon need to be called from the CPU or GPU to set up constants. +// Including a GPU example here, the 'con' value would be stored out to a constant buffer. +// +// AU4 con; +// FsrRcasCon(con, +// 0.0); // The scale is {0.0 := maximum sharpness, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +// --------------- +// RCAS sharpening supports a CAS-like pass-through alpha via, +// #define FSR_RCAS_PASSTHROUGH_ALPHA 1 +// RCAS also supports a define to enable a more expensive path to avoid some sharpening of noise. +// Would suggest it is better to apply film grain after RCAS sharpening (and after scaling) instead of using this define, +// #define FSR_RCAS_DENOISE 1 +//============================================================================================================================== +// This is set at the limit of providing unnatural results for sharpening. +#define FSR_RCAS_LIMIT (0.25-(1.0/16.0)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrRcasCon( +outAU4 con, +// The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +AF1 sharpness){ + // Transform from stops to linear value. + sharpness=AExp2F1(-sharpness); + varAF2(hSharp)=initAF2(sharpness,sharpness); + con[0]=AU1_AF1(sharpness); + con[1]=AU1_AH2_AF2(hSharp); + con[2]=0; + con[3]=0;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_RCAS_F) + // Input callback prototypes that need to be implemented by calling shader + AF4 FsrRcasLoadF(ASU2 p); + void FsrRcasInputF(inout AF1 r,inout AF1 g,inout AF1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasF( + out AF1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AF1 pixG, + out AF1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AF1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASU2 sp=ASU2(ip); + AF3 b=FsrRcasLoadF(sp+ASU2( 0,-1)).rgb; + AF3 d=FsrRcasLoadF(sp+ASU2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AF4 ee=FsrRcasLoadF(sp); + AF3 e=ee.rgb;pixA=ee.a; + #else + AF3 e=FsrRcasLoadF(sp).rgb; + #endif + AF3 f=FsrRcasLoadF(sp+ASU2( 1, 0)).rgb; + AF3 h=FsrRcasLoadF(sp+ASU2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AF1 bR=b.r; + AF1 bG=b.g; + AF1 bB=b.b; + AF1 dR=d.r; + AF1 dG=d.g; + AF1 dB=d.b; + AF1 eR=e.r; + AF1 eG=e.g; + AF1 eB=e.b; + AF1 fR=f.r; + AF1 fG=f.g; + AF1 fB=f.b; + AF1 hR=h.r; + AF1 hG=h.g; + AF1 hB=h.b; + // Run optional input transform. + FsrRcasInputF(bR,bG,bB); + FsrRcasInputF(dR,dG,dB); + FsrRcasInputF(eR,eG,eB); + FsrRcasInputF(fR,fG,fB); + FsrRcasInputF(hR,hG,hB); + // Luma times 2. + AF1 bL=bB*AF1_(0.5)+(bR*AF1_(0.5)+bG); + AF1 dL=dB*AF1_(0.5)+(dR*AF1_(0.5)+dG); + AF1 eL=eB*AF1_(0.5)+(eR*AF1_(0.5)+eG); + AF1 fL=fB*AF1_(0.5)+(fR*AF1_(0.5)+fG); + AF1 hL=hB*AF1_(0.5)+(hR*AF1_(0.5)+hG); + // Noise detection. + AF1 nz=AF1_(0.25)*bL+AF1_(0.25)*dL+AF1_(0.25)*fL+AF1_(0.25)*hL-eL; + nz=ASatF1(abs(nz)*APrxMedRcpF1(AMax3F1(AMax3F1(bL,dL,eL),fL,hL)-AMin3F1(AMin3F1(bL,dL,eL),fL,hL))); + nz=AF1_(-0.5)*nz+AF1_(1.0); + // Min and max of ring. + AF1 mn4R=min(AMin3F1(bR,dR,fR),hR); + AF1 mn4G=min(AMin3F1(bG,dG,fG),hG); + AF1 mn4B=min(AMin3F1(bB,dB,fB),hB); + AF1 mx4R=max(AMax3F1(bR,dR,fR),hR); + AF1 mx4G=max(AMax3F1(bG,dG,fG),hG); + AF1 mx4B=max(AMax3F1(bB,dB,fB),hB); + // Immediate constants for peak range. + AF2 peakC=AF2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R); + AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G); + AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B); + AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y); + AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y); + AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y); + AF1 lobeR=max(-hitMinR,hitMaxR); + AF1 lobeG=max(-hitMinG,hitMaxG); + AF1 lobeB=max(-hitMinB,hitMaxB); + AF1 lobe=max(AF1_(-FSR_RCAS_LIMIT),min(AMax3F1(lobeR,lobeG,lobeB),AF1_(0.0)))*AF1_AU1(con.x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AF1 rcpL=APrxMedRcpF1(AF1_(4.0)*lobe+AF1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL; + return;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_H) + // Input callback prototypes that need to be implemented by calling shader + AH4 FsrRcasLoadH(ASW2 p); + void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasH( + out AH1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AH1 pixG, + out AH1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Sharpening algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASW2 sp=ASW2(ip); + AH3 b=FsrRcasLoadH(sp+ASW2( 0,-1)).rgb; + AH3 d=FsrRcasLoadH(sp+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee=FsrRcasLoadH(sp); + AH3 e=ee.rgb;pixA=ee.a; + #else + AH3 e=FsrRcasLoadH(sp).rgb; + #endif + AH3 f=FsrRcasLoadH(sp+ASW2( 1, 0)).rgb; + AH3 h=FsrRcasLoadH(sp+ASW2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AH1 bR=b.r; + AH1 bG=b.g; + AH1 bB=b.b; + AH1 dR=d.r; + AH1 dG=d.g; + AH1 dB=d.b; + AH1 eR=e.r; + AH1 eG=e.g; + AH1 eB=e.b; + AH1 fR=f.r; + AH1 fG=f.g; + AH1 fB=f.b; + AH1 hR=h.r; + AH1 hG=h.g; + AH1 hB=h.b; + // Run optional input transform. + FsrRcasInputH(bR,bG,bB); + FsrRcasInputH(dR,dG,dB); + FsrRcasInputH(eR,eG,eB); + FsrRcasInputH(fR,fG,fB); + FsrRcasInputH(hR,hG,hB); + // Luma times 2. + AH1 bL=bB*AH1_(0.5)+(bR*AH1_(0.5)+bG); + AH1 dL=dB*AH1_(0.5)+(dR*AH1_(0.5)+dG); + AH1 eL=eB*AH1_(0.5)+(eR*AH1_(0.5)+eG); + AH1 fL=fB*AH1_(0.5)+(fR*AH1_(0.5)+fG); + AH1 hL=hB*AH1_(0.5)+(hR*AH1_(0.5)+hG); + // Noise detection. + AH1 nz=AH1_(0.25)*bL+AH1_(0.25)*dL+AH1_(0.25)*fL+AH1_(0.25)*hL-eL; + nz=ASatH1(abs(nz)*APrxMedRcpH1(AMax3H1(AMax3H1(bL,dL,eL),fL,hL)-AMin3H1(AMin3H1(bL,dL,eL),fL,hL))); + nz=AH1_(-0.5)*nz+AH1_(1.0); + // Min and max of ring. + AH1 mn4R=min(AMin3H1(bR,dR,fR),hR); + AH1 mn4G=min(AMin3H1(bG,dG,fG),hG); + AH1 mn4B=min(AMin3H1(bB,dB,fB),hB); + AH1 mx4R=max(AMax3H1(bR,dR,fR),hR); + AH1 mx4G=max(AMax3H1(bG,dG,fG),hG); + AH1 mx4B=max(AMax3H1(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R); + AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G); + AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B); + AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y); + AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y); + AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y); + AH1 lobeR=max(-hitMinR,hitMaxR); + AH1 lobeG=max(-hitMinG,hitMaxG); + AH1 lobeB=max(-hitMinB,hitMaxB); + AH1 lobe=max(AH1_(-FSR_RCAS_LIMIT),min(AMax3H1(lobeR,lobeG,lobeB),AH1_(0.0)))*AH2_AU1(con.y).x; + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH1 rcpL=APrxMedRcpH1(AH1_(4.0)*lobe+AH1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_HX2) + // Input callback prototypes that need to be implemented by the calling shader + AH4 FsrRcasLoadHx2(ASW2 p); + void FsrRcasInputHx2(inout AH2 r,inout AH2 g,inout AH2 b); +//------------------------------------------------------------------------------------------------------------------------------ + // Can be used to convert from packed Structures of Arrays to Arrays of Structures for store. + void FsrRcasDepackHx2(out AH4 pix0,out AH4 pix1,AH2 pixR,AH2 pixG,AH2 pixB){ + #ifdef A_HLSL + // Invoke a slower path for DX only, since it won't allow uninitialized values. + pix0.a=pix1.a=0.0; + #endif + pix0.rgb=AH3(pixR.x,pixG.x,pixB.x); + pix1.rgb=AH3(pixR.y,pixG.y,pixB.y);} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasHx2( + // Output values are for 2 8x8 tiles in a 16x8 region. + // pix<R,G,B>.x = left 8x8 tile + // pix<R,G,B>.y = right 8x8 tile + // This enables later processing to easily be packed as well. + out AH2 pixR, + out AH2 pixG, + out AH2 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH2 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // No scaling algorithm uses minimal 3x3 pixel neighborhood. + ASW2 sp0=ASW2(ip); + AH3 b0=FsrRcasLoadHx2(sp0+ASW2( 0,-1)).rgb; + AH3 d0=FsrRcasLoadHx2(sp0+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee0=FsrRcasLoadHx2(sp0); + AH3 e0=ee0.rgb;pixA.r=ee0.a; + #else + AH3 e0=FsrRcasLoadHx2(sp0).rgb; + #endif + AH3 f0=FsrRcasLoadHx2(sp0+ASW2( 1, 0)).rgb; + AH3 h0=FsrRcasLoadHx2(sp0+ASW2( 0, 1)).rgb; + ASW2 sp1=sp0+ASW2(8,0); + AH3 b1=FsrRcasLoadHx2(sp1+ASW2( 0,-1)).rgb; + AH3 d1=FsrRcasLoadHx2(sp1+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee1=FsrRcasLoadHx2(sp1); + AH3 e1=ee1.rgb;pixA.g=ee1.a; + #else + AH3 e1=FsrRcasLoadHx2(sp1).rgb; + #endif + AH3 f1=FsrRcasLoadHx2(sp1+ASW2( 1, 0)).rgb; + AH3 h1=FsrRcasLoadHx2(sp1+ASW2( 0, 1)).rgb; + // Arrays of Structures to Structures of Arrays conversion. + AH2 bR=AH2(b0.r,b1.r); + AH2 bG=AH2(b0.g,b1.g); + AH2 bB=AH2(b0.b,b1.b); + AH2 dR=AH2(d0.r,d1.r); + AH2 dG=AH2(d0.g,d1.g); + AH2 dB=AH2(d0.b,d1.b); + AH2 eR=AH2(e0.r,e1.r); + AH2 eG=AH2(e0.g,e1.g); + AH2 eB=AH2(e0.b,e1.b); + AH2 fR=AH2(f0.r,f1.r); + AH2 fG=AH2(f0.g,f1.g); + AH2 fB=AH2(f0.b,f1.b); + AH2 hR=AH2(h0.r,h1.r); + AH2 hG=AH2(h0.g,h1.g); + AH2 hB=AH2(h0.b,h1.b); + // Run optional input transform. + FsrRcasInputHx2(bR,bG,bB); + FsrRcasInputHx2(dR,dG,dB); + FsrRcasInputHx2(eR,eG,eB); + FsrRcasInputHx2(fR,fG,fB); + FsrRcasInputHx2(hR,hG,hB); + // Luma times 2. + AH2 bL=bB*AH2_(0.5)+(bR*AH2_(0.5)+bG); + AH2 dL=dB*AH2_(0.5)+(dR*AH2_(0.5)+dG); + AH2 eL=eB*AH2_(0.5)+(eR*AH2_(0.5)+eG); + AH2 fL=fB*AH2_(0.5)+(fR*AH2_(0.5)+fG); + AH2 hL=hB*AH2_(0.5)+(hR*AH2_(0.5)+hG); + // Noise detection. + AH2 nz=AH2_(0.25)*bL+AH2_(0.25)*dL+AH2_(0.25)*fL+AH2_(0.25)*hL-eL; + nz=ASatH2(abs(nz)*APrxMedRcpH2(AMax3H2(AMax3H2(bL,dL,eL),fL,hL)-AMin3H2(AMin3H2(bL,dL,eL),fL,hL))); + nz=AH2_(-0.5)*nz+AH2_(1.0); + // Min and max of ring. + AH2 mn4R=min(AMin3H2(bR,dR,fR),hR); + AH2 mn4G=min(AMin3H2(bG,dG,fG),hG); + AH2 mn4B=min(AMin3H2(bB,dB,fB),hB); + AH2 mx4R=max(AMax3H2(bR,dR,fR),hR); + AH2 mx4G=max(AMax3H2(bG,dG,fG),hG); + AH2 mx4B=max(AMax3H2(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R); + AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G); + AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B); + AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y); + AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y); + AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y); + AH2 lobeR=max(-hitMinR,hitMaxR); + AH2 lobeG=max(-hitMinG,hitMaxG); + AH2 lobeB=max(-hitMinB,hitMaxB); + AH2 lobe=max(AH2_(-FSR_RCAS_LIMIT),min(AMax3H2(lobeR,lobeG,lobeB),AH2_(0.0)))*AH2_(AH2_AU1(con.y).x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH2 rcpL=APrxMedRcpH2(AH2_(4.0)*lobe+AH2_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [LFGA] LINEAR FILM GRAIN APPLICATOR +// +//------------------------------------------------------------------------------------------------------------------------------ +// Adding output-resolution film grain after scaling is a good way to mask both rendering and scaling artifacts. +// Suggest using tiled blue noise as film grain input, with peak noise frequency set for a specific look and feel. +// The 'Lfga*()' functions provide a convenient way to introduce grain. +// These functions limit grain based on distance to signal limits. +// This is done so that the grain is temporally energy preserving, and thus won't modify image tonality. +// Grain application should be done in a linear colorspace. +// The grain should be temporally changing, but have a temporal sum per pixel that adds to zero (non-biased). +//------------------------------------------------------------------------------------------------------------------------------ +// Usage, +// FsrLfga*( +// color, // In/out linear colorspace color {0 to 1} ranged. +// grain, // Per pixel grain texture value {-0.5 to 0.5} ranged, input is 3-channel to support colored grain. +// amount); // Amount of grain (0 to 1} ranged. +//------------------------------------------------------------------------------------------------------------------------------ +// Example if grain texture is monochrome: 'FsrLfgaF(color,AF3_(grain),amount)' +//============================================================================================================================== +#if defined(A_GPU) + // Maximum grain is the minimum distance to the signal limit. + void FsrLfgaF(inout AF3 c,AF3 t,AF1 a){c+=(t*AF3_(a))*min(AF3_(1.0)-c,c);} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + // Half precision version (slower). + void FsrLfgaH(inout AH3 c,AH3 t,AH1 a){c+=(t*AH3_(a))*min(AH3_(1.0)-c,c);} +//------------------------------------------------------------------------------------------------------------------------------ + // Packed half precision version (faster). + void FsrLfgaHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 tR,AH2 tG,AH2 tB,AH1 a){ + cR+=(tR*AH2_(a))*min(AH2_(1.0)-cR,cR);cG+=(tG*AH2_(a))*min(AH2_(1.0)-cG,cG);cB+=(tB*AH2_(a))*min(AH2_(1.0)-cB,cB);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [SRTM] SIMPLE REVERSIBLE TONE-MAPPER +// +//------------------------------------------------------------------------------------------------------------------------------ +// This provides a way to take linear HDR color {0 to FP16_MAX} and convert it into a temporary {0 to 1} ranged post-tonemapped linear. +// The tonemapper preserves RGB ratio, which helps maintain HDR color bleed during filtering. +//------------------------------------------------------------------------------------------------------------------------------ +// Reversible tonemapper usage, +// FsrSrtm*(color); // {0 to FP16_MAX} converted to {0 to 1}. +// FsrSrtmInv*(color); // {0 to 1} converted into {0 to 32768, output peak safe for FP16}. +//============================================================================================================================== +#if defined(A_GPU) + void FsrSrtmF(inout AF3 c){c*=AF3_(ARcpF1(AMax3F1(c.r,c.g,c.b)+AF1_(1.0)));} + // The extra max solves the c=1.0 case (which is a /0). + void FsrSrtmInvF(inout AF3 c){c*=AF3_(ARcpF1(max(AF1_(1.0/32768.0),AF1_(1.0)-AMax3F1(c.r,c.g,c.b))));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + void FsrSrtmH(inout AH3 c){c*=AH3_(ARcpH1(AMax3H1(c.r,c.g,c.b)+AH1_(1.0)));} + void FsrSrtmInvH(inout AH3 c){c*=AH3_(ARcpH1(max(AH1_(1.0/32768.0),AH1_(1.0)-AMax3H1(c.r,c.g,c.b))));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrSrtmHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(AMax3H2(cR,cG,cB)+AH2_(1.0));cR*=rcp;cG*=rcp;cB*=rcp;} + void FsrSrtmInvHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(max(AH2_(1.0/32768.0),AH2_(1.0)-AMax3H2(cR,cG,cB)));cR*=rcp;cG*=rcp;cB*=rcp;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [TEPD] TEMPORAL ENERGY PRESERVING DITHER +// +//------------------------------------------------------------------------------------------------------------------------------ +// Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// Gamma 2.0 is used so that the conversion back to linear is just to square the color. +// The conversion comes in 8-bit and 10-bit modes, designed for output to 8-bit UNORM or 10:10:10:2 respectively. +// Given good non-biased temporal blue noise as dither input, +// the output dither will temporally conserve energy. +// This is done by choosing the linear nearest step point instead of perceptual nearest. +// See code below for details. +//------------------------------------------------------------------------------------------------------------------------------ +// DX SPEC RULES FOR FLOAT->UNORM 8-BIT CONVERSION +// =============================================== +// - Output is 'uint(floor(saturate(n)*255.0+0.5))'. +// - Thus rounding is to nearest. +// - NaN gets converted to zero. +// - INF is clamped to {0.0 to 1.0}. +//============================================================================================================================== +#if defined(A_GPU) + // Hand tuned integer position to dither value, with more values than simple checkerboard. + // Only 32-bit has enough precision for this compddation. + // Output is {0 to <1}. + AF1 FsrTepdDitF(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + // The 1.61803 golden ratio. + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + // Number designed to provide a good visual pattern. + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AFractF1(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 8-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC8F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(255.0))*AF3_(1.0/255.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/255.0);b=b*b; + // Ratio of 'a' to 'b' required to produce 'c'. + // APrxLoRcpF1() won't work here (at least for very high dynamic ranges). + // APrxMedRcpF1() is an IADD,FMA,MUL. + AF3 r=(c-b)*APrxMedRcpF3(a-b); + // Use the ratio as a cutoff to choose 'a' or 'b'. + // AGtZeroF1() is a MUL. + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 10-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC10F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(1023.0))*AF3_(1.0/1023.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/1023.0);b=b*b; + AF3 r=(c-b)*APrxMedRcpF3(a-b); + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/1023.0));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + AH1 FsrTepdDitH(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AH1(AFractF1(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(255.0))*AH3_(1.0/255.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/255.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(1023.0))*AH3_(1.0/1023.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/1023.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/1023.0));} +//============================================================================================================================== + // This computes dither for positions 'p' and 'p+{8,0}'. + AH2 FsrTepdDitHx2(AU2 p,AU1 f){ + AF2 x; + x.x=AF1_(p.x+f); + x.y=x.x+AF1_(8.0); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*AF2_(a)+AF2_(y*b); + return AH2(AFractF2(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(255.0))*AH2_(1.0/255.0); + nG=floor(nG*AH2_(255.0))*AH2_(1.0/255.0); + nB=floor(nB*AH2_(255.0))*AH2_(1.0/255.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/255.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/255.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/255.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/255.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/255.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(1023.0))*AH2_(1.0/1023.0); + nG=floor(nG*AH2_(1023.0))*AH2_(1.0/1023.0); + nB=floor(nB*AH2_(1023.0))*AH2_(1.0/1023.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/1023.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/1023.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/1023.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/1023.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/1023.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/1023.0));} +#endif diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl b/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl new file mode 100644 index 000000000..8e8755db2 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl @@ -0,0 +1,88 @@ +#version 430 core +precision mediump float; +layout (local_size_x = 64) in; +layout(rgba8, binding = 0, location=0) uniform image2D imgOutput; +layout( location=1 ) uniform sampler2D Source; +layout( location=2 ) uniform float srcX0; +layout( location=3 ) uniform float srcX1; +layout( location=4 ) uniform float srcY0; +layout( location=5 ) uniform float srcY1; +layout( location=6 ) uniform float dstX0; +layout( location=7 ) uniform float dstX1; +layout( location=8 ) uniform float dstY0; +layout( location=9 ) uniform float dstY1; +layout( location=10 ) uniform float scaleX; +layout( location=11 ) uniform float scaleY; + +#define A_GPU 1 +#define A_GLSL 1 +#include "ffx_a.h" + +#define FSR_EASU_F 1 +AU4 con0, con1, con2, con3; +float srcW, srcH, dstW, dstH; +vec2 bLeft, tRight; + +AF2 translate(AF2 pos) { + return AF2(pos.x * scaleX, pos.y * scaleY); +} + +void setBounds(vec2 bottomLeft, vec2 topRight) { + bLeft = bottomLeft; + tRight = topRight; +} + +AF2 translateDest(AF2 pos) { + AF2 translatedPos = AF2(pos.x, pos.y); + translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x; + translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1: translatedPos.y; + return translatedPos; +} + +AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(Source, translate(p), 0); return res; } +AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(Source, translate(p), 1); return res; } +AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(Source, translate(p), 2); return res; } + +#include "ffx_fsr1.h" + +float insideBox(vec2 v) { + vec2 s = step(bLeft, v) - step(tRight, v); + return s.x * s.y; +} + +void CurrFilter(AU2 pos) +{ + if((insideBox(vec2(pos.x, pos.y))) == 0) { + imageStore(imgOutput, ASU2(pos.x, pos.y), AF4(0,0,0,1)); + return; + } + AF3 c; + FsrEasuF(c, AU2(pos.x - bLeft.x, pos.y - bLeft.y), con0, con1, con2, con3); + imageStore(imgOutput, ASU2(translateDest(pos)), AF4(c, 1)); +} + +void main() { + srcW = abs(srcX1 - srcX0); + srcH = abs(srcY1 - srcY0); + dstW = abs(dstX1 - dstX0); + dstH = abs(dstY1 - dstY0); + + AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); + + setBounds(vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1), + vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0)); + + // Upscaling + FsrEasuCon(con0, con1, con2, con3, + srcW, srcH, // Viewport size (top left aligned) in the input image which is to be scaled. + srcW, srcH, // The size of the input image. + dstW, dstH); // The output resolution. + + CurrFilter(gxy); + gxy.x += 8u; + CurrFilter(gxy); + gxy.y += 8u; + CurrFilter(gxy); + gxy.x -= 8u; + CurrFilter(gxy); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl b/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl new file mode 100644 index 000000000..d3b98729a --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl @@ -0,0 +1,37 @@ +#version 430 core +precision mediump float; +layout (local_size_x = 64) in; +layout(rgba8, binding = 0, location=0) uniform image2D imgOutput; +layout( location=1 ) uniform sampler2D source; +layout( location=2 ) uniform float sharpening; + +#define A_GPU 1 +#define A_GLSL 1 +#include "ffx_a.h" + +#define FSR_RCAS_F 1 +AU4 con0; + +AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(source, p, 0)); } +void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {} + +#include "ffx_fsr1.h" + +void CurrFilter(AU2 pos) +{ + AF3 c; + FsrRcasF(c.r, c.g, c.b, pos, con0); + imageStore(imgOutput, ASU2(pos), AF4(c, 1)); +} + +void main() { + FsrRcasCon(con0, sharpening); + AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); + CurrFilter(gxy); + gxy.x += 8u; + CurrFilter(gxy); + gxy.y += 8u; + CurrFilter(gxy); + gxy.x -= 8u; + CurrFilter(gxy); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl b/Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl new file mode 100644 index 000000000..8bdcbca69 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl @@ -0,0 +1,1174 @@ +/*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + + +------------------------------------------------------------------------------ +COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. +------------------------------------------------------------------------------ +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA +OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR +LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, +OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE +THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +------------------------------------------------------------------------------ + INTEGRATION CHECKLIST +------------------------------------------------------------------------------ +(1.) +In the shader source, setup defines for the desired configuration. +When providing multiple shaders (for different presets), +simply setup the defines differently in multiple files. +Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY__PRESET 12 + +Or, + + #define FXAA_360 1 + +Or, + + #define FXAA_PS3 1 + +Etc. + +(2.) +Then include this file, + + #include "Fxaa3_11.h" + +(3.) +Then call the FXAA pixel shader from within your desired shader. +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. +As for FXAA 3.11 all inputs for all shaders are the same +to enable easy porting between platforms. + + return FxaaPixelShader(...); + +(4.) +Insure pass prior to FXAA outputs RGBL (see next section). +Or use, + + #define FXAA_GREEN_AS_LUMA 1 + +(5.) +Setup engine to provide the following constants +which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + +(6.) +Have FXAA vertex shader run as a full screen triangle, +and output "pos" and "fxaaConsolePosPos" +such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + +(7.) +Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + +------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE +------------------------------------------------------------------------------ +FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + +In which case the engine uses green in place of luma, +and requires RGB input is in a non-linear colorspace. + +RGB should be LDR (low dynamic range). +Specifically do FXAA after tonemapping. + +RGB data as returned by a texture fetch can be non-linear, +or linear when FXAA_GREEN_AS_LUMA is not set. +Note an "sRGB format" texture counts as linear, +because the result of a texture fetch is linear data. +Regular "RGBA8" textures in the sRGB colorspace are non-linear. + +If FXAA_GREEN_AS_LUMA is not set, +luma must be stored in the alpha channel prior to running FXAA. +This luma should be in a perceptual space (could be gamma 2.0). +Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + +Another example where output is linear encoded, +say for instance writing to an sRGB formated render target, +where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + +Getting luma correct is required for the algorithm to work correctly. + + +------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? +------------------------------------------------------------------------------ +Applying FXAA to a framebuffer with linear RGB color will look worse. +This is very counter intuitive, but happends to be true in this case. +The reason is because dithering artifacts will be more visiable +in a linear colorspace. + + +------------------------------------------------------------------------------ + COMPLEX INTEGRATION +------------------------------------------------------------------------------ +Q. What if the engine is blending into RGB before wanting to run FXAA? + +A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + +A. Or use FXAA_GREEN_AS_LUMA. + +============================================================================*/ + +#version 430 core + +layout(local_size_x = 16, local_size_y = 16) in; +layout(rgba8, binding = 0) uniform image2D imgOutput; + +uniform sampler2D inputTexture; +layout(location=0) uniform vec2 invResolution; + +#define FXAA_QUALITY__PRESET 12 +#define FXAA_GREEN_AS_LUMA 1 +#define FXAA_PC 1 +#define FXAA_GLSL_130 1 + + +/*============================================================================ + + INTEGRATION KNOBS + +/*==========================================================================*/ +#ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/*==========================================================================*/ +#endif + +vec4 mainImage(vec2 fragCoord) +{ + vec2 rcpFrame = 1./invResolution.xy; + vec2 uv2 = fragCoord.xy / invResolution.xy; + + float fxaaQualitySubpix = 0.75; // [0..1], default 0.75 + float fxaaQualityEdgeThreshold = 0.166; // [0.125..0.33], default 0.166 + float fxaaQualityEdgeThresholdMin = 0.02;//0.0625; // ? + vec4 dummy4 = vec4(0.0,0.0,0.0,0.0); + float dummy1 = 0.0; + + vec4 col = FxaaPixelShader(uv2, dummy4, + inputTexture, inputTexture, inputTexture, + rcpFrame, dummy4, dummy4, dummy4, + fxaaQualitySubpix, fxaaQualityEdgeThreshold, + fxaaQualityEdgeThresholdMin, + dummy1, dummy1, dummy1, dummy4); + + vec4 fragColor = vec4( col.xyz, 1. ); + + return fragColor; +} + +void main() +{ + ivec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec4 outColor = mainImage(texelCoord + vec2(0.5)); + imageStore(imgOutput, texelCoord, outColor); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl new file mode 100644 index 000000000..2201f78c1 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl @@ -0,0 +1,1361 @@ +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------� + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------� + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS<float4, 2> tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl new file mode 100644 index 000000000..c875ce127 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl @@ -0,0 +1,26 @@ +layout(rgba8, binding = 0) uniform image2D imgOutput; + +uniform sampler2D inputTexture; +layout( location=0 ) uniform vec2 invResolution; +uniform sampler2D samplerArea; +uniform sampler2D samplerSearch; + +void main() { + ivec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec2 coord = (texelCoord + vec2(0.5)) / invResolution; + vec2 pixCoord; + vec4 offset[3]; + + SMAABlendingWeightCalculationVS(coord, pixCoord, offset); + + vec4 oColor = SMAABlendingWeightCalculationPS(coord, pixCoord, offset, inputTexture, samplerArea, samplerSearch, ivec4(0)); + + imageStore(imgOutput, texelCoord, oColor); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl new file mode 100644 index 000000000..fd5d97154 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl @@ -0,0 +1,24 @@ +layout(rgba8, binding = 0) uniform image2D imgOutput; + +uniform sampler2D inputTexture; +layout( location=0 ) uniform vec2 invResolution; + +void main() +{ + vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec2 coord = (texelCoord + vec2(0.5)) / invResolution; + vec4 offset[3]; + SMAAEdgeDetectionVS(coord, offset); + vec2 oColor = SMAAColorEdgeDetectionPS(coord, offset, inputTexture); + if (oColor != float2(-2.0, -2.0)) + { + imageStore(imgOutput, texelCoord, vec4(oColor, 0.0, 1.0)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl new file mode 100644 index 000000000..2e9432ae6 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl @@ -0,0 +1,26 @@ +layout(rgba8, binding = 0) uniform image2D imgOutput; + +uniform sampler2D inputTexture; +layout( location=0 ) uniform vec2 invResolution; +uniform sampler2D samplerBlend; + +void main() { + vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec2 coord = (texelCoord + vec2(0.5)) / invResolution; + vec2 pixCoord; + vec4 offset; + + SMAANeighborhoodBlendingVS(coord, offset); + + vec4 oColor = SMAANeighborhoodBlendingPS(coord, offset, inputTexture, samplerBlend); + + imageStore(imgOutput, texelCoord, oColor); + } + } + +} diff --git a/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs b/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs new file mode 100644 index 000000000..1ad300c88 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs @@ -0,0 +1,261 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Image; +using System; + +namespace Ryujinx.Graphics.OpenGL.Effects.Smaa +{ + internal partial class SmaaPostProcessingEffect : IPostProcessingEffect + { + public const int AreaWidth = 160; + public const int AreaHeight = 560; + public const int SearchWidth = 64; + public const int SearchHeight = 16; + + private readonly OpenGLRenderer _renderer; + private TextureStorage _outputTexture; + private TextureStorage _searchTexture; + private TextureStorage _areaTexture; + private int[] _edgeShaderPrograms; + private int[] _blendShaderPrograms; + private int[] _neighbourShaderPrograms; + private TextureStorage _edgeOutputTexture; + private TextureStorage _blendOutputTexture; + private string[] _qualities; + private int _inputUniform; + private int _outputUniform; + private int _samplerAreaUniform; + private int _samplerSearchUniform; + private int _samplerBlendUniform; + private int _resolutionUniform; + private int _quality = 1; + + public int Quality + { + get => _quality; set + { + _quality = Math.Clamp(value, 0, _qualities.Length - 1); + } + } + public SmaaPostProcessingEffect(OpenGLRenderer renderer, int quality) + { + _renderer = renderer; + + _edgeShaderPrograms = Array.Empty<int>(); + _blendShaderPrograms = Array.Empty<int>(); + _neighbourShaderPrograms = Array.Empty<int>(); + + _qualities = new string[] { "SMAA_PRESET_LOW", "SMAA_PRESET_MEDIUM", "SMAA_PRESET_HIGH", "SMAA_PRESET_ULTRA" }; + + Quality = quality; + + Initialize(); + } + + public void Dispose() + { + _searchTexture?.Dispose(); + _areaTexture?.Dispose(); + _outputTexture?.Dispose(); + _edgeOutputTexture?.Dispose(); + _blendOutputTexture?.Dispose(); + + DeleteShaders(); + } + + private void DeleteShaders() + { + for (int i = 0; i < _edgeShaderPrograms.Length; i++) + { + GL.DeleteProgram(_edgeShaderPrograms[i]); + GL.DeleteProgram(_blendShaderPrograms[i]); + GL.DeleteProgram(_neighbourShaderPrograms[i]); + } + } + + private unsafe void RecreateShaders(int width, int height) + { + string baseShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl"); + var pixelSizeDefine = $"#define SMAA_RT_METRICS float4(1.0 / {width}.0, 1.0 / {height}.0, {width}, {height}) \n"; + + _edgeShaderPrograms = new int[_qualities.Length]; + _blendShaderPrograms = new int[_qualities.Length]; + _neighbourShaderPrograms = new int[_qualities.Length]; + + for (int i = 0; i < +_edgeShaderPrograms.Length; i++) + { + var presets = $"#version 430 core \n#define {_qualities[i]} 1 \n{pixelSizeDefine}#define SMAA_GLSL_4 1 \nlayout (local_size_x = 16, local_size_y = 16) in;\n{baseShader}"; + + var edgeShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl"); + var blendShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl"); + var neighbourShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl"); + + var shaders = new string[] { presets, edgeShaderData }; + var edgeProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader); + + shaders[1] = blendShaderData; + var blendProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader); + + shaders[1] = neighbourShaderData; + var neighbourProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader); + + _edgeShaderPrograms[i] = edgeProgram; + _blendShaderPrograms[i] = blendProgram; + _neighbourShaderPrograms[i] = neighbourProgram; + } + + _inputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "inputTexture"); + _outputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "imgOutput"); + _samplerAreaUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerArea"); + _samplerSearchUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerSearch"); + _samplerBlendUniform = GL.GetUniformLocation(_neighbourShaderPrograms[0], "samplerBlend"); + _resolutionUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "invResolution"); + } + + private void Initialize() + { + var areaInfo = new TextureCreateInfo(AreaWidth, + AreaHeight, + 1, + 1, + 1, + 1, + 1, + 1, + Format.R8G8Unorm, + DepthStencilMode.Depth, + Target.Texture2D, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha); + + var searchInfo = new TextureCreateInfo(SearchWidth, + SearchHeight, + 1, + 1, + 1, + 1, + 1, + 1, + Format.R8Unorm, + DepthStencilMode.Depth, + Target.Texture2D, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha); + + _areaTexture = new TextureStorage(_renderer, areaInfo, 1); + _searchTexture = new TextureStorage(_renderer, searchInfo, 1); + + var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin"); + var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin"); + + var areaView = _areaTexture.CreateDefaultView(); + var searchView = _searchTexture.CreateDefaultView(); + + areaView.SetData(areaTexture); + searchView.SetData(searchTexture); + } + + public TextureView Run(TextureView view, int width, int height) + { + if (_outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height) + { + _outputTexture?.Dispose(); + _outputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _outputTexture.CreateDefaultView(); + _edgeOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _edgeOutputTexture.CreateDefaultView(); + _blendOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _blendOutputTexture.CreateDefaultView(); + + DeleteShaders(); + + RecreateShaders(view.Width, view.Height); + } + + var textureView = _outputTexture.CreateView(view.Info, 0, 0) as TextureView; + var edgeOutput = _edgeOutputTexture.DefaultView as TextureView; + var blendOutput = _blendOutputTexture.DefaultView as TextureView; + var areaTexture = _areaTexture.DefaultView as TextureView; + var searchTexture = _searchTexture.DefaultView as TextureView; + + var previousFramebuffer = GL.GetInteger(GetPName.FramebufferBinding); + int previousUnit = GL.GetInteger(GetPName.ActiveTexture); + GL.ActiveTexture(TextureUnit.Texture0); + int previousTextureBinding0 = GL.GetInteger(GetPName.TextureBinding2D); + GL.ActiveTexture(TextureUnit.Texture1); + int previousTextureBinding1 = GL.GetInteger(GetPName.TextureBinding2D); + GL.ActiveTexture(TextureUnit.Texture2); + int previousTextureBinding2 = GL.GetInteger(GetPName.TextureBinding2D); + + var framebuffer = new Framebuffer(); + framebuffer.Bind(); + framebuffer.AttachColor(0, edgeOutput); + GL.Clear(ClearBufferMask.ColorBufferBit); + GL.ClearColor(0, 0, 0, 0); + framebuffer.AttachColor(0, blendOutput); + GL.Clear(ClearBufferMask.ColorBufferBit); + GL.ClearColor(0, 0, 0, 0); + + GL.BindFramebuffer(FramebufferTarget.Framebuffer, previousFramebuffer); + + framebuffer.Dispose(); + + var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); + var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); + + int previousProgram = GL.GetInteger(GetPName.CurrentProgram); + GL.BindImageTexture(0, edgeOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); + GL.UseProgram(_edgeShaderPrograms[Quality]); + view.Bind(0); + GL.Uniform1(_inputUniform, 0); + GL.Uniform1(_outputUniform, 0); + GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height); + GL.DispatchCompute(dispatchX, dispatchY, 1); + GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); + + GL.BindImageTexture(0, blendOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); + GL.UseProgram(_blendShaderPrograms[Quality]); + edgeOutput.Bind(0); + areaTexture.Bind(1); + searchTexture.Bind(2); + GL.Uniform1(_inputUniform, 0); + GL.Uniform1(_outputUniform, 0); + GL.Uniform1(_samplerAreaUniform, 1); + GL.Uniform1(_samplerSearchUniform, 2); + GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height); + GL.DispatchCompute(dispatchX, dispatchY, 1); + GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); + + GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); + GL.UseProgram(_neighbourShaderPrograms[Quality]); + view.Bind(0); + blendOutput.Bind(1); + GL.Uniform1(_inputUniform, 0); + GL.Uniform1(_outputUniform, 0); + GL.Uniform1(_samplerBlendUniform, 1); + GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height); + GL.DispatchCompute(dispatchX, dispatchY, 1); + GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); + + (_renderer.Pipeline as Pipeline).RestoreImages1And2(); + + GL.UseProgram(previousProgram); + + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding0); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding1); + GL.ActiveTexture(TextureUnit.Texture2); + GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding2); + + GL.ActiveTexture((TextureUnit)previousUnit); + + return textureView; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin b/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin new file mode 100644 index 0000000000000000000000000000000000000000..f4a7a1b417766c12bbac4e4bdc56796f18538bd6 GIT binary patch literal 179200 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μw<5_SoZr#d3FkR_X{|a|ppP>)<F?`p3c>nXr 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin b/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin new file mode 100644 index 0000000000000000000000000000000000000000..db5bf73f7d5a0b5e436d336849c90bfbc24d76dc GIT binary patch literal 1024 zcmezOkD<Pvf#Dy7Vt@dk2uKi2{R1+90K~@zpc={6kIhU{#3;3&QvIa3l@@A|qY7?5 evLH0#aK#_8QgZae^^nP+)P73!lj-bXqYVIqI9W{q literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj index 9fd2c48a5..2313cc68f 100644 --- a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj +++ b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj @@ -9,6 +9,20 @@ <PackageReference Include="OpenTK.Graphics" /> </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" /> + <EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" /> + <EmbeddedResource Include="Effects\Shaders\fsr_sharpening.glsl" /> + <EmbeddedResource Include="Effects\Shaders\fxaa.glsl" /> + <EmbeddedResource Include="Effects\Shaders\smaa.hlsl" /> + <EmbeddedResource Include="Effects\Shaders\smaa_blend.glsl" /> + <EmbeddedResource Include="Effects\Shaders\smaa_edge.glsl" /> + <EmbeddedResource Include="Effects\Shaders\smaa_neighbour.glsl" /> + <EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" /> + <EmbeddedResource Include="Effects\Shaders\ffx_a.h" /> + <EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" /> + </ItemGroup> + <ItemGroup> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" /> diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index 8f7917f91..d6606f392 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -1,5 +1,7 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Effects; +using Ryujinx.Graphics.OpenGL.Effects.Smaa; using Ryujinx.Graphics.OpenGL.Image; using System; @@ -7,14 +9,24 @@ namespace Ryujinx.Graphics.OpenGL { class Window : IWindow, IDisposable { - private const int TextureCount = 3; private readonly OpenGLRenderer _renderer; private bool _initialized; private int _width; private int _height; + private bool _updateSize; private int _copyFramebufferHandle; + private IPostProcessingEffect _antiAliasing; + private IScalingFilter _scalingFilter; + private bool _isLinear; + private AntiAliasing _currentAntiAliasing; + private bool _updateEffect; + private ScalingFilter _currentScalingFilter; + private float _scalingFilterLevel; + private bool _updateScalingFilter; + private bool _isBgra; + private TextureView _upscaledTexture; internal BackgroundContextWorker BackgroundContext { get; private set; } @@ -48,6 +60,8 @@ namespace Ryujinx.Graphics.OpenGL { _width = width; _height = height; + + _updateSize = true; } private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback) @@ -57,6 +71,32 @@ namespace Ryujinx.Graphics.OpenGL TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view; + UpdateEffect(); + + if (_antiAliasing != null) + { + var oldView = viewConverted; + + viewConverted = _antiAliasing.Run(viewConverted, _width, _height); + + if (viewConverted.Format.IsBgr()) + { + var swappedView = _renderer.TextureCopy.BgraSwap(viewConverted); + + viewConverted?.Dispose(); + + viewConverted = swappedView; + } + + if (viewConverted != oldView && oldView != view) + { + oldView.Dispose(); + } + } + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); + GL.FramebufferTexture( FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, @@ -71,12 +111,12 @@ namespace Ryujinx.Graphics.OpenGL GL.Clear(ClearBufferMask.ColorBufferBit); int srcX0, srcX1, srcY0, srcY1; - float scale = view.ScaleFactor; + float scale = viewConverted.ScaleFactor; if (crop.Left == 0 && crop.Right == 0) { srcX0 = 0; - srcX1 = (int)(view.Width / scale); + srcX1 = (int)(viewConverted.Width / scale); } else { @@ -87,7 +127,7 @@ namespace Ryujinx.Graphics.OpenGL if (crop.Top == 0 && crop.Bottom == 0) { srcY0 = 0; - srcY1 = (int)(view.Height / scale); + srcY1 = (int)(viewConverted.Height / scale); } else { @@ -125,6 +165,42 @@ namespace Ryujinx.Graphics.OpenGL ScreenCaptureRequested = false; } + if (_scalingFilter != null) + { + if (viewConverted.Format.IsBgr() && !_isBgra) + { + RecreateUpscalingTexture(true); + } + + _scalingFilter.Run( + viewConverted, + _upscaledTexture, + _width, + _height, + new Extents2D( + srcX0, + srcY0, + srcX1, + srcY1), + new Extents2D( + dstX0, + dstY0, + dstX1, + dstY1) + ); + + srcX0 = dstX0; + srcY0 = dstY0; + srcX1 = dstX1; + srcY1 = dstY1; + + GL.FramebufferTexture( + FramebufferTarget.ReadFramebuffer, + FramebufferAttachment.ColorAttachment0, + _upscaledTexture.Handle, + 0); + } + GL.BlitFramebuffer( srcX0, srcY0, @@ -135,7 +211,7 @@ namespace Ryujinx.Graphics.OpenGL dstX1, dstY1, ClearBufferMask.ColorBufferBit, - BlitFramebufferFilter.Linear); + _isLinear ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest); // Remove Alpha channel GL.ColorMask(false, false, false, true); @@ -209,6 +285,135 @@ namespace Ryujinx.Graphics.OpenGL _copyFramebufferHandle = 0; } + + _antiAliasing?.Dispose(); + _scalingFilter?.Dispose(); + _upscaledTexture?.Dispose(); + } + + public void SetAntiAliasing(AntiAliasing effect) + { + if (_currentAntiAliasing == effect && _antiAliasing != null) + { + return; + } + + _currentAntiAliasing = effect; + + _updateEffect = true; + } + + public void SetScalingFilter(ScalingFilter type) + { + if (_currentScalingFilter == type && _antiAliasing != null) + { + return; + } + + _currentScalingFilter = type; + + _updateScalingFilter = true; + } + + private void UpdateEffect() + { + if (_updateEffect) + { + _updateEffect = false; + + switch (_currentAntiAliasing) + { + case AntiAliasing.Fxaa: + _antiAliasing?.Dispose(); + _antiAliasing = new FxaaPostProcessingEffect(_renderer); + break; + case AntiAliasing.None: + _antiAliasing?.Dispose(); + _antiAliasing = null; + break; + case AntiAliasing.SmaaLow: + case AntiAliasing.SmaaMedium: + case AntiAliasing.SmaaHigh: + case AntiAliasing.SmaaUltra: + var quality = _currentAntiAliasing - AntiAliasing.SmaaLow; + if (_antiAliasing is SmaaPostProcessingEffect smaa) + { + smaa.Quality = quality; + } + else + { + _antiAliasing?.Dispose(); + _antiAliasing = new SmaaPostProcessingEffect(_renderer, quality); + } + break; + } + } + + if (_updateSize && !_updateScalingFilter) + { + RecreateUpscalingTexture(); + } + + _updateSize = false; + + if (_updateScalingFilter) + { + _updateScalingFilter = false; + + switch (_currentScalingFilter) + { + case ScalingFilter.Bilinear: + case ScalingFilter.Nearest: + _scalingFilter?.Dispose(); + _scalingFilter = null; + _isLinear = _currentScalingFilter == ScalingFilter.Bilinear; + _upscaledTexture?.Dispose(); + _upscaledTexture = null; + break; + case ScalingFilter.Fsr: + if (_scalingFilter is not FsrScalingFilter) + { + _scalingFilter?.Dispose(); + _scalingFilter = new FsrScalingFilter(_renderer, _antiAliasing); + } + _isLinear = false; + _scalingFilter.Level = _scalingFilterLevel; + + RecreateUpscalingTexture(); + break; + } + } + } + + private void RecreateUpscalingTexture(bool forceBgra = false) + { + _upscaledTexture?.Dispose(); + + var info = new TextureCreateInfo( + _width, + _height, + 1, + 1, + 1, + 1, + 1, + 1, + Format.R8G8B8A8Unorm, + DepthStencilMode.Depth, + Target.Texture2D, + forceBgra ? SwizzleComponent.Blue : SwizzleComponent.Red, + SwizzleComponent.Green, + forceBgra ? SwizzleComponent.Red : SwizzleComponent.Blue, + SwizzleComponent.Alpha); + + _isBgra = forceBgra; + _upscaledTexture = _renderer.CreateTexture(info, 1) as TextureView; + } + + public void SetScalingFilterLevel(float level) + { + _scalingFilterLevel = level; + _updateScalingFilter = true; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 9ac2e61de..19a085023 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -163,6 +163,13 @@ namespace Ryujinx.Graphics.Vulkan SignalDirty(DirtyFlags.Image); } + public void SetImage(int binding, Auto<DisposableImageView> image) + { + _imageRefs[binding] = image; + + SignalDirty(DirtyFlags.Image); + } + public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers) { for (int i = 0; i < buffers.Length; i++) diff --git a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs new file mode 100644 index 000000000..a12070592 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -0,0 +1,208 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using Silk.NET.Vulkan; +using System; +using Extent2D = Ryujinx.Graphics.GAL.Extents2D; + +namespace Ryujinx.Graphics.Vulkan.Effects +{ + internal partial class FsrScalingFilter : IScalingFilter + { + private readonly VulkanRenderer _renderer; + private PipelineHelperShader _pipeline; + private ISampler _sampler; + private ShaderCollection _scalingProgram; + private ShaderCollection _sharpeningProgram; + private float _sharpeningLevel = 1; + private Device _device; + private TextureView _intermediaryTexture; + + public float Level + { + get => _sharpeningLevel; + set + { + _sharpeningLevel = MathF.Max(0.01f, value); + } + } + + public FsrScalingFilter(VulkanRenderer renderer, Device device) + { + _device = device; + _renderer = renderer; + + Initialize(); + } + + public void Dispose() + { + _pipeline.Dispose(); + _scalingProgram.Dispose(); + _sharpeningProgram.Dispose(); + _sampler.Dispose(); + _intermediaryTexture?.Dispose(); + } + + public void Initialize() + { + _pipeline = new PipelineHelperShader(_renderer, _device); + + _pipeline.Initialize(); + + var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv"); + var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv"); + + var computeBindings = new ShaderBindings( + new[] { 2 }, + Array.Empty<int>(), + new[] { 1 }, + new[] { 0 }); + + var sharpeningBindings = new ShaderBindings( + new[] { 2, 3, 4 }, + Array.Empty<int>(), + new[] { 1 }, + new[] { 0 }); + + _sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + + _scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }); + + _sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }); + } + + public void Run( + TextureView view, + CommandBufferScoped cbs, + Auto<DisposableImageView> destinationTexture, + Silk.NET.Vulkan.Format format, + int width, + int height, + Extent2D source, + Extent2D destination) + { + if (_intermediaryTexture == null + || _intermediaryTexture.Info.Width != width + || _intermediaryTexture.Info.Height != height + || !_intermediaryTexture.Info.Equals(view.Info)) + { + var originalInfo = view.Info; + + var swapRB = originalInfo.Format.IsBgr() && originalInfo.SwizzleR == SwizzleComponent.Red; + + var info = new TextureCreateInfo( + width, + height, + originalInfo.Depth, + originalInfo.Levels, + originalInfo.Samples, + originalInfo.BlockWidth, + originalInfo.BlockHeight, + originalInfo.BytesPerPixel, + originalInfo.Format, + originalInfo.DepthStencilMode, + originalInfo.Target, + swapRB ? originalInfo.SwizzleB : originalInfo.SwizzleR, + originalInfo.SwizzleG, + swapRB ? originalInfo.SwizzleR : originalInfo.SwizzleB, + originalInfo.SwizzleA); + _intermediaryTexture?.Dispose(); + _intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + } + + Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; + + viewports[0] = new GAL.Viewport( + new Rectangle<float>(0, 0, view.Width, view.Height), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + scissors[0] = new Rectangle<int>(0, 0, view.Width, view.Height); + + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_scalingProgram); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler); + + float srcWidth = Math.Abs(source.X2 - source.X1); + float srcHeight = Math.Abs(source.Y2 - source.Y1); + float scaleX = srcWidth / view.Width; + float scaleY = srcHeight / view.Height; + + ReadOnlySpan<float> dimensionsBuffer = stackalloc float[] + { + source.X1, + source.X2, + source.Y1, + source.Y2, + destination.X1, + destination.X2, + destination.Y1, + destination.Y2, + scaleX, + scaleY + }; + + int rangeSize = dimensionsBuffer.Length * sizeof(float); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false); + _renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer); + + ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)}; + var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float), false); + _renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer); + + int threadGroupWorkRegionDim = 16; + int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.ComputeBarrier(); + + viewports[0] = new GAL.Viewport( + new Rectangle<float>(0, 0, width, height), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + scissors[0] = new Rectangle<int>(0, 0, width, height); + + // Sharpening pass + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_sharpeningProgram); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float)); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetImage(0, destinationTexture); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.ComputeBarrier(); + + _pipeline.Finish(); + + _renderer.BufferManager.Delete(bufferHandle); + _renderer.BufferManager.Delete(sharpeningBufferHandle); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs new file mode 100644 index 000000000..0f6a0a7ba --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -0,0 +1,127 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using Silk.NET.Vulkan; +using System; + +namespace Ryujinx.Graphics.Vulkan.Effects +{ + internal partial class FxaaPostProcessingEffect : IPostProcessingEffect + { + private readonly VulkanRenderer _renderer; + private ISampler _samplerLinear; + private ShaderCollection _shaderProgram; + + private PipelineHelperShader _pipeline; + private TextureView _texture; + + public FxaaPostProcessingEffect(VulkanRenderer renderer, Device device) + { + _renderer = renderer; + _pipeline = new PipelineHelperShader(renderer, device); + + Initialize(); + } + + public void Dispose() + { + _shaderProgram.Dispose(); + _pipeline.Dispose(); + _samplerLinear.Dispose(); + _texture?.Dispose(); + } + + private void Initialize() + { + _pipeline.Initialize(); + + var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv"); + + var computeBindings = new ShaderBindings( + new[] { 2 }, + Array.Empty<int>(), + new[] { 1 }, + new[] { 0 }); + + _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + + _shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(shader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }); + } + + public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height) + { + if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height) + { + _texture?.Dispose(); + + var info = view.Info; + + if (view.Info.Format.IsBgr()) + { + info = new TextureCreateInfo(info.Width, + info.Height, + info.Depth, + info.Levels, + info.Samples, + info.BlockWidth, + info.BlockHeight, + info.BytesPerPixel, + info.Format, + info.DepthStencilMode, + info.Target, + info.SwizzleB, + info.SwizzleG, + info.SwizzleR, + info.SwizzleA); + } + _texture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + } + + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_shaderProgram); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); + + ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height }; + int rangeSize = resolutionBuffer.Length * sizeof(float); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false); + + _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); + + var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + + Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + + viewports[0] = new GAL.Viewport( + new Rectangle<float>(0, 0, view.Width, view.Height), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; + + var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); + var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); + + _pipeline.SetScissors(stackalloc[] { new Rectangle<int>(0, 0, view.Width, view.Height) }); + _pipeline.SetViewports(viewports, false); + + _pipeline.SetImage(0, _texture, GAL.Format.R8G8B8A8Unorm); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + + _renderer.BufferManager.Delete(bufferHandle); + _pipeline.ComputeBarrier(); + + _pipeline.Finish(); + + return _texture; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs new file mode 100644 index 000000000..d36cf01d4 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ryujinx.Graphics.Vulkan.Effects +{ + internal interface IPostProcessingEffect : IDisposable + { + const int LocalGroupSize = 64; + TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs b/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs new file mode 100644 index 000000000..54f809d71 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs @@ -0,0 +1,20 @@ +using Silk.NET.Vulkan; +using System; +using Extent2D = Ryujinx.Graphics.GAL.Extents2D; + +namespace Ryujinx.Graphics.Vulkan.Effects +{ + internal interface IScalingFilter : IDisposable + { + float Level { get; set; } + void Run( + TextureView view, + CommandBufferScoped cbs, + Auto<DisposableImageView> destinationTexture, + Format format, + int width, + int height, + Extent2D source, + Extent2D destination); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl b/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl new file mode 100644 index 000000000..5eb74b3d1 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl @@ -0,0 +1,3945 @@ +// Scaling + +#version 430 core +layout (local_size_x = 64) in; +layout( rgba8, binding = 0, set = 3) uniform image2D imgOutput; +layout( binding = 1, set = 2) uniform sampler2D Source; +layout( binding = 2 ) uniform dimensions{ + float srcX0; + float srcX1; + float srcY0; + float srcY1; + float dstX0; + float dstX1; + float dstY0; + float dstY1; + float scaleX; + float scaleY; +}; + +#define A_GPU 1 +#define A_GLSL 1 +//============================================================================================================================== +// +// [A] SHADER PORTABILITY 1.20210629 +// +//============================================================================================================================== +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// MIT LICENSE +// =========== +// Copyright (c) 2014 Michal Drobot (for concepts used in "FLOAT APPROXIMATIONS"). +// ----------- +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// ----------- +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +// Software. +// ----------- +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// Common central point for high-level shading language and C portability for various shader headers. +//------------------------------------------------------------------------------------------------------------------------------ +// DEFINES +// ======= +// A_CPU ..... Include the CPU related code. +// A_GPU ..... Include the GPU related code. +// A_GLSL .... Using GLSL. +// A_HLSL .... Using HLSL. +// A_HLSL_6_2 Using HLSL 6.2 with new 'uint16_t' and related types (requires '-enable-16bit-types'). +// A_NO_16_BIT_CAST Don't use instructions that are not availabe in SPIR-V (needed for running A_HLSL_6_2 on Vulkan) +// A_GCC ..... Using a GCC compatible compiler (else assume MSVC compatible compiler by default). +// ======= +// A_BYTE .... Support 8-bit integer. +// A_HALF .... Support 16-bit integer and floating point. +// A_LONG .... Support 64-bit integer. +// A_DUBL .... Support 64-bit floating point. +// ======= +// A_WAVE .... Support wave-wide operations. +//------------------------------------------------------------------------------------------------------------------------------ +// To get #include "ffx_a.h" working in GLSL use '#extension GL_GOOGLE_include_directive:require'. +//------------------------------------------------------------------------------------------------------------------------------ +// SIMPLIFIED TYPE SYSTEM +// ====================== +// - All ints will be unsigned with exception of when signed is required. +// - Type naming simplified and shortened "A<type><#components>", +// - H = 16-bit float (half) +// - F = 32-bit float (float) +// - D = 64-bit float (double) +// - P = 1-bit integer (predicate, not using bool because 'B' is used for byte) +// - B = 8-bit integer (byte) +// - W = 16-bit integer (word) +// - U = 32-bit integer (unsigned) +// - L = 64-bit integer (long) +// - Using "AS<type><#components>" for signed when required. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure 'ALerp*(a,b,m)' does 'b*m+(-a*m+a)' (2 ops). +//------------------------------------------------------------------------------------------------------------------------------ +// CHANGE LOG +// ========== +// 20200914 - Expanded wave ops and prx code. +// 20200713 - Added [ZOL] section, fixed serious bugs in sRGB and Rec.709 color conversion code, etc. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COMMON +//============================================================================================================================== +#define A_2PI 6.28318530718 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// CPU +// +// +//============================================================================================================================== +#ifdef A_CPU + // Supporting user defined overrides. + #ifndef A_RESTRICT + #define A_RESTRICT __restrict + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifndef A_STATIC + #define A_STATIC static + #endif +//------------------------------------------------------------------------------------------------------------------------------ + // Same types across CPU and GPU. + // Predicate uses 32-bit integer (C friendly bool). + typedef uint32_t AP1; + typedef float AF1; + typedef double AD1; + typedef uint8_t AB1; + typedef uint16_t AW1; + typedef uint32_t AU1; + typedef uint64_t AL1; + typedef int8_t ASB1; + typedef int16_t ASW1; + typedef int32_t ASU1; + typedef int64_t ASL1; +//------------------------------------------------------------------------------------------------------------------------------ + #define AD1_(a) ((AD1)(a)) + #define AF1_(a) ((AF1)(a)) + #define AL1_(a) ((AL1)(a)) + #define AU1_(a) ((AU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1_(a) ((ASL1)(a)) + #define ASU1_(a) ((ASU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AU1 AU1_AF1(AF1 a){union{AF1 f;AU1 u;}bits;bits.f=a;return bits.u;} +//------------------------------------------------------------------------------------------------------------------------------ + #define A_TRUE 1 + #define A_FALSE 0 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// CPU/GPU PORTING +// +//------------------------------------------------------------------------------------------------------------------------------ +// Get CPU and GPU to share all setup code, without duplicate code paths. +// This uses a lower-case prefix for special vector constructs. +// - In C restrict pointers are used. +// - In the shading language, in/inout/out arguments are used. +// This depends on the ability to access a vector value in both languages via array syntax (aka color[2]). +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD1 *A_RESTRICT + #define retAD3 AD1 *A_RESTRICT + #define retAD4 AD1 *A_RESTRICT + #define retAF2 AF1 *A_RESTRICT + #define retAF3 AF1 *A_RESTRICT + #define retAF4 AF1 *A_RESTRICT + #define retAL2 AL1 *A_RESTRICT + #define retAL3 AL1 *A_RESTRICT + #define retAL4 AL1 *A_RESTRICT + #define retAU2 AU1 *A_RESTRICT + #define retAU3 AU1 *A_RESTRICT + #define retAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 AD1 *A_RESTRICT + #define inAD3 AD1 *A_RESTRICT + #define inAD4 AD1 *A_RESTRICT + #define inAF2 AF1 *A_RESTRICT + #define inAF3 AF1 *A_RESTRICT + #define inAF4 AF1 *A_RESTRICT + #define inAL2 AL1 *A_RESTRICT + #define inAL3 AL1 *A_RESTRICT + #define inAL4 AL1 *A_RESTRICT + #define inAU2 AU1 *A_RESTRICT + #define inAU3 AU1 *A_RESTRICT + #define inAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 AD1 *A_RESTRICT + #define inoutAD3 AD1 *A_RESTRICT + #define inoutAD4 AD1 *A_RESTRICT + #define inoutAF2 AF1 *A_RESTRICT + #define inoutAF3 AF1 *A_RESTRICT + #define inoutAF4 AF1 *A_RESTRICT + #define inoutAL2 AL1 *A_RESTRICT + #define inoutAL3 AL1 *A_RESTRICT + #define inoutAL4 AL1 *A_RESTRICT + #define inoutAU2 AU1 *A_RESTRICT + #define inoutAU3 AU1 *A_RESTRICT + #define inoutAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 AD1 *A_RESTRICT + #define outAD3 AD1 *A_RESTRICT + #define outAD4 AD1 *A_RESTRICT + #define outAF2 AF1 *A_RESTRICT + #define outAF3 AF1 *A_RESTRICT + #define outAF4 AF1 *A_RESTRICT + #define outAL2 AL1 *A_RESTRICT + #define outAL3 AL1 *A_RESTRICT + #define outAL4 AL1 *A_RESTRICT + #define outAU2 AU1 *A_RESTRICT + #define outAU3 AU1 *A_RESTRICT + #define outAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD1 x[2] + #define varAD3(x) AD1 x[3] + #define varAD4(x) AD1 x[4] + #define varAF2(x) AF1 x[2] + #define varAF3(x) AF1 x[3] + #define varAF4(x) AF1 x[4] + #define varAL2(x) AL1 x[2] + #define varAL3(x) AL1 x[3] + #define varAL4(x) AL1 x[4] + #define varAU2(x) AU1 x[2] + #define varAU3(x) AU1 x[3] + #define varAU4(x) AU1 x[4] +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) {x,y} + #define initAD3(x,y,z) {x,y,z} + #define initAD4(x,y,z,w) {x,y,z,w} + #define initAF2(x,y) {x,y} + #define initAF3(x,y,z) {x,y,z} + #define initAF4(x,y,z,w) {x,y,z,w} + #define initAL2(x,y) {x,y} + #define initAL3(x,y,z) {x,y,z} + #define initAL4(x,y,z,w) {x,y,z,w} + #define initAU2(x,y) {x,y} + #define initAU3(x,y,z) {x,y,z} + #define initAU4(x,y,z,w) {x,y,z,w} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Replace transcendentals with manual versions. +//============================================================================================================================== + #ifdef A_GCC + A_STATIC AD1 AAbsD1(AD1 a){return __builtin_fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return __builtin_fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(__builtin_abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(__builtin_llabs(ASL1_(a)));} + #else + A_STATIC AD1 AAbsD1(AD1 a){return fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(labs((long)ASL1_(a)));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ACosD1(AD1 a){return __builtin_cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return __builtin_cosf(a);} + #else + A_STATIC AD1 ACosD1(AD1 a){return cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return cosf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ADotD2(inAD2 a,inAD2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AD1 ADotD3(inAD3 a,inAD3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AD1 ADotD4(inAD4 a,inAD4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} + A_STATIC AF1 ADotF2(inAF2 a,inAF2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AF1 ADotF3(inAF3 a,inAF3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AF1 ADotF4(inAF4 a,inAF4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AExp2D1(AD1 a){return __builtin_exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return __builtin_exp2f(a);} + #else + A_STATIC AD1 AExp2D1(AD1 a){return exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return exp2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AFloorD1(AD1 a){return __builtin_floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return __builtin_floorf(a);} + #else + A_STATIC AD1 AFloorD1(AD1 a){return floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return floorf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ALerpD1(AD1 a,AD1 b,AD1 c){return b*c+(-a*c+a);} + A_STATIC AF1 ALerpF1(AF1 a,AF1 b,AF1 c){return b*c+(-a*c+a);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ALog2D1(AD1 a){return __builtin_log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return __builtin_log2f(a);} + #else + A_STATIC AD1 ALog2D1(AD1 a){return log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return log2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMaxD1(AD1 a,AD1 b){return a>b?a:b;} + A_STATIC AF1 AMaxF1(AF1 a,AF1 b){return a>b?a:b;} + A_STATIC AL1 AMaxL1(AL1 a,AL1 b){return a>b?a:b;} + A_STATIC AU1 AMaxU1(AU1 a,AU1 b){return a>b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + // These follow the convention that A integer types don't have signage, until they are operated on. + A_STATIC AL1 AMaxSL1(AL1 a,AL1 b){return (ASL1_(a)>ASL1_(b))?a:b;} + A_STATIC AU1 AMaxSU1(AU1 a,AU1 b){return (ASU1_(a)>ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMinD1(AD1 a,AD1 b){return a<b?a:b;} + A_STATIC AF1 AMinF1(AF1 a,AF1 b){return a<b?a:b;} + A_STATIC AL1 AMinL1(AL1 a,AL1 b){return a<b?a:b;} + A_STATIC AU1 AMinU1(AU1 a,AU1 b){return a<b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AL1 AMinSL1(AL1 a,AL1 b){return (ASL1_(a)<ASL1_(b))?a:b;} + A_STATIC AU1 AMinSU1(AU1 a,AU1 b){return (ASU1_(a)<ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARcpD1(AD1 a){return 1.0/a;} + A_STATIC AF1 ARcpF1(AF1 a){return 1.0f/a;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AL1 AShrSL1(AL1 a,AL1 b){return AL1_(ASL1_(a)>>ASL1_(b));} + A_STATIC AU1 AShrSU1(AU1 a,AU1 b){return AU1_(ASU1_(a)>>ASU1_(b));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASinD1(AD1 a){return __builtin_sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return __builtin_sinf(a);} + #else + A_STATIC AD1 ASinD1(AD1 a){return sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return sinf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASqrtD1(AD1 a){return __builtin_sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return __builtin_sqrtf(a);} + #else + A_STATIC AD1 ASqrtD1(AD1 a){return sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return sqrtf(a);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + A_STATIC AD1 AClampD1(AD1 x,AD1 n,AD1 m){return AMaxD1(n,AMinD1(x,m));} + A_STATIC AF1 AClampF1(AF1 x,AF1 n,AF1 m){return AMaxF1(n,AMinF1(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AFractD1(AD1 a){return a-AFloorD1(a);} + A_STATIC AF1 AFractF1(AF1 a){return a-AFloorF1(a);} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 APowD1(AD1 a,AD1 b){return AExp2D1(b*ALog2D1(a));} + A_STATIC AF1 APowF1(AF1 a,AF1 b){return AExp2F1(b*ALog2F1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARsqD1(AD1 a){return ARcpD1(ASqrtD1(a));} + A_STATIC AF1 ARsqF1(AF1 a){return ARcpF1(ASqrtF1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ASatD1(AD1 a){return AMinD1(1.0,AMaxD1(0.0,a));} + A_STATIC AF1 ASatF1(AF1 a){return AMinF1(1.0f,AMaxF1(0.0f,a));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + A_STATIC retAD2 opAAbsD2(outAD2 d,inAD2 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);return d;} + A_STATIC retAD3 opAAbsD3(outAD3 d,inAD3 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);return d;} + A_STATIC retAD4 opAAbsD4(outAD4 d,inAD4 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);d[3]=AAbsD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAbsF2(outAF2 d,inAF2 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);return d;} + A_STATIC retAF3 opAAbsF3(outAF3 d,inAF3 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);return d;} + A_STATIC retAF4 opAAbsF4(outAF4 d,inAF4 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);d[3]=AAbsF1(a[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opACpyD2(outAD2 d,inAD2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAD3 opACpyD3(outAD3 d,inAD3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAD4 opACpyD4(outAD4 d,inAD4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opACpyF2(outAF2 d,inAF2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAF3 opACpyF3(outAF3 d,inAF3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAF4 opACpyF4(outAF4 d,inAF4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);return d;} + A_STATIC retAD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);return d;} + A_STATIC retAD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);d[3]=ALerpD1(a[3],b[3],c[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);return d;} + A_STATIC retAF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);return d;} + A_STATIC retAF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);d[3]=ALerpF1(a[3],b[3],c[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);return d;} + A_STATIC retAD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);return d;} + A_STATIC retAD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);d[3]=ALerpD1(a[3],b[3],c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);return d;} + A_STATIC retAF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);return d;} + A_STATIC retAF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);d[3]=ALerpF1(a[3],b[3],c);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);d[3]=AMaxD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);d[3]=AMaxF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);d[3]=AMinD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);d[3]=AMinF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opANegD2(outAD2 d,inAD2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAD3 opANegD3(outAD3 d,inAD3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAD4 opANegD4(outAD4 d,inAD4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opANegF2(outAF2 d,inAF2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAF3 opANegF3(outAF3 d,inAF3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAF4 opANegF4(outAF4 d,inAF4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opARcpD2(outAD2 d,inAD2 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);return d;} + A_STATIC retAD3 opARcpD3(outAD3 d,inAD3 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);return d;} + A_STATIC retAD4 opARcpD4(outAD4 d,inAD4 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);d[3]=ARcpD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opARcpF2(outAF2 d,inAF2 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);return d;} + A_STATIC retAF3 opARcpF3(outAF3 d,inAF3 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);return d;} + A_STATIC retAF4 opARcpF4(outAF4 d,inAF4 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);d[3]=ARcpF1(a[3]);return d;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF FLOAT PACKING +//============================================================================================================================== + // Convert float to half (in lower 16-bits of output). + // Same fast technique as documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf + // Supports denormals. + // Conversion rules are to make computations possibly "safer" on the GPU, + // -INF & -NaN -> -65504 + // +INF & +NaN -> +65504 + A_STATIC AU1 AU1_AH1_AF1(AF1 f){ + static AW1 base[512]={ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100, + 0x0200,0x0400,0x0800,0x0c00,0x1000,0x1400,0x1800,0x1c00,0x2000,0x2400,0x2800,0x2c00,0x3000,0x3400,0x3800,0x3c00, + 0x4000,0x4400,0x4800,0x4c00,0x5000,0x5400,0x5800,0x5c00,0x6000,0x6400,0x6800,0x6c00,0x7000,0x7400,0x7800,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8001,0x8002,0x8004,0x8008,0x8010,0x8020,0x8040,0x8080,0x8100, + 0x8200,0x8400,0x8800,0x8c00,0x9000,0x9400,0x9800,0x9c00,0xa000,0xa400,0xa800,0xac00,0xb000,0xb400,0xb800,0xbc00, + 0xc000,0xc400,0xc800,0xcc00,0xd000,0xd400,0xd800,0xdc00,0xe000,0xe400,0xe800,0xec00,0xf000,0xf400,0xf800,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff}; + static AB1 shift[512]={ + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}; + union{AF1 f;AU1 u;}bits;bits.f=f;AU1 u=bits.u;AU1 i=u>>23;return (AU1)(base[i])+((u&0x7fffff)>>shift[i]);} +//------------------------------------------------------------------------------------------------------------------------------ + // Used to output packed constant. + A_STATIC AU1 AU1_AH2_AF2(inAF2 a){return AU1_AH1_AF1(a[0])+(AU1_AH1_AF1(a[1])<<16);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GLSL +// +// +//============================================================================================================================== +#if defined(A_GLSL) && defined(A_GPU) + #ifndef A_SKIP_EXT + #ifdef A_HALF + #extension GL_EXT_shader_16bit_storage:require + #extension GL_EXT_shader_explicit_arithmetic_types:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_LONG + #extension GL_ARB_gpu_shader_int64:require + #extension GL_NV_shader_atomic_int64:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_WAVE + #extension GL_KHR_shader_subgroup_arithmetic:require + #extension GL_KHR_shader_subgroup_ballot:require + #extension GL_KHR_shader_subgroup_quad:require + #extension GL_KHR_shader_subgroup_shuffle:require + #endif + #endif +//============================================================================================================================== + #define AP1 bool + #define AP2 bvec2 + #define AP3 bvec3 + #define AP4 bvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 vec2 + #define AF3 vec3 + #define AF4 vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uvec2 + #define AU3 uvec3 + #define AU4 uvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 ivec2 + #define ASU3 ivec3 + #define ASU4 ivec4 +//============================================================================================================================== + #define AF1_AU1(x) uintBitsToFloat(AU1(x)) + #define AF2_AU2(x) uintBitsToFloat(AU2(x)) + #define AF3_AU3(x) uintBitsToFloat(AU3(x)) + #define AF4_AU4(x) uintBitsToFloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) floatBitsToUint(AF1(x)) + #define AU2_AF2(x) floatBitsToUint(AF2(x)) + #define AU3_AF3(x) floatBitsToUint(AF3(x)) + #define AU4_AF4(x) floatBitsToUint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return packHalf2x16(AF2(a,0.0));} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2_AF2 packHalf2x16 + #define AU1_AW2Unorm_AF2 packUnorm2x16 + #define AU1_AB4Unorm_AF4 packUnorm4x8 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF2_AH2_AU1 unpackHalf2x16 + #define AF2_AW2Unorm_AU1 unpackUnorm2x16 + #define AF4_AB4Unorm_AU1 unpackUnorm4x8 +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){return bitfieldExtract(src,ASU1(off),ASU1(bits));} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + // Proxy for V_BFI_B32 where the 'mask' is set as 'bits', 'mask=(1<<bits)-1', and 'bits' needs to be an immediate. + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){return bitfieldInsert(src,ins,0,ASU1(bits));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MED3_F32. + AF1 AClampF1(AF1 x,AF1 n,AF1 m){return clamp(x,n,m);} + AF2 AClampF2(AF2 x,AF2 n,AF2 m){return clamp(x,n,m);} + AF3 AClampF3(AF3 x,AF3 n,AF3 m){return clamp(x,n,m);} + AF4 AClampF4(AF4 x,AF4 n,AF4 m){return clamp(x,n,m);} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F32 (note DX frac() is different). + AF1 AFractF1(AF1 x){return fract(x);} + AF2 AFractF2(AF2 x){return fract(x);} + AF3 AFractF3(AF3 x){return fract(x);} + AF4 AFractF4(AF4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ALerpF1(AF1 x,AF1 y,AF1 a){return mix(x,y,a);} + AF2 ALerpF2(AF2 x,AF2 y,AF2 a){return mix(x,y,a);} + AF3 ALerpF3(AF3 x,AF3 y,AF3 a){return mix(x,y,a);} + AF4 ALerpF4(AF4 x,AF4 y,AF4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MAX3_F32. + AF1 AMax3F1(AF1 x,AF1 y,AF1 z){return max(x,max(y,z));} + AF2 AMax3F2(AF2 x,AF2 y,AF2 z){return max(x,max(y,z));} + AF3 AMax3F3(AF3 x,AF3 y,AF3 z){return max(x,max(y,z));} + AF4 AMax3F4(AF4 x,AF4 y,AF4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3SU1(AU1 x,AU1 y,AU1 z){return AU1(max(ASU1(x),max(ASU1(y),ASU1(z))));} + AU2 AMax3SU2(AU2 x,AU2 y,AU2 z){return AU2(max(ASU2(x),max(ASU2(y),ASU2(z))));} + AU3 AMax3SU3(AU3 x,AU3 y,AU3 z){return AU3(max(ASU3(x),max(ASU3(y),ASU3(z))));} + AU4 AMax3SU4(AU4 x,AU4 y,AU4 z){return AU4(max(ASU4(x),max(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3U1(AU1 x,AU1 y,AU1 z){return max(x,max(y,z));} + AU2 AMax3U2(AU2 x,AU2 y,AU2 z){return max(x,max(y,z));} + AU3 AMax3U3(AU3 x,AU3 y,AU3 z){return max(x,max(y,z));} + AU4 AMax3U4(AU4 x,AU4 y,AU4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMaxSU1(AU1 a,AU1 b){return AU1(max(ASU1(a),ASU1(b)));} + AU2 AMaxSU2(AU2 a,AU2 b){return AU2(max(ASU2(a),ASU2(b)));} + AU3 AMaxSU3(AU3 a,AU3 b){return AU3(max(ASU3(a),ASU3(b)));} + AU4 AMaxSU4(AU4 a,AU4 b){return AU4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Clamp has an easier pattern match for med3 when some ordering is known. + // V_MED3_F32. + AF1 AMed3F1(AF1 x,AF1 y,AF1 z){return max(min(x,y),min(max(x,y),z));} + AF2 AMed3F2(AF2 x,AF2 y,AF2 z){return max(min(x,y),min(max(x,y),z));} + AF3 AMed3F3(AF3 x,AF3 y,AF3 z){return max(min(x,y),min(max(x,y),z));} + AF4 AMed3F4(AF4 x,AF4 y,AF4 z){return max(min(x,y),min(max(x,y),z));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MIN3_F32. + AF1 AMin3F1(AF1 x,AF1 y,AF1 z){return min(x,min(y,z));} + AF2 AMin3F2(AF2 x,AF2 y,AF2 z){return min(x,min(y,z));} + AF3 AMin3F3(AF3 x,AF3 y,AF3 z){return min(x,min(y,z));} + AF4 AMin3F4(AF4 x,AF4 y,AF4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3SU1(AU1 x,AU1 y,AU1 z){return AU1(min(ASU1(x),min(ASU1(y),ASU1(z))));} + AU2 AMin3SU2(AU2 x,AU2 y,AU2 z){return AU2(min(ASU2(x),min(ASU2(y),ASU2(z))));} + AU3 AMin3SU3(AU3 x,AU3 y,AU3 z){return AU3(min(ASU3(x),min(ASU3(y),ASU3(z))));} + AU4 AMin3SU4(AU4 x,AU4 y,AU4 z){return AU4(min(ASU4(x),min(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3U1(AU1 x,AU1 y,AU1 z){return min(x,min(y,z));} + AU2 AMin3U2(AU2 x,AU2 y,AU2 z){return min(x,min(y,z));} + AU3 AMin3U3(AU3 x,AU3 y,AU3 z){return min(x,min(y,z));} + AU4 AMin3U4(AU4 x,AU4 y,AU4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMinSU1(AU1 a,AU1 b){return AU1(min(ASU1(a),ASU1(b)));} + AU2 AMinSU2(AU2 a,AU2 b){return AU2(min(ASU2(a),ASU2(b)));} + AU3 AMinSU3(AU3 a,AU3 b){return AU3(min(ASU3(a),ASU3(b)));} + AU4 AMinSU4(AU4 a,AU4 b){return AU4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Normalized trig. Valid input domain is {-256 to +256}. No GLSL compiler intrinsic exists to map to this currently. + // V_COS_F32. + AF1 ANCosF1(AF1 x){return cos(x*AF1_(A_2PI));} + AF2 ANCosF2(AF2 x){return cos(x*AF2_(A_2PI));} + AF3 ANCosF3(AF3 x){return cos(x*AF3_(A_2PI));} + AF4 ANCosF4(AF4 x){return cos(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + // Normalized trig. Valid input domain is {-256 to +256}. No GLSL compiler intrinsic exists to map to this currently. + // V_SIN_F32. + AF1 ANSinF1(AF1 x){return sin(x*AF1_(A_2PI));} + AF2 ANSinF2(AF2 x){return sin(x*AF2_(A_2PI));} + AF3 ANSinF3(AF3 x){return sin(x*AF3_(A_2PI));} + AF4 ANSinF4(AF4 x){return sin(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARcpF1(AF1 x){return AF1_(1.0)/x;} + AF2 ARcpF2(AF2 x){return AF2_(1.0)/x;} + AF3 ARcpF3(AF3 x){return AF3_(1.0)/x;} + AF4 ARcpF4(AF4 x){return AF4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARsqF1(AF1 x){return AF1_(1.0)/sqrt(x);} + AF2 ARsqF2(AF2 x){return AF2_(1.0)/sqrt(x);} + AF3 ARsqF3(AF3 x){return AF3_(1.0)/sqrt(x);} + AF4 ARsqF4(AF4 x){return AF4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASatF1(AF1 x){return clamp(x,AF1_(0.0),AF1_(1.0));} + AF2 ASatF2(AF2 x){return clamp(x,AF2_(0.0),AF2_(1.0));} + AF3 ASatF3(AF3 x){return clamp(x,AF3_(0.0),AF3_(1.0));} + AF4 ASatF4(AF4 x){return clamp(x,AF4_(0.0),AF4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AShrSU1(AU1 a,AU1 b){return AU1(ASU1(a)>>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #define AB1 uint8_t + #define AB2 u8vec2 + #define AB3 u8vec3 + #define AB4 u8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASB1 int8_t + #define ASB2 i8vec2 + #define ASB3 i8vec3 + #define ASB4 i8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + AB1 AB1_x(AB1 a){return AB1(a);} + AB2 AB2_x(AB1 a){return AB2(a,a);} + AB3 AB3_x(AB1 a){return AB3(a,a,a);} + AB4 AB4_x(AB1 a){return AB4(a,a,a,a);} + #define AB1_(a) AB1_x(AB1(a)) + #define AB2_(a) AB2_x(AB1(a)) + #define AB3_(a) AB3_x(AB1(a)) + #define AB4_(a) AB4_x(AB1(a)) + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 float16_t + #define AH2 f16vec2 + #define AH3 f16vec3 + #define AH4 f16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 u16vec2 + #define AW3 u16vec3 + #define AW4 u16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 i16vec2 + #define ASW3 i16vec3 + #define ASW4 i16vec4 +//============================================================================================================================== + #define AH2_AU1(x) unpackFloat2x16(AU1(x)) + AH4 AH4_AU2_x(AU2 x){return AH4(unpackFloat2x16(x.x),unpackFloat2x16(x.y));} + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) unpackUint2x16(AU1(x)) + #define AW4_AU2(x) unpackUint4x16(pack64(AU2(x))) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2(x) packFloat2x16(AH2(x)) + AU2 AU2_AH4_x(AH4 x){return AU2(packFloat2x16(x.xy),packFloat2x16(x.zw));} + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) packUint2x16(AW2(x)) + #define AU2_AW4(x) unpack32(packUint4x16(AW4(x))) +//============================================================================================================================== + #define AW1_AH1(x) halfBitsToUint16(AH1(x)) + #define AW2_AH2(x) halfBitsToUint16(AH2(x)) + #define AW3_AH3(x) halfBitsToUint16(AH3(x)) + #define AW4_AH4(x) halfBitsToUint16(AH4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AH1_AW1(x) uint16BitsToHalf(AW1(x)) + #define AH2_AW2(x) uint16BitsToHalf(AW2(x)) + #define AH3_AW3(x) uint16BitsToHalf(AW3(x)) + #define AH4_AW4(x) uint16BitsToHalf(AW4(x)) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return clamp(x,n,m);} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return clamp(x,n,m);} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return clamp(x,n,m);} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return clamp(x,n,m);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFractH1(AH1 x){return fract(x);} + AH2 AFractH2(AH2 x){return fract(x);} + AH3 AFractH3(AH3 x){return fract(x);} + AH4 AFractH4(AH4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return mix(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return mix(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return mix(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of max3. + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of min3. + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return AH1_(1.0)/x;} + AH2 ARcpH2(AH2 x){return AH2_(1.0)/x;} + AH3 ARcpH3(AH3 x){return AH3_(1.0)/x;} + AH4 ARcpH4(AH4 x){return AH4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return AH1_(1.0)/sqrt(x);} + AH2 ARsqH2(AH2 x){return AH2_(1.0)/sqrt(x);} + AH3 ARsqH3(AH3 x){return AH3_(1.0)/sqrt(x);} + AH4 ARsqH4(AH4 x){return AH4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return clamp(x,AH1_(0.0),AH1_(1.0));} + AH2 ASatH2(AH2 x){return clamp(x,AH2_(0.0),AH2_(1.0));} + AH3 ASatH3(AH3 x){return clamp(x,AH3_(0.0),AH3_(1.0));} + AH4 ASatH4(AH4 x){return clamp(x,AH4_(0.0),AH4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 dvec2 + #define AD3 dvec3 + #define AD4 dvec4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 x){return fract(x);} + AD2 AFractD2(AD2 x){return fract(x);} + AD3 AFractD3(AD3 x){return fract(x);} + AD4 AFractD4(AD4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return mix(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return mix(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return mix(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return AD1_(1.0)/x;} + AD2 ARcpD2(AD2 x){return AD2_(1.0)/x;} + AD3 ARcpD3(AD3 x){return AD3_(1.0)/x;} + AD4 ARcpD4(AD4 x){return AD4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return AD1_(1.0)/sqrt(x);} + AD2 ARsqD2(AD2 x){return AD2_(1.0)/sqrt(x);} + AD3 ARsqD3(AD3 x){return AD3_(1.0)/sqrt(x);} + AD4 ARsqD4(AD4 x){return AD4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return clamp(x,AD1_(0.0),AD1_(1.0));} + AD2 ASatD2(AD2 x){return clamp(x,AD2_(0.0),AD2_(1.0));} + AD3 ASatD3(AD3 x){return clamp(x,AD3_(0.0),AD3_(1.0));} + AD4 ASatD4(AD4 x){return clamp(x,AD4_(0.0),AD4_(1.0));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #define AL1 uint64_t + #define AL2 u64vec2 + #define AL3 u64vec3 + #define AL4 u64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1 int64_t + #define ASL2 i64vec2 + #define ASL3 i64vec3 + #define ASL4 i64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AL1_AU2(x) packUint2x32(AU2(x)) + #define AU2_AL1(x) unpackUint2x32(AL1(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AL1_x(AL1 a){return AL1(a);} + AL2 AL2_x(AL1 a){return AL2(a,a);} + AL3 AL3_x(AL1 a){return AL3(a,a,a);} + AL4 AL4_x(AL1 a){return AL4(a,a,a,a);} + #define AL1_(a) AL1_x(AL1(a)) + #define AL2_(a) AL2_x(AL1(a)) + #define AL3_(a) AL3_x(AL1(a)) + #define AL4_(a) AL4_x(AL1(a)) +//============================================================================================================================== + AL1 AAbsSL1(AL1 a){return AL1(abs(ASL1(a)));} + AL2 AAbsSL2(AL2 a){return AL2(abs(ASL2(a)));} + AL3 AAbsSL3(AL3 a){return AL3(abs(ASL3(a)));} + AL4 AAbsSL4(AL4 a){return AL4(abs(ASL4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMaxSL1(AL1 a,AL1 b){return AL1(max(ASU1(a),ASU1(b)));} + AL2 AMaxSL2(AL2 a,AL2 b){return AL2(max(ASU2(a),ASU2(b)));} + AL3 AMaxSL3(AL3 a,AL3 b){return AL3(max(ASU3(a),ASU3(b)));} + AL4 AMaxSL4(AL4 a,AL4 b){return AL4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMinSL1(AL1 a,AL1 b){return AL1(min(ASU1(a),ASU1(b)));} + AL2 AMinSL2(AL2 a,AL2 b){return AL2(min(ASU2(a),ASU2(b)));} + AL3 AMinSL3(AL3 a,AL3 b){return AL3(min(ASU3(a),ASU3(b)));} + AL4 AMinSL4(AL4 a,AL4 b){return AL4(min(ASU4(a),ASU4(b)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// WAVE OPERATIONS +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return subgroupShuffleXor(v,x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return subgroupShuffleXor(v,x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return subgroupShuffleXor(v,x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return subgroupShuffleXor(v,x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return subgroupShuffleXor(v,x);} + AU2 AWaveXorU2(AU2 v,AU1 x){return subgroupShuffleXor(v,x);} + AU3 AWaveXorU3(AU3 v,AU1 x){return subgroupShuffleXor(v,x);} + AU4 AWaveXorU4(AU4 v,AU1 x){return subgroupShuffleXor(v,x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(subgroupShuffleXor(AU1_AH2(v),x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(subgroupShuffleXor(AU2_AH4(v),x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(subgroupShuffleXor(AU1_AW2(v),x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU2(subgroupShuffleXor(AU2_AW4(v),x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// HLSL +// +// +//============================================================================================================================== +#if defined(A_HLSL) && defined(A_GPU) + #ifdef A_HLSL_6_2 + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float32_t + #define AF2 float32_t2 + #define AF3 float32_t3 + #define AF4 float32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint32_t + #define AU2 uint32_t2 + #define AU3 uint32_t3 + #define AU4 uint32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int32_t + #define ASU2 int32_t2 + #define ASU3 int32_t3 + #define ASU4 int32_t4 + #else + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 float2 + #define AF3 float3 + #define AF4 float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uint2 + #define AU3 uint3 + #define AU4 uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 int2 + #define ASU3 int3 + #define ASU4 int4 + #endif +//============================================================================================================================== + #define AF1_AU1(x) asfloat(AU1(x)) + #define AF2_AU2(x) asfloat(AU2(x)) + #define AF3_AU3(x) asfloat(AU3(x)) + #define AF4_AU4(x) asfloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) asuint(AF1(x)) + #define AU2_AF2(x) asuint(AF2(x)) + #define AU3_AF3(x) asuint(AF3(x)) + #define AU4_AF4(x) asuint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return f32tof16(a);} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_AF2_x(AF2 a){return f32tof16(a.x)|(f32tof16(a.y)<<16);} + #define AU1_AH2_AF2(a) AU1_AH2_AF2_x(AF2(a)) + #define AU1_AB4Unorm_AF4(x) D3DCOLORtoUBYTE4(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AF2 AF2_AH2_AU1_x(AU1 x){return AF2(f16tof32(x&0xFFFF),f16tof32(x>>16));} + #define AF2_AH2_AU1(x) AF2_AH2_AU1_x(AU1(x)) +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){AU1 mask=(1u<<bits)-1;return (src>>off)&mask;} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){AU1 mask=(1u<<bits)-1;return (ins&mask)|(src&(~mask));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AClampF1(AF1 x,AF1 n,AF1 m){return max(n,min(x,m));} + AF2 AClampF2(AF2 x,AF2 n,AF2 m){return max(n,min(x,m));} + AF3 AClampF3(AF3 x,AF3 n,AF3 m){return max(n,min(x,m));} + AF4 AClampF4(AF4 x,AF4 n,AF4 m){return max(n,min(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFractF1(AF1 x){return x-floor(x);} + AF2 AFractF2(AF2 x){return x-floor(x);} + AF3 AFractF3(AF3 x){return x-floor(x);} + AF4 AFractF4(AF4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ALerpF1(AF1 x,AF1 y,AF1 a){return lerp(x,y,a);} + AF2 ALerpF2(AF2 x,AF2 y,AF2 a){return lerp(x,y,a);} + AF3 ALerpF3(AF3 x,AF3 y,AF3 a){return lerp(x,y,a);} + AF4 ALerpF4(AF4 x,AF4 y,AF4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMax3F1(AF1 x,AF1 y,AF1 z){return max(x,max(y,z));} + AF2 AMax3F2(AF2 x,AF2 y,AF2 z){return max(x,max(y,z));} + AF3 AMax3F3(AF3 x,AF3 y,AF3 z){return max(x,max(y,z));} + AF4 AMax3F4(AF4 x,AF4 y,AF4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3SU1(AU1 x,AU1 y,AU1 z){return AU1(max(ASU1(x),max(ASU1(y),ASU1(z))));} + AU2 AMax3SU2(AU2 x,AU2 y,AU2 z){return AU2(max(ASU2(x),max(ASU2(y),ASU2(z))));} + AU3 AMax3SU3(AU3 x,AU3 y,AU3 z){return AU3(max(ASU3(x),max(ASU3(y),ASU3(z))));} + AU4 AMax3SU4(AU4 x,AU4 y,AU4 z){return AU4(max(ASU4(x),max(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3U1(AU1 x,AU1 y,AU1 z){return max(x,max(y,z));} + AU2 AMax3U2(AU2 x,AU2 y,AU2 z){return max(x,max(y,z));} + AU3 AMax3U3(AU3 x,AU3 y,AU3 z){return max(x,max(y,z));} + AU4 AMax3U4(AU4 x,AU4 y,AU4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMaxSU1(AU1 a,AU1 b){return AU1(max(ASU1(a),ASU1(b)));} + AU2 AMaxSU2(AU2 a,AU2 b){return AU2(max(ASU2(a),ASU2(b)));} + AU3 AMaxSU3(AU3 a,AU3 b){return AU3(max(ASU3(a),ASU3(b)));} + AU4 AMaxSU4(AU4 a,AU4 b){return AU4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMed3F1(AF1 x,AF1 y,AF1 z){return max(min(x,y),min(max(x,y),z));} + AF2 AMed3F2(AF2 x,AF2 y,AF2 z){return max(min(x,y),min(max(x,y),z));} + AF3 AMed3F3(AF3 x,AF3 y,AF3 z){return max(min(x,y),min(max(x,y),z));} + AF4 AMed3F4(AF4 x,AF4 y,AF4 z){return max(min(x,y),min(max(x,y),z));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMin3F1(AF1 x,AF1 y,AF1 z){return min(x,min(y,z));} + AF2 AMin3F2(AF2 x,AF2 y,AF2 z){return min(x,min(y,z));} + AF3 AMin3F3(AF3 x,AF3 y,AF3 z){return min(x,min(y,z));} + AF4 AMin3F4(AF4 x,AF4 y,AF4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3SU1(AU1 x,AU1 y,AU1 z){return AU1(min(ASU1(x),min(ASU1(y),ASU1(z))));} + AU2 AMin3SU2(AU2 x,AU2 y,AU2 z){return AU2(min(ASU2(x),min(ASU2(y),ASU2(z))));} + AU3 AMin3SU3(AU3 x,AU3 y,AU3 z){return AU3(min(ASU3(x),min(ASU3(y),ASU3(z))));} + AU4 AMin3SU4(AU4 x,AU4 y,AU4 z){return AU4(min(ASU4(x),min(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3U1(AU1 x,AU1 y,AU1 z){return min(x,min(y,z));} + AU2 AMin3U2(AU2 x,AU2 y,AU2 z){return min(x,min(y,z));} + AU3 AMin3U3(AU3 x,AU3 y,AU3 z){return min(x,min(y,z));} + AU4 AMin3U4(AU4 x,AU4 y,AU4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMinSU1(AU1 a,AU1 b){return AU1(min(ASU1(a),ASU1(b)));} + AU2 AMinSU2(AU2 a,AU2 b){return AU2(min(ASU2(a),ASU2(b)));} + AU3 AMinSU3(AU3 a,AU3 b){return AU3(min(ASU3(a),ASU3(b)));} + AU4 AMinSU4(AU4 a,AU4 b){return AU4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ANCosF1(AF1 x){return cos(x*AF1_(A_2PI));} + AF2 ANCosF2(AF2 x){return cos(x*AF2_(A_2PI));} + AF3 ANCosF3(AF3 x){return cos(x*AF3_(A_2PI));} + AF4 ANCosF4(AF4 x){return cos(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ANSinF1(AF1 x){return sin(x*AF1_(A_2PI));} + AF2 ANSinF2(AF2 x){return sin(x*AF2_(A_2PI));} + AF3 ANSinF3(AF3 x){return sin(x*AF3_(A_2PI));} + AF4 ANSinF4(AF4 x){return sin(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARcpF1(AF1 x){return rcp(x);} + AF2 ARcpF2(AF2 x){return rcp(x);} + AF3 ARcpF3(AF3 x){return rcp(x);} + AF4 ARcpF4(AF4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARsqF1(AF1 x){return rsqrt(x);} + AF2 ARsqF2(AF2 x){return rsqrt(x);} + AF3 ARsqF3(AF3 x){return rsqrt(x);} + AF4 ARsqF4(AF4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASatF1(AF1 x){return saturate(x);} + AF2 ASatF2(AF2 x){return saturate(x);} + AF3 ASatF3(AF3 x){return saturate(x);} + AF4 ASatF4(AF4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AShrSU1(AU1 a,AU1 b){return AU1(ASU1(a)>>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define AH1 float16_t + #define AH2 float16_t2 + #define AH3 float16_t3 + #define AH4 float16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 uint16_t2 + #define AW3 uint16_t3 + #define AW4 uint16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 int16_t2 + #define ASW3 int16_t3 + #define ASW4 int16_t4 + #else + #define AH1 min16float + #define AH2 min16float2 + #define AH3 min16float3 + #define AH4 min16float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 min16uint + #define AW2 min16uint2 + #define AW3 min16uint3 + #define AW4 min16uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 min16int + #define ASW2 min16int2 + #define ASW3 min16int3 + #define ASW4 min16int4 + #endif +//============================================================================================================================== + // Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). + // Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ + AH2 AH2_AU1_x(AU1 x){AF2 t=f16tof32(AU2(x&0xFFFF,x>>16));return AH2(t);} + AH4 AH4_AU2_x(AU2 x){return AH4(AH2_AU1_x(x.x),AH2_AU1_x(x.y));} + AW2 AW2_AU1_x(AU1 x){AU2 t=AU2(x&0xFFFF,x>>16);return AW2(t);} + AW4 AW4_AU2_x(AU2 x){return AW4(AW2_AU1_x(x.x),AW2_AU1_x(x.y));} + #define AH2_AU1(x) AH2_AU1_x(AU1(x)) + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) AW2_AU1_x(AU1(x)) + #define AW4_AU2(x) AW4_AU2_x(AU2(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_x(AH2 x){return f32tof16(x.x)+(f32tof16(x.y)<<16);} + AU2 AU2_AH4_x(AH4 x){return AU2(AU1_AH2_x(x.xy),AU1_AH2_x(x.zw));} + AU1 AU1_AW2_x(AW2 x){return AU1(x.x)+(AU1(x.y)<<16);} + AU2 AU2_AW4_x(AW4 x){return AU2(AU1_AW2_x(x.xy),AU1_AW2_x(x.zw));} + #define AU1_AH2(x) AU1_AH2_x(AH2(x)) + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) AU1_AW2_x(AW2(x)) + #define AU2_AW4(x) AU2_AW4_x(AW4(x)) +//============================================================================================================================== + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AW1_AH1(x) asuint16(x) + #define AW2_AH2(x) asuint16(x) + #define AW3_AH3(x) asuint16(x) + #define AW4_AH4(x) asuint16(x) + #else + #define AW1_AH1(a) AW1(f32tof16(AF1(a))) + #define AW2_AH2(a) AW2(AW1_AH1((a).x),AW1_AH1((a).y)) + #define AW3_AH3(a) AW3(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z)) + #define AW4_AH4(a) AW4(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z),AW1_AH1((a).w)) + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AH1_AW1(x) asfloat16(x) + #define AH2_AW2(x) asfloat16(x) + #define AH3_AW3(x) asfloat16(x) + #define AH4_AW4(x) asfloat16(x) + #else + #define AH1_AW1(a) AH1(f16tof32(AU1(a))) + #define AH2_AW2(a) AH2(AH1_AW1((a).x),AH1_AW1((a).y)) + #define AH3_AW3(a) AH3(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z)) + #define AH4_AW4(a) AH4(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z),AH1_AW1((a).w)) + #endif +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return max(n,min(x,m));} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return max(n,min(x,m));} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return max(n,min(x,m));} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return max(n,min(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F16 (note DX frac() is different). + AH1 AFractH1(AH1 x){return x-floor(x);} + AH2 AFractH2(AH2 x){return x-floor(x);} + AH3 AFractH3(AH3 x){return x-floor(x);} + AH4 AFractH4(AH4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return lerp(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return lerp(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return lerp(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return rcp(x);} + AH2 ARcpH2(AH2 x){return rcp(x);} + AH3 ARcpH3(AH3 x){return rcp(x);} + AH4 ARcpH4(AH4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return rsqrt(x);} + AH2 ARsqH2(AH2 x){return rsqrt(x);} + AH3 ARsqH3(AH3 x){return rsqrt(x);} + AH4 ARsqH4(AH4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return saturate(x);} + AH2 ASatH2(AH2 x){return saturate(x);} + AH3 ASatH3(AH3 x){return saturate(x);} + AH4 ASatH4(AH4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #ifdef A_HLSL_6_2 + #define AD1 float64_t + #define AD2 float64_t2 + #define AD3 float64_t3 + #define AD4 float64_t4 + #else + #define AD1 double + #define AD2 double2 + #define AD3 double3 + #define AD4 double4 + #endif +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 a){return a-floor(a);} + AD2 AFractD2(AD2 a){return a-floor(a);} + AD3 AFractD3(AD3 a){return a-floor(a);} + AD4 AFractD4(AD4 a){return a-floor(a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return lerp(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return lerp(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return lerp(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return rcp(x);} + AD2 ARcpD2(AD2 x){return rcp(x);} + AD3 ARcpD3(AD3 x){return rcp(x);} + AD4 ARcpD4(AD4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return rsqrt(x);} + AD2 ARsqD2(AD2 x){return rsqrt(x);} + AD3 ARsqD3(AD3 x){return rsqrt(x);} + AD4 ARsqD4(AD4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return saturate(x);} + AD2 ASatD2(AD2 x){return saturate(x);} + AD3 ASatD3(AD3 x){return saturate(x);} + AD4 ASatD4(AD4 x){return saturate(x);} + #endif +//============================================================================================================================== +// HLSL WAVE +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU2 AWaveXorU1(AU2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU3 AWaveXorU1(AU3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU4 AWaveXorU1(AU4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(WaveReadLaneAt(AU1_AH2(v),WaveGetLaneIndex()^x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(WaveReadLaneAt(AU2_AH4(v),WaveGetLaneIndex()^x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(WaveReadLaneAt(AU1_AW2(v),WaveGetLaneIndex()^x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU1(WaveReadLaneAt(AU1_AW4(v),WaveGetLaneIndex()^x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU COMMON +// +// +//============================================================================================================================== +#ifdef A_GPU + // Negative and positive infinity. + #define A_INFP_F AF1_AU1(0x7f800000u) + #define A_INFN_F AF1_AU1(0xff800000u) +//------------------------------------------------------------------------------------------------------------------------------ + // Copy sign from 's' to positive 'd'. + AF1 ACpySgnF1(AF1 d,AF1 s){return AF1_AU1(AU1_AF1(d)|(AU1_AF1(s)&AU1_(0x80000000u)));} + AF2 ACpySgnF2(AF2 d,AF2 s){return AF2_AU2(AU2_AF2(d)|(AU2_AF2(s)&AU2_(0x80000000u)));} + AF3 ACpySgnF3(AF3 d,AF3 s){return AF3_AU3(AU3_AF3(d)|(AU3_AF3(s)&AU3_(0x80000000u)));} + AF4 ACpySgnF4(AF4 d,AF4 s){return AF4_AU4(AU4_AF4(d)|(AU4_AF4(s)&AU4_(0x80000000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Single operation to return (useful to create a mask to use in lerp for branch free logic), + // m=NaN := 0 + // m>=0 := 0 + // m<0 := 1 + // Uses the following useful floating point logic, + // saturate(+a*(-INF)==-INF) := 0 + // saturate( 0*(-INF)== NaN) := 0 + // saturate(-a*(-INF)==+INF) := 1 + AF1 ASignedF1(AF1 m){return ASatF1(m*AF1_(A_INFN_F));} + AF2 ASignedF2(AF2 m){return ASatF2(m*AF2_(A_INFN_F));} + AF3 ASignedF3(AF3 m){return ASatF3(m*AF3_(A_INFN_F));} + AF4 ASignedF4(AF4 m){return ASatF4(m*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AGtZeroF1(AF1 m){return ASatF1(m*AF1_(A_INFP_F));} + AF2 AGtZeroF2(AF2 m){return ASatF2(m*AF2_(A_INFP_F));} + AF3 AGtZeroF3(AF3 m){return ASatF3(m*AF3_(A_INFP_F));} + AF4 AGtZeroF4(AF4 m){return ASatF4(m*AF4_(A_INFP_F));} +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define A_INFP_H AH1_AW1((uint16_t)0x7c00u) + #define A_INFN_H AH1_AW1((uint16_t)0xfc00u) + #else + #define A_INFP_H AH1_AW1(0x7c00u) + #define A_INFN_H AH1_AW1(0xfc00u) + #endif + +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ACpySgnH1(AH1 d,AH1 s){return AH1_AW1(AW1_AH1(d)|(AW1_AH1(s)&AW1_(0x8000u)));} + AH2 ACpySgnH2(AH2 d,AH2 s){return AH2_AW2(AW2_AH2(d)|(AW2_AH2(s)&AW2_(0x8000u)));} + AH3 ACpySgnH3(AH3 d,AH3 s){return AH3_AW3(AW3_AH3(d)|(AW3_AH3(s)&AW3_(0x8000u)));} + AH4 ACpySgnH4(AH4 d,AH4 s){return AH4_AW4(AW4_AH4(d)|(AW4_AH4(s)&AW4_(0x8000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASignedH1(AH1 m){return ASatH1(m*AH1_(A_INFN_H));} + AH2 ASignedH2(AH2 m){return ASatH2(m*AH2_(A_INFN_H));} + AH3 ASignedH3(AH3 m){return ASatH3(m*AH3_(A_INFN_H));} + AH4 ASignedH4(AH4 m){return ASatH4(m*AH4_(A_INFN_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AGtZeroH1(AH1 m){return ASatH1(m*AH1_(A_INFP_H));} + AH2 AGtZeroH2(AH2 m){return ASatH2(m*AH2_(A_INFP_H));} + AH3 AGtZeroH3(AH3 m){return ASatH3(m*AH3_(A_INFP_H));} + AH4 AGtZeroH4(AH4 m){return ASatH4(m*AH4_(A_INFP_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [FIS] FLOAT INTEGER SORTABLE +//------------------------------------------------------------------------------------------------------------------------------ +// Float to integer sortable. +// - If sign bit=0, flip the sign bit (positives). +// - If sign bit=1, flip all bits (negatives). +// Integer sortable to float. +// - If sign bit=1, flip the sign bit (positives). +// - If sign bit=0, flip all bits (negatives). +// Has nice side effects. +// - Larger integers are more positive values. +// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). +// Burns 3 ops for conversion {shift,or,xor}. +//============================================================================================================================== + AU1 AFisToU1(AU1 x){return x^(( AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} + AU1 AFisFromU1(AU1 x){return x^((~AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + // Just adjust high 16-bit value (useful when upper part of 32-bit word is a 16-bit float value). + AU1 AFisToHiU1(AU1 x){return x^(( AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} + AU1 AFisFromHiU1(AU1 x){return x^((~AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AW1 AFisToW1(AW1 x){return x^(( AShrSW1(x,AW1_(15)))|AW1_(0x8000));} + AW1 AFisFromW1(AW1 x){return x^((~AShrSW1(x,AW1_(15)))|AW1_(0x8000));} +//------------------------------------------------------------------------------------------------------------------------------ + AW2 AFisToW2(AW2 x){return x^(( AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + AW2 AFisFromW2(AW2 x){return x^((~AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [PERM] V_PERM_B32 +//------------------------------------------------------------------------------------------------------------------------------ +// Support for V_PERM_B32 started in the 3rd generation of GCN. +//------------------------------------------------------------------------------------------------------------------------------ +// yyyyxxxx - The 'i' input. +// 76543210 +// ======== +// HGFEDCBA - Naming on permutation. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure compiler optimizes this. +//============================================================================================================================== + #ifdef A_HALF + AU1 APerm0E0A(AU2 i){return((i.x )&0xffu)|((i.y<<16)&0xff0000u);} + AU1 APerm0F0B(AU2 i){return((i.x>> 8)&0xffu)|((i.y<< 8)&0xff0000u);} + AU1 APerm0G0C(AU2 i){return((i.x>>16)&0xffu)|((i.y )&0xff0000u);} + AU1 APerm0H0D(AU2 i){return((i.x>>24)&0xffu)|((i.y>> 8)&0xff0000u);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermHGFA(AU2 i){return((i.x )&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGFC(AU2 i){return((i.x>>16)&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGAE(AU2 i){return((i.x<< 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHGCE(AU2 i){return((i.x>> 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHAFE(AU2 i){return((i.x<<16)&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermHCFE(AU2 i){return((i.x )&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermAGFE(AU2 i){return((i.x<<24)&0xff000000u)|(i.y&0x00ffffffu);} + AU1 APermCGFE(AU2 i){return((i.x<< 8)&0xff000000u)|(i.y&0x00ffffffu);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermGCEA(AU2 i){return((i.x)&0x00ff00ffu)|((i.y<<8)&0xff00ff00u);} + AU1 APermGECA(AU2 i){return(((i.x)&0xffu)|((i.x>>8)&0xff00u)|((i.y<<16)&0xff0000u)|((i.y<<8)&0xff000000u));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BUC] BYTE UNSIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Designed to use the optimal conversion, enables the scaling to possibly be factored into other computation. +// Works on a range of {0 to A_BUC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// OPCODE NOTES +// ============ +// GCN does not do UNORM or SNORM for bytes in opcodes. +// - V_CVT_F32_UBYTE{0,1,2,3} - Unsigned byte to float. +// - V_CVT_PKACC_U8_F32 - Float to unsigned byte (does bit-field insert into 32-bit integer). +// V_PERM_B32 does byte packing with ability to zero fill bytes as well. +// - Can pull out byte values from two sources, and zero fill upper 8-bits of packed hi and lo. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U1() - Designed for V_CVT_F32_UBYTE* and V_CVT_PKACCUM_U8_F32 ops. +// ==== ===== +// 0 : 0 +// 1 : 1 +// ... +// 255 : 255 +// : 256 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : 0 +// 1 : 1/512 +// 2 : 1/256 +// ... +// 64 : 1/8 +// 128 : 1/4 +// 255 : 255/512 +// : 1/2 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMAL IMPLEMENTATIONS ON AMD ARCHITECTURES +// ============================================ +// r=ABuc0FromU1(i) +// V_CVT_F32_UBYTE0 r,i +// -------------------------------------------- +// r=ABuc0ToU1(d,i) +// V_CVT_PKACCUM_U8_F32 r,i,0,d +// -------------------------------------------- +// d=ABuc0FromU2(i) +// Where 'k0' is an SGPR with 0x0E0A +// Where 'k1' is an SGPR with {32768.0} packed into the lower 16-bits +// V_PERM_B32 d,i.x,i.y,k0 +// V_PK_FMA_F16 d,d,k1.x,0 +// -------------------------------------------- +// r=ABuc0ToU2(d,i) +// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +// Where 'k1' is an SGPR with 0x???? +// Where 'k2' is an SGPR with 0x???? +// V_PK_FMA_F16 i,i,k0.x,0 +// V_PERM_B32 r.x,i,i,k1 +// V_PERM_B32 r.y,i,i,k2 +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BUC_32 (255.0) + #define A_BUC_16 (255.0/512.0) +//============================================================================================================================== + #if 1 + // Designed to be one V_CVT_PKACCUM_U8_F32. + // The extra min is required to pattern match to V_CVT_PKACCUM_U8_F32. + AU1 ABuc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i),255u) )&(0x000000ffu));} + AU1 ABuc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i),255u)<< 8)&(0x0000ff00u));} + AU1 ABuc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i),255u)<<16)&(0x00ff0000u));} + AU1 ABuc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed to be one V_CVT_F32_UBYTE*. + AF1 ABuc0FromU1(AU1 i){return AF1((i )&255u);} + AF1 ABuc1FromU1(AU1 i){return AF1((i>> 8)&255u);} + AF1 ABuc2FromU1(AU1 i){return AF1((i>>16)&255u);} + AF1 ABuc3FromU1(AU1 i){return AF1((i>>24)&255u);} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABuc01ToW2(AH2 x,AH2 y){x*=AH2_(1.0/32768.0);y*=AH2_(1.0/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 3 ops to do SOA to AOS and conversion. + AU2 ABuc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABuc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABuc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABuc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 2 ops to do both AOS to SOA, and conversion. + AH2 ABuc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0);} + AH2 ABuc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0);} + AH2 ABuc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0);} + AH2 ABuc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BSC] BYTE SIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Similar to [BUC]. +// Works on a range of {-/+ A_BSC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// ENCODING (without zero-based encoding) +// ======== +// 0 = unused (can be used to mean something else) +// 1 = lowest value +// 128 = exact zero center (zero based encoding +// 255 = highest value +//------------------------------------------------------------------------------------------------------------------------------ +// Zero-based [Zb] flips the MSB bit of the byte (making 128 "exact zero" actually zero). +// This is useful if there is a desire for cleared values to decode as zero. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABsc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : -127/512 (unused) +// 1 : -126/512 +// 2 : -125/512 +// ... +// 128 : 0 +// ... +// 255 : 127/512 +// : 1/4 (just outside the encoding range) +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BSC_32 (127.0) + #define A_BSC_16 (127.0/512.0) +//============================================================================================================================== + #if 1 + AU1 ABsc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i+128.0),255u) )&(0x000000ffu));} + AU1 ABsc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i+128.0),255u)<< 8)&(0x0000ff00u));} + AU1 ABsc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i+128.0),255u)<<16)&(0x00ff0000u));} + AU1 ABsc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i+128.0),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABsc0ToZbU1(AU1 d,AF1 i){return ((d&0xffffff00u)|((min(AU1(trunc(i)+128.0),255u) )&(0x000000ffu)))^0x00000080u;} + AU1 ABsc1ToZbU1(AU1 d,AF1 i){return ((d&0xffff00ffu)|((min(AU1(trunc(i)+128.0),255u)<< 8)&(0x0000ff00u)))^0x00008000u;} + AU1 ABsc2ToZbU1(AU1 d,AF1 i){return ((d&0xff00ffffu)|((min(AU1(trunc(i)+128.0),255u)<<16)&(0x00ff0000u)))^0x00800000u;} + AU1 ABsc3ToZbU1(AU1 d,AF1 i){return ((d&0x00ffffffu)|((min(AU1(trunc(i)+128.0),255u)<<24)&(0xff000000u)))^0x80000000u;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromU1(AU1 i){return AF1((i )&255u)-128.0;} + AF1 ABsc1FromU1(AU1 i){return AF1((i>> 8)&255u)-128.0;} + AF1 ABsc2FromU1(AU1 i){return AF1((i>>16)&255u)-128.0;} + AF1 ABsc3FromU1(AU1 i){return AF1((i>>24)&255u)-128.0;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromZbU1(AU1 i){return AF1(((i )&255u)^0x80u)-128.0;} + AF1 ABsc1FromZbU1(AU1 i){return AF1(((i>> 8)&255u)^0x80u)-128.0;} + AF1 ABsc2FromZbU1(AU1 i){return AF1(((i>>16)&255u)^0x80u)-128.0;} + AF1 ABsc3FromZbU1(AU1 i){return AF1(((i>>24)&255u)^0x80u)-128.0;} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABsc01ToW2(AH2 x,AH2 y){x=x*AH2_(1.0/32768.0)+AH2_(0.25/32768.0);y=y*AH2_(1.0/32768.0)+AH2_(0.25/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0)-AH2_(0.25);} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These support only positive inputs. +// Did not see value yet in specialization for range. +// Using quick testing, ended up mostly getting the same "best" approximation for various ranges. +// With hardware that can co-execute transcendentals, the value in approximations could be less than expected. +// However from a latency perspective, if execution of a transcendental is 4 clk, with no packed support, -> 8 clk total. +// And co-execution would require a compiler interleaving a lot of independent work for packed usage. +//------------------------------------------------------------------------------------------------------------------------------ +// The one Newton Raphson iteration form of rsq() was skipped (requires 6 ops total). +// Same with sqrt(), as this could be x*rsq() (7 ops). +//============================================================================================================================== + #ifdef A_HALF + // Minimize squared error across full positive range, 2 ops. + // The 0x1de2 based approximation maps {0 to 1} input maps to < 1 output. + AH1 APrxLoSqrtH1(AH1 a){return AH1_AW1((AW1_AH1(a)>>AW1_(1))+AW1_(0x1de2));} + AH2 APrxLoSqrtH2(AH2 a){return AH2_AW2((AW2_AH2(a)>>AW2_(1))+AW2_(0x1de2));} + AH3 APrxLoSqrtH3(AH3 a){return AH3_AW3((AW3_AH3(a)>>AW3_(1))+AW3_(0x1de2));} + AH4 APrxLoSqrtH4(AH4 a){return AH4_AW4((AW4_AH4(a)>>AW4_(1))+AW4_(0x1de2));} +//------------------------------------------------------------------------------------------------------------------------------ + // Lower precision estimation, 1 op. + // Minimize squared error across {smallest normal to 16384.0}. + AH1 APrxLoRcpH1(AH1 a){return AH1_AW1(AW1_(0x7784)-AW1_AH1(a));} + AH2 APrxLoRcpH2(AH2 a){return AH2_AW2(AW2_(0x7784)-AW2_AH2(a));} + AH3 APrxLoRcpH3(AH3 a){return AH3_AW3(AW3_(0x7784)-AW3_AH3(a));} + AH4 APrxLoRcpH4(AH4 a){return AH4_AW4(AW4_(0x7784)-AW4_AH4(a));} +//------------------------------------------------------------------------------------------------------------------------------ + // Medium precision estimation, one Newton Raphson iteration, 3 ops. + AH1 APrxMedRcpH1(AH1 a){AH1 b=AH1_AW1(AW1_(0x778d)-AW1_AH1(a));return b*(-b*a+AH1_(2.0));} + AH2 APrxMedRcpH2(AH2 a){AH2 b=AH2_AW2(AW2_(0x778d)-AW2_AH2(a));return b*(-b*a+AH2_(2.0));} + AH3 APrxMedRcpH3(AH3 a){AH3 b=AH3_AW3(AW3_(0x778d)-AW3_AH3(a));return b*(-b*a+AH3_(2.0));} + AH4 APrxMedRcpH4(AH4 a){AH4 b=AH4_AW4(AW4_(0x778d)-AW4_AH4(a));return b*(-b*a+AH4_(2.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // Minimize squared error across {smallest normal to 16384.0}, 2 ops. + AH1 APrxLoRsqH1(AH1 a){return AH1_AW1(AW1_(0x59a3)-(AW1_AH1(a)>>AW1_(1)));} + AH2 APrxLoRsqH2(AH2 a){return AH2_AW2(AW2_(0x59a3)-(AW2_AH2(a)>>AW2_(1)));} + AH3 APrxLoRsqH3(AH3 a){return AH3_AW3(AW3_(0x59a3)-(AW3_AH3(a)>>AW3_(1)));} + AH4 APrxLoRsqH4(AH4 a){return AH4_AW4(AW4_(0x59a3)-(AW4_AH4(a)>>AW4_(1)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// FLOAT APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// Michal Drobot has an excellent presentation on these: "Low Level Optimizations For GCN", +// - Idea dates back to SGI, then to Quake 3, etc. +// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +// - sqrt(x)=rsqrt(x)*x +// - rcp(x)=rsqrt(x)*rsqrt(x) for positive x +// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +//------------------------------------------------------------------------------------------------------------------------------ +// These below are from perhaps less complete searching for optimal. +// Used FP16 normal range for testing with +4096 32-bit step size for sampling error. +// So these match up well with the half approximations. +//============================================================================================================================== + AF1 APrxLoSqrtF1(AF1 a){return AF1_AU1((AU1_AF1(a)>>AU1_(1))+AU1_(0x1fbc4639));} + AF1 APrxLoRcpF1(AF1 a){return AF1_AU1(AU1_(0x7ef07ebb)-AU1_AF1(a));} + AF1 APrxMedRcpF1(AF1 a){AF1 b=AF1_AU1(AU1_(0x7ef19fff)-AU1_AF1(a));return b*(-b*a+AF1_(2.0));} + AF1 APrxLoRsqF1(AF1 a){return AF1_AU1(AU1_(0x5f347d74)-(AU1_AF1(a)>>AU1_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxLoSqrtF2(AF2 a){return AF2_AU2((AU2_AF2(a)>>AU2_(1))+AU2_(0x1fbc4639));} + AF2 APrxLoRcpF2(AF2 a){return AF2_AU2(AU2_(0x7ef07ebb)-AU2_AF2(a));} + AF2 APrxMedRcpF2(AF2 a){AF2 b=AF2_AU2(AU2_(0x7ef19fff)-AU2_AF2(a));return b*(-b*a+AF2_(2.0));} + AF2 APrxLoRsqF2(AF2 a){return AF2_AU2(AU2_(0x5f347d74)-(AU2_AF2(a)>>AU2_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxLoSqrtF3(AF3 a){return AF3_AU3((AU3_AF3(a)>>AU3_(1))+AU3_(0x1fbc4639));} + AF3 APrxLoRcpF3(AF3 a){return AF3_AU3(AU3_(0x7ef07ebb)-AU3_AF3(a));} + AF3 APrxMedRcpF3(AF3 a){AF3 b=AF3_AU3(AU3_(0x7ef19fff)-AU3_AF3(a));return b*(-b*a+AF3_(2.0));} + AF3 APrxLoRsqF3(AF3 a){return AF3_AU3(AU3_(0x5f347d74)-(AU3_AF3(a)>>AU3_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxLoSqrtF4(AF4 a){return AF4_AU4((AU4_AF4(a)>>AU4_(1))+AU4_(0x1fbc4639));} + AF4 APrxLoRcpF4(AF4 a){return AF4_AU4(AU4_(0x7ef07ebb)-AU4_AF4(a));} + AF4 APrxMedRcpF4(AF4 a){AF4 b=AF4_AU4(AU4_(0x7ef19fff)-AU4_AF4(a));return b*(-b*a+AF4_(2.0));} + AF4 APrxLoRsqF4(AF4 a){return AF4_AU4(AU4_(0x5f347d74)-(AU4_AF4(a)>>AU4_(1)));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PQ APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// PQ is very close to x^(1/8). The functions below Use the fast float approximation method to do +// PQ<~>Gamma2 (4th power and fast 4th root) and PQ<~>Linear (8th power and fast 8th root). Maximum error is ~0.2%. +//============================================================================================================================== +// Helpers + AF1 Quart(AF1 a) { a = a * a; return a * a;} + AF1 Oct(AF1 a) { a = a * a; a = a * a; return a * a; } + AF2 Quart(AF2 a) { a = a * a; return a * a; } + AF2 Oct(AF2 a) { a = a * a; a = a * a; return a * a; } + AF3 Quart(AF3 a) { a = a * a; return a * a; } + AF3 Oct(AF3 a) { a = a * a; a = a * a; return a * a; } + AF4 Quart(AF4 a) { a = a * a; return a * a; } + AF4 Oct(AF4 a) { a = a * a; a = a * a; return a * a; } + //------------------------------------------------------------------------------------------------------------------------------ + AF1 APrxPQToGamma2(AF1 a) { return Quart(a); } + AF1 APrxPQToLinear(AF1 a) { return Oct(a); } + AF1 APrxLoGamma2ToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); } + AF1 APrxMedGamma2ToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); AF1 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF1 APrxHighGamma2ToPQ(AF1 a) { return sqrt(sqrt(a)); } + AF1 APrxLoLinearToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); } + AF1 APrxMedLinearToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); AF1 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF1 APrxHighLinearToPQ(AF1 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxPQToGamma2(AF2 a) { return Quart(a); } + AF2 APrxPQToLinear(AF2 a) { return Oct(a); } + AF2 APrxLoGamma2ToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); } + AF2 APrxMedGamma2ToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); AF2 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF2 APrxHighGamma2ToPQ(AF2 a) { return sqrt(sqrt(a)); } + AF2 APrxLoLinearToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); } + AF2 APrxMedLinearToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); AF2 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF2 APrxHighLinearToPQ(AF2 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxPQToGamma2(AF3 a) { return Quart(a); } + AF3 APrxPQToLinear(AF3 a) { return Oct(a); } + AF3 APrxLoGamma2ToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); } + AF3 APrxMedGamma2ToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); AF3 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF3 APrxHighGamma2ToPQ(AF3 a) { return sqrt(sqrt(a)); } + AF3 APrxLoLinearToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); } + AF3 APrxMedLinearToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); AF3 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF3 APrxHighLinearToPQ(AF3 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxPQToGamma2(AF4 a) { return Quart(a); } + AF4 APrxPQToLinear(AF4 a) { return Oct(a); } + AF4 APrxLoGamma2ToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); } + AF4 APrxMedGamma2ToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); AF4 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF4 APrxHighGamma2ToPQ(AF4 a) { return sqrt(sqrt(a)); } + AF4 APrxLoLinearToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); } + AF4 APrxMedLinearToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); AF4 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF4 APrxHighLinearToPQ(AF4 a) { return sqrt(sqrt(sqrt(a))); } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PARABOLIC SIN & COS +//------------------------------------------------------------------------------------------------------------------------------ +// Approximate answers to transcendental questions. +//------------------------------------------------------------------------------------------------------------------------------ +//============================================================================================================================== + #if 1 + // Valid input range is {-1 to 1} representing {0 to 2 pi}. + // Output range is {-1/4 to 1/4} representing {-1 to 1}. + AF1 APSinF1(AF1 x){return x*abs(x)-x;} // MAD. + AF2 APSinF2(AF2 x){return x*abs(x)-x;} + AF1 APCosF1(AF1 x){x=AFractF1(x*AF1_(0.5)+AF1_(0.75));x=x*AF1_(2.0)-AF1_(1.0);return APSinF1(x);} // 3x MAD, FRACT + AF2 APCosF2(AF2 x){x=AFractF2(x*AF2_(0.5)+AF2_(0.75));x=x*AF2_(2.0)-AF2_(1.0);return APSinF2(x);} + AF2 APSinCosF1(AF1 x){AF1 y=AFractF1(x*AF1_(0.5)+AF1_(0.75));y=y*AF1_(2.0)-AF1_(1.0);return APSinF2(AF2(x,y));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + // For a packed {sin,cos} pair, + // - Native takes 16 clocks and 4 issue slots (no packed transcendentals). + // - Parabolic takes 8 clocks and 8 issue slots (only fract is non-packed). + AH1 APSinH1(AH1 x){return x*abs(x)-x;} + AH2 APSinH2(AH2 x){return x*abs(x)-x;} // AND,FMA + AH1 APCosH1(AH1 x){x=AFractH1(x*AH1_(0.5)+AH1_(0.75));x=x*AH1_(2.0)-AH1_(1.0);return APSinH1(x);} + AH2 APCosH2(AH2 x){x=AFractH2(x*AH2_(0.5)+AH2_(0.75));x=x*AH2_(2.0)-AH2_(1.0);return APSinH2(x);} // 3x FMA, 2xFRACT, AND + AH2 APSinCosH1(AH1 x){AH1 y=AFractH1(x*AH1_(0.5)+AH1_(0.75));y=y*AH1_(2.0)-AH1_(1.0);return APSinH2(AH2(x,y));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [ZOL] ZERO ONE LOGIC +//------------------------------------------------------------------------------------------------------------------------------ +// Conditional free logic designed for easy 16-bit packing, and backwards porting to 32-bit. +//------------------------------------------------------------------------------------------------------------------------------ +// 0 := false +// 1 := true +//------------------------------------------------------------------------------------------------------------------------------ +// AndNot(x,y) -> !(x&y) .... One op. +// AndOr(x,y,z) -> (x&y)|z ... One op. +// GtZero(x) -> x>0.0 ..... One op. +// Sel(x,y,z) -> x?y:z ..... Two ops, has no precision loss. +// Signed(x) -> x<0.0 ..... One op. +// ZeroPass(x,y) -> x?0:y ..... Two ops, 'y' is a pass through safe for aliasing as integer. +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMIZATION NOTES +// ================== +// - On Vega to use 2 constants in a packed op, pass in as one AW2 or one AH2 'k.xy' and use as 'k.xx' and 'k.yy'. +// For example 'a.xy*k.xx+k.yy'. +//============================================================================================================================== + #if 1 + AU1 AZolAndU1(AU1 x,AU1 y){return min(x,y);} + AU2 AZolAndU2(AU2 x,AU2 y){return min(x,y);} + AU3 AZolAndU3(AU3 x,AU3 y){return min(x,y);} + AU4 AZolAndU4(AU4 x,AU4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolNotU1(AU1 x){return x^AU1_(1);} + AU2 AZolNotU2(AU2 x){return x^AU2_(1);} + AU3 AZolNotU3(AU3 x){return x^AU3_(1);} + AU4 AZolNotU4(AU4 x){return x^AU4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolOrU1(AU1 x,AU1 y){return max(x,y);} + AU2 AZolOrU2(AU2 x,AU2 y){return max(x,y);} + AU3 AZolOrU3(AU3 x,AU3 y){return max(x,y);} + AU4 AZolOrU4(AU4 x,AU4 y){return max(x,y);} +//============================================================================================================================== + AU1 AZolF1ToU1(AF1 x){return AU1(x);} + AU2 AZolF2ToU2(AF2 x){return AU2(x);} + AU3 AZolF3ToU3(AF3 x){return AU3(x);} + AU4 AZolF4ToU4(AF4 x){return AU4(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // 2 ops, denormals don't work in 32-bit on PC (and if they are enabled, OMOD is disabled). + AU1 AZolNotF1ToU1(AF1 x){return AU1(AF1_(1.0)-x);} + AU2 AZolNotF2ToU2(AF2 x){return AU2(AF2_(1.0)-x);} + AU3 AZolNotF3ToU3(AF3 x){return AU3(AF3_(1.0)-x);} + AU4 AZolNotF4ToU4(AF4 x){return AU4(AF4_(1.0)-x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolU1ToF1(AU1 x){return AF1(x);} + AF2 AZolU2ToF2(AU2 x){return AF2(x);} + AF3 AZolU3ToF3(AU3 x){return AF3(x);} + AF4 AZolU4ToF4(AU4 x){return AF4(x);} +//============================================================================================================================== + AF1 AZolAndF1(AF1 x,AF1 y){return min(x,y);} + AF2 AZolAndF2(AF2 x,AF2 y){return min(x,y);} + AF3 AZolAndF3(AF3 x,AF3 y){return min(x,y);} + AF4 AZolAndF4(AF4 x,AF4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASolAndNotF1(AF1 x,AF1 y){return (-x)*y+AF1_(1.0);} + AF2 ASolAndNotF2(AF2 x,AF2 y){return (-x)*y+AF2_(1.0);} + AF3 ASolAndNotF3(AF3 x,AF3 y){return (-x)*y+AF3_(1.0);} + AF4 ASolAndNotF4(AF4 x,AF4 y){return (-x)*y+AF4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolAndOrF1(AF1 x,AF1 y,AF1 z){return ASatF1(x*y+z);} + AF2 AZolAndOrF2(AF2 x,AF2 y,AF2 z){return ASatF2(x*y+z);} + AF3 AZolAndOrF3(AF3 x,AF3 y,AF3 z){return ASatF3(x*y+z);} + AF4 AZolAndOrF4(AF4 x,AF4 y,AF4 z){return ASatF4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolGtZeroF1(AF1 x){return ASatF1(x*AF1_(A_INFP_F));} + AF2 AZolGtZeroF2(AF2 x){return ASatF2(x*AF2_(A_INFP_F));} + AF3 AZolGtZeroF3(AF3 x){return ASatF3(x*AF3_(A_INFP_F));} + AF4 AZolGtZeroF4(AF4 x){return ASatF4(x*AF4_(A_INFP_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolNotF1(AF1 x){return AF1_(1.0)-x;} + AF2 AZolNotF2(AF2 x){return AF2_(1.0)-x;} + AF3 AZolNotF3(AF3 x){return AF3_(1.0)-x;} + AF4 AZolNotF4(AF4 x){return AF4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolOrF1(AF1 x,AF1 y){return max(x,y);} + AF2 AZolOrF2(AF2 x,AF2 y){return max(x,y);} + AF3 AZolOrF3(AF3 x,AF3 y){return max(x,y);} + AF4 AZolOrF4(AF4 x,AF4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSelF1(AF1 x,AF1 y,AF1 z){AF1 r=(-x)*z+z;return x*y+r;} + AF2 AZolSelF2(AF2 x,AF2 y,AF2 z){AF2 r=(-x)*z+z;return x*y+r;} + AF3 AZolSelF3(AF3 x,AF3 y,AF3 z){AF3 r=(-x)*z+z;return x*y+r;} + AF4 AZolSelF4(AF4 x,AF4 y,AF4 z){AF4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSignedF1(AF1 x){return ASatF1(x*AF1_(A_INFN_F));} + AF2 AZolSignedF2(AF2 x){return ASatF2(x*AF2_(A_INFN_F));} + AF3 AZolSignedF3(AF3 x){return ASatF3(x*AF3_(A_INFN_F));} + AF4 AZolSignedF4(AF4 x){return ASatF4(x*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolZeroPassF1(AF1 x,AF1 y){return AF1_AU1((AU1_AF1(x)!=AU1_(0))?AU1_(0):AU1_AF1(y));} + AF2 AZolZeroPassF2(AF2 x,AF2 y){return AF2_AU2((AU2_AF2(x)!=AU2_(0))?AU2_(0):AU2_AF2(y));} + AF3 AZolZeroPassF3(AF3 x,AF3 y){return AF3_AU3((AU3_AF3(x)!=AU3_(0))?AU3_(0):AU3_AF3(y));} + AF4 AZolZeroPassF4(AF4 x,AF4 y){return AF4_AU4((AU4_AF4(x)!=AU4_(0))?AU4_(0):AU4_AF4(y));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AW1 AZolAndW1(AW1 x,AW1 y){return min(x,y);} + AW2 AZolAndW2(AW2 x,AW2 y){return min(x,y);} + AW3 AZolAndW3(AW3 x,AW3 y){return min(x,y);} + AW4 AZolAndW4(AW4 x,AW4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolNotW1(AW1 x){return x^AW1_(1);} + AW2 AZolNotW2(AW2 x){return x^AW2_(1);} + AW3 AZolNotW3(AW3 x){return x^AW3_(1);} + AW4 AZolNotW4(AW4 x){return x^AW4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolOrW1(AW1 x,AW1 y){return max(x,y);} + AW2 AZolOrW2(AW2 x,AW2 y){return max(x,y);} + AW3 AZolOrW3(AW3 x,AW3 y){return max(x,y);} + AW4 AZolOrW4(AW4 x,AW4 y){return max(x,y);} +//============================================================================================================================== + // Uses denormal trick. + AW1 AZolH1ToW1(AH1 x){return AW1_AH1(x*AH1_AW1(AW1_(1)));} + AW2 AZolH2ToW2(AH2 x){return AW2_AH2(x*AH2_AW2(AW2_(1)));} + AW3 AZolH3ToW3(AH3 x){return AW3_AH3(x*AH3_AW3(AW3_(1)));} + AW4 AZolH4ToW4(AH4 x){return AW4_AH4(x*AH4_AW4(AW4_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + // AMD arch lacks a packed conversion opcode. + AH1 AZolW1ToH1(AW1 x){return AH1_AW1(x*AW1_AH1(AH1_(1.0)));} + AH2 AZolW2ToH2(AW2 x){return AH2_AW2(x*AW2_AH2(AH2_(1.0)));} + AH3 AZolW1ToH3(AW3 x){return AH3_AW3(x*AW3_AH3(AH3_(1.0)));} + AH4 AZolW2ToH4(AW4 x){return AH4_AW4(x*AW4_AH4(AH4_(1.0)));} +//============================================================================================================================== + AH1 AZolAndH1(AH1 x,AH1 y){return min(x,y);} + AH2 AZolAndH2(AH2 x,AH2 y){return min(x,y);} + AH3 AZolAndH3(AH3 x,AH3 y){return min(x,y);} + AH4 AZolAndH4(AH4 x,AH4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASolAndNotH1(AH1 x,AH1 y){return (-x)*y+AH1_(1.0);} + AH2 ASolAndNotH2(AH2 x,AH2 y){return (-x)*y+AH2_(1.0);} + AH3 ASolAndNotH3(AH3 x,AH3 y){return (-x)*y+AH3_(1.0);} + AH4 ASolAndNotH4(AH4 x,AH4 y){return (-x)*y+AH4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolAndOrH1(AH1 x,AH1 y,AH1 z){return ASatH1(x*y+z);} + AH2 AZolAndOrH2(AH2 x,AH2 y,AH2 z){return ASatH2(x*y+z);} + AH3 AZolAndOrH3(AH3 x,AH3 y,AH3 z){return ASatH3(x*y+z);} + AH4 AZolAndOrH4(AH4 x,AH4 y,AH4 z){return ASatH4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolGtZeroH1(AH1 x){return ASatH1(x*AH1_(A_INFP_H));} + AH2 AZolGtZeroH2(AH2 x){return ASatH2(x*AH2_(A_INFP_H));} + AH3 AZolGtZeroH3(AH3 x){return ASatH3(x*AH3_(A_INFP_H));} + AH4 AZolGtZeroH4(AH4 x){return ASatH4(x*AH4_(A_INFP_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolNotH1(AH1 x){return AH1_(1.0)-x;} + AH2 AZolNotH2(AH2 x){return AH2_(1.0)-x;} + AH3 AZolNotH3(AH3 x){return AH3_(1.0)-x;} + AH4 AZolNotH4(AH4 x){return AH4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolOrH1(AH1 x,AH1 y){return max(x,y);} + AH2 AZolOrH2(AH2 x,AH2 y){return max(x,y);} + AH3 AZolOrH3(AH3 x,AH3 y){return max(x,y);} + AH4 AZolOrH4(AH4 x,AH4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSelH1(AH1 x,AH1 y,AH1 z){AH1 r=(-x)*z+z;return x*y+r;} + AH2 AZolSelH2(AH2 x,AH2 y,AH2 z){AH2 r=(-x)*z+z;return x*y+r;} + AH3 AZolSelH3(AH3 x,AH3 y,AH3 z){AH3 r=(-x)*z+z;return x*y+r;} + AH4 AZolSelH4(AH4 x,AH4 y,AH4 z){AH4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSignedH1(AH1 x){return ASatH1(x*AH1_(A_INFN_H));} + AH2 AZolSignedH2(AH2 x){return ASatH2(x*AH2_(A_INFN_H));} + AH3 AZolSignedH3(AH3 x){return ASatH3(x*AH3_(A_INFN_H));} + AH4 AZolSignedH4(AH4 x){return ASatH4(x*AH4_(A_INFN_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COLOR CONVERSIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These are all linear to/from some other space (where 'linear' has been shortened out of the function name). +// So 'ToGamma' is 'LinearToGamma', and 'FromGamma' is 'LinearFromGamma'. +// These are branch free implementations. +// The AToSrgbF1() function is useful for stores for compute shaders for GPUs without hardware linear->sRGB store conversion. +//------------------------------------------------------------------------------------------------------------------------------ +// TRANSFER FUNCTIONS +// ================== +// 709 ..... Rec709 used for some HDTVs +// Gamma ... Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native +// Pq ...... PQ native for HDR10 +// Srgb .... The sRGB output, typical of PC displays, useful for 10-bit output, or storing to 8-bit UNORM without SRGB type +// Two ..... Gamma 2.0, fastest conversion (useful for intermediate pass approximations) +// Three ... Gamma 3.0, less fast, but good for HDR. +//------------------------------------------------------------------------------------------------------------------------------ +// KEEPING TO SPEC +// =============== +// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +// Also there is a slight step in the transition regions. +// Precision of the coefficients in the spec being the likely cause. +// Main usage case of the sRGB code is to do the linear->sRGB converstion in a compute shader before store. +// This is to work around lack of hardware (typically only ROP does the conversion for free). +// To "correct" the linear segment, would be to introduce error, because hardware decode of sRGB->linear is fixed (and free). +// So this header keeps with the spec. +// For linear->sRGB transforms, the linear segment in some respects reduces error, because rounding in that region is linear. +// Rounding in the curved region in hardware (and fast software code) introduces error due to rounding in non-linear. +//------------------------------------------------------------------------------------------------------------------------------ +// FOR PQ +// ====== +// Both input and output is {0.0-1.0}, and where output 1.0 represents 10000.0 cd/m^2. +// All constants are only specified to FP32 precision. +// External PQ source reference, +// - https://github.com/ampas/aces-dev/blob/master/transforms/ctl/utilities/ACESlib.Utilities_Color.a1.0.1.ctl +//------------------------------------------------------------------------------------------------------------------------------ +// PACKED VERSIONS +// =============== +// These are the A*H2() functions. +// There is no PQ functions as FP16 seemed to not have enough precision for the conversion. +// The remaining functions are "good enough" for 8-bit, and maybe 10-bit if not concerned about a few 1-bit errors. +// Precision is lowest in the 709 conversion, higher in sRGB, higher still in Two and Gamma (when using 2.2 at least). +//------------------------------------------------------------------------------------------------------------------------------ +// NOTES +// ===== +// Could be faster for PQ conversions to be in ALU or a texture lookup depending on usage case. +//============================================================================================================================== + #if 1 + AF1 ATo709F1(AF1 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 ATo709F2(AF2 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 ATo709F3(AF3 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + // Note 'rcpX' is '1/x', where the 'x' is what would be used in AFromGamma(). + AF1 AToGammaF1(AF1 c,AF1 rcpX){return pow(c,AF1_(rcpX));} + AF2 AToGammaF2(AF2 c,AF1 rcpX){return pow(c,AF2_(rcpX));} + AF3 AToGammaF3(AF3 c,AF1 rcpX){return pow(c,AF3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToPqF1(AF1 x){AF1 p=pow(x,AF1_(0.159302)); + return pow((AF1_(0.835938)+AF1_(18.8516)*p)/(AF1_(1.0)+AF1_(18.6875)*p),AF1_(78.8438));} + AF2 AToPqF1(AF2 x){AF2 p=pow(x,AF2_(0.159302)); + return pow((AF2_(0.835938)+AF2_(18.8516)*p)/(AF2_(1.0)+AF2_(18.6875)*p),AF2_(78.8438));} + AF3 AToPqF1(AF3 x){AF3 p=pow(x,AF3_(0.159302)); + return pow((AF3_(0.835938)+AF3_(18.8516)*p)/(AF3_(1.0)+AF3_(18.6875)*p),AF3_(78.8438));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToSrgbF1(AF1 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 AToSrgbF2(AF2 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 AToSrgbF3(AF3 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToTwoF1(AF1 c){return sqrt(c);} + AF2 AToTwoF2(AF2 c){return sqrt(c);} + AF3 AToTwoF3(AF3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToThreeF1(AF1 c){return pow(c,AF1_(1.0/3.0));} + AF2 AToThreeF2(AF2 c){return pow(c,AF2_(1.0/3.0));} + AF3 AToThreeF3(AF3 c){return pow(c,AF3_(1.0/3.0));} + #endif +//============================================================================================================================== + #if 1 + // Unfortunately median won't work here. + AF1 AFrom709F1(AF1 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFrom709F2(AF2 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFrom709F3(AF3 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromGammaF1(AF1 c,AF1 x){return pow(c,AF1_(x));} + AF2 AFromGammaF2(AF2 c,AF1 x){return pow(c,AF2_(x));} + AF3 AFromGammaF3(AF3 c,AF1 x){return pow(c,AF3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromPqF1(AF1 x){AF1 p=pow(x,AF1_(0.0126833)); + return pow(ASatF1(p-AF1_(0.835938))/(AF1_(18.8516)-AF1_(18.6875)*p),AF1_(6.27739));} + AF2 AFromPqF1(AF2 x){AF2 p=pow(x,AF2_(0.0126833)); + return pow(ASatF2(p-AF2_(0.835938))/(AF2_(18.8516)-AF2_(18.6875)*p),AF2_(6.27739));} + AF3 AFromPqF1(AF3 x){AF3 p=pow(x,AF3_(0.0126833)); + return pow(ASatF3(p-AF3_(0.835938))/(AF3_(18.8516)-AF3_(18.6875)*p),AF3_(6.27739));} +//------------------------------------------------------------------------------------------------------------------------------ + // Unfortunately median won't work here. + AF1 AFromSrgbF1(AF1 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFromSrgbF2(AF2 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFromSrgbF3(AF3 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromTwoF1(AF1 c){return c*c;} + AF2 AFromTwoF2(AF2 c){return c*c;} + AF3 AFromTwoF3(AF3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromThreeF1(AF1 c){return c*c*c;} + AF2 AFromThreeF2(AF2 c){return c*c*c;} + AF3 AFromThreeF3(AF3 c){return c*c*c;} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 ATo709H1(AH1 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 ATo709H2(AH2 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 ATo709H3(AH3 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToGammaH1(AH1 c,AH1 rcpX){return pow(c,AH1_(rcpX));} + AH2 AToGammaH2(AH2 c,AH1 rcpX){return pow(c,AH2_(rcpX));} + AH3 AToGammaH3(AH3 c,AH1 rcpX){return pow(c,AH3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToSrgbH1(AH1 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 AToSrgbH2(AH2 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 AToSrgbH3(AH3 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToTwoH1(AH1 c){return sqrt(c);} + AH2 AToTwoH2(AH2 c){return sqrt(c);} + AH3 AToTwoH3(AH3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToThreeF1(AH1 c){return pow(c,AH1_(1.0/3.0));} + AH2 AToThreeF2(AH2 c){return pow(c,AH2_(1.0/3.0));} + AH3 AToThreeF3(AH3 c){return pow(c,AH3_(1.0/3.0));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 AFrom709H1(AH1 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AFrom709H2(AH2 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AFrom709H3(AH3 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromGammaH1(AH1 c,AH1 x){return pow(c,AH1_(x));} + AH2 AFromGammaH2(AH2 c,AH1 x){return pow(c,AH2_(x));} + AH3 AFromGammaH3(AH3 c,AH1 x){return pow(c,AH3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AHromSrgbF1(AH1 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AHromSrgbF2(AH2 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AHromSrgbF3(AH3 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromTwoH1(AH1 c){return c*c;} + AH2 AFromTwoH2(AH2 c){return c*c;} + AH3 AFromTwoH3(AH3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromThreeH1(AH1 c){return c*c*c;} + AH2 AFromThreeH2(AH2 c){return c*c*c;} + AH3 AFromThreeH3(AH3 c){return c*c*c;} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CS REMAP +//============================================================================================================================== + // Simple remap 64x1 to 8x8 with rotated 2x2 pixel quads in quad linear. + // 543210 + // ====== + // ..xxx. + // yy...y + AU2 ARmp8x8(AU1 a){return AU2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} +//============================================================================================================================== + // More complex remap 64x1 to 8x8 which is necessary for 2D wave reductions. + // 543210 + // ====== + // .xx..x + // y..yy. + // Details, + // LANE TO 8x8 MAPPING + // =================== + // 00 01 08 09 10 11 18 19 + // 02 03 0a 0b 12 13 1a 1b + // 04 05 0c 0d 14 15 1c 1d + // 06 07 0e 0f 16 17 1e 1f + // 20 21 28 29 30 31 38 39 + // 22 23 2a 2b 32 33 3a 3b + // 24 25 2c 2d 34 35 3c 3d + // 26 27 2e 2f 36 37 3e 3f + AU2 ARmpRed8x8(AU1 a){return AU2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} +//============================================================================================================================== + #ifdef A_HALF + AW2 ARmp8x8H(AU1 a){return AW2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} + AW2 ARmpRed8x8H(AU1 a){return AW2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} + #endif +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// REFERENCE +// +//------------------------------------------------------------------------------------------------------------------------------ +// IEEE FLOAT RULES +// ================ +// - saturate(NaN)=0, saturate(-INF)=0, saturate(+INF)=1 +// - {+/-}0 * {+/-}INF = NaN +// - -INF + (+INF) = NaN +// - {+/-}0 / {+/-}0 = NaN +// - {+/-}INF / {+/-}INF = NaN +// - a<(-0) := sqrt(a) = NaN (a=-0.0 won't NaN) +// - 0 == -0 +// - 4/0 = +INF +// - 4/-0 = -INF +// - 4+INF = +INF +// - 4-INF = -INF +// - 4*(+INF) = +INF +// - 4*(-INF) = -INF +// - -4*(+INF) = -INF +// - sqrt(+INF) = +INF +//------------------------------------------------------------------------------------------------------------------------------ +// FP16 ENCODING +// ============= +// fedcba9876543210 +// ---------------- +// ......mmmmmmmmmm 10-bit mantissa (encodes 11-bit 0.5 to 1.0 except for denormals) +// .eeeee.......... 5-bit exponent +// .00000.......... denormals +// .00001.......... -14 exponent +// .11110.......... 15 exponent +// .111110000000000 infinity +// .11111nnnnnnnnnn NaN with n!=0 +// s............... sign +//------------------------------------------------------------------------------------------------------------------------------ +// FP16/INT16 ALIASING DENORMAL +// ============================ +// 11-bit unsigned integers alias with half float denormal/normal values, +// 1 = 2^(-24) = 1/16777216 ....................... first denormal value +// 2 = 2^(-23) +// ... +// 1023 = 2^(-14)*(1-2^(-10)) = 2^(-14)*(1-1/1024) ... last denormal value +// 1024 = 2^(-14) = 1/16384 .......................... first normal value that still maps to integers +// 2047 .............................................. last normal value that still maps to integers +// Scaling limits, +// 2^15 = 32768 ...................................... largest power of 2 scaling +// Largest pow2 conversion mapping is at *32768, +// 1 : 2^(-9) = 1/512 +// 2 : 1/256 +// 4 : 1/128 +// 8 : 1/64 +// 16 : 1/32 +// 32 : 1/16 +// 64 : 1/8 +// 128 : 1/4 +// 256 : 1/2 +// 512 : 1 +// 1024 : 2 +// 2047 : a little less than 4 +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU/CPU PORTABILITY +// +// +//------------------------------------------------------------------------------------------------------------------------------ +// This is the GPU implementation. +// See the CPU implementation for docs. +//============================================================================================================================== +#ifdef A_GPU + #define A_TRUE true + #define A_FALSE false + #define A_STATIC +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD2 + #define retAD3 AD3 + #define retAD4 AD4 + #define retAF2 AF2 + #define retAF3 AF3 + #define retAF4 AF4 + #define retAL2 AL2 + #define retAL3 AL3 + #define retAL4 AL4 + #define retAU2 AU2 + #define retAU3 AU3 + #define retAU4 AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 in AD2 + #define inAD3 in AD3 + #define inAD4 in AD4 + #define inAF2 in AF2 + #define inAF3 in AF3 + #define inAF4 in AF4 + #define inAL2 in AL2 + #define inAL3 in AL3 + #define inAL4 in AL4 + #define inAU2 in AU2 + #define inAU3 in AU3 + #define inAU4 in AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 inout AD2 + #define inoutAD3 inout AD3 + #define inoutAD4 inout AD4 + #define inoutAF2 inout AF2 + #define inoutAF3 inout AF3 + #define inoutAF4 inout AF4 + #define inoutAL2 inout AL2 + #define inoutAL3 inout AL3 + #define inoutAL4 inout AL4 + #define inoutAU2 inout AU2 + #define inoutAU3 inout AU3 + #define inoutAU4 inout AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 out AD2 + #define outAD3 out AD3 + #define outAD4 out AD4 + #define outAF2 out AF2 + #define outAF3 out AF3 + #define outAF4 out AF4 + #define outAL2 out AL2 + #define outAL3 out AL3 + #define outAL4 out AL4 + #define outAU2 out AU2 + #define outAU3 out AU3 + #define outAU4 out AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD2 x + #define varAD3(x) AD3 x + #define varAD4(x) AD4 x + #define varAF2(x) AF2 x + #define varAF3(x) AF3 x + #define varAF4(x) AF4 x + #define varAL2(x) AL2 x + #define varAL3(x) AL3 x + #define varAL4(x) AL4 x + #define varAU2(x) AU2 x + #define varAU3(x) AU3 x + #define varAU4(x) AU4 x +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) AD2(x,y) + #define initAD3(x,y,z) AD3(x,y,z) + #define initAD4(x,y,z,w) AD4(x,y,z,w) + #define initAF2(x,y) AF2(x,y) + #define initAF3(x,y,z) AF3(x,y,z) + #define initAF4(x,y,z,w) AF4(x,y,z,w) + #define initAL2(x,y) AL2(x,y) + #define initAL3(x,y,z) AL3(x,y,z) + #define initAL4(x,y,z,w) AL4(x,y,z,w) + #define initAU2(x,y) AU2(x,y) + #define initAU3(x,y,z) AU3(x,y,z) + #define initAU4(x,y,z,w) AU4(x,y,z,w) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//============================================================================================================================== + #define AAbsD1(a) abs(AD1(a)) + #define AAbsF1(a) abs(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ACosD1(a) cos(AD1(a)) + #define ACosF1(a) cos(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ADotD2(a,b) dot(AD2(a),AD2(b)) + #define ADotD3(a,b) dot(AD3(a),AD3(b)) + #define ADotD4(a,b) dot(AD4(a),AD4(b)) + #define ADotF2(a,b) dot(AF2(a),AF2(b)) + #define ADotF3(a,b) dot(AF3(a),AF3(b)) + #define ADotF4(a,b) dot(AF4(a),AF4(b)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AExp2D1(a) exp2(AD1(a)) + #define AExp2F1(a) exp2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AFloorD1(a) floor(AD1(a)) + #define AFloorF1(a) floor(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ALog2D1(a) log2(AD1(a)) + #define ALog2F1(a) log2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMaxD1(a,b) max(a,b) + #define AMaxF1(a,b) max(a,b) + #define AMaxL1(a,b) max(a,b) + #define AMaxU1(a,b) max(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMinD1(a,b) min(a,b) + #define AMinF1(a,b) min(a,b) + #define AMinL1(a,b) min(a,b) + #define AMinU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASinD1(a) sin(AD1(a)) + #define ASinF1(a) sin(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASqrtD1(a) sqrt(AD1(a)) + #define ASqrtF1(a) sqrt(AF1(a)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + #define APowD1(a,b) pow(AD1(a),AF1(b)) + #define APowF1(a,b) pow(AF1(a),AF1(b)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + #ifdef A_DUBL + AD2 opAAbsD2(outAD2 d,inAD2 a){d=abs(a);return d;} + AD3 opAAbsD3(outAD3 d,inAD3 a){d=abs(a);return d;} + AD4 opAAbsD4(outAD4 d,inAD4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d=a+b;return d;} + AD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d=a+b;return d;} + AD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d=a+AD2_(b);return d;} + AD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d=a+AD3_(b);return d;} + AD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d=a+AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opACpyD2(outAD2 d,inAD2 a){d=a;return d;} + AD3 opACpyD3(outAD3 d,inAD3 a){d=a;return d;} + AD4 opACpyD4(outAD4 d,inAD4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d=ALerpD2(a,b,c);return d;} + AD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d=ALerpD3(a,b,c);return d;} + AD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d=ALerpD4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d=ALerpD2(a,b,AD2_(c));return d;} + AD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d=ALerpD3(a,b,AD3_(c));return d;} + AD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d=ALerpD4(a,b,AD4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d=max(a,b);return d;} + AD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d=max(a,b);return d;} + AD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d=min(a,b);return d;} + AD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d=min(a,b);return d;} + AD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d=a*b;return d;} + AD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d=a*b;return d;} + AD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d=a*AD2_(b);return d;} + AD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d=a*AD3_(b);return d;} + AD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d=a*AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opANegD2(outAD2 d,inAD2 a){d=-a;return d;} + AD3 opANegD3(outAD3 d,inAD3 a){d=-a;return d;} + AD4 opANegD4(outAD4 d,inAD4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opARcpD2(outAD2 d,inAD2 a){d=ARcpD2(a);return d;} + AD3 opARcpD3(outAD3 d,inAD3 a){d=ARcpD3(a);return d;} + AD4 opARcpD4(outAD4 d,inAD4 a){d=ARcpD4(a);return d;} + #endif +//============================================================================================================================== + AF2 opAAbsF2(outAF2 d,inAF2 a){d=abs(a);return d;} + AF3 opAAbsF3(outAF3 d,inAF3 a){d=abs(a);return d;} + AF4 opAAbsF4(outAF4 d,inAF4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d=a+b;return d;} + AF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d=a+b;return d;} + AF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d=a+AF2_(b);return d;} + AF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d=a+AF3_(b);return d;} + AF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d=a+AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opACpyF2(outAF2 d,inAF2 a){d=a;return d;} + AF3 opACpyF3(outAF3 d,inAF3 a){d=a;return d;} + AF4 opACpyF4(outAF4 d,inAF4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d=ALerpF2(a,b,c);return d;} + AF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d=ALerpF3(a,b,c);return d;} + AF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d=ALerpF4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d=ALerpF2(a,b,AF2_(c));return d;} + AF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d=ALerpF3(a,b,AF3_(c));return d;} + AF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d=ALerpF4(a,b,AF4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d=max(a,b);return d;} + AF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d=max(a,b);return d;} + AF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d=min(a,b);return d;} + AF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d=min(a,b);return d;} + AF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d=a*b;return d;} + AF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d=a*b;return d;} + AF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d=a*AF2_(b);return d;} + AF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d=a*AF3_(b);return d;} + AF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d=a*AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opANegF2(outAF2 d,inAF2 a){d=-a;return d;} + AF3 opANegF3(outAF3 d,inAF3 a){d=-a;return d;} + AF4 opANegF4(outAF4 d,inAF4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opARcpF2(outAF2 d,inAF2 a){d=ARcpF2(a);return d;} + AF3 opARcpF3(outAF3 d,inAF3 a){d=ARcpF3(a);return d;} + AF4 opARcpF4(outAF4 d,inAF4 a){d=ARcpF4(a);return d;} +#endif + +#define FSR_EASU_F 1 +AU4 con0, con1, con2, con3; +float srcW, srcH, dstW, dstH; +vec2 bLeft, tRight; + +AF2 translate(AF2 pos) { + return AF2(pos.x * scaleX, pos.y * scaleY); +} + +void setBounds(vec2 bottomLeft, vec2 topRight) { + bLeft = bottomLeft; + tRight = topRight; +} + +AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(Source, translate(p), 0); return res; } +AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(Source, translate(p), 1); return res; } +AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(Source, translate(p), 2); return res; } + +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// AMD FidelityFX SUPER RESOLUTION [FSR 1] ::: SPATIAL SCALING & EXTRAS - v1.20210629 +// +// +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// FSR is a collection of algorithms relating to generating a higher resolution image. +// This specific header focuses on single-image non-temporal image scaling, and related tools. +// +// The core functions are EASU and RCAS: +// [EASU] Edge Adaptive Spatial Upsampling ....... 1x to 4x area range spatial scaling, clamped adaptive elliptical filter. +// [RCAS] Robust Contrast Adaptive Sharpening .... A non-scaling variation on CAS. +// RCAS needs to be applied after EASU as a separate pass. +// +// Optional utility functions are: +// [LFGA] Linear Film Grain Applicator ........... Tool to apply film grain after scaling. +// [SRTM] Simple Reversible Tone-Mapper .......... Linear HDR {0 to FP16_MAX} to {0 to 1} and back. +// [TEPD] Temporal Energy Preserving Dither ...... Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// See each individual sub-section for inline documentation. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FUNCTION PERMUTATIONS +// ===================== +// *F() ..... Single item computation with 32-bit. +// *H() ..... Single item computation with 16-bit, with packing (aka two 16-bit ops in parallel) when possible. +// *Hx2() ... Processing two items in parallel with 16-bit, easier packing. +// Not all interfaces in this file have a *Hx2() form. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING +// +//------------------------------------------------------------------------------------------------------------------------------ +// EASU provides a high quality spatial-only scaling at relatively low cost. +// Meaning EASU is appropiate for laptops and other low-end GPUs. +// Quality from 1x to 4x area scaling is good. +//------------------------------------------------------------------------------------------------------------------------------ +// The scalar uses a modified fast approximation to the standard lanczos(size=2) kernel. +// EASU runs in a single pass, so it applies a directionally and anisotropically adaptive radial lanczos. +// This is also kept as simple as possible to have minimum runtime. +//------------------------------------------------------------------------------------------------------------------------------ +// The lanzcos filter has negative lobes, so by itself it will introduce ringing. +// To remove all ringing, the algorithm uses the nearest 2x2 input texels as a neighborhood, +// and limits output to the minimum and maximum of that neighborhood. +//------------------------------------------------------------------------------------------------------------------------------ +// Input image requirements: +// +// Color needs to be encoded as 3 channel[red, green, blue](e.g.XYZ not supported) +// Each channel needs to be in the range[0, 1] +// Any color primaries are supported +// Display / tonemapping curve needs to be as if presenting to sRGB display or similar(e.g.Gamma 2.0) +// There should be no banding in the input +// There should be no high amplitude noise in the input +// There should be no noise in the input that is not at input pixel granularity +// For performance purposes, use 32bpp formats +//------------------------------------------------------------------------------------------------------------------------------ +// Best to apply EASU at the end of the frame after tonemapping +// but before film grain or composite of the UI. +//------------------------------------------------------------------------------------------------------------------------------ +// Example of including this header for D3D HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan GLSL : +// +// #define A_GPU 1 +// #define A_GLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HLSL_6_2 1 +// #define A_NO_16_BIT_CAST 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of declaring the required input callbacks for GLSL : +// The callbacks need to gather4 for each color channel using the specified texture coordinate 'p'. +// EASU uses gather4 to reduce position computation logic and for free Arrays of Structures to Structures of Arrays conversion. +// +// AH4 FsrEasuRH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,0));} +// AH4 FsrEasuGH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,1));} +// AH4 FsrEasuBH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,2));} +// ... +// The FsrEasuCon function needs to be called from the CPU or GPU to set up constants. +// The difference in viewport and input image size is there to support Dynamic Resolution Scaling. +// To use FsrEasuCon() on the CPU, define A_CPU before including ffx_a and ffx_fsr1. +// Including a GPU example here, the 'con0' through 'con3' values would be stored out to a constant buffer. +// AU4 con0,con1,con2,con3; +// FsrEasuCon(con0,con1,con2,con3, +// 1920.0,1080.0, // Viewport size (top left aligned) in the input image which is to be scaled. +// 3840.0,2160.0, // The size of the input image. +// 2560.0,1440.0); // The output resolution. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrEasuCon( +outAU4 con0, +outAU4 con1, +outAU4 con2, +outAU4 con3, +// This the rendered image resolution being upscaled +AF1 inputViewportInPixelsX, +AF1 inputViewportInPixelsY, +// This is the resolution of the resource containing the input image (useful for dynamic resolution) +AF1 inputSizeInPixelsX, +AF1 inputSizeInPixelsY, +// This is the display resolution which the input image gets upscaled to +AF1 outputSizeInPixelsX, +AF1 outputSizeInPixelsY){ + // Output integer position to a pixel position in viewport. + con0[0]=AU1_AF1(inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)); + con0[1]=AU1_AF1(inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)); + con0[2]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)-AF1_(0.5)); + con0[3]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)-AF1_(0.5)); + // Viewport pixel position to normalized image space. + // This is used to get upper-left of 'F' tap. + con1[0]=AU1_AF1(ARcpF1(inputSizeInPixelsX)); + con1[1]=AU1_AF1(ARcpF1(inputSizeInPixelsY)); + // Centers of gather4, first offset from upper-left of 'F'. + // +---+---+ + // | | | + // +--(0)--+ + // | b | c | + // +---F---+---+---+ + // | e | f | g | h | + // +--(1)--+--(2)--+ + // | i | j | k | l | + // +---+---+---+---+ + // | n | o | + // +--(3)--+ + // | | | + // +---+---+ + con1[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con1[3]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsY)); + // These are from (0) instead of 'F'. + con2[0]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsX)); + con2[1]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con2[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con2[3]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con3[0]=AU1_AF1(AF1_( 0.0)*ARcpF1(inputSizeInPixelsX)); + con3[1]=AU1_AF1(AF1_( 4.0)*ARcpF1(inputSizeInPixelsY)); + con3[2]=con3[3]=0;} + +//If the an offset into the input image resource +A_STATIC void FsrEasuConOffset( + outAU4 con0, + outAU4 con1, + outAU4 con2, + outAU4 con3, + // This the rendered image resolution being upscaled + AF1 inputViewportInPixelsX, + AF1 inputViewportInPixelsY, + // This is the resolution of the resource containing the input image (useful for dynamic resolution) + AF1 inputSizeInPixelsX, + AF1 inputSizeInPixelsY, + // This is the display resolution which the input image gets upscaled to + AF1 outputSizeInPixelsX, + AF1 outputSizeInPixelsY, + // This is the input image offset into the resource containing it (useful for dynamic resolution) + AF1 inputOffsetInPixelsX, + AF1 inputOffsetInPixelsY) { + FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY, inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY); + con0[2] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsX * ARcpF1(outputSizeInPixelsX) - AF1_(0.5) + inputOffsetInPixelsX); + con0[3] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsY * ARcpF1(outputSizeInPixelsY) - AF1_(0.5) + inputOffsetInPixelsY); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_EASU_F) + // Input callback prototypes, need to be implemented by calling shader + AF4 FsrEasuRF(AF2 p); + AF4 FsrEasuGF(AF2 p); + AF4 FsrEasuBF(AF2 p); +//------------------------------------------------------------------------------------------------------------------------------ + // Filtering for a given tap for the scalar. + void FsrEasuTapF( + inout AF3 aC, // Accumulated color, with negative lobe. + inout AF1 aW, // Accumulated weight. + AF2 off, // Pixel offset from resolve position to tap. + AF2 dir, // Gradient direction. + AF2 len, // Length. + AF1 lob, // Negative lobe strength. + AF1 clp, // Clipping point. + AF3 c){ // Tap color. + // Rotate offset by direction. + AF2 v; + v.x=(off.x*( dir.x))+(off.y*dir.y); + v.y=(off.x*(-dir.y))+(off.y*dir.x); + // Anisotropy. + v*=len; + // Compute distance^2. + AF1 d2=v.x*v.x+v.y*v.y; + // Limit to the window as at corner, 2 taps can easily be outside. + d2=min(d2,clp); + // Approximation of lancos2 without sin() or rcp(), or sqrt() to get x. + // (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2 + // |_______________________________________| |_______________| + // base window + // The general form of the 'base' is, + // (a*(b*x^2-1)^2-(a-1)) + // Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe. + AF1 wB=AF1_(2.0/5.0)*d2+AF1_(-1.0); + AF1 wA=lob*d2+AF1_(-1.0); + wB*=wB; + wA*=wA; + wB=AF1_(25.0/16.0)*wB+AF1_(-(25.0/16.0-1.0)); + AF1 w=wB*wA; + // Do weighted average. + aC+=c*w;aW+=w;} +//------------------------------------------------------------------------------------------------------------------------------ + // Accumulate direction and length. + void FsrEasuSetF( + inout AF2 dir, + inout AF1 len, + AF2 pp, + AP1 biS,AP1 biT,AP1 biU,AP1 biV, + AF1 lA,AF1 lB,AF1 lC,AF1 lD,AF1 lE){ + // Compute bilinear weight, branches factor out as predicates are compiler time immediates. + // s t + // u v + AF1 w = AF1_(0.0); + if(biS)w=(AF1_(1.0)-pp.x)*(AF1_(1.0)-pp.y); + if(biT)w= pp.x *(AF1_(1.0)-pp.y); + if(biU)w=(AF1_(1.0)-pp.x)* pp.y ; + if(biV)w= pp.x * pp.y ; + // Direction is the '+' diff. + // a + // b c d + // e + // Then takes magnitude from abs average of both sides of 'c'. + // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms. + AF1 dc=lD-lC; + AF1 cb=lC-lB; + AF1 lenX=max(abs(dc),abs(cb)); + lenX=APrxLoRcpF1(lenX); + AF1 dirX=lD-lB; + dir.x+=dirX*w; + lenX=ASatF1(abs(dirX)*lenX); + lenX*=lenX; + len+=lenX*w; + // Repeat for the y axis. + AF1 ec=lE-lC; + AF1 ca=lC-lA; + AF1 lenY=max(abs(ec),abs(ca)); + lenY=APrxLoRcpF1(lenY); + AF1 dirY=lE-lA; + dir.y+=dirY*w; + lenY=ASatF1(abs(dirY)*lenY); + lenY*=lenY; + len+=lenY*w;} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrEasuF( + out AF3 pix, + AU2 ip, // Integer pixel position in output. + AU4 con0, // Constants generated by FsrEasuCon(). + AU4 con1, + AU4 con2, + AU4 con3){ +//------------------------------------------------------------------------------------------------------------------------------ + // Get position of 'f'. + AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw); + AF2 fp=floor(pp); + pp-=fp; +//------------------------------------------------------------------------------------------------------------------------------ + // 12-tap kernel. + // b c + // e f g h + // i j k l + // n o + // Gather 4 ordering. + // a b + // r g + // For packed FP16, need either {rg} or {ab} so using the following setup for gather in all versions, + // a b <- unused (z) + // r g + // a b a b + // r g r g + // a b + // r g <- unused (z) + // Allowing dead-code removal to remove the 'z's. + AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw); + // These are from p0 to avoid pulling two constants on pre-Navi hardware. + AF2 p1=p0+AF2_AU2(con2.xy); + AF2 p2=p0+AF2_AU2(con2.zw); + AF2 p3=p0+AF2_AU2(con3.xy); + AF4 bczzR=FsrEasuRF(p0); + AF4 bczzG=FsrEasuGF(p0); + AF4 bczzB=FsrEasuBF(p0); + AF4 ijfeR=FsrEasuRF(p1); + AF4 ijfeG=FsrEasuGF(p1); + AF4 ijfeB=FsrEasuBF(p1); + AF4 klhgR=FsrEasuRF(p2); + AF4 klhgG=FsrEasuGF(p2); + AF4 klhgB=FsrEasuBF(p2); + AF4 zzonR=FsrEasuRF(p3); + AF4 zzonG=FsrEasuGF(p3); + AF4 zzonB=FsrEasuBF(p3); +//------------------------------------------------------------------------------------------------------------------------------ + // Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD). + AF4 bczzL=bczzB*AF4_(0.5)+(bczzR*AF4_(0.5)+bczzG); + AF4 ijfeL=ijfeB*AF4_(0.5)+(ijfeR*AF4_(0.5)+ijfeG); + AF4 klhgL=klhgB*AF4_(0.5)+(klhgR*AF4_(0.5)+klhgG); + AF4 zzonL=zzonB*AF4_(0.5)+(zzonR*AF4_(0.5)+zzonG); + // Rename. + AF1 bL=bczzL.x; + AF1 cL=bczzL.y; + AF1 iL=ijfeL.x; + AF1 jL=ijfeL.y; + AF1 fL=ijfeL.z; + AF1 eL=ijfeL.w; + AF1 kL=klhgL.x; + AF1 lL=klhgL.y; + AF1 hL=klhgL.z; + AF1 gL=klhgL.w; + AF1 oL=zzonL.z; + AF1 nL=zzonL.w; + // Accumulate for bilinear interpolation. + AF2 dir=AF2_(0.0); + AF1 len=AF1_(0.0); + FsrEasuSetF(dir,len,pp,true, false,false,false,bL,eL,fL,gL,jL); + FsrEasuSetF(dir,len,pp,false,true ,false,false,cL,fL,gL,hL,kL); + FsrEasuSetF(dir,len,pp,false,false,true ,false,fL,iL,jL,kL,nL); + FsrEasuSetF(dir,len,pp,false,false,false,true ,gL,jL,kL,lL,oL); +//------------------------------------------------------------------------------------------------------------------------------ + // Normalize with approximation, and cleanup close to zero. + AF2 dir2=dir*dir; + AF1 dirR=dir2.x+dir2.y; + AP1 zro=dirR<AF1_(1.0/32768.0); + dirR=APrxLoRsqF1(dirR); + dirR=zro?AF1_(1.0):dirR; + dir.x=zro?AF1_(1.0):dir.x; + dir*=AF2_(dirR); + // Transform from {0 to 2} to {0 to 1} range, and shape with square. + len=len*AF1_(0.5); + len*=len; + // Stretch kernel {1.0 vert|horz, to sqrt(2.0) on diagonal}. + AF1 stretch=(dir.x*dir.x+dir.y*dir.y)*APrxLoRcpF1(max(abs(dir.x),abs(dir.y))); + // Anisotropic length after rotation, + // x := 1.0 lerp to 'stretch' on edges + // y := 1.0 lerp to 2x on edges + AF2 len2=AF2(AF1_(1.0)+(stretch-AF1_(1.0))*len,AF1_(1.0)+AF1_(-0.5)*len); + // Based on the amount of 'edge', + // the window shifts from +/-{sqrt(2.0) to slightly beyond 2.0}. + AF1 lob=AF1_(0.5)+AF1_((1.0/4.0-0.04)-0.5)*len; + // Set distance^2 clipping point to the end of the adjustable window. + AF1 clp=APrxLoRcpF1(lob); +//------------------------------------------------------------------------------------------------------------------------------ + // Accumulation mixed with min/max of 4 nearest. + // b c + // e f g h + // i j k l + // n o + AF3 min4=min(AMin3F3(AF3(ijfeR.z,ijfeG.z,ijfeB.z),AF3(klhgR.w,klhgG.w,klhgB.w),AF3(ijfeR.y,ijfeG.y,ijfeB.y)), + AF3(klhgR.x,klhgG.x,klhgB.x)); + AF3 max4=max(AMax3F3(AF3(ijfeR.z,ijfeG.z,ijfeB.z),AF3(klhgR.w,klhgG.w,klhgB.w),AF3(ijfeR.y,ijfeG.y,ijfeB.y)), + AF3(klhgR.x,klhgG.x,klhgB.x)); + // Accumulation. + AF3 aC=AF3_(0.0); + AF1 aW=AF1_(0.0); + FsrEasuTapF(aC,aW,AF2( 0.0,-1.0)-pp,dir,len2,lob,clp,AF3(bczzR.x,bczzG.x,bczzB.x)); // b + FsrEasuTapF(aC,aW,AF2( 1.0,-1.0)-pp,dir,len2,lob,clp,AF3(bczzR.y,bczzG.y,bczzB.y)); // c + FsrEasuTapF(aC,aW,AF2(-1.0, 1.0)-pp,dir,len2,lob,clp,AF3(ijfeR.x,ijfeG.x,ijfeB.x)); // i + FsrEasuTapF(aC,aW,AF2( 0.0, 1.0)-pp,dir,len2,lob,clp,AF3(ijfeR.y,ijfeG.y,ijfeB.y)); // j + FsrEasuTapF(aC,aW,AF2( 0.0, 0.0)-pp,dir,len2,lob,clp,AF3(ijfeR.z,ijfeG.z,ijfeB.z)); // f + FsrEasuTapF(aC,aW,AF2(-1.0, 0.0)-pp,dir,len2,lob,clp,AF3(ijfeR.w,ijfeG.w,ijfeB.w)); // e + FsrEasuTapF(aC,aW,AF2( 1.0, 1.0)-pp,dir,len2,lob,clp,AF3(klhgR.x,klhgG.x,klhgB.x)); // k + FsrEasuTapF(aC,aW,AF2( 2.0, 1.0)-pp,dir,len2,lob,clp,AF3(klhgR.y,klhgG.y,klhgB.y)); // l + FsrEasuTapF(aC,aW,AF2( 2.0, 0.0)-pp,dir,len2,lob,clp,AF3(klhgR.z,klhgG.z,klhgB.z)); // h + FsrEasuTapF(aC,aW,AF2( 1.0, 0.0)-pp,dir,len2,lob,clp,AF3(klhgR.w,klhgG.w,klhgB.w)); // g + FsrEasuTapF(aC,aW,AF2( 1.0, 2.0)-pp,dir,len2,lob,clp,AF3(zzonR.z,zzonG.z,zzonB.z)); // o + FsrEasuTapF(aC,aW,AF2( 0.0, 2.0)-pp,dir,len2,lob,clp,AF3(zzonR.w,zzonG.w,zzonB.w)); // n +//------------------------------------------------------------------------------------------------------------------------------ + // Normalize and dering. + pix=min(max4,max(min4,aC*AF3_(ARcpF1(aW))));} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_EASU_H) +// Input callback prototypes, need to be implemented by calling shader + AH4 FsrEasuRH(AF2 p); + AH4 FsrEasuGH(AF2 p); + AH4 FsrEasuBH(AF2 p); +//------------------------------------------------------------------------------------------------------------------------------ + // This runs 2 taps in parallel. + void FsrEasuTapH( + inout AH2 aCR,inout AH2 aCG,inout AH2 aCB, + inout AH2 aW, + AH2 offX,AH2 offY, + AH2 dir, + AH2 len, + AH1 lob, + AH1 clp, + AH2 cR,AH2 cG,AH2 cB){ + AH2 vX,vY; + vX=offX* dir.xx +offY*dir.yy; + vY=offX*(-dir.yy)+offY*dir.xx; + vX*=len.x;vY*=len.y; + AH2 d2=vX*vX+vY*vY; + d2=min(d2,AH2_(clp)); + AH2 wB=AH2_(2.0/5.0)*d2+AH2_(-1.0); + AH2 wA=AH2_(lob)*d2+AH2_(-1.0); + wB*=wB; + wA*=wA; + wB=AH2_(25.0/16.0)*wB+AH2_(-(25.0/16.0-1.0)); + AH2 w=wB*wA; + aCR+=cR*w;aCG+=cG*w;aCB+=cB*w;aW+=w;} +//------------------------------------------------------------------------------------------------------------------------------ + // This runs 2 taps in parallel. + void FsrEasuSetH( + inout AH2 dirPX,inout AH2 dirPY, + inout AH2 lenP, + AH2 pp, + AP1 biST,AP1 biUV, + AH2 lA,AH2 lB,AH2 lC,AH2 lD,AH2 lE){ + AH2 w = AH2_(0.0); + if(biST)w=(AH2(1.0,0.0)+AH2(-pp.x,pp.x))*AH2_(AH1_(1.0)-pp.y); + if(biUV)w=(AH2(1.0,0.0)+AH2(-pp.x,pp.x))*AH2_( pp.y); + // ABS is not free in the packed FP16 path. + AH2 dc=lD-lC; + AH2 cb=lC-lB; + AH2 lenX=max(abs(dc),abs(cb)); + lenX=ARcpH2(lenX); + AH2 dirX=lD-lB; + dirPX+=dirX*w; + lenX=ASatH2(abs(dirX)*lenX); + lenX*=lenX; + lenP+=lenX*w; + AH2 ec=lE-lC; + AH2 ca=lC-lA; + AH2 lenY=max(abs(ec),abs(ca)); + lenY=ARcpH2(lenY); + AH2 dirY=lE-lA; + dirPY+=dirY*w; + lenY=ASatH2(abs(dirY)*lenY); + lenY*=lenY; + lenP+=lenY*w;} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrEasuH( + out AH3 pix, + AU2 ip, + AU4 con0, + AU4 con1, + AU4 con2, + AU4 con3){ +//------------------------------------------------------------------------------------------------------------------------------ + AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw); + AF2 fp=floor(pp); + pp-=fp; + AH2 ppp=AH2(pp); +//------------------------------------------------------------------------------------------------------------------------------ + AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw); + AF2 p1=p0+AF2_AU2(con2.xy); + AF2 p2=p0+AF2_AU2(con2.zw); + AF2 p3=p0+AF2_AU2(con3.xy); + AH4 bczzR=FsrEasuRH(p0); + AH4 bczzG=FsrEasuGH(p0); + AH4 bczzB=FsrEasuBH(p0); + AH4 ijfeR=FsrEasuRH(p1); + AH4 ijfeG=FsrEasuGH(p1); + AH4 ijfeB=FsrEasuBH(p1); + AH4 klhgR=FsrEasuRH(p2); + AH4 klhgG=FsrEasuGH(p2); + AH4 klhgB=FsrEasuBH(p2); + AH4 zzonR=FsrEasuRH(p3); + AH4 zzonG=FsrEasuGH(p3); + AH4 zzonB=FsrEasuBH(p3); +//------------------------------------------------------------------------------------------------------------------------------ + AH4 bczzL=bczzB*AH4_(0.5)+(bczzR*AH4_(0.5)+bczzG); + AH4 ijfeL=ijfeB*AH4_(0.5)+(ijfeR*AH4_(0.5)+ijfeG); + AH4 klhgL=klhgB*AH4_(0.5)+(klhgR*AH4_(0.5)+klhgG); + AH4 zzonL=zzonB*AH4_(0.5)+(zzonR*AH4_(0.5)+zzonG); + AH1 bL=bczzL.x; + AH1 cL=bczzL.y; + AH1 iL=ijfeL.x; + AH1 jL=ijfeL.y; + AH1 fL=ijfeL.z; + AH1 eL=ijfeL.w; + AH1 kL=klhgL.x; + AH1 lL=klhgL.y; + AH1 hL=klhgL.z; + AH1 gL=klhgL.w; + AH1 oL=zzonL.z; + AH1 nL=zzonL.w; + // This part is different, accumulating 2 taps in parallel. + AH2 dirPX=AH2_(0.0); + AH2 dirPY=AH2_(0.0); + AH2 lenP=AH2_(0.0); + FsrEasuSetH(dirPX,dirPY,lenP,ppp,true, false,AH2(bL,cL),AH2(eL,fL),AH2(fL,gL),AH2(gL,hL),AH2(jL,kL)); + FsrEasuSetH(dirPX,dirPY,lenP,ppp,false,true ,AH2(fL,gL),AH2(iL,jL),AH2(jL,kL),AH2(kL,lL),AH2(nL,oL)); + AH2 dir=AH2(dirPX.r+dirPX.g,dirPY.r+dirPY.g); + AH1 len=lenP.r+lenP.g; +//------------------------------------------------------------------------------------------------------------------------------ + AH2 dir2=dir*dir; + AH1 dirR=dir2.x+dir2.y; + AP1 zro=dirR<AH1_(1.0/32768.0); + dirR=APrxLoRsqH1(dirR); + dirR=zro?AH1_(1.0):dirR; + dir.x=zro?AH1_(1.0):dir.x; + dir*=AH2_(dirR); + len=len*AH1_(0.5); + len*=len; + AH1 stretch=(dir.x*dir.x+dir.y*dir.y)*APrxLoRcpH1(max(abs(dir.x),abs(dir.y))); + AH2 len2=AH2(AH1_(1.0)+(stretch-AH1_(1.0))*len,AH1_(1.0)+AH1_(-0.5)*len); + AH1 lob=AH1_(0.5)+AH1_((1.0/4.0-0.04)-0.5)*len; + AH1 clp=APrxLoRcpH1(lob); +//------------------------------------------------------------------------------------------------------------------------------ + // FP16 is different, using packed trick to do min and max in same operation. + AH2 bothR=max(max(AH2(-ijfeR.z,ijfeR.z),AH2(-klhgR.w,klhgR.w)),max(AH2(-ijfeR.y,ijfeR.y),AH2(-klhgR.x,klhgR.x))); + AH2 bothG=max(max(AH2(-ijfeG.z,ijfeG.z),AH2(-klhgG.w,klhgG.w)),max(AH2(-ijfeG.y,ijfeG.y),AH2(-klhgG.x,klhgG.x))); + AH2 bothB=max(max(AH2(-ijfeB.z,ijfeB.z),AH2(-klhgB.w,klhgB.w)),max(AH2(-ijfeB.y,ijfeB.y),AH2(-klhgB.x,klhgB.x))); + // This part is different for FP16, working pairs of taps at a time. + AH2 pR=AH2_(0.0); + AH2 pG=AH2_(0.0); + AH2 pB=AH2_(0.0); + AH2 pW=AH2_(0.0); + FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0, 1.0)-ppp.xx,AH2(-1.0,-1.0)-ppp.yy,dir,len2,lob,clp,bczzR.xy,bczzG.xy,bczzB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2(-1.0, 0.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,ijfeR.xy,ijfeG.xy,ijfeB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0,-1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,ijfeR.zw,ijfeG.zw,ijfeB.zw); + FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 2.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,klhgR.xy,klhgG.xy,klhgB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2( 2.0, 1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,klhgR.zw,klhgG.zw,klhgB.zw); + FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 0.0)-ppp.xx,AH2( 2.0, 2.0)-ppp.yy,dir,len2,lob,clp,zzonR.zw,zzonG.zw,zzonB.zw); + AH3 aC=AH3(pR.x+pR.y,pG.x+pG.y,pB.x+pB.y); + AH1 aW=pW.x+pW.y; +//------------------------------------------------------------------------------------------------------------------------------ + // Slightly different for FP16 version due to combined min and max. + pix=min(AH3(bothR.y,bothG.y,bothB.y),max(-AH3(bothR.x,bothG.x,bothB.x),aC*AH3_(ARcpH1(aW))));} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING +// +//------------------------------------------------------------------------------------------------------------------------------ +// CAS uses a simplified mechanism to convert local contrast into a variable amount of sharpness. +// RCAS uses a more exact mechanism, solving for the maximum local sharpness possible before clipping. +// RCAS also has a built in process to limit sharpening of what it detects as possible noise. +// RCAS sharper does not support scaling, as it should be applied after EASU scaling. +// Pass EASU output straight into RCAS, no color conversions necessary. +//------------------------------------------------------------------------------------------------------------------------------ +// RCAS is based on the following logic. +// RCAS uses a 5 tap filter in a cross pattern (same as CAS), +// w n +// w 1 w for taps w m e +// w s +// Where 'w' is the negative lobe weight. +// output = (w*(n+e+w+s)+m)/(4*w+1) +// RCAS solves for 'w' by seeing where the signal might clip out of the {0 to 1} input range, +// 0 == (w*(n+e+w+s)+m)/(4*w+1) -> w = -m/(n+e+w+s) +// 1 == (w*(n+e+w+s)+m)/(4*w+1) -> w = (1-m)/(n+e+w+s-4*1) +// Then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. +// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. +// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps. +// As well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation. +// This stabilizes RCAS. +// RCAS does a simple highpass which is normalized against the local contrast then shaped, +// 0.25 +// 0.25 -1 0.25 +// 0.25 +// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges. +// +// GLSL example for the required callbacks : +// +// AH4 FsrRcasLoadH(ASW2 p){return AH4(imageLoad(imgSrc,ASU2(p)));} +// void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b) +// { +// //do any simple input color conversions here or leave empty if none needed +// } +// +// FsrRcasCon need to be called from the CPU or GPU to set up constants. +// Including a GPU example here, the 'con' value would be stored out to a constant buffer. +// +// AU4 con; +// FsrRcasCon(con, +// 0.0); // The scale is {0.0 := maximum sharpness, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +// --------------- +// RCAS sharpening supports a CAS-like pass-through alpha via, +// #define FSR_RCAS_PASSTHROUGH_ALPHA 1 +// RCAS also supports a define to enable a more expensive path to avoid some sharpening of noise. +// Would suggest it is better to apply film grain after RCAS sharpening (and after scaling) instead of using this define, +// #define FSR_RCAS_DENOISE 1 +//============================================================================================================================== +// This is set at the limit of providing unnatural results for sharpening. +#define FSR_RCAS_LIMIT (0.25-(1.0/16.0)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrRcasCon( +outAU4 con, +// The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +AF1 sharpness){ + // Transform from stops to linear value. + sharpness=AExp2F1(-sharpness); + varAF2(hSharp)=initAF2(sharpness,sharpness); + con[0]=AU1_AF1(sharpness); + con[1]=AU1_AH2_AF2(hSharp); + con[2]=0; + con[3]=0;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_RCAS_F) + // Input callback prototypes that need to be implemented by calling shader + AF4 FsrRcasLoadF(ASU2 p); + void FsrRcasInputF(inout AF1 r,inout AF1 g,inout AF1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasF( + out AF1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AF1 pixG, + out AF1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AF1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASU2 sp=ASU2(ip); + AF3 b=FsrRcasLoadF(sp+ASU2( 0,-1)).rgb; + AF3 d=FsrRcasLoadF(sp+ASU2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AF4 ee=FsrRcasLoadF(sp); + AF3 e=ee.rgb;pixA=ee.a; + #else + AF3 e=FsrRcasLoadF(sp).rgb; + #endif + AF3 f=FsrRcasLoadF(sp+ASU2( 1, 0)).rgb; + AF3 h=FsrRcasLoadF(sp+ASU2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AF1 bR=b.r; + AF1 bG=b.g; + AF1 bB=b.b; + AF1 dR=d.r; + AF1 dG=d.g; + AF1 dB=d.b; + AF1 eR=e.r; + AF1 eG=e.g; + AF1 eB=e.b; + AF1 fR=f.r; + AF1 fG=f.g; + AF1 fB=f.b; + AF1 hR=h.r; + AF1 hG=h.g; + AF1 hB=h.b; + // Run optional input transform. + FsrRcasInputF(bR,bG,bB); + FsrRcasInputF(dR,dG,dB); + FsrRcasInputF(eR,eG,eB); + FsrRcasInputF(fR,fG,fB); + FsrRcasInputF(hR,hG,hB); + // Luma times 2. + AF1 bL=bB*AF1_(0.5)+(bR*AF1_(0.5)+bG); + AF1 dL=dB*AF1_(0.5)+(dR*AF1_(0.5)+dG); + AF1 eL=eB*AF1_(0.5)+(eR*AF1_(0.5)+eG); + AF1 fL=fB*AF1_(0.5)+(fR*AF1_(0.5)+fG); + AF1 hL=hB*AF1_(0.5)+(hR*AF1_(0.5)+hG); + // Noise detection. + AF1 nz=AF1_(0.25)*bL+AF1_(0.25)*dL+AF1_(0.25)*fL+AF1_(0.25)*hL-eL; + nz=ASatF1(abs(nz)*APrxMedRcpF1(AMax3F1(AMax3F1(bL,dL,eL),fL,hL)-AMin3F1(AMin3F1(bL,dL,eL),fL,hL))); + nz=AF1_(-0.5)*nz+AF1_(1.0); + // Min and max of ring. + AF1 mn4R=min(AMin3F1(bR,dR,fR),hR); + AF1 mn4G=min(AMin3F1(bG,dG,fG),hG); + AF1 mn4B=min(AMin3F1(bB,dB,fB),hB); + AF1 mx4R=max(AMax3F1(bR,dR,fR),hR); + AF1 mx4G=max(AMax3F1(bG,dG,fG),hG); + AF1 mx4B=max(AMax3F1(bB,dB,fB),hB); + // Immediate constants for peak range. + AF2 peakC=AF2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R); + AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G); + AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B); + AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y); + AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y); + AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y); + AF1 lobeR=max(-hitMinR,hitMaxR); + AF1 lobeG=max(-hitMinG,hitMaxG); + AF1 lobeB=max(-hitMinB,hitMaxB); + AF1 lobe=max(AF1_(-FSR_RCAS_LIMIT),min(AMax3F1(lobeR,lobeG,lobeB),AF1_(0.0)))*AF1_AU1(con.x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AF1 rcpL=APrxMedRcpF1(AF1_(4.0)*lobe+AF1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL; + return;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_H) + // Input callback prototypes that need to be implemented by calling shader + AH4 FsrRcasLoadH(ASW2 p); + void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasH( + out AH1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AH1 pixG, + out AH1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Sharpening algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASW2 sp=ASW2(ip); + AH3 b=FsrRcasLoadH(sp+ASW2( 0,-1)).rgb; + AH3 d=FsrRcasLoadH(sp+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee=FsrRcasLoadH(sp); + AH3 e=ee.rgb;pixA=ee.a; + #else + AH3 e=FsrRcasLoadH(sp).rgb; + #endif + AH3 f=FsrRcasLoadH(sp+ASW2( 1, 0)).rgb; + AH3 h=FsrRcasLoadH(sp+ASW2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AH1 bR=b.r; + AH1 bG=b.g; + AH1 bB=b.b; + AH1 dR=d.r; + AH1 dG=d.g; + AH1 dB=d.b; + AH1 eR=e.r; + AH1 eG=e.g; + AH1 eB=e.b; + AH1 fR=f.r; + AH1 fG=f.g; + AH1 fB=f.b; + AH1 hR=h.r; + AH1 hG=h.g; + AH1 hB=h.b; + // Run optional input transform. + FsrRcasInputH(bR,bG,bB); + FsrRcasInputH(dR,dG,dB); + FsrRcasInputH(eR,eG,eB); + FsrRcasInputH(fR,fG,fB); + FsrRcasInputH(hR,hG,hB); + // Luma times 2. + AH1 bL=bB*AH1_(0.5)+(bR*AH1_(0.5)+bG); + AH1 dL=dB*AH1_(0.5)+(dR*AH1_(0.5)+dG); + AH1 eL=eB*AH1_(0.5)+(eR*AH1_(0.5)+eG); + AH1 fL=fB*AH1_(0.5)+(fR*AH1_(0.5)+fG); + AH1 hL=hB*AH1_(0.5)+(hR*AH1_(0.5)+hG); + // Noise detection. + AH1 nz=AH1_(0.25)*bL+AH1_(0.25)*dL+AH1_(0.25)*fL+AH1_(0.25)*hL-eL; + nz=ASatH1(abs(nz)*APrxMedRcpH1(AMax3H1(AMax3H1(bL,dL,eL),fL,hL)-AMin3H1(AMin3H1(bL,dL,eL),fL,hL))); + nz=AH1_(-0.5)*nz+AH1_(1.0); + // Min and max of ring. + AH1 mn4R=min(AMin3H1(bR,dR,fR),hR); + AH1 mn4G=min(AMin3H1(bG,dG,fG),hG); + AH1 mn4B=min(AMin3H1(bB,dB,fB),hB); + AH1 mx4R=max(AMax3H1(bR,dR,fR),hR); + AH1 mx4G=max(AMax3H1(bG,dG,fG),hG); + AH1 mx4B=max(AMax3H1(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R); + AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G); + AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B); + AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y); + AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y); + AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y); + AH1 lobeR=max(-hitMinR,hitMaxR); + AH1 lobeG=max(-hitMinG,hitMaxG); + AH1 lobeB=max(-hitMinB,hitMaxB); + AH1 lobe=max(AH1_(-FSR_RCAS_LIMIT),min(AMax3H1(lobeR,lobeG,lobeB),AH1_(0.0)))*AH2_AU1(con.y).x; + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH1 rcpL=APrxMedRcpH1(AH1_(4.0)*lobe+AH1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_HX2) + // Input callback prototypes that need to be implemented by the calling shader + AH4 FsrRcasLoadHx2(ASW2 p); + void FsrRcasInputHx2(inout AH2 r,inout AH2 g,inout AH2 b); +//------------------------------------------------------------------------------------------------------------------------------ + // Can be used to convert from packed Structures of Arrays to Arrays of Structures for store. + void FsrRcasDepackHx2(out AH4 pix0,out AH4 pix1,AH2 pixR,AH2 pixG,AH2 pixB){ + #ifdef A_HLSL + // Invoke a slower path for DX only, since it won't allow uninitialized values. + pix0.a=pix1.a=0.0; + #endif + pix0.rgb=AH3(pixR.x,pixG.x,pixB.x); + pix1.rgb=AH3(pixR.y,pixG.y,pixB.y);} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasHx2( + // Output values are for 2 8x8 tiles in a 16x8 region. + // pix<R,G,B>.x = left 8x8 tile + // pix<R,G,B>.y = right 8x8 tile + // This enables later processing to easily be packed as well. + out AH2 pixR, + out AH2 pixG, + out AH2 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH2 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // No scaling algorithm uses minimal 3x3 pixel neighborhood. + ASW2 sp0=ASW2(ip); + AH3 b0=FsrRcasLoadHx2(sp0+ASW2( 0,-1)).rgb; + AH3 d0=FsrRcasLoadHx2(sp0+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee0=FsrRcasLoadHx2(sp0); + AH3 e0=ee0.rgb;pixA.r=ee0.a; + #else + AH3 e0=FsrRcasLoadHx2(sp0).rgb; + #endif + AH3 f0=FsrRcasLoadHx2(sp0+ASW2( 1, 0)).rgb; + AH3 h0=FsrRcasLoadHx2(sp0+ASW2( 0, 1)).rgb; + ASW2 sp1=sp0+ASW2(8,0); + AH3 b1=FsrRcasLoadHx2(sp1+ASW2( 0,-1)).rgb; + AH3 d1=FsrRcasLoadHx2(sp1+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee1=FsrRcasLoadHx2(sp1); + AH3 e1=ee1.rgb;pixA.g=ee1.a; + #else + AH3 e1=FsrRcasLoadHx2(sp1).rgb; + #endif + AH3 f1=FsrRcasLoadHx2(sp1+ASW2( 1, 0)).rgb; + AH3 h1=FsrRcasLoadHx2(sp1+ASW2( 0, 1)).rgb; + // Arrays of Structures to Structures of Arrays conversion. + AH2 bR=AH2(b0.r,b1.r); + AH2 bG=AH2(b0.g,b1.g); + AH2 bB=AH2(b0.b,b1.b); + AH2 dR=AH2(d0.r,d1.r); + AH2 dG=AH2(d0.g,d1.g); + AH2 dB=AH2(d0.b,d1.b); + AH2 eR=AH2(e0.r,e1.r); + AH2 eG=AH2(e0.g,e1.g); + AH2 eB=AH2(e0.b,e1.b); + AH2 fR=AH2(f0.r,f1.r); + AH2 fG=AH2(f0.g,f1.g); + AH2 fB=AH2(f0.b,f1.b); + AH2 hR=AH2(h0.r,h1.r); + AH2 hG=AH2(h0.g,h1.g); + AH2 hB=AH2(h0.b,h1.b); + // Run optional input transform. + FsrRcasInputHx2(bR,bG,bB); + FsrRcasInputHx2(dR,dG,dB); + FsrRcasInputHx2(eR,eG,eB); + FsrRcasInputHx2(fR,fG,fB); + FsrRcasInputHx2(hR,hG,hB); + // Luma times 2. + AH2 bL=bB*AH2_(0.5)+(bR*AH2_(0.5)+bG); + AH2 dL=dB*AH2_(0.5)+(dR*AH2_(0.5)+dG); + AH2 eL=eB*AH2_(0.5)+(eR*AH2_(0.5)+eG); + AH2 fL=fB*AH2_(0.5)+(fR*AH2_(0.5)+fG); + AH2 hL=hB*AH2_(0.5)+(hR*AH2_(0.5)+hG); + // Noise detection. + AH2 nz=AH2_(0.25)*bL+AH2_(0.25)*dL+AH2_(0.25)*fL+AH2_(0.25)*hL-eL; + nz=ASatH2(abs(nz)*APrxMedRcpH2(AMax3H2(AMax3H2(bL,dL,eL),fL,hL)-AMin3H2(AMin3H2(bL,dL,eL),fL,hL))); + nz=AH2_(-0.5)*nz+AH2_(1.0); + // Min and max of ring. + AH2 mn4R=min(AMin3H2(bR,dR,fR),hR); + AH2 mn4G=min(AMin3H2(bG,dG,fG),hG); + AH2 mn4B=min(AMin3H2(bB,dB,fB),hB); + AH2 mx4R=max(AMax3H2(bR,dR,fR),hR); + AH2 mx4G=max(AMax3H2(bG,dG,fG),hG); + AH2 mx4B=max(AMax3H2(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R); + AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G); + AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B); + AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y); + AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y); + AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y); + AH2 lobeR=max(-hitMinR,hitMaxR); + AH2 lobeG=max(-hitMinG,hitMaxG); + AH2 lobeB=max(-hitMinB,hitMaxB); + AH2 lobe=max(AH2_(-FSR_RCAS_LIMIT),min(AMax3H2(lobeR,lobeG,lobeB),AH2_(0.0)))*AH2_(AH2_AU1(con.y).x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH2 rcpL=APrxMedRcpH2(AH2_(4.0)*lobe+AH2_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [LFGA] LINEAR FILM GRAIN APPLICATOR +// +//------------------------------------------------------------------------------------------------------------------------------ +// Adding output-resolution film grain after scaling is a good way to mask both rendering and scaling artifacts. +// Suggest using tiled blue noise as film grain input, with peak noise frequency set for a specific look and feel. +// The 'Lfga*()' functions provide a convenient way to introduce grain. +// These functions limit grain based on distance to signal limits. +// This is done so that the grain is temporally energy preserving, and thus won't modify image tonality. +// Grain application should be done in a linear colorspace. +// The grain should be temporally changing, but have a temporal sum per pixel that adds to zero (non-biased). +//------------------------------------------------------------------------------------------------------------------------------ +// Usage, +// FsrLfga*( +// color, // In/out linear colorspace color {0 to 1} ranged. +// grain, // Per pixel grain texture value {-0.5 to 0.5} ranged, input is 3-channel to support colored grain. +// amount); // Amount of grain (0 to 1} ranged. +//------------------------------------------------------------------------------------------------------------------------------ +// Example if grain texture is monochrome: 'FsrLfgaF(color,AF3_(grain),amount)' +//============================================================================================================================== +#if defined(A_GPU) + // Maximum grain is the minimum distance to the signal limit. + void FsrLfgaF(inout AF3 c,AF3 t,AF1 a){c+=(t*AF3_(a))*min(AF3_(1.0)-c,c);} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + // Half precision version (slower). + void FsrLfgaH(inout AH3 c,AH3 t,AH1 a){c+=(t*AH3_(a))*min(AH3_(1.0)-c,c);} +//------------------------------------------------------------------------------------------------------------------------------ + // Packed half precision version (faster). + void FsrLfgaHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 tR,AH2 tG,AH2 tB,AH1 a){ + cR+=(tR*AH2_(a))*min(AH2_(1.0)-cR,cR);cG+=(tG*AH2_(a))*min(AH2_(1.0)-cG,cG);cB+=(tB*AH2_(a))*min(AH2_(1.0)-cB,cB);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [SRTM] SIMPLE REVERSIBLE TONE-MAPPER +// +//------------------------------------------------------------------------------------------------------------------------------ +// This provides a way to take linear HDR color {0 to FP16_MAX} and convert it into a temporary {0 to 1} ranged post-tonemapped linear. +// The tonemapper preserves RGB ratio, which helps maintain HDR color bleed during filtering. +//------------------------------------------------------------------------------------------------------------------------------ +// Reversible tonemapper usage, +// FsrSrtm*(color); // {0 to FP16_MAX} converted to {0 to 1}. +// FsrSrtmInv*(color); // {0 to 1} converted into {0 to 32768, output peak safe for FP16}. +//============================================================================================================================== +#if defined(A_GPU) + void FsrSrtmF(inout AF3 c){c*=AF3_(ARcpF1(AMax3F1(c.r,c.g,c.b)+AF1_(1.0)));} + // The extra max solves the c=1.0 case (which is a /0). + void FsrSrtmInvF(inout AF3 c){c*=AF3_(ARcpF1(max(AF1_(1.0/32768.0),AF1_(1.0)-AMax3F1(c.r,c.g,c.b))));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + void FsrSrtmH(inout AH3 c){c*=AH3_(ARcpH1(AMax3H1(c.r,c.g,c.b)+AH1_(1.0)));} + void FsrSrtmInvH(inout AH3 c){c*=AH3_(ARcpH1(max(AH1_(1.0/32768.0),AH1_(1.0)-AMax3H1(c.r,c.g,c.b))));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrSrtmHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(AMax3H2(cR,cG,cB)+AH2_(1.0));cR*=rcp;cG*=rcp;cB*=rcp;} + void FsrSrtmInvHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(max(AH2_(1.0/32768.0),AH2_(1.0)-AMax3H2(cR,cG,cB)));cR*=rcp;cG*=rcp;cB*=rcp;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [TEPD] TEMPORAL ENERGY PRESERVING DITHER +// +//------------------------------------------------------------------------------------------------------------------------------ +// Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// Gamma 2.0 is used so that the conversion back to linear is just to square the color. +// The conversion comes in 8-bit and 10-bit modes, designed for output to 8-bit UNORM or 10:10:10:2 respectively. +// Given good non-biased temporal blue noise as dither input, +// the output dither will temporally conserve energy. +// This is done by choosing the linear nearest step point instead of perceptual nearest. +// See code below for details. +//------------------------------------------------------------------------------------------------------------------------------ +// DX SPEC RULES FOR FLOAT->UNORM 8-BIT CONVERSION +// =============================================== +// - Output is 'uint(floor(saturate(n)*255.0+0.5))'. +// - Thus rounding is to nearest. +// - NaN gets converted to zero. +// - INF is clamped to {0.0 to 1.0}. +//============================================================================================================================== +#if defined(A_GPU) + // Hand tuned integer position to dither value, with more values than simple checkerboard. + // Only 32-bit has enough precision for this compddation. + // Output is {0 to <1}. + AF1 FsrTepdDitF(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + // The 1.61803 golden ratio. + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + // Number designed to provide a good visual pattern. + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AFractF1(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 8-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC8F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(255.0))*AF3_(1.0/255.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/255.0);b=b*b; + // Ratio of 'a' to 'b' required to produce 'c'. + // APrxLoRcpF1() won't work here (at least for very high dynamic ranges). + // APrxMedRcpF1() is an IADD,FMA,MUL. + AF3 r=(c-b)*APrxMedRcpF3(a-b); + // Use the ratio as a cutoff to choose 'a' or 'b'. + // AGtZeroF1() is a MUL. + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 10-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC10F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(1023.0))*AF3_(1.0/1023.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/1023.0);b=b*b; + AF3 r=(c-b)*APrxMedRcpF3(a-b); + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/1023.0));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + AH1 FsrTepdDitH(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AH1(AFractF1(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(255.0))*AH3_(1.0/255.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/255.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(1023.0))*AH3_(1.0/1023.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/1023.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/1023.0));} +//============================================================================================================================== + // This computes dither for positions 'p' and 'p+{8,0}'. + AH2 FsrTepdDitHx2(AU2 p,AU1 f){ + AF2 x; + x.x=AF1_(p.x+f); + x.y=x.x+AF1_(8.0); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*AF2_(a)+AF2_(y*b); + return AH2(AFractF2(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(255.0))*AH2_(1.0/255.0); + nG=floor(nG*AH2_(255.0))*AH2_(1.0/255.0); + nB=floor(nB*AH2_(255.0))*AH2_(1.0/255.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/255.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/255.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/255.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/255.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/255.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(1023.0))*AH2_(1.0/1023.0); + nG=floor(nG*AH2_(1023.0))*AH2_(1.0/1023.0); + nB=floor(nB*AH2_(1023.0))*AH2_(1.0/1023.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/1023.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/1023.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/1023.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/1023.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/1023.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/1023.0));} +#endif + + +float insideBox(vec2 v) { + vec2 s = step(bLeft, v) - step(tRight, v); + return s.x * s.y; +} + +AF2 translateDest(AF2 pos) { + AF2 translatedPos = AF2(pos.x, pos.y); + translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x; + translatedPos.y = dstY0 < dstY1 ? dstY1 + dstY0 - translatedPos.y - 1 : translatedPos.y; + return translatedPos; +} + +void CurrFilter(AU2 pos) +{ + if((insideBox(vec2(pos.x, pos.y))) == 0) { + imageStore(imgOutput, ASU2(pos.x, pos.y), AF4(0,0,0,1)); + return; + } + AF3 c; + FsrEasuF(c, AU2(pos.x - bLeft.x, pos.y - bLeft.y), con0, con1, con2, con3); + imageStore(imgOutput, ASU2(translateDest(pos)), AF4(c, 1)); +} + +void main() { + srcW = abs(srcX1 - srcX0); + srcH = abs(srcY1 - srcY0); + dstW = abs(dstX1 - dstX0); + dstH = abs(dstY1 - dstY0); + + AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); + + setBounds(vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1), + vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0)); + + // Upscaling + FsrEasuCon(con0, con1, con2, con3, + srcW, srcH, // Viewport size (top left aligned) in the input image which is to be scaled. + srcW, srcH, // The size of the input image. + dstW, dstH); // The output resolution. + + CurrFilter(gxy); + gxy.x += 8u; + CurrFilter(gxy); + gxy.y += 8u; + CurrFilter(gxy); + gxy.x -= 8u; + CurrFilter(gxy); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv b/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv new file mode 100644 index 0000000000000000000000000000000000000000..c15b72ec6c278e354720e2518f556e6328973ab3 GIT binary patch literal 44672 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl b/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl new file mode 100644 index 000000000..785bc0c83 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl @@ -0,0 +1,3904 @@ +// Sharpening +#version 430 core +layout (local_size_x = 64) in; +layout( rgba8, binding = 0, set = 3) uniform image2D imgOutput; +layout( binding = 2 ) uniform invResolution +{ + vec2 invResolution_data; +}; +layout( binding = 3 ) uniform outvResolution +{ + vec2 outvResolution_data; +}; +layout( binding = 1, set = 2) uniform sampler2D source; +layout( binding = 4 ) uniform sharpening +{ + float sharpening_data; +}; + +#define A_GPU 1 +#define A_GLSL 1 +//============================================================================================================================== +// +// [A] SHADER PORTABILITY 1.20210629 +// +//============================================================================================================================== +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// MIT LICENSE +// =========== +// Copyright (c) 2014 Michal Drobot (for concepts used in "FLOAT APPROXIMATIONS"). +// ----------- +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// ----------- +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +// Software. +// ----------- +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// Common central point for high-level shading language and C portability for various shader headers. +//------------------------------------------------------------------------------------------------------------------------------ +// DEFINES +// ======= +// A_CPU ..... Include the CPU related code. +// A_GPU ..... Include the GPU related code. +// A_GLSL .... Using GLSL. +// A_HLSL .... Using HLSL. +// A_HLSL_6_2 Using HLSL 6.2 with new 'uint16_t' and related types (requires '-enable-16bit-types'). +// A_NO_16_BIT_CAST Don't use instructions that are not availabe in SPIR-V (needed for running A_HLSL_6_2 on Vulkan) +// A_GCC ..... Using a GCC compatible compiler (else assume MSVC compatible compiler by default). +// ======= +// A_BYTE .... Support 8-bit integer. +// A_HALF .... Support 16-bit integer and floating point. +// A_LONG .... Support 64-bit integer. +// A_DUBL .... Support 64-bit floating point. +// ======= +// A_WAVE .... Support wave-wide operations. +//------------------------------------------------------------------------------------------------------------------------------ +// To get #include "ffx_a.h" working in GLSL use '#extension GL_GOOGLE_include_directive:require'. +//------------------------------------------------------------------------------------------------------------------------------ +// SIMPLIFIED TYPE SYSTEM +// ====================== +// - All ints will be unsigned with exception of when signed is required. +// - Type naming simplified and shortened "A<type><#components>", +// - H = 16-bit float (half) +// - F = 32-bit float (float) +// - D = 64-bit float (double) +// - P = 1-bit integer (predicate, not using bool because 'B' is used for byte) +// - B = 8-bit integer (byte) +// - W = 16-bit integer (word) +// - U = 32-bit integer (unsigned) +// - L = 64-bit integer (long) +// - Using "AS<type><#components>" for signed when required. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure 'ALerp*(a,b,m)' does 'b*m+(-a*m+a)' (2 ops). +//------------------------------------------------------------------------------------------------------------------------------ +// CHANGE LOG +// ========== +// 20200914 - Expanded wave ops and prx code. +// 20200713 - Added [ZOL] section, fixed serious bugs in sRGB and Rec.709 color conversion code, etc. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COMMON +//============================================================================================================================== +#define A_2PI 6.28318530718 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// CPU +// +// +//============================================================================================================================== +#ifdef A_CPU + // Supporting user defined overrides. + #ifndef A_RESTRICT + #define A_RESTRICT __restrict + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifndef A_STATIC + #define A_STATIC static + #endif +//------------------------------------------------------------------------------------------------------------------------------ + // Same types across CPU and GPU. + // Predicate uses 32-bit integer (C friendly bool). + typedef uint32_t AP1; + typedef float AF1; + typedef double AD1; + typedef uint8_t AB1; + typedef uint16_t AW1; + typedef uint32_t AU1; + typedef uint64_t AL1; + typedef int8_t ASB1; + typedef int16_t ASW1; + typedef int32_t ASU1; + typedef int64_t ASL1; +//------------------------------------------------------------------------------------------------------------------------------ + #define AD1_(a) ((AD1)(a)) + #define AF1_(a) ((AF1)(a)) + #define AL1_(a) ((AL1)(a)) + #define AU1_(a) ((AU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1_(a) ((ASL1)(a)) + #define ASU1_(a) ((ASU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AU1 AU1_AF1(AF1 a){union{AF1 f;AU1 u;}bits;bits.f=a;return bits.u;} +//------------------------------------------------------------------------------------------------------------------------------ + #define A_TRUE 1 + #define A_FALSE 0 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// CPU/GPU PORTING +// +//------------------------------------------------------------------------------------------------------------------------------ +// Get CPU and GPU to share all setup code, without duplicate code paths. +// This uses a lower-case prefix for special vector constructs. +// - In C restrict pointers are used. +// - In the shading language, in/inout/out arguments are used. +// This depends on the ability to access a vector value in both languages via array syntax (aka color[2]). +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD1 *A_RESTRICT + #define retAD3 AD1 *A_RESTRICT + #define retAD4 AD1 *A_RESTRICT + #define retAF2 AF1 *A_RESTRICT + #define retAF3 AF1 *A_RESTRICT + #define retAF4 AF1 *A_RESTRICT + #define retAL2 AL1 *A_RESTRICT + #define retAL3 AL1 *A_RESTRICT + #define retAL4 AL1 *A_RESTRICT + #define retAU2 AU1 *A_RESTRICT + #define retAU3 AU1 *A_RESTRICT + #define retAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 AD1 *A_RESTRICT + #define inAD3 AD1 *A_RESTRICT + #define inAD4 AD1 *A_RESTRICT + #define inAF2 AF1 *A_RESTRICT + #define inAF3 AF1 *A_RESTRICT + #define inAF4 AF1 *A_RESTRICT + #define inAL2 AL1 *A_RESTRICT + #define inAL3 AL1 *A_RESTRICT + #define inAL4 AL1 *A_RESTRICT + #define inAU2 AU1 *A_RESTRICT + #define inAU3 AU1 *A_RESTRICT + #define inAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 AD1 *A_RESTRICT + #define inoutAD3 AD1 *A_RESTRICT + #define inoutAD4 AD1 *A_RESTRICT + #define inoutAF2 AF1 *A_RESTRICT + #define inoutAF3 AF1 *A_RESTRICT + #define inoutAF4 AF1 *A_RESTRICT + #define inoutAL2 AL1 *A_RESTRICT + #define inoutAL3 AL1 *A_RESTRICT + #define inoutAL4 AL1 *A_RESTRICT + #define inoutAU2 AU1 *A_RESTRICT + #define inoutAU3 AU1 *A_RESTRICT + #define inoutAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 AD1 *A_RESTRICT + #define outAD3 AD1 *A_RESTRICT + #define outAD4 AD1 *A_RESTRICT + #define outAF2 AF1 *A_RESTRICT + #define outAF3 AF1 *A_RESTRICT + #define outAF4 AF1 *A_RESTRICT + #define outAL2 AL1 *A_RESTRICT + #define outAL3 AL1 *A_RESTRICT + #define outAL4 AL1 *A_RESTRICT + #define outAU2 AU1 *A_RESTRICT + #define outAU3 AU1 *A_RESTRICT + #define outAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD1 x[2] + #define varAD3(x) AD1 x[3] + #define varAD4(x) AD1 x[4] + #define varAF2(x) AF1 x[2] + #define varAF3(x) AF1 x[3] + #define varAF4(x) AF1 x[4] + #define varAL2(x) AL1 x[2] + #define varAL3(x) AL1 x[3] + #define varAL4(x) AL1 x[4] + #define varAU2(x) AU1 x[2] + #define varAU3(x) AU1 x[3] + #define varAU4(x) AU1 x[4] +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) {x,y} + #define initAD3(x,y,z) {x,y,z} + #define initAD4(x,y,z,w) {x,y,z,w} + #define initAF2(x,y) {x,y} + #define initAF3(x,y,z) {x,y,z} + #define initAF4(x,y,z,w) {x,y,z,w} + #define initAL2(x,y) {x,y} + #define initAL3(x,y,z) {x,y,z} + #define initAL4(x,y,z,w) {x,y,z,w} + #define initAU2(x,y) {x,y} + #define initAU3(x,y,z) {x,y,z} + #define initAU4(x,y,z,w) {x,y,z,w} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Replace transcendentals with manual versions. +//============================================================================================================================== + #ifdef A_GCC + A_STATIC AD1 AAbsD1(AD1 a){return __builtin_fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return __builtin_fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(__builtin_abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(__builtin_llabs(ASL1_(a)));} + #else + A_STATIC AD1 AAbsD1(AD1 a){return fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(labs((long)ASL1_(a)));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ACosD1(AD1 a){return __builtin_cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return __builtin_cosf(a);} + #else + A_STATIC AD1 ACosD1(AD1 a){return cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return cosf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ADotD2(inAD2 a,inAD2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AD1 ADotD3(inAD3 a,inAD3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AD1 ADotD4(inAD4 a,inAD4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} + A_STATIC AF1 ADotF2(inAF2 a,inAF2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AF1 ADotF3(inAF3 a,inAF3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AF1 ADotF4(inAF4 a,inAF4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AExp2D1(AD1 a){return __builtin_exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return __builtin_exp2f(a);} + #else + A_STATIC AD1 AExp2D1(AD1 a){return exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return exp2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AFloorD1(AD1 a){return __builtin_floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return __builtin_floorf(a);} + #else + A_STATIC AD1 AFloorD1(AD1 a){return floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return floorf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ALerpD1(AD1 a,AD1 b,AD1 c){return b*c+(-a*c+a);} + A_STATIC AF1 ALerpF1(AF1 a,AF1 b,AF1 c){return b*c+(-a*c+a);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ALog2D1(AD1 a){return __builtin_log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return __builtin_log2f(a);} + #else + A_STATIC AD1 ALog2D1(AD1 a){return log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return log2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMaxD1(AD1 a,AD1 b){return a>b?a:b;} + A_STATIC AF1 AMaxF1(AF1 a,AF1 b){return a>b?a:b;} + A_STATIC AL1 AMaxL1(AL1 a,AL1 b){return a>b?a:b;} + A_STATIC AU1 AMaxU1(AU1 a,AU1 b){return a>b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + // These follow the convention that A integer types don't have signage, until they are operated on. + A_STATIC AL1 AMaxSL1(AL1 a,AL1 b){return (ASL1_(a)>ASL1_(b))?a:b;} + A_STATIC AU1 AMaxSU1(AU1 a,AU1 b){return (ASU1_(a)>ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMinD1(AD1 a,AD1 b){return a<b?a:b;} + A_STATIC AF1 AMinF1(AF1 a,AF1 b){return a<b?a:b;} + A_STATIC AL1 AMinL1(AL1 a,AL1 b){return a<b?a:b;} + A_STATIC AU1 AMinU1(AU1 a,AU1 b){return a<b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AL1 AMinSL1(AL1 a,AL1 b){return (ASL1_(a)<ASL1_(b))?a:b;} + A_STATIC AU1 AMinSU1(AU1 a,AU1 b){return (ASU1_(a)<ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARcpD1(AD1 a){return 1.0/a;} + A_STATIC AF1 ARcpF1(AF1 a){return 1.0f/a;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AL1 AShrSL1(AL1 a,AL1 b){return AL1_(ASL1_(a)>>ASL1_(b));} + A_STATIC AU1 AShrSU1(AU1 a,AU1 b){return AU1_(ASU1_(a)>>ASU1_(b));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASinD1(AD1 a){return __builtin_sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return __builtin_sinf(a);} + #else + A_STATIC AD1 ASinD1(AD1 a){return sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return sinf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASqrtD1(AD1 a){return __builtin_sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return __builtin_sqrtf(a);} + #else + A_STATIC AD1 ASqrtD1(AD1 a){return sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return sqrtf(a);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + A_STATIC AD1 AClampD1(AD1 x,AD1 n,AD1 m){return AMaxD1(n,AMinD1(x,m));} + A_STATIC AF1 AClampF1(AF1 x,AF1 n,AF1 m){return AMaxF1(n,AMinF1(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AFractD1(AD1 a){return a-AFloorD1(a);} + A_STATIC AF1 AFractF1(AF1 a){return a-AFloorF1(a);} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 APowD1(AD1 a,AD1 b){return AExp2D1(b*ALog2D1(a));} + A_STATIC AF1 APowF1(AF1 a,AF1 b){return AExp2F1(b*ALog2F1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARsqD1(AD1 a){return ARcpD1(ASqrtD1(a));} + A_STATIC AF1 ARsqF1(AF1 a){return ARcpF1(ASqrtF1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ASatD1(AD1 a){return AMinD1(1.0,AMaxD1(0.0,a));} + A_STATIC AF1 ASatF1(AF1 a){return AMinF1(1.0f,AMaxF1(0.0f,a));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + A_STATIC retAD2 opAAbsD2(outAD2 d,inAD2 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);return d;} + A_STATIC retAD3 opAAbsD3(outAD3 d,inAD3 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);return d;} + A_STATIC retAD4 opAAbsD4(outAD4 d,inAD4 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);d[3]=AAbsD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAbsF2(outAF2 d,inAF2 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);return d;} + A_STATIC retAF3 opAAbsF3(outAF3 d,inAF3 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);return d;} + A_STATIC retAF4 opAAbsF4(outAF4 d,inAF4 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);d[3]=AAbsF1(a[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opACpyD2(outAD2 d,inAD2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAD3 opACpyD3(outAD3 d,inAD3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAD4 opACpyD4(outAD4 d,inAD4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opACpyF2(outAF2 d,inAF2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAF3 opACpyF3(outAF3 d,inAF3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAF4 opACpyF4(outAF4 d,inAF4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);return d;} + A_STATIC retAD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);return d;} + A_STATIC retAD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);d[3]=ALerpD1(a[3],b[3],c[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);return d;} + A_STATIC retAF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);return d;} + A_STATIC retAF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);d[3]=ALerpF1(a[3],b[3],c[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);return d;} + A_STATIC retAD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);return d;} + A_STATIC retAD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);d[3]=ALerpD1(a[3],b[3],c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);return d;} + A_STATIC retAF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);return d;} + A_STATIC retAF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);d[3]=ALerpF1(a[3],b[3],c);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);d[3]=AMaxD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);d[3]=AMaxF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);d[3]=AMinD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);d[3]=AMinF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opANegD2(outAD2 d,inAD2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAD3 opANegD3(outAD3 d,inAD3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAD4 opANegD4(outAD4 d,inAD4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opANegF2(outAF2 d,inAF2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAF3 opANegF3(outAF3 d,inAF3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAF4 opANegF4(outAF4 d,inAF4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opARcpD2(outAD2 d,inAD2 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);return d;} + A_STATIC retAD3 opARcpD3(outAD3 d,inAD3 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);return d;} + A_STATIC retAD4 opARcpD4(outAD4 d,inAD4 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);d[3]=ARcpD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opARcpF2(outAF2 d,inAF2 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);return d;} + A_STATIC retAF3 opARcpF3(outAF3 d,inAF3 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);return d;} + A_STATIC retAF4 opARcpF4(outAF4 d,inAF4 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);d[3]=ARcpF1(a[3]);return d;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF FLOAT PACKING +//============================================================================================================================== + // Convert float to half (in lower 16-bits of output). + // Same fast technique as documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf + // Supports denormals. + // Conversion rules are to make computations possibly "safer" on the GPU, + // -INF & -NaN -> -65504 + // +INF & +NaN -> +65504 + A_STATIC AU1 AU1_AH1_AF1(AF1 f){ + static AW1 base[512]={ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100, + 0x0200,0x0400,0x0800,0x0c00,0x1000,0x1400,0x1800,0x1c00,0x2000,0x2400,0x2800,0x2c00,0x3000,0x3400,0x3800,0x3c00, + 0x4000,0x4400,0x4800,0x4c00,0x5000,0x5400,0x5800,0x5c00,0x6000,0x6400,0x6800,0x6c00,0x7000,0x7400,0x7800,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8001,0x8002,0x8004,0x8008,0x8010,0x8020,0x8040,0x8080,0x8100, + 0x8200,0x8400,0x8800,0x8c00,0x9000,0x9400,0x9800,0x9c00,0xa000,0xa400,0xa800,0xac00,0xb000,0xb400,0xb800,0xbc00, + 0xc000,0xc400,0xc800,0xcc00,0xd000,0xd400,0xd800,0xdc00,0xe000,0xe400,0xe800,0xec00,0xf000,0xf400,0xf800,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff}; + static AB1 shift[512]={ + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}; + union{AF1 f;AU1 u;}bits;bits.f=f;AU1 u=bits.u;AU1 i=u>>23;return (AU1)(base[i])+((u&0x7fffff)>>shift[i]);} +//------------------------------------------------------------------------------------------------------------------------------ + // Used to output packed constant. + A_STATIC AU1 AU1_AH2_AF2(inAF2 a){return AU1_AH1_AF1(a[0])+(AU1_AH1_AF1(a[1])<<16);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GLSL +// +// +//============================================================================================================================== +#if defined(A_GLSL) && defined(A_GPU) + #ifndef A_SKIP_EXT + #ifdef A_HALF + #extension GL_EXT_shader_16bit_storage:require + #extension GL_EXT_shader_explicit_arithmetic_types:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_LONG + #extension GL_ARB_gpu_shader_int64:require + #extension GL_NV_shader_atomic_int64:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_WAVE + #extension GL_KHR_shader_subgroup_arithmetic:require + #extension GL_KHR_shader_subgroup_ballot:require + #extension GL_KHR_shader_subgroup_quad:require + #extension GL_KHR_shader_subgroup_shuffle:require + #endif + #endif +//============================================================================================================================== + #define AP1 bool + #define AP2 bvec2 + #define AP3 bvec3 + #define AP4 bvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 vec2 + #define AF3 vec3 + #define AF4 vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uvec2 + #define AU3 uvec3 + #define AU4 uvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 ivec2 + #define ASU3 ivec3 + #define ASU4 ivec4 +//============================================================================================================================== + #define AF1_AU1(x) uintBitsToFloat(AU1(x)) + #define AF2_AU2(x) uintBitsToFloat(AU2(x)) + #define AF3_AU3(x) uintBitsToFloat(AU3(x)) + #define AF4_AU4(x) uintBitsToFloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) floatBitsToUint(AF1(x)) + #define AU2_AF2(x) floatBitsToUint(AF2(x)) + #define AU3_AF3(x) floatBitsToUint(AF3(x)) + #define AU4_AF4(x) floatBitsToUint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return packHalf2x16(AF2(a,0.0));} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2_AF2 packHalf2x16 + #define AU1_AW2Unorm_AF2 packUnorm2x16 + #define AU1_AB4Unorm_AF4 packUnorm4x8 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF2_AH2_AU1 unpackHalf2x16 + #define AF2_AW2Unorm_AU1 unpackUnorm2x16 + #define AF4_AB4Unorm_AU1 unpackUnorm4x8 +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){return bitfieldExtract(src,ASU1(off),ASU1(bits));} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + // Proxy for V_BFI_B32 where the 'mask' is set as 'bits', 'mask=(1<<bits)-1', and 'bits' needs to be an immediate. + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){return bitfieldInsert(src,ins,0,ASU1(bits));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MED3_F32. + AF1 AClampF1(AF1 x,AF1 n,AF1 m){return clamp(x,n,m);} + AF2 AClampF2(AF2 x,AF2 n,AF2 m){return clamp(x,n,m);} + AF3 AClampF3(AF3 x,AF3 n,AF3 m){return clamp(x,n,m);} + AF4 AClampF4(AF4 x,AF4 n,AF4 m){return clamp(x,n,m);} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F32 (note DX frac() is different). + AF1 AFractF1(AF1 x){return fract(x);} + AF2 AFractF2(AF2 x){return fract(x);} + AF3 AFractF3(AF3 x){return fract(x);} + AF4 AFractF4(AF4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ALerpF1(AF1 x,AF1 y,AF1 a){return mix(x,y,a);} + AF2 ALerpF2(AF2 x,AF2 y,AF2 a){return mix(x,y,a);} + AF3 ALerpF3(AF3 x,AF3 y,AF3 a){return mix(x,y,a);} + AF4 ALerpF4(AF4 x,AF4 y,AF4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MAX3_F32. + AF1 AMax3F1(AF1 x,AF1 y,AF1 z){return max(x,max(y,z));} + AF2 AMax3F2(AF2 x,AF2 y,AF2 z){return max(x,max(y,z));} + AF3 AMax3F3(AF3 x,AF3 y,AF3 z){return max(x,max(y,z));} + AF4 AMax3F4(AF4 x,AF4 y,AF4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3SU1(AU1 x,AU1 y,AU1 z){return AU1(max(ASU1(x),max(ASU1(y),ASU1(z))));} + AU2 AMax3SU2(AU2 x,AU2 y,AU2 z){return AU2(max(ASU2(x),max(ASU2(y),ASU2(z))));} + AU3 AMax3SU3(AU3 x,AU3 y,AU3 z){return AU3(max(ASU3(x),max(ASU3(y),ASU3(z))));} + AU4 AMax3SU4(AU4 x,AU4 y,AU4 z){return AU4(max(ASU4(x),max(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3U1(AU1 x,AU1 y,AU1 z){return max(x,max(y,z));} + AU2 AMax3U2(AU2 x,AU2 y,AU2 z){return max(x,max(y,z));} + AU3 AMax3U3(AU3 x,AU3 y,AU3 z){return max(x,max(y,z));} + AU4 AMax3U4(AU4 x,AU4 y,AU4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMaxSU1(AU1 a,AU1 b){return AU1(max(ASU1(a),ASU1(b)));} + AU2 AMaxSU2(AU2 a,AU2 b){return AU2(max(ASU2(a),ASU2(b)));} + AU3 AMaxSU3(AU3 a,AU3 b){return AU3(max(ASU3(a),ASU3(b)));} + AU4 AMaxSU4(AU4 a,AU4 b){return AU4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Clamp has an easier pattern match for med3 when some ordering is known. + // V_MED3_F32. + AF1 AMed3F1(AF1 x,AF1 y,AF1 z){return max(min(x,y),min(max(x,y),z));} + AF2 AMed3F2(AF2 x,AF2 y,AF2 z){return max(min(x,y),min(max(x,y),z));} + AF3 AMed3F3(AF3 x,AF3 y,AF3 z){return max(min(x,y),min(max(x,y),z));} + AF4 AMed3F4(AF4 x,AF4 y,AF4 z){return max(min(x,y),min(max(x,y),z));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_MIN3_F32. + AF1 AMin3F1(AF1 x,AF1 y,AF1 z){return min(x,min(y,z));} + AF2 AMin3F2(AF2 x,AF2 y,AF2 z){return min(x,min(y,z));} + AF3 AMin3F3(AF3 x,AF3 y,AF3 z){return min(x,min(y,z));} + AF4 AMin3F4(AF4 x,AF4 y,AF4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3SU1(AU1 x,AU1 y,AU1 z){return AU1(min(ASU1(x),min(ASU1(y),ASU1(z))));} + AU2 AMin3SU2(AU2 x,AU2 y,AU2 z){return AU2(min(ASU2(x),min(ASU2(y),ASU2(z))));} + AU3 AMin3SU3(AU3 x,AU3 y,AU3 z){return AU3(min(ASU3(x),min(ASU3(y),ASU3(z))));} + AU4 AMin3SU4(AU4 x,AU4 y,AU4 z){return AU4(min(ASU4(x),min(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3U1(AU1 x,AU1 y,AU1 z){return min(x,min(y,z));} + AU2 AMin3U2(AU2 x,AU2 y,AU2 z){return min(x,min(y,z));} + AU3 AMin3U3(AU3 x,AU3 y,AU3 z){return min(x,min(y,z));} + AU4 AMin3U4(AU4 x,AU4 y,AU4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMinSU1(AU1 a,AU1 b){return AU1(min(ASU1(a),ASU1(b)));} + AU2 AMinSU2(AU2 a,AU2 b){return AU2(min(ASU2(a),ASU2(b)));} + AU3 AMinSU3(AU3 a,AU3 b){return AU3(min(ASU3(a),ASU3(b)));} + AU4 AMinSU4(AU4 a,AU4 b){return AU4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Normalized trig. Valid input domain is {-256 to +256}. No GLSL compiler intrinsic exists to map to this currently. + // V_COS_F32. + AF1 ANCosF1(AF1 x){return cos(x*AF1_(A_2PI));} + AF2 ANCosF2(AF2 x){return cos(x*AF2_(A_2PI));} + AF3 ANCosF3(AF3 x){return cos(x*AF3_(A_2PI));} + AF4 ANCosF4(AF4 x){return cos(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + // Normalized trig. Valid input domain is {-256 to +256}. No GLSL compiler intrinsic exists to map to this currently. + // V_SIN_F32. + AF1 ANSinF1(AF1 x){return sin(x*AF1_(A_2PI));} + AF2 ANSinF2(AF2 x){return sin(x*AF2_(A_2PI));} + AF3 ANSinF3(AF3 x){return sin(x*AF3_(A_2PI));} + AF4 ANSinF4(AF4 x){return sin(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARcpF1(AF1 x){return AF1_(1.0)/x;} + AF2 ARcpF2(AF2 x){return AF2_(1.0)/x;} + AF3 ARcpF3(AF3 x){return AF3_(1.0)/x;} + AF4 ARcpF4(AF4 x){return AF4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARsqF1(AF1 x){return AF1_(1.0)/sqrt(x);} + AF2 ARsqF2(AF2 x){return AF2_(1.0)/sqrt(x);} + AF3 ARsqF3(AF3 x){return AF3_(1.0)/sqrt(x);} + AF4 ARsqF4(AF4 x){return AF4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASatF1(AF1 x){return clamp(x,AF1_(0.0),AF1_(1.0));} + AF2 ASatF2(AF2 x){return clamp(x,AF2_(0.0),AF2_(1.0));} + AF3 ASatF3(AF3 x){return clamp(x,AF3_(0.0),AF3_(1.0));} + AF4 ASatF4(AF4 x){return clamp(x,AF4_(0.0),AF4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AShrSU1(AU1 a,AU1 b){return AU1(ASU1(a)>>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #define AB1 uint8_t + #define AB2 u8vec2 + #define AB3 u8vec3 + #define AB4 u8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASB1 int8_t + #define ASB2 i8vec2 + #define ASB3 i8vec3 + #define ASB4 i8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + AB1 AB1_x(AB1 a){return AB1(a);} + AB2 AB2_x(AB1 a){return AB2(a,a);} + AB3 AB3_x(AB1 a){return AB3(a,a,a);} + AB4 AB4_x(AB1 a){return AB4(a,a,a,a);} + #define AB1_(a) AB1_x(AB1(a)) + #define AB2_(a) AB2_x(AB1(a)) + #define AB3_(a) AB3_x(AB1(a)) + #define AB4_(a) AB4_x(AB1(a)) + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 float16_t + #define AH2 f16vec2 + #define AH3 f16vec3 + #define AH4 f16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 u16vec2 + #define AW3 u16vec3 + #define AW4 u16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 i16vec2 + #define ASW3 i16vec3 + #define ASW4 i16vec4 +//============================================================================================================================== + #define AH2_AU1(x) unpackFloat2x16(AU1(x)) + AH4 AH4_AU2_x(AU2 x){return AH4(unpackFloat2x16(x.x),unpackFloat2x16(x.y));} + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) unpackUint2x16(AU1(x)) + #define AW4_AU2(x) unpackUint4x16(pack64(AU2(x))) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2(x) packFloat2x16(AH2(x)) + AU2 AU2_AH4_x(AH4 x){return AU2(packFloat2x16(x.xy),packFloat2x16(x.zw));} + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) packUint2x16(AW2(x)) + #define AU2_AW4(x) unpack32(packUint4x16(AW4(x))) +//============================================================================================================================== + #define AW1_AH1(x) halfBitsToUint16(AH1(x)) + #define AW2_AH2(x) halfBitsToUint16(AH2(x)) + #define AW3_AH3(x) halfBitsToUint16(AH3(x)) + #define AW4_AH4(x) halfBitsToUint16(AH4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AH1_AW1(x) uint16BitsToHalf(AW1(x)) + #define AH2_AW2(x) uint16BitsToHalf(AW2(x)) + #define AH3_AW3(x) uint16BitsToHalf(AW3(x)) + #define AH4_AW4(x) uint16BitsToHalf(AW4(x)) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return clamp(x,n,m);} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return clamp(x,n,m);} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return clamp(x,n,m);} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return clamp(x,n,m);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFractH1(AH1 x){return fract(x);} + AH2 AFractH2(AH2 x){return fract(x);} + AH3 AFractH3(AH3 x){return fract(x);} + AH4 AFractH4(AH4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return mix(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return mix(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return mix(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of max3. + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of min3. + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return AH1_(1.0)/x;} + AH2 ARcpH2(AH2 x){return AH2_(1.0)/x;} + AH3 ARcpH3(AH3 x){return AH3_(1.0)/x;} + AH4 ARcpH4(AH4 x){return AH4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return AH1_(1.0)/sqrt(x);} + AH2 ARsqH2(AH2 x){return AH2_(1.0)/sqrt(x);} + AH3 ARsqH3(AH3 x){return AH3_(1.0)/sqrt(x);} + AH4 ARsqH4(AH4 x){return AH4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return clamp(x,AH1_(0.0),AH1_(1.0));} + AH2 ASatH2(AH2 x){return clamp(x,AH2_(0.0),AH2_(1.0));} + AH3 ASatH3(AH3 x){return clamp(x,AH3_(0.0),AH3_(1.0));} + AH4 ASatH4(AH4 x){return clamp(x,AH4_(0.0),AH4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 dvec2 + #define AD3 dvec3 + #define AD4 dvec4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 x){return fract(x);} + AD2 AFractD2(AD2 x){return fract(x);} + AD3 AFractD3(AD3 x){return fract(x);} + AD4 AFractD4(AD4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return mix(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return mix(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return mix(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return AD1_(1.0)/x;} + AD2 ARcpD2(AD2 x){return AD2_(1.0)/x;} + AD3 ARcpD3(AD3 x){return AD3_(1.0)/x;} + AD4 ARcpD4(AD4 x){return AD4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return AD1_(1.0)/sqrt(x);} + AD2 ARsqD2(AD2 x){return AD2_(1.0)/sqrt(x);} + AD3 ARsqD3(AD3 x){return AD3_(1.0)/sqrt(x);} + AD4 ARsqD4(AD4 x){return AD4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return clamp(x,AD1_(0.0),AD1_(1.0));} + AD2 ASatD2(AD2 x){return clamp(x,AD2_(0.0),AD2_(1.0));} + AD3 ASatD3(AD3 x){return clamp(x,AD3_(0.0),AD3_(1.0));} + AD4 ASatD4(AD4 x){return clamp(x,AD4_(0.0),AD4_(1.0));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #define AL1 uint64_t + #define AL2 u64vec2 + #define AL3 u64vec3 + #define AL4 u64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1 int64_t + #define ASL2 i64vec2 + #define ASL3 i64vec3 + #define ASL4 i64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AL1_AU2(x) packUint2x32(AU2(x)) + #define AU2_AL1(x) unpackUint2x32(AL1(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AL1_x(AL1 a){return AL1(a);} + AL2 AL2_x(AL1 a){return AL2(a,a);} + AL3 AL3_x(AL1 a){return AL3(a,a,a);} + AL4 AL4_x(AL1 a){return AL4(a,a,a,a);} + #define AL1_(a) AL1_x(AL1(a)) + #define AL2_(a) AL2_x(AL1(a)) + #define AL3_(a) AL3_x(AL1(a)) + #define AL4_(a) AL4_x(AL1(a)) +//============================================================================================================================== + AL1 AAbsSL1(AL1 a){return AL1(abs(ASL1(a)));} + AL2 AAbsSL2(AL2 a){return AL2(abs(ASL2(a)));} + AL3 AAbsSL3(AL3 a){return AL3(abs(ASL3(a)));} + AL4 AAbsSL4(AL4 a){return AL4(abs(ASL4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMaxSL1(AL1 a,AL1 b){return AL1(max(ASU1(a),ASU1(b)));} + AL2 AMaxSL2(AL2 a,AL2 b){return AL2(max(ASU2(a),ASU2(b)));} + AL3 AMaxSL3(AL3 a,AL3 b){return AL3(max(ASU3(a),ASU3(b)));} + AL4 AMaxSL4(AL4 a,AL4 b){return AL4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMinSL1(AL1 a,AL1 b){return AL1(min(ASU1(a),ASU1(b)));} + AL2 AMinSL2(AL2 a,AL2 b){return AL2(min(ASU2(a),ASU2(b)));} + AL3 AMinSL3(AL3 a,AL3 b){return AL3(min(ASU3(a),ASU3(b)));} + AL4 AMinSL4(AL4 a,AL4 b){return AL4(min(ASU4(a),ASU4(b)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// WAVE OPERATIONS +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return subgroupShuffleXor(v,x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return subgroupShuffleXor(v,x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return subgroupShuffleXor(v,x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return subgroupShuffleXor(v,x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return subgroupShuffleXor(v,x);} + AU2 AWaveXorU2(AU2 v,AU1 x){return subgroupShuffleXor(v,x);} + AU3 AWaveXorU3(AU3 v,AU1 x){return subgroupShuffleXor(v,x);} + AU4 AWaveXorU4(AU4 v,AU1 x){return subgroupShuffleXor(v,x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(subgroupShuffleXor(AU1_AH2(v),x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(subgroupShuffleXor(AU2_AH4(v),x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(subgroupShuffleXor(AU1_AW2(v),x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU2(subgroupShuffleXor(AU2_AW4(v),x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// HLSL +// +// +//============================================================================================================================== +#if defined(A_HLSL) && defined(A_GPU) + #ifdef A_HLSL_6_2 + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float32_t + #define AF2 float32_t2 + #define AF3 float32_t3 + #define AF4 float32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint32_t + #define AU2 uint32_t2 + #define AU3 uint32_t3 + #define AU4 uint32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int32_t + #define ASU2 int32_t2 + #define ASU3 int32_t3 + #define ASU4 int32_t4 + #else + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 float2 + #define AF3 float3 + #define AF4 float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uint2 + #define AU3 uint3 + #define AU4 uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 int2 + #define ASU3 int3 + #define ASU4 int4 + #endif +//============================================================================================================================== + #define AF1_AU1(x) asfloat(AU1(x)) + #define AF2_AU2(x) asfloat(AU2(x)) + #define AF3_AU3(x) asfloat(AU3(x)) + #define AF4_AU4(x) asfloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) asuint(AF1(x)) + #define AU2_AF2(x) asuint(AF2(x)) + #define AU3_AF3(x) asuint(AF3(x)) + #define AU4_AF4(x) asuint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return f32tof16(a);} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_AF2_x(AF2 a){return f32tof16(a.x)|(f32tof16(a.y)<<16);} + #define AU1_AH2_AF2(a) AU1_AH2_AF2_x(AF2(a)) + #define AU1_AB4Unorm_AF4(x) D3DCOLORtoUBYTE4(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AF2 AF2_AH2_AU1_x(AU1 x){return AF2(f16tof32(x&0xFFFF),f16tof32(x>>16));} + #define AF2_AH2_AU1(x) AF2_AH2_AU1_x(AU1(x)) +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){AU1 mask=(1u<<bits)-1;return (src>>off)&mask;} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){AU1 mask=(1u<<bits)-1;return (ins&mask)|(src&(~mask));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AClampF1(AF1 x,AF1 n,AF1 m){return max(n,min(x,m));} + AF2 AClampF2(AF2 x,AF2 n,AF2 m){return max(n,min(x,m));} + AF3 AClampF3(AF3 x,AF3 n,AF3 m){return max(n,min(x,m));} + AF4 AClampF4(AF4 x,AF4 n,AF4 m){return max(n,min(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFractF1(AF1 x){return x-floor(x);} + AF2 AFractF2(AF2 x){return x-floor(x);} + AF3 AFractF3(AF3 x){return x-floor(x);} + AF4 AFractF4(AF4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ALerpF1(AF1 x,AF1 y,AF1 a){return lerp(x,y,a);} + AF2 ALerpF2(AF2 x,AF2 y,AF2 a){return lerp(x,y,a);} + AF3 ALerpF3(AF3 x,AF3 y,AF3 a){return lerp(x,y,a);} + AF4 ALerpF4(AF4 x,AF4 y,AF4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMax3F1(AF1 x,AF1 y,AF1 z){return max(x,max(y,z));} + AF2 AMax3F2(AF2 x,AF2 y,AF2 z){return max(x,max(y,z));} + AF3 AMax3F3(AF3 x,AF3 y,AF3 z){return max(x,max(y,z));} + AF4 AMax3F4(AF4 x,AF4 y,AF4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3SU1(AU1 x,AU1 y,AU1 z){return AU1(max(ASU1(x),max(ASU1(y),ASU1(z))));} + AU2 AMax3SU2(AU2 x,AU2 y,AU2 z){return AU2(max(ASU2(x),max(ASU2(y),ASU2(z))));} + AU3 AMax3SU3(AU3 x,AU3 y,AU3 z){return AU3(max(ASU3(x),max(ASU3(y),ASU3(z))));} + AU4 AMax3SU4(AU4 x,AU4 y,AU4 z){return AU4(max(ASU4(x),max(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMax3U1(AU1 x,AU1 y,AU1 z){return max(x,max(y,z));} + AU2 AMax3U2(AU2 x,AU2 y,AU2 z){return max(x,max(y,z));} + AU3 AMax3U3(AU3 x,AU3 y,AU3 z){return max(x,max(y,z));} + AU4 AMax3U4(AU4 x,AU4 y,AU4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMaxSU1(AU1 a,AU1 b){return AU1(max(ASU1(a),ASU1(b)));} + AU2 AMaxSU2(AU2 a,AU2 b){return AU2(max(ASU2(a),ASU2(b)));} + AU3 AMaxSU3(AU3 a,AU3 b){return AU3(max(ASU3(a),ASU3(b)));} + AU4 AMaxSU4(AU4 a,AU4 b){return AU4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMed3F1(AF1 x,AF1 y,AF1 z){return max(min(x,y),min(max(x,y),z));} + AF2 AMed3F2(AF2 x,AF2 y,AF2 z){return max(min(x,y),min(max(x,y),z));} + AF3 AMed3F3(AF3 x,AF3 y,AF3 z){return max(min(x,y),min(max(x,y),z));} + AF4 AMed3F4(AF4 x,AF4 y,AF4 z){return max(min(x,y),min(max(x,y),z));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AMin3F1(AF1 x,AF1 y,AF1 z){return min(x,min(y,z));} + AF2 AMin3F2(AF2 x,AF2 y,AF2 z){return min(x,min(y,z));} + AF3 AMin3F3(AF3 x,AF3 y,AF3 z){return min(x,min(y,z));} + AF4 AMin3F4(AF4 x,AF4 y,AF4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3SU1(AU1 x,AU1 y,AU1 z){return AU1(min(ASU1(x),min(ASU1(y),ASU1(z))));} + AU2 AMin3SU2(AU2 x,AU2 y,AU2 z){return AU2(min(ASU2(x),min(ASU2(y),ASU2(z))));} + AU3 AMin3SU3(AU3 x,AU3 y,AU3 z){return AU3(min(ASU3(x),min(ASU3(y),ASU3(z))));} + AU4 AMin3SU4(AU4 x,AU4 y,AU4 z){return AU4(min(ASU4(x),min(ASU4(y),ASU4(z))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMin3U1(AU1 x,AU1 y,AU1 z){return min(x,min(y,z));} + AU2 AMin3U2(AU2 x,AU2 y,AU2 z){return min(x,min(y,z));} + AU3 AMin3U3(AU3 x,AU3 y,AU3 z){return min(x,min(y,z));} + AU4 AMin3U4(AU4 x,AU4 y,AU4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AMinSU1(AU1 a,AU1 b){return AU1(min(ASU1(a),ASU1(b)));} + AU2 AMinSU2(AU2 a,AU2 b){return AU2(min(ASU2(a),ASU2(b)));} + AU3 AMinSU3(AU3 a,AU3 b){return AU3(min(ASU3(a),ASU3(b)));} + AU4 AMinSU4(AU4 a,AU4 b){return AU4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ANCosF1(AF1 x){return cos(x*AF1_(A_2PI));} + AF2 ANCosF2(AF2 x){return cos(x*AF2_(A_2PI));} + AF3 ANCosF3(AF3 x){return cos(x*AF3_(A_2PI));} + AF4 ANCosF4(AF4 x){return cos(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ANSinF1(AF1 x){return sin(x*AF1_(A_2PI));} + AF2 ANSinF2(AF2 x){return sin(x*AF2_(A_2PI));} + AF3 ANSinF3(AF3 x){return sin(x*AF3_(A_2PI));} + AF4 ANSinF4(AF4 x){return sin(x*AF4_(A_2PI));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARcpF1(AF1 x){return rcp(x);} + AF2 ARcpF2(AF2 x){return rcp(x);} + AF3 ARcpF3(AF3 x){return rcp(x);} + AF4 ARcpF4(AF4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ARsqF1(AF1 x){return rsqrt(x);} + AF2 ARsqF2(AF2 x){return rsqrt(x);} + AF3 ARsqF3(AF3 x){return rsqrt(x);} + AF4 ARsqF4(AF4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASatF1(AF1 x){return saturate(x);} + AF2 ASatF2(AF2 x){return saturate(x);} + AF3 ASatF3(AF3 x){return saturate(x);} + AF4 ASatF4(AF4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AShrSU1(AU1 a,AU1 b){return AU1(ASU1(a)>>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define AH1 float16_t + #define AH2 float16_t2 + #define AH3 float16_t3 + #define AH4 float16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 uint16_t2 + #define AW3 uint16_t3 + #define AW4 uint16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 int16_t2 + #define ASW3 int16_t3 + #define ASW4 int16_t4 + #else + #define AH1 min16float + #define AH2 min16float2 + #define AH3 min16float3 + #define AH4 min16float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 min16uint + #define AW2 min16uint2 + #define AW3 min16uint3 + #define AW4 min16uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 min16int + #define ASW2 min16int2 + #define ASW3 min16int3 + #define ASW4 min16int4 + #endif +//============================================================================================================================== + // Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). + // Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ + AH2 AH2_AU1_x(AU1 x){AF2 t=f16tof32(AU2(x&0xFFFF,x>>16));return AH2(t);} + AH4 AH4_AU2_x(AU2 x){return AH4(AH2_AU1_x(x.x),AH2_AU1_x(x.y));} + AW2 AW2_AU1_x(AU1 x){AU2 t=AU2(x&0xFFFF,x>>16);return AW2(t);} + AW4 AW4_AU2_x(AU2 x){return AW4(AW2_AU1_x(x.x),AW2_AU1_x(x.y));} + #define AH2_AU1(x) AH2_AU1_x(AU1(x)) + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) AW2_AU1_x(AU1(x)) + #define AW4_AU2(x) AW4_AU2_x(AU2(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_x(AH2 x){return f32tof16(x.x)+(f32tof16(x.y)<<16);} + AU2 AU2_AH4_x(AH4 x){return AU2(AU1_AH2_x(x.xy),AU1_AH2_x(x.zw));} + AU1 AU1_AW2_x(AW2 x){return AU1(x.x)+(AU1(x.y)<<16);} + AU2 AU2_AW4_x(AW4 x){return AU2(AU1_AW2_x(x.xy),AU1_AW2_x(x.zw));} + #define AU1_AH2(x) AU1_AH2_x(AH2(x)) + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) AU1_AW2_x(AW2(x)) + #define AU2_AW4(x) AU2_AW4_x(AW4(x)) +//============================================================================================================================== + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AW1_AH1(x) asuint16(x) + #define AW2_AH2(x) asuint16(x) + #define AW3_AH3(x) asuint16(x) + #define AW4_AH4(x) asuint16(x) + #else + #define AW1_AH1(a) AW1(f32tof16(AF1(a))) + #define AW2_AH2(a) AW2(AW1_AH1((a).x),AW1_AH1((a).y)) + #define AW3_AH3(a) AW3(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z)) + #define AW4_AH4(a) AW4(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z),AW1_AH1((a).w)) + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AH1_AW1(x) asfloat16(x) + #define AH2_AW2(x) asfloat16(x) + #define AH3_AW3(x) asfloat16(x) + #define AH4_AW4(x) asfloat16(x) + #else + #define AH1_AW1(a) AH1(f16tof32(AU1(a))) + #define AH2_AW2(a) AH2(AH1_AW1((a).x),AH1_AW1((a).y)) + #define AH3_AW3(a) AH3(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z)) + #define AH4_AW4(a) AH4(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z),AH1_AW1((a).w)) + #endif +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return max(n,min(x,m));} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return max(n,min(x,m));} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return max(n,min(x,m));} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return max(n,min(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F16 (note DX frac() is different). + AH1 AFractH1(AH1 x){return x-floor(x);} + AH2 AFractH2(AH2 x){return x-floor(x);} + AH3 AFractH3(AH3 x){return x-floor(x);} + AH4 AFractH4(AH4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return lerp(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return lerp(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return lerp(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return rcp(x);} + AH2 ARcpH2(AH2 x){return rcp(x);} + AH3 ARcpH3(AH3 x){return rcp(x);} + AH4 ARcpH4(AH4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return rsqrt(x);} + AH2 ARsqH2(AH2 x){return rsqrt(x);} + AH3 ARsqH3(AH3 x){return rsqrt(x);} + AH4 ARsqH4(AH4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return saturate(x);} + AH2 ASatH2(AH2 x){return saturate(x);} + AH3 ASatH3(AH3 x){return saturate(x);} + AH4 ASatH4(AH4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #ifdef A_HLSL_6_2 + #define AD1 float64_t + #define AD2 float64_t2 + #define AD3 float64_t3 + #define AD4 float64_t4 + #else + #define AD1 double + #define AD2 double2 + #define AD3 double3 + #define AD4 double4 + #endif +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 a){return a-floor(a);} + AD2 AFractD2(AD2 a){return a-floor(a);} + AD3 AFractD3(AD3 a){return a-floor(a);} + AD4 AFractD4(AD4 a){return a-floor(a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return lerp(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return lerp(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return lerp(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return rcp(x);} + AD2 ARcpD2(AD2 x){return rcp(x);} + AD3 ARcpD3(AD3 x){return rcp(x);} + AD4 ARcpD4(AD4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return rsqrt(x);} + AD2 ARsqD2(AD2 x){return rsqrt(x);} + AD3 ARsqD3(AD3 x){return rsqrt(x);} + AD4 ARsqD4(AD4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return saturate(x);} + AD2 ASatD2(AD2 x){return saturate(x);} + AD3 ASatD3(AD3 x){return saturate(x);} + AD4 ASatD4(AD4 x){return saturate(x);} + #endif +//============================================================================================================================== +// HLSL WAVE +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU2 AWaveXorU1(AU2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU3 AWaveXorU1(AU3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU4 AWaveXorU1(AU4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(WaveReadLaneAt(AU1_AH2(v),WaveGetLaneIndex()^x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(WaveReadLaneAt(AU2_AH4(v),WaveGetLaneIndex()^x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(WaveReadLaneAt(AU1_AW2(v),WaveGetLaneIndex()^x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU1(WaveReadLaneAt(AU1_AW4(v),WaveGetLaneIndex()^x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU COMMON +// +// +//============================================================================================================================== +#ifdef A_GPU + // Negative and positive infinity. + #define A_INFP_F AF1_AU1(0x7f800000u) + #define A_INFN_F AF1_AU1(0xff800000u) +//------------------------------------------------------------------------------------------------------------------------------ + // Copy sign from 's' to positive 'd'. + AF1 ACpySgnF1(AF1 d,AF1 s){return AF1_AU1(AU1_AF1(d)|(AU1_AF1(s)&AU1_(0x80000000u)));} + AF2 ACpySgnF2(AF2 d,AF2 s){return AF2_AU2(AU2_AF2(d)|(AU2_AF2(s)&AU2_(0x80000000u)));} + AF3 ACpySgnF3(AF3 d,AF3 s){return AF3_AU3(AU3_AF3(d)|(AU3_AF3(s)&AU3_(0x80000000u)));} + AF4 ACpySgnF4(AF4 d,AF4 s){return AF4_AU4(AU4_AF4(d)|(AU4_AF4(s)&AU4_(0x80000000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Single operation to return (useful to create a mask to use in lerp for branch free logic), + // m=NaN := 0 + // m>=0 := 0 + // m<0 := 1 + // Uses the following useful floating point logic, + // saturate(+a*(-INF)==-INF) := 0 + // saturate( 0*(-INF)== NaN) := 0 + // saturate(-a*(-INF)==+INF) := 1 + AF1 ASignedF1(AF1 m){return ASatF1(m*AF1_(A_INFN_F));} + AF2 ASignedF2(AF2 m){return ASatF2(m*AF2_(A_INFN_F));} + AF3 ASignedF3(AF3 m){return ASatF3(m*AF3_(A_INFN_F));} + AF4 ASignedF4(AF4 m){return ASatF4(m*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AGtZeroF1(AF1 m){return ASatF1(m*AF1_(A_INFP_F));} + AF2 AGtZeroF2(AF2 m){return ASatF2(m*AF2_(A_INFP_F));} + AF3 AGtZeroF3(AF3 m){return ASatF3(m*AF3_(A_INFP_F));} + AF4 AGtZeroF4(AF4 m){return ASatF4(m*AF4_(A_INFP_F));} +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define A_INFP_H AH1_AW1((uint16_t)0x7c00u) + #define A_INFN_H AH1_AW1((uint16_t)0xfc00u) + #else + #define A_INFP_H AH1_AW1(0x7c00u) + #define A_INFN_H AH1_AW1(0xfc00u) + #endif + +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ACpySgnH1(AH1 d,AH1 s){return AH1_AW1(AW1_AH1(d)|(AW1_AH1(s)&AW1_(0x8000u)));} + AH2 ACpySgnH2(AH2 d,AH2 s){return AH2_AW2(AW2_AH2(d)|(AW2_AH2(s)&AW2_(0x8000u)));} + AH3 ACpySgnH3(AH3 d,AH3 s){return AH3_AW3(AW3_AH3(d)|(AW3_AH3(s)&AW3_(0x8000u)));} + AH4 ACpySgnH4(AH4 d,AH4 s){return AH4_AW4(AW4_AH4(d)|(AW4_AH4(s)&AW4_(0x8000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASignedH1(AH1 m){return ASatH1(m*AH1_(A_INFN_H));} + AH2 ASignedH2(AH2 m){return ASatH2(m*AH2_(A_INFN_H));} + AH3 ASignedH3(AH3 m){return ASatH3(m*AH3_(A_INFN_H));} + AH4 ASignedH4(AH4 m){return ASatH4(m*AH4_(A_INFN_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AGtZeroH1(AH1 m){return ASatH1(m*AH1_(A_INFP_H));} + AH2 AGtZeroH2(AH2 m){return ASatH2(m*AH2_(A_INFP_H));} + AH3 AGtZeroH3(AH3 m){return ASatH3(m*AH3_(A_INFP_H));} + AH4 AGtZeroH4(AH4 m){return ASatH4(m*AH4_(A_INFP_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [FIS] FLOAT INTEGER SORTABLE +//------------------------------------------------------------------------------------------------------------------------------ +// Float to integer sortable. +// - If sign bit=0, flip the sign bit (positives). +// - If sign bit=1, flip all bits (negatives). +// Integer sortable to float. +// - If sign bit=1, flip the sign bit (positives). +// - If sign bit=0, flip all bits (negatives). +// Has nice side effects. +// - Larger integers are more positive values. +// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). +// Burns 3 ops for conversion {shift,or,xor}. +//============================================================================================================================== + AU1 AFisToU1(AU1 x){return x^(( AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} + AU1 AFisFromU1(AU1 x){return x^((~AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + // Just adjust high 16-bit value (useful when upper part of 32-bit word is a 16-bit float value). + AU1 AFisToHiU1(AU1 x){return x^(( AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} + AU1 AFisFromHiU1(AU1 x){return x^((~AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AW1 AFisToW1(AW1 x){return x^(( AShrSW1(x,AW1_(15)))|AW1_(0x8000));} + AW1 AFisFromW1(AW1 x){return x^((~AShrSW1(x,AW1_(15)))|AW1_(0x8000));} +//------------------------------------------------------------------------------------------------------------------------------ + AW2 AFisToW2(AW2 x){return x^(( AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + AW2 AFisFromW2(AW2 x){return x^((~AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [PERM] V_PERM_B32 +//------------------------------------------------------------------------------------------------------------------------------ +// Support for V_PERM_B32 started in the 3rd generation of GCN. +//------------------------------------------------------------------------------------------------------------------------------ +// yyyyxxxx - The 'i' input. +// 76543210 +// ======== +// HGFEDCBA - Naming on permutation. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure compiler optimizes this. +//============================================================================================================================== + #ifdef A_HALF + AU1 APerm0E0A(AU2 i){return((i.x )&0xffu)|((i.y<<16)&0xff0000u);} + AU1 APerm0F0B(AU2 i){return((i.x>> 8)&0xffu)|((i.y<< 8)&0xff0000u);} + AU1 APerm0G0C(AU2 i){return((i.x>>16)&0xffu)|((i.y )&0xff0000u);} + AU1 APerm0H0D(AU2 i){return((i.x>>24)&0xffu)|((i.y>> 8)&0xff0000u);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermHGFA(AU2 i){return((i.x )&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGFC(AU2 i){return((i.x>>16)&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGAE(AU2 i){return((i.x<< 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHGCE(AU2 i){return((i.x>> 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHAFE(AU2 i){return((i.x<<16)&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermHCFE(AU2 i){return((i.x )&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermAGFE(AU2 i){return((i.x<<24)&0xff000000u)|(i.y&0x00ffffffu);} + AU1 APermCGFE(AU2 i){return((i.x<< 8)&0xff000000u)|(i.y&0x00ffffffu);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermGCEA(AU2 i){return((i.x)&0x00ff00ffu)|((i.y<<8)&0xff00ff00u);} + AU1 APermGECA(AU2 i){return(((i.x)&0xffu)|((i.x>>8)&0xff00u)|((i.y<<16)&0xff0000u)|((i.y<<8)&0xff000000u));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BUC] BYTE UNSIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Designed to use the optimal conversion, enables the scaling to possibly be factored into other computation. +// Works on a range of {0 to A_BUC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// OPCODE NOTES +// ============ +// GCN does not do UNORM or SNORM for bytes in opcodes. +// - V_CVT_F32_UBYTE{0,1,2,3} - Unsigned byte to float. +// - V_CVT_PKACC_U8_F32 - Float to unsigned byte (does bit-field insert into 32-bit integer). +// V_PERM_B32 does byte packing with ability to zero fill bytes as well. +// - Can pull out byte values from two sources, and zero fill upper 8-bits of packed hi and lo. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U1() - Designed for V_CVT_F32_UBYTE* and V_CVT_PKACCUM_U8_F32 ops. +// ==== ===== +// 0 : 0 +// 1 : 1 +// ... +// 255 : 255 +// : 256 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : 0 +// 1 : 1/512 +// 2 : 1/256 +// ... +// 64 : 1/8 +// 128 : 1/4 +// 255 : 255/512 +// : 1/2 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMAL IMPLEMENTATIONS ON AMD ARCHITECTURES +// ============================================ +// r=ABuc0FromU1(i) +// V_CVT_F32_UBYTE0 r,i +// -------------------------------------------- +// r=ABuc0ToU1(d,i) +// V_CVT_PKACCUM_U8_F32 r,i,0,d +// -------------------------------------------- +// d=ABuc0FromU2(i) +// Where 'k0' is an SGPR with 0x0E0A +// Where 'k1' is an SGPR with {32768.0} packed into the lower 16-bits +// V_PERM_B32 d,i.x,i.y,k0 +// V_PK_FMA_F16 d,d,k1.x,0 +// -------------------------------------------- +// r=ABuc0ToU2(d,i) +// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +// Where 'k1' is an SGPR with 0x???? +// Where 'k2' is an SGPR with 0x???? +// V_PK_FMA_F16 i,i,k0.x,0 +// V_PERM_B32 r.x,i,i,k1 +// V_PERM_B32 r.y,i,i,k2 +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BUC_32 (255.0) + #define A_BUC_16 (255.0/512.0) +//============================================================================================================================== + #if 1 + // Designed to be one V_CVT_PKACCUM_U8_F32. + // The extra min is required to pattern match to V_CVT_PKACCUM_U8_F32. + AU1 ABuc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i),255u) )&(0x000000ffu));} + AU1 ABuc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i),255u)<< 8)&(0x0000ff00u));} + AU1 ABuc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i),255u)<<16)&(0x00ff0000u));} + AU1 ABuc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed to be one V_CVT_F32_UBYTE*. + AF1 ABuc0FromU1(AU1 i){return AF1((i )&255u);} + AF1 ABuc1FromU1(AU1 i){return AF1((i>> 8)&255u);} + AF1 ABuc2FromU1(AU1 i){return AF1((i>>16)&255u);} + AF1 ABuc3FromU1(AU1 i){return AF1((i>>24)&255u);} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABuc01ToW2(AH2 x,AH2 y){x*=AH2_(1.0/32768.0);y*=AH2_(1.0/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 3 ops to do SOA to AOS and conversion. + AU2 ABuc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABuc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABuc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABuc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 2 ops to do both AOS to SOA, and conversion. + AH2 ABuc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0);} + AH2 ABuc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0);} + AH2 ABuc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0);} + AH2 ABuc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BSC] BYTE SIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Similar to [BUC]. +// Works on a range of {-/+ A_BSC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// ENCODING (without zero-based encoding) +// ======== +// 0 = unused (can be used to mean something else) +// 1 = lowest value +// 128 = exact zero center (zero based encoding +// 255 = highest value +//------------------------------------------------------------------------------------------------------------------------------ +// Zero-based [Zb] flips the MSB bit of the byte (making 128 "exact zero" actually zero). +// This is useful if there is a desire for cleared values to decode as zero. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABsc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : -127/512 (unused) +// 1 : -126/512 +// 2 : -125/512 +// ... +// 128 : 0 +// ... +// 255 : 127/512 +// : 1/4 (just outside the encoding range) +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BSC_32 (127.0) + #define A_BSC_16 (127.0/512.0) +//============================================================================================================================== + #if 1 + AU1 ABsc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i+128.0),255u) )&(0x000000ffu));} + AU1 ABsc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i+128.0),255u)<< 8)&(0x0000ff00u));} + AU1 ABsc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i+128.0),255u)<<16)&(0x00ff0000u));} + AU1 ABsc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i+128.0),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABsc0ToZbU1(AU1 d,AF1 i){return ((d&0xffffff00u)|((min(AU1(trunc(i)+128.0),255u) )&(0x000000ffu)))^0x00000080u;} + AU1 ABsc1ToZbU1(AU1 d,AF1 i){return ((d&0xffff00ffu)|((min(AU1(trunc(i)+128.0),255u)<< 8)&(0x0000ff00u)))^0x00008000u;} + AU1 ABsc2ToZbU1(AU1 d,AF1 i){return ((d&0xff00ffffu)|((min(AU1(trunc(i)+128.0),255u)<<16)&(0x00ff0000u)))^0x00800000u;} + AU1 ABsc3ToZbU1(AU1 d,AF1 i){return ((d&0x00ffffffu)|((min(AU1(trunc(i)+128.0),255u)<<24)&(0xff000000u)))^0x80000000u;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromU1(AU1 i){return AF1((i )&255u)-128.0;} + AF1 ABsc1FromU1(AU1 i){return AF1((i>> 8)&255u)-128.0;} + AF1 ABsc2FromU1(AU1 i){return AF1((i>>16)&255u)-128.0;} + AF1 ABsc3FromU1(AU1 i){return AF1((i>>24)&255u)-128.0;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromZbU1(AU1 i){return AF1(((i )&255u)^0x80u)-128.0;} + AF1 ABsc1FromZbU1(AU1 i){return AF1(((i>> 8)&255u)^0x80u)-128.0;} + AF1 ABsc2FromZbU1(AU1 i){return AF1(((i>>16)&255u)^0x80u)-128.0;} + AF1 ABsc3FromZbU1(AU1 i){return AF1(((i>>24)&255u)^0x80u)-128.0;} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABsc01ToW2(AH2 x,AH2 y){x=x*AH2_(1.0/32768.0)+AH2_(0.25/32768.0);y=y*AH2_(1.0/32768.0)+AH2_(0.25/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0)-AH2_(0.25);} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These support only positive inputs. +// Did not see value yet in specialization for range. +// Using quick testing, ended up mostly getting the same "best" approximation for various ranges. +// With hardware that can co-execute transcendentals, the value in approximations could be less than expected. +// However from a latency perspective, if execution of a transcendental is 4 clk, with no packed support, -> 8 clk total. +// And co-execution would require a compiler interleaving a lot of independent work for packed usage. +//------------------------------------------------------------------------------------------------------------------------------ +// The one Newton Raphson iteration form of rsq() was skipped (requires 6 ops total). +// Same with sqrt(), as this could be x*rsq() (7 ops). +//============================================================================================================================== + #ifdef A_HALF + // Minimize squared error across full positive range, 2 ops. + // The 0x1de2 based approximation maps {0 to 1} input maps to < 1 output. + AH1 APrxLoSqrtH1(AH1 a){return AH1_AW1((AW1_AH1(a)>>AW1_(1))+AW1_(0x1de2));} + AH2 APrxLoSqrtH2(AH2 a){return AH2_AW2((AW2_AH2(a)>>AW2_(1))+AW2_(0x1de2));} + AH3 APrxLoSqrtH3(AH3 a){return AH3_AW3((AW3_AH3(a)>>AW3_(1))+AW3_(0x1de2));} + AH4 APrxLoSqrtH4(AH4 a){return AH4_AW4((AW4_AH4(a)>>AW4_(1))+AW4_(0x1de2));} +//------------------------------------------------------------------------------------------------------------------------------ + // Lower precision estimation, 1 op. + // Minimize squared error across {smallest normal to 16384.0}. + AH1 APrxLoRcpH1(AH1 a){return AH1_AW1(AW1_(0x7784)-AW1_AH1(a));} + AH2 APrxLoRcpH2(AH2 a){return AH2_AW2(AW2_(0x7784)-AW2_AH2(a));} + AH3 APrxLoRcpH3(AH3 a){return AH3_AW3(AW3_(0x7784)-AW3_AH3(a));} + AH4 APrxLoRcpH4(AH4 a){return AH4_AW4(AW4_(0x7784)-AW4_AH4(a));} +//------------------------------------------------------------------------------------------------------------------------------ + // Medium precision estimation, one Newton Raphson iteration, 3 ops. + AH1 APrxMedRcpH1(AH1 a){AH1 b=AH1_AW1(AW1_(0x778d)-AW1_AH1(a));return b*(-b*a+AH1_(2.0));} + AH2 APrxMedRcpH2(AH2 a){AH2 b=AH2_AW2(AW2_(0x778d)-AW2_AH2(a));return b*(-b*a+AH2_(2.0));} + AH3 APrxMedRcpH3(AH3 a){AH3 b=AH3_AW3(AW3_(0x778d)-AW3_AH3(a));return b*(-b*a+AH3_(2.0));} + AH4 APrxMedRcpH4(AH4 a){AH4 b=AH4_AW4(AW4_(0x778d)-AW4_AH4(a));return b*(-b*a+AH4_(2.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // Minimize squared error across {smallest normal to 16384.0}, 2 ops. + AH1 APrxLoRsqH1(AH1 a){return AH1_AW1(AW1_(0x59a3)-(AW1_AH1(a)>>AW1_(1)));} + AH2 APrxLoRsqH2(AH2 a){return AH2_AW2(AW2_(0x59a3)-(AW2_AH2(a)>>AW2_(1)));} + AH3 APrxLoRsqH3(AH3 a){return AH3_AW3(AW3_(0x59a3)-(AW3_AH3(a)>>AW3_(1)));} + AH4 APrxLoRsqH4(AH4 a){return AH4_AW4(AW4_(0x59a3)-(AW4_AH4(a)>>AW4_(1)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// FLOAT APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// Michal Drobot has an excellent presentation on these: "Low Level Optimizations For GCN", +// - Idea dates back to SGI, then to Quake 3, etc. +// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +// - sqrt(x)=rsqrt(x)*x +// - rcp(x)=rsqrt(x)*rsqrt(x) for positive x +// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +//------------------------------------------------------------------------------------------------------------------------------ +// These below are from perhaps less complete searching for optimal. +// Used FP16 normal range for testing with +4096 32-bit step size for sampling error. +// So these match up well with the half approximations. +//============================================================================================================================== + AF1 APrxLoSqrtF1(AF1 a){return AF1_AU1((AU1_AF1(a)>>AU1_(1))+AU1_(0x1fbc4639));} + AF1 APrxLoRcpF1(AF1 a){return AF1_AU1(AU1_(0x7ef07ebb)-AU1_AF1(a));} + AF1 APrxMedRcpF1(AF1 a){AF1 b=AF1_AU1(AU1_(0x7ef19fff)-AU1_AF1(a));return b*(-b*a+AF1_(2.0));} + AF1 APrxLoRsqF1(AF1 a){return AF1_AU1(AU1_(0x5f347d74)-(AU1_AF1(a)>>AU1_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxLoSqrtF2(AF2 a){return AF2_AU2((AU2_AF2(a)>>AU2_(1))+AU2_(0x1fbc4639));} + AF2 APrxLoRcpF2(AF2 a){return AF2_AU2(AU2_(0x7ef07ebb)-AU2_AF2(a));} + AF2 APrxMedRcpF2(AF2 a){AF2 b=AF2_AU2(AU2_(0x7ef19fff)-AU2_AF2(a));return b*(-b*a+AF2_(2.0));} + AF2 APrxLoRsqF2(AF2 a){return AF2_AU2(AU2_(0x5f347d74)-(AU2_AF2(a)>>AU2_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxLoSqrtF3(AF3 a){return AF3_AU3((AU3_AF3(a)>>AU3_(1))+AU3_(0x1fbc4639));} + AF3 APrxLoRcpF3(AF3 a){return AF3_AU3(AU3_(0x7ef07ebb)-AU3_AF3(a));} + AF3 APrxMedRcpF3(AF3 a){AF3 b=AF3_AU3(AU3_(0x7ef19fff)-AU3_AF3(a));return b*(-b*a+AF3_(2.0));} + AF3 APrxLoRsqF3(AF3 a){return AF3_AU3(AU3_(0x5f347d74)-(AU3_AF3(a)>>AU3_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxLoSqrtF4(AF4 a){return AF4_AU4((AU4_AF4(a)>>AU4_(1))+AU4_(0x1fbc4639));} + AF4 APrxLoRcpF4(AF4 a){return AF4_AU4(AU4_(0x7ef07ebb)-AU4_AF4(a));} + AF4 APrxMedRcpF4(AF4 a){AF4 b=AF4_AU4(AU4_(0x7ef19fff)-AU4_AF4(a));return b*(-b*a+AF4_(2.0));} + AF4 APrxLoRsqF4(AF4 a){return AF4_AU4(AU4_(0x5f347d74)-(AU4_AF4(a)>>AU4_(1)));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PQ APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// PQ is very close to x^(1/8). The functions below Use the fast float approximation method to do +// PQ<~>Gamma2 (4th power and fast 4th root) and PQ<~>Linear (8th power and fast 8th root). Maximum error is ~0.2%. +//============================================================================================================================== +// Helpers + AF1 Quart(AF1 a) { a = a * a; return a * a;} + AF1 Oct(AF1 a) { a = a * a; a = a * a; return a * a; } + AF2 Quart(AF2 a) { a = a * a; return a * a; } + AF2 Oct(AF2 a) { a = a * a; a = a * a; return a * a; } + AF3 Quart(AF3 a) { a = a * a; return a * a; } + AF3 Oct(AF3 a) { a = a * a; a = a * a; return a * a; } + AF4 Quart(AF4 a) { a = a * a; return a * a; } + AF4 Oct(AF4 a) { a = a * a; a = a * a; return a * a; } + //------------------------------------------------------------------------------------------------------------------------------ + AF1 APrxPQToGamma2(AF1 a) { return Quart(a); } + AF1 APrxPQToLinear(AF1 a) { return Oct(a); } + AF1 APrxLoGamma2ToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); } + AF1 APrxMedGamma2ToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); AF1 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF1 APrxHighGamma2ToPQ(AF1 a) { return sqrt(sqrt(a)); } + AF1 APrxLoLinearToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); } + AF1 APrxMedLinearToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); AF1 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF1 APrxHighLinearToPQ(AF1 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxPQToGamma2(AF2 a) { return Quart(a); } + AF2 APrxPQToLinear(AF2 a) { return Oct(a); } + AF2 APrxLoGamma2ToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); } + AF2 APrxMedGamma2ToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); AF2 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF2 APrxHighGamma2ToPQ(AF2 a) { return sqrt(sqrt(a)); } + AF2 APrxLoLinearToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); } + AF2 APrxMedLinearToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); AF2 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF2 APrxHighLinearToPQ(AF2 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxPQToGamma2(AF3 a) { return Quart(a); } + AF3 APrxPQToLinear(AF3 a) { return Oct(a); } + AF3 APrxLoGamma2ToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); } + AF3 APrxMedGamma2ToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); AF3 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF3 APrxHighGamma2ToPQ(AF3 a) { return sqrt(sqrt(a)); } + AF3 APrxLoLinearToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); } + AF3 APrxMedLinearToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); AF3 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF3 APrxHighLinearToPQ(AF3 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxPQToGamma2(AF4 a) { return Quart(a); } + AF4 APrxPQToLinear(AF4 a) { return Oct(a); } + AF4 APrxLoGamma2ToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); } + AF4 APrxMedGamma2ToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); AF4 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF4 APrxHighGamma2ToPQ(AF4 a) { return sqrt(sqrt(a)); } + AF4 APrxLoLinearToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); } + AF4 APrxMedLinearToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); AF4 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF4 APrxHighLinearToPQ(AF4 a) { return sqrt(sqrt(sqrt(a))); } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PARABOLIC SIN & COS +//------------------------------------------------------------------------------------------------------------------------------ +// Approximate answers to transcendental questions. +//------------------------------------------------------------------------------------------------------------------------------ +//============================================================================================================================== + #if 1 + // Valid input range is {-1 to 1} representing {0 to 2 pi}. + // Output range is {-1/4 to 1/4} representing {-1 to 1}. + AF1 APSinF1(AF1 x){return x*abs(x)-x;} // MAD. + AF2 APSinF2(AF2 x){return x*abs(x)-x;} + AF1 APCosF1(AF1 x){x=AFractF1(x*AF1_(0.5)+AF1_(0.75));x=x*AF1_(2.0)-AF1_(1.0);return APSinF1(x);} // 3x MAD, FRACT + AF2 APCosF2(AF2 x){x=AFractF2(x*AF2_(0.5)+AF2_(0.75));x=x*AF2_(2.0)-AF2_(1.0);return APSinF2(x);} + AF2 APSinCosF1(AF1 x){AF1 y=AFractF1(x*AF1_(0.5)+AF1_(0.75));y=y*AF1_(2.0)-AF1_(1.0);return APSinF2(AF2(x,y));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + // For a packed {sin,cos} pair, + // - Native takes 16 clocks and 4 issue slots (no packed transcendentals). + // - Parabolic takes 8 clocks and 8 issue slots (only fract is non-packed). + AH1 APSinH1(AH1 x){return x*abs(x)-x;} + AH2 APSinH2(AH2 x){return x*abs(x)-x;} // AND,FMA + AH1 APCosH1(AH1 x){x=AFractH1(x*AH1_(0.5)+AH1_(0.75));x=x*AH1_(2.0)-AH1_(1.0);return APSinH1(x);} + AH2 APCosH2(AH2 x){x=AFractH2(x*AH2_(0.5)+AH2_(0.75));x=x*AH2_(2.0)-AH2_(1.0);return APSinH2(x);} // 3x FMA, 2xFRACT, AND + AH2 APSinCosH1(AH1 x){AH1 y=AFractH1(x*AH1_(0.5)+AH1_(0.75));y=y*AH1_(2.0)-AH1_(1.0);return APSinH2(AH2(x,y));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [ZOL] ZERO ONE LOGIC +//------------------------------------------------------------------------------------------------------------------------------ +// Conditional free logic designed for easy 16-bit packing, and backwards porting to 32-bit. +//------------------------------------------------------------------------------------------------------------------------------ +// 0 := false +// 1 := true +//------------------------------------------------------------------------------------------------------------------------------ +// AndNot(x,y) -> !(x&y) .... One op. +// AndOr(x,y,z) -> (x&y)|z ... One op. +// GtZero(x) -> x>0.0 ..... One op. +// Sel(x,y,z) -> x?y:z ..... Two ops, has no precision loss. +// Signed(x) -> x<0.0 ..... One op. +// ZeroPass(x,y) -> x?0:y ..... Two ops, 'y' is a pass through safe for aliasing as integer. +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMIZATION NOTES +// ================== +// - On Vega to use 2 constants in a packed op, pass in as one AW2 or one AH2 'k.xy' and use as 'k.xx' and 'k.yy'. +// For example 'a.xy*k.xx+k.yy'. +//============================================================================================================================== + #if 1 + AU1 AZolAndU1(AU1 x,AU1 y){return min(x,y);} + AU2 AZolAndU2(AU2 x,AU2 y){return min(x,y);} + AU3 AZolAndU3(AU3 x,AU3 y){return min(x,y);} + AU4 AZolAndU4(AU4 x,AU4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolNotU1(AU1 x){return x^AU1_(1);} + AU2 AZolNotU2(AU2 x){return x^AU2_(1);} + AU3 AZolNotU3(AU3 x){return x^AU3_(1);} + AU4 AZolNotU4(AU4 x){return x^AU4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolOrU1(AU1 x,AU1 y){return max(x,y);} + AU2 AZolOrU2(AU2 x,AU2 y){return max(x,y);} + AU3 AZolOrU3(AU3 x,AU3 y){return max(x,y);} + AU4 AZolOrU4(AU4 x,AU4 y){return max(x,y);} +//============================================================================================================================== + AU1 AZolF1ToU1(AF1 x){return AU1(x);} + AU2 AZolF2ToU2(AF2 x){return AU2(x);} + AU3 AZolF3ToU3(AF3 x){return AU3(x);} + AU4 AZolF4ToU4(AF4 x){return AU4(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // 2 ops, denormals don't work in 32-bit on PC (and if they are enabled, OMOD is disabled). + AU1 AZolNotF1ToU1(AF1 x){return AU1(AF1_(1.0)-x);} + AU2 AZolNotF2ToU2(AF2 x){return AU2(AF2_(1.0)-x);} + AU3 AZolNotF3ToU3(AF3 x){return AU3(AF3_(1.0)-x);} + AU4 AZolNotF4ToU4(AF4 x){return AU4(AF4_(1.0)-x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolU1ToF1(AU1 x){return AF1(x);} + AF2 AZolU2ToF2(AU2 x){return AF2(x);} + AF3 AZolU3ToF3(AU3 x){return AF3(x);} + AF4 AZolU4ToF4(AU4 x){return AF4(x);} +//============================================================================================================================== + AF1 AZolAndF1(AF1 x,AF1 y){return min(x,y);} + AF2 AZolAndF2(AF2 x,AF2 y){return min(x,y);} + AF3 AZolAndF3(AF3 x,AF3 y){return min(x,y);} + AF4 AZolAndF4(AF4 x,AF4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASolAndNotF1(AF1 x,AF1 y){return (-x)*y+AF1_(1.0);} + AF2 ASolAndNotF2(AF2 x,AF2 y){return (-x)*y+AF2_(1.0);} + AF3 ASolAndNotF3(AF3 x,AF3 y){return (-x)*y+AF3_(1.0);} + AF4 ASolAndNotF4(AF4 x,AF4 y){return (-x)*y+AF4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolAndOrF1(AF1 x,AF1 y,AF1 z){return ASatF1(x*y+z);} + AF2 AZolAndOrF2(AF2 x,AF2 y,AF2 z){return ASatF2(x*y+z);} + AF3 AZolAndOrF3(AF3 x,AF3 y,AF3 z){return ASatF3(x*y+z);} + AF4 AZolAndOrF4(AF4 x,AF4 y,AF4 z){return ASatF4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolGtZeroF1(AF1 x){return ASatF1(x*AF1_(A_INFP_F));} + AF2 AZolGtZeroF2(AF2 x){return ASatF2(x*AF2_(A_INFP_F));} + AF3 AZolGtZeroF3(AF3 x){return ASatF3(x*AF3_(A_INFP_F));} + AF4 AZolGtZeroF4(AF4 x){return ASatF4(x*AF4_(A_INFP_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolNotF1(AF1 x){return AF1_(1.0)-x;} + AF2 AZolNotF2(AF2 x){return AF2_(1.0)-x;} + AF3 AZolNotF3(AF3 x){return AF3_(1.0)-x;} + AF4 AZolNotF4(AF4 x){return AF4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolOrF1(AF1 x,AF1 y){return max(x,y);} + AF2 AZolOrF2(AF2 x,AF2 y){return max(x,y);} + AF3 AZolOrF3(AF3 x,AF3 y){return max(x,y);} + AF4 AZolOrF4(AF4 x,AF4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSelF1(AF1 x,AF1 y,AF1 z){AF1 r=(-x)*z+z;return x*y+r;} + AF2 AZolSelF2(AF2 x,AF2 y,AF2 z){AF2 r=(-x)*z+z;return x*y+r;} + AF3 AZolSelF3(AF3 x,AF3 y,AF3 z){AF3 r=(-x)*z+z;return x*y+r;} + AF4 AZolSelF4(AF4 x,AF4 y,AF4 z){AF4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSignedF1(AF1 x){return ASatF1(x*AF1_(A_INFN_F));} + AF2 AZolSignedF2(AF2 x){return ASatF2(x*AF2_(A_INFN_F));} + AF3 AZolSignedF3(AF3 x){return ASatF3(x*AF3_(A_INFN_F));} + AF4 AZolSignedF4(AF4 x){return ASatF4(x*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolZeroPassF1(AF1 x,AF1 y){return AF1_AU1((AU1_AF1(x)!=AU1_(0))?AU1_(0):AU1_AF1(y));} + AF2 AZolZeroPassF2(AF2 x,AF2 y){return AF2_AU2((AU2_AF2(x)!=AU2_(0))?AU2_(0):AU2_AF2(y));} + AF3 AZolZeroPassF3(AF3 x,AF3 y){return AF3_AU3((AU3_AF3(x)!=AU3_(0))?AU3_(0):AU3_AF3(y));} + AF4 AZolZeroPassF4(AF4 x,AF4 y){return AF4_AU4((AU4_AF4(x)!=AU4_(0))?AU4_(0):AU4_AF4(y));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AW1 AZolAndW1(AW1 x,AW1 y){return min(x,y);} + AW2 AZolAndW2(AW2 x,AW2 y){return min(x,y);} + AW3 AZolAndW3(AW3 x,AW3 y){return min(x,y);} + AW4 AZolAndW4(AW4 x,AW4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolNotW1(AW1 x){return x^AW1_(1);} + AW2 AZolNotW2(AW2 x){return x^AW2_(1);} + AW3 AZolNotW3(AW3 x){return x^AW3_(1);} + AW4 AZolNotW4(AW4 x){return x^AW4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolOrW1(AW1 x,AW1 y){return max(x,y);} + AW2 AZolOrW2(AW2 x,AW2 y){return max(x,y);} + AW3 AZolOrW3(AW3 x,AW3 y){return max(x,y);} + AW4 AZolOrW4(AW4 x,AW4 y){return max(x,y);} +//============================================================================================================================== + // Uses denormal trick. + AW1 AZolH1ToW1(AH1 x){return AW1_AH1(x*AH1_AW1(AW1_(1)));} + AW2 AZolH2ToW2(AH2 x){return AW2_AH2(x*AH2_AW2(AW2_(1)));} + AW3 AZolH3ToW3(AH3 x){return AW3_AH3(x*AH3_AW3(AW3_(1)));} + AW4 AZolH4ToW4(AH4 x){return AW4_AH4(x*AH4_AW4(AW4_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + // AMD arch lacks a packed conversion opcode. + AH1 AZolW1ToH1(AW1 x){return AH1_AW1(x*AW1_AH1(AH1_(1.0)));} + AH2 AZolW2ToH2(AW2 x){return AH2_AW2(x*AW2_AH2(AH2_(1.0)));} + AH3 AZolW1ToH3(AW3 x){return AH3_AW3(x*AW3_AH3(AH3_(1.0)));} + AH4 AZolW2ToH4(AW4 x){return AH4_AW4(x*AW4_AH4(AH4_(1.0)));} +//============================================================================================================================== + AH1 AZolAndH1(AH1 x,AH1 y){return min(x,y);} + AH2 AZolAndH2(AH2 x,AH2 y){return min(x,y);} + AH3 AZolAndH3(AH3 x,AH3 y){return min(x,y);} + AH4 AZolAndH4(AH4 x,AH4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASolAndNotH1(AH1 x,AH1 y){return (-x)*y+AH1_(1.0);} + AH2 ASolAndNotH2(AH2 x,AH2 y){return (-x)*y+AH2_(1.0);} + AH3 ASolAndNotH3(AH3 x,AH3 y){return (-x)*y+AH3_(1.0);} + AH4 ASolAndNotH4(AH4 x,AH4 y){return (-x)*y+AH4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolAndOrH1(AH1 x,AH1 y,AH1 z){return ASatH1(x*y+z);} + AH2 AZolAndOrH2(AH2 x,AH2 y,AH2 z){return ASatH2(x*y+z);} + AH3 AZolAndOrH3(AH3 x,AH3 y,AH3 z){return ASatH3(x*y+z);} + AH4 AZolAndOrH4(AH4 x,AH4 y,AH4 z){return ASatH4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolGtZeroH1(AH1 x){return ASatH1(x*AH1_(A_INFP_H));} + AH2 AZolGtZeroH2(AH2 x){return ASatH2(x*AH2_(A_INFP_H));} + AH3 AZolGtZeroH3(AH3 x){return ASatH3(x*AH3_(A_INFP_H));} + AH4 AZolGtZeroH4(AH4 x){return ASatH4(x*AH4_(A_INFP_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolNotH1(AH1 x){return AH1_(1.0)-x;} + AH2 AZolNotH2(AH2 x){return AH2_(1.0)-x;} + AH3 AZolNotH3(AH3 x){return AH3_(1.0)-x;} + AH4 AZolNotH4(AH4 x){return AH4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolOrH1(AH1 x,AH1 y){return max(x,y);} + AH2 AZolOrH2(AH2 x,AH2 y){return max(x,y);} + AH3 AZolOrH3(AH3 x,AH3 y){return max(x,y);} + AH4 AZolOrH4(AH4 x,AH4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSelH1(AH1 x,AH1 y,AH1 z){AH1 r=(-x)*z+z;return x*y+r;} + AH2 AZolSelH2(AH2 x,AH2 y,AH2 z){AH2 r=(-x)*z+z;return x*y+r;} + AH3 AZolSelH3(AH3 x,AH3 y,AH3 z){AH3 r=(-x)*z+z;return x*y+r;} + AH4 AZolSelH4(AH4 x,AH4 y,AH4 z){AH4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSignedH1(AH1 x){return ASatH1(x*AH1_(A_INFN_H));} + AH2 AZolSignedH2(AH2 x){return ASatH2(x*AH2_(A_INFN_H));} + AH3 AZolSignedH3(AH3 x){return ASatH3(x*AH3_(A_INFN_H));} + AH4 AZolSignedH4(AH4 x){return ASatH4(x*AH4_(A_INFN_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COLOR CONVERSIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These are all linear to/from some other space (where 'linear' has been shortened out of the function name). +// So 'ToGamma' is 'LinearToGamma', and 'FromGamma' is 'LinearFromGamma'. +// These are branch free implementations. +// The AToSrgbF1() function is useful for stores for compute shaders for GPUs without hardware linear->sRGB store conversion. +//------------------------------------------------------------------------------------------------------------------------------ +// TRANSFER FUNCTIONS +// ================== +// 709 ..... Rec709 used for some HDTVs +// Gamma ... Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native +// Pq ...... PQ native for HDR10 +// Srgb .... The sRGB output, typical of PC displays, useful for 10-bit output, or storing to 8-bit UNORM without SRGB type +// Two ..... Gamma 2.0, fastest conversion (useful for intermediate pass approximations) +// Three ... Gamma 3.0, less fast, but good for HDR. +//------------------------------------------------------------------------------------------------------------------------------ +// KEEPING TO SPEC +// =============== +// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +// Also there is a slight step in the transition regions. +// Precision of the coefficients in the spec being the likely cause. +// Main usage case of the sRGB code is to do the linear->sRGB converstion in a compute shader before store. +// This is to work around lack of hardware (typically only ROP does the conversion for free). +// To "correct" the linear segment, would be to introduce error, because hardware decode of sRGB->linear is fixed (and free). +// So this header keeps with the spec. +// For linear->sRGB transforms, the linear segment in some respects reduces error, because rounding in that region is linear. +// Rounding in the curved region in hardware (and fast software code) introduces error due to rounding in non-linear. +//------------------------------------------------------------------------------------------------------------------------------ +// FOR PQ +// ====== +// Both input and output is {0.0-1.0}, and where output 1.0 represents 10000.0 cd/m^2. +// All constants are only specified to FP32 precision. +// External PQ source reference, +// - https://github.com/ampas/aces-dev/blob/master/transforms/ctl/utilities/ACESlib.Utilities_Color.a1.0.1.ctl +//------------------------------------------------------------------------------------------------------------------------------ +// PACKED VERSIONS +// =============== +// These are the A*H2() functions. +// There is no PQ functions as FP16 seemed to not have enough precision for the conversion. +// The remaining functions are "good enough" for 8-bit, and maybe 10-bit if not concerned about a few 1-bit errors. +// Precision is lowest in the 709 conversion, higher in sRGB, higher still in Two and Gamma (when using 2.2 at least). +//------------------------------------------------------------------------------------------------------------------------------ +// NOTES +// ===== +// Could be faster for PQ conversions to be in ALU or a texture lookup depending on usage case. +//============================================================================================================================== + #if 1 + AF1 ATo709F1(AF1 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 ATo709F2(AF2 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 ATo709F3(AF3 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + // Note 'rcpX' is '1/x', where the 'x' is what would be used in AFromGamma(). + AF1 AToGammaF1(AF1 c,AF1 rcpX){return pow(c,AF1_(rcpX));} + AF2 AToGammaF2(AF2 c,AF1 rcpX){return pow(c,AF2_(rcpX));} + AF3 AToGammaF3(AF3 c,AF1 rcpX){return pow(c,AF3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToPqF1(AF1 x){AF1 p=pow(x,AF1_(0.159302)); + return pow((AF1_(0.835938)+AF1_(18.8516)*p)/(AF1_(1.0)+AF1_(18.6875)*p),AF1_(78.8438));} + AF2 AToPqF1(AF2 x){AF2 p=pow(x,AF2_(0.159302)); + return pow((AF2_(0.835938)+AF2_(18.8516)*p)/(AF2_(1.0)+AF2_(18.6875)*p),AF2_(78.8438));} + AF3 AToPqF1(AF3 x){AF3 p=pow(x,AF3_(0.159302)); + return pow((AF3_(0.835938)+AF3_(18.8516)*p)/(AF3_(1.0)+AF3_(18.6875)*p),AF3_(78.8438));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToSrgbF1(AF1 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 AToSrgbF2(AF2 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 AToSrgbF3(AF3 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToTwoF1(AF1 c){return sqrt(c);} + AF2 AToTwoF2(AF2 c){return sqrt(c);} + AF3 AToTwoF3(AF3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToThreeF1(AF1 c){return pow(c,AF1_(1.0/3.0));} + AF2 AToThreeF2(AF2 c){return pow(c,AF2_(1.0/3.0));} + AF3 AToThreeF3(AF3 c){return pow(c,AF3_(1.0/3.0));} + #endif +//============================================================================================================================== + #if 1 + // Unfortunately median won't work here. + AF1 AFrom709F1(AF1 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFrom709F2(AF2 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFrom709F3(AF3 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromGammaF1(AF1 c,AF1 x){return pow(c,AF1_(x));} + AF2 AFromGammaF2(AF2 c,AF1 x){return pow(c,AF2_(x));} + AF3 AFromGammaF3(AF3 c,AF1 x){return pow(c,AF3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromPqF1(AF1 x){AF1 p=pow(x,AF1_(0.0126833)); + return pow(ASatF1(p-AF1_(0.835938))/(AF1_(18.8516)-AF1_(18.6875)*p),AF1_(6.27739));} + AF2 AFromPqF1(AF2 x){AF2 p=pow(x,AF2_(0.0126833)); + return pow(ASatF2(p-AF2_(0.835938))/(AF2_(18.8516)-AF2_(18.6875)*p),AF2_(6.27739));} + AF3 AFromPqF1(AF3 x){AF3 p=pow(x,AF3_(0.0126833)); + return pow(ASatF3(p-AF3_(0.835938))/(AF3_(18.8516)-AF3_(18.6875)*p),AF3_(6.27739));} +//------------------------------------------------------------------------------------------------------------------------------ + // Unfortunately median won't work here. + AF1 AFromSrgbF1(AF1 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFromSrgbF2(AF2 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFromSrgbF3(AF3 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromTwoF1(AF1 c){return c*c;} + AF2 AFromTwoF2(AF2 c){return c*c;} + AF3 AFromTwoF3(AF3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromThreeF1(AF1 c){return c*c*c;} + AF2 AFromThreeF2(AF2 c){return c*c*c;} + AF3 AFromThreeF3(AF3 c){return c*c*c;} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 ATo709H1(AH1 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 ATo709H2(AH2 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 ATo709H3(AH3 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToGammaH1(AH1 c,AH1 rcpX){return pow(c,AH1_(rcpX));} + AH2 AToGammaH2(AH2 c,AH1 rcpX){return pow(c,AH2_(rcpX));} + AH3 AToGammaH3(AH3 c,AH1 rcpX){return pow(c,AH3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToSrgbH1(AH1 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 AToSrgbH2(AH2 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 AToSrgbH3(AH3 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToTwoH1(AH1 c){return sqrt(c);} + AH2 AToTwoH2(AH2 c){return sqrt(c);} + AH3 AToTwoH3(AH3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToThreeF1(AH1 c){return pow(c,AH1_(1.0/3.0));} + AH2 AToThreeF2(AH2 c){return pow(c,AH2_(1.0/3.0));} + AH3 AToThreeF3(AH3 c){return pow(c,AH3_(1.0/3.0));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 AFrom709H1(AH1 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AFrom709H2(AH2 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AFrom709H3(AH3 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromGammaH1(AH1 c,AH1 x){return pow(c,AH1_(x));} + AH2 AFromGammaH2(AH2 c,AH1 x){return pow(c,AH2_(x));} + AH3 AFromGammaH3(AH3 c,AH1 x){return pow(c,AH3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AHromSrgbF1(AH1 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AHromSrgbF2(AH2 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AHromSrgbF3(AH3 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromTwoH1(AH1 c){return c*c;} + AH2 AFromTwoH2(AH2 c){return c*c;} + AH3 AFromTwoH3(AH3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromThreeH1(AH1 c){return c*c*c;} + AH2 AFromThreeH2(AH2 c){return c*c*c;} + AH3 AFromThreeH3(AH3 c){return c*c*c;} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CS REMAP +//============================================================================================================================== + // Simple remap 64x1 to 8x8 with rotated 2x2 pixel quads in quad linear. + // 543210 + // ====== + // ..xxx. + // yy...y + AU2 ARmp8x8(AU1 a){return AU2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} +//============================================================================================================================== + // More complex remap 64x1 to 8x8 which is necessary for 2D wave reductions. + // 543210 + // ====== + // .xx..x + // y..yy. + // Details, + // LANE TO 8x8 MAPPING + // =================== + // 00 01 08 09 10 11 18 19 + // 02 03 0a 0b 12 13 1a 1b + // 04 05 0c 0d 14 15 1c 1d + // 06 07 0e 0f 16 17 1e 1f + // 20 21 28 29 30 31 38 39 + // 22 23 2a 2b 32 33 3a 3b + // 24 25 2c 2d 34 35 3c 3d + // 26 27 2e 2f 36 37 3e 3f + AU2 ARmpRed8x8(AU1 a){return AU2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} +//============================================================================================================================== + #ifdef A_HALF + AW2 ARmp8x8H(AU1 a){return AW2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} + AW2 ARmpRed8x8H(AU1 a){return AW2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} + #endif +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// REFERENCE +// +//------------------------------------------------------------------------------------------------------------------------------ +// IEEE FLOAT RULES +// ================ +// - saturate(NaN)=0, saturate(-INF)=0, saturate(+INF)=1 +// - {+/-}0 * {+/-}INF = NaN +// - -INF + (+INF) = NaN +// - {+/-}0 / {+/-}0 = NaN +// - {+/-}INF / {+/-}INF = NaN +// - a<(-0) := sqrt(a) = NaN (a=-0.0 won't NaN) +// - 0 == -0 +// - 4/0 = +INF +// - 4/-0 = -INF +// - 4+INF = +INF +// - 4-INF = -INF +// - 4*(+INF) = +INF +// - 4*(-INF) = -INF +// - -4*(+INF) = -INF +// - sqrt(+INF) = +INF +//------------------------------------------------------------------------------------------------------------------------------ +// FP16 ENCODING +// ============= +// fedcba9876543210 +// ---------------- +// ......mmmmmmmmmm 10-bit mantissa (encodes 11-bit 0.5 to 1.0 except for denormals) +// .eeeee.......... 5-bit exponent +// .00000.......... denormals +// .00001.......... -14 exponent +// .11110.......... 15 exponent +// .111110000000000 infinity +// .11111nnnnnnnnnn NaN with n!=0 +// s............... sign +//------------------------------------------------------------------------------------------------------------------------------ +// FP16/INT16 ALIASING DENORMAL +// ============================ +// 11-bit unsigned integers alias with half float denormal/normal values, +// 1 = 2^(-24) = 1/16777216 ....................... first denormal value +// 2 = 2^(-23) +// ... +// 1023 = 2^(-14)*(1-2^(-10)) = 2^(-14)*(1-1/1024) ... last denormal value +// 1024 = 2^(-14) = 1/16384 .......................... first normal value that still maps to integers +// 2047 .............................................. last normal value that still maps to integers +// Scaling limits, +// 2^15 = 32768 ...................................... largest power of 2 scaling +// Largest pow2 conversion mapping is at *32768, +// 1 : 2^(-9) = 1/512 +// 2 : 1/256 +// 4 : 1/128 +// 8 : 1/64 +// 16 : 1/32 +// 32 : 1/16 +// 64 : 1/8 +// 128 : 1/4 +// 256 : 1/2 +// 512 : 1 +// 1024 : 2 +// 2047 : a little less than 4 +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU/CPU PORTABILITY +// +// +//------------------------------------------------------------------------------------------------------------------------------ +// This is the GPU implementation. +// See the CPU implementation for docs. +//============================================================================================================================== +#ifdef A_GPU + #define A_TRUE true + #define A_FALSE false + #define A_STATIC +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD2 + #define retAD3 AD3 + #define retAD4 AD4 + #define retAF2 AF2 + #define retAF3 AF3 + #define retAF4 AF4 + #define retAL2 AL2 + #define retAL3 AL3 + #define retAL4 AL4 + #define retAU2 AU2 + #define retAU3 AU3 + #define retAU4 AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 in AD2 + #define inAD3 in AD3 + #define inAD4 in AD4 + #define inAF2 in AF2 + #define inAF3 in AF3 + #define inAF4 in AF4 + #define inAL2 in AL2 + #define inAL3 in AL3 + #define inAL4 in AL4 + #define inAU2 in AU2 + #define inAU3 in AU3 + #define inAU4 in AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 inout AD2 + #define inoutAD3 inout AD3 + #define inoutAD4 inout AD4 + #define inoutAF2 inout AF2 + #define inoutAF3 inout AF3 + #define inoutAF4 inout AF4 + #define inoutAL2 inout AL2 + #define inoutAL3 inout AL3 + #define inoutAL4 inout AL4 + #define inoutAU2 inout AU2 + #define inoutAU3 inout AU3 + #define inoutAU4 inout AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 out AD2 + #define outAD3 out AD3 + #define outAD4 out AD4 + #define outAF2 out AF2 + #define outAF3 out AF3 + #define outAF4 out AF4 + #define outAL2 out AL2 + #define outAL3 out AL3 + #define outAL4 out AL4 + #define outAU2 out AU2 + #define outAU3 out AU3 + #define outAU4 out AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD2 x + #define varAD3(x) AD3 x + #define varAD4(x) AD4 x + #define varAF2(x) AF2 x + #define varAF3(x) AF3 x + #define varAF4(x) AF4 x + #define varAL2(x) AL2 x + #define varAL3(x) AL3 x + #define varAL4(x) AL4 x + #define varAU2(x) AU2 x + #define varAU3(x) AU3 x + #define varAU4(x) AU4 x +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) AD2(x,y) + #define initAD3(x,y,z) AD3(x,y,z) + #define initAD4(x,y,z,w) AD4(x,y,z,w) + #define initAF2(x,y) AF2(x,y) + #define initAF3(x,y,z) AF3(x,y,z) + #define initAF4(x,y,z,w) AF4(x,y,z,w) + #define initAL2(x,y) AL2(x,y) + #define initAL3(x,y,z) AL3(x,y,z) + #define initAL4(x,y,z,w) AL4(x,y,z,w) + #define initAU2(x,y) AU2(x,y) + #define initAU3(x,y,z) AU3(x,y,z) + #define initAU4(x,y,z,w) AU4(x,y,z,w) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//============================================================================================================================== + #define AAbsD1(a) abs(AD1(a)) + #define AAbsF1(a) abs(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ACosD1(a) cos(AD1(a)) + #define ACosF1(a) cos(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ADotD2(a,b) dot(AD2(a),AD2(b)) + #define ADotD3(a,b) dot(AD3(a),AD3(b)) + #define ADotD4(a,b) dot(AD4(a),AD4(b)) + #define ADotF2(a,b) dot(AF2(a),AF2(b)) + #define ADotF3(a,b) dot(AF3(a),AF3(b)) + #define ADotF4(a,b) dot(AF4(a),AF4(b)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AExp2D1(a) exp2(AD1(a)) + #define AExp2F1(a) exp2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AFloorD1(a) floor(AD1(a)) + #define AFloorF1(a) floor(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ALog2D1(a) log2(AD1(a)) + #define ALog2F1(a) log2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMaxD1(a,b) max(a,b) + #define AMaxF1(a,b) max(a,b) + #define AMaxL1(a,b) max(a,b) + #define AMaxU1(a,b) max(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMinD1(a,b) min(a,b) + #define AMinF1(a,b) min(a,b) + #define AMinL1(a,b) min(a,b) + #define AMinU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASinD1(a) sin(AD1(a)) + #define ASinF1(a) sin(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASqrtD1(a) sqrt(AD1(a)) + #define ASqrtF1(a) sqrt(AF1(a)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + #define APowD1(a,b) pow(AD1(a),AF1(b)) + #define APowF1(a,b) pow(AF1(a),AF1(b)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + #ifdef A_DUBL + AD2 opAAbsD2(outAD2 d,inAD2 a){d=abs(a);return d;} + AD3 opAAbsD3(outAD3 d,inAD3 a){d=abs(a);return d;} + AD4 opAAbsD4(outAD4 d,inAD4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d=a+b;return d;} + AD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d=a+b;return d;} + AD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d=a+AD2_(b);return d;} + AD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d=a+AD3_(b);return d;} + AD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d=a+AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opACpyD2(outAD2 d,inAD2 a){d=a;return d;} + AD3 opACpyD3(outAD3 d,inAD3 a){d=a;return d;} + AD4 opACpyD4(outAD4 d,inAD4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d=ALerpD2(a,b,c);return d;} + AD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d=ALerpD3(a,b,c);return d;} + AD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d=ALerpD4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d=ALerpD2(a,b,AD2_(c));return d;} + AD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d=ALerpD3(a,b,AD3_(c));return d;} + AD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d=ALerpD4(a,b,AD4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d=max(a,b);return d;} + AD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d=max(a,b);return d;} + AD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d=min(a,b);return d;} + AD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d=min(a,b);return d;} + AD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d=a*b;return d;} + AD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d=a*b;return d;} + AD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d=a*AD2_(b);return d;} + AD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d=a*AD3_(b);return d;} + AD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d=a*AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opANegD2(outAD2 d,inAD2 a){d=-a;return d;} + AD3 opANegD3(outAD3 d,inAD3 a){d=-a;return d;} + AD4 opANegD4(outAD4 d,inAD4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opARcpD2(outAD2 d,inAD2 a){d=ARcpD2(a);return d;} + AD3 opARcpD3(outAD3 d,inAD3 a){d=ARcpD3(a);return d;} + AD4 opARcpD4(outAD4 d,inAD4 a){d=ARcpD4(a);return d;} + #endif +//============================================================================================================================== + AF2 opAAbsF2(outAF2 d,inAF2 a){d=abs(a);return d;} + AF3 opAAbsF3(outAF3 d,inAF3 a){d=abs(a);return d;} + AF4 opAAbsF4(outAF4 d,inAF4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d=a+b;return d;} + AF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d=a+b;return d;} + AF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d=a+AF2_(b);return d;} + AF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d=a+AF3_(b);return d;} + AF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d=a+AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opACpyF2(outAF2 d,inAF2 a){d=a;return d;} + AF3 opACpyF3(outAF3 d,inAF3 a){d=a;return d;} + AF4 opACpyF4(outAF4 d,inAF4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d=ALerpF2(a,b,c);return d;} + AF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d=ALerpF3(a,b,c);return d;} + AF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d=ALerpF4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d=ALerpF2(a,b,AF2_(c));return d;} + AF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d=ALerpF3(a,b,AF3_(c));return d;} + AF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d=ALerpF4(a,b,AF4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d=max(a,b);return d;} + AF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d=max(a,b);return d;} + AF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d=min(a,b);return d;} + AF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d=min(a,b);return d;} + AF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d=a*b;return d;} + AF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d=a*b;return d;} + AF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d=a*AF2_(b);return d;} + AF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d=a*AF3_(b);return d;} + AF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d=a*AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opANegF2(outAF2 d,inAF2 a){d=-a;return d;} + AF3 opANegF3(outAF3 d,inAF3 a){d=-a;return d;} + AF4 opANegF4(outAF4 d,inAF4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opARcpF2(outAF2 d,inAF2 a){d=ARcpF2(a);return d;} + AF3 opARcpF3(outAF3 d,inAF3 a){d=ARcpF3(a);return d;} + AF4 opARcpF4(outAF4 d,inAF4 a){d=ARcpF4(a);return d;} +#endif + + +#define FSR_RCAS_F 1 +AU4 con0; + +AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(source, p, 0)); } +void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {} + +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// AMD FidelityFX SUPER RESOLUTION [FSR 1] ::: SPATIAL SCALING & EXTRAS - v1.20210629 +// +// +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// FSR is a collection of algorithms relating to generating a higher resolution image. +// This specific header focuses on single-image non-temporal image scaling, and related tools. +// +// The core functions are EASU and RCAS: +// [EASU] Edge Adaptive Spatial Upsampling ....... 1x to 4x area range spatial scaling, clamped adaptive elliptical filter. +// [RCAS] Robust Contrast Adaptive Sharpening .... A non-scaling variation on CAS. +// RCAS needs to be applied after EASU as a separate pass. +// +// Optional utility functions are: +// [LFGA] Linear Film Grain Applicator ........... Tool to apply film grain after scaling. +// [SRTM] Simple Reversible Tone-Mapper .......... Linear HDR {0 to FP16_MAX} to {0 to 1} and back. +// [TEPD] Temporal Energy Preserving Dither ...... Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// See each individual sub-section for inline documentation. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FUNCTION PERMUTATIONS +// ===================== +// *F() ..... Single item computation with 32-bit. +// *H() ..... Single item computation with 16-bit, with packing (aka two 16-bit ops in parallel) when possible. +// *Hx2() ... Processing two items in parallel with 16-bit, easier packing. +// Not all interfaces in this file have a *Hx2() form. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING +// +//------------------------------------------------------------------------------------------------------------------------------ +// EASU provides a high quality spatial-only scaling at relatively low cost. +// Meaning EASU is appropiate for laptops and other low-end GPUs. +// Quality from 1x to 4x area scaling is good. +//------------------------------------------------------------------------------------------------------------------------------ +// The scalar uses a modified fast approximation to the standard lanczos(size=2) kernel. +// EASU runs in a single pass, so it applies a directionally and anisotropically adaptive radial lanczos. +// This is also kept as simple as possible to have minimum runtime. +//------------------------------------------------------------------------------------------------------------------------------ +// The lanzcos filter has negative lobes, so by itself it will introduce ringing. +// To remove all ringing, the algorithm uses the nearest 2x2 input texels as a neighborhood, +// and limits output to the minimum and maximum of that neighborhood. +//------------------------------------------------------------------------------------------------------------------------------ +// Input image requirements: +// +// Color needs to be encoded as 3 channel[red, green, blue](e.g.XYZ not supported) +// Each channel needs to be in the range[0, 1] +// Any color primaries are supported +// Display / tonemapping curve needs to be as if presenting to sRGB display or similar(e.g.Gamma 2.0) +// There should be no banding in the input +// There should be no high amplitude noise in the input +// There should be no noise in the input that is not at input pixel granularity +// For performance purposes, use 32bpp formats +//------------------------------------------------------------------------------------------------------------------------------ +// Best to apply EASU at the end of the frame after tonemapping +// but before film grain or composite of the UI. +//------------------------------------------------------------------------------------------------------------------------------ +// Example of including this header for D3D HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan GLSL : +// +// #define A_GPU 1 +// #define A_GLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HLSL_6_2 1 +// #define A_NO_16_BIT_CAST 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of declaring the required input callbacks for GLSL : +// The callbacks need to gather4 for each color channel using the specified texture coordinate 'p'. +// EASU uses gather4 to reduce position computation logic and for free Arrays of Structures to Structures of Arrays conversion. +// +// AH4 FsrEasuRH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,0));} +// AH4 FsrEasuGH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,1));} +// AH4 FsrEasuBH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,2));} +// ... +// The FsrEasuCon function needs to be called from the CPU or GPU to set up constants. +// The difference in viewport and input image size is there to support Dynamic Resolution Scaling. +// To use FsrEasuCon() on the CPU, define A_CPU before including ffx_a and ffx_fsr1. +// Including a GPU example here, the 'con0' through 'con3' values would be stored out to a constant buffer. +// AU4 con0,con1,con2,con3; +// FsrEasuCon(con0,con1,con2,con3, +// 1920.0,1080.0, // Viewport size (top left aligned) in the input image which is to be scaled. +// 3840.0,2160.0, // The size of the input image. +// 2560.0,1440.0); // The output resolution. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrEasuCon( +outAU4 con0, +outAU4 con1, +outAU4 con2, +outAU4 con3, +// This the rendered image resolution being upscaled +AF1 inputViewportInPixelsX, +AF1 inputViewportInPixelsY, +// This is the resolution of the resource containing the input image (useful for dynamic resolution) +AF1 inputSizeInPixelsX, +AF1 inputSizeInPixelsY, +// This is the display resolution which the input image gets upscaled to +AF1 outputSizeInPixelsX, +AF1 outputSizeInPixelsY){ + // Output integer position to a pixel position in viewport. + con0[0]=AU1_AF1(inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)); + con0[1]=AU1_AF1(inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)); + con0[2]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)-AF1_(0.5)); + con0[3]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)-AF1_(0.5)); + // Viewport pixel position to normalized image space. + // This is used to get upper-left of 'F' tap. + con1[0]=AU1_AF1(ARcpF1(inputSizeInPixelsX)); + con1[1]=AU1_AF1(ARcpF1(inputSizeInPixelsY)); + // Centers of gather4, first offset from upper-left of 'F'. + // +---+---+ + // | | | + // +--(0)--+ + // | b | c | + // +---F---+---+---+ + // | e | f | g | h | + // +--(1)--+--(2)--+ + // | i | j | k | l | + // +---+---+---+---+ + // | n | o | + // +--(3)--+ + // | | | + // +---+---+ + con1[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con1[3]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsY)); + // These are from (0) instead of 'F'. + con2[0]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsX)); + con2[1]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con2[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con2[3]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con3[0]=AU1_AF1(AF1_( 0.0)*ARcpF1(inputSizeInPixelsX)); + con3[1]=AU1_AF1(AF1_( 4.0)*ARcpF1(inputSizeInPixelsY)); + con3[2]=con3[3]=0;} + +//If the an offset into the input image resource +A_STATIC void FsrEasuConOffset( + outAU4 con0, + outAU4 con1, + outAU4 con2, + outAU4 con3, + // This the rendered image resolution being upscaled + AF1 inputViewportInPixelsX, + AF1 inputViewportInPixelsY, + // This is the resolution of the resource containing the input image (useful for dynamic resolution) + AF1 inputSizeInPixelsX, + AF1 inputSizeInPixelsY, + // This is the display resolution which the input image gets upscaled to + AF1 outputSizeInPixelsX, + AF1 outputSizeInPixelsY, + // This is the input image offset into the resource containing it (useful for dynamic resolution) + AF1 inputOffsetInPixelsX, + AF1 inputOffsetInPixelsY) { + FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY, inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY); + con0[2] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsX * ARcpF1(outputSizeInPixelsX) - AF1_(0.5) + inputOffsetInPixelsX); + con0[3] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsY * ARcpF1(outputSizeInPixelsY) - AF1_(0.5) + inputOffsetInPixelsY); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_EASU_F) + // Input callback prototypes, need to be implemented by calling shader + AF4 FsrEasuRF(AF2 p); + AF4 FsrEasuGF(AF2 p); + AF4 FsrEasuBF(AF2 p); +//------------------------------------------------------------------------------------------------------------------------------ + // Filtering for a given tap for the scalar. + void FsrEasuTapF( + inout AF3 aC, // Accumulated color, with negative lobe. + inout AF1 aW, // Accumulated weight. + AF2 off, // Pixel offset from resolve position to tap. + AF2 dir, // Gradient direction. + AF2 len, // Length. + AF1 lob, // Negative lobe strength. + AF1 clp, // Clipping point. + AF3 c){ // Tap color. + // Rotate offset by direction. + AF2 v; + v.x=(off.x*( dir.x))+(off.y*dir.y); + v.y=(off.x*(-dir.y))+(off.y*dir.x); + // Anisotropy. + v*=len; + // Compute distance^2. + AF1 d2=v.x*v.x+v.y*v.y; + // Limit to the window as at corner, 2 taps can easily be outside. + d2=min(d2,clp); + // Approximation of lancos2 without sin() or rcp(), or sqrt() to get x. + // (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2 + // |_______________________________________| |_______________| + // base window + // The general form of the 'base' is, + // (a*(b*x^2-1)^2-(a-1)) + // Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe. + AF1 wB=AF1_(2.0/5.0)*d2+AF1_(-1.0); + AF1 wA=lob*d2+AF1_(-1.0); + wB*=wB; + wA*=wA; + wB=AF1_(25.0/16.0)*wB+AF1_(-(25.0/16.0-1.0)); + AF1 w=wB*wA; + // Do weighted average. + aC+=c*w;aW+=w;} +//------------------------------------------------------------------------------------------------------------------------------ + // Accumulate direction and length. + void FsrEasuSetF( + inout AF2 dir, + inout AF1 len, + AF2 pp, + AP1 biS,AP1 biT,AP1 biU,AP1 biV, + AF1 lA,AF1 lB,AF1 lC,AF1 lD,AF1 lE){ + // Compute bilinear weight, branches factor out as predicates are compiler time immediates. + // s t + // u v + AF1 w = AF1_(0.0); + if(biS)w=(AF1_(1.0)-pp.x)*(AF1_(1.0)-pp.y); + if(biT)w= pp.x *(AF1_(1.0)-pp.y); + if(biU)w=(AF1_(1.0)-pp.x)* pp.y ; + if(biV)w= pp.x * pp.y ; + // Direction is the '+' diff. + // a + // b c d + // e + // Then takes magnitude from abs average of both sides of 'c'. + // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms. + AF1 dc=lD-lC; + AF1 cb=lC-lB; + AF1 lenX=max(abs(dc),abs(cb)); + lenX=APrxLoRcpF1(lenX); + AF1 dirX=lD-lB; + dir.x+=dirX*w; + lenX=ASatF1(abs(dirX)*lenX); + lenX*=lenX; + len+=lenX*w; + // Repeat for the y axis. + AF1 ec=lE-lC; + AF1 ca=lC-lA; + AF1 lenY=max(abs(ec),abs(ca)); + lenY=APrxLoRcpF1(lenY); + AF1 dirY=lE-lA; + dir.y+=dirY*w; + lenY=ASatF1(abs(dirY)*lenY); + lenY*=lenY; + len+=lenY*w;} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrEasuF( + out AF3 pix, + AU2 ip, // Integer pixel position in output. + AU4 con0, // Constants generated by FsrEasuCon(). + AU4 con1, + AU4 con2, + AU4 con3){ +//------------------------------------------------------------------------------------------------------------------------------ + // Get position of 'f'. + AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw); + AF2 fp=floor(pp); + pp-=fp; +//------------------------------------------------------------------------------------------------------------------------------ + // 12-tap kernel. + // b c + // e f g h + // i j k l + // n o + // Gather 4 ordering. + // a b + // r g + // For packed FP16, need either {rg} or {ab} so using the following setup for gather in all versions, + // a b <- unused (z) + // r g + // a b a b + // r g r g + // a b + // r g <- unused (z) + // Allowing dead-code removal to remove the 'z's. + AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw); + // These are from p0 to avoid pulling two constants on pre-Navi hardware. + AF2 p1=p0+AF2_AU2(con2.xy); + AF2 p2=p0+AF2_AU2(con2.zw); + AF2 p3=p0+AF2_AU2(con3.xy); + AF4 bczzR=FsrEasuRF(p0); + AF4 bczzG=FsrEasuGF(p0); + AF4 bczzB=FsrEasuBF(p0); + AF4 ijfeR=FsrEasuRF(p1); + AF4 ijfeG=FsrEasuGF(p1); + AF4 ijfeB=FsrEasuBF(p1); + AF4 klhgR=FsrEasuRF(p2); + AF4 klhgG=FsrEasuGF(p2); + AF4 klhgB=FsrEasuBF(p2); + AF4 zzonR=FsrEasuRF(p3); + AF4 zzonG=FsrEasuGF(p3); + AF4 zzonB=FsrEasuBF(p3); +//------------------------------------------------------------------------------------------------------------------------------ + // Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD). + AF4 bczzL=bczzB*AF4_(0.5)+(bczzR*AF4_(0.5)+bczzG); + AF4 ijfeL=ijfeB*AF4_(0.5)+(ijfeR*AF4_(0.5)+ijfeG); + AF4 klhgL=klhgB*AF4_(0.5)+(klhgR*AF4_(0.5)+klhgG); + AF4 zzonL=zzonB*AF4_(0.5)+(zzonR*AF4_(0.5)+zzonG); + // Rename. + AF1 bL=bczzL.x; + AF1 cL=bczzL.y; + AF1 iL=ijfeL.x; + AF1 jL=ijfeL.y; + AF1 fL=ijfeL.z; + AF1 eL=ijfeL.w; + AF1 kL=klhgL.x; + AF1 lL=klhgL.y; + AF1 hL=klhgL.z; + AF1 gL=klhgL.w; + AF1 oL=zzonL.z; + AF1 nL=zzonL.w; + // Accumulate for bilinear interpolation. + AF2 dir=AF2_(0.0); + AF1 len=AF1_(0.0); + FsrEasuSetF(dir,len,pp,true, false,false,false,bL,eL,fL,gL,jL); + FsrEasuSetF(dir,len,pp,false,true ,false,false,cL,fL,gL,hL,kL); + FsrEasuSetF(dir,len,pp,false,false,true ,false,fL,iL,jL,kL,nL); + FsrEasuSetF(dir,len,pp,false,false,false,true ,gL,jL,kL,lL,oL); +//------------------------------------------------------------------------------------------------------------------------------ + // Normalize with approximation, and cleanup close to zero. + AF2 dir2=dir*dir; + AF1 dirR=dir2.x+dir2.y; + AP1 zro=dirR<AF1_(1.0/32768.0); + dirR=APrxLoRsqF1(dirR); + dirR=zro?AF1_(1.0):dirR; + dir.x=zro?AF1_(1.0):dir.x; + dir*=AF2_(dirR); + // Transform from {0 to 2} to {0 to 1} range, and shape with square. + len=len*AF1_(0.5); + len*=len; + // Stretch kernel {1.0 vert|horz, to sqrt(2.0) on diagonal}. + AF1 stretch=(dir.x*dir.x+dir.y*dir.y)*APrxLoRcpF1(max(abs(dir.x),abs(dir.y))); + // Anisotropic length after rotation, + // x := 1.0 lerp to 'stretch' on edges + // y := 1.0 lerp to 2x on edges + AF2 len2=AF2(AF1_(1.0)+(stretch-AF1_(1.0))*len,AF1_(1.0)+AF1_(-0.5)*len); + // Based on the amount of 'edge', + // the window shifts from +/-{sqrt(2.0) to slightly beyond 2.0}. + AF1 lob=AF1_(0.5)+AF1_((1.0/4.0-0.04)-0.5)*len; + // Set distance^2 clipping point to the end of the adjustable window. + AF1 clp=APrxLoRcpF1(lob); +//------------------------------------------------------------------------------------------------------------------------------ + // Accumulation mixed with min/max of 4 nearest. + // b c + // e f g h + // i j k l + // n o + AF3 min4=min(AMin3F3(AF3(ijfeR.z,ijfeG.z,ijfeB.z),AF3(klhgR.w,klhgG.w,klhgB.w),AF3(ijfeR.y,ijfeG.y,ijfeB.y)), + AF3(klhgR.x,klhgG.x,klhgB.x)); + AF3 max4=max(AMax3F3(AF3(ijfeR.z,ijfeG.z,ijfeB.z),AF3(klhgR.w,klhgG.w,klhgB.w),AF3(ijfeR.y,ijfeG.y,ijfeB.y)), + AF3(klhgR.x,klhgG.x,klhgB.x)); + // Accumulation. + AF3 aC=AF3_(0.0); + AF1 aW=AF1_(0.0); + FsrEasuTapF(aC,aW,AF2( 0.0,-1.0)-pp,dir,len2,lob,clp,AF3(bczzR.x,bczzG.x,bczzB.x)); // b + FsrEasuTapF(aC,aW,AF2( 1.0,-1.0)-pp,dir,len2,lob,clp,AF3(bczzR.y,bczzG.y,bczzB.y)); // c + FsrEasuTapF(aC,aW,AF2(-1.0, 1.0)-pp,dir,len2,lob,clp,AF3(ijfeR.x,ijfeG.x,ijfeB.x)); // i + FsrEasuTapF(aC,aW,AF2( 0.0, 1.0)-pp,dir,len2,lob,clp,AF3(ijfeR.y,ijfeG.y,ijfeB.y)); // j + FsrEasuTapF(aC,aW,AF2( 0.0, 0.0)-pp,dir,len2,lob,clp,AF3(ijfeR.z,ijfeG.z,ijfeB.z)); // f + FsrEasuTapF(aC,aW,AF2(-1.0, 0.0)-pp,dir,len2,lob,clp,AF3(ijfeR.w,ijfeG.w,ijfeB.w)); // e + FsrEasuTapF(aC,aW,AF2( 1.0, 1.0)-pp,dir,len2,lob,clp,AF3(klhgR.x,klhgG.x,klhgB.x)); // k + FsrEasuTapF(aC,aW,AF2( 2.0, 1.0)-pp,dir,len2,lob,clp,AF3(klhgR.y,klhgG.y,klhgB.y)); // l + FsrEasuTapF(aC,aW,AF2( 2.0, 0.0)-pp,dir,len2,lob,clp,AF3(klhgR.z,klhgG.z,klhgB.z)); // h + FsrEasuTapF(aC,aW,AF2( 1.0, 0.0)-pp,dir,len2,lob,clp,AF3(klhgR.w,klhgG.w,klhgB.w)); // g + FsrEasuTapF(aC,aW,AF2( 1.0, 2.0)-pp,dir,len2,lob,clp,AF3(zzonR.z,zzonG.z,zzonB.z)); // o + FsrEasuTapF(aC,aW,AF2( 0.0, 2.0)-pp,dir,len2,lob,clp,AF3(zzonR.w,zzonG.w,zzonB.w)); // n +//------------------------------------------------------------------------------------------------------------------------------ + // Normalize and dering. + pix=min(max4,max(min4,aC*AF3_(ARcpF1(aW))));} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_EASU_H) +// Input callback prototypes, need to be implemented by calling shader + AH4 FsrEasuRH(AF2 p); + AH4 FsrEasuGH(AF2 p); + AH4 FsrEasuBH(AF2 p); +//------------------------------------------------------------------------------------------------------------------------------ + // This runs 2 taps in parallel. + void FsrEasuTapH( + inout AH2 aCR,inout AH2 aCG,inout AH2 aCB, + inout AH2 aW, + AH2 offX,AH2 offY, + AH2 dir, + AH2 len, + AH1 lob, + AH1 clp, + AH2 cR,AH2 cG,AH2 cB){ + AH2 vX,vY; + vX=offX* dir.xx +offY*dir.yy; + vY=offX*(-dir.yy)+offY*dir.xx; + vX*=len.x;vY*=len.y; + AH2 d2=vX*vX+vY*vY; + d2=min(d2,AH2_(clp)); + AH2 wB=AH2_(2.0/5.0)*d2+AH2_(-1.0); + AH2 wA=AH2_(lob)*d2+AH2_(-1.0); + wB*=wB; + wA*=wA; + wB=AH2_(25.0/16.0)*wB+AH2_(-(25.0/16.0-1.0)); + AH2 w=wB*wA; + aCR+=cR*w;aCG+=cG*w;aCB+=cB*w;aW+=w;} +//------------------------------------------------------------------------------------------------------------------------------ + // This runs 2 taps in parallel. + void FsrEasuSetH( + inout AH2 dirPX,inout AH2 dirPY, + inout AH2 lenP, + AH2 pp, + AP1 biST,AP1 biUV, + AH2 lA,AH2 lB,AH2 lC,AH2 lD,AH2 lE){ + AH2 w = AH2_(0.0); + if(biST)w=(AH2(1.0,0.0)+AH2(-pp.x,pp.x))*AH2_(AH1_(1.0)-pp.y); + if(biUV)w=(AH2(1.0,0.0)+AH2(-pp.x,pp.x))*AH2_( pp.y); + // ABS is not free in the packed FP16 path. + AH2 dc=lD-lC; + AH2 cb=lC-lB; + AH2 lenX=max(abs(dc),abs(cb)); + lenX=ARcpH2(lenX); + AH2 dirX=lD-lB; + dirPX+=dirX*w; + lenX=ASatH2(abs(dirX)*lenX); + lenX*=lenX; + lenP+=lenX*w; + AH2 ec=lE-lC; + AH2 ca=lC-lA; + AH2 lenY=max(abs(ec),abs(ca)); + lenY=ARcpH2(lenY); + AH2 dirY=lE-lA; + dirPY+=dirY*w; + lenY=ASatH2(abs(dirY)*lenY); + lenY*=lenY; + lenP+=lenY*w;} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrEasuH( + out AH3 pix, + AU2 ip, + AU4 con0, + AU4 con1, + AU4 con2, + AU4 con3){ +//------------------------------------------------------------------------------------------------------------------------------ + AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw); + AF2 fp=floor(pp); + pp-=fp; + AH2 ppp=AH2(pp); +//------------------------------------------------------------------------------------------------------------------------------ + AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw); + AF2 p1=p0+AF2_AU2(con2.xy); + AF2 p2=p0+AF2_AU2(con2.zw); + AF2 p3=p0+AF2_AU2(con3.xy); + AH4 bczzR=FsrEasuRH(p0); + AH4 bczzG=FsrEasuGH(p0); + AH4 bczzB=FsrEasuBH(p0); + AH4 ijfeR=FsrEasuRH(p1); + AH4 ijfeG=FsrEasuGH(p1); + AH4 ijfeB=FsrEasuBH(p1); + AH4 klhgR=FsrEasuRH(p2); + AH4 klhgG=FsrEasuGH(p2); + AH4 klhgB=FsrEasuBH(p2); + AH4 zzonR=FsrEasuRH(p3); + AH4 zzonG=FsrEasuGH(p3); + AH4 zzonB=FsrEasuBH(p3); +//------------------------------------------------------------------------------------------------------------------------------ + AH4 bczzL=bczzB*AH4_(0.5)+(bczzR*AH4_(0.5)+bczzG); + AH4 ijfeL=ijfeB*AH4_(0.5)+(ijfeR*AH4_(0.5)+ijfeG); + AH4 klhgL=klhgB*AH4_(0.5)+(klhgR*AH4_(0.5)+klhgG); + AH4 zzonL=zzonB*AH4_(0.5)+(zzonR*AH4_(0.5)+zzonG); + AH1 bL=bczzL.x; + AH1 cL=bczzL.y; + AH1 iL=ijfeL.x; + AH1 jL=ijfeL.y; + AH1 fL=ijfeL.z; + AH1 eL=ijfeL.w; + AH1 kL=klhgL.x; + AH1 lL=klhgL.y; + AH1 hL=klhgL.z; + AH1 gL=klhgL.w; + AH1 oL=zzonL.z; + AH1 nL=zzonL.w; + // This part is different, accumulating 2 taps in parallel. + AH2 dirPX=AH2_(0.0); + AH2 dirPY=AH2_(0.0); + AH2 lenP=AH2_(0.0); + FsrEasuSetH(dirPX,dirPY,lenP,ppp,true, false,AH2(bL,cL),AH2(eL,fL),AH2(fL,gL),AH2(gL,hL),AH2(jL,kL)); + FsrEasuSetH(dirPX,dirPY,lenP,ppp,false,true ,AH2(fL,gL),AH2(iL,jL),AH2(jL,kL),AH2(kL,lL),AH2(nL,oL)); + AH2 dir=AH2(dirPX.r+dirPX.g,dirPY.r+dirPY.g); + AH1 len=lenP.r+lenP.g; +//------------------------------------------------------------------------------------------------------------------------------ + AH2 dir2=dir*dir; + AH1 dirR=dir2.x+dir2.y; + AP1 zro=dirR<AH1_(1.0/32768.0); + dirR=APrxLoRsqH1(dirR); + dirR=zro?AH1_(1.0):dirR; + dir.x=zro?AH1_(1.0):dir.x; + dir*=AH2_(dirR); + len=len*AH1_(0.5); + len*=len; + AH1 stretch=(dir.x*dir.x+dir.y*dir.y)*APrxLoRcpH1(max(abs(dir.x),abs(dir.y))); + AH2 len2=AH2(AH1_(1.0)+(stretch-AH1_(1.0))*len,AH1_(1.0)+AH1_(-0.5)*len); + AH1 lob=AH1_(0.5)+AH1_((1.0/4.0-0.04)-0.5)*len; + AH1 clp=APrxLoRcpH1(lob); +//------------------------------------------------------------------------------------------------------------------------------ + // FP16 is different, using packed trick to do min and max in same operation. + AH2 bothR=max(max(AH2(-ijfeR.z,ijfeR.z),AH2(-klhgR.w,klhgR.w)),max(AH2(-ijfeR.y,ijfeR.y),AH2(-klhgR.x,klhgR.x))); + AH2 bothG=max(max(AH2(-ijfeG.z,ijfeG.z),AH2(-klhgG.w,klhgG.w)),max(AH2(-ijfeG.y,ijfeG.y),AH2(-klhgG.x,klhgG.x))); + AH2 bothB=max(max(AH2(-ijfeB.z,ijfeB.z),AH2(-klhgB.w,klhgB.w)),max(AH2(-ijfeB.y,ijfeB.y),AH2(-klhgB.x,klhgB.x))); + // This part is different for FP16, working pairs of taps at a time. + AH2 pR=AH2_(0.0); + AH2 pG=AH2_(0.0); + AH2 pB=AH2_(0.0); + AH2 pW=AH2_(0.0); + FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0, 1.0)-ppp.xx,AH2(-1.0,-1.0)-ppp.yy,dir,len2,lob,clp,bczzR.xy,bczzG.xy,bczzB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2(-1.0, 0.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,ijfeR.xy,ijfeG.xy,ijfeB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2( 0.0,-1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,ijfeR.zw,ijfeG.zw,ijfeB.zw); + FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 2.0)-ppp.xx,AH2( 1.0, 1.0)-ppp.yy,dir,len2,lob,clp,klhgR.xy,klhgG.xy,klhgB.xy); + FsrEasuTapH(pR,pG,pB,pW,AH2( 2.0, 1.0)-ppp.xx,AH2( 0.0, 0.0)-ppp.yy,dir,len2,lob,clp,klhgR.zw,klhgG.zw,klhgB.zw); + FsrEasuTapH(pR,pG,pB,pW,AH2( 1.0, 0.0)-ppp.xx,AH2( 2.0, 2.0)-ppp.yy,dir,len2,lob,clp,zzonR.zw,zzonG.zw,zzonB.zw); + AH3 aC=AH3(pR.x+pR.y,pG.x+pG.y,pB.x+pB.y); + AH1 aW=pW.x+pW.y; +//------------------------------------------------------------------------------------------------------------------------------ + // Slightly different for FP16 version due to combined min and max. + pix=min(AH3(bothR.y,bothG.y,bothB.y),max(-AH3(bothR.x,bothG.x,bothB.x),aC*AH3_(ARcpH1(aW))));} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING +// +//------------------------------------------------------------------------------------------------------------------------------ +// CAS uses a simplified mechanism to convert local contrast into a variable amount of sharpness. +// RCAS uses a more exact mechanism, solving for the maximum local sharpness possible before clipping. +// RCAS also has a built in process to limit sharpening of what it detects as possible noise. +// RCAS sharper does not support scaling, as it should be applied after EASU scaling. +// Pass EASU output straight into RCAS, no color conversions necessary. +//------------------------------------------------------------------------------------------------------------------------------ +// RCAS is based on the following logic. +// RCAS uses a 5 tap filter in a cross pattern (same as CAS), +// w n +// w 1 w for taps w m e +// w s +// Where 'w' is the negative lobe weight. +// output = (w*(n+e+w+s)+m)/(4*w+1) +// RCAS solves for 'w' by seeing where the signal might clip out of the {0 to 1} input range, +// 0 == (w*(n+e+w+s)+m)/(4*w+1) -> w = -m/(n+e+w+s) +// 1 == (w*(n+e+w+s)+m)/(4*w+1) -> w = (1-m)/(n+e+w+s-4*1) +// Then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. +// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. +// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps. +// As well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation. +// This stabilizes RCAS. +// RCAS does a simple highpass which is normalized against the local contrast then shaped, +// 0.25 +// 0.25 -1 0.25 +// 0.25 +// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges. +// +// GLSL example for the required callbacks : +// +// AH4 FsrRcasLoadH(ASW2 p){return AH4(imageLoad(imgSrc,ASU2(p)));} +// void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b) +// { +// //do any simple input color conversions here or leave empty if none needed +// } +// +// FsrRcasCon need to be called from the CPU or GPU to set up constants. +// Including a GPU example here, the 'con' value would be stored out to a constant buffer. +// +// AU4 con; +// FsrRcasCon(con, +// 0.0); // The scale is {0.0 := maximum sharpness, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +// --------------- +// RCAS sharpening supports a CAS-like pass-through alpha via, +// #define FSR_RCAS_PASSTHROUGH_ALPHA 1 +// RCAS also supports a define to enable a more expensive path to avoid some sharpening of noise. +// Would suggest it is better to apply film grain after RCAS sharpening (and after scaling) instead of using this define, +// #define FSR_RCAS_DENOISE 1 +//============================================================================================================================== +// This is set at the limit of providing unnatural results for sharpening. +#define FSR_RCAS_LIMIT (0.25-(1.0/16.0)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrRcasCon( +outAU4 con, +// The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +AF1 sharpness){ + // Transform from stops to linear value. + sharpness=AExp2F1(-sharpness); + varAF2(hSharp)=initAF2(sharpness,sharpness); + con[0]=AU1_AF1(sharpness); + con[1]=AU1_AH2_AF2(hSharp); + con[2]=0; + con[3]=0;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_RCAS_F) + // Input callback prototypes that need to be implemented by calling shader + AF4 FsrRcasLoadF(ASU2 p); + void FsrRcasInputF(inout AF1 r,inout AF1 g,inout AF1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasF( + out AF1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AF1 pixG, + out AF1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AF1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASU2 sp=ASU2(ip); + AF3 b=FsrRcasLoadF(sp+ASU2( 0,-1)).rgb; + AF3 d=FsrRcasLoadF(sp+ASU2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AF4 ee=FsrRcasLoadF(sp); + AF3 e=ee.rgb;pixA=ee.a; + #else + AF3 e=FsrRcasLoadF(sp).rgb; + #endif + AF3 f=FsrRcasLoadF(sp+ASU2( 1, 0)).rgb; + AF3 h=FsrRcasLoadF(sp+ASU2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AF1 bR=b.r; + AF1 bG=b.g; + AF1 bB=b.b; + AF1 dR=d.r; + AF1 dG=d.g; + AF1 dB=d.b; + AF1 eR=e.r; + AF1 eG=e.g; + AF1 eB=e.b; + AF1 fR=f.r; + AF1 fG=f.g; + AF1 fB=f.b; + AF1 hR=h.r; + AF1 hG=h.g; + AF1 hB=h.b; + // Run optional input transform. + FsrRcasInputF(bR,bG,bB); + FsrRcasInputF(dR,dG,dB); + FsrRcasInputF(eR,eG,eB); + FsrRcasInputF(fR,fG,fB); + FsrRcasInputF(hR,hG,hB); + // Luma times 2. + AF1 bL=bB*AF1_(0.5)+(bR*AF1_(0.5)+bG); + AF1 dL=dB*AF1_(0.5)+(dR*AF1_(0.5)+dG); + AF1 eL=eB*AF1_(0.5)+(eR*AF1_(0.5)+eG); + AF1 fL=fB*AF1_(0.5)+(fR*AF1_(0.5)+fG); + AF1 hL=hB*AF1_(0.5)+(hR*AF1_(0.5)+hG); + // Noise detection. + AF1 nz=AF1_(0.25)*bL+AF1_(0.25)*dL+AF1_(0.25)*fL+AF1_(0.25)*hL-eL; + nz=ASatF1(abs(nz)*APrxMedRcpF1(AMax3F1(AMax3F1(bL,dL,eL),fL,hL)-AMin3F1(AMin3F1(bL,dL,eL),fL,hL))); + nz=AF1_(-0.5)*nz+AF1_(1.0); + // Min and max of ring. + AF1 mn4R=min(AMin3F1(bR,dR,fR),hR); + AF1 mn4G=min(AMin3F1(bG,dG,fG),hG); + AF1 mn4B=min(AMin3F1(bB,dB,fB),hB); + AF1 mx4R=max(AMax3F1(bR,dR,fR),hR); + AF1 mx4G=max(AMax3F1(bG,dG,fG),hG); + AF1 mx4B=max(AMax3F1(bB,dB,fB),hB); + // Immediate constants for peak range. + AF2 peakC=AF2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R); + AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G); + AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B); + AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y); + AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y); + AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y); + AF1 lobeR=max(-hitMinR,hitMaxR); + AF1 lobeG=max(-hitMinG,hitMaxG); + AF1 lobeB=max(-hitMinB,hitMaxB); + AF1 lobe=max(AF1_(-FSR_RCAS_LIMIT),min(AMax3F1(lobeR,lobeG,lobeB),AF1_(0.0)))*AF1_AU1(con.x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AF1 rcpL=APrxMedRcpF1(AF1_(4.0)*lobe+AF1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL; + return;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_H) + // Input callback prototypes that need to be implemented by calling shader + AH4 FsrRcasLoadH(ASW2 p); + void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasH( + out AH1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AH1 pixG, + out AH1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Sharpening algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASW2 sp=ASW2(ip); + AH3 b=FsrRcasLoadH(sp+ASW2( 0,-1)).rgb; + AH3 d=FsrRcasLoadH(sp+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee=FsrRcasLoadH(sp); + AH3 e=ee.rgb;pixA=ee.a; + #else + AH3 e=FsrRcasLoadH(sp).rgb; + #endif + AH3 f=FsrRcasLoadH(sp+ASW2( 1, 0)).rgb; + AH3 h=FsrRcasLoadH(sp+ASW2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AH1 bR=b.r; + AH1 bG=b.g; + AH1 bB=b.b; + AH1 dR=d.r; + AH1 dG=d.g; + AH1 dB=d.b; + AH1 eR=e.r; + AH1 eG=e.g; + AH1 eB=e.b; + AH1 fR=f.r; + AH1 fG=f.g; + AH1 fB=f.b; + AH1 hR=h.r; + AH1 hG=h.g; + AH1 hB=h.b; + // Run optional input transform. + FsrRcasInputH(bR,bG,bB); + FsrRcasInputH(dR,dG,dB); + FsrRcasInputH(eR,eG,eB); + FsrRcasInputH(fR,fG,fB); + FsrRcasInputH(hR,hG,hB); + // Luma times 2. + AH1 bL=bB*AH1_(0.5)+(bR*AH1_(0.5)+bG); + AH1 dL=dB*AH1_(0.5)+(dR*AH1_(0.5)+dG); + AH1 eL=eB*AH1_(0.5)+(eR*AH1_(0.5)+eG); + AH1 fL=fB*AH1_(0.5)+(fR*AH1_(0.5)+fG); + AH1 hL=hB*AH1_(0.5)+(hR*AH1_(0.5)+hG); + // Noise detection. + AH1 nz=AH1_(0.25)*bL+AH1_(0.25)*dL+AH1_(0.25)*fL+AH1_(0.25)*hL-eL; + nz=ASatH1(abs(nz)*APrxMedRcpH1(AMax3H1(AMax3H1(bL,dL,eL),fL,hL)-AMin3H1(AMin3H1(bL,dL,eL),fL,hL))); + nz=AH1_(-0.5)*nz+AH1_(1.0); + // Min and max of ring. + AH1 mn4R=min(AMin3H1(bR,dR,fR),hR); + AH1 mn4G=min(AMin3H1(bG,dG,fG),hG); + AH1 mn4B=min(AMin3H1(bB,dB,fB),hB); + AH1 mx4R=max(AMax3H1(bR,dR,fR),hR); + AH1 mx4G=max(AMax3H1(bG,dG,fG),hG); + AH1 mx4B=max(AMax3H1(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R); + AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G); + AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B); + AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y); + AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y); + AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y); + AH1 lobeR=max(-hitMinR,hitMaxR); + AH1 lobeG=max(-hitMinG,hitMaxG); + AH1 lobeB=max(-hitMinB,hitMaxB); + AH1 lobe=max(AH1_(-FSR_RCAS_LIMIT),min(AMax3H1(lobeR,lobeG,lobeB),AH1_(0.0)))*AH2_AU1(con.y).x; + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH1 rcpL=APrxMedRcpH1(AH1_(4.0)*lobe+AH1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_HX2) + // Input callback prototypes that need to be implemented by the calling shader + AH4 FsrRcasLoadHx2(ASW2 p); + void FsrRcasInputHx2(inout AH2 r,inout AH2 g,inout AH2 b); +//------------------------------------------------------------------------------------------------------------------------------ + // Can be used to convert from packed Structures of Arrays to Arrays of Structures for store. + void FsrRcasDepackHx2(out AH4 pix0,out AH4 pix1,AH2 pixR,AH2 pixG,AH2 pixB){ + #ifdef A_HLSL + // Invoke a slower path for DX only, since it won't allow uninitialized values. + pix0.a=pix1.a=0.0; + #endif + pix0.rgb=AH3(pixR.x,pixG.x,pixB.x); + pix1.rgb=AH3(pixR.y,pixG.y,pixB.y);} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasHx2( + // Output values are for 2 8x8 tiles in a 16x8 region. + // pix<R,G,B>.x = left 8x8 tile + // pix<R,G,B>.y = right 8x8 tile + // This enables later processing to easily be packed as well. + out AH2 pixR, + out AH2 pixG, + out AH2 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH2 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // No scaling algorithm uses minimal 3x3 pixel neighborhood. + ASW2 sp0=ASW2(ip); + AH3 b0=FsrRcasLoadHx2(sp0+ASW2( 0,-1)).rgb; + AH3 d0=FsrRcasLoadHx2(sp0+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee0=FsrRcasLoadHx2(sp0); + AH3 e0=ee0.rgb;pixA.r=ee0.a; + #else + AH3 e0=FsrRcasLoadHx2(sp0).rgb; + #endif + AH3 f0=FsrRcasLoadHx2(sp0+ASW2( 1, 0)).rgb; + AH3 h0=FsrRcasLoadHx2(sp0+ASW2( 0, 1)).rgb; + ASW2 sp1=sp0+ASW2(8,0); + AH3 b1=FsrRcasLoadHx2(sp1+ASW2( 0,-1)).rgb; + AH3 d1=FsrRcasLoadHx2(sp1+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee1=FsrRcasLoadHx2(sp1); + AH3 e1=ee1.rgb;pixA.g=ee1.a; + #else + AH3 e1=FsrRcasLoadHx2(sp1).rgb; + #endif + AH3 f1=FsrRcasLoadHx2(sp1+ASW2( 1, 0)).rgb; + AH3 h1=FsrRcasLoadHx2(sp1+ASW2( 0, 1)).rgb; + // Arrays of Structures to Structures of Arrays conversion. + AH2 bR=AH2(b0.r,b1.r); + AH2 bG=AH2(b0.g,b1.g); + AH2 bB=AH2(b0.b,b1.b); + AH2 dR=AH2(d0.r,d1.r); + AH2 dG=AH2(d0.g,d1.g); + AH2 dB=AH2(d0.b,d1.b); + AH2 eR=AH2(e0.r,e1.r); + AH2 eG=AH2(e0.g,e1.g); + AH2 eB=AH2(e0.b,e1.b); + AH2 fR=AH2(f0.r,f1.r); + AH2 fG=AH2(f0.g,f1.g); + AH2 fB=AH2(f0.b,f1.b); + AH2 hR=AH2(h0.r,h1.r); + AH2 hG=AH2(h0.g,h1.g); + AH2 hB=AH2(h0.b,h1.b); + // Run optional input transform. + FsrRcasInputHx2(bR,bG,bB); + FsrRcasInputHx2(dR,dG,dB); + FsrRcasInputHx2(eR,eG,eB); + FsrRcasInputHx2(fR,fG,fB); + FsrRcasInputHx2(hR,hG,hB); + // Luma times 2. + AH2 bL=bB*AH2_(0.5)+(bR*AH2_(0.5)+bG); + AH2 dL=dB*AH2_(0.5)+(dR*AH2_(0.5)+dG); + AH2 eL=eB*AH2_(0.5)+(eR*AH2_(0.5)+eG); + AH2 fL=fB*AH2_(0.5)+(fR*AH2_(0.5)+fG); + AH2 hL=hB*AH2_(0.5)+(hR*AH2_(0.5)+hG); + // Noise detection. + AH2 nz=AH2_(0.25)*bL+AH2_(0.25)*dL+AH2_(0.25)*fL+AH2_(0.25)*hL-eL; + nz=ASatH2(abs(nz)*APrxMedRcpH2(AMax3H2(AMax3H2(bL,dL,eL),fL,hL)-AMin3H2(AMin3H2(bL,dL,eL),fL,hL))); + nz=AH2_(-0.5)*nz+AH2_(1.0); + // Min and max of ring. + AH2 mn4R=min(AMin3H2(bR,dR,fR),hR); + AH2 mn4G=min(AMin3H2(bG,dG,fG),hG); + AH2 mn4B=min(AMin3H2(bB,dB,fB),hB); + AH2 mx4R=max(AMax3H2(bR,dR,fR),hR); + AH2 mx4G=max(AMax3H2(bG,dG,fG),hG); + AH2 mx4B=max(AMax3H2(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R); + AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G); + AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B); + AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y); + AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y); + AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y); + AH2 lobeR=max(-hitMinR,hitMaxR); + AH2 lobeG=max(-hitMinG,hitMaxG); + AH2 lobeB=max(-hitMinB,hitMaxB); + AH2 lobe=max(AH2_(-FSR_RCAS_LIMIT),min(AMax3H2(lobeR,lobeG,lobeB),AH2_(0.0)))*AH2_(AH2_AU1(con.y).x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH2 rcpL=APrxMedRcpH2(AH2_(4.0)*lobe+AH2_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [LFGA] LINEAR FILM GRAIN APPLICATOR +// +//------------------------------------------------------------------------------------------------------------------------------ +// Adding output-resolution film grain after scaling is a good way to mask both rendering and scaling artifacts. +// Suggest using tiled blue noise as film grain input, with peak noise frequency set for a specific look and feel. +// The 'Lfga*()' functions provide a convenient way to introduce grain. +// These functions limit grain based on distance to signal limits. +// This is done so that the grain is temporally energy preserving, and thus won't modify image tonality. +// Grain application should be done in a linear colorspace. +// The grain should be temporally changing, but have a temporal sum per pixel that adds to zero (non-biased). +//------------------------------------------------------------------------------------------------------------------------------ +// Usage, +// FsrLfga*( +// color, // In/out linear colorspace color {0 to 1} ranged. +// grain, // Per pixel grain texture value {-0.5 to 0.5} ranged, input is 3-channel to support colored grain. +// amount); // Amount of grain (0 to 1} ranged. +//------------------------------------------------------------------------------------------------------------------------------ +// Example if grain texture is monochrome: 'FsrLfgaF(color,AF3_(grain),amount)' +//============================================================================================================================== +#if defined(A_GPU) + // Maximum grain is the minimum distance to the signal limit. + void FsrLfgaF(inout AF3 c,AF3 t,AF1 a){c+=(t*AF3_(a))*min(AF3_(1.0)-c,c);} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + // Half precision version (slower). + void FsrLfgaH(inout AH3 c,AH3 t,AH1 a){c+=(t*AH3_(a))*min(AH3_(1.0)-c,c);} +//------------------------------------------------------------------------------------------------------------------------------ + // Packed half precision version (faster). + void FsrLfgaHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 tR,AH2 tG,AH2 tB,AH1 a){ + cR+=(tR*AH2_(a))*min(AH2_(1.0)-cR,cR);cG+=(tG*AH2_(a))*min(AH2_(1.0)-cG,cG);cB+=(tB*AH2_(a))*min(AH2_(1.0)-cB,cB);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [SRTM] SIMPLE REVERSIBLE TONE-MAPPER +// +//------------------------------------------------------------------------------------------------------------------------------ +// This provides a way to take linear HDR color {0 to FP16_MAX} and convert it into a temporary {0 to 1} ranged post-tonemapped linear. +// The tonemapper preserves RGB ratio, which helps maintain HDR color bleed during filtering. +//------------------------------------------------------------------------------------------------------------------------------ +// Reversible tonemapper usage, +// FsrSrtm*(color); // {0 to FP16_MAX} converted to {0 to 1}. +// FsrSrtmInv*(color); // {0 to 1} converted into {0 to 32768, output peak safe for FP16}. +//============================================================================================================================== +#if defined(A_GPU) + void FsrSrtmF(inout AF3 c){c*=AF3_(ARcpF1(AMax3F1(c.r,c.g,c.b)+AF1_(1.0)));} + // The extra max solves the c=1.0 case (which is a /0). + void FsrSrtmInvF(inout AF3 c){c*=AF3_(ARcpF1(max(AF1_(1.0/32768.0),AF1_(1.0)-AMax3F1(c.r,c.g,c.b))));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + void FsrSrtmH(inout AH3 c){c*=AH3_(ARcpH1(AMax3H1(c.r,c.g,c.b)+AH1_(1.0)));} + void FsrSrtmInvH(inout AH3 c){c*=AH3_(ARcpH1(max(AH1_(1.0/32768.0),AH1_(1.0)-AMax3H1(c.r,c.g,c.b))));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrSrtmHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(AMax3H2(cR,cG,cB)+AH2_(1.0));cR*=rcp;cG*=rcp;cB*=rcp;} + void FsrSrtmInvHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(max(AH2_(1.0/32768.0),AH2_(1.0)-AMax3H2(cR,cG,cB)));cR*=rcp;cG*=rcp;cB*=rcp;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [TEPD] TEMPORAL ENERGY PRESERVING DITHER +// +//------------------------------------------------------------------------------------------------------------------------------ +// Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// Gamma 2.0 is used so that the conversion back to linear is just to square the color. +// The conversion comes in 8-bit and 10-bit modes, designed for output to 8-bit UNORM or 10:10:10:2 respectively. +// Given good non-biased temporal blue noise as dither input, +// the output dither will temporally conserve energy. +// This is done by choosing the linear nearest step point instead of perceptual nearest. +// See code below for details. +//------------------------------------------------------------------------------------------------------------------------------ +// DX SPEC RULES FOR FLOAT->UNORM 8-BIT CONVERSION +// =============================================== +// - Output is 'uint(floor(saturate(n)*255.0+0.5))'. +// - Thus rounding is to nearest. +// - NaN gets converted to zero. +// - INF is clamped to {0.0 to 1.0}. +//============================================================================================================================== +#if defined(A_GPU) + // Hand tuned integer position to dither value, with more values than simple checkerboard. + // Only 32-bit has enough precision for this compddation. + // Output is {0 to <1}. + AF1 FsrTepdDitF(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + // The 1.61803 golden ratio. + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + // Number designed to provide a good visual pattern. + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AFractF1(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 8-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC8F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(255.0))*AF3_(1.0/255.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/255.0);b=b*b; + // Ratio of 'a' to 'b' required to produce 'c'. + // APrxLoRcpF1() won't work here (at least for very high dynamic ranges). + // APrxMedRcpF1() is an IADD,FMA,MUL. + AF3 r=(c-b)*APrxMedRcpF3(a-b); + // Use the ratio as a cutoff to choose 'a' or 'b'. + // AGtZeroF1() is a MUL. + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 10-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC10F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(1023.0))*AF3_(1.0/1023.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/1023.0);b=b*b; + AF3 r=(c-b)*APrxMedRcpF3(a-b); + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/1023.0));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + AH1 FsrTepdDitH(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AH1(AFractF1(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(255.0))*AH3_(1.0/255.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/255.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(1023.0))*AH3_(1.0/1023.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/1023.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/1023.0));} +//============================================================================================================================== + // This computes dither for positions 'p' and 'p+{8,0}'. + AH2 FsrTepdDitHx2(AU2 p,AU1 f){ + AF2 x; + x.x=AF1_(p.x+f); + x.y=x.x+AF1_(8.0); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*AF2_(a)+AF2_(y*b); + return AH2(AFractF2(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(255.0))*AH2_(1.0/255.0); + nG=floor(nG*AH2_(255.0))*AH2_(1.0/255.0); + nB=floor(nB*AH2_(255.0))*AH2_(1.0/255.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/255.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/255.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/255.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/255.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/255.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(1023.0))*AH2_(1.0/1023.0); + nG=floor(nG*AH2_(1023.0))*AH2_(1.0/1023.0); + nB=floor(nB*AH2_(1023.0))*AH2_(1.0/1023.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/1023.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/1023.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/1023.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/1023.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/1023.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/1023.0));} +#endif + + +void CurrFilter(AU2 pos) +{ + AF3 c; + FsrRcasF(c.r, c.g, c.b, pos, con0); + imageStore(imgOutput, ASU2(pos), AF4(c, 1)); +} + +void main() { + FsrRcasCon(con0, sharpening_data); + + AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); + CurrFilter(gxy); + gxy.x += 8u; + CurrFilter(gxy); + gxy.y += 8u; + CurrFilter(gxy); + gxy.x -= 8u; + CurrFilter(gxy); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv b/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv new file mode 100644 index 0000000000000000000000000000000000000000..b2e30e1fe82d1a17c9b837a184d99a88f1c112bb GIT binary patch literal 20472 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl b/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl new file mode 100644 index 000000000..f197c64ca --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl @@ -0,0 +1,1177 @@ +/*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + + +------------------------------------------------------------------------------ +COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. +------------------------------------------------------------------------------ +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA +OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR +LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, +OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE +THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +------------------------------------------------------------------------------ + INTEGRATION CHECKLIST +------------------------------------------------------------------------------ +(1.) +In the shader source, setup defines for the desired configuration. +When providing multiple shaders (for different presets), +simply setup the defines differently in multiple files. +Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY_PRESET 12 + +Or, + + #define FXAA_360 1 + +Or, + + #define FXAA_PS3 1 + +Etc. + +(2.) +Then include this file, + + #include "Fxaa3_11.h" + +(3.) +Then call the FXAA pixel shader from within your desired shader. +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. +As for FXAA 3.11 all inputs for all shaders are the same +to enable easy porting between platforms. + + return FxaaPixelShader(...); + +(4.) +Insure pass prior to FXAA outputs RGBL (see next section). +Or use, + + #define FXAA_GREEN_AS_LUMA 1 + +(5.) +Setup engine to provide the following constants +which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + +(6.) +Have FXAA vertex shader run as a full screen triangle, +and output "pos" and "fxaaConsolePosPos" +such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy_} = upper left of pixel + // {_zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + +(7.) +Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + +------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE +------------------------------------------------------------------------------ +FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + +In which case the engine uses green in place of luma, +and requires RGB input is in a non-linear colorspace. + +RGB should be LDR (low dynamic range). +Specifically do FXAA after tonemapping. + +RGB data as returned by a texture fetch can be non-linear, +or linear when FXAA_GREEN_AS_LUMA is not set. +Note an "sRGB format" texture counts as linear, +because the result of a texture fetch is linear data. +Regular "RGBA8" textures in the sRGB colorspace are non-linear. + +If FXAA_GREEN_AS_LUMA is not set, +luma must be stored in the alpha channel prior to running FXAA. +This luma should be in a perceptual space (could be gamma 2.0). +Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + +Another example where output is linear encoded, +say for instance writing to an sRGB formated render target, +where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + +Getting luma correct is required for the algorithm to work correctly. + + +------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? +------------------------------------------------------------------------------ +Applying FXAA to a framebuffer with linear RGB color will look worse. +This is very counter intuitive, but happends to be true in this case. +The reason is because dithering artifacts will be more visiable +in a linear colorspace. + + +------------------------------------------------------------------------------ + COMPLEX INTEGRATION +------------------------------------------------------------------------------ +Q. What if the engine is blending into RGB before wanting to run FXAA? + +A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + +A. Or use FXAA_GREEN_AS_LUMA. + +============================================================================*/ + +#version 430 core + +layout(local_size_x = 16, local_size_y = 16) in; +layout(rgba8, binding = 0, set = 3) uniform image2D imgOutput; + +layout(binding = 1, set = 2) uniform sampler2D inputImage; +layout(binding = 2) uniform invResolution +{ + vec2 invResolution_data; +}; + +#define FXAA_QUALITY_PRESET 12 +#define FXAA_GREEN_AS_LUMA 1 +#define FXAA_PC 1 +#define FXAA_GLSL_130 1 + + +/*============================================================================ + + INTEGRATION KNOBS + +/*==========================================================================*/ +#ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY_PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY_PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY_PRESET == 10) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 3.0 + #define FXAA_QUALITY_P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 11) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 3.0 + #define FXAA_QUALITY_P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 12) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 4.0 + #define FXAA_QUALITY_P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 13) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 4.0 + #define FXAA_QUALITY_P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 14) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 4.0 + #define FXAA_QUALITY_P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 15) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY_PRESET == 20) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 2.0 + #define FXAA_QUALITY_P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 21) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 22) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 23) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 24) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 3.0 + #define FXAA_QUALITY_P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 25) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 26) + #define FXAA_QUALITY_PS 9 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 4.0 + #define FXAA_QUALITY_P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 27) + #define FXAA_QUALITY_PS 10 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 4.0 + #define FXAA_QUALITY_P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 28) + #define FXAA_QUALITY_PS 11 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 4.0 + #define FXAA_QUALITY_P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY_PRESET == 29) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY_PRESET == 39) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.0 + #define FXAA_QUALITY_P2 1.0 + #define FXAA_QUALITY_P3 1.0 + #define FXAA_QUALITY_P4 1.0 + #define FXAA_QUALITY_P5 1.5 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy_} = upper left of pixel + // {_zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {__a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x__} = -N/screenWidthInPixels + // {_y_} = -N/screenHeightInPixels + // {_z_} = N/screenWidthInPixels + // {__w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x__} = -2.0/screenWidthInPixels + // {_y_} = -2.0/screenHeightInPixels + // {_z_} = 2.0/screenWidthInPixels + // {__w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x__} = 8.0/screenWidthInPixels + // {_y_} = 8.0/screenHeightInPixels + // {_z_} = -4.0/screenWidthInPixels + // {__w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY_SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY_EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY_EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE_EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE_PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE_PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY_P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY_P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY_P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY_P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/*==========================================================================*/ +#endif + +vec4 mainImage(vec2 fragCoord) +{ + vec2 rcpFrame = 1./invResolution_data.xy; + vec2 uv2 = fragCoord.xy / invResolution_data.xy; + + float fxaaQualitySubpix = 0.75; // [0..1], default 0.75 + float fxaaQualityEdgeThreshold = 0.166; // [0.125..0.33], default 0.166 + float fxaaQualityEdgeThresholdMin = 0.02;//0.0625; // ? + vec4 dummy4 = vec4(0.0,0.0,0.0,0.0); + float dummy1 = 0.0; + + vec4 col = FxaaPixelShader(uv2, dummy4, + inputImage, inputImage, inputImage, + rcpFrame, dummy4, dummy4, dummy4, + fxaaQualitySubpix, fxaaQualityEdgeThreshold, + fxaaQualityEdgeThresholdMin, + dummy1, dummy1, dummy1, dummy4); + + vec4 fragColor = vec4( col.xyz, 1. ); + + return fragColor; +} + +void main() +{ + ivec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec4 outColor = mainImage(texelCoord + vec2(0.5)); + imageStore(imgOutput, texelCoord, outColor); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv b/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv new file mode 100644 index 0000000000000000000000000000000000000000..b466bcb659d56d910d30aac5d781d27b8ff1313f GIT binary patch literal 25012 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl new file mode 100644 index 000000000..a518cf25e --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl @@ -0,0 +1,1404 @@ +#version 430 core +#define SMAA_GLSL_4 1 + +layout (constant_id = 0) const int SMAA_PRESET_LOW = 0; +layout (constant_id = 1) const int SMAA_PRESET_MEDIUM = 0; +layout (constant_id = 2) const int SMAA_PRESET_HIGH = 0; +layout (constant_id = 3) const int SMAA_PRESET_ULTRA = 0; +layout (constant_id = 4) const float METRIC_WIDTH = 1920.0; +layout (constant_id = 5) const float METRIC_HEIGHT = 1080.0; + +#define SMAA_RT_METRICS float4(1.0 / METRIC_WIDTH, 1.0 / METRIC_HEIGHT, METRIC_WIDTH, METRIC_HEIGHT) + +layout (local_size_x = 16, local_size_y = 16) in; +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------ + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------ + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS<float4, 2> tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS + +layout(rgba8, binding = 0, set = 3) uniform image2D imgOutput; + +layout(binding = 1, set = 2) uniform sampler2D inputImg; +layout(binding = 3, set = 2) uniform sampler2D samplerArea; +layout(binding = 4, set = 2) uniform sampler2D samplerSearch; +layout( binding = 2 ) uniform invResolution +{ + vec2 invResolution_data; +}; + +void main() { + ivec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec2 coord = (texelCoord + vec2(0.5)) / invResolution_data; + vec2 pixCoord; + vec4 offset[3]; + + SMAABlendingWeightCalculationVS( coord, pixCoord, offset); + + vec4 oColor = SMAABlendingWeightCalculationPS(coord, pixCoord, offset, inputImg, samplerArea, samplerSearch, ivec4(0)); + + imageStore(imgOutput, texelCoord, oColor); + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv new file mode 100644 index 0000000000000000000000000000000000000000..8efa011f77f3c49ed762e7465ae685ebd01b8348 GIT binary patch literal 33728 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl new file mode 100644 index 000000000..668b97d5d --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl @@ -0,0 +1,1402 @@ +#version 430 core +#define SMAA_GLSL_4 1 + +layout (constant_id = 0) const int SMAA_PRESET_LOW = 0; +layout (constant_id = 1) const int SMAA_PRESET_MEDIUM = 0; +layout (constant_id = 2) const int SMAA_PRESET_HIGH = 0; +layout (constant_id = 3) const int SMAA_PRESET_ULTRA = 0; +layout (constant_id = 4) const float METRIC_WIDTH = 1920.0; +layout (constant_id = 5) const float METRIC_HEIGHT = 1080.0; + +#define SMAA_RT_METRICS float4(1.0 / METRIC_WIDTH, 1.0 / METRIC_HEIGHT, METRIC_WIDTH, METRIC_HEIGHT) + +layout (local_size_x = 16, local_size_y = 16) in; +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------ + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------ + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS<float4, 2> tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS + +layout(rgba8, binding = 0, set = 3) uniform image2D imgOutput; + +layout(binding = 1, set = 2) uniform sampler2D inputImg; +layout( binding = 2 ) uniform invResolution +{ + vec2 invResolution_data; +}; + +void main() +{ + vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec2 coord = (texelCoord + vec2(0.5)) / invResolution_data; + vec4 offset[3]; + SMAAEdgeDetectionVS(coord, offset); + vec2 oColor = SMAAColorEdgeDetectionPS(coord, offset, inputImg); + if (oColor != float2(-2.0, -2.0)) + { + imageStore(imgOutput, texelCoord, vec4(oColor, 0.0, 1.0)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv new file mode 100644 index 0000000000000000000000000000000000000000..1062a9e3abd0624aec557d24c06e1a161dec6874 GIT binary patch literal 8464 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl new file mode 100644 index 000000000..df30d727b --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl @@ -0,0 +1,1403 @@ +#version 430 core +#define SMAA_GLSL_4 1 + +layout (constant_id = 0) const int SMAA_PRESET_LOW = 0; +layout (constant_id = 1) const int SMAA_PRESET_MEDIUM = 0; +layout (constant_id = 2) const int SMAA_PRESET_HIGH = 0; +layout (constant_id = 3) const int SMAA_PRESET_ULTRA = 0; +layout (constant_id = 4) const float METRIC_WIDTH = 1920.0; +layout (constant_id = 5) const float METRIC_HEIGHT = 1080.0; + +#define SMAA_RT_METRICS float4(1.0 / METRIC_WIDTH, 1.0 / METRIC_HEIGHT, METRIC_WIDTH, METRIC_HEIGHT) + +layout (local_size_x = 16, local_size_y = 16) in; +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------ + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------ + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS<float4, 2> tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + return float2(-2.0, -2.0); + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS + +layout(rgba8, binding = 0, set = 3) uniform image2D imgOutput; + +layout(binding = 1, set = 2) uniform sampler2D inputImg; +layout(binding = 3, set = 2) uniform sampler2D samplerBlend; +layout( binding = 2 ) uniform invResolution +{ + vec2 invResolution_data; +}; + +void main() { + vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4); + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + ivec2 texelCoord = ivec2(loc.x + i, loc.y + j); + vec2 coord = (texelCoord + vec2(0.5)) / invResolution_data; + vec2 pixCoord; + vec4 offset; + + SMAANeighborhoodBlendingVS(coord, offset); + + vec4 oColor = SMAANeighborhoodBlendingPS(coord, offset, inputImg, samplerBlend); + + imageStore(imgOutput, texelCoord, oColor); + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv b/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv new file mode 100644 index 0000000000000000000000000000000000000000..fa0208f25069dbd07bff6133f52792e1e769f681 GIT binary patch literal 8328 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs b/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs new file mode 100644 index 000000000..a5f060f1b --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan.Effects +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + internal struct SmaaConstants + { + public int QualityLow; + public int QualityMedium; + public int QualityHigh; + public int QualityUltra; + public float Width; + public float Height; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs new file mode 100644 index 000000000..4dcdaa646 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -0,0 +1,314 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using Silk.NET.Vulkan; +using System; +using Format = Ryujinx.Graphics.GAL.Format; + +namespace Ryujinx.Graphics.Vulkan.Effects +{ + internal partial class SmaaPostProcessingEffect : IPostProcessingEffect + { + public const int AreaWidth = 160; + public const int AreaHeight = 560; + public const int SearchWidth = 64; + public const int SearchHeight = 16; + + private readonly VulkanRenderer _renderer; + private ISampler _samplerLinear; + private SmaaConstants _specConstants; + private ShaderCollection _edgeProgram; + private ShaderCollection _blendProgram; + private ShaderCollection _neighbourProgram; + + private PipelineHelperShader _pipeline; + + private TextureView _outputTexture; + private TextureView _edgeOutputTexture; + private TextureView _blendOutputTexture; + private TextureView _areaTexture; + private TextureView _searchTexture; + private Device _device; + private bool _recreatePipelines; + private int _quality; + + public SmaaPostProcessingEffect(VulkanRenderer renderer, Device device, int quality) + { + _device = device; + _renderer = renderer; + _quality = quality; + + Initialize(); + } + + public int Quality + { + get => _quality; + set + { + _quality = value; + + _recreatePipelines = true; + } + } + + public void Dispose() + { + DeletePipelines(); + _samplerLinear?.Dispose(); + _outputTexture?.Dispose(); + _edgeOutputTexture?.Dispose(); + _blendOutputTexture?.Dispose(); + _areaTexture?.Dispose(); + _searchTexture?.Dispose(); + } + + private unsafe void RecreateShaders(int width, int height) + { + _recreatePipelines = false; + + DeletePipelines(); + _pipeline = new PipelineHelperShader(_renderer, _device); + + _pipeline.Initialize(); + + var edgeShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv"); + var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv"); + var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv"); + + var edgeBindings = new ShaderBindings( + new[] { 2 }, + Array.Empty<int>(), + new[] { 1 }, + new[] { 0 }); + + var blendBindings = new ShaderBindings( + new[] { 2 }, + Array.Empty<int>(), + new[] { 1, 3, 4 }, + new[] { 0 }); + + var neighbourBindings = new ShaderBindings( + new[] { 2 }, + Array.Empty<int>(), + new[] { 1, 3 }, + new[] { 0 }); + + _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + + _specConstants = new SmaaConstants() + { + Width = width, + Height = height, + QualityLow = Quality == 0 ? 1 : 0, + QualityMedium = Quality == 1 ? 1 : 0, + QualityHigh = Quality == 2 ? 1 : 0, + QualityUltra = Quality == 3 ? 1 : 0, + }; + + var specInfo = new SpecDescription( + (0, SpecConstType.Int32), + (1, SpecConstType.Int32), + (2, SpecConstType.Int32), + (3, SpecConstType.Int32), + (4, SpecConstType.Float32), + (5, SpecConstType.Float32)); + + _edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(edgeShader, edgeBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }, new[] { specInfo }); + + _blendProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(blendShader, blendBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }, new[] { specInfo }); + + _neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(neighbourShader, neighbourBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }, new[] { specInfo }); + } + + public void DeletePipelines() + { + _pipeline?.Dispose(); + _edgeProgram?.Dispose(); + _blendProgram?.Dispose(); + _neighbourProgram?.Dispose(); + } + + private void Initialize() + { + var areaInfo = new TextureCreateInfo(AreaWidth, + AreaHeight, + 1, + 1, + 1, + 1, + 1, + 1, + Format.R8G8Unorm, + DepthStencilMode.Depth, + Target.Texture2D, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha); + + var searchInfo = new TextureCreateInfo(SearchWidth, + SearchHeight, + 1, + 1, + 1, + 1, + 1, + 1, + Format.R8Unorm, + DepthStencilMode.Depth, + Target.Texture2D, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha); + + var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin"); + var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin"); + + _areaTexture = _renderer.CreateTexture(areaInfo, 1) as TextureView; + _searchTexture = _renderer.CreateTexture(searchInfo, 1) as TextureView; + + _areaTexture.SetData(areaTexture); + _searchTexture.SetData(searchTexture); + } + + public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height) + { + if (_recreatePipelines || _outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height) + { + RecreateShaders(view.Width, view.Height); + _outputTexture?.Dispose(); + _edgeOutputTexture?.Dispose(); + _blendOutputTexture?.Dispose(); + + var info = view.Info; + + if (view.Info.Format.IsBgr()) + { + info = new TextureCreateInfo(info.Width, + info.Height, + info.Depth, + info.Levels, + info.Samples, + info.BlockWidth, + info.BlockHeight, + info.BytesPerPixel, + info.Format, + info.DepthStencilMode, + info.Target, + info.SwizzleB, + info.SwizzleG, + info.SwizzleR, + info.SwizzleA); + } + + _outputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + _edgeOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + _blendOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + } + + Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + + viewports[0] = new GAL.Viewport( + new Rectangle<float>(0, 0, view.Width, view.Height), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; + + scissors[0] = new Rectangle<int>(0, 0, view.Width, view.Height); + + _renderer.HelperShader.Clear(_renderer, + _edgeOutputTexture.GetImageView(), + new float[] { 0, 0, 0, 1 }, + (uint)(ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit), + view.Width, + view.Height, + _edgeOutputTexture.VkFormat, + ComponentType.UnsignedInteger, + scissors[0]); + + _renderer.HelperShader.Clear(_renderer, + _blendOutputTexture.GetImageView(), + new float[] { 0, 0, 0, 1 }, + (uint)(ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit), + view.Width, + view.Height, + _blendOutputTexture.VkFormat, + ComponentType.UnsignedInteger, + scissors[0]); + + _renderer.Pipeline.TextureBarrier(); + + var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); + var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); + + // Edge pass + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_edgeProgram); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); + _pipeline.Specialize(_specConstants); + + ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height }; + int rangeSize = resolutionBuffer.Length * sizeof(float); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false); + + _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); + var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetImage(0, _edgeOutputTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.ComputeBarrier(); + + // Blend pass + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_blendProgram); + _pipeline.Specialize(_specConstants); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetImage(0, _blendOutputTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.ComputeBarrier(); + + // Neighbour pass + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_neighbourProgram); + _pipeline.Specialize(_specConstants); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetImage(0, _outputTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.ComputeBarrier(); + + _pipeline.Finish(); + + _renderer.BufferManager.Delete(bufferHandle); + + return _outputTexture; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin b/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin new file mode 100644 index 0000000000000000000000000000000000000000..f4a7a1b417766c12bbac4e4bdc56796f18538bd6 GIT binary patch literal 179200 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μw<5_SoZr#d3FkR_X{|a|ppP>)<F?`p3c>nXr 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin b/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin new file mode 100644 index 0000000000000000000000000000000000000000..db5bf73f7d5a0b5e436d336849c90bfbc24d76dc GIT binary patch literal 1024 zcmezOkD<Pvf#Dy7Vt@dk2uKi2{R1+90K~@zpc={6kIhU{#3;3&QvIa3l@@A|qY7?5 evLH0#aK#_8QgZae^^nP+)P73!lj-bXqYVIqI9W{q literal 0 HcmV?d00001 diff --git a/Ryujinx.Graphics.Vulkan/NativeArray.cs b/Ryujinx.Graphics.Vulkan/NativeArray.cs index f74074390..3a8512874 100644 --- a/Ryujinx.Graphics.Vulkan/NativeArray.cs +++ b/Ryujinx.Graphics.Vulkan/NativeArray.cs @@ -38,8 +38,11 @@ namespace Ryujinx.Graphics.Vulkan public void Dispose() { - Marshal.FreeHGlobal((IntPtr)Pointer); - Pointer = null; + if (Pointer != null) + { + Marshal.FreeHGlobal((IntPtr)Pointer); + Pointer = null; + } } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index f779305db..583bb9539 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -150,6 +150,28 @@ namespace Ryujinx.Graphics.Vulkan null); } + public void ComputeBarrier() + { + MemoryBarrier memoryBarrier = new MemoryBarrier() + { + SType = StructureType.MemoryBarrier, + SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit + }; + + Gd.Api.CmdPipelineBarrier( + CommandBuffer, + PipelineStageFlags.ComputeShaderBit, + PipelineStageFlags.AllCommandsBit, + 0, + 1, + new ReadOnlySpan<MemoryBarrier>(memoryBarrier), + 0, + ReadOnlySpan<BufferMemoryBarrier>.Empty, + 0, + ReadOnlySpan<ImageMemoryBarrier>.Empty); + } + public void BeginTransformFeedback(GAL.PrimitiveTopology topology) { _tfEnabled = true; @@ -803,6 +825,11 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSetUpdater.SetImage(binding, image, imageFormat); } + public void SetImage(int binding, Auto<DisposableImageView> image) + { + _descriptorSetUpdater.SetImage(binding, image); + } + public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type) { if (buffer.Handle != BufferHandle.Null) diff --git a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index 87f14a6ab..57e2240a7 100644 --- a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -12,6 +12,17 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> + <ItemGroup> + <EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" /> + <EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" /> + <EmbeddedResource Include="Effects\Shaders\FsrScaling.spv" /> + <EmbeddedResource Include="Effects\Shaders\FsrSharpening.spv" /> + <EmbeddedResource Include="Effects\Shaders\Fxaa.spv" /> + <EmbeddedResource Include="Effects\Shaders\SmaaBlend.spv" /> + <EmbeddedResource Include="Effects\Shaders\SmaaEdge.spv" /> + <EmbeddedResource Include="Effects\Shaders\SmaaNeighbour.spv" /> + </ItemGroup> + <ItemGroup> <PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" /> <PackageReference Include="shaderc.net" /> diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index a90a824df..5d6def3a9 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Vulkan.Effects; using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.KHR; using System; @@ -29,6 +30,14 @@ namespace Ryujinx.Graphics.Vulkan private bool _vsyncEnabled; private bool _vsyncModeChanged; private VkFormat _format; + private AntiAliasing _currentAntiAliasing; + private bool _updateEffect; + private IPostProcessingEffect _effect; + private IScalingFilter _scalingFilter; + private bool _isLinear; + private float _scalingFilterLevel; + private bool _updateScalingFilter; + private ScalingFilter _currentScalingFilter; public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device) { @@ -116,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan ImageFormat = surfaceFormat.Format, ImageColorSpace = surfaceFormat.ColorSpace, ImageExtent = extent, - ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit, + ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit | ImageUsageFlags.StorageBit, ImageSharingMode = SharingMode.Exclusive, ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, @@ -280,6 +289,13 @@ namespace Ryujinx.Graphics.Vulkan var view = (TextureView)texture; + UpdateEffect(); + + if (_effect != null) + { + view = _effect.Run(view, cbs, _width, _height); + } + int srcX0, srcX1, srcY0, srcY1; float scale = view.ScaleFactor; @@ -315,6 +331,18 @@ namespace Ryujinx.Graphics.Vulkan if (ScreenCaptureRequested) { + if (_effect != null) + { + _gd.CommandBufferPool.Return( + cbs, + null, + stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit }, + null); + _gd.FlushAllCommands(); + cbs.GetFence().Wait(); + cbs = _gd.CommandBufferPool.Rent(); + } + CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY); ScreenCaptureRequested = false; @@ -335,20 +363,36 @@ namespace Ryujinx.Graphics.Vulkan int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; - _gd.HelperShader.BlitColor( - _gd, - cbs, - view, - _swapchainImageViews[nextImage], - _width, - _height, - 1, - _format, - false, - new Extents2D(srcX0, srcY0, srcX1, srcY1), - new Extents2D(dstX0, dstY1, dstX1, dstY0), - true, - true); + if (_scalingFilter != null) + { + _scalingFilter.Run( + view, + cbs, + _swapchainImageViews[nextImage], + _format, + _width, + _height, + new Extents2D(srcX0, srcY0, srcX1, srcY1), + new Extents2D(dstX0, dstY0, dstX1, dstY1) + ); + } + else + { + _gd.HelperShader.BlitColor( + _gd, + cbs, + view, + _swapchainImageViews[nextImage], + _width, + _height, + 1, + _format, + false, + new Extents2D(srcX0, srcY0, srcX1, srcY1), + new Extents2D(dstX0, dstY1, dstX1, dstY0), + _isLinear, + true); + } Transition( cbs.CommandBuffer, @@ -387,6 +431,95 @@ namespace Ryujinx.Graphics.Vulkan } } + public override void SetAntiAliasing(AntiAliasing effect) + { + if (_currentAntiAliasing == effect && _effect != null) + { + return; + } + + _currentAntiAliasing = effect; + + _updateEffect = true; + } + + public override void SetScalingFilter(ScalingFilter type) + { + if (_currentScalingFilter == type && _effect != null) + { + return; + } + + _currentScalingFilter = type; + + _updateScalingFilter = true; + } + + private void UpdateEffect() + { + if (_updateEffect) + { + _updateEffect = false; + + switch (_currentAntiAliasing) + { + case AntiAliasing.Fxaa: + _effect?.Dispose(); + _effect = new FxaaPostProcessingEffect(_gd, _device); + 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; + if (_effect is SmaaPostProcessingEffect smaa) + { + smaa.Quality = quality; + } + else + { + _effect?.Dispose(); + _effect = new SmaaPostProcessingEffect(_gd, _device, quality); + } + 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: + if (_scalingFilter is not FsrScalingFilter) + { + _scalingFilter?.Dispose(); + _scalingFilter = new FsrScalingFilter(_gd, _device); + } + + _scalingFilter.Level = _scalingFilterLevel; + break; + } + } + } + + public override void SetScalingFilterLevel(float level) + { + _scalingFilterLevel = level; + _updateScalingFilter = true; + } + private unsafe void Transition( CommandBuffer commandBuffer, Image image, @@ -456,8 +589,10 @@ namespace Ryujinx.Graphics.Vulkan } _gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null); - } + + _effect?.Dispose(); + _scalingFilter?.Dispose(); } } diff --git a/Ryujinx.Graphics.Vulkan/WindowBase.cs b/Ryujinx.Graphics.Vulkan/WindowBase.cs index 651fe7c16..0a365e8fb 100644 --- a/Ryujinx.Graphics.Vulkan/WindowBase.cs +++ b/Ryujinx.Graphics.Vulkan/WindowBase.cs @@ -11,5 +11,8 @@ namespace Ryujinx.Graphics.Vulkan public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); public abstract void SetSize(int width, int height); public abstract void ChangeVSyncMode(bool vsyncEnabled); + public abstract void SetAntiAliasing(AntiAliasing effect); + public abstract void SetScalingFilter(ScalingFilter scalerType); + public abstract void SetScalingFilterLevel(float scale); } } \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 226b5933b..e9aec04b2 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <summary> /// The current version of the file format /// </summary> - public const int CurrentVersion = 43; + public const int CurrentVersion = 44; /// <summary> /// Version of the configuration file format @@ -51,6 +51,21 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public AspectRatio AspectRatio { get; set; } + /// <summary> + /// Applies anti-aliasing to the renderer. + /// </summary> + public AntiAliasing AntiAliasing { get; set; } + + /// <summary> + /// Sets the framebuffer upscaling type. + /// </summary> + public ScalingFilter ScalingFilter { get; set; } + + /// <summary> + /// Sets the framebuffer upscaling level. + /// </summary> + public int ScalingFilterLevel { get; set; } + /// <summary> /// Dumps shaders in this local directory /// </summary> diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index f193b1570..bcdd2e70a 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -433,6 +433,21 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public ReactiveObject<GraphicsBackend> GraphicsBackend { get; private set; } + /// <summary> + /// Applies anti-aliasing to the renderer. + /// </summary> + public ReactiveObject<AntiAliasing> AntiAliasing { get; private set; } + + /// <summary> + /// Sets the framebuffer upscaling type. + /// </summary> + public ReactiveObject<ScalingFilter> ScalingFilter { get; private set; } + + /// <summary> + /// Sets the framebuffer upscaling level. + /// </summary> + public ReactiveObject<int> ScalingFilterLevel { get; private set; } + /// <summary> /// Preferred GPU /// </summary> @@ -463,6 +478,12 @@ namespace Ryujinx.Ui.Common.Configuration PreferredGpu.Event += static (sender, e) => LogValueChange(sender, e, nameof(PreferredGpu)); EnableMacroHLE = new ReactiveObject<bool>(); EnableMacroHLE.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableMacroHLE)); + AntiAliasing = new ReactiveObject<AntiAliasing>(); + AntiAliasing.Event += static (sender, e) => LogValueChange(sender, e, nameof(AntiAliasing)); + ScalingFilter = new ReactiveObject<ScalingFilter>(); + ScalingFilter.Event += static (sender, e) => LogValueChange(sender, e, nameof(ScalingFilter)); + ScalingFilterLevel = new ReactiveObject<int>(); + ScalingFilterLevel.Event += static (sender, e) => LogValueChange(sender, e, nameof(ScalingFilterLevel)); } } @@ -540,6 +561,9 @@ namespace Ryujinx.Ui.Common.Configuration ResScaleCustom = Graphics.ResScaleCustom, MaxAnisotropy = Graphics.MaxAnisotropy, AspectRatio = Graphics.AspectRatio, + AntiAliasing = Graphics.AntiAliasing, + ScalingFilter = Graphics.ScalingFilter, + ScalingFilterLevel = Graphics.ScalingFilterLevel, GraphicsShadersDumpPath = Graphics.ShadersDumpPath, LoggingEnableDebug = Logger.EnableDebug, LoggingEnableStub = Logger.EnableStub, @@ -651,6 +675,9 @@ namespace Ryujinx.Ui.Common.Configuration Graphics.EnableShaderCache.Value = true; Graphics.EnableTextureRecompression.Value = false; Graphics.EnableMacroHLE.Value = true; + Graphics.AntiAliasing.Value = AntiAliasing.None; + Graphics.ScalingFilter.Value = ScalingFilter.Bilinear; + Graphics.ScalingFilterLevel.Value = 80; System.EnablePtc.Value = true; System.EnableInternetAccess.Value = false; System.EnableFsIntegrityChecks.Value = true; @@ -1208,6 +1235,17 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.UseHypervisor = true; } + if (configurationFileFormat.Version < 44) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42."); + + configurationFileFormat.AntiAliasing = AntiAliasing.None; + configurationFileFormat.ScalingFilter = ScalingFilter.Bilinear; + configurationFileFormat.ScalingFilterLevel = 80; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1217,6 +1255,9 @@ namespace Ryujinx.Ui.Common.Configuration Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading; Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend; Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu; + Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing; + Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter; + Graphics.ScalingFilterLevel.Value = configurationFileFormat.ScalingFilterLevel; Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug; Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub; Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo; diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 4bf2a70ff..957bbcd55 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Ui using Image = SixLabors.ImageSharp.Image; using Key = Input.Key; using Switch = HLE.Switch; + using ScalingFilter = Graphics.GAL.ScalingFilter; public abstract class RendererWidgetBase : DrawingArea { @@ -116,6 +117,21 @@ namespace Ryujinx.Ui _lastCursorMoveTime = Stopwatch.GetTimestamp(); ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged; + ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAnriAliasing; + ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; + } + + private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e) + { + Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + } + + private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e) + { + Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); } public abstract void InitializeRenderer(); @@ -149,11 +165,19 @@ namespace Ryujinx.Ui private void Renderer_Destroyed(object sender, EventArgs e) { ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged; + ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAnriAliasing; + ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; NpadManager.Dispose(); Dispose(); } + private void UpdateAnriAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e) + { + Renderer?.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); + } + protected override bool OnMotionNotifyEvent(EventMotion evnt) { if (_hideCursorOnIdle) @@ -394,6 +418,10 @@ namespace Ryujinx.Ui Device.Gpu.Renderer.Initialize(_glLogLevel); + Renderer.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); + Renderer.Window.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); + Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + _gpuBackendName = GetGpuBackendName(); _gpuVendorName = GetGpuVendorName(); diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs index 220bb82ae..61af7d397 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -95,10 +95,14 @@ namespace Ryujinx.Ui.Windows [GUI] Entry _graphicsShadersDumpPath; [GUI] ComboBoxText _anisotropy; [GUI] ComboBoxText _aspectRatio; + [GUI] ComboBoxText _antiAliasing; + [GUI] ComboBoxText _scalingFilter; [GUI] ComboBoxText _graphicsBackend; [GUI] ComboBoxText _preferredGpu; [GUI] ComboBoxText _resScaleCombo; [GUI] Entry _resScaleText; + [GUI] Adjustment _scalingFilterLevel; + [GUI] Scale _scalingFilterSlider; [GUI] ToggleButton _configureController1; [GUI] ToggleButton _configureController2; [GUI] ToggleButton _configureController3; @@ -139,6 +143,7 @@ namespace Ryujinx.Ui.Windows _systemTimeZoneEntry.FocusOutEvent += TimeZoneEntry_FocusOut; _resScaleCombo.Changed += (sender, args) => _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; + _scalingFilter.Changed += (sender, args) => _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; _galThreading.Changed += (sender, args) => { if (_galThreading.ActiveId != ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()) @@ -338,6 +343,8 @@ namespace Ryujinx.Ui.Windows _anisotropy.SetActiveId(ConfigurationState.Instance.Graphics.MaxAnisotropy.Value.ToString()); _aspectRatio.SetActiveId(((int)ConfigurationState.Instance.Graphics.AspectRatio.Value).ToString()); _graphicsBackend.SetActiveId(((int)ConfigurationState.Instance.Graphics.GraphicsBackend.Value).ToString()); + _antiAliasing.SetActiveId(((int)ConfigurationState.Instance.Graphics.AntiAliasing.Value).ToString()); + _scalingFilter.SetActiveId(((int)ConfigurationState.Instance.Graphics.ScalingFilter.Value).ToString()); UpdatePreferredGpuComboBox(); @@ -345,7 +352,9 @@ namespace Ryujinx.Ui.Windows _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath; _resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString(); + _scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value; _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; + _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath; _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode; _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset; @@ -605,6 +614,9 @@ namespace Ryujinx.Ui.Windows ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId); ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom; ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f; + ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId); + ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId); + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value; diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade index e39be81a9..c19c1db9f 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.glade +++ b/Ryujinx/Ui/Windows/SettingsWindow.glade @@ -40,6 +40,13 @@ <property name="inline-completion">True</property> <property name="inline-selection">True</property> </object> + <object class="GtkAdjustment" id="_scalingFilterLevel"> + <property name="lower">0</property> + <property name="upper">101</property> + <property name="step-increment">1</property> + <property name="page-increment">5</property> + <property name="page-size">1</property> + </object> <object class="GtkWindow" id="_settingsWin"> <property name="can-focus">False</property> <property name="title" translatable="yes">Ryujinx - Settings</property> @@ -2152,6 +2159,118 @@ <property name="position">3</property> </packing> </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-top">5</property> + <property name="margin-bottom">5</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Applies a final effect to the game render</property> + <property name="label" translatable="yes">Post Processing Effect:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="_antiAliasing"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Applies anti-aliasing to the game render</property> + <property name="active-id">1</property> + <items> + <item id="0" translatable="yes">None</item> + <item id="1" translatable="yes">FXAA</item> + <item id="2" translatable="yes">SMAA Low</item> + <item id="3" translatable="yes">SMAA Medium</item> + <item id="4" translatable="yes">SMAA High</item> + <item id="5" translatable="yes">SMAA Ultra</item> + </items> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="width-request">100</property> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-top">5</property> + <property name="margin-bottom">5</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Enables Framebuffer Upscaling</property> + <property name="label" translatable="yes">Upscale: </property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="_scalingFilter"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Enables Framebuffer Upscaling</property> + <property name="active-id">1</property> + <items> + <item id="0" translatable="yes">Bilinear</item> + <item id="1" translatable="yes">Nearest</item> + <item id="2" translatable="yes">FSR</item> + </items> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkScale" id="_scalingFilterSlider"> + <property name="width-request">200</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="margin-start">5</property> + <property name="adjustment">_scalingFilterLevel</property> + <property name="round-digits">1</property> + <property name="value-pos">right</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">5</property> + </packing> + </child> <child> <object class="GtkBox"> <property name="visible">True</property> @@ -2197,7 +2316,7 @@ <property name="expand">False</property> <property name="fill">True</property> <property name="padding">5</property> - <property name="position">4</property> + <property name="position">6</property> </packing> </child> <child> @@ -2246,7 +2365,7 @@ <property name="expand">False</property> <property name="fill">True</property> <property name="padding">5</property> - <property name="position">5</property> + <property name="position">7</property> </packing> </child> </object> From 9b5a0c388980b16f7adfceb1f57320087bfc6322 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Tue, 28 Feb 2023 03:41:44 +0000 Subject: [PATCH 381/737] Sockets: Properly convert error codes on MacOS (#4491) * Sockets: Properly convert error codes on MacOS The error codes for MacOS are very different to how they are on windows or linux. An alternate mapping is used when the host operating system is MacOS. This PR also defaults IsDhcpEnabled to true when interfaceProperties.DhcpServerAddresses is not available. This change was already in `macos1`. * Address feedback --- .../StaticService/Types/IpAddressSetting.cs | 2 +- .../Sockets/Bsd/Impl/WinSockHelper.cs | 147 ++++++++++++------ 2 files changed, 104 insertions(+), 45 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs index 5bb046abe..30667b928 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types public IpAddressSetting(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastIPAddressInformation) { - IsDhcpEnabled = !OperatingSystem.IsMacOS() && interfaceProperties.DhcpServerAddresses.Count != 0; + IsDhcpEnabled = OperatingSystem.IsMacOS() || interfaceProperties.DhcpServerAddresses.Count != 0; Address = new IpV4Address(unicastIPAddressInformation.Address); IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask); GatewayAddress = new IpV4Address(interfaceProperties.GatewayAddresses[0].Address); diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs index 48439d7d3..5668d30b0 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs @@ -1,4 +1,5 @@ -using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types; +using System; using System.Collections.Generic; using System.Net.Sockets; @@ -9,85 +10,133 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl private static readonly Dictionary<WsaError, LinuxError> _errorMap = new() { // WSAEINTR - {WsaError.WSAEINTR, LinuxError.EINTR}, + { WsaError.WSAEINTR, LinuxError.EINTR }, // WSAEWOULDBLOCK - {WsaError.WSAEWOULDBLOCK, LinuxError.EWOULDBLOCK}, + { WsaError.WSAEWOULDBLOCK, LinuxError.EWOULDBLOCK }, // WSAEINPROGRESS - {WsaError.WSAEINPROGRESS, LinuxError.EINPROGRESS}, + { WsaError.WSAEINPROGRESS, LinuxError.EINPROGRESS }, // WSAEALREADY - {WsaError.WSAEALREADY, LinuxError.EALREADY}, + { WsaError.WSAEALREADY, LinuxError.EALREADY }, // WSAENOTSOCK - {WsaError.WSAENOTSOCK, LinuxError.ENOTSOCK}, + { WsaError.WSAENOTSOCK, LinuxError.ENOTSOCK }, // WSAEDESTADDRREQ - {WsaError.WSAEDESTADDRREQ, LinuxError.EDESTADDRREQ}, + { WsaError.WSAEDESTADDRREQ, LinuxError.EDESTADDRREQ }, // WSAEMSGSIZE - {WsaError.WSAEMSGSIZE, LinuxError.EMSGSIZE}, + { WsaError.WSAEMSGSIZE, LinuxError.EMSGSIZE }, // WSAEPROTOTYPE - {WsaError.WSAEPROTOTYPE, LinuxError.EPROTOTYPE}, + { WsaError.WSAEPROTOTYPE, LinuxError.EPROTOTYPE }, // WSAENOPROTOOPT - {WsaError.WSAENOPROTOOPT, LinuxError.ENOPROTOOPT}, + { WsaError.WSAENOPROTOOPT, LinuxError.ENOPROTOOPT }, // WSAEPROTONOSUPPORT - {WsaError.WSAEPROTONOSUPPORT, LinuxError.EPROTONOSUPPORT}, + { WsaError.WSAEPROTONOSUPPORT, LinuxError.EPROTONOSUPPORT }, // WSAESOCKTNOSUPPORT - {WsaError.WSAESOCKTNOSUPPORT, LinuxError.ESOCKTNOSUPPORT}, + { WsaError.WSAESOCKTNOSUPPORT, LinuxError.ESOCKTNOSUPPORT }, // WSAEOPNOTSUPP - {WsaError.WSAEOPNOTSUPP, LinuxError.EOPNOTSUPP}, + { WsaError.WSAEOPNOTSUPP, LinuxError.EOPNOTSUPP }, // WSAEPFNOSUPPORT - {WsaError.WSAEPFNOSUPPORT, LinuxError.EPFNOSUPPORT}, + { WsaError.WSAEPFNOSUPPORT, LinuxError.EPFNOSUPPORT }, // WSAEAFNOSUPPORT - {WsaError.WSAEAFNOSUPPORT, LinuxError.EAFNOSUPPORT}, + { WsaError.WSAEAFNOSUPPORT, LinuxError.EAFNOSUPPORT }, // WSAEADDRINUSE - {WsaError.WSAEADDRINUSE, LinuxError.EADDRINUSE}, + { WsaError.WSAEADDRINUSE, LinuxError.EADDRINUSE }, // WSAEADDRNOTAVAIL - {WsaError.WSAEADDRNOTAVAIL, LinuxError.EADDRNOTAVAIL}, + { WsaError.WSAEADDRNOTAVAIL, LinuxError.EADDRNOTAVAIL }, // WSAENETDOWN - {WsaError.WSAENETDOWN, LinuxError.ENETDOWN}, + { WsaError.WSAENETDOWN, LinuxError.ENETDOWN }, // WSAENETUNREACH - {WsaError.WSAENETUNREACH, LinuxError.ENETUNREACH}, + { WsaError.WSAENETUNREACH, LinuxError.ENETUNREACH }, // WSAENETRESET - {WsaError.WSAENETRESET, LinuxError.ENETRESET}, + { WsaError.WSAENETRESET, LinuxError.ENETRESET }, // WSAECONNABORTED - {WsaError.WSAECONNABORTED, LinuxError.ECONNABORTED}, + { WsaError.WSAECONNABORTED, LinuxError.ECONNABORTED }, // WSAECONNRESET - {WsaError.WSAECONNRESET, LinuxError.ECONNRESET}, + { WsaError.WSAECONNRESET, LinuxError.ECONNRESET }, // WSAENOBUFS - {WsaError.WSAENOBUFS, LinuxError.ENOBUFS}, + { WsaError.WSAENOBUFS, LinuxError.ENOBUFS }, // WSAEISCONN - {WsaError.WSAEISCONN, LinuxError.EISCONN}, + { WsaError.WSAEISCONN, LinuxError.EISCONN }, // WSAENOTCONN - {WsaError.WSAENOTCONN, LinuxError.ENOTCONN}, + { WsaError.WSAENOTCONN, LinuxError.ENOTCONN }, // WSAESHUTDOWN - {WsaError.WSAESHUTDOWN, LinuxError.ESHUTDOWN}, + { WsaError.WSAESHUTDOWN, LinuxError.ESHUTDOWN }, // WSAETOOMANYREFS - {WsaError.WSAETOOMANYREFS, LinuxError.ETOOMANYREFS}, + { WsaError.WSAETOOMANYREFS, LinuxError.ETOOMANYREFS }, // WSAETIMEDOUT - {WsaError.WSAETIMEDOUT, LinuxError.ETIMEDOUT}, + { WsaError.WSAETIMEDOUT, LinuxError.ETIMEDOUT }, // WSAECONNREFUSED - {WsaError.WSAECONNREFUSED, LinuxError.ECONNREFUSED}, + { WsaError.WSAECONNREFUSED, LinuxError.ECONNREFUSED }, // WSAELOOP - {WsaError.WSAELOOP, LinuxError.ELOOP}, + { WsaError.WSAELOOP, LinuxError.ELOOP }, // WSAENAMETOOLONG - {WsaError.WSAENAMETOOLONG, LinuxError.ENAMETOOLONG}, + { WsaError.WSAENAMETOOLONG, LinuxError.ENAMETOOLONG }, // WSAEHOSTDOWN - {WsaError.WSAEHOSTDOWN, LinuxError.EHOSTDOWN}, + { WsaError.WSAEHOSTDOWN, LinuxError.EHOSTDOWN }, // WSAEHOSTUNREACH - {WsaError.WSAEHOSTUNREACH, LinuxError.EHOSTUNREACH}, + { WsaError.WSAEHOSTUNREACH, LinuxError.EHOSTUNREACH }, // WSAENOTEMPTY - {WsaError.WSAENOTEMPTY, LinuxError.ENOTEMPTY}, + { WsaError.WSAENOTEMPTY, LinuxError.ENOTEMPTY }, // WSAEUSERS - {WsaError.WSAEUSERS, LinuxError.EUSERS}, + { WsaError.WSAEUSERS, LinuxError.EUSERS }, // WSAEDQUOT - {WsaError.WSAEDQUOT, LinuxError.EDQUOT}, + { WsaError.WSAEDQUOT, LinuxError.EDQUOT }, // WSAESTALE - {WsaError.WSAESTALE, LinuxError.ESTALE}, + { WsaError.WSAESTALE, LinuxError.ESTALE }, // WSAEREMOTE - {WsaError.WSAEREMOTE, LinuxError.EREMOTE}, + { WsaError.WSAEREMOTE, LinuxError.EREMOTE }, // WSAEINVAL - {WsaError.WSAEINVAL, LinuxError.EINVAL}, + { WsaError.WSAEINVAL, LinuxError.EINVAL }, // WSAEFAULT - {WsaError.WSAEFAULT, LinuxError.EFAULT}, + { WsaError.WSAEFAULT, LinuxError.EFAULT }, // NOERROR - {0, 0} + { 0, 0 } + }; + + private static readonly Dictionary<int, LinuxError> _errorMapMacOs = new() + { + { 35, LinuxError.EAGAIN }, + { 11, LinuxError.EDEADLOCK }, + { 91, LinuxError.ENOMSG }, + { 90, LinuxError.EIDRM }, + { 77, LinuxError.ENOLCK }, + { 70, LinuxError.ESTALE }, + { 36, LinuxError.EINPROGRESS }, + { 37, LinuxError.EALREADY }, + { 38, LinuxError.ENOTSOCK }, + { 39, LinuxError.EDESTADDRREQ }, + { 40, LinuxError.EMSGSIZE }, + { 41, LinuxError.EPROTOTYPE }, + { 42, LinuxError.ENOPROTOOPT }, + { 43, LinuxError.EPROTONOSUPPORT }, + { 44, LinuxError.ESOCKTNOSUPPORT }, + { 45, LinuxError.EOPNOTSUPP }, + { 46, LinuxError.EPFNOSUPPORT }, + { 47, LinuxError.EAFNOSUPPORT }, + { 48, LinuxError.EADDRINUSE }, + { 49, LinuxError.EADDRNOTAVAIL }, + { 50, LinuxError.ENETDOWN }, + { 51, LinuxError.ENETUNREACH }, + { 52, LinuxError.ENETRESET }, + { 53, LinuxError.ECONNABORTED }, + { 54, LinuxError.ECONNRESET }, + { 55, LinuxError.ENOBUFS }, + { 56, LinuxError.EISCONN }, + { 57, LinuxError.ENOTCONN }, + { 58, LinuxError.ESHUTDOWN }, + { 60, LinuxError.ETIMEDOUT }, + { 61, LinuxError.ECONNREFUSED }, + { 64, LinuxError.EHOSTDOWN }, + { 65, LinuxError.EHOSTUNREACH }, + { 68, LinuxError.EUSERS }, + { 62, LinuxError.ELOOP }, + { 63, LinuxError.ENAMETOOLONG }, + { 66, LinuxError.ENOTEMPTY }, + { 69, LinuxError.EDQUOT }, + { 71, LinuxError.EREMOTE }, + { 78, LinuxError.ENOSYS }, + { 59, LinuxError.ETOOMANYREFS }, + { 92, LinuxError.EILSEQ }, + { 89, LinuxError.ECANCELED }, + { 84, LinuxError.EOVERFLOW } }; private static readonly Dictionary<BsdSocketOption, SocketOptionName> _soSocketOptionMap = new() @@ -136,12 +185,22 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl public static LinuxError ConvertError(WsaError errorCode) { - if (!_errorMap.TryGetValue(errorCode, out LinuxError errno)) + if (OperatingSystem.IsMacOS()) { - errno = (LinuxError)errorCode; + if (_errorMapMacOs.TryGetValue((int)errorCode, out LinuxError errno)) + { + return errno; + } + } + else + { + if (_errorMap.TryGetValue(errorCode, out LinuxError errno)) + { + return errno; + } } - return errno; + return (LinuxError)errorCode; } public static bool TryConvertSocketOption(BsdSocketOption option, SocketOptionLevel level, out SocketOptionName name) From ecee34a50cd8e4266cb2ecc9910d8d33d612c84a Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Wed, 1 Mar 2023 18:42:27 -0800 Subject: [PATCH 382/737] Update LibHac to 0.18.0 (#4414) * Update LibHac to 0.18.0 * Change instance of AsBytes(CreateReadOnlySpan(...)) to AsReadOnlyByteSpan(...) --- Directory.Packages.props | 2 +- Ryujinx.Ava/Common/ApplicationHelper.cs | 6 +- .../UI/Controls/NavigationDialogHost.axaml.cs | 2 +- .../UI/ViewModels/AvatarProfileViewModel.cs | 2 +- .../UI/ViewModels/MainWindowViewModel.cs | 3 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 2 +- .../UserFirmwareAvatarSelectorViewModel.cs | 2 +- .../UI/Views/Main/MainMenuBarView.axaml.cs | 2 +- .../Views/User/UserSaveManagerView.axaml.cs | 2 +- .../DownloadableContentManagerWindow.axaml.cs | 4 +- Ryujinx.HLE/FileSystem/ContentManager.cs | 26 ++--- Ryujinx.HLE/FileSystem/LocationEntry.cs | 2 +- Ryujinx.HLE/FileSystem/VirtualFileSystem.cs | 9 +- Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs | 2 +- Ryujinx.HLE/HOS/ApplicationLoader.cs | 28 ++--- Ryujinx.HLE/HOS/ModLoader.cs | 4 +- Ryujinx.HLE/HOS/ProgramLoader.cs | 8 +- .../Services/Account/Acc/AccountManager.cs | 2 +- .../ApplicationProxy/IApplicationFunctions.cs | 2 +- .../HOS/Services/Bcat/IServiceCreator.cs | 8 +- .../IDeliveryCacheStorageService.cs | 8 +- .../FileSystemProxy/FileSystemProxyHelper.cs | 18 +-- .../Fs/FileSystemProxy/IFileSystem.cs | 8 +- .../HOS/Services/Fs/IFileSystemProxy.cs | 108 +++++++++--------- .../HOS/Services/Fs/IMultiCommitManager.cs | 2 +- .../HOS/Services/Mii/Types/StoreData.cs | 6 +- .../ILocationResolver.cs | 4 +- .../Nifm/StaticService/Types/ProxySetting.cs | 4 +- .../HOS/Services/Sdb/Pl/SharedFontManager.cs | 2 +- .../Settings/ISystemSettingsServer.cs | 2 +- .../Services/Ssl/BuiltInCertificateManager.cs | 4 +- .../Time/TimeZone/TimeZoneContentManager.cs | 6 +- Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 14 +-- Ryujinx/Ui/MainWindow.cs | 2 +- Ryujinx/Ui/Widgets/GameTableContextMenu.cs | 6 +- Ryujinx/Ui/Windows/AvatarWindow.cs | 2 +- Ryujinx/Ui/Windows/DlcWindow.cs | 4 +- Ryujinx/Ui/Windows/TitleUpdateWindow.cs | 2 +- 38 files changed, 161 insertions(+), 159 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 35c98e5a3..b46b77e70 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -19,7 +19,7 @@ <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> <PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" /> - <PackageVersion Include="LibHac" Version="0.17.0" /> + <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 0b8bd8da1..276d18745 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -193,7 +193,7 @@ namespace Ryujinx.Ava.Common { using var ncaFile = new UniqueRef<IFile>(); - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); if (nca.Header.ContentType == NcaContentType.Program) @@ -249,8 +249,8 @@ namespace Ryujinx.Ava.Common using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem); using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination)); - fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref()); - fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref()); + fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); + fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token); diff --git a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs index 6911a4d4c..741885305 100644 --- a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs @@ -121,7 +121,7 @@ namespace Ryujinx.Ava.UI.Controls using var saveDataIterator = new UniqueRef<SaveDataIterator>(); - HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); + HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10]; diff --git a/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs index 1d0906237..b2b310149 100644 --- a/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs @@ -246,7 +246,7 @@ namespace Ryujinx.Ava.UI.ViewModels { using var file = new UniqueRef<IFile>(); - romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read) + romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read) .ThrowIfFailure(); using (MemoryStream stream = new()) diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index a4ccac2da..489dfe621 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -7,8 +7,7 @@ using DynamicData; using DynamicData.Binding; using LibHac.Common; using LibHac.Fs; -using LibHac.FsSystem; -using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index f330006e3..dd9e1b961 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -170,7 +170,7 @@ public class TitleUpdateViewModel : BaseModel using UniqueRef<IFile> nacpFile = new(); - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); TitleUpdates.Add(new TitleUpdateModel(controlData, path)); diff --git a/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index 9d981128c..558cad5a7 100644 --- a/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -126,7 +126,7 @@ namespace Ryujinx.Ava.UI.ViewModels { using var file = new UniqueRef<IFile>(); - romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); + romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new()) using (MemoryStream streamPng = new()) diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 11ecd0fc9..1c6f4265c 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -1,8 +1,8 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; -using LibHac.FsSystem; using LibHac.Ncm; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; diff --git a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs index 074ca30e9..08fef27d0 100644 --- a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs @@ -76,7 +76,7 @@ namespace Ryujinx.Ava.UI.Views.User using var saveDataIterator = new UniqueRef<SaveDataIterator>(); - _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); + _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10]; diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs index 47216c489..2dab1d352 100644 --- a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs @@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.Windows { using UniqueRef<IFile> ncaFile = new(); - partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); if (nca != null) @@ -158,7 +158,7 @@ namespace Ryujinx.Ava.UI.Windows { using var ncaFile = new UniqueRef<IFile>(); - partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path); if (nca == null) diff --git a/Ryujinx.HLE/FileSystem/ContentManager.cs b/Ryujinx.HLE/FileSystem/ContentManager.cs index 9f0f3a4ae..4e3940081 100644 --- a/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -209,7 +209,7 @@ namespace Ryujinx.HLE.FileSystem { using var ncaFile = new UniqueRef<IFile>(); - fs.OpenFile(ref ncaFile.Ref(), ncaPath.FullPath.ToU8Span(), OpenMode.Read); + fs.OpenFile(ref ncaFile.Ref, ncaPath.FullPath.ToU8Span(), OpenMode.Read); var nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); if (nca.Header.ContentType != NcaContentType.Meta) { @@ -221,7 +221,7 @@ namespace Ryujinx.HLE.FileSystem using var pfs0 = nca.OpenFileSystem(0, integrityCheckLevel); using var cnmtFile = new UniqueRef<IFile>(); - pfs0.OpenFile(ref cnmtFile.Ref(), pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read); + pfs0.OpenFile(ref cnmtFile.Ref, pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read); var cnmt = new Cnmt(cnmtFile.Get.AsStream()); if (cnmt.Type != ContentMetaType.AddOnContent || (cnmt.TitleId & 0xFFFFFFFFFFFFE000) != aocBaseId) @@ -276,11 +276,11 @@ namespace Ryujinx.HLE.FileSystem { case ".xci": pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure); - pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read); + pfs.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read); break; case ".nsp": pfs = new PartitionFileSystem(file.AsStorage()); - pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read); + pfs.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read); break; default: return false; // Print error? @@ -625,11 +625,11 @@ namespace Ryujinx.HLE.FileSystem if (filesystem.FileExists($"{path}/00")) { - filesystem.OpenFile(ref file.Ref(), $"{path}/00".ToU8Span(), mode); + filesystem.OpenFile(ref file.Ref, $"{path}/00".ToU8Span(), mode); } else { - filesystem.OpenFile(ref file.Ref(), path.ToU8Span(), mode); + filesystem.OpenFile(ref file.Ref, path.ToU8Span(), mode); } return file.Release(); @@ -751,7 +751,7 @@ namespace Ryujinx.HLE.FileSystem using var metaFile = new UniqueRef<IFile>(); - if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) { var meta = new Cnmt(metaFile.Get.AsStream()); @@ -781,7 +781,7 @@ namespace Ryujinx.HLE.FileSystem using var systemVersionFile = new UniqueRef<IFile>(); - if (romfs.OpenFile(ref systemVersionFile.Ref(), "/file".ToU8Span(), OpenMode.Read).IsSuccess()) + if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); } @@ -820,7 +820,7 @@ namespace Ryujinx.HLE.FileSystem using var metaFile = new UniqueRef<IFile>(); - if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) { var meta = new Cnmt(metaFile.Get.AsStream()); @@ -891,7 +891,7 @@ namespace Ryujinx.HLE.FileSystem using var metaFile = new UniqueRef<IFile>(); - if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) { var meta = new Cnmt(metaFile.Get.AsStream()); @@ -909,7 +909,7 @@ namespace Ryujinx.HLE.FileSystem using var systemVersionFile = new UniqueRef<IFile>(); - if (romfs.OpenFile(ref systemVersionFile.Ref(), "/file".ToU8Span(), OpenMode.Read).IsSuccess()) + if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); } @@ -960,7 +960,7 @@ namespace Ryujinx.HLE.FileSystem using var metaFile = new UniqueRef<IFile>(); - if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) { var meta = new Cnmt(metaFile.Get.AsStream()); @@ -1030,7 +1030,7 @@ namespace Ryujinx.HLE.FileSystem using var systemVersionFile = new UniqueRef<IFile>(); - if (romfs.OpenFile(ref systemVersionFile.Ref(), "/file".ToU8Span(), OpenMode.Read).IsSuccess()) + if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { return new SystemVersion(systemVersionFile.Get.AsStream()); } diff --git a/Ryujinx.HLE/FileSystem/LocationEntry.cs b/Ryujinx.HLE/FileSystem/LocationEntry.cs index 45cbc8cd5..a60c28967 100644 --- a/Ryujinx.HLE/FileSystem/LocationEntry.cs +++ b/Ryujinx.HLE/FileSystem/LocationEntry.cs @@ -1,4 +1,4 @@ -using LibHac.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; namespace Ryujinx.HLE.FileSystem { diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 0b91d3a2d..3f94ce61b 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -260,15 +260,16 @@ namespace Ryujinx.HLE.FileSystem { using var ticketFile = new UniqueRef<IFile>(); - Result result = fs.OpenFile(ref ticketFile.Ref(), ticketEntry.FullPath.ToU8Span(), OpenMode.Read); + Result result = fs.OpenFile(ref ticketFile.Ref, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.Get.AsStream()); + var titleKey = ticket.GetTitleKey(KeySet); - if (ticket.TitleKeyType == TitleKeyType.Common) + if (titleKey != null) { - KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); + KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(titleKey)); } } } @@ -302,7 +303,7 @@ namespace Ryujinx.HLE.FileSystem using var iterator = new UniqueRef<SaveDataIterator>(); - Result rc = hos.Fs.OpenSaveDataIterator(ref iterator.Ref(), spaceId); + Result rc = hos.Fs.OpenSaveDataIterator(ref iterator.Ref, spaceId); if (rc.IsFailure()) return rc; while (true) diff --git a/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs b/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs index ba7efbd7d..c5c6e8e95 100644 --- a/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs @@ -122,7 +122,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error { using var binaryFile = new UniqueRef<IFile>(); - romfs.OpenFile(ref binaryFile.Ref(), filePath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + romfs.OpenFile(ref binaryFile.Ref, filePath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); StreamReader reader = new StreamReader(binaryFile.Get.AsStream(), Encoding.Unicode); return CleanText(reader.ReadToEnd()); diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 67e0a9c7b..82bd9b312 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -110,7 +110,7 @@ namespace Ryujinx.HLE.HOS { using var ncaFile = new UniqueRef<IFile>(); - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); @@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS { using var ncaFile = new UniqueRef<IFile>(); - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); @@ -329,7 +329,7 @@ namespace Ryujinx.HLE.HOS using var npdmFile = new UniqueRef<IFile>(); - Result result = codeFs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); + Result result = codeFs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); MetaLoader metaData; @@ -356,7 +356,7 @@ namespace Ryujinx.HLE.HOS using var nsoFile = new UniqueRef<IFile>(); - codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); } @@ -371,10 +371,10 @@ namespace Ryujinx.HLE.HOS ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false); ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); - string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16"); - bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0; + string titleIdText = npdm.Aci.ProgramId.Value.ToString("x16"); + bool titleIs64Bit = (npdm.Meta.Flags & 1) != 0; - string programName = Encoding.ASCII.GetString(npdm.Meta.Value.ProgramName).TrimEnd('\0'); + string programName = Encoding.ASCII.GetString(npdm.Meta.ProgramName).TrimEnd('\0'); Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]"); } @@ -520,7 +520,7 @@ namespace Ryujinx.HLE.HOS { using var npdmFile = new UniqueRef<IFile>(); - Result result = fs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); + Result result = fs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); MetaLoader metaData; @@ -543,8 +543,8 @@ namespace Ryujinx.HLE.HOS metaData.GetNpdm(out var npdm).ThrowIfFailure(); - TitleId = npdm.Aci.Value.ProgramId.Value; - TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0; + TitleId = npdm.Aci.ProgramId.Value; + TitleIs64Bit = (npdm.Meta.Flags & 1) != 0; _device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId); return metaData; @@ -555,7 +555,7 @@ namespace Ryujinx.HLE.HOS using var controlFile = new UniqueRef<IFile>(); IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); - Result result = controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read); + Result result = controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { @@ -603,7 +603,7 @@ namespace Ryujinx.HLE.HOS using var nsoFile = new UniqueRef<IFile>(); - codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); } @@ -752,7 +752,7 @@ namespace Ryujinx.HLE.HOS _titleName = programInfo.Name; TitleId = programInfo.ProgramId; - TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0; + TitleIs64Bit = (npdm.Meta.Flags & 1) != 0; _device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId); // Explicitly null titleid to disable the shader cache. @@ -798,7 +798,7 @@ namespace Ryujinx.HLE.HOS { using var ncaFile = new UniqueRef<IFile>(); - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs index b6c9973f0..bf0f1f891 100644 --- a/Ryujinx.HLE/HOS/ModLoader.cs +++ b/Ryujinx.HLE/HOS/ModLoader.cs @@ -475,7 +475,7 @@ namespace Ryujinx.HLE.HOS { using var file = new UniqueRef<IFile>(); - baseRom.OpenFile(ref file.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + baseRom.OpenFile(ref file.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); builder.AddFile(entry.FullPath, file.Release()); } @@ -494,7 +494,7 @@ namespace Ryujinx.HLE.HOS { using var file = new UniqueRef<IFile>(); - fs.OpenFile(ref file.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + fs.OpenFile(ref file.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); if (fileSet.Add(entry.FullPath)) { builder.AddFile(entry.FullPath, file.Release()); diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 1f6fd96d7..158ab701f 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -28,9 +28,9 @@ namespace Ryujinx.HLE.HOS public ProgramInfo(in Npdm npdm, string displayVersion, bool diskCacheEnabled, bool allowCodeMemoryForJit) { - ulong programId = npdm.Aci.Value.ProgramId.Value; + ulong programId = npdm.Aci.ProgramId.Value; - Name = StringUtils.Utf8ZToString(npdm.Meta.Value.ProgramName); + Name = StringUtils.Utf8ZToString(npdm.Meta.ProgramName); ProgramId = programId; TitleIdText = programId.ToString("x16"); DisplayVersion = displayVersion; @@ -193,7 +193,7 @@ namespace Ryujinx.HLE.HOS return ProgramLoadResult.Failed; } - ref readonly var meta = ref npdm.Meta.Value; + ref readonly var meta = ref npdm.Meta; ulong argsStart = 0; uint argsSize = 0; @@ -298,7 +298,7 @@ namespace Ryujinx.HLE.HOS KProcess process = new KProcess(context, programInfo.AllowCodeMemoryForJit); - MemoryRegion memoryRegion = (MemoryRegion)((npdm.Acid.Value.Flags >> 2) & 0xf); + MemoryRegion memoryRegion = (MemoryRegion)((npdm.Acid.Flags >> 2) & 0xf); if (memoryRegion > MemoryRegion.NvServices) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs index 41d5028fb..f5364329d 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs @@ -183,7 +183,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc using var saveDataIterator = new UniqueRef<SaveDataIterator>(); - _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); + _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10]; diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index 49331e216..f8f88a1cb 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -1,9 +1,9 @@ using LibHac.Account; using LibHac.Common; using LibHac.Fs; -using LibHac.FsSystem; using LibHac.Ncm; using LibHac.Ns; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; diff --git a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs index d4528efa7..b16ea4c18 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs @@ -54,11 +54,11 @@ namespace Ryujinx.HLE.HOS.Services.Bcat using var serv = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - Result rc = _base.Get.CreateDeliveryCacheStorageService(ref serv.Ref(), pid); + Result rc = _base.Get.CreateDeliveryCacheStorageService(ref serv.Ref, pid); if (rc.IsSuccess()) { - MakeObject(context, new IDeliveryCacheStorageService(context, ref serv.Ref())); + MakeObject(context, new IDeliveryCacheStorageService(context, ref serv.Ref)); } return (ResultCode)rc.Value; @@ -72,11 +72,11 @@ namespace Ryujinx.HLE.HOS.Services.Bcat using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - Result rc = _base.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref service.Ref(), applicationId); + Result rc = _base.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref service.Ref, applicationId); if (rc.IsSuccess()) { - MakeObject(context, new IDeliveryCacheStorageService(context, ref service.Ref())); + MakeObject(context, new IDeliveryCacheStorageService(context, ref service.Ref)); } return (ResultCode)rc.Value; diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs index 71d7aed70..32dd75d8e 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs @@ -20,11 +20,11 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator { using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); - Result result = _base.Get.CreateFileService(ref service.Ref()); + Result result = _base.Get.CreateFileService(ref service.Ref); if (result.IsSuccess()) { - MakeObject(context, new IDeliveryCacheFileService(ref service.Ref())); + MakeObject(context, new IDeliveryCacheFileService(ref service.Ref)); } return (ResultCode)result.Value; @@ -36,11 +36,11 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator { using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); - Result result = _base.Get.CreateDirectoryService(ref service.Ref()); + Result result = _base.Get.CreateDirectoryService(ref service.Ref); if (result.IsSuccess()) { - MakeObject(context, new IDeliveryCacheDirectoryService(ref service.Ref())); + MakeObject(context, new IDeliveryCacheDirectoryService(ref service.Ref)); } return (ResultCode)result.Value; diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs index 2afa34807..ba924db83 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs @@ -30,9 +30,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy ImportTitleKeysFromNsp(nsp.Get, context.Device.System.KeySet); - using SharedRef<LibHac.FsSrv.Sf.IFileSystem> adapter = FileSystemInterfaceAdapter.CreateShared(ref nsp.Ref(), true); + using SharedRef<LibHac.FsSrv.Sf.IFileSystem> adapter = FileSystemInterfaceAdapter.CreateShared(ref nsp.Ref, true); - openedFileSystem = new IFileSystem(ref adapter.Ref()); + openedFileSystem = new IFileSystem(ref adapter.Ref); } catch (HorizonResultException ex) { @@ -58,9 +58,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy LibHac.Fs.Fsa.IFileSystem fileSystem = nca.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel); using var sharedFs = new SharedRef<LibHac.Fs.Fsa.IFileSystem>(fileSystem); - using SharedRef<LibHac.FsSrv.Sf.IFileSystem> adapter = FileSystemInterfaceAdapter.CreateShared(ref sharedFs.Ref(), true); + using SharedRef<LibHac.FsSrv.Sf.IFileSystem> adapter = FileSystemInterfaceAdapter.CreateShared(ref sharedFs.Ref, true); - openedFileSystem = new IFileSystem(ref adapter.Ref()); + openedFileSystem = new IFileSystem(ref adapter.Ref); } catch (HorizonResultException ex) { @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy using var ncaFile = new UniqueRef<LibHac.Fs.Fsa.IFile>(); - Result result = nsp.OpenFile(ref ncaFile.Ref(), filename.ToU8Span(), OpenMode.Read); + Result result = nsp.OpenFile(ref ncaFile.Ref, filename.ToU8Span(), OpenMode.Read); if (result.IsFailure()) { return (ResultCode)result.Value; @@ -121,13 +121,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy { using var ticketFile = new UniqueRef<LibHac.Fs.Fsa.IFile>(); - Result result = nsp.OpenFile(ref ticketFile.Ref(), ticketEntry.FullPath.ToU8Span(), OpenMode.Read); + Result result = nsp.OpenFile(ref ticketFile.Ref, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.Get.AsStream()); + var titleKey = ticket.GetTitleKey(keySet); - keySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(keySet))); + if (titleKey != null) + { + keySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(titleKey)); + } } } } diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs index d68ef3952..623f1371e 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs @@ -111,11 +111,11 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context); using var file = new SharedRef<LibHac.FsSrv.Sf.IFile>(); - Result result = _fileSystem.Get.OpenFile(ref file.Ref(), in name, mode); + Result result = _fileSystem.Get.OpenFile(ref file.Ref, in name, mode); if (result.IsSuccess()) { - IFile fileInterface = new IFile(ref file.Ref()); + IFile fileInterface = new IFile(ref file.Ref); MakeObject(context, fileInterface); } @@ -132,11 +132,11 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context); using var dir = new SharedRef<LibHac.FsSrv.Sf.IDirectory>(); - Result result = _fileSystem.Get.OpenDirectory(ref dir.Ref(), name, mode); + Result result = _fileSystem.Get.OpenDirectory(ref dir.Ref, name, mode); if (result.IsSuccess()) { - IDirectory dirInterface = new IDirectory(ref dir.Ref()); + IDirectory dirInterface = new IDirectory(ref dir.Ref); MakeObject(context, dirInterface); } diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index a4bc62540..e43b1cad0 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -109,10 +109,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ref readonly var path = ref FileSystemProxyHelper.GetFspPath(context); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenBisFileSystem(ref fileSystem.Ref(), in path, bisPartitionId); + Result result = _baseFileSystemProxy.Get.OpenBisFileSystem(ref fileSystem.Ref, in path, bisPartitionId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -124,10 +124,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs BisPartitionId bisPartitionId = (BisPartitionId)context.RequestData.ReadInt32(); using var storage = new SharedRef<IStorage>(); - Result result = _baseFileSystemProxy.Get.OpenBisStorage(ref storage.Ref(), bisPartitionId); + Result result = _baseFileSystemProxy.Get.OpenBisStorage(ref storage.Ref, bisPartitionId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IStorage(ref storage.Ref())); + MakeObject(context, new FileSystemProxy.IStorage(ref storage.Ref)); return ResultCode.Success; } @@ -145,10 +145,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs { using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenSdCardFileSystem(ref fileSystem.Ref()); + Result result = _baseFileSystemProxy.Get.OpenSdCardFileSystem(ref fileSystem.Ref); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -247,10 +247,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs GameCardPartitionRaw partitionId = (GameCardPartitionRaw)context.RequestData.ReadInt32(); using var storage = new SharedRef<IStorage>(); - Result result = _baseFileSystemProxy.Get.OpenGameCardStorage(ref storage.Ref(), handle, partitionId); + Result result = _baseFileSystemProxy.Get.OpenGameCardStorage(ref storage.Ref, handle, partitionId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IStorage(ref storage.Ref())); + MakeObject(context, new FileSystemProxy.IStorage(ref storage.Ref)); return ResultCode.Success; } @@ -263,10 +263,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs GameCardPartition partitionId = (GameCardPartition)context.RequestData.ReadInt32(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenGameCardFileSystem(ref fileSystem.Ref(), handle, partitionId); + Result result = _baseFileSystemProxy.Get.OpenGameCardFileSystem(ref fileSystem.Ref, handle, partitionId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -338,10 +338,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataFileSystem(ref fileSystem.Ref(), spaceId, in attribute); + Result result = _baseFileSystemProxy.Get.OpenSaveDataFileSystem(ref fileSystem.Ref, spaceId, in attribute); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -354,10 +354,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataFileSystemBySystemSaveDataId(ref fileSystem.Ref(), spaceId, in attribute); + Result result = _baseFileSystemProxy.Get.OpenSaveDataFileSystemBySystemSaveDataId(ref fileSystem.Ref, spaceId, in attribute); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -370,10 +370,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenReadOnlySaveDataFileSystem(ref fileSystem.Ref(), spaceId, in attribute); + Result result = _baseFileSystemProxy.Get.OpenReadOnlySaveDataFileSystem(ref fileSystem.Ref, spaceId, in attribute); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -432,10 +432,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs { using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReader(ref infoReader.Ref()); + Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReader(ref infoReader.Ref); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref())); + MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); return ResultCode.Success; } @@ -447,10 +447,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadByte(); using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderBySaveDataSpaceId(ref infoReader.Ref(), spaceId); + Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderBySaveDataSpaceId(ref infoReader.Ref, spaceId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref())); + MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); return ResultCode.Success; } @@ -461,10 +461,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs { using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderOnlyCacheStorage(ref infoReader.Ref()); + Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderOnlyCacheStorage(ref infoReader.Ref); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref())); + MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); return ResultCode.Success; } @@ -477,10 +477,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ulong saveDataId = context.RequestData.ReadUInt64(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataInternalStorageFileSystem(ref fileSystem.Ref(), spaceId, saveDataId); + Result result = _baseFileSystemProxy.Get.OpenSaveDataInternalStorageFileSystem(ref fileSystem.Ref, spaceId, saveDataId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -537,10 +537,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataFilter filter = context.RequestData.ReadStruct<SaveDataFilter>(); using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderWithFilter(ref infoReader.Ref(), spaceId, in filter); + Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderWithFilter(ref infoReader.Ref, spaceId, in filter); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref())); + MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); return ResultCode.Success; } @@ -605,10 +605,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>(); using var file = new SharedRef<LibHac.FsSrv.Sf.IFile>(); - Result result = _baseFileSystemProxy.Get.OpenSaveDataMetaFile(ref file.Ref(), spaceId, in attribute, metaType); + Result result = _baseFileSystemProxy.Get.OpenSaveDataMetaFile(ref file.Ref, spaceId, in attribute, metaType); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new IFile(ref file.Ref())); + MakeObject(context, new IFile(ref file.Ref)); return ResultCode.Success; } @@ -637,10 +637,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ImageDirectoryId directoryId = (ImageDirectoryId)context.RequestData.ReadInt32(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenImageDirectoryFileSystem(ref fileSystem.Ref(), directoryId); + Result result = _baseFileSystemProxy.Get.OpenImageDirectoryFileSystem(ref fileSystem.Ref, directoryId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -651,10 +651,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs BaseFileSystemId fileSystemId = (BaseFileSystemId)context.RequestData.ReadInt32(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenBaseFileSystem(ref fileSystem.Ref(), fileSystemId); + Result result = _baseFileSystemProxy.Get.OpenBaseFileSystem(ref fileSystem.Ref, fileSystemId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -665,10 +665,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ContentStorageId contentStorageId = (ContentStorageId)context.RequestData.ReadInt32(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenContentStorageFileSystem(ref fileSystem.Ref(), contentStorageId); + Result result = _baseFileSystemProxy.Get.OpenContentStorageFileSystem(ref fileSystem.Ref, contentStorageId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -679,10 +679,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs CloudBackupWorkStorageId storageId = (CloudBackupWorkStorageId)context.RequestData.ReadInt32(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenCloudBackupWorkStorageFileSystem(ref fileSystem.Ref(), storageId); + Result result = _baseFileSystemProxy.Get.OpenCloudBackupWorkStorageFileSystem(ref fileSystem.Ref, storageId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -693,10 +693,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs CustomStorageId customStorageId = (CustomStorageId)context.RequestData.ReadInt32(); using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenCustomStorageFileSystem(ref fileSystem.Ref(), customStorageId); + Result result = _baseFileSystemProxy.Get.OpenCustomStorageFileSystem(ref fileSystem.Ref, customStorageId); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -707,9 +707,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs { var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(storage); - using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref())); + using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref)); - MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref())); + MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref)); return ResultCode.Success; } @@ -730,9 +730,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs var storage = context.Device.FileSystem.ModLoader.ApplyRomFsMods(titleId, aocStorage); using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(storage); - using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref())); + using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref)); - MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref())); + MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref)); return ResultCode.Success; } @@ -765,9 +765,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs Nca nca = new Nca(context.Device.System.KeySet, ncaStorage); LibHac.Fs.IStorage romfsStorage = nca.OpenStorage(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel); using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(romfsStorage); - using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref())); + using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref)); - MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref())); + MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref)); } catch (HorizonResultException ex) { @@ -796,9 +796,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs { var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(storage); - using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref())); + using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref)); - MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref())); + MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref)); return ResultCode.Success; } @@ -816,9 +816,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(storage); - using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref())); + using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref)); - MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref())); + MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref)); return ResultCode.Success; } @@ -829,10 +829,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs { using var deviceOperator = new SharedRef<LibHac.FsSrv.Sf.IDeviceOperator>(); - Result result = _baseFileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + Result result = _baseFileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new IDeviceOperator(ref deviceOperator.Ref())); + MakeObject(context, new IDeviceOperator(ref deviceOperator.Ref)); return ResultCode.Success; } @@ -1195,10 +1195,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs { using var fileSystem = new SharedRef<IFileSystem>(); - Result result = _baseFileSystemProxy.Get.OpenRegisteredUpdatePartition(ref fileSystem.Ref()); + Result result = _baseFileSystemProxy.Get.OpenRegisteredUpdatePartition(ref fileSystem.Ref); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref())); + MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); return ResultCode.Success; } @@ -1290,10 +1290,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs { using var commitManager = new SharedRef<LibHac.FsSrv.Sf.IMultiCommitManager>(); - Result result = _baseFileSystemProxy.Get.OpenMultiCommitManager(ref commitManager.Ref()); + Result result = _baseFileSystemProxy.Get.OpenMultiCommitManager(ref commitManager.Ref); if (result.IsFailure()) return (ResultCode)result.Value; - MakeObject(context, new IMultiCommitManager(ref commitManager.Ref())); + MakeObject(context, new IMultiCommitManager(ref commitManager.Ref)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs b/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs index 4c28117bd..1a85e1b2d 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs { using SharedRef<LibHac.FsSrv.Sf.IFileSystem> fileSystem = GetObject<IFileSystem>(context, 0).GetBaseFileSystem(); - Result result = _baseCommitManager.Get.Add(ref fileSystem.Ref()); + Result result = _baseCommitManager.Get.Add(ref fileSystem.Ref); return (ResultCode)result.Value; } diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs b/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs index 31c46bc09..8411693f4 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs @@ -1,7 +1,5 @@ -using LibHac.Common; -using Ryujinx.HLE.Utilities; +using Ryujinx.Common.Utilities; using System; -using System.Diagnostics; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Mii.Types @@ -78,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types private ReadOnlySpan<byte> AsSpan() { - return MemoryMarshal.AsBytes(SpanHelpers.CreateReadOnlySpan(in this, 1)); + return SpanHelpers.AsReadOnlyByteSpan(ref this); } private ReadOnlySpan<byte> AsSpanWithoutDeviceCrc() diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs index 0767b148f..d97bd009b 100644 --- a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs @@ -1,5 +1,5 @@ -using LibHac.FsSystem; -using LibHac.Ncm; +using LibHac.Ncm; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.HLE.FileSystem; using System.Text; diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs index 827520f15..6e534fe1c 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs @@ -1,5 +1,5 @@ -using LibHac.Common; -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; using System; using System.Runtime.InteropServices; diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs index f1ef6a2f0..66a69a8be 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl using var fontFile = new UniqueRef<IFile>(); - romfs.OpenFile(ref fontFile.Ref(), ("/" + fontFilename).ToU8Span(), OpenMode.Read).ThrowIfFailure(); + romfs.OpenFile(ref fontFile.Ref, ("/" + fontFilename).ToU8Span(), OpenMode.Read).ThrowIfFailure(); data = DecryptFont(fontFile.Get.AsStream()); } diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index 7f32ce6bd..bae10d0b0 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings using var firmwareFile = new UniqueRef<IFile>(); - Result result = firmwareRomFs.OpenFile(ref firmwareFile.Ref(), "/file".ToU8Span(), OpenMode.Read); + Result result = firmwareRomFs.OpenFile(ref firmwareFile.Ref, "/file".ToU8Span(), OpenMode.Read); if (result.IsFailure()) { return null; diff --git a/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs b/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs index a164c7455..abbc13541 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs @@ -133,14 +133,14 @@ namespace Ryujinx.HLE.HOS.Services.Ssl using var trustedCertsFileRef = new UniqueRef<IFile>(); - Result result = romfs.OpenFile(ref trustedCertsFileRef.Ref(), "/ssl_TrustedCerts.bdf".ToU8Span(), OpenMode.Read); + Result result = romfs.OpenFile(ref trustedCertsFileRef.Ref, "/ssl_TrustedCerts.bdf".ToU8Span(), OpenMode.Read); if (!result.IsSuccess()) { // [1.0.0 - 2.3.0] if (ResultFs.PathNotFound.Includes(result)) { - result = romfs.OpenFile(ref trustedCertsFileRef.Ref(), "/ssl_TrustedCerts.tcf".ToU8Span(), OpenMode.Read); + result = romfs.OpenFile(ref trustedCertsFileRef.Ref, "/ssl_TrustedCerts.tcf".ToU8Span(), OpenMode.Read); } if (result.IsFailure()) diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs index f4b3a9590..69ed56d45 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs @@ -97,7 +97,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone using var binaryListFile = new UniqueRef<IFile>(); - romfs.OpenFile(ref binaryListFile.Ref(), "/binaryList.txt".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + romfs.OpenFile(ref binaryListFile.Ref, "/binaryList.txt".ToU8Span(), OpenMode.Read).ThrowIfFailure(); StreamReader reader = new StreamReader(binaryListFile.Get.AsStream()); @@ -143,7 +143,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone using var tzif = new UniqueRef<IFile>(); - if (romfs.OpenFile(ref tzif.Ref(), $"/zoneinfo/{locName}".ToU8Span(), OpenMode.Read).IsFailure()) + if (romfs.OpenFile(ref tzif.Ref, $"/zoneinfo/{locName}".ToU8Span(), OpenMode.Read).IsFailure()) { Logger.Error?.Print(LogClass.ServiceTime, $"Error opening /zoneinfo/{locName}"); continue; @@ -273,7 +273,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone using var timeZoneBinaryFile = new UniqueRef<IFile>(); - Result result = romfs.OpenFile(ref timeZoneBinaryFile.Ref(), $"/zoneinfo/{locationName}".ToU8Span(), OpenMode.Read); + Result result = romfs.OpenFile(ref timeZoneBinaryFile.Ref, $"/zoneinfo/{locationName}".ToU8Span(), OpenMode.Read); timeZoneBinaryStream = timeZoneBinaryFile.Release().AsStream(); diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 951516c08..43510d5ec 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Ui.App.Common { using UniqueRef<IFile> controlFile = new(); - controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); } @@ -178,7 +178,7 @@ namespace Ryujinx.Ui.App.Common { using UniqueRef<IFile> ncaFile = new(); - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); @@ -211,7 +211,7 @@ namespace Ryujinx.Ui.App.Common using UniqueRef<IFile> npdmFile = new(); - Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); + Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); if (ResultFs.PathNotFound.Includes(result)) { @@ -241,7 +241,7 @@ namespace Ryujinx.Ui.App.Common { using UniqueRef<IFile> icon = new(); - controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using MemoryStream stream = new(); @@ -259,7 +259,7 @@ namespace Ryujinx.Ui.App.Common using var icon = new UniqueRef<IFile>(); - controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using MemoryStream stream = new(); @@ -572,7 +572,7 @@ namespace Ryujinx.Ui.App.Common { using var icon = new UniqueRef<IFile>(); - controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using MemoryStream stream = new(); @@ -590,7 +590,7 @@ namespace Ryujinx.Ui.App.Common using var icon = new UniqueRef<IFile>(); - controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new()) { diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 5051fb5f6..6d3d4aad6 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -2,10 +2,10 @@ using Gtk; using LibHac.Common; using LibHac.Common.Keys; -using LibHac.FsSystem; using LibHac.Ncm; using LibHac.Ns; using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Audio.Backends.Dummy; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.SDL2; diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index e45509861..a63d68ff2 100644 --- a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -224,7 +224,7 @@ namespace Ryujinx.Ui.Widgets { using var ncaFile = new UniqueRef<IFile>(); - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Release().AsStorage()); @@ -280,8 +280,8 @@ namespace Ryujinx.Ui.Widgets using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem); using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination)); - fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref()); - fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref()); + fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); + fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); diff --git a/Ryujinx/Ui/Windows/AvatarWindow.cs b/Ryujinx/Ui/Windows/AvatarWindow.cs index c715907d7..fc928bde2 100644 --- a/Ryujinx/Ui/Windows/AvatarWindow.cs +++ b/Ryujinx/Ui/Windows/AvatarWindow.cs @@ -134,7 +134,7 @@ namespace Ryujinx.Ui.Windows { using var file = new UniqueRef<IFile>(); - romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); + romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) using (MemoryStream streamPng = new MemoryStream()) diff --git a/Ryujinx/Ui/Windows/DlcWindow.cs b/Ryujinx/Ui/Windows/DlcWindow.cs index 0a97ac2a2..9fccec195 100644 --- a/Ryujinx/Ui/Windows/DlcWindow.cs +++ b/Ryujinx/Ui/Windows/DlcWindow.cs @@ -93,7 +93,7 @@ namespace Ryujinx.Ui.Windows { using var ncaFile = new UniqueRef<IFile>(); - pfs.OpenFile(ref ncaFile.Ref(), dlcNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, dlcNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.ContainerPath); if (nca != null) @@ -161,7 +161,7 @@ namespace Ryujinx.Ui.Windows { using var ncaFile = new UniqueRef<IFile>(); - pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), containerPath); diff --git a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs index 2618168cd..4aea58955 100644 --- a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs +++ b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs @@ -102,7 +102,7 @@ namespace Ryujinx.Ui.Windows using var nacpFile = new UniqueRef<IFile>(); - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); RadioButton radioButton = new RadioButton($"Version {controlData.DisplayVersionString.ToString()} - {path}"); From dba908dc788c639f9c9d2e85108389efc031aac6 Mon Sep 17 00:00:00 2001 From: MetrosexualGarbodor <79612681+MetrosexualGarbodor@users.noreply.github.com> Date: Sat, 4 Mar 2023 02:15:29 +0000 Subject: [PATCH 383/737] Add post processing feature to the readme (#4499) * Add post processing feature to the readme Adds post processing information to the GPU section in the readme. * correct "Anti-Aliasing" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f11daf15..fdb29a481 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located - **GPU** - The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI. + The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment. These enhancements can be adjusted or toggled as desired in the GUI. - **Input** From 155736c9863ed90c3ffa177266f67a0bdaa63fd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Mar 2023 11:32:30 +0000 Subject: [PATCH 384/737] nuget: bump UnicornEngine.Unicorn (#4500) Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-a913199 to 2.0.2-rc1-f7c841d. - [Release notes](https://github.com/unicorn-engine/unicorn/releases) - [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog) - [Commits](https://github.com/unicorn-engine/unicorn/commits) --- updated-dependencies: - dependency-name: UnicornEngine.Unicorn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index b46b77e70..eef982774 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -49,7 +49,7 @@ <PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> - <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-a913199" /> + <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> </ItemGroup> </Project> From 4f3af839be4134ed63dbd705758714bd0fbba9ef Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 4 Mar 2023 10:43:08 -0300 Subject: [PATCH 385/737] Minor code formatting (#4498) --- ARMeilleure/Instructions/InstEmitHash32.cs | 3 +-- ARMeilleure/Instructions/InstEmitHashHelper.cs | 3 +-- .../SoundIoHardwareDeviceDriver.cs | 3 +-- .../Renderer/Dsp/Command/CompressorCommand.cs | 6 +++--- .../Renderer/Server/Effect/CompressorEffect.cs | 2 +- .../UI/Controls/NavigationDialogHost.axaml.cs | 2 +- Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs | 2 +- Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs | 4 +--- .../UI/ViewModels/ControllerSettingsViewModel.cs | 3 --- Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs | 2 +- Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs | 2 +- Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs | 2 +- Ryujinx.Common/SystemInfo/SystemInfo.cs | 4 ++-- Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs | 2 +- Ryujinx.Common/SystemInterop/StdErrAdapter.cs | 4 ++-- .../Shader/ShaderSpecializationState.cs | 2 +- Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs | 4 ++-- Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs | 4 ++-- Ryujinx.Graphics.OpenGL/PersistentBuffers.cs | 8 ++++---- .../CodeGen/Spirv/SpirvDelegates.cs | 6 +++--- .../CodeGen/Spirv/SpirvGenerator.cs | 3 +-- Ryujinx.Graphics.Texture/BC7Decoder.cs | 2 +- Ryujinx.Graphics.Vulkan/BackgroundResources.cs | 6 +++--- Ryujinx.Graphics.Vulkan/IdList.cs | 2 +- .../MoltenVK/MVKInitialization.cs | 2 +- Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs | 2 +- .../HOS/Applets/Controller/ControllerApplet.cs | 9 ++++----- .../SoftwareKeyboard/SoftwareKeyboardApplet.cs | 4 ++-- .../SoftwareKeyboardRendererBase.cs | 8 ++++---- .../Diagnostics/Demangler/Ast/IntegerLiteral.cs | 2 +- Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs | 4 ++-- Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs | 2 +- .../HOS/Kernel/Process/HleProcessDebugger.cs | 2 +- Ryujinx.HLE/HOS/ModLoader.cs | 8 ++++---- .../Services/Account/Acc/AccountSaveDataManager.cs | 2 +- .../LibraryAppletCreator/ILibraryAppletAccessor.cs | 4 ++-- Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs | 2 +- Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs | 3 +-- Ryujinx.HLE/HOS/Services/IpcService.cs | 2 +- .../HOS/Services/Mii/Types/RandomMiiConstants.cs | 4 ++-- Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs | 6 +++--- .../Vi/RootService/IApplicationDisplayService.cs | 6 +++--- Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs | 2 +- Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs | 4 ++-- Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs | 4 ++-- Ryujinx.Horizon/Sm/SmMain.cs | 4 ++-- Ryujinx.Input/Assigner/GamepadButtonAssigner.cs | 2 +- Ryujinx/Ui/Helper/MetalHelper.cs | 2 +- Ryujinx/Ui/RendererWidgetBase.cs | 8 ++++---- Ryujinx/Ui/VKRenderer.cs | 2 +- Ryujinx/Ui/Widgets/ProfileDialog.cs | 3 +-- Ryujinx/Ui/Windows/ControllerWindow.cs | 14 ++++++-------- Ryujinx/Ui/Windows/SettingsWindow.cs | 7 +++---- 53 files changed, 95 insertions(+), 110 deletions(-) diff --git a/ARMeilleure/Instructions/InstEmitHash32.cs b/ARMeilleure/Instructions/InstEmitHash32.cs index fec782dd8..5d39f8afc 100644 --- a/ARMeilleure/Instructions/InstEmitHash32.cs +++ b/ARMeilleure/Instructions/InstEmitHash32.cs @@ -1,9 +1,8 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; - -using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHashHelper; +using static ARMeilleure.Instructions.InstEmitHelper; namespace ARMeilleure.Instructions { diff --git a/ARMeilleure/Instructions/InstEmitHashHelper.cs b/ARMeilleure/Instructions/InstEmitHashHelper.cs index 1dfe771c4..55a03a4f6 100644 --- a/ARMeilleure/Instructions/InstEmitHashHelper.cs +++ b/ARMeilleure/Instructions/InstEmitHashHelper.cs @@ -4,9 +4,8 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; using System.Diagnostics; - -using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.Instructions.InstEmitSimdHelper; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Instructions { diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs index 2eab59086..02da27769 100644 --- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs +++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs @@ -5,9 +5,8 @@ using Ryujinx.Memory; using System; using System.Collections.Concurrent; using System.Threading; - -using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo; +using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; namespace Ryujinx.Audio.Backends.SoundIo { diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs index 8c3442935..34231e614 100644 --- a/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs +++ b/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs @@ -1,8 +1,8 @@ -using System; -using System.Diagnostics; -using Ryujinx.Audio.Renderer.Dsp.Effect; +using Ryujinx.Audio.Renderer.Dsp.Effect; using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter.Effect; +using System; +using System.Diagnostics; namespace Ryujinx.Audio.Renderer.Dsp.Command { diff --git a/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs index f4e5ae829..32162abcd 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs @@ -1,7 +1,7 @@ using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Dsp.State; -using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Server.MemoryPool; using System; using System.Diagnostics; diff --git a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs index 741885305..1b857fae4 100644 --- a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs @@ -16,9 +16,9 @@ using Ryujinx.Ava.UI.Views.User; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Account.Acc; using System; -using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.Controls diff --git a/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs b/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs index bb9681e22..7a29cc198 100644 --- a/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs +++ b/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Ava.UI.Helpers { using AvaLogger = Avalonia.Logging.Logger; using AvaLogLevel = Avalonia.Logging.LogEventLevel; - using RyuLogger = Ryujinx.Common.Logging.Logger; using RyuLogClass = Ryujinx.Common.Logging.LogClass; + using RyuLogger = Ryujinx.Common.Logging.Logger; internal class LoggerAdapter : Avalonia.Logging.ILogSink { diff --git a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index 5561a20ca..5311318c5 100644 --- a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -3,7 +3,6 @@ using Avalonia.Collections; using Avalonia.Media.Imaging; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Windows; @@ -17,7 +16,6 @@ using System.IO; using System.Linq; using System.Net.Http; using System.Text; -using System.Text.Json; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.ViewModels @@ -31,7 +29,7 @@ namespace Ryujinx.Ava.UI.ViewModels private readonly byte[] _amiiboLogoBytes; private readonly HttpClient _httpClient; private readonly StyleableWindow _owner; - + private Bitmap _amiiboImage; private List<Amiibo.AmiiboApi> _amiiboList; private AvaloniaList<Amiibo.AmiiboApi> _amiibos; diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index f63fc3491..35256b3b5 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -3,11 +3,8 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Svg.Skia; using Avalonia.Threading; -using LibHac.Bcat; -using LibHac.Tools.Fs; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Windows; diff --git a/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs b/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs index fb33dcf8f..81938d23b 100644 --- a/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs @@ -5,8 +5,8 @@ using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Controls; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; using Ryujinx.HLE.HOS.Services.Account.Acc; using System; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; diff --git a/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs b/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs index cd4a3d821..b0c15e491 100644 --- a/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs +++ b/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs @@ -1,9 +1,9 @@ +using Ryujinx.Common.Logging; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Runtime.Versioning; -using Ryujinx.Common.Logging; namespace Ryujinx.Common.SystemInfo { diff --git a/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs b/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs index ad022bdf0..06324a54c 100644 --- a/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs +++ b/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs @@ -1,9 +1,9 @@ +using Ryujinx.Common.Logging; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; -using Ryujinx.Common.Logging; namespace Ryujinx.Common.SystemInfo { diff --git a/Ryujinx.Common/SystemInfo/SystemInfo.cs b/Ryujinx.Common/SystemInfo/SystemInfo.cs index 1db72d9b9..e9ce3c58a 100644 --- a/Ryujinx.Common/SystemInfo/SystemInfo.cs +++ b/Ryujinx.Common/SystemInfo/SystemInfo.cs @@ -1,8 +1,8 @@ -using System; +using Ryujinx.Common.Logging; +using System; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; using System.Text; -using Ryujinx.Common.Logging; namespace Ryujinx.Common.SystemInfo { diff --git a/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs b/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs index 11f0785e6..c3598a1eb 100644 --- a/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs +++ b/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs @@ -1,8 +1,8 @@ +using Ryujinx.Common.Logging; using System; using System.Management; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using Ryujinx.Common.Logging; namespace Ryujinx.Common.SystemInfo { diff --git a/Ryujinx.Common/SystemInterop/StdErrAdapter.cs b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs index 12e240ad3..efb142184 100644 --- a/Ryujinx.Common/SystemInterop/StdErrAdapter.cs +++ b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs @@ -1,9 +1,9 @@ +using Ryujinx.Common.Logging; using System; using System.IO; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; -using Ryujinx.Common.Logging; -using System.Runtime.InteropServices; namespace Ryujinx.Common.SystemInterop { diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index a4bf81363..856507cd7 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Memory; -using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Shader.DiskCache; using Ryujinx.Graphics.Shader; using System; diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs b/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs index 873f667ac..3fc3c72a7 100644 --- a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs +++ b/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs @@ -1,7 +1,7 @@ -using System; +using Ryujinx.Graphics.Nvdec.Vp9.Common; +using System; using System.Diagnostics; using System.Runtime.CompilerServices; -using Ryujinx.Graphics.Nvdec.Vp9.Common; using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.TxfmCommon; namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs b/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs index 5d177b409..050951216 100644 --- a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs +++ b/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs @@ -1,6 +1,6 @@ -using System; +using Ryujinx.Common.Memory; +using System; using System.Buffers.Binary; -using Ryujinx.Common.Memory; namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { diff --git a/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs index 872efcc37..654e25b9d 100644 --- a/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs +++ b/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs @@ -1,10 +1,10 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using OpenTK.Graphics.OpenGL; +using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.OpenGL.Image; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.OpenGL { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs index 04c3be1b8..3ccfd7f55 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs @@ -1,7 +1,7 @@ -using FuncUnaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>; -using FuncBinaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>; -using FuncTernaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>; +using FuncBinaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>; using FuncQuaternaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>; +using FuncTernaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>; +using FuncUnaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 9f08b319d..ca8235383 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -9,9 +9,8 @@ using static Spv.Specification; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { using SpvInstruction = Spv.Generator.Instruction; - using SpvLiteralInteger = Spv.Generator.LiteralInteger; - using SpvInstructionPool = Spv.Generator.GeneratorPool<Spv.Generator.Instruction>; + using SpvLiteralInteger = Spv.Generator.LiteralInteger; using SpvLiteralIntegerPool = Spv.Generator.GeneratorPool<Spv.Generator.LiteralInteger>; static class SpirvGenerator diff --git a/Ryujinx.Graphics.Texture/BC7Decoder.cs b/Ryujinx.Graphics.Texture/BC7Decoder.cs index 060d1ab85..b865a5593 100644 --- a/Ryujinx.Graphics.Texture/BC7Decoder.cs +++ b/Ryujinx.Graphics.Texture/BC7Decoder.cs @@ -1,6 +1,6 @@ using Ryujinx.Graphics.Texture.Utils; -using System.Diagnostics; using System; +using System.Diagnostics; using System.Numerics; using System.Runtime.InteropServices; diff --git a/Ryujinx.Graphics.Vulkan/BackgroundResources.cs b/Ryujinx.Graphics.Vulkan/BackgroundResources.cs index 30972f923..b93b7a250 100644 --- a/Ryujinx.Graphics.Vulkan/BackgroundResources.cs +++ b/Ryujinx.Graphics.Vulkan/BackgroundResources.cs @@ -1,7 +1,7 @@ -using System.Threading; -using System.Collections.Generic; +using Silk.NET.Vulkan; using System; -using Silk.NET.Vulkan; +using System.Collections.Generic; +using System.Threading; namespace Ryujinx.Graphics.Vulkan { diff --git a/Ryujinx.Graphics.Vulkan/IdList.cs b/Ryujinx.Graphics.Vulkan/IdList.cs index 5c0623c3f..9fba9fe99 100644 --- a/Ryujinx.Graphics.Vulkan/IdList.cs +++ b/Ryujinx.Graphics.Vulkan/IdList.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; using System; +using System.Collections.Generic; namespace Ryujinx.Graphics.Vulkan { diff --git a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs index ca2fbfb94..5910d1aac 100644 --- a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -1,7 +1,7 @@ using Silk.NET.Vulkan; using System; -using System.Runtime.Versioning; using System.Runtime.InteropServices; +using System.Runtime.Versioning; namespace Ryujinx.Graphics.Vulkan.MoltenVK { diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index c47f95eab..7293b74f9 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -2,8 +2,8 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; -using System.Threading; using System.Linq; +using System.Threading; namespace Ryujinx.Graphics.Vulkan.Queries { diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs index e5af5fd98..5cdfb3143 100644 --- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs @@ -1,12 +1,11 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.HOS.Services.Hid.Types; using System; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Services.Hid; -using Ryujinx.HLE.HOS.Services.Hid.Types; -using Ryujinx.HLE.HOS.Services.Am.AppletAE; - using static Ryujinx.HLE.HOS.Services.Hid.HidServer.HidUtils; namespace Ryujinx.HLE.HOS.Applets diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 4077ad420..278ea56c2 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -297,7 +297,7 @@ namespace Ryujinx.HLE.HOS.Applets _foregroundState = SoftwareKeyboardState.Complete; } - else if(_foregroundState == SoftwareKeyboardState.Complete) + else if (_foregroundState == SoftwareKeyboardState.Complete) { // If we have already completed, we push the result text // back on the output buffer and poll the application. @@ -780,7 +780,7 @@ namespace Ryujinx.HLE.HOS.Applets { return null; } - + if (input.Length == 0) { return string.Empty; diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index 71835e2da..9a91fa321 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -1,16 +1,16 @@ using Ryujinx.HLE.Ui; using Ryujinx.Memory; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using System; using System.Diagnostics; using System.IO; using System.Numerics; using System.Reflection; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.PixelFormats; namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs index ea048d768..33752d00c 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs @@ -1,5 +1,5 @@ -using System.IO; using System; +using System.IO; namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs index a6618eca4..1bfd7ac07 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -1,8 +1,8 @@ -using System; +using Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast; +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast; namespace Ryujinx.HLE.HOS.Diagnostics.Demangler { diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index bd7d5725b..614eb5271 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -1,9 +1,9 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Horizon.Common; using Ryujinx.Memory; using Ryujinx.Memory.Range; -using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs index 0a78a26dd..8fee5c0d1 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process void AppendTrace(ulong address) { - if(AnalyzePointer(out PointerInfo info, address, thread)) + if (AnalyzePointer(out PointerInfo info, address, thread)) { trace.AppendLine($" 0x{address:x16}\t{info.ImageDisplay}\t{info.SubDisplay}"); } diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs index bf0f1f891..a6dc90135 100644 --- a/Ryujinx.HLE/HOS/ModLoader.cs +++ b/Ryujinx.HLE/HOS/ModLoader.cs @@ -7,15 +7,15 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.RomFs; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.HLE.Loaders.Mods; +using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Mods; using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.Linq; -using System.IO; -using Ryujinx.HLE.HOS.Kernel.Process; using System.Globalization; +using System.IO; +using System.Linq; using Path = System.IO.Path; namespace Ryujinx.HLE.HOS diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs index 3bd0e2da4..ec0b0a10b 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -1,6 +1,6 @@ using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 134566d96..4ed502e0e 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -133,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // PopOutData() -> object<nn::am::service::IStorage> public ResultCode PopOutData(ServiceCtx context) { - if(_normalSession.TryPop(out byte[] data)) + if (_normalSession.TryPop(out byte[] data)) { MakeObject(context, new IStorage(data)); @@ -160,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // PopInteractiveOutData() -> object<nn::am::service::IStorage> public ResultCode PopInteractiveOutData(ServiceCtx context) { - if(_interactiveSession.TryPop(out byte[] data)) + if (_interactiveSession.TryPop(out byte[] data)) { MakeObject(context, new IStorage(data)); diff --git a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs index b16ea4c18..937fe76c5 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs @@ -1,8 +1,8 @@ using LibHac; using LibHac.Common; using Ryujinx.Common; -using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator; using Ryujinx.HLE.HOS.Services.Arp; +using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator; namespace Ryujinx.HLE.HOS.Services.Bcat { diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index e43b1cad0..37143a5aa 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -14,12 +14,11 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy; using System; using System.IO; - using static Ryujinx.HLE.Utilities.StringUtils; +using GameCardHandle = System.UInt32; using IFileSystem = LibHac.FsSrv.Sf.IFileSystem; using IStorage = LibHac.FsSrv.Sf.IStorage; using RightsId = LibHac.Fs.RightsId; -using GameCardHandle = System.UInt32; namespace Ryujinx.HLE.HOS.Services.Fs { diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index 526565a58..4c7d83ea6 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -4,8 +4,8 @@ using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; using System.IO; -using System.Reflection; using System.Linq; +using System.Reflection; namespace Ryujinx.HLE.HOS.Services { diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs b/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs index 16e9289eb..82529450b 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs @@ -1,6 +1,6 @@ -using System; +using Ryujinx.Common.Utilities; +using System; using System.Runtime.InteropServices; -using Ryujinx.Common.Utilities; namespace Ryujinx.HLE.HOS.Services.Mii.Types { diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs index 7369bee5f..341b5e576 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs @@ -1,8 +1,8 @@ using Ryujinx.Common.Collections; -using System.Collections.Generic; -using System; -using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Gpu.Memory; +using System; +using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Nv { diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index d6feb33f4..085d6c519 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -6,15 +6,15 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.SurfaceFlinger; using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService; -using Ryujinx.HLE.Ui; using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types; using Ryujinx.HLE.HOS.Services.Vi.Types; +using Ryujinx.HLE.Ui; +using Ryujinx.Horizon.Common; using System; -using System.Diagnostics; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; -using Ryujinx.Horizon.Common; namespace Ryujinx.HLE.HOS.Services.Vi.RootService { diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index fd45792d7..a4a671eaf 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -1,7 +1,7 @@ using Ryujinx.Common; using Ryujinx.Horizon.Common; -using System.Collections.Generic; using System; +using System.Collections.Generic; namespace Ryujinx.Horizon.Sdk.OsTypes.Impl { diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs index 061d7a3cd..40723a5cf 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs @@ -1,5 +1,5 @@ -using Ryujinx.Horizon.Sdk.OsTypes.Impl; -using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes.Impl; using System; namespace Ryujinx.Horizon.Sdk.OsTypes diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs index 9d21290d8..c36cdda26 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -1,5 +1,5 @@ -using Ryujinx.Horizon.Sdk.OsTypes; -using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; using Ryujinx.Horizon.Sdk.Sf.Cmif; using Ryujinx.Horizon.Sdk.Sm; using System; diff --git a/Ryujinx.Horizon/Sm/SmMain.cs b/Ryujinx.Horizon/Sm/SmMain.cs index 5656d464f..f0b4d3300 100644 --- a/Ryujinx.Horizon/Sm/SmMain.cs +++ b/Ryujinx.Horizon/Sm/SmMain.cs @@ -1,5 +1,5 @@ -using Ryujinx.Horizon.Prepo.Types; -using Ryujinx.Horizon.Prepo; +using Ryujinx.Horizon.Prepo; +using Ryujinx.Horizon.Prepo.Types; using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sm.Impl; diff --git a/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs b/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs index e3aaf8b1b..8621b3a52 100644 --- a/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs +++ b/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; using System; +using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/Ryujinx/Ui/Helper/MetalHelper.cs b/Ryujinx/Ui/Helper/MetalHelper.cs index 1e10eb05a..c2d4893e8 100644 --- a/Ryujinx/Ui/Helper/MetalHelper.cs +++ b/Ryujinx/Ui/Helper/MetalHelper.cs @@ -1,7 +1,7 @@ using Gdk; using System; -using System.Runtime.Versioning; using System.Runtime.InteropServices; +using System.Runtime.Versioning; namespace Ryujinx.Ui.Helper { diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 957bbcd55..e5d22d65c 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -4,13 +4,13 @@ using Gtk; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.Ui.Common.Configuration; -using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; +using Ryujinx.Graphics.Gpu; using Ryujinx.Input; using Ryujinx.Input.GTK3; using Ryujinx.Input.HLE; +using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Widgets; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; @@ -26,8 +26,8 @@ namespace Ryujinx.Ui { using Image = SixLabors.ImageSharp.Image; using Key = Input.Key; - using Switch = HLE.Switch; using ScalingFilter = Graphics.GAL.ScalingFilter; + using Switch = HLE.Switch; public abstract class RendererWidgetBase : DrawingArea { @@ -321,7 +321,7 @@ namespace Ryujinx.Ui Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; } - if(ConfigurationState.Instance.Hid.EnableMouse && _isMouseInClient) + if (ConfigurationState.Instance.Hid.EnableMouse && _isMouseInClient) { Window.Cursor = _invisibleCursor; } diff --git a/Ryujinx/Ui/VKRenderer.cs b/Ryujinx/Ui/VKRenderer.cs index e49b30c3b..d2106c58f 100644 --- a/Ryujinx/Ui/VKRenderer.cs +++ b/Ryujinx/Ui/VKRenderer.cs @@ -3,9 +3,9 @@ using Ryujinx.Common.Configuration; using Ryujinx.Input.HLE; using Ryujinx.Ui.Helper; using SPB.Graphics.Vulkan; +using SPB.Platform.Metal; using SPB.Platform.Win32; using SPB.Platform.X11; -using SPB.Platform.Metal; using SPB.Windowing; using System; using System.Runtime.InteropServices; diff --git a/Ryujinx/Ui/Widgets/ProfileDialog.cs b/Ryujinx/Ui/Widgets/ProfileDialog.cs index 96b44d240..242e8bd7d 100644 --- a/Ryujinx/Ui/Widgets/ProfileDialog.cs +++ b/Ryujinx/Ui/Widgets/ProfileDialog.cs @@ -1,8 +1,7 @@ using Gtk; +using Ryujinx.Ui.Common.Configuration; using System; using System.Reflection; -using Ryujinx.Ui.Common.Configuration; - using GUI = Gtk.Builder.ObjectAttribute; namespace Ryujinx.Ui.Widgets diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs index 8c3a43c85..0f0fba0b8 100644 --- a/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -2,11 +2,14 @@ using Gtk; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; -using Ryujinx.Ui.Common.Configuration; using Ryujinx.Input; +using Ryujinx.Input.Assigner; using Ryujinx.Input.GTK3; +using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; @@ -14,15 +17,10 @@ using System.IO; using System.Reflection; using System.Text.Json; using System.Threading; - -using GUI = Gtk.Builder.ObjectAttribute; -using Key = Ryujinx.Common.Configuration.Hid.Key; - using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using Ryujinx.Common.Logging; -using Ryujinx.Input.Assigner; +using GUI = Gtk.Builder.ObjectAttribute; +using Key = Ryujinx.Common.Configuration.Hid.Key; namespace Ryujinx.Ui.Windows { diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs index 61af7d397..f049da505 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -7,9 +7,10 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Graphics.Vulkan; -using Ryujinx.Ui.Common.Configuration; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Time.TimeZone; +using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Helper; using Ryujinx.Ui.Widgets; using System; @@ -18,9 +19,7 @@ using System.Globalization; using System.IO; using System.Reflection; using System.Threading.Tasks; - using GUI = Gtk.Builder.ObjectAttribute; -using Ryujinx.Ui.Common.Configuration.System; namespace Ryujinx.Ui.Windows { @@ -702,7 +701,7 @@ namespace Ryujinx.Ui.Windows { break; } - } while(_gameDirsBoxStore.IterNext(ref treeIter)); + } while (_gameDirsBoxStore.IterNext(ref treeIter)); } if (!_directoryChanged) From b8556530f2b160db70ff571adf25ae26d4b8f58f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Mar 2023 15:37:08 +0100 Subject: [PATCH 386/737] nuget: bump Microsoft.CodeAnalysis.CSharp from 4.4.0 to 4.5.0 (#4488) Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/dotnet/roslyn/releases) - [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md) - [Commits](https://github.com/dotnet/roslyn/commits) --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.CSharp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index eef982774..5de9461a1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,7 +21,7 @@ <PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" /> <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> - <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" /> + <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> From f0562b9c75308c8cfcaa2458dfd37ac42751a374 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 8 Mar 2023 19:25:35 -0300 Subject: [PATCH 387/737] CPU: Avoid argument value copies on the JIT (#4484) * Minor refactoring of the pre-allocator * Avoid LoadArgument copies * PPTC version bump --- ARMeilleure/CodeGen/Arm64/PreAllocator.cs | 86 +-- ARMeilleure/CodeGen/PreAllocatorCommon.cs | 57 ++ ARMeilleure/CodeGen/X86/PreAllocator.cs | 668 +----------------- .../CodeGen/X86/PreAllocatorSystemV.cs | 334 +++++++++ .../CodeGen/X86/PreAllocatorWindows.cs | 327 +++++++++ ARMeilleure/Translation/PTC/Ptc.cs | 2 +- 6 files changed, 766 insertions(+), 708 deletions(-) create mode 100644 ARMeilleure/CodeGen/PreAllocatorCommon.cs create mode 100644 ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs create mode 100644 ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs diff --git a/ARMeilleure/CodeGen/Arm64/PreAllocator.cs b/ARMeilleure/CodeGen/Arm64/PreAllocator.cs index a7f073946..6ea9d2397 100644 --- a/ARMeilleure/CodeGen/Arm64/PreAllocator.cs +++ b/ARMeilleure/CodeGen/Arm64/PreAllocator.cs @@ -9,7 +9,7 @@ using static ARMeilleure.IntermediateRepresentation.Operation.Factory; namespace ARMeilleure.CodeGen.Arm64 { - class PreAllocator + static class PreAllocator { private class ConstantDict { @@ -54,8 +54,8 @@ namespace ARMeilleure.CodeGen.Arm64 continue; } - HandleConstantRegCopy(constants, block.Operations, node); - HandleDestructiveRegCopy(block.Operations, node); + InsertConstantRegCopies(constants, block.Operations, node); + InsertDestructiveRegCopies(block.Operations, node); switch (node.Instruction) { @@ -78,28 +78,28 @@ namespace ARMeilleure.CodeGen.Arm64 // Copy values to registers expected by the function // being called, as mandated by the ABI. - HandleCall(constants, block.Operations, node); + InsertCallCopies(constants, block.Operations, node); break; case Instruction.CompareAndSwap: case Instruction.CompareAndSwap16: case Instruction.CompareAndSwap8: - nextNode = HandleCompareAndSwap(block.Operations, node); + nextNode = GenerateCompareAndSwap(block.Operations, node); break; case Instruction.LoadArgument: - nextNode = HandleLoadArgument(cctx, ref buffer, block.Operations, preservedArgs, node); + nextNode = InsertLoadArgumentCopy(cctx, ref buffer, block.Operations, preservedArgs, node); break; case Instruction.Return: - HandleReturn(block.Operations, node); + InsertReturnCopy(block.Operations, node); break; case Instruction.Tailcall: - HandleTailcall(constants, block.Operations, stackAlloc, node, node); + InsertTailcallCopies(constants, block.Operations, stackAlloc, node, node); break; } } } } - private static void HandleConstantRegCopy(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node) + private static void InsertConstantRegCopies(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node) { if (node.SourcesCount == 0 || IsIntrinsicWithConst(node)) { @@ -211,7 +211,7 @@ namespace ARMeilleure.CodeGen.Arm64 } } - private static void HandleDestructiveRegCopy(IntrusiveList<Operation> nodes, Operation node) + private static void InsertDestructiveRegCopies(IntrusiveList<Operation> nodes, Operation node) { if (node.Destination == default || node.SourcesCount == 0) { @@ -259,7 +259,7 @@ namespace ARMeilleure.CodeGen.Arm64 } } - private static void HandleCall(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node) + private static void InsertCallCopies(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node) { Operation operation = node; @@ -319,7 +319,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operation copyOp = Operation(Instruction.Copy, argReg, source); - HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp)); + InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, copyOp)); sources.Add(argReg); } @@ -329,7 +329,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operation spillOp = Operation(Instruction.SpillArg, default, offset, source); - HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, spillOp)); + InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, spillOp)); stackOffset += source.Type.GetSizeInBytes(); } @@ -364,7 +364,7 @@ namespace ARMeilleure.CodeGen.Arm64 operation.SetSources(sources.ToArray()); } - private static void HandleTailcall( + private static void InsertTailcallCopies( ConstantDict constants, IntrusiveList<Operation> nodes, StackAllocator stackAlloc, @@ -420,7 +420,7 @@ namespace ARMeilleure.CodeGen.Arm64 Operation copyOp = Operation(Instruction.Copy, argReg, source); - HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp)); + InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, copyOp)); sources.Add(argReg); } @@ -444,7 +444,7 @@ namespace ARMeilleure.CodeGen.Arm64 operation.SetSources(sources.ToArray()); } - private static Operation HandleCompareAndSwap(IntrusiveList<Operation> nodes, Operation node) + private static Operation GenerateCompareAndSwap(IntrusiveList<Operation> nodes, Operation node) { Operand expected = node.GetSource(1); @@ -508,7 +508,7 @@ namespace ARMeilleure.CodeGen.Arm64 return node.ListNext; } - private static void HandleReturn(IntrusiveList<Operation> nodes, Operation node) + private static void InsertReturnCopy(IntrusiveList<Operation> nodes, Operation node) { if (node.SourcesCount == 0) { @@ -537,7 +537,7 @@ namespace ARMeilleure.CodeGen.Arm64 } } - private static Operation HandleLoadArgument( + private static Operation InsertLoadArgumentCopy( CompilerContext cctx, ref Span<Operation> buffer, IntrusiveList<Operation> nodes, @@ -629,7 +629,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (dest.AssignmentsCount == 1) { // Let's propagate the argument if we can to avoid copies. - Propagate(ref buffer, dest, preservedArgs[index]); + PreAllocatorCommon.Propagate(ref buffer, dest, preservedArgs[index]); nextNode = node.ListNext; } else @@ -648,54 +648,6 @@ namespace ARMeilleure.CodeGen.Arm64 } } - private static void Propagate(ref Span<Operation> buffer, Operand dest, Operand value) - { - ReadOnlySpan<Operation> uses = dest.GetUses(ref buffer); - - foreach (Operation use in uses) - { - for (int srcIndex = 0; srcIndex < use.SourcesCount; srcIndex++) - { - Operand useSrc = use.GetSource(srcIndex); - - if (useSrc == dest) - { - use.SetSource(srcIndex, value); - } - else if (useSrc.Kind == OperandKind.Memory) - { - MemoryOperand memoryOp = useSrc.GetMemory(); - - Operand baseAddr = memoryOp.BaseAddress; - Operand index = memoryOp.Index; - bool changed = false; - - if (baseAddr == dest) - { - baseAddr = value; - changed = true; - } - - if (index == dest) - { - index = value; - changed = true; - } - - if (changed) - { - use.SetSource(srcIndex, MemoryOp( - useSrc.Type, - baseAddr, - index, - memoryOp.Scale, - memoryOp.Displacement)); - } - } - } - } - } - private static Operand AddFloatConstantCopy( ConstantDict constants, IntrusiveList<Operation> nodes, diff --git a/ARMeilleure/CodeGen/PreAllocatorCommon.cs b/ARMeilleure/CodeGen/PreAllocatorCommon.cs new file mode 100644 index 000000000..53f279fb9 --- /dev/null +++ b/ARMeilleure/CodeGen/PreAllocatorCommon.cs @@ -0,0 +1,57 @@ +using ARMeilleure.IntermediateRepresentation; +using System; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; + +namespace ARMeilleure.CodeGen +{ + static class PreAllocatorCommon + { + public static void Propagate(ref Span<Operation> buffer, Operand dest, Operand value) + { + ReadOnlySpan<Operation> uses = dest.GetUses(ref buffer); + + foreach (Operation use in uses) + { + for (int srcIndex = 0; srcIndex < use.SourcesCount; srcIndex++) + { + Operand useSrc = use.GetSource(srcIndex); + + if (useSrc == dest) + { + use.SetSource(srcIndex, value); + } + else if (useSrc.Kind == OperandKind.Memory) + { + MemoryOperand memoryOp = useSrc.GetMemory(); + + Operand baseAddr = memoryOp.BaseAddress; + Operand index = memoryOp.Index; + bool changed = false; + + if (baseAddr == dest) + { + baseAddr = value; + changed = true; + } + + if (index == dest) + { + index = value; + changed = true; + } + + if (changed) + { + use.SetSource(srcIndex, MemoryOp( + useSrc.Type, + baseAddr, + index, + memoryOp.Scale, + memoryOp.Displacement)); + } + } + } + } + } + } +} diff --git a/ARMeilleure/CodeGen/X86/PreAllocator.cs b/ARMeilleure/CodeGen/X86/PreAllocator.cs index dd73a1dd0..72f56514f 100644 --- a/ARMeilleure/CodeGen/X86/PreAllocator.cs +++ b/ARMeilleure/CodeGen/X86/PreAllocator.cs @@ -2,19 +2,20 @@ using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; -using System.Collections.Generic; using System.Diagnostics; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.IntermediateRepresentation.Operation.Factory; namespace ARMeilleure.CodeGen.X86 { - static class PreAllocator + class PreAllocator { public static void RunPass(CompilerContext cctx, StackAllocator stackAlloc, out int maxCallArgs) { maxCallArgs = -1; + Span<Operation> buffer = default; + CallConvName callConv = CallingConvention.GetCurrentCallConv(); Operand[] preservedArgs = new Operand[CallingConvention.GetArgumentsOnRegsCount()]; @@ -32,9 +33,9 @@ namespace ARMeilleure.CodeGen.X86 continue; } - HandleConstantRegCopy(block.Operations, node); - HandleDestructiveRegCopy(block.Operations, node); - HandleConstrainedRegCopy(block.Operations, node); + InsertConstantRegCopies(block.Operations, node); + InsertDestructiveRegCopies(block.Operations, node); + InsertConstrainedRegCopies(block.Operations, node); switch (node.Instruction) { @@ -59,62 +60,62 @@ namespace ARMeilleure.CodeGen.X86 // being called, as mandated by the ABI. if (callConv == CallConvName.Windows) { - HandleCallWindowsAbi(block.Operations, stackAlloc, node); + PreAllocatorWindows.InsertCallCopies(block.Operations, stackAlloc, node); } else /* if (callConv == CallConvName.SystemV) */ { - HandleCallSystemVAbi(block.Operations, node); + PreAllocatorSystemV.InsertCallCopies(block.Operations, node); } break; case Instruction.ConvertToFPUI: - HandleConvertToFPUI(block.Operations, node); + GenerateConvertToFPUI(block.Operations, node); break; case Instruction.LoadArgument: if (callConv == CallConvName.Windows) { - nextNode = HandleLoadArgumentWindowsAbi(cctx, block.Operations, preservedArgs, node); + nextNode = PreAllocatorWindows.InsertLoadArgumentCopy(cctx, ref buffer, block.Operations, preservedArgs, node); } else /* if (callConv == CallConvName.SystemV) */ { - nextNode = HandleLoadArgumentSystemVAbi(cctx, block.Operations, preservedArgs, node); + nextNode = PreAllocatorSystemV.InsertLoadArgumentCopy(cctx, ref buffer, block.Operations, preservedArgs, node); } break; case Instruction.Negate: if (!node.GetSource(0).Type.IsInteger()) { - HandleNegate(block.Operations, node); + GenerateNegate(block.Operations, node); } break; case Instruction.Return: if (callConv == CallConvName.Windows) { - HandleReturnWindowsAbi(cctx, block.Operations, preservedArgs, node); + PreAllocatorWindows.InsertReturnCopy(cctx, block.Operations, preservedArgs, node); } else /* if (callConv == CallConvName.SystemV) */ { - HandleReturnSystemVAbi(block.Operations, node); + PreAllocatorSystemV.InsertReturnCopy(block.Operations, node); } break; case Instruction.Tailcall: if (callConv == CallConvName.Windows) { - HandleTailcallWindowsAbi(block.Operations, stackAlloc, node); + PreAllocatorWindows.InsertTailcallCopies(block.Operations, stackAlloc, node); } else { - HandleTailcallSystemVAbi(block.Operations, stackAlloc, node); + PreAllocatorSystemV.InsertTailcallCopies(block.Operations, stackAlloc, node); } break; case Instruction.VectorInsert8: if (!HardwareCapabilities.SupportsSse41) { - HandleVectorInsert8(block.Operations, node); + GenerateVectorInsert8(block.Operations, node); } break; @@ -131,7 +132,7 @@ namespace ARMeilleure.CodeGen.X86 } } - private static void HandleConstantRegCopy(IntrusiveList<Operation> nodes, Operation node) + protected static void InsertConstantRegCopies(IntrusiveList<Operation> nodes, Operation node) { if (node.SourcesCount == 0 || IsXmmIntrinsic(node)) { @@ -212,7 +213,7 @@ namespace ARMeilleure.CodeGen.X86 } } - private static void HandleConstrainedRegCopy(IntrusiveList<Operation> nodes, Operation node) + protected static void InsertConstrainedRegCopies(IntrusiveList<Operation> nodes, Operation node) { Operand dest = node.Destination; @@ -369,7 +370,7 @@ namespace ARMeilleure.CodeGen.X86 } } - private static void HandleDestructiveRegCopy(IntrusiveList<Operation> nodes, Operation node) + protected static void InsertDestructiveRegCopies(IntrusiveList<Operation> nodes, Operation node) { if (node.Destination == default || node.SourcesCount == 0) { @@ -447,7 +448,7 @@ namespace ARMeilleure.CodeGen.X86 } } - private static void HandleConvertToFPUI(IntrusiveList<Operation> nodes, Operation node) + private static void GenerateConvertToFPUI(IntrusiveList<Operation> nodes, Operation node) { // Unsigned integer to FP conversions are not supported on X86. // We need to turn them into signed integer to FP conversions, and @@ -501,7 +502,7 @@ namespace ARMeilleure.CodeGen.X86 Delete(nodes, currentNode); } - private static void HandleNegate(IntrusiveList<Operation> nodes, Operation node) + private static void GenerateNegate(IntrusiveList<Operation> nodes, Operation node) { // There's no SSE FP negate instruction, so we need to transform that into // a XOR of the value to be negated with a mask with the highest bit set. @@ -534,7 +535,7 @@ namespace ARMeilleure.CodeGen.X86 Delete(nodes, currentNode); } - private static void HandleVectorInsert8(IntrusiveList<Operation> nodes, Operation node) + private static void GenerateVectorInsert8(IntrusiveList<Operation> nodes, Operation node) { // Handle vector insertion, when SSE 4.1 is not supported. Operand dest = node.Destination; @@ -579,620 +580,7 @@ namespace ARMeilleure.CodeGen.X86 Delete(nodes, currentNode); } - private static void HandleCallWindowsAbi(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) - { - Operand dest = node.Destination; - - // Handle struct arguments. - int retArgs = 0; - int stackAllocOffset = 0; - - int AllocateOnStack(int size) - { - // We assume that the stack allocator is initially empty (TotalSize = 0). - // Taking that into account, we can reuse the space allocated for other - // calls by keeping track of our own allocated size (stackAllocOffset). - // If the space allocated is not big enough, then we just expand it. - int offset = stackAllocOffset; - - if (stackAllocOffset + size > stackAlloc.TotalSize) - { - stackAlloc.Allocate((stackAllocOffset + size) - stackAlloc.TotalSize); - } - - stackAllocOffset += size; - - return offset; - } - - Operand arg0Reg = default; - - if (dest != default && dest.Type == OperandType.V128) - { - int stackOffset = AllocateOnStack(dest.Type.GetSizeInBytes()); - - arg0Reg = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64); - - Operation allocOp = Operation(Instruction.StackAlloc, arg0Reg, Const(stackOffset)); - - nodes.AddBefore(node, allocOp); - - retArgs = 1; - } - - int argsCount = node.SourcesCount - 1; - int maxArgs = CallingConvention.GetArgumentsOnRegsCount() - retArgs; - - if (argsCount > maxArgs) - { - argsCount = maxArgs; - } - - Operand[] sources = new Operand[1 + retArgs + argsCount]; - - sources[0] = node.GetSource(0); - - if (arg0Reg != default) - { - sources[1] = arg0Reg; - } - - for (int index = 1; index < node.SourcesCount; index++) - { - Operand source = node.GetSource(index); - - if (source.Type == OperandType.V128) - { - Operand stackAddr = Local(OperandType.I64); - - int stackOffset = AllocateOnStack(source.Type.GetSizeInBytes()); - - nodes.AddBefore(node, Operation(Instruction.StackAlloc, stackAddr, Const(stackOffset))); - - Operation storeOp = Operation(Instruction.Store, default, stackAddr, source); - - HandleConstantRegCopy(nodes, nodes.AddBefore(node, storeOp)); - - node.SetSource(index, stackAddr); - } - } - - // Handle arguments passed on registers. - for (int index = 0; index < argsCount; index++) - { - Operand source = node.GetSource(index + 1); - Operand argReg; - - int argIndex = index + retArgs; - - if (source.Type.IsInteger()) - { - argReg = Gpr(CallingConvention.GetIntArgumentRegister(argIndex), source.Type); - } - else - { - argReg = Xmm(CallingConvention.GetVecArgumentRegister(argIndex), source.Type); - } - - Operation copyOp = Operation(Instruction.Copy, argReg, source); - - HandleConstantRegCopy(nodes, nodes.AddBefore(node, copyOp)); - - sources[1 + retArgs + index] = argReg; - } - - // The remaining arguments (those that are not passed on registers) - // should be passed on the stack, we write them to the stack with "SpillArg". - for (int index = argsCount; index < node.SourcesCount - 1; index++) - { - Operand source = node.GetSource(index + 1); - Operand offset = Const((index + retArgs) * 8); - - Operation spillOp = Operation(Instruction.SpillArg, default, offset, source); - - HandleConstantRegCopy(nodes, nodes.AddBefore(node, spillOp)); - } - - if (dest != default) - { - if (dest.Type == OperandType.V128) - { - Operand retValueAddr = Local(OperandType.I64); - - nodes.AddBefore(node, Operation(Instruction.Copy, retValueAddr, arg0Reg)); - - Operation loadOp = Operation(Instruction.Load, dest, retValueAddr); - - nodes.AddAfter(node, loadOp); - - node.Destination = default; - } - else - { - Operand retReg = dest.Type.IsInteger() - ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) - : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); - - Operation copyOp = Operation(Instruction.Copy, dest, retReg); - - nodes.AddAfter(node, copyOp); - - node.Destination = retReg; - } - } - - node.SetSources(sources); - } - - private static void HandleCallSystemVAbi(IntrusiveList<Operation> nodes, Operation node) - { - Operand dest = node.Destination; - - List<Operand> sources = new List<Operand> - { - node.GetSource(0) - }; - - int argsCount = node.SourcesCount - 1; - - int intMax = CallingConvention.GetIntArgumentsOnRegsCount(); - int vecMax = CallingConvention.GetVecArgumentsOnRegsCount(); - - int intCount = 0; - int vecCount = 0; - - int stackOffset = 0; - - for (int index = 0; index < argsCount; index++) - { - Operand source = node.GetSource(index + 1); - - bool passOnReg; - - if (source.Type.IsInteger()) - { - passOnReg = intCount < intMax; - } - else if (source.Type == OperandType.V128) - { - passOnReg = intCount + 1 < intMax; - } - else - { - passOnReg = vecCount < vecMax; - } - - if (source.Type == OperandType.V128 && passOnReg) - { - // V128 is a struct, we pass each half on a GPR if possible. - Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); - Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); - - nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); - nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); - - continue; - } - - if (passOnReg) - { - Operand argReg = source.Type.IsInteger() - ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) - : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); - - Operation copyOp = Operation(Instruction.Copy, argReg, source); - - HandleConstantRegCopy(nodes, nodes.AddBefore(node, copyOp)); - - sources.Add(argReg); - } - else - { - Operand offset = Const(stackOffset); - - Operation spillOp = Operation(Instruction.SpillArg, default, offset, source); - - HandleConstantRegCopy(nodes, nodes.AddBefore(node, spillOp)); - - stackOffset += source.Type.GetSizeInBytes(); - } - } - - node.SetSources(sources.ToArray()); - - if (dest != default) - { - if (dest.Type == OperandType.V128) - { - Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); - Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); - - Operation operation = node; - - node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, retLReg)); - nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, retHReg, Const(1))); - - operation.Destination = default; - } - else - { - Operand retReg = dest.Type.IsInteger() - ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) - : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); - - Operation copyOp = Operation(Instruction.Copy, dest, retReg); - - nodes.AddAfter(node, copyOp); - - node.Destination = retReg; - } - } - } - - private static void HandleTailcallSystemVAbi(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) - { - List<Operand> sources = new List<Operand> - { - node.GetSource(0) - }; - - int argsCount = node.SourcesCount - 1; - - int intMax = CallingConvention.GetIntArgumentsOnRegsCount(); - int vecMax = CallingConvention.GetVecArgumentsOnRegsCount(); - - int intCount = 0; - int vecCount = 0; - - // Handle arguments passed on registers. - for (int index = 0; index < argsCount; index++) - { - Operand source = node.GetSource(1 + index); - - bool passOnReg; - - if (source.Type.IsInteger()) - { - passOnReg = intCount + 1 < intMax; - } - else - { - passOnReg = vecCount < vecMax; - } - - if (source.Type == OperandType.V128 && passOnReg) - { - // V128 is a struct, we pass each half on a GPR if possible. - Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); - Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); - - nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); - nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); - - continue; - } - - if (passOnReg) - { - Operand argReg = source.Type.IsInteger() - ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) - : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); - - Operation copyOp = Operation(Instruction.Copy, argReg, source); - - HandleConstantRegCopy(nodes, nodes.AddBefore(node, copyOp)); - - sources.Add(argReg); - } - else - { - throw new NotImplementedException("Spilling is not currently supported for tail calls. (too many arguments)"); - } - } - - // The target address must be on the return registers, since we - // don't return anything and it is guaranteed to not be a - // callee saved register (which would be trashed on the epilogue). - Operand retReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); - - Operation addrCopyOp = Operation(Instruction.Copy, retReg, node.GetSource(0)); - - nodes.AddBefore(node, addrCopyOp); - - sources[0] = retReg; - - node.SetSources(sources.ToArray()); - } - - private static void HandleTailcallWindowsAbi(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) - { - int argsCount = node.SourcesCount - 1; - int maxArgs = CallingConvention.GetArgumentsOnRegsCount(); - - if (argsCount > maxArgs) - { - throw new NotImplementedException("Spilling is not currently supported for tail calls. (too many arguments)"); - } - - Operand[] sources = new Operand[1 + argsCount]; - - // Handle arguments passed on registers. - for (int index = 0; index < argsCount; index++) - { - Operand source = node.GetSource(1 + index); - Operand argReg = source.Type.IsInteger() - ? Gpr(CallingConvention.GetIntArgumentRegister(index), source.Type) - : Xmm(CallingConvention.GetVecArgumentRegister(index), source.Type); - - Operation copyOp = Operation(Instruction.Copy, argReg, source); - - HandleConstantRegCopy(nodes, nodes.AddBefore(node, copyOp)); - - sources[1 + index] = argReg; - } - - // The target address must be on the return registers, since we - // don't return anything and it is guaranteed to not be a - // callee saved register (which would be trashed on the epilogue). - Operand retReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); - - Operation addrCopyOp = Operation(Instruction.Copy, retReg, node.GetSource(0)); - - nodes.AddBefore(node, addrCopyOp); - - sources[0] = retReg; - - node.SetSources(sources); - } - - private static Operation HandleLoadArgumentWindowsAbi( - CompilerContext cctx, - IntrusiveList<Operation> nodes, - Operand[] preservedArgs, - Operation node) - { - Operand source = node.GetSource(0); - - Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind."); - - int retArgs = cctx.FuncReturnType == OperandType.V128 ? 1 : 0; - - int index = source.AsInt32() + retArgs; - - if (index < CallingConvention.GetArgumentsOnRegsCount()) - { - Operand dest = node.Destination; - - if (preservedArgs[index] == default) - { - Operand argReg, pArg; - - if (dest.Type.IsInteger()) - { - argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), dest.Type); - pArg = Local(dest.Type); - } - else if (dest.Type == OperandType.V128) - { - argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), OperandType.I64); - pArg = Local(OperandType.I64); - } - else - { - argReg = Xmm(CallingConvention.GetVecArgumentRegister(index), dest.Type); - pArg = Local(dest.Type); - } - - Operation copyOp = Operation(Instruction.Copy, pArg, argReg); - - cctx.Cfg.Entry.Operations.AddFirst(copyOp); - - preservedArgs[index] = pArg; - } - - Operation argCopyOp = Operation(dest.Type == OperandType.V128 - ? Instruction.Load - : Instruction.Copy, dest, preservedArgs[index]); - - Operation newNode = nodes.AddBefore(node, argCopyOp); - - Delete(nodes, node); - - return newNode; - } - else - { - // TODO: Pass on stack. - return node; - } - } - - private static Operation HandleLoadArgumentSystemVAbi( - CompilerContext cctx, - IntrusiveList<Operation> nodes, - Operand[] preservedArgs, - Operation node) - { - Operand source = node.GetSource(0); - - Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind."); - - int index = source.AsInt32(); - - int intCount = 0; - int vecCount = 0; - - for (int cIndex = 0; cIndex < index; cIndex++) - { - OperandType argType = cctx.FuncArgTypes[cIndex]; - - if (argType.IsInteger()) - { - intCount++; - } - else if (argType == OperandType.V128) - { - intCount += 2; - } - else - { - vecCount++; - } - } - - bool passOnReg; - - if (source.Type.IsInteger()) - { - passOnReg = intCount < CallingConvention.GetIntArgumentsOnRegsCount(); - } - else if (source.Type == OperandType.V128) - { - passOnReg = intCount + 1 < CallingConvention.GetIntArgumentsOnRegsCount(); - } - else - { - passOnReg = vecCount < CallingConvention.GetVecArgumentsOnRegsCount(); - } - - if (passOnReg) - { - Operand dest = node.Destination; - - if (preservedArgs[index] == default) - { - if (dest.Type == OperandType.V128) - { - // V128 is a struct, we pass each half on a GPR if possible. - Operand pArg = Local(OperandType.V128); - - Operand argLReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount), OperandType.I64); - Operand argHReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount + 1), OperandType.I64); - - Operation copyL = Operation(Instruction.VectorCreateScalar, pArg, argLReg); - Operation copyH = Operation(Instruction.VectorInsert, pArg, pArg, argHReg, Const(1)); - - cctx.Cfg.Entry.Operations.AddFirst(copyH); - cctx.Cfg.Entry.Operations.AddFirst(copyL); - - preservedArgs[index] = pArg; - } - else - { - Operand pArg = Local(dest.Type); - - Operand argReg = dest.Type.IsInteger() - ? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type) - : Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type); - - Operation copyOp = Operation(Instruction.Copy, pArg, argReg); - - cctx.Cfg.Entry.Operations.AddFirst(copyOp); - - preservedArgs[index] = pArg; - } - } - - Operation argCopyOp = Operation(Instruction.Copy, dest, preservedArgs[index]); - - Operation newNode = nodes.AddBefore(node, argCopyOp); - - Delete(nodes, node); - - return newNode; - } - else - { - // TODO: Pass on stack. - return node; - } - } - - private static void HandleReturnWindowsAbi( - CompilerContext cctx, - IntrusiveList<Operation> nodes, - Operand[] preservedArgs, - Operation node) - { - if (node.SourcesCount == 0) - { - return; - } - - Operand source = node.GetSource(0); - Operand retReg; - - if (source.Type.IsInteger()) - { - retReg = Gpr(CallingConvention.GetIntReturnRegister(), source.Type); - } - else if (source.Type == OperandType.V128) - { - if (preservedArgs[0] == default) - { - Operand preservedArg = Local(OperandType.I64); - Operand arg0 = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64); - - Operation copyOp = Operation(Instruction.Copy, preservedArg, arg0); - - cctx.Cfg.Entry.Operations.AddFirst(copyOp); - - preservedArgs[0] = preservedArg; - } - - retReg = preservedArgs[0]; - } - else - { - retReg = Xmm(CallingConvention.GetVecReturnRegister(), source.Type); - } - - if (source.Type == OperandType.V128) - { - Operation retStoreOp = Operation(Instruction.Store, default, retReg, source); - - nodes.AddBefore(node, retStoreOp); - } - else - { - Operation retCopyOp = Operation(Instruction.Copy, retReg, source); - - nodes.AddBefore(node, retCopyOp); - } - - node.SetSources(Array.Empty<Operand>()); - } - - private static void HandleReturnSystemVAbi(IntrusiveList<Operation> nodes, Operation node) - { - if (node.SourcesCount == 0) - { - return; - } - - Operand source = node.GetSource(0); - - if (source.Type == OperandType.V128) - { - Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); - Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); - - nodes.AddBefore(node, Operation(Instruction.VectorExtract, retLReg, source, Const(0))); - nodes.AddBefore(node, Operation(Instruction.VectorExtract, retHReg, source, Const(1))); - } - else - { - Operand retReg = source.Type.IsInteger() - ? Gpr(CallingConvention.GetIntReturnRegister(), source.Type) - : Xmm(CallingConvention.GetVecReturnRegister(), source.Type); - - Operation retCopyOp = Operation(Instruction.Copy, retReg, source); - - nodes.AddBefore(node, retCopyOp); - } - } - - private static Operand AddXmmCopy(IntrusiveList<Operation> nodes, Operation node, Operand source) + protected static Operand AddXmmCopy(IntrusiveList<Operation> nodes, Operation node, Operand source) { Operand temp = Local(source.Type); Operand intConst = AddCopy(nodes, node, GetIntConst(source)); @@ -1204,7 +592,7 @@ namespace ARMeilleure.CodeGen.X86 return temp; } - private static Operand AddCopy(IntrusiveList<Operation> nodes, Operation node, Operand source) + protected static Operand AddCopy(IntrusiveList<Operation> nodes, Operation node, Operand source) { Operand temp = Local(source.Type); @@ -1229,7 +617,7 @@ namespace ARMeilleure.CodeGen.X86 return value; } - private static void Delete(IntrusiveList<Operation> nodes, Operation node) + protected static void Delete(IntrusiveList<Operation> nodes, Operation node) { node.Destination = default; @@ -1241,12 +629,12 @@ namespace ARMeilleure.CodeGen.X86 nodes.Remove(node); } - private static Operand Gpr(X86Register register, OperandType type) + protected static Operand Gpr(X86Register register, OperandType type) { return Register((int)register, RegisterType.Integer, type); } - private static Operand Xmm(X86Register register, OperandType type) + protected static Operand Xmm(X86Register register, OperandType type) { return Register((int)register, RegisterType.Vector, type); } diff --git a/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs b/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs new file mode 100644 index 000000000..a84d5050d --- /dev/null +++ b/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs @@ -0,0 +1,334 @@ +using ARMeilleure.CodeGen.RegisterAllocators; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; +using static ARMeilleure.IntermediateRepresentation.Operation.Factory; + +namespace ARMeilleure.CodeGen.X86 +{ + class PreAllocatorSystemV : PreAllocator + { + public static void InsertCallCopies(IntrusiveList<Operation> nodes, Operation node) + { + Operand dest = node.Destination; + + List<Operand> sources = new List<Operand> + { + node.GetSource(0) + }; + + int argsCount = node.SourcesCount - 1; + + int intMax = CallingConvention.GetIntArgumentsOnRegsCount(); + int vecMax = CallingConvention.GetVecArgumentsOnRegsCount(); + + int intCount = 0; + int vecCount = 0; + + int stackOffset = 0; + + for (int index = 0; index < argsCount; index++) + { + Operand source = node.GetSource(index + 1); + + bool passOnReg; + + if (source.Type.IsInteger()) + { + passOnReg = intCount < intMax; + } + else if (source.Type == OperandType.V128) + { + passOnReg = intCount + 1 < intMax; + } + else + { + passOnReg = vecCount < vecMax; + } + + if (source.Type == OperandType.V128 && passOnReg) + { + // V128 is a struct, we pass each half on a GPR if possible. + Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); + + continue; + } + + if (passOnReg) + { + Operand argReg = source.Type.IsInteger() + ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) + : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); + + Operation copyOp = Operation(Instruction.Copy, argReg, source); + + InsertConstantRegCopies(nodes, nodes.AddBefore(node, copyOp)); + + sources.Add(argReg); + } + else + { + Operand offset = Const(stackOffset); + + Operation spillOp = Operation(Instruction.SpillArg, default, offset, source); + + InsertConstantRegCopies(nodes, nodes.AddBefore(node, spillOp)); + + stackOffset += source.Type.GetSizeInBytes(); + } + } + + node.SetSources(sources.ToArray()); + + if (dest != default) + { + if (dest.Type == OperandType.V128) + { + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); + + Operation operation = node; + + node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, retLReg)); + nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, retHReg, Const(1))); + + operation.Destination = default; + } + else + { + Operand retReg = dest.Type.IsInteger() + ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) + : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); + + Operation copyOp = Operation(Instruction.Copy, dest, retReg); + + nodes.AddAfter(node, copyOp); + + node.Destination = retReg; + } + } + } + + public static void InsertTailcallCopies(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) + { + List<Operand> sources = new List<Operand> + { + node.GetSource(0) + }; + + int argsCount = node.SourcesCount - 1; + + int intMax = CallingConvention.GetIntArgumentsOnRegsCount(); + int vecMax = CallingConvention.GetVecArgumentsOnRegsCount(); + + int intCount = 0; + int vecCount = 0; + + // Handle arguments passed on registers. + for (int index = 0; index < argsCount; index++) + { + Operand source = node.GetSource(1 + index); + + bool passOnReg; + + if (source.Type.IsInteger()) + { + passOnReg = intCount + 1 < intMax; + } + else + { + passOnReg = vecCount < vecMax; + } + + if (source.Type == OperandType.V128 && passOnReg) + { + // V128 is a struct, we pass each half on a GPR if possible. + Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); + + continue; + } + + if (passOnReg) + { + Operand argReg = source.Type.IsInteger() + ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type) + : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type); + + Operation copyOp = Operation(Instruction.Copy, argReg, source); + + InsertConstantRegCopies(nodes, nodes.AddBefore(node, copyOp)); + + sources.Add(argReg); + } + else + { + throw new NotImplementedException("Spilling is not currently supported for tail calls. (too many arguments)"); + } + } + + // The target address must be on the return registers, since we + // don't return anything and it is guaranteed to not be a + // callee saved register (which would be trashed on the epilogue). + Operand retReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + + Operation addrCopyOp = Operation(Instruction.Copy, retReg, node.GetSource(0)); + + nodes.AddBefore(node, addrCopyOp); + + sources[0] = retReg; + + node.SetSources(sources.ToArray()); + } + + public static Operation InsertLoadArgumentCopy( + CompilerContext cctx, + ref Span<Operation> buffer, + IntrusiveList<Operation> nodes, + Operand[] preservedArgs, + Operation node) + { + Operand source = node.GetSource(0); + + Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind."); + + int index = source.AsInt32(); + + int intCount = 0; + int vecCount = 0; + + for (int cIndex = 0; cIndex < index; cIndex++) + { + OperandType argType = cctx.FuncArgTypes[cIndex]; + + if (argType.IsInteger()) + { + intCount++; + } + else if (argType == OperandType.V128) + { + intCount += 2; + } + else + { + vecCount++; + } + } + + bool passOnReg; + + if (source.Type.IsInteger()) + { + passOnReg = intCount < CallingConvention.GetIntArgumentsOnRegsCount(); + } + else if (source.Type == OperandType.V128) + { + passOnReg = intCount + 1 < CallingConvention.GetIntArgumentsOnRegsCount(); + } + else + { + passOnReg = vecCount < CallingConvention.GetVecArgumentsOnRegsCount(); + } + + if (passOnReg) + { + Operand dest = node.Destination; + + if (preservedArgs[index] == default) + { + if (dest.Type == OperandType.V128) + { + // V128 is a struct, we pass each half on a GPR if possible. + Operand pArg = Local(OperandType.V128); + + Operand argLReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount), OperandType.I64); + Operand argHReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount + 1), OperandType.I64); + + Operation copyL = Operation(Instruction.VectorCreateScalar, pArg, argLReg); + Operation copyH = Operation(Instruction.VectorInsert, pArg, pArg, argHReg, Const(1)); + + cctx.Cfg.Entry.Operations.AddFirst(copyH); + cctx.Cfg.Entry.Operations.AddFirst(copyL); + + preservedArgs[index] = pArg; + } + else + { + Operand pArg = Local(dest.Type); + + Operand argReg = dest.Type.IsInteger() + ? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type) + : Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type); + + Operation copyOp = Operation(Instruction.Copy, pArg, argReg); + + cctx.Cfg.Entry.Operations.AddFirst(copyOp); + + preservedArgs[index] = pArg; + } + } + + Operation nextNode; + + if (dest.AssignmentsCount == 1) + { + // Let's propagate the argument if we can to avoid copies. + PreAllocatorCommon.Propagate(ref buffer, dest, preservedArgs[index]); + nextNode = node.ListNext; + } + else + { + Operation argCopyOp = Operation(Instruction.Copy, dest, preservedArgs[index]); + nextNode = nodes.AddBefore(node, argCopyOp); + } + + Delete(nodes, node); + return nextNode; + } + else + { + // TODO: Pass on stack. + return node; + } + } + + public static void InsertReturnCopy(IntrusiveList<Operation> nodes, Operation node) + { + if (node.SourcesCount == 0) + { + return; + } + + Operand source = node.GetSource(0); + + if (source.Type == OperandType.V128) + { + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); + + nodes.AddBefore(node, Operation(Instruction.VectorExtract, retLReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, retHReg, source, Const(1))); + } + else + { + Operand retReg = source.Type.IsInteger() + ? Gpr(CallingConvention.GetIntReturnRegister(), source.Type) + : Xmm(CallingConvention.GetVecReturnRegister(), source.Type); + + Operation retCopyOp = Operation(Instruction.Copy, retReg, source); + + nodes.AddBefore(node, retCopyOp); + } + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs b/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs new file mode 100644 index 000000000..45319e6a5 --- /dev/null +++ b/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs @@ -0,0 +1,327 @@ +using ARMeilleure.CodeGen.RegisterAllocators; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Diagnostics; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; +using static ARMeilleure.IntermediateRepresentation.Operation.Factory; + +namespace ARMeilleure.CodeGen.X86 +{ + class PreAllocatorWindows : PreAllocator + { + public static void InsertCallCopies(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) + { + Operand dest = node.Destination; + + // Handle struct arguments. + int retArgs = 0; + int stackAllocOffset = 0; + + int AllocateOnStack(int size) + { + // We assume that the stack allocator is initially empty (TotalSize = 0). + // Taking that into account, we can reuse the space allocated for other + // calls by keeping track of our own allocated size (stackAllocOffset). + // If the space allocated is not big enough, then we just expand it. + int offset = stackAllocOffset; + + if (stackAllocOffset + size > stackAlloc.TotalSize) + { + stackAlloc.Allocate((stackAllocOffset + size) - stackAlloc.TotalSize); + } + + stackAllocOffset += size; + + return offset; + } + + Operand arg0Reg = default; + + if (dest != default && dest.Type == OperandType.V128) + { + int stackOffset = AllocateOnStack(dest.Type.GetSizeInBytes()); + + arg0Reg = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64); + + Operation allocOp = Operation(Instruction.StackAlloc, arg0Reg, Const(stackOffset)); + + nodes.AddBefore(node, allocOp); + + retArgs = 1; + } + + int argsCount = node.SourcesCount - 1; + int maxArgs = CallingConvention.GetArgumentsOnRegsCount() - retArgs; + + if (argsCount > maxArgs) + { + argsCount = maxArgs; + } + + Operand[] sources = new Operand[1 + retArgs + argsCount]; + + sources[0] = node.GetSource(0); + + if (arg0Reg != default) + { + sources[1] = arg0Reg; + } + + for (int index = 1; index < node.SourcesCount; index++) + { + Operand source = node.GetSource(index); + + if (source.Type == OperandType.V128) + { + Operand stackAddr = Local(OperandType.I64); + + int stackOffset = AllocateOnStack(source.Type.GetSizeInBytes()); + + nodes.AddBefore(node, Operation(Instruction.StackAlloc, stackAddr, Const(stackOffset))); + + Operation storeOp = Operation(Instruction.Store, default, stackAddr, source); + + InsertConstantRegCopies(nodes, nodes.AddBefore(node, storeOp)); + + node.SetSource(index, stackAddr); + } + } + + // Handle arguments passed on registers. + for (int index = 0; index < argsCount; index++) + { + Operand source = node.GetSource(index + 1); + Operand argReg; + + int argIndex = index + retArgs; + + if (source.Type.IsInteger()) + { + argReg = Gpr(CallingConvention.GetIntArgumentRegister(argIndex), source.Type); + } + else + { + argReg = Xmm(CallingConvention.GetVecArgumentRegister(argIndex), source.Type); + } + + Operation copyOp = Operation(Instruction.Copy, argReg, source); + + InsertConstantRegCopies(nodes, nodes.AddBefore(node, copyOp)); + + sources[1 + retArgs + index] = argReg; + } + + // The remaining arguments (those that are not passed on registers) + // should be passed on the stack, we write them to the stack with "SpillArg". + for (int index = argsCount; index < node.SourcesCount - 1; index++) + { + Operand source = node.GetSource(index + 1); + Operand offset = Const((index + retArgs) * 8); + + Operation spillOp = Operation(Instruction.SpillArg, default, offset, source); + + InsertConstantRegCopies(nodes, nodes.AddBefore(node, spillOp)); + } + + if (dest != default) + { + if (dest.Type == OperandType.V128) + { + Operand retValueAddr = Local(OperandType.I64); + + nodes.AddBefore(node, Operation(Instruction.Copy, retValueAddr, arg0Reg)); + + Operation loadOp = Operation(Instruction.Load, dest, retValueAddr); + + nodes.AddAfter(node, loadOp); + + node.Destination = default; + } + else + { + Operand retReg = dest.Type.IsInteger() + ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type) + : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type); + + Operation copyOp = Operation(Instruction.Copy, dest, retReg); + + nodes.AddAfter(node, copyOp); + + node.Destination = retReg; + } + } + + node.SetSources(sources); + } + + public static void InsertTailcallCopies(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) + { + int argsCount = node.SourcesCount - 1; + int maxArgs = CallingConvention.GetArgumentsOnRegsCount(); + + if (argsCount > maxArgs) + { + throw new NotImplementedException("Spilling is not currently supported for tail calls. (too many arguments)"); + } + + Operand[] sources = new Operand[1 + argsCount]; + + // Handle arguments passed on registers. + for (int index = 0; index < argsCount; index++) + { + Operand source = node.GetSource(1 + index); + Operand argReg = source.Type.IsInteger() + ? Gpr(CallingConvention.GetIntArgumentRegister(index), source.Type) + : Xmm(CallingConvention.GetVecArgumentRegister(index), source.Type); + + Operation copyOp = Operation(Instruction.Copy, argReg, source); + + InsertConstantRegCopies(nodes, nodes.AddBefore(node, copyOp)); + + sources[1 + index] = argReg; + } + + // The target address must be on the return registers, since we + // don't return anything and it is guaranteed to not be a + // callee saved register (which would be trashed on the epilogue). + Operand retReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + + Operation addrCopyOp = Operation(Instruction.Copy, retReg, node.GetSource(0)); + + nodes.AddBefore(node, addrCopyOp); + + sources[0] = retReg; + + node.SetSources(sources); + } + + public static Operation InsertLoadArgumentCopy( + CompilerContext cctx, + ref Span<Operation> buffer, + IntrusiveList<Operation> nodes, + Operand[] preservedArgs, + Operation node) + { + Operand source = node.GetSource(0); + + Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind."); + + int retArgs = cctx.FuncReturnType == OperandType.V128 ? 1 : 0; + + int index = source.AsInt32() + retArgs; + + if (index < CallingConvention.GetArgumentsOnRegsCount()) + { + Operand dest = node.Destination; + + if (preservedArgs[index] == default) + { + Operand argReg, pArg; + + if (dest.Type.IsInteger()) + { + argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), dest.Type); + pArg = Local(dest.Type); + } + else if (dest.Type == OperandType.V128) + { + argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), OperandType.I64); + pArg = Local(OperandType.I64); + } + else + { + argReg = Xmm(CallingConvention.GetVecArgumentRegister(index), dest.Type); + pArg = Local(dest.Type); + } + + Operation copyOp = Operation(Instruction.Copy, pArg, argReg); + + cctx.Cfg.Entry.Operations.AddFirst(copyOp); + + preservedArgs[index] = pArg; + } + + Operation nextNode; + + if (dest.Type != OperandType.V128 && dest.AssignmentsCount == 1) + { + // Let's propagate the argument if we can to avoid copies. + PreAllocatorCommon.Propagate(ref buffer, dest, preservedArgs[index]); + nextNode = node.ListNext; + } + else + { + Operation argCopyOp = Operation(dest.Type == OperandType.V128 + ? Instruction.Load + : Instruction.Copy, dest, preservedArgs[index]); + + nextNode = nodes.AddBefore(node, argCopyOp); + } + + Delete(nodes, node); + return nextNode; + } + else + { + // TODO: Pass on stack. + return node; + } + } + + public static void InsertReturnCopy( + CompilerContext cctx, + IntrusiveList<Operation> nodes, + Operand[] preservedArgs, + Operation node) + { + if (node.SourcesCount == 0) + { + return; + } + + Operand source = node.GetSource(0); + Operand retReg; + + if (source.Type.IsInteger()) + { + retReg = Gpr(CallingConvention.GetIntReturnRegister(), source.Type); + } + else if (source.Type == OperandType.V128) + { + if (preservedArgs[0] == default) + { + Operand preservedArg = Local(OperandType.I64); + Operand arg0 = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64); + + Operation copyOp = Operation(Instruction.Copy, preservedArg, arg0); + + cctx.Cfg.Entry.Operations.AddFirst(copyOp); + + preservedArgs[0] = preservedArg; + } + + retReg = preservedArgs[0]; + } + else + { + retReg = Xmm(CallingConvention.GetVecReturnRegister(), source.Type); + } + + if (source.Type == OperandType.V128) + { + Operation retStoreOp = Operation(Instruction.Store, default, retReg, source); + + nodes.AddBefore(node, retStoreOp); + } + else + { + Operation retCopyOp = Operation(Instruction.Copy, retReg, source); + + nodes.AddBefore(node, retCopyOp); + } + + node.SetSources(Array.Empty<Operand>()); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index de2294b24..276ec788b 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 4328; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 4484; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; From dad9ab6bb67fd488aae26843ecda1ed59caf845b Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 11 Mar 2023 19:04:13 +0100 Subject: [PATCH 388/737] [Flatpak] Add release github workflow (#4529) * Add flatpak release workflow Co-authored-by: Mary <mary@mary.zone> * infra: Update required SDK version to 7.0.200 --------- Co-authored-by: Mary <mary@mary.zone> --- .github/workflows/flatpak.yml | 161 ++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 9 +- global.json | 2 +- 3 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/flatpak.yml diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml new file mode 100644 index 000000000..2ba37c252 --- /dev/null +++ b/.github/workflows/flatpak.yml @@ -0,0 +1,161 @@ +name: Flatpak release job + +on: + workflow_call: + inputs: + ryujinx_version: + required: true + type: string + + +concurrency: flatpak-release + +jobs: + release: + runs-on: ubuntu-latest + + env: + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages + GIT_COMMITTER_NAME: "RyujinxBot" + GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com" + RYUJINX_PROJECT_FILE: "Ryujinx/Ryujinx.csproj" + NUGET_SOURCES_DESTDIR: "nuget-sources" + RYUJINX_VERSION: ${{ inputs.ryujinx_version }} + + steps: + - uses: actions/checkout@v3 + with: + path: Ryujinx + + - uses: actions/setup-dotnet@v3 + with: + global-json-file: Ryujinx/global.json + + - name: Get version info + id: version_info + working-directory: Ryujinx + run: | + echo "git_short_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - uses: actions/checkout@v3 + with: + repository: flathub/org.ryujinx.Ryujinx + token: ${{ secrets.RYUJINX_BOT_PAT }} + submodules: recursive + path: flathub + + - name: Install dependencies + run: python -m pip install PyYAML lxml + + - name: Restore Nuget packages + run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} + + - name: Generate nuget_sources.json + shell: python + run: | + from pathlib import Path + import base64 + import binascii + import json + import os + + sources = [] + + for path in Path((os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'): + name = path.parent.parent.name + version = path.parent.name + filename = '{}.{}.nupkg'.format(name, version) + url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename) + + with path.open() as fp: + sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii') + + sources.append({ + 'type': 'file', + 'url': url, + 'sha512': sha512, + 'dest': os.environ['NUGET_SOURCES_DESTDIR'], + 'dest-filename': filename, + }) + + with open('flathub/nuget_sources.json', 'w') as fp: + json.dump(sources, fp, indent=4) + + - name: Update flatpak metadata + id: metadata + env: + RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_short_hash }} + shell: python + run: | + import hashlib + import hmac + import json + import os + import yaml + from datetime import datetime + from lxml import etree + + yaml_file = "flathub/org.ryujinx.Ryujinx.yml" + xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml" + + with open(yaml_file, "r") as f: + data = yaml.safe_load(f) + + for source in data["modules"][0]["sources"]: + if type(source) is str: + continue + if ( + source["type"] == "git" + and source["url"] == "https://github.com/Ryujinx/Ryujinx.git" + ): + source["commit"] = os.environ['RYUJINX_GIT_HASH'] + + is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION'] + + with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out: + if is_same_version: + gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}") + else: + gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}") + + if not is_same_version: + data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION'] + + with open(yaml_file, "w") as f: + yaml.safe_dump(data, f, sort_keys=False) + + parser = etree.XMLParser(remove_blank_text=True) + tree = etree.parse(xml_file, parser) + + root = tree.getroot() + + releases = root.find("releases") + + element = etree.Element("release") + element.set("version", os.environ['RYUJINX_VERSION']) + element.set("date", datetime.now().date().isoformat()) + releases.insert(0, element) + + # Ensure 4 spaces + etree.indent(root, space=" ") + + with open(xml_file, "wb") as f: + f.write( + etree.tostring( + tree, + pretty_print=True, + encoding="UTF-8", + doctype='<?xml version="1.0" encoding="UTF-8"?>', + ) + ) + + - name: Push flatpak update + working-directory: flathub + env: + COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }} + run: | + git config user.name "${{ env.GIT_COMMITTER_NAME }}" + git config user.email "${{ env.GIT_COMMITTER_EMAIL }}" + git add . + git commit -m "$COMMIT_MESSAGE" + git push origin master diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9258ff488..1802208f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,11 +25,12 @@ jobs: RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master" + steps: - uses: actions/checkout@v3 - uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + global-json-file: global.json - name: Get version info id: version_info run: | @@ -112,3 +113,9 @@ jobs: owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} token: ${{ secrets.RELEASE_TOKEN }} + + flatpak_release: + uses: ./.github/workflows/flatpak.yml + with: + ryujinx_version: ${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }} + secrets: inherit diff --git a/global.json b/global.json index 1c7274b72..39ccef0d0 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100", + "version": "7.0.200", "rollForward": "latestFeature" } } \ No newline at end of file From 954e995321da27c788f8c45ef0cc1a9aef32e062 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 11 Mar 2023 19:06:42 +0100 Subject: [PATCH 389/737] Attempt to fix syntax error of previous merge --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1802208f5..eaa7376e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,5 +117,5 @@ jobs: flatpak_release: uses: ./.github/workflows/flatpak.yml with: - ryujinx_version: ${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }} + ryujinx_version: "${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" secrets: inherit From a80fa5e33f0856ef20508e7f22101cd8fc911f90 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 11 Mar 2023 19:09:48 +0100 Subject: [PATCH 390/737] gha(release): Makes environment variables global --- .github/workflows/release.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eaa7376e9..029a90318 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,19 +13,17 @@ on: concurrency: release +env: + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + RYUJINX_BASE_VERSION: "1.1" + RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master" + RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx" + RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master" + jobs: release: runs-on: windows-latest - - env: - POWERSHELL_TELEMETRY_OPTOUT: 1 - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - RYUJINX_BASE_VERSION: "1.1" - RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master" - RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx" - RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master" - - steps: - uses: actions/checkout@v3 - uses: actions/setup-dotnet@v3 From 2dc422bc14d5f301108bad14ab732f169bbdc277 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 11 Mar 2023 19:13:40 +0100 Subject: [PATCH 391/737] gha(release): Hopefully fixes it --- .github/workflows/flatpak.yml | 33 ++++++++++++++++++--------------- .github/workflows/release.yml | 4 +++- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 2ba37c252..a05f5c118 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -3,7 +3,10 @@ name: Flatpak release job on: workflow_call: inputs: - ryujinx_version: + ryujinx_base_version: + required: true + type: string + ryujinx_version_minor: required: true type: string @@ -20,7 +23,7 @@ jobs: GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com" RYUJINX_PROJECT_FILE: "Ryujinx/Ryujinx.csproj" NUGET_SOURCES_DESTDIR: "nuget-sources" - RYUJINX_VERSION: ${{ inputs.ryujinx_version }} + RYUJINX_VERSION: "${{ inputs.ryujinx_base_version }}. ${{ inputs.ryujinx_version_minor }}" steps: - uses: actions/checkout@v3 @@ -58,9 +61,9 @@ jobs: import binascii import json import os - + sources = [] - + for path in Path((os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'): name = path.parent.parent.name version = path.parent.name @@ -94,13 +97,13 @@ jobs: import yaml from datetime import datetime from lxml import etree - + yaml_file = "flathub/org.ryujinx.Ryujinx.yml" xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml" - + with open(yaml_file, "r") as f: data = yaml.safe_load(f) - + for source in data["modules"][0]["sources"]: if type(source) is str: continue @@ -109,7 +112,7 @@ jobs: and source["url"] == "https://github.com/Ryujinx/Ryujinx.git" ): source["commit"] = os.environ['RYUJINX_GIT_HASH'] - + is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION'] with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out: @@ -117,28 +120,28 @@ jobs: gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}") else: gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}") - + if not is_same_version: data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION'] with open(yaml_file, "w") as f: yaml.safe_dump(data, f, sort_keys=False) - + parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(xml_file, parser) - + root = tree.getroot() - + releases = root.find("releases") - + element = etree.Element("release") element.set("version", os.environ['RYUJINX_VERSION']) element.set("date", datetime.now().date().isoformat()) releases.insert(0, element) - + # Ensure 4 spaces etree.indent(root, space=" ") - + with open(xml_file, "wb") as f: f.write( etree.tostring( diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 029a90318..0f979da1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -114,6 +114,8 @@ jobs: flatpak_release: uses: ./.github/workflows/flatpak.yml + needs: release with: - ryujinx_version: "${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" + ryujinx_base_version: "1.1" + ryujinx_version_minor: github.run_number secrets: inherit From 81691b9e3716c7f7f8b243f0f4ded1d90c526a4e Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 11 Mar 2023 20:11:09 +0100 Subject: [PATCH 392/737] gha(release): Attempt to fix flathub pusher --- .github/workflows/flatpak.yml | 9 +++------ .github/workflows/release.yml | 3 +-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index a05f5c118..7df990655 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -3,10 +3,7 @@ name: Flatpak release job on: workflow_call: inputs: - ryujinx_base_version: - required: true - type: string - ryujinx_version_minor: + ryujinx_version: required: true type: string @@ -23,7 +20,7 @@ jobs: GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com" RYUJINX_PROJECT_FILE: "Ryujinx/Ryujinx.csproj" NUGET_SOURCES_DESTDIR: "nuget-sources" - RYUJINX_VERSION: "${{ inputs.ryujinx_base_version }}. ${{ inputs.ryujinx_version_minor }}" + RYUJINX_VERSION: "${{ inputs.ryujinx_version }}" steps: - uses: actions/checkout@v3 @@ -64,7 +61,7 @@ jobs: sources = [] - for path in Path((os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'): + for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'): name = path.parent.parent.name version = path.parent.name filename = '{}.{}.nupkg'.format(name, version) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f979da1d..b2c5e2f01 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -116,6 +116,5 @@ jobs: uses: ./.github/workflows/flatpak.yml needs: release with: - ryujinx_base_version: "1.1" - ryujinx_version_minor: github.run_number + ryujinx_version: "1.1.${{ github.run_number }}" secrets: inherit From 23c844b2aa84a65e573dcc023d19b8f5294a8baf Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sat, 11 Mar 2023 15:05:48 -0500 Subject: [PATCH 393/737] Misc performance tweaks (#4509) * use Array.Empty() where instead of allocating new zero-length arrays * structure for loops in a way that the JIT will elide array/Span bounds checking * avoiding function calls in for loop condition tests * avoid LINQ in a hot path * conform with code style * fix mistake in GetNextWaitingObject() * fix GetNextWaitingObject() possibility of returning null if all list items have TimePoint == long.MaxValue * make GetNextWaitingObject() behave FIFO behavior for multiple items with the same TimePoint --- ARMeilleure/CodeGen/Arm64/CodeGenContext.cs | 2 +- .../RegisterAllocators/LinearScanAllocator.cs | 9 ++------ ARMeilleure/Decoders/DecoderHelper.cs | 4 ++-- ARMeilleure/Decoders/OpCodeTable.cs | 6 ++--- Ryujinx.Audio/Common/AudioDeviceSession.cs | 4 +++- Ryujinx.Ava/UI/Windows/IconColorPicker.cs | 2 +- Ryujinx.Graphics.Vulkan/Queries/Counters.cs | 2 +- Ryujinx.Graphics.Vulkan/SpecInfo.cs | 5 ++-- Ryujinx.Graphics.Vulkan/Window.cs | 5 ++-- Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs | 4 ++-- Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 5 ++-- Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 23 +++++++++++++++++-- .../Time/TimeZone/TimeZoneContentManager.cs | 2 +- Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 4 +++- .../MockVirtualMemoryManager.cs | 2 +- 15 files changed, 48 insertions(+), 31 deletions(-) diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs index 1ddde0c19..cebfbde12 100644 --- a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs +++ b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs @@ -265,7 +265,7 @@ namespace ARMeilleure.CodeGen.Arm64 } else { - relocInfo = new RelocInfo(new RelocEntry[0]); + relocInfo = new RelocInfo(Array.Empty<RelocEntry>()); } return (code, relocInfo); diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index 6ea62c28b..d80157afb 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -433,16 +433,11 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private static int GetHighestValueIndex(Span<int> span) { - int highest = span[0]; - - if (highest == int.MaxValue) - { - return 0; - } + int highest = int.MinValue; int selected = 0; - for (int index = 1; index < span.Length; index++) + for (int index = 0; index < span.Length; index++) { int current = span[index]; diff --git a/ARMeilleure/Decoders/DecoderHelper.cs b/ARMeilleure/Decoders/DecoderHelper.cs index 38f98c39c..5227e6a19 100644 --- a/ARMeilleure/Decoders/DecoderHelper.cs +++ b/ARMeilleure/Decoders/DecoderHelper.cs @@ -17,7 +17,7 @@ namespace ARMeilleure.Decoders { uint[] tbl = new uint[256]; - for (int idx = 0; idx < 256; idx++) + for (int idx = 0; idx < tbl.Length; idx++) { tbl[idx] = ExpandImm8ToFP32((uint)idx); } @@ -29,7 +29,7 @@ namespace ARMeilleure.Decoders { ulong[] tbl = new ulong[256]; - for (int idx = 0; idx < 256; idx++) + for (int idx = 0; idx < tbl.Length; idx++) { tbl[idx] = ExpandImm8ToFP64((ulong)idx); } diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index 54abb1418..8464ce556 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -1301,7 +1301,7 @@ namespace ARMeilleure.Decoders { List<InstInfo>[] temp = new List<InstInfo>[FastLookupSize]; - for (int index = 0; index < FastLookupSize; index++) + for (int index = 0; index < temp.Length; index++) { temp[index] = new List<InstInfo>(); } @@ -1311,7 +1311,7 @@ namespace ARMeilleure.Decoders int mask = ToFastLookupIndex(inst.Mask); int value = ToFastLookupIndex(inst.Value); - for (int index = 0; index < FastLookupSize; index++) + for (int index = 0; index < temp.Length; index++) { if ((index & mask) == value) { @@ -1320,7 +1320,7 @@ namespace ARMeilleure.Decoders } } - for (int index = 0; index < FastLookupSize; index++) + for (int index = 0; index < temp.Length; index++) { table[index] = temp[index].ToArray(); } diff --git a/Ryujinx.Audio/Common/AudioDeviceSession.cs b/Ryujinx.Audio/Common/AudioDeviceSession.cs index 07b0a8988..0191f7ccd 100644 --- a/Ryujinx.Audio/Common/AudioDeviceSession.cs +++ b/Ryujinx.Audio/Common/AudioDeviceSession.cs @@ -400,7 +400,9 @@ namespace Ryujinx.Audio.Common { uint bufferIndex = (_releasedBufferIndex - _bufferReleasedCount) % Constants.AudioDeviceBufferCountMax; - for (int i = 0; i < GetTotalBufferCount(); i++) + uint totalBufferCount = GetTotalBufferCount(); + + for (int i = 0; i < totalBufferCount; i++) { if (_buffers[bufferIndex].BufferTag == bufferTag) { diff --git a/Ryujinx.Ava/UI/Windows/IconColorPicker.cs b/Ryujinx.Ava/UI/Windows/IconColorPicker.cs index 9dca83eb6..a4c6287f3 100644 --- a/Ryujinx.Ava/UI/Windows/IconColorPicker.cs +++ b/Ryujinx.Ava/UI/Windows/IconColorPicker.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Ava.UI.Windows public static Bgra32[] GetBuffer(Image<Bgra32> image) { - return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : new Bgra32[0]; + return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : Array.Empty<Bgra32>(); } private static int GetColorScore(Dictionary<int, int> dominantColorBin, int maxHitCount, PaletteColor color) diff --git a/Ryujinx.Graphics.Vulkan/Queries/Counters.cs b/Ryujinx.Graphics.Vulkan/Queries/Counters.cs index 7113d0601..d9d65062f 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/Counters.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/Counters.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries _counterQueues = new CounterQueue[count]; - for (int index = 0; index < count; index++) + for (int index = 0; index < _counterQueues.Length; index++) { CounterType type = (CounterType)index; _counterQueues[index] = new CounterQueue(gd, device, pipeline, type); diff --git a/Ryujinx.Graphics.Vulkan/SpecInfo.cs b/Ryujinx.Graphics.Vulkan/SpecInfo.cs index 83a34cde0..4d226f615 100644 --- a/Ryujinx.Graphics.Vulkan/SpecInfo.cs +++ b/Ryujinx.Graphics.Vulkan/SpecInfo.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan uint structSize = 0; - for (int i = 0; i < count; ++i) + for (int i = 0; i < Map.Length; ++i) { var typeSize = SizeOf(description[i].Type); Map[i] = new SpecializationMapEntry(description[i].Id, structSize, typeSize); @@ -46,11 +46,10 @@ namespace Ryujinx.Graphics.Vulkan // For advanced mapping with overlapping or staggered fields public SpecDescription(SpecializationMapEntry[] map) { - int count = map.Length; Map = map; uint structSize = 0; - for (int i = 0; i < count; ++i) + for (int i = 0; i < map.Length; ++i) { structSize = Math.Max(structSize, map[i].Offset + (uint)map[i].Size); } diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 5d6def3a9..075d1b303 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -60,10 +60,9 @@ namespace Ryujinx.Graphics.Vulkan private void RecreateSwapchain() { var oldSwapchain = _swapchain; - int imageCount = _swapchainImageViews.Length; _vsyncModeChanged = false; - for (int i = 0; i < imageCount; i++) + for (int i = 0; i < _swapchainImageViews.Length; i++) { _swapchainImageViews[i].Dispose(); } @@ -147,7 +146,7 @@ namespace Ryujinx.Graphics.Vulkan _swapchainImageViews = new Auto<DisposableImageView>[imageCount]; - for (int i = 0; i < imageCount; i++) + for (int i = 0; i < _swapchainImageViews.Length; i++) { _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format); } diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs index 439590e0c..e6ed46138 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs @@ -49,12 +49,12 @@ namespace Ryujinx.HLE.HOS.Ipc public static IpcHandleDesc MakeCopy(params int[] handles) { - return new IpcHandleDesc(handles, new int[0]); + return new IpcHandleDesc(handles, Array.Empty<int>()); } public static IpcHandleDesc MakeMove(params int[] handles) { - return new IpcHandleDesc(new int[0], handles); + return new IpcHandleDesc(Array.Empty<int>(), handles); } public byte[] GetBytes() diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 09e237fe5..55044da40 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -132,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Ipc word0 |= (ReceiveBuff.Count & 0xf) << 24; word0 |= (ExchangeBuff.Count & 0xf) << 28; - byte[] handleData = new byte[0]; + byte[] handleData = Array.Empty<byte>(); if (HandleDesc != null) { @@ -202,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Ipc word0 |= (ReceiveBuff.Count & 0xf) << 24; word0 |= (ExchangeBuff.Count & 0xf) << 28; - byte[] handleData = new byte[0]; + byte[] handleData = Array.Empty<byte>(); if (HandleDesc != null) { diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs index 020048f4e..030a314f7 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs @@ -1,7 +1,6 @@ using Ryujinx.Common; using System; using System.Collections.Generic; -using System.Linq; using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.Common @@ -86,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 0); - next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault(); + next = GetNextWaitingObject(); } if (next != null) @@ -140,6 +139,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Common } } + private WaitingObject GetNextWaitingObject() + { + WaitingObject selected = null; + + long lowestTimePoint = long.MaxValue; + + for (int index = _waitingObjects.Count - 1; index >= 0; index--) + { + WaitingObject current = _waitingObjects[index]; + + if (current.TimePoint <= lowestTimePoint) + { + selected = current; + lowestTimePoint = current.TimePoint; + } + } + + return selected; + } + public static long ConvertNanosecondsToMilliseconds(long time) { time /= 1000000; diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs index 69ed56d45..9367024e4 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs @@ -233,7 +233,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone // If the location name is too long, error out. if (locationName.Length > 0x24) { - outLocationNameArray = new string[0]; + outLocationNameArray = Array.Empty<string>(); return ResultCode.LocationNameTooLong; } diff --git a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index b20a76b84..d4086a105 100644 --- a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -27,7 +27,9 @@ namespace Ryujinx.Input.SDL2 SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected; // Add already connected gamepads - for (int joystickIndex = 0; joystickIndex < SDL_NumJoysticks(); joystickIndex++) + int numJoysticks = SDL_NumJoysticks(); + + for (int joystickIndex = 0; joystickIndex < numJoysticks; joystickIndex++) { HandleJoyStickConnected(joystickIndex, SDL_JoystickGetDeviceInstanceID(joystickIndex)); } diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 6729f4a36..ef81a4615 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -78,7 +78,7 @@ namespace Ryujinx.Memory.Tests IEnumerable<MemoryRange> IVirtualMemoryManager.GetPhysicalRegions(ulong va, ulong size) { - return NoMappings ? new MemoryRange[0] : new MemoryRange[] { new MemoryRange(va, size) }; + return NoMappings ? Array.Empty<MemoryRange>() : new MemoryRange[] { new MemoryRange(va, size) }; } public bool IsMapped(ulong va) From d56d335c0bcd37672debd30896d47694fb29f905 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 12 Mar 2023 03:24:11 +0100 Subject: [PATCH 394/737] misc: Some dependencies cleanup (#4507) * Remove dependencies on libraries provided by .NET standard library * Use System.IO.Hashing instead of Crc32.NET --- Directory.Packages.props | 5 +---- .../Ryujinx.Graphics.Vulkan.csproj | 3 --- Ryujinx.Input/Motion/CemuHook/Client.cs | 15 +++++++-------- Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs | 9 +++++---- Ryujinx.Input/Ryujinx.Input.csproj | 2 +- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5de9461a1..953591003 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,7 +12,6 @@ <PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="Concentus" Version="1.1.7" /> - <PackageVersion Include="Crc32.NET" Version="1.2.0" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> <PackageVersion Include="DynamicData" Version="7.12.11" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> @@ -45,10 +44,8 @@ <PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" /> - <PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> + <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" /> - <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> - <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> </ItemGroup> diff --git a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index 57e2240a7..20216e51e 100644 --- a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -29,9 +29,6 @@ <PackageReference Include="Silk.NET.Vulkan" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" /> - <PackageReference Include="System.IO.FileSystem.Primitives" /> - <PackageReference Include="System.Net.NameResolution" /> - <PackageReference Include="System.Threading.ThreadPool" /> </ItemGroup> <ItemGroup> diff --git a/Ryujinx.Input/Motion/CemuHook/Client.cs b/Ryujinx.Input/Motion/CemuHook/Client.cs index ecaf26f03..f5f7b8643 100644 --- a/Ryujinx.Input/Motion/CemuHook/Client.cs +++ b/Ryujinx.Input/Motion/CemuHook/Client.cs @@ -1,5 +1,4 @@ -using Force.Crc32; -using Ryujinx.Common; +using Ryujinx.Common; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller.Motion; @@ -9,6 +8,7 @@ using Ryujinx.Input.Motion.CemuHook.Protocol; using System; using System.Collections.Generic; using System.IO; +using System.IO.Hashing; using System.Net; using System.Net.Sockets; using System.Numerics; @@ -401,10 +401,10 @@ namespace Ryujinx.Input.Motion.CemuHook writer.Seek(6, SeekOrigin.Begin); writer.Write(header.Length); - header.Crc32 = Crc32Algorithm.Compute(stream.ToArray()); + Crc32.Hash(stream.ToArray(), header.Crc32.AsSpan()); writer.Seek(8, SeekOrigin.Begin); - writer.Write(header.Crc32); + writer.Write(header.Crc32.AsSpan()); byte[] data = stream.ToArray(); @@ -440,10 +440,10 @@ namespace Ryujinx.Input.Motion.CemuHook writer.Seek(6, SeekOrigin.Begin); writer.Write(header.Length); - header.Crc32 = Crc32Algorithm.Compute(stream.ToArray()); + Crc32.Hash(stream.ToArray(), header.Crc32.AsSpan()); writer.Seek(8, SeekOrigin.Begin); - writer.Write(header.Crc32); + writer.Write(header.Crc32.AsSpan()); byte[] data = stream.ToArray(); @@ -458,8 +458,7 @@ namespace Ryujinx.Input.Motion.CemuHook Id = (uint)clientId, MagicString = Magic, Version = Version, - Length = 0, - Crc32 = 0 + Length = 0 }; return header; diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs b/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs index 94cf4bb67..57f58ff03 100644 --- a/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs +++ b/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs @@ -1,14 +1,15 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.Input.Motion.CemuHook.Protocol { [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct Header { - public uint MagicString; + public uint MagicString; public ushort Version; public ushort Length; - public uint Crc32; - public uint Id; + public Array4<byte> Crc32; + public uint Id; } } \ No newline at end of file diff --git a/Ryujinx.Input/Ryujinx.Input.csproj b/Ryujinx.Input/Ryujinx.Input.csproj index 40b82246c..df462734f 100644 --- a/Ryujinx.Input/Ryujinx.Input.csproj +++ b/Ryujinx.Input/Ryujinx.Input.csproj @@ -6,7 +6,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Crc32.NET" /> + <PackageReference Include="System.IO.Hashing" /> </ItemGroup> <ItemGroup> From c09c0c002d5bb12218bbecc02d39de84ae773793 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 12 Mar 2023 10:42:33 +0100 Subject: [PATCH 395/737] [Flatpak] Beautify multiline strings again & Add full git commit hash (#4535) * Don't destroy multiline strings * Use full git commit hash --- .github/workflows/flatpak.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 7df990655..ee0cc6f92 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -35,7 +35,7 @@ jobs: id: version_info working-directory: Ryujinx run: | - echo "git_short_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - uses: actions/checkout@v3 with: @@ -84,7 +84,7 @@ jobs: - name: Update flatpak metadata id: metadata env: - RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_short_hash }} + RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }} shell: python run: | import hashlib @@ -94,7 +94,17 @@ jobs: import yaml from datetime import datetime from lxml import etree - + + + # Ensure we don't destroy multiline strings + def str_presenter(dumper, data): + if len(data.splitlines()) > 1: + return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|") + return dumper.represent_scalar("tag:yaml.org,2002:str", data) + + + yaml.representer.SafeRepresenter.add_representer(str, str_presenter) + yaml_file = "flathub/org.ryujinx.Ryujinx.yml" xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml" From eed17f963efd978d01a7fe11f187f5470bbe67a2 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Sun, 12 Mar 2023 16:20:09 +0000 Subject: [PATCH 396/737] Increase access permissions for Ava timezones (#4538) --- Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 7045c9ed3..cbba7fb90 100644 --- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -236,7 +236,7 @@ namespace Ryujinx.Ava.UI.ViewModels public DateTimeOffset DateOffset { get; set; } public TimeSpan TimeOffset { get; set; } - private AvaloniaList<TimeZone> TimeZones { get; set; } + internal AvaloniaList<TimeZone> TimeZones { get; set; } public AvaloniaList<string> GameDirectories { get; set; } public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; } From 05a41b31bc4d2b973662ac370458697c0bf712a9 Mon Sep 17 00:00:00 2001 From: TimeZlicer <llihder@gmail.com> Date: Mon, 13 Mar 2023 00:21:21 +0800 Subject: [PATCH 397/737] Misc: Support space in path on macOS distribution (#4462) * . * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * wildcard(*) needs to be outside of quotes(") for cp to work --------- Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- distribution/macos/create_app_bundle.sh | 34 ++++++------ distribution/macos/create_macos_release.sh | 64 +++++++++++----------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 61a2bcb7e..325ceb2dc 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -13,7 +13,7 @@ </PropertyGroup> <Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))"> - <Exec Command="codesign --entitlements $(ProjectDir)..\distribution\macos\entitlements.xml -f --deep -s $(SigningCertificate) $(TargetDir)$(TargetName)" /> + <Exec Command="codesign --entitlements '$(ProjectDir)..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" /> </Target> <PropertyGroup Condition="'$(RuntimeIdentifier)' != ''"> diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh index b62f3491e..0886bc244 100755 --- a/distribution/macos/create_app_bundle.sh +++ b/distribution/macos/create_app_bundle.sh @@ -6,31 +6,31 @@ PUBLISH_DIRECTORY=$1 OUTPUT_DIRECTORY=$2 ENTITLEMENTS_FILE_PATH=$3 -APP_BUNDLE_DIRECTORY=$OUTPUT_DIRECTORY/Ryujinx.app +APP_BUNDLE_DIRECTORY="$OUTPUT_DIRECTORY/Ryujinx.app" -rm -rf $APP_BUNDLE_DIRECTORY -mkdir -p $APP_BUNDLE_DIRECTORY/Contents -mkdir $APP_BUNDLE_DIRECTORY/Contents/Frameworks -mkdir $APP_BUNDLE_DIRECTORY/Contents/MacOS -mkdir $APP_BUNDLE_DIRECTORY/Contents/Resources +rm -rf "$APP_BUNDLE_DIRECTORY" +mkdir -p "$APP_BUNDLE_DIRECTORY/Contents" +mkdir "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" +mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS" +mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources" # Copy executables first -cp $PUBLISH_DIRECTORY/Ryujinx.Ava $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx -chmod u+x $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx +cp "$PUBLISH_DIRECTORY/Ryujinx.Ava" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx" +chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx" # Then all libraries -cp $PUBLISH_DIRECTORY/*.dylib $APP_BUNDLE_DIRECTORY/Contents/Frameworks +cp "$PUBLISH_DIRECTORY"/*.dylib "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" # Then resources -cp Info.plist $APP_BUNDLE_DIRECTORY/Contents -cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns -cp updater.sh $APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh -cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources +cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents" +cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns" +cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh" +cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources" -echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo +echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo" # Fixup libraries and executable -python3 bundle_fix_up.py $APP_BUNDLE_DIRECTORY MacOS/Ryujinx +python3 bundle_fix_up.py "$APP_BUNDLE_DIRECTORY" MacOS/Ryujinx # Now sign it if ! [ -x "$(command -v codesign)" ]; @@ -44,9 +44,9 @@ then # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" echo "Usign rcodesign for ad-hoc signing" - rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $APP_BUNDLE_DIRECTORY + rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY" else echo "Usign codesign for ad-hoc signing" - codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $APP_BUNDLE_DIRECTORY + codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY" fi diff --git a/distribution/macos/create_macos_release.sh b/distribution/macos/create_macos_release.sh index d979ec8f0..be56c135e 100755 --- a/distribution/macos/create_macos_release.sh +++ b/distribution/macos/create_macos_release.sh @@ -7,54 +7,54 @@ if [ "$#" -ne 6 ]; then exit 1 fi -mkdir -p $1 -mkdir -p $2 -mkdir -p $3 +mkdir -p "$1" +mkdir -p "$2" +mkdir -p "$3" -BASE_DIR=$(readlink -f $1) -TEMP_DIRECTORY=$(readlink -f $2) -OUTPUT_DIRECTORY=$(readlink -f $3) -ENTITLEMENTS_FILE_PATH=$(readlink -f $4) +BASE_DIR=$(readlink -f "$1") +TEMP_DIRECTORY=$(readlink -f "$2") +OUTPUT_DIRECTORY=$(readlink -f "$3") +ENTITLEMENTS_FILE_PATH=$(readlink -f "$4") VERSION=$5 SOURCE_REVISION_ID=$6 RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar -ARM64_APP_BUNDLE=$TEMP_DIRECTORY/output_arm64/Ryujinx.app -X64_APP_BUNDLE=$TEMP_DIRECTORY/output_x64/Ryujinx.app -UNIVERSAL_APP_BUNDLE=$OUTPUT_DIRECTORY/Ryujinx.app +ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app" +X64_APP_BUNDLE="$TEMP_DIRECTORY/output_x64/Ryujinx.app" +UNIVERSAL_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app" EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx -rm -rf $TEMP_DIRECTORY -mkdir -p $TEMP_DIRECTORY +rm -rf "$TEMP_DIRECTORY" +mkdir -p "$TEMP_DIRECTORY" DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true" dotnet restore dotnet build -c Release Ryujinx.Ava -dotnet publish -c Release -r osx-arm64 -o $TEMP_DIRECTORY/publish_arm64 $DOTNET_COMMON_ARGS Ryujinx.Ava -dotnet publish -c Release -r osx-x64 -o $TEMP_DIRECTORY/publish_x64 $DOTNET_COMMON_ARGS Ryujinx.Ava +dotnet publish -c Release -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS Ryujinx.Ava +dotnet publish -c Release -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $DOTNET_COMMON_ARGS Ryujinx.Ava # Get ride of the support library for ARMeilleur for x64 (that's only for arm64) -rm -rf $TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib +rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib" # Get ride of libsoundio from arm64 builds as we don't have a arm64 variant # TODO: remove this once done -rm -rf $TEMP_DIRECTORY/publish_arm64/libsoundio.dylib +rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib" -pushd $BASE_DIR/distribution/macos -./create_app_bundle.sh $TEMP_DIRECTORY/publish_x64 $TEMP_DIRECTORY/output_x64 $ENTITLEMENTS_FILE_PATH -./create_app_bundle.sh $TEMP_DIRECTORY/publish_arm64 $TEMP_DIRECTORY/output_arm64 $ENTITLEMENTS_FILE_PATH +pushd "$BASE_DIR/distribution/macos" +./create_app_bundle.sh "$TEMP_DIRECTORY/publish_x64" "$TEMP_DIRECTORY/output_x64" "$ENTITLEMENTS_FILE_PATH" +./create_app_bundle.sh "$TEMP_DIRECTORY/publish_arm64" "$TEMP_DIRECTORY/output_arm64" "$ENTITLEMENTS_FILE_PATH" popd -rm -rf $UNIVERSAL_APP_BUNDLE -mkdir -p $OUTPUT_DIRECTORY +rm -rf "$UNIVERSAL_APP_BUNDLE" +mkdir -p "$OUTPUT_DIRECTORY" # Let's copy one of the two different app bundle and remove the executable -cp -R $ARM64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE -rm $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH +cp -R "$ARM64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" +rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" # Make it libraries universal -python3 $BASE_DIR/distribution/macos/construct_universal_dylib.py $ARM64_APP_BUNDLE $X64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE "**/*.dylib" +python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_BUNDLE" "$X64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" "**/*.dylib" if ! [ -x "$(command -v lipo)" ]; then @@ -69,12 +69,12 @@ else fi # Make it the executable universal -$LIPO $ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH $X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH -output $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH -create +$LIPO "$ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" "$X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -create # Patch up the Info.plist to have appropriate version -sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist -sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist -rm $UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck +sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist" +sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist" +rm "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck" # Now sign it if ! [ -x "$(command -v codesign)" ]; @@ -88,16 +88,16 @@ then # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" echo "Usign rcodesign for ad-hoc signing" - rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $UNIVERSAL_APP_BUNDLE + rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE" else echo "Usign codesign for ad-hoc signing" - codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $UNIVERSAL_APP_BUNDLE + codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE" fi echo "Creating archive" -pushd $OUTPUT_DIRECTORY +pushd "$OUTPUT_DIRECTORY" tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf $RELEASE_TAR_FILE_NAME Ryujinx.app 1> /dev/null -python3 $BASE_DIR/distribution/misc/add_tar_exec.py $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx" +python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx" gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz rm $RELEASE_TAR_FILE_NAME popd From 6e9bd4de138e6ddedef3d38d711d161a0e400d5c Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sun, 12 Mar 2023 17:01:15 +0000 Subject: [PATCH 398/737] GPU: Scale counter results before addition (#4471) * GPU: Scale counter results before addition Counter results were being scaled on ReportCounter, which meant that the _total_ value of the counter was being scaled. Not only could this result in very large numbers and weird overflows if the game doesn't clear the counter, but it also caused the result to change drastically. This PR changes scaling to be done when the value is added to the counter on the backend. This should evaluate the scale at the same time as before, on report counter, but avoiding the issue with scaling the total. Fixes scaling in Warioware, at least in the demo, where it seems to compare old/new counters and broke down when scaling was enabled. * Fix issues when result is partially uploaded. Drivers tend to write the low half first, then the high half. Retry if the high half is FFFFFFFF. --- .../Engine/Threed/SemaphoreUpdater.cs | 11 ----------- Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 2 +- Ryujinx.Graphics.OpenGL/Pipeline.cs | 10 ++++++++++ Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs | 15 +++++++++++---- Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs | 8 ++++++-- .../Queries/CounterQueueEvent.cs | 7 +++++-- Ryujinx.Graphics.OpenGL/Queries/Counters.cs | 4 ++-- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 10 ++++++++++ Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs | 13 ++++++++++--- Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs | 2 +- .../Queries/CounterQueueEvent.cs | 7 +++++-- 11 files changed, 61 insertions(+), 28 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs index f52f9f814..63a2c841c 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs @@ -152,21 +152,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ulong ticks = _context.GetTimestamp(); - float divisor = type switch - { - ReportCounterType.SamplesPassed => _channel.TextureManager.RenderTargetScale * _channel.TextureManager.RenderTargetScale, - _ => 1f - }; - ICounterEvent counter = null; void resultHandler(object evt, ulong result) { - if (divisor != 1f) - { - result = (ulong)MathF.Ceiling(result / divisor); - } - CounterData counterData = new CounterData { Counter = result, diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 9490684cd..c79700fc2 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.OpenGL } _pipeline.Initialize(this); - _counters.Initialize(); + _counters.Initialize(_pipeline); // This is required to disable [0, 1] clamping for SNorm outputs on compatibility profiles. // This call is expected to fail if we're running with a core profile, diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 970feea0c..62996bd0a 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -773,6 +773,16 @@ namespace Ryujinx.Graphics.OpenGL _tfEnabled = false; } + public double GetCounterDivisor(CounterType type) + { + if (type == CounterType.SamplesPassed) + { + return _renderScale[0].X * _renderScale[0].X; + } + + return 1; + } + public void SetAlphaTest(bool enable, float reference, CompareOp op) { if (!enable) diff --git a/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs index 027495cdd..99ea6a5c7 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs @@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries { private const int MaxQueryRetries = 5000; private const long DefaultValue = -1; + private const ulong HighMask = 0xFFFFFFFF00000000; public int Query { get; } @@ -63,11 +64,17 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } + private bool WaitingForValue(long data) + { + return data == DefaultValue || + ((ulong)data & HighMask) == (unchecked((ulong)DefaultValue) & HighMask); + } + public bool TryGetResult(out long result) { result = Marshal.ReadInt64(_bufferMap); - return result != DefaultValue; + return WaitingForValue(result); } public long AwaitResult(AutoResetEvent wakeSignal = null) @@ -76,7 +83,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries if (wakeSignal == null) { - while (data == DefaultValue) + while (WaitingForValue(data)) { data = Marshal.ReadInt64(_bufferMap); } @@ -84,10 +91,10 @@ namespace Ryujinx.Graphics.OpenGL.Queries else { int iterations = 0; - while (data == DefaultValue && iterations++ < MaxQueryRetries) + while (WaitingForValue(data) && iterations++ < MaxQueryRetries) { data = Marshal.ReadInt64(_bufferMap); - if (data == DefaultValue) + if (WaitingForValue(data)) { wakeSignal.WaitOne(1); } diff --git a/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index 84b2bfdc9..e0aafa6fa 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.OpenGL.Queries public CounterType Type { get; } public bool Disposed { get; private set; } + private readonly Pipeline _pipeline; + private Queue<CounterQueueEvent> _events = new Queue<CounterQueueEvent>(); private CounterQueueEvent _current; @@ -28,10 +30,12 @@ namespace Ryujinx.Graphics.OpenGL.Queries private Thread _consumerThread; - internal CounterQueue(CounterType type) + internal CounterQueue(Pipeline pipeline, CounterType type) { Type = type; + _pipeline = pipeline; + QueryTarget glType = GetTarget(Type); _queryPool = new Queue<BufferedQuery>(QueryPoolInitialSize); @@ -119,7 +123,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries _current.ReserveForHostAccess(); } - _current.Complete(draws > 0); + _current.Complete(draws > 0, _pipeline.GetCounterDivisor(Type)); _events.Enqueue(_current); _current.OnResult += resultHandler; diff --git a/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs b/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs index 81451389c..7297baab5 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries private object _lock = new object(); private ulong _result = ulong.MaxValue; + private double _divisor = 1f; public CounterQueueEvent(CounterQueue queue, QueryTarget type, ulong drawIndex) { @@ -45,9 +46,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries ClearCounter = true; } - internal void Complete(bool withResult) + internal void Complete(bool withResult, double divisor) { _counter.End(withResult); + + _divisor = divisor; } internal bool TryConsume(ref ulong result, bool block, AutoResetEvent wakeSignal = null) @@ -78,7 +81,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } - result += (ulong)queryResult; + result += _divisor == 1 ? (ulong)queryResult : (ulong)Math.Ceiling(queryResult / _divisor); _result = result; diff --git a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index ebfd899c5..e6c7ab2bd 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -14,12 +14,12 @@ namespace Ryujinx.Graphics.OpenGL.Queries _counterQueues = new CounterQueue[count]; } - public void Initialize() + public void Initialize(Pipeline pipeline) { for (int index = 0; index < _counterQueues.Length; index++) { CounterType type = (CounterType)index; - _counterQueues[index] = new CounterQueue(type); + _counterQueues[index] = new CounterQueue(pipeline, type); } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 583bb9539..3abab0657 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -684,6 +684,16 @@ namespace Ryujinx.Graphics.Vulkan _tfEnabled = false; } + public double GetCounterDivisor(CounterType type) + { + if (type == CounterType.SamplesPassed) + { + return _renderScale[0].X * _renderScale[0].X; + } + + return 1; + } + public bool IsCommandBufferActive(CommandBuffer cb) { return CommandBuffer.Handle == cb.Handle; diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index 29efd8e74..861155a30 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private const int MaxQueryRetries = 5000; private const long DefaultValue = -1; private const long DefaultValueInt = 0xFFFFFFFF; + private const ulong HighMask = 0xFFFFFFFF00000000; private readonly Vk _api; private readonly Device _device; @@ -125,6 +126,12 @@ namespace Ryujinx.Graphics.Vulkan.Queries } } + private bool WaitingForValue(long data) + { + return data == _defaultValue || + (!_result32Bit && ((ulong)data & HighMask) == ((ulong)_defaultValue & HighMask)); + } + public bool TryGetResult(out long result) { result = Marshal.ReadInt64(_bufferMap); @@ -138,7 +145,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries if (wakeSignal == null) { - while (data == _defaultValue) + while (WaitingForValue(data)) { data = Marshal.ReadInt64(_bufferMap); } @@ -146,10 +153,10 @@ namespace Ryujinx.Graphics.Vulkan.Queries else { int iterations = 0; - while (data == _defaultValue && iterations++ < MaxQueryRetries) + while (WaitingForValue(data) && iterations++ < MaxQueryRetries) { data = Marshal.ReadInt64(_bufferMap); - if (data == _defaultValue) + if (WaitingForValue(data)) { wakeSignal.WaitOne(1); } diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index 7293b74f9..c30d91c42 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries _current.ReserveForHostAccess(); } - _current.Complete(draws > 0 && Type != CounterType.TransformFeedbackPrimitivesWritten); + _current.Complete(draws > 0 && Type != CounterType.TransformFeedbackPrimitivesWritten, _pipeline.GetCounterDivisor(Type)); _events.Enqueue(_current); _current.OnResult += resultHandler; diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs b/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs index 6b780ba34..d3aedb2f3 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private object _lock = new object(); private ulong _result = ulong.MaxValue; + private double _divisor = 1f; public CounterQueueEvent(CounterQueue queue, CounterType type, ulong drawIndex) { @@ -52,9 +53,11 @@ namespace Ryujinx.Graphics.Vulkan.Queries ClearCounter = true; } - internal void Complete(bool withResult) + internal void Complete(bool withResult, double divisor) { _counter.End(withResult); + + _divisor = divisor; } internal bool TryConsume(ref ulong result, bool block, AutoResetEvent wakeSignal = null) @@ -85,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries } } - result += (ulong)queryResult; + result += _divisor == 1 ? (ulong)queryResult : (ulong)Math.Ceiling(queryResult / _divisor); _result = result; From eafcc314a96f2dc893291c6268ad00278159762f Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Tue, 14 Mar 2023 16:04:38 +0000 Subject: [PATCH 399/737] Ava UI: `DownloadableContentManager` Refactor (#4300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Start refactor * Move around functions * It builds * Menu opens * Buttons * Fix overlapping text * SaveAndClose and Close buttons * Remove button * Layout * It’s a little funky but it works * Enable all/disable all buttons * Fix UpdateCount desyncs * Search bar * Search by title id * Fix fuck ups * Fix selection mode * Update Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Update Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Update Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Fix search bar * Log corrupted DLC json * Fix LibHac changes --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- Ryujinx.Ava/Assets/Locales/en_US.json | 4 +- .../UI/Models/DownloadableContentModel.cs | 3 + .../DownloadableContentManagerViewModel.cs | 340 ++++++++++++++++++ .../UI/ViewModels/MainWindowViewModel.cs | 2 +- .../DownloadableContentManagerWindow.axaml | 280 ++++++++------- .../DownloadableContentManagerWindow.axaml.cs | 335 ++++------------- 6 files changed, 565 insertions(+), 399 deletions(-) create mode 100644 Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index db8d24241..fb8f800c0 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -583,10 +583,10 @@ "SelectUpdateDialogTitle": "Select update files", "UserProfileWindowTitle": "User Profiles Manager", "CheatWindowTitle": "Cheats Manager", - "DlcWindowTitle": "Downloadable Content Manager", + "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", "UpdateWindowTitle": "Title Update Manager", "CheatWindowHeading": "Cheats Available for {0} [{1}]", - "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})", + "DlcWindowHeading": "{0} Downloadable Content(s)", "UserProfilesEditProfile": "Edit Selected", "Cancel": "Cancel", "Save": "Save", diff --git a/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs b/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs index 3070fc029..b2ad0d31d 100644 --- a/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs +++ b/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs @@ -1,4 +1,5 @@ using Ryujinx.Ava.UI.ViewModels; +using System.IO; namespace Ryujinx.Ava.UI.Models { @@ -21,6 +22,8 @@ namespace Ryujinx.Ava.UI.Models public string ContainerPath { get; } public string FullPath { get; } + public string FileName => Path.GetFileName(ContainerPath); + public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled) { TitleId = titleId; diff --git a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs new file mode 100644 index 000000000..e5e4f66b5 --- /dev/null +++ b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -0,0 +1,340 @@ +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Threading; +using DynamicData; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.FileSystem; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Path = System.IO.Path; + +namespace Ryujinx.Ava.UI.ViewModels +{ + public class DownloadableContentManagerViewModel : BaseModel + { + private readonly List<DownloadableContentContainer> _downloadableContentContainerList; + private readonly string _downloadableContentJsonPath; + + private VirtualFileSystem _virtualFileSystem; + private AvaloniaList<DownloadableContentModel> _downloadableContents = new(); + private AvaloniaList<DownloadableContentModel> _views = new(); + private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new(); + + private string _search; + private ulong _titleId; + private string _titleName; + + public AvaloniaList<DownloadableContentModel> DownloadableContents + { + get => _downloadableContents; + set + { + _downloadableContents = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(UpdateCount)); + Sort(); + } + } + + public AvaloniaList<DownloadableContentModel> Views + { + get => _views; + set + { + _views = value; + OnPropertyChanged(); + } + } + + public AvaloniaList<DownloadableContentModel> SelectedDownloadableContents + { + get => _selectedDownloadableContents; + set + { + _selectedDownloadableContents = value; + OnPropertyChanged(); + } + } + + public string Search + { + get => _search; + set + { + _search = value; + OnPropertyChanged(); + Sort(); + } + } + + public string UpdateCount + { + get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); + } + + public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + _virtualFileSystem = virtualFileSystem; + + _titleId = titleId; + _titleName = titleName; + + _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); + + try + { + _downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath); + } + catch + { + Logger.Error?.Print(LogClass.Configuration, "Downloadable Content JSON failed to deserialize."); + _downloadableContentContainerList = new List<DownloadableContentContainer>(); + } + + LoadDownloadableContents(); + } + + private void LoadDownloadableContents() + { + foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList) + { + if (File.Exists(downloadableContentContainer.ContainerPath)) + { + using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); + + PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); + + _virtualFileSystem.ImportTickets(partitionFileSystem); + + foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) + { + using UniqueRef<IFile> ncaFile = new(); + + partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); + if (nca != null) + { + var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), + downloadableContentContainer.ContainerPath, + downloadableContentNca.FullPath, + downloadableContentNca.Enabled); + + DownloadableContents.Add(content); + + if (content.Enabled) + { + SelectedDownloadableContents.Add(content); + } + + OnPropertyChanged(nameof(UpdateCount)); + } + } + } + } + + // NOTE: Save the list again to remove leftovers. + Save(); + Sort(); + } + + public void Sort() + { + DownloadableContents.AsObservableChangeSet() + .Filter(Filter) + .Bind(out var view).AsObservableList(); + + _views.Clear(); + _views.AddRange(view); + OnPropertyChanged(nameof(Views)); + } + + private bool Filter(object arg) + { + if (arg is DownloadableContentModel content) + { + return string.IsNullOrWhiteSpace(_search) || content.FileName.ToLower().Contains(_search.ToLower()) || content.TitleId.ToLower().Contains(_search.ToLower()); + } + + return false; + } + + private Nca TryOpenNca(IStorage ncaStorage, string containerPath) + { + try + { + return new Nca(_virtualFileSystem.KeySet, ncaStorage); + } + catch (Exception ex) + { + Dispatcher.UIThread.InvokeAsync(async () => + { + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogLoadNcaErrorMessage], ex.Message, containerPath)); + }); + } + + return null; + } + + public async void Add() + { + OpenFileDialog dialog = new OpenFileDialog() + { + Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], + AllowMultiple = true + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = "NSP", + Extensions = { "nsp" } + }); + + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null) + { + foreach (string file in files) + { + await AddDownloadableContent(file); + } + } + } + } + + private async Task AddDownloadableContent(string path) + { + if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) + { + return; + } + + using FileStream containerFile = File.OpenRead(path); + + PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); + bool containsDownloadableContent = false; + + _virtualFileSystem.ImportTickets(partitionFileSystem); + + foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef<IFile>(); + + partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path); + if (nca == null) + { + continue; + } + + if (nca.Header.ContentType == NcaContentType.PublicData) + { + if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId) + { + break; + } + + var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true); + DownloadableContents.Add(content); + SelectedDownloadableContents.Add(content); + + OnPropertyChanged(nameof(UpdateCount)); + Sort(); + + containsDownloadableContent = true; + } + } + + if (!containsDownloadableContent) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]); + } + } + + public void Remove(DownloadableContentModel model) + { + DownloadableContents.Remove(model); + OnPropertyChanged(nameof(UpdateCount)); + Sort(); + } + + public void RemoveAll() + { + DownloadableContents.Clear(); + OnPropertyChanged(nameof(UpdateCount)); + Sort(); + } + + public void EnableAll() + { + SelectedDownloadableContents = new(DownloadableContents); + } + + public void DisableAll() + { + SelectedDownloadableContents.Clear(); + } + + public void Save() + { + _downloadableContentContainerList.Clear(); + + DownloadableContentContainer container = default; + + foreach (DownloadableContentModel downloadableContent in DownloadableContents) + { + if (container.ContainerPath != downloadableContent.ContainerPath) + { + if (!string.IsNullOrWhiteSpace(container.ContainerPath)) + { + _downloadableContentContainerList.Add(container); + } + + container = new DownloadableContentContainer + { + ContainerPath = downloadableContent.ContainerPath, + DownloadableContentNcaList = new List<DownloadableContentNca>() + }; + } + + container.DownloadableContentNcaList.Add(new DownloadableContentNca + { + Enabled = downloadableContent.Enabled, + TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), + FullPath = downloadableContent.FullPath + }); + } + + if (!string.IsNullOrWhiteSpace(container.ContainerPath)) + { + _downloadableContentContainerList.Add(container); + } + + using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) + { + downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true))); + } + } + + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 489dfe621..a3663af3e 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1564,7 +1564,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (SelectedApplication != null) { - await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName).ShowDialog(TopLevel as Window); + await DownloadableContentManagerWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName); } } diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml index e524d6e4f..fe446fb3e 100644 --- a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml @@ -1,172 +1,194 @@ -<window:StyleableWindow +<UserControl x:Class="Ryujinx.Ava.UI.Windows.DownloadableContentManagerWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" - Width="800" - Height="500" - MinWidth="800" - MinHeight="500" - MaxWidth="800" - MaxHeight="500" - SizeToContent="Height" - WindowStartupLocation="CenterOwner" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + Width="500" + Height="380" mc:Ignorable="d" + x:CompileBindings="True" + x:DataType="viewModels:DownloadableContentManagerViewModel" Focusable="True"> - <Grid Name="DownloadableContentGrid" Margin="15"> + <Grid> <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> - <TextBlock - Name="Heading" - Grid.Row="1" - MaxWidth="500" - Margin="20,15,20,20" - HorizontalAlignment="Center" - VerticalAlignment="Center" - LineHeight="18" - TextAlignment="Center" - TextWrapping="Wrap" /> - <DockPanel - Grid.Row="2" - Margin="0" - HorizontalAlignment="Left"> - <Button - Name="EnableAllButton" - MinWidth="90" - Margin="5" - Command="{Binding EnableAll}"> - <TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" /> - </Button> - <Button - Name="DisableAllButton" - MinWidth="90" - Margin="5" - Command="{Binding DisableAll}"> - <TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" /> - </Button> - </DockPanel> + <Panel + Margin="0 0 0 10" + Grid.Row="0"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock + Grid.Column="0" + Text="{Binding UpdateCount}" /> + <StackPanel + Margin="10 0" + Grid.Column="1" + Orientation="Horizontal"> + <Button + Name="EnableAllButton" + MinWidth="90" + Margin="5" + Command="{ReflectionBinding EnableAll}"> + <TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" /> + </Button> + <Button + Name="DisableAllButton" + MinWidth="90" + Margin="5" + Command="{ReflectionBinding DisableAll}"> + <TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" /> + </Button> + </StackPanel> + <TextBox + Grid.Column="2" + MinHeight="27" + MaxHeight="27" + HorizontalAlignment="Stretch" + Watermark="{locale:Locale Search}" + Text="{Binding Search}" /> + </Grid> + </Panel> <Border - Grid.Row="3" - Margin="5" + Grid.Row="1" + Margin="0 0 0 24" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - BorderBrush="Gray" - BorderThickness="1"> - <ScrollViewer - VerticalAlignment="Stretch" - HorizontalScrollBarVisibility="Auto" - VerticalScrollBarVisibility="Auto"> - <DataGrid - Name="DlcDataGrid" - MinHeight="200" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" - CanUserReorderColumns="False" - CanUserResizeColumns="True" - CanUserSortColumns="True" - HorizontalScrollBarVisibility="Auto" - Items="{Binding _downloadableContents}" - SelectionMode="Extended" - VerticalScrollBarVisibility="Auto"> - <DataGrid.Styles> - <Styles> - <Style Selector="DataGridCell:nth-child(3), DataGridCell:nth-child(4)"> - <Setter Property="HorizontalAlignment" Value="Left" /> - <Setter Property="HorizontalContentAlignment" Value="Left" /> - </Style> - </Styles> - <Styles> - <Style Selector="DataGridCell:nth-child(1)"> - <Setter Property="HorizontalAlignment" Value="Right" /> - <Setter Property="HorizontalContentAlignment" Value="Right" /> - </Style> - </Styles> - </DataGrid.Styles> - <DataGrid.Columns> - <DataGridTemplateColumn Width="90"> - <DataGridTemplateColumn.CellTemplate> - <DataTemplate> - <CheckBox - Width="50" - MinWidth="40" - HorizontalAlignment="Center" - IsChecked="{Binding Enabled}" /> - </DataTemplate> - </DataGridTemplateColumn.CellTemplate> - <DataGridTemplateColumn.Header> - <TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" /> - </DataGridTemplateColumn.Header> - </DataGridTemplateColumn> - <DataGridTextColumn Width="140" Binding="{Binding TitleId}"> - <DataGridTextColumn.Header> - <TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" /> - </DataGridTextColumn.Header> - </DataGridTextColumn> - <DataGridTextColumn Width="280" Binding="{Binding FullPath}"> - <DataGridTextColumn.Header> - <TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" /> - </DataGridTextColumn.Header> - </DataGridTextColumn> - <DataGridTextColumn Binding="{Binding ContainerPath}"> - <DataGridTextColumn.Header> - <TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" /> - </DataGridTextColumn.Header> - </DataGridTextColumn> - </DataGrid.Columns> - </DataGrid> - </ScrollViewer> + BorderBrush="{DynamicResource AppListHoverBackgroundColor}" + BorderThickness="1" + CornerRadius="5" + Padding="2.5"> + <ListBox + AutoScrollToSelectedItem="False" + VirtualizationMode="None" + SelectionMode="Multiple, Toggle" + Background="Transparent" + SelectionChanged="OnSelectionChanged" + SelectedItems="{Binding SelectedDownloadableContents, Mode=TwoWay}" + Items="{Binding Views}"> + <ListBox.DataTemplates> + <DataTemplate + DataType="models:DownloadableContentModel"> + <Panel Margin="10"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="Auto" /> + </Grid.ColumnDefinitions> + <Grid + Grid.Column="0"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*"></ColumnDefinition> + <ColumnDefinition Width="Auto"></ColumnDefinition> + </Grid.ColumnDefinitions> + <TextBlock + Grid.Column="0" + HorizontalAlignment="Left" + VerticalAlignment="Center" + MaxLines="2" + TextWrapping="Wrap" + TextTrimming="CharacterEllipsis" + Text="{Binding FileName}" /> + <TextBlock + Grid.Column="1" + Margin="10 0" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{Binding TitleId}" /> + </Grid> + <StackPanel + Grid.Column="1" + Spacing="10" + Orientation="Horizontal" + HorizontalAlignment="Right"> + <Button + VerticalAlignment="Center" + HorizontalAlignment="Right" + Padding="10" + MinWidth="0" + MinHeight="0" + Click="OpenLocation"> + <ui:SymbolIcon + Symbol="OpenFolder" + HorizontalAlignment="Center" + VerticalAlignment="Center" /> + </Button> + <Button + VerticalAlignment="Center" + HorizontalAlignment="Right" + Padding="10" + MinWidth="0" + MinHeight="0" + Click="RemoveDLC"> + <ui:SymbolIcon + Symbol="Cancel" + HorizontalAlignment="Center" + VerticalAlignment="Center" /> + </Button> + </StackPanel> + </Grid> + </Panel> + </DataTemplate> + </ListBox.DataTemplates> + <ListBox.Styles> + <Style Selector="ListBoxItem"> + <Setter Property="Background" Value="Transparent" /> + </Style> + </ListBox.Styles> + </ListBox> </Border> - <DockPanel - Grid.Row="4" - Margin="0" + <Panel + Grid.Row="2" HorizontalAlignment="Stretch"> - <DockPanel Margin="0" HorizontalAlignment="Left"> + <StackPanel + Orientation="Horizontal" + Spacing="10" + HorizontalAlignment="Left"> <Button Name="AddButton" MinWidth="90" Margin="5" - Command="{Binding Add}"> + Command="{ReflectionBinding Add}"> <TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" /> </Button> - <Button - Name="RemoveButton" - MinWidth="90" - Margin="5" - Command="{Binding RemoveSelected}"> - <TextBlock Text="{locale:Locale SettingsTabGeneralRemove}" /> - </Button> <Button Name="RemoveAllButton" MinWidth="90" Margin="5" - Command="{Binding RemoveAll}"> + Command="{ReflectionBinding RemoveAll}"> <TextBlock Text="{locale:Locale DlcManagerRemoveAllButton}" /> </Button> - </DockPanel> - <DockPanel Margin="0" HorizontalAlignment="Right"> + </StackPanel> + <StackPanel + Orientation="Horizontal" + Spacing="10" + HorizontalAlignment="Right"> <Button Name="SaveButton" MinWidth="90" Margin="5" - Command="{Binding SaveAndClose}"> + Click="SaveAndClose"> <TextBlock Text="{locale:Locale SettingsButtonSave}" /> </Button> <Button Name="CancelButton" MinWidth="90" Margin="5" - Command="{Binding Close}"> + Click="Close"> <TextBlock Text="{locale:Locale InputDialogCancel}" /> </Button> - </DockPanel> - </DockPanel> + </StackPanel> + </Panel> </Grid> -</window:StyleableWindow> \ No newline at end of file +</UserControl> \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs index 2dab1d352..6dc99fb54 100644 --- a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs @@ -1,314 +1,115 @@ -using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Threading; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; +using Avalonia.Interactivity; +using Avalonia.Styling; +using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.HLE.FileSystem; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reactive.Linq; -using System.Text; +using Ryujinx.Ui.Common.Helper; using System.Threading.Tasks; -using Path = System.IO.Path; +using Button = Avalonia.Controls.Button; namespace Ryujinx.Ava.UI.Windows { - public partial class DownloadableContentManagerWindow : StyleableWindow + public partial class DownloadableContentManagerWindow : UserControl { - private readonly List<DownloadableContentContainer> _downloadableContentContainerList; - private readonly string _downloadableContentJsonPath; - - private VirtualFileSystem _virtualFileSystem { get; } - private AvaloniaList<DownloadableContentModel> _downloadableContents { get; set; } - - private ulong _titleId { get; } - private string _titleName { get; } + public DownloadableContentManagerViewModel ViewModel; public DownloadableContentManagerWindow() { DataContext = this; InitializeComponent(); - - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DlcWindowTitle]} - {_titleName} ({_titleId:X16})"; } public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - _virtualFileSystem = virtualFileSystem; - _downloadableContents = new AvaloniaList<DownloadableContentModel>(); - - _titleId = titleId; - _titleName = titleName; - - _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); - - try - { - _downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath); - } - catch - { - _downloadableContentContainerList = new List<DownloadableContentContainer>(); - } - - DataContext = this; + DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId, titleName); InitializeComponent(); - - RemoveButton.IsEnabled = false; - - DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged; - - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DlcWindowTitle]} - {_titleName} ({_titleId:X16})"; - - LoadDownloadableContents(); - PrintHeading(); } - private void DlcDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) + public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - RemoveButton.IsEnabled = (DlcDataGrid.SelectedItems.Count > 0); - } - - private void PrintHeading() - { - Heading.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DlcWindowHeading, _downloadableContents.Count, _titleName, _titleId.ToString("X16")); - } - - private void LoadDownloadableContents() - { - foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList) + ContentDialog contentDialog = new() { - if (File.Exists(downloadableContentContainer.ContainerPath)) - { - using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); - - PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); - - _virtualFileSystem.ImportTickets(partitionFileSystem); - - foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) - { - using UniqueRef<IFile> ncaFile = new(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); - if (nca != null) - { - _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), - downloadableContentContainer.ContainerPath, - downloadableContentNca.FullPath, - downloadableContentNca.Enabled)); - } - } - } - } - - // NOTE: Save the list again to remove leftovers. - Save(); - } - - private Nca TryOpenNca(IStorage ncaStorage, string containerPath) - { - try - { - return new Nca(_virtualFileSystem.KeySet, ncaStorage); - } - catch (Exception ex) - { - Dispatcher.UIThread.InvokeAsync(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, containerPath)); - }); - } - - return null; - } - - private async Task AddDownloadableContent(string path) - { - if (!File.Exists(path) || _downloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) - { - return; - } - - using FileStream containerFile = File.OpenRead(path); - - PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); - bool containsDownloadableContent = false; - - _virtualFileSystem.ImportTickets(partitionFileSystem); - - foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path); - if (nca == null) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.PublicData) - { - if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId) - { - break; - } - - _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true)); - - containsDownloadableContent = true; - } - } - - if (!containsDownloadableContent) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]); - } - } - - private void RemoveDownloadableContents(bool removeSelectedOnly = false) - { - if (removeSelectedOnly) - { - AvaloniaList<DownloadableContentModel> removedItems = new(); - - foreach (var item in DlcDataGrid.SelectedItems) - { - removedItems.Add(item as DownloadableContentModel); - } - - DlcDataGrid.SelectedItems.Clear(); - - foreach (var item in removedItems) - { - _downloadableContents.RemoveAll(_downloadableContents.Where(x => x.TitleId == item.TitleId).ToList()); - } - } - else - { - _downloadableContents.Clear(); - } - - PrintHeading(); - } - - public void RemoveSelected() - { - RemoveDownloadableContents(true); - } - - public void RemoveAll() - { - RemoveDownloadableContents(); - } - - public void EnableAll() - { - foreach(var item in _downloadableContents) - { - item.Enabled = true; - } - } - - public void DisableAll() - { - foreach (var item in _downloadableContents) - { - item.Enabled = false; - } - } - - public async void Add() - { - OpenFileDialog dialog = new OpenFileDialog() - { - Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], - AllowMultiple = true + PrimaryButtonText = "", + SecondaryButtonText = "", + CloseButtonText = "", + Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId, titleName), + Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")) }; - dialog.Filters.Add(new FileDialogFilter - { - Name = "NSP", - Extensions = { "nsp" } - }); + Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); + bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); - string[] files = await dialog.ShowAsync(this); + contentDialog.Styles.Add(bottomBorder); - if (files != null) - { - foreach (string file in files) - { - await AddDownloadableContent(file); - } - } - - PrintHeading(); + await ContentDialogHelper.ShowAsync(contentDialog); } - public void Save() + private void SaveAndClose(object sender, RoutedEventArgs routedEventArgs) { - _downloadableContentContainerList.Clear(); + ViewModel.Save(); + ((ContentDialog)Parent).Hide(); + } - DownloadableContentContainer container = default; + private void Close(object sender, RoutedEventArgs e) + { + ((ContentDialog)Parent).Hide(); + } - foreach (DownloadableContentModel downloadableContent in _downloadableContents) + private void RemoveDLC(object sender, RoutedEventArgs e) + { + if (sender is Button button) { - if (container.ContainerPath != downloadableContent.ContainerPath) + if (button.DataContext is DownloadableContentModel model) { - if (!string.IsNullOrWhiteSpace(container.ContainerPath)) + ViewModel.Remove(model); + } + } + } + + private void OpenLocation(object sender, RoutedEventArgs e) + { + if (sender is Button button) + { + if (button.DataContext is DownloadableContentModel model) + { + OpenHelper.LocateFile(model.ContainerPath); + } + } + } + + private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + foreach (var content in e.AddedItems) + { + if (content is DownloadableContentModel model) + { + var index = ViewModel.DownloadableContents.IndexOf(model); + + if (index != -1) { - _downloadableContentContainerList.Add(container); + ViewModel.DownloadableContents[index].Enabled = true; } - - container = new DownloadableContentContainer - { - ContainerPath = downloadableContent.ContainerPath, - DownloadableContentNcaList = new List<DownloadableContentNca>() - }; } + } - container.DownloadableContentNcaList.Add(new DownloadableContentNca + foreach (var content in e.RemovedItems) + { + if (content is DownloadableContentModel model) { - Enabled = downloadableContent.Enabled, - TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), - FullPath = downloadableContent.FullPath - }); - } + var index = ViewModel.DownloadableContents.IndexOf(model); - if (!string.IsNullOrWhiteSpace(container.ContainerPath)) - { - _downloadableContentContainerList.Add(container); + if (index != -1) + { + ViewModel.DownloadableContents[index].Enabled = false; + } + } } - - using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) - { - downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true))); - } - } - - public void SaveAndClose() - { - Save(); - Close(); } } } \ No newline at end of file From 1fc90e57d2e7f7bb6886a58b81bcd1f4cb25f8cf Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Tue, 14 Mar 2023 20:08:44 +0000 Subject: [PATCH 400/737] Update range for remapped sparse textures instead of recreating them (#4442) * Update sparsely mapped texture ranges without recreating Important TODO in TexturePool. Smaller TODO: should I look into making textures with views also do this? It needs to be able to detect if the views can be instantly deleted without issue if they're now remapped. * Actually do partial updates * Signal group dirty after mappings changed * Fix various issues (should work now) * Further optimisation Should load a lot less data (16x) when partial updating 3d textures. * Improve stability * Allow granular uploads on large textures, improve rules * Actually avoid updating slices that aren't modified. * Address some feedback, minor optimisation * Small tweak * Refactor DereferenceRequest More specific initialization methods. * Improve code for resetting handles * Explain data loading a bit more * Add some safety for setting null from different threads. All texture sets come from the one thread, but null sets can come from multiple. Only decrement ref count if we succeeded the null set first. * Address feedback 1 * Make a bit safer --- Ryujinx.Cpu/Tracking/CpuRegionHandle.cs | 5 + Ryujinx.Graphics.GAL/Target.cs | 10 ++ Ryujinx.Graphics.Gpu/Image/Texture.cs | 82 ++++++++-- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 33 ++++ Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 152 ++++++++++++++++-- Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 165 ++++++++++++++++++-- Ryujinx.Graphics.Texture/LayoutConverter.cs | 2 +- Ryujinx.Memory/Range/MultiRange.cs | 13 +- 8 files changed, 416 insertions(+), 46 deletions(-) diff --git a/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs b/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs index d2a287493..e766460fe 100644 --- a/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs +++ b/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs @@ -28,5 +28,10 @@ namespace Ryujinx.Cpu.Tracking public void Reprotect(bool asDirty = false) => _impl.Reprotect(asDirty); public bool OverlapsWith(ulong address, ulong size) => _impl.OverlapsWith(address, size); + + public bool RangeEquals(CpuRegionHandle other) + { + return _impl.RealAddress == other._impl.RealAddress && _impl.RealSize == other._impl.RealSize; + } } } diff --git a/Ryujinx.Graphics.GAL/Target.cs b/Ryujinx.Graphics.GAL/Target.cs index e20bd3c84..711eea248 100644 --- a/Ryujinx.Graphics.GAL/Target.cs +++ b/Ryujinx.Graphics.GAL/Target.cs @@ -20,5 +20,15 @@ namespace Ryujinx.Graphics.GAL { return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray; } + + public static bool HasDepthOrLayers(this Target target) + { + return target == Target.Texture3D || + target == Target.Texture1DArray || + target == Target.Texture2DArray || + target == Target.Texture2DMultisampleArray || + target == Target.Cubemap || + target == Target.CubemapArray; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 6c0de5367..363f0f73a 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Image { public TexturePool Pool; public int ID; + public ulong GpuAddress; } private GpuContext _context; @@ -162,6 +163,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public bool IsView => _viewStorage != this; + /// <summary> + /// Whether or not this texture has views. + /// </summary> + public bool HasViews => _views.Count > 0; + private int _referenceCount; private List<TexturePoolOwner> _poolOwners; @@ -383,6 +389,17 @@ namespace Ryujinx.Graphics.Gpu.Image DecrementReferenceCount(); } + /// <summary> + /// Replaces the texture's physical memory range. This forces tracking to regenerate. + /// </summary> + /// <param name="range">New physical memory range backing the texture</param> + public void ReplaceRange(MultiRange range) + { + Range = range; + + Group.RangeChanged(); + } + /// <summary> /// Create a copy dependency to a texture that is view compatible with this one. /// When either texture is modified, the texture data will be copied to the other to keep them in sync. @@ -715,6 +732,8 @@ namespace Ryujinx.Graphics.Gpu.Image height = Math.Max(height >> level, 1); depth = Math.Max(depth >> level, 1); + int sliceDepth = single ? 1 : depth; + SpanOrArray<byte> result; if (Info.IsLinear) @@ -735,7 +754,7 @@ namespace Ryujinx.Graphics.Gpu.Image width, height, depth, - single ? 1 : depth, + sliceDepth, levels, layers, Info.FormatInfo.BlockWidth, @@ -759,7 +778,7 @@ namespace Ryujinx.Graphics.Gpu.Image Info.FormatInfo.BlockHeight, width, height, - depth, + sliceDepth, levels, layers, out byte[] decoded)) @@ -771,7 +790,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (GraphicsConfig.EnableTextureRecompression) { - decoded = BCnEncoder.EncodeBC7(decoded, width, height, depth, levels, layers); + decoded = BCnEncoder.EncodeBC7(decoded, width, height, sliceDepth, levels, layers); } result = decoded; @@ -782,15 +801,15 @@ namespace Ryujinx.Graphics.Gpu.Image { case Format.Etc2RgbaSrgb: case Format.Etc2RgbaUnorm: - result = ETC2Decoder.DecodeRgba(result, width, height, depth, levels, layers); + result = ETC2Decoder.DecodeRgba(result, width, height, sliceDepth, levels, layers); break; case Format.Etc2RgbPtaSrgb: case Format.Etc2RgbPtaUnorm: - result = ETC2Decoder.DecodePta(result, width, height, depth, levels, layers); + result = ETC2Decoder.DecodePta(result, width, height, sliceDepth, levels, layers); break; case Format.Etc2RgbSrgb: case Format.Etc2RgbUnorm: - result = ETC2Decoder.DecodeRgb(result, width, height, depth, levels, layers); + result = ETC2Decoder.DecodeRgb(result, width, height, sliceDepth, levels, layers); break; } } @@ -800,31 +819,31 @@ namespace Ryujinx.Graphics.Gpu.Image { case Format.Bc1RgbaSrgb: case Format.Bc1RgbaUnorm: - result = BCnDecoder.DecodeBC1(result, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC1(result, width, height, sliceDepth, levels, layers); break; case Format.Bc2Srgb: case Format.Bc2Unorm: - result = BCnDecoder.DecodeBC2(result, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC2(result, width, height, sliceDepth, levels, layers); break; case Format.Bc3Srgb: case Format.Bc3Unorm: - result = BCnDecoder.DecodeBC3(result, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC3(result, width, height, sliceDepth, levels, layers); break; case Format.Bc4Snorm: case Format.Bc4Unorm: - result = BCnDecoder.DecodeBC4(result, width, height, depth, levels, layers, Format == Format.Bc4Snorm); + result = BCnDecoder.DecodeBC4(result, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm); break; case Format.Bc5Snorm: case Format.Bc5Unorm: - result = BCnDecoder.DecodeBC5(result, width, height, depth, levels, layers, Format == Format.Bc5Snorm); + result = BCnDecoder.DecodeBC5(result, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm); break; case Format.Bc6HSfloat: case Format.Bc6HUfloat: - result = BCnDecoder.DecodeBC6(result, width, height, depth, levels, layers, Format == Format.Bc6HSfloat); + result = BCnDecoder.DecodeBC6(result, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat); break; case Format.Bc7Srgb: case Format.Bc7Unorm: - result = BCnDecoder.DecodeBC7(result, width, height, depth, levels, layers); + result = BCnDecoder.DecodeBC7(result, width, height, sliceDepth, levels, layers); break; } } @@ -1484,11 +1503,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> /// <param name="pool">The texture pool this texture has been added to</param> /// <param name="id">The ID of the reference to this texture in the pool</param> - public void IncrementReferenceCount(TexturePool pool, int id) + /// <param name="gpuVa">GPU VA of the pool reference</param> + public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa) { lock (_poolOwners) { - _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id }); + _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa }); } _referenceCount++; @@ -1585,6 +1605,36 @@ namespace Ryujinx.Graphics.Gpu.Image InvalidatedSequence++; } + /// <summary> + /// Queue updating texture mappings on the pool. Happens from another thread. + /// </summary> + public void UpdatePoolMappings() + { + lock (_poolOwners) + { + ulong address = 0; + + foreach (var owner in _poolOwners) + { + if (address == 0 || address == owner.GpuAddress) + { + address = owner.GpuAddress; + + owner.Pool.QueueUpdateMapping(this, owner.ID); + } + else + { + // If there is a different GPU VA mapping, prefer the first and delete the others. + owner.Pool.ForceRemove(this, owner.ID, true); + } + } + + _poolOwners.Clear(); + } + + InvalidatedSequence++; + } + /// <summary> /// Delete the texture if it is not used anymore. /// The texture is considered unused when the reference count is zero, @@ -1636,7 +1686,7 @@ namespace Ryujinx.Graphics.Gpu.Image Group.ClearModified(unmapRange); } - RemoveFromPools(true); + UpdatePoolMappings(); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 261d06038..c3243cf23 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -194,6 +194,39 @@ namespace Ryujinx.Graphics.Gpu.Image _cache.Lift(texture); } + /// <summary> + /// Attempts to update a texture's physical memory range. + /// Returns false if there is an existing texture that matches with the updated range. + /// </summary> + /// <param name="texture">Texture to update</param> + /// <param name="range">New physical memory range</param> + /// <returns>True if the mapping was updated, false otherwise</returns> + public bool UpdateMapping(Texture texture, MultiRange range) + { + // There cannot be an existing texture compatible with this mapping in the texture cache already. + int overlapCount = _textures.FindOverlaps(range, ref _textureOverlaps); + + for (int i = 0; i < overlapCount; i++) + { + var other = _textureOverlaps[i]; + + if (texture != other && + (texture.IsViewCompatible(other.Info, other.Range, true, other.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible || + other.IsViewCompatible(texture.Info, texture.Range, true, texture.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible)) + { + return false; + } + } + + _textures.Remove(texture); + + texture.ReplaceRange(range); + + _textures.Add(texture); + + return true; + } + /// <summary> /// Tries to find an existing texture, or create a new one if not found. /// </summary> diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 12a640e15..d9b620aae 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -39,6 +39,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> class TextureGroup : IDisposable { + /// <summary> + /// Threshold of layers to force granular handles (and thus partial loading) on array/3D textures. + /// </summary> + private const int GranularLayerThreshold = 8; + private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false); /// <summary> @@ -116,7 +121,29 @@ namespace Ryujinx.Graphics.Gpu.Image _allOffsets = size.AllOffsets; _sliceSizes = size.SliceSizes; - (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews); + if (Storage.Target.HasDepthOrLayers() && Storage.Info.GetSlices() > GranularLayerThreshold) + { + _hasLayerViews = true; + _hasMipViews = true; + } + else + { + (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews); + + // If the texture is partially mapped, fully subdivide handles immediately. + + MultiRange range = Storage.Range; + for (int i = 0; i < range.Count; i++) + { + if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped) + { + _hasLayerViews = true; + _hasMipViews = true; + + break; + } + } + } RecalculateHandleRegions(); } @@ -249,7 +276,7 @@ namespace Ryujinx.Graphics.Gpu.Image { bool dirty = false; bool anyModified = false; - bool anyUnmapped = false; + bool anyNotDirty = false; for (int i = 0; i < regionCount; i++) { @@ -294,20 +321,21 @@ namespace Ryujinx.Graphics.Gpu.Image dirty |= handleDirty; } - anyUnmapped |= handleUnmapped; - if (group.NeedsCopy) { // The texture we copied from is still being written to. Copy from it again the next time this texture is used. texture.SignalGroupDirty(); } - _loadNeeded[baseHandle + i] = handleDirty && !handleUnmapped; + bool loadNeeded = handleDirty && !handleUnmapped; + + anyNotDirty |= !loadNeeded; + _loadNeeded[baseHandle + i] = loadNeeded; } if (dirty) { - if (anyUnmapped || (_handles.Length > 1 && (anyModified || split))) + if (anyNotDirty || (_handles.Length > 1 && (anyModified || split))) { // Partial texture invalidation. Only update the layers/levels with dirty flags of the storage. @@ -331,24 +359,56 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="regionCount">The number of handles to synchronize</param> private void SynchronizePartial(int baseHandle, int regionCount) { + int spanEndIndex = -1; + int spanBase = 0; + ReadOnlySpan<byte> dataSpan = ReadOnlySpan<byte>.Empty; + for (int i = 0; i < regionCount; i++) { if (_loadNeeded[baseHandle + i]) { var info = GetHandleInformation(baseHandle + i); + // Ensure the data for this handle is loaded in the span. + if (spanEndIndex <= i - 1) + { + spanEndIndex = i; + + if (_is3D) + { + // Look ahead to see how many handles need to be loaded. + for (int j = i + 1; j < regionCount; j++) + { + if (_loadNeeded[baseHandle + j]) + { + spanEndIndex = j; + } + else + { + break; + } + } + } + + var endInfo = spanEndIndex == i ? info : GetHandleInformation(baseHandle + spanEndIndex); + + spanBase = _allOffsets[info.Index]; + int spanLast = _allOffsets[endInfo.Index + endInfo.Layers * endInfo.Levels - 1]; + int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size); + int size = endOffset - spanBase; + + dataSpan = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)spanBase, (ulong)size)); + } + // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views. for (int layer = 0; layer < info.Layers; layer++) { for (int level = 0; level < info.Levels; level++) { int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level); - int offset = _allOffsets[offsetIndex]; - int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size); - int size = endOffset - offset; - ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size)); + ReadOnlySpan<byte> data = dataSpan.Slice(offset - spanBase); SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); @@ -865,8 +925,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>A TextureGroupHandle covering the given views</returns> private TextureGroupHandle GenerateHandles(int viewStart, int views) { + int viewEnd = viewStart + views - 1; + (_, int lastLevel) = GetLayerLevelForView(viewEnd); + int offset = _allOffsets[viewStart]; - int endOffset = (viewStart + views == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[viewStart + views]; + int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel]; int size = endOffset - offset; var result = new List<CpuRegionHandle>(); @@ -1057,7 +1120,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// The dirty flags from the previous handles will be kept. /// </summary> /// <param name="handles">The handles to replace the current handles with</param> - private void ReplaceHandles(TextureGroupHandle[] handles) + /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param> + private void ReplaceHandles(TextureGroupHandle[] handles, bool rangeChanged) { if (_handles != null) { @@ -1065,9 +1129,50 @@ namespace Ryujinx.Graphics.Gpu.Image foreach (TextureGroupHandle groupHandle in handles) { - foreach (CpuRegionHandle handle in groupHandle.Handles) + if (rangeChanged) { - handle.Reprotect(); + // When the storage range changes, this becomes a little different. + // If a range does not match one in the original, treat it as modified. + // It has been newly mapped and its data must be synchronized. + + if (groupHandle.Handles.Length == 0) + { + continue; + } + + foreach (var oldGroup in _handles) + { + if (!groupHandle.OverlapsWith(oldGroup.Offset, oldGroup.Size)) + { + continue; + } + + foreach (CpuRegionHandle handle in groupHandle.Handles) + { + bool hasMatch = false; + + foreach (var oldHandle in oldGroup.Handles) + { + if (oldHandle.RangeEquals(handle)) + { + hasMatch = true; + break; + } + } + + if (hasMatch) + { + handle.Reprotect(); + } + } + } + } + else + { + foreach (CpuRegionHandle handle in groupHandle.Handles) + { + handle.Reprotect(); + } } } @@ -1089,7 +1194,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// Recalculate handle regions for this texture group, and inherit existing state into the new handles. /// </summary> - private void RecalculateHandleRegions() + /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param> + private void RecalculateHandleRegions(bool rangeChanged = false) { TextureGroupHandle[] handles; @@ -1171,7 +1277,21 @@ namespace Ryujinx.Graphics.Gpu.Image } } - ReplaceHandles(handles); + ReplaceHandles(handles, rangeChanged); + } + + /// <summary> + /// Regenerates handles when the storage range has been remapped. + /// This forces the regions to be fully subdivided. + /// </summary> + public void RangeChanged() + { + _hasLayerViews = true; + _hasMipViews = true; + + RecalculateHandleRegions(true); + + SignalAllDirty(); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 0348ca014..717c5c362 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -1,9 +1,12 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; +using Ryujinx.Memory.Range; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.Graphics.Gpu.Image { @@ -12,8 +15,63 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> class TexturePool : Pool<Texture, TextureDescriptor>, IPool<TexturePool> { + /// <summary> + /// A request to dereference a texture from a pool. + /// </summary> + private struct DereferenceRequest + { + /// <summary> + /// Whether the dereference is due to a mapping change or not. + /// </summary> + public readonly bool IsRemapped; + + /// <summary> + /// The texture being dereferenced. + /// </summary> + public readonly Texture Texture; + + /// <summary> + /// The ID of the pool entry this reference belonged to. + /// </summary> + public readonly int ID; + + /// <summary> + /// Create a dereference request for a texture with a specific pool ID, and remapped flag. + /// </summary> + /// <param name="isRemapped">Whether the dereference is due to a mapping change or not</param> + /// <param name="texture">The texture being dereferenced</param> + /// <param name="id">The ID of the pool entry, used to restore remapped textures</param> + private DereferenceRequest(bool isRemapped, Texture texture, int id) + { + IsRemapped = isRemapped; + Texture = texture; + ID = id; + } + + /// <summary> + /// Create a dereference request for a texture removal. + /// </summary> + /// <param name="texture">The texture being removed</param> + /// <returns>A texture removal dereference request</returns> + public static DereferenceRequest Remove(Texture texture) + { + return new DereferenceRequest(false, texture, 0); + } + + /// <summary> + /// Create a dereference request for a texture remapping with a specific pool ID. + /// </summary> + /// <param name="texture">The texture being remapped</param> + /// <param name="id">The ID of the pool entry, used to restore remapped textures</param> + /// <returns>A remap dereference request</returns> + public static DereferenceRequest Remap(Texture texture, int id) + { + return new DereferenceRequest(true, texture, id); + } + } + private readonly GpuChannel _channel; - private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>(); + private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new ConcurrentQueue<DereferenceRequest>(); private TextureDescriptor _defaultDescriptor; /// <summary> @@ -58,7 +116,11 @@ namespace Ryujinx.Graphics.Gpu.Image { TextureInfo info = GetInfo(descriptor, out int layerSize); - ProcessDereferenceQueue(); + // The dereference queue can put our texture back on the cache. + if ((texture = ProcessDereferenceQueue(id)) != null) + { + return ref descriptor; + } texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize); @@ -69,10 +131,10 @@ namespace Ryujinx.Graphics.Gpu.Image } } - texture.IncrementReferenceCount(this, id); - Items[id] = texture; + texture.IncrementReferenceCount(this, id, descriptor.UnpackAddress()); + DescriptorCache[id] = descriptor; } else @@ -155,11 +217,14 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="deferred">If true, queue the dereference to happen on the render thread, otherwise dereference immediately</param> public void ForceRemove(Texture texture, int id, bool deferred) { - Items[id] = null; + var previous = Interlocked.Exchange(ref Items[id], null); if (deferred) { - _dereferenceQueue.Enqueue(texture); + if (previous != null) + { + _dereferenceQueue.Enqueue(DereferenceRequest.Remove(texture)); + } } else { @@ -167,16 +232,91 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Queues a request to update a texture's mapping. + /// Mapping is updated later to avoid deleting the texture if it is still sparsely mapped. + /// </summary> + /// <param name="texture">Texture with potential mapping change</param> + /// <param name="id">ID in cache of texture with potential mapping change</param> + public void QueueUpdateMapping(Texture texture, int id) + { + if (Interlocked.Exchange(ref Items[id], null) == texture) + { + _dereferenceQueue.Enqueue(DereferenceRequest.Remap(texture, id)); + } + } + /// <summary> /// Process the dereference queue, decrementing the reference count for each texture in it. /// This is used to ensure that texture disposal happens on the render thread. /// </summary> - private void ProcessDereferenceQueue() + /// <param name="id">The ID of the entry that triggered this method</param> + /// <returns>Texture that matches the entry ID if it has been readded to the cache.</returns> + private Texture ProcessDereferenceQueue(int id = -1) { - while (_dereferenceQueue.TryDequeue(out Texture toRemove)) + while (_dereferenceQueue.TryDequeue(out DereferenceRequest request)) { - toRemove.DecrementReferenceCount(); + Texture texture = request.Texture; + + // Unmapped storage textures can swap their ranges. The texture must be storage with no views or dependencies. + // TODO: Would need to update ranges on views, or guarantee that ones where the range changes can be instantly deleted. + + if (request.IsRemapped && texture.Group.Storage == texture && !texture.HasViews && !texture.Group.HasCopyDependencies) + { + // Has the mapping for this texture changed? + ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(request.ID); + + ulong address = descriptor.UnpackAddress(); + + MultiRange range = _channel.MemoryManager.GetPhysicalRegions(address, texture.Size); + + // If the texture is not mapped at all, delete its reference. + + if (range.Count == 1 && range.GetSubRange(0).Address == MemoryManager.PteUnmapped) + { + texture.DecrementReferenceCount(); + continue; + } + + Items[request.ID] = texture; + + // Create a new pool reference, as the last one was removed on unmap. + + texture.IncrementReferenceCount(this, request.ID, address); + texture.DecrementReferenceCount(); + + // Refetch the range. Changes since the last check could have been lost + // as the cache entry was not restored (required to queue mapping change). + + range = _channel.MemoryManager.GetPhysicalRegions(address, texture.Size); + + if (!range.Equals(texture.Range)) + { + // Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles. + if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range)) + { + // Texture could not be remapped due to a collision, just delete it. + if (Interlocked.Exchange(ref Items[request.ID], null) != null) + { + // If this is null, a request was already queued to decrement reference. + texture.DecrementReferenceCount(this, request.ID); + } + continue; + } + } + + if (request.ID == id) + { + return texture; + } + } + else + { + texture.DecrementReferenceCount(); + } } + + return null; } /// <summary> @@ -213,9 +353,10 @@ namespace Ryujinx.Graphics.Gpu.Image _channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor); } - texture.DecrementReferenceCount(this, id); - - Items[id] = null; + if (Interlocked.Exchange(ref Items[id], null) != null) + { + texture.DecrementReferenceCount(this, id); + } } } } diff --git a/Ryujinx.Graphics.Texture/LayoutConverter.cs b/Ryujinx.Graphics.Texture/LayoutConverter.cs index b8ec9748b..09eaf3001 100644 --- a/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Texture int outSize = GetTextureSize( width, height, - depth, + sliceDepth, levels, layers, blockWidth, diff --git a/Ryujinx.Memory/Range/MultiRange.cs b/Ryujinx.Memory/Range/MultiRange.cs index e95af02f4..dc2aefe40 100644 --- a/Ryujinx.Memory/Range/MultiRange.cs +++ b/Ryujinx.Memory/Range/MultiRange.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Memory.Range /// </summary> public readonly struct MultiRange : IEquatable<MultiRange> { + private const ulong InvalidAddress = ulong.MaxValue; + private readonly MemoryRange _singleRange; private readonly MemoryRange[] _ranges; @@ -107,7 +109,16 @@ namespace Ryujinx.Memory.Range else if (offset < range.Size) { ulong sliceSize = Math.Min(size, range.Size - offset); - ranges.Add(new MemoryRange(range.Address + offset, sliceSize)); + + if (range.Address == InvalidAddress) + { + ranges.Add(new MemoryRange(range.Address, sliceSize)); + } + else + { + ranges.Add(new MemoryRange(range.Address + offset, sliceSize)); + } + size -= sliceSize; } From da073fce6127243fcd93b736cde951c4e835e508 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Tue, 14 Mar 2023 20:33:44 +0000 Subject: [PATCH 401/737] GPU: Fast path for adding one texture view to a group (#4528) * GPU: Fast path for adding one texture view to a group Texture group handles must store a list of their overlapping views, so they can be properly notified when a write is detected, and a few other things relating to texture readback. This is generally created when the group is established, with each handle looping over all views to find its overlaps. This whole process was also done when only a single view was added (and no handles were changed), however... Sonic Frontiers had a huge cubemap array with 7350 faces (175 cubemaps * 6 faces * 7 levels), so iterating over both handles and existing views added up very fast. Since we are only adding a single view, we only need to _add_ that view to the existing overlaps, rather than recalculate them all. This greatly improves performance during loading screens and a few seconds into gameplay on the "open zone" sections of Sonic Frontiers. May improve loading times or stutters on some other games. Note that the current texture cache rules will cause these views to fall out of the cache, as there are more than the hard cap, so the cost will be repaid when reloading the open zone. I also added some code to properly remove overlaps when texture views are removed, since it seems that was missing. This can be improved further by only iterating handles that overlap the view (filter by range), but so can a few places in TextureGroup, so better to do all at once. The full generation of overlaps could probably be improved in a similar way. I recommend testing a few games to make sure nothing breaks. * Address feedback --- Ryujinx.Graphics.Gpu/Image/Texture.cs | 4 +- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 38 ++++++++++++++++--- .../Image/TextureGroupHandle.cs | 36 ++++++++++++++++++ 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 363f0f73a..b784a5455 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Gpu.Image texture._viewStorage = this; - Group.UpdateViews(_views); + Group.UpdateViews(_views, texture); if (texture.Group != null && texture.Group != Group) { @@ -384,6 +384,8 @@ namespace Ryujinx.Graphics.Gpu.Image { _views.Remove(texture); + Group.RemoveView(texture); + texture._viewStorage = texture; DecrementReferenceCount(); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index d9b620aae..b59a9d086 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -989,7 +989,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// Update the views in this texture group, rebuilding the memory tracking if required. /// </summary> /// <param name="views">The views list of the storage texture</param> - public void UpdateViews(List<Texture> views) + /// <param name="texture">The texture that has been added, if that is the only change, otherwise null</param> + public void UpdateViews(List<Texture> views, Texture texture) { // This is saved to calculate overlapping views for each handle. _views = views; @@ -1027,17 +1028,44 @@ namespace Ryujinx.Graphics.Gpu.Image if (!regionsRebuilt) { - // Must update the overlapping views on all handles, but only if they were not just recreated. - - foreach (TextureGroupHandle handle in _handles) + if (texture != null) { - handle.RecalculateOverlaps(this, views); + int offset = FindOffset(texture); + + foreach (TextureGroupHandle handle in _handles) + { + handle.AddOverlap(offset, texture); + } + } + else + { + // Must update the overlapping views on all handles, but only if they were not just recreated. + + foreach (TextureGroupHandle handle in _handles) + { + handle.RecalculateOverlaps(this, views); + } } } SignalAllDirty(); } + + /// <summary> + /// Removes a view from the group, removing it from all overlap lists. + /// </summary> + /// <param name="view">View to remove from the group</param> + public void RemoveView(Texture view) + { + int offset = FindOffset(view); + + foreach (TextureGroupHandle handle in _handles) + { + handle.RemoveOverlap(offset, view); + } + } + /// <summary> /// Inherit handle state from an old set of handles, such as modified and dirty flags. /// </summary> diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index 1b83cb558..ebb4e9aeb 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -159,6 +159,42 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Adds a single texture view as an overlap if its range overlaps. + /// </summary> + /// <param name="offset">The offset of the view in the group</param> + /// <param name="view">The texture to add as an overlap</param> + public void AddOverlap(int offset, Texture view) + { + // Overlaps can be accessed from the memory tracking signal handler, so access must be atomic. + + if (OverlapsWith(offset, (int)view.Size)) + { + lock (Overlaps) + { + Overlaps.Add(view); + } + } + } + + /// <summary> + /// Removes a single texture view as an overlap if its range overlaps. + /// </summary> + /// <param name="offset">The offset of the view in the group</param> + /// <param name="view">The texture to add as an overlap</param> + public void RemoveOverlap(int offset, Texture view) + { + // Overlaps can be accessed from the memory tracking signal handler, so access must be atomic. + + if (OverlapsWith(offset, (int)view.Size)) + { + lock (Overlaps) + { + Overlaps.Remove(view); + } + } + } + /// <summary> /// Registers a sync action to happen for this handle, and an interim flush action on the tracking handle. /// </summary> From b72916fbc15d9f112f5d2944034b38f1fd43e7b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:50:52 +0100 Subject: [PATCH 402/737] nuget: bump UnicornEngine.Unicorn (#4543) Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-f7c841d to 2.0.2-rc1-fb78016. - [Release notes](https://github.com/unicorn-engine/unicorn/releases) - [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog) - [Commits](https://github.com/unicorn-engine/unicorn/commits) --- updated-dependencies: - dependency-name: UnicornEngine.Unicorn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 953591003..eda7d395a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -46,7 +46,7 @@ <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" /> - <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" /> + <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> </ItemGroup> </Project> From 7870423671cba17c8aad9bb93a67c84bda441366 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:55:19 +0100 Subject: [PATCH 403/737] Update syscall capabilites to include SVCs from FW 15.0.0 (#4530) * Add CapabilityType enum * Add SupervisorCallCount * kernel: Add CapabilityExtensions & Change type of capabilities to uint * Remove private setter from Mask arrays * Pass ReadOnlySpan directly & Remove redundant type casts --- Ryujinx.HLE/HOS/Horizon.cs | 4 +- Ryujinx.HLE/HOS/Kernel/KernelConstants.cs | 4 +- Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 2 +- .../Kernel/Process/CapabilityExtensions.cs | 22 ++++ .../HOS/Kernel/Process/CapabilityType.cs | 19 +++ .../HOS/Kernel/Process/KHandleTable.cs | 6 +- Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 12 +- .../Kernel/Process/KProcessCapabilities.cs | 109 ++++++++++-------- .../Kernel/Process/ProcessCreationFlags.cs | 5 +- .../HOS/Kernel/SupervisorCall/Syscall.cs | 4 +- Ryujinx.HLE/HOS/ProgramLoader.cs | 8 +- Ryujinx.HLE/HOS/Services/ServerBase.cs | 2 +- .../Loaders/Executables/KipExecutable.cs | 6 +- 13 files changed, 128 insertions(+), 75 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 9908cbba8..2b77a7c2e 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -338,7 +338,7 @@ namespace Ryujinx.HLE.HOS ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); - int[] defaultCapabilities = new int[] + uint[] defaultCapabilities = new uint[] { 0x030363F7, 0x1FFFFFCF, @@ -552,4 +552,4 @@ namespace Ryujinx.HLE.HOS IsPaused = pause; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs b/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs index 3817b0aa3..28db750c7 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs @@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel public const int InitialKipId = 1; public const int InitialProcessId = 0x51; + public const int SupervisorCallCount = 0xC0; + public const int MemoryBlockAllocatorSize = 0x2710; public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase; @@ -15,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Kernel public const ulong CounterFrequency = 19200000; } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs index 21d7d90c4..c66f4b578 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel public static Result StartInitialProcess( KernelContext context, ProcessCreationInfo creationInfo, - ReadOnlySpan<int> capabilities, + ReadOnlySpan<uint> capabilities, int mainThreadPriority, ThreadStart customThreadStart) { diff --git a/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs new file mode 100644 index 000000000..66d56fe31 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs @@ -0,0 +1,22 @@ +using System.Numerics; + +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + static class CapabilityExtensions + { + public static CapabilityType GetCapabilityType(this uint cap) + { + return (CapabilityType)(((cap + 1) & ~cap) - 1); + } + + public static uint GetFlag(this CapabilityType type) + { + return (uint)type + 1; + } + + public static uint GetId(this CapabilityType type) + { + return (uint)BitOperations.TrailingZeroCount(type.GetFlag()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs new file mode 100644 index 000000000..51d92316d --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + enum CapabilityType : uint + { + CorePriority = (1u << 3) - 1, + SyscallMask = (1u << 4) - 1, + MapRange = (1u << 6) - 1, + MapIoPage = (1u << 7) - 1, + MapRegion = (1u << 10) - 1, + InterruptPair = (1u << 11) - 1, + ProgramType = (1u << 13) - 1, + KernelVersion = (1u << 14) - 1, + HandleTable = (1u << 15) - 1, + DebugFlags = (1u << 16) - 1, + + Invalid = 0u, + Padding = ~0u + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs index c15ebef5c..50f04e90c 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private int _activeSlotsCount; - private int _size; + private uint _size; private ushort _idCounter; @@ -28,9 +28,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _context = context; } - public Result Initialize(int size) + public Result Initialize(uint size) { - if ((uint)size > 1024) + if (size > 1024) { return KernelResult.OutOfMemory; } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 8d9cd242f..21e89944e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -16,11 +16,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { class KProcess : KSynchronizationObject { - public const int KernelVersionMajor = 10; - public const int KernelVersionMinor = 4; - public const int KernelVersionRevision = 0; + public const uint KernelVersionMajor = 10; + public const uint KernelVersionMinor = 4; + public const uint KernelVersionRevision = 0; - public const int KernelVersionPacked = + public const uint KernelVersionPacked = (KernelVersionMajor << 19) | (KernelVersionMinor << 15) | (KernelVersionRevision << 0); @@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public Result InitializeKip( ProcessCreationInfo creationInfo, - ReadOnlySpan<int> capabilities, + ReadOnlySpan<uint> capabilities, KPageList pageList, KResourceLimit resourceLimit, MemoryRegion memRegion, @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public Result Initialize( ProcessCreationInfo creationInfo, - ReadOnlySpan<int> capabilities, + ReadOnlySpan<uint> capabilities, KResourceLimit resourceLimit, MemoryRegion memRegion, IProcessContextFactory contextFactory, diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index ef55a165f..c99e31123 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -1,4 +1,3 @@ -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; @@ -9,48 +8,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { class KProcessCapabilities { - public byte[] SvcAccessMask { get; private set; } - public byte[] IrqAccessMask { get; private set; } + public byte[] SvcAccessMask { get; } + public byte[] IrqAccessMask { get; } public ulong AllowedCpuCoresMask { get; private set; } public ulong AllowedThreadPriosMask { get; private set; } - public int DebuggingFlags { get; private set; } - public int HandleTableSize { get; private set; } - public int KernelReleaseVersion { get; private set; } - public int ApplicationType { get; private set; } + public uint DebuggingFlags { get; private set; } + public uint HandleTableSize { get; private set; } + public uint KernelReleaseVersion { get; private set; } + public uint ApplicationType { get; private set; } public KProcessCapabilities() { - SvcAccessMask = new byte[0x10]; + // length / number of bits of the underlying type + SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8]; IrqAccessMask = new byte[0x80]; } - public Result InitializeForKernel(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager) + public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager) { AllowedCpuCoresMask = 0xf; AllowedThreadPriosMask = ulong.MaxValue; - DebuggingFlags &= ~3; + DebuggingFlags &= ~3u; KernelReleaseVersion = KProcess.KernelVersionPacked; return Parse(capabilities, memoryManager); } - public Result InitializeForUser(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager) + public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager) { return Parse(capabilities, memoryManager); } - private Result Parse(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager) + private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager) { int mask0 = 0; int mask1 = 0; for (int index = 0; index < capabilities.Length; index++) { - int cap = capabilities[index]; + uint cap = capabilities[index]; - if (((cap + 1) & ~cap) != 0x40) + if (cap.GetCapabilityType() != CapabilityType.MapRange) { Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager); @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return KernelResult.InvalidCombination; } - int prevCap = cap; + uint prevCap = cap; cap = capabilities[++index]; @@ -85,8 +85,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return KernelResult.InvalidSize; } - long address = ((long)(uint)prevCap << 5) & 0xffffff000; - long size = ((long)(uint)cap << 5) & 0xfffff000; + long address = ((long)prevCap << 5) & 0xffffff000; + long size = ((long)cap << 5) & 0xfffff000; if (((ulong)(address + size - 1) >> 36) != 0) { @@ -118,20 +118,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Result.Success; } - private Result ParseCapability(int cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) + private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) { - int code = (cap + 1) & ~cap; + CapabilityType code = cap.GetCapabilityType(); - if (code == 1) + if (code == CapabilityType.Invalid) { return KernelResult.InvalidCapability; } - else if (code == 0) + else if (code == CapabilityType.Padding) { return Result.Success; } - int codeMask = 1 << (32 - BitOperations.LeadingZeroCount((uint)code + 1)); + int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1)); // Check if the property was already set. if (((mask0 & codeMask) & 0x1e008) != 0) @@ -143,23 +143,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process switch (code) { - case 8: + case CapabilityType.CorePriority: { if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) { return KernelResult.InvalidCapability; } - int lowestCpuCore = (cap >> 16) & 0xff; - int highestCpuCore = (cap >> 24) & 0xff; + uint lowestCpuCore = (cap >> 16) & 0xff; + uint highestCpuCore = (cap >> 24) & 0xff; if (lowestCpuCore > highestCpuCore) { return KernelResult.InvalidCombination; } - int highestThreadPrio = (cap >> 4) & 0x3f; - int lowestThreadPrio = (cap >> 10) & 0x3f; + uint highestThreadPrio = (cap >> 4) & 0x3f; + uint lowestThreadPrio = (cap >> 10) & 0x3f; if (lowestThreadPrio > highestThreadPrio) { @@ -177,9 +177,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x10: + case CapabilityType.SyscallMask: { - int slot = (cap >> 29) & 7; + int slot = ((int)cap >> 29) & 7; int svcSlotMask = 1 << slot; @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process mask1 |= svcSlotMask; - int svcMask = (cap >> 5) & 0xffffff; + uint svcMask = (cap >> 5) & 0xffffff; int baseSvc = slot * 24; @@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process int svcId = baseSvc + index; - if (svcId > 0x7f) + if (svcId >= KernelConstants.SupervisorCallCount) { return KernelResult.MaximumExceeded; } @@ -214,20 +214,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x80: + case CapabilityType.MapIoPage: { - long address = ((long)(uint)cap << 4) & 0xffffff000; + long address = ((long)cap << 4) & 0xffffff000; memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite); break; } - case 0x800: + case CapabilityType.MapRegion: + { + // TODO: Implement capabilities for MapRegion + + break; + } + + case CapabilityType.InterruptPair: { // TODO: GIC distributor check. - int irq0 = (cap >> 12) & 0x3ff; - int irq1 = (cap >> 22) & 0x3ff; + int irq0 = ((int)cap >> 12) & 0x3ff; + int irq1 = ((int)cap >> 22) & 0x3ff; if (irq0 != 0x3ff) { @@ -242,11 +249,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x2000: + case CapabilityType.ProgramType: { - int applicationType = cap >> 14; + uint applicationType = (cap >> 14); - if ((uint)applicationType > 7) + if (applicationType > 7) { return KernelResult.ReservedValue; } @@ -256,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x4000: + case CapabilityType.KernelVersion: { // Note: This check is bugged on kernel too, we are just replicating the bug here. if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000) @@ -269,11 +276,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x8000: + case CapabilityType.HandleTable: { - int handleTableSize = cap >> 26; + uint handleTableSize = cap >> 26; - if ((uint)handleTableSize > 0x3ff) + if (handleTableSize > 0x3ff) { return KernelResult.ReservedValue; } @@ -283,16 +290,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x10000: + case CapabilityType.DebugFlags: { - int debuggingFlags = cap >> 19; + uint debuggingFlags = cap >> 19; - if ((uint)debuggingFlags > 3) + if (debuggingFlags > 3) { return KernelResult.ReservedValue; } - DebuggingFlags &= ~3; + DebuggingFlags &= ~3u; DebuggingFlags |= debuggingFlags; break; @@ -304,18 +311,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Result.Success; } - private static ulong GetMaskFromMinMax(int min, int max) + private static ulong GetMaskFromMinMax(uint min, uint max) { - int range = max - min + 1; + uint range = max - min + 1; if (range == 64) { return ulong.MaxValue; } - ulong mask = (1UL << range) - 1; + ulong mask = (1UL << (int)range) - 1; - return mask << min; + return mask << (int)min; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs index a34481e55..a79978ac4 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs @@ -1,5 +1,8 @@ -namespace Ryujinx.HLE.HOS.Kernel.Process +using System; + +namespace Ryujinx.HLE.HOS.Kernel.Process { + [Flags] enum ProcessCreationFlags { Is64Bit = 1 << 0, diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index e23274ebc..eef78e186 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public Result CreateProcess( out int handle, ProcessCreationInfo info, - ReadOnlySpan<int> capabilities, + ReadOnlySpan<uint> capabilities, IProcessContextFactory contextFactory, ThreadStart customThreadStart = null) { @@ -3002,4 +3002,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return (address & 3) != 0; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 158ab701f..4ebcb7e7d 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL; - ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset; + ulong codeAddress = codeBaseAddress + kip.TextOffset; ProcessCreationFlags flags = 0; @@ -231,13 +231,13 @@ namespace Ryujinx.HLE.HOS nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize); - nsoBase[index] = codeStart + (ulong)codeSize; + nsoBase[index] = codeStart + codeSize; codeSize += nsoSize; if (arguments != null && argsSize == 0) { - argsStart = (ulong)codeSize; + argsStart = codeSize; argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize); @@ -318,7 +318,7 @@ namespace Ryujinx.HLE.HOS result = process.Initialize( creationInfo, - MemoryMarshal.Cast<byte, int>(npdm.KernelCapabilityData).ToArray(), + MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData), resourceLimit, memoryRegion, processContextFactory); diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 50f6c99e6..d4382a643 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services // not large enough. private const int PointerBufferSize = 0x8000; - private readonly static int[] DefaultCapabilities = new int[] + private readonly static uint[] DefaultCapabilities = new uint[] { 0x030363F7, 0x1FFFFFCF, diff --git a/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs b/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs index 2073a7e2d..ad2b681cd 100644 --- a/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.Loaders.Executables public uint DataSize { get; } public uint BssSize { get; } - public int[] Capabilities { get; } + public uint[] Capabilities { get; } public bool UsesSecureMemory { get; } public bool Is64BitAddressSpace { get; } public bool Is64Bit { get; } @@ -57,11 +57,11 @@ namespace Ryujinx.HLE.Loaders.Executables Version = reader.Version; Name = reader.Name.ToString(); - Capabilities = new int[32]; + Capabilities = new uint[32]; for (int index = 0; index < Capabilities.Length; index++) { - Capabilities[index] = (int)reader.Capabilities[index]; + Capabilities[index] = reader.Capabilities[index]; } reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure(); From 5131b71437b36f9b876046a2fddd5465652e0f10 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Fri, 17 Mar 2023 08:14:50 -0400 Subject: [PATCH 404/737] Reducing memory allocations (#4537) * add RecyclableMemoryStream dependency and MemoryStreamManager * organize BinaryReader/BinaryWriter extensions * add StreamExtensions to reduce need for BinaryWriter * simple replacments of MemoryStream with RecyclableMemoryStream * add write ReadOnlySequence<byte> support to IVirtualMemoryManager * avoid 0-length array creation * rework IpcMessage and related types to greatly reduce memory allocation by using RecylableMemoryStream, keeping streams around longer, avoiding their creation when possible, and avoiding creation of BinaryReader and BinaryWriter when possible * reduce LINQ-induced memory allocations with custom methods to query KPriorityQueue * use RecyclableMemoryStream in StreamUtils, and use StreamUtils in EmbeddedResources * add constants for nanosecond/millisecond conversions * code formatting * XML doc adjustments * fix: StreamExtension.WriteByte not writing non-zero values for lengths <= 16 * XML Doc improvements. Implement StreamExtensions.WriteByte() block writes for large-enough count values. * add copyless path for StreamExtension.Write(ReadOnlySpan<int>) * add default implementation of IVirtualMemoryManager.Write(ulong, ReadOnlySequence<byte>); remove previous explicit implementations * code style fixes * remove LINQ completely from KScheduler/KPriorityQueue by implementing a custom struct-based enumerator --- ARMeilleure/CodeGen/Arm64/CodeGenContext.cs | 3 +- ARMeilleure/CodeGen/X86/Assembler.cs | 3 +- ARMeilleure/CodeGen/X86/CodeGenContext.cs | 3 +- ARMeilleure/Translation/PTC/Ptc.cs | 7 +- ARMeilleure/Translation/PTC/PtcProfiler.cs | 5 +- Directory.Packages.props | 1 + .../Extensions/BinaryReaderExtensions.cs | 14 - .../Extensions/BinaryWriterExtensions.cs | 28 ++ Ryujinx.Common/Extensions/StreamExtensions.cs | 138 ++++++++ Ryujinx.Common/Memory/MemoryStreamManager.cs | 99 ++++++ Ryujinx.Common/Ryujinx.Common.csproj | 1 + Ryujinx.Common/Utilities/EmbeddedResources.cs | 16 +- Ryujinx.Common/Utilities/StreamUtils.cs | 18 +- Ryujinx.Cpu/MemoryHelper.cs | 8 +- .../DiskCache/ShaderBinarySerializer.cs | 15 +- Ryujinx.HLE/FileSystem/ContentManager.cs | 11 +- .../HOS/Applets/Browser/BrowserApplet.cs | 5 +- .../Applets/Controller/ControllerApplet.cs | 5 +- .../PlayerSelect/PlayerSelectApplet.cs | 5 +- Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs | 69 ++-- Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 213 ++++++------ Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs | 8 +- Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 25 +- .../HOS/Kernel/SupervisorCall/Syscall.cs | 2 +- .../HOS/Kernel/Threading/KPriorityQueue.cs | 149 +++++++-- .../HOS/Kernel/Threading/KScheduler.cs | 52 +-- .../HOS/Kernel/Threading/KSynchronization.cs | 2 +- .../Am/AppletAE/Storage/StorageHelper.cs | 5 +- .../HOS/Services/Sdb/Pl/SharedFontManager.cs | 3 +- Ryujinx.HLE/HOS/Services/ServerBase.cs | 310 +++++++++--------- Ryujinx.HLE/Utilities/StringUtils.cs | 10 +- Ryujinx.Input/Motion/CemuHook/Client.cs | 5 +- Ryujinx.Memory/IVirtualMemoryManager.cs | 16 + Ryujinx/Ui/Windows/AvatarWindow.cs | 7 +- .../Ui/Windows/UserProfilesManagerWindow.cs | 3 +- 35 files changed, 838 insertions(+), 426 deletions(-) create mode 100644 Ryujinx.Common/Extensions/BinaryWriterExtensions.cs create mode 100644 Ryujinx.Common/Extensions/StreamExtensions.cs create mode 100644 Ryujinx.Common/Memory/MemoryStreamManager.cs diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs index cebfbde12..0dd5355f4 100644 --- a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs +++ b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs @@ -1,6 +1,7 @@ using ARMeilleure.CodeGen.Linking; using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.IntermediateRepresentation; +using Ryujinx.Common.Memory; using System; using System.Collections.Generic; using System.IO; @@ -59,7 +60,7 @@ namespace ARMeilleure.CodeGen.Arm64 public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable) { - _stream = new MemoryStream(); + _stream = MemoryStreamManager.Shared.GetStream(); AllocResult = allocResult; diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs index c15deadc5..2ea4208b3 100644 --- a/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/ARMeilleure/CodeGen/X86/Assembler.cs @@ -1,5 +1,6 @@ using ARMeilleure.CodeGen.Linking; using ARMeilleure.IntermediateRepresentation; +using Ryujinx.Common.Memory; using System; using System.Collections.Generic; using System.Diagnostics; @@ -1285,7 +1286,7 @@ namespace ARMeilleure.CodeGen.X86 // Write the code, ignoring the dummy bytes after jumps, into a new stream. _stream.Seek(0, SeekOrigin.Begin); - using var codeStream = new MemoryStream(); + using var codeStream = MemoryStreamManager.Shared.GetStream(); var assembler = new Assembler(codeStream, HasRelocs); bool hasRelocs = HasRelocs; diff --git a/ARMeilleure/CodeGen/X86/CodeGenContext.cs b/ARMeilleure/CodeGen/X86/CodeGenContext.cs index eee71bd78..899487241 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenContext.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenContext.cs @@ -1,5 +1,6 @@ using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.IntermediateRepresentation; +using Ryujinx.Common.Memory; using System.IO; using System.Numerics; @@ -22,7 +23,7 @@ namespace ARMeilleure.CodeGen.X86 public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable) { - _stream = new MemoryStream(); + _stream = MemoryStreamManager.Shared.GetStream(); _blockLabels = new Operand[blocksCount]; AllocResult = allocResult; diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 276ec788b..0b23fd043 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -6,6 +6,7 @@ using ARMeilleure.Memory; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -150,10 +151,10 @@ namespace ARMeilleure.Translation.PTC private void InitializeCarriers() { - _infosStream = new MemoryStream(); + _infosStream = MemoryStreamManager.Shared.GetStream(); _codesList = new List<byte[]>(); - _relocsStream = new MemoryStream(); - _unwindInfosStream = new MemoryStream(); + _relocsStream = MemoryStreamManager.Shared.GetStream(); + _unwindInfosStream = MemoryStreamManager.Shared.GetStream(); } private void DisposeCarriers() diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index 030ccff5f..391e29c76 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -1,6 +1,7 @@ using ARMeilleure.State; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using System; using System.Buffers.Binary; using System.Collections.Concurrent; @@ -182,7 +183,7 @@ namespace ARMeilleure.Translation.PTC return false; } - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) { Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); @@ -274,7 +275,7 @@ namespace ARMeilleure.Translation.PTC outerHeader.SetHeaderHash(); - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) { Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); diff --git a/Directory.Packages.props b/Directory.Packages.props index eda7d395a..14d440753 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,6 +22,7 @@ <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> + <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> <PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" /> diff --git a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs index ea404d2a2..21da6fc01 100644 --- a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs +++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs @@ -12,19 +12,5 @@ namespace Ryujinx.Common { return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0]; } - - public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value) - where T : unmanaged - { - ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); - - writer.Write(data); - } - - public static void Write(this BinaryWriter writer, UInt128 value) - { - writer.Write((ulong)value); - writer.Write((ulong)(value >> 64)); - } } } diff --git a/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs new file mode 100644 index 000000000..fddc8c1b7 --- /dev/null +++ b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common +{ + public static class BinaryWriterExtensions + { + public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value) + where T : unmanaged + { + ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); + + writer.Write(data); + } + + public static void Write(this BinaryWriter writer, UInt128 value) + { + writer.Write((ulong)value); + writer.Write((ulong)(value >> 64)); + } + + public static void Write(this BinaryWriter writer, MemoryStream stream) + { + stream.CopyTo(writer.BaseStream); + } + } +} diff --git a/Ryujinx.Common/Extensions/StreamExtensions.cs b/Ryujinx.Common/Extensions/StreamExtensions.cs new file mode 100644 index 000000000..f6fc870ab --- /dev/null +++ b/Ryujinx.Common/Extensions/StreamExtensions.cs @@ -0,0 +1,138 @@ +using System; +using System.Buffers.Binary; +using System.IO; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common +{ + public static class StreamExtensions + { + /// <summary> + /// Writes a <cref="ReadOnlySpan<int>" /> to this stream. + /// + /// This default implementation converts each buffer value to a stack-allocated + /// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />. + /// </summary> + /// <param name="stream">The stream to be written to</param> + /// <param name="buffer">The buffer of values to be written</param> + public static void Write(this Stream stream, ReadOnlySpan<int> buffer) + { + if (buffer.Length == 0) + { + return; + } + + if (BitConverter.IsLittleEndian) + { + ReadOnlySpan<byte> byteBuffer = MemoryMarshal.Cast<int, byte>(buffer); + stream.Write(byteBuffer); + } + else + { + Span<byte> byteBuffer = stackalloc byte[sizeof(int)]; + + foreach (int value in buffer) + { + BinaryPrimitives.WriteInt32LittleEndian(byteBuffer, value); + stream.Write(byteBuffer); + } + } + } + + /// <summary> + /// Writes a four-byte signed integer to this stream. The current position + /// of the stream is advanced by four. + /// </summary> + /// <param name="stream">The stream to be written to</param> + /// <param name="value">The value to be written</param> + public static void Write(this Stream stream, int value) + { + Span<byte> buffer = stackalloc byte[sizeof(int)]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// <summary> + /// Writes an eight-byte signed integer to this stream. The current position + /// of the stream is advanced by eight. + /// </summary> + /// <param name="stream">The stream to be written to</param> + /// <param name="value">The value to be written</param> + public static void Write(this Stream stream, long value) + { + Span<byte> buffer = stackalloc byte[sizeof(long)]; + BinaryPrimitives.WriteInt64LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// <summary> + // Writes a four-byte unsigned integer to this stream. The current position + // of the stream is advanced by four. + /// </summary> + /// <param name="stream">The stream to be written to</param> + /// <param name="value">The value to be written</param> + public static void Write(this Stream stream, uint value) + { + Span<byte> buffer = stackalloc byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// <summary> + /// Writes an eight-byte unsigned integer to this stream. The current + /// position of the stream is advanced by eight. + /// </summary> + /// <param name="stream">The stream to be written to</param> + /// <param name="value">The value to be written</param> + public static void Write(this Stream stream, ulong value) + { + Span<byte> buffer = stackalloc byte[sizeof(ulong)]; + BinaryPrimitives.WriteUInt64LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// <summary> + /// Writes the contents of source to stream by calling source.CopyTo(stream). + /// Provides consistency with other Stream.Write methods. + /// </summary> + /// <param name="stream">The stream to be written to</param> + /// <param name="source">The stream to be read from</param> + public static void Write(this Stream stream, Stream source) + { + source.CopyTo(stream); + } + + /// <summary> + /// Writes a sequence of bytes to the Stream. + /// </summary> + /// <param name="stream">The stream to be written to.</param> + /// <param name="value">The byte to be written</param> + /// <param name="count">The number of times the value should be written</param> + public static void WriteByte(this Stream stream, byte value, int count) + { + if (count <= 0) + { + return; + } + + const int BlockSize = 16; + + int blockCount = count / BlockSize; + if (blockCount > 0) + { + Span<byte> span = stackalloc byte[BlockSize]; + span.Fill(value); + for (int x = 0; x < blockCount; x++) + { + stream.Write(span); + } + } + + int nonBlockBytes = count % BlockSize; + for (int x = 0; x < nonBlockBytes; x++) + { + stream.WriteByte(value); + } + } + } +} diff --git a/Ryujinx.Common/Memory/MemoryStreamManager.cs b/Ryujinx.Common/Memory/MemoryStreamManager.cs new file mode 100644 index 000000000..68b829993 --- /dev/null +++ b/Ryujinx.Common/Memory/MemoryStreamManager.cs @@ -0,0 +1,99 @@ +using Microsoft.IO; +using System; + +namespace Ryujinx.Common.Memory +{ + public static class MemoryStreamManager + { + private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager(); + + /// <summary> + /// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x + /// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use + /// and b) return them as <c>RecyclableMemoryStream</c> so we don't have to cast. + /// </summary> + public static class Shared + { + /// <summary> + /// Retrieve a new <c>MemoryStream</c> object with no tag and a default initial capacity. + /// </summary> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream() + => new RecyclableMemoryStream(_shared); + + /// <summary> + /// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream(byte[] buffer) + => GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length); + + /// <summary> + /// Retrieve a new <c>MemoryStream</c> object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream(ReadOnlySpan<byte> buffer) + => GetStream(Guid.NewGuid(), null, buffer); + + /// <summary> + /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks> + /// <param name="id">A unique identifier which can be used to trace usages of the stream</param> + /// <param name="tag">A tag which can be used to track the source of the stream</param> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan<byte> buffer) + { + RecyclableMemoryStream stream = null; + try + { + stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length); + stream.Write(buffer); + stream.Position = 0; + return stream; + } + catch + { + stream?.Dispose(); + throw; + } + } + + /// <summary> + /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned</remarks> + /// <param name="id">A unique identifier which can be used to trace usages of the stream</param> + /// <param name="tag">A tag which can be used to track the source of the stream</param> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <param name="offset">The offset from the start of the buffer to copy from</param> + /// <param name="count">The number of bytes to copy from the buffer</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream(Guid id, string tag, byte[] buffer, int offset, int count) + { + RecyclableMemoryStream stream = null; + try + { + stream = new RecyclableMemoryStream(_shared, id, tag, count); + stream.Write(buffer, offset, count); + stream.Position = 0; + return stream; + } + catch + { + stream?.Dispose(); + throw; + } + } + } + } +} diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj index c307f524e..c02b11e0c 100644 --- a/Ryujinx.Common/Ryujinx.Common.csproj +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -7,6 +7,7 @@ </PropertyGroup> <ItemGroup> + <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" /> <PackageReference Include="MsgPack.Cli" /> <PackageReference Include="System.Management" /> </ItemGroup> diff --git a/Ryujinx.Common/Utilities/EmbeddedResources.cs b/Ryujinx.Common/Utilities/EmbeddedResources.cs index e7c8d7d70..22b55f161 100644 --- a/Ryujinx.Common/Utilities/EmbeddedResources.cs +++ b/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -1,3 +1,5 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; using System; using System.IO; using System.Linq; @@ -38,12 +40,7 @@ namespace Ryujinx.Common return null; } - using (var mem = new MemoryStream()) - { - stream.CopyTo(mem); - - return mem.ToArray(); - } + return StreamUtils.StreamToBytes(stream); } } @@ -56,12 +53,7 @@ namespace Ryujinx.Common return null; } - using (var mem = new MemoryStream()) - { - await stream.CopyToAsync(mem); - - return mem.ToArray(); - } + return await StreamUtils.StreamToBytesAsync(stream); } } diff --git a/Ryujinx.Common/Utilities/StreamUtils.cs b/Ryujinx.Common/Utilities/StreamUtils.cs index 9bd03ec90..da97188d8 100644 --- a/Ryujinx.Common/Utilities/StreamUtils.cs +++ b/Ryujinx.Common/Utilities/StreamUtils.cs @@ -1,4 +1,8 @@ -using System.IO; +using Microsoft.IO; +using Ryujinx.Common.Memory; +using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Ryujinx.Common.Utilities { @@ -6,12 +10,22 @@ namespace Ryujinx.Common.Utilities { public static byte[] StreamToBytes(Stream input) { - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) { input.CopyTo(stream); return stream.ToArray(); } } + + public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default) + { + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) + { + await input.CopyToAsync(stream, cancellationToken); + + return stream.ToArray(); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs index 64ff360e5..194a0c35d 100644 --- a/Ryujinx.Cpu/MemoryHelper.cs +++ b/Ryujinx.Cpu/MemoryHelper.cs @@ -1,4 +1,6 @@ -using Ryujinx.Memory; +using Microsoft.IO; +using Ryujinx.Common.Memory; +using Ryujinx.Memory; using System; using System.IO; using System.Runtime.CompilerServices; @@ -40,7 +42,7 @@ namespace Ryujinx.Cpu public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1) { - using (MemoryStream ms = new MemoryStream()) + using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream()) { for (long offs = 0; offs < maxSize || maxSize == -1; offs++) { @@ -54,7 +56,7 @@ namespace Ryujinx.Cpu ms.WriteByte(value); } - return Encoding.ASCII.GetString(ms.ToArray()); + return Encoding.ASCII.GetString(ms.GetReadOnlySequence()); } } } diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs index 5b430e1af..77e526672 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs @@ -1,3 +1,5 @@ +using Ryujinx.Common; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -11,16 +13,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { public static byte[] Pack(ShaderSource[] sources) { - using MemoryStream output = new MemoryStream(); - using BinaryWriter writer = new BinaryWriter(output); + using MemoryStream output = MemoryStreamManager.Shared.GetStream(); - writer.Write(sources.Length); + output.Write(sources.Length); - for (int i = 0; i < sources.Length; i++) + foreach (ShaderSource source in sources) { - writer.Write((int)sources[i].Stage); - writer.Write(sources[i].BinaryCode.Length); - writer.Write(sources[i].BinaryCode); + output.Write((int)source.Stage); + output.Write(source.BinaryCode.Length); + output.Write(source.BinaryCode); } return output.ToArray(); diff --git a/Ryujinx.HLE/FileSystem/ContentManager.cs b/Ryujinx.HLE/FileSystem/ContentManager.cs index 4e3940081..9facdd0b7 100644 --- a/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.Ncm; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Services.Ssl; @@ -637,12 +638,12 @@ namespace Ryujinx.HLE.FileSystem private Stream GetZipStream(ZipArchiveEntry entry) { - MemoryStream dest = new MemoryStream(); + MemoryStream dest = MemoryStreamManager.Shared.GetStream(); - Stream src = entry.Open(); - - src.CopyTo(dest); - src.Dispose(); + using (Stream src = entry.Open()) + { + src.CopyTo(dest); + } return dest; } diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs index 2d5509ff8..952afcd5f 100644 --- a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs @@ -1,5 +1,6 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Am.AppletAE; using System; using System.Collections.Generic; @@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser private byte[] BuildResponseOld(WebCommonReturnValue result) { - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { writer.WriteStruct(result); @@ -80,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser } private byte[] BuildResponseNew(List<BrowserOutput> outputArguments) { - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { writer.WriteStruct(new WebArgHeader diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs index 5cdfb3143..5d5a26c23 100644 --- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Am.AppletAE; using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Hid.Types; @@ -123,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Applets private byte[] BuildResponse(ControllerSupportResultInfo result) { - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>()))); @@ -134,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Applets private byte[] BuildResponse() { - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { writer.Write((ulong)ResultCode.Success); diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs index cec9f213e..a8119a470 100644 --- a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs @@ -1,4 +1,5 @@ -using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Am.AppletAE; using System; using System.IO; @@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Applets { UserProfile currentUser = _system.AccountManager.LastOpenedUser; - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { writer.Write((ulong)PlayerSelectResult.Success); diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs index e6ed46138..c7ef7e9cf 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs @@ -1,3 +1,6 @@ +using Microsoft.IO; +using Ryujinx.Common; +using Ryujinx.Common.Memory; using System; using System.IO; @@ -18,20 +21,27 @@ namespace Ryujinx.HLE.HOS.Ipc HasPId = (word & 1) != 0; - ToCopy = new int[(word >> 1) & 0xf]; - ToMove = new int[(word >> 5) & 0xf]; - PId = HasPId ? reader.ReadUInt64() : 0; - for (int index = 0; index < ToCopy.Length; index++) + int toCopySize = (word >> 1) & 0xf; + int[] toCopy = toCopySize == 0 ? Array.Empty<int>() : new int[toCopySize]; + + for (int index = 0; index < toCopy.Length; index++) { - ToCopy[index] = reader.ReadInt32(); + toCopy[index] = reader.ReadInt32(); } - for (int index = 0; index < ToMove.Length; index++) + ToCopy = toCopy; + + int toMoveSize = (word >> 5) & 0xf; + int[] toMove = toMoveSize == 0 ? Array.Empty<int>() : new int[toMoveSize]; + + for (int index = 0; index < toMove.Length; index++) { - ToMove[index] = reader.ReadInt32(); + toMove[index] = reader.ReadInt32(); } + + ToMove = toMove; } public IpcHandleDesc(int[] copy, int[] move) @@ -57,36 +67,27 @@ namespace Ryujinx.HLE.HOS.Ipc return new IpcHandleDesc(Array.Empty<int>(), handles); } - public byte[] GetBytes() + public RecyclableMemoryStream GetStream() { - using (MemoryStream ms = new MemoryStream()) + RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(); + + int word = HasPId ? 1 : 0; + + word |= (ToCopy.Length & 0xf) << 1; + word |= (ToMove.Length & 0xf) << 5; + + ms.Write(word); + + if (HasPId) { - BinaryWriter writer = new BinaryWriter(ms); - - int word = HasPId ? 1 : 0; - - word |= (ToCopy.Length & 0xf) << 1; - word |= (ToMove.Length & 0xf) << 5; - - writer.Write(word); - - if (HasPId) - { - writer.Write(PId); - } - - foreach (int handle in ToCopy) - { - writer.Write(handle); - } - - foreach (int handle in ToMove) - { - writer.Write(handle); - } - - return ms.ToArray(); + ms.Write(PId); } + + ms.Write(ToCopy); + ms.Write(ToMove); + + ms.Position = 0; + return ms; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 55044da40..4e8f2fbfd 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -1,4 +1,8 @@ +using Microsoft.IO; +using Ryujinx.Common; +using Ryujinx.Common.Memory; using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -32,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Ipc ObjectIds = new List<int>(); } - public IpcMessage(byte[] data, long cmdPtr) : this() + public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this() { - using (MemoryStream ms = new MemoryStream(data)) + using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data)) { BinaryReader reader = new BinaryReader(ms); @@ -114,124 +118,119 @@ namespace Ryujinx.HLE.HOS.Ipc for (int index = 0; index < recvListCount; index++) { - RecvListBuff.Add(new IpcRecvListBuffDesc(reader)); + RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64())); } } - public byte[] GetBytes(long cmdPtr, ulong recvListAddr) + public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr) { - using (MemoryStream ms = new MemoryStream()) + RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(); + + int word0; + int word1; + + word0 = (int)Type; + word0 |= (PtrBuff.Count & 0xf) << 16; + word0 |= (SendBuff.Count & 0xf) << 20; + word0 |= (ReceiveBuff.Count & 0xf) << 24; + word0 |= (ExchangeBuff.Count & 0xf) << 28; + + using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream(); + + int dataLength = RawData?.Length ?? 0; + + dataLength = (dataLength + 3) & ~3; + + int rawLength = dataLength; + + int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8); + + // Apparently, padding after Raw Data is 16 bytes, however when there is + // padding before Raw Data too, we need to subtract the size of this padding. + // This is the weirdest padding I've seen so far... + int pad1 = 0x10 - pad0; + + dataLength = (dataLength + pad0 + pad1) / 4; + + word1 = (dataLength & 0x3ff) | (2 << 10); + + if (HandleDesc != null) { - BinaryWriter writer = new BinaryWriter(ms); - - int word0; - int word1; - - word0 = (int)Type; - word0 |= (PtrBuff.Count & 0xf) << 16; - word0 |= (SendBuff.Count & 0xf) << 20; - word0 |= (ReceiveBuff.Count & 0xf) << 24; - word0 |= (ExchangeBuff.Count & 0xf) << 28; - - byte[] handleData = Array.Empty<byte>(); - - if (HandleDesc != null) - { - handleData = HandleDesc.GetBytes(); - } - - int dataLength = RawData?.Length ?? 0; - - dataLength = (dataLength + 3) & ~3; - - int rawLength = dataLength; - - int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8); - - // Apparently, padding after Raw Data is 16 bytes, however when there is - // padding before Raw Data too, we need to subtract the size of this padding. - // This is the weirdest padding I've seen so far... - int pad1 = 0x10 - pad0; - - dataLength = (dataLength + pad0 + pad1) / 4; - - word1 = (dataLength & 0x3ff) | (2 << 10); - - if (HandleDesc != null) - { - word1 |= 1 << 31; - } - - writer.Write(word0); - writer.Write(word1); - writer.Write(handleData); - - for (int index = 0; index < PtrBuff.Count; index++) - { - writer.Write(PtrBuff[index].GetWord0()); - writer.Write(PtrBuff[index].GetWord1()); - } - - ms.Seek(pad0, SeekOrigin.Current); - - if (RawData != null) - { - writer.Write(RawData); - ms.Seek(rawLength - RawData.Length, SeekOrigin.Current); - } - - writer.Write(new byte[pad1]); - writer.Write(recvListAddr); - - return ms.ToArray(); + word1 |= 1 << 31; } + + ms.Write(word0); + ms.Write(word1); + + if (handleDataStream != null) + { + ms.Write(handleDataStream); + } + + foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff) + { + ms.Write(ptrBuffDesc.GetWord0()); + ms.Write(ptrBuffDesc.GetWord1()); + } + + ms.WriteByte(0, pad0); + + if (RawData != null) + { + ms.Write(RawData); + ms.WriteByte(0, rawLength - RawData.Length); + } + + ms.WriteByte(0, pad1); + + ms.Write(recvListAddr); + + ms.Position = 0; + + return ms; } - public byte[] GetBytesTipc() + public RecyclableMemoryStream GetStreamTipc() { Debug.Assert(PtrBuff.Count == 0); - using (MemoryStream ms = new MemoryStream()) + RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(); + + int word0; + int word1; + + word0 = (int)Type; + word0 |= (SendBuff.Count & 0xf) << 20; + word0 |= (ReceiveBuff.Count & 0xf) << 24; + word0 |= (ExchangeBuff.Count & 0xf) << 28; + + using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream(); + + int dataLength = RawData?.Length ?? 0; + + dataLength = ((dataLength + 3) & ~3) / 4; + + word1 = (dataLength & 0x3ff); + + if (HandleDesc != null) { - BinaryWriter writer = new BinaryWriter(ms); - - int word0; - int word1; - - word0 = (int)Type; - word0 |= (SendBuff.Count & 0xf) << 20; - word0 |= (ReceiveBuff.Count & 0xf) << 24; - word0 |= (ExchangeBuff.Count & 0xf) << 28; - - byte[] handleData = Array.Empty<byte>(); - - if (HandleDesc != null) - { - handleData = HandleDesc.GetBytes(); - } - - int dataLength = RawData?.Length ?? 0; - - dataLength = ((dataLength + 3) & ~3) / 4; - - word1 = (dataLength & 0x3ff); - - if (HandleDesc != null) - { - word1 |= 1 << 31; - } - - writer.Write(word0); - writer.Write(word1); - writer.Write(handleData); - - if (RawData != null) - { - writer.Write(RawData); - } - - return ms.ToArray(); + word1 |= 1 << 31; } + + ms.Write(word0); + ms.Write(word1); + + if (handleDataStream != null) + { + ms.Write(handleDataStream); + } + + if (RawData != null) + { + ms.Write(RawData); + } + + return ms; } private long GetPadSize16(long position) diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs index 10406ac7d..bcc9d8f89 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs @@ -13,13 +13,11 @@ namespace Ryujinx.HLE.HOS.Ipc Size = size; } - public IpcRecvListBuffDesc(BinaryReader reader) + public IpcRecvListBuffDesc(ulong packedValue) { - ulong value = reader.ReadUInt64(); + Position = packedValue & 0xffffffffffff; - Position = value & 0xffffffffffff; - - Size = (ushort)(value >> 48); + Size = (ushort)(packedValue >> 48); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs index 030a314f7..1af171b92 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint) { - Object = schedulerObj; + Object = schedulerObj; TimePoint = timePoint; } } @@ -27,6 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common private bool _keepRunning; private long _enforceWakeupFromSpinWait; + private const long NanosecondsPerSecond = 1000000000L; + private const long NanosecondsPerMillisecond = 1000000L; + public KTimeManager(KernelContext context) { _context = context; @@ -55,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { _waitingObjects.Add(new WaitingObject(schedulerObj, timePoint)); - if (timeout < 1000000) + if (timeout < NanosecondsPerMillisecond) { Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1); } @@ -142,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common private WaitingObject GetNextWaitingObject() { WaitingObject selected = null; - + long lowestTimePoint = long.MaxValue; for (int index = _waitingObjects.Count - 1; index >= 0; index--) @@ -161,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common public static long ConvertNanosecondsToMilliseconds(long time) { - time /= 1000000; + time /= NanosecondsPerMillisecond; if ((ulong)time > int.MaxValue) { @@ -173,18 +176,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Common public static long ConvertMillisecondsToNanoseconds(long time) { - return time * 1000000; + return time * NanosecondsPerMillisecond; } public static long ConvertNanosecondsToHostTicks(long ns) { - long nsDiv = ns / 1000000000; - long nsMod = ns % 1000000000; - long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000; - long tickMod = PerformanceCounter.TicksPerSecond % 1000000000; + long nsDiv = ns / NanosecondsPerSecond; + long nsMod = ns % NanosecondsPerSecond; + long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond; + long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond; - long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000; - return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks; + long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond; + return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks; } public static long ConvertGuestTicksToNanoseconds(long ticks) diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index eef78e186..c6467208e 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -553,7 +553,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length]; + KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length]; for (int index = 0; index < handles.Length; index++) { diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs index 2c9d75742..14fba7045 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs @@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { class KPriorityQueue { - private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore; - private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore; + private readonly LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore; + private readonly LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore; - private long[] _scheduledPrioritiesPerCore; - private long[] _suggestedPrioritiesPerCore; + private readonly long[] _scheduledPrioritiesPerCore; + private readonly long[] _suggestedPrioritiesPerCore; public KPriorityQueue() { @@ -32,43 +32,134 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount]; } - public IEnumerable<KThread> SuggestedThreads(int core) + public readonly ref struct KThreadEnumerable { - return Iterate(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core); - } + readonly LinkedList<KThread>[][] _listPerPrioPerCore; + readonly long[] _prios; + readonly int _core; - public IEnumerable<KThread> ScheduledThreads(int core) - { - return Iterate(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core); - } - - private IEnumerable<KThread> Iterate(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core) - { - long prioMask = prios[core]; - - int prio = BitOperations.TrailingZeroCount(prioMask); - - prioMask &= ~(1L << prio); - - while (prio < KScheduler.PrioritiesCount) + public KThreadEnumerable(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core) { - LinkedList<KThread> list = listPerPrioPerCore[prio][core]; + _listPerPrioPerCore = listPerPrioPerCore; + _prios = prios; + _core = core; + } - LinkedListNode<KThread> node = list.First; + public Enumerator GetEnumerator() + { + return new Enumerator(_listPerPrioPerCore, _prios, _core); + } - while (node != null) + public ref struct Enumerator + { + private readonly LinkedList<KThread>[][] _listPerPrioPerCore; + private readonly int _core; + private long _prioMask; + private int _prio; + private LinkedList<KThread> _list; + private LinkedListNode<KThread> _node; + + public Enumerator(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core) { - yield return node.Value; - - node = node.Next; + _listPerPrioPerCore = listPerPrioPerCore; + _core = core; + _prioMask = prios[core]; + _prio = BitOperations.TrailingZeroCount(_prioMask); + _prioMask &= ~(1L << _prio); } - prio = BitOperations.TrailingZeroCount(prioMask); + public KThread Current => _node?.Value; - prioMask &= ~(1L << prio); + public bool MoveNext() + { + _node = _node?.Next; + + if (_node == null) + { + if (!MoveNextListAndFirstNode()) + { + return false; + } + } + + return _node != null; + } + + private bool MoveNextListAndFirstNode() + { + if (_prio < KScheduler.PrioritiesCount) + { + _list = _listPerPrioPerCore[_prio][_core]; + + _node = _list.First; + + _prio = BitOperations.TrailingZeroCount(_prioMask); + + _prioMask &= ~(1L << _prio); + + return true; + } + else + { + _list = null; + _node = null; + return false; + } + } } } + public KThreadEnumerable ScheduledThreads(int core) + { + return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core); + } + + public KThreadEnumerable SuggestedThreads(int core) + { + return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core); + } + + public KThread ScheduledThreadsFirstOrDefault(int core) + { + return ScheduledThreadsElementAtOrDefault(core, 0); + } + + public KThread ScheduledThreadsElementAtOrDefault(int core, int index) + { + int currentIndex = 0; + foreach (var scheduledThread in ScheduledThreads(core)) + { + if (currentIndex == index) + { + return scheduledThread; + } + else + { + currentIndex++; + } + } + + return null; + } + + public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority) + { + foreach (var scheduledThread in ScheduledThreads(core)) + { + if (scheduledThread.DynamicPriority == dynamicPriority) + { + return scheduledThread; + } + } + + return null; + } + + public bool HasScheduledThreads(int core) + { + return ScheduledThreadsFirstOrDefault(core) != null; + } + public void TransferToCore(int prio, int dstCore, KThread thread) { int srcCore = thread.ActiveCore; diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 0c51b7b9a..b9de7d9c7 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -1,8 +1,6 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Process; using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; using System.Threading; @@ -17,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 }; + private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount]; + private readonly KernelContext _context; private readonly int _coreId; @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading for (int core = 0; core < CpuCoresCount; core++) { - KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault(); + KThread thread = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core); if (thread != null && thread.Owner != null && @@ -115,12 +115,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { // If the core is not idle (there's already a thread running on it), // then we don't need to attempt load balancing. - if (context.PriorityQueue.ScheduledThreads(core).Any()) + if (context.PriorityQueue.HasScheduledThreads(core)) { continue; } - int[] srcCoresHighestPrioThreads = new int[CpuCoresCount]; + Array.Fill(_srcCoresHighestPrioThreads, 0); int srcCoresHighestPrioThreadsCount = 0; @@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading break; } - srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore; + _srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore; } // Not yet selected candidate found. @@ -158,9 +158,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // (the first one that doesn't make the source core idle if moved). for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++) { - int srcCore = srcCoresHighestPrioThreads[index]; + int srcCore = _srcCoresHighestPrioThreads[index]; - KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1); + KThread src = context.PriorityQueue.ScheduledThreadsElementAtOrDefault(srcCore, 1); if (src != null) { @@ -422,9 +422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private static void RotateScheduledQueue(KernelContext context, int core, int prio) { - IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core); - - KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio); + KThread selectedThread = context.PriorityQueue.ScheduledThreadsWithDynamicPriorityFirstOrDefault(core, prio); KThread nextThread = null; // Yield priority queue. @@ -433,14 +431,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread); } - IEnumerable<KThread> SuitableCandidates() + static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate< KThread> predicate) { foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core)) { int suggestedCore = suggested.ActiveCore; if (suggestedCore >= 0) { - KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault(); + KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore); if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2)) { @@ -453,14 +451,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading nextThread == null || nextThread.LastScheduledTime >= suggested.LastScheduledTime) { - yield return suggested; + if (predicate(suggested)) + { + return suggested; + } } } + + return null; } // Select candidate threads that could run on this core. // Only take into account threads that are not yet selected. - KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio); + KThread dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority == prio); if (dst != null) { @@ -469,11 +472,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // If the priority of the currently selected thread is lower or same as the preemption priority, // then try to migrate a thread with lower priority. - KThread bestCandidate = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault(); + KThread bestCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core); if (bestCandidate != null && bestCandidate.DynamicPriority >= prio) { - dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority); + dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority < bestCandidate.DynamicPriority); if (dst != null) { @@ -534,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // Move current thread to the end of the queue. KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread); - IEnumerable<KThread> SuitableCandidates() + static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread nextThread, int lessThanOrEqualPriority) { foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core)) { @@ -554,12 +557,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (suggested.LastScheduledTime <= nextThread.LastScheduledTime || suggested.DynamicPriority < nextThread.DynamicPriority) { - yield return suggested; + if (suggested.DynamicPriority <= lessThanOrEqualPriority) + { + return suggested; + } } } + + return null; } - KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio); + KThread dst = FirstSuitableCandidateOrDefault(context, core, nextThread, prio); if (dst != null) { @@ -596,7 +604,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread); - if (!context.PriorityQueue.ScheduledThreads(core).Any()) + if (!context.PriorityQueue.HasScheduledThreads(core)) { KThread selectedThread = null; @@ -609,7 +617,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading continue; } - KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault(); + KThread firstCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore); if (firstCandidate == suggested) { diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs index 01b65f55e..973d5f6a4 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } else { - LinkedListNode<KThread>[] syncNodes = new LinkedListNode<KThread>[syncObjs.Length]; + LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length]; for (int index = 0; index < syncObjs.Length; index++) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs index 227cfdae1..49e342f27 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs @@ -1,4 +1,5 @@ -using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Account.Acc; using System.IO; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage @@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage public static byte[] MakeLaunchParams(UserProfile userProfile) { // Size needs to be at least 0x88 bytes otherwise application errors. - using (MemoryStream ms = new MemoryStream()) + using (MemoryStream ms = MemoryStreamManager.Shared.GetStream()) { BinaryWriter writer = new BinaryWriter(ms); diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs index 66a69a8be..fef82cbc4 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs @@ -5,6 +5,7 @@ using LibHac.FsSystem; using LibHac.Ncm; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Memory; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Kernel.Memory; @@ -160,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl static uint KXor(uint data) => data ^ FontKey; using (BinaryReader reader = new BinaryReader(bfttfStream)) - using (MemoryStream ttfStream = new MemoryStream()) + using (MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter output = new BinaryWriter(ttfStream)) { if (KXor(reader.ReadUInt32()) != BFTTFMagic) diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index d4382a643..619f54483 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -1,3 +1,5 @@ +using Ryujinx.Common; +using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Ipc; @@ -5,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; using System; +using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; @@ -37,14 +40,27 @@ namespace Ryujinx.HLE.HOS.Services private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>(); private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>(); + private readonly MemoryStream _requestDataStream; + private readonly BinaryReader _requestDataReader; + + private readonly MemoryStream _responseDataStream; + private readonly BinaryWriter _responseDataWriter; + public ManualResetEvent InitDone { get; } public string Name { get; } public Func<IpcService> SmObjectFactory { get; } public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null) { - InitDone = new ManualResetEvent(false); _context = context; + + _requestDataStream = MemoryStreamManager.Shared.GetStream(); + _requestDataReader = new BinaryReader(_requestDataStream); + + _responseDataStream = MemoryStreamManager.Shared.GetStream(); + _responseDataWriter = new BinaryWriter(_responseDataStream); + + InitDone = new ManualResetEvent(false); Name = name; SmObjectFactory = smObjectFactory; @@ -110,15 +126,15 @@ namespace Ryujinx.HLE.HOS.Services while (true) { - int[] portHandles = _portHandles.ToArray(); - int[] sessionHandles = _sessionHandles.ToArray(); - int[] handles = new int[portHandles.Length + sessionHandles.Length]; + int handleCount = _portHandles.Count + _sessionHandles.Count; - portHandles.CopyTo(handles, 0); - sessionHandles.CopyTo(handles, portHandles.Length); + int[] handles = ArrayPool<int>.Shared.Rent(handleCount); + + _portHandles.CopyTo(handles, 0); + _sessionHandles.CopyTo(handles, _portHandles.Count); // We still need a timeout here to allow the service to pick up and listen new sessions... - var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L); + var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L); thread.HandlePostSyscall(); @@ -129,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services replyTargetHandle = 0; - if (rc == Result.Success && signaledIndex >= portHandles.Length) + if (rc == Result.Success && signaledIndex >= _portHandles.Count) { // We got a IPC request, process it, pass to the appropriate service if needed. int signaledHandle = handles[signaledIndex]; @@ -156,6 +172,8 @@ namespace Ryujinx.HLE.HOS.Services _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10); _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48)); } + + ArrayPool<int>.Shared.Return(handles); } Dispose(); @@ -166,13 +184,9 @@ namespace Ryujinx.HLE.HOS.Services KProcess process = KernelStatic.GetCurrentProcess(); KThread thread = KernelStatic.GetCurrentThread(); ulong messagePtr = thread.TlsAddress; - ulong messageSize = 0x100; - byte[] reqData = new byte[messageSize]; + IpcMessage request = ReadRequest(process, messagePtr); - process.CpuMemory.Read(messagePtr, reqData); - - IpcMessage request = new IpcMessage(reqData, (long)messagePtr); IpcMessage response = new IpcMessage(); ulong tempAddr = recvListAddr; @@ -202,158 +216,157 @@ namespace Ryujinx.HLE.HOS.Services bool shouldReply = true; bool isTipcCommunication = false; - using (MemoryStream raw = new MemoryStream(request.RawData)) + _requestDataStream.SetLength(0); + _requestDataStream.Write(request.RawData); + _requestDataStream.Position = 0; + + if (request.Type == IpcMessageType.HipcRequest || + request.Type == IpcMessageType.HipcRequestWithContext) { - BinaryReader reqReader = new BinaryReader(raw); + response.Type = IpcMessageType.HipcResponse; - if (request.Type == IpcMessageType.HipcRequest || - request.Type == IpcMessageType.HipcRequestWithContext) - { - response.Type = IpcMessageType.HipcResponse; + _responseDataStream.SetLength(0); - using (MemoryStream resMs = new MemoryStream()) - { - BinaryWriter resWriter = new BinaryWriter(resMs); + ServiceCtx context = new ServiceCtx( + _context.Device, + process, + process.CpuMemory, + thread, + request, + response, + _requestDataReader, + _responseDataWriter); - ServiceCtx context = new ServiceCtx( - _context.Device, - process, - process.CpuMemory, - thread, - request, - response, - reqReader, - resWriter); + _sessions[serverSessionHandle].CallHipcMethod(context); - _sessions[serverSessionHandle].CallHipcMethod(context); - - response.RawData = resMs.ToArray(); - } - } - else if (request.Type == IpcMessageType.HipcControl || - request.Type == IpcMessageType.HipcControlWithContext) - { - uint magic = (uint)reqReader.ReadUInt64(); - uint cmdId = (uint)reqReader.ReadUInt64(); - - switch (cmdId) - { - case 0: - request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain()); - break; - - case 3: - request = FillResponse(response, 0, PointerBufferSize); - break; - - // TODO: Whats the difference between IpcDuplicateSession/Ex? - case 2: - case 4: - int unknown = reqReader.ReadInt32(); - - _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0); - - AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]); - - response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle); - - request = FillResponse(response, 0); - - break; - - default: throw new NotImplementedException(cmdId.ToString()); - } - } - else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession) - { - _context.Syscall.CloseHandle(serverSessionHandle); - _sessionHandles.Remove(serverSessionHandle); - IpcService service = _sessions[serverSessionHandle]; - if (service is IDisposable disposableObj) - { - disposableObj.Dispose(); - } - _sessions.Remove(serverSessionHandle); - shouldReply = false; - } - // If the type is past 0xF, we are using TIPC - else if (request.Type > IpcMessageType.TipcCloseSession) - { - isTipcCommunication = true; - - // Response type is always the same as request on TIPC. - response.Type = request.Type; - - using (MemoryStream resMs = new MemoryStream()) - { - BinaryWriter resWriter = new BinaryWriter(resMs); - - ServiceCtx context = new ServiceCtx( - _context.Device, - process, - process.CpuMemory, - thread, - request, - response, - reqReader, - resWriter); - - _sessions[serverSessionHandle].CallTipcMethod(context); - - response.RawData = resMs.ToArray(); - } - - process.CpuMemory.Write(messagePtr, response.GetBytesTipc()); - } - else - { - throw new NotImplementedException(request.Type.ToString()); - } - - if (!isTipcCommunication) - { - process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48))); - } - - return shouldReply; + response.RawData = _responseDataStream.ToArray(); } + else if (request.Type == IpcMessageType.HipcControl || + request.Type == IpcMessageType.HipcControlWithContext) + { + uint magic = (uint)_requestDataReader.ReadUInt64(); + uint cmdId = (uint)_requestDataReader.ReadUInt64(); + + switch (cmdId) + { + case 0: + FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain()); + break; + + case 3: + FillHipcResponse(response, 0, PointerBufferSize); + break; + + // TODO: Whats the difference between IpcDuplicateSession/Ex? + case 2: + case 4: + int unknown = _requestDataReader.ReadInt32(); + + _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0); + + AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]); + + response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle); + + FillHipcResponse(response, 0); + + break; + + default: throw new NotImplementedException(cmdId.ToString()); + } + } + else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession) + { + _context.Syscall.CloseHandle(serverSessionHandle); + _sessionHandles.Remove(serverSessionHandle); + IpcService service = _sessions[serverSessionHandle]; + (service as IDisposable)?.Dispose(); + _sessions.Remove(serverSessionHandle); + shouldReply = false; + } + // If the type is past 0xF, we are using TIPC + else if (request.Type > IpcMessageType.TipcCloseSession) + { + isTipcCommunication = true; + + // Response type is always the same as request on TIPC. + response.Type = request.Type; + + _responseDataStream.SetLength(0); + + ServiceCtx context = new ServiceCtx( + _context.Device, + process, + process.CpuMemory, + thread, + request, + response, + _requestDataReader, + _responseDataWriter); + + _sessions[serverSessionHandle].CallTipcMethod(context); + + response.RawData = _responseDataStream.ToArray(); + + using var responseStream = response.GetStreamTipc(); + process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence()); + } + else + { + throw new NotImplementedException(request.Type.ToString()); + } + + if (!isTipcCommunication) + { + using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)); + process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence()); + } + + return shouldReply; } - private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values) + private static IpcMessage ReadRequest(KProcess process, ulong messagePtr) { - using (MemoryStream ms = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(ms); + const int messageSize = 0x100; - foreach (int value in values) - { - writer.Write(value); - } + byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize); - return FillResponse(response, result, ms.ToArray()); - } + Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize); + reqDataSpan.Clear(); + + process.CpuMemory.Read(messagePtr, reqDataSpan); + + IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr); + + ArrayPool<byte>.Shared.Return(reqData); + + return request; } - private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null) + private void FillHipcResponse(IpcMessage response, long result) + { + FillHipcResponse(response, result, ReadOnlySpan<byte>.Empty); + } + + private void FillHipcResponse(IpcMessage response, long result, int value) + { + Span<byte> span = stackalloc byte[sizeof(int)]; + BinaryPrimitives.WriteInt32LittleEndian(span, value); + FillHipcResponse(response, result, span); + } + + private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data) { response.Type = IpcMessageType.HipcResponse; - using (MemoryStream ms = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(ms); + _responseDataStream.SetLength(0); - writer.Write(IpcMagic.Sfco); - writer.Write(result); + _responseDataStream.Write(IpcMagic.Sfco); + _responseDataStream.Write(result); - if (data != null) - { - writer.Write(data); - } + _responseDataStream.Write(data); - response.RawData = ms.ToArray(); - } - - return response; + response.RawData = _responseDataStream.ToArray(); } protected virtual void Dispose(bool disposing) @@ -372,6 +385,11 @@ namespace Ryujinx.HLE.HOS.Services _sessions.Clear(); + _requestDataReader.Dispose(); + _requestDataStream.Dispose(); + _responseDataWriter.Dispose(); + _responseDataStream.Dispose(); + InitDone.Dispose(); } } diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs index a64d451c1..1810b1ad7 100644 --- a/Ryujinx.HLE/Utilities/StringUtils.cs +++ b/Ryujinx.HLE/Utilities/StringUtils.cs @@ -1,4 +1,6 @@ using LibHac.Common; +using Microsoft.IO; +using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS; using System; using System.Globalization; @@ -77,7 +79,7 @@ namespace Ryujinx.HLE.Utilities ulong position = context.Request.PtrBuff[index].Position; ulong size = context.Request.PtrBuff[index].Size; - using (MemoryStream ms = new MemoryStream()) + using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream()) { while (size-- > 0) { @@ -91,7 +93,7 @@ namespace Ryujinx.HLE.Utilities ms.WriteByte(value); } - return Encoding.UTF8.GetString(ms.ToArray()); + return Encoding.UTF8.GetString(ms.GetReadOnlySequence()); } } @@ -110,7 +112,7 @@ namespace Ryujinx.HLE.Utilities ulong position = context.Request.SendBuff[index].Position; ulong size = context.Request.SendBuff[index].Size; - using (MemoryStream ms = new MemoryStream()) + using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream()) { while (size-- > 0) { @@ -124,7 +126,7 @@ namespace Ryujinx.HLE.Utilities ms.WriteByte(value); } - return Encoding.UTF8.GetString(ms.ToArray()); + return Encoding.UTF8.GetString(ms.GetReadOnlySequence()); } } diff --git a/Ryujinx.Input/Motion/CemuHook/Client.cs b/Ryujinx.Input/Motion/CemuHook/Client.cs index f5f7b8643..4498b8ca6 100644 --- a/Ryujinx.Input/Motion/CemuHook/Client.cs +++ b/Ryujinx.Input/Motion/CemuHook/Client.cs @@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.Input.HLE; using Ryujinx.Input.Motion.CemuHook.Protocol; using System; @@ -381,7 +382,7 @@ namespace Ryujinx.Input.Motion.CemuHook Header header = GenerateHeader(clientId); - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { writer.WriteStruct(header); @@ -421,7 +422,7 @@ namespace Ryujinx.Input.Motion.CemuHook Header header = GenerateHeader(clientId); - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { writer.WriteStruct(header); diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs index e1851d48b..edbfc8855 100644 --- a/Ryujinx.Memory/IVirtualMemoryManager.cs +++ b/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Memory.Range; using System; +using System.Buffers; using System.Collections.Generic; namespace Ryujinx.Memory @@ -77,6 +78,21 @@ namespace Ryujinx.Memory /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> void Write(ulong va, ReadOnlySpan<byte> data); + /// <summary> + /// Writes data to CPU mapped memory, with write tracking. + /// </summary> + /// <param name="va">Virtual address to write the data into</param> + /// <param name="data">Data to be written</param> + /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> + public void Write(ulong va, ReadOnlySequence<byte> data) + { + foreach (ReadOnlyMemory<byte> segment in data) + { + Write(va, segment.Span); + va += (ulong)segment.Length; + } + } + /// <summary> /// Writes data to the application process, returning false if the data was not changed. /// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date. diff --git a/Ryujinx/Ui/Windows/AvatarWindow.cs b/Ryujinx/Ui/Windows/AvatarWindow.cs index fc928bde2..0cda890f5 100644 --- a/Ryujinx/Ui/Windows/AvatarWindow.cs +++ b/Ryujinx/Ui/Windows/AvatarWindow.cs @@ -6,6 +6,7 @@ using LibHac.FsSystem; using LibHac.Ncm; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Memory; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.Common.Configuration; using SixLabors.ImageSharp; @@ -136,8 +137,8 @@ namespace Ryujinx.Ui.Windows romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); - using (MemoryStream stream = new MemoryStream()) - using (MemoryStream streamPng = new MemoryStream()) + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) + using (MemoryStream streamPng = MemoryStreamManager.Shared.GetStream()) { file.Get.AsStream().CopyTo(stream); @@ -169,7 +170,7 @@ namespace Ryujinx.Ui.Windows private byte[] ProcessImage(byte[] data) { - using (MemoryStream streamJpg = new MemoryStream()) + using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream()) { Image avatarImage = Image.Load(data, new PngDecoder()); diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs index 495c1d1fa..a08b5dd17 100644 --- a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs +++ b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs @@ -1,4 +1,5 @@ using Gtk; +using Ryujinx.Common.Memory; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.Ui.Common.Configuration; @@ -181,7 +182,7 @@ namespace Ryujinx.Ui.Windows { image.Mutate(x => x.Resize(256, 256)); - using (MemoryStream streamJpg = new MemoryStream()) + using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream()) { image.SaveAsJpeg(streamJpg); From b2623dc27d14e9c672429a3819d20958954cb170 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sat, 18 Mar 2023 23:39:05 +0000 Subject: [PATCH 405/737] OpenGL: Fix inverted conditional for counter flush from #4471 (#4560) Fixes OpenGL. --- Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs index 99ea6a5c7..9d43f6c3f 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs @@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries { result = Marshal.ReadInt64(_bufferMap); - return WaitingForValue(result); + return !WaitingForValue(result); } public long AwaitResult(AutoResetEvent wakeSignal = null) From c05c688ee840dc0f8981858d7e723667efdcb8b5 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:30:04 +0100 Subject: [PATCH 406/737] Avoid copying more handles than we have space for (#4564) * Avoid copying more handles than we have space for * Use locks instead * Reduce nesting by combining the lock statements * Add locks for other uses of _sessionHandles and _portHandles * Use one object to lock instead of locking twice * Release the lock as soon as possible --- Ryujinx.HLE/HOS/Services/ServerBase.cs | 37 +++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 619f54483..8b82b771f 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Services 0x01007FFF }; + private readonly object _handleLock = new(); + private readonly KernelContext _context; private KProcess _selfProcess; @@ -77,7 +79,10 @@ namespace Ryujinx.HLE.HOS.Services private void AddPort(int serverPortHandle, Func<IpcService> objectFactory) { - _portHandles.Add(serverPortHandle); + lock (_handleLock) + { + _portHandles.Add(serverPortHandle); + } _ports.Add(serverPortHandle, objectFactory); } @@ -92,7 +97,10 @@ namespace Ryujinx.HLE.HOS.Services public void AddSessionObj(int serverSessionHandle, IpcService obj) { - _sessionHandles.Add(serverSessionHandle); + lock (_handleLock) + { + _sessionHandles.Add(serverSessionHandle); + } _sessions.Add(serverSessionHandle, obj); } @@ -126,12 +134,20 @@ namespace Ryujinx.HLE.HOS.Services while (true) { - int handleCount = _portHandles.Count + _sessionHandles.Count; + int handleCount; + int portHandleCount; + int[] handles; - int[] handles = ArrayPool<int>.Shared.Rent(handleCount); - - _portHandles.CopyTo(handles, 0); - _sessionHandles.CopyTo(handles, _portHandles.Count); + lock (_handleLock) + { + portHandleCount = _portHandles.Count; + handleCount = portHandleCount + _sessionHandles.Count; + + handles = ArrayPool<int>.Shared.Rent(handleCount); + + _portHandles.CopyTo(handles, 0); + _sessionHandles.CopyTo(handles, portHandleCount); + } // We still need a timeout here to allow the service to pick up and listen new sessions... var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L); @@ -145,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services replyTargetHandle = 0; - if (rc == Result.Success && signaledIndex >= _portHandles.Count) + if (rc == Result.Success && signaledIndex >= portHandleCount) { // We got a IPC request, process it, pass to the appropriate service if needed. int signaledHandle = handles[signaledIndex]; @@ -278,7 +294,10 @@ namespace Ryujinx.HLE.HOS.Services else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession) { _context.Syscall.CloseHandle(serverSessionHandle); - _sessionHandles.Remove(serverSessionHandle); + lock (_handleLock) + { + _sessionHandles.Remove(serverSessionHandle); + } IpcService service = _sessions[serverSessionHandle]; (service as IDisposable)?.Dispose(); _sessions.Remove(serverSessionHandle); From 67b4e63cff0d6ce9629c3032f2b0d6414cee1220 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 19 Mar 2023 13:31:35 -0300 Subject: [PATCH 407/737] Remove MultiRange Min/MaxAddress and rename GetSlice to Slice (#4566) * Delete MinAddress and MaxAddress from MultiRange * Rename MultiRange.GetSlice to MultiRange.Slice --- Ryujinx.Graphics.Gpu/Image/Texture.cs | 4 +-- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 10 +++--- Ryujinx.Memory/Range/MultiRange.cs | 38 +--------------------- 3 files changed, 8 insertions(+), 44 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index b784a5455..f80f20ed6 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1475,8 +1475,8 @@ namespace Ryujinx.Graphics.Gpu.Image MultiRange otherRange = texture.Range; - IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.GetSlice((ulong)region.Offset, (ulong)region.Size)); - IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.GetSlice((ulong)region.Offset, (ulong)region.Size)); + IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.Slice((ulong)region.Offset, (ulong)region.Size)); + IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.Slice((ulong)region.Offset, (ulong)region.Size)); foreach (MultiRange region in regions) { diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index b59a9d086..234e7e8c7 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -397,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu.Image int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size); int size = endOffset - spanBase; - dataSpan = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)spanBase, (ulong)size)); + dataSpan = _physicalMemory.GetSpan(Storage.Range.Slice((ulong)spanBase, (ulong)size)); } // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views. @@ -473,7 +473,7 @@ namespace Ryujinx.Graphics.Gpu.Image int endOffset = Math.Min(offset + _sliceSizes[level], (int)Storage.Size); int size = endOffset - offset; - using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.GetSlice((ulong)offset, (ulong)size), tracked); + using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked); Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture); } @@ -1419,13 +1419,13 @@ namespace Ryujinx.Graphics.Gpu.Image for (int i = 0; i < _allOffsets.Length; i++) { (int layer, int level) = GetLayerLevelForView(i); - MultiRange handleRange = Storage.Range.GetSlice((ulong)_allOffsets[i], 1); + MultiRange handleRange = Storage.Range.Slice((ulong)_allOffsets[i], 1); ulong handleBase = handleRange.GetSubRange(0).Address; for (int j = 0; j < other._handles.Length; j++) { (int otherLayer, int otherLevel) = other.GetLayerLevelForView(j); - MultiRange otherHandleRange = other.Storage.Range.GetSlice((ulong)other._allOffsets[j], 1); + MultiRange otherHandleRange = other.Storage.Range.Slice((ulong)other._allOffsets[j], 1); ulong otherHandleBase = otherHandleRange.GetSubRange(0).Address; if (handleBase == otherHandleBase) @@ -1502,7 +1502,7 @@ namespace Ryujinx.Graphics.Gpu.Image // Handles list is not modified by another thread, only replaced, so this is thread safe. // Remove modified flags from all overlapping handles, so that the textures don't flush to unmapped/remapped GPU memory. - MultiRange subRange = Storage.Range.GetSlice((ulong)handle.Offset, (ulong)handle.Size); + MultiRange subRange = Storage.Range.Slice((ulong)handle.Offset, (ulong)handle.Size); if (range.OverlapsWith(subRange)) { diff --git a/Ryujinx.Memory/Range/MultiRange.cs b/Ryujinx.Memory/Range/MultiRange.cs index dc2aefe40..9dbd76eca 100644 --- a/Ryujinx.Memory/Range/MultiRange.cs +++ b/Ryujinx.Memory/Range/MultiRange.cs @@ -20,16 +20,6 @@ namespace Ryujinx.Memory.Range /// </summary> public int Count => HasSingleRange ? 1 : _ranges.Length; - /// <summary> - /// Minimum start address of all sub-ranges. - /// </summary> - public ulong MinAddress { get; } - - /// <summary> - /// Maximum end address of all sub-ranges. - /// </summary> - public ulong MaxAddress { get; } - /// <summary> /// Creates a new multi-range with a single physical region. /// </summary> @@ -39,8 +29,6 @@ namespace Ryujinx.Memory.Range { _singleRange = new MemoryRange(address, size); _ranges = null; - MinAddress = address; - MaxAddress = address + size; } /// <summary> @@ -52,30 +40,6 @@ namespace Ryujinx.Memory.Range { _singleRange = MemoryRange.Empty; _ranges = ranges ?? throw new ArgumentNullException(nameof(ranges)); - - if (ranges.Length != 0) - { - MinAddress = ulong.MaxValue; - MaxAddress = 0UL; - - foreach (MemoryRange range in ranges) - { - if (MinAddress > range.Address) - { - MinAddress = range.Address; - } - - if (MaxAddress < range.EndAddress) - { - MaxAddress = range.EndAddress; - } - } - } - else - { - MinAddress = 0UL; - MaxAddress = 0UL; - } } /// <summary> @@ -84,7 +48,7 @@ namespace Ryujinx.Memory.Range /// <param name="offset">Offset of the slice into the multi-range in bytes</param> /// <param name="size">Size of the slice in bytes</param> /// <returns>A new multi-range representing the given slice of this one</returns> - public MultiRange GetSlice(ulong offset, ulong size) + public MultiRange Slice(ulong offset, ulong size) { if (HasSingleRange) { From 9f1cf6458c78a42256b1f390f5b3b9159b00a7cb Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sun, 19 Mar 2023 20:56:48 +0000 Subject: [PATCH 408/737] Vulkan: Migrate buffers between memory types to improve GPU performance (#4540) * Initial implementation of migration between memory heaps - Missing OOM handling - Missing `_map` data safety when remapping - Copy may not have completed yet (needs some kind of fence) - Map may be unmapped before it is done being used. (needs scoped access) - SSBO accesses are all "writes" - maybe pass info in another way. - Missing keeping map type when resizing buffers (should this be done?) * Ensure migrated data is in place before flushing. * Fix issue where old waitable would be signalled. - There is a real issue where existing Auto<> references need to be replaced. * Swap bound Auto<> instances when swapping buffer backing * Fix conversion buffers * Don't try move buffers if the host has shared memory. * Make GPU methods return PinnedSpan with scope * Storage Hint * Fix stupidity * Fix rebase * Tweak rules Attempt to sidestep BOTW slowdown * Remove line * Migrate only when command buffers flush * Change backing swap log to debug * Address some feedback * Disallow backing swap when the flush lock is held by the current thread * Make PinnedSpan from ReadOnlySpan explicitly unsafe * Fix some small issues - Index buffer swap fixed - Allocate DeviceLocal buffers using a separate block list to images. * Remove alternative flags * Address feedback --- Ryujinx.Graphics.GAL/IRenderer.cs | 9 +- Ryujinx.Graphics.GAL/ITexture.cs | 4 +- .../Commands/Buffer/BufferGetDataCommand.cs | 4 +- .../Commands/Renderer/CreateBufferCommand.cs | 13 +- .../Commands/Texture/TextureGetDataCommand.cs | 4 +- .../Texture/TextureGetDataSliceCommand.cs | 4 +- .../Multithreading/Model/PinnedSpan.cs | 23 -- .../Resources/ThreadedTexture.cs | 8 +- .../Multithreading/ThreadedRenderer.cs | 8 +- Ryujinx.Graphics.GAL/PinnedSpan.cs | 53 +++ Ryujinx.Graphics.Gpu/Image/Texture.cs | 20 +- Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 6 +- Ryujinx.Graphics.OpenGL/Buffer.cs | 9 +- .../Image/TextureBuffer.cs | 4 +- Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 10 +- Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 4 +- .../BufferAllocationType.cs | 12 + Ryujinx.Graphics.Vulkan/BufferHolder.cs | 302 +++++++++++++++++- Ryujinx.Graphics.Vulkan/BufferManager.cs | 124 +++++-- Ryujinx.Graphics.Vulkan/BufferState.cs | 15 +- .../DescriptorSetUpdater.cs | 21 +- .../Effects/FsrScalingFilter.cs | 4 +- .../Effects/FxaaPostProcessingEffect.cs | 2 +- .../Effects/SmaaPostProcessingEffect.cs | 2 +- Ryujinx.Graphics.Vulkan/HelperShader.cs | 18 +- Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs | 2 +- Ryujinx.Graphics.Vulkan/IndexBufferState.cs | 11 + Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 49 +-- .../MemoryAllocatorBlockList.cs | 4 +- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 19 ++ Ryujinx.Graphics.Vulkan/PipelineFull.cs | 19 ++ Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 8 +- Ryujinx.Graphics.Vulkan/TextureView.cs | 12 +- Ryujinx.Graphics.Vulkan/VertexBufferState.cs | 11 + Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 9 +- 35 files changed, 660 insertions(+), 167 deletions(-) delete mode 100644 Ryujinx.Graphics.GAL/Multithreading/Model/PinnedSpan.cs create mode 100644 Ryujinx.Graphics.GAL/PinnedSpan.cs create mode 100644 Ryujinx.Graphics.Vulkan/BufferAllocationType.cs diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs index 1f2af559f..2af7b5db7 100644 --- a/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/Ryujinx.Graphics.GAL/IRenderer.cs @@ -15,7 +15,12 @@ namespace Ryujinx.Graphics.GAL void BackgroundContextAction(Action action, bool alwaysBackground = false); - BufferHandle CreateBuffer(int size); + BufferHandle CreateBuffer(int size, BufferHandle storageHint); + + BufferHandle CreateBuffer(int size) + { + return CreateBuffer(size, BufferHandle.Null); + } IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info); @@ -26,7 +31,7 @@ namespace Ryujinx.Graphics.GAL void DeleteBuffer(BufferHandle buffer); - ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size); + PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size); Capabilities GetCapabilities(); ulong GetCurrentSync(); diff --git a/Ryujinx.Graphics.GAL/ITexture.cs b/Ryujinx.Graphics.GAL/ITexture.cs index 4dc933036..792c863cb 100644 --- a/Ryujinx.Graphics.GAL/ITexture.cs +++ b/Ryujinx.Graphics.GAL/ITexture.cs @@ -15,8 +15,8 @@ namespace Ryujinx.Graphics.GAL ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel); - ReadOnlySpan<byte> GetData(); - ReadOnlySpan<byte> GetData(int layer, int level); + PinnedSpan<byte> GetData(); + PinnedSpan<byte> GetData(int layer, int level); void SetData(SpanOrArray<byte> data); void SetData(SpanOrArray<byte> data, int layer, int level); diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs index d3a255e7b..031c6153e 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs @@ -21,9 +21,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer public static void Run(ref BufferGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) { - ReadOnlySpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size); + PinnedSpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size); - command._result.Get(threaded).Result = new PinnedSpan<byte>(result); + command._result.Get(threaded).Result = result; } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs index 4f01dea27..b36d8bbed 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs @@ -5,16 +5,25 @@ public CommandType CommandType => CommandType.CreateBuffer; private BufferHandle _threadedHandle; private int _size; + private BufferHandle _storageHint; - public void Set(BufferHandle threadedHandle, int size) + public void Set(BufferHandle threadedHandle, int size, BufferHandle storageHint) { _threadedHandle = threadedHandle; _size = size; + _storageHint = storageHint; } public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) { - threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size)); + BufferHandle hint = BufferHandle.Null; + + if (command._storageHint != BufferHandle.Null) + { + hint = threaded.Buffers.MapBuffer(command._storageHint); + } + + threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, hint)); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs index 1f519ccd8..91320d455 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs @@ -18,9 +18,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture public static void Run(ref TextureGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) { - ReadOnlySpan<byte> result = command._texture.Get(threaded).Base.GetData(); + PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData(); - command._result.Get(threaded).Result = new PinnedSpan<byte>(result); + command._result.Get(threaded).Result = result; } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs index 5ac05971a..ec06cc4dd 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs @@ -22,9 +22,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture public static void Run(ref TextureGetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer) { - ReadOnlySpan<byte> result = command._texture.Get(threaded).Base.GetData(command._layer, command._level); + PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData(command._layer, command._level); - command._result.Get(threaded).Result = new PinnedSpan<byte>(result); + command._result.Get(threaded).Result = result; } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/PinnedSpan.cs b/Ryujinx.Graphics.GAL/Multithreading/Model/PinnedSpan.cs deleted file mode 100644 index 16e148c25..000000000 --- a/Ryujinx.Graphics.GAL/Multithreading/Model/PinnedSpan.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.GAL.Multithreading.Model -{ - unsafe struct PinnedSpan<T> where T : unmanaged - { - private void* _ptr; - private int _size; - - public PinnedSpan(ReadOnlySpan<T> span) - { - _ptr = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); - _size = span.Length; - } - - public ReadOnlySpan<T> Get() - { - return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>()); - } - } -} diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index 1267ab797..ee1cfa29b 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return newTex; } - public ReadOnlySpan<byte> GetData() + public PinnedSpan<byte> GetData() { if (_renderer.IsGpuThread()) { @@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources _renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box)); _renderer.InvokeCommand(); - return box.Result.Get(); + return box.Result; } else { @@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } } - public ReadOnlySpan<byte> GetData(int layer, int level) + public PinnedSpan<byte> GetData(int layer, int level) { if (_renderer.IsGpuThread()) { @@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources _renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level); _renderer.InvokeCommand(); - return box.Result.Get(); + return box.Result; } else { diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 74326f1d2..2148f43f9 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -265,10 +265,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading } } - public BufferHandle CreateBuffer(int size) + public BufferHandle CreateBuffer(int size, BufferHandle storageHint) { BufferHandle handle = Buffers.CreateBufferHandle(); - New<CreateBufferCommand>().Set(handle, size); + New<CreateBufferCommand>().Set(handle, size, storageHint); QueueCommand(); return handle; @@ -329,7 +329,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading QueueCommand(); } - public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) + public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) { if (IsGpuThread()) { @@ -337,7 +337,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box)); InvokeCommand(); - return box.Result.Get(); + return box.Result; } else { diff --git a/Ryujinx.Graphics.GAL/PinnedSpan.cs b/Ryujinx.Graphics.GAL/PinnedSpan.cs new file mode 100644 index 000000000..275b3b866 --- /dev/null +++ b/Ryujinx.Graphics.GAL/PinnedSpan.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL +{ + public unsafe struct PinnedSpan<T> : IDisposable where T : unmanaged + { + private void* _ptr; + private int _size; + private Action _disposeAction; + + /// <summary> + /// Creates a new PinnedSpan from an existing ReadOnlySpan. The span *must* be pinned in memory. + /// The data must be guaranteed to live until disposeAction is called. + /// </summary> + /// <param name="span">Existing span</param> + /// <param name="disposeAction">Action to call on dispose</param> + /// <remarks> + /// If a dispose action is not provided, it is safe to assume the resource will be available until the next call. + /// </remarks> + public static PinnedSpan<T> UnsafeFromSpan(ReadOnlySpan<T> span, Action disposeAction = null) + { + return new PinnedSpan<T>(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)), span.Length, disposeAction); + } + + /// <summary> + /// Creates a new PinnedSpan from an existing unsafe region. The data must be guaranteed to live until disposeAction is called. + /// </summary> + /// <param name="ptr">Pointer to the region</param> + /// <param name="size">The total items of T the region contains</param> + /// <param name="disposeAction">Action to call on dispose</param> + /// <remarks> + /// If a dispose action is not provided, it is safe to assume the resource will be available until the next call. + /// </remarks> + public PinnedSpan(void* ptr, int size, Action disposeAction = null) + { + _ptr = ptr; + _size = size; + _disposeAction = disposeAction; + } + + public ReadOnlySpan<T> Get() + { + return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>()); + } + + public void Dispose() + { + _disposeAction?.Invoke(); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index f80f20ed6..84808a84d 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1022,13 +1022,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// This method should be used to retrieve data that was modified by the host GPU. /// This is not cheap, avoid doing that unless strictly needed. /// </remarks> - /// <param name="output">An output span to place the texture data into. If empty, one is generated</param> + /// <param name="output">An output span to place the texture data into</param> /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param> /// <param name="texture">The specific host texture to flush. Defaults to this texture</param> - /// <returns>The span containing the texture data</returns> - private ReadOnlySpan<byte> GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null) + private void GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null) { - ReadOnlySpan<byte> data; + PinnedSpan<byte> data; if (texture != null) { @@ -1054,9 +1053,9 @@ namespace Ryujinx.Graphics.Gpu.Image } } - data = ConvertFromHostCompatibleFormat(output, data); + ConvertFromHostCompatibleFormat(output, data.Get()); - return data; + data.Dispose(); } /// <summary> @@ -1071,10 +1070,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="level">The level of the texture to flush</param> /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param> /// <param name="texture">The specific host texture to flush. Defaults to this texture</param> - /// <returns>The span containing the texture data</returns> - public ReadOnlySpan<byte> GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null) + public void GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null) { - ReadOnlySpan<byte> data; + PinnedSpan<byte> data; if (texture != null) { @@ -1100,9 +1098,9 @@ namespace Ryujinx.Graphics.Gpu.Image } } - data = ConvertFromHostCompatibleFormat(output, data, level, true); + ConvertFromHostCompatibleFormat(output, data.Get(), level, true); - return data; + data.Dispose(); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 3778cd824..f267dfda7 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Memory Address = address; Size = size; - Handle = context.Renderer.CreateBuffer((int)size); + Handle = context.Renderer.CreateBuffer((int)size, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null); _useGranular = size > GranularBufferThreshold; @@ -415,10 +415,10 @@ namespace Ryujinx.Graphics.Gpu.Memory { int offset = (int)(address - Address); - ReadOnlySpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size); + using PinnedSpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size); // TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers. - _physicalMemory.WriteUntracked(address, data); + _physicalMemory.WriteUntracked(address, data.Get()); } /// <summary> diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/Ryujinx.Graphics.OpenGL/Buffer.cs index 68c82f955..af7d191a6 100644 --- a/Ryujinx.Graphics.OpenGL/Buffer.cs +++ b/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -55,11 +55,14 @@ namespace Ryujinx.Graphics.OpenGL (IntPtr)size); } - public static unsafe ReadOnlySpan<byte> GetData(OpenGLRenderer renderer, BufferHandle buffer, int offset, int size) + public static unsafe PinnedSpan<byte> GetData(OpenGLRenderer renderer, BufferHandle buffer, int offset, int size) { + // Data in the persistent buffer and host array is guaranteed to be available + // until the next time the host thread requests data. + if (HwCapabilities.UsePersistentBufferForFlush) { - return renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size); + return PinnedSpan<byte>.UnsafeFromSpan(renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size)); } else { @@ -69,7 +72,7 @@ namespace Ryujinx.Graphics.OpenGL GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, target); - return new ReadOnlySpan<byte>(target.ToPointer(), size); + return new PinnedSpan<byte>(target.ToPointer(), size); } } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index 76d0149b0..1e9e4d6b2 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -39,12 +39,12 @@ namespace Ryujinx.Graphics.OpenGL.Image throw new NotSupportedException(); } - public ReadOnlySpan<byte> GetData() + public PinnedSpan<byte> GetData() { return Buffer.GetData(_renderer, _buffer, _bufferOffset, _bufferSize); } - public ReadOnlySpan<byte> GetData(int layer, int level) + public PinnedSpan<byte> GetData(int layer, int level) { return GetData(); } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 44df441f7..ddc5f9a37 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.OpenGL.Image _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); } - public unsafe ReadOnlySpan<byte> GetData() + public unsafe PinnedSpan<byte> GetData() { int size = 0; int levels = Info.GetLevelsClamped(); @@ -196,16 +196,16 @@ namespace Ryujinx.Graphics.OpenGL.Image data = FormatConverter.ConvertD24S8ToS8D24(data); } - return data; + return PinnedSpan<byte>.UnsafeFromSpan(data); } - public unsafe ReadOnlySpan<byte> GetData(int layer, int level) + public unsafe PinnedSpan<byte> GetData(int layer, int level) { int size = Info.GetMipSize(level); if (HwCapabilities.UsePersistentBufferForFlush) { - return _renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level); + return PinnedSpan<byte>.UnsafeFromSpan(_renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level)); } else { @@ -213,7 +213,7 @@ namespace Ryujinx.Graphics.OpenGL.Image int offset = WriteTo2D(target, layer, level); - return new ReadOnlySpan<byte>(target.ToPointer(), size).Slice(offset); + return new PinnedSpan<byte>((byte*)target.ToPointer() + offset, size); } } diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index c79700fc2..91e52178f 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.OpenGL ResourcePool = new ResourcePool(); } - public BufferHandle CreateBuffer(int size) + public BufferHandle CreateBuffer(int size, BufferHandle storageHint) { BufferCount++; @@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.OpenGL return new HardwareInfo(GpuVendor, GpuRenderer); } - public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) + public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) { return Buffer.GetData(this, buffer, offset, size); } diff --git a/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs b/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs new file mode 100644 index 000000000..814890411 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Vulkan +{ + internal enum BufferAllocationType + { + Auto = 0, + + HostMappedNoCache, + HostMapped, + DeviceLocal, + DeviceLocalMapped + } +} diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 055d6a59a..21b81bdd3 100644 --- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -1,7 +1,10 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Threading; using VkBuffer = Silk.NET.Vulkan.Buffer; using VkFormat = Silk.NET.Vulkan.Format; @@ -11,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan { private const int MaxUpdateBufferSize = 0x10000; + private const int SetCountThreshold = 100; + private const int WriteCountThreshold = 50; + private const int FlushCountThreshold = 5; + + public const int DeviceLocalSizeThreshold = 256 * 1024; // 256kb + public const AccessFlags DefaultAccessFlags = AccessFlags.IndirectCommandReadBit | AccessFlags.ShaderReadBit | @@ -21,10 +30,10 @@ namespace Ryujinx.Graphics.Vulkan private readonly VulkanRenderer _gd; private readonly Device _device; - private readonly MemoryAllocation _allocation; - private readonly Auto<DisposableBuffer> _buffer; - private readonly Auto<MemoryAllocation> _allocationAuto; - private readonly ulong _bufferHandle; + private MemoryAllocation _allocation; + private Auto<DisposableBuffer> _buffer; + private Auto<MemoryAllocation> _allocationAuto; + private ulong _bufferHandle; private CacheByRange<BufferHolder> _cachedConvertedBuffers; @@ -32,11 +41,28 @@ namespace Ryujinx.Graphics.Vulkan private IntPtr _map; - private readonly MultiFenceHolder _waitable; + private MultiFenceHolder _waitable; private bool _lastAccessIsWrite; - public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size) + private BufferAllocationType _baseType; + private BufferAllocationType _currentType; + private bool _swapQueued; + + public BufferAllocationType DesiredType { get; private set; } + + private int _setCount; + private int _writeCount; + private int _flushCount; + private int _flushTemp; + + private ReaderWriterLock _flushLock; + private FenceHolder _flushFence; + private int _flushWaiting; + + private List<Action> _swapActions; + + public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType) { _gd = gd; _device = device; @@ -47,9 +73,153 @@ namespace Ryujinx.Graphics.Vulkan _bufferHandle = buffer.Handle; Size = size; _map = allocation.HostPointer; + + _baseType = type; + _currentType = currentType; + DesiredType = currentType; + + _flushLock = new ReaderWriterLock(); } - public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size) + public bool TryBackingSwap(ref CommandBufferScoped? cbs) + { + if (_swapQueued && DesiredType != _currentType) + { + // Only swap if the buffer is not used in any queued command buffer. + bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool); + + if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld) + { + var currentAllocation = _allocationAuto; + var currentBuffer = _buffer; + IntPtr currentMap = _map; + + (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, _currentType); + + if (buffer.Handle != 0) + { + _flushLock.AcquireWriterLock(Timeout.Infinite); + + ClearFlushFence(); + + _waitable = new MultiFenceHolder(Size); + + _allocation = allocation; + _allocationAuto = new Auto<MemoryAllocation>(allocation); + _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(_gd.Api, _device, buffer), _waitable, _allocationAuto); + _bufferHandle = buffer.Handle; + _map = allocation.HostPointer; + + if (_map != IntPtr.Zero && currentMap != IntPtr.Zero) + { + // Copy data directly. Readbacks don't have to wait if this is done. + + unsafe + { + new Span<byte>((void*)currentMap, Size).CopyTo(new Span<byte>((void*)_map, Size)); + } + } + else + { + if (cbs == null) + { + cbs = _gd.CommandBufferPool.Rent(); + } + + CommandBufferScoped cbsV = cbs.Value; + + Copy(_gd, cbsV, currentBuffer, _buffer, 0, 0, Size); + + // Need to wait for the data to reach the new buffer before data can be flushed. + + _flushFence = _gd.CommandBufferPool.GetFence(cbsV.CommandBufferIndex); + _flushFence.Get(); + } + + Logger.Debug?.PrintMsg(LogClass.Gpu, $"Converted {Size} buffer {_currentType} to {resultType}"); + + _currentType = resultType; + + if (_swapActions != null) + { + foreach (var action in _swapActions) + { + action(); + } + + _swapActions.Clear(); + } + + currentBuffer.Dispose(); + currentAllocation.Dispose(); + + _gd.PipelineInternal.SwapBuffer(currentBuffer, _buffer); + + _flushLock.ReleaseWriterLock(); + } + + _swapQueued = false; + + return true; + } + else + { + return false; + } + } + else + { + _swapQueued = false; + + return true; + } + } + + private void ConsiderBackingSwap() + { + if (_baseType == BufferAllocationType.Auto) + { + if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold) + { + if (_flushCount > 0 || _flushTemp-- > 0) + { + // Buffers that flush should ideally be mapped in host address space for easy copies. + // If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages). + // If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached. + DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped; + + // It's harder for a buffer that is flushed to revert to another type of mapping. + if (_flushCount > 0) + { + _flushTemp = 1000; + } + } + else if (_writeCount >= WriteCountThreshold) + { + // Buffers that are written often should ideally be in the device local heap. (Storage buffers) + DesiredType = BufferAllocationType.DeviceLocal; + } + else if (_setCount > SetCountThreshold) + { + // Buffers that have their data set often should ideally be host mapped. (Constant buffers) + DesiredType = BufferAllocationType.HostMapped; + } + + _flushCount = 0; + _writeCount = 0; + _setCount = 0; + } + + if (!_swapQueued && DesiredType != _currentType) + { + _swapQueued = true; + + _gd.PipelineInternal.AddBackingSwap(this); + } + } + } + + public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size, Action invalidateView) { var bufferViewCreateInfo = new BufferViewCreateInfo() { @@ -62,9 +232,19 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError(); + (_swapActions ??= new List<Action>()).Add(invalidateView); + return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer); } + public void InheritMetrics(BufferHolder other) + { + _setCount = other._setCount; + _writeCount = other._writeCount; + _flushCount = other._flushCount; + _flushTemp = other._flushTemp; + } + public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite) { // If the last access is write, we always need a barrier to be sure we will read or modify @@ -104,12 +284,22 @@ namespace Ryujinx.Graphics.Vulkan return _buffer; } - public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false) + public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false, bool isSSBO = false) { if (isWrite) { + _writeCount++; + SignalWrite(0, Size); } + else if (isSSBO) + { + // Always consider SSBO access for swapping to device local memory. + + _writeCount++; + + ConsiderBackingSwap(); + } return _buffer; } @@ -118,6 +308,8 @@ namespace Ryujinx.Graphics.Vulkan { if (isWrite) { + _writeCount++; + SignalWrite(offset, size); } @@ -126,6 +318,8 @@ namespace Ryujinx.Graphics.Vulkan public void SignalWrite(int offset, int size) { + ConsiderBackingSwap(); + if (offset == 0 && size == Size) { _cachedConvertedBuffers.Clear(); @@ -147,11 +341,76 @@ namespace Ryujinx.Graphics.Vulkan return _map; } - public unsafe ReadOnlySpan<byte> GetData(int offset, int size) + private void ClearFlushFence() { + // Asusmes _flushLock is held as writer. + + if (_flushFence != null) + { + if (_flushWaiting == 0) + { + _flushFence.Put(); + } + + _flushFence = null; + } + } + + private void WaitForFlushFence() + { + // Assumes the _flushLock is held as reader, returns in same state. + + if (_flushFence != null) + { + // If storage has changed, make sure the fence has been reached so that the data is in place. + + var cookie = _flushLock.UpgradeToWriterLock(Timeout.Infinite); + + if (_flushFence != null) + { + var fence = _flushFence; + Interlocked.Increment(ref _flushWaiting); + + // Don't wait in the lock. + + var restoreCookie = _flushLock.ReleaseLock(); + + fence.Wait(); + + _flushLock.RestoreLock(ref restoreCookie); + + if (Interlocked.Decrement(ref _flushWaiting) == 0) + { + fence.Put(); + } + + _flushFence = null; + } + + _flushLock.DowngradeFromWriterLock(ref cookie); + } + } + + public unsafe PinnedSpan<byte> GetData(int offset, int size) + { + _flushLock.AcquireReaderLock(Timeout.Infinite); + + WaitForFlushFence(); + + _flushCount++; + + Span<byte> result; + if (_map != IntPtr.Zero) { - return GetDataStorage(offset, size); + result = GetDataStorage(offset, size); + + // Need to be careful here, the buffer can't be unmapped while the data is being used. + _buffer.IncrementReferenceCount(); + + _flushLock.ReleaseReaderLock(); + + return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount); } else { @@ -161,12 +420,17 @@ namespace Ryujinx.Graphics.Vulkan { _gd.FlushAllCommands(); - return resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size); + result = resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size); } else { - return resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size); + result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size); } + + _flushLock.ReleaseReaderLock(); + + // Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses. + return PinnedSpan<byte>.UnsafeFromSpan(result); } } @@ -190,6 +454,8 @@ namespace Ryujinx.Graphics.Vulkan return; } + _setCount++; + if (_map != IntPtr.Zero) { // If persistently mapped, set the data directly if the buffer is not currently in use. @@ -268,6 +534,8 @@ namespace Ryujinx.Graphics.Vulkan var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value; + _writeCount--; + InsertBufferBarrier( _gd, cbs.CommandBuffer, @@ -502,11 +770,19 @@ namespace Ryujinx.Graphics.Vulkan public void Dispose() { + _swapQueued = false; + _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); _buffer.Dispose(); _allocationAuto.Dispose(); _cachedConvertedBuffers.Dispose(); + + _flushLock.AcquireWriterLock(Timeout.Infinite); + + ClearFlushFence(); + + _flushLock.ReleaseWriterLock(); } } } diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs index 49fdd75d6..f8f41e5b2 100644 --- a/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using VkFormat = Silk.NET.Vulkan.Format; +using VkBuffer = Silk.NET.Vulkan.Buffer; namespace Ryujinx.Graphics.Vulkan { @@ -16,17 +17,17 @@ namespace Ryujinx.Graphics.Vulkan // Some drivers don't expose a "HostCached" memory type, // so we need those alternative flags for the allocation to succeed there. - private const MemoryPropertyFlags DefaultBufferMemoryAltFlags = + private const MemoryPropertyFlags DefaultBufferMemoryNoCacheFlags = MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit; private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = MemoryPropertyFlags.DeviceLocalBit; - private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags = + private const MemoryPropertyFlags DeviceLocalMappedBufferMemoryFlags = + MemoryPropertyFlags.DeviceLocalBit | MemoryPropertyFlags.HostVisibleBit | - MemoryPropertyFlags.HostCoherentBit | - MemoryPropertyFlags.DeviceLocalBit; + MemoryPropertyFlags.HostCoherentBit; private const BufferUsageFlags DefaultBufferUsageFlags = BufferUsageFlags.TransferSrcBit | @@ -54,14 +55,14 @@ namespace Ryujinx.Graphics.Vulkan StagingBuffer = new StagingBuffer(gd, this); } - public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal) + public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default) { - return CreateWithHandle(gd, size, deviceLocal, out _); + return CreateWithHandle(gd, size, out _, baseType, storageHint); } - public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal, out BufferHolder holder) + public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, out BufferHolder holder, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default) { - holder = Create(gd, size, deviceLocal: deviceLocal); + holder = Create(gd, size, baseType: baseType, storageHint: storageHint); if (holder == null) { return BufferHandle.Null; @@ -74,7 +75,12 @@ namespace Ryujinx.Graphics.Vulkan return Unsafe.As<ulong, BufferHandle>(ref handle64); } - public unsafe BufferHolder Create(VulkanRenderer gd, int size, bool forConditionalRendering = false, bool deviceLocal = false) + public unsafe (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) CreateBacking( + VulkanRenderer gd, + int size, + BufferAllocationType type, + bool forConditionalRendering = false, + BufferAllocationType fallbackType = BufferAllocationType.Auto) { var usage = DefaultBufferUsageFlags; @@ -98,48 +104,106 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); - MemoryPropertyFlags allocateFlags; - MemoryPropertyFlags allocateFlagsAlt; + MemoryAllocation allocation; - if (deviceLocal) + do { - allocateFlags = DeviceLocalBufferMemoryFlags; - allocateFlagsAlt = DeviceLocalBufferMemoryFlags; - } - else - { - allocateFlags = DefaultBufferMemoryFlags; - allocateFlagsAlt = DefaultBufferMemoryAltFlags; - } + var allocateFlags = type switch + { + BufferAllocationType.HostMappedNoCache => DefaultBufferMemoryNoCacheFlags, + BufferAllocationType.HostMapped => DefaultBufferMemoryFlags, + BufferAllocationType.DeviceLocal => DeviceLocalBufferMemoryFlags, + BufferAllocationType.DeviceLocalMapped => DeviceLocalMappedBufferMemoryFlags, + _ => DefaultBufferMemoryFlags + }; - var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt); + // If an allocation with this memory type fails, fall back to the previous one. + try + { + allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, true); + } + catch (VulkanException) + { + allocation = default; + } + } + while (allocation.Memory.Handle == 0 && (--type != fallbackType)); if (allocation.Memory.Handle == 0UL) { gd.Api.DestroyBuffer(_device, buffer, null); - return null; + return default; } gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset); - return new BufferHolder(gd, _device, buffer, allocation, size); + return (buffer, allocation, type); } - public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size) + public unsafe BufferHolder Create( + VulkanRenderer gd, + int size, + bool forConditionalRendering = false, + BufferAllocationType baseType = BufferAllocationType.HostMapped, + BufferHandle storageHint = default) { - if (TryGetBuffer(handle, out var holder)) + BufferAllocationType type = baseType; + BufferHolder storageHintHolder = null; + + if (baseType == BufferAllocationType.Auto) { - return holder.CreateView(format, offset, size); + if (gd.IsSharedMemory) + { + baseType = BufferAllocationType.HostMapped; + type = baseType; + } + else + { + type = size >= BufferHolder.DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocal : BufferAllocationType.HostMapped; + } + + if (storageHint != BufferHandle.Null) + { + if (TryGetBuffer(storageHint, out storageHintHolder)) + { + type = storageHintHolder.DesiredType; + } + } + } + + (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = + CreateBacking(gd, size, type, forConditionalRendering); + + if (buffer.Handle != 0) + { + var holder = new BufferHolder(gd, _device, buffer, allocation, size, baseType, resultType); + + if (storageHintHolder != null) + { + holder.InheritMetrics(storageHintHolder); + } + + return holder; } return null; } - public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite) + public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size, Action invalidateView) { if (TryGetBuffer(handle, out var holder)) { - return holder.GetBuffer(commandBuffer, isWrite); + return holder.CreateView(format, offset, size, invalidateView); + } + + return null; + } + + public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, bool isSSBO = false) + { + if (TryGetBuffer(handle, out var holder)) + { + return holder.GetBuffer(commandBuffer, isWrite, isSSBO); } return null; @@ -332,14 +396,14 @@ namespace Ryujinx.Graphics.Vulkan return null; } - public ReadOnlySpan<byte> GetData(BufferHandle handle, int offset, int size) + public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size) { if (TryGetBuffer(handle, out var holder)) { return holder.GetData(offset, size); } - return ReadOnlySpan<byte>.Empty; + return new PinnedSpan<byte>(); } public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged diff --git a/Ryujinx.Graphics.Vulkan/BufferState.cs b/Ryujinx.Graphics.Vulkan/BufferState.cs index f3a584695..6829f8333 100644 --- a/Ryujinx.Graphics.Vulkan/BufferState.cs +++ b/Ryujinx.Graphics.Vulkan/BufferState.cs @@ -2,14 +2,14 @@ namespace Ryujinx.Graphics.Vulkan { - readonly struct BufferState : IDisposable + struct BufferState : IDisposable { public static BufferState Null => new BufferState(null, 0, 0); private readonly int _offset; private readonly int _size; - private readonly Auto<DisposableBuffer> _buffer; + private Auto<DisposableBuffer> _buffer; public BufferState(Auto<DisposableBuffer> buffer, int offset, int size) { @@ -29,6 +29,17 @@ namespace Ryujinx.Graphics.Vulkan } } + public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + { + if (_buffer == from) + { + _buffer.DecrementReferenceCount(); + to.IncrementReferenceCount(); + + _buffer = to; + } + } + public void Dispose() { _buffer?.DecrementReferenceCount(); diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 19a085023..7e126e044 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Vulkan else { // If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings. - _dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, deviceLocal: true); + _dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, baseType: BufferAllocationType.DeviceLocal); } _dummyTexture = gd.CreateTextureView(new TextureCreateInfo( @@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Vulkan var buffer = assignment.Range; int index = assignment.Binding; - Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); + Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false, isSSBO: true); ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index]; DescriptorBufferInfo info = new DescriptorBufferInfo() @@ -640,6 +640,23 @@ namespace Ryujinx.Graphics.Vulkan Array.Clear(_storageSet); } + private void SwapBuffer(Auto<DisposableBuffer>[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + { + for (int i = 0; i < list.Length; i++) + { + if (list[i] == from) + { + list[i] = to; + } + } + } + + public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + { + SwapBuffer(_uniformBufferRefs, from, to); + SwapBuffer(_storageBufferRefs, from, to); + } + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index a12070592..a871679b0 100644 --- a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -156,11 +156,11 @@ namespace Ryujinx.Graphics.Vulkan.Effects }; int rangeSize = dimensionsBuffer.Length * sizeof(float); - var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); _renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer); ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)}; - var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float), false); + var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float)); _renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer); int threadGroupWorkRegionDim = 16; diff --git a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index 0f6a0a7ba..9e73e1b82 100644 --- a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height }; int rangeSize = resolutionBuffer.Length * sizeof(float); - var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); diff --git a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index 4dcdaa646..bf698ade6 100644 --- a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height }; int rangeSize = resolutionBuffer.Length * sizeof(float); - var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index c67389aa4..8eb3088ec 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -394,7 +394,7 @@ namespace Ryujinx.Graphics.Vulkan (region[2], region[3]) = (region[3], region[2]); } - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize); gd.BufferManager.SetData<float>(bufferHandle, 0, region); @@ -495,7 +495,7 @@ namespace Ryujinx.Graphics.Vulkan (region[2], region[3]) = (region[3], region[2]); } - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize); gd.BufferManager.SetData<float>(bufferHandle, 0, region); @@ -649,7 +649,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetCommandBuffer(cbs); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize); gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor); @@ -726,7 +726,7 @@ namespace Ryujinx.Graphics.Vulkan (region[2], region[3]) = (region[3], region[2]); } - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize); gd.BufferManager.SetData<float>(bufferHandle, 0, region); @@ -802,7 +802,7 @@ namespace Ryujinx.Graphics.Vulkan shaderParams[2] = size; shaderParams[3] = srcOffset; - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); @@ -958,7 +958,7 @@ namespace Ryujinx.Graphics.Vulkan shaderParams[0] = BitOperations.Log2((uint)ratio); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); @@ -1050,7 +1050,7 @@ namespace Ryujinx.Graphics.Vulkan (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples)); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); @@ -1133,7 +1133,7 @@ namespace Ryujinx.Graphics.Vulkan (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples)); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false); + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); @@ -1407,7 +1407,7 @@ namespace Ryujinx.Graphics.Vulkan pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length)); - var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false, out var patternBuffer); + var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, out var patternBuffer); var patternBufferAuto = patternBuffer.GetBuffer(); gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams); diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs index 907742931..11f4ec333 100644 --- a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs +++ b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Vulkan } // Expand the repeating pattern to the number of requested primitives. - BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int)); + BufferHandle newBuffer = _gd.BufferManager.CreateWithHandle(_gd, expectedSize * sizeof(int)); // Copy the old data to the new one. if (_repeatingBuffer != BufferHandle.Null) diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs index 64b95f600..75b18456a 100644 --- a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs +++ b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs @@ -146,5 +146,16 @@ namespace Ryujinx.Graphics.Vulkan { return _buffer == buffer; } + + public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + { + if (_buffer == from) + { + _buffer.DecrementReferenceCount(); + to.IncrementReferenceCount(); + + _buffer = to; + } + } } } diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index e4dcd916e..6a786a96c 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -28,32 +28,25 @@ namespace Ryujinx.Graphics.Vulkan public MemoryAllocation AllocateDeviceMemory( MemoryRequirements requirements, - MemoryPropertyFlags flags = 0) + MemoryPropertyFlags flags = 0, + bool isBuffer = false) { - return AllocateDeviceMemory(requirements, flags, flags); - } - - public MemoryAllocation AllocateDeviceMemory( - MemoryRequirements requirements, - MemoryPropertyFlags flags, - MemoryPropertyFlags alternativeFlags) - { - int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags); + int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags); if (memoryTypeIndex < 0) { return default; } bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit); - return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map); + return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map, isBuffer); } - private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map) + private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer) { for (int i = 0; i < _blockLists.Count; i++) { var bl = _blockLists[i]; - if (bl.MemoryTypeIndex == memoryTypeIndex) + if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer) { lock (bl) { @@ -62,18 +55,15 @@ namespace Ryujinx.Graphics.Vulkan } } - var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment); + var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer); _blockLists.Add(newBl); return newBl.Allocate(size, alignment, map); } private int FindSuitableMemoryTypeIndex( uint memoryTypeBits, - MemoryPropertyFlags flags, - MemoryPropertyFlags alternativeFlags) + MemoryPropertyFlags flags) { - int bestCandidateIndex = -1; - for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++) { var type = _physicalDeviceMemoryProperties.MemoryTypes[i]; @@ -84,14 +74,27 @@ namespace Ryujinx.Graphics.Vulkan { return i; } - else if (type.PropertyFlags.HasFlag(alternativeFlags)) - { - bestCandidateIndex = i; - } } } - return bestCandidateIndex; + return -1; + } + + public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice) + { + // The device is regarded as having shared memory if all heaps have the device local bit. + + api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); + + for (int i = 0; i < properties.MemoryHeapCount; i++) + { + if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit)) + { + return false; + } + } + + return true; } public void Dispose() diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs index dc3eb598a..e564cb264 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs @@ -162,15 +162,17 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; public int MemoryTypeIndex { get; } + public bool ForBuffer { get; } private readonly int _blockAlignment; - public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment) + public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer) { _blocks = new List<Block>(); _api = api; _device = device; MemoryTypeIndex = memoryTypeIndex; + ForBuffer = forBuffer; _blockAlignment = blockAlignment; } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 3abab0657..6c2f16849 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1297,6 +1297,25 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } + public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + { + _indexBuffer.Swap(from, to); + + for (int i = 0; i < _vertexBuffers.Length; i++) + { + _vertexBuffers[i].Swap(from, to); + } + + for (int i = 0; i < _transformFeedbackBuffers.Length; i++) + { + _transformFeedbackBuffers[i].Swap(from, to); + } + + _descriptorSetUpdater.SwapBuffer(from, to); + + SignalCommandBufferChange(); + } + public unsafe void TextureBarrier() { MemoryBarrier memoryBarrier = new MemoryBarrier() diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 6c026a07f..8026103e7 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -17,10 +17,13 @@ namespace Ryujinx.Graphics.Vulkan private ulong _byteWeight; + private List<BufferHolder> _backingSwaps; + public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device) { _activeQueries = new List<(QueryPool, bool)>(); _pendingQueryCopies = new(); + _backingSwaps = new(); CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; } @@ -185,6 +188,20 @@ namespace Ryujinx.Graphics.Vulkan } } + private void TryBackingSwaps() + { + CommandBufferScoped? cbs = null; + + _backingSwaps.RemoveAll((holder) => holder.TryBackingSwap(ref cbs)); + + cbs?.Dispose(); + } + + public void AddBackingSwap(BufferHolder holder) + { + _backingSwaps.Add(holder); + } + public void Restore() { if (Pipeline != null) @@ -230,6 +247,8 @@ namespace Ryujinx.Graphics.Vulkan Gd.ResetCounterPool(); + TryBackingSwaps(); + Restore(); } diff --git a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index bf9a6eadd..738bf57d1 100644 --- a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -57,12 +57,12 @@ namespace Ryujinx.Graphics.Vulkan throw new NotSupportedException(); } - public ReadOnlySpan<byte> GetData() + public PinnedSpan<byte> GetData() { return _gd.GetBufferData(_bufferHandle, _offset, _size); } - public ReadOnlySpan<byte> GetData(int layer, int level) + public PinnedSpan<byte> GetData(int layer, int level) { return GetData(); } @@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Vulkan { if (_bufferView == null) { - _bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size); + _bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size, ReleaseImpl); } return _bufferView?.Get(cbs, _offset, _size).Value ?? default; @@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Vulkan return bufferView.Get(cbs, _offset, _size).Value; } - bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size); + bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size, ReleaseImpl); if (bufferView != null) { diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index 264ecf5db..cd280d5f4 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -531,7 +531,7 @@ namespace Ryujinx.Graphics.Vulkan return bitmap; } - public ReadOnlySpan<byte> GetData() + public PinnedSpan<byte> GetData() { BackgroundResource resources = _gd.BackgroundResources.Get(); @@ -539,15 +539,15 @@ namespace Ryujinx.Graphics.Vulkan { _gd.FlushAllCommands(); - return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer()); + return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer())); } else { - return GetData(resources.GetPool(), resources.GetFlushBuffer()); + return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer())); } } - public ReadOnlySpan<byte> GetData(int layer, int level) + public PinnedSpan<byte> GetData(int layer, int level) { BackgroundResource resources = _gd.BackgroundResources.Get(); @@ -555,11 +555,11 @@ namespace Ryujinx.Graphics.Vulkan { _gd.FlushAllCommands(); - return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level); + return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level)); } else { - return GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level); + return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); } } diff --git a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index 7a0220108..c48560194 100644 --- a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -129,6 +129,17 @@ namespace Ryujinx.Graphics.Vulkan return _buffer == buffer; } + public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + { + if (_buffer == from) + { + _buffer.DecrementReferenceCount(); + to.IncrementReferenceCount(); + + _buffer = to; + } + } + public void Dispose() { // Only dispose if this buffer is not refetched on each bind. diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 8d4e54c4b..7e7d30361 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -80,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan internal bool IsAmdGcn { get; private set; } internal bool IsMoltenVk { get; private set; } internal bool IsTBDR { get; private set; } + internal bool IsSharedMemory { get; private set; } public string GpuVendor { get; private set; } public string GpuRenderer { get; private set; } public string GpuVersion { get; private set; } @@ -313,6 +314,8 @@ namespace Ryujinx.Graphics.Vulkan portabilityFlags, vertexBufferAlignment); + IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice); + MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); @@ -373,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan _initialized = true; } - public BufferHandle CreateBuffer(int size) + public BufferHandle CreateBuffer(int size, BufferHandle storageHint) { - return BufferManager.CreateWithHandle(this, size, false); + return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint); } public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) @@ -439,7 +442,7 @@ namespace Ryujinx.Graphics.Vulkan _syncManager.RegisterFlush(); } - public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) + public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) { return BufferManager.GetData(buffer, offset, size); } From 17620d18db8d4a67e4b917596c760107d26fadc5 Mon Sep 17 00:00:00 2001 From: Wunk <wunkolo@gmail.com> Date: Mon, 20 Mar 2023 12:09:24 -0700 Subject: [PATCH 409/737] ARMeilleure: Add initial support for AVX512 (EVEX encoding) (cont) (#4147) * ARMeilleure: Add AVX512{F,VL,DQ,BW} detection Add `UseAvx512Ortho` and `UseAvx512OrthoFloat` optimization flags as short-hands for `F+VL` and `F+VL+DQ`. * ARMeilleure: Add initial support for EVEX instruction encoding Does not implement rounding, or exception controls. * ARMeilleure: Add `X86Vpternlogd` Accelerates the vector-`Not` instruction. * ARMeilleure: Add check for `OSXSAVE` for AVX{2,512} * ARMeilleure: Add check for `XCR0` flags Add XCR0 register checks for AVX and AVX512F, following the guidelines from section 14.3 and 15.2 from the Intel Architecture Software Developer's Manual. * ARMeilleure: Remove redundant `ReProtect` and `Dispose`, formatting * ARMeilleure: Move XCR0 procedure to GetXcr0Eax * ARMeilleure: Add `XCR0` to `FeatureInfo` structure * ARMeilleure: Utilize `ReadOnlySpan` for Xcr0 assembly Avoids an additional allocation * ARMeilleure: Formatting fixes * ARMeilleure: Fix EVEX encoding src2 register index > Just like in VEX prefix, vvvv is provided in inverted form. * ARMeilleure: Add `X86Vpternlogd` acceleration to `Vmvn_I` Passes unit tests, verified instruction utilization * ARMeilleure: Fix EVEX register operand designations Operand 2 was being sourced improperly. EVEX encoded instructions source their operands like so: Operand 1: ModRM:reg Operand 2: EVEX.vvvvv Operand 3: ModRM:r/m Operand 4: Imm This fixes the improper register designations when emitting vpternlog. Now "dest", "src1", "src2" arguments emit in the proper order in EVEX instructions. * ARMeilleure: Add `X86Vpternlogd` acceleration to `Orn_V` * ARMeilleure: PTC version bump * ARMeilleure: Update EVEX encoding Debug.Assert to Debug.Fail * ARMeilleure: Update EVEX encoding comment capitalization --- ARMeilleure/ARMeilleure.csproj | 1 + ARMeilleure/CodeGen/X86/Assembler.cs | 105 +++++++++++++++++- ARMeilleure/CodeGen/X86/AssemblerTable.cs | 2 + .../CodeGen/X86/HardwareCapabilities.cs | 52 ++++++++- ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 1 + ARMeilleure/CodeGen/X86/X86Instruction.cs | 1 + .../Instructions/InstEmitSimdLogical.cs | 33 +++++- .../Instructions/InstEmitSimdLogical32.cs | 7 ++ .../Instructions/InstEmitSimdMove32.cs | 9 +- .../IntermediateRepresentation/Intrinsic.cs | 1 + ARMeilleure/Optimizations.cs | 11 ++ ARMeilleure/Translation/PTC/Ptc.cs | 14 ++- 12 files changed, 226 insertions(+), 11 deletions(-) diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj index 1c2135ed5..fa5551154 100644 --- a/ARMeilleure/ARMeilleure.csproj +++ b/ARMeilleure/ARMeilleure.csproj @@ -7,6 +7,7 @@ <ItemGroup> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> + <ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" /> </ItemGroup> <ItemGroup> diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs index 2ea4208b3..67736a31f 100644 --- a/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/ARMeilleure/CodeGen/X86/Assembler.cs @@ -1034,7 +1034,13 @@ namespace ARMeilleure.CodeGen.X86 Debug.Assert(opCode != BadOp, "Invalid opcode value."); - if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding) + if ((flags & InstructionFlags.Evex) != 0 && HardwareCapabilities.SupportsEvexEncoding) + { + WriteEvexInst(dest, src1, src2, type, flags, opCode); + + opCode &= 0xff; + } + else if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding) { // In a vex encoding, only one prefix can be active at a time. The active prefix is encoded in the second byte using two bits. @@ -1153,6 +1159,103 @@ namespace ARMeilleure.CodeGen.X86 } } + private void WriteEvexInst( + Operand dest, + Operand src1, + Operand src2, + OperandType type, + InstructionFlags flags, + int opCode, + bool broadcast = false, + int registerWidth = 128, + int maskRegisterIdx = 0, + bool zeroElements = false) + { + int op1Idx = dest.GetRegister().Index; + int op2Idx = src1.GetRegister().Index; + int op3Idx = src2.GetRegister().Index; + + WriteByte(0x62); + + // P0 + // Extend operand 1 register + bool r = (op1Idx & 8) == 0; + // Extend operand 3 register + bool x = (op3Idx & 16) == 0; + // Extend operand 3 register + bool b = (op3Idx & 8) == 0; + // Extend operand 1 register + bool rp = (op1Idx & 16) == 0; + // Escape code index + byte mm = 0b00; + + switch ((ushort)(opCode >> 8)) + { + case 0xf00: mm = 0b01; break; + case 0xf38: mm = 0b10; break; + case 0xf3a: mm = 0b11; break; + + default: Debug.Fail($"Failed to EVEX encode opcode 0x{opCode:X}."); break; + } + + WriteByte( + (byte)( + (r ? 0x80 : 0) | + (x ? 0x40 : 0) | + (b ? 0x20 : 0) | + (rp ? 0x10 : 0) | + mm)); + + // P1 + // Specify 64-bit lane mode + bool w = Is64Bits(type); + // Operand 2 register index + byte vvvv = (byte)(~op2Idx & 0b1111); + // Opcode prefix + byte pp = (flags & InstructionFlags.PrefixMask) switch + { + InstructionFlags.Prefix66 => 0b01, + InstructionFlags.PrefixF3 => 0b10, + InstructionFlags.PrefixF2 => 0b11, + _ => 0 + }; + WriteByte( + (byte)( + (w ? 0x80 : 0) | + (vvvv << 3) | + 0b100 | + pp)); + + // P2 + // Mask register determines what elements to zero, rather than what elements to merge + bool z = zeroElements; + // Specifies register-width + byte ll = 0b00; + switch (registerWidth) + { + case 128: ll = 0b00; break; + case 256: ll = 0b01; break; + case 512: ll = 0b10; break; + + default: Debug.Fail($"Invalid EVEX vector register width {registerWidth}."); break; + } + // Embedded broadcast in the case of a memory operand + bool bcast = broadcast; + // Extend operand 2 register + bool vp = (op2Idx & 16) == 0; + // Mask register index + Debug.Assert(maskRegisterIdx < 8, $"Invalid mask register index {maskRegisterIdx}."); + byte aaa = (byte)(maskRegisterIdx & 0b111); + + WriteByte( + (byte)( + (z ? 0x80 : 0) | + (ll << 5) | + (bcast ? 0x10 : 0) | + (vp ? 8 : 0) | + aaa)); + } + private void WriteCompactInst(Operand operand, int opCode) { int regIndex = operand.GetRegister().Index; diff --git a/ARMeilleure/CodeGen/X86/AssemblerTable.cs b/ARMeilleure/CodeGen/X86/AssemblerTable.cs index ecdc029f9..b47b3ecd1 100644 --- a/ARMeilleure/CodeGen/X86/AssemblerTable.cs +++ b/ARMeilleure/CodeGen/X86/AssemblerTable.cs @@ -20,6 +20,7 @@ namespace ARMeilleure.CodeGen.X86 Reg8Dest = 1 << 2, RexW = 1 << 3, Vex = 1 << 4, + Evex = 1 << 5, PrefixBit = 16, PrefixMask = 7 << PrefixBit, @@ -278,6 +279,7 @@ namespace ARMeilleure.CodeGen.X86 Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vpternlogd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a25, InstructionFlags.Evex | InstructionFlags.Prefix66)); Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None)); Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex)); diff --git a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index c12a4e28b..63a9e46a2 100644 --- a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -1,10 +1,14 @@ +using Ryujinx.Memory; using System; +using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; namespace ARMeilleure.CodeGen.X86 { static class HardwareCapabilities { + private delegate uint GetXcr0(); + static HardwareCapabilities() { if (!X86Base.IsSupported) @@ -24,6 +28,28 @@ namespace ARMeilleure.CodeGen.X86 FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7; FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7; } + + Xcr0InfoEax = (Xcr0FlagsEax)GetXcr0Eax(); + } + + private static uint GetXcr0Eax() + { + ReadOnlySpan<byte> asmGetXcr0 = new byte[] + { + 0x31, 0xc9, // xor ecx, ecx + 0xf, 0x01, 0xd0, // xgetbv + 0xc3, // ret + }; + + using MemoryBlock memGetXcr0 = new MemoryBlock((ulong)asmGetXcr0.Length); + + memGetXcr0.Write(0, asmGetXcr0); + + memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute); + + var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer); + + return fGetXcr0(); } [Flags] @@ -44,6 +70,7 @@ namespace ARMeilleure.CodeGen.X86 Sse42 = 1 << 20, Popcnt = 1 << 23, Aes = 1 << 25, + Osxsave = 1 << 27, Avx = 1 << 28, F16c = 1 << 29 } @@ -52,7 +79,11 @@ namespace ARMeilleure.CodeGen.X86 public enum FeatureFlags7Ebx { Avx2 = 1 << 5, - Sha = 1 << 29 + Avx512f = 1 << 16, + Avx512dq = 1 << 17, + Sha = 1 << 29, + Avx512bw = 1 << 30, + Avx512vl = 1 << 31 } [Flags] @@ -61,10 +92,21 @@ namespace ARMeilleure.CodeGen.X86 Gfni = 1 << 8, } + [Flags] + public enum Xcr0FlagsEax + { + Sse = 1 << 1, + YmmHi128 = 1 << 2, + Opmask = 1 << 5, + ZmmHi256 = 1 << 6, + Hi16Zmm = 1 << 7 + } + public static FeatureFlags1Edx FeatureInfo1Edx { get; } public static FeatureFlags1Ecx FeatureInfo1Ecx { get; } public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0; public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0; + public static Xcr0FlagsEax Xcr0InfoEax { get; } = 0; public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse); public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2); @@ -76,8 +118,13 @@ namespace ARMeilleure.CodeGen.X86 public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42); public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt); public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes); - public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx); + public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128); public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx; + public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Osxsave) + && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128 | Xcr0FlagsEax.Opmask | Xcr0FlagsEax.ZmmHi256 | Xcr0FlagsEax.Hi16Zmm); + public static bool SupportsAvx512Vl => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512vl) && SupportsAvx512F; + public static bool SupportsAvx512Bw => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512bw) && SupportsAvx512F; + public static bool SupportsAvx512Dq => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512dq) && SupportsAvx512F; public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c); public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha); public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni); @@ -85,5 +132,6 @@ namespace ARMeilleure.CodeGen.X86 public static bool ForceLegacySse { get; set; } public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse; + public static bool SupportsEvexEncoding => SupportsAvx512F && !ForceLegacySse; } } \ No newline at end of file diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs index 8c909ac13..c788fa442 100644 --- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs +++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs @@ -180,6 +180,7 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma)); + Add(Intrinsic.X86Vpternlogd, new IntrinsicInfo(X86Instruction.Vpternlogd, IntrinsicType.TernaryImm)); Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary)); Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary)); } diff --git a/ARMeilleure/CodeGen/X86/X86Instruction.cs b/ARMeilleure/CodeGen/X86/X86Instruction.cs index b024394e1..ecfc432d7 100644 --- a/ARMeilleure/CodeGen/X86/X86Instruction.cs +++ b/ARMeilleure/CodeGen/X86/X86Instruction.cs @@ -219,6 +219,7 @@ namespace ARMeilleure.CodeGen.X86 Vfnmsub231sd, Vfnmsub231ss, Vpblendvb, + Vpternlogd, Xor, Xorpd, Xorps, diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical.cs b/ARMeilleure/Instructions/InstEmitSimdLogical.cs index 8ca815801..2bf531e6c 100644 --- a/ARMeilleure/Instructions/InstEmitSimdLogical.cs +++ b/ARMeilleure/Instructions/InstEmitSimdLogical.cs @@ -254,7 +254,22 @@ namespace ARMeilleure.Instructions public static void Not_V(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAvx512Ortho) + { + OpCodeSimd op = (OpCodeSimd)context.CurrOp; + + Operand n = GetVec(op.Rn); + + Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, n, Const(~0b10101010)); + + if (op.RegisterSize == RegisterSize.Simd64) + { + res = context.VectorZeroUpper64(res); + } + + context.Copy(GetVec(op.Rd), res); + } + else if (Optimizations.UseSse2) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -283,6 +298,22 @@ namespace ARMeilleure.Instructions { InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrnV); } + else if (Optimizations.UseAvx512Ortho) + { + OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; + + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010)); + + if (op.RegisterSize == RegisterSize.Simd64) + { + res = context.VectorZeroUpper64(res); + } + + context.Copy(GetVec(op.Rd), res); + } else if (Optimizations.UseSse2) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical32.cs b/ARMeilleure/Instructions/InstEmitSimdLogical32.cs index c2a04778b..68ef4ed17 100644 --- a/ARMeilleure/Instructions/InstEmitSimdLogical32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdLogical32.cs @@ -151,6 +151,13 @@ namespace ARMeilleure.Instructions { InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrnV | Intrinsic.Arm64V128, n, m)); } + else if (Optimizations.UseAvx512Ortho) + { + EmitVectorBinaryOpSimd32(context, (n, m) => + { + return context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010)); + }); + } else if (Optimizations.UseSse2) { Operand mask = context.VectorOne(); diff --git a/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/ARMeilleure/Instructions/InstEmitSimdMove32.cs index 17100eb9c..b8b91b31d 100644 --- a/ARMeilleure/Instructions/InstEmitSimdMove32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdMove32.cs @@ -34,7 +34,14 @@ namespace ARMeilleure.Instructions public static void Vmvn_I(ArmEmitterContext context) { - if (Optimizations.UseSse2) + if (Optimizations.UseAvx512Ortho) + { + EmitVectorUnaryOpSimd32(context, (op1) => + { + return context.AddIntrinsic(Intrinsic.X86Vpternlogd, op1, op1, Const(0b01010101)); + }); + } + else if (Optimizations.UseSse2) { EmitVectorUnaryOpSimd32(context, (op1) => { diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index a665e4b7a..b629345ee 100644 --- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -173,6 +173,7 @@ namespace ARMeilleure.IntermediateRepresentation X86Vfnmadd231ss, X86Vfnmsub231sd, X86Vfnmsub231ss, + X86Vpternlogd, X86Xorpd, X86Xorps, diff --git a/ARMeilleure/Optimizations.cs b/ARMeilleure/Optimizations.cs index 9044314f6..a84a4dc4f 100644 --- a/ARMeilleure/Optimizations.cs +++ b/ARMeilleure/Optimizations.cs @@ -23,6 +23,10 @@ namespace ARMeilleure public static bool UseSse42IfAvailable { get; set; } = true; public static bool UsePopCntIfAvailable { get; set; } = true; public static bool UseAvxIfAvailable { get; set; } = true; + public static bool UseAvx512FIfAvailable { get; set; } = true; + public static bool UseAvx512VlIfAvailable { get; set; } = true; + public static bool UseAvx512BwIfAvailable { get; set; } = true; + public static bool UseAvx512DqIfAvailable { get; set; } = true; public static bool UseF16cIfAvailable { get; set; } = true; public static bool UseFmaIfAvailable { get; set; } = true; public static bool UseAesniIfAvailable { get; set; } = true; @@ -47,11 +51,18 @@ namespace ARMeilleure internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42; internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt; internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse; + internal static bool UseAvx512F => UseAvx512FIfAvailable && X86HardwareCapabilities.SupportsAvx512F && !ForceLegacySse; + internal static bool UseAvx512Vl => UseAvx512VlIfAvailable && X86HardwareCapabilities.SupportsAvx512Vl && !ForceLegacySse; + internal static bool UseAvx512Bw => UseAvx512BwIfAvailable && X86HardwareCapabilities.SupportsAvx512Bw && !ForceLegacySse; + internal static bool UseAvx512Dq => UseAvx512DqIfAvailable && X86HardwareCapabilities.SupportsAvx512Dq && !ForceLegacySse; internal static bool UseF16c => UseF16cIfAvailable && X86HardwareCapabilities.SupportsF16c; internal static bool UseFma => UseFmaIfAvailable && X86HardwareCapabilities.SupportsFma; internal static bool UseAesni => UseAesniIfAvailable && X86HardwareCapabilities.SupportsAesni; internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq; internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha; internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni; + + internal static bool UseAvx512Ortho => UseAvx512F && UseAvx512Vl; + internal static bool UseAvx512OrthoFloat => UseAvx512Ortho && UseAvx512Dq; } } diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 0b23fd043..17f687062 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 4484; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 4485; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; @@ -969,6 +969,7 @@ namespace ARMeilleure.Translation.PTC (ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap, (ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap2, (ulong)Arm64HardwareCapabilities.MacOsFeatureInfo, + 0, 0); } else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) @@ -977,11 +978,12 @@ namespace ARMeilleure.Translation.PTC (ulong)X86HardwareCapabilities.FeatureInfo1Ecx, (ulong)X86HardwareCapabilities.FeatureInfo1Edx, (ulong)X86HardwareCapabilities.FeatureInfo7Ebx, - (ulong)X86HardwareCapabilities.FeatureInfo7Ecx); + (ulong)X86HardwareCapabilities.FeatureInfo7Ecx, + (ulong)X86HardwareCapabilities.Xcr0InfoEax); } else { - return new FeatureInfo(0, 0, 0, 0); + return new FeatureInfo(0, 0, 0, 0, 0); } } @@ -1002,7 +1004,7 @@ namespace ARMeilleure.Translation.PTC return osPlatform; } - [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 78*/)] + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)] private struct OuterHeader { public ulong Magic; @@ -1034,8 +1036,8 @@ namespace ARMeilleure.Translation.PTC } } - [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 32*/)] - private record struct FeatureInfo(ulong FeatureInfo0, ulong FeatureInfo1, ulong FeatureInfo2, ulong FeatureInfo3); + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 40*/)] + private record struct FeatureInfo(ulong FeatureInfo0, ulong FeatureInfo1, ulong FeatureInfo2, ulong FeatureInfo3, ulong FeatureInfo4); [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)] private struct InnerHeader From 4ce4299ca2a6b11332f2341c69f40efd7205282f Mon Sep 17 00:00:00 2001 From: Andrey Sukharev <SukharevAndrey@users.noreply.github.com> Date: Wed, 22 Mar 2023 01:41:19 +0300 Subject: [PATCH 410/737] Use source generated json serializers in order to improve code trimming (#4094) * Use source generated json serializers in order to improve code trimming * Use strongly typed github releases model to fetch updates instead of raw Newtonsoft.Json parsing * Use separate model for LogEventArgs serialization * Make dynamic object formatter static. Fix string builder pooling. * Do not inherit json version of LogEventArgs from EventArgs * Fix extra space in object formatting * Write log json directly to stream instead of using buffer writer * Rebase fixes * Rebase fixes * Rebase fixes * Enforce block-scoped namespaces in the solution. Convert style for existing code * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Rebase indent fix * Fix indent * Delete unnecessary json properties * Rebase fix * Remove overridden json property names as they are handled in the options * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Use default json options in github api calls * Indentation and spacing fixes --------- Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- .editorconfig | 4 + ARMeilleure/Decoders/IOpCode32Exception.cs | 9 +- Ryujinx.Ava/Common/Locale/LocaleManager.cs | 2 +- Ryujinx.Ava/Modules/Updater/Updater.cs | 22 +- Ryujinx.Ava/UI/Models/Amiibo.cs | 72 ---- .../UI/ViewModels/AboutWindowViewModel.cs | 2 +- .../UI/ViewModels/AmiiboWindowViewModel.cs | 35 +- .../ViewModels/ControllerSettingsViewModel.cs | 9 +- .../DownloadableContentManagerViewModel.cs | 10 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 366 +++++++++--------- .../UI/Views/Main/MainMenuBarView.axaml.cs | 2 +- Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs | 6 +- .../UI/Windows/TitleUpdateWindow.axaml.cs | 3 - .../Configuration/AspectRatioExtensions.cs | 6 +- .../Configuration/BackendThreading.cs | 6 +- ...ownloadableContentJsonSerializerContext.cs | 11 + .../Configuration/GraphicsBackend.cs | 6 +- .../Configuration/GraphicsDebugLevel.cs | 4 + .../JsonMotionConfigControllerConverter.cs | 13 +- .../Motion/MotionConfigController.cs | 5 +- .../MotionConfigJsonSerializerContext.cs | 12 + .../Motion/MotionInputBackendType.cs | 6 +- .../Configuration/Hid/ControllerType.cs | 5 +- .../Configuration/Hid/InputBackendType.cs | 6 +- .../Configuration/Hid/InputConfig.cs | 2 + .../Hid/InputConfigJsonSerializerContext.cs | 14 + .../Hid/JsonInputConfigConverter.cs | 13 +- .../Configuration/Hid/PlayerIndex.cs | 4 + .../Configuration/MemoryManagerMode.cs | 6 +- ...itleUpdateMetadataJsonSerializerContext.cs | 10 + .../Logging/Formatters/DefaultLogFormatter.cs | 54 +-- .../Formatters/DynamicObjectFormatter.cs | 84 ++++ Ryujinx.Common/Logging/LogClass.cs | 4 + Ryujinx.Common/Logging/LogEventArgs.cs | 10 +- Ryujinx.Common/Logging/LogEventArgsJson.cs | 30 ++ .../Logging/LogEventJsonSerializerContext.cs | 9 + Ryujinx.Common/Logging/LogLevel.cs | 4 + .../Logging/Targets/JsonLogTarget.cs | 12 +- Ryujinx.Common/Utilities/CommonJsonContext.cs | 11 + Ryujinx.Common/Utilities/JsonHelper.cs | 122 +++--- .../Utilities/TypedStringEnumConverter.cs | 34 ++ Ryujinx.HLE/HOS/ApplicationLoader.cs | 12 +- .../Account/Acc/AccountSaveDataManager.cs | 30 +- .../Acc/ProfilesJsonSerializerContext.cs | 11 + .../Account/Acc/Types/AccountState.cs | 4 + .../Account/Acc/Types/ProfilesJson.cs | 10 + .../Account/Acc/Types/UserProfileJson.cs | 12 + .../Nfc/Nfp/AmiiboJsonSerializerContext.cs | 10 + .../HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 10 +- Ryujinx.Headless.SDL2/Program.cs | 7 +- .../App/ApplicationJsonSerializerContext.cs | 10 + Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 14 +- .../Configuration/AudioBackend.cs | 6 +- .../Configuration/ConfigurationFileFormat.cs | 12 +- .../ConfigurationFileFormatSettings.cs | 9 + .../ConfigurationJsonSerializerContext.cs | 10 + .../Configuration/ConfigurationState.cs | 5 +- .../Configuration/System/Language.cs | 6 +- .../Configuration/System/Region.cs | 6 +- Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs | 57 +++ .../Models/Amiibo/AmiiboApiGamesSwitch.cs | 15 + .../Models/Amiibo/AmiiboApiUsage.cs | 12 + Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs | 14 + .../Amiibo/AmiiboJsonSerializerContext.cs | 9 + .../Github/GithubReleaseAssetJsonResponse.cs | 9 + .../Github/GithubReleasesJsonResponse.cs | 10 + .../GithubReleasesJsonSerializerContext.cs | 9 + Ryujinx/Modules/Updater/Updater.cs | 24 +- Ryujinx/Ui/Windows/AboutWindow.cs | 2 +- Ryujinx/Ui/Windows/AmiiboWindow.cs | 65 +--- Ryujinx/Ui/Windows/ControllerWindow.cs | 11 +- Ryujinx/Ui/Windows/DlcWindow.cs | 15 +- Ryujinx/Ui/Windows/TitleUpdateWindow.cs | 15 +- 73 files changed, 887 insertions(+), 609 deletions(-) delete mode 100644 Ryujinx.Ava/UI/Models/Amiibo.cs create mode 100644 Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs create mode 100644 Ryujinx.Common/Logging/LogEventArgsJson.cs create mode 100644 Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Utilities/CommonJsonContext.cs create mode 100644 Ryujinx.Common/Utilities/TypedStringEnumConverter.cs create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs create mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs diff --git a/.editorconfig b/.editorconfig index 9e00e3bae..8a3054287 100644 --- a/.editorconfig +++ b/.editorconfig @@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion #### C# Coding Conventions #### +# Namespace preferences +csharp_style_namespace_declarations = block_scoped:warning +resharper_csharp_namespace_body = block_scoped + # var preferences csharp_style_var_elsewhere = false:silent csharp_style_var_for_built_in_types = false:silent diff --git a/ARMeilleure/Decoders/IOpCode32Exception.cs b/ARMeilleure/Decoders/IOpCode32Exception.cs index 82819bddd..8f0fb81a0 100644 --- a/ARMeilleure/Decoders/IOpCode32Exception.cs +++ b/ARMeilleure/Decoders/IOpCode32Exception.cs @@ -1,6 +1,7 @@ -namespace ARMeilleure.Decoders; - -interface IOpCode32Exception +namespace ARMeilleure.Decoders { - int Id { get; } + interface IOpCode32Exception + { + int Id { get; } + } } \ No newline at end of file diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index 1374bfee1..464ab780d 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -130,7 +130,7 @@ namespace Ryujinx.Ava.Common.Locale { var localeStrings = new Dictionary<LocaleKeys, string>(); string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); - var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); + var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); foreach (var item in strings) { diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index e89abd1da..c58575284 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -4,13 +4,14 @@ using FluentAvalonia.UI.Controls; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; -using Newtonsoft.Json.Linq; using Ryujinx.Ava; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Helper; +using Ryujinx.Ui.Common.Models.Github; using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,6 +32,7 @@ namespace Ryujinx.Modules internal static class Updater { private const string GitHubApiURL = "https://api.github.com"; + private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); @@ -99,22 +101,16 @@ namespace Ryujinx.Modules string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - JObject jsonRoot = JObject.Parse(fetchedJson); - JToken assets = jsonRoot["assets"]; + var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); + _buildVer = fetched.Name; - _buildVer = (string)jsonRoot["name"]; - - foreach (JToken asset in assets) + foreach (var asset in fetched.Assets) { - string assetName = (string)asset["name"]; - string assetState = (string)asset["state"]; - string downloadURL = (string)asset["browser_download_url"]; - - if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt)) + if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt)) { - _buildUrl = downloadURL; + _buildUrl = asset.BrowserDownloadUrl; - if (assetState != "uploaded") + if (asset.State != "uploaded") { if (showVersionUpToDate) { diff --git a/Ryujinx.Ava/UI/Models/Amiibo.cs b/Ryujinx.Ava/UI/Models/Amiibo.cs deleted file mode 100644 index d0ccafd08..000000000 --- a/Ryujinx.Ava/UI/Models/Amiibo.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ava.UI.Models -{ - public class Amiibo - { - public struct AmiiboJson - { - [JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; } - [JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; } - } - - public struct AmiiboApi - { - [JsonPropertyName("name")] public string Name { get; set; } - [JsonPropertyName("head")] public string Head { get; set; } - [JsonPropertyName("tail")] public string Tail { get; set; } - [JsonPropertyName("image")] public string Image { get; set; } - [JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; } - [JsonPropertyName("character")] public string Character { get; set; } - [JsonPropertyName("gameSeries")] public string GameSeries { get; set; } - [JsonPropertyName("type")] public string Type { get; set; } - - [JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; } - - [JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } - - public override string ToString() - { - return Name; - } - - public string GetId() - { - return Head + Tail; - } - - public override bool Equals(object obj) - { - if (obj is AmiiboApi amiibo) - { - return amiibo.Head + amiibo.Tail == Head + Tail; - } - - return false; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - } - - public class AmiiboApiGamesSwitch - { - [JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; } - - [JsonPropertyName("gameID")] public List<string> GameId { get; set; } - - [JsonPropertyName("gameName")] public string GameName { get; set; } - } - - public class AmiiboApiUsage - { - [JsonPropertyName("Usage")] public string Usage { get; set; } - - [JsonPropertyName("write")] public bool Write { get; set; } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs index 872c1a37f..9b5422adf 100644 --- a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs @@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels { string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n"; + Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray) + "\n\n"); } catch { diff --git a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index 5311318c5..090c13a97 100644 --- a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -4,11 +4,11 @@ using Avalonia.Media.Imaging; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; +using Ryujinx.Ui.Common.Models.Amiibo; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -17,6 +17,7 @@ using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; +using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ava.UI.ViewModels { @@ -31,8 +32,8 @@ namespace Ryujinx.Ava.UI.ViewModels private readonly StyleableWindow _owner; private Bitmap _amiiboImage; - private List<Amiibo.AmiiboApi> _amiiboList; - private AvaloniaList<Amiibo.AmiiboApi> _amiibos; + private List<AmiiboApi> _amiiboList; + private AvaloniaList<AmiiboApi> _amiibos; private ObservableCollection<string> _amiiboSeries; private int _amiiboSelectedIndex; @@ -41,6 +42,8 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _showAllAmiibo; private bool _useRandomUuid; private string _usage; + + private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) { @@ -52,9 +55,9 @@ namespace Ryujinx.Ava.UI.ViewModels Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); _amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); - _amiiboList = new List<Amiibo.AmiiboApi>(); + _amiiboList = new List<AmiiboApi>(); _amiiboSeries = new ObservableCollection<string>(); - _amiibos = new AvaloniaList<Amiibo.AmiiboApi>(); + _amiibos = new AvaloniaList<AmiiboApi>(); _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png"); @@ -94,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public AvaloniaList<Amiibo.AmiiboApi> AmiiboList + public AvaloniaList<AmiiboApi> AmiiboList { get => _amiibos; set @@ -187,9 +190,9 @@ namespace Ryujinx.Ava.UI.ViewModels if (File.Exists(_amiiboJsonPath)) { - amiiboJsonString = File.ReadAllText(_amiiboJsonPath); + amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -206,7 +209,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - _amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo; + _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); ParseAmiiboData(); @@ -223,7 +226,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!ShowAllAmiibo) { - foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) + foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) { if (game != null) { @@ -255,7 +258,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void SelectLastScannedAmiibo() { - Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); + AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries); AmiiboSelectedIndex = AmiiboList.IndexOf(scanned); @@ -270,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList + List<AmiiboApi> amiiboSortedList = _amiiboList .Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex]) .OrderBy(amiibo => amiibo.Name).ToList(); @@ -280,7 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!_showAllAmiibo) { - foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) + foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) { if (game != null) { @@ -314,7 +317,7 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; + AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image; @@ -326,11 +329,11 @@ namespace Ryujinx.Ava.UI.ViewModels { bool writable = false; - foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch) + foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch) { if (item.GameId.Contains(TitleId)) { - foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage) + foreach (AmiiboApiUsage usageItem in item.AmiiboUsage) { usageString += Environment.NewLine + $"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"; diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index 35256b3b5..dd261b103 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -51,6 +51,8 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _isLoaded; private readonly UserControl _owner; + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public IGamepadDriver AvaloniaKeyboardDriver { get; } public IGamepad SelectedGamepad { get; private set; } @@ -706,10 +708,7 @@ namespace Ryujinx.Ava.UI.ViewModels try { - using (Stream stream = File.OpenRead(path)) - { - config = JsonHelper.Deserialize<InputConfig>(stream); - } + config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); } catch (JsonException) { } catch (InvalidOperationException) @@ -775,7 +774,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.ControllerType = Controllers[_controller].Type; - string jsonString = JsonHelper.Serialize(config, true); + string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig); await File.WriteAllTextAsync(path, jsonString); diff --git a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs index e5e4f66b5..1d7da9a40 100644 --- a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -21,7 +21,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; using Path = System.IO.Path; @@ -41,6 +40,8 @@ namespace Ryujinx.Ava.UI.ViewModels private ulong _titleId; private string _titleName; + private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public AvaloniaList<DownloadableContentModel> DownloadableContents { get => _downloadableContents; @@ -100,7 +101,7 @@ namespace Ryujinx.Ava.UI.ViewModels try { - _downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath); + _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer); } catch { @@ -330,10 +331,7 @@ namespace Ryujinx.Ava.UI.ViewModels _downloadableContentContainerList.Add(container); } - using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) - { - downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true))); - } + JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer); } } diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index dd9e1b961..ed5b5eacf 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -25,226 +25,228 @@ using System.Text; using Path = System.IO.Path; using SpanHelpers = LibHac.Common.SpanHelpers; -namespace Ryujinx.Ava.UI.ViewModels; - -public class TitleUpdateViewModel : BaseModel +namespace Ryujinx.Ava.UI.ViewModels { - public TitleUpdateMetadata _titleUpdateWindowData; - public readonly string _titleUpdateJsonPath; - private VirtualFileSystem _virtualFileSystem { get; } - private ulong _titleId { get; } - private string _titleName { get; } - - private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); - private AvaloniaList<object> _views = new(); - private object _selectedUpdate; - - public AvaloniaList<TitleUpdateModel> TitleUpdates + public class TitleUpdateViewModel : BaseModel { - get => _titleUpdates; - set + public TitleUpdateMetadata _titleUpdateWindowData; + public readonly string _titleUpdateJsonPath; + private VirtualFileSystem _virtualFileSystem { get; } + private ulong _titleId { get; } + private string _titleName { get; } + + private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); + private AvaloniaList<object> _views = new(); + private object _selectedUpdate; + + private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + public AvaloniaList<TitleUpdateModel> TitleUpdates { - _titleUpdates = value; - OnPropertyChanged(); - } - } - - public AvaloniaList<object> Views - { - get => _views; - set - { - _views = value; - OnPropertyChanged(); - } - } - - public object SelectedUpdate - { - get => _selectedUpdate; - set - { - _selectedUpdate = value; - OnPropertyChanged(); - } - } - - public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) - { - _virtualFileSystem = virtualFileSystem; - - _titleId = titleId; - _titleName = titleName; - - _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); - - try - { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath); - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); - - _titleUpdateWindowData = new TitleUpdateMetadata + get => _titleUpdates; + set { - Selected = "", - Paths = new List<string>() - }; - - Save(); - } - - LoadUpdates(); - } - - private void LoadUpdates() - { - foreach (string path in _titleUpdateWindowData.Paths) - { - AddUpdate(path); - } - - TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); - - SelectedUpdate = selected; - - // NOTE: Save the list again to remove leftovers. - Save(); - - SortUpdates(); - } - - public void SortUpdates() - { - var list = TitleUpdates.ToList(); - - list.Sort((first, second) => - { - if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) - { - return -1; - } - else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) - { - return 1; - } - - return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; - }); - - Views.Clear(); - Views.Add(new BaseModel()); - Views.AddRange(list); - - if (SelectedUpdate == null) - { - SelectedUpdate = Views[0]; - } - else if (!TitleUpdates.Contains(SelectedUpdate)) - { - if (Views.Count > 1) - { - SelectedUpdate = Views[1]; - } - else - { - SelectedUpdate = Views[0]; + _titleUpdates = value; + OnPropertyChanged(); } } - } - private void AddUpdate(string path) - { - if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) + public AvaloniaList<object> Views { - using FileStream file = new(path, FileMode.Open, FileAccess.Read); + get => _views; + set + { + _views = value; + OnPropertyChanged(); + } + } + + public object SelectedUpdate + { + get => _selectedUpdate; + set + { + _selectedUpdate = value; + OnPropertyChanged(); + } + } + + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + _virtualFileSystem = virtualFileSystem; + + _titleId = titleId; + _titleName = titleName; + + _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); try { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata); + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); - if (controlNca != null && patchNca != null) + _titleUpdateWindowData = new TitleUpdateMetadata { - ApplicationControlProperty controlData = new(); + Selected = "", + Paths = new List<string>() + }; - using UniqueRef<IFile> nacpFile = new(); + Save(); + } - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + LoadUpdates(); + } - TitleUpdates.Add(new TitleUpdateModel(controlData, path)); + private void LoadUpdates() + { + foreach (string path in _titleUpdateWindowData.Paths) + { + AddUpdate(path); + } + + TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); + + SelectedUpdate = selected; + + // NOTE: Save the list again to remove leftovers. + Save(); + SortUpdates(); + } + + public void SortUpdates() + { + var list = TitleUpdates.ToList(); + + list.Sort((first, second) => + { + if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) + { + return -1; + } + else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) + { + return 1; + } + + return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; + }); + + Views.Clear(); + Views.Add(new BaseModel()); + Views.AddRange(list); + + if (SelectedUpdate == null) + { + SelectedUpdate = Views[0]; + } + else if (!TitleUpdates.Contains(SelectedUpdate)) + { + if (Views.Count > 1) + { + SelectedUpdate = Views[1]; } else + { + SelectedUpdate = Views[0]; + } + } + } + + private void AddUpdate(string path) + { + if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) + { + using FileStream file = new(path, FileMode.Open, FileAccess.Read); + + try + { + (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + + if (controlNca != null && patchNca != null) + { + ApplicationControlProperty controlData = new(); + + using UniqueRef<IFile> nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + TitleUpdates.Add(new TitleUpdateModel(controlData, path)); + } + else + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); + }); + } + } + catch (Exception ex) { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)); }); } } - catch (Exception ex) - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)); - }); - } } - } - public void RemoveUpdate(TitleUpdateModel update) - { - TitleUpdates.Remove(update); - - SortUpdates(); - } - - public async void Add() - { - OpenFileDialog dialog = new() + public void RemoveUpdate(TitleUpdateModel update) { - Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], - AllowMultiple = true - }; + TitleUpdates.Remove(update); - dialog.Filters.Add(new FileDialogFilter + SortUpdates(); + } + + public async void Add() { - Name = "NSP", - Extensions = { "nsp" } - }); - - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - string[] files = await dialog.ShowAsync(desktop.MainWindow); - - if (files != null) + OpenFileDialog dialog = new() { - foreach (string file in files) + Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], + AllowMultiple = true + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = "NSP", + Extensions = { "nsp" } + }); + + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null) { - AddUpdate(file); + foreach (string file in files) + { + AddUpdate(file); + } } } + + SortUpdates(); } - SortUpdates(); - } - - public void Save() - { - _titleUpdateWindowData.Paths.Clear(); - _titleUpdateWindowData.Selected = ""; - - foreach (TitleUpdateModel update in TitleUpdates) + public void Save() { - _titleUpdateWindowData.Paths.Add(update.Path); + _titleUpdateWindowData.Paths.Clear(); + _titleUpdateWindowData.Selected = ""; - if (update == SelectedUpdate) + foreach (TitleUpdateModel update in TitleUpdates) { - _titleUpdateWindowData.Selected = update.Path; - } - } + _titleUpdateWindowData.Paths.Add(update.Path); - File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); + if (update == SelectedUpdate) + { + _titleUpdateWindowData.Selected = update.Path; + } + } + + JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); + } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 1c6f4265c..51c71c378 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Main { string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last(); string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}"); - var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); + var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); if (!strings.TryGetValue("Language", out string languageName)) { diff --git a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs index 5368a1333..206d0a7ea 100644 --- a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs @@ -1,7 +1,7 @@ using Avalonia.Interactivity; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ui.Common.Models.Amiibo; namespace Ryujinx.Ava.UI.Windows { @@ -35,14 +35,14 @@ namespace Ryujinx.Ava.UI.Windows } public bool IsScanned { get; set; } - public Amiibo.AmiiboApi ScannedAmiibo { get; set; } + public AmiiboApi ScannedAmiibo { get; set; } public AmiiboWindowViewModel ViewModel { get; set; } private void ScanButton_Click(object sender, RoutedEventArgs e) { if (ViewModel.AmiiboSelectedIndex > -1) { - Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex]; + AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex]; ScannedAmiibo = amiibo; IsScanned = true; Close(); diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 1b50c46f3..153ce95d2 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -6,11 +6,8 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.Common.Helper; -using System.IO; -using System.Text; using System.Threading.Tasks; using Button = Avalonia.Controls.Button; diff --git a/Ryujinx.Common/Configuration/AspectRatioExtensions.cs b/Ryujinx.Common/Configuration/AspectRatioExtensions.cs index 3d0be88e9..5e97ed19c 100644 --- a/Ryujinx.Common/Configuration/AspectRatioExtensions.cs +++ b/Ryujinx.Common/Configuration/AspectRatioExtensions.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))] public enum AspectRatio { Fixed4x3, diff --git a/Ryujinx.Common/Configuration/BackendThreading.cs b/Ryujinx.Common/Configuration/BackendThreading.cs index cfc089146..8833b3f07 100644 --- a/Ryujinx.Common/Configuration/BackendThreading.cs +++ b/Ryujinx.Common/Configuration/BackendThreading.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))] public enum BackendThreading { Auto, diff --git a/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs b/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs new file mode 100644 index 000000000..132c45a44 --- /dev/null +++ b/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(List<DownloadableContentContainer>))] + public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/GraphicsBackend.cs b/Ryujinx.Common/Configuration/GraphicsBackend.cs index 26e4a28a9..d74dd6e19 100644 --- a/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))] public enum GraphicsBackend { Vulkan, diff --git a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs index 556af689a..ad12302a6 100644 --- a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs +++ b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))] public enum GraphicsDebugLevel { None, diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs index d1c2e4e81..2b9e0af42 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.Common.Utilities; +using System; using System.Text.Json; using System.Text.Json.Serialization; @@ -6,6 +7,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController> { + private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader) { // Temporary reader to get the backend type @@ -52,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion return motionBackendType switch { - MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options), - MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options), + MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController), + MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController), _ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"), }; } @@ -63,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion switch (value.MotionBackend) { case MotionInputBackendType.GamepadDriver: - JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options); + JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController); break; case MotionInputBackendType.CemuHook: - JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options); + JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController); break; default: throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}"); diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs index 832aae0d1..7636aa414 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs @@ -1,5 +1,8 @@ -namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { + [JsonConverter(typeof(JsonMotionConfigControllerConverter))] public class MotionConfigController { public MotionInputBackendType MotionBackend { get; set; } diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs new file mode 100644 index 000000000..5cd9e452b --- /dev/null +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(MotionConfigController))] + [JsonSerializable(typeof(CemuHookMotionConfigController))] + [JsonSerializable(typeof(StandardMotionConfigController))] + public partial class MotionConfigJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs index 45d654edc..c65510478 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { + [JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))] public enum MotionInputBackendType : byte { Invalid, diff --git a/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/Ryujinx.Common/Configuration/Hid/ControllerType.cs index 0ad01bbb6..70f811c89 100644 --- a/Ryujinx.Common/Configuration/Hid/ControllerType.cs +++ b/Ryujinx.Common/Configuration/Hid/ControllerType.cs @@ -1,9 +1,12 @@ +using Ryujinx.Common.Utilities; using System; +using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { - [Flags] // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical + [Flags] + [JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))] public enum ControllerType : int { None, diff --git a/Ryujinx.Common/Configuration/Hid/InputBackendType.cs b/Ryujinx.Common/Configuration/Hid/InputBackendType.cs index 9e944f9e8..1db3f5703 100644 --- a/Ryujinx.Common/Configuration/Hid/InputBackendType.cs +++ b/Ryujinx.Common/Configuration/Hid/InputBackendType.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration.Hid +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid { + [JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))] public enum InputBackendType { Invalid, diff --git a/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/Ryujinx.Common/Configuration/Hid/InputConfig.cs index 3364e35fa..16c8f8e32 100644 --- a/Ryujinx.Common/Configuration/Hid/InputConfig.cs +++ b/Ryujinx.Common/Configuration/Hid/InputConfig.cs @@ -1,8 +1,10 @@ using System.ComponentModel; using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { + [JsonConverter(typeof(JsonInputConfigConverter))] public class InputConfig : INotifyPropertyChanged { /// <summary> diff --git a/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs b/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs new file mode 100644 index 000000000..254c4feb4 --- /dev/null +++ b/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs @@ -0,0 +1,14 @@ +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(InputConfig))] + [JsonSerializable(typeof(StandardKeyboardInputConfig))] + [JsonSerializable(typeof(StandardControllerInputConfig))] + public partial class InputConfigJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs b/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs index 7223ad451..08bbcbf17 100644 --- a/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs +++ b/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs @@ -1,13 +1,16 @@ using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Utilities; using System; using System.Text.Json; using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { - class JsonInputConfigConverter : JsonConverter<InputConfig> + public class JsonInputConfigConverter : JsonConverter<InputConfig> { + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader) { // Temporary reader to get the backend type @@ -54,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid return backendType switch { - InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options), - InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options), + InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig), + InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig), _ => throw new InvalidOperationException($"Unknown backend type {backendType}"), }; } @@ -65,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid switch (value.Backend) { case InputBackendType.WindowKeyboard: - JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options); + JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig); break; case InputBackendType.GamepadSDL2: - JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options); + JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig); break; default: throw new ArgumentException($"Unknown backend type {value.Backend}"); diff --git a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs index 2e34cb96c..dd6495d4d 100644 --- a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs +++ b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs @@ -1,6 +1,10 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Configuration.Hid { // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical + [JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))] public enum PlayerIndex : int { Player1 = 0, diff --git a/Ryujinx.Common/Configuration/MemoryManagerMode.cs b/Ryujinx.Common/Configuration/MemoryManagerMode.cs index ad6c2a346..f10fd6f1b 100644 --- a/Ryujinx.Common/Configuration/MemoryManagerMode.cs +++ b/Ryujinx.Common/Configuration/MemoryManagerMode.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))] public enum MemoryManagerMode : byte { SoftwarePageTable, diff --git a/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs b/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs new file mode 100644 index 000000000..5b661b878 --- /dev/null +++ b/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(TitleUpdateMetadata))] + public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs index b9a08323e..28a7d5461 100644 --- a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs +++ b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs @@ -1,22 +1,20 @@ -using System; -using System.Reflection; -using System.Text; +using System.Text; namespace Ryujinx.Common.Logging { internal class DefaultLogFormatter : ILogFormatter { - private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>(); + private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); public string Format(LogEventArgs args) { - StringBuilder sb = _stringBuilderPool.Allocate(); + StringBuilder sb = StringBuilderPool.Allocate(); try { sb.Clear(); - sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time); + sb.Append($@"{args.Time:hh\:mm\:ss\.fff}"); sb.Append($" |{args.Level.ToString()[0]}| "); if (args.ThreadName != null) @@ -27,53 +25,17 @@ namespace Ryujinx.Common.Logging sb.Append(args.Message); - if (args.Data != null) + if (args.Data is not null) { - PropertyInfo[] props = args.Data.GetType().GetProperties(); - - sb.Append(" {"); - - foreach (var prop in props) - { - sb.Append(prop.Name); - sb.Append(": "); - - if (typeof(Array).IsAssignableFrom(prop.PropertyType)) - { - Array array = (Array)prop.GetValue(args.Data); - foreach (var item in array) - { - sb.Append(item.ToString()); - sb.Append(", "); - } - - if (array.Length > 0) - { - sb.Remove(sb.Length - 2, 2); - } - } - else - { - sb.Append(prop.GetValue(args.Data)); - } - - sb.Append(" ; "); - } - - // We remove the final ';' from the string - if (props.Length > 0) - { - sb.Remove(sb.Length - 3, 3); - } - - sb.Append('}'); + sb.Append(' '); + DynamicObjectFormatter.Format(sb, args.Data); } return sb.ToString(); } finally { - _stringBuilderPool.Release(sb); + StringBuilderPool.Release(sb); } } } diff --git a/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs b/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs new file mode 100644 index 000000000..5f15cc2a6 --- /dev/null +++ b/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs @@ -0,0 +1,84 @@ +#nullable enable +using System; +using System.Reflection; +using System.Text; + +namespace Ryujinx.Common.Logging +{ + internal class DynamicObjectFormatter + { + private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); + + public static string? Format(object? dynamicObject) + { + if (dynamicObject is null) + { + return null; + } + + StringBuilder sb = StringBuilderPool.Allocate(); + + try + { + Format(sb, dynamicObject); + + return sb.ToString(); + } + finally + { + StringBuilderPool.Release(sb); + } + } + + public static void Format(StringBuilder sb, object? dynamicObject) + { + if (dynamicObject is null) + { + return; + } + + PropertyInfo[] props = dynamicObject.GetType().GetProperties(); + + sb.Append('{'); + + foreach (var prop in props) + { + sb.Append(prop.Name); + sb.Append(": "); + + if (typeof(Array).IsAssignableFrom(prop.PropertyType)) + { + Array? array = (Array?) prop.GetValue(dynamicObject); + + if (array is not null) + { + foreach (var item in array) + { + sb.Append(item); + sb.Append(", "); + } + + if (array.Length > 0) + { + sb.Remove(sb.Length - 2, 2); + } + } + } + else + { + sb.Append(prop.GetValue(dynamicObject)); + } + + sb.Append(" ; "); + } + + // We remove the final ';' from the string + if (props.Length > 0) + { + sb.Remove(sb.Length - 3, 3); + } + + sb.Append('}'); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 7e53c972b..e62676cd3 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Logging { + [JsonConverter(typeof(TypedStringEnumConverter<LogClass>))] public enum LogClass { Application, diff --git a/Ryujinx.Common/Logging/LogEventArgs.cs b/Ryujinx.Common/Logging/LogEventArgs.cs index 511c8e6e2..a27af7809 100644 --- a/Ryujinx.Common/Logging/LogEventArgs.cs +++ b/Ryujinx.Common/Logging/LogEventArgs.cs @@ -11,15 +11,7 @@ namespace Ryujinx.Common.Logging public readonly string Message; public readonly object Data; - public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message) - { - Level = level; - Time = time; - ThreadName = threadName; - Message = message; - } - - public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data) + public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null) { Level = level; Time = time; diff --git a/Ryujinx.Common/Logging/LogEventArgsJson.cs b/Ryujinx.Common/Logging/LogEventArgsJson.cs new file mode 100644 index 000000000..425b97662 --- /dev/null +++ b/Ryujinx.Common/Logging/LogEventArgsJson.cs @@ -0,0 +1,30 @@ +using System; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Logging +{ + internal class LogEventArgsJson + { + public LogLevel Level { get; } + public TimeSpan Time { get; } + public string ThreadName { get; } + + public string Message { get; } + public string Data { get; } + + [JsonConstructor] + public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null) + { + Level = level; + Time = time; + ThreadName = threadName; + Message = message; + Data = data; + } + + public static LogEventArgsJson FromLogEventArgs(LogEventArgs args) + { + return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs b/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs new file mode 100644 index 000000000..da21f11e8 --- /dev/null +++ b/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Logging +{ + [JsonSerializable(typeof(LogEventArgsJson))] + internal partial class LogEventJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogLevel.cs b/Ryujinx.Common/Logging/LogLevel.cs index 8857fb45a..3786c7561 100644 --- a/Ryujinx.Common/Logging/LogLevel.cs +++ b/Ryujinx.Common/Logging/LogLevel.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Logging { + [JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))] public enum LogLevel { Debug, diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs index 95f96576c..06976433e 100644 --- a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs +++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -1,5 +1,5 @@ -using System.IO; -using System.Text.Json; +using Ryujinx.Common.Utilities; +using System.IO; namespace Ryujinx.Common.Logging { @@ -25,12 +25,8 @@ namespace Ryujinx.Common.Logging public void Log(object sender, LogEventArgs e) { - string text = JsonSerializer.Serialize(e); - - using (BinaryWriter writer = new BinaryWriter(_stream)) - { - writer.Write(text); - } + var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e); + JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson); } public void Dispose() diff --git a/Ryujinx.Common/Utilities/CommonJsonContext.cs b/Ryujinx.Common/Utilities/CommonJsonContext.cs new file mode 100644 index 000000000..d7b3f78cd --- /dev/null +++ b/Ryujinx.Common/Utilities/CommonJsonContext.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Utilities +{ + [JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")] + [JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")] + public partial class CommonJsonContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Utilities/JsonHelper.cs b/Ryujinx.Common/Utilities/JsonHelper.cs index 36f391149..9a2d6f181 100644 --- a/Ryujinx.Common/Utilities/JsonHelper.cs +++ b/Ryujinx.Common/Utilities/JsonHelper.cs @@ -1,15 +1,62 @@ -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using System.IO; +using System.IO; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; namespace Ryujinx.Common.Utilities { public class JsonHelper { - public static JsonNamingPolicy SnakeCase { get; } + private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy(); + private const int DefaultFileWriteBufferSize = 4096; + + /// <summary> + /// Creates new serializer options with default settings. + /// </summary> + /// <remarks> + /// It is REQUIRED for you to save returned options statically or as a part of static serializer context + /// in order to avoid performance issues. You can safely modify returned options for your case before storing. + /// </remarks> + public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) + { + JsonSerializerOptions options = new() + { + DictionaryKeyPolicy = SnakeCasePolicy, + PropertyNamingPolicy = SnakeCasePolicy, + WriteIndented = indented, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + + return options; + } + + public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo) + { + return JsonSerializer.Serialize(value, typeInfo); + } + + public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo) + { + return JsonSerializer.Deserialize(value, typeInfo); + } + + public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo) + { + using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough); + JsonSerializer.Serialize(file, value, typeInfo); + } + + public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo) + { + using FileStream file = File.OpenRead(filePath); + return JsonSerializer.Deserialize(file, typeInfo); + } + + public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo) + { + JsonSerializer.Serialize(stream, value, typeInfo); + } private class SnakeCaseNamingPolicy : JsonNamingPolicy { @@ -20,7 +67,7 @@ namespace Ryujinx.Common.Utilities return name; } - StringBuilder builder = new StringBuilder(); + StringBuilder builder = new(); for (int i = 0; i < name.Length; i++) { @@ -34,7 +81,7 @@ namespace Ryujinx.Common.Utilities } else { - builder.Append("_"); + builder.Append('_'); builder.Append(char.ToLowerInvariant(c)); } } @@ -47,64 +94,5 @@ namespace Ryujinx.Common.Utilities return builder.ToString(); } } - - static JsonHelper() - { - SnakeCase = new SnakeCaseNamingPolicy(); - } - - public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false) - { - JsonSerializerOptions options = new JsonSerializerOptions - { - DictionaryKeyPolicy = SnakeCase, - PropertyNamingPolicy = SnakeCase, - WriteIndented = prettyPrint, - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip - }; - - options.Converters.Add(new JsonStringEnumConverter()); - options.Converters.Add(new JsonInputConfigConverter()); - options.Converters.Add(new JsonMotionConfigControllerConverter()); - - return options; - } - - public static T Deserialize<T>(Stream stream) - { - using (BinaryReader reader = new BinaryReader(stream)) - { - return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions()); - } - } - - public static T DeserializeFromFile<T>(string path) - { - return Deserialize<T>(File.ReadAllText(path)); - } - - public static T Deserialize<T>(string json) - { - return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions()); - } - - public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false) - { - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.Write(SerializeToUtf8Bytes(obj, prettyPrint)); - } - } - - public static string Serialize<TValue>(TValue obj, bool prettyPrint = false) - { - return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint)); - } - - public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false) - { - return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint)); - } } -} +} \ No newline at end of file diff --git a/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs b/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs new file mode 100644 index 000000000..c0127dc4a --- /dev/null +++ b/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs @@ -0,0 +1,34 @@ +#nullable enable +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Utilities +{ + /// <summary> + /// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs + /// </summary> + /// <remarks> + /// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>. + /// Get rid of this converter if dotnet supports similar functionality out of the box. + /// </remarks> + /// <typeparam name="TEnum">Type of enum to serialize</typeparam> + public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum + { + public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var enumValue = reader.GetString(); + if (string.IsNullOrEmpty(enumValue)) + { + return default; + } + + return Enum.Parse<TEnum>(enumValue); + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } + } +} diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 82bd9b312..7d06e5eb7 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -13,6 +13,7 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.Loaders.Executables; @@ -24,14 +25,13 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Text.Json; using static Ryujinx.HLE.HOS.ModLoader; using ApplicationId = LibHac.Ncm.ApplicationId; using Path = System.IO.Path; namespace Ryujinx.HLE.HOS { - using JsonHelper = Common.Utilities.JsonHelper; - public class ApplicationLoader { // Binaries from exefs are loaded into mem in this order. Do not change. @@ -57,6 +57,10 @@ namespace Ryujinx.HLE.HOS private string _displayVersion; private BlitStruct<ApplicationControlProperty> _controlData; + private static readonly JsonSerializerOptions SerializerOptions = JsonHelper.GetDefaultSerializerOptions(); + private static readonly DownloadableContentJsonSerializerContext ContentSerializerContext = new(SerializerOptions); + private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(SerializerOptions); + public BlitStruct<ApplicationControlProperty> ControlData => _controlData; public string TitleName => _titleName; public string DisplayVersion => _displayVersion; @@ -197,7 +201,7 @@ namespace Ryujinx.HLE.HOS if (File.Exists(titleUpdateMetadataPath)) { - updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; + updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; if (File.Exists(updatePath)) { @@ -411,7 +415,7 @@ namespace Ryujinx.HLE.HOS if (File.Exists(titleAocMetadataPath)) { - List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath); + List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(titleAocMetadataPath, ContentSerializerContext.ListDownloadableContentContainer); foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs index ec0b0a10b..535779d2e 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -1,11 +1,11 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.Services.Account.Acc.Types; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; -using System.Text.Json.Serialization; namespace Ryujinx.HLE.HOS.Services.Account.Acc { @@ -13,29 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); - private struct ProfilesJson - { - [JsonPropertyName("profiles")] - public List<UserProfileJson> Profiles { get; set; } - [JsonPropertyName("last_opened")] - public string LastOpened { get; set; } - } - - private struct UserProfileJson - { - [JsonPropertyName("user_id")] - public string UserId { get; set; } - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonPropertyName("account_state")] - public AccountState AccountState { get; set; } - [JsonPropertyName("online_play_state")] - public AccountState OnlinePlayState { get; set; } - [JsonPropertyName("last_modified_timestamp")] - public long LastModifiedTimestamp { get; set; } - [JsonPropertyName("image")] - public byte[] Image { get; set; } - } + private static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public UserId LastOpened { get; set; } @@ -47,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { try { - ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath); + ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson); foreach (var profile in profilesJson.Profiles) { @@ -92,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc }); } - File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true)); + JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs new file mode 100644 index 000000000..6b54898e5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs @@ -0,0 +1,11 @@ +using Ryujinx.HLE.HOS.Services.Account.Acc.Types; +using System.Text.Json.Serialization; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ProfilesJson))] + internal partial class ProfilesJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs index 2382a2554..1699abfbd 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.HLE.HOS.Services.Account.Acc { + [JsonConverter(typeof(TypedStringEnumConverter<AccountState>))] public enum AccountState { Closed, diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs new file mode 100644 index 000000000..09f9d1421 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types +{ + internal struct ProfilesJson + { + public List<UserProfileJson> Profiles { get; set; } + public string LastOpened { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs new file mode 100644 index 000000000..06ff4833f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types +{ + internal struct UserProfileJson + { + public string UserId { get; set; } + public string Name { get; set; } + public AccountState AccountState { get; set; } + public AccountState OnlinePlayState { get; set; } + public long LastModifiedTimestamp { get; set; } + public byte[] Image { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs new file mode 100644 index 000000000..e75f62004 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; +using System.Text.Json.Serialization; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp +{ + [JsonSerializable(typeof(VirtualAmiiboFile))] + internal partial class AmiiboJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 4fdeadcb2..9166e87fa 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Mii.Types; @@ -8,8 +9,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Text.Json; namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { @@ -17,6 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { private static uint _openedApplicationAreaId; + private static readonly AmiiboJsonSerializerContext SerializerContext = AmiiboJsonSerializerContext.Default; + public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid) { if (useRandomUuid) @@ -173,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp if (File.Exists(filePath)) { - virtualAmiiboFile = JsonSerializer.Deserialize<VirtualAmiiboFile>(File.ReadAllText(filePath), new JsonSerializerOptions(JsonSerializerDefaults.General)); + virtualAmiiboFile = JsonHelper.DeserializeFromFile(filePath, SerializerContext.VirtualAmiiboFile); } else { @@ -197,8 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile) { string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"); - - File.WriteAllText(filePath, JsonSerializer.Serialize(virtualAmiiboFile)); + JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, SerializerContext.VirtualAmiiboFile); } } } diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index f618e38d6..40eec4a73 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -56,6 +56,8 @@ namespace Ryujinx.Headless.SDL2 private static bool _enableKeyboard; private static bool _enableMouse; + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + static void Main(string[] args) { Version = ReleaseInformation.GetVersion(); @@ -285,10 +287,7 @@ namespace Ryujinx.Headless.SDL2 try { - using (Stream stream = File.OpenRead(path)) - { - config = JsonHelper.Deserialize<InputConfig>(stream); - } + config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); } catch (JsonException) { diff --git a/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs b/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs new file mode 100644 index 000000000..f81121c28 --- /dev/null +++ b/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.App.Common +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ApplicationMetadata))] + internal partial class ApplicationJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 43510d5ec..add6dad3f 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -10,6 +10,7 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.SystemState; @@ -22,7 +23,6 @@ using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; using Path = System.IO.Path; namespace Ryujinx.Ui.App.Common @@ -42,6 +42,8 @@ namespace Ryujinx.Ui.App.Common private Language _desiredTitleLanguage; private CancellationTokenSource _cancellationToken; + private static readonly ApplicationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public ApplicationLibrary(VirtualFileSystem virtualFileSystem) { _virtualFileSystem = virtualFileSystem; @@ -490,14 +492,12 @@ namespace Ryujinx.Ui.App.Common appMetadata = new ApplicationMetadata(); - using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); - - JsonHelper.Serialize(stream, appMetadata, true); + JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); } try { - appMetadata = JsonHelper.DeserializeFromFile<ApplicationMetadata>(metadataFile); + appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata); } catch (JsonException) { @@ -510,9 +510,7 @@ namespace Ryujinx.Ui.App.Common { modifyFunction(appMetadata); - using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); - - JsonHelper.Serialize(stream, appMetadata, true); + JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); } return appMetadata; diff --git a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs index 99111ea64..1f9bd0baf 100644 --- a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs +++ b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<AudioBackend>))] public enum AudioBackend { Dummy, diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index e9aec04b2..14c03957a 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -5,7 +5,7 @@ using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.Ui; using System.Collections.Generic; -using System.IO; +using System.Text.Json.Nodes; namespace Ryujinx.Ui.Common.Configuration { @@ -321,14 +321,14 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> /// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks> /// TODO: Remove this when those older versions aren't in use anymore. - public List<object> KeyboardConfig { get; set; } + public List<JsonObject> KeyboardConfig { get; set; } /// <summary> /// Legacy controller control bindings /// </summary> /// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks> /// TODO: Remove this when those older versions aren't in use anymore. - public List<object> ControllerConfig { get; set; } + public List<JsonObject> ControllerConfig { get; set; } /// <summary> /// Input configurations @@ -354,11 +354,12 @@ namespace Ryujinx.Ui.Common.Configuration /// Loads a configuration file from disk /// </summary> /// <param name="path">The path to the JSON configuration file</param> + /// <param name="configurationFileFormat">Parsed configuration file</param> public static bool TryLoad(string path, out ConfigurationFileFormat configurationFileFormat) { try { - configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path); + configurationFileFormat = JsonHelper.DeserializeFromFile(path, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); return configurationFileFormat.Version != 0; } @@ -376,8 +377,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <param name="path">The path to the JSON configuration file</param> public void SaveConfig(string path) { - using FileStream fileStream = File.Create(path, 4096, FileOptions.WriteThrough); - JsonHelper.Serialize(fileStream, this, true); + JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); } } } diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs new file mode 100644 index 000000000..6ce2ef01a --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs @@ -0,0 +1,9 @@ +using Ryujinx.Common.Utilities; + +namespace Ryujinx.Ui.Common.Configuration +{ + internal static class ConfigurationFileFormatSettings + { + public static readonly ConfigurationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs new file mode 100644 index 000000000..bb8dfb499 --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ConfigurationFileFormat))] + internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index bcdd2e70a..82a331c16 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -9,6 +9,7 @@ using Ryujinx.Ui.Common.Configuration.Ui; using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; +using System.Text.Json.Nodes; namespace Ryujinx.Ui.Common.Configuration { @@ -631,8 +632,8 @@ namespace Ryujinx.Ui.Common.Configuration EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, Hotkeys = Hid.Hotkeys, - KeyboardConfig = new List<object>(), - ControllerConfig = new List<object>(), + KeyboardConfig = new List<JsonObject>(), + ControllerConfig = new List<JsonObject>(), InputConfig = Hid.InputConfig, GraphicsBackend = Graphics.GraphicsBackend, PreferredGpu = Graphics.PreferredGpu diff --git a/Ryujinx.Ui.Common/Configuration/System/Language.cs b/Ryujinx.Ui.Common/Configuration/System/Language.cs index 3d2dc9914..404f8063d 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Language.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Language.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration.System +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration.System { + [JsonConverter(typeof(TypedStringEnumConverter<Language>))] public enum Language { Japanese, diff --git a/Ryujinx.Ui.Common/Configuration/System/Region.cs b/Ryujinx.Ui.Common/Configuration/System/Region.cs index fb51e08e5..7dfac6388 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Region.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Region.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration.System +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration.System { + [JsonConverter(typeof(TypedStringEnumConverter<Region>))] public enum Region { Japan, diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs new file mode 100644 index 000000000..f412b9504 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public struct AmiiboApi : IEquatable<AmiiboApi> + { + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("head")] + public string Head { get; set; } + [JsonPropertyName("tail")] + public string Tail { get; set; } + [JsonPropertyName("image")] + public string Image { get; set; } + [JsonPropertyName("amiiboSeries")] + public string AmiiboSeries { get; set; } + [JsonPropertyName("character")] + public string Character { get; set; } + [JsonPropertyName("gameSeries")] + public string GameSeries { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("release")] + public Dictionary<string, string> Release { get; set; } + + [JsonPropertyName("gamesSwitch")] + public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } + + public override string ToString() + { + return Name; + } + + public string GetId() + { + return Head + Tail; + } + + public bool Equals(AmiiboApi other) + { + return Head + Tail == other.Head + other.Tail; + } + + public override bool Equals(object obj) + { + return obj is AmiiboApi other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Head, Tail); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs new file mode 100644 index 000000000..def7d1bcc --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public class AmiiboApiGamesSwitch + { + [JsonPropertyName("amiiboUsage")] + public List<AmiiboApiUsage> AmiiboUsage { get; set; } + [JsonPropertyName("gameID")] + public List<string> GameId { get; set; } + [JsonPropertyName("gameName")] + public string GameName { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs new file mode 100644 index 000000000..814573c22 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public class AmiiboApiUsage + { + [JsonPropertyName("Usage")] + public string Usage { get; set; } + [JsonPropertyName("write")] + public bool Write { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs new file mode 100644 index 000000000..feb7993c1 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public struct AmiiboJson + { + [JsonPropertyName("amiibo")] + public List<AmiiboApi> Amiibo { get; set; } + [JsonPropertyName("lastUpdated")] + public DateTime LastUpdated { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs new file mode 100644 index 000000000..4cbb5a7b4 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + [JsonSerializable(typeof(AmiiboJson))] + public partial class AmiiboJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs new file mode 100644 index 000000000..10d014783 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Ui.Common.Models.Github +{ + public class GithubReleaseAssetJsonResponse + { + public string Name { get; set; } + public string State { get; set; } + public string BrowserDownloadUrl { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs new file mode 100644 index 000000000..954d03e31 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ryujinx.Ui.Common.Models.Github +{ + public class GithubReleasesJsonResponse + { + public string Name { get; set; } + public List<GithubReleaseAssetJsonResponse> Assets { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs new file mode 100644 index 000000000..e5fd9d094 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Github +{ + [JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)] + public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs index 5ad5924e8..3f186ce6b 100644 --- a/Ryujinx/Modules/Updater/Updater.cs +++ b/Ryujinx/Modules/Updater/Updater.cs @@ -2,14 +2,14 @@ using Gtk; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; -using Newtonsoft.Json.Linq; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.Ui; +using Ryujinx.Ui.Common.Models.Github; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -38,6 +38,8 @@ namespace Ryujinx.Modules private static string _buildUrl; private static long _buildSize; + private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; @@ -107,22 +109,16 @@ namespace Ryujinx.Modules // Fetch latest build information string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - JObject jsonRoot = JObject.Parse(fetchedJson); - JToken assets = jsonRoot["assets"]; + var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); + _buildVer = fetched.Name; - _buildVer = (string)jsonRoot["name"]; - - foreach (JToken asset in assets) + foreach (var asset in fetched.Assets) { - string assetName = (string)asset["name"]; - string assetState = (string)asset["state"]; - string downloadURL = (string)asset["browser_download_url"]; - - if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) + if (asset.Name.StartsWith("ryujinx") && asset.Name.EndsWith(_platformExt)) { - _buildUrl = downloadURL; + _buildUrl = asset.BrowserDownloadUrl; - if (assetState != "uploaded") + if (asset.State != "uploaded") { if (showVersionUpToDate) { diff --git a/Ryujinx/Ui/Windows/AboutWindow.cs b/Ryujinx/Ui/Windows/AboutWindow.cs index ea827a92f..41cf9c013 100644 --- a/Ryujinx/Ui/Windows/AboutWindow.cs +++ b/Ryujinx/Ui/Windows/AboutWindow.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Ui.Windows { string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)); + _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)); } catch { diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.cs b/Ryujinx/Ui/Windows/AmiiboWindow.cs index 9140a14e9..470032373 100644 --- a/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -3,6 +3,7 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Models.Amiibo; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; @@ -11,65 +12,15 @@ using System.Linq; using System.Net.Http; using System.Reflection; using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json; using System.Threading.Tasks; +using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi; +using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ui.Windows { public partial class AmiiboWindow : Window { - private struct AmiiboJson - { - [JsonPropertyName("amiibo")] - public List<AmiiboApi> Amiibo { get; set; } - [JsonPropertyName("lastUpdated")] - public DateTime LastUpdated { get; set; } - } - - private struct AmiiboApi - { - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonPropertyName("head")] - public string Head { get; set; } - [JsonPropertyName("tail")] - public string Tail { get; set; } - [JsonPropertyName("image")] - public string Image { get; set; } - [JsonPropertyName("amiiboSeries")] - public string AmiiboSeries { get; set; } - [JsonPropertyName("character")] - public string Character { get; set; } - [JsonPropertyName("gameSeries")] - public string GameSeries { get; set; } - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("release")] - public Dictionary<string, string> Release { get; set; } - - [JsonPropertyName("gamesSwitch")] - public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } - } - - private class AmiiboApiGamesSwitch - { - [JsonPropertyName("amiiboUsage")] - public List<AmiiboApiUsage> AmiiboUsage { get; set; } - [JsonPropertyName("gameID")] - public List<string> GameId { get; set; } - [JsonPropertyName("gameName")] - public string GameName { get; set; } - } - - private class AmiiboApiUsage - { - [JsonPropertyName("Usage")] - public string Usage { get; set; } - [JsonPropertyName("write")] - public bool Write { get; set; } - } - private const string DEFAULT_JSON = "{ \"amiibo\": [] }"; public string AmiiboId { get; private set; } @@ -96,6 +47,8 @@ namespace Ryujinx.Ui.Windows private List<AmiiboApi> _amiiboList; + private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo") { Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); @@ -127,9 +80,9 @@ namespace Ryujinx.Ui.Windows if (File.Exists(_amiiboJsonPath)) { - amiiboJsonString = File.ReadAllText(_amiiboJsonPath); + amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -148,7 +101,7 @@ namespace Ryujinx.Ui.Windows } } - _amiiboList = JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).Amiibo; + _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); if (LastScannedAmiiboShowAll) diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs index 0f0fba0b8..9b4befd85 100644 --- a/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -115,6 +115,8 @@ namespace Ryujinx.Ui.Windows private bool _mousePressed; private bool _middleMousePressed; + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { } private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin")) @@ -1120,10 +1122,7 @@ namespace Ryujinx.Ui.Windows try { - using (Stream stream = File.OpenRead(path)) - { - config = JsonHelper.Deserialize<InputConfig>(stream); - } + config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); } catch (JsonException) { } } @@ -1145,9 +1144,7 @@ namespace Ryujinx.Ui.Windows if (profileDialog.Run() == (int)ResponseType.Ok) { string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName); - string jsonString; - - jsonString = JsonHelper.Serialize(inputConfig, true); + string jsonString = JsonHelper.Serialize(inputConfig, SerializerContext.InputConfig); File.WriteAllText(path, jsonString); } diff --git a/Ryujinx/Ui/Windows/DlcWindow.cs b/Ryujinx/Ui/Windows/DlcWindow.cs index 9fccec195..b22f15932 100644 --- a/Ryujinx/Ui/Windows/DlcWindow.cs +++ b/Ryujinx/Ui/Windows/DlcWindow.cs @@ -7,15 +7,13 @@ using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; using System.IO; -using System.Text; - -using GUI = Gtk.Builder.ObjectAttribute; -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; +using GUI = Gtk.Builder.ObjectAttribute; namespace Ryujinx.Ui.Windows { @@ -26,6 +24,8 @@ namespace Ryujinx.Ui.Windows private readonly string _dlcJsonPath; private readonly List<DownloadableContentContainer> _dlcContainerList; + private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + #pragma warning disable CS0649, IDE0044 [GUI] Label _baseTitleInfoLabel; [GUI] TreeView _dlcTreeView; @@ -45,7 +45,7 @@ namespace Ryujinx.Ui.Windows try { - _dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_dlcJsonPath); + _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, SerializerContext.ListDownloadableContentContainer); } catch { @@ -260,10 +260,7 @@ namespace Ryujinx.Ui.Windows while (_dlcTreeView.Model.IterNext(ref parentIter)); } - using (FileStream dlcJsonStream = File.Create(_dlcJsonPath, 4096, FileOptions.WriteThrough)) - { - dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_dlcContainerList, true))); - } + JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, SerializerContext.ListDownloadableContentContainer); Dispose(); } diff --git a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs index 4aea58955..226473fca 100644 --- a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs +++ b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs @@ -7,6 +7,7 @@ using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.Ui.Widgets; @@ -14,10 +15,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; - -using GUI = Gtk.Builder.ObjectAttribute; -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; +using GUI = Gtk.Builder.ObjectAttribute; +using SpanHelpers = LibHac.Common.SpanHelpers; namespace Ryujinx.Ui.Windows { @@ -31,6 +30,7 @@ namespace Ryujinx.Ui.Windows private TitleUpdateMetadata _titleUpdateWindowData; private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary; + private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); #pragma warning disable CS0649, IDE0044 [GUI] Label _baseTitleInfoLabel; @@ -53,7 +53,7 @@ namespace Ryujinx.Ui.Windows try { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_updateJsonPath); + _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, SerializerContext.TitleUpdateMetadata); } catch { @@ -192,10 +192,7 @@ namespace Ryujinx.Ui.Windows } } - using (FileStream dlcJsonStream = File.Create(_updateJsonPath, 4096, FileOptions.WriteThrough)) - { - dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); - } + JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); _parent.UpdateGameTable(); From ba95ee54abf4905d38f3563881a1643f102993b3 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 21 Mar 2023 20:14:46 -0300 Subject: [PATCH 411/737] Revert "Use source generated json serializers in order to improve code trimming (#4094)" (#4576) This reverts commit 4ce4299ca2a6b11332f2341c69f40efd7205282f. --- .editorconfig | 4 - ARMeilleure/Decoders/IOpCode32Exception.cs | 9 +- Ryujinx.Ava/Common/Locale/LocaleManager.cs | 2 +- Ryujinx.Ava/Modules/Updater/Updater.cs | 24 +- Ryujinx.Ava/UI/Models/Amiibo.cs | 72 ++++ .../UI/ViewModels/AboutWindowViewModel.cs | 2 +- .../UI/ViewModels/AmiiboWindowViewModel.cs | 35 +- .../ViewModels/ControllerSettingsViewModel.cs | 9 +- .../DownloadableContentManagerViewModel.cs | 10 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 360 +++++++++--------- .../UI/Views/Main/MainMenuBarView.axaml.cs | 2 +- Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs | 6 +- .../UI/Windows/TitleUpdateWindow.axaml.cs | 3 + .../Configuration/AspectRatioExtensions.cs | 6 +- .../Configuration/BackendThreading.cs | 6 +- ...ownloadableContentJsonSerializerContext.cs | 11 - .../Configuration/GraphicsBackend.cs | 6 +- .../Configuration/GraphicsDebugLevel.cs | 4 - .../JsonMotionConfigControllerConverter.cs | 13 +- .../Motion/MotionConfigController.cs | 5 +- .../MotionConfigJsonSerializerContext.cs | 12 - .../Motion/MotionInputBackendType.cs | 6 +- .../Configuration/Hid/ControllerType.cs | 5 +- .../Configuration/Hid/InputBackendType.cs | 6 +- .../Configuration/Hid/InputConfig.cs | 2 - .../Hid/InputConfigJsonSerializerContext.cs | 14 - .../Hid/JsonInputConfigConverter.cs | 13 +- .../Configuration/Hid/PlayerIndex.cs | 4 - .../Configuration/MemoryManagerMode.cs | 6 +- ...itleUpdateMetadataJsonSerializerContext.cs | 10 - .../Logging/Formatters/DefaultLogFormatter.cs | 54 ++- .../Formatters/DynamicObjectFormatter.cs | 84 ---- Ryujinx.Common/Logging/LogClass.cs | 4 - Ryujinx.Common/Logging/LogEventArgs.cs | 10 +- Ryujinx.Common/Logging/LogEventArgsJson.cs | 30 -- .../Logging/LogEventJsonSerializerContext.cs | 9 - Ryujinx.Common/Logging/LogLevel.cs | 4 - .../Logging/Targets/JsonLogTarget.cs | 12 +- Ryujinx.Common/Utilities/CommonJsonContext.cs | 11 - Ryujinx.Common/Utilities/JsonHelper.cs | 122 +++--- .../Utilities/TypedStringEnumConverter.cs | 34 -- Ryujinx.HLE/HOS/ApplicationLoader.cs | 12 +- .../Account/Acc/AccountSaveDataManager.cs | 30 +- .../Acc/ProfilesJsonSerializerContext.cs | 11 - .../Account/Acc/Types/AccountState.cs | 4 - .../Account/Acc/Types/ProfilesJson.cs | 10 - .../Account/Acc/Types/UserProfileJson.cs | 12 - .../Nfc/Nfp/AmiiboJsonSerializerContext.cs | 10 - .../HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 10 +- Ryujinx.Headless.SDL2/Program.cs | 7 +- .../App/ApplicationJsonSerializerContext.cs | 10 - Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 14 +- .../Configuration/AudioBackend.cs | 6 +- .../Configuration/ConfigurationFileFormat.cs | 12 +- .../ConfigurationFileFormatSettings.cs | 9 - .../ConfigurationJsonSerializerContext.cs | 10 - .../Configuration/ConfigurationState.cs | 5 +- .../Configuration/System/Language.cs | 6 +- .../Configuration/System/Region.cs | 6 +- Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs | 57 --- .../Models/Amiibo/AmiiboApiGamesSwitch.cs | 15 - .../Models/Amiibo/AmiiboApiUsage.cs | 12 - Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs | 14 - .../Amiibo/AmiiboJsonSerializerContext.cs | 9 - .../Github/GithubReleaseAssetJsonResponse.cs | 9 - .../Github/GithubReleasesJsonResponse.cs | 10 - .../GithubReleasesJsonSerializerContext.cs | 9 - Ryujinx/Modules/Updater/Updater.cs | 26 +- Ryujinx/Ui/Windows/AboutWindow.cs | 2 +- Ryujinx/Ui/Windows/AmiiboWindow.cs | 65 +++- Ryujinx/Ui/Windows/ControllerWindow.cs | 11 +- Ryujinx/Ui/Windows/DlcWindow.cs | 15 +- Ryujinx/Ui/Windows/TitleUpdateWindow.cs | 15 +- 73 files changed, 608 insertions(+), 886 deletions(-) create mode 100644 Ryujinx.Ava/UI/Models/Amiibo.cs delete mode 100644 Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs delete mode 100644 Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs delete mode 100644 Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs delete mode 100644 Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs delete mode 100644 Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs delete mode 100644 Ryujinx.Common/Logging/LogEventArgsJson.cs delete mode 100644 Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs delete mode 100644 Ryujinx.Common/Utilities/CommonJsonContext.cs delete mode 100644 Ryujinx.Common/Utilities/TypedStringEnumConverter.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs delete mode 100644 Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs delete mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs delete mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs delete mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs delete mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs delete mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs delete mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs delete mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs delete mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs delete mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs delete mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs diff --git a/.editorconfig b/.editorconfig index 8a3054287..9e00e3bae 100644 --- a/.editorconfig +++ b/.editorconfig @@ -63,10 +63,6 @@ dotnet_code_quality_unused_parameters = all:suggestion #### C# Coding Conventions #### -# Namespace preferences -csharp_style_namespace_declarations = block_scoped:warning -resharper_csharp_namespace_body = block_scoped - # var preferences csharp_style_var_elsewhere = false:silent csharp_style_var_for_built_in_types = false:silent diff --git a/ARMeilleure/Decoders/IOpCode32Exception.cs b/ARMeilleure/Decoders/IOpCode32Exception.cs index 8f0fb81a0..82819bddd 100644 --- a/ARMeilleure/Decoders/IOpCode32Exception.cs +++ b/ARMeilleure/Decoders/IOpCode32Exception.cs @@ -1,7 +1,6 @@ -namespace ARMeilleure.Decoders +namespace ARMeilleure.Decoders; + +interface IOpCode32Exception { - interface IOpCode32Exception - { - int Id { get; } - } + int Id { get; } } \ No newline at end of file diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index 464ab780d..1374bfee1 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -130,7 +130,7 @@ namespace Ryujinx.Ava.Common.Locale { var localeStrings = new Dictionary<LocaleKeys, string>(); string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); - var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); + var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); foreach (var item in strings) { diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index c58575284..e89abd1da 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -4,14 +4,13 @@ using FluentAvalonia.UI.Controls; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; +using Newtonsoft.Json.Linq; using Ryujinx.Ava; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Common; using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Helper; -using Ryujinx.Ui.Common.Models.Github; using System; using System.Collections.Generic; using System.Diagnostics; @@ -32,7 +31,6 @@ namespace Ryujinx.Modules internal static class Updater { private const string GitHubApiURL = "https://api.github.com"; - private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); @@ -101,16 +99,22 @@ namespace Ryujinx.Modules string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); - _buildVer = fetched.Name; + JObject jsonRoot = JObject.Parse(fetchedJson); + JToken assets = jsonRoot["assets"]; - foreach (var asset in fetched.Assets) + _buildVer = (string)jsonRoot["name"]; + + foreach (JToken asset in assets) { - if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt)) - { - _buildUrl = asset.BrowserDownloadUrl; + string assetName = (string)asset["name"]; + string assetState = (string)asset["state"]; + string downloadURL = (string)asset["browser_download_url"]; - if (asset.State != "uploaded") + if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt)) + { + _buildUrl = downloadURL; + + if (assetState != "uploaded") { if (showVersionUpToDate) { diff --git a/Ryujinx.Ava/UI/Models/Amiibo.cs b/Ryujinx.Ava/UI/Models/Amiibo.cs new file mode 100644 index 000000000..d0ccafd08 --- /dev/null +++ b/Ryujinx.Ava/UI/Models/Amiibo.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ava.UI.Models +{ + public class Amiibo + { + public struct AmiiboJson + { + [JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; } + [JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; } + } + + public struct AmiiboApi + { + [JsonPropertyName("name")] public string Name { get; set; } + [JsonPropertyName("head")] public string Head { get; set; } + [JsonPropertyName("tail")] public string Tail { get; set; } + [JsonPropertyName("image")] public string Image { get; set; } + [JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; } + [JsonPropertyName("character")] public string Character { get; set; } + [JsonPropertyName("gameSeries")] public string GameSeries { get; set; } + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; } + + [JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } + + public override string ToString() + { + return Name; + } + + public string GetId() + { + return Head + Tail; + } + + public override bool Equals(object obj) + { + if (obj is AmiiboApi amiibo) + { + return amiibo.Head + amiibo.Tail == Head + Tail; + } + + return false; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + public class AmiiboApiGamesSwitch + { + [JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; } + + [JsonPropertyName("gameID")] public List<string> GameId { get; set; } + + [JsonPropertyName("gameName")] public string GameName { get; set; } + } + + public class AmiiboApiUsage + { + [JsonPropertyName("Usage")] public string Usage { get; set; } + + [JsonPropertyName("write")] public bool Write { get; set; } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs index 9b5422adf..872c1a37f 100644 --- a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs @@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels { string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray) + "\n\n"); + Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n"; } catch { diff --git a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index 090c13a97..5311318c5 100644 --- a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -4,11 +4,11 @@ using Avalonia.Media.Imaging; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; -using Ryujinx.Ui.Common.Models.Amiibo; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -17,7 +17,6 @@ using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ava.UI.ViewModels { @@ -32,8 +31,8 @@ namespace Ryujinx.Ava.UI.ViewModels private readonly StyleableWindow _owner; private Bitmap _amiiboImage; - private List<AmiiboApi> _amiiboList; - private AvaloniaList<AmiiboApi> _amiibos; + private List<Amiibo.AmiiboApi> _amiiboList; + private AvaloniaList<Amiibo.AmiiboApi> _amiibos; private ObservableCollection<string> _amiiboSeries; private int _amiiboSelectedIndex; @@ -42,8 +41,6 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _showAllAmiibo; private bool _useRandomUuid; private string _usage; - - private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) { @@ -55,9 +52,9 @@ namespace Ryujinx.Ava.UI.ViewModels Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); _amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); - _amiiboList = new List<AmiiboApi>(); + _amiiboList = new List<Amiibo.AmiiboApi>(); _amiiboSeries = new ObservableCollection<string>(); - _amiibos = new AvaloniaList<AmiiboApi>(); + _amiibos = new AvaloniaList<Amiibo.AmiiboApi>(); _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png"); @@ -97,7 +94,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public AvaloniaList<AmiiboApi> AmiiboList + public AvaloniaList<Amiibo.AmiiboApi> AmiiboList { get => _amiibos; set @@ -190,9 +187,9 @@ namespace Ryujinx.Ava.UI.ViewModels if (File.Exists(_amiiboJsonPath)) { - amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); + amiiboJsonString = File.ReadAllText(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -209,7 +206,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; + _amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); ParseAmiiboData(); @@ -226,7 +223,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!ShowAllAmiibo) { - foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) + foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) { if (game != null) { @@ -258,7 +255,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void SelectLastScannedAmiibo() { - AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); + Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries); AmiiboSelectedIndex = AmiiboList.IndexOf(scanned); @@ -273,7 +270,7 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - List<AmiiboApi> amiiboSortedList = _amiiboList + List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList .Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex]) .OrderBy(amiibo => amiibo.Name).ToList(); @@ -283,7 +280,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!_showAllAmiibo) { - foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) + foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) { if (game != null) { @@ -317,7 +314,7 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; + Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image; @@ -329,11 +326,11 @@ namespace Ryujinx.Ava.UI.ViewModels { bool writable = false; - foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch) + foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch) { if (item.GameId.Contains(TitleId)) { - foreach (AmiiboApiUsage usageItem in item.AmiiboUsage) + foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage) { usageString += Environment.NewLine + $"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"; diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index dd261b103..35256b3b5 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -51,8 +51,6 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _isLoaded; private readonly UserControl _owner; - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public IGamepadDriver AvaloniaKeyboardDriver { get; } public IGamepad SelectedGamepad { get; private set; } @@ -708,7 +706,10 @@ namespace Ryujinx.Ava.UI.ViewModels try { - config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); + using (Stream stream = File.OpenRead(path)) + { + config = JsonHelper.Deserialize<InputConfig>(stream); + } } catch (JsonException) { } catch (InvalidOperationException) @@ -774,7 +775,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.ControllerType = Controllers[_controller].Type; - string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig); + string jsonString = JsonHelper.Serialize(config, true); await File.WriteAllTextAsync(path, jsonString); diff --git a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs index 1d7da9a40..e5e4f66b5 100644 --- a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using Path = System.IO.Path; @@ -40,8 +41,6 @@ namespace Ryujinx.Ava.UI.ViewModels private ulong _titleId; private string _titleName; - private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public AvaloniaList<DownloadableContentModel> DownloadableContents { get => _downloadableContents; @@ -101,7 +100,7 @@ namespace Ryujinx.Ava.UI.ViewModels try { - _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer); + _downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath); } catch { @@ -331,7 +330,10 @@ namespace Ryujinx.Ava.UI.ViewModels _downloadableContentContainerList.Add(container); } - JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer); + using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) + { + downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true))); + } } } diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index ed5b5eacf..dd9e1b961 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -25,228 +25,226 @@ using System.Text; using Path = System.IO.Path; using SpanHelpers = LibHac.Common.SpanHelpers; -namespace Ryujinx.Ava.UI.ViewModels +namespace Ryujinx.Ava.UI.ViewModels; + +public class TitleUpdateViewModel : BaseModel { - public class TitleUpdateViewModel : BaseModel + public TitleUpdateMetadata _titleUpdateWindowData; + public readonly string _titleUpdateJsonPath; + private VirtualFileSystem _virtualFileSystem { get; } + private ulong _titleId { get; } + private string _titleName { get; } + + private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); + private AvaloniaList<object> _views = new(); + private object _selectedUpdate; + + public AvaloniaList<TitleUpdateModel> TitleUpdates { - public TitleUpdateMetadata _titleUpdateWindowData; - public readonly string _titleUpdateJsonPath; - private VirtualFileSystem _virtualFileSystem { get; } - private ulong _titleId { get; } - private string _titleName { get; } - - private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); - private AvaloniaList<object> _views = new(); - private object _selectedUpdate; - - private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - public AvaloniaList<TitleUpdateModel> TitleUpdates + get => _titleUpdates; + set { - get => _titleUpdates; - set - { - _titleUpdates = value; - OnPropertyChanged(); - } + _titleUpdates = value; + OnPropertyChanged(); } + } - public AvaloniaList<object> Views + public AvaloniaList<object> Views + { + get => _views; + set { - get => _views; - set - { - _views = value; - OnPropertyChanged(); - } + _views = value; + OnPropertyChanged(); } + } - public object SelectedUpdate + public object SelectedUpdate + { + get => _selectedUpdate; + set { - get => _selectedUpdate; - set - { - _selectedUpdate = value; - OnPropertyChanged(); - } + _selectedUpdate = value; + OnPropertyChanged(); } + } - public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + _virtualFileSystem = virtualFileSystem; + + _titleId = titleId; + _titleName = titleName; + + _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); + + try { - _virtualFileSystem = virtualFileSystem; - - _titleId = titleId; - _titleName = titleName; - - _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); - - try - { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata); - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); - - _titleUpdateWindowData = new TitleUpdateMetadata - { - Selected = "", - Paths = new List<string>() - }; - - Save(); - } - - LoadUpdates(); + _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath); } - - private void LoadUpdates() + catch { - foreach (string path in _titleUpdateWindowData.Paths) + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); + + _titleUpdateWindowData = new TitleUpdateMetadata { - AddUpdate(path); - } + Selected = "", + Paths = new List<string>() + }; - TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); - - SelectedUpdate = selected; - - // NOTE: Save the list again to remove leftovers. Save(); - SortUpdates(); } - public void SortUpdates() + LoadUpdates(); + } + + private void LoadUpdates() + { + foreach (string path in _titleUpdateWindowData.Paths) { - var list = TitleUpdates.ToList(); + AddUpdate(path); + } - list.Sort((first, second) => + TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); + + SelectedUpdate = selected; + + // NOTE: Save the list again to remove leftovers. + Save(); + + SortUpdates(); + } + + public void SortUpdates() + { + var list = TitleUpdates.ToList(); + + list.Sort((first, second) => + { + if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) { - if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) - { - return -1; - } - else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) - { - return 1; - } + return -1; + } + else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) + { + return 1; + } - return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; - }); + return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; + }); - Views.Clear(); - Views.Add(new BaseModel()); - Views.AddRange(list); + Views.Clear(); + Views.Add(new BaseModel()); + Views.AddRange(list); - if (SelectedUpdate == null) + if (SelectedUpdate == null) + { + SelectedUpdate = Views[0]; + } + else if (!TitleUpdates.Contains(SelectedUpdate)) + { + if (Views.Count > 1) + { + SelectedUpdate = Views[1]; + } + else { SelectedUpdate = Views[0]; } - else if (!TitleUpdates.Contains(SelectedUpdate)) + } + } + + private void AddUpdate(string path) + { + if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) + { + using FileStream file = new(path, FileMode.Open, FileAccess.Read); + + try { - if (Views.Count > 1) + (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + + if (controlNca != null && patchNca != null) { - SelectedUpdate = Views[1]; + ApplicationControlProperty controlData = new(); + + using UniqueRef<IFile> nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + TitleUpdates.Add(new TitleUpdateModel(controlData, path)); } else - { - SelectedUpdate = Views[0]; - } - } - } - - private void AddUpdate(string path) - { - if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) - { - using FileStream file = new(path, FileMode.Open, FileAccess.Read); - - try - { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); - - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new(); - - using UniqueRef<IFile> nacpFile = new(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - TitleUpdates.Add(new TitleUpdateModel(controlData, path)); - } - else - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); - }); - } - } - catch (Exception ex) { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); }); } } - } - - public void RemoveUpdate(TitleUpdateModel update) - { - TitleUpdates.Remove(update); - - SortUpdates(); - } - - public async void Add() - { - OpenFileDialog dialog = new() + catch (Exception ex) { - Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], - AllowMultiple = true - }; - - dialog.Filters.Add(new FileDialogFilter - { - Name = "NSP", - Extensions = { "nsp" } - }); - - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - string[] files = await dialog.ShowAsync(desktop.MainWindow); - - if (files != null) + Dispatcher.UIThread.Post(async () => { - foreach (string file in files) - { - AddUpdate(file); - } - } + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)); + }); } - - SortUpdates(); - } - - public void Save() - { - _titleUpdateWindowData.Paths.Clear(); - _titleUpdateWindowData.Selected = ""; - - foreach (TitleUpdateModel update in TitleUpdates) - { - _titleUpdateWindowData.Paths.Add(update.Path); - - if (update == SelectedUpdate) - { - _titleUpdateWindowData.Selected = update.Path; - } - } - - JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); } } + + public void RemoveUpdate(TitleUpdateModel update) + { + TitleUpdates.Remove(update); + + SortUpdates(); + } + + public async void Add() + { + OpenFileDialog dialog = new() + { + Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], + AllowMultiple = true + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = "NSP", + Extensions = { "nsp" } + }); + + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null) + { + foreach (string file in files) + { + AddUpdate(file); + } + } + } + + SortUpdates(); + } + + public void Save() + { + _titleUpdateWindowData.Paths.Clear(); + _titleUpdateWindowData.Selected = ""; + + foreach (TitleUpdateModel update in TitleUpdates) + { + _titleUpdateWindowData.Paths.Add(update.Path); + + if (update == SelectedUpdate) + { + _titleUpdateWindowData.Selected = update.Path; + } + } + + File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); + } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 51c71c378..1c6f4265c 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Main { string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last(); string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}"); - var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); + var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); if (!strings.TryGetValue("Language", out string languageName)) { diff --git a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs index 206d0a7ea..5368a1333 100644 --- a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs @@ -1,7 +1,7 @@ using Avalonia.Interactivity; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Ui.Common.Models.Amiibo; namespace Ryujinx.Ava.UI.Windows { @@ -35,14 +35,14 @@ namespace Ryujinx.Ava.UI.Windows } public bool IsScanned { get; set; } - public AmiiboApi ScannedAmiibo { get; set; } + public Amiibo.AmiiboApi ScannedAmiibo { get; set; } public AmiiboWindowViewModel ViewModel { get; set; } private void ScanButton_Click(object sender, RoutedEventArgs e) { if (ViewModel.AmiiboSelectedIndex > -1) { - AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex]; + Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex]; ScannedAmiibo = amiibo; IsScanned = true; Close(); diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 153ce95d2..1b50c46f3 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -6,8 +6,11 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.Common.Helper; +using System.IO; +using System.Text; using System.Threading.Tasks; using Button = Avalonia.Controls.Button; diff --git a/Ryujinx.Common/Configuration/AspectRatioExtensions.cs b/Ryujinx.Common/Configuration/AspectRatioExtensions.cs index 5e97ed19c..3d0be88e9 100644 --- a/Ryujinx.Common/Configuration/AspectRatioExtensions.cs +++ b/Ryujinx.Common/Configuration/AspectRatioExtensions.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration +namespace Ryujinx.Common.Configuration { - [JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))] public enum AspectRatio { Fixed4x3, diff --git a/Ryujinx.Common/Configuration/BackendThreading.cs b/Ryujinx.Common/Configuration/BackendThreading.cs index 8833b3f07..cfc089146 100644 --- a/Ryujinx.Common/Configuration/BackendThreading.cs +++ b/Ryujinx.Common/Configuration/BackendThreading.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration +namespace Ryujinx.Common.Configuration { - [JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))] public enum BackendThreading { Auto, diff --git a/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs b/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs deleted file mode 100644 index 132c45a44..000000000 --- a/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration -{ - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(List<DownloadableContentContainer>))] - public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/GraphicsBackend.cs b/Ryujinx.Common/Configuration/GraphicsBackend.cs index d74dd6e19..26e4a28a9 100644 --- a/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration +namespace Ryujinx.Common.Configuration { - [JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))] public enum GraphicsBackend { Vulkan, diff --git a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs index ad12302a6..556af689a 100644 --- a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs +++ b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - namespace Ryujinx.Common.Configuration { - [JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))] public enum GraphicsDebugLevel { None, diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs index 2b9e0af42..d1c2e4e81 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs @@ -1,5 +1,4 @@ -using Ryujinx.Common.Utilities; -using System; +using System; using System.Text.Json; using System.Text.Json.Serialization; @@ -7,8 +6,6 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController> { - private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader) { // Temporary reader to get the backend type @@ -55,8 +52,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion return motionBackendType switch { - MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController), - MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController), + MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options), + MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options), _ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"), }; } @@ -66,10 +63,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion switch (value.MotionBackend) { case MotionInputBackendType.GamepadDriver: - JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController); + JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options); break; case MotionInputBackendType.CemuHook: - JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController); + JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options); break; default: throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}"); diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs index 7636aa414..832aae0d1 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs @@ -1,8 +1,5 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { - [JsonConverter(typeof(JsonMotionConfigControllerConverter))] public class MotionConfigController { public MotionInputBackendType MotionBackend { get; set; } diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs deleted file mode 100644 index 5cd9e452b..000000000 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration.Hid.Controller.Motion -{ - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(MotionConfigController))] - [JsonSerializable(typeof(CemuHookMotionConfigController))] - [JsonSerializable(typeof(StandardMotionConfigController))] - public partial class MotionConfigJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs index c65510478..45d654edc 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { - [JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))] public enum MotionInputBackendType : byte { Invalid, diff --git a/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/Ryujinx.Common/Configuration/Hid/ControllerType.cs index 70f811c89..0ad01bbb6 100644 --- a/Ryujinx.Common/Configuration/Hid/ControllerType.cs +++ b/Ryujinx.Common/Configuration/Hid/ControllerType.cs @@ -1,12 +1,9 @@ -using Ryujinx.Common.Utilities; using System; -using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { - // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical [Flags] - [JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))] + // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical public enum ControllerType : int { None, diff --git a/Ryujinx.Common/Configuration/Hid/InputBackendType.cs b/Ryujinx.Common/Configuration/Hid/InputBackendType.cs index 1db3f5703..9e944f9e8 100644 --- a/Ryujinx.Common/Configuration/Hid/InputBackendType.cs +++ b/Ryujinx.Common/Configuration/Hid/InputBackendType.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration.Hid +namespace Ryujinx.Common.Configuration.Hid { - [JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))] public enum InputBackendType { Invalid, diff --git a/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/Ryujinx.Common/Configuration/Hid/InputConfig.cs index 16c8f8e32..3364e35fa 100644 --- a/Ryujinx.Common/Configuration/Hid/InputConfig.cs +++ b/Ryujinx.Common/Configuration/Hid/InputConfig.cs @@ -1,10 +1,8 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { - [JsonConverter(typeof(JsonInputConfigConverter))] public class InputConfig : INotifyPropertyChanged { /// <summary> diff --git a/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs b/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs deleted file mode 100644 index 254c4feb4..000000000 --- a/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Configuration.Hid.Keyboard; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration.Hid -{ - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(InputConfig))] - [JsonSerializable(typeof(StandardKeyboardInputConfig))] - [JsonSerializable(typeof(StandardControllerInputConfig))] - public partial class InputConfigJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs b/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs index 08bbcbf17..7223ad451 100644 --- a/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs +++ b/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs @@ -1,16 +1,13 @@ using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Keyboard; -using Ryujinx.Common.Utilities; using System; using System.Text.Json; using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { - public class JsonInputConfigConverter : JsonConverter<InputConfig> + class JsonInputConfigConverter : JsonConverter<InputConfig> { - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader) { // Temporary reader to get the backend type @@ -57,8 +54,8 @@ namespace Ryujinx.Common.Configuration.Hid return backendType switch { - InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig), - InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig), + InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options), + InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options), _ => throw new InvalidOperationException($"Unknown backend type {backendType}"), }; } @@ -68,10 +65,10 @@ namespace Ryujinx.Common.Configuration.Hid switch (value.Backend) { case InputBackendType.WindowKeyboard: - JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig); + JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options); break; case InputBackendType.GamepadSDL2: - JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig); + JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options); break; default: throw new ArgumentException($"Unknown backend type {value.Backend}"); diff --git a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs index dd6495d4d..2e34cb96c 100644 --- a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs +++ b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs @@ -1,10 +1,6 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - namespace Ryujinx.Common.Configuration.Hid { // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical - [JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))] public enum PlayerIndex : int { Player1 = 0, diff --git a/Ryujinx.Common/Configuration/MemoryManagerMode.cs b/Ryujinx.Common/Configuration/MemoryManagerMode.cs index f10fd6f1b..ad6c2a346 100644 --- a/Ryujinx.Common/Configuration/MemoryManagerMode.cs +++ b/Ryujinx.Common/Configuration/MemoryManagerMode.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration +namespace Ryujinx.Common.Configuration { - [JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))] public enum MemoryManagerMode : byte { SoftwarePageTable, diff --git a/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs b/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs deleted file mode 100644 index 5b661b878..000000000 --- a/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Configuration -{ - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(TitleUpdateMetadata))] - public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs index 28a7d5461..b9a08323e 100644 --- a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs +++ b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs @@ -1,20 +1,22 @@ -using System.Text; +using System; +using System.Reflection; +using System.Text; namespace Ryujinx.Common.Logging { internal class DefaultLogFormatter : ILogFormatter { - private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); + private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>(); public string Format(LogEventArgs args) { - StringBuilder sb = StringBuilderPool.Allocate(); + StringBuilder sb = _stringBuilderPool.Allocate(); try { sb.Clear(); - sb.Append($@"{args.Time:hh\:mm\:ss\.fff}"); + sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time); sb.Append($" |{args.Level.ToString()[0]}| "); if (args.ThreadName != null) @@ -25,17 +27,53 @@ namespace Ryujinx.Common.Logging sb.Append(args.Message); - if (args.Data is not null) + if (args.Data != null) { - sb.Append(' '); - DynamicObjectFormatter.Format(sb, args.Data); + PropertyInfo[] props = args.Data.GetType().GetProperties(); + + sb.Append(" {"); + + foreach (var prop in props) + { + sb.Append(prop.Name); + sb.Append(": "); + + if (typeof(Array).IsAssignableFrom(prop.PropertyType)) + { + Array array = (Array)prop.GetValue(args.Data); + foreach (var item in array) + { + sb.Append(item.ToString()); + sb.Append(", "); + } + + if (array.Length > 0) + { + sb.Remove(sb.Length - 2, 2); + } + } + else + { + sb.Append(prop.GetValue(args.Data)); + } + + sb.Append(" ; "); + } + + // We remove the final ';' from the string + if (props.Length > 0) + { + sb.Remove(sb.Length - 3, 3); + } + + sb.Append('}'); } return sb.ToString(); } finally { - StringBuilderPool.Release(sb); + _stringBuilderPool.Release(sb); } } } diff --git a/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs b/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs deleted file mode 100644 index 5f15cc2a6..000000000 --- a/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs +++ /dev/null @@ -1,84 +0,0 @@ -#nullable enable -using System; -using System.Reflection; -using System.Text; - -namespace Ryujinx.Common.Logging -{ - internal class DynamicObjectFormatter - { - private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); - - public static string? Format(object? dynamicObject) - { - if (dynamicObject is null) - { - return null; - } - - StringBuilder sb = StringBuilderPool.Allocate(); - - try - { - Format(sb, dynamicObject); - - return sb.ToString(); - } - finally - { - StringBuilderPool.Release(sb); - } - } - - public static void Format(StringBuilder sb, object? dynamicObject) - { - if (dynamicObject is null) - { - return; - } - - PropertyInfo[] props = dynamicObject.GetType().GetProperties(); - - sb.Append('{'); - - foreach (var prop in props) - { - sb.Append(prop.Name); - sb.Append(": "); - - if (typeof(Array).IsAssignableFrom(prop.PropertyType)) - { - Array? array = (Array?) prop.GetValue(dynamicObject); - - if (array is not null) - { - foreach (var item in array) - { - sb.Append(item); - sb.Append(", "); - } - - if (array.Length > 0) - { - sb.Remove(sb.Length - 2, 2); - } - } - } - else - { - sb.Append(prop.GetValue(dynamicObject)); - } - - sb.Append(" ; "); - } - - // We remove the final ';' from the string - if (props.Length > 0) - { - sb.Remove(sb.Length - 3, 3); - } - - sb.Append('}'); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index e62676cd3..7e53c972b 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - namespace Ryujinx.Common.Logging { - [JsonConverter(typeof(TypedStringEnumConverter<LogClass>))] public enum LogClass { Application, diff --git a/Ryujinx.Common/Logging/LogEventArgs.cs b/Ryujinx.Common/Logging/LogEventArgs.cs index a27af7809..511c8e6e2 100644 --- a/Ryujinx.Common/Logging/LogEventArgs.cs +++ b/Ryujinx.Common/Logging/LogEventArgs.cs @@ -11,7 +11,15 @@ namespace Ryujinx.Common.Logging public readonly string Message; public readonly object Data; - public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null) + public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message) + { + Level = level; + Time = time; + ThreadName = threadName; + Message = message; + } + + public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data) { Level = level; Time = time; diff --git a/Ryujinx.Common/Logging/LogEventArgsJson.cs b/Ryujinx.Common/Logging/LogEventArgsJson.cs deleted file mode 100644 index 425b97662..000000000 --- a/Ryujinx.Common/Logging/LogEventArgsJson.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Logging -{ - internal class LogEventArgsJson - { - public LogLevel Level { get; } - public TimeSpan Time { get; } - public string ThreadName { get; } - - public string Message { get; } - public string Data { get; } - - [JsonConstructor] - public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null) - { - Level = level; - Time = time; - ThreadName = threadName; - Message = message; - Data = data; - } - - public static LogEventArgsJson FromLogEventArgs(LogEventArgs args) - { - return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data)); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs b/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs deleted file mode 100644 index da21f11e8..000000000 --- a/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Logging -{ - [JsonSerializable(typeof(LogEventArgsJson))] - internal partial class LogEventJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogLevel.cs b/Ryujinx.Common/Logging/LogLevel.cs index 3786c7561..8857fb45a 100644 --- a/Ryujinx.Common/Logging/LogLevel.cs +++ b/Ryujinx.Common/Logging/LogLevel.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - namespace Ryujinx.Common.Logging { - [JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))] public enum LogLevel { Debug, diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs index 06976433e..95f96576c 100644 --- a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs +++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -1,5 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.IO; +using System.IO; +using System.Text.Json; namespace Ryujinx.Common.Logging { @@ -25,8 +25,12 @@ namespace Ryujinx.Common.Logging public void Log(object sender, LogEventArgs e) { - var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e); - JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson); + string text = JsonSerializer.Serialize(e); + + using (BinaryWriter writer = new BinaryWriter(_stream)) + { + writer.Write(text); + } } public void Dispose() diff --git a/Ryujinx.Common/Utilities/CommonJsonContext.cs b/Ryujinx.Common/Utilities/CommonJsonContext.cs deleted file mode 100644 index d7b3f78cd..000000000 --- a/Ryujinx.Common/Utilities/CommonJsonContext.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Utilities -{ - [JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")] - [JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")] - public partial class CommonJsonContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Common/Utilities/JsonHelper.cs b/Ryujinx.Common/Utilities/JsonHelper.cs index 9a2d6f181..36f391149 100644 --- a/Ryujinx.Common/Utilities/JsonHelper.cs +++ b/Ryujinx.Common/Utilities/JsonHelper.cs @@ -1,62 +1,15 @@ -using System.IO; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using System.IO; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization.Metadata; +using System.Text.Json.Serialization; namespace Ryujinx.Common.Utilities { public class JsonHelper { - private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy(); - private const int DefaultFileWriteBufferSize = 4096; - - /// <summary> - /// Creates new serializer options with default settings. - /// </summary> - /// <remarks> - /// It is REQUIRED for you to save returned options statically or as a part of static serializer context - /// in order to avoid performance issues. You can safely modify returned options for your case before storing. - /// </remarks> - public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) - { - JsonSerializerOptions options = new() - { - DictionaryKeyPolicy = SnakeCasePolicy, - PropertyNamingPolicy = SnakeCasePolicy, - WriteIndented = indented, - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip - }; - - return options; - } - - public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo) - { - return JsonSerializer.Serialize(value, typeInfo); - } - - public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo) - { - return JsonSerializer.Deserialize(value, typeInfo); - } - - public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo) - { - using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough); - JsonSerializer.Serialize(file, value, typeInfo); - } - - public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo) - { - using FileStream file = File.OpenRead(filePath); - return JsonSerializer.Deserialize(file, typeInfo); - } - - public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo) - { - JsonSerializer.Serialize(stream, value, typeInfo); - } + public static JsonNamingPolicy SnakeCase { get; } private class SnakeCaseNamingPolicy : JsonNamingPolicy { @@ -67,7 +20,7 @@ namespace Ryujinx.Common.Utilities return name; } - StringBuilder builder = new(); + StringBuilder builder = new StringBuilder(); for (int i = 0; i < name.Length; i++) { @@ -81,7 +34,7 @@ namespace Ryujinx.Common.Utilities } else { - builder.Append('_'); + builder.Append("_"); builder.Append(char.ToLowerInvariant(c)); } } @@ -94,5 +47,64 @@ namespace Ryujinx.Common.Utilities return builder.ToString(); } } + + static JsonHelper() + { + SnakeCase = new SnakeCaseNamingPolicy(); + } + + public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DictionaryKeyPolicy = SnakeCase, + PropertyNamingPolicy = SnakeCase, + WriteIndented = prettyPrint, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + + options.Converters.Add(new JsonStringEnumConverter()); + options.Converters.Add(new JsonInputConfigConverter()); + options.Converters.Add(new JsonMotionConfigControllerConverter()); + + return options; + } + + public static T Deserialize<T>(Stream stream) + { + using (BinaryReader reader = new BinaryReader(stream)) + { + return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions()); + } + } + + public static T DeserializeFromFile<T>(string path) + { + return Deserialize<T>(File.ReadAllText(path)); + } + + public static T Deserialize<T>(string json) + { + return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions()); + } + + public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false) + { + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Write(SerializeToUtf8Bytes(obj, prettyPrint)); + } + } + + public static string Serialize<TValue>(TValue obj, bool prettyPrint = false) + { + return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint)); + } + + public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false) + { + return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint)); + } } -} \ No newline at end of file +} diff --git a/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs b/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs deleted file mode 100644 index c0127dc4a..000000000 --- a/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -#nullable enable -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Ryujinx.Common.Utilities -{ - /// <summary> - /// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs - /// </summary> - /// <remarks> - /// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>. - /// Get rid of this converter if dotnet supports similar functionality out of the box. - /// </remarks> - /// <typeparam name="TEnum">Type of enum to serialize</typeparam> - public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum - { - public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var enumValue = reader.GetString(); - if (string.IsNullOrEmpty(enumValue)) - { - return default; - } - - return Enum.Parse<TEnum>(enumValue); - } - - public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } - } -} diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 7d06e5eb7..82bd9b312 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -13,7 +13,6 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.Loaders.Executables; @@ -25,13 +24,14 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; -using System.Text.Json; using static Ryujinx.HLE.HOS.ModLoader; using ApplicationId = LibHac.Ncm.ApplicationId; using Path = System.IO.Path; namespace Ryujinx.HLE.HOS { + using JsonHelper = Common.Utilities.JsonHelper; + public class ApplicationLoader { // Binaries from exefs are loaded into mem in this order. Do not change. @@ -57,10 +57,6 @@ namespace Ryujinx.HLE.HOS private string _displayVersion; private BlitStruct<ApplicationControlProperty> _controlData; - private static readonly JsonSerializerOptions SerializerOptions = JsonHelper.GetDefaultSerializerOptions(); - private static readonly DownloadableContentJsonSerializerContext ContentSerializerContext = new(SerializerOptions); - private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(SerializerOptions); - public BlitStruct<ApplicationControlProperty> ControlData => _controlData; public string TitleName => _titleName; public string DisplayVersion => _displayVersion; @@ -201,7 +197,7 @@ namespace Ryujinx.HLE.HOS if (File.Exists(titleUpdateMetadataPath)) { - updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; + updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; if (File.Exists(updatePath)) { @@ -415,7 +411,7 @@ namespace Ryujinx.HLE.HOS if (File.Exists(titleAocMetadataPath)) { - List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(titleAocMetadataPath, ContentSerializerContext.ListDownloadableContentContainer); + List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath); foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs index 535779d2e..ec0b0a10b 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -1,11 +1,11 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; -using Ryujinx.HLE.HOS.Services.Account.Acc.Types; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Text.Json.Serialization; namespace Ryujinx.HLE.HOS.Services.Account.Acc { @@ -13,7 +13,29 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); - private static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private struct ProfilesJson + { + [JsonPropertyName("profiles")] + public List<UserProfileJson> Profiles { get; set; } + [JsonPropertyName("last_opened")] + public string LastOpened { get; set; } + } + + private struct UserProfileJson + { + [JsonPropertyName("user_id")] + public string UserId { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("account_state")] + public AccountState AccountState { get; set; } + [JsonPropertyName("online_play_state")] + public AccountState OnlinePlayState { get; set; } + [JsonPropertyName("last_modified_timestamp")] + public long LastModifiedTimestamp { get; set; } + [JsonPropertyName("image")] + public byte[] Image { get; set; } + } public UserId LastOpened { get; set; } @@ -25,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { try { - ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson); + ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath); foreach (var profile in profilesJson.Profiles) { @@ -70,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc }); } - JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson); + File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true)); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs deleted file mode 100644 index 6b54898e5..000000000 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Ryujinx.HLE.HOS.Services.Account.Acc.Types; -using System.Text.Json.Serialization; - -namespace Ryujinx.HLE.HOS.Services.Account.Acc -{ - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(ProfilesJson))] - internal partial class ProfilesJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs index 1699abfbd..2382a2554 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - namespace Ryujinx.HLE.HOS.Services.Account.Acc { - [JsonConverter(typeof(TypedStringEnumConverter<AccountState>))] public enum AccountState { Closed, diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs deleted file mode 100644 index 09f9d1421..000000000 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types -{ - internal struct ProfilesJson - { - public List<UserProfileJson> Profiles { get; set; } - public string LastOpened { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs deleted file mode 100644 index 06ff4833f..000000000 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types -{ - internal struct UserProfileJson - { - public string UserId { get; set; } - public string Name { get; set; } - public AccountState AccountState { get; set; } - public AccountState OnlinePlayState { get; set; } - public long LastModifiedTimestamp { get; set; } - public byte[] Image { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs deleted file mode 100644 index e75f62004..000000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; -using System.Text.Json.Serialization; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp -{ - [JsonSerializable(typeof(VirtualAmiiboFile))] - internal partial class AmiiboJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 9166e87fa..4fdeadcb2 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Memory; -using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Mii.Types; @@ -9,6 +8,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; +using System.Text.Json; namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { @@ -16,8 +17,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { private static uint _openedApplicationAreaId; - private static readonly AmiiboJsonSerializerContext SerializerContext = AmiiboJsonSerializerContext.Default; - public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid) { if (useRandomUuid) @@ -174,7 +173,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp if (File.Exists(filePath)) { - virtualAmiiboFile = JsonHelper.DeserializeFromFile(filePath, SerializerContext.VirtualAmiiboFile); + virtualAmiiboFile = JsonSerializer.Deserialize<VirtualAmiiboFile>(File.ReadAllText(filePath), new JsonSerializerOptions(JsonSerializerDefaults.General)); } else { @@ -198,7 +197,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile) { string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"); - JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, SerializerContext.VirtualAmiiboFile); + + File.WriteAllText(filePath, JsonSerializer.Serialize(virtualAmiiboFile)); } } } diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 40eec4a73..f618e38d6 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -56,8 +56,6 @@ namespace Ryujinx.Headless.SDL2 private static bool _enableKeyboard; private static bool _enableMouse; - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - static void Main(string[] args) { Version = ReleaseInformation.GetVersion(); @@ -287,7 +285,10 @@ namespace Ryujinx.Headless.SDL2 try { - config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); + using (Stream stream = File.OpenRead(path)) + { + config = JsonHelper.Deserialize<InputConfig>(stream); + } } catch (JsonException) { diff --git a/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs b/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs deleted file mode 100644 index f81121c28..000000000 --- a/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.App.Common -{ - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(ApplicationMetadata))] - internal partial class ApplicationJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index add6dad3f..43510d5ec 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -10,7 +10,6 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.SystemState; @@ -23,6 +22,7 @@ using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; +using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; using Path = System.IO.Path; namespace Ryujinx.Ui.App.Common @@ -42,8 +42,6 @@ namespace Ryujinx.Ui.App.Common private Language _desiredTitleLanguage; private CancellationTokenSource _cancellationToken; - private static readonly ApplicationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public ApplicationLibrary(VirtualFileSystem virtualFileSystem) { _virtualFileSystem = virtualFileSystem; @@ -492,12 +490,14 @@ namespace Ryujinx.Ui.App.Common appMetadata = new ApplicationMetadata(); - JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); + using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); + + JsonHelper.Serialize(stream, appMetadata, true); } try { - appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata); + appMetadata = JsonHelper.DeserializeFromFile<ApplicationMetadata>(metadataFile); } catch (JsonException) { @@ -510,7 +510,9 @@ namespace Ryujinx.Ui.App.Common { modifyFunction(appMetadata); - JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); + using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); + + JsonHelper.Serialize(stream, appMetadata, true); } return appMetadata; diff --git a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs index 1f9bd0baf..99111ea64 100644 --- a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs +++ b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Configuration +namespace Ryujinx.Ui.Common.Configuration { - [JsonConverter(typeof(TypedStringEnumConverter<AudioBackend>))] public enum AudioBackend { Dummy, diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 14c03957a..e9aec04b2 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -5,7 +5,7 @@ using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.Ui; using System.Collections.Generic; -using System.Text.Json.Nodes; +using System.IO; namespace Ryujinx.Ui.Common.Configuration { @@ -321,14 +321,14 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> /// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks> /// TODO: Remove this when those older versions aren't in use anymore. - public List<JsonObject> KeyboardConfig { get; set; } + public List<object> KeyboardConfig { get; set; } /// <summary> /// Legacy controller control bindings /// </summary> /// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks> /// TODO: Remove this when those older versions aren't in use anymore. - public List<JsonObject> ControllerConfig { get; set; } + public List<object> ControllerConfig { get; set; } /// <summary> /// Input configurations @@ -354,12 +354,11 @@ namespace Ryujinx.Ui.Common.Configuration /// Loads a configuration file from disk /// </summary> /// <param name="path">The path to the JSON configuration file</param> - /// <param name="configurationFileFormat">Parsed configuration file</param> public static bool TryLoad(string path, out ConfigurationFileFormat configurationFileFormat) { try { - configurationFileFormat = JsonHelper.DeserializeFromFile(path, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); + configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path); return configurationFileFormat.Version != 0; } @@ -377,7 +376,8 @@ namespace Ryujinx.Ui.Common.Configuration /// <param name="path">The path to the JSON configuration file</param> public void SaveConfig(string path) { - JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); + using FileStream fileStream = File.Create(path, 4096, FileOptions.WriteThrough); + JsonHelper.Serialize(fileStream, this, true); } } } diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs deleted file mode 100644 index 6ce2ef01a..000000000 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Ryujinx.Common.Utilities; - -namespace Ryujinx.Ui.Common.Configuration -{ - internal static class ConfigurationFileFormatSettings - { - public static readonly ConfigurationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs deleted file mode 100644 index bb8dfb499..000000000 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Configuration -{ - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(ConfigurationFileFormat))] - internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index 82a331c16..bcdd2e70a 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -9,7 +9,6 @@ using Ryujinx.Ui.Common.Configuration.Ui; using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; -using System.Text.Json.Nodes; namespace Ryujinx.Ui.Common.Configuration { @@ -632,8 +631,8 @@ namespace Ryujinx.Ui.Common.Configuration EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, Hotkeys = Hid.Hotkeys, - KeyboardConfig = new List<JsonObject>(), - ControllerConfig = new List<JsonObject>(), + KeyboardConfig = new List<object>(), + ControllerConfig = new List<object>(), InputConfig = Hid.InputConfig, GraphicsBackend = Graphics.GraphicsBackend, PreferredGpu = Graphics.PreferredGpu diff --git a/Ryujinx.Ui.Common/Configuration/System/Language.cs b/Ryujinx.Ui.Common/Configuration/System/Language.cs index 404f8063d..3d2dc9914 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Language.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Language.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Configuration.System +namespace Ryujinx.Ui.Common.Configuration.System { - [JsonConverter(typeof(TypedStringEnumConverter<Language>))] public enum Language { Japanese, diff --git a/Ryujinx.Ui.Common/Configuration/System/Region.cs b/Ryujinx.Ui.Common/Configuration/System/Region.cs index 7dfac6388..fb51e08e5 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Region.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Region.cs @@ -1,9 +1,5 @@ -using Ryujinx.Common.Utilities; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Configuration.System +namespace Ryujinx.Ui.Common.Configuration.System { - [JsonConverter(typeof(TypedStringEnumConverter<Region>))] public enum Region { Japan, diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs deleted file mode 100644 index f412b9504..000000000 --- a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Models.Amiibo -{ - public struct AmiiboApi : IEquatable<AmiiboApi> - { - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonPropertyName("head")] - public string Head { get; set; } - [JsonPropertyName("tail")] - public string Tail { get; set; } - [JsonPropertyName("image")] - public string Image { get; set; } - [JsonPropertyName("amiiboSeries")] - public string AmiiboSeries { get; set; } - [JsonPropertyName("character")] - public string Character { get; set; } - [JsonPropertyName("gameSeries")] - public string GameSeries { get; set; } - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("release")] - public Dictionary<string, string> Release { get; set; } - - [JsonPropertyName("gamesSwitch")] - public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } - - public override string ToString() - { - return Name; - } - - public string GetId() - { - return Head + Tail; - } - - public bool Equals(AmiiboApi other) - { - return Head + Tail == other.Head + other.Tail; - } - - public override bool Equals(object obj) - { - return obj is AmiiboApi other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Head, Tail); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs deleted file mode 100644 index def7d1bcc..000000000 --- a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Models.Amiibo -{ - public class AmiiboApiGamesSwitch - { - [JsonPropertyName("amiiboUsage")] - public List<AmiiboApiUsage> AmiiboUsage { get; set; } - [JsonPropertyName("gameID")] - public List<string> GameId { get; set; } - [JsonPropertyName("gameName")] - public string GameName { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs deleted file mode 100644 index 814573c22..000000000 --- a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Models.Amiibo -{ - public class AmiiboApiUsage - { - [JsonPropertyName("Usage")] - public string Usage { get; set; } - [JsonPropertyName("write")] - public bool Write { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs deleted file mode 100644 index feb7993c1..000000000 --- a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Models.Amiibo -{ - public struct AmiiboJson - { - [JsonPropertyName("amiibo")] - public List<AmiiboApi> Amiibo { get; set; } - [JsonPropertyName("lastUpdated")] - public DateTime LastUpdated { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs deleted file mode 100644 index 4cbb5a7b4..000000000 --- a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Models.Amiibo -{ - [JsonSerializable(typeof(AmiiboJson))] - public partial class AmiiboJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs deleted file mode 100644 index 10d014783..000000000 --- a/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.Ui.Common.Models.Github -{ - public class GithubReleaseAssetJsonResponse - { - public string Name { get; set; } - public string State { get; set; } - public string BrowserDownloadUrl { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs deleted file mode 100644 index 954d03e31..000000000 --- a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Ui.Common.Models.Github -{ - public class GithubReleasesJsonResponse - { - public string Name { get; set; } - public List<GithubReleaseAssetJsonResponse> Assets { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs deleted file mode 100644 index e5fd9d094..000000000 --- a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Ryujinx.Ui.Common.Models.Github -{ - [JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)] - public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs index 3f186ce6b..5ad5924e8 100644 --- a/Ryujinx/Modules/Updater/Updater.cs +++ b/Ryujinx/Modules/Updater/Updater.cs @@ -2,14 +2,14 @@ using Gtk; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; +using Newtonsoft.Json.Linq; using Ryujinx.Common; using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; using Ryujinx.Ui; -using Ryujinx.Ui.Common.Models.Github; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -38,8 +38,6 @@ namespace Ryujinx.Modules private static string _buildUrl; private static long _buildSize; - private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; @@ -109,16 +107,22 @@ namespace Ryujinx.Modules // Fetch latest build information string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); - _buildVer = fetched.Name; + JObject jsonRoot = JObject.Parse(fetchedJson); + JToken assets = jsonRoot["assets"]; - foreach (var asset in fetched.Assets) + _buildVer = (string)jsonRoot["name"]; + + foreach (JToken asset in assets) { - if (asset.Name.StartsWith("ryujinx") && asset.Name.EndsWith(_platformExt)) - { - _buildUrl = asset.BrowserDownloadUrl; + string assetName = (string)asset["name"]; + string assetState = (string)asset["state"]; + string downloadURL = (string)asset["browser_download_url"]; - if (asset.State != "uploaded") + if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) + { + _buildUrl = downloadURL; + + if (assetState != "uploaded") { if (showVersionUpToDate) { diff --git a/Ryujinx/Ui/Windows/AboutWindow.cs b/Ryujinx/Ui/Windows/AboutWindow.cs index 41cf9c013..ea827a92f 100644 --- a/Ryujinx/Ui/Windows/AboutWindow.cs +++ b/Ryujinx/Ui/Windows/AboutWindow.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Ui.Windows { string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)); + _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)); } catch { diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.cs b/Ryujinx/Ui/Windows/AmiiboWindow.cs index 470032373..9140a14e9 100644 --- a/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -3,7 +3,6 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration; -using Ryujinx.Ui.Common.Models.Amiibo; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; @@ -12,15 +11,65 @@ using System.Linq; using System.Net.Http; using System.Reflection; using System.Text; -using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; -using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi; -using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ui.Windows { public partial class AmiiboWindow : Window { + private struct AmiiboJson + { + [JsonPropertyName("amiibo")] + public List<AmiiboApi> Amiibo { get; set; } + [JsonPropertyName("lastUpdated")] + public DateTime LastUpdated { get; set; } + } + + private struct AmiiboApi + { + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("head")] + public string Head { get; set; } + [JsonPropertyName("tail")] + public string Tail { get; set; } + [JsonPropertyName("image")] + public string Image { get; set; } + [JsonPropertyName("amiiboSeries")] + public string AmiiboSeries { get; set; } + [JsonPropertyName("character")] + public string Character { get; set; } + [JsonPropertyName("gameSeries")] + public string GameSeries { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("release")] + public Dictionary<string, string> Release { get; set; } + + [JsonPropertyName("gamesSwitch")] + public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } + } + + private class AmiiboApiGamesSwitch + { + [JsonPropertyName("amiiboUsage")] + public List<AmiiboApiUsage> AmiiboUsage { get; set; } + [JsonPropertyName("gameID")] + public List<string> GameId { get; set; } + [JsonPropertyName("gameName")] + public string GameName { get; set; } + } + + private class AmiiboApiUsage + { + [JsonPropertyName("Usage")] + public string Usage { get; set; } + [JsonPropertyName("write")] + public bool Write { get; set; } + } + private const string DEFAULT_JSON = "{ \"amiibo\": [] }"; public string AmiiboId { get; private set; } @@ -47,8 +96,6 @@ namespace Ryujinx.Ui.Windows private List<AmiiboApi> _amiiboList; - private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo") { Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); @@ -80,9 +127,9 @@ namespace Ryujinx.Ui.Windows if (File.Exists(_amiiboJsonPath)) { - amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); + amiiboJsonString = File.ReadAllText(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -101,7 +148,7 @@ namespace Ryujinx.Ui.Windows } } - _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; + _amiiboList = JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); if (LastScannedAmiiboShowAll) diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs index 9b4befd85..0f0fba0b8 100644 --- a/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -115,8 +115,6 @@ namespace Ryujinx.Ui.Windows private bool _mousePressed; private bool _middleMousePressed; - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { } private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin")) @@ -1122,7 +1120,10 @@ namespace Ryujinx.Ui.Windows try { - config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); + using (Stream stream = File.OpenRead(path)) + { + config = JsonHelper.Deserialize<InputConfig>(stream); + } } catch (JsonException) { } } @@ -1144,7 +1145,9 @@ namespace Ryujinx.Ui.Windows if (profileDialog.Run() == (int)ResponseType.Ok) { string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName); - string jsonString = JsonHelper.Serialize(inputConfig, SerializerContext.InputConfig); + string jsonString; + + jsonString = JsonHelper.Serialize(inputConfig, true); File.WriteAllText(path, jsonString); } diff --git a/Ryujinx/Ui/Windows/DlcWindow.cs b/Ryujinx/Ui/Windows/DlcWindow.cs index b22f15932..9fccec195 100644 --- a/Ryujinx/Ui/Windows/DlcWindow.cs +++ b/Ryujinx/Ui/Windows/DlcWindow.cs @@ -7,13 +7,15 @@ using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; using System.IO; -using GUI = Gtk.Builder.ObjectAttribute; +using System.Text; + +using GUI = Gtk.Builder.ObjectAttribute; +using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; namespace Ryujinx.Ui.Windows { @@ -24,8 +26,6 @@ namespace Ryujinx.Ui.Windows private readonly string _dlcJsonPath; private readonly List<DownloadableContentContainer> _dlcContainerList; - private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - #pragma warning disable CS0649, IDE0044 [GUI] Label _baseTitleInfoLabel; [GUI] TreeView _dlcTreeView; @@ -45,7 +45,7 @@ namespace Ryujinx.Ui.Windows try { - _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, SerializerContext.ListDownloadableContentContainer); + _dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_dlcJsonPath); } catch { @@ -260,7 +260,10 @@ namespace Ryujinx.Ui.Windows while (_dlcTreeView.Model.IterNext(ref parentIter)); } - JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, SerializerContext.ListDownloadableContentContainer); + using (FileStream dlcJsonStream = File.Create(_dlcJsonPath, 4096, FileOptions.WriteThrough)) + { + dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_dlcContainerList, true))); + } Dispose(); } diff --git a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs index 226473fca..4aea58955 100644 --- a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs +++ b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs @@ -7,7 +7,6 @@ using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.Ui.Widgets; @@ -15,8 +14,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; -using SpanHelpers = LibHac.Common.SpanHelpers; +using System.Text; + +using GUI = Gtk.Builder.ObjectAttribute; +using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; namespace Ryujinx.Ui.Windows { @@ -30,7 +31,6 @@ namespace Ryujinx.Ui.Windows private TitleUpdateMetadata _titleUpdateWindowData; private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary; - private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); #pragma warning disable CS0649, IDE0044 [GUI] Label _baseTitleInfoLabel; @@ -53,7 +53,7 @@ namespace Ryujinx.Ui.Windows try { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, SerializerContext.TitleUpdateMetadata); + _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_updateJsonPath); } catch { @@ -192,7 +192,10 @@ namespace Ryujinx.Ui.Windows } } - JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); + using (FileStream dlcJsonStream = File.Create(_updateJsonPath, 4096, FileOptions.WriteThrough)) + { + dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); + } _parent.UpdateGameTable(); From 009c1101d2383ff0e354eac413cebe790c01a89c Mon Sep 17 00:00:00 2001 From: Andrew Glaze <andrew.glaze76@gmail.com> Date: Wed, 22 Mar 2023 08:17:28 -0400 Subject: [PATCH 412/737] CI: add a version tag to correlate release versions with commits (#4572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add step to tag commit with release version * add step to tag commit with release version * Rename step to “Create Tag” * Fix name --- .github/workflows/release.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2c5e2f01..88eb55274 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,6 +112,17 @@ jobs: repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} token: ${{ secrets.RELEASE_TOKEN }} + - name: Create tag + uses: actions/github-script@v5 + with: + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}', + sha: context.sha + }) + flatpak_release: uses: ./.github/workflows/flatpak.yml needs: release From 0992310b7689f2ea1dd828f063e553e1417823ec Mon Sep 17 00:00:00 2001 From: Wunk <wunkolo@gmail.com> Date: Wed, 22 Mar 2023 10:51:21 -0700 Subject: [PATCH 413/737] ARMeilleure: Check for `XSAVE` cpuid flag for AVX{2,512} (#4584) Protection for the `xgetbv` instruction for systems that do not support `xcr0` such as nehalem processors. The `XSAVE` cpuid indicates support for `XSAVE`, `XRESTOR`, `XSETBV`, `XGETBV` while `OSXSAVE` indicates if the operating system itself has `XSAVE` turned on. Both must be checked at the same time. --- ARMeilleure/CodeGen/X86/HardwareCapabilities.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index 63a9e46a2..07cdcd096 100644 --- a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -34,6 +34,12 @@ namespace ARMeilleure.CodeGen.X86 private static uint GetXcr0Eax() { + if (!FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave)) + { + // XSAVE feature required for xgetbv + return 0; + } + ReadOnlySpan<byte> asmGetXcr0 = new byte[] { 0x31, 0xc9, // xor ecx, ecx @@ -70,6 +76,7 @@ namespace ARMeilleure.CodeGen.X86 Sse42 = 1 << 20, Popcnt = 1 << 23, Aes = 1 << 25, + Xsave = 1 << 26, Osxsave = 1 << 27, Avx = 1 << 28, F16c = 1 << 29 @@ -118,9 +125,9 @@ namespace ARMeilleure.CodeGen.X86 public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42); public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt); public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes); - public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128); + public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128); public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx; - public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Osxsave) + public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128 | Xcr0FlagsEax.Opmask | Xcr0FlagsEax.ZmmHi256 | Xcr0FlagsEax.Hi16Zmm); public static bool SupportsAvx512Vl => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512vl) && SupportsAvx512F; public static bool SupportsAvx512Bw => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512bw) && SupportsAvx512F; From 26e30faff3e4489ede6444f895c7f6a7eae78d2d Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 24 Mar 2023 07:56:54 -0300 Subject: [PATCH 414/737] Fix handle leak on IShopServiceAccessServerInterface.CreateServerInterface (#4591) --- .../HOS/Services/Nim/IShopServiceAccessServerInterface.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs index 98f5f6a89..342f1ba1b 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs @@ -14,6 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Nim // CreateServerInterface(pid, handle<unknown>, u64) -> object<nn::ec::IShopServiceAccessServer> public ResultCode CreateServerInterface(ServiceCtx context) { + // Close transfer memory immediately as we don't use it. + context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); + MakeObject(context, new IShopServiceAccessServer()); Logger.Stub?.PrintStub(LogClass.ServiceNim); From 80519af67dd667f00f969cbae6c3e7ed34b4a4f7 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 24 Mar 2023 08:54:58 -0300 Subject: [PATCH 415/737] Update short cache textures if modified (#4586) --- Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 717c5c362..5277e7899 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -130,6 +130,10 @@ namespace Ryujinx.Graphics.Gpu.Image return ref descriptor; } } + else + { + texture.SynchronizeMemory(); + } Items[id] = texture; @@ -233,7 +237,7 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> - /// Queues a request to update a texture's mapping. + /// Queues a request to update a texture's mapping. /// Mapping is updated later to avoid deleting the texture if it is still sparsely mapped. /// </summary> /// <param name="texture">Texture with potential mapping change</param> From 9ecbee8032356cb047d247819a7e192811a23ccc Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 24 Mar 2023 10:19:54 -0300 Subject: [PATCH 416/737] Batch inline index buffer update (#4587) --- .../Engine/Threed/DrawManager.cs | 4 +- .../Engine/Threed/IbStreamer.cs | 93 ++++++++++++++----- 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 61f227d93..7438ba03b 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int firstInstance = (int)_state.State.FirstInstance; - int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(); + int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer); if (inlineIndexCount != 0) { @@ -670,7 +670,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { if (indexedInline) { - int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(); + int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer); BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs index 4862bca18..80d8c00b9 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs @@ -11,9 +11,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct IbStreamer { + private const int BufferCapacity = 256; // Must be a power of 2. + private BufferHandle _inlineIndexBuffer; private int _inlineIndexBufferSize; private int _inlineIndexCount; + private uint[] _buffer; + private int _bufferOffset; /// <summary> /// Indicates if any index buffer data has been pushed. @@ -38,9 +42,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Gets the number of elements on the current inline index buffer, /// while also reseting it to zero for the next draw. /// </summary> + /// <param name="renderer">Host renderer</param> /// <returns>Inline index bufffer count</returns> - public int GetAndResetInlineIndexCount() + public int GetAndResetInlineIndexCount(IRenderer renderer) { + UpdateRemaining(renderer); int temp = _inlineIndexCount; _inlineIndexCount = 0; return temp; @@ -58,16 +64,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed byte i2 = (byte)(argument >> 16); byte i3 = (byte)(argument >> 24); - Span<uint> data = stackalloc uint[4]; + int offset = _inlineIndexCount; - data[0] = i0; - data[1] = i1; - data[2] = i2; - data[3] = i3; - - int offset = _inlineIndexCount * 4; - - renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data)); + PushData(renderer, offset, i0); + PushData(renderer, offset + 1, i1); + PushData(renderer, offset + 2, i2); + PushData(renderer, offset + 3, i3); _inlineIndexCount += 4; } @@ -82,14 +84,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ushort i0 = (ushort)argument; ushort i1 = (ushort)(argument >> 16); - Span<uint> data = stackalloc uint[2]; + int offset = _inlineIndexCount; - data[0] = i0; - data[1] = i1; - - int offset = _inlineIndexCount * 4; - - renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data)); + PushData(renderer, offset, i0); + PushData(renderer, offset + 1, i1); _inlineIndexCount += 2; } @@ -103,13 +101,61 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { uint i0 = (uint)argument; - Span<uint> data = stackalloc uint[1]; + int offset = _inlineIndexCount++; - data[0] = i0; + PushData(renderer, offset, i0); + } - int offset = _inlineIndexCount++ * 4; + /// <summary> + /// Pushes a 32-bit value to the index buffer. + /// </summary> + /// <param name="renderer">Host renderer</param> + /// <param name="offset">Offset where the data should be written, in 32-bit words</param> + /// <param name="value">Index value to be written</param> + private void PushData(IRenderer renderer, int offset, uint value) + { + if (_buffer == null) + { + _buffer = new uint[BufferCapacity]; + } - renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data)); + // We upload data in chunks. + // If we are at the start of a chunk, then the buffer might be full, + // in that case we need to submit any existing data before overwriting the buffer. + int subOffset = offset & (BufferCapacity - 1); + + if (subOffset == 0 && offset != 0) + { + int baseOffset = (offset - BufferCapacity) * sizeof(uint); + BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, BufferCapacity * sizeof(uint)); + renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer)); + } + + _buffer[subOffset] = value; + } + + /// <summary> + /// Makes sure that any pending data is submitted to the GPU before the index buffer is used. + /// </summary> + /// <param name="renderer">Host renderer</param> + private void UpdateRemaining(IRenderer renderer) + { + int offset = _inlineIndexCount; + if (offset == 0) + { + return; + } + + int count = offset & (BufferCapacity - 1); + if (count == 0) + { + count = BufferCapacity; + } + + int baseOffset = (offset - count) * sizeof(uint); + int length = count * sizeof(uint); + BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, length); + renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer).Slice(0, length)); } /// <summary> @@ -117,12 +163,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> /// <param name="renderer">Host renderer</param> /// <param name="offset">Offset where the data will be written</param> + /// <param name="length">Number of bytes that will be written</param> /// <returns>Buffer handle</returns> - private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset) + private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset, int length) { // Calculate a reasonable size for the buffer that can fit all the data, // and that also won't require frequent resizes if we need to push more data. - int size = BitUtils.AlignUp(offset + 0x10, 0x200); + int size = BitUtils.AlignUp(offset + length + 0x10, 0x200); if (_inlineIndexBuffer == BufferHandle.Null) { From 21ce8a9b80c94b0636acb4a8762d8516bdb800e2 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Fri, 24 Mar 2023 22:42:24 +0100 Subject: [PATCH 417/737] chore: Update Ryujinx.SDL2-CS to 2.26.3 (#4479) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 14d440753..921172ab9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,7 +34,7 @@ <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" /> - <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.1-build23" /> + <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" /> <PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> From a34fb0e9392c59e5bd7a764b83f3460bf65b861b Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sun, 26 Mar 2023 11:51:02 +0100 Subject: [PATCH 418/737] Vulkan: Insert barriers before clears (#4596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Vulkan: Insert barriers before clears Newer NVIDIA GPUs seem to be able to start clearing render targets before the last rasterization task is completed, which can cause it to clear a texture while it is being sampled. This change adds a barrier from read to write when doing a clear, assuming it has been sampled in the past. It could be possible for this to be needed for sample into draw by some GPU, but it's not right now afaik. This should fix visual artifacts on newer NVIDIA GPUs and driver combos. Contrary to popular belief, Tetris® Effect: Connected is not affected. Testing welcome, hopefully should fix most cases of this and not cost too much performance. * Visual Studio Moment * Address feedback * Address Feedback 2 --- .../DescriptorSetUpdater.cs | 2 +- Ryujinx.Graphics.Vulkan/EnumConversion.cs | 2 +- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 18 +++++++ Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 ++ Ryujinx.Graphics.Vulkan/TextureStorage.cs | 54 +++++++++++-------- 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 7e126e044..ab3befd81 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Vulkan } else if (texture is TextureView view) { - view.Storage.InsertBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); + view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); _textureRefs[binding] = view.GetImageView(); _samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler(); diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 6c273b050..b69c64aa8 100644 --- a/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -322,7 +322,7 @@ namespace Ryujinx.Graphics.Vulkan GAL.Format.S8Uint => ImageAspectFlags.StencilBit, GAL.Format.D24UnormS8Uint or GAL.Format.D32FloatS8Uint or - GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, + GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, _ => ImageAspectFlags.ColorBit }; } diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 0a1cdcce3..658468d17 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -218,5 +218,23 @@ namespace Ryujinx.Graphics.Vulkan AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit); } + + public void InsertClearBarrier(CommandBufferScoped cbs, int index) + { + if (_colors != null) + { + int realIndex = Array.IndexOf(AttachmentIndices, index); + + if (realIndex != -1) + { + _colors[realIndex].Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit); + } + } + } + + public void InsertClearBarrierDS(CommandBufferScoped cbs) + { + _depthStencil?.Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.EarlyFragmentTestsBit); + } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 6c2f16849..2bec52930 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -226,6 +226,8 @@ namespace Ryujinx.Graphics.Vulkan var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); + FramebufferParams.InsertClearBarrier(Cbs, index); + Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } @@ -256,6 +258,8 @@ namespace Ryujinx.Graphics.Vulkan var attachment = new ClearAttachment(flags, 0, clearValue); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); + FramebufferParams.InsertClearBarrierDS(Cbs); + Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 03a47a091..8ebdd4c0f 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan private AccessFlags _lastModificationAccess; private PipelineStageFlags _lastModificationStage; + private AccessFlags _lastReadAccess; + private PipelineStageFlags _lastReadStage; private int _viewsCount; private ulong _size; @@ -440,31 +442,39 @@ namespace Ryujinx.Graphics.Vulkan _lastModificationStage = stage; } - public void InsertBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) + public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) { + if (_lastReadAccess != AccessFlags.NoneKhr) + { + ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); + + TextureView.InsertImageBarrier( + _gd.Api, + cbs.CommandBuffer, + _imageAuto.Get(cbs).Value, + _lastReadAccess, + dstAccessFlags, + _lastReadStage, + dstStageFlags, + aspectFlags, + 0, + 0, + _info.GetLayers(), + _info.Levels); + + _lastReadAccess = AccessFlags.NoneKhr; + _lastReadStage = PipelineStageFlags.None; + } + } + + public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) + { + _lastReadAccess |= dstAccessFlags; + _lastReadStage |= dstStageFlags; + if (_lastModificationAccess != AccessFlags.NoneKhr) { - ImageAspectFlags aspectFlags; - - if (_info.Format.IsDepthOrStencil()) - { - if (_info.Format == GAL.Format.S8Uint) - { - aspectFlags = ImageAspectFlags.StencilBit; - } - else if (_info.Format == GAL.Format.D16Unorm || _info.Format == GAL.Format.D32Float) - { - aspectFlags = ImageAspectFlags.DepthBit; - } - else - { - aspectFlags = ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit; - } - } - else - { - aspectFlags = ImageAspectFlags.ColorBit; - } + ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); TextureView.InsertImageBarrier( _gd.Api, From f659dcb9d85da034a8863375420cf8f135bd9ec2 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 26 Mar 2023 19:01:30 +0200 Subject: [PATCH 419/737] vulkan: fix broken "VK_EXT_subgroup_size_control" support check (#4607) Not sure since when it was broken... --- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 7e7d30361..cbee37470 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -168,7 +168,9 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt }; - if (Capabilities.SupportsSubgroupSizeControl) + bool supportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control"); + + if (supportsSubgroupSizeControl) { properties2.PNext = &propertiesSubgroupSizeControl; } @@ -292,7 +294,7 @@ namespace Ryujinx.Graphics.Vulkan supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"), supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), - supportedExtensions.Contains("VK_EXT_subgroup_size_control"), + supportsSubgroupSizeControl, featuresShaderInt8.ShaderInt8, supportedExtensions.Contains("VK_EXT_shader_stencil_export"), supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), From f0a3dff136b5fd872c66059cbce779b19515981f Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 27 Mar 2023 04:16:31 +0200 Subject: [PATCH 420/737] vulkan: Remove CreateCommandBufferPool from VulkanInitialization (#4606) It was only called in one place, that can be simplified. --- Ryujinx.Graphics.Vulkan/VulkanInitialization.cs | 5 ----- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index ba3b5ef65..0bb8260a3 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -672,11 +672,6 @@ namespace Ryujinx.Graphics.Vulkan return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray(); } - internal static CommandBufferPool CreateCommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex) - { - return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex); - } - internal unsafe static void CreateDebugMessenger( Vk api, GraphicsDebugLevel logLevel, diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index cbee37470..d8cb5e2b6 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -320,7 +320,7 @@ namespace Ryujinx.Graphics.Vulkan MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); - CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); + CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); DescriptorSetManager = new DescriptorSetManager(_device); From b5032b3c91e9af6a1afb87b4bd8346922084ae75 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 27 Mar 2023 08:40:27 +0200 Subject: [PATCH 421/737] vulkan: Fix access level of extensions fields and make them readonly (#4608) --- Ryujinx.Graphics.Vulkan/VulkanInitialization.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 0bb8260a3..c4e9a6266 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan private const string AppName = "Ryujinx.Graphics.Vulkan"; private const int QueuesCount = 2; - public static string[] DesirableExtensions { get; } = new string[] + private static readonly string[] _desirableExtensions = new string[] { ExtConditionalRendering.ExtensionName, ExtExtendedDynamicState.ExtensionName, @@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan "VK_KHR_portability_subset", // By spec, we should enable this if present. }; - public static string[] RequiredExtensions { get; } = new string[] + private static readonly string[] _requiredExtensions = new string[] { KhrSwapchain.ExtensionName }; @@ -337,14 +337,14 @@ namespace Ryujinx.Graphics.Vulkan { string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName); - if (RequiredExtensions.Contains(extensionName)) + if (_requiredExtensions.Contains(extensionName)) { extensionMatches++; } } } - return extensionMatches == RequiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex; + return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex; } internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount) @@ -626,7 +626,7 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresCustomBorderColor; } - var enabledExtensions = RequiredExtensions.Union(DesirableExtensions.Intersect(supportedExtensions)).ToArray(); + var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(supportedExtensions)).ToArray(); IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; From 7ca779a26d76a3ad1edd94ba6c1cfd7466d73314 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 27 Mar 2023 20:56:36 +0200 Subject: [PATCH 422/737] audout: Fix a possible crash with SDL2 when the SDL2 audio backend is dummy (#4605) This change makes audio device error not fatal. In case of error, the SDL2 audio backend will behave like the dummy backend. --- .../SDL2HardwareDeviceSession.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs index 38e2b1330..14310b934 100644 --- a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs +++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Audio.Backends.SDL2 private ulong _playedSampleCount; private ManualResetEvent _updateRequiredEvent; private uint _outputStream; + private bool _hasSetupError; private SDL_AudioCallback _callbackDelegate; private int _bytesPerFrame; private uint _sampleCount; @@ -42,7 +43,7 @@ namespace Ryujinx.Audio.Backends.SDL2 private void EnsureAudioStreamSetup(AudioBuffer buffer) { uint bufferSampleCount = (uint)GetSampleCount(buffer); - bool needAudioSetup = _outputStream == 0 || + bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) || (bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount); if (needAudioSetup) @@ -51,12 +52,9 @@ namespace Ryujinx.Audio.Backends.SDL2 uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate); - if (newOutputStream == 0) - { - // No stream in place, this is unexpected. - throw new InvalidOperationException($"OpenStream failed with error: \"{SDL_GetError()}\""); - } - else + _hasSetupError = newOutputStream == 0; + + if (!_hasSetupError) { if (_outputStream != 0) { @@ -151,11 +149,20 @@ namespace Ryujinx.Audio.Backends.SDL2 { EnsureAudioStreamSetup(buffer); - SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer)); + if (_outputStream != 0) + { + SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer)); - _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); + _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); - _queuedBuffers.Enqueue(driverBuffer); + _queuedBuffers.Enqueue(driverBuffer); + } + else + { + Interlocked.Add(ref _playedSampleCount, GetSampleCount(buffer)); + + _updateRequiredEvent.Set(); + } } public override void SetVolume(float volume) From 460f96967de6f5cb729ed57baaa4dad2178c8cb6 Mon Sep 17 00:00:00 2001 From: ACGNnsj <ootopoo@vip.qq.com> Date: Tue, 28 Mar 2023 20:59:43 +0800 Subject: [PATCH 423/737] Slight Code Refactoring (#4373) * Simplify return statements by using ternary expressions * Remove a redundant type conversion * Reduce nesting by inverting "if" statements * Try to improve code readability by using LINQ and inverting "if" statements * Try to improve code readability by using LINQ, using ternary expressions, and inverting "if" statements * Add line breaks to long LINQ * Add line breaks to long LINQ --- .../Kernel/SyscallGenerator.cs | 48 +++++----------- .../Kernel/SyscallSyntaxReceiver.cs | 45 +++++++-------- Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs | 7 +-- .../Sdk/Sf/CommandSerialization.cs | 2 +- .../Sdk/Sf/HipcCommandProcessor.cs | 57 ++++++++++--------- Ryujinx.Horizon/Sm/Impl/ServiceManager.cs | 15 +---- 6 files changed, 68 insertions(+), 106 deletions(-) diff --git a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs index 8bc0800c1..51da21878 100644 --- a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs +++ b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; namespace Ryujinx.Horizon.Generators.Kernel { @@ -151,24 +152,15 @@ namespace Ryujinx.Horizon.Generators.Kernel GenerateMethod32(generator, context.Compilation, method); GenerateMethod64(generator, context.Compilation, method); - foreach (var attributeList in method.AttributeLists) + foreach (AttributeSyntax attribute in method.AttributeLists.SelectMany(attributeList => + attributeList.Attributes.Where(attribute => + GetCanonicalTypeName(context.Compilation, attribute) == TypeSvcAttribute))) { - foreach (var attribute in attributeList.Attributes) - { - if (GetCanonicalTypeName(context.Compilation, attribute) != TypeSvcAttribute) - { - continue; - } - - foreach (var attributeArg in attribute.ArgumentList.Arguments) - { - if (attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression) - { - LiteralExpressionSyntax numericLiteral = (LiteralExpressionSyntax)attributeArg.Expression; - syscalls.Add(new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text)); - } - } - } + syscalls.AddRange(from attributeArg in attribute.ArgumentList.Arguments + where attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression + select (LiteralExpressionSyntax)attributeArg.Expression + into numericLiteral + select new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text)); } } @@ -510,28 +502,14 @@ namespace Ryujinx.Horizon.Generators.Kernel private static string GenerateCastFromUInt64(string value, string canonicalTargetTypeName, string targetTypeName) { - if (canonicalTargetTypeName == TypeSystemBoolean) - { - return $"({value} & 1) != 0"; - } - - return $"({targetTypeName}){value}"; + return canonicalTargetTypeName == TypeSystemBoolean ? $"({value} & 1) != 0" : $"({targetTypeName}){value}"; } private static bool IsPointerSized(Compilation compilation, ParameterSyntax parameterSyntax) { - foreach (var attributeList in parameterSyntax.AttributeLists) - { - foreach (var attribute in attributeList.Attributes) - { - if (GetCanonicalTypeName(compilation, attribute) == TypePointerSizedAttribute) - { - return true; - } - } - } - - return false; + return parameterSyntax.AttributeLists.Any(attributeList => + attributeList.Attributes.Any(attribute => + GetCanonicalTypeName(compilation, attribute) == TypePointerSizedAttribute)); } public void Initialize(GeneratorInitializationContext context) diff --git a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs index e2e8e1d35..e480a8593 100644 --- a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs +++ b/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs @@ -16,38 +16,37 @@ namespace Ryujinx.Horizon.Generators.Kernel public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { - if (syntaxNode is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Count != 0) + if (!(syntaxNode is ClassDeclarationSyntax classDeclaration) || classDeclaration.AttributeLists.Count == 0) { - foreach (var attributeList in classDeclaration.AttributeLists) - { - if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "SvcImpl")) - { - foreach (var memberDeclaration in classDeclaration.Members) - { - if (memberDeclaration is MethodDeclarationSyntax methodDeclaration) - { - VisitMethod(methodDeclaration); - } - } + return; + } - break; - } + if (!classDeclaration.AttributeLists.Any(attributeList => + attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "SvcImpl"))) + { + return; + } + + foreach (var memberDeclaration in classDeclaration.Members) + { + if (memberDeclaration is MethodDeclarationSyntax methodDeclaration) + { + VisitMethod(methodDeclaration); } } } private void VisitMethod(MethodDeclarationSyntax methodDeclaration) { - if (methodDeclaration.AttributeLists.Count != 0) + if (methodDeclaration.AttributeLists.Count == 0) { - foreach (var attributeList in methodDeclaration.AttributeLists) - { - if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "Svc")) - { - SvcImplementations.Add(methodDeclaration); - break; - } - } + return; + } + + if (methodDeclaration.AttributeLists.Any(attributeList => + attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "Svc"))) + { + SvcImplementations.Add(methodDeclaration); } } } diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs index fe079d47d..081ce3be5 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs @@ -40,12 +40,7 @@ namespace Ryujinx.Horizon.Sdk.Sf var runtimeMetadata = context.Processor.GetRuntimeMetadata(); Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); - if (result.IsFailure) - { - return result; - } - - return _invoke(ref context, _processor, runtimeMetadata, inRawData, ref outHeader); + return result.IsFailure ? result : _invoke(ref context, _processor, runtimeMetadata, inRawData, ref outHeader); } public static void GetCmifOutHeaderPointer(ref Span<CmifOutHeader> outHeader, ref Span<byte> outRawData) diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs index feb2d666f..4205d3c16 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Horizon.Sdk.Sf public static void SerializeArg<T>(Span<byte> outRawData, int offset, T value) where T : unmanaged { - MemoryMarshal.Cast<byte, T>(outRawData.Slice(offset, Unsafe.SizeOf<T>()))[0] = (T)value; + MemoryMarshal.Cast<byte, T>(outRawData.Slice(offset, Unsafe.SizeOf<T>()))[0] = value; } public static void SerializeCopyHandle(HipcMessageData response, int index, int value) diff --git a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs index 6bba49ae0..a0578d48f 100644 --- a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs +++ b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs @@ -41,10 +41,8 @@ namespace Ryujinx.Horizon.Sdk.Sf { _args = args; - for (int i = 0; i < args.Length; i++) + foreach (CommandArg argInfo in args) { - var argInfo = args[i]; - switch (argInfo.Type) { case CommandArgType.Buffer: @@ -239,14 +237,13 @@ namespace Ryujinx.Horizon.Sdk.Sf { return mode == HipcBufferMode.NonSecure; } - else if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice)) + + if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice)) { return mode == HipcBufferMode.NonDevice; } - else - { - return mode == HipcBufferMode.Normal; - } + + return mode == HipcBufferMode.Normal; } public void SetOutBuffers(HipcMessageData response, bool[] isBufferMapAlias) @@ -261,28 +258,30 @@ namespace Ryujinx.Horizon.Sdk.Sf } var flags = _args[i].BufferFlags; - if (flags.HasFlag(HipcBufferFlags.Out)) + if (!flags.HasFlag(HipcBufferFlags.Out)) { - var buffer = _bufferRanges[i]; + continue; + } - if (flags.HasFlag(HipcBufferFlags.Pointer)) + var buffer = _bufferRanges[i]; + + if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); + } + else if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + { + if (!isBufferMapAlias[i]) { response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); } - else if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + else { - if (!isBufferMapAlias[i]) - { - response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); - } - else - { - response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(0UL, 0, recvPointerIndex); - } + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(0UL, 0, recvPointerIndex); } - - recvPointerIndex++; } + + recvPointerIndex++; } } @@ -339,15 +338,17 @@ namespace Ryujinx.Horizon.Sdk.Sf int inObjectIndex = 0; - for (int i = 0; i < _args.Length; i++) + foreach (CommandArg t in _args) { - if (_args[i].Type == CommandArgType.InObject) + if (t.Type != CommandArgType.InObject) { - int index = inObjectIndex++; - var inObject = inObjects[index]; - - objects[index] = inObject?.ServiceObject; + continue; } + + int index = inObjectIndex++; + var inObject = inObjects[index]; + + objects[index] = inObject?.ServiceObject; } return Result.Success; diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs index 44a1ec46e..d1f94267c 100644 --- a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs +++ b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs @@ -37,12 +37,7 @@ namespace Ryujinx.Horizon.Sm.Impl result = GetServiceImpl(out handle, ref _services[serviceIndex]); - if (result == KernelResult.SessionCountExceeded) - { - return SmResult.OutOfSessions; - } - - return result; + return result == KernelResult.SessionCountExceeded ? SmResult.OutOfSessions : result; } private Result GetServiceImpl(out int handle, ref ServiceInfo serviceInfo) @@ -61,13 +56,7 @@ namespace Ryujinx.Horizon.Sm.Impl } // TODO: Validation with GetProcessInfo etc. - - if (HasServiceInfo(name)) - { - return SmResult.AlreadyRegistered; - } - - return RegisterServiceImpl(out handle, processId, name, maxSessions, isLight); + return HasServiceInfo(name) ? SmResult.AlreadyRegistered : RegisterServiceImpl(out handle, processId, name, maxSessions, isLight); } public Result RegisterServiceForSelf(out int handle, ServiceName name, int maxSessions) From 8198b99935f562ffb2fb9a75175a8df24d235152 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Thu, 30 Mar 2023 16:07:07 -0400 Subject: [PATCH 424/737] Fix Linux hang on shutdown (#4617) * Rework StdErr-to-log redirection to use built-in FileStream, and do reads asynchronously to avoid hanging the process shutdown. * set _disposable to false ASAP --- Ryujinx.Common/SystemInterop/StdErrAdapter.cs | 50 ++++-- Ryujinx.Common/SystemInterop/UnixStream.cs | 155 ------------------ 2 files changed, 33 insertions(+), 172 deletions(-) delete mode 100644 Ryujinx.Common/SystemInterop/UnixStream.cs diff --git a/Ryujinx.Common/SystemInterop/StdErrAdapter.cs b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs index efb142184..b1ed7b689 100644 --- a/Ryujinx.Common/SystemInterop/StdErrAdapter.cs +++ b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs @@ -1,18 +1,21 @@ +using Microsoft.Win32.SafeHandles; using Ryujinx.Common.Logging; using System; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; +using System.Threading.Tasks; namespace Ryujinx.Common.SystemInterop { public partial class StdErrAdapter : IDisposable { private bool _disposable = false; - private UnixStream _pipeReader; - private UnixStream _pipeWriter; - private Thread _worker; + private Stream _pipeReader; + private Stream _pipeWriter; + private CancellationTokenSource _cancellationTokenSource; + private Task _worker; public StdErrAdapter() { @@ -31,37 +34,39 @@ namespace Ryujinx.Common.SystemInterop (int readFd, int writeFd) = MakePipe(); dup2(writeFd, stdErrFileno); - _pipeReader = new UnixStream(readFd); - _pipeWriter = new UnixStream(writeFd); + _pipeReader = CreateFileDescriptorStream(readFd); + _pipeWriter = CreateFileDescriptorStream(writeFd); - _worker = new Thread(EventWorker); + _cancellationTokenSource = new CancellationTokenSource(); + _worker = Task.Run(async () => await EventWorkerAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token); _disposable = true; - _worker.Start(); } - + [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] - private void EventWorker() + private async Task EventWorkerAsync(CancellationToken cancellationToken) { - TextReader reader = new StreamReader(_pipeReader); + using TextReader reader = new StreamReader(_pipeReader, leaveOpen: true); string line; - while ((line = reader.ReadLine()) != null) + while (cancellationToken.IsCancellationRequested == false && (line = await reader.ReadLineAsync(cancellationToken)) != null) { Logger.Error?.PrintRawMsg(line); } } - + private void Dispose(bool disposing) { if (_disposable) { + _disposable = false; + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { + _cancellationTokenSource.Cancel(); + _worker.Wait(0); _pipeReader?.Close(); _pipeWriter?.Close(); } - - _disposable = false; } } @@ -74,11 +79,11 @@ namespace Ryujinx.Common.SystemInterop private static partial int dup2(int fd, int fd2); [LibraryImport("libc", SetLastError = true)] - private static unsafe partial int pipe(int* pipefd); + private static partial int pipe(Span<int> pipefd); - private static unsafe (int, int) MakePipe() + private static (int, int) MakePipe() { - int *pipefd = stackalloc int[2]; + Span<int> pipefd = stackalloc int[2]; if (pipe(pipefd) == 0) { @@ -89,5 +94,16 @@ namespace Ryujinx.Common.SystemInterop throw new(); } } + + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private static Stream CreateFileDescriptorStream(int fd) + { + return new FileStream( + new SafeFileHandle((IntPtr)fd, ownsHandle: true), + FileAccess.ReadWrite + ); + } + } } diff --git a/Ryujinx.Common/SystemInterop/UnixStream.cs b/Ryujinx.Common/SystemInterop/UnixStream.cs deleted file mode 100644 index 1d6449974..000000000 --- a/Ryujinx.Common/SystemInterop/UnixStream.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Common.SystemInterop -{ - [SupportedOSPlatform("linux")] - [SupportedOSPlatform("macos")] - public partial class UnixStream : Stream, IDisposable - { - private const int InvalidFd = -1; - - private int _fd; - - [LibraryImport("libc", SetLastError = true)] - private static partial long read(int fd, IntPtr buf, ulong count); - - [LibraryImport("libc", SetLastError = true)] - private static partial long write(int fd, IntPtr buf, ulong count); - - [LibraryImport("libc", SetLastError = true)] - private static partial int close(int fd); - - public UnixStream(int fd) - { - if (InvalidFd == fd) - { - throw new ArgumentException("Invalid file descriptor"); - } - - _fd = fd; - - CanRead = read(fd, IntPtr.Zero, 0) != -1; - CanWrite = write(fd, IntPtr.Zero, 0) != -1; - } - - ~UnixStream() - { - Close(); - } - - public override bool CanRead { get; } - public override bool CanWrite { get; } - public override bool CanSeek => false; - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override void Flush() - { - } - - public override unsafe int Read([In, Out] byte[] buffer, int offset, int count) - { - if (offset < 0 || offset > (buffer.Length - count) || count < 0) - { - throw new ArgumentOutOfRangeException(); - } - - if (buffer.Length == 0) - { - return 0; - } - - long r = 0; - fixed (byte* buf = &buffer[offset]) - { - do - { - r = read(_fd, (IntPtr)buf, (ulong)count); - } while (ShouldRetry(r)); - } - - return (int)r; - } - - public override unsafe void Write(byte[] buffer, int offset, int count) - { - if (offset < 0 || offset > (buffer.Length - count) || count < 0) - { - throw new ArgumentOutOfRangeException(); - } - - if (buffer.Length == 0) - { - return; - } - - fixed (byte* buf = &buffer[offset]) - { - long r = 0; - do { - r = write(_fd, (IntPtr)buf, (ulong)count); - } while (ShouldRetry(r)); - } - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Close() - { - if (_fd == InvalidFd) - { - return; - } - - Flush(); - - int r; - do { - r = close(_fd); - } while (ShouldRetry(r)); - - _fd = InvalidFd; - } - - void IDisposable.Dispose() - { - Close(); - } - - private bool ShouldRetry(long r) - { - if (r == -1) - { - const int eintr = 4; - - int errno = Marshal.GetLastPInvokeError(); - - if (errno == eintr) - { - return true; - } - - throw new SystemException($"Operation failed with error 0x{errno:X}"); - } - - return false; - } - } -} From 4c2d9ff3ff9d7afb1fd0bd764bee5931fa5f053c Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Fri, 31 Mar 2023 21:16:46 +0200 Subject: [PATCH 425/737] HLE: Refactoring of ApplicationLoader (#4480) * HLE: Refactoring of ApplicationLoader * Fix SDL2 Headless * Addresses gdkchan feedback * Fixes LoadUnpackedNca RomFS loading * remove useless casting * Cleanup and fixe empty application name * Remove ProcessInfo * Fixes typo * ActiveProcess to ActiveApplication * Update check * Clean using. * Use the correct filepath when loading Homebrew.npdm * Fix NRE in ProcessResult if MetaLoader is null * Add more checks for valid processId & return success * Add missing logging statement for npdm error * Return result for LoadKip() * Move error logging out of PFS load extension method This avoids logging "Could not find Main NCA" followed by "Loading main..." when trying to start hbl. * Fix GUIs not checking load results * Fix style and formatting issues * Fix formatting and wording * gtk: Refactor LoadApplication() --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- Ryujinx.Ava/AppHost.cs | 77 +- Ryujinx.Ava/Common/ApplicationHelper.cs | 3 +- .../UI/ViewModels/MainWindowViewModel.cs | 10 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 3 +- .../UI/Views/Main/MainMenuBarView.axaml.cs | 12 +- Ryujinx.HLE/FileSystem/VirtualFileSystem.cs | 6 +- Ryujinx.HLE/HOS/ApplicationLoader.cs | 908 ------------------ Ryujinx.HLE/HOS/Horizon.cs | 5 +- .../HOS/Kernel/Process/ProcessTamperInfo.cs | 2 +- Ryujinx.HLE/HOS/ModLoader.cs | 9 +- .../Acc/IAccountServiceForApplication.cs | 2 +- .../ILibraryAppletSelfAccessor.cs | 4 +- .../ApplicationProxy/IApplicationFunctions.cs | 35 +- .../Services/Arp/ApplicationLaunchProperty.cs | 2 +- .../Caps/IScreenShotApplicationService.cs | 6 +- Ryujinx.HLE/HOS/Services/Fatal/IService.cs | 4 +- .../Friend/ServiceCreator/IFriendService.cs | 2 +- .../HOS/Services/Fs/IFileSystemProxy.cs | 2 +- .../Services/Ns/Aoc/IAddOnContentManager.cs | 14 +- .../Ns/IApplicationManagerInterface.cs | 10 +- ...ReadOnlyApplicationControlDataInterface.cs | 9 +- .../IParentalControlService.cs | 4 +- .../QueryPlayStatisticsManager.cs | 7 +- .../Extensions/FileSystemExtensions.cs | 133 +++ .../Extensions/LocalFileSystemExtensions.cs | 39 + .../Extensions/MetaLoaderExtensions.cs | 61 ++ .../Processes/Extensions/NcaExtensions.cs | 175 ++++ .../PartitionFileSystemExtensions.cs | 177 ++++ Ryujinx.HLE/Loaders/Processes/ProcessConst.cs | 33 + .../Loaders/Processes/ProcessLoader.cs | 244 +++++ .../Processes/ProcessLoaderHelper.cs} | 277 +++--- .../Loaders/Processes/ProcessResult.cs | 92 ++ Ryujinx.HLE/Switch.cs | 29 +- Ryujinx.Headless.SDL2/Program.cs | 57 +- Ryujinx.Headless.SDL2/WindowBase.cs | 16 +- Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 131 ++- Ryujinx/Program.cs | 2 +- Ryujinx/Ui/MainWindow.cs | 263 +++-- Ryujinx/Ui/RendererWidgetBase.cs | 18 +- Ryujinx/Ui/Widgets/GameTableContextMenu.cs | 3 +- Ryujinx/Ui/Windows/TitleUpdateWindow.cs | 3 +- 41 files changed, 1567 insertions(+), 1322 deletions(-) delete mode 100644 Ryujinx.HLE/HOS/ApplicationLoader.cs create mode 100644 Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs create mode 100644 Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs create mode 100644 Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs create mode 100644 Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs create mode 100644 Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs create mode 100644 Ryujinx.HLE/Loaders/Processes/ProcessConst.cs create mode 100644 Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs rename Ryujinx.HLE/{HOS/ProgramLoader.cs => Loaders/Processes/ProcessLoaderHelper.cs} (54%) create mode 100644 Ryujinx.HLE/Loaders/Processes/ProcessResult.cs diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index eb22b39e9..3cdb3906f 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -320,10 +320,14 @@ namespace Ryujinx.Ava _viewModel.IsGameRunning = true; - string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty : $" - {Device.Application.TitleName}"; - string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty : $" v{Device.Application.DisplayVersion}"; - string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty : $" ({Device.Application.TitleIdText.ToUpper()})"; - string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)"; + var activeProcess = Device.Processes.ActiveApplication; + var nacp = activeProcess.ApplicationControlProperties; + int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage; + + string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}"; + string titleVersionSection = string.IsNullOrWhiteSpace(nacp.DisplayVersionString.ToString()) ? string.Empty : $" v{nacp.DisplayVersionString.ToString()}"; + string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})"; + string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; Dispatcher.UIThread.InvokeAsync(() => { @@ -423,9 +427,9 @@ namespace Ryujinx.Ava private void Dispose() { - if (Device.Application != null) + if (Device.Processes != null) { - _viewModel.UpdateGameMetadata(Device.Application.TitleIdText); + _viewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); } ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; @@ -539,7 +543,12 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - Device.LoadNca(ApplicationPath); + if (!Device.LoadNca(ApplicationPath)) + { + Device.Dispose(); + + return false; + } } else if (Directory.Exists(ApplicationPath)) { @@ -554,13 +563,23 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - Device.LoadCart(ApplicationPath, romFsFiles[0]); + if (!Device.LoadCart(ApplicationPath, romFsFiles[0])) + { + Device.Dispose(); + + return false; + } } else { Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - Device.LoadCart(ApplicationPath); + if (!Device.LoadCart(ApplicationPath)) + { + Device.Dispose(); + + return false; + } } } else if (File.Exists(ApplicationPath)) @@ -571,7 +590,12 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - Device.LoadXci(ApplicationPath); + if (!Device.LoadXci(ApplicationPath)) + { + Device.Dispose(); + + return false; + } break; } @@ -579,7 +603,12 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - Device.LoadNca(ApplicationPath); + if (!Device.LoadNca(ApplicationPath)) + { + Device.Dispose(); + + return false; + } break; } @@ -588,7 +617,12 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - Device.LoadNsp(ApplicationPath); + if (!Device.LoadNsp(ApplicationPath)) + { + Device.Dispose(); + + return false; + } break; } @@ -598,13 +632,18 @@ namespace Ryujinx.Ava try { - Device.LoadProgram(ApplicationPath); + if (!Device.LoadProgram(ApplicationPath)) + { + Device.Dispose(); + + return false; + } } catch (ArgumentOutOfRangeException) { Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); - Dispose(); + Device.Dispose(); return false; } @@ -617,14 +656,14 @@ namespace Ryujinx.Ava { Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - Dispose(); + Device.Dispose(); return false; } - DiscordIntegrationModule.SwitchToPlayingState(Device.Application.TitleIdText, Device.Application.TitleName); + DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name); - _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata => + _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow.ToString(); }); @@ -950,7 +989,7 @@ namespace Ryujinx.Ava { if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) { - Device.Application.DiskCacheLoadState?.Cancel(); + Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); } }); @@ -1088,4 +1127,4 @@ namespace Ryujinx.Ava return state; } } -} \ No newline at end of file +} diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 276d18745..161ef8596 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -19,6 +19,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common.Helper; using System; using System.Buffers; @@ -227,7 +228,7 @@ namespace Ryujinx.Ava.Common return; } - (Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); if (updatePatchNca != null) { patchNca = updatePatchNca; diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index a3663af3e..d5ff78545 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1208,10 +1208,10 @@ namespace Ryujinx.Ava.UI.ViewModels public void SetUIProgressHandlers(Switch emulationContext) { - if (emulationContext.Application.DiskCacheLoadState != null) + if (emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) { - emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; - emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler; + emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler; } emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; @@ -1705,8 +1705,8 @@ namespace Ryujinx.Ava.UI.ViewModels if (string.IsNullOrWhiteSpace(titleName)) { - LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Application.TitleName); - TitleName = AppHost.Device.Application.TitleName; + LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name); + TitleName = AppHost.Device.Processes.ActiveApplication.Name; } SwitchToRenderer(startFullscreen); diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index dd9e1b961..0798502cd 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -17,6 +17,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; +using Ryujinx.Ui.App.Common; using System; using System.Collections.Generic; using System.IO; @@ -162,7 +163,7 @@ public class TitleUpdateViewModel : BaseModel try { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); if (controlNca != null && patchNca != null) { diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 1c6f4265c..30d411508 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -126,7 +126,7 @@ namespace Ryujinx.Ava.UI.Views.Main if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId)) { - string titleId = ViewModel.AppHost.Device.Application.TitleIdText.ToUpper(); + string titleId = ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(); AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId); await window.ShowDialog(Window); @@ -148,13 +148,11 @@ namespace Ryujinx.Ava.UI.Views.Main return; } - ApplicationLoader application = ViewModel.AppHost.Device.Application; - if (application != null) - { - await new CheatWindow(Window.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(Window); + string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString(); - ViewModel.AppHost.Device.EnableCheats(); - } + await new CheatWindow(Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name).ShowDialog(Window); + + ViewModel.AppHost.Device.EnableCheats(); } private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 3f94ce61b..1b3968eab 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -20,7 +20,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; - using Path = System.IO.Path; using RightsId = LibHac.Fs.RightsId; @@ -146,6 +145,7 @@ namespace Ryujinx.HLE.FileSystem return $"{basePath}:/{fileName}"; } + return null; } @@ -191,7 +191,7 @@ namespace Ryujinx.HLE.FileSystem fsServerClient = horizon.CreatePrivilegedHorizonClient(); var fsServer = new FileSystemServer(fsServerClient); - RandomDataGenerator randomGenerator = buffer => Random.Shared.NextBytes(buffer); + RandomDataGenerator randomGenerator = Random.Shared.NextBytes; DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet, fsServer, randomGenerator); @@ -264,7 +264,7 @@ namespace Ryujinx.HLE.FileSystem if (result.IsSuccess()) { - Ticket ticket = new Ticket(ticketFile.Get.AsStream()); + Ticket ticket = new(ticketFile.Get.AsStream()); var titleKey = ticket.GetTitleKey(KeySet); if (titleKey != null) diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs deleted file mode 100644 index 82bd9b312..000000000 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ /dev/null @@ -1,908 +0,0 @@ -using LibHac; -using LibHac.Account; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Fs.Shim; -using LibHac.FsSystem; -using LibHac.Loader; -using LibHac.Ncm; -using LibHac.Ns; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Executables; -using Ryujinx.Memory; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using static Ryujinx.HLE.HOS.ModLoader; -using ApplicationId = LibHac.Ncm.ApplicationId; -using Path = System.IO.Path; - -namespace Ryujinx.HLE.HOS -{ - using JsonHelper = Common.Utilities.JsonHelper; - - public class ApplicationLoader - { - // Binaries from exefs are loaded into mem in this order. Do not change. - internal static readonly string[] ExeFsPrefixes = - { - "rtld", - "main", - "subsdk0", - "subsdk1", - "subsdk2", - "subsdk3", - "subsdk4", - "subsdk5", - "subsdk6", - "subsdk7", - "subsdk8", - "subsdk9", - "sdk" - }; - - private readonly Switch _device; - private string _titleName; - private string _displayVersion; - private BlitStruct<ApplicationControlProperty> _controlData; - - public BlitStruct<ApplicationControlProperty> ControlData => _controlData; - public string TitleName => _titleName; - public string DisplayVersion => _displayVersion; - - public ulong TitleId { get; private set; } - public bool TitleIs64Bit { get; private set; } - - public string TitleIdText => TitleId.ToString("x16"); - - public IDiskCacheLoadState DiskCacheLoadState { get; private set; } - - public ApplicationLoader(Switch device) - { - _device = device; - _controlData = new BlitStruct<ApplicationControlProperty>(1); - } - - public void LoadCart(string exeFsDir, string romFsFile = null) - { - LocalFileSystem codeFs = new LocalFileSystem(exeFsDir); - - MetaLoader metaData = ReadNpdm(codeFs); - - _device.Configuration.VirtualFileSystem.ModLoader.CollectMods( - new[] { TitleId }, - _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); - - if (TitleId != 0) - { - EnsureSaveData(new ApplicationId(TitleId)); - } - - ulong pid = LoadExeFs(codeFs, string.Empty, metaData); - - if (romFsFile != null) - { - _device.Configuration.VirtualFileSystem.LoadRomFs(pid, romFsFile); - } - } - - public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) - { - Nca mainNca = null; - Nca patchNca = null; - Nca controlNca = null; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); - - if (ncaProgramIndex != programIndex) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } - } - else if (nca.Header.ContentType == NcaContentType.Control) - { - controlNca = nca; - } - } - - return (mainNca, patchNca, controlNca); - } - - public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex) - { - Nca patchNca = null; - Nca controlNca = null; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); - - if (ncaProgramIndex != programIndex) - { - continue; - } - - if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId) - { - break; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - patchNca = nca; - } - else if (nca.Header.ContentType == NcaContentType.Control) - { - controlNca = nca; - } - } - - return (patchNca, controlNca); - } - - public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath) - { - updatePath = null; - - if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase)) - { - // Clear the program index part. - titleIdBase &= 0xFFFFFFFFFFFFFFF0; - - // Load update informations if existing. - string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); - - if (File.Exists(titleUpdateMetadataPath)) - { - updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; - - if (File.Exists(updatePath)) - { - FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); - - return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex); - } - } - } - - return (null, null); - } - - public void LoadXci(string xciFile) - { - FileStream file = new FileStream(xciFile, FileMode.Open, FileAccess.Read); - Xci xci = new Xci(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage()); - - if (!xci.HasPartition(XciPartitionType.Secure)) - { - Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI secure partition"); - - return; - } - - PartitionFileSystem securePartition = xci.OpenPartition(XciPartitionType.Secure); - - Nca mainNca; - Nca patchNca; - Nca controlNca; - - try - { - (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, securePartition, _device.Configuration.UserChannelPersistence.Index); - - RegisterProgramMapInfo(securePartition).ThrowIfFailure(); - } - catch (Exception e) - { - Logger.Error?.Print(LogClass.Loader, $"Unable to load XCI: {e.Message}"); - - return; - } - - if (mainNca == null) - { - Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find Main NCA"); - - return; - } - - _device.Configuration.ContentManager.LoadEntries(_device); - _device.Configuration.ContentManager.ClearAocData(); - _device.Configuration.ContentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel); - - LoadNca(mainNca, patchNca, controlNca); - } - - public void LoadNsp(string nspFile) - { - FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); - - Nca mainNca; - Nca patchNca; - Nca controlNca; - - try - { - (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, nsp, _device.Configuration.UserChannelPersistence.Index); - - RegisterProgramMapInfo(nsp).ThrowIfFailure(); - } - catch (Exception e) - { - Logger.Error?.Print(LogClass.Loader, $"Unable to load NSP: {e.Message}"); - - return; - } - - if (mainNca != null) - { - _device.Configuration.ContentManager.ClearAocData(); - _device.Configuration.ContentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel); - - LoadNca(mainNca, patchNca, controlNca); - - return; - } - - // This is not a normal NSP, it's actually a ExeFS as a NSP - LoadExeFs(nsp, null, isHomebrew: true); - } - - public void LoadNca(string ncaFile) - { - FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); - Nca nca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); - - LoadNca(nca, null, null); - } - - public void LoadServiceNca(string ncaFile) - { - FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); - Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); - - if (mainNca.Header.ContentType != NcaContentType.Program) - { - Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); - - return; - } - - IFileSystem codeFs = null; - - if (mainNca.CanOpenSection(NcaSectionType.Code)) - { - codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel); - } - - if (codeFs == null) - { - Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA"); - - return; - } - - using var npdmFile = new UniqueRef<IFile>(); - - Result result = codeFs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); - - MetaLoader metaData; - - npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure(); - - var npdmBuffer = new byte[fileSize]; - npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure(); - - metaData = new MetaLoader(); - metaData.Load(npdmBuffer).ThrowIfFailure(); - - NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length]; - - for (int i = 0; i < nsos.Length; i++) - { - string name = ExeFsPrefixes[i]; - - if (!codeFs.FileExists($"/{name}")) - { - continue; // File doesn't exist, skip. - } - - Logger.Info?.Print(LogClass.Loader, $"Loading {name}..."); - - using var nsoFile = new UniqueRef<IFile>(); - - codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); - } - - // Collect the nsos, ignoring ones that aren't used. - NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); - - string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString; - bool usePtc = _device.System.EnablePtc; - - metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false); - ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); - - string titleIdText = npdm.Aci.ProgramId.Value.ToString("x16"); - bool titleIs64Bit = (npdm.Meta.Flags & 1) != 0; - - string programName = Encoding.ASCII.GetString(npdm.Meta.ProgramName).TrimEnd('\0'); - - Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]"); - } - - private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca) - { - if (mainNca.Header.ContentType != NcaContentType.Program) - { - Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); - - return; - } - - IStorage dataStorage = null; - IFileSystem codeFs = null; - - (Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), _device.Configuration.UserChannelPersistence.Index, out _); - - if (updatePatchNca != null) - { - patchNca = updatePatchNca; - } - - if (updateControlNca != null) - { - controlNca = updateControlNca; - } - - // Load program 0 control NCA as we are going to need it for display version. - (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); - - // Load Aoc - string titleAocMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); - - if (File.Exists(titleAocMetadataPath)) - { - List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath); - - foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) - { - foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) - { - if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled) - { - _device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath); - } - else - { - Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {downloadableContentContainer.ContainerPath}. It may have been moved or renamed."); - } - } - } - } - - if (patchNca == null) - { - if (mainNca.CanOpenSection(NcaSectionType.Data)) - { - dataStorage = mainNca.OpenStorage(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); - } - - if (mainNca.CanOpenSection(NcaSectionType.Code)) - { - codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel); - } - } - else - { - if (patchNca.CanOpenSection(NcaSectionType.Data)) - { - dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); - } - - if (patchNca.CanOpenSection(NcaSectionType.Code)) - { - codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, _device.System.FsIntegrityCheckLevel); - } - } - - if (codeFs == null) - { - Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA"); - - return; - } - - MetaLoader metaData = ReadNpdm(codeFs); - - _device.Configuration.VirtualFileSystem.ModLoader.CollectMods( - _device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId), - _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); - - string displayVersion = string.Empty; - - if (controlNca != null) - { - ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion); - } - else - { - ControlData.ByteSpan.Clear(); - } - - // NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application. - // BODY: As such, to avoid PTC cache confusion, we only trust the the program 0 display version when launching a sub program. - if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0) - { - string dummyTitleName = ""; - BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1); - - ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion); - } - - _displayVersion = displayVersion; - - ulong pid = LoadExeFs(codeFs, displayVersion, metaData); - - if (dataStorage == null) - { - Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); - } - else - { - IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage); - - _device.Configuration.VirtualFileSystem.SetRomFs(pid, newStorage.AsStream(FileAccess.Read)); - } - - // Don't create save data for system programs. - if (TitleId != 0 && (TitleId < SystemProgramId.Start.Value || TitleId > SystemAppletId.End.Value)) - { - // Multi-program applications can technically use any program ID for the main program, but in practice they always use 0 in the low nibble. - // We'll know if this changes in the future because stuff will get errors when trying to mount the correct save. - EnsureSaveData(new ApplicationId(TitleId & ~0xFul)); - } - - Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); - } - - // Sets TitleId, so be sure to call before using it - private MetaLoader ReadNpdm(IFileSystem fs) - { - using var npdmFile = new UniqueRef<IFile>(); - - Result result = fs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); - - MetaLoader metaData; - - if (ResultFs.PathNotFound.Includes(result)) - { - Logger.Warning?.Print(LogClass.Loader, "NPDM file not found, using default values!"); - - metaData = GetDefaultNpdm(); - } - else - { - npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure(); - - var npdmBuffer = new byte[fileSize]; - npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure(); - - metaData = new MetaLoader(); - metaData.Load(npdmBuffer).ThrowIfFailure(); - } - - metaData.GetNpdm(out var npdm).ThrowIfFailure(); - - TitleId = npdm.Aci.ProgramId.Value; - TitleIs64Bit = (npdm.Meta.Flags & 1) != 0; - _device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId); - - return metaData; - } - - private static void ReadControlData(Switch device, Nca controlNca, ref BlitStruct<ApplicationControlProperty> controlData, ref string titleName, ref string displayVersion) - { - using var controlFile = new UniqueRef<IFile>(); - - IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); - Result result = controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read); - - if (result.IsSuccess()) - { - result = controlFile.Get.Read(out long bytesRead, 0, controlData.ByteSpan, ReadOption.None); - - if (result.IsSuccess() && bytesRead == controlData.ByteSpan.Length) - { - titleName = controlData.Value.Title[(int)device.System.State.DesiredTitleLanguage].NameString.ToString(); - - if (string.IsNullOrWhiteSpace(titleName)) - { - titleName = controlData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); - } - - displayVersion = controlData.Value.DisplayVersionString.ToString(); - } - } - else - { - controlData.ByteSpan.Clear(); - } - } - - private ulong LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) - { - if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) - { - metaData = null; // TODO: Check if we should retain old npdm. - } - - metaData ??= ReadNpdm(codeFs); - - NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length]; - - for (int i = 0; i < nsos.Length; i++) - { - string name = ExeFsPrefixes[i]; - - if (!codeFs.FileExists($"/{name}")) - { - continue; // File doesn't exist, skip. - } - - Logger.Info?.Print(LogClass.Loader, $"Loading {name}..."); - - using var nsoFile = new UniqueRef<IFile>(); - - codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); - } - - // ExeFs file replacements. - ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos); - - // Collect the nsos, ignoring ones that aren't used. - NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); - - // Take the npdm from mods if present. - if (modLoadResult.Npdm != null) - { - metaData = modLoadResult.Npdm; - } - - _device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); - - _device.Configuration.ContentManager.LoadEntries(_device); - - bool usePtc = _device.System.EnablePtc; - - // Don't use PPTC if ExeFs files have been replaced. - usePtc &= !modLoadResult.Modified; - - if (_device.System.EnablePtc && !usePtc) - { - Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PPTC disabled."); - } - - Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText; - _device.Gpu.HostInitalized.Set(); - - MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode; - - if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) - { - memoryManagerMode = MemoryManagerMode.SoftwarePageTable; - } - - // We allow it for nx-hbloader because it can be used to launch homebrew. - bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew; - - metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit); - ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); - - DiskCacheLoadState = result.DiskCacheLoadState; - - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); - - return result.ProcessId; - } - - public void LoadProgram(string filePath) - { - MetaLoader metaData = GetDefaultNpdm(); - metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true); - - bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; - - IExecutable executable; - Stream romfsStream = null; - - if (isNro) - { - FileStream input = new FileStream(filePath, FileMode.Open); - NroExecutable obj = new NroExecutable(input.AsStorage()); - - executable = obj; - - // Homebrew NRO can actually have some data after the actual NRO. - if (input.Length > obj.FileSize) - { - input.Position = obj.FileSize; - - BinaryReader reader = new BinaryReader(input); - - uint asetMagic = reader.ReadUInt32(); - if (asetMagic == 0x54455341) - { - uint asetVersion = reader.ReadUInt32(); - if (asetVersion == 0) - { - ulong iconOffset = reader.ReadUInt64(); - ulong iconSize = reader.ReadUInt64(); - - ulong nacpOffset = reader.ReadUInt64(); - ulong nacpSize = reader.ReadUInt64(); - - ulong romfsOffset = reader.ReadUInt64(); - ulong romfsSize = reader.ReadUInt64(); - - if (romfsSize != 0) - { - romfsStream = new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset); - } - - if (nacpSize != 0) - { - input.Seek(obj.FileSize + (long)nacpOffset, SeekOrigin.Begin); - - reader.Read(ControlData.ByteSpan); - - ref ApplicationControlProperty nacp = ref ControlData.Value; - - programInfo.Name = nacp.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString(); - - if (string.IsNullOrWhiteSpace(programInfo.Name)) - { - programInfo.Name = nacp.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); - } - - if (nacp.PresenceGroupId != 0) - { - programInfo.ProgramId = nacp.PresenceGroupId; - } - else if (nacp.SaveDataOwnerId != 0) - { - programInfo.ProgramId = nacp.SaveDataOwnerId; - } - else if (nacp.AddOnContentBaseId != 0) - { - programInfo.ProgramId = nacp.AddOnContentBaseId - 0x1000; - } - else - { - programInfo.ProgramId = 0000000000000000; - } - } - } - else - { - Logger.Warning?.Print(LogClass.Loader, $"Unsupported ASET header version found \"{asetVersion}\""); - } - } - } - } - else - { - executable = new NsoExecutable(new LocalStorage(filePath, FileAccess.Read), Path.GetFileNameWithoutExtension(filePath)); - } - - _device.Configuration.ContentManager.LoadEntries(_device); - - _titleName = programInfo.Name; - TitleId = programInfo.ProgramId; - TitleIs64Bit = (npdm.Meta.Flags & 1) != 0; - _device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId); - - // Explicitly null titleid to disable the shader cache. - Graphics.Gpu.GraphicsConfig.TitleId = null; - _device.Gpu.HostInitalized.Set(); - - ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable); - - if (romfsStream != null) - { - _device.Configuration.VirtualFileSystem.SetRomFs(result.ProcessId, romfsStream); - } - - DiskCacheLoadState = result.DiskCacheLoadState; - - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); - } - - private MetaLoader GetDefaultNpdm() - { - Assembly asm = Assembly.GetCallingAssembly(); - - using (Stream npdmStream = asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm")) - { - var npdmBuffer = new byte[npdmStream.Length]; - npdmStream.Read(npdmBuffer); - - var metaLoader = new MetaLoader(); - metaLoader.Load(npdmBuffer).ThrowIfFailure(); - - return metaLoader; - } - } - - private static (ulong applicationId, int programCount) GetMultiProgramInfo(VirtualFileSystem fileSystem, PartitionFileSystem pfs) - { - ulong mainProgramId = 0; - Span<bool> hasIndex = stackalloc bool[0x10]; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - if (nca.Header.ContentType != NcaContentType.Program) - { - continue; - } - - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - continue; - } - - ulong currentProgramId = nca.Header.TitleId; - ulong currentMainProgramId = currentProgramId & ~0xFFFul; - - if (mainProgramId == 0 && currentMainProgramId != 0) - { - mainProgramId = currentMainProgramId; - } - - if (mainProgramId != currentMainProgramId) - { - // As far as I know there aren't any multi-application game cards containing multi-program applications, - // so because multi-application game cards are the only way we should run into multiple applications - // we'll just return that there's a single program. - return (mainProgramId, 1); - } - - hasIndex[(int)(currentProgramId & 0xF)] = true; - } - - int programCount = 0; - - for (int i = 0; i < hasIndex.Length && hasIndex[i]; i++) - { - programCount++; - } - - return (mainProgramId, programCount); - } - - private Result RegisterProgramMapInfo(PartitionFileSystem pfs) - { - (ulong applicationId, int programCount) = GetMultiProgramInfo(_device.Configuration.VirtualFileSystem, pfs); - - if (programCount <= 0) - return Result.Success; - - Span<ProgramIndexMapInfo> mapInfo = stackalloc ProgramIndexMapInfo[0x10]; - - for (int i = 0; i < programCount; i++) - { - mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i); - mapInfo[i].MainProgramId = new ApplicationId(applicationId); - mapInfo[i].ProgramIndex = (byte)i; - } - - return _device.System.LibHacHorizonManager.NsClient.Fs.RegisterProgramIndexMapInfo(mapInfo.Slice(0, programCount)); - } - - private Result EnsureSaveData(ApplicationId applicationId) - { - Logger.Info?.Print(LogClass.Application, "Ensuring required savedata exists."); - - Uid user = _device.System.AccountManager.LastOpenedUser.UserId.ToLibHacUid(); - - ref ApplicationControlProperty control = ref ControlData.Value; - - if (LibHac.Common.Utilities.IsZeros(ControlData.ByteSpan)) - { - // If the current application doesn't have a loaded control property, create a dummy one - // and set the savedata sizes so a user savedata will be created. - control = ref new BlitStruct<ApplicationControlProperty>(1).Value; - - // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; - control.UserAccountSaveDataJournalSize = 0x4000; - control.SaveDataOwnerId = applicationId.Value; - - Logger.Warning?.Print(LogClass.Application, - "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); - } - - HorizonClient hos = _device.System.LibHacHorizonManager.RyujinxClient; - Result resultCode = hos.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control); - - if (resultCode.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationCacheStorage. Result code {resultCode.ToStringWithName()}"); - - return resultCode; - } - - resultCode = hos.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in user); - - if (resultCode.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {resultCode.ToStringWithName()}"); - } - - return resultCode; - } - } -} diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 2b77a7c2e..1639532ed 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -35,6 +35,7 @@ using Ryujinx.HLE.HOS.Services.SurfaceFlinger; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Processes; using Ryujinx.Horizon; using System; using System.Collections.Generic; @@ -358,11 +359,11 @@ namespace Ryujinx.HLE.HOS } } - public void LoadKip(string kipPath) + public bool LoadKip(string kipPath) { using var kipFile = new SharedRef<IStorage>(new LocalStorage(kipPath, FileAccess.Read)); - ProgramLoader.LoadKip(KernelContext, new KipExecutable(in kipFile)); + return ProcessLoaderHelper.LoadKip(KernelContext, new KipExecutable(in kipFile)); } public void ChangeDockedModeState(bool newState) diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs index 556703cf6..4cf67172d 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Kernel.Process { - internal class ProcessTamperInfo + class ProcessTamperInfo { public KProcess Process { get; } public IEnumerable<string> BuildIds { get; } diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs index a6dc90135..165125414 100644 --- a/Ryujinx.HLE/HOS/ModLoader.cs +++ b/Ryujinx.HLE/HOS/ModLoader.cs @@ -10,6 +10,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Mods; +using Ryujinx.HLE.Loaders.Processes; using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -547,7 +548,7 @@ namespace Ryujinx.HLE.HOS return modLoadResult; } - if (nsos.Length != ApplicationLoader.ExeFsPrefixes.Length) + if (nsos.Length != ProcessConst.ExeFsPrefixes.Length) { throw new ArgumentOutOfRangeException("NSO Count is incorrect"); } @@ -556,9 +557,9 @@ namespace Ryujinx.HLE.HOS foreach (var mod in exeMods) { - for (int i = 0; i < ApplicationLoader.ExeFsPrefixes.Length; ++i) + for (int i = 0; i < ProcessConst.ExeFsPrefixes.Length; ++i) { - var nsoName = ApplicationLoader.ExeFsPrefixes[i]; + var nsoName = ProcessConst.ExeFsPrefixes[i]; FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName)); if (nsoFile.Exists) @@ -596,7 +597,7 @@ namespace Ryujinx.HLE.HOS } } - for (int i = ApplicationLoader.ExeFsPrefixes.Length - 1; i >= 0; --i) + for (int i = ProcessConst.ExeFsPrefixes.Length - 1; i >= 0; --i) { if (modLoadResult.Stubs[1 << i] && !modLoadResult.Replaces[1 << i]) // Prioritizes replacements over stubs { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 1b412d74e..413bedced 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // TODO: Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current Pid and store the result (NACP file) internally. // But since we use LibHac and we load one Application at a time, it's not necessary. - context.ResponseData.Write((byte)context.Device.Application.ControlData.Value.UserAccountSwitchLock); + context.ResponseData.Write((byte)context.Device.Processes.ActiveApplication.ApplicationControlProperties.UserAccountSwitchLock); Logger.Stub?.PrintStub(LogClass.ServiceAcc); diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs index 00081e1b1..720497141 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib public ILibraryAppletSelfAccessor(ServiceCtx context) { - if (context.Device.Application.TitleId == 0x0100000000001009) + if (context.Device.Processes.ActiveApplication.ProgramId == 0x0100000000001009) { // Create MiiEdit data. _appletStandalone = new AppletStandalone() @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib } else { - throw new NotImplementedException($"{context.Device.Application.TitleId} applet is not implemented."); + throw new NotImplementedException($"{context.Device.Processes.ActiveApplication.ProgramId} applet is not implemented."); } } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index f8f88a1cb..924f54298 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -115,28 +115,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati Uid userId = context.RequestData.ReadStruct<AccountUid>().ToLibHacUid(); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new ApplicationId(context.Device.Application.TitleId & ~0xFul); + ApplicationId applicationId = new ApplicationId(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); - BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData; - - ref ApplicationControlProperty control = ref controlHolder.Value; - - if (LibHac.Common.Utilities.IsZeros(controlHolder.ByteSpan)) - { - // If the current application doesn't have a loaded control property, create a dummy one - // and set the savedata sizes so a user savedata will be created. - control = ref new BlitStruct<ApplicationControlProperty>(1).Value; - - // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; - control.UserAccountSaveDataJournalSize = 0x4000; - - Logger.Warning?.Print(LogClass.ServiceAm, - "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); - } + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; LibHac.HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient; - LibHac.Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in control, in userId); + LibHac.Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in nacp, in userId); context.ResponseData.Write(requiredSize); @@ -153,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // TODO: When above calls are implemented, switch to using ns:am long desiredLanguageCode = context.Device.System.State.DesiredLanguageCode; - int supportedLanguages = (int)context.Device.Application.ControlData.Value.SupportedLanguageFlag; + int supportedLanguages = (int)context.Device.Processes.ActiveApplication.ApplicationControlProperties.SupportedLanguageFlag; int firstSupported = BitOperations.TrailingZeroCount(supportedLanguages); if (firstSupported > (int)TitleLanguage.BrazilianPortuguese) @@ -196,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati public ResultCode GetDisplayVersion(ServiceCtx context) { // If an NACP isn't found, the buffer will be all '\0' which seems to be the correct implementation. - context.ResponseData.Write(context.Device.Application.ControlData.Value.DisplayVersion); + context.ResponseData.Write(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion); return ResultCode.Success; } @@ -251,13 +235,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati long journalSize = context.RequestData.ReadInt64(); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new ApplicationId(context.Device.Application.TitleId & ~0xFul); + ApplicationId applicationId = new ApplicationId(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); - BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData; + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; LibHac.Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize, - out CacheStorageTargetMedia storageTarget, applicationId, in controlHolder.Value, index, saveSize, - journalSize); + out CacheStorageTargetMedia storageTarget, applicationId, in nacp, index, saveSize, journalSize); if (result.IsFailure()) return (ResultCode)result.Value; @@ -677,7 +660,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati throw new InvalidSystemResourceException($"JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)"); } - context.Device.Application.LoadServiceNca(filePath); + context.Device.LoadNca(filePath); // FIXME: Most likely not how this should be done? while (!context.Device.System.SmRegistry.IsServiceRegistered("jit:u")) diff --git a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs index c985092b8..3e4eca0ac 100644 --- a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs +++ b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp return new ApplicationLaunchProperty { - TitleId = context.Device.Application.TitleId, + TitleId = context.Device.Processes.ActiveApplication.ProgramId, Version = 0x00, BaseGameStorageId = (byte)StorageId.BuiltInSystem, UpdateGameStorageId = (byte)StorageId.None diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs index 1789122e4..e0c65f440 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); diff --git a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index 6d663a4de..c884e8803 100644 --- a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal errorReport.AppendLine(); errorReport.AppendLine("ErrorReport log:"); - errorReport.AppendLine($"\tTitleId: {context.Device.Application.TitleId:x16}"); + errorReport.AppendLine($"\tTitleId: {context.Device.Processes.ActiveApplication.ProgramIdText}"); errorReport.AppendLine($"\tPid: {pid}"); errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}"); errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}"); @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal { errorReport.AppendLine("CPU Context:"); - if (context.Device.Application.TitleIs64Bit) + if (context.Device.Processes.ActiveApplication.Is64Bit) { CpuContext64 cpuContext64 = MemoryMarshal.Cast<byte, CpuContext64>(cpuContext)[0]; diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 17a33b799..4317c8f61 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -334,7 +334,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator } // TODO: Call nn::arp::GetApplicationControlProperty here when implemented. - ApplicationControlProperty controlProperty = context.Device.Application.ControlData.Value; + ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties; /* diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 37143a5aa..1b63f362e 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -808,7 +808,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs { byte programIndex = context.RequestData.ReadByte(); - if ((context.Device.Application.TitleId & 0xf) != programIndex) + if ((context.Device.Processes.ActiveApplication.ProgramId & 0xf) != programIndex) { throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex})."); } diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs index 0d5520038..b8f9e3b97 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return CountAddOnContentImpl(context, context.Device.Application.TitleId); + return CountAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(3)] @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return ListAddContentImpl(context, context.Device.Application.TitleId); + return ListAddContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(4)] // 1.0.0-6.2.0 @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return GetAddOnContentBaseIdImpl(context, context.Device.Application.TitleId); + return GetAddOnContentBaseIdImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(6)] // 1.0.0-6.2.0 @@ -99,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return PrepareAddOnContentImpl(context, context.Device.Application.TitleId); + return PrepareAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(8)] // 4.0.0+ @@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. // TODO: Found where stored value is used. - ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Application.TitleId); + ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.ActiveApplication.ProgramId); if (resultCode != ResultCode.Success) { @@ -294,7 +294,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service calls arp:r GetApplicationControlProperty to get AddOnContentBaseId using TitleId, // If the call fails, it returns ResultCode.InvalidPid. - _addOnContentBaseId = context.Device.Application.ControlData.Value.AddOnContentBaseId; + _addOnContentBaseId = context.Device.Processes.ActiveApplication.ApplicationControlProperties.AddOnContentBaseId; if (_addOnContentBaseId == 0) { @@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc { uint index = context.RequestData.ReadUInt32(); - ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Application.TitleId); + ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.ActiveApplication.ProgramId); if (resultCode != ResultCode.Success) { diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index d3a891782..249343d79 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -1,4 +1,8 @@ -namespace Ryujinx.HLE.HOS.Services.Ns +using LibHac.Ns; +using Ryujinx.Common.Utilities; +using System; + +namespace Ryujinx.HLE.HOS.Services.Ns { [Service("ns:am")] class IApplicationManagerInterface : IpcService @@ -14,9 +18,9 @@ ulong position = context.Request.ReceiveBuff[0].Position; - byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray(); + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; - context.Memory.Write(position, nacpData); + context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs index 3b6965d0a..8f6acc1c6 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs @@ -1,4 +1,7 @@ -namespace Ryujinx.HLE.HOS.Services.Ns +using LibHac.Common; +using LibHac.Ns; + +namespace Ryujinx.HLE.HOS.Services.Ns { class IReadOnlyApplicationControlDataInterface : IpcService { @@ -13,9 +16,9 @@ ulong position = context.Request.ReceiveBuff[0].Position; - byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray(); + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; - context.Memory.Write(position, nacpData); + context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs b/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs index e0017808d..02964749c 100644 --- a/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs +++ b/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs @@ -56,8 +56,8 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory _titleId = titleId; // TODO: Call nn::arp::GetApplicationControlProperty here when implemented, if it return ResultCode.Success we assign fields. - _ratingAge = Array.ConvertAll(context.Device.Application.ControlData.Value.RatingAge.ItemsRo.ToArray(), Convert.ToInt32); - _parentalControlFlag = context.Device.Application.ControlData.Value.ParentalControlFlag; + _ratingAge = Array.ConvertAll(context.Device.Processes.ActiveApplication.ApplicationControlProperties.RatingAge.ItemsRo.ToArray(), Convert.ToInt32); + _parentalControlFlag = context.Device.Processes.ActiveApplication.ApplicationControlProperties.ParentalControlFlag; } } diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs index 1d6cc118e..52a07d466 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService { @@ -16,8 +15,6 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false) { - ref readonly var controlProperty = ref context.Device.Application.ControlData.Value; - ulong inputPosition = context.Request.SendBuff[0].Position; ulong inputSize = context.Request.SendBuff[0].Size; @@ -34,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService } } - PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)controlProperty.PlayLogQueryCapability; + PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryCapability; List<ulong> titleIds = new List<ulong>(); @@ -48,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService // Check if input title ids are in the whitelist. foreach (ulong titleId in titleIds) { - if (!controlProperty.PlayLogQueryableApplicationId.ItemsRo.Contains(titleId)) + if (!context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryableApplicationId.ItemsRo.Contains(titleId)) { return (ResultCode)Am.ResultCode.ObjectInvalid; } diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs new file mode 100644 index 000000000..58759ddb1 --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -0,0 +1,133 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.Loader; +using LibHac.Ns; +using LibHac.Tools.FsSystem; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.Memory; +using System.Linq; +using static Ryujinx.HLE.HOS.ModLoader; + +namespace Ryujinx.HLE.Loaders.Processes.Extensions +{ + static class FileSystemExtensions + { + public static MetaLoader GetNpdm(this IFileSystem fileSystem) + { + MetaLoader metaLoader = new(); + + if (fileSystem == null || !fileSystem.FileExists(ProcessConst.MainNpdmPath)) + { + Logger.Warning?.Print(LogClass.Loader, "NPDM file not found, using default values!"); + + metaLoader.LoadDefault(); + } + else + { + metaLoader.LoadFromFile(fileSystem); + } + + return metaLoader; + } + + public static ProcessResult Load(this IFileSystem exeFs, Switch device, BlitStruct<ApplicationControlProperty> nacpData, MetaLoader metaLoader, bool isHomebrew = false) + { + ulong programId = metaLoader.GetProgramId(); + + // Replace the whole ExeFs partition by the modded one. + if (device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(programId, ref exeFs)) + { + metaLoader = null; + } + + // Reload the MetaLoader in case of ExeFs partition replacement. + metaLoader ??= exeFs.GetNpdm(); + + NsoExecutable[] nsoExecutables = new NsoExecutable[ProcessConst.ExeFsPrefixes.Length]; + + for (int i = 0; i < nsoExecutables.Length; i++) + { + string name = ProcessConst.ExeFsPrefixes[i]; + + if (!exeFs.FileExists($"/{name}")) + { + continue; // File doesn't exist, skip. + } + + Logger.Info?.Print(LogClass.Loader, $"Loading {name}..."); + + using var nsoFile = new UniqueRef<IFile>(); + + exeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + nsoExecutables[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); + } + + // ExeFs file replacements. + ModLoadResult modLoadResult = device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(programId, nsoExecutables); + + // Take the Npdm from mods if present. + if (modLoadResult.Npdm != null) + { + metaLoader = modLoadResult.Npdm; + } + + // Collect the Nsos, ignoring ones that aren't used. + nsoExecutables = nsoExecutables.Where(x => x != null).ToArray(); + + // Apply Nsos patches. + device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(programId, nsoExecutables); + + // Don't use PTC if ExeFS files have been replaced. + bool enablePtc = device.System.EnablePtc && !modLoadResult.Modified; + if (!enablePtc) + { + Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PTC disabled."); + } + + // We allow it for nx-hbloader because it can be used to launch homebrew. + bool allowCodeMemoryForJit = programId == 0x010000000000100DUL || isHomebrew; + + string programName = ""; + + if (!isHomebrew && programId > 0x010000000000FFFF) + { + programName = nacpData.Value.Title[(int)device.System.State.DesiredTitleLanguage].NameString.ToString(); + + if (string.IsNullOrWhiteSpace(programName)) + { + programName = nacpData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); + } + } + + // Initialize GPU. + Graphics.Gpu.GraphicsConfig.TitleId = $"{programId:x16}"; + device.Gpu.HostInitalized.Set(); + + if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) + { + device.Configuration.MemoryManagerMode = MemoryManagerMode.SoftwarePageTable; + } + + ProcessResult processResult = ProcessLoaderHelper.LoadNsos( + device, + device.System.KernelContext, + metaLoader, + nacpData.Value, + enablePtc, + allowCodeMemoryForJit, + programName, + metaLoader.GetProgramId(), + null, + nsoExecutables); + + // TODO: This should be stored using ProcessId instead. + device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(metaLoader.GetProgramId()); + + return processResult; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs new file mode 100644 index 000000000..28d907851 --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -0,0 +1,39 @@ +using LibHac.Common; +using LibHac.FsSystem; +using LibHac.Loader; +using LibHac.Ns; +using Ryujinx.HLE.Loaders.Processes.Extensions; +using ApplicationId = LibHac.Ncm.ApplicationId; + +namespace Ryujinx.HLE.Loaders.Processes +{ + static class LocalFileSystemExtensions + { + public static ProcessResult Load(this LocalFileSystem exeFs, Switch device, string romFsPath = "") + { + MetaLoader metaLoader = exeFs.GetNpdm(); + var nacpData = new BlitStruct<ApplicationControlProperty>(1); + ulong programId = metaLoader.GetProgramId(); + + device.Configuration.VirtualFileSystem.ModLoader.CollectMods( + new[] { programId }, + device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), + device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + + if (programId != 0) + { + ProcessLoaderHelper.EnsureSaveData(device, new ApplicationId(programId), nacpData); + } + + ProcessResult processResult = exeFs.Load(device, nacpData, metaLoader); + + // Load RomFS. + if (!string.IsNullOrEmpty(romFsPath)) + { + device.Configuration.VirtualFileSystem.LoadRomFs(processResult.ProcessId, romFsPath); + } + + return processResult; + } + } +} diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs new file mode 100644 index 000000000..c639ee524 --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs @@ -0,0 +1,61 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.Loader; +using LibHac.Util; +using Ryujinx.Common; +using System; + +namespace Ryujinx.HLE.Loaders.Processes.Extensions +{ + public static class MetaLoaderExtensions + { + public static ulong GetProgramId(this MetaLoader metaLoader) + { + metaLoader.GetNpdm(out var npdm).ThrowIfFailure(); + + return npdm.Aci.ProgramId.Value; + } + + public static string GetProgramName(this MetaLoader metaLoader) + { + metaLoader.GetNpdm(out var npdm).ThrowIfFailure(); + + return StringUtils.Utf8ZToString(npdm.Meta.ProgramName); + } + + public static bool IsProgram64Bit(this MetaLoader metaLoader) + { + metaLoader.GetNpdm(out var npdm).ThrowIfFailure(); + + return (npdm.Meta.Flags & 1) != 0; + } + + public static void LoadDefault(this MetaLoader metaLoader) + { + byte[] npdmBuffer = EmbeddedResources.Read("Ryujinx.HLE/Homebrew.npdm"); + + metaLoader.Load(npdmBuffer).ThrowIfFailure(); + } + + public static void LoadFromFile(this MetaLoader metaLoader, IFileSystem fileSystem, string path = "") + { + if (string.IsNullOrEmpty(path)) + { + path = ProcessConst.MainNpdmPath; + } + + using var npdmFile = new UniqueRef<IFile>(); + + fileSystem.OpenFile(ref npdmFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure(); + + Span<byte> npdmBuffer = new byte[fileSize]; + + npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure(); + + metaLoader.Load(npdmBuffer).ThrowIfFailure(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs new file mode 100644 index 000000000..473f374db --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -0,0 +1,175 @@ +using LibHac; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.Loader; +using LibHac.Ncm; +using LibHac.Ns; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Logging; +using System.IO; +using System.Linq; +using ApplicationId = LibHac.Ncm.ApplicationId; + +namespace Ryujinx.HLE.Loaders.Processes.Extensions +{ + static class NcaExtensions + { + public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca) + { + // Extract RomFs and ExeFs from NCA. + IStorage romFs = nca.GetRomFs(device, patchNca); + IFileSystem exeFs = nca.GetExeFs(device, patchNca); + + if (exeFs == null) + { + Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA"); + + return ProcessResult.Failed; + } + + // Load Npdm file. + MetaLoader metaLoader = exeFs.GetNpdm(); + + // Collecting mods related to AocTitleIds and ProgramId. + device.Configuration.VirtualFileSystem.ModLoader.CollectMods( + device.Configuration.ContentManager.GetAocTitleIds().Prepend(metaLoader.GetProgramId()), + device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), + device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + + // Load Nacp file. + var nacpData = new BlitStruct<ApplicationControlProperty>(1); + + if (controlNca != null) + { + nacpData = controlNca.GetNacp(device); + } + + /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" inexistant update. + + // Load program 0 control NCA as we are going to need it for display version. + (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); + + // NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application. + // As such, to avoid PTC cache confusion, we only trust the program 0 display version when launching a sub program. + if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0) + { + nacpData.Value.DisplayVersion = updateProgram0ControlNca.GetNacp(_device).Value.DisplayVersion; + } + + */ + + ProcessResult processResult = exeFs.Load(device, nacpData, metaLoader); + + // Load RomFS. + if (romFs == null) + { + Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); + } + else + { + romFs = device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(processResult.ProgramId, romFs); + + device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romFs.AsStream(FileAccess.Read)); + } + + // Don't create save data for system programs. + if (processResult.ProgramId != 0 && (processResult.ProgramId < SystemProgramId.Start.Value || processResult.ProgramId > SystemAppletId.End.Value)) + { + // Multi-program applications can technically use any program ID for the main program, but in practice they always use 0 in the low nibble. + // We'll know if this changes in the future because applications will get errors when trying to mount the correct save. + ProcessLoaderHelper.EnsureSaveData(device, new ApplicationId(processResult.ProgramId & ~0xFul), nacpData); + } + + return processResult; + } + + public static int GetProgramIndex(this Nca nca) + { + return (int)(nca.Header.TitleId & 0xF); + } + + public static bool IsProgram(this Nca nca) + { + return nca.Header.ContentType == NcaContentType.Program; + } + + public static bool IsPatch(this Nca nca) + { + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + return nca.IsProgram() && nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection(); + } + + public static bool IsControl(this Nca nca) + { + return nca.Header.ContentType == NcaContentType.Control; + } + + public static IFileSystem GetExeFs(this Nca nca, Switch device, Nca patchNca = null) + { + IFileSystem exeFs = null; + + if (patchNca == null) + { + if (nca.CanOpenSection(NcaSectionType.Code)) + { + exeFs = nca.OpenFileSystem(NcaSectionType.Code, device.System.FsIntegrityCheckLevel); + } + } + else + { + if (patchNca.CanOpenSection(NcaSectionType.Code)) + { + exeFs = nca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, device.System.FsIntegrityCheckLevel); + } + } + + return exeFs; + } + + public static IStorage GetRomFs(this Nca nca, Switch device, Nca patchNca = null) + { + IStorage romFs = null; + + if (patchNca == null) + { + if (nca.CanOpenSection(NcaSectionType.Data)) + { + romFs = nca.OpenStorage(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); + } + } + else + { + if (patchNca.CanOpenSection(NcaSectionType.Data)) + { + romFs = nca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, device.System.FsIntegrityCheckLevel); + } + } + + return romFs; + } + + public static BlitStruct<ApplicationControlProperty> GetNacp(this Nca controlNca, Switch device) + { + var nacpData = new BlitStruct<ApplicationControlProperty>(1); + + using var controlFile = new UniqueRef<IFile>(); + + Result result = controlNca.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel) + .OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read); + + if (result.IsSuccess()) + { + result = controlFile.Get.Read(out long bytesRead, 0, nacpData.ByteSpan, ReadOption.None); + } + else + { + nacpData.ByteSpan.Clear(); + } + + return nacpData; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs new file mode 100644 index 000000000..5147f5c3a --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs @@ -0,0 +1,177 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace Ryujinx.HLE.Loaders.Processes.Extensions +{ + public static class PartitionFileSystemExtensions + { + internal static (bool, ProcessResult) TryLoad(this PartitionFileSystem partitionFileSystem, Switch device, string path, out string errorMessage) + { + errorMessage = null; + + // Load required NCAs. + Nca mainNca = null; + Nca patchNca = null; + Nca controlNca = null; + + try + { + device.Configuration.VirtualFileSystem.ImportTickets(partitionFileSystem); + + // TODO: To support multi-games container, this should use CNMT NCA instead. + foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) + { + Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath); + + if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index) + { + continue; + } + + if (nca.IsPatch()) + { + patchNca = nca; + } + else if (nca.IsProgram()) + { + mainNca = nca; + } + else if (nca.IsControl()) + { + controlNca = nca; + } + } + + ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure(); + } + catch (Exception ex) + { + errorMessage = $"Unable to load: {ex.Message}"; + + return (false, ProcessResult.Failed); + } + + if (mainNca != null) + { + if (mainNca.Header.ContentType != NcaContentType.Program) + { + errorMessage = "Selected NCA file is not a \"Program\" NCA"; + + return (false, ProcessResult.Failed); + } + + // Load Update NCAs. + Nca updatePatchNca = null; + Nca updateControlNca = null; + + if (ulong.TryParse(mainNca.Header.TitleId.ToString("x16"), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase)) + { + // Clear the program index part. + titleIdBase &= ~0xFUL; + + // Load update information if exists. + string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); + if (File.Exists(titleUpdateMetadataPath)) + { + string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; + if (File.Exists(updatePath)) + { + PartitionFileSystem updatePartitionFileSystem = new(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()); + + device.Configuration.VirtualFileSystem.ImportTickets(updatePartitionFileSystem); + + // TODO: This should use CNMT NCA instead. + foreach (DirectoryEntryEx fileEntry in updatePartitionFileSystem.EnumerateEntries("/", "*.nca")) + { + Nca nca = updatePartitionFileSystem.GetNca(device, fileEntry.FullPath); + + if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index) + { + continue; + } + + if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleIdBase.ToString("x16")) + { + break; + } + + if (nca.IsProgram()) + { + updatePatchNca = nca; + } + else if (nca.IsControl()) + { + updateControlNca = nca; + } + } + } + } + } + + if (updatePatchNca != null) + { + patchNca = updatePatchNca; + } + + if (updateControlNca != null) + { + controlNca = updateControlNca; + } + + // Load contained DownloadableContents. + // TODO: If we want to support multi-processes in future, we shouldn't clear AddOnContent data here. + device.Configuration.ContentManager.ClearAocData(); + device.Configuration.ContentManager.AddAocData(partitionFileSystem, path, mainNca.Header.TitleId, device.Configuration.FsIntegrityCheckLevel); + + // Load DownloadableContents. + string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); + if (File.Exists(addOnContentMetadataPath)) + { + List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(addOnContentMetadataPath); + + foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) + { + foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) + { + if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled) + { + device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath); + } + else + { + Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {downloadableContentContainer.ContainerPath}. It may have been moved or renamed."); + } + } + } + } + + return (true, mainNca.Load(device, patchNca, controlNca)); + } + + errorMessage = "Unable to load: Could not find Main NCA"; + + return (false, ProcessResult.Failed); + } + + public static Nca GetNca(this IFileSystem fileSystem, Switch device, string path) + { + using var ncaFile = new UniqueRef<IFile>(); + + fileSystem.OpenFile(ref ncaFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + return new Nca(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs b/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs new file mode 100644 index 000000000..42ae2e89b --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs @@ -0,0 +1,33 @@ +namespace Ryujinx.HLE.Loaders.Processes +{ + static class ProcessConst + { + // Binaries from exefs are loaded into mem in this order. Do not change. + public static readonly string[] ExeFsPrefixes = + { + "rtld", + "main", + "subsdk0", + "subsdk1", + "subsdk2", + "subsdk3", + "subsdk4", + "subsdk5", + "subsdk6", + "subsdk7", + "subsdk8", + "subsdk9", + "sdk" + }; + + public static readonly string MainNpdmPath = "/main.npdm"; + + public const int NroAsetMagic = ('A' << 0) | ('S' << 8) | ('E' << 16) | ('T' << 24); + + public const bool AslrEnabled = true; + + public const int NsoArgsHeaderSize = 8; + public const int NsoArgsDataSize = 0x9000; + public const int NsoArgsTotalSize = NsoArgsHeaderSize + NsoArgsDataSize; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs new file mode 100644 index 000000000..785db0e50 --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -0,0 +1,244 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Ns; +using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Processes.Extensions; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using Path = System.IO.Path; + +namespace Ryujinx.HLE.Loaders.Processes +{ + public class ProcessLoader + { + private readonly Switch _device; + + private readonly ConcurrentDictionary<ulong, ProcessResult> _processesByPid; + + private ulong _latestPid; + + public ProcessResult ActiveApplication => _processesByPid[_latestPid]; + + public ProcessLoader(Switch device) + { + _device = device; + _processesByPid = new ConcurrentDictionary<ulong, ProcessResult>(); + } + + public bool LoadXci(string path) + { + FileStream stream = new(path, FileMode.Open, FileAccess.Read); + Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage()); + + if (!xci.HasPartition(XciPartitionType.Secure)) + { + Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI Secure partition"); + + return false; + } + + (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, out string errorMessage); + + if (!success) + { + Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad)); + + return false; + } + + if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) + { + if (processResult.Start(_device)) + { + _latestPid = processResult.ProcessId; + + return true; + } + } + + return false; + } + + public bool LoadNsp(string path) + { + FileStream file = new(path, FileMode.Open, FileAccess.Read); + PartitionFileSystem partitionFileSystem = new(file.AsStorage()); + + (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage); + + if (processResult.ProcessId == 0) + { + // This is not a normal NSP, it's actually a ExeFS as a NSP + processResult = partitionFileSystem.Load(_device, new BlitStruct<ApplicationControlProperty>(1), partitionFileSystem.GetNpdm(), true); + } + + if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) + { + if (processResult.Start(_device)) + { + _latestPid = processResult.ProcessId; + + return true; + } + } + + if (!success) + { + Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad)); + } + + return false; + } + + public bool LoadNca(string path) + { + FileStream file = new(path, FileMode.Open, FileAccess.Read); + Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); + + ProcessResult processResult = nca.Load(_device, null, null); + + if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) + { + if (processResult.Start(_device)) + { + // NOTE: Check if process is SystemApplicationId or ApplicationId + if (processResult.ProgramId > 0x01000000000007FF) + { + _latestPid = processResult.ProcessId; + } + + return true; + } + } + + return false; + } + + public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null) + { + ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath); + + if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) + { + if (processResult.Start(_device)) + { + _latestPid = processResult.ProcessId; + + return true; + } + } + + return false; + } + + public bool LoadNxo(string path) + { + var nacpData = new BlitStruct<ApplicationControlProperty>(1); + IFileSystem dummyExeFs = null; + Stream romfsStream = null; + + string programName = ""; + ulong programId = 0000000000000000; + + // Load executable. + IExecutable executable; + + if (Path.GetExtension(path).ToLower() == ".nro") + { + FileStream input = new(path, FileMode.Open); + NroExecutable nro = new(input.AsStorage()); + + executable = nro; + + // Open RomFS if exists. + IStorage romFsStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.RomFs, false); + romFsStorage.GetSize(out long romFsSize).ThrowIfFailure(); + if (romFsSize != 0) + { + romfsStream = romFsStorage.AsStream(); + } + + // Load Nacp if exists. + IStorage nacpStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.Nacp, false); + nacpStorage.GetSize(out long nacpSize).ThrowIfFailure(); + if (nacpSize != 0) + { + nacpStorage.Read(0, nacpData.ByteSpan); + + programName = nacpData.Value.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString(); + + if (string.IsNullOrWhiteSpace(programName)) + { + programName = nacpData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); + } + + if (nacpData.Value.PresenceGroupId != 0) + { + programId = nacpData.Value.PresenceGroupId; + } + else if (nacpData.Value.SaveDataOwnerId != 0) + { + programId = nacpData.Value.SaveDataOwnerId; + } + else if (nacpData.Value.AddOnContentBaseId != 0) + { + programId = nacpData.Value.AddOnContentBaseId - 0x1000; + } + } + + // TODO: Add icon maybe ? + } + else + { + programName = System.IO.Path.GetFileNameWithoutExtension(path); + + executable = new NsoExecutable(new LocalStorage(path, FileAccess.Read), programName); + } + + // Explicitly null TitleId to disable the shader cache. + Graphics.Gpu.GraphicsConfig.TitleId = null; + _device.Gpu.HostInitalized.Set(); + + ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device, + _device.System.KernelContext, + dummyExeFs.GetNpdm(), + nacpData.Value, + diskCacheEnabled: false, + allowCodeMemoryForJit: true, + programName, + programId, + null, + executable); + + // Make sure the process id is valid. + if (processResult.ProcessId != 0) + { + // Load RomFS. + if (romfsStream != null) + { + _device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romfsStream); + } + + // Start process. + if (_processesByPid.TryAdd(processResult.ProcessId, processResult)) + { + if (processResult.Start(_device)) + { + _latestPid = processResult.ProcessId; + + return true; + } + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs similarity index 54% rename from Ryujinx.HLE/HOS/ProgramLoader.cs rename to Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index 4ebcb7e7d..7176445e5 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -1,69 +1,132 @@ +using LibHac.Account; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Shim; +using LibHac.FsSystem; using LibHac.Loader; using LibHac.Ncm; -using LibHac.Util; +using LibHac.Ns; +using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common; using Ryujinx.Common.Logging; -using Ryujinx.Cpu; +using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.Horizon.Common; using System; using System.Linq; using System.Runtime.InteropServices; -using Npdm = LibHac.Loader.Npdm; +using ApplicationId = LibHac.Ncm.ApplicationId; -namespace Ryujinx.HLE.HOS +namespace Ryujinx.HLE.Loaders.Processes { - struct ProgramInfo + static class ProcessLoaderHelper { - public string Name; - public ulong ProgramId; - public readonly string TitleIdText; - public readonly string DisplayVersion; - public readonly bool DiskCacheEnabled; - public readonly bool AllowCodeMemoryForJit; - - public ProgramInfo(in Npdm npdm, string displayVersion, bool diskCacheEnabled, bool allowCodeMemoryForJit) + public static LibHac.Result RegisterProgramMapInfo(Switch device, PartitionFileSystem partitionFileSystem) { - ulong programId = npdm.Aci.ProgramId.Value; + ulong applicationId = 0; + int programCount = 0; - Name = StringUtils.Utf8ZToString(npdm.Meta.ProgramName); - ProgramId = programId; - TitleIdText = programId.ToString("x16"); - DisplayVersion = displayVersion; - DiskCacheEnabled = diskCacheEnabled; - AllowCodeMemoryForJit = allowCodeMemoryForJit; + Span<bool> hasIndex = stackalloc bool[0x10]; + + foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) + { + Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath); + + if (!nca.IsProgram() && nca.IsPatch()) + { + continue; + } + + ulong currentProgramId = nca.Header.TitleId; + ulong currentMainProgramId = currentProgramId & ~0xFFFul; + + if (applicationId == 0 && currentMainProgramId != 0) + { + applicationId = currentMainProgramId; + } + + if (applicationId != currentMainProgramId) + { + // Currently there aren't any known multi-application game cards containing multi-program applications, + // so because multi-application game cards are the only way we could run into multiple applications + // we'll just return that there's a single program. + programCount = 1; + + break; + } + + hasIndex[(int)(currentProgramId & 0xF)] = true; + } + + if (programCount == 0) + { + for (int i = 0; i < hasIndex.Length && hasIndex[i]; i++) + { + programCount++; + } + } + + if (programCount <= 0) + { + return LibHac.Result.Success; + } + + Span<ProgramIndexMapInfo> mapInfo = stackalloc ProgramIndexMapInfo[0x10]; + + for (int i = 0; i < programCount; i++) + { + mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i); + mapInfo[i].MainProgramId = new ApplicationId(applicationId); + mapInfo[i].ProgramIndex = (byte)i; + } + + return device.System.LibHacHorizonManager.NsClient.Fs.RegisterProgramIndexMapInfo(mapInfo[..programCount]); } - } - struct ProgramLoadResult - { - public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null, 0); - - public readonly bool Success; - public readonly ProcessTamperInfo TamperInfo; - public readonly IDiskCacheLoadState DiskCacheLoadState; - public readonly ulong ProcessId; - - public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState, ulong pid) + public static LibHac.Result EnsureSaveData(Switch device, ApplicationId applicationId, BlitStruct<ApplicationControlProperty> applicationControlProperty) { - Success = success; - TamperInfo = tamperInfo; - DiskCacheLoadState = diskCacheLoadState; - ProcessId = pid; + Logger.Info?.Print(LogClass.Application, "Ensuring required savedata exists."); + + ref ApplicationControlProperty control = ref applicationControlProperty.Value; + + if (LibHac.Common.Utilities.IsZeros(applicationControlProperty.ByteSpan)) + { + // If the current application doesn't have a loaded control property, create a dummy one and set the savedata sizes so a user savedata will be created. + control = ref new BlitStruct<ApplicationControlProperty>(1).Value; + + // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. + control.UserAccountSaveDataSize = 0x4000; + control.UserAccountSaveDataJournalSize = 0x4000; + control.SaveDataOwnerId = applicationId.Value; + + Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); + } + + LibHac.Result resultCode = device.System.LibHacHorizonManager.RyujinxClient.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control); + if (resultCode.IsFailure()) + { + Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationCacheStorage. Result code {resultCode.ToStringWithName()}"); + + return resultCode; + } + + Uid userId = device.System.AccountManager.LastOpenedUser.UserId.ToLibHacUid(); + + resultCode = device.System.LibHacHorizonManager.RyujinxClient.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in userId); + if (resultCode.IsFailure()) + { + Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {resultCode.ToStringWithName()}"); + } + + return resultCode; } - } - - static class ProgramLoader - { - private const bool AslrEnabled = true; - - private const int ArgsHeaderSize = 8; - private const int ArgsDataSize = 0x9000; - private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize; public static bool LoadKip(KernelContext context, KipExecutable kip) { @@ -74,17 +137,14 @@ namespace Ryujinx.HLE.HOS endOffset = kip.BssOffset + kip.BssSize; } - uint codeSize = BitUtils.AlignUp<uint>(kip.TextOffset + endOffset, KPageTableBase.PageSize); - - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); - + uint codeSize = BitUtils.AlignUp<uint>(kip.TextOffset + endOffset, KPageTableBase.PageSize); + int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL; - - ulong codeAddress = codeBaseAddress + kip.TextOffset; + ulong codeAddress = codeBaseAddress + kip.TextOffset; ProcessCreationFlags flags = 0; - if (AslrEnabled) + if (ProcessConst.AslrEnabled) { // TODO: Randomization. @@ -101,24 +161,11 @@ namespace Ryujinx.HLE.HOS flags |= ProcessCreationFlags.Is64Bit; } - ProcessCreationInfo creationInfo = new ProcessCreationInfo( - kip.Name, - kip.Version, - kip.ProgramId, - codeAddress, - codePagesCount, - flags, - 0, - 0); - - MemoryRegion memoryRegion = kip.UsesSecureMemory - ? MemoryRegion.Service - : MemoryRegion.Application; - - KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion]; + ProcessCreationInfo creationInfo = new(kip.Name, kip.Version, kip.ProgramId, codeAddress, codePagesCount, flags, 0, 0); + MemoryRegion memoryRegion = kip.UsesSecureMemory ? MemoryRegion.Service : MemoryRegion.Application; + KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion]; Result result = region.AllocatePages(out KPageList pageList, (ulong)codePagesCount); - if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -126,7 +173,7 @@ namespace Ryujinx.HLE.HOS return false; } - KProcess process = new KProcess(context); + KProcess process = new(context); var processContextFactory = new ArmProcessContextFactory( context.Device.System.TickSource, @@ -137,14 +184,7 @@ namespace Ryujinx.HLE.HOS codeAddress, codeSize); - result = process.InitializeKip( - creationInfo, - kip.Capabilities, - pageList, - context.ResourceLimit, - memoryRegion, - processContextFactory); - + result = process.InitializeKip(creationInfo, kip.Capabilities, pageList, context.ResourceLimit, memoryRegion, processContextFactory); if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -153,7 +193,6 @@ namespace Ryujinx.HLE.HOS } result = LoadIntoMemory(process, kip, codeBaseAddress); - if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); @@ -164,7 +203,6 @@ namespace Ryujinx.HLE.HOS process.DefaultCpuCore = kip.IdealCoreId; result = process.Start(kip.Priority, (ulong)kip.StackSize); - if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); @@ -177,20 +215,27 @@ namespace Ryujinx.HLE.HOS return true; } - public static ProgramLoadResult LoadNsos( + public static ProcessResult LoadNsos( + Switch device, KernelContext context, - MetaLoader metaData, - ProgramInfo programInfo, + MetaLoader metaLoader, + ApplicationControlProperty applicationControlProperties, + bool diskCacheEnabled, + bool allowCodeMemoryForJit, + string name, + ulong programId, byte[] arguments = null, params IExecutable[] executables) { context.Device.System.ServiceTable.WaitServicesReady(); - LibHac.Result rc = metaData.GetNpdm(out var npdm); + LibHac.Result resultCode = metaLoader.GetNpdm(out var npdm); - if (rc.IsFailure()) + if (resultCode.IsFailure()) { - return ProgramLoadResult.Failed; + Logger.Error?.Print(LogClass.Loader, $"Process initialization failed getting npdm. Result Code {resultCode.ToStringWithName()}"); + + return ProcessResult.Failed; } ref readonly var meta = ref npdm.Meta; @@ -202,10 +247,10 @@ namespace Ryujinx.HLE.HOS var buildIds = executables.Select(e => (e switch { - NsoExecutable nso => BitConverter.ToString(nso.BuildId.ItemsRo.ToArray()), - NroExecutable nro => BitConverter.ToString(nro.Header.BuildId), + NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()), + NroExecutable nro => Convert.ToHexString(nro.Header.BuildId), _ => "" - }).Replace("-", "").ToUpper()); + }).ToUpper()); ulong[] nsoBase = new ulong[executables.Length]; @@ -214,7 +259,7 @@ namespace Ryujinx.HLE.HOS IExecutable nso = executables[index]; uint textEnd = nso.TextOffset + (uint)nso.Text.Length; - uint roEnd = nso.RoOffset + (uint)nso.Ro.Length; + uint roEnd = nso.RoOffset + (uint)nso.Ro.Length; uint dataEnd = nso.DataOffset + (uint)nso.Data.Length + nso.BssSize; uint nsoSize = textEnd; @@ -239,31 +284,30 @@ namespace Ryujinx.HLE.HOS { argsStart = codeSize; - argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize); + argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ProcessConst.NsoArgsTotalSize - 1, KPageTableBase.PageSize); codeSize += argsSize; } } - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); - + int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); int personalMmHeapPagesCount = (int)(meta.SystemResourceSize / KPageTableBase.PageSize); - ProcessCreationInfo creationInfo = new ProcessCreationInfo( - programInfo.Name, + ProcessCreationInfo creationInfo = new( + name, (int)meta.Version, - programInfo.ProgramId, + programId, codeStart, codePagesCount, (ProcessCreationFlags)meta.Flags | ProcessCreationFlags.IsApplication, 0, personalMmHeapPagesCount); - context.Device.System.LibHacHorizonManager.InitializeApplicationClient(new ProgramId(programInfo.ProgramId), in npdm); + context.Device.System.LibHacHorizonManager.InitializeApplicationClient(new ProgramId(programId), in npdm); Result result; - KResourceLimit resourceLimit = new KResourceLimit(context); + KResourceLimit resourceLimit = new(context); long applicationRgSize = (long)context.MemoryManager.MemoryRegions[(int)MemoryRegion.Application].Size; @@ -293,26 +337,26 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values."); - return ProgramLoadResult.Failed; + return ProcessResult.Failed; } - KProcess process = new KProcess(context, programInfo.AllowCodeMemoryForJit); - - MemoryRegion memoryRegion = (MemoryRegion)((npdm.Acid.Flags >> 2) & 0xf); + KProcess process = new(context, allowCodeMemoryForJit); + // NOTE: This field doesn't exists one firmware pre-5.0.0, a workaround have to be found. + MemoryRegion memoryRegion = (MemoryRegion)(npdm.Acid.Flags >> 2 & 0xf); if (memoryRegion > MemoryRegion.NvServices) { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags."); - return ProgramLoadResult.Failed; + return ProcessResult.Failed; } var processContextFactory = new ArmProcessContextFactory( context.Device.System.TickSource, context.Device.Gpu, - programInfo.TitleIdText, - programInfo.DisplayVersion, - programInfo.DiskCacheEnabled, + $"{programId:x16}", + applicationControlProperties.DisplayVersionString.ToString(), + diskCacheEnabled, codeStart, codeSize); @@ -327,7 +371,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - return ProgramLoadResult.Failed; + return ProcessResult.Failed; } for (int index = 0; index < executables.Length; index++) @@ -335,32 +379,22 @@ namespace Ryujinx.HLE.HOS Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBase[index]:x16}..."); result = LoadIntoMemory(process, executables[index], nsoBase[index]); - if (result != Result.Success) { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - return ProgramLoadResult.Failed; + return ProcessResult.Failed; } } process.DefaultCpuCore = meta.DefaultCpuId; - result = process.Start(meta.MainThreadPriority, meta.MainThreadStackSize); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); - - return ProgramLoadResult.Failed; - } - context.Processes.TryAdd(process.Pid, process); // Keep the build ids because the tamper machine uses them to know which process to associate a // tamper to and also keep the starting address of each executable inside a process because some // memory modifications are relative to this address. - ProcessTamperInfo tamperInfo = new ProcessTamperInfo( + ProcessTamperInfo tamperInfo = new( process, buildIds, nsoBase, @@ -368,10 +402,13 @@ namespace Ryujinx.HLE.HOS process.MemoryManager.AliasRegionStart, process.MemoryManager.CodeRegionStart); - return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState, process.Pid); + // Once everything is loaded, we can load cheats. + device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(programId, tamperInfo, device.TamperMachine); + + return new ProcessResult(metaLoader, applicationControlProperties, diskCacheEnabled, allowCodeMemoryForJit, processContextFactory.DiskCacheLoadState, process.Pid, meta.MainThreadPriority, meta.MainThreadStackSize); } - private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) + public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) { ulong textStart = baseAddress + image.TextOffset; ulong roStart = baseAddress + image.RoOffset; @@ -404,14 +441,12 @@ namespace Ryujinx.HLE.HOS } Result result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, KMemoryPermission.ReadAndExecute); - if (result != Result.Success) { return result; } result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, KMemoryPermission.Read); - if (result != Result.Success) { return result; diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs new file mode 100644 index 000000000..6bbeee1b8 --- /dev/null +++ b/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -0,0 +1,92 @@ +using LibHac.Loader; +using LibHac.Ns; +using Ryujinx.Common.Logging; +using Ryujinx.Cpu; +using Ryujinx.HLE.Loaders.Processes.Extensions; +using Ryujinx.Horizon.Common; + +namespace Ryujinx.HLE.Loaders.Processes +{ + public struct ProcessResult + { + public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0); + + private readonly byte _mainThreadPriority; + private readonly uint _mainThreadStackSize; + + public readonly IDiskCacheLoadState DiskCacheLoadState; + + public readonly MetaLoader MetaLoader; + public readonly ApplicationControlProperty ApplicationControlProperties; + + public readonly ulong ProcessId; + public string Name; + public ulong ProgramId; + public readonly string ProgramIdText; + public readonly bool Is64Bit; + public readonly bool DiskCacheEnabled; + public readonly bool AllowCodeMemoryForJit; + + public ProcessResult( + MetaLoader metaLoader, + ApplicationControlProperty applicationControlProperties, + bool diskCacheEnabled, + bool allowCodeMemoryForJit, + IDiskCacheLoadState diskCacheLoadState, + ulong pid, + byte mainThreadPriority, + uint mainThreadStackSize) + { + _mainThreadPriority = mainThreadPriority; + _mainThreadStackSize = mainThreadStackSize; + + DiskCacheLoadState = diskCacheLoadState; + ProcessId = pid; + + MetaLoader = metaLoader; + ApplicationControlProperties = applicationControlProperties; + + if (metaLoader is not null) + { + ulong programId = metaLoader.GetProgramId(); + + Name = metaLoader.GetProgramName(); + ProgramId = programId; + ProgramIdText = $"{programId:x16}"; + Is64Bit = metaLoader.IsProgram64Bit(); + } + + DiskCacheEnabled = diskCacheEnabled; + AllowCodeMemoryForJit = allowCodeMemoryForJit; + } + + public bool Start(Switch device) + { + device.Configuration.ContentManager.LoadEntries(device); + + Result result = device.System.KernelContext.Processes[ProcessId].Start(_mainThreadPriority, _mainThreadStackSize); + if (result != Result.Success) + { + Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); + + return false; + } + + // TODO: LibHac npdm currently doesn't support version field. + string version; + + if (ProgramId > 0x0100000000007FFF) + { + version = ApplicationControlProperties.DisplayVersionString.ToString(); + } + else + { + version = device.System.ContentManager.GetCurrentFirmwareVersion().VersionString; + } + + Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]"); + + return true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 61e5e5727..62d14a546 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -6,6 +6,7 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Apm; using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.Loaders.Processes; using Ryujinx.HLE.Ui; using Ryujinx.Memory; using System; @@ -20,7 +21,7 @@ namespace Ryujinx.HLE public GpuContext Gpu { get; } public VirtualFileSystem FileSystem { get; } public HOS.Horizon System { get; } - public ApplicationLoader Application { get; } + public ProcessLoader Processes { get; } public PerformanceStatistics Statistics { get; } public Hid Hid { get; } public TamperMachine TamperMachine { get; } @@ -50,7 +51,7 @@ namespace Ryujinx.HLE System = new HOS.Horizon(this); Statistics = new PerformanceStatistics(); Hid = new Hid(this, System.HidStorage); - Application = new ApplicationLoader(this); + Processes = new ProcessLoader(this); TamperMachine = new TamperMachine(); System.State.SetLanguage(Configuration.SystemLanguage); @@ -64,29 +65,29 @@ namespace Ryujinx.HLE System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; } - public void LoadCart(string exeFsDir, string romFsFile = null) + public bool LoadCart(string exeFsDir, string romFsFile = null) { - Application.LoadCart(exeFsDir, romFsFile); + return Processes.LoadUnpackedNca(exeFsDir, romFsFile); } - public void LoadXci(string xciFile) + public bool LoadXci(string xciFile) { - Application.LoadXci(xciFile); + return Processes.LoadXci(xciFile); } - public void LoadNca(string ncaFile) + public bool LoadNca(string ncaFile) { - Application.LoadNca(ncaFile); + return Processes.LoadNca(ncaFile); } - public void LoadNsp(string nspFile) + public bool LoadNsp(string nspFile) { - Application.LoadNsp(nspFile); + return Processes.LoadNsp(nspFile); } - public void LoadProgram(string fileName) + public bool LoadProgram(string fileName) { - Application.LoadProgram(fileName); + return Processes.LoadNxo(fileName); } public bool WaitFifo() @@ -123,7 +124,7 @@ namespace Ryujinx.HLE public void EnableCheats() { - FileSystem.ModLoader.EnableCheats(Application.TitleId, TamperMachine); + FileSystem.ModLoader.EnableCheats(Processes.ActiveApplication.ProgramId, TamperMachine); } public bool IsAudioMuted() @@ -152,4 +153,4 @@ namespace Ryujinx.HLE } } } -} \ No newline at end of file +} diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index f618e38d6..54ab18cda 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -447,10 +447,10 @@ namespace Ryujinx.Headless.SDL2 private static void SetupProgressHandler() { - if (_emulationContext.Application.DiskCacheLoadState != null) + if (_emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) { - _emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; - _emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler; + _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler; } _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; @@ -608,12 +608,24 @@ namespace Ryujinx.Headless.SDL2 if (romFsFiles.Length > 0) { Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - _emulationContext.LoadCart(path, romFsFiles[0]); + + if (!_emulationContext.LoadCart(path, romFsFiles[0])) + { + _emulationContext.Dispose(); + + return false; + } } else { Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - _emulationContext.LoadCart(path); + + if (!_emulationContext.LoadCart(path)) + { + _emulationContext.Dispose(); + + return false; + } } } else if (File.Exists(path)) @@ -622,27 +634,52 @@ namespace Ryujinx.Headless.SDL2 { case ".xci": Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - _emulationContext.LoadXci(path); + + if (!_emulationContext.LoadXci(path)) + { + _emulationContext.Dispose(); + + return false; + } break; case ".nca": Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - _emulationContext.LoadNca(path); + + if (!_emulationContext.LoadNca(path)) + { + _emulationContext.Dispose(); + + return false; + } break; case ".nsp": case ".pfs0": Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - _emulationContext.LoadNsp(path); + + if (!_emulationContext.LoadNsp(path)) + { + _emulationContext.Dispose(); + + return false; + } break; default: Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); try { - _emulationContext.LoadProgram(path); + if (!_emulationContext.LoadProgram(path)) + { + _emulationContext.Dispose(); + + return false; + } } catch (ArgumentOutOfRangeException) { Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); + _emulationContext.Dispose(); + return false; } break; @@ -664,4 +701,4 @@ namespace Ryujinx.Headless.SDL2 return true; } } -} \ No newline at end of file +} diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/Ryujinx.Headless.SDL2/WindowBase.cs index db6c8ec4d..e33710421 100644 --- a/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/Ryujinx.Headless.SDL2/WindowBase.cs @@ -145,16 +145,14 @@ namespace Ryujinx.Headless.SDL2 private void InitializeWindow() { - string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty - : $" - {Device.Application.TitleName}"; + var activeProcess = Device.Processes.ActiveApplication; + var nacp = activeProcess.ApplicationControlProperties; + int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage; - string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty - : $" v{Device.Application.DisplayVersion}"; - - string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty - : $" ({Device.Application.TitleIdText.ToUpper()})"; - - string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)"; + string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}"; + string titleVersionSection = string.IsNullOrWhiteSpace(nacp.DisplayVersionString.ToString()) ? string.Empty : $" v{nacp.DisplayVersionString.ToString()}"; + string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})"; + string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DefaultWidth, DefaultHeight, DefaultFlags | GetWindowFlags()); diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 43510d5ec..113e9cb3e 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -11,12 +11,12 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.Ui.Common.Configuration.System; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Reflection; using System.Text; @@ -112,9 +112,9 @@ namespace Ryujinx.Ui.App.Common { return; } - + string extension = Path.GetExtension(app).ToLower(); - + if (!File.GetAttributes(app).HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso") { applications.Add(app); @@ -262,10 +262,9 @@ namespace Ryujinx.Ui.App.Common controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using MemoryStream stream = new(); - + icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); - if (applicationIcon != null) { @@ -400,7 +399,7 @@ namespace Ryujinx.Ui.App.Common }); if (appMetadata.LastPlayed != "Never") - { + { if (!DateTime.TryParse(appMetadata.LastPlayed, out _)) { Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); @@ -470,7 +469,7 @@ namespace Ryujinx.Ui.App.Common private void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId) { - (_, _, Nca controlNca) = ApplicationLoader.GetGameData(_virtualFileSystem, pfs, 0); + (_, _, Nca controlNca) = GetGameData(_virtualFileSystem, pfs, 0); // Return the ControlFS controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); @@ -766,12 +765,12 @@ namespace Ryujinx.Ui.App.Common private bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs) { updatedControlFs = null; - + string updatePath = "(unknown)"; try { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath); + (Nca patchNca, Nca controlNca) = GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath); if (patchNca != null && controlNca != null) { @@ -791,5 +790,119 @@ namespace Ryujinx.Ui.App.Common return false; } + + public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) + { + Nca mainNca = null; + Nca patchNca = null; + Nca controlNca = null; + + fileSystem.ImportTickets(pfs); + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef<IFile>(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); + + int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); + + if (ncaProgramIndex != programIndex) + { + continue; + } + + if (nca.Header.ContentType == NcaContentType.Program) + { + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) + { + patchNca = nca; + } + else + { + mainNca = nca; + } + } + else if (nca.Header.ContentType == NcaContentType.Control) + { + controlNca = nca; + } + } + + return (mainNca, patchNca, controlNca); + } + + public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex) + { + Nca patchNca = null; + Nca controlNca = null; + + fileSystem.ImportTickets(pfs); + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef<IFile>(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); + + int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); + + if (ncaProgramIndex != programIndex) + { + continue; + } + + if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId) + { + break; + } + + if (nca.Header.ContentType == NcaContentType.Program) + { + patchNca = nca; + } + else if (nca.Header.ContentType == NcaContentType.Control) + { + controlNca = nca; + } + } + + return (patchNca, controlNca); + } + + public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath) + { + updatePath = null; + + if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase)) + { + // Clear the program index part. + titleIdBase &= ~0xFUL; + + // Load update information if exists. + string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); + + if (File.Exists(titleUpdateMetadataPath)) + { + updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; + + if (File.Exists(updatePath)) + { + FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); + PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); + + return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex); + } + } + } + + return (null, null); + } } } \ No newline at end of file diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index ace8b87f9..dca87abcb 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -252,7 +252,7 @@ namespace Ryujinx if (CommandLineState.LaunchPathArg != null) { - mainWindow.LoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); + mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); } if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 6d3d4aad6..e0252016f 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -590,10 +590,10 @@ namespace Ryujinx.Ui private void SetupProgressUiHandlers() { - if (_emulationContext.Application.DiskCacheLoadState != null) + if (_emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) { - _emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; - _emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler; + _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler; } _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; @@ -690,7 +690,111 @@ namespace Ryujinx.Ui } } - public void LoadApplication(string path, bool startFullscreen = false) + private bool LoadApplication(string path, bool isFirmwareTitle) + { + SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); + + if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError)) + { + if (SetupValidator.CanFixStartApplication(_contentManager, path, userError, out firmwareVersion)) + { + string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})"; + + ResponseType responseDialog = (ResponseType)GtkDialog.CreateConfirmationDialog("No Firmware Installed", message).Run(); + + if (responseDialog != ResponseType.Yes || !SetupValidator.TryFixStartApplication(_contentManager, path, userError, out _)) + { + UserErrorDialog.CreateUserErrorDialog(userError); + + return false; + } + + // Tell the user that we installed a firmware for them. + + firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); + + RefreshFirmwareLabel(); + + message = $"No installed firmware was found but Ryujinx was able to install firmware {firmwareVersion.VersionString} from the provided game.\nThe emulator will now start."; + + GtkDialog.CreateInfoDialog($"Firmware {firmwareVersion.VersionString} was installed", message); + } + else + { + UserErrorDialog.CreateUserErrorDialog(userError); + + return false; + } + } + + Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); + + if (isFirmwareTitle) + { + Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); + + return _emulationContext.LoadNca(path); + } + + if (Directory.Exists(path)) + { + string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); + + if (romFsFiles.Length == 0) + { + romFsFiles = Directory.GetFiles(path, "*.romfs"); + } + + if (romFsFiles.Length > 0) + { + Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); + + return _emulationContext.LoadCart(path, romFsFiles[0]); + } + + Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); + + return _emulationContext.LoadCart(path); + } + + if (File.Exists(path)) + { + switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) + { + case ".xci": + Logger.Info?.Print(LogClass.Application, "Loading as XCI."); + + return _emulationContext.LoadXci(path); + case ".nca": + Logger.Info?.Print(LogClass.Application, "Loading as NCA."); + + return _emulationContext.LoadNca(path); + case ".nsp": + case ".pfs0": + Logger.Info?.Print(LogClass.Application, "Loading as NSP."); + + return _emulationContext.LoadNsp(path); + default: + Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); + try + { + return _emulationContext.LoadProgram(path); + } + catch (ArgumentOutOfRangeException) + { + Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); + + return false; + } + } + } + + Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); + + return false; + } + + public void RunApplication(string path, bool startFullscreen = false) { if (_gameLoaded) { @@ -710,9 +814,6 @@ namespace Ryujinx.Ui UpdateGraphicsConfig(); - SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - - bool isDirectory = Directory.Exists(path); bool isFirmwareTitle = false; if (path.StartsWith("@SystemContent")) @@ -722,124 +823,10 @@ namespace Ryujinx.Ui isFirmwareTitle = true; } - if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError)) + if (!LoadApplication(path, isFirmwareTitle)) { - if (SetupValidator.CanFixStartApplication(_contentManager, path, userError, out firmwareVersion)) - { - if (userError == UserError.NoFirmware) - { - string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})"; - - ResponseType responseDialog = (ResponseType)GtkDialog.CreateConfirmationDialog("No Firmware Installed", message).Run(); - - if (responseDialog != ResponseType.Yes) - { - UserErrorDialog.CreateUserErrorDialog(userError); - - _emulationContext.Dispose(); - SwitchToGameTable(); - - return; - } - } - - if (!SetupValidator.TryFixStartApplication(_contentManager, path, userError, out _)) - { - UserErrorDialog.CreateUserErrorDialog(userError); - - _emulationContext.Dispose(); - SwitchToGameTable(); - - return; - } - - // Tell the user that we installed a firmware for them. - if (userError == UserError.NoFirmware) - { - firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - - RefreshFirmwareLabel(); - - string message = $"No installed firmware was found but Ryujinx was able to install firmware {firmwareVersion.VersionString} from the provided game.\nThe emulator will now start."; - - GtkDialog.CreateInfoDialog($"Firmware {firmwareVersion.VersionString} was installed", message); - } - } - else - { - UserErrorDialog.CreateUserErrorDialog(userError); - - _emulationContext.Dispose(); - SwitchToGameTable(); - - return; - } - } - - Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); - - if (isFirmwareTitle) - { - Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - - _emulationContext.LoadNca(path); - } - else if (Directory.Exists(path)) - { - string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); - - if (romFsFiles.Length == 0) - { - romFsFiles = Directory.GetFiles(path, "*.romfs"); - } - - if (romFsFiles.Length > 0) - { - Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - _emulationContext.LoadCart(path, romFsFiles[0]); - } - else - { - Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - _emulationContext.LoadCart(path); - } - } - else if (File.Exists(path)) - { - switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) - { - case ".xci": - Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - _emulationContext.LoadXci(path); - break; - case ".nca": - Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - _emulationContext.LoadNca(path); - break; - case ".nsp": - case ".pfs0": - Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - _emulationContext.LoadNsp(path); - break; - default: - Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); - try - { - _emulationContext.LoadProgram(path); - } - catch (ArgumentOutOfRangeException) - { - Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); - } - break; - } - } - else - { - Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - _emulationContext.Dispose(); - RendererWidget.Dispose(); + SwitchToGameTable(); return; } @@ -852,10 +839,7 @@ namespace Ryujinx.Ui Translator.IsReadyForTranslation.Reset(); - Thread windowThread = new Thread(() => - { - CreateGameWindow(); - }) + Thread windowThread = new(CreateGameWindow) { Name = "GUI.WindowThread" }; @@ -871,9 +855,10 @@ namespace Ryujinx.Ui _firmwareInstallFile.Sensitive = false; _firmwareInstallDirectory.Sensitive = false; - DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Application.TitleIdText, _emulationContext.Application.TitleName); + DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText, + _emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString()); - _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Application.TitleIdText, appMetadata => + _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow.ToString(); }); @@ -1055,7 +1040,7 @@ namespace Ryujinx.Ui if (_emulationContext != null) { - UpdateGameMetadata(_emulationContext.Application.TitleIdText); + UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText); if (RendererWidget != null) { @@ -1174,7 +1159,7 @@ namespace Ryujinx.Ui string path = (string)_tableStore.GetValue(treeIter, 9); - LoadApplication(path); + RunApplication(path); } private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args) @@ -1260,7 +1245,7 @@ namespace Ryujinx.Ui if (fileChooser.Run() == (int)ResponseType.Accept) { - LoadApplication(fileChooser.Filename); + RunApplication(fileChooser.Filename); } } } @@ -1271,7 +1256,7 @@ namespace Ryujinx.Ui { if (fileChooser.Run() == (int)ResponseType.Accept) { - LoadApplication(fileChooser.Filename); + RunApplication(fileChooser.Filename); } } } @@ -1287,7 +1272,7 @@ namespace Ryujinx.Ui { string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); - LoadApplication(contentPath); + RunApplication(contentPath); } private void Open_Ryu_Folder(object sender, EventArgs args) @@ -1328,7 +1313,7 @@ namespace Ryujinx.Ui { if (_emulationContext != null) { - UpdateGameMetadata(_emulationContext.Application.TitleIdText); + UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText); } _pauseEmulation.Sensitive = false; @@ -1533,7 +1518,7 @@ namespace Ryujinx.Ui { _userChannelPersistence.ShouldRestart = false; - LoadApplication(_currentEmulatedGamePath); + RunApplication(_currentEmulatedGamePath); } else { @@ -1596,7 +1581,9 @@ namespace Ryujinx.Ui private void ManageCheats_Pressed(object sender, EventArgs args) { - var window = new CheatWindow(_virtualFileSystem, _emulationContext.Application.TitleId, _emulationContext.Application.TitleName); + var window = new CheatWindow(_virtualFileSystem, + _emulationContext.Processes.ActiveApplication.ProgramId, + _emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString()); window.Destroyed += CheatWindow_Destroyed; window.Show(); @@ -1639,7 +1626,7 @@ namespace Ryujinx.Ui LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll, LastScannedAmiiboId = _lastScannedAmiiboId, DeviceId = deviceId, - TitleId = _emulationContext.Application.TitleIdText.ToUpper() + TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.ToUpper() }; amiiboWindow.DeleteEvent += AmiiboWindow_DeleteEvent; diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index e5d22d65c..7cb5b3275 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -495,16 +495,14 @@ namespace Ryujinx.Ui { parent.Present(); - string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty - : $" - {Device.Application.TitleName}"; + var activeProcess = Device.Processes.ActiveApplication; + var nacp = activeProcess.ApplicationControlProperties; + int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage; - string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty - : $" v{Device.Application.DisplayVersion}"; - - string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty - : $" ({Device.Application.TitleIdText.ToUpper()})"; - - string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)"; + string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}"; + string titleVersionSection = string.IsNullOrWhiteSpace(nacp.DisplayVersionString.ToString()) ? string.Empty : $" v{nacp.DisplayVersionString.ToString()}"; + string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})"; + string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); @@ -612,7 +610,7 @@ namespace Ryujinx.Ui { if (!ParentWindow.State.HasFlag(WindowState.Fullscreen)) { - Device.Application.DiskCacheLoadState?.Cancel(); + Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); } } }); diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index a63d68ff2..558288aab 100644 --- a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -15,6 +15,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Windows; @@ -260,7 +261,7 @@ namespace Ryujinx.Ui.Widgets return; } - (Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); if (updatePatchNca != null) { diff --git a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs index 4aea58955..fce751da1 100644 --- a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs +++ b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs @@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; +using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; @@ -94,7 +95,7 @@ namespace Ryujinx.Ui.Windows try { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); + (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); if (controlNca != null && patchNca != null) { From 210557951bd2393f31876d9fc8861e029f64a51a Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 1 Apr 2023 09:27:34 +0200 Subject: [PATCH 426/737] nuget: bump Avalonia dependencies from 0.10.18 to 0.10.19 (#4602) * infra: Update Avalonia to 0.10.19 * infra: Update XamlNameReferenceGenerator to 1.6.1 --- Directory.Packages.props | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 921172ab9..e4b23706d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,11 +3,11 @@ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> </PropertyGroup> <ItemGroup> - <PackageVersion Include="Avalonia" Version="0.10.18" /> - <PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" /> - <PackageVersion Include="Avalonia.Desktop" Version="0.10.18" /> - <PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" /> - <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" /> + <PackageVersion Include="Avalonia" Version="0.10.19" /> + <PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" /> + <PackageVersion Include="Avalonia.Desktop" Version="0.10.19" /> + <PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" /> + <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" /> <PackageVersion Include="Avalonia.Svg" Version="0.10.18" /> <PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" /> @@ -48,6 +48,6 @@ <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> - <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> + <PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" /> </ItemGroup> -</Project> +</Project> \ No newline at end of file From f5a6f45b27d43f8314227e4a8e7f9ddd681a4277 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 1 Apr 2023 10:05:02 +0200 Subject: [PATCH 427/737] vulkan: Separate debug utils logic from VulkanInitialization (#4609) * vulkan: Separate debug utils logic from VulkanInitialization Also checks for VK_EXT_debug_utils existence instead of force enabling it and allow possible error during messenger init * Address gdkchan's comment * Use CreateDebugUtilsMessenger Span variant --- .../VulkanDebugMessenger.cs | 153 ++++++++++++++++++ .../VulkanInitialization.cs | 115 +------------ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 15 +- 3 files changed, 165 insertions(+), 118 deletions(-) create mode 100644 Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs b/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs new file mode 100644 index 000000000..7e39a251e --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs @@ -0,0 +1,153 @@ +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.EXT; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan +{ + class VulkanDebugMessenger : IDisposable + { + private static string[] _excludedMessages = new string[] + { + // NOTE: Done on purpose right now. + "UNASSIGNED-CoreValidation-Shader-OutputNotConsumed", + // TODO: Figure out if fixable + "VUID-vkCmdDrawIndexed-None-04584", + // TODO: Might be worth looking into making this happy to possibly optimize copies. + "UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout", + // TODO: Fix this, it's causing too much noise right now. + "VUID-VkSubpassDependency-srcSubpass-00867" + }; + + private readonly Vk _api; + private readonly Instance _instance; + private readonly GraphicsDebugLevel _logLevel; + private readonly ExtDebugUtils _debugUtils; + private readonly DebugUtilsMessengerEXT? _debugUtilsMessenger; + private bool _disposed; + + public VulkanDebugMessenger(Vk api, Instance instance, GraphicsDebugLevel logLevel) + { + _api = api; + _instance = instance; + _logLevel = logLevel; + + _api.TryGetInstanceExtension(instance, out _debugUtils); + + Result result = TryInitialize(out _debugUtilsMessenger); + + if (result != Result.Success) + { + Logger.Error?.Print(LogClass.Gpu, $"Vulkan debug messenger initialization failed with error {result}"); + } + } + + private Result TryInitialize(out DebugUtilsMessengerEXT? debugUtilsMessengerHandle) + { + debugUtilsMessengerHandle = null; + + if (_debugUtils != null && _logLevel != GraphicsDebugLevel.None) + { + var messageType = _logLevel switch + { + GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt, + GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | + DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, + GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | + DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | + DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, + _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".") + }; + + var messageSeverity = _logLevel switch + { + GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, + GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt | + DebugUtilsMessageSeverityFlagsEXT.WarningBitExt, + GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt | + DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | + DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | + DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, + _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".") + }; + + var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT() + { + SType = StructureType.DebugUtilsMessengerCreateInfoExt, + MessageType = messageType, + MessageSeverity = messageSeverity + }; + + unsafe + { + debugUtilsMessengerCreateInfo.PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(UserCallback); + } + + DebugUtilsMessengerEXT messengerHandle = default; + + Result result = _debugUtils.CreateDebugUtilsMessenger(_instance, SpanHelpers.AsReadOnlySpan(ref debugUtilsMessengerCreateInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref messengerHandle)); + + if (result == Result.Success) + { + debugUtilsMessengerHandle = messengerHandle; + } + + return result; + } + + return Result.Success; + } + + private unsafe static uint UserCallback( + DebugUtilsMessageSeverityFlagsEXT messageSeverity, + DebugUtilsMessageTypeFlagsEXT messageTypes, + DebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { + var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage); + + foreach (string excludedMessagePart in _excludedMessages) + { + if (msg.Contains(excludedMessagePart)) + { + return 0; + } + } + + if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) + { + Logger.Error?.Print(LogClass.Gpu, msg); + } + else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) + { + Logger.Warning?.Print(LogClass.Gpu, msg); + } + else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt)) + { + Logger.Info?.Print(LogClass.Gpu, msg); + } + else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt)) + { + Logger.Debug?.Print(LogClass.Gpu, msg); + } + + return 0; + } + + public void Dispose() + { + if (!_disposed) + { + if (_debugUtilsMessenger.HasValue) + { + _debugUtils.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger.Value, Span<AllocationCallbacks>.Empty); + } + + _disposed = true; + } + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index c4e9a6266..f04ab5c05 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -47,19 +47,7 @@ namespace Ryujinx.Graphics.Vulkan KhrSwapchain.ExtensionName }; - private static string[] _excludedMessages = new string[] - { - // NOTE: Done on purpose right now. - "UNASSIGNED-CoreValidation-Shader-OutputNotConsumed", - // TODO: Figure out if fixable - "VUID-vkCmdDrawIndexed-None-04584", - // TODO: Might be worth looking into making this happy to possibly optimize copies. - "UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout", - // TODO: Fix this, it's causing too much noise right now. - "VUID-VkSubpassDependency-srcSubpass-00867" - }; - - internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger) + internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions) { var enabledLayers = new List<string>(); @@ -95,7 +83,12 @@ namespace Ryujinx.Graphics.Vulkan AddAvailableLayer("VK_LAYER_KHRONOS_validation"); } - var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); + var enabledExtensions = requiredExtensions; + + if (api.IsInstanceExtensionPresent("VK_EXT_debug_utils")) + { + enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); + } var appName = Marshal.StringToHGlobalAnsi(AppName); @@ -145,47 +138,9 @@ namespace Ryujinx.Graphics.Vulkan Marshal.FreeHGlobal(ppEnabledLayers[i]); } - CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger); - return instance; } - private unsafe static uint DebugMessenger( - DebugUtilsMessageSeverityFlagsEXT messageSeverity, - DebugUtilsMessageTypeFlagsEXT messageTypes, - DebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { - var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage); - - foreach (string excludedMessagePart in _excludedMessages) - { - if (msg.Contains(excludedMessagePart)) - { - return 0; - } - } - - if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) - { - Logger.Error?.Print(LogClass.Gpu, msg); - } - else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) - { - Logger.Warning?.Print(LogClass.Gpu, msg); - } - else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt)) - { - Logger.Info?.Print(LogClass.Gpu, msg); - } - else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt)) - { - Logger.Debug?.Print(LogClass.Gpu, msg); - } - - return 0; - } - internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId) { uint physicalDeviceCount; @@ -671,61 +626,5 @@ namespace Ryujinx.Graphics.Vulkan return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray(); } - - internal unsafe static void CreateDebugMessenger( - Vk api, - GraphicsDebugLevel logLevel, - Instance instance, - out ExtDebugUtils debugUtils, - out DebugUtilsMessengerEXT debugUtilsMessenger) - { - debugUtils = default; - - if (logLevel != GraphicsDebugLevel.None) - { - if (!api.TryGetInstanceExtension(instance, out debugUtils)) - { - debugUtilsMessenger = default; - return; - } - - var filterLogType = logLevel switch - { - GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt, - GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | - DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, - GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | - DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | - DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, - _ => throw new ArgumentException($"Invalid log level \"{logLevel}\".") - }; - - var filterLogSeverity = logLevel switch - { - GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, - GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt | - DebugUtilsMessageSeverityFlagsEXT.WarningBitExt, - GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt | - DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | - DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | - DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, - _ => throw new ArgumentException($"Invalid log level \"{logLevel}\".") - }; - - var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT() - { - SType = StructureType.DebugUtilsMessengerCreateInfoExt, - MessageType = filterLogType, - MessageSeverity = filterLogSeverity, - PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger) - }; - - debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError(); - } - else - { - debugUtilsMessenger = default; - } - } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index d8cb5e2b6..81dec12ec 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -36,7 +36,6 @@ namespace Ryujinx.Graphics.Vulkan internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } - internal ExtDebugUtils DebugUtilsApi { get; private set; } internal uint QueueFamilyIndex { get; private set; } internal Queue Queue { get; private set; } @@ -57,11 +56,11 @@ namespace Ryujinx.Graphics.Vulkan internal HashSet<ITexture> Textures { get; } internal HashSet<SamplerHolder> Samplers { get; } + private VulkanDebugMessenger _debugMessenger; private Counters _counters; private SyncManager _syncManager; private PipelineFull _pipeline; - private DebugUtilsMessengerEXT _debugUtilsMessenger; internal HelperShader HelperShader { get; private set; } internal PipelineFull PipelineInternal => _pipeline; @@ -345,9 +344,8 @@ namespace Ryujinx.Graphics.Vulkan Api = api; - _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugUtils debugUtils, out _debugUtilsMessenger); - - DebugUtilsApi = debugUtils; + _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions()); + _debugMessenger = new VulkanDebugMessenger(api, _instance, logLevel); if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi)) { @@ -794,11 +792,6 @@ namespace Ryujinx.Graphics.Vulkan MemoryAllocator.Dispose(); - if (_debugUtilsMessenger.Handle != 0) - { - DebugUtilsApi.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger, null); - } - foreach (var shader in Shaders) { shader.Dispose(); @@ -818,6 +811,8 @@ namespace Ryujinx.Graphics.Vulkan Api.DestroyDevice(_device, null); + _debugMessenger.Dispose(); + // Last step destroy the instance Api.DestroyInstance(_instance, null); } From 1b41b285ac7f551c3495ced436ce3930ad7223b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Apr 2023 08:23:09 +0000 Subject: [PATCH 428/737] nuget: bump DynamicData from 7.12.11 to 7.13.1 (#4490) Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.12.11 to 7.13.1. - [Release notes](https://github.com/reactiveui/DynamicData/releases) - [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md) - [Commits](https://github.com/reactiveui/DynamicData/compare/7.12.11...7.13.1) --- updated-dependencies: - dependency-name: DynamicData dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index e4b23706d..8e9aef4ee 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,7 +13,7 @@ <PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> - <PackageVersion Include="DynamicData" Version="7.12.11" /> + <PackageVersion Include="DynamicData" Version="7.13.1" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> From 3249f8ff41446b47cf458bbc04f8c27e005889aa Mon Sep 17 00:00:00 2001 From: Andrey Sukharev <SukharevAndrey@users.noreply.github.com> Date: Mon, 3 Apr 2023 13:14:19 +0300 Subject: [PATCH 429/737] Source generated json serializers (#4582) * Use source generated json serializers in order to improve code trimming * Use strongly typed github releases model to fetch updates instead of raw Newtonsoft.Json parsing * Use separate model for LogEventArgs serialization * Make dynamic object formatter static. Fix string builder pooling. * Do not inherit json version of LogEventArgs from EventArgs * Fix extra space in object formatting * Write log json directly to stream instead of using buffer writer * Rebase fixes * Rebase fixes * Rebase fixes * Enforce block-scoped namespaces in the solution. Convert style for existing code * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Rebase indent fix * Fix indent * Delete unnecessary json properties * Rebase fix * Remove overridden json property names as they are handled in the options * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Use default json options in github api calls * Indentation and spacing fixes * Fix json serialization * Fix missing JsonConverter for config enums * Add double \n\n after the whole string, not inside join --------- Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- .editorconfig | 4 + ARMeilleure/Decoders/IOpCode32Exception.cs | 9 +- Ryujinx.Ava/Common/Locale/LocaleManager.cs | 2 +- Ryujinx.Ava/Modules/Updater/Updater.cs | 22 +- Ryujinx.Ava/UI/Models/Amiibo.cs | 72 ---- .../UI/ViewModels/AboutWindowViewModel.cs | 2 +- .../UI/ViewModels/AmiiboWindowViewModel.cs | 35 +- .../ViewModels/ControllerSettingsViewModel.cs | 9 +- .../DownloadableContentManagerViewModel.cs | 10 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 367 +++++++++--------- .../UI/Views/Main/MainMenuBarView.axaml.cs | 2 +- Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs | 6 +- .../UI/Windows/TitleUpdateWindow.axaml.cs | 3 - Ryujinx.Common/Configuration/AntiAliasing.cs | 8 +- .../Configuration/AspectRatioExtensions.cs | 6 +- .../Configuration/BackendThreading.cs | 6 +- ...ownloadableContentJsonSerializerContext.cs | 11 + .../Configuration/GraphicsBackend.cs | 6 +- .../Configuration/GraphicsDebugLevel.cs | 4 + .../JsonMotionConfigControllerConverter.cs | 13 +- .../Motion/MotionConfigController.cs | 5 +- .../MotionConfigJsonSerializerContext.cs | 12 + .../Motion/MotionInputBackendType.cs | 6 +- .../Configuration/Hid/ControllerType.cs | 5 +- .../Configuration/Hid/InputBackendType.cs | 6 +- .../Configuration/Hid/InputConfig.cs | 2 + .../Hid/InputConfigJsonSerializerContext.cs | 14 + .../Hid/JsonInputConfigConverter.cs | 13 +- Ryujinx.Common/Configuration/Hid/Key.cs | 8 +- .../Configuration/Hid/KeyboardHotkeys.cs | 4 +- .../Configuration/Hid/PlayerIndex.cs | 4 + .../Configuration/MemoryManagerMode.cs | 6 +- Ryujinx.Common/Configuration/ScalingFilter.cs | 4 + ...itleUpdateMetadataJsonSerializerContext.cs | 10 + .../Logging/Formatters/DefaultLogFormatter.cs | 54 +-- .../Formatters/DynamicObjectFormatter.cs | 84 ++++ Ryujinx.Common/Logging/LogClass.cs | 4 + Ryujinx.Common/Logging/LogEventArgs.cs | 10 +- Ryujinx.Common/Logging/LogEventArgsJson.cs | 30 ++ .../Logging/LogEventJsonSerializerContext.cs | 9 + Ryujinx.Common/Logging/LogLevel.cs | 4 + .../Logging/Targets/JsonLogTarget.cs | 12 +- Ryujinx.Common/Utilities/CommonJsonContext.cs | 11 + Ryujinx.Common/Utilities/JsonHelper.cs | 122 +++--- .../Utilities/TypedStringEnumConverter.cs | 34 ++ .../Account/Acc/AccountSaveDataManager.cs | 30 +- .../Acc/ProfilesJsonSerializerContext.cs | 11 + .../Account/Acc/Types/AccountState.cs | 4 + .../Account/Acc/Types/ProfilesJson.cs | 10 + .../Account/Acc/Types/UserProfileJson.cs | 12 + .../Nfc/Nfp/AmiiboJsonSerializerContext.cs | 10 + .../HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 10 +- .../PartitionFileSystemExtensions.cs | 7 +- Ryujinx.Headless.SDL2/Program.cs | 7 +- .../App/ApplicationJsonSerializerContext.cs | 10 + Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 17 +- .../Configuration/AudioBackend.cs | 6 +- .../Configuration/ConfigurationFileFormat.cs | 12 +- .../ConfigurationFileFormatSettings.cs | 9 + .../ConfigurationJsonSerializerContext.cs | 10 + .../Configuration/ConfigurationState.cs | 5 +- .../Configuration/System/Language.cs | 6 +- .../Configuration/System/Region.cs | 6 +- Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs | 57 +++ .../Models/Amiibo/AmiiboApiGamesSwitch.cs | 15 + .../Models/Amiibo/AmiiboApiUsage.cs | 12 + Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs | 14 + .../Amiibo/AmiiboJsonSerializerContext.cs | 9 + .../Github/GithubReleaseAssetJsonResponse.cs | 9 + .../Github/GithubReleasesJsonResponse.cs | 10 + .../GithubReleasesJsonSerializerContext.cs | 9 + Ryujinx/Modules/Updater/Updater.cs | 24 +- Ryujinx/Ui/Windows/AboutWindow.cs | 2 +- Ryujinx/Ui/Windows/AmiiboWindow.cs | 65 +--- Ryujinx/Ui/Windows/ControllerWindow.cs | 11 +- Ryujinx/Ui/Windows/DlcWindow.cs | 15 +- Ryujinx/Ui/Windows/TitleUpdateWindow.cs | 15 +- 77 files changed, 904 insertions(+), 615 deletions(-) delete mode 100644 Ryujinx.Ava/UI/Models/Amiibo.cs create mode 100644 Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs create mode 100644 Ryujinx.Common/Logging/LogEventArgsJson.cs create mode 100644 Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs create mode 100644 Ryujinx.Common/Utilities/CommonJsonContext.cs create mode 100644 Ryujinx.Common/Utilities/TypedStringEnumConverter.cs create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs create mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs diff --git a/.editorconfig b/.editorconfig index 9e00e3bae..8a3054287 100644 --- a/.editorconfig +++ b/.editorconfig @@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion #### C# Coding Conventions #### +# Namespace preferences +csharp_style_namespace_declarations = block_scoped:warning +resharper_csharp_namespace_body = block_scoped + # var preferences csharp_style_var_elsewhere = false:silent csharp_style_var_for_built_in_types = false:silent diff --git a/ARMeilleure/Decoders/IOpCode32Exception.cs b/ARMeilleure/Decoders/IOpCode32Exception.cs index 82819bddd..8f0fb81a0 100644 --- a/ARMeilleure/Decoders/IOpCode32Exception.cs +++ b/ARMeilleure/Decoders/IOpCode32Exception.cs @@ -1,6 +1,7 @@ -namespace ARMeilleure.Decoders; - -interface IOpCode32Exception +namespace ARMeilleure.Decoders { - int Id { get; } + interface IOpCode32Exception + { + int Id { get; } + } } \ No newline at end of file diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index 1374bfee1..464ab780d 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -130,7 +130,7 @@ namespace Ryujinx.Ava.Common.Locale { var localeStrings = new Dictionary<LocaleKeys, string>(); string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); - var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); + var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); foreach (var item in strings) { diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index e89abd1da..c58575284 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -4,13 +4,14 @@ using FluentAvalonia.UI.Controls; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; -using Newtonsoft.Json.Linq; using Ryujinx.Ava; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Helper; +using Ryujinx.Ui.Common.Models.Github; using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,6 +32,7 @@ namespace Ryujinx.Modules internal static class Updater { private const string GitHubApiURL = "https://api.github.com"; + private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); @@ -99,22 +101,16 @@ namespace Ryujinx.Modules string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - JObject jsonRoot = JObject.Parse(fetchedJson); - JToken assets = jsonRoot["assets"]; + var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); + _buildVer = fetched.Name; - _buildVer = (string)jsonRoot["name"]; - - foreach (JToken asset in assets) + foreach (var asset in fetched.Assets) { - string assetName = (string)asset["name"]; - string assetState = (string)asset["state"]; - string downloadURL = (string)asset["browser_download_url"]; - - if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt)) + if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt)) { - _buildUrl = downloadURL; + _buildUrl = asset.BrowserDownloadUrl; - if (assetState != "uploaded") + if (asset.State != "uploaded") { if (showVersionUpToDate) { diff --git a/Ryujinx.Ava/UI/Models/Amiibo.cs b/Ryujinx.Ava/UI/Models/Amiibo.cs deleted file mode 100644 index d0ccafd08..000000000 --- a/Ryujinx.Ava/UI/Models/Amiibo.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ryujinx.Ava.UI.Models -{ - public class Amiibo - { - public struct AmiiboJson - { - [JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; } - [JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; } - } - - public struct AmiiboApi - { - [JsonPropertyName("name")] public string Name { get; set; } - [JsonPropertyName("head")] public string Head { get; set; } - [JsonPropertyName("tail")] public string Tail { get; set; } - [JsonPropertyName("image")] public string Image { get; set; } - [JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; } - [JsonPropertyName("character")] public string Character { get; set; } - [JsonPropertyName("gameSeries")] public string GameSeries { get; set; } - [JsonPropertyName("type")] public string Type { get; set; } - - [JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; } - - [JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } - - public override string ToString() - { - return Name; - } - - public string GetId() - { - return Head + Tail; - } - - public override bool Equals(object obj) - { - if (obj is AmiiboApi amiibo) - { - return amiibo.Head + amiibo.Tail == Head + Tail; - } - - return false; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - } - - public class AmiiboApiGamesSwitch - { - [JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; } - - [JsonPropertyName("gameID")] public List<string> GameId { get; set; } - - [JsonPropertyName("gameName")] public string GameName { get; set; } - } - - public class AmiiboApiUsage - { - [JsonPropertyName("Usage")] public string Usage { get; set; } - - [JsonPropertyName("write")] public bool Write { get; set; } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs index 872c1a37f..479411cba 100644 --- a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs @@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels { string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n"; + Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)) + "\n\n"; } catch { diff --git a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index 5311318c5..090c13a97 100644 --- a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -4,11 +4,11 @@ using Avalonia.Media.Imaging; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; +using Ryujinx.Ui.Common.Models.Amiibo; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -17,6 +17,7 @@ using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; +using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ava.UI.ViewModels { @@ -31,8 +32,8 @@ namespace Ryujinx.Ava.UI.ViewModels private readonly StyleableWindow _owner; private Bitmap _amiiboImage; - private List<Amiibo.AmiiboApi> _amiiboList; - private AvaloniaList<Amiibo.AmiiboApi> _amiibos; + private List<AmiiboApi> _amiiboList; + private AvaloniaList<AmiiboApi> _amiibos; private ObservableCollection<string> _amiiboSeries; private int _amiiboSelectedIndex; @@ -41,6 +42,8 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _showAllAmiibo; private bool _useRandomUuid; private string _usage; + + private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) { @@ -52,9 +55,9 @@ namespace Ryujinx.Ava.UI.ViewModels Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); _amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); - _amiiboList = new List<Amiibo.AmiiboApi>(); + _amiiboList = new List<AmiiboApi>(); _amiiboSeries = new ObservableCollection<string>(); - _amiibos = new AvaloniaList<Amiibo.AmiiboApi>(); + _amiibos = new AvaloniaList<AmiiboApi>(); _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png"); @@ -94,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public AvaloniaList<Amiibo.AmiiboApi> AmiiboList + public AvaloniaList<AmiiboApi> AmiiboList { get => _amiibos; set @@ -187,9 +190,9 @@ namespace Ryujinx.Ava.UI.ViewModels if (File.Exists(_amiiboJsonPath)) { - amiiboJsonString = File.ReadAllText(_amiiboJsonPath); + amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -206,7 +209,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - _amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo; + _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); ParseAmiiboData(); @@ -223,7 +226,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!ShowAllAmiibo) { - foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) + foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) { if (game != null) { @@ -255,7 +258,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void SelectLastScannedAmiibo() { - Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); + AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries); AmiiboSelectedIndex = AmiiboList.IndexOf(scanned); @@ -270,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList + List<AmiiboApi> amiiboSortedList = _amiiboList .Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex]) .OrderBy(amiibo => amiibo.Name).ToList(); @@ -280,7 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (!_showAllAmiibo) { - foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) + foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) { if (game != null) { @@ -314,7 +317,7 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; + AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image; @@ -326,11 +329,11 @@ namespace Ryujinx.Ava.UI.ViewModels { bool writable = false; - foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch) + foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch) { if (item.GameId.Contains(TitleId)) { - foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage) + foreach (AmiiboApiUsage usageItem in item.AmiiboUsage) { usageString += Environment.NewLine + $"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"; diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs index 35256b3b5..dd261b103 100644 --- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs @@ -51,6 +51,8 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _isLoaded; private readonly UserControl _owner; + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public IGamepadDriver AvaloniaKeyboardDriver { get; } public IGamepad SelectedGamepad { get; private set; } @@ -706,10 +708,7 @@ namespace Ryujinx.Ava.UI.ViewModels try { - using (Stream stream = File.OpenRead(path)) - { - config = JsonHelper.Deserialize<InputConfig>(stream); - } + config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); } catch (JsonException) { } catch (InvalidOperationException) @@ -775,7 +774,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.ControllerType = Controllers[_controller].Type; - string jsonString = JsonHelper.Serialize(config, true); + string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig); await File.WriteAllTextAsync(path, jsonString); diff --git a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs index e5e4f66b5..1d7da9a40 100644 --- a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -21,7 +21,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; using Path = System.IO.Path; @@ -41,6 +40,8 @@ namespace Ryujinx.Ava.UI.ViewModels private ulong _titleId; private string _titleName; + private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public AvaloniaList<DownloadableContentModel> DownloadableContents { get => _downloadableContents; @@ -100,7 +101,7 @@ namespace Ryujinx.Ava.UI.ViewModels try { - _downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath); + _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer); } catch { @@ -330,10 +331,7 @@ namespace Ryujinx.Ava.UI.ViewModels _downloadableContentContainerList.Add(container); } - using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) - { - downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true))); - } + JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer); } } diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index 0798502cd..1f4e3c624 100644 --- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -22,230 +22,231 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using Path = System.IO.Path; using SpanHelpers = LibHac.Common.SpanHelpers; -namespace Ryujinx.Ava.UI.ViewModels; - -public class TitleUpdateViewModel : BaseModel +namespace Ryujinx.Ava.UI.ViewModels { - public TitleUpdateMetadata _titleUpdateWindowData; - public readonly string _titleUpdateJsonPath; - private VirtualFileSystem _virtualFileSystem { get; } - private ulong _titleId { get; } - private string _titleName { get; } - - private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); - private AvaloniaList<object> _views = new(); - private object _selectedUpdate; - - public AvaloniaList<TitleUpdateModel> TitleUpdates + public class TitleUpdateViewModel : BaseModel { - get => _titleUpdates; - set + public TitleUpdateMetadata _titleUpdateWindowData; + public readonly string _titleUpdateJsonPath; + private VirtualFileSystem _virtualFileSystem { get; } + private ulong _titleId { get; } + private string _titleName { get; } + + private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); + private AvaloniaList<object> _views = new(); + private object _selectedUpdate; + + private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + public AvaloniaList<TitleUpdateModel> TitleUpdates { - _titleUpdates = value; - OnPropertyChanged(); - } - } - - public AvaloniaList<object> Views - { - get => _views; - set - { - _views = value; - OnPropertyChanged(); - } - } - - public object SelectedUpdate - { - get => _selectedUpdate; - set - { - _selectedUpdate = value; - OnPropertyChanged(); - } - } - - public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) - { - _virtualFileSystem = virtualFileSystem; - - _titleId = titleId; - _titleName = titleName; - - _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); - - try - { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath); - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); - - _titleUpdateWindowData = new TitleUpdateMetadata + get => _titleUpdates; + set { - Selected = "", - Paths = new List<string>() - }; - - Save(); - } - - LoadUpdates(); - } - - private void LoadUpdates() - { - foreach (string path in _titleUpdateWindowData.Paths) - { - AddUpdate(path); - } - - TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); - - SelectedUpdate = selected; - - // NOTE: Save the list again to remove leftovers. - Save(); - - SortUpdates(); - } - - public void SortUpdates() - { - var list = TitleUpdates.ToList(); - - list.Sort((first, second) => - { - if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) - { - return -1; - } - else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) - { - return 1; - } - - return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; - }); - - Views.Clear(); - Views.Add(new BaseModel()); - Views.AddRange(list); - - if (SelectedUpdate == null) - { - SelectedUpdate = Views[0]; - } - else if (!TitleUpdates.Contains(SelectedUpdate)) - { - if (Views.Count > 1) - { - SelectedUpdate = Views[1]; - } - else - { - SelectedUpdate = Views[0]; + _titleUpdates = value; + OnPropertyChanged(); } } - } - private void AddUpdate(string path) - { - if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) + public AvaloniaList<object> Views { - using FileStream file = new(path, FileMode.Open, FileAccess.Read); + get => _views; + set + { + _views = value; + OnPropertyChanged(); + } + } + + public object SelectedUpdate + { + get => _selectedUpdate; + set + { + _selectedUpdate = value; + OnPropertyChanged(); + } + } + + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + _virtualFileSystem = virtualFileSystem; + + _titleId = titleId; + _titleName = titleName; + + _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); try { - (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata); + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); - if (controlNca != null && patchNca != null) + _titleUpdateWindowData = new TitleUpdateMetadata { - ApplicationControlProperty controlData = new(); + Selected = "", + Paths = new List<string>() + }; - using UniqueRef<IFile> nacpFile = new(); + Save(); + } - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + LoadUpdates(); + } - TitleUpdates.Add(new TitleUpdateModel(controlData, path)); + private void LoadUpdates() + { + foreach (string path in _titleUpdateWindowData.Paths) + { + AddUpdate(path); + } + + TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); + + SelectedUpdate = selected; + + // NOTE: Save the list again to remove leftovers. + Save(); + SortUpdates(); + } + + public void SortUpdates() + { + var list = TitleUpdates.ToList(); + + list.Sort((first, second) => + { + if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) + { + return -1; + } + else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) + { + return 1; + } + + return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; + }); + + Views.Clear(); + Views.Add(new BaseModel()); + Views.AddRange(list); + + if (SelectedUpdate == null) + { + SelectedUpdate = Views[0]; + } + else if (!TitleUpdates.Contains(SelectedUpdate)) + { + if (Views.Count > 1) + { + SelectedUpdate = Views[1]; } else + { + SelectedUpdate = Views[0]; + } + } + } + + private void AddUpdate(string path) + { + if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) + { + using FileStream file = new(path, FileMode.Open, FileAccess.Read); + + try + { + (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + + if (controlNca != null && patchNca != null) + { + ApplicationControlProperty controlData = new(); + + using UniqueRef<IFile> nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + TitleUpdates.Add(new TitleUpdateModel(controlData, path)); + } + else + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); + }); + } + } + catch (Exception ex) { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)); }); } } - catch (Exception ex) - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)); - }); - } } - } - public void RemoveUpdate(TitleUpdateModel update) - { - TitleUpdates.Remove(update); - - SortUpdates(); - } - - public async void Add() - { - OpenFileDialog dialog = new() + public void RemoveUpdate(TitleUpdateModel update) { - Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], - AllowMultiple = true - }; + TitleUpdates.Remove(update); - dialog.Filters.Add(new FileDialogFilter + SortUpdates(); + } + + public async void Add() { - Name = "NSP", - Extensions = { "nsp" } - }); - - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - string[] files = await dialog.ShowAsync(desktop.MainWindow); - - if (files != null) + OpenFileDialog dialog = new() { - foreach (string file in files) + Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], + AllowMultiple = true + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = "NSP", + Extensions = { "nsp" } + }); + + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null) { - AddUpdate(file); + foreach (string file in files) + { + AddUpdate(file); + } } } + + SortUpdates(); } - SortUpdates(); - } - - public void Save() - { - _titleUpdateWindowData.Paths.Clear(); - _titleUpdateWindowData.Selected = ""; - - foreach (TitleUpdateModel update in TitleUpdates) + public void Save() { - _titleUpdateWindowData.Paths.Add(update.Path); + _titleUpdateWindowData.Paths.Clear(); + _titleUpdateWindowData.Selected = ""; - if (update == SelectedUpdate) + foreach (TitleUpdateModel update in TitleUpdates) { - _titleUpdateWindowData.Selected = update.Path; - } - } + _titleUpdateWindowData.Paths.Add(update.Path); - File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); + if (update == SelectedUpdate) + { + _titleUpdateWindowData.Selected = update.Path; + } + } + + JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); + } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 30d411508..c1eaf86fe 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Main { string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last(); string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}"); - var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); + var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); if (!strings.TryGetValue("Language", out string languageName)) { diff --git a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs index 5368a1333..206d0a7ea 100644 --- a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs @@ -1,7 +1,7 @@ using Avalonia.Interactivity; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ui.Common.Models.Amiibo; namespace Ryujinx.Ava.UI.Windows { @@ -35,14 +35,14 @@ namespace Ryujinx.Ava.UI.Windows } public bool IsScanned { get; set; } - public Amiibo.AmiiboApi ScannedAmiibo { get; set; } + public AmiiboApi ScannedAmiibo { get; set; } public AmiiboWindowViewModel ViewModel { get; set; } private void ScanButton_Click(object sender, RoutedEventArgs e) { if (ViewModel.AmiiboSelectedIndex > -1) { - Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex]; + AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex]; ScannedAmiibo = amiibo; IsScanned = true; Close(); diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 1b50c46f3..153ce95d2 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -6,11 +6,8 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.Common.Helper; -using System.IO; -using System.Text; using System.Threading.Tasks; using Button = Avalonia.Controls.Button; diff --git a/Ryujinx.Common/Configuration/AntiAliasing.cs b/Ryujinx.Common/Configuration/AntiAliasing.cs index 6543598c7..159108ae4 100644 --- a/Ryujinx.Common/Configuration/AntiAliasing.cs +++ b/Ryujinx.Common/Configuration/AntiAliasing.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))] public enum AntiAliasing { None, @@ -9,4 +13,4 @@ SmaaHigh, SmaaUltra } -} +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/AspectRatioExtensions.cs b/Ryujinx.Common/Configuration/AspectRatioExtensions.cs index 3d0be88e9..5e97ed19c 100644 --- a/Ryujinx.Common/Configuration/AspectRatioExtensions.cs +++ b/Ryujinx.Common/Configuration/AspectRatioExtensions.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))] public enum AspectRatio { Fixed4x3, diff --git a/Ryujinx.Common/Configuration/BackendThreading.cs b/Ryujinx.Common/Configuration/BackendThreading.cs index cfc089146..8833b3f07 100644 --- a/Ryujinx.Common/Configuration/BackendThreading.cs +++ b/Ryujinx.Common/Configuration/BackendThreading.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))] public enum BackendThreading { Auto, diff --git a/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs b/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs new file mode 100644 index 000000000..132c45a44 --- /dev/null +++ b/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(List<DownloadableContentContainer>))] + public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/GraphicsBackend.cs b/Ryujinx.Common/Configuration/GraphicsBackend.cs index 26e4a28a9..d74dd6e19 100644 --- a/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))] public enum GraphicsBackend { Vulkan, diff --git a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs index 556af689a..ad12302a6 100644 --- a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs +++ b/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))] public enum GraphicsDebugLevel { None, diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs index d1c2e4e81..2b9e0af42 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.Common.Utilities; +using System; using System.Text.Json; using System.Text.Json.Serialization; @@ -6,6 +7,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController> { + private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader) { // Temporary reader to get the backend type @@ -52,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion return motionBackendType switch { - MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options), - MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options), + MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController), + MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController), _ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"), }; } @@ -63,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion switch (value.MotionBackend) { case MotionInputBackendType.GamepadDriver: - JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options); + JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController); break; case MotionInputBackendType.CemuHook: - JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options); + JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController); break; default: throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}"); diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs index 832aae0d1..7636aa414 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs @@ -1,5 +1,8 @@ -namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { + [JsonConverter(typeof(JsonMotionConfigControllerConverter))] public class MotionConfigController { public MotionInputBackendType MotionBackend { get; set; } diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs new file mode 100644 index 000000000..5cd9e452b --- /dev/null +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(MotionConfigController))] + [JsonSerializable(typeof(CemuHookMotionConfigController))] + [JsonSerializable(typeof(StandardMotionConfigController))] + public partial class MotionConfigJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs index 45d654edc..c65510478 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { + [JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))] public enum MotionInputBackendType : byte { Invalid, diff --git a/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/Ryujinx.Common/Configuration/Hid/ControllerType.cs index 0ad01bbb6..70f811c89 100644 --- a/Ryujinx.Common/Configuration/Hid/ControllerType.cs +++ b/Ryujinx.Common/Configuration/Hid/ControllerType.cs @@ -1,9 +1,12 @@ +using Ryujinx.Common.Utilities; using System; +using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { - [Flags] // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical + [Flags] + [JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))] public enum ControllerType : int { None, diff --git a/Ryujinx.Common/Configuration/Hid/InputBackendType.cs b/Ryujinx.Common/Configuration/Hid/InputBackendType.cs index 9e944f9e8..1db3f5703 100644 --- a/Ryujinx.Common/Configuration/Hid/InputBackendType.cs +++ b/Ryujinx.Common/Configuration/Hid/InputBackendType.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration.Hid +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid { + [JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))] public enum InputBackendType { Invalid, diff --git a/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/Ryujinx.Common/Configuration/Hid/InputConfig.cs index 3364e35fa..16c8f8e32 100644 --- a/Ryujinx.Common/Configuration/Hid/InputConfig.cs +++ b/Ryujinx.Common/Configuration/Hid/InputConfig.cs @@ -1,8 +1,10 @@ using System.ComponentModel; using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { + [JsonConverter(typeof(JsonInputConfigConverter))] public class InputConfig : INotifyPropertyChanged { /// <summary> diff --git a/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs b/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs new file mode 100644 index 000000000..254c4feb4 --- /dev/null +++ b/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs @@ -0,0 +1,14 @@ +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(InputConfig))] + [JsonSerializable(typeof(StandardKeyboardInputConfig))] + [JsonSerializable(typeof(StandardControllerInputConfig))] + public partial class InputConfigJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs b/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs index 7223ad451..08bbcbf17 100644 --- a/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs +++ b/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs @@ -1,13 +1,16 @@ using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Utilities; using System; using System.Text.Json; using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid { - class JsonInputConfigConverter : JsonConverter<InputConfig> + public class JsonInputConfigConverter : JsonConverter<InputConfig> { + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader) { // Temporary reader to get the backend type @@ -54,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid return backendType switch { - InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options), - InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options), + InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig), + InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig), _ => throw new InvalidOperationException($"Unknown backend type {backendType}"), }; } @@ -65,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid switch (value.Backend) { case InputBackendType.WindowKeyboard: - JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options); + JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig); break; case InputBackendType.GamepadSDL2: - JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options); + JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig); break; default: throw new ArgumentException($"Unknown backend type {value.Backend}"); diff --git a/Ryujinx.Common/Configuration/Hid/Key.cs b/Ryujinx.Common/Configuration/Hid/Key.cs index 194843a3f..3501b8aed 100644 --- a/Ryujinx.Common/Configuration/Hid/Key.cs +++ b/Ryujinx.Common/Configuration/Hid/Key.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration.Hid +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid { + [JsonConverter(typeof(TypedStringEnumConverter<Key>))] public enum Key { Unknown, @@ -136,4 +140,4 @@ Count } -} +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 45217e02f..8b6c8c145 100644 --- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Common.Configuration.Hid { - public class KeyboardHotkeys + public struct KeyboardHotkeys { public Key ToggleVsync { get; set; } public Key Screenshot { get; set; } @@ -12,4 +12,4 @@ public Key VolumeUp { get; set; } public Key VolumeDown { get; set; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs index 2e34cb96c..dd6495d4d 100644 --- a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs +++ b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs @@ -1,6 +1,10 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Configuration.Hid { // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical + [JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))] public enum PlayerIndex : int { Player1 = 0, diff --git a/Ryujinx.Common/Configuration/MemoryManagerMode.cs b/Ryujinx.Common/Configuration/MemoryManagerMode.cs index ad6c2a346..f10fd6f1b 100644 --- a/Ryujinx.Common/Configuration/MemoryManagerMode.cs +++ b/Ryujinx.Common/Configuration/MemoryManagerMode.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))] public enum MemoryManagerMode : byte { SoftwarePageTable, diff --git a/Ryujinx.Common/Configuration/ScalingFilter.cs b/Ryujinx.Common/Configuration/ScalingFilter.cs index 2095b89b1..e38c7d735 100644 --- a/Ryujinx.Common/Configuration/ScalingFilter.cs +++ b/Ryujinx.Common/Configuration/ScalingFilter.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))] public enum ScalingFilter { Bilinear, diff --git a/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs b/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs new file mode 100644 index 000000000..5b661b878 --- /dev/null +++ b/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(TitleUpdateMetadata))] + public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs index b9a08323e..28a7d5461 100644 --- a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs +++ b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs @@ -1,22 +1,20 @@ -using System; -using System.Reflection; -using System.Text; +using System.Text; namespace Ryujinx.Common.Logging { internal class DefaultLogFormatter : ILogFormatter { - private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>(); + private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); public string Format(LogEventArgs args) { - StringBuilder sb = _stringBuilderPool.Allocate(); + StringBuilder sb = StringBuilderPool.Allocate(); try { sb.Clear(); - sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time); + sb.Append($@"{args.Time:hh\:mm\:ss\.fff}"); sb.Append($" |{args.Level.ToString()[0]}| "); if (args.ThreadName != null) @@ -27,53 +25,17 @@ namespace Ryujinx.Common.Logging sb.Append(args.Message); - if (args.Data != null) + if (args.Data is not null) { - PropertyInfo[] props = args.Data.GetType().GetProperties(); - - sb.Append(" {"); - - foreach (var prop in props) - { - sb.Append(prop.Name); - sb.Append(": "); - - if (typeof(Array).IsAssignableFrom(prop.PropertyType)) - { - Array array = (Array)prop.GetValue(args.Data); - foreach (var item in array) - { - sb.Append(item.ToString()); - sb.Append(", "); - } - - if (array.Length > 0) - { - sb.Remove(sb.Length - 2, 2); - } - } - else - { - sb.Append(prop.GetValue(args.Data)); - } - - sb.Append(" ; "); - } - - // We remove the final ';' from the string - if (props.Length > 0) - { - sb.Remove(sb.Length - 3, 3); - } - - sb.Append('}'); + sb.Append(' '); + DynamicObjectFormatter.Format(sb, args.Data); } return sb.ToString(); } finally { - _stringBuilderPool.Release(sb); + StringBuilderPool.Release(sb); } } } diff --git a/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs b/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs new file mode 100644 index 000000000..5f15cc2a6 --- /dev/null +++ b/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs @@ -0,0 +1,84 @@ +#nullable enable +using System; +using System.Reflection; +using System.Text; + +namespace Ryujinx.Common.Logging +{ + internal class DynamicObjectFormatter + { + private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); + + public static string? Format(object? dynamicObject) + { + if (dynamicObject is null) + { + return null; + } + + StringBuilder sb = StringBuilderPool.Allocate(); + + try + { + Format(sb, dynamicObject); + + return sb.ToString(); + } + finally + { + StringBuilderPool.Release(sb); + } + } + + public static void Format(StringBuilder sb, object? dynamicObject) + { + if (dynamicObject is null) + { + return; + } + + PropertyInfo[] props = dynamicObject.GetType().GetProperties(); + + sb.Append('{'); + + foreach (var prop in props) + { + sb.Append(prop.Name); + sb.Append(": "); + + if (typeof(Array).IsAssignableFrom(prop.PropertyType)) + { + Array? array = (Array?) prop.GetValue(dynamicObject); + + if (array is not null) + { + foreach (var item in array) + { + sb.Append(item); + sb.Append(", "); + } + + if (array.Length > 0) + { + sb.Remove(sb.Length - 2, 2); + } + } + } + else + { + sb.Append(prop.GetValue(dynamicObject)); + } + + sb.Append(" ; "); + } + + // We remove the final ';' from the string + if (props.Length > 0) + { + sb.Remove(sb.Length - 3, 3); + } + + sb.Append('}'); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 7e53c972b..e62676cd3 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Logging { + [JsonConverter(typeof(TypedStringEnumConverter<LogClass>))] public enum LogClass { Application, diff --git a/Ryujinx.Common/Logging/LogEventArgs.cs b/Ryujinx.Common/Logging/LogEventArgs.cs index 511c8e6e2..a27af7809 100644 --- a/Ryujinx.Common/Logging/LogEventArgs.cs +++ b/Ryujinx.Common/Logging/LogEventArgs.cs @@ -11,15 +11,7 @@ namespace Ryujinx.Common.Logging public readonly string Message; public readonly object Data; - public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message) - { - Level = level; - Time = time; - ThreadName = threadName; - Message = message; - } - - public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data) + public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null) { Level = level; Time = time; diff --git a/Ryujinx.Common/Logging/LogEventArgsJson.cs b/Ryujinx.Common/Logging/LogEventArgsJson.cs new file mode 100644 index 000000000..425b97662 --- /dev/null +++ b/Ryujinx.Common/Logging/LogEventArgsJson.cs @@ -0,0 +1,30 @@ +using System; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Logging +{ + internal class LogEventArgsJson + { + public LogLevel Level { get; } + public TimeSpan Time { get; } + public string ThreadName { get; } + + public string Message { get; } + public string Data { get; } + + [JsonConstructor] + public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null) + { + Level = level; + Time = time; + ThreadName = threadName; + Message = message; + Data = data; + } + + public static LogEventArgsJson FromLogEventArgs(LogEventArgs args) + { + return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs b/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs new file mode 100644 index 000000000..da21f11e8 --- /dev/null +++ b/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Logging +{ + [JsonSerializable(typeof(LogEventArgsJson))] + internal partial class LogEventJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/LogLevel.cs b/Ryujinx.Common/Logging/LogLevel.cs index 8857fb45a..3786c7561 100644 --- a/Ryujinx.Common/Logging/LogLevel.cs +++ b/Ryujinx.Common/Logging/LogLevel.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.Common.Logging { + [JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))] public enum LogLevel { Debug, diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs index 95f96576c..06976433e 100644 --- a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs +++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -1,5 +1,5 @@ -using System.IO; -using System.Text.Json; +using Ryujinx.Common.Utilities; +using System.IO; namespace Ryujinx.Common.Logging { @@ -25,12 +25,8 @@ namespace Ryujinx.Common.Logging public void Log(object sender, LogEventArgs e) { - string text = JsonSerializer.Serialize(e); - - using (BinaryWriter writer = new BinaryWriter(_stream)) - { - writer.Write(text); - } + var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e); + JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson); } public void Dispose() diff --git a/Ryujinx.Common/Utilities/CommonJsonContext.cs b/Ryujinx.Common/Utilities/CommonJsonContext.cs new file mode 100644 index 000000000..d7b3f78cd --- /dev/null +++ b/Ryujinx.Common/Utilities/CommonJsonContext.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Utilities +{ + [JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")] + [JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")] + public partial class CommonJsonContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Utilities/JsonHelper.cs b/Ryujinx.Common/Utilities/JsonHelper.cs index 36f391149..9a2d6f181 100644 --- a/Ryujinx.Common/Utilities/JsonHelper.cs +++ b/Ryujinx.Common/Utilities/JsonHelper.cs @@ -1,15 +1,62 @@ -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using System.IO; +using System.IO; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; namespace Ryujinx.Common.Utilities { public class JsonHelper { - public static JsonNamingPolicy SnakeCase { get; } + private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy(); + private const int DefaultFileWriteBufferSize = 4096; + + /// <summary> + /// Creates new serializer options with default settings. + /// </summary> + /// <remarks> + /// It is REQUIRED for you to save returned options statically or as a part of static serializer context + /// in order to avoid performance issues. You can safely modify returned options for your case before storing. + /// </remarks> + public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) + { + JsonSerializerOptions options = new() + { + DictionaryKeyPolicy = SnakeCasePolicy, + PropertyNamingPolicy = SnakeCasePolicy, + WriteIndented = indented, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + + return options; + } + + public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo) + { + return JsonSerializer.Serialize(value, typeInfo); + } + + public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo) + { + return JsonSerializer.Deserialize(value, typeInfo); + } + + public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo) + { + using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough); + JsonSerializer.Serialize(file, value, typeInfo); + } + + public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo) + { + using FileStream file = File.OpenRead(filePath); + return JsonSerializer.Deserialize(file, typeInfo); + } + + public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo) + { + JsonSerializer.Serialize(stream, value, typeInfo); + } private class SnakeCaseNamingPolicy : JsonNamingPolicy { @@ -20,7 +67,7 @@ namespace Ryujinx.Common.Utilities return name; } - StringBuilder builder = new StringBuilder(); + StringBuilder builder = new(); for (int i = 0; i < name.Length; i++) { @@ -34,7 +81,7 @@ namespace Ryujinx.Common.Utilities } else { - builder.Append("_"); + builder.Append('_'); builder.Append(char.ToLowerInvariant(c)); } } @@ -47,64 +94,5 @@ namespace Ryujinx.Common.Utilities return builder.ToString(); } } - - static JsonHelper() - { - SnakeCase = new SnakeCaseNamingPolicy(); - } - - public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false) - { - JsonSerializerOptions options = new JsonSerializerOptions - { - DictionaryKeyPolicy = SnakeCase, - PropertyNamingPolicy = SnakeCase, - WriteIndented = prettyPrint, - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip - }; - - options.Converters.Add(new JsonStringEnumConverter()); - options.Converters.Add(new JsonInputConfigConverter()); - options.Converters.Add(new JsonMotionConfigControllerConverter()); - - return options; - } - - public static T Deserialize<T>(Stream stream) - { - using (BinaryReader reader = new BinaryReader(stream)) - { - return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions()); - } - } - - public static T DeserializeFromFile<T>(string path) - { - return Deserialize<T>(File.ReadAllText(path)); - } - - public static T Deserialize<T>(string json) - { - return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions()); - } - - public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false) - { - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.Write(SerializeToUtf8Bytes(obj, prettyPrint)); - } - } - - public static string Serialize<TValue>(TValue obj, bool prettyPrint = false) - { - return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint)); - } - - public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false) - { - return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint)); - } } -} +} \ No newline at end of file diff --git a/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs b/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs new file mode 100644 index 000000000..c0127dc4a --- /dev/null +++ b/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs @@ -0,0 +1,34 @@ +#nullable enable +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Utilities +{ + /// <summary> + /// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs + /// </summary> + /// <remarks> + /// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>. + /// Get rid of this converter if dotnet supports similar functionality out of the box. + /// </remarks> + /// <typeparam name="TEnum">Type of enum to serialize</typeparam> + public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum + { + public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var enumValue = reader.GetString(); + if (string.IsNullOrEmpty(enumValue)) + { + return default; + } + + return Enum.Parse<TEnum>(enumValue); + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs index ec0b0a10b..535779d2e 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -1,11 +1,11 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.Services.Account.Acc.Types; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; -using System.Text.Json.Serialization; namespace Ryujinx.HLE.HOS.Services.Account.Acc { @@ -13,29 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); - private struct ProfilesJson - { - [JsonPropertyName("profiles")] - public List<UserProfileJson> Profiles { get; set; } - [JsonPropertyName("last_opened")] - public string LastOpened { get; set; } - } - - private struct UserProfileJson - { - [JsonPropertyName("user_id")] - public string UserId { get; set; } - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonPropertyName("account_state")] - public AccountState AccountState { get; set; } - [JsonPropertyName("online_play_state")] - public AccountState OnlinePlayState { get; set; } - [JsonPropertyName("last_modified_timestamp")] - public long LastModifiedTimestamp { get; set; } - [JsonPropertyName("image")] - public byte[] Image { get; set; } - } + private static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public UserId LastOpened { get; set; } @@ -47,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { try { - ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath); + ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson); foreach (var profile in profilesJson.Profiles) { @@ -92,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc }); } - File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true)); + JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs new file mode 100644 index 000000000..6b54898e5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs @@ -0,0 +1,11 @@ +using Ryujinx.HLE.HOS.Services.Account.Acc.Types; +using System.Text.Json.Serialization; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ProfilesJson))] + internal partial class ProfilesJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs index 2382a2554..1699abfbd 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs @@ -1,5 +1,9 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + namespace Ryujinx.HLE.HOS.Services.Account.Acc { + [JsonConverter(typeof(TypedStringEnumConverter<AccountState>))] public enum AccountState { Closed, diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs new file mode 100644 index 000000000..09f9d1421 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types +{ + internal struct ProfilesJson + { + public List<UserProfileJson> Profiles { get; set; } + public string LastOpened { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs new file mode 100644 index 000000000..06ff4833f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types +{ + internal struct UserProfileJson + { + public string UserId { get; set; } + public string Name { get; set; } + public AccountState AccountState { get; set; } + public AccountState OnlinePlayState { get; set; } + public long LastModifiedTimestamp { get; set; } + public byte[] Image { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs new file mode 100644 index 000000000..e75f62004 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; +using System.Text.Json.Serialization; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp +{ + [JsonSerializable(typeof(VirtualAmiiboFile))] + internal partial class AmiiboJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 4fdeadcb2..9166e87fa 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Mii.Types; @@ -8,8 +9,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Text.Json; namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { @@ -17,6 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { private static uint _openedApplicationAreaId; + private static readonly AmiiboJsonSerializerContext SerializerContext = AmiiboJsonSerializerContext.Default; + public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid) { if (useRandomUuid) @@ -173,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp if (File.Exists(filePath)) { - virtualAmiiboFile = JsonSerializer.Deserialize<VirtualAmiiboFile>(File.ReadAllText(filePath), new JsonSerializerOptions(JsonSerializerDefaults.General)); + virtualAmiiboFile = JsonHelper.DeserializeFromFile(filePath, SerializerContext.VirtualAmiiboFile); } else { @@ -197,8 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile) { string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"); - - File.WriteAllText(filePath, JsonSerializer.Serialize(virtualAmiiboFile)); + JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, SerializerContext.VirtualAmiiboFile); } } } diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs index 5147f5c3a..e93802ae8 100644 --- a/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs +++ b/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs @@ -17,6 +17,9 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions { public static class PartitionFileSystemExtensions { + private static readonly DownloadableContentJsonSerializerContext ContentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + internal static (bool, ProcessResult) TryLoad(this PartitionFileSystem partitionFileSystem, Switch device, string path, out string errorMessage) { errorMessage = null; @@ -85,7 +88,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); if (File.Exists(titleUpdateMetadataPath)) { - string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; + string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; if (File.Exists(updatePath)) { PartitionFileSystem updatePartitionFileSystem = new(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()); @@ -139,7 +142,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); if (File.Exists(addOnContentMetadataPath)) { - List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(addOnContentMetadataPath); + List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, ContentSerializerContext.ListDownloadableContentContainer); foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) { diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 54ab18cda..8dff6a1d0 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -56,6 +56,8 @@ namespace Ryujinx.Headless.SDL2 private static bool _enableKeyboard; private static bool _enableMouse; + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + static void Main(string[] args) { Version = ReleaseInformation.GetVersion(); @@ -285,10 +287,7 @@ namespace Ryujinx.Headless.SDL2 try { - using (Stream stream = File.OpenRead(path)) - { - config = JsonHelper.Deserialize<InputConfig>(stream); - } + config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); } catch (JsonException) { diff --git a/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs b/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs new file mode 100644 index 000000000..f81121c28 --- /dev/null +++ b/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.App.Common +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ApplicationMetadata))] + internal partial class ApplicationJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 113e9cb3e..8686383e9 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -10,6 +10,7 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Npdm; @@ -22,7 +23,6 @@ using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; using Path = System.IO.Path; namespace Ryujinx.Ui.App.Common @@ -42,6 +42,9 @@ namespace Ryujinx.Ui.App.Common private Language _desiredTitleLanguage; private CancellationTokenSource _cancellationToken; + private static readonly ApplicationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public ApplicationLibrary(VirtualFileSystem virtualFileSystem) { _virtualFileSystem = virtualFileSystem; @@ -489,14 +492,12 @@ namespace Ryujinx.Ui.App.Common appMetadata = new ApplicationMetadata(); - using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); - - JsonHelper.Serialize(stream, appMetadata, true); + JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); } try { - appMetadata = JsonHelper.DeserializeFromFile<ApplicationMetadata>(metadataFile); + appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata); } catch (JsonException) { @@ -509,9 +510,7 @@ namespace Ryujinx.Ui.App.Common { modifyFunction(appMetadata); - using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); - - JsonHelper.Serialize(stream, appMetadata, true); + JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); } return appMetadata; @@ -890,7 +889,7 @@ namespace Ryujinx.Ui.App.Common if (File.Exists(titleUpdateMetadataPath)) { - updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; + updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; if (File.Exists(updatePath)) { diff --git a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs index 99111ea64..1f9bd0baf 100644 --- a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs +++ b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter<AudioBackend>))] public enum AudioBackend { Dummy, diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index e9aec04b2..14c03957a 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -5,7 +5,7 @@ using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.Ui; using System.Collections.Generic; -using System.IO; +using System.Text.Json.Nodes; namespace Ryujinx.Ui.Common.Configuration { @@ -321,14 +321,14 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> /// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks> /// TODO: Remove this when those older versions aren't in use anymore. - public List<object> KeyboardConfig { get; set; } + public List<JsonObject> KeyboardConfig { get; set; } /// <summary> /// Legacy controller control bindings /// </summary> /// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks> /// TODO: Remove this when those older versions aren't in use anymore. - public List<object> ControllerConfig { get; set; } + public List<JsonObject> ControllerConfig { get; set; } /// <summary> /// Input configurations @@ -354,11 +354,12 @@ namespace Ryujinx.Ui.Common.Configuration /// Loads a configuration file from disk /// </summary> /// <param name="path">The path to the JSON configuration file</param> + /// <param name="configurationFileFormat">Parsed configuration file</param> public static bool TryLoad(string path, out ConfigurationFileFormat configurationFileFormat) { try { - configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path); + configurationFileFormat = JsonHelper.DeserializeFromFile(path, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); return configurationFileFormat.Version != 0; } @@ -376,8 +377,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <param name="path">The path to the JSON configuration file</param> public void SaveConfig(string path) { - using FileStream fileStream = File.Create(path, 4096, FileOptions.WriteThrough); - JsonHelper.Serialize(fileStream, this, true); + JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); } } } diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs new file mode 100644 index 000000000..6ce2ef01a --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs @@ -0,0 +1,9 @@ +using Ryujinx.Common.Utilities; + +namespace Ryujinx.Ui.Common.Configuration +{ + internal static class ConfigurationFileFormatSettings + { + public static readonly ConfigurationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs new file mode 100644 index 000000000..bb8dfb499 --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ConfigurationFileFormat))] + internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index bcdd2e70a..82a331c16 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -9,6 +9,7 @@ using Ryujinx.Ui.Common.Configuration.Ui; using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; +using System.Text.Json.Nodes; namespace Ryujinx.Ui.Common.Configuration { @@ -631,8 +632,8 @@ namespace Ryujinx.Ui.Common.Configuration EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, Hotkeys = Hid.Hotkeys, - KeyboardConfig = new List<object>(), - ControllerConfig = new List<object>(), + KeyboardConfig = new List<JsonObject>(), + ControllerConfig = new List<JsonObject>(), InputConfig = Hid.InputConfig, GraphicsBackend = Graphics.GraphicsBackend, PreferredGpu = Graphics.PreferredGpu diff --git a/Ryujinx.Ui.Common/Configuration/System/Language.cs b/Ryujinx.Ui.Common/Configuration/System/Language.cs index 3d2dc9914..404f8063d 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Language.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Language.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration.System +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration.System { + [JsonConverter(typeof(TypedStringEnumConverter<Language>))] public enum Language { Japanese, diff --git a/Ryujinx.Ui.Common/Configuration/System/Region.cs b/Ryujinx.Ui.Common/Configuration/System/Region.cs index fb51e08e5..7dfac6388 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Region.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Region.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration.System +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration.System { + [JsonConverter(typeof(TypedStringEnumConverter<Region>))] public enum Region { Japan, diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs new file mode 100644 index 000000000..f412b9504 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public struct AmiiboApi : IEquatable<AmiiboApi> + { + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("head")] + public string Head { get; set; } + [JsonPropertyName("tail")] + public string Tail { get; set; } + [JsonPropertyName("image")] + public string Image { get; set; } + [JsonPropertyName("amiiboSeries")] + public string AmiiboSeries { get; set; } + [JsonPropertyName("character")] + public string Character { get; set; } + [JsonPropertyName("gameSeries")] + public string GameSeries { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("release")] + public Dictionary<string, string> Release { get; set; } + + [JsonPropertyName("gamesSwitch")] + public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } + + public override string ToString() + { + return Name; + } + + public string GetId() + { + return Head + Tail; + } + + public bool Equals(AmiiboApi other) + { + return Head + Tail == other.Head + other.Tail; + } + + public override bool Equals(object obj) + { + return obj is AmiiboApi other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Head, Tail); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs new file mode 100644 index 000000000..def7d1bcc --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public class AmiiboApiGamesSwitch + { + [JsonPropertyName("amiiboUsage")] + public List<AmiiboApiUsage> AmiiboUsage { get; set; } + [JsonPropertyName("gameID")] + public List<string> GameId { get; set; } + [JsonPropertyName("gameName")] + public string GameName { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs new file mode 100644 index 000000000..814573c22 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public class AmiiboApiUsage + { + [JsonPropertyName("Usage")] + public string Usage { get; set; } + [JsonPropertyName("write")] + public bool Write { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs new file mode 100644 index 000000000..feb7993c1 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public struct AmiiboJson + { + [JsonPropertyName("amiibo")] + public List<AmiiboApi> Amiibo { get; set; } + [JsonPropertyName("lastUpdated")] + public DateTime LastUpdated { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs new file mode 100644 index 000000000..4cbb5a7b4 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + [JsonSerializable(typeof(AmiiboJson))] + public partial class AmiiboJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs new file mode 100644 index 000000000..10d014783 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Ui.Common.Models.Github +{ + public class GithubReleaseAssetJsonResponse + { + public string Name { get; set; } + public string State { get; set; } + public string BrowserDownloadUrl { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs new file mode 100644 index 000000000..954d03e31 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ryujinx.Ui.Common.Models.Github +{ + public class GithubReleasesJsonResponse + { + public string Name { get; set; } + public List<GithubReleaseAssetJsonResponse> Assets { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs new file mode 100644 index 000000000..e5fd9d094 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Github +{ + [JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)] + public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs index 5ad5924e8..3f186ce6b 100644 --- a/Ryujinx/Modules/Updater/Updater.cs +++ b/Ryujinx/Modules/Updater/Updater.cs @@ -2,14 +2,14 @@ using Gtk; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; -using Newtonsoft.Json.Linq; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.Ui; +using Ryujinx.Ui.Common.Models.Github; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -38,6 +38,8 @@ namespace Ryujinx.Modules private static string _buildUrl; private static long _buildSize; + private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; @@ -107,22 +109,16 @@ namespace Ryujinx.Modules // Fetch latest build information string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - JObject jsonRoot = JObject.Parse(fetchedJson); - JToken assets = jsonRoot["assets"]; + var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); + _buildVer = fetched.Name; - _buildVer = (string)jsonRoot["name"]; - - foreach (JToken asset in assets) + foreach (var asset in fetched.Assets) { - string assetName = (string)asset["name"]; - string assetState = (string)asset["state"]; - string downloadURL = (string)asset["browser_download_url"]; - - if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) + if (asset.Name.StartsWith("ryujinx") && asset.Name.EndsWith(_platformExt)) { - _buildUrl = downloadURL; + _buildUrl = asset.BrowserDownloadUrl; - if (assetState != "uploaded") + if (asset.State != "uploaded") { if (showVersionUpToDate) { diff --git a/Ryujinx/Ui/Windows/AboutWindow.cs b/Ryujinx/Ui/Windows/AboutWindow.cs index ea827a92f..41cf9c013 100644 --- a/Ryujinx/Ui/Windows/AboutWindow.cs +++ b/Ryujinx/Ui/Windows/AboutWindow.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Ui.Windows { string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)); + _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)); } catch { diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.cs b/Ryujinx/Ui/Windows/AmiiboWindow.cs index 9140a14e9..470032373 100644 --- a/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -3,6 +3,7 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Models.Amiibo; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; @@ -11,65 +12,15 @@ using System.Linq; using System.Net.Http; using System.Reflection; using System.Text; -using System.Text.Json.Serialization; +using System.Text.Json; using System.Threading.Tasks; +using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi; +using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ui.Windows { public partial class AmiiboWindow : Window { - private struct AmiiboJson - { - [JsonPropertyName("amiibo")] - public List<AmiiboApi> Amiibo { get; set; } - [JsonPropertyName("lastUpdated")] - public DateTime LastUpdated { get; set; } - } - - private struct AmiiboApi - { - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonPropertyName("head")] - public string Head { get; set; } - [JsonPropertyName("tail")] - public string Tail { get; set; } - [JsonPropertyName("image")] - public string Image { get; set; } - [JsonPropertyName("amiiboSeries")] - public string AmiiboSeries { get; set; } - [JsonPropertyName("character")] - public string Character { get; set; } - [JsonPropertyName("gameSeries")] - public string GameSeries { get; set; } - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("release")] - public Dictionary<string, string> Release { get; set; } - - [JsonPropertyName("gamesSwitch")] - public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } - } - - private class AmiiboApiGamesSwitch - { - [JsonPropertyName("amiiboUsage")] - public List<AmiiboApiUsage> AmiiboUsage { get; set; } - [JsonPropertyName("gameID")] - public List<string> GameId { get; set; } - [JsonPropertyName("gameName")] - public string GameName { get; set; } - } - - private class AmiiboApiUsage - { - [JsonPropertyName("Usage")] - public string Usage { get; set; } - [JsonPropertyName("write")] - public bool Write { get; set; } - } - private const string DEFAULT_JSON = "{ \"amiibo\": [] }"; public string AmiiboId { get; private set; } @@ -96,6 +47,8 @@ namespace Ryujinx.Ui.Windows private List<AmiiboApi> _amiiboList; + private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo") { Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); @@ -127,9 +80,9 @@ namespace Ryujinx.Ui.Windows if (File.Exists(_amiiboJsonPath)) { - amiiboJsonString = File.ReadAllText(_amiiboJsonPath); + amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -148,7 +101,7 @@ namespace Ryujinx.Ui.Windows } } - _amiiboList = JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).Amiibo; + _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); if (LastScannedAmiiboShowAll) diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs index 0f0fba0b8..9b4befd85 100644 --- a/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -115,6 +115,8 @@ namespace Ryujinx.Ui.Windows private bool _mousePressed; private bool _middleMousePressed; + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { } private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin")) @@ -1120,10 +1122,7 @@ namespace Ryujinx.Ui.Windows try { - using (Stream stream = File.OpenRead(path)) - { - config = JsonHelper.Deserialize<InputConfig>(stream); - } + config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); } catch (JsonException) { } } @@ -1145,9 +1144,7 @@ namespace Ryujinx.Ui.Windows if (profileDialog.Run() == (int)ResponseType.Ok) { string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName); - string jsonString; - - jsonString = JsonHelper.Serialize(inputConfig, true); + string jsonString = JsonHelper.Serialize(inputConfig, SerializerContext.InputConfig); File.WriteAllText(path, jsonString); } diff --git a/Ryujinx/Ui/Windows/DlcWindow.cs b/Ryujinx/Ui/Windows/DlcWindow.cs index 9fccec195..b22f15932 100644 --- a/Ryujinx/Ui/Windows/DlcWindow.cs +++ b/Ryujinx/Ui/Windows/DlcWindow.cs @@ -7,15 +7,13 @@ using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; using System.IO; -using System.Text; - -using GUI = Gtk.Builder.ObjectAttribute; -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; +using GUI = Gtk.Builder.ObjectAttribute; namespace Ryujinx.Ui.Windows { @@ -26,6 +24,8 @@ namespace Ryujinx.Ui.Windows private readonly string _dlcJsonPath; private readonly List<DownloadableContentContainer> _dlcContainerList; + private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + #pragma warning disable CS0649, IDE0044 [GUI] Label _baseTitleInfoLabel; [GUI] TreeView _dlcTreeView; @@ -45,7 +45,7 @@ namespace Ryujinx.Ui.Windows try { - _dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_dlcJsonPath); + _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, SerializerContext.ListDownloadableContentContainer); } catch { @@ -260,10 +260,7 @@ namespace Ryujinx.Ui.Windows while (_dlcTreeView.Model.IterNext(ref parentIter)); } - using (FileStream dlcJsonStream = File.Create(_dlcJsonPath, 4096, FileOptions.WriteThrough)) - { - dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_dlcContainerList, true))); - } + JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, SerializerContext.ListDownloadableContentContainer); Dispose(); } diff --git a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs index fce751da1..c40adc116 100644 --- a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs +++ b/Ryujinx/Ui/Windows/TitleUpdateWindow.cs @@ -7,6 +7,7 @@ using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.Ui.App.Common; @@ -15,10 +16,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; - -using GUI = Gtk.Builder.ObjectAttribute; -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; +using GUI = Gtk.Builder.ObjectAttribute; +using SpanHelpers = LibHac.Common.SpanHelpers; namespace Ryujinx.Ui.Windows { @@ -32,6 +31,7 @@ namespace Ryujinx.Ui.Windows private TitleUpdateMetadata _titleUpdateWindowData; private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary; + private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); #pragma warning disable CS0649, IDE0044 [GUI] Label _baseTitleInfoLabel; @@ -54,7 +54,7 @@ namespace Ryujinx.Ui.Windows try { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_updateJsonPath); + _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, SerializerContext.TitleUpdateMetadata); } catch { @@ -193,10 +193,7 @@ namespace Ryujinx.Ui.Windows } } - using (FileStream dlcJsonStream = File.Create(_updateJsonPath, 4096, FileOptions.WriteThrough)) - { - dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); - } + JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); _parent.UpdateGameTable(); From c9bc4eaf58ca0d693a37b1229bb6fc210ce71474 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:37:27 +0200 Subject: [PATCH 430/737] Fix missing string enum converters for the config (#4634) * Fix missing string enum converters for the config * Revert changing KeyboardHotkeys to struct This needs to be done because Avalonia's TwoWay Binding breaks otherwise. --- .../Configuration/Hid/Controller/GamepadInputId.cs | 8 ++++++-- .../Configuration/Hid/Controller/StickInputId.cs | 8 ++++++-- Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs | 4 +++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs b/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs index cae65fc9b..ad1fa6673 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration.Hid.Controller +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller { + [JsonConverter(typeof(TypedStringEnumConverter<GamepadInputId>))] public enum GamepadInputId : byte { Unbound, @@ -51,4 +55,4 @@ Count } -} +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs b/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs index 87945a75a..5fc4d1c87 100644 --- a/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs +++ b/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Common.Configuration.Hid.Controller +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller { + [JsonConverter(typeof(TypedStringEnumConverter<StickInputId>))] public enum StickInputId : byte { Unbound, @@ -8,4 +12,4 @@ Count } -} +} \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 8b6c8c145..1a10c2a53 100644 --- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -1,6 +1,8 @@ namespace Ryujinx.Common.Configuration.Hid { - public struct KeyboardHotkeys + // NOTE: Please don't change this to struct. + // This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys. + public class KeyboardHotkeys { public Key ToggleVsync { get; set; } public Key Screenshot { get; set; } From 52d6f2e656c21c3e6693df93a3f09cd2e6a4e40e Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 5 Apr 2023 03:34:21 +0200 Subject: [PATCH 431/737] hle: Set ProcessResult name from NACP (#4633) * Extract titleName from nacp * Address formatting feedback * Check if the desired language is actually available --- .../Loaders/Processes/ProcessLoaderHelper.cs | 11 ++++++++++- Ryujinx.HLE/Loaders/Processes/ProcessResult.cs | 18 +++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index 7176445e5..b802a6428 100644 --- a/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -405,7 +405,16 @@ namespace Ryujinx.HLE.Loaders.Processes // Once everything is loaded, we can load cheats. device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(programId, tamperInfo, device.TamperMachine); - return new ProcessResult(metaLoader, applicationControlProperties, diskCacheEnabled, allowCodeMemoryForJit, processContextFactory.DiskCacheLoadState, process.Pid, meta.MainThreadPriority, meta.MainThreadStackSize); + return new ProcessResult( + metaLoader, + applicationControlProperties, + diskCacheEnabled, + allowCodeMemoryForJit, + processContextFactory.DiskCacheLoadState, + process.Pid, + meta.MainThreadPriority, + meta.MainThreadStackSize, + device.System.State.DesiredTitleLanguage); } public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index 6bbeee1b8..b9596c8fd 100644 --- a/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -2,6 +2,7 @@ using LibHac.Ns; using Ryujinx.Common.Logging; using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.Horizon.Common; @@ -9,7 +10,7 @@ namespace Ryujinx.HLE.Loaders.Processes { public struct ProcessResult { - public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0); + public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish); private readonly byte _mainThreadPriority; private readonly uint _mainThreadStackSize; @@ -35,7 +36,8 @@ namespace Ryujinx.HLE.Loaders.Processes IDiskCacheLoadState diskCacheLoadState, ulong pid, byte mainThreadPriority, - uint mainThreadStackSize) + uint mainThreadStackSize, + TitleLanguage titleLanguage) { _mainThreadPriority = mainThreadPriority; _mainThreadStackSize = mainThreadStackSize; @@ -50,7 +52,17 @@ namespace Ryujinx.HLE.Loaders.Processes { ulong programId = metaLoader.GetProgramId(); - Name = metaLoader.GetProgramName(); + if (ApplicationControlProperties.Title.ItemsRo.Length > 0) + { + var langIndex = ApplicationControlProperties.Title.ItemsRo.Length > (int)titleLanguage ? (int)titleLanguage : 0; + + Name = ApplicationControlProperties.Title[langIndex].NameString.ToString(); + } + else + { + Name = metaLoader.GetProgramName(); + } + ProgramId = programId; ProgramIdText = $"{programId:x16}"; Is64Bit = metaLoader.IsProgram64Bit(); From c532118d94dea0bbafff7b92000c1a25cd4e021d Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 5 Apr 2023 00:25:19 -0300 Subject: [PATCH 432/737] Use index fragment shader output when dual source blend is enabled (#4404) * Use index fragment shader output when dual source blend is enabled * Shader cache version bump * Actually set DualSourceBlendEnabled to true * Fix XML doc --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- Ryujinx.Graphics.GAL/BlendFactor.cs | 21 +++++++++++++++ .../Threed/SpecializationStateUpdater.cs | 14 ++++++++++ .../Engine/Threed/StateUpdater.cs | 22 ++++++++++++++++ .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 6 +++++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 6 +++++ .../Shader/GpuChannelGraphicsState.cs | 10 ++++++- .../Shader/ShaderSpecializationState.cs | 5 ++++ Ryujinx.Graphics.OpenGL/Pipeline.cs | 26 +++---------------- .../CodeGen/Glsl/Declarations.cs | 22 ++++++++++++++++ .../CodeGen/Spirv/Declarations.cs | 23 +++++++++++++++- Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 +++++++ 12 files changed, 141 insertions(+), 25 deletions(-) diff --git a/Ryujinx.Graphics.GAL/BlendFactor.cs b/Ryujinx.Graphics.GAL/BlendFactor.cs index 135873e99..4149ad514 100644 --- a/Ryujinx.Graphics.GAL/BlendFactor.cs +++ b/Ryujinx.Graphics.GAL/BlendFactor.cs @@ -38,4 +38,25 @@ namespace Ryujinx.Graphics.GAL Src1AlphaGl = 0xc902, OneMinusSrc1AlphaGl = 0xc903 } + + public static class BlendFactorExtensions + { + public static bool IsDualSource(this BlendFactor factor) + { + switch (factor) + { + case BlendFactor.Src1Color: + case BlendFactor.Src1ColorGl: + case BlendFactor.Src1Alpha: + case BlendFactor.Src1AlphaGl: + case BlendFactor.OneMinusSrc1Color: + case BlendFactor.OneMinusSrc1ColorGl: + case BlendFactor.OneMinusSrc1Alpha: + case BlendFactor.OneMinusSrc1AlphaGl: + return true; + } + + return false; + } + } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs index 62df15e79..a8af54970 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -328,5 +328,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Signal(); } } + + /// <summary> + /// Sets the dual-source blend enabled state. + /// </summary> + /// <param name="enabled">True if blending is enabled and using dual-source blend</param> + public void SetDualSourceBlendEnabled(bool enabled) + { + if (enabled != _graphics.DualSourceBlendEnable) + { + _graphics.DualSourceBlendEnable = enabled; + + Signal(); + } + } } } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index d7d197adb..00e09a310 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1183,6 +1183,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool blendIndependent = _state.State.BlendIndependent; ColorF blendConstant = _state.State.BlendConstant; + bool dualSourceBlendEnabled = false; + if (blendIndependent) { for (int index = 0; index < Constants.TotalRenderTargets; index++) @@ -1200,6 +1202,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed FilterBlendFactor(blend.AlphaSrcFactor, index), FilterBlendFactor(blend.AlphaDstFactor, index)); + if (enable && + (blend.ColorSrcFactor.IsDualSource() || + blend.ColorDstFactor.IsDualSource() || + blend.AlphaSrcFactor.IsDualSource() || + blend.AlphaDstFactor.IsDualSource())) + { + dualSourceBlendEnabled = true; + } + _pipeline.BlendDescriptors[index] = descriptor; _context.Renderer.Pipeline.SetBlendState(index, descriptor); } @@ -1219,12 +1230,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed FilterBlendFactor(blend.AlphaSrcFactor, 0), FilterBlendFactor(blend.AlphaDstFactor, 0)); + if (enable && + (blend.ColorSrcFactor.IsDualSource() || + blend.ColorDstFactor.IsDualSource() || + blend.AlphaSrcFactor.IsDualSource() || + blend.AlphaDstFactor.IsDualSource())) + { + dualSourceBlendEnabled = true; + } + for (int index = 0; index < Constants.TotalRenderTargets; index++) { _pipeline.BlendDescriptors[index] = descriptor; _context.Renderer.Pipeline.SetBlendState(index, descriptor); } } + + _currentSpecState.SetDualSourceBlendEnabled(dualSourceBlendEnabled); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 97173c964..17639ca17 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -141,6 +141,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; } + /// <inheritdoc/> + public bool QueryDualSourceBlendEnable() + { + return _oldSpecState.GraphicsState.DualSourceBlendEnable; + } + /// <inheritdoc/> public InputTopology QueryPrimitiveTopology() { diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index edc5a8a08..0b87cc910 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4368; + private const uint CodeGenVersion = 4404; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 05631a210..3e8167331 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -157,6 +157,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; } + /// <inheritdoc/> + public bool QueryDualSourceBlendEnable() + { + return _state.GraphicsState.DualSourceBlendEnable; + } + /// <inheritdoc/> public InputTopology QueryPrimitiveTopology() { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 70ac50170..5247a096f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -92,6 +92,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> public Array8<AttributeType> FragmentOutputTypes; + /// <summary> + /// Indicates whether dual source blend is enabled. + /// </summary> + public bool DualSourceBlendEnable; + /// <summary> /// Creates a new GPU graphics state. /// </summary> @@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> /// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param> /// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param> + /// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param> public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -127,7 +133,8 @@ namespace Ryujinx.Graphics.Gpu.Shader ref Array32<AttributeType> attributeTypes, bool hasConstantBufferDrawParameters, bool hasUnalignedStorageBuffer, - ref Array8<AttributeType> fragmentOutputTypes) + ref Array8<AttributeType> fragmentOutputTypes, + bool dualSourceBlendEnable) { EarlyZForce = earlyZForce; Topology = topology; @@ -145,6 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Shader HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; FragmentOutputTypes = fragmentOutputTypes; + DualSourceBlendEnable = dualSourceBlendEnable; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 856507cd7..b2c4fccdb 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -535,6 +535,11 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable) + { + return false; + } + return Matches(channel, ref poolState, checkTextures, isCompute: false); } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 62996bd0a..6b6d0289c 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -833,31 +833,13 @@ namespace Ryujinx.Graphics.OpenGL (BlendingFactorSrc)blend.AlphaSrcFactor.Convert(), (BlendingFactorDest)blend.AlphaDstFactor.Convert()); - static bool IsDualSource(BlendFactor factor) - { - switch (factor) - { - case BlendFactor.Src1Color: - case BlendFactor.Src1ColorGl: - case BlendFactor.Src1Alpha: - case BlendFactor.Src1AlphaGl: - case BlendFactor.OneMinusSrc1Color: - case BlendFactor.OneMinusSrc1ColorGl: - case BlendFactor.OneMinusSrc1Alpha: - case BlendFactor.OneMinusSrc1AlphaGl: - return true; - } - - return false; - } - EnsureFramebuffer(); _framebuffer.SetDualSourceBlend( - IsDualSource(blend.ColorSrcFactor) || - IsDualSource(blend.ColorDstFactor) || - IsDualSource(blend.AlphaSrcFactor) || - IsDualSource(blend.AlphaDstFactor)); + blend.ColorSrcFactor.IsDualSource() || + blend.ColorDstFactor.IsDualSource() || + blend.AlphaSrcFactor.IsDualSource() || + blend.AlphaDstFactor.IsDualSource()); if (_blendConstant != blend.BlendConstant) { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 996d312b7..9032ca59e 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -612,6 +612,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl else { int usedAttributes = context.Config.UsedOutputAttributes; + + if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) + { + int firstOutput = BitOperations.TrailingZeroCount(usedAttributes); + int mask = 3 << firstOutput; + + if ((usedAttributes & mask) == mask) + { + usedAttributes &= ~mask; + DeclareOutputDualSourceBlendAttribute(context, firstOutput); + } + } + while (usedAttributes != 0) { int index = BitOperations.TrailingZeroCount(usedAttributes); @@ -690,6 +703,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } + private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr) + { + string name = $"{DefaultNames.OAttributePrefix}{attr}"; + string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}"; + + context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};"); + context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};"); + } + private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs) { foreach (int attr in attrs.Order()) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 5108d8619..df42a13cb 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Numerics; using static Spv.Specification; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv @@ -622,7 +623,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) { int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16; - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + + if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) + { + int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes); + int index = location - firstLocation; + int mask = 3 << firstLocation; + + if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask) + { + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation); + context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index); + } + else + { + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } + } + else + { + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } } if (!isOutAttr) diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index f364437c7..ba5f2a92f 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -205,6 +205,15 @@ namespace Ryujinx.Graphics.Shader return false; } + /// <summary> + /// Queries dual source blend state. + /// </summary> + /// <returns>True if blending is enabled with a dual source blend equation, false otherwise</returns> + bool QueryDualSourceBlendEnable() + { + return false; + } + /// <summary> /// Queries host about the presence of the FrontFacing built-in variable bug. /// </summary> From 63dedbda862533342fd8fac148f0c1d6427c9d84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 07:55:57 +0200 Subject: [PATCH 433/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.27.0 to 6.28.1 (#4639) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.27.0 to 6.28.1. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.27.0...6.28.1) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 8e9aef4ee..a18bcbe7c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> - <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" /> + <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> From c95be55091eaee446e1ad51ad215b82be5866bb3 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 5 Apr 2023 19:48:38 +0200 Subject: [PATCH 434/737] vulkan: Cleanup PhysicalDevice and Instance querying (#4632) * vulkan: Move most of the properties enumeration to VulkanPhysicalDevice That clean up a bit of duplicate logic. Also move to use an hashset for device extensions. * vulkan: Move instance querying to VulkanInstance Also cleanup code to use span when possible instead of unsafe pointers. * Address gdkchan's comments --- Ryujinx.Graphics.Vulkan/MemoryAllocator.cs | 23 +- .../VulkanInitialization.cs | 220 +++++------------- Ryujinx.Graphics.Vulkan/VulkanInstance.cs | 127 ++++++++++ .../VulkanPhysicalDevice.cs | 70 ++++++ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 92 ++++---- 5 files changed, 313 insertions(+), 219 deletions(-) create mode 100644 Ryujinx.Graphics.Vulkan/VulkanInstance.cs create mode 100644 Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 6a786a96c..3139e2091 100644 --- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -9,21 +9,18 @@ namespace Ryujinx.Graphics.Vulkan private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private readonly Vk _api; - private readonly PhysicalDevice _physicalDevice; + private readonly VulkanPhysicalDevice _physicalDevice; private readonly Device _device; private readonly List<MemoryAllocatorBlockList> _blockLists; private readonly int _blockAlignment; - private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties; - public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount) + public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device) { _api = api; _physicalDevice = physicalDevice; _device = device; _blockLists = new List<MemoryAllocatorBlockList>(); - _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount); - - _api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties); + _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)_physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount); } public MemoryAllocation AllocateDeviceMemory( @@ -64,9 +61,9 @@ namespace Ryujinx.Graphics.Vulkan uint memoryTypeBits, MemoryPropertyFlags flags) { - for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++) + for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++) { - var type = _physicalDeviceMemoryProperties.MemoryTypes[i]; + var type = _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypes[i]; if ((memoryTypeBits & (1 << i)) != 0) { @@ -80,15 +77,11 @@ namespace Ryujinx.Graphics.Vulkan return -1; } - public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice) + public static bool IsDeviceMemoryShared(VulkanPhysicalDevice physicalDevice) { - // The device is regarded as having shared memory if all heaps have the device local bit. - - api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); - - for (int i = 0; i < properties.MemoryHeapCount; i++) + for (int i = 0; i < physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeapCount; i++) { - if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit)) + if (!physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit)) { return false; } diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index f04ab5c05..4f69cb1d2 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -47,35 +47,23 @@ namespace Ryujinx.Graphics.Vulkan KhrSwapchain.ExtensionName }; - internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions) + internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions) { var enabledLayers = new List<string>(); + var instanceExtensions = VulkanInstance.GetInstanceExtensions(api); + var instanceLayers = VulkanInstance.GetInstanceLayers(api); + void AddAvailableLayer(string layerName) { - uint layerPropertiesCount; - - api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError(); - - LayerProperties[] layerProperties = new LayerProperties[layerPropertiesCount]; - - fixed (LayerProperties* pLayerProperties = layerProperties) + if (instanceLayers.Contains(layerName)) { - api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError(); - - for (int i = 0; i < layerPropertiesCount; i++) - { - string currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName); - - if (currentLayerName == layerName) - { - enabledLayers.Add(layerName); - return; - } - } + enabledLayers.Add(layerName); + } + else + { + Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}"); } - - Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}"); } if (logLevel != GraphicsDebugLevel.None) @@ -85,7 +73,7 @@ namespace Ryujinx.Graphics.Vulkan var enabledExtensions = requiredExtensions; - if (api.IsInstanceExtensionPresent("VK_EXT_debug_utils")) + if (instanceExtensions.Contains("VK_EXT_debug_utils")) { enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); } @@ -124,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan EnabledLayerCount = (uint)enabledLayers.Count }; - api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); + Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var instance); Marshal.FreeHGlobal(appName); @@ -138,21 +126,14 @@ namespace Ryujinx.Graphics.Vulkan Marshal.FreeHGlobal(ppEnabledLayers[i]); } + result.ThrowOnError(); + return instance; } - internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId) + internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(Vk api, VulkanInstance instance, SurfaceKHR surface, string preferredGpuId) { - uint physicalDeviceCount; - - api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError(); - - PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount]; - - fixed (PhysicalDevice* pPhysicalDevices = physicalDevices) - { - api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError(); - } + instance.EnumeratePhysicalDevices(out var physicalDevices).ThrowOnError(); // First we try to pick the the user preferred GPU. for (int i = 0; i < physicalDevices.Length; i++) @@ -198,76 +179,41 @@ namespace Ryujinx.Graphics.Vulkan EnabledLayerCount = 0 }; - api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); - - // We ensure that vkEnumerateInstanceVersion is present (added in 1.1). - // If the instance doesn't support it, no device is going to be 1.1 compatible. - if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero) - { - api.DestroyInstance(instance, null); - - return Array.Empty<DeviceInfo>(); - } - - // We currently assume that the instance is compatible with Vulkan 1.2 - // TODO: Remove this once we relax our initialization codepaths. - uint instanceApiVerison = 0; - api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError(); - - if (instanceApiVerison < MinimalInstanceVulkanVersion) - { - api.DestroyInstance(instance, null); - - return Array.Empty<DeviceInfo>(); - } + Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var rawInstance); Marshal.FreeHGlobal(appName); - uint physicalDeviceCount; + result.ThrowOnError(); - api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError(); + using VulkanInstance instance = rawInstance; - PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount]; - - fixed (PhysicalDevice* pPhysicalDevices = physicalDevices) + // We currently assume that the instance is compatible with Vulkan 1.2 + // TODO: Remove this once we relax our initialization codepaths. + if (instance.InstanceVersion < MinimalInstanceVulkanVersion) { - api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError(); + return Array.Empty<DeviceInfo>(); } - DeviceInfo[] devices = new DeviceInfo[physicalDevices.Length]; + instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError(); - for (int i = 0; i < physicalDevices.Length; i++) + List<DeviceInfo> deviceInfos = new List<DeviceInfo>(); + + foreach (VulkanPhysicalDevice physicalDevice in physicalDevices) { - var physicalDevice = physicalDevices[i]; - api.GetPhysicalDeviceProperties(physicalDevice, out var properties); - - if (properties.ApiVersion < MinimalVulkanVersion) + if (physicalDevice.PhysicalDeviceProperties.ApiVersion < MinimalVulkanVersion) { continue; } - devices[i] = new DeviceInfo( - StringFromIdPair(properties.VendorID, properties.DeviceID), - VendorUtils.GetNameFromId(properties.VendorID), - Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName), - properties.DeviceType == PhysicalDeviceType.DiscreteGpu); + deviceInfos.Add(physicalDevice.ToDeviceInfo()); } - api.DestroyInstance(instance, null); - - return devices; + return deviceInfos.ToArray(); } - public static string StringFromIdPair(uint vendorId, uint deviceId) + private static bool IsPreferredAndSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId) { - return $"0x{vendorId:X}_0x{deviceId:X}"; - } - - private static bool IsPreferredAndSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId) - { - api.GetPhysicalDeviceProperties(physicalDevice, out var properties); - - if (StringFromIdPair(properties.VendorID, properties.DeviceID) != preferredGpuId) + if (physicalDevice.Id != preferredGpuId) { return false; } @@ -275,68 +221,47 @@ namespace Ryujinx.Graphics.Vulkan return IsSuitableDevice(api, physicalDevice, surface); } - private static bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface) + private static bool IsSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface) { int extensionMatches = 0; - uint propertiesCount; - api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError(); - - ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount]; - - fixed (ExtensionProperties* pExtensionProperties = extensionProperties) + foreach (string requiredExtension in _requiredExtensions) { - api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError(); - - for (int i = 0; i < propertiesCount; i++) + if (physicalDevice.IsDeviceExtensionPresent(requiredExtension)) { - string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName); - - if (_requiredExtensions.Contains(extensionName)) - { - extensionMatches++; - } + extensionMatches++; } } return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex; } - internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount) + internal static uint FindSuitableQueueFamily(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount) { const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit; var khrSurface = new KhrSurface(api.Context); - uint propertiesCount; - - api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null); - - QueueFamilyProperties[] properties = new QueueFamilyProperties[propertiesCount]; - - fixed (QueueFamilyProperties* pProperties = properties) + for (uint index = 0; index < physicalDevice.QueueFamilyProperties.Length; index++) { - api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties); - } + ref QueueFamilyProperties property = ref physicalDevice.QueueFamilyProperties[index]; - for (uint index = 0; index < propertiesCount; index++) - { - var queueFlags = properties[index].QueueFlags; + khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice.PhysicalDevice, index, surface, out var surfaceSupported).ThrowOnError(); - khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported).ThrowOnError(); - - if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported) + if (property.QueueFlags.HasFlag(RequiredFlags) && surfaceSupported) { - queueCount = properties[index].QueueCount; + queueCount = property.QueueCount; + return index; } } queueCount = 0; + return InvalidIndex; } - public static Device CreateDevice(Vk api, PhysicalDevice physicalDevice, uint queueFamilyIndex, string[] supportedExtensions, uint queueCount) + internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount) { if (queueCount > QueuesCount) { @@ -358,8 +283,7 @@ namespace Ryujinx.Graphics.Vulkan PQueuePriorities = queuePriorities }; - api.GetPhysicalDeviceProperties(physicalDevice, out var properties); - bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia; + bool useRobustBufferAccess = VendorUtils.FromId(physicalDevice.PhysicalDeviceProperties.VendorID) == Vendor.Nvidia; PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() { @@ -380,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan PNext = features2.PNext }; - if (supportedExtensions.Contains("VK_EXT_custom_border_color")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color")) { features2.PNext = &supportedFeaturesCustomBorderColor; } @@ -391,7 +315,7 @@ namespace Ryujinx.Graphics.Vulkan PNext = features2.PNext }; - if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) { features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart; } @@ -402,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan PNext = features2.PNext }; - if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName)) { features2.PNext = &supportedFeaturesTransformFeedback; } @@ -412,14 +336,14 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceRobustness2FeaturesExt }; - if (supportedExtensions.Contains("VK_EXT_robustness2")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) { supportedFeaturesRobustness2.PNext = features2.PNext; features2.PNext = &supportedFeaturesRobustness2; } - api.GetPhysicalDeviceFeatures2(physicalDevice, &features2); + api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2); var supportedFeatures = features2.Features; @@ -452,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; - if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) + if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName)) { featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() { @@ -466,7 +390,7 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart; - if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) { featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() { @@ -481,7 +405,7 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; - if (supportedExtensions.Contains("VK_EXT_robustness2")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) { featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() { @@ -497,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt, PNext = pExtendedFeatures, - ExtendedDynamicState = supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName) + ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName) }; pExtendedFeatures = &featuresExtendedDynamicState; @@ -515,16 +439,16 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceVulkan12Features, PNext = pExtendedFeatures, - DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"), - DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), - UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout") + DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"), + DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName), + UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout") }; pExtendedFeatures = &featuresVk12; PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8; - if (supportedExtensions.Contains("VK_EXT_index_type_uint8")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8")) { featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT() { @@ -538,7 +462,7 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock; - if (supportedExtensions.Contains("VK_EXT_fragment_shader_interlock")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock")) { featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT() { @@ -552,7 +476,7 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl; - if (supportedExtensions.Contains("VK_EXT_subgroup_size_control")) + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control")) { featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT() { @@ -566,7 +490,7 @@ namespace Ryujinx.Graphics.Vulkan PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; - if (supportedExtensions.Contains("VK_EXT_custom_border_color") && + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") && supportedFeaturesCustomBorderColor.CustomBorderColors && supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) { @@ -581,7 +505,7 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresCustomBorderColor; } - var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(supportedExtensions)).ToArray(); + var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; @@ -601,7 +525,7 @@ namespace Ryujinx.Graphics.Vulkan PEnabledFeatures = &features }; - api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError(); + api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError(); for (int i = 0; i < enabledExtensions.Length; i++) { @@ -610,21 +534,5 @@ namespace Ryujinx.Graphics.Vulkan return device; } - - public static string[] GetSupportedExtensions(Vk api, PhysicalDevice physicalDevice) - { - uint propertiesCount; - - api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError(); - - ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount]; - - fixed (ExtensionProperties* pExtensionProperties = extensionProperties) - { - api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError(); - } - - return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray(); - } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanInstance.cs b/Ryujinx.Graphics.Vulkan/VulkanInstance.cs new file mode 100644 index 000000000..843d34125 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/VulkanInstance.cs @@ -0,0 +1,127 @@ +using Ryujinx.Common.Utilities; +using Silk.NET.Core; +using Silk.NET.Vulkan; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan +{ + class VulkanInstance : IDisposable + { + private readonly Vk _api; + public readonly Instance Instance; + public readonly Version32 InstanceVersion; + + private bool _disposed; + + private VulkanInstance(Vk api, Instance instance) + { + _api = api; + Instance = instance; + + if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero) + { + InstanceVersion = Vk.Version10; + } + else + { + uint rawInstanceVersion = 0; + + if (api.EnumerateInstanceVersion(ref rawInstanceVersion) != Result.Success) + { + rawInstanceVersion = Vk.Version11.Value; + } + + InstanceVersion = (Version32)rawInstanceVersion; + } + } + + public static Result Create(Vk api, ref InstanceCreateInfo createInfo, out VulkanInstance instance) + { + instance = null; + + Instance rawInstance = default; + + Result result = api.CreateInstance(SpanHelpers.AsReadOnlySpan(ref createInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref rawInstance)); + + if (result == Result.Success) + { + instance = new VulkanInstance(api, rawInstance); + } + + return result; + } + + public Result EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices) + { + physicalDevices = null; + + uint physicalDeviceCount = 0; + + Result result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), Span<PhysicalDevice>.Empty); + + if (result != Result.Success) + { + return result; + } + + PhysicalDevice[] rawPhysicalDevices = new PhysicalDevice[physicalDeviceCount]; + + result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), rawPhysicalDevices); + + if (result != Result.Success) + { + return result; + } + + physicalDevices = rawPhysicalDevices.Select(x => new VulkanPhysicalDevice(_api, x)).ToArray(); + + return Result.Success; + } + + public static IReadOnlySet<string> GetInstanceExtensions(Vk api) + { + uint propertiesCount = 0; + + api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError(); + + ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount]; + + api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError(); + + unsafe + { + return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet(); + } + } + + public static IReadOnlySet<string> GetInstanceLayers(Vk api) + { + uint propertiesCount = 0; + + api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), Span<LayerProperties>.Empty).ThrowOnError(); + + LayerProperties[] layerProperties = new LayerProperties[propertiesCount]; + + api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), layerProperties).ThrowOnError(); + + unsafe + { + return layerProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.LayerName)).ToImmutableHashSet(); + } + } + + public void Dispose() + { + if (!_disposed) + { + _api.DestroyInstance(Instance, ReadOnlySpan<AllocationCallbacks>.Empty); + + _disposed = true; + } + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs new file mode 100644 index 000000000..547f36543 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs @@ -0,0 +1,70 @@ +using Ryujinx.Common.Utilities; +using Ryujinx.Graphics.GAL; +using Silk.NET.Vulkan; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Vulkan +{ + readonly struct VulkanPhysicalDevice + { + public readonly PhysicalDevice PhysicalDevice; + public readonly PhysicalDeviceFeatures PhysicalDeviceFeatures; + public readonly PhysicalDeviceProperties PhysicalDeviceProperties; + public readonly PhysicalDeviceMemoryProperties PhysicalDeviceMemoryProperties; + public readonly QueueFamilyProperties[] QueueFamilyProperties; + public readonly string DeviceName; + public readonly IReadOnlySet<string> DeviceExtensions; + + public VulkanPhysicalDevice(Vk api, PhysicalDevice physicalDevice) + { + PhysicalDevice = physicalDevice; + PhysicalDeviceFeatures = api.GetPhysicalDeviceFeature(PhysicalDevice); + + api.GetPhysicalDeviceProperties(PhysicalDevice, out var physicalDeviceProperties); + PhysicalDeviceProperties = physicalDeviceProperties; + + api.GetPhysicalDeviceMemoryProperties(PhysicalDevice, out PhysicalDeviceMemoryProperties); + + unsafe + { + DeviceName = Marshal.PtrToStringAnsi((IntPtr)physicalDeviceProperties.DeviceName); + } + + uint propertiesCount = 0; + + api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), Span<QueueFamilyProperties>.Empty); + + QueueFamilyProperties = new QueueFamilyProperties[propertiesCount]; + + api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), QueueFamilyProperties); + + api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError(); + + ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount]; + + api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError(); + + unsafe + { + DeviceExtensions = extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet(); + } + } + + public string Id => $"0x{PhysicalDeviceProperties.VendorID:X}_0x{PhysicalDeviceProperties.DeviceID:X}"; + + public bool IsDeviceExtensionPresent(string extension) => DeviceExtensions.Contains(extension); + + public DeviceInfo ToDeviceInfo() + { + return new DeviceInfo( + Id, + VendorUtils.GetNameFromId(PhysicalDeviceProperties.VendorID), + DeviceName, + PhysicalDeviceProperties.DeviceType == PhysicalDeviceType.DiscreteGpu); + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 81dec12ec..193cdce31 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Vulkan { public sealed class VulkanRenderer : IRenderer { - private Instance _instance; + private VulkanInstance _instance; private SurfaceKHR _surface; - private PhysicalDevice _physicalDevice; + private VulkanPhysicalDevice _physicalDevice; private Device _device; private WindowBase _window; @@ -106,33 +106,31 @@ namespace Ryujinx.Graphics.Vulkan } } - private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex) + private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex) { - FormatCapabilities = new FormatCapabilities(Api, _physicalDevice); + FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice); - var supportedFeatures = Api.GetPhysicalDeviceFeature(_physicalDevice); - - if (Api.TryGetDeviceExtension(_instance, _device, out ExtConditionalRendering conditionalRenderingApi)) + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtConditionalRendering conditionalRenderingApi)) { ConditionalRenderingApi = conditionalRenderingApi; } - if (Api.TryGetDeviceExtension(_instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi)) + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi)) { ExtendedDynamicStateApi = extendedDynamicStateApi; } - if (Api.TryGetDeviceExtension(_instance, _device, out KhrPushDescriptor pushDescriptorApi)) + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi)) { PushDescriptorApi = pushDescriptorApi; } - if (Api.TryGetDeviceExtension(_instance, _device, out ExtTransformFeedback transformFeedbackApi)) + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtTransformFeedback transformFeedbackApi)) { TransformFeedbackApi = transformFeedbackApi; } - if (Api.TryGetDeviceExtension(_instance, _device, out KhrDrawIndirectCount drawIndirectCountApi)) + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrDrawIndirectCount drawIndirectCountApi)) { DrawIndirectCountApi = drawIndirectCountApi; } @@ -154,7 +152,7 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt }; - bool supportsBlendOperationAdvanced = supportedExtensions.Contains("VK_EXT_blend_operation_advanced"); + bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced"); if (supportsBlendOperationAdvanced) { @@ -167,14 +165,14 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt }; - bool supportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control"); + bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"); if (supportsSubgroupSizeControl) { properties2.PNext = &propertiesSubgroupSizeControl; } - bool supportsTransformFeedback = supportedExtensions.Contains(ExtTransformFeedback.ExtensionName); + bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName); PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT() { @@ -222,30 +220,30 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr }; - if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) + if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) { features2.PNext = &featuresPrimitiveTopologyListRestart; } - if (supportedExtensions.Contains("VK_EXT_robustness2")) + if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) { featuresRobustness2.PNext = features2.PNext; features2.PNext = &featuresRobustness2; } - if (supportedExtensions.Contains("VK_KHR_shader_float16_int8")) + if (_physicalDevice.IsDeviceExtensionPresent("VK_KHR_shader_float16_int8")) { featuresShaderInt8.PNext = features2.PNext; features2.PNext = &featuresShaderInt8; } - if (supportedExtensions.Contains("VK_EXT_custom_border_color")) + if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color")) { featuresCustomBorderColor.PNext = features2.PNext; features2.PNext = &featuresCustomBorderColor; } - bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset"); + bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); if (usePortability) { @@ -256,8 +254,8 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &featuresPortabilitySubset; } - Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2); - Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); + Api.GetPhysicalDeviceProperties2(_physicalDevice.PhysicalDevice, &properties2); + Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2); var portabilityFlags = PortabilitySubsetFlags.None; uint vertexBufferAlignment = 1; @@ -272,7 +270,7 @@ namespace Ryujinx.Graphics.Vulkan portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias; } - bool supportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color") && + bool supportsCustomBorderColor = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") && featuresCustomBorderColor.CustomBorderColors && featuresCustomBorderColor.CustomBorderColorWithoutFormat; @@ -284,30 +282,30 @@ namespace Ryujinx.Graphics.Vulkan properties.Limits.FramebufferStencilSampleCounts; Capabilities = new HardwareCapabilities( - supportedExtensions.Contains("VK_EXT_index_type_uint8"), + _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"), supportsCustomBorderColor, supportsBlendOperationAdvanced, propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap, propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor, propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor, - supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), - supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"), - supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), + _physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName), + _physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"), + _physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"), supportsSubgroupSizeControl, featuresShaderInt8.ShaderInt8, - supportedExtensions.Contains("VK_EXT_shader_stencil_export"), - supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), - supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), + _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"), + _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), + _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), features2.Features.MultiViewport, featuresRobustness2.NullDescriptor || IsMoltenVk, - supportedExtensions.Contains(KhrPushDescriptor.ExtensionName), + _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName), featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, supportsTransformFeedback, propertiesTransformFeedback.TransformFeedbackQueries, features2.Features.OcclusionQueryPrecise, - supportedFeatures.PipelineStatisticsQuery, - supportedFeatures.GeometryShader, + _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery, + _physicalDevice.PhysicalDeviceFeatures.GeometryShader, propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, @@ -315,9 +313,9 @@ namespace Ryujinx.Graphics.Vulkan portabilityFlags, vertexBufferAlignment); - IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice); + IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice); - MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); + MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device); CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); @@ -345,22 +343,21 @@ namespace Ryujinx.Graphics.Vulkan Api = api; _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions()); - _debugMessenger = new VulkanDebugMessenger(api, _instance, logLevel); + _debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel); - if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi)) + if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi)) { SurfaceApi = surfaceApi; } - _surface = _getSurface(_instance, api); + _surface = _getSurface(_instance.Instance, api); _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId); var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount); - var supportedExtensions = VulkanInitialization.GetSupportedExtensions(api, _physicalDevice); - _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, supportedExtensions, maxQueueCount); + _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount); - if (api.TryGetDeviceExtension(_instance, _device, out KhrSwapchain swapchainApi)) + if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi)) { SwapchainApi = swapchainApi; } @@ -369,9 +366,9 @@ namespace Ryujinx.Graphics.Vulkan Queue = queue; QueueLock = new object(); - LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex); + LoadFeatures(maxQueueCount, queueFamilyIndex); - _window = new Window(this, _surface, _physicalDevice, _device); + _window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device); _initialized = true; } @@ -536,10 +533,9 @@ namespace Ryujinx.Graphics.Vulkan PNext = &featuresVk12 }; - Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); - Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties); + Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2); - var limits = properties.Limits; + var limits = _physicalDevice.PhysicalDeviceProperties.Limits; return new Capabilities( api: TargetApi.Vulkan, @@ -623,7 +619,7 @@ namespace Ryujinx.Graphics.Vulkan private unsafe void PrintGpuInformation() { - Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties); + var properties = _physicalDevice.PhysicalDeviceProperties; string vendorName = VendorUtils.GetNameFromId(properties.VendorID); @@ -807,14 +803,14 @@ namespace Ryujinx.Graphics.Vulkan sampler.Dispose(); } - SurfaceApi.DestroySurface(_instance, _surface, null); + SurfaceApi.DestroySurface(_instance.Instance, _surface, null); Api.DestroyDevice(_device, null); _debugMessenger.Dispose(); // Last step destroy the instance - Api.DestroyInstance(_instance, null); + _instance.Dispose(); } } } \ No newline at end of file From 49be977588c358e92eb9d9f5b0a6f49ee52003f4 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:42:32 -0400 Subject: [PATCH 435/737] Eliminate boxing allocations caused by ISampledData structs (#4556) * Redesign use of ISampledData for accessing the SamplingNumber value on input data structs. * Always read SamplingNumber as little-endian * Restored field order for SixAxisSensorState. Rework to allow possibility of non-zero offsets for the SamplingNumber field. Set StructLayout Pack=8 - the KeyboardState struct is 4 bytes shorter with any other value. * fix spelling Co-authored-by: riperiperi <rhy3756547@hotmail.com> * set Pack = 1 for ISampledDataStruct types, added Unknown field to KeyboardState * extend size of KeyboardModifier --------- Co-authored-by: riperiperi <rhy3756547@hotmail.com> --- .../SharedMemory/Common/AtomicStorage.cs | 6 +- .../Types/SharedMemory/Common/ISampledData.cs | 7 -- .../SharedMemory/Common/ISampledDataStruct.cs | 65 +++++++++++++++++++ .../Hid/Types/SharedMemory/Common/RingLifo.cs | 2 +- .../SharedMemory/DebugPad/DebugPadState.cs | 6 +- .../SharedMemory/Keyboard/KeyboardModifier.cs | 3 +- .../SharedMemory/Keyboard/KeyboardState.cs | 6 +- .../Types/SharedMemory/Mouse/MouseState.cs | 6 +- .../SharedMemory/Npad/NpadCommonState.cs | 6 +- .../SharedMemory/Npad/NpadGcTriggerState.cs | 6 +- .../SharedMemory/Npad/SixAxisSensorState.cs | 6 +- .../TouchScreen/TouchScreenState.cs | 6 +- 12 files changed, 91 insertions(+), 34 deletions(-) delete mode 100644 Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs create mode 100644 Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs index 45b92ba98..da53e4211 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common { - struct AtomicStorage<T> where T: unmanaged + struct AtomicStorage<T> where T: unmanaged, ISampledDataStruct { public ulong SamplingNumber; public T Object; @@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common public void SetObject(ref T obj) { - ISampledData samplingProvider = obj as ISampledData; + ulong samplingNumber = ISampledDataStruct.GetSamplingNumber(ref obj); - Interlocked.Exchange(ref SamplingNumber, samplingProvider.SamplingNumber); + Interlocked.Exchange(ref SamplingNumber, samplingNumber); Thread.MemoryBarrier(); diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs deleted file mode 100644 index 08f76747e..000000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common -{ - interface ISampledData - { - ulong SamplingNumber { get; } - } -} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs new file mode 100644 index 000000000..a382c0c28 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs @@ -0,0 +1,65 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common +{ + /// <summary> + /// This is a "marker interface" to add some compile-time safety to a convention-based optimization. + /// + /// Any struct implementing this interface should: + /// - use <c>StructLayoutAttribute</c> (and related attributes) to explicity control how the struct is laid out in memory. + /// - ensure that the method <c>ISampledDataStruct.GetSamplingNumberFieldOffset()</c> correctly returns the offset, in bytes, + /// to the ulong "Sampling Number" field within the struct. Most types have it as the first field, so the default offset is 0. + /// + /// Example: + /// + /// <c> + /// [StructLayout(LayoutKind.Sequential, Pack = 8)] + /// struct DebugPadState : ISampledDataStruct + /// { + /// public ulong SamplingNumber; // 1st field, so no need to add special handling to GetSamplingNumberFieldOffset() + /// // other members... + /// } + /// + /// [StructLayout(LayoutKind.Sequential, Pack = 8)] + /// struct SixAxisSensorState : ISampledDataStruct + /// { + /// public ulong DeltaTime; + /// public ulong SamplingNumber; // Not the first field - needs special handling in GetSamplingNumberFieldOffset() + /// // other members... + /// } + /// </c> + /// </summary> + internal interface ISampledDataStruct + { + // No Instance Members - marker interface only + + public static ulong GetSamplingNumber<T>(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct + { + ReadOnlySpan<T> structSpan = MemoryMarshal.CreateReadOnlySpan(ref sampledDataStruct, 1); + + ReadOnlySpan<byte> byteSpan = MemoryMarshal.Cast<T, byte>(structSpan); + + int fieldOffset = GetSamplingNumberFieldOffset(ref sampledDataStruct); + + if (fieldOffset > 0) + { + byteSpan = byteSpan.Slice(fieldOffset); + } + + ulong value = BinaryPrimitives.ReadUInt64LittleEndian(byteSpan); + + return value; + } + + private static int GetSamplingNumberFieldOffset<T>(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct + { + return sampledDataStruct switch + { + Npad.SixAxisSensorState _ => sizeof(ulong), + _ => 0 + }; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs index 615e38930..ae654d6f8 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs @@ -5,7 +5,7 @@ using System.Threading; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common { - struct RingLifo<T> where T: unmanaged + struct RingLifo<T> where T: unmanaged, ISampledDataStruct { private const ulong MaxEntries = 17; diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs index 3e1e1ad8e..0846cfc73 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs @@ -1,15 +1,15 @@ using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad { - struct DebugPadState : ISampledData + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DebugPadState : ISampledDataStruct { public ulong SamplingNumber; public DebugPadAttribute Attributes; public DebugPadButton Buttons; public AnalogStickState AnalogStickR; public AnalogStickState AnalogStickL; - - ulong ISampledData.SamplingNumber => SamplingNumber; } } diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs index 72d1603aa..839a4e829 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs @@ -2,9 +2,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard { - // TODO: This seems entirely wrong [Flags] - enum KeyboardModifier : uint + enum KeyboardModifier : ulong { None = 0, Control = 1 << 0, diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs index 376085066..4de92813b 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs @@ -1,13 +1,13 @@ using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard { - struct KeyboardState : ISampledData + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct KeyboardState : ISampledDataStruct { public ulong SamplingNumber; public KeyboardModifier Modifiers; public KeyboardKey Keys; - - ulong ISampledData.SamplingNumber => SamplingNumber; } } diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs index 67ad6bf1a..c953c7945 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs @@ -1,8 +1,10 @@ using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse { - struct MouseState : ISampledData + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct MouseState : ISampledDataStruct { public ulong SamplingNumber; public int X; @@ -13,7 +15,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse public int WheelDeltaY; public MouseButton Buttons; public MouseAttribute Attributes; - - ulong ISampledData.SamplingNumber => SamplingNumber; } } diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs index eaccef802..64f75ce9a 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs @@ -1,8 +1,10 @@ using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad { - struct NpadCommonState : ISampledData + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct NpadCommonState : ISampledDataStruct { public ulong SamplingNumber; public NpadButton Buttons; @@ -10,7 +12,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad public AnalogStickState AnalogStickR; public NpadAttribute Attributes; private uint _reserved; - - ulong ISampledData.SamplingNumber => SamplingNumber; } } diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs index 52668f85e..bddd6212d 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs @@ -1,15 +1,15 @@ using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad { - struct NpadGcTriggerState : ISampledData + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct NpadGcTriggerState : ISampledDataStruct { #pragma warning disable CS0649 public ulong SamplingNumber; public uint TriggerL; public uint TriggerR; #pragma warning restore CS0649 - - ulong ISampledData.SamplingNumber => SamplingNumber; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs index d024b0b0e..18be32763 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs @@ -1,9 +1,11 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad { - struct SixAxisSensorState : ISampledData + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SixAxisSensorState : ISampledDataStruct { public ulong DeltaTime; public ulong SamplingNumber; @@ -13,7 +15,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad public Array9<float> Direction; public SixAxisSensorAttribute Attributes; private uint _reserved; - - ulong ISampledData.SamplingNumber => SamplingNumber; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs index 8203e49b5..cdd4cc45e 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs @@ -1,15 +1,15 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen { - struct TouchScreenState : ISampledData + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TouchScreenState : ISampledDataStruct { public ulong SamplingNumber; public int TouchesCount; private int _reserved; public Array16<TouchState> Touches; - - ulong ISampledData.SamplingNumber => SamplingNumber; } } From a1efd87c45027a347e91fd22d42f33c3eed89030 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 9 Apr 2023 13:21:16 -0300 Subject: [PATCH 436/737] Implement remaining Arm64 HINT instructions as NOP (#4658) * Implement remaining HINT instructions as NOP * Split HINT encodings more to account for CSDB --- ARMeilleure/Decoders/OpCodeTable.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index 8464ce556..4f3599583 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -108,6 +108,13 @@ namespace ARMeilleure.Decoders SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit.Eor, OpCodeAluRs.Create); SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create); SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create); + SetA64("11010101000000110010000011011111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint + SetA64("11010101000000110010000011111111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint + SetA64("110101010000001100100001xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint + SetA64("1101010100000011001000100xx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint + SetA64("1101010100000011001000101>>11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint + SetA64("110101010000001100100011xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint + SetA64("11010101000000110010>>xxxxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint SetA64("11010101000000110011xxxx11011111", InstName.Isb, InstEmit.Isb, OpCodeSystem.Create); SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstName.Ldar, InstEmit.Ldar, OpCodeMemEx.Create); SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstName.Ldaxp, InstEmit.Ldaxp, OpCodeMemEx.Create); From 9db73f74cf77484c4d8b34af54c563c68cabb41e Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 10 Apr 2023 11:22:58 +0100 Subject: [PATCH 437/737] ARMeilleure: Respect FZ/RM flags for all floating point operations (#4618) * ARMeilleure: Respect Fz flag for all floating point operations. This is a change in strategy for emulating the Fz FPCR flag. Before, it was set before instructions that "needed it" and reset after. However, this missed a few hot instructions like the multiplication instruction, and the entirety of A32. The new strategy is to set the Fz flag only in the following circumstances: - Set to match FPCR before translated functions/loop are executed. - Reset when calling SoftFloat methods, set when returning. - Reset when exiting execution. This allows us to remove the code around the existing Fz aware instructions, and get the accuracy benefits on all floating point instructions executed while in translated code. Single step executions now need to be called with a context wrapper - right now it just contains the Fz flag initialization, and won't actually do anything on ARM. This fixes a bug in Breath of the Wild where some physics interactions could randomly crash the game due to subnormal values not flushing to zero. This is draft right now because I need to answer the questions: - Does dotnet avoid changing the value of Mxcsr? - Is it a good idea to assume that? Or should the flag set/restore be done on every managed method call, not just softfloat? - If we assume that, do we want a unit test to verify the behaviour? I recommend testing a bunch of games, especially games affected when this was originally added, such as #1611. * Remove unused method * Use FMA for Fmadd, Fmsub, Fnmadd, Fnmsub, Fmla, Fmls ...when available. Similar implementation to A32 * Use FMA for Frecps, Frsqrts * Don't set DAZ. * Add round mode to ARM FP mode * Fix mistakes * Add test for FP state when calling managed methods * Add explanatory comment to test. * Cleanup * Add A64 FPCR flags * Vrintx_S A32 fast path on A64 backend * Address feedback 1, re-enable DAZ * Fix FMA instructions By Elem * Address feedback --- ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs | 2 + ARMeilleure/CodeGen/X86/AssemblerTable.cs | 2 + ARMeilleure/CodeGen/X86/CodeGenerator.cs | 25 +- ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 6 +- ARMeilleure/CodeGen/X86/Mxcsr.cs | 15 + ARMeilleure/CodeGen/X86/PreAllocator.cs | 8 +- ARMeilleure/CodeGen/X86/X86Instruction.cs | 2 + .../Instructions/InstEmitSimdArithmetic.cs | 512 ++++++++++++------ ARMeilleure/Instructions/InstEmitSimdCvt32.cs | 17 +- .../Instructions/InstEmitSimdHelper.cs | 105 ++-- .../Instructions/InstEmitSimdHelper32.cs | 2 + ARMeilleure/Instructions/InstEmitSystem.cs | 4 + ARMeilleure/Instructions/InstEmitSystem32.cs | 2 + .../IntermediateRepresentation/Intrinsic.cs | 8 +- ARMeilleure/Translation/ArmEmitterContext.cs | 15 + ARMeilleure/Translation/DispatcherFunction.cs | 1 + ARMeilleure/Translation/PTC/Ptc.cs | 2 +- ARMeilleure/Translation/TranslatedFunction.cs | 5 + ARMeilleure/Translation/Translator.cs | 4 +- ARMeilleure/Translation/TranslatorStubs.cs | 71 +++ .../Translation/TranslatorTestMethods.cs | 148 +++++ Ryujinx.Tests/Cpu/EnvironmentTests.cs | 91 ++++ 22 files changed, 822 insertions(+), 225 deletions(-) create mode 100644 ARMeilleure/CodeGen/X86/Mxcsr.cs create mode 100644 ARMeilleure/Translation/TranslatorTestMethods.cs create mode 100644 Ryujinx.Tests/Cpu/EnvironmentTests.cs diff --git a/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs b/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs index 53ef152e5..a309d56d9 100644 --- a/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs +++ b/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs @@ -226,6 +226,8 @@ namespace ARMeilleure.CodeGen.Arm64 Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem)); Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd)); Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi)); + Add(Intrinsic.Arm64MrsFpcr, new IntrinsicInfo(0xd53b4400u, IntrinsicType.GetRegister)); + Add(Intrinsic.Arm64MsrFpcr, new IntrinsicInfo(0xd51b4400u, IntrinsicType.SetRegister)); Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister)); Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister)); Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem)); diff --git a/ARMeilleure/CodeGen/X86/AssemblerTable.cs b/ARMeilleure/CodeGen/X86/AssemblerTable.cs index b47b3ecd1..e6a2ff07f 100644 --- a/ARMeilleure/CodeGen/X86/AssemblerTable.cs +++ b/ARMeilleure/CodeGen/X86/AssemblerTable.cs @@ -268,11 +268,13 @@ namespace ARMeilleure.CodeGen.X86 Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66)); + Add(X86Instruction.Vfnmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66)); diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index 8b5a3fc57..e7179b517 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -249,10 +249,9 @@ namespace ARMeilleure.CodeGen.X86 case IntrinsicType.Mxcsr: { Operand offset = operation.GetSource(0); - Operand bits = operation.GetSource(1); - Debug.Assert(offset.Kind == OperandKind.Constant && bits.Kind == OperandKind.Constant); - Debug.Assert(offset.Type == OperandType.I32 && bits.Type == OperandType.I32); + Debug.Assert(offset.Kind == OperandKind.Constant); + Debug.Assert(offset.Type == OperandType.I32); int offs = offset.AsInt32() + context.CallArgsRegionSize; @@ -261,21 +260,23 @@ namespace ARMeilleure.CodeGen.X86 Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding); - context.Assembler.Stmxcsr(memOp); - - if (operation.Intrinsic == Intrinsic.X86Mxcsrmb) + if (operation.Intrinsic == Intrinsic.X86Ldmxcsr) { - context.Assembler.Or(memOp, bits, OperandType.I32); + Operand bits = operation.GetSource(1); + Debug.Assert(bits.Type == OperandType.I32); + + context.Assembler.Mov(memOp, bits, OperandType.I32); + context.Assembler.Ldmxcsr(memOp); } - else /* if (intrinOp.Intrinsic == Intrinsic.X86Mxcsrub) */ + else if (operation.Intrinsic == Intrinsic.X86Stmxcsr) { - Operand notBits = Const(~bits.AsInt32()); + Operand dest = operation.Destination; + Debug.Assert(dest.Type == OperandType.I32); - context.Assembler.And(memOp, notBits, OperandType.I32); + context.Assembler.Stmxcsr(memOp); + context.Assembler.Mov(dest, memOp, OperandType.I32); } - context.Assembler.Ldmxcsr(memOp); - break; } diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs index c788fa442..e3d94b7ae 100644 --- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs +++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs @@ -60,6 +60,7 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary)); Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary)); Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm)); + Add(Intrinsic.X86Ldmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary)); Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary)); Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary)); @@ -75,8 +76,6 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary)); Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary)); Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary)); - Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits. - Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits. Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary)); Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary)); Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary)); @@ -160,6 +159,7 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary)); Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary)); Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary)); + Add(Intrinsic.X86Stmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary)); Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary)); Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary)); @@ -170,11 +170,13 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary)); Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary)); Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm)); + Add(Intrinsic.X86Vfmadd231pd, new IntrinsicInfo(X86Instruction.Vfmadd231pd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma)); + Add(Intrinsic.X86Vfnmadd231pd, new IntrinsicInfo(X86Instruction.Vfnmadd231pd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma)); diff --git a/ARMeilleure/CodeGen/X86/Mxcsr.cs b/ARMeilleure/CodeGen/X86/Mxcsr.cs new file mode 100644 index 000000000..c61eac31a --- /dev/null +++ b/ARMeilleure/CodeGen/X86/Mxcsr.cs @@ -0,0 +1,15 @@ +using System; + +namespace ARMeilleure.CodeGen.X86 +{ + [Flags] + enum Mxcsr + { + Ftz = 1 << 15, // Flush To Zero. + Rhi = 1 << 14, // Round Mode high bit. + Rlo = 1 << 13, // Round Mode low bit. + Um = 1 << 11, // Underflow Mask. + Dm = 1 << 8, // Denormal Mask. + Daz = 1 << 6 // Denormals Are Zero. + } +} diff --git a/ARMeilleure/CodeGen/X86/PreAllocator.cs b/ARMeilleure/CodeGen/X86/PreAllocator.cs index 72f56514f..cb742d67f 100644 --- a/ARMeilleure/CodeGen/X86/PreAllocator.cs +++ b/ARMeilleure/CodeGen/X86/PreAllocator.cs @@ -120,12 +120,18 @@ namespace ARMeilleure.CodeGen.X86 break; case Instruction.Extended: - if (node.Intrinsic == Intrinsic.X86Mxcsrmb || node.Intrinsic == Intrinsic.X86Mxcsrub) + if (node.Intrinsic == Intrinsic.X86Ldmxcsr) { int stackOffset = stackAlloc.Allocate(OperandType.I32); node.SetSources(new Operand[] { Const(stackOffset), node.GetSource(0) }); } + else if (node.Intrinsic == Intrinsic.X86Stmxcsr) + { + int stackOffset = stackAlloc.Allocate(OperandType.I32); + + node.SetSources(new Operand[] { Const(stackOffset) }); + } break; } } diff --git a/ARMeilleure/CodeGen/X86/X86Instruction.cs b/ARMeilleure/CodeGen/X86/X86Instruction.cs index ecfc432d7..9a85c516f 100644 --- a/ARMeilleure/CodeGen/X86/X86Instruction.cs +++ b/ARMeilleure/CodeGen/X86/X86Instruction.cs @@ -208,11 +208,13 @@ namespace ARMeilleure.CodeGen.X86 Vblendvps, Vcvtph2ps, Vcvtps2ph, + Vfmadd231pd, Vfmadd231ps, Vfmadd231sd, Vfmadd231ss, Vfmsub231sd, Vfmsub231ss, + Vfnmadd231pd, Vfnmadd231ps, Vfnmadd231sd, Vfnmadd231ss, diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs index d0bb68e4f..7e7f26b1a 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs @@ -615,14 +615,11 @@ namespace ARMeilleure.Instructions { return EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; + IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; - Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd; + Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd; - return context.AddIntrinsic(addInst, op1, op2); - }, scalar: false, op1, op2); + return context.AddIntrinsic(addInst, op1, op2); }, scalar: false, op1, op2); }); } @@ -696,17 +693,33 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); + Operand res; + if (op.Size == 0) { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); - res = context.AddIntrinsic(Intrinsic.X86Addss, a, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, a, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + res = context.AddIntrinsic(Intrinsic.X86Addss, a, res); + } context.Copy(d, context.VectorZeroUpper96(res)); } else /* if (op.Size == 1) */ { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); - res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, a, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res); + } context.Copy(d, context.VectorZeroUpper64(res)); } @@ -730,10 +743,7 @@ namespace ARMeilleure.Instructions { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); - }, scalar: true, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); }, scalar: true); } else @@ -755,10 +765,7 @@ namespace ARMeilleure.Instructions { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); - }, scalar: false, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); }, scalar: false); } else @@ -886,10 +893,7 @@ namespace ARMeilleure.Instructions { return EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); - }, scalar: false, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); }, scalar: false, op1, op2); }); } @@ -914,10 +918,7 @@ namespace ARMeilleure.Instructions { return EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); - }, scalar: false, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); }, scalar: false, op1, op2); }); } @@ -940,10 +941,7 @@ namespace ARMeilleure.Instructions { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); - }, scalar: true, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); }, scalar: true); } else @@ -965,10 +963,7 @@ namespace ARMeilleure.Instructions { EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); - }, scalar: false, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); }, scalar: false); } else @@ -1096,10 +1091,7 @@ namespace ARMeilleure.Instructions { return EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); - }, scalar: false, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); }, scalar: false, op1, op2); }); } @@ -1124,10 +1116,7 @@ namespace ARMeilleure.Instructions { return EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); - }, scalar: false, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); }, scalar: false, op1, op2); }); } @@ -1146,6 +1135,37 @@ namespace ARMeilleure.Instructions { InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe); } + else if (Optimizations.UseFma) + { + OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6; + + Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask)); + + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, d, n, res); + + context.Copy(d, context.VectorZeroUpper96(res)); + } + else /* if (sizeF == 1) */ + { + int shuffleMask = op.Index | op.Index << 1; + + Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask)); + + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, d, n, res); + + context.Copy(d, context.VectorZeroUpper64(res)); + } + } else { EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => @@ -1171,11 +1191,19 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; + Operand res; + if (sizeF == 0) { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); - - res = context.AddIntrinsic(Intrinsic.X86Addps, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); + res = context.AddIntrinsic(Intrinsic.X86Addps, d, res); + } if (op.RegisterSize == RegisterSize.Simd64) { @@ -1186,9 +1214,15 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); - - res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res); + } context.Copy(d, res); } @@ -1224,8 +1258,15 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask)); - res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res); - res = context.AddIntrinsic(Intrinsic.X86Addps, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, res); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res); + res = context.AddIntrinsic(Intrinsic.X86Addps, d, res); + } if (op.RegisterSize == RegisterSize.Simd64) { @@ -1240,8 +1281,15 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask)); - res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res); - res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, res); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res); + res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res); + } context.Copy(d, res); } @@ -1261,6 +1309,37 @@ namespace ARMeilleure.Instructions { InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe); } + else if (Optimizations.UseFma) + { + OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp; + + Operand d = GetVec(op.Rd); + Operand n = GetVec(op.Rn); + Operand m = GetVec(op.Rm); + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6; + + Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask)); + + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, d, n, res); + + context.Copy(d, context.VectorZeroUpper96(res)); + } + else /* if (sizeF == 1) */ + { + int shuffleMask = op.Index | op.Index << 1; + + Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask)); + + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, d, n, res); + + context.Copy(d, context.VectorZeroUpper64(res)); + } + } else { EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => @@ -1286,11 +1365,19 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; + Operand res; + if (sizeF == 0) { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); - - res = context.AddIntrinsic(Intrinsic.X86Subps, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subps, d, res); + } if (op.RegisterSize == RegisterSize.Simd64) { @@ -1301,9 +1388,15 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); - - res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res); + } context.Copy(d, res); } @@ -1339,8 +1432,15 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask)); - res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res); - res = context.AddIntrinsic(Intrinsic.X86Subps, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, res); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res); + res = context.AddIntrinsic(Intrinsic.X86Subps, d, res); + } if (op.RegisterSize == RegisterSize.Simd64) { @@ -1355,8 +1455,15 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask)); - res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res); - res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, res); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res); + res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res); + } context.Copy(d, res); } @@ -1385,17 +1492,33 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); + Operand res; + if (op.Size == 0) { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); - res = context.AddIntrinsic(Intrinsic.X86Subss, a, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, a, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subss, a, res); + } context.Copy(d, context.VectorZeroUpper96(res)); } else /* if (op.Size == 1) */ { - Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); - res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, a, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res); + } context.Copy(d, context.VectorZeroUpper64(res)); } @@ -1669,25 +1792,39 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); + Operand res; + if (op.Size == 0) { - Operand mask = X86GetScalar(context, -0f); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231ss, a, n, m); + } + else + { + Operand mask = X86GetScalar(context, -0f); + Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a); - Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a); - - Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); - res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res); + res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res); + } context.Copy(d, context.VectorZeroUpper96(res)); } else /* if (op.Size == 1) */ { - Operand mask = X86GetScalar(context, -0d); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231sd, a, n, m); + } + else + { + Operand mask = X86GetScalar(context, -0d); + Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a); - Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a); - - Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); - res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res); + res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res); + } context.Copy(d, context.VectorZeroUpper64(res)); } @@ -1716,25 +1853,39 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); + Operand res; + if (op.Size == 0) { - Operand mask = X86GetScalar(context, -0f); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmsub231ss, a, n, m); + } + else + { + Operand mask = X86GetScalar(context, -0f); + Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a); - Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a); - - Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); - res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res); + res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res); + } context.Copy(d, context.VectorZeroUpper96(res)); } else /* if (op.Size == 1) */ { - Operand mask = X86GetScalar(context, -0d); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfmsub231sd, a, n, m); + } + else + { + Operand mask = X86GetScalar(context, -0d); + Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a); - Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a); - - Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); - res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res); + res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res); + } context.Copy(d, context.VectorZeroUpper64(res)); } @@ -1830,13 +1981,22 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; + Operand res; + if (sizeF == 0) { Operand mask = X86GetScalar(context, 2f); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, mask, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF); context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res)); @@ -1845,9 +2005,16 @@ namespace ARMeilleure.Instructions { Operand mask = X86GetScalar(context, 2d); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, mask, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF); context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res)); @@ -1877,14 +2044,23 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; + Operand res; + if (sizeF == 0) { Operand mask = X86GetAllElements(context, 2f); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); - res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, mask, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res); + res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF); if (op.RegisterSize == RegisterSize.Simd64) { @@ -1897,10 +2073,17 @@ namespace ARMeilleure.Instructions { Operand mask = X86GetAllElements(context, 2d); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); - res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, mask, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res); + res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF); context.Copy(GetVec(op.Rd), res); } @@ -2113,20 +2296,32 @@ namespace ARMeilleure.Instructions public static void Frintx_S(ArmEmitterContext context) { - // TODO Arm64: Fast path. Should we set host FPCR? - EmitScalarUnaryOpF(context, (op1) => + if (Optimizations.UseAdvSimd) { - return EmitRoundByRMode(context, op1); - }); + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintxS); + } + else + { + EmitScalarUnaryOpF(context, (op1) => + { + return EmitRoundByRMode(context, op1); + }); + } } public static void Frintx_V(ArmEmitterContext context) { - // TODO Arm64: Fast path. Should we set host FPCR? - EmitVectorUnaryOpF(context, (op1) => + if (Optimizations.UseAdvSimd) { - return EmitRoundByRMode(context, op1); - }); + InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintxV); + } + else + { + EmitVectorUnaryOpF(context, (op1) => + { + return EmitRoundByRMode(context, op1); + }); + } } public static void Frintz_S(ArmEmitterContext context) @@ -2237,16 +2432,25 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; + Operand res; + if (sizeF == 0) { Operand maskHalf = X86GetScalar(context, 0.5f); Operand maskThree = X86GetScalar(context, 3f); Operand maskOneHalf = X86GetScalar(context, 1.5f); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, maskThree, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res); - res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res); + res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF); context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res)); @@ -2257,10 +2461,17 @@ namespace ARMeilleure.Instructions Operand maskThree = X86GetScalar(context, 3d); Operand maskOneHalf = X86GetScalar(context, 1.5d); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, maskThree, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res); - res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res); + res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF); context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res)); @@ -2290,15 +2501,24 @@ namespace ARMeilleure.Instructions int sizeF = op.Size & 1; + Operand res; + if (sizeF == 0) { Operand maskHalf = X86GetAllElements(context, 0.5f); Operand maskThree = X86GetAllElements(context, 3f); Operand maskOneHalf = X86GetAllElements(context, 1.5f); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, maskThree, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res); res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF); @@ -2315,9 +2535,16 @@ namespace ARMeilleure.Instructions Operand maskThree = X86GetAllElements(context, 3d); Operand maskOneHalf = X86GetAllElements(context, 1.5d); - Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); + if (Optimizations.UseFma) + { + res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, maskThree, n, m); + } + else + { + res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); + res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res); + } - res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res); res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF); @@ -4728,53 +4955,6 @@ namespace ARMeilleure.Instructions } } - public static Operand EmitSseOrAvxHandleFzModeOpF( - ArmEmitterContext context, - Func2I emit, - bool scalar, - Operand n = default, - Operand m = default) - { - Operand nCopy = n == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rn)) : n; - Operand mCopy = m == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rm)) : m; - - EmitSseOrAvxEnterFtzAndDazModesOpF(context, out Operand isTrue); - - Operand res = emit(nCopy, mCopy); - - EmitSseOrAvxExitFtzAndDazModesOpF(context, isTrue); - - if (n != default || m != default) - { - return res; - } - - int sizeF = ((IOpCodeSimd)context.CurrOp).Size & 1; - - if (sizeF == 0) - { - if (scalar) - { - res = context.VectorZeroUpper96(res); - } - else if (((OpCodeSimdReg)context.CurrOp).RegisterSize == RegisterSize.Simd64) - { - res = context.VectorZeroUpper64(res); - } - } - else /* if (sizeF == 1) */ - { - if (scalar) - { - res = context.VectorZeroUpper64(res); - } - } - - context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rd), res); - - return default; - } - private static Operand EmitSse2VectorMaxMinOpF(ArmEmitterContext context, Operand n, Operand m, bool isMax) { IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; @@ -4834,10 +5014,7 @@ namespace ARMeilleure.Instructions Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum); - }, scalar: scalar, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum); }, scalar: scalar, nCopy, mCopy); if (n != default || m != default) @@ -4872,10 +5049,7 @@ namespace ARMeilleure.Instructions Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) => { - return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => - { - return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum); - }, scalar: scalar, op1, op2); + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum); }, scalar: scalar, nCopy, mCopy); if (n != default || m != default) diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index 5fdc3b5ad..33ae83df6 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -356,9 +356,11 @@ namespace ARMeilleure.Instructions ? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)) : typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)); + context.ExitArmFpMode(); context.StoreToContext(); Operand res = context.Call(method, src); context.LoadFromContext(); + context.EnterArmFpMode(); InsertScalar16(context, op.Vd, op.T, res); } @@ -372,9 +374,11 @@ namespace ARMeilleure.Instructions ? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)) : typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)); + context.ExitArmFpMode(); context.StoreToContext(); Operand res = context.Call(method, src); context.LoadFromContext(); + context.EnterArmFpMode(); InsertScalar(context, op.Vd, res); } @@ -542,10 +546,17 @@ namespace ARMeilleure.Instructions // VRINTX (floating-point). public static void Vrintx_S(ArmEmitterContext context) { - EmitScalarUnaryOpF32(context, (op1) => + if (Optimizations.UseAdvSimd) { - return EmitRoundByRMode(context, op1); - }); + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintxS); + } + else + { + EmitScalarUnaryOpF32(context, (op1) => + { + return EmitRoundByRMode(context, op1); + }); + } } private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed) diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs index 0e7af794a..c44c9b4d9 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -1,3 +1,4 @@ +using ARMeilleure.CodeGen.X86; using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; @@ -158,6 +159,75 @@ namespace ARMeilleure.Instructions }; #endregion + public static void EnterArmFpMode(EmitterContext context, Func<FPState, Operand> getFpFlag) + { + if (Optimizations.UseSse2) + { + Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr); + + Operand fzTrue = getFpFlag(FPState.FzFlag); + Operand r0True = getFpFlag(FPState.RMode0Flag); + Operand r1True = getFpFlag(FPState.RMode1Flag); + + mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo))); + + mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(fzTrue, Const((int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Um | Mxcsr.Dm)), Const(0))); + + // X86 round modes in order: nearest, negative, positive, zero + // ARM round modes in order: nearest, positive, negative, zero + // Read the bits backwards to correct this. + + mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r0True, Const((int)Mxcsr.Rhi), Const(0))); + mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r1True, Const((int)Mxcsr.Rlo), Const(0))); + + context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr); + } + else if (Optimizations.UseAdvSimd) + { + Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr); + + Operand fzTrue = getFpFlag(FPState.FzFlag); + Operand r0True = getFpFlag(FPState.RMode0Flag); + Operand r1True = getFpFlag(FPState.RMode1Flag); + + fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1))); + + fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(fzTrue, Const((int)FPCR.Fz), Const(0))); + fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r0True, Const((int)FPCR.RMode0), Const(0))); + fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r1True, Const((int)FPCR.RMode1), Const(0))); + + context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr); + + // TODO: Restore FPSR + } + } + + public static void ExitArmFpMode(EmitterContext context, Action<FPState, Operand> setFpFlag) + { + if (Optimizations.UseSse2) + { + Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr); + + // Unset round mode (to nearest) and ftz. + mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo))); + + context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr); + + // Status flags would be stored here if they were used. + } + else if (Optimizations.UseAdvSimd) + { + Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr); + + // Unset round mode (to nearest) and fz. + fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1))); + + context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr); + + // TODO: Store FPSR + } + } + public static int GetImmShl(OpCodeSimdShImm op) { return op.Imm - (8 << op.Size); @@ -465,9 +535,11 @@ namespace ARMeilleure.Instructions ? typeof(SoftFloat32).GetMethod(name) : typeof(SoftFloat64).GetMethod(name); + context.ExitArmFpMode(); context.StoreToContext(); Operand res = context.Call(info, callArgs); context.LoadFromContext(); + context.EnterArmFpMode(); return res; } @@ -1358,39 +1430,6 @@ namespace ARMeilleure.Instructions } } - [Flags] - public enum Mxcsr - { - Ftz = 1 << 15, // Flush To Zero. - Um = 1 << 11, // Underflow Mask. - Dm = 1 << 8, // Denormal Mask. - Daz = 1 << 6 // Denormals Are Zero. - } - - public static void EmitSseOrAvxEnterFtzAndDazModesOpF(ArmEmitterContext context, out Operand isTrue) - { - isTrue = GetFpFlag(FPState.FzFlag); - - Operand lblTrue = Label(); - context.BranchIfFalse(lblTrue, isTrue); - - context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrmb, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm | Mxcsr.Daz))); - - context.MarkLabel(lblTrue); - } - - public static void EmitSseOrAvxExitFtzAndDazModesOpF(ArmEmitterContext context, Operand isTrue = default) - { - isTrue = isTrue == default ? GetFpFlag(FPState.FzFlag) : isTrue; - - Operand lblTrue = Label(); - context.BranchIfFalse(lblTrue, isTrue); - - context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrub, Const((int)(Mxcsr.Ftz | Mxcsr.Daz))); - - context.MarkLabel(lblTrue); - } - public enum CmpCondition { // Legacy Sse. diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs index 84b01d05c..36d27d425 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs @@ -1197,9 +1197,11 @@ namespace ARMeilleure.Instructions Array.Resize(ref callArgs, callArgs.Length + 1); callArgs[callArgs.Length - 1] = Const(1); + context.ExitArmFpMode(); context.StoreToContext(); Operand res = context.Call(info, callArgs); context.LoadFromContext(); + context.EnterArmFpMode(); return res; } diff --git a/ARMeilleure/Instructions/InstEmitSystem.cs b/ARMeilleure/Instructions/InstEmitSystem.cs index 1345bbf10..f668b83b6 100644 --- a/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/ARMeilleure/Instructions/InstEmitSystem.cs @@ -192,6 +192,8 @@ namespace ARMeilleure.Instructions SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1))); } } + + context.UpdateArmFpMode(); } private static void EmitSetFpsr(ArmEmitterContext context) @@ -210,6 +212,8 @@ namespace ARMeilleure.Instructions SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1))); } } + + context.UpdateArmFpMode(); } } } diff --git a/ARMeilleure/Instructions/InstEmitSystem32.cs b/ARMeilleure/Instructions/InstEmitSystem32.cs index e07db4121..2f6cf19d6 100644 --- a/ARMeilleure/Instructions/InstEmitSystem32.cs +++ b/ARMeilleure/Instructions/InstEmitSystem32.cs @@ -321,6 +321,8 @@ namespace ARMeilleure.Instructions SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1))); } } + + context.UpdateArmFpMode(); } } } diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index b629345ee..f5a776fa2 100644 --- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -53,6 +53,7 @@ namespace ARMeilleure.IntermediateRepresentation X86Haddpd, X86Haddps, X86Insertps, + X86Ldmxcsr, X86Maxpd, X86Maxps, X86Maxsd, @@ -68,8 +69,6 @@ namespace ARMeilleure.IntermediateRepresentation X86Mulps, X86Mulsd, X86Mulss, - X86Mxcsrmb, - X86Mxcsrub, X86Paddb, X86Paddd, X86Paddq, @@ -153,6 +152,7 @@ namespace ARMeilleure.IntermediateRepresentation X86Sqrtps, X86Sqrtsd, X86Sqrtss, + X86Stmxcsr, X86Subpd, X86Subps, X86Subsd, @@ -163,11 +163,13 @@ namespace ARMeilleure.IntermediateRepresentation X86Unpcklps, X86Vcvtph2ps, X86Vcvtps2ph, + X86Vfmadd231pd, X86Vfmadd231ps, X86Vfmadd231sd, X86Vfmadd231ss, X86Vfmsub231sd, X86Vfmsub231ss, + X86Vfnmadd231pd, X86Vfnmadd231ps, X86Vfnmadd231sd, X86Vfnmadd231ss, @@ -394,6 +396,8 @@ namespace ARMeilleure.IntermediateRepresentation Arm64MlsVe, Arm64MlsV, Arm64MoviV, + Arm64MrsFpcr, + Arm64MsrFpcr, Arm64MrsFpsr, Arm64MsrFpsr, Arm64MulVe, diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/ARMeilleure/Translation/ArmEmitterContext.cs index 238f85082..565d2aada 100644 --- a/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/ARMeilleure/Translation/ArmEmitterContext.cs @@ -188,6 +188,21 @@ namespace ARMeilleure.Translation } } + public void EnterArmFpMode() + { + InstEmitSimdHelper.EnterArmFpMode(this, InstEmitHelper.GetFpFlag); + } + + public void UpdateArmFpMode() + { + EnterArmFpMode(); + } + + public void ExitArmFpMode() + { + InstEmitSimdHelper.ExitArmFpMode(this, (flag, value) => InstEmitHelper.SetFpFlag(this, flag, value)); + } + public Operand TryGetComparisonResult(Condition condition) { if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet) diff --git a/ARMeilleure/Translation/DispatcherFunction.cs b/ARMeilleure/Translation/DispatcherFunction.cs index e3ea21f67..7d5a3388e 100644 --- a/ARMeilleure/Translation/DispatcherFunction.cs +++ b/ARMeilleure/Translation/DispatcherFunction.cs @@ -3,4 +3,5 @@ namespace ARMeilleure.Translation { delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress); + delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress); } diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 17f687062..5970c4ff9 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 4485; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 4626; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/ARMeilleure/Translation/TranslatedFunction.cs b/ARMeilleure/Translation/TranslatedFunction.cs index 71eec08ac..f007883ef 100644 --- a/ARMeilleure/Translation/TranslatedFunction.cs +++ b/ARMeilleure/Translation/TranslatedFunction.cs @@ -25,5 +25,10 @@ namespace ARMeilleure.Translation { return _func(context.NativeContextPtr); } + + public ulong Execute(WrapperFunction dispatcher, State.ExecutionContext context) + { + return dispatcher(context.NativeContextPtr, (ulong)FuncPointer); + } } } \ No newline at end of file diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 0c05b2b49..f349c5ebf 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -183,7 +183,7 @@ namespace ARMeilleure.Translation Statistics.StartTimer(); - ulong nextAddr = func.Execute(context); + ulong nextAddr = func.Execute(Stubs.ContextWrapper, context); Statistics.StopTimer(address); @@ -194,7 +194,7 @@ namespace ARMeilleure.Translation { TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true); - address = func.Execute(context); + address = func.Execute(Stubs.ContextWrapper, context); EnqueueForDeletion(address, func); diff --git a/ARMeilleure/Translation/TranslatorStubs.cs b/ARMeilleure/Translation/TranslatorStubs.cs index 6ed84de80..69648df44 100644 --- a/ARMeilleure/Translation/TranslatorStubs.cs +++ b/ARMeilleure/Translation/TranslatorStubs.cs @@ -21,6 +21,7 @@ namespace ARMeilleure.Translation private readonly Translator _translator; private readonly Lazy<IntPtr> _dispatchStub; private readonly Lazy<DispatcherFunction> _dispatchLoop; + private readonly Lazy<WrapperFunction> _contextWrapper; /// <summary> /// Gets the dispatch stub. @@ -64,6 +65,20 @@ namespace ARMeilleure.Translation } } + /// <summary> + /// Gets the context wrapper function. + /// </summary> + /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception> + public WrapperFunction ContextWrapper + { + get + { + ObjectDisposedException.ThrowIf(_disposed, this); + + return _contextWrapper.Value; + } + } + /// <summary> /// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified /// <see cref="Translator"/> instance. @@ -77,6 +92,7 @@ namespace ARMeilleure.Translation _translator = translator; _dispatchStub = new(GenerateDispatchStub, isThreadSafe: true); _dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true); + _contextWrapper = new(GenerateContextWrapper, isThreadSafe: true); } /// <summary> @@ -202,6 +218,32 @@ namespace ARMeilleure.Translation return Marshal.GetFunctionPointerForDelegate(func); } + /// <summary> + /// Emits code that syncs FP state before executing guest code, or returns it to normal. + /// </summary> + /// <param name="context">Emitter context for the method</param> + /// <param name="nativeContext">Pointer to the native context</param> + /// <param name="enter">True if entering guest code, false otherwise</param> + private void EmitSyncFpContext(EmitterContext context, Operand nativeContext, bool enter) + { + if (enter) + { + InstEmitSimdHelper.EnterArmFpMode(context, (flag) => + { + Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag)))); + return context.Load(OperandType.I32, flagAddress); + }); + } + else + { + InstEmitSimdHelper.ExitArmFpMode(context, (flag, value) => + { + Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag)))); + context.Store(flagAddress, value); + }); + } + } + /// <summary> /// Generates a <see cref="DispatchLoop"/> function. /// </summary> @@ -221,6 +263,8 @@ namespace ARMeilleure.Translation Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset())); Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())); + EmitSyncFpContext(context, nativeContext, true); + context.MarkLabel(beginLbl); context.Store(dispatchAddress, guestAddress); context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext)); @@ -229,6 +273,9 @@ namespace ARMeilleure.Translation context.Branch(beginLbl); context.MarkLabel(endLbl); + + EmitSyncFpContext(context, nativeContext, false); + context.Return(); var cfg = context.GetControlFlowGraph(); @@ -237,5 +284,29 @@ namespace ARMeilleure.Translation return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>(); } + + /// <summary> + /// Generates a <see cref="ContextWrapper"/> function. + /// </summary> + /// <returns><see cref="ContextWrapper"/> function</returns> + private WrapperFunction GenerateContextWrapper() + { + var context = new EmitterContext(); + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + Operand guestMethod = context.LoadArgument(OperandType.I64, 1); + + EmitSyncFpContext(context, nativeContext, true); + Operand returnValue = context.Call(guestMethod, OperandType.I64, nativeContext); + EmitSyncFpContext(context, nativeContext, false); + + context.Return(returnValue); + + var cfg = context.GetControlFlowGraph(); + var retType = OperandType.I64; + var argTypes = new[] { OperandType.I64, OperandType.I64 }; + + return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>(); + } } } diff --git a/ARMeilleure/Translation/TranslatorTestMethods.cs b/ARMeilleure/Translation/TranslatorTestMethods.cs new file mode 100644 index 000000000..ab96019a6 --- /dev/null +++ b/ARMeilleure/Translation/TranslatorTestMethods.cs @@ -0,0 +1,148 @@ +using ARMeilleure.CodeGen.X86; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.State; +using ARMeilleure.Translation; +using System; +using System.Runtime.InteropServices; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; + +namespace ARMeilleure.Translation +{ + public static class TranslatorTestMethods + { + public delegate int FpFlagsPInvokeTest(IntPtr managedMethod); + + private static bool SetPlatformFtz(EmitterContext context, bool ftz) + { + if (Optimizations.UseSse2) + { + Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr); + + if (ftz) + { + mxcsr = context.BitwiseOr(mxcsr, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm))); + } + else + { + mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)Mxcsr.Ftz)); + } + + context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr); + + return true; + } + else if (Optimizations.UseAdvSimd) + { + Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr); + + if (ftz) + { + fpcr = context.BitwiseOr(fpcr, Const((int)FPCR.Fz)); + } + else + { + fpcr = context.BitwiseAnd(fpcr, Const(~(int)FPCR.Fz)); + } + + context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr); + + return true; + } + else + { + return false; + } + } + + private static Operand FpBitsToInt(EmitterContext context, Operand fp) + { + Operand vec = context.VectorInsert(context.VectorZero(), fp, 0); + return context.VectorExtract(OperandType.I32, vec, 0); + } + + public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest() + { + EmitterContext context = new EmitterContext(); + + Operand methodAddress = context.Copy(context.LoadArgument(OperandType.I64, 0)); + + // Verify that default dotnet fp state does not flush to zero. + // This is required for SoftFloat to function. + + // Denormal + zero != 0 + + Operand denormal = ConstF(BitConverter.Int32BitsToSingle(1)); // 1.40129846432e-45 + Operand zeroF = ConstF(0f); + Operand zero = Const(0); + + Operand result = context.Add(zeroF, denormal); + + // Must not be zero. + + Operand correct1Label = Label(); + + context.BranchIfFalse(correct1Label, context.ICompareEqual(FpBitsToInt(context, result), zero)); + + context.Return(Const(1)); + + context.MarkLabel(correct1Label); + + // Set flush to zero flag. If unsupported by the backend, just return true. + + if (!SetPlatformFtz(context, true)) + { + context.Return(Const(0)); + } + + // Denormal + zero == 0 + + Operand resultFz = context.Add(zeroF, denormal); + + // Must equal zero. + + Operand correct2Label = Label(); + + context.BranchIfTrue(correct2Label, context.ICompareEqual(FpBitsToInt(context, resultFz), zero)); + + SetPlatformFtz(context, false); + + context.Return(Const(2)); + + context.MarkLabel(correct2Label); + + // Call a managed method. This method should not change Fz state. + + context.Call(methodAddress, OperandType.None); + + // Denormal + zero == 0 + + Operand resultFz2 = context.Add(zeroF, denormal); + + // Must equal zero. + + Operand correct3Label = Label(); + + context.BranchIfTrue(correct3Label, context.ICompareEqual(FpBitsToInt(context, resultFz2), zero)); + + SetPlatformFtz(context, false); + + context.Return(Const(3)); + + context.MarkLabel(correct3Label); + + // Success. + + SetPlatformFtz(context, false); + + context.Return(Const(0)); + + // Compile and return the function. + + ControlFlowGraph cfg = context.GetControlFlowGraph(); + + OperandType[] argTypes = new OperandType[] { OperandType.I64 }; + + return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>(); + } + } +} diff --git a/Ryujinx.Tests/Cpu/EnvironmentTests.cs b/Ryujinx.Tests/Cpu/EnvironmentTests.cs new file mode 100644 index 000000000..d374c08a5 --- /dev/null +++ b/Ryujinx.Tests/Cpu/EnvironmentTests.cs @@ -0,0 +1,91 @@ +using ARMeilleure.Translation; +using NUnit.Framework; +using Ryujinx.Cpu.Jit; +using Ryujinx.Tests.Memory; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Tests.Cpu +{ + internal class EnvironmentTests + { + private static Translator _translator; + + private void EnsureTranslator() + { + // Create a translator, as one is needed to register the signal handler or emit methods. + _translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + private float GetDenormal() + { + return BitConverter.Int32BitsToSingle(1); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + private float GetZero() + { + return BitConverter.Int32BitsToSingle(0); + } + + /// <summary> + /// This test ensures that managed methods do not reset floating point control flags. + /// This is used to avoid changing control flags when running methods that don't require it, such as SVC calls, software memory... + /// </summary> + [Test] + public void FpFlagsPInvoke() + { + EnsureTranslator(); + + // Subnormal results are not flushed to zero by default. + // This operation should not be allowed to do constant propagation, hence the methods that explicitly disallow inlining. + Assert.AreNotEqual(GetDenormal() + GetZero(), 0f); + + bool methodCalled = false; + bool isFz = false; + + var managedMethod = () => + { + // Floating point math should not modify fp flags. + float test = 2f * 3.5f; + + if (test < 4f) + { + throw new System.Exception("Sanity check."); + } + + isFz = GetDenormal() + GetZero() == 0f; + + try + { + if (test >= 4f) + { + throw new System.Exception("Always throws."); + } + } + catch + { + // Exception handling should not modify fp flags. + + methodCalled = true; + } + }; + + var method = TranslatorTestMethods.GenerateFpFlagsPInvokeTest(); + + // This method sets flush-to-zero and then calls the managed method. + // Before and after setting the flags, it ensures subnormal addition works as expected. + // It returns a positive result if any tests fail, and 0 on success (or if the platform cannot change FP flags) + int result = method(Marshal.GetFunctionPointerForDelegate(managedMethod)); + + // Subnormal results are not flushed to zero by default, which we should have returned to exiting the method. + Assert.AreNotEqual(GetDenormal() + GetZero(), 0f); + + Assert.True(result == 0); + Assert.True(methodCalled); + Assert.True(isFz); + } + } +} From a947a45d8135bd0b6b1039904f48d92c0d455f3d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 10 Apr 2023 17:00:23 +0200 Subject: [PATCH 438/737] gtk: Fix a NRE when disposing OpenGL (#4648) --- Ryujinx/Ui/GLRenderer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs index 5804ed181..e3a9804ea 100644 --- a/Ryujinx/Ui/GLRenderer.cs +++ b/Ryujinx/Ui/GLRenderer.cs @@ -119,7 +119,7 @@ namespace Ryujinx.Ui } catch (Exception) { } - Device.DisposeGpu(); + Device?.DisposeGpu(); NpadManager.Dispose(); // Unbind context and destroy everything @@ -129,7 +129,7 @@ namespace Ryujinx.Ui } catch (Exception) { } - _openGLContext.Dispose(); + _openGLContext?.Dispose(); } } } From a4780ab33b9ca6b698917ded3ef6db6e6716cad1 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:04:31 +0100 Subject: [PATCH 439/737] Force activate parent window before dialog is shown (#4663) --- Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs index a49077f21..c87308cfb 100644 --- a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs +++ b/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs @@ -53,6 +53,8 @@ namespace Ryujinx.Ava.UI.Applet bool opened = false; + _parent.Activate(); + UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent, title, message, From 915d6d044cbf8c89935f14b8c7e085ad729f0e28 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Tue, 11 Apr 2023 07:32:31 +0100 Subject: [PATCH 440/737] OpenGL: Fix OBS/Overlays again by binding FB before present (#4668) This seems to have been removed by the Post-Processing PR, but it is required for the display in OBS to be the right way up and properly scaled. I've tested this with AA and FSR on MK8D and it seems to behave properly. Testing is welcome. --- Ryujinx.Graphics.OpenGL/Window.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index d6606f392..b37ec375e 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -226,6 +226,7 @@ namespace Ryujinx.Graphics.OpenGL // Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture. GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne); GL.Viewport(0, 0, _width, _height); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer); swapBuffersCallback(); From 9ef94c8292beda825fa76e05ad2e561c6d571c95 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Tue, 11 Apr 2023 07:55:04 +0100 Subject: [PATCH 441/737] ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext (#4661) * ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext Some games access these system registers several tens of thousands of times in a second from many different threads. While this isn't really crippling, it is a lot of wasted time spent in a reverse pinvoke transition. Example games are Pokemon Scarlet/Violet and BOTW. These games have a lot of different potential bottlenecks so it's unlikely you will see a consistent improvement, but it definitely disappears from the cpu profile. * Remove unreachable code. * Add ulong conversion for offsets * Nit --- ARMeilleure/Instructions/InstEmitSystem.cs | 43 +++++++++++++++--- ARMeilleure/Instructions/InstEmitSystem32.cs | 47 +++++++++++++++----- ARMeilleure/Instructions/NativeInterface.cs | 30 ------------- ARMeilleure/State/ExecutionContext.cs | 13 +++++- ARMeilleure/State/NativeContext.cs | 18 ++++++++ ARMeilleure/Translation/Delegates.cs | 6 --- ARMeilleure/Translation/PTC/Ptc.cs | 2 +- 7 files changed, 101 insertions(+), 58 deletions(-) diff --git a/ARMeilleure/Instructions/InstEmitSystem.cs b/ARMeilleure/Instructions/InstEmitSystem.cs index f668b83b6..f84829aa1 100644 --- a/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/ARMeilleure/Instructions/InstEmitSystem.cs @@ -33,8 +33,8 @@ namespace ARMeilleure.Instructions case 0b11_011_0100_0010_000: EmitGetNzcv(context); return; case 0b11_011_0100_0100_000: EmitGetFpcr(context); return; case 0b11_011_0100_0100_001: EmitGetFpsr(context); return; - case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break; - case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break; + case 0b11_011_1101_0000_010: EmitGetTpidrEl0(context); return; + case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return; case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break; case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break; @@ -49,19 +49,15 @@ namespace ARMeilleure.Instructions { OpCodeSystem op = (OpCodeSystem)context.CurrOp; - MethodInfo info; - switch (GetPackedId(op)) { case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; case 0b11_011_0100_0100_000: EmitSetFpcr(context); return; case 0b11_011_0100_0100_001: EmitSetFpsr(context); return; - case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break; + case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return; default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - - context.Call(info, GetIntOrZR(context, op.Rt)); } public static void Nop(ArmEmitterContext context) @@ -165,6 +161,28 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rt, fpsr); } + private static void EmitGetTpidrEl0(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset()))); + + SetIntOrZR(context, op.Rt, result); + } + + private static void EmitGetTpidrroEl0(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset()))); + + SetIntOrZR(context, op.Rt, result); + } + private static void EmitSetNzcv(ArmEmitterContext context) { OpCodeSystem op = (OpCodeSystem)context.CurrOp; @@ -215,5 +233,16 @@ namespace ARMeilleure.Instructions context.UpdateArmFpMode(); } + + private static void EmitSetTpidrEl0(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand value = GetIntOrZR(context, op.Rt); + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value); + } } } diff --git a/ARMeilleure/Instructions/InstEmitSystem32.cs b/ARMeilleure/Instructions/InstEmitSystem32.cs index 2f6cf19d6..f2732c998 100644 --- a/ARMeilleure/Instructions/InstEmitSystem32.cs +++ b/ARMeilleure/Instructions/InstEmitSystem32.cs @@ -23,8 +23,6 @@ namespace ARMeilleure.Instructions return; } - MethodInfo info; - switch (op.CRn) { case 13: // Process and Thread Info. @@ -36,14 +34,12 @@ namespace ARMeilleure.Instructions switch (op.Opc2) { case 2: - info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break; + EmitSetTpidrEl0(context); return; default: throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); } - break; - case 7: switch (op.CRm) // Cache and Memory barrier. { @@ -64,8 +60,6 @@ namespace ARMeilleure.Instructions default: throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } - - context.Call(info, GetIntA32(context, op.Rt)); } public static void Mrc(ArmEmitterContext context) @@ -79,7 +73,7 @@ namespace ARMeilleure.Instructions return; } - MethodInfo info; + Operand result; switch (op.CRn) { @@ -92,10 +86,10 @@ namespace ARMeilleure.Instructions switch (op.Opc2) { case 2: - info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break; + result = EmitGetTpidrEl0(context); break; case 3: - info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break; + result = EmitGetTpidrroEl0(context); break; default: throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); @@ -110,13 +104,13 @@ namespace ARMeilleure.Instructions if (op.Rt == RegisterAlias.Aarch32Pc) { // Special behavior: copy NZCV flags into APSR. - EmitSetNzcv(context, context.Call(info)); + EmitSetNzcv(context, result); return; } else { - SetIntA32(context, op.Rt, context.Call(info)); + SetIntA32(context, op.Rt, result); } } @@ -324,5 +318,34 @@ namespace ARMeilleure.Instructions context.UpdateArmFpMode(); } + + private static Operand EmitGetTpidrEl0(ArmEmitterContext context) + { + OpCode32System op = (OpCode32System)context.CurrOp; + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset()))); + } + + private static Operand EmitGetTpidrroEl0(ArmEmitterContext context) + { + OpCode32System op = (OpCode32System)context.CurrOp; + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset()))); + } + + private static void EmitSetTpidrEl0(ArmEmitterContext context) + { + OpCode32System op = (OpCode32System)context.CurrOp; + + Operand value = GetIntA32(context, op.Rt); + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), context.ZeroExtend32(OperandType.I64, value)); + } } } diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs index 57964cc8d..2c35387a6 100644 --- a/ARMeilleure/Instructions/NativeInterface.cs +++ b/ARMeilleure/Instructions/NativeInterface.cs @@ -72,26 +72,6 @@ namespace ARMeilleure.Instructions return (ulong)GetContext().DczidEl0; } - public static ulong GetTpidrEl0() - { - return (ulong)GetContext().TpidrEl0; - } - - public static uint GetTpidrEl032() - { - return (uint)GetContext().TpidrEl0; - } - - public static ulong GetTpidrroEl0() - { - return (ulong)GetContext().TpidrroEl0; - } - - public static uint GetTpidr32() - { - return (uint)GetContext().TpidrroEl0; - } - public static ulong GetCntfrqEl0() { return GetContext().CntfrqEl0; @@ -106,16 +86,6 @@ namespace ARMeilleure.Instructions { return GetContext().CntvctEl0; } - - public static void SetTpidrEl0(ulong value) - { - GetContext().TpidrEl0 = (long)value; - } - - public static void SetTpidrEl032(uint value) - { - GetContext().TpidrEl0 = (long)value; - } #endregion #region "Read" diff --git a/ARMeilleure/State/ExecutionContext.cs b/ARMeilleure/State/ExecutionContext.cs index 5d18e6ed8..859fb3a5d 100644 --- a/ARMeilleure/State/ExecutionContext.cs +++ b/ARMeilleure/State/ExecutionContext.cs @@ -27,8 +27,17 @@ namespace ARMeilleure.State // Since EL2 isn't implemented, CNTVOFF_EL2 = 0 public ulong CntvctEl0 => CntpctEl0; - public long TpidrEl0 { get; set; } - public long TpidrroEl0 { get; set; } + public long TpidrEl0 + { + get => _nativeContext.GetTpidrEl0(); + set => _nativeContext.SetTpidrEl0(value); + } + + public long TpidrroEl0 + { + get => _nativeContext.GetTpidrroEl0(); + set => _nativeContext.SetTpidrroEl0(value); + } public uint Pstate { diff --git a/ARMeilleure/State/NativeContext.cs b/ARMeilleure/State/NativeContext.cs index 89e875d12..3189bdd8a 100644 --- a/ARMeilleure/State/NativeContext.cs +++ b/ARMeilleure/State/NativeContext.cs @@ -13,6 +13,8 @@ namespace ARMeilleure.State public fixed ulong V[RegisterConsts.VecRegsCount * 2]; public fixed uint Flags[RegisterConsts.FlagsCount]; public fixed uint FpFlags[RegisterConsts.FpFlagsCount]; + public long TpidrEl0; + public long TpidrroEl0; public int Counter; public ulong DispatchAddress; public ulong ExclusiveAddress; @@ -168,6 +170,12 @@ namespace ARMeilleure.State } } + public long GetTpidrEl0() => GetStorage().TpidrEl0; + public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value; + + public long GetTpidrroEl0() => GetStorage().TpidrroEl0; + public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value; + public int GetCounter() => GetStorage().Counter; public void SetCounter(int value) => GetStorage().Counter = value; @@ -214,6 +222,16 @@ namespace ARMeilleure.State } } + public static int GetTpidrEl0Offset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0); + } + + public static int GetTpidrroEl0Offset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0); + } + public static int GetCounterOffset() { return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter); diff --git a/ARMeilleure/Translation/Delegates.cs b/ARMeilleure/Translation/Delegates.cs index 9d3fdc172..55f1e5145 100644 --- a/ARMeilleure/Translation/Delegates.cs +++ b/ARMeilleure/Translation/Delegates.cs @@ -105,17 +105,11 @@ namespace ARMeilleure.Translation SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only. - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only. SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only. SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess))); diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 5970c4ff9..ea4e715b5 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 4626; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 4661; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; From a64fee29dc6b8e523d61abb7e79ceaa95a558c6c Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Tue, 11 Apr 2023 08:23:41 +0100 Subject: [PATCH 442/737] Vulkan: add situational "Fast Flush" mode (#4667) * Flush in the middle of long command buffers. * Vulkan: add situational "Fast Flush" mode The AutoFlushCounter class was added to periodically flush Vulkan command buffers throughout a frame, which reduces latency to the GPU as commands are submitted and processed much sooner. This was done by allowing command buffers to flush when framebuffer attachments changed. However, some games have incredibly long render passes with a large number of draws, and really aggressive data access that forces GPU sync. The Vulkan backend could potentially end up building a single command buffer for 4-5ms if a pass has enough draws, such as in BOTW. In the scenario where sync is waited on immediately after submission, this would have to wait for the completion of a much longer command buffer than usual. The solution is to force command buffer submission periodically in a "fast flush" mode. This will end up splitting render passes, but it will only enable if sync is aggressive enough. This should improve performance in GPU limited scenarios, or in games that aggressively wait on synchronization. In some games, it may only kick in when res scaling. It won't trigger in games like SMO where sync is not an issue. Improves performance in Pokemon Scarlet/Violet (res scaled) and BOTW (in general). * Add conversions in milliseconds next to flush timers. --- Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs | 71 ++++++++++++++++++++- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 7 +- Ryujinx.Graphics.Vulkan/SyncManager.cs | 14 ++++ Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 14 ++-- 4 files changed, 96 insertions(+), 10 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs index 953316a6c..4e2a9d6bc 100644 --- a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs +++ b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.Common.Logging; +using System; using System.Diagnostics; using System.Linq; @@ -7,12 +8,26 @@ namespace Ryujinx.Graphics.Vulkan internal class AutoFlushCounter { // How often to flush on framebuffer change. - private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; + private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; // (1ms) + + // How often to flush on draw when fast flush mode is enabled. + private readonly static long DrawFlushTimer = Stopwatch.Frequency / 666; // (1.5ms) + + // Average wait time that triggers fast flush mode to be entered. + private readonly static long FastFlushEnterThreshold = Stopwatch.Frequency / 666; // (1.5ms) + + // Average wait time that triggers fast flush mode to be exited. + private readonly static long FastFlushExitThreshold = Stopwatch.Frequency / 10000; // (0.1ms) + + // Number of frames to average waiting times over. + private const int SyncWaitAverageCount = 20; private const int MinDrawCountForFlush = 10; private const int MinConsecutiveQueryForFlush = 10; private const int InitialQueryCountForFlush = 32; + private readonly VulkanRenderer _gd; + private long _lastFlush; private ulong _lastDrawCount; private bool _hasPendingQuery; @@ -23,6 +38,16 @@ namespace Ryujinx.Graphics.Vulkan private int _queryCountHistoryIndex; private int _remainingQueries; + private long[] _syncWaitHistory = new long[SyncWaitAverageCount]; + private int _syncWaitHistoryIndex; + + private bool _fastFlushMode; + + public AutoFlushCounter(VulkanRenderer gd) + { + _gd = gd; + } + public void RegisterFlush(ulong drawCount) { _lastFlush = Stopwatch.GetTimestamp(); @@ -69,6 +94,32 @@ namespace Ryujinx.Graphics.Vulkan return _hasPendingQuery; } + public bool ShouldFlushDraw(ulong drawCount) + { + if (_fastFlushMode) + { + long draws = (long)(drawCount - _lastDrawCount); + + if (draws < MinDrawCountForFlush) + { + if (draws == 0) + { + _lastFlush = Stopwatch.GetTimestamp(); + } + + return false; + } + + long flushTimeout = DrawFlushTimer; + + long now = Stopwatch.GetTimestamp(); + + return now > _lastFlush + flushTimeout; + } + + return false; + } + public bool ShouldFlushAttachmentChange(ulong drawCount) { _queryCount = 0; @@ -102,11 +153,27 @@ namespace Ryujinx.Graphics.Vulkan public void Present() { + // Query flush prediction. + _queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3; _remainingQueries = _queryCountHistory.Max() + 10; _queryCountHistory[_queryCountHistoryIndex] = 0; + + // Fast flush mode toggle. + + _syncWaitHistory[_syncWaitHistoryIndex] = _gd.SyncManager.GetAndResetWaitTicks(); + + _syncWaitHistoryIndex = (_syncWaitHistoryIndex + 1) % SyncWaitAverageCount; + + long averageWait = (long)_syncWaitHistory.Average(); + + if (_fastFlushMode ? averageWait < FastFlushExitThreshold : averageWait > FastFlushEnterThreshold) + { + _fastFlushMode = !_fastFlushMode; + Logger.Debug?.PrintMsg(LogClass.Gpu, $"Switched fast flush mode: ({_fastFlushMode})"); + } } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 2bec52930..c54d7980c 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Vulkan Gd = gd; Device = device; - AutoFlush = new AutoFlushCounter(); + AutoFlush = new AutoFlushCounter(gd); var pipelineCacheCreateInfo = new PipelineCacheCreateInfo() { @@ -1562,6 +1562,11 @@ namespace Ryujinx.Graphics.Vulkan private void RecreatePipelineIfNeeded(PipelineBindPoint pbp) { + if (AutoFlush.ShouldFlushDraw(DrawCount)) + { + Gd.FlushAllCommands(); + } + DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); // Commit changes to the support buffer before drawing. diff --git a/Ryujinx.Graphics.Vulkan/SyncManager.cs b/Ryujinx.Graphics.Vulkan/SyncManager.cs index c046dc3c7..432d224f0 100644 --- a/Ryujinx.Graphics.Vulkan/SyncManager.cs +++ b/Ryujinx.Graphics.Vulkan/SyncManager.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Silk.NET.Vulkan; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace Ryujinx.Graphics.Vulkan @@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; private List<SyncHandle> _handles; private ulong FlushId; + private long WaitTicks; public SyncManager(VulkanRenderer gd, Device device) { @@ -130,6 +132,8 @@ namespace Ryujinx.Graphics.Vulkan return; } + long beforeTicks = Stopwatch.GetTimestamp(); + if (result.NeedsFlush(FlushId)) { _gd.InterruptAction(() => @@ -142,12 +146,14 @@ namespace Ryujinx.Graphics.Vulkan } bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); + if (!signaled) { Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); } else { + WaitTicks += Stopwatch.GetTimestamp() - beforeTicks; result.Signalled = true; } } @@ -188,5 +194,13 @@ namespace Ryujinx.Graphics.Vulkan } } } + + public long GetAndResetWaitTicks() + { + long result = WaitTicks; + WaitTicks = 0; + + return result; + } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 193cdce31..92b453fb1 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan internal PipelineLayoutCache PipelineLayoutCache { get; private set; } internal BackgroundResources BackgroundResources { get; private set; } internal Action<Action> InterruptAction { get; private set; } + internal SyncManager SyncManager { get; private set; } internal BufferManager BufferManager { get; private set; } @@ -58,7 +59,6 @@ namespace Ryujinx.Graphics.Vulkan private VulkanDebugMessenger _debugMessenger; private Counters _counters; - private SyncManager _syncManager; private PipelineFull _pipeline; @@ -327,7 +327,7 @@ namespace Ryujinx.Graphics.Vulkan BufferManager = new BufferManager(this, _device); - _syncManager = new SyncManager(this, _device); + SyncManager = new SyncManager(this, _device); _pipeline = new PipelineFull(this, _device); _pipeline.Initialize(); @@ -436,7 +436,7 @@ namespace Ryujinx.Graphics.Vulkan internal void RegisterFlush() { - _syncManager.RegisterFlush(); + SyncManager.RegisterFlush(); } public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) @@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Vulkan public void PreFrame() { - _syncManager.Cleanup(); + SyncManager.Cleanup(); } public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) @@ -736,7 +736,7 @@ namespace Ryujinx.Graphics.Vulkan public void CreateSync(ulong id, bool strict) { - _syncManager.Create(id, strict); + SyncManager.Create(id, strict); } public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info) @@ -746,12 +746,12 @@ namespace Ryujinx.Graphics.Vulkan public void WaitSync(ulong id) { - _syncManager.Wait(id); + SyncManager.Wait(id); } public ulong GetCurrentSync() { - return _syncManager.GetCurrent(); + return SyncManager.GetCurrent(); } public void SetInterruptAction(Action<Action> interruptAction) From e187a8870a6f19ac0a85b08aece3c1a1e196e379 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 12 Apr 2023 03:09:47 +0200 Subject: [PATCH 443/737] HLE: Deal with empty title names properly (#4643) * hle: Deal with empty titleNames in some languages * gui: Fix displaying the wrong title name * Remove unnecessary bounds check * Fix a NRE when getting the version string * Restore empty string logic --- Ryujinx.Ava/AppHost.cs | 12 +++---- .../Loaders/Processes/ProcessResult.cs | 36 +++++++------------ Ryujinx/Ui/RendererWidgetBase.cs | 12 +++---- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 3cdb3906f..ae9e8e53d 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -321,17 +321,15 @@ namespace Ryujinx.Ava _viewModel.IsGameRunning = true; var activeProcess = Device.Processes.ActiveApplication; - var nacp = activeProcess.ApplicationControlProperties; - int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage; - string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}"; - string titleVersionSection = string.IsNullOrWhiteSpace(nacp.DisplayVersionString.ToString()) ? string.Empty : $" v{nacp.DisplayVersionString.ToString()}"; - string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})"; - string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; + string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; + string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}"; + string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; + string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; Dispatcher.UIThread.InvokeAsync(() => { - _viewModel.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; + _viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); _viewModel.SetUIProgressHandlers(Device); diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index b9596c8fd..2801e4e08 100644 --- a/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -5,6 +5,7 @@ using Ryujinx.Cpu; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.Horizon.Common; +using System.Linq; namespace Ryujinx.HLE.Loaders.Processes { @@ -21,8 +22,9 @@ namespace Ryujinx.HLE.Loaders.Processes public readonly ApplicationControlProperty ApplicationControlProperties; public readonly ulong ProcessId; - public string Name; - public ulong ProgramId; + public readonly string Name; + public readonly string DisplayVersion; + public readonly ulong ProgramId; public readonly string ProgramIdText; public readonly bool Is64Bit; public readonly bool DiskCacheEnabled; @@ -52,20 +54,17 @@ namespace Ryujinx.HLE.Loaders.Processes { ulong programId = metaLoader.GetProgramId(); - if (ApplicationControlProperties.Title.ItemsRo.Length > 0) - { - var langIndex = ApplicationControlProperties.Title.ItemsRo.Length > (int)titleLanguage ? (int)titleLanguage : 0; + Name = ApplicationControlProperties.Title[(int)titleLanguage].NameString.ToString(); - Name = ApplicationControlProperties.Title[langIndex].NameString.ToString(); - } - else + if (string.IsNullOrWhiteSpace(Name)) { - Name = metaLoader.GetProgramName(); + Name = ApplicationControlProperties.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); } - ProgramId = programId; - ProgramIdText = $"{programId:x16}"; - Is64Bit = metaLoader.IsProgram64Bit(); + DisplayVersion = ApplicationControlProperties.DisplayVersionString.ToString(); + ProgramId = programId; + ProgramIdText = $"{programId:x16}"; + Is64Bit = metaLoader.IsProgram64Bit(); } DiskCacheEnabled = diskCacheEnabled; @@ -85,20 +84,11 @@ namespace Ryujinx.HLE.Loaders.Processes } // TODO: LibHac npdm currently doesn't support version field. - string version; - - if (ProgramId > 0x0100000000007FFF) - { - version = ApplicationControlProperties.DisplayVersionString.ToString(); - } - else - { - version = device.System.ContentManager.GetCurrentFirmwareVersion().VersionString; - } + string version = ProgramId > 0x0100000000007FFF ? DisplayVersion : device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?"; Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]"); return true; } } -} \ No newline at end of file +} diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 7cb5b3275..65afa6e47 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -496,15 +496,13 @@ namespace Ryujinx.Ui parent.Present(); var activeProcess = Device.Processes.ActiveApplication; - var nacp = activeProcess.ApplicationControlProperties; - int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage; - string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}"; - string titleVersionSection = string.IsNullOrWhiteSpace(nacp.DisplayVersionString.ToString()) ? string.Empty : $" v{nacp.DisplayVersionString.ToString()}"; - string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})"; - string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; + string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; + string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}"; + string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; + string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; - parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; + parent.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); Thread renderLoopThread = new Thread(Render) From 4c3f09644a033dbf70258c4c0e5a848263b16bbd Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Wed, 12 Apr 2023 20:18:40 +0100 Subject: [PATCH 444/737] Move swkbd message null check into constructor (#4671) --- Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index 78c62a9a7..fb689ec23 100644 --- a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -23,10 +23,11 @@ namespace Ryujinx.Ava.UI.Controls private ContentDialog _host; - public SwkbdAppletDialog(string mainText, string secondaryText, string placeholder) + public SwkbdAppletDialog(string mainText, string secondaryText, string placeholder, string message) { MainText = mainText; SecondaryText = secondaryText; + Message = message ?? ""; DataContext = this; _placeholder = placeholder; InitializeComponent(); @@ -54,10 +55,7 @@ namespace Ryujinx.Ava.UI.Controls UserResult result = UserResult.Cancel; - SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText) - { - Message = args.InitialText ?? "" - }; + SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText); string input = string.Empty; From 11ecff2ff04633d261b9a43db792f6438df63f40 Mon Sep 17 00:00:00 2001 From: Alex Barney <thealexbarney@gmail.com> Date: Fri, 14 Apr 2023 16:00:34 -0700 Subject: [PATCH 445/737] Rename Hipc to Cmif where appropriate (#3880) --- .../ServiceNotImplementedException.cs | 2 +- Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 2 +- Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs | 12 +- .../AccountService/IManagerForApplication.cs | 14 +- .../IManagerForSystemService.cs | 8 +- .../Account/Acc/AccountService/IProfile.cs | 8 +- .../Acc/AccountService/IProfileEditor.cs | 12 +- .../Acc/IAccountServiceForAdministrator.cs | 22 +- .../Acc/IAccountServiceForApplication.cs | 40 ++-- .../Acc/IAccountServiceForSystemService.cs | 20 +- .../HOS/Services/Account/Acc/IAsyncContext.cs | 8 +- .../IAsyncNetworkServiceLicenseKindContext.cs | 2 +- .../ILibraryAppletProxy.cs | 20 +- .../ISystemAppletProxy.cs | 20 +- .../ILibraryAppletAccessor.cs | 28 +-- .../ILibraryAppletSelfAccessor.cs | 6 +- .../IProcessWindingController.cs | 2 +- .../SystemAppletProxy/IAudioController.cs | 10 +- .../SystemAppletProxy/ICommonStateGetter.cs | 36 +-- .../SystemAppletProxy/IDisplayController.cs | 10 +- .../SystemAppletProxy/IHomeMenuFunctions.cs | 4 +- .../ILibraryAppletCreator.cs | 8 +- .../SystemAppletProxy/ISelfController.cs | 64 ++--- .../SystemAppletProxy/IWindowController.cs | 4 +- .../IAllSystemAppletProxiesService.cs | 6 +- .../HOS/Services/Am/AppletAE/IStorage.cs | 2 +- .../Services/Am/AppletAE/IStorageAccessor.cs | 6 +- .../ApplicationProxy/IApplicationFunctions.cs | 72 +++--- .../IApplicationProxy.cs | 16 +- .../Am/AppletOE/IApplicationProxyService.cs | 2 +- Ryujinx.HLE/HOS/Services/Apm/IManager.cs | 6 +- .../HOS/Services/Apm/IManagerPrivileged.cs | 2 +- Ryujinx.HLE/HOS/Services/Apm/ISession.cs | 6 +- .../HOS/Services/Apm/ISystemManager.cs | 6 +- .../Services/Audio/AudioIn/AudioInServer.cs | 30 +-- .../Services/Audio/AudioInManagerServer.cs | 12 +- .../Services/Audio/AudioOut/AudioOutServer.cs | 28 +-- .../Services/Audio/AudioOutManagerServer.cs | 8 +- .../Audio/AudioRenderer/AudioDeviceServer.cs | 28 +-- .../AudioRenderer/AudioRendererServer.cs | 30 +-- .../Audio/AudioRendererManagerServer.cs | 8 +- .../IHardwareOpusDecoder.cs | 16 +- .../Audio/IHardwareOpusDecoderManager.cs | 16 +- .../HOS/Services/Bcat/IServiceCreator.cs | 6 +- .../Bcat/ServiceCreator/IBcatService.cs | 2 +- .../IDeliveryCacheDirectoryService.cs | 6 +- .../IDeliveryCacheFileService.cs | 8 +- .../IDeliveryCacheProgressService.cs | 4 +- .../IDeliveryCacheStorageService.cs | 6 +- .../Services/Bluetooth/IBluetoothDriver.cs | 2 +- .../HOS/Services/Bluetooth/IBluetoothUser.cs | 2 +- .../BluetoothManager/BtmUser/IBtmUserCore.cs | 8 +- .../HOS/Services/BluetoothManager/IBtmUser.cs | 2 +- .../Services/Caps/IAlbumApplicationService.cs | 6 +- .../HOS/Services/Caps/IAlbumControlService.cs | 2 +- .../Caps/IScreenShotApplicationService.cs | 8 +- ...pcAttribute.cs => CommandCmifAttribute.cs} | 4 +- Ryujinx.HLE/HOS/Services/Fatal/IService.cs | 6 +- .../HOS/Services/Friend/IServiceCreator.cs | 6 +- .../Friend/ServiceCreator/IFriendService.cs | 26 +- .../ServiceCreator/INotificationService.cs | 6 +- .../Services/Fs/FileSystemProxy/IDirectory.cs | 4 +- .../HOS/Services/Fs/FileSystemProxy/IFile.cs | 10 +- .../Fs/FileSystemProxy/IFileSystem.cs | 30 +-- .../Services/Fs/FileSystemProxy/IStorage.cs | 4 +- .../HOS/Services/Fs/IDeviceOperator.cs | 6 +- .../HOS/Services/Fs/IFileSystemProxy.cs | 194 +++++++-------- .../HOS/Services/Fs/IMultiCommitManager.cs | 4 +- .../HOS/Services/Fs/ISaveDataInfoReader.cs | 2 +- .../HidServer/IActiveVibrationDeviceList.cs | 2 +- .../Services/Hid/HidServer/IAppletResource.cs | 2 +- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 226 +++++++++--------- .../HOS/Services/Hid/IHidSystemServer.cs | 8 +- .../HOS/Services/Hid/Irs/IIrSensorServer.cs | 26 +- Ryujinx.HLE/HOS/Services/IpcService.cs | 12 +- .../HOS/Services/Lbl/ILblController.cs | 18 +- .../HOS/Services/Ldn/IUserServiceCreator.cs | 2 +- .../IUserLocalCommunicationService.cs | 10 +- .../HOS/Services/Mii/IImageDatabaseService.cs | 4 +- .../HOS/Services/Mii/IStaticService.cs | 2 +- .../Mii/StaticService/IDatabaseService.cs | 52 ++-- Ryujinx.HLE/HOS/Services/Mm/IRequest.cs | 16 +- .../Services/Mnpp/IServiceForApplication.cs | 4 +- .../Ncm/Lr/ILocationResolverManager.cs | 2 +- .../ILocationResolver.cs | 32 +-- .../HOS/Services/Nfc/ISystemManager.cs | 2 +- Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs | 2 +- .../HOS/Services/Nfc/NfcManager/INfc.cs | 16 +- .../HOS/Services/Nfc/Nfp/IDebugManager.cs | 2 +- .../HOS/Services/Nfc/Nfp/ISystemManager.cs | 2 +- .../HOS/Services/Nfc/Nfp/IUserManager.cs | 2 +- .../HOS/Services/Nfc/Nfp/NfpManager/INfp.cs | 52 ++-- Ryujinx.HLE/HOS/Services/Ngct/IService.cs | 4 +- .../Ngct/IServiceWithManagementApi.cs | 4 +- .../HOS/Services/Nifm/IStaticService.cs | 4 +- .../Nifm/StaticService/IGeneralService.cs | 14 +- .../Services/Nifm/StaticService/IRequest.cs | 14 +- .../Services/Nim/IShopServiceAccessServer.cs | 2 +- .../Nim/IShopServiceAccessServerInterface.cs | 4 +- .../HOS/Services/Nim/IShopServiceAccessor.cs | 2 +- .../HOS/Services/Nim/Ntc/IStaticService.cs | 2 +- .../IEnsureNetworkClockAvailabilityService.cs | 8 +- .../Services/Ns/Aoc/IAddOnContentManager.cs | 34 +-- .../Services/Ns/Aoc/IPurchaseEventManager.cs | 6 +- .../Ns/IApplicationManagerInterface.cs | 2 +- ...ReadOnlyApplicationControlDataInterface.cs | 2 +- .../Services/Ns/IServiceGetterInterface.cs | 4 +- Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 28 +-- .../Olsc/IOlscServiceForApplication.cs | 6 +- .../Pctl/IParentalControlServiceFactory.cs | 4 +- .../IParentalControlService.cs | 22 +- .../HOS/Services/Pcv/Bpc/IRtcManager.cs | 2 +- .../Clkrst/ClkrstManager/IClkrstSession.cs | 4 +- .../HOS/Services/Pcv/Clkrst/IClkrstManager.cs | 6 +- .../HOS/Services/Pm/IDebugMonitorInterface.cs | 4 +- .../HOS/Services/Pm/IInformationInterface.cs | 5 +- .../HOS/Services/Pm/IShellInterface.cs | 2 +- .../HOS/Services/Ptm/Psm/IPsmServer.cs | 6 +- .../HOS/Services/Ptm/Psm/IPsmSession.cs | 10 +- .../HOS/Services/Ptm/Ts/IMeasurementServer.cs | 4 +- Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs | 12 +- .../HOS/Services/Sdb/Pdm/IQueryService.cs | 4 +- .../HOS/Services/Sdb/Pl/ISharedFontManager.cs | 14 +- Ryujinx.HLE/HOS/Services/ServerBase.cs | 18 +- .../HOS/Services/Settings/ISettingsServer.cs | 22 +- .../Settings/ISystemSettingsServer.cs | 22 +- Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 10 +- .../HOS/Services/Sockets/Bsd/IClient.cs | 62 ++--- .../HOS/Services/Sockets/Nsd/IManager.cs | 50 ++-- .../Services/Sockets/Sfdnsres/IResolver.cs | 28 +-- .../HOS/Services/Spl/IGeneralInterface.cs | 2 +- .../HOS/Services/Spl/IRandomInterface.cs | 2 +- Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs | 8 +- .../Services/Ssl/SslService/ISslConnection.cs | 56 ++--- .../Services/Ssl/SslService/ISslContext.cs | 8 +- .../SurfaceFlinger/IHOSBinderDriver.cs | 8 +- .../Services/Time/IStaticServiceForGlue.cs | 38 +-- .../HOS/Services/Time/IStaticServiceForPsc.cs | 38 +-- .../HOS/Services/Time/ITimeServiceManager.cs | 36 +-- .../Time/StaticService/ISteadyClock.cs | 16 +- .../Time/StaticService/ISystemClock.cs | 10 +- .../StaticService/ITimeZoneServiceForGlue.cs | 18 +- .../StaticService/ITimeZoneServiceForPsc.cs | 28 +-- .../Services/Vi/IApplicationRootService.cs | 2 +- .../HOS/Services/Vi/IManagerRootService.cs | 2 +- .../HOS/Services/Vi/ISystemRootService.cs | 2 +- .../IManagerDisplayService.cs | 12 +- .../ISystemDisplayService.cs | 8 +- .../RootService/IApplicationDisplayService.cs | 38 +-- 149 files changed, 1152 insertions(+), 1153 deletions(-) rename Ryujinx.HLE/HOS/Services/{CommandHIpcAttribute.cs => CommandCmifAttribute.cs} (63%) diff --git a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs index 3dbf48d82..40b19cc8e 100644 --- a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs +++ b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.Exceptions if (callingType != null && callingMethod != null) { // If the type is past 0xF, we are using TIPC - var ipcCommands = Request.Type > IpcMessageType.TipcCloseSession ? Service.TipcCommands : Service.HipcCommands; + var ipcCommands = Request.Type > IpcMessageType.TipcCloseSession ? Service.TipcCommands : Service.CmifCommands; // Find the handler for the method called var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod); diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 4e8f2fbfd..394bf888e 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Ipc long recvListPos = reader.BaseStream.Position + rawDataSize; - // only HIPC have the padding requirements. + // Only CMIF has the padding requirements. if (Type < IpcMessageType.TipcCloseSession) { long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr); diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs index dd3c56569..1c8622485 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Ipc { enum IpcMessageType { - HipcResponse = 0, - HipcCloseSession = 2, - HipcRequest = 4, - HipcControl = 5, - HipcRequestWithContext = 6, - HipcControlWithContext = 7, + CmifResponse = 0, + CmifCloseSession = 2, + CmifRequest = 4, + CmifControl = 5, + CmifRequestWithContext = 6, + CmifControlWithContext = 7, TipcCloseSession = 0xF } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs index 68ed3f50a..9c058cb59 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs @@ -9,21 +9,21 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService _managerServer = new ManagerServer(userId); } - [CommandHipc(0)] + [CommandCmif(0)] // CheckAvailability() public ResultCode CheckAvailability(ServiceCtx context) { return _managerServer.CheckAvailability(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetAccountId() -> nn::account::NetworkServiceAccountId public ResultCode GetAccountId(ServiceCtx context) { return _managerServer.GetAccountId(context); } - [CommandHipc(2)] + [CommandCmif(2)] // EnsureIdTokenCacheAsync() -> object<nn::account::detail::IAsyncContext> public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context) { @@ -37,28 +37,28 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return resultCode; } - [CommandHipc(3)] + [CommandCmif(3)] // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer<bytes, 6>) public ResultCode LoadIdTokenCache(ServiceCtx context) { return _managerServer.LoadIdTokenCache(context); } - [CommandHipc(130)] + [CommandCmif(130)] // GetNintendoAccountUserResourceCacheForApplication() -> (nn::account::NintendoAccountId, nn::account::nas::NasUserBaseForApplication, buffer<bytes, 6>) public ResultCode GetNintendoAccountUserResourceCacheForApplication(ServiceCtx context) { return _managerServer.GetNintendoAccountUserResourceCacheForApplication(context); } - [CommandHipc(160)] // 5.0.0+ + [CommandCmif(160)] // 5.0.0+ // StoreOpenContext() public ResultCode StoreOpenContext(ServiceCtx context) { return _managerServer.StoreOpenContext(context); } - [CommandHipc(170)] // 6.0.0+ + [CommandCmif(170)] // 6.0.0+ // LoadNetworkServiceLicenseKindAsync() -> object<nn::account::detail::IAsyncNetworkServiceLicenseKindContext> public ResultCode LoadNetworkServiceLicenseKindAsync(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs index d054b80b8..ecd516876 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs @@ -9,21 +9,21 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService _managerServer = new ManagerServer(userId); } - [CommandHipc(0)] + [CommandCmif(0)] // CheckAvailability() public ResultCode CheckAvailability(ServiceCtx context) { return _managerServer.CheckAvailability(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetAccountId() -> nn::account::NetworkServiceAccountId public ResultCode GetAccountId(ServiceCtx context) { return _managerServer.GetAccountId(context); } - [CommandHipc(2)] + [CommandCmif(2)] // EnsureIdTokenCacheAsync() -> object<nn::account::detail::IAsyncContext> public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context) { @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return resultCode; } - [CommandHipc(3)] + [CommandCmif(3)] // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer<bytes, 6>) public ResultCode LoadIdTokenCache(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs index 8595f54a3..14911dfb1 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs @@ -9,28 +9,28 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService _profileServer = new ProfileServer(profile); } - [CommandHipc(0)] + [CommandCmif(0)] // Get() -> (nn::account::profile::ProfileBase, buffer<nn::account::profile::UserData, 0x1a>) public ResultCode Get(ServiceCtx context) { return _profileServer.Get(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetBase() -> nn::account::profile::ProfileBase public ResultCode GetBase(ServiceCtx context) { return _profileServer.GetBase(context); } - [CommandHipc(10)] + [CommandCmif(10)] // GetImageSize() -> u32 public ResultCode GetImageSize(ServiceCtx context) { return _profileServer.GetImageSize(context); } - [CommandHipc(11)] + [CommandCmif(11)] // LoadImage() -> (u32, buffer<bytes, 6>) public ResultCode LoadImage(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs index 9ec467508..64b6070f2 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs @@ -9,42 +9,42 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService _profileServer = new ProfileServer(profile); } - [CommandHipc(0)] + [CommandCmif(0)] // Get() -> (nn::account::profile::ProfileBase, buffer<nn::account::profile::UserData, 0x1a>) public ResultCode Get(ServiceCtx context) { return _profileServer.Get(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetBase() -> nn::account::profile::ProfileBase public ResultCode GetBase(ServiceCtx context) { return _profileServer.GetBase(context); } - [CommandHipc(10)] + [CommandCmif(10)] // GetImageSize() -> u32 public ResultCode GetImageSize(ServiceCtx context) { return _profileServer.GetImageSize(context); } - [CommandHipc(11)] + [CommandCmif(11)] // LoadImage() -> (u32, buffer<bytes, 6>) public ResultCode LoadImage(ServiceCtx context) { return _profileServer.LoadImage(context); } - [CommandHipc(100)] + [CommandCmif(100)] // Store(nn::account::profile::ProfileBase, buffer<nn::account::profile::UserData, 0x19>) public ResultCode Store(ServiceCtx context) { return _profileServer.Store(context); } - [CommandHipc(101)] + [CommandCmif(101)] // StoreWithImage(nn::account::profile::ProfileBase, buffer<nn::account::profile::UserData, 0x19>, buffer<bytes, 5>) public ResultCode StoreWithImage(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs index cb73dcfe1..6a457f042 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs @@ -14,42 +14,42 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc _applicationServiceServer = new ApplicationServiceServer(serviceFlag); } - [CommandHipc(0)] + [CommandCmif(0)] // GetUserCount() -> i32 public ResultCode GetUserCount(ServiceCtx context) { return _applicationServiceServer.GetUserCountImpl(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetUserExistence(nn::account::Uid) -> bool public ResultCode GetUserExistence(ServiceCtx context) { return _applicationServiceServer.GetUserExistenceImpl(context); } - [CommandHipc(2)] + [CommandCmif(2)] // ListAllUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListAllUsers(ServiceCtx context) { return _applicationServiceServer.ListAllUsers(context); } - [CommandHipc(3)] + [CommandCmif(3)] // ListOpenUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListOpenUsers(ServiceCtx context) { return _applicationServiceServer.ListOpenUsers(context); } - [CommandHipc(4)] + [CommandCmif(4)] // GetLastOpenedUser() -> nn::account::Uid public ResultCode GetLastOpenedUser(ServiceCtx context) { return _applicationServiceServer.GetLastOpenedUser(context); } - [CommandHipc(5)] + [CommandCmif(5)] // GetProfile(nn::account::Uid) -> object<nn::account::profile::IProfile> public ResultCode GetProfile(ServiceCtx context) { @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return resultCode; } - [CommandHipc(50)] + [CommandCmif(50)] // IsUserRegistrationRequestPermitted(pid) -> bool public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) { @@ -72,14 +72,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _applicationServiceServer.IsUserRegistrationRequestPermitted(context); } - [CommandHipc(51)] + [CommandCmif(51)] // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) { return _applicationServiceServer.TrySelectUserWithoutInteraction(context); } - [CommandHipc(102)] + [CommandCmif(102)] // GetBaasAccountManagerForSystemService(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication> public ResultCode GetBaasAccountManagerForSystemService(ServiceCtx context) { @@ -98,14 +98,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(140)] // 6.0.0+ + [CommandCmif(140)] // 6.0.0+ // ListQualifiedUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListQualifiedUsers(ServiceCtx context) { return _applicationServiceServer.ListQualifiedUsers(context); } - [CommandHipc(205)] + [CommandCmif(205)] // GetProfileEditor(nn::account::Uid) -> object<nn::account::profile::IProfileEditor> public ResultCode GetProfileEditor(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 413bedced..8ec83e5c1 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -14,42 +14,42 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc _applicationServiceServer = new ApplicationServiceServer(serviceFlag); } - [CommandHipc(0)] + [CommandCmif(0)] // GetUserCount() -> i32 public ResultCode GetUserCount(ServiceCtx context) { return _applicationServiceServer.GetUserCountImpl(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetUserExistence(nn::account::Uid) -> bool public ResultCode GetUserExistence(ServiceCtx context) { return _applicationServiceServer.GetUserExistenceImpl(context); } - [CommandHipc(2)] + [CommandCmif(2)] // ListAllUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListAllUsers(ServiceCtx context) { return _applicationServiceServer.ListAllUsers(context); } - [CommandHipc(3)] + [CommandCmif(3)] // ListOpenUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListOpenUsers(ServiceCtx context) { return _applicationServiceServer.ListOpenUsers(context); } - [CommandHipc(4)] + [CommandCmif(4)] // GetLastOpenedUser() -> nn::account::Uid public ResultCode GetLastOpenedUser(ServiceCtx context) { return _applicationServiceServer.GetLastOpenedUser(context); } - [CommandHipc(5)] + [CommandCmif(5)] // GetProfile(nn::account::Uid) -> object<nn::account::profile::IProfile> public ResultCode GetProfile(ServiceCtx context) { @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return resultCode; } - [CommandHipc(50)] + [CommandCmif(50)] // IsUserRegistrationRequestPermitted(pid) -> bool public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) { @@ -71,16 +71,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _applicationServiceServer.IsUserRegistrationRequestPermitted(context); } - [CommandHipc(51)] + [CommandCmif(51)] // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) { return _applicationServiceServer.TrySelectUserWithoutInteraction(context); } - [CommandHipc(100)] - [CommandHipc(140)] // 6.0.0+ - [CommandHipc(160)] // 13.0.0+ + [CommandCmif(100)] + [CommandCmif(140)] // 6.0.0+ + [CommandCmif(160)] // 13.0.0+ // InitializeApplicationInfo(u64 pid_placeholder, pid) public ResultCode InitializeApplicationInfo(ServiceCtx context) { @@ -105,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(101)] + [CommandCmif(101)] // GetBaasAccountManagerForApplication(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication> public ResultCode GetBaasAccountManagerForApplication(ServiceCtx context) { @@ -124,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(103)] // 4.0.0+ + [CommandCmif(103)] // 4.0.0+ // CheckNetworkServiceAvailabilityAsync() -> object<nn::account::detail::IAsyncContext> public ResultCode CheckNetworkServiceAvailabilityAsync(ServiceCtx context) { @@ -138,21 +138,21 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return resultCode; } - [CommandHipc(110)] + [CommandCmif(110)] // StoreSaveDataThumbnail(nn::account::Uid, buffer<bytes, 5>) public ResultCode StoreSaveDataThumbnail(ServiceCtx context) { return _applicationServiceServer.StoreSaveDataThumbnail(context); } - [CommandHipc(111)] + [CommandCmif(111)] // ClearSaveDataThumbnail(nn::account::Uid) public ResultCode ClearSaveDataThumbnail(ServiceCtx context) { return _applicationServiceServer.ClearSaveDataThumbnail(context); } - [CommandHipc(130)] // 5.0.0+ + [CommandCmif(130)] // 5.0.0+ // LoadOpenContext(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication> public ResultCode LoadOpenContext(ServiceCtx context) { @@ -168,22 +168,22 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(60)] // 5.0.0-5.1.0 - [CommandHipc(131)] // 6.0.0+ + [CommandCmif(60)] // 5.0.0-5.1.0 + [CommandCmif(131)] // 6.0.0+ // ListOpenContextStoredUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListOpenContextStoredUsers(ServiceCtx context) { return _applicationServiceServer.ListOpenContextStoredUsers(context); } - [CommandHipc(141)] // 6.0.0+ + [CommandCmif(141)] // 6.0.0+ // ListQualifiedUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListQualifiedUsers(ServiceCtx context) { return _applicationServiceServer.ListQualifiedUsers(context); } - [CommandHipc(150)] // 6.0.0+ + [CommandCmif(150)] // 6.0.0+ // IsUserAccountSwitchLocked() -> bool public ResultCode IsUserAccountSwitchLocked(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs index 934c06fee..3b5f3b03e 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs @@ -13,42 +13,42 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc _applicationServiceServer = new ApplicationServiceServer(serviceFlag); } - [CommandHipc(0)] + [CommandCmif(0)] // GetUserCount() -> i32 public ResultCode GetUserCount(ServiceCtx context) { return _applicationServiceServer.GetUserCountImpl(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetUserExistence(nn::account::Uid) -> bool public ResultCode GetUserExistence(ServiceCtx context) { return _applicationServiceServer.GetUserExistenceImpl(context); } - [CommandHipc(2)] + [CommandCmif(2)] // ListAllUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListAllUsers(ServiceCtx context) { return _applicationServiceServer.ListAllUsers(context); } - [CommandHipc(3)] + [CommandCmif(3)] // ListOpenUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListOpenUsers(ServiceCtx context) { return _applicationServiceServer.ListOpenUsers(context); } - [CommandHipc(4)] + [CommandCmif(4)] // GetLastOpenedUser() -> nn::account::Uid public ResultCode GetLastOpenedUser(ServiceCtx context) { return _applicationServiceServer.GetLastOpenedUser(context); } - [CommandHipc(5)] + [CommandCmif(5)] // GetProfile(nn::account::Uid) -> object<nn::account::profile::IProfile> public ResultCode GetProfile(ServiceCtx context) { @@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return resultCode; } - [CommandHipc(50)] + [CommandCmif(50)] // IsUserRegistrationRequestPermitted(pid) -> bool public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) { @@ -71,14 +71,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _applicationServiceServer.IsUserRegistrationRequestPermitted(context); } - [CommandHipc(51)] + [CommandCmif(51)] // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) { return _applicationServiceServer.TrySelectUserWithoutInteraction(context); } - [CommandHipc(102)] + [CommandCmif(102)] // GetBaasAccountManagerForSystemService(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication> public ResultCode GetBaasAccountManagerForSystemService(ServiceCtx context) { @@ -97,7 +97,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(140)] // 6.0.0+ + [CommandCmif(140)] // 6.0.0+ // ListQualifiedUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListQualifiedUsers(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs index 9a12e7018..c9af0727f 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc AsyncExecution = asyncExecution; } - [CommandHipc(0)] + [CommandCmif(0)] // GetSystemEvent() -> handle<copy> public ResultCode GetSystemEvent(ServiceCtx context) { @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // Cancel() public ResultCode Cancel(ServiceCtx context) { @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // HasDone() -> b8 public ResultCode HasDone(ServiceCtx context) { @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetResult() public ResultCode GetResult(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs index e62663e6f..1fa5cf2a1 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc _serviceLicenseKind = serviceLicenseKind; } - [CommandHipc(100)] + [CommandCmif(100)] // GetNetworkServiceLicenseKind() -> nn::account::NetworkServiceLicenseKind public ResultCode GetNetworkServiceLicenseKind(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs index 08b8fc7ea..bf86aaaa9 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService _pid = pid; } - [CommandHipc(0)] + [CommandCmif(0)] // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter> public ResultCode GetCommonStateGetter(ServiceCtx context) { @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetSelfController() -> object<nn::am::service::ISelfController> public ResultCode GetSelfController(ServiceCtx context) { @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetWindowController() -> object<nn::am::service::IWindowController> public ResultCode GetWindowController(ServiceCtx context) { @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetAudioController() -> object<nn::am::service::IAudioController> public ResultCode GetAudioController(ServiceCtx context) { @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetDisplayController() -> object<nn::am::service::IDisplayController> public ResultCode GetDisplayController(ServiceCtx context) { @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // GetProcessWindingController() -> object<nn::am::service::IProcessWindingController> public ResultCode GetProcessWindingController(ServiceCtx context) { @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator> public ResultCode GetLibraryAppletCreator(ServiceCtx context) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(20)] + [CommandCmif(20)] // OpenLibraryAppletSelfAccessor() -> object<nn::am::service::ILibraryAppletSelfAccessor> public ResultCode OpenLibraryAppletSelfAccessor(ServiceCtx context) { @@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(21)] + [CommandCmif(21)] // GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions> public ResultCode GetAppletCommonFunctions(ServiceCtx context) { @@ -93,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(1000)] + [CommandCmif(1000)] // GetDebugFunctions() -> object<nn::am::service::IDebugFunctions> public ResultCode GetDebugFunctions(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs index 61a7a46d3..dc26d80c1 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService _pid = pid; } - [CommandHipc(0)] + [CommandCmif(0)] // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter> public ResultCode GetCommonStateGetter(ServiceCtx context) { @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetSelfController() -> object<nn::am::service::ISelfController> public ResultCode GetSelfController(ServiceCtx context) { @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetWindowController() -> object<nn::am::service::IWindowController> public ResultCode GetWindowController(ServiceCtx context) { @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetAudioController() -> object<nn::am::service::IAudioController> public ResultCode GetAudioController(ServiceCtx context) { @@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetDisplayController() -> object<nn::am::service::IDisplayController> public ResultCode GetDisplayController(ServiceCtx context) { @@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator> public ResultCode GetLibraryAppletCreator(ServiceCtx context) { @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(20)] + [CommandCmif(20)] // GetHomeMenuFunctions() -> object<nn::am::service::IHomeMenuFunctions> public ResultCode GetHomeMenuFunctions(ServiceCtx context) { @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(21)] + [CommandCmif(21)] // GetGlobalStateController() -> object<nn::am::service::IGlobalStateController> public ResultCode GetGlobalStateController(ServiceCtx context) { @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(22)] + [CommandCmif(22)] // GetApplicationCreator() -> object<nn::am::service::IApplicationCreator> public ResultCode GetApplicationCreator(ServiceCtx context) { @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - [CommandHipc(1000)] + [CommandCmif(1000)] // GetDebugFunctions() -> object<nn::am::service::IDebugFunctions> public ResultCode GetDebugFunctions(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 4ed502e0e..0057eba34 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib _interactiveOutDataEvent.WritableEvent.Signal(); } - [CommandHipc(0)] + [CommandCmif(0)] // GetAppletStateChangedEvent() -> handle<copy> public ResultCode GetAppletStateChangedEvent(ServiceCtx context) { @@ -79,14 +79,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // Start() public ResultCode Start(ServiceCtx context) { return (ResultCode)_applet.Start(_normalSession.GetConsumer(), _interactiveSession.GetConsumer()); } - [CommandHipc(20)] + [CommandCmif(20)] // RequestExit() public ResultCode RequestExit(ServiceCtx context) { @@ -98,14 +98,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(30)] + [CommandCmif(30)] // GetResult() public ResultCode GetResult(ServiceCtx context) { return (ResultCode)_applet.GetResult(); } - [CommandHipc(60)] + [CommandCmif(60)] // PresetLibraryAppletGpuTimeSliceZero() public ResultCode PresetLibraryAppletGpuTimeSliceZero(ServiceCtx context) { @@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(100)] + [CommandCmif(100)] // PushInData(object<nn::am::service::IStorage>) public ResultCode PushInData(ServiceCtx context) { @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(101)] + [CommandCmif(101)] // PopOutData() -> object<nn::am::service::IStorage> public ResultCode PopOutData(ServiceCtx context) { @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.NotAvailable; } - [CommandHipc(103)] + [CommandCmif(103)] // PushInteractiveInData(object<nn::am::service::IStorage>) public ResultCode PushInteractiveInData(ServiceCtx context) { @@ -156,7 +156,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(104)] + [CommandCmif(104)] // PopInteractiveOutData() -> object<nn::am::service::IStorage> public ResultCode PopInteractiveOutData(ServiceCtx context) { @@ -172,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.NotAvailable; } - [CommandHipc(105)] + [CommandCmif(105)] // GetPopOutDataEvent() -> handle<copy> public ResultCode GetPopOutDataEvent(ServiceCtx context) { @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(106)] + [CommandCmif(106)] // GetPopInteractiveOutDataEvent() -> handle<copy> public ResultCode GetPopInteractiveOutDataEvent(ServiceCtx context) { @@ -206,21 +206,21 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(110)] + [CommandCmif(110)] // NeedsToExitProcess() public ResultCode NeedsToExitProcess(ServiceCtx context) { return ResultCode.Stubbed; } - [CommandHipc(150)] + [CommandCmif(150)] // RequestForAppletToGetForeground() public ResultCode RequestForAppletToGetForeground(ServiceCtx context) { return ResultCode.Stubbed; } - [CommandHipc(160)] // 2.0.0+ + [CommandCmif(160)] // 2.0.0+ // GetIndirectLayerConsumerHandle() -> u64 indirect_layer_consumer_handle public ResultCode GetIndirectLayerConsumerHandle(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs index 720497141..176bd6322 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib } } - [CommandHipc(0)] + [CommandCmif(0)] // PopInData() -> object<nn::am::service::IStorage> public ResultCode PopInData(ServiceCtx context) { @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo public ResultCode GetLibraryAppletInfo(ServiceCtx context) { @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - [CommandHipc(14)] + [CommandCmif(14)] // GetCallerAppletIdentityInfo() -> nn::am::service::AppletIdentityInfo public ResultCode GetCallerAppletIdentityInfo(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs index e6c167342..6acd18cd6 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { public IProcessWindingController() { } - [CommandHipc(0)] + [CommandCmif(0)] // GetLaunchReason() -> nn::am::service::AppletProcessLaunchReason public ResultCode GetLaunchReason(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs index 3f81fda67..48dd42e41 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { public IAudioController() { } - [CommandHipc(0)] + [CommandCmif(0)] // SetExpectedMasterVolume(f32, f32) public ResultCode SetExpectedMasterVolume(ServiceCtx context) { @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetMainAppletExpectedMasterVolume() -> f32 public ResultCode GetMainAppletExpectedMasterVolume(ServiceCtx context) { @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetLibraryAppletExpectedMasterVolume() -> f32 public ResultCode GetLibraryAppletExpectedMasterVolume(ServiceCtx context) { @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // ChangeMainAppletMasterVolume(f32, u64) public ResultCode ChangeMainAppletMasterVolume(ServiceCtx context) { @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // SetTransparentVolumeRate(f32) public ResultCode SetTransparentVolumeRate(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index b145a65d1..381267b05 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _lblControllerServer = new Lbl.LblControllerServer(context); } - [CommandHipc(0)] + [CommandCmif(0)] // GetEventHandle() -> handle<copy> public ResultCode GetEventHandle(ServiceCtx context) { @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // ReceiveMessage() -> nn::am::AppletMessage public ResultCode ReceiveMessage(ServiceCtx context) { @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetOperationMode() -> u8 public ResultCode GetOperationMode(ServiceCtx context) { @@ -90,14 +90,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(6)] + [CommandCmif(6)] // GetPerformanceMode() -> nn::apm::PerformanceMode public ResultCode GetPerformanceMode(ServiceCtx context) { return (ResultCode)_apmManagerServer.GetPerformanceMode(context); } - [CommandHipc(8)] + [CommandCmif(8)] // GetBootMode() -> u8 public ResultCode GetBootMode(ServiceCtx context) { @@ -108,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(9)] + [CommandCmif(9)] // GetCurrentFocusState() -> u8 public ResultCode GetCurrentFocusState(ServiceCtx context) { @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(50)] // 3.0.0+ + [CommandCmif(50)] // 3.0.0+ // IsVrModeEnabled() -> b8 public ResultCode IsVrModeEnabled(ServiceCtx context) { @@ -126,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(51)] // 3.0.0+ + [CommandCmif(51)] // 3.0.0+ // SetVrModeEnabled(b8) public ResultCode SetVrModeEnabled(ServiceCtx context) { @@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(52)] // 4.0.0+ + [CommandCmif(52)] // 4.0.0+ // SetLcdBacklighOffEnabled(b8) public ResultCode SetLcdBacklighOffEnabled(ServiceCtx context) { @@ -151,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(53)] // 7.0.0+ + [CommandCmif(53)] // 7.0.0+ // BeginVrModeEx() public ResultCode BeginVrModeEx(ServiceCtx context) { @@ -160,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(54)] // 7.0.0+ + [CommandCmif(54)] // 7.0.0+ // EndVrModeEx() public ResultCode EndVrModeEx(ServiceCtx context) { @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used. } - [CommandHipc(60)] // 3.0.0+ + [CommandCmif(60)] // 3.0.0+ // GetDefaultDisplayResolution() -> (u32, u32) public ResultCode GetDefaultDisplayResolution(ServiceCtx context) { @@ -204,7 +204,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(61)] // 3.0.0+ + [CommandCmif(61)] // 3.0.0+ // GetDefaultDisplayResolutionChangeEvent() -> handle<copy> public ResultCode GetDefaultDisplayResolutionChangeEvent(ServiceCtx context) { @@ -224,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(62)] // 4.0.0+ + [CommandCmif(62)] // 4.0.0+ // GetHdcpAuthenticationState() -> s32 state public ResultCode GetHdcpAuthenticationState(ServiceCtx context) { @@ -235,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(66)] // 6.0.0+ + [CommandCmif(66)] // 6.0.0+ // SetCpuBoostMode(u32 cpu_boost_mode) public ResultCode SetCpuBoostMode(ServiceCtx context) { @@ -253,14 +253,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(91)] // 7.0.0+ + [CommandCmif(91)] // 7.0.0+ // GetCurrentPerformanceConfiguration() -> nn::apm::PerformanceConfiguration public ResultCode GetCurrentPerformanceConfiguration(ServiceCtx context) { return (ResultCode)_apmSystemManagerServer.GetCurrentPerformanceConfiguration(context); } - [CommandHipc(300)] // 9.0.0+ + [CommandCmif(300)] // 9.0.0+ // GetSettingsPlatformRegion() -> u8 public ResultCode GetSettingsPlatformRegion(ServiceCtx context) { @@ -272,7 +272,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(900)] // 11.0.0+ + [CommandCmif(900)] // 11.0.0+ // SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled() public ResultCode SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs index 7c03fc278..92c97d861 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _transferMem = context.Device.System.AppletCaptureBufferTransfer; } - [CommandHipc(8)] // 2.0.0+ + [CommandCmif(8)] // 2.0.0+ // TakeScreenShotOfOwnLayer(b8, s32) public ResultCode TakeScreenShotOfOwnLayer(ServiceCtx context) { @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // ReleaseLastApplicationCaptureBuffer() public ResultCode ReleaseLastApplicationCaptureBuffer(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(15)] + [CommandCmif(15)] // ReleaseCallerAppletCaptureBuffer() public ResultCode ReleaseCallerAppletCaptureBuffer(ServiceCtx context) { @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(16)] + [CommandCmif(16)] // AcquireLastApplicationCaptureBufferEx() -> (b8, handle<copy>) public ResultCode AcquireLastApplicationCaptureBufferEx(ServiceCtx context) { @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(18)] + [CommandCmif(18)] // AcquireCallerAppletCaptureBufferEx() -> (b8, handle<copy>) public ResultCode AcquireCallerAppletCaptureBufferEx(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs index 2a9848ddc..c7c073ff1 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _channelEvent = new KEvent(system.KernelContext); } - [CommandHipc(10)] + [CommandCmif(10)] // RequestToGetForeground() public ResultCode RequestToGetForeground(ServiceCtx context) { @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(21)] + [CommandCmif(21)] // GetPopFromGeneralChannelEvent() -> handle<copy> public ResultCode GetPopFromGeneralChannelEvent(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs index 8860f007c..fb870c242 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { public ILibraryAppletCreator() { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateLibraryApplet(u32, u32) -> object<nn::am::service::ILibraryAppletAccessor> public ResultCode CreateLibraryApplet(ServiceCtx context) { @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // CreateStorage(u64) -> object<nn::am::service::IStorage> public ResultCode CreateStorage(ServiceCtx context) { @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // CreateTransferMemoryStorage(b8, u64, handle<copy>) -> object<nn::am::service::IStorage> public ResultCode CreateTransferMemoryStorage(ServiceCtx context) { @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(12)] // 2.0.0+ + [CommandCmif(12)] // 2.0.0+ // CreateHandleStorage(u64, handle<copy>) -> object<nn::am::service::IStorage> public ResultCode CreateHandleStorage(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 39be75776..399e778a4 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _pid = pid; } - [CommandHipc(0)] + [CommandCmif(0)] // Exit() public ResultCode Exit(ServiceCtx context) { @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // LockExit() public ResultCode LockExit(ServiceCtx context) { @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // UnlockExit() public ResultCode UnlockExit(ServiceCtx context) { @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(3)] // 2.0.0+ + [CommandCmif(3)] // 2.0.0+ // EnterFatalSection() public ResultCode EnterFatalSection(ServiceCtx context) { @@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(4)] // 2.0.0+ + [CommandCmif(4)] // 2.0.0+ // LeaveFatalSection() public ResultCode LeaveFatalSection(ServiceCtx context) { @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return result; } - [CommandHipc(9)] + [CommandCmif(9)] // GetLibraryAppletLaunchableEvent() -> handle<copy> public ResultCode GetLibraryAppletLaunchableEvent(ServiceCtx context) { @@ -124,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // SetScreenShotPermission(u32) public ResultCode SetScreenShotPermission(ServiceCtx context) { @@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // SetOperationModeChangedNotification(b8) public ResultCode SetOperationModeChangedNotification(ServiceCtx context) { @@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(12)] + [CommandCmif(12)] // SetPerformanceModeChangedNotification(b8) public ResultCode SetPerformanceModeChangedNotification(ServiceCtx context) { @@ -163,7 +163,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(13)] + [CommandCmif(13)] // SetFocusHandlingMode(b8, b8, b8) public ResultCode SetFocusHandlingMode(ServiceCtx context) { @@ -176,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(14)] + [CommandCmif(14)] // SetRestartMessageEnabled(b8) public ResultCode SetRestartMessageEnabled(ServiceCtx context) { @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(16)] // 2.0.0+ + [CommandCmif(16)] // 2.0.0+ // SetOutOfFocusSuspendingEnabled(b8) public ResultCode SetOutOfFocusSuspendingEnabled(ServiceCtx context) { @@ -202,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(19)] // 3.0.0+ + [CommandCmif(19)] // 3.0.0+ // SetScreenShotImageOrientation(u32) public ResultCode SetScreenShotImageOrientation(ServiceCtx context) { @@ -215,7 +215,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(40)] + [CommandCmif(40)] // CreateManagedDisplayLayer() -> u64 public ResultCode CreateManagedDisplayLayer(ServiceCtx context) { @@ -227,7 +227,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(41)] // 4.0.0+ + [CommandCmif(41)] // 4.0.0+ // IsSystemBufferSharingEnabled() public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context) { @@ -236,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.NotImplemented; } - [CommandHipc(44)] // 10.0.0+ + [CommandCmif(44)] // 10.0.0+ // CreateManagedDisplaySeparableLayer() -> (u64, u64) public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context) { @@ -250,7 +250,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(50)] + [CommandCmif(50)] // SetHandlesRequestToDisplay(b8) public ResultCode SetHandlesRequestToDisplay(ServiceCtx context) { @@ -263,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(62)] + [CommandCmif(62)] // SetIdleTimeDetectionExtension(u32) public ResultCode SetIdleTimeDetectionExtension(ServiceCtx context) { @@ -276,7 +276,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(63)] + [CommandCmif(63)] // GetIdleTimeDetectionExtension() -> u32 public ResultCode GetIdleTimeDetectionExtension(ServiceCtx context) { @@ -287,7 +287,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(65)] + [CommandCmif(65)] // ReportUserIsActive() public ResultCode ReportUserIsActive(ServiceCtx context) { @@ -298,19 +298,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(67)] //3.0.0+ + [CommandCmif(67)] //3.0.0+ // IsIlluminanceAvailable() -> bool public ResultCode IsIlluminanceAvailable(ServiceCtx context) { // NOTE: This should call IsAmbientLightSensorAvailable through to Lbl, but there's no situation where we'd want false. context.ResponseData.Write(true); - + Logger.Stub?.PrintStub(LogClass.ServiceAm); return ResultCode.Success; } - [CommandHipc(68)] + [CommandCmif(68)] // SetAutoSleepDisabled(u8) public ResultCode SetAutoSleepDisabled(ServiceCtx context) { @@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(69)] + [CommandCmif(69)] // IsAutoSleepDisabled() -> u8 public ResultCode IsAutoSleepDisabled(ServiceCtx context) { @@ -330,7 +330,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(71)] //5.0.0+ + [CommandCmif(71)] //5.0.0+ // GetCurrentIlluminanceEx() -> (bool, f32) public ResultCode GetCurrentIlluminanceEx(ServiceCtx context) { @@ -343,7 +343,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(80)] // 4.0.0+ + [CommandCmif(80)] // 4.0.0+ // SetWirelessPriorityMode(s32 wireless_priority_mode) public ResultCode SetWirelessPriorityMode(ServiceCtx context) { @@ -359,7 +359,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(90)] // 6.0.0+ + [CommandCmif(90)] // 6.0.0+ // GetAccumulatedSuspendedTickValue() -> u64 public ResultCode GetAccumulatedSuspendedTickValue(ServiceCtx context) { @@ -368,7 +368,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(91)] // 6.0.0+ + [CommandCmif(91)] // 6.0.0+ // GetAccumulatedSuspendedTickChangedEvent() -> handle<copy> public ResultCode GetAccumulatedSuspendedTickChangedEvent(ServiceCtx context) { @@ -389,7 +389,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(100)] // 7.0.0+ + [CommandCmif(100)] // 7.0.0+ // SetAlbumImageTakenNotificationEnabled(u8) public ResultCode SetAlbumImageTakenNotificationEnabled(ServiceCtx context) { @@ -400,7 +400,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(120)] // 11.0.0+ + [CommandCmif(120)] // 11.0.0+ // SaveCurrentScreenshot(s32 album_report_option) public ResultCode SaveCurrentScreenshot(ServiceCtx context) { @@ -416,7 +416,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(130)] // 13.0.0+ + [CommandCmif(130)] // 13.0.0+ // SetRecordVolumeMuted(b8) public ResultCode SetRecordVolumeMuted(ServiceCtx context) { @@ -429,4 +429,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs index 37a3efcba..730df5d05 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _pid = pid; } - [CommandHipc(1)] + [CommandCmif(1)] // GetAppletResourceUserId() -> nn::applet::AppletResourceUserId public ResultCode GetAppletResourceUserId(ServiceCtx context) { @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // AcquireForegroundRights() public ResultCode AcquireForegroundRights(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index 926234f58..728a10180 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE { public IAllSystemAppletProxiesService(ServiceCtx context) { } - [CommandHipc(100)] + [CommandCmif(100)] // OpenSystemAppletProxy(u64, pid, handle<copy>) -> object<nn::am::service::ISystemAppletProxy> public ResultCode OpenSystemAppletProxy(ServiceCtx context) { @@ -16,8 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } - [CommandHipc(200)] - [CommandHipc(201)] // 3.0.0+ + [CommandCmif(200)] + [CommandCmif(201)] // 3.0.0+ // OpenLibraryAppletProxy(u64, pid, handle<copy>) -> object<nn::am::service::ILibraryAppletProxy> public ResultCode OpenLibraryAppletProxy(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs index c844bfdce..190f1a514 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE Data = data; } - [CommandHipc(0)] + [CommandCmif(0)] // Open() -> object<nn::am::service::IStorageAccessor> public ResultCode Open(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs index 33f5393ff..4c7e264d0 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE _storage = storage; } - [CommandHipc(0)] + [CommandCmif(0)] // GetSize() -> u64 public ResultCode GetSize(ServiceCtx context) { @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // Write(u64, buffer<bytes, 0x21>) public ResultCode Write(ServiceCtx context) { @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // Read(u64) -> buffer<bytes, 0x22> public ResultCode Read(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index 924f54298..5ae8f459f 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati _horizon = system.LibHacHorizonManager.AmClient; } - [CommandHipc(1)] + [CommandCmif(1)] // PopLaunchParameter(LaunchParameterKind kind) -> object<nn::am::service::IStorage> public ResultCode PopLaunchParameter(ServiceCtx context) { @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(12)] // 4.0.0+ + [CommandCmif(12)] // 4.0.0+ // CreateApplicationAndRequestToStart(u64 title_id) public ResultCode CreateApplicationAndRequestToStart(ServiceCtx context) { @@ -108,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(20)] + [CommandCmif(20)] // EnsureSaveData(nn::account::Uid) -> u64 public ResultCode EnsureSaveData(ServiceCtx context) { @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return (ResultCode)result.Value; } - [CommandHipc(21)] + [CommandCmif(21)] // GetDesiredLanguage() -> nn::settings::LanguageCode public ResultCode GetDesiredLanguage(ServiceCtx context) { @@ -164,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(22)] + [CommandCmif(22)] // SetTerminateResult(u32) public ResultCode SetTerminateResult(ServiceCtx context) { @@ -175,7 +175,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(23)] + [CommandCmif(23)] // GetDisplayVersion() -> nn::oe::DisplayVersion public ResultCode GetDisplayVersion(ServiceCtx context) { @@ -185,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(25)] // 3.0.0+ + [CommandCmif(25)] // 3.0.0+ // ExtendSaveData(u8 save_data_type, nn::account::Uid, s64 save_size, s64 journal_size) -> u64 result_code public ResultCode ExtendSaveData(ServiceCtx context) { @@ -207,7 +207,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(26)] // 3.0.0+ + [CommandCmif(26)] // 3.0.0+ // GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (s64 save_size, s64 journal_size) public ResultCode GetSaveDataSize(ServiceCtx context) { @@ -226,7 +226,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(27)] // 5.0.0+ + [CommandCmif(27)] // 5.0.0+ // CreateCacheStorage(u16 index, s64 save_size, s64 journal_size) -> (u32 storageTarget, u64 requiredSize) public ResultCode CreateCacheStorage(ServiceCtx context) { @@ -250,7 +250,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(28)] // 11.0.0+ + [CommandCmif(28)] // 11.0.0+ // GetSaveDataSizeMax() -> (s64 save_size_max, s64 journal_size_max) public ResultCode GetSaveDataSizeMax(ServiceCtx context) { @@ -267,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(30)] + [CommandCmif(30)] // BeginBlockingHomeButtonShortAndLongPressed() public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context) { @@ -278,7 +278,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(31)] + [CommandCmif(31)] // EndBlockingHomeButtonShortAndLongPressed() public ResultCode EndBlockingHomeButtonShortAndLongPressed(ServiceCtx context) { @@ -289,7 +289,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(32)] // 2.0.0+ + [CommandCmif(32)] // 2.0.0+ // BeginBlockingHomeButton(u64 nano_second) public ResultCode BeginBlockingHomeButton(ServiceCtx context) { @@ -302,7 +302,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(33)] // 2.0.0+ + [CommandCmif(33)] // 2.0.0+ // EndBlockingHomeButton() public ResultCode EndBlockingHomeButton(ServiceCtx context) { @@ -313,7 +313,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(40)] + [CommandCmif(40)] // NotifyRunning() -> b8 public ResultCode NotifyRunning(ServiceCtx context) { @@ -322,7 +322,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(50)] // 2.0.0+ + [CommandCmif(50)] // 2.0.0+ // GetPseudoDeviceId() -> nn::util::Uuid public ResultCode GetPseudoDeviceId(ServiceCtx context) { @@ -334,7 +334,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(60)] // 2.0.0+ + [CommandCmif(60)] // 2.0.0+ // SetMediaPlaybackStateForApplication(bool enabled) public ResultCode SetMediaPlaybackStateForApplication(ServiceCtx context) { @@ -347,7 +347,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(65)] // 3.0.0+ + [CommandCmif(65)] // 3.0.0+ // IsGamePlayRecordingSupported() -> u8 public ResultCode IsGamePlayRecordingSupported(ServiceCtx context) { @@ -356,7 +356,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(66)] // 3.0.0+ + [CommandCmif(66)] // 3.0.0+ // InitializeGamePlayRecording(u64, handle<copy>) public ResultCode InitializeGamePlayRecording(ServiceCtx context) { @@ -365,7 +365,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(67)] // 3.0.0+ + [CommandCmif(67)] // 3.0.0+ // SetGamePlayRecordingState(u32) public ResultCode SetGamePlayRecordingState(ServiceCtx context) { @@ -376,7 +376,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(90)] // 4.0.0+ + [CommandCmif(90)] // 4.0.0+ // EnableApplicationCrashReport(u8) public ResultCode EnableApplicationCrashReport(ServiceCtx context) { @@ -387,7 +387,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(100)] // 5.0.0+ + [CommandCmif(100)] // 5.0.0+ // InitializeApplicationCopyrightFrameBuffer(s32 width, s32 height, handle<copy, transfer_memory> transfer_memory, u64 transfer_memory_size) public ResultCode InitializeApplicationCopyrightFrameBuffer(ServiceCtx context) { @@ -433,7 +433,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return resultCode; } - [CommandHipc(101)] // 5.0.0+ + [CommandCmif(101)] // 5.0.0+ // SetApplicationCopyrightImage(buffer<bytes, 0x45> frame_buffer, s32 x, s32 y, s32 width, s32 height, s32 window_origin_mode) public ResultCode SetApplicationCopyrightImage(ServiceCtx context) { @@ -480,7 +480,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(102)] // 5.0.0+ + [CommandCmif(102)] // 5.0.0+ // SetApplicationCopyrightVisibility(bool visible) public ResultCode SetApplicationCopyrightVisibility(ServiceCtx context) { @@ -493,7 +493,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(110)] // 5.0.0+ + [CommandCmif(110)] // 5.0.0+ // QueryApplicationPlayStatistics(buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count) public ResultCode QueryApplicationPlayStatistics(ServiceCtx context) { @@ -501,7 +501,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return (ResultCode)QueryPlayStatisticsManager.GetPlayStatistics(context); } - [CommandHipc(111)] // 6.0.0+ + [CommandCmif(111)] // 6.0.0+ // QueryApplicationPlayStatisticsByUid(nn::account::Uid, buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count) public ResultCode QueryApplicationPlayStatisticsByUid(ServiceCtx context) { @@ -509,7 +509,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return (ResultCode)QueryPlayStatisticsManager.GetPlayStatistics(context, true); } - [CommandHipc(120)] // 5.0.0+ + [CommandCmif(120)] // 5.0.0+ // ExecuteProgram(ProgramSpecifyKind kind, u64 value) public ResultCode ExecuteProgram(ServiceCtx context) { @@ -527,7 +527,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(121)] // 5.0.0+ + [CommandCmif(121)] // 5.0.0+ // ClearUserChannel() public ResultCode ClearUserChannel(ServiceCtx context) { @@ -536,7 +536,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(122)] // 5.0.0+ + [CommandCmif(122)] // 5.0.0+ // UnpopToUserChannel(object<nn::am::service::IStorage> input_storage) public ResultCode UnpopToUserChannel(ServiceCtx context) { @@ -547,7 +547,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(123)] // 5.0.0+ + [CommandCmif(123)] // 5.0.0+ // GetPreviousProgramIndex() -> s32 program_index public ResultCode GetPreviousProgramIndex(ServiceCtx context) { @@ -560,7 +560,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(130)] // 8.0.0+ + [CommandCmif(130)] // 8.0.0+ // GetGpuErrorDetectedSystemEvent() -> handle<copy> public ResultCode GetGpuErrorDetectedSystemEvent(ServiceCtx context) { @@ -581,7 +581,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(140)] // 9.0.0+ + [CommandCmif(140)] // 9.0.0+ // GetFriendInvitationStorageChannelEvent() -> handle<copy> public ResultCode GetFriendInvitationStorageChannelEvent(ServiceCtx context) { @@ -598,7 +598,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(141)] // 9.0.0+ + [CommandCmif(141)] // 9.0.0+ // TryPopFromFriendInvitationStorageChannel() -> object<nn::am::service::IStorage> public ResultCode TryPopFromFriendInvitationStorageChannel(ServiceCtx context) { @@ -612,7 +612,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.NotAvailable; } - [CommandHipc(150)] // 9.0.0+ + [CommandCmif(150)] // 9.0.0+ // GetNotificationStorageChannelEvent() -> handle<copy> public ResultCode GetNotificationStorageChannelEvent(ServiceCtx context) { @@ -629,7 +629,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(160)] // 9.0.0+ + [CommandCmif(160)] // 9.0.0+ // GetHealthWarningDisappearedSystemEvent() -> handle<copy> public ResultCode GetHealthWarningDisappearedSystemEvent(ServiceCtx context) { @@ -646,7 +646,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - [CommandHipc(1001)] // 10.0.0+ + [CommandCmif(1001)] // 10.0.0+ // PrepareForJit() public ResultCode PrepareForJit(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs index bb85c1db4..50e3be274 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService _pid = pid; } - [CommandHipc(0)] + [CommandCmif(0)] // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter> public ResultCode GetCommonStateGetter(ServiceCtx context) { @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetSelfController() -> object<nn::am::service::ISelfController> public ResultCode GetSelfController(ServiceCtx context) { @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetWindowController() -> object<nn::am::service::IWindowController> public ResultCode GetWindowController(ServiceCtx context) { @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetAudioController() -> object<nn::am::service::IAudioController> public ResultCode GetAudioController(ServiceCtx context) { @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetDisplayController() -> object<nn::am::service::IDisplayController> public ResultCode GetDisplayController(ServiceCtx context) { @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator> public ResultCode GetLibraryAppletCreator(ServiceCtx context) { @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } - [CommandHipc(20)] + [CommandCmif(20)] // GetApplicationFunctions() -> object<nn::am::service::IApplicationFunctions> public ResultCode GetApplicationFunctions(ServiceCtx context) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } - [CommandHipc(1000)] + [CommandCmif(1000)] // GetDebugFunctions() -> object<nn::am::service::IDebugFunctions> public ResultCode GetDebugFunctions(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs index 252ae352d..3a4c71e4b 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { public IApplicationProxyService(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // OpenApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy> public ResultCode OpenApplicationProxy(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Apm/IManager.cs b/Ryujinx.HLE/HOS/Services/Apm/IManager.cs index 11d28808f..72e39a77f 100644 --- a/Ryujinx.HLE/HOS/Services/Apm/IManager.cs +++ b/Ryujinx.HLE/HOS/Services/Apm/IManager.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm protected abstract PerformanceMode GetPerformanceMode(); protected abstract bool IsCpuOverclockEnabled(); - [CommandHipc(0)] + [CommandCmif(0)] // OpenSession() -> object<nn::apm::ISession> public ResultCode OpenSession(ServiceCtx context) { @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm return resultCode; } - [CommandHipc(1)] + [CommandCmif(1)] // GetPerformanceMode() -> nn::apm::PerformanceMode public ResultCode GetPerformanceMode(ServiceCtx context) { @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm return ResultCode.Success; } - [CommandHipc(6)] // 7.0.0+ + [CommandCmif(6)] // 7.0.0+ // IsCpuOverclockEnabled() -> bool public ResultCode IsCpuOverclockEnabled(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs b/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs index 56a7c5aa9..9620c30a1 100644 --- a/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs +++ b/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm { public IManagerPrivileged(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // OpenSession() -> object<nn::apm::ISession> public ResultCode OpenSession(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Apm/ISession.cs b/Ryujinx.HLE/HOS/Services/Apm/ISession.cs index fca01812c..f828cd175 100644 --- a/Ryujinx.HLE/HOS/Services/Apm/ISession.cs +++ b/Ryujinx.HLE/HOS/Services/Apm/ISession.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm protected abstract ResultCode GetPerformanceConfiguration(PerformanceMode performanceMode, out PerformanceConfiguration performanceConfiguration); protected abstract void SetCpuOverclockEnabled(bool enabled); - [CommandHipc(0)] + [CommandCmif(0)] // SetPerformanceConfiguration(nn::apm::PerformanceMode, nn::apm::PerformanceConfiguration) public ResultCode SetPerformanceConfiguration(ServiceCtx context) { @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm return SetPerformanceConfiguration(performanceMode, performanceConfiguration); } - [CommandHipc(1)] + [CommandCmif(1)] // GetPerformanceConfiguration(nn::apm::PerformanceMode) -> nn::apm::PerformanceConfiguration public ResultCode GetPerformanceConfiguration(ServiceCtx context) { @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm return resultCode; } - [CommandHipc(2)] // 8.0.0+ + [CommandCmif(2)] // 8.0.0+ // SetCpuOverclockEnabled(bool) public ResultCode SetCpuOverclockEnabled(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs b/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs index 1268463b7..9d2c7b0b4 100644 --- a/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs +++ b/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm internal abstract void SetCpuBoostMode(CpuBoostMode cpuBoostMode); protected abstract PerformanceConfiguration GetCurrentPerformanceConfiguration(); - [CommandHipc(0)] + [CommandCmif(0)] // RequestPerformanceMode(nn::apm::PerformanceMode) public ResultCode RequestPerformanceMode(ServiceCtx context) { @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm return ResultCode.Success; } - [CommandHipc(6)] // 7.0.0+ + [CommandCmif(6)] // 7.0.0+ // SetCpuBoostMode(nn::apm::CpuBootMode) public ResultCode SetCpuBoostMode(ServiceCtx context) { @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm return ResultCode.Success; } - [CommandHipc(7)] // 7.0.0+ + [CommandCmif(7)] // 7.0.0+ // GetCurrentPerformanceConfiguration() -> nn::apm::PerformanceConfiguration public ResultCode GetCurrentPerformanceConfiguration(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs index 4911b7f00..a80b94025 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn _impl = impl; } - [CommandHipc(0)] + [CommandCmif(0)] // GetAudioInState() -> u32 state public ResultCode GetAudioInState(ServiceCtx context) { @@ -27,21 +27,21 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // Start() public ResultCode Start(ServiceCtx context) { return _impl.Start(); } - [CommandHipc(2)] + [CommandCmif(2)] // Stop() public ResultCode StopAudioIn(ServiceCtx context) { return _impl.Stop(); } - [CommandHipc(3)] + [CommandCmif(3)] // AppendAudioInBuffer(u64 tag, buffer<nn::audio::AudioInBuffer, 5>) public ResultCode AppendAudioInBuffer(ServiceCtx context) { @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return _impl.AppendBuffer(bufferTag, ref data); } - [CommandHipc(4)] + [CommandCmif(4)] // RegisterBufferEvent() -> handle<copy> public ResultCode RegisterBufferEvent(ServiceCtx context) { @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetReleasedAudioInBuffers() -> (u32 count, buffer<u64, 6> tags) public ResultCode GetReleasedAudioInBuffers(ServiceCtx context) { @@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn } } - [CommandHipc(6)] + [CommandCmif(6)] // ContainsAudioInBuffer(u64 tag) -> b8 public ResultCode ContainsAudioInBuffer(ServiceCtx context) { @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return ResultCode.Success; } - [CommandHipc(7)] // 3.0.0+ + [CommandCmif(7)] // 3.0.0+ // AppendUacInBuffer(u64 tag, handle<copy, unknown>, buffer<nn::audio::AudioInBuffer, 5>) public ResultCode AppendUacInBuffer(ServiceCtx context) { @@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return _impl.AppendUacBuffer(bufferTag, ref data, handle); } - [CommandHipc(8)] // 3.0.0+ + [CommandCmif(8)] // 3.0.0+ // AppendAudioInBufferAuto(u64 tag, buffer<nn::audio::AudioInBuffer, 0x21>) public ResultCode AppendAudioInBufferAuto(ServiceCtx context) { @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return _impl.AppendBuffer(bufferTag, ref data); } - [CommandHipc(9)] // 3.0.0+ + [CommandCmif(9)] // 3.0.0+ // GetReleasedAudioInBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags) public ResultCode GetReleasedAudioInBuffersAuto(ServiceCtx context) { @@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn } } - [CommandHipc(10)] // 3.0.0+ + [CommandCmif(10)] // 3.0.0+ // AppendUacInBufferAuto(u64 tag, handle<copy, event>, buffer<nn::audio::AudioInBuffer, 0x21>) public ResultCode AppendUacInBufferAuto(ServiceCtx context) { @@ -155,7 +155,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return _impl.AppendUacBuffer(bufferTag, ref data, handle); } - [CommandHipc(11)] // 4.0.0+ + [CommandCmif(11)] // 4.0.0+ // GetAudioInBufferCount() -> u32 public ResultCode GetAudioInBufferCount(ServiceCtx context) { @@ -164,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return ResultCode.Success; } - [CommandHipc(12)] // 4.0.0+ + [CommandCmif(12)] // 4.0.0+ // SetAudioInVolume(s32) public ResultCode SetAudioInVolume(ServiceCtx context) { @@ -175,7 +175,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return ResultCode.Success; } - [CommandHipc(13)] // 4.0.0+ + [CommandCmif(13)] // 4.0.0+ // GetAudioInVolume() -> s32 public ResultCode GetAudioInVolume(ServiceCtx context) { @@ -184,7 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return ResultCode.Success; } - [CommandHipc(14)] // 6.0.0+ + [CommandCmif(14)] // 6.0.0+ // FlushAudioInBuffers() -> b8 public ResultCode FlushAudioInBuffers(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs index 7b243c71b..755caee52 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio _impl = impl; } - [CommandHipc(0)] + [CommandCmif(0)] // ListAudioIns() -> (u32, buffer<bytes, 6>) public ResultCode ListAudioIns(ServiceCtx context) { @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // OpenAudioIn(AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 5> name) // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 6> name) public ResultCode OpenAudioIn(ServiceCtx context) @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return resultCode; } - [CommandHipc(2)] // 3.0.0+ + [CommandCmif(2)] // 3.0.0+ // ListAudioInsAuto() -> (u32, buffer<bytes, 0x22>) public ResultCode ListAudioInsAuto(ServiceCtx context) { @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(3)] // 3.0.0+ + [CommandCmif(3)] // 3.0.0+ // OpenAudioInAuto(AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 0x21>) // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 0x22> name) public ResultCode OpenAudioInAuto(ServiceCtx context) @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return resultCode; } - [CommandHipc(4)] // 3.0.0+ + [CommandCmif(4)] // 3.0.0+ // ListAudioInsAutoFiltered() -> (u32, buffer<bytes, 0x22>) public ResultCode ListAudioInsAutoFiltered(ServiceCtx context) { @@ -194,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(5)] // 5.0.0+ + [CommandCmif(5)] // 5.0.0+ // OpenAudioInProtocolSpecified(b64 protocol_specified_related, AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 5> name) // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 6> name) public ResultCode OpenAudioInProtocolSpecified(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs index 2d6908e3f..329e17941 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut _impl = impl; } - [CommandHipc(0)] + [CommandCmif(0)] // GetAudioOutState() -> u32 state public ResultCode GetAudioOutState(ServiceCtx context) { @@ -27,21 +27,21 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // Start() public ResultCode Start(ServiceCtx context) { return _impl.Start(); } - [CommandHipc(2)] + [CommandCmif(2)] // Stop() public ResultCode Stop(ServiceCtx context) { return _impl.Stop(); } - [CommandHipc(3)] + [CommandCmif(3)] // AppendAudioOutBuffer(u64 bufferTag, buffer<nn::audio::AudioOutBuffer, 5> buffer) public ResultCode AppendAudioOutBuffer(ServiceCtx context) { @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return _impl.AppendBuffer(bufferTag, ref data); } - [CommandHipc(4)] + [CommandCmif(4)] // RegisterBufferEvent() -> handle<copy> public ResultCode RegisterBufferEvent(ServiceCtx context) { @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetReleasedAudioOutBuffers() -> (u32 count, buffer<u64, 6> tags) public ResultCode GetReleasedAudioOutBuffers(ServiceCtx context) { @@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut } } - [CommandHipc(6)] + [CommandCmif(6)] // ContainsAudioOutBuffer(u64 tag) -> b8 public ResultCode ContainsAudioOutBuffer(ServiceCtx context) { @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - [CommandHipc(7)] // 3.0.0+ + [CommandCmif(7)] // 3.0.0+ // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>) public ResultCode AppendAudioOutBufferAuto(ServiceCtx context) { @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return _impl.AppendBuffer(bufferTag, ref data); } - [CommandHipc(8)] // 3.0.0+ + [CommandCmif(8)] // 3.0.0+ // GetReleasedAudioOutBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags) public ResultCode GetReleasedAudioOutBuffersAuto(ServiceCtx context) { @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut } } - [CommandHipc(9)] // 4.0.0+ + [CommandCmif(9)] // 4.0.0+ // GetAudioOutBufferCount() -> u32 public ResultCode GetAudioOutBufferCount(ServiceCtx context) { @@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - [CommandHipc(10)] // 4.0.0+ + [CommandCmif(10)] // 4.0.0+ // GetAudioOutPlayedSampleCount() -> u64 public ResultCode GetAudioOutPlayedSampleCount(ServiceCtx context) { @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - [CommandHipc(11)] // 4.0.0+ + [CommandCmif(11)] // 4.0.0+ // FlushAudioOutBuffers() -> b8 public ResultCode FlushAudioOutBuffers(ServiceCtx context) { @@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - [CommandHipc(12)] // 6.0.0+ + [CommandCmif(12)] // 6.0.0+ // SetAudioOutVolume(s32) public ResultCode SetAudioOutVolume(ServiceCtx context) { @@ -165,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - [CommandHipc(13)] // 6.0.0+ + [CommandCmif(13)] // 6.0.0+ // GetAudioOutVolume() -> s32 public ResultCode GetAudioOutVolume(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs index 8a987dba9..7c5d8c4ec 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio _impl = impl; } - [CommandHipc(0)] + [CommandCmif(0)] // ListAudioOuts() -> (u32, buffer<bytes, 6>) public ResultCode ListAudioOuts(ServiceCtx context) { @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // OpenAudioOut(AudioOutInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process> process_handle, buffer<bytes, 5> name_in) // -> (AudioOutInputConfiguration output_config, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out) public ResultCode OpenAudioOut(ServiceCtx context) @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return resultCode; } - [CommandHipc(2)] // 3.0.0+ + [CommandCmif(2)] // 3.0.0+ // ListAudioOutsAuto() -> (u32, buffer<bytes, 0x22>) public ResultCode ListAudioOutsAuto(ServiceCtx context) { @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(3)] // 3.0.0+ + [CommandCmif(3)] // 3.0.0+ // OpenAudioOut(AudioOutInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process> process_handle, buffer<bytes, 0x21> name_in) // -> (AudioOutInputConfiguration output_config, object<nn::audio::detail::IAudioOut>, buffer<bytes, 0x22> name_out) public ResultCode OpenAudioOutAuto(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs index e868ad5af..e7a751216 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer _impl = impl; } - [CommandHipc(0)] + [CommandCmif(0)] // ListAudioDeviceName() -> (u32, buffer<bytes, 6>) public ResultCode ListAudioDeviceName(ServiceCtx context) { @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // SetAudioDeviceOutputVolume(f32 volume, buffer<bytes, 5> name) public ResultCode SetAudioDeviceOutputVolume(ServiceCtx context) { @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return _impl.SetAudioDeviceOutputVolume(deviceName, volume); } - [CommandHipc(2)] + [CommandCmif(2)] // GetAudioDeviceOutputVolume(buffer<bytes, 5> name) -> f32 volume public ResultCode GetAudioDeviceOutputVolume(ServiceCtx context) { @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return result; } - [CommandHipc(3)] + [CommandCmif(3)] // GetActiveAudioDeviceName() -> buffer<bytes, 6> public ResultCode GetActiveAudioDeviceName(ServiceCtx context) { @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // QueryAudioDeviceSystemEvent() -> handle<copy, event> public ResultCode QueryAudioDeviceSystemEvent(ServiceCtx context) { @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetActiveChannelCount() -> u32 public ResultCode GetActiveChannelCount(ServiceCtx context) { @@ -138,7 +138,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(6)] // 3.0.0+ + [CommandCmif(6)] // 3.0.0+ // ListAudioDeviceNameAuto() -> (u32, buffer<bytes, 0x22>) public ResultCode ListAudioDeviceNameAuto(ServiceCtx context) { @@ -171,7 +171,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(7)] // 3.0.0+ + [CommandCmif(7)] // 3.0.0+ // SetAudioDeviceOutputVolumeAuto(f32 volume, buffer<bytes, 0x21> name) public ResultCode SetAudioDeviceOutputVolumeAuto(ServiceCtx context) { @@ -184,7 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return _impl.SetAudioDeviceOutputVolume(deviceName, volume); } - [CommandHipc(8)] // 3.0.0+ + [CommandCmif(8)] // 3.0.0+ // GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21> name) -> f32 public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context) { @@ -202,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(10)] // 3.0.0+ + [CommandCmif(10)] // 3.0.0+ // GetActiveAudioDeviceNameAuto() -> buffer<bytes, 0x22> public ResultCode GetActiveAudioDeviceNameAuto(ServiceCtx context) { @@ -224,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(11)] // 3.0.0+ + [CommandCmif(11)] // 3.0.0+ // QueryAudioDeviceInputEvent() -> handle<copy, event> public ResultCode QueryAudioDeviceInputEvent(ServiceCtx context) { @@ -242,7 +242,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(12)] // 3.0.0+ + [CommandCmif(12)] // 3.0.0+ // QueryAudioDeviceOutputEvent() -> handle<copy, event> public ResultCode QueryAudioDeviceOutputEvent(ServiceCtx context) { @@ -260,7 +260,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(13)] // 13.0.0+ + [CommandCmif(13)] // 13.0.0+ // GetActiveAudioOutputDeviceName() -> buffer<bytes, 6> public ResultCode GetActiveAudioOutputDeviceName(ServiceCtx context) { @@ -283,7 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(14)] // 13.0.0+ + [CommandCmif(14)] // 13.0.0+ // ListAudioOutputDeviceName() -> (u32, buffer<bytes, 6>) public ResultCode ListAudioOutputDeviceName(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index 3843b408e..813210461 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer _impl = impl; } - [CommandHipc(0)] + [CommandCmif(0)] // GetSampleRate() -> u32 public ResultCode GetSampleRate(ServiceCtx context) { @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetSampleCount() -> u32 public ResultCode GetSampleCount(ServiceCtx context) { @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetMixBufferCount() -> u32 public ResultCode GetMixBufferCount(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetState() -> u32 public ResultCode GetState(ServiceCtx context) { @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // RequestUpdate(buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 5> input) // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> performanceOutput) public ResultCode RequestUpdate(ServiceCtx context) @@ -89,21 +89,21 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return result; } - [CommandHipc(5)] + [CommandCmif(5)] // Start() public ResultCode Start(ServiceCtx context) { return _impl.Start(); } - [CommandHipc(6)] + [CommandCmif(6)] // Stop() public ResultCode Stop(ServiceCtx context) { return _impl.Stop(); } - [CommandHipc(7)] + [CommandCmif(7)] // QuerySystemEvent() -> handle<copy, event> public ResultCode QuerySystemEvent(ServiceCtx context) { @@ -122,7 +122,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return result; } - [CommandHipc(8)] + [CommandCmif(8)] // SetAudioRendererRenderingTimeLimit(u32 limit) public ResultCode SetAudioRendererRenderingTimeLimit(ServiceCtx context) { @@ -133,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(9)] + [CommandCmif(9)] // GetAudioRendererRenderingTimeLimit() -> u32 limit public ResultCode GetAudioRendererRenderingTimeLimit(ServiceCtx context) { @@ -144,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(10)] // 3.0.0+ + [CommandCmif(10)] // 3.0.0+ // RequestUpdateAuto(buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x21> input) // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> performanceOutput) public ResultCode RequestUpdateAuto(ServiceCtx context) @@ -172,14 +172,14 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return result; } - [CommandHipc(11)] // 3.0.0+ + [CommandCmif(11)] // 3.0.0+ // ExecuteAudioRendererRendering() public ResultCode ExecuteAudioRendererRendering(ServiceCtx context) { return _impl.ExecuteAudioRendererRendering(); } - [CommandHipc(12)] // 15.0.0+ + [CommandCmif(12)] // 15.0.0+ // SetVoiceDropParameter(f32 voiceDropParameter) public ResultCode SetVoiceDropParameter(ServiceCtx context) { @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return ResultCode.Success; } - [CommandHipc(13)] // 15.0.0+ + [CommandCmif(13)] // 15.0.0+ // GetVoiceDropParameter() -> f32 voiceDropParameter public ResultCode GetVoiceDropParameter(ServiceCtx context) { @@ -209,4 +209,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer } } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs index c9591c06b..80b54e8c7 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio _impl = impl; } - [CommandHipc(0)] + [CommandCmif(0)] // OpenAudioRenderer(nn::audio::detail::AudioRendererParameterInternal parameter, u64 workBufferSize, nn::applet::AppletResourceUserId appletResourceId, pid, handle<copy> workBuffer, handle<copy> processHandle) // -> object<nn::audio::detail::IAudioRenderer> public ResultCode OpenAudioRenderer(ServiceCtx context) @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return result; } - [CommandHipc(1)] + [CommandCmif(1)] // GetWorkBufferSize(nn::audio::detail::AudioRendererParameterInternal parameter) -> u64 workBufferSize public ResultCode GetAudioRendererWorkBufferSize(ServiceCtx context) { @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio } } - [CommandHipc(2)] + [CommandCmif(2)] // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object<nn::audio::detail::IAudioDevice> public ResultCode GetAudioDeviceService(ServiceCtx context) { @@ -96,7 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return result; } - [CommandHipc(4)] // 4.0.0+ + [CommandCmif(4)] // 4.0.0+ // GetAudioDeviceServiceWithRevisionInfo(s32 revision, nn::applet::AppletResourceUserId appletResourceId) -> object<nn::audio::detail::IAudioDevice> public ResultCode GetAudioDeviceServiceWithRevisionInfo(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs b/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs index 84c55d5ec..e94b31ca1 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs @@ -21,35 +21,35 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager _flags = flags; } - [CommandHipc(0)] + [CommandCmif(0)] // DecodeInterleavedOld(buffer<unknown, 5>) -> (u32, u32, buffer<unknown, 6>) public ResultCode DecodeInterleavedOld(ServiceCtx context) { return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false); } - [CommandHipc(2)] + [CommandCmif(2)] // DecodeInterleavedForMultiStreamOld(buffer<unknown, 5>) -> (u32, u32, buffer<unknown, 6>) public ResultCode DecodeInterleavedForMultiStreamOld(ServiceCtx context) { return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false); } - [CommandHipc(4)] // 6.0.0+ + [CommandCmif(4)] // 6.0.0+ // DecodeInterleavedWithPerfOld(buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>) public ResultCode DecodeInterleavedWithPerfOld(ServiceCtx context) { return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true); } - [CommandHipc(5)] // 6.0.0+ + [CommandCmif(5)] // 6.0.0+ // DecodeInterleavedForMultiStreamWithPerfOld(buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>) public ResultCode DecodeInterleavedForMultiStreamWithPerfOld(ServiceCtx context) { return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true); } - [CommandHipc(6)] // 6.0.0+ + [CommandCmif(6)] // 6.0.0+ // DecodeInterleavedWithPerfAndResetOld(bool reset, buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>) public ResultCode DecodeInterleavedWithPerfAndResetOld(ServiceCtx context) { @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset, withPerf: true); } - [CommandHipc(7)] // 6.0.0+ + [CommandCmif(7)] // 6.0.0+ // DecodeInterleavedForMultiStreamWithPerfAndResetOld(bool reset, buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>) public ResultCode DecodeInterleavedForMultiStreamWithPerfAndResetOld(ServiceCtx context) { @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset, withPerf: true); } - [CommandHipc(8)] // 7.0.0+ + [CommandCmif(8)] // 7.0.0+ // DecodeInterleaved(bool reset, buffer<unknown, 0x45>) -> (u32, u32, u64, buffer<unknown, 0x46>) public ResultCode DecodeInterleaved(ServiceCtx context) { @@ -76,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager return DecodeInterleavedInternal(context, _flags, reset, withPerf: true); } - [CommandHipc(9)] // 7.0.0+ + [CommandCmif(9)] // 7.0.0+ // DecodeInterleavedForMultiStream(bool reset, buffer<unknown, 0x45>) -> (u32, u32, u64, buffer<unknown, 0x46>) public ResultCode DecodeInterleavedForMultiStream(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs index 58680db85..8df8a38c9 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio { public IHardwareOpusDecoderManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // Initialize(bytes<8, 4>, u32, handle<copy>) -> object<nn::codec::detail::IHardwareOpusDecoder> public ResultCode Initialize(ServiceCtx context) { @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetWorkBufferSize(bytes<8, 4>) -> u32 public ResultCode GetWorkBufferSize(ServiceCtx context) { @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(2)] // 3.0.0+ + [CommandCmif(2)] // 3.0.0+ // InitializeForMultiStream(u32, handle<copy>, buffer<unknown<0x110>, 0x19>) -> object<nn::codec::detail::IHardwareOpusDecoder> public ResultCode InitializeForMultiStream(ServiceCtx context) { @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(3)] // 3.0.0+ + [CommandCmif(3)] // 3.0.0+ // GetWorkBufferSizeForMultiStream(buffer<unknown<0x110>, 0x19>) -> u32 public ResultCode GetWorkBufferSizeForMultiStream(ServiceCtx context) { @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(4)] // 12.0.0+ + [CommandCmif(4)] // 12.0.0+ // InitializeEx(OpusParametersEx, u32, handle<copy>) -> object<nn::codec::detail::IHardwareOpusDecoder> public ResultCode InitializeEx(ServiceCtx context) { @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(5)] // 12.0.0+ + [CommandCmif(5)] // 12.0.0+ // GetWorkBufferSizeEx(OpusParametersEx) -> u32 public ResultCode GetWorkBufferSizeEx(ServiceCtx context) { @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(6)] // 12.0.0+ + [CommandCmif(6)] // 12.0.0+ // InitializeForMultiStreamEx(u32, handle<copy>, buffer<unknown<0x118>, 0x19>) -> object<nn::codec::detail::IHardwareOpusDecoder> public ResultCode InitializeForMultiStreamEx(ServiceCtx context) { @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - [CommandHipc(7)] // 12.0.0+ + [CommandCmif(7)] // 12.0.0+ // GetWorkBufferSizeForMultiStreamEx(buffer<unknown<0x118>, 0x19>) -> u32 public ResultCode GetWorkBufferSizeForMultiStreamEx(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs index 937fe76c5..1437a8e1f 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat } } - [CommandHipc(0)] + [CommandCmif(0)] // CreateBcatService(pid) -> object<nn::bcat::detail::ipc::IBcatService> public ResultCode CreateBcatService(ServiceCtx context) { @@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // CreateDeliveryCacheStorageService(pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> public ResultCode CreateDeliveryCacheStorageService(ServiceCtx context) { @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat return (ResultCode)rc.Value; } - [CommandHipc(2)] + [CommandCmif(2)] // CreateDeliveryCacheStorageServiceWithApplicationId(nn::ApplicationId) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> public ResultCode CreateDeliveryCacheStorageServiceWithApplicationId(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs index 6c0073ed8..fb11cedad 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator { public IBcatService(ApplicationLaunchProperty applicationLaunchProperty) { } - [CommandHipc(10100)] + [CommandCmif(10100)] // RequestSyncDeliveryCache() -> object<nn::bcat::detail::ipc::IDeliveryCacheProgressService> public ResultCode RequestSyncDeliveryCache(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs index e7ccb6d09..575449779 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator } } - [CommandHipc(0)] + [CommandCmif(0)] // Open(nn::bcat::DirectoryName) public ResultCode Open(ServiceCtx context) { @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - [CommandHipc(1)] + [CommandCmif(1)] // Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>) public ResultCode Read(ServiceCtx context) { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator } } - [CommandHipc(2)] + [CommandCmif(2)] // GetCount() -> u32 public ResultCode GetCount(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs index c7706f921..5a9110e6a 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator } } - [CommandHipc(0)] + [CommandCmif(0)] // Open(nn::bcat::DirectoryName, nn::bcat::FileName) public ResultCode Open(ServiceCtx context) { @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - [CommandHipc(1)] + [CommandCmif(1)] // Read(u64) -> (u64, buffer<bytes, 6>) public ResultCode Read(ServiceCtx context) { @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator } } - [CommandHipc(2)] + [CommandCmif(2)] // GetSize() -> u64 public ResultCode GetSize(ServiceCtx context) { @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - [CommandHipc(3)] + [CommandCmif(3)] // GetDigest() -> nn::bcat::Digest public ResultCode GetDigest(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs index b176195db..1555f1707 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator _event = new KEvent(context.Device.System.KernelContext); } - [CommandHipc(0)] + [CommandCmif(0)] // GetEvent() -> handle<copy> public ResultCode GetEvent(ServiceCtx context) { @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetImpl() -> buffer<nn::bcat::detail::DeliveryCacheProgressImpl, 0x1a> public ResultCode GetImpl(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs index 32dd75d8e..be77226cb 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref baseService); } - [CommandHipc(0)] + [CommandCmif(0)] // CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService> public ResultCode CreateFileService(ServiceCtx context) { @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - [CommandHipc(1)] + [CommandCmif(1)] // CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService> public ResultCode CreateDirectoryService(ServiceCtx context) { @@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - [CommandHipc(10)] + [CommandCmif(10)] // EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>) public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs b/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs index 65535ea10..feff5a73f 100644 --- a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs +++ b/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth public IBluetoothDriver(ServiceCtx context) { } - [CommandHipc(46)] + [CommandCmif(46)] // InitializeBluetoothLe() -> handle<copy> public ResultCode InitializeBluetoothLe(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs b/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs index a175cd853..1a5e25a48 100644 --- a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs +++ b/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth { public IBluetoothUser(ServiceCtx context) { } - [CommandHipc(9)] + [CommandCmif(9)] // RegisterBleEvent(pid) -> handle<copy> public ResultCode RegisterBleEvent(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs b/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs index 026b5bf12..3c9938ad7 100644 --- a/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs +++ b/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser public IBtmUserCore() { } - [CommandHipc(0)] // 5.0.0+ + [CommandCmif(0)] // 5.0.0+ // AcquireBleScanEvent() -> (byte<1>, handle<copy>) public ResultCode AcquireBleScanEvent(ServiceCtx context) { @@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser return ResultCode.Success; } - [CommandHipc(17)] // 5.0.0+ + [CommandCmif(17)] // 5.0.0+ // AcquireBleConnectionEvent() -> (byte<1>, handle<copy>) public ResultCode AcquireBleConnectionEvent(ServiceCtx context) { @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser return ResultCode.Success; } - [CommandHipc(26)] // 5.0.0+ + [CommandCmif(26)] // 5.0.0+ // AcquireBleServiceDiscoveryEvent() -> (byte<1>, handle<copy>) public ResultCode AcquireBleServiceDiscoveryEvent(ServiceCtx context) { @@ -99,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser return ResultCode.Success; } - [CommandHipc(33)] // 5.0.0+ + [CommandCmif(33)] // 5.0.0+ // AcquireBleMtuConfigEvent() -> (byte<1>, handle<copy>) public ResultCode AcquireBleMtuConfigEvent(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs b/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs index 8b74b8b4e..b00f0a2f3 100644 --- a/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs +++ b/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager { public IBtmUser(ServiceCtx context) { } - [CommandHipc(0)] // 5.0.0+ + [CommandCmif(0)] // 5.0.0+ // GetCore() -> object<nn::btm::IBtmUserCore> public ResultCode GetCore(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs b/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs index cc370126c..af99232eb 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs @@ -9,14 +9,14 @@ namespace Ryujinx.HLE.HOS.Services.Caps { public IAlbumApplicationService(ServiceCtx context) { } - [CommandHipc(32)] // 7.0.0+ + [CommandCmif(32)] // 7.0.0+ // SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId) public ResultCode SetShimLibraryVersion(ServiceCtx context) { return context.Device.System.CaptureManager.SetShimLibraryVersion(context); } - [CommandHipc(102)] + [CommandCmif(102)] // GetAlbumFileList0AafeAruidDeprecated(pid, u16 content_type, u64 start_time, u64 end_time, nn::applet::AppletResourceUserId) -> (u64 count, buffer<ApplicationAlbumFileEntry, 0x6>) public ResultCode GetAlbumFileList0AafeAruidDeprecated(ServiceCtx context) { @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps return GetAlbumFileList(context); } - [CommandHipc(142)] + [CommandCmif(142)] // GetAlbumFileList3AaeAruid(pid, u16 content_type, u64 start_time, u64 end_time, nn::applet::AppletResourceUserId) -> (u64 count, buffer<ApplicationAlbumFileEntry, 0x6>) public ResultCode GetAlbumFileList3AaeAruid(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs b/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs index 3b565b67a..b16a41224 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps { public IAlbumControlService(ServiceCtx context) { } - [CommandHipc(33)] // 7.0.0+ + [CommandCmif(33)] // 7.0.0+ // SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId) public ResultCode SetShimLibraryVersion(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs index e0c65f440..a3501286c 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs @@ -8,14 +8,14 @@ namespace Ryujinx.HLE.HOS.Services.Caps { public IScreenShotApplicationService(ServiceCtx context) { } - [CommandHipc(32)] // 7.0.0+ + [CommandCmif(32)] // 7.0.0+ // SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId) public ResultCode SetShimLibraryVersion(ServiceCtx context) { return context.Device.System.CaptureManager.SetShimLibraryVersion(context); } - [CommandHipc(203)] + [CommandCmif(203)] // SaveScreenShotEx0(bytes<0x40> ScreenShotAttribute, u32 unknown, u64 AppletResourceUserId, pid, buffer<bytes, 0x45> ScreenshotData) -> bytes<0x20> ApplicationAlbumEntry public ResultCode SaveScreenShotEx0(ServiceCtx context) { @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps return resultCode; } - [CommandHipc(205)] // 8.0.0+ + [CommandCmif(205)] // 8.0.0+ // SaveScreenShotEx1(bytes<0x40> ScreenShotAttribute, u32 unknown, u64 AppletResourceUserId, pid, buffer<bytes, 0x15> ApplicationData, buffer<bytes, 0x45> ScreenshotData) -> bytes<0x20> ApplicationAlbumEntry public ResultCode SaveScreenShotEx1(ServiceCtx context) { @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps return resultCode; } - [CommandHipc(210)] + [CommandCmif(210)] // SaveScreenShotEx2(bytes<0x40> ScreenShotAttribute, u32 unknown, u64 AppletResourceUserId, buffer<bytes, 0x15> UserIdList, buffer<bytes, 0x45> ScreenshotData) -> bytes<0x20> ApplicationAlbumEntry public ResultCode SaveScreenShotEx2(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/CommandHIpcAttribute.cs b/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs similarity index 63% rename from Ryujinx.HLE/HOS/Services/CommandHIpcAttribute.cs rename to Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs index ecdb75616..3b3279ab6 100644 --- a/Ryujinx.HLE/HOS/Services/CommandHIpcAttribute.cs +++ b/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs @@ -3,10 +3,10 @@ namespace Ryujinx.HLE.HOS.Services { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - class CommandHipcAttribute : Attribute + class CommandCmifAttribute : Attribute { public readonly int Id; - public CommandHipcAttribute(int id) => Id = id; + public CommandCmifAttribute(int id) => Id = id; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index c884e8803..aaa5c873a 100644 --- a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal { public IService(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // ThrowFatal(u64 result_code, u64 pid) public ResultCode ThrowFatal(ServiceCtx context) { @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal return ThrowFatalWithCpuContextImpl(context, resultCode, pid, FatalPolicy.ErrorReportAndErrorScreen, null); } - [CommandHipc(1)] + [CommandCmif(1)] // ThrowFatalWithPolicy(u64 result_code, u32 fatal_policy, u64 pid) public ResultCode ThrowFatalWithPolicy(ServiceCtx context) { @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal return ThrowFatalWithCpuContextImpl(context, resultCode, pid, fatalPolicy, null); } - [CommandHipc(2)] + [CommandCmif(2)] // ThrowFatalWithCpuContext(u64 result_code, u32 fatal_policy, u64 pid, buffer<bytes, 0x15> cpu_context) public ResultCode ThrowFatalWithCpuContext(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs index 4e73c3f2e..d5258a824 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend _permissionLevel = permissionLevel; } - [CommandHipc(0)] + [CommandCmif(0)] // CreateFriendService() -> object<nn::friends::detail::ipc::IFriendService> public ResultCode CreateFriendService(ServiceCtx context) { @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend return ResultCode.Success; } - [CommandHipc(1)] // 2.0.0+ + [CommandCmif(1)] // 2.0.0+ // CreateNotificationService(nn::account::Uid userId) -> object<nn::friends::detail::ipc::INotificationService> public ResultCode CreateNotificationService(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend return ResultCode.Success; } - [CommandHipc(2)] // 4.0.0+ + [CommandCmif(2)] // 4.0.0+ // CreateDaemonSuspendSessionService() -> object<nn::friends::detail::ipc::IDaemonSuspendSessionService> public ResultCode CreateDaemonSuspendSessionService(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 4317c8f61..2858aa468 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator _permissionLevel = permissionLevel; } - [CommandHipc(0)] + [CommandCmif(0)] // GetCompletionEvent() -> handle<copy> public ResultCode GetCompletionEvent(ServiceCtx context) { @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // nn::friends::Cancel() public ResultCode Cancel(ServiceCtx context) { @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10100)] + [CommandCmif(10100)] // nn::friends::GetFriendListIds(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid) // -> int outCount, array<nn::account::NetworkServiceAccountId, 0xa> public ResultCode GetFriendListIds(ServiceCtx context) @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10101)] + [CommandCmif(10101)] // nn::friends::GetFriendList(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid) // -> int outCount, array<nn::friends::detail::FriendImpl, 0x6> public ResultCode GetFriendList(ServiceCtx context) @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10120)] // 10.0.0+ + [CommandCmif(10120)] // 10.0.0+ // nn::friends::IsFriendListCacheAvailable(nn::account::Uid userId) -> bool public ResultCode IsFriendListCacheAvailable(ServiceCtx context) { @@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10121)] // 10.0.0+ + [CommandCmif(10121)] // 10.0.0+ // nn::friends::EnsureFriendListAvailable(nn::account::Uid userId) public ResultCode EnsureFriendListAvailable(ServiceCtx context) { @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10400)] + [CommandCmif(10400)] // nn::friends::GetBlockedUserListIds(int offset, nn::account::Uid userId) -> (u32, buffer<nn::account::NetworkServiceAccountId, 0xa>) public ResultCode GetBlockedUserListIds(ServiceCtx context) { @@ -187,7 +187,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10600)] + [CommandCmif(10600)] // nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId) public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context) { @@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10601)] + [CommandCmif(10601)] // nn::friends::DeclareCloseOnlinePlaySession(nn::account::Uid userId) public ResultCode DeclareCloseOnlinePlaySession(ServiceCtx context) { @@ -223,7 +223,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10610)] + [CommandCmif(10610)] // nn::friends::UpdateUserPresence(nn::account::Uid, u64, pid, buffer<nn::friends::detail::UserPresenceImpl, 0x19>) public ResultCode UpdateUserPresence(ServiceCtx context) { @@ -247,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10700)] + [CommandCmif(10700)] // nn::friends::GetPlayHistoryRegistrationKey(b8 unknown, nn::account::Uid) -> buffer<nn::friends::PlayHistoryRegistrationKey, 0x1a> public ResultCode GetPlayHistoryRegistrationKey(ServiceCtx context) { @@ -309,7 +309,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(10702)] + [CommandCmif(10702)] // nn::friends::AddPlayHistory(nn::account::Uid, u64, pid, buffer<nn::friends::PlayHistoryRegistrationKey, 0x19>, buffer<nn::friends::InAppScreenName, 0x19>, buffer<nn::friends::InAppScreenName, 0x19>) public ResultCode AddPlayHistory(ServiceCtx context) { @@ -349,4 +349,4 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs index 65cbd38e7..063750c6b 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator NotificationEventHandler.Instance.RegisterNotificationService(this); } - [CommandHipc(0)] //2.0.0+ + [CommandCmif(0)] //2.0.0+ // nn::friends::detail::ipc::INotificationService::GetEvent() -> handle<copy> public ResultCode GetEvent(ServiceCtx context) { @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(1)] //2.0.0+ + [CommandCmif(1)] //2.0.0+ // nn::friends::detail::ipc::INotificationService::Clear() public ResultCode Clear(ServiceCtx context) { @@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } - [CommandHipc(2)] // 2.0.0+ + [CommandCmif(2)] // 2.0.0+ // nn::friends::detail::ipc::INotificationService::Pop() -> nn::friends::detail::ipc::SizedNotificationInfo public ResultCode Pop(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs index bfe13592a..b97594498 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy _baseDirectory = SharedRef<LibHac.FsSrv.Sf.IDirectory>.CreateMove(ref directory); } - [CommandHipc(0)] + [CommandCmif(0)] // Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries) public ResultCode Read(ServiceCtx context) { @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy } } - [CommandHipc(1)] + [CommandCmif(1)] // GetEntryCount() -> u64 public ResultCode GetEntryCount(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs index 878fcacf6..4bc58ae5f 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy _baseFile = SharedRef<LibHac.FsSrv.Sf.IFile>.CreateMove(ref baseFile); } - [CommandHipc(0)] + [CommandCmif(0)] // Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> out_buf) public ResultCode Read(ServiceCtx context) { @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy } } - [CommandHipc(1)] + [CommandCmif(1)] // Write(u32 writeOption, u64 offset, u64 size, buffer<u8, 0x45, 0>) public ResultCode Write(ServiceCtx context) { @@ -57,14 +57,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_baseFile.Get.Write(offset, new InBuffer(data), size, writeOption).Value; } - [CommandHipc(2)] + [CommandCmif(2)] // Flush() public ResultCode Flush(ServiceCtx context) { return (ResultCode)_baseFile.Get.Flush().Value; } - [CommandHipc(3)] + [CommandCmif(3)] // SetSize(u64 size) public ResultCode SetSize(ServiceCtx context) { @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_baseFile.Get.SetSize(size).Value; } - [CommandHipc(4)] + [CommandCmif(4)] // GetSize() -> u64 fileSize public ResultCode GetSize(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs index 623f1371e..9effa79dc 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return SharedRef<LibHac.FsSrv.Sf.IFileSystem>.CreateCopy(in _fileSystem); } - [CommandHipc(0)] + [CommandCmif(0)] // CreateFile(u32 createOption, u64 size, buffer<bytes<0x301>, 0x19, 0x301> path) public ResultCode CreateFile(ServiceCtx context) { @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.CreateFile(in name, size, createOption).Value; } - [CommandHipc(1)] + [CommandCmif(1)] // DeleteFile(buffer<bytes<0x301>, 0x19, 0x301> path) public ResultCode DeleteFile(ServiceCtx context) { @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.DeleteFile(in name).Value; } - [CommandHipc(2)] + [CommandCmif(2)] // CreateDirectory(buffer<bytes<0x301>, 0x19, 0x301> path) public ResultCode CreateDirectory(ServiceCtx context) { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.CreateDirectory(in name).Value; } - [CommandHipc(3)] + [CommandCmif(3)] // DeleteDirectory(buffer<bytes<0x301>, 0x19, 0x301> path) public ResultCode DeleteDirectory(ServiceCtx context) { @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.DeleteDirectory(in name).Value; } - [CommandHipc(4)] + [CommandCmif(4)] // DeleteDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path) public ResultCode DeleteDirectoryRecursively(ServiceCtx context) { @@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.DeleteDirectoryRecursively(in name).Value; } - [CommandHipc(5)] + [CommandCmif(5)] // RenameFile(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath) public ResultCode RenameFile(ServiceCtx context) { @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.RenameFile(in currentName, in newName).Value; } - [CommandHipc(6)] + [CommandCmif(6)] // RenameDirectory(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath) public ResultCode RenameDirectory(ServiceCtx context) { @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.RenameDirectory(in currentName, in newName).Value; } - [CommandHipc(7)] + [CommandCmif(7)] // GetEntryType(buffer<bytes<0x301>, 0x19, 0x301> path) -> nn::fssrv::sf::DirectoryEntryType public ResultCode GetEntryType(ServiceCtx context) { @@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)result.Value; } - [CommandHipc(8)] + [CommandCmif(8)] // OpenFile(u32 mode, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFile> file public ResultCode OpenFile(ServiceCtx context) { @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)result.Value; } - [CommandHipc(9)] + [CommandCmif(9)] // OpenDirectory(u32 filter_flags, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IDirectory> directory public ResultCode OpenDirectory(ServiceCtx context) { @@ -144,14 +144,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)result.Value; } - [CommandHipc(10)] + [CommandCmif(10)] // Commit() public ResultCode Commit(ServiceCtx context) { return (ResultCode)_fileSystem.Get.Commit().Value; } - [CommandHipc(11)] + [CommandCmif(11)] // GetFreeSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalFreeSpace public ResultCode GetFreeSpaceSize(ServiceCtx context) { @@ -164,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)result.Value; } - [CommandHipc(12)] + [CommandCmif(12)] // GetTotalSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalSize public ResultCode GetTotalSpaceSize(ServiceCtx context) { @@ -177,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)result.Value; } - [CommandHipc(13)] + [CommandCmif(13)] // CleanDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path) public ResultCode CleanDirectoryRecursively(ServiceCtx context) { @@ -186,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)_fileSystem.Get.CleanDirectoryRecursively(in name).Value; } - [CommandHipc(14)] + [CommandCmif(14)] // GetFileTimeStampRaw(buffer<bytes<0x301>, 0x19, 0x301> path) -> bytes<0x20> timestamp public ResultCode GetFileTimeStampRaw(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs index 5359cadd7..54c7b800b 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy _baseStorage = SharedRef<LibHac.FsSrv.Sf.IStorage>.CreateMove(ref baseStorage); } - [CommandHipc(0)] + [CommandCmif(0)] // Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer public ResultCode Read(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetSize() -> u64 size public ResultCode GetSize(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs b/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs index f069fa3e0..2a40aea4f 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs _baseOperator = SharedRef<LibHac.FsSrv.Sf.IDeviceOperator>.CreateMove(ref baseOperator); } - [CommandHipc(0)] + [CommandCmif(0)] // IsSdCardInserted() -> b8 is_inserted public ResultCode IsSdCardInserted(ServiceCtx context) { @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)result.Value; } - [CommandHipc(200)] + [CommandCmif(200)] // IsGameCardInserted() -> b8 is_inserted public ResultCode IsGameCardInserted(ServiceCtx context) { @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)result.Value; } - [CommandHipc(202)] + [CommandCmif(202)] // GetGameCardHandle() -> u32 gamecard_handle public ResultCode GetGameCardHandle(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 1b63f362e..e961e9b18 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs _baseFileSystemProxy = applicationClient.Fs.Impl.GetFileSystemProxyServiceObject(); } - [CommandHipc(1)] + [CommandCmif(1)] // SetCurrentProcess(u64, pid) public ResultCode SetCurrentProcess(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(8)] + [CommandCmif(8)] // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer<bytes<0x301>, 0x19, 0x301> path) // -> object<nn::fssrv::sf::IFileSystem> contentFs public ResultCode OpenFileSystemWithId(ServiceCtx context) @@ -99,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.InvalidInput; } - [CommandHipc(11)] + [CommandCmif(11)] // OpenBisFileSystem(nn::fssrv::sf::Partition partitionID, buffer<bytes<0x301>, 0x19, 0x301>) -> object<nn::fssrv::sf::IFileSystem> Bis public ResultCode OpenBisFileSystem(ServiceCtx context) { @@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(12)] + [CommandCmif(12)] // OpenBisStorage(u32 partitionId) -> object<nn::fssrv::sf::IStorage> bisStorage public ResultCode OpenBisStorage(ServiceCtx context) { @@ -131,14 +131,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(13)] + [CommandCmif(13)] // InvalidateBisCache() -> () public ResultCode InvalidateBisCache(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.InvalidateBisCache().Value; } - [CommandHipc(18)] + [CommandCmif(18)] // OpenSdCardFileSystem() -> object<nn::fssrv::sf::IFileSystem> public ResultCode OpenSdCardFileSystem(ServiceCtx context) { @@ -152,14 +152,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(19)] + [CommandCmif(19)] // FormatSdCardFileSystem() -> () public ResultCode FormatSdCardFileSystem(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.FormatSdCardFileSystem().Value; } - [CommandHipc(21)] + [CommandCmif(21)] // DeleteSaveDataFileSystem(u64 saveDataId) -> () public ResultCode DeleteSaveDataFileSystem(ServiceCtx context) { @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.DeleteSaveDataFileSystem(saveDataId).Value; } - [CommandHipc(22)] + [CommandCmif(22)] // CreateSaveDataFileSystem(nn::fs::SaveDataAttribute attribute, nn::fs::SaveDataCreationInfo creationInfo, nn::fs::SaveDataMetaInfo metaInfo) -> () public ResultCode CreateSaveDataFileSystem(ServiceCtx context) { @@ -179,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo).Value; } - [CommandHipc(23)] + [CommandCmif(23)] // CreateSaveDataFileSystemBySystemSaveDataId(nn::fs::SaveDataAttribute attribute, nn::fs::SaveDataCreationInfo creationInfo) -> () public ResultCode CreateSaveDataFileSystemBySystemSaveDataId(ServiceCtx context) { @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in creationInfo).Value; } - [CommandHipc(24)] + [CommandCmif(24)] // RegisterSaveDataFileSystemAtomicDeletion(buffer<u64, 5> saveDataIds) -> () public ResultCode RegisterSaveDataFileSystemAtomicDeletion(ServiceCtx context) { @@ -199,7 +199,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.RegisterSaveDataFileSystemAtomicDeletion(new InBuffer(saveIdBuffer)).Value; } - [CommandHipc(25)] + [CommandCmif(25)] // DeleteSaveDataFileSystemBySaveDataSpaceId(u8 spaceId, u64 saveDataId) -> () public ResultCode DeleteSaveDataFileSystemBySaveDataSpaceId(ServiceCtx context) { @@ -209,14 +209,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId).Value; } - [CommandHipc(26)] + [CommandCmif(26)] // FormatSdCardDryRun() -> () public ResultCode FormatSdCardDryRun(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.FormatSdCardDryRun().Value; } - [CommandHipc(27)] + [CommandCmif(27)] // IsExFatSupported() -> (u8 isSupported) public ResultCode IsExFatSupported(ServiceCtx context) { @@ -228,7 +228,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(28)] + [CommandCmif(28)] // DeleteSaveDataFileSystemBySaveDataAttribute(u8 spaceId, nn::fs::SaveDataAttribute attribute) -> () public ResultCode DeleteSaveDataFileSystemBySaveDataAttribute(ServiceCtx context) { @@ -238,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(spaceId, in attribute).Value; } - [CommandHipc(30)] + [CommandCmif(30)] // OpenGameCardStorage(u32 handle, u32 partitionId) -> object<nn::fssrv::sf::IStorage> public ResultCode OpenGameCardStorage(ServiceCtx context) { @@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(31)] + [CommandCmif(31)] // OpenGameCardFileSystem(u32 handle, u32 partitionId) -> object<nn::fssrv::sf::IFileSystem> public ResultCode OpenGameCardFileSystem(ServiceCtx context) { @@ -270,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(32)] + [CommandCmif(32)] // ExtendSaveDataFileSystem(u8 spaceId, u64 saveDataId, s64 dataSize, s64 journalSize) -> () public ResultCode ExtendSaveDataFileSystem(ServiceCtx context) { @@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.ExtendSaveDataFileSystem(spaceId, saveDataId, dataSize, journalSize).Value; } - [CommandHipc(33)] + [CommandCmif(33)] // DeleteCacheStorage(u16 index) -> () public ResultCode DeleteCacheStorage(ServiceCtx context) { @@ -291,7 +291,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.DeleteCacheStorage(index).Value; } - [CommandHipc(34)] + [CommandCmif(34)] // GetCacheStorageSize(u16 index) -> (s64 dataSize, s64 journalSize) public ResultCode GetCacheStorageSize(ServiceCtx context) { @@ -306,7 +306,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(35)] + [CommandCmif(35)] // CreateSaveDataFileSystemWithHashSalt(nn::fs::SaveDataAttribute attribute, nn::fs::SaveDataCreationInfo creationInfo, nn::fs::SaveDataMetaInfo metaInfo nn::fs::HashSalt hashSalt) -> () public ResultCode CreateSaveDataFileSystemWithHashSalt(ServiceCtx context) { @@ -318,7 +318,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, in metaCreateInfo, in hashSalt).Value; } - [CommandHipc(37)] // 14.0.0+ + [CommandCmif(37)] // 14.0.0+ // CreateSaveDataFileSystemWithCreationInfo2(buffer<nn::fs::SaveDataCreationInfo2, 25> creationInfo) -> () public ResultCode CreateSaveDataFileSystemWithCreationInfo2(ServiceCtx context) { @@ -329,7 +329,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemWithCreationInfo2(in creationInfo).Value; } - [CommandHipc(51)] + [CommandCmif(51)] // OpenSaveDataFileSystem(u8 spaceId, nn::fs::SaveDataAttribute attribute) -> object<nn::fssrv::sf::IFileSystem> saveDataFs public ResultCode OpenSaveDataFileSystem(ServiceCtx context) { @@ -345,7 +345,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(52)] + [CommandCmif(52)] // OpenSaveDataFileSystemBySystemSaveDataId(u8 spaceId, nn::fs::SaveDataAttribute attribute) -> object<nn::fssrv::sf::IFileSystem> systemSaveDataFs public ResultCode OpenSaveDataFileSystemBySystemSaveDataId(ServiceCtx context) { @@ -361,7 +361,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(53)] + [CommandCmif(53)] // OpenReadOnlySaveDataFileSystem(u8 spaceId, nn::fs::SaveDataAttribute attribute) -> object<nn::fssrv::sf::IFileSystem> public ResultCode OpenReadOnlySaveDataFileSystem(ServiceCtx context) { @@ -377,7 +377,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(57)] + [CommandCmif(57)] // ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(u8 spaceId, u64 saveDataId) -> (buffer<nn::fs::SaveDataExtraData, 6> extraData) public ResultCode ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(ServiceCtx context) { @@ -395,7 +395,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(58)] + [CommandCmif(58)] // ReadSaveDataFileSystemExtraData(u64 saveDataId) -> (buffer<nn::fs::SaveDataExtraData, 6> extraData) public ResultCode ReadSaveDataFileSystemExtraData(ServiceCtx context) { @@ -412,7 +412,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(59)] + [CommandCmif(59)] // WriteSaveDataFileSystemExtraData(u8 spaceId, u64 saveDataId, buffer<nn::fs::SaveDataExtraData, 5> extraData) -> () public ResultCode WriteSaveDataFileSystemExtraData(ServiceCtx context) { @@ -425,7 +425,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.WriteSaveDataFileSystemExtraData(saveDataId, spaceId, new InBuffer(extraDataBuffer)).Value; } - [CommandHipc(60)] + [CommandCmif(60)] // OpenSaveDataInfoReader() -> object<nn::fssrv::sf::ISaveDataInfoReader> public ResultCode OpenSaveDataInfoReader(ServiceCtx context) { @@ -439,7 +439,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(61)] + [CommandCmif(61)] // OpenSaveDataInfoReaderBySaveDataSpaceId(u8 spaceId) -> object<nn::fssrv::sf::ISaveDataInfoReader> public ResultCode OpenSaveDataInfoReaderBySaveDataSpaceId(ServiceCtx context) { @@ -454,7 +454,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(62)] + [CommandCmif(62)] // OpenSaveDataInfoReaderOnlyCacheStorage() -> object<nn::fssrv::sf::ISaveDataInfoReader> public ResultCode OpenSaveDataInfoReaderOnlyCacheStorage(ServiceCtx context) { @@ -468,7 +468,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(64)] + [CommandCmif(64)] // OpenSaveDataInternalStorageFileSystem(u8 spaceId, u64 saveDataId) -> object<nn::fssrv::sf::ISaveDataInfoReader> public ResultCode OpenSaveDataInternalStorageFileSystem(ServiceCtx context) { @@ -484,7 +484,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(65)] + [CommandCmif(65)] // UpdateSaveDataMacForDebug(u8 spaceId, u64 saveDataId) -> () public ResultCode UpdateSaveDataMacForDebug(ServiceCtx context) { @@ -494,7 +494,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.UpdateSaveDataMacForDebug(spaceId, saveDataId).Value; } - [CommandHipc(66)] + [CommandCmif(66)] public ResultCode WriteSaveDataFileSystemExtraDataWithMask(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -509,7 +509,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.WriteSaveDataFileSystemExtraDataWithMask(saveDataId, spaceId, new InBuffer(extraDataBuffer), new InBuffer(maskBuffer)).Value; } - [CommandHipc(67)] + [CommandCmif(67)] public ResultCode FindSaveDataWithFilter(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -529,7 +529,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(68)] + [CommandCmif(68)] public ResultCode OpenSaveDataInfoReaderWithFilter(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(69)] + [CommandCmif(69)] public ResultCode ReadSaveDataFileSystemExtraDataBySaveDataAttribute(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -561,7 +561,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(70)] + [CommandCmif(70)] public ResultCode WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -576,7 +576,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in attribute, spaceId, new InBuffer(extraDataBuffer), new InBuffer(maskBuffer)).Value; } - [CommandHipc(71)] + [CommandCmif(71)] public ResultCode ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -596,7 +596,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(80)] + [CommandCmif(80)] public ResultCode OpenSaveDataMetaFile(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt32(); @@ -612,7 +612,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(84)] + [CommandCmif(84)] public ResultCode ListAccessibleSaveDataOwnerId(ServiceCtx context) { int startIndex = context.RequestData.ReadInt32(); @@ -630,7 +630,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(100)] + [CommandCmif(100)] public ResultCode OpenImageDirectoryFileSystem(ServiceCtx context) { ImageDirectoryId directoryId = (ImageDirectoryId)context.RequestData.ReadInt32(); @@ -644,7 +644,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(101)] + [CommandCmif(101)] public ResultCode OpenBaseFileSystem(ServiceCtx context) { BaseFileSystemId fileSystemId = (BaseFileSystemId)context.RequestData.ReadInt32(); @@ -658,7 +658,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(110)] + [CommandCmif(110)] public ResultCode OpenContentStorageFileSystem(ServiceCtx context) { ContentStorageId contentStorageId = (ContentStorageId)context.RequestData.ReadInt32(); @@ -672,7 +672,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(120)] + [CommandCmif(120)] public ResultCode OpenCloudBackupWorkStorageFileSystem(ServiceCtx context) { CloudBackupWorkStorageId storageId = (CloudBackupWorkStorageId)context.RequestData.ReadInt32(); @@ -686,7 +686,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(130)] + [CommandCmif(130)] public ResultCode OpenCustomStorageFileSystem(ServiceCtx context) { CustomStorageId customStorageId = (CustomStorageId)context.RequestData.ReadInt32(); @@ -700,7 +700,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(200)] + [CommandCmif(200)] // OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage public ResultCode OpenDataStorageByCurrentProcess(ServiceCtx context) { @@ -713,7 +713,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(202)] + [CommandCmif(202)] // OpenDataStorageByDataId(u8 storageId, nn::ncm::DataId dataId) -> object<nn::fssrv::sf::IStorage> dataStorage public ResultCode OpenDataStorageByDataId(ServiceCtx context) { @@ -789,7 +789,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs throw new FileNotFoundException($"System archive with titleid {titleId:x16} was not found on Storage {storageId}. Found in {installedStorage}."); } - [CommandHipc(203)] + [CommandCmif(203)] // OpenPatchDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> public ResultCode OpenPatchDataStorageByCurrentProcess(ServiceCtx context) { @@ -802,7 +802,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(205)] + [CommandCmif(205)] // OpenDataStorageWithProgramIndex(u8 program_index) -> object<nn::fssrv::sf::IStorage> public ResultCode OpenDataStorageWithProgramIndex(ServiceCtx context) { @@ -822,7 +822,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(400)] + [CommandCmif(400)] // OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage public ResultCode OpenDeviceOperator(ServiceCtx context) { @@ -836,7 +836,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(601)] + [CommandCmif(601)] public ResultCode QuerySaveDataTotalSize(ServiceCtx context) { long dataSize = context.RequestData.ReadInt64(); @@ -850,13 +850,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(511)] + [CommandCmif(511)] public ResultCode NotifySystemDataUpdateEvent(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.NotifySystemDataUpdateEvent().Value; } - [CommandHipc(523)] + [CommandCmif(523)] public ResultCode SimulateDeviceDetectionEvent(ServiceCtx context) { bool signalEvent = context.RequestData.ReadBoolean(); @@ -867,7 +867,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.SimulateDeviceDetectionEvent(port, mode, signalEvent).Value; } - [CommandHipc(602)] + [CommandCmif(602)] public ResultCode VerifySaveDataFileSystem(ServiceCtx context) { ulong saveDataId = context.RequestData.ReadUInt64(); @@ -878,7 +878,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.VerifySaveDataFileSystem(saveDataId, new OutBuffer(readBuffer)).Value; } - [CommandHipc(603)] + [CommandCmif(603)] public ResultCode CorruptSaveDataFileSystem(ServiceCtx context) { ulong saveDataId = context.RequestData.ReadUInt64(); @@ -886,7 +886,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CorruptSaveDataFileSystem(saveDataId).Value; } - [CommandHipc(604)] + [CommandCmif(604)] public ResultCode CreatePaddingFile(ServiceCtx context) { long size = context.RequestData.ReadInt64(); @@ -894,13 +894,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CreatePaddingFile(size).Value; } - [CommandHipc(605)] + [CommandCmif(605)] public ResultCode DeleteAllPaddingFiles(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.DeleteAllPaddingFiles().Value; } - [CommandHipc(606)] + [CommandCmif(606)] public ResultCode GetRightsId(ServiceCtx context) { StorageId storageId = (StorageId)context.RequestData.ReadInt64(); @@ -914,7 +914,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(607)] + [CommandCmif(607)] public ResultCode RegisterExternalKey(ServiceCtx context) { RightsId rightsId = context.RequestData.ReadStruct<RightsId>(); @@ -923,13 +923,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.RegisterExternalKey(in rightsId, in accessKey).Value; } - [CommandHipc(608)] + [CommandCmif(608)] public ResultCode UnregisterAllExternalKey(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.UnregisterAllExternalKey().Value; } - [CommandHipc(609)] + [CommandCmif(609)] public ResultCode GetRightsIdByPath(ServiceCtx context) { ref readonly var path = ref FileSystemProxyHelper.GetFspPath(context); @@ -942,7 +942,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(610)] + [CommandCmif(610)] public ResultCode GetRightsIdAndKeyGenerationByPath(ServiceCtx context) { ref readonly var path = ref FileSystemProxyHelper.GetFspPath(context); @@ -957,7 +957,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(611)] + [CommandCmif(611)] public ResultCode SetCurrentPosixTimeWithTimeDifference(ServiceCtx context) { int timeDifference = context.RequestData.ReadInt32(); @@ -967,7 +967,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.SetCurrentPosixTimeWithTimeDifference(time, timeDifference).Value; } - [CommandHipc(612)] + [CommandCmif(612)] public ResultCode GetFreeSpaceSizeForSaveData(ServiceCtx context) { SaveDataSpaceId spaceId = context.RequestData.ReadStruct<SaveDataSpaceId>(); @@ -980,7 +980,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(613)] + [CommandCmif(613)] public ResultCode VerifySaveDataFileSystemBySaveDataSpaceId(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -992,7 +992,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.VerifySaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId, new OutBuffer(readBuffer)).Value; } - [CommandHipc(614)] + [CommandCmif(614)] public ResultCode CorruptSaveDataFileSystemBySaveDataSpaceId(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -1001,7 +1001,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CorruptSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId).Value; } - [CommandHipc(615)] + [CommandCmif(615)] public ResultCode QuerySaveDataInternalStorageTotalSize(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -1015,7 +1015,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(616)] + [CommandCmif(616)] public ResultCode GetSaveDataCommitId(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -1029,7 +1029,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(617)] + [CommandCmif(617)] public ResultCode UnregisterExternalKey(ServiceCtx context) { RightsId rightsId = context.RequestData.ReadStruct<RightsId>(); @@ -1037,7 +1037,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.UnregisterExternalKey(in rightsId).Value; } - [CommandHipc(620)] + [CommandCmif(620)] public ResultCode SetSdCardEncryptionSeed(ServiceCtx context) { EncryptionSeed encryptionSeed = context.RequestData.ReadStruct<EncryptionSeed>(); @@ -1045,7 +1045,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.SetSdCardEncryptionSeed(in encryptionSeed).Value; } - [CommandHipc(630)] + [CommandCmif(630)] // SetSdCardAccessibility(u8 isAccessible) public ResultCode SetSdCardAccessibility(ServiceCtx context) { @@ -1054,7 +1054,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.SetSdCardAccessibility(isAccessible).Value; } - [CommandHipc(631)] + [CommandCmif(631)] // IsSdCardAccessible() -> u8 isAccessible public ResultCode IsSdCardAccessible(ServiceCtx context) { @@ -1066,7 +1066,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(702)] + [CommandCmif(702)] public ResultCode IsAccessFailureDetected(ServiceCtx context) { ulong processId = context.RequestData.ReadUInt64(); @@ -1079,7 +1079,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(710)] + [CommandCmif(710)] public ResultCode ResolveAccessFailure(ServiceCtx context) { ulong processId = context.RequestData.ReadUInt64(); @@ -1087,7 +1087,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.ResolveAccessFailure(processId).Value; } - [CommandHipc(720)] + [CommandCmif(720)] public ResultCode AbandonAccessFailure(ServiceCtx context) { ulong processId = context.RequestData.ReadUInt64(); @@ -1095,7 +1095,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.AbandonAccessFailure(processId).Value; } - [CommandHipc(800)] + [CommandCmif(800)] public ResultCode GetAndClearErrorInfo(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.GetAndClearErrorInfo(out FileSystemProxyErrorInfo errorInfo); @@ -1106,7 +1106,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(810)] + [CommandCmif(810)] public ResultCode RegisterProgramIndexMapInfo(ServiceCtx context) { int programCount = context.RequestData.ReadInt32(); @@ -1117,7 +1117,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.RegisterProgramIndexMapInfo(new InBuffer(mapInfoBuffer), programCount).Value; } - [CommandHipc(1000)] + [CommandCmif(1000)] public ResultCode SetBisRootForHost(ServiceCtx context) { BisPartitionId partitionId = (BisPartitionId)context.RequestData.ReadInt32(); @@ -1126,7 +1126,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.SetBisRootForHost(partitionId, in path).Value; } - [CommandHipc(1001)] + [CommandCmif(1001)] public ResultCode SetSaveDataSize(ServiceCtx context) { long dataSize = context.RequestData.ReadInt64(); @@ -1135,7 +1135,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.SetSaveDataSize(dataSize, journalSize).Value; } - [CommandHipc(1002)] + [CommandCmif(1002)] public ResultCode SetSaveDataRootPath(ServiceCtx context) { ref readonly var path = ref FileSystemProxyHelper.GetFspPath(context); @@ -1143,13 +1143,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.SetSaveDataRootPath(in path).Value; } - [CommandHipc(1003)] + [CommandCmif(1003)] public ResultCode DisableAutoSaveDataCreation(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.DisableAutoSaveDataCreation().Value; } - [CommandHipc(1004)] + [CommandCmif(1004)] // SetGlobalAccessLogMode(u32 mode) public ResultCode SetGlobalAccessLogMode(ServiceCtx context) { @@ -1160,7 +1160,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(1005)] + [CommandCmif(1005)] // GetGlobalAccessLogMode() -> u32 logMode public ResultCode GetGlobalAccessLogMode(ServiceCtx context) { @@ -1171,7 +1171,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(1006)] + [CommandCmif(1006)] // OutputAccessLogToSdCard(buffer<bytes, 5> log_text) public ResultCode OutputAccessLogToSdCard(ServiceCtx context) { @@ -1183,13 +1183,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(1007)] + [CommandCmif(1007)] public ResultCode RegisterUpdatePartition(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.RegisterUpdatePartition().Value; } - [CommandHipc(1008)] + [CommandCmif(1008)] public ResultCode OpenRegisteredUpdatePartition(ServiceCtx context) { using var fileSystem = new SharedRef<IFileSystem>(); @@ -1202,7 +1202,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(1009)] + [CommandCmif(1009)] public ResultCode GetAndClearMemoryReportInfo(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo); @@ -1213,7 +1213,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(1011)] + [CommandCmif(1011)] public ResultCode GetProgramIndexForAccessLog(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.GetProgramIndexForAccessLog(out int programIndex, out int programCount); @@ -1225,7 +1225,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(1012)] + [CommandCmif(1012)] public ResultCode GetFsStackUsage(ServiceCtx context) { FsStackUsageThreadType threadType = context.RequestData.ReadStruct<FsStackUsageThreadType>(); @@ -1238,25 +1238,25 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.Success; } - [CommandHipc(1013)] + [CommandCmif(1013)] public ResultCode UnsetSaveDataRootPath(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.UnsetSaveDataRootPath().Value; } - [CommandHipc(1014)] + [CommandCmif(1014)] public ResultCode OutputMultiProgramTagAccessLog(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.OutputMultiProgramTagAccessLog().Value; } - [CommandHipc(1016)] + [CommandCmif(1016)] public ResultCode FlushAccessLogOnSdCard(ServiceCtx context) { return (ResultCode)_baseFileSystemProxy.Get.FlushAccessLogOnSdCard().Value; } - [CommandHipc(1017)] + [CommandCmif(1017)] public ResultCode OutputApplicationInfoAccessLog(ServiceCtx context) { ApplicationInfo info = context.RequestData.ReadStruct<ApplicationInfo>(); @@ -1264,7 +1264,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.OutputApplicationInfoAccessLog(in info).Value; } - [CommandHipc(1100)] + [CommandCmif(1100)] public ResultCode OverrideSaveDataTransferTokenSignVerificationKey(ServiceCtx context) { byte[] keyBuffer = new byte[context.Request.SendBuff[0].Size]; @@ -1273,7 +1273,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.OverrideSaveDataTransferTokenSignVerificationKey(new InBuffer(keyBuffer)).Value; } - [CommandHipc(1110)] + [CommandCmif(1110)] public ResultCode CorruptSaveDataFileSystemByOffset(ServiceCtx context) { SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); @@ -1283,7 +1283,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)_baseFileSystemProxy.Get.CorruptSaveDataFileSystemByOffset(spaceId, saveDataId, offset).Value; } - [CommandHipc(1200)] // 6.0.0+ + [CommandCmif(1200)] // 6.0.0+ // OpenMultiCommitManager() -> object<nn::fssrv::sf::IMultiCommitManager> public ResultCode OpenMultiCommitManager(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs b/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs index 1a85e1b2d..aa04a7e64 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs _baseCommitManager = SharedRef<LibHac.FsSrv.Sf.IMultiCommitManager>.CreateMove(ref baseCommitManager); } - [CommandHipc(1)] // 6.0.0+ + [CommandCmif(1)] // 6.0.0+ // Add(object<nn::fssrv::sf::IFileSystem>) public ResultCode Add(ServiceCtx context) { @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)result.Value; } - [CommandHipc(2)] // 6.0.0+ + [CommandCmif(2)] // 6.0.0+ // Commit() public ResultCode Commit(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs index 0bb4123f2..0611375b1 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs _baseReader = SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>.CreateMove(ref baseReader); } - [CommandHipc(0)] + [CommandCmif(0)] // ReadSaveDataInfo() -> (u64, buffer<unknown, 6>) public ResultCode ReadSaveDataInfo(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs index cc089d051..56f63e52e 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer { public IActiveApplicationDeviceList() { } - [CommandHipc(0)] + [CommandCmif(0)] // ActivateVibrationDevice(nn::hid::VibrationDeviceHandle) public ResultCode ActivateVibrationDevice(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs index 29ee1706c..f0aaf5e36 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer _hidSharedMem = hidSharedMem; } - [CommandHipc(0)] + [CommandCmif(0)] // GetSharedMemoryHandle() -> handle<copy> public ResultCode GetSharedMemoryHandle(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index 266fc04fb..d508aba42 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid _vibrationPermitted = true; } - [CommandHipc(0)] + [CommandCmif(0)] // CreateAppletResource(nn::applet::AppletResourceUserId) -> object<nn::hid::IAppletResource> public ResultCode CreateAppletResource(ServiceCtx context) { @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // ActivateDebugPad(nn::applet::AppletResourceUserId) public ResultCode ActivateDebugPad(ServiceCtx context) { @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // ActivateTouchScreen(nn::applet::AppletResourceUserId) public ResultCode ActivateTouchScreen(ServiceCtx context) { @@ -108,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(21)] + [CommandCmif(21)] // ActivateMouse(nn::applet::AppletResourceUserId) public ResultCode ActivateMouse(ServiceCtx context) { @@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(31)] + [CommandCmif(31)] // ActivateKeyboard(nn::applet::AppletResourceUserId) public ResultCode ActivateKeyboard(ServiceCtx context) { @@ -151,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(32)] + [CommandCmif(32)] // SendKeyboardLockKeyEvent(uint flags, pid) public ResultCode SendKeyboardLockKeyEvent(ServiceCtx context) { @@ -164,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(40)] + [CommandCmif(40)] // AcquireXpadIdEventHandle(ulong XpadId) -> nn::sf::NativeHandle public ResultCode AcquireXpadIdEventHandle(ServiceCtx context) { @@ -182,7 +182,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(41)] + [CommandCmif(41)] // ReleaseXpadIdEventHandle(ulong XpadId) public ResultCode ReleaseXpadIdEventHandle(ServiceCtx context) { @@ -195,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(51)] + [CommandCmif(51)] // ActivateXpad(nn::hid::BasicXpadId, nn::applet::AppletResourceUserId) public ResultCode ActivateXpad(ServiceCtx context) { @@ -207,7 +207,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(55)] + [CommandCmif(55)] // GetXpadIds() -> long IdsCount, buffer<array<nn::hid::BasicXpadId>, type: 0xa> public ResultCode GetXpadIds(ServiceCtx context) { @@ -219,7 +219,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(56)] + [CommandCmif(56)] // ActivateJoyXpad(nn::hid::JoyXpadId) public ResultCode ActivateJoyXpad(ServiceCtx context) { @@ -230,7 +230,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(58)] + [CommandCmif(58)] // GetJoyXpadLifoHandle(nn::hid::JoyXpadId) -> nn::sf::NativeHandle public ResultCode GetJoyXpadLifoHandle(ServiceCtx context) { @@ -245,7 +245,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(59)] + [CommandCmif(59)] // GetJoyXpadIds() -> long IdsCount, buffer<array<nn::hid::JoyXpadId>, type: 0xa> public ResultCode GetJoyXpadIds(ServiceCtx context) { @@ -257,7 +257,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(60)] + [CommandCmif(60)] // ActivateSixAxisSensor(nn::hid::BasicXpadId) public ResultCode ActivateSixAxisSensor(ServiceCtx context) { @@ -268,7 +268,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(61)] + [CommandCmif(61)] // DeactivateSixAxisSensor(nn::hid::BasicXpadId) public ResultCode DeactivateSixAxisSensor(ServiceCtx context) { @@ -279,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(62)] + [CommandCmif(62)] // GetSixAxisSensorLifoHandle(nn::hid::BasicXpadId) -> nn::sf::NativeHandle public ResultCode GetSixAxisSensorLifoHandle(ServiceCtx context) { @@ -294,7 +294,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(63)] + [CommandCmif(63)] // ActivateJoySixAxisSensor(nn::hid::JoyXpadId) public ResultCode ActivateJoySixAxisSensor(ServiceCtx context) { @@ -305,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(64)] + [CommandCmif(64)] // DeactivateJoySixAxisSensor(nn::hid::JoyXpadId) public ResultCode DeactivateJoySixAxisSensor(ServiceCtx context) { @@ -316,7 +316,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(65)] + [CommandCmif(65)] // GetJoySixAxisSensorLifoHandle(nn::hid::JoyXpadId) -> nn::sf::NativeHandle public ResultCode GetJoySixAxisSensorLifoHandle(ServiceCtx context) { @@ -331,7 +331,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(66)] + [CommandCmif(66)] // StartSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode StartSixAxisSensor(ServiceCtx context) { @@ -344,7 +344,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(67)] + [CommandCmif(67)] // StopSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode StopSixAxisSensor(ServiceCtx context) { @@ -357,7 +357,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(68)] + [CommandCmif(68)] // IsSixAxisSensorFusionEnabled(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsEnabled public ResultCode IsSixAxisSensorFusionEnabled(ServiceCtx context) { @@ -372,7 +372,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(69)] + [CommandCmif(69)] // EnableSixAxisSensorFusion(bool Enabled, nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode EnableSixAxisSensorFusion(ServiceCtx context) { @@ -385,7 +385,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(70)] + [CommandCmif(70)] // SetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, float RevisePower, float ReviseRange, nn::applet::AppletResourceUserId) public ResultCode SetSixAxisSensorFusionParameters(ServiceCtx context) { @@ -405,7 +405,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(71)] + [CommandCmif(71)] // GetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float RevisePower, float ReviseRange) public ResultCode GetSixAxisSensorFusionParameters(ServiceCtx context) { @@ -421,7 +421,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(72)] + [CommandCmif(72)] // ResetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode ResetSixAxisSensorFusionParameters(ServiceCtx context) { @@ -437,7 +437,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(73)] + [CommandCmif(73)] // SetAccelerometerParameters(nn::hid::SixAxisSensorHandle, float X, float Y, nn::applet::AppletResourceUserId) public ResultCode SetAccelerometerParameters(ServiceCtx context) { @@ -457,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(74)] + [CommandCmif(74)] // GetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float X, float Y public ResultCode GetAccelerometerParameters(ServiceCtx context) { @@ -473,7 +473,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(75)] + [CommandCmif(75)] // ResetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode ResetAccelerometerParameters(ServiceCtx context) { @@ -489,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(76)] + [CommandCmif(76)] // SetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, uint PlayMode, nn::applet::AppletResourceUserId) public ResultCode SetAccelerometerPlayMode(ServiceCtx context) { @@ -503,7 +503,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(77)] + [CommandCmif(77)] // GetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> uint PlayMode public ResultCode GetAccelerometerPlayMode(ServiceCtx context) { @@ -518,7 +518,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(78)] + [CommandCmif(78)] // ResetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode ResetAccelerometerPlayMode(ServiceCtx context) { @@ -533,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(79)] + [CommandCmif(79)] // SetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, uint GyroscopeZeroDriftMode, nn::applet::AppletResourceUserId) public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context) { @@ -546,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(80)] + [CommandCmif(80)] // GetGyroscopeZeroDriftMode(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> int GyroscopeZeroDriftMode public ResultCode GetGyroscopeZeroDriftMode(ServiceCtx context) { @@ -561,7 +561,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(81)] + [CommandCmif(81)] // ResetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode ResetGyroscopeZeroDriftMode(ServiceCtx context) { @@ -576,7 +576,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(82)] + [CommandCmif(82)] // IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest public ResultCode IsSixAxisSensorAtRest(ServiceCtx context) { @@ -593,7 +593,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(83)] // 6.0.0+ + [CommandCmif(83)] // 6.0.0+ // IsFirmwareUpdateAvailableForSixAxisSensor(nn::hid::AppletResourceUserId, nn::hid::SixAxisSensorHandle, pid) -> bool UpdateAvailable public ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(ServiceCtx context) { @@ -608,7 +608,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(84)] // 13.0.0+ + [CommandCmif(84)] // 13.0.0+ // EnableSixAxisSensorUnalteredPassthrough(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle, u8 enabled) public ResultCode EnableSixAxisSensorUnalteredPassthrough(ServiceCtx context) { @@ -621,7 +621,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(85)] // 13.0.0+ + [CommandCmif(85)] // 13.0.0+ // IsSixAxisSensorUnalteredPassthroughEnabled(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> u8 enabled public ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(ServiceCtx context) { @@ -636,7 +636,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(87)] // 13.0.0+ + [CommandCmif(87)] // 13.0.0+ // LoadSixAxisSensorCalibrationParameter(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle, u64 unknown) public ResultCode LoadSixAxisSensorCalibrationParameter(ServiceCtx context) { @@ -651,7 +651,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(88)] // 13.0.0+ + [CommandCmif(88)] // 13.0.0+ // GetSixAxisSensorIcInformation(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> u64 unknown public ResultCode GetSixAxisSensorIcInformation(ServiceCtx context) { @@ -666,7 +666,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(91)] + [CommandCmif(91)] // ActivateGesture(nn::applet::AppletResourceUserId, int Unknown0) public ResultCode ActivateGesture(ServiceCtx context) { @@ -678,7 +678,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(100)] + [CommandCmif(100)] // SetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag) public ResultCode SetSupportedNpadStyleSet(ServiceCtx context) { @@ -694,7 +694,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(101)] + [CommandCmif(101)] // GetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag public ResultCode GetSupportedNpadStyleSet(ServiceCtx context) { @@ -708,7 +708,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(102)] + [CommandCmif(102)] // SetSupportedNpadIdType(nn::applet::AppletResourceUserId, array<NpadIdType, 9>) public ResultCode SetSupportedNpadIdType(ServiceCtx context) { @@ -733,14 +733,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(103)] + [CommandCmif(103)] // ActivateNpad(nn::applet::AppletResourceUserId) public ResultCode ActivateNpad(ServiceCtx context) { return ActiveNpadImpl(context); } - [CommandHipc(104)] + [CommandCmif(104)] // DeactivateNpad(nn::applet::AppletResourceUserId) public ResultCode DeactivateNpad(ServiceCtx context) { @@ -752,7 +752,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(106)] + [CommandCmif(106)] // AcquireNpadStyleSetUpdateEventHandle(nn::applet::AppletResourceUserId, uint, ulong) -> nn::sf::NativeHandle public ResultCode AcquireNpadStyleSetUpdateEventHandle(ServiceCtx context) { @@ -776,7 +776,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(107)] + [CommandCmif(107)] // DisconnectNpad(nn::applet::AppletResourceUserId, uint NpadIdType) public ResultCode DisconnectNpad(ServiceCtx context) { @@ -788,7 +788,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(108)] + [CommandCmif(108)] // GetPlayerLedPattern(u32 npad_id) -> u64 led_pattern public ResultCode GetPlayerLedPattern(ServiceCtx context) { @@ -814,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(109)] // 5.0.0+ + [CommandCmif(109)] // 5.0.0+ // ActivateNpadWithRevision(nn::applet::AppletResourceUserId, ulong revision) public ResultCode ActivateNpadWithRevision(ServiceCtx context) { @@ -859,7 +859,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(120)] + [CommandCmif(120)] // SetNpadJoyHoldType(nn::applet::AppletResourceUserId, ulong NpadJoyHoldType) public ResultCode SetNpadJoyHoldType(ServiceCtx context) { @@ -885,7 +885,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(121)] + [CommandCmif(121)] // GetNpadJoyHoldType(nn::applet::AppletResourceUserId) -> ulong NpadJoyHoldType public ResultCode GetNpadJoyHoldType(ServiceCtx context) { @@ -904,7 +904,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(122)] + [CommandCmif(122)] // SetNpadJoyAssignmentModeSingleByDefault(uint HidControllerId, nn::applet::AppletResourceUserId) public ResultCode SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx context) { @@ -920,7 +920,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(123)] + [CommandCmif(123)] // SetNpadJoyAssignmentModeSingle(uint npadIdType, nn::applet::AppletResourceUserId, uint npadJoyDeviceType) public ResultCode SetNpadJoyAssignmentModeSingle(ServiceCtx context) { @@ -937,7 +937,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(124)] + [CommandCmif(124)] // SetNpadJoyAssignmentModeDual(uint npadIdType, nn::applet::AppletResourceUserId) public ResultCode SetNpadJoyAssignmentModeDual(ServiceCtx context) { @@ -953,7 +953,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(125)] + [CommandCmif(125)] // MergeSingleJoyAsDualJoy(uint npadIdType0, uint npadIdType1, nn::applet::AppletResourceUserId) public ResultCode MergeSingleJoyAsDualJoy(ServiceCtx context) { @@ -969,7 +969,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(126)] + [CommandCmif(126)] // StartLrAssignmentMode(nn::applet::AppletResourceUserId) public ResultCode StartLrAssignmentMode(ServiceCtx context) { @@ -980,7 +980,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(127)] + [CommandCmif(127)] // StopLrAssignmentMode(nn::applet::AppletResourceUserId) public ResultCode StopLrAssignmentMode(ServiceCtx context) { @@ -991,7 +991,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(128)] + [CommandCmif(128)] // SetNpadHandheldActivationMode(nn::applet::AppletResourceUserId, long HidNpadHandheldActivationMode) public ResultCode SetNpadHandheldActivationMode(ServiceCtx context) { @@ -1003,7 +1003,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(129)] + [CommandCmif(129)] // GetNpadHandheldActivationMode(nn::applet::AppletResourceUserId) -> long HidNpadHandheldActivationMode public ResultCode GetNpadHandheldActivationMode(ServiceCtx context) { @@ -1016,7 +1016,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(130)] + [CommandCmif(130)] // SwapNpadAssignment(uint OldNpadAssignment, uint NewNpadAssignment, nn::applet::AppletResourceUserId) public ResultCode SwapNpadAssignment(ServiceCtx context) { @@ -1029,7 +1029,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(131)] + [CommandCmif(131)] // IsUnintendedHomeButtonInputProtectionEnabled(uint Unknown0, nn::applet::AppletResourceUserId) -> bool IsEnabled public ResultCode IsUnintendedHomeButtonInputProtectionEnabled(ServiceCtx context) { @@ -1043,7 +1043,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(132)] + [CommandCmif(132)] // EnableUnintendedHomeButtonInputProtection(bool Enable, uint Unknown0, nn::applet::AppletResourceUserId) public ResultCode EnableUnintendedHomeButtonInputProtection(ServiceCtx context) { @@ -1056,7 +1056,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(133)] // 5.0.0+ + [CommandCmif(133)] // 5.0.0+ // SetNpadJoyAssignmentModeSingleWithDestination(uint npadIdType, uint npadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool npadIdTypeIsSet, uint npadIdTypeSet public ResultCode SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx context) { @@ -1091,7 +1091,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // If not, it returns nothing. } - [CommandHipc(200)] + [CommandCmif(200)] // GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo public ResultCode GetVibrationDeviceInfo(ServiceCtx context) { @@ -1154,7 +1154,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.InvalidNpadDeviceType; } - [CommandHipc(201)] + [CommandCmif(201)] // SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId) public ResultCode SendVibrationValue(ServiceCtx context) { @@ -1185,7 +1185,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(202)] + [CommandCmif(202)] // GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue public ResultCode GetActualVibrationValue(ServiceCtx context) { @@ -1209,7 +1209,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(203)] + [CommandCmif(203)] // CreateActiveVibrationDeviceList() -> object<nn::hid::IActiveVibrationDeviceList> public ResultCode CreateActiveVibrationDeviceList(ServiceCtx context) { @@ -1218,7 +1218,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(204)] + [CommandCmif(204)] // PermitVibration(bool Enable) public ResultCode PermitVibration(ServiceCtx context) { @@ -1229,7 +1229,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(205)] + [CommandCmif(205)] // IsVibrationPermitted() -> bool IsEnabled public ResultCode IsVibrationPermitted(ServiceCtx context) { @@ -1238,7 +1238,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(206)] + [CommandCmif(206)] // SendVibrationValues(nn::applet::AppletResourceUserId, buffer<array<nn::hid::VibrationDeviceHandle>, type: 9>, buffer<array<nn::hid::VibrationValue>, type: 9>) public ResultCode SendVibrationValues(ServiceCtx context) { @@ -1281,7 +1281,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(207)] // 4.0.0+ + [CommandCmif(207)] // 4.0.0+ // SendVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::hid::VibrationGcErmCommand, nn::applet::AppletResourceUserId) public ResultCode SendVibrationGcErmCommand(ServiceCtx context) { @@ -1294,7 +1294,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(208)] // 4.0.0+ + [CommandCmif(208)] // 4.0.0+ // GetActualVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationGcErmCommand public ResultCode GetActualVibrationGcErmCommand(ServiceCtx context) { @@ -1308,7 +1308,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(209)] // 4.0.0+ + [CommandCmif(209)] // 4.0.0+ // BeginPermitVibrationSession(nn::applet::AppletResourceUserId) public ResultCode BeginPermitVibrationSession(ServiceCtx context) { @@ -1319,7 +1319,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(210)] // 4.0.0+ + [CommandCmif(210)] // 4.0.0+ // EndPermitVibrationSession() public ResultCode EndPermitVibrationSession(ServiceCtx context) { @@ -1328,7 +1328,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(211)] // 7.0.0+ + [CommandCmif(211)] // 7.0.0+ // IsVibrationDeviceMounted(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) public ResultCode IsVibrationDeviceMounted(ServiceCtx context) { @@ -1343,7 +1343,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(300)] + [CommandCmif(300)] // ActivateConsoleSixAxisSensor(nn::applet::AppletResourceUserId) public ResultCode ActivateConsoleSixAxisSensor(ServiceCtx context) { @@ -1354,7 +1354,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(301)] + [CommandCmif(301)] // StartConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode StartConsoleSixAxisSensor(ServiceCtx context) { @@ -1366,7 +1366,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(302)] + [CommandCmif(302)] // StopConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode StopConsoleSixAxisSensor(ServiceCtx context) { @@ -1378,7 +1378,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(303)] // 5.0.0+ + [CommandCmif(303)] // 5.0.0+ // ActivateSevenSixAxisSensor(nn::applet::AppletResourceUserId) public ResultCode ActivateSevenSixAxisSensor(ServiceCtx context) { @@ -1389,7 +1389,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(304)] // 5.0.0+ + [CommandCmif(304)] // 5.0.0+ // StartSevenSixAxisSensor(nn::applet::AppletResourceUserId) public ResultCode StartSevenSixAxisSensor(ServiceCtx context) { @@ -1400,7 +1400,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(305)] // 5.0.0+ + [CommandCmif(305)] // 5.0.0+ // StopSevenSixAxisSensor(nn::applet::AppletResourceUserId) public ResultCode StopSevenSixAxisSensor(ServiceCtx context) { @@ -1411,7 +1411,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(306)] // 5.0.0+ + [CommandCmif(306)] // 5.0.0+ // InitializeSevenSixAxisSensor(array<nn::sf::NativeHandle>, ulong Counter0, array<nn::sf::NativeHandle>, ulong Counter1, nn::applet::AppletResourceUserId) public ResultCode InitializeSevenSixAxisSensor(ServiceCtx context) { @@ -1426,7 +1426,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(307)] // 5.0.0+ + [CommandCmif(307)] // 5.0.0+ // FinalizeSevenSixAxisSensor(nn::applet::AppletResourceUserId) public ResultCode FinalizeSevenSixAxisSensor(ServiceCtx context) { @@ -1437,7 +1437,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(308)] // 5.0.0+ + [CommandCmif(308)] // 5.0.0+ // SetSevenSixAxisSensorFusionStrength(float Strength, nn::applet::AppletResourceUserId) public ResultCode SetSevenSixAxisSensorFusionStrength(ServiceCtx context) { @@ -1449,7 +1449,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(309)] // 5.0.0+ + [CommandCmif(309)] // 5.0.0+ // GetSevenSixAxisSensorFusionStrength(nn::applet::AppletResourceUserId) -> float Strength public ResultCode GetSevenSixAxisSensorFusionStrength(ServiceCtx context) { @@ -1462,7 +1462,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(310)] // 6.0.0+ + [CommandCmif(310)] // 6.0.0+ // ResetSevenSixAxisSensorTimestamp(pid, nn::applet::AppletResourceUserId) public ResultCode ResetSevenSixAxisSensorTimestamp(ServiceCtx context) { @@ -1473,7 +1473,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(400)] + [CommandCmif(400)] // IsUsbFullKeyControllerEnabled() -> bool IsEnabled public ResultCode IsUsbFullKeyControllerEnabled(ServiceCtx context) { @@ -1484,7 +1484,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(401)] + [CommandCmif(401)] // EnableUsbFullKeyController(bool Enable) public ResultCode EnableUsbFullKeyController(ServiceCtx context) { @@ -1495,7 +1495,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(402)] + [CommandCmif(402)] // IsUsbFullKeyControllerConnected(uint Unknown0) -> bool Connected public ResultCode IsUsbFullKeyControllerConnected(ServiceCtx context) { @@ -1508,7 +1508,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(403)] // 4.0.0+ + [CommandCmif(403)] // 4.0.0+ // HasBattery(uint NpadId) -> bool HasBattery public ResultCode HasBattery(ServiceCtx context) { @@ -1521,7 +1521,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(404)] // 4.0.0+ + [CommandCmif(404)] // 4.0.0+ // HasLeftRightBattery(uint NpadId) -> bool HasLeftBattery, bool HasRightBattery public ResultCode HasLeftRightBattery(ServiceCtx context) { @@ -1535,7 +1535,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(405)] // 4.0.0+ + [CommandCmif(405)] // 4.0.0+ // GetNpadInterfaceType(uint NpadId) -> uchar InterfaceType public ResultCode GetNpadInterfaceType(ServiceCtx context) { @@ -1548,7 +1548,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(406)] // 4.0.0+ + [CommandCmif(406)] // 4.0.0+ // GetNpadLeftRightInterfaceType(uint NpadId) -> uchar LeftInterfaceType, uchar RightInterfaceType public ResultCode GetNpadLeftRightInterfaceType(ServiceCtx context) { @@ -1562,7 +1562,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(500)] // 5.0.0+ + [CommandCmif(500)] // 5.0.0+ // GetPalmaConnectionHandle(uint Unknown0, nn::applet::AppletResourceUserId) -> nn::hid::PalmaConnectionHandle public ResultCode GetPalmaConnectionHandle(ServiceCtx context) { @@ -1578,7 +1578,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(501)] // 5.0.0+ + [CommandCmif(501)] // 5.0.0+ // InitializePalma(nn::hid::PalmaConnectionHandle) public ResultCode InitializePalma(ServiceCtx context) { @@ -1591,7 +1591,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(502)] // 5.0.0+ + [CommandCmif(502)] // 5.0.0+ // AcquirePalmaOperationCompleteEvent(nn::hid::PalmaConnectionHandle) -> nn::sf::NativeHandle public ResultCode AcquirePalmaOperationCompleteEvent(ServiceCtx context) { @@ -1609,7 +1609,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(503)] // 5.0.0+ + [CommandCmif(503)] // 5.0.0+ // GetPalmaOperationInfo(nn::hid::PalmaConnectionHandle) -> long Unknown0, buffer<Unknown> public ResultCode GetPalmaOperationInfo(ServiceCtx context) { @@ -1624,7 +1624,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(504)] // 5.0.0+ + [CommandCmif(504)] // 5.0.0+ // PlayPalmaActivity(nn::hid::PalmaConnectionHandle, ulong Unknown0) public ResultCode PlayPalmaActivity(ServiceCtx context) { @@ -1638,7 +1638,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(505)] // 5.0.0+ + [CommandCmif(505)] // 5.0.0+ // SetPalmaFrModeType(nn::hid::PalmaConnectionHandle, ulong FrModeType) public ResultCode SetPalmaFrModeType(ServiceCtx context) { @@ -1652,7 +1652,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(506)] // 5.0.0+ + [CommandCmif(506)] // 5.0.0+ // ReadPalmaStep(nn::hid::PalmaConnectionHandle) public ResultCode ReadPalmaStep(ServiceCtx context) { @@ -1663,7 +1663,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(507)] // 5.0.0+ + [CommandCmif(507)] // 5.0.0+ // EnablePalmaStep(nn::hid::PalmaConnectionHandle, bool Enable) public ResultCode EnablePalmaStep(ServiceCtx context) { @@ -1677,7 +1677,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(508)] // 5.0.0+ + [CommandCmif(508)] // 5.0.0+ // ResetPalmaStep(nn::hid::PalmaConnectionHandle) public ResultCode ResetPalmaStep(ServiceCtx context) { @@ -1690,7 +1690,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(509)] // 5.0.0+ + [CommandCmif(509)] // 5.0.0+ // ReadPalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1) public ResultCode ReadPalmaApplicationSection(ServiceCtx context) { @@ -1703,7 +1703,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(510)] // 5.0.0+ + [CommandCmif(510)] // 5.0.0+ // WritePalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1, nn::hid::PalmaApplicationSectionAccessBuffer) public ResultCode WritePalmaApplicationSection(ServiceCtx context) { @@ -1719,7 +1719,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(511)] // 5.0.0+ + [CommandCmif(511)] // 5.0.0+ // ReadPalmaUniqueCode(nn::hid::PalmaConnectionHandle) public ResultCode ReadPalmaUniqueCode(ServiceCtx context) { @@ -1730,7 +1730,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(512)] // 5.0.0+ + [CommandCmif(512)] // 5.0.0+ // SetPalmaUniqueCodeInvalid(nn::hid::PalmaConnectionHandle) public ResultCode SetPalmaUniqueCodeInvalid(ServiceCtx context) { @@ -1741,7 +1741,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(522)] // 5.1.0+ + [CommandCmif(522)] // 5.1.0+ // SetIsPalmaAllConnectable(nn::applet::AppletResourceUserId, bool, pid) public ResultCode SetIsPalmaAllConnectable(ServiceCtx context) { @@ -1753,7 +1753,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(525)] // 5.1.0+ + [CommandCmif(525)] // 5.1.0+ // SetPalmaBoostMode(bool) public ResultCode SetPalmaBoostMode(ServiceCtx context) { @@ -1762,7 +1762,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(1000)] + [CommandCmif(1000)] // SetNpadCommunicationMode(long CommunicationMode, nn::applet::AppletResourceUserId) public ResultCode SetNpadCommunicationMode(ServiceCtx context) { @@ -1774,7 +1774,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(1001)] + [CommandCmif(1001)] // GetNpadCommunicationMode() -> long CommunicationMode public ResultCode GetNpadCommunicationMode(ServiceCtx context) { @@ -1785,7 +1785,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(1002)] // 9.0.0+ + [CommandCmif(1002)] // 9.0.0+ // SetTouchScreenConfiguration(nn::hid::TouchScreenConfigurationForNx, nn::applet::AppletResourceUserId) public ResultCode SetTouchScreenConfiguration(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs index ec8295e27..4a5d0e9b5 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { public IHidSystemServer(ServiceCtx context) { } - [CommandHipc(303)] + [CommandCmif(303)] // ApplyNpadSystemCommonPolicy(u64) public ResultCode ApplyNpadSystemCommonPolicy(ServiceCtx context) { @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(306)] + [CommandCmif(306)] // GetLastActiveNpad(u32) -> u8, u8 public ResultCode GetLastActiveNpad(ServiceCtx context) { @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return resultCode; } - [CommandHipc(307)] + [CommandCmif(307)] // GetNpadSystemExtStyle() -> u64 public ResultCode GetNpadSystemExtStyle(ServiceCtx context) { @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } - [CommandHipc(314)] // 9.0.0+ + [CommandCmif(314)] // 9.0.0+ // GetAppletFooterUiType(u32) -> u8 public ResultCode GetAppletFooterUiType(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs index 7af06431a..130fcf687 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs public IIrSensorServer(ServiceCtx context) { } - [CommandHipc(302)] + [CommandCmif(302)] // ActivateIrsensor(nn::applet::AppletResourceUserId, pid) public ResultCode ActivateIrsensor(ServiceCtx context) { @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(303)] + [CommandCmif(303)] // DeactivateIrsensor(nn::applet::AppletResourceUserId, pid) public ResultCode DeactivateIrsensor(ServiceCtx context) { @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(304)] + [CommandCmif(304)] // GetIrsensorSharedMemoryHandle(nn::applet::AppletResourceUserId, pid) -> handle<copy> public ResultCode GetIrsensorSharedMemoryHandle(ServiceCtx context) { @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(305)] + [CommandCmif(305)] // StopImageProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId) public ResultCode StopImageProcessor(ServiceCtx context) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(306)] + [CommandCmif(306)] // RunMomentProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedMomentProcessorConfig) public ResultCode RunMomentProcessor(ServiceCtx context) { @@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(307)] + [CommandCmif(307)] // RunClusteringProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedClusteringProcessorConfig) public ResultCode RunClusteringProcessor(ServiceCtx context) { @@ -105,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(308)] + [CommandCmif(308)] // RunImageTransferProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedImageTransferProcessorConfig, u64 TransferMemorySize, TransferMemoryHandle) public ResultCode RunImageTransferProcessor(ServiceCtx context) { @@ -122,7 +122,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(309)] + [CommandCmif(309)] // GetImageTransferProcessorState(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId) public ResultCode GetImageTransferProcessorState(ServiceCtx context) { @@ -151,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(310)] + [CommandCmif(310)] // RunTeraPluginProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedTeraPluginProcessorConfig) public ResultCode RunTeraPluginProcessor(ServiceCtx context) { @@ -166,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(311)] + [CommandCmif(311)] // GetNpadIrCameraHandle(u32) -> nn::irsensor::IrCameraHandle public ResultCode GetNpadIrCameraHandle(ServiceCtx context) { @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(314)] // 3.0.0+ + [CommandCmif(314)] // 3.0.0+ // CheckFirmwareVersion(nn::irsensor::IrCameraHandle, nn::irsensor::PackedMcuVersion, nn::applet::AppletResourceUserId, pid) public ResultCode CheckFirmwareVersion(ServiceCtx context) { @@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(318)] // 4.0.0+ + [CommandCmif(318)] // 4.0.0+ // StopImageProcessorAsync(nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, pid) public ResultCode StopImageProcessorAsync(ServiceCtx context) { @@ -215,7 +215,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs return ResultCode.Success; } - [CommandHipc(319)] // 4.0.0+ + [CommandCmif(319)] // 4.0.0+ // ActivateIrsensorWithFunctionLevel(nn::applet::AppletResourceUserId, nn::irsensor::PackedFunctionLevel, pid) public ResultCode ActivateIrsensorWithFunctionLevel(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index 4c7d83ea6..f42fd0e21 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services { abstract class IpcService { - public IReadOnlyDictionary<int, MethodInfo> HipcCommands { get; } + public IReadOnlyDictionary<int, MethodInfo> CmifCommands { get; } public IReadOnlyDictionary<int, MethodInfo> TipcCommands { get; } public ServerBase Server { get; private set; } @@ -23,11 +23,11 @@ namespace Ryujinx.HLE.HOS.Services public IpcService(ServerBase server = null) { - HipcCommands = Assembly.GetExecutingAssembly().GetTypes() + CmifCommands = Assembly.GetExecutingAssembly().GetTypes() .Where(type => type == GetType()) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) - .SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandHipcAttribute)) - .Select(command => (((CommandHipcAttribute)command).Id, methodInfo))) + .SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandCmifAttribute)) + .Select(command => (((CommandCmifAttribute)command).Id, methodInfo))) .ToDictionary(command => command.Id, command => command.methodInfo); TipcCommands = Assembly.GetExecutingAssembly().GetTypes() @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services _isDomain = false; } - public void CallHipcMethod(ServiceCtx context) + public void CallCmifMethod(ServiceCtx context) { IpcService service = this; @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services long sfciMagic = context.RequestData.ReadInt64(); int commandId = (int)context.RequestData.ReadInt64(); - bool serviceExists = service.HipcCommands.TryGetValue(commandId, out MethodInfo processRequest); + bool serviceExists = service.CmifCommands.TryGetValue(commandId, out MethodInfo processRequest); if (context.Device.Configuration.IgnoreMissingServices || serviceExists) { diff --git a/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs b/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs index 3a81c814f..65074d5ff 100644 --- a/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs +++ b/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs @@ -10,14 +10,14 @@ internal abstract void DisableVrMode(); protected abstract bool IsVrModeEnabled(); - [CommandHipc(17)] + [CommandCmif(17)] // SetBrightnessReflectionDelayLevel(float, float) public ResultCode SetBrightnessReflectionDelayLevel(ServiceCtx context) { return ResultCode.Success; } - [CommandHipc(18)] + [CommandCmif(18)] // GetBrightnessReflectionDelayLevel(float) -> float public ResultCode GetBrightnessReflectionDelayLevel(ServiceCtx context) { @@ -26,21 +26,21 @@ return ResultCode.Success; } - [CommandHipc(21)] + [CommandCmif(21)] // SetCurrentAmbientLightSensorMapping(unknown<0xC>) public ResultCode SetCurrentAmbientLightSensorMapping(ServiceCtx context) { return ResultCode.Success; } - [CommandHipc(22)] + [CommandCmif(22)] // GetCurrentAmbientLightSensorMapping() -> unknown<0xC> public ResultCode GetCurrentAmbientLightSensorMapping(ServiceCtx context) { return ResultCode.Success; } - [CommandHipc(24)] // 3.0.0+ + [CommandCmif(24)] // 3.0.0+ // SetCurrentBrightnessSettingForVrMode(float) public ResultCode SetCurrentBrightnessSettingForVrMode(ServiceCtx context) { @@ -51,7 +51,7 @@ return ResultCode.Success; } - [CommandHipc(25)] // 3.0.0+ + [CommandCmif(25)] // 3.0.0+ // GetCurrentBrightnessSettingForVrMode() -> float public ResultCode GetCurrentBrightnessSettingForVrMode(ServiceCtx context) { @@ -62,7 +62,7 @@ return ResultCode.Success; } - [CommandHipc(26)] // 3.0.0+ + [CommandCmif(26)] // 3.0.0+ // EnableVrMode() public ResultCode EnableVrMode(ServiceCtx context) { @@ -71,7 +71,7 @@ return ResultCode.Success; } - [CommandHipc(27)] // 3.0.0+ + [CommandCmif(27)] // 3.0.0+ // DisableVrMode() public ResultCode DisableVrMode(ServiceCtx context) { @@ -80,7 +80,7 @@ return ResultCode.Success; } - [CommandHipc(28)] // 3.0.0+ + [CommandCmif(28)] // 3.0.0+ // IsVrModeEnabled() -> bool public ResultCode IsVrModeEnabled(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs index 7e9aa7021..4f3094ae5 100644 --- a/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn { public IUserServiceCreator(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateUserLocalCommunicationService() -> object<nn::ldn::detail::IUserLocalCommunicationService> public ResultCode CreateUserLocalCommunicationService(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index 0c223c067..f425ddf78 100644 --- a/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator _networkInterface = new NetworkInterface(context.Device.System); } - [CommandHipc(0)] + [CommandCmif(0)] // GetState() -> s32 state public ResultCode GetState(ServiceCtx context) { @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator return result; } - [CommandHipc(100)] + [CommandCmif(100)] // AttachStateChangeEvent() -> handle<copy> public ResultCode AttachStateChangeEvent(ServiceCtx context) { @@ -60,21 +60,21 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator return ResultCode.Success; } - [CommandHipc(400)] + [CommandCmif(400)] // InitializeOld(u64, pid) public ResultCode InitializeOld(ServiceCtx context) { return _networkInterface.Initialize(UnknownValue, 0, null, null); } - [CommandHipc(401)] + [CommandCmif(401)] // Finalize() public ResultCode Finalize(ServiceCtx context) { return _networkInterface.Finalize(); } - [CommandHipc(402)] // 7.0.0+ + [CommandCmif(402)] // 7.0.0+ // Initialize(u64 ip_addresses, u64, pid) public ResultCode Initialize(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs b/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs index 1792bbb71..7d65c73fa 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii public IImageDatabaseService(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // Initialize(b8) -> b8 public ResultCode Initialize(ServiceCtx context) { @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // GetCount() -> u32 public ResultCode GetCount(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs b/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs index 4d1f1e289..a7fc71c96 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii _databaseImpl = DatabaseImpl.Instance; } - [CommandHipc(0)] + [CommandCmif(0)] // GetDatabaseService(u32 mii_key_code) -> object<nn::mii::detail::IDatabaseService> public ResultCode GetDatabaseService(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs index dea32f62b..e95364be8 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService { abstract class IDatabaseService : IpcService { - [CommandHipc(0)] + [CommandCmif(0)] // IsUpdated(SourceFlag flag) -> bool public ResultCode IsUpdated(ServiceCtx context) { @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // IsFullDatabase() -> bool public ResultCode IsFullDatabase(ServiceCtx context) { @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetCount(SourceFlag flag) -> u32 public ResultCode GetCount(ServiceCtx context) { @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // Get(SourceFlag flag) -> (s32 count, buffer<nn::mii::CharInfoRawElement, 6>) public ResultCode Get(ServiceCtx context) { @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(4)] + [CommandCmif(4)] // Get1(SourceFlag flag) -> (s32 count, buffer<nn::mii::CharInfo, 6>) public ResultCode Get1(ServiceCtx context) { @@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(5)] + [CommandCmif(5)] // UpdateLatest(nn::mii::CharInfo old_char_info, SourceFlag flag) -> nn::mii::CharInfo public ResultCode UpdateLatest(ServiceCtx context) { @@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(6)] + [CommandCmif(6)] // BuildRandom(Age age, Gender gender, Race race) -> nn::mii::CharInfo public ResultCode BuildRandom(ServiceCtx context) { @@ -110,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(7)] + [CommandCmif(7)] // BuildDefault(u32 index) -> nn::mii::CharInfoRaw public ResultCode BuildDefault(ServiceCtx context) { @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(8)] + [CommandCmif(8)] // Get2(SourceFlag flag) -> (u32 count, buffer<nn::mii::StoreDataElement, 6>) public ResultCode Get2(ServiceCtx context) { @@ -144,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(9)] + [CommandCmif(9)] // Get3(SourceFlag flag) -> (u32 count, buffer<nn::mii::StoreData, 6>) public ResultCode Get3(ServiceCtx context) { @@ -165,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(10)] + [CommandCmif(10)] // UpdateLatest1(nn::mii::StoreData old_store_data, SourceFlag flag) -> nn::mii::StoreData public ResultCode UpdateLatest1(ServiceCtx context) { @@ -179,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(11)] + [CommandCmif(11)] // FindIndex(nn::mii::CreateId create_id, bool is_special) -> s32 public ResultCode FindIndex(ServiceCtx context) { @@ -193,7 +193,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(12)] + [CommandCmif(12)] // Move(nn::mii::CreateId create_id, s32 new_index) public ResultCode Move(ServiceCtx context) { @@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return Move(createId, newIndex); } - [CommandHipc(13)] + [CommandCmif(13)] // AddOrReplace(nn::mii::StoreData store_data) public ResultCode AddOrReplace(ServiceCtx context) { @@ -212,7 +212,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return AddOrReplace(storeData); } - [CommandHipc(14)] + [CommandCmif(14)] // Delete(nn::mii::CreateId create_id) public ResultCode Delete(ServiceCtx context) { @@ -221,28 +221,28 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return Delete(createId); } - [CommandHipc(15)] + [CommandCmif(15)] // DestroyFile() public ResultCode DestroyFile(ServiceCtx context) { return DestroyFile(); } - [CommandHipc(16)] + [CommandCmif(16)] // DeleteFile() public ResultCode DeleteFile(ServiceCtx context) { return DeleteFile(); } - [CommandHipc(17)] + [CommandCmif(17)] // Format() public ResultCode Format(ServiceCtx context) { return Format(); } - [CommandHipc(18)] + [CommandCmif(18)] // Import(buffer<bytes, 5>) public ResultCode Import(ServiceCtx context) { @@ -251,7 +251,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return Import(data); } - [CommandHipc(19)] + [CommandCmif(19)] // Export() -> buffer<bytes, 6> public ResultCode Export(ServiceCtx context) { @@ -266,7 +266,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(20)] + [CommandCmif(20)] // IsBrokenDatabaseWithClearFlag() -> bool public ResultCode IsBrokenDatabaseWithClearFlag(ServiceCtx context) { @@ -277,7 +277,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(21)] + [CommandCmif(21)] // GetIndex(nn::mii::CharInfo char_info) -> s32 public ResultCode GetIndex(ServiceCtx context) { @@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(22)] // 5.0.0+ + [CommandCmif(22)] // 5.0.0+ // SetInterfaceVersion(u32 version) public ResultCode SetInterfaceVersion(ServiceCtx context) { @@ -301,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return ResultCode.Success; } - [CommandHipc(23)] // 5.0.0+ + [CommandCmif(23)] // 5.0.0+ // Convert(nn::mii::Ver3StoreData ver3_store_data) -> nn::mii::CharInfo public ResultCode Convert(ServiceCtx context) { @@ -314,7 +314,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(24)] // 7.0.0+ + [CommandCmif(24)] // 7.0.0+ // ConvertCoreDataToCharInfo(nn::mii::CoreData core_data) -> nn::mii::CharInfo public ResultCode ConvertCoreDataToCharInfo(ServiceCtx context) { @@ -327,7 +327,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return result; } - [CommandHipc(25)] // 7.0.0+ + [CommandCmif(25)] // 7.0.0+ // ConvertCharInfoToCoreData(nn::mii::CharInfo char_info) -> nn::mii::CoreData public ResultCode ConvertCharInfoToCoreData(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index bce521190..fac425553 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm public IRequest(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // InitializeOld(u32, u32, u32) public ResultCode InitializeOld(ServiceCtx context) { @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // FinalizeOld(u32) public ResultCode FinalizeOld(ServiceCtx context) { @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // SetAndWaitOld(u32, u32, u32) public ResultCode SetAndWaitOld(ServiceCtx context) { @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetOld(u32) -> u32 public ResultCode GetOld(ServiceCtx context) { @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // Initialize(u32, u32, u32) -> u32 public ResultCode Initialize(ServiceCtx context) { @@ -100,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // Finalize(u32) public ResultCode Finalize(ServiceCtx context) { @@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm return ResultCode.Success; } - [CommandHipc(6)] + [CommandCmif(6)] // SetAndWait(u32, u32, u32) public ResultCode SetAndWait(ServiceCtx context) { @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm return ResultCode.Success; } - [CommandHipc(7)] + [CommandCmif(7)] // Get(u32) -> u32 public ResultCode Get(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs index 8e52a94ef..c2a4345c6 100644 --- a/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Mnpp { public IServiceForApplication(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // Initialize(pid) public ResultCode Initialize(ServiceCtx context) { @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Mnpp return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // SendRawTelemetryData(nn::account::Uid user_id, buffer<bytes, 5> title_id) public ResultCode SendRawTelemetryData(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs b/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs index 546c05679..318ad30e6 100644 --- a/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr { public ILocationResolverManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // OpenLocationResolver() public ResultCode OpenLocationResolver(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs index d97bd009b..55b49bcec 100644 --- a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager _storageId = storageId; } - [CommandHipc(0)] + [CommandCmif(0)] // ResolveProgramPath(u64 titleId) public ResultCode ResolveProgramPath(ServiceCtx context) { @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } } - [CommandHipc(1)] + [CommandCmif(1)] // RedirectProgramPath(u64 titleId) public ResultCode RedirectProgramPath(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // ResolveApplicationControlPath(u64 titleId) public ResultCode ResolveApplicationControlPath(ServiceCtx context) { @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } } - [CommandHipc(3)] + [CommandCmif(3)] // ResolveApplicationHtmlDocumentPath(u64 titleId) public ResultCode ResolveApplicationHtmlDocumentPath(ServiceCtx context) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } } - [CommandHipc(4)] + [CommandCmif(4)] // ResolveDataPath(u64 titleId) public ResultCode ResolveDataPath(ServiceCtx context) { @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } } - [CommandHipc(5)] + [CommandCmif(5)] // RedirectApplicationControlPath(u64 titleId) public ResultCode RedirectApplicationControlPath(ServiceCtx context) { @@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(6)] + [CommandCmif(6)] // RedirectApplicationHtmlDocumentPath(u64 titleId) public ResultCode RedirectApplicationHtmlDocumentPath(ServiceCtx context) { @@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(7)] + [CommandCmif(7)] // ResolveApplicationLegalInformationPath(u64 titleId) public ResultCode ResolveApplicationLegalInformationPath(ServiceCtx context) { @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } } - [CommandHipc(8)] + [CommandCmif(8)] // RedirectApplicationLegalInformationPath(u64 titleId) public ResultCode RedirectApplicationLegalInformationPath(ServiceCtx context) { @@ -140,7 +140,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(9)] + [CommandCmif(9)] // Refresh() public ResultCode Refresh(ServiceCtx context) { @@ -149,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // SetProgramNcaPath2(u64 titleId) public ResultCode SetProgramNcaPath2(ServiceCtx context) { @@ -160,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // ClearLocationResolver2() public ResultCode ClearLocationResolver2(ServiceCtx context) { @@ -169,7 +169,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(12)] + [CommandCmif(12)] // DeleteProgramNcaPath(u64 titleId) public ResultCode DeleteProgramNcaPath(ServiceCtx context) { @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(13)] + [CommandCmif(13)] // DeleteControlNcaPath(u64 titleId) public ResultCode DeleteControlNcaPath(ServiceCtx context) { @@ -191,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(14)] + [CommandCmif(14)] // DeleteDocHtmlNcaPath(u64 titleId) public ResultCode DeleteDocHtmlNcaPath(ServiceCtx context) { @@ -202,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return ResultCode.Success; } - [CommandHipc(15)] + [CommandCmif(15)] // DeleteInfoHtmlNcaPath(u64 titleId) public ResultCode DeleteInfoHtmlNcaPath(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs index 9072ed83e..ef90b6ad8 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc { public ISystemManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateSystemInterface() -> object<nn::nfc::detail::ISystem> public ResultCode CreateSystemInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs index 5f0ccf14a..97959a62f 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc { public IUserManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateUserInterface() -> object<nn::nfc::detail::IUser> public ResultCode CreateUserInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs index a1ae0b9c7..b091aabfc 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs @@ -13,8 +13,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager _state = State.NonInitialized; } - [CommandHipc(0)] - [CommandHipc(400)] // 4.0.0+ + [CommandCmif(0)] + [CommandCmif(400)] // 4.0.0+ // Initialize(u64, u64, pid, buffer<unknown, 5>) public ResultCode Initialize(ServiceCtx context) { @@ -25,8 +25,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager return ResultCode.Success; } - [CommandHipc(1)] - [CommandHipc(401)] // 4.0.0+ + [CommandCmif(1)] + [CommandCmif(401)] // 4.0.0+ // Finalize() public ResultCode Finalize(ServiceCtx context) { @@ -37,8 +37,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager return ResultCode.Success; } - [CommandHipc(2)] - [CommandHipc(402)] // 4.0.0+ + [CommandCmif(2)] + [CommandCmif(402)] // 4.0.0+ // GetState() -> u32 public ResultCode GetState(ServiceCtx context) { @@ -47,8 +47,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager return ResultCode.Success; } - [CommandHipc(3)] - [CommandHipc(403)] // 4.0.0+ + [CommandCmif(3)] + [CommandCmif(403)] // 4.0.0+ // IsNfcEnabled() -> b8 public ResultCode IsNfcEnabled(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs index 2faf489c3..fc4544739 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { public IAmManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateDebugInterface() -> object<nn::nfp::detail::IDebug> public ResultCode CreateDebugInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs index ed4c7bbf2..3fcf7a87a 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { public ISystemManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateSystemInterface() -> object<nn::nfp::detail::ISystem> public ResultCode CreateSystemInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs index 09df720a6..93da84194 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { public IUserManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateUserInterface() -> object<nn::nfp::detail::IUser> public ResultCode CreateUserInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs index a7f2dbb85..e25a2972a 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp _permissionLevel = permissionLevel; } - [CommandHipc(0)] + [CommandCmif(0)] // Initialize(u64, u64, pid, buffer<unknown, 5>) public ResultCode Initialize(ServiceCtx context) { @@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // Finalize() public ResultCode Finalize(ServiceCtx context) { @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // ListDevices() -> (u32, buffer<unknown, 0xa>) public ResultCode ListDevices(ServiceCtx context) { @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // StartDetection(bytes<8, 4>) public ResultCode StartDetection(ServiceCtx context) { @@ -176,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // StopDetection(bytes<8, 4>) public ResultCode StopDetection(ServiceCtx context) { @@ -207,7 +207,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // Mount(bytes<8, 4>, u32, u32) public ResultCode Mount(ServiceCtx context) { @@ -265,7 +265,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(6)] + [CommandCmif(6)] // Unmount(bytes<8, 4>) public ResultCode Unmount(ServiceCtx context) { @@ -307,7 +307,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(7)] + [CommandCmif(7)] // OpenApplicationArea(bytes<8, 4>, u32) public ResultCode OpenApplicationArea(ServiceCtx context) { @@ -363,7 +363,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(8)] + [CommandCmif(8)] // GetApplicationArea(bytes<8, 4>) -> (u32, buffer<unknown, 6>) public ResultCode GetApplicationArea(ServiceCtx context) { @@ -431,7 +431,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(9)] + [CommandCmif(9)] // SetApplicationArea(bytes<8, 4>, buffer<unknown, 5>) public ResultCode SetApplicationArea(ServiceCtx context) { @@ -485,7 +485,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(10)] + [CommandCmif(10)] // Flush(bytes<8, 4>) public ResultCode Flush(ServiceCtx context) { @@ -501,14 +501,14 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // Restore(bytes<8, 4>) public ResultCode Restore(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(12)] + [CommandCmif(12)] // CreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>) public ResultCode CreateApplicationArea(ServiceCtx context) { @@ -571,7 +571,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(13)] + [CommandCmif(13)] // GetTagInfo(bytes<8, 4>) -> buffer<unknown<0x58>, 0x1a> public ResultCode GetTagInfo(ServiceCtx context) { @@ -647,7 +647,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(14)] + [CommandCmif(14)] // GetRegisterInfo(bytes<8, 4>) -> buffer<unknown<0x100>, 0x1a> public ResultCode GetRegisterInfo(ServiceCtx context) { @@ -710,7 +710,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(15)] + [CommandCmif(15)] // GetCommonInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a> public ResultCode GetCommonInfo(ServiceCtx context) { @@ -770,7 +770,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(16)] + [CommandCmif(16)] // GetModelInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a> public ResultCode GetModelInfo(ServiceCtx context) { @@ -839,7 +839,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - [CommandHipc(17)] + [CommandCmif(17)] // AttachActivateEvent(bytes<8, 4>) -> handle<copy> public ResultCode AttachActivateEvent(ServiceCtx context) { @@ -865,7 +865,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.DeviceNotFound; } - [CommandHipc(18)] + [CommandCmif(18)] // AttachDeactivateEvent(bytes<8, 4>) -> handle<copy> public ResultCode AttachDeactivateEvent(ServiceCtx context) { @@ -891,7 +891,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.DeviceNotFound; } - [CommandHipc(19)] + [CommandCmif(19)] // GetState() -> u32 public ResultCode GetState(ServiceCtx context) { @@ -900,7 +900,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(20)] + [CommandCmif(20)] // GetDeviceState(bytes<8, 4>) -> u32 public ResultCode GetDeviceState(ServiceCtx context) { @@ -926,7 +926,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.DeviceNotFound; } - [CommandHipc(21)] + [CommandCmif(21)] // GetNpadId(bytes<8, 4>) -> u32 public ResultCode GetNpadId(ServiceCtx context) { @@ -945,7 +945,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.DeviceNotFound; } - [CommandHipc(22)] + [CommandCmif(22)] // GetApplicationAreaSize() -> u32 public ResultCode GetApplicationAreaSize(ServiceCtx context) { @@ -954,7 +954,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(23)] // 3.0.0+ + [CommandCmif(23)] // 3.0.0+ // AttachAvailabilityChangeEvent() -> handle<copy> public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context) { @@ -970,14 +970,14 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } - [CommandHipc(24)] // 3.0.0+ + [CommandCmif(24)] // 3.0.0+ // RecreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>) public ResultCode RecreateApplicationArea(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(102)] + [CommandCmif(102)] // GetRegisterInfo2(bytes<8, 4>) -> buffer<unknown<0x100>, 0x1a> public ResultCode GetRegisterInfo2(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ngct/IService.cs b/Ryujinx.HLE/HOS/Services/Ngct/IService.cs index 05e37db0f..eacf35f32 100644 --- a/Ryujinx.HLE/HOS/Services/Ngct/IService.cs +++ b/Ryujinx.HLE/HOS/Services/Ngct/IService.cs @@ -5,14 +5,14 @@ { public IService(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // Match(buffer<string, 9>) -> b8 public ResultCode Match(ServiceCtx context) { return NgctServer.Match(context); } - [CommandHipc(1)] + [CommandCmif(1)] // Filter(buffer<string, 9>) -> buffer<filtered_string, 10> public ResultCode Filter(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs b/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs index 371bec8b1..5ad056bab 100644 --- a/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs +++ b/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs @@ -5,14 +5,14 @@ { public IServiceWithManagementApi(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // Match(buffer<string, 9>) -> b8 public ResultCode Match(ServiceCtx context) { return NgctServer.Match(context); } - [CommandHipc(1)] + [CommandCmif(1)] // Filter(buffer<string, 9>) -> buffer<filtered_string, 10> public ResultCode Filter(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs b/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs index 10ee1b834..d6a4a29fb 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm { public IStaticService(ServiceCtx context) { } - [CommandHipc(4)] + [CommandCmif(4)] // CreateGeneralServiceOld() -> object<nn::nifm::detail::IGeneralService> public ResultCode CreateGeneralServiceOld(ServiceCtx context) { @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm return ResultCode.Success; } - [CommandHipc(5)] // 3.0.0+ + [CommandCmif(5)] // 3.0.0+ // CreateGeneralService(u64, pid) -> object<nn::nifm::detail::IGeneralService> public ResultCode CreateGeneralService(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index bc70750d9..fd7110408 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService GeneralServiceManager.Add(_generalServiceDetail); } - [CommandHipc(1)] + [CommandCmif(1)] // GetClientId() -> buffer<nn::nifm::ClientId, 0x1a, 4> public ResultCode GetClientId(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // CreateRequest(u32 version) -> object<nn::nifm::detail::IRequest> public ResultCode CreateRequest(ServiceCtx context) { @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetCurrentNetworkProfile() -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a, 0x17c> public ResultCode GetCurrentNetworkProfile(ServiceCtx context) { @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(12)] + [CommandCmif(12)] // GetCurrentIpAddress() -> nn::nifm::IpV4Address public ResultCode GetCurrentIpAddress(ServiceCtx context) { @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(15)] + [CommandCmif(15)] // GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting) public ResultCode GetCurrentIpConfigInfo(ServiceCtx context) { @@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(18)] + [CommandCmif(18)] // GetInternetConnectionStatus() -> nn::nifm::detail::sf::InternetConnectionStatus public ResultCode GetInternetConnectionStatus(ServiceCtx context) { @@ -149,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(21)] + [CommandCmif(21)] // IsAnyInternetRequestAccepted(buffer<nn::nifm::ClientId, 0x19, 4>) -> bool public ResultCode IsAnyInternetRequestAccepted(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs index 88757bee7..87aad30be 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService _version = version; } - [CommandHipc(0)] + [CommandCmif(0)] // GetRequestState() -> u32 public ResultCode GetRequestState(ServiceCtx context) { @@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetResult() public ResultCode GetResult(ServiceCtx context) { @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetSystemEventReadableHandles() -> (handle<copy>, handle<copy>) public ResultCode GetSystemEventReadableHandles(ServiceCtx context) { @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // Cancel() public ResultCode Cancel(ServiceCtx context) { @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // Submit() public ResultCode Submit(ServiceCtx context) { @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // SetConnectionConfirmationOption(i8) public ResultCode SetConnectionConfirmationOption(ServiceCtx context) { @@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - [CommandHipc(21)] + [CommandCmif(21)] // GetAppletInfo(u32) -> (u32, u32, u32, buffer<bytes, 6>) public ResultCode GetAppletInfo(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs index bada642c3..ab17871f8 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface { public IShopServiceAccessServer() { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateAccessorInterface(u8) -> object<nn::ec::IShopServiceAccessor> public ResultCode CreateAccessorInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs index 342f1ba1b..950004fa4 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim { public IShopServiceAccessServerInterface(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateServerInterface(pid, handle<unknown>, u64) -> object<nn::ec::IShopServiceAccessServer> public ResultCode CreateServerInterface(ServiceCtx context) { @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim return ResultCode.Success; } - [CommandHipc(4)] // 10.0.0+ + [CommandCmif(4)] // 10.0.0+ // IsLargeResourceAvailable(pid) -> b8 public ResultCode IsLargeResourceAvailable(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs index d6843d12e..3c0136faf 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServ _event = new KEvent(system.KernelContext); } - [CommandHipc(0)] + [CommandCmif(0)] // CreateAsyncInterface(u64) -> (handle<copy>, object<nn::ec::IShopServiceAsync>) public ResultCode CreateAsyncInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs b/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs index 1a4100ee3..4a63615bc 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc { public IStaticService(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // OpenEnsureNetworkClockAvailabilityService(u64) -> object<nn::ntc::detail::service::IEnsureNetworkClockAvailabilityService> public ResultCode CreateAsyncInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs b/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs index 3b533f0ff..82d0b5a80 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService // autonomic_correction_immediate_try_count_max, autonomic_correction_immediate_try_interval_milliseconds } - [CommandHipc(0)] + [CommandCmif(0)] // StartTask() public ResultCode StartTask(ServiceCtx context) { @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetFinishNotificationEvent() -> handle<copy> public ResultCode GetFinishNotificationEvent(ServiceCtx context) { @@ -53,14 +53,14 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetResult() public ResultCode GetResult(ServiceCtx context) { return _taskResultCode; } - [CommandHipc(3)] + [CommandCmif(3)] // Cancel() public ResultCode Cancel(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs index b8f9e3b97..b4b5bb1f5 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc _addOnContentListChangedEvent = new KEvent(context.Device.System.KernelContext); } - [CommandHipc(0)] // 1.0.0-6.2.0 + [CommandCmif(0)] // 1.0.0-6.2.0 // CountAddOnContentByApplicationId(u64 title_id) -> u32 public ResultCode CountAddOnContentByApplicationId(ServiceCtx context) { @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return CountAddOnContentImpl(context, titleId); } - [CommandHipc(1)] // 1.0.0-6.2.0 + [CommandCmif(1)] // 1.0.0-6.2.0 // ListAddOnContentByApplicationId(u64 title_id, u32 start_index, u32 buffer_size) -> (u32 count, buffer<u32>) public ResultCode ListAddOnContentByApplicationId(ServiceCtx context) { @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ListAddContentImpl(context, titleId); } - [CommandHipc(2)] + [CommandCmif(2)] // CountAddOnContent(pid) -> u32 public ResultCode CountAddOnContent(ServiceCtx context) { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return CountAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } - [CommandHipc(3)] + [CommandCmif(3)] // ListAddOnContent(u32 start_index, u32 buffer_size, pid) -> (u32 count, buffer<u32>) public ResultCode ListAddOnContent(ServiceCtx context) { @@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ListAddContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } - [CommandHipc(4)] // 1.0.0-6.2.0 + [CommandCmif(4)] // 1.0.0-6.2.0 // GetAddOnContentBaseIdByApplicationId(u64 title_id) -> u64 public ResultCode GetAddOnContentBaseIdByApplicationId(ServiceCtx context) { @@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return GetAddOnContentBaseIdImpl(context, titleId); } - [CommandHipc(5)] + [CommandCmif(5)] // GetAddOnContentBaseId(pid) -> u64 public ResultCode GetAddOnContentBaseId(ServiceCtx context) { @@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return GetAddOnContentBaseIdImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } - [CommandHipc(6)] // 1.0.0-6.2.0 + [CommandCmif(6)] // 1.0.0-6.2.0 // PrepareAddOnContentByApplicationId(u64 title_id, u32 index) public ResultCode PrepareAddOnContentByApplicationId(ServiceCtx context) { @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return PrepareAddOnContentImpl(context, titleId); } - [CommandHipc(7)] + [CommandCmif(7)] // PrepareAddOnContent(u32 index, pid) public ResultCode PrepareAddOnContent(ServiceCtx context) { @@ -102,14 +102,14 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return PrepareAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } - [CommandHipc(8)] // 4.0.0+ + [CommandCmif(8)] // 4.0.0+ // GetAddOnContentListChangedEvent() -> handle<copy> public ResultCode GetAddOnContentListChangedEvent(ServiceCtx context) { return GetAddOnContentListChangedEventImpl(context); } - [CommandHipc(9)] // 10.0.0+ + [CommandCmif(9)] // 10.0.0+ // GetAddOnContentLostErrorCode() -> u64 public ResultCode GetAddOnContentLostErrorCode(ServiceCtx context) { @@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(10)] // 11.0.0+ + [CommandCmif(10)] // 11.0.0+ // GetAddOnContentListChangedEventWithProcessId(pid) -> handle<copy> public ResultCode GetAddOnContentListChangedEventWithProcessId(ServiceCtx context) { @@ -138,7 +138,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return GetAddOnContentListChangedEventImpl(context); } - [CommandHipc(11)] // 13.0.0+ + [CommandCmif(11)] // 13.0.0+ // NotifyMountAddOnContent(pid, u64 title_id) public ResultCode NotifyMountAddOnContent(ServiceCtx context) { @@ -156,7 +156,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(12)] // 13.0.0+ + [CommandCmif(12)] // 13.0.0+ // NotifyUnmountAddOnContent(pid, u64 title_id) public ResultCode NotifyUnmountAddOnContent(ServiceCtx context) { @@ -171,7 +171,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(50)] // 13.0.0+ + [CommandCmif(50)] // 13.0.0+ // CheckAddOnContentMountStatus(pid) public ResultCode CheckAddOnContentMountStatus(ServiceCtx context) { @@ -185,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(100)] // 7.0.0+ + [CommandCmif(100)] // 7.0.0+ // CreateEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager> public ResultCode CreateEcPurchasedEventManager(ServiceCtx context) { @@ -194,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(101)] // 9.0.0+ + [CommandCmif(101)] // 9.0.0+ // CreatePermanentEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager> public ResultCode CreatePermanentEcPurchasedEventManager(ServiceCtx context) { @@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(110)] // 12.0.0+ + [CommandCmif(110)] // 12.0.0+ // CreateContentsServiceManager() -> object<nn::ec::IContentsServiceManager> public ResultCode CreateContentsServiceManager(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs index 5ec43a3f5..1673fafc4 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc _purchasedEvent = new KEvent(system.KernelContext); } - [CommandHipc(0)] + [CommandCmif(0)] // SetDefaultDeliveryTarget(pid, buffer<bytes, 5> unknown) public ResultCode SetDefaultDeliveryTarget(ServiceCtx context) { @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetPurchasedEventReadableHandle() -> handle<copy, event> public ResultCode GetPurchasedEventReadableHandle(ServiceCtx context) { @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // PopPurchasedProductInfo(nn::ec::detail::PurchasedProductInfo) public ResultCode PopPurchasedProductInfo(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index 249343d79..06e911f8d 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns { public IApplicationManagerInterface(ServiceCtx context) { } - [CommandHipc(400)] + [CommandCmif(400)] // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>) public ResultCode GetApplicationControlData(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs index 8f6acc1c6..aa37a1e7f 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns { public IReadOnlyApplicationControlDataInterface(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>) public ResultCode GetApplicationControlData(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs index 1d7fa2c33..886bffdd3 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns { public IServiceGetterInterface(ServiceCtx context) { } - [CommandHipc(7996)] + [CommandCmif(7996)] // GetApplicationManagerInterface() -> object<nn::ns::detail::IApplicationManagerInterface> public ResultCode GetApplicationManagerInterface(ServiceCtx context) { @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns return ResultCode.Success; } - [CommandHipc(7989)] + [CommandCmif(7989)] // GetReadOnlyApplicationControlDataInterface() -> object<nn::ns::detail::IReadOnlyApplicationControlDataInterface> public ResultCode GetReadOnlyApplicationControlDataInterface(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 32b429a6c..1d075d43f 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -235,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv } } - [CommandHipc(0)] + [CommandCmif(0)] // Open(buffer<bytes, 5> path) -> (s32 fd, u32 error_code) public ResultCode Open(ServiceCtx context) { @@ -258,7 +258,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // Ioctl(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args) public ResultCode Ioctl(ServiceCtx context) { @@ -299,7 +299,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // Close(s32 fd) -> u32 error_code public ResultCode Close(ServiceCtx context) { @@ -324,7 +324,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // Initialize(u32 transfer_memory_size, handle<copy, process> current_process, handle<copy, transfer_memory> transfer_memory) -> u32 error_code public ResultCode Initialize(ServiceCtx context) { @@ -349,7 +349,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // QueryEvent(s32 fd, u32 event_id) -> (u32, handle<copy, event>) public ResultCode QueryEvent(ServiceCtx context) { @@ -385,7 +385,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // MapSharedMemory(s32 fd, u32 argument, handle<copy, shared_memory>) -> u32 error_code public ResultCode MapSharedMemory(ServiceCtx context) { @@ -410,7 +410,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(6)] + [CommandCmif(6)] // GetStatus() -> (unknown<0x20>, u32 error_code) public ResultCode GetStatus(ServiceCtx context) { @@ -439,14 +439,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(7)] + [CommandCmif(7)] // ForceSetClientPid(u64) -> u32 error_code public ResultCode ForceSetClientPid(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(8)] + [CommandCmif(8)] // SetClientPID(u64, pid) -> u32 error_code public ResultCode SetClientPid(ServiceCtx context) { @@ -457,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(9)] + [CommandCmif(9)] // DumpGraphicsMemoryInfo() public ResultCode DumpGraphicsMemoryInfo(ServiceCtx context) { @@ -466,14 +466,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(10)] // 3.0.0+ + [CommandCmif(10)] // 3.0.0+ // InitializeDevtools(u32, handle<copy>) -> u32 error_code; public ResultCode InitializeDevtools(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(11)] // 3.0.0+ + [CommandCmif(11)] // 3.0.0+ // Ioctl2(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args, buffer<bytes, 0x21> inline_in_buffer) -> (u32 error_code, buffer<bytes, 0x22> out_args) public ResultCode Ioctl2(ServiceCtx context) { @@ -522,7 +522,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(12)] // 3.0.0+ + [CommandCmif(12)] // 3.0.0+ // Ioctl3(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args, buffer<bytes, 0x22> inline_out_buffer) public ResultCode Ioctl3(ServiceCtx context) { @@ -572,7 +572,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [CommandHipc(13)] // 3.0.0+ + [CommandCmif(13)] // 3.0.0+ // FinishInitialize(unknown<8>) public ResultCode FinishInitialize(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs index 013e90641..89fe0c3a2 100644 --- a/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Olsc public IOlscServiceForApplication(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // Initialize(pid) public ResultCode Initialize(ServiceCtx context) { @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Olsc return ResultCode.Success; } - [CommandHipc(13)] + [CommandCmif(13)] // GetSaveDataBackupSetting(nn::account::Uid) -> u8 public ResultCode GetSaveDataBackupSetting(ServiceCtx context) { @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Olsc return ResultCode.Success; } - [CommandHipc(14)] + [CommandCmif(14)] // SetSaveDataBackupSettingEnabled(nn::account::Uid, bool) public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs b/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs index f5be0cb42..990aef092 100644 --- a/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl _permissionFlag = permissionFlag; } - [CommandHipc(0)] + [CommandCmif(0)] // CreateService(u64, pid) -> object<nn::pctl::detail::ipc::IParentalControlService> public ResultCode CreateService(ServiceCtx context) { @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl return ResultCode.Success; } - [CommandHipc(1)] // 4.0.0+ + [CommandCmif(1)] // 4.0.0+ // CreateServiceWithoutInitialize(u64, pid) -> object<nn::pctl::detail::ipc::IParentalControlService> public ResultCode CreateServiceWithoutInitialize(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs b/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs index 02964749c..594ee4e0e 100644 --- a/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs +++ b/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory } } - [CommandHipc(1)] // 4.0.0+ + [CommandCmif(1)] // 4.0.0+ // Initialize() public ResultCode Initialize(ServiceCtx context) { @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return resultCode; } - [CommandHipc(1001)] + [CommandCmif(1001)] // CheckFreeCommunicationPermission() public ResultCode CheckFreeCommunicationPermission(ServiceCtx context) { @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } - [CommandHipc(1017)] // 10.0.0+ + [CommandCmif(1017)] // 10.0.0+ // EndFreeCommunication() public ResultCode EndFreeCommunication(ServiceCtx context) { @@ -107,14 +107,14 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } - [CommandHipc(1013)] // 4.0.0+ + [CommandCmif(1013)] // 4.0.0+ // ConfirmStereoVisionPermission() public ResultCode ConfirmStereoVisionPermission(ServiceCtx context) { return IsStereoVisionPermittedImpl(); } - [CommandHipc(1018)] + [CommandCmif(1018)] // IsFreeCommunicationAvailable() public ResultCode IsFreeCommunicationAvailable(ServiceCtx context) { @@ -131,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } - [CommandHipc(1031)] + [CommandCmif(1031)] // IsRestrictionEnabled() -> b8 public ResultCode IsRestrictionEnabled(ServiceCtx context) { @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } - [CommandHipc(1061)] // 4.0.0+ + [CommandCmif(1061)] // 4.0.0+ // ConfirmStereoVisionRestrictionConfigurable() public ResultCode ConfirmStereoVisionRestrictionConfigurable(ServiceCtx context) { @@ -164,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory } } - [CommandHipc(1062)] // 4.0.0+ + [CommandCmif(1062)] // 4.0.0+ // GetStereoVisionRestriction() -> bool public ResultCode GetStereoVisionRestriction(ServiceCtx context) { @@ -185,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } - [CommandHipc(1063)] // 4.0.0+ + [CommandCmif(1063)] // 4.0.0+ // SetStereoVisionRestriction(bool) public ResultCode SetStereoVisionRestriction(ServiceCtx context) { @@ -209,14 +209,14 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } - [CommandHipc(1064)] // 5.0.0+ + [CommandCmif(1064)] // 5.0.0+ // ResetConfirmedStereoVisionPermission() public ResultCode ResetConfirmedStereoVisionPermission(ServiceCtx context) { return ResultCode.Success; } - [CommandHipc(1065)] // 5.0.0+ + [CommandCmif(1065)] // 5.0.0+ // IsStereoVisionPermitted() -> bool public ResultCode IsStereoVisionPermitted(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs b/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs index 6e1e9e6d6..e185c4787 100644 --- a/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs +++ b/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Bpc { public IRtcManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // GetRtcTime() -> u64 public ResultCode GetRtcTime(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs b/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs index 890c9ac97..b81e7fee0 100644 --- a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs +++ b/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst.ClkrstManager _unknown = unknown; } - [CommandHipc(7)] + [CommandCmif(7)] // SetClockRate(u32 hz) public ResultCode SetClockRate(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst.ClkrstManager return ResultCode.Success; } - [CommandHipc(8)] + [CommandCmif(8)] // GetClockRate() -> u32 hz public ResultCode GetClockRate(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs b/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs index 94ab49cac..4ba2f0945 100644 --- a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs +++ b/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst public IClkrstManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // OpenSession(u32 device_code, u32 unk) -> object<nn::clkrst::IClkrstSession> public ResultCode OpenSession(ServiceCtx context) { @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetModuleStateTableEvent() -> handle<copy> public ResultCode GetModuleStateTableEvent(ServiceCtx context) { @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetModuleStateTableMaxCount() -> u32 max_count public ResultCode GetModuleStateTableMaxCount(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs index 8d4934fab..cce2967a7 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Pm { public IDebugMonitorInterface(ServiceCtx context) { } - [CommandHipc(4)] + [CommandCmif(4)] // GetProgramId() -> sf::Out<ncm::ProgramId> out_process_id public ResultCode GetApplicationProcessId(ServiceCtx context) { @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Pm return ResultCode.ProcessNotFound; } - [CommandHipc(65000)] + [CommandCmif(65000)] // AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status public ResultCode GetProcessInfo(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs index e3ce6d2af..b3b5595fd 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs @@ -1,5 +1,4 @@ -using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.HLE.HOS.Kernel.Process; namespace Ryujinx.HLE.HOS.Services.Pm { @@ -8,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Pm { public IInformationInterface(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // GetProgramId(os::ProcessId process_id) -> sf::Out<ncm::ProgramId> out public ResultCode GetProgramId(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs index e59b02d81..962023264 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs @@ -5,7 +5,7 @@ { public IShellInterface(ServiceCtx context) { } - [CommandHipc(6)] + [CommandCmif(6)] // GetApplicationPid() -> u64 public ResultCode GetApplicationPid(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs index 96a503d41..4e3d3e8e2 100644 --- a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs +++ b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm { public IPsmServer(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // GetBatteryChargePercentage() -> u32 public static ResultCode GetBatteryChargePercentage(ServiceCtx context) { @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetChargerType() -> u32 public static ResultCode GetChargerType(ServiceCtx context) { @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } - [CommandHipc(7)] + [CommandCmif(7)] // OpenSession() -> IPsmSession public ResultCode OpenSession(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs index 9b4e996d9..5d11f227a 100644 --- a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs +++ b/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm _stateChangeEventHandle = -1; } - [CommandHipc(0)] + [CommandCmif(0)] // BindStateChangeEvent() -> KObject public ResultCode BindStateChangeEvent(ServiceCtx context) { @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // UnbindStateChangeEvent() public ResultCode UnbindStateChangeEvent(ServiceCtx context) { @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // SetChargerTypeChangeEventEnabled(u8) public ResultCode SetChargerTypeChangeEventEnabled(ServiceCtx context) { @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // SetPowerSupplyChangeEventEnabled(u8) public ResultCode SetPowerSupplyChangeEventEnabled(ServiceCtx context) { @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // SetBatteryVoltageStateChangeEventEnabled(u8) public ResultCode SetBatteryVoltageStateChangeEventEnabled(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs b/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs index d5a30097b..6ddc0aef2 100644 --- a/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs +++ b/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Ts public IMeasurementServer(ServiceCtx context) { } - [CommandHipc(1)] + [CommandCmif(1)] // GetTemperature(Location location) -> u32 public ResultCode GetTemperature(ServiceCtx context) { @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Ts return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetTemperatureMilliC(Location location) -> u32 public ResultCode GetTemperatureMilliC(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index 263e1c4ce..966adcffe 100644 --- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -424,7 +424,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.InvalidProcess; } - [CommandHipc(0)] + [CommandCmif(0)] // LoadNro(u64, u64, u64, u64, u64, pid) -> u64 public ResultCode LoadNro(ServiceCtx context) { @@ -469,7 +469,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return result; } - [CommandHipc(1)] + [CommandCmif(1)] // UnloadNro(u64, u64, pid) public ResultCode UnloadNro(ServiceCtx context) { @@ -493,7 +493,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return result; } - [CommandHipc(2)] + [CommandCmif(2)] // LoadNrr(u64, u64, u64, pid) public ResultCode LoadNrr(ServiceCtx context) { @@ -526,7 +526,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return result; } - [CommandHipc(3)] + [CommandCmif(3)] // UnloadNrr(u64, u64, pid) public ResultCode UnloadNrr(ServiceCtx context) { @@ -550,7 +550,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return result; } - [CommandHipc(4)] + [CommandCmif(4)] // Initialize(u64, pid, KObject) public ResultCode Initialize(ServiceCtx context) { @@ -572,7 +572,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // LoadNrr2(u64, u64, u64, pid) public ResultCode LoadNrr2(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs index a243b0a0b..1f66ff9de 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs @@ -7,14 +7,14 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm { public IQueryService(ServiceCtx context) { } - [CommandHipc(13)] // 5.0.0+ + [CommandCmif(13)] // 5.0.0+ // QueryApplicationPlayStatisticsForSystem(buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count) public ResultCode QueryApplicationPlayStatisticsForSystem(ServiceCtx context) { return QueryPlayStatisticsManager.GetPlayStatistics(context); } - [CommandHipc(16)] // 6.0.0+ + [CommandCmif(16)] // 6.0.0+ // QueryApplicationPlayStatisticsByUserAccountIdForSystem(nn::account::Uid, buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count) public ResultCode QueryApplicationPlayStatisticsByUserAccountIdForSystem(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs index f95c1d1f3..9e2f7a4e5 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl public ISharedFontManager(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // RequestLoad(u32) public ResultCode RequestLoad(ServiceCtx context) { @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetLoadState(u32) -> u32 public ResultCode GetLoadState(ServiceCtx context) { @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetFontSize(u32) -> u32 public ResultCode GetFontSize(ServiceCtx context) { @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetSharedMemoryAddressOffset(u32) -> u32 public ResultCode GetSharedMemoryAddressOffset(ServiceCtx context) { @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetSharedMemoryNativeHandle() -> handle<copy> public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context) { @@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetSharedFontInOrderOfPriority(bytes<8, 1>) -> (u8, u32, buffer<unknown, 6>, buffer<unknown, 6>, buffer<unknown, 6>) public ResultCode GetSharedFontInOrderOfPriority(ServiceCtx context) { @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return ResultCode.Success; } - [CommandHipc(6)] // 4.0.0+ + [CommandCmif(6)] // 4.0.0+ // GetSharedFontInOrderOfPriorityForSystem(bytes<8, 1>) -> (u8, u32, buffer<unknown, 6>, buffer<unknown, 6>, buffer<unknown, 6>) public ResultCode GetSharedFontInOrderOfPriorityForSystem(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 8b82b771f..762b4fa44 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -236,10 +236,10 @@ namespace Ryujinx.HLE.HOS.Services _requestDataStream.Write(request.RawData); _requestDataStream.Position = 0; - if (request.Type == IpcMessageType.HipcRequest || - request.Type == IpcMessageType.HipcRequestWithContext) + if (request.Type == IpcMessageType.CmifRequest || + request.Type == IpcMessageType.CmifRequestWithContext) { - response.Type = IpcMessageType.HipcResponse; + response.Type = IpcMessageType.CmifResponse; _responseDataStream.SetLength(0); @@ -253,12 +253,12 @@ namespace Ryujinx.HLE.HOS.Services _requestDataReader, _responseDataWriter); - _sessions[serverSessionHandle].CallHipcMethod(context); + _sessions[serverSessionHandle].CallCmifMethod(context); response.RawData = _responseDataStream.ToArray(); } - else if (request.Type == IpcMessageType.HipcControl || - request.Type == IpcMessageType.HipcControlWithContext) + else if (request.Type == IpcMessageType.CmifControl || + request.Type == IpcMessageType.CmifControlWithContext) { uint magic = (uint)_requestDataReader.ReadUInt64(); uint cmdId = (uint)_requestDataReader.ReadUInt64(); @@ -291,7 +291,7 @@ namespace Ryujinx.HLE.HOS.Services default: throw new NotImplementedException(cmdId.ToString()); } } - else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession) + else if (request.Type == IpcMessageType.CmifCloseSession || request.Type == IpcMessageType.TipcCloseSession) { _context.Syscall.CloseHandle(serverSessionHandle); lock (_handleLock) @@ -376,7 +376,7 @@ namespace Ryujinx.HLE.HOS.Services private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data) { - response.Type = IpcMessageType.HipcResponse; + response.Type = IpcMessageType.CmifResponse; _responseDataStream.SetLength(0); @@ -418,4 +418,4 @@ namespace Ryujinx.HLE.HOS.Services Dispose(true); } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs index 703f61632..17e9ec683 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings { public ISettingsServer(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // GetLanguageCode() -> nn::settings::LanguageCode public ResultCode GetLanguageCode(ServiceCtx context) { @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetAvailableLanguageCodes() -> (u32, buffer<nn::settings::LanguageCode, 0xa>) public ResultCode GetAvailableLanguageCodes(ServiceCtx context) { @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0xF); } - [CommandHipc(2)] // 4.0.0+ + [CommandCmif(2)] // 4.0.0+ // MakeLanguageCode(nn::settings::Language language_index) -> nn::settings::LanguageCode public ResultCode MakeLanguageCode(ServiceCtx context) { @@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetAvailableLanguageCodeCount() -> u32 public ResultCode GetAvailableLanguageCodeCount(ServiceCtx context) { @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetRegionCode() -> u32 nn::settings::RegionCode public ResultCode GetRegionCode(ServiceCtx context) { @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetAvailableLanguageCodes2() -> (u32, buffer<nn::settings::LanguageCode, 6>) public ResultCode GetAvailableLanguageCodes2(ServiceCtx context) { @@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings SystemStateMgr.LanguageCodes.Length); } - [CommandHipc(6)] + [CommandCmif(6)] // GetAvailableLanguageCodeCount2() -> u32 public ResultCode GetAvailableLanguageCodeCount2(ServiceCtx context) { @@ -93,14 +93,14 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(7)] // 4.0.0+ + [CommandCmif(7)] // 4.0.0+ // GetKeyCodeMap() -> buffer<nn::kpr::KeyCodeMap, 0x16> public ResultCode GetKeyCodeMap(ServiceCtx context) { return GetKeyCodeMapImpl(context, 1); } - [CommandHipc(8)] // 5.0.0+ + [CommandCmif(8)] // 5.0.0+ // GetQuestFlag() -> bool public ResultCode GetQuestFlag(ServiceCtx context) { @@ -111,14 +111,14 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(9)] // 6.0.0+ + [CommandCmif(9)] // 6.0.0+ // GetKeyCodeMap2() -> buffer<nn::kpr::KeyCodeMap, 0x16> public ResultCode GetKeyCodeMap2(ServiceCtx context) { return GetKeyCodeMapImpl(context, 2); } - [CommandHipc(11)] // 10.1.0+ + [CommandCmif(11)] // 10.1.0+ // GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16> public ResultCode GetDeviceNickName(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index bae10d0b0..ef95fa5cb 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -19,14 +19,14 @@ namespace Ryujinx.HLE.HOS.Services.Settings { public ISystemSettingsServer(ServiceCtx context) { } - [CommandHipc(3)] + [CommandCmif(3)] // GetFirmwareVersion() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100> public ResultCode GetFirmwareVersion(ServiceCtx context) { return GetFirmwareVersion2(context); } - [CommandHipc(4)] + [CommandCmif(4)] // GetFirmwareVersion2() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100> public ResultCode GetFirmwareVersion2(ServiceCtx context) { @@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(23)] + [CommandCmif(23)] // GetColorSetId() -> i32 public ResultCode GetColorSetId(ServiceCtx context) { @@ -96,7 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(24)] + [CommandCmif(24)] // GetColorSetId() -> i32 public ResultCode SetColorSetId(ServiceCtx context) { @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(37)] + [CommandCmif(37)] // GetSettingsItemValueSize(buffer<nn::settings::SettingsName, 0x19>, buffer<nn::settings::SettingsItemKey, 0x19>) -> u64 public ResultCode GetSettingsItemValueSize(ServiceCtx context) { @@ -156,7 +156,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(38)] + [CommandCmif(38)] // GetSettingsItemValue(buffer<nn::settings::SettingsName, 0x19, 0x48>, buffer<nn::settings::SettingsItemKey, 0x19, 0x48>) -> (u64, buffer<unknown, 6, 0>) public ResultCode GetSettingsItemValue(ServiceCtx context) { @@ -222,7 +222,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(60)] + [CommandCmif(60)] // IsUserSystemClockAutomaticCorrectionEnabled() -> bool public ResultCode IsUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) { @@ -234,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(62)] + [CommandCmif(62)] // GetDebugModeFlag() -> bool public ResultCode GetDebugModeFlag(ServiceCtx context) { @@ -245,7 +245,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(77)] + [CommandCmif(77)] // GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16> public ResultCode GetDeviceNickName(ServiceCtx context) { @@ -267,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(78)] + [CommandCmif(78)] // SetDeviceNickName(buffer<nn::settings::system::DeviceNickName, 0x15>) public ResultCode SetDeviceNickName(ServiceCtx context) { @@ -283,7 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - [CommandHipc(90)] + [CommandCmif(90)] // GetMiiAuthorId() -> nn::util::Uuid public ResultCode GetMiiAuthorId(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 73652da4b..005ec32d8 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm .ToDictionary(service => service.Name, service => service.type); } - [CommandHipc(0)] + [CommandCmif(0)] [CommandTipc(0)] // 12.0.0+ // Initialize(pid, u64 reserved) public ResultCode Initialize(ServiceCtx context) @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm return GetService(context); } - [CommandHipc(1)] + [CommandCmif(1)] public ResultCode GetService(ServiceCtx context) { if (!_isInitialized) @@ -127,9 +127,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // RegisterService(ServiceName name, u8 isLight, u32 maxHandles) -> handle<move, port> - public ResultCode RegisterServiceHipc(ServiceCtx context) + public ResultCode RegisterServiceCmif(ServiceCtx context) { if (!_isInitialized) { @@ -199,7 +199,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] [CommandTipc(3)] // 12.0.0+ // UnregisterService(ServiceName name) public ResultCode UnregisterService(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs index bc174cce5..b63864c90 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd context.Memory.Write(bufferPosition, BsdSockAddr.FromIPEndPoint(endPoint)); } - [CommandHipc(0)] + [CommandCmif(0)] // Initialize(nn::socket::BsdBufferConfig config, u64 pid, u64 transferMemorySize, KObject<copy, transfer_memory>, pid) -> u32 bsd_errno public ResultCode RegisterClient(ServiceCtx context) { @@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // StartMonitoring(u64, pid) public ResultCode StartMonitoring(ServiceCtx context) { @@ -169,21 +169,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) public ResultCode Socket(ServiceCtx context) { return SocketInternal(context, false); } - [CommandHipc(3)] + [CommandCmif(3)] // SocketExempt(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) public ResultCode SocketExempt(ServiceCtx context) { return SocketInternal(context, true); } - [CommandHipc(4)] + [CommandCmif(4)] // Open(u32 flags, array<unknown, 0x21> path) -> (i32 ret, u32 bsd_errno) public ResultCode Open(ServiceCtx context) { @@ -204,7 +204,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // Select(u32 nfds, nn::socket::timeval timeout, buffer<nn::socket::fd_set, 0x21, 0> readfds_in, buffer<nn::socket::fd_set, 0x21, 0> writefds_in, buffer<nn::socket::fd_set, 0x21, 0> errorfds_in) // -> (i32 ret, u32 bsd_errno, buffer<nn::socket::fd_set, 0x22, 0> readfds_out, buffer<nn::socket::fd_set, 0x22, 0> writefds_out, buffer<nn::socket::fd_set, 0x22, 0> errorfds_out) public ResultCode Select(ServiceCtx context) @@ -325,7 +325,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return ResultCode.Success; } - [CommandHipc(6)] + [CommandCmif(6)] // Poll(u32 nfds, u32 timeout, buffer<unknown, 0x21, 0> fds) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>) public ResultCode Poll(ServiceCtx context) { @@ -463,7 +463,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, updateCount, errno); } - [CommandHipc(7)] + [CommandCmif(7)] // Sysctl(buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>) public ResultCode Sysctl(ServiceCtx context) { @@ -474,7 +474,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return ResultCode.Success; } - [CommandHipc(8)] + [CommandCmif(8)] // Recv(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, array<i8, 0x22> message) public ResultCode Recv(ServiceCtx context) { @@ -504,7 +504,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(9)] + [CommandCmif(9)] // RecvFrom(u32 sock, u32 flags) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<i8, 0x22, 0> message, buffer<nn::socket::sockaddr_in, 0x22, 0x10>) public ResultCode RecvFrom(ServiceCtx context) { @@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(10)] + [CommandCmif(10)] // Send(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno) public ResultCode Send(ServiceCtx context) { @@ -572,7 +572,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(11)] + [CommandCmif(11)] // SendTo(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<nn::socket::sockaddr_in, 0x21, 0x10>) -> (i32 ret, u32 bsd_errno) public ResultCode SendTo(ServiceCtx context) { @@ -603,7 +603,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(12)] + [CommandCmif(12)] // Accept(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr) public ResultCode Accept(ServiceCtx context) { @@ -646,7 +646,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, -1, errno); } - [CommandHipc(13)] + [CommandCmif(13)] // Bind(u32 socket, buffer<nn::socket::sockaddr_in, 0x21, 0x10> addr) -> (i32 ret, u32 bsd_errno) public ResultCode Bind(ServiceCtx context) { @@ -667,7 +667,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(14)] + [CommandCmif(14)] // Connect(u32 socket, buffer<nn::socket::sockaddr_in, 0x21, 0x10>) -> (i32 ret, u32 bsd_errno) public ResultCode Connect(ServiceCtx context) { @@ -688,7 +688,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(15)] + [CommandCmif(15)] // GetPeerName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr) public ResultCode GetPeerName(ServiceCtx context) { @@ -715,7 +715,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(16)] + [CommandCmif(16)] // GetSockName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr) public ResultCode GetSockName(ServiceCtx context) { @@ -738,7 +738,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(17)] + [CommandCmif(17)] // GetSockOpt(u32 socket, u32 level, u32 option_name) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>) public ResultCode GetSockOpt(ServiceCtx context) { @@ -765,7 +765,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(18)] + [CommandCmif(18)] // Listen(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno) public ResultCode Listen(ServiceCtx context) { @@ -783,7 +783,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(19)] + [CommandCmif(19)] // Ioctl(u32 fd, u32 request, u32 bufcount, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>) public ResultCode Ioctl(ServiceCtx context) { @@ -818,7 +818,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(20)] + [CommandCmif(20)] // Fcntl(u32 socket, u32 cmd, u32 arg) -> (i32 ret, u32 bsd_errno) public ResultCode Fcntl(ServiceCtx context) { @@ -852,7 +852,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(21)] + [CommandCmif(21)] // SetSockOpt(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0> option_value) -> (i32 ret, u32 bsd_errno) public ResultCode SetSockOpt(ServiceCtx context) { @@ -875,7 +875,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(22)] + [CommandCmif(22)] // Shutdown(u32 socket, u32 how) -> (i32 ret, u32 bsd_errno) public ResultCode Shutdown(ServiceCtx context) { @@ -898,7 +898,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(23)] + [CommandCmif(23)] // ShutdownAllSockets(u32 how) -> (i32 ret, u32 bsd_errno) public ResultCode ShutdownAllSockets(ServiceCtx context) { @@ -914,7 +914,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(24)] + [CommandCmif(24)] // Write(u32 fd, buffer<i8, 0x21, 0> message) -> (i32 ret, u32 bsd_errno) public ResultCode Write(ServiceCtx context) { @@ -941,7 +941,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(25)] + [CommandCmif(25)] // Read(u32 fd) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message) public ResultCode Read(ServiceCtx context) { @@ -970,7 +970,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(26)] + [CommandCmif(26)] // Close(u32 fd) -> (i32 ret, u32 bsd_errno) public ResultCode Close(ServiceCtx context) { @@ -986,7 +986,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, 0, errno); } - [CommandHipc(27)] + [CommandCmif(27)] // DuplicateSocket(u32 fd, u64 reserved) -> (i32 ret, u32 bsd_errno) public ResultCode DuplicateSocket(ServiceCtx context) { @@ -1012,7 +1012,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } - [CommandHipc(29)] // 7.0.0+ + [CommandCmif(29)] // 7.0.0+ // RecvMMsg(u32 fd, u32 vlen, u32 flags, u32 reserved, nn::socket::TimeVal timeout) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message); public ResultCode RecvMMsg(ServiceCtx context) { @@ -1055,7 +1055,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(30)] // 7.0.0+ + [CommandCmif(30)] // 7.0.0+ // SendMMsg(u32 fd, u32 vlen, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message); public ResultCode SendMMsg(ServiceCtx context) { @@ -1096,7 +1096,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - [CommandHipc(31)] // 7.0.0+ + [CommandCmif(31)] // 7.0.0+ // EventFd(nn::socket::EventFdFlags flags, u64 initval) -> (i32 ret, u32 bsd_errno) public ResultCode EventFd(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs index b098e2edc..0b7adff4f 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs @@ -47,21 +47,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd }; } - [CommandHipc(5)] // 11.0.0+ + [CommandCmif(5)] // 11.0.0+ // GetSettingUrl() -> buffer<unknown<0x100>, 0x16> public ResultCode GetSettingUrl(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(10)] + [CommandCmif(10)] // GetSettingName() -> buffer<unknown<0x100>, 0x16> public ResultCode GetSettingName(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(11)] + [CommandCmif(11)] // GetEnvironmentIdentifier() -> buffer<bytes<8> environment_identifier, 0x16> public ResultCode GetEnvironmentIdentifier(ServiceCtx context) { @@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return result; } - [CommandHipc(12)] + [CommandCmif(12)] // GetDeviceId() -> bytes<0x10, 1> public ResultCode GetDeviceId(ServiceCtx context) { @@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(13)] + [CommandCmif(13)] // DeleteSettings(u32) public ResultCode DeleteSettings(ServiceCtx context) { @@ -127,14 +127,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(14)] + [CommandCmif(14)] // ImportSettings(u32, buffer<unknown, 5>) -> buffer<unknown, 6> public ResultCode ImportSettings(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(15)] // 4.0.0+ + [CommandCmif(15)] // 4.0.0+ // SetChangeEnvironmentIdentifierDisabled(bytes<1>) public ResultCode SetChangeEnvironmentIdentifierDisabled(ServiceCtx context) { @@ -153,7 +153,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(20)] + [CommandCmif(20)] // Resolve(buffer<unknown<0x100>, 0x15>) -> buffer<unknown<0x100>, 0x16> public ResultCode Resolve(ServiceCtx context) { @@ -176,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return result; } - [CommandHipc(21)] + [CommandCmif(21)] // ResolveEx(buffer<unknown<0x100>, 0x15>) -> (u32, buffer<unknown<0x100>, 0x16>) public ResultCode ResolveEx(ServiceCtx context) { @@ -201,56 +201,56 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return result; } - [CommandHipc(30)] + [CommandCmif(30)] // GetNasServiceSetting(buffer<unknown<0x10>, 0x15>) -> buffer<unknown<0x108>, 0x16> public ResultCode GetNasServiceSetting(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(31)] + [CommandCmif(31)] // GetNasServiceSettingEx(buffer<unknown<0x10>, 0x15>) -> (u32, buffer<unknown<0x108>, 0x16>) public ResultCode GetNasServiceSettingEx(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(40)] + [CommandCmif(40)] // GetNasRequestFqdn() -> buffer<unknown<0x100>, 0x16> public ResultCode GetNasRequestFqdn(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(41)] + [CommandCmif(41)] // GetNasRequestFqdnEx() -> (u32, buffer<unknown<0x100>, 0x16>) public ResultCode GetNasRequestFqdnEx(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(42)] + [CommandCmif(42)] // GetNasApiFqdn() -> buffer<unknown<0x100>, 0x16> public ResultCode GetNasApiFqdn(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(43)] + [CommandCmif(43)] // GetNasApiFqdnEx() -> (u32, buffer<unknown<0x100>, 0x16>) public ResultCode GetNasApiFqdnEx(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(50)] + [CommandCmif(50)] // GetCurrentSetting() -> buffer<unknown<0x12bf0>, 0x16> public ResultCode GetCurrentSetting(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(51)] // 9.0.0+ + [CommandCmif(51)] // 9.0.0+ // WriteTestParameter(buffer<?>) public ResultCode WriteTestParameter(ServiceCtx context) { @@ -259,7 +259,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd throw new ServiceNotImplementedException(this, context); } - [CommandHipc(52)] // 9.0.0+ + [CommandCmif(52)] // 9.0.0+ // ReadTestParameter() -> buffer<?> public ResultCode ReadTestParameter(ServiceCtx context) { @@ -268,7 +268,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd throw new ServiceNotImplementedException(this, context); } - [CommandHipc(60)] + [CommandCmif(60)] // ReadSaveDataFromFsForTest() -> buffer<unknown<0x12bf0>, 0x16> public ResultCode ReadSaveDataFromFsForTest(ServiceCtx context) { @@ -284,7 +284,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(61)] + [CommandCmif(61)] // WriteSaveDataToFsForTest(buffer<unknown<0x12bf0>, 0x15>) public ResultCode WriteSaveDataToFsForTest(ServiceCtx context) { @@ -313,7 +313,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(62)] + [CommandCmif(62)] // DeleteSaveDataOfFsForTest() public ResultCode DeleteSaveDataOfFsForTest(ServiceCtx context) { @@ -342,7 +342,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(63)] // 4.0.0+ + [CommandCmif(63)] // 4.0.0+ // IsChangeEnvironmentIdentifierDisabled() -> bytes<1> public ResultCode IsChangeEnvironmentIdentifierDisabled(ServiceCtx context) { @@ -357,7 +357,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(100)] // 10.0.0+ + [CommandCmif(100)] // 10.0.0+ // GetApplicationServerEnvironmentType() -> bytes<1> public ResultCode GetApplicationServerEnvironmentType(ServiceCtx context) { @@ -385,14 +385,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.Success; } - [CommandHipc(101)] // 10.0.0+ + [CommandCmif(101)] // 10.0.0+ // SetApplicationServerEnvironmentType(bytes<1>) public ResultCode SetApplicationServerEnvironmentType(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(102)] // 10.0.0+ + [CommandCmif(102)] // 10.0.0+ // DeleteApplicationServerEnvironmentType() public ResultCode DeleteApplicationServerEnvironmentType(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs index 13c5597fd..64c3acbb5 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres DnsMitmResolver.Instance.ReloadEntries(context); } - [CommandHipc(0)] + [CommandCmif(0)] // SetDnsAddressesPrivateRequest(u32, buffer<unknown, 5, 0>) public ResultCode SetDnsAddressesPrivateRequest(ServiceCtx context) { @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return ResultCode.NotAllocated; } - [CommandHipc(1)] + [CommandCmif(1)] // GetDnsAddressPrivateRequest(u32) -> buffer<unknown, 6, 0> public ResultCode GetDnsAddressPrivateRequest(ServiceCtx context) { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return ResultCode.NotAllocated; } - [CommandHipc(2)] + [CommandCmif(2)] // GetHostByNameRequest(u8, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>) public ResultCode GetHostByNameRequest(ServiceCtx context) { @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, false, 0, 0); } - [CommandHipc(3)] + [CommandCmif(3)] // GetHostByAddrRequest(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>) public ResultCode GetHostByAddrRequest(ServiceCtx context) { @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, false, 0, 0); } - [CommandHipc(4)] + [CommandCmif(4)] // GetHostStringErrorRequest(u32) -> buffer<unknown, 6, 0> public ResultCode GetHostStringErrorRequest(ServiceCtx context) { @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return resultCode; } - [CommandHipc(5)] + [CommandCmif(5)] // GetGaiStringErrorRequest(u32) -> buffer<byte, 6, 0> public ResultCode GetGaiStringErrorRequest(ServiceCtx context) { @@ -152,7 +152,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return resultCode; } - [CommandHipc(6)] + [CommandCmif(6)] // GetAddrInfoRequest(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints) -> (i32 ret, u32 bsd_errno, u32 packed_addrinfo_size, buffer<packed_addrinfo, 6, 0> response) public ResultCode GetAddrInfoRequest(ServiceCtx context) { @@ -162,7 +162,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, false, 0, 0); } - [CommandHipc(8)] + [CommandCmif(8)] // GetCancelHandleRequest(u64, pid) -> u32 public ResultCode GetCancelHandleRequest(ServiceCtx context) { @@ -176,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return ResultCode.Success; } - [CommandHipc(9)] + [CommandCmif(9)] // CancelRequest(u32, u64, pid) public ResultCode CancelRequest(ServiceCtx context) { @@ -188,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return ResultCode.Success; } - [CommandHipc(10)] // 5.0.0+ + [CommandCmif(10)] // 5.0.0+ // GetHostByNameRequestWithOptions(u8, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>) public ResultCode GetHostByNameRequestWithOptions(ServiceCtx context) { @@ -207,7 +207,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres optionsBufferSize); } - [CommandHipc(11)] // 5.0.0+ + [CommandCmif(11)] // 5.0.0+ // GetHostByAddrRequestWithOptions(u32, u32, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>) public ResultCode GetHostByAddrRequestWithOptions(ServiceCtx context) { @@ -226,7 +226,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres optionsBufferSize); } - [CommandHipc(12)] // 5.0.0+ + [CommandCmif(12)] // 5.0.0+ // GetAddrInfoRequestWithOptions(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints, buffer<unknown, 21, 0>) -> (i32 ret, u32 bsd_errno, u32 unknown, u32 packed_addrinfo_size, buffer<packed_addrinfo, 22, 0> response) public ResultCode GetAddrInfoRequestWithOptions(ServiceCtx context) { @@ -236,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return GetAddrInfoRequestImpl(context, outputBufferPosition, outputBufferSize, true, optionsBufferPosition, optionsBufferSize); } - [CommandHipc(14)] // 5.0.0+ + [CommandCmif(14)] // 5.0.0+ // ResolverSetOptionRequest(buffer<unknown, 5, 0>, u64 unknown, u64 pid_placeholder, pid) -> (i32 ret, u32 bsd_errno) public ResultCode ResolverSetOptionRequest(ServiceCtx context) { @@ -263,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres } // Atmosphère extension for dns_mitm - [CommandHipc(65000)] + [CommandCmif(65000)] // AtmosphereReloadHostsFile() public ResultCode AtmosphereReloadHostsFile(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs b/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs index 30e22e120..aa350b734 100644 --- a/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl { public IGeneralInterface(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // GetConfig(u32 config_item) -> u64 config_value public ResultCode GetConfig(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs index 3de746aca..c911f434c 100644 --- a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl _rng = RandomNumberGenerator.Create(); } - [CommandHipc(0)] + [CommandCmif(0)] // GetRandomBytes() -> buffer<unknown, 6> public ResultCode GetRandomBytes(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs index 73f044418..7741ef7ed 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl // In this case it is acceptable to stub all calls of the service. public ISslService(ServiceCtx context) { } - [CommandHipc(0)] + [CommandCmif(0)] // CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object<nn::ssl::sf::ISslContext> public ResultCode CreateContext(ServiceCtx context) { @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetCertificates(buffer<CaCertificateId, 5> ids) -> (u32 certificates_count, buffer<bytes, 6> certificates) public ResultCode GetCertificates(ServiceCtx context) { @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetCertificateBufSize(buffer<CaCertificateId, 5> ids) -> u32 buffer_size; public ResultCode GetCertificateBufSize(ServiceCtx context) { @@ -110,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // SetInterfaceVersion(u32) public ResultCode SetInterfaceVersion(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs index 726ee122d..b9087f409 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService _verifyOption = VerifyOption.PeerCa | VerifyOption.HostName; } - [CommandHipc(0)] + [CommandCmif(0)] // SetSocketDescriptor(u32) -> u32 public ResultCode SetSocketDescriptor(ServiceCtx context) { @@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService _connection = new SslManagedSocketConnection(_bsdContext, _sslVersion, socketFd, bsdSocket); } - [CommandHipc(1)] + [CommandCmif(1)] // SetHostName(buffer<bytes, 5>) public ResultCode SetHostName(ServiceCtx context) { @@ -100,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // SetVerifyOption(nn::ssl::sf::VerifyOption) public ResultCode SetVerifyOption(ServiceCtx context) { @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // SetIoMode(nn::ssl::sf::IoMode) public ResultCode SetIoMode(ServiceCtx context) { @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetSocketDescriptor() -> u32 public ResultCode GetSocketDescriptor(ServiceCtx context) { @@ -138,7 +138,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetHostName(buffer<bytes, 6>) -> u32 public ResultCode GetHostName(ServiceCtx context) { @@ -157,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(6)] + [CommandCmif(6)] // GetVerifyOption() -> nn::ssl::sf::VerifyOption public ResultCode GetVerifyOption(ServiceCtx context) { @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(7)] + [CommandCmif(7)] // GetIoMode() -> nn::ssl::sf::IoMode public ResultCode GetIoMode(ServiceCtx context) { @@ -179,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(8)] + [CommandCmif(8)] // DoHandshake() public ResultCode DoHandshake(ServiceCtx context) { @@ -191,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return _connection.Handshake(_hostName); } - [CommandHipc(9)] + [CommandCmif(9)] // DoHandshakeGetServerCert() -> (u32, u32, buffer<bytes, 6>) public ResultCode DoHandshakeGetServerCert(ServiceCtx context) { @@ -224,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return result; } - [CommandHipc(10)] + [CommandCmif(10)] // Read() -> (u32, buffer<bytes, 6>) public ResultCode Read(ServiceCtx context) { @@ -249,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return result; } - [CommandHipc(11)] + [CommandCmif(11)] // Write(buffer<bytes, 5>) -> s32 public ResultCode Write(ServiceCtx context) { @@ -272,7 +272,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return result; } - [CommandHipc(12)] + [CommandCmif(12)] // Pending() -> s32 public ResultCode Pending(ServiceCtx context) { @@ -286,7 +286,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(13)] + [CommandCmif(13)] // Peek() -> (s32, buffer<bytes, 6>) public ResultCode Peek(ServiceCtx context) { @@ -311,28 +311,28 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return result; } - [CommandHipc(14)] + [CommandCmif(14)] // Poll(nn::ssl::sf::PollEvent poll_event, u32 timeout) -> nn::ssl::sf::PollEvent public ResultCode Poll(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(15)] + [CommandCmif(15)] // GetVerifyCertError() public ResultCode GetVerifyCertError(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(16)] + [CommandCmif(16)] // GetNeededServerCertBufferSize() -> u32 public ResultCode GetNeededServerCertBufferSize(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(17)] + [CommandCmif(17)] // SetSessionCacheMode(nn::ssl::sf::SessionCacheMode) public ResultCode SetSessionCacheMode(ServiceCtx context) { @@ -345,7 +345,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(18)] + [CommandCmif(18)] // GetSessionCacheMode() -> nn::ssl::sf::SessionCacheMode public ResultCode GetSessionCacheMode(ServiceCtx context) { @@ -356,28 +356,28 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(19)] + [CommandCmif(19)] // FlushSessionCache() public ResultCode FlushSessionCache(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(20)] + [CommandCmif(20)] // SetRenegotiationMode(nn::ssl::sf::RenegotiationMode) public ResultCode SetRenegotiationMode(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(21)] + [CommandCmif(21)] // GetRenegotiationMode() -> nn::ssl::sf::RenegotiationMode public ResultCode GetRenegotiationMode(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(22)] + [CommandCmif(22)] // SetOption(b8 value, nn::ssl::sf::OptionType option) public ResultCode SetOption(ServiceCtx context) { @@ -389,7 +389,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return SetOption(option, value); } - [CommandHipc(23)] + [CommandCmif(23)] // GetOption(nn::ssl::sf::OptionType) -> b8 public ResultCode GetOption(ServiceCtx context) { @@ -407,21 +407,21 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return result; } - [CommandHipc(24)] + [CommandCmif(24)] // GetVerifyCertErrors() -> (u32, u32, buffer<bytes, 6>) public ResultCode GetVerifyCertErrors(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(25)] // 4.0.0+ + [CommandCmif(25)] // 4.0.0+ // GetCipherInfo(u32) -> buffer<bytes, 6> public ResultCode GetCipherInfo(ServiceCtx context) { throw new ServiceNotImplementedException(this, context); } - [CommandHipc(26)] + [CommandCmif(26)] // SetNextAlpnProto(buffer<bytes, 5>) -> u32 public ResultCode SetNextAlpnProto(ServiceCtx context) { @@ -437,7 +437,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(27)] + [CommandCmif(27)] // GetNextAlpnProto(buffer<bytes, 6>) -> u32 public ResultCode GetNextAlpnProto(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs index 93b9b423e..b38ff9214 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService _sslVersion = sslVersion; } - [CommandHipc(2)] + [CommandCmif(2)] // CreateConnection() -> object<nn::ssl::sf::ISslConnection> public ResultCode CreateConnection(ServiceCtx context) { @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetConnectionCount() -> u32 count public ResultCode GetConnectionCount(ServiceCtx context) { @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // ImportServerPki(nn::ssl::sf::CertificateFormat certificateFormat, buffer<bytes, 5> certificate) -> u64 certificateId public ResultCode ImportServerPki(ServiceCtx context) { @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // ImportClientPki(buffer<bytes, 5> certificate, buffer<bytes, 5> ascii_password) -> u64 certificateId public ResultCode ImportClientPki(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index d641f7f09..0724c83ee 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { public IHOSBinderDriver() { } - [CommandHipc(0)] + [CommandCmif(0)] // TransactParcel(s32, u32, u32, buffer<unknown, 5, 0>) -> buffer<unknown, 6, 0> public ResultCode TransactParcel(ServiceCtx context) { @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return result; } - [CommandHipc(1)] + [CommandCmif(1)] // AdjustRefcount(s32, s32, s32) public ResultCode AdjustRefcount(ServiceCtx context) { @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return AdjustRefcount(binderId, addVal, type); } - [CommandHipc(2)] + [CommandCmif(2)] // GetNativeHandle(s32, s32) -> handle<copy> public ResultCode GetNativeHandle(ServiceCtx context) { @@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return ResultCode.Success; } - [CommandHipc(3)] // 3.0.0+ + [CommandCmif(3)] // 3.0.0+ // TransactParcelAuto(s32, u32, u32, buffer<unknown, 21, 0>) -> buffer<unknown, 22, 0> public ResultCode TransactParcelAuto(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs index c65f00420..31548b80c 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs @@ -23,28 +23,28 @@ namespace Ryujinx.HLE.HOS.Services.Time _inner.SetParent(this); } - [CommandHipc(0)] + [CommandCmif(0)] // GetStandardUserSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetStandardUserSystemClock(ServiceCtx context) { return _inner.GetStandardUserSystemClock(context); } - [CommandHipc(1)] + [CommandCmif(1)] // GetStandardNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetStandardNetworkSystemClock(ServiceCtx context) { return _inner.GetStandardNetworkSystemClock(context); } - [CommandHipc(2)] + [CommandCmif(2)] // GetStandardSteadyClock() -> object<nn::timesrv::detail::service::ISteadyClock> public ResultCode GetStandardSteadyClock(ServiceCtx context) { return _inner.GetStandardSteadyClock(context); } - [CommandHipc(3)] + [CommandCmif(3)] // GetTimeZoneService() -> object<nn::timesrv::detail::service::ITimeZoneService> public ResultCode GetTimeZoneService(ServiceCtx context) { @@ -53,28 +53,28 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetStandardLocalSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetStandardLocalSystemClock(ServiceCtx context) { return _inner.GetStandardLocalSystemClock(context); } - [CommandHipc(5)] // 4.0.0+ + [CommandCmif(5)] // 4.0.0+ // GetEphemeralNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetEphemeralNetworkSystemClock(ServiceCtx context) { return _inner.GetEphemeralNetworkSystemClock(context); } - [CommandHipc(20)] // 6.0.0+ + [CommandCmif(20)] // 6.0.0+ // GetSharedMemoryNativeHandle() -> handle<copy> public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context) { return _inner.GetSharedMemoryNativeHandle(context); } - [CommandHipc(50)] // 4.0.0+ + [CommandCmif(50)] // 4.0.0+ // SetStandardSteadyClockInternalOffset(nn::TimeSpanType internal_offset) public ResultCode SetStandardSteadyClockInternalOffset(ServiceCtx context) { @@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(51)] // 9.0.0+ + [CommandCmif(51)] // 9.0.0+ // GetStandardSteadyClockRtcValue() -> u64 public ResultCode GetStandardSteadyClockRtcValue(ServiceCtx context) { @@ -104,21 +104,21 @@ namespace Ryujinx.HLE.HOS.Services.Time return result; } - [CommandHipc(100)] + [CommandCmif(100)] // IsStandardUserSystemClockAutomaticCorrectionEnabled() -> bool public ResultCode IsStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) { return _inner.IsStandardUserSystemClockAutomaticCorrectionEnabled(context); } - [CommandHipc(101)] + [CommandCmif(101)] // SetStandardUserSystemClockAutomaticCorrectionEnabled(b8) public ResultCode SetStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) { return _inner.SetStandardUserSystemClockAutomaticCorrectionEnabled(context); } - [CommandHipc(102)] // 5.0.0+ + [CommandCmif(102)] // 5.0.0+ // GetStandardUserSystemClockInitialYear() -> u32 public ResultCode GetStandardUserSystemClockInitialYear(ServiceCtx context) { @@ -132,49 +132,49 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(200)] // 3.0.0+ + [CommandCmif(200)] // 3.0.0+ // IsStandardNetworkSystemClockAccuracySufficient() -> bool public ResultCode IsStandardNetworkSystemClockAccuracySufficient(ServiceCtx context) { return _inner.IsStandardNetworkSystemClockAccuracySufficient(context); } - [CommandHipc(201)] // 6.0.0+ + [CommandCmif(201)] // 6.0.0+ // GetStandardUserSystemClockAutomaticCorrectionUpdatedTime() -> nn::time::SteadyClockTimePoint public ResultCode GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(ServiceCtx context) { return _inner.GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(context); } - [CommandHipc(300)] // 4.0.0+ + [CommandCmif(300)] // 4.0.0+ // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> s64 public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context) { return _inner.CalculateMonotonicSystemClockBaseTimePoint(context); } - [CommandHipc(400)] // 4.0.0+ + [CommandCmif(400)] // 4.0.0+ // GetClockSnapshot(u8) -> buffer<nn::time::sf::ClockSnapshot, 0x1a> public ResultCode GetClockSnapshot(ServiceCtx context) { return _inner.GetClockSnapshot(context); } - [CommandHipc(401)] // 4.0.0+ + [CommandCmif(401)] // 4.0.0+ // GetClockSnapshotFromSystemClockContext(u8, nn::time::SystemClockContext, nn::time::SystemClockContext) -> buffer<nn::time::sf::ClockSnapshot, 0x1a> public ResultCode GetClockSnapshotFromSystemClockContext(ServiceCtx context) { return _inner.GetClockSnapshotFromSystemClockContext(context); } - [CommandHipc(500)] // 4.0.0+ + [CommandCmif(500)] // 4.0.0+ // CalculateStandardUserSystemClockDifferenceByUser(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> nn::TimeSpanType public ResultCode CalculateStandardUserSystemClockDifferenceByUser(ServiceCtx context) { return _inner.CalculateStandardUserSystemClockDifferenceByUser(context); } - [CommandHipc(501)] // 4.0.0+ + [CommandCmif(501)] // 4.0.0+ // CalculateSpanBetween(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> nn::TimeSpanType public ResultCode CalculateSpanBetween(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs index abb5bb40d..145d4e3b9 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Time _timeManager = manager; } - [CommandHipc(0)] + [CommandCmif(0)] // GetStandardUserSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetStandardUserSystemClock(ServiceCtx context) { @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(1)] + [CommandCmif(1)] // GetStandardNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetStandardNetworkSystemClock(ServiceCtx context) { @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetStandardSteadyClock() -> object<nn::timesrv::detail::service::ISteadyClock> public ResultCode GetStandardSteadyClock(ServiceCtx context) { @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // GetTimeZoneService() -> object<nn::timesrv::detail::service::ITimeZoneService> public ResultCode GetTimeZoneService(ServiceCtx context) { @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(4)] + [CommandCmif(4)] // GetStandardLocalSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetStandardLocalSystemClock(ServiceCtx context) { @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(5)] // 4.0.0+ + [CommandCmif(5)] // 4.0.0+ // GetEphemeralNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock> public ResultCode GetEphemeralNetworkSystemClock(ServiceCtx context) { @@ -96,7 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(20)] // 6.0.0+ + [CommandCmif(20)] // 6.0.0+ // GetSharedMemoryNativeHandle() -> handle<copy> public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context) { @@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(50)] // 4.0.0+ + [CommandCmif(50)] // 4.0.0+ // SetStandardSteadyClockInternalOffset(nn::TimeSpanType internal_offset) public ResultCode SetStandardSteadyClockInternalOffset(ServiceCtx context) { @@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.NotImplemented; } - [CommandHipc(51)] // 9.0.0+ + [CommandCmif(51)] // 9.0.0+ // GetStandardSteadyClockRtcValue() -> u64 public ResultCode GetStandardSteadyClockRtcValue(ServiceCtx context) { @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.NotImplemented; } - [CommandHipc(100)] + [CommandCmif(100)] // IsStandardUserSystemClockAutomaticCorrectionEnabled() -> bool public ResultCode IsStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) { @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(101)] + [CommandCmif(101)] // SetStandardUserSystemClockAutomaticCorrectionEnabled(b8) public ResultCode SetStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) { @@ -181,7 +181,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return result; } - [CommandHipc(102)] // 5.0.0+ + [CommandCmif(102)] // 5.0.0+ // GetStandardUserSystemClockInitialYear() -> u32 public ResultCode GetStandardUserSystemClockInitialYear(ServiceCtx context) { @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.NotImplemented; } - [CommandHipc(200)] // 3.0.0+ + [CommandCmif(200)] // 3.0.0+ // IsStandardNetworkSystemClockAccuracySufficient() -> bool public ResultCode IsStandardNetworkSystemClockAccuracySufficient(ServiceCtx context) { @@ -200,7 +200,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(201)] // 6.0.0+ + [CommandCmif(201)] // 6.0.0+ // GetStandardUserSystemClockAutomaticCorrectionUpdatedTime() -> nn::time::SteadyClockTimePoint public ResultCode GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(ServiceCtx context) { @@ -216,7 +216,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(300)] // 4.0.0+ + [CommandCmif(300)] // 4.0.0+ // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> s64 public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context) { @@ -247,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return result; } - [CommandHipc(400)] // 4.0.0+ + [CommandCmif(400)] // 4.0.0+ // GetClockSnapshot(u8) -> buffer<nn::time::sf::ClockSnapshot, 0x1a> public ResultCode GetClockSnapshot(ServiceCtx context) { @@ -277,7 +277,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return result; } - [CommandHipc(401)] // 4.0.0+ + [CommandCmif(401)] // 4.0.0+ // GetClockSnapshotFromSystemClockContext(u8, nn::time::SystemClockContext, nn::time::SystemClockContext) -> buffer<nn::time::sf::ClockSnapshot, 0x1a> public ResultCode GetClockSnapshotFromSystemClockContext(ServiceCtx context) { @@ -302,7 +302,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return result; } - [CommandHipc(500)] // 4.0.0+ + [CommandCmif(500)] // 4.0.0+ // CalculateStandardUserSystemClockDifferenceByUser(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> nn::TimeSpanType public ResultCode CalculateStandardUserSystemClockDifferenceByUser(ServiceCtx context) { @@ -320,7 +320,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(501)] // 4.0.0+ + [CommandCmif(501)] // 4.0.0+ // CalculateSpanBetween(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> nn::TimeSpanType public ResultCode CalculateSpanBetween(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs index aae9aaafc..6c9c15f1b 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Time _automaticCorrectionEvent = 0; } - [CommandHipc(0)] + [CommandCmif(0)] // GetUserStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetUserStaticService(ServiceCtx context) { @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(5)] + [CommandCmif(5)] // GetAdminStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetAdminStaticService(ServiceCtx context) { @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(6)] + [CommandCmif(6)] // GetRepairStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetRepairStaticService(ServiceCtx context) { @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(9)] + [CommandCmif(9)] // GetManufactureStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetManufactureStaticService(ServiceCtx context) { @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(10)] + [CommandCmif(10)] // SetupStandardSteadyClock(nn::util::Uuid clock_source_id, nn::TimeSpanType setup_value, nn::TimeSpanType internal_offset, nn::TimeSpanType test_offset, bool is_rtc_reset_detected) public ResultCode SetupStandardSteadyClock(ServiceCtx context) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(11)] + [CommandCmif(11)] // SetupStandardLocalSystemClock(nn::time::SystemClockContext context, nn::time::PosixTime posix_time) public ResultCode SetupStandardLocalSystemClock(ServiceCtx context) { @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(12)] + [CommandCmif(12)] // SetupStandardNetworkSystemClock(nn::time::SystemClockContext context, nn::TimeSpanType sufficient_accuracy) public ResultCode SetupStandardNetworkSystemClock(ServiceCtx context) { @@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(13)] + [CommandCmif(13)] // SetupStandardUserSystemClock(bool automatic_correction_enabled, nn::time::SteadyClockTimePoint steady_clock_timepoint) public ResultCode SetupStandardUserSystemClock(ServiceCtx context) { @@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(14)] + [CommandCmif(14)] // SetupTimeZoneManager(nn::time::LocationName location_name, nn::time::SteadyClockTimePoint timezone_update_timepoint, u32 total_location_name_count, nn::time::TimeZoneRuleVersion timezone_rule_version, buffer<nn::time::TimeZoneBinary, 0x21> timezone_binary) public ResultCode SetupTimeZoneManager(ServiceCtx context) { @@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(15)] + [CommandCmif(15)] // SetupEphemeralNetworkSystemClock() public ResultCode SetupEphemeralNetworkSystemClock(ServiceCtx context) { @@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(50)] + [CommandCmif(50)] // Unknown50() -> handle<copy> public ResultCode Unknown50(ServiceCtx context) { @@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.Time throw new ServiceNotImplementedException(this, context); } - [CommandHipc(51)] + [CommandCmif(51)] // Unknown51() -> handle<copy> public ResultCode Unknown51(ServiceCtx context) { @@ -166,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Time throw new ServiceNotImplementedException(this, context); } - [CommandHipc(52)] + [CommandCmif(52)] // Unknown52() -> handle<copy> public ResultCode Unknown52(ServiceCtx context) { @@ -174,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Time throw new ServiceNotImplementedException(this, context); } - [CommandHipc(60)] + [CommandCmif(60)] // GetStandardUserSystemClockAutomaticCorrectionEvent() -> handle<copy> public ResultCode GetStandardUserSystemClockAutomaticCorrectionEvent(ServiceCtx context) { @@ -191,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(100)] + [CommandCmif(100)] // SetStandardSteadyClockRtcOffset(nn::TimeSpanType rtc_offset) public ResultCode SetStandardSteadyClockRtcOffset(ServiceCtx context) { @@ -204,7 +204,7 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } - [CommandHipc(200)] + [CommandCmif(200)] // GetAlarmRegistrationEvent() -> handle<copy> public ResultCode GetAlarmRegistrationEvent(ServiceCtx context) { @@ -212,7 +212,7 @@ namespace Ryujinx.HLE.HOS.Services.Time throw new ServiceNotImplementedException(this, context); } - [CommandHipc(201)] + [CommandCmif(201)] // UpdateSteadyAlarms() public ResultCode UpdateSteadyAlarms(ServiceCtx context) { @@ -220,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Services.Time throw new ServiceNotImplementedException(this, context); } - [CommandHipc(202)] + [CommandCmif(202)] // TryGetNextSteadyClockAlarmSnapshot() -> (bool, nn::time::SteadyClockAlarmSnapshot) public ResultCode TryGetNextSteadyClockAlarmSnapshot(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs index 1e5177131..97d7884ea 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService _bypassUninitializedClock = bypassUninitializedClock; } - [CommandHipc(0)] + [CommandCmif(0)] // GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint public ResultCode GetCurrentTimePoint(ServiceCtx context) { @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } - [CommandHipc(2)] + [CommandCmif(2)] // GetTestOffset() -> nn::TimeSpanType public ResultCode GetTestOffset(ServiceCtx context) { @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // SetTestOffset(nn::TimeSpanType) public ResultCode SetTestOffset(ServiceCtx context) { @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } - [CommandHipc(100)] // 2.0.0+ + [CommandCmif(100)] // 2.0.0+ // GetRtcValue() -> u64 public ResultCode GetRtcValue(ServiceCtx context) { @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(101)] // 2.0.0+ + [CommandCmif(101)] // 2.0.0+ // IsRtcResetDetected() -> bool public ResultCode IsRtcResetDetected(ServiceCtx context) { @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } - [CommandHipc(102)] // 2.0.0+ + [CommandCmif(102)] // 2.0.0+ // GetSetupResultValue() -> u32 public ResultCode GetSetupResultValue(ServiceCtx context) { @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } - [CommandHipc(200)] // 3.0.0+ + [CommandCmif(200)] // 3.0.0+ // GetInternalOffset() -> nn::TimeSpanType public ResultCode GetInternalOffset(ServiceCtx context) { @@ -131,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } - [CommandHipc(201)] // 3.0.0-3.0.2 + [CommandCmif(201)] // 3.0.0-3.0.2 // SetInternalOffset(nn::TimeSpanType) public ResultCode SetInternalOffset(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs index c43c15820..3cd0a4a69 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService _operationEventReadableHandle = 0; } - [CommandHipc(0)] + [CommandCmif(0)] // GetCurrentTime() -> nn::time::PosixTime public ResultCode GetCurrentTime(ServiceCtx context) { @@ -44,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(1)] + [CommandCmif(1)] // SetCurrentTime(nn::time::PosixTime) public ResultCode SetCurrentTime(ServiceCtx context) { @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return _clockCore.SetCurrentTime(tickSource, posixTime); } - [CommandHipc(2)] + [CommandCmif(2)] // GetClockContext() -> nn::time::SystemClockContext public ResultCode GetSystemClockContext(ServiceCtx context) { @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(3)] + [CommandCmif(3)] // SetClockContext(nn::time::SystemClockContext) public ResultCode SetSystemClockContext(ServiceCtx context) { @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(4)] // 9.0.0+ + [CommandCmif(4)] // 9.0.0+ // GetOperationEventReadableHandle() -> handle<copy> public ResultCode GetOperationEventReadableHandle(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs index 265cec5dd..96a7e604f 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs @@ -23,14 +23,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService _inner = new ITimeZoneServiceForPsc(timeZoneContentManager.Manager, writePermission); } - [CommandHipc(0)] + [CommandCmif(0)] // GetDeviceLocationName() -> nn::time::LocationName public ResultCode GetDeviceLocationName(ServiceCtx context) { return _inner.GetDeviceLocationName(context); } - [CommandHipc(1)] + [CommandCmif(1)] // SetDeviceLocationName(nn::time::LocationName) public ResultCode SetDeviceLocationName(ServiceCtx context) { @@ -44,14 +44,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return _timeZoneContentManager.SetDeviceLocationName(locationName); } - [CommandHipc(2)] + [CommandCmif(2)] // GetTotalLocationNameCount() -> u32 public ResultCode GetTotalLocationNameCount(ServiceCtx context) { return _inner.GetTotalLocationNameCount(context); } - [CommandHipc(3)] + [CommandCmif(3)] // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) public ResultCode LoadLocationNameList(ServiceCtx context) { @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return errorCode; } - [CommandHipc(4)] + [CommandCmif(4)] // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> public ResultCode LoadTimeZoneRule(ServiceCtx context) { @@ -111,28 +111,28 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService } } - [CommandHipc(100)] + [CommandCmif(100)] // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) public ResultCode ToCalendarTime(ServiceCtx context) { return _inner.ToCalendarTime(context); } - [CommandHipc(101)] + [CommandCmif(101)] // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context) { return _inner.ToCalendarTimeWithMyRule(context); } - [CommandHipc(201)] + [CommandCmif(201)] // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) public ResultCode ToPosixTime(ServiceCtx context) { return _inner.ToPosixTime(context); } - [CommandHipc(202)] + [CommandCmif(202)] // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) public ResultCode ToPosixTimeWithMyRule(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs index e006c829e..3c9ac71f3 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService _writePermission = writePermission; } - [CommandHipc(0)] + [CommandCmif(0)] // GetDeviceLocationName() -> nn::time::LocationName public ResultCode GetDeviceLocationName(ServiceCtx context) { @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(1)] + [CommandCmif(1)] // SetDeviceLocationName(nn::time::LocationName) public ResultCode SetDeviceLocationName(ServiceCtx context) { @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.NotImplemented; } - [CommandHipc(2)] + [CommandCmif(2)] // GetTotalLocationNameCount() -> u32 public ResultCode GetTotalLocationNameCount(ServiceCtx context) { @@ -63,21 +63,21 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } - [CommandHipc(3)] + [CommandCmif(3)] // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) public ResultCode LoadLocationNameList(ServiceCtx context) { return ResultCode.NotImplemented; } - [CommandHipc(4)] + [CommandCmif(4)] // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> public ResultCode LoadTimeZoneRule(ServiceCtx context) { return ResultCode.NotImplemented; } - [CommandHipc(5)] // 2.0.0+ + [CommandCmif(5)] // 2.0.0+ // GetTimeZoneRuleVersion() -> nn::time::TimeZoneRuleVersion public ResultCode GetTimeZoneRuleVersion(ServiceCtx context) { @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(6)] // 5.0.0+ + [CommandCmif(6)] // 5.0.0+ // GetDeviceLocationNameAndUpdatedTime() -> (nn::time::LocationName, nn::time::SteadyClockTimePoint) public ResultCode GetDeviceLocationNameAndUpdatedTime(ServiceCtx context) { @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(7)] // 9.0.0+ + [CommandCmif(7)] // 9.0.0+ // SetDeviceLocationNameWithTimeZoneRule(nn::time::LocationName locationName, buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) public ResultCode SetDeviceLocationNameWithTimeZoneRule(ServiceCtx context) { @@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(8)] // 9.0.0+ + [CommandCmif(8)] // 9.0.0+ // ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16> public ResultCode ParseTimeZoneBinary(ServiceCtx context) { @@ -178,14 +178,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return result; } - [CommandHipc(20)] // 9.0.0+ + [CommandCmif(20)] // 9.0.0+ // GetDeviceLocationNameOperationEventReadableHandle() -> handle<copy> public ResultCode GetDeviceLocationNameOperationEventReadableHandle(ServiceCtx context) { return ResultCode.NotImplemented; } - [CommandHipc(100)] + [CommandCmif(100)] // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) public ResultCode ToCalendarTime(ServiceCtx context) { @@ -213,7 +213,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return resultCode; } - [CommandHipc(101)] + [CommandCmif(101)] // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context) { @@ -229,7 +229,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return resultCode; } - [CommandHipc(201)] + [CommandCmif(201)] // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) public ResultCode ToPosixTime(ServiceCtx context) { @@ -262,7 +262,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return resultCode; } - [CommandHipc(202)] + [CommandCmif(202)] // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) public ResultCode ToPosixTimeWithMyRule(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs index b4399c584..526cecf84 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi { public IApplicationRootService(ServiceCtx context) : base(context.Device.System.ViServer) { } - [CommandHipc(0)] + [CommandCmif(0)] // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService> public ResultCode GetDisplayService(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs index 7852c613f..d564dabe7 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase public IManagerRootService(ServiceCtx context) : base(context.Device.System.ViServerM) { } - [CommandHipc(2)] + [CommandCmif(2)] // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService> public ResultCode GetDisplayService(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs index f1122efe8..0dfd84f59 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase public ISystemRootService(ServiceCtx context) : base(context.Device.System.ViServerS) { } - [CommandHipc(1)] + [CommandCmif(1)] // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService> public ResultCode GetDisplayService(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs index f40a053c6..6093381cc 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService _applicationDisplayService = applicationDisplayService; } - [CommandHipc(1102)] + [CommandCmif(1102)] // GetDisplayResolution(u64 display_id) -> (u64 width, u64 height) public ResultCode GetDisplayResolution(ServiceCtx context) { @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } - [CommandHipc(2010)] + [CommandCmif(2010)] // CreateManagedLayer(u32, u64, nn::applet::AppletResourceUserId) -> u64 public ResultCode CreateManagedLayer(ServiceCtx context) { @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } - [CommandHipc(2011)] + [CommandCmif(2011)] // DestroyManagedLayer(u64) public ResultCode DestroyManagedLayer(ServiceCtx context) { @@ -52,14 +52,14 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return context.Device.System.SurfaceFlinger.DestroyManagedLayer(layerId); } - [CommandHipc(2012)] // 7.0.0+ + [CommandCmif(2012)] // 7.0.0+ // CreateStrayLayer(u32, u64) -> (u64, u64, buffer<bytes, 6>) public ResultCode CreateStrayLayer(ServiceCtx context) { return _applicationDisplayService.CreateStrayLayer(context); } - [CommandHipc(6000)] + [CommandCmif(6000)] // AddToLayerStack(u32, u64) public ResultCode AddToLayerStack(ServiceCtx context) { @@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } - [CommandHipc(6002)] + [CommandCmif(6002)] // SetLayerVisibility(b8, u64) public ResultCode SetLayerVisibility(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs index 82dd6af63..a24aa0798 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService _applicationDisplayService = applicationDisplayService; } - [CommandHipc(2205)] + [CommandCmif(2205)] // SetLayerZ(u64, u64) public ResultCode SetLayerZ(ServiceCtx context) { @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } - [CommandHipc(2207)] + [CommandCmif(2207)] // SetLayerVisibility(b8, u64) public ResultCode SetLayerVisibility(ServiceCtx context) { @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } - [CommandHipc(2312)] // 1.0.0-6.2.0 + [CommandCmif(2312)] // 1.0.0-6.2.0 // CreateStrayLayer(u32, u64) -> (u64, u64, buffer<bytes, 6>) public ResultCode CreateStrayLayer(ServiceCtx context) { @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return _applicationDisplayService.CreateStrayLayer(context); } - [CommandHipc(3200)] + [CommandCmif(3200)] // GetDisplayMode(u64) -> nn::vi::DisplayModeInfo public ResultCode GetDisplayMode(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 085d6c519..52ed52220 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService AddDisplayInfo("Null", false, 0, 1920, 1080); } - [CommandHipc(100)] + [CommandCmif(100)] // GetRelayService() -> object<nns::hosbinder::IHOSBinderDriver> public ResultCode GetRelayService(ServiceCtx context) { @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(101)] + [CommandCmif(101)] // GetSystemDisplayService() -> object<nn::visrv::sf::ISystemDisplayService> public ResultCode GetSystemDisplayService(ServiceCtx context) { @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(102)] + [CommandCmif(102)] // GetManagerDisplayService() -> object<nn::visrv::sf::IManagerDisplayService> public ResultCode GetManagerDisplayService(ServiceCtx context) { @@ -106,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(103)] // 2.0.0+ + [CommandCmif(103)] // 2.0.0+ // GetIndirectDisplayTransactionService() -> object<nns::hosbinder::IHOSBinderDriver> public ResultCode GetIndirectDisplayTransactionService(ServiceCtx context) { @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(1000)] + [CommandCmif(1000)] // ListDisplays() -> (u64 count, buffer<nn::vi::DisplayInfo, 6>) public ResultCode ListDisplays(ServiceCtx context) { @@ -139,7 +139,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(1010)] + [CommandCmif(1010)] // OpenDisplay(nn::vi::DisplayName) -> u64 display_id public ResultCode OpenDisplay(ServiceCtx context) { @@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return OpenDisplayImpl(context, name); } - [CommandHipc(1011)] + [CommandCmif(1011)] // OpenDefaultDisplay() -> u64 display_id public ResultCode OpenDefaultDisplay(ServiceCtx context) { @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(1020)] + [CommandCmif(1020)] // CloseDisplay(u64 display_id) public ResultCode CloseDisplay(ServiceCtx context) { @@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(1101)] + [CommandCmif(1101)] // SetDisplayEnabled(u32 enabled_bool, u64 display_id) public ResultCode SetDisplayEnabled(ServiceCtx context) { @@ -211,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(1102)] + [CommandCmif(1102)] // GetDisplayResolution(u64 display_id) -> (u64 width, u64 height) public ResultCode GetDisplayResolution(ServiceCtx context) { @@ -227,7 +227,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(2020)] + [CommandCmif(2020)] // OpenLayer(nn::vi::DisplayName, u64, nn::applet::AppletResourceUserId, pid) -> (u64, buffer<bytes, 6>) public ResultCode OpenLayer(ServiceCtx context) { @@ -260,7 +260,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(2021)] + [CommandCmif(2021)] // CloseLayer(u64) public ResultCode CloseLayer(ServiceCtx context) { @@ -269,7 +269,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return context.Device.System.SurfaceFlinger.CloseLayer(layerId); } - [CommandHipc(2030)] + [CommandCmif(2030)] // CreateStrayLayer(u32, u64) -> (u64, u64, buffer<bytes, 6>) public ResultCode CreateStrayLayer(ServiceCtx context) { @@ -297,7 +297,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(2031)] + [CommandCmif(2031)] // DestroyStrayLayer(u64) public ResultCode DestroyStrayLayer(ServiceCtx context) { @@ -306,7 +306,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return context.Device.System.SurfaceFlinger.DestroyStrayLayer(layerId); } - [CommandHipc(2101)] + [CommandCmif(2101)] // SetLayerScalingMode(u32, u64) public ResultCode SetLayerScalingMode(ServiceCtx context) { @@ -319,7 +319,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(2102)] // 5.0.0+ + [CommandCmif(2102)] // 5.0.0+ // ConvertScalingMode(u32 source_scaling_mode) -> u64 destination_scaling_mode public ResultCode ConvertScalingMode(ServiceCtx context) { @@ -366,7 +366,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return (requiredMemorySize + defaultSize - 1) / defaultSize * defaultSize; } - [CommandHipc(2450)] + [CommandCmif(2450)] // GetIndirectLayerImageMap(s64 width, s64 height, u64 handle, nn::applet::AppletResourceUserId, pid) -> (s64, s64, buffer<bytes, 0x46>) public ResultCode GetIndirectLayerImageMap(ServiceCtx context) { @@ -413,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(2460)] + [CommandCmif(2460)] // GetIndirectLayerImageRequiredMemoryInfo(u64 width, u64 height) -> (u64 size, u64 alignment) public ResultCode GetIndirectLayerImageRequiredMemoryInfo(ServiceCtx context) { @@ -454,7 +454,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } - [CommandHipc(5202)] + [CommandCmif(5202)] // GetDisplayVsyncEvent(u64) -> handle<copy> public ResultCode GetDisplayVSyncEvent(ServiceCtx context) { From 5c89e22bb98072adc240c2eb2d26d25fa119fe7d Mon Sep 17 00:00:00 2001 From: Daniel Shala <daniel.shala08@gmail.com> Date: Sat, 15 Apr 2023 18:11:24 +0200 Subject: [PATCH 446/737] Added check for eventual symlink when displaying game files. (#4526) * Added check for eventual symlink when displaying game files. * Moved symlink check logic * Moved symlink check logic * Fixed prev commit --------- Co-authored-by: Daniel Shala <danielshala00@gmail.com> --- Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 8686383e9..66cb7c73b 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -116,11 +116,13 @@ namespace Ryujinx.Ui.App.Common return; } - string extension = Path.GetExtension(app).ToLower(); + var fileInfo = new FileInfo(app); + string extension = fileInfo.Extension.ToLower(); - if (!File.GetAttributes(app).HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso") + if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso") { - applications.Add(app); + var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; + applications.Add(fullPath); numApplicationsFound++; } } @@ -904,4 +906,5 @@ namespace Ryujinx.Ui.App.Common return (null, null); } } -} \ No newline at end of file +} + From c5258cf082b10f335f81487f22b7eeb86075e09e Mon Sep 17 00:00:00 2001 From: NitroTears <73270647+NitroTears@users.noreply.github.com> Date: Sun, 16 Apr 2023 11:03:35 +1000 Subject: [PATCH 447/737] Ability to hide file types in Game List (#4555) * Added HiddenFileTypes to config state, and check to file enumeration * Added hiddenfiletypes checkboxes to the UI * Added Ava version of HiddenFileTypes * Inverted Hide to Show with file types, minor formatting * all variables with a reference to 'hidden' is now 'shown' * one more variable name changed * review feedback * added FileTypes extension methof to get the correlating config value * moved extension method to new folder and file in Ryujinx.Ui.Common * added default case for ToggleFileType * changed exception type to OutOfRangeException --- Ryujinx.Ava/Assets/Locales/en_US.json | 1 + .../UI/ViewModels/MainWindowViewModel.cs | 17 +++++ .../UI/Views/Main/MainMenuBarView.axaml | 1 + .../UI/Views/Main/MainMenuBarView.axaml.cs | 28 +++++++- Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 15 +++- .../Configuration/ConfigurationFileFormat.cs | 7 +- .../Configuration/ConfigurationState.cs | 71 ++++++++++++++++++- Ryujinx.Ui.Common/Configuration/FileTypes.cs | 12 ++++ .../Configuration/Ui/ShownFileTypes.cs | 12 ++++ .../Extensions/FileTypeExtensions.cs | 25 +++++++ Ryujinx/Ui/MainWindow.cs | 68 ++++++++++++++++++ Ryujinx/Ui/MainWindow.glade | 69 ++++++++++++++++++ 12 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 Ryujinx.Ui.Common/Configuration/FileTypes.cs create mode 100644 Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs create mode 100644 Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index fb8f800c0..b2d6b43d7 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -429,6 +429,7 @@ "DlcManagerEnableAllButton": "Enable All", "DlcManagerDisableAllButton": "Disable All", "MenuBarOptionsChangeLanguage": "Change Language", + "MenuBarShowFileTypes": "Show File Types", "CommonSort": "Sort", "CommonShowNames": "Show Names", "CommonFavorite": "Favorite", diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index d5ff78545..da2115265 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1336,6 +1336,23 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public void ToggleFileType(string fileType) + { + _ = fileType switch + { + "NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP, + "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0, + "XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI, + "NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA, + "NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO, + "NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO, + _ => throw new ArgumentOutOfRangeException(fileType), + }; + + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + LoadApplications(); + } + public async void ManageProfiles() { await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient); diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml index b1d768ead..d5b5efcdd 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml @@ -78,6 +78,7 @@ </MenuItem> <Separator /> <MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" /> + <MenuItem Name="ToggleFileTypesMenuItem" Header="{locale:Locale MenuBarShowFileTypes}" /> <Separator /> <MenuItem Click="OpenSettings" diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index c1eaf86fe..bdf2cf9f8 100644 --- a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -11,6 +11,8 @@ using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS; using Ryujinx.Modules; +using Ryujinx.Ui.Common; +using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; @@ -29,6 +31,30 @@ namespace Ryujinx.Ava.UI.Views.Main { InitializeComponent(); + ToggleFileTypesMenuItem.Items = GenerateToggleFileTypeItems(); + ChangeLanguageMenuItem.Items = GenerateLanguageMenuItems(); + } + + private CheckBox[] GenerateToggleFileTypeItems() + { + List<CheckBox> checkBoxes = new(); + + foreach (var item in Enum.GetValues(typeof (FileTypes))) + { + string fileName = Enum.GetName(typeof (FileTypes), item); + checkBoxes.Add(new CheckBox() + { + Content = $".{fileName}", + IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes), + Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)) + }); + } + + return checkBoxes.ToArray(); + } + + private MenuItem[] GenerateLanguageMenuItems() + { List<MenuItem> menuItems = new(); string localePath = "Ryujinx.Ava/Assets/Locales"; @@ -61,7 +87,7 @@ namespace Ryujinx.Ava.UI.Views.Main menuItems.Add(menuItem); } - ChangeLanguageMenuItem.Items = menuItems.ToArray(); + return menuItems.ToArray(); } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 66cb7c73b..b7b57f1a2 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -14,9 +14,11 @@ using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Npdm; +using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; using System; using System.Collections.Generic; +using System.Linq; using System.Globalization; using System.IO; using System.Reflection; @@ -109,7 +111,18 @@ namespace Ryujinx.Ui.App.Common try { - foreach (string app in Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories)) + IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories).Where(file => + { + return + (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value) || + (Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value) || + (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value) || + (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value) || + (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value) || + (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value); + }); + + foreach (string app in files) { if (_cancellationToken.Token.IsCancellationRequested) { diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 14c03957a..cb9adc86c 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <summary> /// The current version of the file format /// </summary> - public const int CurrentVersion = 44; + public const int CurrentVersion = 45; /// <summary> /// Version of the configuration file format @@ -246,6 +246,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public List<string> GameDirs { get; set; } + /// <summary> + /// A list of file types to be hidden in the games List + /// </summary> + public ShownFileTypes ShownFileTypes { get; set; } + /// <summary> /// Language Code for the UI /// </summary> diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index 82a331c16..12cc1b8f8 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -60,6 +60,29 @@ namespace Ryujinx.Ui.Common.Configuration } } + /// <summary> + /// Used to toggle which file types are shown in the UI + /// </summary> + public class ShownFileTypeSettings + { + public ReactiveObject<bool> NSP { get; private set; } + public ReactiveObject<bool> PFS0 { get; private set; } + public ReactiveObject<bool> XCI { get; private set; } + public ReactiveObject<bool> NCA { get; private set; } + public ReactiveObject<bool> NRO { get; private set; } + public ReactiveObject<bool> NSO { get; private set; } + + public ShownFileTypeSettings() + { + NSP = new ReactiveObject<bool>(); + PFS0 = new ReactiveObject<bool>(); + XCI = new ReactiveObject<bool>(); + NCA = new ReactiveObject<bool>(); + NRO = new ReactiveObject<bool>(); + NSO = new ReactiveObject<bool>(); + } + } + /// <summary> /// Used to toggle columns in the GUI /// </summary> @@ -75,6 +98,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public ReactiveObject<List<string>> GameDirs { get; private set; } + /// <summary> + /// A list of file types to be hidden in the games List + /// </summary> + public ShownFileTypeSettings ShownFileTypes { get; private set; } + /// <summary> /// Language Code for the UI /// </summary> @@ -135,6 +163,7 @@ namespace Ryujinx.Ui.Common.Configuration GuiColumns = new Columns(); ColumnSort = new ColumnSortSettings(); GameDirs = new ReactiveObject<List<string>>(); + ShownFileTypes = new ShownFileTypeSettings(); EnableCustomTheme = new ReactiveObject<bool>(); CustomThemePath = new ReactiveObject<string>(); BaseStyle = new ReactiveObject<string>(); @@ -618,6 +647,15 @@ namespace Ryujinx.Ui.Common.Configuration SortAscending = Ui.ColumnSort.SortAscending }, GameDirs = Ui.GameDirs, + ShownFileTypes = new ShownFileTypes + { + NSP = Ui.ShownFileTypes.NSP, + PFS0 = Ui.ShownFileTypes.PFS0, + XCI = Ui.ShownFileTypes.XCI, + NCA = Ui.ShownFileTypes.NCA, + NRO = Ui.ShownFileTypes.NRO, + NSO = Ui.ShownFileTypes.NSO, + }, LanguageCode = Ui.LanguageCode, EnableCustomTheme = Ui.EnableCustomTheme, CustomThemePath = Ui.CustomThemePath, @@ -702,7 +740,13 @@ namespace Ryujinx.Ui.Common.Configuration Ui.ColumnSort.SortColumnId.Value = 0; Ui.ColumnSort.SortAscending.Value = false; Ui.GameDirs.Value = new List<string>(); - Ui.EnableCustomTheme.Value = false; + Ui.ShownFileTypes.NSP.Value = true; + Ui.ShownFileTypes.PFS0.Value = true; + Ui.ShownFileTypes.XCI.Value = true; + Ui.ShownFileTypes.NCA.Value = true; + Ui.ShownFileTypes.NRO.Value = true; + Ui.ShownFileTypes.NSO.Value = true; + Ui.EnableCustomTheme.Value = true; Ui.LanguageCode.Value = "en_US"; Ui.CustomThemePath.Value = ""; Ui.BaseStyle.Value = "Dark"; @@ -1238,7 +1282,7 @@ namespace Ryujinx.Ui.Common.Configuration if (configurationFileFormat.Version < 44) { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42."); + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 44."); configurationFileFormat.AntiAliasing = AntiAliasing.None; configurationFileFormat.ScalingFilter = ScalingFilter.Bilinear; @@ -1247,6 +1291,23 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 45) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 45."); + + configurationFileFormat.ShownFileTypes = new ShownFileTypes + { + NSP = true, + PFS0 = true, + XCI = true, + NCA = true, + NRO = true, + NSO = true + }; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1305,6 +1366,12 @@ namespace Ryujinx.Ui.Common.Configuration Ui.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId; Ui.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending; Ui.GameDirs.Value = configurationFileFormat.GameDirs; + Ui.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; + Ui.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; + Ui.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; + Ui.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA; + Ui.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO; + Ui.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO; Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme; Ui.LanguageCode.Value = configurationFileFormat.LanguageCode; Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath; diff --git a/Ryujinx.Ui.Common/Configuration/FileTypes.cs b/Ryujinx.Ui.Common/Configuration/FileTypes.cs new file mode 100644 index 000000000..0b8b7384b --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/FileTypes.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Ui.Common +{ + public enum FileTypes + { + NSP, + PFS0, + XCI, + NCA, + NRO, + NSO + } +} diff --git a/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs b/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs new file mode 100644 index 000000000..c0b76e85c --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Ui.Common.Configuration.Ui +{ + public struct ShownFileTypes + { + public bool NSP { get; set; } + public bool PFS0 { get; set; } + public bool XCI { get; set; } + public bool NCA { get; set; } + public bool NRO { get; set; } + public bool NSO { get; set; } + } +} diff --git a/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs b/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs new file mode 100644 index 000000000..e2ac49504 --- /dev/null +++ b/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs @@ -0,0 +1,25 @@ +using System; +using static Ryujinx.Ui.Common.Configuration.ConfigurationState.UiSection; + +namespace Ryujinx.Ui.Common +{ + public static class FileTypesExtensions + { + /// <summary> + /// Gets the current <see cref="ShownFileTypeSettings"/> value for the correlating FileType name. + /// </summary> + /// <param name="type">The name of the <see cref="ShownFileTypeSettings"/> parameter to get the value of.</param> + /// <param name="config">The config instance to get the value from.</param> + /// <returns>The current value of the setting. Value is <see langword="true"/> if the file type is the be shown on the games list, <see langword="false"/> otherwise.</returns> + public static bool GetConfigValue(this FileTypes type, ShownFileTypeSettings config) => type switch + { + FileTypes.NSP => config.NSP.Value, + FileTypes.PFS0 => config.PFS0.Value, + FileTypes.XCI => config.XCI.Value, + FileTypes.NCA => config.NCA.Value, + FileTypes.NRO => config.NRO.Value, + FileTypes.NSO => config.NSO.Value, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } +} diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index e0252016f..3e513dd28 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -119,6 +119,12 @@ namespace Ryujinx.Ui [GUI] CheckMenuItem _fileExtToggle; [GUI] CheckMenuItem _pathToggle; [GUI] CheckMenuItem _fileSizeToggle; + [GUI] CheckMenuItem _nspShown; + [GUI] CheckMenuItem _pfs0Shown; + [GUI] CheckMenuItem _xciShown; + [GUI] CheckMenuItem _ncaShown; + [GUI] CheckMenuItem _nroShown; + [GUI] CheckMenuItem _nsoShown; [GUI] Label _gpuBackend; [GUI] Label _dockedMode; [GUI] Label _aspectRatio; @@ -220,6 +226,20 @@ namespace Ryujinx.Ui _pauseEmulation.Sensitive = false; _resumeEmulation.Sensitive = false; + _nspShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value; + _pfs0Shown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value; + _xciShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value; + _ncaShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value; + _nroShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value; + _nsoShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value; + + _nspShown.Toggled += NSP_Shown_Toggled; + _pfs0Shown.Toggled += PFS0_Shown_Toggled; + _xciShown.Toggled += XCI_Shown_Toggled; + _ncaShown.Toggled += NCA_Shown_Toggled; + _nroShown.Toggled += NRO_Shown_Toggled; + _nsoShown.Toggled += NSO_Shown_Toggled; + _fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported; if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true; @@ -1757,6 +1777,54 @@ namespace Ryujinx.Ui UpdateColumns(); } + private void NSP_Shown_Toggled(object sender, EventArgs args) + { + ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = _nspShown.Active; + + SaveConfig(); + UpdateGameTable(); + } + + private void PFS0_Shown_Toggled(object sender, EventArgs args) + { + ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = _pfs0Shown.Active; + + SaveConfig(); + UpdateGameTable(); + } + + private void XCI_Shown_Toggled (object sender, EventArgs args) + { + ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = _xciShown.Active; + + SaveConfig(); + UpdateGameTable(); + } + + private void NCA_Shown_Toggled (object sender, EventArgs args) + { + ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = _ncaShown.Active; + + SaveConfig(); + UpdateGameTable(); + } + + private void NRO_Shown_Toggled (object sender, EventArgs args) + { + ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = _nroShown.Active; + + SaveConfig(); + UpdateGameTable(); + } + + private void NSO_Shown_Toggled (object sender, EventArgs args) + { + ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = _nsoShown.Active; + + SaveConfig(); + UpdateGameTable(); + } + private void RefreshList_Pressed(object sender, ButtonReleaseEventArgs args) { UpdateGameTable(); diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade index 8ffbb97e5..58d5d9558 100644 --- a/Ryujinx/Ui/MainWindow.glade +++ b/Ryujinx/Ui/MainWindow.glade @@ -262,6 +262,75 @@ </child> </object> </child> + <child> + <object class="GtkMenuItem" id="ShownFileTypes"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Select which file types to show</property> + <property name="label" translatable="yes">Show File Types</property> + <property name="use-underline">True</property> + <child type="submenu"> + <object class="GtkMenu"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkCheckMenuItem" id="_nspShown"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Shows .NSP files in the games list</property> + <property name="label" translatable="yes">.NSP</property> + <property name="use-underline">True</property> + </object> + </child> + <child> + <object class="GtkCheckMenuItem" id="_pfs0Shown"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Shows .PFS0 files in the games list</property> + <property name="label" translatable="yes">.PFS0</property> + <property name="use-underline">True</property> + </object> + </child> + <child> + <object class="GtkCheckMenuItem" id="_xciShown"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Shows .XCI files in the games list</property> + <property name="label" translatable="yes">.XCI</property> + <property name="use-underline">True</property> + </object> + </child> + <child> + <object class="GtkCheckMenuItem" id="_ncaShown"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Shows .NCA files in the games list</property> + <property name="label" translatable="yes">.NCA</property> + <property name="use-underline">True</property> + </object> + </child> + <child> + <object class="GtkCheckMenuItem" id="_nroShown"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Shows .NRO files in the games list</property> + <property name="label" translatable="yes">.NRO</property> + <property name="use-underline">True</property> + </object> + </child> + <child> + <object class="GtkCheckMenuItem" id="_nsoShown"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Shows .NSO files in the games list</property> + <property name="label" translatable="yes">.NSO</property> + <property name="use-underline">True</property> + </object> + </child> + </object> + </child> + </object> + </child> <child> <object class="GtkSeparatorMenuItem"> <property name="visible">True</property> From 6dbcdfea47e60aadefd59a75e43549793481f853 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 16 Apr 2023 09:09:02 +0200 Subject: [PATCH 448/737] Ava: Fix nca extraction window never closing & minor cleanup (#4569) * ava: Remove unused doWhileDeferred parameters * ava: Minimally improve swkbd dialog It's currently impossible to get the dialog to redirect focus to the InputBox. * ava: Fix nca extraction dialog never closing Also contains some minor cleanup --- Ryujinx.Ava/Common/ApplicationHelper.cs | 29 +++++----- Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml | 3 +- .../UI/Applet/SwkbdAppletDialog.axaml.cs | 45 +++------------ Ryujinx.Ava/UI/Controls/InputDialog.axaml | 32 ----------- Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs | 57 ------------------- .../UI/Controls/UpdateWaitWindow.axaml.cs | 11 ++++ Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs | 32 +---------- .../UI/ViewModels/MainWindowViewModel.cs | 2 +- .../Windows/ContentDialogOverlayWindow.axaml | 18 +++--- 9 files changed, 45 insertions(+), 184 deletions(-) delete mode 100644 Ryujinx.Ava/UI/Controls/InputDialog.axaml delete mode 100644 Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 161ef8596..8c36a6365 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -13,6 +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.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Logging; @@ -152,25 +153,17 @@ namespace Ryujinx.Ava.Common string destination = await folderDialog.ShowAsync(_owner); var cancellationToken = new CancellationTokenSource(); + UpdateWaitWindow waitingDialog = new( + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)), + cancellationToken); + if (!string.IsNullOrWhiteSpace(destination)) { Thread extractorThread = new(() => { - Dispatcher.UIThread.Post(async () => - { - UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)), - "", - "", - LocaleManager.Instance[LocaleKeys.InputDialogCancel], - LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]); + Dispatcher.UIThread.Post(waitingDialog.Show); - if (result == UserResult.Cancel) - { - cancellationToken.Cancel(); - } - }); - using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); Nca mainNca = null; @@ -222,6 +215,8 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.InvokeAsync(async () => { + waitingDialog.Close(); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); }); @@ -263,11 +258,15 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.InvokeAsync(async () => { + waitingDialog.Close(); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); }); } else if (resultCode.Value.IsSuccess()) { + Dispatcher.UIThread.Post(waitingDialog.Close); + NotificationHelper.Show( LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle], $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}", @@ -284,6 +283,8 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.InvokeAsync(async () => { + waitingDialog.Close(); + await ContentDialogHelper.CreateErrorDialog(ex.Message); }); } diff --git a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml index 43ccf9e71..655045690 100644 --- a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml +++ b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml @@ -48,6 +48,7 @@ Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" + Focusable="True" KeyUp="Message_KeyUp" Text="{Binding Message}" TextInput="Message_TextInput" @@ -61,4 +62,4 @@ HorizontalAlignment="Stretch" TextWrapping="Wrap" /> </Grid> -</UserControl> \ No newline at end of file +</UserControl> diff --git a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index fb689ec23..cb69e96b7 100644 --- a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -45,6 +45,13 @@ namespace Ryujinx.Ava.UI.Controls InitializeComponent(); } + protected override void OnGotFocus(GotFocusEventArgs e) + { + // FIXME: This does not work. Might be a bug in Avalonia with DialogHost + // Currently focus will be redirected to the overlay window instead. + Input.Focus(); + } + public string Message { get; set; } = ""; public string MainText { get; set; } = ""; public string SecondaryText { get; set; } = ""; @@ -59,24 +66,6 @@ namespace Ryujinx.Ava.UI.Controls string input = string.Empty; - var overlay = new ContentDialogOverlayWindow() - { - Height = window.Bounds.Height, - Width = window.Bounds.Width, - Position = window.PointToScreen(new Point()) - }; - - window.PositionChanged += OverlayOnPositionChanged; - - void OverlayOnPositionChanged(object sender, PixelPointEventArgs e) - { - overlay.Position = window.PointToScreen(new Point()); - } - - contentDialog = overlay.ContentDialog; - - bool opened = false; - content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); content._host = contentDialog; @@ -97,25 +86,7 @@ namespace Ryujinx.Ava.UI.Controls }; contentDialog.Closed += handler; - overlay.Opened += OverlayOnActivated; - - async void OverlayOnActivated(object sender, EventArgs e) - { - if (opened) - { - return; - } - - opened = true; - - overlay.Position = window.PointToScreen(new Point()); - - await contentDialog.ShowAsync(overlay); - contentDialog.Closed -= handler; - overlay.Close(); - }; - - await overlay.ShowDialog(window); + await ContentDialogHelper.ShowAsync(contentDialog); return (result, input); } diff --git a/Ryujinx.Ava/UI/Controls/InputDialog.axaml b/Ryujinx.Ava/UI/Controls/InputDialog.axaml deleted file mode 100644 index ed1ceda3b..000000000 --- a/Ryujinx.Ava/UI/Controls/InputDialog.axaml +++ /dev/null @@ -1,32 +0,0 @@ -<UserControl - x:Class="Ryujinx.Ava.UI.Controls.InputDialog" - xmlns="https://github.com/avaloniaui" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" - Focusable="True"> - <Grid - Margin="5,10,5,5" - HorizontalAlignment="Stretch" - VerticalAlignment="Center"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - </Grid.RowDefinitions> - <TextBlock HorizontalAlignment="Center" Text="{Binding Message}" /> - <TextBox - Grid.Row="1" - Width="300" - Margin="10" - HorizontalAlignment="Center" - MaxLength="{Binding MaxLength}" - Text="{Binding Input, Mode=TwoWay}" /> - <TextBlock - Grid.Row="2" - Margin="5,5,5,10" - HorizontalAlignment="Center" - Text="{Binding SubMessage}" /> - </Grid> -</UserControl> \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs b/Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs deleted file mode 100644 index 8dba5e2b4..000000000 --- a/Ryujinx.Ava/UI/Controls/InputDialog.axaml.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Avalonia.Controls; -using FluentAvalonia.UI.Controls; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; -using System.Threading.Tasks; - -namespace Ryujinx.Ava.UI.Controls -{ - public partial class InputDialog : UserControl - { - public string Message { get; set; } - public string Input { get; set; } - public string SubMessage { get; set; } - - public uint MaxLength { get; } - - public InputDialog(string message, string input = "", string subMessage = "", uint maxLength = int.MaxValue) - { - Message = message; - Input = input; - SubMessage = subMessage; - MaxLength = maxLength; - - DataContext = this; - } - - public InputDialog() - { - InitializeComponent(); - } - - public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, string message, - string input = "", string subMessage = "", uint maxLength = int.MaxValue) - { - UserResult result = UserResult.Cancel; - - InputDialog content = new InputDialog(message, input, subMessage, maxLength); - ContentDialog contentDialog = new ContentDialog - { - Title = title, - PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk], - SecondaryButtonText = "", - CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel], - Content = content, - PrimaryButtonCommand = MiniCommand.Create(() => - { - result = UserResult.Ok; - input = content.Input; - }) - }; - await contentDialog.ShowAsync(); - - return (result, input); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs b/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs index 9db7b5d47..80a437e33 100644 --- a/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs @@ -1,15 +1,26 @@ using Avalonia.Controls; using Ryujinx.Ava.UI.Windows; +using System.Threading; namespace Ryujinx.Ava.UI.Controls { public partial class UpdateWaitWindow : StyleableWindow { + public UpdateWaitWindow(string primaryText, string secondaryText, CancellationTokenSource cancellationToken) : this(primaryText, secondaryText) + { + SystemDecorations = SystemDecorations.Full; + ShowInTaskbar = true; + + Closing += (_, _) => cancellationToken.Cancel(); + } + public UpdateWaitWindow(string primaryText, string secondaryText) : this() { PrimaryText.Text = primaryText; SecondaryText.Text = secondaryText; WindowStartupLocation = WindowStartupLocation.CenterOwner; + SystemDecorations = SystemDecorations.BorderOnly; + ShowInTaskbar = false; } public UpdateWaitWindow() diff --git a/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index 8f0c670ea..cb474506b 100644 --- a/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -27,7 +27,6 @@ namespace Ryujinx.Ava.UI.Helpers string closeButton, UserResult primaryButtonResult = UserResult.Ok, ManualResetEvent deferResetEvent = null, - Func<Window, Task> doWhileDeferred = null, TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null) { UserResult result = UserResult.None; @@ -78,12 +77,11 @@ namespace Ryujinx.Ava.UI.Helpers int iconSymbol, UserResult primaryButtonResult = UserResult.Ok, ManualResetEvent deferResetEvent = null, - Func<Window, Task> doWhileDeferred = null, TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null) { Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol); - return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction); + return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction); } public async static Task<UserResult> ShowDeferredContentDialog( @@ -111,7 +109,6 @@ namespace Ryujinx.Ava.UI.Helpers iconSymbol, primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok, deferResetEvent, - doWhileDeferred, DeferClose); async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args) @@ -236,11 +233,6 @@ namespace Ryujinx.Ava.UI.Helpers primaryButtonResult); } - internal static UpdateWaitWindow CreateWaitingDialog(string mainText, string secondaryText) - { - return new(mainText, secondaryText); - } - internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText) { await ShowTextDialog( @@ -319,28 +311,6 @@ namespace Ryujinx.Ava.UI.Helpers LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]); } - internal static async Task<string> CreateInputDialog( - string title, - string mainText, - string subText, - uint maxLength = int.MaxValue, - string input = "") - { - var result = await InputDialog.ShowInputDialog( - title, - mainText, - input, - subText, - maxLength); - - if (result.Result == UserResult.Ok) - { - return result.Input; - } - - return string.Empty; - } - public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog) { ContentDialogResult result; diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index da2115265..14d7a0fe4 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -972,7 +972,7 @@ namespace Ryujinx.Ava.UI.ViewModels LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]); + UpdateWaitWindow waitingDialog = new(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]); if (result == UserResult.Yes) { diff --git a/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml b/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml index 6cdcae2bd..8b52baded 100644 --- a/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml @@ -1,4 +1,4 @@ -<window:StyleableWindow +<window:StyleableWindow xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" @@ -10,20 +10,16 @@ d:DesignHeight="450" x:Class="Ryujinx.Ava.UI.Windows.ContentDialogOverlayWindow" Title="ContentDialogOverlayWindow" - Focusable="True"> + Focusable="False"> <window:StyleableWindow.Styles> <Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot"> <Setter Property="Background" Value="Transparent" /> </Style> </window:StyleableWindow.Styles> - <ContentControl - Focusable="False" - IsVisible="False" - KeyboardNavigation.IsTabStop="False"> - <ui:ContentDialog Name="ContentDialog" - IsPrimaryButtonEnabled="True" - IsSecondaryButtonEnabled="True" - IsVisible="False" /> - </ContentControl> + <ui:ContentDialog Name="ContentDialog" + IsPrimaryButtonEnabled="True" + IsSecondaryButtonEnabled="True" + IsVisible="False" + Focusable="True"/> </window:StyleableWindow> From d5e4378aea086d9219f890e33cf81d566d96b9ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 16 Apr 2023 09:02:06 +0000 Subject: [PATCH 449/737] nuget: bump DynamicData from 7.13.1 to 7.13.5 (#4654) Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.1 to 7.13.5. - [Release notes](https://github.com/reactiveui/DynamicData/releases) - [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md) - [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.1...7.13.5) --- updated-dependencies: - dependency-name: DynamicData dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a18bcbe7c..85776c74b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,7 +13,7 @@ <PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> - <PackageVersion Include="DynamicData" Version="7.13.1" /> + <PackageVersion Include="DynamicData" Version="7.13.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> From baf8752e74488a419074ae1d484e54a00bc01973 Mon Sep 17 00:00:00 2001 From: Vincenzo Nizza <vincenzonizzaufficio@gmail.com> Date: Sun, 16 Apr 2023 11:19:33 +0200 Subject: [PATCH 450/737] Ensure the updater doesn't delete hidden or system files (#4626) * Copy desktop.ini to update directory if it exists in HomeDir * EnumerateFilesToDelete() exclude files with "Hidden" and "System" attributes --- Ryujinx.Ava/Modules/Updater/Updater.cs | 2 +- Ryujinx/Modules/Updater/Updater.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index c58575284..054299351 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -730,7 +730,7 @@ namespace Ryujinx.Modules } } - return files; + return files.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); } private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog) diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs index 3f186ce6b..3e0dc99b4 100644 --- a/Ryujinx/Modules/Updater/Updater.cs +++ b/Ryujinx/Modules/Updater/Updater.cs @@ -577,7 +577,7 @@ namespace Ryujinx.Modules } } - return files; + return files.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); } private static void MoveAllFilesOver(string root, string dest, UpdateDialog dialog) From 2bc88467eb377a0ca1a8b51700300422422c8c37 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Sun, 16 Apr 2023 09:37:31 +0000 Subject: [PATCH 451/737] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdb29a481..7021abc45 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ## Compatibility -As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable. +As of April 2023, Ryujinx has been tested on approximately 4,050 titles; over 4,000 boot past menus and into gameplay, with roughly 3,400 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already! ## Usage From 79d1c190dba48e405a833f654691e47509a29792 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 16 Apr 2023 11:38:07 +0200 Subject: [PATCH 452/737] chore: Update Silk.NET to 2.17.1 (#4686) --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 85776c74b..563d2a685 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -37,9 +37,9 @@ <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" /> <PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" /> - <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> - <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" /> - <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" /> + <PackageVersion Include="Silk.NET.Vulkan" Version="2.17.1" /> + <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.17.1" /> + <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.17.1" /> <PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> From 40e87c634ece65da3f740fcfbb6acb43e5cd71b3 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 16 Apr 2023 16:50:30 +0200 Subject: [PATCH 453/737] Fix a crash in Ryujinx.Headless.SDL2 when loading an app (#4687) Caused by the recent application loader changes. --- Ryujinx.Headless.SDL2/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 8dff6a1d0..35cc74f4f 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -589,8 +589,6 @@ namespace Ryujinx.Headless.SDL2 _emulationContext = InitializeEmulationContext(window, renderer, options); - SetupProgressHandler(); - SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); @@ -693,6 +691,8 @@ namespace Ryujinx.Headless.SDL2 return false; } + SetupProgressHandler(); + Translator.IsReadyForTranslation.Reset(); ExecutionEntrypoint(); From 69b6ef7a4ae36994c293e423e1203096c294744c Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 16 Apr 2023 17:25:20 +0200 Subject: [PATCH 454/737] [GUI] Add network interface dropdown (#4597) * Add network adapter dropdown from LDN build * Ava: Add NetworkInterfaces to SettingsNetworkTab * Add headless network interface option * Add network interface dropdown to Avalonia * Fix handling network interfaces without a gateway address * gtk: Actually save selected network interface to config * Increment config version --- Ryujinx.Ava/AppHost.cs | 10 +- Ryujinx.Ava/Assets/Locales/en_US.json | 5 +- .../UI/ViewModels/SettingsViewModel.cs | 36 +++++ .../Views/Settings/SettingsNetworkView.axaml | 15 +- Ryujinx.Common/Utilities/NetworkHelpers.cs | 66 ++++++++ Ryujinx.HLE/HLEConfiguration.cs | 57 ++++--- .../Nifm/StaticService/IGeneralService.cs | 52 ++----- .../StaticService/Types/IpAddressSetting.cs | 4 +- Ryujinx.Headless.SDL2/Options.cs | 3 + Ryujinx.Headless.SDL2/Program.cs | 3 +- .../Configuration/ConfigurationFileFormat.cs | 7 +- .../Configuration/ConfigurationState.cs | 51 +++++- Ryujinx/Ui/MainWindow.cs | 3 +- Ryujinx/Ui/Windows/SettingsWindow.cs | 18 +++ Ryujinx/Ui/Windows/SettingsWindow.glade | 145 +++++++++++++++++- 15 files changed, 385 insertions(+), 90 deletions(-) create mode 100644 Ryujinx.Common/Utilities/NetworkHelpers.cs diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index ae9e8e53d..957a1c9d3 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -177,6 +177,8 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; + _gpuCancellationTokenSource = new CancellationTokenSource(); } @@ -383,6 +385,11 @@ namespace Ryujinx.Ava }); } + private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs<string> e) + { + Device.Configuration.MultiplayerLanInterfaceId = e.NewValue; + } + public void Stop() { _isActive = false; @@ -739,7 +746,8 @@ namespace Ryujinx.Ava ConfigurationState.Instance.System.IgnoreMissingServices, ConfigurationState.Instance.Graphics.AspectRatio, ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor); + ConfigurationState.Instance.System.UseHypervisor, + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); Device = new Switch(configuration); } diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json index b2d6b43d7..3a4bfc65e 100644 --- a/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/Ryujinx.Ava/Assets/Locales/en_US.json @@ -638,5 +638,8 @@ "SmaaHigh": "SMAA High", "SmaaUltra": "SMAA Ultra", "UserEditorTitle" : "Edit User", - "UserEditorTitleCreate" : "Create User" + "UserEditorTitleCreate" : "Create User", + "SettingsTabNetworkInterface": "Network Interface:", + "NetworkInterfaceTooltip": "The network interface used for LAN features", + "NetworkInterfaceDefault": "Default" } diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index cbba7fb90..232c9d436 100644 --- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -23,6 +23,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.InteropServices; +using System.Net.NetworkInformation; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; namespace Ryujinx.Ava.UI.ViewModels @@ -35,6 +36,8 @@ namespace Ryujinx.Ava.UI.ViewModels private readonly List<string> _validTzRegions; + private readonly Dictionary<string, string> _networkInterfaces; + private float _customResolutionScale; private int _resolutionScale; private int _graphicsBackendMultithreadingIndex; @@ -50,6 +53,7 @@ namespace Ryujinx.Ava.UI.ViewModels public event Action CloseWindow; public event Action SaveSettingsEvent; + private int _networkInterfaceIndex; public int ResolutionScale { @@ -240,6 +244,11 @@ namespace Ryujinx.Ava.UI.ViewModels public AvaloniaList<string> GameDirectories { get; set; } public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; } + public AvaloniaList<string> NetworkInterfaceList + { + get => new AvaloniaList<string>(_networkInterfaces.Keys); + } + public KeyboardHotkeys KeyboardHotkeys { get => _keyboardHotkeys; @@ -251,6 +260,16 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public int NetworkInterfaceIndex + { + get => _networkInterfaceIndex; + set + { + _networkInterfaceIndex = value != -1 ? value : 0; + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]]; + } + } + public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() { _virtualFileSystem = virtualFileSystem; @@ -267,8 +286,10 @@ namespace Ryujinx.Ava.UI.ViewModels TimeZones = new AvaloniaList<TimeZone>(); AvailableGpus = new ObservableCollection<ComboBoxItem>(); _validTzRegions = new List<string>(); + _networkInterfaces = new Dictionary<string, string>(); CheckSoundBackends(); + PopulateNetworkInterfaces(); if (Program.PreviewerDetached) { @@ -327,6 +348,17 @@ namespace Ryujinx.Ava.UI.ViewModels } } + private void PopulateNetworkInterfaces() + { + _networkInterfaces.Clear(); + _networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0"); + + foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces()) + { + _networkInterfaces.Add(networkInterface.Name, networkInterface.Id); + } + } + public void ValidateAndSetTimeZone(string location) { if (_validTzRegions.Contains(location)) @@ -414,6 +446,8 @@ namespace Ryujinx.Ava.UI.ViewModels EnableFsAccessLog = config.Logger.EnableFsAccessLog; FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode; OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value; + + NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(config.Multiplayer.LanInterfaceId.Value); } public void SaveSettings() @@ -515,6 +549,8 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode; config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel; + config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]]; + config.ToFileFormat().SaveConfig(Program.ConfigurationPath); MainWindow.UpdateGraphicsConfig(); diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml index 8efd367dd..ab8a7f6d1 100644 --- a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml +++ b/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml @@ -1,4 +1,4 @@ -<UserControl +<UserControl x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" @@ -29,7 +29,18 @@ <TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}" ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" /> </CheckBox> + <StackPanel Margin="10,0,0,0" Orientation="Horizontal"> + <TextBlock VerticalAlignment="Center" + Text="{locale:Locale SettingsTabNetworkInterface}" + ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}" + Width="200" /> + <ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}" + ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}" + HorizontalContentAlignment="Left" + Items="{Binding NetworkInterfaceList}" + Width="250" /> + </StackPanel> </StackPanel> </Border> </ScrollViewer> -</UserControl> \ No newline at end of file +</UserControl> diff --git a/Ryujinx.Common/Utilities/NetworkHelpers.cs b/Ryujinx.Common/Utilities/NetworkHelpers.cs new file mode 100644 index 000000000..b2f6f45d6 --- /dev/null +++ b/Ryujinx.Common/Utilities/NetworkHelpers.cs @@ -0,0 +1,66 @@ +using System.Net.NetworkInformation; + +namespace Ryujinx.Common.Utilities +{ + public static class NetworkHelpers + { + private static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(NetworkInterface adapter, bool isPreferred) + { + IPInterfaceProperties properties = adapter.GetIPProperties(); + + if (isPreferred || (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0)) + { + foreach (UnicastIPAddressInformation info in properties.UnicastAddresses) + { + // Only accept an IPv4 address + if (info.Address.GetAddressBytes().Length == 4) + { + return (properties, info); + } + } + } + + return (null, null); + } + + public static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(string lanInterfaceId = "0") + { + if (!NetworkInterface.GetIsNetworkAvailable()) + { + return (null, null); + } + + IPInterfaceProperties targetProperties = null; + UnicastIPAddressInformation targetAddressInfo = null; + + NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); + + string guid = lanInterfaceId; + bool hasPreference = guid != "0"; + + foreach (NetworkInterface adapter in interfaces) + { + bool isPreferred = adapter.Id == guid; + + // Ignore loopback and non IPv4 capable interface. + if (isPreferred || (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4))) + { + (IPInterfaceProperties properties, UnicastIPAddressInformation info) = GetLocalInterface(adapter, isPreferred); + + if (properties != null) + { + targetProperties = properties; + targetAddressInfo = info; + + if (isPreferred || !hasPreference) + { + break; + } + } + } + } + + return (targetProperties, targetAddressInfo); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HLEConfiguration.cs b/Ryujinx.HLE/HLEConfiguration.cs index e21157f9b..df8dea6d6 100644 --- a/Ryujinx.HLE/HLEConfiguration.cs +++ b/Ryujinx.HLE/HLEConfiguration.cs @@ -153,6 +153,11 @@ namespace Ryujinx.HLE /// </summary> internal readonly bool UseHypervisor; + /// <summary> + /// Multiplayer LAN Interface ID (device GUID) + /// </summary> + public string MultiplayerLanInterfaceId { internal get; set; } + /// <summary> /// An action called when HLE force a refresh of output after docked mode changed. /// </summary> @@ -181,32 +186,34 @@ namespace Ryujinx.HLE bool ignoreMissingServices, AspectRatio aspectRatio, float audioVolume, - bool useHypervisor) + bool useHypervisor, + string multiplayerLanInterfaceId) { - VirtualFileSystem = virtualFileSystem; - LibHacHorizonManager = libHacHorizonManager; - AccountManager = accountManager; - ContentManager = contentManager; - UserChannelPersistence = userChannelPersistence; - GpuRenderer = gpuRenderer; - AudioDeviceDriver = audioDeviceDriver; - MemoryConfiguration = memoryConfiguration; - HostUiHandler = hostUiHandler; - SystemLanguage = systemLanguage; - Region = region; - EnableVsync = enableVsync; - EnableDockedMode = enableDockedMode; - EnablePtc = enablePtc; - EnableInternetAccess = enableInternetAccess; - FsIntegrityCheckLevel = fsIntegrityCheckLevel; - FsGlobalAccessLogMode = fsGlobalAccessLogMode; - SystemTimeOffset = systemTimeOffset; - TimeZone = timeZone; - MemoryManagerMode = memoryManagerMode; - IgnoreMissingServices = ignoreMissingServices; - AspectRatio = aspectRatio; - AudioVolume = audioVolume; - UseHypervisor = useHypervisor; + VirtualFileSystem = virtualFileSystem; + LibHacHorizonManager = libHacHorizonManager; + AccountManager = accountManager; + ContentManager = contentManager; + UserChannelPersistence = userChannelPersistence; + GpuRenderer = gpuRenderer; + AudioDeviceDriver = audioDeviceDriver; + MemoryConfiguration = memoryConfiguration; + HostUiHandler = hostUiHandler; + SystemLanguage = systemLanguage; + Region = region; + EnableVsync = enableVsync; + EnableDockedMode = enableDockedMode; + EnablePtc = enablePtc; + EnableInternetAccess = enableInternetAccess; + FsIntegrityCheckLevel = fsIntegrityCheckLevel; + FsGlobalAccessLogMode = fsGlobalAccessLogMode; + SystemTimeOffset = systemTimeOffset; + TimeZone = timeZone; + MemoryManagerMode = memoryManagerMode; + IgnoreMissingServices = ignoreMissingServices; + AspectRatio = aspectRatio; + AudioVolume = audioVolume; + UseHypervisor = useHypervisor; + MultiplayerLanInterfaceId = multiplayerLanInterfaceId; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index fd7110408..e9712e927 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -6,7 +6,6 @@ using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types; using System; using System.Net.NetworkInformation; using System.Runtime.CompilerServices; -using System.Text; namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { @@ -16,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService private IPInterfaceProperties _targetPropertiesCache = null; private UnicastIPAddressInformation _targetAddressInfoCache = null; + private string _cacheChosenInterface = null; public IGeneralService() { @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position; - (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(); + (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context); if (interfaceProperties == null || unicastAddress == null) { @@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // GetCurrentIpAddress() -> nn::nifm::IpV4Address public ResultCode GetCurrentIpAddress(ServiceCtx context) { - (_, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(); + (_, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context); if (unicastAddress == null) { @@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting) public ResultCode GetCurrentIpConfigInfo(ServiceCtx context) { - (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(); + (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context); if (interfaceProperties == null || unicastAddress == null) { @@ -163,51 +163,23 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } - private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface() + private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(ServiceCtx context) { if (!NetworkInterface.GetIsNetworkAvailable()) { return (null, null); } - if (_targetPropertiesCache != null && _targetAddressInfoCache != null) + string chosenInterface = context.Device.Configuration.MultiplayerLanInterfaceId; + + if (_targetPropertiesCache == null || _targetAddressInfoCache == null || _cacheChosenInterface != chosenInterface) { - return (_targetPropertiesCache, _targetAddressInfoCache); + _cacheChosenInterface = chosenInterface; + + (_targetPropertiesCache, _targetAddressInfoCache) = NetworkHelpers.GetLocalInterface(chosenInterface); } - IPInterfaceProperties targetProperties = null; - UnicastIPAddressInformation targetAddressInfo = null; - - NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); - - foreach (NetworkInterface adapter in interfaces) - { - // Ignore loopback and non IPv4 capable interface. - if (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4)) - { - IPInterfaceProperties properties = adapter.GetIPProperties(); - - if (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0) - { - foreach (UnicastIPAddressInformation info in properties.UnicastAddresses) - { - // Only accept an IPv4 address - if (info.Address.GetAddressBytes().Length == 4) - { - targetProperties = properties; - targetAddressInfo = info; - - break; - } - } - } - } - } - - _targetPropertiesCache = targetProperties; - _targetAddressInfoCache = targetAddressInfo; - - return (targetProperties, targetAddressInfo); + return (_targetPropertiesCache, _targetAddressInfoCache); } private void LocalInterfaceCacheHandler(object sender, EventArgs e) diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs index 30667b928..59c1f6a7f 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types IsDhcpEnabled = OperatingSystem.IsMacOS() || interfaceProperties.DhcpServerAddresses.Count != 0; Address = new IpV4Address(unicastIPAddressInformation.Address); IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask); - GatewayAddress = new IpV4Address(interfaceProperties.GatewayAddresses[0].Address); + GatewayAddress = (interfaceProperties.GatewayAddresses.Count == 0) ? new IpV4Address() : new IpV4Address(interfaceProperties.GatewayAddresses[0].Address); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Headless.SDL2/Options.cs b/Ryujinx.Headless.SDL2/Options.cs index 5138a0535..982d09909 100644 --- a/Ryujinx.Headless.SDL2/Options.cs +++ b/Ryujinx.Headless.SDL2/Options.cs @@ -132,6 +132,9 @@ namespace Ryujinx.Headless.SDL2 [Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")] public bool UseHypervisor { get; set; } + [Option("lan-interface-id", Required = false, Default = "0", HelpText = "GUID for the network interface used by LAN.")] + public string MultiplayerLanInterfaceId { get; set; } + // Logging [Option("disable-file-logging", Required = false, Default = false, HelpText = "Disables logging to a file on disk.")] diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 35cc74f4f..b0bdb97f1 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -548,7 +548,8 @@ namespace Ryujinx.Headless.SDL2 options.IgnoreMissingServices, options.AspectRatio, options.AudioVolume, - options.UseHypervisor); + options.UseHypervisor, + options.MultiplayerLanInterfaceId); return new Switch(configuration); } diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index cb9adc86c..c9e7f80e8 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <summary> /// The current version of the file format /// </summary> - public const int CurrentVersion = 45; + public const int CurrentVersion = 46; /// <summary> /// Version of the configuration file format @@ -350,6 +350,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public string PreferredGpu { get; set; } + /// <summary> + /// GUID for the network interface used by LAN (or 0 for default) + /// </summary> + public string MultiplayerLanInterfaceId { get; set; } + /// <summary> /// Uses Hypervisor over JIT if available /// </summary> diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index 12cc1b8f8..3a68cc265 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -517,6 +517,22 @@ namespace Ryujinx.Ui.Common.Configuration } } + /// <summary> + /// Multiplayer configuration section + /// </summary> + public class MultiplayerSection + { + /// <summary> + /// GUID for the network interface used by LAN (or 0 for default) + /// </summary> + public ReactiveObject<string> LanInterfaceId { get; private set; } + + public MultiplayerSection() + { + LanInterfaceId = new ReactiveObject<string>(); + } + } + /// <summary> /// The default configuration instance /// </summary> @@ -547,6 +563,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public HidSection Hid { get; private set; } + /// <summary> + /// The Multiplayer section + /// </summary> + public MultiplayerSection Multiplayer { get; private set; } + /// <summary> /// Enables or disables Discord Rich Presence /// </summary> @@ -574,6 +595,7 @@ namespace Ryujinx.Ui.Common.Configuration System = new SystemSection(); Graphics = new GraphicsSection(); Hid = new HidSection(); + Multiplayer = new MultiplayerSection(); EnableDiscordIntegration = new ReactiveObject<bool>(); CheckUpdatesOnStart = new ReactiveObject<bool>(); ShowConfirmExit = new ReactiveObject<bool>(); @@ -674,7 +696,8 @@ namespace Ryujinx.Ui.Common.Configuration ControllerConfig = new List<JsonObject>(), InputConfig = Hid.InputConfig, GraphicsBackend = Graphics.GraphicsBackend, - PreferredGpu = Graphics.PreferredGpu + PreferredGpu = Graphics.PreferredGpu, + MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId }; return configurationFile; @@ -727,6 +750,7 @@ namespace Ryujinx.Ui.Common.Configuration System.ExpandRam.Value = false; System.IgnoreMissingServices.Value = false; System.UseHypervisor.Value = true; + Multiplayer.LanInterfaceId.Value = "0"; Ui.GuiColumns.FavColumn.Value = true; Ui.GuiColumns.IconColumn.Value = true; Ui.GuiColumns.AppColumn.Value = true; @@ -1308,6 +1332,15 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 46) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 45."); + + configurationFileFormat.MultiplayerLanInterfaceId = "0"; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1366,12 +1399,12 @@ namespace Ryujinx.Ui.Common.Configuration Ui.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId; Ui.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending; Ui.GameDirs.Value = configurationFileFormat.GameDirs; - Ui.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; - Ui.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; - Ui.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; - Ui.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA; - Ui.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO; - Ui.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO; + Ui.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; + Ui.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; + Ui.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; + Ui.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA; + Ui.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO; + Ui.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO; Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme; Ui.LanguageCode.Value = configurationFileFormat.LanguageCode; Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath; @@ -1393,6 +1426,8 @@ namespace Ryujinx.Ui.Common.Configuration Hid.InputConfig.Value = new List<InputConfig>(); } + Multiplayer.LanInterfaceId.Value = configurationFileFormat.MultiplayerLanInterfaceId; + if (configurationFileUpdated) { ToFileFormat().SaveConfig(configurationFilePath); @@ -1418,4 +1453,4 @@ namespace Ryujinx.Ui.Common.Configuration Instance = new ConfigurationState(); } } -} \ No newline at end of file +} diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 3e513dd28..bf96e18a4 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -598,7 +598,8 @@ namespace Ryujinx.Ui ConfigurationState.Instance.System.IgnoreMissingServices, ConfigurationState.Instance.Graphics.AspectRatio, ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor); + ConfigurationState.Instance.System.UseHypervisor, + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); _emulationContext = new HLE.Switch(configuration); } diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs index f049da505..27080bda3 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Net.NetworkInformation; using System.Reflection; using System.Threading.Tasks; using GUI = Gtk.Builder.ObjectAttribute; @@ -84,6 +85,7 @@ namespace Ryujinx.Ui.Windows [GUI] Adjustment _systemTimeDaySpinAdjustment; [GUI] Adjustment _systemTimeHourSpinAdjustment; [GUI] Adjustment _systemTimeMinuteSpinAdjustment; + [GUI] ComboBoxText _multiLanSelect; [GUI] CheckButton _custThemeToggle; [GUI] Entry _custThemePath; [GUI] ToggleButton _browseThemePath; @@ -348,6 +350,8 @@ namespace Ryujinx.Ui.Windows UpdatePreferredGpuComboBox(); _graphicsBackend.Changed += (sender, e) => UpdatePreferredGpuComboBox(); + PopulateNetworkInterfaces(); + _multiLanSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath; _resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString(); @@ -490,6 +494,19 @@ namespace Ryujinx.Ui.Windows } } + private void PopulateNetworkInterfaces() + { + NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); + + foreach (NetworkInterface nif in interfaces) + { + string guid = nif.Id; + string name = nif.Name; + + _multiLanSelect.Append(guid, name); + } + } + private void UpdateSystemTimeSpinners() { //Bind system time events @@ -616,6 +633,7 @@ namespace Ryujinx.Ui.Windows ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId); ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId); ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value; diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade index c19c1db9f..8ae6ea72f 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.glade +++ b/Ryujinx/Ui/Windows/SettingsWindow.glade @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.38.2 --> +<!-- Generated with glade 3.40.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkAdjustment" id="_fsLogSpinAdjustment"> @@ -7,6 +7,12 @@ <property name="step-increment">1</property> <property name="page-increment">10</property> </object> + <object class="GtkAdjustment" id="_scalingFilterLevel"> + <property name="upper">101</property> + <property name="step-increment">1</property> + <property name="page-increment">5</property> + <property name="page-size">1</property> + </object> <object class="GtkAdjustment" id="_systemTimeDaySpinAdjustment"> <property name="lower">1</property> <property name="upper">31</property> @@ -40,13 +46,6 @@ <property name="inline-completion">True</property> <property name="inline-selection">True</property> </object> - <object class="GtkAdjustment" id="_scalingFilterLevel"> - <property name="lower">0</property> - <property name="upper">101</property> - <property name="step-increment">1</property> - <property name="page-increment">5</property> - <property name="page-size">1</property> - </object> <object class="GtkWindow" id="_settingsWin"> <property name="can-focus">False</property> <property name="title" translatable="yes">Ryujinx - Settings</property> @@ -2862,6 +2861,136 @@ <property name="tab-fill">False</property> </packing> </child> + <child> + <object class="GtkBox" id="TabMultiplayer"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-left">5</property> + <property name="margin-right">10</property> + <property name="margin-top">5</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="CatLAN"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="valign">start</property> + <property name="margin-left">5</property> + <property name="margin-right">5</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">start</property> + <property name="margin-bottom">5</property> + <property name="label" translatable="yes">LAN Mode</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="LANOptions"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="valign">start</property> + <property name="margin-left">10</property> + <property name="margin-right">10</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="NetworkInterfaceBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">The network interface used for LAN features</property> + <property name="halign">end</property> + <property name="label" translatable="yes">Network Interface:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="_multiLanSelect"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">The network interface used for LAN features</property> + <property name="active-id">0</property> + <items> + <item id="0" translatable="yes">Default</item> + </items> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">start</property> + <property name="margin-bottom">5</property> + <property name="label" translatable="yes">To use LAN functionality in games, Enable Guest Internet Access must be checked in System.</property> + <property name="wrap">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">5</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Multiplayer</property> + </object> + <packing> + <property name="position">5</property> + <property name="tab-fill">False</property> + </packing> + </child> </object> </child> </object> From 3e68a87d63707e53c4b439a80c4c54538de02e4b Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 16 Apr 2023 14:26:22 -0300 Subject: [PATCH 455/737] Change SMAA filter texture clear method (#4685) * Change SMAA filter texture clear method * Alpha should be 1 * Delete more unnecessary code --- .../Effects/FsrScalingFilter.cs | 29 --------- .../Effects/FxaaPostProcessingEffect.cs | 16 ----- .../Effects/SmaaPostProcessingEffect.cs | 63 ++++++------------- 3 files changed, 19 insertions(+), 89 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index a871679b0..5f15f15f6 100644 --- a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -118,20 +118,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects _intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; } - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; - Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; - - viewports[0] = new GAL.Viewport( - new Rectangle<float>(0, 0, view.Width, view.Height), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - scissors[0] = new Rectangle<int>(0, 0, view.Width, view.Height); - _pipeline.SetCommandBuffer(cbs); _pipeline.SetProgram(_scalingProgram); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler); @@ -169,23 +155,10 @@ namespace Ryujinx.Graphics.Vulkan.Effects var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); - _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); _pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); - viewports[0] = new GAL.Viewport( - new Rectangle<float>(0, 0, width, height), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - scissors[0] = new Rectangle<int>(0, 0, width, height); - // Sharpening pass _pipeline.SetCommandBuffer(cbs); _pipeline.SetProgram(_sharpeningProgram); @@ -193,8 +166,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float)); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) }); - _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); _pipeline.SetImage(0, destinationTexture); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); diff --git a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index 9e73e1b82..b7316d857 100644 --- a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -94,25 +94,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; - - viewports[0] = new GAL.Viewport( - new Rectangle<float>(0, 0, view.Width, view.Height), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; - var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); - _pipeline.SetScissors(stackalloc[] { new Rectangle<int>(0, 0, view.Width, view.Height) }); - _pipeline.SetViewports(viewports, false); - _pipeline.SetImage(0, _texture, GAL.Format.R8G8B8A8Unorm); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); diff --git a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index bf698ade6..38f86baeb 100644 --- a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -218,40 +218,10 @@ namespace Ryujinx.Graphics.Vulkan.Effects _blendOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; } - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + _pipeline.SetCommandBuffer(cbs); - viewports[0] = new GAL.Viewport( - new Rectangle<float>(0, 0, view.Width, view.Height), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; - - scissors[0] = new Rectangle<int>(0, 0, view.Width, view.Height); - - _renderer.HelperShader.Clear(_renderer, - _edgeOutputTexture.GetImageView(), - new float[] { 0, 0, 0, 1 }, - (uint)(ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit), - view.Width, - view.Height, - _edgeOutputTexture.VkFormat, - ComponentType.UnsignedInteger, - scissors[0]); - - _renderer.HelperShader.Clear(_renderer, - _blendOutputTexture.GetImageView(), - new float[] { 0, 0, 0, 1 }, - (uint)(ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit), - view.Width, - view.Height, - _blendOutputTexture.VkFormat, - ComponentType.UnsignedInteger, - scissors[0]); + Clear(_edgeOutputTexture); + Clear(_blendOutputTexture); _renderer.Pipeline.TextureBarrier(); @@ -259,7 +229,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); // Edge pass - _pipeline.SetCommandBuffer(cbs); _pipeline.SetProgram(_edgeProgram); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); _pipeline.Specialize(_specConstants); @@ -271,35 +240,25 @@ namespace Ryujinx.Graphics.Vulkan.Effects _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); - _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); _pipeline.SetImage(0, _edgeOutputTexture, GAL.Format.R8G8B8A8Unorm); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); // Blend pass - _pipeline.SetCommandBuffer(cbs); _pipeline.SetProgram(_blendProgram); _pipeline.Specialize(_specConstants); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); - _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); _pipeline.SetImage(0, _blendOutputTexture, GAL.Format.R8G8B8A8Unorm); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); // Neighbour pass - _pipeline.SetCommandBuffer(cbs); _pipeline.SetProgram(_neighbourProgram); _pipeline.Specialize(_specConstants); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); - _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); _pipeline.SetImage(0, _outputTexture, GAL.Format.R8G8B8A8Unorm); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); @@ -310,5 +269,21 @@ namespace Ryujinx.Graphics.Vulkan.Effects return _outputTexture; } + + private void Clear(TextureView texture) + { + Span<uint> colorMasks = stackalloc uint[1]; + + colorMasks[0] = 0xf; + + Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; + + scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height); + + _pipeline.SetRenderTarget(texture.GetImageViewForAttachment(), (uint)texture.Width, (uint)texture.Height, false, texture.VkFormat); + _pipeline.SetRenderTargetColorMasks(colorMasks); + _pipeline.SetScissors(scissors); + _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); + } } } \ No newline at end of file From 138d5dc64adf8e19a2d5b3d5bc6de3ee2099388c Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sun, 16 Apr 2023 18:57:01 +0100 Subject: [PATCH 456/737] Vulkan: HashTableSlim lookup optimization (#4688) --- Ryujinx.Graphics.Vulkan/HashTableSlim.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/HashTableSlim.cs b/Ryujinx.Graphics.Vulkan/HashTableSlim.cs index 2dde2aeb2..e4ad39587 100644 --- a/Ryujinx.Graphics.Vulkan/HashTableSlim.cs +++ b/Ryujinx.Graphics.Vulkan/HashTableSlim.cs @@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Vulkan private struct Entry { + public int Hash; public K Key; public V Value; } @@ -59,6 +60,7 @@ namespace Ryujinx.Graphics.Vulkan { var entry = new Entry() { + Hash = key.GetHashCode(), Key = key, Value = value }; @@ -91,12 +93,11 @@ namespace Ryujinx.Graphics.Vulkan var bucket = _hashTable[hashCode & TotalBucketsMask]; if (bucket != null) { - for (int i = 0; i < bucket.Length; i++) { ref var entry = ref bucket[i]; - if (entry.Key.Equals(ref key)) + if (entry.Hash == hashCode && entry.Key.Equals(ref key)) { value = entry.Value; return true; From eabd0ec93f903fb39605a29b35db4c6290bc9c88 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 16 Apr 2023 20:56:27 -0300 Subject: [PATCH 457/737] Revert "chore: Update Silk.NET to 2.17.1 (#4686)" (#4690) This reverts commit 79d1c190dba48e405a833f654691e47509a29792. --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 563d2a685..85776c74b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -37,9 +37,9 @@ <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" /> <PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" /> - <PackageVersion Include="Silk.NET.Vulkan" Version="2.17.1" /> - <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.17.1" /> - <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.17.1" /> + <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> + <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" /> + <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" /> <PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> From d9b63353b019e1f1775370154f4f045ff14184ce Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 17 Apr 2023 05:13:53 -0300 Subject: [PATCH 458/737] Support copy between multisample and non-multisample depth textures (#4676) * Support copy between multisample and non-multisample depth textures * PR feedback --- .../Image/TextureCompatibility.cs | 7 +- .../Image/IntermmediatePool.cs | 103 +++++ Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 5 + Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 78 +++- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 2 +- Ryujinx.Graphics.Vulkan/HelperShader.cs | 275 +++++++++--- .../DepthDrawToMsFragmentShaderSource.frag | 25 ++ .../DepthDrawToNonMsFragmentShaderSource.frag | 28 ++ .../Shaders/ShaderBinaries.cs | 425 ++++++++++++++++++ .../StencilDrawToMsFragmentShaderSource.frag | 27 ++ ...tencilDrawToNonMsFragmentShaderSource.frag | 30 ++ Ryujinx.Graphics.Vulkan/TextureStorage.cs | 8 +- 12 files changed, 947 insertions(+), 66 deletions(-) create mode 100644 Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag create mode 100644 Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 4b84333df..e93ea0c0c 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -732,12 +732,7 @@ namespace Ryujinx.Graphics.Gpu.Image break; case Target.Texture2DMultisample: case Target.Texture2DMultisampleArray: - // We don't support copy between multisample and non-multisample depth-stencil textures - // because there's no way to emulate that since most GPUs don't support writing a - // custom stencil value into the texture, among several other API limitations. - - if ((rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) && - !rhs.FormatInfo.Format.IsDepthOrStencil()) + if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) { return TextureViewCompatibility.CopyOnly; } diff --git a/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs b/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs new file mode 100644 index 000000000..4f167e893 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs @@ -0,0 +1,103 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class IntermediatePool : IDisposable + { + private readonly OpenGLRenderer _renderer; + private readonly List<TextureView> _entries; + + public IntermediatePool(OpenGLRenderer renderer) + { + _renderer = renderer; + _entries = new List<TextureView>(); + } + + public TextureView GetOrCreateWithAtLeast( + Target target, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + int width, + int height, + int depth, + int levels, + int samples) + { + TextureView entry; + + for (int i = 0; i < _entries.Count; i++) + { + entry = _entries[i]; + + if (entry.Target == target && entry.Format == format && entry.Info.Samples == samples) + { + if (entry.Width < width || + entry.Height < height || + entry.Info.Depth < depth || + entry.Info.Levels < levels) + { + width = Math.Max(width, entry.Width); + height = Math.Max(height, entry.Height); + depth = Math.Max(depth, entry.Info.Depth); + levels = Math.Max(levels, entry.Info.Levels); + + entry.Dispose(); + entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels, samples); + _entries[i] = entry; + } + + return entry; + } + } + + entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels, samples); + _entries.Add(entry); + + return entry; + } + + private TextureView CreateNew( + Target target, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + int width, + int height, + int depth, + int levels, + int samples) + { + return (TextureView)_renderer.CreateTexture(new TextureCreateInfo( + width, + height, + depth, + levels, + samples, + blockWidth, + blockHeight, + bytesPerPixel, + format, + DepthStencilMode.Depth, + target, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha), 1f); + } + + public void Dispose() + { + foreach (TextureView entry in _entries) + { + entry.Dispose(); + } + + _entries.Clear(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index 75c4d8708..a4b08787b 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -15,9 +15,12 @@ namespace Ryujinx.Graphics.OpenGL.Image private int _copyPboHandle; private int _copyPboSize; + public IntermediatePool IntermediatePool { get; } + public TextureCopy(OpenGLRenderer renderer) { _renderer = renderer; + IntermediatePool = new IntermediatePool(renderer); } public void Copy( @@ -514,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image _copyPboHandle = 0; } + + IntermediatePool.Dispose(); } } } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index ddc5f9a37..804b3b03e 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -117,12 +117,20 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - if (!destinationView.Target.IsMultisample() && Target.IsMultisample()) + bool srcIsMultisample = Target.IsMultisample(); + bool dstIsMultisample = destinationView.Target.IsMultisample(); + + if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) + { + int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); + CopyWithBlitForDepthMS(destinationView, 0, firstLayer, layers); + } + else if (!dstIsMultisample && srcIsMultisample) { int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers); } - else if (destinationView.Target.IsMultisample() && !Target.IsMultisample()) + else if (dstIsMultisample && !srcIsMultisample) { int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers); @@ -143,11 +151,18 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - if (!destinationView.Target.IsMultisample() && Target.IsMultisample()) + bool srcIsMultisample = Target.IsMultisample(); + bool dstIsMultisample = destinationView.Target.IsMultisample(); + + if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) + { + CopyWithBlitForDepthMS(destinationView, srcLayer, dstLayer, 1); + } + else if (!dstIsMultisample && srcIsMultisample) { _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1); } - else if (destinationView.Target.IsMultisample() && !Target.IsMultisample()) + else if (dstIsMultisample && !srcIsMultisample) { _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1); } @@ -161,6 +176,61 @@ namespace Ryujinx.Graphics.OpenGL.Image } } + private void CopyWithBlitForDepthMS(TextureView destinationView, int srcLayer, int dstLayer, int layers) + { + // This is currently used for multisample <-> non-multisample copies. + // We can't do that with compute because it's not possible to write depth textures on compute. + // It can be done with draws, but we don't have support for saving and restoring the OpenGL state + // for a draw with different state right now. + // This approach uses blit, which causes a resolution loss since some samples will be lost + // in the process. + + Extents2D srcRegion = new Extents2D(0, 0, Width, Height); + Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); + + if (destinationView.Target.IsMultisample()) + { + TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( + Info.Target, + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + destinationView.Width, + destinationView.Height, + Info.Depth, + 1, + 1); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, dstRegion, false); + _renderer.TextureCopy.Copy(intermmediate, destinationView, dstRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); + } + else + { + Target target = Target switch + { + Target.Texture2DMultisample => Target.Texture2D, + Target.Texture2DMultisampleArray => Target.Texture2DArray, + _ => Target + }; + + TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( + target, + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + Width, + Height, + Info.Depth, + 1, + 1); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, false); + _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); + } + } + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 658468d17..cde992028 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.Vulkan public void InsertClearBarrierDS(CommandBufferScoped cbs) { - _depthStencil?.Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.EarlyFragmentTestsBit); + _depthStencil?.Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.LateFragmentTestsBit); } } } diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 8eb3088ec..c57edaf7c 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -39,8 +39,12 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programColorDrawToMs; private readonly IProgram _programDepthBlit; private readonly IProgram _programDepthBlitMs; + private readonly IProgram _programDepthDrawToMs; + private readonly IProgram _programDepthDrawToNonMs; private readonly IProgram _programStencilBlit; private readonly IProgram _programStencilBlitMs; + private readonly IProgram _programStencilDrawToMs; + private readonly IProgram _programStencilDrawToNonMs; public HelperShader(VulkanRenderer gd, Device device) { @@ -188,6 +192,18 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); + _programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + if (gd.Capabilities.SupportsShaderStencilExport) { _programStencilBlit = gd.CreateProgramWithMinimalLayout(new[] @@ -201,6 +217,18 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); + + _programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); } } @@ -1043,6 +1071,8 @@ namespace Ryujinx.Graphics.Vulkan Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; int samples = src.Info.Samples; + bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil(); + var aspectFlags = src.Info.Format.ConvertAspectFlags(); // X and Y are the expected texture samples. // Z and W are the actual texture samples used. @@ -1061,42 +1091,94 @@ namespace Ryujinx.Graphics.Vulkan TextureStorage.DefaultAccessMask, AccessFlags.ShaderReadBit, PipelineStageFlags.AllCommandsBit, - PipelineStageFlags.ComputeShaderBit, - ImageAspectFlags.ColorBit, + isDepthOrStencil ? PipelineStageFlags.FragmentShaderBit : PipelineStageFlags.ComputeShaderBit, + aspectFlags, src.FirstLayer + srcLayer, src.FirstLevel, depth, 1); _pipeline.SetCommandBuffer(cbs); - - _pipeline.SetProgram(_programColorCopyToNonMs); - - var format = GetFormat(src.Info.BytesPerPixel); - - int dispatchX = (dst.Info.Width + 31) / 32; - int dispatchY = (dst.Info.Height + 31) / 32; - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); - for (int z = 0; z < depth; z++) + if (isDepthOrStencil) { - var srcView = Create2DLayerView(src, srcLayer + z, 0, format); - var dstView = Create2DLayerView(dst, dstLayer + z, 0); + // We can't use compute for this case because compute can't modify depth textures. - _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); - _pipeline.SetImage(0, dstView, format); + Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; - _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height); - if (srcView != src) + viewports[0] = new GAL.Viewport( + rect, + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; + + scissors[0] = new Rectangle<int>(0, 0, dst.Width, dst.Height); + + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + + for (int z = 0; z < depth; z++) { - srcView.Release(); + var srcView = Create2DLayerView(src, srcLayer + z, 0); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); + + _pipeline.SetRenderTarget( + ((TextureView)dstView).GetImageViewForAttachment(), + (uint)dst.Width, + (uint)dst.Height, + true, + dst.VkFormat); + + CopyMSDraw(srcView, aspectFlags, fromMS: true); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } } + } + else + { + var format = GetFormat(src.Info.BytesPerPixel); - if (dstView != dst) + int dispatchX = (dst.Info.Width + 31) / 32; + int dispatchY = (dst.Info.Height + 31) / 32; + + _pipeline.SetProgram(_programColorCopyToNonMs); + + for (int z = 0; z < depth; z++) { - dstView.Release(); + var srcView = Create2DLayerView(src, srcLayer + z, 0, format); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); + + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); + _pipeline.SetImage(0, dstView, format); + + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } } } @@ -1108,11 +1190,11 @@ namespace Ryujinx.Graphics.Vulkan gd.Api, cbs.CommandBuffer, dst.GetImage().Get(cbs).Value, - AccessFlags.ShaderWriteBit, + isDepthOrStencil ? AccessFlags.DepthStencilAttachmentWriteBit : AccessFlags.ShaderWriteBit, TextureStorage.DefaultAccessMask, - PipelineStageFlags.ComputeShaderBit, + isDepthOrStencil ? PipelineStageFlags.LateFragmentTestsBit : PipelineStageFlags.ComputeShaderBit, PipelineStageFlags.AllCommandsBit, - ImageAspectFlags.ColorBit, + aspectFlags, dst.FirstLayer + dstLayer, dst.FirstLevel, depth, @@ -1126,6 +1208,8 @@ namespace Ryujinx.Graphics.Vulkan Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; int samples = dst.Info.Samples; + bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil(); + var aspectFlags = src.Info.Format.ConvertAspectFlags(); // X and Y are the expected texture samples. // Z and W are the actual texture samples used. @@ -1145,7 +1229,7 @@ namespace Ryujinx.Graphics.Vulkan AccessFlags.ShaderReadBit, PipelineStageFlags.AllCommandsBit, PipelineStageFlags.FragmentShaderBit, - ImageAspectFlags.ColorBit, + aspectFlags, src.FirstLayer + srcLayer, src.FirstLevel, depth, @@ -1153,8 +1237,6 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetCommandBuffer(cbs); - _pipeline.SetProgram(_programColorDrawToMs); - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height); @@ -1179,33 +1261,66 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); - var format = GetFormat(src.Info.BytesPerPixel); - var vkFormat = FormatTable.GetFormat(format); - - for (int z = 0; z < depth; z++) + if (isDepthOrStencil) { - var srcView = Create2DLayerView(src, srcLayer + z, 0, format); - var dstView = Create2DLayerView(dst, dstLayer + z, 0); - - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null); - _pipeline.SetRenderTarget( - ((TextureView)dstView).GetView(format).GetImageViewForAttachment(), - (uint)dst.Width, - (uint)dst.Height, - (uint)samples, - false, - vkFormat); - - _pipeline.Draw(4, 1, 0, 0); - - if (srcView != src) + for (int z = 0; z < depth; z++) { - srcView.Release(); + var srcView = Create2DLayerView(src, srcLayer + z, 0); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); + + _pipeline.SetRenderTarget( + ((TextureView)dstView).GetImageViewForAttachment(), + (uint)dst.Width, + (uint)dst.Height, + (uint)samples, + true, + dst.VkFormat); + + CopyMSDraw(srcView, aspectFlags, fromMS: false); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } } + } + else + { + _pipeline.SetProgram(_programColorDrawToMs); - if (dstView != dst) + var format = GetFormat(src.Info.BytesPerPixel); + var vkFormat = FormatTable.GetFormat(format); + + for (int z = 0; z < depth; z++) { - dstView.Release(); + var srcView = Create2DLayerView(src, srcLayer + z, 0, format); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); + + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null); + _pipeline.SetRenderTarget( + ((TextureView)dstView).GetView(format).GetImageViewForAttachment(), + (uint)dst.Width, + (uint)dst.Height, + (uint)samples, + false, + vkFormat); + + _pipeline.Draw(4, 1, 0, 0); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } } } @@ -1217,17 +1332,71 @@ namespace Ryujinx.Graphics.Vulkan gd.Api, cbs.CommandBuffer, dst.GetImage().Get(cbs).Value, - AccessFlags.ColorAttachmentWriteBit, + isDepthOrStencil ? AccessFlags.DepthStencilAttachmentWriteBit : AccessFlags.ColorAttachmentWriteBit, TextureStorage.DefaultAccessMask, - PipelineStageFlags.FragmentShaderBit, + isDepthOrStencil ? PipelineStageFlags.LateFragmentTestsBit : PipelineStageFlags.ColorAttachmentOutputBit, PipelineStageFlags.AllCommandsBit, - ImageAspectFlags.ColorBit, + aspectFlags, dst.FirstLayer + dstLayer, dst.FirstLevel, depth, 1); } + private void CopyMSDraw(TextureView src, ImageAspectFlags aspectFlags, bool fromMS) + { + if (aspectFlags.HasFlag(ImageAspectFlags.DepthBit)) + { + var depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth); + + CopyMSAspectDraw(depthTexture, fromMS, isDepth: true); + + if (depthTexture != src) + { + depthTexture.Release(); + } + } + + if (aspectFlags.HasFlag(ImageAspectFlags.StencilBit) && _programStencilDrawToMs != null) + { + var stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil); + + CopyMSAspectDraw(stencilTexture, fromMS, isDepth: false); + + if (stencilTexture != src) + { + stencilTexture.Release(); + } + } + } + + private void CopyMSAspectDraw(TextureView src, bool fromMS, bool isDepth) + { + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); + + if (isDepth) + { + _pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + } + else + { + _pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); + } + + _pipeline.Draw(4, 1, 0, 0); + + if (isDepth) + { + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always)); + } + else + { + _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); + } + } + private static (int, int) GetSampleCountXYLog2(int samples) { int samplesInXLog2 = 0; @@ -1494,8 +1663,12 @@ namespace Ryujinx.Graphics.Vulkan _programColorDrawToMs.Dispose(); _programDepthBlit.Dispose(); _programDepthBlitMs.Dispose(); + _programDepthDrawToMs.Dispose(); + _programDepthDrawToNonMs.Dispose(); _programStencilBlit?.Dispose(); _programStencilBlitMs?.Dispose(); + _programStencilDrawToMs?.Dispose(); + _programStencilDrawToNonMs?.Dispose(); _samplerNearest.Dispose(); _samplerLinear.Dispose(); _pipeline.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag new file mode 100644 index 000000000..bf5f612f7 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag @@ -0,0 +1,25 @@ +#version 450 core + +layout (std140, binding = 0) uniform sample_counts_log2_in +{ + ivec4 sample_counts_log2; +}; + +layout (set = 2, binding = 0) uniform sampler2D src; + +void main() +{ + int deltaX = sample_counts_log2.x - sample_counts_log2.z; + int deltaY = sample_counts_log2.y - sample_counts_log2.w; + int samplesInXLog2 = sample_counts_log2.z; + int samplesInYLog2 = sample_counts_log2.w; + int samplesInX = 1 << samplesInXLog2; + int samplesInY = 1 << samplesInYLog2; + + int sampleIndex = gl_SampleID; + + int inX = (int(gl_FragCoord.x) << sample_counts_log2.x) | ((sampleIndex & (samplesInX - 1)) << deltaX); + int inY = (int(gl_FragCoord.y) << sample_counts_log2.y) | ((sampleIndex >> samplesInXLog2) << deltaY); + + gl_FragDepth = texelFetch(src, ivec2(inX, inY), 0).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag new file mode 100644 index 000000000..e376b2e72 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag @@ -0,0 +1,28 @@ +#version 450 core + +layout (std140, binding = 0) uniform sample_counts_log2_in +{ + ivec4 sample_counts_log2; +}; + +layout (set = 2, binding = 0) uniform sampler2DMS srcMS; + +void main() +{ + uvec2 coords = uvec2(gl_FragCoord.xy); + + int deltaX = sample_counts_log2.x - sample_counts_log2.z; + int deltaY = sample_counts_log2.y - sample_counts_log2.w; + int samplesInXLog2 = sample_counts_log2.z; + int samplesInYLog2 = sample_counts_log2.w; + int samplesInX = 1 << samplesInXLog2; + int samplesInY = 1 << samplesInYLog2; + int sampleIdx = ((int(coords.x) >> deltaX) & (samplesInX - 1)) | (((int(coords.y) >> deltaY) & (samplesInY - 1)) << samplesInXLog2); + + samplesInXLog2 = sample_counts_log2.x; + samplesInYLog2 = sample_counts_log2.y; + + ivec2 shiftedCoords = ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2); + + gl_FragDepth = texelFetch(srcMS, shiftedCoords, sampleIdx).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index 7fd047a23..c9dde7b63 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -1874,6 +1874,213 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + public static readonly byte[] DepthDrawToMsFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, + 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, + 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, + 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6F, 0x6F, 0x72, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x51, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, + 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x51, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x52, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0xC5, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x4E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x53, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x5B, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x31, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x5D, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + + public static readonly byte[] DepthDrawToNonMsFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, + 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, + 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, + 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, + 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4D, 0x53, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x6D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + public static readonly byte[] StencilBlitFragmentShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, @@ -1984,5 +2191,223 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x22, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + + public static readonly byte[] StencilDrawToMsFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, + 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, + 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, + 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x13, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, + 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, + 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, + 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, + 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6F, 0x6F, 0x72, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x51, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, + 0x72, 0x61, 0x67, 0x53, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x4C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x5A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x51, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00, + }; + + public static readonly byte[] StencilDrawToNonMsFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, + 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, + 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x13, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, + 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, + 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, + 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, + 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, + 0x72, 0x61, 0x67, 0x53, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4D, + 0x53, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x96, 0x13, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x60, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x4A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x5D, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag new file mode 100644 index 000000000..a07ae9d19 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag @@ -0,0 +1,27 @@ +#version 450 core + +#extension GL_ARB_shader_stencil_export : require + +layout (std140, binding = 0) uniform sample_counts_log2_in +{ + ivec4 sample_counts_log2; +}; + +layout (set = 2, binding = 0) uniform isampler2D src; + +void main() +{ + int deltaX = sample_counts_log2.x - sample_counts_log2.z; + int deltaY = sample_counts_log2.y - sample_counts_log2.w; + int samplesInXLog2 = sample_counts_log2.z; + int samplesInYLog2 = sample_counts_log2.w; + int samplesInX = 1 << samplesInXLog2; + int samplesInY = 1 << samplesInYLog2; + + int sampleIndex = gl_SampleID; + + int inX = (int(gl_FragCoord.x) << sample_counts_log2.x) | ((sampleIndex & (samplesInX - 1)) << deltaX); + int inY = (int(gl_FragCoord.y) << sample_counts_log2.y) | ((sampleIndex >> samplesInXLog2) << deltaY); + + gl_FragStencilRefARB = texelFetch(src, ivec2(inX, inY), 0).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag new file mode 100644 index 000000000..3addd9d1a --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag @@ -0,0 +1,30 @@ +#version 450 core + +#extension GL_ARB_shader_stencil_export : require + +layout (std140, binding = 0) uniform sample_counts_log2_in +{ + ivec4 sample_counts_log2; +}; + +layout (set = 2, binding = 0) uniform isampler2DMS srcMS; + +void main() +{ + uvec2 coords = uvec2(gl_FragCoord.xy); + + int deltaX = sample_counts_log2.x - sample_counts_log2.z; + int deltaY = sample_counts_log2.y - sample_counts_log2.w; + int samplesInXLog2 = sample_counts_log2.z; + int samplesInYLog2 = sample_counts_log2.w; + int samplesInX = 1 << samplesInXLog2; + int samplesInY = 1 << samplesInYLog2; + int sampleIdx = ((int(coords.x) >> deltaX) & (samplesInX - 1)) | (((int(coords.y) >> deltaY) & (samplesInY - 1)) << samplesInXLog2); + + samplesInXLog2 = sample_counts_log2.x; + samplesInYLog2 = sample_counts_log2.y; + + ivec2 shiftedCoords = ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2); + + gl_FragStencilRefARB = texelFetch(srcMS, shiftedCoords, sampleIdx).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 8ebdd4c0f..0582e6ca8 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -444,7 +444,7 @@ namespace Ryujinx.Graphics.Vulkan public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) { - if (_lastReadAccess != AccessFlags.NoneKhr) + if (_lastReadAccess != AccessFlags.None) { ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); @@ -462,7 +462,7 @@ namespace Ryujinx.Graphics.Vulkan _info.GetLayers(), _info.Levels); - _lastReadAccess = AccessFlags.NoneKhr; + _lastReadAccess = AccessFlags.None; _lastReadStage = PipelineStageFlags.None; } } @@ -472,7 +472,7 @@ namespace Ryujinx.Graphics.Vulkan _lastReadAccess |= dstAccessFlags; _lastReadStage |= dstStageFlags; - if (_lastModificationAccess != AccessFlags.NoneKhr) + if (_lastModificationAccess != AccessFlags.None) { ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); @@ -490,7 +490,7 @@ namespace Ryujinx.Graphics.Vulkan _info.GetLayers(), _info.Levels); - _lastModificationAccess = AccessFlags.NoneKhr; + _lastModificationAccess = AccessFlags.None; } } From 0dec91bb4230d92e93237f967da0428713c7b01b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 21:30:40 +0200 Subject: [PATCH 459/737] nuget: bump System.Management from 7.0.0 to 7.0.1 (#4695) Bumps [System.Management](https://github.com/dotnet/runtime) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v7.0.0...v7.0.1) --- updated-dependencies: - dependency-name: System.Management dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 85776c74b..75d1eeaff 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -46,7 +46,7 @@ <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> - <PackageVersion Include="System.Management" Version="7.0.0" /> + <PackageVersion Include="System.Management" Version="7.0.1" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" /> </ItemGroup> From 9e50dd99d7dd105ec5fb39f924e7662f974f3652 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 21:31:12 +0200 Subject: [PATCH 460/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.28.1 to 6.29.0 (#4694) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.28.1 to 6.29.0. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.28.1...6.29.0) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 75d1eeaff..cbc67f073 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> - <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" /> + <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.29.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.1" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> From add2a9d15120185166adc6d2431ffee7d8a8b26b Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 20 Apr 2023 12:10:17 -0300 Subject: [PATCH 461/737] Avoid LM service crashes by not reading more than the buffer size (#4701) --- Ryujinx.Common/Memory/SpanReader.cs | 5 +++++ Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Common/Memory/SpanReader.cs b/Ryujinx.Common/Memory/SpanReader.cs index e46649e1c..673932d0b 100644 --- a/Ryujinx.Common/Memory/SpanReader.cs +++ b/Ryujinx.Common/Memory/SpanReader.cs @@ -33,6 +33,11 @@ namespace Ryujinx.Common.Memory return data; } + public ReadOnlySpan<byte> GetSpanSafe(int size) + { + return GetSpan((int)Math.Min((uint)_input.Length, (uint)size)); + } + public T ReadAt<T>(int offset) where T : unmanaged { return MemoryMarshal.Cast<byte, T>(_input.Slice(offset))[0]; diff --git a/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs index 8b3acb101..e930bdd75 100644 --- a/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs +++ b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -113,7 +113,7 @@ namespace Ryujinx.Horizon.LogManager.Ipc } else if (key == LogDataChunkKey.Message) { - string text = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + string text = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); if (isHeadPacket && isTailPacket) { @@ -131,23 +131,23 @@ namespace Ryujinx.Horizon.LogManager.Ipc } else if (key == LogDataChunkKey.Filename) { - _logPacket.Filename = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + _logPacket.Filename = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); } else if (key == LogDataChunkKey.Function) { - _logPacket.Function = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + _logPacket.Function = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); } else if (key == LogDataChunkKey.Module) { - _logPacket.Module = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + _logPacket.Module = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); } else if (key == LogDataChunkKey.Thread) { - _logPacket.Thread = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + _logPacket.Thread = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); } else if (key == LogDataChunkKey.ProgramName) { - _logPacket.ProgramName = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + _logPacket.ProgramName = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); } } From e27f5522e20ce0d84532a01b36222fe425ccd9ce Mon Sep 17 00:00:00 2001 From: SpicerXD <spicerxd.dev@gmail.com> Date: Sat, 22 Apr 2023 08:31:28 -0500 Subject: [PATCH 462/737] Removed MotionInput Calibration (#4705) Don't know why this is here. It just seems to set the filter to an identity. Which then quickly returns to where its supposed to be anyways. --- Ryujinx.Input/Motion/MotionInput.cs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/Ryujinx.Input/Motion/MotionInput.cs b/Ryujinx.Input/Motion/MotionInput.cs index d92c3d7fd..1923d9cbe 100644 --- a/Ryujinx.Input/Motion/MotionInput.cs +++ b/Ryujinx.Input/Motion/MotionInput.cs @@ -12,7 +12,6 @@ namespace Ryujinx.Input public Vector3 Rotation { get; set; } private readonly MotionSensorFilter _filter; - private int _calibrationFrame = 0; public MotionInput() { @@ -29,26 +28,6 @@ namespace Ryujinx.Input { if (TimeStamp != 0) { - if (gyro.Length() <= 1f && accel.Length() >= 0.8f && accel.Z >= 0.8f) - { - _calibrationFrame++; - - if (_calibrationFrame >= 90) - { - gyro = Vector3.Zero; - - Rotation = Vector3.Zero; - - _filter.Reset(); - - _calibrationFrame = 0; - } - } - else - { - _calibrationFrame = 0; - } - Accelerometer = -accel; if (gyro.Length() < deadzone) From 8d9d508dc78eb5225c99cb425fa484999f3c4305 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sat, 22 Apr 2023 22:02:39 +0100 Subject: [PATCH 463/737] Shader: Bias textureGather instructions on AMD/Intel (#4703) * Experimental (GLSL, forced) * SPIR-V attempt * Add capability * Fix pCount == 1 on glsl * Fix typo --- Ryujinx.Graphics.GAL/Capabilities.cs | 6 +++- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 2 ++ Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 10 +++++-- .../Glsl/Instructions/InstGenMemory.cs | 23 +++++++++++++- .../CodeGen/Spirv/Instructions.cs | 30 +++++++++++++++++++ Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 ++++++ .../HardwareCapabilities.cs | 5 +++- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 6 ++-- 9 files changed, 84 insertions(+), 9 deletions(-) diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index 7822da211..bc4a02c97 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -48,6 +48,8 @@ namespace Ryujinx.Graphics.GAL public readonly float MaximumSupportedAnisotropy; public readonly int StorageBufferOffsetAlignment; + public readonly int GatherBiasPrecision; + public Capabilities( TargetApi api, string vendorName, @@ -87,7 +89,8 @@ namespace Ryujinx.Graphics.GAL uint maximumImagesPerStage, int maximumComputeSharedMemorySize, float maximumSupportedAnisotropy, - int storageBufferOffsetAlignment) + int storageBufferOffsetAlignment, + int gatherBiasPrecision) { Api = api; VendorName = vendorName; @@ -128,6 +131,7 @@ namespace Ryujinx.Graphics.GAL MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; MaximumSupportedAnisotropy = maximumSupportedAnisotropy; StorageBufferOffsetAlignment = storageBufferOffsetAlignment; + GatherBiasPrecision = gatherBiasPrecision; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 0b87cc910..48464f832 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4404; + private const uint CodeGenVersion = 4703; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 1402f146b..bbf2702e4 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -112,6 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader }; } + public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision; + public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision; public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug; diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 91e52178f..5a2e3fe4e 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -103,11 +103,14 @@ namespace Ryujinx.Graphics.OpenGL public Capabilities GetCapabilities() { + bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows; + bool amdWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows; + return new Capabilities( api: TargetApi.OpenGL, vendorName: GpuVendor, - hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows, - hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows, + hasFrontFacingBug: intelWindows, + hasVectorIndexingBug: amdWindows, needsFragmentOutputSpecialization: false, reduceShaderPrecision: false, supportsAstcCompression: HwCapabilities.SupportsAstcCompression, @@ -142,7 +145,8 @@ namespace Ryujinx.Graphics.OpenGL maximumImagesPerStage: 8, maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize, maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy, - storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment); + storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment, + gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan. } public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 263eada6f..a5d2632ce 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -677,7 +677,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return vector; } - Append(ApplyScaling(AssemblePVector(pCount))); + string ApplyBias(string vector) + { + int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision(); + if (isGather && gatherBiasPrecision != 0) + { + // GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels. + // Offset by the gather precision divided by 2 to correct for rounding. + + if (pCount == 1) + { + vector = $"{vector} + (1.0 / (float(textureSize({samplerName}, 0)) * float({1 << (gatherBiasPrecision + 1)})))"; + } + else + { + vector = $"{vector} + (1.0 / (vec{pCount}(textureSize({samplerName}, 0).{"xyz".Substring(0, pCount)}) * float({1 << (gatherBiasPrecision + 1)})))"; + } + } + + return vector; + } + + Append(ApplyBias(ApplyScaling(AssemblePVector(pCount)))); string AssembleDerivativesVector(int count) { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 14d6ab52a..b3db19051 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Numerics; using static Spv.Specification; @@ -1556,6 +1557,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } + SpvInstruction ApplyBias(SpvInstruction vector, SpvInstruction image) + { + int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision(); + if (isGather && gatherBiasPrecision != 0) + { + // GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels. + // Offset by the gather precision divided by 2 to correct for rounding. + var sizeType = pCount == 1 ? context.TypeS32() : context.TypeVector(context.TypeS32(), pCount); + var pVectorType = pCount == 1 ? context.TypeFP32() : context.TypeVector(context.TypeFP32(), pCount); + + var bias = context.Constant(context.TypeFP32(), (float)(1 << (gatherBiasPrecision + 1))); + var biasVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(bias, pCount).ToArray()); + + var one = context.Constant(context.TypeFP32(), 1f); + var oneVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(one, pCount).ToArray()); + + var divisor = context.FMul( + pVectorType, + context.ConvertSToF(pVectorType, context.ImageQuerySize(sizeType, image)), + biasVector); + + vector = context.FAdd(pVectorType, vector, context.FDiv(pVectorType, oneVector, divisor)); + } + + return vector; + } + SpvInstruction pCoords = AssemblePVector(pCount); pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount); @@ -1716,6 +1744,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv image = context.Image(imageType, image); } + pCoords = ApplyBias(pCoords, image); + var operands = operandsList.ToArray(); SpvInstruction result; diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index ba5f2a92f..bc5e67c35 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -196,6 +196,15 @@ namespace Ryujinx.Graphics.Shader return false; } + /// <summary> + /// Queries host's gather operation precision bits for biasing their coordinates. Zero means no bias. + /// </summary> + /// <returns>Bits of gather operation precision to use for coordinate bias</returns> + int QueryHostGatherBiasPrecision() + { + return 0; + } + /// <summary> /// Queries host about whether to reduce precision to improve performance. /// </summary> diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index a45c2409b..e206bb299 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -46,6 +46,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly SampleCountFlags SupportedSampleCounts; public readonly PortabilitySubsetFlags PortabilitySubset; public readonly uint VertexBufferAlignment; + public readonly uint SubTexelPrecisionBits; public HardwareCapabilities( bool supportsIndexTypeUint8, @@ -77,7 +78,8 @@ namespace Ryujinx.Graphics.Vulkan ShaderStageFlags requiredSubgroupSizeStages, SampleCountFlags supportedSampleCounts, PortabilitySubsetFlags portabilitySubset, - uint vertexBufferAlignment) + uint vertexBufferAlignment, + uint subTexelPrecisionBits) { SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsCustomBorderColor = supportsCustomBorderColor; @@ -109,6 +111,7 @@ namespace Ryujinx.Graphics.Vulkan SupportedSampleCounts = supportedSampleCounts; PortabilitySubset = portabilitySubset; VertexBufferAlignment = vertexBufferAlignment; + SubTexelPrecisionBits = subTexelPrecisionBits; } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 92b453fb1..1c295d6ff 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -311,7 +311,8 @@ namespace Ryujinx.Graphics.Vulkan propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, supportedSampleCounts, portabilityFlags, - vertexBufferAlignment); + vertexBufferAlignment, + properties.Limits.SubTexelPrecisionBits); IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice); @@ -576,7 +577,8 @@ namespace Ryujinx.Graphics.Vulkan maximumImagesPerStage: Constants.MaxImagesPerStage, maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize, maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy, - storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment); + storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment, + gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0); } public HardwareInfo GetHardwareInfo() From 666e05f5cb9aa9dc86874823c108586e7a5b38bd Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sun, 23 Apr 2023 22:06:23 -0400 Subject: [PATCH 464/737] Reducing Memory Allocations 202303 (#4624) * use ArrayPool, avoid 6000-7000 allocs/sec of runtime * use ArrayPool, avoid ~7k allocs/second during game execution * use ArrayPool, avoid ~3000 allocs/sec during game execution * use MemoryPool, reduce 0.5 MB/sec of new allocations during game execution * avoid over-allocation by setting List<> Capacity when known * remove LINQ in KTimeManager.UnscheduleFutureInvocation * KTimeManager - avoid spinning one more time when the time has arrived * KTimeManager - let SpinWait decide when to Thread.Yield(), and don't SpinOnce() immediately after Thread.Yield() * use MemoryPool, reduce ~175k bytes/sec allocation during game execution * IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array * Make ButtonMappingEntry a record struct to avoid allocations. Set the List<ButtonMappingEntry> capacity according to use. * add MemoryBuffer type for working with MemoryPool<byte> * update changes to use MemoryBuffer * make parameter ReadOnlySpan instead of Span * whitespace fix * Revert "IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array" This reverts commit f2c698bdf65f049e8481c9f2ec7138d9b9a8261d. * tweak KTimeManager spin behavior * replace MemoryBuffer with ByteMemoryPool modeled after System.Buffers.ArrayMemoryPool<T> * make ByteMemoryPoolBuffer responsible for renting memory --- Ryujinx.Audio/Renderer/Server/StateUpdater.cs | 9 +- .../Renderer/Server/Voice/VoiceState.cs | 4 +- .../ByteMemoryPool.ByteMemoryPoolBuffer.cs | 51 +++++++ Ryujinx.Common/Memory/ByteMemoryPool.cs | 108 +++++++++++++++ Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 129 +++++++++--------- Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 17 ++- .../HOS/Kernel/SupervisorCall/Syscall.cs | 7 +- .../HOS/Kernel/Threading/KSynchronization.cs | 7 +- .../AudioRenderer/AudioRendererServer.cs | 39 +++--- Ryujinx.HLE/HOS/Services/IpcService.cs | 2 + Ryujinx.HLE/HOS/Services/ServerBase.cs | 2 + .../SurfaceFlinger/IHOSBinderDriver.cs | 23 ++-- Ryujinx.Input.SDL2/SDL2Gamepad.cs | 14 +- 13 files changed, 303 insertions(+), 109 deletions(-) create mode 100644 Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs create mode 100644 Ryujinx.Common/Memory/ByteMemoryPool.cs diff --git a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs index 0446cd8c6..5cf539c6d 100644 --- a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs +++ b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs @@ -11,6 +11,7 @@ using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Utils; using Ryujinx.Common.Logging; using System; +using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -149,12 +150,16 @@ namespace Ryujinx.Audio.Renderer.Server state.InUse = false; } + Memory<VoiceUpdateState>[] voiceUpdateStatesArray = ArrayPool<Memory<VoiceUpdateState>>.Shared.Rent(Constants.VoiceChannelCountMax); + + Span<Memory<VoiceUpdateState>> voiceUpdateStates = voiceUpdateStatesArray.AsSpan(0, Constants.VoiceChannelCountMax); + // Start processing for (int i = 0; i < context.GetCount(); i++) { VoiceInParameter parameter = parameters[i]; - Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax]; + voiceUpdateStates.Fill(Memory<VoiceUpdateState>.Empty); ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0]; @@ -197,6 +202,8 @@ namespace Ryujinx.Audio.Renderer.Server } } + ArrayPool<Memory<VoiceUpdateState>>.Shared.Return(voiceUpdateStatesArray); + int currentOutputSize = _output.Length; OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount()); diff --git a/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs b/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs index 006d6dd3c..0bf53c544 100644 --- a/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs +++ b/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs @@ -378,7 +378,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// <param name="outStatus">The given user output.</param> /// <param name="parameter">The user parameter.</param> /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> - public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates) + public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates) { #if DEBUG // Sanity check in debug mode of the internal state @@ -424,7 +424,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> /// <param name="mapper">The mapper to use.</param> /// <param name="behaviourContext">The behaviour context.</param> - public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext) + public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext) { errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2]; diff --git a/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs new file mode 100644 index 000000000..eda350bdd --- /dev/null +++ b/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs @@ -0,0 +1,51 @@ +using System; +using System.Buffers; +using System.Threading; + +namespace Ryujinx.Common.Memory +{ + public sealed partial class ByteMemoryPool + { + /// <summary> + /// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from + /// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/> + /// with a length of the requested size. + /// </summary> + private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte> + { + private byte[] _array; + private readonly int _length; + + public ByteMemoryPoolBuffer(int length) + { + _array = ArrayPool<byte>.Shared.Rent(length); + _length = length; + } + + /// <summary> + /// Returns a <see cref="Memory{Byte}"/> belonging to this owner. + /// </summary> + public Memory<byte> Memory + { + get + { + byte[] array = _array; + + ObjectDisposedException.ThrowIf(array is null, this); + + return new Memory<byte>(array, 0, _length); + } + } + + public void Dispose() + { + var array = Interlocked.Exchange(ref _array, null); + + if (array != null) + { + ArrayPool<byte>.Shared.Return(array); + } + } + } + } +} diff --git a/Ryujinx.Common/Memory/ByteMemoryPool.cs b/Ryujinx.Common/Memory/ByteMemoryPool.cs new file mode 100644 index 000000000..2910f408f --- /dev/null +++ b/Ryujinx.Common/Memory/ByteMemoryPool.cs @@ -0,0 +1,108 @@ +using System; +using System.Buffers; + +namespace Ryujinx.Common.Memory +{ + /// <summary> + /// Provides a pool of re-usable byte array instances. + /// </summary> + public sealed partial class ByteMemoryPool + { + private static readonly ByteMemoryPool _shared = new ByteMemoryPool(); + + /// <summary> + /// Constructs a <see cref="ByteMemoryPool"/> instance. Private to force access through + /// the <see cref="ByteMemoryPool.Shared"/> instance. + /// </summary> + private ByteMemoryPool() + { + // No implementation + } + + /// <summary> + /// Retrieves a shared <see cref="ByteMemoryPool"/> instance. + /// </summary> + public static ByteMemoryPool Shared => _shared; + + /// <summary> + /// Returns the maximum buffer size supported by this pool. + /// </summary> + public int MaxBufferSize => Array.MaxLength; + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer may contain data from a prior use. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> Rent(long length) + => RentImpl(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer may contain data from a prior use. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> Rent(ulong length) + => RentImpl(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer may contain data from a prior use. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> Rent(int length) + => RentImpl(length); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer's contents are cleared (set to all 0s) before returning. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> RentCleared(long length) + => RentCleared(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer's contents are cleared (set to all 0s) before returning. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> RentCleared(ulong length) + => RentCleared(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer's contents are cleared (set to all 0s) before returning. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> RentCleared(int length) + { + var buffer = RentImpl(length); + + buffer.Memory.Span.Clear(); + + return buffer; + } + + private static ByteMemoryPoolBuffer RentImpl(int length) + { + if ((uint)length > Array.MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(length), length, null); + } + + return new ByteMemoryPoolBuffer(length); + } + } +} diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 394bf888e..21630c42e 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -27,98 +27,103 @@ namespace Ryujinx.HLE.HOS.Ipc public IpcMessage() { - PtrBuff = new List<IpcPtrBuffDesc>(); - SendBuff = new List<IpcBuffDesc>(); - ReceiveBuff = new List<IpcBuffDesc>(); - ExchangeBuff = new List<IpcBuffDesc>(); - RecvListBuff = new List<IpcRecvListBuffDesc>(); + PtrBuff = new List<IpcPtrBuffDesc>(0); + SendBuff = new List<IpcBuffDesc>(0); + ReceiveBuff = new List<IpcBuffDesc>(0); + ExchangeBuff = new List<IpcBuffDesc>(0); + RecvListBuff = new List<IpcRecvListBuffDesc>(0); - ObjectIds = new List<int>(); + ObjectIds = new List<int>(0); } - public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this() + public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) { using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data)) { BinaryReader reader = new BinaryReader(ms); - Initialize(reader, cmdPtr); - } - } + int word0 = reader.ReadInt32(); + int word1 = reader.ReadInt32(); - private void Initialize(BinaryReader reader, long cmdPtr) - { - int word0 = reader.ReadInt32(); - int word1 = reader.ReadInt32(); + Type = (IpcMessageType)(word0 & 0xffff); - Type = (IpcMessageType)(word0 & 0xffff); + int ptrBuffCount = (word0 >> 16) & 0xf; + int sendBuffCount = (word0 >> 20) & 0xf; + int recvBuffCount = (word0 >> 24) & 0xf; + int xchgBuffCount = (word0 >> 28) & 0xf; - int ptrBuffCount = (word0 >> 16) & 0xf; - int sendBuffCount = (word0 >> 20) & 0xf; - int recvBuffCount = (word0 >> 24) & 0xf; - int xchgBuffCount = (word0 >> 28) & 0xf; + int rawDataSize = (word1 >> 0) & 0x3ff; + int recvListFlags = (word1 >> 10) & 0xf; + bool hndDescEnable = ((word1 >> 31) & 0x1) != 0; - int rawDataSize = (word1 >> 0) & 0x3ff; - int recvListFlags = (word1 >> 10) & 0xf; - bool hndDescEnable = ((word1 >> 31) & 0x1) != 0; - - if (hndDescEnable) - { - HandleDesc = new IpcHandleDesc(reader); - } - - for (int index = 0; index < ptrBuffCount; index++) - { - PtrBuff.Add(new IpcPtrBuffDesc(reader)); - } - - void ReadBuff(List<IpcBuffDesc> buff, int count) - { - for (int index = 0; index < count; index++) + if (hndDescEnable) { - buff.Add(new IpcBuffDesc(reader)); + HandleDesc = new IpcHandleDesc(reader); } - } - ReadBuff(SendBuff, sendBuffCount); - ReadBuff(ReceiveBuff, recvBuffCount); - ReadBuff(ExchangeBuff, xchgBuffCount); + PtrBuff = new List<IpcPtrBuffDesc>(ptrBuffCount); - rawDataSize *= 4; + for (int index = 0; index < ptrBuffCount; index++) + { + PtrBuff.Add(new IpcPtrBuffDesc(reader)); + } - long recvListPos = reader.BaseStream.Position + rawDataSize; + static List<IpcBuffDesc> ReadBuff(BinaryReader reader, int count) + { + List<IpcBuffDesc> buff = new List<IpcBuffDesc>(count); + + for (int index = 0; index < count; index++) + { + buff.Add(new IpcBuffDesc(reader)); + } + + return buff; + } + + SendBuff = ReadBuff(reader, sendBuffCount); + ReceiveBuff = ReadBuff(reader, recvBuffCount); + ExchangeBuff = ReadBuff(reader, xchgBuffCount); + + rawDataSize *= 4; + + long recvListPos = reader.BaseStream.Position + rawDataSize; // Only CMIF has the padding requirements. if (Type < IpcMessageType.TipcCloseSession) { long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr); - if (rawDataSize != 0) - { - rawDataSize -= (int)pad0; + if (rawDataSize != 0) + { + rawDataSize -= (int)pad0; + } + + reader.BaseStream.Seek(pad0, SeekOrigin.Current); } - reader.BaseStream.Seek(pad0, SeekOrigin.Current); - } + int recvListCount = recvListFlags - 2; - int recvListCount = recvListFlags - 2; + if (recvListCount == 0) + { + recvListCount = 1; + } + else if (recvListCount < 0) + { + recvListCount = 0; + } - if (recvListCount == 0) - { - recvListCount = 1; - } - else if (recvListCount < 0) - { - recvListCount = 0; - } + RawData = reader.ReadBytes(rawDataSize); - RawData = reader.ReadBytes(rawDataSize); + reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin); - reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin); + RecvListBuff = new List<IpcRecvListBuffDesc>(recvListCount); - for (int index = 0; index < recvListCount; index++) - { - RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64())); + for (int index = 0; index < recvListCount; index++) + { + RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64())); + } + + ObjectIds = new List<int>(0); } } diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs index 1af171b92..c0cd9ce99 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs @@ -71,7 +71,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { lock (_context.CriticalSection.Lock) { - _waitingObjects.RemoveAll(x => x.Object == schedulerObj); + for (int index = _waitingObjects.Count - 1; index >= 0; index--) + { + if (_waitingObjects[index].Object == schedulerObj) + { + _waitingObjects.RemoveAt(index); + } + } } } @@ -105,16 +111,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Common } else { - while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks <= next.TimePoint) + while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks < next.TimePoint) { + // Our time is close - don't let SpinWait go off and potentially Thread.Sleep(). if (spinWait.NextSpinWillYield) { Thread.Yield(); spinWait.Reset(); } - - spinWait.SpinOnce(); + else + { + spinWait.SpinOnce(); + } } spinWait.Reset(); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index c6467208e..3163c3487 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; using System; +using System.Buffers; using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall @@ -553,7 +554,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length]; + KSynchronizationObject[] syncObjsArray = ArrayPool<KSynchronizationObject>.Shared.Rent(handles.Length); + + Span<KSynchronizationObject> syncObjs = syncObjsArray.AsSpan(0, handles.Length); for (int index = 0; index < handles.Length; index++) { @@ -606,6 +609,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } } + ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray); + return result; } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs index 973d5f6a4..d42f90032 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.Horizon.Common; using System; +using System.Buffers; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Kernel.Threading @@ -59,7 +60,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } else { - LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length]; + LinkedListNode<KThread>[] syncNodesArray = ArrayPool<LinkedListNode<KThread>>.Shared.Rent(syncObjs.Length); + + Span<LinkedListNode<KThread>> syncNodes = syncNodesArray.AsSpan(0, syncObjs.Length); for (int index = 0; index < syncObjs.Length; index++) { @@ -101,6 +104,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading handleIndex = index; } } + + ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray); } _context.CriticalSection.Leave(); diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index 813210461..a137c4133 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; @@ -68,25 +69,29 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); - Memory<byte> output = new byte[outputSize]; - Memory<byte> performanceOutput = new byte[performanceOutputSize]; - - using MemoryHandle outputHandle = output.Pin(); - using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); - - ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); - - if (result == ResultCode.Success) + using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.Shared.RentCleared(outputSize)) + using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Shared.RentCleared(performanceOutputSize)) { - context.Memory.Write(outputPosition, output.Span); - context.Memory.Write(performanceOutputPosition, performanceOutput.Span); - } - else - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}"); - } + Memory<byte> output = outputOwner.Memory; + Memory<byte> performanceOutput = performanceOutputOwner.Memory; - return result; + using MemoryHandle outputHandle = output.Pin(); + using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); + + ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); + + if (result == ResultCode.Success) + { + context.Memory.Write(outputPosition, output.Span); + context.Memory.Write(performanceOutputPosition, performanceOutput.Span); + } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}"); + } + + return result; + } } [CommandCmif(5)] diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index f42fd0e21..048a68a9e 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -76,6 +76,8 @@ namespace Ryujinx.HLE.HOS.Services context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin); + context.Request.ObjectIds.EnsureCapacity(inputObjCount); + for (int index = 0; index < inputObjCount; index++) { context.Request.ObjectIds.Add(context.RequestData.ReadInt32()); diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 762b4fa44..b994679aa 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -217,6 +217,8 @@ namespace Ryujinx.HLE.HOS.Services if (noReceive) { + response.PtrBuff.EnsureCapacity(request.RecvListBuff.Count); + for (int i = 0; i < request.RecvListBuff.Count; i++) { ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2)); diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index 0724c83ee..42fc27612 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -1,7 +1,9 @@ -using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; using System; +using System.Buffers; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { @@ -83,16 +85,19 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); - Span<byte> outputParcel = new Span<byte>(new byte[replySize]); - - ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel); - - if (result == ResultCode.Success) + using (IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.Shared.RentCleared(replySize)) { - context.Memory.Write(replyPos, outputParcel); - } + Span<byte> outputParcel = outputParcelOwner.Memory.Span; + + ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel); - return result; + if (result == ResultCode.Success) + { + context.Memory.Write(replyPos, outputParcel); + } + + return result; + } } protected abstract ResultCode AdjustRefcount(int binderId, int addVal, int type); diff --git a/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/Ryujinx.Input.SDL2/SDL2Gamepad.cs index eec4e07e5..925526737 100644 --- a/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -12,17 +12,7 @@ namespace Ryujinx.Input.SDL2 { private bool HasConfiguration => _configuration != null; - private class ButtonMappingEntry - { - public readonly GamepadButtonInputId To; - public readonly GamepadButtonInputId From; - - public ButtonMappingEntry(GamepadButtonInputId to, GamepadButtonInputId from) - { - To = to; - From = from; - } - } + private record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From); private StandardControllerInputConfig _configuration; @@ -85,7 +75,7 @@ namespace Ryujinx.Input.SDL2 public SDL2Gamepad(IntPtr gamepadHandle, string driverId) { _gamepadHandle = gamepadHandle; - _buttonsUserMapping = new List<ButtonMappingEntry>(); + _buttonsUserMapping = new List<ButtonMappingEntry>(20); Name = SDL_GameControllerName(_gamepadHandle); Id = driverId; From c26aeefe039ff819f79196623a7e94a1908b7add Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 24 Apr 2023 04:08:31 +0200 Subject: [PATCH 465/737] Fix amiibo timeout issues & log errors/exceptions (#4712) --- .../UI/ViewModels/AmiiboWindowViewModel.cs | 26 ++++++++++++++----- Ryujinx/Ui/Windows/AmiiboWindow.cs | 20 ++++++++++---- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index 090c13a97..bb92798fa 100644 --- a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -7,6 +7,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Models.Amiibo; using System; @@ -42,13 +43,18 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _showAllAmiibo; private bool _useRandomUuid; private string _usage; - + private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) { _owner = owner; - _httpClient = new HttpClient { Timeout = TimeSpan.FromMilliseconds(5000) }; + + _httpClient = new HttpClient + { + Timeout = TimeSpan.FromSeconds(30) + }; + LastScannedAmiiboId = lastScannedAmiiboId; TitleId = titleId; @@ -89,9 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels { _showAllAmiibo = value; -#pragma warning disable 4014 ParseAmiiboData(); -#pragma warning restore 4014 OnPropertyChanged(); } @@ -203,8 +207,10 @@ namespace Ryujinx.Ava.UI.ViewModels { amiiboJsonString = await DownloadAmiiboJson(); } - catch + catch (Exception ex) { + Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}"); + ShowInfoDialog(); } } @@ -369,8 +375,10 @@ namespace Ryujinx.Ava.UI.ViewModels return false; } - catch + catch (Exception ex) { + Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}"); + ShowInfoDialog(); return false; @@ -393,6 +401,8 @@ namespace Ryujinx.Ava.UI.ViewModels return amiiboJsonString; } + Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}"); + await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle], LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage], LocaleManager.Instance[LocaleKeys.InputDialogOk], @@ -429,6 +439,10 @@ namespace Ryujinx.Ava.UI.ViewModels AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight)); } } + else + { + Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}"); + } } private void ResetAmiiboPreview() diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.cs b/Ryujinx/Ui/Windows/AmiiboWindow.cs index 470032373..11a566d8d 100644 --- a/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -1,6 +1,7 @@ using Gtk; using Ryujinx.Common; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Models.Amiibo; @@ -12,7 +13,6 @@ using System.Linq; using System.Net.Http; using System.Reflection; using System.Text; -using System.Text.Json; using System.Threading.Tasks; using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi; using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; @@ -57,7 +57,7 @@ namespace Ryujinx.Ui.Windows _httpClient = new HttpClient() { - Timeout = TimeSpan.FromMilliseconds(5000) + Timeout = TimeSpan.FromSeconds(30) }; Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); @@ -93,8 +93,10 @@ namespace Ryujinx.Ui.Windows { amiiboJsonString = await DownloadAmiiboJson(); } - catch + catch (Exception ex) { + Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}"); + ShowInfoDialog(); Close(); @@ -183,8 +185,10 @@ namespace Ryujinx.Ui.Windows return false; } - catch + catch (Exception ex) { + Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}"); + ShowInfoDialog(); return false; @@ -208,6 +212,8 @@ namespace Ryujinx.Ui.Windows } else { + Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}"); + GtkDialog.CreateInfoDialog($"Amiibo API", "An error occured while fetching information from the API."); Close(); @@ -233,6 +239,10 @@ namespace Ryujinx.Ui.Windows _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear); } + else + { + Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}"); + } } private void ShowInfoDialog() @@ -374,4 +384,4 @@ namespace Ryujinx.Ui.Windows base.Dispose(disposing); } } -} +} \ No newline at end of file From 3f98369a17a313c32e1a00cb9b81c6695ac76f27 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 24 Apr 2023 08:15:19 +0200 Subject: [PATCH 466/737] Set the console title for GTK again (#4706) Fixes a regression from #3707 where I accidentally removed that line. --- Ryujinx/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index dca87abcb..2e6ede444 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -96,6 +96,8 @@ namespace Ryujinx // Delete backup files after updating. Task.Run(Updater.CleanupUpdate); + Console.Title = $"Ryujinx Console {Version}"; + // NOTE: GTK3 doesn't init X11 in a multi threaded way. // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads). if (OperatingSystem.IsLinux()) From 4dd77316f722f6b429063784b56ddced7883ea1d Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 24 Apr 2023 03:34:38 -0300 Subject: [PATCH 467/737] Use vector transform feedback outputs with fragment shaders (#4708) * Use vector transform feedback outputs with fragment shaders * Shader cache version bump * Fix missing outputs when vector transform feedback outputs are used --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 44 ++++++++++++------- .../CodeGen/Glsl/OperandManager.cs | 2 +- .../CodeGen/Spirv/CodeGenContext.cs | 2 +- .../CodeGen/Spirv/Declarations.cs | 2 +- .../StructuredIr/StructuredProgram.cs | 2 +- .../Translation/ShaderConfig.cs | 11 +---- .../Translation/TranslatorContext.cs | 4 +- 9 files changed, 38 insertions(+), 33 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 48464f832..cad2341b2 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4703; + private const uint CodeGenVersion = 4707; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 11f7085d3..e1ab93278 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -449,7 +449,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (translatorContexts[i] != null) { translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute); - translatorContexts[i].SetLastInVertexPipeline(translatorContexts[5] != null); + translatorContexts[i].SetLastInVertexPipeline(); break; } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 9032ca59e..5e53d62a1 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -569,7 +569,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment) { - for (int c = 0; c < 4; c++) + int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; + int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); + + if (components > 1) + { + string type = components switch + { + 2 => "vec2", + 3 => "vec3", + 4 => "vec4", + _ => "float" + }; + + context.AppendLine($"layout (location = {attr}) in {type} {name};"); + } + + for (int c = components > 1 ? components : 0; c < 4; c++) { char swzMask = "xyzw"[c]; @@ -642,7 +658,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) { int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; - int components = context.Config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1; + int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); if (components > 1) { @@ -664,22 +680,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};"); } - else + + for (int c = components > 1 ? components : 0; c < 4; c++) { - for (int c = 0; c < 4; c++) + char swzMask = "xyzw"[c]; + + string xfb = string.Empty; + + var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4); + if (tfOutput.Valid) { - char swzMask = "xyzw"[c]; - - string xfb = string.Empty; - - var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4); - if (tfOutput.Valid) - { - xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; - } - - context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};"); + xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; } + + context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};"); } } else diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index ce1ab50e0..ec761fa66 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl ((config.LastInVertexPipeline && isOutAttr) || (config.Stage == ShaderStage.Fragment && !isOutAttr))) { - int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1; + int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}"; if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 41afdf188..e693307da 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -341,7 +341,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv attrOffset = attr; type = elemType; - if (Config.LastInPipeline && isOutAttr) + if (isOutAttr) { int components = Info.GetTransformFeedbackOutputComponents(attr); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index df42a13cb..fdca5e890 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -673,7 +673,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv int components = 1; var type = attrInfo.Type & AggregateType.ElementTypeMask; - if (context.Config.LastInPipeline && isOutAttr) + if (isOutAttr) { components = context.Info.GetTransformFeedbackOutputComponents(attr); diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index ec989cca5..b8d38fa65 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr context.LeaveFunction(); } - if (config.TransformFeedbackEnabled && config.LastInVertexPipeline) + if (config.TransformFeedbackEnabled && (config.LastInVertexPipeline || config.Stage == ShaderStage.Fragment)) { for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++) { diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 2caa8f638..15eb7ed1f 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -17,7 +17,6 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderStage Stage { get; } public bool GpPassthrough { get; } - public bool LastInPipeline { get; private set; } public bool LastInVertexPipeline { get; private set; } public bool HasLayerInputAttribute { get; private set; } @@ -145,7 +144,6 @@ namespace Ryujinx.Graphics.Shader.Translation OmapSampleMask = header.OmapSampleMask; OmapDepth = header.OmapDepth; TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); - LastInPipeline = true; LastInVertexPipeline = header.Stage < ShaderStage.Fragment; } @@ -253,13 +251,8 @@ namespace Ryujinx.Graphics.Shader.Translation GpLayerInputAttribute = attr; } - public void SetLastInVertexPipeline(bool hasFragment) + public void SetLastInVertexPipeline() { - if (!hasFragment) - { - LastInPipeline = true; - } - LastInVertexPipeline = true; } @@ -331,8 +324,6 @@ namespace Ryujinx.Graphics.Shader.Translation config._perPatchAttributeLocations = locationsMap; } - LastInPipeline = false; - // We don't consider geometry shaders using the geometry shader passthrough feature // as being the last because when this feature is used, it can't actually modify any of the outputs, // so the stage that comes before it is the last one that can do modifications. diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 3b88fdbab..856b16b7d 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -143,9 +143,9 @@ namespace Ryujinx.Graphics.Shader.Translation _config.SetGeometryShaderLayerInputAttribute(attr); } - public void SetLastInVertexPipeline(bool hasFragment) + public void SetLastInVertexPipeline() { - _config.SetLastInVertexPipeline(hasFragment); + _config.SetLastInVertexPipeline(); } public ShaderProgram Translate(TranslatorContext other = null) From db4242c5dcca2df90374f58ddeb3601c1eb06cce Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 24 Apr 2023 10:28:03 -0300 Subject: [PATCH 468/737] Implement DMA texture copy component shuffle (#4717) * Implement DMA texture copy component shuffle * Set UInt24 alignment to 1 --- Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs | 287 ++++++++++++++++---- 1 file changed, 234 insertions(+), 53 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index aa94f1f88..fd93cd8ba 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -6,6 +6,7 @@ using Ryujinx.Graphics.Texture; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Intrinsics; namespace Ryujinx.Graphics.Gpu.Engine.Dma @@ -32,6 +33,69 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma RemapEnable = 1 << 10 } + /// <summary> + /// Texture parameters for copy. + /// </summary> + private struct TextureParams + { + /// <summary> + /// Copy region X coordinate. + /// </summary> + public readonly int RegionX; + + /// <summary> + /// Copy region Y coordinate. + /// </summary> + public readonly int RegionY; + + /// <summary> + /// Offset from the base pointer of the data in memory. + /// </summary> + public readonly int BaseOffset; + + /// <summary> + /// Bytes per pixel. + /// </summary> + public readonly int Bpp; + + /// <summary> + /// Whether the texture is linear. If false, the texture is block linear. + /// </summary> + public readonly bool Linear; + + /// <summary> + /// Pixel offset from XYZ coordinates calculator. + /// </summary> + public readonly OffsetCalculator Calculator; + + /// <summary> + /// Creates texture parameters. + /// </summary> + /// <param name="regionX">Copy region X coordinate</param> + /// <param name="regionY">Copy region Y coordinate</param> + /// <param name="baseOffset">Offset from the base pointer of the data in memory</param> + /// <param name="bpp">Bytes per pixel</param> + /// <param name="linear">Whether the texture is linear. If false, the texture is block linear</param> + /// <param name="calculator">Pixel offset from XYZ coordinates calculator</param> + public TextureParams(int regionX, int regionY, int baseOffset, int bpp, bool linear, OffsetCalculator calculator) + { + RegionX = regionX; + RegionY = regionY; + BaseOffset = baseOffset; + Bpp = bpp; + Linear = linear; + Calculator = calculator; + } + } + + [StructLayout(LayoutKind.Sequential, Size = 3, Pack = 1)] + private struct UInt24 + { + public byte Byte0; + public byte Byte1; + public byte Byte2; + } + /// <summary> /// Creates a new instance of the DMA copy engine class. /// </summary> @@ -154,8 +218,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma { // Buffer to texture copy. int componentSize = (int)_state.State.SetRemapComponentsComponentSize + 1; - int srcBpp = remap ? ((int)_state.State.SetRemapComponentsNumSrcComponents + 1) * componentSize : 1; - int dstBpp = remap ? ((int)_state.State.SetRemapComponentsNumDstComponents + 1) * componentSize : 1; + int srcComponents = (int)_state.State.SetRemapComponentsNumSrcComponents + 1; + int dstComponents = (int)_state.State.SetRemapComponentsNumDstComponents + 1; + int srcBpp = remap ? srcComponents * componentSize : 1; + int dstBpp = remap ? dstComponents * componentSize : 1; var dst = Unsafe.As<uint, DmaTexture>(ref _state.State.SetDstBlockSize); var src = Unsafe.As<uint, DmaTexture>(ref _state.State.SetSrcBlockSize); @@ -274,63 +340,51 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma } } - unsafe bool Convert<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan) where T : unmanaged - { - if (srcLinear && dstLinear && srcBpp == dstBpp) - { - // Optimized path for purely linear copies - we don't need to calculate every single byte offset, - // and we can make use of Span.CopyTo which is very very fast (even compared to pointers) - for (int y = 0; y < yCount; y++) - { - srcCalculator.SetY(srcRegionY + y); - dstCalculator.SetY(dstRegionY + y); - int srcOffset = srcCalculator.GetOffset(srcRegionX); - int dstOffset = dstCalculator.GetOffset(dstRegionX); - srcSpan.Slice(srcOffset - srcBaseOffset, xCount * srcBpp) - .CopyTo(dstSpan.Slice(dstOffset - dstBaseOffset, xCount * dstBpp)); - } - } - else - { - fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan) - { - byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset. - byte* srcBase = srcPtr - srcBaseOffset; - - for (int y = 0; y < yCount; y++) - { - srcCalculator.SetY(srcRegionY + y); - dstCalculator.SetY(dstRegionY + y); - - for (int x = 0; x < xCount; x++) - { - int srcOffset = srcCalculator.GetOffset(srcRegionX + x); - int dstOffset = dstCalculator.GetOffset(dstRegionX + x); - - *(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset); - } - } - } - } - - return true; - } - // OPT: This allocates a (potentially) huge temporary array and then copies an existing // region of memory into it, data that might get overwritten entirely anyways. Ideally this should // all be rewritten to use pooled arrays, but that gets complicated with packed data and strides Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray(); - bool _ = srcBpp switch + TextureParams srcParams = new TextureParams(srcRegionX, srcRegionY, srcBaseOffset, srcBpp, srcLinear, srcCalculator); + TextureParams dstParams = new TextureParams(dstRegionX, dstRegionY, dstBaseOffset, dstBpp, dstLinear, dstCalculator); + + // If remapping is enabled, we always copy the components directly, in order. + // If it's enabled, but the mapping is just XYZW, we also copy them in order. + bool isIdentityRemap = !remap || + (_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.SrcX && + (dstComponents < 2 || _state.State.SetRemapComponentsDstY == SetRemapComponentsDst.SrcY) && + (dstComponents < 3 || _state.State.SetRemapComponentsDstZ == SetRemapComponentsDst.SrcZ) && + (dstComponents < 4 || _state.State.SetRemapComponentsDstW == SetRemapComponentsDst.SrcW)); + + if (isIdentityRemap) { - 1 => Convert<byte>(dstSpan, srcSpan), - 2 => Convert<ushort>(dstSpan, srcSpan), - 4 => Convert<uint>(dstSpan, srcSpan), - 8 => Convert<ulong>(dstSpan, srcSpan), - 12 => Convert<Bpp12Pixel>(dstSpan, srcSpan), - 16 => Convert<Vector128<byte>>(dstSpan, srcSpan), - _ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.") - }; + // The order of the components doesn't change, so we can just copy directly + // (with layout conversion if necessary). + + switch (srcBpp) + { + case 1: Copy<byte>(dstSpan, srcSpan, dstParams, srcParams); break; + case 2: Copy<ushort>(dstSpan, srcSpan, dstParams, srcParams); break; + case 4: Copy<uint>(dstSpan, srcSpan, dstParams, srcParams); break; + case 8: Copy<ulong>(dstSpan, srcSpan, dstParams, srcParams); break; + case 12: Copy<Bpp12Pixel>(dstSpan, srcSpan, dstParams, srcParams); break; + case 16: Copy<Vector128<byte>>(dstSpan, srcSpan, dstParams, srcParams); break; + default: throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format."); + } + } + else + { + // The order or value of the components might change. + + switch (componentSize) + { + case 1: CopyShuffle<byte>(dstSpan, srcSpan, dstParams, srcParams); break; + case 2: CopyShuffle<ushort>(dstSpan, srcSpan, dstParams, srcParams); break; + case 3: CopyShuffle<UInt24>(dstSpan, srcSpan, dstParams, srcParams); break; + case 4: CopyShuffle<uint>(dstSpan, srcSpan, dstParams, srcParams); break; + default: throw new NotSupportedException($"Unable to copy ${componentSize} component size."); + } + } memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, dstSpan); } @@ -372,6 +426,133 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma } } + /// <summary> + /// Copies data from one texture to another, while performing layout conversion if necessary. + /// </summary> + /// <typeparam name="T">Pixel type</typeparam> + /// <param name="dstSpan">Destination texture memory region</param> + /// <param name="srcSpan">Source texture memory region</param> + /// <param name="dst">Destination texture parameters</param> + /// <param name="src">Source texture parameters</param> + private unsafe void Copy<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan, TextureParams dst, TextureParams src) where T : unmanaged + { + int xCount = (int)_state.State.LineLengthIn; + int yCount = (int)_state.State.LineCount; + + if (src.Linear && dst.Linear && src.Bpp == dst.Bpp) + { + // Optimized path for purely linear copies - we don't need to calculate every single byte offset, + // and we can make use of Span.CopyTo which is very very fast (even compared to pointers) + for (int y = 0; y < yCount; y++) + { + src.Calculator.SetY(src.RegionY + y); + dst.Calculator.SetY(dst.RegionY + y); + int srcOffset = src.Calculator.GetOffset(src.RegionX); + int dstOffset = dst.Calculator.GetOffset(dst.RegionX); + srcSpan.Slice(srcOffset - src.BaseOffset, xCount * src.Bpp) + .CopyTo(dstSpan.Slice(dstOffset - dst.BaseOffset, xCount * dst.Bpp)); + } + } + else + { + fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan) + { + byte* dstBase = dstPtr - dst.BaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset. + byte* srcBase = srcPtr - src.BaseOffset; + + for (int y = 0; y < yCount; y++) + { + src.Calculator.SetY(src.RegionY + y); + dst.Calculator.SetY(dst.RegionY + y); + + for (int x = 0; x < xCount; x++) + { + int srcOffset = src.Calculator.GetOffset(src.RegionX + x); + int dstOffset = dst.Calculator.GetOffset(dst.RegionX + x); + + *(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset); + } + } + } + } + } + + /// <summary> + /// Sets texture pixel data to a constant value, while performing layout conversion if necessary. + /// </summary> + /// <typeparam name="T">Pixel type</typeparam> + /// <param name="dstSpan">Destination texture memory region</param> + /// <param name="dst">Destination texture parameters</param> + /// <param name="fillValue">Constant pixel value to be set</param> + private unsafe void Fill<T>(Span<byte> dstSpan, TextureParams dst, T fillValue) where T : unmanaged + { + int xCount = (int)_state.State.LineLengthIn; + int yCount = (int)_state.State.LineCount; + + fixed (byte* dstPtr = dstSpan) + { + byte* dstBase = dstPtr - dst.BaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset. + + for (int y = 0; y < yCount; y++) + { + dst.Calculator.SetY(dst.RegionY + y); + + for (int x = 0; x < xCount; x++) + { + int dstOffset = dst.Calculator.GetOffset(dst.RegionX + x); + + *(T*)(dstBase + dstOffset) = fillValue; + } + } + } + } + + /// <summary> + /// Copies data from one texture to another, while performing layout conversion and component shuffling if necessary. + /// </summary> + /// <typeparam name="T">Pixel type</typeparam> + /// <param name="dstSpan">Destination texture memory region</param> + /// <param name="srcSpan">Source texture memory region</param> + /// <param name="dst">Destination texture parameters</param> + /// <param name="src">Source texture parameters</param> + private void CopyShuffle<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan, TextureParams dst, TextureParams src) where T : unmanaged + { + int dstComponents = (int)_state.State.SetRemapComponentsNumDstComponents + 1; + + for (int i = 0; i < dstComponents; i++) + { + SetRemapComponentsDst componentsDst = i switch + { + 0 => _state.State.SetRemapComponentsDstX, + 1 => _state.State.SetRemapComponentsDstY, + 2 => _state.State.SetRemapComponentsDstZ, + _ => _state.State.SetRemapComponentsDstW + }; + + switch (componentsDst) + { + case SetRemapComponentsDst.SrcX: + Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan, dst, src); + break; + case SetRemapComponentsDst.SrcY: + Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>()), dst, src); + break; + case SetRemapComponentsDst.SrcZ: + Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>() * 2), dst, src); + break; + case SetRemapComponentsDst.SrcW: + Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>() * 3), dst, src); + break; + case SetRemapComponentsDst.ConstA: + Fill<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstA)); + break; + case SetRemapComponentsDst.ConstB: + Fill<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstB)); + break; + } + } + } + /// <summary> /// Copies block linear data with block linear GOBs to a block linear destination with linear GOBs. /// </summary> From 097562bc6c227c42f803ce1078fcb4adf06cd20c Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 25 Apr 2023 19:33:14 -0300 Subject: [PATCH 469/737] Add missing check for thread termination on ArbitrateLock (#4722) * Add missing check for thread termination on ArbitrateLock * Use TerminationRequested in all places where it can be used --- Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs | 6 ++---- .../HOS/Kernel/Threading/KAddressArbiter.cs | 16 ++++++++++------ .../HOS/Kernel/Threading/KConditionVariable.cs | 3 +-- .../HOS/Kernel/Threading/KSynchronization.cs | 5 ++--- Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | 12 ++++-------- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs index 9c2184d92..86469c03a 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs @@ -188,8 +188,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc if (request.AsyncEvent == null) { - if (request.ClientThread.ShallBeTerminated || - request.ClientThread.SchedFlags == ThreadSchedState.TerminationPending) + if (request.ClientThread.TerminationRequested) { return KernelResult.ThreadTerminating; } @@ -1104,8 +1103,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { foreach (KSessionRequest request in IterateWithRemovalOfAllRequests()) { - if (request.ClientThread.ShallBeTerminated || - request.ClientThread.SchedFlags == ThreadSchedState.TerminationPending) + if (request.ClientThread.TerminationRequested) { continue; } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index a5f9df5ef..74867b44e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -31,6 +31,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Enter(); + if (currentThread.TerminationRequested) + { + _context.CriticalSection.Leave(); + + return KernelResult.ThreadTerminating; + } + currentThread.SignaledObj = null; currentThread.ObjSyncResult = Result.Success; @@ -114,8 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading currentThread.SignaledObj = null; currentThread.ObjSyncResult = KernelResult.TimedOut; - if (currentThread.ShallBeTerminated || - currentThread.SchedFlags == ThreadSchedState.TerminationPending) + if (currentThread.TerminationRequested) { _context.CriticalSection.Leave(); @@ -280,8 +286,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Enter(); - if (currentThread.ShallBeTerminated || - currentThread.SchedFlags == ThreadSchedState.TerminationPending) + if (currentThread.TerminationRequested) { _context.CriticalSection.Leave(); @@ -351,8 +356,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Enter(); - if (currentThread.ShallBeTerminated || - currentThread.SchedFlags == ThreadSchedState.TerminationPending) + if (currentThread.TerminationRequested) { _context.CriticalSection.Leave(); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs index d146bff01..891e632f9 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs @@ -19,8 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading currentThread.WithholderNode = threadList.AddLast(currentThread); - if (currentThread.ShallBeTerminated || - currentThread.SchedFlags == ThreadSchedState.TerminationPending) + if (currentThread.TerminationRequested) { threadList.Remove(currentThread.WithholderNode); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs index d42f90032..9c196810c 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -47,8 +47,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KThread currentThread = KernelStatic.GetCurrentThread(); - if (currentThread.ShallBeTerminated || - currentThread.SchedFlags == ThreadSchedState.TerminationPending) + if (currentThread.TerminationRequested) { result = KernelResult.ThreadTerminating; } @@ -61,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading else { LinkedListNode<KThread>[] syncNodesArray = ArrayPool<LinkedListNode<KThread>>.Shared.Rent(syncObjs.Length); - + Span<LinkedListNode<KThread>> syncNodes = syncNodesArray.AsSpan(0, syncObjs.Length); for (int index = 0; index < syncObjs.Length; index++) diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 6fd496058..633964686 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -99,11 +99,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private int _shallBeTerminated; - public bool ShallBeTerminated - { - get => _shallBeTerminated != 0; - set => _shallBeTerminated = value ? 1 : 0; - } + private bool ShallBeTerminated => _shallBeTerminated != 0; public bool TerminationRequested => ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending; @@ -322,7 +318,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading ThreadSchedState result; - if (Interlocked.CompareExchange(ref _shallBeTerminated, 1, 0) == 0) + if (Interlocked.Exchange(ref _shallBeTerminated, 1) == 0) { if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.None) { @@ -470,7 +466,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KernelContext.CriticalSection.Enter(); - if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending) + if (TerminationRequested) { KernelContext.CriticalSection.Leave(); @@ -552,7 +548,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.InvalidState; } - if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending) + if (!TerminationRequested) { if (pause) { From 9f12e50a546b15533778ed0d8290202af91c10a2 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 25 Apr 2023 19:51:07 -0300 Subject: [PATCH 470/737] Refactor attribute handling on the shader generator (#4565) * Refactor attribute handling on the shader generator * Implement gl_ViewportMask[] * Add back the Intel FrontFacing bug workaround * Fix GLSL transform feedback outputs mistmatch with fragment stage * Shader cache version bump * Fix geometry shader recognition * PR feedback * Delete GetOperandDef and GetOperandUse * Remove replacements that are no longer needed on GLSL compilation on Vulkan * Fix incorrect load for per-patch outputs * Fix build --- Ryujinx.Graphics.GAL/Capabilities.cs | 9 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 4 +- Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 2 + Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 3 +- .../CodeGen/Glsl/Declarations.cs | 35 +- .../CodeGen/Glsl/GlslGenerator.cs | 16 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 20 +- .../Glsl/Instructions/InstGenHelper.cs | 4 +- .../Glsl/Instructions/InstGenMemory.cs | 158 ++++--- .../CodeGen/Glsl/Instructions/IoMap.cs | 145 +++++++ .../CodeGen/Glsl/OperandManager.cs | 345 +++------------- .../CodeGen/Spirv/CodeGenContext.cs | 255 +----------- .../CodeGen/Spirv/Declarations.cs | 386 +++++------------- .../CodeGen/Spirv/EnumConversion.cs | 3 +- .../CodeGen/Spirv/Instructions.cs | 241 ++++++++--- .../CodeGen/Spirv/IoMap.cs | 86 ++++ .../CodeGen/Spirv/ScalingHelpers.cs | 2 +- .../CodeGen/Spirv/SpirvGenerator.cs | 31 +- Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 5 +- Ryujinx.Graphics.Shader/IGpuAccessor.cs | 19 +- .../Instructions/AttributeMap.cs | 351 ++++++++++++++++ .../Instructions/InstEmitAttribute.cs | 148 ++++--- .../Instructions/InstEmitMemory.cs | 24 +- .../Instructions/InstEmitMove.cs | 36 +- .../IntermediateRepresentation/Instruction.cs | 11 +- .../IntermediateRepresentation/IoVariable.cs | 51 +++ .../OperandHelper.cs | 10 - .../IntermediateRepresentation/OperandType.cs | 10 - .../IntermediateRepresentation/Operation.cs | 18 + .../IntermediateRepresentation/StorageKind.cs | 39 ++ .../StructuredIr/AstOperation.cs | 10 +- .../StructuredIr/AstTextureOperation.cs | 2 +- .../StructuredIr/InstructionInfo.cs | 6 +- .../StructuredIr/IoDefinition.cs | 44 ++ .../StructuredIr/OperandInfo.cs | 2 - .../StructuredIr/StructuredProgram.cs | 77 ++-- .../StructuredIr/StructuredProgramContext.cs | 67 +-- .../StructuredIr/StructuredProgramInfo.cs | 49 +-- .../Translation/AttributeConsts.cs | 121 ++---- .../Translation/AttributeInfo.cs | 210 ---------- .../Translation/EmitterContext.cs | 98 +++-- .../Translation/EmitterContextInsts.cs | 109 +++-- .../Translation/GlobalMemory.cs | 9 +- .../Optimizations/GlobalToStorage.cs | 6 +- .../Translation/Optimizations/Optimizer.cs | 6 +- .../Translation/Rewriter.cs | 23 +- .../Translation/ShaderConfig.cs | 211 +++++++++- .../Translation/ShaderIdentifier.cs | 72 +++- .../Translation/Translator.cs | 57 ++- .../Translation/TranslatorContext.cs | 47 +-- .../HardwareCapabilities.cs | 3 + Ryujinx.Graphics.Vulkan/Shader.cs | 4 - .../VulkanInitialization.cs | 3 +- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 4 +- Ryujinx.ShaderTools/Program.cs | 4 +- 56 files changed, 1967 insertions(+), 1746 deletions(-) create mode 100644 Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs create mode 100644 Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs create mode 100644 Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs create mode 100644 Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs create mode 100644 Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs create mode 100644 Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs delete mode 100644 Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index bc4a02c97..a93d38466 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -35,7 +35,8 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsNonConstantTextureOffset; public readonly bool SupportsShaderBallot; public readonly bool SupportsTextureShadowLod; - public readonly bool SupportsViewportIndex; + public readonly bool SupportsViewportIndexVertexTessellation; + public readonly bool SupportsViewportMask; public readonly bool SupportsViewportSwizzle; public readonly bool SupportsIndirectParameters; @@ -80,7 +81,8 @@ namespace Ryujinx.Graphics.GAL bool supportsNonConstantTextureOffset, bool supportsShaderBallot, bool supportsTextureShadowLod, - bool supportsViewportIndex, + bool supportsViewportIndexVertexTessellation, + bool supportsViewportMask, bool supportsViewportSwizzle, bool supportsIndirectParameters, uint maximumUniformBuffersPerStage, @@ -121,7 +123,8 @@ namespace Ryujinx.Graphics.GAL SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; SupportsShaderBallot = supportsShaderBallot; SupportsTextureShadowLod = supportsTextureShadowLod; - SupportsViewportIndex = supportsViewportIndex; + SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation; + SupportsViewportMask = supportsViewportMask; SupportsViewportSwizzle = supportsViewportSwizzle; SupportsIndirectParameters = supportsIndirectParameters; MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage; diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index cad2341b2..78f9763fb 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4707; + private const uint CodeGenVersion = 4565; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index bbf2702e4..d35b8d921 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -144,7 +144,9 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod; - public bool QueryHostSupportsViewportIndex() => _context.Capabilities.SupportsViewportIndex; + public bool QueryHostSupportsViewportIndexVertexTessellation() => _context.Capabilities.SupportsViewportIndexVertexTessellation; + + public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask; /// <summary> /// Converts a packed Maxwell texture format to the shader translator texture format. diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index 846465260..bf365b4de 100644 --- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture")); private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot")); private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new Lazy<bool>(() => HasExtension("GL_ARB_shader_viewport_layer_array")); + private static readonly Lazy<bool> _supportsViewportArray2 = new Lazy<bool>(() => HasExtension("GL_NV_viewport_array2")); private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc")); private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc")); private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc")); @@ -65,6 +66,7 @@ namespace Ryujinx.Graphics.OpenGL public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value; public static bool SupportsShaderBallot => _supportsShaderBallot.Value; public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value; + public static bool SupportsViewportArray2 => _supportsViewportArray2.Value; public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value; public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value; public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value; diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 5a2e3fe4e..3903b4d4e 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -136,7 +136,8 @@ namespace Ryujinx.Graphics.OpenGL supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset, supportsShaderBallot: HwCapabilities.SupportsShaderBallot, supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, - supportsViewportIndex: HwCapabilities.SupportsShaderViewportLayerArray, + supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, + supportsViewportMask: HwCapabilities.SupportsViewportArray2, supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver? diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 5e53d62a1..81b79ec45 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -59,6 +59,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable"); } + if (context.Config.GpuAccessor.QueryHostSupportsViewportMask()) + { + context.AppendLine("#extension GL_NV_viewport_array2 : enable"); + } + context.AppendLine("#pragma optionNV(fastmath off)"); context.AppendLine(); @@ -215,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) { - var tfOutput = context.Info.GetTransformFeedbackOutput(AttributeConsts.PositionX); + var tfOutput = context.Config.GetTransformFeedbackOutput(AttributeConsts.PositionX); if (tfOutput.Valid) { context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex"); @@ -552,7 +557,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareInputAttribute(CodeGenContext context, StructuredProgramInfo info, int attr) { - string suffix = AttributeInfo.IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty; + string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty; string iq = string.Empty; if (context.Config.Stage == ShaderStage.Fragment) @@ -569,8 +574,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment) { - int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; - int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); + int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0); if (components > 1) { @@ -652,13 +656,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareOutputAttribute(CodeGenContext context, int attr) { - string suffix = AttributeInfo.IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty; + string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty; string name = $"{DefaultNames.OAttributePrefix}{attr}{suffix}"; if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) { - int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; - int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); + int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0); if (components > 1) { @@ -672,7 +675,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string xfb = string.Empty; - var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset); + var tfOutput = context.Config.GetTransformFeedbackOutput(attr, 0); if (tfOutput.Valid) { xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; @@ -687,7 +690,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string xfb = string.Empty; - var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4); + var tfOutput = context.Config.GetTransformFeedbackOutput(attr, c); if (tfOutput.Valid) { xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; @@ -726,6 +729,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};"); } + private static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr) + { + if (isOutAttr) + { + return stage == ShaderStage.TessellationControl; + } + else + { + return stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry; + } + } + private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs) { foreach (int attr in attrs.Order()) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 907275583..751d03507 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -1,5 +1,4 @@ using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; @@ -126,21 +125,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (node is AstAssignment assignment) { + AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination); AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source); - AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true); - - string dest; - - if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute()) - { - bool perPatch = operand.Type == OperandType.AttributePerPatch; - dest = OperandManager.GetOutAttributeName(context, operand.Value, perPatch); - } - else - { - dest = InstGen.GetExpression(context, assignment.Destination); - } + string dest = InstGen.GetExpression(context, assignment.Destination); string src = ReinterpretCast(context, assignment.Source, srcType, dstType); context.AppendLine(dest + " = " + src + ";"); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 9ca4618d3..01bd11e59 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // For shared memory access, the second argument is unused and should be ignored. // It is there to make both storage and shared access have the same number of arguments. // For storage, both inputs are consumed when the argument index is 0, so we should skip it here. - if (argIndex == 1 && (atomic || (inst & Instruction.MrMask) == Instruction.MrShared)) + if (argIndex == 1 && (atomic || operation.StorageKind == StorageKind.SharedMemory)) { continue; } @@ -85,14 +85,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (argIndex == 0 && atomic) { - Instruction memRegion = inst & Instruction.MrMask; - - switch (memRegion) + switch (operation.StorageKind) { - case Instruction.MrShared: args += LoadShared(context, operation); break; - case Instruction.MrStorage: args += LoadStorage(context, operation); break; + case StorageKind.SharedMemory: args += LoadShared(context, operation); break; + case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break; - default: throw new InvalidOperationException($"Invalid memory region \"{memRegion}\"."); + default: throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); } } else @@ -166,8 +164,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.ImageAtomic: return ImageLoadOrStore(context, operation); - case Instruction.LoadAttribute: - return LoadAttribute(context, operation); + case Instruction.Load: + return Load(context, operation); case Instruction.LoadConstant: return LoadConstant(context, operation); @@ -193,8 +191,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.PackHalf2x16: return PackHalf2x16(context, operation); - case Instruction.StoreAttribute: - return StoreAttribute(context, operation); + case Instruction.Store: + return Store(context, operation); case Instruction.StoreLocal: return StoreLocal(context, operation); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 743b695c8..00478f6ab 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.ImageStore, InstType.Special); Add(Instruction.ImageAtomic, InstType.Special); Add(Instruction.IsNan, InstType.CallUnary, "isnan"); - Add(Instruction.LoadAttribute, InstType.Special); + Add(Instruction.Load, InstType.Special); Add(Instruction.LoadConstant, InstType.Special); Add(Instruction.LoadLocal, InstType.Special); Add(Instruction.LoadShared, InstType.Special); @@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor); Add(Instruction.Sine, InstType.CallUnary, "sin"); Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); - Add(Instruction.StoreAttribute, InstType.Special); + Add(Instruction.Store, InstType.Special); Add(Instruction.StoreLocal, InstType.Special); Add(Instruction.StoreShared, InstType.Special); Add(Instruction.StoreShared16, InstType.Special); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index a5d2632ce..99519837b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -210,30 +210,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return texCallBuilder.ToString(); } - public static string LoadAttribute(CodeGenContext context, AstOperation operation) + public static string Load(CodeGenContext context, AstOperation operation) { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - IAstNode src3 = operation.GetSource(2); - - if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {nameof(Instruction.LoadAttribute)} must be a constant operand."); - } - - string indexExpr = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2)); - - if (src2 is AstOperand operand && operand.Type == OperandType.Constant) - { - int attrOffset = baseAttr.Value + (operand.Value << 2); - return OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: false, indexExpr); - } - else - { - string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true); - return OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: false, indexExpr); - } + return GenerateLoadOrStore(context, operation, isStore: false); } public static string LoadConstant(CodeGenContext context, AstOperation operation) @@ -337,33 +316,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}"; } - public static string StoreAttribute(CodeGenContext context, AstOperation operation) + public static string Store(CodeGenContext context, AstOperation operation) { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - IAstNode src3 = operation.GetSource(2); - - if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {nameof(Instruction.StoreAttribute)} must be a constant operand."); - } - - string attrName; - - if (src2 is AstOperand operand && operand.Type == OperandType.Constant) - { - int attrOffset = baseAttr.Value + (operand.Value << 2); - attrName = OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: true); - } - else - { - string attrExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - attrExpr = Enclose(attrExpr, src2, Instruction.ShiftRightS32, isLhs: true); - attrName = OperandManager.GetAttributeName(attrExpr, context.Config, isOutAttr: true); - } - - string value = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2)); - return $"{attrName} = {value}"; + return GenerateLoadOrStore(context, operation, isStore: true); } public static string StoreLocal(CodeGenContext context, AstOperation operation) @@ -847,6 +802,111 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } + private static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) + { + StorageKind storageKind = operation.StorageKind; + + string varName; + AggregateType varType; + int srcIndex = 0; + + switch (storageKind) + { + case StorageKind.Input: + case StorageKind.InputPerPatch: + case StorageKind.Output: + case StorageKind.OutputPerPatch: + if (!(operation.GetSource(srcIndex++) is 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.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + { + if (!(operation.GetSource(srcIndex++) is 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.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + { + component = elemIndex.Value; + srcIndex++; + } + } + + (varName, varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch); + + if (IoMap.IsPerVertexBuiltIn(context.Config.Stage, ioVariable, isOutput)) + { + // Since those exist both as input and output on geometry and tessellation shaders, + // we need the gl_in and gl_out prefixes to disambiguate. + + if (storageKind == StorageKind.Input) + { + string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32); + varName = $"gl_in[{expr}].{varName}"; + } + else if (storageKind == StorageKind.Output) + { + string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32); + varName = $"gl_out[{expr}].{varName}"; + } + } + + int firstSrcIndex = srcIndex; + int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount; + + 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 if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output) + { + // GLSL requires that for tessellation control shader outputs, + // that the index expression must be *exactly* "gl_InvocationID", + // otherwise the compilation fails. + // TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR. + varName += "[gl_InvocationID]"; + } + else + { + varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]"; + } + } + break; + + default: + throw new InvalidOperationException($"Invalid storage kind {storageKind}."); + } + + if (isStore) + { + varType &= AggregateType.ElementTypeMask; + varName = $"{varName} = {GetSoureExpr(context, operation.GetSource(srcIndex), varType)}"; + } + + return varName; + } + private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage) { string sbName = OperandManager.GetShaderStagePrefix(stage); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs new file mode 100644 index 000000000..093ee2322 --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs @@ -0,0 +1,145 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System.Globalization; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + static class IoMap + { + public static (string, AggregateType) GetGlslVariable( + ShaderConfig config, + IoVariable ioVariable, + int location, + int component, + bool isOutput, + bool isPerPatch) + { + return ioVariable switch + { + IoVariable.BackColorDiffuse => ("gl_BackColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.BackColorSpecular => ("gl_BackSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.BaseInstance => ("gl_BaseInstanceARB", AggregateType.S32), + IoVariable.BaseVertex => ("gl_BaseVertexARB", AggregateType.S32), + IoVariable.ClipDistance => ("gl_ClipDistance", AggregateType.Array | AggregateType.FP32), + IoVariable.CtaId => ("gl_WorkGroupID", AggregateType.Vector3 | AggregateType.U32), + IoVariable.DrawIndex => ("gl_DrawIDARB", AggregateType.S32), + IoVariable.FogCoord => ("gl_FogFragCoord", AggregateType.FP32), // Deprecated. + IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location), + IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32), + IoVariable.FragmentOutputIsBgra => (DefaultNames.SupportBlockIsBgraName, AggregateType.Array | AggregateType.Bool), + IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool), + IoVariable.InstanceId => ("gl_InstanceID", AggregateType.S32), + IoVariable.InstanceIndex => ("gl_InstanceIndex", AggregateType.S32), + IoVariable.InvocationId => ("gl_InvocationID", AggregateType.S32), + IoVariable.Layer => ("gl_Layer", AggregateType.S32), + IoVariable.PatchVertices => ("gl_PatchVerticesIn", AggregateType.S32), + IoVariable.PointCoord => ("gl_PointCoord", AggregateType.Vector2 | AggregateType.FP32), + IoVariable.PointSize => ("gl_PointSize", AggregateType.FP32), + IoVariable.Position => ("gl_Position", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.PrimitiveId => GetPrimitiveIdVariableName(config.Stage, isOutput), + IoVariable.SubgroupEqMask => GetSubgroupMaskVariableName(config, "Eq"), + IoVariable.SubgroupGeMask => GetSubgroupMaskVariableName(config, "Ge"), + IoVariable.SubgroupGtMask => GetSubgroupMaskVariableName(config, "Gt"), + IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config), + IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"), + IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"), + IoVariable.SupportBlockRenderScale => (DefaultNames.SupportBlockRenderScaleName, AggregateType.Array | AggregateType.FP32), + IoVariable.SupportBlockViewInverse => (DefaultNames.SupportBlockViewportInverse, AggregateType.Vector2 | AggregateType.FP32), + IoVariable.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32), + IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32), + IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", AggregateType.Array | AggregateType.FP32), + IoVariable.TextureCoord => ("gl_TexCoord", AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.ThreadId => ("gl_LocalInvocationID", AggregateType.Vector3 | AggregateType.U32), + IoVariable.ThreadKill => ("gl_HelperInvocation", AggregateType.Bool), + IoVariable.UserDefined => GetUserDefinedVariableName(config, location, component, isOutput, isPerPatch), + IoVariable.VertexId => ("gl_VertexID", AggregateType.S32), + IoVariable.VertexIndex => ("gl_VertexIndex", AggregateType.S32), + IoVariable.ViewportIndex => ("gl_ViewportIndex", AggregateType.S32), + IoVariable.ViewportMask => ("gl_ViewportMask", AggregateType.Array | AggregateType.S32), + _ => (null, AggregateType.Invalid) + }; + } + + public static bool IsPerVertexBuiltIn(ShaderStage stage, IoVariable ioVariable, bool isOutput) + { + switch (ioVariable) + { + case IoVariable.Layer: + case IoVariable.ViewportIndex: + case IoVariable.PointSize: + case IoVariable.Position: + case IoVariable.ClipDistance: + case IoVariable.PointCoord: + case IoVariable.ViewportMask: + if (isOutput) + { + return stage == ShaderStage.TessellationControl; + } + else + { + return stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry; + } + } + + return false; + } + + private static (string, AggregateType) GetFragmentOutputColorVariableName(ShaderConfig config, int location) + { + if (location < 0) + { + return (DefaultNames.OAttributePrefix, config.GetFragmentOutputColorType(0)); + } + + string name = DefaultNames.OAttributePrefix + location.ToString(CultureInfo.InvariantCulture); + + return (name, config.GetFragmentOutputColorType(location)); + } + + private static (string, AggregateType) GetPrimitiveIdVariableName(ShaderStage stage, bool isOutput) + { + // The geometry stage has an additional gl_PrimitiveIDIn variable. + return (isOutput || stage != ShaderStage.Geometry ? "gl_PrimitiveID" : "gl_PrimitiveIDIn", AggregateType.S32); + } + + private static (string, AggregateType) GetSubgroupMaskVariableName(ShaderConfig config, string cc) + { + return config.GpuAccessor.QueryHostSupportsShaderBallot() + ? ($"unpackUint2x32(gl_SubGroup{cc}MaskARB)", AggregateType.Vector2 | AggregateType.U32) + : ($"gl_Subgroup{cc}Mask", AggregateType.Vector4 | AggregateType.U32); + } + + private static (string, AggregateType) GetSubgroupInvocationIdVariableName(ShaderConfig config) + { + return config.GpuAccessor.QueryHostSupportsShaderBallot() + ? ("gl_SubGroupInvocationARB", AggregateType.U32) + : ("gl_SubgroupInvocationID", AggregateType.U32); + } + + private static (string, AggregateType) GetUserDefinedVariableName(ShaderConfig config, int location, int component, bool isOutput, bool isPerPatch) + { + string name = isPerPatch + ? DefaultNames.PerPatchAttributePrefix + : (isOutput ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix); + + if (location < 0) + { + return (name, config.GetUserDefinedType(0, isOutput)); + } + + name += location.ToString(CultureInfo.InvariantCulture); + + if (config.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput)) + { + name += "_" + "xyzw"[component & 3]; + } + + return (name, config.GetUserDefinedType(location, isOutput)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index ec761fa66..92e833580 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -1,10 +1,10 @@ +using Ryujinx.Graphics.Shader.CodeGen.Glsl.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 System.Numerics; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -12,82 +12,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { class OperandManager { - private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; - - private readonly struct BuiltInAttribute - { - public string Name { get; } - - public AggregateType Type { get; } - - public BuiltInAttribute(string name, AggregateType type) - { - Name = name; - Type = type; - } - } - - private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>() - { - { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", AggregateType.S32) }, - { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", AggregateType.FP32) }, - { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", AggregateType.FP32) }, - { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", AggregateType.FP32) }, - { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", AggregateType.FP32) }, - { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", AggregateType.FP32) }, - { AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", AggregateType.FP32) }, - { AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", AggregateType.FP32) }, - { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", AggregateType.FP32) }, - { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", AggregateType.FP32) }, - { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", AggregateType.FP32) }, - { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", AggregateType.FP32) }, - { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", AggregateType.S32) }, - { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", AggregateType.S32) }, - { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", AggregateType.S32) }, - { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", AggregateType.S32) }, - { AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", AggregateType.S32) }, - { AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", AggregateType.S32) }, - { AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", AggregateType.S32) }, - { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", AggregateType.Bool) }, - - // Special. - { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", AggregateType.FP32) }, - { AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", AggregateType.Bool) }, - { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", AggregateType.U32) }, - { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", AggregateType.U32) }, - { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", AggregateType.U32) }, - { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", AggregateType.U32) }, - { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", AggregateType.U32) }, - { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", AggregateType.U32) }, - { AttributeConsts.LaneId, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", AggregateType.S32) }, - { AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", AggregateType.S32) }, - { AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", AggregateType.S32) }, - { AttributeConsts.EqMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.GeMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.GtMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.LeMask, new BuiltInAttribute(null, AggregateType.U32) }, - { AttributeConsts.LtMask, new BuiltInAttribute(null, AggregateType.U32) }, - - // Support uniforms. - { AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", AggregateType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", AggregateType.Bool) }, - - { AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", AggregateType.FP32) }, - { AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", AggregateType.FP32) } - }; + private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private Dictionary<AstOperand, string> _locals; @@ -110,8 +35,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return operand.Type switch { OperandType.Argument => GetArgumentName(operand.Value), - OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false), - OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config), OperandType.LocalVariable => _locals[operand], @@ -155,177 +78,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); } - public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch) - { - return GetAttributeName(context, value, perPatch, isOutAttr: true); - } - - public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0") - { - ShaderConfig config = context.Config; - - if ((value & AttributeConsts.LoadOutputMask) != 0) - { - isOutAttr = true; - } - - value &= AttributeConsts.Mask & ~3; - char swzMask = GetSwizzleMask((value >> 2) & 3); - - if (perPatch) - { - if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd) - { - value -= AttributeConsts.UserAttributePerPatchBase; - - return $"{DefaultNames.PerPatchAttributePrefix}{(value >> 4)}.{swzMask}"; - } - else if (value < AttributeConsts.UserAttributePerPatchBase) - { - return value switch - { - AttributeConsts.TessLevelOuter0 => "gl_TessLevelOuter[0]", - AttributeConsts.TessLevelOuter1 => "gl_TessLevelOuter[1]", - AttributeConsts.TessLevelOuter2 => "gl_TessLevelOuter[2]", - AttributeConsts.TessLevelOuter3 => "gl_TessLevelOuter[3]", - AttributeConsts.TessLevelInner0 => "gl_TessLevelInner[0]", - AttributeConsts.TessLevelInner1 => "gl_TessLevelInner[1]", - _ => null - }; - } - } - else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) - { - int attrOffset = value; - value -= AttributeConsts.UserAttributeBase; - - string prefix = isOutAttr - ? DefaultNames.OAttributePrefix - : DefaultNames.IAttributePrefix; - - bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing); - - if (indexable) - { - string name = prefix; - - if (config.Stage == ShaderStage.Geometry && !isOutAttr) - { - name += $"[{indexExpr}]"; - } - - return name + $"[{(value >> 4)}]." + swzMask; - } - else if (config.TransformFeedbackEnabled && - ((config.LastInVertexPipeline && isOutAttr) || - (config.Stage == ShaderStage.Fragment && !isOutAttr))) - { - int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset); - string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}"; - - if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) - { - name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; - } - - return components > 1 ? name + '.' + swzMask : name; - } - else - { - string name = $"{prefix}{(value >> 4)}"; - - if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) - { - name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; - } - - return name + '.' + swzMask; - } - } - else - { - if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) - { - value -= AttributeConsts.FragmentOutputColorBase; - - return $"{DefaultNames.OAttributePrefix}{(value >> 4)}.{swzMask}"; - } - else if (_builtInAttributes.TryGetValue(value, out BuiltInAttribute builtInAttr)) - { - string subgroupMask = value switch - { - AttributeConsts.EqMask => "Eq", - AttributeConsts.GeMask => "Ge", - AttributeConsts.GtMask => "Gt", - AttributeConsts.LeMask => "Le", - AttributeConsts.LtMask => "Lt", - _ => null - }; - - if (subgroupMask != null) - { - return config.GpuAccessor.QueryHostSupportsShaderBallot() - ? $"unpackUint2x32(gl_SubGroup{subgroupMask}MaskARB).x" - : $"gl_Subgroup{subgroupMask}Mask.x"; - } - else if (value == AttributeConsts.LaneId) - { - return config.GpuAccessor.QueryHostSupportsShaderBallot() - ? "gl_SubGroupInvocationARB" - : "gl_SubgroupInvocationID"; - } - - if (config.Stage == ShaderStage.Fragment) - { - // TODO: There must be a better way to handle this... - switch (value) - { - case AttributeConsts.PositionX: return $"(gl_FragCoord.x / {DefaultNames.SupportBlockRenderScaleName}[0])"; - case AttributeConsts.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])"; - case AttributeConsts.PositionZ: return "gl_FragCoord.z"; - case AttributeConsts.PositionW: return "gl_FragCoord.w"; - - case AttributeConsts.FrontFacing: - if (config.GpuAccessor.QueryHostHasFrontFacingBug()) - { - // This is required for Intel on Windows, gl_FrontFacing sometimes returns incorrect - // (flipped) values. Doing this seems to fix it. - return "(-floatBitsToInt(float(gl_FrontFacing)) < 0)"; - } - break; - } - } - - string name = builtInAttr.Name; - - if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value)) - { - name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}"; - } - - return name; - } - } - - // TODO: Warn about unknown built-in attribute. - - return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0"; - } - - public static string GetAttributeName(string attrExpr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0") - { - string name = isOutAttr - ? DefaultNames.OAttributePrefix - : DefaultNames.IAttributePrefix; - - if (config.Stage == ShaderStage.Geometry && !isOutAttr) - { - name += $"[{indexExpr}]"; - } - - return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]"; - } - public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable) { if (cbIndexable) @@ -387,12 +139,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { int index = (int)stage; - if ((uint)index >= StagePrefixes.Length) + if ((uint)index >= _stagePrefixes.Length) { return "invalid"; } - return StagePrefixes[index]; + return _stagePrefixes[index]; } private static char GetSwizzleMask(int value) @@ -405,24 +157,54 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; } - public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false) + public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node) { + // TODO: Get rid of that function entirely and return the type from the operation generation + // functions directly, like SPIR-V does. + if (node is AstOperation operation) { - if (operation.Inst == Instruction.LoadAttribute) + if (operation.Inst == Instruction.Load) { - // Load attribute basically just returns the attribute value. - // Some built-in attributes may have different types, so we need - // to return the type based on the attribute that is being read. - if (operation.GetSource(0) is AstOperand operand && operand.Type == OperandType.Constant) + switch (operation.StorageKind) { - if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) - { - return builtInAttr.Type; - } - } + case StorageKind.Input: + case StorageKind.InputPerPatch: + case StorageKind.Output: + case StorageKind.OutputPerPatch: + if (!(operation.GetSource(0) is AstOperand varId) || varId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } - return OperandInfo.GetVarType(OperandType.Attribute); + 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.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + { + if (!(operation.GetSource(1) is 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.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + { + component = elemIndex.Value; + } + } + + (_, AggregateType varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch); + + return varType & AggregateType.ElementTypeMask; + } } else if (operation.Inst == Instruction.Call) { @@ -461,45 +243,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return context.CurrentFunction.GetArgumentType(argIndex); } - return GetOperandVarType(context, operand, isAsgDest); + return OperandInfo.GetVarType(operand); } else { throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); } } - - private static AggregateType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false) - { - if (operand.Type == OperandType.Attribute) - { - if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) - { - return builtInAttr.Type; - } - else if (context.Config.Stage == ShaderStage.Vertex && !isAsgDest && - operand.Value >= AttributeConsts.UserAttributeBase && - operand.Value < AttributeConsts.UserAttributeEnd) - { - int location = (operand.Value - AttributeConsts.UserAttributeBase) / 16; - - AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location); - - return type.ToAggregateType(); - } - else if (context.Config.Stage == ShaderStage.Fragment && isAsgDest && - operand.Value >= AttributeConsts.FragmentOutputColorBase && - operand.Value < AttributeConsts.FragmentOutputColorEnd) - { - int location = (operand.Value - AttributeConsts.FragmentOutputColorBase) / 16; - - AttributeType type = context.Config.GpuAccessor.QueryFragmentOutputType(location); - - return type.ToAggregateType(); - } - } - - return OperandInfo.GetVarType(operand); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index e693307da..ed292ef1a 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -29,15 +29,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Instruction StorageBuffersArray { get; set; } public Instruction LocalMemory { get; set; } public Instruction SharedMemory { get; set; } - public Instruction InputsArray { get; set; } - public Instruction OutputsArray { get; set; } public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>(); public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>(); public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>(); - public Dictionary<int, Instruction> Inputs { get; } = new Dictionary<int, Instruction>(); - public Dictionary<int, Instruction> Outputs { get; } = new Dictionary<int, Instruction>(); - public Dictionary<int, Instruction> InputsPerPatch { get; } = new Dictionary<int, Instruction>(); - public Dictionary<int, Instruction> OutputsPerPatch { get; } = new Dictionary<int, Instruction>(); + public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>(); + public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>(); + public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); + public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); public Instruction CoordTemp { get; set; } private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>(); @@ -163,16 +161,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv mainInterface.AddRange(InputsPerPatch.Values); mainInterface.AddRange(OutputsPerPatch.Values); - if (InputsArray != null) - { - mainInterface.Add(InputsArray); - } - - if (OutputsArray != null) - { - mainInterface.Add(OutputsArray); - } - return mainInterface.ToArray(); } @@ -228,8 +216,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return operand.Type switch { IrOperandType.Argument => GetArgument(type, operand), - IrOperandType.Attribute => GetAttribute(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0), - IrOperandType.AttributePerPatch => GetAttributePerPatch(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0), IrOperandType.Constant => GetConstant(type, operand), IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand), IrOperandType.LocalVariable => GetLocal(type, operand), @@ -275,239 +261,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv }; } - public Instruction GetAttributeElemPointer(int attr, bool isOutAttr, Instruction index, out AggregateType elemType) - { - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrInfo = AttributeInfo.From(Config, attr, isOutAttr); - - int attrOffset = attrInfo.BaseValue; - AggregateType type = attrInfo.Type; - - Instruction ioVariable, elemIndex; - - Instruction invocationId = null; - - if (Config.Stage == ShaderStage.TessellationControl && isOutAttr) - { - invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]); - } - - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; - - if (isUserAttr && - ((!isOutAttr && Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) || - (isOutAttr && Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing)))) - { - elemType = AggregateType.FP32; - ioVariable = isOutAttr ? OutputsArray : InputsArray; - elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); - var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4); - - bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr); - - if (invocationId != null && isArray) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex); - } - else if (invocationId != null) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex); - } - else if (isArray) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex); - } - else - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, vecIndex, elemIndex); - } - } - - bool isViewportInverse = attr == AttributeConsts.SupportBlockViewInverseX || attr == AttributeConsts.SupportBlockViewInverseY; - - if (isViewportInverse) - { - elemType = AggregateType.FP32; - elemIndex = Constant(TypeU32(), (attr - AttributeConsts.SupportBlockViewInverseX) >> 2); - return AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), SupportBuffer, Constant(TypeU32(), 2), elemIndex); - } - - elemType = attrInfo.Type & AggregateType.ElementTypeMask; - - if (isUserAttr && Config.TransformFeedbackEnabled && - ((isOutAttr && Config.LastInVertexPipeline) || - (!isOutAttr && Config.Stage == ShaderStage.Fragment))) - { - attrOffset = attr; - type = elemType; - - if (isOutAttr) - { - int components = Info.GetTransformFeedbackOutputComponents(attr); - - if (components > 1) - { - attrOffset &= ~0xf; - type = components switch - { - 2 => AggregateType.Vector2 | AggregateType.FP32, - 3 => AggregateType.Vector3 | AggregateType.FP32, - 4 => AggregateType.Vector4 | AggregateType.FP32, - _ => AggregateType.FP32 - }; - - attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false); - } - } - } - - ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset]; - - bool isIndexed = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)); - - if ((type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0) - { - if (invocationId != null) - { - return isIndexed - ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index) - : AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId); - } - else - { - return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable; - } - } - - elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); - - if (invocationId != null && isIndexed) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, elemIndex); - } - else if (invocationId != null) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, elemIndex); - } - else if (isIndexed) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex); - } - else - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex); - } - } - - public Instruction GetAttributeElemPointer(Instruction attrIndex, bool isOutAttr, Instruction index, out AggregateType elemType) - { - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - - Instruction invocationId = null; - - if (Config.Stage == ShaderStage.TessellationControl && isOutAttr) - { - invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]); - } - - elemType = AggregateType.FP32; - var ioVariable = isOutAttr ? OutputsArray : InputsArray; - var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2)); - var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3)); - - bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr); - - if (invocationId != null && isArray) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex); - } - else if (invocationId != null) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex); - } - else if (isArray) - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex); - } - else - { - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, vecIndex, elemIndex); - } - } - - public Instruction GetAttribute(AggregateType type, int attr, bool isOutAttr, Instruction index = null) - { - if (!AttributeInfo.Validate(Config, attr, isOutAttr: false)) - { - return GetConstant(type, new AstOperand(IrOperandType.Constant, 0)); - } - - var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType); - var value = Load(GetType(elemType), elemPointer); - - if (Config.Stage == ShaderStage.Fragment) - { - if (attr == AttributeConsts.PositionX || attr == AttributeConsts.PositionY) - { - var pointerType = TypePointer(StorageClass.Uniform, TypeFP32()); - var fieldIndex = Constant(TypeU32(), 4); - var scaleIndex = Constant(TypeU32(), 0); - - var scaleElemPointer = AccessChain(pointerType, SupportBuffer, fieldIndex, scaleIndex); - var scale = Load(TypeFP32(), scaleElemPointer); - - value = FDiv(TypeFP32(), value, scale); - } - else if (attr == AttributeConsts.FrontFacing && Config.GpuAccessor.QueryHostHasFrontFacingBug()) - { - // Workaround for what appears to be a bug on Intel compiler. - var valueFloat = Select(TypeFP32(), value, Constant(TypeFP32(), 1f), Constant(TypeFP32(), 0f)); - var valueAsInt = Bitcast(TypeS32(), valueFloat); - var valueNegated = SNegate(TypeS32(), valueAsInt); - - value = SLessThan(TypeBool(), valueNegated, Constant(TypeS32(), 0)); - } - } - - return BitcastIfNeeded(type, elemType, value); - } - - public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType) - { - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrInfo = AttributeInfo.FromPatch(Config, attr, isOutAttr); - - int attrOffset = attrInfo.BaseValue; - Instruction ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset]; - - elemType = attrInfo.Type & AggregateType.ElementTypeMask; - - if ((attrInfo.Type & (AggregateType.Array | AggregateType.ElementCountMask)) == 0) - { - return ioVariable; - } - - var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); - return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex); - } - - public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr) - { - if (!AttributeInfo.ValidatePerPatch(Config, attr, isOutAttr: false)) - { - return GetConstant(type, new AstOperand(IrOperandType.Constant, 0)); - } - - var elemPointer = GetAttributePerPatchElemPointer(attr, isOutAttr, out var elemType); - return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer)); - } - - public Instruction GetAttribute(AggregateType type, Instruction attr, bool isOutAttr, Instruction index = null) - { - var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType); - return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer)); - } - public Instruction GetConstant(AggregateType type, AstOperand operand) { return type switch diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index fdca5e890..821da4773 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -1,21 +1,19 @@ using Ryujinx.Common; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using Spv.Generator; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Numerics; using static Spv.Specification; +using SpvInstruction = Spv.Generator.Instruction; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { static class Declarations { - // At least 16 attributes are guaranteed by the spec. - public const int MaxAttributes = 16; - private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; public static void DeclareParameters(CodeGenContext context, StructuredFunction function) @@ -59,7 +57,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { StructuredFunction function = functions[funcIndex]; - Instruction[] locals = new Instruction[function.InArguments.Length]; + SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length]; for (int i = 0; i < function.InArguments.Length; i++) { @@ -105,10 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors()); DeclareSamplers(context, context.Config.GetTextureDescriptors()); DeclareImages(context, context.Config.GetImageDescriptors()); - DeclareInputAttributes(context, info, perPatch: false); - DeclareOutputAttributes(context, info, perPatch: false); - DeclareInputAttributes(context, info, perPatch: true); - DeclareOutputAttributes(context, info, perPatch: true); + DeclareInputsAndOutputs(context, info); } private static void DeclareLocalMemory(CodeGenContext context, int size) @@ -121,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size); } - private static Instruction DeclareMemory(CodeGenContext context, StorageClass storage, int size) + private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size) { var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size)); var pointerType = context.TypePointer(storage, arrayType); @@ -395,164 +390,104 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv }; } - private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) + private static void DeclareInputsAndOutputs(CodeGenContext context, StructuredProgramInfo info) { - bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing); - - if (iaIndexing && !perPatch) + foreach (var ioDefinition in info.IoDefinitions) { - var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); + var ioVariable = ioDefinition.IoVariable; - if (context.Config.Stage == ShaderStage.Geometry) - { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices)); - } - - var spvType = context.TypePointer(StorageClass.Input, attrType); - var spvVar = context.Variable(spvType, StorageClass.Input); - - if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) - { - context.Decorate(spvVar, Decoration.PassthroughNV); - } - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); - - context.AddGlobalVariable(spvVar); - context.InputsArray = spvVar; - } - - var inputs = perPatch ? info.InputsPerPatch : info.Inputs; - - foreach (int attr in inputs) - { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch)) + // Those are actually from constant buffer, rather than being actual inputs or outputs, + // so we must ignore them here as they are declared as part of the support buffer. + // TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input"). + if (ioVariable == IoVariable.FragmentOutputIsBgra || + ioVariable == IoVariable.SupportBlockRenderScale || + ioVariable == IoVariable.SupportBlockViewInverse) { continue; } - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; - - if (iaIndexing && isUserAttr && !perPatch) - { - continue; - } + bool isOutput = ioDefinition.StorageKind.IsOutput(); + bool isPerPatch = ioDefinition.StorageKind.IsPerPatch(); PixelImap iq = PixelImap.Unused; if (context.Config.Stage == ShaderStage.Fragment) { - if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) + if (ioVariable == IoVariable.UserDefined) { - iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); + iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType(); } else { - AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false); - AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask; + (_, AggregateType varType) = IoMap.GetSpirvBuiltIn(ioVariable); + AggregateType elemType = varType & AggregateType.ElementTypeMask; - if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32)) + if (elemType == AggregateType.S32 || elemType == AggregateType.U32) { iq = PixelImap.Constant; } } } - DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq); + DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq); } } - private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) + private static void DeclareInputOrOutput(CodeGenContext context, IoDefinition ioDefinition, bool isOutput, bool isPerPatch, PixelImap iq = PixelImap.Unused) { - bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing); + IoVariable ioVariable = ioDefinition.IoVariable; + var storageClass = isOutput ? StorageClass.Output : StorageClass.Input; - if (oaIndexing && !perPatch) + bool isBuiltIn; + BuiltIn builtIn = default; + AggregateType varType; + + if (ioVariable == IoVariable.UserDefined) { - var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); + varType = context.Config.GetUserDefinedType(ioDefinition.Location, isOutput); + isBuiltIn = false; + } + else if (ioVariable == IoVariable.FragmentOutputColor) + { + varType = context.Config.GetFragmentOutputColorType(ioDefinition.Location); + isBuiltIn = false; + } + else + { + (builtIn, varType) = IoMap.GetSpirvBuiltIn(ioVariable); + isBuiltIn = true; - if (context.Config.Stage == ShaderStage.TessellationControl) + if (varType == AggregateType.Invalid) { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + throw new InvalidOperationException($"Unknown variable {ioVariable}."); } - - var spvType = context.TypePointer(StorageClass.Output, attrType); - var spvVar = context.Variable(spvType, StorageClass.Output); - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0); - - context.AddGlobalVariable(spvVar); - context.OutputsArray = spvVar; } - var outputs = perPatch ? info.OutputsPerPatch : info.Outputs; + bool hasComponent = context.Config.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput); - foreach (int attr in outputs) + if (hasComponent) { - if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch)) + varType &= AggregateType.ElementTypeMask; + } + else if (ioVariable == IoVariable.UserDefined && context.Config.HasTransformFeedbackOutputs(isOutput)) + { + varType &= AggregateType.ElementTypeMask; + varType |= context.Config.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch { - continue; - } - - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; - - if (oaIndexing && isUserAttr && !perPatch) - { - continue; - } - - DeclareOutputAttribute(context, attr, perPatch); + 2 => AggregateType.Vector2, + 3 => AggregateType.Vector3, + 4 => AggregateType.Vector4, + _ => AggregateType.Invalid + }; } - if (context.Config.Stage == ShaderStage.Vertex) - { - DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false); - } - } - - private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch) - { - DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true); - } - - public static void DeclareInvocationId(CodeGenContext context) - { - DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false); - } - - private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool perPatch, bool isOutAttr, PixelImap iq = PixelImap.Unused) - { - bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; - if (isUserAttr && context.Config.TransformFeedbackEnabled && !perPatch && - ((isOutAttr && context.Config.LastInVertexPipeline) || - (!isOutAttr && context.Config.Stage == ShaderStage.Fragment))) - { - DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq); - return; - } - - var dict = perPatch - ? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch) - : (isOutAttr ? context.Outputs : context.Inputs); - - var attrInfo = perPatch - ? AttributeInfo.FromPatch(context.Config, attr, isOutAttr) - : AttributeInfo.From(context.Config, attr, isOutAttr); - - if (dict.ContainsKey(attrInfo.BaseValue)) - { - return; - } - - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrType = context.GetType(attrInfo.Type, attrInfo.Length); + var spvType = context.GetType(varType, IoMap.GetSpirvBuiltInArrayLength(ioVariable)); bool builtInPassthrough = false; - if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && !perPatch && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) + if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Config.Stage, isOutput)) { int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32; - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); + spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { @@ -560,69 +495,64 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch) + if (context.Config.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch) { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); } - var spvType = context.TypePointer(storageClass, attrType); - var spvVar = context.Variable(spvType, storageClass); + var spvPointerType = context.TypePointer(storageClass, spvType); + var spvVar = context.Variable(spvPointerType, storageClass); if (builtInPassthrough) { context.Decorate(spvVar, Decoration.PassthroughNV); } - if (attrInfo.IsBuiltin) + if (isBuiltIn) { - if (perPatch) + if (isPerPatch) { context.Decorate(spvVar, Decoration.Patch); } - if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment) + if (context.Config.GpuAccessor.QueryHostReducedPrecision() && ioVariable == IoVariable.Position) { context.Decorate(spvVar, Decoration.Invariant); } - context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); - - if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) - { - var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue); - if (tfOutput.Valid) - { - context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); - context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride); - context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset); - } - } + context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)builtIn); } - else if (perPatch) + else if (isPerPatch) { context.Decorate(spvVar, Decoration.Patch); - int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16); + if (ioVariable == IoVariable.UserDefined) + { + int location = context.Config.GetPerPatchAttributeLocation(ioDefinition.Location); - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } } - else if (isUserAttr) + else if (ioVariable == IoVariable.UserDefined) { - int location = (attr - AttributeConsts.UserAttributeBase) / 16; + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)ioDefinition.Location); - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + if (hasComponent) + { + context.Decorate(spvVar, Decoration.Component, (LiteralInteger)ioDefinition.Component); + } - if (!isOutAttr && - !perPatch && - (context.Config.PassthroughAttributes & (1 << location)) != 0 && + if (!isOutput && + !isPerPatch && + (context.Config.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { context.Decorate(spvVar, Decoration.PassthroughNV); } } - else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) + else if (ioVariable == IoVariable.FragmentOutputColor) { - int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16; + int location = ioDefinition.Location; if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) { @@ -646,7 +576,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - if (!isOutAttr) + if (!isOutput) { switch (iq) { @@ -658,143 +588,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } } - - context.AddGlobalVariable(spvVar); - dict.Add(attrInfo.BaseValue, spvVar); - } - - private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused) - { - var dict = isOutAttr ? context.Outputs : context.Inputs; - var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); - - bool hasComponent = true; - int component = (attr >> 2) & 3; - int components = 1; - var type = attrInfo.Type & AggregateType.ElementTypeMask; - - if (isOutAttr) + else if (context.Config.TryGetTransformFeedbackOutput( + ioVariable, + ioDefinition.Location, + ioDefinition.Component, + out var transformFeedbackOutput)) { - components = context.Info.GetTransformFeedbackOutputComponents(attr); - - if (components > 1) - { - attr &= ~0xf; - type = components switch - { - 2 => AggregateType.Vector2 | AggregateType.FP32, - 3 => AggregateType.Vector3 | AggregateType.FP32, - 4 => AggregateType.Vector4 | AggregateType.FP32, - _ => AggregateType.FP32 - }; - - hasComponent = false; - } - } - - if (dict.ContainsKey(attr)) - { - return; - } - - var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; - var attrType = context.GetType(type, components); - - if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) - { - int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32; - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); - } - - if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr) - { - attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); - } - - var spvType = context.TypePointer(storageClass, attrType); - var spvVar = context.Variable(spvType, storageClass); - - Debug.Assert(attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd); - int location = (attr - AttributeConsts.UserAttributeBase) / 16; - - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); - - if (hasComponent) - { - context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component); - } - - if (isOutAttr) - { - var tfOutput = context.Info.GetTransformFeedbackOutput(attr); - if (tfOutput.Valid) - { - context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); - context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride); - context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset); - } - } - else - { - if ((context.Config.PassthroughAttributes & (1 << location)) != 0 && - context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) - { - context.Decorate(spvVar, Decoration.PassthroughNV); - } - - switch (iq) - { - case PixelImap.Constant: - context.Decorate(spvVar, Decoration.Flat); - break; - case PixelImap.ScreenLinear: - context.Decorate(spvVar, Decoration.NoPerspective); - break; - } + context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)transformFeedbackOutput.Buffer); + context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)transformFeedbackOutput.Stride); + context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)transformFeedbackOutput.Offset); } context.AddGlobalVariable(spvVar); - dict.Add(attr, spvVar); - } - private static BuiltIn GetBuiltIn(CodeGenContext context, int attr) - { - return attr switch - { - AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter, - AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner, - AttributeConsts.Layer => BuiltIn.Layer, - AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex, - AttributeConsts.PointSize => BuiltIn.PointSize, - AttributeConsts.PositionX => context.Config.Stage == ShaderStage.Fragment ? BuiltIn.FragCoord : BuiltIn.Position, - AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance, - AttributeConsts.PointCoordX => BuiltIn.PointCoord, - AttributeConsts.TessCoordX => BuiltIn.TessCoord, - AttributeConsts.InstanceId => BuiltIn.InstanceId, - AttributeConsts.VertexId => BuiltIn.VertexId, - AttributeConsts.BaseInstance => BuiltIn.BaseInstance, - AttributeConsts.BaseVertex => BuiltIn.BaseVertex, - AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex, - AttributeConsts.VertexIndex => BuiltIn.VertexIndex, - AttributeConsts.DrawIndex => BuiltIn.DrawIndex, - AttributeConsts.FrontFacing => BuiltIn.FrontFacing, - AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth, - AttributeConsts.ThreadKill => BuiltIn.HelperInvocation, - AttributeConsts.ThreadIdX => BuiltIn.LocalInvocationId, - AttributeConsts.CtaIdX => BuiltIn.WorkgroupId, - AttributeConsts.LaneId => BuiltIn.SubgroupLocalInvocationId, - AttributeConsts.InvocationId => BuiltIn.InvocationId, - AttributeConsts.PrimitiveId => BuiltIn.PrimitiveId, - AttributeConsts.PatchVerticesIn => BuiltIn.PatchVertices, - AttributeConsts.EqMask => BuiltIn.SubgroupEqMask, - AttributeConsts.GeMask => BuiltIn.SubgroupGeMask, - AttributeConsts.GtMask => BuiltIn.SubgroupGtMask, - AttributeConsts.LeMask => BuiltIn.SubgroupLeMask, - AttributeConsts.LtMask => BuiltIn.SubgroupLtMask, - AttributeConsts.SupportBlockViewInverseX => BuiltIn.Position, - AttributeConsts.SupportBlockViewInverseY => BuiltIn.Position, - _ => throw new ArgumentException($"Invalid attribute number 0x{attr:X}.") - }; + var dict = isPerPatch + ? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch) + : (isOutput ? context.Outputs : context.Inputs); + dict.Add(ioDefinition, spvVar); } private static string GetStagePrefix(ShaderStage stage) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs index aa3d046a2..72541774d 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs @@ -1,5 +1,4 @@ -using Ryujinx.Graphics.Shader.Translation; -using System; +using System; using static Spv.Specification; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index b3db19051..b6ffdb7ac 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.ImageLoad, GenerateImageLoad); Add(Instruction.ImageStore, GenerateImageStore); Add(Instruction.IsNan, GenerateIsNan); - Add(Instruction.LoadAttribute, GenerateLoadAttribute); + Add(Instruction.Load, GenerateLoad); Add(Instruction.LoadConstant, GenerateLoadConstant); Add(Instruction.LoadLocal, GenerateLoadLocal); Add(Instruction.LoadShared, GenerateLoadShared); @@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.ShuffleXor, GenerateShuffleXor); Add(Instruction.Sine, GenerateSine); Add(Instruction.SquareRoot, GenerateSquareRoot); - Add(Instruction.StoreAttribute, GenerateStoreAttribute); + Add(Instruction.Store, GenerateStore); Add(Instruction.StoreLocal, GenerateStoreLocal); Add(Instruction.StoreShared, GenerateStoreShared); Add(Instruction.StoreShared16, GenerateStoreShared16); @@ -862,31 +862,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(AggregateType.Bool, result); } - private static OperationResult GenerateLoadAttribute(CodeGenContext context, AstOperation operation) + private static OperationResult GenerateLoad(CodeGenContext context, AstOperation operation) { - var src1 = operation.GetSource(0); - var src2 = operation.GetSource(1); - var src3 = operation.GetSource(2); - - if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {nameof(Instruction.LoadAttribute)} must be a constant operand."); - } - - var index = context.Get(AggregateType.S32, src3); - var resultType = AggregateType.FP32; - - if (src2 is AstOperand operand && operand.Type == OperandType.Constant) - { - int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2); - bool isOutAttr = (baseAttr.Value & AttributeConsts.LoadOutputMask) != 0; - return new OperationResult(resultType, context.GetAttribute(resultType, attrOffset, isOutAttr, index)); - } - else - { - var attr = context.Get(AggregateType.S32, src2); - return new OperationResult(resultType, context.GetAttribute(resultType, attr, isOutAttr: false, index)); - } + return GenerateLoadOrStore(context, operation, isStore: false); } private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation) @@ -1224,7 +1202,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask); var indexNotSegMask = context.BitwiseAnd(context.TypeU32(), index, notSegMask); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1254,7 +1232,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var notSegMask = context.Not(context.TypeU32(), segMask); var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1281,7 +1259,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var srcThreadId = context.ISub(context.TypeU32(), threadId, index); @@ -1310,7 +1288,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var notSegMask = context.Not(context.TypeU32(), segMask); var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask); var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask); @@ -1336,35 +1314,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateUnary(context, operation, context.Delegates.GlslSqrt, null); } - private static OperationResult GenerateStoreAttribute(CodeGenContext context, AstOperation operation) + private static OperationResult GenerateStore(CodeGenContext context, AstOperation operation) { - var src1 = operation.GetSource(0); - var src2 = operation.GetSource(1); - var src3 = operation.GetSource(2); - - if (!(src1 is AstOperand baseAttr) || baseAttr.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {nameof(Instruction.StoreAttribute)} must be a constant operand."); - } - - SpvInstruction elemPointer; - AggregateType elemType; - - if (src2 is AstOperand operand && operand.Type == OperandType.Constant) - { - int attrOffset = (baseAttr.Value & AttributeConsts.Mask) + (operand.Value << 2); - elemPointer = context.GetAttributeElemPointer(attrOffset, isOutAttr: true, index: null, out elemType); - } - else - { - var attr = context.Get(AggregateType.S32, src2); - elemPointer = context.GetAttributeElemPointer(attr, isOutAttr: true, index: null, out elemType); - } - - var value = context.Get(elemType, src3); - context.Store(elemPointer, value); - - return OperationResult.Invalid; + return GenerateLoadOrStore(context, operation, isStore: true); } private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation) @@ -1448,7 +1400,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var three = context.Constant(context.TypeU32(), 3); - var threadId = context.GetAttribute(AggregateType.U32, AttributeConsts.LaneId, false); + var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId); var shift = context.BitwiseAnd(context.TypeU32(), threadId, three); shift = context.ShiftLeftLogical(context.TypeU32(), shift, context.Constant(context.TypeU32(), 1)); var lutIdx = context.ShiftRightLogical(context.TypeU32(), mask, shift); @@ -1982,20 +1934,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var value = context.GetU32(operation.GetSource(2)); SpvInstruction elemPointer; - Instruction mr = operation.Inst & Instruction.MrMask; - if (mr == Instruction.MrStorage) + if (operation.StorageKind == StorageKind.StorageBuffer) { elemPointer = GetStorageElemPointer(context, operation); } - else if (mr == Instruction.MrShared) + else if (operation.StorageKind == StorageKind.SharedMemory) { var offset = context.GetU32(operation.GetSource(0)); elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset); } else { - throw new InvalidOperationException($"Invalid storage class \"{mr}\"."); + throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); } var one = context.Constant(context.TypeU32(), 1); @@ -2010,20 +1961,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var value1 = context.GetU32(operation.GetSource(3)); SpvInstruction elemPointer; - Instruction mr = operation.Inst & Instruction.MrMask; - if (mr == Instruction.MrStorage) + if (operation.StorageKind == StorageKind.StorageBuffer) { elemPointer = GetStorageElemPointer(context, operation); } - else if (mr == Instruction.MrShared) + else if (operation.StorageKind == StorageKind.SharedMemory) { var offset = context.GetU32(operation.GetSource(0)); elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset); } else { - throw new InvalidOperationException($"Invalid storage class \"{mr}\"."); + throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); } var one = context.Constant(context.TypeU32(), 1); @@ -2032,6 +1982,163 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(AggregateType.U32, context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, value1, value0)); } + private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) + { + StorageKind storageKind = operation.StorageKind; + + SpvInstruction pointer; + AggregateType varType; + int srcIndex = 0; + + switch (storageKind) + { + case StorageKind.Input: + case StorageKind.InputPerPatch: + case StorageKind.Output: + case StorageKind.OutputPerPatch: + if (!(operation.GetSource(srcIndex++) is 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 = 0; + int component = 0; + + if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + { + if (!(operation.GetSource(srcIndex++) is 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.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + { + component = elemIndex.Value; + srcIndex++; + } + } + + if (ioVariable == IoVariable.UserDefined) + { + varType = context.Config.GetUserDefinedType(location, isOutput); + } + else if (ioVariable == IoVariable.FragmentOutputColor) + { + varType = context.Config.GetFragmentOutputColorType(location); + } + else if (ioVariable == IoVariable.FragmentOutputIsBgra) + { + var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeU32()); + var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 1), elemIndex); + varType = AggregateType.U32; + + break; + } + else if (ioVariable == IoVariable.SupportBlockRenderScale) + { + var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); + var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 4), elemIndex); + varType = AggregateType.FP32; + + break; + } + else if (ioVariable == IoVariable.SupportBlockViewInverse) + { + var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); + var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 2), elemIndex); + varType = AggregateType.FP32; + + break; + } + else + { + (_, varType) = IoMap.GetSpirvBuiltIn(ioVariable); + } + + varType &= AggregateType.ElementTypeMask; + + int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex; + var storageClass = isOutput ? StorageClass.Output : StorageClass.Input; + + var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component); + var dict = isPerPatch + ? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch) + : (isOutput ? context.Outputs : context.Inputs); + + SpvInstruction baseObj = dict[ioDefinition]; + SpvInstruction e0, e1, e2; + + switch (inputsCount) + { + case 0: + pointer = baseObj; + break; + case 1: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0); + break; + case 2: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1); + break; + case 3: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2); + break; + default: + var indexes = new SpvInstruction[inputsCount]; + int index = 0; + + for (; index < inputsCount; srcIndex++, index++) + { + indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex)); + } + + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes); + break; + } + break; + + default: + throw new InvalidOperationException($"Invalid storage kind {storageKind}."); + } + + if (isStore) + { + context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex))); + return OperationResult.Invalid; + } + else + { + var result = context.Load(context.GetType(varType), pointer); + return new OperationResult(varType, result); + } + } + + private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable) + { + (_, var varType) = IoMap.GetSpirvBuiltIn(ioVariable); + varType &= AggregateType.ElementTypeMask; + + var ioDefinition = new IoDefinition(StorageKind.Input, ioVariable); + + return context.Load(context.GetType(varType), context.Inputs[ioDefinition]); + } + private static void GenerateStoreSharedSmallInt(CodeGenContext context, AstOperation operation, int bitSize) { var offset = context.Get(AggregateType.U32, operation.GetSource(0)); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs new file mode 100644 index 000000000..d2ff0085c --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs @@ -0,0 +1,86 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using static Spv.Specification; + +namespace Ryujinx.Graphics.Shader.CodeGen.Spirv +{ + static class IoMap + { + // At least 16 attributes are guaranteed by the spec. + private const int MaxAttributes = 16; + + public static (BuiltIn, AggregateType) GetSpirvBuiltIn(IoVariable ioVariable) + { + return ioVariable switch + { + IoVariable.BaseInstance => (BuiltIn.BaseInstance, AggregateType.S32), + IoVariable.BaseVertex => (BuiltIn.BaseVertex, AggregateType.S32), + IoVariable.ClipDistance => (BuiltIn.ClipDistance, AggregateType.Array | AggregateType.FP32), + IoVariable.CtaId => (BuiltIn.WorkgroupId, AggregateType.Vector3 | AggregateType.U32), + IoVariable.DrawIndex => (BuiltIn.DrawIndex, AggregateType.S32), + IoVariable.FragmentCoord => (BuiltIn.FragCoord, AggregateType.Vector4 | AggregateType.FP32), + IoVariable.FragmentOutputDepth => (BuiltIn.FragDepth, AggregateType.FP32), + IoVariable.FrontFacing => (BuiltIn.FrontFacing, AggregateType.Bool), + IoVariable.InstanceId => (BuiltIn.InstanceId, AggregateType.S32), + IoVariable.InstanceIndex => (BuiltIn.InstanceIndex, AggregateType.S32), + IoVariable.InvocationId => (BuiltIn.InvocationId, AggregateType.S32), + IoVariable.Layer => (BuiltIn.Layer, AggregateType.S32), + IoVariable.PatchVertices => (BuiltIn.PatchVertices, AggregateType.S32), + IoVariable.PointCoord => (BuiltIn.PointCoord, AggregateType.Vector2 | AggregateType.FP32), + IoVariable.PointSize => (BuiltIn.PointSize, AggregateType.FP32), + IoVariable.Position => (BuiltIn.Position, AggregateType.Vector4 | AggregateType.FP32), + IoVariable.PrimitiveId => (BuiltIn.PrimitiveId, AggregateType.S32), + IoVariable.SubgroupEqMask => (BuiltIn.SubgroupEqMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupGeMask => (BuiltIn.SubgroupGeMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupGtMask => (BuiltIn.SubgroupGtMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupLaneId => (BuiltIn.SubgroupLocalInvocationId, AggregateType.U32), + IoVariable.SubgroupLeMask => (BuiltIn.SubgroupLeMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.SubgroupLtMask => (BuiltIn.SubgroupLtMask, AggregateType.Vector4 | AggregateType.U32), + IoVariable.TessellationCoord => (BuiltIn.TessCoord, AggregateType.Vector3 | AggregateType.FP32), + IoVariable.TessellationLevelInner => (BuiltIn.TessLevelInner, AggregateType.Array | AggregateType.FP32), + IoVariable.TessellationLevelOuter => (BuiltIn.TessLevelOuter, AggregateType.Array | AggregateType.FP32), + IoVariable.ThreadId => (BuiltIn.LocalInvocationId, AggregateType.Vector3 | AggregateType.U32), + IoVariable.ThreadKill => (BuiltIn.HelperInvocation, AggregateType.Bool), + IoVariable.VertexId => (BuiltIn.VertexId, AggregateType.S32), + IoVariable.VertexIndex => (BuiltIn.VertexIndex, AggregateType.S32), + IoVariable.ViewportIndex => (BuiltIn.ViewportIndex, AggregateType.S32), + IoVariable.ViewportMask => (BuiltIn.ViewportMaskNV, AggregateType.Array | AggregateType.S32), + _ => (default, AggregateType.Invalid) + }; + } + + public static int GetSpirvBuiltInArrayLength(IoVariable ioVariable) + { + return ioVariable switch + { + IoVariable.ClipDistance => 8, + IoVariable.TessellationLevelInner => 2, + IoVariable.TessellationLevelOuter => 4, + IoVariable.ViewportMask => 1, + IoVariable.UserDefined => MaxAttributes, + _ => 1 + }; + } + + public static bool IsPerVertex(IoVariable ioVariable, ShaderStage stage, bool isOutput) + { + switch (ioVariable) + { + case IoVariable.Layer: + case IoVariable.ViewportIndex: + case IoVariable.PointSize: + case IoVariable.Position: + case IoVariable.UserDefined: + case IoVariable.ClipDistance: + case IoVariable.PointCoord: + case IoVariable.ViewportMask: + return !isOutput && + (stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry); + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs index 8503771c3..f6c218c67 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var vectorFloat = context.ConvertSToF(vector2Type, vector); var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated); - var fragCoordPointer = context.Inputs[AttributeConsts.PositionX]; + var fragCoordPointer = context.Inputs[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)]; var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer); var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index ca8235383..3e11a9749 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (config.Stage == ShaderStage.Fragment) { - if (context.Info.Inputs.Contains(AttributeConsts.Layer)) + if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer))) { context.AddCapability(Capability.Geometry); } @@ -93,13 +93,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddCapability(Capability.DrawParameters); } - Declarations.DeclareAll(context, info); + if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.ViewportMask))) + { + context.AddExtension("SPV_NV_viewport_array2"); + context.AddCapability(Capability.ShaderViewportMaskNV); + } if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0) { - Declarations.DeclareInvocationId(context); + info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.SubgroupLaneId)); } + Declarations.DeclareAll(context, info); + for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++) { var function = info.Functions[funcIndex]; @@ -203,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (context.Config.Options.TargetApi == TargetApi.Vulkan) { - // We invert the front face on Vulkan backend, so we need to do that here aswell. + // We invert the front face on Vulkan backend, so we need to do that here as well. tessCw = !tessCw; } @@ -250,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv ? ExecutionMode.OriginUpperLeft : ExecutionMode.OriginLowerLeft); - if (context.Outputs.ContainsKey(AttributeConsts.FragmentOutputDepth)) + if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.FragmentOutputDepth))) { context.AddExecutionMode(spvFunc, ExecutionMode.DepthReplacing); } @@ -389,21 +395,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var source = context.Get(dest.VarType, assignment.Source); context.Store(context.GetLocalPointer(dest), source); } - else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch) - { - bool perPatch = dest.Type == OperandType.AttributePerPatch; - - if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true, perPatch)) - { - AggregateType elemType; - - var elemPointer = perPatch - ? context.GetAttributePerPatchElemPointer(dest.Value, true, out elemType) - : context.GetAttributeElemPointer(dest.Value, true, null, out elemType); - - context.Store(elemPointer, context.Get(elemType, assignment.Source)); - } - } else if (dest.Type == OperandType.Argument) { var source = context.Get(dest.VarType, assignment.Source); diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 380c425e5..c619b9bbc 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -295,10 +295,12 @@ namespace Ryujinx.Graphics.Shader.Decoders if (isStore) { config.SetAllOutputUserAttributes(); + config.SetUsedFeature(FeatureFlags.OaIndexing); } else { config.SetAllInputUserAttributes(); + config.SetUsedFeature(FeatureFlags.IaIndexing); } } else @@ -340,7 +342,8 @@ namespace Ryujinx.Graphics.Shader.Decoders } if (!isStore && - ((attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) || + (attr == AttributeConsts.FogCoord || + (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) || (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd))) { config.SetUsedFeature(FeatureFlags.FixedFuncAttr); diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index bc5e67c35..2207156cd 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -305,9 +305,9 @@ namespace Ryujinx.Graphics.Shader } /// <summary> - /// Queries host support for writes to Layer from vertex or tessellation shader stages. + /// Queries host support for writes to the layer from vertex or tessellation shader stages. /// </summary> - /// <returns>True if writes to layer from vertex or tessellation are supported, false otherwise</returns> + /// <returns>True if writes to the layer from vertex or tessellation are supported, false otherwise</returns> bool QueryHostSupportsLayerVertexTessellation() { return true; @@ -350,10 +350,19 @@ namespace Ryujinx.Graphics.Shader } /// <summary> - /// Queries host GPU shader viewport index output support. + /// Queries host support for writes to the viewport index from vertex or tessellation shader stages. /// </summary> - /// <returns>True if the GPU and driver supports shader viewport index output, false otherwise</returns> - bool QueryHostSupportsViewportIndex() + /// <returns>True if writes to the viewport index from vertex or tessellation are supported, false otherwise</returns> + bool QueryHostSupportsViewportIndexVertexTessellation() + { + return true; + } + + /// <summary> + /// Queries host GPU shader viewport mask output support. + /// </summary> + /// <returns>True if the GPU and driver supports shader viewport mask output, false otherwise</returns> + bool QueryHostSupportsViewportMask() { return true; } diff --git a/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs b/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs new file mode 100644 index 000000000..562fb8d53 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs @@ -0,0 +1,351 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static class AttributeMap + { + private enum StagesMask : byte + { + None = 0, + Compute = 1 << (int)ShaderStage.Compute, + Vertex = 1 << (int)ShaderStage.Vertex, + TessellationControl = 1 << (int)ShaderStage.TessellationControl, + TessellationEvaluation = 1 << (int)ShaderStage.TessellationEvaluation, + Geometry = 1 << (int)ShaderStage.Geometry, + Fragment = 1 << (int)ShaderStage.Fragment, + + Tessellation = TessellationControl | TessellationEvaluation, + VertexTessellationGeometry = Vertex | Tessellation | Geometry, + TessellationGeometryFragment = Tessellation | Geometry | Fragment, + AllGraphics = Vertex | Tessellation | Geometry | Fragment + } + + private struct AttributeEntry + { + public int BaseOffset { get; } + public AggregateType Type { get; } + public IoVariable IoVariable { get; } + public StagesMask InputMask { get; } + public StagesMask OutputMask { get; } + + public AttributeEntry( + int baseOffset, + AggregateType type, + IoVariable ioVariable, + StagesMask inputMask, + StagesMask outputMask) + { + BaseOffset = baseOffset; + Type = type; + IoVariable = ioVariable; + InputMask = inputMask; + OutputMask = outputMask; + } + } + + private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributes; + private static readonly IReadOnlyDictionary<int, AttributeEntry> _attributesPerPatch; + + static AttributeMap() + { + _attributes = CreateMap(); + _attributesPerPatch = CreatePerPatchMap(); + } + + private static IReadOnlyDictionary<int, AttributeEntry> CreateMap() + { + var map = new Dictionary<int, AttributeEntry>(); + + Add(map, 0x060, AggregateType.S32, IoVariable.PrimitiveId, StagesMask.TessellationGeometryFragment, StagesMask.Geometry); + Add(map, 0x064, AggregateType.S32, IoVariable.Layer, StagesMask.Fragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x068, AggregateType.S32, IoVariable.ViewportIndex, StagesMask.Fragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x06c, AggregateType.FP32, IoVariable.PointSize, StagesMask.None, StagesMask.VertexTessellationGeometry); + Add(map, 0x070, AggregateType.Vector4 | AggregateType.FP32, IoVariable.Position, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x080, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.AllGraphics, StagesMask.VertexTessellationGeometry, 32); + Add(map, 0x280, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x290, AggregateType.Vector4 | AggregateType.FP32, IoVariable.FrontColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x2a0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorDiffuse, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x2b0, AggregateType.Vector4 | AggregateType.FP32, IoVariable.BackColorSpecular, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x2c0, AggregateType.Array | AggregateType.FP32, IoVariable.ClipDistance, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry, 8); + Add(map, 0x2e0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.PointCoord, StagesMask.Fragment, StagesMask.None); + Add(map, 0x2e8, AggregateType.FP32, IoVariable.FogCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x2f0, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationCoord, StagesMask.TessellationEvaluation, StagesMask.None); + Add(map, 0x2f8, AggregateType.S32, IoVariable.InstanceId, StagesMask.Vertex, StagesMask.None); + Add(map, 0x2fc, AggregateType.S32, IoVariable.VertexId, StagesMask.Vertex, StagesMask.None); + Add(map, 0x300, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TextureCoord, StagesMask.TessellationGeometryFragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x3a0, AggregateType.Array | AggregateType.S32, IoVariable.ViewportMask, StagesMask.Fragment, StagesMask.VertexTessellationGeometry); + Add(map, 0x3fc, AggregateType.Bool, IoVariable.FrontFacing, StagesMask.Fragment, StagesMask.None); + + return map; + } + + private static IReadOnlyDictionary<int, AttributeEntry> CreatePerPatchMap() + { + var map = new Dictionary<int, AttributeEntry>(); + + Add(map, 0x000, AggregateType.Vector4 | AggregateType.FP32, IoVariable.TessellationLevelOuter, StagesMask.TessellationEvaluation, StagesMask.TessellationControl); + Add(map, 0x010, AggregateType.Vector2 | AggregateType.FP32, IoVariable.TessellationLevelInner, StagesMask.TessellationEvaluation, StagesMask.TessellationControl); + Add(map, 0x018, AggregateType.Vector4 | AggregateType.FP32, IoVariable.UserDefined, StagesMask.TessellationEvaluation, StagesMask.TessellationControl, 31, 0x200); + + return map; + } + + private static void Add( + Dictionary<int, AttributeEntry> attributes, + int offset, + AggregateType type, + IoVariable ioVariable, + StagesMask inputMask, + StagesMask outputMask, + int count = 1, + int upperBound = 0x400) + { + int baseOffset = offset; + + int elementsCount = GetElementCount(type); + + for (int index = 0; index < count; index++) + { + for (int elementIndex = 0; elementIndex < elementsCount; elementIndex++) + { + attributes.Add(offset, new AttributeEntry(baseOffset, type, ioVariable, inputMask, outputMask)); + + offset += 4; + + if (offset >= upperBound) + { + return; + } + } + } + } + + public static Operand GenerateAttributeLoad(EmitterContext context, Operand primVertex, int offset, bool isOutput, bool isPerPatch) + { + if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry)) + { + context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid."); + return Const(0); + } + + StagesMask validUseMask = isOutput ? entry.OutputMask : entry.InputMask; + + if (((StagesMask)(1 << (int)context.Config.Stage) & validUseMask) == StagesMask.None) + { + context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}."); + return Const(0); + } + + if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable)) + { + context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}."); + return Const(0); + } + + if (HasInvocationId(context.Config.Stage, isOutput) && !isPerPatch) + { + primVertex = context.Load(StorageKind.Input, IoVariable.InvocationId); + } + + int innerOffset = offset - entry.BaseOffset; + int innerIndex = innerOffset / 4; + + StorageKind storageKind = isPerPatch + ? (isOutput ? StorageKind.OutputPerPatch : StorageKind.InputPerPatch) + : (isOutput ? StorageKind.Output : StorageKind.Input); + IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry); + AggregateType type = GetType(context.Config, isOutput, innerIndex, in entry); + int elementCount = GetElementCount(type); + + bool isArray = type.HasFlag(AggregateType.Array); + bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput); + + bool hasElementIndex = elementCount > 1; + + if (hasArrayIndex && hasElementIndex) + { + int arrayIndex = innerIndex / elementCount; + int elementIndex = innerIndex - (arrayIndex * elementCount); + + return primVertex == null || isArray + ? context.Load(storageKind, ioVariable, primVertex, Const(arrayIndex), Const(elementIndex)) + : context.Load(storageKind, ioVariable, Const(arrayIndex), primVertex, Const(elementIndex)); + } + else if (hasArrayIndex || hasElementIndex) + { + return primVertex == null || isArray || !hasArrayIndex + ? context.Load(storageKind, ioVariable, primVertex, Const(innerIndex)) + : context.Load(storageKind, ioVariable, Const(innerIndex), primVertex); + } + else + { + return context.Load(storageKind, ioVariable, primVertex); + } + } + + public static void GenerateAttributeStore(EmitterContext context, int offset, bool isPerPatch, Operand value) + { + if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry)) + { + context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid."); + return; + } + + if (((StagesMask)(1 << (int)context.Config.Stage) & entry.OutputMask) == StagesMask.None) + { + context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}."); + return; + } + + if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable)) + { + context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}."); + return; + } + + Operand invocationId = null; + + if (HasInvocationId(context.Config.Stage, isOutput: true) && !isPerPatch) + { + invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId); + } + + int innerOffset = offset - entry.BaseOffset; + int innerIndex = innerOffset / 4; + + StorageKind storageKind = isPerPatch ? StorageKind.OutputPerPatch : StorageKind.Output; + IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry); + AggregateType type = GetType(context.Config, isOutput: true, innerIndex, in entry); + int elementCount = GetElementCount(type); + + bool isArray = type.HasFlag(AggregateType.Array); + bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput: true); + + bool hasElementIndex = elementCount > 1; + + if (hasArrayIndex && hasElementIndex) + { + int arrayIndex = innerIndex / elementCount; + int elementIndex = innerIndex - (arrayIndex * elementCount); + + if (invocationId == null || isArray) + { + context.Store(storageKind, ioVariable, invocationId, Const(arrayIndex), Const(elementIndex), value); + } + else + { + context.Store(storageKind, ioVariable, Const(arrayIndex), invocationId, Const(elementIndex), value); + } + } + else if (hasArrayIndex || hasElementIndex) + { + if (invocationId == null || isArray || !hasArrayIndex) + { + context.Store(storageKind, ioVariable, invocationId, Const(innerIndex), value); + } + else + { + context.Store(storageKind, ioVariable, Const(innerIndex), invocationId, value); + } + } + else + { + context.Store(storageKind, ioVariable, invocationId, value); + } + } + + private static bool IsSupportedByHost(IGpuAccessor gpuAccessor, ShaderStage stage, IoVariable ioVariable) + { + if (ioVariable == IoVariable.ViewportIndex && stage != ShaderStage.Geometry && stage != ShaderStage.Fragment) + { + return gpuAccessor.QueryHostSupportsViewportIndexVertexTessellation(); + } + else if (ioVariable == IoVariable.ViewportMask) + { + return gpuAccessor.QueryHostSupportsViewportMask(); + } + + return true; + } + + public static IoVariable GetIoVariable(ShaderConfig config, int offset, out int location) + { + location = 0; + + if (!_attributes.TryGetValue(offset, out AttributeEntry entry)) + { + return IoVariable.Invalid; + } + + if (((StagesMask)(1 << (int)config.Stage) & entry.OutputMask) == StagesMask.None) + { + return IoVariable.Invalid; + } + + if (config.HasPerLocationInputOrOutput(entry.IoVariable, isOutput: true)) + { + location = (offset - entry.BaseOffset) / 16; + } + + return GetIoVariable(config.Stage, in entry); + } + + private static IoVariable GetIoVariable(ShaderStage stage, in AttributeEntry entry) + { + if (entry.IoVariable == IoVariable.Position && stage == ShaderStage.Fragment) + { + return IoVariable.FragmentCoord; + } + + return entry.IoVariable; + } + + private static AggregateType GetType(ShaderConfig config, bool isOutput, int innerIndex, in AttributeEntry entry) + { + AggregateType type = entry.Type; + + if (entry.IoVariable == IoVariable.UserDefined) + { + type = config.GetUserDefinedType(innerIndex / 4, isOutput); + } + else if (entry.IoVariable == IoVariable.FragmentOutputColor) + { + type = config.GetFragmentOutputColorType(innerIndex / 4); + } + + return type; + } + + public static bool HasPrimitiveVertex(ShaderStage stage, bool isOutput) + { + if (isOutput) + { + return false; + } + + return stage == ShaderStage.TessellationControl || + stage == ShaderStage.TessellationEvaluation || + stage == ShaderStage.Geometry; + } + + public static bool HasInvocationId(ShaderStage stage, bool isOutput) + { + return isOutput && stage == ShaderStage.TessellationControl; + } + + private static int GetElementCount(AggregateType type) + { + return (type & AggregateType.ElementCountMask) switch + { + AggregateType.Vector2 => 2, + AggregateType.Vector3 => 3, + AggregateType.Vector4 => 4, + _ => 1 + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 9f9ac1412..1df387618 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -20,7 +20,16 @@ namespace Ryujinx.Graphics.Shader.Instructions { InstAld op = context.GetOp<InstAld>(); - Operand primVertex = context.Copy(GetSrcReg(context, op.SrcB)); + // Some of those attributes are per invocation, + // so we should ignore any primitive vertex indexing for those. + bool hasPrimitiveVertex = AttributeMap.HasPrimitiveVertex(context.Config.Stage, op.O) && !op.P; + + if (!op.Phys) + { + hasPrimitiveVertex &= HasPrimitiveVertex(op.Imm11); + } + + Operand primVertex = hasPrimitiveVertex ? context.Copy(GetSrcReg(context, op.SrcB)) : null; for (int index = 0; index < (int)op.AlSize + 1; index++) { @@ -33,12 +42,13 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Phys) { - Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase)); - Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2)); + Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase)); + Operand vecIndex = context.ShiftRightU32(offset, Const(4)); + Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3)); - context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex)); + StorageKind storageKind = op.O ? StorageKind.Output : StorageKind.Input; - context.Config.SetUsedFeature(FeatureFlags.IaIndexing); + context.Copy(Register(rd), context.Load(storageKind, IoVariable.UserDefined, primVertex, vecIndex, elemIndex)); } else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P) { @@ -46,14 +56,16 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeRead(offset); - if (op.O && CanLoadOutput(offset)) + bool isOutput = op.O && CanLoadOutput(offset); + + if (!op.P && !isOutput && TryConvertIdToIndexForVulkan(context, offset, out Operand value)) { - offset |= AttributeConsts.LoadOutputMask; + context.Copy(Register(rd), value); + } + else + { + context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, op.P)); } - - Operand src = op.P ? AttributePerPatch(offset) : CreateInputAttribute(context, offset); - - context.Copy(Register(rd), src); } else { @@ -61,14 +73,9 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeRead(offset); - if (op.O && CanLoadOutput(offset)) - { - offset |= AttributeConsts.LoadOutputMask; - } + bool isOutput = op.O && CanLoadOutput(offset); - Operand src = Const(offset); - - context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex)); + context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, false)); } } } @@ -88,12 +95,14 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Phys) { - Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase)); - Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2)); + Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase)); + Operand vecIndex = context.ShiftRightU32(offset, Const(4)); + Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3)); + Operand invocationId = AttributeMap.HasInvocationId(context.Config.Stage, isOutput: true) + ? context.Load(StorageKind.Input, IoVariable.InvocationId) + : null; - context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd)); - - context.Config.SetUsedFeature(FeatureFlags.OaIndexing); + context.Store(StorageKind.Output, IoVariable.UserDefined, invocationId, vecIndex, elemIndex, Register(rd)); } else { @@ -110,9 +119,7 @@ namespace Ryujinx.Graphics.Shader.Instructions context.FlagAttributeWritten(offset); - Operand dest = op.P ? AttributePerPatch(offset) : Attribute(offset); - - context.Copy(dest, Register(rd)); + AttributeMap.GenerateAttributeStore(context, offset, op.P, Register(rd)); } } } @@ -129,13 +136,12 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Idx) { - Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase)); - Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2)); + Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase)); + Operand vecIndex = context.ShiftRightU32(offset, Const(4)); + Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3)); - res = context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Const(0)); - res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW)); - - context.Config.SetUsedFeature(FeatureFlags.IaIndexing); + res = context.Load(StorageKind.Input, IoVariable.UserDefined, null, vecIndex, elemIndex); + res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3))); } else { @@ -147,9 +153,21 @@ namespace Ryujinx.Graphics.Shader.Instructions if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective) { - res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW)); + res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3))); } } + else if (op.Imm10 == AttributeConsts.PositionX || op.Imm10 == AttributeConsts.PositionY) + { + // FragCoord X/Y must be divided by the render target scale, if resolution scaling is active, + // because the shader code is not expecting scaled values. + res = context.FPDivide(res, context.Load(StorageKind.Input, IoVariable.SupportBlockRenderScale, null, Const(0))); + } + else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug()) + { + // gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs. + // This weird trick makes it behave. + res = context.ICompareLess(context.INegate(context.IConvertS32ToFP32(res)), Const(0)); + } } if (op.IpaOp == IpaOp.Multiply && !isFixedFunc) @@ -216,17 +234,17 @@ namespace Ryujinx.Graphics.Shader.Instructions if (tempXLocal != null) { - context.Copy(Attribute(AttributeConsts.PositionX), tempXLocal); + context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(0)), tempXLocal); } if (tempYLocal != null) { - context.Copy(Attribute(AttributeConsts.PositionY), tempYLocal); + context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(1)), tempYLocal); } if (tempZLocal != null) { - context.Copy(Attribute(AttributeConsts.PositionZ), tempZLocal); + context.Copy(context.Load(StorageKind.Input, IoVariable.Position, null, Const(2)), tempZLocal); } } else @@ -241,6 +259,13 @@ namespace Ryujinx.Graphics.Shader.Instructions } } + private static bool HasPrimitiveVertex(int attr) + { + return attr != AttributeConsts.PrimitiveId && + attr != AttributeConsts.TessCoordX && + attr != AttributeConsts.TessCoordY; + } + private static bool CanLoadOutput(int attr) { return attr != AttributeConsts.TessCoordX && attr != AttributeConsts.TessCoordY; @@ -252,13 +277,13 @@ namespace Ryujinx.Graphics.Shader.Instructions { // TODO: If two sided rendering is enabled, then this should return // FrontColor if the fragment is front facing, and back color otherwise. - int index = (attr - AttributeConsts.FrontColorDiffuseR) >> 4; - int userAttrIndex = context.Config.GetFreeUserAttribute(isOutput: false, index); - Operand frontAttr = Attribute(AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf)); - - context.Config.SetInputUserAttributeFixedFunc(userAttrIndex); - - selectedAttr = frontAttr; + selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false)); + return true; + } + else if (attr == AttributeConsts.FogCoord) + { + // TODO: We likely need to emulate the fixed-function functionality for FogCoord here. + selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false)); return true; } else if (attr >= AttributeConsts.BackColorDiffuseR && attr < AttributeConsts.ClipDistance0) @@ -268,14 +293,19 @@ namespace Ryujinx.Graphics.Shader.Instructions } else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd) { - selectedAttr = Attribute(FixedFuncToUserAttribute(context.Config, attr, AttributeConsts.TexCoordBase, 4, isOutput: false)); + selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false)); return true; } - selectedAttr = Attribute(attr); + selectedAttr = GenerateIpaLoad(context, attr); return false; } + private static Operand GenerateIpaLoad(EmitterContext context, int offset) + { + return AttributeMap.GenerateAttributeLoad(context, null, offset, isOutput: false, isPerPatch: false); + } + private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput) { bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation(); @@ -286,13 +316,17 @@ namespace Ryujinx.Graphics.Shader.Instructions attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput); config.SetLayerOutputAttribute(attr); } + else if (attr == AttributeConsts.FogCoord) + { + attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FogCoord, fixedStartAttr, isOutput); + } else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr, isOutput); + attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr + 1, isOutput); } else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 4, isOutput); + attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 5, isOutput); } return attr; @@ -301,11 +335,10 @@ namespace Ryujinx.Graphics.Shader.Instructions private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput) { int index = (attr - baseAttr) >> 4; - int userAttrIndex = config.GetFreeUserAttribute(isOutput, index); + int userAttrIndex = config.GetFreeUserAttribute(isOutput, baseIndex + index); if ((uint)userAttrIndex < Constants.MaxAttributes) { - userAttrIndex += baseIndex; attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf); if (isOutput) @@ -317,25 +350,34 @@ namespace Ryujinx.Graphics.Shader.Instructions config.SetInputUserAttributeFixedFunc(userAttrIndex); } } + else + { + config.GpuAccessor.Log($"No enough user attributes for fixed attribute offset 0x{attr:X}."); + } return attr; } - private static Operand CreateInputAttribute(EmitterContext context, int attr) + private static bool TryConvertIdToIndexForVulkan(EmitterContext context, int attr, out Operand value) { if (context.Config.Options.TargetApi == TargetApi.Vulkan) { if (attr == AttributeConsts.InstanceId) { - return context.ISubtract(Attribute(AttributeConsts.InstanceIndex), Attribute(AttributeConsts.BaseInstance)); + value = context.ISubtract( + context.Load(StorageKind.Input, IoVariable.InstanceIndex), + context.Load(StorageKind.Input, IoVariable.BaseInstance)); + return true; } else if (attr == AttributeConsts.VertexId) { - return Attribute(AttributeConsts.VertexIndex); + value = context.Load(StorageKind.Input, IoVariable.VertexIndex); + return true; } } - return Attribute(attr); + value = null; + return false; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index ceb76de16..c73c6b2ac 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand value = GetSrcReg(context, op.SrcB); - Operand res = EmitAtomicOp(context, Instruction.MrGlobal, op.Op, op.Size, addrLow, addrHigh, value); + Operand res = EmitAtomicOp(context, StorageKind.GlobalMemory, op.Op, op.Size, addrLow, addrHigh, value); context.Copy(GetDest(op.Dest), res); } @@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Shader.Instructions _ => AtomSize.U32 }; - Operand res = EmitAtomicOp(context, Instruction.MrShared, op.AtomOp, size, offset, Const(0), value); + Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, offset, Const(0), value); context.Copy(GetDest(op.Dest), res); } @@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Shader.Instructions (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(op.SrcA, RegisterType.Gpr), op.E, op.Imm20); - EmitAtomicOp(context, Instruction.MrGlobal, (AtomOp)op.RedOp, op.RedSize, addrLow, addrHigh, GetDest(op.SrcB)); + EmitAtomicOp(context, StorageKind.GlobalMemory, (AtomOp)op.RedOp, op.RedSize, addrLow, addrHigh, GetDest(op.SrcB)); } public static void Stg(EmitterContext context) @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.Instructions private static Operand EmitAtomicOp( EmitterContext context, - Instruction mr, + StorageKind storageKind, AtomOp op, AtomSize type, Operand addrLow, @@ -170,7 +170,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Add: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicAdd(mr, addrLow, addrHigh, value); + res = context.AtomicAdd(storageKind, addrLow, addrHigh, value); } else { @@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.And: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicAnd(mr, addrLow, addrHigh, value); + res = context.AtomicAnd(storageKind, addrLow, addrHigh, value); } else { @@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Xor: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicXor(mr, addrLow, addrHigh, value); + res = context.AtomicXor(storageKind, addrLow, addrHigh, value); } else { @@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Or: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicOr(mr, addrLow, addrHigh, value); + res = context.AtomicOr(storageKind, addrLow, addrHigh, value); } else { @@ -210,11 +210,11 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Max: if (type == AtomSize.S32) { - res = context.AtomicMaxS32(mr, addrLow, addrHigh, value); + res = context.AtomicMaxS32(storageKind, addrLow, addrHigh, value); } else if (type == AtomSize.U32) { - res = context.AtomicMaxU32(mr, addrLow, addrHigh, value); + res = context.AtomicMaxU32(storageKind, addrLow, addrHigh, value); } else { @@ -224,11 +224,11 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Min: if (type == AtomSize.S32) { - res = context.AtomicMinS32(mr, addrLow, addrHigh, value); + res = context.AtomicMinS32(storageKind, addrLow, addrHigh, value); } else if (type == AtomSize.U32) { - res = context.AtomicMinU32(mr, addrLow, addrHigh, value); + res = context.AtomicMinU32(storageKind, addrLow, addrHigh, value); } else { diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index 16b02f978..9992ac378 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -76,11 +76,11 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (op.SReg) { case SReg.LaneId: - src = Attribute(AttributeConsts.LaneId); + src = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId); break; case SReg.InvocationId: - src = Attribute(AttributeConsts.InvocationId); + src = context.Load(StorageKind.Input, IoVariable.InvocationId); break; case SReg.YDirection: @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader.Instructions break; case SReg.ThreadKill: - src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0); + src = context.Config.Stage == ShaderStage.Fragment ? context.Load(StorageKind.Input, IoVariable.ThreadKill) : Const(0); break; case SReg.InvocationInfo: @@ -101,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (context.Config.Stage == ShaderStage.TessellationControl || context.Config.Stage == ShaderStage.TessellationEvaluation) { - src = context.ShiftLeft(Attribute(AttributeConsts.PatchVerticesIn), Const(16)); + src = context.ShiftLeft(context.Load(StorageKind.Input, IoVariable.PatchVertices), Const(16)); } else { @@ -115,9 +115,9 @@ namespace Ryujinx.Graphics.Shader.Instructions break; case SReg.TId: - Operand tidX = Attribute(AttributeConsts.ThreadIdX); - Operand tidY = Attribute(AttributeConsts.ThreadIdY); - Operand tidZ = Attribute(AttributeConsts.ThreadIdZ); + Operand tidX = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(0)); + Operand tidY = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(1)); + Operand tidZ = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(2)); tidY = context.ShiftLeft(tidY, Const(16)); tidZ = context.ShiftLeft(tidZ, Const(26)); @@ -126,39 +126,39 @@ namespace Ryujinx.Graphics.Shader.Instructions break; case SReg.TIdX: - src = Attribute(AttributeConsts.ThreadIdX); + src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(0)); break; case SReg.TIdY: - src = Attribute(AttributeConsts.ThreadIdY); + src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(1)); break; case SReg.TIdZ: - src = Attribute(AttributeConsts.ThreadIdZ); + src = context.Load(StorageKind.Input, IoVariable.ThreadId, null, Const(2)); break; case SReg.CtaIdX: - src = Attribute(AttributeConsts.CtaIdX); + src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(0)); break; case SReg.CtaIdY: - src = Attribute(AttributeConsts.CtaIdY); + src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(1)); break; case SReg.CtaIdZ: - src = Attribute(AttributeConsts.CtaIdZ); + src = context.Load(StorageKind.Input, IoVariable.CtaId, null, Const(2)); break; case SReg.EqMask: - src = Attribute(AttributeConsts.EqMask); + src = context.Load(StorageKind.Input, IoVariable.SubgroupEqMask, null, Const(0)); break; case SReg.LtMask: - src = Attribute(AttributeConsts.LtMask); + src = context.Load(StorageKind.Input, IoVariable.SubgroupLtMask, null, Const(0)); break; case SReg.LeMask: - src = Attribute(AttributeConsts.LeMask); + src = context.Load(StorageKind.Input, IoVariable.SubgroupLeMask, null, Const(0)); break; case SReg.GtMask: - src = Attribute(AttributeConsts.GtMask); + src = context.Load(StorageKind.Input, IoVariable.SubgroupGtMask, null, Const(0)); break; case SReg.GeMask: - src = Attribute(AttributeConsts.GeMask); + src = context.Load(StorageKind.Input, IoVariable.SubgroupGeMask, null, Const(0)); break; default: diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index aa9776bcf..d7c4a961c 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation ImageStore, ImageAtomic, IsNan, - LoadAttribute, + Load, LoadConstant, LoadGlobal, LoadLocal, @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation ShuffleXor, Sine, SquareRoot, - StoreAttribute, + Store, StoreGlobal, StoreGlobal16, StoreGlobal8, @@ -144,13 +144,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation FP32 = 1 << 16, FP64 = 1 << 17, - MrShift = 18, - - MrGlobal = 0 << MrShift, - MrShared = 1 << MrShift, - MrStorage = 2 << MrShift, - MrMask = 3 << MrShift, - Mask = 0xffff } diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs new file mode 100644 index 000000000..a2163d14f --- /dev/null +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs @@ -0,0 +1,51 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + enum IoVariable + { + Invalid, + + BackColorDiffuse, + BackColorSpecular, + BaseInstance, + BaseVertex, + ClipDistance, + CtaId, + DrawIndex, + FogCoord, + FragmentCoord, + FragmentOutputColor, + FragmentOutputDepth, + FragmentOutputIsBgra, // TODO: Remove and use constant buffer access. + FrontColorDiffuse, + FrontColorSpecular, + FrontFacing, + InstanceId, + InstanceIndex, + InvocationId, + Layer, + PatchVertices, + PointCoord, + PointSize, + Position, + PrimitiveId, + SubgroupEqMask, + SubgroupGeMask, + SubgroupGtMask, + SubgroupLaneId, + SubgroupLeMask, + SubgroupLtMask, + SupportBlockViewInverse, // TODO: Remove and use constant buffer access. + SupportBlockRenderScale, // TODO: Remove and use constant buffer access. + TessellationCoord, + TessellationLevelInner, + TessellationLevelOuter, + TextureCoord, + ThreadId, + ThreadKill, + UserDefined, + VertexId, + VertexIndex, + ViewportIndex, + ViewportMask + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs index 7fed861e5..37c349e82 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs @@ -10,16 +10,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation return new Operand(OperandType.Argument, value); } - public static Operand Attribute(int value) - { - return new Operand(OperandType.Attribute, value); - } - - public static Operand AttributePerPatch(int value) - { - return new Operand(OperandType.AttributePerPatch, value); - } - public static Operand Cbuf(int slot, int offset) { return new Operand(slot, offset); diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs index 7566a03f3..4d2da734e 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs @@ -3,8 +3,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation enum OperandType { Argument, - Attribute, - AttributePerPatch, Constant, ConstantBuffer, Label, @@ -12,12 +10,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Register, Undefined } - - static class OperandTypeExtensions - { - public static bool IsAttribute(this OperandType type) - { - return type == OperandType.Attribute || type == OperandType.AttributePerPatch; - } - } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index 18e203a70..99179f151 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation class Operation : INode { public Instruction Inst { get; private set; } + public StorageKind StorageKind { get; } private Operand[] _dests; @@ -99,6 +100,23 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation } } + public Operation(Instruction inst, StorageKind storageKind, Operand dest, params Operand[] sources) : this(sources) + { + Inst = inst; + StorageKind = storageKind; + + if (dest != null) + { + dest.AsgOp = this; + + _dests = new[] { dest }; + } + else + { + _dests = Array.Empty<Operand>(); + } + } + public Operation(Instruction inst, int index, Operand dest, params Operand[] sources) : this(inst, dest, sources) { Index = index; diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs new file mode 100644 index 000000000..593574438 --- /dev/null +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs @@ -0,0 +1,39 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + enum StorageKind + { + None, + Input, + InputPerPatch, + Output, + OutputPerPatch, + ConstantBuffer, + StorageBuffer, + LocalMemory, + SharedMemory, + GlobalMemory + } + + static class StorageKindExtensions + { + public static bool IsInputOrOutput(this StorageKind storageKind) + { + return storageKind == StorageKind.Input || + storageKind == StorageKind.InputPerPatch || + storageKind == StorageKind.Output || + storageKind == StorageKind.OutputPerPatch; + } + + public static bool IsOutput(this StorageKind storageKind) + { + return storageKind == StorageKind.Output || + storageKind == StorageKind.OutputPerPatch; + } + + public static bool IsPerPatch(this StorageKind storageKind) + { + return storageKind == StorageKind.InputPerPatch || + storageKind == StorageKind.OutputPerPatch; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs index 193972565..2393fd8d8 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr class AstOperation : AstNode { public Instruction Inst { get; } + public StorageKind StorageKind { get; } public int Index { get; } @@ -16,9 +17,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public int SourcesCount => _sources.Length; - public AstOperation(Instruction inst, IAstNode[] sources, int sourcesCount) + public AstOperation(Instruction inst, StorageKind storageKind, IAstNode[] sources, int sourcesCount) { - Inst = inst; + Inst = inst; + StorageKind = storageKind; _sources = sources; for (int index = 0; index < sources.Length; index++) @@ -36,12 +38,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Index = 0; } - public AstOperation(Instruction inst, int index, IAstNode[] sources, int sourcesCount) : this(inst, sources, sourcesCount) + public AstOperation(Instruction inst, StorageKind storageKind, int index, IAstNode[] sources, int sourcesCount) : this(inst, storageKind, sources, sourcesCount) { Index = index; } - public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, sources, sources.Length) + public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, sources, sources.Length) { } diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 957a956fc..a44f13cc0 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr int cbufSlot, int handle, int index, - params IAstNode[] sources) : base(inst, index, sources, sources.Length) + params IAstNode[] sources) : base(inst, StorageKind.None, index, sources, sources.Length) { Type = type; Format = format; diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 0a9a9e511..8eccef237 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ImageStore, AggregateType.Void); Add(Instruction.ImageAtomic, AggregateType.S32); Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar); - Add(Instruction.LoadAttribute, AggregateType.FP32, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.Load, AggregateType.FP32); Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32); Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32); Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32); @@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool); Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar); - Add(Instruction.StoreAttribute, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.FP32); + Add(Instruction.Store, AggregateType.Void); Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32); Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32); @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { return AggregateType.FP32; } - else if (inst == Instruction.Call) + else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store) { return AggregateType.S32; } diff --git a/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs b/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs new file mode 100644 index 000000000..21a1b3f08 --- /dev/null +++ b/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs @@ -0,0 +1,44 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + readonly struct IoDefinition : IEquatable<IoDefinition> + { + public StorageKind StorageKind { get; } + public IoVariable IoVariable { get; } + public int Location { get; } + public int Component { get; } + + public IoDefinition(StorageKind storageKind, IoVariable ioVariable, int location = 0, int component = 0) + { + StorageKind = storageKind; + IoVariable = ioVariable; + Location = location; + Component = component; + } + + public override bool Equals(object other) + { + return other is IoDefinition ioDefinition && Equals(ioDefinition); + } + + public bool Equals(IoDefinition other) + { + return StorageKind == other.StorageKind && + IoVariable == other.IoVariable && + Location == other.Location && + Component == other.Component; + } + + public override int GetHashCode() + { + return (int)StorageKind | ((int)IoVariable << 8) | (Location << 16) | (Component << 24); + } + + public override string ToString() + { + return $"{StorageKind}.{IoVariable}.{Location}.{Component}"; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs index 730468a43..38ed1584b 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs @@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return type switch { OperandType.Argument => AggregateType.S32, - OperandType.Attribute => AggregateType.FP32, - OperandType.AttributePerPatch => AggregateType.FP32, OperandType.Constant => AggregateType.S32, OperandType.ConstantBuffer => AggregateType.FP32, OperandType.Undefined => AggregateType.S32, diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index b8d38fa65..b4ca8ee58 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -65,49 +65,35 @@ namespace Ryujinx.Graphics.Shader.StructuredIr context.LeaveFunction(); } - if (config.TransformFeedbackEnabled && (config.LastInVertexPipeline || config.Stage == ShaderStage.Fragment)) - { - for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++) - { - var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); - var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); - - for (int i = 0; i < locations.Length; i++) - { - byte location = locations[i]; - if (location < 0xc0) - { - context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, i * 4, stride); - } - } - } - } - return context.Info; } private static void AddOperation(StructuredProgramContext context, Operation operation) { Instruction inst = operation.Inst; + StorageKind storageKind = operation.StorageKind; - if (inst == Instruction.LoadAttribute) + if ((inst == Instruction.Load || inst == Instruction.Store) && storageKind.IsInputOrOutput()) { - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); + IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value; + bool isOutput = storageKind.IsOutput(); + bool perPatch = storageKind.IsPerPatch(); + int location = 0; + int component = 0; - if (src1.Type == OperandType.Constant && src2.Type == OperandType.Constant) + if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) { - int attrOffset = (src1.Value & AttributeConsts.Mask) + (src2.Value << 2); + location = operation.GetSource(1).Value; - if ((src1.Value & AttributeConsts.LoadOutputMask) != 0) + if (operation.SourcesCount > 2 && + operation.GetSource(2).Type == OperandType.Constant && + context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput)) { - context.Info.Outputs.Add(attrOffset); - } - else - { - context.Info.Inputs.Add(attrOffset); + component = operation.GetSource(2).Value; } } + + context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component)); } bool vectorDest = IsVectorDestInst(inst); @@ -119,12 +105,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr for (int index = 0; index < operation.SourcesCount; index++) { - sources[index] = context.GetOperandUse(operation.GetSource(index)); + sources[index] = context.GetOperand(operation.GetSource(index)); } for (int index = 0; index < outDestsCount; index++) { - AstOperand oper = context.GetOperandDef(operation.GetDest(1 + index)); + AstOperand oper = context.GetOperand(operation.GetDest(1 + index)); oper.VarType = InstructionInfo.GetSrcVarType(inst, sourcesCount + index); @@ -163,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount); + source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount); } AggregateType destElemType = destType; @@ -181,17 +167,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr for (int i = 0; i < operation.DestsCount; i++) { - AstOperand dest = context.GetOperandDef(operation.GetDest(i)); + AstOperand dest = context.GetOperand(operation.GetDest(i)); AstOperand index = new AstOperand(OperandType.Constant, i); dest.VarType = destElemType; - context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, new[] { destVec, index }, 2))); + context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, new[] { destVec, index }, 2))); } } else if (operation.Dest != null) { - AstOperand dest = context.GetOperandDef(operation.Dest); + AstOperand dest = context.GetOperand(operation.Dest); // If all the sources are bool, it's better to use short-circuiting // logical operations, rather than forcing a cast to int and doing @@ -234,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else if (!isCopy) { - source = new AstOperation(inst, operation.Index, sources, operation.SourcesCount); + source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount); } else { @@ -255,7 +241,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - context.AddNode(new AstOperation(inst, operation.Index, sources, operation.SourcesCount)); + context.AddNode(new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount)); } // Those instructions needs to be emulated by using helper functions, @@ -263,13 +249,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr // decide which helper functions are needed on the final generated code. switch (operation.Inst) { - case Instruction.AtomicMaxS32 | Instruction.MrShared: - case Instruction.AtomicMinS32 | Instruction.MrShared: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared; - break; - case Instruction.AtomicMaxS32 | Instruction.MrStorage: - case Instruction.AtomicMinS32 | Instruction.MrStorage: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage; + case Instruction.AtomicMaxS32: + case Instruction.AtomicMinS32: + if (operation.StorageKind == StorageKind.SharedMemory) + { + context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared; + } + else if (operation.StorageKind == StorageKind.StorageBuffer) + { + context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage; + } break; case Instruction.MultiplyHighS32: context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32; diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index ce57a5788..68bbdeb14 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -37,43 +37,26 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Config = config; - if (config.Stage == ShaderStage.TessellationControl) - { - // Required to index outputs. - Info.Inputs.Add(AttributeConsts.InvocationId); - } - else if (config.GpPassthrough) + if (config.GpPassthrough) { int passthroughAttributes = config.PassthroughAttributes; while (passthroughAttributes != 0) { int index = BitOperations.TrailingZeroCount(passthroughAttributes); - int attrBase = AttributeConsts.UserAttributeBase + index * 16; - Info.Inputs.Add(attrBase); - Info.Inputs.Add(attrBase + 4); - Info.Inputs.Add(attrBase + 8); - Info.Inputs.Add(attrBase + 12); + Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.UserDefined, index)); passthroughAttributes &= ~(1 << index); } - Info.Inputs.Add(AttributeConsts.PositionX); - Info.Inputs.Add(AttributeConsts.PositionY); - Info.Inputs.Add(AttributeConsts.PositionZ); - Info.Inputs.Add(AttributeConsts.PositionW); - Info.Inputs.Add(AttributeConsts.PointSize); - - for (int i = 0; i < 8; i++) - { - Info.Inputs.Add(AttributeConsts.ClipDistance0 + i * 4); - } + Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.Position)); + Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.PointSize)); + Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.ClipDistance)); } else if (config.Stage == ShaderStage.Fragment) { // Potentially used for texture coordinate scaling. - Info.Inputs.Add(AttributeConsts.PositionX); - Info.Inputs.Add(AttributeConsts.PositionY); + Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)); } } @@ -281,7 +264,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - cond = GetOperandUse(branchOp.GetSource(0)); + cond = GetOperand(branchOp.GetSource(0)); Instruction invInst = type == AstBlockType.If ? Instruction.BranchIfTrue @@ -315,41 +298,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return newTemp; } - public AstOperand GetOperandDef(Operand operand) - { - if (operand.Type == OperandType.Attribute) - { - Info.Outputs.Add(operand.Value & AttributeConsts.Mask); - } - else if (operand.Type == OperandType.AttributePerPatch) - { - Info.OutputsPerPatch.Add(operand.Value & AttributeConsts.Mask); - } - - return GetOperand(operand); - } - - public AstOperand GetOperandUse(Operand operand) - { - // If this flag is set, we're reading from an output attribute instead. - if (operand.Type.IsAttribute() && (operand.Value & AttributeConsts.LoadOutputMask) != 0) - { - return GetOperandDef(operand); - } - - if (operand.Type == OperandType.Attribute) - { - Info.Inputs.Add(operand.Value); - } - else if (operand.Type == OperandType.AttributePerPatch) - { - Info.InputsPerPatch.Add(operand.Value); - } - - return GetOperand(operand); - } - - private AstOperand GetOperand(Operand operand) + public AstOperand GetOperand(Operand operand) { if (operand == null) { diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index 489a59105..c51041467 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -22,60 +22,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { public List<StructuredFunction> Functions { get; } - public HashSet<int> Inputs { get; } - public HashSet<int> Outputs { get; } - public HashSet<int> InputsPerPatch { get; } - public HashSet<int> OutputsPerPatch { get; } + public HashSet<IoDefinition> IoDefinitions { get; } public HelperFunctionsMask HelperFunctionsMask { get; set; } - public TransformFeedbackOutput[] TransformFeedbackOutputs { get; } - public StructuredProgramInfo() { Functions = new List<StructuredFunction>(); - Inputs = new HashSet<int>(); - Outputs = new HashSet<int>(); - InputsPerPatch = new HashSet<int>(); - OutputsPerPatch = new HashSet<int>(); - - TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; - } - - public TransformFeedbackOutput GetTransformFeedbackOutput(int attr) - { - int index = attr / 4; - return TransformFeedbackOutputs[index]; - } - - public int GetTransformFeedbackOutputComponents(int attr) - { - int index = attr / 4; - int baseIndex = index & ~3; - - int count = 1; - - for (; count < 4; count++) - { - ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1]; - ref var curr = ref TransformFeedbackOutputs[baseIndex + count]; - - int prevOffset = prev.Offset; - int currOffset = curr.Offset; - - if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset) - { - break; - } - } - - if (baseIndex + count <= index) - { - return 1; - } - - return count; + IoDefinitions = new HashSet<IoDefinition>(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 08efbc9fd..683b0d8ac 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -2,104 +2,35 @@ namespace Ryujinx.Graphics.Shader.Translation { static class AttributeConsts { - public const int TessLevelOuter0 = 0x000; - public const int TessLevelOuter1 = 0x004; - public const int TessLevelOuter2 = 0x008; - public const int TessLevelOuter3 = 0x00c; - public const int TessLevelInner0 = 0x010; - public const int TessLevelInner1 = 0x014; - public const int PrimitiveId = 0x060; - public const int Layer = 0x064; - public const int ViewportIndex = 0x068; - public const int PointSize = 0x06c; - public const int PositionX = 0x070; - public const int PositionY = 0x074; - public const int PositionZ = 0x078; - public const int PositionW = 0x07c; - public const int FrontColorDiffuseR = 0x280; - public const int FrontColorDiffuseG = 0x284; - public const int FrontColorDiffuseB = 0x288; - public const int FrontColorDiffuseA = 0x28c; - public const int FrontColorSpecularR = 0x290; - public const int FrontColorSpecularG = 0x294; - public const int FrontColorSpecularB = 0x298; - public const int FrontColorSpecularA = 0x29c; - public const int BackColorDiffuseR = 0x2a0; - public const int BackColorDiffuseG = 0x2a4; - public const int BackColorDiffuseB = 0x2a8; - public const int BackColorDiffuseA = 0x2ac; - public const int BackColorSpecularR = 0x2b0; - public const int BackColorSpecularG = 0x2b4; - public const int BackColorSpecularB = 0x2b8; - public const int BackColorSpecularA = 0x2bc; - public const int ClipDistance0 = 0x2c0; - public const int ClipDistance1 = 0x2c4; - public const int ClipDistance2 = 0x2c8; - public const int ClipDistance3 = 0x2cc; - public const int ClipDistance4 = 0x2d0; - public const int ClipDistance5 = 0x2d4; - public const int ClipDistance6 = 0x2d8; - public const int ClipDistance7 = 0x2dc; - public const int PointCoordX = 0x2e0; - public const int PointCoordY = 0x2e4; - public const int TessCoordX = 0x2f0; - public const int TessCoordY = 0x2f4; - public const int InstanceId = 0x2f8; - public const int VertexId = 0x2fc; - public const int TexCoordCount = 10; - public const int TexCoordBase = 0x300; - public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16; - public const int FrontFacing = 0x3fc; + public const int PrimitiveId = 0x060; + public const int Layer = 0x064; + public const int PositionX = 0x070; + public const int PositionY = 0x074; + public const int FrontColorDiffuseR = 0x280; + public const int BackColorDiffuseR = 0x2a0; + public const int ClipDistance0 = 0x2c0; + public const int ClipDistance1 = 0x2c4; + public const int ClipDistance2 = 0x2c8; + public const int ClipDistance3 = 0x2cc; + public const int ClipDistance4 = 0x2d0; + public const int ClipDistance5 = 0x2d4; + public const int ClipDistance6 = 0x2d8; + public const int ClipDistance7 = 0x2dc; + public const int FogCoord = 0x2e8; + public const int TessCoordX = 0x2f0; + public const int TessCoordY = 0x2f4; + public const int InstanceId = 0x2f8; + public const int VertexId = 0x2fc; + public const int TexCoordCount = 10; + public const int TexCoordBase = 0x300; + public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16; + public const int FrontFacing = 0x3fc; public const int UserAttributesCount = 32; - public const int UserAttributeBase = 0x80; - public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16; + public const int UserAttributeBase = 0x80; + public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16; public const int UserAttributePerPatchBase = 0x18; - public const int UserAttributePerPatchEnd = 0x200; - - public const int LoadOutputMask = 1 << 30; - public const int Mask = 0x3fffffff; - - - // Note: Those attributes are used internally by the translator - // only, they don't exist on Maxwell. - public const int SpecialMask = 0xf << 24; - public const int FragmentOutputDepth = 0x1000000; - public const int FragmentOutputColorBase = 0x1000010; - public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16; - - public const int FragmentOutputIsBgraBase = 0x1000100; - public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4; - - public const int SupportBlockViewInverseX = 0x1000200; - public const int SupportBlockViewInverseY = 0x1000204; - - public const int ThreadIdX = 0x2000000; - public const int ThreadIdY = 0x2000004; - public const int ThreadIdZ = 0x2000008; - - public const int CtaIdX = 0x2000010; - public const int CtaIdY = 0x2000014; - public const int CtaIdZ = 0x2000018; - - public const int LaneId = 0x2000020; - - public const int InvocationId = 0x2000024; - public const int PatchVerticesIn = 0x2000028; - - public const int EqMask = 0x2000030; - public const int GeMask = 0x2000034; - public const int GtMask = 0x2000038; - public const int LeMask = 0x200003c; - public const int LtMask = 0x2000040; - - public const int ThreadKill = 0x2000044; - - public const int BaseInstance = 0x2000050; - public const int BaseVertex = 0x2000054; - public const int InstanceIndex = 0x2000058; - public const int VertexIndex = 0x200005c; - public const int DrawIndex = 0x2000060; + public const int UserAttributePerPatchEnd = 0x200; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs deleted file mode 100644 index b671429a8..000000000 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ /dev/null @@ -1,210 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Shader.Translation -{ - readonly struct AttributeInfo - { - private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>() - { - { AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) }, - { AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) }, - { AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) }, - { AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) }, - { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, - { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, - { AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) }, - { AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) }, - { AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) }, - { AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) }, - { AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) }, - { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, - - // Special. - { AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) }, - { AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) }, - { AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) }, - { AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) }, - { AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) }, - { AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) }, - { AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) }, - { AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) }, - { AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) }, - { AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) }, - { AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) }, - { AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) }, - { AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, - { AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, - { AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, - { AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, - { AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) }, - }; - - private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>() - { - { AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) }, - { AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) }, - }; - - public int BaseValue { get; } - public int Value { get; } - public int Length { get; } - public AggregateType Type { get; } - public bool IsBuiltin { get; } - public bool IsValid => Type != AggregateType.Invalid; - - public AttributeInfo(int baseValue, int index, int length, AggregateType type, bool isBuiltin = true) - { - BaseValue = baseValue; - Value = baseValue + index * 4; - Length = length; - Type = type; - IsBuiltin = isBuiltin; - } - - public int GetInnermostIndex() - { - return (Value - BaseValue) / 4; - } - - public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch) - { - return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr); - } - - public static bool Validate(ShaderConfig config, int value, bool isOutAttr) - { - if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex()) - { - return false; - } - - return From(config, value, isOutAttr).IsValid; - } - - public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr) - { - return FromPatch(config, value, isOutAttr).IsValid; - } - - public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr) - { - value &= ~3; - - if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) - { - int location = (value - AttributeConsts.UserAttributeBase) / 16; - - AggregateType elemType; - - if (config.Stage == ShaderStage.Vertex && !isOutAttr) - { - elemType = config.GpuAccessor.QueryAttributeType(location).ToAggregateType(); - } - else - { - elemType = AggregateType.FP32; - } - - return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false); - } - else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) - { - int location = (value - AttributeConsts.FragmentOutputColorBase) / 16; - var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch - { - AttributeType.Sint => AggregateType.S32, - AttributeType.Uint => AggregateType.U32, - _ => AggregateType.FP32 - }; - - return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false); - } - else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY) - { - return new AttributeInfo(value, 0, 1, AggregateType.FP32); - } - else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info)) - { - return info; - } - - return new AttributeInfo(value, 0, 0, AggregateType.Invalid); - } - - public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr) - { - value &= ~3; - - if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd) - { - int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf; - return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false); - } - else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info)) - { - return info; - } - - return new AttributeInfo(value, 0, 0, AggregateType.Invalid); - } - - public static bool IsArrayBuiltIn(int attr) - { - if (attr <= AttributeConsts.TessLevelInner1 || - attr == AttributeConsts.TessCoordX || - attr == AttributeConsts.TessCoordY) - { - return false; - } - - return (attr & AttributeConsts.SpecialMask) == 0; - } - - public static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr) - { - if (isOutAttr) - { - return stage == ShaderStage.TessellationControl; - } - else - { - return stage == ShaderStage.TessellationControl || - stage == ShaderStage.TessellationEvaluation || - stage == ShaderStage.Geometry; - } - } - - public static bool IsArrayAttributeSpirv(ShaderStage stage, bool isOutAttr) - { - if (isOutAttr) - { - return false; - } - else - { - return stage == ShaderStage.TessellationControl || - stage == ShaderStage.TessellationEvaluation || - stage == ShaderStage.Geometry; - } - } - } -} diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 8f33cceda..e81f64252 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Shader.Translation (Config.Options.Flags & TranslationFlags.VertexA) == 0) { // Vulkan requires the point size to be always written on the shader if the primitive topology is points. - this.Copy(Attribute(AttributeConsts.PointSize), ConstF(Config.GpuAccessor.QueryPointSize())); + this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(Config.GpuAccessor.QueryPointSize())); } } @@ -87,6 +87,15 @@ namespace Ryujinx.Graphics.Shader.Translation return dest; } + public Operand Add(Instruction inst, StorageKind storageKind, Operand dest = null, params Operand[] sources) + { + Operation operation = new Operation(inst, storageKind, dest, sources); + + _operations.Add(operation); + + return dest; + } + public (Operand, Operand) Add(Instruction inst, (Operand, Operand) dest, params Operand[] sources) { Operand[] dests = new[] { dest.Item1, dest.Item2 }; @@ -223,30 +232,35 @@ namespace Ryujinx.Graphics.Shader.Translation { if (Config.GpuAccessor.QueryViewportTransformDisable()) { - Operand x = Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask); - Operand y = Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask); - Operand xScale = Attribute(AttributeConsts.SupportBlockViewInverseX); - Operand yScale = Attribute(AttributeConsts.SupportBlockViewInverseY); + Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)); + Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)); + Operand xScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(0)); + Operand yScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(1)); Operand negativeOne = ConstF(-1.0f); - this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne)); - this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne)); + this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne)); + this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne)); } if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne()) { - Operand z = Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask); - Operand w = Attribute(AttributeConsts.PositionW | AttributeConsts.LoadOutputMask); + Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)); + Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3)); Operand halfW = this.FPMultiply(w, ConstF(0.5f)); - this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW)); + this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW)); } if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute) { Config.SetUsedFeature(FeatureFlags.RtLayer); - this.Copy(Attribute(AttributeConsts.Layer), Attribute(Config.GpLayerInputAttribute | AttributeConsts.LoadOutputMask)); + int attrVecIndex = Config.GpLayerInputAttribute >> 2; + int attrComponentIndex = Config.GpLayerInputAttribute & 3; + + Operand layer = this.Load(StorageKind.Output, IoVariable.UserDefined, null, Const(attrVecIndex), Const(attrComponentIndex)); + + this.Store(StorageKind.Output, IoVariable.Layer, null, layer); } } @@ -255,9 +269,9 @@ namespace Ryujinx.Graphics.Shader.Translation if (Config.GpuAccessor.QueryViewportTransformDisable()) { oldXLocal = Local(); - this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask)); + this.Copy(oldXLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(0))); oldYLocal = Local(); - this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask)); + this.Copy(oldYLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(1))); } else { @@ -268,7 +282,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne()) { oldZLocal = Local(); - this.Copy(oldZLocal, Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask)); + this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2))); } else { @@ -293,17 +307,30 @@ namespace Ryujinx.Graphics.Shader.Translation } else if (Config.Stage == ShaderStage.Geometry) { - void WriteOutput(int index, int primIndex) + void WritePositionOutput(int primIndex) { - Operand x = this.LoadAttribute(Const(index), Const(0), Const(primIndex)); - Operand y = this.LoadAttribute(Const(index + 4), Const(0), Const(primIndex)); - Operand z = this.LoadAttribute(Const(index + 8), Const(0), Const(primIndex)); - Operand w = this.LoadAttribute(Const(index + 12), Const(0), Const(primIndex)); + Operand x = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(0)); + Operand y = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(1)); + Operand z = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(2)); + Operand w = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(3)); - this.Copy(Attribute(index), x); - this.Copy(Attribute(index + 4), y); - this.Copy(Attribute(index + 8), z); - this.Copy(Attribute(index + 12), w); + this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), x); + this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), y); + this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), z); + this.Store(StorageKind.Output, IoVariable.Position, null, Const(3), w); + } + + void WriteUserDefinedOutput(int index, int primIndex) + { + Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(0)); + Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(1)); + Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(2)); + Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(3)); + + this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(0), x); + this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(1), y); + this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(2), z); + this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(3), w); } if (Config.GpPassthrough && !Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) @@ -312,13 +339,13 @@ namespace Ryujinx.Graphics.Shader.Translation for (int primIndex = 0; primIndex < inputVertices; primIndex++) { - WriteOutput(AttributeConsts.PositionX, primIndex); + WritePositionOutput(primIndex); int passthroughAttributes = Config.PassthroughAttributes; while (passthroughAttributes != 0) { int index = BitOperations.TrailingZeroCount(passthroughAttributes); - WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex); + WriteUserDefinedOutput(index, primIndex); Config.SetOutputUserAttribute(index); passthroughAttributes &= ~(1 << index); } @@ -337,11 +364,9 @@ namespace Ryujinx.Graphics.Shader.Translation if (Config.OmapDepth) { - Operand dest = Attribute(AttributeConsts.FragmentOutputDepth); - Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr); - this.Copy(dest, src); + this.Store(StorageKind.Output, IoVariable.FragmentOutputDepth, null, src); } AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare(); @@ -390,32 +415,30 @@ namespace Ryujinx.Graphics.Shader.Translation continue; } - int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16; - Operand src = Register(regIndexBase + component, RegisterType.Gpr); // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL). if (!supportsBgra && (component == 0 || component == 2)) { - Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4); + Operand isBgra = this.Load(StorageKind.Input, IoVariable.FragmentOutputIsBgra, null, Const(rtIndex)); Operand lblIsBgra = Label(); Operand lblEnd = Label(); this.BranchIfTrue(lblIsBgra, isBgra); - this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); + this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src); this.Branch(lblEnd); MarkLabel(lblIsBgra); - this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src); + this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(2 - component), src); MarkLabel(lblEnd); } else { - this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); + this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src); } } @@ -441,8 +464,11 @@ namespace Ryujinx.Graphics.Shader.Translation // 11 01 01 01 01 00 00 00 Operand ditherMask = Const(unchecked((int)0xfbb99110u)); - Operand x = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionX)), Const(1)); - Operand y = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionY)), Const(1)); + Operand fragCoordX = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0)); + Operand fragCoordY = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1)); + + Operand x = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordX), Const(1)); + Operand y = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordY), Const(1)); Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1))); Operand alpha = Register(3, RegisterType.Gpr); diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 1fb605089..937482495 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -7,54 +7,54 @@ namespace Ryujinx.Graphics.Shader.Translation { static class EmitterContextInsts { - public static Operand AtomicAdd(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicAdd(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicAdd | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicAdd, storageKind, Local(), a, b, c); } - public static Operand AtomicAnd(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicAnd(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicAnd | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicAnd, storageKind, Local(), a, b, c); } - public static Operand AtomicCompareAndSwap(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c, Operand d) + public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c, Operand d) { - return context.Add(Instruction.AtomicCompareAndSwap | mr, Local(), a, b, c, d); + return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), a, b, c, d); } - public static Operand AtomicMaxS32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicMaxS32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicMaxS32 | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicMaxS32, storageKind, Local(), a, b, c); } - public static Operand AtomicMaxU32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicMaxU32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicMaxU32 | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicMaxU32, storageKind, Local(), a, b, c); } - public static Operand AtomicMinS32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicMinS32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicMinS32 | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicMinS32, storageKind, Local(), a, b, c); } - public static Operand AtomicMinU32(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicMinU32(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicMinU32 | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicMinU32, storageKind, Local(), a, b, c); } - public static Operand AtomicOr(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicOr(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicOr | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicOr, storageKind, Local(), a, b, c); } - public static Operand AtomicSwap(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicSwap(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicSwap | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicSwap, storageKind, Local(), a, b, c); } - public static Operand AtomicXor(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + public static Operand AtomicXor(this EmitterContext context, StorageKind storageKind, Operand a, Operand b, Operand c) { - return context.Add(Instruction.AtomicXor | mr, Local(), a, b, c); + return context.Add(Instruction.AtomicXor, storageKind, Local(), a, b, c); } public static Operand Ballot(this EmitterContext context, Operand a) @@ -549,9 +549,36 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(fpType | Instruction.IsNan, Local(), a); } - public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b, Operand c) + public static Operand Load(this EmitterContext context, StorageKind storageKind, IoVariable ioVariable, Operand primVertex = null) { - return context.Add(Instruction.LoadAttribute, Local(), a, b, c); + return primVertex != null + ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex) + : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable)); + } + + public static Operand Load( + this EmitterContext context, + StorageKind storageKind, + IoVariable ioVariable, + Operand primVertex, + Operand elemIndex) + { + return primVertex != null + ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, elemIndex) + : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), elemIndex); + } + + public static Operand Load( + this EmitterContext context, + StorageKind storageKind, + IoVariable ioVariable, + Operand primVertex, + Operand arrayIndex, + Operand elemIndex) + { + return primVertex != null + ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, arrayIndex, elemIndex) + : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), arrayIndex, elemIndex); } public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b) @@ -662,9 +689,43 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c); } - public static Operand StoreAttribute(this EmitterContext context, Operand a, Operand b, Operand c) + public static Operand Store( + this EmitterContext context, + StorageKind storageKind, + IoVariable ioVariable, + Operand invocationId, + Operand value) { - return context.Add(Instruction.StoreAttribute, null, a, b, c); + return invocationId != null + ? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, value) + : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), value); + } + + public static Operand Store( + this EmitterContext context, + StorageKind storageKind, + IoVariable ioVariable, + Operand invocationId, + Operand elemIndex, + Operand value) + { + return invocationId != null + ? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, elemIndex, value) + : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), elemIndex, value); + } + + public static Operand Store( + this EmitterContext context, + StorageKind storageKind, + IoVariable ioVariable, + Operand invocationId, + Operand arrayIndex, + Operand elemIndex, + Operand value) + { + return invocationId != null + ? context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), invocationId, arrayIndex, elemIndex, value) + : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value); } public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c) diff --git a/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs b/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs index 3915c0d55..774a128d8 100644 --- a/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs +++ b/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs @@ -16,20 +16,15 @@ namespace Ryujinx.Graphics.Shader.Translation public const int UbeDescsSize = StorageDescSize * UbeMaxCount; public const int UbeFirstCbuf = 8; - public static bool UsesGlobalMemory(Instruction inst) + public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind) { - return (inst.IsAtomic() && IsGlobalMr(inst)) || + return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) || inst == Instruction.LoadGlobal || inst == Instruction.StoreGlobal || inst == Instruction.StoreGlobal16 || inst == Instruction.StoreGlobal8; } - private static bool IsGlobalMr(Instruction inst) - { - return (inst & Instruction.MrMask) == Instruction.MrGlobal; - } - public static int GetStorageCbOffset(ShaderStage stage, int slot) { return GetStorageBaseCbOffset(stage) + slot * StorageDescSize; diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index c280a6d80..2a4070e0a 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - if (UsesGlobalMemory(operation.Inst)) + if (UsesGlobalMemory(operation.Inst, operation.StorageKind)) { Operand source = operation.GetSource(0); @@ -104,9 +104,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (isAtomic) { - Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; - - storageOp = new Operation(inst, operation.Dest, sources); + storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources); } else if (operation.Inst == Instruction.LoadGlobal) { diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index a2219b36d..bae774ee4 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -170,10 +170,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return false; } - return x.Type == OperandType.Attribute || - x.Type == OperandType.AttributePerPatch || - x.Type == OperandType.Constant || - x.Type == OperandType.ConstantBuffer; + // TODO: Handle Load operations with the same storage and the same constant parameters. + return x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer; } private static bool PropagatePack(Operation packOp) diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 3ec4e49a7..91e7ace1e 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.Translation { if (hasConstantBufferDrawParameters) { - if (ReplaceConstantBufferWithDrawParameters(operation)) + if (ReplaceConstantBufferWithDrawParameters(node, operation)) { config.SetUsedFeature(FeatureFlags.DrawParameters); } @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Translation nextNode = node.Next; } - else if (UsesGlobalMemory(operation.Inst)) + else if (UsesGlobalMemory(operation.Inst, operation.StorageKind)) { nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode; } @@ -169,9 +169,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (isAtomic) { - Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; - - storageOp = new Operation(inst, operation.Dest, sources); + storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources); } else if (operation.Inst == Instruction.LoadGlobal) { @@ -708,8 +706,15 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static bool ReplaceConstantBufferWithDrawParameters(Operation operation) + private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation) { + Operand GenerateLoad(IoVariable ioVariable) + { + Operand value = Local(); + node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.Input, value, Const((int)ioVariable))); + return value; + } + bool modified = false; for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) @@ -721,15 +726,15 @@ namespace Ryujinx.Graphics.Shader.Translation switch (src.GetCbufOffset()) { case Constants.NvnBaseVertexByteOffset / 4: - operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseVertex)); + operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseVertex)); modified = true; break; case Constants.NvnBaseInstanceByteOffset / 4: - operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseInstance)); + operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseInstance)); modified = true; break; case Constants.NvnDrawIndexByteOffset / 4: - operation.SetSource(srcIndex, Attribute(AttributeConsts.DrawIndex)); + operation.SetSource(srcIndex, GenerateLoad(IoVariable.DrawIndex)); modified = true; break; } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 15eb7ed1f..22f5a671d 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -41,6 +41,46 @@ namespace Ryujinx.Graphics.Shader.Translation public bool TransformFeedbackEnabled { get; } + private TransformFeedbackOutput[] _transformFeedbackOutputs; + + readonly struct TransformFeedbackVariable : IEquatable<TransformFeedbackVariable> + { + public IoVariable IoVariable { get; } + public int Location { get; } + public int Component { get; } + + public TransformFeedbackVariable(IoVariable ioVariable, int location = 0, int component = 0) + { + IoVariable = ioVariable; + Location = location; + Component = component; + } + + public override bool Equals(object other) + { + return other is TransformFeedbackVariable tfbVar && Equals(tfbVar); + } + + public bool Equals(TransformFeedbackVariable other) + { + return IoVariable == other.IoVariable && + Location == other.Location && + Component == other.Component; + } + + public override int GetHashCode() + { + return (int)IoVariable | (Location << 8) | (Component << 16); + } + + public override string ToString() + { + return $"{IoVariable}.{Location}.{Component}"; + } + } + + private readonly Dictionary<TransformFeedbackVariable, TransformFeedbackOutput> _transformFeedbackDefinitions; + public int Size { get; private set; } public byte ClipDistancesWritten { get; private set; } @@ -102,6 +142,8 @@ namespace Ryujinx.Graphics.Shader.Translation GpuAccessor = gpuAccessor; Options = options; + _transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>(); + AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1; AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1; @@ -147,6 +189,173 @@ namespace Ryujinx.Graphics.Shader.Translation LastInVertexPipeline = header.Stage < ShaderStage.Fragment; } + private void EnsureTransformFeedbackInitialized() + { + if (HasTransformFeedbackOutputs() && _transformFeedbackOutputs == null) + { + TransformFeedbackOutput[] transformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; + ulong vecMap = 0UL; + + for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++) + { + var locations = GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); + var stride = GpuAccessor.QueryTransformFeedbackStride(tfbIndex); + + for (int i = 0; i < locations.Length; i++) + { + byte wordOffset = locations[i]; + if (wordOffset < 0xc0) + { + transformFeedbackOutputs[wordOffset] = new TransformFeedbackOutput(tfbIndex, i * 4, stride); + vecMap |= 1UL << (wordOffset / 4); + } + } + } + + _transformFeedbackOutputs = transformFeedbackOutputs; + + while (vecMap != 0) + { + int vecIndex = BitOperations.TrailingZeroCount(vecMap); + + for (int subIndex = 0; subIndex < 4; subIndex++) + { + int wordOffset = vecIndex * 4 + subIndex; + int byteOffset = wordOffset * 4; + + if (transformFeedbackOutputs[wordOffset].Valid) + { + IoVariable ioVariable = Instructions.AttributeMap.GetIoVariable(this, byteOffset, out int location); + int component = 0; + + if (HasPerLocationInputOrOutputComponent(ioVariable, location, subIndex, isOutput: true)) + { + component = subIndex; + } + + var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component); + _transformFeedbackDefinitions.TryAdd(transformFeedbackVariable, transformFeedbackOutputs[wordOffset]); + } + } + + vecMap &= ~(1UL << vecIndex); + } + } + } + + public TransformFeedbackOutput[] GetTransformFeedbackOutputs() + { + EnsureTransformFeedbackInitialized(); + return _transformFeedbackOutputs; + } + + public bool TryGetTransformFeedbackOutput(IoVariable ioVariable, int location, int component, out TransformFeedbackOutput transformFeedbackOutput) + { + EnsureTransformFeedbackInitialized(); + var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component); + return _transformFeedbackDefinitions.TryGetValue(transformFeedbackVariable, out transformFeedbackOutput); + } + + private bool HasTransformFeedbackOutputs() + { + return TransformFeedbackEnabled && (LastInVertexPipeline || Stage == ShaderStage.Fragment); + } + + public bool HasTransformFeedbackOutputs(bool isOutput) + { + return TransformFeedbackEnabled && ((isOutput && LastInVertexPipeline) || (!isOutput && Stage == ShaderStage.Fragment)); + } + + public bool HasPerLocationInputOrOutput(IoVariable ioVariable, bool isOutput) + { + if (ioVariable == IoVariable.UserDefined) + { + return (!isOutput && !UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) || + (isOutput && !UsedFeatures.HasFlag(FeatureFlags.OaIndexing)); + } + + return ioVariable == IoVariable.FragmentOutputColor; + } + + public bool HasPerLocationInputOrOutputComponent(IoVariable ioVariable, int location, int component, bool isOutput) + { + if (ioVariable != IoVariable.UserDefined || !HasTransformFeedbackOutputs(isOutput)) + { + return false; + } + + return GetTransformFeedbackOutputComponents(location, component) == 1; + } + + public TransformFeedbackOutput GetTransformFeedbackOutput(int wordOffset) + { + EnsureTransformFeedbackInitialized(); + + return _transformFeedbackOutputs[wordOffset]; + } + + public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component) + { + return GetTransformFeedbackOutput((AttributeConsts.UserAttributeBase / 4) + location * 4 + component); + } + + public int GetTransformFeedbackOutputComponents(int location, int component) + { + EnsureTransformFeedbackInitialized(); + + int baseIndex = (AttributeConsts.UserAttributeBase / 4) + location * 4; + int index = baseIndex + component; + int count = 1; + + for (; count < 4; count++) + { + ref var prev = ref _transformFeedbackOutputs[baseIndex + count - 1]; + ref var curr = ref _transformFeedbackOutputs[baseIndex + count]; + + int prevOffset = prev.Offset; + int currOffset = curr.Offset; + + if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset) + { + break; + } + } + + if (baseIndex + count <= index) + { + return 1; + } + + return count; + } + + public AggregateType GetFragmentOutputColorType(int location) + { + return AggregateType.Vector4 | GpuAccessor.QueryFragmentOutputType(location).ToAggregateType(); + } + + public AggregateType GetUserDefinedType(int location, bool isOutput) + { + if ((!isOutput && UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) || + (isOutput && UsedFeatures.HasFlag(FeatureFlags.OaIndexing))) + { + return AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32; + } + + AggregateType type = AggregateType.Vector4; + + if (Stage == ShaderStage.Vertex && !isOutput) + { + type |= GpuAccessor.QueryAttributeType(location).ToAggregateType(); + } + else + { + type |= AggregateType.FP32; + } + + return type; + } + public int GetDepthRegister() { // The depth register is always two registers after the last color output. @@ -184,7 +393,7 @@ namespace Ryujinx.Graphics.Shader.Translation return format; } - private bool FormatSupportsAtomic(TextureFormat format) + private static bool FormatSupportsAtomic(TextureFormat format) { return format == TextureFormat.R32Sint || format == TextureFormat.R32Uint; } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs index 206718f2a..53f1e8475 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -53,40 +53,80 @@ namespace Ryujinx.Graphics.Shader.Translation return false; } - if (operation.Inst == Instruction.StoreAttribute) + if (operation.Inst == Instruction.Store && operation.StorageKind == StorageKind.Output) { - return false; - } + Operand src = operation.GetSource(operation.SourcesCount - 1); + Operation srcAttributeAsgOp = null; - if (operation.Inst == Instruction.Copy && operation.Dest.Type == OperandType.Attribute) - { - Operand src = operation.GetSource(0); - - if (src.Type == OperandType.LocalVariable && src.AsgOp is Operation asgOp && asgOp.Inst == Instruction.LoadAttribute) + if (src.Type == OperandType.LocalVariable && + src.AsgOp is Operation asgOp && + asgOp.Inst == Instruction.Load && + asgOp.StorageKind.IsInputOrOutput()) { - src = Attribute(asgOp.GetSource(0).Value); + if (asgOp.StorageKind != StorageKind.Input) + { + return false; + } + + srcAttributeAsgOp = asgOp; } - if (src.Type == OperandType.Attribute) + if (srcAttributeAsgOp != null) { - if (operation.Dest.Value == AttributeConsts.Layer) + IoVariable dstAttribute = (IoVariable)operation.GetSource(0).Value; + IoVariable srcAttribute = (IoVariable)srcAttributeAsgOp.GetSource(0).Value; + + if (dstAttribute == IoVariable.Layer && srcAttribute == IoVariable.UserDefined) { - if ((src.Value & AttributeConsts.LoadOutputMask) != 0) + if (srcAttributeAsgOp.SourcesCount != 4) { return false; } writesLayer = true; - layerInputAttr = src.Value; + layerInputAttr = srcAttributeAsgOp.GetSource(1).Value * 4 + srcAttributeAsgOp.GetSource(3).Value;; } - else if (src.Value != operation.Dest.Value) + else { - return false; + if (dstAttribute != srcAttribute) + { + return false; + } + + int inputsCount = operation.SourcesCount - 2; + + if (dstAttribute == IoVariable.UserDefined) + { + if (operation.GetSource(1).Value != srcAttributeAsgOp.GetSource(1).Value) + { + return false; + } + + inputsCount--; + } + + for (int i = 0; i < inputsCount; i++) + { + int dstIndex = operation.SourcesCount - 2 - i; + int srcIndex = srcAttributeAsgOp.SourcesCount - 1 - i; + + if ((dstIndex | srcIndex) < 0) + { + return false; + } + + if (operation.GetSource(dstIndex).Type != OperandType.Constant || + srcAttributeAsgOp.GetSource(srcIndex).Type != OperandType.Constant || + operation.GetSource(dstIndex).Value != srcAttributeAsgOp.GetSource(srcIndex).Value) + { + return false; + } + } } } else if (src.Type == OperandType.Constant) { - int dstComponent = (operation.Dest.Value >> 2) & 3; + int dstComponent = operation.GetSource(operation.SourcesCount - 2).Value; float expectedValue = dstComponent == 3 ? 1f : 0f; if (src.AsFloat() != expectedValue) diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 6a1230458..77d3b568e 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (config.Stage == ShaderStage.Vertex) { - InitializeOutput(context, AttributeConsts.PositionX, perPatch: false); + InitializePositionOutput(context); } UInt128 usedAttributes = context.Config.NextInputAttributesComponents; @@ -194,20 +194,23 @@ namespace Ryujinx.Graphics.Shader.Translation continue; } - InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false); + InitializeOutputComponent(context, vecIndex, index & 3, perPatch: false); } if (context.Config.NextUsedInputAttributesPerPatch != null) { foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.Order()) { - InitializeOutput(context, AttributeConsts.UserAttributePerPatchBase + vecIndex * 16, perPatch: true); + InitializeOutput(context, vecIndex, perPatch: true); } } if (config.NextUsesFixedFuncAttributes) { - for (int i = 0; i < 4 + AttributeConsts.TexCoordCount; i++) + bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation(); + int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1; + + for (int i = fixedStartAttr; i < fixedStartAttr + 5 + AttributeConsts.TexCoordCount; i++) { int index = config.GetFreeUserAttribute(isOutput: true, i); if (index < 0) @@ -215,26 +218,58 @@ namespace Ryujinx.Graphics.Shader.Translation break; } - InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false); + InitializeOutput(context, index, perPatch: false); config.SetOutputUserAttributeFixedFunc(index); } } } - private static void InitializeOutput(EmitterContext context, int baseAttr, bool perPatch) + private static void InitializePositionOutput(EmitterContext context) { for (int c = 0; c < 4; c++) { - int attrOffset = baseAttr + c * 4; - InitializeOutputComponent(context, attrOffset, perPatch); + context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f)); } } - private static void InitializeOutputComponent(EmitterContext context, int attrOffset, bool perPatch) + private static void InitializeOutput(EmitterContext context, int location, bool perPatch) { - int c = (attrOffset >> 2) & 3; - context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f)); + for (int c = 0; c < 4; c++) + { + InitializeOutputComponent(context, location, c, perPatch); + } + } + + private static void InitializeOutputComponent(EmitterContext context, int location, int c, bool perPatch) + { + StorageKind storageKind = perPatch ? StorageKind.OutputPerPatch : StorageKind.Output; + + if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing)) + { + Operand invocationId = null; + + if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch) + { + invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId); + } + + int index = location * 4 + c; + + context.Store(storageKind, IoVariable.UserDefined, invocationId, Const(index), ConstF(c == 3 ? 1f : 0f)); + } + else + { + if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch) + { + Operand invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId); + context.Store(storageKind, IoVariable.UserDefined, Const(location), invocationId, Const(c), ConstF(c == 3 ? 1f : 0f)); + } + else + { + context.Store(storageKind, IoVariable.UserDefined, null, Const(location), Const(c), ConstF(c == 3 ? 1f : 0f)); + } + } } private static void EmitOps(EmitterContext context, Block block) diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 856b16b7d..4a304f3a8 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -34,15 +34,16 @@ namespace Ryujinx.Graphics.Shader.Translation _config = config; } - private static bool IsUserAttribute(Operand operand) + private static bool IsLoadUserDefined(Operation operation) { - if (operand != null && operand.Type.IsAttribute()) - { - int value = operand.Value & AttributeConsts.Mask; - return value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd; - } + // TODO: Check if sources count match and all sources are constant. + return operation.Inst == Instruction.Load && (IoVariable)operation.GetSource(0).Value == IoVariable.UserDefined; + } - return false; + private static bool IsStoreUserDefined(Operation operation) + { + // TODO: Check if sources count match and all sources are constant. + return operation.Inst == Instruction.Store && (IoVariable)operation.GetSource(0).Value == IoVariable.UserDefined; } private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b, int aStart) @@ -68,9 +69,9 @@ namespace Ryujinx.Graphics.Shader.Translation { Operation operation = a[0].Code[index]; - if (IsUserAttribute(operation.Dest)) + if (IsStoreUserDefined(operation)) { - int tIndex = (operation.Dest.Value - AttributeConsts.UserAttributeBase) / 4; + int tIndex = operation.GetSource(1).Value * 4 + operation.GetSource(2).Value; Operand temp = temps[tIndex]; @@ -82,6 +83,7 @@ namespace Ryujinx.Graphics.Shader.Translation } operation.Dest = temp; + operation.TurnIntoCopy(operation.GetSource(operation.SourcesCount - 1)); } if (operation.Inst == Instruction.Return) @@ -100,18 +102,15 @@ namespace Ryujinx.Graphics.Shader.Translation { Operation operation = b[0].Code[index]; - for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + if (IsLoadUserDefined(operation)) { - Operand src = operation.GetSource(srcIndex); + int tIndex = operation.GetSource(1).Value * 4 + operation.GetSource(2).Value; - if (IsUserAttribute(src)) + Operand temp = temps[tIndex]; + + if (temp != null) { - Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4]; - - if (temp != null) - { - operation.SetSource(srcIndex, temp); - } + operation.TurnIntoCopy(temp); } } @@ -209,15 +208,15 @@ namespace Ryujinx.Graphics.Shader.Translation { int attr = AttributeConsts.UserAttributeBase + attrIndex * 16 + c * 4; - Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v)); + Operand value = context.Load(StorageKind.Input, IoVariable.UserDefined, Const(v), Const(attrIndex), Const(c)); if (attr == layerOutputAttr) { - context.Copy(Attribute(AttributeConsts.Layer), value); + context.Store(StorageKind.Output, IoVariable.Layer, null, value); } else { - context.Copy(Attribute(attr), value); + context.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(attrIndex), Const(c), value); config.SetOutputUserAttribute(attrIndex); } @@ -227,11 +226,9 @@ namespace Ryujinx.Graphics.Shader.Translation for (int c = 0; c < 4; c++) { - int attr = AttributeConsts.PositionX + c * 4; + Operand value = context.Load(StorageKind.Input, IoVariable.Position, Const(v), Const(c)); - Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v)); - - context.Copy(Attribute(attr), value); + context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), value); } context.EmitVertex(); diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index e206bb299..ab82d7b4a 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -40,6 +40,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsPreciseOcclusionQueries; public readonly bool SupportsPipelineStatisticsQuery; public readonly bool SupportsGeometryShader; + public readonly bool SupportsViewportArray2; public readonly uint MinSubgroupSize; public readonly uint MaxSubgroupSize; public readonly ShaderStageFlags RequiredSubgroupSizeStages; @@ -73,6 +74,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsPreciseOcclusionQueries, bool supportsPipelineStatisticsQuery, bool supportsGeometryShader, + bool supportsViewportArray2, uint minSubgroupSize, uint maxSubgroupSize, ShaderStageFlags requiredSubgroupSizeStages, @@ -105,6 +107,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery; SupportsGeometryShader = supportsGeometryShader; + SupportsViewportArray2 = supportsViewportArray2; MinSubgroupSize = minSubgroupSize; MaxSubgroupSize = maxSubgroupSize; RequiredSubgroupSizeStages = requiredSubgroupSizeStages; diff --git a/Ryujinx.Graphics.Vulkan/Shader.cs b/Ryujinx.Graphics.Vulkan/Shader.cs index 26d0ca408..ca99ebf07 100644 --- a/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/Ryujinx.Graphics.Vulkan/Shader.cs @@ -76,10 +76,6 @@ namespace Ryujinx.Graphics.Vulkan private unsafe static byte[] GlslToSpirv(string glsl, ShaderStage stage) { - // TODO: We should generate the correct code on the shader translator instead of doing this compensation. - glsl = glsl.Replace("gl_VertexID", "(gl_VertexIndex - gl_BaseVertex)"); - glsl = glsl.Replace("gl_InstanceID", "(gl_InstanceIndex - gl_BaseInstance)"); - Options options; lock (_shaderOptionsLock) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 4f69cb1d2..50a6fcb90 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -39,7 +39,8 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_shader_subgroup_ballot", "VK_EXT_subgroup_size_control", "VK_NV_geometry_shader_passthrough", - "VK_KHR_portability_subset", // By spec, we should enable this if present. + "VK_NV_viewport_array2", + "VK_KHR_portability_subset" // As per spec, we should enable this if present. }; private static readonly string[] _requiredExtensions = new string[] diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 1c295d6ff..e7475b6b3 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -306,6 +306,7 @@ namespace Ryujinx.Graphics.Vulkan features2.Features.OcclusionQueryPrecise, _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery, _physicalDevice.PhysicalDeviceFeatures.GeometryShader, + _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, @@ -568,7 +569,8 @@ namespace Ryujinx.Graphics.Vulkan supportsNonConstantTextureOffset: false, supportsShaderBallot: false, supportsTextureShadowLod: false, - supportsViewportIndex: featuresVk12.ShaderOutputViewportIndex, + supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex, + supportsViewportMask: Capabilities.SupportsViewportArray2, supportsViewportSwizzle: false, supportsIndirectParameters: true, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 746b780c0..3acebbda6 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -86,8 +86,8 @@ namespace Ryujinx.ShaderTools static void Main(string[] args) { Parser.Default.ParseArguments<Options>(args) - .WithParsed(options => HandleArguments(options)) - .WithNotParsed(errors => errors.Output()); + .WithParsed(options => HandleArguments(options)) + .WithNotParsed(errors => errors.Output()); } } } \ No newline at end of file From cd124bda587ef09668a971fa1cac1c3f0cfc9f21 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 27 Apr 2023 11:09:49 -0300 Subject: [PATCH 471/737] Fix geometry shader layer passthrough regression (#4735) * Fix geometry shader layer passthrough regression * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Shader/Translation/EmitterContext.cs | 8 ++++---- Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 78f9763fb..b182f2995 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4565; + private const uint CodeGenVersion = 4735; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index e81f64252..112baccf6 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -322,10 +322,10 @@ namespace Ryujinx.Graphics.Shader.Translation void WriteUserDefinedOutput(int index, int primIndex) { - Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(0)); - Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(1)); - Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(2)); - Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(primIndex), Const(index), Const(3)); + Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(0)); + Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(1)); + Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(2)); + Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(3)); this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(0), x); this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(1), y); diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 4a304f3a8..4b4cc8d9f 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int attr = AttributeConsts.UserAttributeBase + attrIndex * 16 + c * 4; - Operand value = context.Load(StorageKind.Input, IoVariable.UserDefined, Const(v), Const(attrIndex), Const(c)); + Operand value = context.Load(StorageKind.Input, IoVariable.UserDefined, Const(attrIndex), Const(v), Const(c)); if (attr == layerOutputAttr) { From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:22:00 +0200 Subject: [PATCH 472/737] Move solution and projects to src --- Ryujinx.sln | 80 +++++++++--------- .../ARMeilleure}/ARMeilleure.csproj | 0 .../ARMeilleure}/Allocators.cs | 0 .../CodeGen/Arm64/Arm64Optimizer.cs | 0 .../CodeGen/Arm64/ArmCondition.cs | 0 .../CodeGen/Arm64/ArmExtensionType.cs | 0 .../CodeGen/Arm64/ArmShiftType.cs | 0 .../ARMeilleure}/CodeGen/Arm64/Assembler.cs | 0 .../CodeGen/Arm64/CallingConvention.cs | 0 .../CodeGen/Arm64/CodeGenCommon.cs | 0 .../CodeGen/Arm64/CodeGenContext.cs | 0 .../CodeGen/Arm64/CodeGenerator.cs | 0 .../CodeGen/Arm64/CodeGeneratorIntrinsic.cs | 0 .../CodeGen/Arm64/HardwareCapabilities.cs | 0 .../CodeGen/Arm64/IntrinsicInfo.cs | 0 .../CodeGen/Arm64/IntrinsicTable.cs | 0 .../CodeGen/Arm64/IntrinsicType.cs | 0 .../CodeGen/Arm64/PreAllocator.cs | 0 .../ARMeilleure}/CodeGen/CompiledFunction.cs | 0 .../CodeGen/Linking/RelocEntry.cs | 0 .../ARMeilleure}/CodeGen/Linking/RelocInfo.cs | 0 .../ARMeilleure}/CodeGen/Linking/Symbol.cs | 0 .../CodeGen/Linking/SymbolType.cs | 0 .../CodeGen/Optimizations/BlockPlacement.cs | 0 .../CodeGen/Optimizations/ConstantFolding.cs | 0 .../CodeGen/Optimizations/Optimizer.cs | 0 .../CodeGen/Optimizations/Simplification.cs | 0 .../CodeGen/Optimizations/TailMerge.cs | 0 .../CodeGen/PreAllocatorCommon.cs | 0 .../RegisterAllocators/AllocationResult.cs | 0 .../RegisterAllocators/CopyResolver.cs | 0 .../RegisterAllocators/HybridAllocator.cs | 0 .../RegisterAllocators/IRegisterAllocator.cs | 0 .../RegisterAllocators/LinearScanAllocator.cs | 0 .../RegisterAllocators/LiveInterval.cs | 0 .../RegisterAllocators/LiveIntervalList.cs | 0 .../CodeGen/RegisterAllocators/LiveRange.cs | 0 .../RegisterAllocators/RegisterMasks.cs | 0 .../RegisterAllocators/StackAllocator.cs | 0 .../CodeGen/RegisterAllocators/UseList.cs | 0 .../CodeGen/Unwinding/UnwindInfo.cs | 0 .../CodeGen/Unwinding/UnwindPseudoOp.cs | 0 .../CodeGen/Unwinding/UnwindPushEntry.cs | 0 .../ARMeilleure}/CodeGen/X86/Assembler.cs | 0 .../CodeGen/X86/AssemblerTable.cs | 0 .../ARMeilleure}/CodeGen/X86/CallConvName.cs | 0 .../CodeGen/X86/CallingConvention.cs | 0 .../ARMeilleure}/CodeGen/X86/CodeGenCommon.cs | 0 .../CodeGen/X86/CodeGenContext.cs | 0 .../ARMeilleure}/CodeGen/X86/CodeGenerator.cs | 0 .../CodeGen/X86/HardwareCapabilities.cs | 0 .../ARMeilleure}/CodeGen/X86/IntrinsicInfo.cs | 0 .../CodeGen/X86/IntrinsicTable.cs | 0 .../ARMeilleure}/CodeGen/X86/IntrinsicType.cs | 0 .../ARMeilleure}/CodeGen/X86/Mxcsr.cs | 0 .../ARMeilleure}/CodeGen/X86/PreAllocator.cs | 0 .../CodeGen/X86/PreAllocatorSystemV.cs | 0 .../CodeGen/X86/PreAllocatorWindows.cs | 0 .../ARMeilleure}/CodeGen/X86/X86Condition.cs | 0 .../CodeGen/X86/X86Instruction.cs | 0 .../ARMeilleure}/CodeGen/X86/X86Optimizer.cs | 0 .../ARMeilleure}/CodeGen/X86/X86Register.cs | 0 .../ARMeilleure}/Common/AddressTable.cs | 0 .../ARMeilleure}/Common/Allocator.cs | 0 .../ARMeilleure}/Common/ArenaAllocator.cs | 0 .../ARMeilleure}/Common/BitMap.cs | 0 .../ARMeilleure}/Common/BitUtils.cs | 0 .../ARMeilleure}/Common/Counter.cs | 0 .../ARMeilleure}/Common/EntryTable.cs | 0 .../ARMeilleure}/Common/EnumUtils.cs | 0 .../ARMeilleure}/Common/NativeAllocator.cs | 0 .../ARMeilleure}/Decoders/Block.cs | 0 .../ARMeilleure}/Decoders/Condition.cs | 0 .../ARMeilleure}/Decoders/DataOp.cs | 0 .../ARMeilleure}/Decoders/Decoder.cs | 0 .../ARMeilleure}/Decoders/DecoderHelper.cs | 0 .../ARMeilleure}/Decoders/DecoderMode.cs | 0 .../ARMeilleure}/Decoders/IOpCode.cs | 0 .../ARMeilleure}/Decoders/IOpCode32.cs | 0 .../ARMeilleure}/Decoders/IOpCode32Adr.cs | 0 .../ARMeilleure}/Decoders/IOpCode32Alu.cs | 0 .../ARMeilleure}/Decoders/IOpCode32AluBf.cs | 0 .../ARMeilleure}/Decoders/IOpCode32AluImm.cs | 0 .../Decoders/IOpCode32AluImm16.cs | 0 .../ARMeilleure}/Decoders/IOpCode32AluMla.cs | 0 .../ARMeilleure}/Decoders/IOpCode32AluReg.cs | 0 .../Decoders/IOpCode32AluRsImm.cs | 0 .../Decoders/IOpCode32AluRsReg.cs | 0 .../Decoders/IOpCode32AluUmull.cs | 0 .../ARMeilleure}/Decoders/IOpCode32AluUx.cs | 0 .../ARMeilleure}/Decoders/IOpCode32BImm.cs | 0 .../ARMeilleure}/Decoders/IOpCode32BReg.cs | 0 .../Decoders/IOpCode32Exception.cs | 0 .../Decoders/IOpCode32HasSetFlags.cs | 0 .../ARMeilleure}/Decoders/IOpCode32Mem.cs | 0 .../ARMeilleure}/Decoders/IOpCode32MemEx.cs | 0 .../ARMeilleure}/Decoders/IOpCode32MemMult.cs | 0 .../ARMeilleure}/Decoders/IOpCode32MemReg.cs | 0 .../Decoders/IOpCode32MemRsImm.cs | 0 .../ARMeilleure}/Decoders/IOpCode32Simd.cs | 0 .../ARMeilleure}/Decoders/IOpCode32SimdImm.cs | 0 .../ARMeilleure}/Decoders/IOpCodeAlu.cs | 0 .../ARMeilleure}/Decoders/IOpCodeAluImm.cs | 0 .../ARMeilleure}/Decoders/IOpCodeAluRs.cs | 0 .../ARMeilleure}/Decoders/IOpCodeAluRx.cs | 0 .../ARMeilleure}/Decoders/IOpCodeBImm.cs | 0 .../ARMeilleure}/Decoders/IOpCodeCond.cs | 0 .../ARMeilleure}/Decoders/IOpCodeLit.cs | 0 .../ARMeilleure}/Decoders/IOpCodeSimd.cs | 0 .../ARMeilleure}/Decoders/InstDescriptor.cs | 0 .../ARMeilleure}/Decoders/InstEmitter.cs | 0 .../ARMeilleure}/Decoders/IntType.cs | 0 .../ARMeilleure}/Decoders/OpCode.cs | 0 .../ARMeilleure}/Decoders/OpCode32.cs | 0 .../ARMeilleure}/Decoders/OpCode32Alu.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluBf.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluImm.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluImm16.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluMla.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluReg.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluRsImm.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluRsReg.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluUmull.cs | 0 .../ARMeilleure}/Decoders/OpCode32AluUx.cs | 0 .../ARMeilleure}/Decoders/OpCode32BImm.cs | 0 .../ARMeilleure}/Decoders/OpCode32BReg.cs | 0 .../Decoders/OpCode32Exception.cs | 0 .../ARMeilleure}/Decoders/OpCode32Mem.cs | 0 .../ARMeilleure}/Decoders/OpCode32MemImm.cs | 0 .../ARMeilleure}/Decoders/OpCode32MemImm8.cs | 0 .../ARMeilleure}/Decoders/OpCode32MemLdEx.cs | 0 .../ARMeilleure}/Decoders/OpCode32MemMult.cs | 0 .../ARMeilleure}/Decoders/OpCode32MemReg.cs | 0 .../ARMeilleure}/Decoders/OpCode32MemRsImm.cs | 0 .../ARMeilleure}/Decoders/OpCode32MemStEx.cs | 0 .../ARMeilleure}/Decoders/OpCode32Mrs.cs | 0 .../ARMeilleure}/Decoders/OpCode32MsrReg.cs | 0 .../ARMeilleure}/Decoders/OpCode32Sat.cs | 0 .../ARMeilleure}/Decoders/OpCode32Sat16.cs | 0 .../ARMeilleure}/Decoders/OpCode32Simd.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdBase.cs | 0 .../Decoders/OpCode32SimdBinary.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdCmpZ.cs | 0 .../Decoders/OpCode32SimdCvtFI.cs | 0 .../Decoders/OpCode32SimdCvtTB.cs | 0 .../Decoders/OpCode32SimdDupElem.cs | 0 .../Decoders/OpCode32SimdDupGP.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdExt.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdImm.cs | 0 .../Decoders/OpCode32SimdImm44.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdLong.cs | 0 .../Decoders/OpCode32SimdMemImm.cs | 0 .../Decoders/OpCode32SimdMemMult.cs | 0 .../Decoders/OpCode32SimdMemPair.cs | 0 .../Decoders/OpCode32SimdMemSingle.cs | 0 .../Decoders/OpCode32SimdMovGp.cs | 0 .../Decoders/OpCode32SimdMovGpDouble.cs | 0 .../Decoders/OpCode32SimdMovGpElem.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdMovn.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdReg.cs | 0 .../Decoders/OpCode32SimdRegElem.cs | 0 .../Decoders/OpCode32SimdRegElemLong.cs | 0 .../Decoders/OpCode32SimdRegLong.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdRegS.cs | 0 .../Decoders/OpCode32SimdRegWide.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdRev.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdS.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdSel.cs | 0 .../Decoders/OpCode32SimdShImm.cs | 0 .../Decoders/OpCode32SimdShImmLong.cs | 0 .../Decoders/OpCode32SimdShImmNarrow.cs | 0 .../Decoders/OpCode32SimdSpecial.cs | 0 .../Decoders/OpCode32SimdSqrte.cs | 0 .../ARMeilleure}/Decoders/OpCode32SimdTbl.cs | 0 .../ARMeilleure}/Decoders/OpCode32System.cs | 0 .../ARMeilleure}/Decoders/OpCodeAdr.cs | 0 .../ARMeilleure}/Decoders/OpCodeAlu.cs | 0 .../ARMeilleure}/Decoders/OpCodeAluBinary.cs | 0 .../ARMeilleure}/Decoders/OpCodeAluImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeAluRs.cs | 0 .../ARMeilleure}/Decoders/OpCodeAluRx.cs | 0 .../ARMeilleure}/Decoders/OpCodeBImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeBImmAl.cs | 0 .../ARMeilleure}/Decoders/OpCodeBImmCmp.cs | 0 .../ARMeilleure}/Decoders/OpCodeBImmCond.cs | 0 .../ARMeilleure}/Decoders/OpCodeBImmTest.cs | 0 .../ARMeilleure}/Decoders/OpCodeBReg.cs | 0 .../ARMeilleure}/Decoders/OpCodeBfm.cs | 0 .../ARMeilleure}/Decoders/OpCodeCcmp.cs | 0 .../ARMeilleure}/Decoders/OpCodeCcmpImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeCcmpReg.cs | 0 .../ARMeilleure}/Decoders/OpCodeCsel.cs | 0 .../ARMeilleure}/Decoders/OpCodeException.cs | 0 .../ARMeilleure}/Decoders/OpCodeMem.cs | 0 .../ARMeilleure}/Decoders/OpCodeMemEx.cs | 0 .../ARMeilleure}/Decoders/OpCodeMemImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeMemLit.cs | 0 .../ARMeilleure}/Decoders/OpCodeMemPair.cs | 0 .../ARMeilleure}/Decoders/OpCodeMemReg.cs | 0 .../ARMeilleure}/Decoders/OpCodeMov.cs | 0 .../ARMeilleure}/Decoders/OpCodeMul.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimd.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdCvt.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdExt.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdFcond.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdFmov.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdHelper.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdIns.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdMemImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdMemLit.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdMemMs.cs | 0 .../Decoders/OpCodeSimdMemPair.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdMemReg.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdMemSs.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdReg.cs | 0 .../Decoders/OpCodeSimdRegElem.cs | 0 .../Decoders/OpCodeSimdRegElemF.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdShImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeSimdTbl.cs | 0 .../ARMeilleure}/Decoders/OpCodeSystem.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16.cs | 0 .../Decoders/OpCodeT16AddSubImm3.cs | 0 .../Decoders/OpCodeT16AddSubReg.cs | 0 .../Decoders/OpCodeT16AddSubSp.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16Adr.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16AluImm8.cs | 0 .../Decoders/OpCodeT16AluImmZero.cs | 0 .../Decoders/OpCodeT16AluRegHigh.cs | 0 .../Decoders/OpCodeT16AluRegLow.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16AluUx.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16BImm11.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16BImm8.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16BImmCmp.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16BReg.cs | 0 .../Decoders/OpCodeT16Exception.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16IfThen.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16MemImm5.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16MemLit.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16MemMult.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16MemReg.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16MemSp.cs | 0 .../Decoders/OpCodeT16MemStack.cs | 0 .../Decoders/OpCodeT16ShiftImm.cs | 0 .../Decoders/OpCodeT16ShiftReg.cs | 0 .../ARMeilleure}/Decoders/OpCodeT16SpRel.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32Alu.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32AluBf.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32AluImm.cs | 0 .../Decoders/OpCodeT32AluImm12.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32AluMla.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32AluReg.cs | 0 .../Decoders/OpCodeT32AluRsImm.cs | 0 .../Decoders/OpCodeT32AluUmull.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32AluUx.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32BImm20.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32BImm24.cs | 0 .../Decoders/OpCodeT32MemImm12.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32MemImm8.cs | 0 .../Decoders/OpCodeT32MemImm8D.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32MemLdEx.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32MemMult.cs | 0 .../Decoders/OpCodeT32MemRsImm.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32MemStEx.cs | 0 .../Decoders/OpCodeT32MovImm16.cs | 0 .../Decoders/OpCodeT32ShiftReg.cs | 0 .../ARMeilleure}/Decoders/OpCodeT32Tb.cs | 0 .../ARMeilleure}/Decoders/OpCodeTable.cs | 0 .../Decoders/Optimizations/TailCallRemover.cs | 0 .../ARMeilleure}/Decoders/RegisterSize.cs | 0 .../ARMeilleure}/Decoders/ShiftType.cs | 0 .../ARMeilleure}/Diagnostics/IRDumper.cs | 0 .../ARMeilleure}/Diagnostics/Logger.cs | 0 .../ARMeilleure}/Diagnostics/PassName.cs | 0 .../ARMeilleure}/Diagnostics/Symbols.cs | 0 .../Diagnostics/TranslatorEventSource.cs | 0 .../ARMeilleure}/Instructions/CryptoHelper.cs | 0 .../ARMeilleure}/Instructions/InstEmitAlu.cs | 0 .../Instructions/InstEmitAlu32.cs | 0 .../Instructions/InstEmitAluHelper.cs | 0 .../ARMeilleure}/Instructions/InstEmitBfm.cs | 0 .../ARMeilleure}/Instructions/InstEmitCcmp.cs | 0 .../ARMeilleure}/Instructions/InstEmitCsel.cs | 0 .../ARMeilleure}/Instructions/InstEmitDiv.cs | 0 .../Instructions/InstEmitException.cs | 0 .../Instructions/InstEmitException32.cs | 0 .../ARMeilleure}/Instructions/InstEmitFlow.cs | 0 .../Instructions/InstEmitFlow32.cs | 0 .../Instructions/InstEmitFlowHelper.cs | 0 .../ARMeilleure}/Instructions/InstEmitHash.cs | 0 .../Instructions/InstEmitHash32.cs | 0 .../Instructions/InstEmitHashHelper.cs | 0 .../Instructions/InstEmitHelper.cs | 0 .../Instructions/InstEmitMemory.cs | 0 .../Instructions/InstEmitMemory32.cs | 0 .../Instructions/InstEmitMemoryEx.cs | 0 .../Instructions/InstEmitMemoryEx32.cs | 0 .../Instructions/InstEmitMemoryExHelper.cs | 0 .../Instructions/InstEmitMemoryHelper.cs | 0 .../ARMeilleure}/Instructions/InstEmitMove.cs | 0 .../ARMeilleure}/Instructions/InstEmitMul.cs | 0 .../Instructions/InstEmitMul32.cs | 0 .../Instructions/InstEmitSimdArithmetic.cs | 0 .../Instructions/InstEmitSimdArithmetic32.cs | 0 .../Instructions/InstEmitSimdCmp.cs | 0 .../Instructions/InstEmitSimdCmp32.cs | 0 .../Instructions/InstEmitSimdCrypto.cs | 0 .../Instructions/InstEmitSimdCrypto32.cs | 0 .../Instructions/InstEmitSimdCvt.cs | 0 .../Instructions/InstEmitSimdCvt32.cs | 0 .../Instructions/InstEmitSimdHash.cs | 0 .../Instructions/InstEmitSimdHash32.cs | 0 .../Instructions/InstEmitSimdHashHelper.cs | 0 .../Instructions/InstEmitSimdHelper.cs | 0 .../Instructions/InstEmitSimdHelper32.cs | 0 .../Instructions/InstEmitSimdHelper32Arm64.cs | 0 .../Instructions/InstEmitSimdHelperArm64.cs | 0 .../Instructions/InstEmitSimdLogical.cs | 0 .../Instructions/InstEmitSimdLogical32.cs | 0 .../Instructions/InstEmitSimdMemory.cs | 0 .../Instructions/InstEmitSimdMemory32.cs | 0 .../Instructions/InstEmitSimdMove.cs | 0 .../Instructions/InstEmitSimdMove32.cs | 0 .../Instructions/InstEmitSimdShift.cs | 0 .../Instructions/InstEmitSimdShift32.cs | 0 .../Instructions/InstEmitSystem.cs | 0 .../Instructions/InstEmitSystem32.cs | 0 .../ARMeilleure}/Instructions/InstName.cs | 0 .../Instructions/NativeInterface.cs | 0 .../ARMeilleure}/Instructions/SoftFallback.cs | 0 .../ARMeilleure}/Instructions/SoftFloat.cs | 0 .../IntermediateRepresentation/BasicBlock.cs | 0 .../BasicBlockFrequency.cs | 0 .../IntermediateRepresentation/Comparison.cs | 0 .../IIntrusiveListNode.cs | 0 .../IntermediateRepresentation/Instruction.cs | 0 .../IntermediateRepresentation/Intrinsic.cs | 0 .../IntrusiveList.cs | 0 .../MemoryOperand.cs | 0 .../IntermediateRepresentation/Multiplier.cs | 0 .../IntermediateRepresentation/Operand.cs | 0 .../IntermediateRepresentation/OperandKind.cs | 0 .../IntermediateRepresentation/OperandType.cs | 0 .../IntermediateRepresentation/Operation.cs | 0 .../PhiOperation.cs | 0 .../IntermediateRepresentation/Register.cs | 0 .../RegisterType.cs | 0 .../Memory/IJitMemoryAllocator.cs | 0 .../ARMeilleure}/Memory/IJitMemoryBlock.cs | 0 .../ARMeilleure}/Memory/IMemoryManager.cs | 0 .../Memory/InvalidAccessException.cs | 0 .../ARMeilleure}/Memory/MemoryManagerType.cs | 0 .../ARMeilleure}/Memory/ReservedRegion.cs | 0 .../ARMeilleure}/Native/JitSupportDarwin.cs | 0 .../libs/libarmeilleure-jitsupport.dylib | Bin .../Native/macos_jit_support/Makefile | 0 .../Native/macos_jit_support/support.c | 0 .../ARMeilleure}/Optimizations.cs | 0 .../Signal/NativeSignalHandler.cs | 0 .../ARMeilleure}/Signal/TestMethods.cs | 0 .../Signal/UnixSignalHandlerRegistration.cs | 0 .../Signal/WindowsPartialUnmapHandler.cs | 0 .../WindowsSignalHandlerRegistration.cs | 0 .../ARMeilleure}/State/Aarch32Mode.cs | 0 .../ARMeilleure}/State/ExceptionCallback.cs | 0 .../ARMeilleure}/State/ExecutionContext.cs | 0 .../ARMeilleure}/State/ExecutionMode.cs | 0 .../ARMeilleure}/State/FPCR.cs | 0 .../ARMeilleure}/State/FPException.cs | 0 .../ARMeilleure}/State/FPRoundingMode.cs | 0 .../ARMeilleure}/State/FPSCR.cs | 0 .../ARMeilleure}/State/FPSR.cs | 0 .../ARMeilleure}/State/FPState.cs | 0 .../ARMeilleure}/State/FPType.cs | 0 .../ARMeilleure}/State/ICounter.cs | 0 .../ARMeilleure}/State/NativeContext.cs | 0 .../ARMeilleure}/State/PState.cs | 0 .../ARMeilleure}/State/RegisterAlias.cs | 0 .../ARMeilleure}/State/RegisterConsts.cs | 0 .../ARMeilleure}/State/V128.cs | 0 .../ARMeilleure}/Statistics.cs | 0 .../Translation/ArmEmitterContext.cs | 0 .../Translation/Cache/CacheEntry.cs | 0 .../Translation/Cache/CacheMemoryAllocator.cs | 0 .../Translation/Cache/JitCache.cs | 0 .../Translation/Cache/JitCacheInvalidation.cs | 0 .../Translation/Cache/JitUnwindWindows.cs | 0 .../ARMeilleure}/Translation/Compiler.cs | 0 .../Translation/CompilerContext.cs | 0 .../Translation/CompilerOptions.cs | 0 .../Translation/ControlFlowGraph.cs | 0 .../Translation/DelegateHelper.cs | 0 .../ARMeilleure}/Translation/DelegateInfo.cs | 0 .../ARMeilleure}/Translation/Delegates.cs | 0 .../Translation/DispatcherFunction.cs | 0 .../ARMeilleure}/Translation/Dominance.cs | 0 .../Translation/EmitterContext.cs | 0 .../ARMeilleure}/Translation/GuestFunction.cs | 0 .../ARMeilleure}/Translation/IntervalTree.cs | 0 .../Translation/PTC/EncodingCache.cs | 0 .../Translation/PTC/IPtcLoadState.cs | 0 .../ARMeilleure}/Translation/PTC/Ptc.cs | 0 .../Translation/PTC/PtcFormatter.cs | 0 .../Translation/PTC/PtcLoadingState.cs | 0 .../Translation/PTC/PtcProfiler.cs | 0 .../ARMeilleure}/Translation/PTC/PtcState.cs | 0 .../Translation/RegisterToLocal.cs | 0 .../ARMeilleure}/Translation/RegisterUsage.cs | 0 .../ARMeilleure}/Translation/RejitRequest.cs | 0 .../Translation/SsaConstruction.cs | 0 .../Translation/SsaDeconstruction.cs | 0 .../Translation/TranslatedFunction.cs | 0 .../ARMeilleure}/Translation/Translator.cs | 0 .../Translation/TranslatorCache.cs | 0 .../Translation/TranslatorQueue.cs | 0 .../Translation/TranslatorStubs.cs | 0 .../Translation/TranslatorTestMethods.cs | 0 .../OpenALAudioBuffer.cs | 0 .../OpenALHardwareDeviceDriver.cs | 0 .../OpenALHardwareDeviceSession.cs | 0 .../Ryujinx.Audio.Backends.OpenAL.csproj | 0 .../Ryujinx.Audio.Backends.SDL2.csproj | 0 .../SDL2AudioBuffer.cs | 0 .../SDL2HardwareDeviceDriver.cs | 0 .../SDL2HardwareDeviceSession.cs | 0 .../Native/SoundIo.cs | 0 .../Native/SoundIoBackend.cs | 0 .../Native/SoundIoChannelId.cs | 0 .../Native/SoundIoContext.cs | 0 .../Native/SoundIoDeviceAim.cs | 0 .../Native/SoundIoDeviceContext.cs | 0 .../Native/SoundIoError.cs | 0 .../Native/SoundIoException.cs | 0 .../Native/SoundIoFormat.cs | 0 .../Native/SoundIoOutStreamContext.cs | 0 .../Native/libsoundio/libs/libsoundio.dll | Bin .../Native/libsoundio/libs/libsoundio.dylib | Bin .../Native/libsoundio/libs/libsoundio.so | Bin .../Ryujinx.Audio.Backends.SoundIo.csproj | 0 .../SoundIoAudioBuffer.cs | 0 .../SoundIoHardwareDeviceDriver.cs | 0 .../SoundIoHardwareDeviceSession.cs | 0 .../Ryujinx.Audio}/AudioManager.cs | 0 .../Backends/Common/BackendHelper.cs | 0 .../Backends/Common/DynamicRingBuffer.cs | 0 .../Common/HardwareDeviceSessionOutputBase.cs | 0 .../CompatLayerHardwareDeviceDriver.cs | 0 .../CompatLayerHardwareDeviceSession.cs | 0 .../Backends/CompatLayer/Downmixing.cs | 0 .../Dummy/DummyHardwareDeviceDriver.cs | 0 .../Dummy/DummyHardwareDeviceSessionInput.cs | 0 .../Dummy/DummyHardwareDeviceSessionOutput.cs | 0 .../Ryujinx.Audio}/Common/AudioBuffer.cs | 0 .../Common/AudioDeviceSession.cs | 0 .../Ryujinx.Audio}/Common/AudioDeviceState.cs | 0 .../Common/AudioInputConfiguration.cs | 0 .../Common/AudioOutputConfiguration.cs | 0 .../Ryujinx.Audio}/Common/AudioUserBuffer.cs | 0 .../Ryujinx.Audio}/Common/SampleFormat.cs | 0 .../Ryujinx.Audio}/Constants.cs | 0 .../Ryujinx.Audio}/Input/AudioInputManager.cs | 0 .../Ryujinx.Audio}/Input/AudioInputSystem.cs | 0 .../Integration/HardwareDeviceImpl.cs | 0 .../Integration/IHardwareDevice.cs | 0 .../Integration/IHardwareDeviceDriver.cs | 0 .../Integration/IHardwareDeviceSession.cs | 0 .../Integration/IWritableEvent.cs | 0 .../Output/AudioOutputManager.cs | 0 .../Output/AudioOutputSystem.cs | 0 .../Common/AuxiliaryBufferAddresses.cs | 0 .../Renderer/Common/BehaviourParameter.cs | 0 .../Renderer/Common/EdgeMatrix.cs | 0 .../Renderer/Common/EffectType.cs | 0 .../Renderer/Common/MemoryPoolUserState.cs | 0 .../Renderer/Common/NodeIdHelper.cs | 0 .../Renderer/Common/NodeIdType.cs | 0 .../Renderer/Common/NodeStates.cs | 0 .../Renderer/Common/PerformanceDetailType.cs | 0 .../Renderer/Common/PerformanceEntryType.cs | 0 .../Renderer/Common/PlayState.cs | 0 .../Renderer/Common/ReverbEarlyMode.cs | 0 .../Renderer/Common/ReverbLateMode.cs | 0 .../Renderer/Common/SinkType.cs | 0 .../Renderer/Common/UpdateDataHeader.cs | 0 .../Renderer/Common/VoiceUpdateState.cs | 0 .../Renderer/Common/WaveBuffer.cs | 0 .../Renderer/Common/WorkBufferAllocator.cs | 0 .../Renderer/Device/VirtualDevice.cs | 0 .../Renderer/Device/VirtualDeviceSession.cs | 0 .../Device/VirtualDeviceSessionRegistry.cs | 0 .../Renderer/Dsp/AdpcmHelper.cs | 0 .../Renderer/Dsp/AudioProcessor.cs | 0 .../Renderer/Dsp/BiquadFilterHelper.cs | 0 .../Command/AdpcmDataSourceCommandVersion1.cs | 0 .../Dsp/Command/AuxiliaryBufferCommand.cs | 0 .../Dsp/Command/BiquadFilterCommand.cs | 0 .../Dsp/Command/CaptureBufferCommand.cs | 0 .../Dsp/Command/CircularBufferSinkCommand.cs | 0 .../Dsp/Command/ClearMixBufferCommand.cs | 0 .../Renderer/Dsp/Command/CommandList.cs | 0 .../Renderer/Dsp/Command/CommandType.cs | 0 .../Renderer/Dsp/Command/CompressorCommand.cs | 0 .../Dsp/Command/CopyMixBufferCommand.cs | 0 .../Dsp/Command/DataSourceVersion2Command.cs | 0 .../Renderer/Dsp/Command/DelayCommand.cs | 0 .../Dsp/Command/DepopForMixBuffersCommand.cs | 0 .../Dsp/Command/DepopPrepareCommand.cs | 0 .../Renderer/Dsp/Command/DeviceSinkCommand.cs | 0 .../Command/DownMixSurroundToStereoCommand.cs | 0 .../Dsp/Command/GroupedBiquadFilterCommand.cs | 0 .../Renderer/Dsp/Command/ICommand.cs | 0 .../Dsp/Command/LimiterCommandVersion1.cs | 0 .../Dsp/Command/LimiterCommandVersion2.cs | 0 .../Renderer/Dsp/Command/MixCommand.cs | 0 .../Renderer/Dsp/Command/MixRampCommand.cs | 0 .../Dsp/Command/MixRampGroupedCommand.cs | 0 .../PcmFloatDataSourceCommandVersion1.cs | 0 .../PcmInt16DataSourceCommandVersion1.cs | 0 .../Dsp/Command/PerformanceCommand.cs | 0 .../Renderer/Dsp/Command/Reverb3dCommand.cs | 0 .../Renderer/Dsp/Command/ReverbCommand.cs | 0 .../Renderer/Dsp/Command/UpsampleCommand.cs | 0 .../Renderer/Dsp/Command/VolumeCommand.cs | 0 .../Renderer/Dsp/Command/VolumeRampCommand.cs | 0 .../Renderer/Dsp/DataSourceHelper.cs | 0 .../Renderer/Dsp/Effect/DecayDelay.cs | 0 .../Renderer/Dsp/Effect/DelayLine.cs | 0 .../Renderer/Dsp/Effect/DelayLineReverb3d.cs | 0 .../Dsp/Effect/ExponentialMovingAverage.cs | 0 .../Renderer/Dsp/Effect/IDelayLine.cs | 0 .../Renderer/Dsp/FixedPointHelper.cs | 0 .../Renderer/Dsp/FloatingPointHelper.cs | 0 .../Ryujinx.Audio}/Renderer/Dsp/PcmHelper.cs | 0 .../Renderer/Dsp/ResamplerHelper.cs | 0 .../Renderer/Dsp/State/AdpcmLoopContext.cs | 0 .../Dsp/State/AuxiliaryBufferHeader.cs | 0 .../Renderer/Dsp/State/BiquadFilterState.cs | 0 .../Renderer/Dsp/State/CompressorState.cs | 0 .../Renderer/Dsp/State/DelayState.cs | 0 .../Renderer/Dsp/State/LimiterState.cs | 0 .../Renderer/Dsp/State/Reverb3dState.cs | 0 .../Renderer/Dsp/State/ReverbState.cs | 0 .../Renderer/Dsp/UpsamplerHelper.cs | 0 .../Parameter/AudioRendererConfiguration.cs | 0 .../Parameter/BehaviourErrorInfoOutStatus.cs | 0 .../Parameter/BiquadFilterParameter.cs | 0 .../Effect/AuxiliaryBufferParameter.cs | 0 .../Effect/BiquadFilterEffectParameter.cs | 0 .../Parameter/Effect/BufferMixerParameter.cs | 0 .../Parameter/Effect/CompressorParameter.cs | 0 .../Parameter/Effect/DelayParameter.cs | 0 .../Parameter/Effect/LimiterParameter.cs | 0 .../Parameter/Effect/LimiterStatistics.cs | 0 .../Parameter/Effect/Reverb3dParameter.cs | 0 .../Parameter/Effect/ReverbParameter.cs | 0 .../Parameter/EffectInParameterVersion1.cs | 0 .../Parameter/EffectInParameterVersion2.cs | 0 .../Parameter/EffectOutStatusVersion1.cs | 0 .../Parameter/EffectOutStatusVersion2.cs | 0 .../Renderer/Parameter/EffectResultState.cs | 0 .../Renderer/Parameter/EffectState.cs | 0 .../Renderer/Parameter/IEffectInParameter.cs | 0 .../Renderer/Parameter/IEffectOutStatus.cs | 0 .../Parameter/MemoryPoolInParameter.cs | 0 .../Renderer/Parameter/MemoryPoolOutStatus.cs | 0 .../MixInParameterDirtyOnlyUpdate.cs | 0 .../Renderer/Parameter/MixParameter.cs | 0 .../Performance/PerformanceInParameter.cs | 0 .../Performance/PerformanceOutStatus.cs | 0 .../Parameter/RendererInfoOutStatus.cs | 0 .../Parameter/Sink/CircularBufferParameter.cs | 0 .../Parameter/Sink/DeviceParameter.cs | 0 .../Renderer/Parameter/SinkInParameter.cs | 0 .../Renderer/Parameter/SinkOutStatus.cs | 0 .../SplitterDestinationInParameter.cs | 0 .../Renderer/Parameter/SplitterInParameter.cs | 0 .../Parameter/SplitterInParameterHeader.cs | 0 .../VoiceChannelResourceInParameter.cs | 0 .../Renderer/Parameter/VoiceInParameter.cs | 0 .../Renderer/Parameter/VoiceOutStatus.cs | 0 .../Renderer/Server/AudioRenderSystem.cs | 0 .../Renderer/Server/AudioRendererManager.cs | 0 .../Renderer/Server/BehaviourContext.cs | 0 .../Renderer/Server/CommandBuffer.cs | 0 .../Renderer/Server/CommandGenerator.cs | 0 .../CommandProcessingTimeEstimatorVersion1.cs | 0 .../CommandProcessingTimeEstimatorVersion2.cs | 0 .../CommandProcessingTimeEstimatorVersion3.cs | 0 .../CommandProcessingTimeEstimatorVersion4.cs | 0 .../CommandProcessingTimeEstimatorVersion5.cs | 0 .../Server/Effect/AuxiliaryBufferEffect.cs | 0 .../Renderer/Server/Effect/BaseEffect.cs | 0 .../Server/Effect/BiquadFilterEffect.cs | 0 .../Renderer/Server/Effect/BufferMixEffect.cs | 0 .../Server/Effect/CaptureBufferEffect.cs | 0 .../Server/Effect/CompressorEffect.cs | 0 .../Renderer/Server/Effect/DelayEffect.cs | 0 .../Renderer/Server/Effect/EffectContext.cs | 0 .../Renderer/Server/Effect/LimiterEffect.cs | 0 .../Renderer/Server/Effect/Reverb3dEffect.cs | 0 .../Renderer/Server/Effect/ReverbEffect.cs | 0 .../Renderer/Server/Effect/UsageState.cs | 0 .../Server/ICommandProcessingTimeEstimator.cs | 0 .../Renderer/Server/MemoryPool/AddressInfo.cs | 0 .../Server/MemoryPool/MemoryPoolState.cs | 0 .../Renderer/Server/MemoryPool/PoolMapper.cs | 0 .../Renderer/Server/Mix/MixContext.cs | 0 .../Renderer/Server/Mix/MixState.cs | 0 .../Performance/IPerformanceDetailEntry.cs | 0 .../Server/Performance/IPerformanceEntry.cs | 0 .../Server/Performance/IPerformanceHeader.cs | 0 .../Performance/PerformanceDetailVersion1.cs | 0 .../Performance/PerformanceDetailVersion2.cs | 0 .../Performance/PerformanceEntryAddresses.cs | 0 .../Performance/PerformanceEntryVersion1.cs | 0 .../Performance/PerformanceEntryVersion2.cs | 0 .../PerformanceFrameHeaderVersion1.cs | 0 .../PerformanceFrameHeaderVersion2.cs | 0 .../Server/Performance/PerformanceManager.cs | 0 .../Performance/PerformanceManagerGeneric.cs | 0 .../Renderer/Server/RendererSystemContext.cs | 0 .../Renderer/Server/Sink/BaseSink.cs | 0 .../Server/Sink/CircularBufferSink.cs | 0 .../Renderer/Server/Sink/DeviceSink.cs | 0 .../Renderer/Server/Sink/SinkContext.cs | 0 .../Server/Splitter/SplitterContext.cs | 0 .../Server/Splitter/SplitterDestination.cs | 0 .../Renderer/Server/Splitter/SplitterState.cs | 0 .../Renderer/Server/StateUpdater.cs | 0 .../Types/AudioRendererExecutionMode.cs | 0 .../Types/AudioRendererRenderingDevice.cs | 0 .../Renderer/Server/Types/PlayState.cs | 0 .../Server/Upsampler/UpsamplerBufferState.cs | 0 .../Server/Upsampler/UpsamplerManager.cs | 0 .../Server/Upsampler/UpsamplerState.cs | 0 .../Server/Voice/VoiceChannelResource.cs | 0 .../Renderer/Server/Voice/VoiceContext.cs | 0 .../Renderer/Server/Voice/VoiceState.cs | 0 .../Renderer/Server/Voice/WaveBuffer.cs | 0 .../Utils/AudioProcessorMemoryManager.cs | 0 .../Ryujinx.Audio}/Renderer/Utils/BitArray.cs | 0 .../Renderer/Utils/FileHardwareDevice.cs | 0 .../Ryujinx.Audio}/Renderer/Utils/Mailbox.cs | 0 .../Renderer/Utils/Math/Matrix2x2.cs | 0 .../Renderer/Utils/Math/Matrix6x6.cs | 0 .../Renderer/Utils/Math/MatrixHelper.cs | 0 .../Renderer/Utils/Math/Vector6.cs | 0 .../Renderer/Utils/SpanIOHelper.cs | 0 .../Renderer/Utils/SpanMemoryManager.cs | 0 .../Renderer/Utils/SplitterHardwareDevice.cs | 0 .../Ryujinx.Audio}/ResultCode.cs | 0 .../Ryujinx.Audio}/Ryujinx.Audio.csproj | 0 {Ryujinx.Ava => src/Ryujinx.Ava}/App.axaml | 0 {Ryujinx.Ava => src/Ryujinx.Ava}/App.axaml.cs | 0 {Ryujinx.Ava => src/Ryujinx.Ava}/AppHost.cs | 0 .../Assets/Fonts/SegoeFluentIcons.ttf | Bin .../Ryujinx.Ava}/Assets/Locales/de_DE.json | 0 .../Ryujinx.Ava}/Assets/Locales/el_GR.json | 0 .../Ryujinx.Ava}/Assets/Locales/en_US.json | 0 .../Ryujinx.Ava}/Assets/Locales/es_ES.json | 0 .../Ryujinx.Ava}/Assets/Locales/fr_FR.json | 0 .../Ryujinx.Ava}/Assets/Locales/it_IT.json | 0 .../Ryujinx.Ava}/Assets/Locales/ja_JP.json | 0 .../Ryujinx.Ava}/Assets/Locales/ko_KR.json | 0 .../Ryujinx.Ava}/Assets/Locales/pl_PL.json | 0 .../Ryujinx.Ava}/Assets/Locales/pt_BR.json | 0 .../Ryujinx.Ava}/Assets/Locales/ru_RU.json | 0 .../Ryujinx.Ava}/Assets/Locales/tr_TR.json | 0 .../Ryujinx.Ava}/Assets/Locales/uk_UA.json | 0 .../Ryujinx.Ava}/Assets/Locales/zh_CN.json | 0 .../Ryujinx.Ava}/Assets/Locales/zh_TW.json | 0 .../Ryujinx.Ava}/Assets/Styles/BaseDark.xaml | 0 .../Ryujinx.Ava}/Assets/Styles/BaseLight.xaml | 0 .../Ryujinx.Ava}/Assets/Styles/Styles.xaml | 0 .../Ryujinx.Ava}/Common/ApplicationHelper.cs | 0 .../Ryujinx.Ava}/Common/ApplicationSort.cs | 0 .../Common/KeyboardHotkeyState.cs | 0 .../Common/Locale/LocaleExtension.cs | 0 .../Common/Locale/LocaleManager.cs | 0 .../Ryujinx.Ava}/Input/AvaloniaKeyboard.cs | 0 .../Input/AvaloniaKeyboardDriver.cs | 0 .../Input/AvaloniaKeyboardMappingHelper.cs | 0 .../Ryujinx.Ava}/Input/AvaloniaMouse.cs | 0 .../Ryujinx.Ava}/Input/AvaloniaMouseDriver.cs | 0 .../Ryujinx.Ava}/Modules/Updater/Updater.cs | 0 {Ryujinx.Ava => src/Ryujinx.Ava}/Program.cs | 0 .../Ryujinx.Ava}/Ryujinx.Ava.csproj | 14 +-- {Ryujinx.Ava => src/Ryujinx.Ava}/Ryujinx.ico | Bin .../UI/Applet/AvaHostUiHandler.cs | 0 .../Applet/AvaloniaDynamicTextInputHandler.cs | 0 .../UI/Applet/AvaloniaHostUiTheme.cs | 0 .../UI/Applet/ErrorAppletWindow.axaml | 0 .../UI/Applet/ErrorAppletWindow.axaml.cs | 0 .../UI/Applet/SwkbdAppletDialog.axaml | 0 .../UI/Applet/SwkbdAppletDialog.axaml.cs | 0 .../UI/Controls/GameGridView.axaml | 0 .../UI/Controls/GameGridView.axaml.cs | 0 .../UI/Controls/GameListView.axaml | 0 .../UI/Controls/GameListView.axaml.cs | 0 .../UI/Controls/NavigationDialogHost.axaml | 0 .../UI/Controls/NavigationDialogHost.axaml.cs | 0 .../UI/Controls/UpdateWaitWindow.axaml | 0 .../UI/Controls/UpdateWaitWindow.axaml.cs | 0 .../UI/Helpers/ApplicationOpenedEventArgs.cs | 0 .../UI/Helpers/BitmapArrayValueConverter.cs | 0 .../UI/Helpers/ButtonKeyAssigner.cs | 0 .../UI/Helpers/ContentDialogHelper.cs | 0 .../Ryujinx.Ava}/UI/Helpers/Glyph.cs | 0 .../UI/Helpers/GlyphValueConverter.cs | 0 .../Ryujinx.Ava}/UI/Helpers/HotKeyControl.cs | 0 .../UI/Helpers/KeyValueConverter.cs | 0 .../Ryujinx.Ava}/UI/Helpers/LoggerAdapter.cs | 0 .../Ryujinx.Ava}/UI/Helpers/MiniCommand.cs | 0 .../UI/Helpers/NotificationHelper.cs | 0 .../UI/Helpers/OffscreenTextBox.cs | 0 .../UI/Helpers/UserErrorDialog.cs | 0 .../Ryujinx.Ava}/UI/Helpers/UserResult.cs | 0 .../UI/Helpers/Win32NativeInterop.cs | 0 .../Ryujinx.Ava}/UI/Models/CheatModel.cs | 0 .../Ryujinx.Ava}/UI/Models/CheatsList.cs | 0 .../Ryujinx.Ava}/UI/Models/ControllerModel.cs | 0 .../Ryujinx.Ava}/UI/Models/DeviceType.cs | 0 .../UI/Models/DownloadableContentModel.cs | 0 .../Models/Generic/LastPlayedSortComparer.cs | 0 .../UI/Models/InputConfiguration.cs | 0 .../Ryujinx.Ava}/UI/Models/PlayerModel.cs | 0 .../UI/Models/ProfileImageModel.cs | 0 .../Ryujinx.Ava}/UI/Models/SaveModel.cs | 0 .../UI/Models/StatusUpdatedEventArgs.cs | 0 .../Ryujinx.Ava}/UI/Models/TempProfile.cs | 0 .../Ryujinx.Ava}/UI/Models/TimeZone.cs | 0 .../UI/Models/TitleUpdateModel.cs | 0 .../Ryujinx.Ava}/UI/Models/UserProfile.cs | 0 .../UI/Renderer/EmbeddedWindow.cs | 0 .../UI/Renderer/EmbeddedWindowOpenGL.cs | 0 .../UI/Renderer/EmbeddedWindowVulkan.cs | 0 .../UI/Renderer/OpenTKBindingsContext.cs | 0 .../UI/Renderer/RendererHost.axaml | 0 .../UI/Renderer/RendererHost.axaml.cs | 0 .../UI/Renderer/SPBOpenGLContext.cs | 0 .../UI/ViewModels/AboutWindowViewModel.cs | 0 .../UI/ViewModels/AmiiboWindowViewModel.cs | 0 .../UI/ViewModels/AvatarProfileViewModel.cs | 0 .../Ryujinx.Ava}/UI/ViewModels/BaseModel.cs | 0 .../ViewModels/ControllerSettingsViewModel.cs | 0 .../DownloadableContentManagerViewModel.cs | 0 .../UI/ViewModels/MainWindowViewModel.cs | 0 .../UI/ViewModels/SettingsViewModel.cs | 0 .../UI/ViewModels/TitleUpdateViewModel.cs | 0 .../UserFirmwareAvatarSelectorViewModel.cs | 0 .../UserProfileImageSelectorViewModel.cs | 0 .../UI/ViewModels/UserProfileViewModel.cs | 0 .../UI/ViewModels/UserSaveManagerViewModel.cs | 0 .../UI/Views/Main/MainMenuBarView.axaml | 0 .../UI/Views/Main/MainMenuBarView.axaml.cs | 0 .../UI/Views/Main/MainStatusBarView.axaml | 0 .../UI/Views/Main/MainStatusBarView.axaml.cs | 0 .../UI/Views/Main/MainViewControls.axaml | 0 .../UI/Views/Main/MainViewControls.axaml.cs | 0 .../UI/Views/Settings/SettingsAudioView.axaml | 0 .../Views/Settings/SettingsAudioView.axaml.cs | 0 .../UI/Views/Settings/SettingsCPUView.axaml | 0 .../Views/Settings/SettingsCPUView.axaml.cs | 0 .../Views/Settings/SettingsGraphicsView.axaml | 0 .../Settings/SettingsGraphicsView.axaml.cs | 0 .../Views/Settings/SettingsHotkeysView.axaml | 0 .../Settings/SettingsHotkeysView.axaml.cs | 0 .../UI/Views/Settings/SettingsInputView.axaml | 0 .../Views/Settings/SettingsInputView.axaml.cs | 0 .../Views/Settings/SettingsLoggingView.axaml | 0 .../Settings/SettingsLoggingView.axaml.cs | 0 .../Views/Settings/SettingsNetworkView.axaml | 0 .../Settings/SettingsNetworkView.axaml.cs | 0 .../Views/Settings/SettingsSystemView.axaml | 0 .../Settings/SettingsSystemView.axaml.cs | 0 .../UI/Views/Settings/SettingsUIView.axaml | 0 .../UI/Views/Settings/SettingsUIView.axaml.cs | 0 .../UI/Views/User/UserEditorView.axaml | 0 .../UI/Views/User/UserEditorView.axaml.cs | 0 .../User/UserFirmwareAvatarSelectorView.axaml | 0 .../UserFirmwareAvatarSelectorView.axaml.cs | 0 .../User/UserProfileImageSelectorView.axaml | 0 .../UserProfileImageSelectorView.axaml.cs | 0 .../UI/Views/User/UserRecovererView.axaml | 0 .../UI/Views/User/UserRecovererView.axaml.cs | 0 .../UI/Views/User/UserSaveManagerView.axaml | 0 .../Views/User/UserSaveManagerView.axaml.cs | 0 .../UI/Views/User/UserSelectorView.axaml | 0 .../UI/Views/User/UserSelectorView.axaml.cs | 0 .../Ryujinx.Ava}/UI/Windows/AboutWindow.axaml | 0 .../UI/Windows/AboutWindow.axaml.cs | 0 .../UI/Windows/AmiiboWindow.axaml | 0 .../UI/Windows/AmiiboWindow.axaml.cs | 0 .../Ryujinx.Ava}/UI/Windows/CheatWindow.axaml | 0 .../UI/Windows/CheatWindow.axaml.cs | 0 .../Windows/ContentDialogOverlayWindow.axaml | 0 .../ContentDialogOverlayWindow.axaml.cs | 0 .../UI/Windows/ControllerSettingsWindow.axaml | 0 .../Windows/ControllerSettingsWindow.axaml.cs | 0 .../DownloadableContentManagerWindow.axaml | 0 .../DownloadableContentManagerWindow.axaml.cs | 0 .../UI/Windows/IconColorPicker.cs | 0 .../Ryujinx.Ava}/UI/Windows/MainWindow.axaml | 0 .../UI/Windows/MainWindow.axaml.cs | 0 .../UI/Windows/MotionSettingsWindow.axaml | 0 .../UI/Windows/MotionSettingsWindow.axaml.cs | 0 .../UI/Windows/RumbleSettingsWindow.axaml | 0 .../UI/Windows/RumbleSettingsWindow.axaml.cs | 0 .../UI/Windows/SettingsWindow.axaml | 0 .../UI/Windows/SettingsWindow.axaml.cs | 0 .../UI/Windows/StyleableWindow.cs | 0 .../UI/Windows/TitleUpdateWindow.axaml | 0 .../UI/Windows/TitleUpdateWindow.axaml.cs | 0 .../Ryujinx.Common}/AsyncWorkQueue.cs | 0 .../Collections/IntervalTree.cs | 0 .../Collections/IntrusiveRedBlackTree.cs | 0 .../Collections/IntrusiveRedBlackTreeImpl.cs | 0 .../Collections/IntrusiveRedBlackTreeNode.cs | 0 .../Collections/TreeDictionary.cs | 0 .../Configuration/AntiAliasing.cs | 0 .../Configuration/AppDataManager.cs | 0 .../Configuration/AspectRatioExtensions.cs | 0 .../Configuration/BackendThreading.cs | 0 .../DownloadableContentContainer.cs | 0 ...ownloadableContentJsonSerializerContext.cs | 0 .../Configuration/DownloadableContentNca.cs | 0 .../Configuration/GraphicsBackend.cs | 0 .../Configuration/GraphicsDebugLevel.cs | 0 .../Hid/Controller/GamepadInputId.cs | 0 .../GenericControllerInputConfig.cs | 0 .../Controller/JoyconConfigControllerStick.cs | 0 .../Motion/CemuHookMotionConfigController.cs | 0 .../JsonMotionConfigControllerConverter.cs | 0 .../Motion/MotionConfigController.cs | 0 .../MotionConfigJsonSerializerContext.cs | 0 .../Motion/MotionInputBackendType.cs | 0 .../Motion/StandardMotionConfigController.cs | 0 .../Hid/Controller/RumbleConfigController.cs | 0 .../StandardControllerInputConfig.cs | 0 .../Hid/Controller/StickInputId.cs | 0 .../Configuration/Hid/ControllerType.cs | 0 .../Hid/GenericInputConfigurationCommon.cs | 0 .../Configuration/Hid/InputBackendType.cs | 0 .../Configuration/Hid/InputConfig.cs | 0 .../Hid/InputConfigJsonSerializerContext.cs | 0 .../Hid/JsonInputConfigConverter.cs | 0 .../Ryujinx.Common}/Configuration/Hid/Key.cs | 0 .../Keyboard/GenericKeyboardInputConfig.cs | 0 .../Hid/Keyboard/JoyconConfigKeyboardStick.cs | 0 .../Keyboard/StandardKeyboardInputConfig.cs | 0 .../Configuration/Hid/KeyboardHotkeys.cs | 0 .../Hid/LeftJoyconCommonConfig.cs | 0 .../Configuration/Hid/PlayerIndex.cs | 0 .../Hid/RightJoyconCommonConfig.cs | 0 .../Configuration/MemoryManagerMode.cs | 0 .../Configuration/ScalingFilter.cs | 0 .../Configuration/TitleUpdateMetadata.cs | 0 ...itleUpdateMetadataJsonSerializerContext.cs | 0 .../Extensions/BinaryReaderExtensions.cs | 0 .../Extensions/BinaryWriterExtensions.cs | 0 .../Extensions/StreamExtensions.cs | 0 .../GraphicsDriver/DriverUtilities.cs | 0 .../GraphicsDriver/NVAPI/Nvapi.cs | 0 .../NVAPI/NvapiUnicodeString.cs | 0 .../NVAPI/NvdrsApplicationV4.cs | 0 .../GraphicsDriver/NVAPI/NvdrsProfile.cs | 0 .../GraphicsDriver/NVAPI/NvdrsSetting.cs | 0 .../GraphicsDriver/NVThreadedOptimization.cs | 0 .../Ryujinx.Common}/Hash128.cs | 0 .../Logging/Formatters/DefaultLogFormatter.cs | 0 .../Formatters/DynamicObjectFormatter.cs | 0 .../Logging/Formatters/ILogFormatter.cs | 0 .../Ryujinx.Common}/Logging/LogClass.cs | 0 .../Ryujinx.Common}/Logging/LogEventArgs.cs | 0 .../Logging/LogEventArgsJson.cs | 0 .../Logging/LogEventJsonSerializerContext.cs | 0 .../Ryujinx.Common}/Logging/LogLevel.cs | 0 .../Ryujinx.Common}/Logging/Logger.cs | 0 .../Logging/Targets/AsyncLogTargetWrapper.cs | 0 .../Logging/Targets/ConsoleLogTarget.cs | 0 .../Logging/Targets/FileLogTarget.cs | 0 .../Logging/Targets/ILogTarget.cs | 0 .../Logging/Targets/JsonLogTarget.cs | 0 .../Ryujinx.Common}/Memory/ArrayPtr.cs | 0 .../Ryujinx.Common}/Memory/Box.cs | 0 .../ByteMemoryPool.ByteMemoryPoolBuffer.cs | 0 .../Ryujinx.Common}/Memory/ByteMemoryPool.cs | 0 .../Ryujinx.Common}/Memory/IArray.cs | 0 .../Memory/MemoryStreamManager.cs | 0 .../PartialUnmaps/NativeReaderWriterLock.cs | 0 .../PartialUnmaps/PartialUnmapHelpers.cs | 0 .../Memory/PartialUnmaps/PartialUnmapState.cs | 0 .../Memory/PartialUnmaps/ThreadLocalMap.cs | 0 .../Ryujinx.Common}/Memory/Ptr.cs | 0 .../Ryujinx.Common}/Memory/SpanOrArray.cs | 0 .../Ryujinx.Common}/Memory/SpanReader.cs | 0 .../Ryujinx.Common}/Memory/SpanWriter.cs | 0 .../Memory/StructArrayHelpers.cs | 0 .../Memory/StructByteArrayHelpers.cs | 0 .../Ryujinx.Common}/PerformanceCounter.cs | 0 .../Ryujinx.Common}/Pools/ObjectPool.cs | 0 .../Ryujinx.Common}/Pools/SharedPools.cs | 0 .../Pools/ThreadStaticArray.cs | 0 .../Ryujinx.Common}/ReactiveObject.cs | 0 .../ReferenceEqualityComparer.cs | 0 .../Ryujinx.Common}/ReleaseInformation.cs | 0 .../Ryujinx.Common}/Ryujinx.Common.csproj | 0 .../SystemInfo/LinuxSystemInfo.cs | 0 .../SystemInfo/MacOSSystemInfo.cs | 0 .../Ryujinx.Common}/SystemInfo/SystemInfo.cs | 0 .../SystemInfo/WindowsSystemInfo.cs | 0 .../SystemInterop/DisplaySleep.cs | 0 .../SystemInterop/ForceDpiAware.cs | 0 .../SystemInterop/GdiPlusHelper.cs | 0 .../SystemInterop/StdErrAdapter.cs | 0 .../WindowsMultimediaTimerResolution.cs | 0 .../Ryujinx.Common}/Utilities/BitUtils.cs | 0 .../Utilities/BitfieldExtensions.cs | 0 .../Ryujinx.Common}/Utilities/Buffers.cs | 0 .../Utilities/CommonJsonContext.cs | 0 .../Utilities/EmbeddedResources.cs | 0 .../Ryujinx.Common}/Utilities/HexUtils.cs | 0 .../Ryujinx.Common}/Utilities/JsonHelper.cs | 0 .../Utilities/MessagePackObjectFormatter.cs | 0 .../Utilities/NetworkHelpers.cs | 0 .../Ryujinx.Common}/Utilities/SpanHelpers.cs | 0 .../Ryujinx.Common}/Utilities/StreamUtils.cs | 0 .../Utilities/TypedStringEnumConverter.cs | 0 .../Ryujinx.Common}/Utilities/UInt128Utils.cs | 0 .../Ryujinx.Common}/XXHash128.cs | 0 .../Ryujinx.Cpu}/AddressSpace.cs | 0 .../Ryujinx.Cpu}/AppleHv/Arm/ApFlags.cs | 0 .../AppleHv/Arm/ExceptionClass.cs | 0 .../AppleHv/DummyDiskCacheLoadState.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvAddressSpace.cs | 0 .../AppleHv/HvAddressSpaceRange.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvApi.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvCpuContext.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvEngine.cs | 0 .../AppleHv/HvExecutionContext.cs | 0 .../AppleHv/HvExecutionContextShadow.cs | 0 .../AppleHv/HvExecutionContextVcpu.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvIpaAllocator.cs | 0 .../AppleHv/HvMemoryBlockAllocation.cs | 0 .../AppleHv/HvMemoryBlockAllocator.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvMemoryManager.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvVcpu.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvVcpuPool.cs | 0 .../Ryujinx.Cpu}/AppleHv/HvVm.cs | 0 .../AppleHv/IHvExecutionContext.cs | 0 .../Ryujinx.Cpu}/ExceptionCallbacks.cs | 0 .../Ryujinx.Cpu}/ICpuContext.cs | 0 .../Ryujinx.Cpu}/ICpuEngine.cs | 0 .../Ryujinx.Cpu}/IDiskCacheState.cs | 0 .../Ryujinx.Cpu}/IExecutionContext.cs | 0 .../Ryujinx.Cpu}/ITickSource.cs | 0 .../IVirtualMemoryManagerTracked.cs | 0 .../Ryujinx.Cpu}/Jit/JitCpuContext.cs | 0 .../Ryujinx.Cpu}/Jit/JitDiskCacheLoadState.cs | 0 .../Ryujinx.Cpu}/Jit/JitEngine.cs | 0 .../Ryujinx.Cpu}/Jit/JitExecutionContext.cs | 0 .../Ryujinx.Cpu}/Jit/JitMemoryAllocator.cs | 0 .../Ryujinx.Cpu}/Jit/JitMemoryBlock.cs | 0 .../Ryujinx.Cpu}/Jit/MemoryManager.cs | 0 .../Jit/MemoryManagerHostMapped.cs | 0 {Ryujinx.Cpu => src/Ryujinx.Cpu}/LoadState.cs | 0 .../Ryujinx.Cpu}/MemoryEhMeilleure.cs | 0 .../Ryujinx.Cpu}/MemoryHelper.cs | 0 .../Ryujinx.Cpu}/MemoryManagerBase.cs | 0 .../Ryujinx.Cpu}/PrivateMemoryAllocation.cs | 0 .../Ryujinx.Cpu}/PrivateMemoryAllocator.cs | 0 .../Ryujinx.Cpu}/Ryujinx.Cpu.csproj | 0 .../Ryujinx.Cpu}/TickSource.cs | 0 .../Tracking/CpuMultiRegionHandle.cs | 0 .../Ryujinx.Cpu}/Tracking/CpuRegionHandle.cs | 0 .../Tracking/CpuSmartMultiRegionHandle.cs | 0 .../Ryujinx.Graphics.Device}/DeviceState.cs | 0 .../Ryujinx.Graphics.Device}/IDeviceState.cs | 0 .../IDeviceStateWithContext.cs | 0 .../Ryujinx.Graphics.Device}/RwCallback.cs | 0 .../Ryujinx.Graphics.Device.csproj | 0 .../SizeCalculator.cs | 0 .../Ryujinx.Graphics.GAL}/AddressMode.cs | 0 .../AdvancedBlendDescriptor.cs | 0 .../Ryujinx.Graphics.GAL}/AdvancedBlendOp.cs | 0 .../AdvancedBlendOverlap.cs | 0 .../Ryujinx.Graphics.GAL}/AntiAliasing.cs | 0 .../Ryujinx.Graphics.GAL}/BlendDescriptor.cs | 0 .../Ryujinx.Graphics.GAL}/BlendFactor.cs | 0 .../Ryujinx.Graphics.GAL}/BlendOp.cs | 0 .../Ryujinx.Graphics.GAL}/BufferAssignment.cs | 0 .../Ryujinx.Graphics.GAL}/BufferHandle.cs | 0 .../Ryujinx.Graphics.GAL}/BufferRange.cs | 0 .../Ryujinx.Graphics.GAL}/Capabilities.cs | 0 .../Ryujinx.Graphics.GAL}/ColorF.cs | 0 .../Ryujinx.Graphics.GAL}/CompareMode.cs | 0 .../Ryujinx.Graphics.GAL}/CompareOp.cs | 0 .../Ryujinx.Graphics.GAL}/CounterType.cs | 0 .../Ryujinx.Graphics.GAL}/DepthMode.cs | 0 .../Ryujinx.Graphics.GAL}/DepthStencilMode.cs | 0 .../DepthTestDescriptor.cs | 0 .../Ryujinx.Graphics.GAL}/DeviceInfo.cs | 0 .../Ryujinx.Graphics.GAL}/Extents2D.cs | 0 .../Ryujinx.Graphics.GAL}/Extents2DF.cs | 0 .../Ryujinx.Graphics.GAL}/Face.cs | 0 .../Ryujinx.Graphics.GAL}/Format.cs | 0 .../Ryujinx.Graphics.GAL}/FrontFace.cs | 0 .../Ryujinx.Graphics.GAL}/HardwareInfo.cs | 0 .../Ryujinx.Graphics.GAL}/ICounterEvent.cs | 0 .../Ryujinx.Graphics.GAL}/IPipeline.cs | 0 .../Ryujinx.Graphics.GAL}/IProgram.cs | 0 .../Ryujinx.Graphics.GAL}/IRenderer.cs | 0 .../Ryujinx.Graphics.GAL}/ISampler.cs | 0 .../Ryujinx.Graphics.GAL}/ITexture.cs | 0 .../Ryujinx.Graphics.GAL}/IWindow.cs | 0 .../Ryujinx.Graphics.GAL}/ImageCrop.cs | 0 .../Ryujinx.Graphics.GAL}/IndexType.cs | 0 .../Ryujinx.Graphics.GAL}/LogicalOp.cs | 0 .../Ryujinx.Graphics.GAL}/MagFilter.cs | 0 .../Ryujinx.Graphics.GAL}/MinFilter.cs | 0 .../MultisampleDescriptor.cs | 0 .../Multithreading/BufferMap.cs | 0 .../Multithreading/CommandHelper.cs | 0 .../Multithreading/CommandType.cs | 0 .../Multithreading/Commands/BarrierCommand.cs | 0 .../Commands/BeginTransformFeedbackCommand.cs | 0 .../Commands/Buffer/BufferDisposeCommand.cs | 0 .../Commands/Buffer/BufferGetDataCommand.cs | 0 .../Commands/Buffer/BufferSetDataCommand.cs | 0 .../Commands/ClearBufferCommand.cs | 0 .../Commands/ClearRenderTargetColorCommand.cs | 0 .../ClearRenderTargetDepthStencilCommand.cs | 0 .../Commands/CommandBufferBarrierCommand.cs | 0 .../Commands/CopyBufferCommand.cs | 0 .../CounterEventDisposeCommand.cs | 0 .../CounterEvent/CounterEventFlushCommand.cs | 0 .../Commands/DispatchComputeCommand.cs | 0 .../Multithreading/Commands/DrawCommand.cs | 0 .../Commands/DrawIndexedCommand.cs | 0 .../Commands/DrawIndexedIndirectCommand.cs | 0 .../DrawIndexedIndirectCountCommand.cs | 0 .../Commands/DrawIndirectCommand.cs | 0 .../Commands/DrawIndirectCountCommand.cs | 0 .../Commands/DrawTextureCommand.cs | 0 .../EndHostConditionalRenderingCommand.cs | 0 .../Commands/EndTransformFeedbackCommand.cs | 0 .../Multithreading/Commands/IGALCommand.cs | 0 .../Program/ProgramCheckLinkCommand.cs | 0 .../Commands/Program/ProgramDisposeCommand.cs | 0 .../Program/ProgramGetBinaryCommand.cs | 0 .../Commands/Renderer/ActionCommand.cs | 0 .../Commands/Renderer/CreateBufferCommand.cs | 0 .../Commands/Renderer/CreateProgramCommand.cs | 0 .../Commands/Renderer/CreateSamplerCommand.cs | 0 .../Commands/Renderer/CreateSyncCommand.cs | 0 .../Commands/Renderer/CreateTextureCommand.cs | 0 .../Renderer/GetCapabilitiesCommand.cs | 0 .../Commands/Renderer/PreFrameCommand.cs | 0 .../Commands/Renderer/ReportCounterCommand.cs | 0 .../Commands/Renderer/ResetCounterCommand.cs | 0 .../Renderer/UpdateCountersCommand.cs | 0 .../Commands/Sampler/SamplerDisposeCommand.cs | 0 .../Commands/SetAlphaTestCommand.cs | 0 .../Commands/SetBlendStateAdvancedCommand.cs | 0 .../Commands/SetBlendStateCommand.cs | 0 .../Commands/SetDepthBiasCommand.cs | 0 .../Commands/SetDepthClampCommand.cs | 0 .../Commands/SetDepthModeCommand.cs | 0 .../Commands/SetDepthTestCommand.cs | 0 .../Commands/SetFaceCullingCommand.cs | 0 .../Commands/SetFrontFaceCommand.cs | 0 .../Commands/SetImageCommand.cs | 0 .../Commands/SetIndexBufferCommand.cs | 0 .../Commands/SetLineParametersCommand.cs | 0 .../Commands/SetLogicOpStateCommand.cs | 0 .../Commands/SetMultisampleStateCommand.cs | 0 .../Commands/SetPatchParametersCommand.cs | 0 .../Commands/SetPointParametersCommand.cs | 0 .../Commands/SetPolygonModeCommand.cs | 0 .../Commands/SetPrimitiveRestartCommand.cs | 0 .../Commands/SetPrimitiveTopologyCommand.cs | 0 .../Commands/SetProgramCommand.cs | 0 .../Commands/SetRasterizerDiscardCommand.cs | 0 .../SetRenderTargetColorMasksCommand.cs | 0 .../Commands/SetRenderTargetScaleCommand.cs | 0 .../Commands/SetRenderTargetsCommand.cs | 0 .../Commands/SetScissorsCommand.cs | 0 .../Commands/SetStencilTestCommand.cs | 0 .../Commands/SetStorageBuffersCommand.cs | 0 .../Commands/SetTextureAndSamplerCommand.cs | 0 .../SetTransformFeedbackBuffersCommand.cs | 0 .../Commands/SetUniformBuffersCommand.cs | 0 .../Commands/SetUserClipDistanceCommand.cs | 0 .../Commands/SetVertexAttribsCommand.cs | 0 .../Commands/SetVertexBuffersCommand.cs | 0 .../Commands/SetViewportsCommand.cs | 0 .../Commands/Texture/TextureCopyToCommand.cs | 0 .../Texture/TextureCopyToScaledCommand.cs | 0 .../Texture/TextureCopyToSliceCommand.cs | 0 .../Texture/TextureCreateViewCommand.cs | 0 .../Commands/Texture/TextureGetDataCommand.cs | 0 .../Texture/TextureGetDataSliceCommand.cs | 0 .../Commands/Texture/TextureReleaseCommand.cs | 0 .../Commands/Texture/TextureSetDataCommand.cs | 0 .../Texture/TextureSetDataSliceCommand.cs | 0 .../TextureSetDataSliceRegionCommand.cs | 0 .../Texture/TextureSetStorageCommand.cs | 0 .../Commands/TextureBarrierCommand.cs | 0 .../Commands/TextureBarrierTiledCommand.cs | 0 .../TryHostConditionalRenderingCommand.cs | 0 ...TryHostConditionalRenderingFlushCommand.cs | 0 .../Commands/UpdateRenderScaleCommand.cs | 0 .../Commands/Window/WindowPresentCommand.cs | 0 .../Multithreading/Model/CircularSpanPool.cs | 0 .../Multithreading/Model/ResultBox.cs | 0 .../Multithreading/Model/SpanRef.cs | 0 .../Multithreading/Model/TableRef.cs | 0 .../Multithreading/Resources/ProgramQueue.cs | 0 .../Programs/BinaryProgramRequest.cs | 0 .../Resources/Programs/IProgramRequest.cs | 0 .../Programs/SourceProgramRequest.cs | 0 .../Resources/ThreadedCounterEvent.cs | 0 .../Resources/ThreadedProgram.cs | 0 .../Resources/ThreadedSampler.cs | 0 .../Resources/ThreadedTexture.cs | 0 .../Multithreading/SyncMap.cs | 0 .../Multithreading/ThreadedHelpers.cs | 0 .../Multithreading/ThreadedPipeline.cs | 0 .../Multithreading/ThreadedRenderer.cs | 0 .../Multithreading/ThreadedWindow.cs | 0 .../Ryujinx.Graphics.GAL}/Origin.cs | 0 .../Ryujinx.Graphics.GAL}/PinnedSpan.cs | 0 .../Ryujinx.Graphics.GAL}/PolygonMode.cs | 0 .../Ryujinx.Graphics.GAL}/PolygonModeMask.cs | 0 .../PrimitiveTopology.cs | 0 .../ProgramLinkStatus.cs | 0 .../ProgramPipelineState.cs | 0 .../Ryujinx.Graphics.GAL}/Rectangle.cs | 0 .../Ryujinx.Graphics.GAL.csproj | 0 .../SamplerCreateInfo.cs | 0 .../ScreenCaptureImageInfo.cs | 0 .../Ryujinx.Graphics.GAL}/ShaderBindings.cs | 0 .../Ryujinx.Graphics.GAL}/ShaderInfo.cs | 0 .../Ryujinx.Graphics.GAL}/ShaderSource.cs | 0 .../Ryujinx.Graphics.GAL}/StencilOp.cs | 0 .../StencilTestDescriptor.cs | 0 .../SupportBufferUpdater.cs | 0 .../Ryujinx.Graphics.GAL}/SwizzleComponent.cs | 0 .../Ryujinx.Graphics.GAL}/Target.cs | 0 .../TextureCreateInfo.cs | 0 .../TextureReleaseCallback.cs | 0 .../Ryujinx.Graphics.GAL}/UpscaleType.cs | 0 .../VertexAttribDescriptor.cs | 0 .../VertexBufferDescriptor.cs | 0 .../Ryujinx.Graphics.GAL}/Viewport.cs | 0 .../Ryujinx.Graphics.GAL}/ViewportSwizzle.cs | 0 .../Ryujinx.Graphics.Gpu}/ClassId.cs | 0 .../Ryujinx.Graphics.Gpu}/Constants.cs | 0 .../Engine/Compute/ComputeClass.cs | 0 .../Engine/Compute/ComputeClassState.cs | 0 .../Engine/Compute/ComputeQmd.cs | 0 .../Engine/ConditionalRenderEnabled.cs | 0 .../Engine/DeviceStateWithShadow.cs | 0 .../Engine/Dma/DmaClass.cs | 0 .../Engine/Dma/DmaClassState.cs | 0 .../Engine/Dma/DmaTexture.cs | 0 .../Engine/GPFifo/CompressedMethod.cs | 0 .../Engine/GPFifo/GPEntry.cs | 0 .../Engine/GPFifo/GPFifoClass.cs | 0 .../Engine/GPFifo/GPFifoClassState.cs | 0 .../Engine/GPFifo/GPFifoDevice.cs | 0 .../Engine/GPFifo/GPFifoProcessor.cs | 0 .../InlineToMemory/InlineToMemoryClass.cs | 0 .../InlineToMemoryClassState.cs | 0 .../Engine/MME/AluOperation.cs | 0 .../Engine/MME/AluRegOperation.cs | 0 .../Engine/MME/AssignmentOperation.cs | 0 .../Engine/MME/IMacroEE.cs | 0 .../Ryujinx.Graphics.Gpu}/Engine/MME/Macro.cs | 0 .../Engine/MME/MacroHLE.cs | 0 .../Engine/MME/MacroHLEFunctionName.cs | 0 .../Engine/MME/MacroHLETable.cs | 0 .../Engine/MME/MacroInterpreter.cs | 0 .../Engine/MME/MacroJit.cs | 0 .../Engine/MME/MacroJitCompiler.cs | 0 .../Engine/MME/MacroJitContext.cs | 0 .../Engine/MmeShadowScratch.cs | 0 .../Engine/SetMmeShadowRamControlMode.cs | 0 .../Engine/ShaderTexture.cs | 0 .../Threed/Blender/AdvancedBlendFunctions.cs | 0 .../Threed/Blender/AdvancedBlendManager.cs | 0 .../Blender/AdvancedBlendPreGenTable.cs | 0 .../Threed/Blender/AdvancedBlendUcode.cs | 0 .../Engine/Threed/Blender/UcodeAssembler.cs | 0 .../Engine/Threed/ConditionalRendering.cs | 0 .../Engine/Threed/ConstantBufferUpdater.cs | 0 .../Engine/Threed/DrawManager.cs | 0 .../Engine/Threed/DrawState.cs | 0 .../Engine/Threed/IbStreamer.cs | 0 .../Engine/Threed/IndirectDrawType.cs | 0 .../Engine/Threed/RenderTargetUpdateFlags.cs | 0 .../Engine/Threed/SemaphoreUpdater.cs | 0 .../Threed/SpecializationStateUpdater.cs | 0 .../Engine/Threed/StateUpdateTracker.cs | 0 .../Engine/Threed/StateUpdater.cs | 0 .../Engine/Threed/ThreedClass.cs | 0 .../Engine/Threed/ThreedClassState.cs | 0 .../Engine/Twod/TwodClass.cs | 0 .../Engine/Twod/TwodClassState.cs | 0 .../Engine/Twod/TwodTexture.cs | 0 .../Engine/Types/Boolean32.cs | 0 .../Engine/Types/ColorFormat.cs | 0 .../Engine/Types/GpuVa.cs | 0 .../Engine/Types/MemoryLayout.cs | 0 .../Engine/Types/PrimitiveType.cs | 0 .../Engine/Types/SamplerIndex.cs | 0 .../Engine/Types/SbDescriptor.cs | 0 .../Engine/Types/ZetaFormat.cs | 0 .../Ryujinx.Graphics.Gpu}/GpuChannel.cs | 0 .../Ryujinx.Graphics.Gpu}/GpuContext.cs | 0 .../Ryujinx.Graphics.Gpu}/GraphicsConfig.cs | 0 .../Image/AutoDeleteCache.cs | 0 .../Ryujinx.Graphics.Gpu}/Image/FormatInfo.cs | 0 .../Image/FormatTable.cs | 0 .../Image/ITextureDescriptor.cs | 0 .../Ryujinx.Graphics.Gpu}/Image/Pool.cs | 0 .../Ryujinx.Graphics.Gpu}/Image/PoolCache.cs | 0 .../Image/ReductionFilter.cs | 0 .../Ryujinx.Graphics.Gpu}/Image/Sampler.cs | 0 .../Image/SamplerDescriptor.cs | 0 .../Image/SamplerMinFilter.cs | 0 .../Image/SamplerMipFilter.cs | 0 .../Image/SamplerPool.cs | 0 .../Image/SamplerPoolCache.cs | 0 .../Ryujinx.Graphics.Gpu}/Image/Texture.cs | 0 .../Image/TextureBindingInfo.cs | 0 .../Image/TextureBindingsManager.cs | 0 .../Image/TextureCache.cs | 0 .../Image/TextureCompatibility.cs | 0 .../Image/TextureComponent.cs | 0 .../Image/TextureDependency.cs | 0 .../Image/TextureDescriptor.cs | 0 .../Image/TextureDescriptorType.cs | 0 .../Image/TextureGroup.cs | 0 .../Image/TextureGroupHandle.cs | 0 .../Image/TextureInfo.cs | 0 .../Image/TextureManager.cs | 0 .../Image/TextureMatchQuality.cs | 0 .../Image/TextureMsaaMode.cs | 0 .../Image/TexturePool.cs | 0 .../Image/TexturePoolCache.cs | 0 .../Image/TextureScaleMode.cs | 0 .../Image/TextureSearchFlags.cs | 0 .../Image/TextureTarget.cs | 0 .../Image/TextureViewCompatibility.cs | 0 .../Ryujinx.Graphics.Gpu}/Memory/Buffer.cs | 0 .../Memory/BufferBounds.cs | 0 .../Memory/BufferCache.cs | 0 .../Memory/BufferCacheEntry.cs | 0 .../Memory/BufferManager.cs | 0 .../Memory/BufferMigration.cs | 0 .../Memory/BufferModifiedRangeList.cs | 0 .../Memory/BufferTextureBinding.cs | 0 .../Memory/CounterCache.cs | 0 .../Memory/GpuRegionHandle.cs | 0 .../Memory/IndexBuffer.cs | 0 .../Memory/MemoryManager.cs | 0 .../Memory/MultiRangeWritableBlock.cs | 0 .../Memory/PhysicalMemory.cs | 0 .../Ryujinx.Graphics.Gpu}/Memory/PteKind.cs | 0 .../Memory/ResourceKind.cs | 0 .../Memory/UnmapEventArgs.cs | 0 .../Memory/VertexBuffer.cs | 0 .../Ryujinx.Graphics.Gpu.csproj | 0 .../Shader/CachedShaderBindings.cs | 0 .../Shader/CachedShaderProgram.cs | 0 .../Shader/CachedShaderStage.cs | 0 .../Shader/ComputeShaderCacheHashTable.cs | 0 .../DiskCache/BackgroundDiskCacheWriter.cs | 0 .../Shader/DiskCache/BinarySerializer.cs | 0 .../Shader/DiskCache/CompressionAlgorithm.cs | 0 .../Shader/DiskCache/DiskCacheCommon.cs | 0 .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 0 .../Shader/DiskCache/DiskCacheGuestStorage.cs | 0 .../Shader/DiskCache/DiskCacheHostStorage.cs | 0 .../DiskCache/DiskCacheLoadException.cs | 0 .../Shader/DiskCache/DiskCacheLoadResult.cs | 0 .../DiskCache/DiskCacheOutputStreams.cs | 0 .../Shader/DiskCache/GuestCodeAndCbData.cs | 0 .../DiskCache/ParallelDiskCacheLoader.cs | 0 .../DiskCache/ShaderBinarySerializer.cs | 0 .../Shader/GpuAccessor.cs | 0 .../Shader/GpuAccessorBase.cs | 0 .../Shader/GpuAccessorState.cs | 0 .../Shader/GpuChannelComputeState.cs | 0 .../Shader/GpuChannelGraphicsState.cs | 0 .../Shader/GpuChannelPoolState.cs | 0 .../Shader/HashTable/HashState.cs | 0 .../Shader/HashTable/IDataAccessor.cs | 0 .../Shader/HashTable/PartitionHashTable.cs | 0 .../Shader/HashTable/PartitionedHashTable.cs | 0 .../Shader/HashTable/SmartDataAccessor.cs | 0 .../Shader/ResourceCounts.cs | 0 .../Shader/ShaderAddresses.cs | 0 .../Shader/ShaderCache.cs | 0 .../Shader/ShaderCacheHashTable.cs | 0 .../Shader/ShaderCacheState.cs | 0 .../Shader/ShaderCodeAccessor.cs | 0 .../Shader/ShaderDumpPaths.cs | 0 .../Shader/ShaderDumper.cs | 0 .../Shader/ShaderSpecializationList.cs | 0 .../Shader/ShaderSpecializationState.cs | 0 .../Shader/TransformFeedbackDescriptor.cs | 0 .../Synchronization/SynchronizationManager.cs | 0 .../Synchronization/Syncpoint.cs | 0 .../Synchronization/SyncpointWaiterHandle.cs | 0 .../Ryujinx.Graphics.Gpu}/Window.cs | 0 .../Ryujinx.Graphics.Host1x}/ClassId.cs | 0 .../Ryujinx.Graphics.Host1x}/Devices.cs | 0 .../Ryujinx.Graphics.Host1x}/Host1xClass.cs | 0 .../Host1xClassRegisters.cs | 0 .../Ryujinx.Graphics.Host1x}/Host1xDevice.cs | 0 .../Ryujinx.Graphics.Host1x}/OpCode.cs | 0 .../Ryujinx.Graphics.Host1x.csproj | 0 .../SyncptIncrManager.cs | 0 .../Ryujinx.Graphics.Host1x}/ThiDevice.cs | 0 .../Ryujinx.Graphics.Host1x}/ThiRegisters.cs | 0 .../FFmpegContext.cs | 0 .../H264/Decoder.cs | 0 .../H264/H264BitStreamWriter.cs | 0 .../H264/SpsAndPpsReconstruction.cs | 0 .../Native/AVCodec.cs | 0 .../Native/AVCodec501.cs | 0 .../Native/AVCodecContext.cs | 0 .../Native/AVCodecID.cs | 0 .../Native/AVFrame.cs | 0 .../Native/AVLog.cs | 0 .../Native/AVPacket.cs | 0 .../Native/AVRational.cs | 0 .../Native/FFCodec.cs | 0 .../Native/FFCodecLegacy.cs | 0 .../Native/FFmpegApi.cs | 0 .../Ryujinx.Graphics.Nvdec.FFmpeg.csproj | 0 .../Ryujinx.Graphics.Nvdec.FFmpeg}/Surface.cs | 0 .../Vp8/Decoder.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/BitDepth.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/CodecErr.cs | 0 .../Common/BitUtils.cs | 0 .../Common/MemoryAllocator.cs | 0 .../Common/MemoryUtil.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Constants.cs | 0 .../DecodeFrame.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/DecodeMv.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Decoder.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Detokenize.cs | 0 .../Dsp/Convolve.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Dsp/Filter.cs | 0 .../Dsp/IntraPred.cs | 0 .../Dsp/InvTxfm.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Dsp/Prob.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Dsp/Reader.cs | 0 .../Dsp/TxfmCommon.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Idct.cs | 0 .../InternalErrorException.cs | 0 .../InternalErrorInfo.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/LoopFilter.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Luts.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/PredCommon.cs | 0 .../QuantCommon.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/ReconInter.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/ReconIntra.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9.csproj | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/TileBuffer.cs | 0 .../TileWorkerData.cs | 0 .../Types/BModeInfo.cs | 0 .../Types/BlockSize.cs | 0 .../Types/Buf2D.cs | 0 .../Types/FrameType.cs | 0 .../Types/LoopFilter.cs | 0 .../Types/LoopFilterInfoN.cs | 0 .../Types/LoopFilterMask.cs | 0 .../Types/LoopFilterThresh.cs | 0 .../Types/MacroBlockD.cs | 0 .../Types/MacroBlockDPlane.cs | 0 .../Types/ModeInfo.cs | 0 .../Types/MotionVectorContext.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Types/Mv.cs | 0 .../Ryujinx.Graphics.Nvdec.Vp9}/Types/Mv32.cs | 0 .../Types/MvClassType.cs | 0 .../Types/MvJointType.cs | 0 .../Types/MvRef.cs | 0 .../Types/PartitionType.cs | 0 .../Types/PlaneType.cs | 0 .../Types/Position.cs | 0 .../Types/PredictionMode.cs | 0 .../Types/RefBuffer.cs | 0 .../Types/ReferenceMode.cs | 0 .../Types/ScaleFactors.cs | 0 .../Types/SegLvlFeatures.cs | 0 .../Types/Segmentation.cs | 0 .../Types/Surface.cs | 0 .../Types/TileInfo.cs | 0 .../Types/TxMode.cs | 0 .../Types/TxSize.cs | 0 .../Types/TxType.cs | 0 .../Types/Vp9Common.cs | 0 .../Ryujinx.Graphics.Nvdec}/ApplicationId.cs | 0 .../Ryujinx.Graphics.Nvdec}/H264Decoder.cs | 0 .../Image/SurfaceCache.cs | 0 .../Image/SurfaceCommon.cs | 0 .../Image/SurfaceReader.cs | 0 .../Image/SurfaceWriter.cs | 0 .../MemoryExtensions.cs | 0 .../NvdecDecoderContext.cs | 0 .../Ryujinx.Graphics.Nvdec}/NvdecDevice.cs | 0 .../Ryujinx.Graphics.Nvdec}/NvdecRegisters.cs | 0 .../Ryujinx.Graphics.Nvdec}/NvdecStatus.cs | 0 .../ResourceManager.cs | 0 .../Ryujinx.Graphics.Nvdec.csproj | 0 .../Types/H264/PictureInfo.cs | 0 .../Types/H264/ReferenceFrame.cs | 0 .../Types/Vp8/PictureInfo.cs | 0 .../Types/Vp9/BackwardUpdates.cs | 0 .../Types/Vp9/EntropyProbs.cs | 0 .../Types/Vp9/FrameFlags.cs | 0 .../Types/Vp9/FrameSize.cs | 0 .../Types/Vp9/FrameStats.cs | 0 .../Types/Vp9/LoopFilter.cs | 0 .../Types/Vp9/PictureInfo.cs | 0 .../Types/Vp9/Segmentation.cs | 0 .../Ryujinx.Graphics.Nvdec}/Vp8Decoder.cs | 0 .../Ryujinx.Graphics.Nvdec}/Vp9Decoder.cs | 0 .../BackgroundContextWorker.cs | 0 .../Ryujinx.Graphics.OpenGL}/Buffer.cs | 0 .../Ryujinx.Graphics.OpenGL}/Constants.cs | 0 .../Ryujinx.Graphics.OpenGL}/Debugger.cs | 0 .../DrawTextureEmulation.cs | 0 .../Effects/FsrScalingFilter.cs | 0 .../Effects/FxaaPostProcessingEffect.cs | 0 .../Effects/IPostProcessingEffect.cs | 0 .../Effects/IScalingFilter.cs | 0 .../Effects/ShaderHelper.cs | 0 .../Effects/Shaders/ffx_a.h | 0 .../Effects/Shaders/ffx_fsr1.h | 0 .../Effects/Shaders/fsr_scaling.glsl | 0 .../Effects/Shaders/fsr_sharpening.glsl | 0 .../Effects/Shaders/fxaa.glsl | 0 .../Effects/Shaders/smaa.hlsl | 0 .../Effects/Shaders/smaa_blend.glsl | 0 .../Effects/Shaders/smaa_edge.glsl | 0 .../Effects/Shaders/smaa_neighbour.glsl | 0 .../Effects/SmaaPostProcessingEffect.cs | 0 .../Effects/Textures/SmaaAreaTexture.bin | Bin .../Effects/Textures/SmaaSearchTexture.bin | Bin .../EnumConversion.cs | 0 .../Ryujinx.Graphics.OpenGL}/FormatInfo.cs | 0 .../Ryujinx.Graphics.OpenGL}/FormatTable.cs | 0 .../Ryujinx.Graphics.OpenGL}/Framebuffer.cs | 0 .../Ryujinx.Graphics.OpenGL}/Handle.cs | 0 .../Helper/GLXHelper.cs | 0 .../Helper/WGLHelper.cs | 0 .../HwCapabilities.cs | 0 .../IOpenGLContext.cs | 0 .../Image/FormatConverter.cs | 0 .../Image/ITextureInfo.cs | 0 .../Image/IntermmediatePool.cs | 0 .../Ryujinx.Graphics.OpenGL}/Image/Sampler.cs | 0 .../Image/TextureBase.cs | 0 .../Image/TextureBuffer.cs | 0 .../Image/TextureCopy.cs | 0 .../Image/TextureCopyIncompatible.cs | 0 .../Image/TextureCopyMS.cs | 0 .../Image/TextureStorage.cs | 0 .../Image/TextureView.cs | 0 .../OpenGLRenderer.cs | 0 .../PersistentBuffers.cs | 0 .../Ryujinx.Graphics.OpenGL}/Pipeline.cs | 0 .../Ryujinx.Graphics.OpenGL}/Program.cs | 0 .../Queries/BufferedQuery.cs | 0 .../Queries/CounterQueue.cs | 0 .../Queries/CounterQueueEvent.cs | 0 .../Queries/Counters.cs | 0 .../Ryujinx.Graphics.OpenGL}/ResourcePool.cs | 0 .../Ryujinx.Graphics.OpenGL.csproj | 0 .../Ryujinx.Graphics.OpenGL}/Sync.cs | 0 .../Ryujinx.Graphics.OpenGL}/VertexArray.cs | 0 .../Ryujinx.Graphics.OpenGL}/Window.cs | 0 .../Ryujinx.Graphics.Shader}/AlphaTestOp.cs | 0 .../Ryujinx.Graphics.Shader}/AttributeType.cs | 0 .../BufferDescriptor.cs | 0 .../BufferUsageFlags.cs | 0 .../CodeGen/Glsl/CodeGenContext.cs | 0 .../CodeGen/Glsl/Declarations.cs | 0 .../CodeGen/Glsl/DefaultNames.cs | 0 .../CodeGen/Glsl/GlslGenerator.cs | 0 .../AtomicMinMaxS32Shared.glsl | 0 .../AtomicMinMaxS32Storage.glsl | 0 .../HelperFunctions/HelperFunctionNames.cs | 0 .../Glsl/HelperFunctions/MultiplyHighS32.glsl | 0 .../Glsl/HelperFunctions/MultiplyHighU32.glsl | 0 .../CodeGen/Glsl/HelperFunctions/Shuffle.glsl | 0 .../Glsl/HelperFunctions/ShuffleDown.glsl | 0 .../Glsl/HelperFunctions/ShuffleUp.glsl | 0 .../Glsl/HelperFunctions/ShuffleXor.glsl | 0 .../HelperFunctions/StoreSharedSmallInt.glsl | 0 .../HelperFunctions/StoreStorageSmallInt.glsl | 0 .../Glsl/HelperFunctions/SwizzleAdd.glsl | 0 .../HelperFunctions/TexelFetchScale_cp.glsl | 0 .../HelperFunctions/TexelFetchScale_fp.glsl | 0 .../HelperFunctions/TexelFetchScale_vp.glsl | 0 .../CodeGen/Glsl/Instructions/InstGen.cs | 0 .../Glsl/Instructions/InstGenBallot.cs | 0 .../CodeGen/Glsl/Instructions/InstGenCall.cs | 0 .../CodeGen/Glsl/Instructions/InstGenFSI.cs | 0 .../Glsl/Instructions/InstGenHelper.cs | 0 .../Glsl/Instructions/InstGenMemory.cs | 0 .../Glsl/Instructions/InstGenPacking.cs | 0 .../Glsl/Instructions/InstGenVector.cs | 0 .../CodeGen/Glsl/Instructions/InstInfo.cs | 0 .../CodeGen/Glsl/Instructions/InstType.cs | 0 .../CodeGen/Glsl/Instructions/IoMap.cs | 0 .../CodeGen/Glsl/NumberFormatter.cs | 0 .../CodeGen/Glsl/OperandManager.cs | 0 .../CodeGen/Glsl/TypeConversion.cs | 0 .../CodeGen/Spirv/CodeGenContext.cs | 0 .../CodeGen/Spirv/Declarations.cs | 0 .../CodeGen/Spirv/EnumConversion.cs | 0 .../CodeGen/Spirv/Instructions.cs | 0 .../CodeGen/Spirv/IoMap.cs | 0 .../CodeGen/Spirv/OperationResult.cs | 0 .../CodeGen/Spirv/ScalingHelpers.cs | 0 .../CodeGen/Spirv/SpirvDelegates.cs | 0 .../CodeGen/Spirv/SpirvGenerator.cs | 0 .../CodeGen/Spirv/TextureMeta.cs | 0 .../Ryujinx.Graphics.Shader}/Constants.cs | 0 .../Decoders/Block.cs | 0 .../Decoders/DecodedFunction.cs | 0 .../Decoders/DecodedProgram.cs | 0 .../Decoders/Decoder.cs | 0 .../Decoders/FunctionType.cs | 0 .../Decoders/InstDecoders.cs | 0 .../Decoders/InstName.cs | 0 .../Decoders/InstOp.cs | 0 .../Decoders/InstProps.cs | 0 .../Decoders/InstTable.cs | 0 .../Decoders/Register.cs | 0 .../Decoders/RegisterConsts.cs | 0 .../Decoders/RegisterType.cs | 0 .../Ryujinx.Graphics.Shader}/IGpuAccessor.cs | 0 .../Ryujinx.Graphics.Shader}/InputTopology.cs | 0 .../Instructions/AttributeMap.cs | 0 .../Instructions/InstEmit.cs | 0 .../Instructions/InstEmitAluHelper.cs | 0 .../Instructions/InstEmitAttribute.cs | 0 .../Instructions/InstEmitBarrier.cs | 0 .../Instructions/InstEmitBitfield.cs | 0 .../Instructions/InstEmitConditionCode.cs | 0 .../Instructions/InstEmitConversion.cs | 0 .../Instructions/InstEmitFloatArithmetic.cs | 0 .../Instructions/InstEmitFloatComparison.cs | 0 .../Instructions/InstEmitFloatMinMax.cs | 0 .../Instructions/InstEmitFlowControl.cs | 0 .../Instructions/InstEmitHelper.cs | 0 .../Instructions/InstEmitIntegerArithmetic.cs | 0 .../Instructions/InstEmitIntegerComparison.cs | 0 .../Instructions/InstEmitIntegerLogical.cs | 0 .../Instructions/InstEmitIntegerMinMax.cs | 0 .../Instructions/InstEmitMemory.cs | 0 .../Instructions/InstEmitMove.cs | 0 .../Instructions/InstEmitMultifunction.cs | 0 .../Instructions/InstEmitNop.cs | 0 .../Instructions/InstEmitPredicate.cs | 0 .../Instructions/InstEmitShift.cs | 0 .../Instructions/InstEmitSurface.cs | 0 .../Instructions/InstEmitTexture.cs | 0 .../Instructions/InstEmitVideoArithmetic.cs | 0 .../Instructions/InstEmitVideoMinMax.cs | 0 .../Instructions/InstEmitWarp.cs | 0 .../Instructions/InstEmitter.cs | 0 .../Instructions/Lop3Expression.cs | 0 .../IntermediateRepresentation/BasicBlock.cs | 0 .../IntermediateRepresentation/CommentNode.cs | 0 .../IntermediateRepresentation/Function.cs | 0 .../IntermediateRepresentation/INode.cs | 0 .../IntermediateRepresentation/Instruction.cs | 0 .../IntermediateRepresentation/IoVariable.cs | 0 .../IntermediateRepresentation/IrConsts.cs | 0 .../IntermediateRepresentation/Operand.cs | 0 .../OperandHelper.cs | 0 .../IntermediateRepresentation/OperandType.cs | 0 .../IntermediateRepresentation/Operation.cs | 0 .../IntermediateRepresentation/PhiNode.cs | 0 .../IntermediateRepresentation/StorageKind.cs | 0 .../TextureFlags.cs | 0 .../TextureOperation.cs | 0 .../OutputTopology.cs | 0 .../Ryujinx.Graphics.Shader.csproj | 0 .../Ryujinx.Graphics.Shader}/SamplerType.cs | 0 .../ShaderIdentification.cs | 0 .../Ryujinx.Graphics.Shader}/ShaderProgram.cs | 0 .../ShaderProgramInfo.cs | 0 .../Ryujinx.Graphics.Shader}/ShaderStage.cs | 0 .../StructuredIr/AstAssignment.cs | 0 .../StructuredIr/AstBlock.cs | 0 .../StructuredIr/AstBlockType.cs | 0 .../StructuredIr/AstBlockVisitor.cs | 0 .../StructuredIr/AstComment.cs | 0 .../StructuredIr/AstHelper.cs | 0 .../StructuredIr/AstNode.cs | 0 .../StructuredIr/AstOperand.cs | 0 .../StructuredIr/AstOperation.cs | 0 .../StructuredIr/AstOptimizer.cs | 0 .../StructuredIr/AstTextureOperation.cs | 0 .../StructuredIr/GotoElimination.cs | 0 .../StructuredIr/GotoStatement.cs | 0 .../StructuredIr/HelperFunctionsMask.cs | 0 .../StructuredIr/IAstNode.cs | 0 .../StructuredIr/InstructionInfo.cs | 0 .../StructuredIr/IoDefinition.cs | 0 .../StructuredIr/OperandInfo.cs | 0 .../StructuredIr/PhiFunctions.cs | 0 .../StructuredIr/StructuredFunction.cs | 0 .../StructuredIr/StructuredProgram.cs | 0 .../StructuredIr/StructuredProgramContext.cs | 0 .../StructuredIr/StructuredProgramInfo.cs | 0 .../Ryujinx.Graphics.Shader}/SupportBuffer.cs | 0 .../Ryujinx.Graphics.Shader}/TessPatchType.cs | 0 .../Ryujinx.Graphics.Shader}/TessSpacing.cs | 0 .../TextureDescriptor.cs | 0 .../Ryujinx.Graphics.Shader}/TextureFormat.cs | 0 .../Ryujinx.Graphics.Shader}/TextureHandle.cs | 0 .../TextureUsageFlags.cs | 0 .../Translation/AggregateType.cs | 0 .../Translation/AttributeConsts.cs | 0 .../Translation/ControlFlowGraph.cs | 0 .../Translation/Dominance.cs | 0 .../Translation/EmitterContext.cs | 0 .../Translation/EmitterContextInsts.cs | 0 .../Translation/FeatureFlags.cs | 0 .../Translation/FunctionMatch.cs | 0 .../Translation/GlobalMemory.cs | 0 .../Optimizations/BindlessElimination.cs | 0 .../Optimizations/BindlessToIndexed.cs | 0 .../Optimizations/BranchElimination.cs | 0 .../Optimizations/ConstantFolding.cs | 0 .../Optimizations/GlobalToStorage.cs | 0 .../Translation/Optimizations/Optimizer.cs | 0 .../Optimizations/Simplification.cs | 0 .../Translation/Optimizations/Utils.cs | 0 .../Translation/RegisterUsage.cs | 0 .../Translation/Rewriter.cs | 0 .../Translation/ShaderConfig.cs | 0 .../Translation/ShaderHeader.cs | 0 .../Translation/ShaderIdentifier.cs | 0 .../Translation/Ssa.cs | 0 .../Translation/TargetApi.cs | 0 .../Translation/TargetLanguage.cs | 0 .../Translation/TranslationFlags.cs | 0 .../Translation/TranslationOptions.cs | 0 .../Translation/Translator.cs | 0 .../Translation/TranslatorContext.cs | 0 .../Astc/AstcDecoder.cs | 0 .../Astc/AstcDecoderException.cs | 0 .../Astc/AstcPixel.cs | 0 .../Astc/BitStream128.cs | 0 .../Ryujinx.Graphics.Texture}/Astc/Bits.cs | 0 .../Astc/EndPointSet.cs | 0 .../Astc/IntegerEncoded.cs | 0 .../Astc/IntegerSequence.cs | 0 .../Ryujinx.Graphics.Texture}/BC6Decoder.cs | 0 .../Ryujinx.Graphics.Texture}/BC7Decoder.cs | 0 .../Ryujinx.Graphics.Texture}/BCnDecoder.cs | 0 .../Ryujinx.Graphics.Texture}/BCnEncoder.cs | 0 .../BlockLinearConstants.cs | 0 .../BlockLinearLayout.cs | 0 .../Ryujinx.Graphics.Texture}/Bpp12Pixel.cs | 0 .../Ryujinx.Graphics.Texture}/ETC2Decoder.cs | 0 .../Encoders/BC7Encoder.cs | 0 .../Encoders/EncodeMode.cs | 0 .../LayoutConverter.cs | 0 .../OffsetCalculator.cs | 0 .../PixelConverter.cs | 0 .../Ryujinx.Graphics.Texture}/Region.cs | 0 .../Ryujinx.Graphics.Texture.csproj | 0 .../Ryujinx.Graphics.Texture}/Size.cs | 0 .../SizeCalculator.cs | 0 .../Ryujinx.Graphics.Texture}/SizeInfo.cs | 0 .../Utils/BC67Tables.cs | 0 .../Utils/BC67Utils.cs | 0 .../Utils/BC7ModeInfo.cs | 0 .../Ryujinx.Graphics.Texture}/Utils/Block.cs | 0 .../Utils/RgbaColor32.cs | 0 .../Utils/RgbaColor8.cs | 0 .../Ryujinx.Graphics.Vic}/Blender.cs | 0 .../Ryujinx.Graphics.Vic}/Image/BufferPool.cs | 0 .../Image/InputSurface.cs | 0 .../Ryujinx.Graphics.Vic}/Image/Pixel.cs | 0 .../Ryujinx.Graphics.Vic}/Image/Surface.cs | 0 .../Image/SurfaceCommon.cs | 0 .../Image/SurfaceReader.cs | 0 .../Image/SurfaceWriter.cs | 0 .../Ryujinx.Graphics.Vic}/Rectangle.cs | 0 .../Ryujinx.Graphics.Vic}/ResourceManager.cs | 0 .../Ryujinx.Graphics.Vic.csproj | 0 .../Ryujinx.Graphics.Vic}/Scaler.cs | 0 .../Types/BlendingSlotStruct.cs | 0 .../Types/ClearRectStruct.cs | 0 .../Types/ConfigStruct.cs | 0 .../Types/DeinterlaceMode.cs | 0 .../Types/FrameFormat.cs | 0 .../Types/LumaKeyStruct.cs | 0 .../Types/MatrixStruct.cs | 0 .../Types/OutputConfig.cs | 0 .../Types/OutputSurfaceConfig.cs | 0 .../Ryujinx.Graphics.Vic}/Types/PipeConfig.cs | 0 .../Types/PixelFormat.cs | 0 .../Ryujinx.Graphics.Vic}/Types/SlotConfig.cs | 0 .../Ryujinx.Graphics.Vic}/Types/SlotStruct.cs | 0 .../Types/SlotSurfaceConfig.cs | 0 .../Ryujinx.Graphics.Vic}/VicDevice.cs | 0 .../Ryujinx.Graphics.Vic}/VicRegisters.cs | 0 .../Ryujinx.Graphics.Video}/FrameField.cs | 0 .../H264PictureInfo.cs | 0 .../Ryujinx.Graphics.Video}/IDecoder.cs | 0 .../Ryujinx.Graphics.Video}/IH264Decoder.cs | 0 .../Ryujinx.Graphics.Video}/ISurface.cs | 0 .../Ryujinx.Graphics.Video}/IVp9Decoder.cs | 0 .../Ryujinx.Graphics.Video}/Plane.cs | 0 .../Ryujinx.Graphics.Video.csproj | 0 .../Ryujinx.Graphics.Video}/Vp8PictureInfo.cs | 0 .../Vp9BackwardUpdates.cs | 0 .../Vp9EntropyProbs.cs | 0 .../Ryujinx.Graphics.Video}/Vp9Mv.cs | 0 .../Ryujinx.Graphics.Video}/Vp9MvRef.cs | 0 .../Ryujinx.Graphics.Video}/Vp9PictureInfo.cs | 0 .../Ryujinx.Graphics.Vulkan}/Auto.cs | 0 .../AutoFlushCounter.cs | 0 .../BackgroundResources.cs | 0 .../Ryujinx.Graphics.Vulkan}/BitMap.cs | 0 .../BufferAllocationType.cs | 0 .../Ryujinx.Graphics.Vulkan}/BufferHolder.cs | 0 .../Ryujinx.Graphics.Vulkan}/BufferManager.cs | 0 .../Ryujinx.Graphics.Vulkan}/BufferState.cs | 0 .../BufferUsageBitmap.cs | 0 .../Ryujinx.Graphics.Vulkan}/CacheByRange.cs | 0 .../CommandBufferPool.cs | 0 .../CommandBufferScoped.cs | 0 .../Ryujinx.Graphics.Vulkan}/Constants.cs | 0 .../DescriptorSetCollection.cs | 0 .../DescriptorSetManager.cs | 0 .../DescriptorSetUpdater.cs | 0 .../DisposableBuffer.cs | 0 .../DisposableBufferView.cs | 0 .../DisposableFramebuffer.cs | 0 .../DisposableImage.cs | 0 .../DisposableImageView.cs | 0 .../DisposableMemory.cs | 0 .../DisposablePipeline.cs | 0 .../DisposableRenderPass.cs | 0 .../DisposableSampler.cs | 0 .../Effects/FsrScalingFilter.cs | 0 .../Effects/FxaaPostProcessingEffect.cs | 0 .../Effects/IPostProcessingEffect.cs | 0 .../Effects/IScalingFilter.cs | 0 .../Effects/Shaders/FsrScaling.glsl | 0 .../Effects/Shaders/FsrScaling.spv | Bin .../Effects/Shaders/FsrSharpening.glsl | 0 .../Effects/Shaders/FsrSharpening.spv | Bin .../Effects/Shaders/Fxaa.glsl | 0 .../Effects/Shaders/Fxaa.spv | Bin .../Effects/Shaders/SmaaBlend.glsl | 0 .../Effects/Shaders/SmaaBlend.spv | Bin .../Effects/Shaders/SmaaEdge.glsl | 0 .../Effects/Shaders/SmaaEdge.spv | Bin .../Effects/Shaders/SmaaNeighbour.glsl | 0 .../Effects/Shaders/SmaaNeighbour.spv | Bin .../Effects/SmaaConstants.cs | 0 .../Effects/SmaaPostProcessingEffect.cs | 0 .../Effects/Textures/SmaaAreaTexture.bin | Bin .../Effects/Textures/SmaaSearchTexture.bin | Bin .../EnumConversion.cs | 0 .../Ryujinx.Graphics.Vulkan}/FenceHelper.cs | 0 .../Ryujinx.Graphics.Vulkan}/FenceHolder.cs | 0 .../FormatCapabilities.cs | 0 .../FormatConverter.cs | 0 .../Ryujinx.Graphics.Vulkan}/FormatTable.cs | 0 .../FramebufferParams.cs | 0 .../HardwareCapabilities.cs | 0 .../Ryujinx.Graphics.Vulkan}/HashTableSlim.cs | 0 .../Ryujinx.Graphics.Vulkan}/HelperShader.cs | 0 .../Ryujinx.Graphics.Vulkan}/IdList.cs | 0 .../IndexBufferPattern.cs | 0 .../IndexBufferState.cs | 0 .../MemoryAllocation.cs | 0 .../MemoryAllocator.cs | 0 .../MemoryAllocatorBlockList.cs | 0 .../MoltenVK/MVKConfiguration.cs | 0 .../MoltenVK/MVKInitialization.cs | 0 .../MultiFenceHolder.cs | 0 .../Ryujinx.Graphics.Vulkan}/NativeArray.cs | 0 .../PersistentFlushBuffer.cs | 0 .../Ryujinx.Graphics.Vulkan}/PipelineBase.cs | 0 .../PipelineConverter.cs | 0 .../PipelineDynamicState.cs | 0 .../Ryujinx.Graphics.Vulkan}/PipelineFull.cs | 0 .../PipelineHelperShader.cs | 0 .../PipelineLayoutCache.cs | 0 .../PipelineLayoutCacheEntry.cs | 0 .../PipelineLayoutFactory.cs | 0 .../Ryujinx.Graphics.Vulkan}/PipelineState.cs | 0 .../Ryujinx.Graphics.Vulkan}/PipelineUid.cs | 0 .../Queries/BufferedQuery.cs | 0 .../Queries/CounterQueue.cs | 0 .../Queries/CounterQueueEvent.cs | 0 .../Queries/Counters.cs | 0 .../Ryujinx.Graphics.Vulkan.csproj | 0 .../Ryujinx.Graphics.Vulkan}/SamplerHolder.cs | 0 .../SemaphoreHolder.cs | 0 .../Ryujinx.Graphics.Vulkan}/Shader.cs | 0 .../ShaderCollection.cs | 0 .../ChangeBufferStrideShaderSource.comp | 0 ...lorBlitClearAlphaFragmentShaderSource.frag | 0 .../ColorBlitFragmentShaderSource.frag | 0 .../ColorBlitMsFragmentShaderSource.frag | 0 .../Shaders/ColorBlitVertexShaderSource.vert | 0 .../ColorClearFFragmentShaderSource.frag | 0 .../ColorClearSIFragmentShaderSource.frag | 0 .../ColorClearUIFragmentShaderSource.frag | 0 .../Shaders/ColorClearVertexShaderSource.vert | 0 ...olorCopyShorteningComputeShaderSource.comp | 0 .../ColorCopyToNonMsComputeShaderSource.comp | 0 .../ColorCopyWideningComputeShaderSource.comp | 0 .../ColorDrawToMsFragmentShaderSource.frag | 0 .../ColorDrawToMsVertexShaderSource.vert | 0 .../ConvertIndexBufferShaderSource.comp | 0 .../ConvertIndirectDataShaderSource.comp | 0 .../DepthBlitFragmentShaderSource.frag | 0 .../DepthBlitMsFragmentShaderSource.frag | 0 .../DepthDrawToMsFragmentShaderSource.frag | 0 .../DepthDrawToNonMsFragmentShaderSource.frag | 0 .../Shaders/ShaderBinaries.cs | 0 .../StencilBlitFragmentShaderSource.frag | 0 .../StencilBlitMsFragmentShaderSource.frag | 0 .../StencilDrawToMsFragmentShaderSource.frag | 0 ...tencilDrawToNonMsFragmentShaderSource.frag | 0 .../Ryujinx.Graphics.Vulkan}/SpecInfo.cs | 0 .../Ryujinx.Graphics.Vulkan}/StagingBuffer.cs | 0 .../Ryujinx.Graphics.Vulkan}/SyncManager.cs | 0 .../Ryujinx.Graphics.Vulkan}/TextureBuffer.cs | 0 .../Ryujinx.Graphics.Vulkan}/TextureCopy.cs | 0 .../TextureStorage.cs | 0 .../Ryujinx.Graphics.Vulkan}/TextureView.cs | 0 .../Ryujinx.Graphics.Vulkan}/Vendor.cs | 0 .../VertexBufferState.cs | 0 .../VulkanConfiguration.cs | 0 .../VulkanDebugMessenger.cs | 0 .../VulkanException.cs | 0 .../VulkanInitialization.cs | 0 .../VulkanInstance.cs | 0 .../VulkanPhysicalDevice.cs | 0 .../VulkanRenderer.cs | 0 .../Ryujinx.Graphics.Vulkan}/Window.cs | 0 .../Ryujinx.Graphics.Vulkan}/WindowBase.cs | 0 .../Ryujinx.HLE}/AssemblyInfo.cs | 0 .../GuestBrokeExecutionException.cs | 0 .../Exceptions/InternalServiceException.cs | 0 .../InvalidFirmwarePackageException.cs | 0 .../Exceptions/InvalidNpdmException.cs | 0 .../InvalidStructLayoutException.cs | 0 .../InvalidSystemResourceException.cs | 0 .../ServiceNotImplementedException.cs | 0 .../Exceptions/TamperCompilationException.cs | 0 .../Exceptions/TamperExecutionException.cs | 0 .../UndefinedInstructionException.cs | 0 .../Ryujinx.HLE}/FileSystem/ContentManager.cs | 0 .../Ryujinx.HLE}/FileSystem/ContentPath.cs | 0 .../FileSystem/EncryptedFileSystemCreator.cs | 0 .../Ryujinx.HLE}/FileSystem/LocationEntry.cs | 0 .../Ryujinx.HLE}/FileSystem/SystemVersion.cs | 0 .../FileSystem/VirtualFileSystem.cs | 0 .../Ryujinx.HLE}/HLEConfiguration.cs | 0 .../Ryujinx.HLE}/HOS/Applets/AppletManager.cs | 0 .../HOS/Applets/Browser/BootDisplayKind.cs | 0 .../HOS/Applets/Browser/BrowserApplet.cs | 0 .../HOS/Applets/Browser/BrowserArgument.cs | 0 .../HOS/Applets/Browser/BrowserOutput.cs | 0 .../HOS/Applets/Browser/BrowserOutputType.cs | 0 .../HOS/Applets/Browser/DocumentKind.cs | 0 .../HOS/Applets/Browser/LeftStickMode.cs | 0 .../HOS/Applets/Browser/ShimKind.cs | 0 .../HOS/Applets/Browser/WebArgHeader.cs | 0 .../HOS/Applets/Browser/WebArgTLV.cs | 0 .../HOS/Applets/Browser/WebArgTLVType.cs | 0 .../Applets/Browser/WebCommonReturnValue.cs | 0 .../HOS/Applets/Browser/WebExitReason.cs | 0 .../HOS/Applets/CommonArguments.cs | 0 .../Applets/Controller/ControllerApplet.cs | 0 .../Controller/ControllerAppletUiArgs.cs | 0 .../Controller/ControllerSupportArgHeader.cs | 0 .../Controller/ControllerSupportArgPrivate.cs | 0 .../Controller/ControllerSupportArgV7.cs | 0 .../Controller/ControllerSupportArgVPre7.cs | 0 .../Controller/ControllerSupportMode.cs | 0 .../Controller/ControllerSupportResultInfo.cs | 0 .../HOS/Applets/Error/ApplicationErrorArg.cs | 0 .../HOS/Applets/Error/ErrorApplet.cs | 0 .../HOS/Applets/Error/ErrorCommonArg.cs | 0 .../HOS/Applets/Error/ErrorCommonHeader.cs | 0 .../HOS/Applets/Error/ErrorType.cs | 0 .../Ryujinx.HLE}/HOS/Applets/IApplet.cs | 0 .../PlayerSelect/PlayerSelectApplet.cs | 0 .../PlayerSelect/PlayerSelectResult.cs | 0 .../SoftwareKeyboard/InitialCursorPosition.cs | 0 .../SoftwareKeyboard/InlineKeyboardRequest.cs | 0 .../InlineKeyboardResponse.cs | 0 .../SoftwareKeyboard/InlineKeyboardState.cs | 0 .../SoftwareKeyboard/InlineResponses.cs | 0 .../Applets/SoftwareKeyboard/InputFormMode.cs | 0 .../SoftwareKeyboard/InvalidButtonFlags.cs | 0 .../SoftwareKeyboard/InvalidCharFlags.cs | 0 .../SoftwareKeyboard/KeyboardCalcFlags.cs | 0 .../SoftwareKeyboard/KeyboardInputMode.cs | 0 .../KeyboardMiniaturizationMode.cs | 0 .../Applets/SoftwareKeyboard/KeyboardMode.cs | 0 .../SoftwareKeyboard/KeyboardResult.cs | 0 .../Applets/SoftwareKeyboard/PasswordMode.cs | 0 .../SoftwareKeyboard/Resources/Icon_BtnA.png | Bin .../SoftwareKeyboard/Resources/Icon_BtnA.svg | 0 .../SoftwareKeyboard/Resources/Icon_BtnB.png | Bin .../SoftwareKeyboard/Resources/Icon_BtnB.svg | 0 .../SoftwareKeyboard/Resources/Icon_KeyF6.png | Bin .../SoftwareKeyboard/Resources/Icon_KeyF6.svg | 0 .../Resources/Logo_Ryujinx.png | Bin .../SoftwareKeyboardAppear.cs | 0 .../SoftwareKeyboardAppearEx.cs | 0 .../SoftwareKeyboardApplet.cs | 0 .../SoftwareKeyboard/SoftwareKeyboardCalc.cs | 0 .../SoftwareKeyboardCalcEx.cs | 0 .../SoftwareKeyboardConfig.cs | 0 .../SoftwareKeyboardCustomizeDic.cs | 0 .../SoftwareKeyboardDictSet.cs | 0 .../SoftwareKeyboardInitialize.cs | 0 .../SoftwareKeyboardRenderer.cs | 0 .../SoftwareKeyboardRendererBase.cs | 0 .../SoftwareKeyboard/SoftwareKeyboardState.cs | 0 .../SoftwareKeyboardUiArgs.cs | 0 .../SoftwareKeyboardUiState.cs | 0 .../SoftwareKeyboardUserWord.cs | 0 .../HOS/Applets/SoftwareKeyboard/TRef.cs | 0 .../Applets/SoftwareKeyboard/TimedAction.cs | 0 .../Ryujinx.HLE}/HOS/ArmProcessContext.cs | 0 .../HOS/ArmProcessContextFactory.cs | 0 .../Ast/ArraySubscriptingExpression.cs | 0 .../Diagnostics/Demangler/Ast/ArrayType.cs | 0 .../HOS/Diagnostics/Demangler/Ast/BaseNode.cs | 0 .../Demangler/Ast/BinaryExpression.cs | 0 .../Demangler/Ast/BracedExpression.cs | 0 .../Demangler/Ast/BracedRangeExpression.cs | 0 .../Demangler/Ast/CallExpression.cs | 0 .../Demangler/Ast/CastExpression.cs | 0 .../Demangler/Ast/ConditionalExpression.cs | 0 .../Demangler/Ast/ConversionExpression.cs | 0 .../Demangler/Ast/ConversionOperatorType.cs | 0 .../Demangler/Ast/CtorDtorNameType.cs | 0 .../Demangler/Ast/CtorVtableSpecialName.cs | 0 .../Demangler/Ast/DeleteExpression.cs | 0 .../HOS/Diagnostics/Demangler/Ast/DtorName.cs | 0 .../Demangler/Ast/DynamicExceptionSpec.cs | 0 .../Demangler/Ast/ElaboratedType.cs | 0 .../Demangler/Ast/EnclosedExpression.cs | 0 .../Demangler/Ast/EncodedFunction.cs | 0 .../Demangler/Ast/FoldExpression.cs | 0 .../Demangler/Ast/ForwardTemplateReference.cs | 0 .../Demangler/Ast/FunctionParameter.cs | 0 .../Diagnostics/Demangler/Ast/FunctionType.cs | 0 .../Demangler/Ast/GlobalQualifiedName.cs | 0 .../Demangler/Ast/InitListExpression.cs | 0 .../Demangler/Ast/IntegerCastExpression.cs | 0 .../Demangler/Ast/IntegerLiteral.cs | 0 .../Demangler/Ast/LiteralOperator.cs | 0 .../Diagnostics/Demangler/Ast/LocalName.cs | 0 .../Demangler/Ast/MemberExpression.cs | 0 .../HOS/Diagnostics/Demangler/Ast/NameType.cs | 0 .../Ast/NameTypeWithTemplateArguments.cs | 0 .../Diagnostics/Demangler/Ast/NestedName.cs | 0 .../Demangler/Ast/NewExpression.cs | 0 .../Diagnostics/Demangler/Ast/NodeArray.cs | 0 .../Diagnostics/Demangler/Ast/NoexceptSpec.cs | 0 .../Demangler/Ast/PackedTemplateParameter.cs | 0 .../Ast/PackedTemplateParameterExpansion.cs | 0 .../Diagnostics/Demangler/Ast/ParentNode.cs | 0 .../Diagnostics/Demangler/Ast/PointerType.cs | 0 .../Demangler/Ast/PostfixExpression.cs | 0 .../Demangler/Ast/PostfixQualifiedType.cs | 0 .../Demangler/Ast/PrefixExpression.cs | 0 .../Demangler/Ast/QualifiedName.cs | 0 .../Diagnostics/Demangler/Ast/Qualifier.cs | 0 .../Demangler/Ast/ReferenceType.cs | 0 .../Diagnostics/Demangler/Ast/SpecialName.cs | 0 .../Demangler/Ast/SpecialSubstitution.cs | 0 .../Demangler/Ast/StdQualifiedName.cs | 0 .../Demangler/Ast/TemplateArguments.cs | 0 .../Demangler/Ast/ThrowExpression.cs | 0 .../HOS/Diagnostics/Demangler/Demangler.cs | 0 .../Ryujinx.HLE}/HOS/HomebrewRomFsStream.cs | 0 .../Ryujinx.HLE}/HOS/Horizon.cs | 0 .../Ryujinx.HLE}/HOS/IdDictionary.cs | 0 .../Ryujinx.HLE}/HOS/Ipc/IpcBuffDesc.cs | 0 .../Ryujinx.HLE}/HOS/Ipc/IpcHandleDesc.cs | 0 .../Ryujinx.HLE}/HOS/Ipc/IpcMagic.cs | 0 .../Ryujinx.HLE}/HOS/Ipc/IpcMessage.cs | 0 .../Ryujinx.HLE}/HOS/Ipc/IpcMessageType.cs | 0 .../Ryujinx.HLE}/HOS/Ipc/IpcPtrBuffDesc.cs | 0 .../HOS/Ipc/IpcRecvListBuffDesc.cs | 0 .../HOS/Ipc/ServiceProcessRequest.cs | 0 .../Kernel/Common/IKFutureSchedulerObject.cs | 0 .../HOS/Kernel/Common/KAutoObject.cs | 0 .../HOS/Kernel/Common/KResourceLimit.cs | 0 .../Kernel/Common/KSynchronizationObject.cs | 0 .../HOS/Kernel/Common/KSystemControl.cs | 0 .../HOS/Kernel/Common/KTimeManager.cs | 0 .../HOS/Kernel/Common/KernelInit.cs | 0 .../HOS/Kernel/Common/KernelTransfer.cs | 0 .../HOS/Kernel/Common/LimitableResource.cs | 0 .../HOS/Kernel/Common/MemoryArrange.cs | 0 .../HOS/Kernel/Common/MemroySize.cs | 0 .../HOS/Kernel/Common/MersenneTwister.cs | 0 .../HOS/Kernel/Ipc/ChannelState.cs | 0 .../HOS/Kernel/Ipc/KBufferDescriptor.cs | 0 .../HOS/Kernel/Ipc/KBufferDescriptorTable.cs | 0 .../HOS/Kernel/Ipc/KClientPort.cs | 0 .../HOS/Kernel/Ipc/KClientSession.cs | 0 .../HOS/Kernel/Ipc/KLightClientSession.cs | 0 .../HOS/Kernel/Ipc/KLightServerSession.cs | 0 .../HOS/Kernel/Ipc/KLightSession.cs | 0 .../Ryujinx.HLE}/HOS/Kernel/Ipc/KPort.cs | 0 .../HOS/Kernel/Ipc/KServerPort.cs | 0 .../HOS/Kernel/Ipc/KServerSession.cs | 0 .../Ryujinx.HLE}/HOS/Kernel/Ipc/KSession.cs | 0 .../HOS/Kernel/Ipc/KSessionRequest.cs | 0 .../HOS/Kernel/KernelConstants.cs | 0 .../Ryujinx.HLE}/HOS/Kernel/KernelContext.cs | 0 .../Ryujinx.HLE}/HOS/Kernel/KernelStatic.cs | 0 .../HOS/Kernel/Memory/AddressSpaceType.cs | 0 .../HOS/Kernel/Memory/DramMemoryMap.cs | 0 .../HOS/Kernel/Memory/KCodeMemory.cs | 0 .../HOS/Kernel/Memory/KMemoryBlock.cs | 0 .../HOS/Kernel/Memory/KMemoryBlockManager.cs | 0 .../Kernel/Memory/KMemoryBlockSlabManager.cs | 0 .../HOS/Kernel/Memory/KMemoryInfo.cs | 0 .../HOS/Kernel/Memory/KMemoryManager.cs | 0 .../HOS/Kernel/Memory/KMemoryRegionManager.cs | 0 .../HOS/Kernel/Memory/KPageBitmap.cs | 0 .../HOS/Kernel/Memory/KPageHeap.cs | 0 .../HOS/Kernel/Memory/KPageList.cs | 0 .../HOS/Kernel/Memory/KPageNode.cs | 0 .../HOS/Kernel/Memory/KPageTable.cs | 0 .../HOS/Kernel/Memory/KPageTableBase.cs | 0 .../HOS/Kernel/Memory/KScopedPageList.cs | 0 .../HOS/Kernel/Memory/KSharedMemory.cs | 0 .../HOS/Kernel/Memory/KSlabHeap.cs | 0 .../HOS/Kernel/Memory/KTransferMemory.cs | 0 .../HOS/Kernel/Memory/MemoryAttribute.cs | 0 .../HOS/Kernel/Memory/MemoryFillValue.cs | 0 .../HOS/Kernel/Memory/MemoryPermission.cs | 0 .../HOS/Kernel/Memory/MemoryRegion.cs | 0 .../HOS/Kernel/Memory/MemoryState.cs | 0 .../HOS/Kernel/Memory/SharedMemoryStorage.cs | 0 .../Kernel/Process/CapabilityExtensions.cs | 0 .../HOS/Kernel/Process/CapabilityType.cs | 0 .../HOS/Kernel/Process/HleProcessDebugger.cs | 0 .../HOS/Kernel/Process/IProcessContext.cs | 0 .../Kernel/Process/IProcessContextFactory.cs | 0 .../HOS/Kernel/Process/KContextIdManager.cs | 0 .../HOS/Kernel/Process/KHandleEntry.cs | 0 .../HOS/Kernel/Process/KHandleTable.cs | 0 .../HOS/Kernel/Process/KProcess.cs | 0 .../Kernel/Process/KProcessCapabilities.cs | 0 .../HOS/Kernel/Process/KTlsPageInfo.cs | 0 .../HOS/Kernel/Process/KTlsPageManager.cs | 0 .../HOS/Kernel/Process/ProcessContext.cs | 0 .../Kernel/Process/ProcessContextFactory.cs | 0 .../Kernel/Process/ProcessCreationFlags.cs | 0 .../HOS/Kernel/Process/ProcessCreationInfo.cs | 0 .../Kernel/Process/ProcessExecutionContext.cs | 0 .../HOS/Kernel/Process/ProcessState.cs | 0 .../HOS/Kernel/Process/ProcessTamperInfo.cs | 0 .../SupervisorCall/CodeMemoryOperation.cs | 0 .../HOS/Kernel/SupervisorCall/InfoType.cs | 0 .../HOS/Kernel/SupervisorCall/MemoryInfo.cs | 0 .../SupervisorCall/PointerSizedAttribute.cs | 0 .../HOS/Kernel/SupervisorCall/SvcAttribute.cs | 0 .../Kernel/SupervisorCall/SvcImplAttribute.cs | 0 .../HOS/Kernel/SupervisorCall/Syscall.cs | 0 .../Kernel/SupervisorCall/SyscallHandler.cs | 0 .../Kernel/SupervisorCall/ThreadContext.cs | 0 .../HOS/Kernel/Threading/ArbitrationType.cs | 0 .../HOS/Kernel/Threading/KAddressArbiter.cs | 0 .../Kernel/Threading/KConditionVariable.cs | 0 .../HOS/Kernel/Threading/KCriticalSection.cs | 0 .../HOS/Kernel/Threading/KEvent.cs | 0 .../HOS/Kernel/Threading/KPriorityQueue.cs | 0 .../HOS/Kernel/Threading/KReadableEvent.cs | 0 .../HOS/Kernel/Threading/KScheduler.cs | 0 .../HOS/Kernel/Threading/KSynchronization.cs | 0 .../HOS/Kernel/Threading/KThread.cs | 0 .../HOS/Kernel/Threading/KThreadContext.cs | 0 .../HOS/Kernel/Threading/KWritableEvent.cs | 0 .../HOS/Kernel/Threading/SignalType.cs | 0 .../HOS/Kernel/Threading/ThreadSchedState.cs | 0 .../HOS/Kernel/Threading/ThreadType.cs | 0 .../Ryujinx.HLE}/HOS/LibHacHorizonManager.cs | 0 .../Ryujinx.HLE}/HOS/ModLoader.cs | 0 .../Ryujinx.HLE}/HOS/ResultCode.cs | 0 .../Ryujinx.HLE}/HOS/ServiceCtx.cs | 0 .../Services/Account/Acc/AccountManager.cs | 0 .../Account/Acc/AccountSaveDataManager.cs | 0 .../AccountService/IManagerForApplication.cs | 0 .../IManagerForSystemService.cs | 0 .../Account/Acc/AccountService/IProfile.cs | 0 .../Acc/AccountService/IProfileEditor.cs | 0 .../Acc/AccountService/ManagerServer.cs | 0 .../Acc/AccountService/ProfileServer.cs | 0 .../Account/Acc/ApplicationServiceServer.cs | 0 .../Acc/AsyncContext/AsyncExecution.cs | 0 .../Services/Account/Acc/DefaultUserImage.jpg | Bin .../Acc/IAccountServiceForAdministrator.cs | 0 .../Acc/IAccountServiceForApplication.cs | 0 .../Acc/IAccountServiceForSystemService.cs | 0 .../HOS/Services/Account/Acc/IAsyncContext.cs | 0 .../IAsyncNetworkServiceLicenseKindContext.cs | 0 .../Account/Acc/IBaasAccessTokenAccessor.cs | 0 .../Acc/ProfilesJsonSerializerContext.cs | 0 .../Account/Acc/Types/AccountServiceFlag.cs | 0 .../Account/Acc/Types/AccountState.cs | 0 .../Acc/Types/NetworkServiceLicenseKind.cs | 0 .../Account/Acc/Types/ProfilesJson.cs | 0 .../HOS/Services/Account/Acc/Types/UserId.cs | 0 .../Services/Account/Acc/Types/UserProfile.cs | 0 .../Account/Acc/Types/UserProfileJson.cs | 0 .../HOS/Services/Account/Dauth/IService.cs | 0 .../HOS/Services/Account/ResultCode.cs | 0 .../ILibraryAppletProxy.cs | 0 .../ISystemAppletProxy.cs | 0 .../ILibraryAppletAccessor.cs | 0 .../LibraryAppletProxy/AppletStandalone.cs | 0 .../ILibraryAppletSelfAccessor.cs | 0 .../IProcessWindingController.cs | 0 .../IAppletCommonFunctions.cs | 0 .../SystemAppletProxy/IApplicationCreator.cs | 0 .../SystemAppletProxy/IAudioController.cs | 0 .../SystemAppletProxy/ICommonStateGetter.cs | 0 .../SystemAppletProxy/IDebugFunctions.cs | 0 .../SystemAppletProxy/IDisplayController.cs | 0 .../IGlobalStateController.cs | 0 .../SystemAppletProxy/IHomeMenuFunctions.cs | 0 .../ILibraryAppletCreator.cs | 0 .../SystemAppletProxy/ISelfController.cs | 0 .../SystemAppletProxy/IWindowController.cs | 0 .../Types/AlbumReportOption.cs | 0 .../SystemAppletProxy/Types/AppletMessage.cs | 0 .../SystemAppletProxy/Types/FocusState.cs | 0 .../SystemAppletProxy/Types/OperationMode.cs | 0 .../Types/WirelessPriorityMode.cs | 0 .../HOS/Services/Am/AppletAE/AppletFifo.cs | 0 .../HOS/Services/Am/AppletAE/AppletSession.cs | 0 .../IAllSystemAppletProxiesService.cs | 0 .../HOS/Services/Am/AppletAE/IAppletFifo.cs | 0 .../HOS/Services/Am/AppletAE/IStorage.cs | 0 .../Services/Am/AppletAE/IStorageAccessor.cs | 0 .../Am/AppletAE/Storage/StorageHelper.cs | 0 .../Services/Am/AppletAE/Types/AppletId.cs | 0 .../Am/AppletAE/Types/AppletIdentityInfo.cs | 0 .../Types/AppletProcessLaunchReason.cs | 0 .../Am/AppletAE/Types/LibraryAppletInfo.cs | 0 .../Am/AppletAE/Types/LibraryAppletMode.cs | 0 .../ApplicationProxy/IApplicationFunctions.cs | 0 .../Types/LaunchParameterKind.cs | 0 .../Types/ProgramSpecifyKind.cs | 0 .../IApplicationProxy.cs | 0 .../Am/AppletOE/IApplicationProxyService.cs | 0 .../Services/Am/Idle/IPolicyManagerSystem.cs | 0 .../Services/Am/Omm/IOperationModeManager.cs | 0 .../HOS/Services/Am/ResultCode.cs | 0 .../Services/Am/Spsm/IPowerStateInterface.cs | 0 .../HOS/Services/Am/Tcap/IManager.cs | 0 .../Ryujinx.HLE}/HOS/Services/Apm/IManager.cs | 0 .../HOS/Services/Apm/IManagerPrivileged.cs | 0 .../Ryujinx.HLE}/HOS/Services/Apm/ISession.cs | 0 .../HOS/Services/Apm/ISystemManager.cs | 0 .../HOS/Services/Apm/ManagerServer.cs | 0 .../HOS/Services/Apm/PerformanceState.cs | 0 .../HOS/Services/Apm/ResultCode.cs | 0 .../HOS/Services/Apm/SessionServer.cs | 0 .../HOS/Services/Apm/SystemManagerServer.cs | 0 .../HOS/Services/Apm/Types/CpuBoostMode.cs | 0 .../Apm/Types/PerformanceConfiguration.cs | 0 .../HOS/Services/Apm/Types/PerformanceMode.cs | 0 .../Services/Arp/ApplicationLaunchProperty.cs | 0 .../Ryujinx.HLE}/HOS/Services/Arp/IReader.cs | 0 .../Ryujinx.HLE}/HOS/Services/Arp/IWriter.cs | 0 .../HOS/Services/Arp/LibHacIReader.cs | 0 .../HOS/Services/Audio/AudioIn/AudioIn.cs | 0 .../Services/Audio/AudioIn/AudioInServer.cs | 0 .../HOS/Services/Audio/AudioIn/IAudioIn.cs | 0 .../HOS/Services/Audio/AudioInManager.cs | 0 .../Services/Audio/AudioInManagerServer.cs | 0 .../HOS/Services/Audio/AudioOut/AudioOut.cs | 0 .../Services/Audio/AudioOut/AudioOutServer.cs | 0 .../HOS/Services/Audio/AudioOut/IAudioOut.cs | 0 .../HOS/Services/Audio/AudioOutManager.cs | 0 .../Services/Audio/AudioOutManagerServer.cs | 0 .../Audio/AudioRenderer/AudioDevice.cs | 0 .../Audio/AudioRenderer/AudioDeviceServer.cs | 0 .../Audio/AudioRenderer/AudioKernelEvent.cs | 0 .../Audio/AudioRenderer/AudioRenderer.cs | 0 .../AudioRenderer/AudioRendererServer.cs | 0 .../Audio/AudioRenderer/IAudioDevice.cs | 0 .../Audio/AudioRenderer/IAudioRenderer.cs | 0 .../Services/Audio/AudioRendererManager.cs | 0 .../Audio/AudioRendererManagerServer.cs | 0 .../HardwareOpusDecoderManager/Decoder.cs | 0 .../DecoderCommon.cs | 0 .../HardwareOpusDecoderManager/IDecoder.cs | 0 .../IHardwareOpusDecoder.cs | 0 .../MultiSampleDecoder.cs | 0 .../HOS/Services/Audio/IAudioController.cs | 0 .../HOS/Services/Audio/IAudioInManager.cs | 0 .../Audio/IAudioInManagerForApplet.cs | 0 .../Audio/IAudioInManagerForDebugger.cs | 0 .../HOS/Services/Audio/IAudioOutManager.cs | 0 .../Audio/IAudioOutManagerForApplet.cs | 0 .../Audio/IAudioOutManagerForDebugger.cs | 0 .../Services/Audio/IAudioRendererManager.cs | 0 .../Audio/IAudioRendererManagerForApplet.cs | 0 .../Audio/IAudioRendererManagerForDebugger.cs | 0 .../HOS/Services/Audio/IAudioSnoopManager.cs | 0 .../Audio/IFinalOutputRecorderManager.cs | 0 .../IFinalOutputRecorderManagerForApplet.cs | 0 .../IFinalOutputRecorderManagerForDebugger.cs | 0 .../Audio/IHardwareOpusDecoderManager.cs | 0 .../HOS/Services/Audio/ResultCode.cs | 0 .../Services/Audio/Types/OpusDecoderFlags.cs | 0 .../Audio/Types/OpusMultiStreamParameters.cs | 0 .../Types/OpusMultiStreamParametersEx.cs | 0 .../Services/Audio/Types/OpusPacketHeader.cs | 0 .../Services/Audio/Types/OpusParametersEx.cs | 0 .../HOS/Services/Bcat/IServiceCreator.cs | 0 .../HOS/Services/Bcat/ResultCode.cs | 0 .../Bcat/ServiceCreator/IBcatService.cs | 0 .../IDeliveryCacheDirectoryService.cs | 0 .../IDeliveryCacheFileService.cs | 0 .../IDeliveryCacheProgressService.cs | 0 .../IDeliveryCacheStorageService.cs | 0 .../Types/DeliveryCacheProgressImpl.cs | 0 .../HOS/Services/Bgtc/IStateControlService.cs | 0 .../HOS/Services/Bgtc/ITaskService.cs | 0 .../BluetoothDriver/BluetoothEventManager.cs | 0 .../Services/Bluetooth/IBluetoothDriver.cs | 0 .../HOS/Services/Bluetooth/IBluetoothUser.cs | 0 .../BluetoothManager/BtmUser/IBtmUserCore.cs | 0 .../HOS/Services/BluetoothManager/IBtm.cs | 0 .../Services/BluetoothManager/IBtmDebug.cs | 0 .../Services/BluetoothManager/IBtmSystem.cs | 0 .../HOS/Services/BluetoothManager/IBtmUser.cs | 0 .../Services/BluetoothManager/ResultCode.cs | 0 .../HOS/Services/Caps/CaptureManager.cs | 0 .../Services/Caps/IAlbumAccessorService.cs | 0 .../Services/Caps/IAlbumApplicationService.cs | 0 .../HOS/Services/Caps/IAlbumControlService.cs | 0 .../Caps/IScreenShotApplicationService.cs | 0 .../Caps/IScreenShotControlService.cs | 0 .../HOS/Services/Caps/IScreenshotService.cs | 0 .../HOS/Services/Caps/ResultCode.cs | 0 .../Services/Caps/Types/AlbumFileDateTime.cs | 0 .../Caps/Types/AlbumImageOrientation.cs | 0 .../HOS/Services/Caps/Types/AlbumStorage.cs | 0 .../Caps/Types/ApplicationAlbumEntry.cs | 0 .../HOS/Services/Caps/Types/ContentType.cs | 0 .../Caps/Types/ScreenShotAttribute.cs | 0 .../HOS/Services/Cec/ICecManager.cs | 0 .../HOS/Services/CommandCmifAttribute.cs | 0 .../HOS/Services/CommandTIpcAttribute.cs | 0 .../HOS/Services/DisposableIpcService.cs | 0 .../Ryujinx.HLE}/HOS/Services/DummyService.cs | 0 .../HOS/Services/Ectx/IReaderForSystem.cs | 0 .../Services/Ectx/IWriterForApplication.cs | 0 .../HOS/Services/Ectx/IWriterForSystem.cs | 0 .../HOS/Services/Erpt/IContext.cs | 0 .../HOS/Services/Erpt/ISession.cs | 0 .../HOS/Services/Es/IETicketService.cs | 0 .../HOS/Services/Eupld/IControl.cs | 0 .../HOS/Services/Eupld/IRequest.cs | 0 .../HOS/Services/Fatal/IPrivateService.cs | 0 .../HOS/Services/Fatal/IService.cs | 0 .../HOS/Services/Fatal/Types/CpuContext32.cs | 0 .../HOS/Services/Fatal/Types/CpuContext64.cs | 0 .../HOS/Services/Fatal/Types/FatalPolicy.cs | 0 .../HOS/Services/Friend/IServiceCreator.cs | 0 .../HOS/Services/Friend/ResultCode.cs | 0 .../FriendService/Types/Friend.cs | 0 .../FriendService/Types/FriendFilter.cs | 0 .../FriendService/Types/PresenceStatus.cs | 0 .../Types/PresenceStatusFilter.cs | 0 .../FriendService/Types/UserPresence.cs | 0 .../IDaemonSuspendSessionService.cs | 0 .../Friend/ServiceCreator/IFriendService.cs | 0 .../ServiceCreator/INotificationService.cs | 0 .../NotificationEventHandler.cs | 0 .../Types/NotificationEventType.cs | 0 .../Types/NotificationInfo.cs | 0 .../Types/FriendServicePermissionLevel.cs | 0 .../Types/PlayHistoryRegistrationKey.cs | 0 .../FileSystemProxy/FileSystemProxyHelper.cs | 0 .../Services/Fs/FileSystemProxy/IDirectory.cs | 0 .../HOS/Services/Fs/FileSystemProxy/IFile.cs | 0 .../Fs/FileSystemProxy/IFileSystem.cs | 0 .../Services/Fs/FileSystemProxy/IStorage.cs | 0 .../HOS/Services/Fs/IDeviceOperator.cs | 0 .../HOS/Services/Fs/IFileSystemProxy.cs | 0 .../Services/Fs/IFileSystemProxyForLoader.cs | 0 .../HOS/Services/Fs/IMultiCommitManager.cs | 0 .../HOS/Services/Fs/IProgramRegistry.cs | 0 .../HOS/Services/Fs/ISaveDataInfoReader.cs | 0 .../HOS/Services/Fs/ResultCode.cs | 0 .../HOS/Services/Fs/Types/FileSystemType.cs | 0 .../HOS/Services/Grc/IGrcService.cs | 0 .../HOS/Services/Grc/IRemoteVideoTransfer.cs | 0 .../Ryujinx.HLE}/HOS/Services/Hid/Hid.cs | 0 .../HOS/Services/Hid/HidDevices/BaseDevice.cs | 0 .../Services/Hid/HidDevices/DebugPadDevice.cs | 0 .../Services/Hid/HidDevices/KeyboardDevice.cs | 0 .../Services/Hid/HidDevices/MouseDevice.cs | 0 .../Services/Hid/HidDevices/NpadDevices.cs | 0 .../Services/Hid/HidDevices/TouchDevice.cs | 0 .../Hid/HidDevices/Types/ControllerConfig.cs | 0 .../Hid/HidDevices/Types/GamepadInput.cs | 0 .../Hid/HidDevices/Types/JoystickPosition.cs | 0 .../Hid/HidDevices/Types/KeyboardInput.cs | 0 .../Hid/HidDevices/Types/SixAxisInput.cs | 0 .../Hid/HidDevices/Types/TouchPoint.cs | 0 .../HOS/Services/Hid/HidServer/HidUtils.cs | 0 .../HidServer/IActiveVibrationDeviceList.cs | 0 .../Services/Hid/HidServer/IAppletResource.cs | 0 .../Types/Npad/NpadHandheldActivationMode.cs | 0 .../HidServer/Types/Npad/NpadJoyDeviceType.cs | 0 .../Types/SixAxis/AccelerometerParameters.cs | 0 .../Types/SixAxis/GyroscopeZeroDriftMode.cs | 0 .../Types/SixAxis/SensorFusionParameters.cs | 0 .../Types/Vibration/VibrationDeviceHandle.cs | 0 .../Vibration/VibrationDevicePosition.cs | 0 .../Types/Vibration/VibrationDeviceType.cs | 0 .../Types/Vibration/VibrationDeviceValue.cs | 0 .../Types/Vibration/VibrationValue.cs | 0 .../HOS/Services/Hid/IHidDebugServer.cs | 0 .../HOS/Services/Hid/IHidServer.cs | 0 .../HOS/Services/Hid/IHidSystemServer.cs | 0 .../HOS/Services/Hid/IHidbusServer.cs | 0 .../HOS/Services/Hid/ISystemServer.cs | 0 .../HOS/Services/Hid/Irs/IIrSensorServer.cs | 0 .../Services/Hid/Irs/IIrSensorSystemServer.cs | 0 .../HOS/Services/Hid/Irs/ResultCode.cs | 0 .../Irs/Types/ImageTransferProcessorState.cs | 0 .../Services/Hid/Irs/Types/IrCameraHandle.cs | 0 .../Types/PackedClusteringProcessorConfig.cs | 0 .../PackedImageTransferProcessorConfig.cs | 0 .../Irs/Types/PackedMomentProcessorConfig.cs | 0 .../Types/PackedTeraPluginProcessorConfig.cs | 0 .../HOS/Services/Hid/ResultCode.cs | 0 .../Services/Hid/Types/AppletFooterUiType.cs | 0 .../HOS/Services/Hid/Types/HidVector.cs | 0 .../Services/Hid/Types/Npad/ControllerKeys.cs | 0 .../Services/Hid/Types/Npad/ControllerType.cs | 0 .../HOS/Services/Hid/Types/Npad/NpadColor.cs | 0 .../HOS/Services/Hid/Types/Npad/NpadIdType.cs | 0 .../Services/Hid/Types/Npad/NpadStyleIndex.cs | 0 .../Services/Hid/Types/Npad/PlayerIndex.cs | 0 .../HOS/Services/Hid/Types/NpadJoyHoldType.cs | 0 .../SharedMemory/Common/AnalogStickState.cs | 0 .../SharedMemory/Common/AtomicStorage.cs | 0 .../SharedMemory/Common/ISampledDataStruct.cs | 0 .../Hid/Types/SharedMemory/Common/RingLifo.cs | 0 .../DebugPad/DebugPadAttribute.cs | 0 .../SharedMemory/DebugPad/DebugPadButton.cs | 0 .../SharedMemory/DebugPad/DebugPadState.cs | 0 .../SharedMemory/Keyboard/KeyboardKey.cs | 0 .../SharedMemory/Keyboard/KeyboardKeyShift.cs | 0 .../SharedMemory/Keyboard/KeyboardModifier.cs | 0 .../SharedMemory/Keyboard/KeyboardState.cs | 0 .../SharedMemory/Mouse/MouseAttribute.cs | 0 .../Types/SharedMemory/Mouse/MouseButton.cs | 0 .../Types/SharedMemory/Mouse/MouseState.cs | 0 .../Hid/Types/SharedMemory/Npad/DeviceType.cs | 0 .../Types/SharedMemory/Npad/NpadAttribute.cs | 0 .../SharedMemory/Npad/NpadBatteryLevel.cs | 0 .../Hid/Types/SharedMemory/Npad/NpadButton.cs | 0 .../SharedMemory/Npad/NpadColorAttribute.cs | 0 .../SharedMemory/Npad/NpadCommonState.cs | 0 .../Npad/NpadFullKeyColorState.cs | 0 .../SharedMemory/Npad/NpadGcTriggerState.cs | 0 .../SharedMemory/Npad/NpadInternalState.cs | 0 .../Npad/NpadJoyAssignmentMode.cs | 0 .../SharedMemory/Npad/NpadJoyColorState.cs | 0 .../Types/SharedMemory/Npad/NpadLarkType.cs | 0 .../Types/SharedMemory/Npad/NpadLuciaType.cs | 0 .../Hid/Types/SharedMemory/Npad/NpadState.cs | 0 .../Types/SharedMemory/Npad/NpadStyleTag.cs | 0 .../Npad/NpadSystemButtonProperties.cs | 0 .../SharedMemory/Npad/NpadSystemProperties.cs | 0 .../Npad/SixAxisSensorAttribute.cs | 0 .../SharedMemory/Npad/SixAxisSensorState.cs | 0 .../Hid/Types/SharedMemory/SharedMemory.cs | 0 .../TouchScreen/TouchAttribute.cs | 0 .../TouchScreen/TouchScreenState.cs | 0 .../SharedMemory/TouchScreen/TouchState.cs | 0 .../HOS/Services/Ins/IReceiverManager.cs | 0 .../HOS/Services/Ins/ISenderManager.cs | 0 .../Ryujinx.HLE}/HOS/Services/IpcService.cs | 0 .../HOS/Services/Lbl/ILblController.cs | 0 .../HOS/Services/Lbl/LblControllerServer.cs | 0 .../Services/Ldn/IMonitorServiceCreator.cs | 0 .../HOS/Services/Ldn/ISystemServiceCreator.cs | 0 .../HOS/Services/Ldn/IUserServiceCreator.cs | 0 .../HOS/Services/Ldn/Lp2p/IServiceCreator.cs | 0 .../HOS/Services/Ldn/NetworkInterface.cs | 0 .../HOS/Services/Ldn/ResultCode.cs | 0 .../HOS/Services/Ldn/Types/NetworkState.cs | 0 .../IUserLocalCommunicationService.cs | 0 .../Services/Loader/IDebugMonitorInterface.cs | 0 .../Loader/IProcessManagerInterface.cs | 0 .../HOS/Services/Loader/IShellInterface.cs | 0 .../HOS/Services/Loader/ResultCode.cs | 0 .../Ryujinx.HLE}/HOS/Services/Mig/IService.cs | 0 .../HOS/Services/Mii/DatabaseImpl.cs | 0 .../Services/Mii/DatabaseSessionMetadata.cs | 0 .../Ryujinx.HLE}/HOS/Services/Mii/Helper.cs | 0 .../HOS/Services/Mii/IImageDatabaseService.cs | 0 .../HOS/Services/Mii/IStaticService.cs | 0 .../HOS/Services/Mii/MiiDatabaseManager.cs | 0 .../HOS/Services/Mii/ResultCode.cs | 0 .../Mii/StaticService/DatabaseServiceImpl.cs | 0 .../Mii/StaticService/IDatabaseService.cs | 0 .../HOS/Services/Mii/Types/Age.cs | 0 .../HOS/Services/Mii/Types/BeardType.cs | 0 .../HOS/Services/Mii/Types/CharInfo.cs | 0 .../HOS/Services/Mii/Types/CharInfoElement.cs | 0 .../HOS/Services/Mii/Types/CommonColor.cs | 0 .../HOS/Services/Mii/Types/CoreData.cs | 0 .../HOS/Services/Mii/Types/CreateId.cs | 0 .../HOS/Services/Mii/Types/DefaultMii.cs | 0 .../HOS/Services/Mii/Types/EyeType.cs | 0 .../HOS/Services/Mii/Types/EyebrowType.cs | 0 .../HOS/Services/Mii/Types/FacelineColor.cs | 0 .../HOS/Services/Mii/Types/FacelineMake.cs | 0 .../HOS/Services/Mii/Types/FacelineType.cs | 0 .../HOS/Services/Mii/Types/FacelineWrinkle.cs | 0 .../HOS/Services/Mii/Types/FontRegion.cs | 0 .../HOS/Services/Mii/Types/Gender.cs | 0 .../HOS/Services/Mii/Types/GlassType.cs | 0 .../HOS/Services/Mii/Types/HairFlip.cs | 0 .../HOS/Services/Mii/Types/HairType.cs | 0 .../HOS/Services/Mii/Types/IElement.cs | 0 .../HOS/Services/Mii/Types/IStoredData.cs | 0 .../HOS/Services/Mii/Types/MoleType.cs | 0 .../HOS/Services/Mii/Types/MouthType.cs | 0 .../HOS/Services/Mii/Types/MustacheType.cs | 0 .../HOS/Services/Mii/Types/Nickname.cs | 0 .../Mii/Types/NintendoFigurineDatabase.cs | 0 .../HOS/Services/Mii/Types/NoseType.cs | 0 .../HOS/Services/Mii/Types/Race.cs | 0 .../Services/Mii/Types/RandomMiiConstants.cs | 0 .../HOS/Services/Mii/Types/Source.cs | 0 .../HOS/Services/Mii/Types/SourceFlag.cs | 0 .../Services/Mii/Types/SpecialMiiKeyCode.cs | 0 .../HOS/Services/Mii/Types/StoreData.cs | 0 .../Services/Mii/Types/StoreDataElement.cs | 0 .../HOS/Services/Mii/Types/Ver3StoreData.cs | 0 .../HOS/Services/Mii/UtilityImpl.cs | 0 .../Ryujinx.HLE}/HOS/Services/Mm/IRequest.cs | 0 .../Mm/Types/MultiMediaOperationType.cs | 0 .../Services/Mm/Types/MultiMediaSession.cs | 0 .../Services/Mnpp/IServiceForApplication.cs | 0 .../HOS/Services/Mnpp/ResultCode.cs | 0 .../HOS/Services/Ncm/IContentManager.cs | 0 .../Ncm/Lr/ILocationResolverManager.cs | 0 .../ILocationResolver.cs | 0 .../HOS/Services/Ncm/Lr/ResultCode.cs | 0 .../HOS/Services/News/IServiceCreator.cs | 0 .../HOS/Services/Nfc/IAmManager.cs | 0 .../HOS/Services/Nfc/ISystemManager.cs | 0 .../HOS/Services/Nfc/IUserManager.cs | 0 .../HOS/Services/Nfc/Mifare/IUserManager.cs | 0 .../HOS/Services/Nfc/NfcManager/INfc.cs | 0 .../NfcManager/Types/NfcPermissionLevel.cs | 0 .../Services/Nfc/NfcManager/Types/State.cs | 0 .../Nfc/Nfp/AmiiboJsonSerializerContext.cs | 0 .../HOS/Services/Nfc/Nfp/IDebugManager.cs | 0 .../HOS/Services/Nfc/Nfp/ISystemManager.cs | 0 .../HOS/Services/Nfc/Nfp/IUserManager.cs | 0 .../HOS/Services/Nfc/Nfp/NfpManager/INfp.cs | 0 .../Nfp/NfpManager/Types/AmiiboConstants.cs | 0 .../Nfc/Nfp/NfpManager/Types/CommonInfo.cs | 0 .../Nfc/Nfp/NfpManager/Types/DeviceType.cs | 0 .../Nfc/Nfp/NfpManager/Types/ModelInfo.cs | 0 .../Nfc/Nfp/NfpManager/Types/MountTarget.cs | 0 .../Nfc/Nfp/NfpManager/Types/NfpDevice.cs | 0 .../Nfp/NfpManager/Types/NfpDeviceState.cs | 0 .../NfpManager/Types/NfpPermissionLevel.cs | 0 .../Nfc/Nfp/NfpManager/Types/RegisterInfo.cs | 0 .../Nfc/Nfp/NfpManager/Types/State.cs | 0 .../Nfc/Nfp/NfpManager/Types/TagInfo.cs | 0 .../Nfp/NfpManager/Types/VirtualAmiiboFile.cs | 0 .../HOS/Services/Nfc/Nfp/ResultCode.cs | 0 .../HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 0 .../HOS/Services/Ngct/IService.cs | 0 .../Ngct/IServiceWithManagementApi.cs | 0 .../HOS/Services/Ngct/NgctServer.cs | 0 .../HOS/Services/Nifm/IStaticService.cs | 0 .../HOS/Services/Nifm/ResultCode.cs | 0 .../GeneralService/GeneralServiceManager.cs | 0 .../Types/GeneralServiceDetail.cs | 0 .../Nifm/StaticService/IGeneralService.cs | 0 .../Services/Nifm/StaticService/IRequest.cs | 0 .../Nifm/StaticService/Types/DnsSetting.cs | 0 .../Types/InternetConnectionState.cs | 0 .../Types/InternetConnectionStatus.cs | 0 .../Types/InternetConnectionType.cs | 0 .../StaticService/Types/IpAddressSetting.cs | 0 .../Nifm/StaticService/Types/IpSettingData.cs | 0 .../Nifm/StaticService/Types/IpV4Address.cs | 0 .../StaticService/Types/NetworkProfileData.cs | 0 .../Nifm/StaticService/Types/ProxySetting.cs | 0 .../Types/WirelessSettingData.cs | 0 .../Services/Nim/INetworkInstallManager.cs | 0 .../Services/Nim/IShopServiceAccessServer.cs | 0 .../Nim/IShopServiceAccessServerInterface.cs | 0 .../Nim/IShopServiceAccessSystemInterface.cs | 0 .../HOS/Services/Nim/IShopServiceAccessor.cs | 0 .../HOS/Services/Nim/IShopServiceAsync.cs | 0 .../HOS/Services/Nim/IShopServiceManager.cs | 0 .../HOS/Services/Nim/Ntc/IStaticService.cs | 0 .../IEnsureNetworkClockAvailabilityService.cs | 0 .../HOS/Services/Nim/ResultCode.cs | 0 .../INotificationServicesForApplication.cs | 0 .../INotificationServicesForSystem.cs | 0 .../HOS/Services/Npns/INpnsSystem.cs | 0 .../HOS/Services/Npns/INpnsUser.cs | 0 .../Services/Ns/Aoc/IAddOnContentManager.cs | 0 .../Ns/Aoc/IContentsServiceManager.cs | 0 .../Services/Ns/Aoc/IPurchaseEventManager.cs | 0 .../HOS/Services/Ns/Aoc/ResultCode.cs | 0 .../Ns/IApplicationManagerInterface.cs | 0 .../HOS/Services/Ns/IDevelopInterface.cs | 0 ...ReadOnlyApplicationControlDataInterface.cs | 0 .../Services/Ns/IServiceGetterInterface.cs | 0 .../HOS/Services/Ns/ISystemUpdateInterface.cs | 0 .../Ns/IVulnerabilityManagerInterface.cs | 0 .../HOS/Services/Nv/Host1xContext.cs | 0 .../HOS/Services/Nv/INvDrvDebugFSServices.cs | 0 .../HOS/Services/Nv/INvDrvServices.cs | 0 .../HOS/Services/Nv/INvGemControl.cs | 0 .../HOS/Services/Nv/INvGemCoreDump.cs | 0 .../Services/Nv/NvDrvServices/NvDeviceFile.cs | 0 .../NvHostAsGpu/NvHostAsGpuDeviceFile.cs | 0 .../NvHostAsGpu/Types/AddressSpaceContext.cs | 0 .../NvHostAsGpu/Types/AddressSpaceFlags.cs | 0 .../NvHostAsGpu/Types/AllocSpaceArguments.cs | 0 .../NvHostAsGpu/Types/BindChannelArguments.cs | 0 .../NvHostAsGpu/Types/FreeSpaceArguments.cs | 0 .../Types/GetVaRegionsArguments.cs | 0 .../Types/InitializeExArguments.cs | 0 .../NvHostAsGpu/Types/MapBufferExArguments.cs | 0 .../NvHostAsGpu/Types/RemapArguments.cs | 0 .../NvHostAsGpu/Types/UnmapBufferArguments.cs | 0 .../NvHostChannel/ChannelInitialization.cs | 0 .../NvHostChannel/NvHostChannelDeviceFile.cs | 0 .../NvHostChannel/NvHostGpuDeviceFile.cs | 0 .../Types/AllocGpfifoExArguments.cs | 0 .../Types/AllocObjCtxArguments.cs | 0 .../Types/GetParameterArguments.cs | 0 .../Types/MapCommandBufferArguments.cs | 0 .../NvHostChannel/Types/NvChannel.cs | 0 .../NvHostChannel/Types/NvChannelPriority.cs | 0 .../Types/SetErrorNotifierArguments.cs | 0 .../NvHostChannel/Types/SubmitArguments.cs | 0 .../Types/SubmitGpfifoArguments.cs | 0 .../NvHostChannel/Types/SubmitGpfifoFlags.cs | 0 .../NvHostChannel/Types/ZcullBindArguments.cs | 0 .../NvHostCtrl/NvHostCtrlDeviceFile.cs | 0 .../NvHostCtrl/Types/EventWaitArguments.cs | 0 .../Types/GetConfigurationArguments.cs | 0 .../NvHostCtrl/Types/NvHostEvent.cs | 0 .../NvHostCtrl/Types/NvHostEventState.cs | 0 .../NvHostCtrl/Types/NvHostSyncPt.cs | 0 .../NvHostCtrl/Types/SyncptWaitArguments.cs | 0 .../NvHostCtrl/Types/SyncptWaitExArguments.cs | 0 .../NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs | 0 .../Types/GetActiveSlotMaskArguments.cs | 0 .../Types/GetCharacteristicsArguments.cs | 0 .../Types/GetGpuTimeArguments.cs | 0 .../Types/GetTpcMasksArguments.cs | 0 .../Types/ZbcSetTableArguments.cs | 0 .../Types/ZcullGetCtxSizeArguments.cs | 0 .../Types/ZcullGetInfoArguments.cs | 0 .../NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs | 0 .../NvHostProfGpu/NvHostProfGpuDeviceFile.cs | 0 .../Nv/NvDrvServices/NvInternalResult.cs | 0 .../Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs | 0 .../NvDrvServices/NvMap/Types/NvMapAlloc.cs | 0 .../NvDrvServices/NvMap/Types/NvMapCreate.cs | 0 .../Nv/NvDrvServices/NvMap/Types/NvMapFree.cs | 0 .../NvDrvServices/NvMap/Types/NvMapFromId.cs | 0 .../NvDrvServices/NvMap/Types/NvMapGetId.cs | 0 .../NvDrvServices/NvMap/Types/NvMapHandle.cs | 0 .../NvMap/Types/NvMapHandleParam.cs | 0 .../NvMap/Types/NvMapIdDictionary.cs | 0 .../NvDrvServices/NvMap/Types/NvMapParam.cs | 0 .../Ryujinx.HLE}/HOS/Services/Nv/NvIoctl.cs | 0 .../HOS/Services/Nv/NvMemoryAllocator.cs | 0 .../HOS/Services/Nv/Types/NvFence.cs | 0 .../Types/NvIoctlNotImplementedException.cs | 0 .../NvQueryEventNotImplementedException.cs | 0 .../HOS/Services/Nv/Types/NvResult.cs | 0 .../HOS/Services/Nv/Types/NvStatus.cs | 0 .../Olsc/IOlscServiceForApplication.cs | 0 .../Olsc/IOlscServiceForSystemService.cs | 0 .../HOS/Services/Olsc/ResultCode.cs | 0 .../HOS/Services/Ovln/IReceiverService.cs | 0 .../HOS/Services/Ovln/ISenderService.cs | 0 .../HOS/Services/Pcie/ILogManager.cs | 0 .../HOS/Services/Pcie/IManager.cs | 0 .../Pctl/IParentalControlServiceFactory.cs | 0 .../IParentalControlService.cs | 0 .../HOS/Services/Pctl/ResultCode.cs | 0 .../Pcv/Bpc/IBoardPowerControlManager.cs | 0 .../HOS/Services/Pcv/Bpc/IRtcManager.cs | 0 .../Clkrst/ClkrstManager/IClkrstSession.cs | 0 .../Pcv/Clkrst/IArbitrationManager.cs | 0 .../HOS/Services/Pcv/Clkrst/IClkrstManager.cs | 0 .../HOS/Services/Pcv/IPcvService.cs | 0 .../HOS/Services/Pcv/ResultCode.cs | 0 .../Services/Pcv/Rgltr/IRegulatorManager.cs | 0 .../HOS/Services/Pcv/Rtc/IRtcManager.cs | 0 .../HOS/Services/Pcv/Types/DeviceCode.cs | 0 .../HOS/Services/Pm/IBootModeInterface.cs | 0 .../HOS/Services/Pm/IDebugMonitorInterface.cs | 0 .../HOS/Services/Pm/IInformationInterface.cs | 0 .../HOS/Services/Pm/IShellInterface.cs | 0 .../HOS/Services/Pm/ResultCode.cs | 0 .../HOS/Services/Psc/IPmControl.cs | 0 .../HOS/Services/Psc/IPmService.cs | 0 .../HOS/Services/Psc/IPmUnknown.cs | 0 .../HOS/Services/Ptm/Fan/IManager.cs | 0 .../HOS/Services/Ptm/Fgm/IDebugger.cs | 0 .../HOS/Services/Ptm/Fgm/ISession.cs | 0 .../HOS/Services/Ptm/Pcm/IManager.cs | 0 .../HOS/Services/Ptm/Psm/IPsmServer.cs | 0 .../HOS/Services/Ptm/Psm/IPsmSession.cs | 0 .../HOS/Services/Ptm/Psm/Types/ChargerType.cs | 0 .../HOS/Services/Ptm/Tc/IManager.cs | 0 .../HOS/Services/Ptm/Ts/IMeasurementServer.cs | 0 .../HOS/Services/Ptm/Ts/Types/Location.cs | 0 .../HOS/Services/Ro/IRoInterface.cs | 0 .../HOS/Services/Ro/ResultCode.cs | 0 .../HOS/Services/Ro/Types/NRRCertification.cs | 0 .../HOS/Services/Ro/Types/NroInfo.cs | 0 .../HOS/Services/Ro/Types/NrrHeader.cs | 0 .../HOS/Services/Ro/Types/NrrInfo.cs | 0 .../HOS/Services/Sdb/Avm/IAvmService.cs | 0 .../HOS/Services/Sdb/Pdm/INotifyService.cs | 0 .../HOS/Services/Sdb/Pdm/IQueryService.cs | 0 .../QueryPlayStatisticsManager.cs | 0 .../Types/ApplicationPlayStatistics.cs | 0 .../Types/PlayLogQueryCapability.cs | 0 .../HOS/Services/Sdb/Pdm/ResultCode.cs | 0 .../HOS/Services/Sdb/Pl/ISharedFontManager.cs | 0 .../HOS/Services/Sdb/Pl/SharedFontManager.cs | 0 .../Services/Sdb/Pl/Types/SharedFontType.cs | 0 .../Ryujinx.HLE}/HOS/Services/ServerBase.cs | 0 .../HOS/Services/ServiceAttributes.cs | 0 .../Settings/IFactorySettingsServer.cs | 0 .../Settings/IFirmwareDebugSettingsServer.cs | 0 .../HOS/Services/Settings/ISettingsServer.cs | 0 .../Settings/ISystemSettingsServer.cs | 0 .../HOS/Services/Settings/KeyCodeMaps.cs | 0 .../HOS/Services/Settings/NxSettings.cs | 0 .../HOS/Services/Settings/ResultCode.cs | 0 .../Services/Settings/Types/PlatformRegion.cs | 0 .../HOS/Services/Sm/IManagerInterface.cs | 0 .../HOS/Services/Sm/IUserInterface.cs | 0 .../HOS/Services/Sm/ResultCode.cs | 0 .../HOS/Services/Sm/SmRegistry.cs | 0 .../HOS/Services/Sockets/Bsd/BsdContext.cs | 0 .../HOS/Services/Sockets/Bsd/IClient.cs | 0 .../Services/Sockets/Bsd/IFileDescriptor.cs | 0 .../HOS/Services/Sockets/Bsd/ISocket.cs | 0 .../Sockets/Bsd/Impl/EventFileDescriptor.cs | 0 .../Impl/EventFileDescriptorPollManager.cs | 0 .../Sockets/Bsd/Impl/ManagedSocket.cs | 0 .../Bsd/Impl/ManagedSocketPollManager.cs | 0 .../HOS/Services/Sockets/Bsd/Impl/WSAError.cs | 0 .../Sockets/Bsd/Impl/WinSockHelper.cs | 0 .../Services/Sockets/Bsd/ServerInterface.cs | 0 .../Sockets/Bsd/Types/BsdAddressFamily.cs | 0 .../Services/Sockets/Bsd/Types/BsdIoctl.cs | 0 .../Services/Sockets/Bsd/Types/BsdMMsgHdr.cs | 0 .../Services/Sockets/Bsd/Types/BsdMsgHdr.cs | 0 .../Services/Sockets/Bsd/Types/BsdSockAddr.cs | 0 .../Bsd/Types/BsdSocketCreationFlags.cs | 0 .../Sockets/Bsd/Types/BsdSocketFlags.cs | 0 .../Sockets/Bsd/Types/BsdSocketOption.cs | 0 .../Bsd/Types/BsdSocketShutdownFlags.cs | 0 .../Sockets/Bsd/Types/BsdSocketType.cs | 0 .../Sockets/Bsd/Types/EventFdFlags.cs | 0 .../Sockets/Bsd/Types/IPollManager.cs | 0 .../Services/Sockets/Bsd/Types/LinuxError.cs | 0 .../Services/Sockets/Bsd/Types/PollEvent.cs | 0 .../Sockets/Bsd/Types/PollEventData.cs | 0 .../Sockets/Bsd/Types/PollEventTypeMask.cs | 0 .../HOS/Services/Sockets/Bsd/Types/TimeVal.cs | 0 .../Services/Sockets/Ethc/IEthInterface.cs | 0 .../Sockets/Ethc/IEthInterfaceGroup.cs | 0 .../HOS/Services/Sockets/Nsd/IManager.cs | 0 .../Sockets/Nsd/Manager/FqdnResolver.cs | 0 .../HOS/Services/Sockets/Nsd/ResultCode.cs | 0 .../Types/ApplicationServerEnvironmentType.cs | 0 .../Services/Sockets/Nsd/Types/NsdSettings.cs | 0 .../Services/Sockets/Sfdnsres/IResolver.cs | 0 .../Sockets/Sfdnsres/Proxy/DnsBlacklist.cs | 0 .../Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs | 0 .../Sockets/Sfdnsres/Types/AddrInfo4.cs | 0 .../Sfdnsres/Types/AddrInfoSerialized.cs | 0 .../Types/AddrInfoSerializedHeader.cs | 0 .../Sockets/Sfdnsres/Types/GaiError.cs | 0 .../Sockets/Sfdnsres/Types/NetDBError.cs | 0 .../Sfdnsres/Types/SfdnsresContants.cs | 0 .../HOS/Services/Spl/IGeneralInterface.cs | 0 .../HOS/Services/Spl/IRandomInterface.cs | 0 .../HOS/Services/Spl/ResultCode.cs | 0 .../HOS/Services/Spl/Types/ConfigItem.cs | 0 .../HOS/Services/Spl/Types/DramId.cs | 0 .../HOS/Services/Spl/Types/HardwareState.cs | 0 .../HOS/Services/Spl/Types/HardwareType.cs | 0 .../HOS/Services/Spl/Types/SmcResult.cs | 0 .../HOS/Services/Srepo/ISrepoService.cs | 0 .../Services/Ssl/BuiltInCertificateManager.cs | 0 .../HOS/Services/Ssl/ISslService.cs | 0 .../HOS/Services/Ssl/ResultCode.cs | 0 .../Services/Ssl/SslService/ISslConnection.cs | 0 .../Ssl/SslService/ISslConnectionBase.cs | 0 .../Services/Ssl/SslService/ISslContext.cs | 0 .../SslService/SslManagedSocketConnection.cs | 0 .../Ssl/Types/BuiltInCertificateInfo.cs | 0 .../HOS/Services/Ssl/Types/CaCertificateId.cs | 0 .../Services/Ssl/Types/CertificateFormat.cs | 0 .../HOS/Services/Ssl/Types/IoMode.cs | 0 .../HOS/Services/Ssl/Types/OptionType.cs | 0 .../Services/Ssl/Types/SessionCacheMode.cs | 0 .../HOS/Services/Ssl/Types/SslVersion.cs | 0 .../Services/Ssl/Types/TrustedCertStatus.cs | 0 .../HOS/Services/Ssl/Types/VerifyOption.cs | 0 .../SurfaceFlinger/BufferItemConsumer.cs | 0 .../Services/SurfaceFlinger/BufferQueue.cs | 0 .../SurfaceFlinger/BufferQueueConsumer.cs | 0 .../SurfaceFlinger/BufferQueueCore.cs | 0 .../SurfaceFlinger/BufferQueueProducer.cs | 0 .../HOS/Services/SurfaceFlinger/BufferSlot.cs | 0 .../SurfaceFlinger/BufferSlotArray.cs | 0 .../Services/SurfaceFlinger/ConsumerBase.cs | 0 .../SurfaceFlinger/HOSBinderDriverServer.cs | 0 .../HOS/Services/SurfaceFlinger/IBinder.cs | 0 .../SurfaceFlinger/IConsumerListener.cs | 0 .../Services/SurfaceFlinger/IFlattenable.cs | 0 .../SurfaceFlinger/IGraphicBufferProducer.cs | 0 .../SurfaceFlinger/IHOSBinderDriver.cs | 0 .../SurfaceFlinger/IProducerListener.cs | 0 .../HOS/Services/SurfaceFlinger/LayerState.cs | 0 .../SurfaceFlinger/NativeWindowApi.cs | 0 .../SurfaceFlinger/NativeWindowAttribute.cs | 0 .../SurfaceFlinger/NativeWindowScalingMode.cs | 0 .../SurfaceFlinger/NativeWindowTransform.cs | 0 .../HOS/Services/SurfaceFlinger/Parcel.cs | 0 .../Services/SurfaceFlinger/ParcelHeader.cs | 0 .../Services/SurfaceFlinger/PixelFormat.cs | 0 .../HOS/Services/SurfaceFlinger/Status.cs | 0 .../Services/SurfaceFlinger/SurfaceFlinger.cs | 0 .../SurfaceFlinger/Types/AndroidFence.cs | 0 .../Types/AndroidStrongPointer.cs | 0 .../SurfaceFlinger/Types/BufferInfo.cs | 0 .../SurfaceFlinger/Types/BufferItem.cs | 0 .../SurfaceFlinger/Types/BufferState.cs | 0 .../Types/Color/ColorBytePerPixel.cs | 0 .../Types/Color/ColorComponent.cs | 0 .../Types/Color/ColorDataType.cs | 0 .../SurfaceFlinger/Types/Color/ColorFormat.cs | 0 .../SurfaceFlinger/Types/Color/ColorShift.cs | 0 .../SurfaceFlinger/Types/Color/ColorSpace.cs | 0 .../Types/Color/ColorSwizzle.cs | 0 .../SurfaceFlinger/Types/GraphicBuffer.cs | 0 .../Types/GraphicBufferHeader.cs | 0 .../SurfaceFlinger/Types/NvGraphicBuffer.cs | 0 .../Types/NvGraphicBufferSurface.cs | 0 .../Types/NvGraphicBufferSurfaceArray.cs | 0 .../HOS/Services/SurfaceFlinger/Types/Rect.cs | 0 ...phemeralNetworkSystemClockContextWriter.cs | 0 .../Clock/EphemeralNetworkSystemClockCore.cs | 0 .../Clock/LocalSystemClockContextWriter.cs | 0 .../Clock/NetworkSystemClockContextWriter.cs | 0 .../Clock/StandardLocalSystemClockCore.cs | 0 .../Clock/StandardNetworkSystemClockCore.cs | 0 .../Time/Clock/StandardSteadyClockCore.cs | 0 .../Time/Clock/StandardUserSystemClockCore.cs | 0 .../Services/Time/Clock/SteadyClockCore.cs | 0 .../Clock/SystemClockContextUpdateCallback.cs | 0 .../Services/Time/Clock/SystemClockCore.cs | 0 .../Time/Clock/TickBasedSteadyClockCore.cs | 0 .../Time/Clock/Types/ClockSnapshot.cs | 0 .../Time/Clock/Types/SteadyClockTimePoint.cs | 0 .../Time/Clock/Types/SystemClockContext.cs | 0 .../Services/Time/Clock/Types/TimeSpanType.cs | 0 .../HOS/Services/Time/IAlarmService.cs | 0 .../Time/IPowerStateRequestHandler.cs | 0 .../Services/Time/IStaticServiceForGlue.cs | 0 .../HOS/Services/Time/IStaticServiceForPsc.cs | 0 .../HOS/Services/Time/ITimeServiceManager.cs | 0 .../HOS/Services/Time/ResultCode.cs | 0 .../Time/StaticService/ISteadyClock.cs | 0 .../Time/StaticService/ISystemClock.cs | 0 .../StaticService/ITimeZoneServiceForGlue.cs | 0 .../StaticService/ITimeZoneServiceForPsc.cs | 0 .../HOS/Services/Time/TimeManager.cs | 0 .../HOS/Services/Time/TimeSharedMemory.cs | 0 .../HOS/Services/Time/TimeZone/TimeZone.cs | 0 .../Time/TimeZone/TimeZoneContentManager.cs | 0 .../Services/Time/TimeZone/TimeZoneManager.cs | 0 .../TimeZone/Types/CalendarAdditionalInfo.cs | 0 .../Time/TimeZone/Types/CalendarInfo.cs | 0 .../Time/TimeZone/Types/CalendarTime.cs | 0 .../Time/TimeZone/Types/TimeTypeInfo.cs | 0 .../Time/TimeZone/Types/TimeZoneRule.cs | 0 .../Time/TimeZone/Types/TzifHeader.cs | 0 .../Services/Time/Types/SteadyClockContext.cs | 0 .../Services/Time/Types/TimePermissions.cs | 0 .../HOS/Services/Usb/IClientRootSession.cs | 0 .../HOS/Services/Usb/IDsService.cs | 0 .../HOS/Services/Usb/IPdCradleManager.cs | 0 .../HOS/Services/Usb/IPdManager.cs | 0 .../HOS/Services/Usb/IPmService.cs | 0 .../HOS/Services/Usb/IUnknown1.cs | 0 .../HOS/Services/Usb/IUnknown2.cs | 0 .../Services/Vi/IApplicationRootService.cs | 0 .../HOS/Services/Vi/IManagerRootService.cs | 0 .../HOS/Services/Vi/ISystemRootService.cs | 0 .../HOS/Services/Vi/ResultCode.cs | 0 .../AndroidSurfaceComposerClient.cs | 0 .../IManagerDisplayService.cs | 0 .../ISystemDisplayService.cs | 0 .../Types/DestinationScalingMode.cs | 0 .../Types/DisplayInfo.cs | 0 .../Types/SourceScalingMode.cs | 0 .../RootService/IApplicationDisplayService.cs | 0 .../HOS/Services/Vi/Types/ViServiceType.cs | 0 .../HOS/Services/Wlan/IInfraManager.cs | 0 .../HOS/Services/Wlan/ILocalGetActionFrame.cs | 0 .../HOS/Services/Wlan/ILocalGetFrame.cs | 0 .../HOS/Services/Wlan/ILocalManager.cs | 0 .../HOS/Services/Wlan/ISocketGetFrame.cs | 0 .../HOS/Services/Wlan/ISocketManager.cs | 0 .../HOS/Services/Wlan/IUnknown1.cs | 0 .../HOS/SystemState/AppletStateMgr.cs | 0 .../Ryujinx.HLE}/HOS/SystemState/ColorSet.cs | 0 .../HOS/SystemState/KeyboardLayout.cs | 0 .../HOS/SystemState/RegionCode.cs | 0 .../HOS/SystemState/SystemLanguage.cs | 0 .../HOS/SystemState/SystemStateMgr.cs | 0 .../HOS/SystemState/TitleLanguage.cs | 0 .../HOS/Tamper/AtmosphereCompiler.cs | 0 .../HOS/Tamper/AtmosphereProgram.cs | 0 .../HOS/Tamper/CodeEmitters/Arithmetic.cs | 0 .../CodeEmitters/BeginConditionalBlock.cs | 0 .../HOS/Tamper/CodeEmitters/DebugLog.cs | 0 .../CodeEmitters/EndConditionalBlock.cs | 0 .../CodeEmitters/KeyPressConditional.cs | 0 .../Tamper/CodeEmitters/LegacyArithmetic.cs | 0 .../CodeEmitters/LoadRegisterWithConstant.cs | 0 .../CodeEmitters/LoadRegisterWithMemory.cs | 0 .../Tamper/CodeEmitters/MemoryConditional.cs | 0 .../HOS/Tamper/CodeEmitters/PauseProcess.cs | 0 .../CodeEmitters/ReadOrWriteStaticRegister.cs | 0 .../CodeEmitters/RegisterConditional.cs | 0 .../HOS/Tamper/CodeEmitters/ResumeProcess.cs | 0 .../CodeEmitters/SaveOrRestoreRegister.cs | 0 .../SaveOrRestoreRegisterWithMask.cs | 0 .../HOS/Tamper/CodeEmitters/StartEndLoop.cs | 0 .../CodeEmitters/StoreConstantToAddress.cs | 0 .../CodeEmitters/StoreConstantToMemory.cs | 0 .../CodeEmitters/StoreRegisterToMemory.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/CodeType.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/Comparison.cs | 0 .../HOS/Tamper/CompilationContext.cs | 0 .../HOS/Tamper/Conditions/CondEQ.cs | 0 .../HOS/Tamper/Conditions/CondGE.cs | 0 .../HOS/Tamper/Conditions/CondGT.cs | 0 .../HOS/Tamper/Conditions/CondLE.cs | 0 .../HOS/Tamper/Conditions/CondLT.cs | 0 .../HOS/Tamper/Conditions/CondNE.cs | 0 .../HOS/Tamper/Conditions/ICondition.cs | 0 .../HOS/Tamper/Conditions/InputMask.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/ITamperProgram.cs | 0 .../HOS/Tamper/ITamperedProcess.cs | 0 .../HOS/Tamper/InstructionHelper.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/MemoryHelper.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/MemoryRegion.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/OperationBlock.cs | 0 .../HOS/Tamper/Operations/Block.cs | 0 .../HOS/Tamper/Operations/ForBlock.cs | 0 .../HOS/Tamper/Operations/IOperand.cs | 0 .../HOS/Tamper/Operations/IOperation.cs | 0 .../HOS/Tamper/Operations/IfBlock.cs | 0 .../HOS/Tamper/Operations/OpAdd.cs | 0 .../HOS/Tamper/Operations/OpAnd.cs | 0 .../HOS/Tamper/Operations/OpLog.cs | 0 .../HOS/Tamper/Operations/OpLsh.cs | 0 .../HOS/Tamper/Operations/OpMov.cs | 0 .../HOS/Tamper/Operations/OpMul.cs | 0 .../HOS/Tamper/Operations/OpNot.cs | 0 .../HOS/Tamper/Operations/OpOr.cs | 0 .../HOS/Tamper/Operations/OpProcCtrl.cs | 0 .../HOS/Tamper/Operations/OpRsh.cs | 0 .../HOS/Tamper/Operations/OpSub.cs | 0 .../HOS/Tamper/Operations/OpXor.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/Parameter.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/Pointer.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/Register.cs | 0 .../HOS/Tamper/TamperedKProcess.cs | 0 .../Ryujinx.HLE}/HOS/Tamper/Value.cs | 0 .../Ryujinx.HLE}/HOS/TamperMachine.cs | 0 .../HOS/UserChannelPersistence.cs | 0 .../Ryujinx.HLE}/Homebrew.npdm | Bin .../Ryujinx.HLE}/Loaders/Elf/ElfDynamic.cs | 0 .../Ryujinx.HLE}/Loaders/Elf/ElfDynamicTag.cs | 0 .../Ryujinx.HLE}/Loaders/Elf/ElfSymbol.cs | 0 .../Ryujinx.HLE}/Loaders/Elf/ElfSymbol32.cs | 0 .../Ryujinx.HLE}/Loaders/Elf/ElfSymbol64.cs | 0 .../Loaders/Elf/ElfSymbolBinding.cs | 0 .../Ryujinx.HLE}/Loaders/Elf/ElfSymbolType.cs | 0 .../Loaders/Elf/ElfSymbolVisibility.cs | 0 .../Loaders/Executables/IExecutable.cs | 0 .../Loaders/Executables/KipExecutable.cs | 0 .../Loaders/Executables/NroExecutable.cs | 0 .../Loaders/Executables/NsoExecutable.cs | 0 .../Ryujinx.HLE}/Loaders/Mods/IPSPatcher.cs | 0 .../Loaders/Mods/IPSwitchPatcher.cs | 0 .../Ryujinx.HLE}/Loaders/Mods/MemPatch.cs | 0 .../Ryujinx.HLE}/Loaders/Npdm/ACI0.cs | 0 .../Ryujinx.HLE}/Loaders/Npdm/ACID.cs | 0 .../Loaders/Npdm/FsAccessControl.cs | 0 .../Loaders/Npdm/FsAccessHeader.cs | 0 .../Loaders/Npdm/KernelAccessControl.cs | 0 .../Ryujinx.HLE}/Loaders/Npdm/Npdm.cs | 0 .../Loaders/Npdm/ServiceAccessControl.cs | 0 .../Extensions/FileSystemExtensions.cs | 0 .../Extensions/LocalFileSystemExtensions.cs | 0 .../Extensions/MetaLoaderExtensions.cs | 0 .../Processes/Extensions/NcaExtensions.cs | 0 .../PartitionFileSystemExtensions.cs | 0 .../Loaders/Processes/ProcessConst.cs | 0 .../Loaders/Processes/ProcessLoader.cs | 0 .../Loaders/Processes/ProcessLoaderHelper.cs | 0 .../Loaders/Processes/ProcessResult.cs | 0 .../Ryujinx.HLE}/MemoryConfiguration.cs | 0 .../Ryujinx.HLE}/PerformanceStatistics.cs | 0 .../Ryujinx.HLE}/Ryujinx.HLE.csproj | 0 {Ryujinx.HLE => src/Ryujinx.HLE}/Switch.cs | 0 .../Ui/DynamicTextChangedHandler.cs | 0 .../Ui/IDynamicTextInputHandler.cs | 0 .../Ryujinx.HLE}/Ui/IHostUiHandler.cs | 0 .../Ryujinx.HLE}/Ui/IHostUiTheme.cs | 0 .../Ui/Input/NpadButtonHandler.cs | 0 .../Ryujinx.HLE}/Ui/Input/NpadReader.cs | 0 .../Ryujinx.HLE}/Ui/KeyPressedHandler.cs | 0 .../Ryujinx.HLE}/Ui/KeyReleasedHandler.cs | 0 .../Ryujinx.HLE}/Ui/RenderingSurfaceInfo.cs | 0 .../Ryujinx.HLE}/Ui/ThemeColor.cs | 0 .../Ryujinx.HLE}/Utilities/StringUtils.cs | 0 .../HeadlessDynamicTextInputHandler.cs | 0 .../HeadlessHostUiTheme.cs | 0 .../Ryujinx.Headless.SDL2}/HideCursor.cs | 0 .../OpenGL/OpenGLWindow.cs | 0 .../Ryujinx.Headless.SDL2}/Options.cs | 0 .../Ryujinx.Headless.SDL2}/Program.cs | 0 .../Ryujinx.Headless.SDL2.csproj | 6 +- .../Ryujinx.Headless.SDL2}/Ryujinx.bmp | Bin .../Ryujinx.Headless.SDL2}/SDL2Mouse.cs | 0 .../Ryujinx.Headless.SDL2}/SDL2MouseDriver.cs | 0 .../StatusUpdatedEventArgs.cs | 0 .../Vulkan/VulkanWindow.cs | 0 .../Ryujinx.Headless.SDL2}/WindowBase.cs | 0 .../Ryujinx.Horizon.Common}/ISyscallApi.cs | 0 .../Ryujinx.Horizon.Common}/IThreadContext.cs | 0 .../InvalidResultException.cs | 0 .../Ryujinx.Horizon.Common}/KernelResult.cs | 0 .../Ryujinx.Horizon.Common}/OnScopeExit.cs | 0 .../Ryujinx.Horizon.Common}/Result.cs | 0 .../Ryujinx.Horizon.Common}/ResultNames.cs | 0 .../Ryujinx.Horizon.Common.csproj | 0 .../ThreadTerminatedException.cs | 0 .../CodeGenerator.cs | 0 .../Hipc/CommandArgType.cs | 0 .../Hipc/CommandInterface.cs | 0 .../Hipc/HipcGenerator.cs | 0 .../Hipc/HipcSyntaxReceiver.cs | 0 .../Ryujinx.Horizon.Generators.csproj | 0 .../CodeGenerator.cs | 0 .../Kernel/SyscallGenerator.cs | 0 .../Kernel/SyscallSyntaxReceiver.cs | 0 .../Ryujinx.Horizon.Kernel.Generators.csproj | 0 .../Ryujinx.Horizon}/HeapAllocator.cs | 0 .../Ryujinx.Horizon}/HorizonOptions.cs | 0 .../Ryujinx.Horizon}/HorizonStatic.cs | 0 .../Ryujinx.Horizon}/IService.cs | 0 .../LogManager/Ipc/LmLogger.cs | 0 .../LogManager/Ipc/LogService.cs | 0 .../LogManager/LmIpcServer.cs | 0 .../Ryujinx.Horizon}/LogManager/LmMain.cs | 0 .../LogManager/Types/LogPacket.cs | 0 .../Prepo/Ipc/PrepoService.cs | 0 .../Ryujinx.Horizon}/Prepo/PrepoIpcServer.cs | 0 .../Ryujinx.Horizon}/Prepo/PrepoMain.cs | 0 .../Ryujinx.Horizon}/Prepo/PrepoResult.cs | 0 .../Prepo/PrepoServerManager.cs | 0 .../Prepo/Types/PrepoPortIndex.cs | 0 .../Types/PrepoServicePermissionLevel.cs | 0 .../Ryujinx.Horizon}/Ryujinx.Horizon.csproj | 0 .../Ryujinx.Horizon}/Sdk/Account/Uid.cs | 0 .../Ryujinx.Horizon}/Sdk/DebugUtil.cs | 0 .../Ryujinx.Horizon}/Sdk/Diag/LogSeverity.cs | 0 .../Ryujinx.Horizon}/Sdk/Lm/ILmLogger.cs | 0 .../Ryujinx.Horizon}/Sdk/Lm/ILogService.cs | 0 .../Sdk/Lm/LogDataChunkKey.cs | 0 .../Ryujinx.Horizon}/Sdk/Lm/LogDestination.cs | 0 .../Ryujinx.Horizon}/Sdk/Lm/LogPacketFlags.cs | 0 .../Sdk/Lm/LogPacketHeader.cs | 0 .../Ryujinx.Horizon}/Sdk/Ncm/ApplicationId.cs | 0 .../Ryujinx.Horizon}/Sdk/OsTypes/Event.cs | 0 .../Sdk/OsTypes/EventClearMode.cs | 0 .../Ryujinx.Horizon}/Sdk/OsTypes/EventType.cs | 0 .../Sdk/OsTypes/Impl/InterProcessEvent.cs | 0 .../Sdk/OsTypes/Impl/InterProcessEventImpl.cs | 0 .../Sdk/OsTypes/Impl/MultiWaitImpl.cs | 0 .../Sdk/OsTypes/InitializationState.cs | 0 .../Sdk/OsTypes/InterProcessEventType.cs | 0 .../Ryujinx.Horizon}/Sdk/OsTypes/MultiWait.cs | 0 .../Sdk/OsTypes/MultiWaitHolder.cs | 0 .../Sdk/OsTypes/MultiWaitHolderBase.cs | 0 .../Sdk/OsTypes/MultiWaitHolderOfEvent.cs | 0 .../Sdk/OsTypes/MultiWaitHolderOfHandle.cs | 0 .../Ryujinx.Horizon}/Sdk/OsTypes/OsEvent.cs | 0 .../Sdk/OsTypes/OsMultiWait.cs | 0 .../Sdk/OsTypes/OsProcessHandle.cs | 0 .../Ryujinx.Horizon}/Sdk/OsTypes/OsResult.cs | 0 .../Sdk/OsTypes/OsSystemEvent.cs | 0 .../Sdk/OsTypes/OsThreadManager.cs | 0 .../Sdk/OsTypes/SystemEventType.cs | 0 .../Ryujinx.Horizon}/Sdk/OsTypes/TriBool.cs | 0 .../Sdk/Prepo/IPrepoService.cs | 0 .../Ryujinx.Horizon}/Sdk/ServiceUtil.cs | 0 .../Sdk/Sf/Cmif/CmifDomainInHeader.cs | 0 .../Sdk/Sf/Cmif/CmifDomainOutHeader.cs | 0 .../Sdk/Sf/Cmif/CmifDomainRequestType.cs | 0 .../Sdk/Sf/Cmif/CmifInHeader.cs | 0 .../Sdk/Sf/Cmif/CmifMessage.cs | 0 .../Sdk/Sf/Cmif/CmifOutHeader.cs | 0 .../Sdk/Sf/Cmif/CmifRequest.cs | 0 .../Sdk/Sf/Cmif/CmifRequestFormat.cs | 0 .../Sdk/Sf/Cmif/CmifResponse.cs | 0 .../Sdk/Sf/Cmif/CommandType.cs | 0 .../Sdk/Sf/Cmif/DomainServiceObject.cs | 0 .../Cmif/DomainServiceObjectDispatchTable.cs | 0 .../Sf/Cmif/DomainServiceObjectProcessor.cs | 0 .../Sdk/Sf/Cmif/HandlesToClose.cs | 0 .../Sdk/Sf/Cmif/InlineContext.cs | 0 .../Sdk/Sf/Cmif/PointerAndSize.cs | 0 .../Sdk/Sf/Cmif/ScopedInlineContextChange.cs | 0 .../Sdk/Sf/Cmif/ServerDomainBase.cs | 0 .../Sdk/Sf/Cmif/ServerDomainManager.cs | 0 .../Sdk/Sf/Cmif/ServerMessageProcessor.cs | 0 .../Sf/Cmif/ServerMessageRuntimeMetadata.cs | 0 .../Sdk/Sf/Cmif/ServiceDispatchContext.cs | 0 .../Sdk/Sf/Cmif/ServiceDispatchMeta.cs | 0 .../Sdk/Sf/Cmif/ServiceDispatchTable.cs | 0 .../Sdk/Sf/Cmif/ServiceDispatchTableBase.cs | 0 .../Sdk/Sf/Cmif/ServiceObjectHolder.cs | 0 .../Sdk/Sf/CmifCommandAttribute.cs | 0 .../Ryujinx.Horizon}/Sdk/Sf/CommandArg.cs | 0 .../Sdk/Sf/CommandArgAttributes.cs | 0 .../Ryujinx.Horizon}/Sdk/Sf/CommandHandler.cs | 0 .../Sdk/Sf/CommandSerialization.cs | 0 .../Ryujinx.Horizon}/Sdk/Sf/Hipc/Api.cs | 0 .../Ryujinx.Horizon}/Sdk/Sf/Hipc/Header.cs | 0 .../Sdk/Sf/Hipc/HipcBufferDescriptor.cs | 0 .../Sdk/Sf/Hipc/HipcBufferFlags.cs | 0 .../Sdk/Sf/Hipc/HipcBufferMode.cs | 0 .../Sdk/Sf/Hipc/HipcManager.cs | 0 .../Sdk/Sf/Hipc/HipcMessage.cs | 0 .../Sdk/Sf/Hipc/HipcMessageData.cs | 0 .../Sdk/Sf/Hipc/HipcMetadata.cs | 0 .../Sdk/Sf/Hipc/HipcReceiveListEntry.cs | 0 .../Sdk/Sf/Hipc/HipcResult.cs | 0 .../Sdk/Sf/Hipc/HipcStaticDescriptor.cs | 0 .../Sdk/Sf/Hipc/ManagerOptions.cs | 0 .../Sdk/Sf/Hipc/ReceiveResult.cs | 0 .../Ryujinx.Horizon}/Sdk/Sf/Hipc/Server.cs | 0 .../Sdk/Sf/Hipc/ServerDomainSessionManager.cs | 0 .../Sdk/Sf/Hipc/ServerManager.cs | 0 .../Sdk/Sf/Hipc/ServerManagerBase.cs | 0 .../Sdk/Sf/Hipc/ServerSession.cs | 0 .../Sdk/Sf/Hipc/ServerSessionManager.cs | 0 .../Sdk/Sf/Hipc/SpecialHeader.cs | 0 .../Sdk/Sf/HipcCommandProcessor.cs | 0 .../Ryujinx.Horizon}/Sdk/Sf/IServiceObject.cs | 0 .../Sdk/Sf/RawDataOffsetCalculator.cs | 0 .../Ryujinx.Horizon}/Sdk/Sf/SfResult.cs | 0 .../Sdk/Sm/IManagerService.cs | 0 .../Ryujinx.Horizon}/Sdk/Sm/IUserService.cs | 0 .../Ryujinx.Horizon}/Sdk/Sm/ServiceName.cs | 0 .../Ryujinx.Horizon}/Sdk/Sm/SmApi.cs | 0 .../Ryujinx.Horizon}/ServiceEntry.cs | 0 .../Ryujinx.Horizon}/ServiceTable.cs | 0 .../Ryujinx.Horizon}/Sm/Impl/ServiceInfo.cs | 0 .../Sm/Impl/ServiceManager.cs | 0 .../Ryujinx.Horizon}/Sm/Ipc/ManagerService.cs | 0 .../Ryujinx.Horizon}/Sm/Ipc/UserService.cs | 0 .../Ryujinx.Horizon}/Sm/SmMain.cs | 0 .../Ryujinx.Horizon}/Sm/SmResult.cs | 0 .../Ryujinx.Horizon}/Sm/SmServerManager.cs | 0 .../Ryujinx.Horizon}/Sm/Types/SmPortIndex.cs | 0 .../Ryujinx.Input.SDL2.csproj | 0 .../Ryujinx.Input.SDL2}/SDL2Gamepad.cs | 0 .../Ryujinx.Input.SDL2}/SDL2GamepadDriver.cs | 0 .../Ryujinx.Input.SDL2}/SDL2Keyboard.cs | 0 .../Ryujinx.Input.SDL2}/SDLKeyboardDriver.cs | 0 .../Assigner/GamepadButtonAssigner.cs | 0 .../Assigner/IButtonAssigner.cs | 0 .../Assigner/KeyboardKeyAssigner.cs | 0 .../Ryujinx.Input}/GamepadButtonInputId.cs | 0 .../Ryujinx.Input}/GamepadFeaturesFlag.cs | 0 .../Ryujinx.Input}/GamepadStateSnapshot.cs | 0 .../Ryujinx.Input}/HLE/InputManager.cs | 0 .../Ryujinx.Input}/HLE/NpadController.cs | 0 .../Ryujinx.Input}/HLE/NpadManager.cs | 0 .../Ryujinx.Input}/HLE/TouchScreenManager.cs | 0 .../Ryujinx.Input}/IGamepad.cs | 0 .../Ryujinx.Input}/IGamepadDriver.cs | 0 .../Ryujinx.Input}/IKeyboard.cs | 0 .../Ryujinx.Input}/IMouse.cs | 0 {Ryujinx.Input => src/Ryujinx.Input}/Key.cs | 0 .../Ryujinx.Input}/KeyboardStateSnapshot.cs | 0 .../Ryujinx.Input}/Motion/CemuHook/Client.cs | 0 .../CemuHook/Protocol/ControllerData.cs | 0 .../CemuHook/Protocol/ControllerInfo.cs | 0 .../Motion/CemuHook/Protocol/Header.cs | 0 .../Motion/CemuHook/Protocol/MessageType.cs | 0 .../CemuHook/Protocol/SharedResponse.cs | 0 .../Ryujinx.Input}/Motion/MotionInput.cs | 0 .../Motion/MotionSensorFilter.cs | 0 .../Ryujinx.Input}/MotionInputId.cs | 0 .../Ryujinx.Input}/MouseButton.cs | 0 .../Ryujinx.Input}/MouseStateSnapshot.cs | 0 .../Ryujinx.Input}/Ryujinx.Input.csproj | 0 .../Ryujinx.Input}/StickInputId.cs | 0 .../MockVirtualMemoryManager.cs | 0 .../MultiRegionTrackingTests.cs | 0 .../Ryujinx.Memory.Tests.csproj | 0 .../Ryujinx.Memory.Tests}/Tests.cs | 0 .../Ryujinx.Memory.Tests}/TrackingTests.cs | 0 .../Ryujinx.Memory}/AddressSpaceManager.cs | 0 .../Ryujinx.Memory}/IRefCounted.cs | 0 .../Ryujinx.Memory}/IVirtualMemoryManager.cs | 0 .../Ryujinx.Memory}/IWritableBlock.cs | 0 .../Ryujinx.Memory}/InvalidAccessHandler.cs | 0 .../InvalidMemoryRegionException.cs | 0 .../Ryujinx.Memory}/MemoryAllocationFlags.cs | 0 .../Ryujinx.Memory}/MemoryBlock.cs | 0 .../Ryujinx.Memory}/MemoryConstants.cs | 0 .../Ryujinx.Memory}/MemoryManagement.cs | 0 .../Ryujinx.Memory}/MemoryManagementUnix.cs | 0 .../MemoryManagementWindows.cs | 0 .../MemoryManagerUnixHelper.cs | 0 .../Ryujinx.Memory}/MemoryMapFlags.cs | 0 .../MemoryNotContiguousException.cs | 0 .../Ryujinx.Memory}/MemoryPermission.cs | 0 .../MemoryProtectionException.cs | 0 .../Ryujinx.Memory}/NativeMemoryManager.cs | 0 .../Ryujinx.Memory}/PageTable.cs | 0 .../Ryujinx.Memory}/Range/HostMemoryRange.cs | 0 .../Ryujinx.Memory}/Range/IMultiRangeItem.cs | 0 .../Range/INonOverlappingRange.cs | 0 .../Ryujinx.Memory}/Range/IRange.cs | 0 .../Ryujinx.Memory}/Range/MemoryRange.cs | 0 .../Ryujinx.Memory}/Range/MultiRange.cs | 0 .../Ryujinx.Memory}/Range/MultiRangeList.cs | 0 .../Range/NonOverlappingRangeList.cs | 0 .../Ryujinx.Memory}/Range/RangeList.cs | 0 .../Ryujinx.Memory}/Ryujinx.Memory.csproj | 0 .../Tracking/AbstractRegion.cs | 0 .../Ryujinx.Memory}/Tracking/BitMap.cs | 0 .../Tracking/ConcurrentBitmap.cs | 0 .../Tracking/IMultiRegionHandle.cs | 0 .../Ryujinx.Memory}/Tracking/IRegionHandle.cs | 0 .../Tracking/MemoryTracking.cs | 0 .../Tracking/MultiRegionHandle.cs | 0 .../Tracking/PreciseRegionSignal.cs | 0 .../Ryujinx.Memory}/Tracking/RegionHandle.cs | 0 .../Ryujinx.Memory}/Tracking/RegionSignal.cs | 0 .../Tracking/SmartMultiRegionHandle.cs | 0 .../Ryujinx.Memory}/Tracking/VirtualRegion.cs | 0 .../WindowsShared/MappingTree.cs | 0 .../WindowsShared/PlaceholderManager.cs | 0 .../WindowsShared/WindowsApi.cs | 0 .../WindowsShared/WindowsApiException.cs | 0 .../WindowsShared/WindowsFlags.cs | 0 .../Ryujinx.Memory}/WritableRegion.cs | 0 .../Ryujinx.SDL2.Common.csproj | 0 .../Ryujinx.SDL2.Common}/SDL2Driver.cs | 0 .../Ryujinx.ShaderTools}/Program.cs | 0 .../Ryujinx.ShaderTools.csproj | 0 .../Ryujinx.Tests.Unicorn}/IndexedProperty.cs | 0 .../MemoryPermission.cs | 0 .../Ryujinx.Tests.Unicorn.csproj | 0 .../Ryujinx.Tests.Unicorn}/SimdValue.cs | 0 .../Ryujinx.Tests.Unicorn}/UnicornAArch32.cs | 0 .../Ryujinx.Tests.Unicorn}/UnicornAArch64.cs | 0 .../Ryujinx.Tests}/.runsettings | 0 .../AudioRendererConfigurationTests.cs | 0 .../Audio/Renderer/BehaviourParameterTests.cs | 0 .../Renderer/BiquadFilterParameterTests.cs | 0 .../Renderer/Common/UpdateDataHeaderTests.cs | 0 .../Renderer/Common/VoiceUpdateStateTests.cs | 0 .../Audio/Renderer/Common/WaveBufferTests.cs | 0 .../Audio/Renderer/Dsp/ResamplerTests.cs | 0 .../Audio/Renderer/Dsp/UpsamplerTests.cs | 0 .../Renderer/EffectInfoParameterTests.cs | 0 .../Audio/Renderer/EffectOutStatusTests.cs | 0 .../Renderer/MemoryPoolParameterTests.cs | 0 .../BehaviourErrorInfoOutStatusTests.cs | 0 .../Parameter/Effect/AuxParameterTests.cs | 0 .../BiquadFilterEffectParameterTests.cs | 0 .../Effect/BufferMixerParameterTests.cs | 0 .../Effect/CompressorParameterTests.cs | 0 .../Parameter/Effect/DelayParameterTests.cs | 0 .../Parameter/Effect/LimiterParameterTests.cs | 0 .../Effect/LimiterStatisticsTests.cs | 0 .../Effect/Reverb3dParameterTests.cs | 0 .../Parameter/Effect/ReverbParameterTests.cs | 0 .../MixInParameterDirtyOnlyUpdateTests.cs | 0 .../Renderer/Parameter/MixParameterTests.cs | 0 .../Parameter/PerformanceInParameterTests.cs | 0 .../Parameter/PerformanceOutStatusTests.cs | 0 .../Parameter/RendererInfoOutStatusTests.cs | 0 .../Sink/CircularBufferParameterTests.cs | 0 .../Parameter/Sink/DeviceParameterTests.cs | 0 .../Parameter/SinkInParameterTests.cs | 0 .../Renderer/Parameter/SinkOutStatusTests.cs | 0 .../Parameter/SplitterInParamHeaderTests.cs | 0 .../Audio/Renderer/Server/AddressInfoTests.cs | 0 .../Renderer/Server/BehaviourContextTests.cs | 0 .../Renderer/Server/MemoryPoolStateTests.cs | 0 .../Audio/Renderer/Server/MixStateTests.cs | 0 .../Audio/Renderer/Server/PoolMapperTests.cs | 0 .../Server/SplitterDestinationTests.cs | 0 .../Renderer/Server/SplitterStateTests.cs | 0 .../Server/VoiceChannelResourceTests.cs | 0 .../Audio/Renderer/Server/VoiceStateTests.cs | 0 .../Audio/Renderer/Server/WaveBufferTests.cs | 0 .../VoiceChannelResourceInParameterTests.cs | 0 .../Audio/Renderer/VoiceInParameterTests.cs | 0 .../Audio/Renderer/VoiceOutStatusTests.cs | 0 .../Cpu/Arm64CodeGenCommonTests.cs | 0 .../Ryujinx.Tests}/Cpu/CpuContext.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTest.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTest32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAlu.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAlu32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAluBinary.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAluBinary32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAluImm.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAluImm32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAluRs.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAluRs32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestAluRx.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestBf32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestBfm.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestCcmpImm.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestCcmpReg.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestCsel.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestMisc.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestMisc32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestMov.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestMul.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestMul32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimd.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimd32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdCrypto.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdCrypto32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdCvt.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdCvt32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdExt.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdFcond.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdFmov.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdImm.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdIns.cs | 0 .../Cpu/CpuTestSimdLogical32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdMemory32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdMov32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdReg.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdReg32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdRegElem.cs | 0 .../Cpu/CpuTestSimdRegElem32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdRegElemF.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdShImm.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdShImm32.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSimdTbl.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestSystem.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestT32Alu.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestT32Flow.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestT32Mem.cs | 0 .../Ryujinx.Tests}/Cpu/CpuTestThumb.cs | 0 .../Ryujinx.Tests}/Cpu/EnvironmentTests.cs | 0 .../Cpu/PrecomputedMemoryThumbTestCase.cs | 0 .../Cpu/PrecomputedThumbTestCase.cs | 0 .../HLE/SoftwareKeyboardTests.cs | 0 .../Memory/MockMemoryManager.cs | 0 .../Ryujinx.Tests}/Memory/PartialUnmaps.cs | 0 .../Ryujinx.Tests}/Ryujinx.Tests.csproj | 0 .../Ryujinx.Tests}/Time/TimeZoneRuleTests.cs | 0 .../Ryujinx.Tests}/TreeDictionaryTests.cs | 0 .../App/ApplicationAddedEventArgs.cs | 0 .../App/ApplicationCountUpdatedEventArgs.cs | 0 .../Ryujinx.Ui.Common}/App/ApplicationData.cs | 0 .../App/ApplicationJsonSerializerContext.cs | 0 .../App/ApplicationLibrary.cs | 0 .../App/ApplicationMetadata.cs | 0 .../Configuration/AudioBackend.cs | 0 .../Configuration/ConfigurationFileFormat.cs | 0 .../ConfigurationFileFormatSettings.cs | 0 .../ConfigurationJsonSerializerContext.cs | 0 .../Configuration/ConfigurationLoadResult.cs | 0 .../Configuration/ConfigurationState.cs | 0 .../Configuration/FileTypes.cs | 0 .../Configuration/LoggerModule.cs | 0 .../Configuration/System/Language.cs | 0 .../Configuration/System/Region.cs | 0 .../Configuration/Ui/ColumnSort.cs | 0 .../Configuration/Ui/GuiColumns.cs | 0 .../Configuration/Ui/ShownFileTypes.cs | 0 .../DiscordIntegrationModule.cs | 0 .../Extensions/FileTypeExtensions.cs | 0 .../Helper/CommandLineState.cs | 0 .../Helper/ConsoleHelper.cs | 0 .../Helper/FileAssociationHelper.cs | 0 .../Ryujinx.Ui.Common}/Helper/ObjectiveC.cs | 0 .../Ryujinx.Ui.Common}/Helper/OpenHelper.cs | 0 .../Helper/SetupValidator.cs | 0 .../Models/Amiibo/AmiiboApi.cs | 0 .../Models/Amiibo/AmiiboApiGamesSwitch.cs | 0 .../Models/Amiibo/AmiiboApiUsage.cs | 0 .../Models/Amiibo/AmiiboJson.cs | 0 .../Amiibo/AmiiboJsonSerializerContext.cs | 0 .../Github/GithubReleaseAssetJsonResponse.cs | 0 .../Github/GithubReleasesJsonResponse.cs | 0 .../GithubReleasesJsonSerializerContext.cs | 0 .../Resources/Controller_JoyConLeft.svg | 0 .../Resources/Controller_JoyConPair.svg | 0 .../Resources/Controller_JoyConRight.svg | 0 .../Resources/Controller_ProCon.svg | 0 .../Ryujinx.Ui.Common}/Resources/Icon_NCA.png | Bin .../Ryujinx.Ui.Common}/Resources/Icon_NRO.png | Bin .../Ryujinx.Ui.Common}/Resources/Icon_NSO.png | Bin .../Ryujinx.Ui.Common}/Resources/Icon_NSP.png | Bin .../Ryujinx.Ui.Common}/Resources/Icon_XCI.png | Bin .../Resources/Logo_Amiibo.png | Bin .../Resources/Logo_Discord_Dark.png | Bin .../Resources/Logo_Discord_Light.png | Bin .../Resources/Logo_GitHub_Dark.png | Bin .../Resources/Logo_GitHub_Light.png | Bin .../Resources/Logo_Patreon_Dark.png | Bin .../Resources/Logo_Patreon_Light.png | Bin .../Resources/Logo_Ryujinx.png | Bin .../Resources/Logo_Twitter_Dark.png | Bin .../Resources/Logo_Twitter_Light.png | Bin .../Ryujinx.Ui.Common.csproj | 0 .../Ryujinx.Ui.Common}/UserError.cs | 0 .../LocaleGenerator.cs | 0 .../Ryujinx.Ui.LocaleGenerator.csproj | 0 .../Ryujinx}/Input/GTK3/GTK3Keyboard.cs | 0 .../Ryujinx}/Input/GTK3/GTK3KeyboardDriver.cs | 0 .../Ryujinx}/Input/GTK3/GTK3MappingHelper.cs | 0 .../Ryujinx}/Input/GTK3/GTK3Mouse.cs | 0 .../Ryujinx}/Input/GTK3/GTK3MouseDriver.cs | 0 .../Ryujinx}/Modules/Updater/UpdateDialog.cs | 0 .../Modules/Updater/UpdateDialog.glade | 0 .../Ryujinx}/Modules/Updater/Updater.cs | 0 {Ryujinx => src/Ryujinx}/Program.cs | 0 {Ryujinx => src/Ryujinx}/Ryujinx.csproj | 10 +-- {Ryujinx => src/Ryujinx}/Ryujinx.ico | Bin .../Ryujinx}/Ui/Applet/ErrorAppletDialog.cs | 0 .../Ui/Applet/GtkDynamicTextInputHandler.cs | 0 .../Ryujinx}/Ui/Applet/GtkHostUiHandler.cs | 0 .../Ryujinx}/Ui/Applet/GtkHostUiTheme.cs | 0 .../Ryujinx}/Ui/Applet/SwkbdAppletDialog.cs | 0 {Ryujinx => src/Ryujinx}/Ui/GLRenderer.cs | 0 .../Ryujinx}/Ui/Helper/MetalHelper.cs | 0 .../Ryujinx}/Ui/Helper/SortHelper.cs | 0 .../Ryujinx}/Ui/Helper/ThemeHelper.cs | 0 {Ryujinx => src/Ryujinx}/Ui/MainWindow.cs | 0 {Ryujinx => src/Ryujinx}/Ui/MainWindow.glade | 0 .../Ryujinx}/Ui/OpenToolkitBindingsContext.cs | 0 .../Ryujinx}/Ui/RendererWidgetBase.cs | 0 .../Ryujinx}/Ui/SPBOpenGLContext.cs | 0 .../Ryujinx}/Ui/StatusUpdatedEventArgs.cs | 0 {Ryujinx => src/Ryujinx}/Ui/VKRenderer.cs | 0 .../Widgets/GameTableContextMenu.Designer.cs | 0 .../Ui/Widgets/GameTableContextMenu.cs | 0 .../Ryujinx}/Ui/Widgets/GtkDialog.cs | 0 .../Ryujinx}/Ui/Widgets/GtkInputDialog.cs | 0 .../Ryujinx}/Ui/Widgets/ProfileDialog.cs | 0 .../Ryujinx}/Ui/Widgets/ProfileDialog.glade | 0 .../Ui/Widgets/RawInputToTextEntry.cs | 0 .../Ryujinx}/Ui/Widgets/UserErrorDialog.cs | 0 .../Ui/Windows/AboutWindow.Designer.cs | 0 .../Ryujinx}/Ui/Windows/AboutWindow.cs | 0 .../Ui/Windows/AmiiboWindow.Designer.cs | 0 .../Ryujinx}/Ui/Windows/AmiiboWindow.cs | 0 .../Ryujinx}/Ui/Windows/AvatarWindow.cs | 0 .../Ryujinx}/Ui/Windows/CheatWindow.cs | 0 .../Ryujinx}/Ui/Windows/CheatWindow.glade | 0 .../Ryujinx}/Ui/Windows/ControllerWindow.cs | 0 .../Ui/Windows/ControllerWindow.glade | 0 .../Ryujinx}/Ui/Windows/DlcWindow.cs | 0 .../Ryujinx}/Ui/Windows/DlcWindow.glade | 0 .../Ryujinx}/Ui/Windows/SettingsWindow.cs | 0 .../Ryujinx}/Ui/Windows/SettingsWindow.glade | 0 .../Ryujinx}/Ui/Windows/TitleUpdateWindow.cs | 0 .../Ui/Windows/TitleUpdateWindow.glade | 0 .../UserProfilesManagerWindow.Designer.cs | 0 .../Ui/Windows/UserProfilesManagerWindow.cs | 0 .../Autogenerated/CoreGrammar.cs | 0 .../Autogenerated/GlslStd450Grammar.cs | 0 .../Autogenerated/OpenClGrammar.cs | 0 .../Spv.Generator}/ConstantKey.cs | 0 .../Spv.Generator}/DeterministicHashCode.cs | 0 .../Spv.Generator}/DeterministicStringKey.cs | 0 .../Spv.Generator}/GeneratorPool.cs | 0 .../Spv.Generator}/Instruction.cs | 0 .../Spv.Generator}/InstructionOperands.cs | 0 {Spv.Generator => src/Spv.Generator}/LICENSE | 0 .../Spv.Generator}/LiteralInteger.cs | 0 .../Spv.Generator}/LiteralString.cs | 0 .../Spv.Generator}/Module.cs | 0 .../Spv.Generator}/Operand.cs | 0 .../Spv.Generator}/OperandType.cs | 0 .../Spv.Generator}/Spv.Generator.csproj | 0 .../Spv.Generator}/TypeDeclarationKey.cs | 0 {Spv.Generator => src/Spv.Generator}/spirv.cs | 0 3466 files changed, 55 insertions(+), 55 deletions(-) rename {ARMeilleure => src/ARMeilleure}/ARMeilleure.csproj (100%) rename {ARMeilleure => src/ARMeilleure}/Allocators.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/Arm64Optimizer.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/ArmCondition.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/ArmExtensionType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/ArmShiftType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/Assembler.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/CallingConvention.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/CodeGenCommon.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/CodeGenContext.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/CodeGenerator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/CodeGeneratorIntrinsic.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/HardwareCapabilities.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/IntrinsicInfo.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/IntrinsicTable.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/IntrinsicType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Arm64/PreAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/CompiledFunction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Linking/RelocEntry.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Linking/RelocInfo.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Linking/Symbol.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Linking/SymbolType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Optimizations/BlockPlacement.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Optimizations/ConstantFolding.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Optimizations/Optimizer.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Optimizations/Simplification.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Optimizations/TailMerge.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/PreAllocatorCommon.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/AllocationResult.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/CopyResolver.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/HybridAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/IRegisterAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/LinearScanAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/LiveInterval.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/LiveIntervalList.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/LiveRange.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/RegisterMasks.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/StackAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/RegisterAllocators/UseList.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Unwinding/UnwindInfo.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Unwinding/UnwindPseudoOp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/Unwinding/UnwindPushEntry.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/Assembler.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/AssemblerTable.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/CallConvName.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/CallingConvention.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/CodeGenCommon.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/CodeGenContext.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/CodeGenerator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/HardwareCapabilities.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/IntrinsicInfo.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/IntrinsicTable.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/IntrinsicType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/Mxcsr.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/PreAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/PreAllocatorSystemV.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/PreAllocatorWindows.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/X86Condition.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/X86Instruction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/X86Optimizer.cs (100%) rename {ARMeilleure => src/ARMeilleure}/CodeGen/X86/X86Register.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/AddressTable.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/Allocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/ArenaAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/BitMap.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/BitUtils.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/Counter.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/EntryTable.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/EnumUtils.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Common/NativeAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/Block.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/Condition.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/DataOp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/Decoder.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/DecoderHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/DecoderMode.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32Adr.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32Alu.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluBf.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluImm16.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluMla.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluRsImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluRsReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluUmull.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32AluUx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32BImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32BReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32Exception.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32HasSetFlags.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32Mem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32MemEx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32MemMult.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32MemReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32MemRsImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32Simd.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCode32SimdImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeAlu.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeAluImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeAluRs.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeAluRx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeBImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeCond.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeLit.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IOpCodeSimd.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/InstDescriptor.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/InstEmitter.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/IntType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32Alu.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluBf.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluImm16.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluMla.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluRsImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluRsReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluUmull.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32AluUx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32BImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32BReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32Exception.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32Mem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MemImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MemImm8.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MemLdEx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MemMult.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MemReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MemRsImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MemStEx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32Mrs.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32MsrReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32Sat.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32Sat16.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32Simd.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdBase.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdBinary.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdCmpZ.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdCvtFI.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdCvtTB.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdDupElem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdDupGP.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdExt.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdImm44.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdLong.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMemImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMemMult.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMemPair.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMemSingle.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMovGp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMovGpDouble.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMovGpElem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdMovn.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdRegElem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdRegElemLong.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdRegLong.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdRegS.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdRegWide.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdRev.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdS.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdSel.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdShImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdShImmLong.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdShImmNarrow.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdSpecial.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdSqrte.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32SimdTbl.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCode32System.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeAdr.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeAlu.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeAluBinary.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeAluImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeAluRs.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeAluRx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeBImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeBImmAl.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeBImmCmp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeBImmCond.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeBImmTest.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeBReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeBfm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeCcmp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeCcmpImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeCcmpReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeCsel.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeException.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMemEx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMemImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMemLit.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMemPair.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMemReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMov.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeMul.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimd.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdCvt.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdExt.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdFcond.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdFmov.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdIns.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdMemImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdMemLit.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdMemMs.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdMemPair.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdMemReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdMemSs.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdRegElem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdRegElemF.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdShImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSimdTbl.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeSystem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AddSubImm3.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AddSubReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AddSubSp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16Adr.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AluImm8.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AluImmZero.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AluRegHigh.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AluRegLow.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16AluUx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16BImm11.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16BImm8.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16BImmCmp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16BReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16Exception.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16IfThen.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16MemImm5.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16MemLit.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16MemMult.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16MemReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16MemSp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16MemStack.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16ShiftImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16ShiftReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT16SpRel.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32Alu.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluBf.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluImm12.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluMla.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluRsImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluUmull.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32AluUx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32BImm20.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32BImm24.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MemImm12.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MemImm8.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MemImm8D.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MemLdEx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MemMult.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MemRsImm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MemStEx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32MovImm16.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32ShiftReg.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeT32Tb.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/OpCodeTable.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/Optimizations/TailCallRemover.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/RegisterSize.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Decoders/ShiftType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Diagnostics/IRDumper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Diagnostics/Logger.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Diagnostics/PassName.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Diagnostics/Symbols.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Diagnostics/TranslatorEventSource.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/CryptoHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitAlu.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitAlu32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitAluHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitBfm.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitCcmp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitCsel.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitDiv.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitException.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitException32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitFlow.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitFlow32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitFlowHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitHash.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitHash32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitHashHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMemory.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMemory32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMemoryEx.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMemoryEx32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMemoryExHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMemoryHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMove.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMul.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitMul32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdArithmetic.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdArithmetic32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdCmp.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdCmp32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdCrypto.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdCrypto32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdCvt.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdCvt32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdHash.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdHash32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdHashHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdHelper32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdHelper32Arm64.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdHelperArm64.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdLogical.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdLogical32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdMemory.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdMemory32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdMove.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdMove32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdShift.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSimdShift32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSystem.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstEmitSystem32.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/InstName.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/NativeInterface.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/SoftFallback.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Instructions/SoftFloat.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/BasicBlock.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/BasicBlockFrequency.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/Comparison.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/IIntrusiveListNode.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/Instruction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/Intrinsic.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/IntrusiveList.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/MemoryOperand.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/Multiplier.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/Operand.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/OperandKind.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/OperandType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/Operation.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/PhiOperation.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/Register.cs (100%) rename {ARMeilleure => src/ARMeilleure}/IntermediateRepresentation/RegisterType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Memory/IJitMemoryAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Memory/IJitMemoryBlock.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Memory/IMemoryManager.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Memory/InvalidAccessException.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Memory/MemoryManagerType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Memory/ReservedRegion.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Native/JitSupportDarwin.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Native/libs/libarmeilleure-jitsupport.dylib (100%) rename {ARMeilleure => src/ARMeilleure}/Native/macos_jit_support/Makefile (100%) rename {ARMeilleure => src/ARMeilleure}/Native/macos_jit_support/support.c (100%) rename {ARMeilleure => src/ARMeilleure}/Optimizations.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Signal/NativeSignalHandler.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Signal/TestMethods.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Signal/UnixSignalHandlerRegistration.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Signal/WindowsPartialUnmapHandler.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Signal/WindowsSignalHandlerRegistration.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/Aarch32Mode.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/ExceptionCallback.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/ExecutionContext.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/ExecutionMode.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/FPCR.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/FPException.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/FPRoundingMode.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/FPSCR.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/FPSR.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/FPState.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/FPType.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/ICounter.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/NativeContext.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/PState.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/RegisterAlias.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/RegisterConsts.cs (100%) rename {ARMeilleure => src/ARMeilleure}/State/V128.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Statistics.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/ArmEmitterContext.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Cache/CacheEntry.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Cache/CacheMemoryAllocator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Cache/JitCache.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Cache/JitCacheInvalidation.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Cache/JitUnwindWindows.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Compiler.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/CompilerContext.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/CompilerOptions.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/ControlFlowGraph.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/DelegateHelper.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/DelegateInfo.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Delegates.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/DispatcherFunction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Dominance.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/EmitterContext.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/GuestFunction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/IntervalTree.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/PTC/EncodingCache.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/PTC/IPtcLoadState.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/PTC/Ptc.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/PTC/PtcFormatter.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/PTC/PtcLoadingState.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/PTC/PtcProfiler.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/PTC/PtcState.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/RegisterToLocal.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/RegisterUsage.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/RejitRequest.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/SsaConstruction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/SsaDeconstruction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/TranslatedFunction.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/Translator.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/TranslatorCache.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/TranslatorQueue.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/TranslatorStubs.cs (100%) rename {ARMeilleure => src/ARMeilleure}/Translation/TranslatorTestMethods.cs (100%) rename {Ryujinx.Audio.Backends.OpenAL => src/Ryujinx.Audio.Backends.OpenAL}/OpenALAudioBuffer.cs (100%) rename {Ryujinx.Audio.Backends.OpenAL => src/Ryujinx.Audio.Backends.OpenAL}/OpenALHardwareDeviceDriver.cs (100%) rename {Ryujinx.Audio.Backends.OpenAL => src/Ryujinx.Audio.Backends.OpenAL}/OpenALHardwareDeviceSession.cs (100%) rename {Ryujinx.Audio.Backends.OpenAL => src/Ryujinx.Audio.Backends.OpenAL}/Ryujinx.Audio.Backends.OpenAL.csproj (100%) rename {Ryujinx.Audio.Backends.SDL2 => src/Ryujinx.Audio.Backends.SDL2}/Ryujinx.Audio.Backends.SDL2.csproj (100%) rename {Ryujinx.Audio.Backends.SDL2 => src/Ryujinx.Audio.Backends.SDL2}/SDL2AudioBuffer.cs (100%) rename {Ryujinx.Audio.Backends.SDL2 => src/Ryujinx.Audio.Backends.SDL2}/SDL2HardwareDeviceDriver.cs (100%) rename {Ryujinx.Audio.Backends.SDL2 => src/Ryujinx.Audio.Backends.SDL2}/SDL2HardwareDeviceSession.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIo.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoBackend.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoChannelId.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoContext.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoDeviceAim.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoDeviceContext.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoError.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoException.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoFormat.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/SoundIoOutStreamContext.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/libsoundio/libs/libsoundio.dll (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/libsoundio/libs/libsoundio.dylib (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Native/libsoundio/libs/libsoundio.so (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/Ryujinx.Audio.Backends.SoundIo.csproj (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/SoundIoAudioBuffer.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/SoundIoHardwareDeviceDriver.cs (100%) rename {Ryujinx.Audio.Backends.SoundIo => src/Ryujinx.Audio.Backends.SoundIo}/SoundIoHardwareDeviceSession.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/AudioManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/Common/BackendHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/Common/DynamicRingBuffer.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/Common/HardwareDeviceSessionOutputBase.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/CompatLayer/Downmixing.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/Dummy/DummyHardwareDeviceDriver.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/Dummy/DummyHardwareDeviceSessionInput.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Common/AudioBuffer.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Common/AudioDeviceSession.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Common/AudioDeviceState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Common/AudioInputConfiguration.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Common/AudioOutputConfiguration.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Common/AudioUserBuffer.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Common/SampleFormat.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Constants.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Input/AudioInputManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Input/AudioInputSystem.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Integration/HardwareDeviceImpl.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Integration/IHardwareDevice.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Integration/IHardwareDeviceDriver.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Integration/IHardwareDeviceSession.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Integration/IWritableEvent.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Output/AudioOutputManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Output/AudioOutputSystem.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/AuxiliaryBufferAddresses.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/BehaviourParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/EdgeMatrix.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/EffectType.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/MemoryPoolUserState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/NodeIdHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/NodeIdType.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/NodeStates.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/PerformanceDetailType.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/PerformanceEntryType.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/PlayState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/ReverbEarlyMode.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/ReverbLateMode.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/SinkType.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/UpdateDataHeader.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/VoiceUpdateState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/WaveBuffer.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Common/WorkBufferAllocator.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Device/VirtualDevice.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Device/VirtualDeviceSession.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Device/VirtualDeviceSessionRegistry.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/AdpcmHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/AudioProcessor.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/BiquadFilterHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/BiquadFilterCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/CaptureBufferCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/CircularBufferSinkCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/ClearMixBufferCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/CommandList.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/CommandType.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/CompressorCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/CopyMixBufferCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/DataSourceVersion2Command.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/DelayCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/DepopPrepareCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/DeviceSinkCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/ICommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/LimiterCommandVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/LimiterCommandVersion2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/MixCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/MixRampCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/MixRampGroupedCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/PerformanceCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/Reverb3dCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/ReverbCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/UpsampleCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/VolumeCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Command/VolumeRampCommand.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/DataSourceHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Effect/DecayDelay.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Effect/DelayLine.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Effect/DelayLineReverb3d.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Effect/ExponentialMovingAverage.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/Effect/IDelayLine.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/FixedPointHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/FloatingPointHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/PcmHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/ResamplerHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/AdpcmLoopContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/AuxiliaryBufferHeader.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/BiquadFilterState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/CompressorState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/DelayState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/LimiterState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/Reverb3dState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/State/ReverbState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Dsp/UpsamplerHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/AudioRendererConfiguration.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/BiquadFilterParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/BufferMixerParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/CompressorParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/DelayParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/LimiterParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/LimiterStatistics.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/Reverb3dParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Effect/ReverbParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/EffectInParameterVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/EffectInParameterVersion2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/EffectOutStatusVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/EffectOutStatusVersion2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/EffectResultState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/EffectState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/IEffectInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/IEffectOutStatus.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/MemoryPoolInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/MemoryPoolOutStatus.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/MixParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Performance/PerformanceInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Performance/PerformanceOutStatus.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/RendererInfoOutStatus.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Sink/CircularBufferParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/Sink/DeviceParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/SinkInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/SinkOutStatus.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/SplitterDestinationInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/SplitterInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/SplitterInParameterHeader.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/VoiceChannelResourceInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/VoiceInParameter.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Parameter/VoiceOutStatus.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/AudioRenderSystem.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/AudioRendererManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/BehaviourContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/CommandBuffer.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/CommandGenerator.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/AuxiliaryBufferEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/BaseEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/BiquadFilterEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/BufferMixEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/CaptureBufferEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/CompressorEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/DelayEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/EffectContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/LimiterEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/Reverb3dEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/ReverbEffect.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Effect/UsageState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/ICommandProcessingTimeEstimator.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/MemoryPool/AddressInfo.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/MemoryPool/MemoryPoolState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/MemoryPool/PoolMapper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Mix/MixContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Mix/MixState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/IPerformanceDetailEntry.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/IPerformanceEntry.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/IPerformanceHeader.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceDetailVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceDetailVersion2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceEntryAddresses.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceEntryVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceEntryVersion2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Performance/PerformanceManagerGeneric.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/RendererSystemContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Sink/BaseSink.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Sink/CircularBufferSink.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Sink/DeviceSink.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Sink/SinkContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Splitter/SplitterContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Splitter/SplitterDestination.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Splitter/SplitterState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/StateUpdater.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Types/AudioRendererExecutionMode.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Types/AudioRendererRenderingDevice.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Types/PlayState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Upsampler/UpsamplerBufferState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Upsampler/UpsamplerManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Upsampler/UpsamplerState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Voice/VoiceChannelResource.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Voice/VoiceContext.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Voice/VoiceState.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Server/Voice/WaveBuffer.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/AudioProcessorMemoryManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/BitArray.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/FileHardwareDevice.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/Mailbox.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/Math/Matrix2x2.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/Math/Matrix6x6.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/Math/MatrixHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/Math/Vector6.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/SpanIOHelper.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/SpanMemoryManager.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Renderer/Utils/SplitterHardwareDevice.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/ResultCode.cs (100%) rename {Ryujinx.Audio => src/Ryujinx.Audio}/Ryujinx.Audio.csproj (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/App.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/App.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/AppHost.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Fonts/SegoeFluentIcons.ttf (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/de_DE.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/el_GR.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/en_US.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/es_ES.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/fr_FR.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/it_IT.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/ja_JP.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/ko_KR.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/pl_PL.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/pt_BR.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/ru_RU.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/tr_TR.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/uk_UA.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/zh_CN.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Locales/zh_TW.json (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Styles/BaseDark.xaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Styles/BaseLight.xaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Assets/Styles/Styles.xaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Common/ApplicationHelper.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Common/ApplicationSort.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Common/KeyboardHotkeyState.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Common/Locale/LocaleExtension.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Common/Locale/LocaleManager.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Input/AvaloniaKeyboard.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Input/AvaloniaKeyboardDriver.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Input/AvaloniaKeyboardMappingHelper.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Input/AvaloniaMouse.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Input/AvaloniaMouseDriver.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Modules/Updater/Updater.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Program.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Ryujinx.Ava.csproj (93%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/Ryujinx.ico (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Applet/AvaHostUiHandler.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Applet/AvaloniaDynamicTextInputHandler.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Applet/AvaloniaHostUiTheme.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Applet/ErrorAppletWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Applet/ErrorAppletWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Applet/SwkbdAppletDialog.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Applet/SwkbdAppletDialog.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/GameGridView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/GameGridView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/GameListView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/GameListView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/NavigationDialogHost.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/NavigationDialogHost.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/UpdateWaitWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Controls/UpdateWaitWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/ApplicationOpenedEventArgs.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/BitmapArrayValueConverter.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/ButtonKeyAssigner.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/ContentDialogHelper.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/Glyph.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/GlyphValueConverter.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/HotKeyControl.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/KeyValueConverter.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/LoggerAdapter.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/MiniCommand.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/NotificationHelper.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/OffscreenTextBox.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/UserErrorDialog.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/UserResult.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Helpers/Win32NativeInterop.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/CheatModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/CheatsList.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/ControllerModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/DeviceType.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/DownloadableContentModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/Generic/LastPlayedSortComparer.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/InputConfiguration.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/PlayerModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/ProfileImageModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/SaveModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/StatusUpdatedEventArgs.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/TempProfile.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/TimeZone.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/TitleUpdateModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Models/UserProfile.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Renderer/EmbeddedWindow.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Renderer/EmbeddedWindowOpenGL.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Renderer/EmbeddedWindowVulkan.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Renderer/OpenTKBindingsContext.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Renderer/RendererHost.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Renderer/RendererHost.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Renderer/SPBOpenGLContext.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/AboutWindowViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/AmiiboWindowViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/AvatarProfileViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/BaseModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/ControllerSettingsViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/DownloadableContentManagerViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/MainWindowViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/SettingsViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/TitleUpdateViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/UserProfileImageSelectorViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/UserProfileViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/ViewModels/UserSaveManagerViewModel.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Main/MainMenuBarView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Main/MainMenuBarView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Main/MainStatusBarView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Main/MainStatusBarView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Main/MainViewControls.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Main/MainViewControls.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsAudioView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsAudioView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsCPUView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsCPUView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsGraphicsView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsGraphicsView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsHotkeysView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsHotkeysView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsInputView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsInputView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsLoggingView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsLoggingView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsNetworkView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsNetworkView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsSystemView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsSystemView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsUIView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/Settings/SettingsUIView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserEditorView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserEditorView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserFirmwareAvatarSelectorView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserProfileImageSelectorView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserProfileImageSelectorView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserRecovererView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserRecovererView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserSaveManagerView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserSaveManagerView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserSelectorView.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Views/User/UserSelectorView.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/AboutWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/AboutWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/AmiiboWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/AmiiboWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/CheatWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/CheatWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/ContentDialogOverlayWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/ContentDialogOverlayWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/ControllerSettingsWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/ControllerSettingsWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/DownloadableContentManagerWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/DownloadableContentManagerWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/IconColorPicker.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/MainWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/MainWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/MotionSettingsWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/MotionSettingsWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/RumbleSettingsWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/RumbleSettingsWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/SettingsWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/SettingsWindow.axaml.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/StyleableWindow.cs (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/TitleUpdateWindow.axaml (100%) rename {Ryujinx.Ava => src/Ryujinx.Ava}/UI/Windows/TitleUpdateWindow.axaml.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/AsyncWorkQueue.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Collections/IntervalTree.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Collections/IntrusiveRedBlackTree.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Collections/IntrusiveRedBlackTreeImpl.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Collections/IntrusiveRedBlackTreeNode.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Collections/TreeDictionary.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/AntiAliasing.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/AppDataManager.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/AspectRatioExtensions.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/BackendThreading.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/DownloadableContentContainer.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/DownloadableContentJsonSerializerContext.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/DownloadableContentNca.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/GraphicsBackend.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/GraphicsDebugLevel.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/GamepadInputId.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/GenericControllerInputConfig.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/JoyconConfigControllerStick.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/Motion/MotionConfigController.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/RumbleConfigController.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/StandardControllerInputConfig.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Controller/StickInputId.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/ControllerType.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/GenericInputConfigurationCommon.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/InputBackendType.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/InputConfig.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/InputConfigJsonSerializerContext.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/JsonInputConfigConverter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Key.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/KeyboardHotkeys.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/LeftJoyconCommonConfig.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/PlayerIndex.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/Hid/RightJoyconCommonConfig.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/MemoryManagerMode.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/ScalingFilter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/TitleUpdateMetadata.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Configuration/TitleUpdateMetadataJsonSerializerContext.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Extensions/BinaryReaderExtensions.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Extensions/BinaryWriterExtensions.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Extensions/StreamExtensions.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/GraphicsDriver/DriverUtilities.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/GraphicsDriver/NVAPI/Nvapi.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/GraphicsDriver/NVAPI/NvapiUnicodeString.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/GraphicsDriver/NVAPI/NvdrsProfile.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/GraphicsDriver/NVAPI/NvdrsSetting.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/GraphicsDriver/NVThreadedOptimization.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Hash128.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Formatters/DefaultLogFormatter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Formatters/DynamicObjectFormatter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Formatters/ILogFormatter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/LogClass.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/LogEventArgs.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/LogEventArgsJson.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/LogEventJsonSerializerContext.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/LogLevel.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Logger.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Targets/AsyncLogTargetWrapper.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Targets/ConsoleLogTarget.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Targets/FileLogTarget.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Targets/ILogTarget.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Logging/Targets/JsonLogTarget.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/ArrayPtr.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/Box.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/ByteMemoryPool.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/IArray.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/MemoryStreamManager.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/PartialUnmaps/NativeReaderWriterLock.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/PartialUnmaps/PartialUnmapHelpers.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/PartialUnmaps/PartialUnmapState.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/PartialUnmaps/ThreadLocalMap.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/Ptr.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/SpanOrArray.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/SpanReader.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/SpanWriter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/StructArrayHelpers.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Memory/StructByteArrayHelpers.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/PerformanceCounter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Pools/ObjectPool.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Pools/SharedPools.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Pools/ThreadStaticArray.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/ReactiveObject.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/ReferenceEqualityComparer.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/ReleaseInformation.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Ryujinx.Common.csproj (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInfo/LinuxSystemInfo.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInfo/MacOSSystemInfo.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInfo/SystemInfo.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInfo/WindowsSystemInfo.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInterop/DisplaySleep.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInterop/ForceDpiAware.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInterop/GdiPlusHelper.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInterop/StdErrAdapter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/SystemInterop/WindowsMultimediaTimerResolution.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/BitUtils.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/BitfieldExtensions.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/Buffers.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/CommonJsonContext.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/EmbeddedResources.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/HexUtils.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/JsonHelper.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/MessagePackObjectFormatter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/NetworkHelpers.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/SpanHelpers.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/StreamUtils.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/TypedStringEnumConverter.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/Utilities/UInt128Utils.cs (100%) rename {Ryujinx.Common => src/Ryujinx.Common}/XXHash128.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AddressSpace.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/Arm/ApFlags.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/Arm/ExceptionClass.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/DummyDiskCacheLoadState.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvAddressSpace.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvAddressSpaceRange.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvApi.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvCpuContext.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvEngine.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvExecutionContext.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvExecutionContextShadow.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvExecutionContextVcpu.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvIpaAllocator.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvMemoryBlockAllocation.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvMemoryBlockAllocator.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvMemoryManager.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvVcpu.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvVcpuPool.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/HvVm.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/AppleHv/IHvExecutionContext.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/ExceptionCallbacks.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/ICpuContext.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/ICpuEngine.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/IDiskCacheState.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/IExecutionContext.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/ITickSource.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/IVirtualMemoryManagerTracked.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/JitCpuContext.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/JitDiskCacheLoadState.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/JitEngine.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/JitExecutionContext.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/JitMemoryAllocator.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/JitMemoryBlock.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/MemoryManager.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Jit/MemoryManagerHostMapped.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/LoadState.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/MemoryEhMeilleure.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/MemoryHelper.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/MemoryManagerBase.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/PrivateMemoryAllocation.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/PrivateMemoryAllocator.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Ryujinx.Cpu.csproj (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/TickSource.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Tracking/CpuMultiRegionHandle.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Tracking/CpuRegionHandle.cs (100%) rename {Ryujinx.Cpu => src/Ryujinx.Cpu}/Tracking/CpuSmartMultiRegionHandle.cs (100%) rename {Ryujinx.Graphics.Device => src/Ryujinx.Graphics.Device}/DeviceState.cs (100%) rename {Ryujinx.Graphics.Device => src/Ryujinx.Graphics.Device}/IDeviceState.cs (100%) rename {Ryujinx.Graphics.Device => src/Ryujinx.Graphics.Device}/IDeviceStateWithContext.cs (100%) rename {Ryujinx.Graphics.Device => src/Ryujinx.Graphics.Device}/RwCallback.cs (100%) rename {Ryujinx.Graphics.Device => src/Ryujinx.Graphics.Device}/Ryujinx.Graphics.Device.csproj (100%) rename {Ryujinx.Graphics.Device => src/Ryujinx.Graphics.Device}/SizeCalculator.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/AddressMode.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/AdvancedBlendDescriptor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/AdvancedBlendOp.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/AdvancedBlendOverlap.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/AntiAliasing.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/BlendDescriptor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/BlendFactor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/BlendOp.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/BufferAssignment.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/BufferHandle.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/BufferRange.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Capabilities.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ColorF.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/CompareMode.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/CompareOp.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/CounterType.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/DepthMode.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/DepthStencilMode.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/DepthTestDescriptor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/DeviceInfo.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Extents2D.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Extents2DF.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Face.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Format.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/FrontFace.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/HardwareInfo.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ICounterEvent.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/IPipeline.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/IProgram.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/IRenderer.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ISampler.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ITexture.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/IWindow.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ImageCrop.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/IndexType.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/LogicalOp.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/MagFilter.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/MinFilter.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/MultisampleDescriptor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/BufferMap.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/CommandHelper.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/CommandType.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/BarrierCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/BeginTransformFeedbackCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Buffer/BufferDisposeCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Buffer/BufferGetDataCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Buffer/BufferSetDataCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/ClearBufferCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/ClearRenderTargetColorCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/CommandBufferBarrierCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/CopyBufferCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DispatchComputeCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DrawCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DrawIndexedCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DrawIndexedIndirectCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DrawIndirectCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DrawIndirectCountCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/DrawTextureCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/EndHostConditionalRenderingCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/EndTransformFeedbackCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/IGALCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Program/ProgramDisposeCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/ActionCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/CreateBufferCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/CreateProgramCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/CreateSamplerCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/CreateSyncCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/CreateTextureCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/PreFrameCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/ReportCounterCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/ResetCounterCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Renderer/UpdateCountersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetAlphaTestCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetBlendStateAdvancedCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetBlendStateCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetDepthBiasCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetDepthClampCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetDepthModeCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetDepthTestCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetFaceCullingCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetFrontFaceCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetImageCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetIndexBufferCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetLineParametersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetLogicOpStateCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetMultisampleStateCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetPatchParametersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetPointParametersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetPolygonModeCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetPrimitiveRestartCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetPrimitiveTopologyCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetProgramCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetRasterizerDiscardCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetRenderTargetScaleCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetRenderTargetsCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetScissorsCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetStencilTestCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetStorageBuffersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetTextureAndSamplerCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetUniformBuffersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetUserClipDistanceCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetVertexAttribsCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetVertexBuffersCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/SetViewportsCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureCopyToCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureCreateViewCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureGetDataCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureReleaseCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureSetDataCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Texture/TextureSetStorageCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/TextureBarrierCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/TextureBarrierTiledCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/TryHostConditionalRenderingCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/UpdateRenderScaleCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Commands/Window/WindowPresentCommand.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Model/CircularSpanPool.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Model/ResultBox.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Model/SpanRef.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Model/TableRef.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/ProgramQueue.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/Programs/BinaryProgramRequest.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/Programs/IProgramRequest.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/Programs/SourceProgramRequest.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/ThreadedCounterEvent.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/ThreadedProgram.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/ThreadedSampler.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/Resources/ThreadedTexture.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/SyncMap.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/ThreadedHelpers.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/ThreadedPipeline.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/ThreadedRenderer.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Multithreading/ThreadedWindow.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Origin.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/PinnedSpan.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/PolygonMode.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/PolygonModeMask.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/PrimitiveTopology.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ProgramLinkStatus.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ProgramPipelineState.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Rectangle.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Ryujinx.Graphics.GAL.csproj (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/SamplerCreateInfo.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ScreenCaptureImageInfo.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ShaderBindings.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ShaderInfo.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ShaderSource.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/StencilOp.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/StencilTestDescriptor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/SupportBufferUpdater.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/SwizzleComponent.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Target.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/TextureCreateInfo.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/TextureReleaseCallback.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/UpscaleType.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/VertexAttribDescriptor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/VertexBufferDescriptor.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/Viewport.cs (100%) rename {Ryujinx.Graphics.GAL => src/Ryujinx.Graphics.GAL}/ViewportSwizzle.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/ClassId.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Constants.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Compute/ComputeClass.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Compute/ComputeClassState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Compute/ComputeQmd.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/ConditionalRenderEnabled.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/DeviceStateWithShadow.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Dma/DmaClass.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Dma/DmaClassState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Dma/DmaTexture.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/GPFifo/CompressedMethod.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/GPFifo/GPEntry.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/GPFifo/GPFifoClass.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/GPFifo/GPFifoClassState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/GPFifo/GPFifoDevice.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/GPFifo/GPFifoProcessor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/InlineToMemory/InlineToMemoryClass.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/InlineToMemory/InlineToMemoryClassState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/AluOperation.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/AluRegOperation.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/AssignmentOperation.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/IMacroEE.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/Macro.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/MacroHLE.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/MacroHLEFunctionName.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/MacroHLETable.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/MacroInterpreter.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/MacroJit.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/MacroJitCompiler.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MME/MacroJitContext.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/MmeShadowScratch.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/SetMmeShadowRamControlMode.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/ShaderTexture.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/Blender/AdvancedBlendFunctions.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/Blender/AdvancedBlendManager.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/Blender/AdvancedBlendUcode.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/Blender/UcodeAssembler.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/ConditionalRendering.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/ConstantBufferUpdater.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/DrawManager.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/DrawState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/IbStreamer.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/IndirectDrawType.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/RenderTargetUpdateFlags.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/SemaphoreUpdater.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/SpecializationStateUpdater.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/StateUpdateTracker.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/StateUpdater.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/ThreedClass.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Threed/ThreedClassState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Twod/TwodClass.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Twod/TwodClassState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Twod/TwodTexture.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/Boolean32.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/ColorFormat.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/GpuVa.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/MemoryLayout.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/PrimitiveType.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/SamplerIndex.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/SbDescriptor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Engine/Types/ZetaFormat.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/GpuChannel.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/GpuContext.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/GraphicsConfig.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/AutoDeleteCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/FormatInfo.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/FormatTable.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/ITextureDescriptor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/Pool.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/PoolCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/ReductionFilter.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/Sampler.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/SamplerDescriptor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/SamplerMinFilter.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/SamplerMipFilter.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/SamplerPool.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/SamplerPoolCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/Texture.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureBindingInfo.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureBindingsManager.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureCompatibility.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureComponent.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureDependency.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureDescriptor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureDescriptorType.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureGroup.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureGroupHandle.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureInfo.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureManager.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureMatchQuality.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureMsaaMode.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TexturePool.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TexturePoolCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureScaleMode.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureSearchFlags.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureTarget.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Image/TextureViewCompatibility.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/Buffer.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/BufferBounds.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/BufferCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/BufferCacheEntry.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/BufferManager.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/BufferMigration.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/BufferModifiedRangeList.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/BufferTextureBinding.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/CounterCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/GpuRegionHandle.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/IndexBuffer.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/MemoryManager.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/MultiRangeWritableBlock.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/PhysicalMemory.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/PteKind.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/ResourceKind.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/UnmapEventArgs.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Memory/VertexBuffer.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Ryujinx.Graphics.Gpu.csproj (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/CachedShaderBindings.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/CachedShaderProgram.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/CachedShaderStage.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ComputeShaderCacheHashTable.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/BackgroundDiskCacheWriter.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/BinarySerializer.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/CompressionAlgorithm.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/DiskCacheCommon.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/DiskCacheGpuAccessor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/DiskCacheGuestStorage.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/DiskCacheHostStorage.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/DiskCacheLoadException.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/DiskCacheLoadResult.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/DiskCacheOutputStreams.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/GuestCodeAndCbData.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/ParallelDiskCacheLoader.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/DiskCache/ShaderBinarySerializer.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/GpuAccessor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/GpuAccessorBase.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/GpuAccessorState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/GpuChannelComputeState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/GpuChannelGraphicsState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/GpuChannelPoolState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/HashTable/HashState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/HashTable/IDataAccessor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/HashTable/PartitionHashTable.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/HashTable/PartitionedHashTable.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/HashTable/SmartDataAccessor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ResourceCounts.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderAddresses.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderCache.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderCacheHashTable.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderCacheState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderCodeAccessor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderDumpPaths.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderDumper.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderSpecializationList.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/ShaderSpecializationState.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Shader/TransformFeedbackDescriptor.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Synchronization/SynchronizationManager.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Synchronization/Syncpoint.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Synchronization/SyncpointWaiterHandle.cs (100%) rename {Ryujinx.Graphics.Gpu => src/Ryujinx.Graphics.Gpu}/Window.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/ClassId.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/Devices.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/Host1xClass.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/Host1xClassRegisters.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/Host1xDevice.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/OpCode.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/Ryujinx.Graphics.Host1x.csproj (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/SyncptIncrManager.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/ThiDevice.cs (100%) rename {Ryujinx.Graphics.Host1x => src/Ryujinx.Graphics.Host1x}/ThiRegisters.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/FFmpegContext.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/H264/Decoder.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/H264/H264BitStreamWriter.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/H264/SpsAndPpsReconstruction.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVCodec.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVCodec501.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVCodecContext.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVCodecID.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVFrame.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVLog.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVPacket.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/AVRational.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/FFCodec.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/FFCodecLegacy.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Native/FFmpegApi.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Ryujinx.Graphics.Nvdec.FFmpeg.csproj (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Surface.cs (100%) rename {Ryujinx.Graphics.Nvdec.FFmpeg => src/Ryujinx.Graphics.Nvdec.FFmpeg}/Vp8/Decoder.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/BitDepth.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/CodecErr.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Common/BitUtils.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Common/MemoryAllocator.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Common/MemoryUtil.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Constants.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/DecodeFrame.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/DecodeMv.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Decoder.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Detokenize.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Dsp/Convolve.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Dsp/Filter.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Dsp/IntraPred.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Dsp/InvTxfm.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Dsp/Prob.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Dsp/Reader.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Dsp/TxfmCommon.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Idct.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/InternalErrorException.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/InternalErrorInfo.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/LoopFilter.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Luts.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/PredCommon.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/QuantCommon.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/ReconInter.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/ReconIntra.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Ryujinx.Graphics.Nvdec.Vp9.csproj (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/TileBuffer.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/TileWorkerData.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/BModeInfo.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/BlockSize.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/Buf2D.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/FrameType.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/LoopFilter.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/LoopFilterInfoN.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/LoopFilterMask.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/LoopFilterThresh.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/MacroBlockD.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/MacroBlockDPlane.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/ModeInfo.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/MotionVectorContext.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/Mv.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/Mv32.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/MvClassType.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/MvJointType.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/MvRef.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/PartitionType.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/PlaneType.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/Position.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/PredictionMode.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/RefBuffer.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/ReferenceMode.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/ScaleFactors.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/SegLvlFeatures.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/Segmentation.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/Surface.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/TileInfo.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/TxMode.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/TxSize.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/TxType.cs (100%) rename {Ryujinx.Graphics.Nvdec.Vp9 => src/Ryujinx.Graphics.Nvdec.Vp9}/Types/Vp9Common.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/ApplicationId.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/H264Decoder.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Image/SurfaceCache.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Image/SurfaceCommon.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Image/SurfaceReader.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Image/SurfaceWriter.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/MemoryExtensions.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/NvdecDecoderContext.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/NvdecDevice.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/NvdecRegisters.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/NvdecStatus.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/ResourceManager.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Ryujinx.Graphics.Nvdec.csproj (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/H264/PictureInfo.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/H264/ReferenceFrame.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp8/PictureInfo.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/BackwardUpdates.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/EntropyProbs.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/FrameFlags.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/FrameSize.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/FrameStats.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/LoopFilter.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/PictureInfo.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Types/Vp9/Segmentation.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Vp8Decoder.cs (100%) rename {Ryujinx.Graphics.Nvdec => src/Ryujinx.Graphics.Nvdec}/Vp9Decoder.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/BackgroundContextWorker.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Buffer.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Constants.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Debugger.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/DrawTextureEmulation.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/FsrScalingFilter.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/FxaaPostProcessingEffect.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/IPostProcessingEffect.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/IScalingFilter.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/ShaderHelper.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/ffx_a.h (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/ffx_fsr1.h (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/fsr_scaling.glsl (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/fsr_sharpening.glsl (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/fxaa.glsl (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/smaa.hlsl (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/smaa_blend.glsl (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/smaa_edge.glsl (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Shaders/smaa_neighbour.glsl (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/SmaaPostProcessingEffect.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Textures/SmaaAreaTexture.bin (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Effects/Textures/SmaaSearchTexture.bin (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/EnumConversion.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/FormatInfo.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/FormatTable.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Framebuffer.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Handle.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Helper/GLXHelper.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Helper/WGLHelper.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/HwCapabilities.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/IOpenGLContext.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/FormatConverter.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/ITextureInfo.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/IntermmediatePool.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/Sampler.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/TextureBase.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/TextureBuffer.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/TextureCopy.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/TextureCopyIncompatible.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/TextureCopyMS.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/TextureStorage.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Image/TextureView.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/OpenGLRenderer.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/PersistentBuffers.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Pipeline.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Program.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Queries/BufferedQuery.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Queries/CounterQueue.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Queries/CounterQueueEvent.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Queries/Counters.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/ResourcePool.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Ryujinx.Graphics.OpenGL.csproj (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Sync.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/VertexArray.cs (100%) rename {Ryujinx.Graphics.OpenGL => src/Ryujinx.Graphics.OpenGL}/Window.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/AlphaTestOp.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/AttributeType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/BufferDescriptor.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/BufferUsageFlags.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/CodeGenContext.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Declarations.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/DefaultNames.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/GlslGenerator.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/Shuffle.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGen.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGenBallot.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGenCall.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGenFSI.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGenHelper.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGenMemory.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGenPacking.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstGenVector.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstInfo.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/InstType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/Instructions/IoMap.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/NumberFormatter.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/OperandManager.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Glsl/TypeConversion.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/CodeGenContext.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/Declarations.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/EnumConversion.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/Instructions.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/IoMap.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/OperationResult.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/ScalingHelpers.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/SpirvDelegates.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/SpirvGenerator.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/CodeGen/Spirv/TextureMeta.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Constants.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/Block.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/DecodedFunction.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/DecodedProgram.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/Decoder.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/FunctionType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/InstDecoders.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/InstName.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/InstOp.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/InstProps.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/InstTable.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/Register.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/RegisterConsts.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Decoders/RegisterType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IGpuAccessor.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/InputTopology.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/AttributeMap.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmit.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitAluHelper.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitAttribute.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitBarrier.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitBitfield.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitConditionCode.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitConversion.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitFloatArithmetic.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitFloatComparison.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitFloatMinMax.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitFlowControl.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitHelper.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitIntegerArithmetic.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitIntegerComparison.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitIntegerLogical.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitIntegerMinMax.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitMemory.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitMove.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitMultifunction.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitNop.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitPredicate.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitShift.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitSurface.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitTexture.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitVideoArithmetic.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitVideoMinMax.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitWarp.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/InstEmitter.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Instructions/Lop3Expression.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/BasicBlock.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/CommentNode.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/Function.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/INode.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/Instruction.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/IoVariable.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/IrConsts.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/Operand.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/OperandHelper.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/OperandType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/Operation.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/PhiNode.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/StorageKind.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/TextureFlags.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/IntermediateRepresentation/TextureOperation.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/OutputTopology.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Ryujinx.Graphics.Shader.csproj (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/SamplerType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/ShaderIdentification.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/ShaderProgram.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/ShaderProgramInfo.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/ShaderStage.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstAssignment.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstBlock.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstBlockType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstBlockVisitor.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstComment.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstHelper.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstNode.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstOperand.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstOperation.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstOptimizer.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/AstTextureOperation.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/GotoElimination.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/GotoStatement.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/HelperFunctionsMask.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/IAstNode.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/InstructionInfo.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/IoDefinition.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/OperandInfo.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/PhiFunctions.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/StructuredFunction.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/StructuredProgram.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/StructuredProgramContext.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/StructuredIr/StructuredProgramInfo.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/SupportBuffer.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/TessPatchType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/TessSpacing.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/TextureDescriptor.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/TextureFormat.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/TextureHandle.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/TextureUsageFlags.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/AggregateType.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/AttributeConsts.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/ControlFlowGraph.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Dominance.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/EmitterContext.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/EmitterContextInsts.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/FeatureFlags.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/FunctionMatch.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/GlobalMemory.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/BindlessElimination.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/BindlessToIndexed.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/BranchElimination.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/ConstantFolding.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/GlobalToStorage.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/Optimizer.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/Simplification.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Optimizations/Utils.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/RegisterUsage.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Rewriter.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/ShaderConfig.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/ShaderHeader.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/ShaderIdentifier.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Ssa.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/TargetApi.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/TargetLanguage.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/TranslationFlags.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/TranslationOptions.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/Translator.cs (100%) rename {Ryujinx.Graphics.Shader => src/Ryujinx.Graphics.Shader}/Translation/TranslatorContext.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/AstcDecoder.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/AstcDecoderException.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/AstcPixel.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/BitStream128.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/Bits.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/EndPointSet.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/IntegerEncoded.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Astc/IntegerSequence.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/BC6Decoder.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/BC7Decoder.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/BCnDecoder.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/BCnEncoder.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/BlockLinearConstants.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/BlockLinearLayout.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Bpp12Pixel.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/ETC2Decoder.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Encoders/BC7Encoder.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Encoders/EncodeMode.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/LayoutConverter.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/OffsetCalculator.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/PixelConverter.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Region.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Ryujinx.Graphics.Texture.csproj (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Size.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/SizeCalculator.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/SizeInfo.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Utils/BC67Tables.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Utils/BC67Utils.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Utils/BC7ModeInfo.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Utils/Block.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Utils/RgbaColor32.cs (100%) rename {Ryujinx.Graphics.Texture => src/Ryujinx.Graphics.Texture}/Utils/RgbaColor8.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Blender.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Image/BufferPool.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Image/InputSurface.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Image/Pixel.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Image/Surface.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Image/SurfaceCommon.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Image/SurfaceReader.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Image/SurfaceWriter.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Rectangle.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/ResourceManager.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Ryujinx.Graphics.Vic.csproj (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Scaler.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/BlendingSlotStruct.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/ClearRectStruct.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/ConfigStruct.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/DeinterlaceMode.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/FrameFormat.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/LumaKeyStruct.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/MatrixStruct.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/OutputConfig.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/OutputSurfaceConfig.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/PipeConfig.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/PixelFormat.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/SlotConfig.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/SlotStruct.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/Types/SlotSurfaceConfig.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/VicDevice.cs (100%) rename {Ryujinx.Graphics.Vic => src/Ryujinx.Graphics.Vic}/VicRegisters.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/FrameField.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/H264PictureInfo.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/IDecoder.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/IH264Decoder.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/ISurface.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/IVp9Decoder.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Plane.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Ryujinx.Graphics.Video.csproj (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Vp8PictureInfo.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Vp9BackwardUpdates.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Vp9EntropyProbs.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Vp9Mv.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Vp9MvRef.cs (100%) rename {Ryujinx.Graphics.Video => src/Ryujinx.Graphics.Video}/Vp9PictureInfo.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Auto.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/AutoFlushCounter.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/BackgroundResources.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/BitMap.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/BufferAllocationType.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/BufferHolder.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/BufferManager.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/BufferState.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/BufferUsageBitmap.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/CacheByRange.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/CommandBufferPool.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/CommandBufferScoped.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Constants.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DescriptorSetCollection.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DescriptorSetManager.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DescriptorSetUpdater.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableBuffer.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableBufferView.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableFramebuffer.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableImage.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableImageView.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableMemory.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposablePipeline.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableRenderPass.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/DisposableSampler.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/FsrScalingFilter.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/FxaaPostProcessingEffect.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/IPostProcessingEffect.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/IScalingFilter.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/FsrScaling.glsl (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/FsrScaling.spv (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/FsrSharpening.glsl (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/FsrSharpening.spv (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/Fxaa.glsl (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/Fxaa.spv (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/SmaaBlend.glsl (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/SmaaBlend.spv (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/SmaaEdge.glsl (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/SmaaEdge.spv (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/SmaaNeighbour.glsl (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Shaders/SmaaNeighbour.spv (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/SmaaConstants.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/SmaaPostProcessingEffect.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Textures/SmaaAreaTexture.bin (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Effects/Textures/SmaaSearchTexture.bin (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/EnumConversion.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/FenceHelper.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/FenceHolder.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/FormatCapabilities.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/FormatConverter.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/FormatTable.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/FramebufferParams.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/HardwareCapabilities.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/HashTableSlim.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/HelperShader.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/IdList.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/IndexBufferPattern.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/IndexBufferState.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/MemoryAllocation.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/MemoryAllocator.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/MemoryAllocatorBlockList.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/MoltenVK/MVKConfiguration.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/MoltenVK/MVKInitialization.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/MultiFenceHolder.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/NativeArray.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PersistentFlushBuffer.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineBase.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineConverter.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineDynamicState.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineFull.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineHelperShader.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineLayoutCache.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineLayoutCacheEntry.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineLayoutFactory.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineState.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/PipelineUid.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Queries/BufferedQuery.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Queries/CounterQueue.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Queries/CounterQueueEvent.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Queries/Counters.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Ryujinx.Graphics.Vulkan.csproj (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/SamplerHolder.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/SemaphoreHolder.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shader.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/ShaderCollection.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ChangeBufferStrideShaderSource.comp (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorBlitClearAlphaFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorBlitFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorBlitMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorBlitVertexShaderSource.vert (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorClearFFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorClearSIFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorClearUIFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorClearVertexShaderSource.vert (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorCopyShorteningComputeShaderSource.comp (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorCopyToNonMsComputeShaderSource.comp (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorCopyWideningComputeShaderSource.comp (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorDrawToMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ColorDrawToMsVertexShaderSource.vert (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ConvertIndexBufferShaderSource.comp (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ConvertIndirectDataShaderSource.comp (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/DepthBlitFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/DepthBlitMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/DepthDrawToMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/DepthDrawToNonMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/ShaderBinaries.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/StencilBlitFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/StencilBlitMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/StencilDrawToMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Shaders/StencilDrawToNonMsFragmentShaderSource.frag (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/SpecInfo.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/StagingBuffer.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/SyncManager.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/TextureBuffer.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/TextureCopy.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/TextureStorage.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/TextureView.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Vendor.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VertexBufferState.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VulkanConfiguration.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VulkanDebugMessenger.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VulkanException.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VulkanInitialization.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VulkanInstance.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VulkanPhysicalDevice.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/VulkanRenderer.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/Window.cs (100%) rename {Ryujinx.Graphics.Vulkan => src/Ryujinx.Graphics.Vulkan}/WindowBase.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/AssemblyInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/GuestBrokeExecutionException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/InternalServiceException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/InvalidFirmwarePackageException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/InvalidNpdmException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/InvalidStructLayoutException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/InvalidSystemResourceException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/ServiceNotImplementedException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/TamperCompilationException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/TamperExecutionException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Exceptions/UndefinedInstructionException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/FileSystem/ContentManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/FileSystem/ContentPath.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/FileSystem/EncryptedFileSystemCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/FileSystem/LocationEntry.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/FileSystem/SystemVersion.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/FileSystem/VirtualFileSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HLEConfiguration.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/AppletManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/BootDisplayKind.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/BrowserApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/BrowserArgument.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/BrowserOutput.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/BrowserOutputType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/DocumentKind.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/LeftStickMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/ShimKind.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/WebArgHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/WebArgTLV.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/WebArgTLVType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/WebCommonReturnValue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Browser/WebExitReason.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/CommonArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerAppletUiArgs.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerSupportArgHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerSupportArgPrivate.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerSupportArgV7.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerSupportArgVPre7.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerSupportMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Controller/ControllerSupportResultInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Error/ApplicationErrorArg.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Error/ErrorApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Error/ErrorCommonArg.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Error/ErrorCommonHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/Error/ErrorType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/IApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/PlayerSelect/PlayerSelectResult.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InlineResponses.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InputFormMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/KeyboardInputMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/PasswordMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.png (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.svg (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.png (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.svg (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.png (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.svg (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/Resources/Logo_Ryujinx.png (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalcEx.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardInitialize.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/TRef.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Applets/SoftwareKeyboard/TimedAction.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/ArmProcessContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/ArmProcessContextFactory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ArrayType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/BaseNode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/CallExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/CastExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/DtorName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/FunctionType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/LocalName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/NameType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/NestedName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/NewExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/NodeArray.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ParentNode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/PointerType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/Qualifier.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/SpecialName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Diagnostics/Demangler/Demangler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/HomebrewRomFsStream.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Horizon.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/IdDictionary.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/IpcBuffDesc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/IpcHandleDesc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/IpcMagic.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/IpcMessage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/IpcMessageType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/IpcPtrBuffDesc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/IpcRecvListBuffDesc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Ipc/ServiceProcessRequest.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/IKFutureSchedulerObject.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/KAutoObject.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/KResourceLimit.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/KSynchronizationObject.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/KSystemControl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/KTimeManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/KernelInit.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/KernelTransfer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/LimitableResource.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/MemoryArrange.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/MemroySize.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Common/MersenneTwister.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/ChannelState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KBufferDescriptor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KBufferDescriptorTable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KClientPort.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KClientSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KLightClientSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KLightServerSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KLightSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KPort.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KServerPort.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KServerSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Ipc/KSessionRequest.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/KernelConstants.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/KernelContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/KernelStatic.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/AddressSpaceType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/DramMemoryMap.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KCodeMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KMemoryBlock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KMemoryBlockManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KMemoryInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KMemoryManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KMemoryRegionManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KPageBitmap.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KPageHeap.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KPageList.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KPageNode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KPageTable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KPageTableBase.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KScopedPageList.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KSharedMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KSlabHeap.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/KTransferMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/MemoryAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/MemoryFillValue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/MemoryPermission.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/MemoryRegion.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/MemoryState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Memory/SharedMemoryStorage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/CapabilityExtensions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/CapabilityType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/HleProcessDebugger.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/IProcessContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/IProcessContextFactory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/KContextIdManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/KHandleEntry.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/KHandleTable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/KProcess.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/KProcessCapabilities.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/KTlsPageInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/KTlsPageManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/ProcessContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/ProcessContextFactory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/ProcessCreationFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/ProcessCreationInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/ProcessExecutionContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/ProcessState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Process/ProcessTamperInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/InfoType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/MemoryInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/PointerSizedAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/SvcAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/SvcImplAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/Syscall.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/SyscallHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/SupervisorCall/ThreadContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/ArbitrationType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KAddressArbiter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KConditionVariable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KCriticalSection.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KEvent.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KPriorityQueue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KReadableEvent.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KScheduler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KSynchronization.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KThread.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KThreadContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/KWritableEvent.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/SignalType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/ThreadSchedState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Kernel/Threading/ThreadType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/LibHacHorizonManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/ModLoader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/ServiceCtx.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountSaveDataManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountService/IProfile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountService/ManagerServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AccountService/ProfileServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/ApplicationServiceServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/DefaultUserImage.jpg (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/IAccountServiceForApplication.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/IAsyncContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/Types/AccountState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/Types/ProfilesJson.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/Types/UserId.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/Types/UserProfile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Acc/Types/UserProfileJson.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/Dauth/IService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Account/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AppletFifo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/AppletSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/IAppletFifo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/IStorage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/IStorageAccessor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/Types/AppletId.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/AppletOE/IApplicationProxyService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/Idle/IPolicyManagerSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/Omm/IOperationModeManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/Spsm/IPowerStateInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Am/Tcap/IManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/IManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/IManagerPrivileged.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/ISession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/ISystemManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/ManagerServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/PerformanceState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/SessionServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/SystemManagerServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/Types/CpuBoostMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/Types/PerformanceConfiguration.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Apm/Types/PerformanceMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Arp/ApplicationLaunchProperty.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Arp/IReader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Arp/IWriter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Arp/LibHacIReader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioIn/AudioIn.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioIn/AudioInServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioIn/IAudioIn.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioInManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioInManagerServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioOut/AudioOut.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioOut/AudioOutServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioOut/IAudioOut.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioOutManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioOutManagerServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRenderer/AudioDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRendererManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/AudioRendererManagerServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioInManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioInManagerForApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioInManagerForDebugger.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioOutManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioOutManagerForApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioOutManagerForDebugger.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioRendererManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioRendererManagerForApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IAudioSnoopManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IFinalOutputRecorderManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/IHardwareOpusDecoderManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/Types/OpusDecoderFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/Types/OpusPacketHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Audio/Types/OpusParametersEx.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/IServiceCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/ServiceCreator/IBcatService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bgtc/IStateControlService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bgtc/ITaskService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bluetooth/IBluetoothDriver.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Bluetooth/IBluetoothUser.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/BluetoothManager/IBtm.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/BluetoothManager/IBtmDebug.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/BluetoothManager/IBtmSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/BluetoothManager/IBtmUser.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/BluetoothManager/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/CaptureManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/IAlbumAccessorService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/IAlbumApplicationService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/IAlbumControlService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/IScreenShotApplicationService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/IScreenShotControlService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/IScreenshotService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/Types/AlbumFileDateTime.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/Types/AlbumImageOrientation.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/Types/AlbumStorage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/Types/ContentType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Caps/Types/ScreenShotAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Cec/ICecManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/CommandCmifAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/CommandTIpcAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/DisposableIpcService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/DummyService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ectx/IReaderForSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ectx/IWriterForApplication.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ectx/IWriterForSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Erpt/IContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Erpt/ISession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Es/IETicketService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Eupld/IControl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Eupld/IRequest.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fatal/IPrivateService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fatal/IService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fatal/Types/CpuContext32.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fatal/Types/CpuContext64.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fatal/Types/FatalPolicy.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/IServiceCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/IFriendService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/INotificationService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/FileSystemProxy/IDirectory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/FileSystemProxy/IFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/FileSystemProxy/IStorage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/IDeviceOperator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/IFileSystemProxy.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/IFileSystemProxyForLoader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/IMultiCommitManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/IProgramRegistry.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/ISaveDataInfoReader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Fs/Types/FileSystemType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Grc/IGrcService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Grc/IRemoteVideoTransfer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Hid.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/BaseDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/DebugPadDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/KeyboardDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/MouseDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/NpadDevices.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/TouchDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/HidUtils.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/IAppletResource.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/IHidDebugServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/IHidServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/IHidSystemServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/IHidbusServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/ISystemServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/IIrSensorServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/AppletFooterUiType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/HidVector.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/Npad/ControllerKeys.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/Npad/ControllerType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/Npad/NpadColor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/Npad/NpadIdType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/Npad/PlayerIndex.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/NpadJoyHoldType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ins/IReceiverManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ins/ISenderManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/IpcService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Lbl/ILblController.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Lbl/LblControllerServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/IMonitorServiceCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/ISystemServiceCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/IUserServiceCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/Lp2p/IServiceCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/NetworkInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/Types/NetworkState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Loader/IDebugMonitorInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Loader/IProcessManagerInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Loader/IShellInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Loader/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mig/IService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/DatabaseImpl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/DatabaseSessionMetadata.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Helper.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/IImageDatabaseService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/IStaticService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/MiiDatabaseManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/StaticService/IDatabaseService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/Age.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/BeardType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/CharInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/CharInfoElement.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/CommonColor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/CoreData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/CreateId.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/DefaultMii.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/EyeType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/EyebrowType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/FacelineColor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/FacelineMake.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/FacelineType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/FacelineWrinkle.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/FontRegion.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/Gender.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/GlassType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/HairFlip.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/HairType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/IElement.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/IStoredData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/MoleType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/MouthType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/MustacheType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/Nickname.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/NoseType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/Race.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/RandomMiiConstants.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/Source.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/SourceFlag.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/StoreData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/StoreDataElement.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/Types/Ver3StoreData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mii/UtilityImpl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mm/IRequest.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mm/Types/MultiMediaOperationType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mm/Types/MultiMediaSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mnpp/IServiceForApplication.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Mnpp/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ncm/IContentManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ncm/Lr/ILocationResolverManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ncm/Lr/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/News/IServiceCreator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/IAmManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/ISystemManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/IUserManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Mifare/IUserManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/NfcManager/INfc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/NfcManager/Types/State.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/IDebugManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/ISystemManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/IUserManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ngct/IService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ngct/IServiceWithManagementApi.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ngct/NgctServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/IStaticService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/IGeneralService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/IRequest.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/INetworkInstallManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/IShopServiceAccessServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/IShopServiceAccessServerInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/IShopServiceAccessor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/IShopServiceAsync.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/IShopServiceManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/Ntc/IStaticService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nim/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Notification/INotificationServicesForApplication.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Notification/INotificationServicesForSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Npns/INpnsSystem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Npns/INpnsUser.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/Aoc/IAddOnContentManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/Aoc/IContentsServiceManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/Aoc/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/IApplicationManagerInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/IDevelopInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/IServiceGetterInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/ISystemUpdateInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ns/IVulnerabilityManagerInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/Host1xContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/INvDrvDebugFSServices.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/INvDrvServices.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/INvGemControl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/INvGemCoreDump.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvHostProfGpu/NvHostProfGpuDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvIoctl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/NvMemoryAllocator.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/Types/NvFence.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/Types/NvResult.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Nv/Types/NvStatus.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Olsc/IOlscServiceForApplication.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Olsc/IOlscServiceForSystemService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Olsc/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ovln/IReceiverService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ovln/ISenderService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcie/ILogManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcie/IManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pctl/IParentalControlServiceFactory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pctl/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Bpc/IRtcManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Clkrst/IClkrstManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/IPcvService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Rtc/IRtcManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pcv/Types/DeviceCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pm/IBootModeInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pm/IDebugMonitorInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pm/IInformationInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pm/IShellInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Pm/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Psc/IPmControl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Psc/IPmService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Psc/IPmUnknown.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Fan/IManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Fgm/IDebugger.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Fgm/ISession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Pcm/IManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Psm/IPsmServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Psm/IPsmSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Psm/Types/ChargerType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Tc/IManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Ts/IMeasurementServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ptm/Ts/Types/Location.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ro/IRoInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ro/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ro/Types/NRRCertification.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ro/Types/NroInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ro/Types/NrrHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ro/Types/NrrInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Avm/IAvmService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pdm/INotifyService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pdm/IQueryService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pdm/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pl/ISharedFontManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pl/SharedFontManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sdb/Pl/Types/SharedFontType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/ServerBase.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/ServiceAttributes.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/IFactorySettingsServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/ISettingsServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/ISystemSettingsServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/KeyCodeMaps.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/NxSettings.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Settings/Types/PlatformRegion.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sm/IManagerInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sm/IUserInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sm/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sm/SmRegistry.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/BsdContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/IClient.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/IFileDescriptor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/ISocket.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Impl/WSAError.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/ServerInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdMMsgHdr.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdMsgHdr.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/BsdSocketType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/IPollManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/LinuxError.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/PollEvent.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/PollEventData.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Bsd/Types/TimeVal.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Ethc/IEthInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Nsd/IManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Nsd/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/IResolver.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/IGeneralInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/IRandomInterface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/Types/ConfigItem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/Types/DramId.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/Types/HardwareState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/Types/HardwareType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Spl/Types/SmcResult.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Srepo/ISrepoService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/BuiltInCertificateManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/ISslService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/SslService/ISslConnection.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/SslService/ISslConnectionBase.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/SslService/ISslContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/BuiltInCertificateInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/CaCertificateId.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/CertificateFormat.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/IoMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/OptionType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/SessionCacheMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/SslVersion.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/TrustedCertStatus.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Ssl/Types/VerifyOption.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/BufferQueue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/BufferQueueCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/BufferSlot.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/BufferSlotArray.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/ConsumerBase.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/IBinder.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/IConsumerListener.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/IFlattenable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/IProducerListener.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/LayerState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/NativeWindowApi.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Parcel.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/ParcelHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/PixelFormat.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Status.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/BufferItem.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/BufferState.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/SurfaceFlinger/Types/Rect.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/StandardSteadyClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/SteadyClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/SystemClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/Types/ClockSnapshot.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/Types/SystemClockContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Clock/Types/TimeSpanType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/IAlarmService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/IPowerStateRequestHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/IStaticServiceForGlue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/IStaticServiceForPsc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/ITimeServiceManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/StaticService/ISteadyClock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/StaticService/ISystemClock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeSharedMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/TimeZone.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/TimeZoneManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/Types/CalendarTime.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/TimeZone/Types/TzifHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Types/SteadyClockContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Time/Types/TimePermissions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Usb/IClientRootSession.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Usb/IDsService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Usb/IPdCradleManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Usb/IPdManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Usb/IPmService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Usb/IUnknown1.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Usb/IUnknown2.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/IApplicationRootService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/IManagerRootService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/ISystemRootService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/ResultCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/RootService/IApplicationDisplayService.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Vi/Types/ViServiceType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Wlan/IInfraManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Wlan/ILocalGetActionFrame.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Wlan/ILocalGetFrame.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Wlan/ILocalManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Wlan/ISocketGetFrame.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Wlan/ISocketManager.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Services/Wlan/IUnknown1.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/SystemState/AppletStateMgr.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/SystemState/ColorSet.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/SystemState/KeyboardLayout.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/SystemState/RegionCode.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/SystemState/SystemLanguage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/SystemState/SystemStateMgr.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/SystemState/TitleLanguage.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/AtmosphereCompiler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/AtmosphereProgram.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/Arithmetic.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/BeginConditionalBlock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/DebugLog.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/KeyPressConditional.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/LoadRegisterWithMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/MemoryConditional.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/PauseProcess.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/ReadOrWriteStaticRegister.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/RegisterConditional.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/ResumeProcess.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/SaveOrRestoreRegister.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/SaveOrRestoreRegisterWithMask.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/StartEndLoop.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeEmitters/StoreRegisterToMemory.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CodeType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Comparison.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/CompilationContext.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/CondEQ.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/CondGE.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/CondGT.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/CondLE.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/CondLT.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/CondNE.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/ICondition.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Conditions/InputMask.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/ITamperProgram.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/ITamperedProcess.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/InstructionHelper.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/MemoryHelper.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/MemoryRegion.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/OperationBlock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/Block.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/ForBlock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/IOperand.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/IOperation.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/IfBlock.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpAdd.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpAnd.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpLog.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpLsh.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpMov.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpMul.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpNot.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpOr.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpProcCtrl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpRsh.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpSub.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Operations/OpXor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Parameter.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Pointer.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Register.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/TamperedKProcess.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/Tamper/Value.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/TamperMachine.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/HOS/UserChannelPersistence.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Homebrew.npdm (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfDynamic.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfDynamicTag.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfSymbol.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfSymbol32.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfSymbol64.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfSymbolBinding.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfSymbolType.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Elf/ElfSymbolVisibility.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Executables/IExecutable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Executables/KipExecutable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Executables/NroExecutable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Executables/NsoExecutable.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Mods/IPSPatcher.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Mods/IPSwitchPatcher.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Mods/MemPatch.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Npdm/ACI0.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Npdm/ACID.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Npdm/FsAccessControl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Npdm/FsAccessHeader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Npdm/KernelAccessControl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Npdm/Npdm.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Npdm/ServiceAccessControl.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/Extensions/FileSystemExtensions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/Extensions/MetaLoaderExtensions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/Extensions/NcaExtensions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/ProcessConst.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/ProcessLoader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/ProcessLoaderHelper.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Loaders/Processes/ProcessResult.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/MemoryConfiguration.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/PerformanceStatistics.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ryujinx.HLE.csproj (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Switch.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/DynamicTextChangedHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/IDynamicTextInputHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/IHostUiHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/IHostUiTheme.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/Input/NpadButtonHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/Input/NpadReader.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/KeyPressedHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/KeyReleasedHandler.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/RenderingSurfaceInfo.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Ui/ThemeColor.cs (100%) rename {Ryujinx.HLE => src/Ryujinx.HLE}/Utilities/StringUtils.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/HeadlessDynamicTextInputHandler.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/HeadlessHostUiTheme.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/HideCursor.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/OpenGL/OpenGLWindow.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/Options.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/Program.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/Ryujinx.Headless.SDL2.csproj (93%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/Ryujinx.bmp (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/SDL2Mouse.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/SDL2MouseDriver.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/StatusUpdatedEventArgs.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/Vulkan/VulkanWindow.cs (100%) rename {Ryujinx.Headless.SDL2 => src/Ryujinx.Headless.SDL2}/WindowBase.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/ISyscallApi.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/IThreadContext.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/InvalidResultException.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/KernelResult.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/OnScopeExit.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/Result.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/ResultNames.cs (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/Ryujinx.Horizon.Common.csproj (100%) rename {Ryujinx.Horizon.Common => src/Ryujinx.Horizon.Common}/ThreadTerminatedException.cs (100%) rename {Ryujinx.Horizon.Generators => src/Ryujinx.Horizon.Generators}/CodeGenerator.cs (100%) rename {Ryujinx.Horizon.Generators => src/Ryujinx.Horizon.Generators}/Hipc/CommandArgType.cs (100%) rename {Ryujinx.Horizon.Generators => src/Ryujinx.Horizon.Generators}/Hipc/CommandInterface.cs (100%) rename {Ryujinx.Horizon.Generators => src/Ryujinx.Horizon.Generators}/Hipc/HipcGenerator.cs (100%) rename {Ryujinx.Horizon.Generators => src/Ryujinx.Horizon.Generators}/Hipc/HipcSyntaxReceiver.cs (100%) rename {Ryujinx.Horizon.Generators => src/Ryujinx.Horizon.Generators}/Ryujinx.Horizon.Generators.csproj (100%) rename {Ryujinx.Horizon.Kernel.Generators => src/Ryujinx.Horizon.Kernel.Generators}/CodeGenerator.cs (100%) rename {Ryujinx.Horizon.Kernel.Generators => src/Ryujinx.Horizon.Kernel.Generators}/Kernel/SyscallGenerator.cs (100%) rename {Ryujinx.Horizon.Kernel.Generators => src/Ryujinx.Horizon.Kernel.Generators}/Kernel/SyscallSyntaxReceiver.cs (100%) rename {Ryujinx.Horizon.Kernel.Generators => src/Ryujinx.Horizon.Kernel.Generators}/Ryujinx.Horizon.Kernel.Generators.csproj (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/HeapAllocator.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/HorizonOptions.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/HorizonStatic.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/IService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/LogManager/Ipc/LmLogger.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/LogManager/Ipc/LogService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/LogManager/LmIpcServer.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/LogManager/LmMain.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/LogManager/Types/LogPacket.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Prepo/Ipc/PrepoService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Prepo/PrepoIpcServer.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Prepo/PrepoMain.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Prepo/PrepoResult.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Prepo/PrepoServerManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Prepo/Types/PrepoPortIndex.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Prepo/Types/PrepoServicePermissionLevel.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Ryujinx.Horizon.csproj (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Account/Uid.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/DebugUtil.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Diag/LogSeverity.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Lm/ILmLogger.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Lm/ILogService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Lm/LogDataChunkKey.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Lm/LogDestination.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Lm/LogPacketFlags.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Lm/LogPacketHeader.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Ncm/ApplicationId.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/Event.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/EventClearMode.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/EventType.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/Impl/InterProcessEvent.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/Impl/InterProcessEventImpl.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/Impl/MultiWaitImpl.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/InitializationState.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/InterProcessEventType.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/MultiWait.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/MultiWaitHolder.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/MultiWaitHolderBase.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/MultiWaitHolderOfEvent.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/MultiWaitHolderOfHandle.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/OsEvent.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/OsMultiWait.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/OsProcessHandle.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/OsResult.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/OsSystemEvent.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/OsThreadManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/SystemEventType.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/OsTypes/TriBool.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Prepo/IPrepoService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/ServiceUtil.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifDomainInHeader.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifDomainOutHeader.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifDomainRequestType.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifInHeader.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifMessage.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifOutHeader.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifRequest.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifRequestFormat.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CmifResponse.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/CommandType.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/DomainServiceObject.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/HandlesToClose.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/InlineContext.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/PointerAndSize.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ScopedInlineContextChange.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServerDomainBase.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServerDomainManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServerMessageProcessor.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServiceDispatchContext.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServiceDispatchMeta.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServiceDispatchTable.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Cmif/ServiceObjectHolder.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/CmifCommandAttribute.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/CommandArg.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/CommandArgAttributes.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/CommandHandler.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/CommandSerialization.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/Api.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/Header.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcBufferDescriptor.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcBufferFlags.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcBufferMode.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcMessage.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcMessageData.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcMetadata.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcReceiveListEntry.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcResult.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/HipcStaticDescriptor.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/ManagerOptions.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/ReceiveResult.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/Server.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/ServerDomainSessionManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/ServerManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/ServerManagerBase.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/ServerSession.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/ServerSessionManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/Hipc/SpecialHeader.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/HipcCommandProcessor.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/IServiceObject.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/RawDataOffsetCalculator.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sf/SfResult.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sm/IManagerService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sm/IUserService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sm/ServiceName.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sdk/Sm/SmApi.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/ServiceEntry.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/ServiceTable.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/Impl/ServiceInfo.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/Impl/ServiceManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/Ipc/ManagerService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/Ipc/UserService.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/SmMain.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/SmResult.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/SmServerManager.cs (100%) rename {Ryujinx.Horizon => src/Ryujinx.Horizon}/Sm/Types/SmPortIndex.cs (100%) rename {Ryujinx.Input.SDL2 => src/Ryujinx.Input.SDL2}/Ryujinx.Input.SDL2.csproj (100%) rename {Ryujinx.Input.SDL2 => src/Ryujinx.Input.SDL2}/SDL2Gamepad.cs (100%) rename {Ryujinx.Input.SDL2 => src/Ryujinx.Input.SDL2}/SDL2GamepadDriver.cs (100%) rename {Ryujinx.Input.SDL2 => src/Ryujinx.Input.SDL2}/SDL2Keyboard.cs (100%) rename {Ryujinx.Input.SDL2 => src/Ryujinx.Input.SDL2}/SDLKeyboardDriver.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Assigner/GamepadButtonAssigner.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Assigner/IButtonAssigner.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Assigner/KeyboardKeyAssigner.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/GamepadButtonInputId.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/GamepadFeaturesFlag.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/GamepadStateSnapshot.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/HLE/InputManager.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/HLE/NpadController.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/HLE/NpadManager.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/HLE/TouchScreenManager.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/IGamepad.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/IGamepadDriver.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/IKeyboard.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/IMouse.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Key.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/KeyboardStateSnapshot.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/CemuHook/Client.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/CemuHook/Protocol/ControllerData.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/CemuHook/Protocol/ControllerInfo.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/CemuHook/Protocol/Header.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/CemuHook/Protocol/MessageType.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/CemuHook/Protocol/SharedResponse.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/MotionInput.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Motion/MotionSensorFilter.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/MotionInputId.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/MouseButton.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/MouseStateSnapshot.cs (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/Ryujinx.Input.csproj (100%) rename {Ryujinx.Input => src/Ryujinx.Input}/StickInputId.cs (100%) rename {Ryujinx.Memory.Tests => src/Ryujinx.Memory.Tests}/MockVirtualMemoryManager.cs (100%) rename {Ryujinx.Memory.Tests => src/Ryujinx.Memory.Tests}/MultiRegionTrackingTests.cs (100%) rename {Ryujinx.Memory.Tests => src/Ryujinx.Memory.Tests}/Ryujinx.Memory.Tests.csproj (100%) rename {Ryujinx.Memory.Tests => src/Ryujinx.Memory.Tests}/Tests.cs (100%) rename {Ryujinx.Memory.Tests => src/Ryujinx.Memory.Tests}/TrackingTests.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/AddressSpaceManager.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/IRefCounted.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/IVirtualMemoryManager.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/IWritableBlock.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/InvalidAccessHandler.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/InvalidMemoryRegionException.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryAllocationFlags.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryBlock.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryConstants.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryManagement.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryManagementUnix.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryManagementWindows.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryManagerUnixHelper.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryMapFlags.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryNotContiguousException.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryPermission.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/MemoryProtectionException.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/NativeMemoryManager.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/PageTable.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/HostMemoryRange.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/IMultiRangeItem.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/INonOverlappingRange.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/IRange.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/MemoryRange.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/MultiRange.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/MultiRangeList.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/NonOverlappingRangeList.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Range/RangeList.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Ryujinx.Memory.csproj (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/AbstractRegion.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/BitMap.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/ConcurrentBitmap.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/IMultiRegionHandle.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/IRegionHandle.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/MemoryTracking.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/MultiRegionHandle.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/PreciseRegionSignal.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/RegionHandle.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/RegionSignal.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/SmartMultiRegionHandle.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/Tracking/VirtualRegion.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/WindowsShared/MappingTree.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/WindowsShared/PlaceholderManager.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/WindowsShared/WindowsApi.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/WindowsShared/WindowsApiException.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/WindowsShared/WindowsFlags.cs (100%) rename {Ryujinx.Memory => src/Ryujinx.Memory}/WritableRegion.cs (100%) rename {Ryujinx.SDL2.Common => src/Ryujinx.SDL2.Common}/Ryujinx.SDL2.Common.csproj (100%) rename {Ryujinx.SDL2.Common => src/Ryujinx.SDL2.Common}/SDL2Driver.cs (100%) rename {Ryujinx.ShaderTools => src/Ryujinx.ShaderTools}/Program.cs (100%) rename {Ryujinx.ShaderTools => src/Ryujinx.ShaderTools}/Ryujinx.ShaderTools.csproj (100%) rename {Ryujinx.Tests.Unicorn => src/Ryujinx.Tests.Unicorn}/IndexedProperty.cs (100%) rename {Ryujinx.Tests.Unicorn => src/Ryujinx.Tests.Unicorn}/MemoryPermission.cs (100%) rename {Ryujinx.Tests.Unicorn => src/Ryujinx.Tests.Unicorn}/Ryujinx.Tests.Unicorn.csproj (100%) rename {Ryujinx.Tests.Unicorn => src/Ryujinx.Tests.Unicorn}/SimdValue.cs (100%) rename {Ryujinx.Tests.Unicorn => src/Ryujinx.Tests.Unicorn}/UnicornAArch32.cs (100%) rename {Ryujinx.Tests.Unicorn => src/Ryujinx.Tests.Unicorn}/UnicornAArch64.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/.runsettings (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/AudioRendererConfigurationTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/BehaviourParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/BiquadFilterParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Common/UpdateDataHeaderTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Common/VoiceUpdateStateTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Common/WaveBufferTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Dsp/ResamplerTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Dsp/UpsamplerTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/EffectInfoParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/EffectOutStatusTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/MemoryPoolParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/BehaviourErrorInfoOutStatusTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/AuxParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/BufferMixerParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/DelayParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/Reverb3dParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Effect/ReverbParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdateTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/MixParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/PerformanceInParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/PerformanceOutStatusTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/RendererInfoOutStatusTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Sink/CircularBufferParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/Sink/DeviceParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/SinkInParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/SinkOutStatusTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Parameter/SplitterInParamHeaderTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/AddressInfoTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/BehaviourContextTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/MemoryPoolStateTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/MixStateTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/PoolMapperTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/SplitterDestinationTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/SplitterStateTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/VoiceChannelResourceTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/VoiceStateTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/Server/WaveBufferTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/VoiceChannelResourceInParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/VoiceInParameterTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Audio/Renderer/VoiceOutStatusTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/Arm64CodeGenCommonTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuContext.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTest.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTest32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAlu.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAlu32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAluBinary.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAluBinary32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAluImm.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAluImm32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAluRs.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAluRs32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestAluRx.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestBf32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestBfm.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestCcmpImm.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestCcmpReg.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestCsel.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestMisc.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestMisc32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestMov.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestMul.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestMul32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimd.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimd32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdCrypto.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdCrypto32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdCvt.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdCvt32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdExt.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdFcond.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdFmov.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdImm.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdIns.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdLogical32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdMemory32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdMov32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdReg.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdReg32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdRegElem.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdRegElem32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdRegElemF.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdShImm.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdShImm32.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSimdTbl.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestSystem.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestT32Alu.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestT32Flow.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestT32Mem.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/CpuTestThumb.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/EnvironmentTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/PrecomputedMemoryThumbTestCase.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Cpu/PrecomputedThumbTestCase.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/HLE/SoftwareKeyboardTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Memory/MockMemoryManager.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Memory/PartialUnmaps.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Ryujinx.Tests.csproj (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/Time/TimeZoneRuleTests.cs (100%) rename {Ryujinx.Tests => src/Ryujinx.Tests}/TreeDictionaryTests.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/App/ApplicationAddedEventArgs.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/App/ApplicationCountUpdatedEventArgs.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/App/ApplicationData.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/App/ApplicationJsonSerializerContext.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/App/ApplicationLibrary.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/App/ApplicationMetadata.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/AudioBackend.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/ConfigurationFileFormat.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/ConfigurationFileFormatSettings.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/ConfigurationJsonSerializerContext.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/ConfigurationLoadResult.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/ConfigurationState.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/FileTypes.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/LoggerModule.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/System/Language.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/System/Region.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/Ui/ColumnSort.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/Ui/GuiColumns.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Configuration/Ui/ShownFileTypes.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/DiscordIntegrationModule.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Extensions/FileTypeExtensions.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Helper/CommandLineState.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Helper/ConsoleHelper.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Helper/FileAssociationHelper.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Helper/ObjectiveC.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Helper/OpenHelper.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Helper/SetupValidator.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Amiibo/AmiiboApi.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Amiibo/AmiiboApiGamesSwitch.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Amiibo/AmiiboApiUsage.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Amiibo/AmiiboJson.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Amiibo/AmiiboJsonSerializerContext.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Github/GithubReleaseAssetJsonResponse.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Github/GithubReleasesJsonResponse.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Models/Github/GithubReleasesJsonSerializerContext.cs (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Controller_JoyConLeft.svg (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Controller_JoyConPair.svg (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Controller_JoyConRight.svg (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Controller_ProCon.svg (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Icon_NCA.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Icon_NRO.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Icon_NSO.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Icon_NSP.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Icon_XCI.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Amiibo.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Discord_Dark.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Discord_Light.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_GitHub_Dark.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_GitHub_Light.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Patreon_Dark.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Patreon_Light.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Ryujinx.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Twitter_Dark.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Resources/Logo_Twitter_Light.png (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/Ryujinx.Ui.Common.csproj (100%) rename {Ryujinx.Ui.Common => src/Ryujinx.Ui.Common}/UserError.cs (100%) rename {Ryujinx.Ui.LocaleGenerator => src/Ryujinx.Ui.LocaleGenerator}/LocaleGenerator.cs (100%) rename {Ryujinx.Ui.LocaleGenerator => src/Ryujinx.Ui.LocaleGenerator}/Ryujinx.Ui.LocaleGenerator.csproj (100%) rename {Ryujinx => src/Ryujinx}/Input/GTK3/GTK3Keyboard.cs (100%) rename {Ryujinx => src/Ryujinx}/Input/GTK3/GTK3KeyboardDriver.cs (100%) rename {Ryujinx => src/Ryujinx}/Input/GTK3/GTK3MappingHelper.cs (100%) rename {Ryujinx => src/Ryujinx}/Input/GTK3/GTK3Mouse.cs (100%) rename {Ryujinx => src/Ryujinx}/Input/GTK3/GTK3MouseDriver.cs (100%) rename {Ryujinx => src/Ryujinx}/Modules/Updater/UpdateDialog.cs (100%) rename {Ryujinx => src/Ryujinx}/Modules/Updater/UpdateDialog.glade (100%) rename {Ryujinx => src/Ryujinx}/Modules/Updater/Updater.cs (100%) rename {Ryujinx => src/Ryujinx}/Program.cs (100%) rename {Ryujinx => src/Ryujinx}/Ryujinx.csproj (92%) rename {Ryujinx => src/Ryujinx}/Ryujinx.ico (100%) rename {Ryujinx => src/Ryujinx}/Ui/Applet/ErrorAppletDialog.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Applet/GtkDynamicTextInputHandler.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Applet/GtkHostUiHandler.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Applet/GtkHostUiTheme.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Applet/SwkbdAppletDialog.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/GLRenderer.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Helper/MetalHelper.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Helper/SortHelper.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Helper/ThemeHelper.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/MainWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/MainWindow.glade (100%) rename {Ryujinx => src/Ryujinx}/Ui/OpenToolkitBindingsContext.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/RendererWidgetBase.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/SPBOpenGLContext.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/StatusUpdatedEventArgs.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/VKRenderer.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/GameTableContextMenu.Designer.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/GameTableContextMenu.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/GtkDialog.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/GtkInputDialog.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/ProfileDialog.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/ProfileDialog.glade (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/RawInputToTextEntry.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Widgets/UserErrorDialog.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/AboutWindow.Designer.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/AboutWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/AmiiboWindow.Designer.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/AmiiboWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/AvatarWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/CheatWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/CheatWindow.glade (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/ControllerWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/ControllerWindow.glade (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/DlcWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/DlcWindow.glade (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/SettingsWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/SettingsWindow.glade (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/TitleUpdateWindow.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/TitleUpdateWindow.glade (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/UserProfilesManagerWindow.Designer.cs (100%) rename {Ryujinx => src/Ryujinx}/Ui/Windows/UserProfilesManagerWindow.cs (100%) rename {Spv.Generator => src/Spv.Generator}/Autogenerated/CoreGrammar.cs (100%) rename {Spv.Generator => src/Spv.Generator}/Autogenerated/GlslStd450Grammar.cs (100%) rename {Spv.Generator => src/Spv.Generator}/Autogenerated/OpenClGrammar.cs (100%) rename {Spv.Generator => src/Spv.Generator}/ConstantKey.cs (100%) rename {Spv.Generator => src/Spv.Generator}/DeterministicHashCode.cs (100%) rename {Spv.Generator => src/Spv.Generator}/DeterministicStringKey.cs (100%) rename {Spv.Generator => src/Spv.Generator}/GeneratorPool.cs (100%) rename {Spv.Generator => src/Spv.Generator}/Instruction.cs (100%) rename {Spv.Generator => src/Spv.Generator}/InstructionOperands.cs (100%) rename {Spv.Generator => src/Spv.Generator}/LICENSE (100%) rename {Spv.Generator => src/Spv.Generator}/LiteralInteger.cs (100%) rename {Spv.Generator => src/Spv.Generator}/LiteralString.cs (100%) rename {Spv.Generator => src/Spv.Generator}/Module.cs (100%) rename {Spv.Generator => src/Spv.Generator}/Operand.cs (100%) rename {Spv.Generator => src/Spv.Generator}/OperandType.cs (100%) rename {Spv.Generator => src/Spv.Generator}/Spv.Generator.csproj (100%) rename {Spv.Generator => src/Spv.Generator}/TypeDeclarationKey.cs (100%) rename {Spv.Generator => src/Spv.Generator}/spirv.cs (100%) diff --git a/Ryujinx.sln b/Ryujinx.sln index 12657bf9e..49fdc9c93 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -3,33 +3,33 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "src\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "src\Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "src\Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "src\Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "src\ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Gpu", "Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj", "{ADA7EA87-0D63-4D97-9433-922A2124401F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Gpu", "src\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj", "{ADA7EA87-0D63-4D97-9433-922A2124401F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.GAL", "Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj", "{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.GAL", "src\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj", "{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.OpenGL", "Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj", "{9558FB96-075D-4219-8FFF-401979DC0B69}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.OpenGL", "src\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj", "{9558FB96-075D-4219-8FFF-401979DC0B69}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Texture", "Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj", "{E1B1AD28-289D-47B7-A106-326972240207}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Texture", "src\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj", "{E1B1AD28-289D-47B7-A106-326972240207}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "src\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "src\Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" ProjectSection(SolutionItems) = preProject @@ -37,55 +37,55 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory.Tests", "Ryujinx.Memory.Tests\Ryujinx.Memory.Tests.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory.Tests", "src\Ryujinx.Memory.Tests\Ryujinx.Memory.Tests.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "src\Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Device", "Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj", "{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Device", "src\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj", "{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Host1x", "Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj", "{C35F1536-7DE5-4F9D-9604-B5B4E1561947}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Host1x", "src\Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj", "{C35F1536-7DE5-4F9D-9604-B5B4E1561947}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.Vp9", "Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj", "{B9AECA11-E248-4886-A10B-81B631CAAF29}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.Vp9", "src\Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj", "{B9AECA11-E248-4886-A10B-81B631CAAF29}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "Ryujinx.Graphics.Vic\Ryujinx.Graphics.Vic.csproj", "{81BB2C11-9408-4EA3-822E-42987AF54429}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "src\Ryujinx.Graphics.Vic\Ryujinx.Graphics.Vic.csproj", "{81BB2C11-9408-4EA3-822E-42987AF54429}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "src\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "src\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "src\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "src\Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "src\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "src\Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "src\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "src\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "Ryujinx.Horizon\Ryujinx.Horizon.csproj", "{AF34127A-3A92-43E5-8496-14960A50B1F1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryujinx.Horizon\Ryujinx.Horizon.csproj", "{AF34127A-3A92-43E5-8496-14960A50B1F1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -256,4 +256,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A} EndGlobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/ARMeilleure/ARMeilleure.csproj b/src/ARMeilleure/ARMeilleure.csproj similarity index 100% rename from ARMeilleure/ARMeilleure.csproj rename to src/ARMeilleure/ARMeilleure.csproj diff --git a/ARMeilleure/Allocators.cs b/src/ARMeilleure/Allocators.cs similarity index 100% rename from ARMeilleure/Allocators.cs rename to src/ARMeilleure/Allocators.cs diff --git a/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs b/src/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs rename to src/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs diff --git a/ARMeilleure/CodeGen/Arm64/ArmCondition.cs b/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/ArmCondition.cs rename to src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs diff --git a/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs b/src/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs rename to src/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs diff --git a/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs b/src/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/ArmShiftType.cs rename to src/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs diff --git a/ARMeilleure/CodeGen/Arm64/Assembler.cs b/src/ARMeilleure/CodeGen/Arm64/Assembler.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/Assembler.cs rename to src/ARMeilleure/CodeGen/Arm64/Assembler.cs diff --git a/ARMeilleure/CodeGen/Arm64/CallingConvention.cs b/src/ARMeilleure/CodeGen/Arm64/CallingConvention.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/CallingConvention.cs rename to src/ARMeilleure/CodeGen/Arm64/CallingConvention.cs diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs rename to src/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/CodeGenContext.cs rename to src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/CodeGenerator.cs rename to src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs diff --git a/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs rename to src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs diff --git a/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs b/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs rename to src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs diff --git a/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs rename to src/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs diff --git a/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs rename to src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs diff --git a/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/IntrinsicType.cs rename to src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs diff --git a/ARMeilleure/CodeGen/Arm64/PreAllocator.cs b/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs similarity index 100% rename from ARMeilleure/CodeGen/Arm64/PreAllocator.cs rename to src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs diff --git a/ARMeilleure/CodeGen/CompiledFunction.cs b/src/ARMeilleure/CodeGen/CompiledFunction.cs similarity index 100% rename from ARMeilleure/CodeGen/CompiledFunction.cs rename to src/ARMeilleure/CodeGen/CompiledFunction.cs diff --git a/ARMeilleure/CodeGen/Linking/RelocEntry.cs b/src/ARMeilleure/CodeGen/Linking/RelocEntry.cs similarity index 100% rename from ARMeilleure/CodeGen/Linking/RelocEntry.cs rename to src/ARMeilleure/CodeGen/Linking/RelocEntry.cs diff --git a/ARMeilleure/CodeGen/Linking/RelocInfo.cs b/src/ARMeilleure/CodeGen/Linking/RelocInfo.cs similarity index 100% rename from ARMeilleure/CodeGen/Linking/RelocInfo.cs rename to src/ARMeilleure/CodeGen/Linking/RelocInfo.cs diff --git a/ARMeilleure/CodeGen/Linking/Symbol.cs b/src/ARMeilleure/CodeGen/Linking/Symbol.cs similarity index 100% rename from ARMeilleure/CodeGen/Linking/Symbol.cs rename to src/ARMeilleure/CodeGen/Linking/Symbol.cs diff --git a/ARMeilleure/CodeGen/Linking/SymbolType.cs b/src/ARMeilleure/CodeGen/Linking/SymbolType.cs similarity index 100% rename from ARMeilleure/CodeGen/Linking/SymbolType.cs rename to src/ARMeilleure/CodeGen/Linking/SymbolType.cs diff --git a/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs b/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs similarity index 100% rename from ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs rename to src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs diff --git a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs b/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs similarity index 100% rename from ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs rename to src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs diff --git a/ARMeilleure/CodeGen/Optimizations/Optimizer.cs b/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs similarity index 100% rename from ARMeilleure/CodeGen/Optimizations/Optimizer.cs rename to src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs diff --git a/ARMeilleure/CodeGen/Optimizations/Simplification.cs b/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs similarity index 100% rename from ARMeilleure/CodeGen/Optimizations/Simplification.cs rename to src/ARMeilleure/CodeGen/Optimizations/Simplification.cs diff --git a/ARMeilleure/CodeGen/Optimizations/TailMerge.cs b/src/ARMeilleure/CodeGen/Optimizations/TailMerge.cs similarity index 100% rename from ARMeilleure/CodeGen/Optimizations/TailMerge.cs rename to src/ARMeilleure/CodeGen/Optimizations/TailMerge.cs diff --git a/ARMeilleure/CodeGen/PreAllocatorCommon.cs b/src/ARMeilleure/CodeGen/PreAllocatorCommon.cs similarity index 100% rename from ARMeilleure/CodeGen/PreAllocatorCommon.cs rename to src/ARMeilleure/CodeGen/PreAllocatorCommon.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/UseList.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/UseList.cs similarity index 100% rename from ARMeilleure/CodeGen/RegisterAllocators/UseList.cs rename to src/ARMeilleure/CodeGen/RegisterAllocators/UseList.cs diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs b/src/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs similarity index 100% rename from ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs rename to src/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs b/src/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs similarity index 100% rename from ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs rename to src/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs b/src/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs similarity index 100% rename from ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs rename to src/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/src/ARMeilleure/CodeGen/X86/Assembler.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/Assembler.cs rename to src/ARMeilleure/CodeGen/X86/Assembler.cs diff --git a/ARMeilleure/CodeGen/X86/AssemblerTable.cs b/src/ARMeilleure/CodeGen/X86/AssemblerTable.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/AssemblerTable.cs rename to src/ARMeilleure/CodeGen/X86/AssemblerTable.cs diff --git a/ARMeilleure/CodeGen/X86/CallConvName.cs b/src/ARMeilleure/CodeGen/X86/CallConvName.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/CallConvName.cs rename to src/ARMeilleure/CodeGen/X86/CallConvName.cs diff --git a/ARMeilleure/CodeGen/X86/CallingConvention.cs b/src/ARMeilleure/CodeGen/X86/CallingConvention.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/CallingConvention.cs rename to src/ARMeilleure/CodeGen/X86/CallingConvention.cs diff --git a/ARMeilleure/CodeGen/X86/CodeGenCommon.cs b/src/ARMeilleure/CodeGen/X86/CodeGenCommon.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/CodeGenCommon.cs rename to src/ARMeilleure/CodeGen/X86/CodeGenCommon.cs diff --git a/ARMeilleure/CodeGen/X86/CodeGenContext.cs b/src/ARMeilleure/CodeGen/X86/CodeGenContext.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/CodeGenContext.cs rename to src/ARMeilleure/CodeGen/X86/CodeGenContext.cs diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/CodeGenerator.cs rename to src/ARMeilleure/CodeGen/X86/CodeGenerator.cs diff --git a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/HardwareCapabilities.cs rename to src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs diff --git a/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs b/src/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/IntrinsicInfo.cs rename to src/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/src/ARMeilleure/CodeGen/X86/IntrinsicTable.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/IntrinsicTable.cs rename to src/ARMeilleure/CodeGen/X86/IntrinsicTable.cs diff --git a/ARMeilleure/CodeGen/X86/IntrinsicType.cs b/src/ARMeilleure/CodeGen/X86/IntrinsicType.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/IntrinsicType.cs rename to src/ARMeilleure/CodeGen/X86/IntrinsicType.cs diff --git a/ARMeilleure/CodeGen/X86/Mxcsr.cs b/src/ARMeilleure/CodeGen/X86/Mxcsr.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/Mxcsr.cs rename to src/ARMeilleure/CodeGen/X86/Mxcsr.cs diff --git a/ARMeilleure/CodeGen/X86/PreAllocator.cs b/src/ARMeilleure/CodeGen/X86/PreAllocator.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/PreAllocator.cs rename to src/ARMeilleure/CodeGen/X86/PreAllocator.cs diff --git a/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs b/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs rename to src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs diff --git a/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs b/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs rename to src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs diff --git a/ARMeilleure/CodeGen/X86/X86Condition.cs b/src/ARMeilleure/CodeGen/X86/X86Condition.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/X86Condition.cs rename to src/ARMeilleure/CodeGen/X86/X86Condition.cs diff --git a/ARMeilleure/CodeGen/X86/X86Instruction.cs b/src/ARMeilleure/CodeGen/X86/X86Instruction.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/X86Instruction.cs rename to src/ARMeilleure/CodeGen/X86/X86Instruction.cs diff --git a/ARMeilleure/CodeGen/X86/X86Optimizer.cs b/src/ARMeilleure/CodeGen/X86/X86Optimizer.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/X86Optimizer.cs rename to src/ARMeilleure/CodeGen/X86/X86Optimizer.cs diff --git a/ARMeilleure/CodeGen/X86/X86Register.cs b/src/ARMeilleure/CodeGen/X86/X86Register.cs similarity index 100% rename from ARMeilleure/CodeGen/X86/X86Register.cs rename to src/ARMeilleure/CodeGen/X86/X86Register.cs diff --git a/ARMeilleure/Common/AddressTable.cs b/src/ARMeilleure/Common/AddressTable.cs similarity index 100% rename from ARMeilleure/Common/AddressTable.cs rename to src/ARMeilleure/Common/AddressTable.cs diff --git a/ARMeilleure/Common/Allocator.cs b/src/ARMeilleure/Common/Allocator.cs similarity index 100% rename from ARMeilleure/Common/Allocator.cs rename to src/ARMeilleure/Common/Allocator.cs diff --git a/ARMeilleure/Common/ArenaAllocator.cs b/src/ARMeilleure/Common/ArenaAllocator.cs similarity index 100% rename from ARMeilleure/Common/ArenaAllocator.cs rename to src/ARMeilleure/Common/ArenaAllocator.cs diff --git a/ARMeilleure/Common/BitMap.cs b/src/ARMeilleure/Common/BitMap.cs similarity index 100% rename from ARMeilleure/Common/BitMap.cs rename to src/ARMeilleure/Common/BitMap.cs diff --git a/ARMeilleure/Common/BitUtils.cs b/src/ARMeilleure/Common/BitUtils.cs similarity index 100% rename from ARMeilleure/Common/BitUtils.cs rename to src/ARMeilleure/Common/BitUtils.cs diff --git a/ARMeilleure/Common/Counter.cs b/src/ARMeilleure/Common/Counter.cs similarity index 100% rename from ARMeilleure/Common/Counter.cs rename to src/ARMeilleure/Common/Counter.cs diff --git a/ARMeilleure/Common/EntryTable.cs b/src/ARMeilleure/Common/EntryTable.cs similarity index 100% rename from ARMeilleure/Common/EntryTable.cs rename to src/ARMeilleure/Common/EntryTable.cs diff --git a/ARMeilleure/Common/EnumUtils.cs b/src/ARMeilleure/Common/EnumUtils.cs similarity index 100% rename from ARMeilleure/Common/EnumUtils.cs rename to src/ARMeilleure/Common/EnumUtils.cs diff --git a/ARMeilleure/Common/NativeAllocator.cs b/src/ARMeilleure/Common/NativeAllocator.cs similarity index 100% rename from ARMeilleure/Common/NativeAllocator.cs rename to src/ARMeilleure/Common/NativeAllocator.cs diff --git a/ARMeilleure/Decoders/Block.cs b/src/ARMeilleure/Decoders/Block.cs similarity index 100% rename from ARMeilleure/Decoders/Block.cs rename to src/ARMeilleure/Decoders/Block.cs diff --git a/ARMeilleure/Decoders/Condition.cs b/src/ARMeilleure/Decoders/Condition.cs similarity index 100% rename from ARMeilleure/Decoders/Condition.cs rename to src/ARMeilleure/Decoders/Condition.cs diff --git a/ARMeilleure/Decoders/DataOp.cs b/src/ARMeilleure/Decoders/DataOp.cs similarity index 100% rename from ARMeilleure/Decoders/DataOp.cs rename to src/ARMeilleure/Decoders/DataOp.cs diff --git a/ARMeilleure/Decoders/Decoder.cs b/src/ARMeilleure/Decoders/Decoder.cs similarity index 100% rename from ARMeilleure/Decoders/Decoder.cs rename to src/ARMeilleure/Decoders/Decoder.cs diff --git a/ARMeilleure/Decoders/DecoderHelper.cs b/src/ARMeilleure/Decoders/DecoderHelper.cs similarity index 100% rename from ARMeilleure/Decoders/DecoderHelper.cs rename to src/ARMeilleure/Decoders/DecoderHelper.cs diff --git a/ARMeilleure/Decoders/DecoderMode.cs b/src/ARMeilleure/Decoders/DecoderMode.cs similarity index 100% rename from ARMeilleure/Decoders/DecoderMode.cs rename to src/ARMeilleure/Decoders/DecoderMode.cs diff --git a/ARMeilleure/Decoders/IOpCode.cs b/src/ARMeilleure/Decoders/IOpCode.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode.cs rename to src/ARMeilleure/Decoders/IOpCode.cs diff --git a/ARMeilleure/Decoders/IOpCode32.cs b/src/ARMeilleure/Decoders/IOpCode32.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32.cs rename to src/ARMeilleure/Decoders/IOpCode32.cs diff --git a/ARMeilleure/Decoders/IOpCode32Adr.cs b/src/ARMeilleure/Decoders/IOpCode32Adr.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32Adr.cs rename to src/ARMeilleure/Decoders/IOpCode32Adr.cs diff --git a/ARMeilleure/Decoders/IOpCode32Alu.cs b/src/ARMeilleure/Decoders/IOpCode32Alu.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32Alu.cs rename to src/ARMeilleure/Decoders/IOpCode32Alu.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluBf.cs b/src/ARMeilleure/Decoders/IOpCode32AluBf.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluBf.cs rename to src/ARMeilleure/Decoders/IOpCode32AluBf.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluImm.cs b/src/ARMeilleure/Decoders/IOpCode32AluImm.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluImm.cs rename to src/ARMeilleure/Decoders/IOpCode32AluImm.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluImm16.cs b/src/ARMeilleure/Decoders/IOpCode32AluImm16.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluImm16.cs rename to src/ARMeilleure/Decoders/IOpCode32AluImm16.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluMla.cs b/src/ARMeilleure/Decoders/IOpCode32AluMla.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluMla.cs rename to src/ARMeilleure/Decoders/IOpCode32AluMla.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluReg.cs b/src/ARMeilleure/Decoders/IOpCode32AluReg.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluReg.cs rename to src/ARMeilleure/Decoders/IOpCode32AluReg.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluRsImm.cs b/src/ARMeilleure/Decoders/IOpCode32AluRsImm.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluRsImm.cs rename to src/ARMeilleure/Decoders/IOpCode32AluRsImm.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluRsReg.cs b/src/ARMeilleure/Decoders/IOpCode32AluRsReg.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluRsReg.cs rename to src/ARMeilleure/Decoders/IOpCode32AluRsReg.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluUmull.cs b/src/ARMeilleure/Decoders/IOpCode32AluUmull.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluUmull.cs rename to src/ARMeilleure/Decoders/IOpCode32AluUmull.cs diff --git a/ARMeilleure/Decoders/IOpCode32AluUx.cs b/src/ARMeilleure/Decoders/IOpCode32AluUx.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32AluUx.cs rename to src/ARMeilleure/Decoders/IOpCode32AluUx.cs diff --git a/ARMeilleure/Decoders/IOpCode32BImm.cs b/src/ARMeilleure/Decoders/IOpCode32BImm.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32BImm.cs rename to src/ARMeilleure/Decoders/IOpCode32BImm.cs diff --git a/ARMeilleure/Decoders/IOpCode32BReg.cs b/src/ARMeilleure/Decoders/IOpCode32BReg.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32BReg.cs rename to src/ARMeilleure/Decoders/IOpCode32BReg.cs diff --git a/ARMeilleure/Decoders/IOpCode32Exception.cs b/src/ARMeilleure/Decoders/IOpCode32Exception.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32Exception.cs rename to src/ARMeilleure/Decoders/IOpCode32Exception.cs diff --git a/ARMeilleure/Decoders/IOpCode32HasSetFlags.cs b/src/ARMeilleure/Decoders/IOpCode32HasSetFlags.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32HasSetFlags.cs rename to src/ARMeilleure/Decoders/IOpCode32HasSetFlags.cs diff --git a/ARMeilleure/Decoders/IOpCode32Mem.cs b/src/ARMeilleure/Decoders/IOpCode32Mem.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32Mem.cs rename to src/ARMeilleure/Decoders/IOpCode32Mem.cs diff --git a/ARMeilleure/Decoders/IOpCode32MemEx.cs b/src/ARMeilleure/Decoders/IOpCode32MemEx.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32MemEx.cs rename to src/ARMeilleure/Decoders/IOpCode32MemEx.cs diff --git a/ARMeilleure/Decoders/IOpCode32MemMult.cs b/src/ARMeilleure/Decoders/IOpCode32MemMult.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32MemMult.cs rename to src/ARMeilleure/Decoders/IOpCode32MemMult.cs diff --git a/ARMeilleure/Decoders/IOpCode32MemReg.cs b/src/ARMeilleure/Decoders/IOpCode32MemReg.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32MemReg.cs rename to src/ARMeilleure/Decoders/IOpCode32MemReg.cs diff --git a/ARMeilleure/Decoders/IOpCode32MemRsImm.cs b/src/ARMeilleure/Decoders/IOpCode32MemRsImm.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32MemRsImm.cs rename to src/ARMeilleure/Decoders/IOpCode32MemRsImm.cs diff --git a/ARMeilleure/Decoders/IOpCode32Simd.cs b/src/ARMeilleure/Decoders/IOpCode32Simd.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32Simd.cs rename to src/ARMeilleure/Decoders/IOpCode32Simd.cs diff --git a/ARMeilleure/Decoders/IOpCode32SimdImm.cs b/src/ARMeilleure/Decoders/IOpCode32SimdImm.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCode32SimdImm.cs rename to src/ARMeilleure/Decoders/IOpCode32SimdImm.cs diff --git a/ARMeilleure/Decoders/IOpCodeAlu.cs b/src/ARMeilleure/Decoders/IOpCodeAlu.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeAlu.cs rename to src/ARMeilleure/Decoders/IOpCodeAlu.cs diff --git a/ARMeilleure/Decoders/IOpCodeAluImm.cs b/src/ARMeilleure/Decoders/IOpCodeAluImm.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeAluImm.cs rename to src/ARMeilleure/Decoders/IOpCodeAluImm.cs diff --git a/ARMeilleure/Decoders/IOpCodeAluRs.cs b/src/ARMeilleure/Decoders/IOpCodeAluRs.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeAluRs.cs rename to src/ARMeilleure/Decoders/IOpCodeAluRs.cs diff --git a/ARMeilleure/Decoders/IOpCodeAluRx.cs b/src/ARMeilleure/Decoders/IOpCodeAluRx.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeAluRx.cs rename to src/ARMeilleure/Decoders/IOpCodeAluRx.cs diff --git a/ARMeilleure/Decoders/IOpCodeBImm.cs b/src/ARMeilleure/Decoders/IOpCodeBImm.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeBImm.cs rename to src/ARMeilleure/Decoders/IOpCodeBImm.cs diff --git a/ARMeilleure/Decoders/IOpCodeCond.cs b/src/ARMeilleure/Decoders/IOpCodeCond.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeCond.cs rename to src/ARMeilleure/Decoders/IOpCodeCond.cs diff --git a/ARMeilleure/Decoders/IOpCodeLit.cs b/src/ARMeilleure/Decoders/IOpCodeLit.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeLit.cs rename to src/ARMeilleure/Decoders/IOpCodeLit.cs diff --git a/ARMeilleure/Decoders/IOpCodeSimd.cs b/src/ARMeilleure/Decoders/IOpCodeSimd.cs similarity index 100% rename from ARMeilleure/Decoders/IOpCodeSimd.cs rename to src/ARMeilleure/Decoders/IOpCodeSimd.cs diff --git a/ARMeilleure/Decoders/InstDescriptor.cs b/src/ARMeilleure/Decoders/InstDescriptor.cs similarity index 100% rename from ARMeilleure/Decoders/InstDescriptor.cs rename to src/ARMeilleure/Decoders/InstDescriptor.cs diff --git a/ARMeilleure/Decoders/InstEmitter.cs b/src/ARMeilleure/Decoders/InstEmitter.cs similarity index 100% rename from ARMeilleure/Decoders/InstEmitter.cs rename to src/ARMeilleure/Decoders/InstEmitter.cs diff --git a/ARMeilleure/Decoders/IntType.cs b/src/ARMeilleure/Decoders/IntType.cs similarity index 100% rename from ARMeilleure/Decoders/IntType.cs rename to src/ARMeilleure/Decoders/IntType.cs diff --git a/ARMeilleure/Decoders/OpCode.cs b/src/ARMeilleure/Decoders/OpCode.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode.cs rename to src/ARMeilleure/Decoders/OpCode.cs diff --git a/ARMeilleure/Decoders/OpCode32.cs b/src/ARMeilleure/Decoders/OpCode32.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32.cs rename to src/ARMeilleure/Decoders/OpCode32.cs diff --git a/ARMeilleure/Decoders/OpCode32Alu.cs b/src/ARMeilleure/Decoders/OpCode32Alu.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32Alu.cs rename to src/ARMeilleure/Decoders/OpCode32Alu.cs diff --git a/ARMeilleure/Decoders/OpCode32AluBf.cs b/src/ARMeilleure/Decoders/OpCode32AluBf.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluBf.cs rename to src/ARMeilleure/Decoders/OpCode32AluBf.cs diff --git a/ARMeilleure/Decoders/OpCode32AluImm.cs b/src/ARMeilleure/Decoders/OpCode32AluImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluImm.cs rename to src/ARMeilleure/Decoders/OpCode32AluImm.cs diff --git a/ARMeilleure/Decoders/OpCode32AluImm16.cs b/src/ARMeilleure/Decoders/OpCode32AluImm16.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluImm16.cs rename to src/ARMeilleure/Decoders/OpCode32AluImm16.cs diff --git a/ARMeilleure/Decoders/OpCode32AluMla.cs b/src/ARMeilleure/Decoders/OpCode32AluMla.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluMla.cs rename to src/ARMeilleure/Decoders/OpCode32AluMla.cs diff --git a/ARMeilleure/Decoders/OpCode32AluReg.cs b/src/ARMeilleure/Decoders/OpCode32AluReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluReg.cs rename to src/ARMeilleure/Decoders/OpCode32AluReg.cs diff --git a/ARMeilleure/Decoders/OpCode32AluRsImm.cs b/src/ARMeilleure/Decoders/OpCode32AluRsImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluRsImm.cs rename to src/ARMeilleure/Decoders/OpCode32AluRsImm.cs diff --git a/ARMeilleure/Decoders/OpCode32AluRsReg.cs b/src/ARMeilleure/Decoders/OpCode32AluRsReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluRsReg.cs rename to src/ARMeilleure/Decoders/OpCode32AluRsReg.cs diff --git a/ARMeilleure/Decoders/OpCode32AluUmull.cs b/src/ARMeilleure/Decoders/OpCode32AluUmull.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluUmull.cs rename to src/ARMeilleure/Decoders/OpCode32AluUmull.cs diff --git a/ARMeilleure/Decoders/OpCode32AluUx.cs b/src/ARMeilleure/Decoders/OpCode32AluUx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32AluUx.cs rename to src/ARMeilleure/Decoders/OpCode32AluUx.cs diff --git a/ARMeilleure/Decoders/OpCode32BImm.cs b/src/ARMeilleure/Decoders/OpCode32BImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32BImm.cs rename to src/ARMeilleure/Decoders/OpCode32BImm.cs diff --git a/ARMeilleure/Decoders/OpCode32BReg.cs b/src/ARMeilleure/Decoders/OpCode32BReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32BReg.cs rename to src/ARMeilleure/Decoders/OpCode32BReg.cs diff --git a/ARMeilleure/Decoders/OpCode32Exception.cs b/src/ARMeilleure/Decoders/OpCode32Exception.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32Exception.cs rename to src/ARMeilleure/Decoders/OpCode32Exception.cs diff --git a/ARMeilleure/Decoders/OpCode32Mem.cs b/src/ARMeilleure/Decoders/OpCode32Mem.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32Mem.cs rename to src/ARMeilleure/Decoders/OpCode32Mem.cs diff --git a/ARMeilleure/Decoders/OpCode32MemImm.cs b/src/ARMeilleure/Decoders/OpCode32MemImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MemImm.cs rename to src/ARMeilleure/Decoders/OpCode32MemImm.cs diff --git a/ARMeilleure/Decoders/OpCode32MemImm8.cs b/src/ARMeilleure/Decoders/OpCode32MemImm8.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MemImm8.cs rename to src/ARMeilleure/Decoders/OpCode32MemImm8.cs diff --git a/ARMeilleure/Decoders/OpCode32MemLdEx.cs b/src/ARMeilleure/Decoders/OpCode32MemLdEx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MemLdEx.cs rename to src/ARMeilleure/Decoders/OpCode32MemLdEx.cs diff --git a/ARMeilleure/Decoders/OpCode32MemMult.cs b/src/ARMeilleure/Decoders/OpCode32MemMult.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MemMult.cs rename to src/ARMeilleure/Decoders/OpCode32MemMult.cs diff --git a/ARMeilleure/Decoders/OpCode32MemReg.cs b/src/ARMeilleure/Decoders/OpCode32MemReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MemReg.cs rename to src/ARMeilleure/Decoders/OpCode32MemReg.cs diff --git a/ARMeilleure/Decoders/OpCode32MemRsImm.cs b/src/ARMeilleure/Decoders/OpCode32MemRsImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MemRsImm.cs rename to src/ARMeilleure/Decoders/OpCode32MemRsImm.cs diff --git a/ARMeilleure/Decoders/OpCode32MemStEx.cs b/src/ARMeilleure/Decoders/OpCode32MemStEx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MemStEx.cs rename to src/ARMeilleure/Decoders/OpCode32MemStEx.cs diff --git a/ARMeilleure/Decoders/OpCode32Mrs.cs b/src/ARMeilleure/Decoders/OpCode32Mrs.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32Mrs.cs rename to src/ARMeilleure/Decoders/OpCode32Mrs.cs diff --git a/ARMeilleure/Decoders/OpCode32MsrReg.cs b/src/ARMeilleure/Decoders/OpCode32MsrReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32MsrReg.cs rename to src/ARMeilleure/Decoders/OpCode32MsrReg.cs diff --git a/ARMeilleure/Decoders/OpCode32Sat.cs b/src/ARMeilleure/Decoders/OpCode32Sat.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32Sat.cs rename to src/ARMeilleure/Decoders/OpCode32Sat.cs diff --git a/ARMeilleure/Decoders/OpCode32Sat16.cs b/src/ARMeilleure/Decoders/OpCode32Sat16.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32Sat16.cs rename to src/ARMeilleure/Decoders/OpCode32Sat16.cs diff --git a/ARMeilleure/Decoders/OpCode32Simd.cs b/src/ARMeilleure/Decoders/OpCode32Simd.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32Simd.cs rename to src/ARMeilleure/Decoders/OpCode32Simd.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdBase.cs b/src/ARMeilleure/Decoders/OpCode32SimdBase.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdBase.cs rename to src/ARMeilleure/Decoders/OpCode32SimdBase.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdBinary.cs b/src/ARMeilleure/Decoders/OpCode32SimdBinary.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdBinary.cs rename to src/ARMeilleure/Decoders/OpCode32SimdBinary.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdCmpZ.cs b/src/ARMeilleure/Decoders/OpCode32SimdCmpZ.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdCmpZ.cs rename to src/ARMeilleure/Decoders/OpCode32SimdCmpZ.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs b/src/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdCvtFI.cs rename to src/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs b/src/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdCvtTB.cs rename to src/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdDupElem.cs b/src/ARMeilleure/Decoders/OpCode32SimdDupElem.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdDupElem.cs rename to src/ARMeilleure/Decoders/OpCode32SimdDupElem.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdDupGP.cs b/src/ARMeilleure/Decoders/OpCode32SimdDupGP.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdDupGP.cs rename to src/ARMeilleure/Decoders/OpCode32SimdDupGP.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdExt.cs b/src/ARMeilleure/Decoders/OpCode32SimdExt.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdExt.cs rename to src/ARMeilleure/Decoders/OpCode32SimdExt.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdImm.cs b/src/ARMeilleure/Decoders/OpCode32SimdImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdImm.cs rename to src/ARMeilleure/Decoders/OpCode32SimdImm.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdImm44.cs b/src/ARMeilleure/Decoders/OpCode32SimdImm44.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdImm44.cs rename to src/ARMeilleure/Decoders/OpCode32SimdImm44.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdLong.cs b/src/ARMeilleure/Decoders/OpCode32SimdLong.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdLong.cs rename to src/ARMeilleure/Decoders/OpCode32SimdLong.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMemImm.cs b/src/ARMeilleure/Decoders/OpCode32SimdMemImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMemImm.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMemImm.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMemMult.cs b/src/ARMeilleure/Decoders/OpCode32SimdMemMult.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMemMult.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMemMult.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMemPair.cs b/src/ARMeilleure/Decoders/OpCode32SimdMemPair.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMemPair.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMemPair.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMemSingle.cs b/src/ARMeilleure/Decoders/OpCode32SimdMemSingle.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMemSingle.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMemSingle.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMovGp.cs b/src/ARMeilleure/Decoders/OpCode32SimdMovGp.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMovGp.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMovGp.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMovGpDouble.cs b/src/ARMeilleure/Decoders/OpCode32SimdMovGpDouble.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMovGpDouble.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMovGpDouble.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMovGpElem.cs b/src/ARMeilleure/Decoders/OpCode32SimdMovGpElem.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMovGpElem.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMovGpElem.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdMovn.cs b/src/ARMeilleure/Decoders/OpCode32SimdMovn.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdMovn.cs rename to src/ARMeilleure/Decoders/OpCode32SimdMovn.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdReg.cs b/src/ARMeilleure/Decoders/OpCode32SimdReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdReg.cs rename to src/ARMeilleure/Decoders/OpCode32SimdReg.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdRegElem.cs b/src/ARMeilleure/Decoders/OpCode32SimdRegElem.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdRegElem.cs rename to src/ARMeilleure/Decoders/OpCode32SimdRegElem.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdRegElemLong.cs b/src/ARMeilleure/Decoders/OpCode32SimdRegElemLong.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdRegElemLong.cs rename to src/ARMeilleure/Decoders/OpCode32SimdRegElemLong.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdRegLong.cs b/src/ARMeilleure/Decoders/OpCode32SimdRegLong.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdRegLong.cs rename to src/ARMeilleure/Decoders/OpCode32SimdRegLong.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdRegS.cs b/src/ARMeilleure/Decoders/OpCode32SimdRegS.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdRegS.cs rename to src/ARMeilleure/Decoders/OpCode32SimdRegS.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdRegWide.cs b/src/ARMeilleure/Decoders/OpCode32SimdRegWide.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdRegWide.cs rename to src/ARMeilleure/Decoders/OpCode32SimdRegWide.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdRev.cs b/src/ARMeilleure/Decoders/OpCode32SimdRev.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdRev.cs rename to src/ARMeilleure/Decoders/OpCode32SimdRev.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdS.cs b/src/ARMeilleure/Decoders/OpCode32SimdS.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdS.cs rename to src/ARMeilleure/Decoders/OpCode32SimdS.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdSel.cs b/src/ARMeilleure/Decoders/OpCode32SimdSel.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdSel.cs rename to src/ARMeilleure/Decoders/OpCode32SimdSel.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdShImm.cs b/src/ARMeilleure/Decoders/OpCode32SimdShImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdShImm.cs rename to src/ARMeilleure/Decoders/OpCode32SimdShImm.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdShImmLong.cs b/src/ARMeilleure/Decoders/OpCode32SimdShImmLong.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdShImmLong.cs rename to src/ARMeilleure/Decoders/OpCode32SimdShImmLong.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdShImmNarrow.cs b/src/ARMeilleure/Decoders/OpCode32SimdShImmNarrow.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdShImmNarrow.cs rename to src/ARMeilleure/Decoders/OpCode32SimdShImmNarrow.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdSpecial.cs b/src/ARMeilleure/Decoders/OpCode32SimdSpecial.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdSpecial.cs rename to src/ARMeilleure/Decoders/OpCode32SimdSpecial.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdSqrte.cs b/src/ARMeilleure/Decoders/OpCode32SimdSqrte.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdSqrte.cs rename to src/ARMeilleure/Decoders/OpCode32SimdSqrte.cs diff --git a/ARMeilleure/Decoders/OpCode32SimdTbl.cs b/src/ARMeilleure/Decoders/OpCode32SimdTbl.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32SimdTbl.cs rename to src/ARMeilleure/Decoders/OpCode32SimdTbl.cs diff --git a/ARMeilleure/Decoders/OpCode32System.cs b/src/ARMeilleure/Decoders/OpCode32System.cs similarity index 100% rename from ARMeilleure/Decoders/OpCode32System.cs rename to src/ARMeilleure/Decoders/OpCode32System.cs diff --git a/ARMeilleure/Decoders/OpCodeAdr.cs b/src/ARMeilleure/Decoders/OpCodeAdr.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeAdr.cs rename to src/ARMeilleure/Decoders/OpCodeAdr.cs diff --git a/ARMeilleure/Decoders/OpCodeAlu.cs b/src/ARMeilleure/Decoders/OpCodeAlu.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeAlu.cs rename to src/ARMeilleure/Decoders/OpCodeAlu.cs diff --git a/ARMeilleure/Decoders/OpCodeAluBinary.cs b/src/ARMeilleure/Decoders/OpCodeAluBinary.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeAluBinary.cs rename to src/ARMeilleure/Decoders/OpCodeAluBinary.cs diff --git a/ARMeilleure/Decoders/OpCodeAluImm.cs b/src/ARMeilleure/Decoders/OpCodeAluImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeAluImm.cs rename to src/ARMeilleure/Decoders/OpCodeAluImm.cs diff --git a/ARMeilleure/Decoders/OpCodeAluRs.cs b/src/ARMeilleure/Decoders/OpCodeAluRs.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeAluRs.cs rename to src/ARMeilleure/Decoders/OpCodeAluRs.cs diff --git a/ARMeilleure/Decoders/OpCodeAluRx.cs b/src/ARMeilleure/Decoders/OpCodeAluRx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeAluRx.cs rename to src/ARMeilleure/Decoders/OpCodeAluRx.cs diff --git a/ARMeilleure/Decoders/OpCodeBImm.cs b/src/ARMeilleure/Decoders/OpCodeBImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeBImm.cs rename to src/ARMeilleure/Decoders/OpCodeBImm.cs diff --git a/ARMeilleure/Decoders/OpCodeBImmAl.cs b/src/ARMeilleure/Decoders/OpCodeBImmAl.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeBImmAl.cs rename to src/ARMeilleure/Decoders/OpCodeBImmAl.cs diff --git a/ARMeilleure/Decoders/OpCodeBImmCmp.cs b/src/ARMeilleure/Decoders/OpCodeBImmCmp.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeBImmCmp.cs rename to src/ARMeilleure/Decoders/OpCodeBImmCmp.cs diff --git a/ARMeilleure/Decoders/OpCodeBImmCond.cs b/src/ARMeilleure/Decoders/OpCodeBImmCond.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeBImmCond.cs rename to src/ARMeilleure/Decoders/OpCodeBImmCond.cs diff --git a/ARMeilleure/Decoders/OpCodeBImmTest.cs b/src/ARMeilleure/Decoders/OpCodeBImmTest.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeBImmTest.cs rename to src/ARMeilleure/Decoders/OpCodeBImmTest.cs diff --git a/ARMeilleure/Decoders/OpCodeBReg.cs b/src/ARMeilleure/Decoders/OpCodeBReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeBReg.cs rename to src/ARMeilleure/Decoders/OpCodeBReg.cs diff --git a/ARMeilleure/Decoders/OpCodeBfm.cs b/src/ARMeilleure/Decoders/OpCodeBfm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeBfm.cs rename to src/ARMeilleure/Decoders/OpCodeBfm.cs diff --git a/ARMeilleure/Decoders/OpCodeCcmp.cs b/src/ARMeilleure/Decoders/OpCodeCcmp.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeCcmp.cs rename to src/ARMeilleure/Decoders/OpCodeCcmp.cs diff --git a/ARMeilleure/Decoders/OpCodeCcmpImm.cs b/src/ARMeilleure/Decoders/OpCodeCcmpImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeCcmpImm.cs rename to src/ARMeilleure/Decoders/OpCodeCcmpImm.cs diff --git a/ARMeilleure/Decoders/OpCodeCcmpReg.cs b/src/ARMeilleure/Decoders/OpCodeCcmpReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeCcmpReg.cs rename to src/ARMeilleure/Decoders/OpCodeCcmpReg.cs diff --git a/ARMeilleure/Decoders/OpCodeCsel.cs b/src/ARMeilleure/Decoders/OpCodeCsel.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeCsel.cs rename to src/ARMeilleure/Decoders/OpCodeCsel.cs diff --git a/ARMeilleure/Decoders/OpCodeException.cs b/src/ARMeilleure/Decoders/OpCodeException.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeException.cs rename to src/ARMeilleure/Decoders/OpCodeException.cs diff --git a/ARMeilleure/Decoders/OpCodeMem.cs b/src/ARMeilleure/Decoders/OpCodeMem.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMem.cs rename to src/ARMeilleure/Decoders/OpCodeMem.cs diff --git a/ARMeilleure/Decoders/OpCodeMemEx.cs b/src/ARMeilleure/Decoders/OpCodeMemEx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMemEx.cs rename to src/ARMeilleure/Decoders/OpCodeMemEx.cs diff --git a/ARMeilleure/Decoders/OpCodeMemImm.cs b/src/ARMeilleure/Decoders/OpCodeMemImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMemImm.cs rename to src/ARMeilleure/Decoders/OpCodeMemImm.cs diff --git a/ARMeilleure/Decoders/OpCodeMemLit.cs b/src/ARMeilleure/Decoders/OpCodeMemLit.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMemLit.cs rename to src/ARMeilleure/Decoders/OpCodeMemLit.cs diff --git a/ARMeilleure/Decoders/OpCodeMemPair.cs b/src/ARMeilleure/Decoders/OpCodeMemPair.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMemPair.cs rename to src/ARMeilleure/Decoders/OpCodeMemPair.cs diff --git a/ARMeilleure/Decoders/OpCodeMemReg.cs b/src/ARMeilleure/Decoders/OpCodeMemReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMemReg.cs rename to src/ARMeilleure/Decoders/OpCodeMemReg.cs diff --git a/ARMeilleure/Decoders/OpCodeMov.cs b/src/ARMeilleure/Decoders/OpCodeMov.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMov.cs rename to src/ARMeilleure/Decoders/OpCodeMov.cs diff --git a/ARMeilleure/Decoders/OpCodeMul.cs b/src/ARMeilleure/Decoders/OpCodeMul.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeMul.cs rename to src/ARMeilleure/Decoders/OpCodeMul.cs diff --git a/ARMeilleure/Decoders/OpCodeSimd.cs b/src/ARMeilleure/Decoders/OpCodeSimd.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimd.cs rename to src/ARMeilleure/Decoders/OpCodeSimd.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdCvt.cs b/src/ARMeilleure/Decoders/OpCodeSimdCvt.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdCvt.cs rename to src/ARMeilleure/Decoders/OpCodeSimdCvt.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdExt.cs b/src/ARMeilleure/Decoders/OpCodeSimdExt.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdExt.cs rename to src/ARMeilleure/Decoders/OpCodeSimdExt.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdFcond.cs b/src/ARMeilleure/Decoders/OpCodeSimdFcond.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdFcond.cs rename to src/ARMeilleure/Decoders/OpCodeSimdFcond.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdFmov.cs b/src/ARMeilleure/Decoders/OpCodeSimdFmov.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdFmov.cs rename to src/ARMeilleure/Decoders/OpCodeSimdFmov.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdHelper.cs b/src/ARMeilleure/Decoders/OpCodeSimdHelper.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdHelper.cs rename to src/ARMeilleure/Decoders/OpCodeSimdHelper.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdImm.cs b/src/ARMeilleure/Decoders/OpCodeSimdImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdImm.cs rename to src/ARMeilleure/Decoders/OpCodeSimdImm.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdIns.cs b/src/ARMeilleure/Decoders/OpCodeSimdIns.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdIns.cs rename to src/ARMeilleure/Decoders/OpCodeSimdIns.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdMemImm.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdMemImm.cs rename to src/ARMeilleure/Decoders/OpCodeSimdMemImm.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdMemLit.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemLit.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdMemLit.cs rename to src/ARMeilleure/Decoders/OpCodeSimdMemLit.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdMemMs.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemMs.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdMemMs.cs rename to src/ARMeilleure/Decoders/OpCodeSimdMemMs.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdMemPair.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemPair.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdMemPair.cs rename to src/ARMeilleure/Decoders/OpCodeSimdMemPair.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdMemReg.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdMemReg.cs rename to src/ARMeilleure/Decoders/OpCodeSimdMemReg.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdMemSs.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemSs.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdMemSs.cs rename to src/ARMeilleure/Decoders/OpCodeSimdMemSs.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdReg.cs b/src/ARMeilleure/Decoders/OpCodeSimdReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdReg.cs rename to src/ARMeilleure/Decoders/OpCodeSimdReg.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdRegElem.cs b/src/ARMeilleure/Decoders/OpCodeSimdRegElem.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdRegElem.cs rename to src/ARMeilleure/Decoders/OpCodeSimdRegElem.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdRegElemF.cs b/src/ARMeilleure/Decoders/OpCodeSimdRegElemF.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdRegElemF.cs rename to src/ARMeilleure/Decoders/OpCodeSimdRegElemF.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdShImm.cs b/src/ARMeilleure/Decoders/OpCodeSimdShImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdShImm.cs rename to src/ARMeilleure/Decoders/OpCodeSimdShImm.cs diff --git a/ARMeilleure/Decoders/OpCodeSimdTbl.cs b/src/ARMeilleure/Decoders/OpCodeSimdTbl.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSimdTbl.cs rename to src/ARMeilleure/Decoders/OpCodeSimdTbl.cs diff --git a/ARMeilleure/Decoders/OpCodeSystem.cs b/src/ARMeilleure/Decoders/OpCodeSystem.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeSystem.cs rename to src/ARMeilleure/Decoders/OpCodeSystem.cs diff --git a/ARMeilleure/Decoders/OpCodeT16.cs b/src/ARMeilleure/Decoders/OpCodeT16.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16.cs rename to src/ARMeilleure/Decoders/OpCodeT16.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs b/src/ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs rename to src/ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AddSubReg.cs b/src/ARMeilleure/Decoders/OpCodeT16AddSubReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AddSubReg.cs rename to src/ARMeilleure/Decoders/OpCodeT16AddSubReg.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AddSubSp.cs b/src/ARMeilleure/Decoders/OpCodeT16AddSubSp.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AddSubSp.cs rename to src/ARMeilleure/Decoders/OpCodeT16AddSubSp.cs diff --git a/ARMeilleure/Decoders/OpCodeT16Adr.cs b/src/ARMeilleure/Decoders/OpCodeT16Adr.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16Adr.cs rename to src/ARMeilleure/Decoders/OpCodeT16Adr.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AluImm8.cs b/src/ARMeilleure/Decoders/OpCodeT16AluImm8.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AluImm8.cs rename to src/ARMeilleure/Decoders/OpCodeT16AluImm8.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AluImmZero.cs b/src/ARMeilleure/Decoders/OpCodeT16AluImmZero.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AluImmZero.cs rename to src/ARMeilleure/Decoders/OpCodeT16AluImmZero.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AluRegHigh.cs b/src/ARMeilleure/Decoders/OpCodeT16AluRegHigh.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AluRegHigh.cs rename to src/ARMeilleure/Decoders/OpCodeT16AluRegHigh.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AluRegLow.cs b/src/ARMeilleure/Decoders/OpCodeT16AluRegLow.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AluRegLow.cs rename to src/ARMeilleure/Decoders/OpCodeT16AluRegLow.cs diff --git a/ARMeilleure/Decoders/OpCodeT16AluUx.cs b/src/ARMeilleure/Decoders/OpCodeT16AluUx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16AluUx.cs rename to src/ARMeilleure/Decoders/OpCodeT16AluUx.cs diff --git a/ARMeilleure/Decoders/OpCodeT16BImm11.cs b/src/ARMeilleure/Decoders/OpCodeT16BImm11.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16BImm11.cs rename to src/ARMeilleure/Decoders/OpCodeT16BImm11.cs diff --git a/ARMeilleure/Decoders/OpCodeT16BImm8.cs b/src/ARMeilleure/Decoders/OpCodeT16BImm8.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16BImm8.cs rename to src/ARMeilleure/Decoders/OpCodeT16BImm8.cs diff --git a/ARMeilleure/Decoders/OpCodeT16BImmCmp.cs b/src/ARMeilleure/Decoders/OpCodeT16BImmCmp.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16BImmCmp.cs rename to src/ARMeilleure/Decoders/OpCodeT16BImmCmp.cs diff --git a/ARMeilleure/Decoders/OpCodeT16BReg.cs b/src/ARMeilleure/Decoders/OpCodeT16BReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16BReg.cs rename to src/ARMeilleure/Decoders/OpCodeT16BReg.cs diff --git a/ARMeilleure/Decoders/OpCodeT16Exception.cs b/src/ARMeilleure/Decoders/OpCodeT16Exception.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16Exception.cs rename to src/ARMeilleure/Decoders/OpCodeT16Exception.cs diff --git a/ARMeilleure/Decoders/OpCodeT16IfThen.cs b/src/ARMeilleure/Decoders/OpCodeT16IfThen.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16IfThen.cs rename to src/ARMeilleure/Decoders/OpCodeT16IfThen.cs diff --git a/ARMeilleure/Decoders/OpCodeT16MemImm5.cs b/src/ARMeilleure/Decoders/OpCodeT16MemImm5.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16MemImm5.cs rename to src/ARMeilleure/Decoders/OpCodeT16MemImm5.cs diff --git a/ARMeilleure/Decoders/OpCodeT16MemLit.cs b/src/ARMeilleure/Decoders/OpCodeT16MemLit.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16MemLit.cs rename to src/ARMeilleure/Decoders/OpCodeT16MemLit.cs diff --git a/ARMeilleure/Decoders/OpCodeT16MemMult.cs b/src/ARMeilleure/Decoders/OpCodeT16MemMult.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16MemMult.cs rename to src/ARMeilleure/Decoders/OpCodeT16MemMult.cs diff --git a/ARMeilleure/Decoders/OpCodeT16MemReg.cs b/src/ARMeilleure/Decoders/OpCodeT16MemReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16MemReg.cs rename to src/ARMeilleure/Decoders/OpCodeT16MemReg.cs diff --git a/ARMeilleure/Decoders/OpCodeT16MemSp.cs b/src/ARMeilleure/Decoders/OpCodeT16MemSp.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16MemSp.cs rename to src/ARMeilleure/Decoders/OpCodeT16MemSp.cs diff --git a/ARMeilleure/Decoders/OpCodeT16MemStack.cs b/src/ARMeilleure/Decoders/OpCodeT16MemStack.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16MemStack.cs rename to src/ARMeilleure/Decoders/OpCodeT16MemStack.cs diff --git a/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs b/src/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16ShiftImm.cs rename to src/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs diff --git a/ARMeilleure/Decoders/OpCodeT16ShiftReg.cs b/src/ARMeilleure/Decoders/OpCodeT16ShiftReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16ShiftReg.cs rename to src/ARMeilleure/Decoders/OpCodeT16ShiftReg.cs diff --git a/ARMeilleure/Decoders/OpCodeT16SpRel.cs b/src/ARMeilleure/Decoders/OpCodeT16SpRel.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT16SpRel.cs rename to src/ARMeilleure/Decoders/OpCodeT16SpRel.cs diff --git a/ARMeilleure/Decoders/OpCodeT32.cs b/src/ARMeilleure/Decoders/OpCodeT32.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32.cs rename to src/ARMeilleure/Decoders/OpCodeT32.cs diff --git a/ARMeilleure/Decoders/OpCodeT32Alu.cs b/src/ARMeilleure/Decoders/OpCodeT32Alu.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32Alu.cs rename to src/ARMeilleure/Decoders/OpCodeT32Alu.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluBf.cs b/src/ARMeilleure/Decoders/OpCodeT32AluBf.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluBf.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluBf.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluImm.cs b/src/ARMeilleure/Decoders/OpCodeT32AluImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluImm.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluImm.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluImm12.cs b/src/ARMeilleure/Decoders/OpCodeT32AluImm12.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluImm12.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluImm12.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluMla.cs b/src/ARMeilleure/Decoders/OpCodeT32AluMla.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluMla.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluMla.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluReg.cs b/src/ARMeilleure/Decoders/OpCodeT32AluReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluReg.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluReg.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs b/src/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluRsImm.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluUmull.cs b/src/ARMeilleure/Decoders/OpCodeT32AluUmull.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluUmull.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluUmull.cs diff --git a/ARMeilleure/Decoders/OpCodeT32AluUx.cs b/src/ARMeilleure/Decoders/OpCodeT32AluUx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32AluUx.cs rename to src/ARMeilleure/Decoders/OpCodeT32AluUx.cs diff --git a/ARMeilleure/Decoders/OpCodeT32BImm20.cs b/src/ARMeilleure/Decoders/OpCodeT32BImm20.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32BImm20.cs rename to src/ARMeilleure/Decoders/OpCodeT32BImm20.cs diff --git a/ARMeilleure/Decoders/OpCodeT32BImm24.cs b/src/ARMeilleure/Decoders/OpCodeT32BImm24.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32BImm24.cs rename to src/ARMeilleure/Decoders/OpCodeT32BImm24.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MemImm12.cs b/src/ARMeilleure/Decoders/OpCodeT32MemImm12.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MemImm12.cs rename to src/ARMeilleure/Decoders/OpCodeT32MemImm12.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MemImm8.cs b/src/ARMeilleure/Decoders/OpCodeT32MemImm8.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MemImm8.cs rename to src/ARMeilleure/Decoders/OpCodeT32MemImm8.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs b/src/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MemImm8D.cs rename to src/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MemLdEx.cs b/src/ARMeilleure/Decoders/OpCodeT32MemLdEx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MemLdEx.cs rename to src/ARMeilleure/Decoders/OpCodeT32MemLdEx.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MemMult.cs b/src/ARMeilleure/Decoders/OpCodeT32MemMult.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MemMult.cs rename to src/ARMeilleure/Decoders/OpCodeT32MemMult.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MemRsImm.cs b/src/ARMeilleure/Decoders/OpCodeT32MemRsImm.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MemRsImm.cs rename to src/ARMeilleure/Decoders/OpCodeT32MemRsImm.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MemStEx.cs b/src/ARMeilleure/Decoders/OpCodeT32MemStEx.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MemStEx.cs rename to src/ARMeilleure/Decoders/OpCodeT32MemStEx.cs diff --git a/ARMeilleure/Decoders/OpCodeT32MovImm16.cs b/src/ARMeilleure/Decoders/OpCodeT32MovImm16.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32MovImm16.cs rename to src/ARMeilleure/Decoders/OpCodeT32MovImm16.cs diff --git a/ARMeilleure/Decoders/OpCodeT32ShiftReg.cs b/src/ARMeilleure/Decoders/OpCodeT32ShiftReg.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32ShiftReg.cs rename to src/ARMeilleure/Decoders/OpCodeT32ShiftReg.cs diff --git a/ARMeilleure/Decoders/OpCodeT32Tb.cs b/src/ARMeilleure/Decoders/OpCodeT32Tb.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeT32Tb.cs rename to src/ARMeilleure/Decoders/OpCodeT32Tb.cs diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/src/ARMeilleure/Decoders/OpCodeTable.cs similarity index 100% rename from ARMeilleure/Decoders/OpCodeTable.cs rename to src/ARMeilleure/Decoders/OpCodeTable.cs diff --git a/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs b/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs similarity index 100% rename from ARMeilleure/Decoders/Optimizations/TailCallRemover.cs rename to src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs diff --git a/ARMeilleure/Decoders/RegisterSize.cs b/src/ARMeilleure/Decoders/RegisterSize.cs similarity index 100% rename from ARMeilleure/Decoders/RegisterSize.cs rename to src/ARMeilleure/Decoders/RegisterSize.cs diff --git a/ARMeilleure/Decoders/ShiftType.cs b/src/ARMeilleure/Decoders/ShiftType.cs similarity index 100% rename from ARMeilleure/Decoders/ShiftType.cs rename to src/ARMeilleure/Decoders/ShiftType.cs diff --git a/ARMeilleure/Diagnostics/IRDumper.cs b/src/ARMeilleure/Diagnostics/IRDumper.cs similarity index 100% rename from ARMeilleure/Diagnostics/IRDumper.cs rename to src/ARMeilleure/Diagnostics/IRDumper.cs diff --git a/ARMeilleure/Diagnostics/Logger.cs b/src/ARMeilleure/Diagnostics/Logger.cs similarity index 100% rename from ARMeilleure/Diagnostics/Logger.cs rename to src/ARMeilleure/Diagnostics/Logger.cs diff --git a/ARMeilleure/Diagnostics/PassName.cs b/src/ARMeilleure/Diagnostics/PassName.cs similarity index 100% rename from ARMeilleure/Diagnostics/PassName.cs rename to src/ARMeilleure/Diagnostics/PassName.cs diff --git a/ARMeilleure/Diagnostics/Symbols.cs b/src/ARMeilleure/Diagnostics/Symbols.cs similarity index 100% rename from ARMeilleure/Diagnostics/Symbols.cs rename to src/ARMeilleure/Diagnostics/Symbols.cs diff --git a/ARMeilleure/Diagnostics/TranslatorEventSource.cs b/src/ARMeilleure/Diagnostics/TranslatorEventSource.cs similarity index 100% rename from ARMeilleure/Diagnostics/TranslatorEventSource.cs rename to src/ARMeilleure/Diagnostics/TranslatorEventSource.cs diff --git a/ARMeilleure/Instructions/CryptoHelper.cs b/src/ARMeilleure/Instructions/CryptoHelper.cs similarity index 100% rename from ARMeilleure/Instructions/CryptoHelper.cs rename to src/ARMeilleure/Instructions/CryptoHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitAlu.cs b/src/ARMeilleure/Instructions/InstEmitAlu.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitAlu.cs rename to src/ARMeilleure/Instructions/InstEmitAlu.cs diff --git a/ARMeilleure/Instructions/InstEmitAlu32.cs b/src/ARMeilleure/Instructions/InstEmitAlu32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitAlu32.cs rename to src/ARMeilleure/Instructions/InstEmitAlu32.cs diff --git a/ARMeilleure/Instructions/InstEmitAluHelper.cs b/src/ARMeilleure/Instructions/InstEmitAluHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitAluHelper.cs rename to src/ARMeilleure/Instructions/InstEmitAluHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitBfm.cs b/src/ARMeilleure/Instructions/InstEmitBfm.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitBfm.cs rename to src/ARMeilleure/Instructions/InstEmitBfm.cs diff --git a/ARMeilleure/Instructions/InstEmitCcmp.cs b/src/ARMeilleure/Instructions/InstEmitCcmp.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitCcmp.cs rename to src/ARMeilleure/Instructions/InstEmitCcmp.cs diff --git a/ARMeilleure/Instructions/InstEmitCsel.cs b/src/ARMeilleure/Instructions/InstEmitCsel.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitCsel.cs rename to src/ARMeilleure/Instructions/InstEmitCsel.cs diff --git a/ARMeilleure/Instructions/InstEmitDiv.cs b/src/ARMeilleure/Instructions/InstEmitDiv.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitDiv.cs rename to src/ARMeilleure/Instructions/InstEmitDiv.cs diff --git a/ARMeilleure/Instructions/InstEmitException.cs b/src/ARMeilleure/Instructions/InstEmitException.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitException.cs rename to src/ARMeilleure/Instructions/InstEmitException.cs diff --git a/ARMeilleure/Instructions/InstEmitException32.cs b/src/ARMeilleure/Instructions/InstEmitException32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitException32.cs rename to src/ARMeilleure/Instructions/InstEmitException32.cs diff --git a/ARMeilleure/Instructions/InstEmitFlow.cs b/src/ARMeilleure/Instructions/InstEmitFlow.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitFlow.cs rename to src/ARMeilleure/Instructions/InstEmitFlow.cs diff --git a/ARMeilleure/Instructions/InstEmitFlow32.cs b/src/ARMeilleure/Instructions/InstEmitFlow32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitFlow32.cs rename to src/ARMeilleure/Instructions/InstEmitFlow32.cs diff --git a/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitFlowHelper.cs rename to src/ARMeilleure/Instructions/InstEmitFlowHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitHash.cs b/src/ARMeilleure/Instructions/InstEmitHash.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitHash.cs rename to src/ARMeilleure/Instructions/InstEmitHash.cs diff --git a/ARMeilleure/Instructions/InstEmitHash32.cs b/src/ARMeilleure/Instructions/InstEmitHash32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitHash32.cs rename to src/ARMeilleure/Instructions/InstEmitHash32.cs diff --git a/ARMeilleure/Instructions/InstEmitHashHelper.cs b/src/ARMeilleure/Instructions/InstEmitHashHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitHashHelper.cs rename to src/ARMeilleure/Instructions/InstEmitHashHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitHelper.cs b/src/ARMeilleure/Instructions/InstEmitHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitHelper.cs rename to src/ARMeilleure/Instructions/InstEmitHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitMemory.cs b/src/ARMeilleure/Instructions/InstEmitMemory.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMemory.cs rename to src/ARMeilleure/Instructions/InstEmitMemory.cs diff --git a/ARMeilleure/Instructions/InstEmitMemory32.cs b/src/ARMeilleure/Instructions/InstEmitMemory32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMemory32.cs rename to src/ARMeilleure/Instructions/InstEmitMemory32.cs diff --git a/ARMeilleure/Instructions/InstEmitMemoryEx.cs b/src/ARMeilleure/Instructions/InstEmitMemoryEx.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMemoryEx.cs rename to src/ARMeilleure/Instructions/InstEmitMemoryEx.cs diff --git a/ARMeilleure/Instructions/InstEmitMemoryEx32.cs b/src/ARMeilleure/Instructions/InstEmitMemoryEx32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMemoryEx32.cs rename to src/ARMeilleure/Instructions/InstEmitMemoryEx32.cs diff --git a/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs b/src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMemoryExHelper.cs rename to src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMemoryHelper.cs rename to src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitMove.cs b/src/ARMeilleure/Instructions/InstEmitMove.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMove.cs rename to src/ARMeilleure/Instructions/InstEmitMove.cs diff --git a/ARMeilleure/Instructions/InstEmitMul.cs b/src/ARMeilleure/Instructions/InstEmitMul.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMul.cs rename to src/ARMeilleure/Instructions/InstEmitMul.cs diff --git a/ARMeilleure/Instructions/InstEmitMul32.cs b/src/ARMeilleure/Instructions/InstEmitMul32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitMul32.cs rename to src/ARMeilleure/Instructions/InstEmitMul32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdArithmetic.cs rename to src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/src/ARMeilleure/Instructions/InstEmitSimdCmp.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdCmp.cs rename to src/ARMeilleure/Instructions/InstEmitSimdCmp.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCmp32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdCmp32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdCmp32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdCrypto.cs b/src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdCrypto.cs rename to src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdCrypto32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt.cs b/src/ARMeilleure/Instructions/InstEmitSimdCvt.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdCvt.cs rename to src/ARMeilleure/Instructions/InstEmitSimdCvt.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdCvt32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdHash.cs b/src/ARMeilleure/Instructions/InstEmitSimdHash.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdHash.cs rename to src/ARMeilleure/Instructions/InstEmitSimdHash.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdHash32.cs b/src/ARMeilleure/Instructions/InstEmitSimdHash32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdHash32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdHash32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs b/src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdHashHelper.cs rename to src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdHelper.cs rename to src/ARMeilleure/Instructions/InstEmitSimdHelper.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdHelper32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs rename to src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs rename to src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical.cs b/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdLogical.cs rename to src/ARMeilleure/Instructions/InstEmitSimdLogical.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdLogical32.cs b/src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdLogical32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdMemory.cs b/src/ARMeilleure/Instructions/InstEmitSimdMemory.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdMemory.cs rename to src/ARMeilleure/Instructions/InstEmitSimdMemory.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdMemory32.cs b/src/ARMeilleure/Instructions/InstEmitSimdMemory32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdMemory32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdMemory32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdMove.cs b/src/ARMeilleure/Instructions/InstEmitSimdMove.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdMove.cs rename to src/ARMeilleure/Instructions/InstEmitSimdMove.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdMove32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdMove32.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdShift.cs b/src/ARMeilleure/Instructions/InstEmitSimdShift.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdShift.cs rename to src/ARMeilleure/Instructions/InstEmitSimdShift.cs diff --git a/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSimdShift32.cs rename to src/ARMeilleure/Instructions/InstEmitSimdShift32.cs diff --git a/ARMeilleure/Instructions/InstEmitSystem.cs b/src/ARMeilleure/Instructions/InstEmitSystem.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSystem.cs rename to src/ARMeilleure/Instructions/InstEmitSystem.cs diff --git a/ARMeilleure/Instructions/InstEmitSystem32.cs b/src/ARMeilleure/Instructions/InstEmitSystem32.cs similarity index 100% rename from ARMeilleure/Instructions/InstEmitSystem32.cs rename to src/ARMeilleure/Instructions/InstEmitSystem32.cs diff --git a/ARMeilleure/Instructions/InstName.cs b/src/ARMeilleure/Instructions/InstName.cs similarity index 100% rename from ARMeilleure/Instructions/InstName.cs rename to src/ARMeilleure/Instructions/InstName.cs diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/src/ARMeilleure/Instructions/NativeInterface.cs similarity index 100% rename from ARMeilleure/Instructions/NativeInterface.cs rename to src/ARMeilleure/Instructions/NativeInterface.cs diff --git a/ARMeilleure/Instructions/SoftFallback.cs b/src/ARMeilleure/Instructions/SoftFallback.cs similarity index 100% rename from ARMeilleure/Instructions/SoftFallback.cs rename to src/ARMeilleure/Instructions/SoftFallback.cs diff --git a/ARMeilleure/Instructions/SoftFloat.cs b/src/ARMeilleure/Instructions/SoftFloat.cs similarity index 100% rename from ARMeilleure/Instructions/SoftFloat.cs rename to src/ARMeilleure/Instructions/SoftFloat.cs diff --git a/ARMeilleure/IntermediateRepresentation/BasicBlock.cs b/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/BasicBlock.cs rename to src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs diff --git a/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs b/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs rename to src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs diff --git a/ARMeilleure/IntermediateRepresentation/Comparison.cs b/src/ARMeilleure/IntermediateRepresentation/Comparison.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/Comparison.cs rename to src/ARMeilleure/IntermediateRepresentation/Comparison.cs diff --git a/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs b/src/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs rename to src/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs diff --git a/ARMeilleure/IntermediateRepresentation/Instruction.cs b/src/ARMeilleure/IntermediateRepresentation/Instruction.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/Instruction.cs rename to src/ARMeilleure/IntermediateRepresentation/Instruction.cs diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/Intrinsic.cs rename to src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs diff --git a/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs b/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/IntrusiveList.cs rename to src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs diff --git a/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs b/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/MemoryOperand.cs rename to src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs diff --git a/ARMeilleure/IntermediateRepresentation/Multiplier.cs b/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/Multiplier.cs rename to src/ARMeilleure/IntermediateRepresentation/Multiplier.cs diff --git a/ARMeilleure/IntermediateRepresentation/Operand.cs b/src/ARMeilleure/IntermediateRepresentation/Operand.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/Operand.cs rename to src/ARMeilleure/IntermediateRepresentation/Operand.cs diff --git a/ARMeilleure/IntermediateRepresentation/OperandKind.cs b/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/OperandKind.cs rename to src/ARMeilleure/IntermediateRepresentation/OperandKind.cs diff --git a/ARMeilleure/IntermediateRepresentation/OperandType.cs b/src/ARMeilleure/IntermediateRepresentation/OperandType.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/OperandType.cs rename to src/ARMeilleure/IntermediateRepresentation/OperandType.cs diff --git a/ARMeilleure/IntermediateRepresentation/Operation.cs b/src/ARMeilleure/IntermediateRepresentation/Operation.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/Operation.cs rename to src/ARMeilleure/IntermediateRepresentation/Operation.cs diff --git a/ARMeilleure/IntermediateRepresentation/PhiOperation.cs b/src/ARMeilleure/IntermediateRepresentation/PhiOperation.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/PhiOperation.cs rename to src/ARMeilleure/IntermediateRepresentation/PhiOperation.cs diff --git a/ARMeilleure/IntermediateRepresentation/Register.cs b/src/ARMeilleure/IntermediateRepresentation/Register.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/Register.cs rename to src/ARMeilleure/IntermediateRepresentation/Register.cs diff --git a/ARMeilleure/IntermediateRepresentation/RegisterType.cs b/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs similarity index 100% rename from ARMeilleure/IntermediateRepresentation/RegisterType.cs rename to src/ARMeilleure/IntermediateRepresentation/RegisterType.cs diff --git a/ARMeilleure/Memory/IJitMemoryAllocator.cs b/src/ARMeilleure/Memory/IJitMemoryAllocator.cs similarity index 100% rename from ARMeilleure/Memory/IJitMemoryAllocator.cs rename to src/ARMeilleure/Memory/IJitMemoryAllocator.cs diff --git a/ARMeilleure/Memory/IJitMemoryBlock.cs b/src/ARMeilleure/Memory/IJitMemoryBlock.cs similarity index 100% rename from ARMeilleure/Memory/IJitMemoryBlock.cs rename to src/ARMeilleure/Memory/IJitMemoryBlock.cs diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/src/ARMeilleure/Memory/IMemoryManager.cs similarity index 100% rename from ARMeilleure/Memory/IMemoryManager.cs rename to src/ARMeilleure/Memory/IMemoryManager.cs diff --git a/ARMeilleure/Memory/InvalidAccessException.cs b/src/ARMeilleure/Memory/InvalidAccessException.cs similarity index 100% rename from ARMeilleure/Memory/InvalidAccessException.cs rename to src/ARMeilleure/Memory/InvalidAccessException.cs diff --git a/ARMeilleure/Memory/MemoryManagerType.cs b/src/ARMeilleure/Memory/MemoryManagerType.cs similarity index 100% rename from ARMeilleure/Memory/MemoryManagerType.cs rename to src/ARMeilleure/Memory/MemoryManagerType.cs diff --git a/ARMeilleure/Memory/ReservedRegion.cs b/src/ARMeilleure/Memory/ReservedRegion.cs similarity index 100% rename from ARMeilleure/Memory/ReservedRegion.cs rename to src/ARMeilleure/Memory/ReservedRegion.cs diff --git a/ARMeilleure/Native/JitSupportDarwin.cs b/src/ARMeilleure/Native/JitSupportDarwin.cs similarity index 100% rename from ARMeilleure/Native/JitSupportDarwin.cs rename to src/ARMeilleure/Native/JitSupportDarwin.cs diff --git a/ARMeilleure/Native/libs/libarmeilleure-jitsupport.dylib b/src/ARMeilleure/Native/libs/libarmeilleure-jitsupport.dylib similarity index 100% rename from ARMeilleure/Native/libs/libarmeilleure-jitsupport.dylib rename to src/ARMeilleure/Native/libs/libarmeilleure-jitsupport.dylib diff --git a/ARMeilleure/Native/macos_jit_support/Makefile b/src/ARMeilleure/Native/macos_jit_support/Makefile similarity index 100% rename from ARMeilleure/Native/macos_jit_support/Makefile rename to src/ARMeilleure/Native/macos_jit_support/Makefile diff --git a/ARMeilleure/Native/macos_jit_support/support.c b/src/ARMeilleure/Native/macos_jit_support/support.c similarity index 100% rename from ARMeilleure/Native/macos_jit_support/support.c rename to src/ARMeilleure/Native/macos_jit_support/support.c diff --git a/ARMeilleure/Optimizations.cs b/src/ARMeilleure/Optimizations.cs similarity index 100% rename from ARMeilleure/Optimizations.cs rename to src/ARMeilleure/Optimizations.cs diff --git a/ARMeilleure/Signal/NativeSignalHandler.cs b/src/ARMeilleure/Signal/NativeSignalHandler.cs similarity index 100% rename from ARMeilleure/Signal/NativeSignalHandler.cs rename to src/ARMeilleure/Signal/NativeSignalHandler.cs diff --git a/ARMeilleure/Signal/TestMethods.cs b/src/ARMeilleure/Signal/TestMethods.cs similarity index 100% rename from ARMeilleure/Signal/TestMethods.cs rename to src/ARMeilleure/Signal/TestMethods.cs diff --git a/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs b/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs similarity index 100% rename from ARMeilleure/Signal/UnixSignalHandlerRegistration.cs rename to src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs diff --git a/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs b/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs similarity index 100% rename from ARMeilleure/Signal/WindowsPartialUnmapHandler.cs rename to src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs diff --git a/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs b/src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs similarity index 100% rename from ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs rename to src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs diff --git a/ARMeilleure/State/Aarch32Mode.cs b/src/ARMeilleure/State/Aarch32Mode.cs similarity index 100% rename from ARMeilleure/State/Aarch32Mode.cs rename to src/ARMeilleure/State/Aarch32Mode.cs diff --git a/ARMeilleure/State/ExceptionCallback.cs b/src/ARMeilleure/State/ExceptionCallback.cs similarity index 100% rename from ARMeilleure/State/ExceptionCallback.cs rename to src/ARMeilleure/State/ExceptionCallback.cs diff --git a/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs similarity index 100% rename from ARMeilleure/State/ExecutionContext.cs rename to src/ARMeilleure/State/ExecutionContext.cs diff --git a/ARMeilleure/State/ExecutionMode.cs b/src/ARMeilleure/State/ExecutionMode.cs similarity index 100% rename from ARMeilleure/State/ExecutionMode.cs rename to src/ARMeilleure/State/ExecutionMode.cs diff --git a/ARMeilleure/State/FPCR.cs b/src/ARMeilleure/State/FPCR.cs similarity index 100% rename from ARMeilleure/State/FPCR.cs rename to src/ARMeilleure/State/FPCR.cs diff --git a/ARMeilleure/State/FPException.cs b/src/ARMeilleure/State/FPException.cs similarity index 100% rename from ARMeilleure/State/FPException.cs rename to src/ARMeilleure/State/FPException.cs diff --git a/ARMeilleure/State/FPRoundingMode.cs b/src/ARMeilleure/State/FPRoundingMode.cs similarity index 100% rename from ARMeilleure/State/FPRoundingMode.cs rename to src/ARMeilleure/State/FPRoundingMode.cs diff --git a/ARMeilleure/State/FPSCR.cs b/src/ARMeilleure/State/FPSCR.cs similarity index 100% rename from ARMeilleure/State/FPSCR.cs rename to src/ARMeilleure/State/FPSCR.cs diff --git a/ARMeilleure/State/FPSR.cs b/src/ARMeilleure/State/FPSR.cs similarity index 100% rename from ARMeilleure/State/FPSR.cs rename to src/ARMeilleure/State/FPSR.cs diff --git a/ARMeilleure/State/FPState.cs b/src/ARMeilleure/State/FPState.cs similarity index 100% rename from ARMeilleure/State/FPState.cs rename to src/ARMeilleure/State/FPState.cs diff --git a/ARMeilleure/State/FPType.cs b/src/ARMeilleure/State/FPType.cs similarity index 100% rename from ARMeilleure/State/FPType.cs rename to src/ARMeilleure/State/FPType.cs diff --git a/ARMeilleure/State/ICounter.cs b/src/ARMeilleure/State/ICounter.cs similarity index 100% rename from ARMeilleure/State/ICounter.cs rename to src/ARMeilleure/State/ICounter.cs diff --git a/ARMeilleure/State/NativeContext.cs b/src/ARMeilleure/State/NativeContext.cs similarity index 100% rename from ARMeilleure/State/NativeContext.cs rename to src/ARMeilleure/State/NativeContext.cs diff --git a/ARMeilleure/State/PState.cs b/src/ARMeilleure/State/PState.cs similarity index 100% rename from ARMeilleure/State/PState.cs rename to src/ARMeilleure/State/PState.cs diff --git a/ARMeilleure/State/RegisterAlias.cs b/src/ARMeilleure/State/RegisterAlias.cs similarity index 100% rename from ARMeilleure/State/RegisterAlias.cs rename to src/ARMeilleure/State/RegisterAlias.cs diff --git a/ARMeilleure/State/RegisterConsts.cs b/src/ARMeilleure/State/RegisterConsts.cs similarity index 100% rename from ARMeilleure/State/RegisterConsts.cs rename to src/ARMeilleure/State/RegisterConsts.cs diff --git a/ARMeilleure/State/V128.cs b/src/ARMeilleure/State/V128.cs similarity index 100% rename from ARMeilleure/State/V128.cs rename to src/ARMeilleure/State/V128.cs diff --git a/ARMeilleure/Statistics.cs b/src/ARMeilleure/Statistics.cs similarity index 100% rename from ARMeilleure/Statistics.cs rename to src/ARMeilleure/Statistics.cs diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/src/ARMeilleure/Translation/ArmEmitterContext.cs similarity index 100% rename from ARMeilleure/Translation/ArmEmitterContext.cs rename to src/ARMeilleure/Translation/ArmEmitterContext.cs diff --git a/ARMeilleure/Translation/Cache/CacheEntry.cs b/src/ARMeilleure/Translation/Cache/CacheEntry.cs similarity index 100% rename from ARMeilleure/Translation/Cache/CacheEntry.cs rename to src/ARMeilleure/Translation/Cache/CacheEntry.cs diff --git a/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs b/src/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs similarity index 100% rename from ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs rename to src/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs diff --git a/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs similarity index 100% rename from ARMeilleure/Translation/Cache/JitCache.cs rename to src/ARMeilleure/Translation/Cache/JitCache.cs diff --git a/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs b/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs similarity index 100% rename from ARMeilleure/Translation/Cache/JitCacheInvalidation.cs rename to src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs diff --git a/ARMeilleure/Translation/Cache/JitUnwindWindows.cs b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs similarity index 100% rename from ARMeilleure/Translation/Cache/JitUnwindWindows.cs rename to src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs diff --git a/ARMeilleure/Translation/Compiler.cs b/src/ARMeilleure/Translation/Compiler.cs similarity index 100% rename from ARMeilleure/Translation/Compiler.cs rename to src/ARMeilleure/Translation/Compiler.cs diff --git a/ARMeilleure/Translation/CompilerContext.cs b/src/ARMeilleure/Translation/CompilerContext.cs similarity index 100% rename from ARMeilleure/Translation/CompilerContext.cs rename to src/ARMeilleure/Translation/CompilerContext.cs diff --git a/ARMeilleure/Translation/CompilerOptions.cs b/src/ARMeilleure/Translation/CompilerOptions.cs similarity index 100% rename from ARMeilleure/Translation/CompilerOptions.cs rename to src/ARMeilleure/Translation/CompilerOptions.cs diff --git a/ARMeilleure/Translation/ControlFlowGraph.cs b/src/ARMeilleure/Translation/ControlFlowGraph.cs similarity index 100% rename from ARMeilleure/Translation/ControlFlowGraph.cs rename to src/ARMeilleure/Translation/ControlFlowGraph.cs diff --git a/ARMeilleure/Translation/DelegateHelper.cs b/src/ARMeilleure/Translation/DelegateHelper.cs similarity index 100% rename from ARMeilleure/Translation/DelegateHelper.cs rename to src/ARMeilleure/Translation/DelegateHelper.cs diff --git a/ARMeilleure/Translation/DelegateInfo.cs b/src/ARMeilleure/Translation/DelegateInfo.cs similarity index 100% rename from ARMeilleure/Translation/DelegateInfo.cs rename to src/ARMeilleure/Translation/DelegateInfo.cs diff --git a/ARMeilleure/Translation/Delegates.cs b/src/ARMeilleure/Translation/Delegates.cs similarity index 100% rename from ARMeilleure/Translation/Delegates.cs rename to src/ARMeilleure/Translation/Delegates.cs diff --git a/ARMeilleure/Translation/DispatcherFunction.cs b/src/ARMeilleure/Translation/DispatcherFunction.cs similarity index 100% rename from ARMeilleure/Translation/DispatcherFunction.cs rename to src/ARMeilleure/Translation/DispatcherFunction.cs diff --git a/ARMeilleure/Translation/Dominance.cs b/src/ARMeilleure/Translation/Dominance.cs similarity index 100% rename from ARMeilleure/Translation/Dominance.cs rename to src/ARMeilleure/Translation/Dominance.cs diff --git a/ARMeilleure/Translation/EmitterContext.cs b/src/ARMeilleure/Translation/EmitterContext.cs similarity index 100% rename from ARMeilleure/Translation/EmitterContext.cs rename to src/ARMeilleure/Translation/EmitterContext.cs diff --git a/ARMeilleure/Translation/GuestFunction.cs b/src/ARMeilleure/Translation/GuestFunction.cs similarity index 100% rename from ARMeilleure/Translation/GuestFunction.cs rename to src/ARMeilleure/Translation/GuestFunction.cs diff --git a/ARMeilleure/Translation/IntervalTree.cs b/src/ARMeilleure/Translation/IntervalTree.cs similarity index 100% rename from ARMeilleure/Translation/IntervalTree.cs rename to src/ARMeilleure/Translation/IntervalTree.cs diff --git a/ARMeilleure/Translation/PTC/EncodingCache.cs b/src/ARMeilleure/Translation/PTC/EncodingCache.cs similarity index 100% rename from ARMeilleure/Translation/PTC/EncodingCache.cs rename to src/ARMeilleure/Translation/PTC/EncodingCache.cs diff --git a/ARMeilleure/Translation/PTC/IPtcLoadState.cs b/src/ARMeilleure/Translation/PTC/IPtcLoadState.cs similarity index 100% rename from ARMeilleure/Translation/PTC/IPtcLoadState.cs rename to src/ARMeilleure/Translation/PTC/IPtcLoadState.cs diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs similarity index 100% rename from ARMeilleure/Translation/PTC/Ptc.cs rename to src/ARMeilleure/Translation/PTC/Ptc.cs diff --git a/ARMeilleure/Translation/PTC/PtcFormatter.cs b/src/ARMeilleure/Translation/PTC/PtcFormatter.cs similarity index 100% rename from ARMeilleure/Translation/PTC/PtcFormatter.cs rename to src/ARMeilleure/Translation/PTC/PtcFormatter.cs diff --git a/ARMeilleure/Translation/PTC/PtcLoadingState.cs b/src/ARMeilleure/Translation/PTC/PtcLoadingState.cs similarity index 100% rename from ARMeilleure/Translation/PTC/PtcLoadingState.cs rename to src/ARMeilleure/Translation/PTC/PtcLoadingState.cs diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs similarity index 100% rename from ARMeilleure/Translation/PTC/PtcProfiler.cs rename to src/ARMeilleure/Translation/PTC/PtcProfiler.cs diff --git a/ARMeilleure/Translation/PTC/PtcState.cs b/src/ARMeilleure/Translation/PTC/PtcState.cs similarity index 100% rename from ARMeilleure/Translation/PTC/PtcState.cs rename to src/ARMeilleure/Translation/PTC/PtcState.cs diff --git a/ARMeilleure/Translation/RegisterToLocal.cs b/src/ARMeilleure/Translation/RegisterToLocal.cs similarity index 100% rename from ARMeilleure/Translation/RegisterToLocal.cs rename to src/ARMeilleure/Translation/RegisterToLocal.cs diff --git a/ARMeilleure/Translation/RegisterUsage.cs b/src/ARMeilleure/Translation/RegisterUsage.cs similarity index 100% rename from ARMeilleure/Translation/RegisterUsage.cs rename to src/ARMeilleure/Translation/RegisterUsage.cs diff --git a/ARMeilleure/Translation/RejitRequest.cs b/src/ARMeilleure/Translation/RejitRequest.cs similarity index 100% rename from ARMeilleure/Translation/RejitRequest.cs rename to src/ARMeilleure/Translation/RejitRequest.cs diff --git a/ARMeilleure/Translation/SsaConstruction.cs b/src/ARMeilleure/Translation/SsaConstruction.cs similarity index 100% rename from ARMeilleure/Translation/SsaConstruction.cs rename to src/ARMeilleure/Translation/SsaConstruction.cs diff --git a/ARMeilleure/Translation/SsaDeconstruction.cs b/src/ARMeilleure/Translation/SsaDeconstruction.cs similarity index 100% rename from ARMeilleure/Translation/SsaDeconstruction.cs rename to src/ARMeilleure/Translation/SsaDeconstruction.cs diff --git a/ARMeilleure/Translation/TranslatedFunction.cs b/src/ARMeilleure/Translation/TranslatedFunction.cs similarity index 100% rename from ARMeilleure/Translation/TranslatedFunction.cs rename to src/ARMeilleure/Translation/TranslatedFunction.cs diff --git a/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs similarity index 100% rename from ARMeilleure/Translation/Translator.cs rename to src/ARMeilleure/Translation/Translator.cs diff --git a/ARMeilleure/Translation/TranslatorCache.cs b/src/ARMeilleure/Translation/TranslatorCache.cs similarity index 100% rename from ARMeilleure/Translation/TranslatorCache.cs rename to src/ARMeilleure/Translation/TranslatorCache.cs diff --git a/ARMeilleure/Translation/TranslatorQueue.cs b/src/ARMeilleure/Translation/TranslatorQueue.cs similarity index 100% rename from ARMeilleure/Translation/TranslatorQueue.cs rename to src/ARMeilleure/Translation/TranslatorQueue.cs diff --git a/ARMeilleure/Translation/TranslatorStubs.cs b/src/ARMeilleure/Translation/TranslatorStubs.cs similarity index 100% rename from ARMeilleure/Translation/TranslatorStubs.cs rename to src/ARMeilleure/Translation/TranslatorStubs.cs diff --git a/ARMeilleure/Translation/TranslatorTestMethods.cs b/src/ARMeilleure/Translation/TranslatorTestMethods.cs similarity index 100% rename from ARMeilleure/Translation/TranslatorTestMethods.cs rename to src/ARMeilleure/Translation/TranslatorTestMethods.cs diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs similarity index 100% rename from Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs rename to src/Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs similarity index 100% rename from Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs rename to src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs similarity index 100% rename from Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs rename to src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs diff --git a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj b/src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj similarity index 100% rename from Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj rename to src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj diff --git a/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj b/src/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj similarity index 100% rename from Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj rename to src/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs similarity index 100% rename from Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs rename to src/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs similarity index 100% rename from Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs rename to src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs similarity index 100% rename from Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs rename to src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs rename to src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll rename to src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib rename to src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so rename to src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so diff --git a/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj b/src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj rename to src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs rename to src/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs rename to src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs similarity index 100% rename from Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs rename to src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs diff --git a/Ryujinx.Audio/AudioManager.cs b/src/Ryujinx.Audio/AudioManager.cs similarity index 100% rename from Ryujinx.Audio/AudioManager.cs rename to src/Ryujinx.Audio/AudioManager.cs diff --git a/Ryujinx.Audio/Backends/Common/BackendHelper.cs b/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs similarity index 100% rename from Ryujinx.Audio/Backends/Common/BackendHelper.cs rename to src/Ryujinx.Audio/Backends/Common/BackendHelper.cs diff --git a/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs similarity index 100% rename from Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs rename to src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs diff --git a/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs b/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs similarity index 100% rename from Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs rename to src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs similarity index 100% rename from Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs rename to src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs similarity index 100% rename from Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs rename to src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs diff --git a/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs b/src/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs similarity index 100% rename from Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs rename to src/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs similarity index 100% rename from Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs rename to src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs similarity index 100% rename from Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs rename to src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs similarity index 100% rename from Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs rename to src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs diff --git a/Ryujinx.Audio/Common/AudioBuffer.cs b/src/Ryujinx.Audio/Common/AudioBuffer.cs similarity index 100% rename from Ryujinx.Audio/Common/AudioBuffer.cs rename to src/Ryujinx.Audio/Common/AudioBuffer.cs diff --git a/Ryujinx.Audio/Common/AudioDeviceSession.cs b/src/Ryujinx.Audio/Common/AudioDeviceSession.cs similarity index 100% rename from Ryujinx.Audio/Common/AudioDeviceSession.cs rename to src/Ryujinx.Audio/Common/AudioDeviceSession.cs diff --git a/Ryujinx.Audio/Common/AudioDeviceState.cs b/src/Ryujinx.Audio/Common/AudioDeviceState.cs similarity index 100% rename from Ryujinx.Audio/Common/AudioDeviceState.cs rename to src/Ryujinx.Audio/Common/AudioDeviceState.cs diff --git a/Ryujinx.Audio/Common/AudioInputConfiguration.cs b/src/Ryujinx.Audio/Common/AudioInputConfiguration.cs similarity index 100% rename from Ryujinx.Audio/Common/AudioInputConfiguration.cs rename to src/Ryujinx.Audio/Common/AudioInputConfiguration.cs diff --git a/Ryujinx.Audio/Common/AudioOutputConfiguration.cs b/src/Ryujinx.Audio/Common/AudioOutputConfiguration.cs similarity index 100% rename from Ryujinx.Audio/Common/AudioOutputConfiguration.cs rename to src/Ryujinx.Audio/Common/AudioOutputConfiguration.cs diff --git a/Ryujinx.Audio/Common/AudioUserBuffer.cs b/src/Ryujinx.Audio/Common/AudioUserBuffer.cs similarity index 100% rename from Ryujinx.Audio/Common/AudioUserBuffer.cs rename to src/Ryujinx.Audio/Common/AudioUserBuffer.cs diff --git a/Ryujinx.Audio/Common/SampleFormat.cs b/src/Ryujinx.Audio/Common/SampleFormat.cs similarity index 100% rename from Ryujinx.Audio/Common/SampleFormat.cs rename to src/Ryujinx.Audio/Common/SampleFormat.cs diff --git a/Ryujinx.Audio/Constants.cs b/src/Ryujinx.Audio/Constants.cs similarity index 100% rename from Ryujinx.Audio/Constants.cs rename to src/Ryujinx.Audio/Constants.cs diff --git a/Ryujinx.Audio/Input/AudioInputManager.cs b/src/Ryujinx.Audio/Input/AudioInputManager.cs similarity index 100% rename from Ryujinx.Audio/Input/AudioInputManager.cs rename to src/Ryujinx.Audio/Input/AudioInputManager.cs diff --git a/Ryujinx.Audio/Input/AudioInputSystem.cs b/src/Ryujinx.Audio/Input/AudioInputSystem.cs similarity index 100% rename from Ryujinx.Audio/Input/AudioInputSystem.cs rename to src/Ryujinx.Audio/Input/AudioInputSystem.cs diff --git a/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs b/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs similarity index 100% rename from Ryujinx.Audio/Integration/HardwareDeviceImpl.cs rename to src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs diff --git a/Ryujinx.Audio/Integration/IHardwareDevice.cs b/src/Ryujinx.Audio/Integration/IHardwareDevice.cs similarity index 100% rename from Ryujinx.Audio/Integration/IHardwareDevice.cs rename to src/Ryujinx.Audio/Integration/IHardwareDevice.cs diff --git a/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs similarity index 100% rename from Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs rename to src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs diff --git a/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs b/src/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs similarity index 100% rename from Ryujinx.Audio/Integration/IHardwareDeviceSession.cs rename to src/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs diff --git a/Ryujinx.Audio/Integration/IWritableEvent.cs b/src/Ryujinx.Audio/Integration/IWritableEvent.cs similarity index 100% rename from Ryujinx.Audio/Integration/IWritableEvent.cs rename to src/Ryujinx.Audio/Integration/IWritableEvent.cs diff --git a/Ryujinx.Audio/Output/AudioOutputManager.cs b/src/Ryujinx.Audio/Output/AudioOutputManager.cs similarity index 100% rename from Ryujinx.Audio/Output/AudioOutputManager.cs rename to src/Ryujinx.Audio/Output/AudioOutputManager.cs diff --git a/Ryujinx.Audio/Output/AudioOutputSystem.cs b/src/Ryujinx.Audio/Output/AudioOutputSystem.cs similarity index 100% rename from Ryujinx.Audio/Output/AudioOutputSystem.cs rename to src/Ryujinx.Audio/Output/AudioOutputSystem.cs diff --git a/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs b/src/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs rename to src/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs diff --git a/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs b/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs rename to src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs diff --git a/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs b/src/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs rename to src/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs diff --git a/Ryujinx.Audio/Renderer/Common/EffectType.cs b/src/Ryujinx.Audio/Renderer/Common/EffectType.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/EffectType.cs rename to src/Ryujinx.Audio/Renderer/Common/EffectType.cs diff --git a/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs b/src/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs rename to src/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs diff --git a/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs b/src/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs rename to src/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs diff --git a/Ryujinx.Audio/Renderer/Common/NodeIdType.cs b/src/Ryujinx.Audio/Renderer/Common/NodeIdType.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/NodeIdType.cs rename to src/Ryujinx.Audio/Renderer/Common/NodeIdType.cs diff --git a/Ryujinx.Audio/Renderer/Common/NodeStates.cs b/src/Ryujinx.Audio/Renderer/Common/NodeStates.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/NodeStates.cs rename to src/Ryujinx.Audio/Renderer/Common/NodeStates.cs diff --git a/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs b/src/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs rename to src/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs diff --git a/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs b/src/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs rename to src/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs diff --git a/Ryujinx.Audio/Renderer/Common/PlayState.cs b/src/Ryujinx.Audio/Renderer/Common/PlayState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/PlayState.cs rename to src/Ryujinx.Audio/Renderer/Common/PlayState.cs diff --git a/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs b/src/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs rename to src/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs diff --git a/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs b/src/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs rename to src/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs diff --git a/Ryujinx.Audio/Renderer/Common/SinkType.cs b/src/Ryujinx.Audio/Renderer/Common/SinkType.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/SinkType.cs rename to src/Ryujinx.Audio/Renderer/Common/SinkType.cs diff --git a/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs b/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs rename to src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs diff --git a/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs b/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs rename to src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs diff --git a/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs b/src/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/WaveBuffer.cs rename to src/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs diff --git a/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs b/src/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs rename to src/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs diff --git a/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Device/VirtualDevice.cs rename to src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs diff --git a/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs rename to src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs diff --git a/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs rename to src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs rename to src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs rename to src/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/CompressorState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/CompressorState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/CompressorState.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/CompressorState.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs rename to src/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs diff --git a/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs rename to src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs b/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs rename to src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs rename to src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs rename to src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs rename to src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs rename to src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs rename to src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs rename to src/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/EffectState.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/EffectState.cs rename to src/Ryujinx.Audio/Renderer/Parameter/EffectState.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs rename to src/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs rename to src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs b/src/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs rename to src/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/MixParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs rename to src/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs rename to src/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs rename to src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs rename to src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs diff --git a/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs rename to src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs diff --git a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs rename to src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs diff --git a/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs rename to src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs diff --git a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/BehaviourContext.cs rename to src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs diff --git a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/CommandBuffer.cs rename to src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs diff --git a/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/CommandGenerator.cs rename to src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs rename to src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs rename to src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs rename to src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs rename to src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs rename to src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs diff --git a/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs rename to src/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs diff --git a/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs b/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs rename to src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs diff --git a/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs rename to src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs diff --git a/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs rename to src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs diff --git a/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs rename to src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs diff --git a/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs rename to src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs diff --git a/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Mix/MixState.cs rename to src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs rename to src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs diff --git a/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs b/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs rename to src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs diff --git a/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs rename to src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs diff --git a/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs rename to src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs diff --git a/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs rename to src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs diff --git a/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs rename to src/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs diff --git a/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs rename to src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs diff --git a/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs rename to src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs diff --git a/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs rename to src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs diff --git a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/StateUpdater.cs rename to src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs diff --git a/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs b/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs rename to src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs diff --git a/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs b/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs rename to src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs diff --git a/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs b/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Types/PlayState.cs rename to src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs diff --git a/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs rename to src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs diff --git a/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs rename to src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs diff --git a/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs rename to src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs diff --git a/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs rename to src/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs diff --git a/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs rename to src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs diff --git a/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs rename to src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs diff --git a/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs rename to src/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs diff --git a/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs b/src/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs rename to src/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs diff --git a/Ryujinx.Audio/Renderer/Utils/BitArray.cs b/src/Ryujinx.Audio/Renderer/Utils/BitArray.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/BitArray.cs rename to src/Ryujinx.Audio/Renderer/Utils/BitArray.cs diff --git a/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs b/src/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs rename to src/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs diff --git a/Ryujinx.Audio/Renderer/Utils/Mailbox.cs b/src/Ryujinx.Audio/Renderer/Utils/Mailbox.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/Mailbox.cs rename to src/Ryujinx.Audio/Renderer/Utils/Mailbox.cs diff --git a/Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs rename to src/Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs diff --git a/Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs rename to src/Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs diff --git a/Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs rename to src/Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs diff --git a/Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs rename to src/Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs diff --git a/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs b/src/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs rename to src/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs diff --git a/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs b/src/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs rename to src/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs diff --git a/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs b/src/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs similarity index 100% rename from Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs rename to src/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs diff --git a/Ryujinx.Audio/ResultCode.cs b/src/Ryujinx.Audio/ResultCode.cs similarity index 100% rename from Ryujinx.Audio/ResultCode.cs rename to src/Ryujinx.Audio/ResultCode.cs diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/src/Ryujinx.Audio/Ryujinx.Audio.csproj similarity index 100% rename from Ryujinx.Audio/Ryujinx.Audio.csproj rename to src/Ryujinx.Audio/Ryujinx.Audio.csproj diff --git a/Ryujinx.Ava/App.axaml b/src/Ryujinx.Ava/App.axaml similarity index 100% rename from Ryujinx.Ava/App.axaml rename to src/Ryujinx.Ava/App.axaml diff --git a/Ryujinx.Ava/App.axaml.cs b/src/Ryujinx.Ava/App.axaml.cs similarity index 100% rename from Ryujinx.Ava/App.axaml.cs rename to src/Ryujinx.Ava/App.axaml.cs diff --git a/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs similarity index 100% rename from Ryujinx.Ava/AppHost.cs rename to src/Ryujinx.Ava/AppHost.cs diff --git a/Ryujinx.Ava/Assets/Fonts/SegoeFluentIcons.ttf b/src/Ryujinx.Ava/Assets/Fonts/SegoeFluentIcons.ttf similarity index 100% rename from Ryujinx.Ava/Assets/Fonts/SegoeFluentIcons.ttf rename to src/Ryujinx.Ava/Assets/Fonts/SegoeFluentIcons.ttf diff --git a/Ryujinx.Ava/Assets/Locales/de_DE.json b/src/Ryujinx.Ava/Assets/Locales/de_DE.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/de_DE.json rename to src/Ryujinx.Ava/Assets/Locales/de_DE.json diff --git a/Ryujinx.Ava/Assets/Locales/el_GR.json b/src/Ryujinx.Ava/Assets/Locales/el_GR.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/el_GR.json rename to src/Ryujinx.Ava/Assets/Locales/el_GR.json diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/en_US.json rename to src/Ryujinx.Ava/Assets/Locales/en_US.json diff --git a/Ryujinx.Ava/Assets/Locales/es_ES.json b/src/Ryujinx.Ava/Assets/Locales/es_ES.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/es_ES.json rename to src/Ryujinx.Ava/Assets/Locales/es_ES.json diff --git a/Ryujinx.Ava/Assets/Locales/fr_FR.json b/src/Ryujinx.Ava/Assets/Locales/fr_FR.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/fr_FR.json rename to src/Ryujinx.Ava/Assets/Locales/fr_FR.json diff --git a/Ryujinx.Ava/Assets/Locales/it_IT.json b/src/Ryujinx.Ava/Assets/Locales/it_IT.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/it_IT.json rename to src/Ryujinx.Ava/Assets/Locales/it_IT.json diff --git a/Ryujinx.Ava/Assets/Locales/ja_JP.json b/src/Ryujinx.Ava/Assets/Locales/ja_JP.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/ja_JP.json rename to src/Ryujinx.Ava/Assets/Locales/ja_JP.json diff --git a/Ryujinx.Ava/Assets/Locales/ko_KR.json b/src/Ryujinx.Ava/Assets/Locales/ko_KR.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/ko_KR.json rename to src/Ryujinx.Ava/Assets/Locales/ko_KR.json diff --git a/Ryujinx.Ava/Assets/Locales/pl_PL.json b/src/Ryujinx.Ava/Assets/Locales/pl_PL.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/pl_PL.json rename to src/Ryujinx.Ava/Assets/Locales/pl_PL.json diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/src/Ryujinx.Ava/Assets/Locales/pt_BR.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/pt_BR.json rename to src/Ryujinx.Ava/Assets/Locales/pt_BR.json diff --git a/Ryujinx.Ava/Assets/Locales/ru_RU.json b/src/Ryujinx.Ava/Assets/Locales/ru_RU.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/ru_RU.json rename to src/Ryujinx.Ava/Assets/Locales/ru_RU.json diff --git a/Ryujinx.Ava/Assets/Locales/tr_TR.json b/src/Ryujinx.Ava/Assets/Locales/tr_TR.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/tr_TR.json rename to src/Ryujinx.Ava/Assets/Locales/tr_TR.json diff --git a/Ryujinx.Ava/Assets/Locales/uk_UA.json b/src/Ryujinx.Ava/Assets/Locales/uk_UA.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/uk_UA.json rename to src/Ryujinx.Ava/Assets/Locales/uk_UA.json diff --git a/Ryujinx.Ava/Assets/Locales/zh_CN.json b/src/Ryujinx.Ava/Assets/Locales/zh_CN.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/zh_CN.json rename to src/Ryujinx.Ava/Assets/Locales/zh_CN.json diff --git a/Ryujinx.Ava/Assets/Locales/zh_TW.json b/src/Ryujinx.Ava/Assets/Locales/zh_TW.json similarity index 100% rename from Ryujinx.Ava/Assets/Locales/zh_TW.json rename to src/Ryujinx.Ava/Assets/Locales/zh_TW.json diff --git a/Ryujinx.Ava/Assets/Styles/BaseDark.xaml b/src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml similarity index 100% rename from Ryujinx.Ava/Assets/Styles/BaseDark.xaml rename to src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml diff --git a/Ryujinx.Ava/Assets/Styles/BaseLight.xaml b/src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml similarity index 100% rename from Ryujinx.Ava/Assets/Styles/BaseLight.xaml rename to src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml diff --git a/Ryujinx.Ava/Assets/Styles/Styles.xaml b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml similarity index 100% rename from Ryujinx.Ava/Assets/Styles/Styles.xaml rename to src/Ryujinx.Ava/Assets/Styles/Styles.xaml diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/src/Ryujinx.Ava/Common/ApplicationHelper.cs similarity index 100% rename from Ryujinx.Ava/Common/ApplicationHelper.cs rename to src/Ryujinx.Ava/Common/ApplicationHelper.cs diff --git a/Ryujinx.Ava/Common/ApplicationSort.cs b/src/Ryujinx.Ava/Common/ApplicationSort.cs similarity index 100% rename from Ryujinx.Ava/Common/ApplicationSort.cs rename to src/Ryujinx.Ava/Common/ApplicationSort.cs diff --git a/Ryujinx.Ava/Common/KeyboardHotkeyState.cs b/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs similarity index 100% rename from Ryujinx.Ava/Common/KeyboardHotkeyState.cs rename to src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs diff --git a/Ryujinx.Ava/Common/Locale/LocaleExtension.cs b/src/Ryujinx.Ava/Common/Locale/LocaleExtension.cs similarity index 100% rename from Ryujinx.Ava/Common/Locale/LocaleExtension.cs rename to src/Ryujinx.Ava/Common/Locale/LocaleExtension.cs diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/src/Ryujinx.Ava/Common/Locale/LocaleManager.cs similarity index 100% rename from Ryujinx.Ava/Common/Locale/LocaleManager.cs rename to src/Ryujinx.Ava/Common/Locale/LocaleManager.cs diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboard.cs b/src/Ryujinx.Ava/Input/AvaloniaKeyboard.cs similarity index 100% rename from Ryujinx.Ava/Input/AvaloniaKeyboard.cs rename to src/Ryujinx.Ava/Input/AvaloniaKeyboard.cs diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs similarity index 100% rename from Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs rename to src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs b/src/Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs similarity index 100% rename from Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs rename to src/Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs diff --git a/Ryujinx.Ava/Input/AvaloniaMouse.cs b/src/Ryujinx.Ava/Input/AvaloniaMouse.cs similarity index 100% rename from Ryujinx.Ava/Input/AvaloniaMouse.cs rename to src/Ryujinx.Ava/Input/AvaloniaMouse.cs diff --git a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs similarity index 100% rename from Ryujinx.Ava/Input/AvaloniaMouseDriver.cs rename to src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/src/Ryujinx.Ava/Modules/Updater/Updater.cs similarity index 100% rename from Ryujinx.Ava/Modules/Updater/Updater.cs rename to src/Ryujinx.Ava/Modules/Updater/Updater.cs diff --git a/Ryujinx.Ava/Program.cs b/src/Ryujinx.Ava/Program.cs similarity index 100% rename from Ryujinx.Ava/Program.cs rename to src/Ryujinx.Ava/Program.cs diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/src/Ryujinx.Ava/Ryujinx.Ava.csproj similarity index 93% rename from Ryujinx.Ava/Ryujinx.Ava.csproj rename to src/Ryujinx.Ava/Ryujinx.Ava.csproj index 325ceb2dc..7509798b6 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/src/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -13,7 +13,7 @@ </PropertyGroup> <Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))"> - <Exec Command="codesign --entitlements '$(ProjectDir)..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" /> + <Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" /> </Target> <PropertyGroup Condition="'$(RuntimeIdentifier)' != ''"> @@ -67,25 +67,25 @@ </ItemGroup> <ItemGroup> - <Content Include="..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'"> + <Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>alsoft.ini</TargetPath> </Content> - <Content Include="..\distribution\legal\THIRDPARTY.md"> + <Content Include="..\..\distribution\legal\THIRDPARTY.md"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>THIRDPARTY.md</TargetPath> </Content> - <Content Include="..\LICENSE.txt"> + <Content Include="..\..\LICENSE.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>LICENSE.txt</TargetPath> </Content> </ItemGroup> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'"> - <Content Include="..\distribution\linux\Ryujinx.sh"> + <Content Include="..\..\distribution\linux\Ryujinx.sh"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> - <Content Include="..\distribution\linux\mime\Ryujinx.xml"> + <Content Include="..\..\distribution\linux\mime\Ryujinx.xml"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>mime\Ryujinx.xml</TargetPath> </Content> @@ -189,6 +189,6 @@ <EmbeddedResource Include="Assets\Styles\Styles.xaml" /> </ItemGroup> <ItemGroup> - <AdditionalFiles Include="Assets\Locales\en_US.json" /> + <AdditionalFiles Include="Assets\Locales\en_US.json" /> </ItemGroup> </Project> diff --git a/Ryujinx.Ava/Ryujinx.ico b/src/Ryujinx.Ava/Ryujinx.ico similarity index 100% rename from Ryujinx.Ava/Ryujinx.ico rename to src/Ryujinx.Ava/Ryujinx.ico diff --git a/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs similarity index 100% rename from Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs rename to src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs diff --git a/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/src/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs similarity index 100% rename from Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs rename to src/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs diff --git a/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs b/src/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs similarity index 100% rename from Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs rename to src/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs diff --git a/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml rename to src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml diff --git a/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml similarity index 100% rename from Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml rename to src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml diff --git a/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs rename to src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml b/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Controls/GameGridView.axaml rename to src/Ryujinx.Ava/UI/Controls/GameGridView.axaml diff --git a/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs diff --git a/Ryujinx.Ava/UI/Controls/GameListView.axaml b/src/Ryujinx.Ava/UI/Controls/GameListView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Controls/GameListView.axaml rename to src/Ryujinx.Ava/UI/Controls/GameListView.axaml diff --git a/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Controls/GameListView.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs diff --git a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml b/src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml similarity index 100% rename from Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml rename to src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml diff --git a/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs b/src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs diff --git a/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml b/src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml rename to src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml diff --git a/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs b/src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs b/src/Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs rename to src/Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs diff --git a/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs rename to src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs diff --git a/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs b/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs rename to src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs diff --git a/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs rename to src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs diff --git a/Ryujinx.Ava/UI/Helpers/Glyph.cs b/src/Ryujinx.Ava/UI/Helpers/Glyph.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/Glyph.cs rename to src/Ryujinx.Ava/UI/Helpers/Glyph.cs diff --git a/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs rename to src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs diff --git a/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs b/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/HotKeyControl.cs rename to src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs diff --git a/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs rename to src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs diff --git a/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs b/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs rename to src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs diff --git a/Ryujinx.Ava/UI/Helpers/MiniCommand.cs b/src/Ryujinx.Ava/UI/Helpers/MiniCommand.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/MiniCommand.cs rename to src/Ryujinx.Ava/UI/Helpers/MiniCommand.cs diff --git a/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/NotificationHelper.cs rename to src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs diff --git a/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs b/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs rename to src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs diff --git a/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs b/src/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs rename to src/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs diff --git a/Ryujinx.Ava/UI/Helpers/UserResult.cs b/src/Ryujinx.Ava/UI/Helpers/UserResult.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/UserResult.cs rename to src/Ryujinx.Ava/UI/Helpers/UserResult.cs diff --git a/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs b/src/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs similarity index 100% rename from Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs rename to src/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs diff --git a/Ryujinx.Ava/UI/Models/CheatModel.cs b/src/Ryujinx.Ava/UI/Models/CheatModel.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/CheatModel.cs rename to src/Ryujinx.Ava/UI/Models/CheatModel.cs diff --git a/Ryujinx.Ava/UI/Models/CheatsList.cs b/src/Ryujinx.Ava/UI/Models/CheatsList.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/CheatsList.cs rename to src/Ryujinx.Ava/UI/Models/CheatsList.cs diff --git a/Ryujinx.Ava/UI/Models/ControllerModel.cs b/src/Ryujinx.Ava/UI/Models/ControllerModel.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/ControllerModel.cs rename to src/Ryujinx.Ava/UI/Models/ControllerModel.cs diff --git a/Ryujinx.Ava/UI/Models/DeviceType.cs b/src/Ryujinx.Ava/UI/Models/DeviceType.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/DeviceType.cs rename to src/Ryujinx.Ava/UI/Models/DeviceType.cs diff --git a/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs b/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/DownloadableContentModel.cs rename to src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs diff --git a/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs b/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs rename to src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs diff --git a/Ryujinx.Ava/UI/Models/InputConfiguration.cs b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/InputConfiguration.cs rename to src/Ryujinx.Ava/UI/Models/InputConfiguration.cs diff --git a/Ryujinx.Ava/UI/Models/PlayerModel.cs b/src/Ryujinx.Ava/UI/Models/PlayerModel.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/PlayerModel.cs rename to src/Ryujinx.Ava/UI/Models/PlayerModel.cs diff --git a/Ryujinx.Ava/UI/Models/ProfileImageModel.cs b/src/Ryujinx.Ava/UI/Models/ProfileImageModel.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/ProfileImageModel.cs rename to src/Ryujinx.Ava/UI/Models/ProfileImageModel.cs diff --git a/Ryujinx.Ava/UI/Models/SaveModel.cs b/src/Ryujinx.Ava/UI/Models/SaveModel.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/SaveModel.cs rename to src/Ryujinx.Ava/UI/Models/SaveModel.cs diff --git a/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs b/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs rename to src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs diff --git a/Ryujinx.Ava/UI/Models/TempProfile.cs b/src/Ryujinx.Ava/UI/Models/TempProfile.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/TempProfile.cs rename to src/Ryujinx.Ava/UI/Models/TempProfile.cs diff --git a/Ryujinx.Ava/UI/Models/TimeZone.cs b/src/Ryujinx.Ava/UI/Models/TimeZone.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/TimeZone.cs rename to src/Ryujinx.Ava/UI/Models/TimeZone.cs diff --git a/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs b/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/TitleUpdateModel.cs rename to src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs diff --git a/Ryujinx.Ava/UI/Models/UserProfile.cs b/src/Ryujinx.Ava/UI/Models/UserProfile.cs similarity index 100% rename from Ryujinx.Ava/UI/Models/UserProfile.cs rename to src/Ryujinx.Ava/UI/Models/UserProfile.cs diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs similarity index 100% rename from Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs rename to src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs similarity index 100% rename from Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs rename to src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs similarity index 100% rename from Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs rename to src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs diff --git a/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs b/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs similarity index 100% rename from Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs rename to src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs diff --git a/Ryujinx.Ava/UI/Renderer/RendererHost.axaml b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml similarity index 100% rename from Ryujinx.Ava/UI/Renderer/RendererHost.axaml rename to src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml diff --git a/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs rename to src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs diff --git a/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs b/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs similarity index 100% rename from Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs rename to src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs diff --git a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/BaseModel.cs b/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/BaseModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs diff --git a/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs similarity index 100% rename from Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml rename to src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml diff --git a/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml rename to src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml diff --git a/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml rename to src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml diff --git a/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserEditorView.axaml rename to src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml diff --git a/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml rename to src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml diff --git a/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml rename to src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml diff --git a/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml rename to src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml diff --git a/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml rename to src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml diff --git a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs diff --git a/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml rename to src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml diff --git a/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs rename to src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/AboutWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/CheatWindow.axaml b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/CheatWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/IconColorPicker.cs b/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/IconColorPicker.cs rename to src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/MainWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/MainWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/SettingsWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs diff --git a/Ryujinx.Ava/UI/Windows/StyleableWindow.cs b/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/StyleableWindow.cs rename to src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml similarity index 100% rename from Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml rename to src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs similarity index 100% rename from Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs diff --git a/Ryujinx.Common/AsyncWorkQueue.cs b/src/Ryujinx.Common/AsyncWorkQueue.cs similarity index 100% rename from Ryujinx.Common/AsyncWorkQueue.cs rename to src/Ryujinx.Common/AsyncWorkQueue.cs diff --git a/Ryujinx.Common/Collections/IntervalTree.cs b/src/Ryujinx.Common/Collections/IntervalTree.cs similarity index 100% rename from Ryujinx.Common/Collections/IntervalTree.cs rename to src/Ryujinx.Common/Collections/IntervalTree.cs diff --git a/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs similarity index 100% rename from Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs rename to src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs diff --git a/Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs similarity index 100% rename from Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs rename to src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs diff --git a/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs similarity index 100% rename from Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs rename to src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs diff --git a/Ryujinx.Common/Collections/TreeDictionary.cs b/src/Ryujinx.Common/Collections/TreeDictionary.cs similarity index 100% rename from Ryujinx.Common/Collections/TreeDictionary.cs rename to src/Ryujinx.Common/Collections/TreeDictionary.cs diff --git a/Ryujinx.Common/Configuration/AntiAliasing.cs b/src/Ryujinx.Common/Configuration/AntiAliasing.cs similarity index 100% rename from Ryujinx.Common/Configuration/AntiAliasing.cs rename to src/Ryujinx.Common/Configuration/AntiAliasing.cs diff --git a/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs similarity index 100% rename from Ryujinx.Common/Configuration/AppDataManager.cs rename to src/Ryujinx.Common/Configuration/AppDataManager.cs diff --git a/Ryujinx.Common/Configuration/AspectRatioExtensions.cs b/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs similarity index 100% rename from Ryujinx.Common/Configuration/AspectRatioExtensions.cs rename to src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs diff --git a/Ryujinx.Common/Configuration/BackendThreading.cs b/src/Ryujinx.Common/Configuration/BackendThreading.cs similarity index 100% rename from Ryujinx.Common/Configuration/BackendThreading.cs rename to src/Ryujinx.Common/Configuration/BackendThreading.cs diff --git a/Ryujinx.Common/Configuration/DownloadableContentContainer.cs b/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs similarity index 100% rename from Ryujinx.Common/Configuration/DownloadableContentContainer.cs rename to src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs diff --git a/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs rename to src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs diff --git a/Ryujinx.Common/Configuration/DownloadableContentNca.cs b/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs similarity index 100% rename from Ryujinx.Common/Configuration/DownloadableContentNca.cs rename to src/Ryujinx.Common/Configuration/DownloadableContentNca.cs diff --git a/Ryujinx.Common/Configuration/GraphicsBackend.cs b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs similarity index 100% rename from Ryujinx.Common/Configuration/GraphicsBackend.cs rename to src/Ryujinx.Common/Configuration/GraphicsBackend.cs diff --git a/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs b/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs similarity index 100% rename from Ryujinx.Common/Configuration/GraphicsDebugLevel.cs rename to src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/StandardControllerInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/StandardControllerInputConfig.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/StandardControllerInputConfig.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/StandardControllerInputConfig.cs diff --git a/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs rename to src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs diff --git a/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/ControllerType.cs rename to src/Ryujinx.Common/Configuration/Hid/ControllerType.cs diff --git a/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs b/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs rename to src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs diff --git a/Ryujinx.Common/Configuration/Hid/InputBackendType.cs b/src/Ryujinx.Common/Configuration/Hid/InputBackendType.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/InputBackendType.cs rename to src/Ryujinx.Common/Configuration/Hid/InputBackendType.cs diff --git a/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/InputConfig.cs rename to src/Ryujinx.Common/Configuration/Hid/InputConfig.cs diff --git a/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs rename to src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs diff --git a/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs b/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs rename to src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs diff --git a/Ryujinx.Common/Configuration/Hid/Key.cs b/src/Ryujinx.Common/Configuration/Hid/Key.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Key.cs rename to src/Ryujinx.Common/Configuration/Hid/Key.cs diff --git a/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs rename to src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs diff --git a/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs rename to src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs diff --git a/Ryujinx.Common/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs rename to src/Ryujinx.Common/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs rename to src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs diff --git a/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs rename to src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs diff --git a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/PlayerIndex.cs rename to src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs diff --git a/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs similarity index 100% rename from Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs rename to src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs diff --git a/Ryujinx.Common/Configuration/MemoryManagerMode.cs b/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs similarity index 100% rename from Ryujinx.Common/Configuration/MemoryManagerMode.cs rename to src/Ryujinx.Common/Configuration/MemoryManagerMode.cs diff --git a/Ryujinx.Common/Configuration/ScalingFilter.cs b/src/Ryujinx.Common/Configuration/ScalingFilter.cs similarity index 100% rename from Ryujinx.Common/Configuration/ScalingFilter.cs rename to src/Ryujinx.Common/Configuration/ScalingFilter.cs diff --git a/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs b/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs similarity index 100% rename from Ryujinx.Common/Configuration/TitleUpdateMetadata.cs rename to src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs diff --git a/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs rename to src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs diff --git a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/src/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs similarity index 100% rename from Ryujinx.Common/Extensions/BinaryReaderExtensions.cs rename to src/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs diff --git a/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs b/src/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs similarity index 100% rename from Ryujinx.Common/Extensions/BinaryWriterExtensions.cs rename to src/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs diff --git a/Ryujinx.Common/Extensions/StreamExtensions.cs b/src/Ryujinx.Common/Extensions/StreamExtensions.cs similarity index 100% rename from Ryujinx.Common/Extensions/StreamExtensions.cs rename to src/Ryujinx.Common/Extensions/StreamExtensions.cs diff --git a/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs similarity index 100% rename from Ryujinx.Common/GraphicsDriver/DriverUtilities.cs rename to src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs diff --git a/Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs similarity index 100% rename from Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs rename to src/Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs diff --git a/Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs similarity index 100% rename from Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs rename to src/Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs diff --git a/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs similarity index 100% rename from Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs rename to src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs diff --git a/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs similarity index 100% rename from Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs rename to src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs diff --git a/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs similarity index 100% rename from Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs rename to src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs diff --git a/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs b/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs similarity index 100% rename from Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs rename to src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs diff --git a/Ryujinx.Common/Hash128.cs b/src/Ryujinx.Common/Hash128.cs similarity index 100% rename from Ryujinx.Common/Hash128.cs rename to src/Ryujinx.Common/Hash128.cs diff --git a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs similarity index 100% rename from Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs rename to src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs diff --git a/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs similarity index 100% rename from Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs rename to src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs diff --git a/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs similarity index 100% rename from Ryujinx.Common/Logging/Formatters/ILogFormatter.cs rename to src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs diff --git a/Ryujinx.Common/Logging/LogClass.cs b/src/Ryujinx.Common/Logging/LogClass.cs similarity index 100% rename from Ryujinx.Common/Logging/LogClass.cs rename to src/Ryujinx.Common/Logging/LogClass.cs diff --git a/Ryujinx.Common/Logging/LogEventArgs.cs b/src/Ryujinx.Common/Logging/LogEventArgs.cs similarity index 100% rename from Ryujinx.Common/Logging/LogEventArgs.cs rename to src/Ryujinx.Common/Logging/LogEventArgs.cs diff --git a/Ryujinx.Common/Logging/LogEventArgsJson.cs b/src/Ryujinx.Common/Logging/LogEventArgsJson.cs similarity index 100% rename from Ryujinx.Common/Logging/LogEventArgsJson.cs rename to src/Ryujinx.Common/Logging/LogEventArgsJson.cs diff --git a/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs b/src/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs rename to src/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs diff --git a/Ryujinx.Common/Logging/LogLevel.cs b/src/Ryujinx.Common/Logging/LogLevel.cs similarity index 100% rename from Ryujinx.Common/Logging/LogLevel.cs rename to src/Ryujinx.Common/Logging/LogLevel.cs diff --git a/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs similarity index 100% rename from Ryujinx.Common/Logging/Logger.cs rename to src/Ryujinx.Common/Logging/Logger.cs diff --git a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs similarity index 100% rename from Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs rename to src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs diff --git a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs similarity index 100% rename from Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs rename to src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs similarity index 100% rename from Ryujinx.Common/Logging/Targets/FileLogTarget.cs rename to src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs diff --git a/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs similarity index 100% rename from Ryujinx.Common/Logging/Targets/ILogTarget.cs rename to src/Ryujinx.Common/Logging/Targets/ILogTarget.cs diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs similarity index 100% rename from Ryujinx.Common/Logging/Targets/JsonLogTarget.cs rename to src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs diff --git a/Ryujinx.Common/Memory/ArrayPtr.cs b/src/Ryujinx.Common/Memory/ArrayPtr.cs similarity index 100% rename from Ryujinx.Common/Memory/ArrayPtr.cs rename to src/Ryujinx.Common/Memory/ArrayPtr.cs diff --git a/Ryujinx.Common/Memory/Box.cs b/src/Ryujinx.Common/Memory/Box.cs similarity index 100% rename from Ryujinx.Common/Memory/Box.cs rename to src/Ryujinx.Common/Memory/Box.cs diff --git a/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs similarity index 100% rename from Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs rename to src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs diff --git a/Ryujinx.Common/Memory/ByteMemoryPool.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs similarity index 100% rename from Ryujinx.Common/Memory/ByteMemoryPool.cs rename to src/Ryujinx.Common/Memory/ByteMemoryPool.cs diff --git a/Ryujinx.Common/Memory/IArray.cs b/src/Ryujinx.Common/Memory/IArray.cs similarity index 100% rename from Ryujinx.Common/Memory/IArray.cs rename to src/Ryujinx.Common/Memory/IArray.cs diff --git a/Ryujinx.Common/Memory/MemoryStreamManager.cs b/src/Ryujinx.Common/Memory/MemoryStreamManager.cs similarity index 100% rename from Ryujinx.Common/Memory/MemoryStreamManager.cs rename to src/Ryujinx.Common/Memory/MemoryStreamManager.cs diff --git a/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs similarity index 100% rename from Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs rename to src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs diff --git a/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs similarity index 100% rename from Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs rename to src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs diff --git a/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs similarity index 100% rename from Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs rename to src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs diff --git a/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs similarity index 100% rename from Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs rename to src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs diff --git a/Ryujinx.Common/Memory/Ptr.cs b/src/Ryujinx.Common/Memory/Ptr.cs similarity index 100% rename from Ryujinx.Common/Memory/Ptr.cs rename to src/Ryujinx.Common/Memory/Ptr.cs diff --git a/Ryujinx.Common/Memory/SpanOrArray.cs b/src/Ryujinx.Common/Memory/SpanOrArray.cs similarity index 100% rename from Ryujinx.Common/Memory/SpanOrArray.cs rename to src/Ryujinx.Common/Memory/SpanOrArray.cs diff --git a/Ryujinx.Common/Memory/SpanReader.cs b/src/Ryujinx.Common/Memory/SpanReader.cs similarity index 100% rename from Ryujinx.Common/Memory/SpanReader.cs rename to src/Ryujinx.Common/Memory/SpanReader.cs diff --git a/Ryujinx.Common/Memory/SpanWriter.cs b/src/Ryujinx.Common/Memory/SpanWriter.cs similarity index 100% rename from Ryujinx.Common/Memory/SpanWriter.cs rename to src/Ryujinx.Common/Memory/SpanWriter.cs diff --git a/Ryujinx.Common/Memory/StructArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs similarity index 100% rename from Ryujinx.Common/Memory/StructArrayHelpers.cs rename to src/Ryujinx.Common/Memory/StructArrayHelpers.cs diff --git a/Ryujinx.Common/Memory/StructByteArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs similarity index 100% rename from Ryujinx.Common/Memory/StructByteArrayHelpers.cs rename to src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs diff --git a/Ryujinx.Common/PerformanceCounter.cs b/src/Ryujinx.Common/PerformanceCounter.cs similarity index 100% rename from Ryujinx.Common/PerformanceCounter.cs rename to src/Ryujinx.Common/PerformanceCounter.cs diff --git a/Ryujinx.Common/Pools/ObjectPool.cs b/src/Ryujinx.Common/Pools/ObjectPool.cs similarity index 100% rename from Ryujinx.Common/Pools/ObjectPool.cs rename to src/Ryujinx.Common/Pools/ObjectPool.cs diff --git a/Ryujinx.Common/Pools/SharedPools.cs b/src/Ryujinx.Common/Pools/SharedPools.cs similarity index 100% rename from Ryujinx.Common/Pools/SharedPools.cs rename to src/Ryujinx.Common/Pools/SharedPools.cs diff --git a/Ryujinx.Common/Pools/ThreadStaticArray.cs b/src/Ryujinx.Common/Pools/ThreadStaticArray.cs similarity index 100% rename from Ryujinx.Common/Pools/ThreadStaticArray.cs rename to src/Ryujinx.Common/Pools/ThreadStaticArray.cs diff --git a/Ryujinx.Common/ReactiveObject.cs b/src/Ryujinx.Common/ReactiveObject.cs similarity index 100% rename from Ryujinx.Common/ReactiveObject.cs rename to src/Ryujinx.Common/ReactiveObject.cs diff --git a/Ryujinx.Common/ReferenceEqualityComparer.cs b/src/Ryujinx.Common/ReferenceEqualityComparer.cs similarity index 100% rename from Ryujinx.Common/ReferenceEqualityComparer.cs rename to src/Ryujinx.Common/ReferenceEqualityComparer.cs diff --git a/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs similarity index 100% rename from Ryujinx.Common/ReleaseInformation.cs rename to src/Ryujinx.Common/ReleaseInformation.cs diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/src/Ryujinx.Common/Ryujinx.Common.csproj similarity index 100% rename from Ryujinx.Common/Ryujinx.Common.csproj rename to src/Ryujinx.Common/Ryujinx.Common.csproj diff --git a/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs b/src/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs similarity index 100% rename from Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs rename to src/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs diff --git a/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs b/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs similarity index 100% rename from Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs rename to src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs diff --git a/Ryujinx.Common/SystemInfo/SystemInfo.cs b/src/Ryujinx.Common/SystemInfo/SystemInfo.cs similarity index 100% rename from Ryujinx.Common/SystemInfo/SystemInfo.cs rename to src/Ryujinx.Common/SystemInfo/SystemInfo.cs diff --git a/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs b/src/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs similarity index 100% rename from Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs rename to src/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs diff --git a/Ryujinx.Common/SystemInterop/DisplaySleep.cs b/src/Ryujinx.Common/SystemInterop/DisplaySleep.cs similarity index 100% rename from Ryujinx.Common/SystemInterop/DisplaySleep.cs rename to src/Ryujinx.Common/SystemInterop/DisplaySleep.cs diff --git a/Ryujinx.Common/SystemInterop/ForceDpiAware.cs b/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs similarity index 100% rename from Ryujinx.Common/SystemInterop/ForceDpiAware.cs rename to src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs diff --git a/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs b/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs similarity index 100% rename from Ryujinx.Common/SystemInterop/GdiPlusHelper.cs rename to src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs diff --git a/Ryujinx.Common/SystemInterop/StdErrAdapter.cs b/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs similarity index 100% rename from Ryujinx.Common/SystemInterop/StdErrAdapter.cs rename to src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs diff --git a/Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs b/src/Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs similarity index 100% rename from Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs rename to src/Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs diff --git a/Ryujinx.Common/Utilities/BitUtils.cs b/src/Ryujinx.Common/Utilities/BitUtils.cs similarity index 100% rename from Ryujinx.Common/Utilities/BitUtils.cs rename to src/Ryujinx.Common/Utilities/BitUtils.cs diff --git a/Ryujinx.Common/Utilities/BitfieldExtensions.cs b/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs similarity index 100% rename from Ryujinx.Common/Utilities/BitfieldExtensions.cs rename to src/Ryujinx.Common/Utilities/BitfieldExtensions.cs diff --git a/Ryujinx.Common/Utilities/Buffers.cs b/src/Ryujinx.Common/Utilities/Buffers.cs similarity index 100% rename from Ryujinx.Common/Utilities/Buffers.cs rename to src/Ryujinx.Common/Utilities/Buffers.cs diff --git a/Ryujinx.Common/Utilities/CommonJsonContext.cs b/src/Ryujinx.Common/Utilities/CommonJsonContext.cs similarity index 100% rename from Ryujinx.Common/Utilities/CommonJsonContext.cs rename to src/Ryujinx.Common/Utilities/CommonJsonContext.cs diff --git a/Ryujinx.Common/Utilities/EmbeddedResources.cs b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs similarity index 100% rename from Ryujinx.Common/Utilities/EmbeddedResources.cs rename to src/Ryujinx.Common/Utilities/EmbeddedResources.cs diff --git a/Ryujinx.Common/Utilities/HexUtils.cs b/src/Ryujinx.Common/Utilities/HexUtils.cs similarity index 100% rename from Ryujinx.Common/Utilities/HexUtils.cs rename to src/Ryujinx.Common/Utilities/HexUtils.cs diff --git a/Ryujinx.Common/Utilities/JsonHelper.cs b/src/Ryujinx.Common/Utilities/JsonHelper.cs similarity index 100% rename from Ryujinx.Common/Utilities/JsonHelper.cs rename to src/Ryujinx.Common/Utilities/JsonHelper.cs diff --git a/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs b/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs similarity index 100% rename from Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs rename to src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs diff --git a/Ryujinx.Common/Utilities/NetworkHelpers.cs b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs similarity index 100% rename from Ryujinx.Common/Utilities/NetworkHelpers.cs rename to src/Ryujinx.Common/Utilities/NetworkHelpers.cs diff --git a/Ryujinx.Common/Utilities/SpanHelpers.cs b/src/Ryujinx.Common/Utilities/SpanHelpers.cs similarity index 100% rename from Ryujinx.Common/Utilities/SpanHelpers.cs rename to src/Ryujinx.Common/Utilities/SpanHelpers.cs diff --git a/Ryujinx.Common/Utilities/StreamUtils.cs b/src/Ryujinx.Common/Utilities/StreamUtils.cs similarity index 100% rename from Ryujinx.Common/Utilities/StreamUtils.cs rename to src/Ryujinx.Common/Utilities/StreamUtils.cs diff --git a/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs b/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs similarity index 100% rename from Ryujinx.Common/Utilities/TypedStringEnumConverter.cs rename to src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs diff --git a/Ryujinx.Common/Utilities/UInt128Utils.cs b/src/Ryujinx.Common/Utilities/UInt128Utils.cs similarity index 100% rename from Ryujinx.Common/Utilities/UInt128Utils.cs rename to src/Ryujinx.Common/Utilities/UInt128Utils.cs diff --git a/Ryujinx.Common/XXHash128.cs b/src/Ryujinx.Common/XXHash128.cs similarity index 100% rename from Ryujinx.Common/XXHash128.cs rename to src/Ryujinx.Common/XXHash128.cs diff --git a/Ryujinx.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs similarity index 100% rename from Ryujinx.Cpu/AddressSpace.cs rename to src/Ryujinx.Cpu/AddressSpace.cs diff --git a/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs b/src/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs rename to src/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs diff --git a/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs b/src/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs rename to src/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs diff --git a/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs b/src/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs rename to src/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs diff --git a/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs b/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvAddressSpace.cs rename to src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs diff --git a/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs b/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs rename to src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs diff --git a/Ryujinx.Cpu/AppleHv/HvApi.cs b/src/Ryujinx.Cpu/AppleHv/HvApi.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvApi.cs rename to src/Ryujinx.Cpu/AppleHv/HvApi.cs diff --git a/Ryujinx.Cpu/AppleHv/HvCpuContext.cs b/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvCpuContext.cs rename to src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs diff --git a/Ryujinx.Cpu/AppleHv/HvEngine.cs b/src/Ryujinx.Cpu/AppleHv/HvEngine.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvEngine.cs rename to src/Ryujinx.Cpu/AppleHv/HvEngine.cs diff --git a/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvExecutionContext.cs rename to src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs diff --git a/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs rename to src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs diff --git a/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs rename to src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs diff --git a/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs b/src/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs rename to src/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs rename to src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs rename to src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvMemoryManager.cs rename to src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs diff --git a/Ryujinx.Cpu/AppleHv/HvVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvVcpu.cs rename to src/Ryujinx.Cpu/AppleHv/HvVcpu.cs diff --git a/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs b/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvVcpuPool.cs rename to src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs diff --git a/Ryujinx.Cpu/AppleHv/HvVm.cs b/src/Ryujinx.Cpu/AppleHv/HvVm.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/HvVm.cs rename to src/Ryujinx.Cpu/AppleHv/HvVm.cs diff --git a/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs similarity index 100% rename from Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs rename to src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs diff --git a/Ryujinx.Cpu/ExceptionCallbacks.cs b/src/Ryujinx.Cpu/ExceptionCallbacks.cs similarity index 100% rename from Ryujinx.Cpu/ExceptionCallbacks.cs rename to src/Ryujinx.Cpu/ExceptionCallbacks.cs diff --git a/Ryujinx.Cpu/ICpuContext.cs b/src/Ryujinx.Cpu/ICpuContext.cs similarity index 100% rename from Ryujinx.Cpu/ICpuContext.cs rename to src/Ryujinx.Cpu/ICpuContext.cs diff --git a/Ryujinx.Cpu/ICpuEngine.cs b/src/Ryujinx.Cpu/ICpuEngine.cs similarity index 100% rename from Ryujinx.Cpu/ICpuEngine.cs rename to src/Ryujinx.Cpu/ICpuEngine.cs diff --git a/Ryujinx.Cpu/IDiskCacheState.cs b/src/Ryujinx.Cpu/IDiskCacheState.cs similarity index 100% rename from Ryujinx.Cpu/IDiskCacheState.cs rename to src/Ryujinx.Cpu/IDiskCacheState.cs diff --git a/Ryujinx.Cpu/IExecutionContext.cs b/src/Ryujinx.Cpu/IExecutionContext.cs similarity index 100% rename from Ryujinx.Cpu/IExecutionContext.cs rename to src/Ryujinx.Cpu/IExecutionContext.cs diff --git a/Ryujinx.Cpu/ITickSource.cs b/src/Ryujinx.Cpu/ITickSource.cs similarity index 100% rename from Ryujinx.Cpu/ITickSource.cs rename to src/Ryujinx.Cpu/ITickSource.cs diff --git a/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs b/src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs similarity index 100% rename from Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs rename to src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs diff --git a/Ryujinx.Cpu/Jit/JitCpuContext.cs b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs similarity index 100% rename from Ryujinx.Cpu/Jit/JitCpuContext.cs rename to src/Ryujinx.Cpu/Jit/JitCpuContext.cs diff --git a/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs b/src/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs similarity index 100% rename from Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs rename to src/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs diff --git a/Ryujinx.Cpu/Jit/JitEngine.cs b/src/Ryujinx.Cpu/Jit/JitEngine.cs similarity index 100% rename from Ryujinx.Cpu/Jit/JitEngine.cs rename to src/Ryujinx.Cpu/Jit/JitEngine.cs diff --git a/Ryujinx.Cpu/Jit/JitExecutionContext.cs b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs similarity index 100% rename from Ryujinx.Cpu/Jit/JitExecutionContext.cs rename to src/Ryujinx.Cpu/Jit/JitExecutionContext.cs diff --git a/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs b/src/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs similarity index 100% rename from Ryujinx.Cpu/Jit/JitMemoryAllocator.cs rename to src/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs diff --git a/Ryujinx.Cpu/Jit/JitMemoryBlock.cs b/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs similarity index 100% rename from Ryujinx.Cpu/Jit/JitMemoryBlock.cs rename to src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs diff --git a/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs similarity index 100% rename from Ryujinx.Cpu/Jit/MemoryManager.cs rename to src/Ryujinx.Cpu/Jit/MemoryManager.cs diff --git a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs similarity index 100% rename from Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs rename to src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs diff --git a/Ryujinx.Cpu/LoadState.cs b/src/Ryujinx.Cpu/LoadState.cs similarity index 100% rename from Ryujinx.Cpu/LoadState.cs rename to src/Ryujinx.Cpu/LoadState.cs diff --git a/Ryujinx.Cpu/MemoryEhMeilleure.cs b/src/Ryujinx.Cpu/MemoryEhMeilleure.cs similarity index 100% rename from Ryujinx.Cpu/MemoryEhMeilleure.cs rename to src/Ryujinx.Cpu/MemoryEhMeilleure.cs diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/src/Ryujinx.Cpu/MemoryHelper.cs similarity index 100% rename from Ryujinx.Cpu/MemoryHelper.cs rename to src/Ryujinx.Cpu/MemoryHelper.cs diff --git a/Ryujinx.Cpu/MemoryManagerBase.cs b/src/Ryujinx.Cpu/MemoryManagerBase.cs similarity index 100% rename from Ryujinx.Cpu/MemoryManagerBase.cs rename to src/Ryujinx.Cpu/MemoryManagerBase.cs diff --git a/Ryujinx.Cpu/PrivateMemoryAllocation.cs b/src/Ryujinx.Cpu/PrivateMemoryAllocation.cs similarity index 100% rename from Ryujinx.Cpu/PrivateMemoryAllocation.cs rename to src/Ryujinx.Cpu/PrivateMemoryAllocation.cs diff --git a/Ryujinx.Cpu/PrivateMemoryAllocator.cs b/src/Ryujinx.Cpu/PrivateMemoryAllocator.cs similarity index 100% rename from Ryujinx.Cpu/PrivateMemoryAllocator.cs rename to src/Ryujinx.Cpu/PrivateMemoryAllocator.cs diff --git a/Ryujinx.Cpu/Ryujinx.Cpu.csproj b/src/Ryujinx.Cpu/Ryujinx.Cpu.csproj similarity index 100% rename from Ryujinx.Cpu/Ryujinx.Cpu.csproj rename to src/Ryujinx.Cpu/Ryujinx.Cpu.csproj diff --git a/Ryujinx.Cpu/TickSource.cs b/src/Ryujinx.Cpu/TickSource.cs similarity index 100% rename from Ryujinx.Cpu/TickSource.cs rename to src/Ryujinx.Cpu/TickSource.cs diff --git a/Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs b/src/Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs similarity index 100% rename from Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs rename to src/Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs diff --git a/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs b/src/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs similarity index 100% rename from Ryujinx.Cpu/Tracking/CpuRegionHandle.cs rename to src/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs diff --git a/Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs b/src/Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs similarity index 100% rename from Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs rename to src/Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs diff --git a/Ryujinx.Graphics.Device/DeviceState.cs b/src/Ryujinx.Graphics.Device/DeviceState.cs similarity index 100% rename from Ryujinx.Graphics.Device/DeviceState.cs rename to src/Ryujinx.Graphics.Device/DeviceState.cs diff --git a/Ryujinx.Graphics.Device/IDeviceState.cs b/src/Ryujinx.Graphics.Device/IDeviceState.cs similarity index 100% rename from Ryujinx.Graphics.Device/IDeviceState.cs rename to src/Ryujinx.Graphics.Device/IDeviceState.cs diff --git a/Ryujinx.Graphics.Device/IDeviceStateWithContext.cs b/src/Ryujinx.Graphics.Device/IDeviceStateWithContext.cs similarity index 100% rename from Ryujinx.Graphics.Device/IDeviceStateWithContext.cs rename to src/Ryujinx.Graphics.Device/IDeviceStateWithContext.cs diff --git a/Ryujinx.Graphics.Device/RwCallback.cs b/src/Ryujinx.Graphics.Device/RwCallback.cs similarity index 100% rename from Ryujinx.Graphics.Device/RwCallback.cs rename to src/Ryujinx.Graphics.Device/RwCallback.cs diff --git a/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj b/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj similarity index 100% rename from Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj rename to src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj diff --git a/Ryujinx.Graphics.Device/SizeCalculator.cs b/src/Ryujinx.Graphics.Device/SizeCalculator.cs similarity index 100% rename from Ryujinx.Graphics.Device/SizeCalculator.cs rename to src/Ryujinx.Graphics.Device/SizeCalculator.cs diff --git a/Ryujinx.Graphics.GAL/AddressMode.cs b/src/Ryujinx.Graphics.GAL/AddressMode.cs similarity index 100% rename from Ryujinx.Graphics.GAL/AddressMode.cs rename to src/Ryujinx.Graphics.GAL/AddressMode.cs diff --git a/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs rename to src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs diff --git a/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs similarity index 100% rename from Ryujinx.Graphics.GAL/AdvancedBlendOp.cs rename to src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs diff --git a/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs similarity index 100% rename from Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs rename to src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs diff --git a/Ryujinx.Graphics.GAL/AntiAliasing.cs b/src/Ryujinx.Graphics.GAL/AntiAliasing.cs similarity index 100% rename from Ryujinx.Graphics.GAL/AntiAliasing.cs rename to src/Ryujinx.Graphics.GAL/AntiAliasing.cs diff --git a/Ryujinx.Graphics.GAL/BlendDescriptor.cs b/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/BlendDescriptor.cs rename to src/Ryujinx.Graphics.GAL/BlendDescriptor.cs diff --git a/Ryujinx.Graphics.GAL/BlendFactor.cs b/src/Ryujinx.Graphics.GAL/BlendFactor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/BlendFactor.cs rename to src/Ryujinx.Graphics.GAL/BlendFactor.cs diff --git a/Ryujinx.Graphics.GAL/BlendOp.cs b/src/Ryujinx.Graphics.GAL/BlendOp.cs similarity index 100% rename from Ryujinx.Graphics.GAL/BlendOp.cs rename to src/Ryujinx.Graphics.GAL/BlendOp.cs diff --git a/Ryujinx.Graphics.GAL/BufferAssignment.cs b/src/Ryujinx.Graphics.GAL/BufferAssignment.cs similarity index 100% rename from Ryujinx.Graphics.GAL/BufferAssignment.cs rename to src/Ryujinx.Graphics.GAL/BufferAssignment.cs diff --git a/Ryujinx.Graphics.GAL/BufferHandle.cs b/src/Ryujinx.Graphics.GAL/BufferHandle.cs similarity index 100% rename from Ryujinx.Graphics.GAL/BufferHandle.cs rename to src/Ryujinx.Graphics.GAL/BufferHandle.cs diff --git a/Ryujinx.Graphics.GAL/BufferRange.cs b/src/Ryujinx.Graphics.GAL/BufferRange.cs similarity index 100% rename from Ryujinx.Graphics.GAL/BufferRange.cs rename to src/Ryujinx.Graphics.GAL/BufferRange.cs diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Capabilities.cs rename to src/Ryujinx.Graphics.GAL/Capabilities.cs diff --git a/Ryujinx.Graphics.GAL/ColorF.cs b/src/Ryujinx.Graphics.GAL/ColorF.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ColorF.cs rename to src/Ryujinx.Graphics.GAL/ColorF.cs diff --git a/Ryujinx.Graphics.GAL/CompareMode.cs b/src/Ryujinx.Graphics.GAL/CompareMode.cs similarity index 100% rename from Ryujinx.Graphics.GAL/CompareMode.cs rename to src/Ryujinx.Graphics.GAL/CompareMode.cs diff --git a/Ryujinx.Graphics.GAL/CompareOp.cs b/src/Ryujinx.Graphics.GAL/CompareOp.cs similarity index 100% rename from Ryujinx.Graphics.GAL/CompareOp.cs rename to src/Ryujinx.Graphics.GAL/CompareOp.cs diff --git a/Ryujinx.Graphics.GAL/CounterType.cs b/src/Ryujinx.Graphics.GAL/CounterType.cs similarity index 100% rename from Ryujinx.Graphics.GAL/CounterType.cs rename to src/Ryujinx.Graphics.GAL/CounterType.cs diff --git a/Ryujinx.Graphics.GAL/DepthMode.cs b/src/Ryujinx.Graphics.GAL/DepthMode.cs similarity index 100% rename from Ryujinx.Graphics.GAL/DepthMode.cs rename to src/Ryujinx.Graphics.GAL/DepthMode.cs diff --git a/Ryujinx.Graphics.GAL/DepthStencilMode.cs b/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs similarity index 100% rename from Ryujinx.Graphics.GAL/DepthStencilMode.cs rename to src/Ryujinx.Graphics.GAL/DepthStencilMode.cs diff --git a/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs b/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/DepthTestDescriptor.cs rename to src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs diff --git a/Ryujinx.Graphics.GAL/DeviceInfo.cs b/src/Ryujinx.Graphics.GAL/DeviceInfo.cs similarity index 100% rename from Ryujinx.Graphics.GAL/DeviceInfo.cs rename to src/Ryujinx.Graphics.GAL/DeviceInfo.cs diff --git a/Ryujinx.Graphics.GAL/Extents2D.cs b/src/Ryujinx.Graphics.GAL/Extents2D.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Extents2D.cs rename to src/Ryujinx.Graphics.GAL/Extents2D.cs diff --git a/Ryujinx.Graphics.GAL/Extents2DF.cs b/src/Ryujinx.Graphics.GAL/Extents2DF.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Extents2DF.cs rename to src/Ryujinx.Graphics.GAL/Extents2DF.cs diff --git a/Ryujinx.Graphics.GAL/Face.cs b/src/Ryujinx.Graphics.GAL/Face.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Face.cs rename to src/Ryujinx.Graphics.GAL/Face.cs diff --git a/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Format.cs rename to src/Ryujinx.Graphics.GAL/Format.cs diff --git a/Ryujinx.Graphics.GAL/FrontFace.cs b/src/Ryujinx.Graphics.GAL/FrontFace.cs similarity index 100% rename from Ryujinx.Graphics.GAL/FrontFace.cs rename to src/Ryujinx.Graphics.GAL/FrontFace.cs diff --git a/Ryujinx.Graphics.GAL/HardwareInfo.cs b/src/Ryujinx.Graphics.GAL/HardwareInfo.cs similarity index 100% rename from Ryujinx.Graphics.GAL/HardwareInfo.cs rename to src/Ryujinx.Graphics.GAL/HardwareInfo.cs diff --git a/Ryujinx.Graphics.GAL/ICounterEvent.cs b/src/Ryujinx.Graphics.GAL/ICounterEvent.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ICounterEvent.cs rename to src/Ryujinx.Graphics.GAL/ICounterEvent.cs diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs similarity index 100% rename from Ryujinx.Graphics.GAL/IPipeline.cs rename to src/Ryujinx.Graphics.GAL/IPipeline.cs diff --git a/Ryujinx.Graphics.GAL/IProgram.cs b/src/Ryujinx.Graphics.GAL/IProgram.cs similarity index 100% rename from Ryujinx.Graphics.GAL/IProgram.cs rename to src/Ryujinx.Graphics.GAL/IProgram.cs diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs similarity index 100% rename from Ryujinx.Graphics.GAL/IRenderer.cs rename to src/Ryujinx.Graphics.GAL/IRenderer.cs diff --git a/Ryujinx.Graphics.GAL/ISampler.cs b/src/Ryujinx.Graphics.GAL/ISampler.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ISampler.cs rename to src/Ryujinx.Graphics.GAL/ISampler.cs diff --git a/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ITexture.cs rename to src/Ryujinx.Graphics.GAL/ITexture.cs diff --git a/Ryujinx.Graphics.GAL/IWindow.cs b/src/Ryujinx.Graphics.GAL/IWindow.cs similarity index 100% rename from Ryujinx.Graphics.GAL/IWindow.cs rename to src/Ryujinx.Graphics.GAL/IWindow.cs diff --git a/Ryujinx.Graphics.GAL/ImageCrop.cs b/src/Ryujinx.Graphics.GAL/ImageCrop.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ImageCrop.cs rename to src/Ryujinx.Graphics.GAL/ImageCrop.cs diff --git a/Ryujinx.Graphics.GAL/IndexType.cs b/src/Ryujinx.Graphics.GAL/IndexType.cs similarity index 100% rename from Ryujinx.Graphics.GAL/IndexType.cs rename to src/Ryujinx.Graphics.GAL/IndexType.cs diff --git a/Ryujinx.Graphics.GAL/LogicalOp.cs b/src/Ryujinx.Graphics.GAL/LogicalOp.cs similarity index 100% rename from Ryujinx.Graphics.GAL/LogicalOp.cs rename to src/Ryujinx.Graphics.GAL/LogicalOp.cs diff --git a/Ryujinx.Graphics.GAL/MagFilter.cs b/src/Ryujinx.Graphics.GAL/MagFilter.cs similarity index 100% rename from Ryujinx.Graphics.GAL/MagFilter.cs rename to src/Ryujinx.Graphics.GAL/MagFilter.cs diff --git a/Ryujinx.Graphics.GAL/MinFilter.cs b/src/Ryujinx.Graphics.GAL/MinFilter.cs similarity index 100% rename from Ryujinx.Graphics.GAL/MinFilter.cs rename to src/Ryujinx.Graphics.GAL/MinFilter.cs diff --git a/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs b/src/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/MultisampleDescriptor.cs rename to src/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs b/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/CommandType.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs b/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs rename to src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs diff --git a/Ryujinx.Graphics.GAL/Origin.cs b/src/Ryujinx.Graphics.GAL/Origin.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Origin.cs rename to src/Ryujinx.Graphics.GAL/Origin.cs diff --git a/Ryujinx.Graphics.GAL/PinnedSpan.cs b/src/Ryujinx.Graphics.GAL/PinnedSpan.cs similarity index 100% rename from Ryujinx.Graphics.GAL/PinnedSpan.cs rename to src/Ryujinx.Graphics.GAL/PinnedSpan.cs diff --git a/Ryujinx.Graphics.GAL/PolygonMode.cs b/src/Ryujinx.Graphics.GAL/PolygonMode.cs similarity index 100% rename from Ryujinx.Graphics.GAL/PolygonMode.cs rename to src/Ryujinx.Graphics.GAL/PolygonMode.cs diff --git a/Ryujinx.Graphics.GAL/PolygonModeMask.cs b/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs similarity index 100% rename from Ryujinx.Graphics.GAL/PolygonModeMask.cs rename to src/Ryujinx.Graphics.GAL/PolygonModeMask.cs diff --git a/Ryujinx.Graphics.GAL/PrimitiveTopology.cs b/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs similarity index 100% rename from Ryujinx.Graphics.GAL/PrimitiveTopology.cs rename to src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs diff --git a/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs b/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ProgramLinkStatus.cs rename to src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs diff --git a/Ryujinx.Graphics.GAL/ProgramPipelineState.cs b/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ProgramPipelineState.cs rename to src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs diff --git a/Ryujinx.Graphics.GAL/Rectangle.cs b/src/Ryujinx.Graphics.GAL/Rectangle.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Rectangle.cs rename to src/Ryujinx.Graphics.GAL/Rectangle.cs diff --git a/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj b/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj similarity index 100% rename from Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj rename to src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj diff --git a/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs b/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs similarity index 100% rename from Ryujinx.Graphics.GAL/SamplerCreateInfo.cs rename to src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs diff --git a/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs b/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs rename to src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs diff --git a/Ryujinx.Graphics.GAL/ShaderBindings.cs b/src/Ryujinx.Graphics.GAL/ShaderBindings.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ShaderBindings.cs rename to src/Ryujinx.Graphics.GAL/ShaderBindings.cs diff --git a/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ShaderInfo.cs rename to src/Ryujinx.Graphics.GAL/ShaderInfo.cs diff --git a/Ryujinx.Graphics.GAL/ShaderSource.cs b/src/Ryujinx.Graphics.GAL/ShaderSource.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ShaderSource.cs rename to src/Ryujinx.Graphics.GAL/ShaderSource.cs diff --git a/Ryujinx.Graphics.GAL/StencilOp.cs b/src/Ryujinx.Graphics.GAL/StencilOp.cs similarity index 100% rename from Ryujinx.Graphics.GAL/StencilOp.cs rename to src/Ryujinx.Graphics.GAL/StencilOp.cs diff --git a/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs b/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/StencilTestDescriptor.cs rename to src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs diff --git a/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs similarity index 100% rename from Ryujinx.Graphics.GAL/SupportBufferUpdater.cs rename to src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs diff --git a/Ryujinx.Graphics.GAL/SwizzleComponent.cs b/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs similarity index 100% rename from Ryujinx.Graphics.GAL/SwizzleComponent.cs rename to src/Ryujinx.Graphics.GAL/SwizzleComponent.cs diff --git a/Ryujinx.Graphics.GAL/Target.cs b/src/Ryujinx.Graphics.GAL/Target.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Target.cs rename to src/Ryujinx.Graphics.GAL/Target.cs diff --git a/Ryujinx.Graphics.GAL/TextureCreateInfo.cs b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs similarity index 100% rename from Ryujinx.Graphics.GAL/TextureCreateInfo.cs rename to src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs diff --git a/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs b/src/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs similarity index 100% rename from Ryujinx.Graphics.GAL/TextureReleaseCallback.cs rename to src/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs diff --git a/Ryujinx.Graphics.GAL/UpscaleType.cs b/src/Ryujinx.Graphics.GAL/UpscaleType.cs similarity index 100% rename from Ryujinx.Graphics.GAL/UpscaleType.cs rename to src/Ryujinx.Graphics.GAL/UpscaleType.cs diff --git a/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs b/src/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs rename to src/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs diff --git a/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs b/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs rename to src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs diff --git a/Ryujinx.Graphics.GAL/Viewport.cs b/src/Ryujinx.Graphics.GAL/Viewport.cs similarity index 100% rename from Ryujinx.Graphics.GAL/Viewport.cs rename to src/Ryujinx.Graphics.GAL/Viewport.cs diff --git a/Ryujinx.Graphics.GAL/ViewportSwizzle.cs b/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs similarity index 100% rename from Ryujinx.Graphics.GAL/ViewportSwizzle.cs rename to src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs diff --git a/Ryujinx.Graphics.Gpu/ClassId.cs b/src/Ryujinx.Graphics.Gpu/ClassId.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/ClassId.cs rename to src/Ryujinx.Graphics.Gpu/ClassId.cs diff --git a/Ryujinx.Graphics.Gpu/Constants.cs b/src/Ryujinx.Graphics.Gpu/Constants.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Constants.cs rename to src/Ryujinx.Graphics.Gpu/Constants.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs b/src/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs rename to src/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs b/src/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs rename to src/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs rename to src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs rename to src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs rename to src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs rename to src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs rename to src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs rename to src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs rename to src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs rename to src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs b/src/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs rename to src/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs b/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs rename to src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs rename to src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs rename to src/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/src/Ryujinx.Graphics.Gpu/GpuChannel.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/GpuChannel.cs rename to src/Ryujinx.Graphics.Gpu/GpuChannel.cs diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/GpuContext.cs rename to src/Ryujinx.Graphics.Gpu/GpuContext.cs diff --git a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs b/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/GraphicsConfig.cs rename to src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs rename to src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs diff --git a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/FormatInfo.cs rename to src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs diff --git a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/FormatTable.cs rename to src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs diff --git a/Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs rename to src/Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/src/Ryujinx.Graphics.Gpu/Image/Pool.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/Pool.cs rename to src/Ryujinx.Graphics.Gpu/Image/Pool.cs diff --git a/Ryujinx.Graphics.Gpu/Image/PoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/PoolCache.cs rename to src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs diff --git a/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs b/src/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs rename to src/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs diff --git a/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/Sampler.cs rename to src/Ryujinx.Graphics.Gpu/Image/Sampler.cs diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs rename to src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs rename to src/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs rename to src/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/SamplerPool.cs rename to src/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs rename to src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/Texture.cs rename to src/Ryujinx.Graphics.Gpu/Image/Texture.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureCache.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureComponent.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDependency.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureDependency.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureDependency.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureDependency.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureGroup.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureInfo.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureManager.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TexturePool.cs rename to src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs rename to src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureTarget.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs diff --git a/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs rename to src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/Buffer.cs rename to src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs rename to src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/BufferCache.cs rename to src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs rename to src/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/BufferManager.cs rename to src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs rename to src/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs rename to src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs rename to src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/CounterCache.cs rename to src/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs b/src/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs rename to src/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs rename to src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs rename to src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/MultiRangeWritableBlock.cs b/src/Ryujinx.Graphics.Gpu/Memory/MultiRangeWritableBlock.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/MultiRangeWritableBlock.cs rename to src/Ryujinx.Graphics.Gpu/Memory/MultiRangeWritableBlock.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs rename to src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/PteKind.cs b/src/Ryujinx.Graphics.Gpu/Memory/PteKind.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/PteKind.cs rename to src/Ryujinx.Graphics.Gpu/Memory/PteKind.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs b/src/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs rename to src/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs b/src/Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs rename to src/Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs diff --git a/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs rename to src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs diff --git a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj b/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj similarity index 100% rename from Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj rename to src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs rename to src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs rename to src/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs rename to src/Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs rename to src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs rename to src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs rename to src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs rename to src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs rename to src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs rename to src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs rename to src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs rename to src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/IDataAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/IDataAccessor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/HashTable/IDataAccessor.cs rename to src/Ryujinx.Graphics.Gpu/Shader/HashTable/IDataAccessor.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs rename to src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs rename to src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs rename to src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs b/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs rename to src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs diff --git a/Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs rename to src/Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs diff --git a/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs rename to src/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs diff --git a/Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs rename to src/Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs diff --git a/Ryujinx.Graphics.Gpu/Synchronization/SyncpointWaiterHandle.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/SyncpointWaiterHandle.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Synchronization/SyncpointWaiterHandle.cs rename to src/Ryujinx.Graphics.Gpu/Synchronization/SyncpointWaiterHandle.cs diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs similarity index 100% rename from Ryujinx.Graphics.Gpu/Window.cs rename to src/Ryujinx.Graphics.Gpu/Window.cs diff --git a/Ryujinx.Graphics.Host1x/ClassId.cs b/src/Ryujinx.Graphics.Host1x/ClassId.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/ClassId.cs rename to src/Ryujinx.Graphics.Host1x/ClassId.cs diff --git a/Ryujinx.Graphics.Host1x/Devices.cs b/src/Ryujinx.Graphics.Host1x/Devices.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/Devices.cs rename to src/Ryujinx.Graphics.Host1x/Devices.cs diff --git a/Ryujinx.Graphics.Host1x/Host1xClass.cs b/src/Ryujinx.Graphics.Host1x/Host1xClass.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/Host1xClass.cs rename to src/Ryujinx.Graphics.Host1x/Host1xClass.cs diff --git a/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs b/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs rename to src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs diff --git a/Ryujinx.Graphics.Host1x/Host1xDevice.cs b/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/Host1xDevice.cs rename to src/Ryujinx.Graphics.Host1x/Host1xDevice.cs diff --git a/Ryujinx.Graphics.Host1x/OpCode.cs b/src/Ryujinx.Graphics.Host1x/OpCode.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/OpCode.cs rename to src/Ryujinx.Graphics.Host1x/OpCode.cs diff --git a/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj b/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj similarity index 100% rename from Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj rename to src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj diff --git a/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs b/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/SyncptIncrManager.cs rename to src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs diff --git a/Ryujinx.Graphics.Host1x/ThiDevice.cs b/src/Ryujinx.Graphics.Host1x/ThiDevice.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/ThiDevice.cs rename to src/Ryujinx.Graphics.Host1x/ThiDevice.cs diff --git a/Ryujinx.Graphics.Host1x/ThiRegisters.cs b/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs similarity index 100% rename from Ryujinx.Graphics.Host1x/ThiRegisters.cs rename to src/Ryujinx.Graphics.Host1x/ThiRegisters.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecID.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecID.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecID.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecID.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVRational.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVRational.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVRational.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVRational.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs rename to src/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Constants.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Idct.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Luts.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj b/src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj rename to src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj diff --git a/Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs rename to src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs diff --git a/Ryujinx.Graphics.Nvdec/ApplicationId.cs b/src/Ryujinx.Graphics.Nvdec/ApplicationId.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/ApplicationId.cs rename to src/Ryujinx.Graphics.Nvdec/ApplicationId.cs diff --git a/Ryujinx.Graphics.Nvdec/H264Decoder.cs b/src/Ryujinx.Graphics.Nvdec/H264Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/H264Decoder.cs rename to src/Ryujinx.Graphics.Nvdec/H264Decoder.cs diff --git a/Ryujinx.Graphics.Nvdec/Image/SurfaceCache.cs b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceCache.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Image/SurfaceCache.cs rename to src/Ryujinx.Graphics.Nvdec/Image/SurfaceCache.cs diff --git a/Ryujinx.Graphics.Nvdec/Image/SurfaceCommon.cs b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceCommon.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Image/SurfaceCommon.cs rename to src/Ryujinx.Graphics.Nvdec/Image/SurfaceCommon.cs diff --git a/Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs rename to src/Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs diff --git a/Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs rename to src/Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs diff --git a/Ryujinx.Graphics.Nvdec/MemoryExtensions.cs b/src/Ryujinx.Graphics.Nvdec/MemoryExtensions.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/MemoryExtensions.cs rename to src/Ryujinx.Graphics.Nvdec/MemoryExtensions.cs diff --git a/Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs b/src/Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs rename to src/Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs diff --git a/Ryujinx.Graphics.Nvdec/NvdecDevice.cs b/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/NvdecDevice.cs rename to src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs diff --git a/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs b/src/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/NvdecRegisters.cs rename to src/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs diff --git a/Ryujinx.Graphics.Nvdec/NvdecStatus.cs b/src/Ryujinx.Graphics.Nvdec/NvdecStatus.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/NvdecStatus.cs rename to src/Ryujinx.Graphics.Nvdec/NvdecStatus.cs diff --git a/Ryujinx.Graphics.Nvdec/ResourceManager.cs b/src/Ryujinx.Graphics.Nvdec/ResourceManager.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/ResourceManager.cs rename to src/Ryujinx.Graphics.Nvdec/ResourceManager.cs diff --git a/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj b/src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj similarity index 100% rename from Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj rename to src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj diff --git a/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs rename to src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs b/src/Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs rename to src/Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs diff --git a/Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs rename to src/Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs diff --git a/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs b/src/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Vp8Decoder.cs rename to src/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs diff --git a/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs b/src/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Nvdec/Vp9Decoder.cs rename to src/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs diff --git a/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs b/src/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs rename to src/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/src/Ryujinx.Graphics.OpenGL/Buffer.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Buffer.cs rename to src/Ryujinx.Graphics.OpenGL/Buffer.cs diff --git a/Ryujinx.Graphics.OpenGL/Constants.cs b/src/Ryujinx.Graphics.OpenGL/Constants.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Constants.cs rename to src/Ryujinx.Graphics.OpenGL/Constants.cs diff --git a/Ryujinx.Graphics.OpenGL/Debugger.cs b/src/Ryujinx.Graphics.OpenGL/Debugger.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Debugger.cs rename to src/Ryujinx.Graphics.OpenGL/Debugger.cs diff --git a/Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs b/src/Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs rename to src/Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs diff --git a/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs rename to src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs diff --git a/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs rename to src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs diff --git a/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs rename to src/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs diff --git a/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs b/src/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs rename to src/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs diff --git a/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs b/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs rename to src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl diff --git a/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl b/src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl rename to src/Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl diff --git a/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs rename to src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs diff --git a/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin b/src/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin rename to src/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin diff --git a/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin b/src/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin similarity index 100% rename from Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin rename to src/Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin diff --git a/Ryujinx.Graphics.OpenGL/EnumConversion.cs b/src/Ryujinx.Graphics.OpenGL/EnumConversion.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/EnumConversion.cs rename to src/Ryujinx.Graphics.OpenGL/EnumConversion.cs diff --git a/Ryujinx.Graphics.OpenGL/FormatInfo.cs b/src/Ryujinx.Graphics.OpenGL/FormatInfo.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/FormatInfo.cs rename to src/Ryujinx.Graphics.OpenGL/FormatInfo.cs diff --git a/Ryujinx.Graphics.OpenGL/FormatTable.cs b/src/Ryujinx.Graphics.OpenGL/FormatTable.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/FormatTable.cs rename to src/Ryujinx.Graphics.OpenGL/FormatTable.cs diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Framebuffer.cs rename to src/Ryujinx.Graphics.OpenGL/Framebuffer.cs diff --git a/Ryujinx.Graphics.OpenGL/Handle.cs b/src/Ryujinx.Graphics.OpenGL/Handle.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Handle.cs rename to src/Ryujinx.Graphics.OpenGL/Handle.cs diff --git a/Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs b/src/Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs rename to src/Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs diff --git a/Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs b/src/Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs rename to src/Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/HwCapabilities.cs rename to src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs diff --git a/Ryujinx.Graphics.OpenGL/IOpenGLContext.cs b/src/Ryujinx.Graphics.OpenGL/IOpenGLContext.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/IOpenGLContext.cs rename to src/Ryujinx.Graphics.OpenGL/IOpenGLContext.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs b/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs rename to src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/ITextureInfo.cs b/src/Ryujinx.Graphics.OpenGL/Image/ITextureInfo.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/ITextureInfo.cs rename to src/Ryujinx.Graphics.OpenGL/Image/ITextureInfo.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs b/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs rename to src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/Sampler.cs b/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/Sampler.cs rename to src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/TextureBase.cs rename to src/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs rename to src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs rename to src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs rename to src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs rename to src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs rename to src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Image/TextureView.cs rename to src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs rename to src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs diff --git a/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/PersistentBuffers.cs rename to src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Pipeline.cs rename to src/Ryujinx.Graphics.OpenGL/Pipeline.cs diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/src/Ryujinx.Graphics.OpenGL/Program.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Program.cs rename to src/Ryujinx.Graphics.OpenGL/Program.cs diff --git a/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs rename to src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs diff --git a/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs rename to src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs diff --git a/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs rename to src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs diff --git a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Queries/Counters.cs rename to src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs diff --git a/Ryujinx.Graphics.OpenGL/ResourcePool.cs b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/ResourcePool.cs rename to src/Ryujinx.Graphics.OpenGL/ResourcePool.cs diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj similarity index 100% rename from Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj rename to src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj diff --git a/Ryujinx.Graphics.OpenGL/Sync.cs b/src/Ryujinx.Graphics.OpenGL/Sync.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Sync.cs rename to src/Ryujinx.Graphics.OpenGL/Sync.cs diff --git a/Ryujinx.Graphics.OpenGL/VertexArray.cs b/src/Ryujinx.Graphics.OpenGL/VertexArray.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/VertexArray.cs rename to src/Ryujinx.Graphics.OpenGL/VertexArray.cs diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs similarity index 100% rename from Ryujinx.Graphics.OpenGL/Window.cs rename to src/Ryujinx.Graphics.OpenGL/Window.cs diff --git a/Ryujinx.Graphics.Shader/AlphaTestOp.cs b/src/Ryujinx.Graphics.Shader/AlphaTestOp.cs similarity index 100% rename from Ryujinx.Graphics.Shader/AlphaTestOp.cs rename to src/Ryujinx.Graphics.Shader/AlphaTestOp.cs diff --git a/Ryujinx.Graphics.Shader/AttributeType.cs b/src/Ryujinx.Graphics.Shader/AttributeType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/AttributeType.cs rename to src/Ryujinx.Graphics.Shader/AttributeType.cs diff --git a/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.Shader/BufferDescriptor.cs rename to src/Ryujinx.Graphics.Shader/BufferDescriptor.cs diff --git a/Ryujinx.Graphics.Shader/BufferUsageFlags.cs b/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs similarity index 100% rename from Ryujinx.Graphics.Shader/BufferUsageFlags.cs rename to src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs similarity index 100% rename from Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs diff --git a/Ryujinx.Graphics.Shader/Constants.cs b/src/Ryujinx.Graphics.Shader/Constants.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Constants.cs rename to src/Ryujinx.Graphics.Shader/Constants.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/Block.cs b/src/Ryujinx.Graphics.Shader/Decoders/Block.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/Block.cs rename to src/Ryujinx.Graphics.Shader/Decoders/Block.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs b/src/Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs rename to src/Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs b/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs rename to src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/Decoder.cs rename to src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/FunctionType.cs b/src/Ryujinx.Graphics.Shader/Decoders/FunctionType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/FunctionType.cs rename to src/Ryujinx.Graphics.Shader/Decoders/FunctionType.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs rename to src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/InstName.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstName.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/InstName.cs rename to src/Ryujinx.Graphics.Shader/Decoders/InstName.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/InstOp.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstOp.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/InstOp.cs rename to src/Ryujinx.Graphics.Shader/Decoders/InstOp.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/InstProps.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/InstProps.cs rename to src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/InstTable.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstTable.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/InstTable.cs rename to src/Ryujinx.Graphics.Shader/Decoders/InstTable.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/Register.cs b/src/Ryujinx.Graphics.Shader/Decoders/Register.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/Register.cs rename to src/Ryujinx.Graphics.Shader/Decoders/Register.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs b/src/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs rename to src/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs b/src/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Decoders/RegisterType.cs rename to src/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IGpuAccessor.cs rename to src/Ryujinx.Graphics.Shader/IGpuAccessor.cs diff --git a/Ryujinx.Graphics.Shader/InputTopology.cs b/src/Ryujinx.Graphics.Shader/InputTopology.cs similarity index 100% rename from Ryujinx.Graphics.Shader/InputTopology.cs rename to src/Ryujinx.Graphics.Shader/InputTopology.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs b/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs rename to src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmit.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs rename to src/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs diff --git a/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs b/src/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs rename to src/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs similarity index 100% rename from Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs rename to src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs diff --git a/Ryujinx.Graphics.Shader/OutputTopology.cs b/src/Ryujinx.Graphics.Shader/OutputTopology.cs similarity index 100% rename from Ryujinx.Graphics.Shader/OutputTopology.cs rename to src/Ryujinx.Graphics.Shader/OutputTopology.cs diff --git a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj similarity index 100% rename from Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj rename to src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj diff --git a/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/SamplerType.cs rename to src/Ryujinx.Graphics.Shader/SamplerType.cs diff --git a/Ryujinx.Graphics.Shader/ShaderIdentification.cs b/src/Ryujinx.Graphics.Shader/ShaderIdentification.cs similarity index 100% rename from Ryujinx.Graphics.Shader/ShaderIdentification.cs rename to src/Ryujinx.Graphics.Shader/ShaderIdentification.cs diff --git a/Ryujinx.Graphics.Shader/ShaderProgram.cs b/src/Ryujinx.Graphics.Shader/ShaderProgram.cs similarity index 100% rename from Ryujinx.Graphics.Shader/ShaderProgram.cs rename to src/Ryujinx.Graphics.Shader/ShaderProgram.cs diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs similarity index 100% rename from Ryujinx.Graphics.Shader/ShaderProgramInfo.cs rename to src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs diff --git a/Ryujinx.Graphics.Shader/ShaderStage.cs b/src/Ryujinx.Graphics.Shader/ShaderStage.cs similarity index 100% rename from Ryujinx.Graphics.Shader/ShaderStage.cs rename to src/Ryujinx.Graphics.Shader/ShaderStage.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs similarity index 100% rename from Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs rename to src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs diff --git a/Ryujinx.Graphics.Shader/SupportBuffer.cs b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Shader/SupportBuffer.cs rename to src/Ryujinx.Graphics.Shader/SupportBuffer.cs diff --git a/Ryujinx.Graphics.Shader/TessPatchType.cs b/src/Ryujinx.Graphics.Shader/TessPatchType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/TessPatchType.cs rename to src/Ryujinx.Graphics.Shader/TessPatchType.cs diff --git a/Ryujinx.Graphics.Shader/TessSpacing.cs b/src/Ryujinx.Graphics.Shader/TessSpacing.cs similarity index 100% rename from Ryujinx.Graphics.Shader/TessSpacing.cs rename to src/Ryujinx.Graphics.Shader/TessSpacing.cs diff --git a/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs similarity index 100% rename from Ryujinx.Graphics.Shader/TextureDescriptor.cs rename to src/Ryujinx.Graphics.Shader/TextureDescriptor.cs diff --git a/Ryujinx.Graphics.Shader/TextureFormat.cs b/src/Ryujinx.Graphics.Shader/TextureFormat.cs similarity index 100% rename from Ryujinx.Graphics.Shader/TextureFormat.cs rename to src/Ryujinx.Graphics.Shader/TextureFormat.cs diff --git a/Ryujinx.Graphics.Shader/TextureHandle.cs b/src/Ryujinx.Graphics.Shader/TextureHandle.cs similarity index 100% rename from Ryujinx.Graphics.Shader/TextureHandle.cs rename to src/Ryujinx.Graphics.Shader/TextureHandle.cs diff --git a/Ryujinx.Graphics.Shader/TextureUsageFlags.cs b/src/Ryujinx.Graphics.Shader/TextureUsageFlags.cs similarity index 100% rename from Ryujinx.Graphics.Shader/TextureUsageFlags.cs rename to src/Ryujinx.Graphics.Shader/TextureUsageFlags.cs diff --git a/Ryujinx.Graphics.Shader/Translation/AggregateType.cs b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/AggregateType.cs rename to src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/src/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs rename to src/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs diff --git a/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs b/src/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs rename to src/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Dominance.cs b/src/Ryujinx.Graphics.Shader/Translation/Dominance.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Dominance.cs rename to src/Ryujinx.Graphics.Shader/Translation/Dominance.cs diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/EmitterContext.cs rename to src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs rename to src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs rename to src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs diff --git a/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs b/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs rename to src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs diff --git a/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs b/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs rename to src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs rename to src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs diff --git a/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs b/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs rename to src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Rewriter.cs rename to src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs rename to src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs rename to src/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs rename to src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Ssa.cs b/src/Ryujinx.Graphics.Shader/Translation/Ssa.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Ssa.cs rename to src/Ryujinx.Graphics.Shader/Translation/Ssa.cs diff --git a/Ryujinx.Graphics.Shader/Translation/TargetApi.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/TargetApi.cs rename to src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs diff --git a/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs rename to src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs rename to src/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs rename to src/Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/Translator.cs rename to src/Ryujinx.Graphics.Shader/Translation/Translator.cs diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs similarity index 100% rename from Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs rename to src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs diff --git a/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs rename to src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs diff --git a/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs rename to src/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs diff --git a/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/AstcPixel.cs rename to src/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs diff --git a/Ryujinx.Graphics.Texture/Astc/BitStream128.cs b/src/Ryujinx.Graphics.Texture/Astc/BitStream128.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/BitStream128.cs rename to src/Ryujinx.Graphics.Texture/Astc/BitStream128.cs diff --git a/Ryujinx.Graphics.Texture/Astc/Bits.cs b/src/Ryujinx.Graphics.Texture/Astc/Bits.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/Bits.cs rename to src/Ryujinx.Graphics.Texture/Astc/Bits.cs diff --git a/Ryujinx.Graphics.Texture/Astc/EndPointSet.cs b/src/Ryujinx.Graphics.Texture/Astc/EndPointSet.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/EndPointSet.cs rename to src/Ryujinx.Graphics.Texture/Astc/EndPointSet.cs diff --git a/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs b/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs rename to src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs diff --git a/Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs b/src/Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs rename to src/Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs diff --git a/Ryujinx.Graphics.Texture/BC6Decoder.cs b/src/Ryujinx.Graphics.Texture/BC6Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Texture/BC6Decoder.cs rename to src/Ryujinx.Graphics.Texture/BC6Decoder.cs diff --git a/Ryujinx.Graphics.Texture/BC7Decoder.cs b/src/Ryujinx.Graphics.Texture/BC7Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Texture/BC7Decoder.cs rename to src/Ryujinx.Graphics.Texture/BC7Decoder.cs diff --git a/Ryujinx.Graphics.Texture/BCnDecoder.cs b/src/Ryujinx.Graphics.Texture/BCnDecoder.cs similarity index 100% rename from Ryujinx.Graphics.Texture/BCnDecoder.cs rename to src/Ryujinx.Graphics.Texture/BCnDecoder.cs diff --git a/Ryujinx.Graphics.Texture/BCnEncoder.cs b/src/Ryujinx.Graphics.Texture/BCnEncoder.cs similarity index 100% rename from Ryujinx.Graphics.Texture/BCnEncoder.cs rename to src/Ryujinx.Graphics.Texture/BCnEncoder.cs diff --git a/Ryujinx.Graphics.Texture/BlockLinearConstants.cs b/src/Ryujinx.Graphics.Texture/BlockLinearConstants.cs similarity index 100% rename from Ryujinx.Graphics.Texture/BlockLinearConstants.cs rename to src/Ryujinx.Graphics.Texture/BlockLinearConstants.cs diff --git a/Ryujinx.Graphics.Texture/BlockLinearLayout.cs b/src/Ryujinx.Graphics.Texture/BlockLinearLayout.cs similarity index 100% rename from Ryujinx.Graphics.Texture/BlockLinearLayout.cs rename to src/Ryujinx.Graphics.Texture/BlockLinearLayout.cs diff --git a/Ryujinx.Graphics.Texture/Bpp12Pixel.cs b/src/Ryujinx.Graphics.Texture/Bpp12Pixel.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Bpp12Pixel.cs rename to src/Ryujinx.Graphics.Texture/Bpp12Pixel.cs diff --git a/Ryujinx.Graphics.Texture/ETC2Decoder.cs b/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Texture/ETC2Decoder.cs rename to src/Ryujinx.Graphics.Texture/ETC2Decoder.cs diff --git a/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs b/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs rename to src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs diff --git a/Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs b/src/Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs rename to src/Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs diff --git a/Ryujinx.Graphics.Texture/LayoutConverter.cs b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs similarity index 100% rename from Ryujinx.Graphics.Texture/LayoutConverter.cs rename to src/Ryujinx.Graphics.Texture/LayoutConverter.cs diff --git a/Ryujinx.Graphics.Texture/OffsetCalculator.cs b/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs similarity index 100% rename from Ryujinx.Graphics.Texture/OffsetCalculator.cs rename to src/Ryujinx.Graphics.Texture/OffsetCalculator.cs diff --git a/Ryujinx.Graphics.Texture/PixelConverter.cs b/src/Ryujinx.Graphics.Texture/PixelConverter.cs similarity index 100% rename from Ryujinx.Graphics.Texture/PixelConverter.cs rename to src/Ryujinx.Graphics.Texture/PixelConverter.cs diff --git a/Ryujinx.Graphics.Texture/Region.cs b/src/Ryujinx.Graphics.Texture/Region.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Region.cs rename to src/Ryujinx.Graphics.Texture/Region.cs diff --git a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj b/src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj similarity index 100% rename from Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj rename to src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj diff --git a/Ryujinx.Graphics.Texture/Size.cs b/src/Ryujinx.Graphics.Texture/Size.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Size.cs rename to src/Ryujinx.Graphics.Texture/Size.cs diff --git a/Ryujinx.Graphics.Texture/SizeCalculator.cs b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs similarity index 100% rename from Ryujinx.Graphics.Texture/SizeCalculator.cs rename to src/Ryujinx.Graphics.Texture/SizeCalculator.cs diff --git a/Ryujinx.Graphics.Texture/SizeInfo.cs b/src/Ryujinx.Graphics.Texture/SizeInfo.cs similarity index 100% rename from Ryujinx.Graphics.Texture/SizeInfo.cs rename to src/Ryujinx.Graphics.Texture/SizeInfo.cs diff --git a/Ryujinx.Graphics.Texture/Utils/BC67Tables.cs b/src/Ryujinx.Graphics.Texture/Utils/BC67Tables.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Utils/BC67Tables.cs rename to src/Ryujinx.Graphics.Texture/Utils/BC67Tables.cs diff --git a/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs b/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Utils/BC67Utils.cs rename to src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs diff --git a/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs b/src/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs rename to src/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs diff --git a/Ryujinx.Graphics.Texture/Utils/Block.cs b/src/Ryujinx.Graphics.Texture/Utils/Block.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Utils/Block.cs rename to src/Ryujinx.Graphics.Texture/Utils/Block.cs diff --git a/Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs rename to src/Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs diff --git a/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs similarity index 100% rename from Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs rename to src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs diff --git a/Ryujinx.Graphics.Vic/Blender.cs b/src/Ryujinx.Graphics.Vic/Blender.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Blender.cs rename to src/Ryujinx.Graphics.Vic/Blender.cs diff --git a/Ryujinx.Graphics.Vic/Image/BufferPool.cs b/src/Ryujinx.Graphics.Vic/Image/BufferPool.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Image/BufferPool.cs rename to src/Ryujinx.Graphics.Vic/Image/BufferPool.cs diff --git a/Ryujinx.Graphics.Vic/Image/InputSurface.cs b/src/Ryujinx.Graphics.Vic/Image/InputSurface.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Image/InputSurface.cs rename to src/Ryujinx.Graphics.Vic/Image/InputSurface.cs diff --git a/Ryujinx.Graphics.Vic/Image/Pixel.cs b/src/Ryujinx.Graphics.Vic/Image/Pixel.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Image/Pixel.cs rename to src/Ryujinx.Graphics.Vic/Image/Pixel.cs diff --git a/Ryujinx.Graphics.Vic/Image/Surface.cs b/src/Ryujinx.Graphics.Vic/Image/Surface.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Image/Surface.cs rename to src/Ryujinx.Graphics.Vic/Image/Surface.cs diff --git a/Ryujinx.Graphics.Vic/Image/SurfaceCommon.cs b/src/Ryujinx.Graphics.Vic/Image/SurfaceCommon.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Image/SurfaceCommon.cs rename to src/Ryujinx.Graphics.Vic/Image/SurfaceCommon.cs diff --git a/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs b/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Image/SurfaceReader.cs rename to src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs diff --git a/Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs b/src/Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs rename to src/Ryujinx.Graphics.Vic/Image/SurfaceWriter.cs diff --git a/Ryujinx.Graphics.Vic/Rectangle.cs b/src/Ryujinx.Graphics.Vic/Rectangle.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Rectangle.cs rename to src/Ryujinx.Graphics.Vic/Rectangle.cs diff --git a/Ryujinx.Graphics.Vic/ResourceManager.cs b/src/Ryujinx.Graphics.Vic/ResourceManager.cs similarity index 100% rename from Ryujinx.Graphics.Vic/ResourceManager.cs rename to src/Ryujinx.Graphics.Vic/ResourceManager.cs diff --git a/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj b/src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj similarity index 100% rename from Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj rename to src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj diff --git a/Ryujinx.Graphics.Vic/Scaler.cs b/src/Ryujinx.Graphics.Vic/Scaler.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Scaler.cs rename to src/Ryujinx.Graphics.Vic/Scaler.cs diff --git a/Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs b/src/Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs rename to src/Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs diff --git a/Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs b/src/Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs rename to src/Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs diff --git a/Ryujinx.Graphics.Vic/Types/ConfigStruct.cs b/src/Ryujinx.Graphics.Vic/Types/ConfigStruct.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/ConfigStruct.cs rename to src/Ryujinx.Graphics.Vic/Types/ConfigStruct.cs diff --git a/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs b/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs rename to src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs diff --git a/Ryujinx.Graphics.Vic/Types/FrameFormat.cs b/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/FrameFormat.cs rename to src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs diff --git a/Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs b/src/Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs rename to src/Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs diff --git a/Ryujinx.Graphics.Vic/Types/MatrixStruct.cs b/src/Ryujinx.Graphics.Vic/Types/MatrixStruct.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/MatrixStruct.cs rename to src/Ryujinx.Graphics.Vic/Types/MatrixStruct.cs diff --git a/Ryujinx.Graphics.Vic/Types/OutputConfig.cs b/src/Ryujinx.Graphics.Vic/Types/OutputConfig.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/OutputConfig.cs rename to src/Ryujinx.Graphics.Vic/Types/OutputConfig.cs diff --git a/Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs b/src/Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs rename to src/Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs diff --git a/Ryujinx.Graphics.Vic/Types/PipeConfig.cs b/src/Ryujinx.Graphics.Vic/Types/PipeConfig.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/PipeConfig.cs rename to src/Ryujinx.Graphics.Vic/Types/PipeConfig.cs diff --git a/Ryujinx.Graphics.Vic/Types/PixelFormat.cs b/src/Ryujinx.Graphics.Vic/Types/PixelFormat.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/PixelFormat.cs rename to src/Ryujinx.Graphics.Vic/Types/PixelFormat.cs diff --git a/Ryujinx.Graphics.Vic/Types/SlotConfig.cs b/src/Ryujinx.Graphics.Vic/Types/SlotConfig.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/SlotConfig.cs rename to src/Ryujinx.Graphics.Vic/Types/SlotConfig.cs diff --git a/Ryujinx.Graphics.Vic/Types/SlotStruct.cs b/src/Ryujinx.Graphics.Vic/Types/SlotStruct.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/SlotStruct.cs rename to src/Ryujinx.Graphics.Vic/Types/SlotStruct.cs diff --git a/Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs b/src/Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs similarity index 100% rename from Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs rename to src/Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs diff --git a/Ryujinx.Graphics.Vic/VicDevice.cs b/src/Ryujinx.Graphics.Vic/VicDevice.cs similarity index 100% rename from Ryujinx.Graphics.Vic/VicDevice.cs rename to src/Ryujinx.Graphics.Vic/VicDevice.cs diff --git a/Ryujinx.Graphics.Vic/VicRegisters.cs b/src/Ryujinx.Graphics.Vic/VicRegisters.cs similarity index 100% rename from Ryujinx.Graphics.Vic/VicRegisters.cs rename to src/Ryujinx.Graphics.Vic/VicRegisters.cs diff --git a/Ryujinx.Graphics.Video/FrameField.cs b/src/Ryujinx.Graphics.Video/FrameField.cs similarity index 100% rename from Ryujinx.Graphics.Video/FrameField.cs rename to src/Ryujinx.Graphics.Video/FrameField.cs diff --git a/Ryujinx.Graphics.Video/H264PictureInfo.cs b/src/Ryujinx.Graphics.Video/H264PictureInfo.cs similarity index 100% rename from Ryujinx.Graphics.Video/H264PictureInfo.cs rename to src/Ryujinx.Graphics.Video/H264PictureInfo.cs diff --git a/Ryujinx.Graphics.Video/IDecoder.cs b/src/Ryujinx.Graphics.Video/IDecoder.cs similarity index 100% rename from Ryujinx.Graphics.Video/IDecoder.cs rename to src/Ryujinx.Graphics.Video/IDecoder.cs diff --git a/Ryujinx.Graphics.Video/IH264Decoder.cs b/src/Ryujinx.Graphics.Video/IH264Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Video/IH264Decoder.cs rename to src/Ryujinx.Graphics.Video/IH264Decoder.cs diff --git a/Ryujinx.Graphics.Video/ISurface.cs b/src/Ryujinx.Graphics.Video/ISurface.cs similarity index 100% rename from Ryujinx.Graphics.Video/ISurface.cs rename to src/Ryujinx.Graphics.Video/ISurface.cs diff --git a/Ryujinx.Graphics.Video/IVp9Decoder.cs b/src/Ryujinx.Graphics.Video/IVp9Decoder.cs similarity index 100% rename from Ryujinx.Graphics.Video/IVp9Decoder.cs rename to src/Ryujinx.Graphics.Video/IVp9Decoder.cs diff --git a/Ryujinx.Graphics.Video/Plane.cs b/src/Ryujinx.Graphics.Video/Plane.cs similarity index 100% rename from Ryujinx.Graphics.Video/Plane.cs rename to src/Ryujinx.Graphics.Video/Plane.cs diff --git a/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj b/src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj similarity index 100% rename from Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj rename to src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj diff --git a/Ryujinx.Graphics.Video/Vp8PictureInfo.cs b/src/Ryujinx.Graphics.Video/Vp8PictureInfo.cs similarity index 100% rename from Ryujinx.Graphics.Video/Vp8PictureInfo.cs rename to src/Ryujinx.Graphics.Video/Vp8PictureInfo.cs diff --git a/Ryujinx.Graphics.Video/Vp9BackwardUpdates.cs b/src/Ryujinx.Graphics.Video/Vp9BackwardUpdates.cs similarity index 100% rename from Ryujinx.Graphics.Video/Vp9BackwardUpdates.cs rename to src/Ryujinx.Graphics.Video/Vp9BackwardUpdates.cs diff --git a/Ryujinx.Graphics.Video/Vp9EntropyProbs.cs b/src/Ryujinx.Graphics.Video/Vp9EntropyProbs.cs similarity index 100% rename from Ryujinx.Graphics.Video/Vp9EntropyProbs.cs rename to src/Ryujinx.Graphics.Video/Vp9EntropyProbs.cs diff --git a/Ryujinx.Graphics.Video/Vp9Mv.cs b/src/Ryujinx.Graphics.Video/Vp9Mv.cs similarity index 100% rename from Ryujinx.Graphics.Video/Vp9Mv.cs rename to src/Ryujinx.Graphics.Video/Vp9Mv.cs diff --git a/Ryujinx.Graphics.Video/Vp9MvRef.cs b/src/Ryujinx.Graphics.Video/Vp9MvRef.cs similarity index 100% rename from Ryujinx.Graphics.Video/Vp9MvRef.cs rename to src/Ryujinx.Graphics.Video/Vp9MvRef.cs diff --git a/Ryujinx.Graphics.Video/Vp9PictureInfo.cs b/src/Ryujinx.Graphics.Video/Vp9PictureInfo.cs similarity index 100% rename from Ryujinx.Graphics.Video/Vp9PictureInfo.cs rename to src/Ryujinx.Graphics.Video/Vp9PictureInfo.cs diff --git a/Ryujinx.Graphics.Vulkan/Auto.cs b/src/Ryujinx.Graphics.Vulkan/Auto.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Auto.cs rename to src/Ryujinx.Graphics.Vulkan/Auto.cs diff --git a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs rename to src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs diff --git a/Ryujinx.Graphics.Vulkan/BackgroundResources.cs b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/BackgroundResources.cs rename to src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs diff --git a/Ryujinx.Graphics.Vulkan/BitMap.cs b/src/Ryujinx.Graphics.Vulkan/BitMap.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/BitMap.cs rename to src/Ryujinx.Graphics.Vulkan/BitMap.cs diff --git a/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs b/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/BufferAllocationType.cs rename to src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/BufferHolder.cs rename to src/Ryujinx.Graphics.Vulkan/BufferHolder.cs diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/BufferManager.cs rename to src/Ryujinx.Graphics.Vulkan/BufferManager.cs diff --git a/Ryujinx.Graphics.Vulkan/BufferState.cs b/src/Ryujinx.Graphics.Vulkan/BufferState.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/BufferState.cs rename to src/Ryujinx.Graphics.Vulkan/BufferState.cs diff --git a/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs b/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs rename to src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs diff --git a/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/src/Ryujinx.Graphics.Vulkan/CacheByRange.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/CacheByRange.cs rename to src/Ryujinx.Graphics.Vulkan/CacheByRange.cs diff --git a/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/CommandBufferPool.cs rename to src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs diff --git a/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs rename to src/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs diff --git a/Ryujinx.Graphics.Vulkan/Constants.cs b/src/Ryujinx.Graphics.Vulkan/Constants.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Constants.cs rename to src/Ryujinx.Graphics.Vulkan/Constants.cs diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs rename to src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs rename to src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs rename to src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs b/src/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableBuffer.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableBufferView.cs b/src/Ryujinx.Graphics.Vulkan/DisposableBufferView.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableBufferView.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableBufferView.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs b/src/Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableImage.cs b/src/Ryujinx.Graphics.Vulkan/DisposableImage.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableImage.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableImage.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableImageView.cs b/src/Ryujinx.Graphics.Vulkan/DisposableImageView.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableImageView.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableImageView.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableMemory.cs b/src/Ryujinx.Graphics.Vulkan/DisposableMemory.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableMemory.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableMemory.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposablePipeline.cs b/src/Ryujinx.Graphics.Vulkan/DisposablePipeline.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposablePipeline.cs rename to src/Ryujinx.Graphics.Vulkan/DisposablePipeline.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs b/src/Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs diff --git a/Ryujinx.Graphics.Vulkan/DisposableSampler.cs b/src/Ryujinx.Graphics.Vulkan/DisposableSampler.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/DisposableSampler.cs rename to src/Ryujinx.Graphics.Vulkan/DisposableSampler.cs diff --git a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs rename to src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs diff --git a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs rename to src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs diff --git a/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs rename to src/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs diff --git a/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs rename to src/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl diff --git a/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv b/src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv rename to src/Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv diff --git a/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs rename to src/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs diff --git a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs rename to src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs diff --git a/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin b/src/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin rename to src/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin diff --git a/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin b/src/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin similarity index 100% rename from Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin rename to src/Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/EnumConversion.cs rename to src/Ryujinx.Graphics.Vulkan/EnumConversion.cs diff --git a/Ryujinx.Graphics.Vulkan/FenceHelper.cs b/src/Ryujinx.Graphics.Vulkan/FenceHelper.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/FenceHelper.cs rename to src/Ryujinx.Graphics.Vulkan/FenceHelper.cs diff --git a/Ryujinx.Graphics.Vulkan/FenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/FenceHolder.cs rename to src/Ryujinx.Graphics.Vulkan/FenceHolder.cs diff --git a/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/FormatCapabilities.cs rename to src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs diff --git a/Ryujinx.Graphics.Vulkan/FormatConverter.cs b/src/Ryujinx.Graphics.Vulkan/FormatConverter.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/FormatConverter.cs rename to src/Ryujinx.Graphics.Vulkan/FormatConverter.cs diff --git a/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/FormatTable.cs rename to src/Ryujinx.Graphics.Vulkan/FormatTable.cs diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/FramebufferParams.cs rename to src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs rename to src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs diff --git a/Ryujinx.Graphics.Vulkan/HashTableSlim.cs b/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/HashTableSlim.cs rename to src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/HelperShader.cs rename to src/Ryujinx.Graphics.Vulkan/HelperShader.cs diff --git a/Ryujinx.Graphics.Vulkan/IdList.cs b/src/Ryujinx.Graphics.Vulkan/IdList.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/IdList.cs rename to src/Ryujinx.Graphics.Vulkan/IdList.cs diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs b/src/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs rename to src/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/IndexBufferState.cs rename to src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/MemoryAllocation.cs rename to src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/MemoryAllocator.cs rename to src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs rename to src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs diff --git a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs rename to src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs diff --git a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs rename to src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs diff --git a/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs rename to src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs diff --git a/Ryujinx.Graphics.Vulkan/NativeArray.cs b/src/Ryujinx.Graphics.Vulkan/NativeArray.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/NativeArray.cs rename to src/Ryujinx.Graphics.Vulkan/NativeArray.cs diff --git a/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs rename to src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineBase.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineBase.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineConverter.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineFull.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineFull.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs b/src/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineState.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineState.cs diff --git a/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/PipelineUid.cs rename to src/Ryujinx.Graphics.Vulkan/PipelineUid.cs diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs rename to src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs rename to src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs rename to src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs diff --git a/Ryujinx.Graphics.Vulkan/Queries/Counters.cs b/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Queries/Counters.cs rename to src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs diff --git a/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj similarity index 100% rename from Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj rename to src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj diff --git a/Ryujinx.Graphics.Vulkan/SamplerHolder.cs b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/SamplerHolder.cs rename to src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs diff --git a/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs b/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs rename to src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs diff --git a/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shader.cs rename to src/Ryujinx.Graphics.Vulkan/Shader.cs diff --git a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/ShaderCollection.cs rename to src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ChangeBufferStrideShaderSource.comp b/src/Ryujinx.Graphics.Vulkan/Shaders/ChangeBufferStrideShaderSource.comp similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ChangeBufferStrideShaderSource.comp rename to src/Ryujinx.Graphics.Vulkan/Shaders/ChangeBufferStrideShaderSource.comp diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitClearAlphaFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitClearAlphaFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorBlitClearAlphaFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitClearAlphaFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorBlitFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitVertexShaderSource.vert b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitVertexShaderSource.vert similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorBlitVertexShaderSource.vert rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorBlitVertexShaderSource.vert diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearVertexShaderSource.vert b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearVertexShaderSource.vert similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorClearVertexShaderSource.vert rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorClearVertexShaderSource.vert diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyShorteningComputeShaderSource.comp diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyToNonMsComputeShaderSource.comp b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyToNonMsComputeShaderSource.comp similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorCopyToNonMsComputeShaderSource.comp rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyToNonMsComputeShaderSource.comp diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorCopyWideningComputeShaderSource.comp diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsVertexShaderSource.vert b/src/Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsVertexShaderSource.vert similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsVertexShaderSource.vert rename to src/Ryujinx.Graphics.Vulkan/Shaders/ColorDrawToMsVertexShaderSource.vert diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp b/src/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp rename to src/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp b/src/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp rename to src/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/DepthDrawToNonMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs rename to src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag similarity index 100% rename from Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag rename to src/Ryujinx.Graphics.Vulkan/Shaders/StencilDrawToNonMsFragmentShaderSource.frag diff --git a/Ryujinx.Graphics.Vulkan/SpecInfo.cs b/src/Ryujinx.Graphics.Vulkan/SpecInfo.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/SpecInfo.cs rename to src/Ryujinx.Graphics.Vulkan/SpecInfo.cs diff --git a/Ryujinx.Graphics.Vulkan/StagingBuffer.cs b/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/StagingBuffer.cs rename to src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs diff --git a/Ryujinx.Graphics.Vulkan/SyncManager.cs b/src/Ryujinx.Graphics.Vulkan/SyncManager.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/SyncManager.cs rename to src/Ryujinx.Graphics.Vulkan/SyncManager.cs diff --git a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/TextureBuffer.cs rename to src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs diff --git a/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/TextureCopy.cs rename to src/Ryujinx.Graphics.Vulkan/TextureCopy.cs diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/TextureStorage.cs rename to src/Ryujinx.Graphics.Vulkan/TextureStorage.cs diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/TextureView.cs rename to src/Ryujinx.Graphics.Vulkan/TextureView.cs diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Vendor.cs rename to src/Ryujinx.Graphics.Vulkan/Vendor.cs diff --git a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VertexBufferState.cs rename to src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs b/src/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs rename to src/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs b/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs rename to src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanException.cs b/src/Ryujinx.Graphics.Vulkan/VulkanException.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VulkanException.cs rename to src/Ryujinx.Graphics.Vulkan/VulkanException.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VulkanInitialization.cs rename to src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanInstance.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VulkanInstance.cs rename to src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs b/src/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs rename to src/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/VulkanRenderer.cs rename to src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/Window.cs rename to src/Ryujinx.Graphics.Vulkan/Window.cs diff --git a/Ryujinx.Graphics.Vulkan/WindowBase.cs b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs similarity index 100% rename from Ryujinx.Graphics.Vulkan/WindowBase.cs rename to src/Ryujinx.Graphics.Vulkan/WindowBase.cs diff --git a/Ryujinx.HLE/AssemblyInfo.cs b/src/Ryujinx.HLE/AssemblyInfo.cs similarity index 100% rename from Ryujinx.HLE/AssemblyInfo.cs rename to src/Ryujinx.HLE/AssemblyInfo.cs diff --git a/Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs b/src/Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs rename to src/Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs diff --git a/Ryujinx.HLE/Exceptions/InternalServiceException.cs b/src/Ryujinx.HLE/Exceptions/InternalServiceException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/InternalServiceException.cs rename to src/Ryujinx.HLE/Exceptions/InternalServiceException.cs diff --git a/Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs b/src/Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs rename to src/Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs diff --git a/Ryujinx.HLE/Exceptions/InvalidNpdmException.cs b/src/Ryujinx.HLE/Exceptions/InvalidNpdmException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/InvalidNpdmException.cs rename to src/Ryujinx.HLE/Exceptions/InvalidNpdmException.cs diff --git a/Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs b/src/Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs rename to src/Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs diff --git a/Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs b/src/Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs rename to src/Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs diff --git a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs b/src/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs rename to src/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs diff --git a/Ryujinx.HLE/Exceptions/TamperCompilationException.cs b/src/Ryujinx.HLE/Exceptions/TamperCompilationException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/TamperCompilationException.cs rename to src/Ryujinx.HLE/Exceptions/TamperCompilationException.cs diff --git a/Ryujinx.HLE/Exceptions/TamperExecutionException.cs b/src/Ryujinx.HLE/Exceptions/TamperExecutionException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/TamperExecutionException.cs rename to src/Ryujinx.HLE/Exceptions/TamperExecutionException.cs diff --git a/Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs b/src/Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs similarity index 100% rename from Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs rename to src/Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs diff --git a/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs similarity index 100% rename from Ryujinx.HLE/FileSystem/ContentManager.cs rename to src/Ryujinx.HLE/FileSystem/ContentManager.cs diff --git a/Ryujinx.HLE/FileSystem/ContentPath.cs b/src/Ryujinx.HLE/FileSystem/ContentPath.cs similarity index 100% rename from Ryujinx.HLE/FileSystem/ContentPath.cs rename to src/Ryujinx.HLE/FileSystem/ContentPath.cs diff --git a/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs b/src/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs similarity index 100% rename from Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs rename to src/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs diff --git a/Ryujinx.HLE/FileSystem/LocationEntry.cs b/src/Ryujinx.HLE/FileSystem/LocationEntry.cs similarity index 100% rename from Ryujinx.HLE/FileSystem/LocationEntry.cs rename to src/Ryujinx.HLE/FileSystem/LocationEntry.cs diff --git a/Ryujinx.HLE/FileSystem/SystemVersion.cs b/src/Ryujinx.HLE/FileSystem/SystemVersion.cs similarity index 100% rename from Ryujinx.HLE/FileSystem/SystemVersion.cs rename to src/Ryujinx.HLE/FileSystem/SystemVersion.cs diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs similarity index 100% rename from Ryujinx.HLE/FileSystem/VirtualFileSystem.cs rename to src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs diff --git a/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs similarity index 100% rename from Ryujinx.HLE/HLEConfiguration.cs rename to src/Ryujinx.HLE/HLEConfiguration.cs diff --git a/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/AppletManager.cs rename to src/Ryujinx.HLE/HOS/Applets/AppletManager.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs rename to src/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs diff --git a/Ryujinx.HLE/HOS/Applets/CommonArguments.cs b/src/Ryujinx.HLE/HOS/Applets/CommonArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/CommonArguments.cs rename to src/Ryujinx.HLE/HOS/Applets/CommonArguments.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs rename to src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs diff --git a/Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs rename to src/Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs diff --git a/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs rename to src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs diff --git a/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs rename to src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs diff --git a/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs rename to src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs diff --git a/Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs rename to src/Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs diff --git a/Ryujinx.HLE/HOS/Applets/IApplet.cs b/src/Ryujinx.HLE/HOS/Applets/IApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/IApplet.cs rename to src/Ryujinx.HLE/HOS/Applets/IApplet.cs diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs rename to src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs rename to src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardInputMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardInputMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardInputMode.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardInputMode.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.png b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.png similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.png rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.png diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.svg b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.svg similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.svg rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnA.svg diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.png b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.png similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.png rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.png diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.svg b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.svg similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.svg rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_BtnB.svg diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.png b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.png similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.png rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.png diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.svg b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.svg similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.svg rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Icon_KeyF6.svg diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Logo_Ryujinx.png b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Logo_Ryujinx.png similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Logo_Ryujinx.png rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/Resources/Logo_Ryujinx.png diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalcEx.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalcEx.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalcEx.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalcEx.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardInitialize.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardInitialize.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardInitialize.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardInitialize.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs similarity index 100% rename from Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs rename to src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs diff --git a/Ryujinx.HLE/HOS/ArmProcessContext.cs b/src/Ryujinx.HLE/HOS/ArmProcessContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/ArmProcessContext.cs rename to src/Ryujinx.HLE/HOS/ArmProcessContext.cs diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs similarity index 100% rename from Ryujinx.HLE/HOS/ArmProcessContextFactory.cs rename to src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs similarity index 100% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs rename to src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs diff --git a/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs b/src/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs similarity index 100% rename from Ryujinx.HLE/HOS/HomebrewRomFsStream.cs rename to src/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs similarity index 100% rename from Ryujinx.HLE/HOS/Horizon.cs rename to src/Ryujinx.HLE/HOS/Horizon.cs diff --git a/Ryujinx.HLE/HOS/IdDictionary.cs b/src/Ryujinx.HLE/HOS/IdDictionary.cs similarity index 100% rename from Ryujinx.HLE/HOS/IdDictionary.cs rename to src/Ryujinx.HLE/HOS/IdDictionary.cs diff --git a/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs rename to src/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs rename to src/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/IpcMagic.cs rename to src/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/IpcMessage.cs rename to src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs rename to src/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs diff --git a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs rename to src/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs rename to src/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs diff --git a/Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs b/src/Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs similarity index 100% rename from Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs rename to src/Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs rename to src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs diff --git a/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/KernelConstants.cs rename to src/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs diff --git a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/KernelContext.cs rename to src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/KernelStatic.cs rename to src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockManager.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockManager.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs rename to src/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs rename to src/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/PointerSizedAttribute.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/PointerSizedAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/PointerSizedAttribute.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/PointerSizedAttribute.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcAttribute.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcAttribute.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcAttribute.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcImplAttribute.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcImplAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcImplAttribute.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcImplAttribute.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs rename to src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs rename to src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs diff --git a/Ryujinx.HLE/HOS/LibHacHorizonManager.cs b/src/Ryujinx.HLE/HOS/LibHacHorizonManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/LibHacHorizonManager.cs rename to src/Ryujinx.HLE/HOS/LibHacHorizonManager.cs diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs similarity index 100% rename from Ryujinx.HLE/HOS/ModLoader.cs rename to src/Ryujinx.HLE/HOS/ModLoader.cs diff --git a/Ryujinx.HLE/HOS/ResultCode.cs b/src/Ryujinx.HLE/HOS/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/ResultCode.cs rename to src/Ryujinx.HLE/HOS/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/src/Ryujinx.HLE/HOS/ServiceCtx.cs similarity index 100% rename from Ryujinx.HLE/HOS/ServiceCtx.cs rename to src/Ryujinx.HLE/HOS/ServiceCtx.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg b/src/Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs rename to src/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs diff --git a/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Account/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAppletFifo.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAppletFifo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/IAppletFifo.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAppletFifo.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs rename to src/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs b/src/Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs rename to src/Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs b/src/Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs rename to src/Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs b/src/Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs rename to src/Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Apm/IManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/IManager.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/IManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs b/src/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/ISession.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ISession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/ISession.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/ISession.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs b/src/Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs b/src/Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs b/src/Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs b/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs diff --git a/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs b/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs rename to src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs b/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs rename to src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs diff --git a/Ryujinx.HLE/HOS/Services/Arp/IReader.cs b/src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Arp/IReader.cs rename to src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs diff --git a/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs b/src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Arp/IWriter.cs rename to src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs diff --git a/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs b/src/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs rename to src/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs diff --git a/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs rename to src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs diff --git a/Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs b/src/Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs rename to src/Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs diff --git a/Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs b/src/Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs rename to src/Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs diff --git a/Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs b/src/Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs rename to src/Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs b/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs rename to src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs diff --git a/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs b/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs rename to src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs rename to src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs rename to src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs rename to src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs rename to src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs rename to src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs diff --git a/Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs diff --git a/Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs b/src/Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs rename to src/Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs diff --git a/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs b/src/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/CommandTIpcAttribute.cs b/src/Ryujinx.HLE/HOS/Services/CommandTIpcAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/CommandTIpcAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/CommandTIpcAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/DisposableIpcService.cs b/src/Ryujinx.HLE/HOS/Services/DisposableIpcService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/DisposableIpcService.cs rename to src/Ryujinx.HLE/HOS/Services/DisposableIpcService.cs diff --git a/Ryujinx.HLE/HOS/Services/DummyService.cs b/src/Ryujinx.HLE/HOS/Services/DummyService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/DummyService.cs rename to src/Ryujinx.HLE/HOS/Services/DummyService.cs diff --git a/Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs b/src/Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs rename to src/Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs diff --git a/Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs rename to src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs diff --git a/Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs b/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs rename to src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs diff --git a/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs b/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Erpt/IContext.cs rename to src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs b/src/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Erpt/ISession.cs rename to src/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs diff --git a/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs b/src/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Es/IETicketService.cs rename to src/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs diff --git a/Ryujinx.HLE/HOS/Services/Eupld/IControl.cs b/src/Ryujinx.HLE/HOS/Services/Eupld/IControl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Eupld/IControl.cs rename to src/Ryujinx.HLE/HOS/Services/Eupld/IControl.cs diff --git a/Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs rename to src/Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs diff --git a/Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs rename to src/Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs diff --git a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fatal/IService.cs rename to src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs rename to src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs rename to src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs rename to src/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs rename to src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/IDeviceOperator.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/IMultiCommitManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs b/src/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs b/src/Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs rename to src/Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs diff --git a/Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs b/src/Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs rename to src/Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs diff --git a/Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs b/src/Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs rename to src/Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Hid.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs rename to src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs diff --git a/Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs b/src/Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs b/src/Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/IpcService.cs rename to src/Ryujinx.HLE/HOS/Services/IpcService.cs diff --git a/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs b/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs rename to src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs diff --git a/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs b/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs rename to src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs rename to src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs diff --git a/Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs b/src/Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs b/src/Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Mig/IService.cs b/src/Ryujinx.HLE/HOS/Services/Mig/IService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mig/IService.cs rename to src/Ryujinx.HLE/HOS/Services/Mig/IService.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs b/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs b/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Helper.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Helper.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs b/src/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs b/src/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs b/src/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs b/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs b/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/IElement.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/IElement.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/IElement.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/IElement.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/IStoredData.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/IStoredData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/IStoredData.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/IStoredData.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/Types/Ver3StoreData.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Ver3StoreData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/Types/Ver3StoreData.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/Types/Ver3StoreData.cs diff --git a/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs b/src/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs rename to src/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mm/IRequest.cs rename to src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs diff --git a/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs rename to src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs diff --git a/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs rename to src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs diff --git a/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs rename to src/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs diff --git a/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs rename to src/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs rename to src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs diff --git a/Ryujinx.HLE/HOS/Services/Ngct/IService.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/IService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ngct/IService.cs rename to src/Ryujinx.HLE/HOS/Services/Ngct/IService.cs diff --git a/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs rename to src/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs diff --git a/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs rename to src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs rename to src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs b/src/Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs b/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs b/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs diff --git a/Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs rename to src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs diff --git a/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs rename to src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs diff --git a/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs rename to src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs diff --git a/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs rename to src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostProfGpu/NvHostProfGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostProfGpu/NvHostProfGpuDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostProfGpu/NvHostProfGpuDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostProfGpu/NvHostProfGpuDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs rename to src/Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs diff --git a/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs rename to src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs diff --git a/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs rename to src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs diff --git a/Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs b/src/Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs rename to src/Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs diff --git a/Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs b/src/Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs rename to src/Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcie/IManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs rename to src/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs diff --git a/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs rename to src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs diff --git a/Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IRtcManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs rename to src/Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs b/src/Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs b/src/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs b/src/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs b/src/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs b/src/Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs rename to src/Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs diff --git a/Ryujinx.HLE/HOS/Services/Psc/IPmService.cs b/src/Ryujinx.HLE/HOS/Services/Psc/IPmService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Psc/IPmService.cs rename to src/Ryujinx.HLE/HOS/Services/Psc/IPmService.cs diff --git a/Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs b/src/Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs rename to src/Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs rename to src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ro/Types/NRRCertification.cs b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NRRCertification.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ro/Types/NRRCertification.cs rename to src/Ryujinx.HLE/HOS/Services/Ro/Types/NRRCertification.cs diff --git a/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs rename to src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs diff --git a/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs rename to src/Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/ServerBase.cs rename to src/Ryujinx.HLE/HOS/Services/ServerBase.cs diff --git a/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs b/src/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/ServiceAttributes.cs rename to src/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs b/src/Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs rename to src/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs diff --git a/Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs b/src/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs rename to src/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMMsgHdr.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMMsgHdr.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMMsgHdr.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMMsgHdr.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMsgHdr.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMsgHdr.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMsgHdr.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMsgHdr.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketType.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketType.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketType.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/TimeVal.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/TimeVal.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/TimeVal.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/TimeVal.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs rename to src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs b/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs diff --git a/Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs rename to src/Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs diff --git a/Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs b/src/Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs rename to src/Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/BuiltInCertificateInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/BuiltInCertificateInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/BuiltInCertificateInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/BuiltInCertificateInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs rename to src/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IConsumerListener.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IConsumerListener.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/IConsumerListener.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IConsumerListener.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IFlattenable.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IFlattenable.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/IFlattenable.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IFlattenable.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IProducerListener.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IProducerListener.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/IProducerListener.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IProducerListener.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ParcelHeader.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ParcelHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/ParcelHeader.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ParcelHeader.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/PixelFormat.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/PixelFormat.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/PixelFormat.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/PixelFormat.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs rename to src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs b/src/Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs rename to src/Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs b/src/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs rename to src/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs b/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs rename to src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs b/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs rename to src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs rename to src/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs rename to src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs rename to src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs rename to src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs rename to src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeManager.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs rename to src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs b/src/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs b/src/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs rename to src/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs diff --git a/Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs rename to src/Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs diff --git a/Ryujinx.HLE/HOS/Services/Usb/IDsService.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IDsService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Usb/IDsService.cs rename to src/Ryujinx.HLE/HOS/Services/Usb/IDsService.cs diff --git a/Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs rename to src/Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs rename to src/Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Usb/IPmService.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IPmService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Usb/IPmService.cs rename to src/Ryujinx.HLE/HOS/Services/Usb/IPmService.cs diff --git a/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs rename to src/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs diff --git a/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs rename to src/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs diff --git a/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs b/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs rename to src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs diff --git a/Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs rename to src/Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs rename to src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs diff --git a/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs rename to src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs diff --git a/Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs rename to src/Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs rename to src/Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs diff --git a/Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs rename to src/Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs diff --git a/Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs rename to src/Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs diff --git a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs similarity index 100% rename from Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs rename to src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs diff --git a/Ryujinx.HLE/HOS/SystemState/ColorSet.cs b/src/Ryujinx.HLE/HOS/SystemState/ColorSet.cs similarity index 100% rename from Ryujinx.HLE/HOS/SystemState/ColorSet.cs rename to src/Ryujinx.HLE/HOS/SystemState/ColorSet.cs diff --git a/Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs b/src/Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs similarity index 100% rename from Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs rename to src/Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs diff --git a/Ryujinx.HLE/HOS/SystemState/RegionCode.cs b/src/Ryujinx.HLE/HOS/SystemState/RegionCode.cs similarity index 100% rename from Ryujinx.HLE/HOS/SystemState/RegionCode.cs rename to src/Ryujinx.HLE/HOS/SystemState/RegionCode.cs diff --git a/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs similarity index 100% rename from Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs rename to src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs similarity index 100% rename from Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs rename to src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs diff --git a/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs similarity index 100% rename from Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs rename to src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs diff --git a/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs b/src/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs rename to src/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs diff --git a/Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs b/src/Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs rename to src/Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/BeginConditionalBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/BeginConditionalBlock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/BeginConditionalBlock.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/BeginConditionalBlock.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/DebugLog.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/DebugLog.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/DebugLog.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/DebugLog.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/KeyPressConditional.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/KeyPressConditional.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/KeyPressConditional.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/KeyPressConditional.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithMemory.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithMemory.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithMemory.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/PauseProcess.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/PauseProcess.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/PauseProcess.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/PauseProcess.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ReadOrWriteStaticRegister.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ReadOrWriteStaticRegister.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/ReadOrWriteStaticRegister.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ReadOrWriteStaticRegister.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/RegisterConditional.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/RegisterConditional.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/RegisterConditional.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/RegisterConditional.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegister.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegister.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegister.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegister.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegisterWithMask.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegisterWithMask.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegisterWithMask.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/SaveOrRestoreRegisterWithMask.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreRegisterToMemory.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreRegisterToMemory.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreRegisterToMemory.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreRegisterToMemory.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CodeType.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeType.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CodeType.cs rename to src/Ryujinx.HLE/HOS/Tamper/CodeType.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Comparison.cs b/src/Ryujinx.HLE/HOS/Tamper/Comparison.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Comparison.cs rename to src/Ryujinx.HLE/HOS/Tamper/Comparison.cs diff --git a/Ryujinx.HLE/HOS/Tamper/CompilationContext.cs b/src/Ryujinx.HLE/HOS/Tamper/CompilationContext.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/CompilationContext.cs rename to src/Ryujinx.HLE/HOS/Tamper/CompilationContext.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/ICondition.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/ICondition.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/ICondition.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/ICondition.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs rename to src/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs diff --git a/Ryujinx.HLE/HOS/Tamper/ITamperProgram.cs b/src/Ryujinx.HLE/HOS/Tamper/ITamperProgram.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/ITamperProgram.cs rename to src/Ryujinx.HLE/HOS/Tamper/ITamperProgram.cs diff --git a/Ryujinx.HLE/HOS/Tamper/ITamperedProcess.cs b/src/Ryujinx.HLE/HOS/Tamper/ITamperedProcess.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/ITamperedProcess.cs rename to src/Ryujinx.HLE/HOS/Tamper/ITamperedProcess.cs diff --git a/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs b/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs rename to src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs diff --git a/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs b/src/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs rename to src/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs diff --git a/Ryujinx.HLE/HOS/Tamper/MemoryRegion.cs b/src/Ryujinx.HLE/HOS/Tamper/MemoryRegion.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/MemoryRegion.cs rename to src/Ryujinx.HLE/HOS/Tamper/MemoryRegion.cs diff --git a/Ryujinx.HLE/HOS/Tamper/OperationBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/OperationBlock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/OperationBlock.cs rename to src/Ryujinx.HLE/HOS/Tamper/OperationBlock.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/Block.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/Block.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/Block.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/Block.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/IOperand.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/IOperand.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/IOperand.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/IOperand.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/IOperation.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/IOperation.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/IOperation.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/IOperation.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs rename to src/Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Parameter.cs b/src/Ryujinx.HLE/HOS/Tamper/Parameter.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Parameter.cs rename to src/Ryujinx.HLE/HOS/Tamper/Parameter.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Pointer.cs b/src/Ryujinx.HLE/HOS/Tamper/Pointer.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Pointer.cs rename to src/Ryujinx.HLE/HOS/Tamper/Pointer.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Register.cs b/src/Ryujinx.HLE/HOS/Tamper/Register.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Register.cs rename to src/Ryujinx.HLE/HOS/Tamper/Register.cs diff --git a/Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs b/src/Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs rename to src/Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs diff --git a/Ryujinx.HLE/HOS/Tamper/Value.cs b/src/Ryujinx.HLE/HOS/Tamper/Value.cs similarity index 100% rename from Ryujinx.HLE/HOS/Tamper/Value.cs rename to src/Ryujinx.HLE/HOS/Tamper/Value.cs diff --git a/Ryujinx.HLE/HOS/TamperMachine.cs b/src/Ryujinx.HLE/HOS/TamperMachine.cs similarity index 100% rename from Ryujinx.HLE/HOS/TamperMachine.cs rename to src/Ryujinx.HLE/HOS/TamperMachine.cs diff --git a/Ryujinx.HLE/HOS/UserChannelPersistence.cs b/src/Ryujinx.HLE/HOS/UserChannelPersistence.cs similarity index 100% rename from Ryujinx.HLE/HOS/UserChannelPersistence.cs rename to src/Ryujinx.HLE/HOS/UserChannelPersistence.cs diff --git a/Ryujinx.HLE/Homebrew.npdm b/src/Ryujinx.HLE/Homebrew.npdm similarity index 100% rename from Ryujinx.HLE/Homebrew.npdm rename to src/Ryujinx.HLE/Homebrew.npdm diff --git a/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs diff --git a/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs rename to src/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/IExecutable.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Executables/IExecutable.cs rename to src/Ryujinx.HLE/Loaders/Executables/IExecutable.cs diff --git a/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Executables/KipExecutable.cs rename to src/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs diff --git a/Ryujinx.HLE/Loaders/Executables/NroExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/NroExecutable.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Executables/NroExecutable.cs rename to src/Ryujinx.HLE/Loaders/Executables/NroExecutable.cs diff --git a/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs rename to src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs diff --git a/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs b/src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs rename to src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs diff --git a/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs b/src/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs rename to src/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs diff --git a/Ryujinx.HLE/Loaders/Mods/MemPatch.cs b/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Mods/MemPatch.cs rename to src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs diff --git a/Ryujinx.HLE/Loaders/Npdm/ACI0.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Npdm/ACI0.cs rename to src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs diff --git a/Ryujinx.HLE/Loaders/Npdm/ACID.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Npdm/ACID.cs rename to src/Ryujinx.HLE/Loaders/Npdm/ACID.cs diff --git a/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs rename to src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs diff --git a/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs rename to src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs rename to src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Npdm/Npdm.cs rename to src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs diff --git a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs rename to src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs rename to src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs rename to src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs rename to src/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs rename to src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs rename to src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/ProcessConst.cs rename to src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs rename to src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs rename to src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs similarity index 100% rename from Ryujinx.HLE/Loaders/Processes/ProcessResult.cs rename to src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs diff --git a/Ryujinx.HLE/MemoryConfiguration.cs b/src/Ryujinx.HLE/MemoryConfiguration.cs similarity index 100% rename from Ryujinx.HLE/MemoryConfiguration.cs rename to src/Ryujinx.HLE/MemoryConfiguration.cs diff --git a/Ryujinx.HLE/PerformanceStatistics.cs b/src/Ryujinx.HLE/PerformanceStatistics.cs similarity index 100% rename from Ryujinx.HLE/PerformanceStatistics.cs rename to src/Ryujinx.HLE/PerformanceStatistics.cs diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj similarity index 100% rename from Ryujinx.HLE/Ryujinx.HLE.csproj rename to src/Ryujinx.HLE/Ryujinx.HLE.csproj diff --git a/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs similarity index 100% rename from Ryujinx.HLE/Switch.cs rename to src/Ryujinx.HLE/Switch.cs diff --git a/Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs b/src/Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs similarity index 100% rename from Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs rename to src/Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs diff --git a/Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs b/src/Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs similarity index 100% rename from Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs rename to src/Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs diff --git a/Ryujinx.HLE/Ui/IHostUiHandler.cs b/src/Ryujinx.HLE/Ui/IHostUiHandler.cs similarity index 100% rename from Ryujinx.HLE/Ui/IHostUiHandler.cs rename to src/Ryujinx.HLE/Ui/IHostUiHandler.cs diff --git a/Ryujinx.HLE/Ui/IHostUiTheme.cs b/src/Ryujinx.HLE/Ui/IHostUiTheme.cs similarity index 100% rename from Ryujinx.HLE/Ui/IHostUiTheme.cs rename to src/Ryujinx.HLE/Ui/IHostUiTheme.cs diff --git a/Ryujinx.HLE/Ui/Input/NpadButtonHandler.cs b/src/Ryujinx.HLE/Ui/Input/NpadButtonHandler.cs similarity index 100% rename from Ryujinx.HLE/Ui/Input/NpadButtonHandler.cs rename to src/Ryujinx.HLE/Ui/Input/NpadButtonHandler.cs diff --git a/Ryujinx.HLE/Ui/Input/NpadReader.cs b/src/Ryujinx.HLE/Ui/Input/NpadReader.cs similarity index 100% rename from Ryujinx.HLE/Ui/Input/NpadReader.cs rename to src/Ryujinx.HLE/Ui/Input/NpadReader.cs diff --git a/Ryujinx.HLE/Ui/KeyPressedHandler.cs b/src/Ryujinx.HLE/Ui/KeyPressedHandler.cs similarity index 100% rename from Ryujinx.HLE/Ui/KeyPressedHandler.cs rename to src/Ryujinx.HLE/Ui/KeyPressedHandler.cs diff --git a/Ryujinx.HLE/Ui/KeyReleasedHandler.cs b/src/Ryujinx.HLE/Ui/KeyReleasedHandler.cs similarity index 100% rename from Ryujinx.HLE/Ui/KeyReleasedHandler.cs rename to src/Ryujinx.HLE/Ui/KeyReleasedHandler.cs diff --git a/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs b/src/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs similarity index 100% rename from Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs rename to src/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs diff --git a/Ryujinx.HLE/Ui/ThemeColor.cs b/src/Ryujinx.HLE/Ui/ThemeColor.cs similarity index 100% rename from Ryujinx.HLE/Ui/ThemeColor.cs rename to src/Ryujinx.HLE/Ui/ThemeColor.cs diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/src/Ryujinx.HLE/Utilities/StringUtils.cs similarity index 100% rename from Ryujinx.HLE/Utilities/StringUtils.cs rename to src/Ryujinx.HLE/Utilities/StringUtils.cs diff --git a/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs b/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs similarity index 100% rename from Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs rename to src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs diff --git a/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs b/src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs similarity index 100% rename from Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs rename to src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs diff --git a/Ryujinx.Headless.SDL2/HideCursor.cs b/src/Ryujinx.Headless.SDL2/HideCursor.cs similarity index 100% rename from Ryujinx.Headless.SDL2/HideCursor.cs rename to src/Ryujinx.Headless.SDL2/HideCursor.cs diff --git a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs similarity index 100% rename from Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs rename to src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs diff --git a/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs similarity index 100% rename from Ryujinx.Headless.SDL2/Options.cs rename to src/Ryujinx.Headless.SDL2/Options.cs diff --git a/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs similarity index 100% rename from Ryujinx.Headless.SDL2/Program.cs rename to src/Ryujinx.Headless.SDL2/Program.cs diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj similarity index 93% rename from Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj rename to src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index ebb9c94c3..fc912d329 100644 --- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -32,18 +32,18 @@ </ItemGroup> <ItemGroup> - <Content Include="..\distribution\legal\THIRDPARTY.md"> + <Content Include="..\..\distribution\legal\THIRDPARTY.md"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>THIRDPARTY.md</TargetPath> </Content> - <Content Include="..\LICENSE.txt"> + <Content Include="..\..\LICENSE.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>LICENSE.txt</TargetPath> </Content> </ItemGroup> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'"> - <Content Include="..\distribution\linux\Ryujinx.sh"> + <Content Include="..\..\distribution\linux\Ryujinx.sh"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup> diff --git a/Ryujinx.Headless.SDL2/Ryujinx.bmp b/src/Ryujinx.Headless.SDL2/Ryujinx.bmp similarity index 100% rename from Ryujinx.Headless.SDL2/Ryujinx.bmp rename to src/Ryujinx.Headless.SDL2/Ryujinx.bmp diff --git a/Ryujinx.Headless.SDL2/SDL2Mouse.cs b/src/Ryujinx.Headless.SDL2/SDL2Mouse.cs similarity index 100% rename from Ryujinx.Headless.SDL2/SDL2Mouse.cs rename to src/Ryujinx.Headless.SDL2/SDL2Mouse.cs diff --git a/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs b/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs similarity index 100% rename from Ryujinx.Headless.SDL2/SDL2MouseDriver.cs rename to src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs diff --git a/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs b/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs similarity index 100% rename from Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs rename to src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs diff --git a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs similarity index 100% rename from Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs rename to src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs similarity index 100% rename from Ryujinx.Headless.SDL2/WindowBase.cs rename to src/Ryujinx.Headless.SDL2/WindowBase.cs diff --git a/Ryujinx.Horizon.Common/ISyscallApi.cs b/src/Ryujinx.Horizon.Common/ISyscallApi.cs similarity index 100% rename from Ryujinx.Horizon.Common/ISyscallApi.cs rename to src/Ryujinx.Horizon.Common/ISyscallApi.cs diff --git a/Ryujinx.Horizon.Common/IThreadContext.cs b/src/Ryujinx.Horizon.Common/IThreadContext.cs similarity index 100% rename from Ryujinx.Horizon.Common/IThreadContext.cs rename to src/Ryujinx.Horizon.Common/IThreadContext.cs diff --git a/Ryujinx.Horizon.Common/InvalidResultException.cs b/src/Ryujinx.Horizon.Common/InvalidResultException.cs similarity index 100% rename from Ryujinx.Horizon.Common/InvalidResultException.cs rename to src/Ryujinx.Horizon.Common/InvalidResultException.cs diff --git a/Ryujinx.Horizon.Common/KernelResult.cs b/src/Ryujinx.Horizon.Common/KernelResult.cs similarity index 100% rename from Ryujinx.Horizon.Common/KernelResult.cs rename to src/Ryujinx.Horizon.Common/KernelResult.cs diff --git a/Ryujinx.Horizon.Common/OnScopeExit.cs b/src/Ryujinx.Horizon.Common/OnScopeExit.cs similarity index 100% rename from Ryujinx.Horizon.Common/OnScopeExit.cs rename to src/Ryujinx.Horizon.Common/OnScopeExit.cs diff --git a/Ryujinx.Horizon.Common/Result.cs b/src/Ryujinx.Horizon.Common/Result.cs similarity index 100% rename from Ryujinx.Horizon.Common/Result.cs rename to src/Ryujinx.Horizon.Common/Result.cs diff --git a/Ryujinx.Horizon.Common/ResultNames.cs b/src/Ryujinx.Horizon.Common/ResultNames.cs similarity index 100% rename from Ryujinx.Horizon.Common/ResultNames.cs rename to src/Ryujinx.Horizon.Common/ResultNames.cs diff --git a/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj b/src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj similarity index 100% rename from Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj rename to src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj diff --git a/Ryujinx.Horizon.Common/ThreadTerminatedException.cs b/src/Ryujinx.Horizon.Common/ThreadTerminatedException.cs similarity index 100% rename from Ryujinx.Horizon.Common/ThreadTerminatedException.cs rename to src/Ryujinx.Horizon.Common/ThreadTerminatedException.cs diff --git a/Ryujinx.Horizon.Generators/CodeGenerator.cs b/src/Ryujinx.Horizon.Generators/CodeGenerator.cs similarity index 100% rename from Ryujinx.Horizon.Generators/CodeGenerator.cs rename to src/Ryujinx.Horizon.Generators/CodeGenerator.cs diff --git a/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs b/src/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs similarity index 100% rename from Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs rename to src/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs diff --git a/Ryujinx.Horizon.Generators/Hipc/CommandInterface.cs b/src/Ryujinx.Horizon.Generators/Hipc/CommandInterface.cs similarity index 100% rename from Ryujinx.Horizon.Generators/Hipc/CommandInterface.cs rename to src/Ryujinx.Horizon.Generators/Hipc/CommandInterface.cs diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs similarity index 100% rename from Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs rename to src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcSyntaxReceiver.cs b/src/Ryujinx.Horizon.Generators/Hipc/HipcSyntaxReceiver.cs similarity index 100% rename from Ryujinx.Horizon.Generators/Hipc/HipcSyntaxReceiver.cs rename to src/Ryujinx.Horizon.Generators/Hipc/HipcSyntaxReceiver.cs diff --git a/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj b/src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj similarity index 100% rename from Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj rename to src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj diff --git a/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs b/src/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs similarity index 100% rename from Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs rename to src/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs diff --git a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs b/src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs similarity index 100% rename from Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs rename to src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs diff --git a/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs b/src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs similarity index 100% rename from Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs rename to src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs diff --git a/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj b/src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj similarity index 100% rename from Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj rename to src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj diff --git a/Ryujinx.Horizon/HeapAllocator.cs b/src/Ryujinx.Horizon/HeapAllocator.cs similarity index 100% rename from Ryujinx.Horizon/HeapAllocator.cs rename to src/Ryujinx.Horizon/HeapAllocator.cs diff --git a/Ryujinx.Horizon/HorizonOptions.cs b/src/Ryujinx.Horizon/HorizonOptions.cs similarity index 100% rename from Ryujinx.Horizon/HorizonOptions.cs rename to src/Ryujinx.Horizon/HorizonOptions.cs diff --git a/Ryujinx.Horizon/HorizonStatic.cs b/src/Ryujinx.Horizon/HorizonStatic.cs similarity index 100% rename from Ryujinx.Horizon/HorizonStatic.cs rename to src/Ryujinx.Horizon/HorizonStatic.cs diff --git a/Ryujinx.Horizon/IService.cs b/src/Ryujinx.Horizon/IService.cs similarity index 100% rename from Ryujinx.Horizon/IService.cs rename to src/Ryujinx.Horizon/IService.cs diff --git a/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs similarity index 100% rename from Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs rename to src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs diff --git a/Ryujinx.Horizon/LogManager/Ipc/LogService.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs similarity index 100% rename from Ryujinx.Horizon/LogManager/Ipc/LogService.cs rename to src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs diff --git a/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs similarity index 100% rename from Ryujinx.Horizon/LogManager/LmIpcServer.cs rename to src/Ryujinx.Horizon/LogManager/LmIpcServer.cs diff --git a/Ryujinx.Horizon/LogManager/LmMain.cs b/src/Ryujinx.Horizon/LogManager/LmMain.cs similarity index 100% rename from Ryujinx.Horizon/LogManager/LmMain.cs rename to src/Ryujinx.Horizon/LogManager/LmMain.cs diff --git a/Ryujinx.Horizon/LogManager/Types/LogPacket.cs b/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs similarity index 100% rename from Ryujinx.Horizon/LogManager/Types/LogPacket.cs rename to src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs diff --git a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs b/src/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs similarity index 100% rename from Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs rename to src/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs diff --git a/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs similarity index 100% rename from Ryujinx.Horizon/Prepo/PrepoIpcServer.cs rename to src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs diff --git a/Ryujinx.Horizon/Prepo/PrepoMain.cs b/src/Ryujinx.Horizon/Prepo/PrepoMain.cs similarity index 100% rename from Ryujinx.Horizon/Prepo/PrepoMain.cs rename to src/Ryujinx.Horizon/Prepo/PrepoMain.cs diff --git a/Ryujinx.Horizon/Prepo/PrepoResult.cs b/src/Ryujinx.Horizon/Prepo/PrepoResult.cs similarity index 100% rename from Ryujinx.Horizon/Prepo/PrepoResult.cs rename to src/Ryujinx.Horizon/Prepo/PrepoResult.cs diff --git a/Ryujinx.Horizon/Prepo/PrepoServerManager.cs b/src/Ryujinx.Horizon/Prepo/PrepoServerManager.cs similarity index 100% rename from Ryujinx.Horizon/Prepo/PrepoServerManager.cs rename to src/Ryujinx.Horizon/Prepo/PrepoServerManager.cs diff --git a/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs b/src/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs similarity index 100% rename from Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs rename to src/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs diff --git a/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs b/src/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs similarity index 100% rename from Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs rename to src/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs diff --git a/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj similarity index 100% rename from Ryujinx.Horizon/Ryujinx.Horizon.csproj rename to src/Ryujinx.Horizon/Ryujinx.Horizon.csproj diff --git a/Ryujinx.Horizon/Sdk/Account/Uid.cs b/src/Ryujinx.Horizon/Sdk/Account/Uid.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Account/Uid.cs rename to src/Ryujinx.Horizon/Sdk/Account/Uid.cs diff --git a/Ryujinx.Horizon/Sdk/DebugUtil.cs b/src/Ryujinx.Horizon/Sdk/DebugUtil.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/DebugUtil.cs rename to src/Ryujinx.Horizon/Sdk/DebugUtil.cs diff --git a/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs b/src/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs rename to src/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs diff --git a/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs b/src/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs rename to src/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs diff --git a/Ryujinx.Horizon/Sdk/Lm/ILogService.cs b/src/Ryujinx.Horizon/Sdk/Lm/ILogService.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Lm/ILogService.cs rename to src/Ryujinx.Horizon/Sdk/Lm/ILogService.cs diff --git a/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs b/src/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs rename to src/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs diff --git a/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs b/src/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Lm/LogDestination.cs rename to src/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs diff --git a/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs b/src/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs rename to src/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs diff --git a/Ryujinx.Horizon/Sdk/Lm/LogPacketHeader.cs b/src/Ryujinx.Horizon/Sdk/Lm/LogPacketHeader.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Lm/LogPacketHeader.cs rename to src/Ryujinx.Horizon/Sdk/Lm/LogPacketHeader.cs diff --git a/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs b/src/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs rename to src/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Event.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Event.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/Event.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/Event.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/EventType.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs diff --git a/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs rename to src/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs diff --git a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs b/src/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs rename to src/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs diff --git a/Ryujinx.Horizon/Sdk/ServiceUtil.cs b/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/ServiceUtil.cs rename to src/Ryujinx.Horizon/Sdk/ServiceUtil.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifInHeader.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObject.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/InlineContext.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainBase.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageProcessor.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceObjectHolder.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/CmifCommandAttribute.cs b/src/Ryujinx.Horizon/Sdk/Sf/CmifCommandAttribute.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/CmifCommandAttribute.cs rename to src/Ryujinx.Horizon/Sdk/Sf/CmifCommandAttribute.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/CommandArg.cs rename to src/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs rename to src/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs rename to src/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs rename to src/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs rename to src/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/src/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs rename to src/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/IServiceObject.cs b/src/Ryujinx.Horizon/Sdk/Sf/IServiceObject.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/IServiceObject.cs rename to src/Ryujinx.Horizon/Sdk/Sf/IServiceObject.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs b/src/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs rename to src/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs diff --git a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs b/src/Ryujinx.Horizon/Sdk/Sf/SfResult.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sf/SfResult.cs rename to src/Ryujinx.Horizon/Sdk/Sf/SfResult.cs diff --git a/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs b/src/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sm/IManagerService.cs rename to src/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs diff --git a/Ryujinx.Horizon/Sdk/Sm/IUserService.cs b/src/Ryujinx.Horizon/Sdk/Sm/IUserService.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sm/IUserService.cs rename to src/Ryujinx.Horizon/Sdk/Sm/IUserService.cs diff --git a/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs b/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sm/ServiceName.cs rename to src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs diff --git a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs b/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs similarity index 100% rename from Ryujinx.Horizon/Sdk/Sm/SmApi.cs rename to src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs diff --git a/Ryujinx.Horizon/ServiceEntry.cs b/src/Ryujinx.Horizon/ServiceEntry.cs similarity index 100% rename from Ryujinx.Horizon/ServiceEntry.cs rename to src/Ryujinx.Horizon/ServiceEntry.cs diff --git a/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs similarity index 100% rename from Ryujinx.Horizon/ServiceTable.cs rename to src/Ryujinx.Horizon/ServiceTable.cs diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs b/src/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs similarity index 100% rename from Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs rename to src/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/src/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs similarity index 100% rename from Ryujinx.Horizon/Sm/Impl/ServiceManager.cs rename to src/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs diff --git a/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs b/src/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs similarity index 100% rename from Ryujinx.Horizon/Sm/Ipc/ManagerService.cs rename to src/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs diff --git a/Ryujinx.Horizon/Sm/Ipc/UserService.cs b/src/Ryujinx.Horizon/Sm/Ipc/UserService.cs similarity index 100% rename from Ryujinx.Horizon/Sm/Ipc/UserService.cs rename to src/Ryujinx.Horizon/Sm/Ipc/UserService.cs diff --git a/Ryujinx.Horizon/Sm/SmMain.cs b/src/Ryujinx.Horizon/Sm/SmMain.cs similarity index 100% rename from Ryujinx.Horizon/Sm/SmMain.cs rename to src/Ryujinx.Horizon/Sm/SmMain.cs diff --git a/Ryujinx.Horizon/Sm/SmResult.cs b/src/Ryujinx.Horizon/Sm/SmResult.cs similarity index 100% rename from Ryujinx.Horizon/Sm/SmResult.cs rename to src/Ryujinx.Horizon/Sm/SmResult.cs diff --git a/Ryujinx.Horizon/Sm/SmServerManager.cs b/src/Ryujinx.Horizon/Sm/SmServerManager.cs similarity index 100% rename from Ryujinx.Horizon/Sm/SmServerManager.cs rename to src/Ryujinx.Horizon/Sm/SmServerManager.cs diff --git a/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs b/src/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs similarity index 100% rename from Ryujinx.Horizon/Sm/Types/SmPortIndex.cs rename to src/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs diff --git a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj b/src/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj similarity index 100% rename from Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj rename to src/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj diff --git a/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs similarity index 100% rename from Ryujinx.Input.SDL2/SDL2Gamepad.cs rename to src/Ryujinx.Input.SDL2/SDL2Gamepad.cs diff --git a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs similarity index 100% rename from Ryujinx.Input.SDL2/SDL2GamepadDriver.cs rename to src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs diff --git a/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs similarity index 100% rename from Ryujinx.Input.SDL2/SDL2Keyboard.cs rename to src/Ryujinx.Input.SDL2/SDL2Keyboard.cs diff --git a/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs b/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs similarity index 100% rename from Ryujinx.Input.SDL2/SDLKeyboardDriver.cs rename to src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs diff --git a/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs b/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs similarity index 100% rename from Ryujinx.Input/Assigner/GamepadButtonAssigner.cs rename to src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs diff --git a/Ryujinx.Input/Assigner/IButtonAssigner.cs b/src/Ryujinx.Input/Assigner/IButtonAssigner.cs similarity index 100% rename from Ryujinx.Input/Assigner/IButtonAssigner.cs rename to src/Ryujinx.Input/Assigner/IButtonAssigner.cs diff --git a/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs similarity index 100% rename from Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs rename to src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs diff --git a/Ryujinx.Input/GamepadButtonInputId.cs b/src/Ryujinx.Input/GamepadButtonInputId.cs similarity index 100% rename from Ryujinx.Input/GamepadButtonInputId.cs rename to src/Ryujinx.Input/GamepadButtonInputId.cs diff --git a/Ryujinx.Input/GamepadFeaturesFlag.cs b/src/Ryujinx.Input/GamepadFeaturesFlag.cs similarity index 100% rename from Ryujinx.Input/GamepadFeaturesFlag.cs rename to src/Ryujinx.Input/GamepadFeaturesFlag.cs diff --git a/Ryujinx.Input/GamepadStateSnapshot.cs b/src/Ryujinx.Input/GamepadStateSnapshot.cs similarity index 100% rename from Ryujinx.Input/GamepadStateSnapshot.cs rename to src/Ryujinx.Input/GamepadStateSnapshot.cs diff --git a/Ryujinx.Input/HLE/InputManager.cs b/src/Ryujinx.Input/HLE/InputManager.cs similarity index 100% rename from Ryujinx.Input/HLE/InputManager.cs rename to src/Ryujinx.Input/HLE/InputManager.cs diff --git a/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs similarity index 100% rename from Ryujinx.Input/HLE/NpadController.cs rename to src/Ryujinx.Input/HLE/NpadController.cs diff --git a/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs similarity index 100% rename from Ryujinx.Input/HLE/NpadManager.cs rename to src/Ryujinx.Input/HLE/NpadManager.cs diff --git a/Ryujinx.Input/HLE/TouchScreenManager.cs b/src/Ryujinx.Input/HLE/TouchScreenManager.cs similarity index 100% rename from Ryujinx.Input/HLE/TouchScreenManager.cs rename to src/Ryujinx.Input/HLE/TouchScreenManager.cs diff --git a/Ryujinx.Input/IGamepad.cs b/src/Ryujinx.Input/IGamepad.cs similarity index 100% rename from Ryujinx.Input/IGamepad.cs rename to src/Ryujinx.Input/IGamepad.cs diff --git a/Ryujinx.Input/IGamepadDriver.cs b/src/Ryujinx.Input/IGamepadDriver.cs similarity index 100% rename from Ryujinx.Input/IGamepadDriver.cs rename to src/Ryujinx.Input/IGamepadDriver.cs diff --git a/Ryujinx.Input/IKeyboard.cs b/src/Ryujinx.Input/IKeyboard.cs similarity index 100% rename from Ryujinx.Input/IKeyboard.cs rename to src/Ryujinx.Input/IKeyboard.cs diff --git a/Ryujinx.Input/IMouse.cs b/src/Ryujinx.Input/IMouse.cs similarity index 100% rename from Ryujinx.Input/IMouse.cs rename to src/Ryujinx.Input/IMouse.cs diff --git a/Ryujinx.Input/Key.cs b/src/Ryujinx.Input/Key.cs similarity index 100% rename from Ryujinx.Input/Key.cs rename to src/Ryujinx.Input/Key.cs diff --git a/Ryujinx.Input/KeyboardStateSnapshot.cs b/src/Ryujinx.Input/KeyboardStateSnapshot.cs similarity index 100% rename from Ryujinx.Input/KeyboardStateSnapshot.cs rename to src/Ryujinx.Input/KeyboardStateSnapshot.cs diff --git a/Ryujinx.Input/Motion/CemuHook/Client.cs b/src/Ryujinx.Input/Motion/CemuHook/Client.cs similarity index 100% rename from Ryujinx.Input/Motion/CemuHook/Client.cs rename to src/Ryujinx.Input/Motion/CemuHook/Client.cs diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs similarity index 100% rename from Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs rename to src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs similarity index 100% rename from Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs rename to src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs similarity index 100% rename from Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs rename to src/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs similarity index 100% rename from Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs rename to src/Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs diff --git a/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs similarity index 100% rename from Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs rename to src/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs diff --git a/Ryujinx.Input/Motion/MotionInput.cs b/src/Ryujinx.Input/Motion/MotionInput.cs similarity index 100% rename from Ryujinx.Input/Motion/MotionInput.cs rename to src/Ryujinx.Input/Motion/MotionInput.cs diff --git a/Ryujinx.Input/Motion/MotionSensorFilter.cs b/src/Ryujinx.Input/Motion/MotionSensorFilter.cs similarity index 100% rename from Ryujinx.Input/Motion/MotionSensorFilter.cs rename to src/Ryujinx.Input/Motion/MotionSensorFilter.cs diff --git a/Ryujinx.Input/MotionInputId.cs b/src/Ryujinx.Input/MotionInputId.cs similarity index 100% rename from Ryujinx.Input/MotionInputId.cs rename to src/Ryujinx.Input/MotionInputId.cs diff --git a/Ryujinx.Input/MouseButton.cs b/src/Ryujinx.Input/MouseButton.cs similarity index 100% rename from Ryujinx.Input/MouseButton.cs rename to src/Ryujinx.Input/MouseButton.cs diff --git a/Ryujinx.Input/MouseStateSnapshot.cs b/src/Ryujinx.Input/MouseStateSnapshot.cs similarity index 100% rename from Ryujinx.Input/MouseStateSnapshot.cs rename to src/Ryujinx.Input/MouseStateSnapshot.cs diff --git a/Ryujinx.Input/Ryujinx.Input.csproj b/src/Ryujinx.Input/Ryujinx.Input.csproj similarity index 100% rename from Ryujinx.Input/Ryujinx.Input.csproj rename to src/Ryujinx.Input/Ryujinx.Input.csproj diff --git a/Ryujinx.Input/StickInputId.cs b/src/Ryujinx.Input/StickInputId.cs similarity index 100% rename from Ryujinx.Input/StickInputId.cs rename to src/Ryujinx.Input/StickInputId.cs diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/src/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs similarity index 100% rename from Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs rename to src/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs diff --git a/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs b/src/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs similarity index 100% rename from Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs rename to src/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs diff --git a/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj b/src/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj similarity index 100% rename from Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj rename to src/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj diff --git a/Ryujinx.Memory.Tests/Tests.cs b/src/Ryujinx.Memory.Tests/Tests.cs similarity index 100% rename from Ryujinx.Memory.Tests/Tests.cs rename to src/Ryujinx.Memory.Tests/Tests.cs diff --git a/Ryujinx.Memory.Tests/TrackingTests.cs b/src/Ryujinx.Memory.Tests/TrackingTests.cs similarity index 100% rename from Ryujinx.Memory.Tests/TrackingTests.cs rename to src/Ryujinx.Memory.Tests/TrackingTests.cs diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/src/Ryujinx.Memory/AddressSpaceManager.cs similarity index 100% rename from Ryujinx.Memory/AddressSpaceManager.cs rename to src/Ryujinx.Memory/AddressSpaceManager.cs diff --git a/Ryujinx.Memory/IRefCounted.cs b/src/Ryujinx.Memory/IRefCounted.cs similarity index 100% rename from Ryujinx.Memory/IRefCounted.cs rename to src/Ryujinx.Memory/IRefCounted.cs diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/src/Ryujinx.Memory/IVirtualMemoryManager.cs similarity index 100% rename from Ryujinx.Memory/IVirtualMemoryManager.cs rename to src/Ryujinx.Memory/IVirtualMemoryManager.cs diff --git a/Ryujinx.Memory/IWritableBlock.cs b/src/Ryujinx.Memory/IWritableBlock.cs similarity index 100% rename from Ryujinx.Memory/IWritableBlock.cs rename to src/Ryujinx.Memory/IWritableBlock.cs diff --git a/Ryujinx.Memory/InvalidAccessHandler.cs b/src/Ryujinx.Memory/InvalidAccessHandler.cs similarity index 100% rename from Ryujinx.Memory/InvalidAccessHandler.cs rename to src/Ryujinx.Memory/InvalidAccessHandler.cs diff --git a/Ryujinx.Memory/InvalidMemoryRegionException.cs b/src/Ryujinx.Memory/InvalidMemoryRegionException.cs similarity index 100% rename from Ryujinx.Memory/InvalidMemoryRegionException.cs rename to src/Ryujinx.Memory/InvalidMemoryRegionException.cs diff --git a/Ryujinx.Memory/MemoryAllocationFlags.cs b/src/Ryujinx.Memory/MemoryAllocationFlags.cs similarity index 100% rename from Ryujinx.Memory/MemoryAllocationFlags.cs rename to src/Ryujinx.Memory/MemoryAllocationFlags.cs diff --git a/Ryujinx.Memory/MemoryBlock.cs b/src/Ryujinx.Memory/MemoryBlock.cs similarity index 100% rename from Ryujinx.Memory/MemoryBlock.cs rename to src/Ryujinx.Memory/MemoryBlock.cs diff --git a/Ryujinx.Memory/MemoryConstants.cs b/src/Ryujinx.Memory/MemoryConstants.cs similarity index 100% rename from Ryujinx.Memory/MemoryConstants.cs rename to src/Ryujinx.Memory/MemoryConstants.cs diff --git a/Ryujinx.Memory/MemoryManagement.cs b/src/Ryujinx.Memory/MemoryManagement.cs similarity index 100% rename from Ryujinx.Memory/MemoryManagement.cs rename to src/Ryujinx.Memory/MemoryManagement.cs diff --git a/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs similarity index 100% rename from Ryujinx.Memory/MemoryManagementUnix.cs rename to src/Ryujinx.Memory/MemoryManagementUnix.cs diff --git a/Ryujinx.Memory/MemoryManagementWindows.cs b/src/Ryujinx.Memory/MemoryManagementWindows.cs similarity index 100% rename from Ryujinx.Memory/MemoryManagementWindows.cs rename to src/Ryujinx.Memory/MemoryManagementWindows.cs diff --git a/Ryujinx.Memory/MemoryManagerUnixHelper.cs b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs similarity index 100% rename from Ryujinx.Memory/MemoryManagerUnixHelper.cs rename to src/Ryujinx.Memory/MemoryManagerUnixHelper.cs diff --git a/Ryujinx.Memory/MemoryMapFlags.cs b/src/Ryujinx.Memory/MemoryMapFlags.cs similarity index 100% rename from Ryujinx.Memory/MemoryMapFlags.cs rename to src/Ryujinx.Memory/MemoryMapFlags.cs diff --git a/Ryujinx.Memory/MemoryNotContiguousException.cs b/src/Ryujinx.Memory/MemoryNotContiguousException.cs similarity index 100% rename from Ryujinx.Memory/MemoryNotContiguousException.cs rename to src/Ryujinx.Memory/MemoryNotContiguousException.cs diff --git a/Ryujinx.Memory/MemoryPermission.cs b/src/Ryujinx.Memory/MemoryPermission.cs similarity index 100% rename from Ryujinx.Memory/MemoryPermission.cs rename to src/Ryujinx.Memory/MemoryPermission.cs diff --git a/Ryujinx.Memory/MemoryProtectionException.cs b/src/Ryujinx.Memory/MemoryProtectionException.cs similarity index 100% rename from Ryujinx.Memory/MemoryProtectionException.cs rename to src/Ryujinx.Memory/MemoryProtectionException.cs diff --git a/Ryujinx.Memory/NativeMemoryManager.cs b/src/Ryujinx.Memory/NativeMemoryManager.cs similarity index 100% rename from Ryujinx.Memory/NativeMemoryManager.cs rename to src/Ryujinx.Memory/NativeMemoryManager.cs diff --git a/Ryujinx.Memory/PageTable.cs b/src/Ryujinx.Memory/PageTable.cs similarity index 100% rename from Ryujinx.Memory/PageTable.cs rename to src/Ryujinx.Memory/PageTable.cs diff --git a/Ryujinx.Memory/Range/HostMemoryRange.cs b/src/Ryujinx.Memory/Range/HostMemoryRange.cs similarity index 100% rename from Ryujinx.Memory/Range/HostMemoryRange.cs rename to src/Ryujinx.Memory/Range/HostMemoryRange.cs diff --git a/Ryujinx.Memory/Range/IMultiRangeItem.cs b/src/Ryujinx.Memory/Range/IMultiRangeItem.cs similarity index 100% rename from Ryujinx.Memory/Range/IMultiRangeItem.cs rename to src/Ryujinx.Memory/Range/IMultiRangeItem.cs diff --git a/Ryujinx.Memory/Range/INonOverlappingRange.cs b/src/Ryujinx.Memory/Range/INonOverlappingRange.cs similarity index 100% rename from Ryujinx.Memory/Range/INonOverlappingRange.cs rename to src/Ryujinx.Memory/Range/INonOverlappingRange.cs diff --git a/Ryujinx.Memory/Range/IRange.cs b/src/Ryujinx.Memory/Range/IRange.cs similarity index 100% rename from Ryujinx.Memory/Range/IRange.cs rename to src/Ryujinx.Memory/Range/IRange.cs diff --git a/Ryujinx.Memory/Range/MemoryRange.cs b/src/Ryujinx.Memory/Range/MemoryRange.cs similarity index 100% rename from Ryujinx.Memory/Range/MemoryRange.cs rename to src/Ryujinx.Memory/Range/MemoryRange.cs diff --git a/Ryujinx.Memory/Range/MultiRange.cs b/src/Ryujinx.Memory/Range/MultiRange.cs similarity index 100% rename from Ryujinx.Memory/Range/MultiRange.cs rename to src/Ryujinx.Memory/Range/MultiRange.cs diff --git a/Ryujinx.Memory/Range/MultiRangeList.cs b/src/Ryujinx.Memory/Range/MultiRangeList.cs similarity index 100% rename from Ryujinx.Memory/Range/MultiRangeList.cs rename to src/Ryujinx.Memory/Range/MultiRangeList.cs diff --git a/Ryujinx.Memory/Range/NonOverlappingRangeList.cs b/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs similarity index 100% rename from Ryujinx.Memory/Range/NonOverlappingRangeList.cs rename to src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs diff --git a/Ryujinx.Memory/Range/RangeList.cs b/src/Ryujinx.Memory/Range/RangeList.cs similarity index 100% rename from Ryujinx.Memory/Range/RangeList.cs rename to src/Ryujinx.Memory/Range/RangeList.cs diff --git a/Ryujinx.Memory/Ryujinx.Memory.csproj b/src/Ryujinx.Memory/Ryujinx.Memory.csproj similarity index 100% rename from Ryujinx.Memory/Ryujinx.Memory.csproj rename to src/Ryujinx.Memory/Ryujinx.Memory.csproj diff --git a/Ryujinx.Memory/Tracking/AbstractRegion.cs b/src/Ryujinx.Memory/Tracking/AbstractRegion.cs similarity index 100% rename from Ryujinx.Memory/Tracking/AbstractRegion.cs rename to src/Ryujinx.Memory/Tracking/AbstractRegion.cs diff --git a/Ryujinx.Memory/Tracking/BitMap.cs b/src/Ryujinx.Memory/Tracking/BitMap.cs similarity index 100% rename from Ryujinx.Memory/Tracking/BitMap.cs rename to src/Ryujinx.Memory/Tracking/BitMap.cs diff --git a/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs b/src/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs similarity index 100% rename from Ryujinx.Memory/Tracking/ConcurrentBitmap.cs rename to src/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs diff --git a/Ryujinx.Memory/Tracking/IMultiRegionHandle.cs b/src/Ryujinx.Memory/Tracking/IMultiRegionHandle.cs similarity index 100% rename from Ryujinx.Memory/Tracking/IMultiRegionHandle.cs rename to src/Ryujinx.Memory/Tracking/IMultiRegionHandle.cs diff --git a/Ryujinx.Memory/Tracking/IRegionHandle.cs b/src/Ryujinx.Memory/Tracking/IRegionHandle.cs similarity index 100% rename from Ryujinx.Memory/Tracking/IRegionHandle.cs rename to src/Ryujinx.Memory/Tracking/IRegionHandle.cs diff --git a/Ryujinx.Memory/Tracking/MemoryTracking.cs b/src/Ryujinx.Memory/Tracking/MemoryTracking.cs similarity index 100% rename from Ryujinx.Memory/Tracking/MemoryTracking.cs rename to src/Ryujinx.Memory/Tracking/MemoryTracking.cs diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs similarity index 100% rename from Ryujinx.Memory/Tracking/MultiRegionHandle.cs rename to src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs diff --git a/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs b/src/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs similarity index 100% rename from Ryujinx.Memory/Tracking/PreciseRegionSignal.cs rename to src/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/src/Ryujinx.Memory/Tracking/RegionHandle.cs similarity index 100% rename from Ryujinx.Memory/Tracking/RegionHandle.cs rename to src/Ryujinx.Memory/Tracking/RegionHandle.cs diff --git a/Ryujinx.Memory/Tracking/RegionSignal.cs b/src/Ryujinx.Memory/Tracking/RegionSignal.cs similarity index 100% rename from Ryujinx.Memory/Tracking/RegionSignal.cs rename to src/Ryujinx.Memory/Tracking/RegionSignal.cs diff --git a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs b/src/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs similarity index 100% rename from Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs rename to src/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs diff --git a/Ryujinx.Memory/Tracking/VirtualRegion.cs b/src/Ryujinx.Memory/Tracking/VirtualRegion.cs similarity index 100% rename from Ryujinx.Memory/Tracking/VirtualRegion.cs rename to src/Ryujinx.Memory/Tracking/VirtualRegion.cs diff --git a/Ryujinx.Memory/WindowsShared/MappingTree.cs b/src/Ryujinx.Memory/WindowsShared/MappingTree.cs similarity index 100% rename from Ryujinx.Memory/WindowsShared/MappingTree.cs rename to src/Ryujinx.Memory/WindowsShared/MappingTree.cs diff --git a/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs b/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs similarity index 100% rename from Ryujinx.Memory/WindowsShared/PlaceholderManager.cs rename to src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs diff --git a/Ryujinx.Memory/WindowsShared/WindowsApi.cs b/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs similarity index 100% rename from Ryujinx.Memory/WindowsShared/WindowsApi.cs rename to src/Ryujinx.Memory/WindowsShared/WindowsApi.cs diff --git a/Ryujinx.Memory/WindowsShared/WindowsApiException.cs b/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs similarity index 100% rename from Ryujinx.Memory/WindowsShared/WindowsApiException.cs rename to src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs diff --git a/Ryujinx.Memory/WindowsShared/WindowsFlags.cs b/src/Ryujinx.Memory/WindowsShared/WindowsFlags.cs similarity index 100% rename from Ryujinx.Memory/WindowsShared/WindowsFlags.cs rename to src/Ryujinx.Memory/WindowsShared/WindowsFlags.cs diff --git a/Ryujinx.Memory/WritableRegion.cs b/src/Ryujinx.Memory/WritableRegion.cs similarity index 100% rename from Ryujinx.Memory/WritableRegion.cs rename to src/Ryujinx.Memory/WritableRegion.cs diff --git a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj b/src/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj similarity index 100% rename from Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj rename to src/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj diff --git a/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs similarity index 100% rename from Ryujinx.SDL2.Common/SDL2Driver.cs rename to src/Ryujinx.SDL2.Common/SDL2Driver.cs diff --git a/Ryujinx.ShaderTools/Program.cs b/src/Ryujinx.ShaderTools/Program.cs similarity index 100% rename from Ryujinx.ShaderTools/Program.cs rename to src/Ryujinx.ShaderTools/Program.cs diff --git a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj similarity index 100% rename from Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj rename to src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj diff --git a/Ryujinx.Tests.Unicorn/IndexedProperty.cs b/src/Ryujinx.Tests.Unicorn/IndexedProperty.cs similarity index 100% rename from Ryujinx.Tests.Unicorn/IndexedProperty.cs rename to src/Ryujinx.Tests.Unicorn/IndexedProperty.cs diff --git a/Ryujinx.Tests.Unicorn/MemoryPermission.cs b/src/Ryujinx.Tests.Unicorn/MemoryPermission.cs similarity index 100% rename from Ryujinx.Tests.Unicorn/MemoryPermission.cs rename to src/Ryujinx.Tests.Unicorn/MemoryPermission.cs diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj similarity index 100% rename from Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj rename to src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj diff --git a/Ryujinx.Tests.Unicorn/SimdValue.cs b/src/Ryujinx.Tests.Unicorn/SimdValue.cs similarity index 100% rename from Ryujinx.Tests.Unicorn/SimdValue.cs rename to src/Ryujinx.Tests.Unicorn/SimdValue.cs diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs similarity index 100% rename from Ryujinx.Tests.Unicorn/UnicornAArch32.cs rename to src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/src/Ryujinx.Tests.Unicorn/UnicornAArch64.cs similarity index 100% rename from Ryujinx.Tests.Unicorn/UnicornAArch64.cs rename to src/Ryujinx.Tests.Unicorn/UnicornAArch64.cs diff --git a/Ryujinx.Tests/.runsettings b/src/Ryujinx.Tests/.runsettings similarity index 100% rename from Ryujinx.Tests/.runsettings rename to src/Ryujinx.Tests/.runsettings diff --git a/Ryujinx.Tests/Audio/Renderer/AudioRendererConfigurationTests.cs b/src/Ryujinx.Tests/Audio/Renderer/AudioRendererConfigurationTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/AudioRendererConfigurationTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/AudioRendererConfigurationTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/BehaviourParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/BehaviourParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/BehaviourParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/BehaviourParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/BiquadFilterParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Common/UpdateDataHeaderTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Common/UpdateDataHeaderTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Common/UpdateDataHeaderTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Common/UpdateDataHeaderTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Common/VoiceUpdateStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Common/VoiceUpdateStateTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Common/VoiceUpdateStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Common/VoiceUpdateStateTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Common/WaveBufferTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Common/WaveBufferTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Common/WaveBufferTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Common/WaveBufferTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/EffectInfoParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs b/src/Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/EffectOutStatusTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/MemoryPoolParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/MemoryPoolParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/MemoryPoolParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/MemoryPoolParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/BehaviourErrorInfoOutStatusTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/BehaviourErrorInfoOutStatusTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/BehaviourErrorInfoOutStatusTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/BehaviourErrorInfoOutStatusTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/AuxParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/AuxParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/AuxParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/AuxParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BufferMixerParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BufferMixerParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BufferMixerParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/BufferMixerParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/DelayParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/DelayParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/DelayParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/DelayParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/Reverb3dParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/Reverb3dParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/Reverb3dParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/Reverb3dParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/ReverbParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/ReverbParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Effect/ReverbParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/ReverbParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdateTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdateTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/MixParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/MixParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/MixParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/MixParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceInParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceInParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceInParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceInParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceOutStatusTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceOutStatusTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceOutStatusTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/PerformanceOutStatusTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/RendererInfoOutStatusTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/RendererInfoOutStatusTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/RendererInfoOutStatusTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/RendererInfoOutStatusTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Sink/CircularBufferParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Sink/CircularBufferParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Sink/CircularBufferParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Sink/CircularBufferParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/Sink/DeviceParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Sink/DeviceParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/Sink/DeviceParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/Sink/DeviceParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/SinkInParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/SinkInParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/SinkInParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/SinkInParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/SinkOutStatusTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/SinkOutStatusTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/SinkOutStatusTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/SinkOutStatusTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Parameter/SplitterInParamHeaderTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/SplitterInParamHeaderTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Parameter/SplitterInParamHeaderTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Parameter/SplitterInParamHeaderTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/AddressInfoTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolStateTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/MemoryPoolStateTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/MixStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/MixStateTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/MixStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/MixStateTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/SplitterDestinationTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/SplitterStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/SplitterStateTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/SplitterStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/SplitterStateTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/VoiceChannelResourceTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/VoiceChannelResourceTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/VoiceChannelResourceTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/VoiceChannelResourceTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/VoiceStateTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/VoiceStateTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/VoiceStateTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/VoiceStateTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/Server/WaveBufferTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/WaveBufferTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/Server/WaveBufferTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/Server/WaveBufferTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/VoiceChannelResourceInParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/VoiceChannelResourceInParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/VoiceChannelResourceInParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/VoiceChannelResourceInParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/VoiceInParameterTests.cs diff --git a/Ryujinx.Tests/Audio/Renderer/VoiceOutStatusTests.cs b/src/Ryujinx.Tests/Audio/Renderer/VoiceOutStatusTests.cs similarity index 100% rename from Ryujinx.Tests/Audio/Renderer/VoiceOutStatusTests.cs rename to src/Ryujinx.Tests/Audio/Renderer/VoiceOutStatusTests.cs diff --git a/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs b/src/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs similarity index 100% rename from Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs rename to src/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs diff --git a/Ryujinx.Tests/Cpu/CpuContext.cs b/src/Ryujinx.Tests/Cpu/CpuContext.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuContext.cs rename to src/Ryujinx.Tests/Cpu/CpuContext.cs diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/src/Ryujinx.Tests/Cpu/CpuTest.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTest.cs rename to src/Ryujinx.Tests/Cpu/CpuTest.cs diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/src/Ryujinx.Tests/Cpu/CpuTest32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTest32.cs rename to src/Ryujinx.Tests/Cpu/CpuTest32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/src/Ryujinx.Tests/Cpu/CpuTestAlu.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAlu.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAlu.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAlu32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAluBinary.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluImm.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAluImm.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAluImm.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAluImm32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluRs.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAluRs.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAluRs.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAluRs32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluRx.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestAluRx.cs rename to src/Ryujinx.Tests/Cpu/CpuTestAluRx.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestBf32.cs b/src/Ryujinx.Tests/Cpu/CpuTestBf32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestBf32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestBf32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestBfm.cs b/src/Ryujinx.Tests/Cpu/CpuTestBfm.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestBfm.cs rename to src/Ryujinx.Tests/Cpu/CpuTestBfm.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs rename to src/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs b/src/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs rename to src/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestCsel.cs b/src/Ryujinx.Tests/Cpu/CpuTestCsel.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestCsel.cs rename to src/Ryujinx.Tests/Cpu/CpuTestCsel.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestMisc.cs rename to src/Ryujinx.Tests/Cpu/CpuTestMisc.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc32.cs b/src/Ryujinx.Tests/Cpu/CpuTestMisc32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestMisc32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestMisc32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestMov.cs b/src/Ryujinx.Tests/Cpu/CpuTestMov.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestMov.cs rename to src/Ryujinx.Tests/Cpu/CpuTestMov.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestMul.cs b/src/Ryujinx.Tests/Cpu/CpuTestMul.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestMul.cs rename to src/Ryujinx.Tests/Cpu/CpuTestMul.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestMul32.cs b/src/Ryujinx.Tests/Cpu/CpuTestMul32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestMul32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestMul32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimd.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimd.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimd32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdExt.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdImm.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdIns.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdReg.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdRegElem32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestSystem.cs b/src/Ryujinx.Tests/Cpu/CpuTestSystem.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestSystem.cs rename to src/Ryujinx.Tests/Cpu/CpuTestSystem.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestT32Alu.cs rename to src/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestT32Flow.cs rename to src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestT32Mem.cs rename to src/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs diff --git a/Ryujinx.Tests/Cpu/CpuTestThumb.cs b/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs similarity index 100% rename from Ryujinx.Tests/Cpu/CpuTestThumb.cs rename to src/Ryujinx.Tests/Cpu/CpuTestThumb.cs diff --git a/Ryujinx.Tests/Cpu/EnvironmentTests.cs b/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs similarity index 100% rename from Ryujinx.Tests/Cpu/EnvironmentTests.cs rename to src/Ryujinx.Tests/Cpu/EnvironmentTests.cs diff --git a/Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs b/src/Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs similarity index 100% rename from Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs rename to src/Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs diff --git a/Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs b/src/Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs similarity index 100% rename from Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs rename to src/Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs diff --git a/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs b/src/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs similarity index 100% rename from Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs rename to src/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs diff --git a/Ryujinx.Tests/Memory/MockMemoryManager.cs b/src/Ryujinx.Tests/Memory/MockMemoryManager.cs similarity index 100% rename from Ryujinx.Tests/Memory/MockMemoryManager.cs rename to src/Ryujinx.Tests/Memory/MockMemoryManager.cs diff --git a/Ryujinx.Tests/Memory/PartialUnmaps.cs b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs similarity index 100% rename from Ryujinx.Tests/Memory/PartialUnmaps.cs rename to src/Ryujinx.Tests/Memory/PartialUnmaps.cs diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/src/Ryujinx.Tests/Ryujinx.Tests.csproj similarity index 100% rename from Ryujinx.Tests/Ryujinx.Tests.csproj rename to src/Ryujinx.Tests/Ryujinx.Tests.csproj diff --git a/Ryujinx.Tests/Time/TimeZoneRuleTests.cs b/src/Ryujinx.Tests/Time/TimeZoneRuleTests.cs similarity index 100% rename from Ryujinx.Tests/Time/TimeZoneRuleTests.cs rename to src/Ryujinx.Tests/Time/TimeZoneRuleTests.cs diff --git a/Ryujinx.Tests/TreeDictionaryTests.cs b/src/Ryujinx.Tests/TreeDictionaryTests.cs similarity index 100% rename from Ryujinx.Tests/TreeDictionaryTests.cs rename to src/Ryujinx.Tests/TreeDictionaryTests.cs diff --git a/Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs b/src/Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs similarity index 100% rename from Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs rename to src/Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs diff --git a/Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs b/src/Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs similarity index 100% rename from Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs rename to src/Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs diff --git a/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs similarity index 100% rename from Ryujinx.Ui.Common/App/ApplicationData.cs rename to src/Ryujinx.Ui.Common/App/ApplicationData.cs diff --git a/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs rename to src/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs similarity index 100% rename from Ryujinx.Ui.Common/App/ApplicationLibrary.cs rename to src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs diff --git a/Ryujinx.Ui.Common/App/ApplicationMetadata.cs b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs similarity index 100% rename from Ryujinx.Ui.Common/App/ApplicationMetadata.cs rename to src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs diff --git a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs b/src/Ryujinx.Ui.Common/Configuration/AudioBackend.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/AudioBackend.cs rename to src/Ryujinx.Ui.Common/Configuration/AudioBackend.cs diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs rename to src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs rename to src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs rename to src/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs rename to src/Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/ConfigurationState.cs rename to src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs diff --git a/Ryujinx.Ui.Common/Configuration/FileTypes.cs b/src/Ryujinx.Ui.Common/Configuration/FileTypes.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/FileTypes.cs rename to src/Ryujinx.Ui.Common/Configuration/FileTypes.cs diff --git a/Ryujinx.Ui.Common/Configuration/LoggerModule.cs b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/LoggerModule.cs rename to src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs diff --git a/Ryujinx.Ui.Common/Configuration/System/Language.cs b/src/Ryujinx.Ui.Common/Configuration/System/Language.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/System/Language.cs rename to src/Ryujinx.Ui.Common/Configuration/System/Language.cs diff --git a/Ryujinx.Ui.Common/Configuration/System/Region.cs b/src/Ryujinx.Ui.Common/Configuration/System/Region.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/System/Region.cs rename to src/Ryujinx.Ui.Common/Configuration/System/Region.cs diff --git a/Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs b/src/Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs rename to src/Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs diff --git a/Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs b/src/Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs rename to src/Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs diff --git a/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs b/src/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs similarity index 100% rename from Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs rename to src/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs diff --git a/Ryujinx.Ui.Common/DiscordIntegrationModule.cs b/src/Ryujinx.Ui.Common/DiscordIntegrationModule.cs similarity index 100% rename from Ryujinx.Ui.Common/DiscordIntegrationModule.cs rename to src/Ryujinx.Ui.Common/DiscordIntegrationModule.cs diff --git a/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs b/src/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs similarity index 100% rename from Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs rename to src/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs diff --git a/Ryujinx.Ui.Common/Helper/CommandLineState.cs b/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs similarity index 100% rename from Ryujinx.Ui.Common/Helper/CommandLineState.cs rename to src/Ryujinx.Ui.Common/Helper/CommandLineState.cs diff --git a/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs b/src/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs similarity index 100% rename from Ryujinx.Ui.Common/Helper/ConsoleHelper.cs rename to src/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs diff --git a/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs similarity index 100% rename from Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs rename to src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs diff --git a/Ryujinx.Ui.Common/Helper/ObjectiveC.cs b/src/Ryujinx.Ui.Common/Helper/ObjectiveC.cs similarity index 100% rename from Ryujinx.Ui.Common/Helper/ObjectiveC.cs rename to src/Ryujinx.Ui.Common/Helper/ObjectiveC.cs diff --git a/Ryujinx.Ui.Common/Helper/OpenHelper.cs b/src/Ryujinx.Ui.Common/Helper/OpenHelper.cs similarity index 100% rename from Ryujinx.Ui.Common/Helper/OpenHelper.cs rename to src/Ryujinx.Ui.Common/Helper/OpenHelper.cs diff --git a/Ryujinx.Ui.Common/Helper/SetupValidator.cs b/src/Ryujinx.Ui.Common/Helper/SetupValidator.cs similarity index 100% rename from Ryujinx.Ui.Common/Helper/SetupValidator.cs rename to src/Ryujinx.Ui.Common/Helper/SetupValidator.cs diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs rename to src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs rename to src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs rename to src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs rename to src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs rename to src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs b/src/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs rename to src/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs b/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs rename to src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs similarity index 100% rename from Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs rename to src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs diff --git a/Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg b/src/Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg similarity index 100% rename from Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg rename to src/Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg diff --git a/Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg b/src/Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg similarity index 100% rename from Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg rename to src/Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg diff --git a/Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg b/src/Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg similarity index 100% rename from Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg rename to src/Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg diff --git a/Ryujinx.Ui.Common/Resources/Controller_ProCon.svg b/src/Ryujinx.Ui.Common/Resources/Controller_ProCon.svg similarity index 100% rename from Ryujinx.Ui.Common/Resources/Controller_ProCon.svg rename to src/Ryujinx.Ui.Common/Resources/Controller_ProCon.svg diff --git a/Ryujinx.Ui.Common/Resources/Icon_NCA.png b/src/Ryujinx.Ui.Common/Resources/Icon_NCA.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Icon_NCA.png rename to src/Ryujinx.Ui.Common/Resources/Icon_NCA.png diff --git a/Ryujinx.Ui.Common/Resources/Icon_NRO.png b/src/Ryujinx.Ui.Common/Resources/Icon_NRO.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Icon_NRO.png rename to src/Ryujinx.Ui.Common/Resources/Icon_NRO.png diff --git a/Ryujinx.Ui.Common/Resources/Icon_NSO.png b/src/Ryujinx.Ui.Common/Resources/Icon_NSO.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Icon_NSO.png rename to src/Ryujinx.Ui.Common/Resources/Icon_NSO.png diff --git a/Ryujinx.Ui.Common/Resources/Icon_NSP.png b/src/Ryujinx.Ui.Common/Resources/Icon_NSP.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Icon_NSP.png rename to src/Ryujinx.Ui.Common/Resources/Icon_NSP.png diff --git a/Ryujinx.Ui.Common/Resources/Icon_XCI.png b/src/Ryujinx.Ui.Common/Resources/Icon_XCI.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Icon_XCI.png rename to src/Ryujinx.Ui.Common/Resources/Icon_XCI.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Amiibo.png b/src/Ryujinx.Ui.Common/Resources/Logo_Amiibo.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Amiibo.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Amiibo.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Discord_Dark.png b/src/Ryujinx.Ui.Common/Resources/Logo_Discord_Dark.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Discord_Dark.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Discord_Dark.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Discord_Light.png b/src/Ryujinx.Ui.Common/Resources/Logo_Discord_Light.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Discord_Light.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Discord_Light.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_GitHub_Dark.png b/src/Ryujinx.Ui.Common/Resources/Logo_GitHub_Dark.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_GitHub_Dark.png rename to src/Ryujinx.Ui.Common/Resources/Logo_GitHub_Dark.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_GitHub_Light.png b/src/Ryujinx.Ui.Common/Resources/Logo_GitHub_Light.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_GitHub_Light.png rename to src/Ryujinx.Ui.Common/Resources/Logo_GitHub_Light.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Patreon_Dark.png b/src/Ryujinx.Ui.Common/Resources/Logo_Patreon_Dark.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Patreon_Dark.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Patreon_Dark.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Patreon_Light.png b/src/Ryujinx.Ui.Common/Resources/Logo_Patreon_Light.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Patreon_Light.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Patreon_Light.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Ryujinx.png b/src/Ryujinx.Ui.Common/Resources/Logo_Ryujinx.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Ryujinx.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Ryujinx.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Twitter_Dark.png b/src/Ryujinx.Ui.Common/Resources/Logo_Twitter_Dark.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Twitter_Dark.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Twitter_Dark.png diff --git a/Ryujinx.Ui.Common/Resources/Logo_Twitter_Light.png b/src/Ryujinx.Ui.Common/Resources/Logo_Twitter_Light.png similarity index 100% rename from Ryujinx.Ui.Common/Resources/Logo_Twitter_Light.png rename to src/Ryujinx.Ui.Common/Resources/Logo_Twitter_Light.png diff --git a/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj b/src/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj similarity index 100% rename from Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj rename to src/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj diff --git a/Ryujinx.Ui.Common/UserError.cs b/src/Ryujinx.Ui.Common/UserError.cs similarity index 100% rename from Ryujinx.Ui.Common/UserError.cs rename to src/Ryujinx.Ui.Common/UserError.cs diff --git a/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs b/src/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs similarity index 100% rename from Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs rename to src/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs diff --git a/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj b/src/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj similarity index 100% rename from Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj rename to src/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj diff --git a/Ryujinx/Input/GTK3/GTK3Keyboard.cs b/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs similarity index 100% rename from Ryujinx/Input/GTK3/GTK3Keyboard.cs rename to src/Ryujinx/Input/GTK3/GTK3Keyboard.cs diff --git a/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs b/src/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs similarity index 100% rename from Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs rename to src/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs diff --git a/Ryujinx/Input/GTK3/GTK3MappingHelper.cs b/src/Ryujinx/Input/GTK3/GTK3MappingHelper.cs similarity index 100% rename from Ryujinx/Input/GTK3/GTK3MappingHelper.cs rename to src/Ryujinx/Input/GTK3/GTK3MappingHelper.cs diff --git a/Ryujinx/Input/GTK3/GTK3Mouse.cs b/src/Ryujinx/Input/GTK3/GTK3Mouse.cs similarity index 100% rename from Ryujinx/Input/GTK3/GTK3Mouse.cs rename to src/Ryujinx/Input/GTK3/GTK3Mouse.cs diff --git a/Ryujinx/Input/GTK3/GTK3MouseDriver.cs b/src/Ryujinx/Input/GTK3/GTK3MouseDriver.cs similarity index 100% rename from Ryujinx/Input/GTK3/GTK3MouseDriver.cs rename to src/Ryujinx/Input/GTK3/GTK3MouseDriver.cs diff --git a/Ryujinx/Modules/Updater/UpdateDialog.cs b/src/Ryujinx/Modules/Updater/UpdateDialog.cs similarity index 100% rename from Ryujinx/Modules/Updater/UpdateDialog.cs rename to src/Ryujinx/Modules/Updater/UpdateDialog.cs diff --git a/Ryujinx/Modules/Updater/UpdateDialog.glade b/src/Ryujinx/Modules/Updater/UpdateDialog.glade similarity index 100% rename from Ryujinx/Modules/Updater/UpdateDialog.glade rename to src/Ryujinx/Modules/Updater/UpdateDialog.glade diff --git a/Ryujinx/Modules/Updater/Updater.cs b/src/Ryujinx/Modules/Updater/Updater.cs similarity index 100% rename from Ryujinx/Modules/Updater/Updater.cs rename to src/Ryujinx/Modules/Updater/Updater.cs diff --git a/Ryujinx/Program.cs b/src/Ryujinx/Program.cs similarity index 100% rename from Ryujinx/Program.cs rename to src/Ryujinx/Program.cs diff --git a/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj similarity index 92% rename from Ryujinx/Ryujinx.csproj rename to src/Ryujinx/Ryujinx.csproj index 6aae6296f..027cdd129 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -48,25 +48,25 @@ </ItemGroup> <ItemGroup> - <Content Include="..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'"> + <Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>alsoft.ini</TargetPath> </Content> - <Content Include="..\distribution\legal\THIRDPARTY.md"> + <Content Include="..\..\distribution\legal\THIRDPARTY.md"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>THIRDPARTY.md</TargetPath> </Content> - <Content Include="..\LICENSE.txt"> + <Content Include="..\..\LICENSE.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>LICENSE.txt</TargetPath> </Content> </ItemGroup> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'"> - <Content Include="..\distribution\linux\Ryujinx.sh"> + <Content Include="..\..\distribution\linux\Ryujinx.sh"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> - <Content Include="..\distribution\linux\mime\Ryujinx.xml"> + <Content Include="..\..\distribution\linux\mime\Ryujinx.xml"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <TargetPath>mime\Ryujinx.xml</TargetPath> </Content> diff --git a/Ryujinx/Ryujinx.ico b/src/Ryujinx/Ryujinx.ico similarity index 100% rename from Ryujinx/Ryujinx.ico rename to src/Ryujinx/Ryujinx.ico diff --git a/Ryujinx/Ui/Applet/ErrorAppletDialog.cs b/src/Ryujinx/Ui/Applet/ErrorAppletDialog.cs similarity index 100% rename from Ryujinx/Ui/Applet/ErrorAppletDialog.cs rename to src/Ryujinx/Ui/Applet/ErrorAppletDialog.cs diff --git a/Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs b/src/Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs similarity index 100% rename from Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs rename to src/Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs diff --git a/Ryujinx/Ui/Applet/GtkHostUiHandler.cs b/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs similarity index 100% rename from Ryujinx/Ui/Applet/GtkHostUiHandler.cs rename to src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs diff --git a/Ryujinx/Ui/Applet/GtkHostUiTheme.cs b/src/Ryujinx/Ui/Applet/GtkHostUiTheme.cs similarity index 100% rename from Ryujinx/Ui/Applet/GtkHostUiTheme.cs rename to src/Ryujinx/Ui/Applet/GtkHostUiTheme.cs diff --git a/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs similarity index 100% rename from Ryujinx/Ui/Applet/SwkbdAppletDialog.cs rename to src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs diff --git a/Ryujinx/Ui/GLRenderer.cs b/src/Ryujinx/Ui/GLRenderer.cs similarity index 100% rename from Ryujinx/Ui/GLRenderer.cs rename to src/Ryujinx/Ui/GLRenderer.cs diff --git a/Ryujinx/Ui/Helper/MetalHelper.cs b/src/Ryujinx/Ui/Helper/MetalHelper.cs similarity index 100% rename from Ryujinx/Ui/Helper/MetalHelper.cs rename to src/Ryujinx/Ui/Helper/MetalHelper.cs diff --git a/Ryujinx/Ui/Helper/SortHelper.cs b/src/Ryujinx/Ui/Helper/SortHelper.cs similarity index 100% rename from Ryujinx/Ui/Helper/SortHelper.cs rename to src/Ryujinx/Ui/Helper/SortHelper.cs diff --git a/Ryujinx/Ui/Helper/ThemeHelper.cs b/src/Ryujinx/Ui/Helper/ThemeHelper.cs similarity index 100% rename from Ryujinx/Ui/Helper/ThemeHelper.cs rename to src/Ryujinx/Ui/Helper/ThemeHelper.cs diff --git a/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs similarity index 100% rename from Ryujinx/Ui/MainWindow.cs rename to src/Ryujinx/Ui/MainWindow.cs diff --git a/Ryujinx/Ui/MainWindow.glade b/src/Ryujinx/Ui/MainWindow.glade similarity index 100% rename from Ryujinx/Ui/MainWindow.glade rename to src/Ryujinx/Ui/MainWindow.glade diff --git a/Ryujinx/Ui/OpenToolkitBindingsContext.cs b/src/Ryujinx/Ui/OpenToolkitBindingsContext.cs similarity index 100% rename from Ryujinx/Ui/OpenToolkitBindingsContext.cs rename to src/Ryujinx/Ui/OpenToolkitBindingsContext.cs diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs similarity index 100% rename from Ryujinx/Ui/RendererWidgetBase.cs rename to src/Ryujinx/Ui/RendererWidgetBase.cs diff --git a/Ryujinx/Ui/SPBOpenGLContext.cs b/src/Ryujinx/Ui/SPBOpenGLContext.cs similarity index 100% rename from Ryujinx/Ui/SPBOpenGLContext.cs rename to src/Ryujinx/Ui/SPBOpenGLContext.cs diff --git a/Ryujinx/Ui/StatusUpdatedEventArgs.cs b/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs similarity index 100% rename from Ryujinx/Ui/StatusUpdatedEventArgs.cs rename to src/Ryujinx/Ui/StatusUpdatedEventArgs.cs diff --git a/Ryujinx/Ui/VKRenderer.cs b/src/Ryujinx/Ui/VKRenderer.cs similarity index 100% rename from Ryujinx/Ui/VKRenderer.cs rename to src/Ryujinx/Ui/VKRenderer.cs diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs similarity index 100% rename from Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs rename to src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs similarity index 100% rename from Ryujinx/Ui/Widgets/GameTableContextMenu.cs rename to src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs diff --git a/Ryujinx/Ui/Widgets/GtkDialog.cs b/src/Ryujinx/Ui/Widgets/GtkDialog.cs similarity index 100% rename from Ryujinx/Ui/Widgets/GtkDialog.cs rename to src/Ryujinx/Ui/Widgets/GtkDialog.cs diff --git a/Ryujinx/Ui/Widgets/GtkInputDialog.cs b/src/Ryujinx/Ui/Widgets/GtkInputDialog.cs similarity index 100% rename from Ryujinx/Ui/Widgets/GtkInputDialog.cs rename to src/Ryujinx/Ui/Widgets/GtkInputDialog.cs diff --git a/Ryujinx/Ui/Widgets/ProfileDialog.cs b/src/Ryujinx/Ui/Widgets/ProfileDialog.cs similarity index 100% rename from Ryujinx/Ui/Widgets/ProfileDialog.cs rename to src/Ryujinx/Ui/Widgets/ProfileDialog.cs diff --git a/Ryujinx/Ui/Widgets/ProfileDialog.glade b/src/Ryujinx/Ui/Widgets/ProfileDialog.glade similarity index 100% rename from Ryujinx/Ui/Widgets/ProfileDialog.glade rename to src/Ryujinx/Ui/Widgets/ProfileDialog.glade diff --git a/Ryujinx/Ui/Widgets/RawInputToTextEntry.cs b/src/Ryujinx/Ui/Widgets/RawInputToTextEntry.cs similarity index 100% rename from Ryujinx/Ui/Widgets/RawInputToTextEntry.cs rename to src/Ryujinx/Ui/Widgets/RawInputToTextEntry.cs diff --git a/Ryujinx/Ui/Widgets/UserErrorDialog.cs b/src/Ryujinx/Ui/Widgets/UserErrorDialog.cs similarity index 100% rename from Ryujinx/Ui/Widgets/UserErrorDialog.cs rename to src/Ryujinx/Ui/Widgets/UserErrorDialog.cs diff --git a/Ryujinx/Ui/Windows/AboutWindow.Designer.cs b/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs similarity index 100% rename from Ryujinx/Ui/Windows/AboutWindow.Designer.cs rename to src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs diff --git a/Ryujinx/Ui/Windows/AboutWindow.cs b/src/Ryujinx/Ui/Windows/AboutWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/AboutWindow.cs rename to src/Ryujinx/Ui/Windows/AboutWindow.cs diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs b/src/Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs similarity index 100% rename from Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs rename to src/Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.cs b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/AmiiboWindow.cs rename to src/Ryujinx/Ui/Windows/AmiiboWindow.cs diff --git a/Ryujinx/Ui/Windows/AvatarWindow.cs b/src/Ryujinx/Ui/Windows/AvatarWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/AvatarWindow.cs rename to src/Ryujinx/Ui/Windows/AvatarWindow.cs diff --git a/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/CheatWindow.cs rename to src/Ryujinx/Ui/Windows/CheatWindow.cs diff --git a/Ryujinx/Ui/Windows/CheatWindow.glade b/src/Ryujinx/Ui/Windows/CheatWindow.glade similarity index 100% rename from Ryujinx/Ui/Windows/CheatWindow.glade rename to src/Ryujinx/Ui/Windows/CheatWindow.glade diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/src/Ryujinx/Ui/Windows/ControllerWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/ControllerWindow.cs rename to src/Ryujinx/Ui/Windows/ControllerWindow.cs diff --git a/Ryujinx/Ui/Windows/ControllerWindow.glade b/src/Ryujinx/Ui/Windows/ControllerWindow.glade similarity index 100% rename from Ryujinx/Ui/Windows/ControllerWindow.glade rename to src/Ryujinx/Ui/Windows/ControllerWindow.glade diff --git a/Ryujinx/Ui/Windows/DlcWindow.cs b/src/Ryujinx/Ui/Windows/DlcWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/DlcWindow.cs rename to src/Ryujinx/Ui/Windows/DlcWindow.cs diff --git a/Ryujinx/Ui/Windows/DlcWindow.glade b/src/Ryujinx/Ui/Windows/DlcWindow.glade similarity index 100% rename from Ryujinx/Ui/Windows/DlcWindow.glade rename to src/Ryujinx/Ui/Windows/DlcWindow.glade diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/src/Ryujinx/Ui/Windows/SettingsWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/SettingsWindow.cs rename to src/Ryujinx/Ui/Windows/SettingsWindow.cs diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/src/Ryujinx/Ui/Windows/SettingsWindow.glade similarity index 100% rename from Ryujinx/Ui/Windows/SettingsWindow.glade rename to src/Ryujinx/Ui/Windows/SettingsWindow.glade diff --git a/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/TitleUpdateWindow.cs rename to src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs diff --git a/Ryujinx/Ui/Windows/TitleUpdateWindow.glade b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.glade similarity index 100% rename from Ryujinx/Ui/Windows/TitleUpdateWindow.glade rename to src/Ryujinx/Ui/Windows/TitleUpdateWindow.glade diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs b/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs similarity index 100% rename from Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs rename to src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs similarity index 100% rename from Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs rename to src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs diff --git a/Spv.Generator/Autogenerated/CoreGrammar.cs b/src/Spv.Generator/Autogenerated/CoreGrammar.cs similarity index 100% rename from Spv.Generator/Autogenerated/CoreGrammar.cs rename to src/Spv.Generator/Autogenerated/CoreGrammar.cs diff --git a/Spv.Generator/Autogenerated/GlslStd450Grammar.cs b/src/Spv.Generator/Autogenerated/GlslStd450Grammar.cs similarity index 100% rename from Spv.Generator/Autogenerated/GlslStd450Grammar.cs rename to src/Spv.Generator/Autogenerated/GlslStd450Grammar.cs diff --git a/Spv.Generator/Autogenerated/OpenClGrammar.cs b/src/Spv.Generator/Autogenerated/OpenClGrammar.cs similarity index 100% rename from Spv.Generator/Autogenerated/OpenClGrammar.cs rename to src/Spv.Generator/Autogenerated/OpenClGrammar.cs diff --git a/Spv.Generator/ConstantKey.cs b/src/Spv.Generator/ConstantKey.cs similarity index 100% rename from Spv.Generator/ConstantKey.cs rename to src/Spv.Generator/ConstantKey.cs diff --git a/Spv.Generator/DeterministicHashCode.cs b/src/Spv.Generator/DeterministicHashCode.cs similarity index 100% rename from Spv.Generator/DeterministicHashCode.cs rename to src/Spv.Generator/DeterministicHashCode.cs diff --git a/Spv.Generator/DeterministicStringKey.cs b/src/Spv.Generator/DeterministicStringKey.cs similarity index 100% rename from Spv.Generator/DeterministicStringKey.cs rename to src/Spv.Generator/DeterministicStringKey.cs diff --git a/Spv.Generator/GeneratorPool.cs b/src/Spv.Generator/GeneratorPool.cs similarity index 100% rename from Spv.Generator/GeneratorPool.cs rename to src/Spv.Generator/GeneratorPool.cs diff --git a/Spv.Generator/Instruction.cs b/src/Spv.Generator/Instruction.cs similarity index 100% rename from Spv.Generator/Instruction.cs rename to src/Spv.Generator/Instruction.cs diff --git a/Spv.Generator/InstructionOperands.cs b/src/Spv.Generator/InstructionOperands.cs similarity index 100% rename from Spv.Generator/InstructionOperands.cs rename to src/Spv.Generator/InstructionOperands.cs diff --git a/Spv.Generator/LICENSE b/src/Spv.Generator/LICENSE similarity index 100% rename from Spv.Generator/LICENSE rename to src/Spv.Generator/LICENSE diff --git a/Spv.Generator/LiteralInteger.cs b/src/Spv.Generator/LiteralInteger.cs similarity index 100% rename from Spv.Generator/LiteralInteger.cs rename to src/Spv.Generator/LiteralInteger.cs diff --git a/Spv.Generator/LiteralString.cs b/src/Spv.Generator/LiteralString.cs similarity index 100% rename from Spv.Generator/LiteralString.cs rename to src/Spv.Generator/LiteralString.cs diff --git a/Spv.Generator/Module.cs b/src/Spv.Generator/Module.cs similarity index 100% rename from Spv.Generator/Module.cs rename to src/Spv.Generator/Module.cs diff --git a/Spv.Generator/Operand.cs b/src/Spv.Generator/Operand.cs similarity index 100% rename from Spv.Generator/Operand.cs rename to src/Spv.Generator/Operand.cs diff --git a/Spv.Generator/OperandType.cs b/src/Spv.Generator/OperandType.cs similarity index 100% rename from Spv.Generator/OperandType.cs rename to src/Spv.Generator/OperandType.cs diff --git a/Spv.Generator/Spv.Generator.csproj b/src/Spv.Generator/Spv.Generator.csproj similarity index 100% rename from Spv.Generator/Spv.Generator.csproj rename to src/Spv.Generator/Spv.Generator.csproj diff --git a/Spv.Generator/TypeDeclarationKey.cs b/src/Spv.Generator/TypeDeclarationKey.cs similarity index 100% rename from Spv.Generator/TypeDeclarationKey.cs rename to src/Spv.Generator/TypeDeclarationKey.cs diff --git a/Spv.Generator/spirv.cs b/src/Spv.Generator/spirv.cs similarity index 100% rename from Spv.Generator/spirv.cs rename to src/Spv.Generator/spirv.cs From 609abc8b9b3c05a63bef94c2133550b3c07f97b0 Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 26 Apr 2023 04:34:16 +0200 Subject: [PATCH 473/737] Rename Ryujinx.Memory.Tests to Ryujinx.Tests.Memory --- Ryujinx.sln | 2 +- .../MockVirtualMemoryManager.cs | 0 .../MultiRegionTrackingTests.cs | 0 .../Ryujinx.Tests.Memory.csproj} | 0 src/{Ryujinx.Memory.Tests => Ryujinx.Tests.Memory}/Tests.cs | 0 .../TrackingTests.cs | 0 src/Ryujinx.Tests/Ryujinx.Tests.csproj | 2 +- 7 files changed, 2 insertions(+), 2 deletions(-) rename src/{Ryujinx.Memory.Tests => Ryujinx.Tests.Memory}/MockVirtualMemoryManager.cs (100%) rename src/{Ryujinx.Memory.Tests => Ryujinx.Tests.Memory}/MultiRegionTrackingTests.cs (100%) rename src/{Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj => Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj} (100%) rename src/{Ryujinx.Memory.Tests => Ryujinx.Tests.Memory}/Tests.cs (100%) rename src/{Ryujinx.Memory.Tests => Ryujinx.Tests.Memory}/TrackingTests.cs (100%) diff --git a/Ryujinx.sln b/Ryujinx.sln index 49fdc9c93..f9b28c1ba 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -39,7 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory.Tests", "src\Ryujinx.Memory.Tests\Ryujinx.Memory.Tests.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory.Tests", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "src\Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}" EndProject diff --git a/src/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs similarity index 100% rename from src/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs rename to src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs diff --git a/src/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs similarity index 100% rename from src/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs rename to src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs diff --git a/src/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj b/src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj similarity index 100% rename from src/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj rename to src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj diff --git a/src/Ryujinx.Memory.Tests/Tests.cs b/src/Ryujinx.Tests.Memory/Tests.cs similarity index 100% rename from src/Ryujinx.Memory.Tests/Tests.cs rename to src/Ryujinx.Tests.Memory/Tests.cs diff --git a/src/Ryujinx.Memory.Tests/TrackingTests.cs b/src/Ryujinx.Tests.Memory/TrackingTests.cs similarity index 100% rename from src/Ryujinx.Memory.Tests/TrackingTests.cs rename to src/Ryujinx.Tests.Memory/TrackingTests.cs diff --git a/src/Ryujinx.Tests/Ryujinx.Tests.csproj b/src/Ryujinx.Tests/Ryujinx.Tests.csproj index 5976e998b..ab331ce58 100644 --- a/src/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/src/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -27,7 +27,7 @@ <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" /> <ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" /> <ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" /> - <ProjectReference Include="..\Ryujinx.Memory.Tests\Ryujinx.Memory.Tests.csproj" /> + <ProjectReference Include="..\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj" /> <ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" /> <ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" /> <ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" /> From 206e0882c2535efa2b061cb885664b812de60fea Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 26 Apr 2023 04:35:11 +0200 Subject: [PATCH 474/737] Adjust Ryujinx.Tests.Memory namespace --- src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs | 5 +++-- src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs | 5 +++-- src/Ryujinx.Tests.Memory/Tests.cs | 3 ++- src/Ryujinx.Tests.Memory/TrackingTests.cs | 3 ++- src/Ryujinx.Tests/Memory/PartialUnmaps.cs | 1 - 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs index ef81a4615..59dc1a525 100644 --- a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs +++ b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs @@ -1,8 +1,9 @@ -using Ryujinx.Memory.Range; +using Ryujinx.Memory; +using Ryujinx.Memory.Range; using System; using System.Collections.Generic; -namespace Ryujinx.Memory.Tests +namespace Ryujinx.Tests.Memory { public class MockVirtualMemoryManager : IVirtualMemoryManager { diff --git a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs index 38cb49216..674d23565 100644 --- a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs @@ -1,10 +1,11 @@ using NUnit.Framework; +using Ryujinx.Memory; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Memory.Tests +namespace Ryujinx.Tests.Memory { public class MultiRegionTrackingTests { @@ -436,4 +437,4 @@ namespace Ryujinx.Memory.Tests Assert.AreEqual(pagesModified, new bool[] { true, false, false }); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Tests.Memory/Tests.cs b/src/Ryujinx.Tests.Memory/Tests.cs index 2717b76ad..d8a243e3e 100644 --- a/src/Ryujinx.Tests.Memory/Tests.cs +++ b/src/Ryujinx.Tests.Memory/Tests.cs @@ -1,8 +1,9 @@ using NUnit.Framework; +using Ryujinx.Memory; using System; using System.Runtime.InteropServices; -namespace Ryujinx.Memory.Tests +namespace Ryujinx.Tests.Memory { public class Tests { diff --git a/src/Ryujinx.Tests.Memory/TrackingTests.cs b/src/Ryujinx.Tests.Memory/TrackingTests.cs index eb679804c..faf295575 100644 --- a/src/Ryujinx.Tests.Memory/TrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/TrackingTests.cs @@ -1,11 +1,12 @@ using NUnit.Framework; +using Ryujinx.Memory; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; -namespace Ryujinx.Memory.Tests +namespace Ryujinx.Tests.Memory { public class TrackingTests { diff --git a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs index b805969d3..c60afc2de 100644 --- a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs +++ b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs @@ -5,7 +5,6 @@ using Ryujinx.Common.Memory.PartialUnmaps; using Ryujinx.Cpu; using Ryujinx.Cpu.Jit; using Ryujinx.Memory; -using Ryujinx.Memory.Tests; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; From 250fc51374d39b4ea08d45b12d9674dd8b3451eb Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 26 Apr 2023 05:12:19 +0200 Subject: [PATCH 475/737] Adjust github workflows for new src directory --- .github/workflows/build.yml | 8 ++++---- .github/workflows/flatpak.yml | 4 ++-- .github/workflows/release.yml | 26 +++++++++++++------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ac19f717a..843dd492a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,13 +61,13 @@ jobs: - name: Test run: dotnet test --no-build -c "${{ matrix.configuration }}" - name: Publish Ryujinx - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained true + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true if: github.event_name == 'pull_request' - name: Publish Ryujinx.Headless.SDL2 - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained true + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true if: github.event_name == 'pull_request' - name: Publish Ryujinx.Ava - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true if: github.event_name == 'pull_request' - name: Upload Ryujinx artifact uses: actions/upload-artifact@v3 @@ -86,4 +86,4 @@ jobs: with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_ava - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' \ No newline at end of file diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index ee0cc6f92..86a80eabf 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -18,7 +18,7 @@ jobs: NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages GIT_COMMITTER_NAME: "RyujinxBot" GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com" - RYUJINX_PROJECT_FILE: "Ryujinx/Ryujinx.csproj" + RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj" NUGET_SOURCES_DESTDIR: "nuget-sources" RYUJINX_VERSION: "${{ inputs.ryujinx_version }}" @@ -168,4 +168,4 @@ jobs: git config user.email "${{ env.GIT_COMMITTER_EMAIL }}" git add . git commit -m "$COMMIT_MESSAGE" - git push origin master + git push origin master \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 88eb55274..914e5e33e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,19 +37,19 @@ jobs: shell: bash - name: Configure for release run: | - sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs shell: bash - name: Create output dir run: "mkdir release_output" - name: Publish Windows run: | - dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true - dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true - dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true + dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true + dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true + dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true - name: Packing Windows builds run: | pushd publish_windows @@ -67,9 +67,9 @@ jobs: - name: Publish Linux run: | - dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true - dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true - dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true + dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true + dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true + dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true - name: Packing Linux builds run: | @@ -104,7 +104,7 @@ jobs: name: ${{ steps.version_info.outputs.build_version }} artifacts: "release_output/*.tar.gz,release_output/*.zip" tag: ${{ steps.version_info.outputs.build_version }} - body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." + body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." allowUpdates: true removeArtifacts: true replacesArtifacts: true @@ -128,4 +128,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit + secrets: inherit \ No newline at end of file From 12504f280c1ab51b208d0eff8a60594e208315a2 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:14:44 +0200 Subject: [PATCH 476/737] Fix paths and typos for macOS scripts (#4738) * Fix paths and typos for macOS scripts * Update outdated comments about rcodesign --------- Co-authored-by: Mary <thog@protonmail.com> --- distribution/macos/create_app_bundle.sh | 6 ++---- distribution/macos/create_macos_release.sh | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh index 0886bc244..858c06f59 100755 --- a/distribution/macos/create_app_bundle.sh +++ b/distribution/macos/create_app_bundle.sh @@ -41,12 +41,10 @@ then exit 1 fi - # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. - # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" + # cargo install apple-codesign echo "Usign rcodesign for ad-hoc signing" rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY" else echo "Usign codesign for ad-hoc signing" codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY" -fi - +fi \ No newline at end of file diff --git a/distribution/macos/create_macos_release.sh b/distribution/macos/create_macos_release.sh index be56c135e..59eb1efd2 100755 --- a/distribution/macos/create_macos_release.sh +++ b/distribution/macos/create_macos_release.sh @@ -30,14 +30,14 @@ mkdir -p "$TEMP_DIRECTORY" DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true" dotnet restore -dotnet build -c Release Ryujinx.Ava -dotnet publish -c Release -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS Ryujinx.Ava -dotnet publish -c Release -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $DOTNET_COMMON_ARGS Ryujinx.Ava +dotnet build -c Release src/Ryujinx.Ava +dotnet publish -c Release -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava +dotnet publish -c Release -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava -# Get ride of the support library for ARMeilleur for x64 (that's only for arm64) +# Get rid of the support library for ARMeilleure for x64 (that's only for arm64) rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib" -# Get ride of libsoundio from arm64 builds as we don't have a arm64 variant +# Get rid of libsoundio from arm64 builds as we don't have a arm64 variant # TODO: remove this once done rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib" @@ -102,4 +102,4 @@ gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz rm $RELEASE_TAR_FILE_NAME popd -echo "Done" +echo "Done" \ No newline at end of file From 3b4ff2d6d909dc0b74f4befe391ec537cd963ec0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 11:54:19 +0200 Subject: [PATCH 477/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.29.0 to 6.30.0 (#4736) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.29.0 to 6.30.0. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.29.0...6.30.0) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index cbc67f073..4759c9ba5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> - <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.29.0" /> + <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.1" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> From 21c4176157a1c7ef3086333ddb5036325869eac3 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Fri, 28 Apr 2023 21:59:53 +0100 Subject: [PATCH 478/737] Allow window to remember its size, position and state (GTK + Avalonia) (#4657) * Update ConfigurationState.cs * Update ConfigurationFileFormat.cs * Update MainWindow.cs * Update ConfigurationFileFormat.cs * Update ConfigurationState.cs * Update MainWindow.cs * Update MainWindow.cs * Update Ryujinx.Ui.Common/Configuration/ConfigurationState.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Update MainWindow.cs * Update Ryujinx/Ui/MainWindow.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Initial properties * Viewmodel adjustments and additions * abstract and monitor dimension changes * Remove position from ViewModel and simplify methods * Remove unused dep * Update configuration and fix typo from AA * review changes * Review changes * Screensize checks - Ava * Review changes 2 * basic review changes * Standardise GTK/Ava functions * Actually call function --------- Co-authored-by: HaizenTrist <123991082+HaizenTrist@users.noreply.github.com> Co-authored-by: gdkchan <gab.dark.100@gmail.com> --- .../UI/ViewModels/MainWindowViewModel.cs | 25 +++++++ src/Ryujinx.Ava/UI/Windows/MainWindow.axaml | 5 +- .../UI/Windows/MainWindow.axaml.cs | 49 +++++++++++++ .../Configuration/ConfigurationFileFormat.cs | 7 +- .../Configuration/ConfigurationState.cs | 69 +++++++++++++++++-- .../Configuration/Ui/WindowStartup.cs | 11 +++ src/Ryujinx/Ui/MainWindow.cs | 38 ++++++++-- 7 files changed, 189 insertions(+), 15 deletions(-) create mode 100644 src/Ryujinx.Ui.Common/Configuration/Ui/WindowStartup.cs diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 14d7a0fe4..f4556bc3c 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -95,6 +95,9 @@ namespace Ryujinx.Ava.UI.ViewModels private string _currentEmulatedGamePath; private AutoResetEvent _rendererWaitEvent; private WindowState _windowState; + private double _windowWidth; + private double _windowHeight; + private bool _isActive; public ApplicationData ListSelectedApplication; @@ -622,6 +625,28 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); } } + + public double WindowWidth + { + get => _windowWidth; + set + { + _windowWidth = value; + + OnPropertyChanged(); + } + } + + public double WindowHeight + { + get => _windowHeight; + set + { + _windowHeight = value; + + OnPropertyChanged(); + } + } public bool IsGrid => Glyph == Glyph.Grid; public bool IsList => Glyph == Glyph.List; diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml index 08b99cf53..de01f90fb 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml @@ -12,15 +12,14 @@ Cursor="{Binding Cursor}" Title="{Binding Title}" WindowState="{Binding WindowState}" - Width="1280" - Height="777" + Width="{Binding WindowWidth}" + Height="{Binding WindowHeight}" MinWidth="1092" MinHeight="672" d:DesignHeight="720" d:DesignWidth="1280" x:CompileBindings="True" x:DataType="viewModels:MainWindowViewModel" - WindowStartupLocation="CenterScreen" mc:Ignorable="d" Focusable="True"> <Window.Styles> diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index 81e055063..404d4ee05 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -62,6 +62,8 @@ namespace Ryujinx.Ava.UI.Windows DataContext = ViewModel; + SetWindowSizePosition(); + InitializeComponent(); Load(); @@ -297,6 +299,51 @@ namespace Ryujinx.Ava.UI.Windows LoadHotKeys(); } + private void SetWindowSizePosition() + { + PixelPoint SavedPoint = new PixelPoint(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, + ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); + + ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor; + ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor; + + ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal; + + if (CheckScreenBounds(SavedPoint)) + { + Position = SavedPoint; + } + + else WindowStartupLocation = WindowStartupLocation.CenterScreen; + } + + private bool CheckScreenBounds(PixelPoint configPoint) + { + for (int i = 0; i < Screens.ScreenCount; i++) + { + if (Screens.All[i].Bounds.Contains(configPoint)) + { + return true; + } + } + + Logger.Warning?.Print(LogClass.Application, $"Failed to find valid start-up coordinates. Defaulting to primary monitor center."); + return false; + } + + private void SaveWindowSizePosition() + { + ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight.Value = (int)Height; + ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth.Value = (int)Width; + + ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX.Value = Position.X; + ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY.Value = Position.Y; + + ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized; + + MainWindowViewModel.SaveConfig(); + } + protected override void OnOpened(EventArgs e) { base.OnOpened(e); @@ -388,6 +435,8 @@ namespace Ryujinx.Ava.UI.Windows return; } + SaveWindowSizePosition(); + ApplicationLibrary.CancelLoading(); InputManager.Dispose(); Program.Exit(); diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index c9e7f80e8..3168766b5 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <summary> /// The current version of the file format /// </summary> - public const int CurrentVersion = 46; + public const int CurrentVersion = 47; /// <summary> /// Version of the configuration file format @@ -251,6 +251,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public ShownFileTypes ShownFileTypes { get; set; } + /// <summary> + /// Main window start-up position, size and state + /// </summary> + public WindowStartup WindowStartup { get; set; } + /// <summary> /// Language Code for the UI /// </summary> diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index 3a68cc265..fc3693ae8 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -83,6 +83,27 @@ namespace Ryujinx.Ui.Common.Configuration } } + // <summary> + /// Determines main window start-up position, size and state + ///<summary> + public class WindowStartupSettings + { + public ReactiveObject<int> WindowSizeWidth { get; private set; } + public ReactiveObject<int> WindowSizeHeight { get; private set; } + public ReactiveObject<int> WindowPositionX { get; private set; } + public ReactiveObject<int> WindowPositionY { get; private set; } + public ReactiveObject<bool> WindowMaximized { get; private set; } + + public WindowStartupSettings() + { + WindowSizeWidth = new ReactiveObject<int>(); + WindowSizeHeight = new ReactiveObject<int>(); + WindowPositionX = new ReactiveObject<int>(); + WindowPositionY = new ReactiveObject<int>(); + WindowMaximized = new ReactiveObject<bool>(); + } + } + /// <summary> /// Used to toggle columns in the GUI /// </summary> @@ -103,6 +124,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public ShownFileTypeSettings ShownFileTypes { get; private set; } + /// <summary> + /// Determines main window start-up position, size and state + /// </summary> + public WindowStartupSettings WindowStartup { get; private set; } + /// <summary> /// Language Code for the UI /// </summary> @@ -163,7 +189,8 @@ namespace Ryujinx.Ui.Common.Configuration GuiColumns = new Columns(); ColumnSort = new ColumnSortSettings(); GameDirs = new ReactiveObject<List<string>>(); - ShownFileTypes = new ShownFileTypeSettings(); + ShownFileTypes = new ShownFileTypeSettings(); + WindowStartup = new WindowStartupSettings(); EnableCustomTheme = new ReactiveObject<bool>(); CustomThemePath = new ReactiveObject<string>(); BaseStyle = new ReactiveObject<string>(); @@ -663,12 +690,12 @@ namespace Ryujinx.Ui.Common.Configuration FileSizeColumn = Ui.GuiColumns.FileSizeColumn, PathColumn = Ui.GuiColumns.PathColumn }, - ColumnSort = new ColumnSort + ColumnSort = new ColumnSort { SortColumnId = Ui.ColumnSort.SortColumnId, SortAscending = Ui.ColumnSort.SortAscending }, - GameDirs = Ui.GameDirs, + GameDirs = Ui.GameDirs, ShownFileTypes = new ShownFileTypes { NSP = Ui.ShownFileTypes.NSP, @@ -678,6 +705,14 @@ namespace Ryujinx.Ui.Common.Configuration NRO = Ui.ShownFileTypes.NRO, NSO = Ui.ShownFileTypes.NSO, }, + WindowStartup = new WindowStartup + { + WindowSizeWidth = Ui.WindowStartup.WindowSizeWidth, + WindowSizeHeight = Ui.WindowStartup.WindowSizeHeight, + WindowPositionX = Ui.WindowStartup.WindowPositionX, + WindowPositionY = Ui.WindowStartup.WindowPositionY, + WindowMaximized = Ui.WindowStartup.WindowMaximized, + }, LanguageCode = Ui.LanguageCode, EnableCustomTheme = Ui.EnableCustomTheme, CustomThemePath = Ui.CustomThemePath, @@ -781,6 +816,11 @@ namespace Ryujinx.Ui.Common.Configuration Ui.IsAscendingOrder.Value = true; Ui.StartFullscreen.Value = false; Ui.ShowConsole.Value = true; + Ui.WindowStartup.WindowSizeWidth.Value = 1280; + Ui.WindowStartup.WindowSizeHeight.Value = 760; + Ui.WindowStartup.WindowPositionX.Value = 0; + Ui.WindowStartup.WindowPositionY.Value = 0; + Ui.WindowStartup.WindowMaximized.Value = false; Hid.EnableKeyboard.Value = false; Hid.EnableMouse.Value = false; Hid.Hotkeys.Value = new KeyboardHotkeys @@ -1334,13 +1374,29 @@ namespace Ryujinx.Ui.Common.Configuration if (configurationFileFormat.Version < 46) { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 45."); + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 46."); configurationFileFormat.MultiplayerLanInterfaceId = "0"; configurationFileUpdated = true; } + if (configurationFileFormat.Version < 47) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 47."); + + configurationFileFormat.WindowStartup = new WindowStartup + { + WindowPositionX = 0, + WindowPositionY = 0, + WindowSizeHeight = 760, + WindowSizeWidth = 1280, + WindowMaximized = false, + }; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1416,6 +1472,11 @@ namespace Ryujinx.Ui.Common.Configuration Ui.ApplicationSort.Value = configurationFileFormat.ApplicationSort; Ui.StartFullscreen.Value = configurationFileFormat.StartFullscreen; Ui.ShowConsole.Value = configurationFileFormat.ShowConsole; + Ui.WindowStartup.WindowSizeWidth.Value = configurationFileFormat.WindowStartup.WindowSizeWidth; + Ui.WindowStartup.WindowSizeHeight.Value = configurationFileFormat.WindowStartup.WindowSizeHeight; + Ui.WindowStartup.WindowPositionX.Value = configurationFileFormat.WindowStartup.WindowPositionX; + Ui.WindowStartup.WindowPositionY.Value = configurationFileFormat.WindowStartup.WindowPositionY; + Ui.WindowStartup.WindowMaximized.Value = configurationFileFormat.WindowStartup.WindowMaximized; Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard; Hid.EnableMouse.Value = configurationFileFormat.EnableMouse; Hid.Hotkeys.Value = configurationFileFormat.Hotkeys; diff --git a/src/Ryujinx.Ui.Common/Configuration/Ui/WindowStartup.cs b/src/Ryujinx.Ui.Common/Configuration/Ui/WindowStartup.cs new file mode 100644 index 000000000..ce0dde6aa --- /dev/null +++ b/src/Ryujinx.Ui.Common/Configuration/Ui/WindowStartup.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Ui.Common.Configuration.Ui +{ + public struct WindowStartup + { + public int WindowSizeWidth { get; set; } + public int WindowSizeHeight { get; set; } + public int WindowPositionX { get; set; } + public int WindowPositionY { get; set; } + public bool WindowMaximized { get; set; } + } +} diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index bf96e18a4..4911c9006 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -153,13 +153,8 @@ namespace Ryujinx.Ui // Apply custom theme if needed. ThemeHelper.ApplyTheme(); - Gdk.Monitor monitor = Display.GetMonitor(0); - // Sets overridden fields. - int monitorWidth = monitor.Geometry.Width * monitor.ScaleFactor; - int monitorHeight = monitor.Geometry.Height * monitor.ScaleFactor; - DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280; - DefaultHeight = monitorHeight < 760 ? monitorHeight : 760; + SetWindowSizePosition(); Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Title = $"Ryujinx {Program.Version}"; @@ -1314,6 +1309,7 @@ namespace Ryujinx.Ui { if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) { + SaveWindowSizePosition(); End(); } } @@ -1322,6 +1318,7 @@ namespace Ryujinx.Ui { if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) { + SaveWindowSizePosition(); End(); } else @@ -1330,6 +1327,33 @@ namespace Ryujinx.Ui } } + private void SetWindowSizePosition() + { + DefaultWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth; + DefaultHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight; + + Move(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); + + if (ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized) + { + Maximize(); + } + } + + private void SaveWindowSizePosition() + { + GetSize(out int windowWidth, out int windowHeight); + GetPosition(out int windowXPos, out int windowYPos); + + ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value = IsMaximized; + ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth.Value = windowWidth; + ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight.Value = windowHeight; + ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX.Value = windowXPos; + ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY.Value = windowYPos; + + SaveConfig(); + } + private void StopEmulation_Pressed(object sender, EventArgs args) { if (_emulationContext != null) @@ -1831,4 +1855,4 @@ namespace Ryujinx.Ui UpdateGameTable(); } } -} \ No newline at end of file +} From 680e54802234c37b9e46633db328b3ecd1dd1f86 Mon Sep 17 00:00:00 2001 From: al81-ru <62964318+al81-ru@users.noreply.github.com> Date: Sat, 29 Apr 2023 23:54:41 +0300 Subject: [PATCH 479/737] Uneven frame pacing with vsync (#4744) fixes issue #3906 --- src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs index cd29a9da0..09bcdec11 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs @@ -252,6 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo public void Interrupt() { _interrupt = true; + _event.Set(); } /// <summary> From 36f10df775cf0c678548b97346432095823dfd8a Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 1 May 2023 19:32:32 +0100 Subject: [PATCH 480/737] GPU: Fix errors handling texture remapping (#4745) * GPU: Fix errors handling texture remapping - Fixes an error where a pool entry and memory mapping changing at the same time could cause a texture to rebind its data from the wrong GPU VA (data swaps) - Fixes an error where the texture pool could act on a mapping change before the mapping has actually been changed ("Unmapped" event happens before change, we need to signal it changed _after_ it completes) TODO: remove textures from partially mapped list... if they aren't. * Add Remap actions for handling post-mapping behaviours * Remove unused code. * Address feedback * Nit --- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 4 +- .../Image/TextureCache.cs | 79 ++++++++++++++++--- src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 10 ++- .../Memory/MemoryManager.cs | 26 +++++- .../Memory/UnmapEventArgs.cs | 12 ++- src/Ryujinx.Memory/Range/MemoryRange.cs | 14 ++++ src/Ryujinx.Memory/Range/MultiRange.cs | 9 +++ 7 files changed, 135 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 84808a84d..f0df55e68 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1610,6 +1610,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public void UpdatePoolMappings() { + ChangedMapping = true; + lock (_poolOwners) { ulong address = 0; @@ -1685,8 +1687,6 @@ namespace Ryujinx.Graphics.Gpu.Image { Group.ClearModified(unmapRange); } - - UpdatePoolMappings(); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index c3243cf23..97d78a349 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> - /// Handles removal of textures written to a memory region being unmapped. + /// Handles marking of textures written to a memory region being (partially) remapped. /// </summary> /// <param name="sender">Sender object</param> /// <param name="e">Event arguments</param> @@ -80,26 +80,41 @@ namespace Ryujinx.Graphics.Gpu.Image overlapCount = _textures.FindOverlaps(unmapped, ref overlaps); } - for (int i = 0; i < overlapCount; i++) + if (overlapCount > 0) { - overlaps[i].Unmapped(unmapped); + for (int i = 0; i < overlapCount; i++) + { + overlaps[i].Unmapped(unmapped); + } } - // If any range was previously unmapped, we also need to purge - // all partially mapped texture, as they might be fully mapped now. - for (int i = 0; i < unmapped.Count; i++) + lock (_partiallyMappedTextures) { - if (unmapped.GetSubRange(i).Address == MemoryManager.PteUnmapped) + if (overlapCount > 0 || _partiallyMappedTextures.Count > 0) { - lock (_partiallyMappedTextures) + e.AddRemapAction(() => { - foreach (var texture in _partiallyMappedTextures) + lock (_partiallyMappedTextures) { - texture.Unmapped(unmapped); - } - } + if (overlapCount > 0) + { + for (int i = 0; i < overlapCount; i++) + { + _partiallyMappedTextures.Add(overlaps[i]); + } + } - break; + // Any texture that has been unmapped at any point or is partially unmapped + // should update their pool references after the remap completes. + + MultiRange unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size); + + foreach (var texture in _partiallyMappedTextures) + { + texture.UpdatePoolMappings(); + } + } + }); } } } @@ -1135,6 +1150,44 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Queries a texture's memory range and marks it as partially mapped or not. + /// Partially mapped textures re-evaluate their memory range after each time GPU memory is mapped. + /// </summary> + /// <param name="memoryManager">GPU memory manager where the texture is mapped</param> + /// <param name="address">The virtual address of the texture</param> + /// <param name="texture">The texture to be marked</param> + /// <returns>The physical regions for the texture, found when evaluating whether the texture was partially mapped</returns> + public MultiRange UpdatePartiallyMapped(MemoryManager memoryManager, ulong address, Texture texture) + { + MultiRange range; + lock (_partiallyMappedTextures) + { + range = memoryManager.GetPhysicalRegions(address, texture.Size); + bool partiallyMapped = false; + + for (int i = 0; i < range.Count; i++) + { + if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped) + { + partiallyMapped = true; + break; + } + } + + if (partiallyMapped) + { + _partiallyMappedTextures.Add(texture); + } + else + { + _partiallyMappedTextures.Remove(texture); + } + } + + return range; + } + /// <summary> /// Adds a texture to the short duration cache. This typically keeps it alive for two ticks. /// </summary> diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 5277e7899..dbcb2e75e 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -272,7 +272,15 @@ namespace Ryujinx.Graphics.Gpu.Image ulong address = descriptor.UnpackAddress(); - MultiRange range = _channel.MemoryManager.GetPhysicalRegions(address, texture.Size); + if (!descriptor.Equals(ref DescriptorCache[request.ID])) + { + // If the pool entry has already been replaced, just remove the texture. + + texture.DecrementReferenceCount(); + continue; + } + + MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture); // If the texture is not mapped at all, delete its reference. diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index b0f7e7992..0d4a41f02 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -365,6 +365,22 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// <summary> + /// Runs remap actions that are added to an unmap event. + /// These must run after the mapping completes. + /// </summary> + /// <param name="e">Event with remap actions</param> + private void RunRemapActions(UnmapEventArgs e) + { + if (e.RemapActions != null) + { + foreach (Action action in e.RemapActions) + { + action(); + } + } + } + /// <summary> /// Maps a given range of pages to the specified CPU virtual address. /// </summary> @@ -379,12 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Memory { lock (_pageTable) { - MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size)); + UnmapEventArgs e = new(va, size); + MemoryUnmapped?.Invoke(this, e); for (ulong offset = 0; offset < size; offset += PageSize) { SetPte(va + offset, PackPte(pa + offset, kind)); } + + RunRemapActions(e); } } @@ -398,12 +417,15 @@ namespace Ryujinx.Graphics.Gpu.Memory lock (_pageTable) { // Event handlers are not expected to be thread safe. - MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size)); + UnmapEventArgs e = new(va, size); + MemoryUnmapped?.Invoke(this, e); for (ulong offset = 0; offset < size; offset += PageSize) { SetPte(va + offset, PteUnmapped); } + + RunRemapActions(e); } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs b/src/Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs index 305747f85..837b5aab4 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/UnmapEventArgs.cs @@ -1,14 +1,24 @@ -namespace Ryujinx.Graphics.Gpu.Memory +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Memory { public class UnmapEventArgs { public ulong Address { get; } public ulong Size { get; } + public List<Action> RemapActions { get; private set; } public UnmapEventArgs(ulong address, ulong size) { Address = address; Size = size; } + + public void AddRemapAction(Action action) + { + RemapActions ??= new List<Action>(); + RemapActions.Add(action); + } } } diff --git a/src/Ryujinx.Memory/Range/MemoryRange.cs b/src/Ryujinx.Memory/Range/MemoryRange.cs index 7465fbcb3..c7ee6db22 100644 --- a/src/Ryujinx.Memory/Range/MemoryRange.cs +++ b/src/Ryujinx.Memory/Range/MemoryRange.cs @@ -57,5 +57,19 @@ return thisAddress < otherEndAddress && otherAddress < thisEndAddress; } + + /// <summary> + /// Returns a string summary of the memory range. + /// </summary> + /// <returns>A string summary of the memory range</returns> + public override string ToString() + { + if (Address == ulong.MaxValue) + { + return $"[Unmapped 0x{Size:X}]"; + } + + return $"[0x{Address:X}, 0x{EndAddress:X})"; + } } } diff --git a/src/Ryujinx.Memory/Range/MultiRange.cs b/src/Ryujinx.Memory/Range/MultiRange.cs index 9dbd76eca..42ef24be6 100644 --- a/src/Ryujinx.Memory/Range/MultiRange.cs +++ b/src/Ryujinx.Memory/Range/MultiRange.cs @@ -319,5 +319,14 @@ namespace Ryujinx.Memory.Range return hash.ToHashCode(); } + + /// <summary> + /// Returns a string summary of the ranges contained in the MultiRange. + /// </summary> + /// <returns>A string summary of the ranges contained within</returns> + public override string ToString() + { + return HasSingleRange ? _singleRange.ToString() : string.Join(", ", _ranges); + } } } From e18d258fa09379f31ca4310fbbe9e1869581d49f Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 1 May 2023 20:05:12 +0100 Subject: [PATCH 481/737] GPU: Pre-emptively flush textures that are flushed often (to imported memory when available) (#4711) * WIP texture pre-flush Improve performance of TextureView GetData to buffer Fix copy/sync ordering Fix minor bug Make this actually work WIP host mapping stuff * Fix usage flags * message * Cleanup 1 * Fix rebase * Fix * Improve pre-flush rules * Fix pre-flush * A lot of cleanup * Use the host memory bits * Select the correct memory type * Cleanup TextureGroupHandle * Missing comment * Remove debugging logs * Revert BufferHandle _value access modifier * One interrupt action at a time. * Support D32S8 to D24S8 conversion, safeguards * Interrupt cannot happen in sync handle's lock Waitable needs to be checked twice now, but this should stop it from deadlocking. * Remove unused using * Address some feedback * Address feedback * Address more feedback * Address more feedback * Improve sync rules Should allow for faster sync in some cases. --- src/Ryujinx.Graphics.GAL/BufferAccess.cs | 8 + src/Ryujinx.Graphics.GAL/IRenderer.cs | 3 + src/Ryujinx.Graphics.GAL/ITexture.cs | 1 + .../Multithreading/CommandHelper.cs | 3 + .../Multithreading/CommandType.cs | 3 + .../Renderer/CreateBufferAccessCommand.cs | 22 ++ .../Renderer/CreateHostBufferCommand.cs | 22 ++ .../Texture/TextureCopyToBufferCommand.cs | 29 +++ .../Resources/ThreadedTexture.cs | 6 + .../Multithreading/ThreadedRenderer.cs | 33 ++- .../Engine/GPFifo/GPFifoClass.cs | 7 +- .../Engine/Threed/ThreedClass.cs | 3 +- src/Ryujinx.Graphics.Gpu/GpuContext.cs | 40 ++-- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 4 +- .../Image/TextureGroup.cs | 110 +++++++++- .../Image/TextureGroupHandle.cs | 120 +++++++++- src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 12 +- .../Memory/PhysicalMemory.cs | 29 +++ .../Synchronization/HostSyncFlags.cs | 30 +++ .../Synchronization/ISyncActionHandler.cs | 22 ++ src/Ryujinx.Graphics.OpenGL/Buffer.cs | 20 +- .../Image/TextureBuffer.cs | 5 + .../Image/TextureView.cs | 21 ++ src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 30 ++- .../PersistentBuffers.cs | 29 ++- src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 30 ++- src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 68 +++++- src/Ryujinx.Graphics.Vulkan/EnumConversion.cs | 9 + .../HardwareCapabilities.cs | 3 + src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 88 ++++++++ .../HostMemoryAllocator.cs | 188 ++++++++++++++++ .../MemoryAllocation.cs | 24 +- .../MemoryAllocator.cs | 2 +- .../ConvertD32S8ToD24S8ShaderSource.comp | 58 +++++ .../Shaders/ShaderBinaries.cs | 207 ++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/SyncManager.cs | 31 +-- src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 5 + src/Ryujinx.Graphics.Vulkan/TextureView.cs | 60 ++++- .../VulkanInitialization.cs | 1 + src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 21 ++ 40 files changed, 1328 insertions(+), 79 deletions(-) create mode 100644 src/Ryujinx.Graphics.GAL/BufferAccess.cs create mode 100644 src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs create mode 100644 src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs create mode 100644 src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs create mode 100644 src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs create mode 100644 src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs create mode 100644 src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/ConvertD32S8ToD24S8ShaderSource.comp diff --git a/src/Ryujinx.Graphics.GAL/BufferAccess.cs b/src/Ryujinx.Graphics.GAL/BufferAccess.cs new file mode 100644 index 000000000..3a2d6c9b6 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/BufferAccess.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum BufferAccess + { + Default, + FlushPersistent + } +} diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs index 2af7b5db7..d36dd26b6 100644 --- a/src/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -21,11 +21,14 @@ namespace Ryujinx.Graphics.GAL { return CreateBuffer(size, BufferHandle.Null); } + BufferHandle CreateBuffer(nint pointer, int size); + BufferHandle CreateBuffer(int size, BufferAccess access); IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info); ISampler CreateSampler(SamplerCreateInfo info); ITexture CreateTexture(TextureCreateInfo info, float scale); + bool PrepareHostMapping(nint address, ulong size); void CreateSync(ulong id, bool strict); diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs index 792c863cb..2f9f5fbff 100644 --- a/src/Ryujinx.Graphics.GAL/ITexture.cs +++ b/src/Ryujinx.Graphics.GAL/ITexture.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.GAL void CopyTo(ITexture destination, int firstLayer, int firstLevel); void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel); void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter); + void CopyTo(BufferRange range, int layer, int level, int stride); ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs index 063b7edf9..9f6e483cd 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -43,6 +43,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register<ActionCommand>(CommandType.Action); Register<CreateBufferCommand>(CommandType.CreateBuffer); + Register<CreateBufferAccessCommand>(CommandType.CreateBufferAccess); + Register<CreateHostBufferCommand>(CommandType.CreateHostBuffer); Register<CreateProgramCommand>(CommandType.CreateProgram); Register<CreateSamplerCommand>(CommandType.CreateSampler); Register<CreateSyncCommand>(CommandType.CreateSync); @@ -69,6 +71,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register<TextureCopyToCommand>(CommandType.TextureCopyTo); Register<TextureCopyToScaledCommand>(CommandType.TextureCopyToScaled); Register<TextureCopyToSliceCommand>(CommandType.TextureCopyToSlice); + Register<TextureCopyToBufferCommand>(CommandType.TextureCopyToBuffer); Register<TextureCreateViewCommand>(CommandType.TextureCreateView); Register<TextureGetDataCommand>(CommandType.TextureGetData); Register<TextureGetDataSliceCommand>(CommandType.TextureGetDataSlice); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs index 61e729b44..8d9c1ec8d 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -4,6 +4,8 @@ { Action, CreateBuffer, + CreateBufferAccess, + CreateHostBuffer, CreateProgram, CreateSampler, CreateSync, @@ -29,6 +31,7 @@ SamplerDispose, TextureCopyTo, + TextureCopyToBuffer, TextureCopyToScaled, TextureCopyToSlice, TextureCreateView, diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs new file mode 100644 index 000000000..ece98b70f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateBufferAccessCommand : IGALCommand, IGALCommand<CreateBufferAccessCommand> + { + public CommandType CommandType => CommandType.CreateBufferAccess; + private BufferHandle _threadedHandle; + private int _size; + private BufferAccess _access; + + public void Set(BufferHandle threadedHandle, int size, BufferAccess access) + { + _threadedHandle = threadedHandle; + _size = size; + _access = access; + } + + public static void Run(ref CreateBufferAccessCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, command._access)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs new file mode 100644 index 000000000..e25f84687 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateHostBufferCommand : IGALCommand, IGALCommand<CreateHostBufferCommand> + { + public CommandType CommandType => CommandType.CreateHostBuffer; + private BufferHandle _threadedHandle; + private nint _pointer; + private int _size; + + public void Set(BufferHandle threadedHandle, nint pointer, int size) + { + _threadedHandle = threadedHandle; + _pointer = pointer; + _size = size; + } + + public static void Run(ref CreateHostBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._pointer, command._size)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs new file mode 100644 index 000000000..ac0e07d67 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs @@ -0,0 +1,29 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCopyToBufferCommand : IGALCommand, IGALCommand<TextureCopyToBufferCommand> + { + public CommandType CommandType => CommandType.TextureCopyToBuffer; + private TableRef<ThreadedTexture> _texture; + private BufferRange _range; + private int _layer; + private int _level; + private int _stride; + + public void Set(TableRef<ThreadedTexture> texture, BufferRange range, int layer, int level, int stride) + { + _texture = texture; + _range = range; + _layer = layer; + _level = level; + _stride = stride; + } + + public static void Run(ref TextureCopyToBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._texture.Get(threaded).Base.CopyTo(threaded.Buffers.MapBufferRange(command._range), command._layer, command._level, command._stride); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index ee1cfa29b..bb0b05fb8 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -108,6 +108,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } } + public void CopyTo(BufferRange range, int layer, int level, int stride) + { + _renderer.New<TextureCopyToBufferCommand>().Set(Ref(this), range, layer, level, stride); + _renderer.QueueCommand(); + } + public void SetData(SpanOrArray<byte> data) { _renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray())); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 2148f43f9..3e179621e 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading private int _refConsumerPtr; private Action _interruptAction; + private object _interruptLock = new(); public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; @@ -274,6 +275,24 @@ namespace Ryujinx.Graphics.GAL.Multithreading return handle; } + public BufferHandle CreateBuffer(nint pointer, int size) + { + BufferHandle handle = Buffers.CreateBufferHandle(); + New<CreateHostBufferCommand>().Set(handle, pointer, size); + QueueCommand(); + + return handle; + } + + public BufferHandle CreateBuffer(int size, BufferAccess access) + { + BufferHandle handle = Buffers.CreateBufferHandle(); + New<CreateBufferAccessCommand>().Set(handle, size, access); + QueueCommand(); + + return handle; + } + public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { var program = new ThreadedProgram(this); @@ -448,11 +467,14 @@ namespace Ryujinx.Graphics.GAL.Multithreading } else { - while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { } + lock (_interruptLock) + { + while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { } - _galWorkAvailable.Set(); + _galWorkAvailable.Set(); - _interruptRun.WaitOne(); + _interruptRun.WaitOne(); + } } } @@ -461,6 +483,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading // Threaded renderer ignores given interrupt action, as it provides its own to the child renderer. } + public bool PrepareHostMapping(nint address, ulong size) + { + return _baseRenderer.PrepareHostMapping(address, size); + } + public void Dispose() { // Dispose must happen from the render thread, after all commands have completed. diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs index e80d98a15..7a11c649c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Device; using Ryujinx.Graphics.Gpu.Engine.MME; +using Ryujinx.Graphics.Gpu.Synchronization; using System; using System.Collections.Generic; using System.Threading; @@ -59,7 +60,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo if (_createSyncPending) { _createSyncPending = false; - _context.CreateHostSyncIfNeeded(false, false); + _context.CreateHostSyncIfNeeded(HostSyncFlags.None); } } @@ -157,7 +158,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo } else if (operation == SyncpointbOperation.Incr) { - _context.CreateHostSyncIfNeeded(true, true); + _context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint); _context.Synchronization.IncrementSyncpoint(syncpointId); } @@ -184,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo { _context.Renderer.Pipeline.CommandBufferBarrier(); - _context.CreateHostSyncIfNeeded(false, true); + _context.CreateHostSyncIfNeeded(HostSyncFlags.Strict); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index caeee18e5..1386071cf 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; using Ryujinx.Graphics.Gpu.Engine.Threed.Blender; +using Ryujinx.Graphics.Gpu.Synchronization; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -254,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed uint syncpointId = (uint)argument & 0xFFFF; _context.AdvanceSequence(); - _context.CreateHostSyncIfNeeded(true, true); + _context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint); _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result. _context.Synchronization.IncrementSyncpoint(syncpointId); } diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index 917588632..bab62b952 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -60,14 +60,14 @@ namespace Ryujinx.Graphics.Gpu /// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>, /// and the SyncNumber will be incremented. /// </summary> - internal List<Action> SyncActions { get; } + internal List<ISyncActionHandler> SyncActions { get; } /// <summary> /// Actions to be performed when a CPU waiting syncpoint is triggered. /// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>, /// and the SyncNumber will be incremented. /// </summary> - internal List<Action> SyncpointActions { get; } + internal List<ISyncActionHandler> SyncpointActions { get; } /// <summary> /// Buffer migrations that are currently in-flight. These are checked whenever sync is created to determine if buffer migration @@ -114,8 +114,8 @@ namespace Ryujinx.Graphics.Gpu HostInitalized = new ManualResetEvent(false); - SyncActions = new List<Action>(); - SyncpointActions = new List<Action>(); + SyncActions = new List<ISyncActionHandler>(); + SyncpointActions = new List<ISyncActionHandler>(); BufferMigrations = new List<BufferMigration>(); DeferredActions = new Queue<Action>(); @@ -296,9 +296,9 @@ namespace Ryujinx.Graphics.Gpu /// Registers an action to be performed the next time a syncpoint is incremented. /// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented. /// </summary> - /// <param name="action">The action to be performed on sync object creation</param> + /// <param name="action">The resource with action to be performed on sync object creation</param> /// <param name="syncpointOnly">True if the sync action should only run when syncpoints are incremented</param> - public void RegisterSyncAction(Action action, bool syncpointOnly = false) + internal void RegisterSyncAction(ISyncActionHandler action, bool syncpointOnly = false) { if (syncpointOnly) { @@ -315,10 +315,13 @@ namespace Ryujinx.Graphics.Gpu /// Creates a host sync object if there are any pending sync actions. The actions will then be called. /// If no actions are present, a host sync object is not created. /// </summary> - /// <param name="syncpoint">True if host sync is being created by a syncpoint</param> - /// <param name="strict">True if the sync should signal as soon as possible</param> - public void CreateHostSyncIfNeeded(bool syncpoint, bool strict) + /// <param name="flags">Modifiers for how host sync should be created</param> + internal void CreateHostSyncIfNeeded(HostSyncFlags flags) { + bool syncpoint = flags.HasFlag(HostSyncFlags.Syncpoint); + bool strict = flags.HasFlag(HostSyncFlags.Strict); + bool force = flags.HasFlag(HostSyncFlags.Force); + if (BufferMigrations.Count > 0) { ulong currentSyncNumber = Renderer.GetCurrentSync(); @@ -336,24 +339,17 @@ namespace Ryujinx.Graphics.Gpu } } - if (_pendingSync || (syncpoint && SyncpointActions.Count > 0)) + if (force || _pendingSync || (syncpoint && SyncpointActions.Count > 0)) { Renderer.CreateSync(SyncNumber, strict); + SyncActions.ForEach(action => action.SyncPreAction(syncpoint)); + SyncpointActions.ForEach(action => action.SyncPreAction(syncpoint)); + SyncNumber++; - foreach (Action action in SyncActions) - { - action(); - } - - foreach (Action action in SyncpointActions) - { - action(); - } - - SyncActions.Clear(); - SyncpointActions.Clear(); + SyncActions.RemoveAll(action => action.SyncAction(syncpoint)); + SyncpointActions.RemoveAll(action => action.SyncAction(syncpoint)); } _pendingSync = false; diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index f0df55e68..3b2579889 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1423,7 +1423,7 @@ namespace Ryujinx.Graphics.Gpu.Image _scaledSetScore = Math.Max(0, _scaledSetScore - 1); } - if (_modifiedStale || Group.HasCopyDependencies) + if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer) { _modifiedStale = false; Group.SignalModifying(this, bound); @@ -1685,6 +1685,8 @@ namespace Ryujinx.Graphics.Gpu.Image if (Group.Storage == this) { + Group.Unmapped(); + Group.ClearModified(unmapRange); } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 234e7e8c7..14ab5d1e4 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -57,6 +57,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public bool HasCopyDependencies { get; set; } + /// <summary> + /// Indicates if the texture group has a pre-emptive flush buffer. + /// When one is present, the group must always be notified on unbind. + /// </summary> + public bool HasFlushBuffer => _flushBuffer != BufferHandle.Null; + /// <summary> /// Indicates if this texture has any incompatible overlaps alive. /// </summary> @@ -89,6 +95,10 @@ namespace Ryujinx.Graphics.Gpu.Image private bool _incompatibleOverlapsDirty = true; private bool _flushIncompatibleOverlaps; + private BufferHandle _flushBuffer; + private bool _flushBufferImported; + private bool _flushBufferInvalid; + /// <summary> /// Create a new texture group. /// </summary> @@ -464,8 +474,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// </remarks> /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param> /// <param name="sliceIndex">The index of the slice to flush</param> + /// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param> /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param> - private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, ITexture texture = null) + private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, bool inBuffer, ITexture texture = null) { (int layer, int level) = GetLayerLevelForView(sliceIndex); @@ -475,7 +486,16 @@ namespace Ryujinx.Graphics.Gpu.Image using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked); - Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture); + if (inBuffer) + { + using PinnedSpan<byte> data = _context.Renderer.GetBufferData(_flushBuffer, offset, size); + + Storage.ConvertFromHostCompatibleFormat(region.Memory.Span, data.Get(), level, true); + } + else + { + Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture); + } } /// <summary> @@ -484,12 +504,13 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param> /// <param name="sliceStart">The first slice to flush</param> /// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param> + /// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param> /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param> - private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, ITexture texture = null) + private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, bool inBuffer, ITexture texture = null) { for (int i = sliceStart; i < sliceEnd; i++) { - FlushTextureDataSliceToGuest(tracked, i, texture); + FlushTextureDataSliceToGuest(tracked, i, inBuffer, texture); } } @@ -520,7 +541,7 @@ namespace Ryujinx.Graphics.Gpu.Image { if (endSlice > startSlice) { - FlushSliceRange(tracked, startSlice, endSlice); + FlushSliceRange(tracked, startSlice, endSlice, false); flushed = true; } @@ -553,7 +574,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - FlushSliceRange(tracked, startSlice, endSlice); + FlushSliceRange(tracked, startSlice, endSlice, false); } flushed = true; @@ -565,6 +586,58 @@ namespace Ryujinx.Graphics.Gpu.Image return flushed; } + /// <summary> + /// Flush the texture data into a persistently mapped buffer. + /// If the buffer does not exist, this method will create it. + /// </summary> + /// <param name="handle">Handle of the texture group to flush slices of</param> + public void FlushIntoBuffer(TextureGroupHandle handle) + { + // Ensure that the buffer exists. + + if (_flushBufferInvalid && _flushBuffer != BufferHandle.Null) + { + _flushBufferInvalid = false; + _context.Renderer.DeleteBuffer(_flushBuffer); + _flushBuffer = BufferHandle.Null; + } + + if (_flushBuffer == BufferHandle.Null) + { + if (!TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities)) + { + return; + } + + bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel; + + var hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0; + + if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size)) + { + _flushBuffer = _context.Renderer.CreateBuffer(hostPointer, (int)Storage.Size); + _flushBufferImported = true; + } + else + { + _flushBuffer = _context.Renderer.CreateBuffer((int)Storage.Size, BufferAccess.FlushPersistent); + _flushBufferImported = false; + } + + Storage.BlacklistScale(); + } + + int sliceStart = handle.BaseSlice; + int sliceEnd = sliceStart + handle.SliceCount; + + for (int i = sliceStart; i < sliceEnd; i++) + { + (int layer, int level) = GetLayerLevelForView(i); + + Storage.GetFlushTexture().CopyTo(new BufferRange(_flushBuffer, _allOffsets[i], _sliceSizes[level]), layer, level, _flushBufferImported ? Storage.Info.Stride : 0); + } + } + /// <summary> /// Clears competing modified flags for all incompatible ranges, if they have possibly been modified. /// </summary> @@ -1570,10 +1643,7 @@ namespace Ryujinx.Graphics.Gpu.Image _context.Renderer.BackgroundContextAction(() => { - if (!isGpuThread) - { - handle.Sync(_context); - } + bool inBuffer = !isGpuThread && handle.Sync(_context); Storage.SignalModifiedDirty(); @@ -1585,13 +1655,24 @@ namespace Ryujinx.Graphics.Gpu.Image } } - if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities)) + if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities) && !(inBuffer && _flushBufferImported)) { - FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, Storage.GetFlushTexture()); + FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, inBuffer, Storage.GetFlushTexture()); } }); } + /// <summary> + /// Called if any part of the storage texture is unmapped. + /// </summary> + public void Unmapped() + { + if (_flushBufferImported) + { + _flushBufferInvalid = true; + } + } + /// <summary> /// Dispose this texture group, disposing all related memory tracking handles. /// </summary> @@ -1606,6 +1687,11 @@ namespace Ryujinx.Graphics.Gpu.Image { incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this); } + + if (_flushBuffer != BufferHandle.Null) + { + _context.Renderer.DeleteBuffer(_flushBuffer); + } } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index ebb4e9aeb..9f66744be 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -1,4 +1,5 @@ using Ryujinx.Cpu.Tracking; +using Ryujinx.Graphics.Gpu.Synchronization; using System; using System.Collections.Generic; using System.Linq; @@ -13,8 +14,14 @@ namespace Ryujinx.Graphics.Gpu.Image /// Also tracks copy dependencies for the handle - references to other handles that must be kept /// in sync with this one before use. /// </summary> - class TextureGroupHandle : IDisposable + class TextureGroupHandle : ISyncActionHandler, IDisposable { + private const int FlushBalanceIncrement = 6; + private const int FlushBalanceWriteCost = 1; + private const int FlushBalanceThreshold = 7; + private const int FlushBalanceMax = 60; + private const int FlushBalanceMin = -10; + private TextureGroup _group; private int _bindCount; private int _firstLevel; @@ -26,6 +33,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// The sync number last registered. /// </summary> private ulong _registeredSync; + private ulong _registeredBufferSync = ulong.MaxValue; + private ulong _registeredBufferGuestSync = ulong.MaxValue; /// <summary> /// The sync number when the texture was last modified by GPU. @@ -42,6 +51,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> private bool _syncActionRegistered; + /// <summary> + /// Determines the balance of synced writes to flushes. + /// Used to determine if the texture should always write data to a persistent buffer for flush. + /// </summary> + private int _flushBalance; + /// <summary> /// The byte offset from the start of the storage of this handle. /// </summary> @@ -132,6 +147,12 @@ namespace Ryujinx.Graphics.Gpu.Image } Handles = handles; + + if (group.Storage.Info.IsLinear) + { + // Linear textures are presumed to be used for readback initially. + _flushBalance = FlushBalanceThreshold + FlushBalanceIncrement; + } } /// <summary> @@ -159,6 +180,35 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Determine if the next sync will copy into the flush buffer. + /// </summary> + /// <returns>True if it will copy, false otherwise</returns> + private bool NextSyncCopies() + { + return _flushBalance - FlushBalanceWriteCost > FlushBalanceThreshold; + } + + /// <summary> + /// Alters the flush balance by the given value. Should increase significantly with each sync, decrease with each write. + /// A flush balance higher than the threshold will cause a texture to repeatedly copy to a flush buffer on each use. + /// </summary> + /// <param name="modifier">Value to add to the existing flush balance</param> + /// <returns>True if the new balance is over the threshold, false otherwise</returns> + private bool ModifyFlushBalance(int modifier) + { + int result; + int existingBalance; + do + { + existingBalance = _flushBalance; + result = Math.Max(FlushBalanceMin, Math.Min(FlushBalanceMax, existingBalance + modifier)); + } + while (Interlocked.CompareExchange(ref _flushBalance, result, existingBalance) != existingBalance); + + return result > FlushBalanceThreshold; + } + /// <summary> /// Adds a single texture view as an overlap if its range overlaps. /// </summary> @@ -204,7 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (!_syncActionRegistered) { _modifiedSync = context.SyncNumber; - context.RegisterSyncAction(SyncAction, true); + context.RegisterSyncAction(this, true); _syncActionRegistered = true; } @@ -241,6 +291,13 @@ namespace Ryujinx.Graphics.Gpu.Image { SignalModified(context); + if (!bound && _syncActionRegistered && NextSyncCopies()) + { + // On unbind, textures that flush often should immediately create sync so their result can be obtained as soon as possible. + + context.CreateHostSyncIfNeeded(HostSyncFlags.Force); + } + // Note: Bind count currently resets to 0 on inherit for safety, as the handle <-> view relationship can change. _bindCount = Math.Max(0, _bindCount + (bound ? 1 : -1)); } @@ -266,25 +323,35 @@ namespace Ryujinx.Graphics.Gpu.Image /// removing the modified flag if it was reached, or leaving it set if it has not yet been created. /// </summary> /// <param name="context">The GPU context used to wait for sync</param> - public void Sync(GpuContext context) + /// <returns>True if the texture data can be read from the flush buffer</returns> + public bool Sync(GpuContext context) { - ulong registeredSync = _registeredSync; - long diff = (long)(context.SyncNumber - registeredSync); + // Currently assumes the calling thread is a guest thread. + + bool inBuffer = _registeredBufferGuestSync != ulong.MaxValue; + ulong sync = inBuffer ? _registeredBufferGuestSync : _registeredSync; + + long diff = (long)(context.SyncNumber - sync); + + ModifyFlushBalance(FlushBalanceIncrement); if (diff > 0) { - context.Renderer.WaitSync(registeredSync); + context.Renderer.WaitSync(sync); - if ((long)(_modifiedSync - registeredSync) > 0) + if ((long)(_modifiedSync - sync) > 0) { // Flush the data in a previous state. Do not remove the modified flag - it will be removed to ignore following writes. - return; + return inBuffer; } Modified = false; + + return inBuffer; } // If the difference is <= 0, no data is not ready yet. Flush any data we can without waiting or removing modified flag. + return false; } /// <summary> @@ -296,15 +363,41 @@ namespace Ryujinx.Graphics.Gpu.Image Interlocked.Exchange(ref _actionRegistered, 0); } + /// <summary> + /// Action to perform before a sync number is registered after modification. + /// This action will copy the texture data to the flush buffer if this texture + /// flushes often enough, which is determined by the flush balance. + /// </summary> + /// <inheritdoc/> + public void SyncPreAction(bool syncpoint) + { + if (syncpoint || NextSyncCopies()) + { + if (ModifyFlushBalance(0) && _registeredBufferSync != _modifiedSync) + { + _group.FlushIntoBuffer(this); + _registeredBufferSync = _modifiedSync; + } + } + } + /// <summary> /// Action to perform when a sync number is registered after modification. /// This action will register a read tracking action on the memory tracking handle so that a flush from CPU can happen. /// </summary> - private void SyncAction() + /// <inheritdoc/> + public bool SyncAction(bool syncpoint) { // The storage will need to signal modified again to update the sync number in future. _group.Storage.SignalModifiedDirty(); + bool lastInBuffer = _registeredBufferSync == _modifiedSync; + + if (!lastInBuffer) + { + _registeredBufferSync = ulong.MaxValue; + } + lock (Overlaps) { foreach (Texture texture in Overlaps) @@ -314,6 +407,7 @@ namespace Ryujinx.Graphics.Gpu.Image } // Register region tracking for CPU? (again) + _registeredSync = _modifiedSync; _syncActionRegistered = false; @@ -321,6 +415,14 @@ namespace Ryujinx.Graphics.Gpu.Image { _group.RegisterAction(this); } + + if (syncpoint) + { + _registeredBufferGuestSync = _registeredBufferSync; + } + + // If the last modification is in the buffer, keep this sync action alive until it sees a syncpoint. + return syncpoint || !lastInBuffer; } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index f267dfda7..ef8c80746 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -1,5 +1,6 @@ using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; using System; @@ -11,7 +12,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <summary> /// Buffer, used to store vertex and index data, uniform and storage buffers, and others. /// </summary> - class Buffer : IRange, IDisposable + class Buffer : IRange, ISyncActionHandler, IDisposable { private const ulong GranularBufferThreshold = 4096; @@ -248,7 +249,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (!_syncActionRegistered) { - _context.RegisterSyncAction(SyncAction); + _context.RegisterSyncAction(this); _syncActionRegistered = true; } } @@ -267,7 +268,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Action to be performed when a syncpoint is reached after modification. /// This will register read/write tracking to flush the buffer from GPU when its memory is used. /// </summary> - private void SyncAction() + /// <inheritdoc/> + public bool SyncAction(bool syncpoint) { _syncActionRegistered = false; @@ -284,6 +286,8 @@ namespace Ryujinx.Graphics.Gpu.Memory _memoryTracking.RegisterAction(_externalFlushDelegate); SynchronizeMemory(Address, Size); } + + return true; } /// <summary> @@ -296,7 +300,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (from._syncActionRegistered && !_syncActionRegistered) { - _context.RegisterSyncAction(SyncAction); + _context.RegisterSyncAction(this); _syncActionRegistered = true; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index bd33383e5..b976667c3 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -8,6 +8,7 @@ using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Linq; using System.Threading; namespace Ryujinx.Graphics.Gpu.Memory @@ -82,6 +83,34 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// <summary> + /// Gets a host pointer for a given range of application memory. + /// If the memory region is not a single contiguous block, this method returns 0. + /// </summary> + /// <remarks> + /// Getting a host pointer is unsafe. It should be considered invalid immediately if the GPU memory is unmapped. + /// </remarks> + /// <param name="range">Ranges of physical memory where the target data is located</param> + /// <returns>Pointer to the range of memory</returns> + public nint GetHostPointer(MultiRange range) + { + if (range.Count == 1) + { + var singleRange = range.GetSubRange(0); + if (singleRange.Address != MemoryManager.PteUnmapped) + { + var regions = _cpuMemory.GetHostRegions(singleRange.Address, singleRange.Size); + + if (regions != null && regions.Count() == 1) + { + return (nint)regions.First().Address; + } + } + } + + return 0; + } + /// <summary> /// Gets a span of data from the application process. /// </summary> diff --git a/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs new file mode 100644 index 000000000..426669914 --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs @@ -0,0 +1,30 @@ +using System; + +namespace Ryujinx.Graphics.Gpu.Synchronization +{ + /// <summary> + /// Modifier flags for creating host sync. + /// </summary> + [Flags] + internal enum HostSyncFlags + { + None = 0, + + /// <summary> + /// Present if host sync is being created by a syncpoint. + /// </summary> + Syncpoint = 1 << 0, + + /// <summary> + /// Present if the sync should signal as soon as possible. + /// </summary> + Strict = 1 << 1, + + /// <summary> + /// Present will force the sync to be created, even if no actions are eligible. + /// </summary> + Force = 1 << 2, + + StrictSyncpoint = Strict | Syncpoint + } +} diff --git a/src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs new file mode 100644 index 000000000..d470d2f07 --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Gpu.Synchronization +{ + /// <summary> + /// This interface indicates that a class can be registered for a sync action. + /// </summary> + interface ISyncActionHandler + { + /// <summary> + /// Action to be performed when some synchronizing action is reached after modification. + /// Generally used to register read/write tracking to flush resources from GPU when their memory is used. + /// </summary> + /// <param name="syncpoint">True if the action is a guest syncpoint</param> + /// <returns>True if the action is to be removed, false otherwise</returns> + bool SyncAction(bool syncpoint); + + /// <summary> + /// Action to be performed immediately before sync is created. + /// </summary> + /// <param name="syncpoint">True if the action is a guest syncpoint</param> + void SyncPreAction(bool syncpoint) { } + } +} diff --git a/src/Ryujinx.Graphics.OpenGL/Buffer.cs b/src/Ryujinx.Graphics.OpenGL/Buffer.cs index af7d191a6..2a5143101 100644 --- a/src/Ryujinx.Graphics.OpenGL/Buffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -42,6 +42,20 @@ namespace Ryujinx.Graphics.OpenGL return Handle.FromInt32<BufferHandle>(handle); } + public static BufferHandle CreatePersistent(int size) + { + int handle = GL.GenBuffer(); + + GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); + GL.BufferStorage(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, + BufferStorageFlags.MapPersistentBit | + BufferStorageFlags.MapCoherentBit | + BufferStorageFlags.ClientStorageBit | + BufferStorageFlags.MapReadBit); + + return Handle.FromInt32<BufferHandle>(handle); + } + public static void Copy(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) { GL.BindBuffer(BufferTarget.CopyReadBuffer, source.ToInt32()); @@ -60,7 +74,11 @@ namespace Ryujinx.Graphics.OpenGL // Data in the persistent buffer and host array is guaranteed to be available // until the next time the host thread requests data. - if (HwCapabilities.UsePersistentBufferForFlush) + if (renderer.PersistentBuffers.TryGet(buffer, out IntPtr ptr)) + { + return new PinnedSpan<byte>(IntPtr.Add(ptr, offset).ToPointer(), size); + } + else if (HwCapabilities.UsePersistentBufferForFlush) { return PinnedSpan<byte>.UnsafeFromSpan(renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size)); } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index 1e9e4d6b2..116f70cc7 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -49,6 +49,11 @@ namespace Ryujinx.Graphics.OpenGL.Image return GetData(); } + public void CopyTo(BufferRange range, int layer, int level, int stride) + { + throw new NotImplementedException(); + } + public void SetData(SpanOrArray<byte> data) { var dataSpan = data.AsSpan(); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 804b3b03e..f24a58fc3 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -3,6 +3,7 @@ using Ryujinx.Common; using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using System; +using System.Diagnostics; namespace Ryujinx.Graphics.OpenGL.Image { @@ -287,6 +288,26 @@ namespace Ryujinx.Graphics.OpenGL.Image } } + public void CopyTo(BufferRange range, int layer, int level, int stride) + { + if (stride != 0 && stride != BitUtils.AlignUp(Info.Width * Info.BytesPerPixel, 4)) + { + throw new NotSupportedException("Stride conversion for texture copy to buffer not supported."); + } + + GL.BindBuffer(BufferTarget.PixelPackBuffer, range.Handle.ToInt32()); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + if (format.PixelFormat == PixelFormat.DepthStencil) + { + throw new InvalidOperationException("DepthStencil copy to buffer is not supported for layer/level > 0."); + } + + int offset = WriteToPbo2D(range.Offset, layer, level); + + Debug.Assert(offset == 0); + } + public void WriteToPbo(int offset, bool forceBgra) { WriteTo(IntPtr.Zero + offset, forceBgra); diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 3903b4d4e..7d5fe8931 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -58,10 +58,31 @@ namespace Ryujinx.Graphics.OpenGL } public BufferHandle CreateBuffer(int size, BufferHandle storageHint) + { + return CreateBuffer(size, GAL.BufferAccess.Default); + } + + public BufferHandle CreateBuffer(int size, GAL.BufferAccess access) { BufferCount++; - return Buffer.Create(size); + if (access == GAL.BufferAccess.FlushPersistent) + { + BufferHandle handle = Buffer.CreatePersistent(size); + + PersistentBuffers.Map(handle, size); + + return handle; + } + else + { + return Buffer.Create(size); + } + } + + public BufferHandle CreateBuffer(nint pointer, int size) + { + throw new NotSupportedException(); } public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) @@ -88,6 +109,8 @@ namespace Ryujinx.Graphics.OpenGL public void DeleteBuffer(BufferHandle buffer) { + PersistentBuffers.Unmap(buffer); + Buffer.Delete(buffer); } @@ -272,5 +295,10 @@ namespace Ryujinx.Graphics.OpenGL { ScreenCaptured?.Invoke(this, bitmap); } + + public bool PrepareHostMapping(nint address, ulong size) + { + return false; + } } } diff --git a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs index 654e25b9d..aac288b7e 100644 --- a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs +++ b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs @@ -1,8 +1,9 @@ -using OpenTK.Graphics.OpenGL; +using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.OpenGL.Image; using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -13,6 +14,8 @@ namespace Ryujinx.Graphics.OpenGL private PersistentBuffer _main = new PersistentBuffer(); private PersistentBuffer _background = new PersistentBuffer(); + private Dictionary<BufferHandle, IntPtr> _maps = new Dictionary<BufferHandle, IntPtr>(); + public PersistentBuffer Default => BackgroundContextWorker.InBackground ? _background : _main; public void Dispose() @@ -20,6 +23,30 @@ namespace Ryujinx.Graphics.OpenGL _main?.Dispose(); _background?.Dispose(); } + + public void Map(BufferHandle handle, int size) + { + GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); + IntPtr ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, IntPtr.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); + + _maps[handle] = ptr; + } + + public void Unmap(BufferHandle handle) + { + if (_maps.ContainsKey(handle)) + { + GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); + GL.UnmapBuffer(BufferTarget.CopyWriteBuffer); + + _maps.Remove(handle); + } + } + + public bool TryGet(BufferHandle handle, out IntPtr ptr) + { + return _maps.TryGetValue(handle, out ptr); + } } class PersistentBuffer : IDisposable diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 21b81bdd3..a1ea6836f 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -33,6 +33,7 @@ namespace Ryujinx.Graphics.Vulkan private MemoryAllocation _allocation; private Auto<DisposableBuffer> _buffer; private Auto<MemoryAllocation> _allocationAuto; + private bool _allocationImported; private ulong _bufferHandle; private CacheByRange<BufferHolder> _cachedConvertedBuffers; @@ -81,6 +82,26 @@ namespace Ryujinx.Graphics.Vulkan _flushLock = new ReaderWriterLock(); } + public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset) + { + _gd = gd; + _device = device; + _allocation = allocation.GetUnsafe(); + _allocationAuto = allocation; + _allocationImported = true; + _waitable = new MultiFenceHolder(size); + _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), _waitable, _allocationAuto); + _bufferHandle = buffer.Handle; + Size = size; + _map = _allocation.HostPointer + offset; + + _baseType = type; + _currentType = currentType; + DesiredType = currentType; + + _flushLock = new ReaderWriterLock(); + } + public bool TryBackingSwap(ref CommandBufferScoped? cbs) { if (_swapQueued && DesiredType != _currentType) @@ -775,8 +796,15 @@ namespace Ryujinx.Graphics.Vulkan _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); _buffer.Dispose(); - _allocationAuto.Dispose(); _cachedConvertedBuffers.Dispose(); + if (_allocationImported) + { + _allocationAuto.DecrementReferenceCount(); + } + else + { + _allocationAuto.Dispose(); + } _flushLock.AcquireWriterLock(Timeout.Infinite); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index f8f41e5b2..27678ed5e 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan { class BufferManager : IDisposable { - private const MemoryPropertyFlags DefaultBufferMemoryFlags = + public const MemoryPropertyFlags DefaultBufferMemoryFlags = MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit | MemoryPropertyFlags.HostCachedBit; @@ -40,6 +40,10 @@ namespace Ryujinx.Graphics.Vulkan BufferUsageFlags.VertexBufferBit | BufferUsageFlags.TransformFeedbackBufferBitExt; + private const BufferUsageFlags HostImportedBufferUsageFlags = + BufferUsageFlags.TransferSrcBit | + BufferUsageFlags.TransferDstBit; + private readonly Device _device; private readonly IdList<BufferHolder> _buffers; @@ -48,11 +52,47 @@ namespace Ryujinx.Graphics.Vulkan public StagingBuffer StagingBuffer { get; } + public MemoryRequirements HostImportedBufferMemoryRequirements { get; } + public BufferManager(VulkanRenderer gd, Device device) { _device = device; _buffers = new IdList<BufferHolder>(); StagingBuffer = new StagingBuffer(gd, this); + + HostImportedBufferMemoryRequirements = GetHostImportedUsageRequirements(gd); + } + + public unsafe BufferHandle CreateHostImported(VulkanRenderer gd, nint pointer, int size) + { + var usage = HostImportedBufferUsageFlags; + + if (gd.Capabilities.SupportsIndirectParameters) + { + usage |= BufferUsageFlags.IndirectBufferBit; + } + + var bufferCreateInfo = new BufferCreateInfo() + { + SType = StructureType.BufferCreateInfo, + Size = (ulong)size, + Usage = usage, + SharingMode = SharingMode.Exclusive + }; + + gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); + + (Auto<MemoryAllocation> allocation, ulong offset) = gd.HostMemoryAllocator.GetExistingAllocation(pointer, (ulong)size); + + gd.Api.BindBufferMemory(_device, buffer, allocation.GetUnsafe().Memory, allocation.GetUnsafe().Offset + offset); + + var holder = new BufferHolder(gd, _device, buffer, allocation, size, BufferAllocationType.HostMapped, BufferAllocationType.HostMapped, (int)offset); + + BufferCount++; + + ulong handle64 = (uint)_buffers.Add(holder); + + return Unsafe.As<ulong, BufferHandle>(ref handle64); } public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default) @@ -75,6 +115,32 @@ namespace Ryujinx.Graphics.Vulkan return Unsafe.As<ulong, BufferHandle>(ref handle64); } + public unsafe MemoryRequirements GetHostImportedUsageRequirements(VulkanRenderer gd) + { + var usage = HostImportedBufferUsageFlags; + + if (gd.Capabilities.SupportsIndirectParameters) + { + usage |= BufferUsageFlags.IndirectBufferBit; + } + + var bufferCreateInfo = new BufferCreateInfo() + { + SType = StructureType.BufferCreateInfo, + Size = (ulong)Environment.SystemPageSize, + Usage = usage, + SharingMode = SharingMode.Exclusive + }; + + gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); + + gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); + + gd.Api.DestroyBuffer(_device, buffer, null); + + return requirements; + } + public unsafe (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) CreateBacking( VulkanRenderer gd, int size, diff --git a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs index b69c64aa8..1f03b68c2 100644 --- a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -364,6 +364,15 @@ namespace Ryujinx.Graphics.Vulkan }; } + public static BufferAllocationType Convert(this BufferAccess access) + { + return access switch + { + BufferAccess.FlushPersistent => BufferAllocationType.HostMapped, + _ => BufferAllocationType.Auto + }; + } + private static T2 LogInvalidAndReturn<T1, T2>(T1 value, string name, T2 defaultValue = default) { Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}."); diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index ab82d7b4a..5cab4113a 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsPipelineStatisticsQuery; public readonly bool SupportsGeometryShader; public readonly bool SupportsViewportArray2; + public readonly bool SupportsHostImportedMemory; public readonly uint MinSubgroupSize; public readonly uint MaxSubgroupSize; public readonly ShaderStageFlags RequiredSubgroupSizeStages; @@ -75,6 +76,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsPipelineStatisticsQuery, bool supportsGeometryShader, bool supportsViewportArray2, + bool supportsHostImportedMemory, uint minSubgroupSize, uint maxSubgroupSize, ShaderStageFlags requiredSubgroupSizeStages, @@ -108,6 +110,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery; SupportsGeometryShader = supportsGeometryShader; SupportsViewportArray2 = supportsViewportArray2; + SupportsHostImportedMemory = supportsHostImportedMemory; MinSubgroupSize = minSubgroupSize; MaxSubgroupSize = maxSubgroupSize; RequiredSubgroupSizeStages = requiredSubgroupSizeStages; diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index c57edaf7c..155c7f6fe 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programColorClearSI; private readonly IProgram _programColorClearUI; private readonly IProgram _programStrideChange; + private readonly IProgram _programConvertD32S8ToD24S8; private readonly IProgram _programConvertIndexBuffer; private readonly IProgram _programConvertIndirectData; private readonly IProgram _programColorCopyShortening; @@ -158,6 +159,17 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), }); + var convertD32S8ToD24S8Bindings = new ShaderBindings( + new[] { 0 }, + new[] { 1, 2 }, + Array.Empty<int>(), + Array.Empty<int>()); + + _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, convertD32S8ToD24S8Bindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + var convertIndexBufferBindings = new ShaderBindings( new[] { 0 }, new[] { 1, 2 }, @@ -1644,6 +1656,82 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.Finish(gd, cbs); } + public unsafe void ConvertD32S8ToD24S8(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, Auto<DisposableBuffer> dstBufferAuto, int pixelCount, int dstOffset) + { + int inSize = pixelCount * 2 * sizeof(int); + int outSize = pixelCount * sizeof(int); + + var srcBufferAuto = src.GetBuffer(); + + var srcBuffer = srcBufferAuto.Get(cbs, 0, inSize).Value; + var dstBuffer = dstBufferAuto.Get(cbs, dstOffset, outSize).Value; + + var access = AccessFlags.ShaderWriteBit; + var stage = PipelineStageFlags.ComputeShaderBit; + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + srcBuffer, + BufferHolder.DefaultAccessFlags, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + stage, + 0, + outSize); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + BufferHolder.DefaultAccessFlags, + access, + PipelineStageFlags.AllCommandsBit, + stage, + 0, + outSize); + + const int ParamsBufferSize = sizeof(int) * 2; + + Span<int> shaderParams = stackalloc int[2]; + + shaderParams[0] = pixelCount; + shaderParams[1] = dstOffset; + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + + gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); + + _pipeline.SetCommandBuffer(cbs); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + + Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2]; + + sbRanges[0] = srcBufferAuto; + sbRanges[1] = dstBufferAuto; + + _pipeline.SetStorageBuffers(1, sbRanges); + + _pipeline.SetProgram(_programConvertD32S8ToD24S8); + _pipeline.DispatchCompute(1, 1, 1); + + gd.BufferManager.Delete(bufferHandle); + + _pipeline.Finish(gd, cbs); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + access, + BufferHolder.DefaultAccessFlags, + stage, + PipelineStageFlags.AllCommandsBit, + 0, + outSize); + } + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs new file mode 100644 index 000000000..e62b2dbba --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs @@ -0,0 +1,188 @@ +using Ryujinx.Common; +using Ryujinx.Common.Collections; +using Ryujinx.Common.Logging; +using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.EXT; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Vulkan +{ + internal class HostMemoryAllocator + { + private struct HostMemoryAllocation + { + public readonly Auto<MemoryAllocation> Allocation; + public readonly IntPtr Pointer; + public readonly ulong Size; + + public ulong Start => (ulong)Pointer; + public ulong End => (ulong)Pointer + Size; + + public HostMemoryAllocation(Auto<MemoryAllocation> allocation, IntPtr pointer, ulong size) + { + Allocation = allocation; + Pointer = pointer; + Size = size; + } + } + + private readonly MemoryAllocator _allocator; + private readonly Vk _api; + private readonly ExtExternalMemoryHost _hostMemoryApi; + private readonly Device _device; + private readonly object _lock = new(); + + private List<HostMemoryAllocation> _allocations; + private IntervalTree<ulong, HostMemoryAllocation> _allocationTree; + + public HostMemoryAllocator(MemoryAllocator allocator, Vk api, ExtExternalMemoryHost hostMemoryApi, Device device) + { + _allocator = allocator; + _api = api; + _hostMemoryApi = hostMemoryApi; + _device = device; + + _allocations = new List<HostMemoryAllocation>(); + _allocationTree = new IntervalTree<ulong, HostMemoryAllocation>(); + } + + public unsafe bool TryImport( + MemoryRequirements requirements, + MemoryPropertyFlags flags, + IntPtr pointer, + ulong size) + { + lock (_lock) + { + // Does a compatible allocation exist in the tree? + var allocations = new HostMemoryAllocation[10]; + + ulong start = (ulong)pointer; + ulong end = start + size; + + int count = _allocationTree.Get(start, end, ref allocations); + + // A compatible range is one that where the start and end completely cover the requested range. + for (int i = 0; i < count; i++) + { + HostMemoryAllocation existing = allocations[i]; + + if (start >= existing.Start && end <= existing.End) + { + try + { + existing.Allocation.IncrementReferenceCount(); + + return true; + } + catch (InvalidOperationException) + { + // Can throw if the allocation has been disposed. + // Just continue the search if this happens. + } + } + } + + nint pageAlignedPointer = BitUtils.AlignDown(pointer, Environment.SystemPageSize); + nint pageAlignedEnd = BitUtils.AlignUp((nint)((ulong)pointer + size), Environment.SystemPageSize); + ulong pageAlignedSize = (ulong)(pageAlignedEnd - pageAlignedPointer); + + Result getResult = _hostMemoryApi.GetMemoryHostPointerProperties(_device, ExternalMemoryHandleTypeFlags.HostAllocationBitExt, (void*)pageAlignedPointer, out MemoryHostPointerPropertiesEXT properties); + if (getResult < Result.Success) + { + return false; + } + + int memoryTypeIndex = _allocator.FindSuitableMemoryTypeIndex(properties.MemoryTypeBits & requirements.MemoryTypeBits, flags); + if (memoryTypeIndex < 0) + { + return false; + } + + ImportMemoryHostPointerInfoEXT importInfo = new ImportMemoryHostPointerInfoEXT() + { + SType = StructureType.ImportMemoryHostPointerInfoExt, + HandleType = ExternalMemoryHandleTypeFlags.HostAllocationBitExt, + PHostPointer = (void*)pageAlignedPointer + }; + + var memoryAllocateInfo = new MemoryAllocateInfo() + { + SType = StructureType.MemoryAllocateInfo, + AllocationSize = pageAlignedSize, + MemoryTypeIndex = (uint)memoryTypeIndex, + PNext = &importInfo + }; + + Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory); + + if (result < Result.Success) + { + Logger.Debug?.PrintMsg(LogClass.Gpu, $"Host mapping import 0x{pageAlignedPointer:x16} 0x{pageAlignedSize:x8} failed."); + return false; + } + + var allocation = new MemoryAllocation(this, deviceMemory, pageAlignedPointer, 0, pageAlignedSize); + var allocAuto = new Auto<MemoryAllocation>(allocation); + var hostAlloc = new HostMemoryAllocation(allocAuto, pageAlignedPointer, pageAlignedSize); + + allocAuto.IncrementReferenceCount(); + allocAuto.Dispose(); // Kept alive by ref count only. + + // Register this mapping for future use. + + _allocationTree.Add(hostAlloc.Start, hostAlloc.End, hostAlloc); + _allocations.Add(hostAlloc); + } + + return true; + } + + public (Auto<MemoryAllocation>, ulong) GetExistingAllocation(IntPtr pointer, ulong size) + { + lock (_lock) + { + // Does a compatible allocation exist in the tree? + var allocations = new HostMemoryAllocation[10]; + + ulong start = (ulong)pointer; + ulong end = start + size; + + int count = _allocationTree.Get(start, end, ref allocations); + + // A compatible range is one that where the start and end completely cover the requested range. + for (int i = 0; i < count; i++) + { + HostMemoryAllocation existing = allocations[i]; + + if (start >= existing.Start && end <= existing.End) + { + return (existing.Allocation, start - existing.Start); + } + } + + throw new InvalidOperationException($"No host allocation was prepared for requested range 0x{pointer:x16}:0x{size:x16}."); + } + } + + public void Free(DeviceMemory memory, ulong offset, ulong size) + { + lock (_lock) + { + _allocations.RemoveAll(allocation => + { + if (allocation.Allocation.GetUnsafe().Memory.Handle == memory.Handle) + { + _allocationTree.Remove(allocation.Start, allocation); + return true; + } + + return false; + }); + } + + _api.FreeMemory(_device, memory, ReadOnlySpan<AllocationCallbacks>.Empty); + } + } +} diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs index 76de12967..3c98c417c 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Vulkan { private readonly MemoryAllocatorBlockList _owner; private readonly MemoryAllocatorBlockList.Block _block; + private readonly HostMemoryAllocator _hostMemory; public DeviceMemory Memory { get; } public IntPtr HostPointer { get;} @@ -29,9 +30,30 @@ namespace Ryujinx.Graphics.Vulkan Size = size; } + public MemoryAllocation( + HostMemoryAllocator hostMemory, + DeviceMemory memory, + IntPtr hostPointer, + ulong offset, + ulong size) + { + _hostMemory = hostMemory; + Memory = memory; + HostPointer = hostPointer; + Offset = offset; + Size = size; + } + public void Dispose() { - _owner.Free(_block, Offset, Size); + if (_hostMemory != null) + { + _hostMemory.Free(Memory, Offset, Size); + } + else + { + _owner.Free(_block, Offset, Size); + } } } } diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 3139e2091..462d8f052 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Vulkan return newBl.Allocate(size, alignment, map); } - private int FindSuitableMemoryTypeIndex( + internal int FindSuitableMemoryTypeIndex( uint memoryTypeBits, MemoryPropertyFlags flags) { diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/ConvertD32S8ToD24S8ShaderSource.comp b/src/Ryujinx.Graphics.Vulkan/Shaders/ConvertD32S8ToD24S8ShaderSource.comp new file mode 100644 index 000000000..d3a74b1c8 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/Shaders/ConvertD32S8ToD24S8ShaderSource.comp @@ -0,0 +1,58 @@ +#version 450 core + +#extension GL_EXT_scalar_block_layout : require + +layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout (std430, set = 0, binding = 0) uniform stride_arguments +{ + int pixelCount; + int dstStartOffset; +}; + +layout (std430, set = 1, binding = 1) buffer in_s +{ + uint[] in_data; +}; + +layout (std430, set = 1, binding = 2) buffer out_s +{ + uint[] out_data; +}; + +void main() +{ + // Determine what slice of the stride copies this invocation will perform. + int invocations = int(gl_WorkGroupSize.x); + + int copiesRequired = 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(gl_LocalInvocationID.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 = dstStartOffset + startCopy; + + // Perform the conversion for this region. + for (int i = 0; i < copyCount; i++) + { + float depth = uintBitsToFloat(in_data[srcOffset++]); + uint stencil = in_data[srcOffset++]; + + uint rescaledDepth = uint(clamp(depth, 0.0, 1.0) * 16777215.0); + + out_data[dstOffset++] = (rescaledDepth << 8) | (stencil & 0xff); + } +} diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index c9dde7b63..69d1fa3ce 100644 --- a/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -1236,6 +1236,213 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x38, 0x00, 0x01, 0x00, }; + public static readonly byte[] ConvertD32S8ToD24S8ShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, + 0x58, 0x54, 0x5F, 0x73, 0x63, 0x61, 0x6C, 0x61, 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F, + 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, + 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, + 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x69, 0x6E, + 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61, 0x72, 0x67, 0x75, 0x6D, + 0x65, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, + 0x06, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x61, 0x6C, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x43, 0x6F, 0x70, + 0x69, 0x65, 0x73, 0x00, 0x05, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, + 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x4C, + 0x6F, 0x63, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x29, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x79, + 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x70, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4F, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x64, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x05, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x64, + 0x61, 0x74, 0x61, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x63, 0x61, 0x6C, 0x65, 0x64, + 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x76, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x25, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, + 0x2B, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x4B, + 0x1D, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6B, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x4A, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0xA9, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x2D, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x41, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x41, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x4B, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x5A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x5D, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x49, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, + 0x6D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, + 0xC7, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x6C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x71, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x44, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x41, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x43, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 + }; + public static readonly byte[] ConvertIndexBufferShaderSource = new byte[] { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x91, 0x00, 0x00, 0x00, diff --git a/src/Ryujinx.Graphics.Vulkan/SyncManager.cs b/src/Ryujinx.Graphics.Vulkan/SyncManager.cs index 432d224f0..b3f6e8e5a 100644 --- a/src/Ryujinx.Graphics.Vulkan/SyncManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/SyncManager.cs @@ -125,6 +125,24 @@ namespace Ryujinx.Graphics.Vulkan if (result != null) { + if (result.Waitable == null) + { + return; + } + + long beforeTicks = Stopwatch.GetTimestamp(); + + if (result.NeedsFlush(FlushId)) + { + _gd.InterruptAction(() => + { + if (result.NeedsFlush(FlushId)) + { + _gd.FlushAllCommands(); + } + }); + } + lock (result) { if (result.Waitable == null) @@ -132,19 +150,6 @@ namespace Ryujinx.Graphics.Vulkan return; } - long beforeTicks = Stopwatch.GetTimestamp(); - - if (result.NeedsFlush(FlushId)) - { - _gd.InterruptAction(() => - { - if (result.NeedsFlush(FlushId)) - { - _gd.FlushAllCommands(); - } - }); - } - bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); if (!signaled) diff --git a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index 738bf57d1..66951153d 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -67,6 +67,11 @@ namespace Ryujinx.Graphics.Vulkan return GetData(); } + public void CopyTo(BufferRange range, int layer, int level, int stride) + { + throw new NotImplementedException(); + } + public void Release() { if (_gd.Textures.Remove(this)) diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index cd280d5f4..62d481eb9 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -563,6 +563,34 @@ namespace Ryujinx.Graphics.Vulkan } } + public void CopyTo(BufferRange range, int layer, int level, int stride) + { + _gd.PipelineInternal.EndRenderPass(); + var cbs = _gd.PipelineInternal.CurrentCommandBuffer; + + int outSize = Info.GetMipSize(level); + int hostSize = GetBufferDataLength(outSize); + + var image = GetImage().Get(cbs).Value; + int offset = range.Offset; + + Auto<DisposableBuffer> autoBuffer = _gd.BufferManager.GetBuffer(cbs.CommandBuffer, range.Handle, true); + VkBuffer buffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; + + if (PrepareOutputBuffer(cbs, hostSize, buffer, out VkBuffer copyToBuffer, out BufferHolder tempCopyHolder)) + { + offset = 0; + } + + CopyFromOrToBuffer(cbs.CommandBuffer, copyToBuffer, image, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride); + + if (tempCopyHolder != null) + { + CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset); + tempCopyHolder.Dispose(); + } + } + private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer) { int size = 0; @@ -693,6 +721,30 @@ namespace Ryujinx.Graphics.Vulkan return storage; } + private bool PrepareOutputBuffer(CommandBufferScoped cbs, int hostSize, VkBuffer target, out VkBuffer copyTarget, out BufferHolder copyTargetHolder) + { + if (NeedsD24S8Conversion()) + { + copyTargetHolder = _gd.BufferManager.Create(_gd, 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<DisposableBuffer> copyTarget, int hostSize, int dstOffset) + { + if (NeedsD24S8Conversion()) + { + _gd.HelperShader.ConvertD32S8ToD24S8(_gd, cbs, hostData, copyTarget, hostSize / (2 * sizeof(int)), dstOffset); + } + } + private bool NeedsD24S8Conversion() { return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint; @@ -708,7 +760,9 @@ namespace Ryujinx.Graphics.Vulkan int dstLevel, int dstLayers, int dstLevels, - bool singleSlice) + bool singleSlice, + int offset = 0, + int stride = 0) { bool is3D = Info.Target == Target.Texture3D; int width = Math.Max(1, Info.Width >> dstLevel); @@ -718,8 +772,6 @@ namespace Ryujinx.Graphics.Vulkan int layers = dstLayers; int levels = dstLevels; - int offset = 0; - for (int level = 0; level < levels; level++) { int mipSize = GetBufferDataLength(Info.GetMipSize2D(dstLevel + level) * dstLayers); @@ -731,7 +783,7 @@ namespace Ryujinx.Graphics.Vulkan break; } - int rowLength = (Info.GetMipStride(dstLevel + level) / Info.BytesPerPixel) * Info.BlockWidth; + int rowLength = ((stride == 0 ? Info.GetMipStride(dstLevel + level) : stride) / Info.BytesPerPixel) * Info.BlockWidth; var aspectFlags = Info.Format.ConvertAspectFlags(); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 50a6fcb90..bad6641e3 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan ExtTransformFeedback.ExtensionName, KhrDrawIndirectCount.ExtensionName, KhrPushDescriptor.ExtensionName, + ExtExternalMemoryHost.ExtensionName, "VK_EXT_blend_operation_advanced", "VK_EXT_custom_border_color", "VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV. diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index e7475b6b3..06fec4f1b 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.Vulkan internal object QueueLock { get; private set; } internal MemoryAllocator MemoryAllocator { get; private set; } + internal HostMemoryAllocator HostMemoryAllocator { get; private set; } internal CommandBufferPool CommandBufferPool { get; private set; } internal DescriptorSetManager DescriptorSetManager { get; private set; } internal PipelineLayoutCache PipelineLayoutCache { get; private set; } @@ -307,6 +308,7 @@ namespace Ryujinx.Graphics.Vulkan _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery, _physicalDevice.PhysicalDeviceFeatures.GeometryShader, _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), + _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, @@ -319,6 +321,9 @@ namespace Ryujinx.Graphics.Vulkan MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device); + Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi); + HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device); + CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); DescriptorSetManager = new DescriptorSetManager(_device); @@ -375,11 +380,21 @@ namespace Ryujinx.Graphics.Vulkan _initialized = true; } + public BufferHandle CreateBuffer(int size, BufferAccess access) + { + return BufferManager.CreateWithHandle(this, size, access.Convert()); + } + public BufferHandle CreateBuffer(int size, BufferHandle storageHint) { return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint); } + public BufferHandle CreateBuffer(nint pointer, int size) + { + return BufferManager.CreateHostImported(this, pointer, size); + } + public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) { bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute; @@ -816,5 +831,11 @@ namespace Ryujinx.Graphics.Vulkan // Last step destroy the instance _instance.Dispose(); } + + public bool PrepareHostMapping(nint address, ulong size) + { + return Capabilities.SupportsHostImportedMemory && + HostMemoryAllocator.TryImport(BufferManager.HostImportedBufferMemoryRequirements, BufferManager.DefaultBufferMemoryFlags, address, size); + } } } \ No newline at end of file From 2c94ac455ead867aac0a7a689a55d814a8bcc0da Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 1 May 2023 20:27:51 +0100 Subject: [PATCH 482/737] GPU: Keep rendered textures without any pool references alive (#4662) * GPU: Keep sampled textures without any pool references alive Occasionally games are very wasteful and clear/write to a texture without ever sampling it. As rendered textures in NVN games seem to all have overlapping memory ranges, the texture will eventually get overwritten. Normally, this would trigger a removal from the auto delete cache, but a pool entry would keep the texture alive. However, with these textures that are never used, they will get deleted immediately and recreated on the next frame. This change makes it so the ShortTextureCache can keep textures that have naver had a pool reference alive for a few frames, so they're not constantly being created and deleted. This improves performance in Zelda BOTW a little. * Cleanup --- .../Image/AutoDeleteCache.cs | 48 +++++++++++++++++-- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 10 +++- .../Image/TextureCache.cs | 22 ++++++++- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index a0b9f57bd..2465efb0f 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.Collections.Concurrent; using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image @@ -9,6 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> class ShortTextureCacheEntry { + public bool IsAutoDelete; public readonly TextureDescriptor Descriptor; public readonly int InvalidatedSequence; public readonly Texture Texture; @@ -24,6 +24,17 @@ namespace Ryujinx.Graphics.Gpu.Image InvalidatedSequence = texture.InvalidatedSequence; Texture = texture; } + + /// <summary> + /// Create a new entry on the short duration texture cache from the auto delete cache. + /// </summary> + /// <param name="texture">The texture</param> + public ShortTextureCacheEntry(Texture texture) + { + IsAutoDelete = true; + InvalidatedSequence = texture.InvalidatedSequence; + Texture = texture; + } } /// <summary> @@ -199,7 +210,11 @@ namespace Ryujinx.Graphics.Gpu.Image { texture.DecrementReferenceCount(); - _shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor); + if (!texture.ShortCacheEntry.IsAutoDelete) + { + _shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor); + } + texture.ShortCacheEntry = null; } } @@ -222,6 +237,25 @@ namespace Ryujinx.Graphics.Gpu.Image texture.IncrementReferenceCount(); } + /// <summary> + /// Adds a texture to the short duration cache without a descriptor. This typically keeps it alive for two ticks. + /// On expiry, it will be removed from the AutoDeleteCache. + /// </summary> + /// <param name="texture">Texture to add to the short cache</param> + public void AddShortCache(Texture texture) + { + if (texture.ShortCacheEntry != null) + { + var entry = new ShortTextureCacheEntry(texture); + + _shortCacheBuilder.Add(entry); + + texture.ShortCacheEntry = entry; + + texture.IncrementReferenceCount(); + } + } + /// <summary> /// Delete textures from the short duration cache. /// Moves the builder set to be deleted on next process. @@ -234,7 +268,15 @@ namespace Ryujinx.Graphics.Gpu.Image { entry.Texture.DecrementReferenceCount(); - _shortCacheLookup.Remove(entry.Descriptor); + if (entry.IsAutoDelete) + { + Remove(entry.Texture, false); + } + else + { + _shortCacheLookup.Remove(entry.Descriptor); + } + entry.Texture.ShortCacheEntry = null; } diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 3b2579889..6c9de8d6a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -144,6 +144,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public ShortTextureCacheEntry ShortCacheEntry { get; set; } + /// <summary> + /// Whether this texture has ever been referenced by a pool. + /// </summary> + public bool HadPoolOwner { get; private set; } + /// Physical memory ranges where the texture data is located. /// </summary> public MultiRange Range { get; private set; } @@ -1506,10 +1511,13 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="gpuVa">GPU VA of the pool reference</param> public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa) { + HadPoolOwner = true; + lock (_poolOwners) { _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa }); } + _referenceCount++; if (ShortCacheEntry != null) @@ -1594,7 +1602,7 @@ namespace Ryujinx.Graphics.Gpu.Image _poolOwners.Clear(); } - if (ShortCacheEntry != null && _context.IsGpuThread()) + if (ShortCacheEntry != null && !ShortCacheEntry.IsAutoDelete && _context.IsGpuThread()) { // If this is called from another thread (unmapped), the short cache will // have to remove this texture on a future tick. diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 97d78a349..fc2c07e55 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -848,7 +848,17 @@ namespace Ryujinx.Graphics.Gpu.Image if (overlapInCache) { - _cache.Remove(overlap, flush); + if (flush || overlap.HadPoolOwner || overlap.IsView) + { + _cache.Remove(overlap, flush); + } + else + { + // This texture has only ever been referenced in the AutoDeleteCache. + // Keep this texture alive with the short duration cache, as it may be used often but not sampled. + + _cache.AddShortCache(overlap); + } } removeOverlap = modified; @@ -1198,6 +1208,16 @@ namespace Ryujinx.Graphics.Gpu.Image _cache.AddShortCache(texture, ref descriptor); } + /// <summary> + /// Adds a texture to the short duration cache without a descriptor. This typically keeps it alive for two ticks. + /// On expiry, it will be removed from the AutoDeleteCache. + /// </summary> + /// <param name="texture">Texture to add to the short cache</param> + public void AddShortCache(Texture texture) + { + _cache.AddShortCache(texture); + } + /// <summary> /// Removes a texture from the short duration cache. /// </summary> From dd574146fb5f05c1c0a469a4ad4a20c46bb37d74 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 2 May 2023 03:29:47 +0200 Subject: [PATCH 483/737] Add hide-cursor command line argument & always hide cursor option (#4613) * Add hide-cursor command line argument * gtk: Adjust SettingsWindow for hide cursor options * ava: Adjust SettingsWindow for hide cursor options * ava: Add override check for HideCursor arg * Remove copy&paste sins * ava: Leave a little more room between the options * gtk: Fix hide cursor issues * ava: Only hide cursor if it's within the embedded window --- src/Ryujinx.Ava/AppHost.cs | 44 ++++++----- src/Ryujinx.Ava/Assets/Locales/en_US.json | 5 +- src/Ryujinx.Ava/Program.cs | 14 +++- .../UI/ViewModels/SettingsViewModel.cs | 6 +- .../UI/Views/Settings/SettingsUIView.axaml | 31 ++++++-- .../Configuration/HideCursorMode.cs | 9 +++ src/Ryujinx.Headless.SDL2/HideCursor.cs | 9 --- .../OpenGL/OpenGLWindow.cs | 4 +- src/Ryujinx.Headless.SDL2/Options.cs | 4 +- src/Ryujinx.Headless.SDL2/Program.cs | 4 +- src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs | 13 ++-- .../Vulkan/VulkanWindow.cs | 4 +- src/Ryujinx.Headless.SDL2/WindowBase.cs | 4 +- .../Configuration/ConfigurationFileFormat.cs | 6 +- .../Configuration/ConfigurationState.cs | 12 +-- .../Helper/CommandLineState.cs | 11 +++ src/Ryujinx/Program.cs | 16 +++- src/Ryujinx/Ui/RendererWidgetBase.cs | 59 +++++++++----- src/Ryujinx/Ui/Windows/SettingsWindow.cs | 32 ++++++-- src/Ryujinx/Ui/Windows/SettingsWindow.glade | 78 +++++++++++++++++-- 20 files changed, 266 insertions(+), 99 deletions(-) create mode 100644 src/Ryujinx.Common/Configuration/HideCursorMode.cs delete mode 100644 src/Ryujinx.Headless.SDL2/HideCursor.cs diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 957a1c9d3..e11a954d8 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -157,7 +157,7 @@ namespace Ryujinx.Ava _isFirmwareTitle = true; } - ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorState_Changed; + ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; _topLevel.PointerMoved += TopLevel_PointerMoved; @@ -468,9 +468,9 @@ namespace Ryujinx.Ava (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null); } - private void HideCursorState_Changed(object sender, ReactiveEventArgs<bool> state) + private void HideCursorState_Changed(object sender, ReactiveEventArgs<HideCursorMode> state) { - if (state.NewValue) + if (state.NewValue == HideCursorMode.OnIdle) { _lastCursorMoveTime = Stopwatch.GetTimestamp(); } @@ -965,30 +965,38 @@ namespace Ryujinx.Ava if (_viewModel.IsActive) { - if (ConfigurationState.Instance.Hid.EnableMouse) + if (_isCursorInRenderer) { - if (_isCursorInRenderer) + if (ConfigurationState.Instance.Hid.EnableMouse) { HideCursor(); } else { - ShowCursor(); + switch (ConfigurationState.Instance.HideCursor.Value) + { + case HideCursorMode.Never: + ShowCursor(); + break; + case HideCursorMode.OnIdle: + if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) + { + HideCursor(); + } + else + { + ShowCursor(); + } + break; + case HideCursorMode.Always: + HideCursor(); + break; + } } } else { - if (ConfigurationState.Instance.HideCursorOnIdle) - { - if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) - { - HideCursor(); - } - else - { - ShowCursor(); - } - } + ShowCursor(); } Dispatcher.UIThread.Post(() => @@ -1133,4 +1141,4 @@ namespace Ryujinx.Ava return state; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 3a4bfc65e..617cad34f 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -80,7 +80,10 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Enable Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch", "SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog", - "SettingsTabGeneralHideCursorOnIdle": "Hide Cursor on Idle", + "SettingsTabGeneralHideCursor": "Hide Cursor:", + "SettingsTabGeneralHideCursorNever": "Never", + "SettingsTabGeneralHideCursorOnIdle": "On Idle", + "SettingsTabGeneralHideCursorAlways": "Always", "SettingsTabGeneralGameDirectories": "Game Directories", "SettingsTabGeneralAdd": "Add", "SettingsTabGeneralRemove": "Remove", diff --git a/src/Ryujinx.Ava/Program.cs b/src/Ryujinx.Ava/Program.cs index 7f35c62a4..0629e6062 100644 --- a/src/Ryujinx.Ava/Program.cs +++ b/src/Ryujinx.Ava/Program.cs @@ -183,6 +183,18 @@ namespace Ryujinx.Ava { ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; } + + // Check if HideCursor was overridden. + if (CommandLineState.OverrideHideCursor is not null) + { + ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch + { + "never" => HideCursorMode.Never, + "onidle" => HideCursorMode.OnIdle, + "always" => HideCursorMode.Always, + _ => ConfigurationState.Instance.HideCursor.Value + }; + } } private static void PrintSystemInfo() @@ -226,4 +238,4 @@ namespace Ryujinx.Ava Logger.Shutdown(); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 232c9d436..08612117a 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -132,7 +132,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableDiscordIntegration { get; set; } public bool CheckUpdatesOnStart { get; set; } public bool ShowConfirmExit { get; set; } - public bool HideCursorOnIdle { get; set; } + public int HideCursor { get; set; } public bool EnableDockedMode { get; set; } public bool EnableKeyboard { get; set; } public bool EnableMouse { get; set; } @@ -375,7 +375,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableDiscordIntegration = config.EnableDiscordIntegration; CheckUpdatesOnStart = config.CheckUpdatesOnStart; ShowConfirmExit = config.ShowConfirmExit; - HideCursorOnIdle = config.HideCursorOnIdle; + HideCursor = (int)config.HideCursor.Value; GameDirectories.Clear(); GameDirectories.AddRange(config.Ui.GameDirs.Value); @@ -458,7 +458,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.EnableDiscordIntegration.Value = EnableDiscordIntegration; config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart; config.ShowConfirmExit.Value = ShowConfirmExit; - config.HideCursorOnIdle.Value = HideCursorOnIdle; + config.HideCursor.Value = (HideCursorMode)HideCursor; if (_directoryChanged) { diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml index 61b6c4335..acc5e2b70 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml @@ -1,4 +1,4 @@ -<UserControl +<UserControl x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUIView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" @@ -12,7 +12,7 @@ <Design.DataContext> <viewModels:SettingsViewModel /> </Design.DataContext> - <ScrollViewer + <ScrollViewer Name="UiPage" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" @@ -37,9 +37,24 @@ <CheckBox IsChecked="{Binding ShowConfirmExit}"> <TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" /> </CheckBox> - <CheckBox IsChecked="{Binding HideCursorOnIdle}"> - <TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" /> - </CheckBox> + <StackPanel Margin="0, 15, 0, 10" Orientation="Horizontal"> + <TextBlock VerticalAlignment="Center" + Text="{locale:Locale SettingsTabGeneralHideCursor}" + Width="150" /> + <ComboBox SelectedIndex="{Binding HideCursor}" + HorizontalContentAlignment="Left" + MinWidth="100"> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorNever}" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" /> + </ComboBoxItem> + <ComboBoxItem> + <TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorAlways}" /> + </ComboBoxItem> + </ComboBox> + </StackPanel> </StackPanel> <Separator Height="1" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" /> @@ -105,7 +120,7 @@ <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> - <CheckBox + <CheckBox IsChecked="{Binding EnableCustomTheme}" ToolTip.Tip="{locale:Locale CustomThemeCheckTooltip}"> <TextBlock Text="{locale:Locale SettingsTabGeneralThemeEnableCustomTheme}" /> @@ -122,7 +137,7 @@ Grid.Column="1" Margin="0,10,0,0" Text="{Binding CustomThemePath}" /> - <Button + <Button Grid.Row="1" Grid.Column="2" Margin="10,10,0,0" @@ -153,4 +168,4 @@ </StackPanel> </Border> </ScrollViewer> -</UserControl> \ No newline at end of file +</UserControl> diff --git a/src/Ryujinx.Common/Configuration/HideCursorMode.cs b/src/Ryujinx.Common/Configuration/HideCursorMode.cs new file mode 100644 index 000000000..895fc1076 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/HideCursorMode.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Common.Configuration +{ + public enum HideCursorMode + { + Never, + OnIdle, + Always + } +} \ No newline at end of file diff --git a/src/Ryujinx.Headless.SDL2/HideCursor.cs b/src/Ryujinx.Headless.SDL2/HideCursor.cs deleted file mode 100644 index 2dc0bd6ab..000000000 --- a/src/Ryujinx.Headless.SDL2/HideCursor.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.Headless.SDL2 -{ - public enum HideCursor - { - Never, - OnIdle, - Always - } -} \ No newline at end of file diff --git a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs index 69b0f42fb..dc7d811b9 100644 --- a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs @@ -107,8 +107,8 @@ namespace Ryujinx.Headless.SDL2.OpenGL GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse, - HideCursor hideCursor) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor) + HideCursorMode hideCursorMode) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode) { _glLogLevel = glLogLevel; } diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index 982d09909..7dffa1b00 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -76,8 +76,8 @@ namespace Ryujinx.Headless.SDL2 [Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")] public bool EnableMouse { get; set; } - [Option("hide-cursor", Required = false, Default = HideCursor.OnIdle, HelpText = "Change when the cursor gets hidden.")] - public HideCursor HideCursor { get; set; } + [Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")] + public HideCursorMode HideCursorMode { get; set; } [Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")] public bool ListInputProfiles { get; set; } diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index b0bdb97f1..453c470e7 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -478,8 +478,8 @@ namespace Ryujinx.Headless.SDL2 private static WindowBase CreateWindow(Options options) { return options.GraphicsBackend == GraphicsBackend.Vulkan - ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor) - : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor); + ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode) + : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode); } private static IRenderer CreateRenderer(Options options, WindowBase window) diff --git a/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs b/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs index 8c3412ff9..7b88e2655 100644 --- a/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs +++ b/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs @@ -1,4 +1,5 @@ -using Ryujinx.Input; +using Ryujinx.Common.Configuration; +using Ryujinx.Input; using System; using System.Diagnostics; using System.Drawing; @@ -13,7 +14,7 @@ namespace Ryujinx.Headless.SDL2 private const int CursorHideIdleTime = 5; // seconds private bool _isDisposed; - private HideCursor _hideCursor; + private HideCursorMode _hideCursorMode; private bool _isHidden; private long _lastCursorMoveTime; @@ -23,12 +24,12 @@ namespace Ryujinx.Headless.SDL2 public Vector2 Scroll { get; private set; } public Size _clientSize; - public SDL2MouseDriver(HideCursor hideCursor) + public SDL2MouseDriver(HideCursorMode hideCursorMode) { PressedButtons = new bool[(int)MouseButton.Count]; - _hideCursor = hideCursor; + _hideCursorMode = hideCursorMode; - if (_hideCursor == HideCursor.Always) + if (_hideCursorMode == HideCursorMode.Always) { SDL_ShowCursor(SDL_DISABLE); _isHidden = true; @@ -59,7 +60,7 @@ namespace Ryujinx.Headless.SDL2 private void CheckIdle() { - if (_hideCursor != HideCursor.OnIdle) + if (_hideCursorMode != HideCursorMode.OnIdle) { return; } diff --git a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs index 172b7685a..5d048da1f 100644 --- a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs @@ -17,8 +17,8 @@ namespace Ryujinx.Headless.SDL2.Vulkan GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse, - HideCursor hideCursor) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor) + HideCursorMode hideCursorMode) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode) { _glLogLevel = glLogLevel; } diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index e33710421..7c3101535 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -78,9 +78,9 @@ namespace Ryujinx.Headless.SDL2 GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse, - HideCursor hideCursor) + HideCursorMode hideCursorMode) { - MouseDriver = new SDL2MouseDriver(hideCursor); + MouseDriver = new SDL2MouseDriver(hideCursorMode); _inputManager = inputManager; _inputManager.SetMouseDriver(MouseDriver); NpadManager = _inputManager.CreateNpadManager(); diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 3168766b5..f0489915d 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -162,9 +162,9 @@ namespace Ryujinx.Ui.Common.Configuration public bool ShowConfirmExit { get; set; } /// <summary> - /// Hide Cursor on Idle + /// Whether to hide cursor on idle, always or never /// </summary> - public bool HideCursorOnIdle { get; set; } + public HideCursorMode HideCursor { get; set; } /// <summary> /// Enables or disables Vertical Sync @@ -395,4 +395,4 @@ namespace Ryujinx.Ui.Common.Configuration JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index fc3693ae8..146a9b500 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -613,7 +613,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <summary> /// Hide Cursor on Idle /// </summary> - public ReactiveObject<bool> HideCursorOnIdle { get; private set; } + public ReactiveObject<HideCursorMode> HideCursor { get; private set; } private ConfigurationState() { @@ -626,7 +626,7 @@ namespace Ryujinx.Ui.Common.Configuration EnableDiscordIntegration = new ReactiveObject<bool>(); CheckUpdatesOnStart = new ReactiveObject<bool>(); ShowConfirmExit = new ReactiveObject<bool>(); - HideCursorOnIdle = new ReactiveObject<bool>(); + HideCursor = new ReactiveObject<HideCursorMode>(); } public ConfigurationFileFormat ToFileFormat() @@ -662,7 +662,7 @@ namespace Ryujinx.Ui.Common.Configuration EnableDiscordIntegration = EnableDiscordIntegration, CheckUpdatesOnStart = CheckUpdatesOnStart, ShowConfirmExit = ShowConfirmExit, - HideCursorOnIdle = HideCursorOnIdle, + HideCursor = HideCursor, EnableVsync = Graphics.EnableVsync, EnableShaderCache = Graphics.EnableShaderCache, EnableTextureRecompression = Graphics.EnableTextureRecompression, @@ -767,7 +767,7 @@ namespace Ryujinx.Ui.Common.Configuration EnableDiscordIntegration.Value = true; CheckUpdatesOnStart.Value = true; ShowConfirmExit.Value = true; - HideCursorOnIdle.Value = false; + HideCursor.Value = Ryujinx.Common.Configuration.HideCursorMode.Never; Graphics.EnableVsync.Value = true; Graphics.EnableShaderCache.Value = true; Graphics.EnableTextureRecompression.Value = false; @@ -1046,7 +1046,7 @@ namespace Ryujinx.Ui.Common.Configuration { Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 22."); - configurationFileFormat.HideCursorOnIdle = false; + configurationFileFormat.HideCursor = HideCursorMode.Never; configurationFileUpdated = true; } @@ -1427,7 +1427,7 @@ namespace Ryujinx.Ui.Common.Configuration EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; - HideCursorOnIdle.Value = configurationFileFormat.HideCursorOnIdle; + HideCursor.Value = configurationFileFormat.HideCursor; Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; diff --git a/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs b/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs index 8ca7fba18..660a4ce92 100644 --- a/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs +++ b/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Ui.Common.Helper public static bool? OverrideDockedMode { get; private set; } public static string OverrideGraphicsBackend { get; private set; } + public static string OverrideHideCursor { get; private set; } public static string BaseDirPathArg { get; private set; } public static string Profile { get; private set; } public static string LaunchPathArg { get; private set; } @@ -76,6 +77,16 @@ namespace Ryujinx.Ui.Common.Helper case "--handheld-mode": OverrideDockedMode = false; break; + case "--hide-cursor": + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + OverrideHideCursor = args[++i]; + break; default: LaunchPathArg = arg; break; diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 2e6ede444..836483d89 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -209,7 +209,7 @@ namespace Ryujinx } } - // Check if graphics backend was overridden + // Check if graphics backend was overridden. if (CommandLineState.OverrideGraphicsBackend != null) { if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl") @@ -224,7 +224,19 @@ namespace Ryujinx } } - // Check if docked mode was overriden. + // Check if HideCursor was overridden. + if (CommandLineState.OverrideHideCursor is not null) + { + ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch + { + "never" => HideCursorMode.Never, + "onidle" => HideCursorMode.OnIdle, + "always" => HideCursorMode.Always, + _ => ConfigurationState.Instance.HideCursor.Value + }; + } + + // Check if docked mode was overridden. if (CommandLineState.OverrideDockedMode.HasValue) { ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index 65afa6e47..0fa7240b8 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Ui const int CursorHideIdleTime = 5; // seconds private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor); private long _lastCursorMoveTime; - private bool _hideCursorOnIdle; + private HideCursorMode _hideCursorMode; private InputManager _inputManager; private IKeyboard _keyboardInterface; private GraphicsDebugLevel _glLogLevel; @@ -113,10 +113,10 @@ namespace Ryujinx.Ui _gpuCancellationTokenSource = new CancellationTokenSource(); - _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; + _hideCursorMode = ConfigurationState.Instance.HideCursor; _lastCursorMoveTime = Stopwatch.GetTimestamp(); - ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged; + ConfigurationState.Instance.HideCursor.Event += HideCursorStateChanged; ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAnriAliasing; ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; @@ -145,26 +145,32 @@ namespace Ryujinx.Ui return Renderer.GetHardwareInfo().GpuVendor; } - private void HideCursorStateChanged(object sender, ReactiveEventArgs<bool> state) + private void HideCursorStateChanged(object sender, ReactiveEventArgs<HideCursorMode> state) { Application.Invoke(delegate { - _hideCursorOnIdle = state.NewValue; + _hideCursorMode = state.NewValue; - if (_hideCursorOnIdle) + switch (_hideCursorMode) { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - else - { - Window.Cursor = null; + case HideCursorMode.Never: + Window.Cursor = null; + break; + case HideCursorMode.OnIdle: + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + break; + case HideCursorMode.Always: + Window.Cursor = _invisibleCursor; + break; + default: + throw new ArgumentOutOfRangeException(); } }); } private void Renderer_Destroyed(object sender, EventArgs e) { - ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged; + ConfigurationState.Instance.HideCursor.Event -= HideCursorStateChanged; ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAnriAliasing; ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; @@ -180,7 +186,7 @@ namespace Ryujinx.Ui protected override bool OnMotionNotifyEvent(EventMotion evnt) { - if (_hideCursorOnIdle) + if (_hideCursorMode == HideCursorMode.OnIdle) { _lastCursorMoveTime = Stopwatch.GetTimestamp(); } @@ -315,15 +321,28 @@ namespace Ryujinx.Ui _toggleDockedMode = toggleDockedMode; - if (_hideCursorOnIdle && !ConfigurationState.Instance.Hid.EnableMouse) + if (ConfigurationState.Instance.Hid.EnableMouse.Value) { - long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; - Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; + if (_isMouseInClient) + { + Window.Cursor = _invisibleCursor; + } } - - if (ConfigurationState.Instance.Hid.EnableMouse && _isMouseInClient) + else { - Window.Cursor = _invisibleCursor; + switch (_hideCursorMode) + { + case HideCursorMode.OnIdle: + long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; + Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; + break; + case HideCursorMode.Always: + Window.Cursor = _invisibleCursor; + break; + case HideCursorMode.Never: + Window.Cursor = null; + break; + } } } @@ -775,4 +794,4 @@ namespace Ryujinx.Ui return state; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx/Ui/Windows/SettingsWindow.cs b/src/Ryujinx/Ui/Windows/SettingsWindow.cs index 27080bda3..3fb0447d3 100644 --- a/src/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/src/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -52,7 +52,9 @@ namespace Ryujinx.Ui.Windows [GUI] CheckButton _discordToggle; [GUI] CheckButton _checkUpdatesToggle; [GUI] CheckButton _showConfirmExitToggle; - [GUI] CheckButton _hideCursorOnIdleToggle; + [GUI] RadioButton _hideCursorNever; + [GUI] RadioButton _hideCursorOnIdle; + [GUI] RadioButton _hideCursorAlways; [GUI] CheckButton _vSyncToggle; [GUI] CheckButton _shaderCacheToggle; [GUI] CheckButton _textureRecompressionToggle; @@ -226,9 +228,17 @@ namespace Ryujinx.Ui.Windows _showConfirmExitToggle.Click(); } - if (ConfigurationState.Instance.HideCursorOnIdle) + switch (ConfigurationState.Instance.HideCursor.Value) { - _hideCursorOnIdleToggle.Click(); + case HideCursorMode.Never: + _hideCursorNever.Click(); + break; + case HideCursorMode.OnIdle: + _hideCursorOnIdle.Click(); + break; + case HideCursorMode.Always: + _hideCursorAlways.Click(); + break; } if (ConfigurationState.Instance.Graphics.EnableVsync) @@ -560,6 +570,18 @@ namespace Ryujinx.Ui.Windows _directoryChanged = false; } + HideCursorMode hideCursor = HideCursorMode.Never; + + if (_hideCursorOnIdle.Active) + { + hideCursor = HideCursorMode.OnIdle; + } + + if (_hideCursorAlways.Active) + { + hideCursor = HideCursorMode.Always; + } + if (!float.TryParse(_resScaleText.Buffer.Text, out float resScaleCustom) || resScaleCustom <= 0.0f) { resScaleCustom = 1.0f; @@ -602,7 +624,7 @@ namespace Ryujinx.Ui.Windows ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active; - ConfigurationState.Instance.HideCursorOnIdle.Value = _hideCursorOnIdleToggle.Active; + ConfigurationState.Instance.HideCursor.Value = hideCursor; ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active; @@ -813,4 +835,4 @@ namespace Ryujinx.Ui.Windows Dispose(); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx/Ui/Windows/SettingsWindow.glade b/src/Ryujinx/Ui/Windows/SettingsWindow.glade index 8ae6ea72f..0caa477bd 100644 --- a/src/Ryujinx/Ui/Windows/SettingsWindow.glade +++ b/src/Ryujinx/Ui/Windows/SettingsWindow.glade @@ -160,19 +160,83 @@ </packing> </child> <child> - <object class="GtkCheckButton" id="_hideCursorOnIdleToggle"> - <property name="label" translatable="yes">Hide Cursor On Idle</property> + <object class="GtkBox" id="_hideCursorBox"> <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="halign">start</property> - <property name="draw-indicator">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">end</property> + <property name="label" translatable="yes">Hide Cursor:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="_hideCursorNever"> + <property name="label" translatable="yes">Never</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="halign">start</property> + <property name="margin-top">5</property> + <property name="margin-bottom">5</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="_hideCursorOnIdle"> + <property name="label" translatable="yes">On Idle</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="halign">start</property> + <property name="margin-top">5</property> + <property name="margin-bottom">5</property> + <property name="draw-indicator">True</property> + <property name="group">_hideCursorNever</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="_hideCursorAlways"> + <property name="label" translatable="yes">Always</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="halign">start</property> + <property name="margin-top">5</property> + <property name="margin-bottom">5</property> + <property name="draw-indicator">True</property> + <property name="group">_hideCursorNever</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="padding">5</property> - <property name="position">3</property> + <property name="position">4</property> </packing> </child> </object> From cc1a933a2f4adf05a45c7adcf02669c4f423bedb Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 3 May 2023 02:07:16 +0200 Subject: [PATCH 484/737] ModLoader: Fix case sensitivy issues (#4720) * Fix case sensitivity for mod subdirectories * Small refactoring of ModLoader --- .../UI/ViewModels/MainWindowViewModel.cs | 8 +- .../UI/Windows/CheatWindow.axaml.cs | 4 +- src/Ryujinx.HLE/HOS/ModLoader.cs | 269 +++++++++--------- .../Extensions/LocalFileSystemExtensions.cs | 7 +- .../Processes/Extensions/NcaExtensions.cs | 5 +- .../Ui/Widgets/GameTableContextMenu.cs | 8 +- src/Ryujinx/Ui/Windows/CheatWindow.cs | 4 +- 7 files changed, 158 insertions(+), 147 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f4556bc3c..4ce479990 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1576,8 +1576,8 @@ namespace Ryujinx.Ava.UI.ViewModels { if (SelectedApplication != null) { - string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -1587,8 +1587,8 @@ namespace Ryujinx.Ava.UI.ViewModels { if (SelectedApplication != null) { - string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); + string sdModsBasePath = ModLoader.GetSdModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index cb939763b..241a6c346 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -35,8 +35,8 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); - string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId); ulong titleIdValue = ulong.Parse(titleId, System.Globalization.NumberStyles.HexNumber); _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index 165125414..a173ac418 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS } // Title independent mods - public class PatchCache + private class PatchCache { public List<Mod<DirectoryInfo>> NsoPatches { get; } public List<Mod<DirectoryInfo>> NroPatches { get; } @@ -107,14 +107,14 @@ namespace Ryujinx.HLE.HOS } } - public Dictionary<ulong, ModCache> AppMods; // key is TitleId - public PatchCache Patches; + private readonly Dictionary<ulong, ModCache> _appMods; // key is TitleId + private PatchCache _patches; - private static readonly EnumerationOptions _dirEnumOptions; + private static readonly EnumerationOptions DirEnumOptions; static ModLoader() { - _dirEnumOptions = new EnumerationOptions + DirEnumOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, MatchType = MatchType.Simple, @@ -125,37 +125,73 @@ namespace Ryujinx.HLE.HOS public ModLoader() { - AppMods = new Dictionary<ulong, ModCache>(); - Patches = new PatchCache(); + _appMods = new Dictionary<ulong, ModCache>(); + _patches = new PatchCache(); } - public void Clear() + private void Clear() { - AppMods.Clear(); - Patches = new PatchCache(); + _appMods.Clear(); + _patches = new PatchCache(); } private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); - public string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); - public string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); + public static string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); + public static string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); - private string EnsureBaseDirStructure(string modsBasePath) + private static string EnsureBaseDirStructure(string modsBasePath) { var modsDir = new DirectoryInfo(modsBasePath); modsDir.CreateSubdirectory(AmsContentsDir); modsDir.CreateSubdirectory(AmsNsoPatchDir); modsDir.CreateSubdirectory(AmsNroPatchDir); - // modsDir.CreateSubdirectory(AmsKipPatchDir); // uncomment when KIPs are supported + // TODO: uncomment when KIPs are supported + // modsDir.CreateSubdirectory(AmsKipPatchDir); return modsDir.FullName; } private static DirectoryInfo FindTitleDir(DirectoryInfo contentsDir, string titleId) - => contentsDir.EnumerateDirectories($"{titleId}*", _dirEnumOptions).FirstOrDefault(); + => contentsDir.EnumerateDirectories($"{titleId}*", DirEnumOptions).FirstOrDefault(); - public string GetTitleDir(string modsBasePath, string titleId) + private static void AddModsFromDirectory(ModCache mods, DirectoryInfo dir, string titleId) + { + System.Text.StringBuilder types = new(); + + foreach (var modDir in dir.EnumerateDirectories()) + { + types.Clear(); + Mod<DirectoryInfo> mod = new("", null); + + if (StrEquals(RomfsDir, modDir.Name)) + { + mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} RomFs>", modDir)); + types.Append('R'); + } + else if (StrEquals(ExefsDir, modDir.Name)) + { + mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} ExeFs>", modDir)); + types.Append('E'); + } + else if (StrEquals(CheatDir, modDir.Name)) + { + types.Append('C', QueryCheatsDir(mods, modDir)); + } + else + { + AddModsFromDirectory(mods, modDir, titleId); + } + + if (types.Length > 0) + { + Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); + } + } + } + + public static string GetTitleDir(string modsBasePath, string titleId) { var contentsDir = new DirectoryInfo(Path.Combine(modsBasePath, AmsContentsDir)); var titleModsPath = FindTitleDir(contentsDir, titleId); @@ -170,17 +206,32 @@ namespace Ryujinx.HLE.HOS } // Static Query Methods - public static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) + private static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) { - if (cache.Initialized || !patchDir.Exists) return; + if (cache.Initialized || !patchDir.Exists) + { + return; + } - var patches = cache.KipPatches; - string type = null; + List<Mod<DirectoryInfo>> patches; + string type; - if (StrEquals(AmsNsoPatchDir, patchDir.Name)) { patches = cache.NsoPatches; type = "NSO"; } - else if (StrEquals(AmsNroPatchDir, patchDir.Name)) { patches = cache.NroPatches; type = "NRO"; } - else if (StrEquals(AmsKipPatchDir, patchDir.Name)) { patches = cache.KipPatches; type = "KIP"; } - else return; + if (StrEquals(AmsNsoPatchDir, patchDir.Name)) + { + patches = cache.NsoPatches; type = "NSO"; + } + else if (StrEquals(AmsNroPatchDir, patchDir.Name)) + { + patches = cache.NroPatches; type = "NRO"; + } + else if (StrEquals(AmsKipPatchDir, patchDir.Name)) + { + patches = cache.KipPatches; type = "KIP"; + } + else + { + return; + } foreach (var modDir in patchDir.EnumerateDirectories()) { @@ -189,9 +240,12 @@ namespace Ryujinx.HLE.HOS } } - public static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) + private static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) { - if (!titleDir.Exists) return; + if (!titleDir.Exists) + { + return; + } var fsFile = new FileInfo(Path.Combine(titleDir.FullName, RomfsContainer)); if (fsFile.Exists) @@ -205,64 +259,15 @@ namespace Ryujinx.HLE.HOS mods.ExefsContainers.Add(new Mod<FileInfo>($"<{titleDir.Name} ExeFs>", fsFile)); } - System.Text.StringBuilder types = new System.Text.StringBuilder(5); - - foreach (var modDir in titleDir.EnumerateDirectories()) - { - types.Clear(); - Mod<DirectoryInfo> mod = new Mod<DirectoryInfo>("", null); - - if (StrEquals(RomfsDir, modDir.Name)) - { - mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleDir.Name} RomFs>", modDir)); - types.Append('R'); - } - else if (StrEquals(ExefsDir, modDir.Name)) - { - mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleDir.Name} ExeFs>", modDir)); - types.Append('E'); - } - else if (StrEquals(CheatDir, modDir.Name)) - { - for (int i = 0; i < QueryCheatsDir(mods, modDir); i++) - { - types.Append('C'); - } - } - else - { - var romfs = new DirectoryInfo(Path.Combine(modDir.FullName, RomfsDir)); - var exefs = new DirectoryInfo(Path.Combine(modDir.FullName, ExefsDir)); - var cheat = new DirectoryInfo(Path.Combine(modDir.FullName, CheatDir)); - - if (romfs.Exists) - { - mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(modDir.Name, romfs)); - types.Append('R'); - } - - if (exefs.Exists) - { - mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(modDir.Name, exefs)); - types.Append('E'); - } - - if (cheat.Exists) - { - for (int i = 0; i < QueryCheatsDir(mods, cheat); i++) - { - types.Append('C'); - } - } - } - - if (types.Length > 0) Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); - } + AddModsFromDirectory(mods, titleDir, titleDir.Name); } public static void QueryContentsDir(ModCache mods, DirectoryInfo contentsDir, ulong titleId) { - if (!contentsDir.Exists) return; + if (!contentsDir.Exists) + { + return; + } Logger.Info?.Print(LogClass.ModLoader, $"Searching mods for {((titleId & 0x1000) != 0 ? "DLC" : "Title")} {titleId:X16}"); @@ -302,9 +307,16 @@ namespace Ryujinx.HLE.HOS continue; } + int oldCheatsCount = mods.Cheats.Count; + // A cheat file can contain several cheats for the same executable, so the file must be parsed in // order to properly enumerate them. mods.Cheats.AddRange(GetCheatsInFile(file)); + + if (mods.Cheats.Count - oldCheatsCount > 0) + { + numMods++; + } } return numMods; @@ -313,57 +325,54 @@ namespace Ryujinx.HLE.HOS private static IEnumerable<Cheat> GetCheatsInFile(FileInfo cheatFile) { string cheatName = DefaultCheatName; - List<string> instructions = new List<string>(); - List<Cheat> cheats = new List<Cheat>(); + List<string> instructions = new(); + List<Cheat> cheats = new(); - using (StreamReader cheatData = cheatFile.OpenText()) + using StreamReader cheatData = cheatFile.OpenText(); + while (cheatData.ReadLine() is { } line) { - string line; - while ((line = cheatData.ReadLine()) != null) + line = line.Trim(); + + if (line.StartsWith('[')) { - line = line.Trim(); - - if (line.StartsWith('[')) + // This line starts a new cheat section. + if (!line.EndsWith(']') || line.Length < 3) { - // This line starts a new cheat section. - if (!line.EndsWith(']') || line.Length < 3) - { - // Skip the entire file if there's any error while parsing the cheat file. + // Skip the entire file if there's any error while parsing the cheat file. - Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); + Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); - return new List<Cheat>(); - } - - // Add the previous section to the list. - if (instructions.Count != 0) - { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); - } - - // Start a new cheat section. - cheatName = line.Substring(1, line.Length - 2); - instructions = new List<string>(); + return Array.Empty<Cheat>(); } - else if (line.Length > 0) + + // Add the previous section to the list. + if (instructions.Count > 0) { - // The line contains an instruction. - instructions.Add(line); + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); } + + // Start a new cheat section. + cheatName = line.Substring(1, line.Length - 2); + instructions.Clear(); } - - // Add the last section being processed. - if (instructions.Count != 0) + else if (line.Length > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + // The line contains an instruction. + instructions.Add(line); } } + // Add the last section being processed. + if (instructions.Count > 0) + { + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + } + return cheats; } // Assumes searchDirPaths don't overlap - public static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths) + private static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths) { static bool IsPatchesDir(string name) => StrEquals(AmsNsoPatchDir, name) || StrEquals(AmsNroPatchDir, name) || @@ -375,7 +384,7 @@ namespace Ryujinx.HLE.HOS { if (IsContentsDir(searchDir.Name)) { - foreach (var (titleId, cache) in modCaches) + foreach ((ulong titleId, ModCache cache) in modCaches) { QueryContentsDir(cache, searchDir, titleId); } @@ -419,15 +428,15 @@ namespace Ryujinx.HLE.HOS foreach (ulong titleId in titles) { - AppMods[titleId] = new ModCache(); + _appMods[titleId] = new ModCache(); } - CollectMods(AppMods, Patches, searchDirPaths); + CollectMods(_appMods, _patches, searchDirPaths); } internal IStorage ApplyRomFsMods(ulong titleId, IStorage baseStorage) { - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) { return baseStorage; } @@ -487,7 +496,7 @@ namespace Ryujinx.HLE.HOS return newStorage; } - private static void AddFiles(IFileSystem fs, string modName, HashSet<string> fileSet, RomFsBuilder builder) + private static void AddFiles(IFileSystem fs, string modName, ISet<string> fileSet, RomFsBuilder builder) { foreach (var entry in fs.EnumerateEntries() .Where(f => f.Type == DirectoryEntryType.File) @@ -509,7 +518,7 @@ namespace Ryujinx.HLE.HOS internal bool ReplaceExefsPartition(ulong titleId, ref IFileSystem exefs) { - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) { return false; } @@ -537,13 +546,13 @@ namespace Ryujinx.HLE.HOS internal ModLoadResult ApplyExefsMods(ulong titleId, NsoExecutable[] nsos) { - ModLoadResult modLoadResult = new ModLoadResult + ModLoadResult modLoadResult = new() { Stubs = new BitVector32(), Replaces = new BitVector32() }; - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) { return modLoadResult; } @@ -561,7 +570,7 @@ namespace Ryujinx.HLE.HOS { var nsoName = ProcessConst.ExeFsPrefixes[i]; - FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName)); + FileInfo nsoFile = new(Path.Combine(mod.Path.FullName, nsoName)); if (nsoFile.Exists) { if (modLoadResult.Replaces[1 << i]) @@ -580,7 +589,7 @@ namespace Ryujinx.HLE.HOS modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension)); } - FileInfo npdmFile = new FileInfo(Path.Combine(mod.Path.FullName, "main.npdm")); + FileInfo npdmFile = new(Path.Combine(mod.Path.FullName, "main.npdm")); if (npdmFile.Exists) { if (modLoadResult.Npdm != null) @@ -611,7 +620,7 @@ namespace Ryujinx.HLE.HOS internal void ApplyNroPatches(NroExecutable nro) { - var nroPatches = Patches.NroPatches; + var nroPatches = _patches.NroPatches; if (nroPatches.Count == 0) return; @@ -622,9 +631,9 @@ namespace Ryujinx.HLE.HOS internal bool ApplyNsoPatches(ulong titleId, params IExecutable[] programs) { - IEnumerable<Mod<DirectoryInfo>> nsoMods = Patches.NsoPatches; + IEnumerable<Mod<DirectoryInfo>> nsoMods = _patches.NsoPatches; - if (AppMods.TryGetValue(titleId, out ModCache mods)) + if (_appMods.TryGetValue(titleId, out ModCache mods)) { nsoMods = nsoMods.Concat(mods.ExefsDirs); } @@ -636,7 +645,7 @@ namespace Ryujinx.HLE.HOS internal void LoadCheats(ulong titleId, ProcessTamperInfo tamperInfo, TamperMachine tamperMachine) { - if (tamperInfo == null || tamperInfo.BuildIds == null || tamperInfo.CodeAddresses == null) + if (tamperInfo?.BuildIds == null || tamperInfo.CodeAddresses == null) { Logger.Error?.Print(LogClass.ModLoader, "Unable to install cheat because the associated process is invalid"); @@ -645,14 +654,14 @@ namespace Ryujinx.HLE.HOS Logger.Info?.Print(LogClass.ModLoader, $"Build ids found for title {titleId:X16}:\n {String.Join("\n ", tamperInfo.BuildIds)}"); - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) { return; } var cheats = mods.Cheats; var processExes = tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses, (k, v) => new { k, v }) - .ToDictionary(x => x.k.Substring(0, Math.Min(Cheat.CheatIdSize, x.k.Length)), x => x.v); + .ToDictionary(x => x.k[..Math.Min(Cheat.CheatIdSize, x.k.Length)], x => x.v); foreach (var cheat in cheats) { @@ -758,4 +767,4 @@ namespace Ryujinx.HLE.HOS return count > 0; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs index 28d907851..fb85329d2 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -2,6 +2,7 @@ using LibHac.FsSystem; using LibHac.Loader; using LibHac.Ns; +using Ryujinx.HLE.HOS; using Ryujinx.HLE.Loaders.Processes.Extensions; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -17,8 +18,8 @@ namespace Ryujinx.HLE.Loaders.Processes device.Configuration.VirtualFileSystem.ModLoader.CollectMods( new[] { programId }, - device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + ModLoader.GetModsBasePath(), + ModLoader.GetSdModsBasePath()); if (programId != 0) { @@ -36,4 +37,4 @@ namespace Ryujinx.HLE.Loaders.Processes return processResult; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index 473f374db..e11b81d7f 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -8,6 +8,7 @@ using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS; using System.IO; using System.Linq; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -35,8 +36,8 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions // Collecting mods related to AocTitleIds and ProgramId. device.Configuration.VirtualFileSystem.ModLoader.CollectMods( device.Configuration.ContentManager.GetAocTitleIds().Prepend(metaLoader.GetProgramId()), - device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + ModLoader.GetModsBasePath(), + ModLoader.GetSdModsBasePath()); // Load Nacp file. var nacpData = new BlitStruct<ApplicationControlProperty>(1); diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 558288aab..6d8331658 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -460,16 +460,16 @@ namespace Ryujinx.Ui.Widgets private void OpenTitleModDir_Clicked(object sender, EventArgs args) { - string modsBasePath = _virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, _titleIdText); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) { - string sdModsBasePath = _virtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); + string sdModsBasePath = ModLoader.GetSdModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs index 917603b29..7dbea0128 100644 --- a/src/Ryujinx/Ui/Windows/CheatWindow.cs +++ b/src/Ryujinx/Ui/Windows/CheatWindow.cs @@ -28,8 +28,8 @@ namespace Ryujinx.Ui.Windows builder.Autoconnect(this); _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; - string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt"); From 7c327fecb3794ee41df6f3b82e8d18e0942ad1b9 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Wed, 3 May 2023 09:42:21 +0100 Subject: [PATCH 485/737] Vulkan: Record modifications after changing the framebuffer (#4775) Our Vulkan backend inserts image barriers when a texture is sampled after it is rendered. This is done via a "modification flag" which is set when a render target is unbound (presuming that a texture has finished drawing to it). Imagine the following scenario: - Game sets render target to texture A - Game renders to texture A - (render pass ends) - Game binds texture A to a sampler - Game sets render target to texture B - Renders to texture B using texture A (barrier required) Because of the previous behaviour, the check to add a barrier for sampling a texture actually happens before it is registered as modified, meaning no barrier was added at all. This isn't always the case, but it was definitely causing issues in Xenoblade 2. This doesn't fix any more complicated issues where a texture is repeatedly sampled while it is currently being rendered. Fixes visual glitches at lower resolutions in Xenoblade 2. May fix other cases. --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c54d7980c..cf31a7f83 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1015,8 +1015,8 @@ namespace Ryujinx.Graphics.Vulkan private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) { - FramebufferParams?.UpdateModifications(); CreateFramebuffer(colors, depthStencil, filterWriteMasked); + FramebufferParams?.UpdateModifications(); CreateRenderPass(); SignalStateChange(); SignalAttachmentChange(); From b7d2bff6aab6a2f8eb501af2a12fac87d2834f06 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 3 May 2023 11:20:05 +0200 Subject: [PATCH 486/737] Revert "ModLoader: Fix case sensitivy issues (#4720)" (#4781) This reverts commit cc1a933a2f4adf05a45c7adcf02669c4f423bedb. --- .../UI/ViewModels/MainWindowViewModel.cs | 8 +- .../UI/Windows/CheatWindow.axaml.cs | 4 +- src/Ryujinx.HLE/HOS/ModLoader.cs | 269 +++++++++--------- .../Extensions/LocalFileSystemExtensions.cs | 7 +- .../Processes/Extensions/NcaExtensions.cs | 5 +- .../Ui/Widgets/GameTableContextMenu.cs | 8 +- src/Ryujinx/Ui/Windows/CheatWindow.cs | 4 +- 7 files changed, 147 insertions(+), 158 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 4ce479990..f4556bc3c 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1576,8 +1576,8 @@ namespace Ryujinx.Ava.UI.ViewModels { if (SelectedApplication != null) { - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); + string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); + string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -1587,8 +1587,8 @@ namespace Ryujinx.Ava.UI.ViewModels { if (SelectedApplication != null) { - string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); + string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); + string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index 241a6c346..cb939763b 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -35,8 +35,8 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId); + string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); + string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId); ulong titleIdValue = ulong.Parse(titleId, System.Globalization.NumberStyles.HexNumber); _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index a173ac418..165125414 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS } // Title independent mods - private class PatchCache + public class PatchCache { public List<Mod<DirectoryInfo>> NsoPatches { get; } public List<Mod<DirectoryInfo>> NroPatches { get; } @@ -107,14 +107,14 @@ namespace Ryujinx.HLE.HOS } } - private readonly Dictionary<ulong, ModCache> _appMods; // key is TitleId - private PatchCache _patches; + public Dictionary<ulong, ModCache> AppMods; // key is TitleId + public PatchCache Patches; - private static readonly EnumerationOptions DirEnumOptions; + private static readonly EnumerationOptions _dirEnumOptions; static ModLoader() { - DirEnumOptions = new EnumerationOptions + _dirEnumOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, MatchType = MatchType.Simple, @@ -125,73 +125,37 @@ namespace Ryujinx.HLE.HOS public ModLoader() { - _appMods = new Dictionary<ulong, ModCache>(); - _patches = new PatchCache(); + AppMods = new Dictionary<ulong, ModCache>(); + Patches = new PatchCache(); } - private void Clear() + public void Clear() { - _appMods.Clear(); - _patches = new PatchCache(); + AppMods.Clear(); + Patches = new PatchCache(); } private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); - public static string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); - public static string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); + public string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); + public string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); - private static string EnsureBaseDirStructure(string modsBasePath) + private string EnsureBaseDirStructure(string modsBasePath) { var modsDir = new DirectoryInfo(modsBasePath); modsDir.CreateSubdirectory(AmsContentsDir); modsDir.CreateSubdirectory(AmsNsoPatchDir); modsDir.CreateSubdirectory(AmsNroPatchDir); - // TODO: uncomment when KIPs are supported - // modsDir.CreateSubdirectory(AmsKipPatchDir); + // modsDir.CreateSubdirectory(AmsKipPatchDir); // uncomment when KIPs are supported return modsDir.FullName; } private static DirectoryInfo FindTitleDir(DirectoryInfo contentsDir, string titleId) - => contentsDir.EnumerateDirectories($"{titleId}*", DirEnumOptions).FirstOrDefault(); + => contentsDir.EnumerateDirectories($"{titleId}*", _dirEnumOptions).FirstOrDefault(); - private static void AddModsFromDirectory(ModCache mods, DirectoryInfo dir, string titleId) - { - System.Text.StringBuilder types = new(); - - foreach (var modDir in dir.EnumerateDirectories()) - { - types.Clear(); - Mod<DirectoryInfo> mod = new("", null); - - if (StrEquals(RomfsDir, modDir.Name)) - { - mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} RomFs>", modDir)); - types.Append('R'); - } - else if (StrEquals(ExefsDir, modDir.Name)) - { - mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} ExeFs>", modDir)); - types.Append('E'); - } - else if (StrEquals(CheatDir, modDir.Name)) - { - types.Append('C', QueryCheatsDir(mods, modDir)); - } - else - { - AddModsFromDirectory(mods, modDir, titleId); - } - - if (types.Length > 0) - { - Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); - } - } - } - - public static string GetTitleDir(string modsBasePath, string titleId) + public string GetTitleDir(string modsBasePath, string titleId) { var contentsDir = new DirectoryInfo(Path.Combine(modsBasePath, AmsContentsDir)); var titleModsPath = FindTitleDir(contentsDir, titleId); @@ -206,32 +170,17 @@ namespace Ryujinx.HLE.HOS } // Static Query Methods - private static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) + public static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) { - if (cache.Initialized || !patchDir.Exists) - { - return; - } + if (cache.Initialized || !patchDir.Exists) return; - List<Mod<DirectoryInfo>> patches; - string type; + var patches = cache.KipPatches; + string type = null; - if (StrEquals(AmsNsoPatchDir, patchDir.Name)) - { - patches = cache.NsoPatches; type = "NSO"; - } - else if (StrEquals(AmsNroPatchDir, patchDir.Name)) - { - patches = cache.NroPatches; type = "NRO"; - } - else if (StrEquals(AmsKipPatchDir, patchDir.Name)) - { - patches = cache.KipPatches; type = "KIP"; - } - else - { - return; - } + if (StrEquals(AmsNsoPatchDir, patchDir.Name)) { patches = cache.NsoPatches; type = "NSO"; } + else if (StrEquals(AmsNroPatchDir, patchDir.Name)) { patches = cache.NroPatches; type = "NRO"; } + else if (StrEquals(AmsKipPatchDir, patchDir.Name)) { patches = cache.KipPatches; type = "KIP"; } + else return; foreach (var modDir in patchDir.EnumerateDirectories()) { @@ -240,12 +189,9 @@ namespace Ryujinx.HLE.HOS } } - private static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) + public static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) { - if (!titleDir.Exists) - { - return; - } + if (!titleDir.Exists) return; var fsFile = new FileInfo(Path.Combine(titleDir.FullName, RomfsContainer)); if (fsFile.Exists) @@ -259,15 +205,64 @@ namespace Ryujinx.HLE.HOS mods.ExefsContainers.Add(new Mod<FileInfo>($"<{titleDir.Name} ExeFs>", fsFile)); } - AddModsFromDirectory(mods, titleDir, titleDir.Name); + System.Text.StringBuilder types = new System.Text.StringBuilder(5); + + foreach (var modDir in titleDir.EnumerateDirectories()) + { + types.Clear(); + Mod<DirectoryInfo> mod = new Mod<DirectoryInfo>("", null); + + if (StrEquals(RomfsDir, modDir.Name)) + { + mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleDir.Name} RomFs>", modDir)); + types.Append('R'); + } + else if (StrEquals(ExefsDir, modDir.Name)) + { + mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleDir.Name} ExeFs>", modDir)); + types.Append('E'); + } + else if (StrEquals(CheatDir, modDir.Name)) + { + for (int i = 0; i < QueryCheatsDir(mods, modDir); i++) + { + types.Append('C'); + } + } + else + { + var romfs = new DirectoryInfo(Path.Combine(modDir.FullName, RomfsDir)); + var exefs = new DirectoryInfo(Path.Combine(modDir.FullName, ExefsDir)); + var cheat = new DirectoryInfo(Path.Combine(modDir.FullName, CheatDir)); + + if (romfs.Exists) + { + mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(modDir.Name, romfs)); + types.Append('R'); + } + + if (exefs.Exists) + { + mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(modDir.Name, exefs)); + types.Append('E'); + } + + if (cheat.Exists) + { + for (int i = 0; i < QueryCheatsDir(mods, cheat); i++) + { + types.Append('C'); + } + } + } + + if (types.Length > 0) Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); + } } public static void QueryContentsDir(ModCache mods, DirectoryInfo contentsDir, ulong titleId) { - if (!contentsDir.Exists) - { - return; - } + if (!contentsDir.Exists) return; Logger.Info?.Print(LogClass.ModLoader, $"Searching mods for {((titleId & 0x1000) != 0 ? "DLC" : "Title")} {titleId:X16}"); @@ -307,16 +302,9 @@ namespace Ryujinx.HLE.HOS continue; } - int oldCheatsCount = mods.Cheats.Count; - // A cheat file can contain several cheats for the same executable, so the file must be parsed in // order to properly enumerate them. mods.Cheats.AddRange(GetCheatsInFile(file)); - - if (mods.Cheats.Count - oldCheatsCount > 0) - { - numMods++; - } } return numMods; @@ -325,54 +313,57 @@ namespace Ryujinx.HLE.HOS private static IEnumerable<Cheat> GetCheatsInFile(FileInfo cheatFile) { string cheatName = DefaultCheatName; - List<string> instructions = new(); - List<Cheat> cheats = new(); + List<string> instructions = new List<string>(); + List<Cheat> cheats = new List<Cheat>(); - using StreamReader cheatData = cheatFile.OpenText(); - while (cheatData.ReadLine() is { } line) + using (StreamReader cheatData = cheatFile.OpenText()) { - line = line.Trim(); - - if (line.StartsWith('[')) + string line; + while ((line = cheatData.ReadLine()) != null) { - // This line starts a new cheat section. - if (!line.EndsWith(']') || line.Length < 3) + line = line.Trim(); + + if (line.StartsWith('[')) { - // Skip the entire file if there's any error while parsing the cheat file. + // This line starts a new cheat section. + if (!line.EndsWith(']') || line.Length < 3) + { + // Skip the entire file if there's any error while parsing the cheat file. - Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); + Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); - return Array.Empty<Cheat>(); + return new List<Cheat>(); + } + + // Add the previous section to the list. + if (instructions.Count != 0) + { + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + } + + // Start a new cheat section. + cheatName = line.Substring(1, line.Length - 2); + instructions = new List<string>(); } - - // Add the previous section to the list. - if (instructions.Count > 0) + else if (line.Length > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + // The line contains an instruction. + instructions.Add(line); } - - // Start a new cheat section. - cheatName = line.Substring(1, line.Length - 2); - instructions.Clear(); } - else if (line.Length > 0) + + // Add the last section being processed. + if (instructions.Count != 0) { - // The line contains an instruction. - instructions.Add(line); + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); } } - // Add the last section being processed. - if (instructions.Count > 0) - { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); - } - return cheats; } // Assumes searchDirPaths don't overlap - private static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths) + public static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths) { static bool IsPatchesDir(string name) => StrEquals(AmsNsoPatchDir, name) || StrEquals(AmsNroPatchDir, name) || @@ -384,7 +375,7 @@ namespace Ryujinx.HLE.HOS { if (IsContentsDir(searchDir.Name)) { - foreach ((ulong titleId, ModCache cache) in modCaches) + foreach (var (titleId, cache) in modCaches) { QueryContentsDir(cache, searchDir, titleId); } @@ -428,15 +419,15 @@ namespace Ryujinx.HLE.HOS foreach (ulong titleId in titles) { - _appMods[titleId] = new ModCache(); + AppMods[titleId] = new ModCache(); } - CollectMods(_appMods, _patches, searchDirPaths); + CollectMods(AppMods, Patches, searchDirPaths); } internal IStorage ApplyRomFsMods(ulong titleId, IStorage baseStorage) { - if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) + if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) { return baseStorage; } @@ -496,7 +487,7 @@ namespace Ryujinx.HLE.HOS return newStorage; } - private static void AddFiles(IFileSystem fs, string modName, ISet<string> fileSet, RomFsBuilder builder) + private static void AddFiles(IFileSystem fs, string modName, HashSet<string> fileSet, RomFsBuilder builder) { foreach (var entry in fs.EnumerateEntries() .Where(f => f.Type == DirectoryEntryType.File) @@ -518,7 +509,7 @@ namespace Ryujinx.HLE.HOS internal bool ReplaceExefsPartition(ulong titleId, ref IFileSystem exefs) { - if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) + if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) { return false; } @@ -546,13 +537,13 @@ namespace Ryujinx.HLE.HOS internal ModLoadResult ApplyExefsMods(ulong titleId, NsoExecutable[] nsos) { - ModLoadResult modLoadResult = new() + ModLoadResult modLoadResult = new ModLoadResult { Stubs = new BitVector32(), Replaces = new BitVector32() }; - if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) + if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) { return modLoadResult; } @@ -570,7 +561,7 @@ namespace Ryujinx.HLE.HOS { var nsoName = ProcessConst.ExeFsPrefixes[i]; - FileInfo nsoFile = new(Path.Combine(mod.Path.FullName, nsoName)); + FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName)); if (nsoFile.Exists) { if (modLoadResult.Replaces[1 << i]) @@ -589,7 +580,7 @@ namespace Ryujinx.HLE.HOS modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension)); } - FileInfo npdmFile = new(Path.Combine(mod.Path.FullName, "main.npdm")); + FileInfo npdmFile = new FileInfo(Path.Combine(mod.Path.FullName, "main.npdm")); if (npdmFile.Exists) { if (modLoadResult.Npdm != null) @@ -620,7 +611,7 @@ namespace Ryujinx.HLE.HOS internal void ApplyNroPatches(NroExecutable nro) { - var nroPatches = _patches.NroPatches; + var nroPatches = Patches.NroPatches; if (nroPatches.Count == 0) return; @@ -631,9 +622,9 @@ namespace Ryujinx.HLE.HOS internal bool ApplyNsoPatches(ulong titleId, params IExecutable[] programs) { - IEnumerable<Mod<DirectoryInfo>> nsoMods = _patches.NsoPatches; + IEnumerable<Mod<DirectoryInfo>> nsoMods = Patches.NsoPatches; - if (_appMods.TryGetValue(titleId, out ModCache mods)) + if (AppMods.TryGetValue(titleId, out ModCache mods)) { nsoMods = nsoMods.Concat(mods.ExefsDirs); } @@ -645,7 +636,7 @@ namespace Ryujinx.HLE.HOS internal void LoadCheats(ulong titleId, ProcessTamperInfo tamperInfo, TamperMachine tamperMachine) { - if (tamperInfo?.BuildIds == null || tamperInfo.CodeAddresses == null) + if (tamperInfo == null || tamperInfo.BuildIds == null || tamperInfo.CodeAddresses == null) { Logger.Error?.Print(LogClass.ModLoader, "Unable to install cheat because the associated process is invalid"); @@ -654,14 +645,14 @@ namespace Ryujinx.HLE.HOS Logger.Info?.Print(LogClass.ModLoader, $"Build ids found for title {titleId:X16}:\n {String.Join("\n ", tamperInfo.BuildIds)}"); - if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) + if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) { return; } var cheats = mods.Cheats; var processExes = tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses, (k, v) => new { k, v }) - .ToDictionary(x => x.k[..Math.Min(Cheat.CheatIdSize, x.k.Length)], x => x.v); + .ToDictionary(x => x.k.Substring(0, Math.Min(Cheat.CheatIdSize, x.k.Length)), x => x.v); foreach (var cheat in cheats) { @@ -767,4 +758,4 @@ namespace Ryujinx.HLE.HOS return count > 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs index fb85329d2..28d907851 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -2,7 +2,6 @@ using LibHac.FsSystem; using LibHac.Loader; using LibHac.Ns; -using Ryujinx.HLE.HOS; using Ryujinx.HLE.Loaders.Processes.Extensions; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -18,8 +17,8 @@ namespace Ryujinx.HLE.Loaders.Processes device.Configuration.VirtualFileSystem.ModLoader.CollectMods( new[] { programId }, - ModLoader.GetModsBasePath(), - ModLoader.GetSdModsBasePath()); + device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), + device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); if (programId != 0) { @@ -37,4 +36,4 @@ namespace Ryujinx.HLE.Loaders.Processes return processResult; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index e11b81d7f..473f374db 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -8,7 +8,6 @@ using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS; using System.IO; using System.Linq; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -36,8 +35,8 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions // Collecting mods related to AocTitleIds and ProgramId. device.Configuration.VirtualFileSystem.ModLoader.CollectMods( device.Configuration.ContentManager.GetAocTitleIds().Prepend(metaLoader.GetProgramId()), - ModLoader.GetModsBasePath(), - ModLoader.GetSdModsBasePath()); + device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), + device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); // Load Nacp file. var nacpData = new BlitStruct<ApplicationControlProperty>(1); diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 6d8331658..558288aab 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -460,16 +460,16 @@ namespace Ryujinx.Ui.Widgets private void OpenTitleModDir_Clicked(object sender, EventArgs args) { - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText); + string modsBasePath = _virtualFileSystem.ModLoader.GetModsBasePath(); + string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) { - string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); + string sdModsBasePath = _virtualFileSystem.ModLoader.GetSdModsBasePath(); + string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs index 7dbea0128..917603b29 100644 --- a/src/Ryujinx/Ui/Windows/CheatWindow.cs +++ b/src/Ryujinx/Ui/Windows/CheatWindow.cs @@ -28,8 +28,8 @@ namespace Ryujinx.Ui.Windows builder.Autoconnect(this); _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); + string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); + string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt"); From 6279f5e4305b1dd5844c0a0e3538fcdd72e6bf33 Mon Sep 17 00:00:00 2001 From: Erdem Keskin <erdemkeskines@gmail.com> Date: Wed, 3 May 2023 17:04:40 +0300 Subject: [PATCH 487/737] Update SettingsWindow.cs (#4785) fix saving if directory path directly pasted in to the text field instead of using FileChooser. --- src/Ryujinx/Ui/Windows/SettingsWindow.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx/Ui/Windows/SettingsWindow.cs b/src/Ryujinx/Ui/Windows/SettingsWindow.cs index 3fb0447d3..7d39b9ea1 100644 --- a/src/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/src/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -720,6 +720,7 @@ namespace Ryujinx.Ui.Windows if (Directory.Exists(_addGameDirBox.Buffer.Text)) { _gameDirsBoxStore.AppendValues(_addGameDirBox.Buffer.Text); + _directoryChanged = true; } else { @@ -835,4 +836,4 @@ namespace Ryujinx.Ui.Windows Dispose(); } } -} \ No newline at end of file +} From 4d1579acbf60ff23f14b591db762267f93092d0c Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 3 May 2023 21:20:12 -0300 Subject: [PATCH 488/737] Fix some invalid blits involving depth textures (#4723) --- src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs | 9 ++++++++- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 8 ++++++++ src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs | 8 ++++---- src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs | 5 +++-- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs index 4ce53e786..2ac84ab50 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs @@ -300,11 +300,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod IsCopyRegionComplete(srcCopyTexture, srcCopyTextureFormat, srcX1, srcY1, srcX2, srcY2) && IsCopyRegionComplete(dstCopyTexture, dstCopyTextureFormat, dstX1, dstY1, dstX2, dstY2); + // We can only allow aliasing of color formats as depth if the source and destination textures + // are the same, as we can't blit between different depth formats. + bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format; + var srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture( memoryManager, srcCopyTexture, offset, srcCopyTextureFormat, + srcDepthAlias, !canDirectCopy, false, srcHint); @@ -325,6 +330,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod // When the source texture that was found has a depth format, // we must enforce the target texture also has a depth format, // as copies between depth and color formats are not allowed. + // For depth blit, the destination texture format should always match exactly. if (srcTexture.Format.IsDepthOrStencil()) { @@ -340,7 +346,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod dstCopyTexture, 0, dstCopyTextureFormat, - true, + depthAlias: false, + shouldCreate: true, srcTexture.ScaleMode == TextureScaleMode.Scaled, dstHint); diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 6c9de8d6a..0427d09b2 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1118,7 +1118,7 @@ namespace Ryujinx.Graphics.Gpu.Image { bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0; - TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0); + TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.DepthAlias) != 0); if (matchQuality == TextureMatchQuality.NoMatch) { diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index fc2c07e55..49191239f 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -249,6 +249,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="copyTexture">Copy texture to find or create</param> /// <param name="offset">Offset to be added to the physical texture address</param> /// <param name="formatInfo">Format information of the copy texture</param> + /// <param name="depthAlias">Indicates if aliasing between color and depth format should be allowed</param> + /// <param name="shouldCreate">Indicates if a new texture should be created if none is found on the cache</param> /// <param name="preferScaling">Indicates if the texture should be scaled from the start</param> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param> /// <returns>The texture</returns> @@ -257,6 +259,7 @@ namespace Ryujinx.Graphics.Gpu.Image TwodTexture copyTexture, ulong offset, FormatInfo formatInfo, + bool depthAlias, bool shouldCreate, bool preferScaling, Size sizeHint) @@ -293,6 +296,11 @@ namespace Ryujinx.Graphics.Gpu.Image TextureSearchFlags flags = TextureSearchFlags.ForCopy; + if (depthAlias) + { + flags |= TextureSearchFlags.DepthAlias; + } + if (preferScaling) { flags |= TextureSearchFlags.WithUpscale; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index e93ea0c0c..5d8462220 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -220,18 +220,18 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="lhs">Texture information to compare</param> /// <param name="rhs">Texture information to compare with</param> /// <param name="forSampler">Indicates that the texture will be used for shader sampling</param> - /// <param name="forCopy">Indicates that the texture will be used as copy source or target</param> + /// <param name="depthAlias">Indicates if aliasing between color and depth format should be allowed</param> /// <returns>A value indicating how well the formats match</returns> - public static TextureMatchQuality FormatMatches(TextureInfo lhs, TextureInfo rhs, bool forSampler, bool forCopy) + public static TextureMatchQuality FormatMatches(TextureInfo lhs, TextureInfo rhs, bool forSampler, bool depthAlias) { // D32F and R32F texture have the same representation internally, // however the R32F format is used to sample from depth textures. - if (lhs.FormatInfo.Format == Format.D32Float && rhs.FormatInfo.Format == Format.R32Float && (forSampler || forCopy)) + if (lhs.FormatInfo.Format == Format.D32Float && rhs.FormatInfo.Format == Format.R32Float && (forSampler || depthAlias)) { return TextureMatchQuality.FormatAlias; } - if (forCopy) + if (depthAlias) { // The 2D engine does not support depth-stencil formats, so it will instead // use equivalent color formats. We must also consider them as compatible. diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs index 890bf1736..d7b99a173 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs @@ -11,7 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Image None = 0, ForSampler = 1 << 1, ForCopy = 1 << 2, - WithUpscale = 1 << 3, - NoCreate = 1 << 4 + DepthAlias = 1 << 3, + WithUpscale = 1 << 4, + NoCreate = 1 << 5 } } \ No newline at end of file From 42507323535443ad79be071367f3d4815afca688 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen <emmausssss@gmail.com> Date: Thu, 4 May 2023 14:26:10 +0000 Subject: [PATCH 489/737] bcat ipc (#4446) --- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- .../HOS/Services/Bcat/IServiceCreator.cs | 85 ------------------- .../HOS/Services/Bcat/ResultCode.cs | 29 ------- .../Bcat/ServiceCreator/IBcatService.cs | 18 ---- .../IDeliveryCacheDirectoryService.cs | 65 -------------- .../IDeliveryCacheFileService.cs | 78 ----------------- .../IDeliveryCacheProgressService.cs | 63 -------------- .../IDeliveryCacheStorageService.cs | 74 ---------------- src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs | 48 +++++++++++ src/Ryujinx.Horizon/Bcat/BcatMain.cs | 24 ++++++ src/Ryujinx.Horizon/Bcat/BcatResult.cs | 29 +++++++ src/Ryujinx.Horizon/Bcat/BcatServerManager.cs | 28 ++++++ .../Bcat/Ipc/ServiceCreator.cs | 82 ++++++++++++++++++ .../Bcat/Ipc/ServiceCreator/BcatService.cs | 25 ++++++ .../DeliveryCacheDirectoryService.cs | 48 +++++++++++ .../DeliveryCacheFileService.cs | 54 ++++++++++++ .../DeliveryCacheProgressService.cs | 58 +++++++++++++ .../DeliveryCacheStorageService.cs | 74 ++++++++++++++++ .../Types/DeliveryCacheProgressImpl.cs | 2 +- .../Bcat/Types/BcatPortIndex.cs | 10 +++ .../Bcat/Types/BcatServicePermissionLevel.cs | 10 +++ src/Ryujinx.Horizon/HorizonOptions.cs | 7 +- src/Ryujinx.Horizon/LibHacResultExtensions.cs | 12 +++ src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs | 6 +- src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs | 10 +++ .../Bcat/IDeliveryCacheDirectoryService.cs | 14 +++ .../Sdk/Bcat/IDeliveryCacheFileService.cs | 15 ++++ .../Sdk/Bcat/IDeliveryCacheProgressService.cs | 12 +++ .../Sdk/Bcat/IDeliveryCacheStorageService.cs | 14 +++ .../Sdk/Bcat/IServiceCreator.cs | 12 +++ src/Ryujinx.Horizon/ServiceTable.cs | 2 + 31 files changed, 592 insertions(+), 418 deletions(-) delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatMain.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatResult.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatServerManager.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs rename src/{Ryujinx.HLE/HOS/Services/Bcat => Ryujinx.Horizon/Bcat/Ipc}/ServiceCreator/Types/DeliveryCacheProgressImpl.cs (86%) create mode 100644 src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs create mode 100644 src/Ryujinx.Horizon/LibHacResultExtensions.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 1639532ed..34ab0d399 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -327,7 +327,7 @@ namespace Ryujinx.HLE.HOS private void StartNewServices() { ServiceTable = new ServiceTable(); - var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices)); + var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient)); foreach (var service in services) { diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs deleted file mode 100644 index 1437a8e1f..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs +++ /dev/null @@ -1,85 +0,0 @@ -using LibHac; -using LibHac.Common; -using Ryujinx.Common; -using Ryujinx.HLE.HOS.Services.Arp; -using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator; - -namespace Ryujinx.HLE.HOS.Services.Bcat -{ - [Service("bcat:a", "bcat:a")] - [Service("bcat:m", "bcat:m")] - [Service("bcat:u", "bcat:u")] - [Service("bcat:s", "bcat:s")] - class IServiceCreator : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _base; - - public IServiceCreator(ServiceCtx context, string serviceName) - { - var applicationClient = context.Device.System.LibHacHorizonManager.ApplicationClient; - applicationClient.Sm.GetService(ref _base, serviceName).ThrowIfFailure(); - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - - [CommandCmif(0)] - // CreateBcatService(pid) -> object<nn::bcat::detail::ipc::IBcatService> - public ResultCode CreateBcatService(ServiceCtx context) - { - // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId. - // Add an instance of nn::bcat::detail::service::core::PassphraseManager. - // Add an instance of nn::bcat::detail::service::ServiceMemoryManager. - // Add an instance of nn::bcat::detail::service::core::TaskManager who load "bcat-sys:/" system save data and open "dc/task.bin". - // If the file don't exist, create a new one (size of 0x800) and write 2 empty struct with a size of 0x400. - - MakeObject(context, new IBcatService(ApplicationLaunchProperty.GetByPid(context))); - - // NOTE: If the IBcatService is null this error is returned, Doesn't occur in our case. - // return ResultCode.NullObject; - - return ResultCode.Success; - } - - [CommandCmif(1)] - // CreateDeliveryCacheStorageService(pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> - public ResultCode CreateDeliveryCacheStorageService(ServiceCtx context) - { - ulong pid = context.RequestData.ReadUInt64(); - - using var serv = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - - Result rc = _base.Get.CreateDeliveryCacheStorageService(ref serv.Ref, pid); - - if (rc.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheStorageService(context, ref serv.Ref)); - } - - return (ResultCode)rc.Value; - } - - [CommandCmif(2)] - // CreateDeliveryCacheStorageServiceWithApplicationId(nn::ApplicationId) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> - public ResultCode CreateDeliveryCacheStorageServiceWithApplicationId(ServiceCtx context) - { - ApplicationId applicationId = context.RequestData.ReadStruct<ApplicationId>(); - - using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - - Result rc = _base.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref service.Ref, applicationId); - - if (rc.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheStorageService(context, ref service.Ref)); - } - - return (ResultCode)rc.Value; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs deleted file mode 100644 index 7f1b313e1..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Bcat -{ - enum ResultCode - { - ModuleId = 122, - ErrorCodeShift = 9, - - Success = 0, - - InvalidArgument = (1 << ErrorCodeShift) | ModuleId, - NotFound = (2 << ErrorCodeShift) | ModuleId, - TargetLocked = (3 << ErrorCodeShift) | ModuleId, - TargetAlreadyMounted = (4 << ErrorCodeShift) | ModuleId, - TargetNotMounted = (5 << ErrorCodeShift) | ModuleId, - AlreadyOpen = (6 << ErrorCodeShift) | ModuleId, - NotOpen = (7 << ErrorCodeShift) | ModuleId, - InternetRequestDenied = (8 << ErrorCodeShift) | ModuleId, - ServiceOpenLimitReached = (9 << ErrorCodeShift) | ModuleId, - SaveDataNotFound = (10 << ErrorCodeShift) | ModuleId, - NetworkServiceAccountNotAvailable = (31 << ErrorCodeShift) | ModuleId, - PassphrasePathNotFound = (80 << ErrorCodeShift) | ModuleId, - DataVerificationFailed = (81 << ErrorCodeShift) | ModuleId, - PermissionDenied = (90 << ErrorCodeShift) | ModuleId, - AllocationFailed = (91 << ErrorCodeShift) | ModuleId, - InvalidOperation = (98 << ErrorCodeShift) | ModuleId, - InvalidDeliveryCacheStorageFile = (204 << ErrorCodeShift) | ModuleId, - StorageOpenLimitReached = (205 << ErrorCodeShift) | ModuleId - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs deleted file mode 100644 index fb11cedad..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ryujinx.HLE.HOS.Services.Arp; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IBcatService : IpcService - { - public IBcatService(ApplicationLaunchProperty applicationLaunchProperty) { } - - [CommandCmif(10100)] - // RequestSyncDeliveryCache() -> object<nn::bcat::detail::ipc::IDeliveryCacheProgressService> - public ResultCode RequestSyncDeliveryCache(ServiceCtx context) - { - MakeObject(context, new IDeliveryCacheProgressService(context)); - - return ResultCode.Success; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs deleted file mode 100644 index 575449779..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs +++ /dev/null @@ -1,65 +0,0 @@ -using LibHac; -using LibHac.Bcat; -using LibHac.Common; -using Ryujinx.Common; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheDirectoryService : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _base; - - public IDeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> baseService) - { - _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref baseService); - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - - [CommandCmif(0)] - // Open(nn::bcat::DirectoryName) - public ResultCode Open(ServiceCtx context) - { - DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>(); - - Result result = _base.Get.Open(ref directoryName); - - return (ResultCode)result.Value; - } - - [CommandCmif(1)] - // Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>) - public ResultCode Read(ServiceCtx context) - { - ulong bufferAddress = context.Request.ReceiveBuff[0].Position; - ulong bufferLen = context.Request.ReceiveBuff[0].Size; - - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(region.Memory.Span)); - - context.ResponseData.Write(entriesRead); - - return (ResultCode)result.Value; - } - } - - [CommandCmif(2)] - // GetCount() -> u32 - public ResultCode GetCount(ServiceCtx context) - { - Result result = _base.Get.GetCount(out int count); - - context.ResponseData.Write(count); - - return (ResultCode)result.Value; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs deleted file mode 100644 index 5a9110e6a..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs +++ /dev/null @@ -1,78 +0,0 @@ -using LibHac; -using LibHac.Bcat; -using LibHac.Common; -using Ryujinx.Common; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheFileService : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _base; - - public IDeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> baseService) - { - _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref baseService); - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - - [CommandCmif(0)] - // Open(nn::bcat::DirectoryName, nn::bcat::FileName) - public ResultCode Open(ServiceCtx context) - { - DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>(); - FileName fileName = context.RequestData.ReadStruct<FileName>(); - - Result result = _base.Get.Open(ref directoryName, ref fileName); - - return (ResultCode)result.Value; - } - - [CommandCmif(1)] - // Read(u64) -> (u64, buffer<bytes, 6>) - public ResultCode Read(ServiceCtx context) - { - ulong bufferAddress = context.Request.ReceiveBuff[0].Position; - ulong bufferLen = context.Request.ReceiveBuff[0].Size; - - long offset = context.RequestData.ReadInt64(); - - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _base.Get.Read(out long bytesRead, offset, region.Memory.Span); - - context.ResponseData.Write(bytesRead); - - return (ResultCode)result.Value; - } - } - - [CommandCmif(2)] - // GetSize() -> u64 - public ResultCode GetSize(ServiceCtx context) - { - Result result = _base.Get.GetSize(out long size); - - context.ResponseData.Write(size); - - return (ResultCode)result.Value; - } - - [CommandCmif(3)] - // GetDigest() -> nn::bcat::Digest - public ResultCode GetDigest(ServiceCtx context) - { - Result result = _base.Get.GetDigest(out Digest digest); - - context.ResponseData.WriteStruct(digest); - - return (ResultCode)result.Value; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs deleted file mode 100644 index 1555f1707..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types; -using Ryujinx.Horizon.Common; -using System; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheProgressService : IpcService - { - private KEvent _event; - private int _eventHandle; - - public IDeliveryCacheProgressService(ServiceCtx context) - { - _event = new KEvent(context.Device.System.KernelContext); - } - - [CommandCmif(0)] - // GetEvent() -> handle<copy> - public ResultCode GetEvent(ServiceCtx context) - { - if (_eventHandle == 0) - { - if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle); - - Logger.Stub?.PrintStub(LogClass.ServiceBcat); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // GetImpl() -> buffer<nn::bcat::detail::DeliveryCacheProgressImpl, 0x1a> - public ResultCode GetImpl(ServiceCtx context) - { - DeliveryCacheProgressImpl deliveryCacheProgress = new DeliveryCacheProgressImpl - { - State = DeliveryCacheProgressImpl.Status.Done, - Result = 0 - }; - - ulong dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress); - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(dcpSize); - - Logger.Stub?.PrintStub(LogClass.ServiceBcat); - - return ResultCode.Success; - } - - private ulong WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress) - { - return MemoryHelper.Write(context.Memory, ipcDesc.Position, deliveryCacheProgress); - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs deleted file mode 100644 index be77226cb..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs +++ /dev/null @@ -1,74 +0,0 @@ -using LibHac; -using LibHac.Bcat; -using LibHac.Common; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheStorageService : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _base; - - public IDeliveryCacheStorageService(ServiceCtx context, ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> baseService) - { - _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref baseService); - } - - [CommandCmif(0)] - // CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService> - public ResultCode CreateFileService(ServiceCtx context) - { - using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); - - Result result = _base.Get.CreateFileService(ref service.Ref); - - if (result.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheFileService(ref service.Ref)); - } - - return (ResultCode)result.Value; - } - - [CommandCmif(1)] - // CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService> - public ResultCode CreateDirectoryService(ServiceCtx context) - { - using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); - - Result result = _base.Get.CreateDirectoryService(ref service.Ref); - - if (result.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheDirectoryService(ref service.Ref)); - } - - return (ResultCode)result.Value; - } - - [CommandCmif(10)] - // EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>) - public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context) - { - ulong bufferAddress = context.Request.ReceiveBuff[0].Position; - ulong bufferLen = context.Request.ReceiveBuff[0].Size; - - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(region.Memory.Span)); - - context.ResponseData.Write(count); - - return (ResultCode)result.Value; - } - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs new file mode 100644 index 000000000..4a5378af2 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs @@ -0,0 +1,48 @@ +using Ryujinx.Horizon.Bcat.Ipc; +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Bcat +{ + internal class BcatIpcServer + { + private const int BcatMaxSessionsCount = 8; + private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4; + + private const int PointerBufferSize = 0x400; + private const int MaxDomains = 64; + private const int MaxDomainObjects = 64; + private const int MaxPortsCount = 4; + + private SmApi _sm; + private BcatServerManager _serverManager; + + private static readonly ManagerOptions _bcatManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + internal void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount); + + _serverManager.RegisterServer((int)BcatPortIndex.Admin, ServiceName.Encode("bcat:a"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.User, ServiceName.Encode("bcat:u"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.System, ServiceName.Encode("bcat:s"), BcatMaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatMain.cs b/src/Ryujinx.Horizon/Bcat/BcatMain.cs new file mode 100644 index 000000000..d4166fb18 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatMain.cs @@ -0,0 +1,24 @@ +using Ryujinx.Horizon.LogManager; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ryujinx.Horizon.Bcat +{ + internal class BcatMain : IService + { + public static void Main(ServiceTable serviceTable) + { + BcatIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatResult.cs b/src/Ryujinx.Horizon/Bcat/BcatResult.cs new file mode 100644 index 000000000..014c52e7c --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatResult.cs @@ -0,0 +1,29 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Bcat +{ + class BcatResult + { + private const int ModuleId = 122; + + public static Result Success => new(ModuleId, 0); + public static Result InvalidArgument => new(ModuleId, 1); + public static Result NotFound => new(ModuleId, 2); + public static Result TargetLocked => new(ModuleId, 3); + public static Result TargetAlreadyMounted => new(ModuleId, 4); + public static Result TargetNotMounted => new(ModuleId, 5); + public static Result AlreadyOpen => new(ModuleId, 6); + public static Result NotOpen => new(ModuleId, 7); + public static Result InternetRequestDenied => new(ModuleId, 8); + public static Result ServiceOpenLimitReached => new(ModuleId, 9); + public static Result SaveDataNotFound => new(ModuleId, 10); + public static Result NetworkServiceAccountNotAvailable => new(ModuleId, 31); + public static Result PassphrasePathNotFound => new(ModuleId, 80); + public static Result DataVerificationFailed => new(ModuleId, 81); + public static Result PermissionDenied => new(ModuleId, 90); + public static Result AllocationFailed => new(ModuleId, 91); + public static Result InvalidOperation => new(ModuleId, 98); + public static Result InvalidDeliveryCacheStorageFile => new(ModuleId, 204); + public static Result StorageOpenLimitReached => new(ModuleId, 205); + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs new file mode 100644 index 000000000..953f6a225 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs @@ -0,0 +1,28 @@ +using Ryujinx.Horizon.Bcat.Ipc; +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using System; + +namespace Ryujinx.Horizon.Bcat +{ + class BcatServerManager : ServerManager + { + public BcatServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) + { + } + + protected override Result OnNeedsToAccept(int portIndex, Server server) + { + return (BcatPortIndex)portIndex switch + { + BcatPortIndex.Admin => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.Admin)), + BcatPortIndex.Manager => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.Manager)), + BcatPortIndex.User => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.User)), + BcatPortIndex.System => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.System)), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + }; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs new file mode 100644 index 000000000..d1610f7d9 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs @@ -0,0 +1,82 @@ +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using System; +using System.Threading; +using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class ServiceCreator : IServiceCreator, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _libHacService; + + private int _disposalState; + + public ServiceCreator(string serviceName) + { + HorizonStatic.Options.BcatClient.Sm.GetService(ref _libHacService, serviceName).ThrowIfFailure(); + } + + [CmifCommand(0)] + public Result CreateBcatService(out IBcatService bcatService, [ClientProcessId] ulong pid) + { + // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId. + // Add an instance of nn::bcat::detail::service::core::PassphraseManager. + // Add an instance of nn::bcat::detail::service::ServiceMemoryManager. + // Add an instance of nn::bcat::detail::service::core::TaskManager who loads "bcat-sys:/" system save data and opens "dc/task.bin". + // If the file don't exist, create a new one (with a size of 0x800 bytes) and write 2 empty structs with a size of 0x400 bytes. + + bcatService = new BcatService(Bcat.Types.BcatServicePermissionLevel.User); + + return Result.Success; + } + + [CmifCommand(1)] + public Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, [ClientProcessId] ulong pid) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + var resultCode = _libHacService.Get.CreateDeliveryCacheStorageService(ref libHacService.Ref, pid); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheStorageService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(2)] + public Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, ApplicationId applicationId) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + var resultCode = _libHacService.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref libHacService.Ref, new LibHac.ApplicationId(applicationId.Id)); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheStorageService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs new file mode 100644 index 000000000..e82f597e9 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs @@ -0,0 +1,25 @@ +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class BcatService : IBcatService + { + private readonly BcatServicePermissionLevel _permissionLevel; + + public BcatService(BcatServicePermissionLevel permissionLevel) + { + _permissionLevel = permissionLevel; + } + + [CmifCommand(10100)] + public Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService) + { + deliveryCacheProgressService = new DeliveryCacheProgressService(); + + return Result.Success; + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs new file mode 100644 index 000000000..dd13eefb4 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs @@ -0,0 +1,48 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _libHacService; + private int _disposalState; + + public DeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result Open(DirectoryName directoryName) + { + return _libHacService.Get.Open(ref directoryName).ToHorizonResult(); + } + + [CmifCommand(1)] + public Result Read(out int entriesRead, [Buffer(Sdk.Sf.Hipc.HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeliveryCacheDirectoryEntry> entriesBuffer) + { + return _libHacService.Get.Read(out entriesRead, entriesBuffer).ToHorizonResult(); + } + + [CmifCommand(2)] + public Result GetCount(out int count) + { + return _libHacService.Get.GetCount(out count).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs new file mode 100644 index 000000000..d23f5f41f --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs @@ -0,0 +1,54 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheFileService : IDeliveryCacheFileService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _libHacService; + private int _disposalState; + + public DeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result Open(DirectoryName directoryName, FileName fileName) + { + return _libHacService.Get.Open(ref directoryName, ref fileName).ToHorizonResult(); + } + + [CmifCommand(1)] + public Result Read(long offset, out long bytesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> data) + { + return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult(); + } + + [CmifCommand(2)] + public Result GetSize(out long size) + { + return _libHacService.Get.GetSize(out size).ToHorizonResult(); + } + + [CmifCommand(3)] + public Result GetDigest(out Digest digest) + { + return _libHacService.Get.GetDigest(out digest).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs new file mode 100644 index 000000000..91aa26867 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs @@ -0,0 +1,58 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Bcat.Ipc.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheProgressService : IDeliveryCacheProgressService, IDisposable + { + private int _handle; + private SystemEventType _systemEvent; + private int _disposalState; + + [CmifCommand(0)] + public Result GetEvent([CopyHandle] out int handle) + { + if (_handle == 0) + { + Os.CreateSystemEvent(out _systemEvent, EventClearMode.ManualClear, true).AbortOnFailure(); + + _handle = Os.GetReadableHandleOfSystemEvent(ref _systemEvent); + } + + handle = _handle; + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return Result.Success; + } + + [CmifCommand(1)] + public Result GetImpl([Buffer(HipcBufferFlags.Out | HipcBufferFlags.Pointer, 0x200)] out DeliveryCacheProgressImpl deliveryCacheProgressImpl) + { + deliveryCacheProgressImpl = new DeliveryCacheProgressImpl + { + State = DeliveryCacheProgressImpl.Status.Done, + Result = 0 + }; + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return Result.Success; + } + + public void Dispose() + { + if (_handle != 0 && Interlocked.Exchange(ref _disposalState, 1) == 0) + { + Os.DestroySystemEvent(ref _systemEvent); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs new file mode 100644 index 000000000..9507a8a0e --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs @@ -0,0 +1,74 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheStorageService : IDeliveryCacheStorageService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _libHacService; + private int _disposalState; + + public DeliveryCacheStorageService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result CreateFileService(out IDeliveryCacheFileService service) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); + + var resultCode = _libHacService.Get.CreateFileService(ref libHacService.Ref); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheFileService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(1)] + public Result CreateDirectoryService(out IDeliveryCacheDirectoryService service) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); + + var resultCode = _libHacService.Get.CreateDirectoryService(ref libHacService.Ref); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheDirectoryService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(10)] + public Result EnumerateDeliveryCacheDirectory(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DirectoryName> directoryNames) + { + return _libHacService.Get.EnumerateDeliveryCacheDirectory(out count, directoryNames).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs similarity index 86% rename from src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs rename to src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs index fb9a67be6..10e0b54fe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types +namespace Ryujinx.Horizon.Bcat.Ipc.Types { [StructLayout(LayoutKind.Sequential, Size = 0x200)] public struct DeliveryCacheProgressImpl diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs new file mode 100644 index 000000000..e448dfdcd --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Bcat.Types +{ + enum BcatPortIndex + { + Admin, + Manager, + User, + System + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs new file mode 100644 index 000000000..54d7461a7 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Bcat.Types +{ + enum BcatServicePermissionLevel + { + Admin = -1, + User = 1, + System = 2, + Manager = 6 + } +} \ No newline at end of file diff --git a/src/Ryujinx.Horizon/HorizonOptions.cs b/src/Ryujinx.Horizon/HorizonOptions.cs index 6d580e8b9..1ec56bfa7 100644 --- a/src/Ryujinx.Horizon/HorizonOptions.cs +++ b/src/Ryujinx.Horizon/HorizonOptions.cs @@ -1,3 +1,5 @@ +using LibHac; + namespace Ryujinx.Horizon { public struct HorizonOptions @@ -5,10 +7,13 @@ namespace Ryujinx.Horizon public bool IgnoreMissingServices { get; } public bool ThrowOnInvalidCommandIds { get; } - public HorizonOptions(bool ignoreMissingServices) + public HorizonClient BcatClient { get; } + + public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient) { IgnoreMissingServices = ignoreMissingServices; ThrowOnInvalidCommandIds = true; + BcatClient = bcatClient; } } } diff --git a/src/Ryujinx.Horizon/LibHacResultExtensions.cs b/src/Ryujinx.Horizon/LibHacResultExtensions.cs new file mode 100644 index 000000000..01d181640 --- /dev/null +++ b/src/Ryujinx.Horizon/LibHacResultExtensions.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon +{ + internal static class LibHacResultExtensions + { + public static Result ToHorizonResult(this LibHac.Result result) + { + return new Result((int)result.Module, (int)result.Description); + } + } +} diff --git a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs index ba19ff6f6..b80399eaa 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs @@ -9,12 +9,12 @@ namespace Ryujinx.Horizon.Prepo private const int PrepoMaxSessionsCount = 12; private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6; - private const int PointerBufferSize = 0x3800; + private const int PointerBufferSize = 0x80; private const int MaxDomains = 64; private const int MaxDomainObjects = 16; private const int MaxPortsCount = 6; - private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + private static readonly ManagerOptions _prepoManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); private SmApi _sm; private PrepoServerManager _serverManager; @@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Prepo _sm = new SmApi(); _sm.Initialize().AbortOnFailure(); - _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, PrepoTotalMaxSessionsCount); + _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _prepoManagerOptions, PrepoTotalMaxSessionsCount); _serverManager.RegisterServer((int)PrepoPortIndex.Admin, ServiceName.Encode("prepo:a"), PrepoMaxSessionsCount); // 1.0.0-5.1.0 _serverManager.RegisterServer((int)PrepoPortIndex.Admin2, ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+ diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs new file mode 100644 index 000000000..f6c8e6dd3 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs @@ -0,0 +1,10 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IBcatService : IServiceObject + { + Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs new file mode 100644 index 000000000..23a740c35 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs @@ -0,0 +1,14 @@ +using LibHac.Bcat; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheDirectoryService : IServiceObject + { + Result GetCount(out int count); + Result Open(DirectoryName directoryName); + Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entriesBuffer); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs new file mode 100644 index 000000000..a1e5d43ba --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs @@ -0,0 +1,15 @@ +using LibHac.Bcat; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheFileService : IServiceObject + { + Result GetDigest(out Digest digest); + Result GetSize(out long size); + Result Open(DirectoryName directoryName, FileName fileName); + Result Read(long offset, out long bytesRead, Span<byte> data); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs new file mode 100644 index 000000000..07a796f88 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Bcat.Ipc.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheProgressService : IServiceObject + { + Result GetEvent(out int handle); + Result GetImpl(out DeliveryCacheProgressImpl deliveryCacheProgressImpl); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs new file mode 100644 index 000000000..5638f074c --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs @@ -0,0 +1,14 @@ +using LibHac.Bcat; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheStorageService : IServiceObject + { + Result CreateDirectoryService(out IDeliveryCacheDirectoryService service); + Result CreateFileService(out IDeliveryCacheFileService service); + Result EnumerateDeliveryCacheDirectory(out int count, Span<DirectoryName> directoryNames); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs new file mode 100644 index 000000000..edc525900 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IServiceCreator : IServiceObject + { + Result CreateBcatService(out IBcatService service, ulong pid); + Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, ulong pid); + Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, Ncm.ApplicationId applicationId); + } +} diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs index 2edc6ea10..d97457d92 100644 --- a/src/Ryujinx.Horizon/ServiceTable.cs +++ b/src/Ryujinx.Horizon/ServiceTable.cs @@ -1,3 +1,4 @@ +using Ryujinx.Horizon.Bcat; using Ryujinx.Horizon.LogManager; using Ryujinx.Horizon.Prepo; using System.Collections.Generic; @@ -23,6 +24,7 @@ namespace Ryujinx.Horizon RegisterService<LmMain>(); RegisterService<PrepoMain>(); + RegisterService<BcatMain>(); _totalServices = entries.Count; From 3b8ac1641a8a40849915396813e26384b5894911 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Thu, 4 May 2023 16:41:06 +0200 Subject: [PATCH 490/737] UI: Move ApplicationContextMenu in a separated class (#4755) * UI: Move ApplicationContextMenu in a separated class This PR remove duplicated code related to the context menu on the Application list/grid by create a control for the menu which include related handler. I've renamed "GameList/GameGrid" by "Application" for consistencies. And I've removed all uneeded field from the project file too. While I cleaned up things, I've found an issue about purging Ptc/Shader cache, both methods list files even if the user say "No", shader cache is purged even if the user say "No". It's fixed. * Adresses feedbacks --- src/Ryujinx.Ava/Ryujinx.Ava.csproj | 44 --- .../UI/Controls/ApplicationContextMenu.axaml | 80 +++++ .../Controls/ApplicationContextMenu.axaml.cs | 320 ++++++++++++++++++ .../UI/Controls/ApplicationGridView.axaml | 102 ++++++ ....axaml.cs => ApplicationGridView.axaml.cs} | 8 +- ...stView.axaml => ApplicationListView.axaml} | 83 +---- ....axaml.cs => ApplicationListView.axaml.cs} | 8 +- .../UI/Controls/GameGridView.axaml | 177 ---------- .../UI/ViewModels/MainWindowViewModel.cs | 266 +-------------- src/Ryujinx.Ava/UI/Windows/MainWindow.axaml | 8 +- .../UI/Windows/MainWindow.axaml.cs | 8 +- 11 files changed, 526 insertions(+), 578 deletions(-) create mode 100644 src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml create mode 100644 src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs create mode 100644 src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml rename src/Ryujinx.Ava/UI/Controls/{GameGridView.axaml.cs => ApplicationGridView.axaml.cs} (87%) rename src/Ryujinx.Ava/UI/Controls/{GameListView.axaml => ApplicationListView.axaml} (62%) rename src/Ryujinx.Ava/UI/Controls/{GameListView.axaml.cs => ApplicationListView.axaml.cs} (87%) delete mode 100644 src/Ryujinx.Ava/UI/Controls/GameGridView.axaml diff --git a/src/Ryujinx.Ava/Ryujinx.Ava.csproj b/src/Ryujinx.Ava/Ryujinx.Ava.csproj index 7509798b6..2b6432e9b 100644 --- a/src/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/src/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -103,50 +103,6 @@ <Generator>MSBuild:Compile</Generator> </AvaloniaResource> <AvaloniaResource Include="Assets\Styles\Styles.xaml" /> - - <Compile Update="App.axaml.cs"> - <DependentUpon>App.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="Ui\Windows\MainWindow.axaml.cs"> - <DependentUpon>MainWindow.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="Ui\Windows\AboutWindow.axaml.cs"> - <DependentUpon>AboutWindow.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="Ui\Applet\ErrorAppletWindow.axaml.cs"> - <DependentUpon>ProfileWindow.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="Ui\Applet\SwkbdAppletWindow.axaml.cs"> - <DependentUpon>ProfileWindow.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="Ui\Controls\InputDialog.axaml.cs"> - <DependentUpon>InputDialog.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="Ui\Windows\ContentDialogOverlay.xaml.cs"> - <DependentUpon>ContentDialogOverlay.xaml</DependentUpon> - </Compile> - <Compile Update="Ui\Controls\GameListView.axaml.cs"> - <DependentUpon>GameListView.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="UI\Views\User\UserEditorView.axaml.cs"> - <DependentUpon>UserEditor.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="UI\Views\User\UserRecovererView.axaml.cs"> - <DependentUpon>UserRecoverer.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> - <Compile Update="UI\Views\User\UserSelectorView.axaml.cs"> - <DependentUpon>UserSelector.axaml</DependentUpon> - <SubType>Code</SubType> - </Compile> </ItemGroup> <ItemGroup> diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml new file mode 100644 index 000000000..1750e8000 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml @@ -0,0 +1,80 @@ +<MenuFlyout + x:Class="Ryujinx.Ava.UI.Controls.ApplicationContextMenu" + xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"> + <MenuItem + Click="ToggleFavorite_Click" + Header="{locale:Locale GameListContextMenuToggleFavorite}" + ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" /> + <Separator /> + <MenuItem + Click="OpenUserSaveDirectory_Click" + Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}" + IsEnabled="{Binding OpenUserSaveDirectoryEnabled}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> + <MenuItem + Click="OpenDeviceSaveDirectory_Click" + Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}" + IsEnabled="{Binding OpenDeviceSaveDirectoryEnabled}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" /> + <MenuItem + Click="OpenBcatSaveDirectory_Click" + Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}" + IsEnabled="{Binding OpenBcatSaveDirectoryEnabled}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" /> + <Separator /> + <MenuItem + Click="OpenTitleUpdateManager_Click" + Header="{locale:Locale GameListContextMenuManageTitleUpdates}" + ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" /> + <MenuItem + Click="OpenDownloadableContentManager_Click" + Header="{locale:Locale GameListContextMenuManageDlc}" + ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" /> + <MenuItem + Click="OpenCheatManager_Click" + Header="{locale:Locale GameListContextMenuManageCheat}" + ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" /> + <MenuItem + Click="OpenModsDirectory_Click" + Header="{locale:Locale GameListContextMenuOpenModsDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" /> + <MenuItem + Click="OpenSdModsDirectory_Click" + Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" /> + <Separator /> + <MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}"> + <MenuItem + Click="PurgePtcCache_Click" + Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}" + ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" /> + <MenuItem + Click="PurgeShaderCache_Click" + Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}" + ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" /> + <MenuItem + Click="OpenPtcDirectory_Click" + Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" /> + <MenuItem + Click="OpenShaderCacheDirectory_Click" + Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}" + ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" /> + </MenuItem> + <MenuItem Header="{locale:Locale GameListContextMenuExtractData}"> + <MenuItem + Click="ExtractApplicationLogo_Click" + Header="{locale:Locale GameListContextMenuExtractDataExeFS}" + ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" /> + <MenuItem + Click="ExtractApplicationRomFs_Click" + Header="{locale:Locale GameListContextMenuExtractDataRomFS}" + ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" /> + <MenuItem + Click="ExtractApplicationExeFs_Click" + Header="{locale:Locale GameListContextMenuExtractDataLogo}" + ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" /> + </MenuItem> +</MenuFlyout> \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs new file mode 100644 index 000000000..dc0dee2ac --- /dev/null +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -0,0 +1,320 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; +using LibHac.Fs; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using Ryujinx.Common.Configuration; +using Ryujinx.Ui.Common.Helper; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using Path = System.IO.Path; +using UserId = LibHac.Fs.UserId; + +namespace Ryujinx.Ava.UI.Controls +{ + public class ApplicationContextMenu : MenuFlyout + { + public ApplicationContextMenu() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public void ToggleFavorite_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite; + + viewModel.ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata => + { + appMetadata.Favorite = viewModel.SelectedApplication.Favorite; + }); + + viewModel.RefreshView(); + } + } + + public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low)); + } + + public void OpenDeviceSaveDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + OpenSaveDirectory(viewModel, SaveDataType.Device, userId: default); + } + + public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + OpenSaveDirectory(viewModel, SaveDataType.Bcat, userId: default); + } + + private void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId) + { + if (viewModel.SelectedApplication != null) + { + if (!ulong.TryParse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) + { + Dispatcher.UIThread.InvokeAsync(async () => + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); + }); + + return; + } + + var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default); + + ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.TitleName); + } + } + + public async void OpenTitleUpdateManager_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); + } + } + + public async void OpenDownloadableContentManager_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); + } + } + + public async void OpenCheatManager_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window); + } + } + + public void OpenModsDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string modsBasePath = viewModel.VirtualFileSystem.ModLoader.GetModsBasePath(); + string titleModsPath = viewModel.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId); + + OpenHelper.OpenFolder(titleModsPath); + } + } + + public void OpenSdModsDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string sdModsBasePath = viewModel.VirtualFileSystem.ModLoader.GetSdModsBasePath(); + string titleModsPath = viewModel.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId); + + OpenHelper.OpenFolder(titleModsPath); + } + } + + public async void PurgePtcCache_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result == UserResult.Yes) + { + DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "0")); + DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "1")); + + List<FileInfo> cacheFiles = new(); + + if (mainDir.Exists) + { + cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache")); + } + + if (backupDir.Exists) + { + cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); + } + + if (cacheFiles.Count > 0) + { + foreach (FileInfo file in cacheFiles) + { + try + { + file.Delete(); + } + catch (Exception ex) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, ex)); + } + } + } + } + } + } + + public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result == UserResult.Yes) + { + DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader")); + + List<DirectoryInfo> oldCacheDirectories = new(); + List<FileInfo> newCacheFiles = new(); + + if (shaderCacheDir.Exists) + { + oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*")); + newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc")); + newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data")); + } + + if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0)) + { + foreach (DirectoryInfo directory in oldCacheDirectories) + { + try + { + directory.Delete(true); + } + catch (Exception ex) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, ex)); + } + } + + foreach (FileInfo file in newCacheFiles) + { + try + { + file.Delete(); + } + catch (Exception ex) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, ex)); + } + } + } + } + } + } + + public void OpenPtcDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu"); + string mainDir = Path.Combine(ptcDir, "0"); + string backupDir = Path.Combine(ptcDir, "1"); + + if (!Directory.Exists(ptcDir)) + { + Directory.CreateDirectory(ptcDir); + Directory.CreateDirectory(mainDir); + Directory.CreateDirectory(backupDir); + } + + OpenHelper.OpenFolder(ptcDir); + } + } + + public void OpenShaderCacheDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader"); + + if (!Directory.Exists(shaderCacheDir)) + { + Directory.CreateDirectory(shaderCacheDir); + } + + OpenHelper.OpenFolder(shaderCacheDir); + } + } + + public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + } + } + + public async void ExtractApplicationRomFs_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Data, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + } + } + + public async void ExtractApplicationExeFs_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + } + } + } +} diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml new file mode 100644 index 000000000..f7a120b1a --- /dev/null +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml @@ -0,0 +1,102 @@ +<UserControl + x:Class="Ryujinx.Ava.UI.Controls.ApplicationGridView" + xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox" + xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + d:DesignHeight="450" + d:DesignWidth="800" + Focusable="True" + mc:Ignorable="d"> + <UserControl.Resources> + <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> + <controls:ApplicationContextMenu x:Key="ApplicationContextMenu" /> + </UserControl.Resources> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="*" /> + </Grid.RowDefinitions> + <ListBox + Grid.Row="0" + Padding="8" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" + ContextFlyout="{StaticResource ApplicationContextMenu}" + DoubleTapped="GameList_DoubleTapped" + Items="{Binding AppsObservableList}" + SelectionChanged="GameList_SelectionChanged"> + <ListBox.ItemsPanel> + <ItemsPanelTemplate> + <flex:FlexPanel + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" + AlignContent="FlexStart" + JustifyContent="Center" /> + </ItemsPanelTemplate> + </ListBox.ItemsPanel> + <ListBox.Styles> + <Style Selector="ListBoxItem"> + <Setter Property="Margin" Value="5" /> + <Setter Property="CornerRadius" Value="4" /> + </Style> + <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator"> + <Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" /> + </Style> + </ListBox.Styles> + <ListBox.ItemTemplate> + <DataTemplate> + <Grid> + <Border + Margin="10" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" + Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}" + Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}" + Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}" + Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}" + ClipToBounds="True" + CornerRadius="4"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Image + Grid.Row="0" + HorizontalAlignment="Stretch" + VerticalAlignment="Top" + Source="{Binding Icon, Converter={StaticResource ByteImage}}" /> + <Panel + Grid.Row="1" + Height="50" + Margin="0,10,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" + IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}"> + <TextBlock + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Text="{Binding TitleName}" + TextAlignment="Center" + TextWrapping="Wrap" /> + </Panel> + </Grid> + </Border> + <ui:SymbolIcon + Margin="5,5,0,0" + HorizontalAlignment="Left" + VerticalAlignment="Top" + FontSize="16" + Foreground="{DynamicResource SystemAccentColor}" + IsVisible="{Binding Favorite}" + Symbol="StarFilled" /> + </Grid> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Grid> +</UserControl> \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs similarity index 87% rename from src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs index aa76b7c95..c30c75b36 100644 --- a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs @@ -9,10 +9,10 @@ using System; namespace Ryujinx.Ava.UI.Controls { - public partial class GameGridView : UserControl + public partial class ApplicationGridView : UserControl { public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = - RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); + RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened { @@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Controls remove { RemoveHandler(ApplicationOpenedEvent, value); } } - public GameGridView() + public ApplicationGridView() { InitializeComponent(); } @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.Controls } } - private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) + private void SearchBox_OnKeyUp(object sender, KeyEventArgs args) { (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; } diff --git a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml similarity index 62% rename from src/Ryujinx.Ava/UI/Controls/GameListView.axaml rename to src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml index c13eaae80..fa8ebf627 100644 --- a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml @@ -1,10 +1,10 @@ <UserControl - x:Class="Ryujinx.Ava.UI.Controls.GameListView" + x:Class="Ryujinx.Ava.UI.Controls.ApplicationListView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" d:DesignHeight="450" @@ -13,82 +13,7 @@ mc:Ignorable="d"> <UserControl.Resources> <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> - <MenuFlyout x:Key="GameContextMenu"> - <MenuItem - Command="{Binding ToggleFavorite}" - Header="{locale:Locale GameListContextMenuToggleFavorite}" - ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" /> - <Separator /> - <MenuItem - Command="{Binding OpenUserSaveDirectory}" - IsEnabled="{Binding EnabledUserSaveDirectory}" - Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenDeviceSaveDirectory}" - IsEnabled="{Binding EnabledDeviceSaveDirectory}" - Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenBcatSaveDirectory}" - IsEnabled="{Binding EnabledBcatSaveDirectory}" - Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" /> - <Separator /> - <MenuItem - Command="{Binding OpenTitleUpdateManager}" - Header="{locale:Locale GameListContextMenuManageTitleUpdates}" - ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" /> - <MenuItem - Command="{Binding OpenDownloadableContentManager}" - Header="{locale:Locale GameListContextMenuManageDlc}" - ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" /> - <MenuItem - Command="{Binding OpenCheatManager}" - Header="{locale:Locale GameListContextMenuManageCheat}" - ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" /> - <MenuItem - Command="{Binding OpenModsDirectory}" - Header="{locale:Locale GameListContextMenuOpenModsDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenSdModsDirectory}" - Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" /> - <Separator /> - <MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}"> - <MenuItem - Command="{Binding PurgePtcCache}" - Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" /> - <MenuItem - Command="{Binding PurgeShaderCache}" - Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" /> - <MenuItem - Command="{Binding OpenPtcDirectory}" - Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenShaderCacheDirectory}" - Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" /> - </MenuItem> - <MenuItem Header="{locale:Locale GameListContextMenuExtractData}"> - <MenuItem - Command="{Binding ExtractExeFs}" - Header="{locale:Locale GameListContextMenuExtractDataExeFS}" - ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" /> - <MenuItem - Command="{Binding ExtractRomFs}" - Header="{locale:Locale GameListContextMenuExtractDataRomFS}" - ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" /> - <MenuItem - Command="{Binding ExtractLogo}" - Header="{locale:Locale GameListContextMenuExtractDataLogo}" - ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" /> - </MenuItem> - </MenuFlyout> + <controls:ApplicationContextMenu x:Key="ApplicationContextMenu" /> </UserControl.Resources> <Grid> <Grid.RowDefinitions> @@ -100,7 +25,7 @@ Padding="8" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - ContextFlyout="{StaticResource GameContextMenu}" + ContextFlyout="{StaticResource ApplicationContextMenu}" DoubleTapped="GameList_DoubleTapped" Items="{Binding AppsObservableList}" SelectionChanged="GameList_SelectionChanged"> diff --git a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs similarity index 87% rename from src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs index a64497097..1a07c4251 100644 --- a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs @@ -9,10 +9,10 @@ using System; namespace Ryujinx.Ava.UI.Controls { - public partial class GameListView : UserControl + public partial class ApplicationListView : UserControl { public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = - RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); + RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened { @@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Controls remove { RemoveHandler(ApplicationOpenedEvent, value); } } - public GameListView() + public ApplicationListView() { InitializeComponent(); } @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.Controls } } - private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) + private void SearchBox_OnKeyUp(object sender, KeyEventArgs args) { (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; } diff --git a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml b/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml deleted file mode 100644 index 32cabfaa8..000000000 --- a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml +++ /dev/null @@ -1,177 +0,0 @@ -<UserControl - x:Class="Ryujinx.Ava.UI.Controls.GameGridView" - xmlns="https://github.com/avaloniaui" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" - xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" - d:DesignHeight="450" - d:DesignWidth="800" - mc:Ignorable="d" - Focusable="True"> - <UserControl.Resources> - <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> - <MenuFlyout x:Key="GameContextMenu"> - <MenuItem - Command="{Binding ToggleFavorite}" - Header="{locale:Locale GameListContextMenuToggleFavorite}" - ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" /> - <Separator /> - <MenuItem - Command="{Binding OpenUserSaveDirectory}" - IsEnabled="{Binding EnabledUserSaveDirectory}" - Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenDeviceSaveDirectory}" - IsEnabled="{Binding EnabledDeviceSaveDirectory}" - Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenBcatSaveDirectory}" - IsEnabled="{Binding EnabledBcatSaveDirectory}" - Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" /> - <Separator /> - <MenuItem - Command="{Binding OpenTitleUpdateManager}" - Header="{locale:Locale GameListContextMenuManageTitleUpdates}" - ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" /> - <MenuItem - Command="{Binding OpenDownloadableContentManager}" - Header="{locale:Locale GameListContextMenuManageDlc}" - ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" /> - <MenuItem - Command="{Binding OpenCheatManager}" - Header="{locale:Locale GameListContextMenuManageCheat}" - ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" /> - <MenuItem - Command="{Binding OpenModsDirectory}" - Header="{locale:Locale GameListContextMenuOpenModsDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenSdModsDirectory}" - Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" /> - <Separator /> - <MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}"> - <MenuItem - Command="{Binding PurgePtcCache}" - Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" /> - <MenuItem - Command="{Binding PurgeShaderCache}" - Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" /> - <MenuItem - Command="{Binding OpenPtcDirectory}" - Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" /> - <MenuItem - Command="{Binding OpenShaderCacheDirectory}" - Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" /> - </MenuItem> - <MenuItem Header="{locale:Locale GameListContextMenuExtractData}"> - <MenuItem - Command="{Binding ExtractExeFs}" - Header="{locale:Locale GameListContextMenuExtractDataExeFS}" - ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" /> - <MenuItem - Command="{Binding ExtractRomFs}" - Header="{locale:Locale GameListContextMenuExtractDataRomFS}" - ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" /> - <MenuItem - Command="{Binding ExtractLogo}" - Header="{locale:Locale GameListContextMenuExtractDataLogo}" - ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" /> - </MenuItem> - </MenuFlyout> - </UserControl.Resources> - <Grid> - <Grid.RowDefinitions> - <RowDefinition Height="*" /> - </Grid.RowDefinitions> - <ListBox - Grid.Row="0" - Padding="8" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" - ContextFlyout="{StaticResource GameContextMenu}" - DoubleTapped="GameList_DoubleTapped" - Items="{Binding AppsObservableList}" - SelectionChanged="GameList_SelectionChanged"> - <ListBox.ItemsPanel> - <ItemsPanelTemplate> - <flex:FlexPanel - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" - AlignContent="FlexStart" - JustifyContent="Center" /> - </ItemsPanelTemplate> - </ListBox.ItemsPanel> - <ListBox.Styles> - <Style Selector="ListBoxItem"> - <Setter Property="Margin" Value="5" /> - <Setter Property="CornerRadius" Value="4" /> - </Style> - <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator"> - <Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" /> - </Style> - </ListBox.Styles> - <ListBox.ItemTemplate> - <DataTemplate> - <Grid> - <Border - Margin="10" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" - Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}" - Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}" - Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}" - Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}" - ClipToBounds="True" - CornerRadius="4"> - <Grid> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - </Grid.RowDefinitions> - <Image - Grid.Row="0" - HorizontalAlignment="Stretch" - VerticalAlignment="Top" - Source="{Binding Icon, Converter={StaticResource ByteImage}}" /> - <Panel - Grid.Row="1" - Height="50" - Margin="0 10 0 0" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" - IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}"> - <TextBlock - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - Text="{Binding TitleName}" - TextAlignment="Center" - TextWrapping="Wrap" /> - </Panel> - </Grid> - </Border> - <ui:SymbolIcon - Margin="5,5,0,0" - HorizontalAlignment="Left" - VerticalAlignment="Top" - FontSize="16" - Foreground="{DynamicResource SystemAccentColor}" - IsVisible="{Binding Favorite}" - Symbol="StarFilled" /> - </Grid> - </DataTemplate> - </ListBox.ItemTemplate> - </ListBox> - </Grid> -</UserControl> \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f4556bc3c..b5c82d655 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -6,8 +6,6 @@ using Avalonia.Threading; using DynamicData; using DynamicData.Binding; using LibHac.Common; -using LibHac.Fs; -using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; @@ -33,13 +31,11 @@ using SixLabors.ImageSharp.PixelFormats; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; using Path = System.IO.Path; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; -using UserId = LibHac.Fs.UserId; namespace Ryujinx.Ava.UI.ViewModels { @@ -346,11 +342,11 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool EnabledUserSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; + public bool OpenUserSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; - public bool EnabledDeviceSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; + public bool OpenDeviceSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; - public bool EnabledBcatSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; + public bool OpenBcatSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public string LoadHeading { @@ -941,7 +937,7 @@ namespace Ryujinx.Ava.UI.ViewModels }; } - private void RefreshView() + public void RefreshView() { RefreshGrid(); } @@ -1116,30 +1112,6 @@ namespace Ryujinx.Ava.UI.ViewModels })); } - private async void ExtractLogo() - { - if (SelectedApplication != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName); - } - } - - private async void ExtractRomFs() - { - if (SelectedApplication != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName); - } - } - - private async void ExtractExeFs() - { - if (SelectedApplication != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Code, SelectedApplication.Path, SelectedApplication.TitleName); - } - } - private void PrepareLoadScreen() { using MemoryStream stream = new(SelectedIcon); @@ -1383,241 +1355,11 @@ namespace Ryujinx.Ava.UI.ViewModels await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient); } - public void OpenPtcDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu"); - string mainPath = Path.Combine(ptcDir, "0"); - string backupPath = Path.Combine(ptcDir, "1"); - - if (!Directory.Exists(ptcDir)) - { - Directory.CreateDirectory(ptcDir); - Directory.CreateDirectory(mainPath); - Directory.CreateDirectory(backupPath); - } - - OpenHelper.OpenFolder(ptcDir); - } - } - - public async void PurgePtcCache() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0")); - DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1")); - - // FIXME: Found a way to reproduce the bold effect on the title name (fork?). - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, selection.TitleName), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - - List<FileInfo> cacheFiles = new(); - - if (mainDir.Exists) - { - cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache")); - } - - if (backupDir.Exists) - { - cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); - } - - if (cacheFiles.Count > 0 && result == UserResult.Yes) - { - foreach (FileInfo file in cacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, e)); - } - } - } - } - } - - public void OpenShaderCacheDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"); - - if (!Directory.Exists(shaderCacheDir)) - { - Directory.CreateDirectory(shaderCacheDir); - } - - OpenHelper.OpenFolder(shaderCacheDir); - } - } - public void SimulateWakeUpMessage() { AppHost.Device.System.SimulateWakeUpMessage(); } - public async void PurgeShaderCache() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader")); - - // FIXME: Found a way to reproduce the bold effect on the title name (fork?). - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, selection.TitleName), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - - List<DirectoryInfo> oldCacheDirectories = new(); - List<FileInfo> newCacheFiles = new(); - - if (shaderCacheDir.Exists) - { - oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data")); - } - - if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0) && result == UserResult.Yes) - { - foreach (DirectoryInfo directory in oldCacheDirectories) - { - try - { - directory.Delete(true); - } - catch (Exception e) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, e)); - } - } - } - - foreach (FileInfo file in newCacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, e)); - } - } - } - } - - public void ToggleFavorite() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - selection.Favorite = !selection.Favorite; - - ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata => - { - appMetadata.Favorite = selection.Favorite; - }); - - RefreshView(); - } - } - - public void OpenUserSaveDirectory() - { - OpenSaveDirectory(SaveDataType.Account, userId: new UserId((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low)); - } - - public void OpenDeviceSaveDirectory() - { - OpenSaveDirectory(SaveDataType.Device, userId: default); - } - - public void OpenBcatSaveDirectory() - { - OpenSaveDirectory(SaveDataType.Bcat, userId: default); - } - - private void OpenSaveDirectory(SaveDataType saveDataType, UserId userId) - { - if (SelectedApplication != null) - { - if (!ulong.TryParse(SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) - { - Dispatcher.UIThread.InvokeAsync(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]); - }); - - return; - } - - var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default); - - ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, SelectedApplication.ControlHolder, SelectedApplication.TitleName); - } - } - - public void OpenModsDirectory() - { - if (SelectedApplication != null) - { - string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); - - OpenHelper.OpenFolder(titleModsPath); - } - } - - public void OpenSdModsDirectory() - { - if (SelectedApplication != null) - { - string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); - - OpenHelper.OpenFolder(titleModsPath); - } - } - - public async void OpenTitleUpdateManager() - { - if (SelectedApplication != null) - { - await TitleUpdateWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName); - } - } - - public async void OpenDownloadableContentManager() - { - if (SelectedApplication != null) - { - await DownloadableContentManagerWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName); - } - } - - public async void OpenCheatManager() - { - if (SelectedApplication != null) - { - await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window); - } - } - public async void LoadApplications() { await Dispatcher.UIThread.InvokeAsync(() => diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml index de01f90fb..fa07d9773 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml @@ -88,16 +88,16 @@ <main:MainViewControls Name="ViewControls" Grid.Row="0"/> - <controls:GameListView - x:Name="GameList" + <controls:ApplicationListView + x:Name="ApplicationList" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" IsVisible="{Binding IsList}" /> - <controls:GameGridView - x:Name="GameGrid" + <controls:ApplicationGridView + x:Name="ApplicationGrid" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index 404d4ee05..eec77479e 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -288,13 +288,13 @@ namespace Ryujinx.Ava.UI.Windows { StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged; - GameGrid.ApplicationOpened += Application_Opened; + ApplicationGrid.ApplicationOpened += Application_Opened; - GameGrid.DataContext = ViewModel; + ApplicationGrid.DataContext = ViewModel; - GameList.ApplicationOpened += Application_Opened; + ApplicationList.ApplicationOpened += Application_Opened; - GameList.DataContext = ViewModel; + ApplicationList.DataContext = ViewModel; LoadHotKeys(); } From 264438ff19898bcb8f8fc16dc9243bf9f4eba072 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 4 May 2023 13:16:51 -0300 Subject: [PATCH 491/737] Revert "bcat ipc (#4446)" (#4801) This reverts commit 42507323535443ad79be071367f3d4815afca688. --- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- .../HOS/Services/Bcat/IServiceCreator.cs | 85 +++++++++++++++++++ .../HOS/Services/Bcat/ResultCode.cs | 29 +++++++ .../Bcat/ServiceCreator/IBcatService.cs | 18 ++++ .../IDeliveryCacheDirectoryService.cs | 65 ++++++++++++++ .../IDeliveryCacheFileService.cs | 78 +++++++++++++++++ .../IDeliveryCacheProgressService.cs | 63 ++++++++++++++ .../IDeliveryCacheStorageService.cs | 74 ++++++++++++++++ .../Types/DeliveryCacheProgressImpl.cs | 2 +- src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs | 48 ----------- src/Ryujinx.Horizon/Bcat/BcatMain.cs | 24 ------ src/Ryujinx.Horizon/Bcat/BcatResult.cs | 29 ------- src/Ryujinx.Horizon/Bcat/BcatServerManager.cs | 28 ------ .../Bcat/Ipc/ServiceCreator.cs | 82 ------------------ .../Bcat/Ipc/ServiceCreator/BcatService.cs | 25 ------ .../DeliveryCacheDirectoryService.cs | 48 ----------- .../DeliveryCacheFileService.cs | 54 ------------ .../DeliveryCacheProgressService.cs | 58 ------------- .../DeliveryCacheStorageService.cs | 74 ---------------- .../Bcat/Types/BcatPortIndex.cs | 10 --- .../Bcat/Types/BcatServicePermissionLevel.cs | 10 --- src/Ryujinx.Horizon/HorizonOptions.cs | 7 +- src/Ryujinx.Horizon/LibHacResultExtensions.cs | 12 --- src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs | 6 +- src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs | 10 --- .../Bcat/IDeliveryCacheDirectoryService.cs | 14 --- .../Sdk/Bcat/IDeliveryCacheFileService.cs | 15 ---- .../Sdk/Bcat/IDeliveryCacheProgressService.cs | 12 --- .../Sdk/Bcat/IDeliveryCacheStorageService.cs | 14 --- .../Sdk/Bcat/IServiceCreator.cs | 12 --- src/Ryujinx.Horizon/ServiceTable.cs | 2 - 31 files changed, 418 insertions(+), 592 deletions(-) create mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs rename src/{Ryujinx.Horizon/Bcat/Ipc => Ryujinx.HLE/HOS/Services/Bcat}/ServiceCreator/Types/DeliveryCacheProgressImpl.cs (86%) delete mode 100644 src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/BcatMain.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/BcatResult.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/BcatServerManager.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs delete mode 100644 src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs delete mode 100644 src/Ryujinx.Horizon/LibHacResultExtensions.cs delete mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs delete mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs delete mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs delete mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs delete mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs delete mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 34ab0d399..1639532ed 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -327,7 +327,7 @@ namespace Ryujinx.HLE.HOS private void StartNewServices() { ServiceTable = new ServiceTable(); - var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient)); + var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices)); foreach (var service in services) { diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs new file mode 100644 index 000000000..1437a8e1f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs @@ -0,0 +1,85 @@ +using LibHac; +using LibHac.Common; +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Services.Arp; +using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator; + +namespace Ryujinx.HLE.HOS.Services.Bcat +{ + [Service("bcat:a", "bcat:a")] + [Service("bcat:m", "bcat:m")] + [Service("bcat:u", "bcat:u")] + [Service("bcat:s", "bcat:s")] + class IServiceCreator : DisposableIpcService + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _base; + + public IServiceCreator(ServiceCtx context, string serviceName) + { + var applicationClient = context.Device.System.LibHacHorizonManager.ApplicationClient; + applicationClient.Sm.GetService(ref _base, serviceName).ThrowIfFailure(); + } + + protected override void Dispose(bool isDisposing) + { + if (isDisposing) + { + _base.Destroy(); + } + } + + [CommandCmif(0)] + // CreateBcatService(pid) -> object<nn::bcat::detail::ipc::IBcatService> + public ResultCode CreateBcatService(ServiceCtx context) + { + // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId. + // Add an instance of nn::bcat::detail::service::core::PassphraseManager. + // Add an instance of nn::bcat::detail::service::ServiceMemoryManager. + // Add an instance of nn::bcat::detail::service::core::TaskManager who load "bcat-sys:/" system save data and open "dc/task.bin". + // If the file don't exist, create a new one (size of 0x800) and write 2 empty struct with a size of 0x400. + + MakeObject(context, new IBcatService(ApplicationLaunchProperty.GetByPid(context))); + + // NOTE: If the IBcatService is null this error is returned, Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } + + [CommandCmif(1)] + // CreateDeliveryCacheStorageService(pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> + public ResultCode CreateDeliveryCacheStorageService(ServiceCtx context) + { + ulong pid = context.RequestData.ReadUInt64(); + + using var serv = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + Result rc = _base.Get.CreateDeliveryCacheStorageService(ref serv.Ref, pid); + + if (rc.IsSuccess()) + { + MakeObject(context, new IDeliveryCacheStorageService(context, ref serv.Ref)); + } + + return (ResultCode)rc.Value; + } + + [CommandCmif(2)] + // CreateDeliveryCacheStorageServiceWithApplicationId(nn::ApplicationId) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> + public ResultCode CreateDeliveryCacheStorageServiceWithApplicationId(ServiceCtx context) + { + ApplicationId applicationId = context.RequestData.ReadStruct<ApplicationId>(); + + using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + Result rc = _base.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref service.Ref, applicationId); + + if (rc.IsSuccess()) + { + MakeObject(context, new IDeliveryCacheStorageService(context, ref service.Ref)); + } + + return (ResultCode)rc.Value; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs new file mode 100644 index 000000000..7f1b313e1 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.HLE.HOS.Services.Bcat +{ + enum ResultCode + { + ModuleId = 122, + ErrorCodeShift = 9, + + Success = 0, + + InvalidArgument = (1 << ErrorCodeShift) | ModuleId, + NotFound = (2 << ErrorCodeShift) | ModuleId, + TargetLocked = (3 << ErrorCodeShift) | ModuleId, + TargetAlreadyMounted = (4 << ErrorCodeShift) | ModuleId, + TargetNotMounted = (5 << ErrorCodeShift) | ModuleId, + AlreadyOpen = (6 << ErrorCodeShift) | ModuleId, + NotOpen = (7 << ErrorCodeShift) | ModuleId, + InternetRequestDenied = (8 << ErrorCodeShift) | ModuleId, + ServiceOpenLimitReached = (9 << ErrorCodeShift) | ModuleId, + SaveDataNotFound = (10 << ErrorCodeShift) | ModuleId, + NetworkServiceAccountNotAvailable = (31 << ErrorCodeShift) | ModuleId, + PassphrasePathNotFound = (80 << ErrorCodeShift) | ModuleId, + DataVerificationFailed = (81 << ErrorCodeShift) | ModuleId, + PermissionDenied = (90 << ErrorCodeShift) | ModuleId, + AllocationFailed = (91 << ErrorCodeShift) | ModuleId, + InvalidOperation = (98 << ErrorCodeShift) | ModuleId, + InvalidDeliveryCacheStorageFile = (204 << ErrorCodeShift) | ModuleId, + StorageOpenLimitReached = (205 << ErrorCodeShift) | ModuleId + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs new file mode 100644 index 000000000..fb11cedad --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs @@ -0,0 +1,18 @@ +using Ryujinx.HLE.HOS.Services.Arp; + +namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator +{ + class IBcatService : IpcService + { + public IBcatService(ApplicationLaunchProperty applicationLaunchProperty) { } + + [CommandCmif(10100)] + // RequestSyncDeliveryCache() -> object<nn::bcat::detail::ipc::IDeliveryCacheProgressService> + public ResultCode RequestSyncDeliveryCache(ServiceCtx context) + { + MakeObject(context, new IDeliveryCacheProgressService(context)); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs new file mode 100644 index 000000000..575449779 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs @@ -0,0 +1,65 @@ +using LibHac; +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator +{ + class IDeliveryCacheDirectoryService : DisposableIpcService + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _base; + + public IDeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> baseService) + { + _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref baseService); + } + + protected override void Dispose(bool isDisposing) + { + if (isDisposing) + { + _base.Destroy(); + } + } + + [CommandCmif(0)] + // Open(nn::bcat::DirectoryName) + public ResultCode Open(ServiceCtx context) + { + DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>(); + + Result result = _base.Get.Open(ref directoryName); + + return (ResultCode)result.Value; + } + + [CommandCmif(1)] + // Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>) + public ResultCode Read(ServiceCtx context) + { + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; + + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(region.Memory.Span)); + + context.ResponseData.Write(entriesRead); + + return (ResultCode)result.Value; + } + } + + [CommandCmif(2)] + // GetCount() -> u32 + public ResultCode GetCount(ServiceCtx context) + { + Result result = _base.Get.GetCount(out int count); + + context.ResponseData.Write(count); + + return (ResultCode)result.Value; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs new file mode 100644 index 000000000..5a9110e6a --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs @@ -0,0 +1,78 @@ +using LibHac; +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Common; + +namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator +{ + class IDeliveryCacheFileService : DisposableIpcService + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _base; + + public IDeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> baseService) + { + _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref baseService); + } + + protected override void Dispose(bool isDisposing) + { + if (isDisposing) + { + _base.Destroy(); + } + } + + [CommandCmif(0)] + // Open(nn::bcat::DirectoryName, nn::bcat::FileName) + public ResultCode Open(ServiceCtx context) + { + DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>(); + FileName fileName = context.RequestData.ReadStruct<FileName>(); + + Result result = _base.Get.Open(ref directoryName, ref fileName); + + return (ResultCode)result.Value; + } + + [CommandCmif(1)] + // Read(u64) -> (u64, buffer<bytes, 6>) + public ResultCode Read(ServiceCtx context) + { + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; + + long offset = context.RequestData.ReadInt64(); + + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _base.Get.Read(out long bytesRead, offset, region.Memory.Span); + + context.ResponseData.Write(bytesRead); + + return (ResultCode)result.Value; + } + } + + [CommandCmif(2)] + // GetSize() -> u64 + public ResultCode GetSize(ServiceCtx context) + { + Result result = _base.Get.GetSize(out long size); + + context.ResponseData.Write(size); + + return (ResultCode)result.Value; + } + + [CommandCmif(3)] + // GetDigest() -> nn::bcat::Digest + public ResultCode GetDigest(ServiceCtx context) + { + Result result = _base.Get.GetDigest(out Digest digest); + + context.ResponseData.WriteStruct(digest); + + return (ResultCode)result.Value; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs new file mode 100644 index 000000000..1555f1707 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs @@ -0,0 +1,63 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator +{ + class IDeliveryCacheProgressService : IpcService + { + private KEvent _event; + private int _eventHandle; + + public IDeliveryCacheProgressService(ServiceCtx context) + { + _event = new KEvent(context.Device.System.KernelContext); + } + + [CommandCmif(0)] + // GetEvent() -> handle<copy> + public ResultCode GetEvent(ServiceCtx context) + { + if (_eventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle); + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // GetImpl() -> buffer<nn::bcat::detail::DeliveryCacheProgressImpl, 0x1a> + public ResultCode GetImpl(ServiceCtx context) + { + DeliveryCacheProgressImpl deliveryCacheProgress = new DeliveryCacheProgressImpl + { + State = DeliveryCacheProgressImpl.Status.Done, + Result = 0 + }; + + ulong dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(dcpSize); + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return ResultCode.Success; + } + + private ulong WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress) + { + return MemoryHelper.Write(context.Memory, ipcDesc.Position, deliveryCacheProgress); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs new file mode 100644 index 000000000..be77226cb --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs @@ -0,0 +1,74 @@ +using LibHac; +using LibHac.Bcat; +using LibHac.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator +{ + class IDeliveryCacheStorageService : DisposableIpcService + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _base; + + public IDeliveryCacheStorageService(ServiceCtx context, ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> baseService) + { + _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref baseService); + } + + [CommandCmif(0)] + // CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService> + public ResultCode CreateFileService(ServiceCtx context) + { + using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); + + Result result = _base.Get.CreateFileService(ref service.Ref); + + if (result.IsSuccess()) + { + MakeObject(context, new IDeliveryCacheFileService(ref service.Ref)); + } + + return (ResultCode)result.Value; + } + + [CommandCmif(1)] + // CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService> + public ResultCode CreateDirectoryService(ServiceCtx context) + { + using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); + + Result result = _base.Get.CreateDirectoryService(ref service.Ref); + + if (result.IsSuccess()) + { + MakeObject(context, new IDeliveryCacheDirectoryService(ref service.Ref)); + } + + return (ResultCode)result.Value; + } + + [CommandCmif(10)] + // EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>) + public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context) + { + ulong bufferAddress = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; + + using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + { + Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(region.Memory.Span)); + + context.ResponseData.Write(count); + + return (ResultCode)result.Value; + } + } + + protected override void Dispose(bool isDisposing) + { + if (isDisposing) + { + _base.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs similarity index 86% rename from src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs rename to src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs index 10e0b54fe..fb9a67be6 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Ryujinx.Horizon.Bcat.Ipc.Types +namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types { [StructLayout(LayoutKind.Sequential, Size = 0x200)] public struct DeliveryCacheProgressImpl diff --git a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs deleted file mode 100644 index 4a5378af2..000000000 --- a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Ryujinx.Horizon.Bcat.Ipc; -using Ryujinx.Horizon.Bcat.Types; -using Ryujinx.Horizon.Sdk.Sf.Hipc; -using Ryujinx.Horizon.Sdk.Sm; - -namespace Ryujinx.Horizon.Bcat -{ - internal class BcatIpcServer - { - private const int BcatMaxSessionsCount = 8; - private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4; - - private const int PointerBufferSize = 0x400; - private const int MaxDomains = 64; - private const int MaxDomainObjects = 64; - private const int MaxPortsCount = 4; - - private SmApi _sm; - private BcatServerManager _serverManager; - - private static readonly ManagerOptions _bcatManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); - - internal void Initialize() - { - HeapAllocator allocator = new(); - - _sm = new SmApi(); - _sm.Initialize().AbortOnFailure(); - - _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount); - - _serverManager.RegisterServer((int)BcatPortIndex.Admin, ServiceName.Encode("bcat:a"), BcatMaxSessionsCount); - _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount); - _serverManager.RegisterServer((int)BcatPortIndex.User, ServiceName.Encode("bcat:u"), BcatMaxSessionsCount); - _serverManager.RegisterServer((int)BcatPortIndex.System, ServiceName.Encode("bcat:s"), BcatMaxSessionsCount); - } - - public void ServiceRequests() - { - _serverManager.ServiceRequests(); - } - - public void Shutdown() - { - _serverManager.Dispose(); - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/BcatMain.cs b/src/Ryujinx.Horizon/Bcat/BcatMain.cs deleted file mode 100644 index d4166fb18..000000000 --- a/src/Ryujinx.Horizon/Bcat/BcatMain.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Ryujinx.Horizon.LogManager; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Ryujinx.Horizon.Bcat -{ - internal class BcatMain : IService - { - public static void Main(ServiceTable serviceTable) - { - BcatIpcServer ipcServer = new(); - - ipcServer.Initialize(); - - serviceTable.SignalServiceReady(); - - ipcServer.ServiceRequests(); - ipcServer.Shutdown(); - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/BcatResult.cs b/src/Ryujinx.Horizon/Bcat/BcatResult.cs deleted file mode 100644 index 014c52e7c..000000000 --- a/src/Ryujinx.Horizon/Bcat/BcatResult.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Ryujinx.Horizon.Common; - -namespace Ryujinx.Horizon.Bcat -{ - class BcatResult - { - private const int ModuleId = 122; - - public static Result Success => new(ModuleId, 0); - public static Result InvalidArgument => new(ModuleId, 1); - public static Result NotFound => new(ModuleId, 2); - public static Result TargetLocked => new(ModuleId, 3); - public static Result TargetAlreadyMounted => new(ModuleId, 4); - public static Result TargetNotMounted => new(ModuleId, 5); - public static Result AlreadyOpen => new(ModuleId, 6); - public static Result NotOpen => new(ModuleId, 7); - public static Result InternetRequestDenied => new(ModuleId, 8); - public static Result ServiceOpenLimitReached => new(ModuleId, 9); - public static Result SaveDataNotFound => new(ModuleId, 10); - public static Result NetworkServiceAccountNotAvailable => new(ModuleId, 31); - public static Result PassphrasePathNotFound => new(ModuleId, 80); - public static Result DataVerificationFailed => new(ModuleId, 81); - public static Result PermissionDenied => new(ModuleId, 90); - public static Result AllocationFailed => new(ModuleId, 91); - public static Result InvalidOperation => new(ModuleId, 98); - public static Result InvalidDeliveryCacheStorageFile => new(ModuleId, 204); - public static Result StorageOpenLimitReached => new(ModuleId, 205); - } -} diff --git a/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs deleted file mode 100644 index 953f6a225..000000000 --- a/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Ryujinx.Horizon.Bcat.Ipc; -using Ryujinx.Horizon.Bcat.Types; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Sf.Hipc; -using Ryujinx.Horizon.Sdk.Sm; -using System; - -namespace Ryujinx.Horizon.Bcat -{ - class BcatServerManager : ServerManager - { - public BcatServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) - { - } - - protected override Result OnNeedsToAccept(int portIndex, Server server) - { - return (BcatPortIndex)portIndex switch - { - BcatPortIndex.Admin => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.Admin)), - BcatPortIndex.Manager => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.Manager)), - BcatPortIndex.User => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.User)), - BcatPortIndex.System => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.System)), - _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), - }; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs deleted file mode 100644 index d1610f7d9..000000000 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs +++ /dev/null @@ -1,82 +0,0 @@ -using LibHac.Common; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Bcat; -using Ryujinx.Horizon.Sdk.Sf; -using System; -using System.Threading; -using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId; - -namespace Ryujinx.Horizon.Bcat.Ipc -{ - partial class ServiceCreator : IServiceCreator, IDisposable - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _libHacService; - - private int _disposalState; - - public ServiceCreator(string serviceName) - { - HorizonStatic.Options.BcatClient.Sm.GetService(ref _libHacService, serviceName).ThrowIfFailure(); - } - - [CmifCommand(0)] - public Result CreateBcatService(out IBcatService bcatService, [ClientProcessId] ulong pid) - { - // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId. - // Add an instance of nn::bcat::detail::service::core::PassphraseManager. - // Add an instance of nn::bcat::detail::service::ServiceMemoryManager. - // Add an instance of nn::bcat::detail::service::core::TaskManager who loads "bcat-sys:/" system save data and opens "dc/task.bin". - // If the file don't exist, create a new one (with a size of 0x800 bytes) and write 2 empty structs with a size of 0x400 bytes. - - bcatService = new BcatService(Bcat.Types.BcatServicePermissionLevel.User); - - return Result.Success; - } - - [CmifCommand(1)] - public Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, [ClientProcessId] ulong pid) - { - using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - - var resultCode = _libHacService.Get.CreateDeliveryCacheStorageService(ref libHacService.Ref, pid); - - if (resultCode.IsSuccess()) - { - service = new DeliveryCacheStorageService(ref libHacService.Ref); - } - else - { - service = null; - } - - return resultCode.ToHorizonResult(); - } - - [CmifCommand(2)] - public Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, ApplicationId applicationId) - { - using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - - var resultCode = _libHacService.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref libHacService.Ref, new LibHac.ApplicationId(applicationId.Id)); - - if (resultCode.IsSuccess()) - { - service = new DeliveryCacheStorageService(ref libHacService.Ref); - } - else - { - service = null; - } - - return resultCode.ToHorizonResult(); - } - - public void Dispose() - { - if (Interlocked.Exchange(ref _disposalState, 1) == 0) - { - _libHacService.Destroy(); - } - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs deleted file mode 100644 index e82f597e9..000000000 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Ryujinx.Horizon.Bcat.Types; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Bcat; -using Ryujinx.Horizon.Sdk.Sf; - -namespace Ryujinx.Horizon.Bcat.Ipc -{ - partial class BcatService : IBcatService - { - private readonly BcatServicePermissionLevel _permissionLevel; - - public BcatService(BcatServicePermissionLevel permissionLevel) - { - _permissionLevel = permissionLevel; - } - - [CmifCommand(10100)] - public Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService) - { - deliveryCacheProgressService = new DeliveryCacheProgressService(); - - return Result.Success; - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs deleted file mode 100644 index dd13eefb4..000000000 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs +++ /dev/null @@ -1,48 +0,0 @@ -using LibHac.Bcat; -using LibHac.Common; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Bcat; -using Ryujinx.Horizon.Sdk.Sf; -using Ryujinx.Horizon.Sdk.Sf.Hipc; -using System; -using System.Threading; - -namespace Ryujinx.Horizon.Bcat.Ipc -{ - partial class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService, IDisposable - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _libHacService; - private int _disposalState; - - public DeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> libHacService) - { - _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref libHacService); - } - - [CmifCommand(0)] - public Result Open(DirectoryName directoryName) - { - return _libHacService.Get.Open(ref directoryName).ToHorizonResult(); - } - - [CmifCommand(1)] - public Result Read(out int entriesRead, [Buffer(Sdk.Sf.Hipc.HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeliveryCacheDirectoryEntry> entriesBuffer) - { - return _libHacService.Get.Read(out entriesRead, entriesBuffer).ToHorizonResult(); - } - - [CmifCommand(2)] - public Result GetCount(out int count) - { - return _libHacService.Get.GetCount(out count).ToHorizonResult(); - } - - public void Dispose() - { - if (Interlocked.Exchange(ref _disposalState, 1) == 0) - { - _libHacService.Destroy(); - } - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs deleted file mode 100644 index d23f5f41f..000000000 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs +++ /dev/null @@ -1,54 +0,0 @@ -using LibHac.Bcat; -using LibHac.Common; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Bcat; -using Ryujinx.Horizon.Sdk.Sf; -using Ryujinx.Horizon.Sdk.Sf.Hipc; -using System; -using System.Threading; - -namespace Ryujinx.Horizon.Bcat.Ipc -{ - partial class DeliveryCacheFileService : IDeliveryCacheFileService, IDisposable - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _libHacService; - private int _disposalState; - - public DeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> libHacService) - { - _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref libHacService); - } - - [CmifCommand(0)] - public Result Open(DirectoryName directoryName, FileName fileName) - { - return _libHacService.Get.Open(ref directoryName, ref fileName).ToHorizonResult(); - } - - [CmifCommand(1)] - public Result Read(long offset, out long bytesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> data) - { - return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult(); - } - - [CmifCommand(2)] - public Result GetSize(out long size) - { - return _libHacService.Get.GetSize(out size).ToHorizonResult(); - } - - [CmifCommand(3)] - public Result GetDigest(out Digest digest) - { - return _libHacService.Get.GetDigest(out digest).ToHorizonResult(); - } - - public void Dispose() - { - if (Interlocked.Exchange(ref _disposalState, 1) == 0) - { - _libHacService.Destroy(); - } - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs deleted file mode 100644 index 91aa26867..000000000 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Horizon.Bcat.Ipc.Types; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Bcat; -using Ryujinx.Horizon.Sdk.OsTypes; -using Ryujinx.Horizon.Sdk.Sf; -using Ryujinx.Horizon.Sdk.Sf.Hipc; -using System; -using System.Threading; - -namespace Ryujinx.Horizon.Bcat.Ipc -{ - partial class DeliveryCacheProgressService : IDeliveryCacheProgressService, IDisposable - { - private int _handle; - private SystemEventType _systemEvent; - private int _disposalState; - - [CmifCommand(0)] - public Result GetEvent([CopyHandle] out int handle) - { - if (_handle == 0) - { - Os.CreateSystemEvent(out _systemEvent, EventClearMode.ManualClear, true).AbortOnFailure(); - - _handle = Os.GetReadableHandleOfSystemEvent(ref _systemEvent); - } - - handle = _handle; - - Logger.Stub?.PrintStub(LogClass.ServiceBcat); - - return Result.Success; - } - - [CmifCommand(1)] - public Result GetImpl([Buffer(HipcBufferFlags.Out | HipcBufferFlags.Pointer, 0x200)] out DeliveryCacheProgressImpl deliveryCacheProgressImpl) - { - deliveryCacheProgressImpl = new DeliveryCacheProgressImpl - { - State = DeliveryCacheProgressImpl.Status.Done, - Result = 0 - }; - - Logger.Stub?.PrintStub(LogClass.ServiceBcat); - - return Result.Success; - } - - public void Dispose() - { - if (_handle != 0 && Interlocked.Exchange(ref _disposalState, 1) == 0) - { - Os.DestroySystemEvent(ref _systemEvent); - } - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs deleted file mode 100644 index 9507a8a0e..000000000 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs +++ /dev/null @@ -1,74 +0,0 @@ -using LibHac.Bcat; -using LibHac.Common; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Bcat; -using Ryujinx.Horizon.Sdk.Sf; -using Ryujinx.Horizon.Sdk.Sf.Hipc; -using System; -using System.Threading; - -namespace Ryujinx.Horizon.Bcat.Ipc -{ - partial class DeliveryCacheStorageService : IDeliveryCacheStorageService, IDisposable - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _libHacService; - private int _disposalState; - - public DeliveryCacheStorageService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> libHacService) - { - _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref libHacService); - } - - [CmifCommand(0)] - public Result CreateFileService(out IDeliveryCacheFileService service) - { - using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); - - var resultCode = _libHacService.Get.CreateFileService(ref libHacService.Ref); - - if (resultCode.IsSuccess()) - { - service = new DeliveryCacheFileService(ref libHacService.Ref); - } - else - { - service = null; - } - - return resultCode.ToHorizonResult(); - } - - [CmifCommand(1)] - public Result CreateDirectoryService(out IDeliveryCacheDirectoryService service) - { - using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); - - var resultCode = _libHacService.Get.CreateDirectoryService(ref libHacService.Ref); - - if (resultCode.IsSuccess()) - { - service = new DeliveryCacheDirectoryService(ref libHacService.Ref); - } - else - { - service = null; - } - - return resultCode.ToHorizonResult(); - } - - [CmifCommand(10)] - public Result EnumerateDeliveryCacheDirectory(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DirectoryName> directoryNames) - { - return _libHacService.Get.EnumerateDeliveryCacheDirectory(out count, directoryNames).ToHorizonResult(); - } - - public void Dispose() - { - if (Interlocked.Exchange(ref _disposalState, 1) == 0) - { - _libHacService.Destroy(); - } - } - } -} diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs deleted file mode 100644 index e448dfdcd..000000000 --- a/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Horizon.Bcat.Types -{ - enum BcatPortIndex - { - Admin, - Manager, - User, - System - } -} diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs deleted file mode 100644 index 54d7461a7..000000000 --- a/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Horizon.Bcat.Types -{ - enum BcatServicePermissionLevel - { - Admin = -1, - User = 1, - System = 2, - Manager = 6 - } -} \ No newline at end of file diff --git a/src/Ryujinx.Horizon/HorizonOptions.cs b/src/Ryujinx.Horizon/HorizonOptions.cs index 1ec56bfa7..6d580e8b9 100644 --- a/src/Ryujinx.Horizon/HorizonOptions.cs +++ b/src/Ryujinx.Horizon/HorizonOptions.cs @@ -1,5 +1,3 @@ -using LibHac; - namespace Ryujinx.Horizon { public struct HorizonOptions @@ -7,13 +5,10 @@ namespace Ryujinx.Horizon public bool IgnoreMissingServices { get; } public bool ThrowOnInvalidCommandIds { get; } - public HorizonClient BcatClient { get; } - - public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient) + public HorizonOptions(bool ignoreMissingServices) { IgnoreMissingServices = ignoreMissingServices; ThrowOnInvalidCommandIds = true; - BcatClient = bcatClient; } } } diff --git a/src/Ryujinx.Horizon/LibHacResultExtensions.cs b/src/Ryujinx.Horizon/LibHacResultExtensions.cs deleted file mode 100644 index 01d181640..000000000 --- a/src/Ryujinx.Horizon/LibHacResultExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ryujinx.Horizon.Common; - -namespace Ryujinx.Horizon -{ - internal static class LibHacResultExtensions - { - public static Result ToHorizonResult(this LibHac.Result result) - { - return new Result((int)result.Module, (int)result.Description); - } - } -} diff --git a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs index b80399eaa..ba19ff6f6 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs @@ -9,12 +9,12 @@ namespace Ryujinx.Horizon.Prepo private const int PrepoMaxSessionsCount = 12; private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6; - private const int PointerBufferSize = 0x80; + private const int PointerBufferSize = 0x3800; private const int MaxDomains = 64; private const int MaxDomainObjects = 16; private const int MaxPortsCount = 6; - private static readonly ManagerOptions _prepoManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); private SmApi _sm; private PrepoServerManager _serverManager; @@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Prepo _sm = new SmApi(); _sm.Initialize().AbortOnFailure(); - _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _prepoManagerOptions, PrepoTotalMaxSessionsCount); + _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, PrepoTotalMaxSessionsCount); _serverManager.RegisterServer((int)PrepoPortIndex.Admin, ServiceName.Encode("prepo:a"), PrepoMaxSessionsCount); // 1.0.0-5.1.0 _serverManager.RegisterServer((int)PrepoPortIndex.Admin2, ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+ diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs deleted file mode 100644 index f6c8e6dd3..000000000 --- a/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Sf; - -namespace Ryujinx.Horizon.Sdk.Bcat -{ - internal interface IBcatService : IServiceObject - { - Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService); - } -} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs deleted file mode 100644 index 23a740c35..000000000 --- a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using LibHac.Bcat; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Sf; -using System; - -namespace Ryujinx.Horizon.Sdk.Bcat -{ - internal interface IDeliveryCacheDirectoryService : IServiceObject - { - Result GetCount(out int count); - Result Open(DirectoryName directoryName); - Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entriesBuffer); - } -} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs deleted file mode 100644 index a1e5d43ba..000000000 --- a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using LibHac.Bcat; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Sf; -using System; - -namespace Ryujinx.Horizon.Sdk.Bcat -{ - internal interface IDeliveryCacheFileService : IServiceObject - { - Result GetDigest(out Digest digest); - Result GetSize(out long size); - Result Open(DirectoryName directoryName, FileName fileName); - Result Read(long offset, out long bytesRead, Span<byte> data); - } -} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs deleted file mode 100644 index 07a796f88..000000000 --- a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ryujinx.Horizon.Bcat.Ipc.Types; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Sf; - -namespace Ryujinx.Horizon.Sdk.Bcat -{ - internal interface IDeliveryCacheProgressService : IServiceObject - { - Result GetEvent(out int handle); - Result GetImpl(out DeliveryCacheProgressImpl deliveryCacheProgressImpl); - } -} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs deleted file mode 100644 index 5638f074c..000000000 --- a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using LibHac.Bcat; -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Sf; -using System; - -namespace Ryujinx.Horizon.Sdk.Bcat -{ - internal interface IDeliveryCacheStorageService : IServiceObject - { - Result CreateDirectoryService(out IDeliveryCacheDirectoryService service); - Result CreateFileService(out IDeliveryCacheFileService service); - Result EnumerateDeliveryCacheDirectory(out int count, Span<DirectoryName> directoryNames); - } -} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs deleted file mode 100644 index edc525900..000000000 --- a/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ryujinx.Horizon.Common; -using Ryujinx.Horizon.Sdk.Sf; - -namespace Ryujinx.Horizon.Sdk.Bcat -{ - internal interface IServiceCreator : IServiceObject - { - Result CreateBcatService(out IBcatService service, ulong pid); - Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, ulong pid); - Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, Ncm.ApplicationId applicationId); - } -} diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs index d97457d92..2edc6ea10 100644 --- a/src/Ryujinx.Horizon/ServiceTable.cs +++ b/src/Ryujinx.Horizon/ServiceTable.cs @@ -1,4 +1,3 @@ -using Ryujinx.Horizon.Bcat; using Ryujinx.Horizon.LogManager; using Ryujinx.Horizon.Prepo; using System.Collections.Generic; @@ -24,7 +23,6 @@ namespace Ryujinx.Horizon RegisterService<LmMain>(); RegisterService<PrepoMain>(); - RegisterService<BcatMain>(); _totalServices = entries.Count; From 1f5e1ffa80e26b100478d27897fe3bc1be77f2df Mon Sep 17 00:00:00 2001 From: Horizon <omkar@anthaathi.co> Date: Fri, 5 May 2023 12:36:15 +0530 Subject: [PATCH 492/737] fix: linux launcher breaks when there are spaces in the directory path (#4795) * fix: linux launcher breaks when there are spaces in the directory path * Add quotes around $0 as well --------- Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- distribution/linux/Ryujinx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh index ccb8d65e1..91fb467df 100644 --- a/distribution/linux/Ryujinx.sh +++ b/distribution/linux/Ryujinx.sh @@ -1,6 +1,6 @@ #!/bin/sh -SCRIPT_DIR=$(dirname $(realpath $0)) +SCRIPT_DIR=$(dirname "$(realpath "$0")") RYUJINX_BIN="Ryujinx" if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then From 1f664100bd6d5244f63f973798f0bb1ffc80ae71 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 5 May 2023 09:39:08 +0200 Subject: [PATCH 493/737] ModLoader: Fix case sensitivy issues without breaking cheats (#4783) * Fix case sensitivity for mod subdirectories * Small refactoring of ModLoader * Don't share instruction list between all cheats Co-authored-by: riperiperi <rhy3756547@hotmail.com> --------- Co-authored-by: riperiperi <rhy3756547@hotmail.com> --- .../Controls/ApplicationContextMenu.axaml.cs | 48 ++-- .../UI/Windows/CheatWindow.axaml.cs | 4 +- src/Ryujinx.HLE/HOS/ModLoader.cs | 269 +++++++++--------- .../Extensions/LocalFileSystemExtensions.cs | 7 +- .../Processes/Extensions/NcaExtensions.cs | 5 +- .../Ui/Widgets/GameTableContextMenu.cs | 8 +- src/Ryujinx/Ui/Windows/CheatWindow.cs | 4 +- 7 files changed, 179 insertions(+), 166 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index dc0dee2ac..83fe29ea8 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Configuration; +using Ryujinx.HLE.HOS; using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; @@ -36,7 +37,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite; @@ -51,9 +52,10 @@ namespace Ryujinx.Ava.UI.Controls public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args) { - var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - - OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low)); + if ((sender as MenuItem)?.DataContext is MainWindowViewModel viewModel) + { + OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low)); + } } public void OpenDeviceSaveDirectory_Click(object sender, RoutedEventArgs args) @@ -70,9 +72,9 @@ namespace Ryujinx.Ava.UI.Controls OpenSaveDirectory(viewModel, SaveDataType.Bcat, userId: default); } - private void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId) + private static void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId) { - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { if (!ulong.TryParse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber)) { @@ -94,7 +96,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); } @@ -104,7 +106,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); } @@ -114,7 +116,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window); } @@ -124,10 +126,10 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { - string modsBasePath = viewModel.VirtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = viewModel.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -137,10 +139,10 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { - string sdModsBasePath = viewModel.VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = viewModel.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId); + string sdModsBasePath = ModLoader.GetSdModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -150,7 +152,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName), @@ -197,7 +199,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName), @@ -253,7 +255,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu"); string mainDir = Path.Combine(ptcDir, "0"); @@ -274,7 +276,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader"); @@ -291,7 +293,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); } @@ -301,7 +303,7 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { await ApplicationHelper.ExtractSection(NcaSectionType.Data, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); } @@ -311,10 +313,10 @@ namespace Ryujinx.Ava.UI.Controls { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - if (viewModel.SelectedApplication != null) + if (viewModel?.SelectedApplication != null) { await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index cb939763b..241a6c346 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -35,8 +35,8 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); - string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId); ulong titleIdValue = ulong.Parse(titleId, System.Globalization.NumberStyles.HexNumber); _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index 165125414..bde4d11ce 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS } // Title independent mods - public class PatchCache + private class PatchCache { public List<Mod<DirectoryInfo>> NsoPatches { get; } public List<Mod<DirectoryInfo>> NroPatches { get; } @@ -107,14 +107,14 @@ namespace Ryujinx.HLE.HOS } } - public Dictionary<ulong, ModCache> AppMods; // key is TitleId - public PatchCache Patches; + private readonly Dictionary<ulong, ModCache> _appMods; // key is TitleId + private PatchCache _patches; - private static readonly EnumerationOptions _dirEnumOptions; + private static readonly EnumerationOptions DirEnumOptions; static ModLoader() { - _dirEnumOptions = new EnumerationOptions + DirEnumOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, MatchType = MatchType.Simple, @@ -125,37 +125,73 @@ namespace Ryujinx.HLE.HOS public ModLoader() { - AppMods = new Dictionary<ulong, ModCache>(); - Patches = new PatchCache(); + _appMods = new Dictionary<ulong, ModCache>(); + _patches = new PatchCache(); } - public void Clear() + private void Clear() { - AppMods.Clear(); - Patches = new PatchCache(); + _appMods.Clear(); + _patches = new PatchCache(); } private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); - public string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); - public string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); + public static string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); + public static string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); - private string EnsureBaseDirStructure(string modsBasePath) + private static string EnsureBaseDirStructure(string modsBasePath) { var modsDir = new DirectoryInfo(modsBasePath); modsDir.CreateSubdirectory(AmsContentsDir); modsDir.CreateSubdirectory(AmsNsoPatchDir); modsDir.CreateSubdirectory(AmsNroPatchDir); - // modsDir.CreateSubdirectory(AmsKipPatchDir); // uncomment when KIPs are supported + // TODO: uncomment when KIPs are supported + // modsDir.CreateSubdirectory(AmsKipPatchDir); return modsDir.FullName; } private static DirectoryInfo FindTitleDir(DirectoryInfo contentsDir, string titleId) - => contentsDir.EnumerateDirectories($"{titleId}*", _dirEnumOptions).FirstOrDefault(); + => contentsDir.EnumerateDirectories($"{titleId}*", DirEnumOptions).FirstOrDefault(); - public string GetTitleDir(string modsBasePath, string titleId) + private static void AddModsFromDirectory(ModCache mods, DirectoryInfo dir, string titleId) + { + System.Text.StringBuilder types = new(); + + foreach (var modDir in dir.EnumerateDirectories()) + { + types.Clear(); + Mod<DirectoryInfo> mod = new("", null); + + if (StrEquals(RomfsDir, modDir.Name)) + { + mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} RomFs>", modDir)); + types.Append('R'); + } + else if (StrEquals(ExefsDir, modDir.Name)) + { + mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} ExeFs>", modDir)); + types.Append('E'); + } + else if (StrEquals(CheatDir, modDir.Name)) + { + types.Append('C', QueryCheatsDir(mods, modDir)); + } + else + { + AddModsFromDirectory(mods, modDir, titleId); + } + + if (types.Length > 0) + { + Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); + } + } + } + + public static string GetTitleDir(string modsBasePath, string titleId) { var contentsDir = new DirectoryInfo(Path.Combine(modsBasePath, AmsContentsDir)); var titleModsPath = FindTitleDir(contentsDir, titleId); @@ -170,17 +206,32 @@ namespace Ryujinx.HLE.HOS } // Static Query Methods - public static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) + private static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) { - if (cache.Initialized || !patchDir.Exists) return; + if (cache.Initialized || !patchDir.Exists) + { + return; + } - var patches = cache.KipPatches; - string type = null; + List<Mod<DirectoryInfo>> patches; + string type; - if (StrEquals(AmsNsoPatchDir, patchDir.Name)) { patches = cache.NsoPatches; type = "NSO"; } - else if (StrEquals(AmsNroPatchDir, patchDir.Name)) { patches = cache.NroPatches; type = "NRO"; } - else if (StrEquals(AmsKipPatchDir, patchDir.Name)) { patches = cache.KipPatches; type = "KIP"; } - else return; + if (StrEquals(AmsNsoPatchDir, patchDir.Name)) + { + patches = cache.NsoPatches; type = "NSO"; + } + else if (StrEquals(AmsNroPatchDir, patchDir.Name)) + { + patches = cache.NroPatches; type = "NRO"; + } + else if (StrEquals(AmsKipPatchDir, patchDir.Name)) + { + patches = cache.KipPatches; type = "KIP"; + } + else + { + return; + } foreach (var modDir in patchDir.EnumerateDirectories()) { @@ -189,9 +240,12 @@ namespace Ryujinx.HLE.HOS } } - public static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) + private static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) { - if (!titleDir.Exists) return; + if (!titleDir.Exists) + { + return; + } var fsFile = new FileInfo(Path.Combine(titleDir.FullName, RomfsContainer)); if (fsFile.Exists) @@ -205,64 +259,15 @@ namespace Ryujinx.HLE.HOS mods.ExefsContainers.Add(new Mod<FileInfo>($"<{titleDir.Name} ExeFs>", fsFile)); } - System.Text.StringBuilder types = new System.Text.StringBuilder(5); - - foreach (var modDir in titleDir.EnumerateDirectories()) - { - types.Clear(); - Mod<DirectoryInfo> mod = new Mod<DirectoryInfo>("", null); - - if (StrEquals(RomfsDir, modDir.Name)) - { - mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleDir.Name} RomFs>", modDir)); - types.Append('R'); - } - else if (StrEquals(ExefsDir, modDir.Name)) - { - mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleDir.Name} ExeFs>", modDir)); - types.Append('E'); - } - else if (StrEquals(CheatDir, modDir.Name)) - { - for (int i = 0; i < QueryCheatsDir(mods, modDir); i++) - { - types.Append('C'); - } - } - else - { - var romfs = new DirectoryInfo(Path.Combine(modDir.FullName, RomfsDir)); - var exefs = new DirectoryInfo(Path.Combine(modDir.FullName, ExefsDir)); - var cheat = new DirectoryInfo(Path.Combine(modDir.FullName, CheatDir)); - - if (romfs.Exists) - { - mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(modDir.Name, romfs)); - types.Append('R'); - } - - if (exefs.Exists) - { - mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(modDir.Name, exefs)); - types.Append('E'); - } - - if (cheat.Exists) - { - for (int i = 0; i < QueryCheatsDir(mods, cheat); i++) - { - types.Append('C'); - } - } - } - - if (types.Length > 0) Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); - } + AddModsFromDirectory(mods, titleDir, titleDir.Name); } public static void QueryContentsDir(ModCache mods, DirectoryInfo contentsDir, ulong titleId) { - if (!contentsDir.Exists) return; + if (!contentsDir.Exists) + { + return; + } Logger.Info?.Print(LogClass.ModLoader, $"Searching mods for {((titleId & 0x1000) != 0 ? "DLC" : "Title")} {titleId:X16}"); @@ -302,9 +307,16 @@ namespace Ryujinx.HLE.HOS continue; } + int oldCheatsCount = mods.Cheats.Count; + // A cheat file can contain several cheats for the same executable, so the file must be parsed in // order to properly enumerate them. mods.Cheats.AddRange(GetCheatsInFile(file)); + + if (mods.Cheats.Count - oldCheatsCount > 0) + { + numMods++; + } } return numMods; @@ -313,57 +325,54 @@ namespace Ryujinx.HLE.HOS private static IEnumerable<Cheat> GetCheatsInFile(FileInfo cheatFile) { string cheatName = DefaultCheatName; - List<string> instructions = new List<string>(); - List<Cheat> cheats = new List<Cheat>(); + List<string> instructions = new(); + List<Cheat> cheats = new(); - using (StreamReader cheatData = cheatFile.OpenText()) + using StreamReader cheatData = cheatFile.OpenText(); + while (cheatData.ReadLine() is { } line) { - string line; - while ((line = cheatData.ReadLine()) != null) + line = line.Trim(); + + if (line.StartsWith('[')) { - line = line.Trim(); - - if (line.StartsWith('[')) + // This line starts a new cheat section. + if (!line.EndsWith(']') || line.Length < 3) { - // This line starts a new cheat section. - if (!line.EndsWith(']') || line.Length < 3) - { - // Skip the entire file if there's any error while parsing the cheat file. + // Skip the entire file if there's any error while parsing the cheat file. - Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); + Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); - return new List<Cheat>(); - } - - // Add the previous section to the list. - if (instructions.Count != 0) - { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); - } - - // Start a new cheat section. - cheatName = line.Substring(1, line.Length - 2); - instructions = new List<string>(); + return Array.Empty<Cheat>(); } - else if (line.Length > 0) + + // Add the previous section to the list. + if (instructions.Count > 0) { - // The line contains an instruction. - instructions.Add(line); + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); } + + // Start a new cheat section. + cheatName = line.Substring(1, line.Length - 2); + instructions = new List<string>(); } - - // Add the last section being processed. - if (instructions.Count != 0) + else if (line.Length > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + // The line contains an instruction. + instructions.Add(line); } } + // Add the last section being processed. + if (instructions.Count > 0) + { + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + } + return cheats; } // Assumes searchDirPaths don't overlap - public static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths) + private static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths) { static bool IsPatchesDir(string name) => StrEquals(AmsNsoPatchDir, name) || StrEquals(AmsNroPatchDir, name) || @@ -375,7 +384,7 @@ namespace Ryujinx.HLE.HOS { if (IsContentsDir(searchDir.Name)) { - foreach (var (titleId, cache) in modCaches) + foreach ((ulong titleId, ModCache cache) in modCaches) { QueryContentsDir(cache, searchDir, titleId); } @@ -419,15 +428,15 @@ namespace Ryujinx.HLE.HOS foreach (ulong titleId in titles) { - AppMods[titleId] = new ModCache(); + _appMods[titleId] = new ModCache(); } - CollectMods(AppMods, Patches, searchDirPaths); + CollectMods(_appMods, _patches, searchDirPaths); } internal IStorage ApplyRomFsMods(ulong titleId, IStorage baseStorage) { - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) { return baseStorage; } @@ -487,7 +496,7 @@ namespace Ryujinx.HLE.HOS return newStorage; } - private static void AddFiles(IFileSystem fs, string modName, HashSet<string> fileSet, RomFsBuilder builder) + private static void AddFiles(IFileSystem fs, string modName, ISet<string> fileSet, RomFsBuilder builder) { foreach (var entry in fs.EnumerateEntries() .Where(f => f.Type == DirectoryEntryType.File) @@ -509,7 +518,7 @@ namespace Ryujinx.HLE.HOS internal bool ReplaceExefsPartition(ulong titleId, ref IFileSystem exefs) { - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) { return false; } @@ -537,13 +546,13 @@ namespace Ryujinx.HLE.HOS internal ModLoadResult ApplyExefsMods(ulong titleId, NsoExecutable[] nsos) { - ModLoadResult modLoadResult = new ModLoadResult + ModLoadResult modLoadResult = new() { Stubs = new BitVector32(), Replaces = new BitVector32() }; - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) { return modLoadResult; } @@ -561,7 +570,7 @@ namespace Ryujinx.HLE.HOS { var nsoName = ProcessConst.ExeFsPrefixes[i]; - FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName)); + FileInfo nsoFile = new(Path.Combine(mod.Path.FullName, nsoName)); if (nsoFile.Exists) { if (modLoadResult.Replaces[1 << i]) @@ -580,7 +589,7 @@ namespace Ryujinx.HLE.HOS modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension)); } - FileInfo npdmFile = new FileInfo(Path.Combine(mod.Path.FullName, "main.npdm")); + FileInfo npdmFile = new(Path.Combine(mod.Path.FullName, "main.npdm")); if (npdmFile.Exists) { if (modLoadResult.Npdm != null) @@ -611,7 +620,7 @@ namespace Ryujinx.HLE.HOS internal void ApplyNroPatches(NroExecutable nro) { - var nroPatches = Patches.NroPatches; + var nroPatches = _patches.NroPatches; if (nroPatches.Count == 0) return; @@ -622,9 +631,9 @@ namespace Ryujinx.HLE.HOS internal bool ApplyNsoPatches(ulong titleId, params IExecutable[] programs) { - IEnumerable<Mod<DirectoryInfo>> nsoMods = Patches.NsoPatches; + IEnumerable<Mod<DirectoryInfo>> nsoMods = _patches.NsoPatches; - if (AppMods.TryGetValue(titleId, out ModCache mods)) + if (_appMods.TryGetValue(titleId, out ModCache mods)) { nsoMods = nsoMods.Concat(mods.ExefsDirs); } @@ -636,7 +645,7 @@ namespace Ryujinx.HLE.HOS internal void LoadCheats(ulong titleId, ProcessTamperInfo tamperInfo, TamperMachine tamperMachine) { - if (tamperInfo == null || tamperInfo.BuildIds == null || tamperInfo.CodeAddresses == null) + if (tamperInfo?.BuildIds == null || tamperInfo.CodeAddresses == null) { Logger.Error?.Print(LogClass.ModLoader, "Unable to install cheat because the associated process is invalid"); @@ -645,14 +654,14 @@ namespace Ryujinx.HLE.HOS Logger.Info?.Print(LogClass.ModLoader, $"Build ids found for title {titleId:X16}:\n {String.Join("\n ", tamperInfo.BuildIds)}"); - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) { return; } var cheats = mods.Cheats; var processExes = tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses, (k, v) => new { k, v }) - .ToDictionary(x => x.k.Substring(0, Math.Min(Cheat.CheatIdSize, x.k.Length)), x => x.v); + .ToDictionary(x => x.k[..Math.Min(Cheat.CheatIdSize, x.k.Length)], x => x.v); foreach (var cheat in cheats) { @@ -758,4 +767,4 @@ namespace Ryujinx.HLE.HOS return count > 0; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs index 28d907851..fb85329d2 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -2,6 +2,7 @@ using LibHac.FsSystem; using LibHac.Loader; using LibHac.Ns; +using Ryujinx.HLE.HOS; using Ryujinx.HLE.Loaders.Processes.Extensions; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -17,8 +18,8 @@ namespace Ryujinx.HLE.Loaders.Processes device.Configuration.VirtualFileSystem.ModLoader.CollectMods( new[] { programId }, - device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + ModLoader.GetModsBasePath(), + ModLoader.GetSdModsBasePath()); if (programId != 0) { @@ -36,4 +37,4 @@ namespace Ryujinx.HLE.Loaders.Processes return processResult; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index 473f374db..e11b81d7f 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -8,6 +8,7 @@ using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS; using System.IO; using System.Linq; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -35,8 +36,8 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions // Collecting mods related to AocTitleIds and ProgramId. device.Configuration.VirtualFileSystem.ModLoader.CollectMods( device.Configuration.ContentManager.GetAocTitleIds().Prepend(metaLoader.GetProgramId()), - device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + ModLoader.GetModsBasePath(), + ModLoader.GetSdModsBasePath()); // Load Nacp file. var nacpData = new BlitStruct<ApplicationControlProperty>(1); diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 558288aab..6d8331658 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -460,16 +460,16 @@ namespace Ryujinx.Ui.Widgets private void OpenTitleModDir_Clicked(object sender, EventArgs args) { - string modsBasePath = _virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, _titleIdText); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) { - string sdModsBasePath = _virtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); + string sdModsBasePath = ModLoader.GetSdModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs index 917603b29..7dbea0128 100644 --- a/src/Ryujinx/Ui/Windows/CheatWindow.cs +++ b/src/Ryujinx/Ui/Windows/CheatWindow.cs @@ -28,8 +28,8 @@ namespace Ryujinx.Ui.Windows builder.Autoconnect(this); _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; - string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt"); From 1f5d8818609a82df893167a8ec5bd6ccda406c61 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Fri, 5 May 2023 14:47:15 +0100 Subject: [PATCH 494/737] GPU: Allow granular buffer updates from the constant buffer updater (#4749) * GPU: Allow granular buffer updates from the constant buffer updater Sometimes, constant buffer updates can't be avoided, either due to a cb0 access that cannot be eliminated, or the game updating a buffer between draws to the detriment of everyone. To avoid uploading the full 4096 bytes each time, this PR remembers the offset and size containing all constant buffer updates since the last sync. It will then upload that range after sync. * Allow clearing the dirty range * Always use precise Might want to not do this if distance between the existing range and new one is too high. * Use old force dirty mechanism when distance between regions is too great * Update src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Fix inheritance of _dirtyStart and _dirtyEnd --------- Co-authored-by: gdkchan <gab.dark.100@gmail.com> --- src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 109 +++++++++++++++++++++- 1 file changed, 104 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index ef8c80746..8e16b3ae1 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -68,6 +68,9 @@ namespace Ryujinx.Graphics.Gpu.Memory private int _referenceCount = 1; + private ulong _dirtyStart = ulong.MaxValue; + private ulong _dirtyEnd = ulong.MaxValue; + /// <summary> /// Creates a new instance of the buffer. /// </summary> @@ -221,6 +224,26 @@ namespace Ryujinx.Graphics.Gpu.Memory } _sequenceNumber = _context.SequenceNumber; + _dirtyStart = ulong.MaxValue; + } + } + + if (_dirtyStart != ulong.MaxValue) + { + ulong end = address + size; + + if (end > _dirtyStart && address < _dirtyEnd) + { + if (_modifiedRanges != null) + { + _modifiedRanges.ExcludeModifiedRegions(_dirtyStart, _dirtyEnd - _dirtyStart, _loadDelegate); + } + else + { + LoadRegion(_dirtyStart, _dirtyEnd - _dirtyStart); + } + + _dirtyStart = ulong.MaxValue; } } } @@ -291,7 +314,7 @@ namespace Ryujinx.Graphics.Gpu.Memory } /// <summary> - /// Inherit modified ranges from another buffer. + /// Inherit modified and dirty ranges from another buffer. /// </summary> /// <param name="from">The buffer to inherit from</param> public void InheritModifiedRanges(Buffer from) @@ -320,6 +343,11 @@ namespace Ryujinx.Graphics.Gpu.Memory _modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction); } + + if (from._dirtyStart != ulong.MaxValue) + { + ForceDirty(from._dirtyStart, from._dirtyEnd - from._dirtyStart); + } } /// <summary> @@ -338,6 +366,44 @@ namespace Ryujinx.Graphics.Gpu.Memory return false; } + /// <summary> + /// Clear the dirty range that overlaps with the given region. + /// </summary> + /// <param name="address">Start address of the modified region</param> + /// <param name="size">Size of the modified region</param> + private void ClearDirty(ulong address, ulong size) + { + if (_dirtyStart != ulong.MaxValue) + { + ulong end = address + size; + + if (end > _dirtyStart && address < _dirtyEnd) + { + if (address <= _dirtyStart) + { + // Cut off the start. + + if (end < _dirtyEnd) + { + _dirtyStart = end; + } + else + { + _dirtyStart = ulong.MaxValue; + } + } + else if (end >= _dirtyEnd) + { + // Cut off the end. + + _dirtyEnd = address; + } + + // If fully contained, do nothing. + } + } + } + /// <summary> /// Indicate that a region of the buffer was modified, and must be loaded from memory. /// </summary> @@ -357,6 +423,8 @@ namespace Ryujinx.Graphics.Gpu.Memory mSize = maxSize; } + ClearDirty(mAddress, mSize); + if (_modifiedRanges != null) { _modifiedRanges.ExcludeModifiedRegions(mAddress, mSize, _loadDelegate); @@ -380,14 +448,12 @@ namespace Ryujinx.Graphics.Gpu.Memory } /// <summary> - /// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check. + /// Force a region of the buffer to be dirty within the memory tracking. Avoids reprotection and nullifies sequence number check. /// </summary> /// <param name="mAddress">Start address of the modified region</param> /// <param name="mSize">Size of the region to force dirty</param> - public void ForceDirty(ulong mAddress, ulong mSize) + private void ForceTrackingDirty(ulong mAddress, ulong mSize) { - _modifiedRanges?.Clear(mAddress, mSize); - if (_useGranular) { _memoryTrackingGranular.ForceDirty(mAddress, mSize); @@ -399,6 +465,39 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// <summary> + /// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check. + /// </summary> + /// <param name="mAddress">Start address of the modified region</param> + /// <param name="mSize">Size of the region to force dirty</param> + public void ForceDirty(ulong mAddress, ulong mSize) + { + _modifiedRanges?.Clear(mAddress, mSize); + + ulong end = mAddress + mSize; + + if (_dirtyStart == ulong.MaxValue) + { + _dirtyStart = mAddress; + _dirtyEnd = end; + } + else + { + // Is the new range more than a page away from the existing one? + + if ((long)(mAddress - _dirtyEnd) >= (long)MemoryManager.PageSize || + (long)(_dirtyStart - end) >= (long)MemoryManager.PageSize) + { + ForceTrackingDirty(mAddress, mSize); + } + else + { + _dirtyStart = Math.Min(_dirtyStart, mAddress); + _dirtyEnd = Math.Max(_dirtyEnd, end); + } + } + } + /// <summary> /// Performs copy of all the buffer data from one buffer to another. /// </summary> From aa021085cfab10ab63a7e6c2f9c9e29b4111525c Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 5 May 2023 11:20:20 -0300 Subject: [PATCH 495/737] Allow any shader SSBO constant buffer slot and offset (#2237) * Allow any shader SSBO constant buffer slot and offset * Fix slot value passed to SetUsedStorageBuffer on fallback case * Shader cache version * Ensure that the storage buffer source constant buffer offset is word aligned * Fix FirstBinding on GetUniformBufferDescriptors --- .../Engine/Compute/ComputeClass.cs | 7 +- .../Engine/Threed/StateUpdater.cs | 7 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../BufferDescriptor.cs | 18 ++- .../BufferUsageFlags.cs | 2 +- .../Translation/GlobalMemory.cs | 2 + .../Optimizations/GlobalToStorage.cs | 104 +++++++++++++----- .../Translation/Optimizations/Optimizer.cs | 1 - .../Translation/Rewriter.cs | 11 +- .../Translation/ShaderConfig.cs | 92 +++++++++++++++- 10 files changed, 194 insertions(+), 52 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 2ac738fdf..4ec23c791 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -157,11 +157,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { BufferDescriptor sb = info.SBuffers[index]; - ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0); - - int sbDescOffset = 0x310 + sb.Slot * 0x10; - - sbDescAddress += (ulong)sbDescOffset; + ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot); + sbDescAddress += (ulong)sb.SbCbOffset * 4; SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 00e09a310..1c9bf1d2a 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -351,11 +351,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { BufferDescriptor sb = info.SBuffers[index]; - ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, 0); - - int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10; - - sbDescAddress += (ulong)sbDescOffset; + ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot); + sbDescAddress += (ulong)sb.SbCbOffset * 4; SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b182f2995..85233c0a3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4735; + private const uint CodeGenVersion = 2237; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs index 4ce8a896d..410c1991d 100644 --- a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -5,13 +5,27 @@ namespace Ryujinx.Graphics.Shader // New fields should be added to the end of the struct to keep disk shader cache compatibility. public readonly int Binding; - public readonly int Slot; + public readonly byte Slot; + public readonly byte SbCbSlot; + public readonly ushort SbCbOffset; public BufferUsageFlags Flags; public BufferDescriptor(int binding, int slot) { Binding = binding; - Slot = slot; + Slot = (byte)slot; + SbCbSlot = 0; + SbCbOffset = 0; + + Flags = BufferUsageFlags.None; + } + + public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset) + { + Binding = binding; + Slot = (byte)slot; + SbCbSlot = (byte)sbCbSlot; + SbCbOffset = (ushort)sbCbOffset; Flags = BufferUsageFlags.None; } diff --git a/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs b/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs index 657546cb7..ab81d5756 100644 --- a/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs +++ b/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Shader /// Flags that indicate how a buffer will be used in a shader. /// </summary> [Flags] - public enum BufferUsageFlags + public enum BufferUsageFlags : byte { None = 0, diff --git a/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs b/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs index 774a128d8..a81d0fc4b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Shader.Translation public const int UbeDescsSize = StorageDescSize * UbeMaxCount; public const int UbeFirstCbuf = 8; + public const int DriverReservedCb = 0; + public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind) { return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) || diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 2a4070e0a..a83682445 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -8,6 +8,20 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class GlobalToStorage { + private struct SearchResult + { + public static SearchResult NotFound => new SearchResult(-1, 0); + public bool Found => SbCbSlot != -1; + public int SbCbSlot { get; } + public int SbCbOffset { get; } + + public SearchResult(int sbCbSlot, int sbCbOffset) + { + SbCbSlot = sbCbSlot; + SbCbOffset = sbCbOffset; + } + } + public static void RunPass(BasicBlock block, ShaderConfig config, ref int sbUseMask, ref int ubeUseMask) { int sbStart = GetStorageBaseCbOffset(config.Stage); @@ -49,30 +63,33 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { Operand source = operation.GetSource(0); - int storageIndex = SearchForStorageBase(block, source, sbStart, sbEnd); - - if (storageIndex >= 0) + var result = SearchForStorageBase(config, block, source); + if (!result.Found) { - // Storage buffers are implemented using global memory access. - // If we know from where the base address of the access is loaded, - // we can guess which storage buffer it is accessing. - // We can then replace the global memory access with a storage - // buffer access. - node = ReplaceGlobalWithStorage(block, node, config, storageIndex); + continue; } - else if (config.Stage == ShaderStage.Compute && operation.Inst == Instruction.LoadGlobal) + + if (config.Stage == ShaderStage.Compute && + operation.Inst == Instruction.LoadGlobal && + result.SbCbSlot == DriverReservedCb && + result.SbCbOffset >= UbeBaseOffset && + result.SbCbOffset < UbeBaseOffset + UbeDescsSize) { // Here we effectively try to replace a LDG instruction with LDC. // The hardware only supports a limited amount of constant buffers // so NVN "emulates" more constant buffers using global memory access. // Here we try to replace the global access back to a constant buffer // load. - storageIndex = SearchForStorageBase(block, source, ubeStart, ubeStart + ubeEnd); - - if (storageIndex >= 0) - { - node = ReplaceLdgWithLdc(node, config, storageIndex); - } + node = ReplaceLdgWithLdc(node, config, (result.SbCbOffset - UbeBaseOffset) / StorageDescSize); + } + else + { + // Storage buffers are implemented using global memory access. + // If we know from where the base address of the access is loaded, + // we can guess which storage buffer it is accessing. + // We can then replace the global memory access with a storage + // buffer access. + node = ReplaceGlobalWithStorage(block, node, config, config.GetSbSlot((byte)result.SbCbSlot, (ushort)result.SbCbOffset)); } } } @@ -159,7 +176,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (byteOffset == null) { - Operand baseAddrLow = Cbuf(0, baseAddressCbOffset); + (int sbCbSlot, int sbCbOffset) = config.GetSbCbInfo(storageIndex); + + Operand baseAddrLow = Cbuf(sbCbSlot, sbCbOffset); Operand baseAddrTrunc = Local(); Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); @@ -360,20 +379,20 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return node; } - private static int SearchForStorageBase(BasicBlock block, Operand globalAddress, int sbStart, int sbEnd) + private static SearchResult SearchForStorageBase(ShaderConfig config, BasicBlock block, Operand globalAddress) { globalAddress = Utils.FindLastOperation(globalAddress, block); if (globalAddress.Type == OperandType.ConstantBuffer) { - return GetStorageIndex(globalAddress, sbStart, sbEnd); + return GetStorageIndex(config, globalAddress); } Operation operation = globalAddress.AsgOp as Operation; if (operation == null || operation.Inst != Instruction.Add) { - return -1; + return SearchResult.NotFound; } Operand src1 = operation.GetSource(0); @@ -382,34 +401,65 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if ((src1.Type == OperandType.LocalVariable && src2.Type == OperandType.Constant) || (src2.Type == OperandType.LocalVariable && src1.Type == OperandType.Constant)) { + Operand baseAddr; + if (src1.Type == OperandType.LocalVariable) { - operation = Utils.FindLastOperation(src1, block).AsgOp as Operation; + baseAddr = Utils.FindLastOperation(src1, block); } else { - operation = Utils.FindLastOperation(src2, block).AsgOp as Operation; + baseAddr = Utils.FindLastOperation(src2, block); } + var result = GetStorageIndex(config, baseAddr); + if (result.Found) + { + return result; + } + + operation = baseAddr.AsgOp as Operation; + if (operation == null || operation.Inst != Instruction.Add) { - return -1; + return SearchResult.NotFound; } } + var selectedResult = SearchResult.NotFound; + for (int index = 0; index < operation.SourcesCount; index++) { Operand source = operation.GetSource(index); - int storageIndex = GetStorageIndex(source, sbStart, sbEnd); + var result = GetStorageIndex(config, source); - if (storageIndex != -1) + // If we already have a result, we give preference to the ones from + // the driver reserved constant buffer, as those are the ones that + // contains the base address. + if (result.Found && (!selectedResult.Found || result.SbCbSlot == GlobalMemory.DriverReservedCb)) { - return storageIndex; + selectedResult = result; } } - return -1; + return selectedResult; + } + + private static SearchResult GetStorageIndex(ShaderConfig config, Operand operand) + { + if (operand.Type == OperandType.ConstantBuffer) + { + int slot = operand.GetCbufSlot(); + int offset = operand.GetCbufOffset(); + + if ((offset & 3) == 0) + { + return new SearchResult(slot, offset); + } + } + + return SearchResult.NotFound; } private static int GetStorageIndex(Operand operand, int sbStart, int sbEnd) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index bae774ee4..16848bdc8 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -68,7 +68,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } ConstantFolding.RunPass(operation); - Simplification.RunPass(operation); if (DestIsLocalVar(operation)) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 91e7ace1e..8167efc1d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -110,9 +110,9 @@ namespace Ryujinx.Graphics.Shader.Translation Operand BindingRangeCheck(int cbOffset, out Operand baseAddrLow) { - baseAddrLow = Cbuf(0, cbOffset); - Operand baseAddrHigh = Cbuf(0, cbOffset + 1); - Operand size = Cbuf(0, cbOffset + 2); + baseAddrLow = Cbuf(DriverReservedCb, cbOffset); + Operand baseAddrHigh = Cbuf(DriverReservedCb, cbOffset + 1); + Operand size = Cbuf(DriverReservedCb, cbOffset + 2); Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow); Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow); @@ -134,9 +134,10 @@ namespace Ryujinx.Graphics.Shader.Translation sbUseMask &= ~(1 << slot); - config.SetUsedStorageBuffer(slot, isWrite); - int cbOffset = GetStorageCbOffset(config.Stage, slot); + slot = config.GetSbSlot(DriverReservedCb, (ushort)cbOffset); + + config.SetUsedStorageBuffer(slot, isWrite); Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow); diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 22f5a671d..ae60bcc6c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -125,6 +125,9 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures; private readonly Dictionary<TextureInfo, TextureMeta> _usedImages; + private readonly Dictionary<int, int> _sbSlots; + private readonly Dictionary<int, int> _sbSlotsReverse; + private BufferDescriptor[] _cachedConstantBufferDescriptors; private BufferDescriptor[] _cachedStorageBufferDescriptors; private TextureDescriptor[] _cachedTextureDescriptors; @@ -152,6 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); _usedImages = new Dictionary<TextureInfo, TextureMeta>(); + + _sbSlots = new Dictionary<int, int>(); + _sbSlotsReverse = new Dictionary<int, int>(); } public ShaderConfig( @@ -770,9 +776,8 @@ namespace Ryujinx.Graphics.Shader.Translation usedMask |= (int)GpuAccessor.QueryConstantBufferUse(); } - return _cachedConstantBufferDescriptors = GetBufferDescriptors( + return _cachedConstantBufferDescriptors = GetUniformBufferDescriptors( usedMask, - 0, UsedFeatures.HasFlag(FeatureFlags.CbIndexing), out _firstConstantBufferBinding, GpuAccessor.QueryBindingConstantBuffer); @@ -785,7 +790,7 @@ namespace Ryujinx.Graphics.Shader.Translation return _cachedStorageBufferDescriptors; } - return _cachedStorageBufferDescriptors = GetBufferDescriptors( + return _cachedStorageBufferDescriptors = GetStorageBufferDescriptors( _usedStorageBuffers, _usedStorageBuffersWrite, true, @@ -793,7 +798,48 @@ namespace Ryujinx.Graphics.Shader.Translation GpuAccessor.QueryBindingStorageBuffer); } - private static BufferDescriptor[] GetBufferDescriptors( + private static BufferDescriptor[] GetUniformBufferDescriptors(int usedMask, bool isArray, out int firstBinding, Func<int, int> getBindingCallback) + { + firstBinding = 0; + int lastSlot = -1; + bool hasFirstBinding = false; + var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)]; + + for (int i = 0; i < descriptors.Length; i++) + { + int slot = BitOperations.TrailingZeroCount(usedMask); + + if (isArray) + { + // The next array entries also consumes bindings, even if they are unused. + for (int j = lastSlot + 1; j < slot; j++) + { + int binding = getBindingCallback(j); + + if (!hasFirstBinding) + { + firstBinding = binding; + hasFirstBinding = true; + } + } + } + + lastSlot = slot; + descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot); + + if (!hasFirstBinding) + { + firstBinding = descriptors[i].Binding; + hasFirstBinding = true; + } + + usedMask &= ~(1 << slot); + } + + return descriptors; + } + + private BufferDescriptor[] GetStorageBufferDescriptors( int usedMask, int writtenMask, bool isArray, @@ -827,7 +873,9 @@ namespace Ryujinx.Graphics.Shader.Translation lastSlot = slot; - descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot); + (int sbCbSlot, int sbCbOffset) = GetSbCbInfo(slot); + + descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot, sbCbSlot, sbCbOffset); if (!hasFirstBinding) { @@ -924,6 +972,40 @@ namespace Ryujinx.Graphics.Shader.Translation return FindDescriptorIndex(GetImageDescriptors(), texOp); } + public int GetSbSlot(byte sbCbSlot, ushort sbCbOffset) + { + int key = PackSbCbInfo(sbCbSlot, sbCbOffset); + + if (!_sbSlots.TryGetValue(key, out int slot)) + { + slot = _sbSlots.Count; + _sbSlots.Add(key, slot); + _sbSlotsReverse.Add(slot, key); + } + + return slot; + } + + public (int, int) GetSbCbInfo(int slot) + { + if (_sbSlotsReverse.TryGetValue(slot, out int key)) + { + return UnpackSbCbInfo(key); + } + + throw new ArgumentException($"Invalid slot {slot}.", nameof(slot)); + } + + private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset) + { + return sbCbOffset | ((int)sbCbSlot << 16); + } + + private static (int, int) UnpackSbCbInfo(int key) + { + return ((byte)(key >> 16), (ushort)key); + } + public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None) { return new ShaderProgramInfo( From 9ff21f9ab6b81b8a36227e76595da6ed61c5bf53 Mon Sep 17 00:00:00 2001 From: "Skyth (Asilkan)" <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Fri, 5 May 2023 19:35:59 +0300 Subject: [PATCH 496/737] Use ToLowerInvariant when detecting GPU vendor. (#4815) --- src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index bf365b4de..dd0b1501f 100644 --- a/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.OpenGL private static GpuVendor GetGpuVendor() { - string vendor = GL.GetString(StringName.Vendor).ToLower(); + string vendor = GL.GetString(StringName.Vendor).ToLowerInvariant(); if (vendor == "nvidia corporation") { @@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.OpenGL } else if (vendor == "intel") { - string renderer = GL.GetString(StringName.Renderer).ToLower(); + string renderer = GL.GetString(StringName.Renderer).ToLowerInvariant(); return renderer.Contains("mesa") ? GpuVendor.IntelUnix : GpuVendor.IntelWindows; } From f8ec878796f194e56cf6a258577ec0f2edd213ef Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine <eltociear@gmail.com> Date: Sat, 6 May 2023 05:17:36 +0900 Subject: [PATCH 497/737] Fix typo in TextureBindingsManager.cs (#4798) accomodate -> accommodate --- src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index bbfb704d0..b08fb3eb1 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -537,7 +537,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (hostTexture != null && texture.Target == Target.TextureBuffer) { // Ensure that the buffer texture is using the correct buffer as storage. - // Buffers are frequently re-created to accomodate larger data, so we need to re-bind + // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false); @@ -666,7 +666,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (hostTexture != null && texture.Target == Target.TextureBuffer) { // Ensure that the buffer texture is using the correct buffer as storage. - // Buffers are frequently re-created to accomodate larger data, so we need to re-bind + // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. Format format = bindingInfo.Format; @@ -879,4 +879,4 @@ namespace Ryujinx.Graphics.Gpu.Image Array.Clear(_imageState); } } -} \ No newline at end of file +} From e5c9838b0bb8e53c9802c8d33d3b46843737f2f2 Mon Sep 17 00:00:00 2001 From: ashuk <84274627+ashuk2000@users.noreply.github.com> Date: Sat, 6 May 2023 02:08:57 +0530 Subject: [PATCH 498/737] Correct tooltips for add,remove,removeall buttons (#4819) --- src/Ryujinx/Ui/Windows/DlcWindow.glade | 6 +++--- src/Ryujinx/Ui/Windows/TitleUpdateWindow.glade | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx/Ui/Windows/DlcWindow.glade b/src/Ryujinx/Ui/Windows/DlcWindow.glade index cd0d86744..bdb0e647a 100644 --- a/src/Ryujinx/Ui/Windows/DlcWindow.glade +++ b/src/Ryujinx/Ui/Windows/DlcWindow.glade @@ -89,7 +89,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> - <property name="tooltip_text" translatable="yes">Adds an update to this list</property> + <property name="tooltip_text" translatable="yes">Adds a DLC to this list</property> <property name="margin_left">10</property> <signal name="clicked" handler="AddButton_Clicked" swapped="no"/> </object> @@ -105,7 +105,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> - <property name="tooltip_text" translatable="yes">Removes the selected update</property> + <property name="tooltip_text" translatable="yes">Removes the selected DLC</property> <property name="margin_left">10</property> <signal name="clicked" handler="RemoveButton_Clicked" swapped="no"/> </object> @@ -121,7 +121,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> - <property name="tooltip_text" translatable="yes">Removes the selected update</property> + <property name="tooltip_text" translatable="yes">Removes all DLCs</property> <property name="margin_left">10</property> <signal name="clicked" handler="RemoveAllButton_Clicked" swapped="no"/> </object> diff --git a/src/Ryujinx/Ui/Windows/TitleUpdateWindow.glade b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.glade index de557471a..cfbac86dd 100644 --- a/src/Ryujinx/Ui/Windows/TitleUpdateWindow.glade +++ b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.glade @@ -133,7 +133,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> - <property name="tooltip_text" translatable="yes">Removes the selected update</property> + <property name="tooltip_text" translatable="yes">Removes all the updates</property> <property name="margin_left">10</property> <signal name="clicked" handler="RemoveAllButton_Clicked" swapped="no"/> </object> From d6698680bef4ac37b63d67c5415edf5717a84b3a Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Fri, 5 May 2023 23:24:35 +0200 Subject: [PATCH 499/737] UI: Fix sections extraction (#4820) * UI: Fix sections extraction There is currently an issue when the update NCA doesn't contains the section we want to extract, this is fixed by adding a check. I have fixed the inverted handler of ExeFs/Logo introduced in #4755. Fixes #4521 * Addresses feedback --- src/Ryujinx.Ava/Common/ApplicationHelper.cs | 11 ++++++++--- .../UI/Controls/ApplicationContextMenu.axaml | 4 ++-- .../UI/Controls/ApplicationContextMenu.axaml.cs | 8 ++++---- src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs | 10 ++++++++-- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Ava/Common/ApplicationHelper.cs b/src/Ryujinx.Ava/Common/ApplicationHelper.cs index 8c36a6365..c961d76c8 100644 --- a/src/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/src/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -233,9 +233,14 @@ namespace Ryujinx.Ava.Common try { - IFileSystem ncaFileSystem = patchNca != null - ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) - : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); + bool sectionExistsInPatch = false; + if (patchNca != null) + { + sectionExistsInPatch = patchNca.CanOpenSection(index); + } + + IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) + : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); FileSystemClient fsClient = _horizonClient.Fs; diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml index 1750e8000..35d5fe859 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml @@ -65,7 +65,7 @@ </MenuItem> <MenuItem Header="{locale:Locale GameListContextMenuExtractData}"> <MenuItem - Click="ExtractApplicationLogo_Click" + Click="ExtractApplicationExeFs_Click" Header="{locale:Locale GameListContextMenuExtractDataExeFS}" ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" /> <MenuItem @@ -73,7 +73,7 @@ Header="{locale:Locale GameListContextMenuExtractDataRomFS}" ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" /> <MenuItem - Click="ExtractApplicationExeFs_Click" + Click="ExtractApplicationLogo_Click" Header="{locale:Locale GameListContextMenuExtractDataLogo}" ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" /> </MenuItem> diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index 83fe29ea8..90c72e028 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -289,13 +289,13 @@ namespace Ryujinx.Ava.UI.Controls } } - public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args) + public async void ExtractApplicationExeFs_Click(object sender, RoutedEventArgs args) { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; if (viewModel?.SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); } } @@ -309,13 +309,13 @@ namespace Ryujinx.Ava.UI.Controls } } - public async void ExtractApplicationExeFs_Click(object sender, RoutedEventArgs args) + public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args) { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; if (viewModel?.SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); } } } diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 6d8331658..28ec5a43c 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -270,8 +270,14 @@ namespace Ryujinx.Ui.Widgets int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); - IFileSystem ncaFileSystem = patchNca != null ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) - : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); + bool sectionExistsInPatch = false; + if (patchNca != null) + { + sectionExistsInPatch = patchNca.CanOpenSection(index); + } + + IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) + : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); FileSystemClient fsClient = _horizonClient.Fs; From 7df4fcada702dbc01d09b8f6fa027f5d409e33e3 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Fri, 5 May 2023 22:40:46 +0100 Subject: [PATCH 500/737] GPU: Remove CPU region handle containers (#4817) * GPU: Remove CPU region handle containers. Another one for the "I don't know why I didn't do this earlier" pile. This removes the "Cpu" prefixed region handle classes, which each mirror a region handle type from Ryujinx.Memory. Originally, not all projects had a reference to Ryujinx.Memory, so these classes were introduced to bridge the gap. Someone else crossed that bridge since, so these classes don't have much of a purpose anymore. This PR replaces all uses of CpuRegionHandle etc to their direct Ryujinx.Memory versions. RegionHandle methods (specifically QueryModified) are about the hottest path there is in the entire emulator, so there is a nice boost from doing this. * Add docs --- src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs | 13 +++---- .../IVirtualMemoryManagerTracked.cs | 9 ++--- src/Ryujinx.Cpu/Jit/MemoryManager.cs | 13 +++---- .../Jit/MemoryManagerHostMapped.cs | 13 +++---- .../Tracking/CpuMultiRegionHandle.cs | 28 -------------- src/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs | 37 ------------------- .../Tracking/CpuSmartMultiRegionHandle.cs | 26 ------------- src/Ryujinx.Graphics.Gpu/Image/Pool.cs | 4 +- .../Image/TextureGroup.cs | 22 +++++------ .../Image/TextureGroupHandle.cs | 10 ++--- src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 7 ++-- .../Memory/GpuRegionHandle.cs | 7 ++-- .../Memory/PhysicalMemory.cs | 9 ++--- src/Ryujinx.Memory/Tracking/RegionHandle.cs | 11 ++++++ 14 files changed, 61 insertions(+), 148 deletions(-) delete mode 100644 src/Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs delete mode 100644 src/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs delete mode 100644 src/Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs index 437e02aea..56a329e04 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -1,5 +1,4 @@ using ARMeilleure.Memory; -using Ryujinx.Cpu.Tracking; using Ryujinx.Memory; using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; @@ -822,21 +821,21 @@ namespace Ryujinx.Cpu.AppleHv } /// <inheritdoc/> - public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) + public RegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); + return Tracking.BeginTracking(address, size, id); } /// <inheritdoc/> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); + return Tracking.BeginGranularTracking(address, size, handles, granularity, id); } /// <inheritdoc/> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) + public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); + return Tracking.BeginSmartGranularTracking(address, size, granularity, id); } /// <summary> diff --git a/src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs b/src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs index 92d3c76ca..5fa88d623 100644 --- a/src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs +++ b/src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs @@ -1,5 +1,4 @@ -using Ryujinx.Cpu.Tracking; -using Ryujinx.Memory; +using Ryujinx.Memory; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; @@ -30,7 +29,7 @@ namespace Ryujinx.Cpu /// <param name="size">Size of the region</param> /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - CpuRegionHandle BeginTracking(ulong address, ulong size, int id); + RegionHandle BeginTracking(ulong address, ulong size, int id); /// <summary> /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. @@ -41,7 +40,7 @@ namespace Ryujinx.Cpu /// <param name="granularity">Desired granularity of write tracking</param> /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id); + MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id); /// <summary> /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. @@ -51,6 +50,6 @@ namespace Ryujinx.Cpu /// <param name="granularity">Desired granularity of write tracking</param> /// <param name="id">Handle ID</param> /// <returns>The memory tracking handle</returns> - CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id); + SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id); } } diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs index 8542d53e2..6da11fb83 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -1,5 +1,4 @@ using ARMeilleure.Memory; -using Ryujinx.Cpu.Tracking; using Ryujinx.Memory; using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; @@ -629,21 +628,21 @@ namespace Ryujinx.Cpu.Jit } /// <inheritdoc/> - public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) + public RegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); + return Tracking.BeginTracking(address, size, id); } /// <inheritdoc/> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); + return Tracking.BeginGranularTracking(address, size, handles, granularity, id); } /// <inheritdoc/> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) + public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); + return Tracking.BeginSmartGranularTracking(address, size, granularity, id); } /// <inheritdoc/> diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 090740abe..363f9000d 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -1,5 +1,4 @@ using ARMeilleure.Memory; -using Ryujinx.Cpu.Tracking; using Ryujinx.Memory; using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; @@ -706,21 +705,21 @@ namespace Ryujinx.Cpu.Jit } /// <inheritdoc/> - public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) + public RegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); + return Tracking.BeginTracking(address, size, id); } /// <inheritdoc/> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); + return Tracking.BeginGranularTracking(address, size, handles, granularity, id); } /// <inheritdoc/> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) + public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); + return Tracking.BeginSmartGranularTracking(address, size, granularity, id); } /// <summary> diff --git a/src/Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs b/src/Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs deleted file mode 100644 index 0ed8bfc58..000000000 --- a/src/Ryujinx.Cpu/Tracking/CpuMultiRegionHandle.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Ryujinx.Memory.Tracking; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Cpu.Tracking -{ - public class CpuMultiRegionHandle : IMultiRegionHandle - { - private readonly MultiRegionHandle _impl; - - public bool Dirty => _impl.Dirty; - - internal CpuMultiRegionHandle(MultiRegionHandle impl) - { - _impl = impl; - } - - public void Dispose() => _impl.Dispose(); - public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size); - public IEnumerable<IRegionHandle> GetHandles() => _impl.GetHandles(); - public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction); - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction); - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber); - public void RegisterAction(ulong address, ulong size, RegionSignal action) => _impl.RegisterAction(address, size, action); - public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action) => _impl.RegisterPreciseAction(address, size, action); - public void SignalWrite() => _impl.SignalWrite(); - } -} diff --git a/src/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs b/src/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs deleted file mode 100644 index e766460fe..000000000 --- a/src/Ryujinx.Cpu/Tracking/CpuRegionHandle.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Ryujinx.Memory.Tracking; -using System; - -namespace Ryujinx.Cpu.Tracking -{ - public class CpuRegionHandle : IRegionHandle - { - private readonly RegionHandle _impl; - - public bool Dirty => _impl.Dirty; - public bool Unmapped => _impl.Unmapped; - public ulong Address => _impl.Address; - public ulong Size => _impl.Size; - public ulong EndAddress => _impl.EndAddress; - - internal CpuRegionHandle(RegionHandle impl) - { - _impl = impl; - } - - public void Dispose() => _impl.Dispose(); - public bool DirtyOrVolatile() => _impl.DirtyOrVolatile(); - public void ForceDirty() => _impl.ForceDirty(); - public IRegionHandle GetHandle() => _impl; - public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action); - public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action); - public void RegisterDirtyEvent(Action action) => _impl.RegisterDirtyEvent(action); - public void Reprotect(bool asDirty = false) => _impl.Reprotect(asDirty); - - public bool OverlapsWith(ulong address, ulong size) => _impl.OverlapsWith(address, size); - - public bool RangeEquals(CpuRegionHandle other) - { - return _impl.RealAddress == other._impl.RealAddress && _impl.RealSize == other._impl.RealSize; - } - } -} diff --git a/src/Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs b/src/Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs deleted file mode 100644 index 665271c61..000000000 --- a/src/Ryujinx.Cpu/Tracking/CpuSmartMultiRegionHandle.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Ryujinx.Memory.Tracking; -using System; - -namespace Ryujinx.Cpu.Tracking -{ - public class CpuSmartMultiRegionHandle : IMultiRegionHandle - { - private readonly SmartMultiRegionHandle _impl; - - public bool Dirty => _impl.Dirty; - - internal CpuSmartMultiRegionHandle(SmartMultiRegionHandle impl) - { - _impl = impl; - } - - public void Dispose() => _impl.Dispose(); - public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size); - public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action); - public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action); - public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction); - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction); - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber); - public void SignalWrite() => _impl.SignalWrite(); - } -} diff --git a/src/Ryujinx.Graphics.Gpu/Image/Pool.cs b/src/Ryujinx.Graphics.Gpu/Image/Pool.cs index 3e557c0bd..63be151f3 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -1,5 +1,5 @@ -using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.Memory.Tracking; using System; using System.Runtime.InteropServices; @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public ulong Size { get; } - private readonly CpuMultiRegionHandle _memoryTracking; + private readonly MultiRegionHandle _memoryTracking; private readonly Action<ulong, ulong> _modifiedDelegate; private int _modifiedSequenceOffset; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 14ab5d1e4..b36b16e94 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -1,10 +1,10 @@ using Ryujinx.Common.Memory; -using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Texture; using Ryujinx.Memory; using Ryujinx.Memory.Range; +using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Image { TextureGroupHandle group = _handles[baseHandle + i]; - foreach (CpuRegionHandle handle in group.Handles) + foreach (RegionHandle handle in group.Handles) { if (handle.Dirty) { @@ -296,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Image bool handleDirty = false; bool handleUnmapped = false; - foreach (CpuRegionHandle handle in group.Handles) + foreach (RegionHandle handle in group.Handles) { if (handle.Dirty) { @@ -703,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="group">The group to register an action for</param> public void RegisterAction(TextureGroupHandle group) { - foreach (CpuRegionHandle handle in group.Handles) + foreach (RegionHandle handle in group.Handles) { handle.RegisterAction((address, size) => FlushAction(group, address, size)); } @@ -985,7 +985,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="address">The start address of the tracked region</param> /// <param name="size">The size of the tracked region</param> /// <returns>A CpuRegionHandle covering the given range</returns> - private CpuRegionHandle GenerateHandle(ulong address, ulong size) + private RegionHandle GenerateHandle(ulong address, ulong size) { return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture); } @@ -1005,7 +1005,7 @@ namespace Ryujinx.Graphics.Gpu.Image int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel]; int size = endOffset - offset; - var result = new List<CpuRegionHandle>(); + var result = new List<RegionHandle>(); for (int i = 0; i < TextureRange.Count; i++) { @@ -1050,7 +1050,7 @@ namespace Ryujinx.Graphics.Gpu.Image views, result.ToArray()); - foreach (CpuRegionHandle handle in result) + foreach (RegionHandle handle in result) { handle.RegisterDirtyEvent(() => DirtyAction(groupHandle)); } @@ -1248,7 +1248,7 @@ namespace Ryujinx.Graphics.Gpu.Image continue; } - foreach (CpuRegionHandle handle in groupHandle.Handles) + foreach (RegionHandle handle in groupHandle.Handles) { bool hasMatch = false; @@ -1270,7 +1270,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - foreach (CpuRegionHandle handle in groupHandle.Handles) + foreach (RegionHandle handle in groupHandle.Handles) { handle.Reprotect(); } @@ -1303,7 +1303,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (!(_hasMipViews || _hasLayerViews)) { // Single dirty region. - var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count]; + var cpuRegionHandles = new RegionHandle[TextureRange.Count]; int count = 0; for (int i = 0; i < TextureRange.Count; i++) @@ -1322,7 +1322,7 @@ namespace Ryujinx.Graphics.Gpu.Image var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles); - foreach (CpuRegionHandle handle in cpuRegionHandles) + foreach (RegionHandle handle in cpuRegionHandles) { handle.RegisterDirtyEvent(() => DirtyAction(groupHandle)); } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index 9f66744be..fd66269dd 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -1,5 +1,5 @@ -using Ryujinx.Cpu.Tracking; -using Ryujinx.Graphics.Gpu.Synchronization; +using Ryujinx.Graphics.Gpu.Synchronization; +using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; using System.Linq; @@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// The CPU memory tracking handles that cover this handle. /// </summary> - public CpuRegionHandle[] Handles { get; } + public RegionHandle[] Handles { get; } /// <summary> /// True if a texture overlapping this handle has been modified. Is set false when the flush action is called. @@ -127,7 +127,7 @@ namespace Ryujinx.Graphics.Gpu.Image int firstLevel, int baseSlice, int sliceCount, - CpuRegionHandle[] handles) + RegionHandle[] handles) { _group = group; _firstLayer = firstLayer; @@ -642,7 +642,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public void Dispose() { - foreach (CpuRegionHandle handle in Handles) + foreach (RegionHandle handle in Handles) { handle.Dispose(); } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 8e16b3ae1..dc5037c56 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -1,4 +1,3 @@ -using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.Memory.Range; @@ -54,8 +53,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </remarks> private BufferModifiedRangeList _modifiedRanges = null; - private readonly CpuMultiRegionHandle _memoryTrackingGranular; - private readonly CpuRegionHandle _memoryTracking; + private readonly MultiRegionHandle _memoryTrackingGranular; + private readonly RegionHandle _memoryTracking; private readonly RegionSignal _externalFlushDelegate; private readonly Action<ulong, ulong> _loadDelegate; @@ -102,7 +101,7 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - return Enumerable.Repeat(buffer._memoryTracking.GetHandle(), 1); + return Enumerable.Repeat(buffer._memoryTracking, 1); } }); } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs b/src/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs index bc07bfad1..9f73de4e2 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs @@ -1,5 +1,4 @@ -using Ryujinx.Cpu.Tracking; -using Ryujinx.Memory.Tracking; +using Ryujinx.Memory.Tracking; using System; namespace Ryujinx.Graphics.Gpu.Memory @@ -9,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </summary> class GpuRegionHandle : IRegionHandle { - private readonly CpuRegionHandle[] _cpuRegionHandles; + private readonly RegionHandle[] _cpuRegionHandles; public bool Dirty { @@ -35,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Create a new GpuRegionHandle, made up of mulitple CpuRegionHandles. /// </summary> /// <param name="cpuRegionHandles">The CpuRegionHandles that make up this handle</param> - public GpuRegionHandle(CpuRegionHandle[] cpuRegionHandles) + public GpuRegionHandle(RegionHandle[] cpuRegionHandles) { _cpuRegionHandles = cpuRegionHandles; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index b976667c3..364488aae 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -1,5 +1,4 @@ using Ryujinx.Cpu; -using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Memory; @@ -348,7 +347,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="size">Size of the region</param> /// <param name="kind">Kind of the resource being tracked</param> /// <returns>The memory tracking handle</returns> - public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind) + public RegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind) { return _cpuMemory.BeginTracking(address, size, (int)kind); } @@ -361,7 +360,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <returns>The memory tracking handle</returns> public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind) { - var cpuRegionHandles = new CpuRegionHandle[range.Count]; + var cpuRegionHandles = new RegionHandle[range.Count]; int count = 0; for (int i = 0; i < range.Count; i++) @@ -390,7 +389,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="handles">Handles to inherit state from or reuse</param> /// <param name="granularity">Desired granularity of write tracking</param> /// <returns>The memory tracking handle</returns> - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096) + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096) { return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind); } @@ -403,7 +402,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="kind">Kind of the resource being tracked</param> /// <param name="granularity">Desired granularity of write tracking</param> /// <returns>The memory tracking handle</returns> - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096) + public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096) { return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind); } diff --git a/src/Ryujinx.Memory/Tracking/RegionHandle.cs b/src/Ryujinx.Memory/Tracking/RegionHandle.cs index 7a59f9f25..63a168847 100644 --- a/src/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/src/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; using System.Threading; namespace Ryujinx.Memory.Tracking @@ -443,6 +444,16 @@ namespace Ryujinx.Memory.Tracking return Address < address + size && address < EndAddress; } + /// <summary> + /// Determines if this handle's memory range matches another exactly. + /// </summary> + /// <param name="other">The other handle</param> + /// <returns>True on a match, false otherwise</returns> + public bool RangeEquals(RegionHandle other) + { + return RealAddress == other.RealAddress && RealSize == other.RealSize; + } + /// <summary> /// Dispose the handle. Within the tracking lock, this removes references from virtual regions. /// </summary> From 332891b5ff44a2f7647da2c59c600ac6a88e36d5 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Fri, 5 May 2023 22:59:36 +0100 Subject: [PATCH 501/737] Use correct offset for storage constant buffer elimination (#4821) --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Optimizations/GlobalToStorage.cs | 20 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 85233c0a3..f30f1ff1b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 2237; + private const uint CodeGenVersion = 4821; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index a83682445..774d3e36d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -161,12 +161,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand addrLow, bool isStg16Or8) { - int baseAddressCbOffset = GetStorageCbOffset(config.Stage, storageIndex); + (int sbCbSlot, int sbCbOffset) = config.GetSbCbInfo(storageIndex); bool storageAligned = !(config.GpuAccessor.QueryHasUnalignedStorageBuffer() || config.GpuAccessor.QueryHostStorageBufferOffsetAlignment() > Constants.StorageAlignment); (Operand byteOffset, int constantOffset) = storageAligned ? - GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) : + GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), sbCbSlot, sbCbOffset) : (null, 0); if (byteOffset != null) @@ -176,8 +176,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (byteOffset == null) { - (int sbCbSlot, int sbCbOffset) = config.GetSbCbInfo(storageIndex); - Operand baseAddrLow = Cbuf(sbCbSlot, sbCbOffset); Operand baseAddrTrunc = Local(); @@ -217,9 +215,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return wordOffset; } - private static bool IsCb0Offset(Operand operand, int offset) + private static bool IsCbOffset(Operand operand, int slot, int offset) { - return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == 0 && operand.GetCbufOffset() == offset; + return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == slot && operand.GetCbufOffset() == offset; } private static void ReplaceAddressAlignment(LinkedList<INode> list, Operand address, Operand byteOffset, int constantOffset) @@ -263,9 +261,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int baseAddressCbOffset) + private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int cbSlot, int baseAddressCbOffset) { - if (IsCb0Offset(address, baseAddressCbOffset)) + if (IsCbOffset(address, cbSlot, baseAddressCbOffset)) { // Direct offset: zero. return (Const(0), 0); @@ -275,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations address = Utils.FindLastOperation(address, block); - if (IsCb0Offset(address, baseAddressCbOffset)) + if (IsCbOffset(address, cbSlot, baseAddressCbOffset)) { // Only constant offset return (Const(0), constantOffset); @@ -289,11 +287,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand src1 = offsetAdd.GetSource(0); Operand src2 = Utils.FindLastOperation(offsetAdd.GetSource(1), block); - if (IsCb0Offset(src2, baseAddressCbOffset)) + if (IsCbOffset(src2, cbSlot, baseAddressCbOffset)) { return (src1, constantOffset); } - else if (IsCb0Offset(src1, baseAddressCbOffset)) + else if (IsCbOffset(src1, cbSlot, baseAddressCbOffset)) { return (src2, constantOffset); } From fab11ba3f1fec3e3827a8350791d76d2fcd50173 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Sat, 6 May 2023 03:33:50 +0200 Subject: [PATCH 502/737] AM: Stub some service calls (#4825) * AM: Stub some service call Some IPC I have stubbed during private testing and I don't want to deal with them anymore. Nothing more. * ICommonStateGetter disposable --- .../ISystemAppletProxy.cs | 9 ++++ .../SystemAppletProxy/ICommonStateGetter.cs | 51 ++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs index dc26d80c1..93dff041c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs @@ -92,6 +92,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } + [CommandCmif(23)] + // GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions> + public ResultCode GetAppletCommonFunctions(ServiceCtx context) + { + MakeObject(context, new IAppletCommonFunctions()); + + return ResultCode.Success; + } + [CommandCmif(1000)] // GetDebugFunctions() -> object<nn::am::service::IDebugFunctions> public ResultCode GetDebugFunctions(ServiceCtx context) diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index 381267b05..5e7d0baea 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -9,8 +9,10 @@ using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy { - class ICommonStateGetter : IpcService + class ICommonStateGetter : DisposableIpcService { + private readonly ServiceCtx _context; + private Apm.ManagerServer _apmManagerServer; private Apm.SystemManagerServer _apmSystemManagerServer; private Lbl.LblControllerServer _lblControllerServer; @@ -23,11 +25,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private int _messageEventHandle; private int _displayResolutionChangedEventHandle; + private KEvent _acquiredSleepLockEvent; + private int _acquiredSleepLockEventHandle; + public ICommonStateGetter(ServiceCtx context) { + _context = context; + _apmManagerServer = new Apm.ManagerServer(context); _apmSystemManagerServer = new Apm.SystemManagerServer(context); _lblControllerServer = new Lbl.LblControllerServer(context); + + _acquiredSleepLockEvent = new KEvent(context.Device.System.KernelContext); } [CommandCmif(0)] @@ -117,6 +126,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandCmif(10)] + // RequestToAcquireSleepLock() + public ResultCode RequestToAcquireSleepLock(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(13)] + // GetAcquiredSleepLockEvent() -> handle<copy> + public ResultCode GetAcquiredSleepLockEvent(ServiceCtx context) + { + if (_acquiredSleepLockEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_acquiredSleepLockEvent.ReadableEvent, out _acquiredSleepLockEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_acquiredSleepLockEventHandle); + + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + [CommandCmif(50)] // 3.0.0+ // IsVrModeEnabled() -> b8 public ResultCode IsVrModeEnabled(ServiceCtx context) @@ -281,5 +318,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + + protected override void Dispose(bool isDisposing) + { + if (isDisposing) + { + if (_acquiredSleepLockEventHandle != 0) + { + _context.Process.HandleTable.CloseHandle(_acquiredSleepLockEventHandle); + _acquiredSleepLockEventHandle = 0; + } + } + } } } \ No newline at end of file From 4c3d2d5d75c46a522d55c0a3ae6820255294517c Mon Sep 17 00:00:00 2001 From: Nico <Nico.Reinartz@rwth-aachen.de> Date: Sat, 6 May 2023 15:35:46 +0200 Subject: [PATCH 503/737] UI: Add progress bar for re-packaging shaders (#4805) * feat: introduce new shader loading state for progress tracking when writing shaders to disk * fix: move translation to bottom of locale file * fix: change back to foreach and add requested spacing between lines * style: fix formatting Co-authored-by: gdkchan <gab.dark.100@gmail.com> --------- Co-authored-by: gdkchan <gab.dark.100@gmail.com> --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 3 ++- src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs | 4 ++++ .../Shader/DiskCache/ParallelDiskCacheLoader.cs | 6 ++++++ src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs | 2 ++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 617cad34f..965dfa3a5 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -644,5 +644,6 @@ "UserEditorTitleCreate" : "Create User", "SettingsTabNetworkInterface": "Network Interface:", "NetworkInterfaceTooltip": "The network interface used for LAN features", - "NetworkInterfaceDefault": "Default" + "NetworkInterfaceDefault": "Default", + "PackagingShaders": "Packaging Shaders" } diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index b5c82d655..4db78afeb 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1099,6 +1099,10 @@ namespace Ryujinx.Ava.UI.ViewModels LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders]; IsLoadingIndeterminate = false; break; + case ShaderCacheLoadingState.Packaging: + LoadHeading = LocaleManager.Instance[LocaleKeys.PackagingShaders]; + IsLoadingIndeterminate = false; + break; case ShaderCacheLoadingState.Loaded: LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName); IsLoadingIndeterminate = true; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 77fb3ca4b..58e5c7b10 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -299,10 +299,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache if (_programList.Count != 0) { + _stateChangeCallback(ShaderCacheState.Packaging, 0, _programList.Count); + Logger.Info?.Print(LogClass.Gpu, $"Rebuilding {_programList.Count} shaders..."); using var streams = _hostStorage.GetOutputStreams(_context); + int packagedShaders = 0; foreach (var kv in _programList) { if (!Active) @@ -311,7 +314,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } (CachedShaderProgram program, byte[] binaryCode) = kv.Value; + _hostStorage.AddShader(_context, program, binaryCode, streams); + + _stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count); } Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully."); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs index 623b73d79..b94a6c054 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Shader Start, /// <summary>Shader cache is loading</summary> Loading, + /// <summary>Shader cache is written to disk</summary> + Packaging, /// <summary>Shader cache finished loading</summary> Loaded } From dde208b480f3e2aeb0e3abc15857d031ceb22bf4 Mon Sep 17 00:00:00 2001 From: gnisman <gleb.nisman@gmail.com> Date: Sun, 7 May 2023 17:36:44 +0300 Subject: [PATCH 504/737] UI: Expose games build ID for cheat management (#4340) * Ava UI: Expose games build ID for cheat management * Fix bad merge * Change integrity check level to error on invalid * Add support for GDK * Remove whitespace * Add BID identifier * PR Comments fix * Restore title id in cheats GTK window * use halign center instead of margin_left * Merge * fix after merge * PR comments fix - design AVA * PR fix - Move GetApplicationBuildId to ApplicationData class * PR comment fix - Add empty line before method * Align with PR #4755 * PR comments fix * Change BuildId label to support translation * Comments fix * Remove unused BuildIdLabel property --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 1 + .../Controls/ApplicationContextMenu.axaml.cs | 7 +- .../UI/Views/Main/MainMenuBarView.axaml.cs | 7 +- src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml | 37 ++++- .../UI/Windows/CheatWindow.axaml.cs | 10 +- src/Ryujinx.Ui.Common/App/ApplicationData.cs | 128 ++++++++++++++++++ src/Ryujinx/Ui/MainWindow.cs | 9 +- .../Ui/Widgets/GameTableContextMenu.cs | 2 +- src/Ryujinx/Ui/Windows/CheatWindow.cs | 7 +- src/Ryujinx/Ui/Windows/CheatWindow.glade | 17 ++- 10 files changed, 210 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 965dfa3a5..c9b10f54a 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -590,6 +590,7 @@ "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", "UpdateWindowTitle": "Title Update Manager", "CheatWindowHeading": "Cheats Available for {0} [{1}]", + "BuildId": "BuildId:", "DlcWindowHeading": "{0} Downloadable Content(s)", "UserProfilesEditProfile": "Edit Selected", "Cancel": "Cancel", diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index 90c72e028..a9269386a 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Configuration; +using Ryujinx.Ui.App.Common; using Ryujinx.HLE.HOS; using Ryujinx.Ui.Common.Helper; using System; @@ -118,7 +119,11 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window); + await new CheatWindow( + viewModel.VirtualFileSystem, + viewModel.SelectedApplication.TitleId, + viewModel.SelectedApplication.TitleName, + viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window); } } diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index bdf2cf9f8..557528eb1 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -11,6 +11,7 @@ using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS; using Ryujinx.Modules; +using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; @@ -176,7 +177,11 @@ 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).ShowDialog(Window); + await new CheatWindow( + Window.VirtualFileSystem, + ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, + name, + Window.ViewModel.SelectedApplication.Path).ShowDialog(Window); ViewModel.AppHost.Device.EnableCheats(); } diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml index 3557ed696..11e86211e 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml @@ -21,23 +21,52 @@ </Window.Styles> <Grid Name="CheatGrid" Margin="15"> <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> <TextBlock Grid.Row="1" + Grid.Column="0" + Grid.ColumnSpan="2" MaxWidth="500" - Margin="20,15,20,20" + Margin="20,15,20,5" HorizontalAlignment="Center" VerticalAlignment="Center" LineHeight="18" Text="{Binding Heading}" TextAlignment="Center" TextWrapping="Wrap" /> - <Border + <TextBlock Grid.Row="2" + Grid.Column="0" + MaxWidth="500" + Margin="140,15,20,5" + HorizontalAlignment="Center" + VerticalAlignment="Center" + LineHeight="30" + Text="{locale:Locale BuildId}" + TextAlignment="Center" + TextWrapping="Wrap" /> + <TextBox + Grid.Row="2" + Grid.Column="1" + Margin="0,5,110,5" + MinWidth="160" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Text="{Binding BuildId}" + IsReadOnly="True" /> + <Border + Grid.Row="3" + Grid.Column="0" + Grid.ColumnSpan="2" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" @@ -81,7 +110,9 @@ </TreeView> </Border> <DockPanel - Grid.Row="3" + Grid.Row="4" + Grid.Column="0" + Grid.ColumnSpan="2" Margin="0" HorizontalAlignment="Stretch"> <DockPanel Margin="0" HorizontalAlignment="Right"> diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index 241a6c346..f5bba7d2d 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -1,8 +1,10 @@ -using Avalonia.Collections; +using Avalonia; +using Avalonia.Collections; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Models; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; +using Ryujinx.Ui.App.Common; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,6 +19,7 @@ namespace Ryujinx.Ava.UI.Windows private AvaloniaList<CheatsList> LoadedCheats { get; } public string Heading { get; } + public string BuildId { get; } public CheatWindow() { @@ -27,12 +30,13 @@ namespace Ryujinx.Ava.UI.Windows Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle]; } - public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) + public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath) { LoadedCheats = new AvaloniaList<CheatsList>(); Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper()); - + BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath); + InitializeComponent(); string modsBasePath = ModLoader.GetModsBasePath(); diff --git a/src/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs index ba430172f..d9d3cf685 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationData.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationData.cs @@ -1,5 +1,16 @@ using LibHac.Common; using LibHac.Ns; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Loader; +using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.FileSystem; +using System; +using System.IO; namespace Ryujinx.Ui.App.Common { @@ -19,5 +30,122 @@ namespace Ryujinx.Ui.App.Common public double FileSizeBytes { get; set; } public string Path { get; set; } public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } + + public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath) + { + using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); + + Nca mainNca = null; + Nca patchNca = null; + + if (!System.IO.Path.Exists(titleFilePath)) + { + Logger.Error?.Print(LogClass.Application, $"File does not exists. {titleFilePath}"); + return string.Empty; + } + + string extension = System.IO.Path.GetExtension(titleFilePath).ToLower(); + + if (extension is ".nsp" or ".xci") + { + PartitionFileSystem pfs; + + if (extension == ".xci") + { + Xci xci = new(virtualFileSystem.KeySet, file.AsStorage()); + + pfs = xci.OpenPartition(XciPartitionType.Secure); + } + else + { + pfs = new PartitionFileSystem(file.AsStorage()); + } + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef<IFile>(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); + + if (nca.Header.ContentType != NcaContentType.Program) + { + continue; + } + + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + + if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) + { + patchNca = nca; + } + else + { + mainNca = nca; + } + } + } + else if (extension == ".nca") + { + mainNca = new Nca(virtualFileSystem.KeySet, file.AsStorage()); + } + + if (mainNca == null) + { + Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file"); + + return string.Empty; + } + + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); + + if (updatePatchNca != null) + { + patchNca = updatePatchNca; + } + + IFileSystem codeFs = null; + + if (patchNca == null) + { + if (mainNca.CanOpenSection(NcaSectionType.Code)) + { + codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid); + } + } + else + { + if (patchNca.CanOpenSection(NcaSectionType.Code)) + { + codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid); + } + } + + if (codeFs == null) + { + Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA"); + + return string.Empty; + } + + const string mainExeFs = "main"; + + if (!codeFs.FileExists($"/{mainExeFs}")) + { + Logger.Error?.Print(LogClass.Loader, "No main binary ExeFS found in ExeFS"); + + return string.Empty; + } + + using var nsoFile = new UniqueRef<IFile>(); + + codeFs.OpenFile(ref nsoFile.Ref, $"/{mainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + NsoReader reader = new NsoReader(); + reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure(); + + return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16]; + } } } \ No newline at end of file diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index 4911c9006..b61855e4c 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -1626,9 +1626,12 @@ namespace Ryujinx.Ui private void ManageCheats_Pressed(object sender, EventArgs args) { - var window = new CheatWindow(_virtualFileSystem, - _emulationContext.Processes.ActiveApplication.ProgramId, - _emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString()); + var window = new CheatWindow( + _virtualFileSystem, + _emulationContext.Processes.ActiveApplication.ProgramId, + _emulationContext.Processes.ActiveApplication.ApplicationControlProperties + .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(), + _currentEmulatedGamePath); window.Destroyed += CheatWindow_Destroyed; window.Show(); diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 28ec5a43c..74f6043d4 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -461,7 +461,7 @@ namespace Ryujinx.Ui.Widgets private void ManageCheats_Clicked(object sender, EventArgs args) { - new CheatWindow(_virtualFileSystem, _titleId, _titleName).Show(); + new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show(); } private void OpenTitleModDir_Clicked(object sender, EventArgs args) diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs index 7dbea0128..32df2c0c2 100644 --- a/src/Ryujinx/Ui/Windows/CheatWindow.cs +++ b/src/Ryujinx/Ui/Windows/CheatWindow.cs @@ -1,6 +1,7 @@ using Gtk; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; +using Ryujinx.Ui.App.Common; using System; using System.Collections.Generic; using System.IO; @@ -17,16 +18,18 @@ namespace Ryujinx.Ui.Windows #pragma warning disable CS0649, IDE0044 [GUI] Label _baseTitleInfoLabel; + [GUI] TextView _buildIdTextView; [GUI] TreeView _cheatTreeView; [GUI] Button _saveButton; #pragma warning restore CS0649, IDE0044 - public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName) { } + public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { } - private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : base(builder.GetRawOwnedObject("_cheatWindow")) + private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow")) { builder.Autoconnect(this); _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; + _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}"; string modsBasePath = ModLoader.GetModsBasePath(); string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.glade b/src/Ryujinx/Ui/Windows/CheatWindow.glade index 37b1cbe07..9a165f1a8 100644 --- a/src/Ryujinx/Ui/Windows/CheatWindow.glade +++ b/src/Ryujinx/Ui/Windows/CheatWindow.glade @@ -31,6 +31,21 @@ <property name="position">0</property> </packing> </child> + <child> + <object class="GtkTextView" id="_buildIdTextView"> + <property name="visible">True</property> + <property name="margin_top">10</property> + <property name="halign">center</property> + <property name="margin_bottom">10</property> + <property name="editable">False</property> + <property name="cursor_visible">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> <child> <object class="GtkScrolledWindow"> <property name="visible">True</property> @@ -57,7 +72,7 @@ <packing> <property name="expand">True</property> <property name="fill">True</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </object> From 5440d4ad5c5421a474a61e582e524ced5bd65547 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 7 May 2023 22:50:45 +0200 Subject: [PATCH 505/737] misc: Switch ProcessResult to a class (#4846) This avoid giant copies being performed when being returned or passed. --- src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index 2801e4e08..cf8168f74 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -9,7 +9,7 @@ using System.Linq; namespace Ryujinx.HLE.Loaders.Processes { - public struct ProcessResult + public class ProcessResult { public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish); From 470a8031a44145f906b50919ce1a4e01d51c15ff Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 8 May 2023 00:15:58 +0200 Subject: [PATCH 506/737] time: Update for 15.0.0 changes and fixes long standing issues (#4822) * time: Update for 15.0.0 changes Last time we did an upgrade on the time service was during 9.x era, it was about time to take back that reverse again! 15.0.0 added a new structure on the shared memory to get steady clock raw timepoints with a granularity in nanoseconds. This commit implements this new part. I plan to write a follow up with a bit of refactoring of this ancient part of the emulator. As always, reverse and work done by your truly. PS: As a reminder, if this change is reused anywhere else, work should be credited as Ryujinx and not my person. * time: Do not set setup value to posix time This should fix local and network clock returning 0 under usage with shared memory. This probably fix #2430. * Address gdkchan's comment * Fix internal offset not working since changes and ensure that user clock have a valid clock id * time: Report auto correcting clock and hardcode steady clock unique id Fix Pokemon Sword Pokejobs for real. * Address gdkchan's comment --- src/Ryujinx.HLE/HOS/Horizon.cs | 11 ++-- .../Types/ContinuousAdjustmentTimePoint.cs | 13 +++++ .../HOS/Services/Time/TimeSharedMemory.cs | 53 +++++++++++++------ 3 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 1639532ed..f73dea1d9 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -200,9 +200,10 @@ namespace Ryujinx.HLE.HOS LibHacHorizonManager = device.Configuration.LibHacHorizonManager; + // We hardcode a clock source id to avoid it changing between each start. // TODO: use set:sys (and get external clock source id from settings) // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. - UInt128 clockSourceId = UInt128Utils.CreateRandom(); + UInt128 clockSourceId = new UInt128(0x36a0328702ce8bc1, 0x1608eaba02333284); IRtcManager.GetExternalRtcValue(out ulong rtcValue); // We assume the rtc is system time. @@ -222,22 +223,22 @@ namespace Ryujinx.HLE.HOS internalOffset = internalOffset.AddSeconds(-3600L); } - internalOffset = new TimeSpanType(-internalOffset.NanoSeconds); + systemTime = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds); // First init the standard steady clock - TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false); + TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, TimeSpanType.Zero, TimeSpanType.Zero, TimeSpanType.Zero, false); TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds()); + TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext); if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes)) { TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000); // The network system clock needs a valid system clock, as such we setup this system clock using the local system clock. - TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext); TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy); } - TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom()); + TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, true, localSytemClockContext.SteadyTimePoint); // FIXME: TimeZone should be init here but it's actually done in ContentManager diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs new file mode 100644 index 000000000..b57dfaa06 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ContinuousAdjustmentTimePoint + { + public ulong ClockOffset; + public long Multiplier; + public long DivisorLog2; + public SystemClockContext Context; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs index 7063290bc..6b1e16875 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs @@ -1,8 +1,8 @@ using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Services.Time.Clock; +using Ryujinx.HLE.HOS.Services.Time.Clock.Types; using Ryujinx.HLE.HOS.Services.Time.Types; -using Ryujinx.HLE.Utilities; using System; using System.Runtime.CompilerServices; using System.Threading; @@ -16,10 +16,11 @@ namespace Ryujinx.HLE.HOS.Services.Time private SharedMemoryStorage _timeSharedMemoryStorage; private int _timeSharedMemorySize; - private const uint SteadyClockContextOffset = 0x00; - private const uint LocalSystemClockContextOffset = 0x38; - private const uint NetworkSystemClockContextOffset = 0x80; + private const uint SteadyClockContextOffset = 0x00; + private const uint LocalSystemClockContextOffset = 0x38; + private const uint NetworkSystemClockContextOffset = 0x80; private const uint AutomaticCorrectionEnabledOffset = 0xC8; + private const uint ContinuousAdjustmentTimePointOffset = 0xD0; public void Initialize(Switch device, KSharedMemory sharedMemory, SharedMemoryStorage timeSharedMemoryStorage, int timeSharedMemorySize) { @@ -39,15 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint) { - TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); - - SteadyClockContext context = new SteadyClockContext - { - InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds), - ClockSourceId = clockSourceId - }; - - WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context); + UpdateSteadyClock(tickSource, clockSourceId, currentTimePoint); } public void SetAutomaticCorrectionEnabled(bool isAutomaticCorrectionEnabled) @@ -58,10 +51,38 @@ namespace Ryujinx.HLE.HOS.Services.Time public void SetSteadyClockRawTimePoint(ITickSource tickSource, TimeSpanType currentTimePoint) { - SteadyClockContext context = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4); - TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); + SteadyClockContext context = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4); - context.InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds); + UpdateSteadyClock(tickSource, context.ClockSourceId, currentTimePoint); + } + + private void UpdateSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint) + { + TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); + + ContinuousAdjustmentTimePoint adjustmentTimePoint = new ContinuousAdjustmentTimePoint + { + ClockOffset = (ulong)ticksTimeSpan.NanoSeconds, + Multiplier = 1, + DivisorLog2 = 0, + Context = new SystemClockContext + { + Offset = 0, + SteadyTimePoint = new SteadyClockTimePoint + { + ClockSourceId = clockSourceId, + TimePoint = 0 + } + } + }; + + WriteObjectToSharedMemory(ContinuousAdjustmentTimePointOffset, 4, adjustmentTimePoint); + + SteadyClockContext context = new SteadyClockContext + { + InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds), + ClockSourceId = clockSourceId + }; WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context); } From adf4ebcd60d00f5ef43d2168c3ba9893ff2c1153 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Mon, 8 May 2023 00:31:08 +0200 Subject: [PATCH 507/737] Ava: Fix SystemTimeOffset calculation (#4848) * Ava: Fix SystemTimeOffset calculation During testing of #4822, Mary pointed out the way we calculate time offset is wrong in our Avalonia UI. This PR fixed that. The axaml file is autoformatted too. * DateTime.Now in local var --- .../UI/ViewModels/SettingsViewModel.cs | 16 +-- .../Views/Settings/SettingsSystemView.axaml | 105 +++++++++--------- 2 files changed, 59 insertions(+), 62 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 08612117a..75a5168fc 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -238,8 +238,9 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public DateTimeOffset DateOffset { get; set; } - public TimeSpan TimeOffset { get; set; } + public DateTimeOffset CurrentDate { get; set; } + public TimeSpan CurrentTime { get; set; } + internal AvaloniaList<TimeZone> TimeZones { get; set; } public AvaloniaList<string> GameDirectories { get; set; } public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; } @@ -397,10 +398,11 @@ namespace Ryujinx.Ava.UI.ViewModels Language = (int)config.System.Language.Value; TimeZone = config.System.TimeZone; - DateTime dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset); + DateTime currentDateTime = DateTime.Now; + + CurrentDate = currentDateTime.Date; + CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset)); - DateOffset = dateTimeOffset.Date; - TimeOffset = dateTimeOffset.TimeOfDay; EnableVsync = config.Graphics.EnableVsync; EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks; ExpandDramSize = config.System.ExpandRam; @@ -487,9 +489,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.TimeZone.Value = TimeZone; } - TimeSpan systemTimeOffset = DateOffset - DateTime.Now; - - config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds; + config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds()); config.Graphics.EnableVsync.Value = EnableVsync; config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.ExpandRam.Value = ExpandDramSize; diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml index 1d4f040fd..cc60ef24d 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml @@ -3,12 +3,12 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" - mc:Ignorable="d" x:CompileBindings="True" - x:DataType="viewModels:SettingsViewModel"> + x:DataType="viewModels:SettingsViewModel" + mc:Ignorable="d"> <Design.DataContext> <viewModels:SettingsViewModel /> </Design.DataContext> @@ -27,13 +27,15 @@ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" /> <StackPanel Margin="10,0,0,0" Orientation="Vertical"> <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> - <TextBlock VerticalAlignment="Center" - Text="{locale:Locale SettingsTabSystemSystemRegion}" - Width="250" /> - <ComboBox SelectedIndex="{Binding Region}" - ToolTip.Tip="{locale:Locale RegionTooltip}" - HorizontalContentAlignment="Left" - Width="350"> + <TextBlock + Width="250" + VerticalAlignment="Center" + Text="{locale:Locale SettingsTabSystemSystemRegion}" /> + <ComboBox + Width="350" + HorizontalContentAlignment="Left" + SelectedIndex="{Binding Region}" + ToolTip.Tip="{locale:Locale RegionTooltip}"> <ComboBoxItem> <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" /> </ComboBoxItem> @@ -58,20 +60,21 @@ </ComboBox> </StackPanel> <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> - <TextBlock VerticalAlignment="Center" - Text="{locale:Locale SettingsTabSystemSystemLanguage}" - ToolTip.Tip="{locale:Locale LanguageTooltip}" - Width="250" /> - <ComboBox SelectedIndex="{Binding Language}" - ToolTip.Tip="{locale:Locale LanguageTooltip}" - HorizontalContentAlignment="Left" - Width="350"> + <TextBlock + Width="250" + VerticalAlignment="Center" + Text="{locale:Locale SettingsTabSystemSystemLanguage}" + ToolTip.Tip="{locale:Locale LanguageTooltip}" /> + <ComboBox + Width="350" + HorizontalContentAlignment="Left" + SelectedIndex="{Binding Language}" + ToolTip.Tip="{locale:Locale LanguageTooltip}"> <ComboBoxItem> <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" /> </ComboBoxItem> <ComboBoxItem> - <TextBlock - Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" /> </ComboBoxItem> <ComboBoxItem> <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" /> @@ -104,71 +107,67 @@ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" /> </ComboBoxItem> <ComboBoxItem> - <TextBlock - Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" /> </ComboBoxItem> <ComboBoxItem> - <TextBlock - Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" /> </ComboBoxItem> <ComboBoxItem> - <TextBlock - Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" /> </ComboBoxItem> <ComboBoxItem> - <TextBlock - Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" /> </ComboBoxItem> <ComboBoxItem> - <TextBlock - Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" /> </ComboBoxItem> <ComboBoxItem> - <TextBlock - Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" /> </ComboBoxItem> </ComboBox> </StackPanel> <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> - <TextBlock VerticalAlignment="Center" - Text="{locale:Locale SettingsTabSystemSystemTimeZone}" - ToolTip.Tip="{locale:Locale TimezoneTooltip}" - Width="250" /> + <TextBlock + Width="250" + VerticalAlignment="Center" + Text="{locale:Locale SettingsTabSystemSystemTimeZone}" + ToolTip.Tip="{locale:Locale TimezoneTooltip}" /> <AutoCompleteBox Name="TimeZoneBox" Width="350" - MaxDropDownHeight="500" FilterMode="Contains" Items="{Binding TimeZones}" + MaxDropDownHeight="500" SelectionChanged="TimeZoneBox_OnSelectionChanged" Text="{Binding Path=TimeZone, Mode=OneWay}" TextChanged="TimeZoneBox_OnTextChanged" ToolTip.Tip="{locale:Locale TimezoneTooltip}" /> </StackPanel> <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> - <TextBlock VerticalAlignment="Center" - Text="{locale:Locale SettingsTabSystemSystemTime}" - ToolTip.Tip="{locale:Locale TimeTooltip}" - Width="250"/> - <DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}" - ToolTip.Tip="{locale:Locale TimeTooltip}" - Width="350" /> + <TextBlock + Width="250" + VerticalAlignment="Center" + Text="{locale:Locale SettingsTabSystemSystemTime}" + ToolTip.Tip="{locale:Locale TimeTooltip}" /> + <DatePicker + Width="350" + VerticalAlignment="Center" + SelectedDate="{Binding CurrentDate}" + ToolTip.Tip="{locale:Locale TimeTooltip}" /> </StackPanel> <StackPanel Margin="250,0,0,10" Orientation="Horizontal"> <TimePicker + Width="350" VerticalAlignment="Center" ClockIdentifier="24HourClock" - SelectedTime="{Binding TimeOffset}" - Width="350" + SelectedTime="{Binding CurrentTime}" ToolTip.Tip="{locale:Locale TimeTooltip}" /> </StackPanel> <CheckBox IsChecked="{Binding EnableVsync}"> - <TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}" - ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}" ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" /> </CheckBox> <CheckBox IsChecked="{Binding EnableFsIntegrityChecks}"> - <TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" - ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" /> + <TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" /> </CheckBox> </StackPanel> <Separator Height="1" /> @@ -180,12 +179,10 @@ Margin="10,0,0,0" HorizontalAlignment="Stretch" Orientation="Vertical"> - <CheckBox IsChecked="{Binding ExpandDramSize}" - ToolTip.Tip="{locale:Locale DRamTooltip}"> + <CheckBox IsChecked="{Binding ExpandDramSize}" ToolTip.Tip="{locale:Locale DRamTooltip}"> <TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" /> </CheckBox> - <CheckBox IsChecked="{Binding IgnoreMissingServices}" - ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}"> + <CheckBox IsChecked="{Binding IgnoreMissingServices}" ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}"> <TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" /> </CheckBox> </StackPanel> From 0e06aace458109f70dc5f36535f77117465ea707 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 8 May 2023 01:50:07 +0200 Subject: [PATCH 508/737] misc: Avoid copy of ApplicationControlProperty (#4849) Avoid more giant copy when passing it around. --- .../Extensions/FileSystemExtensions.cs | 2 +- .../Loaders/Processes/ProcessLoader.cs | 2 +- .../Loaders/Processes/ProcessLoaderHelper.cs | 4 +-- .../Loaders/Processes/ProcessResult.cs | 25 ++++++++++--------- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index 58759ddb1..782ccef31 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -116,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions device, device.System.KernelContext, metaLoader, - nacpData.Value, + nacpData, enablePtc, allowCodeMemoryForJit, programName, diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index 785db0e50..0eedc2131 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -209,7 +209,7 @@ namespace Ryujinx.HLE.Loaders.Processes ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device, _device.System.KernelContext, dummyExeFs.GetNpdm(), - nacpData.Value, + nacpData, diskCacheEnabled: false, allowCodeMemoryForJit: true, programName, diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index b802a6428..d1c60f167 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -219,7 +219,7 @@ namespace Ryujinx.HLE.Loaders.Processes Switch device, KernelContext context, MetaLoader metaLoader, - ApplicationControlProperty applicationControlProperties, + BlitStruct<ApplicationControlProperty> applicationControlProperties, bool diskCacheEnabled, bool allowCodeMemoryForJit, string name, @@ -355,7 +355,7 @@ namespace Ryujinx.HLE.Loaders.Processes context.Device.System.TickSource, context.Device.Gpu, $"{programId:x16}", - applicationControlProperties.DisplayVersionString.ToString(), + applicationControlProperties.Value.DisplayVersionString.ToString(), diskCacheEnabled, codeStart, codeSize); diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index cf8168f74..81e75e270 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -1,4 +1,5 @@ -using LibHac.Loader; +using LibHac.Common; +using LibHac.Loader; using LibHac.Ns; using Ryujinx.Common.Logging; using Ryujinx.Cpu; @@ -11,7 +12,7 @@ namespace Ryujinx.HLE.Loaders.Processes { public class ProcessResult { - public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish); + public static ProcessResult Failed => new(null, new BlitStruct<ApplicationControlProperty>(1), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish); private readonly byte _mainThreadPriority; private readonly uint _mainThreadStackSize; @@ -31,15 +32,15 @@ namespace Ryujinx.HLE.Loaders.Processes public readonly bool AllowCodeMemoryForJit; public ProcessResult( - MetaLoader metaLoader, - ApplicationControlProperty applicationControlProperties, - bool diskCacheEnabled, - bool allowCodeMemoryForJit, - IDiskCacheLoadState diskCacheLoadState, - ulong pid, - byte mainThreadPriority, - uint mainThreadStackSize, - TitleLanguage titleLanguage) + MetaLoader metaLoader, + BlitStruct<ApplicationControlProperty> applicationControlProperties, + bool diskCacheEnabled, + bool allowCodeMemoryForJit, + IDiskCacheLoadState diskCacheLoadState, + ulong pid, + byte mainThreadPriority, + uint mainThreadStackSize, + TitleLanguage titleLanguage) { _mainThreadPriority = mainThreadPriority; _mainThreadStackSize = mainThreadStackSize; @@ -48,7 +49,7 @@ namespace Ryujinx.HLE.Loaders.Processes ProcessId = pid; MetaLoader = metaLoader; - ApplicationControlProperties = applicationControlProperties; + ApplicationControlProperties = applicationControlProperties.Value; if (metaLoader is not null) { From 895d9b53bc37507fed6829a7f91a1b8e3237ab0b Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 8 May 2023 09:59:26 +0100 Subject: [PATCH 509/737] Vulkan: Batch vertex buffer updates (#4843) * Vulkan: Batch vertex buffer updates Some games can bind a large number of vertex buffers for draws. This PR allows for vertex buffers to be updated with one call rather than one per buffer. This mostly affects the AMD Mesa driver, the testing platform was Steam Deck with Super Mario Odyssey. It was taking about 12% before, should be greatly reduced now. A small optimization has been added to avoid looking up the same buffer multiple times, as a common pattern is for the same buffer to be bound many times in a row with different ranges. * Only rebind vertex buffers if they have changed * Address feedback --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 43 +++++++--- .../VertexBufferState.cs | 39 ++------- .../VertexBufferUpdater.cs | 84 +++++++++++++++++++ 3 files changed, 124 insertions(+), 42 deletions(-) create mode 100644 src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index cf31a7f83..4aec0dbca 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -66,6 +66,8 @@ namespace Ryujinx.Graphics.Vulkan private ulong _vertexBuffersDirty; protected Rectangle<int> ClearScissor; + private readonly VertexBufferUpdater _vertexBufferUpdater; + public SupportBufferUpdater SupportBufferUpdater; public IndexBufferPattern QuadsToTrisPattern; public IndexBufferPattern TriFanToTrisPattern; @@ -96,6 +98,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); _descriptorSetUpdater = new DescriptorSetUpdater(gd, this); + _vertexBufferUpdater = new VertexBufferUpdater(gd); _transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers]; _vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1]; @@ -1185,6 +1188,9 @@ namespace Ryujinx.Graphics.Vulkan int validCount = 1; + BufferHandle lastHandle = default; + Auto<DisposableBuffer> lastBuffer = default; + for (int i = 0; i < count; i++) { var vertexBuffer = vertexBuffers[i]; @@ -1194,7 +1200,12 @@ namespace Ryujinx.Graphics.Vulkan if (vertexBuffer.Buffer.Handle != BufferHandle.Null) { - var vb = Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false); + Auto<DisposableBuffer> vb = (vertexBuffer.Buffer.Handle == lastHandle) ? lastBuffer : + Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false); + + lastHandle = vertexBuffer.Buffer.Handle; + lastBuffer = vb; + if (vb != null) { int binding = i + 1; @@ -1222,24 +1233,29 @@ namespace Ryujinx.Graphics.Vulkan ref var buffer = ref _vertexBuffers[binding]; int oldScalarAlign = buffer.AttributeScalarAlignment; - buffer.Dispose(); - if (Gd.Capabilities.VertexBufferAlignment < 2 && (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) { - buffer = new VertexBufferState( - vb, - descriptorIndex, - vertexBuffer.Buffer.Offset, - vbSize, - vertexBuffer.Stride); + if (!buffer.Matches(vb, descriptorIndex, vertexBuffer.Buffer.Offset, vbSize, vertexBuffer.Stride)) + { + buffer.Dispose(); - buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState); + buffer = new VertexBufferState( + vb, + descriptorIndex, + vertexBuffer.Buffer.Offset, + vbSize, + vertexBuffer.Stride); + + buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater); + } } else { // May need to be rewritten. Bind this buffer before draw. + buffer.Dispose(); + buffer = new VertexBufferState( vertexBuffer.Buffer.Handle, descriptorIndex, @@ -1255,6 +1271,8 @@ namespace Ryujinx.Graphics.Vulkan } } + _vertexBufferUpdater.Commit(Cbs); + _newState.VertexBindingDescriptionsCount = (uint)validCount; SignalStateChange(); } @@ -1596,10 +1614,12 @@ namespace Ryujinx.Graphics.Vulkan { int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty); - _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState); + _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater); _vertexBuffersDirty &= ~(1UL << i); } + + _vertexBufferUpdater.Commit(Cbs); } if (_stateDirty || Pbp != pbp) @@ -1712,6 +1732,7 @@ namespace Ryujinx.Graphics.Vulkan _framebuffer?.Dispose(); _newState.Dispose(); _descriptorSetUpdater.Dispose(); + _vertexBufferUpdater.Dispose(); for (int i = 0; i < _vertexBuffers.Length; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index c48560194..2118bfaeb 100644 --- a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan AttributeScalarAlignment = 1; } - public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state) + public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state, VertexBufferUpdater updater) { var autoBuffer = _buffer; @@ -65,21 +65,7 @@ namespace Ryujinx.Graphics.Vulkan var buffer = autoBuffer.Get(cbs, 0, newSize).Value; - if (gd.Capabilities.SupportsExtendedDynamicState) - { - gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( - cbs.CommandBuffer, - binding, - 1, - buffer, - 0, - (ulong)newSize, - (ulong)stride); - } - else - { - gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0); - } + updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride); _buffer = autoBuffer; @@ -106,21 +92,7 @@ namespace Ryujinx.Graphics.Vulkan { var buffer = autoBuffer.Get(cbs, _offset, _size).Value; - if (gd.Capabilities.SupportsExtendedDynamicState) - { - gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( - cbs.CommandBuffer, - binding, - 1, - buffer, - (ulong)_offset, - (ulong)_size, - (ulong)_stride); - } - else - { - gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset); - } + updater.BindVertexBuffer(cbs, binding, buffer, (ulong)_offset, (ulong)_size, (ulong)_stride); } } @@ -129,6 +101,11 @@ namespace Ryujinx.Graphics.Vulkan return _buffer == buffer; } + public bool Matches(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0) + { + return _buffer == buffer && DescriptorIndex == descriptorIndex && _offset == offset && _size == size && _stride == stride; + } + public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) { if (_buffer == from) diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs new file mode 100644 index 000000000..bceaeb8c9 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs @@ -0,0 +1,84 @@ +using Silk.NET.Vulkan; +using System; + +using VkBuffer = Silk.NET.Vulkan.Buffer; + +namespace Ryujinx.Graphics.Vulkan +{ + internal class VertexBufferUpdater : IDisposable + { + private VulkanRenderer _gd; + + private uint _baseBinding; + private uint _count; + + private NativeArray<VkBuffer> _buffers; + private NativeArray<ulong> _offsets; + private NativeArray<ulong> _sizes; + private NativeArray<ulong> _strides; + + public VertexBufferUpdater(VulkanRenderer gd) + { + _gd = gd; + + _buffers = new NativeArray<VkBuffer>(Constants.MaxVertexBuffers); + _offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers); + _sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers); + _strides = new NativeArray<ulong>(Constants.MaxVertexBuffers); + } + + public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride) + { + if (_count == 0) + { + _baseBinding = binding; + } + else if (_baseBinding + _count != binding) + { + Commit(cbs); + _baseBinding = binding; + } + + int index = (int)_count; + + _buffers[index] = buffer; + _offsets[index] = offset; + _sizes[index] = size; + _strides[index] = stride; + + _count++; + } + + public unsafe void Commit(CommandBufferScoped cbs) + { + if (_count != 0) + { + if (_gd.Capabilities.SupportsExtendedDynamicState) + { + _gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( + cbs.CommandBuffer, + _baseBinding, + _count, + _buffers.Pointer, + _offsets.Pointer, + _sizes.Pointer, + _strides.Pointer); + } + else + { + _gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, _baseBinding, _count, _buffers.Pointer, _offsets.Pointer); + } + + _count = 0; + } + } + + public void Dispose() + { + _buffers.Dispose(); + _offsets.Dispose(); + _sizes.Dispose(); + _strides.Dispose(); + } + } +} From 1b28ecd63eb49917e3711eb7e06739ebe87e8f41 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 8 May 2023 11:45:12 +0100 Subject: [PATCH 510/737] Vulkan: Simplify MultiFenceHolder and managing them (#4845) * Vulkan: Simplify waitable add/remove Removal of unnecessary hashset and dictionary * Thread safety for GetBufferData in PersistentFlushBuffer * Fix WaitForFencesImpl thread safety * Proper methods for risky reference increments * Wrong type of CB. * Address feedback --- src/Ryujinx.Graphics.Vulkan/Auto.cs | 17 +++ src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 5 +- .../CommandBufferPool.cs | 14 ++- src/Ryujinx.Graphics.Vulkan/FenceHolder.cs | 19 +++ .../MultiFenceHolder.cs | 117 ++++++++++++------ .../PersistentFlushBuffer.cs | 14 ++- 6 files changed, 138 insertions(+), 48 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/Auto.cs b/src/Ryujinx.Graphics.Vulkan/Auto.cs index 77261de98..fdce7232c 100644 --- a/src/Ryujinx.Graphics.Vulkan/Auto.cs +++ b/src/Ryujinx.Graphics.Vulkan/Auto.cs @@ -105,6 +105,23 @@ namespace Ryujinx.Graphics.Vulkan } } + 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) diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index a1ea6836f..9a23280d0 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -599,9 +599,10 @@ namespace Ryujinx.Graphics.Vulkan Auto<DisposableBuffer> dst, int srcOffset, int dstOffset, - int size) + int size, + bool registerSrcUsage = true) { - var srcBuffer = src.Get(cbs, srcOffset, size).Value; + var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value; var dstBuffer = dst.Get(cbs, dstOffset, size).Value; InsertBufferBarrier( diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs index 4cbb24ef7..42b46eaec 100644 --- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Vulkan public SemaphoreHolder Semaphore; public List<IAuto> Dependants; - public HashSet<MultiFenceHolder> Waitables; + public List<MultiFenceHolder> Waitables; public HashSet<SemaphoreHolder> Dependencies; public void Initialize(Vk api, Device device, CommandPool pool) @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Vulkan api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer); Dependants = new List<IAuto>(); - Waitables = new HashSet<MultiFenceHolder>(); + Waitables = new List<MultiFenceHolder>(); Dependencies = new HashSet<SemaphoreHolder>(); } } @@ -143,8 +143,10 @@ namespace Ryujinx.Graphics.Vulkan public void AddWaitable(int cbIndex, MultiFenceHolder waitable) { ref var entry = ref _commandBuffers[cbIndex]; - waitable.AddFence(cbIndex, entry.Fence); - entry.Waitables.Add(waitable); + if (waitable.AddFence(cbIndex, entry.Fence)) + { + entry.Waitables.Add(waitable); + } } public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size) @@ -156,7 +158,7 @@ namespace Ryujinx.Graphics.Vulkan ref var entry = ref _commandBuffers[i]; if (entry.InUse && - entry.Waitables.Contains(waitable) && + waitable.HasFence(i) && waitable.IsBufferRangeInUse(i, offset, size)) { return true; @@ -331,7 +333,7 @@ namespace Ryujinx.Graphics.Vulkan foreach (var waitable in entry.Waitables) { - waitable.RemoveFence(cbIndex, entry.Fence); + waitable.RemoveFence(cbIndex); waitable.RemoveBufferUses(cbIndex); } diff --git a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs index 1c1e62407..39d226983 100644 --- a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs @@ -32,6 +32,25 @@ namespace Ryujinx.Graphics.Vulkan return _fence; } + public bool TryGet(out Fence 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 Fence Get() { Interlocked.Increment(ref _referenceCount); diff --git a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs index 9a9a3626c..13a4f4c14 100644 --- a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs @@ -1,6 +1,5 @@ using Silk.NET.Vulkan; -using System.Collections.Generic; -using System.Linq; +using System; namespace Ryujinx.Graphics.Vulkan { @@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan { private static int BufferUsageTrackingGranularity = 4096; - private readonly Dictionary<FenceHolder, int> _fences; + private readonly FenceHolder[] _fences; private BufferUsageBitmap _bufferUsageBitmap; /// <summary> @@ -19,7 +18,7 @@ namespace Ryujinx.Graphics.Vulkan /// </summary> public MultiFenceHolder() { - _fences = new Dictionary<FenceHolder, int>(); + _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; } /// <summary> @@ -28,7 +27,7 @@ namespace Ryujinx.Graphics.Vulkan /// <param name="size">Size of the buffer</param> public MultiFenceHolder(int size) { - _fences = new Dictionary<FenceHolder, int>(); + _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); } @@ -80,25 +79,37 @@ namespace Ryujinx.Graphics.Vulkan /// </summary> /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param> /// <param name="fence">Fence to be added</param> - public void AddFence(int cbIndex, FenceHolder fence) + /// <returns>True if the command buffer's previous fence value was null</returns> + public bool AddFence(int cbIndex, FenceHolder fence) { - lock (_fences) + ref FenceHolder fenceRef = ref _fences[cbIndex]; + + if (fenceRef == null) { - _fences.TryAdd(fence, cbIndex); + fenceRef = fence; + return true; } + + return false; } /// <summary> /// Removes a fence from the holder. /// </summary> /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param> - /// <param name="fence">Fence to be removed</param> - public void RemoveFence(int cbIndex, FenceHolder fence) + public void RemoveFence(int cbIndex) { - lock (_fences) - { - _fences.Remove(fence); - } + _fences[cbIndex] = null; + } + + /// <summary> + /// Determines if a fence referenced on the given command buffer. + /// </summary> + /// <param name="cbIndex">Index of the command buffer to check if it's used</param> + /// <returns>True if referenced, false otherwise</returns> + public bool HasFence(int cbIndex) + { + return _fences[cbIndex] != null; } /// <summary> @@ -147,21 +158,29 @@ namespace Ryujinx.Graphics.Vulkan /// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns> private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout) { - FenceHolder[] fenceHolders; - Fence[] fences; + Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - lock (_fences) + int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders); + Span<Fence> fences = stackalloc Fence[count]; + + int fenceCount = 0; + + for (int i = 0; i < count; i++) { - fenceHolders = size != 0 ? GetOverlappingFences(offset, size) : _fences.Keys.ToArray(); - fences = new Fence[fenceHolders.Length]; - - for (int i = 0; i < fenceHolders.Length; i++) + if (fenceHolders[i].TryGet(out Fence fence)) { - fences[i] = fenceHolders[i].Get(); + fences[fenceCount] = fence; + + if (fenceCount < i) + { + fenceHolders[fenceCount] = fenceHolders[i]; + } + + fenceCount++; } } - if (fences.Length == 0) + if (fenceCount == 0) { return true; } @@ -170,14 +189,14 @@ namespace Ryujinx.Graphics.Vulkan if (hasTimeout) { - signaled = FenceHelper.AllSignaled(api, device, fences, timeout); + signaled = FenceHelper.AllSignaled(api, device, fences.Slice(0, fenceCount), timeout); } else { - FenceHelper.WaitAllIndefinitely(api, device, fences); + FenceHelper.WaitAllIndefinitely(api, device, fences.Slice(0, fenceCount)); } - for (int i = 0; i < fenceHolders.Length; i++) + for (int i = 0; i < fenceCount; i++) { fenceHolders[i].Put(); } @@ -186,27 +205,49 @@ namespace Ryujinx.Graphics.Vulkan } /// <summary> - /// Gets fences to wait for use of a given buffer region. + /// Gets fences to wait for. /// </summary> - /// <param name="offset">Offset of the range</param> - /// <param name="size">Size of the range in bytes</param> - /// <returns>Fences for the specified region</returns> - private FenceHolder[] GetOverlappingFences(int offset, int size) + /// <param name="storage">Span to store fences in</param> + /// <returns>Number of fences placed in storage</returns> + private int GetFences(Span<FenceHolder> storage) { - List<FenceHolder> overlapping = new List<FenceHolder>(); + int count = 0; - foreach (var kv in _fences) + for (int i = 0; i < _fences.Length; i++) { - var fence = kv.Key; - var ownerCbIndex = kv.Value; + var fence = _fences[i]; - if (_bufferUsageBitmap.OverlapsWith(ownerCbIndex, offset, size)) + if (fence != null) { - overlapping.Add(fence); + storage[count++] = fence; } } - return overlapping.ToArray(); + return count; + } + + /// <summary> + /// Gets fences to wait for use of a given buffer region. + /// </summary> + /// <param name="storage">Span to store overlapping fences in</param> + /// <param name="offset">Offset of the range</param> + /// <param name="size">Size of the range in bytes</param> + /// <returns>Number of fences for the specified region placed in storage</returns> + private int GetOverlappingFences(Span<FenceHolder> storage, int offset, int size) + { + int count = 0; + + for (int i = 0; i < _fences.Length; i++) + { + var fence = _fences[i]; + + if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size)) + { + storage[count++] = fence; + } + } + + return count; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs index fca13c314..fc98b68f7 100644 --- a/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs @@ -34,16 +34,26 @@ namespace Ryujinx.Graphics.Vulkan public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size) { var flushStorage = ResizeIfNeeded(size); + Auto<DisposableBuffer> srcBuffer; using (var cbs = cbp.Rent()) { - var srcBuffer = buffer.GetBuffer(cbs.CommandBuffer); + srcBuffer = buffer.GetBuffer(cbs.CommandBuffer); var dstBuffer = flushStorage.GetBuffer(cbs.CommandBuffer); - BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size); + if (srcBuffer.TryIncrementReferenceCount()) + { + BufferHolder.Copy(_gd, 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); } From 162798b026a860a4fa51bd94dabb15f8f00e5a2b Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 8 May 2023 12:48:16 +0200 Subject: [PATCH 511/737] vulkan: Avoid hardcoding features in CreateDevice (#4858) Those shouldn't have been hardcoded. --- .../VulkanInitialization.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index bad6641e3..499a9ef78 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -351,26 +351,26 @@ namespace Ryujinx.Graphics.Vulkan var features = new PhysicalDeviceFeatures() { - DepthBiasClamp = true, + DepthBiasClamp = supportedFeatures.DepthBiasClamp, DepthClamp = supportedFeatures.DepthClamp, DualSrcBlend = supportedFeatures.DualSrcBlend, - FragmentStoresAndAtomics = true, + FragmentStoresAndAtomics = supportedFeatures.FragmentStoresAndAtomics, GeometryShader = supportedFeatures.GeometryShader, - ImageCubeArray = true, - IndependentBlend = true, + ImageCubeArray = supportedFeatures.ImageCubeArray, + IndependentBlend = supportedFeatures.IndependentBlend, LogicOp = supportedFeatures.LogicOp, OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, MultiViewport = supportedFeatures.MultiViewport, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, - SamplerAnisotropy = true, - ShaderClipDistance = true, + SamplerAnisotropy = supportedFeatures.SamplerAnisotropy, + ShaderClipDistance = supportedFeatures.ShaderClipDistance, ShaderFloat64 = supportedFeatures.ShaderFloat64, ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, // ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true, TessellationShader = supportedFeatures.TessellationShader, - VertexPipelineStoresAndAtomics = true, + VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics, RobustBufferAccess = useRobustBufferAccess }; From a8950d6ac4c4d4e8b3884f799566603db2d9c406 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 8 May 2023 13:05:37 +0200 Subject: [PATCH 512/737] vulkan: Pass Vk instance to VulkanRenderer (#4859) This will allow possible multiple driver selection without any need of LD preload. (useful when testing custom version of mesa for example) --- src/Ryujinx.Ava/AppHost.cs | 2 ++ .../UI/ViewModels/SettingsViewModel.cs | 3 +- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 29 +++++++++---------- src/Ryujinx.Headless.SDL2/Program.cs | 4 ++- src/Ryujinx/Ui/MainWindow.cs | 2 +- src/Ryujinx/Ui/Windows/SettingsWindow.cs | 3 +- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index e11a954d8..0955fb270 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -35,6 +35,7 @@ using Ryujinx.Input.HLE; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; +using Silk.NET.Vulkan; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; @@ -701,6 +702,7 @@ namespace Ryujinx.Ava if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) { renderer = new VulkanRenderer( + Vk.GetApi(), (_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, VulkanHelper.GetRequiredInstanceExtensions, ConfigurationState.Instance.Graphics.PreferredGpu.Value); diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 75a5168fc..89392f6b2 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -25,6 +25,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Net.NetworkInformation; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; +using Silk.NET.Vulkan; namespace Ryujinx.Ava.UI.ViewModels { @@ -310,7 +311,7 @@ namespace Ryujinx.Ava.UI.ViewModels { _gpuIds = new List<string>(); List<string> names = new(); - var devices = VulkanRenderer.GetPhysicalDevices(); + var devices = VulkanRenderer.GetPhysicalDevices(Vk.GetApi()); if (devices.Length == 0) { diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 06fec4f1b..e4ce4904b 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -89,11 +89,12 @@ namespace Ryujinx.Graphics.Vulkan public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; - public VulkanRenderer(Func<Instance, Vk, SurfaceKHR> surfaceFunc, Func<string[]> requiredExtensionsFunc, string preferredGpuId) + public VulkanRenderer(Vk api, Func<Instance, Vk, SurfaceKHR> surfaceFunc, Func<string[]> requiredExtensionsFunc, string preferredGpuId) { _getSurface = surfaceFunc; _getRequiredExtensions = requiredExtensionsFunc; _preferredGpuId = preferredGpuId; + Api = api; Shaders = new HashSet<ShaderCollection>(); Textures = new HashSet<ITexture>(); Samplers = new HashSet<SamplerHolder>(); @@ -345,31 +346,27 @@ namespace Ryujinx.Graphics.Vulkan private unsafe void SetupContext(GraphicsDebugLevel logLevel) { - var api = Vk.GetApi(); + _instance = VulkanInitialization.CreateInstance(Api, logLevel, _getRequiredExtensions()); + _debugMessenger = new VulkanDebugMessenger(Api, _instance.Instance, logLevel); - Api = api; - - _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions()); - _debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel); - - if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi)) + if (Api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi)) { SurfaceApi = surfaceApi; } - _surface = _getSurface(_instance.Instance, api); - _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId); + _surface = _getSurface(_instance.Instance, Api); + _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(Api, _instance, _surface, _preferredGpuId); - var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount); + var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(Api, _physicalDevice, _surface, out uint maxQueueCount); - _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount); + _device = VulkanInitialization.CreateDevice(Api, _physicalDevice, queueFamilyIndex, maxQueueCount); - if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi)) + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi)) { SwapchainApi = swapchainApi; } - api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue); + Api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue); Queue = queue; QueueLock = new object(); @@ -603,11 +600,11 @@ namespace Ryujinx.Graphics.Vulkan return new HardwareInfo(GpuVendor, GpuRenderer); } - public static DeviceInfo[] GetPhysicalDevices() + public static DeviceInfo[] GetPhysicalDevices(Vk api) { try { - return VulkanInitialization.GetSuitablePhysicalDevices(Vk.GetApi()); + return VulkanInitialization.GetSuitablePhysicalDevices(api); } catch (Exception) { diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 453c470e7..643db845f 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -487,11 +487,12 @@ namespace Ryujinx.Headless.SDL2 if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) { string preferredGpuId = string.Empty; + Vk api = Vk.GetApi(); if (!string.IsNullOrEmpty(options.PreferredGpuVendor)) { string preferredGpuVendor = options.PreferredGpuVendor.ToLowerInvariant(); - var devices = VulkanRenderer.GetPhysicalDevices(); + var devices = VulkanRenderer.GetPhysicalDevices(api); foreach (var device in devices) { @@ -504,6 +505,7 @@ namespace Ryujinx.Headless.SDL2 } return new VulkanRenderer( + api, (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), vulkanWindow.GetRequiredInstanceExtensions, preferredGpuId); diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index b61855e4c..f4cb3d072 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -426,7 +426,7 @@ namespace Ryujinx.Ui if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) { string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - renderer = new VulkanRenderer(CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); + renderer = new VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); } else { diff --git a/src/Ryujinx/Ui/Windows/SettingsWindow.cs b/src/Ryujinx/Ui/Windows/SettingsWindow.cs index 7d39b9ea1..65175ff15 100644 --- a/src/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/src/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -13,6 +13,7 @@ using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Helper; using Ryujinx.Ui.Widgets; +using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Globalization; @@ -477,7 +478,7 @@ namespace Ryujinx.Ui.Windows if (Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan) { - var devices = VulkanRenderer.GetPhysicalDevices(); + var devices = VulkanRenderer.GetPhysicalDevices(Vk.GetApi()); string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value; string preferredGpuId = preferredGpuIdFromConfig; bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig); From 40c17673f5a1a7c96f548dc4efaf05bba832a340 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 9 May 2023 06:02:39 -0300 Subject: [PATCH 513/737] Replace DelegateHelper with pre-generated delegates (#4867) --- Ryujinx.sln | 4 +- src/ARMeilleure/Translation/DelegateHelper.cs | 104 ---- src/ARMeilleure/Translation/Delegates.cs | 512 ++++++++++++------ 3 files changed, 345 insertions(+), 275 deletions(-) delete mode 100644 src/ARMeilleure/Translation/DelegateHelper.cs diff --git a/Ryujinx.sln b/Ryujinx.sln index f9b28c1ba..bb196cabc 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -39,7 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory.Tests", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "src\Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}" EndProject @@ -256,4 +256,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A} EndGlobalSection -EndGlobal \ No newline at end of file +EndGlobal diff --git a/src/ARMeilleure/Translation/DelegateHelper.cs b/src/ARMeilleure/Translation/DelegateHelper.cs deleted file mode 100644 index 43a39bab0..000000000 --- a/src/ARMeilleure/Translation/DelegateHelper.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; - -namespace ARMeilleure.Translation -{ - static class DelegateHelper - { - private const string DelegateTypesAssemblyName = "JitDelegateTypes"; - - private static readonly ModuleBuilder _modBuilder; - - private static readonly Dictionary<string, Type> _delegateTypesCache; - - static DelegateHelper() - { - AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run); - - _modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName); - - _delegateTypesCache = new Dictionary<string, Type>(); - } - - public static Delegate GetDelegate(MethodInfo info) - { - ArgumentNullException.ThrowIfNull(info); - - Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray(); - Type returnType = info.ReturnType; - - Type delegateType = GetDelegateType(parameters, returnType); - - return Delegate.CreateDelegate(delegateType, info); - } - - private static Type GetDelegateType(Type[] parameters, Type returnType) - { - string key = GetFunctionSignatureKey(parameters, returnType); - - if (!_delegateTypesCache.TryGetValue(key, out Type delegateType)) - { - delegateType = MakeDelegateType(parameters, returnType, key); - - _delegateTypesCache.TryAdd(key, delegateType); - } - - return delegateType; - } - - private static string GetFunctionSignatureKey(Type[] parameters, Type returnType) - { - string sig = GetTypeName(returnType); - - foreach (Type type in parameters) - { - sig += '_' + GetTypeName(type); - } - - return sig; - } - - private static string GetTypeName(Type type) - { - return type.FullName.Replace(".", string.Empty); - } - - private const MethodAttributes CtorAttributes = - MethodAttributes.RTSpecialName | - MethodAttributes.HideBySig | - MethodAttributes.Public; - - private const TypeAttributes DelegateTypeAttributes = - TypeAttributes.Class | - TypeAttributes.Public | - TypeAttributes.Sealed | - TypeAttributes.AnsiClass | - TypeAttributes.AutoClass; - - private const MethodImplAttributes ImplAttributes = - MethodImplAttributes.Runtime | - MethodImplAttributes.Managed; - - private const MethodAttributes InvokeAttributes = - MethodAttributes.Public | - MethodAttributes.HideBySig | - MethodAttributes.NewSlot | - MethodAttributes.Virtual; - - private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) }; - - private static Type MakeDelegateType(Type[] parameters, Type returnType, string name) - { - TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate)); - - builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes); - - builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes); - - return builder.CreateTypeInfo(); - } - } -} diff --git a/src/ARMeilleure/Translation/Delegates.cs b/src/ARMeilleure/Translation/Delegates.cs index 55f1e5145..63db789df 100644 --- a/src/ARMeilleure/Translation/Delegates.cs +++ b/src/ARMeilleure/Translation/Delegates.cs @@ -1,4 +1,5 @@ using ARMeilleure.Instructions; +using ARMeilleure.State; using System; using System.Collections.Generic; using System.Reflection; @@ -63,11 +64,9 @@ namespace ARMeilleure.Translation return index; } - private static void SetDelegateInfo(MethodInfo info) + private static void SetDelegateInfo(Delegate dlg) { - string key = GetKey(info); - - Delegate dlg = DelegateHelper.GetDelegate(info); + string key = GetKey(dlg.Method); _delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key). } @@ -83,179 +82,354 @@ namespace ARMeilleure.Translation { _delegates = new SortedList<string, DelegateInfo>(); - SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) })); - SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) })); - SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) })); - SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) })); - SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) })); + SetDelegateInfo(new MathAbs(Math.Abs)); + SetDelegateInfo(new MathCeiling(Math.Ceiling)); + SetDelegateInfo(new MathFloor(Math.Floor)); + SetDelegateInfo(new MathRound(Math.Round)); + SetDelegateInfo(new MathTruncate(Math.Truncate)); - SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) })); - SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) })); - SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) })); - SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) })); - SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) })); + SetDelegateInfo(new MathFAbs(MathF.Abs)); + SetDelegateInfo(new MathFCeiling(MathF.Ceiling)); + SetDelegateInfo(new MathFFloor(MathF.Floor)); + SetDelegateInfo(new MathFRound(MathF.Round)); + SetDelegateInfo(new MathFTruncate(MathF.Truncate)); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128))); + SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break)); + SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization)); + SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit)); + SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0)); + SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0)); + SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0)); + SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0)); + SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0)); + SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress)); + SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine)); + SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte)); + SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16)); + SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32)); + SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64)); + SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128)); + SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking)); + SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall)); + SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess)); + SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined)); + SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte)); + SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16)); + SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32)); + SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64)); + SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128)); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4))); - SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64))); + SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns)); + SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros)); + SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b)); + SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb)); + SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch)); + SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw)); + SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx)); + SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h)); + SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w)); + SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x)); + SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt)); + SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt)); + SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate)); + SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose)); + SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower)); + SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority)); + SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity)); + SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper)); + SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns)); + SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns)); + SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128)); + SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32)); + SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64)); + SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32)); + SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64)); + SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32)); + SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64)); + SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32)); + SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64)); + SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1)); + SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2)); + SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1)); + SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2)); + SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64)); + SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1)); + SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2)); + SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3)); + SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4)); + SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1)); + SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2)); + SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3)); + SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4)); + SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64)); - SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert))); - SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))); + SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert)); + SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert)); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only. - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt))); - SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub))); + SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd)); + SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare)); + SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ)); + SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE)); + SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT)); + SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE)); + SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT)); + SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv)); + SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax)); + SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum)); + SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin)); + SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum)); + SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul)); + SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd)); + SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub)); + SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX)); + SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd)); + SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub)); + SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate)); + SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused)); + SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX)); + SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate)); + SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only. + SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused)); + SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt)); + SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub)); - SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert))); + SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert)); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only. - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt))); - SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub))); + SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd)); + SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare)); + SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ)); + SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE)); + SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT)); + SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE)); + SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT)); + SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv)); + SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax)); + SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum)); + SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin)); + SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum)); + SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul)); + SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd)); + SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub)); + SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX)); + SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd)); + SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub)); + SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate)); + SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused)); + SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX)); + SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate)); + SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only. + SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused)); + SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt)); + SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub)); - SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))); + SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert)); } + + private delegate double MathAbs(double value); + private delegate double MathCeiling(double a); + private delegate double MathFloor(double d); + private delegate double MathRound(double value, MidpointRounding mode); + private delegate double MathTruncate(double d); + + private delegate float MathFAbs(float x); + private delegate float MathFCeiling(float x); + private delegate float MathFFloor(float x); + private delegate float MathFRound(float x, MidpointRounding mode); + private delegate float MathFTruncate(float x); + + private delegate void NativeInterfaceBreak(ulong address, int imm); + private delegate bool NativeInterfaceCheckSynchronization(); + private delegate void NativeInterfaceEnqueueForRejit(ulong address); + private delegate ulong NativeInterfaceGetCntfrqEl0(); + private delegate ulong NativeInterfaceGetCntpctEl0(); + private delegate ulong NativeInterfaceGetCntvctEl0(); + private delegate ulong NativeInterfaceGetCtrEl0(); + private delegate ulong NativeInterfaceGetDczidEl0(); + private delegate ulong NativeInterfaceGetFunctionAddress(ulong address); + private delegate void NativeInterfaceInvalidateCacheLine(ulong address); + private delegate byte NativeInterfaceReadByte(ulong address); + private delegate ushort NativeInterfaceReadUInt16(ulong address); + private delegate uint NativeInterfaceReadUInt32(ulong address); + private delegate ulong NativeInterfaceReadUInt64(ulong address); + private delegate V128 NativeInterfaceReadVector128(ulong address); + private delegate void NativeInterfaceSignalMemoryTracking(ulong address, ulong size, bool write); + private delegate void NativeInterfaceSupervisorCall(ulong address, int imm); + private delegate void NativeInterfaceThrowInvalidMemoryAccess(ulong address); + private delegate void NativeInterfaceUndefined(ulong address, int opCode); + private delegate void NativeInterfaceWriteByte(ulong address, byte value); + private delegate void NativeInterfaceWriteUInt16(ulong address, ushort value); + private delegate void NativeInterfaceWriteUInt32(ulong address, uint value); + private delegate void NativeInterfaceWriteUInt64(ulong address, ulong value); + private delegate void NativeInterfaceWriteVector128(ulong address, V128 value); + + private delegate ulong SoftFallbackCountLeadingSigns(ulong value, int size); + private delegate ulong SoftFallbackCountLeadingZeros(ulong value, int size); + private delegate uint SoftFallbackCrc32b(uint crc, byte value); + private delegate uint SoftFallbackCrc32cb(uint crc, byte value); + private delegate uint SoftFallbackCrc32ch(uint crc, ushort value); + private delegate uint SoftFallbackCrc32cw(uint crc, uint value); + private delegate uint SoftFallbackCrc32cx(uint crc, ulong value); + private delegate uint SoftFallbackCrc32h(uint crc, ushort value); + private delegate uint SoftFallbackCrc32w(uint crc, uint value); + private delegate uint SoftFallbackCrc32x(uint crc, ulong value); + private delegate V128 SoftFallbackDecrypt(V128 value, V128 roundKey); + private delegate V128 SoftFallbackEncrypt(V128 value, V128 roundKey); + private delegate uint SoftFallbackFixedRotate(uint hash_e); + private delegate V128 SoftFallbackHashChoose(V128 hash_abcd, uint hash_e, V128 wk); + private delegate V128 SoftFallbackHashLower(V128 hash_abcd, V128 hash_efgh, V128 wk); + private delegate V128 SoftFallbackHashMajority(V128 hash_abcd, uint hash_e, V128 wk); + private delegate V128 SoftFallbackHashParity(V128 hash_abcd, uint hash_e, V128 wk); + private delegate V128 SoftFallbackHashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk); + private delegate V128 SoftFallbackInverseMixColumns(V128 value); + private delegate V128 SoftFallbackMixColumns(V128 value); + private delegate V128 SoftFallbackPolynomialMult64_128(ulong op1, ulong op2); + private delegate int SoftFallbackSatF32ToS32(float value); + private delegate long SoftFallbackSatF32ToS64(float value); + private delegate uint SoftFallbackSatF32ToU32(float value); + private delegate ulong SoftFallbackSatF32ToU64(float value); + private delegate int SoftFallbackSatF64ToS32(double value); + private delegate long SoftFallbackSatF64ToS64(double value); + private delegate uint SoftFallbackSatF64ToU32(double value); + private delegate ulong SoftFallbackSatF64ToU64(double value); + private delegate V128 SoftFallbackSha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11); + private delegate V128 SoftFallbackSha1SchedulePart2(V128 tw0_3, V128 w12_15); + private delegate V128 SoftFallbackSha256SchedulePart1(V128 w0_3, V128 w4_7); + private delegate V128 SoftFallbackSha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15); + private delegate long SoftFallbackSignedShrImm64(long value, long roundConst, int shift); + private delegate V128 SoftFallbackTbl1(V128 vector, int bytes, V128 tb0); + private delegate V128 SoftFallbackTbl2(V128 vector, int bytes, V128 tb0, V128 tb1); + private delegate V128 SoftFallbackTbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2); + private delegate V128 SoftFallbackTbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3); + private delegate V128 SoftFallbackTbx1(V128 dest, V128 vector, int bytes, V128 tb0); + private delegate V128 SoftFallbackTbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1); + private delegate V128 SoftFallbackTbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2); + private delegate V128 SoftFallbackTbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3); + private delegate ulong SoftFallbackUnsignedShrImm64(ulong value, long roundConst, int shift); + + private delegate float SoftFloat16_32FPConvert(ushort valueBits); + + private delegate double SoftFloat16_64FPConvert(ushort valueBits); + + private delegate float SoftFloat32FPAdd(float value1, float value2); + private delegate float SoftFloat32FPAddFpscr(float value1, float value2, bool standardFpscr); + private delegate int SoftFloat32FPCompare(float value1, float value2, bool signalNaNs); + private delegate float SoftFloat32FPCompareEQ(float value1, float value2); + private delegate float SoftFloat32FPCompareEQFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPCompareGE(float value1, float value2); + private delegate float SoftFloat32FPCompareGEFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPCompareGT(float value1, float value2); + private delegate float SoftFloat32FPCompareGTFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPCompareLE(float value1, float value2); + private delegate float SoftFloat32FPCompareLEFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPCompareLT(float value1, float value2); + private delegate float SoftFloat32FPCompareLTFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPDiv(float value1, float value2); + private delegate float SoftFloat32FPMax(float value1, float value2); + private delegate float SoftFloat32FPMaxFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPMaxNum(float value1, float value2); + private delegate float SoftFloat32FPMaxNumFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPMin(float value1, float value2); + private delegate float SoftFloat32FPMinFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPMinNum(float value1, float value2); + private delegate float SoftFloat32FPMinNumFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPMul(float value1, float value2); + private delegate float SoftFloat32FPMulFpscr(float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPMulAdd(float valueA, float value1, float value2); + private delegate float SoftFloat32FPMulAddFpscr(float valueA, float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPMulSub(float valueA, float value1, float value2); + private delegate float SoftFloat32FPMulSubFpscr(float valueA, float value1, float value2, bool standardFpscr); + private delegate float SoftFloat32FPMulX(float value1, float value2); + private delegate float SoftFloat32FPNegMulAdd(float valueA, float value1, float value2); + private delegate float SoftFloat32FPNegMulSub(float valueA, float value1, float value2); + private delegate float SoftFloat32FPRecipEstimate(float value); + private delegate float SoftFloat32FPRecipEstimateFpscr(float value, bool standardFpscr); + private delegate float SoftFloat32FPRecipStep(float value1, float value2); + private delegate float SoftFloat32FPRecipStepFused(float value1, float value2); + private delegate float SoftFloat32FPRecpX(float value); + private delegate float SoftFloat32FPRSqrtEstimate(float value); + private delegate float SoftFloat32FPRSqrtEstimateFpscr(float value, bool standardFpscr); + private delegate float SoftFloat32FPRSqrtStep(float value1, float value2); + private delegate float SoftFloat32FPRSqrtStepFused(float value1, float value2); + private delegate float SoftFloat32FPSqrt(float value); + private delegate float SoftFloat32FPSub(float value1, float value2); + + private delegate ushort SoftFloat32_16FPConvert(float value); + + private delegate double SoftFloat64FPAdd(double value1, double value2); + private delegate double SoftFloat64FPAddFpscr(double value1, double value2, bool standardFpscr); + private delegate int SoftFloat64FPCompare(double value1, double value2, bool signalNaNs); + private delegate double SoftFloat64FPCompareEQ(double value1, double value2); + private delegate double SoftFloat64FPCompareEQFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPCompareGE(double value1, double value2); + private delegate double SoftFloat64FPCompareGEFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPCompareGT(double value1, double value2); + private delegate double SoftFloat64FPCompareGTFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPCompareLE(double value1, double value2); + private delegate double SoftFloat64FPCompareLEFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPCompareLT(double value1, double value2); + private delegate double SoftFloat64FPCompareLTFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPDiv(double value1, double value2); + private delegate double SoftFloat64FPMax(double value1, double value2); + private delegate double SoftFloat64FPMaxFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPMaxNum(double value1, double value2); + private delegate double SoftFloat64FPMaxNumFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPMin(double value1, double value2); + private delegate double SoftFloat64FPMinFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPMinNum(double value1, double value2); + private delegate double SoftFloat64FPMinNumFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPMul(double value1, double value2); + private delegate double SoftFloat64FPMulFpscr(double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPMulAdd(double valueA, double value1, double value2); + private delegate double SoftFloat64FPMulAddFpscr(double valueA, double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPMulSub(double valueA, double value1, double value2); + private delegate double SoftFloat64FPMulSubFpscr(double valueA, double value1, double value2, bool standardFpscr); + private delegate double SoftFloat64FPMulX(double value1, double value2); + private delegate double SoftFloat64FPNegMulAdd(double valueA, double value1, double value2); + private delegate double SoftFloat64FPNegMulSub(double valueA, double value1, double value2); + private delegate double SoftFloat64FPRecipEstimate(double value); + private delegate double SoftFloat64FPRecipEstimateFpscr(double value, bool standardFpscr); + private delegate double SoftFloat64FPRecipStep(double value1, double value2); + private delegate double SoftFloat64FPRecipStepFused(double value1, double value2); + private delegate double SoftFloat64FPRecpX(double value); + private delegate double SoftFloat64FPRSqrtEstimate(double value); + private delegate double SoftFloat64FPRSqrtEstimateFpscr(double value, bool standardFpscr); + private delegate double SoftFloat64FPRSqrtStep(double value1, double value2); + private delegate double SoftFloat64FPRSqrtStepFused(double value1, double value2); + private delegate double SoftFloat64FPSqrt(double value); + private delegate double SoftFloat64FPSub(double value1, double value2); + + private delegate ushort SoftFloat64_16FPConvert(double value); } } From 0bc8151c7ecdacc1506305a8d60e7b3c7b13622d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen <emmausssss@gmail.com> Date: Tue, 9 May 2023 21:46:23 +0000 Subject: [PATCH 514/737] IPC - Refactor Bcat service to use new ipc - Revisit (#4803) * bcat ipc * fix hipc buffer flags * add buffer fixed size flag on generator --- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- .../HOS/Services/Bcat/IServiceCreator.cs | 85 ------------------- .../HOS/Services/Bcat/ResultCode.cs | 29 ------- .../Bcat/ServiceCreator/IBcatService.cs | 18 ---- .../IDeliveryCacheDirectoryService.cs | 65 -------------- .../IDeliveryCacheFileService.cs | 78 ----------------- .../IDeliveryCacheProgressService.cs | 63 -------------- .../IDeliveryCacheStorageService.cs | 74 ---------------- .../Hipc/HipcGenerator.cs | 2 +- src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs | 48 +++++++++++ src/Ryujinx.Horizon/Bcat/BcatMain.cs | 24 ++++++ src/Ryujinx.Horizon/Bcat/BcatResult.cs | 29 +++++++ src/Ryujinx.Horizon/Bcat/BcatServerManager.cs | 28 ++++++ .../Bcat/Ipc/ServiceCreator.cs | 85 +++++++++++++++++++ .../Bcat/Ipc/ServiceCreator/BcatService.cs | 25 ++++++ .../DeliveryCacheDirectoryService.cs | 48 +++++++++++ .../DeliveryCacheFileService.cs | 54 ++++++++++++ .../DeliveryCacheProgressService.cs | 58 +++++++++++++ .../DeliveryCacheStorageService.cs | 74 ++++++++++++++++ .../Types/DeliveryCacheProgressImpl.cs | 2 +- .../Bcat/Types/BcatPortIndex.cs | 10 +++ .../Bcat/Types/BcatServicePermissionLevel.cs | 10 +++ src/Ryujinx.Horizon/HorizonOptions.cs | 7 +- src/Ryujinx.Horizon/LibHacResultExtensions.cs | 12 +++ src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs | 6 +- src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs | 10 +++ .../Bcat/IDeliveryCacheDirectoryService.cs | 14 +++ .../Sdk/Bcat/IDeliveryCacheFileService.cs | 15 ++++ .../Sdk/Bcat/IDeliveryCacheProgressService.cs | 12 +++ .../Sdk/Bcat/IDeliveryCacheStorageService.cs | 14 +++ .../Sdk/Bcat/IServiceCreator.cs | 12 +++ src/Ryujinx.Horizon/ServiceTable.cs | 2 + 32 files changed, 596 insertions(+), 419 deletions(-) delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatMain.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatResult.cs create mode 100644 src/Ryujinx.Horizon/Bcat/BcatServerManager.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs rename src/{Ryujinx.HLE/HOS/Services/Bcat => Ryujinx.Horizon/Bcat/Ipc}/ServiceCreator/Types/DeliveryCacheProgressImpl.cs (86%) create mode 100644 src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs create mode 100644 src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs create mode 100644 src/Ryujinx.Horizon/LibHacResultExtensions.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index f73dea1d9..a71837cab 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -328,7 +328,7 @@ namespace Ryujinx.HLE.HOS private void StartNewServices() { ServiceTable = new ServiceTable(); - var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices)); + var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient)); foreach (var service in services) { diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs deleted file mode 100644 index 1437a8e1f..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs +++ /dev/null @@ -1,85 +0,0 @@ -using LibHac; -using LibHac.Common; -using Ryujinx.Common; -using Ryujinx.HLE.HOS.Services.Arp; -using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator; - -namespace Ryujinx.HLE.HOS.Services.Bcat -{ - [Service("bcat:a", "bcat:a")] - [Service("bcat:m", "bcat:m")] - [Service("bcat:u", "bcat:u")] - [Service("bcat:s", "bcat:s")] - class IServiceCreator : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _base; - - public IServiceCreator(ServiceCtx context, string serviceName) - { - var applicationClient = context.Device.System.LibHacHorizonManager.ApplicationClient; - applicationClient.Sm.GetService(ref _base, serviceName).ThrowIfFailure(); - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - - [CommandCmif(0)] - // CreateBcatService(pid) -> object<nn::bcat::detail::ipc::IBcatService> - public ResultCode CreateBcatService(ServiceCtx context) - { - // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId. - // Add an instance of nn::bcat::detail::service::core::PassphraseManager. - // Add an instance of nn::bcat::detail::service::ServiceMemoryManager. - // Add an instance of nn::bcat::detail::service::core::TaskManager who load "bcat-sys:/" system save data and open "dc/task.bin". - // If the file don't exist, create a new one (size of 0x800) and write 2 empty struct with a size of 0x400. - - MakeObject(context, new IBcatService(ApplicationLaunchProperty.GetByPid(context))); - - // NOTE: If the IBcatService is null this error is returned, Doesn't occur in our case. - // return ResultCode.NullObject; - - return ResultCode.Success; - } - - [CommandCmif(1)] - // CreateDeliveryCacheStorageService(pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> - public ResultCode CreateDeliveryCacheStorageService(ServiceCtx context) - { - ulong pid = context.RequestData.ReadUInt64(); - - using var serv = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - - Result rc = _base.Get.CreateDeliveryCacheStorageService(ref serv.Ref, pid); - - if (rc.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheStorageService(context, ref serv.Ref)); - } - - return (ResultCode)rc.Value; - } - - [CommandCmif(2)] - // CreateDeliveryCacheStorageServiceWithApplicationId(nn::ApplicationId) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService> - public ResultCode CreateDeliveryCacheStorageServiceWithApplicationId(ServiceCtx context) - { - ApplicationId applicationId = context.RequestData.ReadStruct<ApplicationId>(); - - using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); - - Result rc = _base.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref service.Ref, applicationId); - - if (rc.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheStorageService(context, ref service.Ref)); - } - - return (ResultCode)rc.Value; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs deleted file mode 100644 index 7f1b313e1..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ResultCode.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Bcat -{ - enum ResultCode - { - ModuleId = 122, - ErrorCodeShift = 9, - - Success = 0, - - InvalidArgument = (1 << ErrorCodeShift) | ModuleId, - NotFound = (2 << ErrorCodeShift) | ModuleId, - TargetLocked = (3 << ErrorCodeShift) | ModuleId, - TargetAlreadyMounted = (4 << ErrorCodeShift) | ModuleId, - TargetNotMounted = (5 << ErrorCodeShift) | ModuleId, - AlreadyOpen = (6 << ErrorCodeShift) | ModuleId, - NotOpen = (7 << ErrorCodeShift) | ModuleId, - InternetRequestDenied = (8 << ErrorCodeShift) | ModuleId, - ServiceOpenLimitReached = (9 << ErrorCodeShift) | ModuleId, - SaveDataNotFound = (10 << ErrorCodeShift) | ModuleId, - NetworkServiceAccountNotAvailable = (31 << ErrorCodeShift) | ModuleId, - PassphrasePathNotFound = (80 << ErrorCodeShift) | ModuleId, - DataVerificationFailed = (81 << ErrorCodeShift) | ModuleId, - PermissionDenied = (90 << ErrorCodeShift) | ModuleId, - AllocationFailed = (91 << ErrorCodeShift) | ModuleId, - InvalidOperation = (98 << ErrorCodeShift) | ModuleId, - InvalidDeliveryCacheStorageFile = (204 << ErrorCodeShift) | ModuleId, - StorageOpenLimitReached = (205 << ErrorCodeShift) | ModuleId - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs deleted file mode 100644 index fb11cedad..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IBcatService.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ryujinx.HLE.HOS.Services.Arp; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IBcatService : IpcService - { - public IBcatService(ApplicationLaunchProperty applicationLaunchProperty) { } - - [CommandCmif(10100)] - // RequestSyncDeliveryCache() -> object<nn::bcat::detail::ipc::IDeliveryCacheProgressService> - public ResultCode RequestSyncDeliveryCache(ServiceCtx context) - { - MakeObject(context, new IDeliveryCacheProgressService(context)); - - return ResultCode.Success; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs deleted file mode 100644 index 575449779..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs +++ /dev/null @@ -1,65 +0,0 @@ -using LibHac; -using LibHac.Bcat; -using LibHac.Common; -using Ryujinx.Common; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheDirectoryService : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _base; - - public IDeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> baseService) - { - _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref baseService); - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - - [CommandCmif(0)] - // Open(nn::bcat::DirectoryName) - public ResultCode Open(ServiceCtx context) - { - DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>(); - - Result result = _base.Get.Open(ref directoryName); - - return (ResultCode)result.Value; - } - - [CommandCmif(1)] - // Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>) - public ResultCode Read(ServiceCtx context) - { - ulong bufferAddress = context.Request.ReceiveBuff[0].Position; - ulong bufferLen = context.Request.ReceiveBuff[0].Size; - - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(region.Memory.Span)); - - context.ResponseData.Write(entriesRead); - - return (ResultCode)result.Value; - } - } - - [CommandCmif(2)] - // GetCount() -> u32 - public ResultCode GetCount(ServiceCtx context) - { - Result result = _base.Get.GetCount(out int count); - - context.ResponseData.Write(count); - - return (ResultCode)result.Value; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs deleted file mode 100644 index 5a9110e6a..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs +++ /dev/null @@ -1,78 +0,0 @@ -using LibHac; -using LibHac.Bcat; -using LibHac.Common; -using Ryujinx.Common; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheFileService : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _base; - - public IDeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> baseService) - { - _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref baseService); - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - - [CommandCmif(0)] - // Open(nn::bcat::DirectoryName, nn::bcat::FileName) - public ResultCode Open(ServiceCtx context) - { - DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>(); - FileName fileName = context.RequestData.ReadStruct<FileName>(); - - Result result = _base.Get.Open(ref directoryName, ref fileName); - - return (ResultCode)result.Value; - } - - [CommandCmif(1)] - // Read(u64) -> (u64, buffer<bytes, 6>) - public ResultCode Read(ServiceCtx context) - { - ulong bufferAddress = context.Request.ReceiveBuff[0].Position; - ulong bufferLen = context.Request.ReceiveBuff[0].Size; - - long offset = context.RequestData.ReadInt64(); - - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _base.Get.Read(out long bytesRead, offset, region.Memory.Span); - - context.ResponseData.Write(bytesRead); - - return (ResultCode)result.Value; - } - } - - [CommandCmif(2)] - // GetSize() -> u64 - public ResultCode GetSize(ServiceCtx context) - { - Result result = _base.Get.GetSize(out long size); - - context.ResponseData.Write(size); - - return (ResultCode)result.Value; - } - - [CommandCmif(3)] - // GetDigest() -> nn::bcat::Digest - public ResultCode GetDigest(ServiceCtx context) - { - Result result = _base.Get.GetDigest(out Digest digest); - - context.ResponseData.WriteStruct(digest); - - return (ResultCode)result.Value; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs deleted file mode 100644 index 1555f1707..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types; -using Ryujinx.Horizon.Common; -using System; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheProgressService : IpcService - { - private KEvent _event; - private int _eventHandle; - - public IDeliveryCacheProgressService(ServiceCtx context) - { - _event = new KEvent(context.Device.System.KernelContext); - } - - [CommandCmif(0)] - // GetEvent() -> handle<copy> - public ResultCode GetEvent(ServiceCtx context) - { - if (_eventHandle == 0) - { - if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle); - - Logger.Stub?.PrintStub(LogClass.ServiceBcat); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // GetImpl() -> buffer<nn::bcat::detail::DeliveryCacheProgressImpl, 0x1a> - public ResultCode GetImpl(ServiceCtx context) - { - DeliveryCacheProgressImpl deliveryCacheProgress = new DeliveryCacheProgressImpl - { - State = DeliveryCacheProgressImpl.Status.Done, - Result = 0 - }; - - ulong dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress); - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(dcpSize); - - Logger.Stub?.PrintStub(LogClass.ServiceBcat); - - return ResultCode.Success; - } - - private ulong WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress) - { - return MemoryHelper.Write(context.Memory, ipcDesc.Position, deliveryCacheProgress); - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs deleted file mode 100644 index be77226cb..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs +++ /dev/null @@ -1,74 +0,0 @@ -using LibHac; -using LibHac.Bcat; -using LibHac.Common; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator -{ - class IDeliveryCacheStorageService : DisposableIpcService - { - private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _base; - - public IDeliveryCacheStorageService(ServiceCtx context, ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> baseService) - { - _base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref baseService); - } - - [CommandCmif(0)] - // CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService> - public ResultCode CreateFileService(ServiceCtx context) - { - using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); - - Result result = _base.Get.CreateFileService(ref service.Ref); - - if (result.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheFileService(ref service.Ref)); - } - - return (ResultCode)result.Value; - } - - [CommandCmif(1)] - // CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService> - public ResultCode CreateDirectoryService(ServiceCtx context) - { - using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); - - Result result = _base.Get.CreateDirectoryService(ref service.Ref); - - if (result.IsSuccess()) - { - MakeObject(context, new IDeliveryCacheDirectoryService(ref service.Ref)); - } - - return (ResultCode)result.Value; - } - - [CommandCmif(10)] - // EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>) - public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context) - { - ulong bufferAddress = context.Request.ReceiveBuff[0].Position; - ulong bufferLen = context.Request.ReceiveBuff[0].Size; - - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(region.Memory.Span)); - - context.ResponseData.Write(count); - - return (ResultCode)result.Value; - } - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _base.Destroy(); - } - } - } -} diff --git a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs index 8f4c37f70..284df4820 100644 --- a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs +++ b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -145,7 +145,7 @@ namespace Ryujinx.Horizon.Generators.Hipc if (bufferFixedSize != null) { - arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})"; + arg = $"new CommandArg({bufferFlags} | HipcBufferFlags.FixedSize, {bufferFixedSize})"; } else { diff --git a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs new file mode 100644 index 000000000..4a5378af2 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs @@ -0,0 +1,48 @@ +using Ryujinx.Horizon.Bcat.Ipc; +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Bcat +{ + internal class BcatIpcServer + { + private const int BcatMaxSessionsCount = 8; + private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4; + + private const int PointerBufferSize = 0x400; + private const int MaxDomains = 64; + private const int MaxDomainObjects = 64; + private const int MaxPortsCount = 4; + + private SmApi _sm; + private BcatServerManager _serverManager; + + private static readonly ManagerOptions _bcatManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + internal void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount); + + _serverManager.RegisterServer((int)BcatPortIndex.Admin, ServiceName.Encode("bcat:a"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.User, ServiceName.Encode("bcat:u"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.System, ServiceName.Encode("bcat:s"), BcatMaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatMain.cs b/src/Ryujinx.Horizon/Bcat/BcatMain.cs new file mode 100644 index 000000000..d4166fb18 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatMain.cs @@ -0,0 +1,24 @@ +using Ryujinx.Horizon.LogManager; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ryujinx.Horizon.Bcat +{ + internal class BcatMain : IService + { + public static void Main(ServiceTable serviceTable) + { + BcatIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatResult.cs b/src/Ryujinx.Horizon/Bcat/BcatResult.cs new file mode 100644 index 000000000..014c52e7c --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatResult.cs @@ -0,0 +1,29 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Bcat +{ + class BcatResult + { + private const int ModuleId = 122; + + public static Result Success => new(ModuleId, 0); + public static Result InvalidArgument => new(ModuleId, 1); + public static Result NotFound => new(ModuleId, 2); + public static Result TargetLocked => new(ModuleId, 3); + public static Result TargetAlreadyMounted => new(ModuleId, 4); + public static Result TargetNotMounted => new(ModuleId, 5); + public static Result AlreadyOpen => new(ModuleId, 6); + public static Result NotOpen => new(ModuleId, 7); + public static Result InternetRequestDenied => new(ModuleId, 8); + public static Result ServiceOpenLimitReached => new(ModuleId, 9); + public static Result SaveDataNotFound => new(ModuleId, 10); + public static Result NetworkServiceAccountNotAvailable => new(ModuleId, 31); + public static Result PassphrasePathNotFound => new(ModuleId, 80); + public static Result DataVerificationFailed => new(ModuleId, 81); + public static Result PermissionDenied => new(ModuleId, 90); + public static Result AllocationFailed => new(ModuleId, 91); + public static Result InvalidOperation => new(ModuleId, 98); + public static Result InvalidDeliveryCacheStorageFile => new(ModuleId, 204); + public static Result StorageOpenLimitReached => new(ModuleId, 205); + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs new file mode 100644 index 000000000..f02eafd9d --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs @@ -0,0 +1,28 @@ +using Ryujinx.Horizon.Bcat.Ipc; +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using System; + +namespace Ryujinx.Horizon.Bcat +{ + class BcatServerManager : ServerManager + { + public BcatServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) + { + } + + protected override Result OnNeedsToAccept(int portIndex, Server server) + { + return (BcatPortIndex)portIndex switch + { + BcatPortIndex.Admin => AcceptImpl(server, new ServiceCreator("bcat:a", BcatServicePermissionLevel.Admin)), + BcatPortIndex.Manager => AcceptImpl(server, new ServiceCreator("bcat:m", BcatServicePermissionLevel.Manager)), + BcatPortIndex.User => AcceptImpl(server, new ServiceCreator("bcat:u", BcatServicePermissionLevel.User)), + BcatPortIndex.System => AcceptImpl(server, new ServiceCreator("bcat:s", BcatServicePermissionLevel.System)), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + }; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs new file mode 100644 index 000000000..7dfa419d6 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs @@ -0,0 +1,85 @@ +using LibHac.Common; +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using System; +using System.Threading; +using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class ServiceCreator : IServiceCreator, IDisposable + { + private readonly BcatServicePermissionLevel _permissionLevel; + private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _libHacService; + + private int _disposalState; + + public ServiceCreator(string serviceName, BcatServicePermissionLevel permissionLevel) + { + HorizonStatic.Options.BcatClient.Sm.GetService(ref _libHacService, serviceName).ThrowIfFailure(); + _permissionLevel = permissionLevel; + } + + [CmifCommand(0)] + public Result CreateBcatService(out IBcatService bcatService, [ClientProcessId] ulong pid) + { + // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId. + // Add an instance of nn::bcat::detail::service::core::PassphraseManager. + // Add an instance of nn::bcat::detail::service::ServiceMemoryManager. + // Add an instance of nn::bcat::detail::service::core::TaskManager who loads "bcat-sys:/" system save data and opens "dc/task.bin". + // If the file don't exist, create a new one (with a size of 0x800 bytes) and write 2 empty structs with a size of 0x400 bytes. + + bcatService = new BcatService(_permissionLevel); + + return Result.Success; + } + + [CmifCommand(1)] + public Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, [ClientProcessId] ulong pid) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + var resultCode = _libHacService.Get.CreateDeliveryCacheStorageService(ref libHacService.Ref, pid); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheStorageService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(2)] + public Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, ApplicationId applicationId) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + var resultCode = _libHacService.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref libHacService.Ref, new LibHac.ApplicationId(applicationId.Id)); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheStorageService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs new file mode 100644 index 000000000..e82f597e9 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs @@ -0,0 +1,25 @@ +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class BcatService : IBcatService + { + private readonly BcatServicePermissionLevel _permissionLevel; + + public BcatService(BcatServicePermissionLevel permissionLevel) + { + _permissionLevel = permissionLevel; + } + + [CmifCommand(10100)] + public Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService) + { + deliveryCacheProgressService = new DeliveryCacheProgressService(); + + return Result.Success; + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs new file mode 100644 index 000000000..dd13eefb4 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs @@ -0,0 +1,48 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _libHacService; + private int _disposalState; + + public DeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result Open(DirectoryName directoryName) + { + return _libHacService.Get.Open(ref directoryName).ToHorizonResult(); + } + + [CmifCommand(1)] + public Result Read(out int entriesRead, [Buffer(Sdk.Sf.Hipc.HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeliveryCacheDirectoryEntry> entriesBuffer) + { + return _libHacService.Get.Read(out entriesRead, entriesBuffer).ToHorizonResult(); + } + + [CmifCommand(2)] + public Result GetCount(out int count) + { + return _libHacService.Get.GetCount(out count).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs new file mode 100644 index 000000000..d23f5f41f --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs @@ -0,0 +1,54 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheFileService : IDeliveryCacheFileService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _libHacService; + private int _disposalState; + + public DeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result Open(DirectoryName directoryName, FileName fileName) + { + return _libHacService.Get.Open(ref directoryName, ref fileName).ToHorizonResult(); + } + + [CmifCommand(1)] + public Result Read(long offset, out long bytesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> data) + { + return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult(); + } + + [CmifCommand(2)] + public Result GetSize(out long size) + { + return _libHacService.Get.GetSize(out size).ToHorizonResult(); + } + + [CmifCommand(3)] + public Result GetDigest(out Digest digest) + { + return _libHacService.Get.GetDigest(out digest).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs new file mode 100644 index 000000000..91aa26867 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs @@ -0,0 +1,58 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Bcat.Ipc.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheProgressService : IDeliveryCacheProgressService, IDisposable + { + private int _handle; + private SystemEventType _systemEvent; + private int _disposalState; + + [CmifCommand(0)] + public Result GetEvent([CopyHandle] out int handle) + { + if (_handle == 0) + { + Os.CreateSystemEvent(out _systemEvent, EventClearMode.ManualClear, true).AbortOnFailure(); + + _handle = Os.GetReadableHandleOfSystemEvent(ref _systemEvent); + } + + handle = _handle; + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return Result.Success; + } + + [CmifCommand(1)] + public Result GetImpl([Buffer(HipcBufferFlags.Out | HipcBufferFlags.Pointer, 0x200)] out DeliveryCacheProgressImpl deliveryCacheProgressImpl) + { + deliveryCacheProgressImpl = new DeliveryCacheProgressImpl + { + State = DeliveryCacheProgressImpl.Status.Done, + Result = 0 + }; + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return Result.Success; + } + + public void Dispose() + { + if (_handle != 0 && Interlocked.Exchange(ref _disposalState, 1) == 0) + { + Os.DestroySystemEvent(ref _systemEvent); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs new file mode 100644 index 000000000..9507a8a0e --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs @@ -0,0 +1,74 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheStorageService : IDeliveryCacheStorageService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _libHacService; + private int _disposalState; + + public DeliveryCacheStorageService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result CreateFileService(out IDeliveryCacheFileService service) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); + + var resultCode = _libHacService.Get.CreateFileService(ref libHacService.Ref); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheFileService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(1)] + public Result CreateDirectoryService(out IDeliveryCacheDirectoryService service) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); + + var resultCode = _libHacService.Get.CreateDirectoryService(ref libHacService.Ref); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheDirectoryService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(10)] + public Result EnumerateDeliveryCacheDirectory(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DirectoryName> directoryNames) + { + return _libHacService.Get.EnumerateDeliveryCacheDirectory(out count, directoryNames).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs similarity index 86% rename from src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs rename to src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs index fb9a67be6..10e0b54fe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/Types/DeliveryCacheProgressImpl.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types +namespace Ryujinx.Horizon.Bcat.Ipc.Types { [StructLayout(LayoutKind.Sequential, Size = 0x200)] public struct DeliveryCacheProgressImpl diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs new file mode 100644 index 000000000..e448dfdcd --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Bcat.Types +{ + enum BcatPortIndex + { + Admin, + Manager, + User, + System + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs new file mode 100644 index 000000000..54d7461a7 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Bcat.Types +{ + enum BcatServicePermissionLevel + { + Admin = -1, + User = 1, + System = 2, + Manager = 6 + } +} \ No newline at end of file diff --git a/src/Ryujinx.Horizon/HorizonOptions.cs b/src/Ryujinx.Horizon/HorizonOptions.cs index 6d580e8b9..1ec56bfa7 100644 --- a/src/Ryujinx.Horizon/HorizonOptions.cs +++ b/src/Ryujinx.Horizon/HorizonOptions.cs @@ -1,3 +1,5 @@ +using LibHac; + namespace Ryujinx.Horizon { public struct HorizonOptions @@ -5,10 +7,13 @@ namespace Ryujinx.Horizon public bool IgnoreMissingServices { get; } public bool ThrowOnInvalidCommandIds { get; } - public HorizonOptions(bool ignoreMissingServices) + public HorizonClient BcatClient { get; } + + public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient) { IgnoreMissingServices = ignoreMissingServices; ThrowOnInvalidCommandIds = true; + BcatClient = bcatClient; } } } diff --git a/src/Ryujinx.Horizon/LibHacResultExtensions.cs b/src/Ryujinx.Horizon/LibHacResultExtensions.cs new file mode 100644 index 000000000..01d181640 --- /dev/null +++ b/src/Ryujinx.Horizon/LibHacResultExtensions.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon +{ + internal static class LibHacResultExtensions + { + public static Result ToHorizonResult(this LibHac.Result result) + { + return new Result((int)result.Module, (int)result.Description); + } + } +} diff --git a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs index ba19ff6f6..b80399eaa 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs @@ -9,12 +9,12 @@ namespace Ryujinx.Horizon.Prepo private const int PrepoMaxSessionsCount = 12; private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6; - private const int PointerBufferSize = 0x3800; + private const int PointerBufferSize = 0x80; private const int MaxDomains = 64; private const int MaxDomainObjects = 16; private const int MaxPortsCount = 6; - private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + private static readonly ManagerOptions _prepoManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); private SmApi _sm; private PrepoServerManager _serverManager; @@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Prepo _sm = new SmApi(); _sm.Initialize().AbortOnFailure(); - _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, PrepoTotalMaxSessionsCount); + _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _prepoManagerOptions, PrepoTotalMaxSessionsCount); _serverManager.RegisterServer((int)PrepoPortIndex.Admin, ServiceName.Encode("prepo:a"), PrepoMaxSessionsCount); // 1.0.0-5.1.0 _serverManager.RegisterServer((int)PrepoPortIndex.Admin2, ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+ diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs new file mode 100644 index 000000000..f6c8e6dd3 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IBcatService.cs @@ -0,0 +1,10 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IBcatService : IServiceObject + { + Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs new file mode 100644 index 000000000..23a740c35 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheDirectoryService.cs @@ -0,0 +1,14 @@ +using LibHac.Bcat; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheDirectoryService : IServiceObject + { + Result GetCount(out int count); + Result Open(DirectoryName directoryName); + Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entriesBuffer); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs new file mode 100644 index 000000000..a1e5d43ba --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheFileService.cs @@ -0,0 +1,15 @@ +using LibHac.Bcat; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheFileService : IServiceObject + { + Result GetDigest(out Digest digest); + Result GetSize(out long size); + Result Open(DirectoryName directoryName, FileName fileName); + Result Read(long offset, out long bytesRead, Span<byte> data); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs new file mode 100644 index 000000000..07a796f88 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheProgressService.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Bcat.Ipc.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheProgressService : IServiceObject + { + Result GetEvent(out int handle); + Result GetImpl(out DeliveryCacheProgressImpl deliveryCacheProgressImpl); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs new file mode 100644 index 000000000..5638f074c --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IDeliveryCacheStorageService.cs @@ -0,0 +1,14 @@ +using LibHac.Bcat; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IDeliveryCacheStorageService : IServiceObject + { + Result CreateDirectoryService(out IDeliveryCacheDirectoryService service); + Result CreateFileService(out IDeliveryCacheFileService service); + Result EnumerateDeliveryCacheDirectory(out int count, Span<DirectoryName> directoryNames); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs new file mode 100644 index 000000000..edc525900 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Bcat +{ + internal interface IServiceCreator : IServiceObject + { + Result CreateBcatService(out IBcatService service, ulong pid); + Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, ulong pid); + Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, Ncm.ApplicationId applicationId); + } +} diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs index 2edc6ea10..d97457d92 100644 --- a/src/Ryujinx.Horizon/ServiceTable.cs +++ b/src/Ryujinx.Horizon/ServiceTable.cs @@ -1,3 +1,4 @@ +using Ryujinx.Horizon.Bcat; using Ryujinx.Horizon.LogManager; using Ryujinx.Horizon.Prepo; using System.Collections.Generic; @@ -23,6 +24,7 @@ namespace Ryujinx.Horizon RegisterService<LmMain>(); RegisterService<PrepoMain>(); + RegisterService<BcatMain>(); _totalServices = entries.Count; From a7c6e6a8cf40a54b0b374cbd65078a8d9ed9a0f5 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan <aaron.omullan@gmail.com> Date: Tue, 9 May 2023 22:31:52 -0300 Subject: [PATCH 515/737] fix(mvk): resumeLostDevice (#4800) Command buffer errors currently trigger an exception "DeviceLost" crashing the process. Looking at [MKV's code](https://github.com/KhronosGroup/MoltenVK/blob/53a4eb26f2fbd7322eb087eb4af263fe466543b0/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm#L392-L408) we observe that: - It hard fails if error is: ``` MTLCommandBufferErrorBlacklisted || MTLCommandBufferErrorNotPermitted || MTLCommandBufferErrorDeviceRemoved ``` - Otherwise fails conditionally if `config.resumeLostDevice == false` (current default) For Ryujinx's use-case it's more graceful to resume on those errors rather than crashing the app, the error isn't totally silenced since `mvk` still logs it Fixes #4704, #4575 --- src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs index 5910d1aac..b5d88dde6 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE; config.SynchronousQueueSubmits = false; + config.ResumeLostDevice = true; + vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize); } } From 0a0675a7f610354c17b2b642738b6fbd2dfc91ba Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 10 May 2023 21:29:17 -0300 Subject: [PATCH 516/737] Fix missing domain service object dispose (#4879) --- src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs index 62ee27380..f789b6c0c 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs @@ -165,6 +165,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif entry.Owner = null; obj = entry.Obj; + + if (obj.ServiceObject is IDisposable disposableObj) + { + disposableObj.Dispose(); + } + entry.Obj = null; _entries.Remove(entry.Node); entry.Node = null; From ba71141bdcb4742ee8707b9c354365d9ea142ca9 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 10 May 2023 21:46:38 -0300 Subject: [PATCH 517/737] Ensure background translation threads exited before disposing JIT (#4874) --- src/ARMeilleure/Translation/Translator.cs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index f349c5ebf..234be2ac7 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -54,6 +54,7 @@ namespace ARMeilleure.Translation internal TranslatorQueue Queue { get; } internal IMemoryManager Memory { get; } + private Thread[] _backgroundTranslationThreads; private volatile int _threadCount; // FIXME: Remove this once the init logic of the emulator will be redone. @@ -127,18 +128,22 @@ namespace ARMeilleure.Translation int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3); int threadCount = Math.Min(4, unboundedThreadCount); + Thread[] backgroundTranslationThreads = new Thread[threadCount]; + for (int i = 0; i < threadCount; i++) { bool last = i != 0 && i == unboundedThreadCount - 1; - Thread backgroundTranslatorThread = new Thread(BackgroundTranslate) + backgroundTranslationThreads[i] = new Thread(BackgroundTranslate) { Name = "CPU.BackgroundTranslatorThread." + i, Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal }; - backgroundTranslatorThread.Start(); + backgroundTranslationThreads[i].Start(); } + + Interlocked.Exchange(ref _backgroundTranslationThreads, backgroundTranslationThreads); } Statistics.InitializeTimer(); @@ -162,9 +167,20 @@ namespace ARMeilleure.Translation if (Interlocked.Decrement(ref _threadCount) == 0) { + Queue.Dispose(); + + Thread[] backgroundTranslationThreads = Interlocked.Exchange(ref _backgroundTranslationThreads, null); + + if (backgroundTranslationThreads != null) + { + foreach (Thread thread in backgroundTranslationThreads) + { + thread.Join(); + } + } + ClearJitCache(); - Queue.Dispose(); Stubs.Dispose(); FunctionTable.Dispose(); CountTable.Dispose(); From 9095941fd143a6b81478371d5ab3daa29d2ab119 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 11 May 2023 17:36:53 +0200 Subject: [PATCH 518/737] Update release workflow & Add jobs for macOS (#4837) * Add build config and extra args to create_macos_build.sh * Use matrix strategy for releases * Add macOS jobs Co-authored-by: Mary <thog@protonmail.com> * Fix wrong version argument * Fix check for the correct amount of args * Install latest rcodesign release Co-authored-by: Mary <thog@protonmail.com> * Set executable bits for PR builds on linux --------- Co-authored-by: Mary <thog@protonmail.com> --- .github/workflows/build.yml | 76 +++++- .github/workflows/release.yml | 243 ++++++++++++------ ...macos_release.sh => create_macos_build.sh} | 28 +- 3 files changed, 248 insertions(+), 99 deletions(-) rename distribution/macos/{create_macos_release.sh => create_macos_build.sh} (80%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 843dd492a..8bc55ec3e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,9 +18,14 @@ on: - '*.yml' - 'README.md' +env: + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + RYUJINX_BASE_VERSION: "1.1.0" + jobs: build: - name: ${{ matrix.os }} (${{ matrix.configuration }}) + name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} strategy: matrix: @@ -43,47 +48,106 @@ jobs: RELEASE_ZIP_OS_NAME: win_x64 fail-fast: false - env: - POWERSHELL_TELEMETRY_OPTOUT: 1 - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - RYUJINX_BASE_VERSION: "1.1.0" steps: - uses: actions/checkout@v3 + - uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + global-json-file: global.json + - name: Get git short hash id: git_short_hash run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT shell: bash + - name: Build run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER + - name: Test run: dotnet test --no-build -c "${{ matrix.configuration }}" + - name: Publish Ryujinx run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true if: github.event_name == 'pull_request' + - name: Publish Ryujinx.Headless.SDL2 run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true if: github.event_name == 'pull_request' + - name: Publish Ryujinx.Ava run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true if: github.event_name == 'pull_request' + + - name: Set executable bit + run: | + chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh + chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh + chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh + if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest' + - name: Upload Ryujinx artifact uses: actions/upload-artifact@v3 with: name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish if: github.event_name == 'pull_request' + - name: Upload Ryujinx.Headless.SDL2 artifact uses: actions/upload-artifact@v3 with: name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_sdl2_headless if: github.event_name == 'pull_request' + - name: Upload Ryujinx.Ava artifact uses: actions/upload-artifact@v3 with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_ava + if: github.event_name == 'pull_request' + + build_macos: + name: MacOS universal (${{ matrix.configuration }}) + runs-on: ubuntu-latest + strategy: + matrix: + configuration: [ Debug, Release ] + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-dotnet@v3 + with: + global-json-file: global.json + + - name: Setup LLVM 14 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 14 + + - name: Install rcodesign + run: | + mkdir -p $HOME/.bin + gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz' + tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1 + rm apple-codesign.tar.gz + mv rcodesign $HOME/.bin/ + echo "$HOME/.bin" >> $GITHUB_PATH + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get git short hash + id: git_short_hash + run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT + + - name: Publish macOS + run: | + ./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER" + + - name: Upload Ryujinx.Ava artifact + uses: actions/upload-artifact@v3 + with: + name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal + path: "publish_ava/*.tar.gz" if: github.event_name == 'pull_request' \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 914e5e33e..8d0d0e860 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,95 +22,15 @@ env: RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master" jobs: - release: - runs-on: windows-latest + tag: + name: Create tag + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-dotnet@v3 - with: - global-json-file: global.json - name: Get version info id: version_info run: | echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT - echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT shell: bash - - name: Configure for release - run: | - sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs - shell: bash - - name: Create output dir - run: "mkdir release_output" - - name: Publish Windows - run: | - dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true - dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true - dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true - - name: Packing Windows builds - run: | - pushd publish_windows - 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish - popd - - pushd publish_windows_sdl2_headless - 7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish - popd - - pushd publish_windows_ava - 7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish - popd - shell: bash - - - name: Publish Linux - run: | - dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true - dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true - dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true - - - name: Packing Linux builds - run: | - pushd publish_linux - tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish - python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx" - python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh" - gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz - rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar - popd - - pushd publish_linux_sdl2_headless - tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish - python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2" - python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh" - gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz - rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar - popd - - pushd publish_linux_ava - tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish - python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava" - python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh" - gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz - rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar - popd - shell: bash - - - name: Pushing new release - uses: ncipollo/release-action@v1 - with: - name: ${{ steps.version_info.outputs.build_version }} - artifacts: "release_output/*.tar.gz,release_output/*.zip" - tag: ${{ steps.version_info.outputs.build_version }} - body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." - allowUpdates: true - removeArtifacts: true - replacesArtifacts: true - owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} - repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} - token: ${{ secrets.RELEASE_TOKEN }} - name: Create tag uses: actions/github-script@v5 @@ -123,6 +43,163 @@ jobs: sha: context.sha }) + release: + name: Release ${{ matrix.OS_NAME }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, windows-latest ] + include: + - os: ubuntu-latest + OS_NAME: Linux x64 + DOTNET_RUNTIME_IDENTIFIER: linux-x64 + RELEASE_ZIP_OS_NAME: linux_x64 + + - os: windows-latest + OS_NAME: Windows x64 + DOTNET_RUNTIME_IDENTIFIER: win10-x64 + RELEASE_ZIP_OS_NAME: win_x64 + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-dotnet@v3 + with: + global-json-file: global.json + + - name: Get version info + id: version_info + run: | + echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT + shell: bash + + - name: Configure for release + run: | + sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + shell: bash + + - name: Create output dir + run: "mkdir release_output" + + - name: Publish + run: | + dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_gtk/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true + dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true + dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true + + - name: Packing Windows builds + if: matrix.os == "windows-latest" + run: | + pushd publish_gtk + 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish + popd + + pushd publish_sdl2_headless + 7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish + popd + + pushd publish_ava + 7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish + popd + shell: bash + + - name: Packing Linux builds + if: matrix.os == "ubuntu-latest" + run: | + pushd publish_gtk + chmod +x publish/Ryujinx.sh publish/Ryujinx + tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish + popd + + pushd publish_sdl2_headless + chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2 + tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish + popd + + pushd publish_ava + chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava + tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish + popd + shell: bash + + - name: Pushing new release + uses: ncipollo/release-action@v1 + with: + name: ${{ steps.version_info.outputs.build_version }} + artifacts: "release_output/*.tar.gz,release_output/*.zip" + tag: ${{ steps.version_info.outputs.build_version }} + body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." + omitBodyDuringUpdate: true + allowUpdates: true + replacesArtifacts: true + owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} + repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} + token: ${{ secrets.RELEASE_TOKEN }} + + macos_release: + name: Release MacOS universal + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-dotnet@v3 + with: + global-json-file: global.json + + - name: Setup LLVM 14 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 14 + + - name: Install rcodesign + run: | + mkdir -p $HOME/.bin + gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz' + tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1 + rm apple-codesign.tar.gz + mv rcodesign $HOME/.bin/ + echo "$HOME/.bin" >> $GITHUB_PATH + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get version info + id: version_info + run: | + echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT + + - name: Configure for release + run: | + sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + shell: bash + + - name: Publish macOS + run: | + ./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release + + - name: Pushing new release + uses: ncipollo/release-action@v1 + with: + name: ${{ steps.version_info.outputs.build_version }} + artifacts: "publish_ava/*.tar.gz" + tag: ${{ steps.version_info.outputs.build_version }} + body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." + omitBodyDuringUpdate: true + allowUpdates: true + replacesArtifacts: true + owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} + repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} + token: ${{ secrets.RELEASE_TOKEN }} + flatpak_release: uses: ./.github/workflows/flatpak.yml needs: release diff --git a/distribution/macos/create_macos_release.sh b/distribution/macos/create_macos_build.sh similarity index 80% rename from distribution/macos/create_macos_release.sh rename to distribution/macos/create_macos_build.sh index 59eb1efd2..1a5a38738 100755 --- a/distribution/macos/create_macos_release.sh +++ b/distribution/macos/create_macos_build.sh @@ -2,8 +2,8 @@ set -e -if [ "$#" -ne 6 ]; then - echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID>" +if [ "$#" -lt 7 ]; then + echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <EXTRA_ARGS>" exit 1 fi @@ -17,8 +17,16 @@ OUTPUT_DIRECTORY=$(readlink -f "$3") ENTITLEMENTS_FILE_PATH=$(readlink -f "$4") VERSION=$5 SOURCE_REVISION_ID=$6 +CONFIGURATION=$7 +EXTRA_ARGS=$8 + +if [ "$VERSION" == "1.1.0" ]; +then + RELEASE_TAR_FILE_NAME=Ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar +else + RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar +fi -RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app" X64_APP_BUNDLE="$TEMP_DIRECTORY/output_x64/Ryujinx.app" UNIVERSAL_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app" @@ -27,12 +35,12 @@ EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx rm -rf "$TEMP_DIRECTORY" mkdir -p "$TEMP_DIRECTORY" -DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true" +DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true $EXTRA_ARGS" dotnet restore -dotnet build -c Release src/Ryujinx.Ava -dotnet publish -c Release -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava -dotnet publish -c Release -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava +dotnet build -c $CONFIGURATION src/Ryujinx.Ava +dotnet publish -c $CONFIGURATION -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava +dotnet publish -c $CONFIGURATION -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava # Get rid of the support library for ARMeilleure for x64 (that's only for arm64) rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib" @@ -68,7 +76,7 @@ else LIPO=lipo fi -# Make it the executable universal +# Make the executable universal $LIPO "$ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" "$X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -create # Patch up the Info.plist to have appropriate version @@ -87,10 +95,10 @@ then # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" - echo "Usign rcodesign for ad-hoc signing" + echo "Using rcodesign for ad-hoc signing" rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE" else - echo "Usign codesign for ad-hoc signing" + echo "Using codesign for ad-hoc signing" codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE" fi From 3d429958223068ae64051829bb402b3f1471585f Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Thu, 11 May 2023 17:42:33 +0200 Subject: [PATCH 519/737] Attempt to fix release.yml after merge --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8d0d0e860..363e9cadd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,7 +92,7 @@ jobs: dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true - name: Packing Windows builds - if: matrix.os == "windows-latest" + if: matrix.os == 'windows-latest' run: | pushd publish_gtk 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish @@ -108,7 +108,7 @@ jobs: shell: bash - name: Packing Linux builds - if: matrix.os == "ubuntu-latest" + if: matrix.os == 'ubuntu-latest' run: | pushd publish_gtk chmod +x publish/Ryujinx.sh publish/Ryujinx @@ -205,4 +205,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit \ No newline at end of file + secrets: inherit From 95bad6995c110a662c8059e4f8035426303c0840 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Thu, 11 May 2023 17:41:34 +0100 Subject: [PATCH 520/737] GPU: Fix shader cache assuming past shader data was mapped (#4885) This fixes a potential issue where a shader lookup could match the address of a previous _different_ shader, but that shader is now partially unmapped. This would just crash with an invalid region exception. To compare a shader in the address cache with one in memory, we get the memory at the location with the previous shader's size. However, it's possible it has been unmapped and then remapped with a smaller size. In this case, we should just get back the mapped portion of the shader, which will then fail the comparison immediately and get to compile/lookup for the new one. This might fix a random crash in TOTK that was reported by Piplup. I don't know if it does, because I don't have the game yet. --- src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index e1ab93278..d543f42ad 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -591,7 +591,7 @@ namespace Ryujinx.Graphics.Gpu.Shader return true; } - ReadOnlySpan<byte> memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length); + ReadOnlySpan<byte> memoryCode = memoryManager.GetSpanMapped(gpuVa, shader.Code.Length); return memoryCode.SequenceEqual(shader.Code); } From 42f7f986661ef7ba65996e56d8448cb8f8985515 Mon Sep 17 00:00:00 2001 From: 2435043xia <363032601@qq.com> Date: Fri, 12 May 2023 01:10:24 +0800 Subject: [PATCH 521/737] =?UTF-8?q?Fix=20the=20issue=20of=20unequal=20chec?= =?UTF-8?q?k=20for=20amiibo=20file=20date=20due=20to=20the=20lack=20o?= =?UTF-8?q?=E2=80=A6=20(#4832)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix the issue of unequal check for amiibo file date due to the lack of sub-second units in the header, causing slow opening of the amiibo interface. * Supplement the unrepaired. --- src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs | 2 +- src/Ryujinx/Ui/Windows/AmiiboWindow.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index bb92798fa..ead1a1442 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -370,7 +370,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (response.IsSuccessStatusCode) { - return response.Content.Headers.LastModified != oldLastModified; + return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero); } return false; diff --git a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs index 11a566d8d..9cfb29427 100644 --- a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -180,7 +180,7 @@ namespace Ryujinx.Ui.Windows if (response.IsSuccessStatusCode) { - return response.Content.Headers.LastModified != oldLastModified; + return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero); } return false; From ec0bb74968de92230dd026faa35e5f8bd975ec35 Mon Sep 17 00:00:00 2001 From: John <loneboco@gmail.com> Date: Thu, 11 May 2023 11:13:01 -0700 Subject: [PATCH 522/737] Stop SDL from inhibiting sleep. (#4842) --- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 970e287de..c41c6269d 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -63,6 +63,7 @@ namespace Ryujinx.SDL2.Common SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); // NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them. From 40d47b7aa235b464974480d09875eef0377bb261 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Thu, 11 May 2023 20:14:02 +0200 Subject: [PATCH 523/737] amadeus: Fix wrong channel mapping check and an old typo (#4888) * amadeus: Fix wrong channel mapping check This was always going to happens, as a result quadratic would break and move index after the channel count point, effectively breaking input/output indices. * amadeus: Fix reverb 3d early delay wrong output index --- src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs | 4 ++-- src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs | 6 +++--- src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs | 4 ++-- src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index cb5678c7b..6dc766594 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -49,8 +49,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]); } - DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices); - DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices); + DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount); + DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices, Parameter.ChannelCount); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs index eeb645673..74b53b24b 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs @@ -66,8 +66,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour // TODO: Update reverb 3d processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping. - DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices); - DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices); + DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount); + DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices, Parameter.ChannelCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -116,7 +116,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command for (int i = 0; i < targetEarlyDelayLineIndicesTable.Length; i++) { int earlyDelayIndex = targetEarlyDelayLineIndicesTable[i]; - int outputIndex = outputEarlyIndicesTable[i]; + int outputIndex = outputEarlyIndicesTable[earlyDelayIndex]; float tempTapOut = state.PreDelayLine.TapUnsafe(state.EarlyDelayTime[earlyDelayIndex], delayLineSampleIndexOffset); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs index 0a32a065d..cd08b838a 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs @@ -71,8 +71,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour // TODO: Update reverb processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping. - DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices); - DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices); + DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount); + DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices, Parameter.ChannelCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs index 5ca1ddbac..721830c9a 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs @@ -430,9 +430,9 @@ namespace Ryujinx.Audio.Renderer.Dsp } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span<ushort> bufferIndices) + public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span<ushort> bufferIndices, uint channelCount) { - if (!isSupported && bufferIndices.Length == 6) + if (!isSupported && channelCount == 6) { ushort backLeft = bufferIndices[2]; ushort backRight = bufferIndices[3]; @@ -447,9 +447,9 @@ namespace Ryujinx.Audio.Renderer.Dsp } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span<ushort> bufferIndices) + public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span<ushort> bufferIndices, uint channelCount) { - if (isSupported && bufferIndices.Length == 6) + if (isSupported && channelCount == 6) { ushort frontCenter = bufferIndices[2]; ushort lowFrequency = bufferIndices[3]; From 0ed40c71754e194c42a5a2758c0db1a5fc1de50b Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 11 May 2023 15:47:55 -0300 Subject: [PATCH 524/737] Fix incorrect ASTC endpoint color when using LuminanceDelta mode (#4890) --- src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index 08738583e..80683d17b 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -956,7 +956,7 @@ namespace Ryujinx.Graphics.Texture.Astc { Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); - int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU); + int l1 = (int)Math.Min(l0 + (val[1] & 0x3F), 0xFFU); endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0); endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1); From 920507759052b1a3a04c598d46c9b20ce0988d99 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 11 May 2023 16:35:36 -0300 Subject: [PATCH 525/737] Enable explicit LOD for array textures with depth compare on SPIR-V (#4892) --- src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index b6ffdb7ac..5521fa5c9 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -1442,14 +1442,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GetZeroOperationResult(context, texOp, AggregateType.FP32, colorIsVector); } - // This combination is valid, but not available on GLSL. - // For now, ignore the LOD level and do a normal sample. - // TODO: How to implement it properly? - if (hasLodLevel && isArray && isShadow) - { - hasLodLevel = false; - } - int srcIndex = isBindless ? 1 : 0; SpvInstruction Src(AggregateType type) From aa784c3e5e900e376e0f5ccea87458905108485e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 May 2023 21:42:46 +0200 Subject: [PATCH 526/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.30.0 to 6.30.1 (#4886) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.30.0 to 6.30.1. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.30.0...6.30.1) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 4759c9ba5..f123ab7f0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> - <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.0" /> + <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.1" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> From e0544dd9c74e86cca6ee37c4d521d2d07f9ccdcc Mon Sep 17 00:00:00 2001 From: Nico <Nico.Reinartz@rwth-aachen.de> Date: Thu, 11 May 2023 23:10:57 +0200 Subject: [PATCH 527/737] UI: Adjust input mapping view (#4866) * refactor: clean up controller settings ui - Remove inconsistencies between left and right side - Use style to set ToggleButton properties (since they are all the same) - Move topmost controller settings from one line to 2x2 grid for improved clarity - Properly adjust borders, text widths, etc. to neighboring elements to eliminate misaligned visual lines * fix: merge issues * fix: prevent sliders from jumping by giving text block fixed width * refactor: add more separators and increase margin * refactor: center deadzone and range descriptions * refactor: move rumble border top margin to -1 and prevent double border * refactor: remove margins & double borders + switch profile & input selection * style: apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../UI/Windows/ControllerSettingsWindow.axaml | 715 ++++++++---------- 1 file changed, 334 insertions(+), 381 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml index 8a4d22ffa..1a11a8ff3 100644 --- a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml @@ -21,35 +21,54 @@ <UserControl.Resources> <helpers:KeyValueConverter x:Key="Key" /> </UserControl.Resources> + <UserControl.Styles> + <Style Selector="ToggleButton"> + <Setter Property="Width" Value="90" /> + <Setter Property="Height" Value="27" /> + <Setter Property="HorizontalAlignment" Value="Stretch" /> + </Style> + </UserControl.Styles> + <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical"> - <Grid Margin="2,2,2,5" HorizontalAlignment="Stretch"> + <Grid Margin="0,2,0,5" HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> - <ColumnDefinition Width="0.5*" /> - <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + + <!-- Player selection --> <Border + Grid.Row="0" Grid.Column="0" - Margin="0,0,2,0" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - Padding="2,0"> - <StackPanel + Padding="2"> + <Grid Margin="2" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Orientation="Vertical"> + HorizontalAlignment="Stretch" + VerticalAlignment="Center"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> <TextBlock - Margin="0,0,0,4" - HorizontalAlignment="Center" + Margin="5,0,10,0" + Width="90" + HorizontalAlignment="Left" + VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsPlayer}" /> <ComboBox + Grid.Column="1" Name="PlayerIndexBox" - Width="150" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" SelectionChanged="PlayerIndexBox_OnSelectionChanged" Items="{Binding PlayerIndexes}" SelectedIndex="{Binding PlayerId}"> @@ -59,69 +78,147 @@ </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> - </StackPanel> + </Grid> </Border> - - <!-- Main Controller Settings --> + <!-- Profile selection --> <Border - Grid.Column="1" - Margin="0,0,2,0" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - Padding="2,0"> - <StackPanel - Margin="2" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - Orientation="Vertical"> - <TextBlock - Margin="0,0,0,5" - HorizontalAlignment="Center" - Text="{locale:Locale ControllerSettingsInputDevice}" /> - <Grid HorizontalAlignment="Stretch"> - <Grid.ColumnDefinitions> - <ColumnDefinition /> - <ColumnDefinition Width="Auto" /> - </Grid.ColumnDefinitions> - <ComboBox - Name="DeviceBox" - HorizontalAlignment="Stretch" - Items="{Binding DeviceList}" - SelectedIndex="{Binding Device}" /> - <Button - Grid.Column="1" - MinWidth="0" - Margin="5,0,0,0" - VerticalAlignment="Center" - Command="{Binding LoadDevices}"> - <ui:SymbolIcon - Symbol="Refresh" - FontSize="15" - Height="20" /> - </Button> - </Grid> - </StackPanel> - </Border> - <Border - Grid.Column="2" - Margin="0,0,2,0" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - Padding="2,0"> + Grid.Row="1" + Grid.Column="0" + Margin="0,-1,0,0" + BorderBrush="{DynamicResource ThemeControlBorderColor}" + BorderThickness="1" + Padding="2"> <Grid Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Center"> - <Grid.RowDefinitions> - <RowDefinition /> - <RowDefinition /> - </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> <TextBlock - Margin="0,0,0,4" - HorizontalAlignment="Center" + Margin="5,0,10,0" + Width="90" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsProfile}" /> + <ui:ComboBox + Grid.Column="1" + IsEditable="True" + Name="ProfileBox" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + SelectedIndex="0" + Items="{Binding ProfilesList}" + Text="{Binding ProfileName}" /> + <Button + Grid.Column="2" + MinWidth="0" + Margin="5,0,0,0" + VerticalAlignment="Center" + ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}" + Command="{Binding LoadProfile}"> + <ui:SymbolIcon + Symbol="Upload" + FontSize="15" + Height="20" /> + </Button> + <Button + Grid.Column="3" + MinWidth="0" + Margin="5,0,0,0" + VerticalAlignment="Center" + ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}" + Command="{Binding SaveProfile}"> + <ui:SymbolIcon + Symbol="Save" + FontSize="15" + Height="20" /> + </Button> + <Button + Grid.Column="4" + MinWidth="0" + Margin="5,0,0,0" + VerticalAlignment="Center" + ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}" + Command="{Binding RemoveProfile}"> + <ui:SymbolIcon + Symbol="Delete" + FontSize="15" + Height="20" /> + </Button> + </Grid> + </Border> + + <!-- Input device --> + <Border + Grid.Row="0" + Grid.Column="1" + Margin="-1,0,0,0" + BorderBrush="{DynamicResource ThemeControlBorderColor}" + BorderThickness="1" + Padding="2"> + <Grid Margin="2" HorizontalAlignment="Stretch"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*"/> + <ColumnDefinition Width="Auto" /> + </Grid.ColumnDefinitions> + <TextBlock + Grid.Column="0" + Margin="5,0,10,0" + Width="90" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsInputDevice}" /> + <ComboBox + Grid.Column="1" + Name="DeviceBox" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Items="{Binding DeviceList}" + SelectedIndex="{Binding Device}" /> + <Button + Grid.Column="2" + MinWidth="0" + Margin="5,0,0,0" + VerticalAlignment="Center" + Command="{Binding LoadDevices}"> + <ui:SymbolIcon + Symbol="Refresh" + FontSize="15" + Height="20"/> + </Button> + </Grid> + </Border> + + <!-- Controler type --> + <Border + Grid.Row="1" + Grid.Column="1" + Margin="-1,-1,0,0" + BorderBrush="{DynamicResource ThemeControlBorderColor}" + BorderThickness="1" + Padding="2"> + <Grid + Margin="2" + HorizontalAlignment="Stretch" + VerticalAlignment="Center"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock + Margin="5,0,10,0" + Width="90" + HorizontalAlignment="Left" + VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsControllerType}" /> <ComboBox - Grid.Row="1" + Grid.Column="1" HorizontalAlignment="Stretch" Items="{Binding Controllers}" SelectedIndex="{Binding Controller}"> @@ -133,64 +230,6 @@ </ComboBox> </Grid> </Border> - <Border - Grid.Column="3" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - Padding="2,0" > - <StackPanel - Margin="2" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Orientation="Vertical"> - <TextBlock - Margin="0,0,0,4" - HorizontalAlignment="Center" - Text="{locale:Locale ControllerSettingsProfile}" /> - <StackPanel Orientation="Horizontal"> - <ui:ComboBox - IsEditable="True" - Name="ProfileBox" - Width="100" - SelectedIndex="0" - Items="{Binding ProfilesList}" - Text="{Binding ProfileName}" /> - <Button - MinWidth="0" - Margin="5,0,0,0" - VerticalAlignment="Center" - ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}" - Command="{Binding LoadProfile}"> - <ui:SymbolIcon - Symbol="Upload" - FontSize="15" - Height="20" /> - </Button> - <Button - MinWidth="0" - Margin="5,0,0,0" - VerticalAlignment="Center" - ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}" - Command="{Binding SaveProfile}"> - <ui:SymbolIcon - Symbol="Save" - FontSize="15" - Height="20" /> - </Button> - <Button - MinWidth="0" - Margin="5,0,0,0" - VerticalAlignment="Center" - ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}" - Command="{Binding RemoveProfile}"> - <ui:SymbolIcon - Symbol="Delete" - FontSize="15" - Height="20" /> - </Button> - </StackPanel> - </StackPanel> - </Border> </Grid> <!-- Button / JoyStick Settings --> @@ -206,7 +245,7 @@ <!-- Left --> <Grid - Margin="0,0,10,0" + Margin="0,0,5,0" Grid.Column="0" VerticalAlignment="Stretch" DockPanel.Dock="Left"> @@ -221,7 +260,8 @@ Grid.Row="0" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsLeft}"> + IsVisible="{Binding IsLeft}" + MinHeight="90"> <Grid Margin="10" HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition /> @@ -232,7 +272,6 @@ <RowDefinition /> </Grid.RowDefinitions> <StackPanel - Margin="0,0,0,4" Grid.Column="0" Grid.Row="0" Background="{DynamicResource ThemeDarkColor}" @@ -243,10 +282,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerZL}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.ButtonZl, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -263,19 +299,15 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerL}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.ButtonL, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> <StackPanel - Margin="0,0,0,4" Grid.Column="1" - Grid.Row="0" + Grid.Row="1" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock @@ -284,10 +316,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsButtonMinus}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.ButtonMinus, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -301,7 +330,8 @@ Grid.Row="1" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsLeft}"> + IsVisible="{Binding IsLeft}" + Margin="0,5,0,0"> <StackPanel Margin="10" Orientation="Vertical"> <TextBlock Margin="0,0,0,10" @@ -320,10 +350,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickButton}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -339,10 +366,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickUp}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftStickUp, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -358,10 +382,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickDown}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftStickDown, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -377,10 +398,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickLeft}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -396,10 +414,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickRight}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftStickRight, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -419,10 +434,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickButton}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -438,16 +450,13 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickStick}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch" - Tag="stick"> + <ToggleButton Tag="stick"> <TextBlock Text="{Binding Configuration.LeftJoystick, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> + <Separator Margin="0,8,0,8" Height="1" /> <CheckBox IsChecked="{Binding Configuration.LeftInvertStickX}"> <TextBlock Text="{locale:Locale ControllerSettingsLStickInvertXAxis}" /> </CheckBox> @@ -457,9 +466,11 @@ <CheckBox IsChecked="{Binding Configuration.LeftRotate90}"> <TextBlock Text="{locale:Locale ControllerSettingsRotate90}" /> </CheckBox> - <Separator Margin="0,4,0,4" Height="1" /> + <Separator Margin="0,8,0,8" Height="1" /> <StackPanel Orientation="Vertical"> - <TextBlock Text="{locale:Locale ControllerSettingsLStickDeadzone}" /> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsLStickDeadzone}" /> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" @@ -473,9 +484,12 @@ Value="{Binding Configuration.DeadzoneLeft, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" + Width="25" Text="{Binding Configuration.DeadzoneLeft, StringFormat=\{0:0.00\}}" /> </StackPanel> - <TextBlock Text="{locale:Locale ControllerSettingsStickRange}" /> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsStickRange}" /> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" @@ -489,6 +503,7 @@ Value="{Binding Configuration.RangeLeft, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" + Width="25" Text="{Binding Configuration.RangeLeft, StringFormat=\{0:0.00\}}" /> </StackPanel> </StackPanel> @@ -502,7 +517,8 @@ BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" VerticalAlignment="Top" - IsVisible="{Binding IsLeft}"> + IsVisible="{Binding IsLeft}" + Margin="0,5,0,0"> <StackPanel Margin="10" Orientation="Vertical"> <TextBlock Margin="0,0,0,10" @@ -519,8 +535,6 @@ Text="{locale:Locale ControllerSettingsDPadUp}" TextAlignment="Center" /> <ToggleButton - Width="90" - Height="27" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Configuration.DpadUp, Mode=TwoWay, Converter={StaticResource Key}}" @@ -538,8 +552,6 @@ Text="{locale:Locale ControllerSettingsDPadDown}" TextAlignment="Center" /> <ToggleButton - Width="90" - Height="27" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Configuration.DpadDown, Mode=TwoWay, Converter={StaticResource Key}}" @@ -557,8 +569,6 @@ Text="{locale:Locale ControllerSettingsDPadLeft}" TextAlignment="Center" /> <ToggleButton - Width="90" - Height="27" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Configuration.DpadLeft, Mode=TwoWay, Converter={StaticResource Key}}" @@ -576,8 +586,6 @@ Text="{locale:Locale ControllerSettingsDPadRight}" TextAlignment="Center" /> <ToggleButton - Width="90" - Height="27" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Configuration.DpadRight, Mode=TwoWay, Converter={StaticResource Key}}" @@ -590,11 +598,11 @@ </Grid> <!-- Triggers And Side Buttons--> - <StackPanel Grid.Column="1" HorizontalAlignment="Stretch"> + <StackPanel Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Border BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1"> - <StackPanel Margin="10" Orientation="Vertical"> + BorderThickness="1" MinHeight="90"> + <StackPanel Margin="8" Orientation="Vertical"> <TextBlock HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerThreshold}" /> <StackPanel HorizontalAlignment="Center" Orientation="Horizontal"> <Slider @@ -604,7 +612,9 @@ IsSnapToTickEnabled="True" Minimum="0" Value="{Binding Configuration.TriggerThreshold, Mode=TwoWay}" /> - <TextBlock Text="{Binding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" /> + <TextBlock + Width="25" + Text="{Binding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" /> </StackPanel> <StackPanel Margin="0,4,0,0" @@ -619,10 +629,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLeftSR}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -641,10 +648,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLeftSL}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.LeftButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -663,10 +667,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRightSR}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -685,10 +686,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRightSL}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -702,14 +700,15 @@ Margin="0,10,0,0" MaxHeight="250" HorizontalAlignment="Stretch" - VerticalAlignment="Top" + VerticalAlignment="Stretch" Source="{Binding Image}" /> <!-- Motion+Rumble --> - <StackPanel Margin="0,10,0,0" Orientation="Vertical" > + <StackPanel Margin="0,10,0,0" Orientation="Vertical" VerticalAlignment="Bottom"> <Border BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" + VerticalAlignment="Bottom" HorizontalAlignment="Stretch" IsVisible="{Binding IsController}"> <Grid> @@ -733,7 +732,8 @@ BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" HorizontalAlignment="Stretch" - IsVisible="{Binding IsController}"> + IsVisible="{Binding IsController}" + Margin="0,-1,0,0"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> @@ -756,7 +756,7 @@ <!--Right Controls--> <Grid - Margin="10,0,0,0" + Margin="5,0,0,0" Grid.Column="2" VerticalAlignment="Top" HorizontalAlignment="Stretch" > @@ -770,165 +770,99 @@ Grid.Row="0" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsRight}"> - <StackPanel Margin="10" Orientation="Vertical"> - <Grid HorizontalAlignment="Stretch"> - <Grid.ColumnDefinitions> - <ColumnDefinition /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition /> - <RowDefinition /> - </Grid.RowDefinitions> - <StackPanel - Margin="0,0,0,4" - Grid.Column="1" - Grid.Row="0" - Background="{DynamicResource ThemeDarkColor}" - Orientation="Horizontal"> - <TextBlock - Width="20" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsTriggerZR}" - TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> - <TextBlock - Text="{Binding Configuration.ButtonZr, Mode=TwoWay, Converter={StaticResource Key}}" - TextAlignment="Center" /> - </ToggleButton> - </StackPanel> - <StackPanel - Grid.Column="1" - Grid.Row="1" + IsVisible="{Binding IsRight}" + MinHeight="90"> + <Grid Margin="10" HorizontalAlignment="Stretch"> + <Grid.ColumnDefinitions> + <ColumnDefinition /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition /> + <RowDefinition /> + </Grid.RowDefinitions> + <StackPanel + Grid.Column="1" + Grid.Row="0" + Background="{DynamicResource ThemeDarkColor}" + Orientation="Horizontal"> + <TextBlock + Width="20" HorizontalAlignment="Center" VerticalAlignment="Center" - Background="{DynamicResource ThemeDarkColor}" - Orientation="Horizontal"> + Text="{locale:Locale ControllerSettingsTriggerZR}" + TextAlignment="Center" /> + <ToggleButton> <TextBlock - Width="20" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsTriggerR}" + Text="{Binding Configuration.ButtonZr, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> - <TextBlock - Text="{Binding Configuration.ButtonR, Mode=TwoWay, Converter={StaticResource Key}}" - TextAlignment="Center" /> - </ToggleButton> - </StackPanel> - <StackPanel - Margin="0,0,8,4" - Grid.Column="0" - Grid.Row="0" - HorizontalAlignment="Right" + </ToggleButton> + </StackPanel> + <StackPanel + Grid.Column="1" + Grid.Row="1" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Background="{DynamicResource ThemeDarkColor}" + Orientation="Horizontal"> + <TextBlock + Width="20" + HorizontalAlignment="Center" VerticalAlignment="Center" - Background="{DynamicResource ThemeDarkColor}" - Orientation="Horizontal"> + Text="{locale:Locale ControllerSettingsTriggerR}" + TextAlignment="Center" /> + <ToggleButton> <TextBlock - Width="20" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsButtonPlus}" + Text="{Binding Configuration.ButtonR, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> - <TextBlock - Text="{Binding Configuration.ButtonPlus, Mode=TwoWay, Converter={StaticResource Key}}" - TextAlignment="Center" /> - </ToggleButton> - </StackPanel> - </Grid> - </StackPanel> + </ToggleButton> + </StackPanel> + <StackPanel + Grid.Column="0" + Grid.Row="1" + HorizontalAlignment="Right" + VerticalAlignment="Center" + Background="{DynamicResource ThemeDarkColor}" + Orientation="Horizontal"> + <TextBlock + Width="20" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsButtonPlus}" + TextAlignment="Center" /> + <ToggleButton> + <TextBlock + Text="{Binding Configuration.ButtonPlus, Mode=TwoWay, Converter={StaticResource Key}}" + TextAlignment="Center" /> + </ToggleButton> + </StackPanel> + </Grid> </Border> <Border Grid.Row="1" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsRight}"> + IsVisible="{Binding IsRight}" + Margin="0,5,0,0"> <StackPanel Margin="10" Orientation="Vertical"> <TextBlock Margin="0,0,0,10" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsButtons}" /> - <Grid HorizontalAlignment="Stretch"> - <Grid.ColumnDefinitions> - <ColumnDefinition /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition /> - <RowDefinition /> - </Grid.RowDefinitions> - <!-- Right Buttons X --> - <StackPanel - Margin="0,0,0,4" - Grid.Column="0" - Grid.Row="0" - Background="{DynamicResource ThemeDarkColor}" - Orientation="Horizontal"> - <TextBlock - Width="20" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsButtonX}" - TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> - <TextBlock - Text="{Binding Configuration.ButtonX, Mode=TwoWay, Converter={StaticResource Key}}" - TextAlignment="Center" /> - </ToggleButton> - </StackPanel> - <!-- Right Buttons Y --> - <StackPanel - Grid.Column="0" - Grid.Row="1" - Background="{DynamicResource ThemeDarkColor}" - Orientation="Horizontal"> - <TextBlock - Width="20" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsButtonY}" - TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> - <TextBlock - Text="{Binding Configuration.ButtonY, Mode=TwoWay, Converter={StaticResource Key}}" - TextAlignment="Center" /> - </ToggleButton> - </StackPanel> + <StackPanel Orientation="Vertical"> <!-- Right Buttons A --> <StackPanel Margin="0,0,0,4" - Grid.Column="1" - Grid.Row="0" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock - Width="20" + Width="120" + Margin="0,0,10,0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsButtonA}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.ButtonA, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -936,26 +870,59 @@ </StackPanel> <!-- Right Buttons B --> <StackPanel - Grid.Column="1" - Grid.Row="1" + Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock - Width="20" + Width="120" + Margin="0,0,10,0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsButtonB}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.ButtonB, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - </Grid> + <!-- Right Buttons X --> + <StackPanel + Margin="0,0,0,4" + Background="{DynamicResource ThemeDarkColor}" + Orientation="Horizontal"> + <TextBlock + Width="120" + Margin="0,0,10,0" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsButtonX}" + TextAlignment="Center" /> + <ToggleButton> + <TextBlock + Text="{Binding Configuration.ButtonX, Mode=TwoWay, Converter={StaticResource Key}}" + TextAlignment="Center" /> + </ToggleButton> + </StackPanel> + <!-- Right Buttons Y --> + <StackPanel + Margin="0,0,0,4" + Background="{DynamicResource ThemeDarkColor}" + Orientation="Horizontal"> + <TextBlock + Width="120" + Margin="0,0,10,0" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsButtonY}" + TextAlignment="Center" /> + <ToggleButton> + <TextBlock + Text="{Binding Configuration.ButtonY, Mode=TwoWay, Converter={StaticResource Key}}" + TextAlignment="Center" /> + </ToggleButton> + </StackPanel> + </StackPanel> </StackPanel> </Border> <Border @@ -963,7 +930,8 @@ Padding="10" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsRight}"> + IsVisible="{Binding IsRight}" + Margin="0,5,0,0"> <StackPanel Orientation="Vertical"> <TextBlock Margin="0,0,0,10" @@ -982,10 +950,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickButton}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -1001,10 +966,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickUp}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightStickUp, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -1020,10 +982,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickDown}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightStickDown, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -1039,10 +998,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickLeft}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -1058,10 +1014,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickRight}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightStickRight, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -1081,10 +1034,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickButton}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock Text="{Binding Configuration.RightControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> @@ -1101,16 +1051,13 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickStick}" TextAlignment="Center" /> - <ToggleButton - Width="90" - Height="27" - HorizontalAlignment="Stretch" - Tag="stick"> + <ToggleButton Tag="stick"> <TextBlock Text="{Binding Configuration.RightJoystick, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> + <Separator Margin="0,8,0,8" Height="1" /> <CheckBox IsChecked="{Binding Configuration.RightInvertStickX}"> <TextBlock Text="{locale:Locale ControllerSettingsRStickInvertXAxis}" /> </CheckBox> @@ -1120,9 +1067,11 @@ <CheckBox IsChecked="{Binding Configuration.RightRotate90}"> <TextBlock Text="{locale:Locale ControllerSettingsRotate90}" /> </CheckBox> - <Separator Margin="0,4,0,4" Height="1" /> + <Separator Margin="0,8,0,8" Height="1" /> <StackPanel Orientation="Vertical"> - <TextBlock Text="{locale:Locale ControllerSettingsRStickDeadzone}" /> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsRStickDeadzone}" /> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" @@ -1138,9 +1087,12 @@ Value="{Binding Configuration.DeadzoneRight, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" + Width="25" Text="{Binding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" /> </StackPanel> - <TextBlock Text="{locale:Locale ControllerSettingsStickRange}" /> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsStickRange}" /> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" @@ -1154,6 +1106,7 @@ Value="{Binding Configuration.RangeRight, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" + Width="25" Text="{Binding Configuration.RangeRight, StringFormat=\{0:0.00\}}" /> </StackPanel> </StackPanel> From 5cbdfbc7a4b7413a4f633c77190a79bfc6520e98 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Fri, 12 May 2023 00:19:19 +0200 Subject: [PATCH 528/737] amadeus: Allow 5.1 sink output (#4894) * amadeus: Allow 5.1 sink output Also add a simple Stereo to 5.1 change for device sink. Tested against NES - Nintendo Switch Online that output stereo on the audio renderer. * Remove outdated comment --- src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs | 4 +--- .../Renderer/Dsp/Command/DeviceSinkCommand.cs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs index 7bd0443c2..899c2ef97 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs @@ -65,9 +65,7 @@ namespace Ryujinx.Audio.Renderer.Dsp { OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax]; - // TODO: Before enabling this, we need up-mixing from stereo to 5.1. - // uint channelCount = GetHardwareChannelCount(deviceDriver); - uint channelCount = 2; + uint channelCount = GetHardwareChannelCount(deviceDriver); for (int i = 0; i < OutputDevices.Length; i++) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs index 9c88a4e7f..27bb34bf3 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs @@ -67,7 +67,19 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command const int sampleCount = Constants.TargetSampleCount; - short[] outputBuffer = new short[bufferCount * sampleCount]; + uint inputCount; + + // In case of upmixing to 5.1, we allocate the right amount. + if (bufferCount != channelCount && channelCount == 6) + { + inputCount = (uint)channelCount; + } + else + { + inputCount = bufferCount; + } + + short[] outputBuffer = new short[inputCount * sampleCount]; for (int i = 0; i < bufferCount; i++) { @@ -79,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } - device.AppendBuffer(outputBuffer, InputCount); + device.AppendBuffer(outputBuffer, inputCount); } else { From 531da8a1c0760c8ebf121ac83ba4c840ead9e443 Mon Sep 17 00:00:00 2001 From: SamusAranX <hallo@emmalyx.site> Date: Fri, 12 May 2023 01:56:37 +0200 Subject: [PATCH 529/737] Changed LastPlayed field from string to nullable DateTime (#4861) * Changed LastPlayed field from string to nullable DateTime Added ApplicationData.LastPlayedString property Added NullableDateTimeConverter for the DateTime->string conversion in Avalonia * Added migration from string-based last_played to DateTime-based last_played_utc * Updated comment style * Added MarkupExtension to NullableDateTimeConverter and changed its usage Cleaned up leftover usings * Missed one comment --- src/Ryujinx.Ava/AppHost.cs | 2 +- .../UI/Controls/ApplicationListView.axaml | 2 +- .../UI/Helpers/NullableDateTimeConverter.cs | 38 ++++++++++++++++ .../Models/Generic/LastPlayedSortComparer.cs | 15 +++---- .../UI/ViewModels/MainWindowViewModel.cs | 5 +-- src/Ryujinx.Ui.Common/App/ApplicationData.cs | 45 +++++++++++++------ .../App/ApplicationLibrary.cs | 23 ++++++---- .../App/ApplicationMetadata.cs | 13 +++++- src/Ryujinx/Ui/MainWindow.cs | 13 +++--- 9 files changed, 113 insertions(+), 43 deletions(-) create mode 100644 src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 0955fb270..795c3f7a7 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -671,7 +671,7 @@ namespace Ryujinx.Ava _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => { - appMetadata.LastPlayed = DateTime.UtcNow.ToString(); + appMetadata.LastPlayed = DateTime.UtcNow; }); return true; diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml index fa8ebf627..227b4723b 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml @@ -129,7 +129,7 @@ TextWrapping="Wrap" /> <TextBlock HorizontalAlignment="Stretch" - Text="{Binding LastPlayed}" + Text="{Binding LastPlayed, Converter={helpers:NullableDateTimeConverter}}" TextAlignment="Right" TextWrapping="Wrap" /> <TextBlock diff --git a/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs b/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs new file mode 100644 index 000000000..1d862de01 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs @@ -0,0 +1,38 @@ +using Avalonia.Data.Converters; +using Avalonia.Markup.Xaml; +using Ryujinx.Ava.Common.Locale; +using System; +using System.Globalization; + +namespace Ryujinx.Ava.UI.Helpers +{ + internal class NullableDateTimeConverter : MarkupExtension, IValueConverter + { + private static readonly NullableDateTimeConverter _instance = new(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return LocaleManager.Instance[LocaleKeys.Never]; + } + + if (value is DateTime dateTime) + { + return dateTime.ToLocalTime().ToString(culture); + } + + throw new NotSupportedException(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + return _instance; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs b/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs index 98caceb50..3627ada9a 100644 --- a/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs +++ b/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs @@ -1,4 +1,3 @@ -using Ryujinx.Ava.Common.Locale; using Ryujinx.Ui.App.Common; using System; using System.Collections.Generic; @@ -14,20 +13,20 @@ namespace Ryujinx.Ava.UI.Models.Generic public int Compare(ApplicationData x, ApplicationData y) { - string aValue = x.LastPlayed; - string bValue = y.LastPlayed; + var aValue = x.LastPlayed; + var bValue = y.LastPlayed; - if (aValue == LocaleManager.Instance[LocaleKeys.Never]) + if (!aValue.HasValue) { - aValue = DateTime.UnixEpoch.ToString(); + aValue = DateTime.UnixEpoch; } - if (bValue == LocaleManager.Instance[LocaleKeys.Never]) + if (!bValue.HasValue) { - bValue = DateTime.UnixEpoch.ToString(); + bValue = DateTime.UnixEpoch; } - return (IsAscending ? 1 : -1) * DateTime.Compare(DateTime.Parse(bValue), DateTime.Parse(aValue)); + return (IsAscending ? 1 : -1) * DateTime.Compare(bValue.Value, aValue.Value); } } } \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 4db78afeb..f8dd41435 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1524,10 +1524,9 @@ namespace Ryujinx.Ava.UI.ViewModels { ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { - if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) + if (appMetadata.LastPlayed.HasValue) { - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - + double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); } }); diff --git a/src/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs index d9d3cf685..f0aa40be2 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationData.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationData.cs @@ -10,27 +10,44 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using System; +using System.Globalization; using System.IO; +using System.Text.Json.Serialization; namespace Ryujinx.Ui.App.Common { public class ApplicationData { - public bool Favorite { get; set; } - public byte[] Icon { get; set; } - public string TitleName { get; set; } - public string TitleId { get; set; } - public string Developer { get; set; } - public string Version { get; set; } - public string TimePlayed { get; set; } - public double TimePlayedNum { get; set; } - public string LastPlayed { get; set; } - public string FileExtension { get; set; } - public string FileSize { get; set; } - public double FileSizeBytes { get; set; } - public string Path { get; set; } + public bool Favorite { get; set; } + public byte[] Icon { get; set; } + public string TitleName { get; set; } + public string TitleId { get; set; } + public string Developer { get; set; } + public string Version { get; set; } + public string TimePlayed { get; set; } + public double TimePlayedNum { get; set; } + public DateTime? LastPlayed { get; set; } + public string FileExtension { get; set; } + public string FileSize { get; set; } + public double FileSizeBytes { get; set; } + public string Path { get; set; } public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } - + + [JsonIgnore] + public string LastPlayedString + { + get + { + if (!LastPlayed.HasValue) + { + // TODO: maybe put localized string here instead of just "Never" + return "Never"; + } + + return LastPlayed.Value.ToLocalTime().ToString(CultureInfo.CurrentCulture); + } + } + public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath) { using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index b7b57f1a2..0407036a0 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -414,21 +414,28 @@ namespace Ryujinx.Ui.App.Common ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata => { appMetadata.Title = titleName; - }); - if (appMetadata.LastPlayed != "Never") - { - if (!DateTime.TryParse(appMetadata.LastPlayed, out _)) + if (appMetadata.LastPlayedOld == default || appMetadata.LastPlayed.HasValue) { - Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); + // Don't do the migration if last_played doesn't exist or last_played_utc already has a value. + return; + } - appMetadata.LastPlayed = "Never"; + // Migrate from string-based last_played to DateTime-based last_played_utc. + if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed)) + { + Logger.Info?.Print(LogClass.Application, $"last_played found: \"{appMetadata.LastPlayedOld}\", migrating to last_played_utc"); + appMetadata.LastPlayed = lastPlayedOldParsed; + + // Migration successful: deleting last_played from the metadata file. + appMetadata.LastPlayedOld = default; } else { - appMetadata.LastPlayed = appMetadata.LastPlayed[..^3]; + // Migration failed: emitting warning but leaving the unparsable value in the metadata file so the user can fix it. + Logger.Warning?.Print(LogClass.Application, $"Last played string \"{appMetadata.LastPlayedOld}\" is invalid for current system culture, skipping (did current culture change?)"); } - } + }); ApplicationData data = new() { diff --git a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs index e19f7483f..0abd4680d 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs @@ -1,10 +1,19 @@ -namespace Ryujinx.Ui.App.Common +using System; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.App.Common { public class ApplicationMetadata { public string Title { get; set; } public bool Favorite { get; set; } public double TimePlayed { get; set; } - public string LastPlayed { get; set; } = "Never"; + + [JsonPropertyName("last_played_utc")] + public DateTime? LastPlayed { get; set; } = null; + + [JsonPropertyName("last_played")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string LastPlayedOld { get; set; } } } \ No newline at end of file diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index f4cb3d072..7cae62227 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -876,7 +876,7 @@ namespace Ryujinx.Ui _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => { - appMetadata.LastPlayed = DateTime.UtcNow.ToString(); + appMetadata.LastPlayed = DateTime.UtcNow; }); } } @@ -1019,10 +1019,11 @@ namespace Ryujinx.Ui { _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { - DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed); - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - - appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + if (appMetadata.LastPlayed.HasValue) + { + double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; + appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + } }); } } @@ -1089,7 +1090,7 @@ namespace Ryujinx.Ui args.AppData.Developer, args.AppData.Version, args.AppData.TimePlayed, - args.AppData.LastPlayed, + args.AppData.LastPlayedString, args.AppData.FileExtension, args.AppData.FileSize, args.AppData.Path, From 49c63ea07779eb27674ae8c4a14e1dcf4b794a95 Mon Sep 17 00:00:00 2001 From: John <loneboco@gmail.com> Date: Thu, 11 May 2023 17:14:29 -0700 Subject: [PATCH 530/737] Fix the restart after an update. (#4869) * Fix the restart after an update. * Fix the updater for the Ava UI too. * Fixing up the code after some change requests. Removed a line of code that was accidentally left in. * Fix restarting on Linux Avalonia. * Fix issues with escaped arguments. --- src/Ryujinx.Ava/Modules/Updater/Updater.cs | 42 ++++++++++++++++----- src/Ryujinx/Modules/Updater/UpdateDialog.cs | 15 +++++++- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Ava/Modules/Updater/Updater.cs b/src/Ryujinx.Ava/Modules/Updater/Updater.cs index 054299351..77d77d794 100644 --- a/src/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/src/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -295,14 +295,7 @@ namespace Ryujinx.Modules if (shouldRestart) { List<string> arguments = CommandLineState.Arguments.ToList(); - string ryuName = Path.GetFileName(Environment.ProcessPath); string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; - string executablePath = Path.Combine(executableDirectory, ryuName); - - if (!Path.Exists(executablePath)) - { - executablePath = Path.Combine(executableDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); - } // On macOS we perform the update at relaunch. if (OperatingSystem.IsMacOS()) @@ -310,13 +303,42 @@ namespace Ryujinx.Modules string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app"); string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); - string currentPid = Process.GetCurrentProcess().Id.ToString(); + string currentPid = Environment.ProcessId.ToString(); - executablePath = "/bin/bash"; arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid }); + Process.Start("/bin/bash", arguments); + } + else + { + // Find the process name. + string ryuName = Path.GetFileName(Environment.ProcessPath); + + // Some operating systems can see the renamed executable, so strip off the .ryuold if found. + if (ryuName.EndsWith(".ryuold")) + { + ryuName = ryuName[..^7]; + } + + // Fallback if the executable could not be found. + if (!Path.Exists(Path.Combine(executableDirectory, ryuName))) + { + ryuName = OperatingSystem.IsWindows() ? "Ryujinx.Ava.exe" : "Ryujinx.Ava"; + } + + ProcessStartInfo processStart = new(ryuName) + { + UseShellExecute = true, + WorkingDirectory = executableDirectory + }; + + foreach (string argument in CommandLineState.Arguments) + { + processStart.ArgumentList.Add(argument); + } + + Process.Start(processStart); } - Process.Start(executablePath, arguments); Environment.Exit(0); } } diff --git a/src/Ryujinx/Modules/Updater/UpdateDialog.cs b/src/Ryujinx/Modules/Updater/UpdateDialog.cs index 4957b681b..e0a257fd6 100644 --- a/src/Ryujinx/Modules/Updater/UpdateDialog.cs +++ b/src/Ryujinx/Modules/Updater/UpdateDialog.cs @@ -1,5 +1,6 @@ using Gdk; using Gtk; +using Ryujinx.Common; using Ryujinx.Ui; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; @@ -47,9 +48,19 @@ namespace Ryujinx.Modules if (_restartQuery) { string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; - string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); - Process.Start(ryuExe, CommandLineState.Arguments); + ProcessStartInfo processStart = new(ryuName) + { + UseShellExecute = true, + WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory() + }; + + foreach (string argument in CommandLineState.Arguments) + { + processStart.ArgumentList.Add(argument); + } + + Process.Start(processStart); Environment.Exit(0); } From 95c06de4c16d292ad1cd962121762f83b9cce373 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Fri, 12 May 2023 01:30:47 +0100 Subject: [PATCH 531/737] GPU: Remove swizzle undefined matching and rework depth aliasing (#4896) * GPU: Remove swizzle undefined matching and rework depth aliasing @gdkchan pointed out that UI textures in TOTK seemed to be setting their texture swizzle incorrectly (texture was RGB but was sampling A, swizzle for A was wrong), so I determined that SwizzleComponentMatches was the problem and set on eliminating it. This PR combines existing work to select the most recently modified texture (now used when selecting which aliased texture to use) with some additional changes to remove the swizzle check and support aliased view creation. The original observation (#1538) was that we wanted to match depth textures for the purposes of aliasing with color textures, but they often had different swizzle from what was sampled (as it's generally the identity swizzle once rendered). At the time, I decided to allow swizzles to match if only the defined components matched, which fixed the issue in all known cases but could easily be broken by a game _expecting_ a given swizzle, such as a 1/0 value on a component. This error case could also occur in textures that don't even depth alias, such as R11G11B10, as the rule was created to generally apply to all cases. The solution is now to fail this exact match test, and allow the search for an R32 texture to create a swizzled view of a D32 texture (and other such cases). This allows the creation of a view that mismatches the requested format, which wasn't present before and was the reason for the swizzle matching approach. The exact match and view creation rules now follow the same rules over what textures to select when there are multiple options (such as a "perfect" match and an "aliased" match at the same time). It now selects the most recently modified texture, which is done with a new sequence number in the GpuContext (because we don't have enough of these). Reportedly fixes UI having weird coloured backgrounds in TOTK. This also fixes an issue in MK8D where returning from a race resulted in the character selection cubemaps being broken. May work around issues introduced by the "short texture cache" PR due to modification ordering, though they won't be truly fixed. Should allow (#4365) to avoid copies in more cases. Need to test that. I tested a bunch of games #1538 originally affected and they seem to be fine. This change affects all games so it would be good to get some wide testing on it. * Address feedback 1, fix an issue * Workaround: Do not allow copies for format alias. These should be removed when D32<->R32 copy dependencies become legal --- src/Ryujinx.Graphics.Gpu/GpuContext.cs | 11 +++ src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 6 +- .../Image/TextureCache.cs | 92 +++++++++++-------- .../Image/TextureCompatibility.cs | 78 +++------------- .../Image/TextureGroup.cs | 9 ++ src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs | 34 ++++++- .../Image/TextureViewCompatibility.cs | 1 + 7 files changed, 125 insertions(+), 106 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index bab62b952..ccaabf70f 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -98,6 +98,8 @@ namespace Ryujinx.Graphics.Gpu private Thread _gpuThread; private bool _pendingSync; + private long _modifiedSequence; + /// <summary> /// Creates a new instance of the GPU emulation context. /// </summary> @@ -200,6 +202,15 @@ namespace Ryujinx.Graphics.Gpu return divided * NsToTicksFractionNumerator + errorBias; } + /// <summary> + /// Gets a sequence number for resource modification ordering. This increments on each call. + /// </summary> + /// <returns>A sequence number for resource modification ordering</returns> + public long GetModifiedSequence() + { + return _modifiedSequence++; + } + /// <summary> /// Gets the value of the GPU timer. /// </summary> diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 0427d09b2..a7af1aad7 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1170,6 +1170,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="caps">Host GPU capabilities</param> /// <param name="firstLayer">Texture view initial layer on this texture</param> /// <param name="firstLevel">Texture view first mipmap level on this texture</param> + /// <param name="flags">Texture search flags</param> /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns> public TextureViewCompatibility IsViewCompatible( TextureInfo info, @@ -1178,11 +1179,12 @@ namespace Ryujinx.Graphics.Gpu.Image int layerSize, Capabilities caps, out int firstLayer, - out int firstLevel) + out int firstLevel, + TextureSearchFlags flags = TextureSearchFlags.None) { TextureViewCompatibility result = TextureViewCompatibility.Full; - result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps)); + result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps, flags)); if (result != TextureViewCompatibility.Incompatible) { result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps)); diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 49191239f..bccd3fd72 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -569,7 +569,7 @@ namespace Ryujinx.Graphics.Gpu.Image Texture texture = null; - TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch; + long bestSequence = 0; for (int index = 0; index < sameAddressOverlapsCount; index++) { @@ -601,17 +601,12 @@ namespace Ryujinx.Graphics.Gpu.Image continue; } } - } - if (matchQuality == TextureMatchQuality.Perfect) - { - texture = overlap; - break; - } - else if (matchQuality > bestQuality) - { - texture = overlap; - bestQuality = matchQuality; + if (texture == null || overlap.Group.ModifiedSequence - bestSequence > 0) + { + texture = overlap; + bestSequence = overlap.Group.ModifiedSequence; + } } } @@ -664,6 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image int fullyCompatible = 0; // Evaluate compatibility of overlaps, add temporary references + int preferredOverlap = -1; for (int index = 0; index < overlapsCount; index++) { @@ -675,17 +671,26 @@ namespace Ryujinx.Graphics.Gpu.Image sizeInfo.LayerSize, _context.Capabilities, out int firstLayer, - out int firstLevel); + out int firstLevel, + flags); - if (overlapCompatibility == TextureViewCompatibility.Full) + if (overlapCompatibility >= TextureViewCompatibility.FormatAlias) { if (overlap.IsView) { - overlapCompatibility = TextureViewCompatibility.CopyOnly; + overlapCompatibility = overlapCompatibility == TextureViewCompatibility.FormatAlias ? + TextureViewCompatibility.Incompatible : + TextureViewCompatibility.CopyOnly; } else { fullyCompatible++; + + if (preferredOverlap == -1 || overlap.Group.ModifiedSequence - bestSequence > 0) + { + preferredOverlap = index; + bestSequence = overlap.Group.ModifiedSequence; + } } } @@ -695,37 +700,50 @@ namespace Ryujinx.Graphics.Gpu.Image // Search through the overlaps to find a compatible view and establish any copy dependencies. - for (int index = 0; index < overlapsCount; index++) + if (preferredOverlap != -1) { - Texture overlap = _textureOverlaps[index]; - OverlapInfo oInfo = _overlapInfo[index]; + Texture overlap = _textureOverlaps[preferredOverlap]; + OverlapInfo oInfo = _overlapInfo[preferredOverlap]; - if (oInfo.Compatibility == TextureViewCompatibility.Full) + bool aliased = oInfo.Compatibility == TextureViewCompatibility.FormatAlias; + + if (!isSamplerTexture) { - if (!isSamplerTexture) - { - // If this is not a sampler texture, the size might be different from the requested size, - // so we need to make sure the texture information has the correct size for this base texture, - // before creating the view. - info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel); - } + // If this is not a sampler texture, the size might be different from the requested size, + // so we need to make sure the texture information has the correct size for this base texture, + // before creating the view. - texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); - texture.SynchronizeMemory(); - break; + info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel, aliased); } - else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0) + else if (aliased) { - // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead. + // The format must be changed to match the parent. + info = info.CreateInfoWithFormat(overlap.Info.FormatInfo); + } - texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode); + texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); + texture.SynchronizeMemory(); + } + else + { + for (int index = 0; index < overlapsCount; index++) + { + Texture overlap = _textureOverlaps[index]; + OverlapInfo oInfo = _overlapInfo[index]; - texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>()); - texture.InitializeData(false, false); + if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0) + { + // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead. - overlap.SynchronizeMemory(); - overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true); - break; + texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode); + + texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>()); + texture.InitializeData(false, false); + + overlap.SynchronizeMemory(); + overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true); + break; + } } } @@ -740,7 +758,7 @@ namespace Ryujinx.Graphics.Gpu.Image Texture overlap = _textureOverlaps[index]; OverlapInfo oInfo = _overlapInfo[index]; - if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible) + if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible || oInfo.Compatibility == TextureViewCompatibility.FormatAlias) { if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility)) { diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 5d8462220..85ad0bb01 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -291,22 +291,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>The minimum compatibility level of two provided view compatibility results</returns> public static TextureViewCompatibility PropagateViewCompatibility(TextureViewCompatibility first, TextureViewCompatibility second) { - if (first == TextureViewCompatibility.Incompatible || second == TextureViewCompatibility.Incompatible) - { - return TextureViewCompatibility.Incompatible; - } - else if (first == TextureViewCompatibility.LayoutIncompatible || second == TextureViewCompatibility.LayoutIncompatible) - { - return TextureViewCompatibility.LayoutIncompatible; - } - else if (first == TextureViewCompatibility.CopyOnly || second == TextureViewCompatibility.CopyOnly) - { - return TextureViewCompatibility.CopyOnly; - } - else - { - return TextureViewCompatibility.Full; - } + return (TextureViewCompatibility)Math.Min((int)first, (int)second); } /// <summary> @@ -628,15 +613,21 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="lhs">Texture information of the texture view</param> /// <param name="rhs">Texture information of the texture view</param> /// <param name="caps">Host GPU capabilities</param> + /// <param name="flags">Texture search flags</param> /// <returns>The view compatibility level of the texture formats</returns> - public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) + public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps, TextureSearchFlags flags) { FormatInfo lhsFormat = lhs.FormatInfo; FormatInfo rhsFormat = rhs.FormatInfo; if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil()) { - return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; + return FormatMatches(lhs, rhs, flags.HasFlag(TextureSearchFlags.ForSampler), flags.HasFlag(TextureSearchFlags.DepthAlias)) switch + { + TextureMatchQuality.Perfect => TextureViewCompatibility.Full, + TextureMatchQuality.FormatAlias => TextureViewCompatibility.FormatAlias, + _ => TextureViewCompatibility.Incompatible + }; } if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps)) @@ -754,49 +745,6 @@ namespace Ryujinx.Graphics.Gpu.Image return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; } - /// <summary> - /// Checks if a swizzle component in two textures functionally match, taking into account if the components are defined. - /// </summary> - /// <param name="lhs">Texture information to compare</param> - /// <param name="rhs">Texture information to compare with</param> - /// <param name="swizzleLhs">Swizzle component for the first texture</param> - /// <param name="swizzleRhs">Swizzle component for the second texture</param> - /// <param name="component">Component index, starting at 0 for red</param> - /// <returns>True if the swizzle components functionally match, false othersize</returns> - private static bool SwizzleComponentMatches(TextureInfo lhs, TextureInfo rhs, SwizzleComponent swizzleLhs, SwizzleComponent swizzleRhs, int component) - { - int lhsComponents = lhs.FormatInfo.Components; - int rhsComponents = rhs.FormatInfo.Components; - - if (lhsComponents == 4 && rhsComponents == 4) - { - return swizzleLhs == swizzleRhs; - } - - // Swizzles after the number of components a format defines are "undefined". - // We allow these to not be equal under certain circumstances. - // This can only happen when there are less than 4 components in a format. - // It tends to happen when float depth textures are sampled. - - bool lhsDefined = (swizzleLhs - SwizzleComponent.Red) < lhsComponents; - bool rhsDefined = (swizzleRhs - SwizzleComponent.Red) < rhsComponents; - - if (lhsDefined == rhsDefined) - { - // If both are undefined, return true. Otherwise just check if they're equal. - return lhsDefined ? swizzleLhs == swizzleRhs : true; - } - else - { - SwizzleComponent defined = lhsDefined ? swizzleLhs : swizzleRhs; - SwizzleComponent undefined = lhsDefined ? swizzleRhs : swizzleLhs; - - // Undefined swizzle can be matched by a forced value (0, 1), exact equality, or expected value. - // For example, R___ matches R001, RGBA but not RBGA. - return defined == undefined || defined < SwizzleComponent.Red || defined == SwizzleComponent.Red + component; - } - } - /// <summary> /// Checks if the texture shader sampling parameters of two texture informations match. /// </summary> @@ -806,10 +754,10 @@ namespace Ryujinx.Graphics.Gpu.Image public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs) { return lhs.DepthStencilMode == rhs.DepthStencilMode && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleR, rhs.SwizzleR, 0) && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleG, rhs.SwizzleG, 1) && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleB, rhs.SwizzleB, 2) && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleA, rhs.SwizzleA, 3); + lhs.SwizzleR == rhs.SwizzleR && + lhs.SwizzleG == rhs.SwizzleG && + lhs.SwizzleB == rhs.SwizzleB && + lhs.SwizzleA == rhs.SwizzleA; } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index b36b16e94..2fa1e79e5 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -68,6 +68,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0; + /// <summary> + /// Number indicating the order this texture group was modified relative to others. + /// </summary> + public long ModifiedSequence { get; private set; } + private readonly GpuContext _context; private readonly PhysicalMemory _physicalMemory; @@ -664,6 +669,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="texture">The texture that has been modified</param> public void SignalModified(Texture texture) { + ModifiedSequence = _context.GetModifiedSequence(); + ClearIncompatibleOverlaps(texture); EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => @@ -684,6 +691,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="bound">True if this texture is being bound, false if unbound</param> public void SignalModifying(Texture texture, bool bound) { + ModifiedSequence = _context.GetModifiedSequence(); + ClearIncompatibleOverlaps(texture); EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs index a7ee12bcc..1994d2263 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs @@ -300,8 +300,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> /// <param name="parent">The parent texture</param> /// <param name="firstLevel">The first level of the texture view</param> + /// <param name="parentFormat">True if the parent format should be inherited</param> /// <returns>The adjusted texture information with the new size</returns> - public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel) + public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel, bool parentFormat) { // When the texture is used as view of another texture, we must // ensure that the sizes are valid, otherwise data uploads would fail @@ -370,7 +371,36 @@ namespace Ryujinx.Graphics.Gpu.Image GobBlocksInZ, GobBlocksInTileX, target, - FormatInfo, + parentFormat ? parent.Info.FormatInfo : FormatInfo, + DepthStencilMode, + SwizzleR, + SwizzleG, + SwizzleB, + SwizzleA); + } + + /// <summary> + /// Creates texture information for a given format and this information. + /// </summary> + /// <param name="formatInfo">Format for the new texture info</param> + /// <returns>New info with the specified format</returns> + public TextureInfo CreateInfoWithFormat(FormatInfo formatInfo) + { + return new TextureInfo( + GpuAddress, + Width, + Height, + DepthOrLayers, + Levels, + SamplesInX, + SamplesInY, + Stride, + IsLinear, + GobBlocksInY, + GobBlocksInZ, + GobBlocksInTileX, + Target, + formatInfo, DepthStencilMode, SwizzleR, SwizzleG, diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs index b89936ebd..dfa688c45 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs @@ -9,6 +9,7 @@ Incompatible = 0, LayoutIncompatible, CopyOnly, + FormatAlias, Full } } From 5fda543f8478222ce80ab3e159fd480de80b2980 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Fri, 12 May 2023 02:06:15 +0100 Subject: [PATCH 532/737] Vulkan: Partially workaround MoltenVK InvalidResource error (#4880) * Add MVK stage flags workaround * Actually do the workaround * Remove GS on VS stuff * Address feedback --- .../PipelineLayoutFactory.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index 96b3b3b1c..2d8814191 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -12,6 +12,28 @@ namespace Ryujinx.Graphics.Vulkan ShaderStageFlags.FragmentBit | ShaderStageFlags.ComputeBit; + private static ShaderStageFlags ActiveStages(uint stages) + { + ShaderStageFlags stageFlags = 0; + + while (stages != 0) + { + int stage = BitOperations.TrailingZeroCount(stages); + stages &= ~(1u << stage); + + stageFlags |= stage switch + { + 1 => ShaderStageFlags.FragmentBit, + 2 => ShaderStageFlags.GeometryBit, + 3 => ShaderStageFlags.TessellationControlBit, + 4 => ShaderStageFlags.TessellationEvaluationBit, + _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit + }; + } + + return stageFlags; + } + public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout) { int stagesCount = BitOperations.PopCount(stages); @@ -34,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan }; int iter = 0; + var activeStages = ActiveStages(stages); while (stages != 0) { @@ -67,12 +90,16 @@ namespace Ryujinx.Graphics.Vulkan void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0) { + // There's a bug on MoltenVK where using the same buffer across different stages + // causes invalid resource errors, allow the binding on all active stages as workaround. + var flags = gd.IsMoltenVk ? activeStages : stageFlags; + bindings[start + iter] = new DescriptorSetLayoutBinding { Binding = (uint)(start + stage * maxPerStage), DescriptorType = DescriptorType.StorageBuffer, DescriptorCount = (uint)maxPerStage, - StageFlags = stageFlags + StageFlags = flags }; } From 7271f1b18e13d4188abe408443272da89e7dcacc Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Fri, 12 May 2023 18:53:14 +0200 Subject: [PATCH 533/737] Bump shader cache codegen version That was missing from #4892 --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index f30f1ff1b..71098efa8 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4821; + private const uint CodeGenVersion = 4892; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; From 2b6e81deea47c1546e44fdcb8702a7c667056fde Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Fri, 12 May 2023 21:39:42 +0200 Subject: [PATCH 534/737] Ava: Fix wrong MouseButton (#4900) --- src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs index b3e1a21a1..8f64aa28d 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs @@ -70,11 +70,14 @@ namespace Ryujinx.Ava.Input private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args) { - int button = (int)args.InitialPressMouseButton - 1; - - if (PressedButtons.Count() >= button) + if (args.InitialPressMouseButton != Avalonia.Input.MouseButton.None) { - PressedButtons[button] = false; + int button = (int)args.InitialPressMouseButton; + + if (PressedButtons.Count() >= button) + { + PressedButtons[button] = false; + } } } From c2709b3bddf94e61eaacfa1271494d16e5412b26 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Fri, 12 May 2023 23:53:52 +0100 Subject: [PATCH 535/737] macOS CI Adjustments (#4910) * Fix macOS build name in CI Fixes updater * Update build.yml Don't publish x86 Mac builds * Naming nitpick * Berry changes * Use the same prefix for PR and release build archives --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- .github/workflows/build.yml | 18 +++++++++--------- distribution/macos/create_macos_build.sh | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8bc55ec3e..54389e55f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: RELEASE_ZIP_OS_NAME: linux_x64 - os: macOS-latest - OS_NAME: MacOS x64 + OS_NAME: macOS x64 DOTNET_RUNTIME_IDENTIFIER: osx-x64 RELEASE_ZIP_OS_NAME: osx_x64 @@ -68,15 +68,15 @@ jobs: - name: Publish Ryujinx run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Publish Ryujinx.Headless.SDL2 run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Publish Ryujinx.Ava run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Set executable bit run: | @@ -90,24 +90,24 @@ jobs: with: name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Upload Ryujinx.Headless.SDL2 artifact uses: actions/upload-artifact@v3 with: name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_sdl2_headless - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Upload Ryujinx.Ava artifact uses: actions/upload-artifact@v3 with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_ava - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' build_macos: - name: MacOS universal (${{ matrix.configuration }}) + name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest strategy: matrix: @@ -150,4 +150,4 @@ jobs: with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal path: "publish_ava/*.tar.gz" - if: github.event_name == 'pull_request' \ No newline at end of file + if: github.event_name == 'pull_request' diff --git a/distribution/macos/create_macos_build.sh b/distribution/macos/create_macos_build.sh index 1a5a38738..7405073d2 100755 --- a/distribution/macos/create_macos_build.sh +++ b/distribution/macos/create_macos_build.sh @@ -22,9 +22,9 @@ EXTRA_ARGS=$8 if [ "$VERSION" == "1.1.0" ]; then - RELEASE_TAR_FILE_NAME=Ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar + RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar else - RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar + RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$VERSION-macos_universal.app.tar fi ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app" From f679f25e084b7196f1eabd6a0e9ea60bca679a75 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 12 May 2023 21:59:46 -0300 Subject: [PATCH 536/737] Set OpenGL PixelPackBuffer to 0 when done (#4921) --- src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 2 ++ src/Ryujinx.Graphics.OpenGL/Window.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index f24a58fc3..90a2936d0 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -306,6 +306,8 @@ namespace Ryujinx.Graphics.OpenGL.Image int offset = WriteToPbo2D(range.Offset, layer, level); Debug.Assert(offset == 0); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); } public void WriteToPbo(int offset, bool forceBgra) diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index b37ec375e..cc9836e06 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.OpenGL oldView.Dispose(); } } - + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); From 880fd3cfcb1c394b06bdb4cd3433e23379b4fbe7 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 13 May 2023 09:15:16 +0200 Subject: [PATCH 537/737] audio: sdl2: Do not report 5.1 if the device doesn't support it (#4908) * amadeus: adjust VirtualDevice channel configuration reporting with HardwareDevice * audio: sdl2: Do not report 5.1 if device doesn't support it SDL2 5.1 to Stereo conversion is terrible and make everything sound quiet. Let's not expose 5.1 if not truly supported by the device. --- .../SDL2HardwareDeviceDriver.cs | 27 +++++++++++++++++++ .../Renderer/Device/VirtualDevice.cs | 2 +- .../Device/VirtualDeviceSessionRegistry.cs | 19 ++++++++++++- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index b190b4c83..d3a73cfcb 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -5,6 +5,7 @@ using Ryujinx.Memory; using Ryujinx.SDL2.Common; using System; using System.Collections.Concurrent; +using System.Runtime.InteropServices; using System.Threading; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; @@ -18,6 +19,13 @@ namespace Ryujinx.Audio.Backends.SDL2 private readonly ManualResetEvent _pauseEvent; private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions; + private bool _supportSurroundConfiguration; + + // TODO: Add this to SDL2-CS + // NOTE: We use a DllImport here because of marshaling issue for spec. + [DllImport("SDL2")] + private static extern int SDL_GetDefaultAudioInfo(IntPtr name, out SDL_AudioSpec spec, int isCapture); + public SDL2HardwareDeviceDriver() { _updateRequiredEvent = new ManualResetEvent(false); @@ -25,6 +33,20 @@ namespace Ryujinx.Audio.Backends.SDL2 _sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>(); SDL2Driver.Instance.Initialize(); + + int res = SDL_GetDefaultAudioInfo(IntPtr.Zero, out var spec, 0); + + if (res != 0) + { + Logger.Error?.Print(LogClass.Application, + $"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\""); + + _supportSurroundConfiguration = true; + } + else + { + _supportSurroundConfiguration = spec.channels == 6; + } } public static bool IsSupported => IsSupportedInternal(); @@ -164,6 +186,11 @@ namespace Ryujinx.Audio.Backends.SDL2 public bool SupportsChannelCount(uint channelCount) { + if (channelCount == 6) + { + return _supportSurroundConfiguration; + } + return true; } diff --git a/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs index 2fa030a8f..90692b004 100644 --- a/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs +++ b/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Device /// <param name="name">The name of the <see cref="VirtualDevice"/>.</param> /// <param name="channelCount">The count of channels supported by the <see cref="VirtualDevice"/>.</param> /// <param name="isExternalOutput">Indicate if the <see cref="VirtualDevice"/> is provided by an external interface.</param> - private VirtualDevice(string name, uint channelCount, bool isExternalOutput) + public VirtualDevice(string name, uint channelCount, bool isExternalOutput) { Name = name; ChannelCount = channelCount; diff --git a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs index 927e45add..696af90fa 100644 --- a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs +++ b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs @@ -1,3 +1,4 @@ +using Ryujinx.Audio.Integration; using System.Collections.Generic; namespace Ryujinx.Audio.Renderer.Device @@ -22,7 +23,23 @@ namespace Ryujinx.Audio.Renderer.Device /// The current active <see cref="VirtualDevice"/>. /// </summary> // TODO: make this configurable - public VirtualDevice ActiveDevice = VirtualDevice.Devices[2]; + public VirtualDevice ActiveDevice { get; } + + public VirtualDeviceSessionRegistry(IHardwareDeviceDriver driver) + { + uint channelCount; + + if (driver.GetRealDeviceDriver().SupportsChannelCount(6)) + { + channelCount = 6; + } + else + { + channelCount = 2; + } + + ActiveDevice = new VirtualDevice("AudioTvOutput", channelCount, false); + } /// <summary> /// Get the associated <see cref="T:VirtualDeviceSession[]"/> from an AppletResourceId. diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index a71837cab..166761c2c 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -261,7 +261,7 @@ namespace Ryujinx.HLE.HOS AudioInputManager = new AudioInputManager(); AudioRendererManager = new AudioRendererManager(tickSource); AudioRendererManager.SetVolume(Device.Configuration.AudioVolume); - AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(); + AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(Device.AudioDeviceDriver); IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax]; From aae4595bdbf16ebfc73299fa63ae7dffb8300d56 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 13 May 2023 13:24:43 +0200 Subject: [PATCH 538/737] Add timeout of 35 minutes to workflow jobs (#4928) --- .github/workflows/build.yml | 4 +++- .github/workflows/flatpak.yml | 1 + .github/workflows/nightly_pr_comment.yml | 3 ++- .github/workflows/release.yml | 6 ++++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54389e55f..c6dc724c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} + timeout-minutes: 35 strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -109,6 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest + timeout-minutes: 35 strategy: matrix: configuration: [ Debug, Release ] @@ -150,4 +152,4 @@ jobs: with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal path: "publish_ava/*.tar.gz" - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' \ No newline at end of file diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 86a80eabf..04b917c34 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -12,6 +12,7 @@ concurrency: flatpak-release jobs: release: + timeout-minutes: 35 runs-on: ubuntu-latest env: diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index bc3d1c43f..9ddd458c6 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -7,6 +7,7 @@ jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest + timeout-minutes: 35 steps: - uses: actions/github-script@v6 with: @@ -65,4 +66,4 @@ jobs: } else { core.info(`Creating a comment`); await github.rest.issues.createComment({repo, owner, issue_number, body}); - } + } \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 363e9cadd..a32033190 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,6 +46,7 @@ jobs: release: name: Release ${{ matrix.OS_NAME }} runs-on: ${{ matrix.os }} + timeout-minutes: 35 strategy: matrix: os: [ ubuntu-latest, windows-latest ] @@ -143,13 +144,14 @@ jobs: macos_release: name: Release MacOS universal runs-on: ubuntu-latest + timeout-minutes: 35 steps: - uses: actions/checkout@v3 - uses: actions/setup-dotnet@v3 with: global-json-file: global.json - + - name: Setup LLVM 14 run: | wget https://apt.llvm.org/llvm.sh @@ -205,4 +207,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit + secrets: inherit \ No newline at end of file From 17ba217940aa287821bd8f1e5d93e22b58998617 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sat, 13 May 2023 14:15:05 +0100 Subject: [PATCH 539/737] Vulkan: Device map buffers written more than flushed (#4911) --- src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 24 ++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 9a23280d0..6e10fad00 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan private int _writeCount; private int _flushCount; private int _flushTemp; + private int _lastFlushWrite = -1; private ReaderWriterLock _flushLock; private FenceHolder _flushFence; @@ -200,14 +201,21 @@ namespace Ryujinx.Graphics.Vulkan { if (_baseType == BufferAllocationType.Auto) { - if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold) + // When flushed, wait for a bit more info to make a decision. + bool wasFlushed = _flushTemp > 0; + int multiplier = wasFlushed ? 2 : 0; + if (_writeCount >= (WriteCountThreshold << multiplier) || _setCount >= (SetCountThreshold << multiplier) || _flushCount >= (FlushCountThreshold << multiplier)) { if (_flushCount > 0 || _flushTemp-- > 0) { // Buffers that flush should ideally be mapped in host address space for easy copies. // If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages). // If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached. - DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped; + + bool hostMappingSensitive = _gd.Vendor == Vendor.Nvidia; + bool deviceLocalMapped = Size > DeviceLocalSizeThreshold || (wasFlushed && _writeCount > _flushCount * 10 && hostMappingSensitive) || _currentType == BufferAllocationType.DeviceLocalMapped; + + DesiredType = deviceLocalMapped ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped; // It's harder for a buffer that is flushed to revert to another type of mapping. if (_flushCount > 0) @@ -215,17 +223,18 @@ namespace Ryujinx.Graphics.Vulkan _flushTemp = 1000; } } - else if (_writeCount >= WriteCountThreshold) + else if (_writeCount >= (WriteCountThreshold << multiplier)) { // Buffers that are written often should ideally be in the device local heap. (Storage buffers) DesiredType = BufferAllocationType.DeviceLocal; } - else if (_setCount > SetCountThreshold) + else if (_setCount > (SetCountThreshold << multiplier)) { // Buffers that have their data set often should ideally be host mapped. (Constant buffers) DesiredType = BufferAllocationType.HostMapped; } + _lastFlushWrite = -1; _flushCount = 0; _writeCount = 0; _setCount = 0; @@ -418,7 +427,12 @@ namespace Ryujinx.Graphics.Vulkan WaitForFlushFence(); - _flushCount++; + if (_lastFlushWrite != _writeCount) + { + // If it's on the same page as the last flush, ignore it. + _lastFlushWrite = _writeCount; + _flushCount++; + } Span<byte> result; From 22202be3946b5cb65511059e717af449168812f3 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 14 May 2023 16:34:31 +0200 Subject: [PATCH 540/737] [GUI] Fix always hide cursor mode not hiding the cursor until it was moved (#4927) * gtk: Add missing isMouseInClient check for hide-cursor * ava: Add missing events and default isCursorInRenderer to true This is necessary because we don't receive a initial PointerEnter event for some reason. --- src/Ryujinx.Ava/AppHost.cs | 18 ++++++++++++---- src/Ryujinx/Ui/RendererWidgetBase.cs | 32 ++++++++++++++-------------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 795c3f7a7..f3e90ef17 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -86,7 +86,7 @@ namespace Ryujinx.Ava private KeyboardHotkeyState _prevHotkeyState; private long _lastCursorMoveTime; - private bool _isCursorInRenderer; + private bool _isCursorInRenderer = true; private bool _isStopped; private bool _isActive; @@ -160,7 +160,9 @@ namespace Ryujinx.Ava ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; - _topLevel.PointerMoved += TopLevel_PointerMoved; + _topLevel.PointerMoved += TopLevel_PointerEnterOrMoved; + _topLevel.PointerEnter += TopLevel_PointerEnterOrMoved; + _topLevel.PointerLeave += TopLevel_PointerLeave; if (OperatingSystem.IsWindows()) { @@ -183,7 +185,7 @@ namespace Ryujinx.Ava _gpuCancellationTokenSource = new CancellationTokenSource(); } - private void TopLevel_PointerMoved(object sender, PointerEventArgs e) + private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e) { if (sender is MainWindow window) { @@ -201,6 +203,12 @@ namespace Ryujinx.Ava } } } + + private void TopLevel_PointerLeave(object sender, PointerEventArgs e) + { + _isCursorInRenderer = false; + } + private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e) { _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); @@ -446,7 +454,9 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; - _topLevel.PointerMoved -= TopLevel_PointerMoved; + _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved; + _topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved; + _topLevel.PointerLeave -= TopLevel_PointerLeave; _gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Dispose(); diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index 0fa7240b8..573b69b35 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -321,27 +321,27 @@ namespace Ryujinx.Ui _toggleDockedMode = toggleDockedMode; - if (ConfigurationState.Instance.Hid.EnableMouse.Value) + if (_isMouseInClient) { - if (_isMouseInClient) + if (ConfigurationState.Instance.Hid.EnableMouse.Value) { Window.Cursor = _invisibleCursor; } - } - else - { - switch (_hideCursorMode) + else { - case HideCursorMode.OnIdle: - long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; - Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; - break; - case HideCursorMode.Always: - Window.Cursor = _invisibleCursor; - break; - case HideCursorMode.Never: - Window.Cursor = null; - break; + switch (_hideCursorMode) + { + case HideCursorMode.OnIdle: + long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; + Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; + break; + case HideCursorMode.Always: + Window.Cursor = _invisibleCursor; + break; + case HideCursorMode.Never: + Window.Cursor = null; + break; + } } } } From f8cdd5f484f1b1d0c633f6da2016713e14e4c6e7 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 17 May 2023 19:02:15 +0200 Subject: [PATCH 541/737] macos: Fix relaunch with updater when no arguments were provided to the emulator (#4987) The updater still seems to be erroring when replacing installed folder under normal operations. --- distribution/macos/updater.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh index b60ac34df..1b3224939 100755 --- a/distribution/macos/updater.sh +++ b/distribution/macos/updater.sh @@ -36,4 +36,9 @@ sleep 1 # Now replace and reopen. rm -rf "$INSTALL_DIRECTORY" mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY" -open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS" + +if [ "$#" -le 3 ]; then + open -a "$INSTALL_DIRECTORY" +else + open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS" +fi From cb4b58052f325864ece956898cdd64373edf896a Mon Sep 17 00:00:00 2001 From: OpaqueReptile <1337paf92@gmail.com> Date: Wed, 17 May 2023 13:38:59 -0500 Subject: [PATCH 542/737] Start GPU performance counter at 0 instead of host GPU value (#4992) * Start performance counter at 0 instead of host perf counter value * whitespace * init _firstTimestamp in constructer per feedback * change comment * punctuation * address feedback * revise comment --- src/Ryujinx.Graphics.Gpu/GpuContext.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index ccaabf70f..0fe6a28ff 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -99,6 +99,7 @@ namespace Ryujinx.Graphics.Gpu private bool _pendingSync; private long _modifiedSequence; + private ulong _firstTimestamp; /// <summary> /// Creates a new instance of the GPU emulation context. @@ -123,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu DeferredActions = new Queue<Action>(); PhysicalMemoryRegistry = new ConcurrentDictionary<ulong, PhysicalMemory>(); + + _firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); } /// <summary> @@ -217,7 +220,8 @@ namespace Ryujinx.Graphics.Gpu /// <returns>The current GPU timestamp</returns> public ulong GetTimestamp() { - ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); + // Guest timestamp will start at 0, instead of host value. + ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds) - _firstTimestamp; if (GraphicsConfig.FastGpuTime) { From b3bf05356be755bcbf82611530ebd44c9b61b384 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 17 May 2023 21:27:49 +0200 Subject: [PATCH 543/737] ava: Fix crash when extracting sections from NCA with no data section (#5002) Tested against Omega Strickers. --- src/Ryujinx.Ava/Common/ApplicationHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Ava/Common/ApplicationHelper.cs b/src/Ryujinx.Ava/Common/ApplicationHelper.cs index c961d76c8..882c06949 100644 --- a/src/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/src/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -193,7 +193,7 @@ namespace Ryujinx.Ava.Common if (nca.Header.ContentType == NcaContentType.Program) { int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) + if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { patchNca = nca; } From ecbf303266d78d7b4287ce4ea97d59107a05fb2f Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Thu, 18 May 2023 07:56:34 +0100 Subject: [PATCH 544/737] GPU: Avoid using garbage size for non-cb0 storage buffers (#4999) * GPU: Avoid using garbage size for non-cb0 storage buffers In the depths area, Tears of the Kingdom uses a global memory access with address on constant buffer slot 6. This isn't standard and thus doesn't actually have a size 8 bytes after it, so we were reading back a garbage size that ended up very large (at least in version 1.1.0), and would synchronize a lot of data per frame. This PR makes storage buffers created from addresses outside constant buffer slot 0 get their size as the number of bytes remaining in the GPU mapping starting at the given virtual address. This should bound the buffer to a reasonable size, and ideally stop it crossing into other memory. * Limit max size * Add TODO * Feedback --- .../Engine/Threed/StateUpdater.cs | 16 ++++++++++- .../Memory/MemoryManager.cs | 27 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 1c9bf1d2a..87e58ead7 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public const int PrimitiveRestartStateIndex = 12; public const int RenderTargetStateIndex = 27; + private const ulong MaxUnknownStorageSize = 0x100000; + private readonly GpuContext _context; private readonly GpuChannel _channel; private readonly DeviceStateWithShadow<ThreedClassState> _state; @@ -356,7 +358,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); - _channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); + uint size; + if (sb.SbCbSlot == 0) + { + // Only trust the SbDescriptor size if it comes from slot 0. + size = (uint)sbDescriptor.Size; + } + else + { + // TODO: Use full mapped size and somehow speed up buffer sync. + size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), MaxUnknownStorageSize); + } + + _channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 0d4a41f02..c7a138c98 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -637,6 +637,33 @@ namespace Ryujinx.Graphics.Gpu.Memory return UnpackPaFromPte(pte) + (va & PageMask); } + /// <summary> + /// Translates a GPU virtual address and returns the number of bytes that are mapped after it. + /// </summary> + /// <param name="va">GPU virtual address to be translated</param> + /// <param name="maxSize">Maximum size in bytes to scan</param> + /// <returns>Number of bytes, 0 if unmapped</returns> + public ulong GetMappedSize(ulong va, ulong maxSize) + { + if (!ValidateAddress(va)) + { + return 0; + } + + ulong startVa = va; + ulong endVa = va + maxSize; + + ulong pte = GetPte(va); + + while (pte != PteUnmapped && va < endVa) + { + va += PageSize - (va & PageMask); + pte = GetPte(va); + } + + return Math.Min(maxSize, va - startVa); + } + /// <summary> /// Gets the kind of a given memory page. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling. From f864a490142c4da608dab8d2025fc18da857f93f Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 18 May 2023 18:16:03 -0300 Subject: [PATCH 545/737] Fix Vulkan blit-like operations swizzle (#5003) --- .../DescriptorSetUpdater.cs | 29 ++++++++++++++++++- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 12 ++++---- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 5 ++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index ab3befd81..a47ea5ff8 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -228,7 +228,12 @@ namespace Ryujinx.Graphics.Vulkan SignalDirty(DirtyFlags.Storage); } - public void SetTextureAndSampler(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture texture, ISampler sampler) + public void SetTextureAndSampler( + CommandBufferScoped cbs, + ShaderStage stage, + int binding, + ITexture texture, + ISampler sampler) { if (texture is TextureBuffer textureBuffer) { @@ -251,6 +256,28 @@ namespace Ryujinx.Graphics.Vulkan SignalDirty(DirtyFlags.Texture); } + public void SetTextureAndSamplerIdentitySwizzle( + CommandBufferScoped cbs, + ShaderStage stage, + int binding, + ITexture texture, + ISampler sampler) + { + if (texture is TextureView view) + { + view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); + + _textureRefs[binding] = view.GetIdentityImageView(); + _samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler(); + + SignalDirty(DirtyFlags.Texture); + } + else + { + SetTextureAndSampler(cbs, stage, binding, texture, sampler); + } + } + public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers) { for (int i = 0; i < buffers.Length; i++) diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 155c7f6fe..c064696d8 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -415,7 +415,7 @@ namespace Ryujinx.Graphics.Vulkan var sampler = linearFilter ? _samplerLinear : _samplerNearest; - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); + _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, sampler); Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)]; @@ -625,7 +625,7 @@ namespace Ryujinx.Graphics.Vulkan private void BlitDepthStencilDraw(TextureView src, bool isDepth) { - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); + _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest); if (isDepth) { @@ -1037,7 +1037,7 @@ namespace Ryujinx.Graphics.Vulkan var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat); var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l); - _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); + _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetImage(0, dstView, dstFormat); int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; @@ -1177,7 +1177,7 @@ namespace Ryujinx.Graphics.Vulkan var srcView = Create2DLayerView(src, srcLayer + z, 0, format); var dstView = Create2DLayerView(dst, dstLayer + z, 0); - _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); + _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetImage(0, dstView, format); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); @@ -1313,7 +1313,7 @@ namespace Ryujinx.Graphics.Vulkan var srcView = Create2DLayerView(src, srcLayer + z, 0, format); var dstView = Create2DLayerView(dst, dstLayer + z, 0); - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null); + _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null); _pipeline.SetRenderTarget( ((TextureView)dstView).GetView(format).GetImageViewForAttachment(), (uint)dst.Width, @@ -1384,7 +1384,7 @@ namespace Ryujinx.Graphics.Vulkan private void CopyMSAspectDraw(TextureView src, bool fromMS, bool isDepth) { - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); + _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest); if (isDepth) { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 4aec0dbca..ce6148e2f 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1098,6 +1098,11 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSetUpdater.SetTextureAndSampler(Cbs, stage, binding, texture, sampler); } + public void SetTextureAndSamplerIdentitySwizzle(ShaderStage stage, int binding, ITexture texture, ISampler sampler) + { + _descriptorSetUpdater.SetTextureAndSamplerIdentitySwizzle(Cbs, stage, binding, texture, sampler); + } + public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) { PauseTransformFeedbackInternal(); From a40c90e7dd0c37ae6fb7be84ad720cbf88fd8d28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 06:52:44 +0200 Subject: [PATCH 546/737] nuget: bump DynamicData from 7.13.5 to 7.13.8 (#5001) Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.5 to 7.13.8. - [Release notes](https://github.com/reactiveui/DynamicData/releases) - [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md) - [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.5...7.13.8) --- updated-dependencies: - dependency-name: DynamicData dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index f123ab7f0..0bbe81406 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,7 +13,7 @@ <PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> - <PackageVersion Include="DynamicData" Version="7.13.5" /> + <PackageVersion Include="DynamicData" Version="7.13.8" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> From fc26189fe1338ffcba12d83a922da9c706738902 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 19 May 2023 11:52:31 -0300 Subject: [PATCH 547/737] Eliminate redundant multiplications by gl_FragCoord.w on the shader (#4578) * Eliminate redundant multiplications by gl_FragCoord.w on the shader * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Translation/Optimizations/Optimizer.cs | 75 +++++++++++++++++++ .../Translation/Optimizations/Utils.cs | 29 +++++++ 3 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 71098efa8..ee7714521 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4892; + private const uint CodeGenVersion = 4578; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 16848bdc8..b41e47e42 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -20,6 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask); BindlessToIndexed.RunPass(blocks[blkIndex], config); BindlessElimination.RunPass(blocks[blkIndex], config); + + // FragmentCoord only exists on fragment shaders, so we don't need to check other stages. + if (config.Stage == ShaderStage.Fragment) + { + EliminateMultiplyByFragmentCoordW(blocks[blkIndex]); + } } config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask); @@ -281,6 +287,75 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return modified; } + private static void EliminateMultiplyByFragmentCoordW(BasicBlock block) + { + foreach (INode node in block.Operations) + { + if (node is Operation operation) + { + EliminateMultiplyByFragmentCoordW(operation); + } + } + } + + private static void EliminateMultiplyByFragmentCoordW(Operation operation) + { + // We're looking for the pattern: + // y = x * gl_FragCoord.w + // v = y * (1.0 / gl_FragCoord.w) + // Then we transform it into: + // v = x + // This pattern is common on fragment shaders due to the way how perspective correction is done. + + // We are expecting a multiplication by the reciprocal of gl_FragCoord.w. + if (operation.Inst != (Instruction.FP32 | Instruction.Multiply)) + { + return; + } + + Operand lhs = operation.GetSource(0); + Operand rhs = operation.GetSource(1); + + // Check LHS of the the main multiplication operation. We expect an input being multiplied by gl_FragCoord.w. + if (!(lhs.AsgOp is Operation attrMulOp) || attrMulOp.Inst != (Instruction.FP32 | Instruction.Multiply)) + { + return; + } + + Operand attrMulLhs = attrMulOp.GetSource(0); + Operand attrMulRhs = attrMulOp.GetSource(1); + + // LHS should be any input, RHS should be exactly gl_FragCoord.w. + if (!Utils.IsInputLoad(attrMulLhs.AsgOp) || !Utils.IsInputLoad(attrMulRhs.AsgOp, IoVariable.FragmentCoord, 3)) + { + return; + } + + // RHS of the main multiplication should be a reciprocal operation (1.0 / x). + if (!(rhs.AsgOp is Operation reciprocalOp) || reciprocalOp.Inst != (Instruction.FP32 | Instruction.Divide)) + { + return; + } + + Operand reciprocalLhs = reciprocalOp.GetSource(0); + Operand reciprocalRhs = reciprocalOp.GetSource(1); + + // Check if the divisor is a constant equal to 1.0. + if (reciprocalLhs.Type != OperandType.Constant || reciprocalLhs.AsFloat() != 1.0f) + { + return; + } + + // Check if the dividend is gl_FragCoord.w. + if (!Utils.IsInputLoad(reciprocalRhs.AsgOp, IoVariable.FragmentCoord, 3)) + { + return; + } + + // If everything matches, we can replace the operation with the input load result. + operation.TurnIntoCopy(attrMulLhs); + } + private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode) { // Remove a node from the nodes list, and also remove itself diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs index 4ca6d6877..a0d58d079 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs @@ -4,6 +4,35 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class Utils { + public static bool IsInputLoad(INode node) + { + return (node is Operation operation) && + operation.Inst == Instruction.Load && + operation.StorageKind == StorageKind.Input; + } + + public static bool IsInputLoad(INode node, IoVariable ioVariable, int elemIndex) + { + if (!(node is Operation operation) || + operation.Inst != Instruction.Load || + operation.StorageKind != StorageKind.Input || + operation.SourcesCount != 2) + { + return false; + } + + Operand ioVariableSrc = operation.GetSource(0); + + if (ioVariableSrc.Type != OperandType.Constant || (IoVariable)ioVariableSrc.Value != ioVariable) + { + return false; + } + + Operand elemIndexSrc = operation.GetSource(1); + + return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex; + } + private static Operation FindBranchSource(BasicBlock block) { foreach (BasicBlock sourceBlock in block.Predecessors) From bba51c2eeb21da09e53caef52163f3e1c7598689 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Fri, 19 May 2023 20:20:01 +0100 Subject: [PATCH 548/737] Fix macOS Update Script (#5014) * Update updater.sh * Better script * Revert "Better script" This reverts commit 9bf6be863892e5e10c2f2dba45f1d0a60daca688. --- distribution/macos/updater.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh index 1b3224939..0854d4347 100755 --- a/distribution/macos/updater.sh +++ b/distribution/macos/updater.sh @@ -25,14 +25,14 @@ error_handler() { exit 1 } -trap 'error_handler ${LINENO}' ERR - # Wait for Ryujinx to exit # NOTE: in case no fds are open, lsof could be returning with a process still living. # We wait 1s and assume the process stopped after that lsof -p $APP_PID +r 1 &>/dev/null sleep 1 +trap 'error_handler ${LINENO}' ERR + # Now replace and reopen. rm -rf "$INSTALL_DIRECTORY" mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY" From 69a9de33d37de03693a4a4f6f51aead63b0c6334 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sat, 20 May 2023 14:52:26 +0100 Subject: [PATCH 549/737] SPIR-V: Only allow implicit LOD sampling on fragment (#5026) --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Spirv/Instructions.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index ee7714521..6a81720c0 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4578; + private const uint CodeGenVersion = 5027; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 5521fa5c9..6da8f29d1 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -1623,7 +1623,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (hasLodBias) { - lodBias = Src(AggregateType.FP32); + lodBias = Src(AggregateType.FP32); + } + + if (!isGather && !intCoords && !isMultisample && !hasLodLevel && !hasDerivatives && context.Config.Stage != ShaderStage.Fragment) + { + // Implicit LOD is only valid on fragment. + // Use the LOD bias as explicit LOD if available. + + lod = lodBias ?? context.Constant(context.TypeFP32(), 0f); + + lodBias = null; + hasLodBias = false; + hasLodLevel = true; } SpvInstruction compIdx = null; From fb27042e01b0fa110184673d436ec96ec8cf20c7 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 20 May 2023 13:15:07 -0300 Subject: [PATCH 550/737] Limit compute storage buffer size (#5028) --- src/Ryujinx.Graphics.Gpu/Constants.cs | 26 +++++++------------ .../Engine/Compute/ComputeClass.cs | 14 +++++++++- .../Engine/Threed/StateUpdater.cs | 6 ++--- src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 18 ++++++++++--- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Constants.cs b/src/Ryujinx.Graphics.Gpu/Constants.cs index 1897f5d0f..b559edc25 100644 --- a/src/Ryujinx.Graphics.Gpu/Constants.cs +++ b/src/Ryujinx.Graphics.Gpu/Constants.cs @@ -40,22 +40,6 @@ namespace Ryujinx.Graphics.Gpu /// </summary> public const int TotalTransformFeedbackBuffers = 4; - /// <summary> - /// Maximum number of textures on a single shader stage. - /// </summary> - /// <remarks> - /// The maximum number of textures is API limited, the hardware supports an unlimited amount. - /// </remarks> - public const int TotalTextures = 32; - - /// <summary> - /// Maximum number of images on a single shader stage. - /// </summary> - /// <remarks> - /// The maximum number of images is API limited, the hardware supports an unlimited amount. - /// </remarks> - public const int TotalImages = 8; - /// <summary> /// Maximum number of render target color buffers. /// </summary> @@ -100,5 +84,15 @@ namespace Ryujinx.Graphics.Gpu /// Expected byte alignment for storage buffers /// </summary> public const int StorageAlignment = 16; + + /// <summary> + /// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses. + /// </summary> + public const int DriverReservedUniformBuffer = 0; + + /// <summary> + /// Maximum size that an storage buffer is assumed to have when the correct size is unknown. + /// </summary> + public const ulong MaxUnknownStorageSize = 0x100000; } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 4ec23c791..998ece224 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -162,7 +162,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); - _channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); + uint size; + if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer) + { + // Only trust the SbDescriptor size if it comes from slot 0. + size = (uint)sbDescriptor.Size; + } + else + { + // TODO: Use full mapped size and somehow speed up buffer sync. + size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), Constants.MaxUnknownStorageSize); + } + + _channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags); } if ((_channel.BufferManager.HasUnalignedStorageBuffers) != hasUnaligned) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 87e58ead7..4feb8bafc 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public const int PrimitiveRestartStateIndex = 12; public const int RenderTargetStateIndex = 27; - private const ulong MaxUnknownStorageSize = 0x100000; - private readonly GpuContext _context; private readonly GpuChannel _channel; private readonly DeviceStateWithShadow<ThreedClassState> _state; @@ -359,7 +357,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); uint size; - if (sb.SbCbSlot == 0) + if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer) { // Only trust the SbDescriptor size if it comes from slot 0. size = (uint)sbDescriptor.Size; @@ -367,7 +365,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed else { // TODO: Use full mapped size and somehow speed up buffer sync. - size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), MaxUnknownStorageSize); + size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), Constants.MaxUnknownStorageSize); } _channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index 27678ed5e..e046bf899 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -1,4 +1,5 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; using System.Runtime.CompilerServices; @@ -95,16 +96,27 @@ namespace Ryujinx.Graphics.Vulkan return Unsafe.As<ulong, BufferHandle>(ref handle64); } - public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default) + public BufferHandle CreateWithHandle( + VulkanRenderer gd, + int size, + BufferAllocationType baseType = BufferAllocationType.HostMapped, + BufferHandle storageHint = default) { return CreateWithHandle(gd, size, out _, baseType, storageHint); } - public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, out BufferHolder holder, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default) + public BufferHandle CreateWithHandle( + VulkanRenderer gd, + int size, + out BufferHolder holder, + BufferAllocationType baseType = BufferAllocationType.HostMapped, + BufferHandle storageHint = default) { holder = Create(gd, size, baseType: baseType, storageHint: storageHint); if (holder == null) { + Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\"."); + return BufferHandle.Null; } From 402f05b8ef013807997589ecc0a8ff50267dcd23 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 20 May 2023 16:19:26 -0300 Subject: [PATCH 551/737] Replace constant buffer access on shader with new Load instruction (#4646) --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 19 +- .../CodeGen/Glsl/Declarations.cs | 98 +++------ .../CodeGen/Glsl/DefaultNames.cs | 10 - .../HelperFunctions/TexelFetchScale_cp.glsl | 4 +- .../HelperFunctions/TexelFetchScale_fp.glsl | 4 +- .../HelperFunctions/TexelFetchScale_vp.glsl | 4 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 3 - .../Glsl/Instructions/InstGenHelper.cs | 1 - .../Glsl/Instructions/InstGenMemory.cs | 98 +++++---- .../CodeGen/Glsl/Instructions/IoMap.cs | 3 - .../CodeGen/Glsl/OperandManager.cs | 67 ++---- .../CodeGen/Spirv/CodeGenContext.cs | 30 +-- .../CodeGen/Spirv/Declarations.cs | 129 +++++------- .../CodeGen/Spirv/Instructions.cs | 193 ++++++------------ .../CodeGen/Spirv/ScalingHelpers.cs | 8 +- .../Instructions/InstEmitAttribute.cs | 2 +- .../Instructions/InstEmitMemory.cs | 39 +++- .../IntermediateRepresentation/Instruction.cs | 1 - .../IntermediateRepresentation/IoVariable.cs | 3 - .../StructuredIr/AstOperand.cs | 14 +- .../StructuredIr/BufferDefinition.cs | 20 ++ .../StructuredIr/BufferLayout.cs | 8 + .../StructuredIr/InstructionInfo.cs | 1 - .../StructuredIr/OperandInfo.cs | 1 - .../StructuredIr/ShaderProperties.cs | 21 ++ .../StructuredIr/StructureType.cs | 28 +++ .../StructuredIr/StructuredProgram.cs | 41 ++-- .../StructuredIr/StructuredProgramContext.cs | 32 ++- src/Ryujinx.Graphics.Shader/SupportBuffer.cs | 26 +++ .../Translation/AggregateType.cs | 31 +++ .../Translation/EmitterContext.cs | 6 +- .../Translation/EmitterContextInsts.cs | 46 +++-- .../Translation/FeatureFlags.cs | 1 - .../Optimizations/BindlessToIndexed.cs | 32 ++- .../Optimizations/ConstantFolding.cs | 19 +- .../Optimizations/GlobalToStorage.cs | 24 ++- .../Translation/Optimizations/Optimizer.cs | 8 +- .../Translation/ResourceManager.cs | 126 ++++++++++++ .../Translation/Rewriter.cs | 115 +++++++++-- .../Translation/ShaderConfig.cs | 93 ++------- .../Translation/Translator.cs | 2 +- 42 files changed, 788 insertions(+), 625 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 6a81720c0..b4764d578 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5027; + private const uint CodeGenVersion = 4646; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index d35b8d921..7db627ba9 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; +using System; namespace Ryujinx.Graphics.Gpu.Shader { @@ -16,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly ResourceCounts _resourceCounts; private readonly int _stageIndex; + private readonly int[] _constantBufferBindings; + /// <summary> /// Creates a new GPU accessor. /// </summary> @@ -25,6 +28,12 @@ namespace Ryujinx.Graphics.Gpu.Shader _context = context; _resourceCounts = resourceCounts; _stageIndex = stageIndex; + + if (context.Capabilities.Api != TargetApi.Vulkan) + { + _constantBufferBindings = new int[Constants.TotalGpUniformBuffers]; + _constantBufferBindings.AsSpan().Fill(-1); + } } public int QueryBindingConstantBuffer(int index) @@ -36,7 +45,15 @@ namespace Ryujinx.Graphics.Gpu.Shader } else { - return _resourceCounts.UniformBuffersCount++; + int binding = _constantBufferBindings[index]; + + if (binding < 0) + { + binding = _resourceCounts.UniformBuffersCount++; + _constantBufferBindings[index] = binding; + } + + return binding; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 81b79ec45..8d805e32e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Numerics; @@ -102,13 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - var cBufferDescriptors = context.Config.GetConstantBufferDescriptors(); - if (cBufferDescriptors.Length != 0) - { - DeclareUniforms(context, cBufferDescriptors); - - context.AppendLine(); - } + DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); var sBufferDescriptors = context.Config.GetStorageBufferDescriptors(); if (sBufferDescriptors.Length != 0) @@ -265,18 +260,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl scaleElements++; // Also includes render target scale, for gl_FragCoord. } - DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements); - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0) { AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl"); context.AppendLine(); } } - else if (isFragment || context.Config.Stage == ShaderStage.Vertex) - { - DeclareSupportUniformBlock(context, context.Config.Stage, 0); - } } if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0) @@ -389,36 +378,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl }; } - private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors) + private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) { - string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]"; - - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)) + foreach (BufferDefinition buffer in buffers) { - string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); - - ubName += "_" + DefaultNames.UniformNamePrefix; - - string blockName = $"{ubName}_{DefaultNames.BlockSuffix}"; - - context.AppendLine($"layout (binding = {context.Config.FirstConstantBufferBinding}, std140) uniform {blockName}"); - context.EnterScope(); - context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";"); - context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];"); - } - else - { - foreach (var descriptor in descriptors) + string layout = buffer.Layout switch { - string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); + BufferLayout.Std140 => "std140", + _ => "std430" + }; - ubName += "_" + DefaultNames.UniformNamePrefix + descriptor.Slot; + context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}"); + context.EnterScope(); - context.AppendLine($"layout (binding = {descriptor.Binding}, std140) uniform {ubName}"); - context.EnterScope(); - context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, descriptor.Slot, false) + ubSize + ";"); - context.LeaveScope(";"); + foreach (StructureField field in buffer.Type.Fields) + { + if (field.Type.HasFlag(AggregateType.Array)) + { + string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); + string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture); + + context.AppendLine($"{typeName} {field.Name}[{arraySize}];"); + } + else + { + string typeName = GetVarTypeName(context, field.Type); + + context.AppendLine($"{typeName} {field.Name};"); + } } + + context.LeaveScope($" {buffer.Name};"); + context.AppendLine(); } } @@ -759,39 +750,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"layout (location = {location}) patch out vec4 {name};"); } - private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) - { - bool needsSupportBlock = stage == ShaderStage.Fragment || - (context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()); - - if (!needsSupportBlock && scaleElements == 0) - { - return; - } - - context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}"); - context.EnterScope(); - - switch (stage) - { - case ShaderStage.Fragment: - case ShaderStage.Vertex: - context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};"); - context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];"); - context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};"); - context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};"); - break; - case ShaderStage.Compute: - context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];"); - break; - } - - context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{SupportBuffer.RenderScaleMaxCount}];"); - - context.LeaveScope(";"); - context.AppendLine(); - } - private static void AppendHelperFunction(CodeGenContext context, string filename) { string code = EmbeddedResources.ReadAllText(filename); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 3ab4814ce..fc3004a8f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -15,18 +15,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string DataName = "data"; - public const string SupportBlockName = "support_block"; - public const string SupportBlockAlphaTestName = "s_alpha_test"; - public const string SupportBlockIsBgraName = "s_is_bgra"; - public const string SupportBlockViewportInverse = "s_viewport_inverse"; - public const string SupportBlockFragmentScaleCount = "s_frag_scale_count"; - public const string SupportBlockRenderScaleName = "s_render_scale"; - public const string BlockSuffix = "block"; - public const string UniformNamePrefix = "c"; - public const string UniformNameSuffix = "data"; - public const string LocalMemoryName = "local_mem"; public const string SharedMemoryName = "shared_mem"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl index 4ebade5e3..08c625488 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl @@ -1,6 +1,6 @@ ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) { - float scale = s_render_scale[samplerIndex]; + float scale = support_buffer.s_render_scale[1 + samplerIndex]; if (scale == 1.0) { return inputVec; @@ -10,7 +10,7 @@ int Helper_TextureSizeUnscale(int size, int samplerIndex) { - float scale = s_render_scale[samplerIndex]; + float scale = support_buffer.s_render_scale[1 + samplerIndex]; if (scale == 1.0) { return size; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl index 6c670f91b..07a38a7a6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl @@ -1,6 +1,6 @@ ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) { - float scale = s_render_scale[1 + samplerIndex]; + float scale = support_buffer.s_render_scale[1 + samplerIndex]; if (scale == 1.0) { return inputVec; @@ -17,7 +17,7 @@ int Helper_TextureSizeUnscale(int size, int samplerIndex) { - float scale = abs(s_render_scale[1 + samplerIndex]); + float scale = abs(support_buffer.s_render_scale[1 + samplerIndex]); if (scale == 1.0) { return size; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl index 19eb119db..72baa441d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl @@ -1,6 +1,6 @@ ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) { - float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]); + float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]); if (scale == 1.0) { return inputVec; @@ -11,7 +11,7 @@ int Helper_TextureSizeUnscale(int size, int samplerIndex) { - float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]); + float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]); if (scale == 1.0) { return size; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 01bd11e59..24ea66d02 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -167,9 +167,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.Load: return Load(context, operation); - case Instruction.LoadConstant: - return LoadConstant(context, operation); - case Instruction.LoadLocal: return LoadLocal(context, operation); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 00478f6ab..71e40fe7c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -83,7 +83,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.ImageAtomic, InstType.Special); Add(Instruction.IsNan, InstType.CallUnary, "isnan"); Add(Instruction.Load, InstType.Special); - Add(Instruction.LoadConstant, InstType.Special); Add(Instruction.LoadLocal, InstType.Special); Add(Instruction.LoadShared, InstType.Special); Add(Instruction.LoadStorage, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 99519837b..ef5260d10 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -215,29 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return GenerateLoadOrStore(context, operation, isStore: false); } - public static string LoadConstant(CodeGenContext context, AstOperation operation) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - - string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true); - - var config = context.Config; - bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug(); - - if (src1 is AstOperand operand && operand.Type == OperandType.Constant) - { - bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing); - return OperandManager.GetConstantBufferName(operand.Value, offsetExpr, config.Stage, cbIndexable, indexElement); - } - else - { - string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, config.Stage, indexElement); - } - } - public static string LoadLocal(CodeGenContext context, AstOperation operation) { return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName); @@ -809,9 +786,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string varName; AggregateType varType; int srcIndex = 0; + int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount; switch (storageKind) { + case StorageKind.ConstantBuffer: + if (!(operation.GetSource(srcIndex++) is 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 = context.Config.Properties.ConstantBuffers[binding]; + + if (!(operation.GetSource(srcIndex++) is 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]; + varName = $"{buffer.Name}.{field.Name}"; + varType = field.Type; + break; + case StorageKind.Input: case StorageKind.InputPerPatch: case StorageKind.Output: @@ -864,40 +861,39 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions varName = $"gl_out[{expr}].{varName}"; } } - - int firstSrcIndex = srcIndex; - int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount; - - 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 if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output) - { - // GLSL requires that for tessellation control shader outputs, - // that the index expression must be *exactly* "gl_InvocationID", - // otherwise the compilation fails. - // TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR. - varName += "[gl_InvocationID]"; - } - else - { - varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]"; - } - } break; default: throw new InvalidOperationException($"Invalid storage kind {storageKind}."); } + int firstSrcIndex = srcIndex; + + 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 if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output) + { + // GLSL requires that for tessellation control shader outputs, + // that the index expression must be *exactly* "gl_InvocationID", + // otherwise the compilation fails. + // TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR. + varName += "[gl_InvocationID]"; + } + else + { + varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]"; + } + } + if (isStore) { varType &= AggregateType.ElementTypeMask; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs index 093ee2322..2a73b8b07 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs @@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32), IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location), IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32), - IoVariable.FragmentOutputIsBgra => (DefaultNames.SupportBlockIsBgraName, AggregateType.Array | AggregateType.Bool), IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool), @@ -46,8 +45,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config), IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"), IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"), - IoVariable.SupportBlockRenderScale => (DefaultNames.SupportBlockRenderScaleName, AggregateType.Array | AggregateType.FP32), - IoVariable.SupportBlockViewInverse => (DefaultNames.SupportBlockViewportInverse, AggregateType.Vector2 | AggregateType.FP32), IoVariable.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32), IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32), IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", AggregateType.Array | AggregateType.FP32), diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 92e833580..e34e4e076 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -36,63 +36,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { OperandType.Argument => GetArgumentName(operand.Value), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), - OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config), OperandType.LocalVariable => _locals[operand], OperandType.Undefined => DefaultNames.UndefinedName, _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") }; } - private static string GetConstantBufferName(AstOperand operand, ShaderConfig config) - { - return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)); - } - - public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable) - { - return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}"; - } - - private static string GetVec4Indexed(string vectorName, string indexExpr, bool indexElement) - { - if (indexElement) - { - return $"{vectorName}[{indexExpr}]"; - } - - string result = $"{vectorName}.x"; - for (int i = 1; i < 4; i++) - { - result = $"(({indexExpr}) == {i}) ? ({vectorName}.{GetSwizzleMask(i)}) : ({result})"; - } - return $"({result})"; - } - - public static string GetConstantBufferName(int slot, string offsetExpr, ShaderStage stage, bool cbIndexable, bool indexElement) - { - return GetVec4Indexed(GetUbName(stage, slot, cbIndexable) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); - } - - public static string GetConstantBufferName(string slotExpr, string offsetExpr, ShaderStage stage, bool indexElement) - { - return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); - } - - public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable) - { - if (cbIndexable) - { - return GetUbName(stage, NumberFormatter.FormatInt(slot, AggregateType.S32)); - } - - return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}"; - } - - private static string GetUbName(ShaderStage stage, string slotExpr) - { - return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}[{slotExpr}].{DefaultNames.DataName}"; - } - public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr) { return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr); @@ -168,6 +117,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { switch (operation.StorageKind) { + case StorageKind.ConstantBuffer: + if (!(operation.GetSource(0) is 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 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 = context.Config.Properties.ConstantBuffers[bindingIndex.Value]; + StructureField field = buffer.Type.Fields[fieldIndex.Value]; + + return field.Type & AggregateType.ElementTypeMask; + case StorageKind.Input: case StorageKind.InputPerPatch: case StorageKind.Output: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index ed292ef1a..0ef89b398 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -23,9 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public int InputVertices { get; } - public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>(); - public Instruction SupportBuffer { get; set; } - public Instruction UniformBuffersArray { get; set; } + public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>(); public Instruction StorageBuffersArray { get; set; } public Instruction LocalMemory { get; set; } public Instruction SharedMemory { get; set; } @@ -217,7 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { IrOperandType.Argument => GetArgument(type, operand), IrOperandType.Constant => GetConstant(type, operand), - IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand), IrOperandType.LocalVariable => GetLocal(type, operand), IrOperandType.Undefined => GetUndefined(type), _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") @@ -274,31 +271,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv }; } - public Instruction GetConstantBuffer(AggregateType type, AstOperand operand) - { - var i1 = Constant(TypeS32(), 0); - var i2 = Constant(TypeS32(), operand.CbufOffset >> 2); - var i3 = Constant(TypeU32(), operand.CbufOffset & 3); - - Instruction elemPointer; - - if (UniformBuffersArray != null) - { - var ubVariable = UniformBuffersArray; - var i0 = Constant(TypeS32(), operand.CbufSlot); - - elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i0, i1, i2, i3); - } - else - { - var ubVariable = UniformBuffers[operand.CbufSlot]; - - elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i1, i2, i3); - } - - return BitcastIfNeeded(type, AggregateType.FP32, Load(TypeFP32(), elemPointer)); - } - public Instruction GetLocalPointer(AstOperand local) { return _locals[local]; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 821da4773..7c242589e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -98,8 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv DeclareLocalMemory(context, localMemorySize); } - DeclareSupportBuffer(context); - DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors()); + DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors()); DeclareSamplers(context, context.Config.GetTextureDescriptors()); DeclareImages(context, context.Config.GetImageDescriptors()); @@ -127,84 +126,63 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return variable; } - private static void DeclareSupportBuffer(CodeGenContext context) + private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) { - if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable())) + HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>(); + + foreach (BufferDefinition buffer in buffers) { - return; - } + int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4; + int alignmentMask = alignment - 1; + int offset = 0; - var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount)); - var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4); - var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount)); + SpvInstruction[] structFieldTypes = new SpvInstruction[buffer.Type.Fields.Length]; + int[] structFieldOffsets = new int[buffer.Type.Fields.Length]; - context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize); - context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize); + for (int fieldIndex = 0; fieldIndex < buffer.Type.Fields.Length; fieldIndex++) + { + StructureField field = buffer.Type.Fields[fieldIndex]; + int fieldSize = (field.Type.GetSizeInBytes() + alignmentMask) & ~alignmentMask; - var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType); + structFieldTypes[fieldIndex] = context.GetType(field.Type, field.ArrayLength); + structFieldOffsets[fieldIndex] = offset; - context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset); - context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset); - context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset); - context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset); - context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset); - context.Decorate(supportBufferStructType, Decoration.Block); + if (field.Type.HasFlag(AggregateType.Array)) + { + // We can't decorate the type more than once. + if (decoratedTypes.Add(structFieldTypes[fieldIndex])) + { + context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize); + } - var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType); - var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform); + offset += fieldSize * field.ArrayLength; + } + else + { + offset += fieldSize; + } + } - context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0); - context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0); + var ubStructType = context.TypeStruct(false, structFieldTypes); - context.AddGlobalVariable(supportBufferVariable); + if (decoratedTypes.Add(ubStructType)) + { + context.Decorate(ubStructType, Decoration.Block); - context.SupportBuffer = supportBufferVariable; - } + for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++) + { + context.MemberDecorate(ubStructType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]); + } + } - private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors) - { - if (descriptors.Length == 0) - { - return; - } - - uint ubSize = Constants.ConstantBufferSize / 16; - - var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true); - context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16); - var ubStructType = context.TypeStruct(true, ubArrayType); - context.Decorate(ubStructType, Decoration.Block); - context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0); - - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)) - { - int count = descriptors.Max(x => x.Slot) + 1; - - var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count)); - var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType); + var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType); var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform); - context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u"); - context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0); - context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding); + context.Name(ubVariable, buffer.Name); + context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set); + context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding); context.AddGlobalVariable(ubVariable); - - context.UniformBuffersArray = ubVariable; - } - else - { - var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType); - - foreach (var descriptor in descriptors) - { - var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform); - - context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}"); - context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0); - context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding); - context.AddGlobalVariable(ubVariable); - context.UniformBuffers.Add(descriptor.Slot, ubVariable); - } + context.ConstantBuffers.Add(buffer.Binding, ubVariable); } } @@ -394,25 +372,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { foreach (var ioDefinition in info.IoDefinitions) { - var ioVariable = ioDefinition.IoVariable; - - // Those are actually from constant buffer, rather than being actual inputs or outputs, - // so we must ignore them here as they are declared as part of the support buffer. - // TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input"). - if (ioVariable == IoVariable.FragmentOutputIsBgra || - ioVariable == IoVariable.SupportBlockRenderScale || - ioVariable == IoVariable.SupportBlockViewInverse) - { - continue; - } - - bool isOutput = ioDefinition.StorageKind.IsOutput(); - bool isPerPatch = ioDefinition.StorageKind.IsPerPatch(); - PixelImap iq = PixelImap.Unused; if (context.Config.Stage == ShaderStage.Fragment) { + var ioVariable = ioDefinition.IoVariable; if (ioVariable == IoVariable.UserDefined) { iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType(); @@ -429,6 +393,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } + bool isOutput = ioDefinition.StorageKind.IsOutput(); + bool isPerPatch = ioDefinition.StorageKind.IsPerPatch(); + DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq); } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 6da8f29d1..fda0dc47c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -98,7 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.ImageStore, GenerateImageStore); Add(Instruction.IsNan, GenerateIsNan); Add(Instruction.Load, GenerateLoad); - Add(Instruction.LoadConstant, GenerateLoadConstant); Add(Instruction.LoadLocal, GenerateLoadLocal); Add(Instruction.LoadShared, GenerateLoadShared); Add(Instruction.LoadStorage, GenerateLoadStorage); @@ -313,10 +312,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv for (int i = 0; i < args.Length; i++) { - var operand = (AstOperand)operation.GetSource(i + 1); + var operand = operation.GetSource(i + 1); + if (i >= function.InArguments.Length) { - args[i] = context.GetLocalPointer(operand); + args[i] = context.GetLocalPointer((AstOperand)operand); } else { @@ -867,68 +867,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateLoadOrStore(context, operation, isStore: false); } - private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation) - { - var src1 = operation.GetSource(0); - var src2 = context.Get(AggregateType.S32, operation.GetSource(1)); - - var i1 = context.Constant(context.TypeS32(), 0); - var i2 = context.ShiftRightArithmetic(context.TypeS32(), src2, context.Constant(context.TypeS32(), 2)); - var i3 = context.BitwiseAnd(context.TypeS32(), src2, context.Constant(context.TypeS32(), 3)); - - SpvInstruction value = null; - - if (context.Config.GpuAccessor.QueryHostHasVectorIndexingBug()) - { - // Test for each component individually. - for (int i = 0; i < 4; i++) - { - var component = context.Constant(context.TypeS32(), i); - - SpvInstruction elemPointer; - if (context.UniformBuffersArray != null) - { - var ubVariable = context.UniformBuffersArray; - var i0 = context.Get(AggregateType.S32, src1); - - elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i0, i1, i2, component); - } - else - { - var ubVariable = context.UniformBuffers[((AstOperand)src1).Value]; - - elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i1, i2, component); - } - - SpvInstruction newValue = context.Load(context.TypeFP32(), elemPointer); - - value = value != null ? context.Select(context.TypeFP32(), context.IEqual(context.TypeBool(), i3, component), newValue, value) : newValue; - } - } - else - { - SpvInstruction elemPointer; - - if (context.UniformBuffersArray != null) - { - var ubVariable = context.UniformBuffersArray; - var i0 = context.Get(AggregateType.S32, src1); - - elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i0, i1, i2, i3); - } - else - { - var ubVariable = context.UniformBuffers[((AstOperand)src1).Value]; - - elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i1, i2, i3); - } - - value = context.Load(context.TypeFP32(), elemPointer); - } - - return new OperationResult(AggregateType.FP32, value); - } - private static OperationResult GenerateLoadLocal(CodeGenContext context, AstOperation operation) { return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory); @@ -1990,12 +1928,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { StorageKind storageKind = operation.StorageKind; - SpvInstruction pointer; + StorageClass storageClass; + SpvInstruction baseObj; AggregateType varType; int srcIndex = 0; switch (storageKind) { + case StorageKind.ConstantBuffer: + if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + if (!(operation.GetSource(srcIndex) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant) + { + throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value]; + StructureField field = buffer.Type.Fields[fieldIndex.Value]; + + storageClass = StorageClass.Uniform; + varType = field.Type & AggregateType.ElementTypeMask; + baseObj = context.ConstantBuffers[bindingIndex.Value]; + break; + case StorageKind.Input: case StorageKind.InputPerPatch: case StorageKind.Output: @@ -2038,33 +1996,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { varType = context.Config.GetFragmentOutputColorType(location); } - else if (ioVariable == IoVariable.FragmentOutputIsBgra) - { - var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeU32()); - var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 1), elemIndex); - varType = AggregateType.U32; - - break; - } - else if (ioVariable == IoVariable.SupportBlockRenderScale) - { - var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); - var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 4), elemIndex); - varType = AggregateType.FP32; - - break; - } - else if (ioVariable == IoVariable.SupportBlockViewInverse) - { - var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); - var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 2), elemIndex); - varType = AggregateType.FP32; - - break; - } else { (_, varType) = IoMap.GetSpirvBuiltIn(ioVariable); @@ -2072,55 +2003,57 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv varType &= AggregateType.ElementTypeMask; - int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex; - var storageClass = isOutput ? StorageClass.Output : StorageClass.Input; + storageClass = isOutput ? StorageClass.Output : StorageClass.Input; var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component); var dict = isPerPatch ? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch) : (isOutput ? context.Outputs : context.Inputs); - SpvInstruction baseObj = dict[ioDefinition]; - SpvInstruction e0, e1, e2; - - switch (inputsCount) - { - case 0: - pointer = baseObj; - break; - case 1: - e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0); - break; - case 2: - e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1); - break; - case 3: - e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); - pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2); - break; - default: - var indexes = new SpvInstruction[inputsCount]; - int index = 0; - - for (; index < inputsCount; srcIndex++, index++) - { - indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex)); - } - - pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes); - break; - } + baseObj = dict[ioDefinition]; break; default: throw new InvalidOperationException($"Invalid storage kind {storageKind}."); } + int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex; + SpvInstruction e0, e1, e2; + SpvInstruction pointer; + + switch (inputsCount) + { + case 0: + pointer = baseObj; + break; + case 1: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0); + break; + case 2: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1); + break; + case 3: + e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++)); + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2); + break; + default: + var indexes = new SpvInstruction[inputsCount]; + int index = 0; + + for (; index < inputsCount; srcIndex++, index++) + { + indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex)); + } + + pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes); + break; + } + if (isStore) { context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex))); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs index f6c218c67..c8b21e881 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (context.Config.Stage == ShaderStage.Vertex) { var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32()); - var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3)); + var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3)); var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer); scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount); @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1)); - var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex); + var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex); var scale = context.Load(context.TypeFP32(), scaleElemPointer); var ivector2Type = context.TypeVector(context.TypeS32(), 2); @@ -201,7 +201,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (context.Config.Stage == ShaderStage.Vertex) { var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32()); - var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3)); + var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3)); var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer); scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount); @@ -209,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1)); - var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex); + var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex); var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer)); var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f)); diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 1df387618..bb60d2746 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { // FragCoord X/Y must be divided by the render target scale, if resolution scaling is active, // because the shader code is not expecting scaled values. - res = context.FPDivide(res, context.Load(StorageKind.Input, IoVariable.SupportBlockRenderScale, null, Const(0))); + res = context.FPDivide(res, context.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.RenderScale), Const(0))); } else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug()) { diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index c73c6b2ac..6f5913eb3 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; +using System.Numerics; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -80,7 +81,6 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand addr = context.IAdd(srcA, Const(Imm16ToSInt(op.CbufOffset))); Operand wordOffset = context.ShiftRightU32(addr, Const(2)); - Operand bitOffset = GetBitOffset(context, addr); for (int index = 0; index < count; index++) { @@ -92,11 +92,11 @@ namespace Ryujinx.Graphics.Shader.Instructions } Operand offset = context.IAdd(wordOffset, Const(index)); - Operand value = context.LoadConstant(slot, offset); + Operand value = EmitLoadConstant(context, slot, offset); if (isSmallInt) { - value = ExtractSmallInt(context, (LsSize)op.LsSize, bitOffset, value); + value = ExtractSmallInt(context, (LsSize)op.LsSize, GetBitOffset(context, addr), value); } context.Copy(Register(dest), value); @@ -154,6 +154,39 @@ namespace Ryujinx.Graphics.Shader.Instructions EmitStore(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); } + private static Operand EmitLoadConstant(EmitterContext context, Operand slot, Operand offset) + { + Operand vecIndex = context.ShiftRightU32(offset, Const(2)); + Operand elemIndex = context.BitwiseAnd(offset, Const(3)); + + if (slot.Type == OperandType.Constant) + { + int binding = context.Config.ResourceManager.GetConstantBufferBinding(slot.Value); + return context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex); + } + else + { + Operand value = Const(0); + + uint cbUseMask = context.Config.GpuAccessor.QueryConstantBufferUse(); + + while (cbUseMask != 0) + { + int cbIndex = BitOperations.TrailingZeroCount(cbUseMask); + int binding = context.Config.ResourceManager.GetConstantBufferBinding(cbIndex); + + Operand isCurrent = context.ICompareEqual(slot, Const(cbIndex)); + Operand currentValue = context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex); + + value = context.ConditionalSelect(isCurrent, currentValue, value); + + cbUseMask &= ~(1u << cbIndex); + } + + return value; + } + } + private static Operand EmitAtomicOp( EmitterContext context, StorageKind storageKind, diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index d7c4a961c..817755bbe 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -79,7 +79,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation ImageAtomic, IsNan, Load, - LoadConstant, LoadGlobal, LoadLocal, LoadShared, diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs index a2163d14f..fb9b57bdd 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs @@ -15,7 +15,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation FragmentCoord, FragmentOutputColor, FragmentOutputDepth, - FragmentOutputIsBgra, // TODO: Remove and use constant buffer access. FrontColorDiffuse, FrontColorSpecular, FrontFacing, @@ -34,8 +33,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation SubgroupLaneId, SubgroupLeMask, SubgroupLtMask, - SupportBlockViewInverse, // TODO: Remove and use constant buffer access. - SupportBlockRenderScale, // TODO: Remove and use constant buffer access. TessellationCoord, TessellationLevelInner, TessellationLevelOuter, diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs index 1fc0035f2..473aa2e7b 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs @@ -15,9 +15,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public int Value { get; } - public int CbufSlot { get; } - public int CbufOffset { get; } - private AstOperand() { Defs = new HashSet<IAstNode>(); @@ -29,16 +26,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public AstOperand(Operand operand) : this() { Type = operand.Type; - - if (Type == OperandType.ConstantBuffer) - { - CbufSlot = operand.GetCbufSlot(); - CbufOffset = operand.GetCbufOffset(); - } - else - { - Value = operand.Value; - } + Value = operand.Value; } public AstOperand(OperandType type, int value = 0) : this() diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs new file mode 100644 index 000000000..5afebc75f --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + readonly struct BufferDefinition + { + public BufferLayout Layout { get; } + public int Set { get; } + public int Binding { get; } + public string Name { get; } + public StructureType Type { get; } + + public BufferDefinition(BufferLayout layout, int set, int binding, string name, StructureType type) + { + Layout = layout; + Set = set; + Binding = binding; + Name = name; + Type = type; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs new file mode 100644 index 000000000..43a866626 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + enum BufferLayout + { + Std140, + Std430 + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 8eccef237..ab8132540 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -90,7 +90,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ImageAtomic, AggregateType.S32); Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar); Add(Instruction.Load, AggregateType.FP32); - Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32); Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32); Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32); Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32); diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs index 38ed1584b..48060f6b9 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs @@ -24,7 +24,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { OperandType.Argument => AggregateType.S32, OperandType.Constant => AggregateType.S32, - OperandType.ConstantBuffer => AggregateType.FP32, OperandType.Undefined => AggregateType.S32, _ => throw new ArgumentException($"Invalid operand type \"{type}\".") }; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs new file mode 100644 index 000000000..061c89edd --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class ShaderProperties + { + private readonly Dictionary<int, BufferDefinition> _constantBuffers; + + public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers; + + public ShaderProperties() + { + _constantBuffers = new Dictionary<int, BufferDefinition>(); + } + + public void AddConstantBuffer(int binding, BufferDefinition definition) + { + _constantBuffers[binding] = definition; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs new file mode 100644 index 000000000..17f497386 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + struct StructureField + { + public AggregateType Type { get; } + public string Name { get; } + public int ArrayLength { get; } + + public StructureField(AggregateType type, string name, int arrayLength = 1) + { + Type = type; + Name = name; + ArrayLength = arrayLength; + } + } + + class StructureType + { + public StructureField[] Fields { get; } + + public StructureType(StructureField[] fields) + { + Fields = fields; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index b4ca8ee58..939a52f3e 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -73,27 +73,34 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Instruction inst = operation.Inst; StorageKind storageKind = operation.StorageKind; - if ((inst == Instruction.Load || inst == Instruction.Store) && storageKind.IsInputOrOutput()) + if (inst == Instruction.Load || inst == Instruction.Store) { - IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value; - bool isOutput = storageKind.IsOutput(); - bool perPatch = storageKind.IsPerPatch(); - int location = 0; - int component = 0; - - if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + if (storageKind.IsInputOrOutput()) { - location = operation.GetSource(1).Value; + IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value; + bool isOutput = storageKind.IsOutput(); + bool perPatch = storageKind.IsPerPatch(); + int location = 0; + int component = 0; - if (operation.SourcesCount > 2 && - operation.GetSource(2).Type == OperandType.Constant && - context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput)) + if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) { - component = operation.GetSource(2).Value; - } - } + location = operation.GetSource(1).Value; - context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component)); + if (operation.SourcesCount > 2 && + operation.GetSource(2).Type == OperandType.Constant && + context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput)) + { + component = operation.GetSource(2).Value; + } + } + + context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component)); + } + else if (storageKind == StorageKind.ConstantBuffer && operation.GetSource(0).Type == OperandType.Constant) + { + context.Config.ResourceManager.SetUsedConstantBufferBinding(operation.GetSource(0).Value); + } } bool vectorDest = IsVectorDestInst(inst); @@ -105,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr for (int index = 0; index < operation.SourcesCount; index++) { - sources[index] = context.GetOperand(operation.GetSource(index)); + sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index)); } for (int index = 0; index < outDestsCount; index++) diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index 68bbdeb14..c5ad3683a 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -298,6 +298,33 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return newTemp; } + public IAstNode GetOperandOrCbLoad(Operand operand) + { + if (operand.Type == OperandType.ConstantBuffer) + { + int cbufSlot = operand.GetCbufSlot(); + int cbufOffset = operand.GetCbufOffset(); + + int binding = Config.ResourceManager.GetConstantBufferBinding(cbufSlot); + int vecIndex = cbufOffset >> 2; + int elemIndex = cbufOffset & 3; + + Config.ResourceManager.SetUsedConstantBufferBinding(binding); + + IAstNode[] sources = new IAstNode[] + { + new AstOperand(OperandType.Constant, binding), + new AstOperand(OperandType.Constant, 0), + new AstOperand(OperandType.Constant, vecIndex), + new AstOperand(OperandType.Constant, elemIndex) + }; + + return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, sources, sources.Length); + } + + return GetOperand(operand); + } + public AstOperand GetOperand(Operand operand) { if (operand == null) @@ -307,11 +334,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (operand.Type != OperandType.LocalVariable) { - if (operand.Type == OperandType.ConstantBuffer) - { - Config.SetUsedConstantBuffer(operand.GetCbufSlot()); - } - return new AstOperand(operand); } diff --git a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs index 5fe993278..5eb7fe467 100644 --- a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs +++ b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs @@ -1,4 +1,6 @@ using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System.Runtime.CompilerServices; namespace Ryujinx.Graphics.Shader @@ -11,8 +13,20 @@ namespace Ryujinx.Graphics.Shader public T W; } + enum SupportBufferField + { + // Must match the order of the fields on the struct. + FragmentAlphaTest, + FragmentIsBgra, + ViewportInverse, + FragmentRenderScaleCount, + RenderScale + } + public struct SupportBuffer { + internal const int Binding = 0; + public static int FieldSize; public static int RequiredSize; @@ -47,6 +61,18 @@ namespace Ryujinx.Graphics.Shader ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; } + internal static StructureType GetStructureType() + { + return new StructureType(new[] + { + new StructureField(AggregateType.U32, "s_alpha_test"), + new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount), + new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"), + new StructureField(AggregateType.S32, "s_frag_scale_count"), + new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount) + }); + } + public Vector4<int> FragmentAlphaTest; public Array8<Vector4<int>> FragmentIsBgra; public Vector4<float> ViewportInverse; diff --git a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs index 24993e00d..b1b40f652 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs @@ -22,4 +22,35 @@ Array = 1 << 10 } + + static class AggregateTypeExtensions + { + public static int GetSizeInBytes(this AggregateType type) + { + int elementSize = (type & AggregateType.ElementTypeMask) switch + { + AggregateType.Bool or + AggregateType.FP32 or + AggregateType.S32 or + AggregateType.U32 => 4, + AggregateType.FP64 => 8, + _ => 0 + }; + + switch (type & AggregateType.ElementCountMask) + { + case AggregateType.Vector2: + elementSize *= 2; + break; + case AggregateType.Vector3: + elementSize *= 3; + break; + case AggregateType.Vector4: + elementSize *= 4; + break; + } + + return elementSize; + } + } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 112baccf6..0c51b16f4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -234,8 +234,8 @@ namespace Ryujinx.Graphics.Shader.Translation { Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)); Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)); - Operand xScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(0)); - Operand yScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(1)); + Operand xScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(0)); + Operand yScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(1)); Operand negativeOne = ConstF(-1.0f); this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne)); @@ -420,7 +420,7 @@ namespace Ryujinx.Graphics.Shader.Translation // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL). if (!supportsBgra && (component == 0 || component == 2)) { - Operand isBgra = this.Load(StorageKind.Input, IoVariable.FragmentOutputIsBgra, null, Const(rtIndex)); + Operand isBgra = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.FragmentIsBgra), Const(rtIndex)); Operand lblIsBgra = Label(); Operand lblEnd = Label(); diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 937482495..e41a28f12 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -549,11 +549,31 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(fpType | Instruction.IsNan, Local(), a); } + public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding) + { + return context.Add(Instruction.Load, storageKind, Local(), Const(binding)); + } + + public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0) + { + return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0); + } + + public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1) + { + return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0, e1); + } + + public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand e2) + { + return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0, e1, e2); + } + public static Operand Load(this EmitterContext context, StorageKind storageKind, IoVariable ioVariable, Operand primVertex = null) { return primVertex != null - ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex) - : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable)); + ? context.Load(storageKind, (int)ioVariable, primVertex) + : context.Load(storageKind, (int)ioVariable); } public static Operand Load( @@ -564,8 +584,8 @@ namespace Ryujinx.Graphics.Shader.Translation Operand elemIndex) { return primVertex != null - ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, elemIndex) - : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), elemIndex); + ? context.Load(storageKind, (int)ioVariable, primVertex, elemIndex) + : context.Load(storageKind, (int)ioVariable, elemIndex); } public static Operand Load( @@ -577,22 +597,8 @@ namespace Ryujinx.Graphics.Shader.Translation Operand elemIndex) { return primVertex != null - ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, arrayIndex, elemIndex) - : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), arrayIndex, elemIndex); - } - - public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b) - { - if (a.Type == OperandType.Constant) - { - context.Config.SetUsedConstantBuffer(a.Value); - } - else - { - context.Config.SetUsedFeature(FeatureFlags.CbIndexing); - } - - return context.Add(Instruction.LoadConstant, Local(), a, b); + ? context.Load(storageKind, (int)ioVariable, primVertex, arrayIndex, elemIndex) + : context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex); } public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b) diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index c035f212d..e55ed13da 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Shader.Translation InstanceId = 1 << 3, DrawParameters = 1 << 4, RtLayer = 1 << 5, - CbIndexing = 1 << 6, IaIndexing = 1 << 7, OaIndexing = 1 << 8, FixedFuncAttr = 1 << 9 diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs index ca46a1f53..9a3ae1b8f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs @@ -32,25 +32,49 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - if (handleAsgOp.Inst != Instruction.LoadConstant) + if (handleAsgOp.Inst != Instruction.Load || + handleAsgOp.StorageKind != StorageKind.ConstantBuffer || + handleAsgOp.SourcesCount != 4) { continue; } Operand ldcSrc0 = handleAsgOp.GetSource(0); + + if (ldcSrc0.Type != OperandType.Constant || + !config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) || + src0CbufSlot != 2) + { + continue; + } + Operand ldcSrc1 = handleAsgOp.GetSource(1); - if (ldcSrc0.Type != OperandType.Constant || ldcSrc0.Value != 2) + // We expect field index 0 to be accessed. + if (ldcSrc1.Type != OperandType.Constant || ldcSrc1.Value != 0) { continue; } - if (!(ldcSrc1.AsgOp is Operation shrOp) || shrOp.Inst != Instruction.ShiftRightU32) + Operand ldcSrc2 = handleAsgOp.GetSource(2); + + // FIXME: This is missing some checks, for example, a check to ensure that the shift value is 2. + // Might be not worth fixing since if that doesn't kick in, the result will be no texture + // to access anyway which is also wrong. + // Plus this whole transform is fundamentally flawed as-is since we have no way to know the array size. + // Eventually, this should be entirely removed in favor of a implementation that supports true bindless + // texture access. + if (!(ldcSrc2.AsgOp is Operation shrOp) || shrOp.Inst != Instruction.ShiftRightU32) { continue; } - if (!(shrOp.GetSource(0).AsgOp is Operation addOp) || addOp.Inst != Instruction.Add) + if (!(shrOp.GetSource(0).AsgOp is Operation shrOp2) || shrOp2.Inst != Instruction.ShiftRightU32) + { + continue; + } + + if (!(shrOp2.GetSource(0).AsgOp is Operation addOp) || addOp.Inst != Instruction.Add) { continue; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs index 6729f0770..4caadb737 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class ConstantFolding { - public static void RunPass(Operation operation) + public static void RunPass(ShaderConfig config, Operation operation) { if (!AreAllSourcesConstant(operation)) { @@ -153,8 +153,21 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations EvaluateFPUnary(operation, (x) => float.IsNaN(x)); break; - case Instruction.LoadConstant: - operation.TurnIntoCopy(Cbuf(operation.GetSource(0).Value, operation.GetSource(1).Value)); + case Instruction.Load: + if (operation.StorageKind == StorageKind.ConstantBuffer && operation.SourcesCount == 4) + { + int binding = operation.GetSource(0).Value; + int fieldIndex = operation.GetSource(1).Value; + + if (config.ResourceManager.TryGetConstantBufferSlot(binding, out int cbufSlot) && fieldIndex == 0) + { + int vecIndex = operation.GetSource(2).Value; + int elemIndex = operation.GetSource(3).Value; + int cbufOffset = vecIndex * 4 + elemIndex; + + operation.TurnIntoCopy(Cbuf(cbufSlot, cbufOffset)); + } + } break; case Instruction.Maximum: diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 774d3e36d..7758b4c61 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -347,21 +347,23 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return wordOffset; } - Operand[] sources = new Operand[operation.SourcesCount]; + Operand cbufOffset = GetCbufOffset(); + Operand vecIndex = Local(); + Operand elemIndex = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ShiftRightU32, 0, vecIndex, cbufOffset, Const(2))); + node.List.AddBefore(node, new Operation(Instruction.BitwiseAnd, 0, elemIndex, cbufOffset, Const(3))); + + Operand[] sources = new Operand[4]; int cbSlot = UbeFirstCbuf + storageIndex; - sources[0] = Const(cbSlot); - sources[1] = GetCbufOffset(); + sources[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot)); + sources[1] = Const(0); + sources[2] = vecIndex; + sources[3] = elemIndex; - config.SetUsedConstantBuffer(cbSlot); - - for (int index = 2; index < operation.SourcesCount; index++) - { - sources[index] = operation.GetSource(index); - } - - Operation ldcOp = new Operation(Instruction.LoadConstant, operation.Dest, sources); + Operation ldcOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, operation.Dest, sources); for (int index = 0; index < operation.SourcesCount; index++) { diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index b41e47e42..b126e2c48 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { public static void RunPass(BasicBlock[] blocks, ShaderConfig config) { - RunOptimizationPasses(blocks); + RunOptimizationPasses(blocks, config); int sbUseMask = 0; int ubeUseMask = 0; @@ -31,10 +31,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask); // Run optimizations one last time to remove any code that is now optimizable after above passes. - RunOptimizationPasses(blocks); + RunOptimizationPasses(blocks, config); } - private static void RunOptimizationPasses(BasicBlock[] blocks) + private static void RunOptimizationPasses(BasicBlock[] blocks, ShaderConfig config) { bool modified; @@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - ConstantFolding.RunPass(operation); + ConstantFolding.RunPass(config, operation); Simplification.RunPass(operation); if (DestIsLocalVar(operation)) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs new file mode 100644 index 000000000..b31790d34 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -0,0 +1,126 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Ryujinx.Graphics.Shader.Translation +{ + class ResourceManager + { + private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; + + private readonly IGpuAccessor _gpuAccessor; + private readonly ShaderProperties _properties; + private readonly string _stagePrefix; + + private readonly int[] _cbSlotToBindingMap; + + private readonly HashSet<int> _usedConstantBufferBindings; + + public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties) + { + _gpuAccessor = gpuAccessor; + _properties = properties; + _stagePrefix = GetShaderStagePrefix(stage); + + _cbSlotToBindingMap = new int[18]; + _cbSlotToBindingMap.AsSpan().Fill(-1); + + _usedConstantBufferBindings = new HashSet<int>(); + + properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); + } + + public int GetConstantBufferBinding(int slot) + { + int binding = _cbSlotToBindingMap[slot]; + if (binding < 0) + { + binding = _gpuAccessor.QueryBindingConstantBuffer(slot); + _cbSlotToBindingMap[slot] = binding; + string slotNumber = slot.ToString(CultureInfo.InvariantCulture); + AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}"); + } + + return binding; + } + + public bool TryGetConstantBufferSlot(int binding, out int slot) + { + for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++) + { + if (_cbSlotToBindingMap[slot] == binding) + { + return true; + } + } + + slot = 0; + return false; + } + + public void SetUsedConstantBufferBinding(int binding) + { + _usedConstantBufferBindings.Add(binding); + } + + public BufferDescriptor[] GetConstantBufferDescriptors() + { + var descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count]; + + int descriptorIndex = 0; + + for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++) + { + int binding = _cbSlotToBindingMap[slot]; + + if (binding >= 0 && _usedConstantBufferBindings.Contains(binding)) + { + descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot); + } + } + + if (descriptors.Length != descriptorIndex) + { + Array.Resize(ref descriptors, descriptorIndex); + } + + return descriptors; + } + + private void AddNewConstantBuffer(int binding, string name) + { + StructureType type = new StructureType(new[] + { + new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16) + }); + + _properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); + } + + public void InheritFrom(ResourceManager other) + { + for (int i = 0; i < other._cbSlotToBindingMap.Length; i++) + { + int binding = other._cbSlotToBindingMap[i]; + + if (binding >= 0) + { + _cbSlotToBindingMap[i] = binding; + } + } + } + + public static string GetShaderStagePrefix(ShaderStage stage) + { + uint index = (uint)stage; + + if (index >= _stagePrefixes.Length) + { + return "invalid"; + } + + return _stagePrefixes[index]; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 8167efc1d..711661c9b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -1,7 +1,6 @@ -using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Numerics; @@ -16,6 +15,7 @@ namespace Ryujinx.Graphics.Shader.Translation { bool isVertexShader = config.Stage == ShaderStage.Vertex; bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); + bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug(); bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat(); for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) @@ -45,6 +45,11 @@ namespace Ryujinx.Graphics.Shader.Translation } } + if (hasVectorIndexingBug) + { + InsertVectorComponentSelect(node, config); + } + LinkedListNode<INode> nextNode = node.Next; if (operation is TextureOperation texOp) @@ -71,6 +76,84 @@ namespace Ryujinx.Graphics.Shader.Translation } } + private static void InsertVectorComponentSelect(LinkedListNode<INode> node, ShaderConfig config) + { + Operation operation = (Operation)node.Value; + + if (operation.Inst != Instruction.Load || + operation.StorageKind != StorageKind.ConstantBuffer || + operation.SourcesCount < 3) + { + return; + } + + Operand bindingIndex = operation.GetSource(0); + Operand fieldIndex = operation.GetSource(1); + Operand elemIndex = operation.GetSource(operation.SourcesCount - 1); + + if (bindingIndex.Type != OperandType.Constant || + fieldIndex.Type != OperandType.Constant || + elemIndex.Type == OperandType.Constant) + { + return; + } + + BufferDefinition buffer = config.Properties.ConstantBuffers[bindingIndex.Value]; + StructureField field = buffer.Type.Fields[fieldIndex.Value]; + + int elemCount = (field.Type & AggregateType.ElementCountMask) switch + { + AggregateType.Vector2 => 2, + AggregateType.Vector3 => 3, + AggregateType.Vector4 => 4, + _ => 1 + }; + + if (elemCount == 1) + { + return; + } + + Operand result = null; + + for (int i = 0; i < elemCount; i++) + { + Operand value = Local(); + Operand[] inputs = new Operand[operation.SourcesCount]; + + for (int srcIndex = 0; srcIndex < inputs.Length - 1; srcIndex++) + { + inputs[srcIndex] = operation.GetSource(srcIndex); + } + + inputs[inputs.Length - 1] = Const(i); + + Operation loadOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, value, inputs); + + node.List.AddBefore(node, loadOp); + + if (i == 0) + { + result = value; + } + else + { + Operand isCurrentIndex = Local(); + Operand selection = Local(); + + Operation compareOp = new Operation(Instruction.CompareEqual, isCurrentIndex, new Operand[] { elemIndex, Const(i) }); + Operation selectOp = new Operation(Instruction.ConditionalSelect, selection, new Operand[] { isCurrentIndex, value, result }); + + node.List.AddBefore(node, compareOp); + node.List.AddBefore(node, selectOp); + + result = selection; + } + } + + operation.TurnIntoCopy(result); + } + private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config) { Operation operation = (Operation)node.Value; @@ -90,6 +173,15 @@ namespace Ryujinx.Graphics.Shader.Translation return local; } + Operand PrependStorageOperation(Instruction inst, StorageKind storageKind, params Operand[] sources) + { + Operand local = Local(); + + node.List.AddBefore(node, new Operation(inst, storageKind, local, sources)); + + return local; + } + Operand PrependExistingOperation(Operation operation) { Operand local = Local(); @@ -204,8 +296,6 @@ namespace Ryujinx.Graphics.Shader.Translation cbeUseMask &= ~(1 << slot); - config.SetUsedConstantBuffer(cbSlot); - Operand previousResult = PrependExistingOperation(storageOp); int cbOffset = GetConstantUbeOffset(slot); @@ -216,18 +306,17 @@ namespace Ryujinx.Graphics.Shader.Translation Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst); Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2)); + Operand vecIndex = PrependOperation(Instruction.ShiftRightU32, cbIndex, Const(2)); + Operand elemIndex = PrependOperation(Instruction.BitwiseAnd, cbIndex, Const(3)); - Operand[] sourcesCb = new Operand[operation.SourcesCount]; + Operand[] sourcesCb = new Operand[4]; - sourcesCb[0] = Const(cbSlot); - sourcesCb[1] = cbIndex; + sourcesCb[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot)); + sourcesCb[1] = Const(0); + sourcesCb[2] = vecIndex; + sourcesCb[3] = elemIndex; - for (int index = 2; index < operation.SourcesCount; index++) - { - sourcesCb[index] = operation.GetSource(index); - } - - Operand ldcResult = PrependOperation(Instruction.LoadConstant, sourcesCb); + Operand ldcResult = PrependStorageOperation(Instruction.Load, StorageKind.ConstantBuffer, sourcesCb); storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index ae60bcc6c..775607972 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -39,6 +39,10 @@ namespace Ryujinx.Graphics.Shader.Translation public TranslationOptions Options { get; } + public ShaderProperties Properties { get; } + + public ResourceManager ResourceManager { get; } + public bool TransformFeedbackEnabled { get; } private TransformFeedbackOutput[] _transformFeedbackOutputs; @@ -109,7 +113,6 @@ namespace Ryujinx.Graphics.Shader.Translation public int AccessibleStorageBuffersMask { get; private set; } public int AccessibleConstantBuffersMask { get; private set; } - private int _usedConstantBuffers; private int _usedStorageBuffers; private int _usedStorageBuffersWrite; @@ -128,20 +131,17 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly Dictionary<int, int> _sbSlots; private readonly Dictionary<int, int> _sbSlotsReverse; - private BufferDescriptor[] _cachedConstantBufferDescriptors; private BufferDescriptor[] _cachedStorageBufferDescriptors; private TextureDescriptor[] _cachedTextureDescriptors; private TextureDescriptor[] _cachedImageDescriptors; - private int _firstConstantBufferBinding; private int _firstStorageBufferBinding; - public int FirstConstantBufferBinding => _firstConstantBufferBinding; public int FirstStorageBufferBinding => _firstStorageBufferBinding; - public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options) + public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options) { - Stage = ShaderStage.Compute; + Stage = stage; GpuAccessor = gpuAccessor; Options = options; @@ -158,6 +158,9 @@ namespace Ryujinx.Graphics.Shader.Translation _sbSlots = new Dictionary<int, int>(); _sbSlotsReverse = new Dictionary<int, int>(); + + Properties = new ShaderProperties(); + ResourceManager = new ResourceManager(stage, gpuAccessor, Properties); } public ShaderConfig( @@ -165,9 +168,8 @@ namespace Ryujinx.Graphics.Shader.Translation OutputTopology outputTopology, int maxOutputVertices, IGpuAccessor gpuAccessor, - TranslationOptions options) : this(gpuAccessor, options) + TranslationOptions options) : this(stage, gpuAccessor, options) { - Stage = stage; ThreadsPerInputPrimitive = 1; OutputTopology = outputTopology; MaxOutputVertices = maxOutputVertices; @@ -179,9 +181,8 @@ namespace Ryujinx.Graphics.Shader.Translation } } - public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options) + public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options) { - Stage = header.Stage; GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive; OutputTopology = header.OutputTopology; @@ -428,12 +429,13 @@ namespace Ryujinx.Graphics.Shader.Translation public void InheritFrom(ShaderConfig other) { + ResourceManager.InheritFrom(other.ResourceManager); + ClipDistancesWritten |= other.ClipDistancesWritten; UsedFeatures |= other.UsedFeatures; UsedInputAttributes |= other.UsedInputAttributes; UsedOutputAttributes |= other.UsedOutputAttributes; - _usedConstantBuffers |= other._usedConstantBuffers; _usedStorageBuffers |= other._usedStorageBuffers; _usedStorageBuffersWrite |= other._usedStorageBuffersWrite; @@ -641,11 +643,6 @@ namespace Ryujinx.Graphics.Shader.Translation AccessibleConstantBuffersMask = ubeMask; } - public void SetUsedConstantBuffer(int slot) - { - _usedConstantBuffers |= 1 << slot; - } - public void SetUsedStorageBuffer(int slot, bool write) { int mask = 1 << slot; @@ -762,27 +759,6 @@ namespace Ryujinx.Graphics.Shader.Translation return meta; } - public BufferDescriptor[] GetConstantBufferDescriptors() - { - if (_cachedConstantBufferDescriptors != null) - { - return _cachedConstantBufferDescriptors; - } - - int usedMask = _usedConstantBuffers; - - if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing)) - { - usedMask |= (int)GpuAccessor.QueryConstantBufferUse(); - } - - return _cachedConstantBufferDescriptors = GetUniformBufferDescriptors( - usedMask, - UsedFeatures.HasFlag(FeatureFlags.CbIndexing), - out _firstConstantBufferBinding, - GpuAccessor.QueryBindingConstantBuffer); - } - public BufferDescriptor[] GetStorageBufferDescriptors() { if (_cachedStorageBufferDescriptors != null) @@ -798,47 +774,6 @@ namespace Ryujinx.Graphics.Shader.Translation GpuAccessor.QueryBindingStorageBuffer); } - private static BufferDescriptor[] GetUniformBufferDescriptors(int usedMask, bool isArray, out int firstBinding, Func<int, int> getBindingCallback) - { - firstBinding = 0; - int lastSlot = -1; - bool hasFirstBinding = false; - var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)]; - - for (int i = 0; i < descriptors.Length; i++) - { - int slot = BitOperations.TrailingZeroCount(usedMask); - - if (isArray) - { - // The next array entries also consumes bindings, even if they are unused. - for (int j = lastSlot + 1; j < slot; j++) - { - int binding = getBindingCallback(j); - - if (!hasFirstBinding) - { - firstBinding = binding; - hasFirstBinding = true; - } - } - } - - lastSlot = slot; - descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot); - - if (!hasFirstBinding) - { - firstBinding = descriptors[i].Binding; - hasFirstBinding = true; - } - - usedMask &= ~(1 << slot); - } - - return descriptors; - } - private BufferDescriptor[] GetStorageBufferDescriptors( int usedMask, int writtenMask, @@ -1009,7 +944,7 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None) { return new ShaderProgramInfo( - GetConstantBufferDescriptors(), + ResourceManager.GetConstantBufferDescriptors(), GetStorageBufferDescriptors(), GetTextureDescriptors(), GetImageDescriptors(), diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 77d3b568e..87d97e52e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (options.Flags.HasFlag(TranslationFlags.Compute)) { - config = new ShaderConfig(gpuAccessor, options); + config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options); program = Decoder.Decode(config, address); } From 5626f2ca1c49342b20772224f956147df6957b5a Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 21 May 2023 14:04:21 -0300 Subject: [PATCH 552/737] Replace ShaderBindings with new ResourceLayout structure for Vulkan (#5025) * Introduce ResourceLayout * Part 1: Use new ResourceSegments array on UpdateAndBind * Part 2: Use ResourceLayout to build PipelineLayout * Delete old code * XML docs * Fix shader cache load NRE * Fix typo --- src/Ryujinx.Graphics.GAL/ResourceLayout.cs | 179 +++++++++++ src/Ryujinx.Graphics.GAL/ShaderBindings.cs | 24 -- src/Ryujinx.Graphics.GAL/ShaderInfo.cs | 7 +- src/Ryujinx.Graphics.GAL/ShaderSource.cs | 8 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 9 +- .../DiskCache/ParallelDiskCacheLoader.cs | 13 +- .../DiskCache/ShaderBinarySerializer.cs | 17 +- .../Shader/GpuAccessorBase.cs | 6 +- .../Shader/ShaderCache.cs | 34 +-- .../Shader/ShaderInfoBuilder.cs | 260 ++++++++++++++++ .../DescriptorSetUpdater.cs | 219 +++++++------- .../Effects/FsrScalingFilter.cs | 30 +- .../Effects/FxaaPostProcessingEffect.cs | 13 +- .../Effects/SmaaPostProcessingEffect.cs | 42 +-- src/Ryujinx.Graphics.Vulkan/EnumConversion.cs | 52 +++- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 198 ++++++------- .../PipelineLayoutCache.cs | 99 +++++-- .../PipelineLayoutCacheEntry.cs | 26 +- .../PipelineLayoutFactory.cs | 277 +++--------------- .../ResourceBindingSegment.cs | 22 ++ .../ResourceLayoutBuilder.cs | 67 +++++ src/Ryujinx.Graphics.Vulkan/Shader.cs | 3 - .../ShaderCollection.cs | 106 +++++-- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 13 +- 24 files changed, 1047 insertions(+), 677 deletions(-) create mode 100644 src/Ryujinx.Graphics.GAL/ResourceLayout.cs delete mode 100644 src/Ryujinx.Graphics.GAL/ShaderBindings.cs create mode 100644 src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs create mode 100644 src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs create mode 100644 src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs new file mode 100644 index 000000000..3cde281fc --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.ObjectModel; + +namespace Ryujinx.Graphics.GAL +{ + public enum ResourceType : byte + { + UniformBuffer, + StorageBuffer, + Texture, + Sampler, + TextureAndSampler, + Image, + BufferTexture, + BufferImage + } + + public enum ResourceAccess : byte + { + None = 0, + Read = 1, + Write = 2, + ReadWrite = Read | Write + } + + [Flags] + public enum ResourceStages : byte + { + None = 0, + Compute = 1 << 0, + Vertex = 1 << 1, + TessellationControl = 1 << 2, + TessellationEvaluation = 1 << 3, + Geometry = 1 << 4, + Fragment = 1 << 5 + } + + public readonly struct ResourceDescriptor : IEquatable<ResourceDescriptor> + { + public int Binding { get; } + public int Count { get; } + public ResourceType Type { get; } + public ResourceStages Stages { get; } + + public ResourceDescriptor(int binding, int count, ResourceType type, ResourceStages stages) + { + Binding = binding; + Count = count; + Type = type; + Stages = stages; + } + + public override int GetHashCode() + { + return HashCode.Combine(Binding, Count, Type, Stages); + } + + public override bool Equals(object obj) + { + return obj is ResourceDescriptor other && Equals(other); + } + + public bool Equals(ResourceDescriptor other) + { + return Binding == other.Binding && Count == other.Count && Type == other.Type && Stages == other.Stages; + } + } + + public readonly struct ResourceUsage : IEquatable<ResourceUsage> + { + public int Binding { get; } + public ResourceType Type { get; } + public ResourceStages Stages { get; } + public ResourceAccess Access { get; } + + public ResourceUsage(int binding, ResourceType type, ResourceStages stages, ResourceAccess access) + { + Binding = binding; + Type = type; + Stages = stages; + Access = access; + } + + public override int GetHashCode() + { + return HashCode.Combine(Binding, Type, Stages, Access); + } + + public override bool Equals(object obj) + { + return obj is ResourceUsage other && Equals(other); + } + + public bool Equals(ResourceUsage other) + { + return Binding == other.Binding && Type == other.Type && Stages == other.Stages && Access == other.Access; + } + } + + public readonly struct ResourceDescriptorCollection + { + public ReadOnlyCollection<ResourceDescriptor> Descriptors { get; } + + public ResourceDescriptorCollection(ReadOnlyCollection<ResourceDescriptor> descriptors) + { + Descriptors = descriptors; + } + + public override int GetHashCode() + { + HashCode hasher = new HashCode(); + + if (Descriptors != null) + { + foreach (var descriptor in Descriptors) + { + hasher.Add(descriptor); + } + } + + return hasher.ToHashCode(); + } + + public override bool Equals(object obj) + { + return obj is ResourceDescriptorCollection other && Equals(other); + } + + public bool Equals(ResourceDescriptorCollection other) + { + if ((Descriptors == null) != (other.Descriptors == null)) + { + return false; + } + + if (Descriptors != null) + { + if (Descriptors.Count != other.Descriptors.Count) + { + return false; + } + + for (int index = 0; index < Descriptors.Count; index++) + { + if (!Descriptors[index].Equals(other.Descriptors[index])) + { + return false; + } + } + } + + return true; + } + } + + public readonly struct ResourceUsageCollection + { + public ReadOnlyCollection<ResourceUsage> Usages { get; } + + public ResourceUsageCollection(ReadOnlyCollection<ResourceUsage> usages) + { + Usages = usages; + } + } + + public readonly struct ResourceLayout + { + public ReadOnlyCollection<ResourceDescriptorCollection> Sets { get; } + public ReadOnlyCollection<ResourceUsageCollection> SetUsages { get; } + + public ResourceLayout( + ReadOnlyCollection<ResourceDescriptorCollection> sets, + ReadOnlyCollection<ResourceUsageCollection> setUsages) + { + Sets = sets; + SetUsages = setUsages; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/ShaderBindings.cs b/src/Ryujinx.Graphics.GAL/ShaderBindings.cs deleted file mode 100644 index 6ab293829..000000000 --- a/src/Ryujinx.Graphics.GAL/ShaderBindings.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Graphics.GAL -{ - public readonly struct ShaderBindings - { - public IReadOnlyCollection<int> UniformBufferBindings { get; } - public IReadOnlyCollection<int> StorageBufferBindings { get; } - public IReadOnlyCollection<int> TextureBindings { get; } - public IReadOnlyCollection<int> ImageBindings { get; } - - public ShaderBindings( - IReadOnlyCollection<int> uniformBufferBindings, - IReadOnlyCollection<int> storageBufferBindings, - IReadOnlyCollection<int> textureBindings, - IReadOnlyCollection<int> imageBindings) - { - UniformBufferBindings = uniformBufferBindings; - StorageBufferBindings = storageBufferBindings; - TextureBindings = textureBindings; - ImageBindings = imageBindings; - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs index b4c871178..643f1bc5f 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -3,19 +3,22 @@ namespace Ryujinx.Graphics.GAL public struct ShaderInfo { public int FragmentOutputMap { get; } + public ResourceLayout ResourceLayout { get; } public ProgramPipelineState? State { get; } public bool FromCache { get; set; } - public ShaderInfo(int fragmentOutputMap, ProgramPipelineState state, bool fromCache = false) + public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false) { FragmentOutputMap = fragmentOutputMap; + ResourceLayout = resourceLayout; State = state; FromCache = fromCache; } - public ShaderInfo(int fragmentOutputMap, bool fromCache = false) + public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false) { FragmentOutputMap = fragmentOutputMap; + ResourceLayout = resourceLayout; State = null; FromCache = fromCache; } diff --git a/src/Ryujinx.Graphics.GAL/ShaderSource.cs b/src/Ryujinx.Graphics.GAL/ShaderSource.cs index 91d3a6325..773c0a8a1 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderSource.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderSource.cs @@ -7,24 +7,22 @@ namespace Ryujinx.Graphics.GAL { public string Code { get; } public byte[] BinaryCode { get; } - public ShaderBindings Bindings { get; } public ShaderStage Stage { get; } public TargetLanguage Language { get; } - public ShaderSource(string code, byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) + public ShaderSource(string code, byte[] binaryCode, ShaderStage stage, TargetLanguage language) { Code = code; BinaryCode = binaryCode; - Bindings = bindings; Stage = stage; Language = language; } - public ShaderSource(string code, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(code, null, bindings, stage, language) + public ShaderSource(string code, ShaderStage stage, TargetLanguage language) : this(code, null, stage, language) { } - public ShaderSource(byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, bindings, stage, language) + public ShaderSource(byte[] binaryCode, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, stage, language) { } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b4764d578..9f263e9d7 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -368,12 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache if (hostCode != null) { - bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null; - int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1; - - ShaderInfo shaderInfo = specState.PipelineState.HasValue - ? new ShaderInfo(fragmentOutputMap, specState.PipelineState.Value, fromCache: true) - : new ShaderInfo(fragmentOutputMap, fromCache: true); + ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(context, shaders, specState.PipelineState); IProgram hostProgram; @@ -385,6 +380,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } else { + bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null; + hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo); } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 58e5c7b10..d80518b10 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -491,23 +491,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - int fragmentOutputMap = -1; + ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context); for (int index = 0; index < compilation.TranslatedStages.Length; index++) { ShaderProgram shader = compilation.TranslatedStages[index]; shaderSources[index] = CreateShaderSource(shader); - - if (shader.Info.Stage == ShaderStage.Fragment) - { - fragmentOutputMap = shader.Info.FragmentOutputMap; - } + shaderInfoBuilder.AddStageInfo(shader.Info); } - ShaderInfo shaderInfo = compilation.SpecializationState.PipelineState.HasValue - ? new ShaderInfo(fragmentOutputMap, compilation.SpecializationState.PipelineState.Value, fromCache: true) - : new ShaderInfo(fragmentOutputMap, fromCache: true); - + ShaderInfo shaderInfo = shaderInfoBuilder.Build(compilation.SpecializationState.PipelineState, fromCache: true); IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo); CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs index 77e526672..2dc5c9719 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs @@ -42,25 +42,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache int binaryCodeLength = reader.ReadInt32(); byte[] binaryCode = reader.ReadBytes(binaryCodeLength); - output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv)); + output.Add(new ShaderSource(binaryCode, stage, TargetLanguage.Spirv)); } return output.ToArray(); } - - private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage) - { - for (int i = 0; i < stages.Length; i++) - { - CachedShaderStage currentStage = stages[i]; - - if (currentStage?.Info != null && currentStage.Info.Stage == stage) - { - return ShaderCache.GetBindings(currentStage.Info); - } - } - - return new ShaderBindings(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>()); - } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 7db627ba9..e4e1e0d61 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -110,16 +110,16 @@ namespace Ryujinx.Graphics.Gpu.Shader Logger.Error?.Print(LogClass.Gpu, $"{resourceName} index {index} exceeds per stage limit of {maxPerStage}."); } - return GetStageIndex() * (int)maxPerStage + index; + return GetStageIndex(_stageIndex) * (int)maxPerStage + index; } - private int GetStageIndex() + public static int GetStageIndex(int stageIndex) { // This is just a simple remapping to ensure that most frequently used shader stages // have the lowest binding numbers. // This is useful because if we need to run on a system with a low limit on the bindings, // then we can still get most games working as the most common shaders will have low binding numbers. - return _stageIndex switch + return stageIndex switch { 4 => 1, // Fragment 3 => 2, // Geometry diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index d543f42ad..b7ba159a5 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -219,12 +219,11 @@ namespace Ryujinx.Graphics.Gpu.Shader GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState); TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa); - TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode); ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) }; - - IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1)); + ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info); + IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info); cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader); @@ -363,6 +362,8 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatorContext previousStage = null; + ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context); + for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) { TranslatorContext currentStage = translatorContexts[stageIndex + 1]; @@ -398,6 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (program != null) { shaderSources.Add(CreateShaderSource(program)); + infoBuilder.AddStageInfo(program.Info); } previousStage = currentStage; @@ -414,8 +416,9 @@ namespace Ryujinx.Graphics.Gpu.Shader ShaderSource[] shaderSourcesArray = shaderSources.ToArray(); - int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1; - IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline)); + ShaderInfo info = infoBuilder.Build(pipeline); + + IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info); gpShaders = new CachedShaderProgram(hostProgram, specState, shaders); @@ -466,7 +469,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <returns>Shader source</returns> public static ShaderSource CreateShaderSource(ShaderProgram program) { - return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language); + return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language); } /// <summary> @@ -717,25 +720,6 @@ namespace Ryujinx.Graphics.Gpu.Shader }; } - /// <summary> - /// Gets information about the bindings used by a shader program. - /// </summary> - /// <param name="info">Shader program information to get the information from</param> - /// <returns>Shader bindings</returns> - public static ShaderBindings GetBindings(ShaderProgramInfo info) - { - var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray(); - var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray(); - var textureBindings = info.Textures.Select(x => x.Binding).ToArray(); - var imageBindings = info.Images.Select(x => x.Binding).ToArray(); - - return new ShaderBindings( - uniformBufferBindings, - storageBufferBindings, - textureBindings, - imageBindings); - } - /// <summary> /// Creates shader translation options with the requested graphics API and flags. /// The shader language is choosen based on the current configuration and graphics API. diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs new file mode 100644 index 000000000..39b31cf6a --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -0,0 +1,260 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + /// <summary> + /// Shader info structure builder. + /// </summary> + class ShaderInfoBuilder + { + private const int TotalSets = 4; + + private const int UniformSetIndex = 0; + private const int StorageSetIndex = 1; + private const int TextureSetIndex = 2; + private const int ImageSetIndex = 3; + + private const ResourceStages SupportBufferStags = + ResourceStages.Compute | + ResourceStages.Vertex | + ResourceStages.Fragment; + + private readonly GpuContext _context; + + private int _fragmentOutputMap; + + private readonly List<ResourceDescriptor>[] _resourceDescriptors; + private readonly List<ResourceUsage>[] _resourceUsages; + + /// <summary> + /// Creates a new shader info builder. + /// </summary> + /// <param name="context">GPU context that owns the shaders that will be added to the builder</param> + public ShaderInfoBuilder(GpuContext context) + { + _context = context; + + _fragmentOutputMap = -1; + + _resourceDescriptors = new List<ResourceDescriptor>[TotalSets]; + _resourceUsages = new List<ResourceUsage>[TotalSets]; + + for (int index = 0; index < TotalSets; index++) + { + _resourceDescriptors[index] = new(); + _resourceUsages[index] = new(); + } + + AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); + } + + /// <summary> + /// Adds information from a given shader stage. + /// </summary> + /// <param name="info">Shader stage information</param> + public void AddStageInfo(ShaderProgramInfo info) + { + if (info.Stage == ShaderStage.Fragment) + { + _fragmentOutputMap = info.FragmentOutputMap; + } + + int stageIndex = GpuAccessorBase.GetStageIndex(info.Stage switch + { + ShaderStage.TessellationControl => 1, + ShaderStage.TessellationEvaluation => 2, + ShaderStage.Geometry => 3, + ShaderStage.Fragment => 4, + _ => 0 + }); + + 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 + }; + + int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage; + int storagesPerStage = (int)_context.Capabilities.MaximumStorageBuffersPerStage; + int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage; + int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage; + + int uniformBinding = 1 + stageIndex * uniformsPerStage; + int storageBinding = stageIndex * storagesPerStage; + int textureBinding = stageIndex * texturesPerStage * 2; + int imageBinding = stageIndex * imagesPerStage * 2; + + AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage); + AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage); + AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage); + AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage); + + AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false); + AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true); + AddUsage(info.Textures, stages, TextureSetIndex, isImage: false); + AddUsage(info.Images, stages, ImageSetIndex, isImage: true); + } + + /// <summary> + /// Adds a resource descriptor to the list of descriptors. + /// </summary> + /// <param name="stages">Shader stages where the resource is used</param> + /// <param name="type">Type of the resource</param> + /// <param name="setIndex">Descriptor set number where the resource will be bound</param> + /// <param name="binding">Binding number where the resource will be bound</param> + /// <param name="count">Number of resources bound at the binding location</param> + private void AddDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count) + { + for (int index = 0; index < count; index++) + { + _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding + index, 1, type, stages)); + } + } + + /// <summary> + /// Adds two interleaved groups of resources to the list of descriptors. + /// </summary> + /// <param name="stages">Shader stages where the resource is used</param> + /// <param name="type">Type of the first interleaved resource</param> + /// <param name="type2">Type of the second interleaved resource</param> + /// <param name="setIndex">Descriptor set number where the resource will be bound</param> + /// <param name="binding">Binding number where the resource will be bound</param> + /// <param name="count">Number of resources bound at the binding location</param> + private void AddDualDescriptor(ResourceStages stages, ResourceType type, ResourceType type2, int setIndex, int binding, int count) + { + AddDescriptor(stages, type, setIndex, binding, count); + AddDescriptor(stages, type2, setIndex, binding + count, count); + } + + /// <summary> + /// Adds an array resource to the list of descriptors. + /// </summary> + /// <param name="stages">Shader stages where the resource is used</param> + /// <param name="type">Type of the resource</param> + /// <param name="setIndex">Descriptor set number where the resource will be bound</param> + /// <param name="binding">Binding number where the resource will be bound</param> + /// <param name="count">Number of resources bound at the binding location</param> + private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count) + { + _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages)); + } + + /// <summary> + /// Adds buffer usage information to the list of usages. + /// </summary> + /// <param name="buffers">Buffers to be added</param> + /// <param name="stages">Stages where the buffers are used</param> + /// <param name="setIndex">Descriptor set index where the buffers will be bound</param> + /// <param name="isStorage">True for storage buffers, false for uniform buffers</param> + private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, int setIndex, bool isStorage) + { + foreach (BufferDescriptor buffer in buffers) + { + _resourceUsages[setIndex].Add(new ResourceUsage( + buffer.Binding, + isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer, + stages, + buffer.Flags.HasFlag(BufferUsageFlags.Write) ? ResourceAccess.ReadWrite : ResourceAccess.Read)); + } + } + + /// <summary> + /// Adds texture usage information to the list of usages. + /// </summary> + /// <param name="textures">Textures to be added</param> + /// <param name="stages">Stages where the textures are used</param> + /// <param name="setIndex">Descriptor set index where the textures will be bound</param> + /// <param name="isImage">True for images, false for textures</param> + private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage) + { + foreach (TextureDescriptor texture in textures) + { + bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; + + ResourceType type = isBuffer + ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture) + : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler); + + _resourceUsages[setIndex].Add(new ResourceUsage( + texture.Binding, + type, + stages, + texture.Flags.HasFlag(TextureUsageFlags.ImageStore) ? ResourceAccess.ReadWrite : ResourceAccess.Read)); + } + } + + /// <summary> + /// Creates a new shader information structure from the added information. + /// </summary> + /// <param name="pipeline">Optional pipeline state for background shader compilation</param> + /// <param name="fromCache">Indicates if the shader comes from a disk cache</param> + /// <returns>Shader information</returns> + public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false) + { + var descriptors = new ResourceDescriptorCollection[TotalSets]; + var 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()); + } + + ResourceLayout resourceLayout = new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly()); + + if (pipeline.HasValue) + { + return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache); + } + else + { + return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache); + } + } + + /// <summary> + /// Builds shader information for shaders from the disk cache. + /// </summary> + /// <param name="context">GPU context that owns the shaders</param> + /// <param name="programs">Shaders from the disk cache</param> + /// <param name="pipeline">Optional pipeline for background compilation</param> + /// <returns>Shader information</returns> + public static ShaderInfo BuildForCache(GpuContext context, IEnumerable<CachedShaderStage> programs, ProgramPipelineState? pipeline) + { + ShaderInfoBuilder builder = new ShaderInfoBuilder(context); + + foreach (CachedShaderStage program in programs) + { + if (program?.Info != null) + { + builder.AddStageInfo(program.Info); + } + } + + return builder.Build(pipeline, fromCache: true); + } + + /// <summary> + /// Builds shader information for a compute shader. + /// </summary> + /// <param name="context">GPU context that owns the shader</param> + /// <param name="info">Compute shader information</param> + /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param> + /// <returns>Shader information</returns> + public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false) + { + ShaderInfoBuilder builder = new ShaderInfoBuilder(context); + + builder.AddStageInfo(info); + + return builder.Build(null, fromCache); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index a47ea5ff8..f3ac36e13 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -372,8 +372,9 @@ namespace Ryujinx.Graphics.Vulkan private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp) { var program = _program; - int stagesCount = program.Bindings[setIndex].Length; - if (stagesCount == 0 && setIndex != PipelineBase.UniformSetIndex) + var bindingSegments = program.BindingSegments[setIndex]; + + if (bindingSegments.Length == 0 && setIndex != PipelineBase.UniformSetIndex) { return; } @@ -410,125 +411,113 @@ namespace Ryujinx.Graphics.Vulkan } } - for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++) + foreach (ResourceBindingSegment segment in bindingSegments) { - var stageBindings = program.Bindings[setIndex][stageIndex]; - int bindingsCount = stageBindings.Length; - int count; + int binding = segment.Binding; + int count = segment.Count; - for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count) + if (setIndex == PipelineBase.UniformSetIndex) { - int binding = stageBindings[bindingIndex]; - count = 1; - - while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count) + for (int i = 0; i < count; i++) { - count++; + int index = binding + i; + + if (!_uniformSet[index]) + { + UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); + + _uniformSet[index] = true; + } } - if (setIndex == PipelineBase.UniformSetIndex) + ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; + dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); + } + else if (setIndex == PipelineBase.StorageSetIndex) + { + for (int i = 0; i < count; i++) { + int index = binding + i; + + if (!_storageSet[index]) + { + UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer); + + _storageSet[index] = true; + } + } + + ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers; + if (program.HasMinimalLayout) + { + dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer); + } + else + { + dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count)); + } + } + else if (setIndex == PipelineBase.TextureSetIndex) + { + if (segment.Type != ResourceType.BufferTexture) + { + Span<DescriptorImageInfo> textures = _textures; + for (int i = 0; i < count; i++) { - int index = binding + i; + ref var texture = ref textures[i]; - if (!_uniformSet[index]) + texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default; + texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default; + + if (texture.ImageView.Handle == 0) { - UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); + texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; + } - _uniformSet[index] = true; + if (texture.Sampler.Handle == 0) + { + texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; } } - ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; - dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); + dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler); } - else if (setIndex == PipelineBase.StorageSetIndex) + else { + Span<BufferView> bufferTextures = _bufferTextures; + for (int i = 0; i < count; i++) { - int index = binding + i; - - if (!_storageSet[index]) - { - UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer); - - _storageSet[index] = true; - } + bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default; } - ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers; - if (program.HasMinimalLayout) - { - dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer); - } - else - { - dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count)); - } + dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer); } - else if (setIndex == PipelineBase.TextureSetIndex) + } + else if (setIndex == PipelineBase.ImageSetIndex) + { + if (segment.Type != ResourceType.BufferImage) { - if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout) + Span<DescriptorImageInfo> images = _images; + + for (int i = 0; i < count; i++) { - Span<DescriptorImageInfo> textures = _textures; - - for (int i = 0; i < count; i++) - { - ref var texture = ref textures[i]; - - texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default; - texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default; - - if (texture.ImageView.Handle == 0) - { - texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; - } - - if (texture.Sampler.Handle == 0) - { - texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; - } - } - - dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler); + images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default; } - else - { - Span<BufferView> bufferTextures = _bufferTextures; - for (int i = 0; i < count; i++) - { - bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default; - } - - dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer); - } + dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage); } - else if (setIndex == PipelineBase.ImageSetIndex) + else { - if (((uint)binding % (Constants.MaxImagesPerStage * 2)) < Constants.MaxImagesPerStage || program.HasMinimalLayout) + Span<BufferView> bufferImages = _bufferImages; + + for (int i = 0; i < count; i++) { - Span<DescriptorImageInfo> images = _images; - - for (int i = 0; i < count; i++) - { - images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default; - } - - dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage); + bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default; } - else - { - Span<BufferView> bufferImages = _bufferImages; - for (int i = 0; i < count; i++) - { - bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default; - } - - dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer); - } + dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer); } } } @@ -568,9 +557,6 @@ namespace Ryujinx.Graphics.Vulkan [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp) { - var dummyBuffer = _dummyBuffer?.GetBuffer(); - int stagesCount = _program.Bindings[PipelineBase.UniformSetIndex].Length; - if (!_uniformSet[0]) { Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1]; @@ -587,41 +573,32 @@ namespace Ryujinx.Graphics.Vulkan UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer); } - for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++) + var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex]; + var dummyBuffer = _dummyBuffer?.GetBuffer(); + + foreach (ResourceBindingSegment segment in bindingSegments) { - var stageBindings = _program.Bindings[PipelineBase.UniformSetIndex][stageIndex]; - int bindingsCount = stageBindings.Length; - int count; + int binding = segment.Binding; + int count = segment.Count; - for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count) + bool doUpdate = false; + + for (int i = 0; i < count; i++) { - int binding = stageBindings[bindingIndex]; - count = 1; + int index = binding + i; - while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count) + if (!_uniformSet[index]) { - count++; + UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); + _uniformSet[index] = true; + doUpdate = true; } + } - bool doUpdate = false; - - for (int i = 0; i < count; i++) - { - int index = binding + i; - - if (!_uniformSet[index]) - { - UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); - _uniformSet[index] = true; - doUpdate = true; - } - } - - if (doUpdate) - { - ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; - UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); - } + if (doUpdate) + { + ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; + UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index 5f15f15f6..e9952126f 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -54,29 +54,29 @@ namespace Ryujinx.Graphics.Vulkan.Effects var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv"); var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv"); - var computeBindings = new ShaderBindings( - new[] { 2 }, - Array.Empty<int>(), - new[] { 1 }, - new[] { 0 }); + var scalingResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); - var sharpeningBindings = new ShaderBindings( - new[] { 2, 3, 4 }, - Array.Empty<int>(), - new[] { 1 }, - new[] { 0 }); + var sharpeningResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3) + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); _scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv) - }); + new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv) + }, scalingResourceLayout); _sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv) - }); + new ShaderSource(sharpeningShader, ShaderStage.Compute, TargetLanguage.Spirv) + }, sharpeningResourceLayout); } public void Run( @@ -160,10 +160,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.ComputeBarrier(); // Sharpening pass - _pipeline.SetCommandBuffer(cbs); _pipeline.SetProgram(_sharpeningProgram); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float)); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) }); _pipeline.SetImage(0, destinationTexture); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index b7316d857..9da003dda 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -38,18 +38,17 @@ namespace Ryujinx.Graphics.Vulkan.Effects var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv"); - var computeBindings = new ShaderBindings( - new[] { 2 }, - Array.Empty<int>(), - new[] { 1 }, - new[] { 0 }); + var resourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); _shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(shader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv) - }); + new ShaderSource(shader, ShaderStage.Compute, TargetLanguage.Spirv) + }, resourceLayout); } public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height) diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index 38f86baeb..0d392a65f 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -77,23 +77,23 @@ namespace Ryujinx.Graphics.Vulkan.Effects var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv"); var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv"); - var edgeBindings = new ShaderBindings( - new[] { 2 }, - Array.Empty<int>(), - new[] { 1 }, - new[] { 0 }); + var edgeResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); - var blendBindings = new ShaderBindings( - new[] { 2 }, - Array.Empty<int>(), - new[] { 1, 3, 4 }, - new[] { 0 }); + var blendResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4) + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); - var neighbourBindings = new ShaderBindings( - new[] { 2 }, - Array.Empty<int>(), - new[] { 1, 3 }, - new[] { 0 }); + var neighbourResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); @@ -117,18 +117,18 @@ namespace Ryujinx.Graphics.Vulkan.Effects _edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(edgeShader, edgeBindings, ShaderStage.Compute, TargetLanguage.Spirv) - }, new[] { specInfo }); + new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv) + }, edgeResourceLayout, new[] { specInfo }); _blendProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(blendShader, blendBindings, ShaderStage.Compute, TargetLanguage.Spirv) - }, new[] { specInfo }); + new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv) + }, blendResourceLayout, new[] { specInfo }); _neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(neighbourShader, neighbourBindings, ShaderStage.Compute, TargetLanguage.Spirv) - }, new[] { specInfo }); + new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv) + }, neighbourResourceLayout, new[] { specInfo }); } public void DeletePipelines() diff --git a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 1f03b68c2..55868ee35 100644 --- a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -36,6 +36,56 @@ namespace Ryujinx.Graphics.Vulkan }; } + public static ShaderStageFlags Convert(this ResourceStages stages) + { + ShaderStageFlags stageFlags = stages.HasFlag(ResourceStages.Compute) + ? ShaderStageFlags.ComputeBit + : ShaderStageFlags.None; + + if (stages.HasFlag(ResourceStages.Vertex)) + { + stageFlags |= ShaderStageFlags.VertexBit; + } + + if (stages.HasFlag(ResourceStages.TessellationControl)) + { + stageFlags |= ShaderStageFlags.TessellationControlBit; + } + + if (stages.HasFlag(ResourceStages.TessellationEvaluation)) + { + stageFlags |= ShaderStageFlags.TessellationEvaluationBit; + } + + if (stages.HasFlag(ResourceStages.Geometry)) + { + stageFlags |= ShaderStageFlags.GeometryBit; + } + + if (stages.HasFlag(ResourceStages.Fragment)) + { + stageFlags |= ShaderStageFlags.FragmentBit; + } + + return stageFlags; + } + + public static DescriptorType Convert(this ResourceType type) + { + return type switch + { + ResourceType.UniformBuffer => DescriptorType.UniformBuffer, + ResourceType.StorageBuffer => DescriptorType.StorageBuffer, + ResourceType.Texture => DescriptorType.SampledImage, + ResourceType.Sampler => DescriptorType.Sampler, + ResourceType.TextureAndSampler => DescriptorType.CombinedImageSampler, + ResourceType.Image => DescriptorType.StorageImage, + ResourceType.BufferTexture => DescriptorType.UniformTexelBuffer, + ResourceType.BufferImage => DescriptorType.StorageTexelBuffer, + _ => throw new ArgumentException($"Invalid resource type \"{type}\".") + }; + } + public static SamplerAddressMode Convert(this AddressMode mode) { return mode switch @@ -48,7 +98,7 @@ namespace Ryujinx.Graphics.Vulkan AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder, AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat, AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge, - _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp. + _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp. }; } diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index c064696d8..43214be4d 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -55,192 +55,168 @@ namespace Ryujinx.Graphics.Vulkan _samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); _samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); - var blitVertexBindings = new ShaderBindings( - new[] { 1 }, - Array.Empty<int>(), - Array.Empty<int>(), - Array.Empty<int>()); - - var blitFragmentBindings = new ShaderBindings( - Array.Empty<int>(), - Array.Empty<int>(), - new[] { 0 }, - Array.Empty<int>()); + var blitResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1) + .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build(); _programColorBlit = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, blitResourceLayout); _programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, blitResourceLayout); _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, blitResourceLayout); - var colorClearFragmentBindings = new ShaderBindings( - Array.Empty<int>(), - Array.Empty<int>(), - Array.Empty<int>(), - Array.Empty<int>()); + var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build(); _programColorClearF = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorClearResourceLayout); _programColorClearSI = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorClearResourceLayout); _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorClearResourceLayout); - var strideChangeBindings = new ShaderBindings( - new[] { 0 }, - new[] { 1, 2 }, - Array.Empty<int>(), - Array.Empty<int>()); + var strideChangeResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); _programStrideChange = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + }, strideChangeResourceLayout); - var colorCopyBindings = new ShaderBindings( - new[] { 0 }, - Array.Empty<int>(), - new[] { 0 }, - new[] { 0 }); + var colorCopyResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0) + .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + }, colorCopyResourceLayout); _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + }, colorCopyResourceLayout); _programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + }, colorCopyResourceLayout); - var colorDrawToMsVertexBindings = new ShaderBindings( - Array.Empty<int>(), - Array.Empty<int>(), - Array.Empty<int>(), - Array.Empty<int>()); - - var colorDrawToMsFragmentBindings = new ShaderBindings( - new[] { 0 }, - Array.Empty<int>(), - new[] { 0 }, - Array.Empty<int>()); + var colorDrawToMsResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build(); _programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorDrawToMsResourceLayout); - var convertD32S8ToD24S8Bindings = new ShaderBindings( - new[] { 0 }, - new[] { 1, 2 }, - Array.Empty<int>(), - Array.Empty<int>()); + var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, convertD32S8ToD24S8Bindings, ShaderStage.Compute, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + }, convertD32S8ToD24S8ResourceLayout); - var convertIndexBufferBindings = new ShaderBindings( - new[] { 0 }, - new[] { 1, 2 }, - Array.Empty<int>(), - Array.Empty<int>()); + var convertIndexBufferResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + }, convertIndexBufferResourceLayout); - var convertIndirectDataBindings = new ShaderBindings( - new[] { 0 }, - new[] { 1, 2, 3 }, - Array.Empty<int>(), - Array.Empty<int>()); + var convertIndirectDataResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + }, convertIndirectDataResourceLayout); _programDepthBlit = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, blitResourceLayout); _programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, blitResourceLayout); _programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorDrawToMsResourceLayout); _programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorDrawToMsResourceLayout); if (gd.Capabilities.SupportsShaderStencilExport) { _programStencilBlit = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, blitResourceLayout); _programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, blitResourceLayout); _programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorDrawToMsResourceLayout); _programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), - }); + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorDrawToMsResourceLayout); } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs index c834fa621..e7c435670 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs @@ -1,52 +1,101 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; -using System.Collections.Generic; +using System; +using System.Collections.Concurrent; +using System.Collections.ObjectModel; namespace Ryujinx.Graphics.Vulkan { class PipelineLayoutCache { - private readonly PipelineLayoutCacheEntry[] _plce; - private readonly List<PipelineLayoutCacheEntry> _plceMinimal; + private readonly struct PlceKey : IEquatable<PlceKey> + { + public readonly ReadOnlyCollection<ResourceDescriptorCollection> SetDescriptors; + public readonly bool UsePushDescriptors; + + public PlceKey(ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, bool usePushDescriptors) + { + SetDescriptors = setDescriptors; + UsePushDescriptors = usePushDescriptors; + } + + public override int GetHashCode() + { + HashCode hasher = new HashCode(); + + if (SetDescriptors != null) + { + foreach (var setDescriptor in SetDescriptors) + { + hasher.Add(setDescriptor); + } + } + + hasher.Add(UsePushDescriptors); + + return hasher.ToHashCode(); + } + + public override bool Equals(object obj) + { + return obj is PlceKey other && Equals(other); + } + + public bool Equals(PlceKey other) + { + if ((SetDescriptors == null) != (other.SetDescriptors == null)) + { + return false; + } + + if (SetDescriptors != null) + { + if (SetDescriptors.Count != other.SetDescriptors.Count) + { + return false; + } + + for (int index = 0; index < SetDescriptors.Count; index++) + { + if (!SetDescriptors[index].Equals(other.SetDescriptors[index])) + { + return false; + } + } + } + + return UsePushDescriptors == other.UsePushDescriptors; + } + } + + private readonly ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry> _plces; public PipelineLayoutCache() { - _plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages]; - _plceMinimal = new List<PipelineLayoutCacheEntry>(); + _plces = new ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry>(); } - public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders) + public PipelineLayoutCacheEntry GetOrCreate( + VulkanRenderer gd, + Device device, + ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, + bool usePushDescriptors) { - var plce = new PipelineLayoutCacheEntry(gd, device, shaders); - _plceMinimal.Add(plce); - return plce; - } + var key = new PlceKey(setDescriptors, usePushDescriptors); - public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd) - { - if (_plce[stages] == null) - { - _plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd); - } - - return _plce[stages]; + return _plces.GetOrAdd(key, (newKey) => new PipelineLayoutCacheEntry(gd, device, setDescriptors, usePushDescriptors)); } protected virtual unsafe void Dispose(bool disposing) { if (disposing) { - for (int i = 0; i < _plce.Length; i++) - { - _plce[i]?.Dispose(); - } - - foreach (var plce in _plceMinimal) + foreach (var plce in _plces.Values) { plce.Dispose(); } - _plceMinimal.Clear(); + _plces.Clear(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs index 2c9661159..eeb25dc0f 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System.Collections.Generic; +using System.Collections.ObjectModel; namespace Ryujinx.Graphics.Vulkan { @@ -16,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly int[] _dsCacheCursor; private int _dsLastCbIndex; - private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device) + private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount) { _gd = gd; _device = device; @@ -25,27 +26,24 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++) { - _dsCache[i] = new List<Auto<DescriptorSetCollection>>[PipelineBase.DescriptorSetLayouts]; + _dsCache[i] = new List<Auto<DescriptorSetCollection>>[setsCount]; - for (int j = 0; j < PipelineBase.DescriptorSetLayouts; j++) + for (int j = 0; j < _dsCache[i].Length; j++) { _dsCache[i][j] = new List<Auto<DescriptorSetCollection>>(); } } - _dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts]; + _dsCacheCursor = new int[setsCount]; } - public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, uint stages, bool usePd) : this(gd, device) + public PipelineLayoutCacheEntry( + VulkanRenderer gd, + Device device, + ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, + bool usePushDescriptors) : this(gd, device, setDescriptors.Count) { - DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, usePd, out var pipelineLayout); - PipelineLayout = pipelineLayout; - } - - public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, ShaderSource[] shaders) : this(gd, device) - { - DescriptorSetLayouts = PipelineLayoutFactory.CreateMinimal(gd, device, shaders, out var pipelineLayout); - PipelineLayout = pipelineLayout; + (DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors); } public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection( @@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Vulkan { _dsLastCbIndex = commandBufferIndex; - for (int i = 0; i < PipelineBase.DescriptorSetLayouts; i++) + for (int i = 0; i < _dsCacheCursor.Length; i++) { _dsCacheCursor[i] = 0; } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index 2d8814191..bcb2c1a50 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -1,257 +1,74 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; -using System.Collections.Generic; -using System.Numerics; +using System.Collections.ObjectModel; namespace Ryujinx.Graphics.Vulkan { static class PipelineLayoutFactory { - private const ShaderStageFlags SupportBufferStages = - ShaderStageFlags.VertexBit | - ShaderStageFlags.FragmentBit | - ShaderStageFlags.ComputeBit; - - private static ShaderStageFlags ActiveStages(uint stages) + public static unsafe (DescriptorSetLayout[], PipelineLayout) Create( + VulkanRenderer gd, + Device device, + ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, + bool usePushDescriptors) { - ShaderStageFlags stageFlags = 0; + DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count]; - while (stages != 0) + bool isMoltenVk = gd.IsMoltenVk; + + for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++) { - int stage = BitOperations.TrailingZeroCount(stages); - stages &= ~(1u << stage); + ResourceDescriptorCollection rdc = setDescriptors[setIndex]; - stageFlags |= stage switch + ResourceStages activeStages = ResourceStages.None; + + if (isMoltenVk) { - 1 => ShaderStageFlags.FragmentBit, - 2 => ShaderStageFlags.GeometryBit, - 3 => ShaderStageFlags.TessellationControlBit, - 4 => ShaderStageFlags.TessellationEvaluationBit, - _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit - }; - } - - return stageFlags; - } - - public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout) - { - int stagesCount = BitOperations.PopCount(stages); - - int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1; - int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount; - int iCount = Constants.MaxImagesPerStage * 2 * stagesCount; - - DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount]; - DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount]; - DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount]; - DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount]; - - uLayoutBindings[0] = new DescriptorSetLayoutBinding - { - Binding = 0, - DescriptorType = DescriptorType.UniformBuffer, - DescriptorCount = 1, - StageFlags = SupportBufferStages - }; - - int iter = 0; - var activeStages = ActiveStages(stages); - - while (stages != 0) - { - int stage = BitOperations.TrailingZeroCount(stages); - stages &= ~(1u << stage); - - var stageFlags = stage switch - { - 1 => ShaderStageFlags.FragmentBit, - 2 => ShaderStageFlags.GeometryBit, - 3 => ShaderStageFlags.TessellationControlBit, - 4 => ShaderStageFlags.TessellationEvaluationBit, - _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit - }; - - void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip) - { - int totalPerStage = maxPerStage * skip; - - for (int i = 0; i < maxPerStage; i++) + for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++) { - bindings[start + iter * totalPerStage + i] = new DescriptorSetLayoutBinding - { - Binding = (uint)(start + stage * totalPerStage + i), - DescriptorType = type, - DescriptorCount = 1, - StageFlags = stageFlags - }; + activeStages |= rdc.Descriptors[descIndex].Stages; } } - void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0) - { - // There's a bug on MoltenVK where using the same buffer across different stages - // causes invalid resource errors, allow the binding on all active stages as workaround. - var flags = gd.IsMoltenVk ? activeStages : stageFlags; + DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count]; - bindings[start + iter] = new DescriptorSetLayoutBinding + for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++) + { + ResourceDescriptor descriptor = rdc.Descriptors[descIndex]; + + ResourceStages stages = descriptor.Stages; + + if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk) { - Binding = (uint)(start + stage * maxPerStage), - DescriptorType = DescriptorType.StorageBuffer, - DescriptorCount = (uint)maxPerStage, - StageFlags = flags + // There's a bug on MoltenVK where using the same buffer across different stages + // causes invalid resource errors, allow the binding on all active stages as workaround. + stages = activeStages; + } + + layoutBindings[descIndex] = new DescriptorSetLayoutBinding() + { + Binding = (uint)descriptor.Binding, + DescriptorType = descriptor.Type.Convert(), + DescriptorCount = (uint)descriptor.Count, + StageFlags = stages.Convert() }; } - Set(uLayoutBindings, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer, 1, 1); - SetStorage(sLayoutBindings, Constants.MaxStorageBuffersPerStage); - Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.CombinedImageSampler, 0, 2); - Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.UniformTexelBuffer, Constants.MaxTexturesPerStage, 2); - Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageImage, 0, 2); - Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageTexelBuffer, Constants.MaxImagesPerStage, 2); - - iter++; - } - - DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts]; - - var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = uLayoutBindings, - BindingCount = (uint)uCount, - Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0 - }; - - var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = sLayoutBindings, - BindingCount = (uint)stagesCount - }; - - var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = tLayoutBindings, - BindingCount = (uint)tCount - }; - - var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = iLayoutBindings, - BindingCount = (uint)iCount - }; - - gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError(); - gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError(); - gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError(); - gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError(); - - fixed (DescriptorSetLayout* pLayouts = layouts) - { - var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo() + fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings) { - SType = StructureType.PipelineLayoutCreateInfo, - PSetLayouts = pLayouts, - SetLayoutCount = PipelineBase.DescriptorSetLayouts - }; - - gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError(); - } - - return layouts; - } - - public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout) - { - int stagesCount = shaders.Length; - - int uCount = 0; - int sCount = 0; - int tCount = 0; - int iCount = 0; - - foreach (var shader in shaders) - { - uCount += shader.Bindings.UniformBufferBindings.Count; - sCount += shader.Bindings.StorageBufferBindings.Count; - tCount += shader.Bindings.TextureBindings.Count; - iCount += shader.Bindings.ImageBindings.Count; - } - - DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount]; - DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount]; - DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount]; - DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount]; - - int uIndex = 0; - int sIndex = 0; - int tIndex = 0; - int iIndex = 0; - - foreach (var shader in shaders) - { - var stageFlags = shader.Stage.Convert(); - - void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable<int> bds) - { - foreach (var b in bds) + var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() { - bindings[start++] = new DescriptorSetLayoutBinding - { - Binding = (uint)b, - DescriptorType = type, - DescriptorCount = 1, - StageFlags = stageFlags - }; - } - } + SType = StructureType.DescriptorSetLayoutCreateInfo, + PBindings = pLayoutBindings, + BindingCount = (uint)layoutBindings.Length, + Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None + }; - // TODO: Support buffer textures and images here. - // This is only used for the helper shaders on the backend, and we don't use buffer textures on them - // so far, so it's not really necessary right now. - Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings); - Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings); - Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings); - Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings); + gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError(); + } } - DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts]; - - var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = uLayoutBindings, - BindingCount = (uint)uCount - }; - - var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = sLayoutBindings, - BindingCount = (uint)sCount - }; - - var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = tLayoutBindings, - BindingCount = (uint)tCount - }; - - var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() - { - SType = StructureType.DescriptorSetLayoutCreateInfo, - PBindings = iLayoutBindings, - BindingCount = (uint)iCount - }; - - gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError(); - gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError(); - gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError(); - gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError(); + PipelineLayout layout; fixed (DescriptorSetLayout* pLayouts = layouts) { @@ -259,13 +76,13 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PipelineLayoutCreateInfo, PSetLayouts = pLayouts, - SetLayoutCount = PipelineBase.DescriptorSetLayouts + SetLayoutCount = (uint)layouts.Length }; gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError(); } - return layouts; + return (layouts, layout); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs b/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs new file mode 100644 index 000000000..feeba4744 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Vulkan +{ + readonly struct ResourceBindingSegment + { + public readonly int Binding; + public readonly int Count; + public readonly ResourceType Type; + public readonly ResourceStages Stages; + public readonly ResourceAccess Access; + + public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, ResourceAccess access) + { + Binding = binding; + Count = count; + Type = type; + Stages = stages; + Access = access; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs new file mode 100644 index 000000000..eac0d6c20 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs @@ -0,0 +1,67 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Vulkan +{ + class ResourceLayoutBuilder + { + private const int TotalSets = PipelineBase.DescriptorSetLayouts; + + private readonly List<ResourceDescriptor>[] _resourceDescriptors; + private readonly List<ResourceUsage>[] _resourceUsages; + + public ResourceLayoutBuilder() + { + _resourceDescriptors = new List<ResourceDescriptor>[TotalSets]; + _resourceUsages = new List<ResourceUsage>[TotalSets]; + + for (int index = 0; index < TotalSets; index++) + { + _resourceDescriptors[index] = new(); + _resourceUsages[index] = new(); + } + } + + public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding) + { + int setIndex = type switch + { + ResourceType.UniformBuffer => PipelineBase.UniformSetIndex, + ResourceType.StorageBuffer => PipelineBase.StorageSetIndex, + ResourceType.TextureAndSampler or ResourceType.BufferTexture => PipelineBase.TextureSetIndex, + ResourceType.Image or ResourceType.BufferImage => PipelineBase.ImageSetIndex, + _ => throw new ArgumentException($"Invalid resource type \"{type}\".") + }; + + ResourceAccess access = IsReadOnlyType(type) ? ResourceAccess.Read : ResourceAccess.ReadWrite; + + _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding, type, stages, access)); + + return this; + } + + private static bool IsReadOnlyType(ResourceType type) + { + return type == ResourceType.UniformBuffer || + type == ResourceType.Sampler || + type == ResourceType.TextureAndSampler || + type == ResourceType.BufferTexture; + } + + public ResourceLayout Build() + { + var descriptors = new ResourceDescriptorCollection[TotalSets]; + var 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()); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs index ca99ebf07..d853bb04c 100644 --- a/src/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs @@ -26,8 +26,6 @@ namespace Ryujinx.Graphics.Vulkan public ShaderStageFlags StageFlags => _stage; - public ShaderBindings Bindings { get; } - public ProgramLinkStatus CompileStatus { private set; get; } public readonly Task CompileTask; @@ -36,7 +34,6 @@ namespace Ryujinx.Graphics.Vulkan { _api = api; _device = device; - Bindings = shaderSource.Bindings; CompileStatus = ProgramLinkStatus.Incomplete; diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index 1694049c9..334dfc200 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; @@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan public uint Stages { get; } - public int[][][] Bindings { get; } + public ResourceBindingSegment[][] BindingSegments { get; } public ProgramLinkStatus LinkStatus { get; private set; } @@ -54,7 +55,13 @@ namespace Ryujinx.Graphics.Vulkan private Task _compileTask; private bool _firstBackgroundUse; - public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false) + public ShaderCollection( + VulkanRenderer gd, + Device device, + ShaderSource[] shaders, + ResourceLayout resourceLayout, + SpecDescription[] specDescription = null, + bool isMinimal = false) { _gd = gd; _device = device; @@ -99,39 +106,16 @@ namespace Ryujinx.Graphics.Vulkan _shaders = internalShaders; - bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors; + bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors; - _plce = isMinimal - ? gd.PipelineLayoutCache.Create(gd, device, shaders) - : gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd); + _plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors); HasMinimalLayout = isMinimal; - UsePushDescriptors = usePd; + UsePushDescriptors = usePushDescriptors; Stages = stages; - int[][] GrabAll(Func<ShaderBindings, IReadOnlyCollection<int>> selector) - { - bool hasAny = false; - int[][] bindings = new int[internalShaders.Length][]; - - for (int i = 0; i < internalShaders.Length; i++) - { - var collection = selector(internalShaders[i].Bindings); - hasAny |= collection.Count != 0; - bindings[i] = collection.ToArray(); - } - - return hasAny ? bindings : Array.Empty<int[]>(); - } - - Bindings = new[] - { - GrabAll(x => x.UniformBufferBindings), - GrabAll(x => x.StorageBufferBindings), - GrabAll(x => x.TextureBindings), - GrabAll(x => x.ImageBindings) - }; + BindingSegments = BuildBindingSegments(resourceLayout.SetUsages); _compileTask = Task.CompletedTask; _firstBackgroundUse = false; @@ -141,8 +125,9 @@ namespace Ryujinx.Graphics.Vulkan VulkanRenderer gd, Device device, ShaderSource[] sources, + ResourceLayout resourceLayout, ProgramPipelineState state, - bool fromCache) : this(gd, device, sources) + bool fromCache) : this(gd, device, sources, resourceLayout) { _state = state; @@ -150,6 +135,67 @@ namespace Ryujinx.Graphics.Vulkan _firstBackgroundUse = !fromCache; } + private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages) + { + ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; + + for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) + { + List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>(); + + ResourceUsage currentUsage = default; + int currentCount = 0; + + for (int index = 0; index < setUsages[setIndex].Usages.Count; index++) + { + ResourceUsage usage = setUsages[setIndex].Usages[index]; + + // If the resource is not accessed, we don't need to update it. + if (usage.Access == ResourceAccess.None) + { + continue; + } + + if (currentUsage.Binding + currentCount != usage.Binding || + currentUsage.Type != usage.Type || + currentUsage.Stages != usage.Stages || + currentUsage.Access != usage.Access) + { + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentUsage.Binding, + currentCount, + currentUsage.Type, + currentUsage.Stages, + currentUsage.Access)); + } + + currentUsage = usage; + currentCount = 1; + } + else + { + currentCount++; + } + } + + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentUsage.Binding, + currentCount, + currentUsage.Type, + currentUsage.Stages, + currentUsage.Access)); + } + + segments[setIndex] = currentSegments.ToArray(); + } + + return segments; + } + private async Task BackgroundCompilation() { await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index e4ce4904b..ffa1a1034 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -10,7 +10,6 @@ using Silk.NET.Vulkan.Extensions.EXT; using Silk.NET.Vulkan.Extensions.KHR; using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Vulkan @@ -398,17 +397,17 @@ namespace Ryujinx.Graphics.Vulkan if (info.State.HasValue || isCompute) { - return new ShaderCollection(this, _device, sources, info.State ?? default, info.FromCache); + return new ShaderCollection(this, _device, sources, info.ResourceLayout, info.State ?? default, info.FromCache); } else { - return new ShaderCollection(this, _device, sources); + return new ShaderCollection(this, _device, sources, info.ResourceLayout); } } - internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, SpecDescription[] specDescription = null) + internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, ResourceLayout resourceLayout, SpecDescription[] specDescription = null) { - return new ShaderCollection(this, _device, sources, specDescription: specDescription, isMinimal: true); + return new ShaderCollection(this, _device, sources, resourceLayout, specDescription, isMinimal: true); } public ISampler CreateSampler(GAL.SamplerCreateInfo info) @@ -658,7 +657,7 @@ namespace Ryujinx.Graphics.Vulkan Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); } - public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology) + internal GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology) { return topology switch { @@ -669,7 +668,7 @@ namespace Ryujinx.Graphics.Vulkan }; } - public bool TopologyUnsupported(GAL.PrimitiveTopology topology) + internal bool TopologyUnsupported(GAL.PrimitiveTopology topology) { return topology switch { From 21e88f17f6ebeeae61c3aa95d610ee7adf48d62c Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sun, 21 May 2023 15:28:51 -0400 Subject: [PATCH 553/737] ServerBase thread safety (#4577) * Add guard against ServerBase.Dispose() being called multiple times. Add reset event to avoid Dispose() being called while the ServerLoop is still running. * remove unused usings * rework ServerBase to use one collection each for sessions and ports, and make all accesses thread-safe. * fix Logger call * use GetSessionObj(int) instead of using _sessions directly * move _threadStopped check inside "dispose once" test * - Replace _threadStopped event with attempt to Join() the ending thread (if that isn't the current thread) instead. - Use the instance-local _selfProcess and (new) _selfThread variables to avoid suggesting that the current KProcess and KThread could change. Per gdkchan, they can't currently, and this old IPC system will be removed before that changes. - Re-order Dispose() so that the Interlocked _isDisposed check is the last check before disposing, to increase the likelihood that multiple callers will result in one of them succeeding. * code style suggestions per AcK77 * add infinite wait for thread termination --- src/Ryujinx.HLE/HOS/Services/ServerBase.cs | 237 +++++++++++++++------ 1 file changed, 166 insertions(+), 71 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index b994679aa..ff6df8a38 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; @@ -32,13 +33,14 @@ namespace Ryujinx.HLE.HOS.Services 0x01007FFF }; - private readonly object _handleLock = new(); + // The amount of time Dispose() will wait to Join() the thread executing the ServerLoop() + private static readonly TimeSpan ThreadJoinTimeout = TimeSpan.FromSeconds(3); private readonly KernelContext _context; private KProcess _selfProcess; + private KThread _selfThread; - private readonly List<int> _sessionHandles = new List<int>(); - private readonly List<int> _portHandles = new List<int>(); + private readonly ReaderWriterLockSlim _handleLock = new ReaderWriterLockSlim(); private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>(); private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>(); @@ -48,6 +50,8 @@ namespace Ryujinx.HLE.HOS.Services private readonly MemoryStream _responseDataStream; private readonly BinaryWriter _responseDataWriter; + private int _isDisposed = 0; + public ManualResetEvent InitDone { get; } public string Name { get; } public Func<IpcService> SmObjectFactory { get; } @@ -79,11 +83,20 @@ namespace Ryujinx.HLE.HOS.Services private void AddPort(int serverPortHandle, Func<IpcService> objectFactory) { - lock (_handleLock) + bool lockTaken = false; + try { - _portHandles.Add(serverPortHandle); + lockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite); + + _ports.Add(serverPortHandle, objectFactory); + } + finally + { + if (lockTaken) + { + _handleLock.ExitWriteLock(); + } } - _ports.Add(serverPortHandle, objectFactory); } public void AddSessionObj(KServerSession serverSession, IpcService obj) @@ -92,16 +105,62 @@ namespace Ryujinx.HLE.HOS.Services InitDone.WaitOne(); _selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle); + AddSessionObj(serverSessionHandle, obj); } public void AddSessionObj(int serverSessionHandle, IpcService obj) { - lock (_handleLock) + bool lockTaken = false; + try { - _sessionHandles.Add(serverSessionHandle); + lockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite); + + _sessions.Add(serverSessionHandle, obj); + } + finally + { + if (lockTaken) + { + _handleLock.ExitWriteLock(); + } + } + } + + private IpcService GetSessionObj(int serverSessionHandle) + { + bool lockTaken = false; + try + { + lockTaken = _handleLock.TryEnterReadLock(Timeout.Infinite); + + return _sessions[serverSessionHandle]; + } + finally + { + if (lockTaken) + { + _handleLock.ExitReadLock(); + } + } + } + + private bool RemoveSessionObj(int serverSessionHandle, out IpcService obj) + { + bool lockTaken = false; + try + { + lockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite); + + return _sessions.Remove(serverSessionHandle, out obj); + } + finally + { + if (lockTaken) + { + _handleLock.ExitWriteLock(); + } } - _sessions.Add(serverSessionHandle, obj); } private void Main() @@ -112,6 +171,7 @@ namespace Ryujinx.HLE.HOS.Services private void ServerLoop() { _selfProcess = KernelStatic.GetCurrentProcess(); + _selfThread = KernelStatic.GetCurrentThread(); if (SmObjectFactory != null) { @@ -122,8 +182,7 @@ namespace Ryujinx.HLE.HOS.Services InitDone.Set(); - KThread thread = KernelStatic.GetCurrentThread(); - ulong messagePtr = thread.TlsAddress; + ulong messagePtr = _selfThread.TlsAddress; _context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000); _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0); @@ -134,27 +193,39 @@ namespace Ryujinx.HLE.HOS.Services while (true) { - int handleCount; int portHandleCount; + int handleCount; int[] handles; - lock (_handleLock) + bool handleLockTaken = false; + try { - portHandleCount = _portHandles.Count; - handleCount = portHandleCount + _sessionHandles.Count; + handleLockTaken = _handleLock.TryEnterReadLock(Timeout.Infinite); + + portHandleCount = _ports.Count; + + handleCount = portHandleCount + _sessions.Count; handles = ArrayPool<int>.Shared.Rent(handleCount); - _portHandles.CopyTo(handles, 0); - _sessionHandles.CopyTo(handles, portHandleCount); + _ports.Keys.CopyTo(handles, 0); + + _sessions.Keys.CopyTo(handles, portHandleCount); + } + finally + { + if (handleLockTaken) + { + _handleLock.ExitReadLock(); + } } // We still need a timeout here to allow the service to pick up and listen new sessions... var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L); - thread.HandlePostSyscall(); + _selfThread.HandlePostSyscall(); - if (!thread.Context.Running) + if (!_selfThread.Context.Running) { break; } @@ -178,9 +249,20 @@ namespace Ryujinx.HLE.HOS.Services // We got a new connection, accept the session to allow servicing future requests. if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success) { - IpcService obj = _ports[handles[signaledIndex]].Invoke(); - - AddSessionObj(serverSessionHandle, obj); + bool handleWriteLockTaken = false; + try + { + handleWriteLockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite); + IpcService obj = _ports[handles[signaledIndex]].Invoke(); + _sessions.Add(serverSessionHandle, obj); + } + finally + { + if (handleWriteLockTaken) + { + _handleLock.ExitWriteLock(); + } + } } } @@ -197,11 +279,7 @@ namespace Ryujinx.HLE.HOS.Services private bool Process(int serverSessionHandle, ulong recvListAddr) { - KProcess process = KernelStatic.GetCurrentProcess(); - KThread thread = KernelStatic.GetCurrentThread(); - ulong messagePtr = thread.TlsAddress; - - IpcMessage request = ReadRequest(process, messagePtr); + IpcMessage request = ReadRequest(); IpcMessage response = new IpcMessage(); @@ -247,15 +325,15 @@ namespace Ryujinx.HLE.HOS.Services ServiceCtx context = new ServiceCtx( _context.Device, - process, - process.CpuMemory, - thread, + _selfProcess, + _selfProcess.CpuMemory, + _selfThread, request, response, _requestDataReader, _responseDataWriter); - _sessions[serverSessionHandle].CallCmifMethod(context); + GetSessionObj(serverSessionHandle).CallCmifMethod(context); response.RawData = _responseDataStream.ToArray(); } @@ -268,7 +346,7 @@ namespace Ryujinx.HLE.HOS.Services switch (cmdId) { case 0: - FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain()); + FillHipcResponse(response, 0, GetSessionObj(serverSessionHandle).ConvertToDomain()); break; case 3: @@ -278,17 +356,31 @@ namespace Ryujinx.HLE.HOS.Services // TODO: Whats the difference between IpcDuplicateSession/Ex? case 2: case 4: - int unknown = _requestDataReader.ReadInt32(); + { + _ = _requestDataReader.ReadInt32(); - _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0); + _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0); - AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]); + bool writeLockTaken = false; + try + { + writeLockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite); + _sessions[dupServerSessionHandle] = _sessions[serverSessionHandle]; + } + finally + { + if (writeLockTaken) + { + _handleLock.ExitWriteLock(); + } + } - response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle); + response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle); - FillHipcResponse(response, 0); + FillHipcResponse(response, 0); - break; + break; + } default: throw new NotImplementedException(cmdId.ToString()); } @@ -296,13 +388,10 @@ namespace Ryujinx.HLE.HOS.Services else if (request.Type == IpcMessageType.CmifCloseSession || request.Type == IpcMessageType.TipcCloseSession) { _context.Syscall.CloseHandle(serverSessionHandle); - lock (_handleLock) + if (RemoveSessionObj(serverSessionHandle, out var session)) { - _sessionHandles.Remove(serverSessionHandle); + (session as IDisposable)?.Dispose(); } - IpcService service = _sessions[serverSessionHandle]; - (service as IDisposable)?.Dispose(); - _sessions.Remove(serverSessionHandle); shouldReply = false; } // If the type is past 0xF, we are using TIPC @@ -317,20 +406,20 @@ namespace Ryujinx.HLE.HOS.Services ServiceCtx context = new ServiceCtx( _context.Device, - process, - process.CpuMemory, - thread, + _selfProcess, + _selfProcess.CpuMemory, + _selfThread, request, response, _requestDataReader, _responseDataWriter); - _sessions[serverSessionHandle].CallTipcMethod(context); + GetSessionObj(serverSessionHandle).CallTipcMethod(context); response.RawData = _responseDataStream.ToArray(); using var responseStream = response.GetStreamTipc(); - process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence()); + _selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence()); } else { @@ -339,27 +428,24 @@ namespace Ryujinx.HLE.HOS.Services if (!isTipcCommunication) { - using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)); - process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence()); + using var responseStream = response.GetStream((long)_selfThread.TlsAddress, recvListAddr | ((ulong)PointerBufferSize << 48)); + _selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence()); } return shouldReply; } - private static IpcMessage ReadRequest(KProcess process, ulong messagePtr) + private IpcMessage ReadRequest() { const int messageSize = 0x100; - byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize); + using IMemoryOwner<byte> reqDataOwner = ByteMemoryPool.Shared.Rent(messageSize); - Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize); - reqDataSpan.Clear(); + Span<byte> reqDataSpan = reqDataOwner.Memory.Span; - process.CpuMemory.Read(messagePtr, reqDataSpan); + _selfProcess.CpuMemory.Read(_selfThread.TlsAddress, reqDataSpan); - IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr); - - ArrayPool<byte>.Shared.Return(reqData); + IpcMessage request = new IpcMessage(reqDataSpan, (long)_selfThread.TlsAddress); return request; } @@ -392,26 +478,35 @@ namespace Ryujinx.HLE.HOS.Services protected virtual void Dispose(bool disposing) { - if (disposing) + if (disposing && _selfThread != null) { - foreach (IpcService service in _sessions.Values) + if (_selfThread.HostThread.ManagedThreadId != Environment.CurrentManagedThreadId && _selfThread.HostThread.Join(ThreadJoinTimeout) == false) { - if (service is IDisposable disposableObj) - { - disposableObj.Dispose(); - } + Logger.Warning?.Print(LogClass.Service, $"The ServerBase thread didn't terminate within {ThreadJoinTimeout:g}, waiting longer."); - service.DestroyAtExit(); + _selfThread.HostThread.Join(Timeout.Infinite); } - _sessions.Clear(); + if (Interlocked.Exchange(ref _isDisposed, 1) == 0) + { + foreach (IpcService service in _sessions.Values) + { + (service as IDisposable)?.Dispose(); - _requestDataReader.Dispose(); - _requestDataStream.Dispose(); - _responseDataWriter.Dispose(); - _responseDataStream.Dispose(); + service.DestroyAtExit(); + } - InitDone.Dispose(); + _sessions.Clear(); + _ports.Clear(); + _handleLock.Dispose(); + + _requestDataReader.Dispose(); + _requestDataStream.Dispose(); + _responseDataWriter.Dispose(); + _responseDataStream.Dispose(); + + InitDone.Dispose(); + } } } From ac66643346df76561ff85be741e2998290d43646 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sun, 21 May 2023 15:39:06 -0400 Subject: [PATCH 554/737] Fix crash in SettingsViewModel when Vulkan isn't available (#4985) * fix crash when Vulkan isn't available * add VulkanRenderer.GetPhysicalDevices() overload that provides its own Vk API object and logs on failure * adjustments per AcK77 --- .../UI/ViewModels/SettingsViewModel.cs | 2 +- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 19 +++++++++++++++++++ src/Ryujinx/Ui/Windows/SettingsWindow.cs | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 89392f6b2..d260b6fc7 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -311,7 +311,7 @@ namespace Ryujinx.Ava.UI.ViewModels { _gpuIds = new List<string>(); List<string> names = new(); - var devices = VulkanRenderer.GetPhysicalDevices(Vk.GetApi()); + var devices = VulkanRenderer.GetPhysicalDevices(); if (devices.Length == 0) { diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index ffa1a1034..74267325c 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -599,6 +599,25 @@ namespace Ryujinx.Graphics.Vulkan return new HardwareInfo(GpuVendor, GpuRenderer); } + /// <summary> + /// Gets the available Vulkan devices using the default Vulkan API + /// object returned by <see cref="Vk.GetApi()"/> + /// </summary> + /// <returns></returns> + public static DeviceInfo[] GetPhysicalDevices() + { + try + { + return VulkanInitialization.GetSuitablePhysicalDevices(Vk.GetApi()); + } + catch (Exception ex) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Error querying Vulkan devices: {ex.Message}"); + + return Array.Empty<DeviceInfo>(); + } + } + public static DeviceInfo[] GetPhysicalDevices(Vk api) { try diff --git a/src/Ryujinx/Ui/Windows/SettingsWindow.cs b/src/Ryujinx/Ui/Windows/SettingsWindow.cs index 65175ff15..fbfbf527c 100644 --- a/src/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/src/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -478,7 +478,7 @@ namespace Ryujinx.Ui.Windows if (Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan) { - var devices = VulkanRenderer.GetPhysicalDevices(Vk.GetApi()); + var devices = VulkanRenderer.GetPhysicalDevices(); string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value; string preferredGpuId = preferredGpuIdFromConfig; bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig); From b53e7ffd46b5c4ac5c4ac3dcc24385b2c9dc4fa4 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 22 May 2023 00:16:20 +0100 Subject: [PATCH 555/737] Ava UI: Input Menu Redesign (#4990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cleanup * Remove redundant locales * Start SVG Fixes… Better +/- buttons Fix the grips Bumpers Better directional pad More SVG stuff Grip adjustments Final stuff * Make image bigger * Border radius * More cleanup * Restructure * Restructure Rumble View * Use compiled bindings where possible * Round those pesky corners * Ack Suggestions * More suggestions * Update src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 29 +- ...ewModel.cs => ControllerInputViewModel.cs} | 11 +- .../UI/ViewModels/MotionInputViewModel.cs | 93 +++ .../UI/ViewModels/RumbleInputViewModel.cs | 27 + .../Input/ControllerInputView.axaml} | 602 +++++++++--------- .../Input/ControllerInputView.axaml.cs} | 11 +- .../Input/MotionInputView.axaml} | 104 +-- .../Input/MotionInputView.axaml.cs} | 43 +- .../Input/RumbleInputView.axaml} | 7 +- .../Input/RumbleInputView.axaml.cs} | 37 +- .../UI/Views/Settings/SettingsInputView.axaml | 70 +- .../Resources/Controller_ProCon.svg | 133 +++- 12 files changed, 731 insertions(+), 436 deletions(-) rename src/Ryujinx.Ava/UI/ViewModels/{ControllerSettingsViewModel.cs => ControllerInputViewModel.cs} (99%) create mode 100644 src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs create mode 100644 src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs rename src/Ryujinx.Ava/UI/{Windows/ControllerSettingsWindow.axaml => Views/Input/ControllerInputView.axaml} (71%) rename src/Ryujinx.Ava/UI/{Windows/ControllerSettingsWindow.axaml.cs => Views/Input/ControllerInputView.axaml.cs} (94%) rename src/Ryujinx.Ava/UI/{Windows/MotionSettingsWindow.axaml => Views/Input/MotionInputView.axaml} (59%) rename src/Ryujinx.Ava/UI/{Windows/MotionSettingsWindow.axaml.cs => Views/Input/MotionInputView.axaml.cs} (51%) rename src/Ryujinx.Ava/UI/{Windows/RumbleSettingsWindow.axaml => Views/Input/RumbleInputView.axaml} (91%) rename src/Ryujinx.Ava/UI/{Windows/RumbleSettingsWindow.axaml.cs => Views/Input/RumbleInputView.axaml.cs} (50%) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index c9b10f54a..a7b490b91 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -216,26 +216,17 @@ "ControllerSettingsDPadDown": "Down", "ControllerSettingsDPadLeft": "Left", "ControllerSettingsDPadRight": "Right", + "ControllerSettingsStickButton": "Button", + "ControllerSettingsStickUp": "Up", + "ControllerSettingsStickDown": "Down", + "ControllerSettingsStickLeft": "Left", + "ControllerSettingsStickRight": "Right", + "ControllerSettingsStickStick": "Stick", + "ControllerSettingsStickInvertXAxis": "Invert Stick X", + "ControllerSettingsStickInvertYAxis": "Invert Stick Y", + "ControllerSettingsStickDeadzone": "Deadzone:", "ControllerSettingsLStick": "Left Stick", - "ControllerSettingsLStickButton": "Button", - "ControllerSettingsLStickUp": "Up", - "ControllerSettingsLStickDown": "Down", - "ControllerSettingsLStickLeft": "Left", - "ControllerSettingsLStickRight": "Right", - "ControllerSettingsLStickStick": "Stick", - "ControllerSettingsLStickInvertXAxis": "Invert Stick X", - "ControllerSettingsLStickInvertYAxis": "Invert Stick Y", - "ControllerSettingsLStickDeadzone": "Deadzone:", "ControllerSettingsRStick": "Right Stick", - "ControllerSettingsRStickButton": "Button", - "ControllerSettingsRStickUp": "Up", - "ControllerSettingsRStickDown": "Down", - "ControllerSettingsRStickLeft": "Left", - "ControllerSettingsRStickRight": "Right", - "ControllerSettingsRStickStick": "Stick", - "ControllerSettingsRStickInvertXAxis": "Invert Stick X", - "ControllerSettingsRStickInvertYAxis": "Invert Stick Y", - "ControllerSettingsRStickDeadzone": "Deadzone:", "ControllerSettingsTriggersLeft": "Triggers Left", "ControllerSettingsTriggersRight": "Triggers Right", "ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left", @@ -647,4 +638,4 @@ "NetworkInterfaceTooltip": "The network interface used for LAN features", "NetworkInterfaceDefault": "Default", "PackagingShaders": "Packaging Shaders" -} +} \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs similarity index 99% rename from src/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs index dd261b103..fda58504e 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs @@ -7,6 +7,7 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -30,7 +31,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key; namespace Ryujinx.Ava.UI.ViewModels { - public class ControllerSettingsViewModel : BaseModel, IDisposable + public class ControllerInputViewModel : BaseModel, IDisposable { private const string Disabled = "disabled"; private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg"; @@ -231,7 +232,7 @@ namespace Ryujinx.Ava.UI.ViewModels public InputConfig Config { get; set; } - public ControllerSettingsViewModel(UserControl owner) : this() + public ControllerInputViewModel(UserControl owner) : this() { _owner = owner; @@ -258,7 +259,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public ControllerSettingsViewModel() + public ControllerInputViewModel() { PlayerIndexes = new ObservableCollection<PlayerModel>(); Controllers = new ObservableCollection<ControllerModel>(); @@ -328,12 +329,12 @@ namespace Ryujinx.Ava.UI.ViewModels public async void ShowMotionConfig() { - await MotionSettingsWindow.Show(this); + await MotionInputView.Show(this); } public async void ShowRumbleConfig() { - await RumbleSettingsWindow.Show(this); + await RumbleInputView.Show(this); } private void LoadInputDriver() diff --git a/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs new file mode 100644 index 000000000..d57ccc6bb --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs @@ -0,0 +1,93 @@ +namespace Ryujinx.Ava.UI.ViewModels +{ + public class MotionInputViewModel : BaseModel + { + private int _slot; + public int Slot + { + get => _slot; + set + { + _slot = value; + OnPropertyChanged(); + } + } + + private int _altSlot; + public int AltSlot + { + get => _altSlot; + set + { + _altSlot = value; + OnPropertyChanged(); + } + } + + private string _dsuServerHost; + public string DsuServerHost + { + get => _dsuServerHost; + set + { + _dsuServerHost = value; + OnPropertyChanged(); + } + } + + private int _dsuServerPort; + public int DsuServerPort + { + get => _dsuServerPort; + set + { + _dsuServerPort = value; + OnPropertyChanged(); + } + } + + private bool _mirrorInput; + public bool MirrorInput + { + get => _mirrorInput; + set + { + _mirrorInput = value; + OnPropertyChanged(); + } + } + + private int _sensitivity; + public int Sensitivity + { + get => _sensitivity; + set + { + _sensitivity = value; + OnPropertyChanged(); + } + } + + private double _gryoDeadzone; + public double GyroDeadzone + { + get => _gryoDeadzone; + set + { + _gryoDeadzone = value; + OnPropertyChanged(); + } + } + + private bool _enableCemuHookMotion; + public bool EnableCemuHookMotion + { + get => _enableCemuHookMotion; + set + { + _enableCemuHookMotion = value; + OnPropertyChanged(); + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs new file mode 100644 index 000000000..2d53968f7 --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.Ava.UI.ViewModels +{ + public class RumbleInputViewModel : BaseModel + { + private float _strongRumble; + public float StrongRumble + { + get => _strongRumble; + set + { + _strongRumble = value; + OnPropertyChanged(); + } + } + + private float _weakRumble; + public float WeakRumble + { + get => _weakRumble; + set + { + _weakRumble = value; + OnPropertyChanged(); + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml similarity index 71% rename from src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml rename to src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml index 1a11a8ff3..2395b353c 100644 --- a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml @@ -1,22 +1,24 @@ <UserControl - x:Class="Ryujinx.Ava.UI.Windows.ControllerSettingsWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" d:DesignHeight="800" d:DesignWidth="800" - x:CompileBindings="False" + x:Class="Ryujinx.Ava.UI.Views.Input.ControllerInputView" + x:DataType="viewModels:ControllerInputViewModel" + x:CompileBindings="True" mc:Ignorable="d" Focusable="True"> <Design.DataContext> - <viewModels:ControllerSettingsViewModel /> + <viewModels:ControllerInputViewModel /> </Design.DataContext> <UserControl.Resources> <helpers:KeyValueConverter x:Key="Key" /> @@ -28,29 +30,23 @@ <Setter Property="HorizontalAlignment" Value="Stretch" /> </Style> </UserControl.Styles> - <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical"> - <Grid Margin="0,2,0,5" HorizontalAlignment="Stretch"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="*" /> - <ColumnDefinition Width="*" /> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - </Grid.RowDefinitions> - - <!-- Player selection --> - <Border - Grid.Row="0" - Grid.Column="0" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - Padding="2"> + <StackPanel + Margin="0 0 0 5" + Orientation="Vertical" + Spacing="5"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="10" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <!-- Player Selection --> <Grid + Grid.Column="0" Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Center"> @@ -79,16 +75,9 @@ </ComboBox.ItemTemplate> </ComboBox> </Grid> - </Border> - <!-- Profile selection --> - <Border - Grid.Row="1" - Grid.Column="0" - Margin="0,-1,0,0" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - Padding="2"> + <!-- Profile Selection --> <Grid + Grid.Column="2" Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Center"> @@ -120,7 +109,7 @@ Margin="5,0,0,0" VerticalAlignment="Center" ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}" - Command="{Binding LoadProfile}"> + Command="{ReflectionBinding LoadProfile}"> <ui:SymbolIcon Symbol="Upload" FontSize="15" @@ -132,7 +121,7 @@ Margin="5,0,0,0" VerticalAlignment="Center" ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}" - Command="{Binding SaveProfile}"> + Command="{ReflectionBinding SaveProfile}"> <ui:SymbolIcon Symbol="Save" FontSize="15" @@ -144,24 +133,26 @@ Margin="5,0,0,0" VerticalAlignment="Center" ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}" - Command="{Binding RemoveProfile}"> + Command="{ReflectionBinding RemoveProfile}"> <ui:SymbolIcon Symbol="Delete" FontSize="15" Height="20" /> </Button> </Grid> - </Border> - - <!-- Input device --> - <Border - Grid.Row="0" - Grid.Column="1" - Margin="-1,0,0,0" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - Padding="2"> - <Grid Margin="2" HorizontalAlignment="Stretch"> + </Grid> + <Separator /> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="10" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <!-- Input Device --> + <Grid + Grid.Column="0" + Margin="2" + HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> @@ -186,24 +177,16 @@ MinWidth="0" Margin="5,0,0,0" VerticalAlignment="Center" - Command="{Binding LoadDevices}"> + Command="{ReflectionBinding LoadDevices}"> <ui:SymbolIcon Symbol="Refresh" FontSize="15" Height="20"/> </Button> </Grid> - </Border> - - <!-- Controler type --> - <Border - Grid.Row="1" - Grid.Column="1" - Margin="-1,-1,0,0" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - Padding="2"> + <!-- Controller Type --> <Grid + Grid.Column="2" Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Center"> @@ -220,19 +203,18 @@ <ComboBox Grid.Column="1" HorizontalAlignment="Stretch" - Items="{Binding Controllers}" - SelectedIndex="{Binding Controller}"> + Items="{ReflectionBinding Controllers}" + SelectedIndex="{ReflectionBinding Controller}"> <ComboBox.ItemTemplate> - <DataTemplate> + <DataTemplate DataType="models:ControllerModel"> <TextBlock Text="{Binding Name}" /> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> </Grid> - </Border> - </Grid> - - <!-- Button / JoyStick Settings --> + </Grid> + </StackPanel> + <!-- Button / JoyStick Settings --> <Grid Name="SettingButtons" MinHeight="450" @@ -242,27 +224,21 @@ <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> - - <!-- Left --> - <Grid + <!-- Left Controls --> + <StackPanel + Orientation="Vertical" Margin="0,0,5,0" - Grid.Column="0" - VerticalAlignment="Stretch" - DockPanel.Dock="Left"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - </Grid.RowDefinitions> - - <!-- Left Triggers --> + Grid.Column="0"> + <!-- Left Triggers --> <Border - Grid.Row="0" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" IsVisible="{Binding IsLeft}" - MinHeight="90"> - <Grid Margin="10" HorizontalAlignment="Stretch"> + MinHeight="90" + CornerRadius="5"> + <Grid + Margin="10" + HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> @@ -274,7 +250,6 @@ <StackPanel Grid.Column="0" Grid.Row="0" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -284,14 +259,13 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonZl, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonZl, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> <StackPanel Grid.Column="0" Grid.Row="1" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -301,14 +275,13 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonL, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonL, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> <StackPanel Grid.Column="1" Grid.Row="1" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -318,159 +291,172 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonMinus, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonMinus, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> </Grid> </Border> - - <!-- Left Joystick --> + <!-- Left Joystick --> <Border - Grid.Row="1" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" IsVisible="{Binding IsLeft}" - Margin="0,5,0,0"> - <StackPanel Margin="10" Orientation="Vertical"> + Margin="0,5,0,0" + CornerRadius="5"> + <StackPanel + Margin="10" + Orientation="Vertical"> <TextBlock Margin="0,0,0,10" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsLStick}" /> - - <!-- Left Joystick Keyboard --> - <StackPanel IsVisible="{Binding !IsController}" Orientation="Vertical"> - - <!-- Left Joystick Button --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left Joystick Keyboard --> + <StackPanel + IsVisible="{Binding !IsController}" + Orientation="Vertical"> + <!-- Left Joystick Button --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickButton}" + Text="{locale:Locale ControllerSettingsStickButton}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left Joystick Up --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left Joystick Up --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickUp}" + Text="{locale:Locale ControllerSettingsStickUp}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftStickUp, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftStickUp, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left Joystick Down --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left Joystick Down --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickDown}" + Text="{locale:Locale ControllerSettingsStickDown}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftStickDown, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftStickDown, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left Joystick Left --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left Joystick Left --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickLeft}" + Text="{locale:Locale ControllerSettingsStickLeft}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left Joystick Right --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left Joystick Right --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickRight}" + Text="{locale:Locale ControllerSettingsStickRight}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftStickRight, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftStickRight, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> </StackPanel> - - <!-- Left Joystick Controller --> - <StackPanel IsVisible="{Binding IsController}" Orientation="Vertical"> - - <!-- Left Joystick Button --> - <StackPanel Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left Joystick Controller --> + <StackPanel + IsVisible="{Binding IsController}" + Orientation="Vertical"> + <!-- Left Joystick Button --> + <StackPanel + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickButton}" + Text="{locale:Locale ControllerSettingsStickButton}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left Joystick Stick --> - <StackPanel Margin="0,4,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left Joystick Stick --> + <StackPanel + Margin="0,4,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickStick}" + Text="{locale:Locale ControllerSettingsStickStick}" TextAlignment="Center" /> <ToggleButton Tag="stick"> <TextBlock - Text="{Binding Configuration.LeftJoystick, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftJoystick, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - <Separator Margin="0,8,0,8" Height="1" /> - <CheckBox IsChecked="{Binding Configuration.LeftInvertStickX}"> - <TextBlock Text="{locale:Locale ControllerSettingsLStickInvertXAxis}" /> + <Separator + Margin="0,8,0,8" + Height="1" /> + <CheckBox IsChecked="{ReflectionBinding Configuration.LeftInvertStickX}"> + <TextBlock Text="{locale:Locale ControllerSettingsStickInvertXAxis}" /> </CheckBox> - <CheckBox IsChecked="{Binding Configuration.LeftInvertStickY}"> - <TextBlock Text="{locale:Locale ControllerSettingsLStickInvertYAxis}" /> + <CheckBox IsChecked="{ReflectionBinding Configuration.LeftInvertStickY}"> + <TextBlock Text="{locale:Locale ControllerSettingsStickInvertYAxis}" /> </CheckBox> - <CheckBox IsChecked="{Binding Configuration.LeftRotate90}"> + <CheckBox IsChecked="{ReflectionBinding Configuration.LeftRotate90}"> <TextBlock Text="{locale:Locale ControllerSettingsRotate90}" /> </CheckBox> - <Separator Margin="0,8,0,8" Height="1" /> + <Separator + Margin="0,8,0,8" + Height="1" /> <StackPanel Orientation="Vertical"> - <TextBlock - HorizontalAlignment="Center" - Text="{locale:Locale ControllerSettingsLStickDeadzone}" /> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsStickDeadzone}" /> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" @@ -481,14 +467,14 @@ TickFrequency="0.01" IsSnapToTickEnabled="True" Minimum="0" - Value="{Binding Configuration.DeadzoneLeft, Mode=TwoWay}" /> + Value="{ReflectionBinding Configuration.DeadzoneLeft, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" Width="25" - Text="{Binding Configuration.DeadzoneLeft, StringFormat=\{0:0.00\}}" /> + Text="{ReflectionBinding Configuration.DeadzoneLeft, StringFormat=\{0:0.00\}}" /> </StackPanel> - <TextBlock - HorizontalAlignment="Center" + <TextBlock + HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsStickRange}" /> <StackPanel HorizontalAlignment="Center" @@ -500,33 +486,36 @@ TickFrequency="0.01" IsSnapToTickEnabled="True" Minimum="0" - Value="{Binding Configuration.RangeLeft, Mode=TwoWay}" /> + Value="{ReflectionBinding Configuration.RangeLeft, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" Width="25" - Text="{Binding Configuration.RangeLeft, StringFormat=\{0:0.00\}}" /> + Text="{ReflectionBinding Configuration.RangeLeft, StringFormat=\{0:0.00\}}" /> </StackPanel> </StackPanel> </StackPanel> </StackPanel> </Border> - - <!-- Left DPad --> + <!-- Left DPad --> <Border - Grid.Row="2" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" VerticalAlignment="Top" IsVisible="{Binding IsLeft}" - Margin="0,5,0,0"> - <StackPanel Margin="10" Orientation="Vertical"> + Margin="0,5,0,0" + CornerRadius="5"> + <StackPanel + Margin="10" + Orientation="Vertical"> <TextBlock Margin="0,0,0,10" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsDPad}" /> <StackPanel Orientation="Vertical"> - <!-- Left DPad Up --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left DPad Up --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" @@ -534,16 +523,16 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadUp}" TextAlignment="Center" /> - <ToggleButton - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock - Text="{Binding Configuration.DpadUp, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.DpadUp, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left DPad Down --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left DPad Down --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" @@ -551,16 +540,16 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadDown}" TextAlignment="Center" /> - <ToggleButton - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock - Text="{Binding Configuration.DpadDown, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.DpadDown, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left DPad Left --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left DPad Left --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" @@ -568,16 +557,16 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadLeft}" TextAlignment="Center" /> - <ToggleButton - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock - Text="{Binding Configuration.DpadLeft, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.DpadLeft, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Left DPad Right --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Left DPad Right --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" @@ -585,42 +574,50 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadRight}" TextAlignment="Center" /> - <ToggleButton - HorizontalAlignment="Stretch"> + <ToggleButton> <TextBlock - Text="{Binding Configuration.DpadRight, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.DpadRight, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> </StackPanel> </StackPanel> </Border> - </Grid> - - <!-- Triggers And Side Buttons--> - <StackPanel Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> + </StackPanel> + <!-- Triggers & Side Buttons --> + <StackPanel + Grid.Column="1" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch"> <Border BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" MinHeight="90"> - <StackPanel Margin="8" Orientation="Vertical"> - <TextBlock HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerThreshold}" /> - <StackPanel HorizontalAlignment="Center" Orientation="Horizontal"> + BorderThickness="1" + CornerRadius="5" + MinHeight="90"> + <StackPanel + Margin="8" + Orientation="Vertical"> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsTriggerThreshold}" /> + <StackPanel + HorizontalAlignment="Center" + Orientation="Horizontal"> <Slider Width="130" Maximum="1" TickFrequency="0.01" IsSnapToTickEnabled="True" Minimum="0" - Value="{Binding Configuration.TriggerThreshold, Mode=TwoWay}" /> + Value="{ReflectionBinding Configuration.TriggerThreshold, Mode=TwoWay}" /> <TextBlock Width="25" - Text="{Binding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" /> + Text="{ReflectionBinding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" /> </StackPanel> <StackPanel Margin="0,4,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" - Background="{DynamicResource ThemeDarkColor}" IsVisible="{Binding !IsRight}" Orientation="Horizontal"> <TextBlock @@ -631,7 +628,7 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> @@ -640,7 +637,6 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding !IsRight}" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -650,7 +646,7 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.LeftButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.LeftButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> @@ -659,7 +655,6 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding !IsLeft}" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -669,7 +664,7 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> @@ -678,7 +673,6 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding !IsLeft}" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -688,26 +682,29 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> </StackPanel> </Border> - - <!-- Controller Picture --> + <!-- Controller Picture --> <Image Margin="0,10,0,0" - MaxHeight="250" + MaxHeight="300" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Source="{Binding Image}" /> - - <!-- Motion+Rumble --> - <StackPanel Margin="0,10,0,0" Orientation="Vertical" VerticalAlignment="Bottom"> + <!-- Motion + Rumble --> + <StackPanel + Margin="0,10,0,0" + Spacing="5" + Orientation="Vertical" + VerticalAlignment="Bottom"> <Border BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" + CornerRadius="5" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" IsVisible="{Binding IsController}"> @@ -720,10 +717,13 @@ Margin="10" MinWidth="0" Grid.Column="0" - IsChecked="{Binding Configuration.EnableMotion, Mode=TwoWay}"> + IsChecked="{ReflectionBinding Configuration.EnableMotion, Mode=TwoWay}"> <TextBlock Text="{locale:Locale ControllerSettingsMotion}" /> </CheckBox> - <Button Margin="10" Grid.Column="1" Command="{Binding ShowMotionConfig}"> + <Button + Margin="10" + Grid.Column="1" + Command="{ReflectionBinding ShowMotionConfig}"> <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" /> </Button> </Grid> @@ -731,6 +731,7 @@ <Border BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" + CornerRadius="5" HorizontalAlignment="Stretch" IsVisible="{Binding IsController}" Margin="0,-1,0,0"> @@ -743,36 +744,34 @@ Margin="10" MinWidth="0" Grid.Column="0" - IsChecked="{Binding Configuration.EnableRumble, Mode=TwoWay}"> + IsChecked="{ReflectionBinding Configuration.EnableRumble, Mode=TwoWay}"> <TextBlock Text="{locale:Locale ControllerSettingsRumble}" /> </CheckBox> - <Button Margin="10" Grid.Column="1" Command="{Binding ShowRumbleConfig}"> + <Button + Margin="10" + Grid.Column="1" + Command="{ReflectionBinding ShowRumbleConfig}"> <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" /> </Button> </Grid> </Border> </StackPanel> </StackPanel> - - <!--Right Controls--> - <Grid + <!-- Right Controls --> + <StackPanel + Orientation="Vertical" Margin="5,0,0,0" - Grid.Column="2" - VerticalAlignment="Top" - HorizontalAlignment="Stretch" > - <Grid.RowDefinitions> - <RowDefinition /> - <RowDefinition /> - <RowDefinition /> - <RowDefinition /> - </Grid.RowDefinitions> + Grid.Column="2"> + <!-- Right Triggers --> <Border - Grid.Row="0" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" IsVisible="{Binding IsRight}" - MinHeight="90"> - <Grid Margin="10" HorizontalAlignment="Stretch"> + MinHeight="90" + CornerRadius="5"> + <Grid + Margin="10" + HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> @@ -784,7 +783,6 @@ <StackPanel Grid.Column="1" Grid.Row="0" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -794,7 +792,7 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonZr, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonZr, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> @@ -803,7 +801,6 @@ Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -813,7 +810,7 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonR, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonR, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> @@ -822,7 +819,6 @@ Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="20" @@ -832,28 +828,31 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonPlus, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonPlus, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> </Grid> </Border> + <!-- Right Joystick --> <Border - Grid.Row="1" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" IsVisible="{Binding IsRight}" - Margin="0,5,0,0"> - <StackPanel Margin="10" Orientation="Vertical"> + Margin="0,5,0,0" + CornerRadius="5"> + <StackPanel + Margin="10" + Orientation="Vertical"> <TextBlock Margin="0,0,0,10" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsButtons}" /> - <StackPanel Orientation="Vertical"> - <!-- Right Buttons A --> + <StackPanel + Orientation="Vertical"> + <!-- Right Buttons A --> <StackPanel Margin="0,0,0,4" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="120" @@ -864,14 +863,13 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonA, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonA, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - <!-- Right Buttons B --> + <!-- Right Buttons B --> <StackPanel Margin="0,0,0,4" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="120" @@ -882,14 +880,13 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonB, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonB, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - <!-- Right Buttons X --> + <!-- Right Buttons X --> <StackPanel Margin="0,0,0,4" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="120" @@ -900,14 +897,13 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonX, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonX, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - <!-- Right Buttons Y --> + <!-- Right Buttons Y --> <StackPanel Margin="0,0,0,4" - Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <TextBlock Width="120" @@ -918,18 +914,19 @@ TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.ButtonY, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.ButtonY, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> </StackPanel> </StackPanel> </Border> + <!-- Right DPad --> <Border - Grid.Row="2" Padding="10" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" + CornerRadius="5" IsVisible="{Binding IsRight}" Margin="0,5,0,0"> <StackPanel Orientation="Vertical"> @@ -937,141 +934,149 @@ Margin="0,0,0,10" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsRStick}" /> - - <!-- Right Joystick Keyboard --> - <StackPanel IsVisible="{Binding !IsController}" Orientation="Vertical"> - - <!-- Right Joystick Button --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Right Joystick Keyboard --> + <StackPanel + IsVisible="{Binding !IsController}" + Orientation="Vertical"> + <!-- Right Joystick Button --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickButton}" + Text="{locale:Locale ControllerSettingsStickButton}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Right Joystick Up --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Right Joystick Up --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickUp}" + Text="{locale:Locale ControllerSettingsStickUp}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightStickUp, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightStickUp, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Right Joystick Down --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Right Joystick Down --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickDown}" + Text="{locale:Locale ControllerSettingsStickDown}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightStickDown, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightStickDown, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Right Joystick Left --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Right Joystick Left --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickLeft}" + Text="{locale:Locale ControllerSettingsStickLeft}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - <!-- Right Joystick Right --> - <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Right Joystick Right --> + <StackPanel + Margin="0,0,0,4" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickRight}" + Text="{locale:Locale ControllerSettingsStickRight}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightStickRight, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightStickRight, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> </StackPanel> - - <!-- Right Joystick Controller --> - <StackPanel IsVisible="{Binding IsController}" Orientation="Vertical"> - - <!-- Right Joystick Button --> - <StackPanel Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Right Joystick Controller --> + <StackPanel + IsVisible="{Binding IsController}" + Orientation="Vertical"> + <!-- Right Joystick Button --> + <StackPanel + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickButton}" + Text="{locale:Locale ControllerSettingsStickButton}" TextAlignment="Center" /> <ToggleButton> <TextBlock - Text="{Binding Configuration.RightControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> - - - <!-- Right Joystick Stick --> - <StackPanel Margin="0,4,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> + <!-- Right Joystick Stick --> + <StackPanel + Margin="0,4,0,4" + Background="{DynamicResource ThemeDarkColor}" + Orientation="Horizontal"> <TextBlock Margin="0,0,10,0" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickStick}" + Text="{locale:Locale ControllerSettingsStickStick}" TextAlignment="Center" /> <ToggleButton Tag="stick"> <TextBlock - Text="{Binding Configuration.RightJoystick, Mode=TwoWay, Converter={StaticResource Key}}" + Text="{ReflectionBinding Configuration.RightJoystick, Mode=TwoWay, Converter={StaticResource Key}}" TextAlignment="Center" /> </ToggleButton> </StackPanel> <Separator Margin="0,8,0,8" Height="1" /> - <CheckBox IsChecked="{Binding Configuration.RightInvertStickX}"> - <TextBlock Text="{locale:Locale ControllerSettingsRStickInvertXAxis}" /> + <CheckBox IsChecked="{ReflectionBinding Configuration.RightInvertStickX}"> + <TextBlock Text="{locale:Locale ControllerSettingsStickInvertXAxis}" /> </CheckBox> - <CheckBox IsChecked="{Binding Configuration.RightInvertStickY}"> - <TextBlock Text="{locale:Locale ControllerSettingsRStickInvertYAxis}" /> + <CheckBox IsChecked="{ReflectionBinding Configuration.RightInvertStickY}"> + <TextBlock Text="{locale:Locale ControllerSettingsStickInvertYAxis}" /> </CheckBox> - <CheckBox IsChecked="{Binding Configuration.RightRotate90}"> + <CheckBox IsChecked="{ReflectionBinding Configuration.RightRotate90}"> <TextBlock Text="{locale:Locale ControllerSettingsRotate90}" /> </CheckBox> <Separator Margin="0,8,0,8" Height="1" /> <StackPanel Orientation="Vertical"> - <TextBlock - HorizontalAlignment="Center" - Text="{locale:Locale ControllerSettingsRStickDeadzone}" /> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsStickDeadzone}" /> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" @@ -1084,14 +1089,14 @@ Padding="0" VerticalAlignment="Center" Minimum="0" - Value="{Binding Configuration.DeadzoneRight, Mode=TwoWay}" /> + Value="{ReflectionBinding Configuration.DeadzoneRight, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" Width="25" - Text="{Binding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" /> + Text="{ReflectionBinding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" /> </StackPanel> - <TextBlock - HorizontalAlignment="Center" + <TextBlock + HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsStickRange}" /> <StackPanel HorizontalAlignment="Center" @@ -1103,20 +1108,17 @@ TickFrequency="0.01" IsSnapToTickEnabled="True" Minimum="0" - Value="{Binding Configuration.RangeRight, Mode=TwoWay}" /> + Value="{ReflectionBinding Configuration.RangeRight, Mode=TwoWay}" /> <TextBlock VerticalAlignment="Center" Width="25" - Text="{Binding Configuration.RangeRight, StringFormat=\{0:0.00\}}" /> + Text="{ReflectionBinding Configuration.RangeRight, StringFormat=\{0:0.00\}}" /> </StackPanel> </StackPanel> </StackPanel> </StackPanel> </Border> - </Grid> - + </StackPanel> </Grid> - </StackPanel> - </UserControl> \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs similarity index 94% rename from src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs index 2864b6daa..8fe7b9412 100644 --- a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs @@ -4,7 +4,6 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; @@ -13,18 +12,18 @@ using Ryujinx.Input; using Ryujinx.Input.Assigner; using System; -namespace Ryujinx.Ava.UI.Windows +namespace Ryujinx.Ava.UI.Views.Input { - public partial class ControllerSettingsWindow : UserControl + public partial class ControllerInputView : UserControl { private bool _dialogOpen; private ButtonKeyAssigner _currentAssigner; - internal ControllerSettingsViewModel ViewModel { get; set; } + internal ControllerInputViewModel ViewModel { get; set; } - public ControllerSettingsWindow() + public ControllerInputView() { - DataContext = ViewModel = new ControllerSettingsViewModel(this); + DataContext = ViewModel = new ControllerInputViewModel(this); InitializeComponent(); diff --git a/src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml similarity index 59% rename from src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml rename to src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml index 862998ac8..b18324379 100644 --- a/src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml @@ -1,12 +1,15 @@ -<UserControl +<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:Class="Ryujinx.Ava.UI.Windows.MotionSettingsWindow" + x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView" + x:CompileBindings="True" + x:DataType="viewModels:MotionInputViewModel" Focusable="True"> <Grid Margin="10"> <Grid.RowDefinitions> @@ -14,7 +17,9 @@ <RowDefinition /> </Grid.RowDefinitions> <StackPanel Orientation="Vertical"> - <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> + <StackPanel + Orientation="Horizontal" + HorizontalAlignment="Center"> <TextBlock Margin="0" HorizontalAlignment="Center" @@ -28,11 +33,14 @@ Maximum="100" Minimum="0" Value="{Binding Sensitivity, Mode=TwoWay}" /> - <TextBlock HorizontalAlignment="Center" - Margin="5, 0" - Text="{Binding Sensitivity, StringFormat=\{0:0\}%}" /> + <TextBlock + HorizontalAlignment="Center" + Margin="5, 0" + Text="{Binding Sensitivity, StringFormat=\{0:0\}%}" /> </StackPanel> - <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> + <StackPanel + Orientation="Horizontal" + HorizontalAlignment="Center"> <TextBlock Margin="0" HorizontalAlignment="Center" @@ -51,17 +59,25 @@ Margin="5, 0" Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" /> </StackPanel> - <Separator Height="1" Margin="0,5" /> - <CheckBox Margin="5" IsChecked="{Binding EnableCemuHookMotion}"> - <TextBlock Margin="0,3,0,0" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsMotionUseCemuhookCompatibleMotion}" /> + <Separator + Height="1" + Margin="0,5" /> + <CheckBox + Margin="5" + IsChecked="{Binding EnableCemuHookMotion}"> + <TextBlock + Margin="0,3,0,0" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsMotionUseCemuhookCompatibleMotion}" /> </CheckBox> </StackPanel> - <Border Grid.Row="1" - Padding="20,5" - BorderBrush="{DynamicResource ThemeControlBorderColor}" - BorderThickness="1" - HorizontalAlignment="Stretch"> + <Border + Grid.Row="1" + Padding="20,5" + BorderBrush="{DynamicResource ThemeControlBorderColor}" + BorderThickness="1" + CornerRadius="5" + HorizontalAlignment="Stretch"> <Grid VerticalAlignment="Top"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> @@ -109,30 +125,42 @@ <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> - <TextBlock Margin="0,10,0,0" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsMotionControllerSlot}" /> - <ui:NumberBox Grid.Row="0" Grid.Column="1" - Name="CemuHookSlotUpDown" - SmallChange="1" - LargeChange="1" - Maximum="4" - Minimum="0" - Value="{Binding Slot}" /> - <TextBlock Margin="0,10,0,0" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" - Text="{locale:Locale ControllerSettingsMotionRightJoyConSlot}" /> - <ui:NumberBox Grid.Row="1" Grid.Column="1" - Name="CemuHookRightJoyConSlotUpDown" - SmallChange="1" - LargeChange="1" - Maximum="4" - Minimum="0" - Value="{Binding AltSlot}" /> + <TextBlock + Margin="0,10,0,0" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsMotionControllerSlot}" /> + <ui:NumberBox + Grid.Row="0" + Grid.Column="1" + Name="CemuHookSlotUpDown" + SmallChange="1" + LargeChange="1" + Maximum="4" + Minimum="0" + Value="{Binding Slot}" /> + <TextBlock + Margin="0,10,0,0" + Grid.Row="1" + Grid.Column="0" + VerticalAlignment="Center" + Text="{locale:Locale ControllerSettingsMotionRightJoyConSlot}" /> + <ui:NumberBox + Grid.Row="1" + Grid.Column="1" + Name="CemuHookRightJoyConSlotUpDown" + SmallChange="1" + LargeChange="1" + Maximum="4" + Minimum="0" + Value="{Binding AltSlot}" /> </Grid> </StackPanel> - <CheckBox HorizontalAlignment="Center" - IsChecked="{Binding MirrorInput, Mode=TwoWay}"> - <TextBlock HorizontalAlignment="Center" - Text="{locale:Locale ControllerSettingsMotionMirrorInput}" /> + <CheckBox + HorizontalAlignment="Center" + IsChecked="{Binding MirrorInput, Mode=TwoWay}"> + <TextBlock + HorizontalAlignment="Center" + Text="{locale:Locale ControllerSettingsMotionMirrorInput}" /> </CheckBox> </StackPanel> </Grid> diff --git a/src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs similarity index 51% rename from src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs index c686e7c3e..88bbcd932 100644 --- a/src/Ryujinx.Ava/UI/Windows/MotionSettingsWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs @@ -6,44 +6,42 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common.Configuration.Hid.Controller; using System.Threading.Tasks; -namespace Ryujinx.Ava.UI.Windows +namespace Ryujinx.Ava.UI.Views.Input { - public partial class MotionSettingsWindow : UserControl + public partial class MotionInputView : UserControl { - private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel; + private MotionInputViewModel _viewModel; - public MotionSettingsWindow() + public MotionInputView() { InitializeComponent(); - DataContext = _viewmodel; } - public MotionSettingsWindow(ControllerSettingsViewModel viewmodel) + public MotionInputView(ControllerInputViewModel viewModel) { - var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; + var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; - _viewmodel = new InputConfiguration<GamepadInputId, StickInputId>() + _viewModel = new MotionInputViewModel { Slot = config.Slot, AltSlot = config.AltSlot, DsuServerHost = config.DsuServerHost, DsuServerPort = config.DsuServerPort, MirrorInput = config.MirrorInput, - EnableMotion = config.EnableMotion, Sensitivity = config.Sensitivity, GyroDeadzone = config.GyroDeadzone, EnableCemuHookMotion = config.EnableCemuHookMotion }; InitializeComponent(); - DataContext = _viewmodel; + DataContext = _viewModel; } - public static async Task Show(ControllerSettingsViewModel viewmodel) + public static async Task Show(ControllerInputViewModel viewModel) { - MotionSettingsWindow content = new MotionSettingsWindow(viewmodel); + MotionInputView content = new(viewModel); - ContentDialog contentDialog = new ContentDialog + ContentDialog contentDialog = new() { Title = LocaleManager.Instance[LocaleKeys.ControllerMotionTitle], PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave], @@ -53,16 +51,15 @@ namespace Ryujinx.Ava.UI.Windows }; contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; - config.Slot = content._viewmodel.Slot; - config.EnableMotion = content._viewmodel.EnableMotion; - 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; + var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; + 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.Ava/UI/Windows/RumbleSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml similarity index 91% rename from src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml rename to src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml index e47cc5bd1..3882ebe21 100644 --- a/src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml @@ -1,11 +1,14 @@ -<UserControl +<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:Class="Ryujinx.Ava.UI.Windows.RumbleSettingsWindow" + x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView" + x:DataType="viewModels:RumbleInputViewModel" + x:CompileBindings="True" Focusable="True"> <Grid Margin="10"> <Grid.RowDefinitions> diff --git a/src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs similarity index 50% rename from src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs rename to src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs index 178109d65..dfe05b08b 100644 --- a/src/Ryujinx.Ava/UI/Windows/RumbleSettingsWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs @@ -6,36 +6,37 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common.Configuration.Hid.Controller; using System.Threading.Tasks; -namespace Ryujinx.Ava.UI.Windows +namespace Ryujinx.Ava.UI.Views.Input { - public partial class RumbleSettingsWindow : UserControl + public partial class RumbleInputView : UserControl { - private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel; + private RumbleInputViewModel _viewModel; - public RumbleSettingsWindow() + public RumbleInputView() { InitializeComponent(); - DataContext = _viewmodel; } - public RumbleSettingsWindow(ControllerSettingsViewModel viewmodel) + public RumbleInputView(ControllerInputViewModel viewModel) { - var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; + var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; - _viewmodel = new InputConfiguration<GamepadInputId, StickInputId>() + _viewModel = new RumbleInputViewModel { - StrongRumble = config.StrongRumble, WeakRumble = config.WeakRumble + StrongRumble = config.StrongRumble, + WeakRumble = config.WeakRumble }; InitializeComponent(); - DataContext = _viewmodel; + + DataContext = _viewModel; } - public static async Task Show(ControllerSettingsViewModel viewmodel) + public static async Task Show(ControllerInputViewModel viewModel) { - RumbleSettingsWindow content = new RumbleSettingsWindow(viewmodel); + RumbleInputView content = new(viewModel); - ContentDialog contentDialog = new ContentDialog + ContentDialog contentDialog = new() { Title = LocaleManager.Instance[LocaleKeys.ControllerRumbleTitle], PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave], @@ -43,14 +44,14 @@ namespace Ryujinx.Ava.UI.Windows CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], Content = content, }; - + contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; - config.StrongRumble = content._viewmodel.StrongRumble; - config.WeakRumble = content._viewmodel.WeakRumble; + var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>; + config.StrongRumble = content._viewModel.StrongRumble; + config.WeakRumble = content._viewModel.WeakRumble; }; - + await contentDialog.ShowAsync(); } } diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml index 1c774bdad..22ff38f57 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml @@ -1,11 +1,11 @@ -<UserControl +<UserControl x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" - xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" + xmlns:views="clr-namespace:Ryujinx.Ava.UI.Views.Input" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" x:CompileBindings="True" @@ -13,34 +13,56 @@ <Design.DataContext> <viewModels:SettingsViewModel /> </Design.DataContext> - <ScrollViewer + <ScrollViewer Name="InputPage" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <Border Classes="settings"> - <StackPanel Margin="4" Orientation="Vertical"> - <StackPanel Orientation="Horizontal"> - <CheckBox Margin="5,0" - ToolTip.Tip="{locale:Locale DockModeToggleTooltip}" - IsChecked="{Binding EnableDockedMode}"> - <TextBlock VerticalAlignment="Center" - Text="{locale:Locale SettingsTabInputEnableDockedMode}" /> - </CheckBox> - <CheckBox Margin="5,0" - ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}" - IsChecked="{Binding EnableKeyboard}"> - <TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" /> - </CheckBox> - <CheckBox Margin="5,0" - ToolTip.Tip="{locale:Locale DirectMouseTooltip}" - IsChecked="{Binding EnableMouse}"> - <TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" /> - </CheckBox> - </StackPanel> - <window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" /> - </StackPanel> + <Panel + Margin="10"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <views:ControllerInputView + Grid.Row="0" + Name="ControllerSettings" /> + <StackPanel + Orientation="Vertical" + Grid.Row="2"> + <Separator + Margin="0 10" + Height="1" /> + <StackPanel + Orientation="Horizontal" + Spacing="10"> + <CheckBox + ToolTip.Tip="{locale:Locale DockModeToggleTooltip}" + MinWidth="0" + IsChecked="{Binding EnableDockedMode}"> + <TextBlock + Text="{locale:Locale SettingsTabInputEnableDockedMode}" /> + </CheckBox> + <CheckBox + ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}" + IsChecked="{Binding EnableKeyboard}"> + <TextBlock + Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" /> + </CheckBox> + <CheckBox + ToolTip.Tip="{locale:Locale DirectMouseTooltip}" + IsChecked="{Binding EnableMouse}"> + <TextBlock + Text="{locale:Locale SettingsTabInputDirectMouseAccess}" /> + </CheckBox> + </StackPanel> + </StackPanel> + </Grid> + </Panel> </Border> </ScrollViewer> </UserControl> \ No newline at end of file diff --git a/src/Ryujinx.Ui.Common/Resources/Controller_ProCon.svg b/src/Ryujinx.Ui.Common/Resources/Controller_ProCon.svg index 882167764..f5380f3ad 100644 --- a/src/Ryujinx.Ui.Common/Resources/Controller_ProCon.svg +++ b/src/Ryujinx.Ui.Common/Resources/Controller_ProCon.svg @@ -1 +1,132 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="Layer_1" width="1050" height="1050.5" x="0" y="0" version="1.1" viewBox="0 0 1050 1050.5" xml:space="preserve"><metadata id="metadata393"/><defs id="defs391"><rect id="rect6367" width="45.694" height="61.787" x="-3.253" y="255.147"/><rect id="rect6355" width="51.414" height="73.033" x="-2742.251" y="-791.542"/><rect id="rect6363" width="62.279" height="71.997" x="-2742.251" y="-791.542"/><rect id="rect6375" width="45.694" height="61.787" x="-3.253" y="255.147"/></defs><style id="style280" type="text/css">.st1{fill:#1abc9c}.st2{fill:#ccc}.st3{fill:#fff}</style><style id="style1175" type="text/css">.st1{fill:#1abc9c}.st2{fill:#ccc}.st3{fill:#fff}</style><path id="path6345" d="m 660.218,317.28666 c -1.32475,0.81977 -0.20077,-1.70417 0.90068,-3.96853 3.06704,-6.30522 10.36825,-21.51171 18.29034,-36.65329 l 18.74304,-38.70005 7.52422,0.63048 c 2.87452,0.24086 19.69754,1.26308 37.84754,2.37647 86.06419,5.27955 152.87764,13.51997 163.54184,20.45174 0.71817,0.46681 2.77389,0.67882 2.86049,2.64483 0.13669,3.10302 -2.35468,8.99019 -5.41139,15.0731 -8.3397,16.59616 -12.04409,19.99098 -31.6575,24.44371 -26.13617,5.93357 -75.85796,9.73292 -160.88227,12.78643 -15.4,0.55306 -33.54449,0.6281 -40.00699,0.90614 -6.4625,0.27804 -11.75,0.28208 -11.75,0.009 z" style="opacity:1;fill:#3b3b3b;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6351" d="m 928.97724,329.51506 c 0.89282,-2.32663 6.90796,-23.8275 9.59446,-36.95969 9.22672,-45.10219 -9.52386,-91.93982 -26.87551,-118.1679 -0.34662,-0.52393 -9.61153,-9.20064 -9.96658,-9.69927 -13.68797,-19.22298 -65.66266,-59.56877 -76.83998,-60.73732 l -7.75062,-0.8103 -8.08788,-0.61111 3.94362,-9.019453 4.21503,-8.375144 c 10.36263,-20.590259 45.3232,-61.279156 59.73381,-70.957432 21.64007,-14.53364318 60.90681,-17.7914332 85.98048,-7.3149858 13.89131,5.8041638 33.90662,26.3200388 43.19013,46.0415988 10.4758,22.254425 13.9398,51.317276 12.4007,82.800486 -1.2288,25.13632 -5.2752,51.6733 -14.21,78.5612 -8.05068,24.22731 -19.93852,51.13312 -30.6471,69.72515 -3.8677,6.71502 -7.81756,12.23106 -11.04486,16.30587 -9.56335,12.07472 -36.06924,35.56003 -33.6357,29.2183 z" style="opacity:1;fill:#20221f;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6343" d="m 896.3932,236.40082 c -14.73005,-3.67801 -34.06098,-5.89763 -69.32894,-7.85779 -48.10172,-2.67345 -68.35314,-5.1435 -88.48333,-12.09268 -17.7828,-6.13883 -23.07021,-7.70117 -22.47581,-9.25015 5.74755,-14.97788 19.27653,-45.64132 33.33804,-71.49694 l 17.12305,-34.21339 21.88878,0.83519 c 10.89713,0.41579 23.74401,1.09904 30.42156,1.55732 l 12.11856,0.83168 17.96766,8.82897 16.58267,8.46727 7.65051,9.04576 c 3.88827,4.59738 8.20709,10.87063 9.7851,13.97094 3.83927,7.54295 10.28643,26.46749 14.52728,42.61223 3.62749,13.8097 11.1299,48.18972 11.1299,50.89707 0,1.93883 -2.69118,0.25006 -12.24503,-2.13548 z" style="opacity:1;fill:#3b3b3b;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6347" d="M 430.81645,341.14038 C 328.01189,339.106 130.76839,333.529 126.98969,331.19364 c -2.92039,-1.8049 -18.21136,-45.9964 -19.54296,-62.85189 -1.26055,-15.95616 8.14081,-40.64895 15.84477,-65.96842 7.80426,-25.64915 19.18555,-50.98797 29.70319,-65.63351 6.19529,-8.62678 26.86202,-14.49866 34.09013,-14.49866 2.73437,0 -5.66433,5.63407 -11.26391,14.10589 -10.06385,15.22597 -25.97579,76.07039 -29.84978,97.98818 l -0.62262,5.45685 7.57609,-0.73847 c 0.74354,-0.46776 18.3032,-3.38852 29.3032,-5.48486 21.24871,-4.0495 54.08963,-10.14433 91.74463,-11.93172 21.96014,-1.04239 61.39483,-10.57283 64.87702,-13.89209 0.45583,-0.4345 -0.37428,-1.44573 -1.13356,-3.04684 -0.71715,-1.51229 -1.31867,-3.54895 -2.45223,-6.18464 -4.02719,-9.36381 -11.55245,-26.49123 -26.13961,-55.79333 -10.26205,-20.614 -20.52938,-40.18092 -20.52938,-40.74191 0,-0.561 109.44794,-0.46341 239.28946,-0.4722 l 238.68208,-0.0162 -19.23191,37.92857 c -7.98876,15.75519 -16.84723,33.36429 -22.74728,47.1076 -5.11552,11.91583 -9.17244,20.45764 -9.17244,21.73967 0,0.37257 6.06429,2.61196 14.80045,5.98098 21.0242,8.10779 42.09211,11.36166 94.68919,14.99998 7.75551,0.53648 15.13353,0.58128 21.41919,1.05917 29.19801,2.21991 43.29941,4.45186 54.02756,7.55838 3.84274,1.11274 7.99529,2.2287 8.96927,2.2287 0.49446,0 -0.0137,-0.88636 -0.24327,-2.01215 -0.33046,-1.62088 -0.66287,-4.05709 -1.47808,-7.7471 -1.65929,-7.51067 -3.63948,-19.1425 -7.7827,-35.53558 -7.90415,-31.27352 -13.06793,-45.47103 -19.62771,-55.59337 -2.58463,-3.98832 -15.12169,-17.01052 -14.22735,-17.01052 1.29312,0 4.6922,0.22172 10.56097,1.35242 4.39446,0.84665 10.49263,1.71888 16.38523,6.03414 4.17718,3.05902 9.78726,8.62065 13.45068,14.36454 3.02831,4.7481 4.90948,9.21841 6.05935,11.29667 4.81156,8.69637 8.2604,17.33698 11.80941,26.7987 3.10277,8.27205 8.87912,16.83457 10.52454,30.03522 0.70348,5.64376 4.53035,17.85454 4.99414,24.33129 0.75806,10.58628 3.68412,21.48621 2.32532,33.46193 -1.23556,10.88961 -3.57121,23.26448 -7.15946,38.31613 -5.05294,21.1956 -3.81899,23.09078 -10.08691,24.04914 -7.71966,1.18034 -157.76255,4.44944 -255.00795,7.07484 -104.7425,2.8278 -165.2198,3.29116 -239,1.83116 z m 349.15519,-27.29742 c 2.93727,-0.46968 4.89508,-0.20648 7.71447,-0.46913 67.71627,-6.30851 94.47658,-9.47411 104.50527,-16.9154 5.81258,-4.31293 18.14295,-26.03782 18.14295,-32.17293 0,-2.34077 -1.36725,-2.6709 -4.9219,-4.05522 -5.38948,-2.09888 -15.94783,-4.94969 -35.504,-7.68968 -46.58674,-6.52722 -133.58256,-12.19005 -162.72833,-14.01158 l -9.02804,-0.56423 -20.16277,40.05385 -19.68097,39.38995 -130.90866,0.34843 -131.06455,0.80846 -20.22066,-40.22362 -19.81946,-40.37707 -9.84077,1.17715 c -1.51926,-2.64069 -145.07493,10.90858 -180.01561,18.28975 -21.22527,4.48381 -20.51101,2.95642 -21.13776,8.29802 -0.81348,6.93308 8.60665,25.50448 15.24682,30.72764 3.78538,2.97759 11.78984,5.57819 21.72697,7.9163 23.55969,5.54337 66.89281,8.33463 150.11066,12.45301 30.75983,1.52228 359.8758,-0.85471 429.11569,-1.81686 5.40419,-0.0751 7.87332,-0.28349 9.84902,-0.36884 l 2.52586,-0.10912 3.00376,-0.19446 z" style="opacity:1;fill:#0d0d0a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6339" d="m 385.39661,318.03846 c -0.76321,-0.006 -47.73002,-1.77227 -68.30035,-2.63645 -47.52029,-1.99639 -83.47452,-4.47552 -109.3216,-7.35671 -10.99949,-1.22612 -12.48382,-1.28243 -31.2223,-5.69426 -8.94814,-2.10678 -14.00674,-4.54081 -17.97755,-8.4534 -3.07008,-3.02508 -6.24015,-7.88424 -9.07503,-13.77755 -2.60541,-5.41629 -3.843,-8.64821 -3.57014,-14.14522 l -0.0104,-3.69784 1.76577,-0.32872 c 0.74671,-0.13901 11.18731,-2.58802 23.7341,-4.95894 l 22.88327,-4.32416 38.875,-4.33363 c 21.38125,-2.3835 39.83125,-4.39928 41,-4.47953 21.92819,-1.50556 81.72866,-5.81661 81.87889,-5.6599 0.26152,0.2728 39.53504,79.30961 39.53504,79.56255 0,0.20707 -6.06608,0.3488 -7.21501,0.30534 z" style="opacity:1;fill:#3b3b3b;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6341" d="m 145.8992,238.98605 c 0,-0.64677 3.14823,-15.81079 4.74421,-23.28297 6.7661,-31.67803 13.89927,-56.06607 20.70463,-70.08918 3.02084,-6.22476 6.22717,-11.4123 10.45234,-16.45362 8.02748,-9.57808 21.3614,-17.57626 35.99217,-22.52619 8.42795,-2.85136 10.18951,-3.24979 23.65007,-3.53454 5.98125,-0.12653 13.62941,-0.37342 16.86066,-0.50984 7.81005,-0.32972 29.22068,-0.74869 29.32258,-0.62079 0.0443,0.0556 8.32713,16.08042 18.05652,35.59361 l 17.89637,35.24109 7.47085,17.25691 c 4.08431,9.43435 7.25653,17.60188 7.21128,17.64714 -0.13475,0.13475 -6.81092,2.43774 -12.10622,4.02081 -9.76614,2.91967 -18.87712,5.22303 -24.25283,6.13733 -5.47447,0.93109 -21.02753,3.49834 -44.03713,5.33177 -26.83629,2.13835 -34.23532,3.20617 -46.51732,4.88491 -12.27931,1.67836 -23.94321,3.68548 -33.96676,5.85411 -3.1625,0.68423 -6.91801,1.55441 -8.15551,1.81227 -3.11385,0.64883 -11.12826,1.7309 -16.51806,2.34908 -2.475,0.28386 -5.06207,0.7761 -5.4402,0.84021 -0.45935,0.0779 -1.36765,0.26671 -1.36765,0.0479 z" style="opacity:1;fill:#3b3b3b;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6349" d="M 97.182033,306.84924 C 72.556268,280.92468 54.707301,245.97749 43.411151,201.56894 36.486077,174.3444 33.812002,157.52803 33.796098,120.40365 33.780088,83.031093 37.418728,76.526937 43.514598,57.840656 45.804885,50.820011 48.498762,44.194039 51.955984,38.390178 62.467876,20.743191 78.236697,9.0493953 96.663495,3.6925802 c 22.319815,-6.488546 54.014365,-3.36788696 74.674575,6.7402138 17.80332,8.710352 49.74494,46.322219 64.89055,72.591173 l 3.73815,6.483551 5.51717,12.761852 -10.29812,0.2151 c -10.32559,0.21567 -29.13818,7.09123 -39.31129,13.93818 -0.66089,0.44481 -3.42209,3.31258 -6.38509,4.59126 -0.0665,0.0287 -0.62137,0.45894 -0.80037,0.5422 -0.35092,0.16322 -0.86592,0.0836 -1.00005,0.13592 -3.55394,1.38486 -7.46457,1.39449 -10.52846,1.95331 -5.86689,1.07006 -13.65754,3.82832 -19.00675,8.52499 -17.29997,15.18958 -38.57715,67.97467 -44.1677,110.18627 -2.81527,21.25668 -2.74867,27.30955 2.30097,52.02795 3.1532,15.43518 10.0155,36.17367 9.52454,36.66463 -0.25427,0.25427 -5.54004,-2.23867 -11.47619,-7.27082 -5.52574,-4.68423 -11.93986,-11.44061 -17.153397,-16.92912 z" style="opacity:1;fill:#20221f;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path625" style="fill:#4d4d4d;stroke-width:1"/><path id="path649" style="fill:#4d4d4d;stroke-width:.999999"/><path id="path657" style="fill:#4d4d4d;stroke-width:.999999"/><path id="path679" style="fill:#333;stroke-width:1"/><path id="path689" d="m 147.55184,432.51372 c 0.019,-0.0179 0.72327,-0.45129 1.56514,-0.96306 4.94389,-3.00542 9.65971,-6.02725 15.34793,-9.83473 2.41182,-1.61438 5.23198,-3.50157 6.26703,-4.19375 15.44392,-10.32806 39.24969,-18.63753 71.42414,-24.93077 3.7449,-0.7325 8.05839,-1.52001 11.58335,-2.11478 1.5017,-0.25338 2.81413,-0.47936 2.91652,-0.50217 0.17311,-0.0386 0.18616,-0.0134 0.18616,0.35856 v 0.40005 l -1.59271,0.27192 c -29.70268,5.071 -51.83288,11.11479 -68.11422,18.60211 -6.36259,2.92597 -15.97557,8.49335 -28.13099,16.29213 -3.27997,2.10439 -7.44,4.74186 -9.55334,6.05684 -0.89911,0.55946 -0.97425,0.59023 -1.44103,0.59023 -0.27085,0 -0.47694,-0.0147 -0.45798,-0.0326 z" style="fill:#333;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path695" d="m 568.88289,377.33785 c 0.1357,-0.0261 0.33207,-0.0251 0.43637,0.002 0.1043,0.0273 -0.007,0.0487 -0.24673,0.0474 -0.24,-10e-4 -0.32534,-0.0236 -0.18964,-0.0496 z" style="fill:#333;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path697" style="fill:#333;stroke-width:1"/><path id="path733" style="fill:#333;stroke-width:.999999"/><path style="stroke:none;stroke-width:0" id="path320" d="m 687.40741,351.36236 v 0 c -0.19999,0 -0.29999,-0.11251 -0.29999,-0.33752 0,-0.11251 0.1,-0.22502 0.19999,-0.22502 0.1,0 0.29999,0.11251 0.29999,0.22502 0.1,0.22501 0,0.33752 -0.19999,0.33752 0,0 0,0 0,0 z" class="st2"/><path style="fill:#000;stroke:none;stroke-width:0" id="path338" d="m 789.9,534.19982 c -20.7,0 -37.6,-16.80001 -37.5,-37.60001 0,-20.7 16.8,-37.6 37.6,-37.5 20.7,0 37.5,16.8 37.5,37.6 -0.1,20.7 -16.9,37.40001 -37.6,37.50001 z m 0,-73.10001 c -19.6,0 -35.6,15.9 -35.6,35.6 0,19.6 15.9,35.60001 35.6,35.60001 19.6,0 35.6,-15.90001 35.6,-35.60001 -0.1,-19.7 -16,-35.6 -35.6,-35.6 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path340" d="m 789.9,670.19982 c -20.7,0 -37.6,-16.8 -37.5,-37.6 0,-20.7 16.8,-37.6 37.6,-37.5 20.7,0 37.5,16.8 37.5,37.6 -0.1,20.7 -16.9,37.5 -37.6,37.5 z m 0,-73.1 c -19.6,0 -35.6,15.9 -35.6,35.6 0,19.6 15.9,35.6 35.6,35.6 19.6,0 35.6,-15.9 35.6,-35.6 -0.1,-19.7 -16,-35.6 -35.6,-35.6 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path346" d="m 868.1,602.19982 c -20.7,0 -37.6,-16.8 -37.6,-37.5 0,-20.7 16.8,-37.60001 37.5,-37.60001 20.7,0 37.6,16.80001 37.6,37.50001 0,0 0,0 0,0 0,20.8 -16.8,37.6 -37.5,37.6 z m 0,-73.10001 c -19.6,0 -35.6,15.90001 -35.6,35.60001 0,19.7 15.9,35.6 35.6,35.6 19.6,0 35.6,-15.9 35.6,-35.6 0,0 0,0 0,0 -0.1,-19.7 -16,-35.60001 -35.6,-35.60001 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path348" d="m 711.7,602.19982 c -20.7,0 -37.6,-16.8 -37.6,-37.6 0,-20.8 16.8,-37.60001 37.6,-37.60001 20.7,0 37.6,16.80001 37.6,37.60001 -0.1,20.8 -16.9,37.6 -37.6,37.6 z m 0,-73.10001 c -19.6,0 -35.6,15.90001 -35.6,35.60001 0,19.7 15.9,35.6 35.6,35.6 19.6,0 35.6,-15.9 35.6,-35.6 v 0 c -0.1,-19.7 -16,-35.60001 -35.6,-35.60001 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path350" d="m 649.1,512.39981 c -12.8,0 -23.1,-10.3 -23.1,-23.1 0,-12.8 10.3,-23.1 23.1,-23.1 12.8,0 23.1,10.3 23.1,23.1 0,12.7 -10.4,23.1 -23.1,23.1 z m 0,-44.2 c -11.7,0 -21.1,9.5 -21.1,21.1 0,11.7 9.5,21.1 21.1,21.1 11.7,0 21.1,-9.5 21.1,-21.1 0,-11.7 -9.5,-21.1 -21.1,-21.1 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path352" d="m 596.3,587.69982 c -12.8,0 -23.1,-10.3 -23.1,-23.1 0,-12.8 10.3,-23.1 23.1,-23.1 12.8,0 23.1,10.3 23.1,23.1 -0.1,12.8 -10.4,23.1 -23.1,23.1 z m 0,-44.2 c -11.7,0 -21.1,9.5 -21.1,21.1 0,11.7 9.5,21.1 21.1,21.1 11.7,0 21.1,-9.5 21.1,-21.1 -0.1,-11.6 -9.5,-21.1 -21.1,-21.1 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path354" d="m 400.9,512.39981 c -12.8,0 -23.1,-10.3 -23.1,-23.1 0,-12.8 10.3,-23.1 23.1,-23.1 12.8,0 23.1,10.3 23.1,23.1 0,12.7 -10.3,23.1 -23.1,23.1 z m 0,-44.2 c -11.7,0 -21.1,9.5 -21.1,21.1 0,11.7 9.5,21.1 21.1,21.1 11.7,0 21.1,-9.5 21.1,-21.1 0,-11.7 -9.4,-21.1 -21.1,-21.1 z" class="st1"/><path style="fill:#fff;stroke:none;stroke-width:0;fill-opacity:1" id="path356" d="m 453.7,579.59982 c -8.3,0 -15,-6.7 -15,-15 0,-8.3 6.7,-15 15,-15 8.3,0 15,6.7 15,15 v 0 c 0,8.3 -6.7,15 -15,15 z m 0,-27.9 c -7.2,0 -13,5.8 -13,13 0,7.2 5.8,13 13,13 7.2,0 13,-5.8 13,-13 0,0 0,0 0,0 0,-7.2 -5.8,-13 -13,-13 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path364" d="m 392.3933,777.93146 -40.3945,2.09195 c -2.79625,0.14481 -3.89022,-6.05152 -4.05662,-8.84657 l -2.46936,-41.47702 c -0.10103,-1.697 -2.56109,-1.21326 -4.26035,-1.26313 l -41.54022,-1.21911 c -2.7988,-0.0821 -1.30219,-2.94022 -1.32149,-5.74015 l -0.25306,-36.71074 c -0.0193,-2.79994 -1.26916,-6.2565 1.52955,-6.34148 l 44.78698,-1.35981 c 1.69922,-0.0516 1.92388,-0.78373 1.99894,-2.48207 l 1.73813,-39.32715 c 0.063,-1.42619 -0.84622,-5.34524 0.0923,-6.27478 0.90222,-0.89363 5.39003,-0.25407 6.76217,-0.22648 l 35.69884,0.7178 c 1.47105,0.0296 3.27983,-0.55868 4.17557,0.39618 0.80887,0.86226 0.72779,3.19961 0.75922,4.52789 l 1.00717,42.56553 c 0.0402,1.69952 1.86894,1.64457 3.56797,1.70203 L 439.7,679.99982 c 2.7984,0.0947 5.4441,-1.49169 5.52652,1.3071 l 1.25563,42.63654 c 0.0824,2.79879 -3.98428,2.54706 -6.78215,2.65636 l -39.2067,1.53164 c -1.6987,0.0664 -3.1,1.4 -3.1,3.1 v 41.6 c 0,2.8 -2.20374,4.95519 -5,5.1 z M 303.8,682.09982 c -1.7,0 -3.1,1.4 -3.1,3.1 v 36.4 c 0,1.7 1.4,3.1 3.1,3.1 h 41.6 c 2.8,0 5.8517,2.3629 5.80424,5.1625 L 350.5,771.39982 c -0.0288,1.69976 1.4,3.1 3.1,3.1 H 390 c 1.7,0 3.14335,-1.40055 3.1,-3.1 l -1.07924,-42.31081 c -0.0714,-2.79909 3.37924,-4.38919 6.17924,-4.38919 h 41.6 c 1.7,0 3.1,-1.4 3.1,-3.1 v -36.4 c 0,-1.7 -1.4,-3.1 -3.1,-3.1 H 398 c -2.8,0 -5.53564,-2.33548 -5.53564,-5.13548 v -41.6 c 0,-1.7 -0.96451,-2.5533 -2.66436,-2.57611 l -36.4,-0.48841 c -1.69985,-0.0228 -3.1,1.4 -3.1,3.1 v 41.6 c 0,2.8 -2.3,5.1 -5.1,5.1 z" class="st1"/><rect style="fill:#fff;stroke:none;stroke-width:0;fill-opacity:1" id="rect366" width="25.008" height="4.168" x="388.464" y="487.053" class="st1"/><path style="fill:#fff;stroke:none;stroke-width:0;fill-opacity:1" id="path378" d="m 596.26878,548.69925 -14.80006,14.63243 4.67775,-0.0638 v 11.31317 h 21.05218 l -0.38054,-10.10362 4.06609,-1.90776 z M 600.2,570.39982 h -7.7 l 0.13976,-7.29486 7.56024,-0.10514 z" class="st1"/><path id="path1264" d="m 145.5384,261.9246 48.91355,-9.94386 76.90845,-8.57854 84.93459,-5.43741 37.04503,74.3832 2.99509,6.21749 c 0,0 -129.73659,-2.92442 -194.04465,-10.73313 -5.38953,-0.65443 -10.7458,-1.67167 -16.00517,-3.01863 -8.41917,-2.15621 -17.65668,-3.11317 -24.78964,-8.0783 -4.83746,-3.36728 -8.22948,-8.68837 -10.97526,-13.86362 -2.08724,-3.93401 -3.54076,-7.41109 -4.40777,-11.38815 -0.67992,-3.11884 -0.57422,-9.55905 -0.57422,-9.55905 z" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1284" d="M 287.88085,101.48987 H 766.56621" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1286" d="m 396.33511,318.56548 h 275.406" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1288" d="m 808.46074,102.92871 c 0,0 5.9121,-14.05762 9.50328,-20.141861 6.24979,-10.588551 13.39852,-20.461562 21.10717,-29.564671 13.01682,-15.37152 25.71944,-32.454575 42.1772,-41.972944 12.83748,-7.4245942 27.60697,-10.0373272 41.85587,-10.49481123 15.06084,-0.483564 30.86116,1.02458303 44.673,8.23805823 11.121,5.808126 20.61215,16.057921 28.54295,27.059753 7.66049,10.62687 13.87769,23.161545 17.57429,36.488105 4.4199,15.933991 4.7948,33.231601 4.9643,50.021861 0.1768,17.51733 -0.8974,35.23647 -4.1241,52.32289 -4.4248,23.43001 -11.7156,46.18491 -20.67481,67.67419 -8.7404,20.96449 -18.53729,41.90855 -31.94166,58.97171 -9.63232,12.26152 -34.12961,30.15512 -34.12961,30.15512" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1292" d="m 865.54544,122.0103 -34.16042,-17.50553 -28.19287,-1.91216 -36.62594,-1.10274 -35.81056,70.65473 -15.19618,35.85898 c 0,0 24.14907,10.05694 36.85358,12.86852 42.63162,9.43462 87.22428,6.13602 130.33953,13.0311 8.87438,1.41921 26.32615,5.81829 26.32615,5.81829 0,0 -11.41175,-65.80964 -26.13433,-95.26227 -4.23308,-8.46829 -17.39896,-22.44892 -17.39896,-22.44892 z" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1294" d="m 908.90866,261.9246 c -14.90518,-7.33225 -31.40718,-7.58007 -47.34252,-10.12985 -25.97853,-4.15676 -78.47947,-8.39255 -78.47947,-8.39255 l -84.93461,-5.43741 -37.04502,74.3832 -2.9951,6.21749 c 0,0 129.7366,-2.92442 194.04467,-10.73313 5.38953,-0.65443 10.7458,-1.67167 16.00517,-3.01863 8.41917,-2.15621 17.89878,-2.78229 24.78964,-8.0783 4.64513,-3.57005 7.4273,-8.87793 10.04415,-13.98801 1.82473,-3.56325 4.22105,-7.41969 5.33887,-11.26376 0.89131,-3.06513 3.43851,-8.15003 0.57422,-9.55905 z" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1320" d="M 89.901385,1043.0497 C 51.120844,1028.2274 26.779773,979.55292 26.705262,924.73816 c -0.02683,-19.73531 7.500083,-83.46075 19.625415,-156.87527 10.010309,-60.60883 32.750067,-178.8554 35.554651,-186.82711 0.817815,-2.32455 5.251509,8.08641 10.198999,15.96393 54.661393,87.0333 126.458083,185.19303 157.975213,221.58253 4.35584,5.02923 9.62016,11.21075 10.65491,12.24515 L 282.5,850.79982 270.63232,861.07967 c -2.03922,1.76637 -4.399,4.33811 -6.86286,8.08522 -7.45471,11.33731 -19.73625,34.26035 -48.72183,89.81381 -28.99495,55.5714 -38.84733,69.8604 -58.75364,80.6948 -10.52791,5.73 -19.35414,9.0786 -35.10656,9.6624 -12.5899,0.4666 -25.260689,-3.9833 -31.286045,-6.2862 z" style="opacity:1;fill:#20221f;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path1326" style="opacity:1;fill:#3b3b3b;fill-opacity:1;stroke:#121212;stroke-width:1.68413;stroke-opacity:1"/><path id="path1330" d="m 138.81233,436.14206 c 0.21748,-0.69262 0.7531,-2.92046 1.94059,-5.29196 7.37903,-14.73636 19.26824,-27.90919 34.8344,-38.16487 3.81894,-2.51609 5.85687,-3.54224 9.70119,-5.68509 29.69601,-16.5528 71.96426,-26.20708 122.31518,-27.87295 2.49708,-0.0826 8.56877,-0.16798 11.88456,-0.17343 6.37619,-0.0105 7.71418,0.23428 10.5988,0.96961 7.33708,1.87045 19.9332,7.12156 38.42333,15.71366 4.27028,1.98434 5.04118,2.48573 4.47404,2.58916 -0.31975,0.0583 -4.2958,0.40431 -7.16131,0.53416 -13.19154,0.59778 -31.76437,1.70089 -46.38574,2.92088 -68.93447,5.75182 -116.54811,16.15028 -143.66353,31.71267 -2.96636,1.70249 -4.78573,2.58619 -9.50067,5.80187 -8.30679,5.66538 -18.18384,11.91045 -24.93362,15.76503 -0.90059,0.5143 -1.86291,1.07097 -2.13849,1.23705 L 138.7,436.49981 Z" style="opacity:1;fill:#3b3b3b;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path1332" d="m 909.6042,435.71402 c -9.87144,-5.81392 -17.23177,-10.10098 -26.70487,-16.43841 -7.02111,-4.69706 -11.25203,-6.98493 -16.67517,-9.65548 -32.24722,-15.87973 -85.19827,-25.4901 -165.57978,-30.28955 -3.87964,-0.23165 -21.45583,-1.14338 -24.33622,-1.27015 -1.08245,-0.0476 25.57748,-11.99148 34.14889,-15.27939 4.23673,-1.62517 8.35215,-3.08194 10.99929,-3.77654 1.87062,-0.49084 3.51887,-0.31274 13.59939,-0.29756 35.94962,0.0542 72.31633,5.85992 100.35572,15.37117 35.26438,11.96204 59.72087,30.05022 72.48708,53.83754 1.30939,2.43979 3.84287,8.4153 3.53114,8.63887 -0.0331,0.0238 -1.13077,-0.43135 -1.82547,-0.8405 z" style="opacity:1;fill:#3b3b3b;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><path id="path1338" d="m 395.45596,509.87628 c -6.48502,-1.8592 -10.93652,-5.6855 -13.79382,-11.50404 -1.42805,-2.90806 -2.00722,-4.68505 -2.00722,-9.02233 0,-4.22426 0.72494,-6.37076 2.03455,-9.16375 2.1242,-4.53029 5.32048,-7.67886 9.786,-10.10035 3.45943,-1.87593 4.33529,-2.11449 9.31057,-2.11449 4.866,0 6.1706,0.81029 9.7833,2.58721 10.37932,5.1051 14.4768,17.77016 9.3676,28.14973 -2.01976,4.10321 -5.25707,7.6088 -9.50131,9.65682 -4.68671,2.26152 -10.49957,2.7956 -14.97967,1.5112 z m 18.0159,-20.73944 v -2.084 h -12.50402 -12.50403 v 2.084 2.08401 h 12.50403 12.50402 z" style="opacity:1;fill:#454644;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><path id="path1340" d="m 643.89381,510.1283 c -16.00277,-4.30084 -21.37493,-23.94309 -9.79788,-35.82407 1.36762,-1.40352 4.05762,-3.34652 5.97776,-4.31776 3.069,-1.55235 4.16297,-1.76629 9.04668,-1.76926 4.7407,-0.003 6.07338,0.23936 9.08653,1.65168 8.75447,4.10336 14.04024,14.18549 12.17527,23.22327 -1.81288,8.78543 -7.95134,15.16261 -16.54121,17.18454 -4.31764,1.01628 -5.68968,0.99582 -9.94715,-0.1484 z m 7.38702,-13.20979 v -5.55574 h 5.55551 5.5555 v -2.16057 -2.16056 h -5.5555 -5.55551 v -5.24709 -5.24711 h -2.16046 -2.16048 v 5.24711 5.24709 h -5.24686 -5.24687 v 2.16056 2.16057 h 5.24687 5.24686 v 5.55574 5.55575 h 2.16048 2.16046 z" style="opacity:1;fill:#454644;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><path id="path1342" d="m 589.20372,584.95719 c -4.25344,-1.78823 -7.33824,-4.25637 -9.64428,-7.16798 -3.26193,-4.11853 -4.4165,-7.83555 -4.41985,-13.14327 -0.004,-5.96137 2.0676,-11.00718 6.21309,-15.07644 8.27191,-8.1198 21.67702,-8.45496 29.94893,-0.33516 4.15996,4.08346 6.18998,9.80428 6.19669,15.74469 0.006,5.55279 -1.87228,10.36674 -5.60025,14.49316 -5.32618,5.89543 -15.60753,8.46445 -22.69433,5.485 z m 17.18736,-16.03276 v -5.65658 h 1.78629 c 0.98246,0 1.78629,-0.20533 1.78629,-0.45628 0,-0.25096 -3.08135,-3.5289 -6.84744,-7.2843 l -6.84744,-6.82802 -6.84744,6.82802 c -3.7661,3.7554 -6.84745,7.03334 -6.84745,7.2843 0,0.25095 0.80383,0.45628 1.78629,0.45628 h 1.78629 v 5.65658 5.65659 h 10.12231 10.1223 z" style="opacity:1;fill:#454644;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><path id="path1344" d="m 592.49048,566.79368 0.0388,-3.67426 4.15382,-0.071 3.5169,-0.0486 0.0319,3.68856 -0.0319,3.71144 -3.8388,-8.5e-4 -3.8612,8.5e-4 z" style="opacity:1;fill:#454644;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><path id="path1346" d="m 451.39084,577.72034 c -2.71039,-0.5432 -5.45219,-2.01231 -7.54207,-4.33676 -5.69656,-6.33595 -3.94544,-16.32095 3.86975,-20.47481 1.44094,-0.76587 3.47455,-1.38574 6.37542,-1.38505 3.06777,7.3e-4 4.22644,0.63601 6.07554,1.60638 2.75862,1.44768 5.06775,4.59791 5.96889,7.31097 0.92317,2.7794 0.69844,7.94508 -0.51419,10.29957 -2.56241,4.97531 -8.96363,8.03584 -14.23334,6.9797 z" style="opacity:1;fill:#454644;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><path id="path1352" style="opacity:1;fill:#444543;fill-opacity:1;stroke:#121212;stroke-width:1.19086;stroke-opacity:1"/><path id="path2106" d="m 959.69862,1043.0497 c 38.78058,-14.8223 63.12158,-63.49678 63.19608,-118.31154 0.027,-19.73531 -7.5,-83.46075 -19.6254,-156.87527 -10.0103,-60.60883 -32.75,-178.8554 -35.55463,-186.82711 -0.81781,-2.32455 -5.25151,8.08641 -10.199,15.96393 -54.66139,87.0333 -126.45808,185.19303 -157.97521,221.58253 -4.35584,5.02923 -9.62016,11.21075 -10.65491,12.24515 L 767.1,850.79982 l 11.86768,10.27985 c 2.03922,1.76637 4.399,4.33811 6.86286,8.08522 7.45471,11.33731 19.73625,34.26035 48.72183,89.81381 28.99495,55.5714 38.84733,69.8604 58.75364,80.6948 10.52791,5.73 19.35414,9.0786 35.10656,9.6624 12.5899,0.4666 25.26069,-3.9833 31.28605,-6.2862 z" style="opacity:1;fill:#20221f;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><style id="style280-6" type="text/css">.st1{fill:#1abc9c}.st2{fill:#ccc}.st3{fill:#fff}</style><style id="style1175-5" type="text/css">.st1{fill:#1abc9c}.st2{fill:#ccc}.st3{fill:#fff}</style><g id="g3161" transform="translate(-2057.3322,-706.68848)"><path id="path625-9" style="fill:#4d4d4d;stroke-width:1"/><path id="path649-2" style="fill:#4d4d4d;stroke-width:.999999"/><path id="path657-2" style="fill:#4d4d4d;stroke-width:.999999"/><path id="path679-8" style="fill:#333;stroke-width:1"/><path id="path697-6" style="fill:#333;stroke-width:1"/><path id="path733-2" style="fill:#333;stroke-width:.999999"/><path style="fill:#fff" id="path370" d="m 2857.7582,1215.7524 h -4.5 l -6.3,-10.4 -6.3,10.4 h -4.4 l 8.6,-13.2 -7.9,-12.6 h 4.2 l 5.9,9.7 5.9,-9.7 h 4 l -7.9,12.4 z" class="st1"/><path style="fill:#fff" id="path372" d="m 2779.9248,1257.09 -9.3,16.5 v 9.2 h -3.5 v -9.3 l -9.3,-16.5 h 4.2 l 5.1,9.4 1.9,3.8 1.7,-3.4 5.2,-9.8 z" class="st1"/><path style="fill:#fff" id="path374" d="m 2936.0178,1282.6378 h -3.8 l -1.8,-5.6 h -10.7 l -1.8,5.6 h -3.6 l 8.5,-25.7 h 4.8 z m -6.6,-8.7 -4.4,-13.8 -4.4,13.8 z" class="st1"/><path style="fill:#fff" id="path376" d="m 2856.4301,1344.0269 c 0,1.1 -0.2,2.3 -0.7,3.3 -0.5,1 -1.2,1.8 -2,2.4 -1,0.7 -2.1,1.2 -3.2,1.5 -1.4,0.4 -2.8,0.5 -4.2,0.5 h -6.7 v -25.7 h 7.4 c 5.7,0 8.6,2.1 8.6,6.3 0,1.3 -0.3,2.5 -1,3.6 -0.8,1.1 -1.9,1.9 -3.2,2.2 0.7,0.1 1.4,0.4 2,0.7 0.6,0.3 1.2,0.7 1.6,1.2 0.5,0.5 0.9,1.1 1.1,1.8 0.2,0.6 0.4,1.4 0.3,2.2 z m -4.5,-11.3 c 0,-0.5 -0.1,-1 -0.2,-1.5 -0.2,-0.5 -0.4,-0.9 -0.8,-1.2 -0.5,-0.4 -1,-0.6 -1.6,-0.8 -0.8,-0.2 -1.7,-0.3 -2.5,-0.3 h -3.7 v 8.1 h 3.5 c 0.7,0 1.5,-0.1 2.2,-0.3 0.6,-0.1 1.2,-0.4 1.7,-0.8 0.5,-0.3 0.8,-0.8 1.1,-1.3 0.2,-0.6 0.3,-1.2 0.3,-1.9 z m 0.8,11.4 c 0,-0.6 -0.1,-1.2 -0.4,-1.8 -0.3,-0.5 -0.7,-1 -1.2,-1.3 -0.6,-0.4 -1.2,-0.7 -1.9,-0.8 -0.8,-0.2 -1.7,-0.3 -2.5,-0.3 h -3.6 v 8.9 h 3.7 c 1.6,0.1 3.2,-0.3 4.5,-1.1 1,-0.9 1.5,-2.2 1.4,-3.6 z" class="st1"/></g><style id="style1175-2" type="text/css">.st1{fill:#1abc9c}.st2{fill:#ccc}.st3{fill:#fff}</style><g style="fill:#000" id="g6246" transform="translate(-181.90137,798.00717)"><path style="fill:#000" id="path6242" d="m 816.4233,-34.702478 c -31.6,0 -57.2,-25.6 -57.2,-57.19999 0,-31.600002 25.6,-57.200002 57.2,-57.200002 31.6,0 57.2,25.6 57.2,57.200002 0,31.49999 -25.6,57.09999 -57.2,57.19999 z m 0,-112.499992 c -30.5,0 -55.2,24.7 -55.2,55.200002 0,30.49999 24.7,55.19999 55.2,55.19999 30.5,0 55.2,-24.7 55.2,-55.19999 0,0 0,0 0,0 0,-30.400002 -24.7,-55.100002 -55.2,-55.200002 z" class="st1"/><path style="fill:#000;stroke-width:1.11974" id="path6244" d="m 816.50147,-45.055391 c -25.5864,0 -46.36838,-20.872572 -46.36838,-46.570513 0,-25.697956 20.78198,-46.570526 46.36838,-46.570526 25.58642,0 46.3684,20.87257 46.3684,46.570526 0,25.697941 -20.78198,46.458295 -46.3684,46.570513 z m 0,-91.008899 c -24.35737,0 -44.13377,19.86262 -44.13377,44.326168 0,24.463541 19.7764,44.32615 44.13377,44.32615 24.35738,0 44.13377,-19.862609 44.13377,-44.32615 v 0 c 0,-24.463548 -19.77639,-44.326168 -44.13377,-44.326168 z" class="st1"/></g><g style="fill:#000" id="g6252" transform="translate(-181.90137,798.00717)"><path style="fill:#000" id="path6248" d="m 816.4233,-34.702478 c -31.6,0 -57.2,-25.6 -57.2,-57.19999 0,-31.600002 25.6,-57.200002 57.2,-57.200002 31.6,0 57.2,25.6 57.2,57.200002 0,31.49999 -25.6,57.09999 -57.2,57.19999 z m 0,-112.499992 c -30.5,0 -55.2,24.7 -55.2,55.200002 0,30.49999 24.7,55.19999 55.2,55.19999 30.5,0 55.2,-24.7 55.2,-55.19999 0,0 0,0 0,0 0,-30.400002 -24.7,-55.100002 -55.2,-55.200002 z" class="st1"/><path style="fill:#000;stroke-width:1.11974" id="path6250" d="m 816.50147,-45.055391 c -25.5864,0 -46.36838,-20.872572 -46.36838,-46.570513 0,-25.697956 20.78198,-46.570526 46.36838,-46.570526 25.58642,0 46.3684,20.87257 46.3684,46.570526 0,25.697941 -20.78198,46.458295 -46.3684,46.570513 z m 0,-91.008899 c -24.35737,0 -44.13377,19.86262 -44.13377,44.326168 0,24.463541 19.7764,44.32615 44.13377,44.32615 24.35738,0 44.13377,-19.862609 44.13377,-44.32615 v 0 c 0,-24.463548 -19.77639,-44.326168 -44.13377,-44.326168 z" class="st1"/></g><path style="fill:#000;stroke:none;stroke-width:0" id="path330" d="m 118.7,1050.4998 c -53.4,0 -93.7,-53.6 -93.7,-124.69998 0,-48.7 29.9,-227 55.6,-347.8 0.1,-0.5 0.6,-0.9 1.2,-0.8 0.3,0.1 0.5,0.2 0.7,0.5 53.7,90.5 161.9,245.4 202.4,272.4 0.5,0.3 0.6,0.9 0.3,1.4 l -0.3,0.3 c -17.4,9.7 -27.3,28.8 -38.8,50.9 -3.1,6 -6.3,12.1 -9.8,18.4 -7.3,13.1 -13.7,26.1 -19.8,38.7 -24,48.69998 -44.6,90.69998 -97.8,90.69998 z M 82.1,580.89982 c -25.6,120.7 -55.1,296.6 -55.1,344.9 0,70 39.4,122.79998 91.7,122.79998 52,0 72.3,-41.5 95.9,-89.59998 6.2,-12.6 12.6,-25.7 19.9,-38.8 3.5,-6.3 5.51428,-12.24367 8.61428,-18.24367 11.3,-21.8 21.2,-40.7 38.2,-51 C 239.91428,821.65615 135.7,670.89982 82.1,580.89982 Z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path332" d="m 931.3,1050.4998 c -53.3,0 -73.9,-42 -97.7,-90.69998 -6.2,-12.6 -12.5,-25.6 -19.8,-38.7 -3.5,-6.3 -6.7,-12.5 -9.8,-18.4 -11.5,-22.1 -21.4,-41.2 -38.8,-50.9 -0.5,-0.3 -0.7,-0.9 -0.4,-1.4 0.1,-0.1 0.2,-0.3 0.3,-0.3 40.6,-27 148.7,-181.8 202.4,-272.4 0.3,-0.5 0.9,-0.6 1.4,-0.3 0.2,0.1 0.4,0.4 0.5,0.7 25.7,120.9 55.6,299.1 55.6,347.8 0,70.99998 -40.3,124.59998 -93.7,124.59998 z M 767.5,850.79982 c 17.1,10.2 26.9,29.2 38.2,51 3.1,6 6.3,12.1 9.8,18.4 7.3,13.1 13.7,26.1 19.9,38.7 23.5,48.09998 43.9,89.59998 95.9,89.59998 52.3,0 91.7,-52.79998 91.7,-122.79998 0,-48.2 -29.5,-224.2 -55.1,-344.9 -53.6,90.1 -159,240.7 -200.4,270 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path328" d="m 765.4,850.99982 c -0.1,0 -0.2,0 -0.2,-0.1 -8.6,-4.8 -18.6,-7.2 -30.7,-7.2 l -395.10335,0.27402 -24.50466,0.20066 C 312.32037,844.1956 293,846.09982 284.4,850.89982 c -0.2,0.1 -0.4,0.1 -0.5,0 -40.8,-27.1 -149.1,-182.2 -202.9,-272.9 -0.1,-0.1 -0.1,-0.2 -0.1,-0.4 11.3,-53.30001 20.7,-90.00001 27.1,-106.10001 1.2,-3.1 2.9,-5.9 4.9,-8.5 l 12.7,-16.2 c 2.9,-3.7 6.5,-6.8 10.6,-9 v 0 0 c 7.1,-3.9 18.5,-10.6 32.8,-20.5 32,-22 99.8,-34.9 207.3,-39.4 49.5,-2.1 100.5,-2.3 148.4,-2.3 47.9,0 98.8,0.2 148.4,2.3 107.5,4.6 175.3,17.5 207.3,39.4 14.4,9.9 25.8,16.6 32.9,20.5 4.1,2.2 7.7,5.3 10.6,9 l 12.7,16.2 c 2.1,2.6 3.7,5.5 4.9,8.6 6.4,16.1 15.7,52.8 27.1,106.10001 0,0.1 0,0.3 -0.1,0.4 -53.7,90.6 -162,245.7 -202.8,272.8 -0.1,0.1 -0.2,0.1 -0.3,0.1 z M 82,577.69982 c 53.6,90.4 161.4,244.7 202.2,272.2 8.7,-4.8 18.8,-7.2 30.9,-7.2 h 419.4 c 12.1,0 22.2,2.3 30.9,7.2 40.8,-27.5 148.6,-181.8 202.2,-272.2 -11.3,-53.10001 -20.6,-89.70001 -27,-105.70001 -1.2,-3 -2.8,-5.8 -4.8,-8.3 l -12.7,-16.2 c -2.8,-3.6 -6.3,-6.5 -10.3,-8.7 -7.1,-3.9 -18.5,-10.7 -33,-20.6 -31.8,-21.9 -99.35112,-34.43567 -206.75112,-39.03567 -49.5,-2.1 -100.49968,-1.9233 -148.39968,-1.9233 -38.06383,0 -77.6616,0.0368 -117.31701,1.00603 -8.71122,0.2129 -17.6639,0.19536 -26.33551,0.5418 -1.52925,0.0611 -2.97996,0.25114 -4.50687,0.31579 -107.3,4.6 -175.08981,17.19535 -206.88981,39.09535 -14.4,9.9 -25.8,16.6 -32.9,20.5 0,0 -0.1,0 -0.1,0.1 -4,2.2 -7.5,5.2 -10.3,8.7 l -12.7,16.2 c -2,2.5 -3.6,5.3 -4.8,8.3 -6.2,16 -15.5,52.6 -26.8,105.70001 z" class="st3"/><path style="fill:#000;stroke:#000;stroke-width:0;stroke-opacity:1" id="path360" d="m 136.7,439.69981 c -0.6,0 -1,-0.4 -1,-1 0,-0.1 0,-0.2 0.1,-0.3 27.3,-73.2 138.4,-81.3 186,-81.3 0.3,0 0.6,0 1,0 h 1.2 c 6.6,0 25.5,8.1 40.3,14.9 0,0 0.1,0 0.1,0 7.2,3.3 12.6,6 12.7,6 0.5,0.2 0.7,0.8 0.5,1.3 -0.2,0.3 -0.5,0.5 -0.9,0.6 -107.2,4.6 -174.8,17.4 -206.5,39.2 -14.6,10 -26,16.7 -33,20.6 -0.1,-0.1 -0.3,0 -0.5,0 z m 185.1,-80.7 c -46.6,0 -154.6,7.8 -183.1,77.5 7,-4 17.5,-10.3 30.4,-19.1 31.6,-21.7 98.2,-34.6 203.6,-39.4 -2.2,-1.1 -5.4,-2.5 -8.9,-4.2 0,0 -0.1,0 -0.1,0 -20.6,-9.5 -34.7,-14.7 -39.5,-14.7 H 323 c -0.5,-0.1 -0.9,-0.1 -1.2,-0.1 z" class="st1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path362" d="m 913.3,439.69981 c -0.2,0 -0.3,0 -0.5,-0.1 -7,-3.9 -18.5,-10.7 -33,-20.6 -31.7,-21.8 -99.3,-34.6 -206.5,-39.2 -0.6,0 -1,-0.5 -1,-1 0,-0.4 0.2,-0.7 0.6,-0.9 0.1,0 5.4,-2.6 12.7,-6 21.6,-9.9 35.2,-14.9 40.4,-14.9 h 1.2 c 0.3,0 0.6,0 1,0 47.6,0 158.7,8.1 186.1,81.4 0.2,0.5 -0.1,1.1 -0.6,1.3 -0.2,0 -0.3,0 -0.4,0 z m -235.9,-61.7 c 105.3,4.7 171.9,17.6 203.6,39.4 12.9,8.8 23.3,15.1 30.4,19.1 -28.6,-69.7 -136.6,-77.5 -183.2,-77.5 -0.3,0 -0.6,0 -1,0 H 726 c -4.8,0 -18.8,5.2 -39.5,14.7 -3.7,1.7 -6.9,3.2 -9.1,4.3 z" class="st1"/><path id="path2112" d="m 350.75975,774.19055 c -0.54927,-0.17612 -0.0304,-12.31917 -0.12645,-24.6074 l -0.16419,-21.00762 -1.8128,-2.08455 c -1.57803,-1.8146 -1.66877,-1.70163 -23.52296,-1.70163 -16.47213,0 -23.55123,0.39462 -24.17546,-0.22962 -1.27311,-1.2731 -1.62935,-41.11494 -0.35204,-42.17504 0.60567,-0.50266 10.32741,-0.77493 25.10191,-0.48637 l 21.46163,-0.58137 1.38487,-1.7435 c 1.25128,-1.57532 1.30939,-2.1523 1.30939,-22.89787 0,-19.54641 -0.16902,-23.2189 0.85054,-24.23846 1.03624,-1.03624 4.51783,-0.84054 21.40164,-0.40154 10.58355,0.27519 19.61437,0.44536 20.11141,0.84002 0.67278,0.53419 0.3109,7.0564 0.3109,23.63141 0,11.97674 0.63693,21.62246 0.87851,22.25201 1.12567,2.93343 2.76338,2.82585 25.60261,2.82585 16.02364,0 23.0059,0.34496 23.6292,0.96825 1.20955,1.20956 1.1588,40.71383 -0.0507,41.92338 -0.62488,0.62488 -7.7734,0.39847 -24.56401,0.39847 -22.63183,0 -21.53798,-0.26923 -23.4191,1.22892 l -2.29007,1.82382 0.41436,21.53509 c 0.23712,12.32354 0.51219,23.26414 0.16846,23.95969 -0.56067,1.13434 -2.92867,1.71623 -21.01702,1.71623 -15.01343,0 -17.03811,0.36407 -17.79796,0.12042 z m 25.10886,-20.28089 3.69537,-6.52399 -7.50066,-0.20164 c -4.12537,-0.11091 -7.64608,-0.0562 -7.8238,0.12148 -0.37461,0.37462 6.97415,13.16997 7.54951,13.14489 0.21131,-0.009 2.04713,-2.95256 4.07958,-6.54074 z M 327.97907,703.1396 v -7.83007 l -4.06587,2.30176 c -7.06587,4.0001 -9.51691,5.66817 -9.03576,6.14932 0.74429,0.74428 11.98533,7.17681 12.5713,7.19374 0.29168,0.008 0.53033,-3.50821 0.53033,-7.81475 z m 94.81485,4.01649 c 5.61176,-3.29722 6.30966,-3.91042 5.15427,-4.52876 -0.73498,-0.39336 -3.80043,-2.172 -6.81211,-3.95256 l -5.47577,-3.23737 v 7.76614 c 0,4.27137 0.14468,7.76613 0.32151,7.76613 0.17683,0 3.24228,-1.71611 6.8121,-3.81358 z m -44.44726,-49.3962 c -0.75161,-1.26396 -2.56476,-4.31 -4.02923,-6.76899 l -2.66268,-4.47089 -1.2059,1.99602 c -0.66324,1.09781 -2.40361,4.14385 -3.86748,6.76898 l -2.66157,4.77298 h 7.8967 7.8967 z" style="opacity:.799474;fill:#444542;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path1336" d="m 437.35424,581.29672 -1.08639,-1.60879 0.0495,-14.98492 -0.41398,-14.40897 1.06048,-1.68135 2.00158,-1.31138 14.833,-0.0381 c 14.06192,-0.0362 15.60326,-0.18545 16.19055,0.86734 l 1.1674,1.75439 -0.0867,14.72169 c -0.0812,13.78808 0.21407,16.34726 -0.41314,16.97452 -0.61829,0.61829 -3.1575,0.44351 -16.54008,0.52112 l -14.96612,0.0868 z m 22.92584,-3.4701 c 3.09349,-1.5135 5.88301,-5.06063 7.07806,-8.23205 1.29928,-3.44801 1.15335,-8.33121 -0.43733,-11.42296 -2.22354,-4.3218 -5.13707,-6.72337 -9.82905,-7.9254 -11.07143,-2.83637 -20.55238,7.01721 -17.71247,18.10246 1.26932,4.95463 4.06179,7.84575 8.8494,9.86399 2.04825,0.86345 2.72763,1.4466 5.99421,1.33037 3.13256,-0.11146 4.1083,-0.76291 6.05718,-1.71641 z" style="opacity:1;fill:#454644;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><path style="fill:#000;stroke:none;stroke-width:0" id="path358" d="m 469.86081,585.31506 -32.17259,0.57149 c -2.79956,0.0497 -5.35157,-3.04452 -5.1647,-5.83827 L 434.5,550.49982 c 0.18687,-2.79376 2.59239,-7.14622 5.39057,-7.24738 l 28.15713,-1.018 c 2.79817,-0.10117 6.75127,8.48687 6.73887,11.28684 l -0.11689,26.38609 c -0.0124,2.79997 -2.00932,5.35796 -4.80887,5.40769 z M 439.6,547.49982 c -1.7,0 -3.1,1.4 -3.1,3.1 v 28.2 c 0,1.7 1.4,3.1 3.1,3.1 h 28.2 c 1.7,0 3.1,-1.4 3.1,-3.1 v -28.2 c 0,-1.7 -1.4,-3.1 -3.1,-3.1 z" class="st1"/><path id="path1318" d="M 262.50166,830.10759 C 244.24758,811.11682 214.58124,773.7454 183.66724,731.0786 150.88651,685.83541 116.6707,634.66231 92.005119,594.01656 L 81.8,577.19982 l 5.257509,-23.44168 3.352955,-14.46615 c 4.957804,-21.3902 8.899419,-36.90855 12.268796,-48.5026 3.04238,-10.46885 5.35584,-19.13152 9.14145,-25.14387 2.48317,-3.94383 6.02553,-7.48951 8.85837,-11.09885 5.7754,-7.35846 9.66251,-12.50883 11.82422,-13.47062 0.46368,-0.2063 2.60002,-1.74907 3.88232,-2.25714 4.63496,-1.83646 12.99593,-6.22816 19.94628,-10.67346 10.55698,-6.75203 16.67966,-12.26572 27.36027,-17.21005 38.9392,-18.02594 88.69183,-25.56733 184.49216,-31.56997 48.87565,-3.06244 262.01808,-3.1463 316.31734,-0.0684 95.0191,5.38606 142.66061,13.37877 180.89807,31.17065 8.5408,3.97403 15.95358,8.79384 24.06384,13.77994 13.4002,8.2383 21.70209,12.94269 27.49527,17.288 8.16129,6.12157 10.27897,11.49599 17.73343,19.91564 5.02772,5.6787 7.52785,12.21012 10.61828,23.23356 2.37202,8.46087 5.22197,19.16259 9.58627,36.73997 6.45918,26.01455 12.77442,53.76144 12.77442,56.14817 0,4.50646 -20.43062,34.6723 -47.16826,74.94221 -17.41857,26.23433 -39.68786,58.53506 -58.45143,84.39364 -18.7,27.60148 -47.28252,63.71724 -69.5773,87.75077 -0.16981,0.18305 -0.97937,1.13358 -1.14841,1.31522 -1.21549,1.30612 -1.54999,2.33678 -2.56142,3.48432 -3.85443,4.37318 -7.14619,7.20254 -10.399,9.81016 -1.50516,1.20661 -3.00255,2.87265 -4.78392,4.41824 -2.37042,2.05668 -5.09247,4.12174 -7.53662,6.75954 -0.93701,-0.22972 -6.76483,-3.21051 -7.59407,-3.543 l -4.99221,-1.83981 -31.87894,-2.16022 c -24.12167,2.90388 -54.236,-0.28978 -79.89263,-0.17807 -3.49775,0.0152 -2.27306,-0.16267 -11.11902,-0.0628 -26.1018,0.29468 -44.65554,0.38108 -63.1151,0.42308 -53.63268,0.12176 -88.22706,0.45084 -151.44647,0.62478 -23.69721,0.36382 -50.28982,-0.769 -75.95584,-0.83882 -20.12032,-0.0548 -39.87616,-0.30918 -55.85061,7.02762 l -11.69327,-9.38338 z m 133.53232,-78.4489 1.19086,-23.81719 23.81719,-1.19085 23.81718,-1.19086 v -22.62633 -22.62633 l -24.33021,-0.67784 -24.33021,-0.67784 -0.67783,-24.33021 -0.67784,-24.33021 -23.1375,-0.68067 -23.13749,-0.68065 -0.67969,24.49784 -0.6797,24.49786 -24.41262,0.67874 -24.41261,0.67877 v 23.73385 23.73388 h 23.56236 c 19.28869,0 23.8246,0.68331 25.00805,3.76737 0.79514,2.07205 1.44568,13.3645 1.44568,25.09429 l 0.4034,20.75476 22.81836,-0.10952 23.22176,-0.68167 z m 270.31695,1.26142 c 9.13621,-5.8658 13.71653,-12.21885 17.02487,-18.24496 3.57414,-6.51026 7.79608,-12.21891 7.85093,-26.20665 0.0913,-23.28806 -9.83157,-37.72965 -22.46493,-47.30461 -5.16471,-3.9144 -11.10001,-6.86455 -17.43752,-9.06155 -4.80041,-1.66413 -9.42965,-3.08667 -16.43307,-3.07696 -9.76371,0.0135 -16.41029,2.92117 -22.76776,5.73122 -7.81361,3.45367 -14.66324,7.41246 -19.89879,13.6241 -8.19204,9.71929 -14.53643,20.26212 -14.11646,39.45278 0.26901,12.29219 2.38279,17.74867 6.1523,24.3032 1.99429,3.46771 4.07437,7.60471 8.48939,12.4198 5.01082,5.46489 15.54775,12.34376 16.38852,12.40156 0.84079,0.0579 13.42068,5.8405 20.20713,5.97858 14.26146,0.29017 22.33663,-0.59859 37.00539,-10.01651 z m 139.74492,-86.72966 c 10.49023,-4.38311 18.40382,-16.21017 20.35557,-26.61388 3.92483,-20.92116 -10.28456,-40.79439 -31.65025,-43.99838 -21.81952,-3.27204 -42.19466,14.84436 -42.19466,36.99456 0,12.87803 7.91595,27.04428 19.77641,33.02516 12.1809,6.14247 22.74637,5.17465 33.71293,0.59254 z M 277.20427,589.71676 c 4.4022,-2.85596 7.46638,-7.54028 9.15453,-10.00429 3.89756,-5.68884 5.88117,-7.33165 6.71615,-12.62071 1.18905,-7.53183 7.08793,-17.66056 1.60823,-34.15841 -5.11798,-15.40882 -10.58182,-25.09295 -23.6605,-33.26863 -14.72235,-9.20316 -34.14273,-11.94531 -49.53478,-6.37283 -13.46366,4.87432 -25.95269,14.7297 -31.49101,27.45439 -2.35071,5.40091 -7.43872,21.06926 -6.20057,31.97796 1.746,15.38313 4.02431,15.9125 10.05702,26.91496 7.86427,14.34286 21.27803,17.99271 34.68295,22.77629 10.32927,3.68602 20.38835,1.39693 28.80095,-1.4132 1.46664,-0.48992 6.1248,-2.37016 7.85976,-3.49573 z m 452.97542,7.04138 c 3.79705,-1.96354 8.2363,-6.1028 11.88452,-10.88588 8.63292,-11.31835 8.89569,-30.94681 1.12047,-41.44103 -8.53608,-11.52115 -16.65572,-16.51548 -29.56326,-16.51548 -14.10096,0 -23.57985,3.3657 -31.68532,13.99252 -18.72064,24.54403 -1.12255,59.91545 29.69132,59.91545 6.42106,0 14.75519,-3.10204 18.55227,-5.06558 z m 155.58796,0.61357 c 19.02116,-9.02614 25.09689,-35.1501 13.03606,-52.81936 -7.7435,-11.34432 -19.93395,-17.64615 -31.98757,-17.27201 -5.50456,0.17086 -11.04882,1.43361 -16.00737,4.77519 -13.79416,9.29589 -21.12543,20.03414 -19.95481,34.61626 2.27156,28.29659 29.3138,42.84785 54.91369,30.69992 z m -274.0042,-16.35355 c 15.34191,-15.34192 5.37647,-39.00423 -16.32334,-39.00423 -6.52801,0 -11.19811,2.64263 -14.93237,6.15966 -4.23253,3.9863 -7.09815,9.29018 -7.09815,16.68923 0,9.40544 5.71132,16.0601 11.85927,19.9156 3.11769,1.95517 7.04151,2.30186 10.51618,2.50689 5.48181,0.32346 10.65837,-0.9471 15.97841,-6.26715 z M 474.14897,566.26567 c 0.90205,-20.72264 -0.95185,-23.00737 -18.66886,-23.00737 -21.29023,0 -21.33863,0.0494 -21.33863,21.78661 v 18.89525 l 20.1195,0.18271 14.83849,0.15549 4.34037,-1.7217 z m 326.54775,-33.97512 c 11.11979,-3.18913 22.30016,-15.18724 25.30514,-26.3469 2.41912,-8.98395 0.87148,-16.14462 -1.99306,-23.43852 -2.03352,-5.1779 -4.88992,-9.86114 -9.23712,-13.77761 -1.66141,-1.4968 -3.72255,-3.12355 -5.97613,-4.21449 -7.31392,-3.5406 -12.70908,-5.29993 -19.16163,-5.22828 -4.65799,0.0517 -9.81293,0.81439 -15.26714,3.29031 -23.65959,10.74021 -29.52208,41.89066 -10.63237,60.78035 5.70777,5.70776 12.24597,8.3241 18.94039,9.79054 3.78493,0.82911 7.44377,0.8628 11.50298,0.59722 2.00533,-0.13121 4.3437,-0.82877 6.51894,-1.45262 z M 418.68641,504.6854 c -0.16274,0.59432 0.32344,-1.62386 0.75648,-2.28608 4.85679,-7.42707 5.08522,-17.14952 1.04856,-24.6921 -2.88468,-5.39008 -12.88343,-11.74836 -19.66088,-11.36295 -18.92297,1.07607 -30.23763,22.02677 -17.02835,37.72514 5.49536,6.53088 12.54404,8.54387 19.69176,7.81449 3.35463,-0.34232 7.79008,-2.74995 11.13376,-4.47947 2.04114,-1.05577 5.02277,-6.23984 4.5255,-4.42384 z m 247.08582,0.41903 c 1.95189,-1.95189 3.08725,-4.09639 4.03923,-6.11879 1.1885,-2.52486 1.94383,-5.03962 1.94383,-8.83261 0,-11.83725 -4.47207,-16.73854 -13.25436,-21.28002 -5.50658,-2.84756 -8.6781,-2.42563 -11.97938,-2.14441 -3.41406,0.29084 -6.94004,0.56724 -11.67627,5.30348 -7.55182,7.55184 -8.96528,10.25402 -8.40434,19.07976 0.73664,11.59008 8.25419,17.98427 18.23442,20.57041 5.69347,1.47533 9.79388,0.11812 13.35414,-1.25582 2.79403,-1.07825 4.64672,-2.226 7.74273,-5.322 z" style="opacity:1;fill:#121212;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"/><polygon style="fill:#e6e6e6;stroke:none;stroke-width:0" id="polygon386" points="302.6 475.9 302.6 490.9 289.6 483.4" class="st1" transform="translate(25,219.99981)"/><polygon style="fill:#e6e6e6;stroke:none;stroke-width:0" id="polygon2110" points="354.2 527.4 346.7 540.4 339.2 527.4" class="st1" transform="matrix(1,0,0,-1,25,1187.3998)"/><polygon style="fill:#ececec;stroke:none;stroke-width:0" id="polygon384" points="390.7 475.9 390.7 490.9 403.7 483.4" class="st1" transform="translate(25,219.99981)"/><polygon style="fill:#e6e6e6;stroke:none;stroke-width:0" id="polygon382" points="339.2 527.4 354.2 527.4 346.7 540.4" class="st1" transform="translate(25,219.99981)"/><path id="path6258" d="m 646.96781,496.91365 v -5.5625 h -5.25 -5.25 v -2.125 -2.125 h 5.25 5.25 v -5.25 -5.25 h 2.15625 2.15625 v 5.25 5.25 h 5.53125 5.53125 v 2.125 2.125 h -5.53125 -5.53125 v 5.5625 5.5625 h -2.15625 -2.15625 z" style="opacity:1;fill:#fff;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6278" d="m 628.29435,761.15533 c -11.60031,-1.42753 -23.70942,-6.90024 -32.022,-15.09109 -4.89556,-4.82387 -8.4109,-9.79856 -11.48205,-16.05235 -17.34201,-35.31358 7.66631,-77.56695 47.03643,-79.25209 18.36135,-0.78591 36.01381,7.22886 47.19618,22.06765 3.36213,4.46148 7.79919,13.89603 9.13529,19.05313 6.43139,24.82385 -5.13299,50.83774 -27.7939,63.01024 -9.19401,4.93864 -21.81915,7.52597 -32.06995,6.26451 z m 17.8537,-9.82262 c 4.4309,-1.17123 10.75912,-4.28294 14.3016,-6.72023 8.63416,-5.94046 15.23153,-14.95301 18.50818,-25.60297 1.24392,-4.04306 1.61707,-4.89692 1.61707,-12.71774 0,-7.82083 -0.35054,-8.44557 -1.59446,-12.48862 -2.58737,-8.40964 -6.53571,-15.03925 -12.51361,-20.75566 -5.78223,-5.52929 -10.84609,-8.68768 -18.78824,-11.11462 -4.08655,-1.24876 -5.6836,-1.5599 -13.24188,-1.5599 -7.53025,0 -8.28081,0.10808 -12.29161,1.33464 -15.98307,4.88787 -26.8864,16.00238 -31.78068,31.99103 -1.23506,4.03469 -1.96468,4.87729 -1.96468,12.68194 0,7.80465 0.71609,8.81347 1.95115,12.84816 3.22046,10.52059 9.74604,19.70941 18.06875,25.36302 5.16767,3.5104 12.37195,6.11339 18.57678,7.23365 4.17973,0.75464 14.67909,0.68954 19.15163,-0.4927 z" style="opacity:1;fill:#252625;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6280" d="m 624.91691,750.31745 c -16.24363,-3.63718 -29.30122,-17.18614 -33.58555,-33.43687 -1.37227,-5.20509 -1.27366,-15.03979 -0.0536,-19.9939 2.28647,-9.28454 5.71057,-15.73309 12.53311,-22.49597 6.70419,-6.64558 14.26588,-10.45063 23.40764,-12.04482 5.49687,-0.95857 9.26266,-0.83041 14.75953,0.12816 8.73986,1.52411 16.12049,4.6729 22.716,10.89327 9.43811,8.90129 14.24857,20.14141 14.18619,33.16178 -0.0369,7.69449 -1.17093,12.62045 -4.67527,19.72867 -2.80369,5.68699 -6.60776,10.7198 -11.38242,14.61071 -3.6785,2.99765 -12.18105,7.38353 -16.63751,8.59841 -4.23849,1.15546 -17.01344,1.80324 -21.26809,0.85056 z" style="opacity:1;fill:#545553;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><g style="fill:#000" id="g6327" transform="translate(-576.1663,639.16947)"><path style="fill:#000" id="path6323" d="m 816.4233,-34.702478 c -31.6,0 -57.2,-25.6 -57.2,-57.19999 0,-31.600002 25.6,-57.200002 57.2,-57.200002 31.6,0 57.2,25.6 57.2,57.200002 0,31.49999 -25.6,57.09999 -57.2,57.19999 z m 0,-112.499992 c -30.5,0 -55.2,24.7 -55.2,55.200002 0,30.49999 24.7,55.19999 55.2,55.19999 30.5,0 55.2,-24.7 55.2,-55.19999 0,0 0,0 0,0 0,-30.400002 -24.7,-55.100002 -55.2,-55.200002 z" class="st1"/><path style="fill:#000;stroke-width:1.11974" id="path6325" d="m 816.50147,-45.055391 c -25.5864,0 -46.36838,-20.872572 -46.36838,-46.570513 0,-25.697956 20.78198,-46.570526 46.36838,-46.570526 25.58642,0 46.3684,20.87257 46.3684,46.570526 0,25.697941 -20.78198,46.458295 -46.3684,46.570513 z m 0,-91.008899 c -24.35737,0 -44.13377,19.86262 -44.13377,44.326168 0,24.463541 19.7764,44.32615 44.13377,44.32615 24.35738,0 44.13377,-19.862609 44.13377,-44.32615 v 0 c 0,-24.463548 -19.77639,-44.326168 -44.13377,-44.326168 z" class="st1"/></g><g style="fill:#000" id="g6333" transform="translate(-576.1663,639.16947)"><path style="fill:#000" id="path6329" d="m 816.4233,-34.702478 c -31.6,0 -57.2,-25.6 -57.2,-57.19999 0,-31.600002 25.6,-57.200002 57.2,-57.200002 31.6,0 57.2,25.6 57.2,57.200002 0,31.49999 -25.6,57.09999 -57.2,57.19999 z m 0,-112.499992 c -30.5,0 -55.2,24.7 -55.2,55.200002 0,30.49999 24.7,55.19999 55.2,55.19999 30.5,0 55.2,-24.7 55.2,-55.19999 0,0 0,0 0,0 0,-30.400002 -24.7,-55.100002 -55.2,-55.200002 z" class="st1"/><path style="fill:#000;stroke-width:1.11974" id="path6331" d="m 816.50147,-45.055391 c -25.5864,0 -46.36838,-20.872572 -46.36838,-46.570513 0,-25.697956 20.78198,-46.570526 46.36838,-46.570526 25.58642,0 46.3684,20.87257 46.3684,46.570526 0,25.697941 -20.78198,46.458295 -46.3684,46.570513 z m 0,-91.008899 c -24.35737,0 -44.13377,19.86262 -44.13377,44.326168 0,24.463541 19.7764,44.32615 44.13377,44.32615 24.35738,0 44.13377,-19.862609 44.13377,-44.32615 v 0 c 0,-24.463548 -19.77639,-44.326168 -44.13377,-44.326168 z" class="st1"/></g><path id="path6335" d="m 234.02942,602.31763 c -11.60031,-1.42753 -23.70942,-6.90024 -32.022,-15.09109 -4.89556,-4.82387 -8.4109,-9.79856 -11.48205,-16.05235 -17.34201,-35.31358 7.66631,-77.56695 47.03643,-79.25209 18.36135,-0.78591 36.01381,7.22886 47.19618,22.06765 3.36213,4.46148 7.79919,13.89603 9.13529,19.05313 6.43139,24.82385 -5.13299,50.83774 -27.7939,63.01024 -9.19401,4.93864 -21.81915,7.52597 -32.06995,6.26451 z m 17.8537,-9.82262 c 4.4309,-1.17123 10.75912,-4.28294 14.3016,-6.72023 8.63416,-5.94046 15.23153,-14.95301 18.50818,-25.60297 1.24392,-4.04306 1.61707,-4.89692 1.61707,-12.71774 0,-7.82083 -0.35054,-8.44557 -1.59446,-12.48862 -2.58737,-8.40964 -6.53571,-15.03925 -12.51361,-20.75566 -5.78223,-5.52929 -10.84609,-8.68768 -18.78824,-11.11462 -4.08655,-1.24876 -5.6836,-1.5599 -13.24188,-1.5599 -7.53025,0 -8.28081,0.10808 -12.29161,1.33464 -15.98307,4.88787 -26.8864,16.00238 -31.78068,31.99103 -1.23506,4.03469 -1.96468,4.87729 -1.96468,12.68194 0,7.80465 0.71609,8.81347 1.95115,12.84816 3.22046,10.52059 9.74604,19.70941 18.06875,25.36302 5.16767,3.5104 12.37195,6.11339 18.57678,7.23365 4.17973,0.75464 14.67909,0.68954 19.15163,-0.4927 z" style="opacity:1;fill:#252625;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6337" d="m 230.65198,591.47975 c -16.24363,-3.63718 -29.30122,-17.18614 -33.58555,-33.43687 -1.37227,-5.20509 -1.27366,-15.03979 -0.0536,-19.9939 2.28647,-9.28454 5.71057,-15.73309 12.53311,-22.49597 6.70419,-6.64558 14.26588,-10.45063 23.40764,-12.04482 5.49687,-0.95857 9.26266,-0.83041 14.75953,0.12816 8.73986,1.52411 16.12049,4.6729 22.716,10.89327 9.43811,8.90129 14.24857,20.14141 14.18619,33.16178 -0.0369,7.69449 -1.17093,12.62045 -4.67527,19.72867 -2.80369,5.68699 -6.60776,10.7198 -11.38242,14.61071 -3.6785,2.99765 -12.18105,7.38353 -16.63751,8.59841 -4.23849,1.15546 -17.01344,1.80324 -21.26809,0.85056 z" style="opacity:1;fill:#545553;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6291" d="m 188.90163,122.0103 c 0,0 -18.85549,1.7878 -26.77012,7.2931 -8.68779,6.0431 -15.27391,16.2386 -20.37699,26.60834 -15.65228,31.80624 -27.81774,67.96216 -29.2399,104.84918 -0.93086,24.14405 13.84853,70.628 13.84853,70.628 m 0,0 c 0,0 252.01795,12.28985 378.34438,12.53315 141.20796,0.27196 423.49847,-12.38079 423.49847,-12.38079" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1262" d="m 188.90163,122.0103 c 9.84115,-8.17682 21.83128,-14.08485 34.16041,-17.50553 9.07636,-2.51821 18.78492,-1.45165 28.19287,-1.91216 12.19957,-0.59715 36.62594,-1.10274 36.62594,-1.10274 l 35.81058,70.65473 15.19617,35.85898 c 0,0 -26.42571,8.80374 -39.32217,10.53318 -42.8309,5.7437 -85.65259,7.21069 -128.18325,17.09047 -8.36038,1.9421 -25.82529,3.74497 -25.82529,3.74497 0,0 11.32358,-65.5795 25.97665,-94.93299 4.2233,-8.46024 10.04498,-16.34429 17.36809,-22.42891 z" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path id="path1258" d="m 245.84128,102.4386 c 0,0 -5.76706,-13.56751 -9.35823,-19.651751 C 230.23325,72.198298 223.08453,62.325287 215.37588,53.222178 202.35905,37.850658 189.65643,20.767603 173.19867,11.249234 160.36119,3.8246518 145.5836,1.4898198 131.34281,0.75442277 116.00437,-0.03765523 99.981569,0.87541177 85.616627,7.368779 74.607367,12.345281 65.963835,20.164119 56.507422,31.298832 47.573652,41.818136 43.039216,58.02839 39.342587,71.354951 34.922682,87.28896 33.648381,116.53993 33.648381,116.53993 c 0,0 -0.960455,43.0713 8.788617,78.59059 9.749073,35.51928 9.900725,37.90374 18.095353,55.58077 8.330773,17.97071 18.318558,35.30495 31.233138,50.5532 9.755691,11.51855 27.739781,29.96595 34.597661,30.12443" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><text style="font-style:normal;font-weight:400;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect6355);fill:#fff;fill-opacity:1;stroke:none" id="text6353" transform="matrix(0.97792603,0,0,0.96451704,2900.9057,909.73499)" xml:space="preserve"><tspan x="-2742.252" y="-757.381"><tspan style="fill:#fff">ZL</tspan></tspan></text><text style="font-style:normal;font-weight:400;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect6363);fill:#fff;fill-opacity:1;stroke:none" id="text6361" transform="matrix(0.97792603,0,0,0.96451704,3473.1053,909.73499)" xml:space="preserve"><tspan x="-2742.252" y="-757.381"><tspan style="fill:#fff">ZR</tspan></tspan></text><text style="font-style:normal;font-weight:400;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect6367);fill:#fff;fill-opacity:1;stroke:none" id="text6365" transform="translate(250.14856,2.4219055)" xml:space="preserve"><tspan x="-3.254" y="289.306"><tspan style="fill:#fff">L</tspan></tspan></text><text style="font-style:normal;font-weight:400;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect6375);fill:#fff;fill-opacity:1;stroke:none" id="text6373" transform="translate(781.74891,2.4219055)" xml:space="preserve"><tspan x="-3.254" y="289.306"><tspan style="fill:#fff">R</tspan></tspan></text><path id="path6377" d="m 785.64551,667.92499 c -8.15309,-1.13602 -15.0704,-4.54677 -20.75974,-10.23612 -8.9285,-8.9285 -12.39431,-21.39165 -9.34523,-33.60576 3.36093,-13.46336 14.29474,-23.65724 28.22997,-26.31953 3.11895,-0.59587 9.18634,-0.58261 12.375,0.027 13.05146,2.49536 23.44512,11.64463 27.45735,24.17 1.30965,4.08843 1.65393,6.39581 1.63662,10.9685 -0.0122,3.23174 -0.13271,4.67657 -0.55639,6.67211 -1.50148,7.07212 -4.72455,13.03435 -9.77783,18.08764 -5.02258,5.02257 -11.04724,8.29153 -17.96,9.74503 -2.78306,0.58517 -8.7602,0.84494 -11.29975,0.49109 z m 6.8002,-22.77451 c 2.08011,-0.43751 3.82122,-1.37987 5.0267,-2.72066 1.35168,-1.50339 1.76103,-2.84172 1.63465,-5.34431 -0.0805,-1.59334 -0.20733,-2.03516 -0.84488,-2.94229 -0.45588,-0.64863 -1.25285,-1.33 -2.03138,-1.7367 l -1.28117,-0.6693 1.0859,-0.76541 c 1.41215,-0.99537 2.15231,-2.46375 2.3035,-4.56982 0.14493,-2.01889 -0.21817,-3.22199 -1.35078,-4.47566 -1.71403,-1.89722 -4.96187,-2.58958 -11.46774,-2.44462 l -3.25,0.0724 -0.0647,12.9375 -0.0648,12.9375 h 4.48995 c 2.46947,0 5.0861,-0.12539 5.81475,-0.27864 z" style="opacity:1;fill:#3b3c3a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6379" d="m 785.89551,638.05412 v -4.43662 l 2.94997,0.10518 c 2.50133,0.0892 3.1362,0.19842 4.17451,0.71831 1.70933,0.85588 2.24219,1.67349 2.24219,3.44041 0,1.91885 -0.63165,2.98958 -2.23339,3.78586 -1.04436,0.51919 -1.68535,0.62925 -4.18331,0.71831 l -2.94997,0.10518 z" style="opacity:1;fill:#3b3c3a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6381" d="m 785.89551,626.58046 v -3.90134 h 2.3893 c 2.77536,0 4.23874,0.30963 5.24118,1.10895 0.62892,0.50149 0.72898,0.76908 0.81958,2.19205 0.12529,1.96762 -0.28074,2.89767 -1.6315,3.73713 -0.82075,0.51007 -1.32589,0.59799 -3.88106,0.67547 l -2.9375,0.0891 z" style="opacity:1;fill:#3b3c3a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6383" d="m 707.39551,599.92499 c -7.92113,-1.1037 -14.96701,-4.50926 -20.38541,-9.85308 -6.22903,-6.1433 -9.75995,-13.54668 -10.61451,-22.25575 -1.07687,-10.97477 3.61757,-22.59585 12.03642,-29.79613 4.92609,-4.21307 10.10613,-6.76316 16.49169,-8.11872 3.45265,-0.73294 9.46672,-0.78505 12.97181,-0.11239 14.48926,2.78063 25.42291,13.49277 28.54643,27.9681 0.73248,3.39453 0.79693,10.17685 0.12818,13.48958 -2.8807,14.27003 -13.69532,25.20587 -27.87486,28.1873 -2.78306,0.58517 -8.7602,0.84494 -11.29975,0.49109 z m 6,-28.0293 v -4.53343 l 4.64728,-8.23595 c 2.55601,-4.52978 4.60913,-8.27396 4.5625,-8.32041 -0.0466,-0.0465 -0.98287,-0.12211 -2.08055,-0.16812 l -1.99577,-0.0837 -1.2063,2.25 c -0.66346,1.2375 -2.20164,4.15068 -3.41817,6.47372 l -2.21187,4.22372 -0.5442,-1.09872 c -0.29931,-0.60429 -1.85477,-3.54559 -3.45658,-6.53622 l -2.91239,-5.4375 h -2.19197 c -1.20559,0 -2.19198,0.053 -2.19198,0.11787 0,0.0648 2.08125,3.80978 4.625,8.32212 l 4.625,8.20425 v 4.67788 4.67788 h 1.875 1.875 z" style="opacity:1;fill:#3b3c3a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6385" d="m 785.64551,531.92499 c -8.15309,-1.13602 -15.0704,-4.54677 -20.75974,-10.23612 -8.9285,-8.9285 -12.39431,-21.39165 -9.34523,-33.60576 3.36093,-13.46336 14.29474,-23.65724 28.22997,-26.31953 3.11895,-0.59587 9.18634,-0.58261 12.375,0.027 13.05146,2.49536 23.44512,11.64463 27.45735,24.17 1.30965,4.08843 1.65393,6.39581 1.63662,10.9685 -0.0122,3.23174 -0.13271,4.67657 -0.55639,6.67211 -1.50148,7.07212 -4.72455,13.03435 -9.77783,18.08764 -5.02258,5.02257 -11.04724,8.29153 -17.96,9.74503 -2.78306,0.58517 -8.7602,0.84494 -11.29975,0.49109 z m 0.875,-27.49805 c 1.65,-2.74797 3.05748,-4.99665 3.12774,-4.99706 0.0703,-5e-4 1.47651,2.24475 3.125,4.98925 l 2.99726,4.99001 2.43188,0.01 2.43189,0.01 -1.35916,-2.10687 c -0.74754,-1.15877 -2.22449,-3.4369 -3.28212,-5.0625 -1.05763,-1.6256 -2.40627,-3.70686 -2.99698,-4.62503 l -1.07402,-1.66939 3.86176,-6.04894 c 2.12396,-3.32693 3.86175,-6.15213 3.86175,-6.27823 0,-0.12637 -0.92533,-0.19669 -2.06162,-0.15667 l -2.06161,0.0726 -2.78041,4.58251 c -1.52923,2.52038 -2.85752,4.60163 -2.95177,4.625 -0.0942,0.0234 -1.43761,-2.03877 -2.98527,-4.58252 l -2.81392,-4.62499 -2.20335,-0.0727 c -1.92048,-0.0633 -2.18395,-0.0221 -2.05224,0.32113 0.0831,0.21659 1.84382,3.09152 3.91268,6.38873 l 3.76156,5.99492 -4.00742,6.13862 c -2.20408,3.37624 -4.1237,6.3559 -4.26582,6.62145 -0.24676,0.46108 -0.15383,0.4827 2.0629,0.4799 l 2.32129,-0.003 3,-4.99631 z" style="opacity:1;fill:#3b3c3a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6387" d="m 864.77051,600.03207 c -5.36259,-0.77061 -8.80039,-1.78331 -12.6516,-3.72686 -3.56595,-1.79958 -5.81339,-3.45471 -8.989,-6.61994 -5.2846,-5.26732 -8.43262,-11.12516 -9.90901,-18.43867 -0.62781,-3.10997 -0.6123,-10.0886 0.0293,-13.19248 3.02446,-14.6309 13.99984,-25.42204 28.77029,-28.28733 3.36305,-0.65239 9.43968,-0.58876 12.84681,0.13452 7.99698,1.69764 14.39024,5.4025 19.83,11.49139 6.24807,6.99365 9.66289,17.34209 8.71871,26.42159 -1.64716,15.8395 -12.84574,28.38681 -28.22077,31.61961 -2.33603,0.49118 -8.64865,0.8534 -10.42475,0.59817 z m -3.12922,-26.53974 0.87922,-2.81179 h 5.25 5.25 l 0.87928,2.81179 0.87928,2.81179 2.05822,0.0726 c 1.13202,0.0399 2.05584,0.0399 2.05294,0 -0.003,-0.0399 -1.9058,-5.86636 -4.22865,-12.94761 l -4.22335,-12.875 -2.51137,-0.0711 -2.51136,-0.0711 -4.17287,12.69614 c -2.29508,6.98287 -4.22603,12.84174 -4.291,13.01971 -0.0917,0.25111 0.32179,0.30709 1.84616,0.25 l 1.96428,-0.0736 z" style="opacity:1;fill:#3b3c3a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path6389" d="m 863.75778,566.74162 c 0.3174,-1.31077 3.92282,-12.36072 4.00491,-12.27432 0.0631,0.0664 2.6759,8.18388 3.99103,12.39932 l 0.17548,0.5625 h -4.16894 -4.16895 z" style="opacity:1;fill:#3b3c3a;fill-opacity:1;stroke:#000;stroke-width:0;stroke-opacity:1"/><path id="path978" d="m 865.98538,122.0103 c 0,0 18.85549,1.7878 26.77012,7.2931 8.68779,6.0431 15.27391,16.2386 20.37699,26.60834 15.65228,31.80624 27.81774,67.96216 29.2399,104.84918 0.93086,24.14405 -13.84853,70.628 -13.84853,70.628 m 0,0 c 0,0 -252.01795,12.28985 -378.34438,12.53315 -141.20796,0.27196 -423.49847,-12.38079 -423.49847,-12.38079" style="fill:none;stroke:#000;stroke-width:1.02767px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/></svg> \ No newline at end of file +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 27.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1050 1050.5" + style="enable-background:new 0 0 1050 1050.5;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#20221F;} + .st1{fill:#3B3B3B;} + .st2{fill:#121212;} + .st3{fill:#444542;} + .st4{fill:#FFFFFF;} + .st5{fill:#444542;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;} + .st6{fill:#454644;} + .st7{fill:#454644;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;} + .st8{fill:#3B3C3A;} + .st9{font-family:'Helvetica-Bold';} + .st10{font-size:40px;} + .st11{fill:#0D0D0A;} +</style> +<g id="Front"> + <path id="Right_Grip_00000028282943321285403220000008369785803052272051_" class="st0" d="M766,850.5 + c34,28.2,27.6,35.9,68.5,108.5c36.7,74.7,64.4,104.4,125.1,84.1v0c95.3-57.9,59.3-145.3,43.6-275.2c-10-60.6-35.6-190.3-35.6-190.3 + L766,850.5z"/> + <path id="Left_Grip" class="st0" d="M82.3,577.6c0,0-25.6,129.7-35.6,190.3C31,897.8-5,985.1,90.3,1043v0 + c60.8,20.3,88.4-9.4,125.1-84.1c40.9-72.7,34.5-80.3,68.5-108.5L82.3,577.6z"/> + <path id="Right_Bumper_00000006710349871522532470000011078040965381267594_" class="st1" d="M676.3,378.4 + c10.1-4.3,39.7-22.5,58.7-19.7c59.5,0.9,166.7,17.7,172.6,81.2"/> + <path id="Left_Bumper_00000024680414077879639570000011759596763560342154_" class="st1" d="M142.4,439.9 + c5.9-63.4,113-80.2,172.6-81.2c19-2.8,48.6,15.4,58.7,19.7"/> + <path id="Background_00000141418846164053065470000016150094984198570163_" class="st2" d="M766,850.5 + c35.5-30.8,68.5-74.7,96-113.5c26.9-36.3,94.7-136.7,105.6-159.3c0-2.4-6.3-30.1-12.8-56.2C919.1,361.9,702.2,378.1,525,378.1 + c-177.4,0-394.1-16.2-429.9,143.3c-6.5,26-12.8,53.8-12.8,56.2c10.9,22.6,78.8,123,105.6,159.3c27.5,38.8,60.5,82.8,96,113.5"/> + <g id="Directional_Pad"> + <path id="Background_00000032628022449190479560000015279211462520783249_" class="st3" d="M466.2,683.5h-40c-2.8,0-5-2.2-5-5v-40 + c0-2.8-2.2-5-5-5h-30c-2.8,0-5,2.2-5,5v40c0,2.8-2.2,5-5,5h-40c-2.8,0-5,2.2-5,5v30c0,2.8,2.2,5,5,5h40c2.8,0,5,2.2,5,5v40 + c0,2.8,2.2,5,5,5h30c2.8,0,5-2.2,5-5v-40c0-2.8,2.2-5,5-5h40c2.8,0,5-2.2,5-5v-30C471.2,685.8,469,683.5,466.2,683.5z"/> + <g id="Arrows"> + <g> + <polygon class="st4" points="393.7,746 408.7,746 401.2,761 "/> + </g> + <g> + <polygon class="st4" points="358.7,696 358.7,711 343.7,703.5 "/> + </g> + <g> + <polygon class="st4" points="408.7,661 393.7,661 401.2,646 "/> + </g> + <g> + <polygon class="st4" points="443.7,711 443.7,696 458.7,703.5 "/> + </g> + </g> + </g> + <g id="R_Thumbstick_00000152226188525111835500000011838297421350334865_"> + <circle id="Background_00000035532849542660068350000006517224202948159422_" class="st0" cx="650.6" cy="703.5" r="55"/> + <circle id="Stick" class="st5" cx="650.6" cy="703.5" r="45"/> + </g> + <g id="L_Thumbstick_00000047032468231999382210000005512347386782594484_"> + <circle id="Background_00000182502673988292164000000007125719133096369561_" class="st0" cx="240.2" cy="564.8" r="55"/> + <circle id="Stick_00000075121990265259598900000000214370239054002365_" class="st5" cx="240.2" cy="564.8" r="45"/> + </g> + <g id="Minus_Button"> + <circle id="_Background_00000120554951013892796430000015877571645746699662_" class="st6" cx="401" cy="489.3" r="22.5"/> + <polyline id="Plus_00000039131319101621183460000006196023733899658629_" class="st4" points="386.2,491.8 386.2,486.8 + 416.2,486.8 416.2,491.8 "/> + </g> + <g id="Plus_Button"> + <circle id="_Background" class="st6" cx="650.4" cy="489.6" r="22.5"/> + <polygon id="Plus" class="st4" points="665.6,487.1 653.1,487.1 653.1,474.4 648.1,474.4 648.1,487.1 635.6,487.1 635.6,492.1 + 648.1,492.1 648.1,504.4 653.1,504.4 653.1,492.1 665.6,492.1 "/> + </g> + <g id="Home_Button_00000029758737660217614780000001403165237001195407_"> + <circle id="_Background_00000132788487854287834010000009548421243227981499_" class="st6" cx="605.4" cy="564.8" r="22.5"/> + <path id="Home" class="st4" d="M605.4,549.8l-15,15h5v15h20v-15h5L605.4,549.8z M610.4,574.8h-10v-10h10V574.8z"/> + </g> + <g id="Capture_Button_00000105394663133565750060000017455731898661404072_"> + <path class="st6" d="M468.6,586.5h-30c-2.8,0-5-2.2-5-5v-29.5c0-2.8,2.2-5,5-5h30c2.8,0,5,2.2,5,5v29.5 + C473.6,584.2,471.4,586.5,468.6,586.5z"/> + <circle class="st7" cx="453.6" cy="566.7" r="15"/> + </g> + <g id="Buttons_00000023239109225132251950000005218343074279628213_"> + <g id="A_Button"> + <circle id="Background_00000006699118933065716380000004636085088820886913_" class="st8" cx="863.9" cy="564.8" r="35"/> + <text transform="matrix(1 0 0 1 849.4224 578.6607)" class="st4 st9 st10">A</text> + </g> + <g id="X_Button"> + <circle id="Background_00000083074713085756701790000016893839312974798515_" class="st8" cx="793.9" cy="494.8" r="35"/> + <text transform="matrix(1 0 0 1 780.5266 508.6604)" class="st4 st9 st10">X</text> + </g> + <g id="Y_Button_00000100344340438574137780000014238704828967683973_"> + <circle id="Background_00000137100455694543496620000011124722786613194377_" class="st8" cx="723.9" cy="564.8" r="35"/> + <text transform="matrix(1 0 0 1 710.5263 578.661)" class="st4 st9 st10">Y</text> + </g> + <g id="B_Button_00000041994261956088037220000013468634544777304733_"> + <circle id="Background_00000096038108578846046800000001873940014252420514_" class="st8" cx="793.9" cy="634.8" r="35"/> + <text transform="matrix(1 0 0 1 780.9706 648.6605)" class="st4 st9 st10">B</text> + </g> + </g> +</g> +<g id="Top_Down"> + <path id="Left_Grip_00000026131988385328425370000016677941743356253314_" class="st0" d="M219.2,78.5 + c-12.5-17.6-25.9-42.3-45.6-58.6C153.5,3.3,112.1-4.7,87.1,5.8c-13.9,5.8-33.4,33.1-42.7,52.8C33.9,80.9,30.4,109.9,32,141.4 + c1.2,25.1,5.3,51.7,14.2,78.6c0,0,14.3,53.8,42.8,80.8c11.2,10.6,35,26.6,35,26.6l116-217.5C240,109.9,224.6,86.2,219.2,78.5z"/> + <path id="Right_Grip_00000016782759094708820330000002450847065936193693_" class="st0" d="M828.6,78.5 + c12.5-17.6,25.9-42.3,45.6-58.6c20.1-16.6,61.4-24.5,86.5-14.1c13.9,5.8,33.4,33.1,42.7,52.8c10.5,22.3,13.9,51.3,12.4,82.8 + c-1.2,25.1-5.3,51.7-14.2,78.6c0,0-14.3,53.8-42.8,80.8c-11.2,10.6-35,26.6-35,26.6l-116-217.5C807.8,109.9,823.2,86.2,828.6,78.5z + "/> + <path id="Background_00000169534857628063347190000007586592143875928969_" class="st11" d="M866,122.2 + c66.3,18.7,85.1,128.8,69,186c-2.5,54.2-148.9,15.3-265.1,31.2c-41.1,1.7-91.8,2.4-145.9,2.3c-54.1,0-104.8-0.6-145.9-2.3 + c-116.2-15.9-262.6,23.1-265.1-31.2c-16.1-57.1,2.6-167.3,69-186l60.5-18.8l38.9-1.9c40.2,0.1,142.8,0,242.7,0 + c99.9,0,202.4,0.1,242.7,0l38.9,1.9L866,122.2z"/> + <g id="ZL_Trigger_00000005254517714433203260000014117442438696169895_"> + <path id="Background_00000111870097528015387240000017384507710402295183_" class="st1" d="M145.9,239.2 + c15.2-97.4,38.1-147.2,141.7-137c8.2,16.4,43.3,83,50.6,105.7C280.6,227.2,204.7,225.6,145.9,239.2z"/> + <text id="ZL_Trigger" transform="matrix(1.0139 0 0 1 218.3906 179.3992)" class="st4 st9 st10">ZL</text> + </g> + <g id="ZR_Trigger"> + <path id="Background_00000133526766189752063450000016781240006605114763_" class="st1" d="M716.2,207.9 + c7.4-22.7,42.5-89.3,50.6-105.7c103.7-10.2,126.5,39.6,141.7,137C849.8,225.6,773.8,227.2,716.2,207.9z"/> + + <text id="ZL_Trigger_00000000206350378518266660000001315160307759857328_" transform="matrix(1.0139 0 0 1 784.2356 179.3992)" class="st4 st9 st10">ZR</text> + </g> + <g id="R_Trigger_00000085939413106284991650000014018840000393673094_"> + <path id="Background" class="st1" d="M664,318.5c7-10.1,27.8-78.4,45.4-78.7C1040.8,243.7,897.1,334,664,318.5z"/> + <text id="R_Trigger" transform="matrix(1 0 0 1 769.6461 292.8947)" class="st4 st9 st10">R</text> + </g> + <g id="L_Trigger"> + <path id="Background_00000043427985111927735300000011910735497762731703_" class="st1" d="M340.6,238.6 + c17.6,0.3,38.4,68.6,45.4,78.7C152.9,332.8,9.2,242.6,340.6,238.6z"/> + + <text id="R_Trigger_00000092444210070373642420000009814634285137007748_" transform="matrix(1 0 0 1 253.7327 291.7279)" class="st4 st9 st10">L</text> + </g> +</g> +</svg> From c2e4c8f98e5d49749f8691fd0982a746339f8211 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 22 May 2023 18:41:08 +0100 Subject: [PATCH 556/737] Bump MVK Version (#5057) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0bbe81406..c46a41bf0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -32,7 +32,7 @@ <PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" /> <PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" /> - <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> + <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.3" /> <PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" /> <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" /> <PackageVersion Include="shaderc.net" Version="0.1.0" /> From 2725e40838070f34e2d10938f3a4223ee0629186 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 22 May 2023 17:12:11 -0300 Subject: [PATCH 557/737] Revert "Bump MVK Version (#5057)" (#5061) This reverts commit c2e4c8f98e5d49749f8691fd0982a746339f8211. --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c46a41bf0..0bbe81406 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -32,7 +32,7 @@ <PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" /> <PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" /> - <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.3" /> + <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" /> <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" /> <PackageVersion Include="shaderc.net" Version="0.1.0" /> From 6cb6b15612a20717c0e98045914b535f582cdc33 Mon Sep 17 00:00:00 2001 From: makigumo <makigumo@users.noreply.github.com> Date: Mon, 22 May 2023 22:32:15 +0200 Subject: [PATCH 558/737] Implement p2rc, p2ri, p2rr and r2p.cc shaders (#5031) * implement P2rC, P2rI, P2rR shaders * implement R2p.CC shader * bump CodeGenVersion * address feedback --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Instructions/InstEmit.cs | 21 ------ .../Instructions/InstEmitMove.cs | 22 +++---- .../Instructions/InstEmitPredicate.cs | 64 ++++++++++++++++++- 4 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 9f263e9d7..400f63f56 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4646; + private const uint CodeGenVersion = 5031; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs index 3a9e658aa..963a5c656 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs @@ -187,27 +187,6 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented."); } - public static void P2rR(EmitterContext context) - { - InstP2rR op = context.GetOp<InstP2rR>(); - - context.Config.GpuAccessor.Log("Shader instruction P2rR is not implemented."); - } - - public static void P2rI(EmitterContext context) - { - InstP2rI op = context.GetOp<InstP2rI>(); - - context.Config.GpuAccessor.Log("Shader instruction P2rI is not implemented."); - } - - public static void P2rC(EmitterContext context) - { - InstP2rC op = context.GetOp<InstP2rC>(); - - context.Config.GpuAccessor.Log("Shader instruction P2rC is not implemented."); - } - public static void Pexit(EmitterContext context) { InstPexit op = context.GetOp<InstPexit>(); diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index 9992ac378..e12177f7d 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -209,21 +209,15 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0)); } - if (ccpr) + int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount; + RegisterType type = ccpr ? RegisterType.Flag : RegisterType.Predicate; + int shift = (int)byteSel * 8; + + for (int bit = 0; bit < count; bit++) { - // TODO: Support Register to condition code flags copy. - context.Config.GpuAccessor.Log("R2P.CC not implemented."); - } - else - { - int shift = (int)byteSel * 8; - - for (int bit = 0; bit < RegisterConsts.PredsCount; bit++) - { - Operand pred = Register(bit, RegisterType.Predicate); - Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), pred); - context.Copy(pred, res); - } + Operand flag = Register(bit, type); + Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), flag); + context.Copy(flag, res); } } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs index d605661ff..79919624e 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -50,5 +49,68 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res); context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res); } + + public static void P2rC(EmitterContext context) + { + InstP2rC op = context.GetOp<InstP2rC>(); + + Operand srcA = GetSrcReg(context, op.SrcA); + Operand dest = GetSrcReg(context, op.Dest); + Operand mask = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); + + EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr); + } + + public static void P2rI(EmitterContext context) + { + InstP2rI op = context.GetOp<InstP2rI>(); + + Operand srcA = GetSrcReg(context, op.SrcA); + Operand dest = GetSrcReg(context, op.Dest); + Operand mask = GetSrcImm(context, op.Imm20); + + EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr); + } + + public static void P2rR(EmitterContext context) + { + InstP2rR op = context.GetOp<InstP2rR>(); + + Operand srcA = GetSrcReg(context, op.SrcA); + Operand dest = GetSrcReg(context, op.Dest); + Operand mask = GetSrcReg(context, op.SrcB); + + EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr); + } + + private static void EmitP2r( + EmitterContext context, + Operand srcA, + Operand dest, + Operand mask, + ByteSel byteSel, + bool ccpr) + { + int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount; + int shift = (int)byteSel * 8; + mask = context.BitwiseAnd(mask, Const(0xff)); + + Operand insert = Const(0); + for (int i = 0; i < count; i++) + { + Operand condition = ccpr + ? Register(i, RegisterType.Flag) + : Register(i, RegisterType.Predicate); + + Operand bit = context.ConditionalSelect(condition, Const(1 << (i + shift)), Const(0)); + insert = context.BitwiseOr(insert, bit); + } + + Operand maskShifted = context.ShiftLeft(mask, Const(shift)); + Operand masked = context.BitwiseAnd(srcA, context.BitwiseNot(maskShifted)); + Operand res = context.BitwiseOr(masked, context.BitwiseAnd(insert, maskShifted)); + + context.Copy(dest, res); + } } } \ No newline at end of file From 4ca78eded52f21089400cc28351b9353279b8171 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 23 May 2023 05:41:37 -0300 Subject: [PATCH 559/737] Vulkan: Do not set storage flag for multisample textures if not supported (#5060) --- src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs | 3 +++ src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 6 +++--- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 2 +- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 5cab4113a..4b9ace3aa 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsSubgroupSizeControl; public readonly bool SupportsShaderInt8; public readonly bool SupportsShaderStencilExport; + public readonly bool SupportsShaderStorageImageMultisample; public readonly bool SupportsConditionalRendering; public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsMultiView; @@ -63,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsSubgroupSizeControl, bool supportsShaderInt8, bool supportsShaderStencilExport, + bool supportsShaderStorageImageMultisample, bool supportsConditionalRendering, bool supportsExtendedDynamicState, bool supportsMultiView, @@ -97,6 +99,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsSubgroupSizeControl = supportsSubgroupSizeControl; SupportsShaderInt8 = supportsShaderInt8; SupportsShaderStencilExport = supportsShaderStencilExport; + SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample; SupportsConditionalRendering = supportsConditionalRendering; SupportsExtendedDynamicState = supportsExtendedDynamicState; SupportsMultiView = supportsMultiView; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 0582e6ca8..4edb6f2fe 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = GetImageUsageFromFormat(info.Format); + var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); var flags = ImageCreateFlags.CreateMutableFormatBit; @@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsageFromFormat(GAL.Format format) + public static ImageUsageFlags GetImageUsage(GAL.Format format, Target target, bool supportsMsStorage) { var usage = DefaultUsageFlags; @@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } - if (format.IsImageCompatible()) + if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample())) { usage |= ImageUsageFlags.StorageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 62d481eb9..c2be74974 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); - var usage = TextureStorage.GetImageUsageFromFormat(info.Format); + var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 74267325c..4f3f72345 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -295,6 +295,7 @@ namespace Ryujinx.Graphics.Vulkan supportsSubgroupSizeControl, featuresShaderInt8.ShaderInt8, _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"), + features2.Features.ShaderStorageImageMultisample, _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), features2.Features.MultiViewport, From 274af65f69f25c88c50b626b785a33aade9a1b63 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Thu, 25 May 2023 15:17:37 +0100 Subject: [PATCH 560/737] Update release.yml (#5058) --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a32033190..491fe6d9e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: shell: bash - name: Create tag - uses: actions/github-script@v5 + uses: actions/github-script@v6 with: script: | github.rest.git.createRef({ @@ -207,4 +207,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit \ No newline at end of file + secrets: inherit From 2c9715acf6cb9ae8bceabec5e81602c7d64c7192 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 25 May 2023 17:03:51 -0300 Subject: [PATCH 561/737] Truncate vertex attribute format if it exceeds stride on MoltenVK (#5094) * Truncate vertex attribute format if it exceeds stride on MoltenVK * Fix BGR format * Move vertex attribute check to pipeline creation to avoid costs * No need for this to be public --- src/Ryujinx.Graphics.Vulkan/FormatTable.cs | 218 +++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 49 ++++- 2 files changed, 265 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs index 45fc46ada..a030d8c85 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs @@ -168,5 +168,223 @@ namespace Ryujinx.Graphics.Vulkan { return _table[(int)format]; } + + public static int GetAttributeFormatSize(VkFormat format) + { + switch (format) + { + case VkFormat.R8Unorm: + case VkFormat.R8SNorm: + case VkFormat.R8Uint: + case VkFormat.R8Sint: + case VkFormat.R8Uscaled: + case VkFormat.R8Sscaled: + return 1; + + case VkFormat.R8G8Unorm: + case VkFormat.R8G8SNorm: + case VkFormat.R8G8Uint: + case VkFormat.R8G8Sint: + case VkFormat.R8G8Uscaled: + case VkFormat.R8G8Sscaled: + case VkFormat.R16Sfloat: + case VkFormat.R16Unorm: + case VkFormat.R16SNorm: + case VkFormat.R16Uint: + case VkFormat.R16Sint: + case VkFormat.R16Uscaled: + case VkFormat.R16Sscaled: + return 2; + + case VkFormat.R8G8B8Unorm: + case VkFormat.R8G8B8SNorm: + case VkFormat.R8G8B8Uint: + case VkFormat.R8G8B8Sint: + case VkFormat.R8G8B8Uscaled: + case VkFormat.R8G8B8Sscaled: + return 3; + + case VkFormat.R8G8B8A8Unorm: + case VkFormat.R8G8B8A8SNorm: + case VkFormat.R8G8B8A8Uint: + case VkFormat.R8G8B8A8Sint: + case VkFormat.R8G8B8A8Srgb: + case VkFormat.R8G8B8A8Uscaled: + case VkFormat.R8G8B8A8Sscaled: + case VkFormat.B8G8R8A8Unorm: + case VkFormat.B8G8R8A8Srgb: + case VkFormat.R16G16Sfloat: + case VkFormat.R16G16Unorm: + case VkFormat.R16G16SNorm: + case VkFormat.R16G16Uint: + case VkFormat.R16G16Sint: + case VkFormat.R16G16Uscaled: + case VkFormat.R16G16Sscaled: + case VkFormat.R32Sfloat: + case VkFormat.R32Uint: + case VkFormat.R32Sint: + case VkFormat.A2B10G10R10UnormPack32: + case VkFormat.A2B10G10R10UintPack32: + case VkFormat.B10G11R11UfloatPack32: + case VkFormat.E5B9G9R9UfloatPack32: + case VkFormat.A2B10G10R10SNormPack32: + case VkFormat.A2B10G10R10SintPack32: + case VkFormat.A2B10G10R10UscaledPack32: + case VkFormat.A2B10G10R10SscaledPack32: + return 4; + + case VkFormat.R16G16B16Sfloat: + case VkFormat.R16G16B16Unorm: + case VkFormat.R16G16B16SNorm: + case VkFormat.R16G16B16Uint: + case VkFormat.R16G16B16Sint: + case VkFormat.R16G16B16Uscaled: + case VkFormat.R16G16B16Sscaled: + return 6; + + case VkFormat.R16G16B16A16Sfloat: + case VkFormat.R16G16B16A16Unorm: + case VkFormat.R16G16B16A16SNorm: + case VkFormat.R16G16B16A16Uint: + case VkFormat.R16G16B16A16Sint: + case VkFormat.R16G16B16A16Uscaled: + case VkFormat.R16G16B16A16Sscaled: + case VkFormat.R32G32Sfloat: + case VkFormat.R32G32Uint: + case VkFormat.R32G32Sint: + return 8; + + case VkFormat.R32G32B32Sfloat: + case VkFormat.R32G32B32Uint: + case VkFormat.R32G32B32Sint: + return 12; + + case VkFormat.R32G32B32A32Sfloat: + case VkFormat.R32G32B32A32Uint: + case VkFormat.R32G32B32A32Sint: + return 16; + } + + return 1; + } + + public static VkFormat DropLastComponent(VkFormat format) + { + switch (format) + { + case VkFormat.R8G8Unorm: + return VkFormat.R8Unorm; + case VkFormat.R8G8SNorm: + return VkFormat.R8SNorm; + case VkFormat.R8G8Uint: + return VkFormat.R8Uint; + case VkFormat.R8G8Sint: + return VkFormat.R8Sint; + case VkFormat.R8G8Uscaled: + return VkFormat.R8Uscaled; + case VkFormat.R8G8Sscaled: + return VkFormat.R8Sscaled; + + case VkFormat.R8G8B8Unorm: + return VkFormat.R8G8Unorm; + case VkFormat.R8G8B8SNorm: + return VkFormat.R8G8SNorm; + case VkFormat.R8G8B8Uint: + return VkFormat.R8G8Uint; + case VkFormat.R8G8B8Sint: + return VkFormat.R8G8Sint; + case VkFormat.R8G8B8Uscaled: + return VkFormat.R8G8Uscaled; + case VkFormat.R8G8B8Sscaled: + return VkFormat.R8G8Sscaled; + + case VkFormat.R8G8B8A8Unorm: + return VkFormat.R8G8B8Unorm; + case VkFormat.R8G8B8A8SNorm: + return VkFormat.R8G8B8SNorm; + case VkFormat.R8G8B8A8Uint: + return VkFormat.R8G8B8Uint; + case VkFormat.R8G8B8A8Sint: + return VkFormat.R8G8B8Sint; + case VkFormat.R8G8B8A8Srgb: + return VkFormat.R8G8B8Srgb; + case VkFormat.R8G8B8A8Uscaled: + return VkFormat.R8G8B8Uscaled; + case VkFormat.R8G8B8A8Sscaled: + return VkFormat.R8G8B8Sscaled; + case VkFormat.B8G8R8A8Unorm: + return VkFormat.B8G8R8Unorm; + case VkFormat.B8G8R8A8Srgb: + return VkFormat.B8G8R8Srgb; + + case VkFormat.R16G16Sfloat: + return VkFormat.R16Sfloat; + case VkFormat.R16G16Unorm: + return VkFormat.R16Unorm; + case VkFormat.R16G16SNorm: + return VkFormat.R16SNorm; + case VkFormat.R16G16Uint: + return VkFormat.R16Uint; + case VkFormat.R16G16Sint: + return VkFormat.R16Sint; + case VkFormat.R16G16Uscaled: + return VkFormat.R16Uscaled; + case VkFormat.R16G16Sscaled: + return VkFormat.R16Sscaled; + + case VkFormat.R16G16B16Sfloat: + return VkFormat.R16G16Sfloat; + case VkFormat.R16G16B16Unorm: + return VkFormat.R16G16Unorm; + case VkFormat.R16G16B16SNorm: + return VkFormat.R16G16SNorm; + case VkFormat.R16G16B16Uint: + return VkFormat.R16G16Uint; + case VkFormat.R16G16B16Sint: + return VkFormat.R16G16Sint; + case VkFormat.R16G16B16Uscaled: + return VkFormat.R16G16Uscaled; + case VkFormat.R16G16B16Sscaled: + return VkFormat.R16G16Sscaled; + + case VkFormat.R16G16B16A16Sfloat: + return VkFormat.R16G16B16Sfloat; + case VkFormat.R16G16B16A16Unorm: + return VkFormat.R16G16B16Unorm; + case VkFormat.R16G16B16A16SNorm: + return VkFormat.R16G16B16SNorm; + case VkFormat.R16G16B16A16Uint: + return VkFormat.R16G16B16Uint; + case VkFormat.R16G16B16A16Sint: + return VkFormat.R16G16B16Sint; + case VkFormat.R16G16B16A16Uscaled: + return VkFormat.R16G16B16Uscaled; + case VkFormat.R16G16B16A16Sscaled: + return VkFormat.R16G16B16Sscaled; + + case VkFormat.R32G32Sfloat: + return VkFormat.R32Sfloat; + case VkFormat.R32G32Uint: + return VkFormat.R32Uint; + case VkFormat.R32G32Sint: + return VkFormat.R32Sint; + + case VkFormat.R32G32B32Sfloat: + return VkFormat.R32G32Sfloat; + case VkFormat.R32G32B32Uint: + return VkFormat.R32G32Uint; + case VkFormat.R32G32B32Sint: + return VkFormat.R32G32Sint; + + case VkFormat.R32G32B32A32Sfloat: + return VkFormat.R32G32B32Sfloat; + case VkFormat.R32G32B32A32Uint: + return VkFormat.R32G32B32Uint; + case VkFormat.R32G32B32A32Sint: + return VkFormat.R32G32B32Sint; + } + + return format; + } } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index dccc8ce68..a3d8dd6fb 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -1,4 +1,5 @@ -using Silk.NET.Vulkan; +using Ryujinx.Common.Memory; +using Silk.NET.Vulkan; using System; namespace Ryujinx.Graphics.Vulkan @@ -308,6 +309,8 @@ namespace Ryujinx.Graphics.Vulkan public PipelineLayout PipelineLayout; public SpecData SpecializationData; + private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2; + public void Initialize() { Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages); @@ -400,7 +403,15 @@ namespace Ryujinx.Graphics.Vulkan Pipeline pipelineHandle = default; + bool isMoltenVk = gd.IsMoltenVk; + + if (isMoltenVk) + { + UpdateVertexAttributeDescriptions(); + } + fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0]) + fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0]) fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0]) fixed (Viewport* pViewports = &Internal.Viewports[0]) fixed (Rect2D* pScissors = &Internal.Scissors[0]) @@ -410,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PipelineVertexInputStateCreateInfo, VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount, - PVertexAttributeDescriptions = pVertexAttributeDescriptions, + PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, VertexBindingDescriptionCount = VertexBindingDescriptionsCount, PVertexBindingDescriptions = pVertexBindingDescriptions }; @@ -612,6 +623,40 @@ namespace Ryujinx.Graphics.Vulkan } } + private void UpdateVertexAttributeDescriptions() + { + // 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]; + ref var vb = ref Internal.VertexBindingDescriptions[(int)attribute.Binding]; + + 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; + } + + attribute.Format = format; + _vertexAttributeDescriptions2[index] = attribute; + } + } + public void Dispose() { Stages.Dispose(); From 8f0c89ffd65eb2b979b24d457708218050fec6ae Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 25 May 2023 17:46:58 -0300 Subject: [PATCH 562/737] Generate scaling helper functions on IR (#4714) * Generate scaling helper functions on IR * Delete unused code * Split RewriteTextureSample and move gather bias add to an earlier pass * Remove using * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 29 +- .../HelperFunctions/TexelFetchScale_cp.glsl | 19 - .../HelperFunctions/TexelFetchScale_fp.glsl | 26 - .../HelperFunctions/TexelFetchScale_vp.glsl | 20 - .../Glsl/Instructions/InstGenHelper.cs | 1 + .../Glsl/Instructions/InstGenMemory.cs | 84 +--- .../CodeGen/Spirv/CodeGenContext.cs | 1 + .../CodeGen/Spirv/Instructions.cs | 54 +-- .../CodeGen/Spirv/ScalingHelpers.cs | 227 --------- .../CodeGen/Spirv/SpirvDelegates.cs | 2 + .../CodeGen/Spirv/SpirvGenerator.cs | 5 +- .../IntermediateRepresentation/Instruction.cs | 1 + .../Ryujinx.Graphics.Shader.csproj | 7 - .../StructuredIr/AstTextureOperation.cs | 5 - .../StructuredIr/InstructionInfo.cs | 1 + .../StructuredIr/StructuredProgram.cs | 4 +- .../Translation/EmitterContext.cs | 10 +- .../Translation/EmitterContextInsts.cs | 6 +- .../Translation/HelperFunctionManager.cs | 134 ++++++ .../Translation/HelperFunctionName.cs | 11 + .../Translation/Rewriter.cs | 445 +++++++++++++----- .../Translation/ShaderConfig.cs | 30 +- .../Translation/ShaderIdentifier.cs | 8 +- .../Translation/Translator.cs | 12 +- 25 files changed, 560 insertions(+), 584 deletions(-) delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 400f63f56..b8d74a463 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5031; + private const uint CodeGenVersion = 4714; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 8d805e32e..1bd0182b5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -239,33 +239,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - bool isFragment = context.Config.Stage == ShaderStage.Fragment; - - if (isFragment || context.Config.Stage == ShaderStage.Compute || context.Config.Stage == ShaderStage.Vertex) + if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryEarlyZForce()) { - if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce()) - { - context.AppendLine("layout(early_fragment_tests) in;"); - context.AppendLine(); - } - - if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0) - { - string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage); - - int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length; - - if (isFragment) - { - scaleElements++; // Also includes render target scale, for gl_FragCoord. - } - - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0) - { - AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl"); - context.AppendLine(); - } - } + context.AppendLine("layout(early_fragment_tests) in;"); + context.AppendLine(); } if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl deleted file mode 100644 index 08c625488..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl +++ /dev/null @@ -1,19 +0,0 @@ -ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) -{ - float scale = support_buffer.s_render_scale[1 + samplerIndex]; - if (scale == 1.0) - { - return inputVec; - } - return ivec2(vec2(inputVec) * scale); -} - -int Helper_TextureSizeUnscale(int size, int samplerIndex) -{ - float scale = support_buffer.s_render_scale[1 + samplerIndex]; - if (scale == 1.0) - { - return size; - } - return int(float(size) / scale); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl deleted file mode 100644 index 07a38a7a6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl +++ /dev/null @@ -1,26 +0,0 @@ -ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) -{ - float scale = support_buffer.s_render_scale[1 + samplerIndex]; - if (scale == 1.0) - { - return inputVec; - } - if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position. - { - return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, 0.0 - scale)); - } - else - { - return ivec2(vec2(inputVec) * scale); - } -} - -int Helper_TextureSizeUnscale(int size, int samplerIndex) -{ - float scale = abs(support_buffer.s_render_scale[1 + samplerIndex]); - if (scale == 1.0) - { - return size; - } - return int(float(size) / scale); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl deleted file mode 100644 index 72baa441d..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl +++ /dev/null @@ -1,20 +0,0 @@ -ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) -{ - float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]); - if (scale == 1.0) - { - return inputVec; - } - - return ivec2(vec2(inputVec) * scale); -} - -int Helper_TextureSizeUnscale(int size, int samplerIndex) -{ - float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]); - if (scale == 1.0) - { - return size; - } - return int(float(size) / scale); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 71e40fe7c..6cf36a2a6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -101,6 +101,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier"); Add(Instruction.Minimum, InstType.CallBinary, "min"); Add(Instruction.MinimumU32, InstType.CallBinary, "min"); + Add(Instruction.Modulo, InstType.CallBinary, "mod"); Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1); Add(Instruction.MultiplyHighS32, InstType.CallBinary, HelperFunctionNames.MultiplyHighS32); Add(Instruction.MultiplyHighU32, InstType.CallBinary, HelperFunctionNames.MultiplyHighU32); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index ef5260d10..dfc8197b6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -97,30 +97,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCallBuilder.Append(str); } - string ApplyScaling(string vector) - { - if (context.Config.Stage.SupportsRenderScale() && - texOp.Inst == Instruction.ImageLoad && - !isBindless && - !isIndexed) - { - // Image scales start after texture ones. - int scaleIndex = context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp); - - if (pCount == 3 && isArray) - { - // The array index is not scaled, just x and y. - vector = $"ivec3(Helper_TexelFetchScale(({vector}).xy, {scaleIndex}), ({vector}).z)"; - } - else if (pCount == 2 && !isArray) - { - vector = $"Helper_TexelFetchScale({vector}, {scaleIndex})"; - } - } - - return vector; - } - if (pCount > 1) { string[] elems = new string[pCount]; @@ -130,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions elems[index] = Src(AggregateType.S32); } - Append(ApplyScaling($"ivec{pCount}({string.Join(", ", elems)})")); + Append($"ivec{pCount}({string.Join(", ", elems)})"); } else { @@ -584,53 +560,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } - string ApplyScaling(string vector) - { - if (intCoords) - { - if (context.Config.Stage.SupportsRenderScale() && - !isBindless && - !isIndexed) - { - int index = context.Config.FindTextureDescriptorIndex(texOp); - - if (pCount == 3 && isArray) - { - // The array index is not scaled, just x and y. - vector = "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + index + "), (" + vector + ").z)"; - } - else if (pCount == 2 && !isArray) - { - vector = "Helper_TexelFetchScale(" + vector + ", " + index + ")"; - } - } - } - - return vector; - } - - string ApplyBias(string vector) - { - int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision(); - if (isGather && gatherBiasPrecision != 0) - { - // GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels. - // Offset by the gather precision divided by 2 to correct for rounding. - - if (pCount == 1) - { - vector = $"{vector} + (1.0 / (float(textureSize({samplerName}, 0)) * float({1 << (gatherBiasPrecision + 1)})))"; - } - else - { - vector = $"{vector} + (1.0 / (vec{pCount}(textureSize({samplerName}, 0).{"xyz".Substring(0, pCount)}) * float({1 << (gatherBiasPrecision + 1)})))"; - } - } - - return vector; - } - - Append(ApplyBias(ApplyScaling(AssemblePVector(pCount)))); + Append(AssemblePVector(pCount)); string AssembleDerivativesVector(int count) { @@ -750,7 +680,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else { - (TextureDescriptor descriptor, int descriptorIndex) = context.Config.FindTextureDescriptor(texOp); + TextureDescriptor descriptor = context.Config.FindTextureDescriptor(texOp); bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer; string texCall; @@ -767,14 +697,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}"; } - if (context.Config.Stage.SupportsRenderScale() && - (texOp.Index < 2 || (texOp.Type & SamplerType.Mask) == SamplerType.Texture3D) && - !isBindless && - !isIndexed) - { - texCall = $"Helper_TextureSizeUnscale({texCall}, {descriptorIndex})"; - } - return texCall; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 0ef89b398..7af6d316e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); public Instruction CoordTemp { get; set; } + public StructuredFunction CurrentFunction { get; set; } private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>(); private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>(); private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index fda0dc47c..eb64f8241 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Numerics; using static Spv.Specification; @@ -114,6 +113,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.MemoryBarrier, GenerateMemoryBarrier); Add(Instruction.Minimum, GenerateMinimum); Add(Instruction.MinimumU32, GenerateMinimumU32); + Add(Instruction.Modulo, GenerateModulo); Add(Instruction.Multiply, GenerateMultiply); Add(Instruction.MultiplyHighS32, GenerateMultiplyHighS32); Add(Instruction.MultiplyHighU32, GenerateMultiplyHighU32); @@ -744,8 +744,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv pCoords = Src(AggregateType.S32); } - pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords: true, isBindless, isIndexed, isArray, pCount); - (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; var image = context.Load(imageType, imageVariable); @@ -1040,6 +1038,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateBinaryU32(context, operation, context.Delegates.GlslUMin); } + private static OperationResult GenerateModulo(CodeGenContext context, AstOperation operation) + { + return GenerateBinary(context, operation, context.Delegates.FMod, null); + } + private static OperationResult GenerateMultiply(CodeGenContext context, AstOperation operation) { return GenerateBinary(context, operation, context.Delegates.FMul, context.Delegates.IMul); @@ -1101,7 +1104,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation) { - context.Return(); + if (operation.SourcesCount != 0) + { + context.ReturnValue(context.Get(context.CurrentFunction.ReturnType, operation.GetSource(0))); + } + else + { + context.Return(); + } + return OperationResult.Invalid; } @@ -1439,35 +1450,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - SpvInstruction ApplyBias(SpvInstruction vector, SpvInstruction image) - { - int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision(); - if (isGather && gatherBiasPrecision != 0) - { - // GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels. - // Offset by the gather precision divided by 2 to correct for rounding. - var sizeType = pCount == 1 ? context.TypeS32() : context.TypeVector(context.TypeS32(), pCount); - var pVectorType = pCount == 1 ? context.TypeFP32() : context.TypeVector(context.TypeFP32(), pCount); - - var bias = context.Constant(context.TypeFP32(), (float)(1 << (gatherBiasPrecision + 1))); - var biasVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(bias, pCount).ToArray()); - - var one = context.Constant(context.TypeFP32(), 1f); - var oneVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(one, pCount).ToArray()); - - var divisor = context.FMul( - pVectorType, - context.ConvertSToF(pVectorType, context.ImageQuerySize(sizeType, image)), - biasVector); - - vector = context.FAdd(pVectorType, vector, context.FDiv(pVectorType, oneVector, divisor)); - } - - return vector; - } - SpvInstruction pCoords = AssemblePVector(pCount); - pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount); SpvInstruction AssembleDerivativesVector(int count) { @@ -1638,8 +1621,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv image = context.Image(imageType, image); } - pCoords = ApplyBias(pCoords, image); - var operands = operandsList.ToArray(); SpvInstruction result; @@ -1755,11 +1736,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index); } - if (texOp.Index < 2 || (type & SamplerType.Mask) == SamplerType.Texture3D) - { - result = ScalingHelpers.ApplyUnscaling(context, texOp.WithType(type), result, isBindless, isIndexed); - } - return new OperationResult(AggregateType.S32, result); } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs deleted file mode 100644 index c8b21e881..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs +++ /dev/null @@ -1,227 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using static Spv.Specification; - -namespace Ryujinx.Graphics.Shader.CodeGen.Spirv -{ - using SpvInstruction = Spv.Generator.Instruction; - - static class ScalingHelpers - { - public static SpvInstruction ApplyScaling( - CodeGenContext context, - AstTextureOperation texOp, - SpvInstruction vector, - bool intCoords, - bool isBindless, - bool isIndexed, - bool isArray, - int pCount) - { - if (intCoords) - { - if (context.Config.Stage.SupportsRenderScale() && - !isBindless && - !isIndexed) - { - int index = texOp.Inst == Instruction.ImageLoad - ? context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp) - : context.Config.FindTextureDescriptorIndex(texOp); - - if (pCount == 3 && isArray) - { - return ApplyScaling2DArray(context, vector, index); - } - else if (pCount == 2 && !isArray) - { - return ApplyScaling2D(context, vector, index); - } - } - } - - return vector; - } - - private static SpvInstruction ApplyScaling2DArray(CodeGenContext context, SpvInstruction vector, int index) - { - // The array index is not scaled, just x and y. - var vectorXY = context.VectorShuffle(context.TypeVector(context.TypeS32(), 2), vector, vector, 0, 1); - var vectorZ = context.CompositeExtract(context.TypeS32(), vector, 2); - var vectorXYScaled = ApplyScaling2D(context, vectorXY, index); - var vectorScaled = context.CompositeConstruct(context.TypeVector(context.TypeS32(), 3), vectorXYScaled, vectorZ); - - return vectorScaled; - } - - private static SpvInstruction ApplyScaling2D(CodeGenContext context, SpvInstruction vector, int index) - { - var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); - var fieldIndex = context.Constant(context.TypeU32(), 4); - var scaleIndex = context.Constant(context.TypeU32(), index); - - if (context.Config.Stage == ShaderStage.Vertex) - { - var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32()); - var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3)); - var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer); - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount); - } - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1)); - - var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex); - var scale = context.Load(context.TypeFP32(), scaleElemPointer); - - var ivector2Type = context.TypeVector(context.TypeS32(), 2); - var localVector = context.CoordTemp; - - var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f)); - - var mergeLabel = context.Label(); - - if (context.Config.Stage == ShaderStage.Fragment) - { - var scaledInterpolatedLabel = context.Label(); - var scaledNoInterpolationLabel = context.Label(); - - var needsInterpolation = context.FOrdLessThan(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 0f)); - - context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone); - context.BranchConditional(needsInterpolation, scaledInterpolatedLabel, scaledNoInterpolationLabel); - - // scale < 0.0 - context.AddLabel(scaledInterpolatedLabel); - - ApplyScalingInterpolated(context, localVector, vector, scale); - context.Branch(mergeLabel); - - // scale >= 0.0 - context.AddLabel(scaledNoInterpolationLabel); - - ApplyScalingNoInterpolation(context, localVector, vector, scale); - context.Branch(mergeLabel); - - context.AddLabel(mergeLabel); - - var passthroughLabel = context.Label(); - var finalMergeLabel = context.Label(); - - context.SelectionMerge(finalMergeLabel, SelectionControlMask.MaskNone); - context.BranchConditional(passthrough, passthroughLabel, finalMergeLabel); - - context.AddLabel(passthroughLabel); - - context.Store(localVector, vector); - context.Branch(finalMergeLabel); - - context.AddLabel(finalMergeLabel); - - return context.Load(ivector2Type, localVector); - } - else - { - var passthroughLabel = context.Label(); - var scaledLabel = context.Label(); - - context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone); - context.BranchConditional(passthrough, passthroughLabel, scaledLabel); - - // scale == 1.0 - context.AddLabel(passthroughLabel); - - context.Store(localVector, vector); - context.Branch(mergeLabel); - - // scale != 1.0 - context.AddLabel(scaledLabel); - - ApplyScalingNoInterpolation(context, localVector, vector, scale); - context.Branch(mergeLabel); - - context.AddLabel(mergeLabel); - - return context.Load(ivector2Type, localVector); - } - } - - private static void ApplyScalingInterpolated(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale) - { - var vector2Type = context.TypeVector(context.TypeFP32(), 2); - - var scaleNegated = context.FNegate(context.TypeFP32(), scale); - var scaleVector = context.CompositeConstruct(vector2Type, scaleNegated, scaleNegated); - - var vectorFloat = context.ConvertSToF(vector2Type, vector); - var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated); - - var fragCoordPointer = context.Inputs[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)]; - var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer); - var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1); - - var scaleMod = context.FMod(vector2Type, fragCoordXY, scaleVector); - var vectorInterpolated = context.FAdd(vector2Type, vectorScaled, scaleMod); - - context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorInterpolated)); - } - - private static void ApplyScalingNoInterpolation(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale) - { - if (context.Config.Stage == ShaderStage.Vertex) - { - scale = context.GlslFAbs(context.TypeFP32(), scale); - } - - var vector2Type = context.TypeVector(context.TypeFP32(), 2); - - var vectorFloat = context.ConvertSToF(vector2Type, vector); - var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scale); - - context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorScaled)); - } - - public static SpvInstruction ApplyUnscaling( - CodeGenContext context, - AstTextureOperation texOp, - SpvInstruction size, - bool isBindless, - bool isIndexed) - { - if (context.Config.Stage.SupportsRenderScale() && - !isBindless && - !isIndexed) - { - int index = context.Config.FindTextureDescriptorIndex(texOp); - - var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32()); - var fieldIndex = context.Constant(context.TypeU32(), 4); - var scaleIndex = context.Constant(context.TypeU32(), index); - - if (context.Config.Stage == ShaderStage.Vertex) - { - var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32()); - var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3)); - var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer); - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount); - } - - scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1)); - - var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex); - var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer)); - - var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f)); - - var sizeFloat = context.ConvertSToF(context.TypeFP32(), size); - var sizeUnscaled = context.FDiv(context.TypeFP32(), sizeFloat, scale); - var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled); - - return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt); - } - - return size; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs index 3ccfd7f55..0fa954e15 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs @@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public readonly FuncBinaryInstruction GlslSMax; public readonly FuncBinaryInstruction GlslFMin; public readonly FuncBinaryInstruction GlslSMin; + public readonly FuncBinaryInstruction FMod; public readonly FuncBinaryInstruction FMul; public readonly FuncBinaryInstruction IMul; public readonly FuncBinaryInstruction FSub; @@ -174,6 +175,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv GlslSMax = context.GlslSMax; GlslFMin = context.GlslFMin; GlslSMin = context.GlslSMin; + FMod = context.FMod; FMul = context.FMul; IMul = context.IMul; FSub = context.FSub; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 3e11a9749..a55e09fd3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -144,10 +144,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex) { - var function = info.Functions[funcIndex]; - - (_, var spvFunc) = context.GetFunction(funcIndex); + (var function, var spvFunc) = context.GetFunction(funcIndex); + context.CurrentFunction = function; context.AddFunction(spvFunc); context.StartFunction(); diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 817755bbe..f7afe5071 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation MemoryBarrier, Minimum, MinimumU32, + Modulo, Multiply, MultiplyHighS32, MultiplyHighU32, diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 3434e2a81..2efcbca4f 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -4,10 +4,6 @@ <TargetFramework>net7.0</TargetFramework> </PropertyGroup> - <ItemGroup> - <None Remove="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" /> - </ItemGroup> - <ItemGroup> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> <ProjectReference Include="..\Spv.Generator\Spv.Generator.csproj" /> @@ -25,9 +21,6 @@ <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" /> - <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" /> - <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_fp.glsl" /> - <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_cp.glsl" /> </ItemGroup> </Project> diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index a44f13cc0..6c27279c9 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -27,10 +27,5 @@ namespace Ryujinx.Graphics.Shader.StructuredIr CbufSlot = cbufSlot; Handle = handle; } - - public AstTextureOperation WithType(SamplerType type) - { - return new AstTextureOperation(Inst, type, Format, Flags, CbufSlot, Handle, Index); - } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index ab8132540..44f0fad95 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -104,6 +104,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); + Add(Instruction.Modulo, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32); Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32); diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 939a52f3e..6846245e6 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { static class StructuredProgram { - public static StructuredProgramInfo MakeStructuredProgram(Function[] functions, ShaderConfig config) + public static StructuredProgramInfo MakeStructuredProgram(IReadOnlyList<Function> functions, ShaderConfig config) { StructuredProgramContext context = new StructuredProgramContext(config); - for (int funcIndex = 0; funcIndex < functions.Length; funcIndex++) + for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { Function function = functions[funcIndex]; diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 0c51b16f4..2786caaa0 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -49,13 +49,17 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly List<Operation> _operations; private readonly Dictionary<ulong, BlockLabel> _labels; - public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) + public EmitterContext() + { + _operations = new List<Operation>(); + _labels = new Dictionary<ulong, BlockLabel>(); + } + + public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) : this() { Program = program; Config = config; IsNonMain = isNonMain; - _operations = new List<Operation>(); - _labels = new Dictionary<ulong, BlockLabel>(); EmitStart(); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index e41a28f12..6d4104cee 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -307,6 +307,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(fpType | Instruction.Minimum, Local(), a, b); } + public static Operand FPModulo(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) + { + return context.Add(fpType | Instruction.Modulo, Local(), a, b); + } + public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) { return context.Add(fpType | Instruction.Multiply, Local(), a, b); @@ -656,7 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation public static void Return(this EmitterContext context, Operand returnValue) { - context.PrepareForReturn(); context.Add(Instruction.Return, null, returnValue); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs new file mode 100644 index 000000000..206facd46 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs @@ -0,0 +1,134 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + class HelperFunctionManager + { + private readonly List<Function> _functionList; + private readonly Dictionary<HelperFunctionName, int> _functionIds; + private readonly ShaderStage _stage; + + public HelperFunctionManager(List<Function> functionList, ShaderStage stage) + { + _functionList = functionList; + _functionIds = new Dictionary<HelperFunctionName, int>(); + _stage = stage; + } + + public int GetOrCreateFunctionId(HelperFunctionName functionName) + { + if (_functionIds.TryGetValue(functionName, out int functionId)) + { + return functionId; + } + + Function function = GenerateFunction(functionName); + functionId = _functionList.Count; + _functionList.Add(function); + _functionIds.Add(functionName, functionId); + + return functionId; + } + + private Function GenerateFunction(HelperFunctionName functionName) + { + return functionName switch + { + HelperFunctionName.TexelFetchScale => GenerateTexelFetchScaleFunction(), + HelperFunctionName.TextureSizeUnscale => GenerateTextureSizeUnscaleFunction(), + _ => throw new ArgumentException($"Invalid function name {functionName}") + }; + } + + private Function GenerateTexelFetchScaleFunction() + { + EmitterContext context = new EmitterContext(); + + Operand input = Argument(0); + Operand samplerIndex = Argument(1); + Operand index = GetScaleIndex(context, samplerIndex); + + Operand scale = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index); + + Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f)); + Operand lblScaleNotOne = Label(); + + context.BranchIfFalse(lblScaleNotOne, scaleIsOne); + context.Return(input); + context.MarkLabel(lblScaleNotOne); + + int inArgumentsCount; + + if (_stage == ShaderStage.Fragment) + { + Operand scaleIsLessThanZero = context.FPCompareLess(scale, ConstF(0f)); + Operand lblScaleGreaterOrEqualZero = Label(); + + context.BranchIfFalse(lblScaleGreaterOrEqualZero, scaleIsLessThanZero); + + Operand negScale = context.FPNegate(scale); + Operand inputScaled = context.FPMultiply(context.IConvertS32ToFP32(input), negScale); + Operand fragCoordX = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0)); + Operand fragCoordY = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1)); + Operand fragCoord = context.ConditionalSelect(Argument(2), fragCoordY, fragCoordX); + Operand inputBias = context.FPModulo(fragCoord, negScale); + Operand inputWithBias = context.FPAdd(inputScaled, inputBias); + + context.Return(context.FP32ConvertToS32(inputWithBias)); + context.MarkLabel(lblScaleGreaterOrEqualZero); + + inArgumentsCount = 3; + } + else + { + inArgumentsCount = 2; + } + + Operand inputScaled2 = context.FPMultiply(context.IConvertS32ToFP32(input), scale); + + context.Return(context.FP32ConvertToS32(inputScaled2)); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TexelFetchScale", true, inArgumentsCount, 0); + } + + private Function GenerateTextureSizeUnscaleFunction() + { + EmitterContext context = new EmitterContext(); + + Operand input = Argument(0); + Operand samplerIndex = Argument(1); + Operand index = GetScaleIndex(context, samplerIndex); + + Operand scale = context.FPAbsolute(context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index)); + + Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f)); + Operand lblScaleNotOne = Label(); + + context.BranchIfFalse(lblScaleNotOne, scaleIsOne); + context.Return(input); + context.MarkLabel(lblScaleNotOne); + + Operand inputUnscaled = context.FPDivide(context.IConvertS32ToFP32(input), scale); + + context.Return(context.FP32ConvertToS32(inputUnscaled)); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TextureSizeUnscale", true, 2, 0); + } + + private Operand GetScaleIndex(EmitterContext context, Operand index) + { + switch (_stage) + { + case ShaderStage.Vertex: + Operand fragScaleCount = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.FragmentRenderScaleCount)); + return context.IAdd(Const(1), context.IAdd(index, fragScaleCount)); + default: + return context.IAdd(Const(1), index); + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs new file mode 100644 index 000000000..5accdf65f --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation +{ + enum HelperFunctionName + { + TexelFetchScale, + TextureSizeUnscale + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 711661c9b..6b55a484b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation { static class Rewriter { - public static void RunPass(BasicBlock[] blocks, ShaderConfig config) + public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { bool isVertexShader = config.Stage == ShaderStage.Vertex; bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); @@ -54,9 +54,14 @@ namespace Ryujinx.Graphics.Shader.Translation if (operation is TextureOperation texOp) { + node = InsertTexelFetchScale(hfm, node, config); + node = InsertTextureSizeUnscale(hfm, node, config); + if (texOp.Inst == Instruction.TextureSample) { - node = RewriteTextureSample(node, config); + node = InsertCoordNormalization(node, config); + node = InsertCoordGatherBias(node, config); + node = InsertConstOffsets(node, config); if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat) { @@ -344,10 +349,279 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static LinkedListNode<INode> RewriteTextureSample(LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) { TextureOperation texOp = (TextureOperation)node.Value; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + bool isImage = IsImageInstructionWithScale(texOp.Inst); + + if ((texOp.Inst == Instruction.TextureSample || isImage) && + intCoords && + !isBindless && + !isIndexed && + config.Stage.SupportsRenderScale() && + TypeSupportsScale(texOp.Type)) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); + int samplerIndex = isImage + ? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp) + : config.FindTextureDescriptorIndex(texOp); + + for (int index = 0; index < coordsCount; index++) + { + Operand scaledCoord = Local(); + Operand[] callArgs; + + if (config.Stage == ShaderStage.Fragment) + { + callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex), Const(index) }; + } + else + { + callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex) }; + } + + node.List.AddBefore(node, new Operation(Instruction.Call, 0, scaledCoord, callArgs)); + + texOp.SetSource(coordsIndex + index, scaledCoord); + } + } + + return node; + } + + private static LinkedListNode<INode> InsertTextureSizeUnscale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) + { + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + if (texOp.Inst == Instruction.TextureSize && + texOp.Index < 2 && + !isBindless && + !isIndexed && + config.Stage.SupportsRenderScale() && + TypeSupportsScale(texOp.Type)) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale); + int samplerIndex = config.FindTextureDescriptorIndex(texOp, ignoreType: true); + + for (int index = texOp.DestsCount - 1; index >= 0; index--) + { + Operand dest = texOp.GetDest(index); + + Operand unscaledSize = Local(); + + // Replace all uses with the unscaled size value. + // This must be done before the call is added, since it also is a use of the original size. + foreach (INode useOp in dest.UseOps) + { + for (int srcIndex = 0; srcIndex < useOp.SourcesCount; srcIndex++) + { + if (useOp.GetSource(srcIndex) == dest) + { + useOp.SetSource(srcIndex, unscaledSize); + } + } + } + + Operand[] callArgs = new Operand[] { Const(functionId), dest, Const(samplerIndex) }; + + node.List.AddAfter(node, new Operation(Instruction.Call, 0, unscaledSize, callArgs)); + } + } + + return node; + } + + private static bool IsImageInstructionWithScale(Instruction inst) + { + // Currently, we don't support scaling images that are modified, + // so we only need to care about the load instruction. + return inst == Instruction.ImageLoad; + } + + private static bool TypeSupportsScale(SamplerType type) + { + return (type & SamplerType.Mask) == SamplerType.Texture2D; + } + + private static LinkedListNode<INode> InsertCoordNormalization(LinkedListNode<INode> node, ShaderConfig config) + { + // Emulate non-normalized coordinates by normalizing the coordinates on the shader. + // Without normalization, the coordinates are expected to the in the [0, W or H] range, + // and otherwise, it is expected to be in the [0, 1] range. + // We normalize by dividing the coords by the texture size. + + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); + + if (isCoordNormalized || intCoords) + { + return node; + } + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; + + for (int index = 0; index < normCoordsCount; index++) + { + Operand coordSize = Local(); + + Operand[] texSizeSources; + + if (isBindless || isIndexed) + { + texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) }; + } + else + { + texSizeSources = new Operand[] { Const(0) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + index, + new[] { coordSize }, + texSizeSources)); + + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + + Operand source = texOp.GetSource(coordsIndex + index); + + Operand coordNormalized = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize))); + + texOp.SetSource(coordsIndex + index, coordNormalized); + } + + return node; + } + + private static LinkedListNode<INode> InsertCoordGatherBias(LinkedListNode<INode> node, ShaderConfig config) + { + // The gather behavior when the coordinate sits right in the middle of two texels is not well defined. + // To ensure the correct texel is sampled, we add a small bias value to the coordinate. + // This value is calculated as the minimum value required to change the texel it will sample from, + // and is 0 if the host does not require the bias. + + TextureOperation texOp = (TextureOperation)node.Value; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + + int gatherBiasPrecision = config.GpuAccessor.QueryHostGatherBiasPrecision(); + + if (!isGather || gatherBiasPrecision == 0) + { + return node; + } + + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + int coordsCount = texOp.Type.GetDimensions(); + int coordsIndex = isBindless || isIndexed ? 1 : 0; + + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; + + for (int index = 0; index < normCoordsCount; index++) + { + Operand coordSize = Local(); + Operand scaledSize = Local(); + Operand bias = Local(); + + Operand[] texSizeSources; + + if (isBindless || isIndexed) + { + texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) }; + } + else + { + texSizeSources = new Operand[] { Const(0) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.CbufSlot, + texOp.Handle, + index, + new[] { coordSize }, + texSizeSources)); + + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Multiply, + scaledSize, + GenerateI2f(node, coordSize), + ConstF((float)(1 << (gatherBiasPrecision + 1))))); + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, bias, ConstF(1f), scaledSize)); + + Operand source = texOp.GetSource(coordsIndex + index); + + Operand coordBiased = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordBiased, source, bias)); + + texOp.SetSource(coordsIndex + index, coordBiased); + } + + return node; + } + + private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ShaderConfig config) + { + // Non-constant texture offsets are not allowed (according to the spec), + // however some GPUs does support that. + // For GPUs where it is not supported, we can replace the instruction with the following: + // For texture*Offset, we replace it by texture*, and add the offset to the P coords. + // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). + // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. + // For textureGatherOffset, we split the operation into up to 4 operations, one for each component + // that is accessed, where each textureGather operation has a different offset for each pixel. + + TextureOperation texOp = (TextureOperation)node.Value; + bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; @@ -355,9 +629,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); - - if (!hasInvalidOffset && isCoordNormalized) + if (!hasInvalidOffset) { return node; } @@ -454,7 +726,7 @@ namespace Ryujinx.Graphics.Shader.Translation hasInvalidOffset &= !areAllOffsetsConstant; - if (!hasInvalidOffset && isCoordNormalized) + if (!hasInvalidOffset) { return node; } @@ -473,63 +745,6 @@ namespace Ryujinx.Graphics.Shader.Translation int componentIndex = texOp.Index; - Operand Float(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value)); - - return res; - } - - // Emulate non-normalized coordinates by normalizing the coordinates on the shader. - // Without normalization, the coordinates are expected to the in the [0, W or H] range, - // and otherwise, it is expected to be in the [0, 1] range. - // We normalize by dividing the coords by the texture size. - if (!isCoordNormalized && !intCoords) - { - config.SetUsedFeature(FeatureFlags.IntegerSampling); - - int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; - - for (int index = 0; index < normCoordsCount; index++) - { - Operand coordSize = Local(); - - Operand[] texSizeSources; - - if (isBindless || isIndexed) - { - texSizeSources = new Operand[] { sources[0], Const(0) }; - } - else - { - texSizeSources = new Operand[] { Const(0) }; - } - - node.List.AddBefore(node, new TextureOperation( - Instruction.TextureSize, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.CbufSlot, - texOp.Handle, - index, - new[] { coordSize }, - texSizeSources)); - - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - - Operand source = sources[coordsIndex + index]; - - Operand coordNormalized = Local(); - - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, Float(coordSize))); - - sources[coordsIndex + index] = coordNormalized; - } - } - Operand[] dests = new Operand[texOp.DestsCount]; for (int i = 0; i < texOp.DestsCount; i++) @@ -541,15 +756,7 @@ namespace Ryujinx.Graphics.Shader.Translation LinkedListNode<INode> oldNode = node; - // Technically, non-constant texture offsets are not allowed (according to the spec), - // however some GPUs does support that. - // For GPUs where it is not supported, we can replace the instruction with the following: - // For texture*Offset, we replace it by texture*, and add the offset to the P coords. - // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). - // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. - // For textureGatherOffset, we split the operation into up to 4 operations, one for each component - // that is accessed, where each textureGather operation has a different offset for each pixel. - if (hasInvalidOffset && isGather && !isShadow) + if (isGather && !isShadow) { config.SetUsedFeature(FeatureFlags.IntegerSampling); @@ -557,7 +764,7 @@ namespace Ryujinx.Graphics.Shader.Translation sources.CopyTo(newSources, 0); - Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); int destIndex = 0; @@ -576,7 +783,11 @@ namespace Ryujinx.Graphics.Shader.Translation Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Divide, + offset, + GenerateI2f(node, intOffset), + GenerateI2f(node, texSizes[index]))); Operand source = sources[coordsIndex + index]; @@ -603,45 +814,46 @@ namespace Ryujinx.Graphics.Shader.Translation } else { - if (hasInvalidOffset) + if (intCoords) { - if (intCoords) + for (int index = 0; index < coordsCount; index++) { - for (int index = 0; index < coordsCount; index++) - { - Operand source = sources[coordsIndex + index]; + Operand source = sources[coordsIndex + index]; - Operand coordPlusOffset = Local(); + Operand coordPlusOffset = Local(); - node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); + node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); - sources[coordsIndex + index] = coordPlusOffset; - } + sources[coordsIndex + index] = coordPlusOffset; } - else + } + else + { + config.SetUsedFeature(FeatureFlags.IntegerSampling); + + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); + + for (int index = 0; index < coordsCount; index++) { - config.SetUsedFeature(FeatureFlags.IntegerSampling); + config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand offset = Local(); - for (int index = 0; index < coordsCount; index++) - { - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + Operand intOffset = offsets[index]; - Operand offset = Local(); + node.List.AddBefore(node, new Operation( + Instruction.FP32 | Instruction.Divide, + offset, + GenerateI2f(node, intOffset), + GenerateI2f(node, texSizes[index]))); - Operand intOffset = offsets[index]; + Operand source = sources[coordsIndex + index]; - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index]))); + Operand coordPlusOffset = Local(); - Operand source = sources[coordsIndex + index]; + node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); - Operand coordPlusOffset = Local(); - - node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset)); - - sources[coordsIndex + index] = coordPlusOffset; - } + sources[coordsIndex + index] = coordPlusOffset; } } @@ -669,22 +881,13 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static Operand[] InsertTextureSize( + private static Operand[] InsertTextureLod( LinkedListNode<INode> node, TextureOperation texOp, Operand[] lodSources, Operand bindlessHandle, int coordsCount) { - Operand Int(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); - - return res; - } - Operand[] texSizes = new Operand[coordsCount]; Operand lod = Local(); @@ -708,11 +911,11 @@ namespace Ryujinx.Graphics.Shader.Translation if (bindlessHandle != null) { - texSizeSources = new Operand[] { bindlessHandle, Int(lod) }; + texSizeSources = new Operand[] { bindlessHandle, GenerateF2i(node, lod) }; } else { - texSizeSources = new Operand[] { Int(lod) }; + texSizeSources = new Operand[] { GenerateF2i(node, lod) }; } node.List.AddBefore(node, new TextureOperation( @@ -796,6 +999,24 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } + private static Operand GenerateI2f(LinkedListNode<INode> node, Operand value) + { + Operand res = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value)); + + return res; + } + + private static Operand GenerateF2i(LinkedListNode<INode> node, Operand value) + { + Operand res = Local(); + + node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value)); + + return res; + } + private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation) { Operand GenerateLoad(IoVariable ioVariable) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 775607972..73525cb27 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -860,7 +860,7 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors; } - public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp) + public TextureDescriptor FindTextureDescriptor(AstTextureOperation texOp) { TextureDescriptor[] descriptors = GetTextureDescriptors(); @@ -872,11 +872,11 @@ namespace Ryujinx.Graphics.Shader.Translation descriptor.HandleIndex == texOp.Handle && descriptor.Format == texOp.Format) { - return (descriptor, i); + return descriptor; } } - return (default, -1); + return default; } private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp) @@ -897,12 +897,30 @@ namespace Ryujinx.Graphics.Shader.Translation return -1; } - public int FindTextureDescriptorIndex(AstTextureOperation texOp) + private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false) { - return FindDescriptorIndex(GetTextureDescriptors(), texOp); + for (int i = 0; i < array.Length; i++) + { + var descriptor = array[i]; + + if ((descriptor.Type == texOp.Type || ignoreType) && + descriptor.CbufSlot == texOp.CbufSlot && + descriptor.HandleIndex == texOp.Handle && + descriptor.Format == texOp.Format) + { + return i; + } + } + + return -1; } - public int FindImageDescriptorIndex(AstTextureOperation texOp) + public int FindTextureDescriptorIndex(TextureOperation texOp, bool ignoreType = false) + { + return FindDescriptorIndex(GetTextureDescriptors(), texOp, ignoreType); + } + + public int FindImageDescriptorIndex(TextureOperation texOp) { return FindDescriptorIndex(GetImageDescriptors(), texOp); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs index 53f1e8475..867e24379 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -1,11 +1,11 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; +using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.Translation { static class ShaderIdentifier { - public static ShaderIdentification Identify(Function[] functions, ShaderConfig config) + public static ShaderIdentification Identify(IReadOnlyList<Function> functions, ShaderConfig config) { if (config.Stage == ShaderStage.Geometry && config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles && @@ -20,12 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation return ShaderIdentification.None; } - private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr) + private static bool IsLayerPassthroughGeometryShader(IReadOnlyList<Function> functions, out int layerInputAttr) { bool writesLayer = false; layerInputAttr = 0; - if (functions.Length != 1) + if (functions.Count != 1) { return false; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 87d97e52e..5bbc00097 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation.Optimizations; using System; +using System.Collections.Generic; using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -44,7 +45,14 @@ namespace Ryujinx.Graphics.Shader.Translation } } - Function[] funcs = new Function[functions.Length]; + List<Function> funcs = new List<Function>(functions.Length); + + for (int i = 0; i < functions.Length; i++) + { + funcs.Add(null); + } + + HelperFunctionManager hfm = new HelperFunctionManager(funcs, config.Stage); for (int i = 0; i < functions.Length; i++) { @@ -71,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation Ssa.Rename(cfg.Blocks); Optimizer.RunPass(cfg.Blocks, config); - Rewriter.RunPass(cfg.Blocks, config); + Rewriter.RunPass(hfm, cfg.Blocks, config); } funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); From 5b42a4d2c4703c826dc9c1b1dc54e5336dc6883d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 25 May 2023 23:41:03 +0200 Subject: [PATCH 563/737] Fix mod names (#5088) --- src/Ryujinx.HLE/HOS/ModLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index bde4d11ce..ac983cfe5 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -167,12 +167,12 @@ namespace Ryujinx.HLE.HOS if (StrEquals(RomfsDir, modDir.Name)) { - mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} RomFs>", modDir)); + mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir)); types.Append('R'); } else if (StrEquals(ExefsDir, modDir.Name)) { - mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} ExeFs>", modDir)); + mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir)); types.Append('E'); } else if (StrEquals(CheatDir, modDir.Name)) From e6658c133ca8bef4a6612d6feabfc6180f0c8318 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 25 May 2023 23:42:49 -0300 Subject: [PATCH 564/737] Fix resolution scaling of image operation coordinates (#5102) * Fix resolution scaling of image operation coordinates * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b8d74a463..f4d7f3b58 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4714; + private const uint CodeGenVersion = 5102; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 6b55a484b..eab0f9d2f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -366,7 +366,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool isImage = IsImageInstructionWithScale(texOp.Inst); if ((texOp.Inst == Instruction.TextureSample || isImage) && - intCoords && + (intCoords || isImage) && !isBindless && !isIndexed && config.Stage.SupportsRenderScale() && From 3b375525fbaded422fb2f9a9984a5770a3779fcb Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 26 May 2023 15:19:37 -0300 Subject: [PATCH 565/737] Force reciprocal operation with value biased by constant to be precise on macOS (#5110) * Force operations to be precise in some cases on SPIR-V * Make it a bit more strict, add comments * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Spirv/Instructions.cs | 8 +++--- .../IntermediateRepresentation/Operation.cs | 2 ++ .../StructuredIr/AstOperation.cs | 14 +++++++--- .../StructuredIr/AstTextureOperation.cs | 2 +- .../StructuredIr/StructuredProgram.cs | 26 ++++++++++++++++--- .../StructuredIr/StructuredProgramContext.cs | 2 +- .../Translation/Rewriter.cs | 25 ++++++++++++++++++ 8 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index f4d7f3b58..e2346bd0b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5102; + private const uint CodeGenVersion = 5110; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index eb64f8241..f088a47f3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -2245,7 +2245,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2256,7 +2256,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2316,7 +2316,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2327,7 +2327,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index 99179f151..d502a9b65 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public Instruction Inst { get; private set; } public StorageKind StorageKind { get; } + public bool ForcePrecise { get; set; } + private Operand[] _dests; public Operand Dest diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs index 2393fd8d8..4cf729d09 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs @@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { public Instruction Inst { get; } public StorageKind StorageKind { get; } + public bool ForcePrecise { get; } public int Index { get; } @@ -17,10 +18,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public int SourcesCount => _sources.Length; - public AstOperation(Instruction inst, StorageKind storageKind, IAstNode[] sources, int sourcesCount) + public AstOperation(Instruction inst, StorageKind storageKind, bool forcePrecise, IAstNode[] sources, int sourcesCount) { Inst = inst; StorageKind = storageKind; + ForcePrecise = forcePrecise; _sources = sources; for (int index = 0; index < sources.Length; index++) @@ -38,12 +40,18 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Index = 0; } - public AstOperation(Instruction inst, StorageKind storageKind, int index, IAstNode[] sources, int sourcesCount) : this(inst, storageKind, sources, sourcesCount) + public AstOperation( + Instruction inst, + StorageKind storageKind, + bool forcePrecise, + int index, + IAstNode[] sources, + int sourcesCount) : this(inst, storageKind, forcePrecise, sources, sourcesCount) { Index = index; } - public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, sources, sources.Length) + public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, false, sources, sources.Length) { } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 6c27279c9..a4e097eb6 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr int cbufSlot, int handle, int index, - params IAstNode[] sources) : base(inst, StorageKind.None, index, sources, sources.Length) + params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) { Type = type; Format = format; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 6846245e6..4405c07aa 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -156,7 +156,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount); + source = new AstOperation( + inst, + operation.StorageKind, + operation.ForcePrecise, + operation.Index, + sources, + operation.SourcesCount); } AggregateType destElemType = destType; @@ -179,7 +185,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr dest.VarType = destElemType; - context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, new[] { destVec, index }, 2))); + context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, false, new[] { destVec, index }, 2))); } } else if (operation.Dest != null) @@ -227,7 +233,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else if (!isCopy) { - source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount); + source = new AstOperation( + inst, + operation.StorageKind, + operation.ForcePrecise, + operation.Index, + sources, + operation.SourcesCount); } else { @@ -248,7 +260,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - context.AddNode(new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount)); + context.AddNode(new AstOperation( + inst, + operation.StorageKind, + operation.ForcePrecise, + operation.Index, + sources, + operation.SourcesCount)); } // Those instructions needs to be emulated by using helper functions, diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index c5ad3683a..a4d079914 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -319,7 +319,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr new AstOperand(OperandType.Constant, elemIndex) }; - return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, sources, sources.Length); + return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, false, sources, sources.Length); } return GetOperand(operand); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index eab0f9d2f..866ae5223 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { bool isVertexShader = config.Stage == ShaderStage.Vertex; + bool isImpreciseFragmentShader = config.Stage == ShaderStage.Fragment && config.GpuAccessor.QueryHostReducedPrecision(); bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug(); bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat(); @@ -45,6 +46,11 @@ namespace Ryujinx.Graphics.Shader.Translation } } + if (isImpreciseFragmentShader) + { + EnableForcePreciseIfNeeded(operation); + } + if (hasVectorIndexingBug) { InsertVectorComponentSelect(node, config); @@ -81,6 +87,25 @@ namespace Ryujinx.Graphics.Shader.Translation } } + private static void EnableForcePreciseIfNeeded(Operation operation) + { + // There are some cases where a small bias is added to values to prevent division by zero. + // When operating with reduced precision, it is possible for this bias to get rounded to 0 + // and cause a division by zero. + // To prevent that, we force those operations to be precise even if the host wants + // imprecise operations for performance. + + if (operation.Inst == (Instruction.FP32 | Instruction.Divide) && + operation.GetSource(0).Type == OperandType.Constant && + operation.GetSource(0).AsFloat() == 1f && + operation.GetSource(1).AsgOp is Operation addOp && + addOp.Inst == (Instruction.FP32 | Instruction.Add) && + addOp.GetSource(1).Type == OperandType.Constant) + { + addOp.ForcePrecise = true; + } + } + private static void InsertVectorComponentSelect(LinkedListNode<INode> node, ShaderConfig config) { Operation operation = (Operation)node.Value; From 42b9c1e8fede88880454154f8c3683f1f8424ed9 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Fri, 26 May 2023 17:57:43 -0400 Subject: [PATCH 566/737] Ryujinx.Ava: fixes for random hangs on exit (#4827) * Attempt at fixing hang on exit by ending the WindowNotificationManager notification loop, so that the Thread running it can exit. * explicitly apply the NotificationManager template to allow the notification loop to begin * NotificationHelper - remove explicity call to ApplyTemplate(). Change to ManualResetEventSlim so we can cancel the Wait on it. * add a timeout to AudioRenderSystem.Stop()'s waiting for the termination signal, log a warning if this timeout occurs, and continue execution * NotifiationHelper - cancel first, the CompleteAdding() * Remove AudioRenderSystem._terminationEvent, redundant * NotificationHelper - use host.Closing event to trigger cancellation instead of _notifationManager.DetachedFromLogicalTree * Change NotificationHelper to use an explicit Thread for background work. Wait on the cancellationToken's WaitHandle so the Thread doesn't have to deal with async. Wrap foreach in try/catch (OperationCanceledException) to swallow the escaping exception from the GetConsumingEnumerable(). * adjust formatting of AsyncWorkQueue constructor to use object initializers consistently * use AsyncWorkQueue to do everything I added in SetNotificationManager() * Revert "use AsyncWorkQueue to do everything I added in SetNotificationManager()" This reverts commit f0e78366b8776ec8e2fef8ab023c0db1833155d3. * use AsyncWorkQueue to handle the Thread-related changes previously made to NotificationHelper.SetNotificationHelper(). Wrap it in Lazy<T> and force instantiation in the TemplateApplied event handler to accomodate for the fact that AsyncWorkQueue starts immediately, and the notification dispatch loop was being delayed by _templateAppliedEvent. * impl changes suggested by AcK77 * impl changes suggested by AcK77 (more) --- .../Renderer/Server/AudioRenderSystem.cs | 14 -------- .../UI/Helpers/NotificationHelper.cs | 35 +++++++++++-------- src/Ryujinx.Common/AsyncWorkQueue.cs | 8 +++-- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 53570243d..1ad7b8590 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -31,7 +31,6 @@ namespace Ryujinx.Audio.Renderer.Server private AudioRendererRenderingDevice _renderingDevice; private AudioRendererExecutionMode _executionMode; private IWritableEvent _systemEvent; - private ManualResetEvent _terminationEvent; private MemoryPoolState _dspMemoryPoolState; private VoiceContext _voiceContext; private MixContext _mixContext; @@ -83,7 +82,6 @@ namespace Ryujinx.Audio.Renderer.Server public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent) { _manager = manager; - _terminationEvent = new ManualResetEvent(false); _dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); _voiceContext = new VoiceContext(); _mixContext = new MixContext(); @@ -387,11 +385,6 @@ namespace Ryujinx.Audio.Renderer.Server _isActive = false; } - if (_executionMode == AudioRendererExecutionMode.Auto) - { - _terminationEvent.WaitOne(); - } - Logger.Info?.Print(LogClass.AudioRenderer, $"Stopped renderer id {_sessionId}"); } @@ -668,8 +661,6 @@ namespace Ryujinx.Audio.Renderer.Server { if (_isActive) { - _terminationEvent.Reset(); - if (!_manager.Processor.HasRemainingCommands(_sessionId)) { GenerateCommandList(out CommandList commands); @@ -686,10 +677,6 @@ namespace Ryujinx.Audio.Renderer.Server _isDspRunningBehind = true; } } - else - { - _terminationEvent.Set(); - } } } @@ -857,7 +844,6 @@ namespace Ryujinx.Audio.Renderer.Server } _manager.Unregister(this); - _terminationEvent.Dispose(); _workBufferMemoryPin.Dispose(); if (MemoryManager is IRefCounted rc) diff --git a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs index 7e2afb8bd..f207c5fb0 100644 --- a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs @@ -3,21 +3,20 @@ using Avalonia.Controls; using Avalonia.Controls.Notifications; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Common; using System; using System.Collections.Concurrent; using System.Threading; -using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Helpers { public static class NotificationHelper { - private const int MaxNotifications = 4; + private const int MaxNotifications = 4; private const int NotificationDelayInMs = 5000; private static WindowNotificationManager _notificationManager; - private static readonly ManualResetEvent _templateAppliedEvent = new(false); private static readonly BlockingCollection<Notification> _notifications = new(); public static void SetNotificationManager(Window host) @@ -29,25 +28,31 @@ namespace Ryujinx.Ava.UI.Helpers Margin = new Thickness(0, 0, 15, 40) }; + var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>( + () => new AsyncWorkQueue<Notification>(notification => + { + Dispatcher.UIThread.Post(() => + { + _notificationManager.Show(notification); + }); + }, + "UI.NotificationThread", + _notifications), + LazyThreadSafetyMode.ExecutionAndPublication); + _notificationManager.TemplateApplied += (sender, args) => { - _templateAppliedEvent.Set(); + // NOTE: Force creation of the AsyncWorkQueue. + _ = maybeAsyncWorkQueue.Value; }; - Task.Run(async () => + host.Closing += (sender, args) => { - _templateAppliedEvent.WaitOne(); - - foreach (var notification in _notifications.GetConsumingEnumerable()) + if (maybeAsyncWorkQueue.IsValueCreated) { - Dispatcher.UIThread.Post(() => - { - _notificationManager.Show(notification); - }); - - await Task.Delay(NotificationDelayInMs / MaxNotifications); + maybeAsyncWorkQueue.Value.Dispose(); } - }); + }; } public static void Show(string title, string text, NotificationType type, bool waitingExit = false, Action onClick = null, Action onClose = null) diff --git a/src/Ryujinx.Common/AsyncWorkQueue.cs b/src/Ryujinx.Common/AsyncWorkQueue.cs index 80f8dcfe4..746ef4cac 100644 --- a/src/Ryujinx.Common/AsyncWorkQueue.cs +++ b/src/Ryujinx.Common/AsyncWorkQueue.cs @@ -22,9 +22,11 @@ namespace Ryujinx.Common _cts = new CancellationTokenSource(); _queue = collection; _workerAction = callback; - _workerThread = new Thread(DoWork) { Name = name }; - - _workerThread.IsBackground = true; + _workerThread = new Thread(DoWork) + { + Name = name, + IsBackground = true + }; _workerThread.Start(); } From 986ac9ff8330b61a734f91187bf4b97c16a452c7 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 28 May 2023 08:02:30 +0200 Subject: [PATCH 567/737] Use variables to configure job timeouts (#5123) --- .github/workflows/build.yml | 4 ++-- .github/workflows/flatpak.yml | 2 +- .github/workflows/nightly_pr_comment.yml | 2 +- .github/workflows/release.yml | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c6dc724c8..37b68448c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -110,7 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} strategy: matrix: configuration: [ Debug, Release ] diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 04b917c34..96bc77e16 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -12,7 +12,7 @@ concurrency: flatpak-release jobs: release: - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} runs-on: ubuntu-latest env: diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index 9ddd458c6..c7af4acad 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -7,7 +7,7 @@ jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} steps: - uses: actions/github-script@v6 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 491fe6d9e..4018facab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: release: name: Release ${{ matrix.OS_NAME }} runs-on: ${{ matrix.os }} - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} strategy: matrix: os: [ ubuntu-latest, windows-latest ] @@ -144,7 +144,7 @@ jobs: macos_release: name: Release MacOS universal runs-on: ubuntu-latest - timeout-minutes: 35 + timeout-minutes: ${{ vars.JOB_TIMEOUT }} steps: - uses: actions/checkout@v3 @@ -207,4 +207,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit + secrets: inherit \ No newline at end of file From f3873620a339ef5c6b0cc2602ffe033134a1a300 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 28 May 2023 08:08:47 +0200 Subject: [PATCH 568/737] actions: Workaround YAML limitation for timeout-minutes Because Github Actions wants an int, we use fromJSON to hack around this. --- .github/workflows/build.yml | 4 ++-- .github/workflows/flatpak.yml | 2 +- .github/workflows/nightly_pr_comment.yml | 2 +- .github/workflows/release.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37b68448c..efd7d2314 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -110,7 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} strategy: matrix: configuration: [ Debug, Release ] diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 96bc77e16..d4380e05f 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -12,7 +12,7 @@ concurrency: flatpak-release jobs: release: - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} runs-on: ubuntu-latest env: diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index c7af4acad..deabae670 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -7,7 +7,7 @@ jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} steps: - uses: actions/github-script@v6 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4018facab..98ba34822 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: release: name: Release ${{ matrix.OS_NAME }} runs-on: ${{ matrix.os }} - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} strategy: matrix: os: [ ubuntu-latest, windows-latest ] @@ -144,7 +144,7 @@ jobs: macos_release: name: Release MacOS universal runs-on: ubuntu-latest - timeout-minutes: ${{ vars.JOB_TIMEOUT }} + timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} steps: - uses: actions/checkout@v3 From e8f5e97fa475e196da4102574725cabd8c9994d5 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 28 May 2023 11:34:57 +0200 Subject: [PATCH 569/737] actions: revert timeout-minutes changes for PR workflow Varibales aren't exposed to PRs... --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index efd7d2314..d379437b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} + timeout-minutes: 45 strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -110,7 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} + timeout-minutes: 45 strategy: matrix: configuration: [ Debug, Release ] From 378080eb8721f7ef652f7a546efd00d0b821c80d Mon Sep 17 00:00:00 2001 From: Daniel Shala <daniel.shala08@gmail.com> Date: Sun, 28 May 2023 22:44:46 +0200 Subject: [PATCH 570/737] Added Custom Path case when saving screenshots (#5086) --- src/Ryujinx.Ava/AppHost.cs | 2 +- src/Ryujinx/Ui/RendererWidgetBase.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index f3e90ef17..349b64b41 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -270,7 +270,7 @@ namespace Ryujinx.Ava string directory = AppDataManager.Mode switch { - AppDataManager.LaunchMode.Portable => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), + AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") }; diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index 573b69b35..e2cba7775 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -381,7 +381,7 @@ namespace Ryujinx.Ui string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; string directory = AppDataManager.Mode switch { - AppDataManager.LaunchMode.Portable => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), + AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), _ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") }; From 7e0b4bd538cbd4d9a7aff7f1936b781bb036998a Mon Sep 17 00:00:00 2001 From: siegmund-heiss-ich <119589995+siegmund-heiss-ich@users.noreply.github.com> Date: Sun, 28 May 2023 22:54:30 +0200 Subject: [PATCH 571/737] Improve macOS updater (#5064) * Fix macOS Updater (once again) * Also fix my brain's issues * Move set -e that lsof doesn't trigger exit 1 * Resolve yesterdays brain malfunction 2 * Revert "Move set -e that lsof doesn't trigger exit 1" This reverts commit 589a630610fff26f6a549d82c73be61b74187327. * Also check if PID exists * Remove lsof and instead check for running processes * Remove empty lines * Increase max iterations * Address feedback * Remove obsolete check for child processes * Update comments * Update comments * I swear this is the last commit * lsof + ps check --- distribution/macos/updater.sh | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh index 0854d4347..4d7dcdf23 100755 --- a/distribution/macos/updater.sh +++ b/distribution/macos/updater.sh @@ -25,14 +25,27 @@ error_handler() { exit 1 } -# Wait for Ryujinx to exit -# NOTE: in case no fds are open, lsof could be returning with a process still living. -# We wait 1s and assume the process stopped after that -lsof -p $APP_PID +r 1 &>/dev/null -sleep 1 - trap 'error_handler ${LINENO}' ERR +# Wait for Ryujinx to exit. +# If the main process is still acitve, we wait for 1 second and check it again. +# After the fifth time checking, this script exits with status 1. + +attempt=0 +while true; do + if lsof -p $APP_PID +r 1 &>/dev/null || ps -p "$APP_PID" &>/dev/null; then + if [ "$attempt" -eq 4 ]; then + exit 1 + fi + sleep 1 + else + break + fi + (( attempt++ )) +done + +sleep 1 + # Now replace and reopen. rm -rf "$INSTALL_DIRECTORY" mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY" From dd514a115ccdf44662868dbd4bc7b02254c1fe9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Arrouye?= <5017270+tarrouye@users.noreply.github.com> Date: Sun, 28 May 2023 14:03:27 -0700 Subject: [PATCH 572/737] Update LastPlayed date on emulation end. (#5056) --- src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs | 2 ++ src/Ryujinx/Ui/MainWindow.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f8dd41435..e7013968d 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1529,6 +1529,8 @@ namespace Ryujinx.Ava.UI.ViewModels double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); } + + appMetadata.LastPlayed = DateTime.UtcNow; }); } diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index 7cae62227..1918594ce 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -1024,6 +1024,8 @@ namespace Ryujinx.Ui double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); } + + appMetadata.LastPlayed = DateTime.UtcNow; }); } } From c9e297b74c99f8269f82863a31495a47ee1f7b0d Mon Sep 17 00:00:00 2001 From: yell0wsuit <5692900+yell0wsuit@users.noreply.github.com> Date: Mon, 29 May 2023 04:13:40 +0700 Subject: [PATCH 573/737] About window: Add changelog link under ver. number (#5095) --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 4 +++- src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml | 12 ++++++++++ .../Ui/Windows/AboutWindow.Designer.cs | 22 +++++++++++++++++++ src/Ryujinx/Ui/Windows/AboutWindow.cs | 5 +++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index a7b490b91..03cddcc84 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -637,5 +637,7 @@ "SettingsTabNetworkInterface": "Network Interface:", "NetworkInterfaceTooltip": "The network interface used for LAN features", "NetworkInterfaceDefault": "Default", - "PackagingShaders": "Packaging Shaders" + "PackagingShaders": "Packaging Shaders", + "AboutChangelogButton": "View Changelog on GitHub", + "AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser." } \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml index 7bd3e20d3..28a851d67 100644 --- a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml @@ -72,6 +72,18 @@ LineHeight="12" Text="{Binding Version}" TextAlignment="Center" /> + <Button + Padding="5" + HorizontalAlignment="Center" + Background="Transparent" + Click="Button_OnClick" + Tag="https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog"> + <TextBlock + FontSize="10" + Text="{locale:Locale AboutChangelogButton}" + TextAlignment="Center" + ToolTip.Tip="{locale:Locale AboutChangelogButtonTooltipMessage}" /> + </Button> </StackPanel> <StackPanel Grid.Row="2" diff --git a/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs b/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs index fa1a06578..3edc002d7 100644 --- a/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs +++ b/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs @@ -48,6 +48,8 @@ namespace Ryujinx.Ui.Windows private Label _patreonNamesLabel; private ScrolledWindow _patreonNamesScrolled; private TextView _patreonNamesText; + private EventBox _changelogEventBox; + private Label _changelogLinkLabel; private void InitializeComponent() { @@ -148,6 +150,23 @@ namespace Ryujinx.Ui.Windows Margin = 5 }; + // + // _changelogEventBox + // + _changelogEventBox = new EventBox(); + _changelogEventBox.ButtonPressEvent += ChangelogButton_Pressed; + + // + // _changelogLinkLabel + // + _changelogLinkLabel = new Label("View Changelog on GitHub") + { + TooltipText = "Click to open the changelog for this version in your default browser.", + Justify = Justification.Center, + Attributes = new AttrList() + }; + _changelogLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); + // // _disclaimerLabel // @@ -464,8 +483,11 @@ namespace Ryujinx.Ui.Windows _socialBox.Add(_discordEventBox); _socialBox.Add(_twitterEventBox); + _changelogEventBox.Add(_changelogLinkLabel); + _leftBox.Add(_logoBox); _leftBox.Add(_versionLabel); + _leftBox.Add(_changelogEventBox); _leftBox.Add(_disclaimerLabel); _leftBox.Add(_amiiboApiLink); _leftBox.Add(_socialBox); diff --git a/src/Ryujinx/Ui/Windows/AboutWindow.cs b/src/Ryujinx/Ui/Windows/AboutWindow.cs index 41cf9c013..15bfa500d 100644 --- a/src/Ryujinx/Ui/Windows/AboutWindow.cs +++ b/src/Ryujinx/Ui/Windows/AboutWindow.cs @@ -76,5 +76,10 @@ namespace Ryujinx.Ui.Windows { OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a"); } + + private void ChangelogButton_Pressed(object sender, ButtonPressEventArgs args) + { + OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog"); + } } } \ No newline at end of file From 994f4dc77db72c7dd796f594b7c1798895cdb33d Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sun, 28 May 2023 23:25:55 +0200 Subject: [PATCH 574/737] chore: Update Avalonia to 0.10.21 (#5124) --- Directory.Packages.props | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0bbe81406..5054689ca 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,11 +3,11 @@ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> </PropertyGroup> <ItemGroup> - <PackageVersion Include="Avalonia" Version="0.10.19" /> - <PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" /> - <PackageVersion Include="Avalonia.Desktop" Version="0.10.19" /> - <PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" /> - <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" /> + <PackageVersion Include="Avalonia" Version="0.10.21" /> + <PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" /> + <PackageVersion Include="Avalonia.Desktop" Version="0.10.21" /> + <PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" /> + <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" /> <PackageVersion Include="Avalonia.Svg" Version="0.10.18" /> <PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" /> From dc0dbc50abdaedfdcca05e5a5c1a5f26f70e3b79 Mon Sep 17 00:00:00 2001 From: cstamford <christopherstamford@improbable.io> Date: Sun, 28 May 2023 22:31:56 +0100 Subject: [PATCH 575/737] Add support for VK_EXT_depth_clip_control. (#5027) * Add support for VK_EXT_depth_clip_control. * Code review feedback Minor formatting Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Check .DepthClipControl to make sure the host actually supports the feature. * Review feedback: remove Vulkan platform switch, relying on QueryHostSupportsDepthClipControl to drive the behaviour - OpenGL returns true, and any future platforms that don't support the [-1, 1] depth mode can return false for the transformation. --------- Co-authored-by: gdkchan <gab.dark.100@gmail.com> --- src/Ryujinx.Graphics.GAL/Capabilities.cs | 3 +++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 2 ++ src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 1 + src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 +++++++ .../Translation/EmitterContext.cs | 4 +-- .../HardwareCapabilities.cs | 3 +++ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 8 ++++-- src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 18 +++++++++++++ .../VulkanInitialization.cs | 27 +++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 15 +++++++++++ 11 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index a93d38466..48b37d35d 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -39,6 +39,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsViewportMask; public readonly bool SupportsViewportSwizzle; public readonly bool SupportsIndirectParameters; + public readonly bool SupportsDepthClipControl; public readonly uint MaximumUniformBuffersPerStage; public readonly uint MaximumStorageBuffersPerStage; @@ -85,6 +86,7 @@ namespace Ryujinx.Graphics.GAL bool supportsViewportMask, bool supportsViewportSwizzle, bool supportsIndirectParameters, + bool supportsDepthClipControl, uint maximumUniformBuffersPerStage, uint maximumStorageBuffersPerStage, uint maximumTexturesPerStage, @@ -127,6 +129,7 @@ namespace Ryujinx.Graphics.GAL SupportsViewportMask = supportsViewportMask; SupportsViewportSwizzle = supportsViewportSwizzle; SupportsIndirectParameters = supportsIndirectParameters; + SupportsDepthClipControl = supportsDepthClipControl; MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage; MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage; MaximumTexturesPerStage = maximumTexturesPerStage; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index e2346bd0b..7f83f5880 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5110; + private const uint CodeGenVersion = 5027; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index e4e1e0d61..d206aad0b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -165,6 +165,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask; + public bool QueryHostSupportsDepthClipControl() => _context.Capabilities.SupportsDepthClipControl; + /// <summary> /// Converts a packed Maxwell texture format to the shader translator texture format. /// </summary> diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 7d5fe8931..161191b85 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -163,6 +163,7 @@ namespace Ryujinx.Graphics.OpenGL supportsViewportMask: HwCapabilities.SupportsViewportArray2, supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, + supportsDepthClipControl: true, maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver? maximumStorageBuffersPerStage: 16, maximumTexturesPerStage: 32, diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 2207156cd..3be5088e4 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -367,6 +367,15 @@ namespace Ryujinx.Graphics.Shader return true; } + /// <summary> + /// Queries whether the host supports depth clip control. + /// </summary> + /// <returns>True if the GPU and driver supports depth clip control, false otherwise</returns> + bool QueryHostSupportsDepthClipControl() + { + return true; + } + /// <summary> /// Queries the point size from the GPU state, used when it is not explicitly set on the shader. /// </summary> diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 2786caaa0..6ca74a379 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Shader.Translation this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne)); } - if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne()) + if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl()) { Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)); Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3)); @@ -283,7 +283,7 @@ namespace Ryujinx.Graphics.Shader.Translation oldYLocal = null; } - if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne()) + if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl()) { oldZLocal = Local(); this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2))); diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 4b9ace3aa..f600d93f0 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -43,6 +43,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsGeometryShader; public readonly bool SupportsViewportArray2; public readonly bool SupportsHostImportedMemory; + public readonly bool SupportsDepthClipControl; public readonly uint MinSubgroupSize; public readonly uint MaxSubgroupSize; public readonly ShaderStageFlags RequiredSubgroupSizeStages; @@ -79,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsGeometryShader, bool supportsViewportArray2, bool supportsHostImportedMemory, + bool supportsDepthClipControl, uint minSubgroupSize, uint maxSubgroupSize, ShaderStageFlags requiredSubgroupSizeStages, @@ -114,6 +116,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsGeometryShader = supportsGeometryShader; SupportsViewportArray2 = supportsViewportArray2; SupportsHostImportedMemory = supportsHostImportedMemory; + SupportsDepthClipControl = supportsDepthClipControl; MinSubgroupSize = minSubgroupSize; MaxSubgroupSize = maxSubgroupSize; RequiredSubgroupSizeStages = requiredSubgroupSizeStages; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index ce6148e2f..b4eccfbb2 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -813,8 +813,12 @@ namespace Ryujinx.Graphics.Vulkan public void SetDepthMode(DepthMode mode) { - // Currently this is emulated on the shader, because Vulkan had no support for changing the depth mode. - // In the future, we may want to use the VK_EXT_depth_clip_control extension to change it here. + bool oldMode = _newState.DepthMode; + _newState.DepthMode = mode == DepthMode.MinusOneToOne; + if (_newState.DepthMode != oldMode) + { + SignalStateChange(); + } } public void SetDepthTest(DepthTestDescriptor depthTest) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index a3d8dd6fb..aff3f13f0 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -304,6 +304,12 @@ namespace Ryujinx.Graphics.Vulkan set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); } + public bool DepthMode + { + get => ((Internal.Id9 >> 6) & 0x1) != 0UL; + set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); + } + public NativeArray<PipelineShaderStageCreateInfo> Stages; public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes; public PipelineLayout PipelineLayout; @@ -331,6 +337,7 @@ namespace Ryujinx.Graphics.Vulkan LineWidth = 1f; SamplesCount = 1; + DepthMode = true; } public unsafe Auto<DisposablePipeline> CreateComputePipeline( @@ -482,6 +489,17 @@ namespace Ryujinx.Graphics.Vulkan PScissors = pScissors }; + if (gd.Capabilities.SupportsDepthClipControl) + { + var viewportDepthClipControlState = new PipelineViewportDepthClipControlCreateInfoEXT() + { + SType = StructureType.PipelineViewportDepthClipControlCreateInfoExt, + NegativeOneToOne = DepthMode + }; + + viewportState.PNext = &viewportDepthClipControlState; + } + var multisampleState = new PipelineMultisampleStateCreateInfo { SType = StructureType.PipelineMultisampleStateCreateInfo, diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 499a9ef78..51a3b129a 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_subgroup_size_control", "VK_NV_geometry_shader_passthrough", "VK_NV_viewport_array2", + "VK_EXT_depth_clip_control", "VK_KHR_portability_subset" // As per spec, we should enable this if present. }; @@ -345,6 +346,17 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesRobustness2; } + PhysicalDeviceDepthClipControlFeaturesEXT supportedFeaturesDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + { + SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, + PNext = features2.PNext + }; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control")) + { + features2.PNext = &supportedFeaturesDepthClipControl; + } + api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2); var supportedFeatures = features2.Features; @@ -507,6 +519,21 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresCustomBorderColor; } + PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control") && + supportedFeaturesDepthClipControl.DepthClipControl) + { + featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + { + SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, + PNext = pExtendedFeatures, + DepthClipControl = true + }; + + pExtendedFeatures = &featuresDepthClipControl; + } + var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 4f3f72345..3987be9b4 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -216,6 +216,11 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt }; + PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + { + SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt + }; + PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR() { SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr @@ -244,6 +249,14 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &featuresCustomBorderColor; } + bool supportsDepthClipControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control"); + + if (supportsDepthClipControl) + { + featuresDepthClipControl.PNext = features2.PNext; + features2.PNext = &featuresDepthClipControl; + } + bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); if (usePortability) @@ -310,6 +323,7 @@ namespace Ryujinx.Graphics.Vulkan _physicalDevice.PhysicalDeviceFeatures.GeometryShader, _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), + supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, @@ -585,6 +599,7 @@ namespace Ryujinx.Graphics.Vulkan supportsViewportMask: Capabilities.SupportsViewportArray2, supportsViewportSwizzle: false, supportsIndirectParameters: true, + supportsDepthClipControl: Capabilities.SupportsDepthClipControl, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage, From 7bc9d0cdad5ce1b79d0f6a90b3ee6322db6aff39 Mon Sep 17 00:00:00 2001 From: subanz <subanz@protonmail.com> Date: Sun, 28 May 2023 16:54:22 -0500 Subject: [PATCH 576/737] Linux: Use gamemode if it is available when using Ryujinx.sh. (#4938) * Linux: Detect if gamemode is installed and start it when launching Ryujinx. When using the Ryujinx.sh script to start the emulator check if gamemoderun exists and use it if it does. Gamemode mode on Linux changes some system settings to make performance during gaming more consistent mainly by changing the CPU governor to performance. https://github.com/FeralInteractive/gamemode * Removed if statement. * Fix due to wrong assumption about the output of which. Checks if the which output contains a no match response, otherwise use gamemoderun. Using a case statement because it makes substring matching possible in sh and also it turns out that adding an empty string after env throws an error because env attempts to parse it as a paramater. * Missed a couple semicolons. * Different approach for checking if gamemode is available. Should hopefully work across all implementations of which. * Remove unneeded which command. * Change code to keep launch command to a single line. --- distribution/linux/Ryujinx.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh index 91fb467df..a80cdcaec 100644 --- a/distribution/linux/Ryujinx.sh +++ b/distribution/linux/Ryujinx.sh @@ -11,4 +11,10 @@ if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then RYUJINX_BIN="Ryujinx.Headless.SDL2" fi -env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/$RYUJINX_BIN" "$@" +COMMAND="env DOTNET_EnableAlternateStackCheck=1" + +if command -v gamemoderun > /dev/null 2>&1; then + COMMAND="$COMMAND gamemoderun" +fi + +$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@" From 1cf6d7b7bbcb4f10025e8230339873a15dea2752 Mon Sep 17 00:00:00 2001 From: Simon Wegendt <plneappl@users.noreply.github.com> Date: Mon, 29 May 2023 00:07:27 +0200 Subject: [PATCH 577/737] Fix #5108: Allow surround sound for SDL2 in more scenarios (#5131) --- src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index d3a73cfcb..7bfff5f9b 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Backends.SDL2 } else { - _supportSurroundConfiguration = spec.channels == 6; + _supportSurroundConfiguration = spec.channels >= 6; } } From 597388ecda35b0feaa3b678b7216689a5d84bbac Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 28 May 2023 19:17:07 -0300 Subject: [PATCH 578/737] Fix incorrect vertex attribute format change (#5112) * Fix incorrect vertex attribute format change * Only change vertex format if the host supports the new format --- .../FormatCapabilities.cs | 7 +++ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 1 - src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 50 +++++++++++++------ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index 7019dfd91..a2ebc518e 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -65,6 +65,13 @@ namespace Ryujinx.Graphics.Vulkan return (formatFeatureFlags & flags) == flags; } + public bool BufferFormatSupports(FormatFeatureFlags flags, VkFormat format) + { + _api.GetPhysicalDeviceFormatProperties(_physicalDevice, format, out var fp); + + return (fp.BufferFeatures & flags) == flags; + } + public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format) { var formatFeatureFlags = _optimalTable[(int)format]; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index b4eccfbb2..c254d3781 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -551,7 +551,6 @@ namespace Ryujinx.Graphics.Vulkan (uint)maxDrawCount, (uint)stride); } - } else { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index aff3f13f0..1a396b5c8 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -414,7 +414,7 @@ namespace Ryujinx.Graphics.Vulkan if (isMoltenVk) { - UpdateVertexAttributeDescriptions(); + UpdateVertexAttributeDescriptions(gd); } fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0]) @@ -641,7 +641,7 @@ namespace Ryujinx.Graphics.Vulkan } } - private void UpdateVertexAttributeDescriptions() + private void UpdateVertexAttributeDescriptions(VulkanRenderer gd) { // Vertex attributes exceeding the stride are invalid. // In metal, they cause glitches with the vertex shader fetching incorrect values. @@ -651,30 +651,52 @@ namespace Ryujinx.Graphics.Vulkan for (int index = 0; index < VertexAttributeDescriptionsCount; index++) { var attribute = Internal.VertexAttributeDescriptions[index]; - ref var vb = ref Internal.VertexBindingDescriptions[(int)attribute.Binding]; + int vbIndex = GetVertexBufferIndex(attribute.Binding); - Format format = attribute.Format; - - while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) + if (vbIndex >= 0) { - Format newFormat = FormatTable.DropLastComponent(format); + ref var vb = ref Internal.VertexBindingDescriptions[vbIndex]; - if (newFormat == format) + Format format = attribute.Format; + + while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) { - // 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 = 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; } - format = newFormat; + if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format)) + { + attribute.Format = 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; + } + public void Dispose() { Stages.Dispose(); From 96d1f0da2d10545600d83a88e63cfef9b2785e25 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 28 May 2023 19:24:35 -0300 Subject: [PATCH 579/737] Workaround for MoltenVK barrier issues (#5118) --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c254d3781..688682f49 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -80,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan private PipelineColorBlendAttachmentState[] _storedBlend; + private ulong _drawCountSinceBarrier; public ulong DrawCount { get; private set; } public bool RenderPassActive { get; private set; } @@ -133,6 +134,18 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void Barrier() { + if (_drawCountSinceBarrier != DrawCount) + { + _drawCountSinceBarrier = DrawCount; + + // Barriers apparently have no effect inside a render pass on MoltenVK. + // As a workaround, end the render pass. + if (Gd.IsMoltenVk) + { + EndRenderPass(); + } + } + MemoryBarrier memoryBarrier = new MemoryBarrier() { SType = StructureType.MemoryBarrier, From 832a5e885281f6063f7855e92c3cf85eac82e715 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 28 May 2023 19:38:04 -0300 Subject: [PATCH 580/737] Make sure blend is disabled if render target has integer format (#5122) * Make sure blend is disabled if render target has integer format * Change approach to avoid permanently mutating state --- .../FramebufferParams.cs | 9 ++++++ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 1 + .../PipelineConverter.cs | 7 +++++ src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 31 +++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/PipelineUid.cs | 1 + 5 files changed, 49 insertions(+) diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index cde992028..0437a402c 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Vulkan public uint[] AttachmentSamples { get; } public VkFormat[] AttachmentFormats { get; } public int[] AttachmentIndices { get; } + public uint AttachmentIntegerFormatMask { get; } public int AttachmentsCount { get; } public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1; @@ -74,6 +75,7 @@ namespace Ryujinx.Graphics.Vulkan int index = 0; int bindIndex = 0; + uint attachmentIntegerFormatMask = 0; foreach (ITexture color in colors) { @@ -89,6 +91,11 @@ namespace Ryujinx.Graphics.Vulkan AttachmentFormats[index] = texture.VkFormat; AttachmentIndices[index] = bindIndex; + if (texture.Info.Format.IsInteger()) + { + attachmentIntegerFormatMask |= 1u << bindIndex; + } + width = Math.Min(width, (uint)texture.Width); height = Math.Min(height, (uint)texture.Height); layers = Math.Min(layers, (uint)texture.Layers); @@ -102,6 +109,8 @@ namespace Ryujinx.Graphics.Vulkan bindIndex++; } + AttachmentIntegerFormatMask = attachmentIntegerFormatMask; + if (depthStencil is TextureView dsTexture && dsTexture.Valid) { _attachments[count - 1] = dsTexture.GetImageViewForAttachment(); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 688682f49..dcffa2473 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1487,6 +1487,7 @@ namespace Ryujinx.Graphics.Vulkan { var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); + _newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask; for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index da480d9f5..79179ce07 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -294,6 +294,7 @@ namespace Ryujinx.Graphics.Vulkan int attachmentCount = 0; int maxColorAttachmentIndex = -1; + uint attachmentIntegerFormatMask = 0; for (int i = 0; i < Constants.MaxRenderTargets; i++) { @@ -301,6 +302,11 @@ namespace Ryujinx.Graphics.Vulkan { pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); maxColorAttachmentIndex = i; + + if (state.AttachmentFormats[i].IsInteger()) + { + attachmentIntegerFormatMask |= 1u << i; + } } } @@ -311,6 +317,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); + pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask; return pipeline; } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index 1a396b5c8..7e803913f 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Silk.NET.Vulkan; using System; +using System.Numerics; namespace Ryujinx.Graphics.Vulkan { @@ -542,6 +543,27 @@ namespace Ryujinx.Graphics.Vulkan MaxDepthBounds = MaxDepthBounds }; + uint blendEnables = 0; + + if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0) + { + // Blend can't be enabled for integer formats, so let's make sure it is disabled. + uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask; + + while (attachmentIntegerFormatMask != 0) + { + int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask); + + if (Internal.ColorBlendAttachmentState[i].BlendEnable) + { + blendEnables |= 1u << i; + } + + Internal.ColorBlendAttachmentState[i].BlendEnable = false; + attachmentIntegerFormatMask &= ~(1u << i); + } + } + var colorBlendState = new PipelineColorBlendStateCreateInfo() { SType = StructureType.PipelineColorBlendStateCreateInfo, @@ -619,6 +641,15 @@ namespace Ryujinx.Graphics.Vulkan }; gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError(); + + // Restore previous blend enable values if we changed it. + while (blendEnables != 0) + { + int i = BitOperations.TrailingZeroCount(blendEnables); + + Internal.ColorBlendAttachmentState[i].BlendEnable = true; + blendEnables &= ~(1u << i); + } } pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle)); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs index 78d6e9f71..bf23f4714 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Graphics.Vulkan public Array16<Rect2D> Scissors; public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState; public Array9<Format> AttachmentFormats; + public uint AttachmentIntegerFormatMask; public override bool Equals(object obj) { From a73a5d7e85e4008a7d8c7eb8abd6bae80b950bba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 01:14:07 +0200 Subject: [PATCH 581/737] nuget: bump Microsoft.NET.Test.Sdk from 17.5.0 to 17.6.0 (#4986) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.5.0 to 17.6.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.5.0...v17.6.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5054689ca..9991b65a9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,7 +21,7 @@ <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> - <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.0" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> From 35d91a0e58cb0b2916b7a4f138c63fcc12b71112 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 30 May 2023 01:48:37 +0200 Subject: [PATCH 582/737] Linux: Automatically increase vm.max_map_count if it's too low (#4702) * memory: Check results of pinvoke calls * Increase vm.max_map_count when running Ryujinx * Add SupportedOSPlatform attribute for WindowsApiException * Revert increasing vm.max_map_count via script * Add LinuxHelper to detect and increase vm.max_map_count With GUI dialogs, this should be a bit more user-friendly. * Supply arguments as a list to RunPkExec * Add error logging in case RunPkExec() fails * Prevent Gtk from crashing --- distribution/linux/Ryujinx.sh | 2 +- src/Ryujinx.Ava/Assets/Locales/en_US.json | 7 ++ src/Ryujinx.Ava/Ryujinx.Ava.csproj | 3 +- .../UI/Helpers/ContentDialogHelper.cs | 7 +- .../UI/Windows/MainWindow.axaml.cs | 74 ++++++++++++++++++- src/Ryujinx.Memory/MemoryManagementUnix.cs | 29 ++++++-- src/Ryujinx.Memory/MemoryManagementWindows.cs | 5 +- src/Ryujinx.Memory/MemoryManagerUnixHelper.cs | 5 ++ .../MemoryProtectionException.cs | 3 +- .../WindowsShared/WindowsApi.cs | 2 + .../WindowsShared/WindowsApiException.cs | 2 + src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs | 62 ++++++++++++++++ src/Ryujinx/Program.cs | 65 ++++++++++++++++ src/Ryujinx/Ryujinx.csproj | 3 +- 14 files changed, 252 insertions(+), 17 deletions(-) create mode 100644 src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh index a80cdcaec..f356cad01 100644 --- a/distribution/linux/Ryujinx.sh +++ b/distribution/linux/Ryujinx.sh @@ -17,4 +17,4 @@ if command -v gamemoderun > /dev/null 2>&1; then COMMAND="$COMMAND gamemoderun" fi -$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@" +$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@" \ No newline at end of file diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 03cddcc84..a68f91975 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -74,6 +74,13 @@ "GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)", "StatusBarGamesLoaded": "{0}/{1} Games Loaded", "StatusBarSystemVersion": "System Version: {0}", + "LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected", + "LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}", + "LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.", + "LinuxVmMaxMapCountDialogButtonUntilRestart": "Yes, until the next restart", + "LinuxVmMaxMapCountDialogButtonPersistent": "Yes, permanently", + "LinuxVmMaxMapCountWarningTextPrimary": "Max amount of memory mappings is lower than recommended.", + "LinuxVmMaxMapCountWarningTextSecondary": "The current value of vm.max_map_count ({0}) is lower than {1}. Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.\n\nYou might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that.", "Settings": "Settings", "SettingsTabGeneral": "User Interface", "SettingsTabGeneralGeneral": "General", diff --git a/src/Ryujinx.Ava/Ryujinx.Ava.csproj b/src/Ryujinx.Ava/Ryujinx.Ava.csproj index 2b6432e9b..1fac5400e 100644 --- a/src/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/src/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -18,6 +18,7 @@ <PropertyGroup Condition="'$(RuntimeIdentifier)' != ''"> <PublishSingleFile>true</PublishSingleFile> + <TrimmerSingleWarn>false</TrimmerSingleWarn> <PublishTrimmed>true</PublishTrimmed> <TrimMode>partial</TrimMode> </PropertyGroup> @@ -147,4 +148,4 @@ <ItemGroup> <AdditionalFiles Include="Assets\Locales\en_US.json" /> </ItemGroup> -</Project> +</Project> \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index cb474506b..d85895fc8 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -6,7 +6,6 @@ using Avalonia.Threading; using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Logging; using System; @@ -19,7 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers { private static bool _isChoiceDialogOpen; - public async static Task<UserResult> ShowContentDialog( + private async static Task<UserResult> ShowContentDialog( string title, object content, string primaryButton, @@ -67,7 +66,7 @@ namespace Ryujinx.Ava.UI.Helpers return result; } - private async static Task<UserResult> ShowTextDialog( + public async static Task<UserResult> ShowTextDialog( string title, string primaryText, string secondaryText, @@ -319,7 +318,7 @@ namespace Ryujinx.Ava.UI.Helpers Window parent = GetMainWindow(); - if (parent != null && parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning) + if (parent is { IsActive: true } and MainWindow window && window.ViewModel.IsGameRunning) { contentDialogOverlayWindow = new() { diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index eec77479e..cf84807e3 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -23,6 +23,7 @@ using Ryujinx.Ui.Common.Helper; using System; using System.ComponentModel; using System.IO; +using System.Runtime.Versioning; using System.Threading.Tasks; using InputManager = Ryujinx.Input.HLE.InputManager; @@ -258,7 +259,64 @@ namespace Ryujinx.Ava.UI.Windows ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this); } - protected void CheckLaunchState() + [SupportedOSPlatform("linux")] + private static async void ShowVmMaxMapCountWarning() + { + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary, + LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount); + + await ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextPrimary], + LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary] + ); + } + + [SupportedOSPlatform("linux")] + private static async void ShowVmMaxMapCountDialog() + { + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary, + LinuxHelper.RecommendedVmMaxMapCount); + + UserResult response = await ContentDialogHelper.ShowTextDialog( + $"Ryujinx - {LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTitle]}", + LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary], + LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextSecondary], + LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonUntilRestart], + LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonPersistent], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + (int)Symbol.Help + ); + + int rc; + + switch (response) + { + case UserResult.Ok: + rc = LinuxHelper.RunPkExec($"echo {LinuxHelper.RecommendedVmMaxMapCount} > {LinuxHelper.VmMaxMapCountPath}"); + if (rc == 0) + { + Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount} until the next restart."); + } + else + { + Logger.Error?.Print(LogClass.Application, $"Unable to change vm.max_map_count. Process exited with code: {rc}"); + } + break; + case UserResult.No: + rc = LinuxHelper.RunPkExec($"echo \"vm.max_map_count = {LinuxHelper.RecommendedVmMaxMapCount}\" > {LinuxHelper.SysCtlConfigPath} && sysctl -p {LinuxHelper.SysCtlConfigPath}"); + if (rc == 0) + { + Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount}. Written to config: {LinuxHelper.SysCtlConfigPath}"); + } + else + { + Logger.Error?.Print(LogClass.Application, $"Unable to write new value for vm.max_map_count to config. Process exited with code: {rc}"); + } + break; + } + } + + private void CheckLaunchState() { if (ShowKeyErrorOnLoad) { @@ -268,6 +326,20 @@ namespace Ryujinx.Ava.UI.Windows UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, this)); } + if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount) + { + Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})"); + + if (LinuxHelper.PkExecPath is not null) + { + Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog); + } + else + { + Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning); + } + } + if (_deferLoad) { _deferLoad = false; diff --git a/src/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs index affcff92b..30baf0353 100644 --- a/src/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/src/Ryujinx.Memory/MemoryManagementUnix.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; @@ -53,9 +54,9 @@ namespace Ryujinx.Memory IntPtr ptr = mmap(IntPtr.Zero, size, prot, flags, -1, 0); - if (ptr == new IntPtr(-1L)) + if (ptr == MAP_FAILED) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } if (!_allocations.TryAdd(ptr, size)) @@ -76,17 +77,33 @@ namespace Ryujinx.Memory prot |= MmapProts.PROT_EXEC; } - return mprotect(address, size, prot) == 0; + if (mprotect(address, size, prot) != 0) + { + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); + } + + return true; } public static bool Decommit(IntPtr address, ulong size) { // Must be writable for madvise to work properly. - mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE); + if (mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) != 0) + { + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); + } - madvise(address, size, MADV_REMOVE); + if (madvise(address, size, MADV_REMOVE) != 0) + { + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); + } - return mprotect(address, size, MmapProts.PROT_NONE) == 0; + if (mprotect(address, size, MmapProts.PROT_NONE) != 0) + { + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); + } + + return true; } public static bool Reprotect(IntPtr address, ulong size, MemoryPermission permission) diff --git a/src/Ryujinx.Memory/MemoryManagementWindows.cs b/src/Ryujinx.Memory/MemoryManagementWindows.cs index 2f89a921c..cbf3ecbac 100644 --- a/src/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/src/Ryujinx.Memory/MemoryManagementWindows.cs @@ -1,5 +1,6 @@ using Ryujinx.Memory.WindowsShared; using System; +using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace Ryujinx.Memory @@ -36,7 +37,7 @@ namespace Ryujinx.Memory if (ptr == IntPtr.Zero) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } return ptr; @@ -48,7 +49,7 @@ namespace Ryujinx.Memory if (ptr == IntPtr.Zero) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } return ptr; diff --git a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs index 204f1ca4d..a7b207ab0 100644 --- a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs +++ b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs @@ -1,8 +1,11 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.Versioning; namespace Ryujinx.Memory { + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] public static partial class MemoryManagerUnixHelper { [Flags] @@ -41,6 +44,8 @@ namespace Ryujinx.Memory O_SYNC = 256, } + public const IntPtr MAP_FAILED = -1; + private const int MAP_ANONYMOUS_LINUX_GENERIC = 0x20; private const int MAP_NORESERVE_LINUX_GENERIC = 0x4000; private const int MAP_UNLOCKED_LINUX_GENERIC = 0x80000; diff --git a/src/Ryujinx.Memory/MemoryProtectionException.cs b/src/Ryujinx.Memory/MemoryProtectionException.cs index 27e950a16..e5606e99f 100644 --- a/src/Ryujinx.Memory/MemoryProtectionException.cs +++ b/src/Ryujinx.Memory/MemoryProtectionException.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; namespace Ryujinx.Memory { @@ -8,7 +9,7 @@ namespace Ryujinx.Memory { } - public MemoryProtectionException(MemoryPermission permission) : base($"Failed to set memory protection to \"{permission}\".") + public MemoryProtectionException(MemoryPermission permission) : base($"Failed to set memory protection to \"{permission}\": {Marshal.GetLastPInvokeErrorMessage()}") { } diff --git a/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs b/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs index 67e704ea4..c554e3205 100644 --- a/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs +++ b/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs @@ -1,8 +1,10 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.Versioning; namespace Ryujinx.Memory.WindowsShared { + [SupportedOSPlatform("windows")] static partial class WindowsApi { public static readonly IntPtr InvalidHandleValue = new IntPtr(-1); diff --git a/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs b/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs index 3140d705b..330c1842a 100644 --- a/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs +++ b/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs @@ -1,7 +1,9 @@ using System; +using System.Runtime.Versioning; namespace Ryujinx.Memory.WindowsShared { + [SupportedOSPlatform("windows")] class WindowsApiException : Exception { public WindowsApiException() diff --git a/src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs b/src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs new file mode 100644 index 000000000..cfbf4b57d --- /dev/null +++ b/src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs @@ -0,0 +1,62 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.Versioning; + +namespace Ryujinx.Ui.Common.Helper +{ + [SupportedOSPlatform("linux")] + public static class LinuxHelper + { + // NOTE: This value was determined by manual tests and might need to be increased again. + public const int RecommendedVmMaxMapCount = 524288; + public const string VmMaxMapCountPath = "/proc/sys/vm/max_map_count"; + public const string SysCtlConfigPath = "/etc/sysctl.d/99-Ryujinx.conf"; + public static int VmMaxMapCount => int.Parse(File.ReadAllText(VmMaxMapCountPath)); + public static string PkExecPath { get; } = GetBinaryPath("pkexec"); + + private static string GetBinaryPath(string binary) + { + string pathVar = Environment.GetEnvironmentVariable("PATH"); + + if (pathVar is null || string.IsNullOrEmpty(binary)) + { + return null; + } + + foreach (var searchPath in pathVar.Split(":", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) + { + string binaryPath = Path.Combine(searchPath, binary); + + if (File.Exists(binaryPath)) + { + return binaryPath; + } + } + + return null; + } + + public static int RunPkExec(string command) + { + if (PkExecPath == null) + { + return 1; + } + + using Process process = new() + { + StartInfo = + { + FileName = PkExecPath, + ArgumentList = { "sh", "-c", command } + } + }; + + process.Start(); + process.WaitForExit(); + + return process.ExitCode; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 836483d89..96024548f 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -264,6 +264,71 @@ namespace Ryujinx MainWindow mainWindow = new MainWindow(); mainWindow.Show(); + if (OperatingSystem.IsLinux()) + { + int currentVmMaxMapCount = LinuxHelper.VmMaxMapCount; + + if (LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount) + { + Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({currentVmMaxMapCount})"); + + if (LinuxHelper.PkExecPath is not null) + { + var buttonTexts = new Dictionary<int, string>() + { + { 0, "Yes, until the next restart" }, + { 1, "Yes, permanently" }, + { 2, "No" } + }; + + ResponseType response = GtkDialog.CreateCustomDialog( + "Ryujinx - Low limit for memory mappings detected", + $"Would you like to increase the value of vm.max_map_count to {LinuxHelper.RecommendedVmMaxMapCount}?", + "Some games might try to create more memory mappings than currently allowed. " + + "Ryujinx will crash as soon as this limit gets exceeded.", + buttonTexts, + MessageType.Question); + + int rc; + + switch ((int)response) + { + case 0: + rc = LinuxHelper.RunPkExec($"echo {LinuxHelper.RecommendedVmMaxMapCount} > {LinuxHelper.VmMaxMapCountPath}"); + if (rc == 0) + { + Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount} until the next restart."); + } + else + { + Logger.Error?.Print(LogClass.Application, $"Unable to change vm.max_map_count. Process exited with code: {rc}"); + } + break; + case 1: + rc = LinuxHelper.RunPkExec($"echo \"vm.max_map_count = {LinuxHelper.RecommendedVmMaxMapCount}\" > {LinuxHelper.SysCtlConfigPath} && sysctl -p {LinuxHelper.SysCtlConfigPath}"); + if (rc == 0) + { + Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount}. Written to config: {LinuxHelper.SysCtlConfigPath}"); + } + else + { + Logger.Error?.Print(LogClass.Application, $"Unable to write new value for vm.max_map_count to config. Process exited with code: {rc}"); + } + break; + } + } + else + { + GtkDialog.CreateWarningDialog( + "Max amount of memory mappings is lower than recommended.", + $"The current value of vm.max_map_count ({currentVmMaxMapCount}) is lower than {LinuxHelper.RecommendedVmMaxMapCount}." + + "Some games might try to create more memory mappings than currently allowed. " + + "Ryujinx will crash as soon as this limit gets exceeded.\n\n" + + "You might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that."); + } + } + } + if (CommandLineState.LaunchPathArg != null) { mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 027cdd129..cf4435e57 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -14,6 +14,7 @@ <PropertyGroup Condition="'$(RuntimeIdentifier)' != ''"> <PublishSingleFile>true</PublishSingleFile> + <TrimmerSingleWarn>false</TrimmerSingleWarn> <PublishTrimmed>true</PublishTrimmed> <TrimMode>partial</TrimMode> </PropertyGroup> @@ -100,4 +101,4 @@ <EmbeddedResource Include="Modules\Updater\UpdateDialog.glade" /> </ItemGroup> -</Project> +</Project> \ No newline at end of file From 0dca1fbe12c637ccf04ac2853ac7476d41310932 Mon Sep 17 00:00:00 2001 From: Patrick Hovsepian <patrick.hovsepian@gmail.com> Date: Tue, 30 May 2023 11:51:03 -0700 Subject: [PATCH 583/737] Add Context Menu Option to Run Application (#5154) --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 7 ++++--- .../UI/Controls/ApplicationContextMenu.axaml | 3 +++ .../UI/Controls/ApplicationContextMenu.axaml.cs | 10 ++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index a68f91975..8f4965e14 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -289,6 +289,7 @@ "ControllerSettingsSaveProfileToolTip": "Save Profile", "MenuBarFileToolsTakeScreenshot": "Take Screenshot", "MenuBarFileToolsHideUi": "Hide UI", + "GameListContextMenuRunApplication": "Run Application", "GameListContextMenuToggleFavorite": "Toggle Favorite", "GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game", "SettingsTabGeneralTheme": "Theme", @@ -627,7 +628,7 @@ "Search": "Search", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "Recover": "Recover", - "UserProfilesRecoverHeading" : "Saves were found for the following accounts", + "UserProfilesRecoverHeading": "Saves were found for the following accounts", "UserProfilesRecoverEmptyList": "No profiles to recover", "GraphicsAATooltip": "Applies anti-aliasing to the game render", "GraphicsAALabel": "Anti-Aliasing:", @@ -639,8 +640,8 @@ "SmaaMedium": "SMAA Medium", "SmaaHigh": "SMAA High", "SmaaUltra": "SMAA Ultra", - "UserEditorTitle" : "Edit User", - "UserEditorTitleCreate" : "Create User", + "UserEditorTitle": "Edit User", + "UserEditorTitleCreate": "Create User", "SettingsTabNetworkInterface": "Network Interface:", "NetworkInterfaceTooltip": "The network interface used for LAN features", "NetworkInterfaceDefault": "Default", diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml index 35d5fe859..29547f5fe 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml @@ -3,6 +3,9 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"> + <MenuItem + Click="RunApplication_Click" + Header="{locale:Locale GameListContextMenuRunApplication}" /> <MenuItem Click="ToggleFavorite_Click" Header="{locale:Locale GameListContextMenuToggleFavorite}" diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index a9269386a..73c53e7f6 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -323,5 +323,15 @@ namespace Ryujinx.Ava.UI.Controls await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); } } + + public void RunApplication_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel?.SelectedApplication != null) + { + viewModel.LoadApplication(viewModel.SelectedApplication.Path); + } + } } } \ No newline at end of file From 0e037d021347e802b28bd74af3459b007b48083e Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Wed, 31 May 2023 08:08:50 +0100 Subject: [PATCH 584/737] macOS Headless Fixes (#5167) * Default hypervisor to disabled * Include MVK on macOS * Properly sign headless builds on macOS * Force Vulkan on macOS * Suggestions --- src/Ryujinx.Headless.SDL2/Options.cs | 2 +- src/Ryujinx.Headless.SDL2/Program.cs | 13 +++++++++++-- .../Ryujinx.Headless.SDL2.csproj | 8 +++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index 7dffa1b00..d7ad8cf79 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -130,7 +130,7 @@ namespace Ryujinx.Headless.SDL2 public float AudioVolume { get; set; } [Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")] - public bool UseHypervisor { get; set; } + public bool? UseHypervisor { get; set; } [Option("lan-interface-id", Required = false, Default = "0", HelpText = "GUID for the network interface used by LAN.")] public string MultiplayerLanInterfaceId { get; set; } diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 643db845f..fc1b23ebb 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -339,6 +339,15 @@ namespace Ryujinx.Headless.SDL2 GraphicsConfig.EnableShaderCache = true; + if (OperatingSystem.IsMacOS()) + { + if (option.GraphicsBackend == GraphicsBackend.OpenGl) + { + option.GraphicsBackend = GraphicsBackend.Vulkan; + Logger.Warning?.Print(LogClass.Application, "OpenGL is not supported on macOS, switching to Vulkan!"); + } + } + IGamepad gamepad; if (option.ListInputIds) @@ -550,7 +559,7 @@ namespace Ryujinx.Headless.SDL2 options.IgnoreMissingServices, options.AspectRatio, options.AudioVolume, - options.UseHypervisor, + options.UseHypervisor ?? true, options.MultiplayerLanInterfaceId); return new Switch(configuration); @@ -703,4 +712,4 @@ namespace Ryujinx.Headless.SDL2 return true; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index fc912d329..d2585c563 100644 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -7,6 +7,7 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <Version>1.0.0-dirty</Version> <DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants> + <SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate> <TieredPGO>true</TieredPGO> </PropertyGroup> @@ -15,6 +16,10 @@ <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" /> </ItemGroup> + <Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))"> + <Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" /> + </Target> + <ItemGroup> <ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" /> <ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" /> @@ -29,6 +34,7 @@ <ItemGroup> <PackageReference Include="CommandLineParser" /> + <PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" /> </ItemGroup> <ItemGroup> @@ -63,4 +69,4 @@ <PublishTrimmed>true</PublishTrimmed> <TrimMode>partial</TrimMode> </PropertyGroup> -</Project> +</Project> \ No newline at end of file From c27e453fd342688ea2a75a973566d711b00efcbb Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 31 May 2023 17:17:50 -0300 Subject: [PATCH 585/737] Share ResourceManager vertex vertex A and B shaders (#5181) --- .../Shader/GpuAccessorBase.cs | 18 +----------------- .../Translation/ResourceManager.cs | 15 ++------------- .../Translation/ShaderConfig.cs | 9 +++------ .../Translation/TranslatorContext.cs | 3 +++ 4 files changed, 9 insertions(+), 36 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index d206aad0b..0001243d4 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -17,8 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly ResourceCounts _resourceCounts; private readonly int _stageIndex; - private readonly int[] _constantBufferBindings; - /// <summary> /// Creates a new GPU accessor. /// </summary> @@ -28,12 +26,6 @@ namespace Ryujinx.Graphics.Gpu.Shader _context = context; _resourceCounts = resourceCounts; _stageIndex = stageIndex; - - if (context.Capabilities.Api != TargetApi.Vulkan) - { - _constantBufferBindings = new int[Constants.TotalGpUniformBuffers]; - _constantBufferBindings.AsSpan().Fill(-1); - } } public int QueryBindingConstantBuffer(int index) @@ -45,15 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } else { - int binding = _constantBufferBindings[index]; - - if (binding < 0) - { - binding = _resourceCounts.UniformBuffersCount++; - _constantBufferBindings[index] = binding; - } - - return binding; + return _resourceCounts.UniformBuffersCount++; } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index b31790d34..a2cfbe227 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly HashSet<int> _usedConstantBufferBindings; + public ShaderProperties Properties => _properties; + public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties) { _gpuAccessor = gpuAccessor; @@ -98,19 +100,6 @@ namespace Ryujinx.Graphics.Shader.Translation _properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); } - public void InheritFrom(ResourceManager other) - { - for (int i = 0; i < other._cbSlotToBindingMap.Length; i++) - { - int binding = other._cbSlotToBindingMap[i]; - - if (binding >= 0) - { - _cbSlotToBindingMap[i] = binding; - } - } - } - public static string GetShaderStagePrefix(ShaderStage stage) { uint index = (uint)stage; diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 73525cb27..40a32e2dc 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -39,9 +39,9 @@ namespace Ryujinx.Graphics.Shader.Translation public TranslationOptions Options { get; } - public ShaderProperties Properties { get; } + public ShaderProperties Properties => ResourceManager.Properties; - public ResourceManager ResourceManager { get; } + public ResourceManager ResourceManager { get; set; } public bool TransformFeedbackEnabled { get; } @@ -159,8 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation _sbSlots = new Dictionary<int, int>(); _sbSlotsReverse = new Dictionary<int, int>(); - Properties = new ShaderProperties(); - ResourceManager = new ResourceManager(stage, gpuAccessor, Properties); + ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); } public ShaderConfig( @@ -429,8 +428,6 @@ namespace Ryujinx.Graphics.Shader.Translation public void InheritFrom(ShaderConfig other) { - ResourceManager.InheritFrom(other.ResourceManager); - ClipDistancesWritten |= other.ClipDistancesWritten; UsedFeatures |= other.UsedFeatures; diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 4b4cc8d9f..9647b13f1 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -155,6 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation { other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>()); + // We need to share the resource manager since both shaders accesses the same constant buffers. + other._config.ResourceManager = _config.ResourceManager; + FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart); code = Combine(otherCode, code, aStart); From 232237bf287c4cd29b3d1032b61286af0f7fffef Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 31 May 2023 17:51:11 -0300 Subject: [PATCH 586/737] Skip draws with zero vertex count (#5149) --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index dcffa2473..1ee03536d 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.Vulkan public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { - if (!_program.IsLinked) + if (!_program.IsLinked || vertexCount == 0) { return; } @@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { - if (!_program.IsLinked) + if (!_program.IsLinked || indexCount == 0) { return; } From 92b0b7d753a07b9865386881524838fe42f3668a Mon Sep 17 00:00:00 2001 From: yell0wsuit <5692900+yell0wsuit@users.noreply.github.com> Date: Thu, 1 Jun 2023 04:03:11 +0700 Subject: [PATCH 587/737] Avalonia UI: Fix letter "x" in Ryujinx logo being cut off (#5176) Also make the pronunciation center-aligned --- src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml index 28a851d67..cc7556a6b 100644 --- a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml @@ -58,11 +58,20 @@ JustifyContent="SpaceAround" RowSpacing="2"> <TextBlock + HorizontalAlignment="Center" + VerticalAlignment="Center" FontSize="28" FontWeight="Bold" Text="Ryujinx" - TextAlignment="Left" /> - <TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" /> + TextAlignment="Center" + Width="100" /> + <TextBlock + HorizontalAlignment="Center" + VerticalAlignment="Center" + FontSize="11" + Text="(REE-YOU-JINX)" + TextAlignment="Center" + Width="100" /> </flex:FlexPanel> </Grid> <TextBlock From c6676007bfdc65724afebac27990c47a5d6aa3dd Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Wed, 31 May 2023 22:43:20 +0100 Subject: [PATCH 588/737] GPU: Dispose Renderer after running deferred actions (#5144) * GAL: Dispose Renderer after running deferred actions Deferred actions from disposing physical memory instances always dispose the resources in their caches. The renderer can't be disposed before these resources get disposed, otherwise the dispose actions will not actually run, and the ThreadedRenderer may get stuck trying to enqueue too many commands when there is nothing consuming them. This should fix most instances of the emulator freezing on close. * Wait for main render commands to finish, but keep RenderThread alive til dispose * Address some feedback. * No parameterize needed * Set thread name as part of constructor * Port to Ava and SDL2 --- src/Ryujinx.Ava/AppHost.cs | 19 ++++++++++--- src/Ryujinx.Graphics.GAL/IRenderer.cs | 3 +- .../Multithreading/ThreadedRenderer.cs | 28 ++++++++++++------- src/Ryujinx.Graphics.Gpu/GpuContext.cs | 3 +- src/Ryujinx.Headless.SDL2/WindowBase.cs | 15 +++++++++- src/Ryujinx/Ui/RendererWidgetBase.cs | 15 +++++++++- 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 349b64b41..2502fa41b 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -92,6 +92,8 @@ namespace Ryujinx.Ava private bool _isActive; private bool _renderingStarted; + private ManualResetEvent _gpuDoneEvent; + private IRenderer _renderer; private readonly Thread _renderingThread; private readonly CancellationTokenSource _gpuCancellationTokenSource; @@ -183,6 +185,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; _gpuCancellationTokenSource = new CancellationTokenSource(); + _gpuDoneEvent = new ManualResetEvent(false); } private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e) @@ -423,10 +426,10 @@ namespace Ryujinx.Ava _isActive = false; - if (_renderingThread.IsAlive) - { - _renderingThread.Join(); - } + // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. + // We only need to wait for all commands submitted during the main gpu loop to be processed. + _gpuDoneEvent.WaitOne(); + _gpuDoneEvent.Dispose(); DisplaySleep.Restore(); @@ -917,6 +920,14 @@ namespace Ryujinx.Ava UpdateStatus(); } } + + // Make sure all commands in the run loop are fully executed before leaving the loop. + if (Device.Gpu.Renderer is ThreadedRenderer threaded) + { + threaded.FlushThreadedCommands(); + } + + _gpuDoneEvent.Set(); }); (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null); diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs index d36dd26b6..b668d56ec 100644 --- a/src/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration; using System; +using System.Threading; namespace Ryujinx.Graphics.GAL { @@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.GAL void ResetCounter(CounterType type); - void RunLoop(Action gpuLoop) + void RunLoop(ThreadStart gpuLoop) { gpuLoop(); } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 3e179621e..e6169d895 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -30,7 +30,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading private IRenderer _baseRenderer; private Thread _gpuThread; private Thread _backendThread; - private bool _disposed; private bool _running; private AutoResetEvent _frameComplete = new AutoResetEvent(true); @@ -98,19 +97,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading _refQueue = new object[MaxRefsPerCommand * QueueCount]; } - public void RunLoop(Action gpuLoop) + public void RunLoop(ThreadStart gpuLoop) { _running = true; _backendThread = Thread.CurrentThread; - _gpuThread = new Thread(() => { - gpuLoop(); - _running = false; - _galWorkAvailable.Set(); - }); + _gpuThread = new Thread(gpuLoop) + { + Name = "GPU.MainThread" + }; - _gpuThread.Name = "GPU.MainThread"; _gpuThread.Start(); RenderLoop(); @@ -120,7 +117,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading { // Power through the render queue until the Gpu thread work is done. - while (_running && !_disposed) + while (_running) { _galWorkAvailable.Wait(); _galWorkAvailable.Reset(); @@ -488,12 +485,23 @@ namespace Ryujinx.Graphics.GAL.Multithreading return _baseRenderer.PrepareHostMapping(address, size); } + public void FlushThreadedCommands() + { + SpinWait wait = new(); + + while (Volatile.Read(ref _commandCount) > 0) + { + wait.SpinOnce(); + } + } + public void Dispose() { // Dispose must happen from the render thread, after all commands have completed. // Stop the GPU thread. - _disposed = true; + _running = false; + _galWorkAvailable.Set(); if (_gpuThread != null && _gpuThread.IsAlive) { diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index 0fe6a28ff..233227b4d 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -390,7 +390,6 @@ namespace Ryujinx.Graphics.Gpu /// </summary> public void Dispose() { - Renderer.Dispose(); GPFifo.Dispose(); HostInitalized.Dispose(); @@ -403,6 +402,8 @@ namespace Ryujinx.Graphics.Gpu PhysicalMemoryRegistry.Clear(); RunDeferredActions(); + + Renderer.Dispose(); } } } \ No newline at end of file diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index 7c3101535..d163da229 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -62,6 +62,7 @@ namespace Ryujinx.Headless.SDL2 private readonly long _ticksPerFrame; private readonly CancellationTokenSource _gpuCancellationTokenSource; private readonly ManualResetEvent _exitEvent; + private readonly ManualResetEvent _gpuDoneEvent; private long _ticks; private bool _isActive; @@ -91,6 +92,7 @@ namespace Ryujinx.Headless.SDL2 _ticksPerFrame = Stopwatch.Frequency / TargetFps; _gpuCancellationTokenSource = new CancellationTokenSource(); _exitEvent = new ManualResetEvent(false); + _gpuDoneEvent = new ManualResetEvent(false); _aspectRatio = aspectRatio; _enableMouse = enableMouse; HostUiTheme = new HeadlessHostUiTheme(); @@ -275,6 +277,14 @@ namespace Ryujinx.Headless.SDL2 _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); } } + + // Make sure all commands in the run loop are fully executed before leaving the loop. + if (Device.Gpu.Renderer is ThreadedRenderer threaded) + { + threaded.FlushThreadedCommands(); + } + + _gpuDoneEvent.Set(); }); FinalizeWindowRenderer(); @@ -404,7 +414,10 @@ namespace Ryujinx.Headless.SDL2 MainLoop(); - renderLoopThread.Join(); + // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. + // We only need to wait for all commands submitted during the main gpu loop to be processed. + _gpuDoneEvent.WaitOne(); + _gpuDoneEvent.Dispose(); nvStutterWorkaround?.Join(); Exit(); diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index e2cba7775..87ff7f6c0 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -65,6 +65,7 @@ namespace Ryujinx.Ui private KeyboardHotkeyState _prevHotkeyState; private readonly ManualResetEvent _exitEvent; + private readonly ManualResetEvent _gpuDoneEvent; private readonly CancellationTokenSource _gpuCancellationTokenSource; @@ -110,6 +111,7 @@ namespace Ryujinx.Ui | EventMask.KeyReleaseMask)); _exitEvent = new ManualResetEvent(false); + _gpuDoneEvent = new ManualResetEvent(false); _gpuCancellationTokenSource = new CancellationTokenSource(); @@ -499,6 +501,14 @@ namespace Ryujinx.Ui _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); } } + + // Make sure all commands in the run loop are fully executed before leaving the loop. + if (Device.Gpu.Renderer is ThreadedRenderer threaded) + { + threaded.FlushThreadedCommands(); + } + + _gpuDoneEvent.Set(); }); } @@ -542,7 +552,10 @@ namespace Ryujinx.Ui MainLoop(); - renderLoopThread.Join(); + // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. + // We only need to wait for all commands submitted during the main gpu loop to be processed. + _gpuDoneEvent.WaitOne(); + _gpuDoneEvent.Dispose(); nvStutterWorkaround?.Join(); Exit(); From 4741a05df95dd8964543f9770d8bfe15d842beaf Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Thu, 1 Jun 2023 08:05:39 +0100 Subject: [PATCH 589/737] Vulkan: Include DepthMode in ProgramPipelineState (#5185) --- src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs | 2 ++ .../Engine/Threed/StateUpdater.cs | 6 +++++- .../Shader/ShaderSpecializationState.cs | 15 +++++++++++++++ src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs b/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs index 41afb34b0..96fd667ad 100644 --- a/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs +++ b/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs @@ -63,6 +63,8 @@ namespace Ryujinx.Graphics.GAL public bool PrimitiveRestartEnable; public uint PatchControlPoints; + public DepthMode DepthMode; + public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) { VertexAttribCount = vertexAttribs.Length; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 4feb8bafc..5fa4702b8 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -771,7 +771,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> private void UpdateDepthMode() { - _context.Renderer.Pipeline.SetDepthMode(GetDepthMode()); + DepthMode mode = GetDepthMode(); + + _pipeline.DepthMode = mode; + + _context.Renderer.Pipeline.SetDepthMode(mode); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index b2c4fccdb..9b0c8b9b9 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -736,6 +736,19 @@ namespace Ryujinx.Graphics.Gpu.Shader return MatchesTexture(specializationState, descriptor); } + /// <summary> + /// Populates pipeline state that doesn't exist in older caches with default values + /// based on specialization state. + /// </summary> + /// <param name="pipelineState">Pipeline state to prepare</param> + private void PreparePipelineState(ref ProgramPipelineState pipelineState) + { + if (!_compute) + { + pipelineState.DepthMode = GraphicsState.DepthMode ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne; + } + } + /// <summary> /// Reads shader specialization state that has been serialized. /// </summary> @@ -776,6 +789,8 @@ namespace Ryujinx.Graphics.Gpu.Shader { ProgramPipelineState pipelineState = default; dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic); + + specState.PreparePipelineState(ref pipelineState); specState.PipelineState = pipelineState; } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 79179ce07..a52b44622 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -165,6 +165,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.DepthTestEnable = state.DepthTest.TestEnable; pipeline.DepthWriteEnable = state.DepthTest.WriteEnable; pipeline.DepthCompareOp = state.DepthTest.Func.Convert(); + pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne; pipeline.FrontFace = state.FrontFace.Convert(); From e3c6be5e29afd32fe570b743c82e6518a0bd2a40 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 1 Jun 2023 09:42:49 +0200 Subject: [PATCH 590/737] Only run one workflow for a PR at a time (#5137) --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d379437b2..97db387f0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,10 @@ on: - '*.yml' - 'README.md' +concurrency: + group: pr-checks-${{ github.event.number }} + cancel-in-progress: true + env: POWERSHELL_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1 From 12c62fdbc223af1fcd0aca9f0146a6dff38ec1b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:35:04 +0000 Subject: [PATCH 591/737] nuget: bump DynamicData from 7.13.8 to 7.14.2 (#5148) Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.8 to 7.14.2. - [Release notes](https://github.com/reactiveui/DynamicData/releases) - [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md) - [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.8...7.14.2) --- updated-dependencies: - dependency-name: DynamicData dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 9991b65a9..0b0861405 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,7 +13,7 @@ <PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> - <PackageVersion Include="DynamicData" Version="7.13.8" /> + <PackageVersion Include="DynamicData" Version="7.14.2" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> From f4539c49d8def4c6086a61fe4bd9ed9665fac4b1 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:47:53 +0200 Subject: [PATCH 592/737] [Logger] Add print with stacktrace method (#5129) * Add print with stacktrace method * Adjust logging namespaces * Add static keyword to DynamicObjectFormatter --- .../Logging/Formatters/DefaultLogFormatter.cs | 15 ++++++++++++--- .../Logging/Formatters/DynamicObjectFormatter.cs | 6 +++--- .../Logging/Formatters/ILogFormatter.cs | 4 ++-- src/Ryujinx.Common/Logging/LogEventArgsJson.cs | 3 ++- src/Ryujinx.Common/Logging/Logger.cs | 15 +++++++++++++-- .../Logging/Targets/AsyncLogTargetWrapper.cs | 2 +- .../Logging/Targets/ConsoleLogTarget.cs | 7 ++++--- .../Logging/Targets/FileLogTarget.cs | 5 +++-- src/Ryujinx.Common/Logging/Targets/ILogTarget.cs | 2 +- .../Logging/Targets/JsonLogTarget.cs | 2 +- src/Ryujinx.Headless.SDL2/Program.cs | 1 + .../Configuration/LoggerModule.cs | 1 + 12 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs index 28a7d5461..3769b03a7 100644 --- a/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs +++ b/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs @@ -1,6 +1,7 @@ -using System.Text; +using System.Diagnostics; +using System.Text; -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Formatters { internal class DefaultLogFormatter : ILogFormatter { @@ -27,6 +28,14 @@ namespace Ryujinx.Common.Logging if (args.Data is not null) { + if (args.Data is StackTrace trace) + { + sb.Append('\n'); + sb.Append(trace); + + return sb.ToString(); + } + sb.Append(' '); DynamicObjectFormatter.Format(sb, args.Data); } @@ -39,4 +48,4 @@ namespace Ryujinx.Common.Logging } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs index 5f15cc2a6..6e3b0043d 100644 --- a/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs +++ b/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs @@ -3,9 +3,9 @@ using System; using System.Reflection; using System.Text; -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Formatters { - internal class DynamicObjectFormatter + internal static class DynamicObjectFormatter { private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); @@ -17,7 +17,7 @@ namespace Ryujinx.Common.Logging } StringBuilder sb = StringBuilderPool.Allocate(); - + try { Format(sb, dynamicObject); diff --git a/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs index 9a55bc6b0..25a06d831 100644 --- a/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs +++ b/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs @@ -1,7 +1,7 @@ -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Formatters { interface ILogFormatter { string Format(LogEventArgs args); } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Common/Logging/LogEventArgsJson.cs b/src/Ryujinx.Common/Logging/LogEventArgsJson.cs index 425b97662..5203b17a6 100644 --- a/src/Ryujinx.Common/Logging/LogEventArgsJson.cs +++ b/src/Ryujinx.Common/Logging/LogEventArgsJson.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.Common.Logging.Formatters; +using System; using System.Text.Json.Serialization; namespace Ryujinx.Common.Logging diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs index 4d48dd48d..25f9c5eee 100644 --- a/src/Ryujinx.Common/Logging/Logger.cs +++ b/src/Ryujinx.Common/Logging/Logger.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging.Targets; using Ryujinx.Common.SystemInterop; using System; using System.Collections.Generic; @@ -55,6 +56,16 @@ namespace Ryujinx.Common.Logging } } + [StackTraceHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PrintStack(LogClass logClass, string message, [CallerMemberName] string caller = "") + { + if (m_EnabledClasses[(int)logClass]) + { + Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), new StackTrace(true))); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "") { @@ -122,7 +133,7 @@ namespace Ryujinx.Common.Logging AsyncLogTargetOverflowAction.Discard)); Notice = new Log(LogLevel.Notice); - + // Enable important log levels before configuration is loaded Error = new Log(LogLevel.Error); Warning = new Log(LogLevel.Warning); @@ -221,4 +232,4 @@ namespace Ryujinx.Common.Logging m_EnabledClasses[(int)logClass] = enabled; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs index 43c62d319..ddc547acd 100644 --- a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs +++ b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; using System.Threading; -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Targets { public enum AsyncLogTargetOverflowAction { diff --git a/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs index 7b77c4f28..b5986461e 100644 --- a/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs @@ -1,6 +1,7 @@ -using System; +using Ryujinx.Common.Logging.Formatters; +using System; -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Targets { public class ConsoleLogTarget : ILogTarget { @@ -38,4 +39,4 @@ namespace Ryujinx.Common.Logging Console.ResetColor(); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs index 24dd6d179..2cc4a8237 100644 --- a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs @@ -1,8 +1,9 @@ -using System; +using Ryujinx.Common.Logging.Formatters; +using System; using System.IO; using System.Linq; -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Targets { public class FileLogTarget : ILogTarget { diff --git a/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs index d4d26a936..e3b1ad208 100644 --- a/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Targets { public interface ILogTarget : IDisposable { diff --git a/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs index 06976433e..ae264e593 100644 --- a/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Utilities; using System.IO; -namespace Ryujinx.Common.Logging +namespace Ryujinx.Common.Logging.Targets { public class JsonLogTarget : ILogTarget { diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index fc1b23ebb..6e6b4a7f3 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -9,6 +9,7 @@ using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Logging; +using Ryujinx.Common.Logging.Targets; using Ryujinx.Common.SystemInterop; using Ryujinx.Common.Utilities; using Ryujinx.Cpu; diff --git a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs index f4712213e..85b50e763 100644 --- a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs +++ b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs @@ -1,5 +1,6 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Logging.Targets; using System; namespace Ryujinx.Ui.Common.Configuration From 57524a4c8afef5c9513ed1eebb6cdf806922d06c Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Thu, 1 Jun 2023 17:32:38 +0200 Subject: [PATCH 593/737] =?UTF-8?q?Add=C2=A0issue=C2=A0template=20for?= =?UTF-8?q?=C2=A0missing=20shader=C2=A0instructions=20(#5048)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add issue template for missing shader instructions * fixup! Add issue template for missing shader instructions * Update .github/ISSUE_TEMPLATE/missing_shader_instruction.yml --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../missing_shader_instruction.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/missing_shader_instruction.yml diff --git a/.github/ISSUE_TEMPLATE/missing_shader_instruction.yml b/.github/ISSUE_TEMPLATE/missing_shader_instruction.yml new file mode 100644 index 000000000..df37859a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/missing_shader_instruction.yml @@ -0,0 +1,19 @@ +name: Missing Shader Instruction +description: Shader Instruction is missing in Ryujinx. +title: "[GPU]" +labels: [gpu, not-implemented] +body: + - type: textarea + id: instruction + attributes: + label: Shader instruction + description: What shader instruction is missing? + validations: + required: true + - type: textarea + id: required + attributes: + label: Required by + description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction. + validations: + required: true From 6966211e0723685ccf1895e34e3c9e93c52fbb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Arrouye?= <5017270+tarrouye@users.noreply.github.com> Date: Thu, 1 Jun 2023 08:40:44 -0700 Subject: [PATCH 594/737] Give Library header DockPanel explicit height (#5160) --- src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml index ac8ede7ae..f7dbf2b21 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml @@ -16,6 +16,7 @@ </Design.DataContext> <DockPanel Margin="0,0,0,5" + Height="35" HorizontalAlignment="Stretch"> <Button Width="40" From b8f48bcf640acae519602a26fd6c4ce49c04709b Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Thu, 1 Jun 2023 18:24:00 +0200 Subject: [PATCH 595/737] UI: Fix empty homebrew icon (#5189) * UI: Fix empty homebrew icon We currently don't check the icon size when we read it from the homebrew data. That could cause issues at UI side since the buffer isn't null but empty. Extra check have been added UI side too. (I cleaned up some files during my research too) Fixes #5188 * Remove additional check * Remove unused using --- .../UI/Helpers/BitmapArrayValueConverter.cs | 1 + .../UI/Views/Main/MainMenuBarView.axaml.cs | 2 -- .../App/ApplicationLibrary.cs | 18 ++++++++++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs index 3fd368f89..133f8dbc9 100644 --- a/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Ava.UI.Helpers if (value is byte[] buffer && targetType == typeof(IImage)) { MemoryStream mem = new(buffer); + return new Bitmap(mem); } diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index 557528eb1..f94b80d45 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -9,9 +9,7 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Utilities; -using Ryujinx.HLE.HOS; using Ryujinx.Modules; -using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 0407036a0..f52af611e 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -343,7 +343,14 @@ namespace Ryujinx.Ui.App.Common ulong nacpSize = reader.ReadUInt64(); // Reads and stores game icon as byte array - applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); + if (iconSize > 0) + { + applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); + } + else + { + applicationIcon = _nroIcon; + } // Read the NACP data Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); @@ -666,7 +673,14 @@ namespace Ryujinx.Ui.App.Common long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); // Reads and stores game icon as byte array - applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); + if (iconSize > 0) + { + applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); + } + else + { + applicationIcon = _nroIcon; + } } else { From 96ea4e8c8e38f42073c1f8d7fb66c89a663e95e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 21:03:00 +0200 Subject: [PATCH 596/737] nuget: bump Microsoft.NET.Test.Sdk from 17.6.0 to 17.6.1 (#5192) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.0 to 17.6.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.0...v17.6.1) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0b0861405..9922f0719 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,7 +21,7 @@ <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> - <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.0" /> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.1" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> From c545c598512f57de2d178f78095f8bc7b31f07c3 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Sat, 3 Jun 2023 05:37:00 +0200 Subject: [PATCH 597/737] ava: Fix exit dialog while guest is running. (#5207) * ava: Fix exit dialog while guest is running. There is currently an issue while a game runs, the content dialog creation method check if `IsGameRunning` is true to show the popup. But the condition here is wrong (`window` is null) so it throw a NullException silently in `Dispatcher.UIThread`. This is now fixed by using the right casting. * improve condition * Fix spacing --- src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs | 2 +- src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index d85895fc8..045d508c6 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -318,7 +318,7 @@ namespace Ryujinx.Ava.UI.Helpers Window parent = GetMainWindow(); - if (parent is { IsActive: true } and MainWindow window && window.ViewModel.IsGameRunning) + if (parent != null && parent.IsActive && (parent as MainWindow).ViewModel.IsGameRunning) { contentDialogOverlayWindow = new() { diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index cf84807e3..66988c4b3 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -519,14 +519,14 @@ namespace Ryujinx.Ava.UI.Windows private void ConfirmExit() { Dispatcher.UIThread.InvokeAsync(async () => - { - ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog(); + { + ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog(); - if (ViewModel.IsClosing) - { - Close(); - } - }); + if (ViewModel.IsClosing) + { + Close(); + } + }); } public async void LoadApplications() From 8a352df3c676e347c267919f2d9a56d75daef348 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 3 Jun 2023 00:43:00 -0300 Subject: [PATCH 598/737] Allow BGRA images on Vulkan (#5203) --- src/Ryujinx.Graphics.GAL/Format.cs | 1 + .../Effects/FsrScalingFilter.cs | 8 ++-- .../Effects/FxaaPostProcessingEffect.cs | 25 +----------- .../Effects/SmaaPostProcessingEffect.cs | 38 ++++--------------- src/Ryujinx.Graphics.Vulkan/FormatTable.cs | 10 +++++ 5 files changed, 24 insertions(+), 58 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index 5e0274e58..7e0e07d48 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -383,6 +383,7 @@ namespace Ryujinx.Graphics.GAL case Format.R10G10B10A2Unorm: case Format.R10G10B10A2Uint: case Format.R11G11B10Float: + case Format.B8G8R8A8Unorm: return true; } diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index e9952126f..7317b567a 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -96,8 +96,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects { var originalInfo = view.Info; - var swapRB = originalInfo.Format.IsBgr() && originalInfo.SwizzleR == SwizzleComponent.Red; - var info = new TextureCreateInfo( width, height, @@ -110,9 +108,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects originalInfo.Format, originalInfo.DepthStencilMode, originalInfo.Target, - swapRB ? originalInfo.SwizzleB : originalInfo.SwizzleR, + originalInfo.SwizzleR, originalInfo.SwizzleG, - swapRB ? originalInfo.SwizzleR : originalInfo.SwizzleB, + originalInfo.SwizzleB, originalInfo.SwizzleA); _intermediaryTexture?.Dispose(); _intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; @@ -155,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); - _pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index 9da003dda..3c3516bbf 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -56,28 +56,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height) { _texture?.Dispose(); - - var info = view.Info; - - if (view.Info.Format.IsBgr()) - { - info = new TextureCreateInfo(info.Width, - info.Height, - info.Depth, - info.Levels, - info.Samples, - info.BlockWidth, - info.BlockHeight, - info.BytesPerPixel, - info.Format, - info.DepthStencilMode, - info.Target, - info.SwizzleB, - info.SwizzleG, - info.SwizzleR, - info.SwizzleA); - } - _texture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + _texture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; } _pipeline.SetCommandBuffer(cbs); @@ -96,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); - _pipeline.SetImage(0, _texture, GAL.Format.R8G8B8A8Unorm); + _pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _renderer.BufferManager.Delete(bufferHandle); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index 0d392a65f..f6de3ac2e 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; using Silk.NET.Vulkan; using System; -using Format = Ryujinx.Graphics.GAL.Format; namespace Ryujinx.Graphics.Vulkan.Effects { @@ -149,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects 1, 1, 1, - Format.R8G8Unorm, + GAL.Format.R8G8Unorm, DepthStencilMode.Depth, Target.Texture2D, SwizzleComponent.Red, @@ -165,7 +164,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects 1, 1, 1, - Format.R8Unorm, + GAL.Format.R8Unorm, DepthStencilMode.Depth, Target.Texture2D, SwizzleComponent.Red, @@ -192,30 +191,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects _edgeOutputTexture?.Dispose(); _blendOutputTexture?.Dispose(); - var info = view.Info; - - if (view.Info.Format.IsBgr()) - { - info = new TextureCreateInfo(info.Width, - info.Height, - info.Depth, - info.Levels, - info.Samples, - info.BlockWidth, - info.BlockHeight, - info.BytesPerPixel, - info.Format, - info.DepthStencilMode, - info.Target, - info.SwizzleB, - info.SwizzleG, - info.SwizzleR, - info.SwizzleA); - } - - _outputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; - _edgeOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; - _blendOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + _outputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; + _edgeOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; + _blendOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; } _pipeline.SetCommandBuffer(cbs); @@ -240,7 +218,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); - _pipeline.SetImage(0, _edgeOutputTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); @@ -250,7 +228,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear); - _pipeline.SetImage(0, _blendOutputTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.SetImage(0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); @@ -259,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.Specialize(_specConstants); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); - _pipeline.SetImage(0, _outputTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.SetImage(0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs index a030d8c85..3d70f6f26 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs @@ -169,6 +169,16 @@ namespace Ryujinx.Graphics.Vulkan return _table[(int)format]; } + public static Format ConvertRgba8SrgbToUnorm(Format format) + { + return format switch + { + Format.R8G8B8A8Srgb => Format.R8G8B8A8Unorm, + Format.B8G8R8A8Srgb => Format.B8G8R8A8Unorm, + _ => format + }; + } + public static int GetAttributeFormatSize(VkFormat format) { switch (format) From 52cf1418743950fde932e92fcf9655d5c392d9d7 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Sat, 3 Jun 2023 10:23:51 +0200 Subject: [PATCH 599/737] Armeilleure: Fix support for Windows on ARM64 (#5202) * Armeilleure: Fix support for Windows on ARM64 Tested on Windows DevKit 2023. * Address gdkchan's comments --- src/ARMeilleure/Translation/Cache/JitCache.cs | 22 ++++++++++++++++--- .../Translation/Cache/JitCacheInvalidation.cs | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index f496a8e9c..daa2eeac2 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -6,10 +6,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.Versioning; namespace ARMeilleure.Translation.Cache { - static class JitCache + static partial class JitCache { private const int PageSize = 4 * 1024; private const int PageMask = PageSize - 1; @@ -27,6 +28,10 @@ namespace ARMeilleure.Translation.Cache private static readonly object _lock = new object(); private static bool _initialized; + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32.dll", SetLastError = true)] + public static partial IntPtr FlushInstructionCache(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize); + public static void Initialize(IJitMemoryAllocator allocator) { if (_initialized) return; @@ -36,7 +41,11 @@ namespace ARMeilleure.Translation.Cache if (_initialized) return; _jitRegion = new ReservedRegion(allocator, CacheSize); - _jitCacheInvalidator = new JitCacheInvalidation(allocator); + + if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) + { + _jitCacheInvalidator = new JitCacheInvalidation(allocator); + } _cacheAllocator = new CacheMemoryAllocator(CacheSize); @@ -77,7 +86,14 @@ namespace ARMeilleure.Translation.Cache Marshal.Copy(code, 0, funcPtr, code.Length); ReprotectAsExecutable(funcOffset, code.Length); - _jitCacheInvalidator.Invalidate(funcPtr, (ulong)code.Length); + if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (UIntPtr)code.Length); + } + else + { + _jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length); + } } Add(funcOffset, code.Length, func.UnwindInfo); diff --git a/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs b/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs index ec2ae73bb..57f7bf121 100644 --- a/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs +++ b/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs @@ -47,8 +47,8 @@ namespace ARMeilleure.Translation.Cache public JitCacheInvalidation(IJitMemoryAllocator allocator) { - // On macOS, a different path is used to write to the JIT cache, which does the invalidation. - if (!OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + // On macOS and Windows, a different path is used to write to the JIT cache, which does the invalidation. + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { ulong size = (ulong)_invalidationCode.Length * sizeof(int); ulong mask = (ulong)ReservedRegion.DefaultGranularity - 1; From 9367e3c35d4ad26625e5bec7d144cc12ba0ee069 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Sat, 3 Jun 2023 11:03:34 +0200 Subject: [PATCH 600/737] ava: Fix Open Applet menu enabled (#5206) Currently, the `Open Applet` menu is still enabled when a guest is running, which is wrong. This is not fixed by refreshing the property binding on `IsEnabled`. --- src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index e7013968d..409ccad3c 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -257,6 +257,7 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); OnPropertyChanged(nameof(EnableNonGameRunningControls)); + OnPropertyChanged(nameof(IsAppletMenuActive)); OnPropertyChanged(nameof(StatusBarVisible)); OnPropertyChanged(nameof(ShowFirmwareStatus)); } From 81c9052847f1aa4a70010fefa8e6ee38b5ace612 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Sat, 3 Jun 2023 17:03:42 +0200 Subject: [PATCH 601/737] ava: Fix Input Touch (#5204) --- src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs | 59 +++++++++----------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs index 8f64aa28d..b7e5a4d95 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; -using FluentAvalonia.Core; using Ryujinx.Input; using System; using System.Numerics; @@ -30,14 +29,14 @@ namespace Ryujinx.Ava.Input _window = window; _widget.PointerMoved += Parent_PointerMovedEvent; - _widget.PointerPressed += Parent_PointerPressEvent; - _widget.PointerReleased += Parent_PointerReleaseEvent; - _widget.PointerWheelChanged += Parent_ScrollEvent; + _widget.PointerPressed += Parent_PointerPressedEvent; + _widget.PointerReleased += Parent_PointerReleasedEvent; + _widget.PointerWheelChanged += Parent_PointerWheelChanged; _window.PointerMoved += Parent_PointerMovedEvent; - _window.PointerPressed += Parent_PointerPressEvent; - _window.PointerReleased += Parent_PointerReleaseEvent; - _window.PointerWheelChanged += Parent_ScrollEvent; + _window.PointerPressed += Parent_PointerPressedEvent; + _window.PointerReleased += Parent_PointerReleasedEvent; + _window.PointerWheelChanged += Parent_PointerWheelChanged; PressedButtons = new bool[(int)MouseButton.Count]; @@ -63,29 +62,25 @@ namespace Ryujinx.Ava.Input _size = new Size((int)rect.Width, (int)rect.Height); } - private void Parent_ScrollEvent(object o, PointerWheelEventArgs args) + private void Parent_PointerWheelChanged(object o, PointerWheelEventArgs args) { Scroll = new Vector2((float)args.Delta.X, (float)args.Delta.Y); } - private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args) + private void Parent_PointerReleasedEvent(object o, PointerReleasedEventArgs args) { - if (args.InitialPressMouseButton != Avalonia.Input.MouseButton.None) - { - int button = (int)args.InitialPressMouseButton; + uint button = (uint)args.InitialPressMouseButton - 1; - if (PressedButtons.Count() >= button) - { - PressedButtons[button] = false; - } + if ((uint)PressedButtons.Length > button) + { + PressedButtons[button] = false; } } - - private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args) + private void Parent_PointerPressedEvent(object o, PointerPressedEventArgs args) { - int button = (int)args.GetCurrentPoint(_widget).Properties.PointerUpdateKind; + uint button = (uint)args.GetCurrentPoint(_widget).Properties.PointerUpdateKind; - if (PressedButtons.Count() >= button) + if ((uint)PressedButtons.Length > button) { PressedButtons[button] = true; } @@ -100,17 +95,17 @@ namespace Ryujinx.Ava.Input public void SetMousePressed(MouseButton button) { - if (PressedButtons.Count() >= (int)button) + if ((uint)PressedButtons.Length > (uint)button) { - PressedButtons[(int)button] = true; + PressedButtons[(uint)button] = true; } } public void SetMouseReleased(MouseButton button) { - if (PressedButtons.Count() >= (int)button) + if ((uint)PressedButtons.Length > (uint)button) { - PressedButtons[(int)button] = false; + PressedButtons[(uint)button] = false; } } @@ -121,9 +116,9 @@ namespace Ryujinx.Ava.Input public bool IsButtonPressed(MouseButton button) { - if (PressedButtons.Count() >= (int)button) + if ((uint)PressedButtons.Length > (uint)button) { - return PressedButtons[(int)button]; + return PressedButtons[(uint)button]; } return false; @@ -149,14 +144,14 @@ namespace Ryujinx.Ava.Input _isDisposed = true; _widget.PointerMoved -= Parent_PointerMovedEvent; - _widget.PointerPressed -= Parent_PointerPressEvent; - _widget.PointerReleased -= Parent_PointerReleaseEvent; - _widget.PointerWheelChanged -= Parent_ScrollEvent; + _widget.PointerPressed -= Parent_PointerPressedEvent; + _widget.PointerReleased -= Parent_PointerReleasedEvent; + _widget.PointerWheelChanged -= Parent_PointerWheelChanged; _window.PointerMoved -= Parent_PointerMovedEvent; - _window.PointerPressed -= Parent_PointerPressEvent; - _window.PointerReleased -= Parent_PointerReleaseEvent; - _window.PointerWheelChanged -= Parent_ScrollEvent; + _window.PointerPressed -= Parent_PointerPressedEvent; + _window.PointerReleased -= Parent_PointerReleasedEvent; + _window.PointerWheelChanged -= Parent_PointerWheelChanged; _widget = null; } From 21c9ac6240a3db3300143d1d0dd4a1070d4f576f Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 3 Jun 2023 20:12:18 -0300 Subject: [PATCH 602/737] Implement shader storage buffer operations using new Load/Store instructions (#4993) * Implement storage buffer operations using new Load/Store instruction * Extend GenerateMultiTargetStorageOp to also match access with constant offset, and log and comments * Remove now unused code * Catch more complex cases of global memory usage * Shader cache version bump * Extend global access elimination to work with more shared memory cases * Change alignment requirement from 16 bytes to 8 bytes, handle cases where we need more than 16 storage buffers * Tweak preferencing to catch more cases * Enable CB0 elimination even when host storage buffer alignment is > 16 (for Intel) * Fix storage buffer bindings * Simplify some code * Shader cache version bump * Fix typo * Extend global memory elimination to handle shared memory with multiple possible offsets and local memory --- src/Ryujinx.Graphics.Gpu/Constants.cs | 5 - .../Engine/Compute/ComputeClass.cs | 24 - .../Memory/BufferManager.cs | 2 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/ShaderInfoBuilder.cs | 15 +- .../CodeGen/Glsl/Declarations.cs | 60 +- .../CodeGen/Glsl/DefaultNames.cs | 6 - .../AtomicMinMaxS32Storage.glsl | 21 - .../HelperFunctions/StoreStorageSmallInt.glsl | 23 - .../CodeGen/Glsl/Instructions/InstGen.cs | 56 +- .../Glsl/Instructions/InstGenHelper.cs | 4 - .../Glsl/Instructions/InstGenMemory.cs | 89 +- .../CodeGen/Glsl/OperandManager.cs | 5 +- .../CodeGen/Spirv/CodeGenContext.cs | 11 +- .../CodeGen/Spirv/Declarations.cs | 76 +- .../CodeGen/Spirv/Instructions.cs | 119 +- src/Ryujinx.Graphics.Shader/Constants.cs | 2 - src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 2 +- .../Instructions/InstEmitFlowControl.cs | 2 + .../Instructions/InstEmitMemory.cs | 46 +- .../IntermediateRepresentation/Instruction.cs | 8 - .../IntermediateRepresentation/StorageKind.cs | 7 +- .../Ryujinx.Graphics.Shader.csproj | 2 - .../StructuredIr/HelperFunctionsMask.cs | 2 - .../StructuredIr/InstructionInfo.cs | 8 +- .../StructuredIr/ShaderProperties.cs | 8 + .../StructuredIr/StructuredProgram.cs | 8 - .../Translation/EmitterContextInsts.cs | 86 +- .../Translation/GlobalMemory.cs | 54 - .../Translation/HelperFunctionManager.cs | 11 +- .../Optimizations/GlobalToStorage.cs | 1361 ++++++++++++----- .../Translation/Optimizations/Optimizer.cs | 8 +- .../Optimizations/Simplification.cs | 68 +- .../Translation/Optimizations/Utils.cs | 13 + .../Translation/ResourceManager.cs | 104 ++ .../Translation/Rewriter.cs | 205 +-- .../Translation/ShaderConfig.cs | 151 +- .../Translation/ShaderIdentifier.cs | 14 +- .../Translation/Translator.cs | 2 +- src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 4 +- .../DescriptorSetCollection.cs | 24 - .../DescriptorSetUpdater.cs | 9 +- 42 files changed, 1468 insertions(+), 1259 deletions(-) delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs diff --git a/src/Ryujinx.Graphics.Gpu/Constants.cs b/src/Ryujinx.Graphics.Gpu/Constants.cs index b559edc25..ff90e61ba 100644 --- a/src/Ryujinx.Graphics.Gpu/Constants.cs +++ b/src/Ryujinx.Graphics.Gpu/Constants.cs @@ -80,11 +80,6 @@ namespace Ryujinx.Graphics.Gpu /// </summary> public const int GobAlignment = 64; - /// <summary> - /// Expected byte alignment for storage buffers - /// </summary> - public const int StorageAlignment = 16; - /// <summary> /// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses. /// </summary> diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 998ece224..8227a7ff1 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -187,30 +187,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute info = cs.Shaders[0].Info; } - for (int index = 0; index < info.CBuffers.Count; index++) - { - BufferDescriptor cb = info.CBuffers[index]; - - // NVN uses the "hardware" constant buffer for anything that is less than 8, - // and those are already bound above. - // Anything greater than or equal to 8 uses the emulated constant buffers. - // They are emulated using global memory loads. - if (cb.Slot < 8) - { - continue; - } - - ulong cbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0); - - int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10; - - cbDescAddress += (ulong)cbDescOffset; - - SbDescriptor cbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(cbDescAddress); - - _channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size); - } - _channel.BufferManager.SetComputeBufferBindings(cs.Bindings); _channel.TextureManager.SetComputeBindings(cs.Bindings); diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index e20e1bb68..48cb33b4d 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -222,7 +222,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="gpuVa">Start GPU virtual address of the buffer</param> private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa) { - bool unaligned = (gpuVa & (Constants.StorageAlignment - 1)) != 0; + bool unaligned = (gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1)) != 0; if (unaligned || HasUnalignedStorageBuffers) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 7f83f5880..4b828080d 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5027; + private const uint CodeGenVersion = 4992; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 39b31cf6a..3fc32d711 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Shader int imageBinding = stageIndex * imagesPerStage * 2; AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage); - AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage); + AddDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage); AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage); AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage); @@ -133,19 +133,6 @@ namespace Ryujinx.Graphics.Gpu.Shader AddDescriptor(stages, type2, setIndex, binding + count, count); } - /// <summary> - /// Adds an array resource to the list of descriptors. - /// </summary> - /// <param name="stages">Shader stages where the resource is used</param> - /// <param name="type">Type of the resource</param> - /// <param name="setIndex">Descriptor set number where the resource will be bound</param> - /// <param name="binding">Binding number where the resource will be bound</param> - /// <param name="count">Number of resources bound at the binding location</param> - private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count) - { - _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages)); - } - /// <summary> /// Adds buffer usage information to the list of usages. /// </summary> diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 1bd0182b5..958f1cef3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -104,14 +104,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); - - var sBufferDescriptors = context.Config.GetStorageBufferDescriptors(); - if (sBufferDescriptors.Length != 0) - { - DeclareStorages(context, sBufferDescriptors); - - context.AppendLine(); - } + DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); var textureDescriptors = context.Config.GetTextureDescriptors(); if (textureDescriptors.Length != 0) @@ -250,11 +243,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl"); } - if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Storage) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl"); - } - if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0) { AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl"); @@ -290,11 +278,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl"); } - if ((info.HelperFunctionsMask & HelperFunctionsMask.StoreStorageSmallInt) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl"); - } - if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) { AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl"); @@ -356,6 +339,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) + { + DeclareBuffers(context, buffers, "uniform"); + } + + private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) + { + DeclareBuffers(context, buffers, "buffer"); + } + + private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, string declType) { foreach (BufferDefinition buffer in buffers) { @@ -365,7 +358,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl _ => "std430" }; - context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}"); + context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}"); context.EnterScope(); foreach (StructureField field in buffer.Type.Fields) @@ -373,9 +366,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (field.Type.HasFlag(AggregateType.Array)) { string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); - string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture); - context.AppendLine($"{typeName} {field.Name}[{arraySize}];"); + if (field.ArrayLength > 0) + { + string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture); + + context.AppendLine($"{typeName} {field.Name}[{arraySize}];"); + } + else + { + context.AppendLine($"{typeName} {field.Name}[];"); + } } else { @@ -390,22 +391,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareStorages(CodeGenContext context, BufferDescriptor[] descriptors) - { - string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage); - - sbName += "_" + DefaultNames.StorageNamePrefix; - - string blockName = $"{sbName}_{DefaultNames.BlockSuffix}"; - - string layout = context.Config.Options.TargetApi == TargetApi.Vulkan ? ", set = 1" : string.Empty; - - context.AppendLine($"layout (binding = {context.Config.FirstStorageBufferBinding}{layout}, std430) buffer {blockName}"); - context.EnterScope(); - context.AppendLine("uint " + DefaultNames.DataName + "[];"); - context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];"); - } - private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) { int arraySize = 0; @@ -733,7 +718,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl code = code.Replace("\t", CodeGenContext.Tab); code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName); - code = code.Replace("$STORAGE_MEM$", OperandManager.GetShaderStagePrefix(context.Config.Stage) + "_" + DefaultNames.StorageNamePrefix); if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot()) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index fc3004a8f..5ee8259cf 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -11,12 +11,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string IAttributePrefix = "in_attr"; public const string OAttributePrefix = "out_attr"; - public const string StorageNamePrefix = "s"; - - public const string DataName = "data"; - - public const string BlockSuffix = "block"; - public const string LocalMemoryName = "local_mem"; public const string SharedMemoryName = "shared_mem"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl deleted file mode 100644 index 0862a71bf..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl +++ /dev/null @@ -1,21 +0,0 @@ -int Helper_AtomicMaxS32(int index, int offset, int value) -{ - uint oldValue, newValue; - do - { - oldValue = $STORAGE_MEM$[index].data[offset]; - newValue = uint(max(int(oldValue), value)); - } while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue); - return int(oldValue); -} - -int Helper_AtomicMinS32(int index, int offset, int value) -{ - uint oldValue, newValue; - do - { - oldValue = $STORAGE_MEM$[index].data[offset]; - newValue = uint(min(int(oldValue), value)); - } while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue); - return int(oldValue); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl deleted file mode 100644 index f2253a796..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl +++ /dev/null @@ -1,23 +0,0 @@ -void Helper_StoreStorage16(int index, int offset, uint value) -{ - int wordOffset = offset >> 2; - int bitOffset = (offset & 3) * 8; - uint oldValue, newValue; - do - { - oldValue = $STORAGE_MEM$[index].data[wordOffset]; - newValue = bitfieldInsert(oldValue, value, bitOffset, 16); - } while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue); -} - -void Helper_StoreStorage8(int index, int offset, uint value) -{ - int wordOffset = offset >> 2; - int bitOffset = (offset & 3) * 8; - uint oldValue, newValue; - do - { - oldValue = $STORAGE_MEM$[index].data[wordOffset]; - newValue = bitfieldInsert(oldValue, value, bitOffset, 8); - } while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 24ea66d02..01d8a6e7a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -68,33 +68,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string args = string.Empty; - for (int argIndex = 0; argIndex < arity; argIndex++) + if (atomic && operation.StorageKind == StorageKind.StorageBuffer) { + args = GenerateLoadOrStore(context, operation, isStore: false); + + AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32 + ? AggregateType.S32 + : AggregateType.U32; + + for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) + { + args += ", " + GetSoureExpr(context, operation.GetSource(argIndex), dstType); + } + } + else if (atomic && operation.StorageKind == StorageKind.SharedMemory) + { + args = LoadShared(context, operation); + // For shared memory access, the second argument is unused and should be ignored. // It is there to make both storage and shared access have the same number of arguments. // For storage, both inputs are consumed when the argument index is 0, so we should skip it here. - if (argIndex == 1 && (atomic || operation.StorageKind == StorageKind.SharedMemory)) - { - continue; - } - if (argIndex != 0) + for (int argIndex = 2; argIndex < arity; argIndex++) { args += ", "; - } - if (argIndex == 0 && atomic) + AggregateType dstType = GetSrcVarType(inst, argIndex); + + args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); + } + } + else + { + for (int argIndex = 0; argIndex < arity; argIndex++) { - switch (operation.StorageKind) + if (argIndex != 0) { - case StorageKind.SharedMemory: args += LoadShared(context, operation); break; - case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break; - - default: throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); + args += ", "; } - } - else - { + AggregateType dstType = GetSrcVarType(inst, argIndex); args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); @@ -173,9 +185,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.LoadShared: return LoadShared(context, operation); - case Instruction.LoadStorage: - return LoadStorage(context, operation); - case Instruction.Lod: return Lod(context, operation); @@ -203,15 +212,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.StoreShared8: return StoreShared8(context, operation); - case Instruction.StoreStorage: - return StoreStorage(context, operation); - - case Instruction.StoreStorage16: - return StoreStorage16(context, operation); - - case Instruction.StoreStorage8: - return StoreStorage8(context, operation); - case Instruction.TextureSample: return TextureSample(context, operation); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 6cf36a2a6..f42d98986 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.Load, InstType.Special); Add(Instruction.LoadLocal, InstType.Special); Add(Instruction.LoadShared, InstType.Special); - Add(Instruction.LoadStorage, InstType.Special); Add(Instruction.Lod, InstType.Special); Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); @@ -123,9 +122,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.StoreShared, InstType.Special); Add(Instruction.StoreShared16, InstType.Special); Add(Instruction.StoreShared8, InstType.Special); - Add(Instruction.StoreStorage, InstType.Special); - Add(Instruction.StoreStorage16, InstType.Special); - Add(Instruction.StoreStorage8, InstType.Special); Add(Instruction.Subtract, InstType.OpBinary, "-", 2); Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); Add(Instruction.TextureSample, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index dfc8197b6..c8084d9dd 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -210,17 +210,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"{arrayName}[{offsetExpr}]"; } - public static string LoadStorage(CodeGenContext context, AstOperation operation) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - - string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - - return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); - } - public static string Lod(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; @@ -326,60 +315,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})"; } - public static string StoreStorage(CodeGenContext context, AstOperation operation) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - IAstNode src3 = operation.GetSource(2); - - string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - - AggregateType srcType = OperandManager.GetNodeDestType(context, src3); - - string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32); - - string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); - - return $"{sb} = {src}"; - } - - public static string StoreStorage16(CodeGenContext context, AstOperation operation) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - IAstNode src3 = operation.GetSource(2); - - string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - - AggregateType srcType = OperandManager.GetNodeDestType(context, src3); - - string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32); - - string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); - - return $"{HelperFunctionNames.StoreStorage16}({indexExpr}, {offsetExpr}, {src})"; - } - - public static string StoreStorage8(CodeGenContext context, AstOperation operation) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - IAstNode src3 = operation.GetSource(2); - - string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - - AggregateType srcType = OperandManager.GetNodeDestType(context, src3); - - string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32); - - string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); - - return $"{HelperFunctionNames.StoreStorage8}({indexExpr}, {offsetExpr}, {src})"; - } - public static string TextureSample(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; @@ -701,25 +636,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } - private static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) + public static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) { StorageKind storageKind = operation.StorageKind; string varName; AggregateType varType; int srcIndex = 0; - int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount; + bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); + int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount; + + if (operation.Inst == Instruction.AtomicCompareAndSwap) + { + inputsCount--; + } switch (storageKind) { case StorageKind.ConstantBuffer: + case StorageKind.StorageBuffer: if (!(operation.GetSource(srcIndex++) is 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 = context.Config.Properties.ConstantBuffers[binding]; + BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer + ? context.Config.Properties.ConstantBuffers[binding] + : context.Config.Properties.StorageBuffers[binding]; if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant) { @@ -825,15 +769,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return varName; } - private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage) - { - string sbName = OperandManager.GetShaderStagePrefix(stage); - - sbName += "_" + DefaultNames.StorageNamePrefix; - - return $"{sbName}[{slotExpr}].{DefaultNames.DataName}[{offsetExpr}]"; - } - private static string GetMask(int index) { return $".{"rgba".AsSpan(index, 1)}"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index e34e4e076..4fd1d17c4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -118,6 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl switch (operation.StorageKind) { case StorageKind.ConstantBuffer: + case StorageKind.StorageBuffer: if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant) { throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); @@ -128,7 +129,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); } - BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value]; + BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer + ? context.Config.Properties.ConstantBuffers[bindingIndex.Value] + : context.Config.Properties.StorageBuffers[bindingIndex.Value]; StructureField field = buffer.Type.Fields[fieldIndex.Value]; return field.Type & AggregateType.ElementTypeMask; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 7af6d316e..c1bfa0883 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public int InputVertices { get; } public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>(); - public Instruction StorageBuffersArray { get; set; } + public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>(); public Instruction LocalMemory { get; set; } public Instruction SharedMemory { get; set; } public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>(); @@ -308,7 +308,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { if ((type & AggregateType.Array) != 0) { - return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length)); + if (length > 0) + { + return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length)); + } + else + { + return TypeRuntimeArray(GetType(type & ~AggregateType.Array)); + } } else if ((type & AggregateType.ElementCountMask) != 0) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 7c242589e..eb2db514d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.Translation; using Spv.Generator; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Numerics; using static Spv.Specification; @@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); - DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors()); + DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); DeclareSamplers(context, context.Config.GetTextureDescriptors()); DeclareImages(context, context.Config.GetImageDescriptors()); DeclareInputsAndOutputs(context, info); @@ -127,6 +128,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) + { + DeclareBuffers(context, buffers, isBuffer: false); + } + + private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) + { + DeclareBuffers(context, buffers, isBuffer: true); + } + + private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool isBuffer) { HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>(); @@ -155,6 +166,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize); } + // Zero lengths are assumed to be a "runtime array" (which does not have a explicit length + // specified on the shader, and instead assumes the bound buffer length). + // It is only valid as the last struct element. + + Debug.Assert(field.ArrayLength > 0 || fieldIndex == buffer.Type.Fields.Length - 1); + offset += fieldSize * field.ArrayLength; } else @@ -163,56 +180,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - var ubStructType = context.TypeStruct(false, structFieldTypes); + var structType = context.TypeStruct(false, structFieldTypes); - if (decoratedTypes.Add(ubStructType)) + if (decoratedTypes.Add(structType)) { - context.Decorate(ubStructType, Decoration.Block); + context.Decorate(structType, isBuffer ? Decoration.BufferBlock : Decoration.Block); for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++) { - context.MemberDecorate(ubStructType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]); + context.MemberDecorate(structType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]); } } - var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType); - var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform); + var pointerType = context.TypePointer(StorageClass.Uniform, structType); + var variable = context.Variable(pointerType, StorageClass.Uniform); - context.Name(ubVariable, buffer.Name); - context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set); - context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding); - context.AddGlobalVariable(ubVariable); - context.ConstantBuffers.Add(buffer.Binding, ubVariable); + context.Name(variable, buffer.Name); + context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set); + context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding); + context.AddGlobalVariable(variable); + + if (isBuffer) + { + context.StorageBuffers.Add(buffer.Binding, variable); + } + else + { + context.ConstantBuffers.Add(buffer.Binding, variable); + } } } - private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors) - { - if (descriptors.Length == 0) - { - return; - } - - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0; - int count = descriptors.Max(x => x.Slot) + 1; - - var sbArrayType = context.TypeRuntimeArray(context.TypeU32()); - context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4); - var sbStructType = context.TypeStruct(true, sbArrayType); - context.Decorate(sbStructType, Decoration.BufferBlock); - context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0); - var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count)); - var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType); - var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform); - - context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s"); - context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); - context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstStorageBufferBinding); - context.AddGlobalVariable(sbVariable); - - context.StorageBuffersArray = sbVariable; - } - private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) { foreach (var descriptor in descriptors) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index f088a47f3..4be0c62be 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -99,7 +99,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.Load, GenerateLoad); Add(Instruction.LoadLocal, GenerateLoadLocal); Add(Instruction.LoadShared, GenerateLoadShared); - Add(Instruction.LoadStorage, GenerateLoadStorage); Add(Instruction.Lod, GenerateLod); Add(Instruction.LogarithmB2, GenerateLogarithmB2); Add(Instruction.LogicalAnd, GenerateLogicalAnd); @@ -137,9 +136,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.StoreShared, GenerateStoreShared); Add(Instruction.StoreShared16, GenerateStoreShared16); Add(Instruction.StoreShared8, GenerateStoreShared8); - Add(Instruction.StoreStorage, GenerateStoreStorage); - Add(Instruction.StoreStorage16, GenerateStoreStorage16); - Add(Instruction.StoreStorage8, GenerateStoreStorage8); Add(Instruction.Subtract, GenerateSubtract); Add(Instruction.SwizzleAdd, GenerateSwizzleAdd); Add(Instruction.TextureSample, GenerateTextureSample); @@ -889,14 +885,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(AggregateType.U32, value); } - private static OperationResult GenerateLoadStorage(CodeGenContext context, AstOperation operation) - { - var elemPointer = GetStorageElemPointer(context, operation); - var value = context.Load(context.TypeU32(), elemPointer); - - return new OperationResult(AggregateType.U32, value); - } - private static OperationResult GenerateLod(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; @@ -1307,28 +1295,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return OperationResult.Invalid; } - private static OperationResult GenerateStoreStorage(CodeGenContext context, AstOperation operation) - { - var elemPointer = GetStorageElemPointer(context, operation); - context.Store(elemPointer, context.Get(AggregateType.U32, operation.GetSource(2))); - - return OperationResult.Invalid; - } - - private static OperationResult GenerateStoreStorage16(CodeGenContext context, AstOperation operation) - { - GenerateStoreStorageSmallInt(context, operation, 16); - - return OperationResult.Invalid; - } - - private static OperationResult GenerateStoreStorage8(CodeGenContext context, AstOperation operation) - { - GenerateStoreStorageSmallInt(context, operation, 8); - - return OperationResult.Invalid; - } - private static OperationResult GenerateSubtract(CodeGenContext context, AstOperation operation) { return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub); @@ -1849,13 +1815,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AstOperation operation, Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitU) { - var value = context.GetU32(operation.GetSource(2)); + var value = context.GetU32(operation.GetSource(operation.SourcesCount - 1)); SpvInstruction elemPointer; if (operation.StorageKind == StorageKind.StorageBuffer) { - elemPointer = GetStorageElemPointer(context, operation); + elemPointer = GetStoragePointer(context, operation, out _); } else if (operation.StorageKind == StorageKind.SharedMemory) { @@ -1875,14 +1841,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation) { - var value0 = context.GetU32(operation.GetSource(2)); - var value1 = context.GetU32(operation.GetSource(3)); + var value0 = context.GetU32(operation.GetSource(operation.SourcesCount - 2)); + var value1 = context.GetU32(operation.GetSource(operation.SourcesCount - 1)); SpvInstruction elemPointer; if (operation.StorageKind == StorageKind.StorageBuffer) { - elemPointer = GetStorageElemPointer(context, operation); + elemPointer = GetStoragePointer(context, operation, out _); } else if (operation.StorageKind == StorageKind.SharedMemory) { @@ -1901,17 +1867,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) + { + SpvInstruction pointer = GetStoragePointer(context, operation, out AggregateType varType); + + if (isStore) + { + context.Store(pointer, context.Get(varType, operation.GetSource(operation.SourcesCount - 1))); + return OperationResult.Invalid; + } + else + { + var result = context.Load(context.GetType(varType), pointer); + return new OperationResult(varType, result); + } + } + + private static SpvInstruction GetStoragePointer(CodeGenContext context, AstOperation operation, out AggregateType varType) { StorageKind storageKind = operation.StorageKind; StorageClass storageClass; SpvInstruction baseObj; - AggregateType varType; int srcIndex = 0; switch (storageKind) { case StorageKind.ConstantBuffer: + case StorageKind.StorageBuffer: if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant) { throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); @@ -1922,12 +1904,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); } - BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value]; + BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer + ? context.Config.Properties.ConstantBuffers[bindingIndex.Value] + : context.Config.Properties.StorageBuffers[bindingIndex.Value]; StructureField field = buffer.Type.Fields[fieldIndex.Value]; storageClass = StorageClass.Uniform; varType = field.Type & AggregateType.ElementTypeMask; - baseObj = context.ConstantBuffers[bindingIndex.Value]; + baseObj = storageKind == StorageKind.ConstantBuffer + ? context.ConstantBuffers[bindingIndex.Value] + : context.StorageBuffers[bindingIndex.Value]; break; case StorageKind.Input: @@ -1993,7 +1979,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv throw new InvalidOperationException($"Invalid storage kind {storageKind}."); } - int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex; + bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); + int inputsCount = (isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex; + + if (operation.Inst == Instruction.AtomicCompareAndSwap) + { + inputsCount--; + } + SpvInstruction e0, e1, e2; SpvInstruction pointer; @@ -2030,16 +2023,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } - if (isStore) - { - context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex))); - return OperationResult.Invalid; - } - else - { - var result = context.Load(context.GetType(varType), pointer); - return new OperationResult(varType, result); - } + return pointer; } private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable) @@ -2068,25 +2052,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize); } - private static void GenerateStoreStorageSmallInt(CodeGenContext context, AstOperation operation, int bitSize) - { - var i0 = context.Get(AggregateType.S32, operation.GetSource(0)); - var offset = context.Get(AggregateType.U32, operation.GetSource(1)); - var value = context.Get(AggregateType.U32, operation.GetSource(2)); - - var wordOffset = context.ShiftRightLogical(context.TypeU32(), offset, context.Constant(context.TypeU32(), 2)); - var bitOffset = context.BitwiseAnd(context.TypeU32(), offset, context.Constant(context.TypeU32(), 3)); - bitOffset = context.ShiftLeftLogical(context.TypeU32(), bitOffset, context.Constant(context.TypeU32(), 3)); - - var sbVariable = context.StorageBuffersArray; - - var i1 = context.Constant(context.TypeS32(), 0); - - var elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeU32()), sbVariable, i0, i1, wordOffset); - - GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize); - } - private static void GenerateStoreSmallInt( CodeGenContext context, SpvInstruction elemPointer, @@ -2173,16 +2138,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - private static SpvInstruction GetStorageElemPointer(CodeGenContext context, AstOperation operation) - { - var sbVariable = context.StorageBuffersArray; - var i0 = context.Get(AggregateType.S32, operation.GetSource(0)); - var i1 = context.Constant(context.TypeS32(), 0); - var i2 = context.Get(AggregateType.S32, operation.GetSource(1)); - - return context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeU32()), sbVariable, i0, i1, i2); - } - private static OperationResult GenerateUnary( CodeGenContext context, AstOperation operation, diff --git a/src/Ryujinx.Graphics.Shader/Constants.cs b/src/Ryujinx.Graphics.Shader/Constants.cs index c6f9ef494..7f1445ed0 100644 --- a/src/Ryujinx.Graphics.Shader/Constants.cs +++ b/src/Ryujinx.Graphics.Shader/Constants.cs @@ -10,7 +10,5 @@ namespace Ryujinx.Graphics.Shader public const int NvnBaseVertexByteOffset = 0x640; public const int NvnBaseInstanceByteOffset = 0x644; public const int NvnDrawIndexByteOffset = 0x648; - - public const int StorageAlignment = 16; } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 3be5088e4..473964def 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader /// <returns>Binding number</returns> int QueryBindingConstantBuffer(int index) { - return index; + return index + 1; } /// <summary> diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs index 91c232303..736963552 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs @@ -164,6 +164,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Ccc == Ccc.T) { + context.PrepareForReturn(); context.Return(); } else @@ -175,6 +176,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand lblSkip = Label(); context.BranchIfFalse(lblSkip, cond); + context.PrepareForReturn(); context.Return(); context.MarkLabel(lblSkip); } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 6f5913eb3..9aa738200 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -336,13 +336,12 @@ namespace Ryujinx.Graphics.Shader.Instructions int offset, bool extended) { - bool isSmallInt = size < LsSize.B32; - int count = GetVectorCount(size); + StorageKind storageKind = GetStorageKind(size); - (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset); + (_, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset); - Operand bitOffset = GetBitOffset(context, addrLow); + Operand srcA = context.Copy(new Operand(new Register(ra, RegisterType.Gpr))); for (int index = 0; index < count; index++) { @@ -353,12 +352,7 @@ namespace Ryujinx.Graphics.Shader.Instructions break; } - Operand value = context.LoadGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh); - - if (isSmallInt) - { - value = ExtractSmallInt(context, size, bitOffset, value); - } + Operand value = context.Load(storageKind, context.IAdd(srcA, Const(offset + index * 4)), addrHigh); context.Copy(Register(dest), value); } @@ -445,10 +439,11 @@ namespace Ryujinx.Graphics.Shader.Instructions } int count = GetVectorCount((LsSize)size); + StorageKind storageKind = GetStorageKind((LsSize)size); - (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset); + (_, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset); - Operand bitOffset = GetBitOffset(context, addrLow); + Operand srcA = context.Copy(new Operand(new Register(ra, RegisterType.Gpr))); for (int index = 0; index < count; index++) { @@ -456,23 +451,24 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr); - Operand addrLowOffset = context.IAdd(addrLow, Const(index * 4)); + Operand addrLowOffset = context.IAdd(srcA, Const(offset + index * 4)); - if (size == LsSize2.U8 || size == LsSize2.S8) - { - context.StoreGlobal8(addrLowOffset, addrHigh, value); - } - else if (size == LsSize2.U16 || size == LsSize2.S16) - { - context.StoreGlobal16(addrLowOffset, addrHigh, value); - } - else - { - context.StoreGlobal(addrLowOffset, addrHigh, value); - } + context.Store(storageKind, addrLowOffset, addrHigh, value); } } + private static StorageKind GetStorageKind(LsSize size) + { + return size switch + { + LsSize.U8 => StorageKind.GlobalMemoryU8, + LsSize.S8 => StorageKind.GlobalMemoryS8, + LsSize.U16 => StorageKind.GlobalMemoryU16, + LsSize.S16 => StorageKind.GlobalMemoryS16, + _ => StorageKind.GlobalMemory + }; + } + private static int GetVectorCount(LsSize size) { switch (size) diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index f7afe5071..aecb67249 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -79,10 +79,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation ImageAtomic, IsNan, Load, - LoadGlobal, LoadLocal, LoadShared, - LoadStorage, Lod, LogarithmB2, LogicalAnd, @@ -117,16 +115,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Sine, SquareRoot, Store, - StoreGlobal, - StoreGlobal16, - StoreGlobal8, StoreLocal, StoreShared, StoreShared16, StoreShared8, - StoreStorage, - StoreStorage16, - StoreStorage8, Subtract, SwizzleAdd, TextureSample, diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs index 593574438..2b5dd1dec 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs @@ -11,7 +11,12 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation StorageBuffer, LocalMemory, SharedMemory, - GlobalMemory + GlobalMemory, + // TODO: Remove those and store type as a field on the Operation class itself. + GlobalMemoryS8, + GlobalMemoryS16, + GlobalMemoryU8, + GlobalMemoryU16 } static class StorageKindExtensions diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 2efcbca4f..86de2e755 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -11,7 +11,6 @@ <ItemGroup> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" /> - <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Storage.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" /> @@ -19,7 +18,6 @@ <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" /> - <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" /> </ItemGroup> diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index d45f8d4ee..c348b5d93 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr enum HelperFunctionsMask { AtomicMinMaxS32Shared = 1 << 0, - AtomicMinMaxS32Storage = 1 << 1, MultiplyHighS32 = 1 << 2, MultiplyHighU32 = 1 << 3, Shuffle = 1 << 4, @@ -14,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr ShuffleUp = 1 << 6, ShuffleXor = 1 << 7, StoreSharedSmallInt = 1 << 8, - StoreStorageSmallInt = 1 << 9, SwizzleAdd = 1 << 10, FSI = 1 << 11 } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 44f0fad95..6e2013501 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -90,10 +90,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ImageAtomic, AggregateType.S32); Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar); Add(Instruction.Load, AggregateType.FP32); - Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32); Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32); Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32); - Add(Instruction.LoadStorage, AggregateType.U32, AggregateType.S32, AggregateType.S32); Add(Instruction.Lod, AggregateType.FP32); Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.LogicalAnd, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool); @@ -123,14 +121,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.Store, AggregateType.Void); - Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32); Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32); Add(Instruction.StoreShared16, AggregateType.Void, AggregateType.S32, AggregateType.U32); Add(Instruction.StoreShared8, AggregateType.Void, AggregateType.S32, AggregateType.U32); - Add(Instruction.StoreStorage, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); - Add(Instruction.StoreStorage16, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); - Add(Instruction.StoreStorage8, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32); Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32); Add(Instruction.TextureSample, AggregateType.FP32); @@ -166,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { return AggregateType.FP32; } - else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store) + else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store || inst.IsAtomic()) { return AggregateType.S32; } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index 061c89edd..157c5937d 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -5,17 +5,25 @@ namespace Ryujinx.Graphics.Shader.StructuredIr class ShaderProperties { private readonly Dictionary<int, BufferDefinition> _constantBuffers; + private readonly Dictionary<int, BufferDefinition> _storageBuffers; public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers; + public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers; public ShaderProperties() { _constantBuffers = new Dictionary<int, BufferDefinition>(); + _storageBuffers = new Dictionary<int, BufferDefinition>(); } public void AddConstantBuffer(int binding, BufferDefinition definition) { _constantBuffers[binding] = definition; } + + public void AddStorageBuffer(int binding, BufferDefinition definition) + { + _storageBuffers[binding] = definition; + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 4405c07aa..a8f132766 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -280,10 +280,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared; } - else if (operation.StorageKind == StorageKind.StorageBuffer) - { - context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage; - } break; case Instruction.MultiplyHighS32: context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32; @@ -307,10 +303,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr case Instruction.StoreShared8: context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreSharedSmallInt; break; - case Instruction.StoreStorage16: - case Instruction.StoreStorage8: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreStorageSmallInt; - break; case Instruction.SwizzleAdd: context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd; break; diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 6d4104cee..be0cba809 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -57,6 +57,56 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.AtomicXor, storageKind, Local(), a, b, c); } + public static Operand AtomicAdd(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicAdd, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicAnd(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicAnd, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand compare, Operand value) + { + return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), Const(binding), e0, e1, compare, value); + } + + public static Operand AtomicMaxS32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicMaxS32, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicMaxU32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicMaxU32, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicMinS32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicMinS32, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicMinU32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicMinU32, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicOr(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicOr, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicSwap, storageKind, Local(), Const(binding), e0, e1, value); + } + + public static Operand AtomicXor(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.AtomicXor, storageKind, Local(), Const(binding), e0, e1, value); + } + public static Operand Ballot(this EmitterContext context, Operand a) { return context.Add(Instruction.Ballot, Local(), a); @@ -554,6 +604,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(fpType | Instruction.IsNan, Local(), a); } + public static Operand Load(this EmitterContext context, StorageKind storageKind, Operand e0, Operand e1) + { + return context.Add(Instruction.Load, storageKind, Local(), e0, e1); + } + public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding) { return context.Add(Instruction.Load, storageKind, Local(), Const(binding)); @@ -606,11 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation : context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex); } - public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b) - { - return context.Add(Instruction.LoadGlobal, Local(), a, b); - } - public static Operand LoadLocal(this EmitterContext context, Operand a) { return context.Add(Instruction.LoadLocal, Local(), a); @@ -655,7 +705,6 @@ namespace Ryujinx.Graphics.Shader.Translation public static void Return(this EmitterContext context) { - context.PrepareForReturn(); context.Add(Instruction.Return); } @@ -699,6 +748,16 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c); } + public static Operand Store(this EmitterContext context, StorageKind storageKind, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.Store, storageKind, null, e0, e1, value); + } + + public static Operand Store(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) + { + return context.Add(Instruction.Store, storageKind, null, Const(binding), e0, e1, value); + } + public static Operand Store( this EmitterContext context, StorageKind storageKind, @@ -738,21 +797,6 @@ namespace Ryujinx.Graphics.Shader.Translation : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value); } - public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c) - { - return context.Add(Instruction.StoreGlobal, null, a, b, c); - } - - public static Operand StoreGlobal16(this EmitterContext context, Operand a, Operand b, Operand c) - { - return context.Add(Instruction.StoreGlobal16, null, a, b, c); - } - - public static Operand StoreGlobal8(this EmitterContext context, Operand a, Operand b, Operand c) - { - return context.Add(Instruction.StoreGlobal8, null, a, b, c); - } - public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b) { return context.Add(Instruction.StoreLocal, null, a, b); diff --git a/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs b/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs deleted file mode 100644 index a81d0fc4b..000000000 --- a/src/Ryujinx.Graphics.Shader/Translation/GlobalMemory.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; - -namespace Ryujinx.Graphics.Shader.Translation -{ - static class GlobalMemory - { - private const int StorageDescsBaseOffset = 0x44; // In words. - - public const int StorageDescSize = 4; // In words. - public const int StorageMaxCount = 16; - - public const int StorageDescsSize = StorageDescSize * StorageMaxCount; - - public const int UbeBaseOffset = 0x98; // In words. - public const int UbeMaxCount = 9; - public const int UbeDescsSize = StorageDescSize * UbeMaxCount; - public const int UbeFirstCbuf = 8; - - public const int DriverReservedCb = 0; - - public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind) - { - return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) || - inst == Instruction.LoadGlobal || - inst == Instruction.StoreGlobal || - inst == Instruction.StoreGlobal16 || - inst == Instruction.StoreGlobal8; - } - - public static int GetStorageCbOffset(ShaderStage stage, int slot) - { - return GetStorageBaseCbOffset(stage) + slot * StorageDescSize; - } - - public static int GetStorageBaseCbOffset(ShaderStage stage) - { - return stage switch - { - ShaderStage.Compute => StorageDescsBaseOffset + 2 * StorageDescsSize, - ShaderStage.Vertex => StorageDescsBaseOffset, - ShaderStage.TessellationControl => StorageDescsBaseOffset + 1 * StorageDescsSize, - ShaderStage.TessellationEvaluation => StorageDescsBaseOffset + 2 * StorageDescsSize, - ShaderStage.Geometry => StorageDescsBaseOffset + 3 * StorageDescsSize, - ShaderStage.Fragment => StorageDescsBaseOffset + 4 * StorageDescsSize, - _ => 0 - }; - } - - public static int GetConstantUbeOffset(int slot) - { - return UbeBaseOffset + slot * StorageDescSize; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs index 206facd46..7dd267f3c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs @@ -19,6 +19,14 @@ namespace Ryujinx.Graphics.Shader.Translation _stage = stage; } + public int AddFunction(Function function) + { + int functionId = _functionList.Count; + _functionList.Add(function); + + return functionId; + } + public int GetOrCreateFunctionId(HelperFunctionName functionName) { if (_functionIds.TryGetValue(functionName, out int functionId)) @@ -27,8 +35,7 @@ namespace Ryujinx.Graphics.Shader.Translation } Function function = GenerateFunction(functionName); - functionId = _functionList.Count; - _functionList.Add(function); + functionId = AddFunction(function); _functionIds.Add(functionName, functionId); return functionId; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 7758b4c61..14904b260 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -1,483 +1,1140 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; using System.Collections.Generic; +using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; -using static Ryujinx.Graphics.Shader.Translation.GlobalMemory; namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class GlobalToStorage { + private const int DriverReservedCb = 0; + + enum LsMemoryType + { + Local, + Shared + } + + private class GtsContext + { + private struct Entry + { + public readonly int FunctionId; + public readonly Instruction Inst; + public readonly StorageKind StorageKind; + public readonly bool IsMultiTarget; + public readonly IReadOnlyList<uint> TargetCbs; + + public Entry( + int functionId, + Instruction inst, + StorageKind storageKind, + bool isMultiTarget, + IReadOnlyList<uint> targetCbs) + { + FunctionId = functionId; + Inst = inst; + StorageKind = storageKind; + IsMultiTarget = isMultiTarget; + TargetCbs = targetCbs; + } + } + + private struct LsKey : IEquatable<LsKey> + { + public readonly Operand BaseOffset; + public readonly int ConstOffset; + public readonly LsMemoryType Type; + + public LsKey(Operand baseOffset, int constOffset, LsMemoryType type) + { + BaseOffset = baseOffset; + ConstOffset = constOffset; + Type = type; + } + + public override int GetHashCode() + { + return HashCode.Combine(BaseOffset, ConstOffset, Type); + } + + public override bool Equals(object obj) + { + return obj is LsKey other && Equals(other); + } + + public bool Equals(LsKey other) + { + return other.BaseOffset == BaseOffset && other.ConstOffset == ConstOffset && other.Type == Type; + } + } + + private readonly List<Entry> _entries; + private readonly Dictionary<LsKey, Dictionary<uint, SearchResult>> _sharedEntries; + private readonly HelperFunctionManager _hfm; + + public GtsContext(HelperFunctionManager hfm) + { + _entries = new List<Entry>(); + _sharedEntries = new Dictionary<LsKey, Dictionary<uint, SearchResult>>(); + _hfm = hfm; + } + + public int AddFunction(Operation baseOp, bool isMultiTarget, IReadOnlyList<uint> targetCbs, Function function) + { + int functionId = _hfm.AddFunction(function); + + _entries.Add(new Entry(functionId, baseOp.Inst, baseOp.StorageKind, isMultiTarget, targetCbs)); + + return functionId; + } + + public bool TryGetFunctionId(Operation baseOp, bool isMultiTarget, IReadOnlyList<uint> targetCbs, out int functionId) + { + foreach (Entry entry in _entries) + { + if (entry.Inst != baseOp.Inst || + entry.StorageKind != baseOp.StorageKind || + entry.IsMultiTarget != isMultiTarget || + entry.TargetCbs.Count != targetCbs.Count) + { + continue; + } + + bool allEqual = true; + + for (int index = 0; index < targetCbs.Count; index++) + { + if (targetCbs[index] != entry.TargetCbs[index]) + { + allEqual = false; + break; + } + } + + if (allEqual) + { + functionId = entry.FunctionId; + return true; + } + } + + functionId = -1; + return false; + } + + public void AddMemoryTargetCb(LsMemoryType type, Operand baseOffset, int constOffset, uint targetCb, SearchResult result) + { + LsKey key = new LsKey(baseOffset, constOffset, type); + + if (!_sharedEntries.TryGetValue(key, out Dictionary<uint, SearchResult> targetCbs)) + { + // No entry with this base offset, create a new one. + + targetCbs = new Dictionary<uint, SearchResult>() { { targetCb, result } }; + + _sharedEntries.Add(key, targetCbs); + } + else if (targetCbs.TryGetValue(targetCb, out SearchResult existingResult)) + { + // If our entry already exists, but does not match the new result, + // we set the offset to null to indicate there are multiple possible offsets. + // This will be used on the multi-target access that does not need to know the offset. + + if (existingResult.Offset != null && + (existingResult.Offset != result.Offset || + existingResult.ConstOffset != result.ConstOffset)) + { + targetCbs[targetCb] = new SearchResult(result.SbCbSlot, result.SbCbOffset); + } + } + else + { + // An entry for this base offset already exists, but not for the specified + // constant buffer region where the storage buffer base address and size + // comes from. + + targetCbs.Add(targetCb, result); + } + } + + public bool TryGetMemoryTargetCb(LsMemoryType type, Operand baseOffset, int constOffset, out SearchResult result) + { + LsKey key = new LsKey(baseOffset, constOffset, type); + + if (_sharedEntries.TryGetValue(key, out Dictionary<uint, SearchResult> targetCbs) && targetCbs.Count == 1) + { + SearchResult candidateResult = targetCbs.Values.First(); + + if (candidateResult.Found) + { + result = candidateResult; + + return true; + } + } + + result = default; + + return false; + } + } + private struct SearchResult { public static SearchResult NotFound => new SearchResult(-1, 0); public bool Found => SbCbSlot != -1; public int SbCbSlot { get; } public int SbCbOffset { get; } + public Operand Offset { get; } + public int ConstOffset { get; } public SearchResult(int sbCbSlot, int sbCbOffset) { SbCbSlot = sbCbSlot; SbCbOffset = sbCbOffset; } + + public SearchResult(int sbCbSlot, int sbCbOffset, Operand offset, int constOffset = 0) + { + SbCbSlot = sbCbSlot; + SbCbOffset = sbCbOffset; + Offset = offset; + ConstOffset = constOffset; + } } - public static void RunPass(BasicBlock block, ShaderConfig config, ref int sbUseMask, ref int ubeUseMask) + public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { - int sbStart = GetStorageBaseCbOffset(config.Stage); - int sbEnd = sbStart + StorageDescsSize; + GtsContext gtsContext = new GtsContext(hfm); - int ubeStart = UbeBaseOffset; - int ubeEnd = UbeBaseOffset + UbeDescsSize; - - for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) + foreach (BasicBlock block in blocks) { - for (int index = 0; index < node.Value.SourcesCount; index++) + for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) { - Operand src = node.Value.GetSource(index); - - int storageIndex = GetStorageIndex(src, sbStart, sbEnd); - - if (storageIndex >= 0) - { - sbUseMask |= 1 << storageIndex; - } - - if (config.Stage == ShaderStage.Compute) - { - int constantIndex = GetStorageIndex(src, ubeStart, ubeEnd); - - if (constantIndex >= 0) - { - ubeUseMask |= 1 << constantIndex; - } - } - } - - if (!(node.Value is Operation operation)) - { - continue; - } - - if (UsesGlobalMemory(operation.Inst, operation.StorageKind)) - { - Operand source = operation.GetSource(0); - - var result = SearchForStorageBase(config, block, source); - if (!result.Found) + if (!(node.Value is Operation operation)) { continue; } - if (config.Stage == ShaderStage.Compute && - operation.Inst == Instruction.LoadGlobal && - result.SbCbSlot == DriverReservedCb && - result.SbCbOffset >= UbeBaseOffset && - result.SbCbOffset < UbeBaseOffset + UbeDescsSize) + if (IsGlobalMemory(operation.StorageKind)) { - // Here we effectively try to replace a LDG instruction with LDC. - // The hardware only supports a limited amount of constant buffers - // so NVN "emulates" more constant buffers using global memory access. - // Here we try to replace the global access back to a constant buffer - // load. - node = ReplaceLdgWithLdc(node, config, (result.SbCbOffset - UbeBaseOffset) / StorageDescSize); - } - else - { - // Storage buffers are implemented using global memory access. - // If we know from where the base address of the access is loaded, - // we can guess which storage buffer it is accessing. - // We can then replace the global memory access with a storage - // buffer access. - node = ReplaceGlobalWithStorage(block, node, config, config.GetSbSlot((byte)result.SbCbSlot, (ushort)result.SbCbOffset)); - } - } - } + LinkedListNode<INode> nextNode = ReplaceGlobalMemoryWithStorage(gtsContext, config, block, node); - config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask); - } - - private static LinkedListNode<INode> ReplaceGlobalWithStorage(BasicBlock block, LinkedListNode<INode> node, ShaderConfig config, int storageIndex) - { - Operation operation = (Operation)node.Value; - - bool isAtomic = operation.Inst.IsAtomic(); - bool isStg16Or8 = operation.Inst == Instruction.StoreGlobal16 || operation.Inst == Instruction.StoreGlobal8; - bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal || isStg16Or8; - - config.SetUsedStorageBuffer(storageIndex, isWrite); - - Operand[] sources = new Operand[operation.SourcesCount]; - - sources[0] = Const(storageIndex); - sources[1] = GetStorageOffset(block, node, config, storageIndex, operation.GetSource(0), isStg16Or8); - - for (int index = 2; index < operation.SourcesCount; index++) - { - sources[index] = operation.GetSource(index); - } - - Operation storageOp; - - if (isAtomic) - { - storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources); - } - else if (operation.Inst == Instruction.LoadGlobal) - { - storageOp = new Operation(Instruction.LoadStorage, operation.Dest, sources); - } - else - { - Instruction storeInst = operation.Inst switch - { - Instruction.StoreGlobal16 => Instruction.StoreStorage16, - Instruction.StoreGlobal8 => Instruction.StoreStorage8, - _ => Instruction.StoreStorage - }; - - storageOp = new Operation(storeInst, null, sources); - } - - for (int index = 0; index < operation.SourcesCount; index++) - { - operation.SetSource(index, null); - } - - LinkedListNode<INode> oldNode = node; - - node = node.List.AddBefore(node, storageOp); - - node.List.Remove(oldNode); - - return node; - } - - private static Operand GetStorageOffset( - BasicBlock block, - LinkedListNode<INode> node, - ShaderConfig config, - int storageIndex, - Operand addrLow, - bool isStg16Or8) - { - (int sbCbSlot, int sbCbOffset) = config.GetSbCbInfo(storageIndex); - - bool storageAligned = !(config.GpuAccessor.QueryHasUnalignedStorageBuffer() || config.GpuAccessor.QueryHostStorageBufferOffsetAlignment() > Constants.StorageAlignment); - - (Operand byteOffset, int constantOffset) = storageAligned ? - GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), sbCbSlot, sbCbOffset) : - (null, 0); - - if (byteOffset != null) - { - ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset); - } - - if (byteOffset == null) - { - Operand baseAddrLow = Cbuf(sbCbSlot, sbCbOffset); - Operand baseAddrTrunc = Local(); - - Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); - - Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); - - node.List.AddBefore(node, andOp); - - Operand offset = Local(); - Operation subOp = new Operation(Instruction.Subtract, offset, addrLow, baseAddrTrunc); - - node.List.AddBefore(node, subOp); - - byteOffset = offset; - } - else if (constantOffset != 0) - { - Operand offset = Local(); - Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset)); - - node.List.AddBefore(node, addOp); - - byteOffset = offset; - } - - if (isStg16Or8) - { - return byteOffset; - } - - Operand wordOffset = Local(); - Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2)); - - node.List.AddBefore(node, shrOp); - - return wordOffset; - } - - private static bool IsCbOffset(Operand operand, int slot, int offset) - { - return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == slot && operand.GetCbufOffset() == offset; - } - - private static void ReplaceAddressAlignment(LinkedList<INode> list, Operand address, Operand byteOffset, int constantOffset) - { - // When we emit 16/8-bit LDG, we add extra code to determine the address alignment. - // Eliminate the storage buffer base address from this too, leaving only the byte offset. - - foreach (INode useNode in address.UseOps) - { - if (useNode is Operation op && op.Inst == Instruction.BitwiseAnd) - { - Operand src1 = op.GetSource(0); - Operand src2 = op.GetSource(1); - - int addressIndex = -1; - - if (src1 == address && src2.Type == OperandType.Constant && src2.Value == 3) - { - addressIndex = 0; - } - else if (src2 == address && src1.Type == OperandType.Constant && src1.Value == 3) - { - addressIndex = 1; - } - - if (addressIndex != -1) - { - LinkedListNode<INode> node = list.Find(op); - - // Add offset calculation before the use. Needs to be on the same block. - if (node != null) + if (nextNode == null) { - Operand offset = Local(); - Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset)); - list.AddBefore(node, addOp); + // The returned value being null means that the global memory replacement failed, + // so we just make loads read 0 and stores do nothing. - op.SetSource(addressIndex, offset); + config.GpuAccessor.Log($"Failed to reserve storage buffer for global memory operation \"{operation.Inst}\"."); + + if (operation.Dest != null) + { + operation.TurnIntoCopy(Const(0)); + } + else + { + Utils.DeleteNode(node, operation); + } + } + else + { + node = nextNode; + } + } + else if (operation.Inst == Instruction.StoreShared || operation.Inst == Instruction.StoreLocal) + { + // The NVIDIA compiler can sometimes use shared or local memory as temporary + // storage to place the base address and size on, so we need + // to be able to find such information stored in memory too. + + if (TryGetMemoryOffsets(operation, out LsMemoryType type, out Operand baseOffset, out int constOffset)) + { + Operand value = operation.GetSource(operation.SourcesCount - 1); + + var result = FindUniqueBaseAddressCb(gtsContext, block, value, needsOffset: false); + if (result.Found) + { + uint targetCb = PackCbSlotAndOffset(result.SbCbSlot, result.SbCbOffset); + gtsContext.AddMemoryTargetCb(type, baseOffset, constOffset, targetCb, result); + } } } } } } - private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int cbSlot, int baseAddressCbOffset) + private static bool IsGlobalMemory(StorageKind storageKind) { - if (IsCbOffset(address, cbSlot, baseAddressCbOffset)) - { - // Direct offset: zero. - return (Const(0), 0); - } - - (address, int constantOffset) = GetStorageConstantOffset(block, address); - - address = Utils.FindLastOperation(address, block); - - if (IsCbOffset(address, cbSlot, baseAddressCbOffset)) - { - // Only constant offset - return (Const(0), constantOffset); - } - - if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add) - { - return (null, 0); - } - - Operand src1 = offsetAdd.GetSource(0); - Operand src2 = Utils.FindLastOperation(offsetAdd.GetSource(1), block); - - if (IsCbOffset(src2, cbSlot, baseAddressCbOffset)) - { - return (src1, constantOffset); - } - else if (IsCbOffset(src1, cbSlot, baseAddressCbOffset)) - { - return (src2, constantOffset); - } - - return (null, 0); + return storageKind == StorageKind.GlobalMemory || + storageKind == StorageKind.GlobalMemoryS8 || + storageKind == StorageKind.GlobalMemoryS16 || + storageKind == StorageKind.GlobalMemoryU8 || + storageKind == StorageKind.GlobalMemoryU16; } - private static (Operand, int) GetStorageConstantOffset(BasicBlock block, Operand address) + private static bool IsSmallInt(StorageKind storageKind) { - if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add) - { - return (address, 0); - } - - Operand src1 = offsetAdd.GetSource(0); - Operand src2 = offsetAdd.GetSource(1); - - if (src2.Type != OperandType.Constant) - { - return (address, 0); - } - - return (src1, src2.Value); + return storageKind == StorageKind.GlobalMemoryS8 || + storageKind == StorageKind.GlobalMemoryS16 || + storageKind == StorageKind.GlobalMemoryU8 || + storageKind == StorageKind.GlobalMemoryU16; } - private static LinkedListNode<INode> ReplaceLdgWithLdc(LinkedListNode<INode> node, ShaderConfig config, int storageIndex) + private static LinkedListNode<INode> ReplaceGlobalMemoryWithStorage( + GtsContext gtsContext, + ShaderConfig config, + BasicBlock block, + LinkedListNode<INode> node) { - Operation operation = (Operation)node.Value; + Operation operation = node.Value as Operation; + Operand globalAddress = operation.GetSource(0); + SearchResult result = FindUniqueBaseAddressCb(gtsContext, block, globalAddress, needsOffset: true); - Operand GetCbufOffset() + if (result.Found) { - Operand addrLow = operation.GetSource(0); + // We found the storage buffer that is being accessed. + // There are two possible paths here, if the operation is simple enough, + // we just generate the storage access code inline. + // Otherwise, we generate a function call (and the function if necessary). - Operand baseAddrLow = Cbuf(0, UbeBaseOffset + storageIndex * StorageDescSize); + Operand offset = result.Offset; - Operand baseAddrTrunc = Local(); + bool storageUnaligned = config.GpuAccessor.QueryHasUnalignedStorageBuffer(); - Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); + if (storageUnaligned) + { + Operand baseAddress = Cbuf(result.SbCbSlot, result.SbCbOffset); - Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); + Operand baseAddressMasked = Local(); + Operand hostOffset = Local(); - node.List.AddBefore(node, andOp); + int alignment = config.GpuAccessor.QueryHostStorageBufferOffsetAlignment(); - Operand byteOffset = Local(); - Operand wordOffset = Local(); + Operation maskOp = new Operation(Instruction.BitwiseAnd, baseAddressMasked, new[] { baseAddress, Const(-alignment) }); + Operation subOp = new Operation(Instruction.Subtract, hostOffset, new[] { globalAddress, baseAddressMasked }); - Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc); - Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2)); + node.List.AddBefore(node, maskOp); + node.List.AddBefore(node, subOp); - node.List.AddBefore(node, subOp); - node.List.AddBefore(node, shrOp); + offset = hostOffset; + } + else if (result.ConstOffset != 0) + { + Operand newOffset = Local(); - return wordOffset; + Operation addOp = new Operation(Instruction.Add, newOffset, new[] { offset, Const(result.ConstOffset) }); + + node.List.AddBefore(node, addOp); + + offset = newOffset; + } + + if (CanUseInlineStorageOp(operation, config.Options.TargetLanguage)) + { + return GenerateInlineStorageOp(config, node, operation, offset, result); + } + else + { + if (!TryGenerateSingleTargetStorageOp(gtsContext, config, operation, result, out int functionId)) + { + return null; + } + + return GenerateCallStorageOp(node, operation, offset, functionId); + } } - - Operand cbufOffset = GetCbufOffset(); - Operand vecIndex = Local(); - Operand elemIndex = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ShiftRightU32, 0, vecIndex, cbufOffset, Const(2))); - node.List.AddBefore(node, new Operation(Instruction.BitwiseAnd, 0, elemIndex, cbufOffset, Const(3))); - - Operand[] sources = new Operand[4]; - - int cbSlot = UbeFirstCbuf + storageIndex; - - sources[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot)); - sources[1] = Const(0); - sources[2] = vecIndex; - sources[3] = elemIndex; - - Operation ldcOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, operation.Dest, sources); - - for (int index = 0; index < operation.SourcesCount; index++) + else { - operation.SetSource(index, null); + // Failed to find the storage buffer directly. + // Try to walk through Phi chains and find all possible constant buffers where + // the base address might be stored. + // Generate a helper function that will check all possible storage buffers and use the right one. + + if (!TryGenerateMultiTargetStorageOp(gtsContext, config, block, operation, out int functionId)) + { + return null; + } + + return GenerateCallStorageOp(node, operation, null, functionId); } - - LinkedListNode<INode> oldNode = node; - - node = node.List.AddBefore(node, ldcOp); - - node.List.Remove(oldNode); - - return node; } - private static SearchResult SearchForStorageBase(ShaderConfig config, BasicBlock block, Operand globalAddress) + private static bool CanUseInlineStorageOp(Operation operation, TargetLanguage targetLanguage) + { + if (operation.StorageKind != StorageKind.GlobalMemory) + { + return false; + } + + return (operation.Inst != Instruction.AtomicMaxS32 && + operation.Inst != Instruction.AtomicMinS32) || targetLanguage == TargetLanguage.Spirv; + } + + private static LinkedListNode<INode> GenerateInlineStorageOp( + ShaderConfig config, + LinkedListNode<INode> node, + Operation operation, + Operand offset, + SearchResult result) + { + bool isStore = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); + if (!config.ResourceManager.TryGetStorageBufferBinding(result.SbCbSlot, result.SbCbOffset, isStore, out int binding)) + { + return null; + } + + Operand wordOffset = Local(); + + Operand[] sources; + + if (operation.Inst == Instruction.AtomicCompareAndSwap) + { + sources = new Operand[] + { + Const(binding), + Const(0), + wordOffset, + operation.GetSource(operation.SourcesCount - 2), + operation.GetSource(operation.SourcesCount - 1) + }; + } + else if (isStore) + { + sources = new Operand[] { Const(binding), Const(0), wordOffset, operation.GetSource(operation.SourcesCount - 1) }; + } + else + { + sources = new Operand[] { Const(binding), Const(0), wordOffset }; + } + + Operation shiftOp = new Operation(Instruction.ShiftRightU32, wordOffset, new[] { offset, Const(2) }); + Operation storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources); + + node.List.AddBefore(node, shiftOp); + LinkedListNode<INode> newNode = node.List.AddBefore(node, storageOp); + + Utils.DeleteNode(node, operation); + + return newNode; + } + + private static LinkedListNode<INode> GenerateCallStorageOp(LinkedListNode<INode> node, Operation operation, Operand offset, int functionId) + { + // Generate call to a helper function that will perform the storage buffer operation. + + Operand[] sources = new Operand[operation.SourcesCount - 1 + (offset == null ? 2 : 1)]; + + sources[0] = Const(functionId); + + if (offset != null) + { + // If the offset was supplised, we use that and skip the global address. + + sources[1] = offset; + + for (int srcIndex = 2; srcIndex < operation.SourcesCount; srcIndex++) + { + sources[srcIndex] = operation.GetSource(srcIndex); + } + } + else + { + // Use the 64-bit global address which is split in 2 32-bit arguments. + + for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + { + sources[srcIndex + 1] = operation.GetSource(srcIndex); + } + } + + bool returnsValue = operation.Dest != null; + Operand returnValue = returnsValue ? Local() : null; + + Operation callOp = new Operation(Instruction.Call, returnValue, sources); + + LinkedListNode<INode> newNode = node.List.AddBefore(node, callOp); + + if (returnsValue) + { + operation.TurnIntoCopy(returnValue); + + return node; + } + else + { + Utils.DeleteNode(node, operation); + + return newNode; + } + } + + private static bool TryGenerateSingleTargetStorageOp( + GtsContext gtsContext, + ShaderConfig config, + Operation operation, + SearchResult result, + out int functionId) + { + List<uint> targetCbs = new List<uint>() { PackCbSlotAndOffset(result.SbCbSlot, result.SbCbOffset) }; + + if (gtsContext.TryGetFunctionId(operation, isMultiTarget: false, targetCbs, out functionId)) + { + return true; + } + + int inArgumentsCount = 1; + + if (operation.Inst == Instruction.AtomicCompareAndSwap) + { + inArgumentsCount = 3; + } + else if (operation.Inst == Instruction.Store || operation.Inst.IsAtomic()) + { + inArgumentsCount = 2; + } + + EmitterContext context = new EmitterContext(); + + Operand offset = Argument(0); + Operand compare = null; + Operand value = null; + + if (inArgumentsCount == 3) + { + compare = Argument(1); + value = Argument(2); + } + else if (inArgumentsCount == 2) + { + value = Argument(1); + } + + if (!TryGenerateStorageOp( + config, + context, + operation.Inst, + operation.StorageKind, + offset, + compare, + value, + result, + out Operand resultValue)) + { + functionId = 0; + return false; + } + + bool returnsValue = resultValue != null; + + if (returnsValue) + { + context.Return(resultValue); + } + else + { + context.Return(); + } + + string functionName = GetFunctionName(operation, isMultiTarget: false, targetCbs); + + Function function = new Function( + ControlFlowGraph.Create(context.GetOperations()).Blocks, + functionName, + returnsValue, + inArgumentsCount, + 0); + + functionId = gtsContext.AddFunction(operation, isMultiTarget: false, targetCbs, function); + + return true; + } + + private static bool TryGenerateMultiTargetStorageOp( + GtsContext gtsContext, + ShaderConfig config, + BasicBlock block, + Operation operation, + out int functionId) + { + Queue<PhiNode> phis = new Queue<PhiNode>(); + HashSet<PhiNode> visited = new HashSet<PhiNode>(); + List<uint> targetCbs = new List<uint>(); + + Operand globalAddress = operation.GetSource(0); + + if (globalAddress.AsgOp is Operation addOp && addOp.Inst == Instruction.Add) + { + Operand src1 = addOp.GetSource(0); + Operand src2 = addOp.GetSource(1); + + if (src1.Type == OperandType.Constant && src2.Type == OperandType.LocalVariable) + { + globalAddress = src2; + } + else if (src1.Type == OperandType.LocalVariable && src2.Type == OperandType.Constant) + { + globalAddress = src1; + } + } + + if (globalAddress.AsgOp is PhiNode phi && visited.Add(phi)) + { + phis.Enqueue(phi); + } + else + { + SearchResult result = FindUniqueBaseAddressCb(gtsContext, block, operation.GetSource(0), needsOffset: false); + + if (result.Found) + { + targetCbs.Add(PackCbSlotAndOffset(result.SbCbSlot, result.SbCbOffset)); + } + } + + while (phis.TryDequeue(out phi)) + { + for (int srcIndex = 0; srcIndex < phi.SourcesCount; srcIndex++) + { + BasicBlock phiBlock = phi.GetBlock(srcIndex); + Operand phiSource = phi.GetSource(srcIndex); + + SearchResult result = FindUniqueBaseAddressCb(gtsContext, phiBlock, phiSource, needsOffset: false); + + if (result.Found) + { + uint targetCb = PackCbSlotAndOffset(result.SbCbSlot, result.SbCbOffset); + + if (!targetCbs.Contains(targetCb)) + { + targetCbs.Add(targetCb); + } + } + else if (phiSource.AsgOp is PhiNode phi2 && visited.Add(phi2)) + { + phis.Enqueue(phi2); + } + } + } + + targetCbs.Sort(); + + if (targetCbs.Count == 0) + { + config.GpuAccessor.Log($"Failed to find storage buffer for global memory operation \"{operation.Inst}\"."); + } + + if (gtsContext.TryGetFunctionId(operation, isMultiTarget: true, targetCbs, out functionId)) + { + return true; + } + + int inArgumentsCount = 2; + + if (operation.Inst == Instruction.AtomicCompareAndSwap) + { + inArgumentsCount = 4; + } + else if (operation.Inst == Instruction.Store || operation.Inst.IsAtomic()) + { + inArgumentsCount = 3; + } + + EmitterContext context = new EmitterContext(); + + Operand globalAddressLow = Argument(0); + Operand globalAddressHigh = Argument(1); + + foreach (uint targetCb in targetCbs) + { + (int sbCbSlot, int sbCbOffset) = UnpackCbSlotAndOffset(targetCb); + + Operand baseAddrLow = Cbuf(sbCbSlot, sbCbOffset); + Operand baseAddrHigh = Cbuf(sbCbSlot, sbCbOffset + 1); + Operand size = Cbuf(sbCbSlot, sbCbOffset + 2); + + Operand offset = context.ISubtract(globalAddressLow, baseAddrLow); + Operand borrow = context.ICompareLessUnsigned(globalAddressLow, baseAddrLow); + + Operand inRangeLow = context.ICompareLessUnsigned(offset, size); + + Operand addrHighBorrowed = context.IAdd(globalAddressHigh, borrow); + + Operand inRangeHigh = context.ICompareEqual(addrHighBorrowed, baseAddrHigh); + + Operand inRange = context.BitwiseAnd(inRangeLow, inRangeHigh); + + Operand lblSkip = Label(); + context.BranchIfFalse(lblSkip, inRange); + + Operand compare = null; + Operand value = null; + + if (inArgumentsCount == 4) + { + compare = Argument(2); + value = Argument(3); + } + else if (inArgumentsCount == 3) + { + value = Argument(2); + } + + SearchResult result = new SearchResult(sbCbSlot, sbCbOffset); + + int alignment = config.GpuAccessor.QueryHostStorageBufferOffsetAlignment(); + + Operand baseAddressMasked = context.BitwiseAnd(baseAddrLow, Const(-alignment)); + Operand hostOffset = context.ISubtract(globalAddressLow, baseAddressMasked); + + if (!TryGenerateStorageOp( + config, + context, + operation.Inst, + operation.StorageKind, + hostOffset, + compare, + value, + result, + out Operand resultValue)) + { + functionId = 0; + return false; + } + + if (resultValue != null) + { + context.Return(resultValue); + } + else + { + context.Return(); + } + + context.MarkLabel(lblSkip); + } + + bool returnsValue = operation.Dest != null; + + if (returnsValue) + { + context.Return(Const(0)); + } + else + { + context.Return(); + } + + string functionName = GetFunctionName(operation, isMultiTarget: true, targetCbs); + + Function function = new Function( + ControlFlowGraph.Create(context.GetOperations()).Blocks, + functionName, + returnsValue, + inArgumentsCount, + 0); + + functionId = gtsContext.AddFunction(operation, isMultiTarget: true, targetCbs, function); + + return true; + } + + private static uint PackCbSlotAndOffset(int cbSlot, int cbOffset) + { + return (uint)((ushort)cbSlot | ((ushort)cbOffset << 16)); + } + + private static (int, int) UnpackCbSlotAndOffset(uint packed) + { + return ((ushort)packed, (ushort)(packed >> 16)); + } + + private static string GetFunctionName(Operation baseOp, bool isMultiTarget, IReadOnlyList<uint> targetCbs) + { + string name = baseOp.Inst.ToString(); + + name += baseOp.StorageKind switch + { + StorageKind.GlobalMemoryS8 => "S8", + StorageKind.GlobalMemoryS16 => "S16", + StorageKind.GlobalMemoryU8 => "U8", + StorageKind.GlobalMemoryU16 => "U16", + _ => string.Empty + }; + + if (isMultiTarget) + { + name += "Multi"; + } + + foreach (uint targetCb in targetCbs) + { + (int sbCbSlot, int sbCbOffset) = UnpackCbSlotAndOffset(targetCb); + + name += $"_c{sbCbSlot}o{sbCbOffset}"; + } + + return name; + } + + private static bool TryGenerateStorageOp( + ShaderConfig config, + EmitterContext context, + Instruction inst, + StorageKind storageKind, + Operand offset, + Operand compare, + Operand value, + SearchResult result, + out Operand resultValue) + { + resultValue = null; + bool isStore = inst.IsAtomic() || inst == Instruction.Store; + + if (!config.ResourceManager.TryGetStorageBufferBinding(result.SbCbSlot, result.SbCbOffset, isStore, out int binding)) + { + return false; + } + + Operand wordOffset = context.ShiftRightU32(offset, Const(2)); + + if (inst.IsAtomic()) + { + if (IsSmallInt(storageKind)) + { + throw new NotImplementedException(); + } + + switch (inst) + { + case Instruction.AtomicAdd: + resultValue = context.AtomicAdd(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + break; + case Instruction.AtomicAnd: + resultValue = context.AtomicAnd(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + break; + case Instruction.AtomicCompareAndSwap: + resultValue = context.AtomicCompareAndSwap(StorageKind.StorageBuffer, binding, Const(0), wordOffset, compare, value); + break; + case Instruction.AtomicMaxS32: + if (config.Options.TargetLanguage == TargetLanguage.Spirv) + { + resultValue = context.AtomicMaxS32(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + } + else + { + resultValue = GenerateAtomicCasLoop(context, wordOffset, binding, (memValue) => + { + return context.IMaximumS32(memValue, value); + }); + } + break; + case Instruction.AtomicMaxU32: + resultValue = context.AtomicMaxU32(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + break; + case Instruction.AtomicMinS32: + if (config.Options.TargetLanguage == TargetLanguage.Spirv) + { + resultValue = context.AtomicMinS32(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + } + else + { + resultValue = GenerateAtomicCasLoop(context, wordOffset, binding, (memValue) => + { + return context.IMinimumS32(memValue, value); + }); + } + break; + case Instruction.AtomicMinU32: + resultValue = context.AtomicMinU32(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + break; + case Instruction.AtomicOr: + resultValue = context.AtomicOr(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + break; + case Instruction.AtomicSwap: + resultValue = context.AtomicSwap(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + break; + case Instruction.AtomicXor: + resultValue = context.AtomicXor(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + break; + } + } + else if (inst == Instruction.Store) + { + int bitSize = storageKind switch + { + StorageKind.GlobalMemoryS8 or + StorageKind.GlobalMemoryU8 => 8, + StorageKind.GlobalMemoryS16 or + StorageKind.GlobalMemoryU16 => 16, + _ => 32 + }; + + if (bitSize < 32) + { + Operand bitOffset = GetBitOffset(context, offset); + + GenerateAtomicCasLoop(context, wordOffset, binding, (memValue) => + { + return context.BitfieldInsert(memValue, value, bitOffset, Const(bitSize)); + }); + } + else + { + context.Store(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); + } + } + else + { + value = context.Load(StorageKind.StorageBuffer, binding, Const(0), wordOffset); + + if (IsSmallInt(storageKind)) + { + Operand bitOffset = GetBitOffset(context, offset); + + switch (storageKind) + { + case StorageKind.GlobalMemoryS8: + value = context.ShiftRightS32(value, bitOffset); + value = context.BitfieldExtractS32(value, Const(0), Const(8)); + break; + case StorageKind.GlobalMemoryS16: + value = context.ShiftRightS32(value, bitOffset); + value = context.BitfieldExtractS32(value, Const(0), Const(16)); + break; + case StorageKind.GlobalMemoryU8: + value = context.ShiftRightU32(value, bitOffset); + value = context.BitwiseAnd(value, Const(byte.MaxValue)); + break; + case StorageKind.GlobalMemoryU16: + value = context.ShiftRightU32(value, bitOffset); + value = context.BitwiseAnd(value, Const(ushort.MaxValue)); + break; + } + } + + resultValue = value; + } + + return true; + } + + private static Operand GetBitOffset(EmitterContext context, Operand offset) + { + return context.ShiftLeft(context.BitwiseAnd(offset, Const(3)), Const(3)); + } + + private static Operand GenerateAtomicCasLoop(EmitterContext context, Operand wordOffset, int binding, Func<Operand, Operand> opCallback) + { + Operand lblLoopHead = Label(); + + context.MarkLabel(lblLoopHead); + + Operand oldValue = context.Load(StorageKind.StorageBuffer, binding, Const(0), wordOffset); + Operand newValue = opCallback(oldValue); + + Operand casResult = context.AtomicCompareAndSwap( + StorageKind.StorageBuffer, + binding, + Const(0), + wordOffset, + oldValue, + newValue); + + Operand casFail = context.ICompareNotEqual(casResult, oldValue); + + context.BranchIfTrue(lblLoopHead, casFail); + + return oldValue; + } + + private static SearchResult FindUniqueBaseAddressCb(GtsContext gtsContext, BasicBlock block, Operand globalAddress, bool needsOffset) { globalAddress = Utils.FindLastOperation(globalAddress, block); if (globalAddress.Type == OperandType.ConstantBuffer) { - return GetStorageIndex(config, globalAddress); + return GetBaseAddressCbWithOffset(globalAddress, Const(0), 0); } Operation operation = globalAddress.AsgOp as Operation; if (operation == null || operation.Inst != Instruction.Add) { - return SearchResult.NotFound; + return FindBaseAddressCbFromMemory(gtsContext, operation, 0, needsOffset); } Operand src1 = operation.GetSource(0); Operand src2 = operation.GetSource(1); + int constOffset = 0; + if ((src1.Type == OperandType.LocalVariable && src2.Type == OperandType.Constant) || (src2.Type == OperandType.LocalVariable && src1.Type == OperandType.Constant)) { Operand baseAddr; + Operand offset; if (src1.Type == OperandType.LocalVariable) { baseAddr = Utils.FindLastOperation(src1, block); + offset = src2; } else { baseAddr = Utils.FindLastOperation(src2, block); + offset = src1; } - var result = GetStorageIndex(config, baseAddr); + var result = GetBaseAddressCbWithOffset(baseAddr, offset, 0); if (result.Found) { return result; } + constOffset = offset.Value; operation = baseAddr.AsgOp as Operation; if (operation == null || operation.Inst != Instruction.Add) { - return SearchResult.NotFound; + return FindBaseAddressCbFromMemory(gtsContext, operation, constOffset, needsOffset); } } - var selectedResult = SearchResult.NotFound; + src1 = operation.GetSource(0); + src2 = operation.GetSource(1); - for (int index = 0; index < operation.SourcesCount; index++) + // If we have two possible results, we give preference to the ones from + // the driver reserved constant buffer, as those are the ones that + // contains the base address. + + // If both are constant buffer, give preference to the second operand, + // because constant buffer are always encoded as the second operand, + // so the second operand will always be the one from the last instruction. + + if (src1.Type != OperandType.ConstantBuffer || + (src1.Type == OperandType.ConstantBuffer && src2.Type == OperandType.ConstantBuffer) || + (src2.Type == OperandType.ConstantBuffer && src2.GetCbufSlot() == DriverReservedCb)) { - Operand source = operation.GetSource(index); - - var result = GetStorageIndex(config, source); - - // If we already have a result, we give preference to the ones from - // the driver reserved constant buffer, as those are the ones that - // contains the base address. - if (result.Found && (!selectedResult.Found || result.SbCbSlot == GlobalMemory.DriverReservedCb)) - { - selectedResult = result; - } + return GetBaseAddressCbWithOffset(src2, src1, constOffset); } - return selectedResult; + return GetBaseAddressCbWithOffset(src1, src2, constOffset); } - private static SearchResult GetStorageIndex(ShaderConfig config, Operand operand) + private static SearchResult FindBaseAddressCbFromMemory(GtsContext gtsContext, Operation operation, int constOffset, bool needsOffset) { - if (operand.Type == OperandType.ConstantBuffer) + if (operation != null) { - int slot = operand.GetCbufSlot(); - int offset = operand.GetCbufOffset(); - - if ((offset & 3) == 0) + if (TryGetMemoryOffsets(operation, out LsMemoryType type, out Operand bo, out int co) && + gtsContext.TryGetMemoryTargetCb(type, bo, co, out SearchResult result) && + (result.Offset != null || !needsOffset)) { - return new SearchResult(slot, offset); + if (constOffset != 0) + { + return new SearchResult( + result.SbCbSlot, + result.SbCbOffset, + result.Offset, + result.ConstOffset + constOffset); + } + + return result; } } return SearchResult.NotFound; } - private static int GetStorageIndex(Operand operand, int sbStart, int sbEnd) + private static SearchResult GetBaseAddressCbWithOffset(Operand baseAddress, Operand offset, int constOffset) { - if (operand.Type == OperandType.ConstantBuffer) + if (baseAddress.Type == OperandType.ConstantBuffer) { - int slot = operand.GetCbufSlot(); - int offset = operand.GetCbufOffset(); + int sbCbSlot = baseAddress.GetCbufSlot(); + int sbCbOffset = baseAddress.GetCbufOffset(); - if (slot == 0 && offset >= sbStart && offset < sbEnd) + // We require the offset to be aligned to 1 word (64 bits), + // since the address size is 64-bit and the GPU only supports aligned memory access. + if ((sbCbOffset & 1) == 0) { - int storageIndex = (offset - sbStart) / StorageDescSize; - - return storageIndex; + return new SearchResult(sbCbSlot, sbCbOffset, offset, constOffset); } } - return -1; + return SearchResult.NotFound; + } + + private static bool TryGetMemoryOffsets(Operation operation, out LsMemoryType type, out Operand baseOffset, out int constOffset) + { + baseOffset = null; + + if (operation.Inst == Instruction.LoadShared || operation.Inst == Instruction.StoreShared) + { + type = LsMemoryType.Shared; + return TryGetSharedMemoryOffsets(operation, out baseOffset, out constOffset); + } + else if (operation.Inst == Instruction.LoadLocal || operation.Inst == Instruction.StoreLocal) + { + type = LsMemoryType.Local; + return TryGetLocalMemoryOffset(operation, out constOffset); + } + + type = default; + constOffset = 0; + return false; + } + + private static bool TryGetSharedMemoryOffsets(Operation operation, out Operand baseOffset, out int constOffset) + { + baseOffset = null; + constOffset = 0; + + // The byte offset is right shifted by 2 to get the 32-bit word offset, + // so we want to get the byte offset back, since each one of those word + // offsets are a new "local variable" which will not match. + + if (operation.GetSource(0).AsgOp is Operation shiftRightOp && + shiftRightOp.Inst == Instruction.ShiftRightU32 && + shiftRightOp.GetSource(1).Type == OperandType.Constant && + shiftRightOp.GetSource(1).Value == 2) + { + baseOffset = shiftRightOp.GetSource(0); + } + + // Check if we have a constant offset being added to the base offset. + + if (baseOffset?.AsgOp is Operation addOp && addOp.Inst == Instruction.Add) + { + Operand src1 = addOp.GetSource(0); + Operand src2 = addOp.GetSource(1); + + if (src1.Type == OperandType.Constant && src2.Type == OperandType.LocalVariable) + { + constOffset = src1.Value; + baseOffset = src2; + } + else if (src1.Type == OperandType.LocalVariable && src2.Type == OperandType.Constant) + { + baseOffset = src1; + constOffset = src2.Value; + } + } + + return baseOffset != null && baseOffset.Type == OperandType.LocalVariable; + } + + private static bool TryGetLocalMemoryOffset(Operation operation, out int constOffset) + { + if (operation.GetSource(0).Type == OperandType.Constant) + { + constOffset = operation.GetSource(0).Value; + return true; + } + + constOffset = 0; + return false; } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index b126e2c48..bdb3a62ec 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -7,17 +7,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class Optimizer { - public static void RunPass(BasicBlock[] blocks, ShaderConfig config) + public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { RunOptimizationPasses(blocks, config); - int sbUseMask = 0; - int ubeUseMask = 0; + GlobalToStorage.RunPass(hfm, blocks, config); // Those passes are looking for specific patterns and only needs to run once. for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { - GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask); BindlessToIndexed.RunPass(blocks[blkIndex], config); BindlessElimination.RunPass(blocks[blkIndex], config); @@ -28,8 +26,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask); - // Run optimizations one last time to remove any code that is now optimizable after above passes. RunOptimizationPasses(blocks, config); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs index 8d05f99af..9b78c8aaa 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs @@ -13,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations switch (operation.Inst) { case Instruction.Add: - case Instruction.BitwiseExclusiveOr: TryEliminateBinaryOpCommutative(operation, 0); break; @@ -21,6 +20,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations TryEliminateBitwiseAnd(operation); break; + case Instruction.BitwiseExclusiveOr: + if (!TryEliminateXorSwap(operation)) + { + TryEliminateBinaryOpCommutative(operation, 0); + } + break; + case Instruction.BitwiseOr: TryEliminateBitwiseOr(operation); break; @@ -49,8 +55,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static void TryEliminateBitwiseAnd(Operation operation) { // Try to recognize and optimize those 3 patterns (in order): - // x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y, - // x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000 + // x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y, + // x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000 + Operand x = operation.GetSource(0); Operand y = operation.GetSource(1); @@ -68,11 +75,62 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } + private static bool TryEliminateXorSwap(Operation xCopyOp) + { + // Try to recognize XOR swap pattern: + // x = x ^ y + // y = x ^ y + // x = x ^ y + // Or, in SSA: + // x2 = x ^ y + // y2 = x2 ^ y + // x3 = x2 ^ y2 + // Transform it into something more sane: + // temp = y + // y = x + // x = temp + + // Note that because XOR is commutative, there are actually + // multiple possible combinations of this pattern, for + // simplicity this only catches one of them. + + Operand x = xCopyOp.GetSource(0); + Operand y = xCopyOp.GetSource(1); + + if (x.AsgOp is not Operation tCopyOp || tCopyOp.Inst != Instruction.BitwiseExclusiveOr || + y.AsgOp is not Operation yCopyOp || yCopyOp.Inst != Instruction.BitwiseExclusiveOr) + { + return false; + } + + if (tCopyOp == yCopyOp) + { + return false; + } + + if (yCopyOp.GetSource(0) != x || + yCopyOp.GetSource(1) != tCopyOp.GetSource(1) || + x.UseOps.Count != 2) + { + return false; + } + + x = tCopyOp.GetSource(0); + y = tCopyOp.GetSource(1); + + tCopyOp.TurnIntoCopy(y); // Temp = Y + yCopyOp.TurnIntoCopy(x); // Y = X + xCopyOp.TurnIntoCopy(tCopyOp.Dest); // X = Temp + + return true; + } + private static void TryEliminateBitwiseOr(Operation operation) { // Try to recognize and optimize those 3 patterns (in order): - // x | 0x00000000 == x, 0x00000000 | y == y, - // x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF + // x | 0x00000000 == x, 0x00000000 | y == y, + // x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF + Operand x = operation.GetSource(0); Operand y = operation.GetSource(1); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs index a0d58d079..ffbd16f85 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.Translation.Optimizations { @@ -93,5 +94,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return source; } + + public static void DeleteNode(LinkedListNode<INode> node, Operation operation) + { + node.List.Remove(node); + + for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + { + operation.SetSource(srcIndex, null); + } + + operation.Dest = null; + } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index a2cfbe227..2d19a5a70 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -14,6 +14,11 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly string _stagePrefix; private readonly int[] _cbSlotToBindingMap; + private readonly int[] _sbSlotToBindingMap; + private uint _sbSlotWritten; + + private readonly Dictionary<int, int> _sbSlots; + private readonly Dictionary<int, int> _sbSlotsReverse; private readonly HashSet<int> _usedConstantBufferBindings; @@ -26,7 +31,12 @@ namespace Ryujinx.Graphics.Shader.Translation _stagePrefix = GetShaderStagePrefix(stage); _cbSlotToBindingMap = new int[18]; + _sbSlotToBindingMap = new int[16]; _cbSlotToBindingMap.AsSpan().Fill(-1); + _sbSlotToBindingMap.AsSpan().Fill(-1); + + _sbSlots = new Dictionary<int, int>(); + _sbSlotsReverse = new Dictionary<int, int>(); _usedConstantBufferBindings = new HashSet<int>(); @@ -47,6 +57,52 @@ namespace Ryujinx.Graphics.Shader.Translation return binding; } + public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding) + { + if (!TryGetSbSlot((byte)sbCbSlot, (ushort)sbCbOffset, out int slot)) + { + binding = 0; + return false; + } + + binding = _sbSlotToBindingMap[slot]; + + if (binding < 0) + { + binding = _gpuAccessor.QueryBindingStorageBuffer(slot); + _sbSlotToBindingMap[slot] = binding; + string slotNumber = slot.ToString(CultureInfo.InvariantCulture); + AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}"); + } + + if (write) + { + _sbSlotWritten |= 1u << slot; + } + + return true; + } + + private bool TryGetSbSlot(byte sbCbSlot, ushort sbCbOffset, out int slot) + { + int key = PackSbCbInfo(sbCbSlot, sbCbOffset); + + if (!_sbSlots.TryGetValue(key, out slot)) + { + slot = _sbSlots.Count; + + if (slot >= _sbSlotToBindingMap.Length) + { + return false; + } + + _sbSlots.Add(key, slot); + _sbSlotsReverse.Add(slot, key); + } + + return true; + } + public bool TryGetConstantBufferSlot(int binding, out int slot) { for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++) @@ -90,6 +146,34 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors; } + public BufferDescriptor[] GetStorageBufferDescriptors() + { + var descriptors = new BufferDescriptor[_sbSlots.Count]; + + int descriptorIndex = 0; + + foreach ((int key, int slot) in _sbSlots) + { + int binding = _sbSlotToBindingMap[slot]; + + if (binding >= 0) + { + (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); + descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset) + { + Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None + }; + } + } + + if (descriptors.Length != descriptorIndex) + { + Array.Resize(ref descriptors, descriptorIndex); + } + + return descriptors; + } + private void AddNewConstantBuffer(int binding, string name) { StructureType type = new StructureType(new[] @@ -100,6 +184,16 @@ namespace Ryujinx.Graphics.Shader.Translation _properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); } + private void AddNewStorageBuffer(int binding, string name) + { + StructureType type = new StructureType(new[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + }); + + _properties.AddStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type)); + } + public static string GetShaderStagePrefix(ShaderStage stage) { uint index = (uint)stage; @@ -111,5 +205,15 @@ namespace Ryujinx.Graphics.Shader.Translation return _stagePrefixes[index]; } + + private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset) + { + return sbCbOffset | ((int)sbCbSlot << 16); + } + + private static (int, int) UnpackSbCbInfo(int key) + { + return ((byte)(key >> 16), (ushort)key); + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 866ae5223..baa88251b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -2,10 +2,8 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System.Collections.Generic; using System.Linq; -using System.Numerics; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; -using static Ryujinx.Graphics.Shader.Translation.GlobalMemory; namespace Ryujinx.Graphics.Shader.Translation { @@ -23,11 +21,10 @@ namespace Ryujinx.Graphics.Shader.Translation { BasicBlock block = blocks[blkIndex]; - for (LinkedListNode<INode> node = block.Operations.First; node != null;) + for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) { if (node.Value is not Operation operation) { - node = node.Next; continue; } @@ -56,8 +53,6 @@ namespace Ryujinx.Graphics.Shader.Translation InsertVectorComponentSelect(node, config); } - LinkedListNode<INode> nextNode = node.Next; - if (operation is TextureOperation texOp) { node = InsertTexelFetchScale(hfm, node, config); @@ -74,15 +69,7 @@ namespace Ryujinx.Graphics.Shader.Translation node = InsertSnormNormalization(node, config); } } - - nextNode = node.Next; } - else if (UsesGlobalMemory(operation.Inst, operation.StorageKind)) - { - nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode; - } - - node = nextNode; } } } @@ -184,196 +171,6 @@ namespace Ryujinx.Graphics.Shader.Translation operation.TurnIntoCopy(result); } - private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config) - { - Operation operation = (Operation)node.Value; - - bool isAtomic = operation.Inst.IsAtomic(); - bool isStg16Or8 = operation.Inst == Instruction.StoreGlobal16 || operation.Inst == Instruction.StoreGlobal8; - bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal || isStg16Or8; - - Operation storageOp = null; - - Operand PrependOperation(Instruction inst, params Operand[] sources) - { - Operand local = Local(); - - node.List.AddBefore(node, new Operation(inst, local, sources)); - - return local; - } - - Operand PrependStorageOperation(Instruction inst, StorageKind storageKind, params Operand[] sources) - { - Operand local = Local(); - - node.List.AddBefore(node, new Operation(inst, storageKind, local, sources)); - - return local; - } - - Operand PrependExistingOperation(Operation operation) - { - Operand local = Local(); - - operation.Dest = local; - node.List.AddBefore(node, operation); - - return local; - } - - Operand addrLow = operation.GetSource(0); - Operand addrHigh = operation.GetSource(1); - - Operand sbBaseAddrLow = Const(0); - Operand sbSlot = Const(0); - - Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); - - Operand BindingRangeCheck(int cbOffset, out Operand baseAddrLow) - { - baseAddrLow = Cbuf(DriverReservedCb, cbOffset); - Operand baseAddrHigh = Cbuf(DriverReservedCb, cbOffset + 1); - Operand size = Cbuf(DriverReservedCb, cbOffset + 2); - - Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow); - Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow); - - Operand inRangeLow = PrependOperation(Instruction.CompareLessU32, offset, size); - - Operand addrHighBorrowed = PrependOperation(Instruction.Add, addrHigh, borrow); - - Operand inRangeHigh = PrependOperation(Instruction.CompareEqual, addrHighBorrowed, baseAddrHigh); - - return PrependOperation(Instruction.BitwiseAnd, inRangeLow, inRangeHigh); - } - - int sbUseMask = config.AccessibleStorageBuffersMask; - - while (sbUseMask != 0) - { - int slot = BitOperations.TrailingZeroCount(sbUseMask); - - sbUseMask &= ~(1 << slot); - - int cbOffset = GetStorageCbOffset(config.Stage, slot); - slot = config.GetSbSlot(DriverReservedCb, (ushort)cbOffset); - - config.SetUsedStorageBuffer(slot, isWrite); - - Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow); - - sbBaseAddrLow = PrependOperation(Instruction.ConditionalSelect, inRange, baseAddrLow, sbBaseAddrLow); - sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot); - } - - if (config.AccessibleStorageBuffersMask != 0) - { - Operand baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, alignMask); - Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc); - - Operand[] sources = new Operand[operation.SourcesCount]; - - sources[0] = sbSlot; - - if (isStg16Or8) - { - sources[1] = byteOffset; - } - else - { - sources[1] = PrependOperation(Instruction.ShiftRightU32, byteOffset, Const(2)); - } - - for (int index = 2; index < operation.SourcesCount; index++) - { - sources[index] = operation.GetSource(index); - } - - if (isAtomic) - { - storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources); - } - else if (operation.Inst == Instruction.LoadGlobal) - { - storageOp = new Operation(Instruction.LoadStorage, operation.Dest, sources); - } - else - { - Instruction storeInst = operation.Inst switch - { - Instruction.StoreGlobal16 => Instruction.StoreStorage16, - Instruction.StoreGlobal8 => Instruction.StoreStorage8, - _ => Instruction.StoreStorage - }; - - storageOp = new Operation(storeInst, null, sources); - } - } - else if (operation.Dest != null) - { - storageOp = new Operation(Instruction.Copy, operation.Dest, Const(0)); - } - - if (operation.Inst == Instruction.LoadGlobal) - { - int cbeUseMask = config.AccessibleConstantBuffersMask; - - while (cbeUseMask != 0) - { - int slot = BitOperations.TrailingZeroCount(cbeUseMask); - int cbSlot = UbeFirstCbuf + slot; - - cbeUseMask &= ~(1 << slot); - - Operand previousResult = PrependExistingOperation(storageOp); - - int cbOffset = GetConstantUbeOffset(slot); - - Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow); - - Operand baseAddrTruncConst = PrependOperation(Instruction.BitwiseAnd, baseAddrLow, alignMask); - Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst); - - Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2)); - Operand vecIndex = PrependOperation(Instruction.ShiftRightU32, cbIndex, Const(2)); - Operand elemIndex = PrependOperation(Instruction.BitwiseAnd, cbIndex, Const(3)); - - Operand[] sourcesCb = new Operand[4]; - - sourcesCb[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot)); - sourcesCb[1] = Const(0); - sourcesCb[2] = vecIndex; - sourcesCb[3] = elemIndex; - - Operand ldcResult = PrependStorageOperation(Instruction.Load, StorageKind.ConstantBuffer, sourcesCb); - - storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult); - } - } - - for (int index = 0; index < operation.SourcesCount; index++) - { - operation.SetSource(index, null); - } - - LinkedListNode<INode> oldNode = node; - LinkedList<INode> oldNodeList = oldNode.List; - - if (storageOp != null) - { - node = node.List.AddBefore(node, storageOp); - } - else - { - node = null; - } - - oldNodeList.Remove(oldNode); - - return node; - } - private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) { TextureOperation texOp = (TextureOperation)node.Value; diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 40a32e2dc..5c0a1fb60 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -110,12 +110,6 @@ namespace Ryujinx.Graphics.Shader.Translation public UInt128 NextInputAttributesComponents { get; private set; } public UInt128 ThisInputAttributesComponents { get; private set; } - public int AccessibleStorageBuffersMask { get; private set; } - public int AccessibleConstantBuffersMask { get; private set; } - - private int _usedStorageBuffers; - private int _usedStorageBuffersWrite; - private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format); private struct TextureMeta @@ -127,18 +121,9 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures; private readonly Dictionary<TextureInfo, TextureMeta> _usedImages; - - private readonly Dictionary<int, int> _sbSlots; - private readonly Dictionary<int, int> _sbSlotsReverse; - - private BufferDescriptor[] _cachedStorageBufferDescriptors; private TextureDescriptor[] _cachedTextureDescriptors; private TextureDescriptor[] _cachedImageDescriptors; - private int _firstStorageBufferBinding; - - public int FirstStorageBufferBinding => _firstStorageBufferBinding; - public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options) { Stage = stage; @@ -147,18 +132,12 @@ namespace Ryujinx.Graphics.Shader.Translation _transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>(); - AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1; - AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1; - UsedInputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>(); _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); _usedImages = new Dictionary<TextureInfo, TextureMeta>(); - _sbSlots = new Dictionary<int, int>(); - _sbSlotsReverse = new Dictionary<int, int>(); - ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); } @@ -173,11 +152,6 @@ namespace Ryujinx.Graphics.Shader.Translation OutputTopology = outputTopology; MaxOutputVertices = maxOutputVertices; TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); - - if (Stage != ShaderStage.Compute) - { - AccessibleConstantBuffersMask = 0; - } } public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options) @@ -433,8 +407,6 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributes |= other.UsedInputAttributes; UsedOutputAttributes |= other.UsedOutputAttributes; - _usedStorageBuffers |= other._usedStorageBuffers; - _usedStorageBuffersWrite |= other._usedStorageBuffersWrite; foreach (var kv in other._usedTextures) { @@ -634,23 +606,6 @@ namespace Ryujinx.Graphics.Shader.Translation UsedFeatures |= flags; } - public void SetAccessibleBufferMasks(int sbMask, int ubeMask) - { - AccessibleStorageBuffersMask = sbMask; - AccessibleConstantBuffersMask = ubeMask; - } - - public void SetUsedStorageBuffer(int slot, bool write) - { - int mask = 1 << slot; - _usedStorageBuffers |= mask; - - if (write) - { - _usedStorageBuffersWrite |= mask; - } - } - public void SetUsedTexture( Instruction inst, SamplerType type, @@ -756,76 +711,6 @@ namespace Ryujinx.Graphics.Shader.Translation return meta; } - public BufferDescriptor[] GetStorageBufferDescriptors() - { - if (_cachedStorageBufferDescriptors != null) - { - return _cachedStorageBufferDescriptors; - } - - return _cachedStorageBufferDescriptors = GetStorageBufferDescriptors( - _usedStorageBuffers, - _usedStorageBuffersWrite, - true, - out _firstStorageBufferBinding, - GpuAccessor.QueryBindingStorageBuffer); - } - - private BufferDescriptor[] GetStorageBufferDescriptors( - int usedMask, - int writtenMask, - bool isArray, - out int firstBinding, - Func<int, int> getBindingCallback) - { - firstBinding = 0; - bool hasFirstBinding = false; - var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)]; - - int lastSlot = -1; - - for (int i = 0; i < descriptors.Length; i++) - { - int slot = BitOperations.TrailingZeroCount(usedMask); - - if (isArray) - { - // The next array entries also consumes bindings, even if they are unused. - for (int j = lastSlot + 1; j < slot; j++) - { - int binding = getBindingCallback(j); - - if (!hasFirstBinding) - { - firstBinding = binding; - hasFirstBinding = true; - } - } - } - - lastSlot = slot; - - (int sbCbSlot, int sbCbOffset) = GetSbCbInfo(slot); - - descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot, sbCbSlot, sbCbOffset); - - if (!hasFirstBinding) - { - firstBinding = descriptors[i].Binding; - hasFirstBinding = true; - } - - if ((writtenMask & (1 << slot)) != 0) - { - descriptors[i].SetFlag(BufferUsageFlags.Write); - } - - usedMask &= ~(1 << slot); - } - - return descriptors; - } - public TextureDescriptor[] GetTextureDescriptors() { return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture); @@ -922,45 +807,11 @@ namespace Ryujinx.Graphics.Shader.Translation return FindDescriptorIndex(GetImageDescriptors(), texOp); } - public int GetSbSlot(byte sbCbSlot, ushort sbCbOffset) - { - int key = PackSbCbInfo(sbCbSlot, sbCbOffset); - - if (!_sbSlots.TryGetValue(key, out int slot)) - { - slot = _sbSlots.Count; - _sbSlots.Add(key, slot); - _sbSlotsReverse.Add(slot, key); - } - - return slot; - } - - public (int, int) GetSbCbInfo(int slot) - { - if (_sbSlotsReverse.TryGetValue(slot, out int key)) - { - return UnpackSbCbInfo(key); - } - - throw new ArgumentException($"Invalid slot {slot}.", nameof(slot)); - } - - private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset) - { - return sbCbOffset | ((int)sbCbSlot << 16); - } - - private static (int, int) UnpackSbCbInfo(int key) - { - return ((byte)(key >> 16), (ushort)key); - } - public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None) { return new ShaderProgramInfo( ResourceManager.GetConstantBufferDescriptors(), - GetStorageBufferDescriptors(), + ResourceManager.GetStorageBufferDescriptors(), GetTextureDescriptors(), GetImageDescriptors(), identification, diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs index 867e24379..684004379 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.Translation continue; } - if (IsResourceWrite(operation.Inst)) + if (IsResourceWrite(operation.Inst, operation.StorageKind)) { return false; } @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Shader.Translation return totalVerticesCount + verticesCount == 3 && writesLayer; } - private static bool IsResourceWrite(Instruction inst) + private static bool IsResourceWrite(Instruction inst, StorageKind storageKind) { switch (inst) { @@ -170,13 +170,11 @@ namespace Ryujinx.Graphics.Shader.Translation case Instruction.AtomicXor: case Instruction.ImageAtomic: case Instruction.ImageStore: - case Instruction.StoreGlobal: - case Instruction.StoreGlobal16: - case Instruction.StoreGlobal8: - case Instruction.StoreStorage: - case Instruction.StoreStorage16: - case Instruction.StoreStorage8: return true; + case Instruction.Store: + return storageKind == StorageKind.StorageBuffer || + storageKind == StorageKind.SharedMemory || + storageKind == StorageKind.LocalMemory; } return false; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 5bbc00097..c0212a5bc 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.Translation Ssa.Rename(cfg.Blocks); - Optimizer.RunPass(cfg.Blocks, config); + Optimizer.RunPass(hfm, cfg.Blocks, config); Rewriter.RunPass(hfm, cfg.Blocks, config); } diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index e046bf899..521a132a7 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -115,8 +115,6 @@ namespace Ryujinx.Graphics.Vulkan holder = Create(gd, size, baseType: baseType, storageHint: storageHint); if (holder == null) { - Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\"."); - return BufferHandle.Null; } @@ -264,6 +262,8 @@ namespace Ryujinx.Graphics.Vulkan return holder; } + Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\"."); + return null; } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs index c57cb1a95..70b3ebfe4 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs @@ -70,30 +70,6 @@ namespace Ryujinx.Graphics.Vulkan } } - public unsafe void UpdateStorageBuffers(int setIndex, int baseBinding, ReadOnlySpan<DescriptorBufferInfo> bufferInfo) - { - if (bufferInfo.Length == 0) - { - return; - } - - fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo) - { - var writeDescriptorSet = new WriteDescriptorSet - { - SType = StructureType.WriteDescriptorSet, - DstSet = _descriptorSets[setIndex], - DstBinding = (uint)(baseBinding & ~(Constants.MaxStorageBuffersPerStage - 1)), - DstArrayElement = (uint)(baseBinding & (Constants.MaxStorageBuffersPerStage - 1)), - DescriptorType = DescriptorType.StorageBuffer, - DescriptorCount = (uint)bufferInfo.Length, - PBufferInfo = pBufferInfo - }; - - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); - } - } - public unsafe void UpdateImage(int setIndex, int bindingIndex, DescriptorImageInfo imageInfo, DescriptorType type) { if (imageInfo.ImageView.Handle != 0UL) diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index f3ac36e13..cbac1cd47 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -448,14 +448,7 @@ namespace Ryujinx.Graphics.Vulkan } ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers; - if (program.HasMinimalLayout) - { - dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer); - } - else - { - dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count)); - } + dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer); } else if (setIndex == PipelineBase.TextureSetIndex) { From d511c845b70a8771de7d64369e24ab3f1ed1c325 Mon Sep 17 00:00:00 2001 From: WilliamWsyHK <WilliamWsyHK@users.noreply.github.com> Date: Sun, 4 Jun 2023 11:30:24 +0800 Subject: [PATCH 603/737] Check KeyboardMode in GUI (#4343) * Update SoftwareKeyboard to send KeyboardMode to UI * Update GTK UI to check text against KeyboardMode * Update Ava UI to check text against KeyboardMode * Restructure input validation * true when text is not empty * Add English validation text for SoftwareKeyboardMode * Add Chinese validation text for SoftwareKeyboardMode * Update base on feedback --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 3 + src/Ryujinx.Ava/Assets/Locales/zh_CN.json | 3 + src/Ryujinx.Ava/Assets/Locales/zh_TW.json | 3 + .../UI/Applet/SwkbdAppletDialog.axaml.cs | 59 +++++++++++++++---- .../Applets/SoftwareKeyboard/KeyboardMode.cs | 2 +- .../SoftwareKeyboardApplet.cs | 1 + .../SoftwareKeyboardUiArgs.cs | 3 + src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs | 1 + src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs | 56 ++++++++++++++---- 9 files changed, 109 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 8f4965e14..79765db16 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -544,6 +544,9 @@ "SwkbdMinCharacters": "Must be at least {0} characters long", "SwkbdMinRangeCharacters": "Must be {0}-{1} characters long", "SoftwareKeyboard": "Software Keyboard", + "SoftwareKeyboardModeNumbersOnly": "Must be numbers only", + "SoftwareKeyboardModeAlphabet": "Must be alphabets only", + "SoftwareKeyboardModeASCII": "Must be ASCII text only", "DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.", "DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.", "DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n", diff --git a/src/Ryujinx.Ava/Assets/Locales/zh_CN.json b/src/Ryujinx.Ava/Assets/Locales/zh_CN.json index 25dc3cbaf..b47dda975 100644 --- a/src/Ryujinx.Ava/Assets/Locales/zh_CN.json +++ b/src/Ryujinx.Ava/Assets/Locales/zh_CN.json @@ -527,6 +527,9 @@ "SwkbdMinCharacters": "至少应为 {0} 个字长", "SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字长", "SoftwareKeyboard": "软件键盘", + "SoftwareKeyboardModeNumbersOnly": "只接受数字", + "SoftwareKeyboardModeAlphabet": "只接受英文字母", + "SoftwareKeyboardModeASCII": "只接受 ASCII 符号", "DialogControllerAppletMessagePlayerRange": "游戏需要 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。", "DialogControllerAppletMessage": "游戏需要刚好 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。", "DialogControllerAppletDockModeSet": "目前处于主机模式,无法使用掌机操作方式", diff --git a/src/Ryujinx.Ava/Assets/Locales/zh_TW.json b/src/Ryujinx.Ava/Assets/Locales/zh_TW.json index 940282a0f..e943691ce 100644 --- a/src/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/src/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -527,6 +527,9 @@ "SwkbdMinCharacters": "至少應為 {0} 個字長", "SwkbdMinRangeCharacters": "必須為 {0}-{1} 個字長", "SoftwareKeyboard": "軟體鍵盤", + "SoftwareKeyboardModeNumbersOnly": "只接受數字", + "SoftwareKeyboardModeAlphabet": "只接受英文字母", + "SoftwareKeyboardModeASCII": "只接受 ASCII 符號", "DialogControllerAppletMessagePlayerRange": "本遊戲需要 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。", "DialogControllerAppletMessage": "本遊戲需要剛好 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。", "DialogControllerAppletDockModeSet": "現在處於主機模式,無法使用掌機操作方式\n\n", diff --git a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index cb69e96b7..04bc46193 100644 --- a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -9,14 +9,17 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.HLE.HOS.Applets; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using System; +using System.Linq; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Controls { internal partial class SwkbdAppletDialog : UserControl { - private Predicate<int> _checkLength; + private Predicate<int> _checkLength = _ => true; + private Predicate<string> _checkInput = _ => true; private int _inputMax; private int _inputMin; private string _placeholder; @@ -35,8 +38,6 @@ namespace Ryujinx.Ava.UI.Controls Input.Watermark = _placeholder; Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true); - - SetInputLengthValidation(0, int.MaxValue); // Disable by default. } public SwkbdAppletDialog() @@ -67,6 +68,7 @@ namespace Ryujinx.Ava.UI.Controls string input = string.Empty; content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); + content.SetInputValidation(args.KeyboardMode); content._host = contentDialog; contentDialog.Title = title; @@ -91,6 +93,12 @@ namespace Ryujinx.Ava.UI.Controls return (result, input); } + private void ApplyValidationInfo(string text) + { + Error.IsVisible = !string.IsNullOrEmpty(text); + Error.Text = text; + } + public void SetInputLengthValidation(int min, int max) { _inputMin = Math.Min(min, max); @@ -99,6 +107,8 @@ namespace Ryujinx.Ava.UI.Controls Error.IsVisible = false; Error.FontStyle = FontStyle.Italic; + string validationInfoText = ""; + if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable. { Error.IsVisible = false; @@ -107,21 +117,48 @@ namespace Ryujinx.Ava.UI.Controls } else if (_inputMin > 0 && _inputMax == int.MaxValue) { - Error.IsVisible = true; - - Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin); + validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin); _checkLength = length => _inputMin <= length; } else { - Error.IsVisible = true; - - Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax); + validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax); _checkLength = length => _inputMin <= length && length <= _inputMax; } + ApplyValidationInfo(validationInfoText); + Message_TextInput(this, new TextInputEventArgs()); + } + + private void SetInputValidation(KeyboardMode mode) + { + string validationInfoText = Error.Text; + string localeText; + switch (mode) + { + case KeyboardMode.NumbersOnly: + localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeNumbersOnly); + validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText); + _checkInput = text => text.All(char.IsDigit); + break; + case KeyboardMode.Alphabet: + localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet); + validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText); + _checkInput = text => text.All(char.IsAsciiLetter); + break; + case KeyboardMode.ASCII: + localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII); + validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText); + _checkInput = text => text.All(char.IsAscii); + break; + default: + _checkInput = _ => true; + break; + } + + ApplyValidationInfo(validationInfoText); Message_TextInput(this, new TextInputEventArgs()); } @@ -129,7 +166,7 @@ namespace Ryujinx.Ava.UI.Controls { if (_host != null) { - _host.IsPrimaryButtonEnabled = _checkLength(Message.Length); + _host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message); } } @@ -141,7 +178,7 @@ namespace Ryujinx.Ava.UI.Controls } else { - _host.IsPrimaryButtonEnabled = _checkLength(Message.Length); + _host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs index f512050e1..01b3c9634 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs @@ -3,7 +3,7 @@ /// <summary> /// Identifies the variant of keyboard displayed on screen. /// </summary> - enum KeyboardMode : uint + public enum KeyboardMode : uint { /// <summary> /// A full alpha-numeric keyboard. diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 278ea56c2..4b484e802 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -209,6 +209,7 @@ namespace Ryujinx.HLE.HOS.Applets // Call the configured GUI handler to get user's input. var args = new SoftwareKeyboardUiArgs { + KeyboardMode = _keyboardForegroundConfig.Mode, HeaderText = StripUnicodeControlCodes(_keyboardForegroundConfig.HeaderText), SubtitleText = StripUnicodeControlCodes(_keyboardForegroundConfig.SubtitleText), GuideText = StripUnicodeControlCodes(_keyboardForegroundConfig.GuideText), diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs index d24adec33..d67a44094 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs @@ -1,7 +1,10 @@ +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; + namespace Ryujinx.HLE.HOS.Applets { public struct SoftwareKeyboardUiArgs { + public KeyboardMode KeyboardMode; public string HeaderText; public string SubtitleText; public string InitialText; diff --git a/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs b/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs index d81cbe3c5..b7577b85d 100644 --- a/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs +++ b/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs @@ -106,6 +106,7 @@ namespace Ryujinx.Ui.Applet swkbdDialog.OkButton.Label = args.SubmitText; swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); + swkbdDialog.SetInputValidation(args.KeyboardMode); if (swkbdDialog.Run() == (int)ResponseType.Ok) { diff --git a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs index 7c14f0e8d..28067b751 100644 --- a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs +++ b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs @@ -1,5 +1,7 @@ using Gtk; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using System; +using System.Linq; namespace Ryujinx.Ui.Applet { @@ -7,8 +9,12 @@ namespace Ryujinx.Ui.Applet { private int _inputMin; private int _inputMax; + private KeyboardMode _mode; - private Predicate<int> _checkLength; + private string _validationInfoText = ""; + + private Predicate<int> _checkLength = _ => true; + private Predicate<string> _checkInput = _ => true; private readonly Label _validationInfo; @@ -38,8 +44,12 @@ namespace Ryujinx.Ui.Applet ((Box)MessageArea).PackEnd(_validationInfo, true, true, 0); ((Box)MessageArea).PackEnd(InputEntry, true, true, 4); + } - SetInputLengthValidation(0, int.MaxValue); // Disable by default. + private void ApplyValidationInfo() + { + _validationInfo.Visible = !string.IsNullOrEmpty(_validationInfoText); + _validationInfo.Markup = _validationInfoText; } public void SetInputLengthValidation(int min, int max) @@ -53,23 +63,49 @@ namespace Ryujinx.Ui.Applet { _validationInfo.Visible = false; - _checkLength = (length) => true; + _checkLength = _ => true; } else if (_inputMin > 0 && _inputMax == int.MaxValue) { - _validationInfo.Visible = true; - _validationInfo.Markup = $"<i>Must be at least {_inputMin} characters long</i>"; + _validationInfoText = $"<i>Must be at least {_inputMin} characters long.</i> "; - _checkLength = (length) => _inputMin <= length; + _checkLength = length => _inputMin <= length; } else { - _validationInfo.Visible = true; - _validationInfo.Markup = $"<i>Must be {_inputMin}-{_inputMax} characters long</i>"; + _validationInfoText = $"<i>Must be {_inputMin}-{_inputMax} characters long.</i> "; - _checkLength = (length) => _inputMin <= length && length <= _inputMax; + _checkLength = length => _inputMin <= length && length <= _inputMax; } + ApplyValidationInfo(); + OnInputChanged(this, EventArgs.Empty); + } + + public void SetInputValidation(KeyboardMode mode) + { + _mode = mode; + + switch (mode) + { + case KeyboardMode.NumbersOnly: + _validationInfoText += "<i>Must be numbers only.</i>"; + _checkInput = text => text.All(char.IsDigit); + break; + case KeyboardMode.Alphabet: + _validationInfoText += "<i>Must be alphabets only.</i>"; + _checkInput = text => text.All(char.IsAsciiLetter); + break; + case KeyboardMode.ASCII: + _validationInfoText += "<i>Must be ASCII text only.</i>"; + _checkInput = text => text.All(char.IsAscii); + break; + default: + _checkInput = _ => true; + break; + } + + ApplyValidationInfo(); OnInputChanged(this, EventArgs.Empty); } @@ -83,7 +119,7 @@ namespace Ryujinx.Ui.Applet private void OnInputChanged(object sender, EventArgs e) { - OkButton.Sensitive = _checkLength(InputEntry.Text.Length); + OkButton.Sensitive = _checkLength(InputEntry.Text.Length) && _checkInput(InputEntry.Text); } } } \ No newline at end of file From d2f3adbf69396488bfc5ab7b515a6f6bcfdf0c8b Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sun, 4 Jun 2023 21:25:57 +0100 Subject: [PATCH 604/737] Texture: Fix layout conversion when gobs in z is used with depth = 1 (#5220) * Texture: Fix layout conversion when gobs in z is used with depth = 1 The size calculator methods deliberately reduce the gob size of textures if they are deemed too small for it. This is required to get correct sizes when iterating mip levels of a texture. Rendering to a slice of a 3D texture can produce a 3D texture with depth 1, but a gob size matching a much larger texture. We _can't_ "correct" this gob size, as it is intended as a slice of a larger 3D texture. Ignoring it causes layout conversion to break on read and flush. This caused an issue in Tears of the Kingdom where the compressed 3D texture used for the gloom would always break on OpenGL, and seemingly randomly break on Vulkan. In the first case, the data is forcibly flushed to decompress the BC4 texture on the CPU to upload it as 3D, which was broken due to the incorrect layout. In the second, the data may be randomly flushed if it falls out of the cache, but it will appear correct if it's able to form copy dependencies. This change only allows gob sizes to be reduced once per mip level. For the purpose of aligned size, it can still be reduced infinitely as our texture cache isn't properly able to handle a view being _misaligned_. The SizeCalculator has also been changed to reduce the size of rendered depth slices to only include the exact range a single depth slice will cover. (before, the size was way too small with gobs in z reduced to 1, and too large when using the correct value) Gobs in Y logic remains untouched, we don't support Y slices of textures so it's fine as is. This is probably worth testing in a few games as it also affects texture size and view logic. * Improve wording * Maybe a bit better --- .../Image/TextureCompatibility.cs | 9 ++++--- src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 2 +- .../LayoutConverter.cs | 4 ++-- .../SizeCalculator.cs | 24 ++++++++++++++----- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 85ad0bb01..9a8d048ed 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -541,7 +541,8 @@ namespace Ryujinx.Graphics.Gpu.Image depth, lhs.FormatInfo.BlockHeight, lhs.GobBlocksInY, - lhs.GobBlocksInZ); + lhs.GobBlocksInZ, + level); return gobBlocksInY == rhs.GobBlocksInY && gobBlocksInZ == rhs.GobBlocksInZ; @@ -587,7 +588,8 @@ namespace Ryujinx.Graphics.Gpu.Image lhsDepth, lhs.FormatInfo.BlockHeight, lhs.GobBlocksInY, - lhs.GobBlocksInZ); + lhs.GobBlocksInZ, + lhsLevel); int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel); int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel); @@ -597,7 +599,8 @@ namespace Ryujinx.Graphics.Gpu.Image rhsDepth, rhs.FormatInfo.BlockHeight, rhs.GobBlocksInY, - rhs.GobBlocksInZ); + rhs.GobBlocksInZ, + rhsLevel); return lhsGobBlocksInY == rhsGobBlocksInY && lhsGobBlocksInZ == rhsGobBlocksInZ; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index dbcb2e75e..bade9bbb3 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Image depthOrLayers = Math.Max(1, depthOrLayers >> minLod); } - (gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ); + (gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ, minLod); } levels = (maxLod - minLod) + 1; diff --git a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs index 09eaf3001..503985c98 100644 --- a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.Texture mipGobBlocksInY >>= 1; } - while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) + if (level > 0 && d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) { mipGobBlocksInZ >>= 1; } @@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Texture mipGobBlocksInY >>= 1; } - while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) + if (level > 0 && d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) { mipGobBlocksInZ >>= 1; } diff --git a/src/Ryujinx.Graphics.Texture/SizeCalculator.cs b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs index 5568784f8..4ddd8d4df 100644 --- a/src/Ryujinx.Graphics.Texture/SizeCalculator.cs +++ b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Texture int gobBlocksInTileX, int gpuLayerSize = 0) { - bool is3D = depth > 1; + bool is3D = depth > 1 || gobBlocksInZ > 1; int layerSize = 0; @@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Texture mipGobBlocksInY >>= 1; } - while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) + if (level > 0 && d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) { mipGobBlocksInZ >>= 1; } @@ -88,6 +88,9 @@ namespace Ryujinx.Graphics.Texture int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize; + mipOffsets[level] = layerSize; + sliceSizes[level] = totalBlocksOfGobsInY * robSize; + if (is3D) { int gobSize = mipGobBlocksInY * GobSize; @@ -105,10 +108,18 @@ namespace Ryujinx.Graphics.Texture allOffsets[z + depthLevelOffset] = baseOffset + zLow * gobSize + zHigh * sliceSize; } + + int gobRemainderZ = d % mipGobBlocksInZ; + + if (gobRemainderZ != 0 && level == levels - 1) + { + // The slice only covers up to the end of this slice's depth, rather than the full aligned size. + // Avoids size being too large on partial views of 3d textures. + + sliceSizes[level] -= gobSize * (mipGobBlocksInZ - gobRemainderZ); + } } - mipOffsets[level] = layerSize; - sliceSizes[level] = totalBlocksOfGobsInY * robSize; levelSizes[level] = totalBlocksOfGobsInZ * sliceSizes[level]; layerSize += levelSizes[level]; @@ -267,7 +278,8 @@ namespace Ryujinx.Graphics.Texture int depth, int blockHeight, int gobBlocksInY, - int gobBlocksInZ) + int gobBlocksInZ, + int level = int.MaxValue) { height = BitUtils.DivRoundUp(height, blockHeight); @@ -276,7 +288,7 @@ namespace Ryujinx.Graphics.Texture gobBlocksInY >>= 1; } - while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1) + while (level-- > 0 && depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1) { gobBlocksInZ >>= 1; } From 8954ff3af20b8b6bb3940c073b24cda1c81703d5 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Sun, 4 Jun 2023 17:30:04 -0300 Subject: [PATCH 605/737] Replacing ZbcColorArray with Array4<uint> (#5210) * Related "if/else if" statements should not have the same condition * replacing ZbcColorArray with Array4<uint> * fix alignment --- .../Types/ZbcSetTableArguments.cs | 47 +++---------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs index ed74cc263..a4651f844 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs @@ -1,49 +1,16 @@ -using System; +using Ryujinx.Common.Memory; +using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types { - [StructLayout(LayoutKind.Sequential)] - struct ZbcColorArray - { - private uint element0; - private uint element1; - private uint element2; - private uint element3; - - public uint this[int index] - { - get - { - if (index == 0) - { - return element0; - } - else if (index == 1) - { - return element1; - } - else if (index == 2) - { - return element2; - } - else if (index == 2) - { - return element3; - } - - throw new IndexOutOfRangeException(); - } - } - } - [StructLayout(LayoutKind.Sequential)] struct ZbcSetTableArguments { - public ZbcColorArray ColorDs; - public ZbcColorArray ColorL2; - public uint Depth; - public uint Format; - public uint Type; + public Array4<uint> ColorDs; + public Array4<uint> ColorL2; + public uint Depth; + public uint Format; + public uint Type; } } From 306f7e93a0d99f7844c42a6e3c84ee9040f17864 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:19:46 +0100 Subject: [PATCH 606/737] Dont Error on Invalid Enum Values (#5169) * Dont Error on Invalid Enum * Use TryParse * Log warning --- .../Utilities/TypedStringEnumConverter.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs b/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs index c0127dc4a..73765129e 100644 --- a/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs +++ b/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs @@ -1,4 +1,5 @@ #nullable enable +using Ryujinx.Common.Logging; using System; using System.Text.Json; using System.Text.Json.Serialization; @@ -18,12 +19,14 @@ namespace Ryujinx.Common.Utilities public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var enumValue = reader.GetString(); - if (string.IsNullOrEmpty(enumValue)) + + if (Enum.TryParse(enumValue, out TEnum value)) { - return default; + return value; } - return Enum.Parse<TEnum>(enumValue); + Logger.Warning?.Print(LogClass.Configuration, $"Failed to parse enum value \"{enumValue}\" for {typeof(TEnum)}, using default \"{default(TEnum)}\""); + return default; } public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) @@ -31,4 +34,4 @@ namespace Ryujinx.Common.Utilities writer.WriteStringValue(value.ToString()); } } -} +} \ No newline at end of file From d98da47a0fd7966be34a37c89a262902b9fa413a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Arrouye?= <5017270+tarrouye@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:48:11 -0700 Subject: [PATCH 607/737] Better application grid flex (#5218) --- src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml index f7a120b1a..3d55793f9 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml @@ -32,10 +32,10 @@ <ListBox.ItemsPanel> <ItemsPanelTemplate> <flex:FlexPanel - HorizontalAlignment="Stretch" + HorizontalAlignment="Center" VerticalAlignment="Stretch" AlignContent="FlexStart" - JustifyContent="Center" /> + JustifyContent="FlexStart" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.Styles> From 68848000f77e587ed4cd99e84a234fcf79fd7ea3 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 5 Jun 2023 12:33:09 +0100 Subject: [PATCH 608/737] Texture: Fix 3D texture size when totalBlocksOfGobsInZ > 1 (#5228) * Texture: Fix 3D texture size when totalBlocksOfGobsInZ > 0 When there is a remainder when dividing depth by gobs in z, it is used to remove the unused part of the 3D texture's size. This was done to calculate correct sizes for single slice views of 3D textures. However, this case can also apply to 3D textures with many slices, and more than one total block of gobs in z. In this case it's meant to trim off the end of the level size. Most textures won't encounter this as their size will be aligned, but UE4 games tend to use 3D textures with funny unaligned sizes. The size offset should have been applied to the level size instead of the slice size, and it should only affect the slice size if it ends up larger. Hopefully should fix issues with UE4 games without breaking other stuff, I don't have much time to test. * Whoops --- src/Ryujinx.Graphics.Texture/SizeCalculator.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Texture/SizeCalculator.cs b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs index 4ddd8d4df..0120bd7ac 100644 --- a/src/Ryujinx.Graphics.Texture/SizeCalculator.cs +++ b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs @@ -90,6 +90,7 @@ namespace Ryujinx.Graphics.Texture mipOffsets[level] = layerSize; sliceSizes[level] = totalBlocksOfGobsInY * robSize; + levelSizes[level] = totalBlocksOfGobsInZ * sliceSizes[level]; if (is3D) { @@ -116,12 +117,15 @@ namespace Ryujinx.Graphics.Texture // The slice only covers up to the end of this slice's depth, rather than the full aligned size. // Avoids size being too large on partial views of 3d textures. - sliceSizes[level] -= gobSize * (mipGobBlocksInZ - gobRemainderZ); + levelSizes[level] -= gobSize * (mipGobBlocksInZ - gobRemainderZ); + + if (sliceSizes[level] > levelSizes[level]) + { + sliceSizes[level] = levelSizes[level]; + } } } - levelSizes[level] = totalBlocksOfGobsInZ * sliceSizes[level]; - layerSize += levelSizes[level]; depthLevelOffset += d; From af1906ea04dad5972a6a2771a44f353c97dec326 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 5 Jun 2023 09:01:33 -0300 Subject: [PATCH 609/737] Fix wrong unaligned SB state when fetching compute shaders (#5223) --- .../Engine/Compute/ComputeClass.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 8227a7ff1..d8103ac71 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -151,8 +151,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute ShaderProgramInfo info = cs.Shaders[0].Info; - bool hasUnaligned = _channel.BufferManager.HasUnalignedStorageBuffers; - for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; @@ -177,9 +175,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute _channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags); } - if ((_channel.BufferManager.HasUnalignedStorageBuffers) != hasUnaligned) + if (_channel.BufferManager.HasUnalignedStorageBuffers != computeState.HasUnalignedStorageBuffer) { // Refetch the shader, as assumptions about storage buffer alignment have changed. + computeState = new GpuChannelComputeState( + qmd.CtaThreadDimension0, + qmd.CtaThreadDimension1, + qmd.CtaThreadDimension2, + localMemorySize, + sharedMemorySize, + _channel.BufferManager.HasUnalignedStorageBuffers); + cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); From 5813b2e354e072d43eb30c51083e737cf6bc1ee2 Mon Sep 17 00:00:00 2001 From: Kurochi51 <andrei5125@gmail.com> Date: Mon, 5 Jun 2023 15:19:17 +0300 Subject: [PATCH 610/737] Updater: Ignore files introduced by the user in base directory (#5092) * Updater: Ignore files introduced by the user in base directory * Replicate logic in Avalonia version. * Address requested changes * Updater: Ignore files introduced by the user in base directory * Replicate logic in Avalonia version. * Address requested changes * Address requested changes * Address requested changes * Comment cleanup * Address feedback * Forgot comment, tehe --- src/Ryujinx.Ava/Modules/Updater/Updater.cs | 12 ++++++++++++ src/Ryujinx/Modules/Updater/Updater.cs | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Ryujinx.Ava/Modules/Updater/Updater.cs b/src/Ryujinx.Ava/Modules/Updater/Updater.cs index 77d77d794..839526c4e 100644 --- a/src/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/src/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -740,6 +740,18 @@ namespace Ryujinx.Modules { var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. + // Determine and exclude user files only when the updater is running, not when cleaning old files + if (_running) + { + // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. + var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename)); + + // Remove user files from the paths in files. + files = files.Except(userFiles); + } + if (OperatingSystem.IsWindows()) { foreach (string dir in WindowsDependencyDirs) diff --git a/src/Ryujinx/Modules/Updater/Updater.cs b/src/Ryujinx/Modules/Updater/Updater.cs index 3e0dc99b4..344edf9e5 100644 --- a/src/Ryujinx/Modules/Updater/Updater.cs +++ b/src/Ryujinx/Modules/Updater/Updater.cs @@ -565,6 +565,18 @@ namespace Ryujinx.Modules { var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. + // Determine and exclude user files only when the updater is running, not when cleaning old files + if (Running) + { + // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. + var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename)); + + // Remove user files from the paths in files. + files = files.Except(userFiles); + } + if (OperatingSystem.IsWindows()) { foreach (string dir in WindowsDependencyDirs) From fe30c03cac9d1f09270a4156aceab273dbac81fb Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 8 Jun 2023 17:09:14 -0300 Subject: [PATCH 611/737] Implement soft float64 conversion on shaders when host has no support (#5159) * Implement soft float64 conversion on shaders when host has no support * Shader cache version bump * Fix rebase error --- src/Ryujinx.Graphics.GAL/Capabilities.cs | 3 + .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 2 + src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 1 + src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 ++ .../IntermediateRepresentation/Operation.cs | 30 +++++++ .../Translation/HelperFunctionManager.cs | 89 +++++++++++++++++++ .../Translation/HelperFunctionName.cs | 5 +- .../Optimizations/DoubleToFloat.cs | 70 +++++++++++++++ .../Translation/Optimizations/Optimizer.cs | 10 +++ .../HardwareCapabilities.cs | 3 + src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 2 + 12 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index 48b37d35d..f2dd0963f 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsCubemapView; public readonly bool SupportsNonConstantTextureOffset; public readonly bool SupportsShaderBallot; + public readonly bool SupportsShaderFloat64; public readonly bool SupportsTextureShadowLod; public readonly bool SupportsViewportIndexVertexTessellation; public readonly bool SupportsViewportMask; @@ -81,6 +82,7 @@ namespace Ryujinx.Graphics.GAL bool supportsCubemapView, bool supportsNonConstantTextureOffset, bool supportsShaderBallot, + bool supportsShaderFloat64, bool supportsTextureShadowLod, bool supportsViewportIndexVertexTessellation, bool supportsViewportMask, @@ -124,6 +126,7 @@ namespace Ryujinx.Graphics.GAL SupportsCubemapView = supportsCubemapView; SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; SupportsShaderBallot = supportsShaderBallot; + SupportsShaderFloat64 = supportsShaderFloat64; SupportsTextureShadowLod = supportsTextureShadowLod; SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation; SupportsViewportMask = supportsViewportMask; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 4b828080d..9419ea92c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4992; + private const uint CodeGenVersion = 5159; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 0001243d4..a60564e0e 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -141,6 +141,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot; + public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64; + public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat; public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod; diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 161191b85..234340e5f 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -158,6 +158,7 @@ namespace Ryujinx.Graphics.OpenGL supportsCubemapView: true, supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset, supportsShaderBallot: HwCapabilities.SupportsShaderBallot, + supportsShaderFloat64: true, supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, supportsViewportMask: HwCapabilities.SupportsViewportArray2, diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 473964def..d4f99e11c 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -331,6 +331,15 @@ namespace Ryujinx.Graphics.Shader return true; } + /// <summary> + /// Queries host GPU support for 64-bit floating point (double precision) operations on the shader. + /// </summary> + /// <returns>True if the GPU and driver supports double operations, false otherwise</returns> + bool QueryHostSupportsShaderFloat64() + { + return true; + } + /// <summary> /// Queries host GPU support for signed normalized buffer texture formats. /// </summary> diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index d502a9b65..425cfd909 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -255,5 +255,35 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation _sources = new Operand[] { source }; } + + public void TurnDoubleIntoFloat() + { + if ((Inst & ~Instruction.Mask) == Instruction.FP64) + { + Inst = (Inst & Instruction.Mask) | Instruction.FP32; + } + else + { + switch (Inst) + { + case Instruction.ConvertFP32ToFP64: + case Instruction.ConvertFP64ToFP32: + Inst = Instruction.Copy; + break; + case Instruction.ConvertFP64ToS32: + Inst = Instruction.ConvertFP32ToS32; + break; + case Instruction.ConvertFP64ToU32: + Inst = Instruction.ConvertFP32ToU32; + break; + case Instruction.ConvertS32ToFP64: + Inst = Instruction.ConvertS32ToFP32; + break; + case Instruction.ConvertU32ToFP64: + Inst = Instruction.ConvertU32ToFP32; + break; + } + } + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs index 7dd267f3c..6958b86f2 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs @@ -45,12 +45,101 @@ namespace Ryujinx.Graphics.Shader.Translation { return functionName switch { + HelperFunctionName.ConvertDoubleToFloat => GenerateConvertDoubleToFloatFunction(), + HelperFunctionName.ConvertFloatToDouble => GenerateConvertFloatToDoubleFunction(), HelperFunctionName.TexelFetchScale => GenerateTexelFetchScaleFunction(), HelperFunctionName.TextureSizeUnscale => GenerateTextureSizeUnscaleFunction(), _ => throw new ArgumentException($"Invalid function name {functionName}") }; } + private Function GenerateConvertDoubleToFloatFunction() + { + EmitterContext context = new EmitterContext(); + + Operand valueLow = Argument(0); + Operand valueHigh = Argument(1); + + Operand mantissaLow = context.BitwiseAnd(valueLow, Const(((1 << 22) - 1))); + Operand mantissa = context.ShiftRightU32(valueLow, Const(22)); + + mantissa = context.BitwiseOr(mantissa, context.ShiftLeft(context.BitwiseAnd(valueHigh, Const(0xfffff)), Const(10))); + mantissa = context.BitwiseOr(mantissa, context.ConditionalSelect(mantissaLow, Const(1), Const(0))); + + Operand exp = context.BitwiseAnd(context.ShiftRightU32(valueHigh, Const(20)), Const(0x7ff)); + Operand sign = context.ShiftRightS32(valueHigh, Const(31)); + + Operand resultSign = context.ShiftLeft(sign, Const(31)); + + Operand notZero = context.BitwiseOr(mantissa, exp); + + Operand lblNotZero = Label(); + + context.BranchIfTrue(lblNotZero, notZero); + + context.Return(resultSign); + + context.MarkLabel(lblNotZero); + + Operand notNaNOrInf = context.ICompareNotEqual(exp, Const(0x7ff)); + + mantissa = context.BitwiseOr(mantissa, Const(0x40000000)); + exp = context.ISubtract(exp, Const(0x381)); + + // Note: Overflow cases are not handled here and might produce incorrect results. + + Operand roundBits = context.BitwiseAnd(mantissa, Const(0x7f)); + Operand roundBitsXor64 = context.BitwiseExclusiveOr(roundBits, Const(0x40)); + mantissa = context.ShiftRightU32(context.IAdd(mantissa, Const(0x40)), Const(7)); + mantissa = context.BitwiseAnd(mantissa, context.ConditionalSelect(roundBitsXor64, Const(~0), Const(~1))); + + exp = context.ConditionalSelect(mantissa, exp, Const(0)); + exp = context.ConditionalSelect(notNaNOrInf, exp, Const(0xff)); + + Operand result = context.IAdd(context.IAdd(mantissa, context.ShiftLeft(exp, Const(23))), resultSign); + + context.Return(result); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ConvertDoubleToFloat", true, 2, 0); + } + + private Function GenerateConvertFloatToDoubleFunction() + { + EmitterContext context = new EmitterContext(); + + Operand value = Argument(0); + + Operand mantissa = context.BitwiseAnd(value, Const(0x7fffff)); + Operand exp = context.BitwiseAnd(context.ShiftRightU32(value, Const(23)), Const(0xff)); + Operand sign = context.ShiftRightS32(value, Const(31)); + + Operand notNaNOrInf = context.ICompareNotEqual(exp, Const(0xff)); + Operand expNotZero = context.ICompareNotEqual(exp, Const(0)); + Operand notDenorm = context.BitwiseOr(expNotZero, context.ICompareEqual(mantissa, Const(0))); + + exp = context.IAdd(exp, Const(0x380)); + + Operand shiftDist = context.ISubtract(Const(32), context.FindMSBU32(mantissa)); + Operand normExp = context.ISubtract(context.ISubtract(Const(1), shiftDist), Const(1)); + Operand normMant = context.ShiftLeft(mantissa, shiftDist); + + exp = context.ConditionalSelect(notNaNOrInf, exp, Const(0x7ff)); + exp = context.ConditionalSelect(notDenorm, exp, normExp); + mantissa = context.ConditionalSelect(expNotZero, mantissa, normMant); + + Operand resultLow = context.ShiftLeft(mantissa, Const(29)); + Operand resultHigh = context.ShiftRightU32(mantissa, Const(3)); + + resultHigh = context.IAdd(resultHigh, context.ShiftLeft(exp, Const(20))); + resultHigh = context.IAdd(resultHigh, context.ShiftLeft(sign, Const(31))); + + context.Copy(Argument(1), resultLow); + context.Copy(Argument(2), resultHigh); + context.Return(); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ConvertFloatToDouble", false, 1, 2); + } + private Function GenerateTexelFetchScaleFunction() { EmitterContext context = new EmitterContext(); diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs index 5accdf65f..8c37c34c7 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs @@ -1,10 +1,9 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using System.Collections.Generic; - namespace Ryujinx.Graphics.Shader.Translation { enum HelperFunctionName { + ConvertDoubleToFloat, + ConvertFloatToDouble, TexelFetchScale, TextureSizeUnscale } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs new file mode 100644 index 000000000..42bce5cc2 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs @@ -0,0 +1,70 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + static class DoubleToFloat + { + public static void RunPass(HelperFunctionManager hfm, BasicBlock block) + { + for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) + { + if (node.Value is not Operation operation) + { + continue; + } + + node = InsertSoftFloat64(hfm, node); + } + } + + private static LinkedListNode<INode> InsertSoftFloat64(HelperFunctionManager hfm, LinkedListNode<INode> node) + { + Operation operation = (Operation)node.Value; + + if (operation.Inst == Instruction.PackDouble2x32) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.ConvertDoubleToFloat); + + Operand[] callArgs = new Operand[] { Const(functionId), operation.GetSource(0), operation.GetSource(1) }; + + Operand floatValue = operation.Dest; + + operation.Dest = null; + + LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, floatValue, callArgs)); + + Utils.DeleteNode(node, operation); + + return newNode; + } + else if (operation.Inst == Instruction.UnpackDouble2x32) + { + int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.ConvertFloatToDouble); + + // TODO: Allow UnpackDouble2x32 to produce two outputs and get rid of "operation.Index". + + Operand resultLow = operation.Index == 0 ? operation.Dest : Local(); + Operand resultHigh = operation.Index == 1 ? operation.Dest : Local(); + + operation.Dest = null; + + Operand[] callArgs = new Operand[] { Const(functionId), operation.GetSource(0), resultLow, resultHigh }; + + LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, (Operand)null, callArgs)); + + Utils.DeleteNode(node, operation); + + return newNode; + } + else + { + operation.TurnDoubleIntoFloat(); + + return node; + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index bdb3a62ec..8d2669c0d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -11,8 +11,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { RunOptimizationPasses(blocks, config); + // TODO: Some of those are not optimizations and shouldn't be here. + GlobalToStorage.RunPass(hfm, blocks, config); + bool hostSupportsShaderFloat64 = config.GpuAccessor.QueryHostSupportsShaderFloat64(); + // Those passes are looking for specific patterns and only needs to run once. for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { @@ -24,6 +28,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { EliminateMultiplyByFragmentCoordW(blocks[blkIndex]); } + + // If the host does not support double operations, we need to turn them into float operations. + if (!hostSupportsShaderFloat64) + { + DoubleToFloat.RunPass(hfm, blocks[blkIndex]); + } } // Run optimizations one last time to remove any code that is now optimizable after above passes. diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index f600d93f0..393bcf1a2 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsSubgroupSizeControl; + public readonly bool SupportsShaderFloat64; public readonly bool SupportsShaderInt8; public readonly bool SupportsShaderStencilExport; public readonly bool SupportsShaderStorageImageMultisample; @@ -63,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsFragmentShaderInterlock, bool supportsGeometryShaderPassthrough, bool supportsSubgroupSizeControl, + bool supportsShaderFloat64, bool supportsShaderInt8, bool supportsShaderStencilExport, bool supportsShaderStorageImageMultisample, @@ -99,6 +101,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsSubgroupSizeControl = supportsSubgroupSizeControl; + SupportsShaderFloat64 = supportsShaderFloat64; SupportsShaderInt8 = supportsShaderInt8; SupportsShaderStencilExport = supportsShaderStencilExport; SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 3987be9b4..0daec00c3 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -306,6 +306,7 @@ namespace Ryujinx.Graphics.Vulkan _physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"), _physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"), supportsSubgroupSizeControl, + features2.Features.ShaderFloat64, featuresShaderInt8.ShaderInt8, _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"), features2.Features.ShaderStorageImageMultisample, @@ -594,6 +595,7 @@ namespace Ryujinx.Graphics.Vulkan supportsCubemapView: !IsAmdGcn, supportsNonConstantTextureOffset: false, supportsShaderBallot: false, + supportsShaderFloat64: Capabilities.SupportsShaderFloat64, supportsTextureShadowLod: false, supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex, supportsViewportMask: Capabilities.SupportsViewportArray2, From 2cdcfe46d8959b0cbd8aea3b4439b30a55d47f00 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 8 Jun 2023 17:43:16 -0300 Subject: [PATCH 612/737] Remove barrier on Intel if control flow is potentially divergent (#5044) * Remove barrier on Intel if control flow is potentially divergent * Shader cache version bump --- src/Ryujinx.Graphics.GAL/Capabilities.cs | 3 ++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 2 ++ src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 2 ++ .../CodeGen/Glsl/GlslGenerator.cs | 32 ++++++++++++++++--- .../CodeGen/Spirv/CodeGenContext.cs | 7 +++- .../CodeGen/Spirv/Instructions.cs | 12 +++++++ .../CodeGen/Spirv/SpirvGenerator.cs | 2 +- src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 ++++++ src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 10 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index f2dd0963f..3b6e6b906 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsCubemapView; public readonly bool SupportsNonConstantTextureOffset; public readonly bool SupportsShaderBallot; + public readonly bool SupportsShaderBarrierDivergence; public readonly bool SupportsShaderFloat64; public readonly bool SupportsTextureShadowLod; public readonly bool SupportsViewportIndexVertexTessellation; @@ -82,6 +83,7 @@ namespace Ryujinx.Graphics.GAL bool supportsCubemapView, bool supportsNonConstantTextureOffset, bool supportsShaderBallot, + bool supportsShaderBarrierDivergence, bool supportsShaderFloat64, bool supportsTextureShadowLod, bool supportsViewportIndexVertexTessellation, @@ -126,6 +128,7 @@ namespace Ryujinx.Graphics.GAL SupportsCubemapView = supportsCubemapView; SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; SupportsShaderBallot = supportsShaderBallot; + SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; SupportsShaderFloat64 = supportsShaderFloat64; SupportsTextureShadowLod = supportsTextureShadowLod; SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 9419ea92c..f35b542a2 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5159; + private const uint CodeGenVersion = 5044; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index a60564e0e..57e79ac7f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -141,6 +141,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot; + public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence; + public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64; public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat; diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 234340e5f..81faa00ef 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -127,6 +127,7 @@ namespace Ryujinx.Graphics.OpenGL public Capabilities GetCapabilities() { bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows; + bool intelUnix = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelUnix; bool amdWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows; return new Capabilities( @@ -158,6 +159,7 @@ namespace Ryujinx.Graphics.OpenGL supportsCubemapView: true, supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset, supportsShaderBallot: HwCapabilities.SupportsShaderBallot, + supportsShaderBarrierDivergence: !(intelWindows || intelUnix), supportsShaderFloat64: true, supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 751d03507..fe0d275b6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -28,18 +28,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl for (int i = 1; i < info.Functions.Count; i++) { - PrintFunction(context, info, info.Functions[i]); + PrintFunction(context, info.Functions[i]); context.AppendLine(); } } - PrintFunction(context, info, info.Functions[0], MainFunctionName); + PrintFunction(context, info.Functions[0], MainFunctionName); return context.GetCode(); } - private static void PrintFunction(CodeGenContext context, StructuredProgramInfo info, StructuredFunction function, string funcName = null) + private static void PrintFunction(CodeGenContext context, StructuredFunction function, string funcName = null) { context.CurrentFunction = function; @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl Declarations.DeclareLocals(context, function); - PrintBlock(context, function.MainBlock); + PrintBlock(context, function.MainBlock, funcName == MainFunctionName); context.LeaveScope(); } @@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"{Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; } - private static void PrintBlock(CodeGenContext context, AstBlock block) + private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction) { AstBlockVisitor visitor = new AstBlockVisitor(block); @@ -112,10 +112,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } }; + bool supportsBarrierDivergence = context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence(); + 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.Config.GpuAccessor.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) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index c1bfa0883..1f5167e66 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -76,6 +76,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public SpirvDelegates Delegates { get; } + public bool IsMainFunction { get; private set; } + public bool MayHaveReturned { get; set; } + public CodeGenContext( StructuredProgramInfo info, ShaderConfig config, @@ -108,8 +111,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Delegates = new SpirvDelegates(this); } - public void StartFunction() + public void StartFunction(bool isMainFunction) { + IsMainFunction = isMainFunction; + MayHaveReturned = false; _locals.Clear(); _localForArgs.Clear(); _funcArgs.Clear(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 4be0c62be..6c1157525 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -242,6 +242,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateBarrier(CodeGenContext context, AstOperation operation) { + // Barrier on divergent control flow paths may cause the GPU to hang, + // so skip emitting the barrier for those cases. + if (!context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence() && + (context.CurrentBlock.Type != AstBlockType.Main || context.MayHaveReturned || !context.IsMainFunction)) + { + context.Config.GpuAccessor.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); + + return OperationResult.Invalid; + } + context.ControlBarrier( context.Constant(context.TypeU32(), Scope.Workgroup), context.Constant(context.TypeU32(), Scope.Workgroup), @@ -1092,6 +1102,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation) { + context.MayHaveReturned = true; + if (operation.SourcesCount != 0) { context.ReturnValue(context.Get(context.CurrentFunction.ReturnType, operation.GetSource(0))); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index a55e09fd3..5c736b605 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.CurrentFunction = function; context.AddFunction(spvFunc); - context.StartFunction(); + context.StartFunction(isMainFunction: funcIndex == 0); Declarations.DeclareParameters(context, function); diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index d4f99e11c..d3794cddd 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -331,6 +331,15 @@ namespace Ryujinx.Graphics.Shader return true; } + /// <summary> + /// Queries host GPU shader support for barrier instructions on divergent control flow paths. + /// </summary> + /// <returns>True if the GPU supports barriers on divergent control flow paths, false otherwise</returns> + bool QueryHostSupportsShaderBarrierDivergence() + { + return true; + } + /// <summary> /// Queries host GPU support for 64-bit floating point (double precision) operations on the shader. /// </summary> diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 0daec00c3..a059d683a 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -595,6 +595,7 @@ namespace Ryujinx.Graphics.Vulkan supportsCubemapView: !IsAmdGcn, supportsNonConstantTextureOffset: false, supportsShaderBallot: false, + supportsShaderBarrierDivergence: Vendor != Vendor.Intel, supportsShaderFloat64: Capabilities.SupportsShaderFloat64, supportsTextureShadowLod: false, supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex, From 0003a7c11815d2a9dbbd5bf89845c9d90f6fff62 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Fri, 9 Jun 2023 00:23:36 +0100 Subject: [PATCH 613/737] Vulkan: Use aspect flags for identity views for bindings (#5267) --- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index c2be74974..eb094b3e6 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; private readonly Auto<DisposableImageView> _imageView; + private readonly Auto<DisposableImageView> _imageViewDraw; private readonly Auto<DisposableImageView> _imageViewIdentity; private readonly Auto<DisposableImageView> _imageView2dArray; private Dictionary<GAL.Format, TextureView> _selfManagedViews; @@ -127,7 +128,8 @@ namespace Ryujinx.Graphics.Vulkan ComponentSwizzle.B, ComponentSwizzle.A); - _imageViewIdentity = CreateImageView(identityComponentMapping, subresourceRangeDepth, type, usage); + _imageViewDraw = CreateImageView(identityComponentMapping, subresourceRangeDepth, type, usage); + _imageViewIdentity = aspectFlagsDepth == aspectFlags ? _imageViewDraw : CreateImageView(identityComponentMapping, subresourceRange, type, usage); // Framebuffer attachments also require 3D textures to be bound as 2D array. if (info.Target == Target.Texture3D) @@ -169,7 +171,7 @@ namespace Ryujinx.Graphics.Vulkan public Auto<DisposableImageView> GetImageViewForAttachment() { - return _imageView2dArray ?? _imageViewIdentity; + return _imageView2dArray ?? _imageViewDraw; } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) @@ -909,6 +911,11 @@ namespace Ryujinx.Graphics.Vulkan _imageViewIdentity.Dispose(); _imageView2dArray?.Dispose(); + if (_imageViewDraw != _imageViewIdentity) + { + _imageViewDraw.Dispose(); + } + Storage.DecrementViewsCount(); } } From 0e8e735a6d02d46da3dd49091e13eef2a25475c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 10:40:25 +0200 Subject: [PATCH 614/737] nuget: bump System.IdentityModel.Tokens.Jwt from 6.30.1 to 6.31.0 (#5265) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.30.1 to 6.31.0. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.30.1...6.31.0) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 9922f0719..5fe384a1c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> - <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" /> + <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.1" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> From f35aa8e9d6cbef59e287fc338932e6d4a78cfc0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 11:02:56 +0200 Subject: [PATCH 615/737] nuget: bump Microsoft.NET.Test.Sdk from 17.6.1 to 17.6.2 (#5250) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.1 to 17.6.2. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.1...v17.6.2) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5fe384a1c..9a1118844 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,7 +21,7 @@ <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> - <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.1" /> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.2" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> From 86de288142188341a98fab6d132152077b391fbd Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Fri, 9 Jun 2023 06:23:44 -0300 Subject: [PATCH 616/737] Removing shift by 0 (#5249) * Integral numbers should not be shifted by zero or more than their number of bits-1 * more --- .../Engine/Compute/ComputeClassState.cs | 100 ++-- .../Engine/Dma/DmaClassState.cs | 26 +- .../Engine/GPFifo/CompressedMethod.cs | 2 +- .../Engine/GPFifo/GPEntry.cs | 6 +- .../Engine/GPFifo/GPFifoClassState.cs | 18 +- .../InlineToMemoryClassState.cs | 20 +- .../Engine/Threed/ThreedClassState.cs | 20 +- .../Engine/Twod/TwodClassState.cs | 82 +-- .../Decoders/InstDecoders.cs | 530 +++++++++--------- src/Ryujinx.Graphics.Shader/TextureHandle.cs | 2 +- src/Ryujinx.Graphics.Vulkan/PipelineUid.cs | 2 +- .../HOS/Kernel/Process/KHandleTable.cs | 2 +- 12 files changed, 405 insertions(+), 405 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs index 5d81de5de..73dd31b23 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs @@ -100,22 +100,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { #pragma warning disable CS0649 public uint SetObject; - public int SetObjectClassId => (int)((SetObject >> 0) & 0xFFFF); + public int SetObjectClassId => (int)(SetObject & 0xFFFF); public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)((SetNotifyA >> 0) & 0xFF); + public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); public uint SetNotifyB; public uint Notify; public NotifyType NotifyType => (NotifyType)(Notify); public uint WaitForIdle; public fixed uint Reserved114[7]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)((SetGlobalRenderEnableA >> 0) & 0xFF); + public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)((SetGlobalRenderEnableC >> 0) & 0x7); + public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public uint PmTriggerWfi; @@ -126,11 +126,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint LineLengthIn; public uint LineCount; public uint OffsetOutUpper; - public int OffsetOutUpperValue => (int)((OffsetOutUpper >> 0) & 0xFF); + public int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); public uint OffsetOut; public uint PitchOut; public uint SetDstBlockSize; - public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)((SetDstBlockSize >> 0) & 0xF); + public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); public uint SetDstWidth; @@ -138,11 +138,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOriginBytesX; - public int SetDstOriginBytesXV => (int)((SetDstOriginBytesX >> 0) & 0xFFFFF); + public int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); public uint SetDstOriginSamplesY; - public int SetDstOriginSamplesYV => (int)((SetDstOriginSamplesY >> 0) & 0xFFFF); + public int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); public uint LaunchDma; - public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)((LaunchDma >> 0) & 0x1); + public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); @@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint LoadInlineData; public fixed uint Reserved1B8[9]; public uint SetI2mSemaphoreA; - public int SetI2mSemaphoreAOffsetUpper => (int)((SetI2mSemaphoreA >> 0) & 0xFF); + public int SetI2mSemaphoreAOffsetUpper => (int)(SetI2mSemaphoreA & 0xFF); public uint SetI2mSemaphoreB; public uint SetI2mSemaphoreC; public fixed uint Reserved1E8[2]; @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetI2mSpareNoop02; public uint SetI2mSpareNoop03; public uint SetValidSpanOverflowAreaA; - public int SetValidSpanOverflowAreaAAddressUpper => (int)((SetValidSpanOverflowAreaA >> 0) & 0xFF); + public int SetValidSpanOverflowAreaAAddressUpper => (int)(SetValidSpanOverflowAreaA & 0xFF); public uint SetValidSpanOverflowAreaB; public uint SetValidSpanOverflowAreaC; public uint SetCoalesceWaitingPeriodUnit; @@ -185,12 +185,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetReservedSwMethod06; public uint SetReservedSwMethod07; public uint SetCwdControl; - public SetCwdControlSmSelection SetCwdControlSmSelection => (SetCwdControlSmSelection)((SetCwdControl >> 0) & 0x1); + public SetCwdControlSmSelection SetCwdControlSmSelection => (SetCwdControlSmSelection)(SetCwdControl & 0x1); public uint InvalidateTextureHeaderCacheNoWfi; - public InvalidateCacheLines InvalidateTextureHeaderCacheNoWfiLines => (InvalidateCacheLines)((InvalidateTextureHeaderCacheNoWfi >> 0) & 0x1); + public InvalidateCacheLines InvalidateTextureHeaderCacheNoWfiLines => (InvalidateCacheLines)(InvalidateTextureHeaderCacheNoWfi & 0x1); public int InvalidateTextureHeaderCacheNoWfiTag => (int)((InvalidateTextureHeaderCacheNoWfi >> 4) & 0x3FFFFF); public uint SetCwdRefCounter; - public int SetCwdRefCounterSelect => (int)((SetCwdRefCounter >> 0) & 0x3F); + public int SetCwdRefCounterSelect => (int)(SetCwdRefCounter & 0x3F); public int SetCwdRefCounterValue => (int)((SetCwdRefCounter >> 8) & 0xFFFF); public uint SetReservedSwMethod08; public uint SetReservedSwMethod09; @@ -201,28 +201,28 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetReservedSwMethod14; public uint SetReservedSwMethod15; public uint SetGwcScgType; - public SetGwcScgTypeScgType SetGwcScgTypeScgType => (SetGwcScgTypeScgType)((SetGwcScgType >> 0) & 0x1); + public SetGwcScgTypeScgType SetGwcScgTypeScgType => (SetGwcScgTypeScgType)(SetGwcScgType & 0x1); public uint SetScgControl; - public int SetScgControlCompute1MaxSmCount => (int)((SetScgControl >> 0) & 0x1FF); + public int SetScgControlCompute1MaxSmCount => (int)(SetScgControl & 0x1FF); public uint InvalidateConstantBufferCacheA; - public int InvalidateConstantBufferCacheAAddressUpper => (int)((InvalidateConstantBufferCacheA >> 0) & 0xFF); + public int InvalidateConstantBufferCacheAAddressUpper => (int)(InvalidateConstantBufferCacheA & 0xFF); public uint InvalidateConstantBufferCacheB; public uint InvalidateConstantBufferCacheC; - public int InvalidateConstantBufferCacheCByteCount => (int)((InvalidateConstantBufferCacheC >> 0) & 0x1FFFF); + public int InvalidateConstantBufferCacheCByteCount => (int)(InvalidateConstantBufferCacheC & 0x1FFFF); public bool InvalidateConstantBufferCacheCThruL2 => (InvalidateConstantBufferCacheC & 0x80000000) != 0; public uint SetComputeClassVersion; - public int SetComputeClassVersionCurrent => (int)((SetComputeClassVersion >> 0) & 0xFFFF); + public int SetComputeClassVersionCurrent => (int)(SetComputeClassVersion & 0xFFFF); public int SetComputeClassVersionOldestSupported => (int)((SetComputeClassVersion >> 16) & 0xFFFF); public uint CheckComputeClassVersion; - public int CheckComputeClassVersionCurrent => (int)((CheckComputeClassVersion >> 0) & 0xFFFF); + public int CheckComputeClassVersionCurrent => (int)(CheckComputeClassVersion & 0xFFFF); public int CheckComputeClassVersionOldestSupported => (int)((CheckComputeClassVersion >> 16) & 0xFFFF); public uint SetQmdVersion; - public int SetQmdVersionCurrent => (int)((SetQmdVersion >> 0) & 0xFFFF); + public int SetQmdVersionCurrent => (int)(SetQmdVersion & 0xFFFF); public int SetQmdVersionOldestSupported => (int)((SetQmdVersion >> 16) & 0xFFFF); public uint SetWfiConfig; public bool SetWfiConfigEnableScgTypeWfi => (SetWfiConfig & 0x1) != 0; public uint CheckQmdVersion; - public int CheckQmdVersionCurrent => (int)((CheckQmdVersion >> 0) & 0xFFFF); + public int CheckQmdVersionCurrent => (int)(CheckQmdVersion & 0xFFFF); public int CheckQmdVersionOldestSupported => (int)((CheckQmdVersion >> 16) & 0xFFFF); public uint WaitForIdleScgType; public uint InvalidateSkedCaches; @@ -231,28 +231,28 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public bool SetScgRenderEnableControlCompute1UsesRenderEnable => (SetScgRenderEnableControl & 0x1) != 0; public fixed uint Reserved2A0[4]; public uint SetCwdSlotCount; - public int SetCwdSlotCountV => (int)((SetCwdSlotCount >> 0) & 0xFF); + public int SetCwdSlotCountV => (int)(SetCwdSlotCount & 0xFF); public uint SendPcasA; public uint SendPcasB; - public int SendPcasBFrom => (int)((SendPcasB >> 0) & 0xFFFFFF); + public int SendPcasBFrom => (int)(SendPcasB & 0xFFFFFF); public int SendPcasBDelta => (int)((SendPcasB >> 24) & 0xFF); public uint SendSignalingPcasB; public bool SendSignalingPcasBInvalidate => (SendSignalingPcasB & 0x1) != 0; public bool SendSignalingPcasBSchedule => (SendSignalingPcasB & 0x2) != 0; public fixed uint Reserved2C0[9]; public uint SetShaderLocalMemoryNonThrottledA; - public int SetShaderLocalMemoryNonThrottledASizeUpper => (int)((SetShaderLocalMemoryNonThrottledA >> 0) & 0xFF); + public int SetShaderLocalMemoryNonThrottledASizeUpper => (int)(SetShaderLocalMemoryNonThrottledA & 0xFF); public uint SetShaderLocalMemoryNonThrottledB; public uint SetShaderLocalMemoryNonThrottledC; - public int SetShaderLocalMemoryNonThrottledCMaxSmCount => (int)((SetShaderLocalMemoryNonThrottledC >> 0) & 0x1FF); + public int SetShaderLocalMemoryNonThrottledCMaxSmCount => (int)(SetShaderLocalMemoryNonThrottledC & 0x1FF); public uint SetShaderLocalMemoryThrottledA; - public int SetShaderLocalMemoryThrottledASizeUpper => (int)((SetShaderLocalMemoryThrottledA >> 0) & 0xFF); + public int SetShaderLocalMemoryThrottledASizeUpper => (int)(SetShaderLocalMemoryThrottledA & 0xFF); public uint SetShaderLocalMemoryThrottledB; public uint SetShaderLocalMemoryThrottledC; - public int SetShaderLocalMemoryThrottledCMaxSmCount => (int)((SetShaderLocalMemoryThrottledC >> 0) & 0x1FF); + public int SetShaderLocalMemoryThrottledCMaxSmCount => (int)(SetShaderLocalMemoryThrottledC & 0x1FF); public fixed uint Reserved2FC[5]; public uint SetSpaVersion; - public int SetSpaVersionMinor => (int)((SetSpaVersion >> 0) & 0xFF); + public int SetSpaVersionMinor => (int)(SetSpaVersion & 0xFF); public int SetSpaVersionMajor => (int)((SetSpaVersion >> 8) & 0xFF); public fixed uint Reserved314[123]; public uint SetFalcon00; @@ -291,14 +291,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetShaderLocalMemoryWindow; public fixed uint Reserved780[4]; public uint SetShaderLocalMemoryA; - public int SetShaderLocalMemoryAAddressUpper => (int)((SetShaderLocalMemoryA >> 0) & 0xFF); + public int SetShaderLocalMemoryAAddressUpper => (int)(SetShaderLocalMemoryA & 0xFF); public uint SetShaderLocalMemoryB; public fixed uint Reserved798[383]; public uint SetShaderCacheControl; public bool SetShaderCacheControlIcachePrefetchEnable => (SetShaderCacheControl & 0x1) != 0; public fixed uint ReservedD98[19]; public uint SetSmTimeoutInterval; - public int SetSmTimeoutIntervalCounterBit => (int)((SetSmTimeoutInterval >> 0) & 0x3F); + public int SetSmTimeoutIntervalCounterBit => (int)(SetSmTimeoutInterval & 0x3F); public fixed uint ReservedDE8[87]; public uint SetSpareNoop12; public uint SetSpareNoop13; @@ -324,48 +324,48 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public bool InvalidateTextureHeaderCacheAllV => (InvalidateTextureHeaderCacheAll & 0x1) != 0; public fixed uint Reserved1214[29]; public uint InvalidateTextureDataCacheNoWfi; - public InvalidateCacheLines InvalidateTextureDataCacheNoWfiLines => (InvalidateCacheLines)((InvalidateTextureDataCacheNoWfi >> 0) & 0x1); + public InvalidateCacheLines InvalidateTextureDataCacheNoWfiLines => (InvalidateCacheLines)(InvalidateTextureDataCacheNoWfi & 0x1); public int InvalidateTextureDataCacheNoWfiTag => (int)((InvalidateTextureDataCacheNoWfi >> 4) & 0x3FFFFF); public fixed uint Reserved128C[7]; public uint ActivatePerfSettingsForComputeContext; public bool ActivatePerfSettingsForComputeContextAll => (ActivatePerfSettingsForComputeContext & 0x1) != 0; public fixed uint Reserved12AC[33]; public uint InvalidateSamplerCache; - public InvalidateCacheLines InvalidateSamplerCacheLines => (InvalidateCacheLines)((InvalidateSamplerCache >> 0) & 0x1); + public InvalidateCacheLines InvalidateSamplerCacheLines => (InvalidateCacheLines)(InvalidateSamplerCache & 0x1); public int InvalidateSamplerCacheTag => (int)((InvalidateSamplerCache >> 4) & 0x3FFFFF); public uint InvalidateTextureHeaderCache; - public InvalidateCacheLines InvalidateTextureHeaderCacheLines => (InvalidateCacheLines)((InvalidateTextureHeaderCache >> 0) & 0x1); + public InvalidateCacheLines InvalidateTextureHeaderCacheLines => (InvalidateCacheLines)(InvalidateTextureHeaderCache & 0x1); public int InvalidateTextureHeaderCacheTag => (int)((InvalidateTextureHeaderCache >> 4) & 0x3FFFFF); public uint InvalidateTextureDataCache; - public InvalidateCacheLines InvalidateTextureDataCacheLines => (InvalidateCacheLines)((InvalidateTextureDataCache >> 0) & 0x1); + public InvalidateCacheLines InvalidateTextureDataCacheLines => (InvalidateCacheLines)(InvalidateTextureDataCache & 0x1); public int InvalidateTextureDataCacheTag => (int)((InvalidateTextureDataCache >> 4) & 0x3FFFFF); public fixed uint Reserved133C[58]; public uint InvalidateSamplerCacheNoWfi; - public InvalidateCacheLines InvalidateSamplerCacheNoWfiLines => (InvalidateCacheLines)((InvalidateSamplerCacheNoWfi >> 0) & 0x1); + public InvalidateCacheLines InvalidateSamplerCacheNoWfiLines => (InvalidateCacheLines)(InvalidateSamplerCacheNoWfi & 0x1); public int InvalidateSamplerCacheNoWfiTag => (int)((InvalidateSamplerCacheNoWfi >> 4) & 0x3FFFFF); public fixed uint Reserved1428[64]; public uint SetShaderExceptions; public bool SetShaderExceptionsEnable => (SetShaderExceptions & 0x1) != 0; public fixed uint Reserved152C[9]; public uint SetRenderEnableA; - public int SetRenderEnableAOffsetUpper => (int)((SetRenderEnableA >> 0) & 0xFF); + public int SetRenderEnableAOffsetUpper => (int)(SetRenderEnableA & 0xFF); public uint SetRenderEnableB; public uint SetRenderEnableC; - public int SetRenderEnableCMode => (int)((SetRenderEnableC >> 0) & 0x7); + public int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); public uint SetTexSamplerPoolA; - public int SetTexSamplerPoolAOffsetUpper => (int)((SetTexSamplerPoolA >> 0) & 0xFF); + public int SetTexSamplerPoolAOffsetUpper => (int)(SetTexSamplerPoolA & 0xFF); public uint SetTexSamplerPoolB; public uint SetTexSamplerPoolC; - public int SetTexSamplerPoolCMaximumIndex => (int)((SetTexSamplerPoolC >> 0) & 0xFFFFF); + public int SetTexSamplerPoolCMaximumIndex => (int)(SetTexSamplerPoolC & 0xFFFFF); public fixed uint Reserved1568[3]; public uint SetTexHeaderPoolA; - public int SetTexHeaderPoolAOffsetUpper => (int)((SetTexHeaderPoolA >> 0) & 0xFF); + public int SetTexHeaderPoolAOffsetUpper => (int)(SetTexHeaderPoolA & 0xFF); public uint SetTexHeaderPoolB; public uint SetTexHeaderPoolC; - public int SetTexHeaderPoolCMaximumIndex => (int)((SetTexHeaderPoolC >> 0) & 0x3FFFFF); + public int SetTexHeaderPoolCMaximumIndex => (int)(SetTexHeaderPoolC & 0x3FFFFF); public fixed uint Reserved1580[34]; public uint SetProgramRegionA; - public int SetProgramRegionAAddressUpper => (int)((SetProgramRegionA >> 0) & 0xFF); + public int SetProgramRegionAAddressUpper => (int)(SetProgramRegionA & 0xFF); public uint SetProgramRegionB; public fixed uint Reserved1610[34]; public uint InvalidateShaderCachesNoWfi; @@ -374,7 +374,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public bool InvalidateShaderCachesNoWfiConstant => (InvalidateShaderCachesNoWfi & 0x1000) != 0; public fixed uint Reserved169C[170]; public uint SetRenderEnableOverride; - public SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)((SetRenderEnableOverride >> 0) & 0x3); + public SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)(SetRenderEnableOverride & 0x3); public fixed uint Reserved1948[57]; public uint PipeNop; public uint SetSpare00; @@ -383,11 +383,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetSpare03; public fixed uint Reserved1A40[48]; public uint SetReportSemaphoreA; - public int SetReportSemaphoreAOffsetUpper => (int)((SetReportSemaphoreA >> 0) & 0xFF); + public int SetReportSemaphoreAOffsetUpper => (int)(SetReportSemaphoreA & 0xFF); public uint SetReportSemaphoreB; public uint SetReportSemaphoreC; public uint SetReportSemaphoreD; - public SetReportSemaphoreDOperation SetReportSemaphoreDOperation => (SetReportSemaphoreDOperation)((SetReportSemaphoreD >> 0) & 0x3); + public SetReportSemaphoreDOperation SetReportSemaphoreDOperation => (SetReportSemaphoreDOperation)(SetReportSemaphoreD & 0x3); public bool SetReportSemaphoreDAwakenEnable => (SetReportSemaphoreD & 0x100000) != 0; public SetReportSemaphoreDStructureSize SetReportSemaphoreDStructureSize => (SetReportSemaphoreDStructureSize)((SetReportSemaphoreD >> 28) & 0x1); public bool SetReportSemaphoreDFlushDisable => (SetReportSemaphoreD & 0x4) != 0; @@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public SetReportSemaphoreDReductionFormat SetReportSemaphoreDReductionFormat => (SetReportSemaphoreDReductionFormat)((SetReportSemaphoreD >> 17) & 0x3); public fixed uint Reserved1B10[702]; public uint SetBindlessTexture; - public int SetBindlessTextureConstantBufferSlotSelect => (int)((SetBindlessTexture >> 0) & 0x7); + public int SetBindlessTextureConstantBufferSlotSelect => (int)(SetBindlessTexture & 0x7); public uint SetTrapHandler; public fixed uint Reserved2610[843]; public Array8<uint> SetShaderPerformanceCounterValueUpper; @@ -423,11 +423,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public bool SetShaderPerformanceCounterControlBWindowed(int i) => (SetShaderPerformanceCounterControlB[i] & 0x8) != 0; public int SetShaderPerformanceCounterControlBFunc(int i) => (int)((SetShaderPerformanceCounterControlB[i] >> 4) & 0xFFFF); public uint SetShaderPerformanceCounterTrapControl; - public int SetShaderPerformanceCounterTrapControlMask => (int)((SetShaderPerformanceCounterTrapControl >> 0) & 0xFF); + public int SetShaderPerformanceCounterTrapControlMask => (int)(SetShaderPerformanceCounterTrapControl & 0xFF); public uint StartShaderPerformanceCounter; - public int StartShaderPerformanceCounterCounterMask => (int)((StartShaderPerformanceCounter >> 0) & 0xFF); + public int StartShaderPerformanceCounterCounterMask => (int)(StartShaderPerformanceCounter & 0xFF); public uint StopShaderPerformanceCounter; - public int StopShaderPerformanceCounterCounterMask => (int)((StopShaderPerformanceCounter >> 0) & 0xFF); + public int StopShaderPerformanceCounterCounterMask => (int)(StopShaderPerformanceCounter & 0xFF); public fixed uint Reserved33E8[6]; public MmeShadowScratch SetMmeShadowScratch; #pragma warning restore CS0649 diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs index 7de4d5f08..6f3b91f2c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs @@ -186,22 +186,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public uint PmTrigger; public fixed uint Reserved144[63]; public uint SetSemaphoreA; - public int SetSemaphoreAUpper => (int)((SetSemaphoreA >> 0) & 0xFF); + public int SetSemaphoreAUpper => (int)(SetSemaphoreA & 0xFF); public uint SetSemaphoreB; public uint SetSemaphorePayload; public fixed uint Reserved24C[2]; public uint SetRenderEnableA; - public int SetRenderEnableAUpper => (int)((SetRenderEnableA >> 0) & 0xFF); + public int SetRenderEnableAUpper => (int)(SetRenderEnableA & 0xFF); public uint SetRenderEnableB; public uint SetRenderEnableC; - public int SetRenderEnableCMode => (int)((SetRenderEnableC >> 0) & 0x7); + public int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); public uint SetSrcPhysMode; - public SetPhysModeTarget SetSrcPhysModeTarget => (SetPhysModeTarget)((SetSrcPhysMode >> 0) & 0x3); + public SetPhysModeTarget SetSrcPhysModeTarget => (SetPhysModeTarget)(SetSrcPhysMode & 0x3); public uint SetDstPhysMode; - public SetPhysModeTarget SetDstPhysModeTarget => (SetPhysModeTarget)((SetDstPhysMode >> 0) & 0x3); + public SetPhysModeTarget SetDstPhysModeTarget => (SetPhysModeTarget)(SetDstPhysMode & 0x3); public fixed uint Reserved268[38]; public uint LaunchDma; - public LaunchDmaDataTransferType LaunchDmaDataTransferType => (LaunchDmaDataTransferType)((LaunchDma >> 0) & 0x3); + public LaunchDmaDataTransferType LaunchDmaDataTransferType => (LaunchDmaDataTransferType)(LaunchDma & 0x3); public bool LaunchDmaFlushEnable => (LaunchDma & 0x4) != 0; public LaunchDmaSemaphoreType LaunchDmaSemaphoreType => (LaunchDmaSemaphoreType)((LaunchDma >> 3) & 0x3); public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 5) & 0x3); @@ -218,10 +218,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public LaunchDmaBypassL2 LaunchDmaBypassL2 => (LaunchDmaBypassL2)((LaunchDma >> 20) & 0x1); public fixed uint Reserved304[63]; public uint OffsetInUpper; - public int OffsetInUpperUpper => (int)((OffsetInUpper >> 0) & 0xFF); + public int OffsetInUpperUpper => (int)(OffsetInUpper & 0xFF); public uint OffsetInLower; public uint OffsetOutUpper; - public int OffsetOutUpperUpper => (int)((OffsetOutUpper >> 0) & 0xFF); + public int OffsetOutUpperUpper => (int)(OffsetOutUpper & 0xFF); public uint OffsetOutLower; public uint PitchIn; public uint PitchOut; @@ -231,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public uint SetRemapConstA; public uint SetRemapConstB; public uint SetRemapComponents; - public SetRemapComponentsDst SetRemapComponentsDstX => (SetRemapComponentsDst)((SetRemapComponents >> 0) & 0x7); + public SetRemapComponentsDst SetRemapComponentsDstX => (SetRemapComponentsDst)(SetRemapComponents & 0x7); public SetRemapComponentsDst SetRemapComponentsDstY => (SetRemapComponentsDst)((SetRemapComponents >> 4) & 0x7); public SetRemapComponentsDst SetRemapComponentsDstZ => (SetRemapComponentsDst)((SetRemapComponents >> 8) & 0x7); public SetRemapComponentsDst SetRemapComponentsDstW => (SetRemapComponentsDst)((SetRemapComponents >> 12) & 0x7); @@ -239,7 +239,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public SetRemapComponentsNumComponents SetRemapComponentsNumSrcComponents => (SetRemapComponentsNumComponents)((SetRemapComponents >> 20) & 0x3); public SetRemapComponentsNumComponents SetRemapComponentsNumDstComponents => (SetRemapComponentsNumComponents)((SetRemapComponents >> 24) & 0x3); public uint SetDstBlockSize; - public SetBlockSizeWidth SetDstBlockSizeWidth => (SetBlockSizeWidth)((SetDstBlockSize >> 0) & 0xF); + public SetBlockSizeWidth SetDstBlockSizeWidth => (SetBlockSizeWidth)(SetDstBlockSize & 0xF); public SetBlockSizeHeight SetDstBlockSizeHeight => (SetBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); public SetBlockSizeDepth SetDstBlockSizeDepth => (SetBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); public SetBlockSizeGobHeight SetDstBlockSizeGobHeight => (SetBlockSizeGobHeight)((SetDstBlockSize >> 12) & 0xF); @@ -248,11 +248,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOrigin; - public int SetDstOriginX => (int)((SetDstOrigin >> 0) & 0xFFFF); + public int SetDstOriginX => (int)(SetDstOrigin & 0xFFFF); public int SetDstOriginY => (int)((SetDstOrigin >> 16) & 0xFFFF); public uint Reserved724; public uint SetSrcBlockSize; - public SetBlockSizeWidth SetSrcBlockSizeWidth => (SetBlockSizeWidth)((SetSrcBlockSize >> 0) & 0xF); + public SetBlockSizeWidth SetSrcBlockSizeWidth => (SetBlockSizeWidth)(SetSrcBlockSize & 0xF); public SetBlockSizeHeight SetSrcBlockSizeHeight => (SetBlockSizeHeight)((SetSrcBlockSize >> 4) & 0xF); public SetBlockSizeDepth SetSrcBlockSizeDepth => (SetBlockSizeDepth)((SetSrcBlockSize >> 8) & 0xF); public SetBlockSizeGobHeight SetSrcBlockSizeGobHeight => (SetBlockSizeGobHeight)((SetSrcBlockSize >> 12) & 0xF); @@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public uint SetSrcDepth; public uint SetSrcLayer; public uint SetSrcOrigin; - public int SetSrcOriginX => (int)((SetSrcOrigin >> 0) & 0xFFFF); + public int SetSrcOriginX => (int)(SetSrcOrigin & 0xFFFF); public int SetSrcOriginY => (int)((SetSrcOrigin >> 16) & 0xFFFF); public fixed uint Reserved740[629]; public uint PmTriggerEnd; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs index 458dc8f6f..d082ee9dd 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo public uint Method; #pragma warning restore CS0649 public int MethodAddressOld => (int)((Method >> 2) & 0x7FF); - public int MethodAddress => (int)((Method >> 0) & 0xFFF); + public int MethodAddress => (int)(Method & 0xFFF); public int SubdeviceMask => (int)((Method >> 4) & 0xFFF); public int MethodSubchannel => (int)((Method >> 13) & 0x7); public TertOp TertOp => (TertOp)((Method >> 16) & 0x3); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs index b1b236e77..31ba3217d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs @@ -39,17 +39,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo #pragma warning disable CS0649 public uint Entry0; #pragma warning restore CS0649 - public Entry0Fetch Entry0Fetch => (Entry0Fetch)((Entry0 >> 0) & 0x1); + public Entry0Fetch Entry0Fetch => (Entry0Fetch)(Entry0 & 0x1); public int Entry0Get => (int)((Entry0 >> 2) & 0x3FFFFFFF); public int Entry0Operand => (int)(Entry0); #pragma warning disable CS0649 public uint Entry1; #pragma warning restore CS0649 - public int Entry1GetHi => (int)((Entry1 >> 0) & 0xFF); + public int Entry1GetHi => (int)(Entry1 & 0xFF); public Entry1Priv Entry1Priv => (Entry1Priv)((Entry1 >> 8) & 0x1); public Entry1Level Entry1Level => (Entry1Level)((Entry1 >> 9) & 0x1); public int Entry1Length => (int)((Entry1 >> 10) & 0x1FFFFF); public Entry1Sync Entry1Sync => (Entry1Sync)((Entry1 >> 31) & 0x1); - public Entry1Opcode Entry1Opcode => (Entry1Opcode)((Entry1 >> 0) & 0xFF); + public Entry1Opcode Entry1Opcode => (Entry1Opcode)(Entry1 & 0xFF); } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs index 07d062eb6..ebfe15664 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs @@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo { #pragma warning disable CS0649 public uint SetObject; - public int SetObjectNvclass => (int)((SetObject >> 0) & 0xFFFF); + public int SetObjectNvclass => (int)(SetObject & 0xFFFF); public int SetObjectEngine => (int)((SetObject >> 16) & 0x1F); public uint Illegal; public int IllegalHandle => (int)(Illegal); @@ -161,13 +161,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo public int NopHandle => (int)(Nop); public uint Reserved0C; public uint Semaphorea; - public int SemaphoreaOffsetUpper => (int)((Semaphorea >> 0) & 0xFF); + public int SemaphoreaOffsetUpper => (int)(Semaphorea & 0xFF); public uint Semaphoreb; public int SemaphorebOffsetLower => (int)((Semaphoreb >> 2) & 0x3FFFFFFF); public uint Semaphorec; public int SemaphorecPayload => (int)(Semaphorec); public uint Semaphored; - public SemaphoredOperation SemaphoredOperation => (SemaphoredOperation)((Semaphored >> 0) & 0x1F); + public SemaphoredOperation SemaphoredOperation => (SemaphoredOperation)(Semaphored & 0x1F); public SemaphoredAcquireSwitch SemaphoredAcquireSwitch => (SemaphoredAcquireSwitch)((Semaphored >> 12) & 0x1); public SemaphoredReleaseWfi SemaphoredReleaseWfi => (SemaphoredReleaseWfi)((Semaphored >> 20) & 0x1); public SemaphoredReleaseSize SemaphoredReleaseSize => (SemaphoredReleaseSize)((Semaphored >> 24) & 0x1); @@ -181,14 +181,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo public uint Reserved2C; public uint MemOpC; public int MemOpCOperandLow => (int)((MemOpC >> 2) & 0x3FFFFFFF); - public MemOpCTlbInvalidatePdb MemOpCTlbInvalidatePdb => (MemOpCTlbInvalidatePdb)((MemOpC >> 0) & 0x1); + public MemOpCTlbInvalidatePdb MemOpCTlbInvalidatePdb => (MemOpCTlbInvalidatePdb)(MemOpC & 0x1); public MemOpCTlbInvalidateGpc MemOpCTlbInvalidateGpc => (MemOpCTlbInvalidateGpc)((MemOpC >> 1) & 0x1); public MemOpCTlbInvalidateTarget MemOpCTlbInvalidateTarget => (MemOpCTlbInvalidateTarget)((MemOpC >> 10) & 0x3); public int MemOpCTlbInvalidateAddrLo => (int)((MemOpC >> 12) & 0xFFFFF); public uint MemOpD; - public int MemOpDOperandHigh => (int)((MemOpD >> 0) & 0xFF); + public int MemOpDOperandHigh => (int)(MemOpD & 0xFF); public MemOpDOperation MemOpDOperation => (MemOpDOperation)((MemOpD >> 27) & 0x1F); - public int MemOpDTlbInvalidateAddrHi => (int)((MemOpD >> 0) & 0xFF); + public int MemOpDTlbInvalidateAddrHi => (int)(MemOpD & 0xFF); public uint Reserved38; public uint Reserved3C; public uint Reserved40; @@ -207,15 +207,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo public uint Syncpointa; public int SyncpointaPayload => (int)(Syncpointa); public uint Syncpointb; - public SyncpointbOperation SyncpointbOperation => (SyncpointbOperation)((Syncpointb >> 0) & 0x1); + public SyncpointbOperation SyncpointbOperation => (SyncpointbOperation)(Syncpointb & 0x1); public SyncpointbWaitSwitch SyncpointbWaitSwitch => (SyncpointbWaitSwitch)((Syncpointb >> 4) & 0x1); public int SyncpointbSyncptIndex => (int)((Syncpointb >> 8) & 0xFFF); public uint Wfi; - public WfiScope WfiScope => (WfiScope)((Wfi >> 0) & 0x1); + public WfiScope WfiScope => (WfiScope)(Wfi & 0x1); public uint CrcCheck; public int CrcCheckValue => (int)(CrcCheck); public uint Yield; - public YieldOp YieldOp => (YieldOp)((Yield >> 0) & 0x3); + public YieldOp YieldOp => (YieldOp)(Yield & 0x3); // TODO: Eventually move this to per-engine state. public Array31<uint> Reserved84; public uint NoOperation; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs index d0c82a5e4..f6d9602a6 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs @@ -113,22 +113,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory { #pragma warning disable CS0649 public uint SetObject; - public int SetObjectClassId => (int)((SetObject >> 0) & 0xFFFF); + public int SetObjectClassId => (int)(SetObject & 0xFFFF); public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)((SetNotifyA >> 0) & 0xFF); + public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); public uint SetNotifyB; public uint Notify; public NotifyType NotifyType => (NotifyType)(Notify); public uint WaitForIdle; public fixed uint Reserved114[7]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)((SetGlobalRenderEnableA >> 0) & 0xFF); + public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)((SetGlobalRenderEnableC >> 0) & 0x7); + public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public uint PmTriggerWfi; @@ -139,11 +139,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory public uint LineLengthIn; public uint LineCount; public uint OffsetOutUpper; - public int OffsetOutUpperValue => (int)((OffsetOutUpper >> 0) & 0xFF); + public int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); public uint OffsetOut; public uint PitchOut; public uint SetDstBlockSize; - public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)((SetDstBlockSize >> 0) & 0xF); + public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); public uint SetDstWidth; @@ -151,11 +151,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOriginBytesX; - public int SetDstOriginBytesXV => (int)((SetDstOriginBytesX >> 0) & 0xFFFFF); + public int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); public uint SetDstOriginSamplesY; - public int SetDstOriginSamplesYV => (int)((SetDstOriginSamplesY >> 0) & 0xFFFF); + public int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); public uint LaunchDma; - public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)((LaunchDma >> 0) & 0x1); + public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory public uint LoadInlineData; public fixed uint Reserved1B8[9]; public uint SetI2mSemaphoreA; - public int SetI2mSemaphoreAOffsetUpper => (int)((SetI2mSemaphoreA >> 0) & 0xFF); + public int SetI2mSemaphoreAOffsetUpper => (int)(SetI2mSemaphoreA & 0xFF); public uint SetI2mSemaphoreB; public uint SetI2mSemaphoreC; public fixed uint Reserved1E8[2]; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index 8f26f38ff..beda2dbfe 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -746,12 +746,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { #pragma warning disable CS0649 public uint SetObject; - public int SetObjectClassId => (int)((SetObject >> 0) & 0xFFFF); + public int SetObjectClassId => (int)(SetObject & 0xFFFF); public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)((SetNotifyA >> 0) & 0xFF); + public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); public uint SetNotifyB; public uint Notify; public NotifyType NotifyType => (NotifyType)(Notify); @@ -761,13 +761,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public uint LoadMmeStartAddressRamPointer; public uint LoadMmeStartAddressRam; public uint SetMmeShadowRamControl; - public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)((SetMmeShadowRamControl >> 0) & 0x3); + public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)(SetMmeShadowRamControl & 0x3); public fixed uint Reserved128[2]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)((SetGlobalRenderEnableA >> 0) & 0xFF); + public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)((SetGlobalRenderEnableC >> 0) & 0x7); + public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public uint PmTriggerWfi; @@ -778,11 +778,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public uint LineLengthIn; public uint LineCount; public uint OffsetOutUpper; - public int OffsetOutUpperValue => (int)((OffsetOutUpper >> 0) & 0xFF); + public int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); public uint OffsetOut; public uint PitchOut; public uint SetDstBlockSize; - public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)((SetDstBlockSize >> 0) & 0xF); + public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); public uint SetDstWidth; @@ -790,11 +790,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOriginBytesX; - public int SetDstOriginBytesXV => (int)((SetDstOriginBytesX >> 0) & 0xFFFFF); + public int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); public uint SetDstOriginSamplesY; - public int SetDstOriginSamplesYV => (int)((SetDstOriginSamplesY >> 0) & 0xFFFF); + public int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); public uint LaunchDma; - public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)((LaunchDma >> 0) & 0x1); + public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs index 46fddb04c..55e5019fc 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs @@ -499,12 +499,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod { #pragma warning disable CS0649 public uint SetObject; - public int SetObjectClassId => (int)((SetObject >> 0) & 0xFFFF); + public int SetObjectClassId => (int)(SetObject & 0xFFFF); public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)((SetNotifyA >> 0) & 0x1FFFFFF); + public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0x1FFFFFF); public uint SetNotifyB; public uint Notify; public NotifyType NotifyType => (NotifyType)(Notify); @@ -514,13 +514,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint LoadMmeStartAddressRamPointer; public uint LoadMmeStartAddressRam; public uint SetMmeShadowRamControl; - public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)((SetMmeShadowRamControl >> 0) & 0x3); + public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)(SetMmeShadowRamControl & 0x3); public fixed uint Reserved128[2]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)((SetGlobalRenderEnableA >> 0) & 0xFF); + public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)((SetGlobalRenderEnableC >> 0) & 0x7); + public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public fixed uint Reserved144[3]; @@ -533,9 +533,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public int SetMmeSwitchStateRestoreMacro => (int)((SetMmeSwitchState >> 12) & 0xFF); public fixed uint Reserved1F0[4]; public uint SetDstFormat; - public SetDstFormatV SetDstFormatV => (SetDstFormatV)((SetDstFormat >> 0) & 0xFF); + public SetDstFormatV SetDstFormatV => (SetDstFormatV)(SetDstFormat & 0xFF); public uint SetDstMemoryLayout; - public SetDstMemoryLayoutV SetDstMemoryLayoutV => (SetDstMemoryLayoutV)((SetDstMemoryLayout >> 0) & 0x1); + public SetDstMemoryLayoutV SetDstMemoryLayoutV => (SetDstMemoryLayoutV)(SetDstMemoryLayout & 0x1); public uint SetDstBlockSize; public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0x7); public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0x7); @@ -545,37 +545,37 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetDstWidth; public uint SetDstHeight; public uint SetDstOffsetUpper; - public int SetDstOffsetUpperV => (int)((SetDstOffsetUpper >> 0) & 0xFF); + public int SetDstOffsetUpperV => (int)(SetDstOffsetUpper & 0xFF); public uint SetDstOffsetLower; public uint FlushAndInvalidateRopMiniCache; public bool FlushAndInvalidateRopMiniCacheV => (FlushAndInvalidateRopMiniCache & 0x1) != 0; public uint SetSpareNoop06; public uint SetSrcFormat; - public SetSrcFormatV SetSrcFormatV => (SetSrcFormatV)((SetSrcFormat >> 0) & 0xFF); + public SetSrcFormatV SetSrcFormatV => (SetSrcFormatV)(SetSrcFormat & 0xFF); public uint SetSrcMemoryLayout; - public SetSrcMemoryLayoutV SetSrcMemoryLayoutV => (SetSrcMemoryLayoutV)((SetSrcMemoryLayout >> 0) & 0x1); + public SetSrcMemoryLayoutV SetSrcMemoryLayoutV => (SetSrcMemoryLayoutV)(SetSrcMemoryLayout & 0x1); public uint SetSrcBlockSize; public SetSrcBlockSizeHeight SetSrcBlockSizeHeight => (SetSrcBlockSizeHeight)((SetSrcBlockSize >> 4) & 0x7); public SetSrcBlockSizeDepth SetSrcBlockSizeDepth => (SetSrcBlockSizeDepth)((SetSrcBlockSize >> 8) & 0x7); public uint SetSrcDepth; public uint TwodInvalidateTextureDataCache; - public TwodInvalidateTextureDataCacheV TwodInvalidateTextureDataCacheV => (TwodInvalidateTextureDataCacheV)((TwodInvalidateTextureDataCache >> 0) & 0x3); + public TwodInvalidateTextureDataCacheV TwodInvalidateTextureDataCacheV => (TwodInvalidateTextureDataCacheV)(TwodInvalidateTextureDataCache & 0x3); public uint SetSrcPitch; public uint SetSrcWidth; public uint SetSrcHeight; public uint SetSrcOffsetUpper; - public int SetSrcOffsetUpperV => (int)((SetSrcOffsetUpper >> 0) & 0xFF); + public int SetSrcOffsetUpperV => (int)(SetSrcOffsetUpper & 0xFF); public uint SetSrcOffsetLower; public uint SetPixelsFromMemorySectorPromotion; - public SetPixelsFromMemorySectorPromotionV SetPixelsFromMemorySectorPromotionV => (SetPixelsFromMemorySectorPromotionV)((SetPixelsFromMemorySectorPromotion >> 0) & 0x3); + public SetPixelsFromMemorySectorPromotionV SetPixelsFromMemorySectorPromotionV => (SetPixelsFromMemorySectorPromotionV)(SetPixelsFromMemorySectorPromotion & 0x3); public uint SetSpareNoop12; public uint SetNumProcessingClusters; - public SetNumProcessingClustersV SetNumProcessingClustersV => (SetNumProcessingClustersV)((SetNumProcessingClusters >> 0) & 0x1); + public SetNumProcessingClustersV SetNumProcessingClustersV => (SetNumProcessingClustersV)(SetNumProcessingClusters & 0x1); public uint SetRenderEnableA; - public int SetRenderEnableAOffsetUpper => (int)((SetRenderEnableA >> 0) & 0xFF); + public int SetRenderEnableAOffsetUpper => (int)(SetRenderEnableA & 0xFF); public uint SetRenderEnableB; public uint SetRenderEnableC; - public int SetRenderEnableCMode => (int)((SetRenderEnableC >> 0) & 0x7); + public int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); public uint SetSpareNoop08; public uint SetSpareNoop01; public uint SetSpareNoop11; @@ -587,25 +587,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetClipEnable; public bool SetClipEnableV => (SetClipEnable & 0x1) != 0; public uint SetColorKeyFormat; - public SetColorKeyFormatV SetColorKeyFormatV => (SetColorKeyFormatV)((SetColorKeyFormat >> 0) & 0x7); + public SetColorKeyFormatV SetColorKeyFormatV => (SetColorKeyFormatV)(SetColorKeyFormat & 0x7); public uint SetColorKey; public uint SetColorKeyEnable; public bool SetColorKeyEnableV => (SetColorKeyEnable & 0x1) != 0; public uint SetRop; - public int SetRopV => (int)((SetRop >> 0) & 0xFF); + public int SetRopV => (int)(SetRop & 0xFF); public uint SetBeta1; public uint SetBeta4; - public int SetBeta4B => (int)((SetBeta4 >> 0) & 0xFF); + public int SetBeta4B => (int)(SetBeta4 & 0xFF); public int SetBeta4G => (int)((SetBeta4 >> 8) & 0xFF); public int SetBeta4R => (int)((SetBeta4 >> 16) & 0xFF); public int SetBeta4A => (int)((SetBeta4 >> 24) & 0xFF); public uint SetOperation; - public SetOperationV SetOperationV => (SetOperationV)((SetOperation >> 0) & 0x7); + public SetOperationV SetOperationV => (SetOperationV)(SetOperation & 0x7); public uint SetPatternOffset; - public int SetPatternOffsetX => (int)((SetPatternOffset >> 0) & 0x3F); + public int SetPatternOffsetX => (int)(SetPatternOffset & 0x3F); public int SetPatternOffsetY => (int)((SetPatternOffset >> 8) & 0x3F); public uint SetPatternSelect; - public SetPatternSelectV SetPatternSelectV => (SetPatternSelectV)((SetPatternSelect >> 0) & 0x3); + public SetPatternSelectV SetPatternSelectV => (SetPatternSelectV)(SetPatternSelect & 0x3); public uint SetDstColorRenderToZetaSurface; public bool SetDstColorRenderToZetaSurfaceV => (SetDstColorRenderToZetaSurface & 0x1) != 0; public uint SetSpareNoop04; @@ -618,15 +618,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public bool SetCompressionEnable => (SetCompression & 0x1) != 0; public uint SetSpareNoop09; public uint SetRenderEnableOverride; - public SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)((SetRenderEnableOverride >> 0) & 0x3); + public SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)(SetRenderEnableOverride & 0x3); public uint SetPixelsFromMemoryDirection; - public SetPixelsFromMemoryDirectionHorizontal SetPixelsFromMemoryDirectionHorizontal => (SetPixelsFromMemoryDirectionHorizontal)((SetPixelsFromMemoryDirection >> 0) & 0x3); + public SetPixelsFromMemoryDirectionHorizontal SetPixelsFromMemoryDirectionHorizontal => (SetPixelsFromMemoryDirectionHorizontal)(SetPixelsFromMemoryDirection & 0x3); public SetPixelsFromMemoryDirectionVertical SetPixelsFromMemoryDirectionVertical => (SetPixelsFromMemoryDirectionVertical)((SetPixelsFromMemoryDirection >> 4) & 0x3); public uint SetSpareNoop10; public uint SetMonochromePatternColorFormat; - public SetMonochromePatternColorFormatV SetMonochromePatternColorFormatV => (SetMonochromePatternColorFormatV)((SetMonochromePatternColorFormat >> 0) & 0x7); + public SetMonochromePatternColorFormatV SetMonochromePatternColorFormatV => (SetMonochromePatternColorFormatV)(SetMonochromePatternColorFormat & 0x7); public uint SetMonochromePatternFormat; - public SetMonochromePatternFormatV SetMonochromePatternFormatV => (SetMonochromePatternFormatV)((SetMonochromePatternFormat >> 0) & 0x1); + public SetMonochromePatternFormatV SetMonochromePatternFormatV => (SetMonochromePatternFormatV)(SetMonochromePatternFormat & 0x1); public uint SetMonochromePatternColor0; public uint SetMonochromePatternColor1; public uint SetMonochromePattern0; @@ -662,26 +662,26 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetRenderSolidPrimColor2; public uint SetRenderSolidPrimColor3; public uint SetMmeMemAddressA; - public int SetMmeMemAddressAUpper => (int)((SetMmeMemAddressA >> 0) & 0x1FFFFFF); + public int SetMmeMemAddressAUpper => (int)(SetMmeMemAddressA & 0x1FFFFFF); public uint SetMmeMemAddressB; public uint SetMmeDataRamAddress; public uint MmeDmaRead; public uint MmeDmaReadFifoed; public uint MmeDmaWrite; public uint MmeDmaReduction; - public MmeDmaReductionReductionOp MmeDmaReductionReductionOp => (MmeDmaReductionReductionOp)((MmeDmaReduction >> 0) & 0x7); + public MmeDmaReductionReductionOp MmeDmaReductionReductionOp => (MmeDmaReductionReductionOp)(MmeDmaReduction & 0x7); public MmeDmaReductionReductionFormat MmeDmaReductionReductionFormat => (MmeDmaReductionReductionFormat)((MmeDmaReduction >> 4) & 0x3); public MmeDmaReductionReductionSize MmeDmaReductionReductionSize => (MmeDmaReductionReductionSize)((MmeDmaReduction >> 8) & 0x1); public uint MmeDmaSysmembar; public bool MmeDmaSysmembarV => (MmeDmaSysmembar & 0x1) != 0; public uint MmeDmaSync; public uint SetMmeDataFifoConfig; - public SetMmeDataFifoConfigFifoSize SetMmeDataFifoConfigFifoSize => (SetMmeDataFifoConfigFifoSize)((SetMmeDataFifoConfig >> 0) & 0x7); + public SetMmeDataFifoConfigFifoSize SetMmeDataFifoConfigFifoSize => (SetMmeDataFifoConfigFifoSize)(SetMmeDataFifoConfig & 0x7); public fixed uint Reserved578[2]; public uint RenderSolidPrimMode; - public RenderSolidPrimModeV RenderSolidPrimModeV => (RenderSolidPrimModeV)((RenderSolidPrimMode >> 0) & 0x7); + public RenderSolidPrimModeV RenderSolidPrimModeV => (RenderSolidPrimModeV)(RenderSolidPrimMode & 0x7); public uint SetRenderSolidPrimColorFormat; - public SetRenderSolidPrimColorFormatV SetRenderSolidPrimColorFormatV => (SetRenderSolidPrimColorFormatV)((SetRenderSolidPrimColorFormat >> 0) & 0xFF); + public SetRenderSolidPrimColorFormatV SetRenderSolidPrimColorFormatV => (SetRenderSolidPrimColorFormatV)(SetRenderSolidPrimColorFormat & 0xFF); public uint SetRenderSolidPrimColor; public uint SetRenderSolidLineTieBreakBits; public bool SetRenderSolidLineTieBreakBitsXmajXincYinc => (SetRenderSolidLineTieBreakBits & 0x1) != 0; @@ -690,24 +690,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public bool SetRenderSolidLineTieBreakBitsYmajXdecYinc => (SetRenderSolidLineTieBreakBits & 0x1000) != 0; public fixed uint Reserved590[20]; public uint RenderSolidPrimPointXY; - public int RenderSolidPrimPointXYX => (int)((RenderSolidPrimPointXY >> 0) & 0xFFFF); + public int RenderSolidPrimPointXYX => (int)(RenderSolidPrimPointXY & 0xFFFF); public int RenderSolidPrimPointXYY => (int)((RenderSolidPrimPointXY >> 16) & 0xFFFF); public fixed uint Reserved5E4[7]; public Array64<RenderSolidPrimPoint> RenderSolidPrimPoint; public uint SetPixelsFromCpuDataType; - public SetPixelsFromCpuDataTypeV SetPixelsFromCpuDataTypeV => (SetPixelsFromCpuDataTypeV)((SetPixelsFromCpuDataType >> 0) & 0x1); + public SetPixelsFromCpuDataTypeV SetPixelsFromCpuDataTypeV => (SetPixelsFromCpuDataTypeV)(SetPixelsFromCpuDataType & 0x1); public uint SetPixelsFromCpuColorFormat; - public SetPixelsFromCpuColorFormatV SetPixelsFromCpuColorFormatV => (SetPixelsFromCpuColorFormatV)((SetPixelsFromCpuColorFormat >> 0) & 0xFF); + public SetPixelsFromCpuColorFormatV SetPixelsFromCpuColorFormatV => (SetPixelsFromCpuColorFormatV)(SetPixelsFromCpuColorFormat & 0xFF); public uint SetPixelsFromCpuIndexFormat; - public SetPixelsFromCpuIndexFormatV SetPixelsFromCpuIndexFormatV => (SetPixelsFromCpuIndexFormatV)((SetPixelsFromCpuIndexFormat >> 0) & 0x3); + public SetPixelsFromCpuIndexFormatV SetPixelsFromCpuIndexFormatV => (SetPixelsFromCpuIndexFormatV)(SetPixelsFromCpuIndexFormat & 0x3); public uint SetPixelsFromCpuMonoFormat; - public SetPixelsFromCpuMonoFormatV SetPixelsFromCpuMonoFormatV => (SetPixelsFromCpuMonoFormatV)((SetPixelsFromCpuMonoFormat >> 0) & 0x1); + public SetPixelsFromCpuMonoFormatV SetPixelsFromCpuMonoFormatV => (SetPixelsFromCpuMonoFormatV)(SetPixelsFromCpuMonoFormat & 0x1); public uint SetPixelsFromCpuWrap; - public SetPixelsFromCpuWrapV SetPixelsFromCpuWrapV => (SetPixelsFromCpuWrapV)((SetPixelsFromCpuWrap >> 0) & 0x3); + public SetPixelsFromCpuWrapV SetPixelsFromCpuWrapV => (SetPixelsFromCpuWrapV)(SetPixelsFromCpuWrap & 0x3); public uint SetPixelsFromCpuColor0; public uint SetPixelsFromCpuColor1; public uint SetPixelsFromCpuMonoOpacity; - public SetPixelsFromCpuMonoOpacityV SetPixelsFromCpuMonoOpacityV => (SetPixelsFromCpuMonoOpacityV)((SetPixelsFromCpuMonoOpacity >> 0) & 0x1); + public SetPixelsFromCpuMonoOpacityV SetPixelsFromCpuMonoOpacityV => (SetPixelsFromCpuMonoOpacityV)(SetPixelsFromCpuMonoOpacity & 0x1); public fixed uint Reserved820[6]; public uint SetPixelsFromCpuSrcWidth; public uint SetPixelsFromCpuSrcHeight; @@ -753,13 +753,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public bool SetBigEndianControlOverride => (SetBigEndianControl & 0x10000000) != 0; public fixed uint Reserved874[3]; public uint SetPixelsFromMemoryBlockShape; - public SetPixelsFromMemoryBlockShapeV SetPixelsFromMemoryBlockShapeV => (SetPixelsFromMemoryBlockShapeV)((SetPixelsFromMemoryBlockShape >> 0) & 0x7); + public SetPixelsFromMemoryBlockShapeV SetPixelsFromMemoryBlockShapeV => (SetPixelsFromMemoryBlockShapeV)(SetPixelsFromMemoryBlockShape & 0x7); public uint SetPixelsFromMemoryCorralSize; - public int SetPixelsFromMemoryCorralSizeV => (int)((SetPixelsFromMemoryCorralSize >> 0) & 0x3FF); + public int SetPixelsFromMemoryCorralSizeV => (int)(SetPixelsFromMemoryCorralSize & 0x3FF); public uint SetPixelsFromMemorySafeOverlap; public bool SetPixelsFromMemorySafeOverlapV => (SetPixelsFromMemorySafeOverlap & 0x1) != 0; public uint SetPixelsFromMemorySampleMode; - public SetPixelsFromMemorySampleModeOrigin SetPixelsFromMemorySampleModeOrigin => (SetPixelsFromMemorySampleModeOrigin)((SetPixelsFromMemorySampleMode >> 0) & 0x1); + public SetPixelsFromMemorySampleModeOrigin SetPixelsFromMemorySampleModeOrigin => (SetPixelsFromMemorySampleModeOrigin)(SetPixelsFromMemorySampleMode & 0x1); public SetPixelsFromMemorySampleModeFilter SetPixelsFromMemorySampleModeFilter => (SetPixelsFromMemorySampleModeFilter)((SetPixelsFromMemorySampleMode >> 4) & 0x1); public fixed uint Reserved890[8]; public uint SetPixelsFromMemoryDstX0; diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs index 0c22ddc05..136762938 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs @@ -851,14 +851,14 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstConditional(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); } struct InstAl2p { private ulong _opcode; public InstAl2p(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -872,7 +872,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstAld(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -889,7 +889,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstAst(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); - public int SrcB => (int)((_opcode >> 0) & 0xFF); + public int SrcB => (int)(_opcode & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -903,7 +903,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstAtom(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -918,7 +918,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstAtomCas(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -931,7 +931,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstAtoms(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -945,7 +945,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstAtomsCas(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -957,7 +957,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstB2r(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int DestPred => (int)((_opcode >> 45) & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -985,7 +985,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstBfeR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -999,7 +999,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstBfeI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1013,7 +1013,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstBfeC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1028,7 +1028,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstBfiR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1041,7 +1041,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstBfiI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1054,7 +1054,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstBfiC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1068,7 +1068,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstBfiRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -1092,7 +1092,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstBra(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Ca => (_opcode & 0x20) != 0; public bool Lmt => (_opcode & 0x40) != 0; @@ -1105,7 +1105,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstBrk(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); } struct InstBrx @@ -1115,7 +1115,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Ca => (_opcode & 0x20) != 0; public bool Lmt => (_opcode & 0x40) != 0; @@ -1140,7 +1140,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm30 => (int)((_opcode >> 22) & 0x3FFFFFFF); public bool E => (_opcode & 0x10000000000000) != 0; public CacheType Cache => (CacheType)((_opcode >> 4) & 0x7); - public CctlOp CctlOp => (CctlOp)((_opcode >> 0) & 0xF); + public CctlOp CctlOp => (CctlOp)(_opcode & 0xF); } struct InstCctll @@ -1152,7 +1152,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool PredInv => (_opcode & 0x80000) != 0; public int Imm22 => (int)((_opcode >> 22) & 0x3FFFFF); public int Cache => (int)((_opcode >> 4) & 0x3); - public CctlOp CctlOp => (CctlOp)((_opcode >> 0) & 0xF); + public CctlOp CctlOp => (CctlOp)(_opcode & 0xF); } struct InstCctlt @@ -1162,7 +1162,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public int TsIdx13 => (int)((_opcode >> 36) & 0x1FFF); - public CctltOp CctltOp => (CctltOp)((_opcode >> 0) & 0x3); + public CctltOp CctltOp => (CctltOp)(_opcode & 0x3); } struct InstCctltR @@ -1170,7 +1170,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstCctltR(ulong opcode) => _opcode = opcode; public int SrcC => (int)((_opcode >> 39) & 0xFF); - public CctltOp CctltOp => (CctltOp)((_opcode >> 0) & 0x3); + public CctltOp CctltOp => (CctltOp)(_opcode & 0x3); } struct InstCont @@ -1179,14 +1179,14 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstCont(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); } struct InstCset { private ulong _opcode; public InstCset(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public bool WriteCC => (_opcode & 0x800000000000) != 0; @@ -1206,7 +1206,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool WriteCC => (_opcode & 0x800000000000) != 0; public Ccc Ccc => (Ccc)((_opcode >> 8) & 0x1F); public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int SrcPred => (int)((_opcode >> 39) & 0x7); public bool SrcPredInv => (_opcode & 0x40000000000) != 0; public BoolOp Bop => (BoolOp)((_opcode >> 45) & 0x3); @@ -1216,7 +1216,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstCs2r(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public SReg SReg => (SReg)((_opcode >> 20) & 0xFF); @@ -1226,7 +1226,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDaddR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1243,7 +1243,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDaddI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1260,7 +1260,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDaddC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1283,14 +1283,14 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Le => (_opcode & 0x20000000) != 0; public int Sbid => (int)((_opcode >> 26) & 0x7); public int PendCnt => (int)((_opcode >> 20) & 0x3F); - public int Imm6 => (int)((_opcode >> 0) & 0x3F); + public int Imm6 => (int)(_opcode & 0x3F); } struct InstDfmaR { private ulong _opcode; public InstDfmaR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1306,7 +1306,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDfmaI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1322,7 +1322,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDfmaC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1339,7 +1339,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDfmaRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -1356,7 +1356,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDmnmxR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1374,7 +1374,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDmnmxI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1392,7 +1392,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDmnmxC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1411,7 +1411,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDmulR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1425,7 +1425,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDmulI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1439,7 +1439,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDmulC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1454,7 +1454,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDsetR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1475,7 +1475,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDsetI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1496,7 +1496,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstDsetC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1531,7 +1531,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool AbsA => (_opcode & 0x80) != 0; public bool NegB => (_opcode & 0x40) != 0; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); } struct InstDsetpI @@ -1551,7 +1551,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool AbsA => (_opcode & 0x80) != 0; public bool NegB => (_opcode & 0x40) != 0; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); } struct InstDsetpC @@ -1572,7 +1572,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool AbsA => (_opcode & 0x80) != 0; public bool NegB => (_opcode & 0x40) != 0; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); } struct InstExit @@ -1581,7 +1581,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstExit(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); public bool KeepRefCnt => (_opcode & 0x20) != 0; } @@ -1589,7 +1589,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstF2fR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1608,7 +1608,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstF2fI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1627,7 +1627,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstF2fC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1647,7 +1647,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstF2iR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1665,7 +1665,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstF2iI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1683,7 +1683,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstF2iC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1702,7 +1702,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFaddR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1721,7 +1721,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFaddI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1740,7 +1740,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFaddC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1760,7 +1760,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFadd32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1826,7 +1826,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFcmpR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1840,7 +1840,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFcmpI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1854,7 +1854,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFcmpC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1869,7 +1869,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFcmpRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -1884,7 +1884,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFfmaR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1902,7 +1902,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFfmaI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -1920,7 +1920,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFfmaC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -1939,7 +1939,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFfmaRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -1958,7 +1958,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFfma32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm32 => (int)(_opcode >> 20); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1974,7 +1974,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFloR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1988,7 +1988,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFloI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2002,7 +2002,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFloC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2017,7 +2017,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFmnmxR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2036,7 +2036,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFmnmxI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2055,7 +2055,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFmnmxC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2075,7 +2075,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFmulR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2092,7 +2092,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFmulI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2109,7 +2109,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFmulC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2127,7 +2127,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFmul32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2141,7 +2141,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFsetR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2163,7 +2163,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFsetC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2186,7 +2186,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFsetI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2209,7 +2209,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstFsetpR(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2231,7 +2231,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstFsetpI(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2253,7 +2253,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstFsetpC(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2275,7 +2275,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstFswzadd(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2291,21 +2291,21 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstGetcrsptr(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); } struct InstGetlmembase { private ulong _opcode; public InstGetlmembase(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); } struct InstHadd2R { private ulong _opcode; public InstHadd2R(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public OFmt OFmt => (OFmt)((_opcode >> 49) & 0x3); @@ -2325,7 +2325,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHadd2I(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int BimmH0 => (int)((_opcode >> 20) & 0x3FF); public int BimmH1 => (int)((_opcode >> 47) & 0x200) | (int)((_opcode >> 30) & 0x1FF); @@ -2343,7 +2343,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHadd2C(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2363,7 +2363,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHadd232i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm => (int)(_opcode >> 20); public HalfSwizzle ASwizzle => (HalfSwizzle)((_opcode >> 53) & 0x3); @@ -2378,7 +2378,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHfma2R(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -2398,7 +2398,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHfma2I(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int BimmH0 => (int)((_opcode >> 20) & 0x3FF); public int BimmH1 => (int)((_opcode >> 47) & 0x200) | (int)((_opcode >> 30) & 0x1FF); @@ -2417,7 +2417,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHfma2C(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2437,7 +2437,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHfma2Rc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2457,7 +2457,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHfma232i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm => (int)(_opcode >> 20); public HalfSwizzle ASwizzle => (HalfSwizzle)((_opcode >> 47) & 0x3); @@ -2471,7 +2471,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHmul2R(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public OFmt OFmt => (OFmt)((_opcode >> 49) & 0x3); @@ -2490,7 +2490,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHmul2I(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int BimmH0 => (int)((_opcode >> 20) & 0x3FF); public int BimmH1 => (int)((_opcode >> 47) & 0x200) | (int)((_opcode >> 30) & 0x1FF); @@ -2508,7 +2508,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHmul2C(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2527,7 +2527,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHmul232i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm32 => (int)(_opcode >> 20); public HalfSwizzle ASwizzle => (HalfSwizzle)((_opcode >> 53) & 0x3); @@ -2541,7 +2541,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHset2R(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public HalfSwizzle ASwizzle => (HalfSwizzle)((_opcode >> 47) & 0x3); @@ -2564,7 +2564,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHset2I(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int BimmH0 => (int)((_opcode >> 20) & 0x3FF); public int BimmH1 => (int)((_opcode >> 47) & 0x200) | (int)((_opcode >> 30) & 0x1FF); @@ -2585,7 +2585,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstHset2C(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2608,7 +2608,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstHsetp2R(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2632,7 +2632,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstHsetp2I(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2654,7 +2654,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstHsetp2C(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2677,7 +2677,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstI2fR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2694,7 +2694,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstI2fI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2711,7 +2711,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstI2fC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2729,7 +2729,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstI2iR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2746,7 +2746,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstI2iI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2763,7 +2763,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstI2iC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2781,7 +2781,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIaddR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2796,7 +2796,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIaddI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -2811,7 +2811,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIaddC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2827,7 +2827,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIadd32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -2842,7 +2842,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIadd3R(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -2863,7 +2863,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIadd3I(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -2880,7 +2880,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIadd3C(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2898,7 +2898,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIcmpR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -2912,7 +2912,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIcmpI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -2926,7 +2926,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIcmpC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2941,7 +2941,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIcmpRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -2964,7 +2964,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIdpR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -2980,7 +2980,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIdpC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -2997,7 +2997,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3016,7 +3016,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3035,7 +3035,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3055,7 +3055,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -3075,7 +3075,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImad32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3091,7 +3091,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadspR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3106,7 +3106,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadspI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3121,7 +3121,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadspC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3137,7 +3137,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImadspRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -3153,7 +3153,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImnmxR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3169,7 +3169,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImnmxI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3185,7 +3185,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImnmxC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3202,7 +3202,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImulR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3217,7 +3217,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImulI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3232,7 +3232,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImulC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3248,7 +3248,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstImul32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3263,7 +3263,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIpa(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3282,7 +3282,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIsberd(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3296,7 +3296,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIscaddR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3310,7 +3310,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIscaddI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3324,7 +3324,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIscaddC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3339,7 +3339,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIscadd32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3352,7 +3352,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIsetR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3371,7 +3371,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIsetI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3390,7 +3390,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstIsetC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3421,7 +3421,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcPred => (int)((_opcode >> 39) & 0x7); public bool SrcPredInv => (_opcode & 0x40000000000) != 0; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); } struct InstIsetpI @@ -3439,7 +3439,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcPred => (int)((_opcode >> 39) & 0x7); public bool SrcPredInv => (_opcode & 0x40000000000) != 0; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); } struct InstIsetpC @@ -3458,7 +3458,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcPred => (int)((_opcode >> 39) & 0x7); public bool SrcPredInv => (_opcode & 0x40000000000) != 0; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); } struct InstJcal @@ -3477,7 +3477,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); public bool Ca => (_opcode & 0x20) != 0; public int Imm32 => (int)(_opcode >> 20); public bool Lmt => (_opcode & 0x40) != 0; @@ -3491,7 +3491,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); public bool Ca => (_opcode & 0x20) != 0; public int Imm32 => (int)(_opcode >> 20); public bool Lmt => (_opcode & 0x40) != 0; @@ -3503,14 +3503,14 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstKil(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); } struct InstLd { private ulong _opcode; public InstLd(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3525,7 +3525,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLdc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3539,7 +3539,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLdg(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3553,7 +3553,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLdl(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3566,7 +3566,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLds(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3579,7 +3579,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLeaR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3595,7 +3595,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLeaI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3611,7 +3611,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLeaC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3628,7 +3628,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLeaHiR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3645,7 +3645,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLeaHiC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3669,14 +3669,14 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLongjmp(ulong opcode) => _opcode = opcode; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); } struct InstLopR { private ulong _opcode; public InstLopR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3694,7 +3694,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLopI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3712,7 +3712,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLopC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3731,7 +3731,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLop3R(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3748,7 +3748,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLop3I(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -3763,7 +3763,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLop3C(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3779,7 +3779,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstLop32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3798,14 +3798,14 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public Membar Membar => (Membar)((_opcode >> 8) & 0x3); - public Ivall Ivall => (Ivall)((_opcode >> 0) & 0x3); + public Ivall Ivall => (Ivall)(_opcode & 0x3); } struct InstMovR { private ulong _opcode; public InstMovR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3816,7 +3816,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstMovI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3827,7 +3827,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstMovC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3839,7 +3839,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstMov32i(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm32 => (int)(_opcode >> 20); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3850,7 +3850,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstMufu(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3875,7 +3875,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstOutR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3887,7 +3887,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstOutI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3899,7 +3899,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstOutC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3912,7 +3912,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstP2rR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3925,7 +3925,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstP2rI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3938,7 +3938,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstP2rC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -3975,7 +3975,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPixld(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3996,7 +3996,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPopcR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4007,7 +4007,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPopcI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4018,7 +4018,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPopcC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4039,7 +4039,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPrmtR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4052,7 +4052,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPrmtI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4065,7 +4065,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPrmtC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -4079,7 +4079,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPrmtRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -4093,7 +4093,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstPset(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public bool WriteCC => (_opcode & 0x800000000000) != 0; @@ -4115,7 +4115,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 3) & 0x7); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public int Src2Pred => (int)((_opcode >> 12) & 0x7); public bool Src2PredInv => (_opcode & 0x8000) != 0; public int Src1Pred => (int)((_opcode >> 29) & 0x7); @@ -4185,7 +4185,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private ulong _opcode; public InstRed(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); - public int SrcB => (int)((_opcode >> 0) & 0xFF); + public int SrcB => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public int Imm20 => (int)((_opcode >> 28) & 0xFFFFF); @@ -4200,14 +4200,14 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstRet(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); } struct InstRroR { private ulong _opcode; public InstRroR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4220,7 +4220,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstRroI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4233,7 +4233,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstRroC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4253,7 +4253,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstS2r(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public SReg SReg => (SReg)((_opcode >> 20) & 0xFF); @@ -4269,7 +4269,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSelR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4282,7 +4282,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSelI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4295,7 +4295,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSelC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -4323,7 +4323,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShfLR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4339,7 +4339,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShfRR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4355,7 +4355,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShfLI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4371,7 +4371,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShfRI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4387,7 +4387,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShfl(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4405,7 +4405,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShlR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4419,7 +4419,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShlI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4433,7 +4433,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShlC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -4448,7 +4448,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShrR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4464,7 +4464,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShrI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4480,7 +4480,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstShrC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -4505,7 +4505,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSt(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4520,7 +4520,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstStg(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4534,7 +4534,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstStl(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4555,7 +4555,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSts(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4567,7 +4567,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuatomB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4584,7 +4584,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuatom(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4601,7 +4601,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuatomB2(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4619,7 +4619,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuatomCasB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -4636,7 +4636,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuatomCas(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4653,7 +4653,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuldDB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4670,7 +4670,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuldD(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4687,7 +4687,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuldB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4703,7 +4703,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuld(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4719,7 +4719,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSuredB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4735,7 +4735,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSured(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4751,7 +4751,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSustDB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4767,7 +4767,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSustD(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4783,7 +4783,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSustB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4798,7 +4798,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstSust(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -4815,14 +4815,14 @@ namespace Ryujinx.Graphics.Shader.Decoders public InstSync(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; - public Ccc Ccc => (Ccc)((_opcode >> 0) & 0x1F); + public Ccc Ccc => (Ccc)(_opcode & 0x1F); } struct InstTex { private ulong _opcode; public InstTex(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4843,7 +4843,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTexB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4863,7 +4863,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTexs(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4879,7 +4879,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTld(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4899,7 +4899,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTldB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4918,7 +4918,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTlds(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4934,7 +4934,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTld4(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4955,7 +4955,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTld4B(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4975,7 +4975,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTld4s(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4992,7 +4992,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTmml(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -5008,7 +5008,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTmmlB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -5023,7 +5023,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTxa(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -5037,7 +5037,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTxd(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -5055,7 +5055,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTxdB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -5072,7 +5072,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTxq(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -5086,7 +5086,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstTxqB(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -5099,7 +5099,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVabsdiff(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5118,7 +5118,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVabsdiff4(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5139,7 +5139,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVadd(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5160,7 +5160,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVmad(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5180,7 +5180,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVmnmx(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5201,7 +5201,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVote(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public int SrcPred => (int)((_opcode >> 39) & 0x7); @@ -5224,7 +5224,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVset(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5254,7 +5254,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int SrcPred => (int)((_opcode >> 39) & 0x7); public bool SrcPredInv => (_opcode & 0x40000000000) != 0; public int DestPred => (int)((_opcode >> 3) & 0x7); - public int DestPredInv => (int)((_opcode >> 0) & 0x7); + public int DestPredInv => (int)(_opcode & 0x7); public bool BVideo => (_opcode & 0x4000000000000) != 0; } @@ -5262,7 +5262,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVshl(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5282,7 +5282,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstVshr(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5302,7 +5302,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstXmadR(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); @@ -5323,7 +5323,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstXmadI(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -5343,7 +5343,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstXmadC(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); public int CbufOffset => (int)((_opcode >> 20) & 0x3FFF); @@ -5365,7 +5365,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { private ulong _opcode; public InstXmadRc(ulong opcode) => _opcode = opcode; - public int Dest => (int)((_opcode >> 0) & 0xFF); + public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcC => (int)((_opcode >> 39) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); diff --git a/src/Ryujinx.Graphics.Shader/TextureHandle.cs b/src/Ryujinx.Graphics.Shader/TextureHandle.cs index 39d5c1c32..a59c8cd4a 100644 --- a/src/Ryujinx.Graphics.Shader/TextureHandle.cs +++ b/src/Ryujinx.Graphics.Shader/TextureHandle.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int UnpackTextureId(int packedId) { - return (packedId >> 0) & 0xfffff; + return packedId & 0xfffff; } /// <summary> diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs index bf23f4714..f404be744 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Vulkan private uint VertexAttributeDescriptionsCount => (byte)((Id6 >> 38) & 0xFF); private uint VertexBindingDescriptionsCount => (byte)((Id6 >> 46) & 0xFF); private uint ViewportsCount => (byte)((Id6 >> 54) & 0xFF); - private uint ScissorsCount => (byte)((Id7 >> 0) & 0xFF); + private uint ScissorsCount => (byte)(Id7 & 0xFF); private uint ColorBlendAttachmentStateCount => (byte)((Id7 >> 8) & 0xFF); private bool HasDepthStencil => ((Id7 >> 63) & 0x1) != 0UL; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs index 50f04e90c..6dd7e5b78 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs @@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public void CancelHandleReservation(int handle) { - int index = (handle >> 0) & 0x7fff; + int index = handle & 0x7fff; lock (_table) { From 2bf4555591d246ba315505d5b70fcaee43c1fa11 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Fri, 9 Jun 2023 11:11:53 +0100 Subject: [PATCH 617/737] Swkbd Applet Fixes (#5236) * Swkbd Applet Fixes * Forgot a full stop * Update src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Update src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 2 +- .../UI/Applet/SwkbdAppletDialog.axaml.cs | 2 +- .../SoftwareKeyboard/CJKCharacterValidation.cs | 17 +++++++++++++++++ .../Applets/SoftwareKeyboard/KeyboardMode.cs | 18 +++++++++++++----- src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs | 4 ++-- 5 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 79765db16..4065a1dfb 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -545,7 +545,7 @@ "SwkbdMinRangeCharacters": "Must be {0}-{1} characters long", "SoftwareKeyboard": "Software Keyboard", "SoftwareKeyboardModeNumbersOnly": "Must be numbers only", - "SoftwareKeyboardModeAlphabet": "Must be alphabets only", + "SoftwareKeyboardModeAlphabet": "Must be non CJK-characters only", "SoftwareKeyboardModeASCII": "Must be ASCII text only", "DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.", "DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.", diff --git a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index 04bc46193..81258b448 100644 --- a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -146,7 +146,7 @@ namespace Ryujinx.Ava.UI.Controls case KeyboardMode.Alphabet: localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet); validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText); - _checkInput = text => text.All(char.IsAsciiLetter); + _checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value)); break; case KeyboardMode.ASCII: localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII); diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs new file mode 100644 index 000000000..36e6ff512 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs @@ -0,0 +1,17 @@ +using System.Text.RegularExpressions; + +namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard +{ + public static partial class CJKCharacterValidation + { + public static bool IsCJK(char value) + { + Regex regex = CJKRegex(); + + return regex.IsMatch(value.ToString()); + } + + [GeneratedRegex("\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")] + private static partial Regex CJKRegex(); + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs index 01b3c9634..e28622111 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs @@ -6,22 +6,30 @@ public enum KeyboardMode : uint { /// <summary> - /// A full alpha-numeric keyboard. + /// All UTF-16 characters allowed. /// </summary> Default = 0, /// <summary> - /// Number pad. + /// Only numbers allowed. /// </summary> NumbersOnly = 1, /// <summary> - /// ASCII characters keyboard. + /// Only ASCII characters allowed. /// </summary> ASCII = 2, - FullLatin = 3, - Alphabet = 4, + /// <summary> + /// Synonymous with default. + /// </summary> + FullLatin = 3, + + /// <summary> + /// All UTF-16 characters except CJK characters allowed. + /// </summary> + Alphabet = 4, + SimplifiedChinese = 5, TraditionalChinese = 6, Korean = 7, diff --git a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs index 28067b751..13d30f6c0 100644 --- a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs +++ b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs @@ -93,8 +93,8 @@ namespace Ryujinx.Ui.Applet _checkInput = text => text.All(char.IsDigit); break; case KeyboardMode.Alphabet: - _validationInfoText += "<i>Must be alphabets only.</i>"; - _checkInput = text => text.All(char.IsAsciiLetter); + _validationInfoText += "<i>Must be non CJK-characters only.</i>"; + _checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value)); break; case KeyboardMode.ASCII: _validationInfoText += "<i>Must be ASCII text only.</i>"; From e94d24f5086e6bd371fe74661ad8a650fb99ea55 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Fri, 9 Jun 2023 08:05:32 -0300 Subject: [PATCH 618/737] Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' (#5231) * Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup * fix --- src/Ryujinx.HLE/FileSystem/ContentManager.cs | 22 +++++++++----------- src/Ryujinx.Input/Motion/CemuHook/Client.cs | 8 +++---- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs index 9facdd0b7..e00310a07 100644 --- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -181,7 +181,7 @@ namespace Ryujinx.HLE.FileSystem } } - if (_locationEntries.ContainsKey(storageId) && _locationEntries[storageId]?.Count == 0) + if (_locationEntries.TryGetValue(storageId, out var locationEntriesItem) && locationEntriesItem?.Count == 0) { _locationEntries.Remove(storageId); } @@ -347,9 +347,9 @@ namespace Ryujinx.HLE.FileSystem { lock (_lock) { - if (_contentDictionary.ContainsKey((titleId, contentType))) + if (_contentDictionary.TryGetValue((titleId, contentType), out var contentDictionaryItem)) { - return UInt128Utils.FromHex(_contentDictionary[(titleId, contentType)]); + return UInt128Utils.FromHex(contentDictionaryItem); } } @@ -719,9 +719,9 @@ namespace Ryujinx.HLE.FileSystem Nca nca = new Nca(_virtualFileSystem.KeySet, storage); - if (updateNcas.ContainsKey(nca.Header.TitleId)) + if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem)) { - updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName)); + updateNcasItem.Add((nca.Header.ContentType, entry.FullName)); } else { @@ -732,10 +732,8 @@ namespace Ryujinx.HLE.FileSystem } } - if (updateNcas.ContainsKey(SystemUpdateTitleId)) + if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry)) { - var ncaEntry = updateNcas[SystemUpdateTitleId]; - string metaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; CnmtContentMetaEntry[] metaEntries = null; @@ -770,9 +768,9 @@ namespace Ryujinx.HLE.FileSystem throw new FileNotFoundException("System update title was not found in the firmware package."); } - if (updateNcas.ContainsKey(SystemVersionTitleId)) + if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem)) { - string versionEntry = updateNcas[SystemVersionTitleId].Find(x => x.type != NcaContentType.Meta).path; + string versionEntry = updateNcasItem.Find(x => x.type != NcaContentType.Meta).path; using (Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry))) { @@ -916,9 +914,9 @@ namespace Ryujinx.HLE.FileSystem } } - if (updateNcas.ContainsKey(nca.Header.TitleId)) + if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem)) { - updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullPath)); + updateNcasItem.Add((nca.Header.ContentType, entry.FullPath)); } else { diff --git a/src/Ryujinx.Input/Motion/CemuHook/Client.cs b/src/Ryujinx.Input/Motion/CemuHook/Client.cs index 4498b8ca6..a79412a17 100644 --- a/src/Ryujinx.Input/Motion/CemuHook/Client.cs +++ b/src/Ryujinx.Input/Motion/CemuHook/Client.cs @@ -338,12 +338,10 @@ namespace Ryujinx.Input.Motion.CemuHook { int slot = inputData.Shared.Slot; - if (_motionData.ContainsKey(clientId)) + if (_motionData.TryGetValue(clientId, out var motionDataItem)) { - if (_motionData[clientId].ContainsKey(slot)) + if (motionDataItem.TryGetValue(slot, out var previousData)) { - MotionInput previousData = _motionData[clientId][slot]; - previousData.Update(accelerometer, gyroscrope, timestamp, cemuHookConfig.Sensitivity, (float)cemuHookConfig.GyroDeadzone); } else @@ -352,7 +350,7 @@ namespace Ryujinx.Input.Motion.CemuHook input.Update(accelerometer, gyroscrope, timestamp, cemuHookConfig.Sensitivity, (float)cemuHookConfig.GyroDeadzone); - _motionData[clientId].Add(slot, input); + motionDataItem.Add(slot, input); } } else From f7ec3102316cb209614f52afc5b069e33bcf6924 Mon Sep 17 00:00:00 2001 From: siegmund-heiss-ich <119589995+siegmund-heiss-ich@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:31:19 +0200 Subject: [PATCH 619/737] Check if existing oldConfigPath is a Symlink (#5271) --- src/Ryujinx.Common/Configuration/AppDataManager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs index d6e778430..b685e7064 100644 --- a/src/Ryujinx.Common/Configuration/AppDataManager.cs +++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs @@ -96,7 +96,7 @@ namespace Ryujinx.Common.Configuration if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile) { string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir); - if (Path.Exists(oldConfigPath) && !Path.Exists(BaseDirPath)) + if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath)) { CopyDirectory(oldConfigPath, BaseDirPath); Directory.Delete(oldConfigPath, true); @@ -115,6 +115,14 @@ namespace Ryujinx.Common.Configuration Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir)); } + // Check if existing old baseDirPath is a symlink, to prevent possible errors. + // Should be removed, when the existance of the old directory isn't checked anymore. + private static bool IsPathSymlink(string path) + { + FileAttributes attributes = File.GetAttributes(path); + return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint; + } + private static void CopyDirectory(string sourceDir, string destinationDir) { var dir = new DirectoryInfo(sourceDir); From 27ee86f33b8fb562f70c5fc2850f85683315626d Mon Sep 17 00:00:00 2001 From: siegmund-heiss-ich <119589995+siegmund-heiss-ich@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:35:24 +0200 Subject: [PATCH 620/737] Exclude macOS from checking for changed files (#5270) --- src/Ryujinx.Ava/Modules/Updater/Updater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Ava/Modules/Updater/Updater.cs b/src/Ryujinx.Ava/Modules/Updater/Updater.cs index 839526c4e..71d978c67 100644 --- a/src/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/src/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -741,7 +741,7 @@ namespace Ryujinx.Modules var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. // Determine and exclude user files only when the updater is running, not when cleaning old files - if (_running) + if (_running && !OperatingSystem.IsMacOS()) { // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); From 76b474e97b553f3edbb1f3c54e50170b97203171 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Fri, 9 Jun 2023 11:53:20 -0300 Subject: [PATCH 621/737] Update ShaderConfig.cs (#5226) --- src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 5c0a1fb60..41558dc3e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -726,7 +726,7 @@ namespace Ryujinx.Graphics.Shader.Translation var descriptors = new TextureDescriptor[dict.Count]; int i = 0; - foreach (var kv in dict.OrderBy(x => x.Key.Indexed).OrderBy(x => x.Key.Handle)) + foreach (var kv in dict.OrderBy(x => x.Key.Indexed).ThenBy(x => x.Key.Handle)) { var info = kv.Key; var meta = kv.Value; @@ -824,4 +824,4 @@ namespace Ryujinx.Graphics.Shader.Translation OmapTargets); } } -} \ No newline at end of file +} From 0e95a8271ac96b2c54907858140e2511a25a2b10 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Fri, 9 Jun 2023 14:44:22 -0300 Subject: [PATCH 622/737] Non-flags enums should not be used in bitwise operations (#5214) --- src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs | 3 +++ src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs | 3 +++ src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs | 3 +++ src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs | 5 ++++- src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs | 3 +++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index f5a776fa2..df5d39ae4 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -1,5 +1,8 @@ +using System; + namespace ARMeilleure.IntermediateRepresentation { + [Flags] enum Intrinsic : ushort { // X86 (SSE and AVX) diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs index 136762938..c7c506ec4 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs @@ -1,3 +1,5 @@ +using System; + namespace Ryujinx.Graphics.Shader.Decoders { enum AlSize @@ -711,6 +713,7 @@ namespace Ryujinx.Graphics.Shader.Decoders TexSamplerBorderColor = 22, } + [Flags] enum VectorSelect { U8B0 = 0, diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs index 1af94ab59..3f39e631d 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs @@ -1,5 +1,8 @@ +using System; + namespace Ryujinx.Graphics.Shader.Decoders { + [Flags] enum InstProps : ushort { None = 0, diff --git a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs index b1b40f652..a54eddc59 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs @@ -1,5 +1,8 @@ -namespace Ryujinx.Graphics.Shader.Translation +using System; + +namespace Ryujinx.Graphics.Shader.Translation { + [Flags] enum AggregateType { Invalid, diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs index 9577075c0..1d09e021e 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs @@ -1,5 +1,8 @@ +using System; + namespace Ryujinx.HLE.HOS.Kernel.Threading { + [Flags] enum ThreadSchedState : ushort { LowMask = 0xf, From eb0bb36bbfc3a4f5f2ac1c8721e192239f899a1d Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 10 Jun 2023 18:31:38 -0300 Subject: [PATCH 623/737] Implement transform feedback emulation for hardware without native support (#5080) * Implement transform feedback emulation for hardware without native support * Stop doing some useless buffer updates and account for non-zero base instance * Reduce redundant updates even more * Update descriptor init logic to account for ResourceLayout * Fix transform feedback and storage buffers not being updated in some cases * Shader cache version bump * PR feedback * SetInstancedDrawVertexCount must be always called after UpdateState * Minor typo --- src/Ryujinx.Graphics.GAL/Capabilities.cs | 3 + .../Engine/Threed/DrawManager.cs | 12 ++ .../Engine/Threed/StateUpdater.cs | 18 ++- .../Memory/BufferManager.cs | 110 ++++++++++++++++-- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 2 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 8 +- .../DiskCache/ParallelDiskCacheLoader.cs | 2 +- .../Shader/GpuAccessor.cs | 4 +- .../Shader/GpuAccessorBase.cs | 30 ++++- .../Shader/ResourceCounts.cs | 8 -- .../Shader/ShaderCache.cs | 2 +- .../Shader/ShaderInfoBuilder.cs | 63 ++++++++-- src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 1 + src/Ryujinx.Graphics.Shader/Constants.cs | 6 + src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 ++ .../Translation/EmitterContext.cs | 39 +++++++ .../Translation/ShaderConfig.cs | 32 ++++- .../DescriptorSetCollection.cs | 4 +- .../DescriptorSetUpdater.cs | 33 ++---- .../ShaderCollection.cs | 56 +++++++++ src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 21 files changed, 375 insertions(+), 68 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index 3b6e6b906..ef2da0b77 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsGeometryShader; public readonly bool SupportsGeometryShaderPassthrough; + public readonly bool SupportsTransformFeedback; public readonly bool SupportsImageLoadFormatted; public readonly bool SupportsLayerVertexTessellation; public readonly bool SupportsMismatchingViewFormat; @@ -77,6 +78,7 @@ namespace Ryujinx.Graphics.GAL bool supportsFragmentShaderOrderingIntel, bool supportsGeometryShader, bool supportsGeometryShaderPassthrough, + bool supportsTransformFeedback, bool supportsImageLoadFormatted, bool supportsLayerVertexTessellation, bool supportsMismatchingViewFormat, @@ -122,6 +124,7 @@ namespace Ryujinx.Graphics.GAL SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsGeometryShader = supportsGeometryShader; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; + SupportsTransformFeedback = supportsTransformFeedback; SupportsImageLoadFormatted = supportsImageLoadFormatted; SupportsLayerVertexTessellation = supportsLayerVertexTessellation; SupportsMismatchingViewFormat = supportsMismatchingViewFormat; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 7438ba03b..9c4921c8b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -539,6 +539,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed engine.UpdateState(); + if (instanceCount > 1) + { + // Must be called after UpdateState as it assumes the shader state + // has already been set, and that bindings have been updated already. + + _channel.BufferManager.SetInstancedDrawVertexCount(count); + } + if (indexed) { _context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance); @@ -676,6 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); } + _channel.BufferManager.SetInstancedDrawVertexCount(_instancedIndexCount); + _context.Renderer.Pipeline.DrawIndexed( _instancedIndexCount, _instanceIndex + 1, @@ -685,6 +695,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } else { + _channel.BufferManager.SetInstancedDrawVertexCount(_instancedDrawStateCount); + _context.Renderer.Pipeline.Draw( _instancedDrawStateCount, _instanceIndex + 1, diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 5fa4702b8..92e980ced 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -269,7 +269,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _prevFirstVertex = _state.State.FirstVertex; } - bool tfEnable = _state.State.TfEnable; + bool tfEnable = _state.State.TfEnable && _context.Capabilities.SupportsTransformFeedback; if (!tfEnable && _prevTfEnable) { @@ -1367,6 +1367,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _vsUsesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false; _vsClipDistancesWritten = gs.Shaders[1]?.Info.ClipDistancesWritten ?? 0; + bool hasTransformFeedback = gs.SpecializationState.TransformFeedbackDescriptors != null; + if (hasTransformFeedback != _channel.BufferManager.HasTransformFeedbackOutputs) + { + if (!_context.Capabilities.SupportsTransformFeedback) + { + // If host does not support transform feedback, and the shader changed, + // we might need to update bindings as transform feedback emulation + // uses storage buffer bindings that might have been used for something + // else in a previous draw. + + _channel.BufferManager.ForceTransformFeedbackAndStorageBuffersDirty(); + } + + _channel.BufferManager.HasTransformFeedbackOutputs = hasTransformFeedback; + } + if (oldVsClipDistancesWritten != _vsClipDistancesWritten) { UpdateUserClipState(); diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 48cb33b4d..07429cfe8 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -6,6 +6,7 @@ using Ryujinx.Graphics.Shader; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Memory { @@ -14,12 +15,17 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </summary> class BufferManager { + private const int TfInfoVertexCountOffset = Constants.TotalTransformFeedbackBuffers * sizeof(int); + private const int TfInfoBufferSize = TfInfoVertexCountOffset + sizeof(int); + private readonly GpuContext _context; private readonly GpuChannel _channel; private int _unalignedStorageBuffers; public bool HasUnalignedStorageBuffers => _unalignedStorageBuffers > 0; + public bool HasTransformFeedbackOutputs { get; set; } + private IndexBuffer _indexBuffer; private readonly VertexBuffer[] _vertexBuffers; private readonly BufferBounds[] _transformFeedbackBuffers; @@ -98,6 +104,9 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly BuffersPerStage[] _gpStorageBuffers; private readonly BuffersPerStage[] _gpUniformBuffers; + private BufferHandle _tfInfoBuffer; + private int[] _tfInfoData; + private bool _gpStorageBuffersDirty; private bool _gpUniformBuffersDirty; @@ -137,6 +146,11 @@ namespace Ryujinx.Graphics.Gpu.Memory _bufferTextures = new List<BufferTextureBinding>(); _ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages]; + + if (!context.Capabilities.SupportsTransformFeedback) + { + _tfInfoData = new int[Constants.TotalTransformFeedbackBuffers]; + } } @@ -319,6 +333,31 @@ namespace Ryujinx.Graphics.Gpu.Memory _gpUniformBuffersDirty = true; } + /// <summary> + /// Sets the number of vertices per instance on a instanced draw. Used for transform feedback emulation. + /// </summary> + /// <param name="vertexCount">Vertex count per instance</param> + public void SetInstancedDrawVertexCount(int vertexCount) + { + if (!_context.Capabilities.SupportsTransformFeedback && + HasTransformFeedbackOutputs && + _tfInfoBuffer != BufferHandle.Null) + { + Span<byte> data = stackalloc byte[sizeof(int)]; + MemoryMarshal.Cast<byte, int>(data)[0] = vertexCount; + _context.Renderer.SetBufferData(_tfInfoBuffer, TfInfoVertexCountOffset, data); + } + } + + /// <summary> + /// Forces transform feedback and storage buffers to be updated on the next draw. + /// </summary> + public void ForceTransformFeedbackAndStorageBuffersDirty() + { + _transformFeedbackBuffersDirty = true; + _gpStorageBuffersDirty = true; + } + /// <summary> /// Sets the binding points for the storage buffers bound on the compute pipeline. /// </summary> @@ -537,22 +576,75 @@ namespace Ryujinx.Graphics.Gpu.Memory { _transformFeedbackBuffersDirty = false; - Span<BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers]; - - for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) + if (_context.Capabilities.SupportsTransformFeedback) { - BufferBounds tfb = _transformFeedbackBuffers[index]; + Span<BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers]; - if (tfb.Address == 0) + for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) { - tfbs[index] = BufferRange.Empty; - continue; + BufferBounds tfb = _transformFeedbackBuffers[index]; + + if (tfb.Address == 0) + { + tfbs[index] = BufferRange.Empty; + continue; + } + + tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size, write: true); } - tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size, write: true); + _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); } + else if (HasTransformFeedbackOutputs) + { + Span<int> info = _tfInfoData.AsSpan(); + Span<BufferAssignment> buffers = stackalloc BufferAssignment[Constants.TotalTransformFeedbackBuffers + 1]; - _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); + bool needsDataUpdate = false; + + if (_tfInfoBuffer == BufferHandle.Null) + { + _tfInfoBuffer = _context.Renderer.CreateBuffer(TfInfoBufferSize); + } + + buffers[0] = new BufferAssignment(0, new BufferRange(_tfInfoBuffer, 0, TfInfoBufferSize)); + + int alignment = _context.Capabilities.StorageBufferOffsetAlignment; + + for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) + { + BufferBounds tfb = _transformFeedbackBuffers[index]; + + if (tfb.Address == 0) + { + buffers[1 + index] = new BufferAssignment(1 + index, BufferRange.Empty); + } + else + { + ulong endAddress = tfb.Address + tfb.Size; + ulong address = BitUtils.AlignDown(tfb.Address, (ulong)alignment); + ulong size = endAddress - address; + + int tfeOffset = ((int)tfb.Address & (alignment - 1)) / 4; + + if (info[index] != tfeOffset) + { + info[index] = tfeOffset; + needsDataUpdate = true; + } + + buffers[1 + index] = new BufferAssignment(1 + index, bufferCache.GetBufferRange(address, size, write: true)); + } + } + + if (needsDataUpdate) + { + Span<byte> infoData = MemoryMarshal.Cast<int, byte>(info); + _context.Renderer.SetBufferData(_tfInfoBuffer, 0, infoData); + } + + _context.Renderer.Pipeline.SetStorageBuffers(buffers); + } } else { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 17639ca17..537cead0e 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache ShaderSpecializationState oldSpecState, ShaderSpecializationState newSpecState, ResourceCounts counts, - int stageIndex) : base(context, counts, stageIndex) + int stageIndex) : base(context, counts, stageIndex, oldSpecState.TransformFeedbackDescriptors != null) { _data = data; _cb1Data = cb1Data; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index f35b542a2..153a2e8c1 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5044; + private const uint CodeGenVersion = 5080; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; @@ -368,7 +368,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache if (hostCode != null) { - ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(context, shaders, specState.PipelineState); + ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache( + context, + shaders, + specState.PipelineState, + specState.TransformFeedbackDescriptors != null); IProgram hostProgram; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index d80518b10..8df89824b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -491,7 +491,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context); + ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null); for (int index = 0; index < compilation.TranslatedStages.Length; index++) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 3e8167331..5e18e61f2 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Shader GpuContext context, GpuChannel channel, GpuAccessorState state, - int stageIndex) : base(context, state.ResourceCounts, stageIndex) + int stageIndex) : base(context, state.ResourceCounts, stageIndex, state.TransformFeedbackDescriptors != null) { _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; _channel = channel; @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="context">GPU context</param> /// <param name="channel">GPU channel</param> /// <param name="state">Current GPU state</param> - public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context, state.ResourceCounts, 0) + public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context, state.ResourceCounts, 0, false) { _channel = channel; _state = state; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 57e79ac7f..2dd7c631c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -17,40 +17,56 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly ResourceCounts _resourceCounts; private readonly int _stageIndex; + private readonly int _reservedConstantBuffers; + private readonly int _reservedStorageBuffers; + /// <summary> /// Creates a new GPU accessor. /// </summary> /// <param name="context">GPU context</param> - public GpuAccessorBase(GpuContext context, ResourceCounts resourceCounts, int stageIndex) + /// <param name="resourceCounts">Counter of GPU resources used by the shader</param> + /// <param name="stageIndex">Index of the shader stage, 0 for compute</param> + /// <param name="tfEnabled">Indicates if the current graphics shader is used with transform feedback enabled</param> + public GpuAccessorBase(GpuContext context, ResourceCounts resourceCounts, int stageIndex, bool tfEnabled) { _context = context; _resourceCounts = resourceCounts; _stageIndex = stageIndex; + + _reservedConstantBuffers = 1; // For the support buffer. + _reservedStorageBuffers = !context.Capabilities.SupportsTransformFeedback && tfEnabled ? 5 : 0; } public int QueryBindingConstantBuffer(int index) { + int binding; + if (_context.Capabilities.Api == TargetApi.Vulkan) { - // We need to start counting from 1 since binding 0 is reserved for the support uniform buffer. - return GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer") + 1; + binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer"); } else { - return _resourceCounts.UniformBuffersCount++; + binding = _resourceCounts.UniformBuffersCount++; } + + return binding + _reservedConstantBuffers; } public int QueryBindingStorageBuffer(int index) { + int binding; + if (_context.Capabilities.Api == TargetApi.Vulkan) { - return GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer"); + binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer"); } else { - return _resourceCounts.StorageBuffersCount++; + binding = _resourceCounts.StorageBuffersCount++; } + + return binding + _reservedStorageBuffers; } public int QueryBindingTexture(int index, bool isBuffer) @@ -149,6 +165,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod; + public bool QueryHostSupportsTransformFeedback() => _context.Capabilities.SupportsTransformFeedback; + public bool QueryHostSupportsViewportIndexVertexTessellation() => _context.Capabilities.SupportsViewportIndexVertexTessellation; public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs b/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs index b85423cb3..f495229ff 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs @@ -24,13 +24,5 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Total of images used by the shaders. /// </summary> public int ImagesCount; - - /// <summary> - /// Creates a new instance of the shader resource counts class. - /// </summary> - public ResourceCounts() - { - UniformBuffersCount = 1; // The first binding is reserved for the support buffer. - } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index b7ba159a5..913f6e9ec 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -362,7 +362,7 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatorContext previousStage = null; - ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context); + ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context, transformFeedbackDescriptors != null); for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 3fc32d711..83d92edc3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -16,15 +16,24 @@ namespace Ryujinx.Graphics.Gpu.Shader private const int TextureSetIndex = 2; private const int ImageSetIndex = 3; - private const ResourceStages SupportBufferStags = + private const ResourceStages SupportBufferStages = ResourceStages.Compute | ResourceStages.Vertex | ResourceStages.Fragment; + private const ResourceStages VtgStages = + ResourceStages.Vertex | + ResourceStages.TessellationControl | + ResourceStages.TessellationEvaluation | + ResourceStages.Geometry; + private readonly GpuContext _context; private int _fragmentOutputMap; + private readonly int _reservedConstantBuffers; + private readonly int _reservedStorageBuffers; + private readonly List<ResourceDescriptor>[] _resourceDescriptors; private readonly List<ResourceUsage>[] _resourceUsages; @@ -32,7 +41,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Creates a new shader info builder. /// </summary> /// <param name="context">GPU context that owns the shaders that will be added to the builder</param> - public ShaderInfoBuilder(GpuContext context) + /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param> + public ShaderInfoBuilder(GpuContext context, bool tfEnabled) { _context = context; @@ -47,7 +57,22 @@ namespace Ryujinx.Graphics.Gpu.Shader _resourceUsages[index] = new(); } - AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); + AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); + + _reservedConstantBuffers = 1; // For the support buffer. + + if (!context.Capabilities.SupportsTransformFeedback && tfEnabled) + { + _reservedStorageBuffers = 5; + + AddDescriptor(VtgStages, ResourceType.StorageBuffer, StorageSetIndex, 0, 5); + AddUsage(VtgStages, ResourceType.StorageBuffer, ResourceAccess.Read, StorageSetIndex, 0, 1); + AddUsage(VtgStages, ResourceType.StorageBuffer, ResourceAccess.Write, StorageSetIndex, 1, 4); + } + else + { + _reservedStorageBuffers = 0; + } } /// <summary> @@ -86,8 +111,8 @@ namespace Ryujinx.Graphics.Gpu.Shader int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage; int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage; - int uniformBinding = 1 + stageIndex * uniformsPerStage; - int storageBinding = stageIndex * storagesPerStage; + int uniformBinding = _reservedConstantBuffers + stageIndex * uniformsPerStage; + int storageBinding = _reservedStorageBuffers + stageIndex * storagesPerStage; int textureBinding = stageIndex * texturesPerStage * 2; int imageBinding = stageIndex * imagesPerStage * 2; @@ -133,6 +158,23 @@ namespace Ryujinx.Graphics.Gpu.Shader AddDescriptor(stages, type2, setIndex, binding + count, count); } + /// <summary> + /// Adds buffer usage information to the list of usages. + /// </summary> + /// <param name="stages">Shader stages where the resource is used</param> + /// <param name="type">Type of the resource</param> + /// <param name="access">How the resource is accessed by the shader stages where it is used</param> + /// <param name="setIndex">Descriptor set number where the resource will be bound</param> + /// <param name="binding">Binding number where the resource will be bound</param> + /// <param name="count">Number of resources bound at the binding location</param> + private void AddUsage(ResourceStages stages, ResourceType type, ResourceAccess access, int setIndex, int binding, int count) + { + for (int index = 0; index < count; index++) + { + _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, type, stages, access)); + } + } + /// <summary> /// Adds buffer usage information to the list of usages. /// </summary> @@ -212,10 +254,15 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="context">GPU context that owns the shaders</param> /// <param name="programs">Shaders from the disk cache</param> /// <param name="pipeline">Optional pipeline for background compilation</param> + /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param> /// <returns>Shader information</returns> - public static ShaderInfo BuildForCache(GpuContext context, IEnumerable<CachedShaderStage> programs, ProgramPipelineState? pipeline) + public static ShaderInfo BuildForCache( + GpuContext context, + IEnumerable<CachedShaderStage> programs, + ProgramPipelineState? pipeline, + bool tfEnabled) { - ShaderInfoBuilder builder = new ShaderInfoBuilder(context); + ShaderInfoBuilder builder = new ShaderInfoBuilder(context, tfEnabled); foreach (CachedShaderStage program in programs) { @@ -237,7 +284,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <returns>Shader information</returns> public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false) { - ShaderInfoBuilder builder = new ShaderInfoBuilder(context); + ShaderInfoBuilder builder = new ShaderInfoBuilder(context, tfEnabled: false); builder.AddStageInfo(info); diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 81faa00ef..c7c01a37b 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -153,6 +153,7 @@ namespace Ryujinx.Graphics.OpenGL supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, supportsGeometryShader: true, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, + supportsTransformFeedback: true, supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted, supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat, diff --git a/src/Ryujinx.Graphics.Shader/Constants.cs b/src/Ryujinx.Graphics.Shader/Constants.cs index 7f1445ed0..39d6b2381 100644 --- a/src/Ryujinx.Graphics.Shader/Constants.cs +++ b/src/Ryujinx.Graphics.Shader/Constants.cs @@ -10,5 +10,11 @@ namespace Ryujinx.Graphics.Shader public const int NvnBaseVertexByteOffset = 0x640; public const int NvnBaseInstanceByteOffset = 0x644; public const int NvnDrawIndexByteOffset = 0x648; + + // Transform Feedback emulation. + + public const int TfeInfoBinding = 0; + public const int TfeBufferBaseBinding = 1; + public const int TfeBuffersCount = 4; } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index d3794cddd..1c2b28097 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -367,6 +367,15 @@ namespace Ryujinx.Graphics.Shader return true; } + /// <summary> + /// Queries host GPU transform feedback support. + /// </summary> + /// <returns>True if the GPU and driver supports transform feedback, false otherwise</returns> + bool QueryHostSupportsTransformFeedback() + { + return true; + } + /// <summary> /// Queries host support for writes to the viewport index from vertex or tessellation shader stages. /// </summary> diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 6ca74a379..87e5457f9 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -234,6 +234,45 @@ namespace Ryujinx.Graphics.Shader.Translation public void PrepareForVertexReturn() { + if (!Config.GpuAccessor.QueryHostSupportsTransformFeedback() && Config.GpuAccessor.QueryTransformFeedbackEnabled()) + { + Operand vertexCount = this.Load(StorageKind.StorageBuffer, Constants.TfeInfoBinding, Const(1)); + + for (int tfbIndex = 0; tfbIndex < Constants.TfeBuffersCount; tfbIndex++) + { + var locations = Config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); + var stride = Config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); + + Operand baseOffset = this.Load(StorageKind.StorageBuffer, Constants.TfeInfoBinding, Const(0), Const(tfbIndex)); + Operand baseVertex = this.Load(StorageKind.Input, IoVariable.BaseVertex); + Operand baseInstance = this.Load(StorageKind.Input, IoVariable.BaseInstance); + Operand vertexIndex = this.Load(StorageKind.Input, IoVariable.VertexIndex); + Operand instanceIndex = this.Load(StorageKind.Input, IoVariable.InstanceIndex); + + Operand outputVertexOffset = this.ISubtract(vertexIndex, baseVertex); + Operand outputInstanceOffset = this.ISubtract(instanceIndex, baseInstance); + + Operand outputBaseVertex = this.IMultiply(outputInstanceOffset, vertexCount); + + Operand vertexOffset = this.IMultiply(this.IAdd(outputBaseVertex, outputVertexOffset), Const(stride / 4)); + baseOffset = this.IAdd(baseOffset, vertexOffset); + + for (int j = 0; j < locations.Length; j++) + { + byte location = locations[j]; + if (location == 0xff) + { + continue; + } + + Operand offset = this.IAdd(baseOffset, Const(j)); + Operand value = Instructions.AttributeMap.GenerateAttributeLoad(this, null, location * 4, isOutput: true, isPerPatch: false); + + this.Store(StorageKind.StorageBuffer, Constants.TfeBufferBaseBinding + tfbIndex, Const(0), offset, value); + } + } + } + if (Config.GpuAccessor.QueryViewportTransformDisable()) { Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)); diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 41558dc3e..534bda70e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -132,6 +132,11 @@ namespace Ryujinx.Graphics.Shader.Translation _transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>(); + TransformFeedbackEnabled = + stage != ShaderStage.Compute && + gpuAccessor.QueryTransformFeedbackEnabled() && + gpuAccessor.QueryHostSupportsTransformFeedback(); + UsedInputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>(); @@ -139,6 +144,31 @@ namespace Ryujinx.Graphics.Shader.Translation _usedImages = new Dictionary<TextureInfo, TextureMeta>(); ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); + + if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) + { + StructureType tfeInfoStruct = new StructureType(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "base_offset", 4), + new StructureField(AggregateType.U32, "vertex_count") + }); + + BufferDefinition tfeInfoBuffer = new BufferDefinition(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); + + Properties.AddStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); + + StructureType tfeDataStruct = new StructureType(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + }); + + for (int i = 0; i < Constants.TfeBuffersCount; i++) + { + int binding = Constants.TfeBufferBaseBinding + i; + BufferDefinition tfeDataBuffer = new BufferDefinition(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); + Properties.AddStorageBuffer(binding, tfeDataBuffer); + } + } } public ShaderConfig( @@ -151,7 +181,6 @@ namespace Ryujinx.Graphics.Shader.Translation ThreadsPerInputPrimitive = 1; OutputTopology = outputTopology; MaxOutputVertices = maxOutputVertices; - TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); } public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options) @@ -165,7 +194,6 @@ namespace Ryujinx.Graphics.Shader.Translation OmapTargets = header.OmapTargets; OmapSampleMask = header.OmapSampleMask; OmapDepth = header.OmapDepth; - TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); LastInVertexPipeline = header.Stage < ShaderStage.Fragment; } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs index 70b3ebfe4..a185d7871 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs @@ -16,9 +16,9 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSets = descriptorSets; } - public void InitializeBuffers(int setIndex, int baseBinding, int countPerUnit, DescriptorType type, VkBuffer dummyBuffer) + public void InitializeBuffers(int setIndex, int baseBinding, int count, DescriptorType type, VkBuffer dummyBuffer) { - Span<DescriptorBufferInfo> infos = stackalloc DescriptorBufferInfo[countPerUnit]; + Span<DescriptorBufferInfo> infos = stackalloc DescriptorBufferInfo[count]; infos.Fill(new DescriptorBufferInfo() { diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index cbac1cd47..b09a0667e 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -596,36 +596,19 @@ namespace Ryujinx.Graphics.Vulkan } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc) { + // We don't support clearing texture descriptors currently. + if (setIndex != PipelineBase.UniformSetIndex && setIndex != PipelineBase.StorageSetIndex) + { + return; + } + var dummyBuffer = _dummyBuffer?.GetBuffer().Get(cbs).Value ?? default; - uint stages = _program.Stages; - - while (stages != 0) + foreach (ResourceBindingSegment segment in _program.ClearSegments[setIndex]) { - int stage = BitOperations.TrailingZeroCount(stages); - stages &= ~(1u << stage); - - if (setIndex == PipelineBase.UniformSetIndex) - { - dsc.InitializeBuffers( - 0, - 1 + stage * Constants.MaxUniformBuffersPerStage, - Constants.MaxUniformBuffersPerStage, - DescriptorType.UniformBuffer, - dummyBuffer); - } - else if (setIndex == PipelineBase.StorageSetIndex) - { - dsc.InitializeBuffers( - 0, - stage * Constants.MaxStorageBuffersPerStage, - Constants.MaxStorageBuffersPerStage, - DescriptorType.StorageBuffer, - dummyBuffer); - } + dsc.InitializeBuffers(0, segment.Binding, segment.Count, segment.Type.Convert(), dummyBuffer); } } diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index 334dfc200..222cdf2a7 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan public uint Stages { get; } + public ResourceBindingSegment[][] ClearSegments { get; } public ResourceBindingSegment[][] BindingSegments { get; } public ProgramLinkStatus LinkStatus { get; private set; } @@ -115,6 +116,7 @@ namespace Ryujinx.Graphics.Vulkan Stages = stages; + ClearSegments = BuildClearSegments(resourceLayout.Sets); BindingSegments = BuildBindingSegments(resourceLayout.SetUsages); _compileTask = Task.CompletedTask; @@ -135,6 +137,60 @@ namespace Ryujinx.Graphics.Vulkan _firstBackgroundUse = !fromCache; } + private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets) + { + ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][]; + + for (int setIndex = 0; setIndex < sets.Count; setIndex++) + { + List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>(); + + ResourceDescriptor currentDescriptor = default; + int currentCount = 0; + + for (int index = 0; index < sets[setIndex].Descriptors.Count; index++) + { + ResourceDescriptor descriptor = sets[setIndex].Descriptors[index]; + + if (currentDescriptor.Binding + currentCount != descriptor.Binding || + currentDescriptor.Type != descriptor.Type || + currentDescriptor.Stages != descriptor.Stages) + { + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentDescriptor.Binding, + currentCount, + currentDescriptor.Type, + currentDescriptor.Stages, + ResourceAccess.ReadWrite)); + } + + currentDescriptor = descriptor; + currentCount = descriptor.Count; + } + else + { + currentCount += descriptor.Count; + } + } + + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentDescriptor.Binding, + currentCount, + currentDescriptor.Type, + currentDescriptor.Stages, + ResourceAccess.ReadWrite)); + } + + segments[setIndex] = currentSegments.ToArray(); + } + + return segments; + } + private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages) { ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index a059d683a..8c1787d69 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -589,6 +589,7 @@ namespace Ryujinx.Graphics.Vulkan supportsFragmentShaderOrderingIntel: false, supportsGeometryShader: Capabilities.SupportsGeometryShader, supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, + supportsTransformFeedback: Capabilities.SupportsTransformFeedback, supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat, supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer, supportsMismatchingViewFormat: true, From 193ca3c9a28b63613407c2923f5903e1dd33fdce Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 10 Jun 2023 21:51:35 -0300 Subject: [PATCH 624/737] Implement fast path for AES crypto instructions on Arm64 (#5281) * Implement fast path for AES crypto instructions on Arm64 * PPTC version bump * Use AES HW feature check --- .../CodeGen/Arm64/CodeGenerator.cs | 2 -- .../CodeGen/Arm64/CodeGeneratorIntrinsic.cs | 29 +++++++++++++++++++ .../CodeGen/Arm64/IntrinsicTable.cs | 4 +-- .../CodeGen/Arm64/IntrinsicType.cs | 7 +++-- src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs | 1 + .../Instructions/InstEmitSimdCrypto.cs | 24 ++++++++++++--- .../Instructions/InstEmitSimdCrypto32.cs | 24 ++++++++++++--- src/ARMeilleure/Optimizations.cs | 2 ++ src/ARMeilleure/Translation/PTC/Ptc.cs | 2 +- 9 files changed, 79 insertions(+), 16 deletions(-) diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs index fc4fa976e..927198505 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs @@ -168,8 +168,6 @@ namespace ARMeilleure.CodeGen.Arm64 Logger.StartPass(PassName.CodeGeneration); - //Console.Error.WriteLine(IRDumper.GetDump(cfg)); - bool relocatable = (cctx.Options & CompilerOptions.Relocatable) != 0; CodeGenContext context = new(allocResult, maxCallArgs, cfg.Blocks.Count, relocatable); diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs index aaa00bb65..1309404a8 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs @@ -179,6 +179,35 @@ namespace ARMeilleure.CodeGen.Arm64 (uint)operation.GetSource(2).AsInt32()); break; + case IntrinsicType.Vector128Unary: + GenerateVectorUnary( + context, + 1, + 0, + info.Inst, + operation.Destination, + operation.GetSource(0)); + break; + case IntrinsicType.Vector128Binary: + GenerateVectorBinary( + context, + 1, + 0, + info.Inst, + operation.Destination, + operation.GetSource(0), + operation.GetSource(1)); + break; + case IntrinsicType.Vector128BinaryRd: + GenerateVectorUnary( + context, + 1, + 0, + info.Inst, + operation.Destination, + operation.GetSource(1)); + break; + case IntrinsicType.VectorUnary: GenerateVectorUnary( context, diff --git a/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs index a309d56d9..c2bd0bd51 100644 --- a/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs +++ b/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs @@ -19,8 +19,8 @@ namespace ARMeilleure.CodeGen.Arm64 Add(Intrinsic.Arm64AddvV, new IntrinsicInfo(0x0e31b800u, IntrinsicType.VectorUnary)); Add(Intrinsic.Arm64AddS, new IntrinsicInfo(0x5e208400u, IntrinsicType.ScalarBinary)); Add(Intrinsic.Arm64AddV, new IntrinsicInfo(0x0e208400u, IntrinsicType.VectorBinary)); - Add(Intrinsic.Arm64AesdV, new IntrinsicInfo(0x4e285800u, IntrinsicType.Vector128Unary)); - Add(Intrinsic.Arm64AeseV, new IntrinsicInfo(0x4e284800u, IntrinsicType.Vector128Unary)); + Add(Intrinsic.Arm64AesdV, new IntrinsicInfo(0x4e285800u, IntrinsicType.Vector128BinaryRd)); + Add(Intrinsic.Arm64AeseV, new IntrinsicInfo(0x4e284800u, IntrinsicType.Vector128BinaryRd)); Add(Intrinsic.Arm64AesimcV, new IntrinsicInfo(0x4e287800u, IntrinsicType.Vector128Unary)); Add(Intrinsic.Arm64AesmcV, new IntrinsicInfo(0x4e286800u, IntrinsicType.Vector128Unary)); Add(Intrinsic.Arm64AndV, new IntrinsicInfo(0x0e201c00u, IntrinsicType.VectorBinaryBitwise)); diff --git a/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs index 800eca93c..df61ea1eb 100644 --- a/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs +++ b/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs @@ -23,6 +23,10 @@ namespace ARMeilleure.CodeGen.Arm64 ScalarTernaryShlRd, ScalarTernaryShrRd, + Vector128Unary, + Vector128Binary, + Vector128BinaryRd, + VectorUnary, VectorUnaryBitwise, VectorUnaryByElem, @@ -50,9 +54,6 @@ namespace ARMeilleure.CodeGen.Arm64 VectorTernaryShlRd, VectorTernaryShrRd, - Vector128Unary, - Vector128Binary, - GetRegister, SetRegister } diff --git a/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs b/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs index 6ea9d2397..74f80e0fb 100644 --- a/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs +++ b/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs @@ -746,6 +746,7 @@ namespace ARMeilleure.CodeGen.Arm64 info.Type == IntrinsicType.ScalarTernaryFPRdByElem || info.Type == IntrinsicType.ScalarTernaryShlRd || info.Type == IntrinsicType.ScalarTernaryShrRd || + info.Type == IntrinsicType.Vector128BinaryRd || info.Type == IntrinsicType.VectorBinaryRd || info.Type == IntrinsicType.VectorInsertByElem || info.Type == IntrinsicType.VectorTernaryRd || diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs b/src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs index db24e0290..6226e35ae 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs @@ -17,7 +17,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AesdV, d, n); + } + else if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } @@ -38,7 +42,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AeseV, d, n); + } + else if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } @@ -58,7 +66,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AesimcV, n); + } + else if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesimc, n); } @@ -78,7 +90,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AesmcV, n); + } + else if (Optimizations.UseAesni) { Operand roundKey = context.VectorZero(); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs index f713a388c..24fd7c87e 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs @@ -17,7 +17,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AesdV, d, n); + } + else if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } @@ -38,7 +42,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AeseV, d, n); + } + else if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } @@ -58,7 +66,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AesimcV, n); + } + else if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesimc, n); } @@ -78,7 +90,11 @@ namespace ARMeilleure.Instructions Operand res; - if (Optimizations.UseAesni) + if (Optimizations.UseArm64Aes) + { + res = context.AddIntrinsic(Intrinsic.Arm64AesmcV, n); + } + else if (Optimizations.UseAesni) { Operand roundKey = context.VectorZero(); diff --git a/src/ARMeilleure/Optimizations.cs b/src/ARMeilleure/Optimizations.cs index a84a4dc4f..13348cec2 100644 --- a/src/ARMeilleure/Optimizations.cs +++ b/src/ARMeilleure/Optimizations.cs @@ -13,6 +13,7 @@ namespace ARMeilleure public static bool UseUnmanagedDispatchLoop { get; set; } = true; public static bool UseAdvSimdIfAvailable { get; set; } = true; + public static bool UseArm64AesIfAvailable { get; set; } = true; public static bool UseArm64PmullIfAvailable { get; set; } = true; public static bool UseSseIfAvailable { get; set; } = true; @@ -41,6 +42,7 @@ namespace ARMeilleure } internal static bool UseAdvSimd => UseAdvSimdIfAvailable && Arm64HardwareCapabilities.SupportsAdvSimd; + internal static bool UseArm64Aes => UseArm64AesIfAvailable && Arm64HardwareCapabilities.SupportsAes; internal static bool UseArm64Pmull => UseArm64PmullIfAvailable && Arm64HardwareCapabilities.SupportsPmull; internal static bool UseSse => UseSseIfAvailable && X86HardwareCapabilities.SupportsSse; diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index ea4e715b5..366dea6bf 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 4661; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5281; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; From 638f3761f3215d088ed8c6da3cd461bb830d2c31 Mon Sep 17 00:00:00 2001 From: Patrick Hovsepian <patrick.hovsepian@gmail.com> Date: Sun, 11 Jun 2023 06:34:56 -0700 Subject: [PATCH 625/737] Show/Hide UI Hotkey fix on Avalonia (#5133) * fix show/hide ui for ava * revert style * unbound by default * revert --- src/Ryujinx.Ava/AppHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 2502fa41b..0c816538b 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -1044,7 +1044,7 @@ namespace Ryujinx.Ava ScreenshotRequested = true; break; case KeyboardHotkeyState.ShowUi: - _viewModel.ShowMenuAndStatusBar = true; + _viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar; break; case KeyboardHotkeyState.Pause: if (_viewModel.IsPaused) From 9a1b74799d350f9b4ba365bf8b118bddf517711f Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 11 Jun 2023 18:31:22 +0200 Subject: [PATCH 626/737] Ava: Fix OpenGL on Linux again (#5216) * ava: Fix OpenGL on Linux again This shouldn't be working like that, but for some reason it does. * Apply the correct fix * gtk: Add warning messages for caught exceptions * ava: Handle disposing the same way as GTK does * Address review feedback --- src/Ryujinx.Ava/AppHost.cs | 18 +++++++++++---- src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 2 +- .../UI/Renderer/EmbeddedWindowOpenGL.cs | 23 ++++++++++++------- src/Ryujinx/Ui/GLRenderer.cs | 18 +++++++++++---- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 0c816538b..d3e0ea392 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -40,6 +40,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SPB.Graphics.Exceptions; using SPB.Graphics.Vulkan; using System; using System.Collections.Generic; @@ -475,11 +476,20 @@ namespace Ryujinx.Ava _windowsMultimediaTimerResolution = null; } - (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(); + if (_rendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) + { + // Try to bind the OpenGL context before calling the shutdown event. + openGlWindow.MakeCurrent(false, false); - Device.DisposeGpu(); + Device.DisposeGpu(); - (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null); + // Unbind context and destroy everything. + openGlWindow.MakeCurrent(true, false); + } + else + { + Device.DisposeGpu(); + } } private void HideCursorState_Changed(object sender, ReactiveEventArgs<HideCursorMode> state) @@ -930,7 +940,7 @@ namespace Ryujinx.Ava _gpuDoneEvent.Set(); }); - (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null); + (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); } public void UpdateStatus() diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs index a5c8b0031..3b2c32e3f 100644 --- a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs +++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -123,7 +123,7 @@ namespace Ryujinx.Ava.UI.Renderer } else { - X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow; + X11Window = PlatformHelper.CreateOpenGLWindow(new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false), 0, 0, 100, 100) as GLXWindow; } WindowHandle = X11Window.WindowHandle.RawHandle; diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs index 305e891a1..d427ab88c 100644 --- a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs +++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs @@ -1,9 +1,11 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.OpenGL; using Ryujinx.Ui.Common.Configuration; using SPB.Graphics; +using SPB.Graphics.Exceptions; using SPB.Graphics.OpenGL; using SPB.Platform; using SPB.Platform.WGL; @@ -18,8 +20,6 @@ namespace Ryujinx.Ava.UI.Renderer public OpenGLContextBase Context { get; set; } - public EmbeddedWindowOpenGL() { } - protected override void OnWindowDestroying() { Context.Dispose(); @@ -62,14 +62,21 @@ namespace Ryujinx.Ava.UI.Renderer Context.MakeCurrent(null); } - public void MakeCurrent() + public void MakeCurrent(bool unbind = false, bool shouldThrow = true) { - Context?.MakeCurrent(_window); - } + try + { + Context?.MakeCurrent(!unbind ? _window : null); + } + catch (ContextException e) + { + if (shouldThrow) + { + throw; + } - public void MakeCurrent(NativeWindowBase window) - { - Context?.MakeCurrent(window); + Logger.Warning?.Print(LogClass.Ui, $"Failed to {(!unbind ? "bind" : "unbind")} OpenGL context: {e}"); + } } public void SwapBuffers() diff --git a/src/Ryujinx/Ui/GLRenderer.cs b/src/Ryujinx/Ui/GLRenderer.cs index e3a9804ea..c56996910 100644 --- a/src/Ryujinx/Ui/GLRenderer.cs +++ b/src/Ryujinx/Ui/GLRenderer.cs @@ -1,8 +1,10 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.OpenGL; using Ryujinx.Input.HLE; using SPB.Graphics; +using SPB.Graphics.Exceptions; using SPB.Graphics.OpenGL; using SPB.Platform; using SPB.Platform.GLX; @@ -112,24 +114,30 @@ namespace Ryujinx.Ui protected override void Dispose(bool disposing) { - // Try to bind the OpenGL context before calling the shutdown event + // Try to bind the OpenGL context before calling the shutdown event. try { _openGLContext?.MakeCurrent(_nativeWindow); } - catch (Exception) { } + catch (ContextException e) + { + Logger.Warning?.Print(LogClass.Ui, $"Failed to bind OpenGL context: {e}"); + } Device?.DisposeGpu(); NpadManager.Dispose(); - // Unbind context and destroy everything + // Unbind context and destroy everything. try { _openGLContext?.MakeCurrent(null); } - catch (Exception) { } + catch (ContextException e) + { + Logger.Warning?.Print(LogClass.Ui, $"Failed to unbind OpenGL context: {e}"); + } _openGLContext?.Dispose(); } } -} +} \ No newline at end of file From 0cc266ff190b2594304271f1635b624ef008036f Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 12 Jun 2023 12:29:41 +0200 Subject: [PATCH 627/737] infra: Add PR triage action (#5293) This is a bare minimal triage action that handle big categories. In the future we could also label all services correctly but I didn't felt this was required for a first iteration. --- .github/assign/audio.yml | 8 ++++++ .github/assign/cpu.yml | 11 +++++++ .github/assign/global.yml | 4 +++ .github/assign/gpu.yml | 10 +++++++ .github/assign/gui.yml | 11 +++++++ .github/assign/horizon.yml | 11 +++++++ .github/assign/infra.yml | 9 ++++++ .github/labeler.yml | 33 +++++++++++++++++++++ .github/workflows/pr_triage.yml | 51 +++++++++++++++++++++++++++++++++ 9 files changed, 148 insertions(+) create mode 100644 .github/assign/audio.yml create mode 100644 .github/assign/cpu.yml create mode 100644 .github/assign/global.yml create mode 100644 .github/assign/gpu.yml create mode 100644 .github/assign/gui.yml create mode 100644 .github/assign/horizon.yml create mode 100644 .github/assign/infra.yml create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/pr_triage.yml diff --git a/.github/assign/audio.yml b/.github/assign/audio.yml new file mode 100644 index 000000000..337007d39 --- /dev/null +++ b/.github/assign/audio.yml @@ -0,0 +1,8 @@ +addReviewers: true + +reviewers: + - marysaka + +filterLabels: + include: + - audio \ No newline at end of file diff --git a/.github/assign/cpu.yml b/.github/assign/cpu.yml new file mode 100644 index 000000000..da824bdc3 --- /dev/null +++ b/.github/assign/cpu.yml @@ -0,0 +1,11 @@ +addReviewers: true + +reviewers: + - gdkchan + - riperiperi + - marysaka + - LDj3SNuD + +filterLabels: + include: + - cpu \ No newline at end of file diff --git a/.github/assign/global.yml b/.github/assign/global.yml new file mode 100644 index 000000000..afd5ce445 --- /dev/null +++ b/.github/assign/global.yml @@ -0,0 +1,4 @@ +addReviewers: true + +reviewers: + - Developers \ No newline at end of file diff --git a/.github/assign/gpu.yml b/.github/assign/gpu.yml new file mode 100644 index 000000000..b96d9d87d --- /dev/null +++ b/.github/assign/gpu.yml @@ -0,0 +1,10 @@ +addReviewers: true + +reviewers: + - gdkchan + - riperiperi + - marysaka + +filterLabels: + include: + - gpu \ No newline at end of file diff --git a/.github/assign/gui.yml b/.github/assign/gui.yml new file mode 100644 index 000000000..9731ea5bd --- /dev/null +++ b/.github/assign/gui.yml @@ -0,0 +1,11 @@ +addReviewers: true + +reviewers: + - Ack77 + - emmauss + - TSRBerry + - marysaka + +filterLabels: + include: + - gui \ No newline at end of file diff --git a/.github/assign/horizon.yml b/.github/assign/horizon.yml new file mode 100644 index 000000000..966382b29 --- /dev/null +++ b/.github/assign/horizon.yml @@ -0,0 +1,11 @@ +addReviewers: true + +reviewers: + - gdkchan + - Ack77 + - marysaka + - TSRBerry + +filterLabels: + include: + - horizon \ No newline at end of file diff --git a/.github/assign/infra.yml b/.github/assign/infra.yml new file mode 100644 index 000000000..d319fef19 --- /dev/null +++ b/.github/assign/infra.yml @@ -0,0 +1,9 @@ +addReviewers: true + +reviewers: + - marysaka + - TSRBerry + +filterLabels: + include: + - infra \ No newline at end of file diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..7b8ae302d --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,33 @@ +audio: 'src/Ryujinx.Audio*' + +cpu: + - 'src/ARMeilleure//*' + - 'src/Ryujinx.Cpu/*' + - 'src/Ryujinx.Memory/*' + +gpu: + - 'src/Ryujinx.Graphics.*' + - 'src/Spv.Generator/*' + - 'src/Ryujinx.ShaderTools/*' + +'graphics-backend:opengl': 'src/Ryujinx.Graphics.OpenGL/*' +'graphics-backend:vulkan': + - 'src/Ryujinx.Graphics.Vulkan/*' + - 'src/Spv.Generator/*' + +gui: + - 'src/Ryujinx/*' + - 'src/Ryujinx.Ui.Common/*' + - 'src/Ryujinx.Ui.LocaleGenerator/*' + - 'src/Ryujinx.Ava/*' + +horizon: + - 'src/Ryujinx.HLE/*' + - 'src/Ryujinx.Horizon*' + +kernel: 'src/Ryujinx.HLE/HOS/Kernel/*' + +infra: + - '.github/*' + - 'distribution/*' + - 'Directory.Packages.props' diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml new file mode 100644 index 000000000..32a88480b --- /dev/null +++ b/.github/workflows/pr_triage.yml @@ -0,0 +1,51 @@ +name: "Pull Request Triage" +on: +- pull_request_target + +jobs: + triage: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Update labels based on changes + uses: actions/labeler@v4 + with: + sync-labels: true + dot: true + + - uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/audio.yml' + if: github.event.action == 'opened' + + - uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/cpu.yml' + if: github.event.action == 'opened' + + - uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/gpu.yml' + if: github.event.action == 'opened' + + - uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/gui.yml' + if: github.event.action == 'opened' + + - uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/horizon.yml' + if: github.event.action == 'opened' + + - uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/infra.yml' + if: github.event.action == 'opened' + + - uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/global.yml' + if: github.event.action == 'opened' From 915a0f7173299c0b8a2c285f4e90b34cdae80811 Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Mon, 12 Jun 2023 08:33:13 -0700 Subject: [PATCH 628/737] hle: Stub IHidbusServer.GetBusHandle (#5284) --- .../HOS/Services/Hid/IHidbusServer.cs | 23 ++++++++++++++++++- .../HOS/Services/Hid/Types/Npad/BusHandle.cs | 14 +++++++++++ .../HOS/Services/Hid/Types/Npad/BusType.cs | 9 ++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs index bfd1d4dcd..eec5292f5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs @@ -1,8 +1,29 @@ -namespace Ryujinx.HLE.HOS.Services.Hid +using Ryujinx.Common; +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Hid { [Service("hidbus")] class IHidbusServer : IpcService { public IHidbusServer(ServiceCtx context) { } + + [CommandCmif(1)] + // GetBusHandle(nn::hid::NpadIdType, nn::hidbus::BusType, nn::applet::AppletResourceUserId) -> (bool HasHandle, nn::hidbus::BusHandle) + public ResultCode GetBusHandle(ServiceCtx context) + { + NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); + context.RequestData.BaseStream.Position += 4; // Padding + BusType busType = (BusType)context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); + + context.ResponseData.Write(false); + context.ResponseData.BaseStream.Position += 7; // Padding + context.ResponseData.WriteStruct(new BusHandle()); + + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { npadIdType, busType, appletResourceUserId }); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs new file mode 100644 index 000000000..936ee68c4 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Hid +{ + [StructLayout(LayoutKind.Sequential)] + struct BusHandle + { + public int AbstractedPadId; + public byte InternalIndex; + public byte PlayerNumber; + public byte BusTypeId; + public byte IsValid; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs new file mode 100644 index 000000000..41852365b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public enum BusType : long + { + LeftJoyRail = 0, + RightJoyRail = 1, + InternalBus = 2 + } +} From 5a02433080f194407a891283c1b12275944a3d9f Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 12 Jun 2023 20:42:27 +0200 Subject: [PATCH 629/737] infra: Fix PR triage workflow glob patterns (#5297) * Use glob patterns to match file paths * Update ignored paths for releases * Adjust build.yml as well * Add names to auto-assign steps * Fix developer team name * Allow build workflows to run if workflows changed --- .github/assign/global.yml | 2 +- .github/labeler.yml | 40 ++++++++++++++++----------------- .github/workflows/build.yml | 19 ++++++---------- .github/workflows/pr_triage.yml | 25 +++++++++++++-------- .github/workflows/release.yml | 5 +++-- 5 files changed, 47 insertions(+), 44 deletions(-) diff --git a/.github/assign/global.yml b/.github/assign/global.yml index afd5ce445..53a9af429 100644 --- a/.github/assign/global.yml +++ b/.github/assign/global.yml @@ -1,4 +1,4 @@ addReviewers: true reviewers: - - Developers \ No newline at end of file + - Ryujinx/developers \ No newline at end of file diff --git a/.github/labeler.yml b/.github/labeler.yml index 7b8ae302d..587830be1 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,33 +1,33 @@ -audio: 'src/Ryujinx.Audio*' +audio: 'src/Ryujinx.Audio*/**' cpu: - - 'src/ARMeilleure//*' - - 'src/Ryujinx.Cpu/*' - - 'src/Ryujinx.Memory/*' + - 'src/ARMeilleure/**' + - 'src/Ryujinx.Cpu/**' + - 'src/Ryujinx.Memory/**' gpu: - - 'src/Ryujinx.Graphics.*' - - 'src/Spv.Generator/*' - - 'src/Ryujinx.ShaderTools/*' + - 'src/Ryujinx.Graphics.*/**' + - 'src/Spv.Generator/**' + - 'src/Ryujinx.ShaderTools/**' -'graphics-backend:opengl': 'src/Ryujinx.Graphics.OpenGL/*' +'graphics-backend:opengl': 'src/Ryujinx.Graphics.OpenGL/**' 'graphics-backend:vulkan': - - 'src/Ryujinx.Graphics.Vulkan/*' - - 'src/Spv.Generator/*' + - 'src/Ryujinx.Graphics.Vulkan/**' + - 'src/Spv.Generator/**' gui: - - 'src/Ryujinx/*' - - 'src/Ryujinx.Ui.Common/*' - - 'src/Ryujinx.Ui.LocaleGenerator/*' - - 'src/Ryujinx.Ava/*' + - 'src/Ryujinx/**' + - 'src/Ryujinx.Ui.Common/**' + - 'src/Ryujinx.Ui.LocaleGenerator/**' + - 'src/Ryujinx.Ava/**' horizon: - - 'src/Ryujinx.HLE/*' - - 'src/Ryujinx.Horizon*' + - 'src/Ryujinx.HLE/**' + - 'src/Ryujinx.Horizon*/**' -kernel: 'src/Ryujinx.HLE/HOS/Kernel/*' +kernel: 'src/Ryujinx.HLE/HOS/Kernel/**' infra: - - '.github/*' - - 'distribution/*' - - 'Directory.Packages.props' + - '.github/**' + - 'distribution/**' + - 'Directory.Packages.props' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97db387f0..886bb0444 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,20 +3,15 @@ name: Build job on: workflow_dispatch: inputs: {} - #push: - # branches: [ master ] - # paths-ignore: - # - '.github/*' - # - '.github/ISSUE_TEMPLATE/**' - # - '*.yml' - # - 'README.md' pull_request: branches: [ master ] - paths-ignore: - - '.github/*' - - '.github/ISSUE_TEMPLATE/**' - - '*.yml' - - 'README.md' + paths: + - '!.github/**' + - '!*.yml' + - '!*.json' + - '!*.config' + - '!README.md' + - '.github/workflows/*.yml' concurrency: group: pr-checks-${{ github.event.number }} diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml index 32a88480b..8a27e3c95 100644 --- a/.github/workflows/pr_triage.yml +++ b/.github/workflows/pr_triage.yml @@ -1,6 +1,6 @@ name: "Pull Request Triage" on: -- pull_request_target + pull_request_target: jobs: triage: @@ -15,37 +15,44 @@ jobs: sync-labels: true dot: true - - uses: kentaro-m/auto-assign-action@v1.2.5 + - name: Auto Assign [Audio] + uses: kentaro-m/auto-assign-action@v1 with: configuration-path: '.github/assign/audio.yml' if: github.event.action == 'opened' - - uses: kentaro-m/auto-assign-action@v1.2.5 + - name: Auto Assign [CPU] + uses: kentaro-m/auto-assign-action@v1 with: configuration-path: '.github/assign/cpu.yml' if: github.event.action == 'opened' - - uses: kentaro-m/auto-assign-action@v1.2.5 + - name: Auto Assign [GPU] + uses: kentaro-m/auto-assign-action@v1 with: configuration-path: '.github/assign/gpu.yml' if: github.event.action == 'opened' - - uses: kentaro-m/auto-assign-action@v1.2.5 + - name: Auto Assign [GUI] + uses: kentaro-m/auto-assign-action@v1 with: configuration-path: '.github/assign/gui.yml' if: github.event.action == 'opened' - - uses: kentaro-m/auto-assign-action@v1.2.5 + - name: Auto Assign [Horizon] + uses: kentaro-m/auto-assign-action@v1 with: configuration-path: '.github/assign/horizon.yml' if: github.event.action == 'opened' - - uses: kentaro-m/auto-assign-action@v1.2.5 + - name: Auto Assign [Infra] + uses: kentaro-m/auto-assign-action@v1 with: configuration-path: '.github/assign/infra.yml' if: github.event.action == 'opened' - - uses: kentaro-m/auto-assign-action@v1.2.5 + - name: Auto Assign [Global] + uses: kentaro-m/auto-assign-action@v1 with: configuration-path: '.github/assign/global.yml' - if: github.event.action == 'opened' + if: github.event.action == 'opened' \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 98ba34822..63e6d0187 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,9 +6,10 @@ on: push: branches: [ master ] paths-ignore: - - '.github/*' - - '.github/ISSUE_TEMPLATE/**' + - '.github/**' - '*.yml' + - '*.json' + - '*.config' - 'README.md' concurrency: release From 52aa4b6c22ce697f82ae59c6c6610eadf6bbeacf Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 12 Jun 2023 21:57:07 +0200 Subject: [PATCH 630/737] Fix action version (#5299) --- .github/workflows/pr_triage.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml index 8a27e3c95..27a00f101 100644 --- a/.github/workflows/pr_triage.yml +++ b/.github/workflows/pr_triage.yml @@ -16,43 +16,43 @@ jobs: dot: true - name: Auto Assign [Audio] - uses: kentaro-m/auto-assign-action@v1 + uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/audio.yml' if: github.event.action == 'opened' - name: Auto Assign [CPU] - uses: kentaro-m/auto-assign-action@v1 + uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/cpu.yml' if: github.event.action == 'opened' - name: Auto Assign [GPU] - uses: kentaro-m/auto-assign-action@v1 + uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/gpu.yml' if: github.event.action == 'opened' - name: Auto Assign [GUI] - uses: kentaro-m/auto-assign-action@v1 + uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/gui.yml' if: github.event.action == 'opened' - name: Auto Assign [Horizon] - uses: kentaro-m/auto-assign-action@v1 + uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/horizon.yml' if: github.event.action == 'opened' - name: Auto Assign [Infra] - uses: kentaro-m/auto-assign-action@v1 + uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/infra.yml' if: github.event.action == 'opened' - name: Auto Assign [Global] - uses: kentaro-m/auto-assign-action@v1 + uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/global.yml' if: github.event.action == 'opened' \ No newline at end of file From cf4c78b9c825f61ff3ee83eff88442a149ae01d9 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 12 Jun 2023 21:12:06 -0300 Subject: [PATCH 631/737] Make LM skip instead of crashing for invalid messages (#5290) --- src/Ryujinx.Common/Memory/SpanReader.cs | 18 ++++++++ .../LogManager/Ipc/LmLogger.cs | 45 +++++++++++++------ 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx.Common/Memory/SpanReader.cs b/src/Ryujinx.Common/Memory/SpanReader.cs index 673932d0b..9a1a0a3f0 100644 --- a/src/Ryujinx.Common/Memory/SpanReader.cs +++ b/src/Ryujinx.Common/Memory/SpanReader.cs @@ -24,6 +24,24 @@ namespace Ryujinx.Common.Memory return value; } + public bool TryRead<T>(out T value) where T : unmanaged + { + int valueSize = Unsafe.SizeOf<T>(); + + if (valueSize > _input.Length) + { + value = default; + + return false; + } + + value = MemoryMarshal.Cast<byte, T>(_input)[0]; + + _input = _input.Slice(valueSize); + + return true; + } + public ReadOnlySpan<byte> GetSpan(int size) { ReadOnlySpan<byte> data = _input.Slice(0, size); diff --git a/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs index e930bdd75..88dddef5e 100644 --- a/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs +++ b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Horizon.LogManager.Ipc private const int MessageLengthLimit = 5000; private readonly LogService _log; - private readonly ulong _pid; + private readonly ulong _pid; private LogPacket _logPacket; @@ -74,8 +74,12 @@ namespace Ryujinx.Horizon.LogManager.Ipc private bool LogImpl(ReadOnlySpan<byte> message) { - SpanReader reader = new(message); - LogPacketHeader header = reader.Read<LogPacketHeader>(); + SpanReader reader = new(message); + + if (!reader.TryRead(out LogPacketHeader header)) + { + return true; + } bool isHeadPacket = (header.Flags & LogPacketFlags.IsHead) != 0; bool isTailPacket = (header.Flags & LogPacketFlags.IsTail) != 0; @@ -84,8 +88,10 @@ namespace Ryujinx.Horizon.LogManager.Ipc while (reader.Length > 0) { - int type = ReadUleb128(ref reader); - int size = ReadUleb128(ref reader); + if (!TryReadUleb128(ref reader, out int type) || !TryReadUleb128(ref reader, out int size)) + { + return true; + } LogDataChunkKey key = (LogDataChunkKey)type; @@ -101,15 +107,24 @@ namespace Ryujinx.Horizon.LogManager.Ipc } else if (key == LogDataChunkKey.Line) { - _logPacket.Line = reader.Read<int>(); + if (!reader.TryRead<int>(out _logPacket.Line)) + { + return true; + } } else if (key == LogDataChunkKey.DropCount) { - _logPacket.DropCount = reader.Read<long>(); + if (!reader.TryRead<long>(out _logPacket.DropCount)) + { + return true; + } } else if (key == LogDataChunkKey.Time) { - _logPacket.Time = reader.Read<long>(); + if (!reader.TryRead<long>(out _logPacket.Time)) + { + return true; + } } else if (key == LogDataChunkKey.Message) { @@ -154,23 +169,25 @@ namespace Ryujinx.Horizon.LogManager.Ipc return isTailPacket; } - private static int ReadUleb128(ref SpanReader reader) + private static bool TryReadUleb128(ref SpanReader reader, out int result) { - int result = 0; - int count = 0; - + result = 0; + int count = 0; byte encoded; do { - encoded = reader.Read<byte>(); + if (!reader.TryRead<byte>(out encoded)) + { + return false; + } result += (encoded & 0x7F) << (7 * count); count++; } while ((encoded & 0x80) != 0); - return result; + return true; } } } \ No newline at end of file From 5bd2c58ad63ba42cb22d1525585efeacaef10c46 Mon Sep 17 00:00:00 2001 From: mmdurrant <mmdurrant@users.noreply.github.com> Date: Mon, 12 Jun 2023 18:36:40 -0600 Subject: [PATCH 632/737] UI: Correctly set 'shell/open/command; registry key for file associations (#5244) * Correctly set 'shell/open/command; registry key for file associations * File association fixes * 'using' statements instead of blocks * Idempotent unregistration * Single "hey shell, we changed file associations" notification at the end instead of 1 for every operation, speeds things up greatly. * Adapt and fix Linux specific function as well --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- .../Helper/FileAssociationHelper.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs index 4f4b25245..542db9a7e 100644 --- a/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs +++ b/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs @@ -11,10 +11,10 @@ namespace Ryujinx.Ui.Common.Helper { public static partial class FileAssociationHelper { - private static string[] _fileExtensions = new string[] { ".nca", ".nro", ".nso", ".nsp", ".xci" }; + private static readonly string[] _fileExtensions = { ".nca", ".nro", ".nso", ".nsp", ".xci" }; [SupportedOSPlatform("linux")] - private static string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); + private static readonly string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); private const int SHCNE_ASSOCCHANGED = 0x8000000; private const int SHCNF_FLUSH = 0x1000; @@ -32,7 +32,7 @@ namespace Ryujinx.Ui.Common.Helper { string installKeyword = uninstall ? "uninstall" : "install"; - if (!AreMimeTypesRegisteredLinux()) + if ((uninstall && AreMimeTypesRegisteredLinux()) || (!uninstall && !AreMimeTypesRegisteredLinux())) { string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml"); string additionalArgs = !uninstall ? "--novendor" : ""; @@ -81,9 +81,9 @@ namespace Ryujinx.Ui.Common.Helper return false; } - key.OpenSubKey(@"shell\open\command"); + var openCmd = key.OpenSubKey(@"shell\open\command"); - string keyValue = (string)key.GetValue(""); + string keyValue = (string)openCmd.GetValue(""); return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName)); } @@ -107,30 +107,31 @@ namespace Ryujinx.Ui.Common.Helper if (uninstall) { + // If the types don't already exist, there's nothing to do and we can call this operation successful. if (!AreMimeTypesRegisteredWindows()) { - return false; + return true; } - + Logger.Debug?.Print(LogClass.Application, $"Removing type association {ext}"); Registry.CurrentUser.DeleteSubKeyTree(keyString); + Logger.Debug?.Print(LogClass.Application, $"Removed type association {ext}"); } else { - RegistryKey key = Registry.CurrentUser.CreateSubKey(keyString); + using var key = Registry.CurrentUser.CreateSubKey(keyString); + if (key is null) { return false; } - key.CreateSubKey(@"shell\open\command"); + Logger.Debug?.Print(LogClass.Application, $"Adding type association {ext}"); + using var openCmd = key.CreateSubKey(@"shell\open\command"); + openCmd.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\""); + Logger.Debug?.Print(LogClass.Application, $"Added type association {ext}"); - key.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\""); - key.Close(); } - // Notify Explorer the file association has been changed. - SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero); - return true; } @@ -141,6 +142,9 @@ namespace Ryujinx.Ui.Common.Helper registered |= RegisterExtension(ext, uninstall); } + // Notify Explorer the file association has been changed. + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero); + return registered; } From 4a27d29412f68d3926da3e3fd1d1619914b1b5fc Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Tue, 13 Jun 2023 11:51:22 +0200 Subject: [PATCH 633/737] infra: Sync paths-ignore with release job and attempt to fix review assign --- .github/workflows/build.yml | 13 ++++++------- .github/workflows/pr_triage.yml | 12 ++++-------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 886bb0444..bf8fd000a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,13 +5,12 @@ on: inputs: {} pull_request: branches: [ master ] - paths: - - '!.github/**' - - '!*.yml' - - '!*.json' - - '!*.config' - - '!README.md' - - '.github/workflows/*.yml' + paths-ignore: + - '.github/**' + - '*.yml' + - '*.json' + - '*.config' + - 'README.md' concurrency: group: pr-checks-${{ github.event.number }} diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml index 27a00f101..e32dd27ad 100644 --- a/.github/workflows/pr_triage.yml +++ b/.github/workflows/pr_triage.yml @@ -1,13 +1,16 @@ name: "Pull Request Triage" on: pull_request_target: + types: [opened, ready_for_review] jobs: triage: permissions: contents: read pull-requests: write + runs-on: ubuntu-latest + steps: - name: Update labels based on changes uses: actions/labeler@v4 @@ -19,40 +22,33 @@ jobs: uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/audio.yml' - if: github.event.action == 'opened' - name: Auto Assign [CPU] uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/cpu.yml' - if: github.event.action == 'opened' - name: Auto Assign [GPU] uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/gpu.yml' - if: github.event.action == 'opened' - name: Auto Assign [GUI] uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/gui.yml' - if: github.event.action == 'opened' - name: Auto Assign [Horizon] uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/horizon.yml' - if: github.event.action == 'opened' - name: Auto Assign [Infra] uses: kentaro-m/auto-assign-action@v1.2.5 with: configuration-path: '.github/assign/infra.yml' - if: github.event.action == 'opened' - name: Auto Assign [Global] uses: kentaro-m/auto-assign-action@v1.2.5 with: - configuration-path: '.github/assign/global.yml' - if: github.event.action == 'opened' \ No newline at end of file + configuration-path: '.github/assign/global.yml' \ No newline at end of file From 4d804ed45e1c00b74714089e26f941e71a1c8c45 Mon Sep 17 00:00:00 2001 From: Kurochi51 <andrei5125@gmail.com> Date: Tue, 13 Jun 2023 21:47:33 +0300 Subject: [PATCH 634/737] Mod Loader: Stop loading mods from folders that don't exactly match titleId (#5298) * Stop loading mods from folders that don't exactly match titleId * What the worst that can happen? --- src/Ryujinx.HLE/HOS/ModLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index ac983cfe5..243510967 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS } private static DirectoryInfo FindTitleDir(DirectoryInfo contentsDir, string titleId) - => contentsDir.EnumerateDirectories($"{titleId}*", DirEnumOptions).FirstOrDefault(); + => contentsDir.EnumerateDirectories(titleId, DirEnumOptions).FirstOrDefault(); private static void AddModsFromDirectory(ModCache mods, DirectoryInfo dir, string titleId) { From 105c9712c1cf8400b3ff34c3a69a8af81ee4431e Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Wed, 14 Jun 2023 00:57:02 -0300 Subject: [PATCH 635/737] Fix Arm32 double to int/uint conversion on Arm64 (#5292) * Fix Arm32 double to int/uint conversion on Arm64 * PPTC version bump --- .../Instructions/InstEmitSimdCvt32.cs | 74 ++++++++++++++----- .../Instructions/InstEmitSimdHelper32Arm64.cs | 12 ++- src/ARMeilleure/Translation/PTC/Ptc.cs | 2 +- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index 33ae83df6..bec36e2d6 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -165,7 +165,7 @@ namespace ARMeilleure.Instructions { Operand m = GetVecA32(op.Vm >> 1); - Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, doubleSize); + Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, true); Intrinsic inst = (unsigned ? Intrinsic.Arm64FcvtzuGp : Intrinsic.Arm64FcvtzsGp) | Intrinsic.Arm64VDouble; @@ -175,7 +175,7 @@ namespace ARMeilleure.Instructions } else { - InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuS : Intrinsic.Arm64FcvtzsS); + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuS : Intrinsic.Arm64FcvtzsS, false); } } else if (!roundWithFpscr && Optimizations.UseSse41) @@ -260,28 +260,64 @@ namespace ARMeilleure.Instructions if (Optimizations.UseAdvSimd) { - if (unsigned) + bool doubleSize = floatSize == OperandType.FP64; + + if (doubleSize) { - inst = rm switch { - 0b00 => Intrinsic.Arm64FcvtauS, - 0b01 => Intrinsic.Arm64FcvtnuS, - 0b10 => Intrinsic.Arm64FcvtpuS, - 0b11 => Intrinsic.Arm64FcvtmuS, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) - }; + Operand m = GetVecA32(op.Vm >> 1); + + Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, true); + + if (unsigned) + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtauGp, + 0b01 => Intrinsic.Arm64FcvtnuGp, + 0b10 => Intrinsic.Arm64FcvtpuGp, + 0b11 => Intrinsic.Arm64FcvtmuGp, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + else + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtasGp, + 0b01 => Intrinsic.Arm64FcvtnsGp, + 0b10 => Intrinsic.Arm64FcvtpsGp, + 0b11 => Intrinsic.Arm64FcvtmsGp, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + + Operand asInteger = context.AddIntrinsicInt(inst | Intrinsic.Arm64VDouble, toConvert); + + InsertScalar(context, op.Vd, asInteger); } else { - inst = rm switch { - 0b00 => Intrinsic.Arm64FcvtasS, - 0b01 => Intrinsic.Arm64FcvtnsS, - 0b10 => Intrinsic.Arm64FcvtpsS, - 0b11 => Intrinsic.Arm64FcvtmsS, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) - }; - } + if (unsigned) + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtauS, + 0b01 => Intrinsic.Arm64FcvtnuS, + 0b10 => Intrinsic.Arm64FcvtpuS, + 0b11 => Intrinsic.Arm64FcvtmuS, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + else + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtasS, + 0b01 => Intrinsic.Arm64FcvtnsS, + 0b10 => Intrinsic.Arm64FcvtpsS, + 0b11 => Intrinsic.Arm64FcvtmsS, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } - InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst); + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst); + } } else if (Optimizations.UseSse41) { diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs index 98236be6d..804d915c4 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs @@ -192,11 +192,10 @@ namespace ARMeilleure.Instructions EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(inst, d, n, m)); } - public static void EmitScalarUnaryOpSimd32(ArmEmitterContext context, Func1I scalarFunc) + public static void EmitScalarUnaryOpSimd32(ArmEmitterContext context, Func1I scalarFunc, bool doubleSize) { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; - bool doubleSize = (op.Size & 1) != 0; int shift = doubleSize ? 1 : 2; Operand m = GetVecA32(op.Vm >> shift); Operand d = GetVecA32(op.Vd >> shift); @@ -215,8 +214,13 @@ namespace ARMeilleure.Instructions { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; - inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; - EmitScalarUnaryOpSimd32(context, (m) => (inst == 0) ? m : context.AddIntrinsic(inst, m)); + EmitScalarUnaryOpF32(context, inst, (op.Size & 1) != 0); + } + + public static void EmitScalarUnaryOpF32(ArmEmitterContext context, Intrinsic inst, bool doubleSize) + { + inst |= (doubleSize ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitScalarUnaryOpSimd32(context, (m) => (inst == 0) ? m : context.AddIntrinsic(inst, m), doubleSize); } public static void EmitScalarBinaryOpSimd32(ArmEmitterContext context, Func2I scalarFunc) diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 366dea6bf..3c697bffe 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 5281; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5292; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; From 6f28c4abadfead6fb5146caa5775dba1641bd79f Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 14 Jun 2023 18:02:41 +0200 Subject: [PATCH 636/737] test: Make tests runnable on system without 4KiB page size (#5184) * ARMeilleure: Do not hardcode 4KiB page size in JitCache * test: Do not hardcode page size to 4KiB for Ryujinx.Tests.Memory.Tests Fix running tests on Asahi Linux with 16KiB pages. * test: Do not hardcode page size to 4KiB for Ryujinx.Tests.Cpu Fix running tests on Asahi Linux. Test runner still crash when trying to run all test suite. * test: Do not hardcode page size to 4KiB for Ryujinx.Tests.Cpu Fix somecrashes on Asahi Linux. * test: Ignore Vshl test on ARM64 due to unicorn crashes * test: Workaround hardcoded size on some tests Change mapping of code and data in case of non 4KiB configuration. * test: Make CpuTestT32Flow depends on code address Fix failure with different page size. * test: Disable CpuTestThumb.TestRandomTestCases when page size isn't 4KiB The test data needs to be reevaluated to take different page size into account. * Address gdkchan's comments --- src/ARMeilleure/Translation/Cache/JitCache.cs | 5 +-- src/Ryujinx.Tests.Memory/Tests.cs | 35 +++++++++++-------- src/Ryujinx.Tests/Cpu/CpuTest.cs | 24 +++++++++---- src/Ryujinx.Tests/Cpu/CpuTest32.cs | 24 +++++++++---- src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs | 34 +++++++++--------- src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs | 6 ++++ src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs | 6 ++-- src/Ryujinx.Tests/Cpu/CpuTestThumb.cs | 6 ++++ 8 files changed, 93 insertions(+), 47 deletions(-) diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index daa2eeac2..aa732d0ae 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -2,6 +2,7 @@ using ARMeilleure.CodeGen; using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.Memory; using ARMeilleure.Native; +using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Diagnostics; @@ -12,8 +13,8 @@ namespace ARMeilleure.Translation.Cache { static partial class JitCache { - private const int PageSize = 4 * 1024; - private const int PageMask = PageSize - 1; + private static readonly int PageSize = (int)MemoryBlock.GetPageSize(); + private static readonly int PageMask = PageSize - 1; private const int CodeAlignment = 4; // Bytes. private const int CacheSize = 2047 * 1024 * 1024; diff --git a/src/Ryujinx.Tests.Memory/Tests.cs b/src/Ryujinx.Tests.Memory/Tests.cs index d8a243e3e..5ab01d5a5 100644 --- a/src/Ryujinx.Tests.Memory/Tests.cs +++ b/src/Ryujinx.Tests.Memory/Tests.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Tests.Memory { public class Tests { - private const ulong MemorySize = 0x8000; + private static readonly ulong MemorySize = MemoryBlock.GetPageSize() * 8; private MemoryBlock _memoryBlock; @@ -44,14 +44,17 @@ namespace Ryujinx.Tests.Memory [Platform(Exclude = "MacOsX")] public void Test_Alias() { - using MemoryBlock backing = new MemoryBlock(0x10000, MemoryAllocationFlags.Mirrorable); - using MemoryBlock toAlias = new MemoryBlock(0x10000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + ulong pageSize = MemoryBlock.GetPageSize(); + ulong blockSize = MemoryBlock.GetPageSize() * 16; - toAlias.MapView(backing, 0x1000, 0, 0x4000); - toAlias.UnmapView(backing, 0x3000, 0x1000); + using MemoryBlock backing = new MemoryBlock(blockSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new MemoryBlock(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + + toAlias.MapView(backing, pageSize, 0, pageSize * 4); + toAlias.UnmapView(backing, pageSize * 3, pageSize); toAlias.Write(0, 0xbadc0de); - Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, 0x1000), 0xbadc0de); + Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, (int)pageSize), 0xbadc0de); } [Test] @@ -59,8 +62,12 @@ namespace Ryujinx.Tests.Memory [Platform(Exclude = "MacOsX")] public void Test_AliasRandom() { - using MemoryBlock backing = new MemoryBlock(0x80000, MemoryAllocationFlags.Mirrorable); - using MemoryBlock toAlias = new MemoryBlock(0x80000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + ulong pageSize = MemoryBlock.GetPageSize(); + int pageBits = (int)ulong.Log2(pageSize); + ulong blockSize = MemoryBlock.GetPageSize() * 128; + + using MemoryBlock backing = new MemoryBlock(blockSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new MemoryBlock(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); Random rng = new Random(123); @@ -72,16 +79,16 @@ namespace Ryujinx.Tests.Memory if ((rng.Next() & 1) != 0) { - toAlias.MapView(backing, (ulong)srcPage << 12, (ulong)dstPage << 12, (ulong)pages << 12); + toAlias.MapView(backing, (ulong)srcPage << pageBits, (ulong)dstPage << pageBits, (ulong)pages << pageBits); - int offset = rng.Next(0, 0x1000 - sizeof(int)); + int offset = rng.Next(0, (int)pageSize - sizeof(int)); - toAlias.Write((ulong)((dstPage << 12) + offset), 0xbadc0de); - Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, (srcPage << 12) + offset), 0xbadc0de); + toAlias.Write((ulong)((dstPage << pageBits) + offset), 0xbadc0de); + Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, (srcPage << pageBits) + offset), 0xbadc0de); } else { - toAlias.UnmapView(backing, (ulong)dstPage << 12, (ulong)pages << 12); + toAlias.UnmapView(backing, (ulong)dstPage << pageBits, (ulong)pages << pageBits); } } } @@ -91,7 +98,7 @@ namespace Ryujinx.Tests.Memory [Platform(Exclude = "MacOsX")] public void Test_AliasMapLeak() { - ulong pageSize = 4096; + ulong pageSize = MemoryBlock.GetPageSize(); ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that. using MemoryBlock backing = new MemoryBlock(pageSize, MemoryAllocationFlags.Mirrorable); diff --git a/src/Ryujinx.Tests/Cpu/CpuTest.cs b/src/Ryujinx.Tests/Cpu/CpuTest.cs index 979b313b0..ad4ba539b 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTest.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Tests.Cpu [TestFixture] public class CpuTest { - protected const ulong Size = 0x1000; - protected const ulong CodeBaseAddress = 0x1000; - protected const ulong DataBaseAddress = CodeBaseAddress + Size; + protected static readonly ulong Size = MemoryBlock.GetPageSize(); + protected static ulong CodeBaseAddress = Size; + protected static ulong DataBaseAddress = CodeBaseAddress + Size; private static bool Ignore_FpcrFz = false; private static bool Ignore_FpcrDn = false; @@ -39,12 +39,24 @@ namespace Ryujinx.Tests.Cpu [SetUp] public void Setup() { - _currAddress = CodeBaseAddress; + int pageBits = (int)ulong.Log2(Size); _ram = new MemoryBlock(Size * 2); - _memory = new MemoryManager(_ram, 1ul << 16); + _memory = new MemoryManager(_ram, 1ul << (pageBits + 4)); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, 0, Size * 2, MemoryMapFlags.Private); + + // Some tests depends on hardcoded address that were computed for 4KiB. + // We change the layout on non 4KiB platforms to keep compat here. + if (Size > 0x1000) + { + DataBaseAddress = 0; + CodeBaseAddress = Size; + } + + _currAddress = CodeBaseAddress; + + _memory.Map(CodeBaseAddress, 0, Size, MemoryMapFlags.Private); + _memory.Map(DataBaseAddress, Size, Size, MemoryMapFlags.Private); _context = CpuContext.CreateExecutionContext(); Translator.IsReadyForTranslation.Set(); diff --git a/src/Ryujinx.Tests/Cpu/CpuTest32.cs b/src/Ryujinx.Tests/Cpu/CpuTest32.cs index 47dc9f8a8..a1f6431cf 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Tests.Cpu [TestFixture] public class CpuTest32 { - protected const uint Size = 0x1000; - protected const uint CodeBaseAddress = 0x1000; - protected const uint DataBaseAddress = CodeBaseAddress + Size; + protected static readonly uint Size = (uint)MemoryBlock.GetPageSize(); + protected static uint CodeBaseAddress = Size; + protected static uint DataBaseAddress = CodeBaseAddress + Size; private uint _currAddress; @@ -33,12 +33,24 @@ namespace Ryujinx.Tests.Cpu [SetUp] public void Setup() { - _currAddress = CodeBaseAddress; + int pageBits = (int)ulong.Log2(Size); _ram = new MemoryBlock(Size * 2); - _memory = new MemoryManager(_ram, 1ul << 16); + _memory = new MemoryManager(_ram, 1ul << (pageBits + 4)); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, 0, Size * 2, MemoryMapFlags.Private); + + // Some tests depends on hardcoded address that were computed for 4KiB. + // We change the layout on non 4KiB platforms to keep compat here. + if (Size > 0x1000) + { + DataBaseAddress = 0; + CodeBaseAddress = Size; + } + + _currAddress = CodeBaseAddress; + + _memory.Map(CodeBaseAddress, 0, Size, MemoryMapFlags.Private); + _memory.Map(DataBaseAddress, Size, Size, MemoryMapFlags.Private); _context = CpuContext.CreateExecutionContext(); _context.IsAarch32 = true; diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs index 2f9504cbf..c88c02c1b 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs @@ -1,6 +1,7 @@ #define SimdMemory32 using ARMeilleure.State; +using Ryujinx.Memory; using NUnit.Framework; using System; @@ -9,6 +10,7 @@ namespace Ryujinx.Tests.Cpu [Category("SimdMemory32")] public sealed class CpuTestSimdMemory32 : CpuTest32 { + private static readonly uint TestOffset = DataBaseAddress + 0x500; #if SimdMemory32 private uint[] _ldStModes = @@ -42,7 +44,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 3u)] uint n, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4a00000u; // VLD1.8 {D0[0]}, [R0], R0 @@ -58,7 +60,7 @@ namespace Ryujinx.Tests.Cpu opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. - SingleOpcode(opcode, r0: 0x2500, r1: offset, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -72,7 +74,7 @@ namespace Ryujinx.Tests.Cpu [Values] bool t, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4a00c00u; // VLD1.8 {D0[0]}, [R0], R0 @@ -85,7 +87,7 @@ namespace Ryujinx.Tests.Cpu opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. if (t) opcode |= 1 << 5; - SingleOpcode(opcode, r0: 0x2500, r1: offset, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -98,7 +100,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 10u)] uint mode, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4200000u; // VLD4.8 {D0, D1, D2, D3}, [R0], R0 @@ -114,7 +116,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); - SingleOpcode(opcode, r0: 0x2500, r1: offset, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -128,7 +130,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 3u)] uint n, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); (V128 vec1, V128 vec2, V128 vec3, V128 vec4) = GenerateTestVectors(); @@ -146,7 +148,7 @@ namespace Ryujinx.Tests.Cpu opcode |= (n & 3) << 8; // ST1 is 0, ST2 is 1 etc. - SingleOpcode(opcode, r0: 0x2500, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); CompareAgainstUnicorn(); } @@ -159,7 +161,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 10u)] uint mode, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); (V128 vec1, V128 vec2, V128 vec3, V128 vec4) = GenerateTestVectors(); @@ -177,7 +179,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); - SingleOpcode(opcode, r0: 0x2500, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); CompareAgainstUnicorn(); } @@ -189,7 +191,7 @@ namespace Ryujinx.Tests.Cpu [Values(0x1u, 0x32u)] uint regs, [Values] bool single) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xec100a00u; // VST4.8 {D0, D1, D2, D3}, [R0], R0 @@ -225,7 +227,7 @@ namespace Ryujinx.Tests.Cpu opcode |= regs & 0xff; - SingleOpcode(opcode, r0: 0x2500, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -237,7 +239,7 @@ namespace Ryujinx.Tests.Cpu [Values(0x0u)] uint imm, [Values] bool sub) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xed900a00u; // VLDR.32 S0, [R0, #0] @@ -260,7 +262,7 @@ namespace Ryujinx.Tests.Cpu } opcode |= imm & 0xff; - SingleOpcode(opcode, r0: 0x2500); + SingleOpcode(opcode, r0: TestOffset); CompareAgainstUnicorn(); } @@ -272,7 +274,7 @@ namespace Ryujinx.Tests.Cpu [Values(0x0u)] uint imm, [Values] bool sub) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xed800a00u; // VSTR.32 S0, [R0, #0] @@ -297,7 +299,7 @@ namespace Ryujinx.Tests.Cpu (V128 vec1, V128 vec2, _, _) = GenerateTestVectors(); - SingleOpcode(opcode, r0: 0x2500, v0: vec1, v1: vec2); + SingleOpcode(opcode, r0: TestOffset, v0: vec1, v1: vec2); CompareAgainstUnicorn(); } diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs index b19137a4b..603e2a559 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs @@ -3,6 +3,7 @@ using ARMeilleure.State; using NUnit.Framework; using System.Collections.Generic; +using System.Runtime.InteropServices; namespace Ryujinx.Tests.Cpu { @@ -703,6 +704,11 @@ namespace Ryujinx.Tests.Cpu [Values] bool q, [Values] bool u) { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + Assert.Ignore("Unicorn on ARM64 crash while executing this test"); + } + uint opcode = 0xf2000400u; // VSHL.S8 D0, D0, D0 if (q) { diff --git a/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs index 2c83b01d8..03d90a1f1 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs @@ -109,7 +109,7 @@ namespace Ryujinx.Tests.Cpu ExecuteOpcodes(runUnicorn: false); - Assert.That(GetContext().GetX(0), Is.EqualTo(0x1005)); + Assert.That(GetContext().GetX(0), Is.EqualTo(CodeBaseAddress + 0x5)); } [Test] @@ -133,7 +133,7 @@ namespace Ryujinx.Tests.Cpu ExecuteOpcodes(runUnicorn: false); - Assert.That(GetContext().GetX(0), Is.EqualTo(0x1005)); + Assert.That(GetContext().GetX(0), Is.EqualTo(CodeBaseAddress + 0x5)); Assert.That(GetContext().GetPstateFlag(PState.TFlag), Is.EqualTo(false)); } @@ -160,7 +160,7 @@ namespace Ryujinx.Tests.Cpu ExecuteOpcodes(runUnicorn: false); - Assert.That(GetContext().GetX(0), Is.EqualTo(0x1007)); + Assert.That(GetContext().GetX(0), Is.EqualTo(CodeBaseAddress + 0x7)); Assert.That(GetContext().GetPstateFlag(PState.TFlag), Is.EqualTo(false)); } } diff --git a/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs b/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs index b740f524a..3d13ff733 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs @@ -268,6 +268,12 @@ namespace Ryujinx.Tests.Cpu [Test] public void TestRandomTestCases([ValueSource(nameof(RandomTestCases))] PrecomputedThumbTestCase test) { + if (Size != 0x1000) + { + // TODO: Change it to depend on DataBaseAddress instead. + Assert.Ignore("This test currently only support 4KiB page size"); + } + RunPrecomputedTestCase(test); } From f978d3726a87dcc067abb2541b96b831e4390fbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:21:17 +0200 Subject: [PATCH 637/737] nuget: bump System.Management from 7.0.1 to 7.0.2 (#5302) Bumps [System.Management](https://github.com/dotnet/runtime) from 7.0.1 to 7.0.2. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v7.0.1...v7.0.2) --- updated-dependencies: - dependency-name: System.Management dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 9a1118844..a100f8261 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -46,7 +46,7 @@ <PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> - <PackageVersion Include="System.Management" Version="7.0.1" /> + <PackageVersion Include="System.Management" Version="7.0.2" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" /> </ItemGroup> From 82f90704a0662bba7254cb0bc262d785acdabc67 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Wed, 14 Jun 2023 21:34:55 -0300 Subject: [PATCH 638/737] Blocks should be synchronized on read-only fields (#5212) * Blocks should be synchronized on read-only fields * more readonlys * fix alignment * more * Update ISelfController.cs * simplify new * simplify new --- src/ARMeilleure/Signal/NativeSignalHandler.cs | 2 +- src/ARMeilleure/Translation/Cache/JitCache.cs | 2 +- .../OpenALHardwareDeviceSession.cs | 2 +- src/Ryujinx.Audio/AudioManager.cs | 2 +- src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs | 2 +- src/Ryujinx.Audio/Input/AudioInputManager.cs | 4 ++-- src/Ryujinx.Audio/Input/AudioInputSystem.cs | 2 +- src/Ryujinx.Audio/Output/AudioOutputManager.cs | 4 ++-- src/Ryujinx.Audio/Output/AudioOutputSystem.cs | 2 +- src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs | 2 +- src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs | 4 ++-- .../Renderer/Server/Upsampler/UpsamplerManager.cs | 2 +- .../Multithreading/ThreadedRenderer.cs | 2 +- .../Memory/BufferModifiedRangeList.cs | 4 ++-- src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs | 2 +- src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs | 2 +- src/Ryujinx.Graphics.OpenGL/ResourcePool.cs | 2 +- src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs | 2 +- src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs | 2 +- .../Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs | 2 +- .../Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs | 2 +- .../SoftwareKeyboard/SoftwareKeyboardRendererBase.cs | 4 ++-- .../HOS/Applets/SoftwareKeyboard/TimedAction.cs | 4 ++-- src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 7 ++----- src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | 4 +--- .../SystemAppletProxy/ISelfController.cs | 6 +++--- .../Friend/ServiceCreator/INotificationService.cs | 2 +- .../Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs | 2 +- .../Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs | 2 +- .../HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs | 2 +- .../HOS/Services/Ssl/BuiltInCertificateManager.cs | 8 ++++---- .../HOS/Services/SurfaceFlinger/BufferQueueCore.cs | 2 +- .../HOS/Services/SurfaceFlinger/BufferQueueProducer.cs | 2 +- .../HOS/Services/SurfaceFlinger/ConsumerBase.cs | 4 ++-- .../HOS/Services/SurfaceFlinger/SurfaceFlinger.cs | 2 +- .../HOS/Services/Time/TimeZone/TimeZoneManager.cs | 3 +-- src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs | 4 +--- src/Ryujinx.Input.SDL2/SDL2Gamepad.cs | 4 ++-- src/Ryujinx.Input.SDL2/SDL2Keyboard.cs | 2 +- src/Ryujinx.Input/HLE/NpadManager.cs | 4 ++-- src/Ryujinx.Memory/Tracking/RegionHandle.cs | 2 +- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 2 +- src/Ryujinx/Input/GTK3/GTK3Keyboard.cs | 2 +- 45 files changed, 60 insertions(+), 68 deletions(-) diff --git a/src/ARMeilleure/Signal/NativeSignalHandler.cs b/src/ARMeilleure/Signal/NativeSignalHandler.cs index cddeb8174..5da0c7726 100644 --- a/src/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/src/ARMeilleure/Signal/NativeSignalHandler.cs @@ -78,7 +78,7 @@ namespace ARMeilleure.Signal private static IntPtr _signalHandlerPtr; private static IntPtr _signalHandlerHandle; - private static readonly object _lock = new object(); + private static readonly object _lock = new(); private static bool _initialized; static NativeSignalHandler() diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index aa732d0ae..03cba5ade 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -26,7 +26,7 @@ namespace ARMeilleure.Translation.Cache private static readonly List<CacheEntry> _cacheEntries = new List<CacheEntry>(); - private static readonly object _lock = new object(); + private static readonly object _lock = new(); private static bool _initialized; [SupportedOSPlatform("windows")] diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs index ac3319e0d..8d7d0d6a2 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Backends.OpenAL private Queue<OpenALAudioBuffer> _queuedBuffers; private ulong _playedSampleCount; - private object _lock = new object(); + private readonly object _lock = new(); public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) { diff --git a/src/Ryujinx.Audio/AudioManager.cs b/src/Ryujinx.Audio/AudioManager.cs index c37ca4a3d..9f2a05b09 100644 --- a/src/Ryujinx.Audio/AudioManager.cs +++ b/src/Ryujinx.Audio/AudioManager.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio /// <summary> /// Lock used to control the waiters registration. /// </summary> - private object _lock = new object(); + private readonly object _lock = new(); /// <summary> /// Events signaled when the driver played audio buffers. diff --git a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs index 9bf20d4b5..d17303cd3 100644 --- a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs +++ b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Audio.Backends.Common { private const int RingBufferAlignment = 2048; - private object _lock = new object(); + private readonly object _lock = new(); private byte[] _buffer; private int _size; diff --git a/src/Ryujinx.Audio/Input/AudioInputManager.cs b/src/Ryujinx.Audio/Input/AudioInputManager.cs index ac012c4a9..63cbe031f 100644 --- a/src/Ryujinx.Audio/Input/AudioInputManager.cs +++ b/src/Ryujinx.Audio/Input/AudioInputManager.cs @@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Input /// </summary> public class AudioInputManager : IDisposable { - private object _lock = new object(); + private readonly object _lock = new(); /// <summary> /// Lock used for session allocation. /// </summary> - private object _sessionLock = new object(); + private readonly object _sessionLock = new(); /// <summary> /// The session ids allocation table. diff --git a/src/Ryujinx.Audio/Input/AudioInputSystem.cs b/src/Ryujinx.Audio/Input/AudioInputSystem.cs index b3ca0fd68..33364e28a 100644 --- a/src/Ryujinx.Audio/Input/AudioInputSystem.cs +++ b/src/Ryujinx.Audio/Input/AudioInputSystem.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Input /// <summary> /// The lock of the parent. /// </summary> - private object _parentLock; + private readonly object _parentLock; /// <summary> /// The dispose state. diff --git a/src/Ryujinx.Audio/Output/AudioOutputManager.cs b/src/Ryujinx.Audio/Output/AudioOutputManager.cs index 8c21f76a1..bc2fc6f43 100644 --- a/src/Ryujinx.Audio/Output/AudioOutputManager.cs +++ b/src/Ryujinx.Audio/Output/AudioOutputManager.cs @@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Output /// </summary> public class AudioOutputManager : IDisposable { - private object _lock = new object(); + private readonly object _lock = new(); /// <summary> /// Lock used for session allocation. /// </summary> - private object _sessionLock = new object(); + private readonly object _sessionLock = new(); /// <summary> /// The session ids allocation table. diff --git a/src/Ryujinx.Audio/Output/AudioOutputSystem.cs b/src/Ryujinx.Audio/Output/AudioOutputSystem.cs index 93df87aab..8378f33f8 100644 --- a/src/Ryujinx.Audio/Output/AudioOutputSystem.cs +++ b/src/Ryujinx.Audio/Output/AudioOutputSystem.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Output /// <summary> /// THe lock of the parent. /// </summary> - private object _parentLock; + private readonly object _parentLock; /// <summary> /// The dispose state. diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 1ad7b8590..8485fb4c5 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server { public class AudioRenderSystem : IDisposable { - private object _lock = new object(); + private readonly object _lock = new(); private AudioRendererRenderingDevice _renderingDevice; private AudioRendererExecutionMode _executionMode; diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs index 4de0ad166..e41d5cc50 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs @@ -19,12 +19,12 @@ namespace Ryujinx.Audio.Renderer.Server /// <summary> /// Lock used for session allocation. /// </summary> - private object _sessionLock = new object(); + private readonly object _sessionLock = new(); /// <summary> /// Lock used to control the <see cref="AudioProcessor"/> running state. /// </summary> - private object _audioProcessorLock = new object(); + private readonly object _audioProcessorLock = new(); /// <summary> /// The session ids allocation table. diff --git a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs index b37988fed..0fee00001 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler /// <summary> /// Global lock of the object. /// </summary> - private object Lock = new object(); + private readonly object Lock = new(); /// <summary> /// The upsamplers instances. diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index e6169d895..bc96f2225 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading private int _refConsumerPtr; private Action _interruptAction; - private object _interruptLock = new(); + private readonly object _interruptLock = new(); public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index d0230b629..03504b11f 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private List<BufferMigration> _sources; private BufferMigration _migrationTarget; - private object _lock = new object(); + private readonly object _lock = new(); /// <summary> /// Whether the modified range list has any entries or not. @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int i = 0; i < count; i++) { BufferModifiedRange overlap = overlaps[i]; - + if (overlap.Address > address) { // The start of the remaining region is uncovered by this overlap. Call the action for it. diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index e0aafa6fa..648600bc2 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries private ulong _accumulatedCounter; private int _waiterCount; - private object _lock = new object(); + private readonly object _lock = new(); private Queue<BufferedQuery> _queryPool; private AutoResetEvent _queuedEvent = new AutoResetEvent(false); diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs index 7297baab5..ffe1f774f 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries private bool _hostAccessReserved = false; private int _refCount = 1; // Starts with a reference from the counter queue. - private object _lock = new object(); + private readonly object _lock = new(); private ulong _result = ulong.MaxValue; private double _divisor = 1f; diff --git a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs index 57231cd65..69d2a78e1 100644 --- a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs +++ b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.OpenGL { private const int DisposedLiveFrames = 2; - private readonly object _lock = new object(); + private readonly object _lock = new(); private readonly Dictionary<TextureCreateInfo, List<DisposedTexture>> _textures = new Dictionary<TextureCreateInfo, List<DisposedTexture>>(); /// <summary> diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index c30d91c42..2fdddaeab 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private ulong _accumulatedCounter; private int _waiterCount; - private object _lock = new object(); + private readonly object _lock = new(); private Queue<BufferedQuery> _queryPool; private AutoResetEvent _queuedEvent = new AutoResetEvent(false); diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs index d3aedb2f3..9d0a674b4 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private bool _hostAccessReserved = false; private int _refCount = 1; // Starts with a reference from the counter queue. - private object _lock = new object(); + private readonly object _lock = new(); private ulong _result = ulong.MaxValue; private double _divisor = 1f; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 4b484e802..4337ec44b 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Applets private bool _canAcceptController = false; private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard; - private object _lock = new object(); + private readonly object _lock = new(); public event EventHandler AppletStateChanged; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs index c30ad11b0..fb4cec82a 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard private const int TextBoxBlinkSleepMilliseconds = 100; private const int RendererWaitTimeoutMilliseconds = 100; - private readonly object _stateLock = new object(); + private readonly object _stateLock = new(); private SoftwareKeyboardUiState _state = new SoftwareKeyboardUiState(); private SoftwareKeyboardRendererBase _renderer; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index 9a91fa321..595223ed3 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard const string CancelText = "Cancel"; const string ControllerToggleText = "Toggle input"; - private readonly object _bufferLock = new object(); + private readonly object _bufferLock = new(); private RenderingSurfaceInfo _surfaceInfo = null; private Image<Argb32> _surface = null; @@ -311,7 +311,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard private static RectangleF MeasureString(ReadOnlySpan<char> text, Font font) { RendererOptions options = new RendererOptions(font); - + if (text == "") { FontRectangle emptyRectangle = TextMeasurer.Measure(" ", options); diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs index 0de78a0e5..ed876ffd2 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs @@ -26,8 +26,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard } private TRef<bool> _cancelled = null; - private Thread _thread = null; - private object _lock = new object(); + private Thread _thread = null; + private readonly object _lock = new(); public bool IsRunning { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 21e89944e..510c99ea3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -40,8 +40,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public ProcessState State { get; private set; } - private object _processLock; - private object _threadingLock; + private readonly object _processLock = new(); + private readonly object _threadingLock = new(); public KAddressArbiter AddressArbiter { get; private set; } @@ -94,9 +94,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public KProcess(KernelContext context, bool allowCodeMemoryForJit = false) : base(context) { - _processLock = new object(); - _threadingLock = new object(); - AddressArbiter = new KAddressArbiter(context); _fullTlsPages = new SortedDictionary<ulong, KTlsPageInfo>(); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 633964686..78bd577e1 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public bool WaitingInArbitration { get; set; } - private object _activityOperationLock; + private readonly object _activityOperationLock = new(); public KThread(KernelContext context) : base(context) { @@ -123,8 +123,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _mutexWaiters = new LinkedList<KThread>(); _pinnedWaiters = new LinkedList<KThread>(); - - _activityOperationLock = new object(); } public Result Initialize( diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 399e778a4..8f93117e1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -17,8 +17,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private KEvent _accumulatedSuspendedTickChangedEvent; private int _accumulatedSuspendedTickChangedEventHandle; - private object _fatalSectionLock = new object(); - private int _fatalSectionCount; + private readonly object _fatalSectionLock = new(); + private int _fatalSectionCount; // TODO: Set this when the game goes in suspension (go back to home menu ect), we currently don't support that so we can keep it set to 0. private ulong _accumulatedSuspendedTickValue = 0; @@ -429,4 +429,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs index 063750c6b..505580221 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator private readonly UserId _userId; private readonly FriendServicePermissionLevel _permissionLevel; - private readonly object _lock = new object(); + private readonly object _lock = new(); private KEvent _notificationEvent; private int _notificationEventHandle = 0; diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs index ac5512eda..383fb3fbe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl private NvFence _previousFailingFence; private uint _failingCount; - public readonly object Lock = new object(); + public readonly object Lock = new(); /// <summary> /// Max failing count until waiting on CPU. diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs index 27dd1bd16..1b842aa17 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl private Switch _device; - private object _syncpointAllocatorLock = new object(); + private readonly object _syncpointAllocatorLock = new(); public NvHostSyncpt(Switch device) { diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs index a93f176a5..b0ac6e680 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { private static ConcurrentDictionary<ulong, BsdContext> _registry = new ConcurrentDictionary<ulong, BsdContext>(); - private readonly object _lock = new object(); + private readonly object _lock = new(); private List<IFileDescriptor> _fds; diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs index 6514d4855..d7b53158a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl private ulong _value; private readonly EventFdFlags _flags; - private object _lock = new object(); + private readonly object _lock = new(); public bool Blocking { get => !_flags.HasFlag(EventFdFlags.NonBlocking); set => throw new NotSupportedException(); } diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs index c911f434c..db2241631 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl { private RandomNumberGenerator _rng; - private object _lock = new object(); + private readonly object _lock = new(); public IRandomInterface(ServiceCtx context) { diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs index abbc13541..dae0698c3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs @@ -40,13 +40,13 @@ namespace Ryujinx.HLE.HOS.Services.Ssl } } - private VirtualFileSystem _virtualFileSystem; + private VirtualFileSystem _virtualFileSystem; private IntegrityCheckLevel _fsIntegrityCheckLevel; - private ContentManager _contentManager; - private bool _initialized; + private ContentManager _contentManager; + private bool _initialized; private Dictionary<CaCertificateId, CertStoreEntry> _certificates; - private object _lock = new object(); + private readonly object _lock = new(); private struct CertStoreFileHeader { diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs index 1efd37f49..8cf55912e 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public bool EnableExternalEvent; public int MaxBufferCountCached; - public readonly object Lock = new object(); + public readonly object Lock = new(); private KEvent _waitBufferFreeEvent; private KEvent _frameAvailableEvent; diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs index 833bc26ec..fa840f2ad 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private uint _currentCallbackTicket; private uint _callbackTicket; - private readonly object _callbackLock = new object(); + private readonly object _callbackLock = new(); public BufferQueueProducer(BufferQueueCore core, ITickSource tickSource) { diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs index 49fceed9f..754fa7d73 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger protected BufferQueueConsumer Consumer; - protected readonly object Lock = new object(); + protected readonly object Lock = new(); private IConsumerListener _listener; @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Slot slot = Slots[slotIndex]; - // TODO: Check this. On Android, this checks the "handle". I assume NvMapHandle is the handle, but it might not be. + // TODO: Check this. On Android, this checks the "handle". I assume NvMapHandle is the handle, but it might not be. return !slot.GraphicBuffer.IsNull && slot.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle == graphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle; } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index c7cddf100..0c1cea517 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private int _swapInterval; private int _swapIntervalDelay; - private readonly object Lock = new object(); + private readonly object Lock = new(); public long RenderLayerId { get; private set; } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs index ef4b7b399..8b85d697b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs @@ -13,14 +13,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone private UInt128 _timeZoneRuleVersion; private uint _totalLocationNameCount; private SteadyClockTimePoint _timeZoneUpdateTimePoint; - private object _lock; + private readonly object _lock = new(); public TimeZoneManager() { _isInitialized = false; _deviceLocationName = "UTC"; _timeZoneRuleVersion = new UInt128(); - _lock = new object(); _myRules = new Box<TimeZoneRule>(); _timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom(); diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index a4a671eaf..04bc8d1db 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl private readonly List<MultiWaitHolderBase> _multiWaits; - private object _lock; + private readonly object _lock = new(); private int _waitingThreadHandle; @@ -24,8 +24,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl public MultiWaitImpl() { _multiWaits = new List<MultiWaitHolderBase>(); - - _lock = new object(); } public void LinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder) diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index 925526737..6a8c1204f 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Input.SDL2 SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE2, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE3, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE4, - SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_TOUCHPAD, + SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_TOUCHPAD, // Virtual buttons are invalid, ignored. SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID, @@ -55,7 +55,7 @@ namespace Ryujinx.Input.SDL2 SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID, }; - private object _userMappingLock = new object(); + private readonly object _userMappingLock = new(); private List<ButtonMappingEntry> _buttonsUserMapping; diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs index edadae15e..2fe0614d8 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Input.SDL2 } } - private object _userMappingLock = new object(); + private readonly object _userMappingLock = new(); private readonly SDL2KeyboardDriver _driver; private StandardKeyboardInputConfig _configuration; diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 5290ecbb7..89a2f585b 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Input.HLE { private CemuHookClient _cemuHookClient; - private object _lock = new object(); + private readonly object _lock = new(); private bool _blockInputUpdates; @@ -271,7 +271,7 @@ namespace Ryujinx.Input.HLE _device.Hid.Mouse.Update((int)position.X, (int)position.Y, buttons, (int)mouseInput.Scroll.X, (int)mouseInput.Scroll.Y, true); } - else + else { _device.Hid.Mouse.Update(0, 0); } diff --git a/src/Ryujinx.Memory/Tracking/RegionHandle.cs b/src/Ryujinx.Memory/Tracking/RegionHandle.cs index 63a168847..777944888 100644 --- a/src/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/src/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Memory.Tracking private event Action _onDirty; - private object _preActionLock = new object(); + private readonly object _preActionLock = new(); private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access. private PreciseRegionSignal _preciseAction; // Action to perform on a precise read or write. private readonly List<VirtualRegion> _regions; diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index c41c6269d..5e2deb26d 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -41,7 +41,7 @@ namespace Ryujinx.SDL2.Common private ConcurrentDictionary<uint, Action<SDL_Event>> _registeredWindowHandlers; - private object _lock = new object(); + private readonly object _lock = new(); private SDL2Driver() {} diff --git a/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs b/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs index bc0ad87e5..ca3ff32f4 100644 --- a/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs +++ b/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Input.GTK3 } } - private object _userMappingLock = new object(); + private readonly object _userMappingLock = new(); private readonly GTK3KeyboardDriver _driver; private StandardKeyboardInputConfig _configuration; From 32d21ddf17ff7d61d8185a79bec3f5d02706109b Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Thu, 15 Jun 2023 00:54:27 -0300 Subject: [PATCH 639/737] Inheritance list should not be redundant (#5230) --- src/ARMeilleure/Decoders/OpCode32SimdSel.cs | 2 +- src/ARMeilleure/State/ExecutionMode.cs | 2 +- .../Native/SoundIoBackend.cs | 2 +- src/Ryujinx.Common/Configuration/Hid/ControllerType.cs | 2 +- src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs | 2 +- .../MoltenVK/MVKConfiguration.cs | 10 +++++----- .../HOS/Services/Hid/Types/Npad/ControllerType.cs | 2 +- .../HOS/Services/Hid/Types/Npad/NpadIdType.cs | 2 +- .../HOS/Services/Hid/Types/Npad/PlayerIndex.cs | 2 +- .../Services/Hid/Types/SharedMemory/Npad/DeviceType.cs | 2 +- .../Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs | 2 +- .../HOS/Services/Mii/Types/RandomMiiConstants.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs | 2 +- .../HOS/Services/Nv/NvDrvServices/NvInternalResult.cs | 2 +- .../Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs | 2 +- .../HOS/Services/SurfaceFlinger/NativeWindowApi.cs | 2 +- src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs | 2 +- 20 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/ARMeilleure/Decoders/OpCode32SimdSel.cs b/src/ARMeilleure/Decoders/OpCode32SimdSel.cs index 37fd714a4..bd4865ea5 100644 --- a/src/ARMeilleure/Decoders/OpCode32SimdSel.cs +++ b/src/ARMeilleure/Decoders/OpCode32SimdSel.cs @@ -13,7 +13,7 @@ } } - enum OpCode32SimdSelMode : int + enum OpCode32SimdSelMode { Eq = 0, Vs, diff --git a/src/ARMeilleure/State/ExecutionMode.cs b/src/ARMeilleure/State/ExecutionMode.cs index 29154a255..f43c5569f 100644 --- a/src/ARMeilleure/State/ExecutionMode.cs +++ b/src/ARMeilleure/State/ExecutionMode.cs @@ -1,6 +1,6 @@ namespace ARMeilleure.State { - enum ExecutionMode : int + enum ExecutionMode { Aarch32Arm = 0, Aarch32Thumb = 1, diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs index 92f8ea375..4e9123a0f 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native { - public enum SoundIoBackend : int + public enum SoundIoBackend { None = 0, Jack = 1, diff --git a/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs index 70f811c89..1c273537c 100644 --- a/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs +++ b/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Common.Configuration.Hid // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical [Flags] [JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))] - public enum ControllerType : int + public enum ControllerType { None, ProController = 1 << 0, diff --git a/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs index dd6495d4d..4e9a0434d 100644 --- a/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs +++ b/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Common.Configuration.Hid { // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical [JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))] - public enum PlayerIndex : int + public enum PlayerIndex { Player1 = 0, Player2 = 1, diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs index 4fbae86e7..414dbc628 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Vulkan.MoltenVK { - enum MVKConfigLogLevel : int + enum MVKConfigLogLevel { None = 0, Error = 1, @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK Debug = 4 } - enum MVKConfigTraceVulkanCalls : int + enum MVKConfigTraceVulkanCalls { None = 0, Enter = 1, @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK Duration = 3 } - enum MVKConfigAutoGPUCaptureScope : int + enum MVKConfigAutoGPUCaptureScope { None = 0, Device = 1, @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK } [Flags] - enum MVKConfigAdvertiseExtensions : int + enum MVKConfigAdvertiseExtensions { All = 0x00000001, MoltenVK = 0x00000002, @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK Portability = 0x00000008 } - enum MVKVkSemaphoreSupportStyle : int + enum MVKVkSemaphoreSupportStyle { MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE = 0, MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS_WHERE_SAFE = 1, diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs index b2d34e8e2..d830f46ad 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.HLE.HOS.Services.Hid { [Flags] - public enum ControllerType : int + public enum ControllerType { None, ProController = 1 << 0, diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs index 1abd84680..4b488cd2c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid { - public enum NpadIdType : int + public enum NpadIdType { Player1 = 0, Player2 = 1, diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs index f4ced5df5..972d69b45 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid { - public enum PlayerIndex : int + public enum PlayerIndex { Player1 = 0, Player2 = 1, diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs index b02018353..95b1cb518 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs @@ -3,7 +3,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad { [Flags] - enum DeviceType : int + enum DeviceType { None = 0, diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs index 477dfd10f..e10e55cd8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad { - enum NpadBatteryLevel : int + enum NpadBatteryLevel { Percent0, Percent25, diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs index f3a101d8b..abf18e36e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs @@ -775,7 +775,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types private static ReadOnlySpan<ElementInfo> ElementInfos => MemoryMarshal.Cast<byte, ElementInfo>(ElementInfoArray); - private enum ElementInfoIndex : int + private enum ElementInfoIndex { HairType, Height, diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs index 82529450b..6def469db 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types }; [Flags] - public enum BeardAndMustacheFlag : int + public enum BeardAndMustacheFlag { Beard = 1, Mustache diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs index 1ded636a9..2ae02ea03 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types { - enum Source : int + enum Source { Database, Default diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs index d51dce879..c9682bdbb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs @@ -3,7 +3,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types { [Flags] - enum SourceFlag : int + enum SourceFlag { Database = 1 << Source.Database, Default = 1 << Source.Default, diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs index 9345baeb5..9a3aa7aa1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices { - enum NvInternalResult : int + enum NvInternalResult { Success = 0, OperationNotPermitted = -1, diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs index 61b73cba2..9eb7efff9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { - enum NvMapHandleParam : int + enum NvMapHandleParam { Size = 1, Align = 2, diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs index 058748685..e04a35954 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs @@ -5,7 +5,7 @@ using System.Net.Sockets; namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { - interface ISocket : IDisposable, IFileDescriptor + interface ISocket : IFileDescriptor { IPEndPoint RemoteEndPoint { get; } IPEndPoint LocalEndPoint { get; } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs index 1ae2732f5..794c48b82 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { - enum NativeWindowApi : int + enum NativeWindowApi { NoApi = 0, NVN = 1, diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs index 5a1519021..925811fa2 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { - enum Status : int + enum Status { Success = 0, WouldBlock = -11, From f92921a6d118aa9c6acdb3ecaa3cd61a19fe341e Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Thu, 15 Jun 2023 17:31:53 -0300 Subject: [PATCH 640/737] Implement Load/Store Local/Shared and Atomic shared using new instructions (#5241) * Implement Load/Store Local/Shared and Atomic shared using new instructions * Remove now unused code * Fix base offset register overwrite * Fix missing storage buffer set index when generating GLSL for Vulkan * Shader cache version bump * Remove more unused code * Some PR feedback --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 75 +++----- .../CodeGen/Glsl/DefaultNames.cs | 3 - .../AtomicMinMaxS32Shared.glsl | 21 --- .../HelperFunctions/HelperFunctionNames.cs | 8 - .../HelperFunctions/StoreSharedSmallInt.glsl | 23 --- .../CodeGen/Glsl/Instructions/InstGen.cs | 37 +--- .../Glsl/Instructions/InstGenHelper.cs | 8 - .../Glsl/Instructions/InstGenMemory.cs | 86 ++------- .../CodeGen/Glsl/OperandManager.cs | 15 +- .../CodeGen/Spirv/CodeGenContext.cs | 5 +- .../CodeGen/Spirv/Declarations.cs | 60 ++---- .../CodeGen/Spirv/Instructions.cs | 176 +++--------------- .../Instructions/InstEmitMemory.cs | 109 +++++------ .../IntermediateRepresentation/Instruction.cs | 6 - .../IntermediateRepresentation/StorageKind.cs | 11 +- .../Ryujinx.Graphics.Shader.csproj | 2 - .../StructuredIr/HelperFunctionsMask.cs | 18 +- .../StructuredIr/InstructionInfo.cs | 6 - .../StructuredIr/MemoryDefinition.cs | 18 ++ .../StructuredIr/ShaderProperties.cs | 22 +++ .../StructuredIr/StructuredProgram.cs | 11 -- .../Translation/EmitterContextInsts.cs | 40 +--- .../Translation/HelperFunctionManager.cs | 109 ++++++++++- .../Translation/HelperFunctionName.cs | 4 + .../Optimizations/GlobalToStorage.cs | 32 ++-- .../Translation/ResourceManager.cs | 25 ++- .../Translation/Rewriter.cs | 92 +++++++++ .../Translation/ShaderConfig.cs | 16 +- .../Translation/Translator.cs | 2 +- 30 files changed, 475 insertions(+), 567 deletions(-) delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 153a2e8c1..5cfbfd386 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5080; + private const uint CodeGenVersion = 5241; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 958f1cef3..08e8eb195 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -71,40 +71,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;"); context.AppendLine(); - if (context.Config.Stage == ShaderStage.Compute) - { - int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4); - - if (localMemorySize != 0) - { - string localMemorySizeStr = NumberFormatter.FormatInt(localMemorySize); - - context.AppendLine($"uint {DefaultNames.LocalMemoryName}[{localMemorySizeStr}];"); - context.AppendLine(); - } - - int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4); - - if (sharedMemorySize != 0) - { - string sharedMemorySizeStr = NumberFormatter.FormatInt(sharedMemorySize); - - context.AppendLine($"shared uint {DefaultNames.SharedMemoryName}[{sharedMemorySizeStr}];"); - context.AppendLine(); - } - } - else if (context.Config.LocalMemorySize != 0) - { - int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4); - - string localMemorySizeStr = NumberFormatter.FormatInt(localMemorySize); - - context.AppendLine($"uint {DefaultNames.LocalMemoryName}[{localMemorySizeStr}];"); - context.AppendLine(); - } - DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); + DeclareMemories(context, context.Config.Properties.LocalMemories.Values, isShared: false); + DeclareMemories(context, context.Config.Properties.SharedMemories.Values, isShared: true); var textureDescriptors = context.Config.GetTextureDescriptors(); if (textureDescriptors.Length != 0) @@ -238,11 +208,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl"); - } - if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0) { AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl"); @@ -273,11 +238,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl"); } - if ((info.HelperFunctionsMask & HelperFunctionsMask.StoreSharedSmallInt) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl"); - } - if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) { AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl"); @@ -358,7 +318,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl _ => "std430" }; - context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}"); + string set = string.Empty; + + if (context.Config.Options.TargetApi == TargetApi.Vulkan) + { + set = $"set = {buffer.Set}, "; + } + + context.AppendLine($"layout ({set}binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}"); context.EnterScope(); foreach (StructureField field in buffer.Type.Fields) @@ -391,6 +358,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } + private static void DeclareMemories(CodeGenContext context, IEnumerable<MemoryDefinition> memories, bool isShared) + { + string prefix = isShared ? "shared " : string.Empty; + + foreach (MemoryDefinition memory in memories) + { + string typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array); + + if (memory.ArrayLength > 0) + { + string arraySize = memory.ArrayLength.ToString(CultureInfo.InvariantCulture); + + context.AppendLine($"{prefix}{typeName} {memory.Name}[{arraySize}];"); + } + else + { + context.AppendLine($"{prefix}{typeName} {memory.Name}[];"); + } + } + } + private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) { int arraySize = 0; @@ -717,7 +705,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string code = EmbeddedResources.ReadAllText(filename); code = code.Replace("\t", CodeGenContext.Tab); - code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName); if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot()) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 5ee8259cf..e909dcf04 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -11,9 +11,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string IAttributePrefix = "in_attr"; public const string OAttributePrefix = "out_attr"; - public const string LocalMemoryName = "local_mem"; - public const string SharedMemoryName = "shared_mem"; - public const string ArgumentNamePrefix = "a"; public const string UndefinedName = "undef"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl deleted file mode 100644 index 82b76bccf..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl +++ /dev/null @@ -1,21 +0,0 @@ -int Helper_AtomicMaxS32(int offset, int value) -{ - uint oldValue, newValue; - do - { - oldValue = $SHARED_MEM$[offset]; - newValue = uint(max(int(oldValue), value)); - } while (atomicCompSwap($SHARED_MEM$[offset], oldValue, newValue) != oldValue); - return int(oldValue); -} - -int Helper_AtomicMinS32(int offset, int value) -{ - uint oldValue, newValue; - do - { - oldValue = $SHARED_MEM$[offset]; - newValue = uint(min(int(oldValue), value)); - } while (atomicCompSwap($SHARED_MEM$[offset], oldValue, newValue) != oldValue); - return int(oldValue); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs index 54f35b15a..21c435475 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs @@ -2,9 +2,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { static class HelperFunctionNames { - public static string AtomicMaxS32 = "Helper_AtomicMaxS32"; - public static string AtomicMinS32 = "Helper_AtomicMinS32"; - public static string MultiplyHighS32 = "Helper_MultiplyHighS32"; public static string MultiplyHighU32 = "Helper_MultiplyHighU32"; @@ -13,10 +10,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string ShuffleUp = "Helper_ShuffleUp"; public static string ShuffleXor = "Helper_ShuffleXor"; public static string SwizzleAdd = "Helper_SwizzleAdd"; - - public static string StoreShared16 = "Helper_StoreShared16"; - public static string StoreShared8 = "Helper_StoreShared8"; - public static string StoreStorage16 = "Helper_StoreStorage16"; - public static string StoreStorage8 = "Helper_StoreStorage8"; } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl deleted file mode 100644 index 2f57b5ff6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl +++ /dev/null @@ -1,23 +0,0 @@ -void Helper_StoreShared16(int offset, uint value) -{ - int wordOffset = offset >> 2; - int bitOffset = (offset & 3) * 8; - uint oldValue, newValue; - do - { - oldValue = $SHARED_MEM$[wordOffset]; - newValue = bitfieldInsert(oldValue, value, bitOffset, 16); - } while (atomicCompSwap($SHARED_MEM$[wordOffset], oldValue, newValue) != oldValue); -} - -void Helper_StoreShared8(int offset, uint value) -{ - int wordOffset = offset >> 2; - int bitOffset = (offset & 3) * 8; - uint oldValue, newValue; - do - { - oldValue = $SHARED_MEM$[wordOffset]; - newValue = bitfieldInsert(oldValue, value, bitOffset, 8); - } while (atomicCompSwap($SHARED_MEM$[wordOffset], oldValue, newValue) != oldValue); -} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 01d8a6e7a..b2577a999 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string args = string.Empty; - if (atomic && operation.StorageKind == StorageKind.StorageBuffer) + if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory)) { args = GenerateLoadOrStore(context, operation, isStore: false); @@ -81,23 +81,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions args += ", " + GetSoureExpr(context, operation.GetSource(argIndex), dstType); } } - else if (atomic && operation.StorageKind == StorageKind.SharedMemory) - { - args = LoadShared(context, operation); - - // For shared memory access, the second argument is unused and should be ignored. - // It is there to make both storage and shared access have the same number of arguments. - // For storage, both inputs are consumed when the argument index is 0, so we should skip it here. - - for (int argIndex = 2; argIndex < arity; argIndex++) - { - args += ", "; - - AggregateType dstType = GetSrcVarType(inst, argIndex); - - args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); - } - } else { for (int argIndex = 0; argIndex < arity; argIndex++) @@ -179,12 +162,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.Load: return Load(context, operation); - case Instruction.LoadLocal: - return LoadLocal(context, operation); - - case Instruction.LoadShared: - return LoadShared(context, operation); - case Instruction.Lod: return Lod(context, operation); @@ -200,18 +177,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.Store: return Store(context, operation); - case Instruction.StoreLocal: - return StoreLocal(context, operation); - - case Instruction.StoreShared: - return StoreShared(context, operation); - - case Instruction.StoreShared16: - return StoreShared16(context, operation); - - case Instruction.StoreShared8: - return StoreShared8(context, operation); - case Instruction.TextureSample: return TextureSample(context, operation); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index f42d98986..8b0b744ad 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -17,9 +17,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd"); Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomicAnd"); Add(Instruction.AtomicCompareAndSwap, InstType.AtomicTernary, "atomicCompSwap"); - Add(Instruction.AtomicMaxS32, InstType.CallTernary, HelperFunctionNames.AtomicMaxS32); Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "atomicMax"); - Add(Instruction.AtomicMinS32, InstType.CallTernary, HelperFunctionNames.AtomicMinS32); Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "atomicMin"); Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomicOr"); Add(Instruction.AtomicSwap, InstType.AtomicBinary, "atomicExchange"); @@ -83,8 +81,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.ImageAtomic, InstType.Special); Add(Instruction.IsNan, InstType.CallUnary, "isnan"); Add(Instruction.Load, InstType.Special); - Add(Instruction.LoadLocal, InstType.Special); - Add(Instruction.LoadShared, InstType.Special); Add(Instruction.Lod, InstType.Special); Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); @@ -118,10 +114,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.Sine, InstType.CallUnary, "sin"); Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); Add(Instruction.Store, InstType.Special); - Add(Instruction.StoreLocal, InstType.Special); - Add(Instruction.StoreShared, InstType.Special); - Add(Instruction.StoreShared16, InstType.Special); - Add(Instruction.StoreShared8, InstType.Special); Add(Instruction.Subtract, InstType.OpBinary, "-", 2); Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); Add(Instruction.TextureSample, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index c8084d9dd..99376ffb2 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -191,25 +191,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return GenerateLoadOrStore(context, operation, isStore: false); } - public static string LoadLocal(CodeGenContext context, AstOperation operation) - { - return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName); - } - - public static string LoadShared(CodeGenContext context, AstOperation operation) - { - return LoadLocalOrShared(context, operation, DefaultNames.SharedMemoryName); - } - - private static string LoadLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName) - { - IAstNode src1 = operation.GetSource(0); - - string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - - return $"{arrayName}[{offsetExpr}]"; - } - public static string Lod(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; @@ -263,58 +244,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return GenerateLoadOrStore(context, operation, isStore: true); } - public static string StoreLocal(CodeGenContext context, AstOperation operation) - { - return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName); - } - - public static string StoreShared(CodeGenContext context, AstOperation operation) - { - return StoreLocalOrShared(context, operation, DefaultNames.SharedMemoryName); - } - - private static string StoreLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - - string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - - AggregateType srcType = OperandManager.GetNodeDestType(context, src2); - - string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32); - - return $"{arrayName}[{offsetExpr}] = {src}"; - } - - public static string StoreShared16(CodeGenContext context, AstOperation operation) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - - string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - - AggregateType srcType = OperandManager.GetNodeDestType(context, src2); - - string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32); - - return $"{HelperFunctionNames.StoreShared16}({offsetExpr}, {src})"; - } - - public static string StoreShared8(CodeGenContext context, AstOperation operation) - { - IAstNode src1 = operation.GetSource(0); - IAstNode src2 = operation.GetSource(1); - - string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - - AggregateType srcType = OperandManager.GetNodeDestType(context, src2); - - string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32); - - return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})"; - } - public static string TextureSample(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; @@ -675,6 +604,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions varType = field.Type; break; + case StorageKind.LocalMemory: + case StorageKind.SharedMemory: + if (!(operation.GetSource(srcIndex++) is AstOperand bindingId) || bindingId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + MemoryDefinition memory = storageKind == StorageKind.LocalMemory + ? context.Config.Properties.LocalMemories[bindingId.Value] + : context.Config.Properties.SharedMemories[bindingId.Value]; + + varName = memory.Name; + varType = memory.Type; + break; + case StorageKind.Input: case StorageKind.InputPerPatch: case StorageKind.Output: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 4fd1d17c4..4f6ca642c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (node is AstOperation operation) { - if (operation.Inst == Instruction.Load) + if (operation.Inst == Instruction.Load || operation.Inst.IsAtomic()) { switch (operation.StorageKind) { @@ -136,6 +136,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return field.Type & AggregateType.ElementTypeMask; + case StorageKind.LocalMemory: + case StorageKind.SharedMemory: + if (!(operation.GetSource(0) is AstOperand bindingId) || bindingId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); + } + + MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory + ? context.Config.Properties.LocalMemories[bindingId.Value] + : context.Config.Properties.SharedMemories[bindingId.Value]; + + return memory.Type & AggregateType.ElementTypeMask; + case StorageKind.Input: case StorageKind.InputPerPatch: case StorageKind.Output: diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 1f5167e66..a4daaa67e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -25,8 +25,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>(); public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>(); - public Instruction LocalMemory { get; set; } - public Instruction SharedMemory { get; set; } + public Dictionary<int, Instruction> LocalMemories { get; } = new Dictionary<int, Instruction>(); + public Dictionary<int, Instruction> SharedMemories { get; } = new Dictionary<int, Instruction>(); public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>(); public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>(); public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>(); @@ -35,7 +35,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); - public Instruction CoordTemp { get; set; } public StructuredFunction CurrentFunction { get; set; } private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>(); private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index eb2db514d..59acea4f6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -6,7 +6,6 @@ using Spv.Generator; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Numerics; using static Spv.Specification; using SpvInstruction = Spv.Generator.Instruction; @@ -44,13 +43,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddLocalVariable(spvLocal); context.DeclareLocal(local, spvLocal); } - - var ivector2Type = context.TypeVector(context.TypeS32(), 2); - var coordTempPointerType = context.TypePointer(StorageClass.Function, ivector2Type); - var coordTemp = context.Variable(coordTempPointerType, StorageClass.Function); - - context.AddLocalVariable(coordTemp); - context.CoordTemp = coordTemp; } public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions) @@ -77,54 +69,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info) { - if (context.Config.Stage == ShaderStage.Compute) - { - int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4); - - if (localMemorySize != 0) - { - DeclareLocalMemory(context, localMemorySize); - } - - int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4); - - if (sharedMemorySize != 0) - { - DeclareSharedMemory(context, sharedMemorySize); - } - } - else if (context.Config.LocalMemorySize != 0) - { - int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4); - DeclareLocalMemory(context, localMemorySize); - } - DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); + DeclareMemories(context, context.Config.Properties.LocalMemories, context.LocalMemories, StorageClass.Private); + DeclareMemories(context, context.Config.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup); DeclareSamplers(context, context.Config.GetTextureDescriptors()); DeclareImages(context, context.Config.GetImageDescriptors()); DeclareInputsAndOutputs(context, info); } - private static void DeclareLocalMemory(CodeGenContext context, int size) + private static void DeclareMemories( + CodeGenContext context, + IReadOnlyDictionary<int, MemoryDefinition> memories, + Dictionary<int, SpvInstruction> dict, + StorageClass storage) { - context.LocalMemory = DeclareMemory(context, StorageClass.Private, size); - } + foreach ((int id, MemoryDefinition memory) in memories) + { + var pointerType = context.TypePointer(storage, context.GetType(memory.Type, memory.ArrayLength)); + var variable = context.Variable(pointerType, storage); - private static void DeclareSharedMemory(CodeGenContext context, int size) - { - context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size); - } + context.AddGlobalVariable(variable); - private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size) - { - var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size)); - var pointerType = context.TypePointer(storage, arrayType); - var variable = context.Variable(pointerType, storage); - - context.AddGlobalVariable(variable); - - return variable; + dict.Add(id, variable); + } } private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 6c1157525..b451f7a48 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -97,8 +97,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.ImageStore, GenerateImageStore); Add(Instruction.IsNan, GenerateIsNan); Add(Instruction.Load, GenerateLoad); - Add(Instruction.LoadLocal, GenerateLoadLocal); - Add(Instruction.LoadShared, GenerateLoadShared); Add(Instruction.Lod, GenerateLod); Add(Instruction.LogarithmB2, GenerateLogarithmB2); Add(Instruction.LogicalAnd, GenerateLogicalAnd); @@ -132,10 +130,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.Sine, GenerateSine); Add(Instruction.SquareRoot, GenerateSquareRoot); Add(Instruction.Store, GenerateStore); - Add(Instruction.StoreLocal, GenerateStoreLocal); - Add(Instruction.StoreShared, GenerateStoreShared); - Add(Instruction.StoreShared16, GenerateStoreShared16); - Add(Instruction.StoreShared8, GenerateStoreShared8); Add(Instruction.Subtract, GenerateSubtract); Add(Instruction.SwizzleAdd, GenerateSwizzleAdd); Add(Instruction.TextureSample, GenerateTextureSample); @@ -871,30 +865,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateLoadOrStore(context, operation, isStore: false); } - private static OperationResult GenerateLoadLocal(CodeGenContext context, AstOperation operation) - { - return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory); - } - - private static OperationResult GenerateLoadShared(CodeGenContext context, AstOperation operation) - { - return GenerateLoadLocalOrShared(context, operation, StorageClass.Workgroup, context.SharedMemory); - } - - private static OperationResult GenerateLoadLocalOrShared( - CodeGenContext context, - AstOperation operation, - StorageClass storageClass, - SpvInstruction memory) - { - var offset = context.Get(AggregateType.S32, operation.GetSource(0)); - - var elemPointer = context.AccessChain(context.TypePointer(storageClass, context.TypeU32()), memory, offset); - var value = context.Load(context.TypeU32(), elemPointer); - - return new OperationResult(AggregateType.U32, value); - } - private static OperationResult GenerateLod(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; @@ -1268,45 +1238,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GenerateLoadOrStore(context, operation, isStore: true); } - private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation) - { - return GenerateStoreLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory); - } - - private static OperationResult GenerateStoreShared(CodeGenContext context, AstOperation operation) - { - return GenerateStoreLocalOrShared(context, operation, StorageClass.Workgroup, context.SharedMemory); - } - - private static OperationResult GenerateStoreLocalOrShared( - CodeGenContext context, - AstOperation operation, - StorageClass storageClass, - SpvInstruction memory) - { - var offset = context.Get(AggregateType.S32, operation.GetSource(0)); - var value = context.Get(AggregateType.U32, operation.GetSource(1)); - - var elemPointer = context.AccessChain(context.TypePointer(storageClass, context.TypeU32()), memory, offset); - context.Store(elemPointer, value); - - return OperationResult.Invalid; - } - - private static OperationResult GenerateStoreShared16(CodeGenContext context, AstOperation operation) - { - GenerateStoreSharedSmallInt(context, operation, 16); - - return OperationResult.Invalid; - } - - private static OperationResult GenerateStoreShared8(CodeGenContext context, AstOperation operation) - { - GenerateStoreSharedSmallInt(context, operation, 8); - - return OperationResult.Invalid; - } - private static OperationResult GenerateSubtract(CodeGenContext context, AstOperation operation) { return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub); @@ -1827,55 +1758,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AstOperation operation, Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitU) { - var value = context.GetU32(operation.GetSource(operation.SourcesCount - 1)); + SpvInstruction elemPointer = GetStoragePointer(context, operation, out AggregateType varType); - SpvInstruction elemPointer; - - if (operation.StorageKind == StorageKind.StorageBuffer) - { - elemPointer = GetStoragePointer(context, operation, out _); - } - else if (operation.StorageKind == StorageKind.SharedMemory) - { - var offset = context.GetU32(operation.GetSource(0)); - elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset); - } - else - { - throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); - } + var value = context.Get(varType, operation.GetSource(operation.SourcesCount - 1)); var one = context.Constant(context.TypeU32(), 1); var zero = context.Constant(context.TypeU32(), 0); - return new OperationResult(AggregateType.U32, emitU(context.TypeU32(), elemPointer, one, zero, value)); + return new OperationResult(varType, emitU(context.GetType(varType), elemPointer, one, zero, value)); } private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation) { - var value0 = context.GetU32(operation.GetSource(operation.SourcesCount - 2)); - var value1 = context.GetU32(operation.GetSource(operation.SourcesCount - 1)); + SpvInstruction elemPointer = GetStoragePointer(context, operation, out AggregateType varType); - SpvInstruction elemPointer; - - if (operation.StorageKind == StorageKind.StorageBuffer) - { - elemPointer = GetStoragePointer(context, operation, out _); - } - else if (operation.StorageKind == StorageKind.SharedMemory) - { - var offset = context.GetU32(operation.GetSource(0)); - elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset); - } - else - { - throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\"."); - } + var value0 = context.Get(varType, operation.GetSource(operation.SourcesCount - 2)); + var value1 = context.Get(varType, operation.GetSource(operation.SourcesCount - 1)); var one = context.Constant(context.TypeU32(), 1); var zero = context.Constant(context.TypeU32(), 0); - return new OperationResult(AggregateType.U32, context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, value1, value0)); + return new OperationResult(varType, context.AtomicCompareExchange(context.GetType(varType), elemPointer, one, zero, zero, value1, value0)); } private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) @@ -1928,6 +1831,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv : context.StorageBuffers[bindingIndex.Value]; break; + case StorageKind.LocalMemory: + case StorageKind.SharedMemory: + if (!(operation.GetSource(srcIndex++) is AstOperand bindingId) || bindingId.Type != OperandType.Constant) + { + throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); + } + + if (storageKind == StorageKind.LocalMemory) + { + storageClass = StorageClass.Private; + varType = context.Config.Properties.LocalMemories[bindingId.Value].Type & AggregateType.ElementTypeMask; + baseObj = context.LocalMemories[bindingId.Value]; + } + else + { + storageClass = StorageClass.Workgroup; + varType = context.Config.Properties.SharedMemories[bindingId.Value].Type & AggregateType.ElementTypeMask; + baseObj = context.SharedMemories[bindingId.Value]; + } + break; + case StorageKind.Input: case StorageKind.InputPerPatch: case StorageKind.Output: @@ -2048,50 +1972,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Load(context.GetType(varType), context.Inputs[ioDefinition]); } - private static void GenerateStoreSharedSmallInt(CodeGenContext context, AstOperation operation, int bitSize) - { - var offset = context.Get(AggregateType.U32, operation.GetSource(0)); - var value = context.Get(AggregateType.U32, operation.GetSource(1)); - - var wordOffset = context.ShiftRightLogical(context.TypeU32(), offset, context.Constant(context.TypeU32(), 2)); - var bitOffset = context.BitwiseAnd(context.TypeU32(), offset, context.Constant(context.TypeU32(), 3)); - bitOffset = context.ShiftLeftLogical(context.TypeU32(), bitOffset, context.Constant(context.TypeU32(), 3)); - - var memory = context.SharedMemory; - - var elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), memory, wordOffset); - - GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize); - } - - private static void GenerateStoreSmallInt( - CodeGenContext context, - SpvInstruction elemPointer, - SpvInstruction bitOffset, - SpvInstruction value, - int bitSize) - { - var loopStart = context.Label(); - var loopEnd = context.Label(); - - context.Branch(loopStart); - context.AddLabel(loopStart); - - var oldValue = context.Load(context.TypeU32(), elemPointer); - var newValue = context.BitFieldInsert(context.TypeU32(), oldValue, value, bitOffset, context.Constant(context.TypeU32(), bitSize)); - - var one = context.Constant(context.TypeU32(), 1); - var zero = context.Constant(context.TypeU32(), 0); - - var result = context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, newValue, oldValue); - var failed = context.INotEqual(context.TypeBool(), result, oldValue); - - context.LoopMerge(loopEnd, loopStart, LoopControlMask.MaskNone); - context.BranchConditional(failed, loopStart, loopEnd); - - context.AddLabel(loopEnd); - } - private static OperationResult GetZeroOperationResult( CodeGenContext context, AstTextureOperation texOp, diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 9aa738200..99d7bec97 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -10,12 +10,6 @@ namespace Ryujinx.Graphics.Shader.Instructions { static partial class InstEmit { - private enum MemoryRegion - { - Local, - Shared - } - public static void Atom(EmitterContext context) { InstAtom op = context.GetOp<InstAtom>(); @@ -51,7 +45,8 @@ namespace Ryujinx.Graphics.Shader.Instructions _ => AtomSize.U32 }; - Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, offset, Const(0), value); + Operand id = Const(context.Config.ResourceManager.SharedMemoryId); + Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, id, offset, value); context.Copy(GetDest(op.Dest), res); } @@ -114,14 +109,14 @@ namespace Ryujinx.Graphics.Shader.Instructions { InstLdl op = context.GetOp<InstLdl>(); - EmitLoad(context, MemoryRegion.Local, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); + EmitLoad(context, StorageKind.LocalMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); } public static void Lds(EmitterContext context) { InstLds op = context.GetOp<InstLds>(); - EmitLoad(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); + EmitLoad(context, StorageKind.SharedMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); } public static void Red(EmitterContext context) @@ -144,14 +139,14 @@ namespace Ryujinx.Graphics.Shader.Instructions { InstStl op = context.GetOp<InstStl>(); - EmitStore(context, MemoryRegion.Local, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); + EmitStore(context, StorageKind.LocalMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); } public static void Sts(EmitterContext context) { InstSts op = context.GetOp<InstSts>(); - EmitStore(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); + EmitStore(context, StorageKind.SharedMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); } private static Operand EmitLoadConstant(EmitterContext context, Operand slot, Operand offset) @@ -192,8 +187,8 @@ namespace Ryujinx.Graphics.Shader.Instructions StorageKind storageKind, AtomOp op, AtomSize type, - Operand addrLow, - Operand addrHigh, + Operand e0, + Operand e1, Operand value) { Operand res = Const(0); @@ -203,7 +198,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Add: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicAdd(storageKind, addrLow, addrHigh, value); + res = context.AtomicAdd(storageKind, e0, e1, value); } else { @@ -213,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.And: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicAnd(storageKind, addrLow, addrHigh, value); + res = context.AtomicAnd(storageKind, e0, e1, value); } else { @@ -223,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Xor: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicXor(storageKind, addrLow, addrHigh, value); + res = context.AtomicXor(storageKind, e0, e1, value); } else { @@ -233,7 +228,7 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Or: if (type == AtomSize.S32 || type == AtomSize.U32) { - res = context.AtomicOr(storageKind, addrLow, addrHigh, value); + res = context.AtomicOr(storageKind, e0, e1, value); } else { @@ -243,11 +238,11 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Max: if (type == AtomSize.S32) { - res = context.AtomicMaxS32(storageKind, addrLow, addrHigh, value); + res = context.AtomicMaxS32(storageKind, e0, e1, value); } else if (type == AtomSize.U32) { - res = context.AtomicMaxU32(storageKind, addrLow, addrHigh, value); + res = context.AtomicMaxU32(storageKind, e0, e1, value); } else { @@ -257,11 +252,11 @@ namespace Ryujinx.Graphics.Shader.Instructions case AtomOp.Min: if (type == AtomSize.S32) { - res = context.AtomicMinS32(storageKind, addrLow, addrHigh, value); + res = context.AtomicMinS32(storageKind, e0, e1, value); } else if (type == AtomSize.U32) { - res = context.AtomicMinU32(storageKind, addrLow, addrHigh, value); + res = context.AtomicMinU32(storageKind, e0, e1, value); } else { @@ -275,7 +270,7 @@ namespace Ryujinx.Graphics.Shader.Instructions private static void EmitLoad( EmitterContext context, - MemoryRegion region, + StorageKind storageKind, LsSize2 size, Operand srcA, int rd, @@ -287,19 +282,19 @@ namespace Ryujinx.Graphics.Shader.Instructions return; } + int id = storageKind == StorageKind.LocalMemory + ? context.Config.ResourceManager.LocalMemoryId + : context.Config.ResourceManager.SharedMemoryId; bool isSmallInt = size < LsSize2.B32; - int count = 1; - - switch (size) + int count = size switch { - case LsSize2.B64: count = 2; break; - case LsSize2.B128: count = 4; break; - } + LsSize2.B64 => 2, + LsSize2.B128 => 4, + _ => 1 + }; - Operand baseOffset = context.IAdd(srcA, Const(offset)); - Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2)); // Word offset = byte offset / 4 (one word = 4 bytes). - Operand bitOffset = GetBitOffset(context, baseOffset); + Operand baseOffset = context.Copy(srcA); for (int index = 0; index < count; index++) { @@ -310,14 +305,10 @@ namespace Ryujinx.Graphics.Shader.Instructions break; } - Operand elemOffset = context.IAdd(wordOffset, Const(index)); - Operand value = null; - - switch (region) - { - case MemoryRegion.Local: value = context.LoadLocal(elemOffset); break; - case MemoryRegion.Shared: value = context.LoadShared(elemOffset); break; - } + Operand byteOffset = context.IAdd(baseOffset, Const(offset + index * 4)); + Operand wordOffset = context.ShiftRightU32(byteOffset, Const(2)); // Word offset = byte offset / 4 (one word = 4 bytes). + Operand bitOffset = GetBitOffset(context, byteOffset); + Operand value = context.Load(storageKind, id, wordOffset); if (isSmallInt) { @@ -360,7 +351,7 @@ namespace Ryujinx.Graphics.Shader.Instructions private static void EmitStore( EmitterContext context, - MemoryRegion region, + StorageKind storageKind, LsSize2 size, Operand srcA, int rd, @@ -372,52 +363,54 @@ namespace Ryujinx.Graphics.Shader.Instructions return; } + int id = storageKind == StorageKind.LocalMemory + ? context.Config.ResourceManager.LocalMemoryId + : context.Config.ResourceManager.SharedMemoryId; bool isSmallInt = size < LsSize2.B32; - int count = 1; - - switch (size) + int count = size switch { - case LsSize2.B64: count = 2; break; - case LsSize2.B128: count = 4; break; - } + LsSize2.B64 => 2, + LsSize2.B128 => 4, + _ => 1 + }; - Operand baseOffset = context.IAdd(srcA, Const(offset)); - Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2)); - Operand bitOffset = GetBitOffset(context, baseOffset); + Operand baseOffset = context.Copy(srcA); for (int index = 0; index < count; index++) { bool isRz = rd + index >= RegisterConsts.RegisterZeroIndex; Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr); - Operand elemOffset = context.IAdd(wordOffset, Const(index)); + Operand byteOffset = context.IAdd(baseOffset, Const(offset + index * 4)); + Operand wordOffset = context.ShiftRightU32(byteOffset, Const(2)); + Operand bitOffset = GetBitOffset(context, byteOffset); - if (isSmallInt && region == MemoryRegion.Local) + if (isSmallInt && storageKind == StorageKind.LocalMemory) { - Operand word = context.LoadLocal(elemOffset); + Operand word = context.Load(storageKind, id, wordOffset); value = InsertSmallInt(context, (LsSize)size, bitOffset, word, value); } - if (region == MemoryRegion.Local) + if (storageKind == StorageKind.LocalMemory) { - context.StoreLocal(elemOffset, value); + context.Store(storageKind, id, wordOffset, value); } - else if (region == MemoryRegion.Shared) + else if (storageKind == StorageKind.SharedMemory) { switch (size) { case LsSize2.U8: case LsSize2.S8: - context.StoreShared8(baseOffset, value); + context.Store(StorageKind.SharedMemory8, id, byteOffset, value); break; case LsSize2.U16: case LsSize2.S16: - context.StoreShared16(baseOffset, value); + context.Store(StorageKind.SharedMemory16, id, byteOffset, value); break; default: - context.StoreShared(elemOffset, value); + context.Store(storageKind, id, wordOffset, value); break; } } diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index aecb67249..de41a2cf7 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -79,8 +79,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation ImageAtomic, IsNan, Load, - LoadLocal, - LoadShared, Lod, LogarithmB2, LogicalAnd, @@ -115,10 +113,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Sine, SquareRoot, Store, - StoreLocal, - StoreShared, - StoreShared16, - StoreShared8, Subtract, SwizzleAdd, TextureSample, diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs index 2b5dd1dec..20576a454 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs @@ -11,12 +11,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation StorageBuffer, LocalMemory, SharedMemory, + SharedMemory8, // TODO: Remove this and store type as a field on the Operation class itself. + SharedMemory16, // TODO: Remove this and store type as a field on the Operation class itself. GlobalMemory, - // TODO: Remove those and store type as a field on the Operation class itself. - GlobalMemoryS8, - GlobalMemoryS16, - GlobalMemoryU8, - GlobalMemoryU16 + GlobalMemoryS8, // TODO: Remove this and store type as a field on the Operation class itself. + GlobalMemoryS16, // TODO: Remove this and store type as a field on the Operation class itself. + GlobalMemoryU8, // TODO: Remove this and store type as a field on the Operation class itself. + GlobalMemoryU16 // TODO: Remove this and store type as a field on the Operation class itself. } static class StorageKindExtensions diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 86de2e755..b1f1fb963 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -10,14 +10,12 @@ </ItemGroup> <ItemGroup> - <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleDown.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" /> - <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" /> </ItemGroup> diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index c348b5d93..ed910f96d 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -5,15 +5,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr [Flags] enum HelperFunctionsMask { - AtomicMinMaxS32Shared = 1 << 0, - MultiplyHighS32 = 1 << 2, - MultiplyHighU32 = 1 << 3, - Shuffle = 1 << 4, - ShuffleDown = 1 << 5, - ShuffleUp = 1 << 6, - ShuffleXor = 1 << 7, - StoreSharedSmallInt = 1 << 8, - SwizzleAdd = 1 << 10, - FSI = 1 << 11 + MultiplyHighS32 = 1 << 2, + MultiplyHighU32 = 1 << 3, + Shuffle = 1 << 4, + ShuffleDown = 1 << 5, + ShuffleUp = 1 << 6, + ShuffleXor = 1 << 7, + SwizzleAdd = 1 << 10, + FSI = 1 << 11 } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 6e2013501..b08478ad3 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -90,8 +90,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ImageAtomic, AggregateType.S32); Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar); Add(Instruction.Load, AggregateType.FP32); - Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32); - Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32); Add(Instruction.Lod, AggregateType.FP32); Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.LogicalAnd, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool); @@ -121,10 +119,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.Store, AggregateType.Void); - Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32); - Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32); - Add(Instruction.StoreShared16, AggregateType.Void, AggregateType.S32, AggregateType.U32); - Add(Instruction.StoreShared8, AggregateType.Void, AggregateType.S32, AggregateType.U32); Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32); Add(Instruction.TextureSample, AggregateType.FP32); diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs new file mode 100644 index 000000000..c0bb750e7 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs @@ -0,0 +1,18 @@ +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + readonly struct MemoryDefinition + { + public string Name { get; } + public AggregateType Type { get; } + public int ArrayLength { get; } + + public MemoryDefinition(string name, AggregateType type, int arrayLength = 1) + { + Name = name; + Type = type; + ArrayLength = arrayLength; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index 157c5937d..c6132ef8c 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -6,14 +6,20 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { private readonly Dictionary<int, BufferDefinition> _constantBuffers; private readonly Dictionary<int, BufferDefinition> _storageBuffers; + private readonly Dictionary<int, MemoryDefinition> _localMemories; + private readonly Dictionary<int, MemoryDefinition> _sharedMemories; public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers; public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers; + public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories; + public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories; public ShaderProperties() { _constantBuffers = new Dictionary<int, BufferDefinition>(); _storageBuffers = new Dictionary<int, BufferDefinition>(); + _localMemories = new Dictionary<int, MemoryDefinition>(); + _sharedMemories = new Dictionary<int, MemoryDefinition>(); } public void AddConstantBuffer(int binding, BufferDefinition definition) @@ -25,5 +31,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { _storageBuffers[binding] = definition; } + + public int AddLocalMemory(MemoryDefinition definition) + { + int id = _localMemories.Count; + _localMemories.Add(id, definition); + + return id; + } + + public int AddSharedMemory(MemoryDefinition definition) + { + int id = _sharedMemories.Count; + _sharedMemories.Add(id, definition); + + return id; + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index a8f132766..9d12a73cd 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -274,13 +274,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr // decide which helper functions are needed on the final generated code. switch (operation.Inst) { - case Instruction.AtomicMaxS32: - case Instruction.AtomicMinS32: - if (operation.StorageKind == StorageKind.SharedMemory) - { - context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared; - } - break; case Instruction.MultiplyHighS32: context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32; break; @@ -299,10 +292,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr case Instruction.ShuffleXor: context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleXor; break; - case Instruction.StoreShared16: - case Instruction.StoreShared8: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreSharedSmallInt; - break; case Instruction.SwizzleAdd: context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd; break; diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index be0cba809..0ba26107c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -67,6 +67,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.AtomicAnd, storageKind, Local(), Const(binding), e0, e1, value); } + public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand compare, Operand value) + { + return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), Const(binding), e0, compare, value); + } + public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand compare, Operand value) { return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), Const(binding), e0, e1, compare, value); @@ -661,16 +666,6 @@ namespace Ryujinx.Graphics.Shader.Translation : context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex); } - public static Operand LoadLocal(this EmitterContext context, Operand a) - { - return context.Add(Instruction.LoadLocal, Local(), a); - } - - public static Operand LoadShared(this EmitterContext context, Operand a) - { - return context.Add(Instruction.LoadShared, Local(), a); - } - public static Operand MemoryBarrier(this EmitterContext context) { return context.Add(Instruction.MemoryBarrier); @@ -753,6 +748,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.Store, storageKind, null, e0, e1, value); } + public static Operand Store(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand value) + { + return context.Add(Instruction.Store, storageKind, null, Const(binding), e0, value); + } + public static Operand Store(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value) { return context.Add(Instruction.Store, storageKind, null, Const(binding), e0, e1, value); @@ -797,26 +797,6 @@ namespace Ryujinx.Graphics.Shader.Translation : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value); } - public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b) - { - return context.Add(Instruction.StoreLocal, null, a, b); - } - - public static Operand StoreShared(this EmitterContext context, Operand a, Operand b) - { - return context.Add(Instruction.StoreShared, null, a, b); - } - - public static Operand StoreShared16(this EmitterContext context, Operand a, Operand b) - { - return context.Add(Instruction.StoreShared16, null, a, b); - } - - public static Operand StoreShared8(this EmitterContext context, Operand a, Operand b) - { - return context.Add(Instruction.StoreShared8, null, a, b); - } - public static Operand UnpackDouble2x32High(this EmitterContext context, Operand a) { return UnpackDouble2x32(context, a, 1); diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs index 6958b86f2..51a396821 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs @@ -9,13 +9,13 @@ namespace Ryujinx.Graphics.Shader.Translation class HelperFunctionManager { private readonly List<Function> _functionList; - private readonly Dictionary<HelperFunctionName, int> _functionIds; + private readonly Dictionary<int, int> _functionIds; private readonly ShaderStage _stage; public HelperFunctionManager(List<Function> functionList, ShaderStage stage) { _functionList = functionList; - _functionIds = new Dictionary<HelperFunctionName, int>(); + _functionIds = new Dictionary<int, int>(); _stage = stage; } @@ -29,14 +29,30 @@ namespace Ryujinx.Graphics.Shader.Translation public int GetOrCreateFunctionId(HelperFunctionName functionName) { - if (_functionIds.TryGetValue(functionName, out int functionId)) + if (_functionIds.TryGetValue((int)functionName, out int functionId)) { return functionId; } Function function = GenerateFunction(functionName); functionId = AddFunction(function); - _functionIds.Add(functionName, functionId); + _functionIds.Add((int)functionName, functionId); + + return functionId; + } + + public int GetOrCreateFunctionId(HelperFunctionName functionName, int id) + { + int key = (int)functionName | (id << 16); + + if (_functionIds.TryGetValue(key, out int functionId)) + { + return functionId; + } + + Function function = GenerateFunction(functionName, id); + functionId = AddFunction(function); + _functionIds.Add(key, functionId); return functionId; } @@ -140,6 +156,67 @@ namespace Ryujinx.Graphics.Shader.Translation return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ConvertFloatToDouble", false, 1, 2); } + private static Function GenerateFunction(HelperFunctionName functionName, int id) + { + return functionName switch + { + HelperFunctionName.SharedAtomicMaxS32 => GenerateSharedAtomicSigned(id, isMin: false), + HelperFunctionName.SharedAtomicMinS32 => GenerateSharedAtomicSigned(id, isMin: true), + HelperFunctionName.SharedStore8 => GenerateSharedStore8(id), + HelperFunctionName.SharedStore16 => GenerateSharedStore16(id), + _ => throw new ArgumentException($"Invalid function name {functionName}") + }; + } + + private static Function GenerateSharedAtomicSigned(int id, bool isMin) + { + EmitterContext context = new EmitterContext(); + + Operand wordOffset = Argument(0); + Operand value = Argument(1); + + Operand result = GenerateSharedAtomicCasLoop(context, wordOffset, id, (memValue) => + { + return isMin + ? context.IMinimumS32(memValue, value) + : context.IMaximumS32(memValue, value); + }); + + context.Return(result); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, $"SharedAtomic{(isMin ? "Min" : "Max")}_{id}", true, 2, 0); + } + + private static Function GenerateSharedStore8(int id) + { + return GenerateSharedStore(id, 8); + } + + private static Function GenerateSharedStore16(int id) + { + return GenerateSharedStore(id, 16); + } + + private static Function GenerateSharedStore(int id, int bitSize) + { + EmitterContext context = new EmitterContext(); + + Operand offset = Argument(0); + Operand value = Argument(1); + + Operand wordOffset = context.ShiftRightU32(offset, Const(2)); + Operand bitOffset = GetBitOffset(context, offset); + + GenerateSharedAtomicCasLoop(context, wordOffset, id, (memValue) => + { + return context.BitfieldInsert(memValue, value, bitOffset, Const(bitSize)); + }); + + context.Return(); + + return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, $"SharedStore{bitSize}_{id}", false, 2, 0); + } + private Function GenerateTexelFetchScaleFunction() { EmitterContext context = new EmitterContext(); @@ -226,5 +303,29 @@ namespace Ryujinx.Graphics.Shader.Translation return context.IAdd(Const(1), index); } } + + public static Operand GetBitOffset(EmitterContext context, Operand offset) + { + return context.ShiftLeft(context.BitwiseAnd(offset, Const(3)), Const(3)); + } + + private static Operand GenerateSharedAtomicCasLoop(EmitterContext context, Operand wordOffset, int id, Func<Operand, Operand> opCallback) + { + Operand lblLoopHead = Label(); + + context.MarkLabel(lblLoopHead); + + Operand oldValue = context.Load(StorageKind.SharedMemory, id, wordOffset); + Operand newValue = opCallback(oldValue); + + Operand casResult = context.AtomicCompareAndSwap(StorageKind.SharedMemory, id, wordOffset, oldValue, newValue); + + Operand casFail = context.ICompareNotEqual(casResult, oldValue); + + context.BranchIfTrue(lblLoopHead, casFail); + + return oldValue; + } + } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs index 8c37c34c7..984f2d047 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs @@ -4,6 +4,10 @@ namespace Ryujinx.Graphics.Shader.Translation { ConvertDoubleToFloat, ConvertFloatToDouble, + SharedAtomicMaxS32, + SharedAtomicMinS32, + SharedStore8, + SharedStore16, TexelFetchScale, TextureSizeUnscale } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 14904b260..9d260c678 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -244,7 +244,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations node = nextNode; } } - else if (operation.Inst == Instruction.StoreShared || operation.Inst == Instruction.StoreLocal) + else if (operation.Inst == Instruction.Store && + (operation.StorageKind == StorageKind.SharedMemory || + operation.StorageKind == StorageKind.LocalMemory)) { // The NVIDIA compiler can sometimes use shared or local memory as temporary // storage to place the base address and size on, so we need @@ -874,7 +876,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (bitSize < 32) { - Operand bitOffset = GetBitOffset(context, offset); + Operand bitOffset = HelperFunctionManager.GetBitOffset(context, offset); GenerateAtomicCasLoop(context, wordOffset, binding, (memValue) => { @@ -892,7 +894,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (IsSmallInt(storageKind)) { - Operand bitOffset = GetBitOffset(context, offset); + Operand bitOffset = HelperFunctionManager.GetBitOffset(context, offset); switch (storageKind) { @@ -921,11 +923,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return true; } - private static Operand GetBitOffset(EmitterContext context, Operand offset) - { - return context.ShiftLeft(context.BitwiseAnd(offset, Const(3)), Const(3)); - } - private static Operand GenerateAtomicCasLoop(EmitterContext context, Operand wordOffset, int binding, Func<Operand, Operand> opCallback) { Operand lblLoopHead = Label(); @@ -1070,15 +1067,18 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { baseOffset = null; - if (operation.Inst == Instruction.LoadShared || operation.Inst == Instruction.StoreShared) + if (operation.Inst == Instruction.Load || operation.Inst == Instruction.Store) { - type = LsMemoryType.Shared; - return TryGetSharedMemoryOffsets(operation, out baseOffset, out constOffset); - } - else if (operation.Inst == Instruction.LoadLocal || operation.Inst == Instruction.StoreLocal) - { - type = LsMemoryType.Local; - return TryGetLocalMemoryOffset(operation, out constOffset); + if (operation.StorageKind == StorageKind.SharedMemory) + { + type = LsMemoryType.Shared; + return TryGetSharedMemoryOffsets(operation, out baseOffset, out constOffset); + } + else if (operation.StorageKind == StorageKind.LocalMemory) + { + type = LsMemoryType.Local; + return TryGetLocalMemoryOffset(operation, out constOffset); + } } type = default; diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 2d19a5a70..c58e4828b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Graphics.Shader.StructuredIr; using System; using System.Collections.Generic; @@ -22,9 +23,12 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly HashSet<int> _usedConstantBufferBindings; + public int LocalMemoryId { get; } + public int SharedMemoryId { get; } + public ShaderProperties Properties => _properties; - public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties) + public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties, int localMemorySize) { _gpuAccessor = gpuAccessor; _properties = properties; @@ -41,6 +45,25 @@ namespace Ryujinx.Graphics.Shader.Translation _usedConstantBufferBindings = new HashSet<int>(); properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); + + LocalMemoryId = -1; + SharedMemoryId = -1; + + if (localMemorySize != 0) + { + var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(localMemorySize, sizeof(uint))); + + LocalMemoryId = properties.AddLocalMemory(lmem); + } + + int sharedMemorySize = stage == ShaderStage.Compute ? gpuAccessor.QueryComputeSharedMemorySize() : 0; + + if (sharedMemorySize != 0) + { + var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(sharedMemorySize, sizeof(uint))); + + SharedMemoryId = properties.AddSharedMemory(smem); + } } public int GetConstantBufferBinding(int slot) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index baa88251b..f5a524a0f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -1,6 +1,8 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation.Optimizations; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -70,6 +72,15 @@ namespace Ryujinx.Graphics.Shader.Translation } } } + else + { + node = InsertSharedStoreSmallInt(hfm, node); + + if (config.Options.TargetLanguage != TargetLanguage.Spirv) + { + node = InsertSharedAtomicSigned(hfm, node); + } + } } } } @@ -171,6 +182,87 @@ namespace Ryujinx.Graphics.Shader.Translation operation.TurnIntoCopy(result); } + private static LinkedListNode<INode> InsertSharedStoreSmallInt(HelperFunctionManager hfm, LinkedListNode<INode> node) + { + Operation operation = (Operation)node.Value; + HelperFunctionName name; + + if (operation.StorageKind == StorageKind.SharedMemory8) + { + name = HelperFunctionName.SharedStore8; + } + else if (operation.StorageKind == StorageKind.SharedMemory16) + { + name = HelperFunctionName.SharedStore16; + } + else + { + return node; + } + + if (operation.Inst != Instruction.Store) + { + return node; + } + + Operand memoryId = operation.GetSource(0); + Operand byteOffset = operation.GetSource(1); + Operand value = operation.GetSource(2); + + Debug.Assert(memoryId.Type == OperandType.Constant); + + int functionId = hfm.GetOrCreateFunctionId(name, memoryId.Value); + + Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value }; + + LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, (Operand)null, callArgs)); + + Utils.DeleteNode(node, operation); + + return newNode; + } + + private static LinkedListNode<INode> InsertSharedAtomicSigned(HelperFunctionManager hfm, LinkedListNode<INode> node) + { + Operation operation = (Operation)node.Value; + HelperFunctionName name; + + if (operation.Inst == Instruction.AtomicMaxS32) + { + name = HelperFunctionName.SharedAtomicMaxS32; + } + else if (operation.Inst == Instruction.AtomicMinS32) + { + name = HelperFunctionName.SharedAtomicMinS32; + } + else + { + return node; + } + + if (operation.StorageKind != StorageKind.SharedMemory) + { + return node; + } + + Operand result = operation.Dest; + Operand memoryId = operation.GetSource(0); + Operand byteOffset = operation.GetSource(1); + Operand value = operation.GetSource(2); + + Debug.Assert(memoryId.Type == OperandType.Constant); + + int functionId = hfm.GetOrCreateFunctionId(name, memoryId.Value); + + Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value }; + + LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, result, callArgs)); + + Utils.DeleteNode(node, operation); + + return newNode; + } + private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) { TextureOperation texOp = (TextureOperation)node.Value; diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 534bda70e..fa1250022 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -124,7 +124,7 @@ namespace Ryujinx.Graphics.Shader.Translation private TextureDescriptor[] _cachedTextureDescriptors; private TextureDescriptor[] _cachedImageDescriptors; - public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options) + public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize) { Stage = stage; GpuAccessor = gpuAccessor; @@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.Shader.Translation _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); _usedImages = new Dictionary<TextureInfo, TextureMeta>(); - ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); + ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties(), localMemorySize); if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) { @@ -176,14 +176,17 @@ namespace Ryujinx.Graphics.Shader.Translation OutputTopology outputTopology, int maxOutputVertices, IGpuAccessor gpuAccessor, - TranslationOptions options) : this(stage, gpuAccessor, options) + TranslationOptions options) : this(stage, gpuAccessor, options, 0) { ThreadsPerInputPrimitive = 1; OutputTopology = outputTopology; MaxOutputVertices = maxOutputVertices; } - public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options) + public ShaderConfig( + ShaderHeader header, + IGpuAccessor gpuAccessor, + TranslationOptions options) : this(header.Stage, gpuAccessor, options, GetLocalMemorySize(header)) { GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive; @@ -197,6 +200,11 @@ namespace Ryujinx.Graphics.Shader.Translation LastInVertexPipeline = header.Stage < ShaderStage.Fragment; } + private static int GetLocalMemorySize(ShaderHeader header) + { + return header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize + (header.ShaderLocalMemoryCrsSize / ThreadsPerWarp); + } + private void EnsureTransformFeedbackInitialized() { if (HasTransformFeedbackOutputs() && _transformFeedbackOutputs == null) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index c0212a5bc..b44d6daaa 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (options.Flags.HasFlag(TranslationFlags.Compute)) { - config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options); + config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options, gpuAccessor.QueryComputeLocalMemorySize()); program = Decoder.Decode(config, address); } From f9a538bb0f02b4665f8cccbde0730e08da208024 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 17 Jun 2023 16:28:27 -0300 Subject: [PATCH 641/737] Ensure shader local and shared memory sizes are not zero (#5321) --- .../Decoders/Decoder.cs | 11 ++++ .../Instructions/InstEmitMemory.cs | 18 +++++++ .../Translation/FeatureFlags.cs | 4 +- .../Translation/ResourceManager.cs | 51 ++++++++++++++----- .../Translation/ShaderConfig.cs | 10 ++-- .../Translation/TranslatorContext.cs | 12 +++++ 6 files changed, 88 insertions(+), 18 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index c619b9bbc..4e6c6a5df 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -247,6 +247,17 @@ namespace Ryujinx.Graphics.Shader.Decoders { block.AddPushOp(op); } + else if (op.Name == InstName.Ldl || op.Name == InstName.Stl) + { + config.SetUsedFeature(FeatureFlags.LocalMemory); + } + else if (op.Name == InstName.Atoms || + op.Name == InstName.AtomsCas || + op.Name == InstName.Lds || + op.Name == InstName.Sts) + { + config.SetUsedFeature(FeatureFlags.SharedMemory); + } block.OpCodes.Add(op); diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 99d7bec97..40312f4a4 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -27,6 +27,12 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Atoms(EmitterContext context) { + if (context.Config.Stage != ShaderStage.Compute) + { + context.Config.GpuAccessor.Log($"Atoms instruction is not valid on \"{context.Config.Stage}\" stage."); + return; + } + InstAtoms op = context.GetOp<InstAtoms>(); Operand offset = context.ShiftRightU32(GetSrcReg(context, op.SrcA), Const(2)); @@ -114,6 +120,12 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Lds(EmitterContext context) { + if (context.Config.Stage != ShaderStage.Compute) + { + context.Config.GpuAccessor.Log($"Lds instruction is not valid on \"{context.Config.Stage}\" stage."); + return; + } + InstLds op = context.GetOp<InstLds>(); EmitLoad(context, StorageKind.SharedMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); @@ -144,6 +156,12 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Sts(EmitterContext context) { + if (context.Config.Stage != ShaderStage.Compute) + { + context.Config.GpuAccessor.Log($"Sts instruction is not valid on \"{context.Config.Stage}\" stage."); + return; + } + InstSts op = context.GetOp<InstSts>(); EmitStore(context, StorageKind.SharedMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24)); diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index e55ed13da..59d35d906 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -21,6 +21,8 @@ namespace Ryujinx.Graphics.Shader.Translation RtLayer = 1 << 5, IaIndexing = 1 << 7, OaIndexing = 1 << 8, - FixedFuncAttr = 1 << 9 + FixedFuncAttr = 1 << 9, + LocalMemory = 1 << 10, + SharedMemory = 1 << 11 } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index c58e4828b..3a46f6e4e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -8,6 +8,11 @@ namespace Ryujinx.Graphics.Shader.Translation { class ResourceManager { + // Those values are used if the shader as local or shared memory access, + // but for some reason the supplied size was 0. + private const int DefaultLocalMemorySize = 128; + private const int DefaultSharedMemorySize = 4096; + private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private readonly IGpuAccessor _gpuAccessor; @@ -23,12 +28,12 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly HashSet<int> _usedConstantBufferBindings; - public int LocalMemoryId { get; } - public int SharedMemoryId { get; } + public int LocalMemoryId { get; private set; } + public int SharedMemoryId { get; private set; } public ShaderProperties Properties => _properties; - public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties, int localMemorySize) + public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties) { _gpuAccessor = gpuAccessor; _properties = properties; @@ -48,21 +53,43 @@ namespace Ryujinx.Graphics.Shader.Translation LocalMemoryId = -1; SharedMemoryId = -1; + } - if (localMemorySize != 0) + public void SetCurrentLocalMemory(int size, bool isUsed) + { + if (isUsed) { - var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(localMemorySize, sizeof(uint))); + if (size <= 0) + { + size = DefaultLocalMemorySize; + } - LocalMemoryId = properties.AddLocalMemory(lmem); + var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); + + LocalMemoryId = Properties.AddLocalMemory(lmem); } - - int sharedMemorySize = stage == ShaderStage.Compute ? gpuAccessor.QueryComputeSharedMemorySize() : 0; - - if (sharedMemorySize != 0) + else { - var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(sharedMemorySize, sizeof(uint))); + LocalMemoryId = -1; + } + } - SharedMemoryId = properties.AddSharedMemory(smem); + public void SetCurrentSharedMemory(int size, bool isUsed) + { + if (isUsed) + { + if (size <= 0) + { + size = DefaultSharedMemorySize; + } + + var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); + + SharedMemoryId = Properties.AddSharedMemory(smem); + } + else + { + SharedMemoryId = -1; } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index fa1250022..e50c9a845 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -126,9 +126,10 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize) { - Stage = stage; - GpuAccessor = gpuAccessor; - Options = options; + Stage = stage; + GpuAccessor = gpuAccessor; + Options = options; + LocalMemorySize = localMemorySize; _transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>(); @@ -143,7 +144,7 @@ namespace Ryujinx.Graphics.Shader.Translation _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); _usedImages = new Dictionary<TextureInfo, TextureMeta>(); - ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties(), localMemorySize); + ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) { @@ -192,7 +193,6 @@ namespace Ryujinx.Graphics.Shader.Translation ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive; OutputTopology = header.OutputTopology; MaxOutputVertices = header.MaxOutputVertexCount; - LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize + (header.ShaderLocalMemoryCrsSize / ThreadsPerWarp); ImapTypes = header.ImapTypes; OmapTargets = header.OmapTargets; OmapSampleMask = header.OmapSampleMask; diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 9647b13f1..13c5e0e40 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -149,6 +149,17 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderProgram Translate(TranslatorContext other = null) { + bool usesLocalMemory = _config.UsedFeatures.HasFlag(FeatureFlags.LocalMemory); + + _config.ResourceManager.SetCurrentLocalMemory(_config.LocalMemorySize, usesLocalMemory); + + if (_config.Stage == ShaderStage.Compute) + { + bool usesSharedMemory = _config.UsedFeatures.HasFlag(FeatureFlags.SharedMemory); + + _config.ResourceManager.SetCurrentSharedMemory(GpuAccessor.QueryComputeSharedMemorySize(), usesSharedMemory); + } + FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _); if (other != null) @@ -157,6 +168,7 @@ namespace Ryujinx.Graphics.Shader.Translation // We need to share the resource manager since both shaders accesses the same constant buffers. other._config.ResourceManager = _config.ResourceManager; + other._config.ResourceManager.SetCurrentLocalMemory(other._config.LocalMemorySize, other._config.UsedFeatures.HasFlag(FeatureFlags.LocalMemory)); FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart); From 649d372f7da8559f8b6d74ca44af64ba7d7853c4 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Tue, 20 Jun 2023 17:33:54 +0200 Subject: [PATCH 642/737] misc: Implement address space size workarounds (#5191) * misc: Implement address space size workarounds This adds code to support userland with less than 39 bits of address space available by testing reserving multiple sizes and reducing guess address space when needed. This is required for ARM64 support when the kernel is configured to use 63..39 bits for kernel space.(meaning only 38 bits is available to userland) * Address comments * Fix 32 bits address space support and address more comments --- src/Ryujinx.Cpu/AddressSpace.cs | 45 ++++- .../Jit/MemoryManagerHostMapped.cs | 19 +- src/Ryujinx.HLE/HOS/ArmProcessContext.cs | 12 +- .../HOS/ArmProcessContextFactory.cs | 29 ++- .../HOS/Kernel/Memory/KPageTable.cs | 2 +- .../HOS/Kernel/Memory/KPageTableBase.cs | 180 +++++++++--------- .../HOS/Kernel/Process/IProcessContext.cs | 2 + .../HOS/Kernel/Process/KProcess.cs | 2 +- .../HOS/Kernel/Process/ProcessContext.cs | 5 +- .../Kernel/Process/ProcessContextFactory.cs | 2 +- 10 files changed, 187 insertions(+), 111 deletions(-) diff --git a/src/Ryujinx.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs index 9dc32426c..0e27b1582 100644 --- a/src/Ryujinx.Cpu/AddressSpace.cs +++ b/src/Ryujinx.Cpu/AddressSpace.cs @@ -5,7 +5,7 @@ using System; namespace Ryujinx.Cpu { - class AddressSpace : IDisposable + public class AddressSpace : IDisposable { private const ulong PageSize = 0x1000; @@ -154,7 +154,9 @@ namespace Ryujinx.Cpu public MemoryBlock Base { get; } public MemoryBlock Mirror { get; } - public AddressSpace(MemoryBlock backingMemory, ulong asSize, bool supports4KBPages) + public ulong AddressSpaceSize { get; } + + public AddressSpace(MemoryBlock backingMemory, MemoryBlock baseMemory, MemoryBlock mirrorMemory, ulong addressSpaceSize, bool supports4KBPages) { if (!supports4KBPages) { @@ -163,17 +165,48 @@ namespace Ryujinx.Cpu _privateTree = new IntrusiveRedBlackTree<PrivateMapping>(); _treeLock = new object(); - _mappingTree.Add(new Mapping(0UL, asSize, MappingType.None)); - _privateTree.Add(new PrivateMapping(0UL, asSize, default)); + _mappingTree.Add(new Mapping(0UL, addressSpaceSize, MappingType.None)); + _privateTree.Add(new PrivateMapping(0UL, addressSpaceSize, default)); } _backingMemory = backingMemory; _supports4KBPages = supports4KBPages; + Base = baseMemory; + Mirror = mirrorMemory; + AddressSpaceSize = addressSpaceSize; + } + + public static bool TryCreate(MemoryBlock backingMemory, ulong asSize, bool supports4KBPages, out AddressSpace addressSpace) + { + addressSpace = null; + MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; - Base = new MemoryBlock(asSize, asFlags); - Mirror = new MemoryBlock(asSize, asFlags); + ulong minAddressSpaceSize = Math.Min(asSize, 1UL << 36); + + // Attempt to create the address space with expected size or try to reduce it until it succeed. + for (ulong addressSpaceSize = asSize; addressSpaceSize >= minAddressSpaceSize; addressSpaceSize >>= 1) + { + MemoryBlock baseMemory = null; + MemoryBlock mirrorMemory = null; + + try + { + baseMemory = new MemoryBlock(addressSpaceSize, asFlags); + mirrorMemory = new MemoryBlock(addressSpaceSize, asFlags); + addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize, supports4KBPages); + + break; + } + catch (OutOfMemoryException) + { + baseMemory?.Dispose(); + mirrorMemory?.Dispose(); + } + } + + return addressSpace != null; } public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 363f9000d..3686eb088 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -38,7 +38,8 @@ namespace Ryujinx.Cpu.Jit private readonly bool _unsafeMode; private readonly AddressSpace _addressSpace; - private readonly ulong _addressSpaceSize; + + public ulong AddressSpaceSize { get; } private readonly PageTable<ulong> _pageTable; @@ -62,21 +63,21 @@ namespace Ryujinx.Cpu.Jit /// <summary> /// Creates a new instance of the host mapped memory manager. /// </summary> - /// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param> - /// <param name="addressSpaceSize">Size of the address space</param> + /// <param name="addressSpace">Address space instance to use</param> /// <param name="unsafeMode">True if unmanaged access should not be masked (unsafe), false otherwise.</param> /// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param> - public MemoryManagerHostMapped(MemoryBlock backingMemory, ulong addressSpaceSize, bool unsafeMode, InvalidAccessHandler invalidAccessHandler = null) + public MemoryManagerHostMapped(AddressSpace addressSpace, bool unsafeMode, InvalidAccessHandler invalidAccessHandler) { + _addressSpace = addressSpace; _pageTable = new PageTable<ulong>(); _invalidAccessHandler = invalidAccessHandler; _unsafeMode = unsafeMode; - _addressSpaceSize = addressSpaceSize; + AddressSpaceSize = addressSpace.AddressSpaceSize; ulong asSize = PageSize; int asBits = PageBits; - while (asSize < addressSpaceSize) + while (asSize < AddressSpaceSize) { asSize <<= 1; asBits++; @@ -86,8 +87,6 @@ namespace Ryujinx.Cpu.Jit _pageBitmap = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))]; - _addressSpace = new AddressSpace(backingMemory, asSize, Supports4KBPages); - Tracking = new MemoryTracking(this, (int)MemoryBlock.GetPageSize(), invalidAccessHandler); _memoryEh = new MemoryEhMeilleure(_addressSpace.Base, _addressSpace.Mirror, Tracking); } @@ -99,7 +98,7 @@ namespace Ryujinx.Cpu.Jit /// <returns>True if the virtual address is part of the addressable space</returns> private bool ValidateAddress(ulong va) { - return va < _addressSpaceSize; + return va < AddressSpaceSize; } /// <summary> @@ -111,7 +110,7 @@ namespace Ryujinx.Cpu.Jit private bool ValidateAddressAndSize(ulong va, ulong size) { ulong endVa = va + size; - return endVa >= va && endVa >= size && endVa <= _addressSpaceSize; + return endVa >= va && endVa >= size && endVa <= AddressSpaceSize; } /// <summary> diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContext.cs b/src/Ryujinx.HLE/HOS/ArmProcessContext.cs index 6338edc1e..99b355286 100644 --- a/src/Ryujinx.HLE/HOS/ArmProcessContext.cs +++ b/src/Ryujinx.HLE/HOS/ArmProcessContext.cs @@ -25,7 +25,15 @@ namespace Ryujinx.HLE.HOS public IVirtualMemoryManager AddressSpace => _memoryManager; - public ArmProcessContext(ulong pid, ICpuEngine cpuEngine, GpuContext gpuContext, T memoryManager, bool for64Bit) + public ulong AddressSpaceSize { get; } + + public ArmProcessContext( + ulong pid, + ICpuEngine cpuEngine, + GpuContext gpuContext, + T memoryManager, + ulong addressSpaceSize, + bool for64Bit) { if (memoryManager is IRefCounted rc) { @@ -38,6 +46,8 @@ namespace Ryujinx.HLE.HOS _gpuContext = gpuContext; _cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit); _memoryManager = memoryManager; + + AddressSpaceSize = addressSpaceSize; } public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks) diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 1b0d66ac6..140b10a26 100644 --- a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.Cpu.AppleHv; using Ryujinx.Cpu.Jit; @@ -49,7 +50,7 @@ namespace Ryujinx.HLE.HOS { var cpuEngine = new HvEngine(_tickSource); var memoryManager = new HvMemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); - processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, for64Bit); + processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit); } else { @@ -57,23 +58,41 @@ namespace Ryujinx.HLE.HOS if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) { + Logger.Warning?.Print(LogClass.Cpu, "Host system doesn't support views, falling back to software page table"); + mode = MemoryManagerMode.SoftwarePageTable; } var cpuEngine = new JitEngine(_tickSource); + AddressSpace addressSpace = null; + + if (mode == MemoryManagerMode.HostMapped || mode == MemoryManagerMode.HostMappedUnsafe) + { + if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, MemoryBlock.GetPageSize() == MemoryManagerHostMapped.PageSize, out addressSpace)) + { + Logger.Warning?.Print(LogClass.Cpu, "Address space creation failed, falling back to software page table"); + + mode = MemoryManagerMode.SoftwarePageTable; + } + } + switch (mode) { case MemoryManagerMode.SoftwarePageTable: var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); - processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, memoryManager, for64Bit); + processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit); break; case MemoryManagerMode.HostMapped: case MemoryManagerMode.HostMappedUnsafe: - bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; - var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler); - processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); + if (addressSpaceSize != addressSpace.AddressSpaceSize) + { + Logger.Warning?.Print(LogClass.Emulation, $"Allocated address space (0x{addressSpace.AddressSpaceSize:X}) is smaller than guest application requirements (0x{addressSpaceSize:X})"); + } + + var memoryManagerHostMapped = new MemoryManagerHostMapped(addressSpace, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler); + processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, memoryManagerHostMapped, addressSpace.AddressSpaceSize, for64Bit); break; default: diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs index 28e9f90aa..119034c16 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory protected override bool Supports4KBPages => _cpuMemory.Supports4KBPages; - public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context) + public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory, ulong reservedAddressSpaceSize) : base(context, reservedAddressSpaceSize) { _cpuMemory = cpuMemory; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index 614eb5271..6746a0a7a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -22,6 +22,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory 0x40000000 }; + private const ulong RegionAlignment = 0x200000; + public const int PageSize = 0x1000; private const int KMemoryBlockSize = 0x40; @@ -53,6 +55,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public ulong TlsIoRegionStart { get; private set; } public ulong TlsIoRegionEnd { get; private set; } + public ulong AslrRegionStart { get; private set; } + public ulong AslrRegionEnd { get; private set; } + private ulong _heapCapacity; public ulong PhysicalMemoryUsage { get; private set; } @@ -61,10 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private MemoryRegion _memRegion; - private bool _aslrDisabled; - - public int AddrSpaceWidth { get; private set; } - + private bool _allocateFromBack; private bool _isKernel; private bool _aslrEnabled; @@ -78,7 +80,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private MemoryFillValue _heapFillValue; private MemoryFillValue _ipcFillValue; - public KPageTableBase(KernelContext context) + private ulong _reservedAddressSpaceSize; + + public KPageTableBase(KernelContext context, ulong reservedAddressSpaceSize) { Context = context; @@ -88,6 +92,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _heapFillValue = MemoryFillValue.Zero; _ipcFillValue = MemoryFillValue.Zero; + + _reservedAddressSpaceSize = reservedAddressSpaceSize; } private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 }; @@ -95,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public Result InitializeForProcess( AddressSpaceType addrSpaceType, bool aslrEnabled, - bool aslrDisabled, + bool fromBack, MemoryRegion memRegion, ulong address, ulong size, @@ -114,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Result result = CreateUserAddressSpace( addrSpaceType, aslrEnabled, - aslrDisabled, + fromBack, addrSpaceBase, addrSpaceSize, memRegion, @@ -130,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - private class Region + private struct Region { public ulong Start; public ulong End; @@ -141,7 +147,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private Result CreateUserAddressSpace( AddressSpaceType addrSpaceType, bool aslrEnabled, - bool aslrDisabled, + bool fromBack, ulong addrSpaceStart, ulong addrSpaceEnd, MemoryRegion memRegion, @@ -159,7 +165,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong codeRegionSize; ulong stackAndTlsIoStart; ulong stackAndTlsIoEnd; - ulong baseAddress; switch (addrSpaceType) { @@ -170,10 +175,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory tlsIoRegion.Size = 0; CodeRegionStart = 0x200000; codeRegionSize = 0x3fe00000; + AslrRegionStart = 0x200000; + AslrRegionEnd = AslrRegionStart + 0xffe00000; stackAndTlsIoStart = 0x200000; stackAndTlsIoEnd = 0x40000000; - baseAddress = 0x200000; - AddrSpaceWidth = 32; break; case AddressSpaceType.Addr36Bits: @@ -183,10 +188,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory tlsIoRegion.Size = 0; CodeRegionStart = 0x8000000; codeRegionSize = 0x78000000; + AslrRegionStart = 0x8000000; + AslrRegionEnd = AslrRegionStart + 0xff8000000; stackAndTlsIoStart = 0x8000000; stackAndTlsIoEnd = 0x80000000; - baseAddress = 0x8000000; - AddrSpaceWidth = 36; break; case AddressSpaceType.Addr32BitsNoMap: @@ -196,23 +201,42 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory tlsIoRegion.Size = 0; CodeRegionStart = 0x200000; codeRegionSize = 0x3fe00000; + AslrRegionStart = 0x200000; + AslrRegionEnd = AslrRegionStart + 0xffe00000; stackAndTlsIoStart = 0x200000; stackAndTlsIoEnd = 0x40000000; - baseAddress = 0x200000; - AddrSpaceWidth = 32; break; case AddressSpaceType.Addr39Bits: - aliasRegion.Size = 0x1000000000; - heapRegion.Size = 0x180000000; - stackRegion.Size = 0x80000000; - tlsIoRegion.Size = 0x1000000000; - CodeRegionStart = BitUtils.AlignDown<ulong>(address, 0x200000); - codeRegionSize = BitUtils.AlignUp<ulong>(endAddr, 0x200000) - CodeRegionStart; - stackAndTlsIoStart = 0; - stackAndTlsIoEnd = 0; - baseAddress = 0x8000000; - AddrSpaceWidth = 39; + if (_reservedAddressSpaceSize < addrSpaceEnd) + { + int addressSpaceWidth = (int)ulong.Log2(_reservedAddressSpaceSize); + + aliasRegion.Size = 1UL << (addressSpaceWidth - 3); + heapRegion.Size = 0x180000000; + stackRegion.Size = 1UL << (addressSpaceWidth - 8); + tlsIoRegion.Size = 1UL << (addressSpaceWidth - 3); + CodeRegionStart = BitUtils.AlignDown<ulong>(address, RegionAlignment); + codeRegionSize = BitUtils.AlignUp<ulong>(endAddr, RegionAlignment) - CodeRegionStart; + stackAndTlsIoStart = 0; + stackAndTlsIoEnd = 0; + AslrRegionStart = 0x8000000; + addrSpaceEnd = 1UL << addressSpaceWidth; + AslrRegionEnd = addrSpaceEnd; + } + else + { + aliasRegion.Size = 0x1000000000; + heapRegion.Size = 0x180000000; + stackRegion.Size = 0x80000000; + tlsIoRegion.Size = 0x1000000000; + CodeRegionStart = BitUtils.AlignDown(address, RegionAlignment); + codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart; + AslrRegionStart = 0x8000000; + AslrRegionEnd = AslrRegionStart + 0x7ff8000000; + stackAndTlsIoStart = 0; + stackAndTlsIoEnd = 0; + } break; default: throw new ArgumentException(nameof(addrSpaceType)); @@ -223,11 +247,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong mapBaseAddress; ulong mapAvailableSize; - if (CodeRegionStart - baseAddress >= addrSpaceEnd - CodeRegionEnd) + if (CodeRegionStart - AslrRegionStart >= addrSpaceEnd - CodeRegionEnd) { // Has more space before the start of the code region. - mapBaseAddress = baseAddress; - mapAvailableSize = CodeRegionStart - baseAddress; + mapBaseAddress = AslrRegionStart; + mapAvailableSize = CodeRegionStart - AslrRegionStart; } else { @@ -254,14 +278,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (aslrEnabled) { - aliasRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21; - heapRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21; - stackRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21; - tlsIoRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21; + aliasRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset / RegionAlignment) * RegionAlignment; + heapRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset / RegionAlignment) * RegionAlignment; + stackRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset / RegionAlignment) * RegionAlignment; + tlsIoRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset / RegionAlignment) * RegionAlignment; } // Regions are sorted based on ASLR offset. - // When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo. + // When ASLR is disabled, the order is Alias, Heap, Stack and TlsIo. aliasRegion.Start = mapBaseAddress + aliasRegion.AslrOffset; aliasRegion.End = aliasRegion.Start + aliasRegion.Size; heapRegion.Start = mapBaseAddress + heapRegion.AslrOffset; @@ -271,12 +295,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory tlsIoRegion.Start = mapBaseAddress + tlsIoRegion.AslrOffset; tlsIoRegion.End = tlsIoRegion.Start + tlsIoRegion.Size; - SortRegion(heapRegion, aliasRegion); + SortRegion(ref aliasRegion, ref heapRegion, true); if (stackRegion.Size != 0) { - SortRegion(stackRegion, aliasRegion); - SortRegion(stackRegion, heapRegion); + stackRegion.Start = mapBaseAddress + stackRegion.AslrOffset; + stackRegion.End = stackRegion.Start + stackRegion.Size; + + SortRegion(ref aliasRegion, ref stackRegion); + SortRegion(ref heapRegion, ref stackRegion); } else { @@ -286,9 +313,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (tlsIoRegion.Size != 0) { - SortRegion(tlsIoRegion, aliasRegion); - SortRegion(tlsIoRegion, heapRegion); - SortRegion(tlsIoRegion, stackRegion); + tlsIoRegion.Start = mapBaseAddress + tlsIoRegion.AslrOffset; + tlsIoRegion.End = tlsIoRegion.Start + tlsIoRegion.Size; + + SortRegion(ref aliasRegion, ref tlsIoRegion); + SortRegion(ref heapRegion, ref tlsIoRegion); + + if (stackRegion.Size != 0) + { + SortRegion(ref stackRegion, ref tlsIoRegion); + } } else { @@ -312,11 +346,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory PhysicalMemoryUsage = 0; _memRegion = memRegion; - _aslrDisabled = aslrDisabled; + _allocateFromBack = fromBack; return _blockManager.Initialize(addrSpaceStart, addrSpaceEnd, slabManager); } + private static void SortRegion(ref Region lhs, ref Region rhs, bool checkForEquality = false) + { + bool res = checkForEquality ? lhs.AslrOffset <= rhs.AslrOffset : lhs.AslrOffset < rhs.AslrOffset; + + if (res) + { + rhs.Start += lhs.Size; + rhs.End += lhs.Size; + } + else + { + lhs.Start += rhs.Size; + lhs.End += rhs.Size; + } + } + private ulong GetRandomValue(ulong min, ulong max) { return (ulong)GetRandomValue((long)min, (long)max); @@ -332,20 +382,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return _randomNumberGenerator.GenRandomNumber(min, max); } - private static void SortRegion(Region lhs, Region rhs) - { - if (lhs.AslrOffset < rhs.AslrOffset) - { - rhs.Start += lhs.Size; - rhs.End += lhs.Size; - } - else - { - lhs.Start += rhs.Size; - lhs.End += rhs.Size; - } - } - public Result MapPages(ulong address, KPageList pageList, MemoryState state, KMemoryPermission permission) { ulong pagesCount = pageList.GetPagesCount(); @@ -1827,7 +1863,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory // If not, allocate a new page and copy the unaligned chunck. if (addressTruncated < addressRounded) { - dstFirstPagePa = GetMemoryRegionManager().AllocatePagesContiguous(Context, 1, _aslrDisabled); + dstFirstPagePa = GetMemoryRegionManager().AllocatePagesContiguous(Context, 1, _allocateFromBack); if (dstFirstPagePa == 0) { @@ -1841,7 +1877,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory // If not, allocate a new page and copy the unaligned chunck. if (endAddrTruncated < endAddrRounded && (addressTruncated == addressRounded || addressTruncated < endAddrTruncated)) { - dstLastPagePa = GetMemoryRegionManager().AllocatePagesContiguous(Context, 1, _aslrDisabled); + dstLastPagePa = GetMemoryRegionManager().AllocatePagesContiguous(Context, 1, _allocateFromBack); if (dstLastPagePa == 0) { @@ -2799,38 +2835,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public ulong GetAddrSpaceBaseAddr() { - if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39) - { - return 0x8000000; - } - else if (AddrSpaceWidth == 32) - { - return 0x200000; - } - else - { - throw new InvalidOperationException("Invalid address space width!"); - } + return AslrRegionStart; } public ulong GetAddrSpaceSize() { - if (AddrSpaceWidth == 36) - { - return 0xff8000000; - } - else if (AddrSpaceWidth == 39) - { - return 0x7ff8000000; - } - else if (AddrSpaceWidth == 32) - { - return 0xffe00000; - } - else - { - throw new InvalidOperationException("Invalid address space width!"); - } + return AslrRegionEnd - AslrRegionStart; } private static ulong GetDramAddressFromPa(ulong pa) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs index c8063a62a..bdfef9d7a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs @@ -8,6 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { IVirtualMemoryManager AddressSpace { get; } + ulong AddressSpaceSize { get; } + IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks); void Execute(IExecutionContext context, ulong codeAddress); void InvalidateCacheRegion(ulong address, ulong size); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 510c99ea3..c284243ae 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -1082,7 +1082,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit); - MemoryManager = new KPageTable(KernelContext, CpuMemory); + MemoryManager = new KPageTable(KernelContext, CpuMemory, Context.AddressSpaceSize); } private bool InvalidAccessHandler(ulong va) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs index 872968309..cab5c6082 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs @@ -8,9 +8,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { public IVirtualMemoryManager AddressSpace { get; } - public ProcessContext(IVirtualMemoryManager asManager) + public ulong AddressSpaceSize { get; } + + public ProcessContext(IVirtualMemoryManager asManager, ulong addressSpaceSize) { AddressSpace = asManager; + AddressSpaceSize = addressSpaceSize; } public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs index 1c5798b40..60dd5abf1 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit) { - return new ProcessContext(new AddressSpaceManager(context.Memory, addressSpaceSize)); + return new ProcessContext(new AddressSpaceManager(context.Memory, addressSpaceSize), addressSpaceSize); } } } From 58907e2c290473326e5ab74bdfe1429b8a518ba4 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Thu, 22 Jun 2023 13:36:07 -0300 Subject: [PATCH 643/737] GetHashCode should not reference mutable fields (#5331) --- src/ARMeilleure/State/V128.cs | 4 ++-- src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs | 2 +- src/Ryujinx.Tests.Unicorn/SimdValue.cs | 4 ++-- src/Spv.Generator/ConstantKey.cs | 2 +- src/Spv.Generator/DeterministicStringKey.cs | 2 +- src/Spv.Generator/LiteralString.cs | 2 +- src/Spv.Generator/TypeDeclarationKey.cs | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ARMeilleure/State/V128.cs b/src/ARMeilleure/State/V128.cs index 3fa9f9a99..441bbfa6d 100644 --- a/src/ARMeilleure/State/V128.cs +++ b/src/ARMeilleure/State/V128.cs @@ -13,8 +13,8 @@ namespace ARMeilleure.State // _e0 & _e1 could be marked as readonly, however they are not readonly because we modify them through the Unsafe // APIs. This also means that one should be careful when changing the layout of this struct. - private ulong _e0; - private ulong _e1; + private readonly ulong _e0; + private readonly ulong _e1; /// <summary> /// Gets a new <see cref="V128"/> with all bits set to zero. diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs index c1a97f526..283d82fc8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] struct CreateId : IEquatable<CreateId> { - public UInt128 Raw; + public readonly UInt128 Raw; public bool IsNull => Raw == UInt128.Zero; public bool IsValid => !IsNull && ((Raw >> 64) & 0xC0) == 0x80; diff --git a/src/Ryujinx.Tests.Unicorn/SimdValue.cs b/src/Ryujinx.Tests.Unicorn/SimdValue.cs index 2b5284305..ce9e178cc 100644 --- a/src/Ryujinx.Tests.Unicorn/SimdValue.cs +++ b/src/Ryujinx.Tests.Unicorn/SimdValue.cs @@ -4,8 +4,8 @@ namespace Ryujinx.Tests.Unicorn { public struct SimdValue : IEquatable<SimdValue> { - private ulong _e0; - private ulong _e1; + private readonly ulong _e0; + private readonly ulong _e1; public SimdValue(ulong e0, ulong e1) { diff --git a/src/Spv.Generator/ConstantKey.cs b/src/Spv.Generator/ConstantKey.cs index d3c1b905a..e7758b408 100644 --- a/src/Spv.Generator/ConstantKey.cs +++ b/src/Spv.Generator/ConstantKey.cs @@ -5,7 +5,7 @@ namespace Spv.Generator { internal struct ConstantKey : IEquatable<ConstantKey> { - private Instruction _constant; + private readonly Instruction _constant; public ConstantKey(Instruction constant) { diff --git a/src/Spv.Generator/DeterministicStringKey.cs b/src/Spv.Generator/DeterministicStringKey.cs index 491bb745a..cab4dbcc2 100644 --- a/src/Spv.Generator/DeterministicStringKey.cs +++ b/src/Spv.Generator/DeterministicStringKey.cs @@ -5,7 +5,7 @@ namespace Spv.Generator { internal class DeterministicStringKey : IEquatable<DeterministicStringKey> { - private string _value; + private readonly string _value; public DeterministicStringKey(string value) { diff --git a/src/Spv.Generator/LiteralString.cs b/src/Spv.Generator/LiteralString.cs index 629ff7bf6..741d922bb 100644 --- a/src/Spv.Generator/LiteralString.cs +++ b/src/Spv.Generator/LiteralString.cs @@ -8,7 +8,7 @@ namespace Spv.Generator { public OperandType Type => OperandType.String; - private string _value; + private readonly string _value; public LiteralString(string value) { diff --git a/src/Spv.Generator/TypeDeclarationKey.cs b/src/Spv.Generator/TypeDeclarationKey.cs index a4aa95634..e4fd5fd57 100644 --- a/src/Spv.Generator/TypeDeclarationKey.cs +++ b/src/Spv.Generator/TypeDeclarationKey.cs @@ -5,7 +5,7 @@ namespace Spv.Generator { internal struct TypeDeclarationKey : IEquatable<TypeDeclarationKey> { - private Instruction _typeDeclaration; + private readonly Instruction _typeDeclaration; public TypeDeclarationKey(Instruction typeDeclaration) { From d604e982276105db043ca495a16f1b047bb2d0f6 Mon Sep 17 00:00:00 2001 From: Kurochi51 <andrei5125@gmail.com> Date: Thu, 22 Jun 2023 22:35:06 +0300 Subject: [PATCH 644/737] Fix regression introduced by 1.1.1733 on Intel GPUs (#5311) * Fix regression introduced by 1.1733 on Intel iGPUs * Should have actually figured the variable, oops. * maybe something goes wrong here? honestly lost * Shader cache bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 5cfbfd386..267112865 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5241; + private const uint CodeGenVersion = 5311; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index bb60d2746..76b2e0783 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { // gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs. // This weird trick makes it behave. - res = context.ICompareLess(context.INegate(context.IConvertS32ToFP32(res)), Const(0)); + res = context.ICompareLess(context.INegate(context.FP32ConvertToS32(context.ConditionalSelect(res, ConstF(1f), ConstF(0f)))), Const(0)); } } From 7608cb37ab798db49f33f3870f2f84fbe0809266 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Thu, 22 Jun 2023 20:37:25 -0300 Subject: [PATCH 645/737] "Exists" method should be used instead of the "Any" extension (#5345) --- src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs | 2 +- src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index fd66269dd..da8dd849d 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -512,7 +512,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>True if at least one of the handles is dirty</returns> private bool CheckDirty() { - return Handles.Any(handle => handle.Dirty); + return Array.Exists(Handles, handle => handle.Dirty); } /// <summary> diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index 222cdf2a7..2a3f86579 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -256,7 +256,7 @@ namespace Ryujinx.Graphics.Vulkan { await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); - if (_shaders.Any(shader => shader.CompileStatus == ProgramLinkStatus.Failure)) + if (Array.Exists(_shaders, shader => shader.CompileStatus == ProgramLinkStatus.Failure)) { LinkStatus = ProgramLinkStatus.Failure; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index 74867b44e..57fbabd51 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -206,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading WakeThreads(_condVarThreads, count, TryAcquireMutex, x => x.CondVarAddress == address); - if (!_condVarThreads.Any(x => x.CondVarAddress == address)) + if (!_condVarThreads.Exists(x => x.CondVarAddress == address)) { KernelTransfer.KernelToUser(address, 0); } diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 9166e87fa..9e1db7fcd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); - if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId)) + if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId)) { _openedApplicationAreaId = applicationAreaId; @@ -124,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); - if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId)) + if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId)) { return false; } @@ -144,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); - if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == _openedApplicationAreaId)) + if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == _openedApplicationAreaId)) { for (int i = 0; i < virtualAmiiboFile.ApplicationAreas.Count; i++) { From efbd29463d2eb38cd177818a170f245556852f17 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Thu, 22 Jun 2023 20:42:23 -0300 Subject: [PATCH 646/737] "Find" method should be used instead of the "FirstOrDefault" extension (#5344) --- src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs | 4 ++-- .../Loaders/Processes/Extensions/FileSystemExtensions.cs | 3 ++- src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs | 3 ++- src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs | 3 ++- src/Ryujinx/Ui/Windows/AmiiboWindow.cs | 4 ++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index ead1a1442..ecd425ef2 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -264,7 +264,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void SelectLastScannedAmiibo() { - AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); + AmiiboApi scanned = _amiiboList.Find(amiibo => amiibo.GetId() == LastScannedAmiiboId); SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries); AmiiboSelectedIndex = AmiiboList.IndexOf(scanned); @@ -325,7 +325,7 @@ namespace Ryujinx.Ava.UI.ViewModels AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; - string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image; + string imageUrl = _amiiboList.Find(amiibo => amiibo.Equals(selected)).Image; string usageString = ""; diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index 782ccef31..b619a7134 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -8,6 +8,7 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.Memory; +using System; using System.Linq; using static Ryujinx.HLE.HOS.ModLoader; @@ -99,7 +100,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions if (string.IsNullOrWhiteSpace(programName)) { - programName = nacpData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); + programName = Array.Find(nacpData.Value.Title.ItemsRo.ToArray(), x => x.Name[0] != 0).NameString.ToString(); } } diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index 0eedc2131..f391f9656 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Logging; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Processes.Extensions; +using System; using System.Collections.Concurrent; using System.IO; using System.Linq; @@ -176,7 +177,7 @@ namespace Ryujinx.HLE.Loaders.Processes if (string.IsNullOrWhiteSpace(programName)) { - programName = nacpData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); + programName = Array.Find(nacpData.Value.Title.ItemsRo.ToArray(), x => x.Name[0] != 0).NameString.ToString(); } if (nacpData.Value.PresenceGroupId != 0) diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index 81e75e270..40b516cc8 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -6,6 +6,7 @@ using Ryujinx.Cpu; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.Horizon.Common; +using System; using System.Linq; namespace Ryujinx.HLE.Loaders.Processes @@ -59,7 +60,7 @@ namespace Ryujinx.HLE.Loaders.Processes if (string.IsNullOrWhiteSpace(Name)) { - Name = ApplicationControlProperties.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); + Name = Array.Find(ApplicationControlProperties.Title.ItemsRo.ToArray(), x => x.Name[0] != 0).NameString.ToString(); } DisplayVersion = ApplicationControlProperties.DisplayVersionString.ToString(); diff --git a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs index 9cfb29427..5bf69d5af 100644 --- a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -163,7 +163,7 @@ namespace Ryujinx.Ui.Windows private void SelectLastScannedAmiibo() { - bool isSet = _amiiboSeriesComboBox.SetActiveId(_amiiboList.FirstOrDefault(amiibo => amiibo.Head + amiibo.Tail == LastScannedAmiiboId).AmiiboSeries); + bool isSet = _amiiboSeriesComboBox.SetActiveId(_amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == LastScannedAmiiboId).AmiiboSeries); isSet = _amiiboCharsComboBox.SetActiveId(LastScannedAmiiboId); if (isSet == false) @@ -305,7 +305,7 @@ namespace Ryujinx.Ui.Windows _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes); - string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Head + amiibo.Tail == _amiiboCharsComboBox.ActiveId).Image; + string imageUrl = _amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == _amiiboCharsComboBox.ActiveId).Image; var usageStringBuilder = new StringBuilder(); From 91e4caaa69954f5aac06f01650542e5072ead3de Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Thu, 22 Jun 2023 21:15:14 -0300 Subject: [PATCH 647/737] "StartsWith" and "EndsWith" overloads that take a "char" should be used instead of the ones that take a "string" (#5347) --- .../HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs | 2 +- .../HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs index aefd668de..cc14d9645 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(Params); - if (Params.EndsWith(">")) + if (Params.EndsWith('>')) { writer.Write(" "); } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs index 8eece5ea0..0e18c5705 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy } // Ignore comments and empty lines - if (line.StartsWith("#") || line.Trim().Length == 0) + if (line.StartsWith('#') || line.Trim().Length == 0) { continue; } From bf96bc84a82f2c4ab771b2ab0c610f86d00b1adf Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Thu, 22 Jun 2023 21:51:44 -0300 Subject: [PATCH 648/737] "Where" should be used before "OrderBy" (#5346) --- src/Ryujinx.Graphics.Shader/Translation/Translator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index b44d6daaa..255030a4b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.Shader.Translation FunctionMatch.RunPass(program); - foreach (DecodedFunction function in program.OrderBy(x => x.Address).Where(x => !x.IsCompilerGenerated)) + foreach (DecodedFunction function in program.Where(x => !x.IsCompilerGenerated).OrderBy(x => x.Address)) { program.AddFunctionAndSetId(function); } From 7d160e98fde05e0b9b542dc04ea72dc34994bc5b Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 24 Jun 2023 02:46:04 +0200 Subject: [PATCH 649/737] MemoryManagement: Change return types for Commit/Decommit to void (#5325) * Replace return type with void for Commit/Decommit * Small cleanup --- src/ARMeilleure/Memory/IJitMemoryBlock.cs | 4 ++-- src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs | 4 ++-- src/Ryujinx.Memory/MemoryBlock.cs | 13 ++++++------- src/Ryujinx.Memory/MemoryManagement.cs | 12 ++++++------ src/Ryujinx.Memory/MemoryManagementUnix.cs | 16 +++++----------- src/Ryujinx.Memory/MemoryManagementWindows.cs | 16 +++++++++++----- 6 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/ARMeilleure/Memory/IJitMemoryBlock.cs b/src/ARMeilleure/Memory/IJitMemoryBlock.cs index 670f2862d..9b11e07ff 100644 --- a/src/ARMeilleure/Memory/IJitMemoryBlock.cs +++ b/src/ARMeilleure/Memory/IJitMemoryBlock.cs @@ -6,9 +6,9 @@ namespace ARMeilleure.Memory { IntPtr Pointer { get; } - bool Commit(ulong offset, ulong size); + void Commit(ulong offset, ulong size); void MapAsRx(ulong offset, ulong size); void MapAsRwx(ulong offset, ulong size); } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs b/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs index 327fb303e..61e27eaf5 100644 --- a/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs +++ b/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs @@ -15,10 +15,10 @@ namespace Ryujinx.Cpu.Jit _impl = new MemoryBlock(size, flags); } - public bool Commit(ulong offset, ulong size) => _impl.Commit(offset, size); + public void Commit(ulong offset, ulong size) => _impl.Commit(offset, size); public void MapAsRx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndExecute); public void MapAsRwx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadWriteExecute); public void Dispose() => _impl.Dispose(); } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Memory/MemoryBlock.cs b/src/Ryujinx.Memory/MemoryBlock.cs index 885ef4569..2cf04628a 100644 --- a/src/Ryujinx.Memory/MemoryBlock.cs +++ b/src/Ryujinx.Memory/MemoryBlock.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Memory @@ -101,12 +100,12 @@ namespace Ryujinx.Memory /// </summary> /// <param name="offset">Starting offset of the range to be committed</param> /// <param name="size">Size of the range to be committed</param> - /// <returns>True if the operation was successful, false otherwise</returns> + /// <exception cref="SystemException">Throw when the operation was not successful</exception> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> - public bool Commit(ulong offset, ulong size) + public void Commit(ulong offset, ulong size) { - return MemoryManagement.Commit(GetPointerInternal(offset, size), size, _forJit); + MemoryManagement.Commit(GetPointerInternal(offset, size), size, _forJit); } /// <summary> @@ -115,12 +114,12 @@ namespace Ryujinx.Memory /// </summary> /// <param name="offset">Starting offset of the range to be decommitted</param> /// <param name="size">Size of the range to be decommitted</param> - /// <returns>True if the operation was successful, false otherwise</returns> + /// <exception cref="SystemException">Throw when the operation was not successful</exception> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> - public bool Decommit(ulong offset, ulong size) + public void Decommit(ulong offset, ulong size) { - return MemoryManagement.Decommit(GetPointerInternal(offset, size), size); + MemoryManagement.Decommit(GetPointerInternal(offset, size), size); } /// <summary> diff --git a/src/Ryujinx.Memory/MemoryManagement.cs b/src/Ryujinx.Memory/MemoryManagement.cs index c4b5ac4c9..7acf8345f 100644 --- a/src/Ryujinx.Memory/MemoryManagement.cs +++ b/src/Ryujinx.Memory/MemoryManagement.cs @@ -36,15 +36,15 @@ namespace Ryujinx.Memory } } - public static bool Commit(IntPtr address, ulong size, bool forJit) + public static void Commit(IntPtr address, ulong size, bool forJit) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Commit(address, (IntPtr)size); + MemoryManagementWindows.Commit(address, (IntPtr)size); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - return MemoryManagementUnix.Commit(address, size, forJit); + MemoryManagementUnix.Commit(address, size, forJit); } else { @@ -52,15 +52,15 @@ namespace Ryujinx.Memory } } - public static bool Decommit(IntPtr address, ulong size) + public static void Decommit(IntPtr address, ulong size) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Decommit(address, (IntPtr)size); + MemoryManagementWindows.Decommit(address, (IntPtr)size); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - return MemoryManagementUnix.Decommit(address, size); + MemoryManagementUnix.Decommit(address, size); } else { diff --git a/src/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs index 30baf0353..d665b2294 100644 --- a/src/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/src/Ryujinx.Memory/MemoryManagementUnix.cs @@ -2,8 +2,6 @@ using System.Collections.Concurrent; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Text; - using static Ryujinx.Memory.MemoryManagerUnixHelper; namespace Ryujinx.Memory @@ -12,7 +10,7 @@ namespace Ryujinx.Memory [SupportedOSPlatform("macos")] static class MemoryManagementUnix { - private static readonly ConcurrentDictionary<IntPtr, ulong> _allocations = new ConcurrentDictionary<IntPtr, ulong>(); + private static readonly ConcurrentDictionary<IntPtr, ulong> _allocations = new(); public static IntPtr Allocate(ulong size, bool forJit) { @@ -68,7 +66,7 @@ namespace Ryujinx.Memory return ptr; } - public static bool Commit(IntPtr address, ulong size, bool forJit) + public static void Commit(IntPtr address, ulong size, bool forJit) { MmapProts prot = MmapProts.PROT_READ | MmapProts.PROT_WRITE; @@ -81,11 +79,9 @@ namespace Ryujinx.Memory { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } - - return true; } - public static bool Decommit(IntPtr address, ulong size) + public static void Decommit(IntPtr address, ulong size) { // Must be writable for madvise to work properly. if (mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) != 0) @@ -102,8 +98,6 @@ namespace Ryujinx.Memory { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } - - return true; } public static bool Reprotect(IntPtr address, ulong size, MemoryPermission permission) @@ -146,7 +140,7 @@ namespace Ryujinx.Memory if (OperatingSystem.IsMacOS()) { - byte[] memName = Encoding.ASCII.GetBytes("Ryujinx-XXXXXX"); + byte[] memName = "Ryujinx-XXXXXX"u8.ToArray(); fixed (byte* pMemName = memName) { @@ -164,7 +158,7 @@ namespace Ryujinx.Memory } else { - byte[] fileName = Encoding.ASCII.GetBytes("/dev/shm/Ryujinx-XXXXXX"); + byte[] fileName = "/dev/shm/Ryujinx-XXXXXX"u8.ToArray(); fixed (byte* pFileName = fileName) { diff --git a/src/Ryujinx.Memory/MemoryManagementWindows.cs b/src/Ryujinx.Memory/MemoryManagementWindows.cs index cbf3ecbac..d7d78bd86 100644 --- a/src/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/src/Ryujinx.Memory/MemoryManagementWindows.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Memory { public const int PageSize = 0x1000; - private static readonly PlaceholderManager _placeholders = new PlaceholderManager(); + private static readonly PlaceholderManager _placeholders = new(); public static IntPtr Allocate(IntPtr size) { @@ -55,14 +55,20 @@ namespace Ryujinx.Memory return ptr; } - public static bool Commit(IntPtr location, IntPtr size) + public static void Commit(IntPtr location, IntPtr size) { - return WindowsApi.VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) != IntPtr.Zero; + if (WindowsApi.VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) == IntPtr.Zero) + { + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); + } } - public static bool Decommit(IntPtr location, IntPtr size) + public static void Decommit(IntPtr location, IntPtr size) { - return WindowsApi.VirtualFree(location, size, AllocationType.Decommit); + if (!WindowsApi.VirtualFree(location, size, AllocationType.Decommit)) + { + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); + } } public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner) From fffc3ed19308ba5a5139add55af8cb4918bc5378 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Sat, 24 Jun 2023 09:01:59 -0300 Subject: [PATCH 650/737] Mutable fields should not be "public static" (#5352) --- src/Ryujinx.Audio/Constants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Audio/Constants.cs b/src/Ryujinx.Audio/Constants.cs index 7d2ffa57b..cde87744f 100644 --- a/src/Ryujinx.Audio/Constants.cs +++ b/src/Ryujinx.Audio/Constants.cs @@ -164,7 +164,7 @@ namespace Ryujinx.Audio /// <summary> /// The default coefficients used for standard 5.1 surround to stereo downmixing. /// </summary> - public static float[] DefaultSurroundToStereoCoefficients = new float[4] + public static readonly float[] DefaultSurroundToStereoCoefficients = new float[4] { 1.0f, 0.707f, From bc392e55dfd0eed72ce30a550e91fa480eba1ef9 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Sat, 24 Jun 2023 09:06:58 -0300 Subject: [PATCH 651/737] Empty "case" clauses that fall through to the "default" should be omitted (#5353) * Empty "case" clauses that fall through to the "default" should be omitted * default throw exception * format --- src/ARMeilleure/Instructions/SoftFloat.cs | 54 ++++++++++++++++++----- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/ARMeilleure/Instructions/SoftFloat.cs b/src/ARMeilleure/Instructions/SoftFloat.cs index 9e3db68d9..4af73c6df 100644 --- a/src/ARMeilleure/Instructions/SoftFloat.cs +++ b/src/ARMeilleure/Instructions/SoftFloat.cs @@ -228,7 +228,6 @@ namespace ARMeilleure.Instructions switch (context.Fpcr.GetRoundingMode()) { - default: case FPRoundingMode.ToNearest: roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); overflowToInf = true; @@ -248,6 +247,9 @@ namespace ARMeilleure.Instructions roundUp = false; overflowToInf = false; break; + + default: + throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.GetRoundingMode()}\"."); } if (roundUp) @@ -412,7 +414,6 @@ namespace ARMeilleure.Instructions switch (context.Fpcr.GetRoundingMode()) { - default: case FPRoundingMode.ToNearest: roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); overflowToInf = true; @@ -432,6 +433,9 @@ namespace ARMeilleure.Instructions roundUp = false; overflowToInf = false; break; + + default: + throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.GetRoundingMode()}\"."); } if (roundUp) @@ -585,7 +589,6 @@ namespace ARMeilleure.Instructions switch (context.Fpcr.GetRoundingMode()) { - default: case FPRoundingMode.ToNearest: roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); overflowToInf = true; @@ -605,6 +608,9 @@ namespace ARMeilleure.Instructions roundUp = false; overflowToInf = false; break; + + default: + throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.GetRoundingMode()}\"."); } if (roundUp) @@ -1433,11 +1439,24 @@ namespace ARMeilleure.Instructions switch (fpcr.GetRoundingMode()) { + case FPRoundingMode.ToNearest: + overflowToInf = true; + break; + + case FPRoundingMode.TowardsPlusInfinity: + overflowToInf = !sign; + break; + + case FPRoundingMode.TowardsMinusInfinity: + overflowToInf = sign; + break; + + case FPRoundingMode.TowardsZero: + overflowToInf = false; + break; + default: - case FPRoundingMode.ToNearest: overflowToInf = true; break; - case FPRoundingMode.TowardsPlusInfinity: overflowToInf = !sign; break; - case FPRoundingMode.TowardsMinusInfinity: overflowToInf = sign; break; - case FPRoundingMode.TowardsZero: overflowToInf = false; break; + throw new ArgumentException($"Invalid rounding mode \"{fpcr.GetRoundingMode()}\"."); } result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); @@ -2845,11 +2864,24 @@ namespace ARMeilleure.Instructions switch (fpcr.GetRoundingMode()) { + case FPRoundingMode.ToNearest: + overflowToInf = true; + break; + + case FPRoundingMode.TowardsPlusInfinity: + overflowToInf = !sign; + break; + + case FPRoundingMode.TowardsMinusInfinity: + overflowToInf = sign; + break; + + case FPRoundingMode.TowardsZero: + overflowToInf = false; + break; + default: - case FPRoundingMode.ToNearest: overflowToInf = true; break; - case FPRoundingMode.TowardsPlusInfinity: overflowToInf = !sign; break; - case FPRoundingMode.TowardsMinusInfinity: overflowToInf = sign; break; - case FPRoundingMode.TowardsZero: overflowToInf = false; break; + throw new ArgumentException($"Invalid rounding mode \"{fpcr.GetRoundingMode()}\"."); } result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); From df5be5812f9ebfd6533ef3eefdcdb24d30f29954 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 01:29:40 +0200 Subject: [PATCH 652/737] [Ryujinx.Audio.Backends.OpenAL] Address dotnet-format issues (#5359) * dotnet format style --severity info Some changes were manually reverted. * Restore a few unused methods and variables * Address dotnet format CA1816 warnings * Address most dotnet format whitespace warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase --- .../OpenALHardwareDeviceDriver.cs | 5 +-- .../OpenALHardwareDeviceSession.cs | 36 ++++++++----------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs index 0c793f248..92946900f 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Backends.OpenAL private readonly ManualResetEvent _pauseEvent; private readonly ConcurrentDictionary<OpenALHardwareDeviceSession, byte> _sessions; private bool _stillRunning; - private Thread _updaterThread; + private readonly Thread _updaterThread; public OpenALHardwareDeviceDriver() { @@ -73,7 +73,7 @@ namespace Ryujinx.Audio.Backends.OpenAL throw new ArgumentException($"{channelCount}"); } - OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); + OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); _sessions.TryAdd(session, 0); @@ -123,6 +123,7 @@ namespace Ryujinx.Audio.Backends.OpenAL public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs index 8d7d0d6a2..4a2d521fe 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs @@ -10,11 +10,11 @@ namespace Ryujinx.Audio.Backends.OpenAL { class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase { - private OpenALHardwareDeviceDriver _driver; - private int _sourceId; - private ALFormat _targetFormat; + private readonly OpenALHardwareDeviceDriver _driver; + private readonly int _sourceId; + private readonly ALFormat _targetFormat; private bool _isActive; - private Queue<OpenALAudioBuffer> _queuedBuffers; + private readonly Queue<OpenALAudioBuffer> _queuedBuffers; private ulong _playedSampleCount; private readonly object _lock = new(); @@ -32,23 +32,17 @@ namespace Ryujinx.Audio.Backends.OpenAL private ALFormat GetALFormat() { - switch (RequestedSampleFormat) + return RequestedSampleFormat switch { - case SampleFormat.PcmInt16: - switch (RequestedChannelCount) - { - case 1: - return ALFormat.Mono16; - case 2: - return ALFormat.Stereo16; - case 6: - return ALFormat.Multi51Chn16Ext; - default: - throw new NotImplementedException($"Unsupported channel config {RequestedChannelCount}"); - } - default: - throw new NotImplementedException($"Unsupported sample format {RequestedSampleFormat}"); - } + SampleFormat.PcmInt16 => RequestedChannelCount switch + { + 1 => ALFormat.Mono16, + 2 => ALFormat.Stereo16, + 6 => ALFormat.Multi51Chn16Ext, + _ => throw new NotImplementedException($"Unsupported channel config {RequestedChannelCount}"), + }, + _ => throw new NotImplementedException($"Unsupported sample format {RequestedSampleFormat}"), + }; } public override void PrepareToClose() { } @@ -69,7 +63,7 @@ namespace Ryujinx.Audio.Backends.OpenAL { lock (_lock) { - OpenALAudioBuffer driverBuffer = new OpenALAudioBuffer + OpenALAudioBuffer driverBuffer = new() { DriverIdentifier = buffer.DataPointer, BufferId = AL.GenBuffer(), From ede5b3c3240f0d4cf06f38174bf50b9f2f3593e8 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 02:15:56 +0200 Subject: [PATCH 653/737] [Ryujinx.Audio.Backends.SoundIo] Address dotnet-format issues (#5360) * dotnet format style --severity info Some changes were manually reverted. * Address dotnet format CA1816 warnings * Address dotnet format CA1401 warnings * Address most dotnet format whitespace warnings * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Address review feedback --- .../Native/SoundIo.cs | 52 +++++++++---------- .../Native/SoundIoContext.cs | 1 - .../SoundIoHardwareDeviceDriver.cs | 6 ++- .../SoundIoHardwareDeviceSession.cs | 12 ++--- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs index 9c3e686df..31af3e9d3 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs @@ -10,19 +10,19 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native private const string LibraryName = "libsoundio"; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public unsafe delegate void OnDeviceChangeNativeDelegate(IntPtr ctx); + public delegate void OnDeviceChangeNativeDelegate(IntPtr ctx); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public unsafe delegate void OnBackendDisconnectedDelegate(IntPtr ctx, SoundIoError err); + public delegate void OnBackendDisconnectedDelegate(IntPtr ctx, SoundIoError err); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public unsafe delegate void OnEventsSignalDelegate(IntPtr ctx); + public delegate void OnEventsSignalDelegate(IntPtr ctx); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public unsafe delegate void EmitRtPrioWarningDelegate(); + public delegate void EmitRtPrioWarningDelegate(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public unsafe delegate void JackCallbackDelegate(IntPtr msg); + public delegate void JackCallbackDelegate(IntPtr msg); [StructLayout(LayoutKind.Sequential)] public struct SoundIoStruct @@ -110,69 +110,69 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native } [LibraryImport(LibraryName)] - public static partial IntPtr soundio_create(); + internal static partial IntPtr soundio_create(); [LibraryImport(LibraryName)] - public static partial SoundIoError soundio_connect(IntPtr ctx); + internal static partial SoundIoError soundio_connect(IntPtr ctx); [LibraryImport(LibraryName)] - public static partial void soundio_disconnect(IntPtr ctx); + internal static partial void soundio_disconnect(IntPtr ctx); [LibraryImport(LibraryName)] - public static partial void soundio_flush_events(IntPtr ctx); + internal static partial void soundio_flush_events(IntPtr ctx); [LibraryImport(LibraryName)] - public static partial int soundio_output_device_count(IntPtr ctx); + internal static partial int soundio_output_device_count(IntPtr ctx); [LibraryImport(LibraryName)] - public static partial int soundio_default_output_device_index(IntPtr ctx); + internal static partial int soundio_default_output_device_index(IntPtr ctx); [LibraryImport(LibraryName)] - public static partial IntPtr soundio_get_output_device(IntPtr ctx, int index); + internal static partial IntPtr soundio_get_output_device(IntPtr ctx, int index); [LibraryImport(LibraryName)] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool soundio_device_supports_format(IntPtr devCtx, SoundIoFormat format); + internal static partial bool soundio_device_supports_format(IntPtr devCtx, SoundIoFormat format); [LibraryImport(LibraryName)] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool soundio_device_supports_layout(IntPtr devCtx, IntPtr layout); + internal static partial bool soundio_device_supports_layout(IntPtr devCtx, IntPtr layout); [LibraryImport(LibraryName)] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool soundio_device_supports_sample_rate(IntPtr devCtx, int sampleRate); + internal static partial bool soundio_device_supports_sample_rate(IntPtr devCtx, int sampleRate); [LibraryImport(LibraryName)] - public static partial IntPtr soundio_outstream_create(IntPtr devCtx); + internal static partial IntPtr soundio_outstream_create(IntPtr devCtx); [LibraryImport(LibraryName)] - public static partial SoundIoError soundio_outstream_open(IntPtr outStreamCtx); + internal static partial SoundIoError soundio_outstream_open(IntPtr outStreamCtx); [LibraryImport(LibraryName)] - public static partial SoundIoError soundio_outstream_start(IntPtr outStreamCtx); + internal static partial SoundIoError soundio_outstream_start(IntPtr outStreamCtx); [LibraryImport(LibraryName)] - public static partial SoundIoError soundio_outstream_begin_write(IntPtr outStreamCtx, IntPtr areas, IntPtr frameCount); + internal static partial SoundIoError soundio_outstream_begin_write(IntPtr outStreamCtx, IntPtr areas, IntPtr frameCount); [LibraryImport(LibraryName)] - public static partial SoundIoError soundio_outstream_end_write(IntPtr outStreamCtx); + internal static partial SoundIoError soundio_outstream_end_write(IntPtr outStreamCtx); [LibraryImport(LibraryName)] - public static partial SoundIoError soundio_outstream_pause(IntPtr devCtx, [MarshalAs(UnmanagedType.Bool)] bool pause); + internal static partial SoundIoError soundio_outstream_pause(IntPtr devCtx, [MarshalAs(UnmanagedType.Bool)] bool pause); [LibraryImport(LibraryName)] - public static partial SoundIoError soundio_outstream_set_volume(IntPtr devCtx, double volume); + internal static partial SoundIoError soundio_outstream_set_volume(IntPtr devCtx, double volume); [LibraryImport(LibraryName)] - public static partial void soundio_outstream_destroy(IntPtr streamCtx); + internal static partial void soundio_outstream_destroy(IntPtr streamCtx); [LibraryImport(LibraryName)] - public static partial void soundio_destroy(IntPtr ctx); + internal static partial void soundio_destroy(IntPtr ctx); [LibraryImport(LibraryName)] - public static partial IntPtr soundio_channel_layout_get_default(int channelCount); + internal static partial IntPtr soundio_channel_layout_get_default(int channelCount); [LibraryImport(LibraryName)] - public static partial IntPtr soundio_strerror(SoundIoError err); + internal static partial IntPtr soundio_strerror(SoundIoError err); } } diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs index 3744c2e64..afa86befa 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs @@ -1,5 +1,4 @@ using System; -using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; diff --git a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs index 02da27769..9dac0992c 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs @@ -141,7 +141,7 @@ namespace Ryujinx.Audio.Backends.SoundIo throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!"); } - SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); + SoundIoHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); _sessions.TryAdd(session, 0); @@ -162,7 +162,7 @@ namespace Ryujinx.Audio.Backends.SoundIo SampleFormat.PcmInt24 => SoundIoFormat.S24LE, SampleFormat.PcmInt32 => SoundIoFormat.S32LE, SampleFormat.PcmFloat => SoundIoFormat.Float32LE, - _ => throw new ArgumentException ($"Unsupported sample format {format}"), + _ => throw new ArgumentException($"Unsupported sample format {format}"), }; } @@ -202,6 +202,8 @@ namespace Ryujinx.Audio.Backends.SoundIo public void Dispose() { + GC.SuppressFinalize(this); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); diff --git a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs index 96d9ce970..0aa13e7ed 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Audio.Backends.SoundIo { class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase { - private SoundIoHardwareDeviceDriver _driver; - private ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers; + private readonly SoundIoHardwareDeviceDriver _driver; + private readonly ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers; private SoundIoOutStreamContext _outputStream; - private DynamicRingBuffer _ringBuffer; + private readonly DynamicRingBuffer _ringBuffer; private ulong _playedSampleCount; - private ManualResetEvent _updateRequiredEvent; + private readonly ManualResetEvent _updateRequiredEvent; private int _disposeState; public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) @@ -54,7 +54,7 @@ namespace Ryujinx.Audio.Backends.SoundIo public override void QueueBuffer(AudioBuffer buffer) { - SoundIoAudioBuffer driverBuffer = new SoundIoAudioBuffer(buffer.DataPointer, GetSampleCount(buffer)); + SoundIoAudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer)); _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); @@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Backends.SoundIo _driver.FlushContextEvents(); } - public override void UnregisterBuffer(AudioBuffer buffer) {} + public override void UnregisterBuffer(AudioBuffer buffer) { } public override bool WasBufferFullyConsumed(AudioBuffer buffer) { From 7c2f07d12458ce6d2ee9c98f78d56ec8b20762bb Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 13:40:37 +0200 Subject: [PATCH 654/737] [Ryujinx.Horizon.Common] Address dotnet-format issues (#5382) * dotnet format style --severity info Some changes were manually reverted. * Address most dotnet format whitespace warnings * Address IDE0251 warnings * dotnet format whitespace after rebase --- src/Ryujinx.Horizon.Common/KernelResult.cs | 62 +++++++++++----------- src/Ryujinx.Horizon.Common/OnScopeExit.cs | 2 +- src/Ryujinx.Horizon.Common/Result.cs | 22 ++++---- src/Ryujinx.Horizon.Common/ResultNames.cs | 2 +- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/Ryujinx.Horizon.Common/KernelResult.cs b/src/Ryujinx.Horizon.Common/KernelResult.cs index 51fec2057..ff367ac46 100644 --- a/src/Ryujinx.Horizon.Common/KernelResult.cs +++ b/src/Ryujinx.Horizon.Common/KernelResult.cs @@ -4,36 +4,36 @@ { private const int ModuleId = 1; - public static Result SessionCountExceeded => new Result(ModuleId, 7); - public static Result InvalidCapability => new Result(ModuleId, 14); - public static Result ThreadNotStarted => new Result(ModuleId, 57); - public static Result ThreadTerminating => new Result(ModuleId, 59); - public static Result InvalidSize => new Result(ModuleId, 101); - public static Result InvalidAddress => new Result(ModuleId, 102); - public static Result OutOfResource => new Result(ModuleId, 103); - public static Result OutOfMemory => new Result(ModuleId, 104); - public static Result HandleTableFull => new Result(ModuleId, 105); - public static Result InvalidMemState => new Result(ModuleId, 106); - public static Result InvalidPermission => new Result(ModuleId, 108); - public static Result InvalidMemRange => new Result(ModuleId, 110); - public static Result InvalidPriority => new Result(ModuleId, 112); - public static Result InvalidCpuCore => new Result(ModuleId, 113); - public static Result InvalidHandle => new Result(ModuleId, 114); - public static Result UserCopyFailed => new Result(ModuleId, 115); - public static Result InvalidCombination => new Result(ModuleId, 116); - public static Result TimedOut => new Result(ModuleId, 117); - public static Result Cancelled => new Result(ModuleId, 118); - public static Result MaximumExceeded => new Result(ModuleId, 119); - public static Result InvalidEnumValue => new Result(ModuleId, 120); - public static Result NotFound => new Result(ModuleId, 121); - public static Result InvalidThread => new Result(ModuleId, 122); - public static Result PortRemoteClosed => new Result(ModuleId, 123); - public static Result InvalidState => new Result(ModuleId, 125); - public static Result ReservedValue => new Result(ModuleId, 126); - public static Result PortClosed => new Result(ModuleId, 131); - public static Result ResLimitExceeded => new Result(ModuleId, 132); - public static Result ReceiveListBroken => new Result(ModuleId, 258); - public static Result OutOfVaSpace => new Result(ModuleId, 259); - public static Result CmdBufferTooSmall => new Result(ModuleId, 260); + public static Result SessionCountExceeded => new(ModuleId, 7); + public static Result InvalidCapability => new(ModuleId, 14); + public static Result ThreadNotStarted => new(ModuleId, 57); + public static Result ThreadTerminating => new(ModuleId, 59); + public static Result InvalidSize => new(ModuleId, 101); + public static Result InvalidAddress => new(ModuleId, 102); + public static Result OutOfResource => new(ModuleId, 103); + public static Result OutOfMemory => new(ModuleId, 104); + public static Result HandleTableFull => new(ModuleId, 105); + public static Result InvalidMemState => new(ModuleId, 106); + public static Result InvalidPermission => new(ModuleId, 108); + public static Result InvalidMemRange => new(ModuleId, 110); + public static Result InvalidPriority => new(ModuleId, 112); + public static Result InvalidCpuCore => new(ModuleId, 113); + public static Result InvalidHandle => new(ModuleId, 114); + public static Result UserCopyFailed => new(ModuleId, 115); + public static Result InvalidCombination => new(ModuleId, 116); + public static Result TimedOut => new(ModuleId, 117); + public static Result Cancelled => new(ModuleId, 118); + public static Result MaximumExceeded => new(ModuleId, 119); + public static Result InvalidEnumValue => new(ModuleId, 120); + public static Result NotFound => new(ModuleId, 121); + public static Result InvalidThread => new(ModuleId, 122); + public static Result PortRemoteClosed => new(ModuleId, 123); + public static Result InvalidState => new(ModuleId, 125); + public static Result ReservedValue => new(ModuleId, 126); + public static Result PortClosed => new(ModuleId, 131); + public static Result ResLimitExceeded => new(ModuleId, 132); + public static Result ReceiveListBroken => new(ModuleId, 258); + public static Result OutOfVaSpace => new(ModuleId, 259); + public static Result CmdBufferTooSmall => new(ModuleId, 260); } } diff --git a/src/Ryujinx.Horizon.Common/OnScopeExit.cs b/src/Ryujinx.Horizon.Common/OnScopeExit.cs index 2b81e492f..deba7bbad 100644 --- a/src/Ryujinx.Horizon.Common/OnScopeExit.cs +++ b/src/Ryujinx.Horizon.Common/OnScopeExit.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Horizon.Common { - public struct OnScopeExit : IDisposable + public readonly struct OnScopeExit : IDisposable { private readonly Action _action; diff --git a/src/Ryujinx.Horizon.Common/Result.cs b/src/Ryujinx.Horizon.Common/Result.cs index 28056310f..8e458475d 100644 --- a/src/Ryujinx.Horizon.Common/Result.cs +++ b/src/Ryujinx.Horizon.Common/Result.cs @@ -13,13 +13,13 @@ namespace Ryujinx.Horizon.Common public int ErrorCode { get; } - public bool IsSuccess => ErrorCode == 0; - public bool IsFailure => ErrorCode != 0; + public readonly bool IsSuccess => ErrorCode == 0; + public readonly bool IsFailure => ErrorCode != 0; - public int Module => ErrorCode & (ModuleMax - 1); - public int Description => (ErrorCode >> ModuleBits) & (DescriptionMax - 1); + public readonly int Module => ErrorCode & (ModuleMax - 1); + public readonly int Description => (ErrorCode >> ModuleBits) & (DescriptionMax - 1); - public string PrintableResult => $"{2000 + Module:D4}-{Description:D4}"; + public readonly string PrintableResult => $"{2000 + Module:D4}-{Description:D4}"; public Result(int module, int description) { @@ -36,17 +36,17 @@ namespace Ryujinx.Horizon.Common ErrorCode = module | (description << ModuleBits); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is Result result && result.Equals(this); } - public bool Equals(Result other) + public readonly bool Equals(Result other) { return other.ErrorCode == ErrorCode; } - public override int GetHashCode() + public readonly override int GetHashCode() { return ErrorCode; } @@ -61,7 +61,7 @@ namespace Ryujinx.Horizon.Common return !lhs.Equals(rhs); } - public bool InRange(int minInclusive, int maxInclusive) + public readonly bool InRange(int minInclusive, int maxInclusive) { return (uint)(Description - minInclusive) <= (uint)(maxInclusive - minInclusive); } @@ -105,7 +105,7 @@ namespace Ryujinx.Horizon.Common throw new InvalidResultException(this); } - public override string ToString() + public readonly override string ToString() { if (ResultNames.TryGet(ErrorCode, out string name)) { @@ -115,4 +115,4 @@ namespace Ryujinx.Horizon.Common return PrintableResult; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon.Common/ResultNames.cs b/src/Ryujinx.Horizon.Common/ResultNames.cs index 8f8173ed5..84058cf2c 100644 --- a/src/Ryujinx.Horizon.Common/ResultNames.cs +++ b/src/Ryujinx.Horizon.Common/ResultNames.cs @@ -1698,4 +1698,4 @@ namespace Ryujinx.Horizon.Common return _names.TryGetValue(errorCode, out name); } } -} \ No newline at end of file +} From e3bacfa77481738aabee5f8b8be3f8ff91132c43 Mon Sep 17 00:00:00 2001 From: Shihta Kuan <Shihta@users.noreply.github.com> Date: Sun, 25 Jun 2023 20:49:53 +0800 Subject: [PATCH 655/737] Set COMPlus_DefaultStackSize to 2M in macOS (#5349) * Set COMPlus_DefaultStackSize to 2M in macOS * Remove the custom thread stack size on Ryujinx.Ava --- distribution/macos/Info.plist | 7 ++++++- src/Ryujinx.Ava/AppHost.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist index 6c3f7717c..968814f94 100644 --- a/distribution/macos/Info.plist +++ b/distribution/macos/Info.plist @@ -39,10 +39,15 @@ <key>CSResourcesFileMapped</key> <true/> <key>NSHumanReadableCopyright</key> - <string>Copyright © 2018 - 2022 Ryujinx Team and Contributors.</string> + <string>Copyright © 2018 - 2023 Ryujinx Team and Contributors.</string> <key>LSApplicationCategoryType</key> <string>public.app-category.games</string> <key>LSMinimumSystemVersion</key> <string>11.0</string> + <key>LSEnvironment</key> + <dict> + <key>COMPlus_DefaultStackSize</key> + <string>200000</string> + </dict> </dict> </plist> diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index d3e0ea392..a25713796 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -134,7 +134,7 @@ namespace Ryujinx.Ava _inputManager = inputManager; _accountManager = accountManager; _userChannelPersistence = userChannelPersistence; - _renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" }; + _renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; _lastCursorMoveTime = Stopwatch.GetTimestamp(); _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; _topLevel = topLevel; From bddb2a148355ef2ce326d47e8e5217bd8af36a98 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 18:03:08 +0200 Subject: [PATCH 656/737] [Ryujinx.Tests.Unicorn] Address dotnet-format issues (#5391) * dotnet format style --severity info Some changes were manually reverted. * Restore a few unused methods and variables * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Final dotnet format pass and fix naming rule violations --- src/Ryujinx.Tests.Unicorn/IndexedProperty.cs | 6 +- src/Ryujinx.Tests.Unicorn/MemoryPermission.cs | 2 +- src/Ryujinx.Tests.Unicorn/SimdValue.cs | 32 ++++----- src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs | 42 ++++++------ src/Ryujinx.Tests.Unicorn/UnicornAArch64.cs | 68 +++++++++---------- 5 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/Ryujinx.Tests.Unicorn/IndexedProperty.cs b/src/Ryujinx.Tests.Unicorn/IndexedProperty.cs index 65d445fc0..347b91a08 100644 --- a/src/Ryujinx.Tests.Unicorn/IndexedProperty.cs +++ b/src/Ryujinx.Tests.Unicorn/IndexedProperty.cs @@ -4,12 +4,12 @@ namespace Ryujinx.Tests.Unicorn { public class IndexedProperty<TIndex, TValue> { - private Func<TIndex, TValue> _getFunc; - private Action<TIndex, TValue> _setAction; + private readonly Func<TIndex, TValue> _getFunc; + private readonly Action<TIndex, TValue> _setAction; public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction) { - _getFunc = getFunc; + _getFunc = getFunc; _setAction = setAction; } diff --git a/src/Ryujinx.Tests.Unicorn/MemoryPermission.cs b/src/Ryujinx.Tests.Unicorn/MemoryPermission.cs index 044b3176b..6d3e7370d 100644 --- a/src/Ryujinx.Tests.Unicorn/MemoryPermission.cs +++ b/src/Ryujinx.Tests.Unicorn/MemoryPermission.cs @@ -11,4 +11,4 @@ namespace Ryujinx.Tests.Unicorn Exec = 4, All = 7, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests.Unicorn/SimdValue.cs b/src/Ryujinx.Tests.Unicorn/SimdValue.cs index ce9e178cc..0401c693f 100644 --- a/src/Ryujinx.Tests.Unicorn/SimdValue.cs +++ b/src/Ryujinx.Tests.Unicorn/SimdValue.cs @@ -2,7 +2,7 @@ using System; namespace Ryujinx.Tests.Unicorn { - public struct SimdValue : IEquatable<SimdValue> + public readonly struct SimdValue : IEquatable<SimdValue> { private readonly ulong _e0; private readonly ulong _e1; @@ -39,31 +39,29 @@ namespace Ryujinx.Tests.Unicorn return BitConverter.Int64BitsToDouble(GetInt64(index)); } - public int GetInt32(int index) => (int)GetUInt32(index); + public int GetInt32(int index) => (int)GetUInt32(index); public long GetInt64(int index) => (long)GetUInt64(index); public uint GetUInt32(int index) { - switch (index) + return index switch { - case 0: return (uint)(_e0 >> 0); - case 1: return (uint)(_e0 >> 32); - case 2: return (uint)(_e1 >> 0); - case 3: return (uint)(_e1 >> 32); - } - - throw new ArgumentOutOfRangeException(nameof(index)); + 0 => (uint)(_e0 >> 0), + 1 => (uint)(_e0 >> 32), + 2 => (uint)(_e1 >> 0), + 3 => (uint)(_e1 >> 32), + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; } public ulong GetUInt64(int index) { - switch (index) + return index switch { - case 0: return _e0; - case 1: return _e1; - } - - throw new ArgumentOutOfRangeException(nameof(index)); + 0 => _e0, + 1 => _e1, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; } public byte[] ToArray() @@ -109,4 +107,4 @@ namespace Ryujinx.Tests.Unicorn return $"0x{_e1:X16}{_e0:X16}"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs index a095e6641..6532beab8 100644 --- a/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs +++ b/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Tests.Unicorn { public class UnicornAArch32 : IDisposable { - internal readonly UnicornEngine.Unicorn uc; + internal readonly UnicornEngine.Unicorn Uc; private bool _isDisposed; public IndexedProperty<int, uint> R => new(GetX, SetX); @@ -84,7 +84,7 @@ namespace Ryujinx.Tests.Unicorn public UnicornAArch32() { - uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM, Common.UC_MODE_LITTLE_ENDIAN); + Uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM, Common.UC_MODE_LITTLE_ENDIAN); SetRegister(Arm.UC_ARM_REG_C1_C0_2, GetRegister(Arm.UC_ARM_REG_C1_C0_2) | 0xf00000); SetRegister(Arm.UC_ARM_REG_FPEXC, 0x40000000); @@ -105,7 +105,7 @@ namespace Ryujinx.Tests.Unicorn { if (!_isDisposed) { - uc.Close(); + Uc.Close(); _isDisposed = true; } } @@ -113,7 +113,7 @@ namespace Ryujinx.Tests.Unicorn public void RunForCount(ulong count) { // FIXME: untilAddr should be 0xFFFFFFFFFFFFFFFFu - uc.EmuStart(this.PC, -1, 0, (long)count); + Uc.EmuStart(this.PC, -1, 0, (long)count); } public void Step() @@ -121,7 +121,7 @@ namespace Ryujinx.Tests.Unicorn RunForCount(1); } - private static int[] XRegisters = + private static readonly int[] _xRegisters = { Arm.UC_ARM_REG_R0, Arm.UC_ARM_REG_R1, @@ -141,7 +141,8 @@ namespace Ryujinx.Tests.Unicorn Arm.UC_ARM_REG_R15, }; - private static int[] QRegisters = +#pragma warning disable IDE0051, IDE0052 // Remove unused private member + private static readonly int[] _qRegisters = { Arm.UC_ARM_REG_Q0, Arm.UC_ARM_REG_Q1, @@ -160,6 +161,7 @@ namespace Ryujinx.Tests.Unicorn Arm.UC_ARM_REG_Q14, Arm.UC_ARM_REG_Q15 }; +#pragma warning restore IDE0051, IDE0052 public uint GetX(int index) { @@ -168,7 +170,7 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - return GetRegister(XRegisters[index]); + return GetRegister(_xRegisters[index]); } public void SetX(int index, uint value) @@ -178,7 +180,7 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - SetRegister(XRegisters[index], value); + SetRegister(_xRegisters[index], value); } public SimdValue GetQ(int index) @@ -206,7 +208,7 @@ namespace Ryujinx.Tests.Unicorn { byte[] data = new byte[4]; - uc.RegRead(register, data); + Uc.RegRead(register, data); return BitConverter.ToUInt32(data, 0); } @@ -215,16 +217,16 @@ namespace Ryujinx.Tests.Unicorn { byte[] data = BitConverter.GetBytes(value); - uc.RegWrite(register, data); + Uc.RegWrite(register, data); } public SimdValue GetVector(int register) { byte[] data = new byte[8]; - uc.RegRead(register, data); + Uc.RegRead(register, data); ulong lo = BitConverter.ToUInt64(data, 0); - uc.RegRead(register + 1, data); + Uc.RegRead(register + 1, data); ulong hi = BitConverter.ToUInt64(data, 0); return new SimdValue(lo, hi); @@ -233,16 +235,16 @@ namespace Ryujinx.Tests.Unicorn private void SetVector(int register, SimdValue value) { byte[] data = BitConverter.GetBytes(value.GetUInt64(0)); - uc.RegWrite(register, data); + Uc.RegWrite(register, data); data = BitConverter.GetBytes(value.GetUInt64(1)); - uc.RegWrite(register + 1, data); + Uc.RegWrite(register + 1, data); } public byte[] MemoryRead(ulong address, ulong size) { byte[] value = new byte[size]; - uc.MemRead((long)address, value); + Uc.MemRead((long)address, value); return value; } @@ -254,7 +256,7 @@ namespace Ryujinx.Tests.Unicorn public void MemoryWrite(ulong address, byte[] value) { - uc.MemWrite((long)address, value); + Uc.MemWrite((long)address, value); } public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new[] { value }); @@ -267,17 +269,17 @@ namespace Ryujinx.Tests.Unicorn public void MemoryMap(ulong address, ulong size, MemoryPermission permissions) { - uc.MemMap((long)address, (long)size, (int)permissions); + Uc.MemMap((long)address, (long)size, (int)permissions); } public void MemoryUnmap(ulong address, ulong size) { - uc.MemUnmap((long)address, (long)size); + Uc.MemUnmap((long)address, (long)size); } public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions) { - uc.MemProtect((long)address, (long)size, (int)permissions); + Uc.MemProtect((long)address, (long)size, (int)permissions); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/src/Ryujinx.Tests.Unicorn/UnicornAArch64.cs index 16dfd93bd..bdb535581 100644 --- a/src/Ryujinx.Tests.Unicorn/UnicornAArch64.cs +++ b/src/Ryujinx.Tests.Unicorn/UnicornAArch64.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Tests.Unicorn { public class UnicornAArch64 : IDisposable { - internal readonly UnicornEngine.Unicorn uc; + internal readonly UnicornEngine.Unicorn Uc; private bool _isDisposed; public IndexedProperty<int, ulong> X => new(GetX, SetX); @@ -33,48 +33,48 @@ namespace Ryujinx.Tests.Unicorn public uint Pstate { get => (uint)GetRegister(Arm64.UC_ARM64_REG_PSTATE); - set => SetRegister(Arm64.UC_ARM64_REG_PSTATE, value); + set => SetRegister(Arm64.UC_ARM64_REG_PSTATE, value); } public int Fpcr { get => (int)GetRegister(Arm64.UC_ARM64_REG_FPCR); - set => SetRegister(Arm64.UC_ARM64_REG_FPCR, (uint)value); + set => SetRegister(Arm64.UC_ARM64_REG_FPCR, (uint)value); } public int Fpsr { get => (int)GetRegister(Arm64.UC_ARM64_REG_FPSR); - set => SetRegister(Arm64.UC_ARM64_REG_FPSR, (uint)value); + set => SetRegister(Arm64.UC_ARM64_REG_FPSR, (uint)value); } public bool OverflowFlag { - get => (Pstate & 0x10000000u) != 0; + get => (Pstate & 0x10000000u) != 0; set => Pstate = (Pstate & ~0x10000000u) | (value ? 0x10000000u : 0u); } public bool CarryFlag { - get => (Pstate & 0x20000000u) != 0; + get => (Pstate & 0x20000000u) != 0; set => Pstate = (Pstate & ~0x20000000u) | (value ? 0x20000000u : 0u); } public bool ZeroFlag { - get => (Pstate & 0x40000000u) != 0; + get => (Pstate & 0x40000000u) != 0; set => Pstate = (Pstate & ~0x40000000u) | (value ? 0x40000000u : 0u); } public bool NegativeFlag { - get => (Pstate & 0x80000000u) != 0; + get => (Pstate & 0x80000000u) != 0; set => Pstate = (Pstate & ~0x80000000u) | (value ? 0x80000000u : 0u); } public UnicornAArch64() { - uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM64, Common.UC_MODE_LITTLE_ENDIAN); + Uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM64, Common.UC_MODE_LITTLE_ENDIAN); SetRegister(Arm64.UC_ARM64_REG_CPACR_EL1, 0x00300000); } @@ -94,7 +94,7 @@ namespace Ryujinx.Tests.Unicorn { if (!_isDisposed) { - uc.Close(); + Uc.Close(); _isDisposed = true; } } @@ -102,7 +102,7 @@ namespace Ryujinx.Tests.Unicorn public void RunForCount(ulong count) { // FIXME: untilAddr should be 0xFFFFFFFFFFFFFFFFul - uc.EmuStart((long)this.PC, -1, 0, (long)count); + Uc.EmuStart((long)this.PC, -1, 0, (long)count); } public void Step() @@ -110,7 +110,7 @@ namespace Ryujinx.Tests.Unicorn RunForCount(1); } - private static int[] XRegisters = + private static readonly int[] _xRegisters = { Arm64.UC_ARM64_REG_X0, Arm64.UC_ARM64_REG_X1, @@ -145,7 +145,7 @@ namespace Ryujinx.Tests.Unicorn Arm64.UC_ARM64_REG_X30, }; - private static int[] QRegisters = + private static readonly int[] _qRegisters = { Arm64.UC_ARM64_REG_Q0, Arm64.UC_ARM64_REG_Q1, @@ -188,7 +188,7 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - return GetRegister(XRegisters[index]); + return GetRegister(_xRegisters[index]); } public void SetX(int index, ulong value) @@ -198,7 +198,7 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - SetRegister(XRegisters[index], value); + SetRegister(_xRegisters[index], value); } public SimdValue GetQ(int index) @@ -208,7 +208,7 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - return GetVector(QRegisters[index]); + return GetVector(_qRegisters[index]); } public void SetQ(int index, SimdValue value) @@ -218,14 +218,14 @@ namespace Ryujinx.Tests.Unicorn throw new ArgumentOutOfRangeException(nameof(index)); } - SetVector(QRegisters[index], value); + SetVector(_qRegisters[index], value); } private ulong GetRegister(int register) { byte[] data = new byte[8]; - uc.RegRead(register, data); + Uc.RegRead(register, data); return BitConverter.ToUInt64(data, 0); } @@ -234,14 +234,14 @@ namespace Ryujinx.Tests.Unicorn { byte[] data = BitConverter.GetBytes(value); - uc.RegWrite(register, data); + Uc.RegWrite(register, data); } private SimdValue GetVector(int register) { byte[] data = new byte[16]; - uc.RegRead(register, data); + Uc.RegRead(register, data); return new SimdValue(data); } @@ -250,49 +250,49 @@ namespace Ryujinx.Tests.Unicorn { byte[] data = value.ToArray(); - uc.RegWrite(register, data); + Uc.RegWrite(register, data); } public byte[] MemoryRead(ulong address, ulong size) { byte[] value = new byte[size]; - uc.MemRead((long)address, value); + Uc.MemRead((long)address, value); return value; } - public byte MemoryRead8 (ulong address) => MemoryRead(address, 1)[0]; + public byte MemoryRead8(ulong address) => MemoryRead(address, 1)[0]; public ushort MemoryRead16(ulong address) => BitConverter.ToUInt16(MemoryRead(address, 2), 0); - public uint MemoryRead32(ulong address) => BitConverter.ToUInt32(MemoryRead(address, 4), 0); - public ulong MemoryRead64(ulong address) => BitConverter.ToUInt64(MemoryRead(address, 8), 0); + public uint MemoryRead32(ulong address) => BitConverter.ToUInt32(MemoryRead(address, 4), 0); + public ulong MemoryRead64(ulong address) => BitConverter.ToUInt64(MemoryRead(address, 8), 0); public void MemoryWrite(ulong address, byte[] value) { - uc.MemWrite((long)address, value); + Uc.MemWrite((long)address, value); } - public void MemoryWrite8 (ulong address, byte value) => MemoryWrite(address, new[]{ value }); - public void MemoryWrite16(ulong address, short value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new[] { value }); + public void MemoryWrite16(ulong address, short value) => MemoryWrite(address, BitConverter.GetBytes(value)); public void MemoryWrite16(ulong address, ushort value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite32(ulong address, int value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite32(ulong address, int value) => MemoryWrite(address, BitConverter.GetBytes(value)); public void MemoryWrite32(ulong address, uint value) => MemoryWrite(address, BitConverter.GetBytes(value)); - public void MemoryWrite64(ulong address, long value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite64(ulong address, long value) => MemoryWrite(address, BitConverter.GetBytes(value)); public void MemoryWrite64(ulong address, ulong value) => MemoryWrite(address, BitConverter.GetBytes(value)); public void MemoryMap(ulong address, ulong size, MemoryPermission permissions) { - uc.MemMap((long)address, (long)size, (int)permissions); + Uc.MemMap((long)address, (long)size, (int)permissions); } public void MemoryUnmap(ulong address, ulong size) { - uc.MemUnmap((long)address, (long)size); + Uc.MemUnmap((long)address, (long)size); } public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions) { - uc.MemProtect((long)address, (long)size, (int)permissions); + Uc.MemProtect((long)address, (long)size, (int)permissions); } } -} \ No newline at end of file +} From bc53d0046310293a90a4312fa9317bb698dded5c Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 18:37:09 +0200 Subject: [PATCH 657/737] [Ryujinx.Graphics.Vic] Address dotnet-format issues (#5374) * dotnet format style --severity info Some changes were manually reverted. * Restore a few unused methods and variables * Address review comments * Address most dotnet format whitespace warnings * Add comments to disabled warnings * Address IDE0251 warnings * dotnet format whitespace after rebase * Remove SuppressMessage attribute for removed rule --- src/Ryujinx.Graphics.Vic/Image/BufferPool.cs | 2 +- .../Image/InputSurface.cs | 6 +++--- .../Image/SurfaceReader.cs | 13 ++++++------ src/Ryujinx.Graphics.Vic/Rectangle.cs | 2 +- src/Ryujinx.Graphics.Vic/Scaler.cs | 2 +- .../Types/BlendingSlotStruct.cs | 6 +++--- .../Types/ClearRectStruct.cs | 8 ++++---- .../Types/ConfigStruct.cs | 2 +- .../Types/DeinterlaceMode.cs | 2 +- src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs | 2 +- .../Types/LumaKeyStruct.cs | 6 +++--- .../Types/MatrixStruct.cs | 10 +++++----- .../Types/OutputConfig.cs | 8 ++++---- .../Types/OutputSurfaceConfig.cs | 8 ++++---- src/Ryujinx.Graphics.Vic/Types/PipeConfig.cs | 10 +++++----- src/Ryujinx.Graphics.Vic/Types/SlotConfig.cs | 20 ++++++++++--------- .../Types/SlotSurfaceConfig.cs | 6 +++--- src/Ryujinx.Graphics.Vic/VicDevice.cs | 4 ++-- src/Ryujinx.Graphics.Vic/VicRegisters.cs | 4 ++-- 19 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/Ryujinx.Graphics.Vic/Image/BufferPool.cs b/src/Ryujinx.Graphics.Vic/Image/BufferPool.cs index cde7e6ebb..1f7dc08a1 100644 --- a/src/Ryujinx.Graphics.Vic/Image/BufferPool.cs +++ b/src/Ryujinx.Graphics.Vic/Image/BufferPool.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Vic.Image { int index = RentMinimum(length, out T[] bufferArray); - buffer = new Span<T>(bufferArray).Slice(0, length); + buffer = new Span<T>(bufferArray)[..length]; return index; } diff --git a/src/Ryujinx.Graphics.Vic/Image/InputSurface.cs b/src/Ryujinx.Graphics.Vic/Image/InputSurface.cs index 15ac04600..04994468c 100644 --- a/src/Ryujinx.Graphics.Vic/Image/InputSurface.cs +++ b/src/Ryujinx.Graphics.Vic/Image/InputSurface.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vic.Image { ref struct RentedBuffer { - public static RentedBuffer Empty => new RentedBuffer(Span<byte>.Empty, -1); + public static RentedBuffer Empty => new(Span<byte>.Empty, -1); public Span<byte> Data; public int Index; @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Vic.Image Index = index; } - public void Return(BufferPool<byte> pool) + public readonly void Return(BufferPool<byte> pool) { if (Index != -1) { @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Vic.Image Buffer2Index = buffer.Index; } - public void Return(BufferPool<byte> pool) + public readonly void Return(BufferPool<byte> pool) { if (Buffer0Index != -1) { diff --git a/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs b/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs index 10fd9d8d3..079b4ef12 100644 --- a/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs +++ b/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs @@ -21,7 +21,8 @@ namespace Ryujinx.Graphics.Vic.Image { switch (surfaceConfig.SlotPixelFormat) { - case PixelFormat.Y8___V8U8_N420: return ReadNv12(rm, ref config, ref surfaceConfig, ref offsets); + case PixelFormat.Y8___V8U8_N420: + return ReadNv12(rm, ref config, ref surfaceConfig, ref offsets); } Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{surfaceConfig.SlotPixelFormat}\"."); @@ -46,7 +47,7 @@ namespace Ryujinx.Graphics.Vic.Image int yStride = GetPitch(width, 1); int uvStride = GetPitch(input.UvWidth, 2); - Surface output = new Surface(rm.SurfacePool, width, height); + Surface output = new(rm.SurfacePool, width, height); if (Sse41.IsSupported) { @@ -276,7 +277,7 @@ namespace Ryujinx.Graphics.Vic.Image int bytesPerPixel, int planes) { - InputSurface surface = new InputSurface(); + InputSurface surface = new(); surface.Initialize(); @@ -458,7 +459,7 @@ namespace Ryujinx.Graphics.Vic.Image int outSize = dstStride * height; int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer); Span<byte> dst = buffer; - dst = dst.Slice(0, outSize); + dst = dst[..outSize]; for (int y = 0; y < height; y++) { @@ -485,9 +486,9 @@ namespace Ryujinx.Graphics.Vic.Image int outSize = dstStride * height; int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer); Span<byte> dst = buffer; - dst = dst.Slice(0, outSize); + dst = dst[..outSize]; - LayoutConverter.ConvertBlockLinearToLinear(dst.Slice(dstStart), width, height, dstStride, bytesPerPixel, gobBlocksInY, src); + LayoutConverter.ConvertBlockLinearToLinear(dst[dstStart..], width, height, dstStride, bytesPerPixel, gobBlocksInY, src); return new RentedBuffer(dst, bufferIndex); } diff --git a/src/Ryujinx.Graphics.Vic/Rectangle.cs b/src/Ryujinx.Graphics.Vic/Rectangle.cs index 8a8dd63a4..b12560bd2 100644 --- a/src/Ryujinx.Graphics.Vic/Rectangle.cs +++ b/src/Ryujinx.Graphics.Vic/Rectangle.cs @@ -15,4 +15,4 @@ namespace Ryujinx.Graphics.Vic Height = height; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vic/Scaler.cs b/src/Ryujinx.Graphics.Vic/Scaler.cs index 18ae66c4a..7d539299a 100644 --- a/src/Ryujinx.Graphics.Vic/Scaler.cs +++ b/src/Ryujinx.Graphics.Vic/Scaler.cs @@ -121,4 +121,4 @@ namespace Ryujinx.Graphics.Vic } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs b/src/Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs index 86da41d29..2cc6b1e28 100644 --- a/src/Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs +++ b/src/Ryujinx.Graphics.Vic/Types/BlendingSlotStruct.cs @@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Vic.Types { - struct BlendingSlotStruct + readonly struct BlendingSlotStruct { - private long _word0; - private long _word1; + private readonly long _word0; + private readonly long _word1; public int AlphaK1 => (int)_word0.Extract(0, 10); public int AlphaK2 => (int)_word0.Extract(16, 10); diff --git a/src/Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs b/src/Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs index ae582a920..da9888f78 100644 --- a/src/Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs +++ b/src/Ryujinx.Graphics.Vic/Types/ClearRectStruct.cs @@ -2,11 +2,11 @@ namespace Ryujinx.Graphics.Vic.Types { - struct ClearRectStruct + readonly struct ClearRectStruct { -#pragma warning disable CS0649 - private long _word0; - private long _word1; +#pragma warning disable CS0649 // Field is never assigned to + private readonly long _word0; + private readonly long _word1; #pragma warning restore CS0649 public int ClearRect0Left => (int)_word0.Extract(0, 14); diff --git a/src/Ryujinx.Graphics.Vic/Types/ConfigStruct.cs b/src/Ryujinx.Graphics.Vic/Types/ConfigStruct.cs index 5edc81839..bf94606c0 100644 --- a/src/Ryujinx.Graphics.Vic/Types/ConfigStruct.cs +++ b/src/Ryujinx.Graphics.Vic/Types/ConfigStruct.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vic.Types { struct ConfigStruct { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public PipeConfig PipeConfig; public OutputConfig OutputConfig; public OutputSurfaceConfig OutputSurfaceConfig; diff --git a/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs b/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs index aa0654f01..216995b39 100644 --- a/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs +++ b/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Graphics.Vic.Types Disi1, WeaveLumaBobFieldChroma } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs b/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs index 91f5751b4..f6007f924 100644 --- a/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs +++ b/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs @@ -76,4 +76,4 @@ namespace Ryujinx.Graphics.Vic.Types return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs b/src/Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs index 5d83bd711..0cb5e6d9d 100644 --- a/src/Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs +++ b/src/Ryujinx.Graphics.Vic/Types/LumaKeyStruct.cs @@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Vic.Types { - struct LumaKeyStruct + readonly struct LumaKeyStruct { - private long _word0; - private long _word1; + private readonly long _word0; + private readonly long _word1; public int LumaCoeff0 => (int)_word0.Extract(0, 20); public int LumaCoeff1 => (int)_word0.Extract(20, 20); diff --git a/src/Ryujinx.Graphics.Vic/Types/MatrixStruct.cs b/src/Ryujinx.Graphics.Vic/Types/MatrixStruct.cs index c0a4c34ee..f89a142f8 100644 --- a/src/Ryujinx.Graphics.Vic/Types/MatrixStruct.cs +++ b/src/Ryujinx.Graphics.Vic/Types/MatrixStruct.cs @@ -2,12 +2,12 @@ namespace Ryujinx.Graphics.Vic.Types { - struct MatrixStruct + readonly struct MatrixStruct { - private long _word0; - private long _word1; - private long _word2; - private long _word3; + private readonly long _word0; + private readonly long _word1; + private readonly long _word2; + private readonly long _word3; public int MatrixCoeff00 => (int)_word0.ExtractSx(0, 20); public int MatrixCoeff10 => (int)_word0.ExtractSx(20, 20); diff --git a/src/Ryujinx.Graphics.Vic/Types/OutputConfig.cs b/src/Ryujinx.Graphics.Vic/Types/OutputConfig.cs index 7b8669942..10ceb240f 100644 --- a/src/Ryujinx.Graphics.Vic/Types/OutputConfig.cs +++ b/src/Ryujinx.Graphics.Vic/Types/OutputConfig.cs @@ -2,11 +2,11 @@ namespace Ryujinx.Graphics.Vic.Types { - struct OutputConfig + readonly struct OutputConfig { -#pragma warning disable CS0649 - private long _word0; - private long _word1; +#pragma warning disable CS0649 // Field is never assigned to + private readonly long _word0; + private readonly long _word1; #pragma warning restore CS0649 public int AlphaFillMode => (int)_word0.Extract(0, 3); diff --git a/src/Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs b/src/Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs index 6a8b21e13..ad236882d 100644 --- a/src/Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs +++ b/src/Ryujinx.Graphics.Vic/Types/OutputSurfaceConfig.cs @@ -2,11 +2,11 @@ namespace Ryujinx.Graphics.Vic.Types { - struct OutputSurfaceConfig + readonly struct OutputSurfaceConfig { -#pragma warning disable CS0649 - private long _word0; - private long _word1; +#pragma warning disable CS0649 // Field is never assigned to + private readonly long _word0; + private readonly long _word1; #pragma warning restore CS0649 public PixelFormat OutPixelFormat => (PixelFormat)_word0.Extract(0, 7); diff --git a/src/Ryujinx.Graphics.Vic/Types/PipeConfig.cs b/src/Ryujinx.Graphics.Vic/Types/PipeConfig.cs index 76720eb1c..408bd83ea 100644 --- a/src/Ryujinx.Graphics.Vic/Types/PipeConfig.cs +++ b/src/Ryujinx.Graphics.Vic/Types/PipeConfig.cs @@ -2,12 +2,12 @@ namespace Ryujinx.Graphics.Vic.Types { - struct PipeConfig + readonly struct PipeConfig { -#pragma warning disable CS0169, CS0649 - private long _word0; - private long _word1; -#pragma warning restore CS0169, CS0649 +#pragma warning disable CS0169, CS0649, IDE0051 // Remove unused private member + private readonly long _word0; + private readonly long _word1; +#pragma warning restore CS0169, CS0649, IDE0051 public int DownsampleHoriz => (int)_word0.Extract(0, 11); public int DownsampleVert => (int)_word0.Extract(16, 11); diff --git a/src/Ryujinx.Graphics.Vic/Types/SlotConfig.cs b/src/Ryujinx.Graphics.Vic/Types/SlotConfig.cs index aba61add3..4031bf995 100644 --- a/src/Ryujinx.Graphics.Vic/Types/SlotConfig.cs +++ b/src/Ryujinx.Graphics.Vic/Types/SlotConfig.cs @@ -2,16 +2,18 @@ namespace Ryujinx.Graphics.Vic.Types { - struct SlotConfig + readonly struct SlotConfig { - private long _word0; - private long _word1; - private long _word2; - private long _word3; - private long _word4; - private long _word5; - private long _word6; - private long _word7; + private readonly long _word0; + private readonly long _word1; + private readonly long _word2; + private readonly long _word3; + private readonly long _word4; + private readonly long _word5; + private readonly long _word6; +#pragma warning disable IDE0051 // Remove unused private member + private readonly long _word7; +#pragma warning restore IDE0051 public bool SlotEnable => _word0.Extract(0); public bool DeNoise => _word0.Extract(1); diff --git a/src/Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs b/src/Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs index 4492c85f9..0be0d4f43 100644 --- a/src/Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs +++ b/src/Ryujinx.Graphics.Vic/Types/SlotSurfaceConfig.cs @@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Vic.Types { - struct SlotSurfaceConfig + readonly struct SlotSurfaceConfig { - private long _word0; - private long _word1; + private readonly long _word0; + private readonly long _word1; public PixelFormat SlotPixelFormat => (PixelFormat)_word0.Extract(0, 7); public int SlotChromaLocHoriz => (int)_word0.Extract(7, 2); diff --git a/src/Ryujinx.Graphics.Vic/VicDevice.cs b/src/Ryujinx.Graphics.Vic/VicDevice.cs index 8b66727df..b2bc98d81 100644 --- a/src/Ryujinx.Graphics.Vic/VicDevice.cs +++ b/src/Ryujinx.Graphics.Vic/VicDevice.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Vic { ConfigStruct config = ReadIndirect<ConfigStruct>(_state.State.SetConfigStructOffset); - using Surface output = new Surface( + using Surface output = new( _rm.SurfacePool, config.OutputSurfaceConfig.OutSurfaceWidth + 1, config.OutputSurfaceConfig.OutSurfaceHeight + 1); @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Vic int targetW = Math.Min(output.Width - targetX, Math.Abs(x2 - x1)); int targetH = Math.Min(output.Height - targetY, Math.Abs(y2 - y1)); - Rectangle targetRect = new Rectangle(targetX, targetY, targetW, targetH); + Rectangle targetRect = new(targetX, targetY, targetW, targetH); Blender.BlendOne(output, src, ref slot, targetRect); } diff --git a/src/Ryujinx.Graphics.Vic/VicRegisters.cs b/src/Ryujinx.Graphics.Vic/VicRegisters.cs index 1c11b5549..1e002839c 100644 --- a/src/Ryujinx.Graphics.Vic/VicRegisters.cs +++ b/src/Ryujinx.Graphics.Vic/VicRegisters.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vic { struct PlaneOffsets { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint LumaOffset; public uint ChromaUOffset; public uint ChromaVOffset; @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vic struct VicRegisters { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Array64<uint> Reserved0; public uint Nop; public Array15<uint> Reserved104; From 2b2ce68f07041189977515aa5b6ff6f04477d386 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 18:37:53 +0200 Subject: [PATCH 658/737] [Ryujinx.Tests.Memory] Address dotnet-format issues (#5390) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Address dotnet format CA1822 warnings * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Final dotnet format pass and fix naming rule violations * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Remove unused constant --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../MultiRegionTrackingTests.cs | 21 ++++++------ src/Ryujinx.Tests.Memory/Tests.cs | 20 ++++++------ src/Ryujinx.Tests.Memory/TrackingTests.cs | 32 ++++++++++--------- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs index 674d23565..dffbe74d8 100644 --- a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs @@ -9,8 +9,6 @@ namespace Ryujinx.Tests.Memory { public class MultiRegionTrackingTests { - private const int RndCnt = 3; - private const ulong MemorySize = 0x8000; private const int PageSize = 4096; @@ -39,7 +37,7 @@ namespace Ryujinx.Tests.Memory (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity, 0); } - private void RandomOrder(Random random, List<int> indices, Action<int> action) + private static void RandomOrder(Random random, List<int> indices, Action<int> action) { List<int> choices = indices.ToList(); @@ -51,7 +49,7 @@ namespace Ryujinx.Tests.Memory } } - private int ExpectQueryInOrder(IMultiRegionHandle handle, ulong startAddress, ulong size, Func<ulong, bool> addressPredicate) + private static int ExpectQueryInOrder(IMultiRegionHandle handle, ulong startAddress, ulong size, Func<ulong, bool> addressPredicate) { int regionCount = 0; ulong lastAddress = startAddress; @@ -67,7 +65,7 @@ namespace Ryujinx.Tests.Memory return regionCount; } - private int ExpectQueryInOrder(IMultiRegionHandle handle, ulong startAddress, ulong size, Func<ulong, bool> addressPredicate, int sequenceNumber) + private static int ExpectQueryInOrder(IMultiRegionHandle handle, ulong startAddress, ulong size, Func<ulong, bool> addressPredicate, int sequenceNumber) { int regionCount = 0; ulong lastAddress = startAddress; @@ -83,9 +81,9 @@ namespace Ryujinx.Tests.Memory return regionCount; } - private void PreparePages(IMultiRegionHandle handle, int pageCount, ulong address = 0) + private static void PreparePages(IMultiRegionHandle handle, int pageCount, ulong address = 0) { - Random random = new Random(); + Random random = new(); // Make sure the list has minimum granularity (smart region changes granularity based on requested ranges) RandomOrder(random, Enumerable.Range(0, pageCount).ToList(), (i) => @@ -105,7 +103,7 @@ namespace Ryujinx.Tests.Memory const int pageCount = 32; IMultiRegionHandle handle = GetGranular(smart, 0, PageSize * pageCount, PageSize); - Random random = new Random(); + Random random = new(); PreparePages(handle, pageCount); @@ -149,7 +147,7 @@ namespace Ryujinx.Tests.Memory PreparePages(handle, pageCount); - Random random = new Random(); + Random random = new(); IEnumerable<int> halfRange = Enumerable.Range(0, pageCount / 2); List<int> odd = halfRange.Select(x => x * 2 + 1).ToList(); @@ -240,7 +238,8 @@ namespace Ryujinx.Tests.Memory ulong expectedAddress = 0; // Expect each region to trigger in its entirety, in address ascending order. - handle.QueryModified((address, size) => { + handle.QueryModified((address, size) => + { int region = regionSizes[regionInd++]; Assert.AreEqual(address, expectedAddress); @@ -437,4 +436,4 @@ namespace Ryujinx.Tests.Memory Assert.AreEqual(pagesModified, new bool[] { true, false, false }); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests.Memory/Tests.cs b/src/Ryujinx.Tests.Memory/Tests.cs index 5ab01d5a5..bfc6344b7 100644 --- a/src/Ryujinx.Tests.Memory/Tests.cs +++ b/src/Ryujinx.Tests.Memory/Tests.cs @@ -7,14 +7,14 @@ namespace Ryujinx.Tests.Memory { public class Tests { - private static readonly ulong MemorySize = MemoryBlock.GetPageSize() * 8; + private static readonly ulong _memorySize = MemoryBlock.GetPageSize() * 8; private MemoryBlock _memoryBlock; [SetUp] public void Setup() { - _memoryBlock = new MemoryBlock(MemorySize); + _memoryBlock = new MemoryBlock(_memorySize); } [TearDown] @@ -47,8 +47,8 @@ namespace Ryujinx.Tests.Memory ulong pageSize = MemoryBlock.GetPageSize(); ulong blockSize = MemoryBlock.GetPageSize() * 16; - using MemoryBlock backing = new MemoryBlock(blockSize, MemoryAllocationFlags.Mirrorable); - using MemoryBlock toAlias = new MemoryBlock(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + using MemoryBlock backing = new(blockSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); toAlias.MapView(backing, pageSize, 0, pageSize * 4); toAlias.UnmapView(backing, pageSize * 3, pageSize); @@ -66,10 +66,10 @@ namespace Ryujinx.Tests.Memory int pageBits = (int)ulong.Log2(pageSize); ulong blockSize = MemoryBlock.GetPageSize() * 128; - using MemoryBlock backing = new MemoryBlock(blockSize, MemoryAllocationFlags.Mirrorable); - using MemoryBlock toAlias = new MemoryBlock(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + using MemoryBlock backing = new(blockSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); - Random rng = new Random(123); + Random rng = new(123); for (int i = 0; i < 20000; i++) { @@ -101,8 +101,8 @@ namespace Ryujinx.Tests.Memory ulong pageSize = MemoryBlock.GetPageSize(); ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that. - using MemoryBlock backing = new MemoryBlock(pageSize, MemoryAllocationFlags.Mirrorable); - using MemoryBlock toAlias = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + using MemoryBlock backing = new(pageSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); for (ulong offset = 0; offset < size; offset += pageSize) { @@ -115,4 +115,4 @@ namespace Ryujinx.Tests.Memory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests.Memory/TrackingTests.cs b/src/Ryujinx.Tests.Memory/TrackingTests.cs index faf295575..dce73cf4f 100644 --- a/src/Ryujinx.Tests.Memory/TrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/TrackingTests.cs @@ -102,15 +102,17 @@ namespace Ryujinx.Tests.Memory allHandle.Reprotect(); (ulong address, ulong size)? readTrackingTriggeredAll = null; - Action registerReadAction = () => + + void RegisterReadAction() { readTrackingTriggeredAll = null; allHandle.RegisterAction((address, size) => { readTrackingTriggeredAll = (address, size); }); - }; - registerReadAction(); + } + + RegisterReadAction(); // Create 16 page sized handles contained within the allHandle. RegionHandle[] containedHandles = new RegionHandle[16]; @@ -149,7 +151,7 @@ namespace Ryujinx.Tests.Memory } // Clear flags and reset read action. - registerReadAction(); + RegisterReadAction(); allHandle.Reprotect(); containedHandles[i].Reprotect(); } @@ -157,8 +159,8 @@ namespace Ryujinx.Tests.Memory [Test] public void PageAlignment( - [Values(1ul, 512ul, 2048ul, 4096ul, 65536ul)] [Random(1ul, 65536ul, RndCnt)] ulong address, - [Values(1ul, 4ul, 1024ul, 4096ul, 65536ul)] [Random(1ul, 65536ul, RndCnt)] ulong size) + [Values(1ul, 512ul, 2048ul, 4096ul, 65536ul)][Random(1ul, 65536ul, RndCnt)] ulong address, + [Values(1ul, 4ul, 1024ul, 4096ul, 65536ul)][Random(1ul, 65536ul, RndCnt)] ulong size) { ulong alignedStart = (address / PageSize) * PageSize; ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize; @@ -204,7 +206,7 @@ namespace Ryujinx.Tests.Memory long finishedTime = 0; RegionHandle[] handles = new RegionHandle[threadCount * handlesPerThread]; - Random globalRand = new Random(); + Random globalRand = new(); for (int i = 0; i < handles.Length; i++) { @@ -212,7 +214,7 @@ namespace Ryujinx.Tests.Memory handles[i].Reprotect(); } - List<Thread> testThreads = new List<Thread>(); + List<Thread> testThreads = new(); // Dirty flag consumer threads int dirtyFlagReprotects = 0; @@ -224,7 +226,7 @@ namespace Ryujinx.Tests.Memory int handleBase = randSeed * handlesPerThread; while (Stopwatch.GetTimestamp() < finishedTime) { - Random random = new Random(randSeed); + Random random = new(randSeed); RegionHandle handle = handles[handleBase + random.Next(handlesPerThread)]; if (handle.Dirty) @@ -243,7 +245,7 @@ namespace Ryujinx.Tests.Memory int randSeed = i; testThreads.Add(new Thread(() => { - Random random = new Random(randSeed); + Random random = new(randSeed); ulong handleBase = (ulong)(randSeed * handlesPerThread * PageSize); while (Stopwatch.GetTimestamp() < finishedTime) { @@ -261,7 +263,7 @@ namespace Ryujinx.Tests.Memory testThreads.Add(new Thread(() => { int maxAddress = threadCount * handlesPerThread * PageSize; - Random random = new Random(randSeed + 512); + Random random = new(randSeed + 512); while (Stopwatch.GetTimestamp() < finishedTime) { RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536), 0); @@ -303,7 +305,7 @@ namespace Ryujinx.Tests.Memory int signalThreadsDone = 0; bool isRegistered = false; - Action registerReadAction = () => + void RegisterReadAction() { registeredCount++; handle.RegisterAction((address, size) => @@ -311,7 +313,7 @@ namespace Ryujinx.Tests.Memory isRegistered = false; Interlocked.Increment(ref triggeredCount); }); - }; + } const int threadCount = 16; const int iterationCount = 10000; @@ -322,7 +324,7 @@ namespace Ryujinx.Tests.Memory int randSeed = i; signalThreads[i] = new Thread(() => { - Random random = new Random(randSeed); + Random random = new(randSeed); for (int j = 0; j < iterationCount; j++) { _tracking.VirtualMemoryEvent((ulong)random.Next(PageSize), 4, false); @@ -346,7 +348,7 @@ namespace Ryujinx.Tests.Memory if (!isRegistered) { isRegistered = true; - registerReadAction(); + RegisterReadAction(); } } From 7ffe7f8442eb70980da55832a293fab52ff58cf1 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 19:03:48 +0200 Subject: [PATCH 659/737] [Ryujinx.Graphics.Nvdec.FFmpeg] Address dotnet-format issues (#5370) * dotnet format style --severity info Some changes were manually reverted. * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Address IDE0251 warnings * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass --- .../FFmpegContext.cs | 6 +++--- .../H264/Decoder.cs | 4 ++-- .../H264/H264BitStreamWriter.cs | 6 +++--- .../H264/SpsAndPpsReconstruction.cs | 2 +- .../Native/AVCodec.cs | 2 +- .../Native/AVCodec501.cs | 2 +- .../Native/AVCodecContext.cs | 2 +- .../Native/AVFrame.cs | 2 +- .../Native/AVPacket.cs | 16 +++++++--------- .../Native/FFCodec.cs | 4 ++-- .../Native/FFCodecLegacy.cs | 4 ++-- .../Native/FFmpegApi.cs | 7 +++---- src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs | 6 +++--- src/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs | 6 +++--- 14 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs index 572ceaaab..284a35a24 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs @@ -12,8 +12,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg private readonly AVCodec_decode _decodeFrame; private static readonly FFmpegApi.av_log_set_callback_callback _logFunc; private readonly AVCodec* _codec; - private AVPacket* _packet; - private AVCodecContext* _context; + private readonly AVPacket* _packet; + private readonly AVCodecContext* _context; public FFmpegContext(AVCodecID codecId) { @@ -164,7 +164,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg FFmpegApi.av_packet_free(ppPacket); } - FFmpegApi.avcodec_close(_context); + _ = FFmpegApi.avcodec_close(_context); fixed (AVCodecContext** ppContext = &_context) { diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs index d8b213e55..7dd643ce3 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 private readonly byte[] _workBuffer = new byte[WorkBufferSize]; - private FFmpegContext _context = new FFmpegContext(AVCodecID.AV_CODEC_ID_H264); + private FFmpegContext _context = new(AVCodecID.AV_CODEC_ID_H264); private int _oldOutputWidth; private int _oldOutputHeight; @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 byte[] output = new byte[data.Length + prep.Length]; prep.CopyTo(output); - data.CopyTo(new Span<byte>(output).Slice(prep.Length)); + data.CopyTo(new Span<byte>(output)[prep.Length..]); return output; } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs index 3d3b32933..57ab9fb53 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs @@ -84,9 +84,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 Flush(); } - public Span<byte> AsSpan() + public readonly Span<byte> AsSpan() { - return new Span<byte>(_workBuffer).Slice(0, _offset); + return new Span<byte>(_workBuffer)[.._offset]; } public void WriteU(uint value, int valueSize) => WriteBits((int)value, valueSize); @@ -118,4 +118,4 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 WriteBits((int)value, size - 1); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs index 57fb65d50..d8bf6f746 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 { public static Span<byte> Reconstruct(ref H264PictureInfo pictureInfo, byte[] workBuffer) { - H264BitStreamWriter writer = new H264BitStreamWriter(workBuffer); + H264BitStreamWriter writer = new(workBuffer); // Sequence Parameter Set. writer.WriteU(1, 24); diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs index 46d3ad61c..434b9b29b 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { struct AVCodec { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public unsafe byte* Name; public unsafe byte* LongName; public int Type; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs index 47d4969ac..7b82b5ddf 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { struct AVCodec501 { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public unsafe byte* Name; public unsafe byte* LongName; public int Type; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs index 6c9fbc893..a9605c63f 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { struct AVCodecContext { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public unsafe IntPtr AvClass; public int LogLevelOffset; public int CodecType; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs index faaf5c7d8..cdc9ebfdc 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { struct AVFrame { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Array8<IntPtr> Data; public Array8<int> LineSize; public IntPtr ExtendedData; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs index d5b021040..47b9bef98 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs @@ -1,26 +1,24 @@ -using System; - -using AVBufferRef = System.IntPtr; +using AVBufferRef = System.IntPtr; namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { struct AVPacket { -#pragma warning disable CS0649 - public unsafe AVBufferRef *Buf; +#pragma warning disable CS0649 // Field is never assigned to + public unsafe AVBufferRef* Buf; public long Pts; public long Dts; public unsafe byte* Data; public int Size; public int StreamIndex; public int Flags; - public IntPtr SizeData; + public AVBufferRef SizeData; public int SizeDataElems; public long Duration; public long Position; - public IntPtr Opaque; - public unsafe AVBufferRef *OpaqueRef; + public AVBufferRef Opaque; + public unsafe AVBufferRef* OpaqueRef; public AVRational TimeBase; #pragma warning restore CS0649 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs index 4df45af46..89a33697c 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs @@ -2,9 +2,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { - struct FFCodec<T> where T: struct + struct FFCodec<T> where T : struct { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public T Base; public int CapsInternalOrCbType; public int PrivDataSize; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs index 910270a55..630e2c179 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs @@ -2,9 +2,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { - struct FFCodecLegacy<T> where T: struct + struct FFCodecLegacy<T> where T : struct { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public T Base; public uint CapsInternalOrCbType; public int PrivDataSize; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs index d173a4129..262d26431 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public const string AvCodecLibraryName = "avcodec"; public const string AvUtilLibraryName = "avutil"; - private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new Dictionary<string, (int, int)> + private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new() { { AvCodecLibraryName, (58, 59) }, { AvUtilLibraryName, (56, 57) } @@ -61,9 +61,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { NativeLibrary.SetDllImportResolver(typeof(FFmpegApi).Assembly, (name, assembly, path) => { - IntPtr handle; - if (name == AvUtilLibraryName && TryLoadWhitelistedLibrary(AvUtilLibraryName, assembly, path, out handle)) + if (name == AvUtilLibraryName && TryLoadWhitelistedLibrary(AvUtilLibraryName, assembly, path, out nint handle)) { return handle; } @@ -106,7 +105,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native internal static unsafe partial AVCodecContext* avcodec_alloc_context3(AVCodec* codec); [LibraryImport(AvCodecLibraryName)] - internal static unsafe partial int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void **options); + internal static unsafe partial int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void** options); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial int avcodec_close(AVCodecContext* avctx); diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs index 1ca9d1d53..959791b6e 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg public int RequestedWidth { get; } public int RequestedHeight { get; } - public Plane YPlane => new Plane((IntPtr)Frame->Data[0], Stride * Height); - public Plane UPlane => new Plane((IntPtr)Frame->Data[1], UvStride * UvHeight); - public Plane VPlane => new Plane((IntPtr)Frame->Data[2], UvStride * UvHeight); + public Plane YPlane => new((IntPtr)Frame->Data[0], Stride * Height); + public Plane UPlane => new((IntPtr)Frame->Data[1], UvStride * UvHeight); + public Plane VPlane => new((IntPtr)Frame->Data[2], UvStride * UvHeight); public FrameField Field => Frame->InterlacedFrame != 0 ? FrameField.Interlaced : FrameField.Progressive; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs index 3570c3ecc..5e38c8c8e 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Vp8 { public bool IsHardwareAccelerated => false; - private readonly FFmpegContext _context = new FFmpegContext(AVCodecID.AV_CODEC_ID_VP8); + private readonly FFmpegContext _context = new(AVCodecID.AV_CODEC_ID_VP8); public ISurface CreateSurface(int width, int height) { @@ -43,11 +43,11 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Vp8 frame[9] = (byte)((pictureInfo.FrameHeight >> 8) & 0x3F); } - bitstream.CopyTo(new Span<byte>(frame).Slice(uncompHeaderSize)); + bitstream.CopyTo(new Span<byte>(frame)[uncompHeaderSize..]); return _context.DecodeFrame(outSurf, frame) == 0; } public void Dispose() => _context.Dispose(); } -} \ No newline at end of file +} From fd01259d2b43efcb67efdb32c8879c67e96ccdfe Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:37:33 +0200 Subject: [PATCH 660/737] [Ryujinx.ShaderTools] Address dotnet-format issues (#5388) * dotnet format style --severity info Some changes were manually reverted. * dotnet format whitespace after rebase --- src/Ryujinx.ShaderTools/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.ShaderTools/Program.cs b/src/Ryujinx.ShaderTools/Program.cs index 3acebbda6..55ff12bea 100644 --- a/src/Ryujinx.ShaderTools/Program.cs +++ b/src/Ryujinx.ShaderTools/Program.cs @@ -20,7 +20,7 @@ namespace Ryujinx.ShaderTools public ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize) { - return MemoryMarshal.Cast<byte, ulong>(new ReadOnlySpan<byte>(_data).Slice((int)address)); + return MemoryMarshal.Cast<byte, ulong>(new ReadOnlySpan<byte>(_data)[(int)address..]); } } @@ -53,7 +53,7 @@ namespace Ryujinx.ShaderTools byte[] data = File.ReadAllBytes(options.InputPath); - TranslationOptions translationOptions = new TranslationOptions(options.TargetLanguage, options.TargetApi, flags); + TranslationOptions translationOptions = new(options.TargetLanguage, options.TargetApi, flags); ShaderProgram program = Translator.CreateContext(0, new GpuAccessor(data), translationOptions).Translate(); @@ -90,4 +90,4 @@ namespace Ryujinx.ShaderTools .WithNotParsed(errors => errors.Output()); } } -} \ No newline at end of file +} From 07fc3ded687fd1321f9ae73d0c72b2d7566c87d5 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:44:42 +0200 Subject: [PATCH 661/737] [Ryujinx.Graphics.Nvdec] Address dotnet-format issues (#5369) * dotnet format style --severity info Some changes were manually reverted. * Restore a few unused methods and variables * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add previously silenced warnings back I have no clue how these disappeared * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Address IDE0251 warnings * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass --- src/Ryujinx.Graphics.Nvdec/H264Decoder.cs | 10 ++--- .../Image/SurfaceReader.cs | 2 +- .../Image/SurfaceWriter.cs | 8 ++-- .../NvdecDecoderContext.cs | 2 +- src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs | 2 +- src/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs | 2 +- src/Ryujinx.Graphics.Nvdec/NvdecStatus.cs | 4 +- .../Types/H264/PictureInfo.cs | 39 ++++++++++--------- .../Types/H264/ReferenceFrame.cs | 4 +- .../Types/Vp8/PictureInfo.cs | 2 +- .../Types/Vp9/EntropyProbs.cs | 2 +- .../Types/Vp9/FrameSize.cs | 2 +- .../Types/Vp9/FrameStats.cs | 2 +- .../Types/Vp9/LoopFilter.cs | 2 +- .../Types/Vp9/PictureInfo.cs | 4 +- .../Types/Vp9/Segmentation.cs | 2 +- src/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs | 2 +- src/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs | 10 ++--- 18 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/Ryujinx.Graphics.Nvdec/H264Decoder.cs b/src/Ryujinx.Graphics.Nvdec/H264Decoder.cs index ecc7dbc79..c99d4a176 100644 --- a/src/Ryujinx.Graphics.Nvdec/H264Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec/H264Decoder.cs @@ -17,12 +17,12 @@ namespace Ryujinx.Graphics.Nvdec ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize); - int width = (int)pictureInfo.PicWidthInMbs * MbSizeInPixels; + int width = (int)pictureInfo.PicWidthInMbs * MbSizeInPixels; int height = (int)pictureInfo.PicHeightInMbs * MbSizeInPixels; int surfaceIndex = (int)pictureInfo.OutputSurfaceIndex; - uint lumaOffset = state.SetPictureLumaOffset[surfaceIndex]; + uint lumaOffset = state.SetPictureLumaOffset[surfaceIndex]; uint chromaOffset = state.SetPictureChromaOffset[surfaceIndex]; Decoder decoder = context.GetH264Decoder(); @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Nvdec SurfaceWriter.Write( rm.Gmm, outputSurface, - lumaOffset + pictureInfo.LumaFrameOffset, + lumaOffset + pictureInfo.LumaFrameOffset, chromaOffset + pictureInfo.ChromaFrameOffset); } else @@ -44,9 +44,9 @@ namespace Ryujinx.Graphics.Nvdec SurfaceWriter.WriteInterlaced( rm.Gmm, outputSurface, - lumaOffset + pictureInfo.LumaTopFieldOffset, + lumaOffset + pictureInfo.LumaTopFieldOffset, chromaOffset + pictureInfo.ChromaTopFieldOffset, - lumaOffset + pictureInfo.LumaBottomFieldOffset, + lumaOffset + pictureInfo.LumaBottomFieldOffset, chromaOffset + pictureInfo.ChromaBottomFieldOffset); } } diff --git a/src/Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs index 039a25832..598b71992 100644 --- a/src/Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs +++ b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceReader.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Nvdec.Image int width, int height) { - OffsetCalculator calc = new OffsetCalculator(width, height, 0, false, 2, 2); + OffsetCalculator calc = new(width, height, 0, false, 2, 2); if (Sse2.IsSupported) { diff --git a/src/Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs index cc5c251be..dd67252af 100644 --- a/src/Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs +++ b/src/Ryujinx.Graphics.Nvdec/Image/SurfaceWriter.cs @@ -60,7 +60,7 @@ namespace Ryujinx.Graphics.Nvdec.Image WriteLuma( lumaBottom.Memory.Span, - surface.YPlane.AsSpan().Slice(surface.Stride), + surface.YPlane.AsSpan()[surface.Stride..], surface.Stride * 2, surface.Width, surface.Height / 2); @@ -80,8 +80,8 @@ namespace Ryujinx.Graphics.Nvdec.Image WriteChroma( chromaBottom.Memory.Span, - surface.UPlane.AsSpan().Slice(surface.UvStride), - surface.VPlane.AsSpan().Slice(surface.UvStride), + surface.UPlane.AsSpan()[surface.UvStride..], + surface.VPlane.AsSpan()[surface.UvStride..], surface.UvStride * 2, surface.UvWidth, surface.UvHeight / 2); @@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Nvdec.Image int width, int height) { - OffsetCalculator calc = new OffsetCalculator(width, height, 0, false, 2, 2); + OffsetCalculator calc = new(width, height, 0, false, 2, 2); if (Sse2.IsSupported) { diff --git a/src/Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs b/src/Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs index 54934bc5a..aaa734a88 100644 --- a/src/Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs +++ b/src/Ryujinx.Graphics.Nvdec/NvdecDecoderContext.cs @@ -26,4 +26,4 @@ namespace Ryujinx.Graphics.Nvdec _vp8Decoder = null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs b/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs index ef8185f4d..4ab7886d0 100644 --- a/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs +++ b/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Nvdec private readonly DeviceState<NvdecRegisters> _state; private long _currentId; - private ConcurrentDictionary<long, NvdecDecoderContext> _contexts; + private readonly ConcurrentDictionary<long, NvdecDecoderContext> _contexts; private NvdecDecoderContext _currentContext; public NvdecDevice(MemoryManager gmm) diff --git a/src/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs b/src/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs index cf8677838..5effcb498 100644 --- a/src/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs +++ b/src/Ryujinx.Graphics.Nvdec/NvdecRegisters.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Nvdec { struct NvdecRegisters { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Array64<uint> Reserved0; public uint Nop; public Array63<uint> Reserved104; diff --git a/src/Ryujinx.Graphics.Nvdec/NvdecStatus.cs b/src/Ryujinx.Graphics.Nvdec/NvdecStatus.cs index 0712af88b..1d6b4a60d 100644 --- a/src/Ryujinx.Graphics.Nvdec/NvdecStatus.cs +++ b/src/Ryujinx.Graphics.Nvdec/NvdecStatus.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Nvdec { struct NvdecStatus { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint MbsCorrectlyDecoded; public uint MbsInError; public uint Reserved; @@ -13,4 +13,4 @@ namespace Ryujinx.Graphics.Nvdec public uint SliceHeaderErrorCode; #pragma warning restore CS0649 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs index 7c779dff3..c0e0a463d 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs @@ -5,8 +5,9 @@ namespace Ryujinx.Graphics.Nvdec.Types.H264 { struct PictureInfo { -#pragma warning disable CS0169, CS0649 +#pragma warning disable IDE0051, CS0169, CS0649 // Remove unused private member Array18<uint> Unknown0; +#pragma warning restore IDE0051 public uint BitstreamSize; public uint NumSlices; public uint Unknown50; @@ -50,24 +51,24 @@ namespace Ryujinx.Graphics.Nvdec.Types.H264 public Array10<uint> Unknown2D4; #pragma warning restore CS0169, CS0649 - public bool MbAdaptiveFrameFieldFlag => (Flags & (1 << 0)) != 0; - public bool Direct8x8InferenceFlag => (Flags & (1 << 1)) != 0; - public bool WeightedPredFlag => (Flags & (1 << 2)) != 0; - public bool ConstrainedIntraPredFlag => (Flags & (1 << 3)) != 0; - public bool IsReference => (Flags & (1 << 4)) != 0; - public bool FieldPicFlag => (Flags & (1 << 5)) != 0; - public bool BottomFieldFlag => (Flags & (1 << 6)) != 0; - public uint Log2MaxFrameNumMinus4 => (uint)(Flags >> 8) & 0xf; - public ushort ChromaFormatIdc => (ushort)((Flags >> 12) & 3); - public uint PicOrderCntType => (uint)(Flags >> 14) & 3; - public int PicInitQpMinus26 => ExtractSx(Flags, 16, 6); - public int ChromaQpIndexOffset => ExtractSx(Flags, 22, 5); - public int SecondChromaQpIndexOffset => ExtractSx(Flags, 27, 5); - public uint WeightedBipredIdc => (uint)(Flags >> 32) & 3; - public uint OutputSurfaceIndex => (uint)(Flags >> 34) & 0x7f; - public uint ColIndex => (uint)(Flags >> 41) & 0x1f; - public ushort FrameNum => (ushort)(Flags >> 46); - public bool QpprimeYZeroTransformBypassFlag => (Flags2 & (1 << 1)) != 0; + public readonly bool MbAdaptiveFrameFieldFlag => (Flags & (1 << 0)) != 0; + public readonly bool Direct8x8InferenceFlag => (Flags & (1 << 1)) != 0; + public readonly bool WeightedPredFlag => (Flags & (1 << 2)) != 0; + public readonly bool ConstrainedIntraPredFlag => (Flags & (1 << 3)) != 0; + public readonly bool IsReference => (Flags & (1 << 4)) != 0; + public readonly bool FieldPicFlag => (Flags & (1 << 5)) != 0; + public readonly bool BottomFieldFlag => (Flags & (1 << 6)) != 0; + public readonly uint Log2MaxFrameNumMinus4 => (uint)(Flags >> 8) & 0xf; + public readonly ushort ChromaFormatIdc => (ushort)((Flags >> 12) & 3); + public readonly uint PicOrderCntType => (uint)(Flags >> 14) & 3; + public readonly int PicInitQpMinus26 => ExtractSx(Flags, 16, 6); + public readonly int ChromaQpIndexOffset => ExtractSx(Flags, 22, 5); + public readonly int SecondChromaQpIndexOffset => ExtractSx(Flags, 27, 5); + public readonly uint WeightedBipredIdc => (uint)(Flags >> 32) & 3; + public readonly uint OutputSurfaceIndex => (uint)(Flags >> 34) & 0x7f; + public readonly uint ColIndex => (uint)(Flags >> 41) & 0x1f; + public readonly ushort FrameNum => (ushort)(Flags >> 46); + public readonly bool QpprimeYZeroTransformBypassFlag => (Flags2 & (1 << 1)) != 0; private static int ExtractSx(ulong packed, int lsb, int length) { diff --git a/src/Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs b/src/Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs index d205a47a4..9ab9d1320 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/H264/ReferenceFrame.cs @@ -4,12 +4,12 @@ namespace Ryujinx.Graphics.Nvdec.Types.H264 { struct ReferenceFrame { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Flags; public Array2<uint> FieldOrderCnt; public uint FrameNum; #pragma warning restore CS0649 - public uint OutputSurfaceIndex => (uint)Flags & 0x7f; + public readonly uint OutputSurfaceIndex => (uint)Flags & 0x7f; } } diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs index 844f21030..76e07a3ff 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp8 { struct PictureInfo { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Array13<uint> Unknown0; public uint GpTimerTimeoutValue; public ushort FrameWidth; diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs index b2858d2d8..dd5221b18 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 { struct EntropyProbs { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Array10<Array10<Array8<byte>>> KfYModeProbE0ToE7; public Array10<Array10<byte>> KfYModeProbE8; public Array3<byte> Padding384; diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs index d449ec4d6..31c08a529 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameSize.cs @@ -2,7 +2,7 @@ { struct FrameSize { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public ushort Width; public ushort Height; public ushort LumaPitch; diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs index 26aab5060..9b0325cf0 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameStats.cs @@ -2,7 +2,7 @@ { struct FrameStats { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Unknown0; public uint Unknown4; public uint Pass2CycleCount; diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs index 7cb0fd7a8..ebef7f677 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/LoopFilter.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 { struct LoopFilter { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public byte ModeRefDeltaEnabled; public Array4<sbyte> RefDeltas; public Array2<sbyte> ModeDeltas; diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs index 7d06f7474..50569dbff 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 { struct PictureInfo { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Array12<uint> Unknown0; public uint BitstreamSize; public uint IsEncrypted; @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 public uint UnknownFC; #pragma warning restore CS0649 - public uint BitDepth => (SurfaceParams >> 1) & 0xf; + public readonly uint BitDepth => (SurfaceParams >> 1) & 0xf; public Vp9PictureInfo Convert() { diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs index f6c4f0b17..ab9954c68 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/Segmentation.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 { struct Segmentation { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public byte Enabled; public byte UpdateMap; public byte TemporalUpdate; diff --git a/src/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs b/src/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs index cce9a5744..e56b23d73 100644 --- a/src/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec/Vp8Decoder.cs @@ -30,4 +30,4 @@ namespace Ryujinx.Graphics.Nvdec rm.Cache.Put(outputSurface); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs b/src/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs index 9bb3529e8..f78bb7028 100644 --- a/src/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec/Vp9Decoder.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Nvdec { static class Vp9Decoder { - private static Decoder _decoder = new Decoder(); + private static readonly Decoder _decoder = new(); public unsafe static void Decode(ResourceManager rm, ref NvdecRegisters state) { @@ -25,9 +25,9 @@ namespace Ryujinx.Graphics.Nvdec return rm.Cache.Get(_decoder, lumaOffset, chromaOffset, size.Width, size.Height); } - ISurface lastSurface = Rent(state.SetPictureLumaOffset[0], state.SetPictureChromaOffset[0], pictureInfo.LastFrameSize); - ISurface goldenSurface = Rent(state.SetPictureLumaOffset[1], state.SetPictureChromaOffset[1], pictureInfo.GoldenFrameSize); - ISurface altSurface = Rent(state.SetPictureLumaOffset[2], state.SetPictureChromaOffset[2], pictureInfo.AltFrameSize); + ISurface lastSurface = Rent(state.SetPictureLumaOffset[0], state.SetPictureChromaOffset[0], pictureInfo.LastFrameSize); + ISurface goldenSurface = Rent(state.SetPictureLumaOffset[1], state.SetPictureChromaOffset[1], pictureInfo.GoldenFrameSize); + ISurface altSurface = Rent(state.SetPictureLumaOffset[2], state.SetPictureChromaOffset[2], pictureInfo.AltFrameSize); ISurface currentSurface = Rent(state.SetPictureLumaOffset[3], state.SetPictureChromaOffset[3], pictureInfo.CurrentFrameSize); Vp9PictureInfo info = pictureInfo.Convert(); @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Nvdec Span<Vp9MvRef> mvsOut = MemoryMarshal.Cast<byte, Vp9MvRef>(mvsRegion.Memory.Span); - uint lumaOffset = state.SetPictureLumaOffset[3]; + uint lumaOffset = state.SetPictureLumaOffset[3]; uint chromaOffset = state.SetPictureChromaOffset[3]; if (_decoder.Decode(ref info, currentSurface, bitstream, mvsIn, mvsOut)) From 42d31f646dfe2dfc8ed646102c06ca44af7b8b07 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:50:59 +0200 Subject: [PATCH 662/737] [Ryujinx.Audio.Backends.SDL2] Address dotnet-format issues (#5364) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Address dotnet format CA1816 warnings * Address most dotnet format whitespace warnings * Run dotnet format style after rebase * Run dotnet format analyzers after rebase * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Update src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../SDL2HardwareDeviceDriver.cs | 10 +++++---- .../SDL2HardwareDeviceSession.cs | 22 +++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index 7bfff5f9b..550cc3491 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -19,12 +19,14 @@ namespace Ryujinx.Audio.Backends.SDL2 private readonly ManualResetEvent _pauseEvent; private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions; - private bool _supportSurroundConfiguration; + private readonly bool _supportSurroundConfiguration; // TODO: Add this to SDL2-CS // NOTE: We use a DllImport here because of marshaling issue for spec. +#pragma warning disable SYSLIB1054 [DllImport("SDL2")] private static extern int SDL_GetDefaultAudioInfo(IntPtr name, out SDL_AudioSpec spec, int isCapture); +#pragma warning restore SYSLIB1054 public SDL2HardwareDeviceDriver() { @@ -90,7 +92,7 @@ namespace Ryujinx.Audio.Backends.SDL2 throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!"); } - SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); + SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); _sessions.TryAdd(session, 0); @@ -135,8 +137,7 @@ namespace Ryujinx.Audio.Backends.SDL2 if (device == 0) { - Logger.Error?.Print(LogClass.Application, - $"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\""); + Logger.Error?.Print(LogClass.Application, $"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\""); return 0; } @@ -156,6 +157,7 @@ namespace Ryujinx.Audio.Backends.SDL2 public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs index 14310b934..0bd73f3c7 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs @@ -12,19 +12,19 @@ namespace Ryujinx.Audio.Backends.SDL2 { class SDL2HardwareDeviceSession : HardwareDeviceSessionOutputBase { - private SDL2HardwareDeviceDriver _driver; - private ConcurrentQueue<SDL2AudioBuffer> _queuedBuffers; - private DynamicRingBuffer _ringBuffer; + private readonly SDL2HardwareDeviceDriver _driver; + private readonly ConcurrentQueue<SDL2AudioBuffer> _queuedBuffers; + private readonly DynamicRingBuffer _ringBuffer; private ulong _playedSampleCount; - private ManualResetEvent _updateRequiredEvent; + private readonly ManualResetEvent _updateRequiredEvent; private uint _outputStream; private bool _hasSetupError; - private SDL_AudioCallback _callbackDelegate; - private int _bytesPerFrame; + private readonly SDL_AudioCallback _callbackDelegate; + private readonly int _bytesPerFrame; private uint _sampleCount; private bool _started; private float _volume; - private ushort _nativeSampleFormat; + private readonly ushort _nativeSampleFormat; public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) { @@ -72,7 +72,7 @@ namespace Ryujinx.Audio.Backends.SDL2 private unsafe void Update(IntPtr userdata, IntPtr stream, int streamLength) { - Span<byte> streamSpan = new Span<byte>((void*)stream, streamLength); + Span<byte> streamSpan = new((void*)stream, streamLength); int maxFrameCount = (int)GetSampleCount(streamLength); int bufferedFrames = _ringBuffer.Length / _bytesPerFrame; @@ -82,7 +82,7 @@ namespace Ryujinx.Audio.Backends.SDL2 if (frameCount == 0) { // SDL2 left the responsibility to the user to clear the buffer. - streamSpan.Fill(0); + streamSpan.Clear(); return; } @@ -96,7 +96,7 @@ namespace Ryujinx.Audio.Backends.SDL2 IntPtr pStreamSrc = (IntPtr)p; // Zero the dest buffer - streamSpan.Fill(0); + streamSpan.Clear(); // Apply volume to written data SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME)); @@ -151,7 +151,7 @@ namespace Ryujinx.Audio.Backends.SDL2 if (_outputStream != 0) { - SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer)); + SDL2AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer)); _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); From f6ada8d1697502a6b37192c9cf9a32acfa28b51a Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:58:44 +0200 Subject: [PATCH 663/737] [Ryujinx.Graphics.Device] Address dotnet-format issues (#5363) * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Address most dotnet format whitespace warnings * dotnet format whitespace after rebase --- src/Ryujinx.Graphics.Device/DeviceState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Device/DeviceState.cs b/src/Ryujinx.Graphics.Device/DeviceState.cs index a9b446e18..b07582a8a 100644 --- a/src/Ryujinx.Graphics.Device/DeviceState.cs +++ b/src/Ryujinx.Graphics.Device/DeviceState.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Device public TState State; - private uint Size => (uint)(Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize; + private static uint Size => (uint)(Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize; private readonly Func<int>[] _readCallbacks; private readonly Action<int>[] _writeCallbacks; From 9860bfb2cdb2a3f6093d936d647b1f254a23120d Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Mon, 26 Jun 2023 03:37:12 +0200 Subject: [PATCH 664/737] misc: memory: Migrate from OutOfMemoryException to SystemException entirely (#5399) Fix a regression with address space allocation while providing more information about the context of the exception. --- src/Ryujinx.Cpu/AddressSpace.cs | 2 +- src/Ryujinx.Memory/MemoryBlock.cs | 6 +++--- src/Ryujinx.Memory/MemoryManagementUnix.cs | 12 ++++++------ src/Ryujinx.Memory/MemoryManagementWindows.cs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs index 0e27b1582..e051244d5 100644 --- a/src/Ryujinx.Cpu/AddressSpace.cs +++ b/src/Ryujinx.Cpu/AddressSpace.cs @@ -199,7 +199,7 @@ namespace Ryujinx.Cpu break; } - catch (OutOfMemoryException) + catch (SystemException) { baseMemory?.Dispose(); mirrorMemory?.Dispose(); diff --git a/src/Ryujinx.Memory/MemoryBlock.cs b/src/Ryujinx.Memory/MemoryBlock.cs index 2cf04628a..e7fc47516 100644 --- a/src/Ryujinx.Memory/MemoryBlock.cs +++ b/src/Ryujinx.Memory/MemoryBlock.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Memory /// </summary> /// <param name="size">Size of the memory block in bytes</param> /// <param name="flags">Flags that controls memory block memory allocation</param> - /// <exception cref="OutOfMemoryException">Throw when there's no enough memory to allocate the requested size</exception> + /// <exception cref="SystemException">Throw when there's an error while allocating the requested size</exception> /// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception> public MemoryBlock(ulong size, MemoryAllocationFlags flags = MemoryAllocationFlags.None) { @@ -66,7 +66,7 @@ namespace Ryujinx.Memory /// </summary> /// <param name="size">Size of the memory block in bytes</param> /// <param name="sharedMemory">Shared memory to use as backing storage for this block</param> - /// <exception cref="OutOfMemoryException">Throw when there's no enough address space left to map the shared memory</exception> + /// <exception cref="SystemException">Throw when there's an error while mapping the shared memory</exception> /// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception> private MemoryBlock(ulong size, IntPtr sharedMemory) { @@ -82,7 +82,7 @@ namespace Ryujinx.Memory /// </summary> /// <returns>A new memory block that shares storage with this one</returns> /// <exception cref="NotSupportedException">Throw when the current memory block does not support mirroring</exception> - /// <exception cref="OutOfMemoryException">Throw when there's no enough address space left to map the shared memory</exception> + /// <exception cref="SystemException">Throw when there's an error while mapping the shared memory</exception> /// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception> public MemoryBlock CreateMirror() { diff --git a/src/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs index d665b2294..4314c3928 100644 --- a/src/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/src/Ryujinx.Memory/MemoryManagementUnix.cs @@ -147,12 +147,12 @@ namespace Ryujinx.Memory fd = shm_open((IntPtr)pMemName, 0x2 | 0x200 | 0x800 | 0x400, 384); // O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0600 if (fd == -1) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } if (shm_unlink((IntPtr)pMemName) != 0) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } } } @@ -165,22 +165,22 @@ namespace Ryujinx.Memory fd = mkstemp((IntPtr)pFileName); if (fd == -1) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } if (unlink((IntPtr)pFileName) != 0) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } } } if (ftruncate(fd, (IntPtr)size) != 0) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } - return (IntPtr)fd; + return fd; } public static void DestroySharedMemory(IntPtr handle) diff --git a/src/Ryujinx.Memory/MemoryManagementWindows.cs b/src/Ryujinx.Memory/MemoryManagementWindows.cs index d7d78bd86..aaed5a637 100644 --- a/src/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/src/Ryujinx.Memory/MemoryManagementWindows.cs @@ -114,7 +114,7 @@ namespace Ryujinx.Memory if (handle == IntPtr.Zero) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } return handle; @@ -134,7 +134,7 @@ namespace Ryujinx.Memory if (ptr == IntPtr.Zero) { - throw new OutOfMemoryException(); + throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } return ptr; From b29ded1d6009dc6a82a0aefd594eda21b5c2d814 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 26 Jun 2023 03:51:16 +0200 Subject: [PATCH 665/737] [Ryujinx.SDL2.Common] Address dotnet-format issues (#5387) * dotnet format style --severity info Some changes were manually reverted. * Address dotnet format CA1816 warnings * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address most dotnet format whitespace warnings * dotnet format whitespace after rebase --- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 5e2deb26d..2642b26fa 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -19,10 +19,7 @@ namespace Ryujinx.SDL2.Common { get { - if (_instance == null) - { - _instance = new SDL2Driver(); - } + _instance ??= new SDL2Driver(); return _instance; } @@ -43,7 +40,7 @@ namespace Ryujinx.SDL2.Common private readonly object _lock = new(); - private SDL2Driver() {} + private SDL2Driver() { } private const string SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS = "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS"; @@ -80,8 +77,15 @@ namespace Ryujinx.SDL2.Common } // First ensure that we only enable joystick events (for connected/disconnected). - SDL_GameControllerEventState(SDL_DISABLE); - SDL_JoystickEventState(SDL_ENABLE); + if (SDL_GameControllerEventState(SDL_IGNORE) != SDL_IGNORE) + { + Logger.Error?.PrintMsg(LogClass.Application, "Couldn't change the state of game controller events."); + } + + if (SDL_JoystickEventState(SDL_ENABLE) < 0) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Failed to enable joystick event polling: {SDL_GetError()}"); + } // Disable all joysticks information, we don't need them no need to flood the event queue for that. SDL_EventState(SDL_EventType.SDL_JOYAXISMOTION, SDL_DISABLE); @@ -153,7 +157,7 @@ namespace Ryujinx.SDL2.Common { const int WaitTimeMs = 10; - using ManualResetEventSlim waitHandle = new ManualResetEventSlim(false); + using ManualResetEventSlim waitHandle = new(false); while (_isRunning) { @@ -199,6 +203,7 @@ namespace Ryujinx.SDL2.Common public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } } From 2de78a2d55a1306761788570ab192897299c55d8 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 26 Jun 2023 03:55:25 +0200 Subject: [PATCH 666/737] [Ryujinx.Input.SDL2] Address dotnet-format issues (#5385) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0052 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address most dotnet format whitespace warnings * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Add trailing commas, log errors instead of throwing and remove redundant code --- src/Ryujinx.Input.SDL2/SDL2Gamepad.cs | 44 ++++++++++++++------- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 11 +++--- src/Ryujinx.Input.SDL2/SDL2Keyboard.cs | 38 +++++++----------- src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs | 3 +- 4 files changed, 52 insertions(+), 44 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index 6a8c1204f..f04fdeb3c 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -57,13 +57,13 @@ namespace Ryujinx.Input.SDL2 private readonly object _userMappingLock = new(); - private List<ButtonMappingEntry> _buttonsUserMapping; + private readonly List<ButtonMappingEntry> _buttonsUserMapping; - private StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count] + private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count] { StickInputId.Unbound, StickInputId.Left, - StickInputId.Right + StickInputId.Right, }; public GamepadFeaturesFlag Features { get; } @@ -85,8 +85,15 @@ namespace Ryujinx.Input.SDL2 // Enable motion tracking if (Features.HasFlag(GamepadFeaturesFlag.Motion)) { - SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE); - SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, SDL_bool.SDL_TRUE); + if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE) != 0) + { + Logger.Error?.Print(LogClass.Hid, $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}."); + } + + if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, SDL_bool.SDL_TRUE) != 0) + { + Logger.Error?.Print(LogClass.Hid, $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}."); + } } } @@ -144,7 +151,10 @@ namespace Ryujinx.Input.SDL2 if (durationMs == uint.MaxValue) { - SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY); + if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) != 0) + { + Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); + } } else if (durationMs > SDL_HAPTIC_INFINITY) { @@ -152,7 +162,10 @@ namespace Ryujinx.Input.SDL2 } else { - SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs); + if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0) + { + Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); + } } } } @@ -182,13 +195,14 @@ namespace Ryujinx.Input.SDL2 if (result == 0) { - Vector3 value = new Vector3(values[0], values[1], values[2]); + Vector3 value = new(values[0], values[1], values[2]); if (inputId == MotionInputId.Gyroscope) { return RadToDegree(value); } - else if (inputId == MotionInputId.Accelerometer) + + if (inputId == MotionInputId.Accelerometer) { return GsToMs2(value); } @@ -359,18 +373,18 @@ namespace Ryujinx.Input.SDL2 { return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT)) > _triggerThreshold; } - else if (inputId == GamepadButtonInputId.RightTrigger) + + if (inputId == GamepadButtonInputId.RightTrigger) { return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) > _triggerThreshold; } - else if (_buttonsDriverMapping[(int)inputId] == SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID) + + if (_buttonsDriverMapping[(int)inputId] == SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID) { return false; } - else - { - return SDL_GameControllerGetButton(_gamepadHandle, _buttonsDriverMapping[(int)inputId]) == 1; - } + + return SDL_GameControllerGetButton(_gamepadHandle, _buttonsDriverMapping[(int)inputId]) == 1; } } } diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index d4086a105..d0e793de4 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -7,8 +7,8 @@ namespace Ryujinx.Input.SDL2 { public class SDL2GamepadDriver : IGamepadDriver { - private Dictionary<int, string> _gamepadsInstanceIdsMapping; - private List<string> _gamepadsIds; + private readonly Dictionary<int, string> _gamepadsInstanceIdsMapping; + private readonly List<string> _gamepadsIds; public ReadOnlySpan<string> GamepadsIds => _gamepadsIds.ToArray(); @@ -35,7 +35,7 @@ namespace Ryujinx.Input.SDL2 } } - private string GenerateGamepadId(int joystickIndex) + private static string GenerateGamepadId(int joystickIndex) { Guid guid = SDL_JoystickGetDeviceGUID(joystickIndex); @@ -44,10 +44,10 @@ namespace Ryujinx.Input.SDL2 return null; } - return joystickIndex + "-" + guid.ToString(); + return joystickIndex + "-" + guid; } - private int GetJoystickIndexByGamepadId(string id) + private static int GetJoystickIndexByGamepadId(string id) { string[] data = id.Split("-"); @@ -118,6 +118,7 @@ namespace Ryujinx.Input.SDL2 public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs index 2fe0614d8..f46930a8e 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs @@ -26,9 +26,11 @@ namespace Ryujinx.Input.SDL2 private readonly object _userMappingLock = new(); +#pragma warning disable IDE0052 // Remove unread private member private readonly SDL2KeyboardDriver _driver; +#pragma warning restore IDE0052 private StandardKeyboardInputConfig _configuration; - private List<ButtonMappingEntry> _buttonsUserMapping; + private readonly List<ButtonMappingEntry> _buttonsUserMapping; private static readonly SDL_Keycode[] _keysDriverMapping = new SDL_Keycode[(int)Key.Count] { @@ -208,29 +210,19 @@ namespace Ryujinx.Input.SDL2 private static SDL_Keymod GetKeyboardModifierMask(Key key) { - switch (key) + return key switch { - case Key.ShiftLeft: - return SDL_Keymod.KMOD_LSHIFT; - case Key.ShiftRight: - return SDL_Keymod.KMOD_RSHIFT; - case Key.ControlLeft: - return SDL_Keymod.KMOD_LCTRL; - case Key.ControlRight: - return SDL_Keymod.KMOD_RCTRL; - case Key.AltLeft: - return SDL_Keymod.KMOD_LALT; - case Key.AltRight: - return SDL_Keymod.KMOD_RALT; - case Key.WinLeft: - return SDL_Keymod.KMOD_LGUI; - case Key.WinRight: - return SDL_Keymod.KMOD_RGUI; + Key.ShiftLeft => SDL_Keymod.KMOD_LSHIFT, + Key.ShiftRight => SDL_Keymod.KMOD_RSHIFT, + Key.ControlLeft => SDL_Keymod.KMOD_LCTRL, + Key.ControlRight => SDL_Keymod.KMOD_RCTRL, + Key.AltLeft => SDL_Keymod.KMOD_LALT, + Key.AltRight => SDL_Keymod.KMOD_RALT, + Key.WinLeft => SDL_Keymod.KMOD_LGUI, + Key.WinRight => SDL_Keymod.KMOD_RGUI, // NOTE: Menu key isn't supported by SDL2. - case Key.Menu: - default: - return SDL_Keymod.KMOD_NONE; - } + _ => SDL_Keymod.KMOD_NONE, + }; } public KeyboardStateSnapshot GetKeyboardStateSnapshot() @@ -416,4 +408,4 @@ namespace Ryujinx.Input.SDL2 return Vector3.Zero; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs b/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs index e9361c248..d0268adbb 100644 --- a/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Input.SDL2 public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -51,4 +52,4 @@ namespace Ryujinx.Input.SDL2 return new SDL2Keyboard(this, _keyboardIdentifers[0], "All keyboards"); } } -} \ No newline at end of file +} From ff53dcf5607a82ad38388502b4cf5cc8cca77733 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 26 Jun 2023 07:25:06 +0200 Subject: [PATCH 667/737] [ARMeilleure] Address dotnet-format issues (#5357) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Address or silence dotnet format IDE1006 warnings * Address or silence dotnet format CA2208 warnings * Address dotnet format CA1822 warnings * Address or silence dotnet format CA1069 warnings * Silence CA1806 and CA1834 issues * Address dotnet format CA1401 warnings * Fix new dotnet-format issues after rebase * Address review comments * Address dotnet format CA2208 warnings properly * Fix formatting for switch expressions * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add previously silenced warnings back I have no clue how these disappeared * Revert formatting changes for OpCodeTable.cs * Enable formatting for a few cases again * Format if-blocks correctly * Enable formatting for a few more cases again * Fix inline comment alignment * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Remove a few unused parameters * Adjust namespaces * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Fix and silence a few dotnet-format warnings again * Address IDE0251 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Remove unnecessary formatting exclusion * Add unsafe dotnet format changes * Change visibility of JitSupportDarwin to internal --- src/ARMeilleure/Allocators.cs | 5 +- .../CodeGen/Arm64/Arm64Optimizer.cs | 2 +- src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs | 28 +- .../CodeGen/Arm64/ArmExtensionType.cs | 2 +- src/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs | 4 +- src/ARMeilleure/CodeGen/Arm64/Assembler.cs | 8 +- .../CodeGen/Arm64/CallingConvention.cs | 2 +- .../CodeGen/Arm64/CodeGenCommon.cs | 2 +- .../CodeGen/Arm64/CodeGenContext.cs | 14 +- .../CodeGen/Arm64/CodeGenerator.cs | 119 ++-- .../CodeGen/Arm64/CodeGeneratorIntrinsic.cs | 2 +- .../CodeGen/Arm64/HardwareCapabilities.cs | 169 +++-- .../CodeGen/Arm64/IntrinsicInfo.cs | 6 +- .../CodeGen/Arm64/IntrinsicTable.cs | 6 +- .../CodeGen/Arm64/IntrinsicType.cs | 4 +- src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs | 34 +- src/ARMeilleure/CodeGen/CompiledFunction.cs | 6 +- src/ARMeilleure/CodeGen/Linking/RelocEntry.cs | 2 +- src/ARMeilleure/CodeGen/Linking/RelocInfo.cs | 2 +- src/ARMeilleure/CodeGen/Linking/SymbolType.cs | 2 +- .../CodeGen/Optimizations/ConstantFolding.cs | 4 +- .../CodeGen/Optimizations/Optimizer.cs | 4 +- .../CodeGen/Optimizations/Simplification.cs | 13 +- .../RegisterAllocators/AllocationResult.cs | 6 +- .../RegisterAllocators/CopyResolver.cs | 42 +- .../RegisterAllocators/HybridAllocator.cs | 18 +- .../RegisterAllocators/IRegisterAllocator.cs | 2 +- .../RegisterAllocators/LinearScanAllocator.cs | 57 +- .../RegisterAllocators/LiveInterval.cs | 8 +- .../RegisterAllocators/LiveIntervalList.cs | 6 +- .../CodeGen/RegisterAllocators/LiveRange.cs | 2 +- .../RegisterAllocators/RegisterMasks.cs | 12 +- .../RegisterAllocators/StackAllocator.cs | 2 +- .../CodeGen/RegisterAllocators/UseList.cs | 36 +- .../CodeGen/Unwinding/UnwindInfo.cs | 2 +- .../CodeGen/Unwinding/UnwindPseudoOp.cs | 10 +- .../CodeGen/Unwinding/UnwindPushEntry.cs | 2 +- src/ARMeilleure/CodeGen/X86/Assembler.cs | 67 +- src/ARMeilleure/CodeGen/X86/AssemblerTable.cs | 52 +- src/ARMeilleure/CodeGen/X86/CallConvName.cs | 4 +- .../CodeGen/X86/CallingConvention.cs | 34 +- src/ARMeilleure/CodeGen/X86/CodeGenContext.cs | 4 +- src/ARMeilleure/CodeGen/X86/CodeGenerator.cs | 618 +++++++++--------- .../CodeGen/X86/HardwareCapabilities.cs | 12 +- src/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs | 4 +- src/ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 6 +- src/ARMeilleure/CodeGen/X86/IntrinsicType.cs | 4 +- src/ARMeilleure/CodeGen/X86/Mxcsr.cs | 2 +- src/ARMeilleure/CodeGen/X86/PreAllocator.cs | 296 ++++----- .../CodeGen/X86/PreAllocatorSystemV.cs | 25 +- .../CodeGen/X86/PreAllocatorWindows.cs | 4 +- src/ARMeilleure/CodeGen/X86/X86Condition.cs | 36 +- src/ARMeilleure/CodeGen/X86/X86Instruction.cs | 4 +- src/ARMeilleure/CodeGen/X86/X86Optimizer.cs | 2 +- src/ARMeilleure/CodeGen/X86/X86Register.cs | 31 +- src/ARMeilleure/Common/ArenaAllocator.cs | 15 +- src/ARMeilleure/Common/BitMap.cs | 12 +- src/ARMeilleure/Decoders/Block.cs | 16 +- src/ARMeilleure/Decoders/Condition.cs | 26 +- src/ARMeilleure/Decoders/DataOp.cs | 8 +- src/ARMeilleure/Decoders/Decoder.cs | 34 +- src/ARMeilleure/Decoders/DecoderHelper.cs | 46 +- src/ARMeilleure/Decoders/DecoderMode.cs | 2 +- src/ARMeilleure/Decoders/IOpCode.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32Alu.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32AluImm.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32AluImm16.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32AluRsImm.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32AluRsReg.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32BImm.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32BReg.cs | 2 +- .../Decoders/IOpCode32Exception.cs | 2 +- .../Decoders/IOpCode32HasSetFlags.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32Mem.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32MemMult.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32MemReg.cs | 2 +- src/ARMeilleure/Decoders/IOpCode32MemRsImm.cs | 2 +- src/ARMeilleure/Decoders/IOpCodeAlu.cs | 2 +- src/ARMeilleure/Decoders/IOpCodeAluImm.cs | 2 +- src/ARMeilleure/Decoders/IOpCodeAluRs.cs | 4 +- src/ARMeilleure/Decoders/IOpCodeAluRx.cs | 4 +- src/ARMeilleure/Decoders/IOpCodeBImm.cs | 2 +- src/ARMeilleure/Decoders/IOpCodeCond.cs | 2 +- src/ARMeilleure/Decoders/IOpCodeLit.cs | 10 +- src/ARMeilleure/Decoders/IOpCodeSimd.cs | 2 +- src/ARMeilleure/Decoders/InstDescriptor.cs | 8 +- src/ARMeilleure/Decoders/InstEmitter.cs | 2 +- src/ARMeilleure/Decoders/IntType.cs | 12 +- src/ARMeilleure/Decoders/OpCode.cs | 27 +- src/ARMeilleure/Decoders/OpCode32.cs | 2 +- src/ARMeilleure/Decoders/OpCode32Alu.cs | 2 +- src/ARMeilleure/Decoders/OpCode32AluImm.cs | 2 +- src/ARMeilleure/Decoders/OpCode32AluRsImm.cs | 6 +- src/ARMeilleure/Decoders/OpCode32BImm.cs | 2 +- src/ARMeilleure/Decoders/OpCode32BReg.cs | 2 +- src/ARMeilleure/Decoders/OpCode32Mem.cs | 20 +- src/ARMeilleure/Decoders/OpCode32MemImm.cs | 2 +- src/ARMeilleure/Decoders/OpCode32MemImm8.cs | 2 +- src/ARMeilleure/Decoders/OpCode32MemMult.cs | 12 +- src/ARMeilleure/Decoders/OpCode32Mrs.cs | 4 +- src/ARMeilleure/Decoders/OpCode32MsrReg.cs | 8 +- src/ARMeilleure/Decoders/OpCode32Sat.cs | 2 +- src/ARMeilleure/Decoders/OpCode32Sat16.cs | 2 +- src/ARMeilleure/Decoders/OpCode32SimdBase.cs | 24 +- src/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs | 6 +- src/ARMeilleure/Decoders/OpCode32SimdLong.cs | 12 +- .../Decoders/OpCode32SimdMemPair.cs | 4 +- src/ARMeilleure/Decoders/OpCode32SimdSel.cs | 2 +- src/ARMeilleure/Decoders/OpCodeAdr.cs | 6 +- src/ARMeilleure/Decoders/OpCodeAlu.cs | 6 +- src/ARMeilleure/Decoders/OpCodeAluBinary.cs | 2 +- src/ARMeilleure/Decoders/OpCodeAluImm.cs | 4 +- src/ARMeilleure/Decoders/OpCodeAluRs.cs | 6 +- src/ARMeilleure/Decoders/OpCodeAluRx.cs | 8 +- src/ARMeilleure/Decoders/OpCodeBImm.cs | 2 +- src/ARMeilleure/Decoders/OpCodeBImmAl.cs | 2 +- src/ARMeilleure/Decoders/OpCodeBImmCmp.cs | 2 +- src/ARMeilleure/Decoders/OpCodeBImmCond.cs | 2 +- src/ARMeilleure/Decoders/OpCodeBImmTest.cs | 6 +- src/ARMeilleure/Decoders/OpCodeBReg.cs | 4 +- src/ARMeilleure/Decoders/OpCodeBfm.cs | 8 +- src/ARMeilleure/Decoders/OpCodeCcmp.cs | 10 +- src/ARMeilleure/Decoders/OpCodeCcmpImm.cs | 2 +- src/ARMeilleure/Decoders/OpCodeCcmpReg.cs | 2 +- src/ARMeilleure/Decoders/OpCodeCsel.cs | 4 +- src/ARMeilleure/Decoders/OpCodeException.cs | 2 +- src/ARMeilleure/Decoders/OpCodeMem.cs | 12 +- src/ARMeilleure/Decoders/OpCodeMemEx.cs | 6 +- src/ARMeilleure/Decoders/OpCodeMemImm.cs | 22 +- src/ARMeilleure/Decoders/OpCodeMemLit.cs | 34 +- src/ARMeilleure/Decoders/OpCodeMemPair.cs | 10 +- src/ARMeilleure/Decoders/OpCodeMemReg.cs | 12 +- src/ARMeilleure/Decoders/OpCodeMov.cs | 8 +- src/ARMeilleure/Decoders/OpCodeMul.cs | 2 +- src/ARMeilleure/Decoders/OpCodeSimd.cs | 14 +- src/ARMeilleure/Decoders/OpCodeSimdCvt.cs | 4 +- src/ARMeilleure/Decoders/OpCodeSimdExt.cs | 2 +- src/ARMeilleure/Decoders/OpCodeSimdFcond.cs | 2 +- src/ARMeilleure/Decoders/OpCodeSimdFmov.cs | 8 +- src/ARMeilleure/Decoders/OpCodeSimdHelper.cs | 9 +- src/ARMeilleure/Decoders/OpCodeSimdImm.cs | 21 +- src/ARMeilleure/Decoders/OpCodeSimdIns.cs | 20 +- src/ARMeilleure/Decoders/OpCodeSimdMemImm.cs | 2 +- src/ARMeilleure/Decoders/OpCodeSimdMemLit.cs | 8 +- src/ARMeilleure/Decoders/OpCodeSimdMemMs.cs | 51 +- src/ARMeilleure/Decoders/OpCodeSimdMemPair.cs | 2 +- src/ARMeilleure/Decoders/OpCodeSimdMemReg.cs | 2 +- src/ARMeilleure/Decoders/OpCodeSimdMemSs.cs | 100 +-- src/ARMeilleure/Decoders/OpCodeSimdReg.cs | 12 +- src/ARMeilleure/Decoders/OpCodeSimdRegElem.cs | 8 +- .../Decoders/OpCodeSimdRegElemF.cs | 4 +- src/ARMeilleure/Decoders/OpCodeSimdTbl.cs | 2 +- src/ARMeilleure/Decoders/OpCodeSystem.cs | 14 +- src/ARMeilleure/Decoders/OpCodeT16.cs | 2 +- .../Decoders/OpCodeT16AddSubImm3.cs | 6 +- src/ARMeilleure/Decoders/OpCodeT16BImm11.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT16BImm8.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT16MemImm5.cs | 22 +- src/ARMeilleure/Decoders/OpCodeT16MemMult.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs | 4 +- src/ARMeilleure/Decoders/OpCodeT32.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32Alu.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32AluImm.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32AluImm12.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32AluReg.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs | 6 +- src/ARMeilleure/Decoders/OpCodeT32BImm20.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32BImm24.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32MemImm12.cs | 2 +- src/ARMeilleure/Decoders/OpCodeT32MemImm8.cs | 4 +- src/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs | 4 +- src/ARMeilleure/Decoders/OpCodeT32MemMult.cs | 12 +- src/ARMeilleure/Decoders/OpCodeT32MovImm16.cs | 4 +- src/ARMeilleure/Decoders/OpCodeT32Tb.cs | 2 +- src/ARMeilleure/Decoders/OpCodeTable.cs | 67 +- .../Decoders/Optimizations/TailCallRemover.cs | 12 +- src/ARMeilleure/Decoders/RegisterSize.cs | 4 +- src/ARMeilleure/Decoders/ShiftType.cs | 4 +- src/ARMeilleure/Diagnostics/IRDumper.cs | 32 +- src/ARMeilleure/Diagnostics/Logger.cs | 4 +- src/ARMeilleure/Diagnostics/PassName.cs | 4 +- src/ARMeilleure/Diagnostics/Symbols.cs | 3 +- .../Diagnostics/TranslatorEventSource.cs | 6 +- src/ARMeilleure/Instructions/CryptoHelper.cs | 38 +- src/ARMeilleure/Instructions/InstEmitAlu.cs | 19 +- src/ARMeilleure/Instructions/InstEmitAlu32.cs | 3 +- .../Instructions/InstEmitAluHelper.cs | 135 ++-- src/ARMeilleure/Instructions/InstEmitBfm.cs | 6 +- src/ARMeilleure/Instructions/InstEmitCcmp.cs | 5 +- src/ARMeilleure/Instructions/InstEmitCsel.cs | 7 +- src/ARMeilleure/Instructions/InstEmitDiv.cs | 7 +- .../Instructions/InstEmitException.cs | 2 +- src/ARMeilleure/Instructions/InstEmitFlow.cs | 6 +- .../Instructions/InstEmitFlow32.cs | 6 +- .../Instructions/InstEmitFlowHelper.cs | 76 +-- .../Instructions/InstEmitHashHelper.cs | 16 +- .../Instructions/InstEmitHelper.cs | 129 ++-- .../Instructions/InstEmitMemory.cs | 68 +- .../Instructions/InstEmitMemory32.cs | 25 +- .../Instructions/InstEmitMemoryEx.cs | 35 +- .../Instructions/InstEmitMemoryExHelper.cs | 13 +- .../Instructions/InstEmitMemoryHelper.cs | 261 +++++--- src/ARMeilleure/Instructions/InstEmitMove.cs | 2 +- src/ARMeilleure/Instructions/InstEmitMul.cs | 13 +- src/ARMeilleure/Instructions/InstEmitMul32.cs | 11 +- .../Instructions/InstEmitSimdArithmetic.cs | 116 ++-- .../Instructions/InstEmitSimdArithmetic32.cs | 9 +- .../Instructions/InstEmitSimdCmp.cs | 7 +- .../Instructions/InstEmitSimdCvt.cs | 149 +++-- .../Instructions/InstEmitSimdCvt32.cs | 55 +- .../Instructions/InstEmitSimdHash.cs | 8 +- .../Instructions/InstEmitSimdHash32.cs | 4 +- .../Instructions/InstEmitSimdHashHelper.cs | 6 +- .../Instructions/InstEmitSimdHelper.cs | 174 ++--- .../Instructions/InstEmitSimdHelper32.cs | 49 +- .../Instructions/InstEmitSimdHelper32Arm64.cs | 18 +- .../Instructions/InstEmitSimdHelperArm64.cs | 8 +- .../Instructions/InstEmitSimdLogical.cs | 21 +- .../Instructions/InstEmitSimdLogical32.cs | 24 +- .../Instructions/InstEmitSimdMemory.cs | 4 +- .../Instructions/InstEmitSimdMove.cs | 85 ++- .../Instructions/InstEmitSimdMove32.cs | 7 +- .../Instructions/InstEmitSimdShift.cs | 39 +- .../Instructions/InstEmitSimdShift32.cs | 7 +- .../Instructions/InstEmitSystem.cs | 90 ++- .../Instructions/InstEmitSystem32.cs | 55 +- .../Instructions/NativeInterface.cs | 6 +- src/ARMeilleure/Instructions/SoftFallback.cs | 94 ++- src/ARMeilleure/Instructions/SoftFloat.cs | 306 ++++----- .../IntermediateRepresentation/BasicBlock.cs | 11 +- .../BasicBlockFrequency.cs | 4 +- .../IntermediateRepresentation/Comparison.cs | 20 +- .../IntermediateRepresentation/Instruction.cs | 4 +- .../IntermediateRepresentation/Intrinsic.cs | 6 +- .../MemoryOperand.cs | 12 +- .../IntermediateRepresentation/Multiplier.cs | 4 +- .../IntermediateRepresentation/Operand.cs | 80 +-- .../IntermediateRepresentation/OperandKind.cs | 4 +- .../IntermediateRepresentation/OperandType.cs | 55 +- .../IntermediateRepresentation/Operation.cs | 64 +- .../IntermediateRepresentation/Register.cs | 6 +- .../RegisterType.cs | 4 +- src/ARMeilleure/Memory/IJitMemoryBlock.cs | 2 +- src/ARMeilleure/Memory/IMemoryManager.cs | 2 +- src/ARMeilleure/Memory/MemoryManagerType.cs | 2 +- src/ARMeilleure/Native/JitSupportDarwin.cs | 2 +- src/ARMeilleure/Optimizations.cs | 44 +- src/ARMeilleure/Signal/NativeSignalHandler.cs | 18 +- src/ARMeilleure/Signal/TestMethods.cs | 6 +- .../Signal/UnixSignalHandlerRegistration.cs | 4 +- .../Signal/WindowsPartialUnmapHandler.cs | 2 + src/ARMeilleure/State/Aarch32Mode.cs | 16 +- src/ARMeilleure/State/ExceptionCallback.cs | 2 +- src/ARMeilleure/State/ExecutionContext.cs | 6 +- src/ARMeilleure/State/ExecutionMode.cs | 4 +- src/ARMeilleure/State/FPCR.cs | 6 +- src/ARMeilleure/State/FPException.cs | 10 +- src/ARMeilleure/State/FPRoundingMode.cs | 8 +- src/ARMeilleure/State/FPSCR.cs | 2 +- src/ARMeilleure/State/FPSR.cs | 2 +- src/ARMeilleure/State/FPState.cs | 2 +- src/ARMeilleure/State/FPType.cs | 2 +- src/ARMeilleure/State/ICounter.cs | 2 +- src/ARMeilleure/State/NativeContext.cs | 4 +- src/ARMeilleure/State/PState.cs | 2 +- src/ARMeilleure/State/RegisterAlias.cs | 18 +- src/ARMeilleure/State/RegisterConsts.cs | 14 +- src/ARMeilleure/State/V128.cs | 40 +- src/ARMeilleure/Statistics.cs | 14 +- .../Translation/ArmEmitterContext.cs | 10 +- .../Translation/Cache/CacheEntry.cs | 8 +- .../Translation/Cache/CacheMemoryAllocator.cs | 2 +- src/ARMeilleure/Translation/Cache/JitCache.cs | 32 +- .../Translation/Cache/JitCacheInvalidation.cs | 8 +- .../Translation/Cache/JitUnwindWindows.cs | 113 ++-- src/ARMeilleure/Translation/Compiler.cs | 10 +- .../Translation/CompilerContext.cs | 18 +- .../Translation/CompilerOptions.cs | 12 +- .../Translation/ControlFlowGraph.cs | 4 +- src/ARMeilleure/Translation/DelegateInfo.cs | 2 + src/ARMeilleure/Translation/Dominance.cs | 4 +- src/ARMeilleure/Translation/EmitterContext.cs | 8 +- src/ARMeilleure/Translation/GuestFunction.cs | 2 +- src/ARMeilleure/Translation/IntervalTree.cs | 132 ++-- .../Translation/PTC/EncodingCache.cs | 2 +- .../Translation/PTC/IPtcLoadState.cs | 2 +- src/ARMeilleure/Translation/PTC/Ptc.cs | 405 ++++++------ .../Translation/PTC/PtcFormatter.cs | 4 +- .../Translation/PTC/PtcLoadingState.cs | 4 +- .../Translation/PTC/PtcProfiler.cs | 130 ++-- src/ARMeilleure/Translation/PTC/PtcState.cs | 4 +- .../Translation/RegisterToLocal.cs | 4 +- src/ARMeilleure/Translation/RegisterUsage.cs | 25 +- .../Translation/SsaConstruction.cs | 4 +- .../Translation/SsaDeconstruction.cs | 2 +- .../Translation/TranslatedFunction.cs | 2 +- src/ARMeilleure/Translation/Translator.cs | 16 +- .../Translation/TranslatorStubs.cs | 2 +- .../Translation/TranslatorTestMethods.cs | 5 +- 300 files changed, 3515 insertions(+), 3120 deletions(-) diff --git a/src/ARMeilleure/Allocators.cs b/src/ARMeilleure/Allocators.cs index deabf9a26..ba782b99a 100644 --- a/src/ARMeilleure/Allocators.cs +++ b/src/ARMeilleure/Allocators.cs @@ -23,10 +23,7 @@ namespace ARMeilleure [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ArenaAllocator GetAllocator(ref ArenaAllocator alloc, uint pageSize, uint pageCount) { - if (alloc == null) - { - alloc = new ArenaAllocator(pageSize, pageCount); - } + alloc ??= new ArenaAllocator(pageSize, pageCount); return alloc; } diff --git a/src/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs b/src/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs index fdd4d0241..00ffd1958 100644 --- a/src/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs +++ b/src/ARMeilleure/CodeGen/Arm64/Arm64Optimizer.cs @@ -221,7 +221,7 @@ namespace ARMeilleure.CodeGen.Arm64 2 => Multiplier.x4, 3 => Multiplier.x8, 4 => Multiplier.x16, - _ => Multiplier.x1 + _ => Multiplier.x1, }; baseOp = indexOnSrc2 ? src1 : src2; diff --git a/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs b/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs index db27a8104..5db898591 100644 --- a/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs +++ b/src/ARMeilleure/CodeGen/Arm64/ArmCondition.cs @@ -5,22 +5,22 @@ namespace ARMeilleure.CodeGen.Arm64 { enum ArmCondition { - Eq = 0, - Ne = 1, + Eq = 0, + Ne = 1, GeUn = 2, LtUn = 3, - Mi = 4, - Pl = 5, - Vs = 6, - Vc = 7, + Mi = 4, + Pl = 5, + Vs = 6, + Vc = 7, GtUn = 8, LeUn = 9, - Ge = 10, - Lt = 11, - Gt = 12, - Le = 13, - Al = 14, - Nv = 15 + Ge = 10, + Lt = 11, + Gt = 12, + Le = 13, + Al = 14, + Nv = 15, } static class ComparisonArm64Extensions @@ -29,6 +29,7 @@ namespace ARMeilleure.CodeGen.Arm64 { return comp switch { +#pragma warning disable IDE0055 // Disable formatting Comparison.Equal => ArmCondition.Eq, Comparison.NotEqual => ArmCondition.Ne, Comparison.Greater => ArmCondition.Gt, @@ -39,8 +40,9 @@ namespace ARMeilleure.CodeGen.Arm64 Comparison.Less => ArmCondition.Lt, Comparison.GreaterOrEqualUI => ArmCondition.GeUn, Comparison.LessUI => ArmCondition.LtUn, +#pragma warning restore IDE0055 - _ => throw new ArgumentException(null, nameof(comp)) + _ => throw new ArgumentException(null, nameof(comp)), }; } } diff --git a/src/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs b/src/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs index 062a6d0b7..20ccfd4ba 100644 --- a/src/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs +++ b/src/ARMeilleure/CodeGen/Arm64/ArmExtensionType.cs @@ -9,6 +9,6 @@ namespace ARMeilleure.CodeGen.Arm64 Sxtb = 4, Sxth = 5, Sxtw = 6, - Sxtx = 7 + Sxtx = 7, } } diff --git a/src/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs b/src/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs index d223a1464..f32407c43 100644 --- a/src/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs +++ b/src/ARMeilleure/CodeGen/Arm64/ArmShiftType.cs @@ -6,6 +6,6 @@ namespace ARMeilleure.CodeGen.Arm64 Lsl = 0, Lsr = 1, Asr = 2, - Ror = 3 + Ror = 3, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/Assembler.cs b/src/ARMeilleure/CodeGen/Arm64/Assembler.cs index 0ec0be7cb..41684faf2 100644 --- a/src/ARMeilleure/CodeGen/Arm64/Assembler.cs +++ b/src/ARMeilleure/CodeGen/Arm64/Assembler.cs @@ -188,7 +188,7 @@ namespace ARMeilleure.CodeGen.Arm64 uint rmode = topHalf ? 1u << 19 : 0u; uint ftype = rd.Type == OperandType.FP64 || rn.Type == OperandType.FP64 ? 1u << 22 : 0u; - uint sf = rd.Type == OperandType.I64 || rn.Type == OperandType.I64 ? SfFlag : 0u; + uint sf = rd.Type == OperandType.I64 || rn.Type == OperandType.I64 ? SfFlag : 0u; WriteUInt32(0x1e260000u | (opcode << 16) | rmode | ftype | sf | EncodeReg(rd) | (EncodeReg(rn) << 5)); } @@ -992,7 +992,7 @@ namespace ARMeilleure.CodeGen.Arm64 { OperandType.FP32 => 0, OperandType.FP64 => 1, - _ => 2 + _ => 2, }; instruction = vecInst | ((uint)opc << 30); @@ -1124,10 +1124,11 @@ namespace ARMeilleure.CodeGen.Arm64 OperandType.FP32 => 2, OperandType.FP64 => 3, OperandType.V128 => 4, - _ => throw new ArgumentException($"Invalid type {type}.") + _ => throw new ArgumentException($"Invalid type {type}."), }; } +#pragma warning disable IDE0051 // Remove unused private member private void WriteInt16(short value) { WriteUInt16((ushort)value); @@ -1142,6 +1143,7 @@ namespace ARMeilleure.CodeGen.Arm64 { _stream.WriteByte(value); } +#pragma warning restore IDE0051 private void WriteUInt16(ushort value) { diff --git a/src/ARMeilleure/CodeGen/Arm64/CallingConvention.cs b/src/ARMeilleure/CodeGen/Arm64/CallingConvention.cs index fda8d7867..a487c2ed3 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CallingConvention.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CallingConvention.cs @@ -93,4 +93,4 @@ namespace ARMeilleure.CodeGen.Arm64 return 0; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs index 8d1e597ba..1f0148d5e 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGenCommon.cs @@ -88,4 +88,4 @@ namespace ARMeilleure.CodeGen.Arm64 return true; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs index 0dd5355f4..12ebabddd 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs @@ -14,7 +14,7 @@ namespace ARMeilleure.CodeGen.Arm64 private const int CbnzInstLength = 4; private const int LdrLitInstLength = 4; - private Stream _stream; + private readonly Stream _stream; public int StreamOffset => (int)_stream.Length; @@ -32,7 +32,7 @@ namespace ARMeilleure.CodeGen.Arm64 private readonly Dictionary<BasicBlock, long> _visitedBlocks; private readonly Dictionary<BasicBlock, List<(ArmCondition Condition, long BranchPos)>> _pendingBranches; - private struct ConstantPoolEntry + private readonly struct ConstantPoolEntry { public readonly int Offset; public readonly Symbol Symbol; @@ -58,7 +58,7 @@ namespace ARMeilleure.CodeGen.Arm64 private readonly bool _relocatable; - public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable) + public CodeGenContext(AllocationResult allocResult, int maxCallArgs, bool relocatable) { _stream = MemoryStreamManager.Shared.GetStream(); @@ -93,10 +93,10 @@ namespace ARMeilleure.CodeGen.Arm64 if (_pendingBranches.TryGetValue(block, out var list)) { - foreach (var tuple in list) + foreach ((ArmCondition condition, long branchPos) in list) { - _stream.Seek(tuple.BranchPos, SeekOrigin.Begin); - WriteBranch(tuple.Condition, target); + _stream.Seek(branchPos, SeekOrigin.Begin); + WriteBranch(condition, target); } _stream.Seek(target, SeekOrigin.Begin); @@ -284,4 +284,4 @@ namespace ARMeilleure.CodeGen.Arm64 _stream.WriteByte((byte)(value >> 56)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs index 927198505..2df86671a 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs @@ -10,7 +10,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Numerics; - using static ARMeilleure.IntermediateRepresentation.Operand; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -31,15 +30,16 @@ namespace ARMeilleure.CodeGen.Arm64 { Byte, Hword, - Auto + Auto, } - private static Action<CodeGenContext, Operation>[] _instTable; + private static readonly Action<CodeGenContext, Operation>[] _instTable; static CodeGenerator() { _instTable = new Action<CodeGenContext, Operation>[EnumUtils.GetCount(typeof(Instruction))]; +#pragma warning disable IDE0055 // Disable formatting Add(Instruction.Add, GenerateAdd); Add(Instruction.BitwiseAnd, GenerateBitwiseAnd); Add(Instruction.BitwiseExclusiveOr, GenerateBitwiseExclusiveOr); @@ -48,7 +48,7 @@ namespace ARMeilleure.CodeGen.Arm64 Add(Instruction.BranchIf, GenerateBranchIf); Add(Instruction.ByteSwap, GenerateByteSwap); Add(Instruction.Call, GenerateCall); - //Add(Instruction.Clobber, GenerateClobber); + // Add(Instruction.Clobber, GenerateClobber); Add(Instruction.Compare, GenerateCompare); Add(Instruction.CompareAndSwap, GenerateCompareAndSwap); Add(Instruction.CompareAndSwap16, GenerateCompareAndSwap16); @@ -100,6 +100,7 @@ namespace ARMeilleure.CodeGen.Arm64 Add(Instruction.ZeroExtend16, GenerateZeroExtend16); Add(Instruction.ZeroExtend32, GenerateZeroExtend32); Add(Instruction.ZeroExtend8, GenerateZeroExtend8); +#pragma warning restore IDE0055 static void Add(Instruction inst, Action<CodeGenContext, Operation> func) { @@ -131,7 +132,7 @@ namespace ARMeilleure.CodeGen.Arm64 StackAllocator stackAlloc = new(); - PreAllocator.RunPass(cctx, stackAlloc, out int maxCallArgs); + PreAllocator.RunPass(cctx, out int maxCallArgs); Logger.EndPass(PassName.PreAllocation, cfg); @@ -170,7 +171,7 @@ namespace ARMeilleure.CodeGen.Arm64 bool relocatable = (cctx.Options & CompilerOptions.Relocatable) != 0; - CodeGenContext context = new(allocResult, maxCallArgs, cfg.Blocks.Count, relocatable); + CodeGenContext context = new(allocResult, maxCallArgs, relocatable); UnwindInfo unwindInfo = WritePrologue(context); @@ -292,7 +293,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateBitwiseNot(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); ValidateUnOp(dest, source); @@ -330,7 +331,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateByteSwap(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); ValidateUnOp(dest, source); @@ -364,15 +365,15 @@ namespace ARMeilleure.CodeGen.Arm64 { if (operation.SourcesCount == 5) // CompareAndSwap128 has 5 sources, compared to CompareAndSwap64/32's 3. { - Operand actualLow = operation.GetDestination(0); - Operand actualHigh = operation.GetDestination(1); - Operand temp0 = operation.GetDestination(2); - Operand temp1 = operation.GetDestination(3); - Operand address = operation.GetSource(0); - Operand expectedLow = operation.GetSource(1); + Operand actualLow = operation.GetDestination(0); + Operand actualHigh = operation.GetDestination(1); + Operand temp0 = operation.GetDestination(2); + Operand temp1 = operation.GetDestination(3); + Operand address = operation.GetSource(0); + Operand expectedLow = operation.GetSource(1); Operand expectedHigh = operation.GetSource(2); - Operand desiredLow = operation.GetSource(3); - Operand desiredHigh = operation.GetSource(4); + Operand desiredLow = operation.GetSource(3); + Operand desiredHigh = operation.GetSource(4); GenerateAtomicDcas( context, @@ -388,11 +389,11 @@ namespace ARMeilleure.CodeGen.Arm64 } else { - Operand actual = operation.GetDestination(0); - Operand result = operation.GetDestination(1); - Operand address = operation.GetSource(0); + Operand actual = operation.GetDestination(0); + Operand result = operation.GetDestination(1); + Operand address = operation.GetSource(0); Operand expected = operation.GetSource(1); - Operand desired = operation.GetSource(2); + Operand desired = operation.GetSource(2); GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Auto); } @@ -400,22 +401,22 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateCompareAndSwap16(CodeGenContext context, Operation operation) { - Operand actual = operation.GetDestination(0); - Operand result = operation.GetDestination(1); - Operand address = operation.GetSource(0); + Operand actual = operation.GetDestination(0); + Operand result = operation.GetDestination(1); + Operand address = operation.GetSource(0); Operand expected = operation.GetSource(1); - Operand desired = operation.GetSource(2); + Operand desired = operation.GetSource(2); GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Hword); } private static void GenerateCompareAndSwap8(CodeGenContext context, Operation operation) { - Operand actual = operation.GetDestination(0); - Operand result = operation.GetDestination(1); - Operand address = operation.GetSource(0); + Operand actual = operation.GetDestination(0); + Operand result = operation.GetDestination(1); + Operand address = operation.GetSource(0); Operand expected = operation.GetSource(1); - Operand desired = operation.GetSource(2); + Operand desired = operation.GetSource(2); GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Byte); } @@ -444,13 +445,13 @@ namespace ARMeilleure.CodeGen.Arm64 Debug.Assert(dest.Type.IsInteger()); Debug.Assert(src1.Type == OperandType.I32); - context.Assembler.Cmp (src1, Const(src1.Type, 0)); + context.Assembler.Cmp(src1, Const(src1.Type, 0)); context.Assembler.Csel(dest, src2, src3, ArmCondition.Ne); } private static void GenerateConvertI64ToI32(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.I32 && source.Type == OperandType.I64); @@ -460,7 +461,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateConvertToFP(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64); @@ -479,7 +480,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateConvertToFPUI(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64); @@ -491,7 +492,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateCopy(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); EnsureSameType(dest, source); @@ -523,7 +524,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateCountLeadingZeros(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); EnsureSameType(dest, source); @@ -535,9 +536,9 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateDivide(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand dividend = operation.GetSource(0); - Operand divisor = operation.GetSource(1); + Operand divisor = operation.GetSource(1); ValidateBinOp(dest, dividend, divisor); @@ -553,9 +554,9 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateDivideUI(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand dividend = operation.GetSource(0); - Operand divisor = operation.GetSource(1); + Operand divisor = operation.GetSource(1); ValidateBinOp(dest, dividend, divisor); @@ -564,7 +565,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateLoad(CodeGenContext context, Operation operation) { - Operand value = operation.Destination; + Operand value = operation.Destination; Operand address = operation.GetSource(0); context.Assembler.Ldr(value, address); @@ -572,7 +573,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateLoad16(CodeGenContext context, Operation operation) { - Operand value = operation.Destination; + Operand value = operation.Destination; Operand address = operation.GetSource(0); Debug.Assert(value.Type.IsInteger()); @@ -582,7 +583,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateLoad8(CodeGenContext context, Operation operation) { - Operand value = operation.Destination; + Operand value = operation.Destination; Operand address = operation.GetSource(0); Debug.Assert(value.Type.IsInteger()); @@ -641,7 +642,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateNegate(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); ValidateUnOp(dest, source); @@ -728,7 +729,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateSignExtend16(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -738,7 +739,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateSignExtend32(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -748,7 +749,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateSignExtend8(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -758,7 +759,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateFill(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand offset = operation.GetSource(0); Debug.Assert(offset.Kind == OperandKind.Constant); @@ -799,7 +800,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateStackAlloc(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand offset = operation.GetSource(0); Debug.Assert(offset.Kind == OperandKind.Constant); @@ -811,7 +812,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateStore(CodeGenContext context, Operation operation) { - Operand value = operation.GetSource(1); + Operand value = operation.GetSource(1); Operand address = operation.GetSource(0); context.Assembler.Str(value, address); @@ -819,7 +820,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateStore16(CodeGenContext context, Operation operation) { - Operand value = operation.GetSource(1); + Operand value = operation.GetSource(1); Operand address = operation.GetSource(0); Debug.Assert(value.Type.IsInteger()); @@ -829,7 +830,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateStore8(CodeGenContext context, Operation operation) { - Operand value = operation.GetSource(1); + Operand value = operation.GetSource(1); Operand address = operation.GetSource(0); Debug.Assert(value.Type.IsInteger()); @@ -876,7 +877,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateVectorCreateScalar(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); if (dest != default) @@ -1022,7 +1023,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateVectorZeroUpper64(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128); @@ -1032,7 +1033,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateVectorZeroUpper96(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128); @@ -1042,7 +1043,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateZeroExtend16(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1052,7 +1053,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateZeroExtend32(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1068,7 +1069,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static void GenerateZeroExtend8(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1078,7 +1079,7 @@ namespace ARMeilleure.CodeGen.Arm64 private static UnwindInfo WritePrologue(CodeGenContext context) { - List<UnwindPushEntry> pushEntries = new List<UnwindPushEntry>(); + List<UnwindPushEntry> pushEntries = new(); Operand rsp = Register(SpRegister); @@ -1568,11 +1569,13 @@ namespace ARMeilleure.CodeGen.Arm64 Debug.Assert(op1.Type == op3.Type); } +#pragma warning disable IDE0051 // Remove unused private member private static void EnsureSameType(Operand op1, Operand op2, Operand op3, Operand op4) { Debug.Assert(op1.Type == op2.Type); Debug.Assert(op1.Type == op3.Type); Debug.Assert(op1.Type == op4.Type); } +#pragma warning restore IDE0051 } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs b/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs index 1309404a8..b87370557 100644 --- a/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs +++ b/src/ARMeilleure/CodeGen/Arm64/CodeGeneratorIntrinsic.cs @@ -688,4 +688,4 @@ namespace ARMeilleure.CodeGen.Arm64 context.Assembler.WriteInstruction(instruction, rd, rn); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs b/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs index 99ff299e9..86afc2b4d 100644 --- a/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs +++ b/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs @@ -1,7 +1,4 @@ using System; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.Arm; using System.Runtime.Versioning; @@ -35,7 +32,7 @@ namespace ARMeilleure.CodeGen.Arm64 } } -#region Linux + #region Linux private const ulong AT_HWCAP = 16; private const ulong AT_HWCAP2 = 26; @@ -46,88 +43,88 @@ namespace ARMeilleure.CodeGen.Arm64 [Flags] public enum LinuxFeatureFlagsHwCap : ulong { - Fp = 1 << 0, - Asimd = 1 << 1, - Evtstrm = 1 << 2, - Aes = 1 << 3, - Pmull = 1 << 4, - Sha1 = 1 << 5, - Sha2 = 1 << 6, - Crc32 = 1 << 7, - Atomics = 1 << 8, - FpHp = 1 << 9, - AsimdHp = 1 << 10, - CpuId = 1 << 11, - AsimdRdm = 1 << 12, - Jscvt = 1 << 13, - Fcma = 1 << 14, - Lrcpc = 1 << 15, - DcpOp = 1 << 16, - Sha3 = 1 << 17, - Sm3 = 1 << 18, - Sm4 = 1 << 19, - AsimdDp = 1 << 20, - Sha512 = 1 << 21, - Sve = 1 << 22, - AsimdFhm = 1 << 23, - Dit = 1 << 24, - Uscat = 1 << 25, - Ilrcpc = 1 << 26, - FlagM = 1 << 27, - Ssbs = 1 << 28, - Sb = 1 << 29, - Paca = 1 << 30, - Pacg = 1UL << 31 + Fp = 1 << 0, + Asimd = 1 << 1, + Evtstrm = 1 << 2, + Aes = 1 << 3, + Pmull = 1 << 4, + Sha1 = 1 << 5, + Sha2 = 1 << 6, + Crc32 = 1 << 7, + Atomics = 1 << 8, + FpHp = 1 << 9, + AsimdHp = 1 << 10, + CpuId = 1 << 11, + AsimdRdm = 1 << 12, + Jscvt = 1 << 13, + Fcma = 1 << 14, + Lrcpc = 1 << 15, + DcpOp = 1 << 16, + Sha3 = 1 << 17, + Sm3 = 1 << 18, + Sm4 = 1 << 19, + AsimdDp = 1 << 20, + Sha512 = 1 << 21, + Sve = 1 << 22, + AsimdFhm = 1 << 23, + Dit = 1 << 24, + Uscat = 1 << 25, + Ilrcpc = 1 << 26, + FlagM = 1 << 27, + Ssbs = 1 << 28, + Sb = 1 << 29, + Paca = 1 << 30, + Pacg = 1UL << 31, } [Flags] public enum LinuxFeatureFlagsHwCap2 : ulong { - Dcpodp = 1 << 0, - Sve2 = 1 << 1, - SveAes = 1 << 2, - SvePmull = 1 << 3, - SveBitperm = 1 << 4, - SveSha3 = 1 << 5, - SveSm4 = 1 << 6, - FlagM2 = 1 << 7, - Frint = 1 << 8, - SveI8mm = 1 << 9, - SveF32mm = 1 << 10, - SveF64mm = 1 << 11, - SveBf16 = 1 << 12, - I8mm = 1 << 13, - Bf16 = 1 << 14, - Dgh = 1 << 15, - Rng = 1 << 16, - Bti = 1 << 17, - Mte = 1 << 18, - Ecv = 1 << 19, - Afp = 1 << 20, - Rpres = 1 << 21, - Mte3 = 1 << 22, - Sme = 1 << 23, - Sme_i16i64 = 1 << 24, - Sme_f64f64 = 1 << 25, - Sme_i8i32 = 1 << 26, - Sme_f16f32 = 1 << 27, - Sme_b16f32 = 1 << 28, - Sme_f32f32 = 1 << 29, - Sme_fa64 = 1 << 30, - Wfxt = 1UL << 31, - Ebf16 = 1UL << 32, - Sve_Ebf16 = 1UL << 33, - Cssc = 1UL << 34, - Rprfm = 1UL << 35, - Sve2p1 = 1UL << 36 + Dcpodp = 1 << 0, + Sve2 = 1 << 1, + SveAes = 1 << 2, + SvePmull = 1 << 3, + SveBitperm = 1 << 4, + SveSha3 = 1 << 5, + SveSm4 = 1 << 6, + FlagM2 = 1 << 7, + Frint = 1 << 8, + SveI8mm = 1 << 9, + SveF32mm = 1 << 10, + SveF64mm = 1 << 11, + SveBf16 = 1 << 12, + I8mm = 1 << 13, + Bf16 = 1 << 14, + Dgh = 1 << 15, + Rng = 1 << 16, + Bti = 1 << 17, + Mte = 1 << 18, + Ecv = 1 << 19, + Afp = 1 << 20, + Rpres = 1 << 21, + Mte3 = 1 << 22, + Sme = 1 << 23, + Sme_i16i64 = 1 << 24, + Sme_f64f64 = 1 << 25, + Sme_i8i32 = 1 << 26, + Sme_f16f32 = 1 << 27, + Sme_b16f32 = 1 << 28, + Sme_f32f32 = 1 << 29, + Sme_fa64 = 1 << 30, + Wfxt = 1UL << 31, + Ebf16 = 1UL << 32, + Sve_Ebf16 = 1UL << 33, + Cssc = 1UL << 34, + Rprfm = 1UL << 35, + Sve2p1 = 1UL << 36, } public static LinuxFeatureFlagsHwCap LinuxFeatureInfoHwCap { get; } = 0; public static LinuxFeatureFlagsHwCap2 LinuxFeatureInfoHwCap2 { get; } = 0; -#endregion + #endregion -#region macOS + #region macOS [LibraryImport("libSystem.dylib", SetLastError = true)] private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize); @@ -143,7 +140,7 @@ namespace ARMeilleure.CodeGen.Arm64 return false; } - private static string[] _sysctlNames = new string[] + private static readonly string[] _sysctlNames = new string[] { "hw.optional.floatingpoint", "hw.optional.AdvSIMD", @@ -153,26 +150,26 @@ namespace ARMeilleure.CodeGen.Arm64 "hw.optional.arm.FEAT_LSE", "hw.optional.armv8_crc32", "hw.optional.arm.FEAT_SHA1", - "hw.optional.arm.FEAT_SHA256" + "hw.optional.arm.FEAT_SHA256", }; [Flags] public enum MacOsFeatureFlags { - Fp = 1 << 0, + Fp = 1 << 0, AdvSimd = 1 << 1, - Fp16 = 1 << 2, - Aes = 1 << 3, - Pmull = 1 << 4, - Lse = 1 << 5, - Crc32 = 1 << 6, - Sha1 = 1 << 7, - Sha256 = 1 << 8 + Fp16 = 1 << 2, + Aes = 1 << 3, + Pmull = 1 << 4, + Lse = 1 << 5, + Crc32 = 1 << 6, + Sha1 = 1 << 7, + Sha256 = 1 << 8, } public static MacOsFeatureFlags MacOsFeatureInfo { get; } = 0; -#endregion + #endregion public static bool SupportsAdvSimd => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Asimd) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.AdvSimd); public static bool SupportsAes => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Aes) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Aes); diff --git a/src/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs index 8695db903..956fc778d 100644 --- a/src/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs +++ b/src/ARMeilleure/CodeGen/Arm64/IntrinsicInfo.cs @@ -1,8 +1,8 @@ namespace ARMeilleure.CodeGen.Arm64 { - struct IntrinsicInfo + readonly struct IntrinsicInfo { - public uint Inst { get; } + public uint Inst { get; } public IntrinsicType Type { get; } public IntrinsicInfo(uint inst, IntrinsicType type) @@ -11,4 +11,4 @@ namespace ARMeilleure.CodeGen.Arm64 Type = type; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs index c2bd0bd51..dbd5bdd10 100644 --- a/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs +++ b/src/ARMeilleure/CodeGen/Arm64/IntrinsicTable.cs @@ -5,12 +5,13 @@ namespace ARMeilleure.CodeGen.Arm64 { static class IntrinsicTable { - private static IntrinsicInfo[] _intrinTable; + private static readonly IntrinsicInfo[] _intrinTable; static IntrinsicTable() { _intrinTable = new IntrinsicInfo[EnumUtils.GetCount(typeof(Intrinsic))]; +#pragma warning disable IDE0055 // Disable formatting Add(Intrinsic.Arm64AbsS, new IntrinsicInfo(0x5e20b800u, IntrinsicType.ScalarUnary)); Add(Intrinsic.Arm64AbsV, new IntrinsicInfo(0x0e20b800u, IntrinsicType.VectorUnary)); Add(Intrinsic.Arm64AddhnV, new IntrinsicInfo(0x0e204000u, IntrinsicType.VectorTernaryRd)); @@ -448,6 +449,7 @@ namespace ARMeilleure.CodeGen.Arm64 Add(Intrinsic.Arm64XtnV, new IntrinsicInfo(0x0e212800u, IntrinsicType.VectorUnary)); Add(Intrinsic.Arm64Zip1V, new IntrinsicInfo(0x0e003800u, IntrinsicType.VectorBinary)); Add(Intrinsic.Arm64Zip2V, new IntrinsicInfo(0x0e007800u, IntrinsicType.VectorBinary)); +#pragma warning restore IDE0055 } private static void Add(Intrinsic intrin, IntrinsicInfo info) @@ -460,4 +462,4 @@ namespace ARMeilleure.CodeGen.Arm64 return _intrinTable[(int)intrin]; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs b/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs index df61ea1eb..7538575c9 100644 --- a/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs +++ b/src/ARMeilleure/CodeGen/Arm64/IntrinsicType.cs @@ -55,6 +55,6 @@ namespace ARMeilleure.CodeGen.Arm64 VectorTernaryShrRd, GetRegister, - SetRegister + SetRegister, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs b/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs index 74f80e0fb..f66bb66e6 100644 --- a/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs +++ b/src/ARMeilleure/CodeGen/Arm64/PreAllocator.cs @@ -1,4 +1,3 @@ -using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; @@ -31,7 +30,7 @@ namespace ARMeilleure.CodeGen.Arm64 } } - public static void RunPass(CompilerContext cctx, StackAllocator stackAlloc, out int maxCallArgs) + public static void RunPass(CompilerContext cctx, out int maxCallArgs) { maxCallArgs = -1; @@ -41,7 +40,7 @@ namespace ARMeilleure.CodeGen.Arm64 for (BasicBlock block = cctx.Cfg.Blocks.First; block != null; block = block.ListNext) { - ConstantDict constants = new ConstantDict(); + ConstantDict constants = new(); Operation nextNode; @@ -92,7 +91,7 @@ namespace ARMeilleure.CodeGen.Arm64 InsertReturnCopy(block.Operations, node); break; case Instruction.Tailcall: - InsertTailcallCopies(constants, block.Operations, stackAlloc, node, node); + InsertTailcallCopies(constants, block.Operations, node, node); break; } } @@ -138,10 +137,7 @@ namespace ARMeilleure.CodeGen.Arm64 { src2 = node.GetSource(1); - Operand temp = src1; - - src1 = src2; - src2 = temp; + (src2, src1) = (src1, src2); node.SetSource(0, src1); node.SetSource(1, src2); @@ -265,9 +261,9 @@ namespace ARMeilleure.CodeGen.Arm64 Operand dest = operation.Destination; - List<Operand> sources = new List<Operand> + List<Operand> sources = new() { - operation.GetSource(0) + operation.GetSource(0), }; int argsCount = operation.SourcesCount - 1; @@ -302,10 +298,10 @@ namespace ARMeilleure.CodeGen.Arm64 if (source.Type == OperandType.V128 && passOnReg) { // V128 is a struct, we pass each half on a GPR if possible. - Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); - nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); continue; @@ -339,7 +335,7 @@ namespace ARMeilleure.CodeGen.Arm64 { if (dest.Type == OperandType.V128) { - Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, retLReg)); @@ -364,16 +360,14 @@ namespace ARMeilleure.CodeGen.Arm64 operation.SetSources(sources.ToArray()); } - private static void InsertTailcallCopies( - ConstantDict constants, + private static void InsertTailcallCopies(ConstantDict constants, IntrusiveList<Operation> nodes, - StackAllocator stackAlloc, Operation node, Operation operation) { - List<Operand> sources = new List<Operand> + List<Operand> sources = new() { - operation.GetSource(0) + operation.GetSource(0), }; int argsCount = operation.SourcesCount - 1; @@ -403,7 +397,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (source.Type == OperandType.V128 && passOnReg) { // V128 is a struct, we pass each half on a GPR if possible. - Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); @@ -519,7 +513,7 @@ namespace ARMeilleure.CodeGen.Arm64 if (source.Type == OperandType.V128) { - Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); nodes.AddBefore(node, Operation(Instruction.VectorExtract, retLReg, source, Const(0))); diff --git a/src/ARMeilleure/CodeGen/CompiledFunction.cs b/src/ARMeilleure/CodeGen/CompiledFunction.cs index 0560bf2e9..3844cbfc9 100644 --- a/src/ARMeilleure/CodeGen/CompiledFunction.cs +++ b/src/ARMeilleure/CodeGen/CompiledFunction.cs @@ -35,9 +35,9 @@ namespace ARMeilleure.CodeGen /// <param name="relocInfo">Relocation info</param> internal CompiledFunction(byte[] code, UnwindInfo unwindInfo, RelocInfo relocInfo) { - Code = code; + Code = code; UnwindInfo = unwindInfo; - RelocInfo = relocInfo; + RelocInfo = relocInfo; } /// <summary> @@ -65,4 +65,4 @@ namespace ARMeilleure.CodeGen return Marshal.GetDelegateForFunctionPointer<T>(codePointer); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Linking/RelocEntry.cs b/src/ARMeilleure/CodeGen/Linking/RelocEntry.cs index a27bfded2..d103bc395 100644 --- a/src/ARMeilleure/CodeGen/Linking/RelocEntry.cs +++ b/src/ARMeilleure/CodeGen/Linking/RelocEntry.cs @@ -35,4 +35,4 @@ namespace ARMeilleure.CodeGen.Linking return $"({nameof(Position)} = {Position}, {nameof(Symbol)} = {Symbol})"; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Linking/RelocInfo.cs b/src/ARMeilleure/CodeGen/Linking/RelocInfo.cs index caaf08e3d..01ff0347b 100644 --- a/src/ARMeilleure/CodeGen/Linking/RelocInfo.cs +++ b/src/ARMeilleure/CodeGen/Linking/RelocInfo.cs @@ -29,4 +29,4 @@ namespace ARMeilleure.CodeGen.Linking _entries = entries; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Linking/SymbolType.cs b/src/ARMeilleure/CodeGen/Linking/SymbolType.cs index b05b69692..ed348751b 100644 --- a/src/ARMeilleure/CodeGen/Linking/SymbolType.cs +++ b/src/ARMeilleure/CodeGen/Linking/SymbolType.cs @@ -23,6 +23,6 @@ /// <summary> /// Refers to a special symbol which is handled by <see cref="Translation.PTC.Ptc.PatchCode"/>. /// </summary> - Special + Special, } } diff --git a/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs b/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs index c5a22a537..be3dff58c 100644 --- a/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs +++ b/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs @@ -164,7 +164,7 @@ namespace ARMeilleure.CodeGen.Optimizations } break; - case Instruction.Multiply: + case Instruction.Multiply: if (type == OperandType.I32) { EvaluateBinaryI32(operation, (x, y) => x * y); @@ -343,4 +343,4 @@ namespace ARMeilleure.CodeGen.Optimizations operation.TurnIntoCopy(Const(op(x, y))); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs b/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs index a45bb4551..1afc3a782 100644 --- a/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs +++ b/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs @@ -182,7 +182,7 @@ namespace ARMeilleure.CodeGen.Optimizations private static void PropagateCopy(ref Span<Operation> buffer, Operation copyOp) { // Propagate copy source operand to all uses of the destination operand. - Operand dest = copyOp.Destination; + Operand dest = copyOp.Destination; Operand source = copyOp.GetSource(0); Span<Operation> uses = dest.GetUses(ref buffer); @@ -249,4 +249,4 @@ namespace ARMeilleure.CodeGen.Optimizations return operation.Destination.Type == operation.GetSource(0).Type; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs b/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs index a439d6424..53a7f3ede 100644 --- a/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs +++ b/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs @@ -171,13 +171,12 @@ namespace ARMeilleure.CodeGen.Optimizations private static ulong AllOnes(OperandType type) { - switch (type) + return type switch { - case OperandType.I32: return ~0U; - case OperandType.I64: return ~0UL; - } - - throw new ArgumentException("Invalid operand type \"" + type + "\"."); + OperandType.I32 => ~0U, + OperandType.I64 => ~0UL, + _ => throw new ArgumentException("Invalid operand type \"" + type + "\"."), + }; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs index 43e5c7e2c..7b9c2f77f 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs @@ -4,7 +4,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { public int IntUsedRegisters { get; } public int VecUsedRegisters { get; } - public int SpillRegionSize { get; } + public int SpillRegionSize { get; } public AllocationResult( int intUsedRegisters, @@ -13,7 +13,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { IntUsedRegisters = intUsedRegisters; VecUsedRegisters = vecUsedRegisters; - SpillRegionSize = spillRegionSize; + SpillRegionSize = spillRegionSize; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs index 587b1a024..af10330ba 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs @@ -1,7 +1,6 @@ using ARMeilleure.IntermediateRepresentation; using System; using System.Collections.Generic; - using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.IntermediateRepresentation.Operation.Factory; @@ -13,16 +12,16 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { private readonly struct Copy { - public Register Dest { get; } + public Register Dest { get; } public Register Source { get; } public OperandType Type { get; } public Copy(Register dest, Register source, OperandType type) { - Dest = dest; + Dest = dest; Source = source; - Type = type; + Type = type; } } @@ -42,19 +41,19 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public void Sequence(List<Operation> sequence) { - Dictionary<Register, Register> locations = new Dictionary<Register, Register>(); - Dictionary<Register, Register> sources = new Dictionary<Register, Register>(); + Dictionary<Register, Register> locations = new(); + Dictionary<Register, Register> sources = new(); - Dictionary<Register, OperandType> types = new Dictionary<Register, OperandType>(); + Dictionary<Register, OperandType> types = new(); - Queue<Register> pendingQueue = new Queue<Register>(); - Queue<Register> readyQueue = new Queue<Register>(); + Queue<Register> pendingQueue = new(); + Queue<Register> readyQueue = new(); foreach (Copy copy in _copies) { locations[copy.Source] = copy.Source; - sources[copy.Dest] = copy.Source; - types[copy.Dest] = copy.Type; + sources[copy.Dest] = copy.Source; + types[copy.Dest] = copy.Type; pendingQueue.Enqueue(copy.Dest); } @@ -91,7 +90,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } - copyDest = current; + copyDest = current; origSource = sources[copyDest]; copySource = locations[origSource]; @@ -186,10 +185,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void AddSplitFill(LiveInterval left, LiveInterval right, OperandType type) { - if (_fillQueue == null) - { - _fillQueue = new Queue<Operation>(); - } + _fillQueue ??= new Queue<Operation>(); Operand register = GetRegister(right.Register, type); Operand offset = Const(left.SpillOffset); @@ -201,10 +197,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void AddSplitSpill(LiveInterval left, LiveInterval right, OperandType type) { - if (_spillQueue == null) - { - _spillQueue = new Queue<Operation>(); - } + _spillQueue ??= new Queue<Operation>(); Operand offset = Const(right.SpillOffset); Operand register = GetRegister(left.Register, type); @@ -216,10 +209,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void AddSplitCopy(LiveInterval left, LiveInterval right, OperandType type) { - if (_parallelCopy == null) - { - _parallelCopy = new ParallelCopy(); - } + _parallelCopy ??= new ParallelCopy(); _parallelCopy.AddCopy(right.Register, left.Register, type); @@ -228,7 +218,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public Operation[] Sequence() { - List<Operation> sequence = new List<Operation>(); + List<Operation> sequence = new(); if (_spillQueue != null) { @@ -256,4 +246,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return Register(reg.Index, reg.Type, type); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs index 25952c775..5f1d6ce89 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs @@ -20,7 +20,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public BlockInfo(bool hasCall, int intFixedRegisters, int vecFixedRegisters) { - HasCall = hasCall; + HasCall = hasCall; IntFixedRegisters = intFixedRegisters; VecFixedRegisters = vecFixedRegisters; } @@ -39,7 +39,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private int _first; private int _last; - public bool IsBlockLocal => _first == _last; + public readonly bool IsBlockLocal => _first == _last; public LocalInfo(OperandType type, int uses, int blkIndex) { @@ -53,7 +53,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators SpillOffset = default; _first = -1; - _last = -1; + _last = -1; SetBlockIndex(blkIndex); } @@ -348,17 +348,17 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (dest.Type.IsInteger()) { intLocalFreeRegisters &= ~(1 << selectedReg); - intUsedRegisters |= 1 << selectedReg; + intUsedRegisters |= 1 << selectedReg; } else { vecLocalFreeRegisters &= ~(1 << selectedReg); - vecUsedRegisters |= 1 << selectedReg; + vecUsedRegisters |= 1 << selectedReg; } } else { - info.Register = default; + info.Register = default; info.SpillOffset = Const(stackAlloc.Allocate(dest.Type.GetSizeInBytes())); } } @@ -382,7 +382,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators : GetSpillTemp(dest, vecSpillTempRegisters, ref vecLocalAsg); info.Sequence = sequence; - info.Temp = temp; + info.Temp = temp; } dest = temp; @@ -408,7 +408,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private static int SelectSpillTemps(int mask0, int mask1) { int selection = 0; - int count = 0; + int count = 0; while (count < MaxIROperands && mask0 != 0) { @@ -451,4 +451,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return local.AssignmentsCount + local.UsesCount; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs index 8f236c253..7d4ce2ea6 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs @@ -9,4 +9,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators StackAllocator stackAlloc, RegisterMasks regMasks); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index d80157afb..f156e0886 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -14,7 +14,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators // http://www.christianwimmer.at/Publications/Wimmer04a/Wimmer04a.pdf class LinearScanAllocator : IRegisterAllocator { - private const int InstructionGap = 2; + private const int InstructionGap = 2; private const int InstructionGapMask = InstructionGap - 1; private HashSet<int> _blockEdges; @@ -33,7 +33,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public StackAllocator StackAlloc { get; } - public BitMap Active { get; } + public BitMap Active { get; } public BitMap Inactive { get; } public int IntUsedRegisters { get; set; } @@ -47,9 +47,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public AllocationContext(StackAllocator stackAlloc, RegisterMasks masks, int intervalsCount) { StackAlloc = stackAlloc; - Masks = masks; + Masks = masks; - Active = new BitMap(Allocators.Default, intervalsCount); + Active = new BitMap(Allocators.Default, intervalsCount); Inactive = new BitMap(Allocators.Default, intervalsCount); PopulateFreePositions(RegisterType.Integer, out _intFreePositions, out _intFreePositionsCount); @@ -443,7 +443,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (highest < current) { - highest = current; + highest = current; selected = index; if (current == int.MaxValue) @@ -485,9 +485,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void SplitAndSpillOverlappingInterval( AllocationContext context, - LiveInterval current, - LiveInterval interval, - int registersCount) + LiveInterval current, + LiveInterval interval, + int registersCount) { // If there's a next use after the start of the current interval, // we need to split the spilled interval twice, and re-insert it @@ -530,8 +530,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void InsertInterval(LiveInterval interval, int registersCount) { Debug.Assert(interval.UsesCount != 0, "Trying to insert a interval without uses."); - Debug.Assert(!interval.IsEmpty, "Trying to insert a empty interval."); - Debug.Assert(!interval.IsSpilled, "Trying to insert a spilled interval."); + Debug.Assert(!interval.IsEmpty, "Trying to insert a empty interval."); + Debug.Assert(!interval.IsSpilled, "Trying to insert a spilled interval."); int startIndex = registersCount * 2; @@ -545,9 +545,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators _intervals.Insert(insertIndex, interval); } - private void Spill(AllocationContext context, LiveInterval interval) + private static void Spill(AllocationContext context, LiveInterval interval) { - Debug.Assert(!interval.IsFixed, "Trying to spill a fixed interval."); + Debug.Assert(!interval.IsFixed, "Trying to spill a fixed interval."); Debug.Assert(interval.UsesCount == 0, "Trying to spill a interval with uses."); // We first check if any of the siblings were spilled, if so we can reuse @@ -561,7 +561,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private void InsertSplitCopies() { - Dictionary<int, CopyResolver> copyResolvers = new Dictionary<int, CopyResolver>(); + Dictionary<int, CopyResolver> copyResolvers = new(); CopyResolver GetCopyResolver(int position) { @@ -668,18 +668,15 @@ namespace ARMeilleure.CodeGen.RegisterAllocators continue; } - int lEnd = _blockRanges[block.Index].End - 1; + int lEnd = _blockRanges[block.Index].End - 1; int rStart = _blockRanges[succIndex].Start; - LiveInterval left = interval.GetSplitChild(lEnd); + LiveInterval left = interval.GetSplitChild(lEnd); LiveInterval right = interval.GetSplitChild(rStart); if (left != default && right != default && left != right) { - if (copyResolver == null) - { - copyResolver = new CopyResolver(); - } + copyResolver ??= new CopyResolver(); copyResolver.AddSplit(left, right); } @@ -856,14 +853,14 @@ namespace ARMeilleure.CodeGen.RegisterAllocators int mapSize = _intervals.Count; - BitMap[] blkLiveGen = new BitMap[cfg.Blocks.Count]; + BitMap[] blkLiveGen = new BitMap[cfg.Blocks.Count]; BitMap[] blkLiveKill = new BitMap[cfg.Blocks.Count]; // Compute local live sets. for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { - BitMap liveGen = new BitMap(Allocators.Default, mapSize); - BitMap liveKill = new BitMap(Allocators.Default, mapSize); + BitMap liveGen = new(Allocators.Default, mapSize); + BitMap liveKill = new(Allocators.Default, mapSize); for (Operation node = block.Operations.First; node != default; node = node.ListNext) { @@ -910,17 +907,17 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } - blkLiveGen [block.Index] = liveGen; + blkLiveGen[block.Index] = liveGen; blkLiveKill[block.Index] = liveKill; } // Compute global live sets. - BitMap[] blkLiveIn = new BitMap[cfg.Blocks.Count]; + BitMap[] blkLiveIn = new BitMap[cfg.Blocks.Count]; BitMap[] blkLiveOut = new BitMap[cfg.Blocks.Count]; for (int index = 0; index < cfg.Blocks.Count; index++) { - blkLiveIn [index] = new BitMap(Allocators.Default, mapSize); + blkLiveIn[index] = new BitMap(Allocators.Default, mapSize); blkLiveOut[index] = new BitMap(Allocators.Default, mapSize); } @@ -945,9 +942,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators BitMap liveIn = blkLiveIn[block.Index]; - liveIn.Set (liveOut); + liveIn.Set(liveOut); liveIn.Clear(blkLiveKill[block.Index]); - liveIn.Set (blkLiveGen [block.Index]); + liveIn.Set(blkLiveGen[block.Index]); } } while (modified); @@ -969,7 +966,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators int instCount = Math.Max(block.Operations.Count, 1); int blockStart = operationPos - instCount * InstructionGap; - int blockEnd = operationPos; + int blockEnd = operationPos; _blockRanges[block.Index] = new LiveRange(blockStart, blockEnd); @@ -1061,7 +1058,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { int regIndex = BitOperations.TrailingZeroCount(mask); - Register callerSavedReg = new Register(regIndex, regType); + Register callerSavedReg = new(regIndex, regType); LiveInterval interval = _intervals[GetRegisterId(callerSavedReg)]; @@ -1098,4 +1095,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators kind == OperandKind.Register; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs index d739ad281..333d3951b 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs @@ -240,8 +240,10 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public LiveInterval Split(int position) { - LiveInterval result = new(Local, Parent); - result.End = End; + LiveInterval result = new(Local, Parent) + { + End = End, + }; LiveRange prev = PrevRange; LiveRange curr = CurrRange; @@ -393,4 +395,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return string.Join(", ", GetRanges()); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs index 06b979ead..d999d767b 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveIntervalList.cs @@ -8,8 +8,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private int _count; private int _capacity; - public int Count => _count; - public Span<LiveInterval> Span => new(_items, _count); + public readonly int Count => _count; + public readonly Span<LiveInterval> Span => new(_items, _count); public void Add(LiveInterval interval) { @@ -37,4 +37,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators _count++; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs index e38b5190d..412d597e8 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs @@ -71,4 +71,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return $"[{Start}, {End})"; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs index bc948f95f..e6972cf0f 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs @@ -5,8 +5,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { readonly struct RegisterMasks { - public int IntAvailableRegisters { get; } - public int VecAvailableRegisters { get; } + public int IntAvailableRegisters { get; } + public int VecAvailableRegisters { get; } public int IntCallerSavedRegisters { get; } public int VecCallerSavedRegisters { get; } public int IntCalleeSavedRegisters { get; } @@ -22,13 +22,13 @@ namespace ARMeilleure.CodeGen.RegisterAllocators int vecCalleeSavedRegisters, int registersCount) { - IntAvailableRegisters = intAvailableRegisters; - VecAvailableRegisters = vecAvailableRegisters; + IntAvailableRegisters = intAvailableRegisters; + VecAvailableRegisters = vecAvailableRegisters; IntCallerSavedRegisters = intCallerSavedRegisters; VecCallerSavedRegisters = vecCallerSavedRegisters; IntCalleeSavedRegisters = intCalleeSavedRegisters; VecCalleeSavedRegisters = vecCalleeSavedRegisters; - RegistersCount = registersCount; + RegistersCount = registersCount; } public int GetAvailableRegisters(RegisterType type) @@ -47,4 +47,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs index 038312fed..13995bc8d 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs @@ -22,4 +22,4 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return offset; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/UseList.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/UseList.cs index c89f0854d..a945eccf4 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/UseList.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/UseList.cs @@ -6,15 +6,15 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { private int* _items; private int _capacity; - private int _count; - public int Count => _count; - public int FirstUse => _count > 0 ? _items[_count - 1] : LiveInterval.NotFound; - public Span<int> Span => new(_items, _count); + public int Count { get; private set; } + + public readonly int FirstUse => Count > 0 ? _items[Count - 1] : LiveInterval.NotFound; + public readonly Span<int> Span => new(_items, Count); public void Add(int position) { - if (_count + 1 > _capacity) + if (Count + 1 > _capacity) { var oldSpan = Span; @@ -28,7 +28,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators // Use positions are usually inserted in descending order, so inserting in descending order is faster, // since the number of half exchanges is reduced. - int i = _count - 1; + int i = Count - 1; while (i >= 0 && _items[i] < position) { @@ -36,19 +36,19 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } _items[i + 1] = position; - _count++; + Count++; } - public int NextUse(int position) + public readonly int NextUse(int position) { int index = NextUseIndex(position); return index != LiveInterval.NotFound ? _items[index] : LiveInterval.NotFound; } - public int NextUseIndex(int position) + public readonly int NextUseIndex(int position) { - int i = _count - 1; + int i = Count - 1; if (i == -1 || position > _items[0]) { @@ -69,16 +69,18 @@ namespace ARMeilleure.CodeGen.RegisterAllocators // Since the list is in descending order, the new split list takes the front of the list and the current // list takes the back of the list. - UseList result = new(); - result._count = index + 1; - result._capacity = result._count; + UseList result = new() + { + Count = index + 1, + }; + result._capacity = result.Count; result._items = _items; - _count = _count - result._count; - _capacity = _count; - _items = _items + result._count; + Count -= result.Count; + _capacity = Count; + _items += result.Count; return result; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs b/src/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs index 3d0bc21d5..127b84231 100644 --- a/src/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs +++ b/src/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs @@ -13,4 +13,4 @@ namespace ARMeilleure.CodeGen.Unwinding PrologSize = prologSize; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs b/src/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs index 4a8288a28..2045019a3 100644 --- a/src/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs +++ b/src/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOp.cs @@ -2,10 +2,10 @@ namespace ARMeilleure.CodeGen.Unwinding { enum UnwindPseudoOp { - PushReg = 0, - SetFrame = 1, + PushReg = 0, + SetFrame = 1, AllocStack = 2, - SaveReg = 3, - SaveXmm128 = 4 + SaveReg = 3, + SaveXmm128 = 4, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs b/src/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs index fd8ea402b..507ace598 100644 --- a/src/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs +++ b/src/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs @@ -17,4 +17,4 @@ namespace ARMeilleure.CodeGen.Unwinding StackOffsetOrAllocSize = stackOffsetOrAllocSize; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/Assembler.cs b/src/ARMeilleure/CodeGen/X86/Assembler.cs index 67736a31f..55bf07248 100644 --- a/src/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/src/ARMeilleure/CodeGen/X86/Assembler.cs @@ -15,7 +15,7 @@ namespace ARMeilleure.CodeGen.X86 private const int OpModRMBits = 24; - private const byte RexPrefix = 0x40; + private const byte RexPrefix = 0x40; private const byte RexWPrefix = 0x48; private const byte LockPrefix = 0xf0; @@ -799,7 +799,7 @@ namespace ARMeilleure.CodeGen.X86 { JumpIndex = _jumps.Count - 1, Position = (int)_stream.Position, - Symbol = source.Symbol + Symbol = source.Symbol, }); } @@ -959,7 +959,7 @@ namespace ARMeilleure.CodeGen.X86 } } - bool needsSibByte = false; + bool needsSibByte = false; bool needsDisplacement = false; int sib = 0; @@ -971,7 +971,7 @@ namespace ARMeilleure.CodeGen.X86 X86Register baseRegLow = (X86Register)(baseReg.Index & 0b111); - needsSibByte = memOp.Index != default || baseRegLow == X86Register.Rsp; + needsSibByte = memOp.Index != default || baseRegLow == X86Register.Rsp; needsDisplacement = memOp.Displacement != 0 || baseRegLow == X86Register.Rbp; if (needsDisplacement) @@ -1049,7 +1049,7 @@ namespace ARMeilleure.CodeGen.X86 InstructionFlags.Prefix66 => 1, InstructionFlags.PrefixF3 => 2, InstructionFlags.PrefixF2 => 3, - _ => 0 + _ => 0, }; if (src1 != default) @@ -1081,11 +1081,19 @@ namespace ARMeilleure.CodeGen.X86 switch (opCodeHigh) { - case 0xf: vexByte1 |= 1; break; - case 0xf38: vexByte1 |= 2; break; - case 0xf3a: vexByte1 |= 3; break; + case 0xf: + vexByte1 |= 1; + break; + case 0xf38: + vexByte1 |= 2; + break; + case 0xf3a: + vexByte1 |= 3; + break; - default: Debug.Assert(false, $"Failed to VEX encode opcode 0x{opCode:X}."); break; + default: + Debug.Assert(false, $"Failed to VEX encode opcode 0x{opCode:X}."); + break; } vexByte2 |= (rexPrefix & 8) << 4; @@ -1191,11 +1199,19 @@ namespace ARMeilleure.CodeGen.X86 switch ((ushort)(opCode >> 8)) { - case 0xf00: mm = 0b01; break; - case 0xf38: mm = 0b10; break; - case 0xf3a: mm = 0b11; break; + case 0xf00: + mm = 0b01; + break; + case 0xf38: + mm = 0b10; + break; + case 0xf3a: + mm = 0b11; + break; - default: Debug.Fail($"Failed to EVEX encode opcode 0x{opCode:X}."); break; + default: + Debug.Fail($"Failed to EVEX encode opcode 0x{opCode:X}."); + break; } WriteByte( @@ -1217,7 +1233,7 @@ namespace ARMeilleure.CodeGen.X86 InstructionFlags.Prefix66 => 0b01, InstructionFlags.PrefixF3 => 0b10, InstructionFlags.PrefixF2 => 0b11, - _ => 0 + _ => 0, }; WriteByte( (byte)( @@ -1233,11 +1249,19 @@ namespace ARMeilleure.CodeGen.X86 byte ll = 0b00; switch (registerWidth) { - case 128: ll = 0b00; break; - case 256: ll = 0b01; break; - case 512: ll = 0b10; break; + case 128: + ll = 0b00; + break; + case 256: + ll = 0b01; + break; + case 512: + ll = 0b10; + break; - default: Debug.Fail($"Invalid EVEX vector register width {registerWidth}."); break; + default: + Debug.Fail($"Invalid EVEX vector register width {registerWidth}."); + break; } // Embedded broadcast in the case of a memory operand bool bcast = broadcast; @@ -1315,10 +1339,7 @@ namespace ARMeilleure.CodeGen.X86 ref Jump jump = ref jumps[i]; // If jump target not resolved yet, resolve it. - if (jump.JumpTarget == null) - { - jump.JumpTarget = _labels[jump.JumpLabel]; - } + jump.JumpTarget ??= _labels[jump.JumpLabel]; long jumpTarget = jump.JumpTarget.Value; long offset = jumpTarget - jump.JumpPosition; @@ -1556,4 +1577,4 @@ namespace ARMeilleure.CodeGen.X86 _stream.WriteByte((byte)(value >> 56)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/AssemblerTable.cs b/src/ARMeilleure/CodeGen/X86/AssemblerTable.cs index e6a2ff07f..e4114a335 100644 --- a/src/ARMeilleure/CodeGen/X86/AssemblerTable.cs +++ b/src/ARMeilleure/CodeGen/X86/AssemblerTable.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace ARMeilleure.CodeGen.X86 { @@ -12,47 +13,48 @@ namespace ARMeilleure.CodeGen.X86 private const int BadOp = 0; [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] private enum InstructionFlags { - None = 0, - RegOnly = 1 << 0, - Reg8Src = 1 << 1, + None = 0, + RegOnly = 1 << 0, + Reg8Src = 1 << 1, Reg8Dest = 1 << 2, - RexW = 1 << 3, - Vex = 1 << 4, - Evex = 1 << 5, + RexW = 1 << 3, + Vex = 1 << 4, + Evex = 1 << 5, - PrefixBit = 16, + PrefixBit = 16, PrefixMask = 7 << PrefixBit, - Prefix66 = 1 << PrefixBit, - PrefixF3 = 2 << PrefixBit, - PrefixF2 = 4 << PrefixBit + Prefix66 = 1 << PrefixBit, + PrefixF3 = 2 << PrefixBit, + PrefixF2 = 4 << PrefixBit, } private readonly struct InstructionInfo { - public int OpRMR { get; } - public int OpRMImm8 { get; } + public int OpRMR { get; } + public int OpRMImm8 { get; } public int OpRMImm32 { get; } - public int OpRImm64 { get; } - public int OpRRM { get; } + public int OpRImm64 { get; } + public int OpRRM { get; } public InstructionFlags Flags { get; } public InstructionInfo( - int opRMR, - int opRMImm8, - int opRMImm32, - int opRImm64, - int opRRM, + int opRMR, + int opRMImm8, + int opRMImm32, + int opRImm64, + int opRRM, InstructionFlags flags) { - OpRMR = opRMR; - OpRMImm8 = opRMImm8; + OpRMR = opRMR; + OpRMImm8 = opRMImm8; OpRMImm32 = opRMImm32; - OpRImm64 = opRImm64; - OpRRM = opRRM; - Flags = flags; + OpRImm64 = opRImm64; + OpRRM = opRRM; + Flags = flags; } } @@ -62,6 +64,7 @@ namespace ARMeilleure.CodeGen.X86 { _instTable = new InstructionInfo[(int)X86Instruction.Count]; +#pragma warning disable IDE0055 // Disable formatting // Name RM/R RM/I8 RM/I32 R/I64 R/RM Flags Add(X86Instruction.Add, new InstructionInfo(0x00000001, 0x00000083, 0x00000081, BadOp, 0x00000003, InstructionFlags.None)); Add(X86Instruction.Addpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.Prefix66)); @@ -285,6 +288,7 @@ namespace ARMeilleure.CodeGen.X86 Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None)); Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex)); +#pragma warning restore IDE0055 static void Add(X86Instruction inst, in InstructionInfo info) { diff --git a/src/ARMeilleure/CodeGen/X86/CallConvName.cs b/src/ARMeilleure/CodeGen/X86/CallConvName.cs index be3676282..6208da1ec 100644 --- a/src/ARMeilleure/CodeGen/X86/CallConvName.cs +++ b/src/ARMeilleure/CodeGen/X86/CallConvName.cs @@ -3,6 +3,6 @@ namespace ARMeilleure.CodeGen.X86 enum CallConvName { SystemV, - Windows + Windows, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/CallingConvention.cs b/src/ARMeilleure/CodeGen/X86/CallingConvention.cs index 953fef5b0..8433aaea9 100644 --- a/src/ARMeilleure/CodeGen/X86/CallingConvention.cs +++ b/src/ARMeilleure/CodeGen/X86/CallingConvention.cs @@ -20,6 +20,7 @@ namespace ARMeilleure.CodeGen.X86 { if (GetCurrentCallConv() == CallConvName.Windows) { +#pragma warning disable IDE0055 // Disable formatting return (1 << (int)X86Register.Rax) | (1 << (int)X86Register.Rcx) | (1 << (int)X86Register.Rdx) | @@ -39,6 +40,7 @@ namespace ARMeilleure.CodeGen.X86 (1 << (int)X86Register.R9) | (1 << (int)X86Register.R10) | (1 << (int)X86Register.R11); +#pragma warning restore IDE0055 } } @@ -90,22 +92,32 @@ namespace ARMeilleure.CodeGen.X86 { switch (index) { - case 0: return X86Register.Rcx; - case 1: return X86Register.Rdx; - case 2: return X86Register.R8; - case 3: return X86Register.R9; + case 0: + return X86Register.Rcx; + case 1: + return X86Register.Rdx; + case 2: + return X86Register.R8; + case 3: + return X86Register.R9; } } else /* if (GetCurrentCallConv() == CallConvName.SystemV) */ { switch (index) { - case 0: return X86Register.Rdi; - case 1: return X86Register.Rsi; - case 2: return X86Register.Rdx; - case 3: return X86Register.Rcx; - case 4: return X86Register.R8; - case 5: return X86Register.R9; + case 0: + return X86Register.Rdi; + case 1: + return X86Register.Rsi; + case 2: + return X86Register.Rdx; + case 3: + return X86Register.Rcx; + case 4: + return X86Register.R8; + case 5: + return X86Register.R9; } } @@ -155,4 +167,4 @@ namespace ARMeilleure.CodeGen.X86 : CallConvName.SystemV; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/CodeGenContext.cs b/src/ARMeilleure/CodeGen/X86/CodeGenContext.cs index 899487241..d4d4c2058 100644 --- a/src/ARMeilleure/CodeGen/X86/CodeGenContext.cs +++ b/src/ARMeilleure/CodeGen/X86/CodeGenContext.cs @@ -30,7 +30,7 @@ namespace ARMeilleure.CodeGen.X86 Assembler = new Assembler(_stream, relocatable); CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize); - XmmSaveRegionSize = xmmSaveRegionSize; + XmmSaveRegionSize = xmmSaveRegionSize; } private static int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize) @@ -102,4 +102,4 @@ namespace ARMeilleure.CodeGen.X86 return label; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs index e7179b517..3cab0b6ce 100644 --- a/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -17,7 +17,7 @@ namespace ARMeilleure.CodeGen.X86 static class CodeGenerator { private const int RegistersCount = 16; - private const int PageSize = 0x1000; + private const int PageSize = 0x1000; private const int StackGuardSize = 0x2000; private static readonly Action<CodeGenContext, Operation>[] _instTable; @@ -26,6 +26,7 @@ namespace ARMeilleure.CodeGen.X86 { _instTable = new Action<CodeGenContext, Operation>[EnumUtils.GetCount(typeof(Instruction))]; +#pragma warning disable IDE0055 // Disable formatting Add(Instruction.Add, GenerateAdd); Add(Instruction.BitwiseAnd, GenerateBitwiseAnd); Add(Instruction.BitwiseExclusiveOr, GenerateBitwiseExclusiveOr); @@ -85,6 +86,7 @@ namespace ARMeilleure.CodeGen.X86 Add(Instruction.ZeroExtend16, GenerateZeroExtend16); Add(Instruction.ZeroExtend32, GenerateZeroExtend32); Add(Instruction.ZeroExtend8, GenerateZeroExtend8); +#pragma warning restore IDE0055 static void Add(Instruction inst, Action<CodeGenContext, Operation> func) { @@ -203,290 +205,290 @@ namespace ARMeilleure.CodeGen.X86 switch (info.Type) { case IntrinsicType.Comis_: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - - switch (operation.Intrinsic) - { - case Intrinsic.X86Comisdeq: - context.Assembler.Comisd(src1, src2); - context.Assembler.Setcc(dest, X86Condition.Equal); - break; - - case Intrinsic.X86Comisdge: - context.Assembler.Comisd(src1, src2); - context.Assembler.Setcc(dest, X86Condition.AboveOrEqual); - break; - - case Intrinsic.X86Comisdlt: - context.Assembler.Comisd(src1, src2); - context.Assembler.Setcc(dest, X86Condition.Below); - break; - - case Intrinsic.X86Comisseq: - context.Assembler.Comiss(src1, src2); - context.Assembler.Setcc(dest, X86Condition.Equal); - break; - - case Intrinsic.X86Comissge: - context.Assembler.Comiss(src1, src2); - context.Assembler.Setcc(dest, X86Condition.AboveOrEqual); - break; - - case Intrinsic.X86Comisslt: - context.Assembler.Comiss(src1, src2); - context.Assembler.Setcc(dest, X86Condition.Below); - break; - } - - context.Assembler.Movzx8(dest, dest, OperandType.I32); - - break; - } - - case IntrinsicType.Mxcsr: - { - Operand offset = operation.GetSource(0); - - Debug.Assert(offset.Kind == OperandKind.Constant); - Debug.Assert(offset.Type == OperandType.I32); - - int offs = offset.AsInt32() + context.CallArgsRegionSize; - - Operand rsp = Register(X86Register.Rsp); - Operand memOp = MemoryOp(OperandType.I32, rsp, default, Multiplier.x1, offs); - - Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding); - - if (operation.Intrinsic == Intrinsic.X86Ldmxcsr) - { - Operand bits = operation.GetSource(1); - Debug.Assert(bits.Type == OperandType.I32); - - context.Assembler.Mov(memOp, bits, OperandType.I32); - context.Assembler.Ldmxcsr(memOp); - } - else if (operation.Intrinsic == Intrinsic.X86Stmxcsr) { Operand dest = operation.Destination; - Debug.Assert(dest.Type == OperandType.I32); + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); - context.Assembler.Stmxcsr(memOp); - context.Assembler.Mov(dest, memOp, OperandType.I32); + switch (operation.Intrinsic) + { + case Intrinsic.X86Comisdeq: + context.Assembler.Comisd(src1, src2); + context.Assembler.Setcc(dest, X86Condition.Equal); + break; + + case Intrinsic.X86Comisdge: + context.Assembler.Comisd(src1, src2); + context.Assembler.Setcc(dest, X86Condition.AboveOrEqual); + break; + + case Intrinsic.X86Comisdlt: + context.Assembler.Comisd(src1, src2); + context.Assembler.Setcc(dest, X86Condition.Below); + break; + + case Intrinsic.X86Comisseq: + context.Assembler.Comiss(src1, src2); + context.Assembler.Setcc(dest, X86Condition.Equal); + break; + + case Intrinsic.X86Comissge: + context.Assembler.Comiss(src1, src2); + context.Assembler.Setcc(dest, X86Condition.AboveOrEqual); + break; + + case Intrinsic.X86Comisslt: + context.Assembler.Comiss(src1, src2); + context.Assembler.Setcc(dest, X86Condition.Below); + break; + } + + context.Assembler.Movzx8(dest, dest, OperandType.I32); + + break; } - break; - } + case IntrinsicType.Mxcsr: + { + Operand offset = operation.GetSource(0); + + Debug.Assert(offset.Kind == OperandKind.Constant); + Debug.Assert(offset.Type == OperandType.I32); + + int offs = offset.AsInt32() + context.CallArgsRegionSize; + + Operand rsp = Register(X86Register.Rsp); + Operand memOp = MemoryOp(OperandType.I32, rsp, default, Multiplier.x1, offs); + + Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding); + + if (operation.Intrinsic == Intrinsic.X86Ldmxcsr) + { + Operand bits = operation.GetSource(1); + Debug.Assert(bits.Type == OperandType.I32); + + context.Assembler.Mov(memOp, bits, OperandType.I32); + context.Assembler.Ldmxcsr(memOp); + } + else if (operation.Intrinsic == Intrinsic.X86Stmxcsr) + { + Operand dest = operation.Destination; + Debug.Assert(dest.Type == OperandType.I32); + + context.Assembler.Stmxcsr(memOp); + context.Assembler.Mov(dest, memOp, OperandType.I32); + } + + break; + } case IntrinsicType.PopCount: - { - Operand dest = operation.Destination; - Operand source = operation.GetSource(0); + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); - EnsureSameType(dest, source); + EnsureSameType(dest, source); - Debug.Assert(dest.Type.IsInteger()); + Debug.Assert(dest.Type.IsInteger()); - context.Assembler.Popcnt(dest, source, dest.Type); + context.Assembler.Popcnt(dest, source, dest.Type); - break; - } + break; + } case IntrinsicType.Unary: - { - Operand dest = operation.Destination; - Operand source = operation.GetSource(0); + { + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); - EnsureSameType(dest, source); + EnsureSameType(dest, source); - Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!dest.Type.IsInteger()); - context.Assembler.WriteInstruction(info.Inst, dest, source); + context.Assembler.WriteInstruction(info.Inst, dest, source); - break; - } + break; + } case IntrinsicType.UnaryToGpr: - { - Operand dest = operation.Destination; - Operand source = operation.GetSource(0); - - Debug.Assert(dest.Type.IsInteger() && !source.Type.IsInteger()); - - if (operation.Intrinsic == Intrinsic.X86Cvtsi2si) { - if (dest.Type == OperandType.I32) - { - context.Assembler.Movd(dest, source); // int _mm_cvtsi128_si32(__m128i a) - } - else /* if (dest.Type == OperandType.I64) */ - { - context.Assembler.Movq(dest, source); // __int64 _mm_cvtsi128_si64(__m128i a) - } - } - else - { - context.Assembler.WriteInstruction(info.Inst, dest, source, dest.Type); - } + Operand dest = operation.Destination; + Operand source = operation.GetSource(0); - break; - } + Debug.Assert(dest.Type.IsInteger() && !source.Type.IsInteger()); + + if (operation.Intrinsic == Intrinsic.X86Cvtsi2si) + { + if (dest.Type == OperandType.I32) + { + context.Assembler.Movd(dest, source); // int _mm_cvtsi128_si32(__m128i a) + } + else /* if (dest.Type == OperandType.I64) */ + { + context.Assembler.Movq(dest, source); // __int64 _mm_cvtsi128_si64(__m128i a) + } + } + else + { + context.Assembler.WriteInstruction(info.Inst, dest, source, dest.Type); + } + + break; + } case IntrinsicType.Binary: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - - EnsureSameType(dest, src1); - - if (!HardwareCapabilities.SupportsVexEncoding) { - EnsureSameReg(dest, src1); - } + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); - Debug.Assert(!dest.Type.IsInteger()); - Debug.Assert(!src2.Type.IsInteger() || src2.Kind == OperandKind.Constant); + EnsureSameType(dest, src1); - context.Assembler.WriteInstruction(info.Inst, dest, src1, src2); + if (!HardwareCapabilities.SupportsVexEncoding) + { + EnsureSameReg(dest, src1); + } - break; - } - - case IntrinsicType.BinaryGpr: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - - EnsureSameType(dest, src1); - - if (!HardwareCapabilities.SupportsVexEncoding) - { - EnsureSameReg(dest, src1); - } - - Debug.Assert(!dest.Type.IsInteger() && src2.Type.IsInteger()); - - context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src2.Type); - - break; - } - - case IntrinsicType.Crc32: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - - EnsureSameReg(dest, src1); - - Debug.Assert(dest.Type.IsInteger() && src1.Type.IsInteger() && src2.Type.IsInteger()); - - context.Assembler.WriteInstruction(info.Inst, dest, src2, dest.Type); - - break; - } - - case IntrinsicType.BinaryImm: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - - EnsureSameType(dest, src1); - - if (!HardwareCapabilities.SupportsVexEncoding) - { - EnsureSameReg(dest, src1); - } - - Debug.Assert(!dest.Type.IsInteger() && src2.Kind == OperandKind.Constant); - - context.Assembler.WriteInstruction(info.Inst, dest, src1, src2.AsByte()); - - break; - } - - case IntrinsicType.Ternary: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - Operand src3 = operation.GetSource(2); - - EnsureSameType(dest, src1, src2, src3); - - Debug.Assert(!dest.Type.IsInteger()); - - if (info.Inst == X86Instruction.Blendvpd && HardwareCapabilities.SupportsVexEncoding) - { - context.Assembler.WriteInstruction(X86Instruction.Vblendvpd, dest, src1, src2, src3); - } - else if (info.Inst == X86Instruction.Blendvps && HardwareCapabilities.SupportsVexEncoding) - { - context.Assembler.WriteInstruction(X86Instruction.Vblendvps, dest, src1, src2, src3); - } - else if (info.Inst == X86Instruction.Pblendvb && HardwareCapabilities.SupportsVexEncoding) - { - context.Assembler.WriteInstruction(X86Instruction.Vpblendvb, dest, src1, src2, src3); - } - else - { - EnsureSameReg(dest, src1); - - Debug.Assert(src3.GetRegister().Index == 0); + Debug.Assert(!dest.Type.IsInteger()); + Debug.Assert(!src2.Type.IsInteger() || src2.Kind == OperandKind.Constant); context.Assembler.WriteInstruction(info.Inst, dest, src1, src2); + + break; } - break; - } + case IntrinsicType.BinaryGpr: + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + EnsureSameType(dest, src1); + + if (!HardwareCapabilities.SupportsVexEncoding) + { + EnsureSameReg(dest, src1); + } + + Debug.Assert(!dest.Type.IsInteger() && src2.Type.IsInteger()); + + context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src2.Type); + + break; + } + + case IntrinsicType.Crc32: + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + EnsureSameReg(dest, src1); + + Debug.Assert(dest.Type.IsInteger() && src1.Type.IsInteger() && src2.Type.IsInteger()); + + context.Assembler.WriteInstruction(info.Inst, dest, src2, dest.Type); + + break; + } + + case IntrinsicType.BinaryImm: + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + EnsureSameType(dest, src1); + + if (!HardwareCapabilities.SupportsVexEncoding) + { + EnsureSameReg(dest, src1); + } + + Debug.Assert(!dest.Type.IsInteger() && src2.Kind == OperandKind.Constant); + + context.Assembler.WriteInstruction(info.Inst, dest, src1, src2.AsByte()); + + break; + } + + case IntrinsicType.Ternary: + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + Operand src3 = operation.GetSource(2); + + EnsureSameType(dest, src1, src2, src3); + + Debug.Assert(!dest.Type.IsInteger()); + + if (info.Inst == X86Instruction.Blendvpd && HardwareCapabilities.SupportsVexEncoding) + { + context.Assembler.WriteInstruction(X86Instruction.Vblendvpd, dest, src1, src2, src3); + } + else if (info.Inst == X86Instruction.Blendvps && HardwareCapabilities.SupportsVexEncoding) + { + context.Assembler.WriteInstruction(X86Instruction.Vblendvps, dest, src1, src2, src3); + } + else if (info.Inst == X86Instruction.Pblendvb && HardwareCapabilities.SupportsVexEncoding) + { + context.Assembler.WriteInstruction(X86Instruction.Vpblendvb, dest, src1, src2, src3); + } + else + { + EnsureSameReg(dest, src1); + + Debug.Assert(src3.GetRegister().Index == 0); + + context.Assembler.WriteInstruction(info.Inst, dest, src1, src2); + } + + break; + } case IntrinsicType.TernaryImm: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - Operand src3 = operation.GetSource(2); - - EnsureSameType(dest, src1, src2); - - if (!HardwareCapabilities.SupportsVexEncoding) { - EnsureSameReg(dest, src1); + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + Operand src3 = operation.GetSource(2); + + EnsureSameType(dest, src1, src2); + + if (!HardwareCapabilities.SupportsVexEncoding) + { + EnsureSameReg(dest, src1); + } + + Debug.Assert(!dest.Type.IsInteger() && src3.Kind == OperandKind.Constant); + + context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src3.AsByte()); + + break; } - Debug.Assert(!dest.Type.IsInteger() && src3.Kind == OperandKind.Constant); - - context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src3.AsByte()); - - break; - } - case IntrinsicType.Fma: - { - Operand dest = operation.Destination; - Operand src1 = operation.GetSource(0); - Operand src2 = operation.GetSource(1); - Operand src3 = operation.GetSource(2); + { + Operand dest = operation.Destination; + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + Operand src3 = operation.GetSource(2); - Debug.Assert(HardwareCapabilities.SupportsVexEncoding); + Debug.Assert(HardwareCapabilities.SupportsVexEncoding); - Debug.Assert(dest.Kind == OperandKind.Register && src1.Kind == OperandKind.Register && src2.Kind == OperandKind.Register); - Debug.Assert(src3.Kind == OperandKind.Register || src3.Kind == OperandKind.Memory); + Debug.Assert(dest.Kind == OperandKind.Register && src1.Kind == OperandKind.Register && src2.Kind == OperandKind.Register); + Debug.Assert(src3.Kind == OperandKind.Register || src3.Kind == OperandKind.Memory); - EnsureSameType(dest, src1, src2, src3); - Debug.Assert(dest.Type == OperandType.V128); + EnsureSameType(dest, src1, src2, src3); + Debug.Assert(dest.Type == OperandType.V128); - Debug.Assert(dest.Value == src1.Value); + Debug.Assert(dest.Value == src1.Value); - context.Assembler.WriteInstruction(info.Inst, dest, src2, src3); + context.Assembler.WriteInstruction(info.Inst, dest, src2, src3); - break; - } + break; + } } } else @@ -592,7 +594,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateBitwiseNot(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); ValidateUnOp(dest, source); @@ -630,7 +632,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateByteSwap(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); ValidateUnOp(dest, source); @@ -761,19 +763,19 @@ namespace ARMeilleure.CodeGen.X86 Operand src2 = operation.GetSource(1); Operand src3 = operation.GetSource(2); - EnsureSameReg (dest, src3); + EnsureSameReg(dest, src3); EnsureSameType(dest, src2, src3); Debug.Assert(dest.Type.IsInteger()); Debug.Assert(src1.Type == OperandType.I32); - context.Assembler.Test (src1, src1, src1.Type); + context.Assembler.Test(src1, src1, src1.Type); context.Assembler.Cmovcc(dest, src2, dest.Type, X86Condition.NotEqual); } private static void GenerateConvertI64ToI32(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.I32 && source.Type == OperandType.I64); @@ -783,7 +785,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateConvertToFP(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64); @@ -794,7 +796,7 @@ namespace ARMeilleure.CodeGen.X86 if (source.Type.IsInteger()) { - context.Assembler.Xorps (dest, dest, dest); + context.Assembler.Xorps(dest, dest, dest); context.Assembler.Cvtsi2ss(dest, dest, source, source.Type); } else /* if (source.Type == OperandType.FP64) */ @@ -810,7 +812,7 @@ namespace ARMeilleure.CodeGen.X86 if (source.Type.IsInteger()) { - context.Assembler.Xorps (dest, dest, dest); + context.Assembler.Xorps(dest, dest, dest); context.Assembler.Cvtsi2sd(dest, dest, source, source.Type); } else /* if (source.Type == OperandType.FP32) */ @@ -824,7 +826,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateCopy(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); EnsureSameType(dest, source); @@ -837,7 +839,7 @@ namespace ARMeilleure.CodeGen.X86 return; } - if (dest.Kind == OperandKind.Register && + if (dest.Kind == OperandKind.Register && source.Kind == OperandKind.Constant && source.Value == 0) { // Assemble "mov reg, 0" as "xor reg, reg" as the later is more efficient. @@ -855,7 +857,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateCountLeadingZeros(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); EnsureSameType(dest, source); @@ -888,9 +890,9 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateDivide(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand dividend = operation.GetSource(0); - Operand divisor = operation.GetSource(1); + Operand divisor = operation.GetSource(1); if (!dest.Type.IsInteger()) { @@ -938,7 +940,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateFill(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand offset = operation.GetSource(0); Debug.Assert(offset.Kind == OperandKind.Constant); @@ -954,7 +956,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateLoad(CodeGenContext context, Operation operation) { - Operand value = operation.Destination; + Operand value = operation.Destination; Operand address = Memory(operation.GetSource(0), value.Type); GenerateLoad(context, address, value); @@ -962,7 +964,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateLoad16(CodeGenContext context, Operation operation) { - Operand value = operation.Destination; + Operand value = operation.Destination; Operand address = Memory(operation.GetSource(0), value.Type); Debug.Assert(value.Type.IsInteger()); @@ -972,7 +974,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateLoad8(CodeGenContext context, Operation operation) { - Operand value = operation.Destination; + Operand value = operation.Destination; Operand address = Memory(operation.GetSource(0), value.Type); Debug.Assert(value.Type.IsInteger()); @@ -1039,7 +1041,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateNegate(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); ValidateUnOp(dest, source); @@ -1102,7 +1104,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateSignExtend16(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1112,7 +1114,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateSignExtend32(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1122,7 +1124,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateSignExtend8(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1158,7 +1160,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateStackAlloc(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand offset = operation.GetSource(0); Debug.Assert(offset.Kind == OperandKind.Constant); @@ -1174,7 +1176,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateStore(CodeGenContext context, Operation operation) { - Operand value = operation.GetSource(1); + Operand value = operation.GetSource(1); Operand address = Memory(operation.GetSource(0), value.Type); GenerateStore(context, address, value); @@ -1182,7 +1184,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateStore16(CodeGenContext context, Operation operation) { - Operand value = operation.GetSource(1); + Operand value = operation.GetSource(1); Operand address = Memory(operation.GetSource(0), value.Type); Debug.Assert(value.Type.IsInteger()); @@ -1192,7 +1194,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateStore8(CodeGenContext context, Operation operation) { - Operand value = operation.GetSource(1); + Operand value = operation.GetSource(1); Operand address = Memory(operation.GetSource(0), value.Type); Debug.Assert(value.Type.IsInteger()); @@ -1231,7 +1233,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateVectorCreateScalar(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(!dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1278,7 +1280,7 @@ namespace ARMeilleure.CodeGen.X86 mask1 = BitUtils.RotateRight(mask1, 8 - index * 2, 8); context.Assembler.Pshufd(src1, src1, (byte)mask0); - context.Assembler.Movd (dest, src1); + context.Assembler.Movd(dest, src1); context.Assembler.Pshufd(src1, src1, (byte)mask1); } } @@ -1297,7 +1299,7 @@ namespace ARMeilleure.CodeGen.X86 const byte mask = 0b01_00_11_10; context.Assembler.Pshufd(src1, src1, mask); - context.Assembler.Movq (dest, src1); + context.Assembler.Movq(dest, src1); context.Assembler.Pshufd(src1, src1, mask); } } @@ -1308,7 +1310,7 @@ namespace ARMeilleure.CodeGen.X86 (index == 1 && dest.Type == OperandType.FP64)) { context.Assembler.Movhlps(dest, dest, src1); - context.Assembler.Movq (dest, dest); + context.Assembler.Movq(dest, dest); } else { @@ -1455,11 +1457,11 @@ namespace ARMeilleure.CodeGen.X86 int mask0 = 0b11_10_01_00; int mask1 = 0b11_10_01_00; - mask0 = BitUtils.RotateRight(mask0, index * 2, 8); + mask0 = BitUtils.RotateRight(mask0, index * 2, 8); mask1 = BitUtils.RotateRight(mask1, 8 - index * 2, 8); context.Assembler.Pshufd(src1, src1, (byte)mask0); // Lane to be inserted in position 0. - context.Assembler.Movss (dest, src1, src2); // dest[127:0] = src1[127:32] | src2[31:0] + context.Assembler.Movss(dest, src1, src2); // dest[127:0] = src1[127:32] | src2[31:0] context.Assembler.Pshufd(dest, dest, (byte)mask1); // Inserted lane in original position. if (dest.GetRegister() != src1.GetRegister()) @@ -1555,7 +1557,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateVectorZeroUpper64(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128); @@ -1565,7 +1567,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateVectorZeroUpper96(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128); @@ -1575,7 +1577,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateZeroExtend16(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1585,7 +1587,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateZeroExtend32(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1601,7 +1603,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateZeroExtend8(CodeGenContext context, Operation operation) { - Operand dest = operation.Destination; + Operand dest = operation.Destination; Operand source = operation.GetSource(0); Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); @@ -1613,13 +1615,25 @@ namespace ARMeilleure.CodeGen.X86 { switch (value.Type) { - case OperandType.I32: context.Assembler.Mov (value, address, OperandType.I32); break; - case OperandType.I64: context.Assembler.Mov (value, address, OperandType.I64); break; - case OperandType.FP32: context.Assembler.Movd (value, address); break; - case OperandType.FP64: context.Assembler.Movq (value, address); break; - case OperandType.V128: context.Assembler.Movdqu(value, address); break; + case OperandType.I32: + context.Assembler.Mov(value, address, OperandType.I32); + break; + case OperandType.I64: + context.Assembler.Mov(value, address, OperandType.I64); + break; + case OperandType.FP32: + context.Assembler.Movd(value, address); + break; + case OperandType.FP64: + context.Assembler.Movq(value, address); + break; + case OperandType.V128: + context.Assembler.Movdqu(value, address); + break; - default: Debug.Assert(false); break; + default: + Debug.Assert(false); + break; } } @@ -1627,13 +1641,25 @@ namespace ARMeilleure.CodeGen.X86 { switch (value.Type) { - case OperandType.I32: context.Assembler.Mov (address, value, OperandType.I32); break; - case OperandType.I64: context.Assembler.Mov (address, value, OperandType.I64); break; - case OperandType.FP32: context.Assembler.Movd (address, value); break; - case OperandType.FP64: context.Assembler.Movq (address, value); break; - case OperandType.V128: context.Assembler.Movdqu(address, value); break; + case OperandType.I32: + context.Assembler.Mov(address, value, OperandType.I32); + break; + case OperandType.I64: + context.Assembler.Mov(address, value, OperandType.I64); + break; + case OperandType.FP32: + context.Assembler.Movd(address, value); + break; + case OperandType.FP64: + context.Assembler.Movq(address, value); + break; + case OperandType.V128: + context.Assembler.Movdqu(address, value); + break; - default: Debug.Assert(false); break; + default: + Debug.Assert(false); + break; } } @@ -1670,21 +1696,21 @@ namespace ARMeilleure.CodeGen.X86 [Conditional("DEBUG")] private static void ValidateUnOp(Operand dest, Operand source) { - EnsureSameReg (dest, source); + EnsureSameReg(dest, source); EnsureSameType(dest, source); } [Conditional("DEBUG")] private static void ValidateBinOp(Operand dest, Operand src1, Operand src2) { - EnsureSameReg (dest, src1); + EnsureSameReg(dest, src1); EnsureSameType(dest, src1, src2); } [Conditional("DEBUG")] private static void ValidateShift(Operand dest, Operand src1, Operand src2) { - EnsureSameReg (dest, src1); + EnsureSameReg(dest, src1); EnsureSameType(dest, src1); Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32); @@ -1722,7 +1748,7 @@ namespace ARMeilleure.CodeGen.X86 private static UnwindInfo WritePrologue(CodeGenContext context) { - List<UnwindPushEntry> pushEntries = new List<UnwindPushEntry>(); + List<UnwindPushEntry> pushEntries = new(); Operand rsp = Register(X86Register.Rsp); @@ -1831,7 +1857,7 @@ namespace ARMeilleure.CodeGen.X86 size = (size + pageMask) & ~pageMask; - Operand rsp = Register(X86Register.Rsp); + Operand rsp = Register(X86Register.Rsp); Operand temp = Register(CallingConvention.GetIntReturnRegister()); for (int offset = PageSize; offset < size; offset += PageSize) @@ -1862,4 +1888,4 @@ namespace ARMeilleure.CodeGen.X86 return Operand.Factory.Register((int)register, RegisterType.Vector, OperandType.V128); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index 07cdcd096..4f6f1e87b 100644 --- a/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -47,7 +47,7 @@ namespace ARMeilleure.CodeGen.X86 0xc3, // ret }; - using MemoryBlock memGetXcr0 = new MemoryBlock((ulong)asmGetXcr0.Length); + using MemoryBlock memGetXcr0 = new((ulong)asmGetXcr0.Length); memGetXcr0.Write(0, asmGetXcr0); @@ -62,7 +62,7 @@ namespace ARMeilleure.CodeGen.X86 public enum FeatureFlags1Edx { Sse = 1 << 25, - Sse2 = 1 << 26 + Sse2 = 1 << 26, } [Flags] @@ -79,7 +79,7 @@ namespace ARMeilleure.CodeGen.X86 Xsave = 1 << 26, Osxsave = 1 << 27, Avx = 1 << 28, - F16c = 1 << 29 + F16c = 1 << 29, } [Flags] @@ -90,7 +90,7 @@ namespace ARMeilleure.CodeGen.X86 Avx512dq = 1 << 17, Sha = 1 << 29, Avx512bw = 1 << 30, - Avx512vl = 1 << 31 + Avx512vl = 1 << 31, } [Flags] @@ -106,7 +106,7 @@ namespace ARMeilleure.CodeGen.X86 YmmHi128 = 1 << 2, Opmask = 1 << 5, ZmmHi256 = 1 << 6, - Hi16Zmm = 1 << 7 + Hi16Zmm = 1 << 7, } public static FeatureFlags1Edx FeatureInfo1Edx { get; } @@ -141,4 +141,4 @@ namespace ARMeilleure.CodeGen.X86 public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse; public static bool SupportsEvexEncoding => SupportsAvx512F && !ForceLegacySse; } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs b/src/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs index 302bf4d3c..16054c616 100644 --- a/src/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs +++ b/src/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs @@ -3,7 +3,7 @@ namespace ARMeilleure.CodeGen.X86 readonly struct IntrinsicInfo { public X86Instruction Inst { get; } - public IntrinsicType Type { get; } + public IntrinsicType Type { get; } public IntrinsicInfo(X86Instruction inst, IntrinsicType type) { @@ -11,4 +11,4 @@ namespace ARMeilleure.CodeGen.X86 Type = type; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/src/ARMeilleure/CodeGen/X86/IntrinsicTable.cs index e3d94b7ae..daa1f8f60 100644 --- a/src/ARMeilleure/CodeGen/X86/IntrinsicTable.cs +++ b/src/ARMeilleure/CodeGen/X86/IntrinsicTable.cs @@ -5,12 +5,13 @@ namespace ARMeilleure.CodeGen.X86 { static class IntrinsicTable { - private static IntrinsicInfo[] _intrinTable; + private static readonly IntrinsicInfo[] _intrinTable; static IntrinsicTable() { _intrinTable = new IntrinsicInfo[EnumUtils.GetCount(typeof(Intrinsic))]; +#pragma warning disable IDE0055 // Disable formatting Add(Intrinsic.X86Addpd, new IntrinsicInfo(X86Instruction.Addpd, IntrinsicType.Binary)); Add(Intrinsic.X86Addps, new IntrinsicInfo(X86Instruction.Addps, IntrinsicType.Binary)); Add(Intrinsic.X86Addsd, new IntrinsicInfo(X86Instruction.Addsd, IntrinsicType.Binary)); @@ -185,6 +186,7 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Vpternlogd, new IntrinsicInfo(X86Instruction.Vpternlogd, IntrinsicType.TernaryImm)); Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary)); Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary)); +#pragma warning restore IDE0055 } private static void Add(Intrinsic intrin, IntrinsicInfo info) @@ -197,4 +199,4 @@ namespace ARMeilleure.CodeGen.X86 return _intrinTable[(int)intrin]; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/IntrinsicType.cs b/src/ARMeilleure/CodeGen/X86/IntrinsicType.cs index 5a9c14afa..7c3ef354d 100644 --- a/src/ARMeilleure/CodeGen/X86/IntrinsicType.cs +++ b/src/ARMeilleure/CodeGen/X86/IntrinsicType.cs @@ -13,6 +13,6 @@ namespace ARMeilleure.CodeGen.X86 Crc32, Ternary, TernaryImm, - Fma + Fma, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/Mxcsr.cs b/src/ARMeilleure/CodeGen/X86/Mxcsr.cs index c61eac31a..83d7a5845 100644 --- a/src/ARMeilleure/CodeGen/X86/Mxcsr.cs +++ b/src/ARMeilleure/CodeGen/X86/Mxcsr.cs @@ -10,6 +10,6 @@ namespace ARMeilleure.CodeGen.X86 Rlo = 1 << 13, // Round Mode low bit. Um = 1 << 11, // Underflow Mask. Dm = 1 << 8, // Denormal Mask. - Daz = 1 << 6 // Denormals Are Zero. + Daz = 1 << 6, // Denormals Are Zero. } } diff --git a/src/ARMeilleure/CodeGen/X86/PreAllocator.cs b/src/ARMeilleure/CodeGen/X86/PreAllocator.cs index cb742d67f..590c35c7b 100644 --- a/src/ARMeilleure/CodeGen/X86/PreAllocator.cs +++ b/src/ARMeilleure/CodeGen/X86/PreAllocator.cs @@ -104,11 +104,11 @@ namespace ARMeilleure.CodeGen.X86 case Instruction.Tailcall: if (callConv == CallConvName.Windows) { - PreAllocatorWindows.InsertTailcallCopies(block.Operations, stackAlloc, node); + PreAllocatorWindows.InsertTailcallCopies(block.Operations, node); } else { - PreAllocatorSystemV.InsertTailcallCopies(block.Operations, stackAlloc, node); + PreAllocatorSystemV.InsertTailcallCopies(block.Operations, node); } break; @@ -177,10 +177,7 @@ namespace ARMeilleure.CodeGen.X86 { src2 = node.GetSource(1); - Operand temp = src1; - - src1 = src2; - src2 = temp; + (src2, src1) = (src1, src2); node.SetSource(0, src1); node.SetSource(1, src2); @@ -228,151 +225,151 @@ namespace ARMeilleure.CodeGen.X86 case Instruction.CompareAndSwap: case Instruction.CompareAndSwap16: case Instruction.CompareAndSwap8: - { - OperandType type = node.GetSource(1).Type; - - if (type == OperandType.V128) { - // Handle the many restrictions of the compare and exchange (16 bytes) instruction: - // - The expected value should be in RDX:RAX. - // - The new value to be written should be in RCX:RBX. - // - The value at the memory location is loaded to RDX:RAX. - void SplitOperand(Operand source, Operand lr, Operand hr) + OperandType type = node.GetSource(1).Type; + + if (type == OperandType.V128) { - nodes.AddBefore(node, Operation(Instruction.VectorExtract, lr, source, Const(0))); - nodes.AddBefore(node, Operation(Instruction.VectorExtract, hr, source, Const(1))); + // Handle the many restrictions of the compare and exchange (16 bytes) instruction: + // - The expected value should be in RDX:RAX. + // - The new value to be written should be in RCX:RBX. + // - The value at the memory location is loaded to RDX:RAX. + void SplitOperand(Operand source, Operand lr, Operand hr) + { + nodes.AddBefore(node, Operation(Instruction.VectorExtract, lr, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, hr, source, Const(1))); + } + + Operand rax = Gpr(X86Register.Rax, OperandType.I64); + Operand rbx = Gpr(X86Register.Rbx, OperandType.I64); + Operand rcx = Gpr(X86Register.Rcx, OperandType.I64); + Operand rdx = Gpr(X86Register.Rdx, OperandType.I64); + + SplitOperand(node.GetSource(1), rax, rdx); + SplitOperand(node.GetSource(2), rbx, rcx); + + Operation operation = node; + + node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, rax)); + nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, rdx, Const(1))); + + operation.SetDestinations(new Operand[] { rdx, rax }); + operation.SetSources(new Operand[] { operation.GetSource(0), rdx, rax, rcx, rbx }); + } + else + { + // Handle the many restrictions of the compare and exchange (32/64) instruction: + // - The expected value should be in (E/R)AX. + // - The value at the memory location is loaded to (E/R)AX. + Operand expected = node.GetSource(1); + Operand newValue = node.GetSource(2); + + Operand rax = Gpr(X86Register.Rax, expected.Type); + + nodes.AddBefore(node, Operation(Instruction.Copy, rax, expected)); + + // We need to store the new value into a temp, since it may + // be a constant, and this instruction does not support immediate operands. + Operand temp = Local(newValue.Type); + + nodes.AddBefore(node, Operation(Instruction.Copy, temp, newValue)); + + node.SetSources(new Operand[] { node.GetSource(0), rax, temp }); + + nodes.AddAfter(node, Operation(Instruction.Copy, dest, rax)); + + node.Destination = rax; } - Operand rax = Gpr(X86Register.Rax, OperandType.I64); - Operand rbx = Gpr(X86Register.Rbx, OperandType.I64); - Operand rcx = Gpr(X86Register.Rcx, OperandType.I64); - Operand rdx = Gpr(X86Register.Rdx, OperandType.I64); - - SplitOperand(node.GetSource(1), rax, rdx); - SplitOperand(node.GetSource(2), rbx, rcx); - - Operation operation = node; - - node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, rax)); - nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, rdx, Const(1))); - - operation.SetDestinations(new Operand[] { rdx, rax }); - operation.SetSources(new Operand[] { operation.GetSource(0), rdx, rax, rcx, rbx }); + break; } - else - { - // Handle the many restrictions of the compare and exchange (32/64) instruction: - // - The expected value should be in (E/R)AX. - // - The value at the memory location is loaded to (E/R)AX. - Operand expected = node.GetSource(1); - Operand newValue = node.GetSource(2); - - Operand rax = Gpr(X86Register.Rax, expected.Type); - - nodes.AddBefore(node, Operation(Instruction.Copy, rax, expected)); - - // We need to store the new value into a temp, since it may - // be a constant, and this instruction does not support immediate operands. - Operand temp = Local(newValue.Type); - - nodes.AddBefore(node, Operation(Instruction.Copy, temp, newValue)); - - node.SetSources(new Operand[] { node.GetSource(0), rax, temp }); - - nodes.AddAfter(node, Operation(Instruction.Copy, dest, rax)); - - node.Destination = rax; - } - - break; - } case Instruction.Divide: case Instruction.DivideUI: - { - // Handle the many restrictions of the division instructions: - // - The dividend is always in RDX:RAX. - // - The result is always in RAX. - // - Additionally it also writes the remainder in RDX. - if (dest.Type.IsInteger()) { + // Handle the many restrictions of the division instructions: + // - The dividend is always in RDX:RAX. + // - The result is always in RAX. + // - Additionally it also writes the remainder in RDX. + if (dest.Type.IsInteger()) + { + Operand src1 = node.GetSource(0); + + Operand rax = Gpr(X86Register.Rax, src1.Type); + Operand rdx = Gpr(X86Register.Rdx, src1.Type); + + nodes.AddBefore(node, Operation(Instruction.Copy, rax, src1)); + nodes.AddBefore(node, Operation(Instruction.Clobber, rdx)); + + nodes.AddAfter(node, Operation(Instruction.Copy, dest, rax)); + + node.SetSources(new Operand[] { rdx, rax, node.GetSource(1) }); + node.Destination = rax; + } + + break; + } + + case Instruction.Extended: + { + bool isBlend = node.Intrinsic == Intrinsic.X86Blendvpd || + node.Intrinsic == Intrinsic.X86Blendvps || + node.Intrinsic == Intrinsic.X86Pblendvb; + + // BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported. + // SHA256RNDS2 always has an implied XMM0 as a last operand. + if ((isBlend && !HardwareCapabilities.SupportsVexEncoding) || node.Intrinsic == Intrinsic.X86Sha256Rnds2) + { + Operand xmm0 = Xmm(X86Register.Xmm0, OperandType.V128); + + nodes.AddBefore(node, Operation(Instruction.Copy, xmm0, node.GetSource(2))); + + node.SetSource(2, xmm0); + } + + break; + } + + case Instruction.Multiply64HighSI: + case Instruction.Multiply64HighUI: + { + // Handle the many restrictions of the i64 * i64 = i128 multiply instructions: + // - The multiplicand is always in RAX. + // - The lower 64-bits of the result is always in RAX. + // - The higher 64-bits of the result is always in RDX. Operand src1 = node.GetSource(0); Operand rax = Gpr(X86Register.Rax, src1.Type); Operand rdx = Gpr(X86Register.Rdx, src1.Type); - nodes.AddBefore(node, Operation(Instruction.Copy, rax, src1)); - nodes.AddBefore(node, Operation(Instruction.Clobber, rdx)); + nodes.AddBefore(node, Operation(Instruction.Copy, rax, src1)); - nodes.AddAfter(node, Operation(Instruction.Copy, dest, rax)); + node.SetSource(0, rax); - node.SetSources(new Operand[] { rdx, rax, node.GetSource(1) }); - node.Destination = rax; + nodes.AddAfter(node, Operation(Instruction.Copy, dest, rdx)); + + node.SetDestinations(new Operand[] { rdx, rax }); + + break; } - break; - } - - case Instruction.Extended: - { - bool isBlend = node.Intrinsic == Intrinsic.X86Blendvpd || - node.Intrinsic == Intrinsic.X86Blendvps || - node.Intrinsic == Intrinsic.X86Pblendvb; - - // BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported. - // SHA256RNDS2 always has an implied XMM0 as a last operand. - if ((isBlend && !HardwareCapabilities.SupportsVexEncoding) || node.Intrinsic == Intrinsic.X86Sha256Rnds2) - { - Operand xmm0 = Xmm(X86Register.Xmm0, OperandType.V128); - - nodes.AddBefore(node, Operation(Instruction.Copy, xmm0, node.GetSource(2))); - - node.SetSource(2, xmm0); - } - - break; - } - - case Instruction.Multiply64HighSI: - case Instruction.Multiply64HighUI: - { - // Handle the many restrictions of the i64 * i64 = i128 multiply instructions: - // - The multiplicand is always in RAX. - // - The lower 64-bits of the result is always in RAX. - // - The higher 64-bits of the result is always in RDX. - Operand src1 = node.GetSource(0); - - Operand rax = Gpr(X86Register.Rax, src1.Type); - Operand rdx = Gpr(X86Register.Rdx, src1.Type); - - nodes.AddBefore(node, Operation(Instruction.Copy, rax, src1)); - - node.SetSource(0, rax); - - nodes.AddAfter(node, Operation(Instruction.Copy, dest, rdx)); - - node.SetDestinations(new Operand[] { rdx, rax }); - - break; - } - case Instruction.RotateRight: case Instruction.ShiftLeft: case Instruction.ShiftRightSI: case Instruction.ShiftRightUI: - { - // The shift register is always implied to be CL (low 8-bits of RCX or ECX). - if (node.GetSource(1).Kind == OperandKind.LocalVariable) { - Operand rcx = Gpr(X86Register.Rcx, OperandType.I32); + // The shift register is always implied to be CL (low 8-bits of RCX or ECX). + if (node.GetSource(1).Kind == OperandKind.LocalVariable) + { + Operand rcx = Gpr(X86Register.Rcx, OperandType.I32); - nodes.AddBefore(node, Operation(Instruction.Copy, rcx, node.GetSource(1))); + nodes.AddBefore(node, Operation(Instruction.Copy, rcx, node.GetSource(1))); - node.SetSource(1, rcx); + node.SetSource(1, rcx); + } + + break; } - - break; - } } } @@ -459,7 +456,7 @@ namespace ARMeilleure.CodeGen.X86 // Unsigned integer to FP conversions are not supported on X86. // We need to turn them into signed integer to FP conversions, and // adjust the final result. - Operand dest = node.Destination; + Operand dest = node.Destination; Operand source = node.GetSource(0); Debug.Assert(source.Type.IsInteger(), $"Invalid source type \"{source.Type}\"."); @@ -472,8 +469,8 @@ namespace ARMeilleure.CodeGen.X86 // and then use the 64-bits signed conversion instructions. Operand zex = Local(OperandType.I64); - node = nodes.AddAfter(node, Operation(Instruction.ZeroExtend32, zex, source)); - node = nodes.AddAfter(node, Operation(Instruction.ConvertToFP, dest, zex)); + node = nodes.AddAfter(node, Operation(Instruction.ZeroExtend32, zex, source)); + nodes.AddAfter(node, Operation(Instruction.ConvertToFP, dest, zex)); } else /* if (source.Type == OperandType.I64) */ { @@ -487,15 +484,15 @@ namespace ARMeilleure.CodeGen.X86 // --- This can be done efficiently by adding the result to itself. // -- Then, we need to add the least significant bit that was shifted out. // --- We can convert the least significant bit to float, and add it to the result. - Operand lsb = Local(OperandType.I64); + Operand lsb = Local(OperandType.I64); Operand half = Local(OperandType.I64); Operand lsbF = Local(dest.Type); - node = nodes.AddAfter(node, Operation(Instruction.Copy, lsb, source)); + node = nodes.AddAfter(node, Operation(Instruction.Copy, lsb, source)); node = nodes.AddAfter(node, Operation(Instruction.Copy, half, source)); - node = nodes.AddAfter(node, Operation(Instruction.BitwiseAnd, lsb, lsb, Const(1L))); + node = nodes.AddAfter(node, Operation(Instruction.BitwiseAnd, lsb, lsb, Const(1L))); node = nodes.AddAfter(node, Operation(Instruction.ShiftRightUI, half, half, Const(1))); node = nodes.AddAfter(node, Operation(Instruction.ConvertToFP, lsbF, lsb)); @@ -513,7 +510,7 @@ namespace ARMeilleure.CodeGen.X86 // There's no SSE FP negate instruction, so we need to transform that into // a XOR of the value to be negated with a mask with the highest bit set. // This also produces -0 for a negation of the value 0. - Operand dest = node.Destination; + Operand dest = node.Destination; Operand source = node.GetSource(0); Debug.Assert(dest.Type == OperandType.FP32 || @@ -569,14 +566,14 @@ namespace ARMeilleure.CodeGen.X86 if ((index & 1) != 0) { node = nodes.AddAfter(node, Operation(Instruction.ZeroExtend8, temp1, temp1)); - node = nodes.AddAfter(node, Operation(Instruction.ShiftLeft, temp2, temp2, Const(8))); - node = nodes.AddAfter(node, Operation(Instruction.BitwiseOr, temp1, temp1, temp2)); + node = nodes.AddAfter(node, Operation(Instruction.ShiftLeft, temp2, temp2, Const(8))); + node = nodes.AddAfter(node, Operation(Instruction.BitwiseOr, temp1, temp1, temp2)); } else { node = nodes.AddAfter(node, Operation(Instruction.ZeroExtend8, temp2, temp2)); - node = nodes.AddAfter(node, Operation(Instruction.BitwiseAnd, temp1, temp1, Const(0xff00))); - node = nodes.AddAfter(node, Operation(Instruction.BitwiseOr, temp1, temp1, temp2)); + node = nodes.AddAfter(node, Operation(Instruction.BitwiseAnd, temp1, temp1, Const(0xff00))); + node = nodes.AddAfter(node, Operation(Instruction.BitwiseOr, temp1, temp1, temp2)); } Operation vinsOp = Operation(Instruction.VectorInsert16, dest, src1, temp1, Const(index >> 1)); @@ -709,16 +706,11 @@ namespace ARMeilleure.CodeGen.X86 private static bool HasConstSrc1(Instruction inst) { - switch (inst) + return inst switch { - case Instruction.Copy: - case Instruction.LoadArgument: - case Instruction.Spill: - case Instruction.SpillArg: - return true; - } - - return false; + Instruction.Copy or Instruction.LoadArgument or Instruction.Spill or Instruction.SpillArg => true, + _ => false, + }; } private static bool HasConstSrc2(Instruction inst) @@ -762,15 +754,15 @@ namespace ARMeilleure.CodeGen.X86 case Instruction.BranchIf: case Instruction.Compare: - { - Operand comp = operation.GetSource(2); + { + Operand comp = operation.GetSource(2); - Debug.Assert(comp.Kind == OperandKind.Constant); + Debug.Assert(comp.Kind == OperandKind.Constant); - var compType = (Comparison)comp.AsInt32(); + var compType = (Comparison)comp.AsInt32(); - return compType == Comparison.Equal || compType == Comparison.NotEqual; - } + return compType == Comparison.Equal || compType == Comparison.NotEqual; + } } return false; @@ -793,4 +785,4 @@ namespace ARMeilleure.CodeGen.X86 return info.Type != IntrinsicType.Crc32; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs b/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs index a84d5050d..e754cb09b 100644 --- a/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs +++ b/src/ARMeilleure/CodeGen/X86/PreAllocatorSystemV.cs @@ -1,4 +1,3 @@ -using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; @@ -15,9 +14,9 @@ namespace ARMeilleure.CodeGen.X86 { Operand dest = node.Destination; - List<Operand> sources = new List<Operand> + List<Operand> sources = new() { - node.GetSource(0) + node.GetSource(0), }; int argsCount = node.SourcesCount - 1; @@ -52,10 +51,10 @@ namespace ARMeilleure.CodeGen.X86 if (source.Type == OperandType.V128 && passOnReg) { // V128 is a struct, we pass each half on a GPR if possible. - Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); + Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64); - nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); + nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0))); nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1))); continue; @@ -91,7 +90,7 @@ namespace ARMeilleure.CodeGen.X86 { if (dest.Type == OperandType.V128) { - Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); Operation operation = node; @@ -116,11 +115,11 @@ namespace ARMeilleure.CodeGen.X86 } } - public static void InsertTailcallCopies(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) + public static void InsertTailcallCopies(IntrusiveList<Operation> nodes, Operation node) { - List<Operand> sources = new List<Operand> + List<Operand> sources = new() { - node.GetSource(0) + node.GetSource(0), }; int argsCount = node.SourcesCount - 1; @@ -251,11 +250,11 @@ namespace ARMeilleure.CodeGen.X86 // V128 is a struct, we pass each half on a GPR if possible. Operand pArg = Local(OperandType.V128); - Operand argLReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount), OperandType.I64); + Operand argLReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount), OperandType.I64); Operand argHReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount + 1), OperandType.I64); Operation copyL = Operation(Instruction.VectorCreateScalar, pArg, argLReg); - Operation copyH = Operation(Instruction.VectorInsert, pArg, pArg, argHReg, Const(1)); + Operation copyH = Operation(Instruction.VectorInsert, pArg, pArg, argHReg, Const(1)); cctx.Cfg.Entry.Operations.AddFirst(copyH); cctx.Cfg.Entry.Operations.AddFirst(copyL); @@ -313,7 +312,7 @@ namespace ARMeilleure.CodeGen.X86 if (source.Type == OperandType.V128) { - Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); + Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64); Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64); nodes.AddBefore(node, Operation(Instruction.VectorExtract, retLReg, source, Const(0))); @@ -331,4 +330,4 @@ namespace ARMeilleure.CodeGen.X86 } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs b/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs index 45319e6a5..10a2bd129 100644 --- a/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs +++ b/src/ARMeilleure/CodeGen/X86/PreAllocatorWindows.cs @@ -155,7 +155,7 @@ namespace ARMeilleure.CodeGen.X86 node.SetSources(sources); } - public static void InsertTailcallCopies(IntrusiveList<Operation> nodes, StackAllocator stackAlloc, Operation node) + public static void InsertTailcallCopies(IntrusiveList<Operation> nodes, Operation node) { int argsCount = node.SourcesCount - 1; int maxArgs = CallingConvention.GetArgumentsOnRegsCount(); @@ -324,4 +324,4 @@ namespace ARMeilleure.CodeGen.X86 node.SetSources(Array.Empty<Operand>()); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/X86Condition.cs b/src/ARMeilleure/CodeGen/X86/X86Condition.cs index c82cbdec5..70699a207 100644 --- a/src/ARMeilleure/CodeGen/X86/X86Condition.cs +++ b/src/ARMeilleure/CodeGen/X86/X86Condition.cs @@ -5,22 +5,22 @@ namespace ARMeilleure.CodeGen.X86 { enum X86Condition { - Overflow = 0x0, - NotOverflow = 0x1, - Below = 0x2, - AboveOrEqual = 0x3, - Equal = 0x4, - NotEqual = 0x5, - BelowOrEqual = 0x6, - Above = 0x7, - Sign = 0x8, - NotSign = 0x9, - ParityEven = 0xa, - ParityOdd = 0xb, - Less = 0xc, + Overflow = 0x0, + NotOverflow = 0x1, + Below = 0x2, + AboveOrEqual = 0x3, + Equal = 0x4, + NotEqual = 0x5, + BelowOrEqual = 0x6, + Above = 0x7, + Sign = 0x8, + NotSign = 0x9, + ParityEven = 0xa, + ParityOdd = 0xb, + Less = 0xc, GreaterOrEqual = 0xd, - LessOrEqual = 0xe, - Greater = 0xf + LessOrEqual = 0xe, + Greater = 0xf, } static class ComparisonX86Extensions @@ -29,6 +29,7 @@ namespace ARMeilleure.CodeGen.X86 { return comp switch { +#pragma warning disable IDE0055 // Disable formatting Comparison.Equal => X86Condition.Equal, Comparison.NotEqual => X86Condition.NotEqual, Comparison.Greater => X86Condition.Greater, @@ -39,9 +40,10 @@ namespace ARMeilleure.CodeGen.X86 Comparison.Less => X86Condition.Less, Comparison.GreaterOrEqualUI => X86Condition.AboveOrEqual, Comparison.LessUI => X86Condition.Below, +#pragma warning restore IDE0055 - _ => throw new ArgumentException(null, nameof(comp)) + _ => throw new ArgumentException(null, nameof(comp)), }; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/X86Instruction.cs b/src/ARMeilleure/CodeGen/X86/X86Instruction.cs index 9a85c516f..e1979011d 100644 --- a/src/ARMeilleure/CodeGen/X86/X86Instruction.cs +++ b/src/ARMeilleure/CodeGen/X86/X86Instruction.cs @@ -226,6 +226,6 @@ namespace ARMeilleure.CodeGen.X86 Xorpd, Xorps, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/CodeGen/X86/X86Optimizer.cs b/src/ARMeilleure/CodeGen/X86/X86Optimizer.cs index 98a19b9a2..9d23f9ada 100644 --- a/src/ARMeilleure/CodeGen/X86/X86Optimizer.cs +++ b/src/ARMeilleure/CodeGen/X86/X86Optimizer.cs @@ -215,7 +215,7 @@ namespace ARMeilleure.CodeGen.X86 1 => Multiplier.x2, 2 => Multiplier.x4, 3 => Multiplier.x8, - _ => Multiplier.x1 + _ => Multiplier.x1, }; baseOp = indexOnSrc2 ? src1 : src2; diff --git a/src/ARMeilleure/CodeGen/X86/X86Register.cs b/src/ARMeilleure/CodeGen/X86/X86Register.cs index 01f63e311..0a6563663 100644 --- a/src/ARMeilleure/CodeGen/X86/X86Register.cs +++ b/src/ARMeilleure/CodeGen/X86/X86Register.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace ARMeilleure.CodeGen.X86 { + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum X86Register { Invalid = -1, @@ -12,8 +15,8 @@ namespace ARMeilleure.CodeGen.X86 Rbp = 5, Rsi = 6, Rdi = 7, - R8 = 8, - R9 = 9, + R8 = 8, + R9 = 9, R10 = 10, R11 = 11, R12 = 12, @@ -21,21 +24,21 @@ namespace ARMeilleure.CodeGen.X86 R14 = 14, R15 = 15, - Xmm0 = 0, - Xmm1 = 1, - Xmm2 = 2, - Xmm3 = 3, - Xmm4 = 4, - Xmm5 = 5, - Xmm6 = 6, - Xmm7 = 7, - Xmm8 = 8, - Xmm9 = 9, + Xmm0 = 0, + Xmm1 = 1, + Xmm2 = 2, + Xmm3 = 3, + Xmm4 = 4, + Xmm5 = 5, + Xmm6 = 6, + Xmm7 = 7, + Xmm8 = 8, + Xmm9 = 9, Xmm10 = 10, Xmm11 = 11, Xmm12 = 12, Xmm13 = 13, Xmm14 = 14, - Xmm15 = 15 + Xmm15 = 15, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Common/ArenaAllocator.cs b/src/ARMeilleure/Common/ArenaAllocator.cs index bce6794ad..f810c2abd 100644 --- a/src/ARMeilleure/Common/ArenaAllocator.cs +++ b/src/ARMeilleure/Common/ArenaAllocator.cs @@ -82,8 +82,10 @@ namespace ARMeilleure.Common } else { - _page = new PageInfo(); - _page.Pointer = (byte*)NativeAllocator.Instance.Allocate(_pageSize); + _page = new PageInfo + { + Pointer = (byte*)NativeAllocator.Instance.Allocate(_pageSize), + }; _pages.Add(_page); } @@ -106,7 +108,7 @@ namespace ARMeilleure.Common // Free excess pages that was allocated. while (_pages.Count > _pageCount) { - NativeAllocator.Instance.Free(_pages[_pages.Count - 1].Pointer); + NativeAllocator.Instance.Free(_pages[^1].Pointer); _pages.RemoveAt(_pages.Count - 1); } @@ -125,12 +127,13 @@ namespace ARMeilleure.Common // If arena is used frequently, keep pages for longer. Otherwise keep pages for a shorter amount of time. int now = Environment.TickCount; - int count = (now - _lastReset) switch { + int count = (now - _lastReset) switch + { >= 5000 => 0, >= 2500 => 50, >= 1000 => 100, - >= 10 => 1500, - _ => 5000 + >= 10 => 1500, + _ => 5000, }; for (int i = _pages.Count - 1; i >= 0; i--) diff --git a/src/ARMeilleure/Common/BitMap.cs b/src/ARMeilleure/Common/BitMap.cs index 27ef031f3..94d47ea59 100644 --- a/src/ARMeilleure/Common/BitMap.cs +++ b/src/ARMeilleure/Common/BitMap.cs @@ -138,7 +138,7 @@ namespace ARMeilleure.Common var newSpan = new Span<long>(_masks, _count); oldSpan.CopyTo(newSpan); - newSpan.Slice(oldSpan.Length).Clear(); + newSpan[oldSpan.Length..].Clear(); _allocator.Free(oldMask); } @@ -176,8 +176,8 @@ namespace ARMeilleure.Common private int _bit; private readonly BitMap _map; - public int Current => (int)_index * IntSize + _bit; - object IEnumerator.Current => Current; + public readonly int Current => (int)_index * IntSize + _bit; + readonly object IEnumerator.Current => Current; public Enumerator(BitMap map) { @@ -214,9 +214,9 @@ namespace ARMeilleure.Common return true; } - public void Reset() { } + public readonly void Reset() { } - public void Dispose() { } + public readonly void Dispose() { } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/Block.cs b/src/ARMeilleure/Decoders/Block.cs index f296d299d..bb88170da 100644 --- a/src/ARMeilleure/Decoders/Block.cs +++ b/src/ARMeilleure/Decoders/Block.cs @@ -5,10 +5,10 @@ namespace ARMeilleure.Decoders { class Block { - public ulong Address { get; set; } + public ulong Address { get; set; } public ulong EndAddress { get; set; } - public Block Next { get; set; } + public Block Next { get; set; } public Block Branch { get; set; } public bool Exit { get; set; } @@ -43,14 +43,14 @@ namespace ARMeilleure.Decoders rightBlock.EndAddress = EndAddress; - rightBlock.Next = Next; + rightBlock.Next = Next; rightBlock.Branch = Branch; rightBlock.OpCodes.AddRange(OpCodes.GetRange(splitIndex, splitCount)); EndAddress = rightBlock.Address; - Next = rightBlock; + Next = rightBlock; Branch = null; OpCodes.RemoveRange(splitIndex, splitCount); @@ -58,9 +58,9 @@ namespace ARMeilleure.Decoders private static int BinarySearch(List<OpCode> opCodes, ulong address) { - int left = 0; + int left = 0; int middle = 0; - int right = opCodes.Count - 1; + int right = opCodes.Count - 1; while (left <= right) { @@ -92,10 +92,10 @@ namespace ARMeilleure.Decoders { if (OpCodes.Count > 0) { - return OpCodes[OpCodes.Count - 1]; + return OpCodes[^1]; } return null; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/Condition.cs b/src/ARMeilleure/Decoders/Condition.cs index 727f897da..961825a10 100644 --- a/src/ARMeilleure/Decoders/Condition.cs +++ b/src/ARMeilleure/Decoders/Condition.cs @@ -2,22 +2,22 @@ namespace ARMeilleure.Decoders { enum Condition { - Eq = 0, - Ne = 1, + Eq = 0, + Ne = 1, GeUn = 2, LtUn = 3, - Mi = 4, - Pl = 5, - Vs = 6, - Vc = 7, + Mi = 4, + Pl = 5, + Vs = 6, + Vc = 7, GtUn = 8, LeUn = 9, - Ge = 10, - Lt = 11, - Gt = 12, - Le = 13, - Al = 14, - Nv = 15 + Ge = 10, + Lt = 11, + Gt = 12, + Le = 13, + Al = 14, + Nv = 15, } static class ConditionExtensions @@ -29,4 +29,4 @@ namespace ARMeilleure.Decoders return (Condition)((int)cond ^ 1); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/DataOp.cs b/src/ARMeilleure/Decoders/DataOp.cs index 464d00898..f99fd5e70 100644 --- a/src/ARMeilleure/Decoders/DataOp.cs +++ b/src/ARMeilleure/Decoders/DataOp.cs @@ -2,9 +2,9 @@ namespace ARMeilleure.Decoders { enum DataOp { - Adr = 0, + Adr = 0, Arithmetic = 1, - Logical = 2, - BitField = 3 + Logical = 2, + BitField = 3, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/Decoder.cs b/src/ARMeilleure/Decoders/Decoder.cs index 426465aaa..d8abeb9c8 100644 --- a/src/ARMeilleure/Decoders/Decoder.cs +++ b/src/ARMeilleure/Decoders/Decoder.cs @@ -20,11 +20,11 @@ namespace ARMeilleure.Decoders public static Block[] Decode(IMemoryManager memory, ulong address, ExecutionMode mode, bool highCq, DecoderMode dMode) { - List<Block> blocks = new List<Block>(); + List<Block> blocks = new(); - Queue<Block> workQueue = new Queue<Block>(); + Queue<Block> workQueue = new(); - Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>(); + Dictionary<ulong, Block> visited = new(); Debug.Assert(MaxInstsPerFunctionLowCq <= MaxInstsPerFunction); @@ -163,7 +163,7 @@ namespace ARMeilleure.Decoders { index = 0; - int left = 0; + int left = 0; int right = blocks.Count - 1; while (left <= right) @@ -196,9 +196,9 @@ namespace ARMeilleure.Decoders private static void FillBlock( IMemoryManager memory, - ExecutionMode mode, - Block block, - ulong limitAddress) + ExecutionMode mode, + Block block, + ulong limitAddress) { ulong address = block.Address; int itBlockSize = 0; @@ -241,12 +241,12 @@ namespace ARMeilleure.Decoders private static bool IsUnconditionalBranch(OpCode opCode) { return opCode is OpCodeBImmAl || - opCode is OpCodeBReg || IsAarch32UnconditionalBranch(opCode); + opCode is OpCodeBReg || IsAarch32UnconditionalBranch(opCode); } private static bool IsAarch32UnconditionalBranch(OpCode opCode) { - if (!(opCode is OpCode32 op)) + if (opCode is not OpCode32 op) { return false; } @@ -290,9 +290,9 @@ namespace ARMeilleure.Decoders if (opCode is IOpCode32Mem opMem) { - rt = opMem.Rt; - rn = opMem.Rn; - wBack = opMem.WBack; + rt = opMem.Rt; + rn = opMem.Rn; + wBack = opMem.WBack; isLoad = opMem.IsLoad; // For the dual load, we also need to take into account the @@ -306,10 +306,10 @@ namespace ARMeilleure.Decoders { const int pcMask = 1 << RegisterAlias.Aarch32Pc; - rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0; - rn = opMemMult.Rn; - wBack = opMemMult.PostOffset != 0; - isLoad = opMemMult.IsLoad; + rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0; + rn = opMemMult.Rn; + wBack = opMemMult.PostOffset != 0; + isLoad = opMemMult.IsLoad; } else { @@ -388,4 +388,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/DecoderHelper.cs b/src/ARMeilleure/Decoders/DecoderHelper.cs index 5227e6a19..35e573955 100644 --- a/src/ARMeilleure/Decoders/DecoderHelper.cs +++ b/src/ARMeilleure/Decoders/DecoderHelper.cs @@ -10,7 +10,7 @@ namespace ARMeilleure.Decoders Imm8ToFP64Table = BuildImm8ToFP64Table(); } - public static readonly uint[] Imm8ToFP32Table; + public static readonly uint[] Imm8ToFP32Table; public static readonly ulong[] Imm8ToFP64Table; private static uint[] BuildImm8ToFP32Table() @@ -40,47 +40,47 @@ namespace ARMeilleure.Decoders // abcdefgh -> aBbbbbbc defgh000 00000000 00000000 (B = ~b) private static uint ExpandImm8ToFP32(uint imm) { - uint MoveBit(uint bits, int from, int to) + static uint MoveBit(uint bits, int from, int to) { return ((bits >> from) & 1U) << to; } return MoveBit(imm, 7, 31) | MoveBit(~imm, 6, 30) | - MoveBit(imm, 6, 29) | MoveBit( imm, 6, 28) | - MoveBit(imm, 6, 27) | MoveBit( imm, 6, 26) | - MoveBit(imm, 6, 25) | MoveBit( imm, 5, 24) | - MoveBit(imm, 4, 23) | MoveBit( imm, 3, 22) | - MoveBit(imm, 2, 21) | MoveBit( imm, 1, 20) | + MoveBit(imm, 6, 29) | MoveBit(imm, 6, 28) | + MoveBit(imm, 6, 27) | MoveBit(imm, 6, 26) | + MoveBit(imm, 6, 25) | MoveBit(imm, 5, 24) | + MoveBit(imm, 4, 23) | MoveBit(imm, 3, 22) | + MoveBit(imm, 2, 21) | MoveBit(imm, 1, 20) | MoveBit(imm, 0, 19); } // abcdefgh -> aBbbbbbb bbcdefgh 00000000 00000000 00000000 00000000 00000000 00000000 (B = ~b) private static ulong ExpandImm8ToFP64(ulong imm) { - ulong MoveBit(ulong bits, int from, int to) + static ulong MoveBit(ulong bits, int from, int to) { return ((bits >> from) & 1UL) << to; } return MoveBit(imm, 7, 63) | MoveBit(~imm, 6, 62) | - MoveBit(imm, 6, 61) | MoveBit( imm, 6, 60) | - MoveBit(imm, 6, 59) | MoveBit( imm, 6, 58) | - MoveBit(imm, 6, 57) | MoveBit( imm, 6, 56) | - MoveBit(imm, 6, 55) | MoveBit( imm, 6, 54) | - MoveBit(imm, 5, 53) | MoveBit( imm, 4, 52) | - MoveBit(imm, 3, 51) | MoveBit( imm, 2, 50) | - MoveBit(imm, 1, 49) | MoveBit( imm, 0, 48); + MoveBit(imm, 6, 61) | MoveBit(imm, 6, 60) | + MoveBit(imm, 6, 59) | MoveBit(imm, 6, 58) | + MoveBit(imm, 6, 57) | MoveBit(imm, 6, 56) | + MoveBit(imm, 6, 55) | MoveBit(imm, 6, 54) | + MoveBit(imm, 5, 53) | MoveBit(imm, 4, 52) | + MoveBit(imm, 3, 51) | MoveBit(imm, 2, 50) | + MoveBit(imm, 1, 49) | MoveBit(imm, 0, 48); } public struct BitMask { public long WMask; public long TMask; - public int Pos; - public int Shift; + public int Pos; + public int Shift; public bool IsUndefined; - public static BitMask Invalid => new BitMask { IsUndefined = true }; + public static BitMask Invalid => new() { IsUndefined = true }; } public static BitMask DecodeBitMask(int opCode, bool immediate) @@ -88,7 +88,7 @@ namespace ARMeilleure.Decoders int immS = (opCode >> 10) & 0x3f; int immR = (opCode >> 16) & 0x3f; - int n = (opCode >> 22) & 1; + int n = (opCode >> 22) & 1; int sf = (opCode >> 31) & 1; int length = BitUtils.HighestBitSet((~immS & 0x3f) | (n << 6)); @@ -115,7 +115,7 @@ namespace ARMeilleure.Decoders if (r > 0) { - wMask = BitUtils.RotateRight(wMask, r, size); + wMask = BitUtils.RotateRight(wMask, r, size); wMask &= BitUtils.FillWithOnes(size); } @@ -124,8 +124,8 @@ namespace ARMeilleure.Decoders WMask = BitUtils.Replicate(wMask, size), TMask = BitUtils.Replicate(tMask, size), - Pos = immS, - Shift = immR + Pos = immS, + Shift = immR, }; } @@ -164,4 +164,4 @@ namespace ARMeilleure.Decoders return false; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/DecoderMode.cs b/src/ARMeilleure/Decoders/DecoderMode.cs index 553620847..280ebb649 100644 --- a/src/ARMeilleure/Decoders/DecoderMode.cs +++ b/src/ARMeilleure/Decoders/DecoderMode.cs @@ -6,4 +6,4 @@ SingleBlock, SingleInstruction, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode.cs b/src/ARMeilleure/Decoders/IOpCode.cs index 37ba7a4c6..9d5e3bf7a 100644 --- a/src/ARMeilleure/Decoders/IOpCode.cs +++ b/src/ARMeilleure/Decoders/IOpCode.cs @@ -14,4 +14,4 @@ namespace ARMeilleure.Decoders OperandType GetOperandType(); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32.cs b/src/ARMeilleure/Decoders/IOpCode32.cs index 126c10690..578925dee 100644 --- a/src/ARMeilleure/Decoders/IOpCode32.cs +++ b/src/ARMeilleure/Decoders/IOpCode32.cs @@ -6,4 +6,4 @@ namespace ARMeilleure.Decoders uint GetPc(); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32Alu.cs b/src/ARMeilleure/Decoders/IOpCode32Alu.cs index 69fee164c..a85ef44ad 100644 --- a/src/ARMeilleure/Decoders/IOpCode32Alu.cs +++ b/src/ARMeilleure/Decoders/IOpCode32Alu.cs @@ -5,4 +5,4 @@ namespace ARMeilleure.Decoders int Rd { get; } int Rn { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32AluImm.cs b/src/ARMeilleure/Decoders/IOpCode32AluImm.cs index 342fb8f6c..9d49a440d 100644 --- a/src/ARMeilleure/Decoders/IOpCode32AluImm.cs +++ b/src/ARMeilleure/Decoders/IOpCode32AluImm.cs @@ -6,4 +6,4 @@ bool IsRotated { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32AluImm16.cs b/src/ARMeilleure/Decoders/IOpCode32AluImm16.cs index cd128f657..dd42a70b1 100644 --- a/src/ARMeilleure/Decoders/IOpCode32AluImm16.cs +++ b/src/ARMeilleure/Decoders/IOpCode32AluImm16.cs @@ -4,4 +4,4 @@ namespace ARMeilleure.Decoders { int Immediate { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32AluRsImm.cs b/src/ARMeilleure/Decoders/IOpCode32AluRsImm.cs index e899a6592..8b976b58f 100644 --- a/src/ARMeilleure/Decoders/IOpCode32AluRsImm.cs +++ b/src/ARMeilleure/Decoders/IOpCode32AluRsImm.cs @@ -7,4 +7,4 @@ ShiftType ShiftType { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32AluRsReg.cs b/src/ARMeilleure/Decoders/IOpCode32AluRsReg.cs index 879db0593..e8c33c2b1 100644 --- a/src/ARMeilleure/Decoders/IOpCode32AluRsReg.cs +++ b/src/ARMeilleure/Decoders/IOpCode32AluRsReg.cs @@ -7,4 +7,4 @@ ShiftType ShiftType { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32BImm.cs b/src/ARMeilleure/Decoders/IOpCode32BImm.cs index ec7db2c26..8d22d5c40 100644 --- a/src/ARMeilleure/Decoders/IOpCode32BImm.cs +++ b/src/ARMeilleure/Decoders/IOpCode32BImm.cs @@ -1,4 +1,4 @@ namespace ARMeilleure.Decoders { interface IOpCode32BImm : IOpCode32, IOpCodeBImm { } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32BReg.cs b/src/ARMeilleure/Decoders/IOpCode32BReg.cs index 097ab4275..9badc9858 100644 --- a/src/ARMeilleure/Decoders/IOpCode32BReg.cs +++ b/src/ARMeilleure/Decoders/IOpCode32BReg.cs @@ -4,4 +4,4 @@ namespace ARMeilleure.Decoders { int Rm { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32Exception.cs b/src/ARMeilleure/Decoders/IOpCode32Exception.cs index 8f0fb81a0..4c1fc231c 100644 --- a/src/ARMeilleure/Decoders/IOpCode32Exception.cs +++ b/src/ARMeilleure/Decoders/IOpCode32Exception.cs @@ -4,4 +4,4 @@ { int Id { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32HasSetFlags.cs b/src/ARMeilleure/Decoders/IOpCode32HasSetFlags.cs index 71ca6d19c..772e1080f 100644 --- a/src/ARMeilleure/Decoders/IOpCode32HasSetFlags.cs +++ b/src/ARMeilleure/Decoders/IOpCode32HasSetFlags.cs @@ -4,4 +4,4 @@ { bool? SetFlags { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32Mem.cs b/src/ARMeilleure/Decoders/IOpCode32Mem.cs index 6664ddffd..a34bc0e2a 100644 --- a/src/ARMeilleure/Decoders/IOpCode32Mem.cs +++ b/src/ARMeilleure/Decoders/IOpCode32Mem.cs @@ -13,4 +13,4 @@ namespace ARMeilleure.Decoders int Immediate { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32MemMult.cs b/src/ARMeilleure/Decoders/IOpCode32MemMult.cs index 4b891bc1b..0c5e48f22 100644 --- a/src/ARMeilleure/Decoders/IOpCode32MemMult.cs +++ b/src/ARMeilleure/Decoders/IOpCode32MemMult.cs @@ -12,4 +12,4 @@ namespace ARMeilleure.Decoders int Offset { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32MemReg.cs b/src/ARMeilleure/Decoders/IOpCode32MemReg.cs index 7fe1b0229..f356e4d7c 100644 --- a/src/ARMeilleure/Decoders/IOpCode32MemReg.cs +++ b/src/ARMeilleure/Decoders/IOpCode32MemReg.cs @@ -4,4 +4,4 @@ { int Rm { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCode32MemRsImm.cs b/src/ARMeilleure/Decoders/IOpCode32MemRsImm.cs index 65b7ee0b4..3407e98ac 100644 --- a/src/ARMeilleure/Decoders/IOpCode32MemRsImm.cs +++ b/src/ARMeilleure/Decoders/IOpCode32MemRsImm.cs @@ -5,4 +5,4 @@ namespace ARMeilleure.Decoders int Rm { get; } ShiftType ShiftType { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeAlu.cs b/src/ARMeilleure/Decoders/IOpCodeAlu.cs index b8c28513d..059769ba9 100644 --- a/src/ARMeilleure/Decoders/IOpCodeAlu.cs +++ b/src/ARMeilleure/Decoders/IOpCodeAlu.cs @@ -7,4 +7,4 @@ namespace ARMeilleure.Decoders DataOp DataOp { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeAluImm.cs b/src/ARMeilleure/Decoders/IOpCodeAluImm.cs index 02f4c997b..40a69cc90 100644 --- a/src/ARMeilleure/Decoders/IOpCodeAluImm.cs +++ b/src/ARMeilleure/Decoders/IOpCodeAluImm.cs @@ -4,4 +4,4 @@ namespace ARMeilleure.Decoders { long Immediate { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeAluRs.cs b/src/ARMeilleure/Decoders/IOpCodeAluRs.cs index 22540b11a..eec956982 100644 --- a/src/ARMeilleure/Decoders/IOpCodeAluRs.cs +++ b/src/ARMeilleure/Decoders/IOpCodeAluRs.cs @@ -3,8 +3,8 @@ namespace ARMeilleure.Decoders interface IOpCodeAluRs : IOpCodeAlu { int Shift { get; } - int Rm { get; } + int Rm { get; } ShiftType ShiftType { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeAluRx.cs b/src/ARMeilleure/Decoders/IOpCodeAluRx.cs index 9d16be787..e5a8559d8 100644 --- a/src/ARMeilleure/Decoders/IOpCodeAluRx.cs +++ b/src/ARMeilleure/Decoders/IOpCodeAluRx.cs @@ -3,8 +3,8 @@ namespace ARMeilleure.Decoders interface IOpCodeAluRx : IOpCodeAlu { int Shift { get; } - int Rm { get; } + int Rm { get; } IntType IntType { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeBImm.cs b/src/ARMeilleure/Decoders/IOpCodeBImm.cs index 958bff28d..9ce7512a1 100644 --- a/src/ARMeilleure/Decoders/IOpCodeBImm.cs +++ b/src/ARMeilleure/Decoders/IOpCodeBImm.cs @@ -4,4 +4,4 @@ namespace ARMeilleure.Decoders { long Immediate { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeCond.cs b/src/ARMeilleure/Decoders/IOpCodeCond.cs index 9808f7c08..6604f19a2 100644 --- a/src/ARMeilleure/Decoders/IOpCodeCond.cs +++ b/src/ARMeilleure/Decoders/IOpCodeCond.cs @@ -4,4 +4,4 @@ namespace ARMeilleure.Decoders { Condition Cond { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeLit.cs b/src/ARMeilleure/Decoders/IOpCodeLit.cs index 74084a457..434e4da88 100644 --- a/src/ARMeilleure/Decoders/IOpCodeLit.cs +++ b/src/ARMeilleure/Decoders/IOpCodeLit.cs @@ -2,10 +2,10 @@ namespace ARMeilleure.Decoders { interface IOpCodeLit : IOpCode { - int Rt { get; } + int Rt { get; } long Immediate { get; } - int Size { get; } - bool Signed { get; } - bool Prefetch { get; } + int Size { get; } + bool Signed { get; } + bool Prefetch { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IOpCodeSimd.cs b/src/ARMeilleure/Decoders/IOpCodeSimd.cs index 056ef045c..598d9d7f8 100644 --- a/src/ARMeilleure/Decoders/IOpCodeSimd.cs +++ b/src/ARMeilleure/Decoders/IOpCodeSimd.cs @@ -4,4 +4,4 @@ namespace ARMeilleure.Decoders { int Size { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/InstDescriptor.cs b/src/ARMeilleure/Decoders/InstDescriptor.cs index 577ff3946..c35c754a9 100644 --- a/src/ARMeilleure/Decoders/InstDescriptor.cs +++ b/src/ARMeilleure/Decoders/InstDescriptor.cs @@ -4,15 +4,15 @@ namespace ARMeilleure.Decoders { readonly struct InstDescriptor { - public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und); + public static InstDescriptor Undefined => new(InstName.Und, InstEmit.Und); - public InstName Name { get; } + public InstName Name { get; } public InstEmitter Emitter { get; } public InstDescriptor(InstName name, InstEmitter emitter) { - Name = name; + Name = name; Emitter = emitter; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/InstEmitter.cs b/src/ARMeilleure/Decoders/InstEmitter.cs index a8b526569..43bfcdca3 100644 --- a/src/ARMeilleure/Decoders/InstEmitter.cs +++ b/src/ARMeilleure/Decoders/InstEmitter.cs @@ -3,4 +3,4 @@ using ARMeilleure.Translation; namespace ARMeilleure.Decoders { delegate void InstEmitter(ArmEmitterContext context); -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/IntType.cs b/src/ARMeilleure/Decoders/IntType.cs index 244e96805..937a569aa 100644 --- a/src/ARMeilleure/Decoders/IntType.cs +++ b/src/ARMeilleure/Decoders/IntType.cs @@ -2,13 +2,13 @@ namespace ARMeilleure.Decoders { enum IntType { - UInt8 = 0, + UInt8 = 0, UInt16 = 1, UInt32 = 2, UInt64 = 3, - Int8 = 4, - Int16 = 5, - Int32 = 6, - Int64 = 7 + Int8 = 4, + Int16 = 5, + Int32 = 6, + Int64 = 7, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode.cs b/src/ARMeilleure/Decoders/OpCode.cs index f9aed7924..c8123308b 100644 --- a/src/ARMeilleure/Decoders/OpCode.cs +++ b/src/ARMeilleure/Decoders/OpCode.cs @@ -5,8 +5,8 @@ namespace ARMeilleure.Decoders { class OpCode : IOpCode { - public ulong Address { get; } - public int RawOpCode { get; } + public ulong Address { get; } + public int RawOpCode { get; } public int OpCodeSizeInBytes { get; protected set; } = 4; @@ -14,13 +14,13 @@ namespace ARMeilleure.Decoders public RegisterSize RegisterSize { get; protected set; } - public static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode(inst, address, opCode); + public static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new(inst, address, opCode); public OpCode(InstDescriptor inst, ulong address, int opCode) { Instruction = inst; - Address = address; - RawOpCode = opCode; + Address = address; + RawOpCode = opCode; RegisterSize = RegisterSize.Int64; } @@ -30,15 +30,14 @@ namespace ARMeilleure.Decoders public int GetBitsCount() { - switch (RegisterSize) + return RegisterSize switch { - case RegisterSize.Int32: return 32; - case RegisterSize.Int64: return 64; - case RegisterSize.Simd64: return 64; - case RegisterSize.Simd128: return 128; - } - - throw new InvalidOperationException(); + RegisterSize.Int32 => 32, + RegisterSize.Int64 => 64, + RegisterSize.Simd64 => 64, + RegisterSize.Simd128 => 128, + _ => throw new InvalidOperationException(), + }; } public OperandType GetOperandType() @@ -46,4 +45,4 @@ namespace ARMeilleure.Decoders return RegisterSize == RegisterSize.Int32 ? OperandType.I32 : OperandType.I64; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32.cs b/src/ARMeilleure/Decoders/OpCode32.cs index c2f14145b..a2be01e9a 100644 --- a/src/ARMeilleure/Decoders/OpCode32.cs +++ b/src/ARMeilleure/Decoders/OpCode32.cs @@ -31,4 +31,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32Alu.cs b/src/ARMeilleure/Decoders/OpCode32Alu.cs index 1625aee0a..8634f5ce9 100644 --- a/src/ARMeilleure/Decoders/OpCode32Alu.cs +++ b/src/ARMeilleure/Decoders/OpCode32Alu.cs @@ -17,4 +17,4 @@ namespace ARMeilleure.Decoders SetFlags = ((opCode >> 20) & 1) != 0; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32AluImm.cs b/src/ARMeilleure/Decoders/OpCode32AluImm.cs index b5435aaf1..c8b05e6bc 100644 --- a/src/ARMeilleure/Decoders/OpCode32AluImm.cs +++ b/src/ARMeilleure/Decoders/OpCode32AluImm.cs @@ -20,4 +20,4 @@ namespace ARMeilleure.Decoders IsRotated = shift != 0; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32AluRsImm.cs b/src/ARMeilleure/Decoders/OpCode32AluRsImm.cs index c2dee6c9a..4b2c5897a 100644 --- a/src/ARMeilleure/Decoders/OpCode32AluRsImm.cs +++ b/src/ARMeilleure/Decoders/OpCode32AluRsImm.cs @@ -2,7 +2,7 @@ namespace ARMeilleure.Decoders { class OpCode32AluRsImm : OpCode32Alu, IOpCode32AluRsImm { - public int Rm { get; } + public int Rm { get; } public int Immediate { get; } public ShiftType ShiftType { get; } @@ -11,10 +11,10 @@ namespace ARMeilleure.Decoders public OpCode32AluRsImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rm = (opCode >> 0) & 0xf; + Rm = (opCode >> 0) & 0xf; Immediate = (opCode >> 7) & 0x1f; ShiftType = (ShiftType)((opCode >> 5) & 3); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32BImm.cs b/src/ARMeilleure/Decoders/OpCode32BImm.cs index f2959b331..e7f5d6db1 100644 --- a/src/ARMeilleure/Decoders/OpCode32BImm.cs +++ b/src/ARMeilleure/Decoders/OpCode32BImm.cs @@ -26,4 +26,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32BReg.cs b/src/ARMeilleure/Decoders/OpCode32BReg.cs index d4f5f7601..8939c0de3 100644 --- a/src/ARMeilleure/Decoders/OpCode32BReg.cs +++ b/src/ARMeilleure/Decoders/OpCode32BReg.cs @@ -11,4 +11,4 @@ namespace ARMeilleure.Decoders Rm = opCode & 0xf; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32Mem.cs b/src/ARMeilleure/Decoders/OpCode32Mem.cs index ceb1e49f5..8a2421996 100644 --- a/src/ARMeilleure/Decoders/OpCode32Mem.cs +++ b/src/ARMeilleure/Decoders/OpCode32Mem.cs @@ -9,9 +9,9 @@ namespace ARMeilleure.Decoders public int Immediate { get; protected set; } - public bool Index { get; } - public bool Add { get; } - public bool WBack { get; } + public bool Index { get; } + public bool Add { get; } + public bool WBack { get; } public bool Unprivileged { get; } public bool IsLoad { get; } @@ -24,16 +24,16 @@ namespace ARMeilleure.Decoders Rn = (opCode >> 16) & 0xf; bool isLoad = (opCode & (1 << 20)) != 0; - bool w = (opCode & (1 << 21)) != 0; - bool u = (opCode & (1 << 23)) != 0; - bool p = (opCode & (1 << 24)) != 0; + bool w = (opCode & (1 << 21)) != 0; + bool u = (opCode & (1 << 23)) != 0; + bool p = (opCode & (1 << 24)) != 0; - Index = p; - Add = u; - WBack = !p || w; + Index = p; + Add = u; + WBack = !p || w; Unprivileged = !p && w; IsLoad = isLoad || inst.Name == InstName.Ldrd; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32MemImm.cs b/src/ARMeilleure/Decoders/OpCode32MemImm.cs index 3af4b6f7c..fa10e04ee 100644 --- a/src/ARMeilleure/Decoders/OpCode32MemImm.cs +++ b/src/ARMeilleure/Decoders/OpCode32MemImm.cs @@ -9,4 +9,4 @@ namespace ARMeilleure.Decoders Immediate = opCode & 0xfff; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32MemImm8.cs b/src/ARMeilleure/Decoders/OpCode32MemImm8.cs index 1b8a57de4..248ee8e65 100644 --- a/src/ARMeilleure/Decoders/OpCode32MemImm8.cs +++ b/src/ARMeilleure/Decoders/OpCode32MemImm8.cs @@ -12,4 +12,4 @@ namespace ARMeilleure.Decoders Immediate = imm4L | (imm4H << 4); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32MemMult.cs b/src/ARMeilleure/Decoders/OpCode32MemMult.cs index 522b96bb9..6e39e3479 100644 --- a/src/ARMeilleure/Decoders/OpCode32MemMult.cs +++ b/src/ARMeilleure/Decoders/OpCode32MemMult.cs @@ -7,8 +7,8 @@ namespace ARMeilleure.Decoders public int Rn { get; } public int RegisterMask { get; } - public int Offset { get; } - public int PostOffset { get; } + public int Offset { get; } + public int PostOffset { get; } public bool IsLoad { get; } @@ -19,9 +19,9 @@ namespace ARMeilleure.Decoders Rn = (opCode >> 16) & 0xf; bool isLoad = (opCode & (1 << 20)) != 0; - bool w = (opCode & (1 << 21)) != 0; - bool u = (opCode & (1 << 23)) != 0; - bool p = (opCode & (1 << 24)) != 0; + bool w = (opCode & (1 << 21)) != 0; + bool u = (opCode & (1 << 23)) != 0; + bool p = (opCode & (1 << 24)) != 0; RegisterMask = opCode & 0xffff; @@ -49,4 +49,4 @@ namespace ARMeilleure.Decoders IsLoad = isLoad; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32Mrs.cs b/src/ARMeilleure/Decoders/OpCode32Mrs.cs index c34a8b997..b681b54c0 100644 --- a/src/ARMeilleure/Decoders/OpCode32Mrs.cs +++ b/src/ARMeilleure/Decoders/OpCode32Mrs.cs @@ -2,8 +2,8 @@ namespace ARMeilleure.Decoders { class OpCode32Mrs : OpCode32 { - public bool R { get; } - public int Rd { get; } + public bool R { get; } + public int Rd { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32Mrs(inst, address, opCode); diff --git a/src/ARMeilleure/Decoders/OpCode32MsrReg.cs b/src/ARMeilleure/Decoders/OpCode32MsrReg.cs index d897ffd80..7186ebf74 100644 --- a/src/ARMeilleure/Decoders/OpCode32MsrReg.cs +++ b/src/ARMeilleure/Decoders/OpCode32MsrReg.cs @@ -4,11 +4,11 @@ namespace ARMeilleure.Decoders { class OpCode32MsrReg : OpCode32 { - public bool R { get; } - public int Mask { get; } - public int Rd { get; } + public bool R { get; } + public int Mask { get; } + public int Rd { get; } public bool Banked { get; } - public int Rn { get; } + public int Rn { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32MsrReg(inst, address, opCode); diff --git a/src/ARMeilleure/Decoders/OpCode32Sat.cs b/src/ARMeilleure/Decoders/OpCode32Sat.cs index 621def27c..35c5cf47a 100644 --- a/src/ARMeilleure/Decoders/OpCode32Sat.cs +++ b/src/ARMeilleure/Decoders/OpCode32Sat.cs @@ -21,4 +21,4 @@ namespace ARMeilleure.Decoders ShiftType = (ShiftType)((opCode >> 5) & 2); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32Sat16.cs b/src/ARMeilleure/Decoders/OpCode32Sat16.cs index 51061b079..01f4d3b23 100644 --- a/src/ARMeilleure/Decoders/OpCode32Sat16.cs +++ b/src/ARMeilleure/Decoders/OpCode32Sat16.cs @@ -15,4 +15,4 @@ namespace ARMeilleure.Decoders SatImm = (opCode >> 16) & 0xf; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32SimdBase.cs b/src/ARMeilleure/Decoders/OpCode32SimdBase.cs index 4382fc2aa..2361ac1eb 100644 --- a/src/ARMeilleure/Decoders/OpCode32SimdBase.cs +++ b/src/ARMeilleure/Decoders/OpCode32SimdBase.cs @@ -24,27 +24,21 @@ namespace ARMeilleure.Decoders protected int GetQuadwordIndex(int index) { - switch (RegisterSize) + return RegisterSize switch { - case RegisterSize.Simd128: - case RegisterSize.Simd64: - return index >> 1; - } - - throw new InvalidOperationException(); + RegisterSize.Simd128 or RegisterSize.Simd64 => index >> 1, + _ => throw new InvalidOperationException(), + }; } protected int GetQuadwordSubindex(int index) { - switch (RegisterSize) + return RegisterSize switch { - case RegisterSize.Simd128: - return 0; - case RegisterSize.Simd64: - return index & 1; - } - - throw new InvalidOperationException(); + RegisterSize.Simd128 => 0, + RegisterSize.Simd64 => index & 1, + _ => throw new InvalidOperationException(), + }; } protected OpCode32SimdBase(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode) diff --git a/src/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs b/src/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs index a95b32ab0..d3beb4bfd 100644 --- a/src/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs +++ b/src/ARMeilleure/Decoders/OpCode32SimdCvtTB.cs @@ -15,8 +15,8 @@ namespace ARMeilleure.Decoders { IsThumb = isThumb; - Op = ((opCode >> 16) & 0x1) != 0; - T = ((opCode >> 7) & 0x1) != 0; + Op = ((opCode >> 16) & 0x1) != 0; + T = ((opCode >> 7) & 0x1) != 0; Size = ((opCode >> 8) & 0x1); RegisterSize = Size == 1 ? RegisterSize.Int64 : RegisterSize.Int32; @@ -41,4 +41,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCode32SimdLong.cs b/src/ARMeilleure/Decoders/OpCode32SimdLong.cs index 8d64d673a..558771a38 100644 --- a/src/ARMeilleure/Decoders/OpCode32SimdLong.cs +++ b/src/ARMeilleure/Decoders/OpCode32SimdLong.cs @@ -14,9 +14,15 @@ // The value must be a power of 2, otherwise it is the encoding of another instruction. switch (imm3h) { - case 1: Size = 0; break; - case 2: Size = 1; break; - case 4: Size = 2; break; + case 1: + Size = 0; + break; + case 2: + Size = 1; + break; + case 4: + Size = 2; + break; } U = ((opCode >> (isThumb ? 28 : 24)) & 0x1) != 0; diff --git a/src/ARMeilleure/Decoders/OpCode32SimdMemPair.cs b/src/ARMeilleure/Decoders/OpCode32SimdMemPair.cs index da88eed27..325be4ece 100644 --- a/src/ARMeilleure/Decoders/OpCode32SimdMemPair.cs +++ b/src/ARMeilleure/Decoders/OpCode32SimdMemPair.cs @@ -4,12 +4,12 @@ namespace ARMeilleure.Decoders { class OpCode32SimdMemPair : OpCode32, IOpCode32Simd { - private static int[] _regsMap = + private static readonly int[] _regsMap = { 1, 1, 4, 2, 1, 1, 3, 1, 1, 1, 2, 1, - 1, 1, 1, 1 + 1, 1, 1, 1, }; public int Vd { get; } diff --git a/src/ARMeilleure/Decoders/OpCode32SimdSel.cs b/src/ARMeilleure/Decoders/OpCode32SimdSel.cs index bd4865ea5..cb28418c3 100644 --- a/src/ARMeilleure/Decoders/OpCode32SimdSel.cs +++ b/src/ARMeilleure/Decoders/OpCode32SimdSel.cs @@ -18,6 +18,6 @@ Eq = 0, Vs, Ge, - Gt + Gt, } } diff --git a/src/ARMeilleure/Decoders/OpCodeAdr.cs b/src/ARMeilleure/Decoders/OpCodeAdr.cs index 9655c766c..080280404 100644 --- a/src/ARMeilleure/Decoders/OpCodeAdr.cs +++ b/src/ARMeilleure/Decoders/OpCodeAdr.cs @@ -6,14 +6,14 @@ namespace ARMeilleure.Decoders public long Immediate { get; } - public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeAdr(inst, address, opCode); + public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeAdr(inst, address, opCode); public OpCodeAdr(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { Rd = opCode & 0x1f; - Immediate = DecoderHelper.DecodeImmS19_2(opCode); + Immediate = DecoderHelper.DecodeImmS19_2(opCode); Immediate |= ((long)opCode >> 29) & 3; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeAlu.cs b/src/ARMeilleure/Decoders/OpCodeAlu.cs index 4d7f03a71..1619ecd8e 100644 --- a/src/ARMeilleure/Decoders/OpCodeAlu.cs +++ b/src/ARMeilleure/Decoders/OpCodeAlu.cs @@ -11,8 +11,8 @@ namespace ARMeilleure.Decoders public OpCodeAlu(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rd = (opCode >> 0) & 0x1f; - Rn = (opCode >> 5) & 0x1f; + Rd = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; DataOp = (DataOp)((opCode >> 24) & 0x3); RegisterSize = (opCode >> 31) != 0 @@ -20,4 +20,4 @@ namespace ARMeilleure.Decoders : RegisterSize.Int32; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeAluBinary.cs b/src/ARMeilleure/Decoders/OpCodeAluBinary.cs index e8b10656a..4413581ca 100644 --- a/src/ARMeilleure/Decoders/OpCodeAluBinary.cs +++ b/src/ARMeilleure/Decoders/OpCodeAluBinary.cs @@ -11,4 +11,4 @@ namespace ARMeilleure.Decoders Rm = (opCode >> 16) & 0x1f; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeAluImm.cs b/src/ARMeilleure/Decoders/OpCodeAluImm.cs index 91aa95531..0d2f7202f 100644 --- a/src/ARMeilleure/Decoders/OpCodeAluImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeAluImm.cs @@ -33,8 +33,8 @@ namespace ARMeilleure.Decoders } else { - throw new ArgumentException(nameof(opCode)); + throw new ArgumentException($"Invalid data operation: {DataOp}", nameof(opCode)); } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeAluRs.cs b/src/ARMeilleure/Decoders/OpCodeAluRs.cs index 949833363..47a47e7d0 100644 --- a/src/ARMeilleure/Decoders/OpCodeAluRs.cs +++ b/src/ARMeilleure/Decoders/OpCodeAluRs.cs @@ -3,7 +3,7 @@ namespace ARMeilleure.Decoders class OpCodeAluRs : OpCodeAlu, IOpCodeAluRs { public int Shift { get; } - public int Rm { get; } + public int Rm { get; } public ShiftType ShiftType { get; } @@ -22,8 +22,8 @@ namespace ARMeilleure.Decoders Shift = shift; - Rm = (opCode >> 16) & 0x1f; + Rm = (opCode >> 16) & 0x1f; ShiftType = (ShiftType)((opCode >> 22) & 0x3); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeAluRx.cs b/src/ARMeilleure/Decoders/OpCodeAluRx.cs index d39da9e74..c21486788 100644 --- a/src/ARMeilleure/Decoders/OpCodeAluRx.cs +++ b/src/ARMeilleure/Decoders/OpCodeAluRx.cs @@ -3,7 +3,7 @@ namespace ARMeilleure.Decoders class OpCodeAluRx : OpCodeAlu, IOpCodeAluRx { public int Shift { get; } - public int Rm { get; } + public int Rm { get; } public IntType IntType { get; } @@ -11,9 +11,9 @@ namespace ARMeilleure.Decoders public OpCodeAluRx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Shift = (opCode >> 10) & 0x7; + Shift = (opCode >> 10) & 0x7; IntType = (IntType)((opCode >> 13) & 0x7); - Rm = (opCode >> 16) & 0x1f; + Rm = (opCode >> 16) & 0x1f; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeBImm.cs b/src/ARMeilleure/Decoders/OpCodeBImm.cs index e302516e2..2848c1409 100644 --- a/src/ARMeilleure/Decoders/OpCodeBImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeBImm.cs @@ -8,4 +8,4 @@ namespace ARMeilleure.Decoders public OpCodeBImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeBImmAl.cs b/src/ARMeilleure/Decoders/OpCodeBImmAl.cs index 47ae5f562..6c4b28c6c 100644 --- a/src/ARMeilleure/Decoders/OpCodeBImmAl.cs +++ b/src/ARMeilleure/Decoders/OpCodeBImmAl.cs @@ -9,4 +9,4 @@ namespace ARMeilleure.Decoders Immediate = (long)address + DecoderHelper.DecodeImm26_2(opCode); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeBImmCmp.cs b/src/ARMeilleure/Decoders/OpCodeBImmCmp.cs index a52465699..c477ddecf 100644 --- a/src/ARMeilleure/Decoders/OpCodeBImmCmp.cs +++ b/src/ARMeilleure/Decoders/OpCodeBImmCmp.cs @@ -17,4 +17,4 @@ namespace ARMeilleure.Decoders : RegisterSize.Int32; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeBImmCond.cs b/src/ARMeilleure/Decoders/OpCodeBImmCond.cs index b57a7ea85..7a51a0720 100644 --- a/src/ARMeilleure/Decoders/OpCodeBImmCond.cs +++ b/src/ARMeilleure/Decoders/OpCodeBImmCond.cs @@ -22,4 +22,4 @@ namespace ARMeilleure.Decoders Immediate = (long)address + DecoderHelper.DecodeImmS19_2(opCode); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeBImmTest.cs b/src/ARMeilleure/Decoders/OpCodeBImmTest.cs index bad984055..f989e59e4 100644 --- a/src/ARMeilleure/Decoders/OpCodeBImmTest.cs +++ b/src/ARMeilleure/Decoders/OpCodeBImmTest.cs @@ -2,7 +2,7 @@ namespace ARMeilleure.Decoders { class OpCodeBImmTest : OpCodeBImm { - public int Rt { get; } + public int Rt { get; } public int Bit { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeBImmTest(inst, address, opCode); @@ -13,8 +13,8 @@ namespace ARMeilleure.Decoders Immediate = (long)address + DecoderHelper.DecodeImmS14_2(opCode); - Bit = (opCode >> 19) & 0x1f; + Bit = (opCode >> 19) & 0x1f; Bit |= (opCode >> 26) & 0x20; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeBReg.cs b/src/ARMeilleure/Decoders/OpCodeBReg.cs index b5dcbfd8e..3b84cf5c0 100644 --- a/src/ARMeilleure/Decoders/OpCodeBReg.cs +++ b/src/ARMeilleure/Decoders/OpCodeBReg.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.Decoders public OpCodeBReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - int op4 = (opCode >> 0) & 0x1f; + int op4 = (opCode >> 0) & 0x1f; int op2 = (opCode >> 16) & 0x1f; if (op2 != 0b11111 || op4 != 0b00000) @@ -21,4 +21,4 @@ namespace ARMeilleure.Decoders Rn = (opCode >> 5) & 0x1f; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeBfm.cs b/src/ARMeilleure/Decoders/OpCodeBfm.cs index 8e1c78361..d51efade2 100644 --- a/src/ARMeilleure/Decoders/OpCodeBfm.cs +++ b/src/ARMeilleure/Decoders/OpCodeBfm.cs @@ -4,8 +4,8 @@ namespace ARMeilleure.Decoders { public long WMask { get; } public long TMask { get; } - public int Pos { get; } - public int Shift { get; } + public int Pos { get; } + public int Shift { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeBfm(inst, address, opCode); @@ -22,8 +22,8 @@ namespace ARMeilleure.Decoders WMask = bm.WMask; TMask = bm.TMask; - Pos = bm.Pos; + Pos = bm.Pos; Shift = bm.Shift; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeCcmp.cs b/src/ARMeilleure/Decoders/OpCodeCcmp.cs index aa47146f8..d40353486 100644 --- a/src/ARMeilleure/Decoders/OpCodeCcmp.cs +++ b/src/ARMeilleure/Decoders/OpCodeCcmp.cs @@ -4,7 +4,7 @@ namespace ARMeilleure.Decoders { class OpCodeCcmp : OpCodeAlu, IOpCodeCond { - public int Nzcv { get; } + public int Nzcv { get; } protected int RmImm; public Condition Cond { get; } @@ -22,11 +22,11 @@ namespace ARMeilleure.Decoders return; } - Nzcv = (opCode >> 0) & 0xf; - Cond = (Condition)((opCode >> 12) & 0xf); - RmImm = (opCode >> 16) & 0x1f; + Nzcv = (opCode >> 0) & 0xf; + Cond = (Condition)((opCode >> 12) & 0xf); + RmImm = (opCode >> 16) & 0x1f; Rd = RegisterAlias.Zr; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeCcmpImm.cs b/src/ARMeilleure/Decoders/OpCodeCcmpImm.cs index 3548f2da8..9d6acf196 100644 --- a/src/ARMeilleure/Decoders/OpCodeCcmpImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeCcmpImm.cs @@ -8,4 +8,4 @@ namespace ARMeilleure.Decoders public OpCodeCcmpImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeCcmpReg.cs b/src/ARMeilleure/Decoders/OpCodeCcmpReg.cs index d5df3b102..349afa120 100644 --- a/src/ARMeilleure/Decoders/OpCodeCcmpReg.cs +++ b/src/ARMeilleure/Decoders/OpCodeCcmpReg.cs @@ -12,4 +12,4 @@ namespace ARMeilleure.Decoders public OpCodeCcmpReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeCsel.cs b/src/ARMeilleure/Decoders/OpCodeCsel.cs index 4b8dc7fdd..418962e08 100644 --- a/src/ARMeilleure/Decoders/OpCodeCsel.cs +++ b/src/ARMeilleure/Decoders/OpCodeCsel.cs @@ -10,8 +10,8 @@ namespace ARMeilleure.Decoders public OpCodeCsel(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rm = (opCode >> 16) & 0x1f; + Rm = (opCode >> 16) & 0x1f; Cond = (Condition)((opCode >> 12) & 0xf); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeException.cs b/src/ARMeilleure/Decoders/OpCodeException.cs index 6b72138ee..eee636405 100644 --- a/src/ARMeilleure/Decoders/OpCodeException.cs +++ b/src/ARMeilleure/Decoders/OpCodeException.cs @@ -11,4 +11,4 @@ namespace ARMeilleure.Decoders Id = (opCode >> 5) & 0xffff; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMem.cs b/src/ARMeilleure/Decoders/OpCodeMem.cs index 0ba2bcd18..9b4e5ff3e 100644 --- a/src/ARMeilleure/Decoders/OpCodeMem.cs +++ b/src/ARMeilleure/Decoders/OpCodeMem.cs @@ -2,18 +2,18 @@ namespace ARMeilleure.Decoders { class OpCodeMem : OpCode { - public int Rt { get; protected set; } - public int Rn { get; protected set; } - public int Size { get; protected set; } + public int Rt { get; protected set; } + public int Rn { get; protected set; } + public int Size { get; protected set; } public bool Extend64 { get; protected set; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMem(inst, address, opCode); public OpCodeMem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rt = (opCode >> 0) & 0x1f; - Rn = (opCode >> 5) & 0x1f; + Rt = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; Size = (opCode >> 30) & 0x3; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMemEx.cs b/src/ARMeilleure/Decoders/OpCodeMemEx.cs index 899024853..1dc73140f 100644 --- a/src/ARMeilleure/Decoders/OpCodeMemEx.cs +++ b/src/ARMeilleure/Decoders/OpCodeMemEx.cs @@ -3,14 +3,14 @@ namespace ARMeilleure.Decoders class OpCodeMemEx : OpCodeMem { public int Rt2 { get; } - public int Rs { get; } + public int Rs { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMemEx(inst, address, opCode); public OpCodeMemEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { Rt2 = (opCode >> 10) & 0x1f; - Rs = (opCode >> 16) & 0x1f; + Rs = (opCode >> 16) & 0x1f; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMemImm.cs b/src/ARMeilleure/Decoders/OpCodeMemImm.cs index d6ed2282f..4d5eeb1ed 100644 --- a/src/ARMeilleure/Decoders/OpCodeMemImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeMemImm.cs @@ -2,18 +2,18 @@ namespace ARMeilleure.Decoders { class OpCodeMemImm : OpCodeMem { - public long Immediate { get; protected set; } - public bool WBack { get; protected set; } - public bool PostIdx { get; protected set; } - protected bool Unscaled { get; } + public long Immediate { get; protected set; } + public bool WBack { get; protected set; } + public bool PostIdx { get; protected set; } + protected bool Unscaled { get; } private enum MemOp { - Unscaled = 0, - PostIndexed = 1, + Unscaled = 0, + PostIndexed = 1, Unprivileged = 2, - PreIndexed = 3, - Unsigned + PreIndexed = 3, + Unsigned, } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMemImm(inst, address, opCode); @@ -21,13 +21,13 @@ namespace ARMeilleure.Decoders public OpCodeMemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { Extend64 = ((opCode >> 22) & 3) == 2; - WBack = ((opCode >> 24) & 1) == 0; + WBack = ((opCode >> 24) & 1) == 0; // The type is not valid for the Unsigned Immediate 12-bits encoding, // because the bits 11:10 are used for the larger Immediate offset. MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned; - PostIdx = type == MemOp.PostIndexed; + PostIdx = type == MemOp.PostIndexed; Unscaled = type == MemOp.Unscaled || type == MemOp.Unprivileged; @@ -50,4 +50,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMemLit.cs b/src/ARMeilleure/Decoders/OpCodeMemLit.cs index 986d66340..8712a78e3 100644 --- a/src/ARMeilleure/Decoders/OpCodeMemLit.cs +++ b/src/ARMeilleure/Decoders/OpCodeMemLit.cs @@ -2,11 +2,11 @@ namespace ARMeilleure.Decoders { class OpCodeMemLit : OpCode, IOpCodeLit { - public int Rt { get; } + public int Rt { get; } public long Immediate { get; } - public int Size { get; } - public bool Signed { get; } - public bool Prefetch { get; } + public int Size { get; } + public bool Signed { get; } + public bool Prefetch { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMemLit(inst, address, opCode); @@ -18,11 +18,27 @@ namespace ARMeilleure.Decoders switch ((opCode >> 30) & 3) { - case 0: Size = 2; Signed = false; Prefetch = false; break; - case 1: Size = 3; Signed = false; Prefetch = false; break; - case 2: Size = 2; Signed = true; Prefetch = false; break; - case 3: Size = 0; Signed = false; Prefetch = true; break; + case 0: + Size = 2; + Signed = false; + Prefetch = false; + break; + case 1: + Size = 3; + Signed = false; + Prefetch = false; + break; + case 2: + Size = 2; + Signed = true; + Prefetch = false; + break; + case 3: + Size = 0; + Signed = false; + Prefetch = true; + break; } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMemPair.cs b/src/ARMeilleure/Decoders/OpCodeMemPair.cs index 21018033d..eb696cfeb 100644 --- a/src/ARMeilleure/Decoders/OpCodeMemPair.cs +++ b/src/ARMeilleure/Decoders/OpCodeMemPair.cs @@ -8,11 +8,11 @@ namespace ARMeilleure.Decoders public OpCodeMemPair(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rt2 = (opCode >> 10) & 0x1f; - WBack = ((opCode >> 23) & 0x1) != 0; - PostIdx = ((opCode >> 23) & 0x3) == 1; + Rt2 = (opCode >> 10) & 0x1f; + WBack = ((opCode >> 23) & 0x1) != 0; + PostIdx = ((opCode >> 23) & 0x3) == 1; Extend64 = ((opCode >> 30) & 0x3) == 1; - Size = ((opCode >> 31) & 0x1) | 2; + Size = ((opCode >> 31) & 0x1) | 2; DecodeImm(opCode); } @@ -22,4 +22,4 @@ namespace ARMeilleure.Decoders Immediate = ((long)(opCode >> 15) << 57) >> (57 - Size); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMemReg.cs b/src/ARMeilleure/Decoders/OpCodeMemReg.cs index 73d6c5d2c..9b0d15959 100644 --- a/src/ARMeilleure/Decoders/OpCodeMemReg.cs +++ b/src/ARMeilleure/Decoders/OpCodeMemReg.cs @@ -3,7 +3,7 @@ namespace ARMeilleure.Decoders class OpCodeMemReg : OpCodeMem { public bool Shift { get; } - public int Rm { get; } + public int Rm { get; } public IntType IntType { get; } @@ -11,10 +11,10 @@ namespace ARMeilleure.Decoders public OpCodeMemReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Shift = ((opCode >> 12) & 0x1) != 0; - IntType = (IntType)((opCode >> 13) & 0x7); - Rm = (opCode >> 16) & 0x1f; - Extend64 = ((opCode >> 22) & 0x3) == 2; + Shift = ((opCode >> 12) & 0x1) != 0; + IntType = (IntType)((opCode >> 13) & 0x7); + Rm = (opCode >> 16) & 0x1f; + Extend64 = ((opCode >> 22) & 0x3) == 2; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMov.cs b/src/ARMeilleure/Decoders/OpCodeMov.cs index 50af88cb9..a2914b71c 100644 --- a/src/ARMeilleure/Decoders/OpCodeMov.cs +++ b/src/ARMeilleure/Decoders/OpCodeMov.cs @@ -22,9 +22,9 @@ namespace ARMeilleure.Decoders return; } - Rd = (opCode >> 0) & 0x1f; - Immediate = (opCode >> 5) & 0xffff; - Bit = (opCode >> 21) & 0x3; + Rd = (opCode >> 0) & 0x1f; + Immediate = (opCode >> 5) & 0xffff; + Bit = (opCode >> 21) & 0x3; Bit <<= 4; @@ -35,4 +35,4 @@ namespace ARMeilleure.Decoders : RegisterSize.Int32; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeMul.cs b/src/ARMeilleure/Decoders/OpCodeMul.cs index 31d140a65..9b1dd37b8 100644 --- a/src/ARMeilleure/Decoders/OpCodeMul.cs +++ b/src/ARMeilleure/Decoders/OpCodeMul.cs @@ -13,4 +13,4 @@ namespace ARMeilleure.Decoders Rm = (opCode >> 16) & 0x1f; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimd.cs b/src/ARMeilleure/Decoders/OpCodeSimd.cs index 85713690a..bd34d74d9 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimd.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimd.cs @@ -2,18 +2,18 @@ namespace ARMeilleure.Decoders { class OpCodeSimd : OpCode, IOpCodeSimd { - public int Rd { get; } - public int Rn { get; } - public int Opc { get; } + public int Rd { get; } + public int Rn { get; } + public int Opc { get; } public int Size { get; protected set; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimd(inst, address, opCode); public OpCodeSimd(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rd = (opCode >> 0) & 0x1f; - Rn = (opCode >> 5) & 0x1f; - Opc = (opCode >> 15) & 0x3; + Rd = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; + Opc = (opCode >> 15) & 0x3; Size = (opCode >> 22) & 0x3; RegisterSize = ((opCode >> 30) & 1) != 0 @@ -21,4 +21,4 @@ namespace ARMeilleure.Decoders : RegisterSize.Simd64; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdCvt.cs b/src/ARMeilleure/Decoders/OpCodeSimdCvt.cs index 05b32941a..e50cf12e6 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdCvt.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdCvt.cs @@ -9,7 +9,7 @@ namespace ARMeilleure.Decoders public OpCodeSimdCvt(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { int scale = (opCode >> 10) & 0x3f; - int sf = (opCode >> 31) & 0x1; + int sf = (opCode >> 31) & 0x1; FBits = 64 - scale; @@ -18,4 +18,4 @@ namespace ARMeilleure.Decoders : RegisterSize.Int32; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdExt.cs b/src/ARMeilleure/Decoders/OpCodeSimdExt.cs index a0e264d9d..0a3359e13 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdExt.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdExt.cs @@ -11,4 +11,4 @@ namespace ARMeilleure.Decoders Imm4 = (opCode >> 11) & 0xf; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdFcond.cs b/src/ARMeilleure/Decoders/OpCodeSimdFcond.cs index aa16e0c19..510cd3101 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdFcond.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdFcond.cs @@ -10,7 +10,7 @@ namespace ARMeilleure.Decoders public OpCodeSimdFcond(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Nzcv = (opCode >> 0) & 0xf; + Nzcv = (opCode >> 0) & 0xf; Cond = (Condition)((opCode >> 12) & 0xf); } } diff --git a/src/ARMeilleure/Decoders/OpCodeSimdFmov.cs b/src/ARMeilleure/Decoders/OpCodeSimdFmov.cs index 9f9062b8d..662abe284 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdFmov.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdFmov.cs @@ -2,9 +2,9 @@ namespace ARMeilleure.Decoders { class OpCodeSimdFmov : OpCode, IOpCodeSimd { - public int Rd { get; } + public int Rd { get; } public long Immediate { get; } - public int Size { get; } + public int Size { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdFmov(inst, address, opCode); @@ -16,7 +16,7 @@ namespace ARMeilleure.Decoders long imm; - Rd = (opCode >> 0) & 0x1f; + Rd = (opCode >> 0) & 0x1f; imm = (opCode >> 13) & 0xff; if (type == 0) @@ -29,4 +29,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdHelper.cs b/src/ARMeilleure/Decoders/OpCodeSimdHelper.cs index 02f74d030..d900ed9ba 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdHelper.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdHelper.cs @@ -52,17 +52,20 @@ else if ((modeHigh & 0b110) == 0b100) { // 16-bits shifted Immediate. - size = 1; imm <<= (modeHigh & 1) << 3; + size = 1; + imm <<= (modeHigh & 1) << 3; } else if ((modeHigh & 0b100) == 0b000) { // 32-bits shifted Immediate. - size = 2; imm <<= modeHigh << 3; + size = 2; + imm <<= modeHigh << 3; } else if ((modeHigh & 0b111) == 0b110) { // 32-bits shifted Immediate (fill with ones). - size = 2; imm = ShlOnes(imm, 8 << modeLow); + size = 2; + imm = ShlOnes(imm, 8 << modeLow); } else { diff --git a/src/ARMeilleure/Decoders/OpCodeSimdImm.cs b/src/ARMeilleure/Decoders/OpCodeSimdImm.cs index eeca77096..3f4bad7f7 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdImm.cs @@ -2,9 +2,9 @@ namespace ARMeilleure.Decoders { class OpCodeSimdImm : OpCode, IOpCodeSimd { - public int Rd { get; } + public int Rd { get; } public long Immediate { get; } - public int Size { get; } + public int Size { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdImm(inst, address, opCode); @@ -13,14 +13,14 @@ namespace ARMeilleure.Decoders Rd = opCode & 0x1f; int cMode = (opCode >> 12) & 0xf; - int op = (opCode >> 29) & 0x1; + int op = (opCode >> 29) & 0x1; - int modeLow = cMode & 1; + int modeLow = cMode & 1; int modeHigh = cMode >> 1; long imm; - imm = ((uint)opCode >> 5) & 0x1f; + imm = ((uint)opCode >> 5) & 0x1f; imm |= ((uint)opCode >> 11) & 0xe0; if (modeHigh == 0b111) @@ -67,17 +67,20 @@ namespace ARMeilleure.Decoders else if ((modeHigh & 0b110) == 0b100) { // 16-bits shifted Immediate. - Size = 1; imm <<= (modeHigh & 1) << 3; + Size = 1; + imm <<= (modeHigh & 1) << 3; } else if ((modeHigh & 0b100) == 0b000) { // 32-bits shifted Immediate. - Size = 2; imm <<= modeHigh << 3; + Size = 2; + imm <<= modeHigh << 3; } else if ((modeHigh & 0b111) == 0b110) { // 32-bits shifted Immediate (fill with ones). - Size = 2; imm = ShlOnes(imm, 8 << modeLow); + Size = 2; + imm = ShlOnes(imm, 8 << modeLow); } else { @@ -104,4 +107,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdIns.cs b/src/ARMeilleure/Decoders/OpCodeSimdIns.cs index f6f9249d1..95436879c 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdIns.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdIns.cs @@ -23,14 +23,22 @@ namespace ARMeilleure.Decoders switch (Size) { - case 1: Size = 0; break; - case 2: Size = 1; break; - case 4: Size = 2; break; - case 8: Size = 3; break; + case 1: + Size = 0; + break; + case 2: + Size = 1; + break; + case 4: + Size = 2; + break; + case 8: + Size = 3; + break; } - SrcIndex = imm4 >> Size; + SrcIndex = imm4 >> Size; DstIndex = imm5 >> (Size + 1); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdMemImm.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemImm.cs index c11594cb0..14a9d7c9c 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdMemImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdMemImm.cs @@ -25,4 +25,4 @@ namespace ARMeilleure.Decoders Extend64 = false; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdMemLit.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemLit.cs index 8e2129661..efa558bf9 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdMemLit.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdMemLit.cs @@ -2,10 +2,10 @@ namespace ARMeilleure.Decoders { class OpCodeSimdMemLit : OpCode, IOpCodeSimd, IOpCodeLit { - public int Rt { get; } + public int Rt { get; } public long Immediate { get; } - public int Size { get; } - public bool Signed => false; + public int Size { get; } + public bool Signed => false; public bool Prefetch => false; public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdMemLit(inst, address, opCode); @@ -28,4 +28,4 @@ namespace ARMeilleure.Decoders Size = opc + 2; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdMemMs.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemMs.cs index 8922c18f6..c05b52494 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdMemMs.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdMemMs.cs @@ -2,10 +2,10 @@ namespace ARMeilleure.Decoders { class OpCodeSimdMemMs : OpCodeMemReg, IOpCodeSimd { - public int Reps { get; } - public int SElems { get; } - public int Elems { get; } - public bool WBack { get; } + public int Reps { get; } + public int SElems { get; } + public int Elems { get; } + public bool WBack { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdMemMs(inst, address, opCode); @@ -13,18 +13,41 @@ namespace ARMeilleure.Decoders { switch ((opCode >> 12) & 0xf) { - case 0b0000: Reps = 1; SElems = 4; break; - case 0b0010: Reps = 4; SElems = 1; break; - case 0b0100: Reps = 1; SElems = 3; break; - case 0b0110: Reps = 3; SElems = 1; break; - case 0b0111: Reps = 1; SElems = 1; break; - case 0b1000: Reps = 1; SElems = 2; break; - case 0b1010: Reps = 2; SElems = 1; break; + case 0b0000: + Reps = 1; + SElems = 4; + break; + case 0b0010: + Reps = 4; + SElems = 1; + break; + case 0b0100: + Reps = 1; + SElems = 3; + break; + case 0b0110: + Reps = 3; + SElems = 1; + break; + case 0b0111: + Reps = 1; + SElems = 1; + break; + case 0b1000: + Reps = 1; + SElems = 2; + break; + case 0b1010: + Reps = 2; + SElems = 1; + break; - default: Instruction = InstDescriptor.Undefined; return; + default: + Instruction = InstDescriptor.Undefined; + return; } - Size = (opCode >> 10) & 3; + Size = (opCode >> 10) & 3; WBack = ((opCode >> 23) & 1) != 0; bool q = ((opCode >> 30) & 1) != 0; @@ -45,4 +68,4 @@ namespace ARMeilleure.Decoders Elems = (GetBitsCount() >> 3) >> Size; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdMemPair.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemPair.cs index 1ab953679..697163896 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdMemPair.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdMemPair.cs @@ -13,4 +13,4 @@ namespace ARMeilleure.Decoders DecodeImm(opCode); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdMemReg.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemReg.cs index 9ea6dda37..be7b25b9d 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdMemReg.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdMemReg.cs @@ -18,4 +18,4 @@ namespace ARMeilleure.Decoders Extend64 = false; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdMemSs.cs b/src/ARMeilleure/Decoders/OpCodeSimdMemSs.cs index 44abdd389..5bc614e19 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdMemSs.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdMemSs.cs @@ -2,21 +2,21 @@ namespace ARMeilleure.Decoders { class OpCodeSimdMemSs : OpCodeMemReg, IOpCodeSimd { - public int SElems { get; } - public int Index { get; } + public int SElems { get; } + public int Index { get; } public bool Replicate { get; } - public bool WBack { get; } + public bool WBack { get; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdMemSs(inst, address, opCode); public OpCodeSimdMemSs(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - int size = (opCode >> 10) & 3; - int s = (opCode >> 12) & 1; + int size = (opCode >> 10) & 3; + int s = (opCode >> 12) & 1; int sElems = (opCode >> 12) & 2; - int scale = (opCode >> 14) & 3; - int l = (opCode >> 22) & 1; - int q = (opCode >> 30) & 1; + int scale = (opCode >> 14) & 3; + int l = (opCode >> 22) & 1; + int q = (opCode >> 30) & 1; sElems |= (opCode >> 21) & 1; @@ -27,63 +27,63 @@ namespace ARMeilleure.Decoders switch (scale) { case 1: - { - if ((size & 1) != 0) { - Instruction = InstDescriptor.Undefined; + if ((size & 1) != 0) + { + Instruction = InstDescriptor.Undefined; - return; + return; + } + + index >>= 1; + + break; } - index >>= 1; - - break; - } - case 2: - { - if ((size & 2) != 0 || - ((size & 1) != 0 && s != 0)) { - Instruction = InstDescriptor.Undefined; + if ((size & 2) != 0 || + ((size & 1) != 0 && s != 0)) + { + Instruction = InstDescriptor.Undefined; - return; + return; + } + + if ((size & 1) != 0) + { + index >>= 3; + + scale = 3; + } + else + { + index >>= 2; + } + + break; } - if ((size & 1) != 0) - { - index >>= 3; - - scale = 3; - } - else - { - index >>= 2; - } - - break; - } - case 3: - { - if (l == 0 || s != 0) { - Instruction = InstDescriptor.Undefined; + if (l == 0 || s != 0) + { + Instruction = InstDescriptor.Undefined; - return; + return; + } + + scale = size; + + Replicate = true; + + break; } - - scale = size; - - Replicate = true; - - break; - } } - Index = index; + Index = index; SElems = sElems; - Size = scale; + Size = scale; Extend64 = false; @@ -94,4 +94,4 @@ namespace ARMeilleure.Decoders : RegisterSize.Simd64; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdReg.cs b/src/ARMeilleure/Decoders/OpCodeSimdReg.cs index ac4f71dae..40f9b1c53 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdReg.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdReg.cs @@ -3,16 +3,16 @@ namespace ARMeilleure.Decoders class OpCodeSimdReg : OpCodeSimd { public bool Bit3 { get; } - public int Ra { get; } - public int Rm { get; protected set; } + public int Ra { get; } + public int Rm { get; protected set; } public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdReg(inst, address, opCode); public OpCodeSimdReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Bit3 = ((opCode >> 3) & 0x1) != 0; - Ra = (opCode >> 10) & 0x1f; - Rm = (opCode >> 16) & 0x1f; + Bit3 = ((opCode >> 3) & 0x1) != 0; + Ra = (opCode >> 10) & 0x1f; + Rm = (opCode >> 16) & 0x1f; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdRegElem.cs b/src/ARMeilleure/Decoders/OpCodeSimdRegElem.cs index 92368deea..bb248ab6b 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdRegElem.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdRegElem.cs @@ -12,7 +12,7 @@ namespace ARMeilleure.Decoders { case 1: Index = (opCode >> 20) & 3 | - (opCode >> 9) & 4; + (opCode >> 9) & 4; Rm &= 0xf; @@ -24,8 +24,10 @@ namespace ARMeilleure.Decoders break; - default: Instruction = InstDescriptor.Undefined; break; + default: + Instruction = InstDescriptor.Undefined; + break; } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSimdRegElemF.cs b/src/ARMeilleure/Decoders/OpCodeSimdRegElemF.cs index d46dd57ed..c97bd787e 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdRegElemF.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdRegElemF.cs @@ -26,7 +26,9 @@ namespace ARMeilleure.Decoders break; - default: Instruction = InstDescriptor.Undefined; break; + default: + Instruction = InstDescriptor.Undefined; + break; } } } diff --git a/src/ARMeilleure/Decoders/OpCodeSimdTbl.cs b/src/ARMeilleure/Decoders/OpCodeSimdTbl.cs index 9c631e485..3a7ef6aba 100644 --- a/src/ARMeilleure/Decoders/OpCodeSimdTbl.cs +++ b/src/ARMeilleure/Decoders/OpCodeSimdTbl.cs @@ -9,4 +9,4 @@ namespace ARMeilleure.Decoders Size = ((opCode >> 13) & 3) + 1; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeSystem.cs b/src/ARMeilleure/Decoders/OpCodeSystem.cs index 4d79421a8..215134153 100644 --- a/src/ARMeilleure/Decoders/OpCodeSystem.cs +++ b/src/ARMeilleure/Decoders/OpCodeSystem.cs @@ -2,7 +2,7 @@ namespace ARMeilleure.Decoders { class OpCodeSystem : OpCode { - public int Rt { get; } + public int Rt { get; } public int Op2 { get; } public int CRm { get; } public int CRn { get; } @@ -13,12 +13,12 @@ namespace ARMeilleure.Decoders public OpCodeSystem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rt = (opCode >> 0) & 0x1f; - Op2 = (opCode >> 5) & 0x7; - CRm = (opCode >> 8) & 0xf; - CRn = (opCode >> 12) & 0xf; - Op1 = (opCode >> 16) & 0x7; + Rt = (opCode >> 0) & 0x1f; + Op2 = (opCode >> 5) & 0x7; + CRm = (opCode >> 8) & 0xf; + CRn = (opCode >> 12) & 0xf; + Op1 = (opCode >> 16) & 0x7; Op0 = ((opCode >> 19) & 0x1) | 2; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT16.cs b/src/ARMeilleure/Decoders/OpCodeT16.cs index 9c3d6b006..de946b961 100644 --- a/src/ARMeilleure/Decoders/OpCodeT16.cs +++ b/src/ARMeilleure/Decoders/OpCodeT16.cs @@ -12,4 +12,4 @@ namespace ARMeilleure.Decoders OpCodeSizeInBytes = 2; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs b/src/ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs index 95f180548..cefb50e4a 100644 --- a/src/ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs +++ b/src/ARMeilleure/Decoders/OpCodeT16AddSubImm3.cs @@ -1,6 +1,6 @@ namespace ARMeilleure.Decoders { - class OpCodeT16AddSubImm3: OpCodeT16, IOpCode32AluImm + class OpCodeT16AddSubImm3 : OpCodeT16, IOpCode32AluImm { public int Rd { get; } public int Rn { get; } @@ -15,8 +15,8 @@ public OpCodeT16AddSubImm3(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rd = (opCode >> 0) & 0x7; - Rn = (opCode >> 3) & 0x7; + Rd = (opCode >> 0) & 0x7; + Rn = (opCode >> 3) & 0x7; Immediate = (opCode >> 6) & 0x7; IsRotated = false; } diff --git a/src/ARMeilleure/Decoders/OpCodeT16BImm11.cs b/src/ARMeilleure/Decoders/OpCodeT16BImm11.cs index f230b20e2..fab098a8f 100644 --- a/src/ARMeilleure/Decoders/OpCodeT16BImm11.cs +++ b/src/ARMeilleure/Decoders/OpCodeT16BImm11.cs @@ -8,7 +8,7 @@ public OpCodeT16BImm11(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - int imm = (opCode << 21) >> 20; + int imm = (opCode << 21) >> 20; Immediate = GetPc() + imm; } } diff --git a/src/ARMeilleure/Decoders/OpCodeT16BImm8.cs b/src/ARMeilleure/Decoders/OpCodeT16BImm8.cs index 5f6842983..edfa86ba5 100644 --- a/src/ARMeilleure/Decoders/OpCodeT16BImm8.cs +++ b/src/ARMeilleure/Decoders/OpCodeT16BImm8.cs @@ -10,7 +10,7 @@ { Cond = (Condition)((opCode >> 8) & 0xf); - int imm = (opCode << 24) >> 23; + int imm = (opCode << 24) >> 23; Immediate = GetPc() + imm; } } diff --git a/src/ARMeilleure/Decoders/OpCodeT16MemImm5.cs b/src/ARMeilleure/Decoders/OpCodeT16MemImm5.cs index 20ef31e27..873c63b93 100644 --- a/src/ARMeilleure/Decoders/OpCodeT16MemImm5.cs +++ b/src/ARMeilleure/Decoders/OpCodeT16MemImm5.cs @@ -36,23 +36,13 @@ namespace ARMeilleure.Decoders break; } - switch (inst.Name) + Immediate = inst.Name switch { - case InstName.Str: - case InstName.Ldr: - Immediate = ((opCode >> 6) & 0x1f) << 2; - break; - case InstName.Strb: - case InstName.Ldrb: - Immediate = ((opCode >> 6) & 0x1f); - break; - case InstName.Strh: - case InstName.Ldrh: - Immediate = ((opCode >> 6) & 0x1f) << 1; - break; - default: - throw new InvalidOperationException(); - } + InstName.Str or InstName.Ldr => ((opCode >> 6) & 0x1f) << 2, + InstName.Strb or InstName.Ldrb => ((opCode >> 6) & 0x1f), + InstName.Strh or InstName.Ldrh => ((opCode >> 6) & 0x1f) << 1, + _ => throw new InvalidOperationException(), + }; } } } diff --git a/src/ARMeilleure/Decoders/OpCodeT16MemMult.cs b/src/ARMeilleure/Decoders/OpCodeT16MemMult.cs index f4185cfcb..3f3057acd 100644 --- a/src/ARMeilleure/Decoders/OpCodeT16MemMult.cs +++ b/src/ARMeilleure/Decoders/OpCodeT16MemMult.cs @@ -27,7 +27,7 @@ namespace ARMeilleure.Decoders { InstName.Ldm => true, InstName.Stm => false, - _ => throw new InvalidOperationException() + _ => throw new InvalidOperationException(), }; } } diff --git a/src/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs b/src/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs index a540026ec..8f35e4391 100644 --- a/src/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeT16ShiftImm.cs @@ -15,8 +15,8 @@ public OpCodeT16ShiftImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rd = (opCode >> 0) & 0x7; - Rm = (opCode >> 3) & 0x7; + Rd = (opCode >> 0) & 0x7; + Rm = (opCode >> 3) & 0x7; Immediate = (opCode >> 6) & 0x1F; ShiftType = (ShiftType)((opCode >> 11) & 3); } diff --git a/src/ARMeilleure/Decoders/OpCodeT32.cs b/src/ARMeilleure/Decoders/OpCodeT32.cs index cf43d4298..5ccbd6a27 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32.cs @@ -12,4 +12,4 @@ OpCodeSizeInBytes = 4; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32Alu.cs b/src/ARMeilleure/Decoders/OpCodeT32Alu.cs index a81b3b3dc..1f92f7558 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32Alu.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32Alu.cs @@ -17,4 +17,4 @@ SetFlags = ((opCode >> 20) & 1) != 0; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32AluImm.cs b/src/ARMeilleure/Decoders/OpCodeT32AluImm.cs index 0895c29b4..863d14bda 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32AluImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32AluImm.cs @@ -35,4 +35,4 @@ namespace ARMeilleure.Decoders } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32AluImm12.cs b/src/ARMeilleure/Decoders/OpCodeT32AluImm12.cs index 31de63dd3..12b65a100 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32AluImm12.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32AluImm12.cs @@ -13,4 +13,4 @@ namespace ARMeilleure.Decoders Immediate = (opCode & 0xff) | ((opCode >> 4) & 0x700) | ((opCode >> 15) & 0x800); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32AluReg.cs b/src/ARMeilleure/Decoders/OpCodeT32AluReg.cs index a487f55a6..4ac983470 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32AluReg.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32AluReg.cs @@ -11,4 +11,4 @@ namespace ARMeilleure.Decoders Rm = (opCode >> 0) & 0xf; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs b/src/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs index 1c9ba7a2c..edf0298df 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32AluRsImm.cs @@ -2,7 +2,7 @@ { class OpCodeT32AluRsImm : OpCodeT32Alu, IOpCode32AluRsImm { - public int Rm { get; } + public int Rm { get; } public int Immediate { get; } public ShiftType ShiftType { get; } @@ -11,10 +11,10 @@ public OpCodeT32AluRsImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Rm = (opCode >> 0) & 0xf; + Rm = (opCode >> 0) & 0xf; Immediate = ((opCode >> 6) & 3) | ((opCode >> 10) & 0x1c); ShiftType = (ShiftType)((opCode >> 4) & 3); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32BImm20.cs b/src/ARMeilleure/Decoders/OpCodeT32BImm20.cs index b6da8abdb..13256ee54 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32BImm20.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32BImm20.cs @@ -24,4 +24,4 @@ Cond = (Condition)((opCode >> 22) & 0xf); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32BImm24.cs b/src/ARMeilleure/Decoders/OpCodeT32BImm24.cs index 774ec3a64..d7c606619 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32BImm24.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32BImm24.cs @@ -32,4 +32,4 @@ namespace ARMeilleure.Decoders Immediate = pc + imm32; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32MemImm12.cs b/src/ARMeilleure/Decoders/OpCodeT32MemImm12.cs index 7838604b2..4977cdf50 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32MemImm12.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32MemImm12.cs @@ -22,4 +22,4 @@ IsLoad = ((opCode >> 20) & 1) != 0; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32MemImm8.cs b/src/ARMeilleure/Decoders/OpCodeT32MemImm8.cs index d8b7763cb..f84e41400 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32MemImm8.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32MemImm8.cs @@ -18,7 +18,7 @@ Rn = (opCode >> 16) & 0xf; Index = ((opCode >> 10) & 1) != 0; - Add = ((opCode >> 9) & 1) != 0; + Add = ((opCode >> 9) & 1) != 0; WBack = ((opCode >> 8) & 1) != 0; Immediate = opCode & 0xff; @@ -26,4 +26,4 @@ IsLoad = ((opCode >> 20) & 1) != 0; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs b/src/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs index 7a078c489..51f5042f2 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32MemImm8D.cs @@ -20,7 +20,7 @@ namespace ARMeilleure.Decoders Rn = (opCode >> 16) & 0xf; Index = ((opCode >> 24) & 1) != 0; - Add = ((opCode >> 23) & 1) != 0; + Add = ((opCode >> 23) & 1) != 0; WBack = ((opCode >> 21) & 1) != 0; Immediate = (opCode & 0xff) << 2; @@ -28,4 +28,4 @@ namespace ARMeilleure.Decoders IsLoad = ((opCode >> 20) & 1) != 0; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32MemMult.cs b/src/ARMeilleure/Decoders/OpCodeT32MemMult.cs index a9ba306dc..d155842a6 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32MemMult.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32MemMult.cs @@ -7,8 +7,8 @@ namespace ARMeilleure.Decoders public int Rn { get; } public int RegisterMask { get; } - public int Offset { get; } - public int PostOffset { get; } + public int Offset { get; } + public int PostOffset { get; } public bool IsLoad { get; } @@ -19,9 +19,9 @@ namespace ARMeilleure.Decoders Rn = (opCode >> 16) & 0xf; bool isLoad = (opCode & (1 << 20)) != 0; - bool w = (opCode & (1 << 21)) != 0; - bool u = (opCode & (1 << 23)) != 0; - bool p = (opCode & (1 << 24)) != 0; + bool w = (opCode & (1 << 21)) != 0; + bool u = (opCode & (1 << 23)) != 0; + bool p = (opCode & (1 << 24)) != 0; RegisterMask = opCode & 0xffff; @@ -49,4 +49,4 @@ namespace ARMeilleure.Decoders IsLoad = isLoad; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32MovImm16.cs b/src/ARMeilleure/Decoders/OpCodeT32MovImm16.cs index 5161892bb..2f871c740 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32MovImm16.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32MovImm16.cs @@ -4,8 +4,6 @@ namespace ARMeilleure.Decoders { public int Immediate { get; } - public bool IsRotated => false; - public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32MovImm16(inst, address, opCode); public OpCodeT32MovImm16(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) @@ -13,4 +11,4 @@ namespace ARMeilleure.Decoders Immediate = (opCode & 0xff) | ((opCode >> 4) & 0x700) | ((opCode >> 15) & 0x800) | ((opCode >> 4) & 0xf000); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeT32Tb.cs b/src/ARMeilleure/Decoders/OpCodeT32Tb.cs index 527754b1f..0a4d2a6c4 100644 --- a/src/ARMeilleure/Decoders/OpCodeT32Tb.cs +++ b/src/ARMeilleure/Decoders/OpCodeT32Tb.cs @@ -13,4 +13,4 @@ namespace ARMeilleure.Decoders Rn = (opCode >> 16) & 0xf; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/OpCodeTable.cs b/src/ARMeilleure/Decoders/OpCodeTable.cs index 4f3599583..d3fc4ca08 100644 --- a/src/ARMeilleure/Decoders/OpCodeTable.cs +++ b/src/ARMeilleure/Decoders/OpCodeTable.cs @@ -13,7 +13,7 @@ namespace ARMeilleure.Decoders private readonly struct InstInfo { - public int Mask { get; } + public int Mask { get; } public int Value { get; } public InstDescriptor Inst { get; } @@ -22,24 +22,25 @@ namespace ARMeilleure.Decoders public InstInfo(int mask, int value, InstDescriptor inst, MakeOp makeOp) { - Mask = mask; - Value = value; - Inst = inst; + Mask = mask; + Value = value; + Inst = inst; MakeOp = makeOp; } } - private static List<InstInfo> AllInstA32 = new(); - private static List<InstInfo> AllInstT32 = new(); - private static List<InstInfo> AllInstA64 = new(); + private static readonly List<InstInfo> _allInstA32 = new(); + private static readonly List<InstInfo> _allInstT32 = new(); + private static readonly List<InstInfo> _allInstA64 = new(); - private static InstInfo[][] InstA32FastLookup = new InstInfo[FastLookupSize][]; - private static InstInfo[][] InstT32FastLookup = new InstInfo[FastLookupSize][]; - private static InstInfo[][] InstA64FastLookup = new InstInfo[FastLookupSize][]; + private static readonly InstInfo[][] _instA32FastLookup = new InstInfo[FastLookupSize][]; + private static readonly InstInfo[][] _instT32FastLookup = new InstInfo[FastLookupSize][]; + private static readonly InstInfo[][] _instA64FastLookup = new InstInfo[FastLookupSize][]; static OpCodeTable() { -#region "OpCode Table (AArch64)" +#pragma warning disable IDE0055 // Disable formatting + #region "OpCode Table (AArch64)" // Base SetA64("x0011010000xxxxx000000xxxxxxxxxx", InstName.Adc, InstEmit.Adc, OpCodeAluRs.Create); SetA64("x0111010000xxxxx000000xxxxxxxxxx", InstName.Adcs, InstEmit.Adcs, OpCodeAluRs.Create); @@ -638,9 +639,9 @@ namespace ARMeilleure.Decoders SetA64("0x001110<<100001001010xxxxxxxxxx", InstName.Xtn_V, InstEmit.Xtn_V, OpCodeSimd.Create); SetA64("0>001110<<0xxxxx001110xxxxxxxxxx", InstName.Zip1_V, InstEmit.Zip1_V, OpCodeSimdReg.Create); SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstName.Zip2_V, InstEmit.Zip2_V, OpCodeSimdReg.Create); -#endregion + #endregion -#region "OpCode Table (AArch32, A32)" + #region "OpCode Table (AArch32, A32)" // Base SetA32("<<<<0010101xxxxxxxxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCode32AluImm.Create); SetA32("<<<<0000101xxxxxxxxxxxxxxxx0xxxx", InstName.Adc, InstEmit32.Adc, OpCode32AluRsImm.Create); @@ -1050,9 +1051,9 @@ namespace ARMeilleure.Decoders SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); SetAsimd("111100111x11<<10xxxx00010xx0xxxx", InstName.Vuzp, InstEmit32.Vuzp, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); SetAsimd("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); -#endregion + #endregion -#region "OpCode Table (AArch32, T16)" + #region "OpCode Table (AArch32, T16)" SetT16("000<<xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftImm.Create); SetT16("0001100xxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AddSubReg.Create); SetT16("0001101xxxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AddSubReg.Create); @@ -1131,7 +1132,7 @@ namespace ARMeilleure.Decoders SetT16("11100xxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT16BImm11.Create); #endregion -#region "OpCode Table (AArch32, T32)" + #region "OpCode Table (AArch32, T32)" // Base SetT32("11101011010xxxxx0xxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT32AluRsImm.Create); SetT32("11110x01010xxxxx0xxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT32AluImm.Create); @@ -1299,12 +1300,13 @@ namespace ARMeilleure.Decoders SetT32("11110011101011111000000000000001", InstName.Yield, InstEmit32.Nop, OpCodeT32.Create); #endregion - FillFastLookupTable(InstA32FastLookup, AllInstA32, ToFastLookupIndexA); - FillFastLookupTable(InstT32FastLookup, AllInstT32, ToFastLookupIndexT); - FillFastLookupTable(InstA64FastLookup, AllInstA64, ToFastLookupIndexA); + FillFastLookupTable(_instA32FastLookup, _allInstA32, ToFastLookupIndexA); + FillFastLookupTable(_instT32FastLookup, _allInstT32, ToFastLookupIndexT); + FillFastLookupTable(_instA64FastLookup, _allInstA64, ToFastLookupIndexA); +#pragma warning restore IDE0055 } - private static void FillFastLookupTable(InstInfo[][] table, List<InstInfo> allInsts, Func<int, int> ToFastLookupIndex) + private static void FillFastLookupTable(InstInfo[][] table, List<InstInfo> allInsts, Func<int, int> toFastLookupIndex) { List<InstInfo>[] temp = new List<InstInfo>[FastLookupSize]; @@ -1315,8 +1317,8 @@ namespace ARMeilleure.Decoders foreach (InstInfo inst in allInsts) { - int mask = ToFastLookupIndex(inst.Mask); - int value = ToFastLookupIndex(inst.Value); + int mask = toFastLookupIndex(inst.Mask); + int value = toFastLookupIndex(inst.Value); for (int index = 0; index < temp.Length; index++) { @@ -1335,22 +1337,21 @@ namespace ARMeilleure.Decoders private static void SetA32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) { - Set(encoding, AllInstA32, new InstDescriptor(name, emitter), makeOp); + Set(encoding, _allInstA32, new InstDescriptor(name, emitter), makeOp); } private static void SetT16(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) { encoding = "xxxxxxxxxxxxxxxx" + encoding; - Set(encoding, AllInstT32, new InstDescriptor(name, emitter), makeOp); + Set(encoding, _allInstT32, new InstDescriptor(name, emitter), makeOp); } private static void SetT32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) { string reversedEncoding = $"{encoding.AsSpan(16)}{encoding.AsSpan(0, 16)}"; - MakeOp reversedMakeOp = - (inst, address, opCode) + OpCode ReversedMakeOp(InstDescriptor inst, ulong address, int opCode) => makeOp(inst, address, (int)BitOperations.RotateRight((uint)opCode, 16)); - Set(reversedEncoding, AllInstT32, new InstDescriptor(name, emitter), reversedMakeOp); + Set(reversedEncoding, _allInstT32, new InstDescriptor(name, emitter), ReversedMakeOp); } private static void SetVfp(string encoding, InstName name, InstEmitter emitter, MakeOp makeOpA32, MakeOp makeOpT32) @@ -1395,12 +1396,12 @@ namespace ARMeilleure.Decoders private static void SetA64(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp) { - Set(encoding, AllInstA64, new InstDescriptor(name, emitter), makeOp); + Set(encoding, _allInstA64, new InstDescriptor(name, emitter), makeOp); } private static void Set(string encoding, List<InstInfo> list, InstDescriptor inst, MakeOp makeOp) { - int bit = encoding.Length - 1; + int bit = encoding.Length - 1; int value = 0; int xMask = 0; int xBits = 0; @@ -1439,7 +1440,7 @@ namespace ARMeilleure.Decoders } else if (chr != '0') { - throw new ArgumentException(nameof(encoding)); + throw new ArgumentException($"Invalid encoding: {encoding}", nameof(encoding)); } } @@ -1470,17 +1471,17 @@ namespace ARMeilleure.Decoders public static (InstDescriptor inst, MakeOp makeOp) GetInstA32(int opCode) { - return GetInstFromList(InstA32FastLookup[ToFastLookupIndexA(opCode)], opCode); + return GetInstFromList(_instA32FastLookup[ToFastLookupIndexA(opCode)], opCode); } public static (InstDescriptor inst, MakeOp makeOp) GetInstT32(int opCode) { - return GetInstFromList(InstT32FastLookup[ToFastLookupIndexT(opCode)], opCode); + return GetInstFromList(_instT32FastLookup[ToFastLookupIndexT(opCode)], opCode); } public static (InstDescriptor inst, MakeOp makeOp) GetInstA64(int opCode) { - return GetInstFromList(InstA64FastLookup[ToFastLookupIndexA(opCode)], opCode); + return GetInstFromList(_instA64FastLookup[ToFastLookupIndexA(opCode)], opCode); } private static (InstDescriptor inst, MakeOp makeOp) GetInstFromList(InstInfo[] insts, int opCode) diff --git a/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs b/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs index 17c17812d..ff9a6f271 100644 --- a/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs +++ b/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs @@ -22,10 +22,10 @@ namespace ARMeilleure.Decoders.Optimizations Block entryBlock = blocks[entryBlockId]; Block startBlock = entryBlock; - Block endBlock = entryBlock; + Block endBlock = entryBlock; int startBlockIndex = entryBlockId; - int endBlockIndex = entryBlockId; + int endBlockIndex = entryBlockId; for (int i = entryBlockId + 1; i < blocks.Count; i++) // Search forwards. { @@ -36,7 +36,7 @@ namespace ARMeilleure.Decoders.Optimizations break; // End of contiguous function. } - endBlock = block; + endBlock = block; endBlockIndex = i; } @@ -49,7 +49,7 @@ namespace ARMeilleure.Decoders.Optimizations break; // End of contiguous function. } - startBlock = block; + startBlock = block; startBlockIndex = i; } @@ -57,7 +57,7 @@ namespace ARMeilleure.Decoders.Optimizations { return blocks.ToArray(); // Nothing to do here. } - + // Mark branches whose target is outside of the contiguous region as an exit block. for (int i = startBlockIndex; i <= endBlockIndex; i++) { @@ -69,7 +69,7 @@ namespace ARMeilleure.Decoders.Optimizations } } - var newBlocks = new List<Block>(blocks.Count); + var newBlocks = new List<Block>(blocks.Count); // Finally, rebuild decoded block list, ignoring blocks outside the contiguous range. for (int i = 0; i < blocks.Count; i++) diff --git a/src/ARMeilleure/Decoders/RegisterSize.cs b/src/ARMeilleure/Decoders/RegisterSize.cs index c9cea03ed..7c00984e8 100644 --- a/src/ARMeilleure/Decoders/RegisterSize.cs +++ b/src/ARMeilleure/Decoders/RegisterSize.cs @@ -5,6 +5,6 @@ namespace ARMeilleure.Decoders Int32, Int64, Simd64, - Simd128 + Simd128, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Decoders/ShiftType.cs b/src/ARMeilleure/Decoders/ShiftType.cs index 8583f16ad..43b738f3f 100644 --- a/src/ARMeilleure/Decoders/ShiftType.cs +++ b/src/ARMeilleure/Decoders/ShiftType.cs @@ -5,6 +5,6 @@ namespace ARMeilleure.Decoders Lsl = 0, Lsr = 1, Asr = 2, - Ror = 3 + Ror = 3, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Diagnostics/IRDumper.cs b/src/ARMeilleure/Diagnostics/IRDumper.cs index 3d1a60e58..16833d085 100644 --- a/src/ARMeilleure/Diagnostics/IRDumper.cs +++ b/src/ARMeilleure/Diagnostics/IRDumper.cs @@ -34,7 +34,9 @@ namespace ARMeilleure.Diagnostics for (int index = 0; index < _indentLevel; index++) { +#pragma warning disable CA1834 // Use StringBuilder.Append(char) for single character strings _builder.Append(Indentation); +#pragma warning restore CA1834 } } @@ -110,10 +112,18 @@ namespace ARMeilleure.Diagnostics switch (reg.Type) { - case RegisterType.Flag: _builder.Append('b'); break; - case RegisterType.FpFlag: _builder.Append('f'); break; - case RegisterType.Integer: _builder.Append('r'); break; - case RegisterType.Vector: _builder.Append('v'); break; + case RegisterType.Flag: + _builder.Append('b'); + break; + case RegisterType.FpFlag: + _builder.Append('f'); + break; + case RegisterType.Integer: + _builder.Append('r'); + break; + case RegisterType.Vector: + _builder.Append('v'); + break; } _builder.Append(reg.Index); @@ -145,9 +155,15 @@ namespace ARMeilleure.Diagnostics switch (memOp.Scale) { - case Multiplier.x2: _builder.Append("*2"); break; - case Multiplier.x4: _builder.Append("*4"); break; - case Multiplier.x8: _builder.Append("*8"); break; + case Multiplier.x2: + _builder.Append("*2"); + break; + case Multiplier.x4: + _builder.Append("*4"); + break; + case Multiplier.x8: + _builder.Append("*8"); + break; } } @@ -308,4 +324,4 @@ namespace ARMeilleure.Diagnostics }; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Diagnostics/Logger.cs b/src/ARMeilleure/Diagnostics/Logger.cs index 07a60667e..d7f61230c 100644 --- a/src/ARMeilleure/Diagnostics/Logger.cs +++ b/src/ARMeilleure/Diagnostics/Logger.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.Diagnostics { private static long _startTime; - private static long[] _accumulatedTime; + private static readonly long[] _accumulatedTime; static Logger() { @@ -53,4 +53,4 @@ namespace ARMeilleure.Diagnostics Console.WriteLine(text); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Diagnostics/PassName.cs b/src/ARMeilleure/Diagnostics/PassName.cs index e34bf0d2f..2d87659f0 100644 --- a/src/ARMeilleure/Diagnostics/PassName.cs +++ b/src/ARMeilleure/Diagnostics/PassName.cs @@ -14,6 +14,6 @@ namespace ARMeilleure.Diagnostics RegisterAllocation, CodeGeneration, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Diagnostics/Symbols.cs b/src/ARMeilleure/Diagnostics/Symbols.cs index 6bde62f56..86469d8bb 100644 --- a/src/ARMeilleure/Diagnostics/Symbols.cs +++ b/src/ARMeilleure/Diagnostics/Symbols.cs @@ -33,9 +33,8 @@ namespace ARMeilleure.Diagnostics public static string Get(ulong address) { - string result; - if (_symbols.TryGetValue(address, out result)) + if (_symbols.TryGetValue(address, out string result)) { return result; } diff --git a/src/ARMeilleure/Diagnostics/TranslatorEventSource.cs b/src/ARMeilleure/Diagnostics/TranslatorEventSource.cs index a4f17844d..6452bf0af 100644 --- a/src/ARMeilleure/Diagnostics/TranslatorEventSource.cs +++ b/src/ARMeilleure/Diagnostics/TranslatorEventSource.cs @@ -19,19 +19,19 @@ namespace ARMeilleure.Diagnostics { _rejitQueueCounter = new PollingCounter("rejit-queue-length", this, () => _rejitQueue) { - DisplayName = "Rejit Queue Length" + DisplayName = "Rejit Queue Length", }; _funcTabSizeCounter = new PollingCounter("addr-tab-alloc", this, () => _funcTabSize / 1024d / 1024d) { DisplayName = "AddressTable Total Bytes Allocated", - DisplayUnits = "MiB" + DisplayUnits = "MiB", }; _funcTabLeafSizeCounter = new PollingCounter("addr-tab-leaf-alloc", this, () => _funcTabLeafSize / 1024d / 1024d) { DisplayName = "AddressTable Total Leaf Bytes Allocated", - DisplayUnits = "MiB" + DisplayUnits = "MiB", }; } diff --git a/src/ARMeilleure/Instructions/CryptoHelper.cs b/src/ARMeilleure/Instructions/CryptoHelper.cs index e517c75d2..ba68cebb3 100644 --- a/src/ARMeilleure/Instructions/CryptoHelper.cs +++ b/src/ARMeilleure/Instructions/CryptoHelper.cs @@ -7,7 +7,8 @@ namespace ARMeilleure.Instructions { static class CryptoHelper { -#region "LookUp Tables" + #region "LookUp Tables" +#pragma warning disable IDE1006 // Naming rule violation private static ReadOnlySpan<byte> _sBox => new byte[] { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, @@ -25,7 +26,7 @@ namespace ARMeilleure.Instructions 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, }; private static ReadOnlySpan<byte> _invSBox => new byte[] @@ -45,7 +46,7 @@ namespace ARMeilleure.Instructions 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, }; private static ReadOnlySpan<byte> _gfMul02 => new byte[] @@ -65,7 +66,7 @@ namespace ARMeilleure.Instructions 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85, 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5, - 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 + 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5, }; private static ReadOnlySpan<byte> _gfMul03 => new byte[] @@ -85,7 +86,7 @@ namespace ARMeilleure.Instructions 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a, 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a, 0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a, - 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a + 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a, }; private static ReadOnlySpan<byte> _gfMul09 => new byte[] @@ -105,7 +106,7 @@ namespace ARMeilleure.Instructions 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed, 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, 0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6, - 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 + 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46, }; private static ReadOnlySpan<byte> _gfMul0B => new byte[] @@ -125,7 +126,7 @@ namespace ARMeilleure.Instructions 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68, 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, 0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13, - 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 + 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3, }; private static ReadOnlySpan<byte> _gfMul0D => new byte[] @@ -145,7 +146,7 @@ namespace ARMeilleure.Instructions 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc, 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, 0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47, - 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 + 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97, }; private static ReadOnlySpan<byte> _gfMul0E => new byte[] @@ -165,23 +166,24 @@ namespace ARMeilleure.Instructions 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6, 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56, 0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d, - 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d + 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d, }; private static ReadOnlySpan<byte> _srPerm => new byte[] { - 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 + 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3, }; private static ReadOnlySpan<byte> _isrPerm => new byte[] { - 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 + 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11, }; -#endregion +#pragma warning restore IDE1006 + #endregion public static V128 AesInvMixColumns(V128 op) { - byte[] inState = op.ToArray(); + byte[] inState = op.ToArray(); byte[] outState = new byte[16]; for (int columns = 0; columns <= 3; columns++) @@ -204,7 +206,7 @@ namespace ARMeilleure.Instructions public static V128 AesInvShiftRows(V128 op) { - byte[] inState = op.ToArray(); + byte[] inState = op.ToArray(); byte[] outState = new byte[16]; for (int idx = 0; idx <= 15; idx++) @@ -217,7 +219,7 @@ namespace ARMeilleure.Instructions public static V128 AesInvSubBytes(V128 op) { - byte[] inState = op.ToArray(); + byte[] inState = op.ToArray(); byte[] outState = new byte[16]; for (int idx = 0; idx <= 15; idx++) @@ -230,7 +232,7 @@ namespace ARMeilleure.Instructions public static V128 AesMixColumns(V128 op) { - byte[] inState = op.ToArray(); + byte[] inState = op.ToArray(); byte[] outState = new byte[16]; for (int columns = 0; columns <= 3; columns++) @@ -253,7 +255,7 @@ namespace ARMeilleure.Instructions public static V128 AesShiftRows(V128 op) { - byte[] inState = op.ToArray(); + byte[] inState = op.ToArray(); byte[] outState = new byte[16]; for (int idx = 0; idx <= 15; idx++) @@ -266,7 +268,7 @@ namespace ARMeilleure.Instructions public static V128 AesSubBytes(V128 op) { - byte[] inState = op.ToArray(); + byte[] inState = op.ToArray(); byte[] outState = new byte[16]; for (int idx = 0; idx <= 15; idx++) diff --git a/src/ARMeilleure/Instructions/InstEmitAlu.cs b/src/ARMeilleure/Instructions/InstEmitAlu.cs index e0d10e77d..ac17c32a7 100644 --- a/src/ARMeilleure/Instructions/InstEmitAlu.cs +++ b/src/ARMeilleure/Instructions/InstEmitAlu.cs @@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System.Diagnostics; - using static ARMeilleure.Instructions.InstEmitAluHelper; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -12,7 +11,7 @@ namespace ARMeilleure.Instructions { static partial class InstEmit { - public static void Adc(ArmEmitterContext context) => EmitAdc(context, setFlags: false); + public static void Adc(ArmEmitterContext context) => EmitAdc(context, setFlags: false); public static void Adcs(ArmEmitterContext context) => EmitAdc(context, setFlags: true); private static void EmitAdc(ArmEmitterContext context, bool setFlags) @@ -87,7 +86,7 @@ namespace ARMeilleure.Instructions SetAluDOrZR(context, context.ShiftRightSI(GetAluN(context), GetAluMShift(context))); } - public static void Bic(ArmEmitterContext context) => EmitBic(context, setFlags: false); + public static void Bic(ArmEmitterContext context) => EmitBic(context, setFlags: false); public static void Bics(ArmEmitterContext context) => EmitBic(context, setFlags: true); private static void EmitBic(ArmEmitterContext context, bool setFlags) @@ -190,7 +189,7 @@ namespace ARMeilleure.Instructions SetAluDOrZR(context, context.ShiftRightUI(GetAluN(context), GetAluMShift(context))); } - public static void Sbc(ArmEmitterContext context) => EmitSbc(context, setFlags: false); + public static void Sbc(ArmEmitterContext context) => EmitSbc(context, setFlags: false); public static void Sbcs(ArmEmitterContext context) => EmitSbc(context, setFlags: true); private static void EmitSbc(ArmEmitterContext context, bool setFlags) @@ -281,16 +280,16 @@ namespace ARMeilleure.Instructions Debug.Assert(op.Type == OperandType.I64); Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaaaaaaaaaaul)), Const(1)), - context.ShiftLeft (context.BitwiseAnd(op, Const(0x5555555555555555ul)), Const(1))); + context.ShiftLeft(context.BitwiseAnd(op, Const(0x5555555555555555ul)), Const(1))); val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccccccccccul)), Const(2)), - context.ShiftLeft (context.BitwiseAnd(val, Const(0x3333333333333333ul)), Const(2))); + context.ShiftLeft(context.BitwiseAnd(val, Const(0x3333333333333333ul)), Const(2))); val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0f0f0f0f0ul)), Const(4)), - context.ShiftLeft (context.BitwiseAnd(val, Const(0x0f0f0f0f0f0f0f0ful)), Const(4))); + context.ShiftLeft(context.BitwiseAnd(val, Const(0x0f0f0f0f0f0f0f0ful)), Const(4))); val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00ff00ff00ul)), Const(8)), - context.ShiftLeft (context.BitwiseAnd(val, Const(0x00ff00ff00ff00fful)), Const(8))); + context.ShiftLeft(context.BitwiseAnd(val, Const(0x00ff00ff00ff00fful)), Const(8))); val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xffff0000ffff0000ul)), Const(16)), - context.ShiftLeft (context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16))); + context.ShiftLeft(context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16))); return context.BitwiseOr(context.ShiftRightUI(val, Const(32)), context.ShiftLeft(val, Const(32))); } @@ -340,7 +339,7 @@ namespace ARMeilleure.Instructions Operand val = EmitReverseBytes16_64Op(context, op); return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xffff0000ffff0000ul)), Const(16)), - context.ShiftLeft (context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16))); + context.ShiftLeft(context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16))); } public static void Rev64(ArmEmitterContext context) diff --git a/src/ARMeilleure/Instructions/InstEmitAlu32.cs b/src/ARMeilleure/Instructions/InstEmitAlu32.cs index 584ada7e0..3a5e71bcc 100644 --- a/src/ARMeilleure/Instructions/InstEmitAlu32.cs +++ b/src/ARMeilleure/Instructions/InstEmitAlu32.cs @@ -2,13 +2,14 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; - +using System.Diagnostics.CodeAnalysis; using static ARMeilleure.Instructions.InstEmitAluHelper; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Instructions { + [SuppressMessage("Style", "IDE0059: Remove unnecessary value assignment")] static partial class InstEmit32 { public static void Add(ArmEmitterContext context) diff --git a/src/ARMeilleure/Instructions/InstEmitAluHelper.cs b/src/ARMeilleure/Instructions/InstEmitAluHelper.cs index 994878ad7..4d4a31f7b 100644 --- a/src/ARMeilleure/Instructions/InstEmitAluHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitAluHelper.cs @@ -26,7 +26,7 @@ namespace ARMeilleure.Instructions public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d) { - SetFlag(context, PState.NFlag, context.ICompareLess (d, Const(d.Type, 0))); + SetFlag(context, PState.NFlag, context.ICompareLess(d, Const(d.Type, 0))); SetFlag(context, PState.ZFlag, context.ICompareEqual(d, Const(d.Type, 0))); } @@ -196,60 +196,73 @@ namespace ARMeilleure.Instructions { // ARM32. case IOpCode32AluImm op: - { - if (ShouldSetFlags(context) && op.IsRotated && setCarry) { - SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31)); + if (ShouldSetFlags(context) && op.IsRotated && setCarry) + { + SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31)); + } + + return Const(op.Immediate); } + case IOpCode32AluImm16 op: return Const(op.Immediate); - } - case IOpCode32AluImm16 op: return Const(op.Immediate); + case IOpCode32AluRsImm op: + return GetMShiftedByImmediate(context, op, setCarry); + case IOpCode32AluRsReg op: + return GetMShiftedByReg(context, op, setCarry); - case IOpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry); - case IOpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry); - - case IOpCode32AluReg op: return GetIntA32(context, op.Rm); + case IOpCode32AluReg op: + return GetIntA32(context, op.Rm); // ARM64. case IOpCodeAluImm op: - { - if (op.GetOperandType() == OperandType.I32) { - return Const((int)op.Immediate); + if (op.GetOperandType() == OperandType.I32) + { + return Const((int)op.Immediate); + } + else + { + return Const(op.Immediate); + } } - else - { - return Const(op.Immediate); - } - } case IOpCodeAluRs op: - { - Operand value = GetIntOrZR(context, op.Rm); - - switch (op.ShiftType) { - case ShiftType.Lsl: value = context.ShiftLeft (value, Const(op.Shift)); break; - case ShiftType.Lsr: value = context.ShiftRightUI(value, Const(op.Shift)); break; - case ShiftType.Asr: value = context.ShiftRightSI(value, Const(op.Shift)); break; - case ShiftType.Ror: value = context.RotateRight (value, Const(op.Shift)); break; + Operand value = GetIntOrZR(context, op.Rm); + + switch (op.ShiftType) + { + case ShiftType.Lsl: + value = context.ShiftLeft(value, Const(op.Shift)); + break; + case ShiftType.Lsr: + value = context.ShiftRightUI(value, Const(op.Shift)); + break; + case ShiftType.Asr: + value = context.ShiftRightSI(value, Const(op.Shift)); + break; + case ShiftType.Ror: + value = context.RotateRight(value, Const(op.Shift)); + break; + } + + return value; } - return value; - } - case IOpCodeAluRx op: - { - Operand value = GetExtendedM(context, op.Rm, op.IntType); + { + Operand value = GetExtendedM(context, op.Rm, op.IntType); - value = context.ShiftLeft(value, Const(op.Shift)); + value = context.ShiftLeft(value, Const(op.Shift)); - return value; - } + return value; + } - default: throw InvalidOpCodeType(context.CurrOp); + default: + throw InvalidOpCodeType(context.CurrOp); } } @@ -269,9 +282,15 @@ namespace ARMeilleure.Instructions { switch (op.ShiftType) { - case ShiftType.Lsr: shift = 32; break; - case ShiftType.Asr: shift = 32; break; - case ShiftType.Ror: shift = 1; break; + case ShiftType.Lsr: + shift = 32; + break; + case ShiftType.Asr: + shift = 32; + break; + case ShiftType.Ror: + shift = 1; + break; } } @@ -281,9 +300,15 @@ namespace ARMeilleure.Instructions switch (op.ShiftType) { - case ShiftType.Lsl: m = GetLslC(context, m, setCarry, shift); break; - case ShiftType.Lsr: m = GetLsrC(context, m, setCarry, shift); break; - case ShiftType.Asr: m = GetAsrC(context, m, setCarry, shift); break; + case ShiftType.Lsl: + m = GetLslC(context, m, setCarry, shift); + break; + case ShiftType.Lsr: + m = GetLsrC(context, m, setCarry, shift); + break; + case ShiftType.Asr: + m = GetAsrC(context, m, setCarry, shift); + break; case ShiftType.Ror: if (op.Immediate != 0) { @@ -306,9 +331,15 @@ namespace ARMeilleure.Instructions { switch (shiftType) { - case ShiftType.Lsr: shift = 32; break; - case ShiftType.Asr: shift = 32; break; - case ShiftType.Ror: shift = 1; break; + case ShiftType.Lsr: + shift = 32; + break; + case ShiftType.Asr: + shift = 32; + break; + case ShiftType.Ror: + shift = 1; + break; } } @@ -328,10 +359,18 @@ namespace ARMeilleure.Instructions switch (op.ShiftType) { - case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break; - case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break; - case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break; - case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break; + case ShiftType.Lsl: + shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); + break; + case ShiftType.Lsr: + shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); + break; + case ShiftType.Asr: + shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); + break; + case ShiftType.Ror: + shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); + break; } return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult); diff --git a/src/ARMeilleure/Instructions/InstEmitBfm.cs b/src/ARMeilleure/Instructions/InstEmitBfm.cs index 46a7ddddd..aaf228756 100644 --- a/src/ARMeilleure/Instructions/InstEmitBfm.cs +++ b/src/ARMeilleure/Instructions/InstEmitBfm.cs @@ -84,9 +84,9 @@ namespace ARMeilleure.Instructions { Operand res = GetIntOrZR(context, op.Rn); - res = context.ShiftLeft (res, Const(bitsCount - 1 - op.Pos)); + res = context.ShiftLeft(res, Const(bitsCount - 1 - op.Pos)); res = context.ShiftRightSI(res, Const(bitsCount - 1)); - res = context.BitwiseAnd (res, Const(res.Type, ~op.TMask)); + res = context.BitwiseAnd(res, Const(res.Type, ~op.TMask)); Operand n2 = GetBfmN(context); @@ -193,4 +193,4 @@ namespace ARMeilleure.Instructions return context.BitwiseAnd(context.RotateRight(res, Const(op.Shift)), Const(res.Type, mask)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitCcmp.cs b/src/ARMeilleure/Instructions/InstEmitCcmp.cs index 7f0beb6cb..a71fc2689 100644 --- a/src/ARMeilleure/Instructions/InstEmitCcmp.cs +++ b/src/ARMeilleure/Instructions/InstEmitCcmp.cs @@ -2,7 +2,6 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; - using static ARMeilleure.Instructions.InstEmitAluHelper; using static ARMeilleure.Instructions.InstEmitFlowHelper; using static ARMeilleure.Instructions.InstEmitHelper; @@ -20,7 +19,7 @@ namespace ARMeilleure.Instructions OpCodeCcmp op = (OpCodeCcmp)context.CurrOp; Operand lblTrue = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); EmitCondBranch(context, lblTrue, op.Cond); @@ -58,4 +57,4 @@ namespace ARMeilleure.Instructions context.MarkLabel(lblEnd); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitCsel.cs b/src/ARMeilleure/Instructions/InstEmitCsel.cs index 926b9a9ed..1cd936b31 100644 --- a/src/ARMeilleure/Instructions/InstEmitCsel.cs +++ b/src/ARMeilleure/Instructions/InstEmitCsel.cs @@ -1,7 +1,6 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; - using static ARMeilleure.Instructions.InstEmitFlowHelper; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -15,10 +14,10 @@ namespace ARMeilleure.Instructions None, Increment, Invert, - Negate + Negate, } - public static void Csel(ArmEmitterContext context) => EmitCsel(context, CselOperation.None); + public static void Csel(ArmEmitterContext context) => EmitCsel(context, CselOperation.None); public static void Csinc(ArmEmitterContext context) => EmitCsel(context, CselOperation.Increment); public static void Csinv(ArmEmitterContext context) => EmitCsel(context, CselOperation.Invert); public static void Csneg(ArmEmitterContext context) => EmitCsel(context, CselOperation.Negate); @@ -50,4 +49,4 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rd, d); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitDiv.cs b/src/ARMeilleure/Instructions/InstEmitDiv.cs index 39a5c32e6..728462ed4 100644 --- a/src/ARMeilleure/Instructions/InstEmitDiv.cs +++ b/src/ARMeilleure/Instructions/InstEmitDiv.cs @@ -1,7 +1,6 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -23,7 +22,7 @@ namespace ARMeilleure.Instructions Operand divisorIsZero = context.ICompareEqual(m, Const(m.Type, 0)); Operand lblBadDiv = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); context.BranchIfTrue(lblBadDiv, divisorIsZero); @@ -33,7 +32,7 @@ namespace ARMeilleure.Instructions bool is32Bits = op.RegisterSize == RegisterSize.Int32; Operand intMin = is32Bits ? Const(int.MinValue) : Const(long.MinValue); - Operand minus1 = is32Bits ? Const(-1) : Const(-1L); + Operand minus1 = is32Bits ? Const(-1) : Const(-1L); Operand nIsIntMin = context.ICompareEqual(n, intMin); Operand mIsMinus1 = context.ICompareEqual(m, minus1); @@ -51,7 +50,7 @@ namespace ARMeilleure.Instructions Operand d = unsigned ? context.DivideUI(n, m) - : context.Divide (n, m); + : context.Divide(n, m); SetAluDOrZR(context, d); diff --git a/src/ARMeilleure/Instructions/InstEmitException.cs b/src/ARMeilleure/Instructions/InstEmitException.cs index 0baaa87d7..d30fb2fbd 100644 --- a/src/ARMeilleure/Instructions/InstEmitException.cs +++ b/src/ARMeilleure/Instructions/InstEmitException.cs @@ -52,4 +52,4 @@ namespace ARMeilleure.Instructions context.Return(Const(op.Address)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitFlow.cs b/src/ARMeilleure/Instructions/InstEmitFlow.cs index c40eb55cf..a986bf66f 100644 --- a/src/ARMeilleure/Instructions/InstEmitFlow.cs +++ b/src/ARMeilleure/Instructions/InstEmitFlow.cs @@ -53,7 +53,7 @@ namespace ARMeilleure.Instructions } public static void Cbnz(ArmEmitterContext context) => EmitCb(context, onNotZero: true); - public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false); + public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false); private static void EmitCb(ArmEmitterContext context, bool onNotZero) { @@ -70,7 +70,7 @@ namespace ARMeilleure.Instructions } public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true); - public static void Tbz(ArmEmitterContext context) => EmitTb(context, onNotZero: false); + public static void Tbz(ArmEmitterContext context) => EmitTb(context, onNotZero: false); private static void EmitTb(ArmEmitterContext context, bool onNotZero) { @@ -104,4 +104,4 @@ namespace ARMeilleure.Instructions } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitFlow32.cs b/src/ARMeilleure/Instructions/InstEmitFlow32.cs index 3a7707ee9..289d3f483 100644 --- a/src/ARMeilleure/Instructions/InstEmitFlow32.cs +++ b/src/ARMeilleure/Instructions/InstEmitFlow32.cs @@ -82,7 +82,7 @@ namespace ARMeilleure.Instructions } public static void Cbnz(ArmEmitterContext context) => EmitCb(context, onNotZero: true); - public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false); + public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false); private static void EmitCb(ArmEmitterContext context, bool onNotZero) { @@ -109,7 +109,7 @@ namespace ARMeilleure.Instructions } public static void Tbb(ArmEmitterContext context) => EmitTb(context, halfword: false); - public static void Tbh(ArmEmitterContext context) => EmitTb(context, halfword: true); + public static void Tbh(ArmEmitterContext context) => EmitTb(context, halfword: true); private static void EmitTb(ArmEmitterContext context, bool halfword) { @@ -133,4 +133,4 @@ namespace ARMeilleure.Instructions EmitVirtualJump(context, targetAddress, isReturn: false); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs index 6ac329085..2009bafda 100644 --- a/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs @@ -75,66 +75,66 @@ namespace ARMeilleure.Instructions break; case Condition.GtUn: - { - Operand c = GetFlag(PState.CFlag); - Operand z = GetFlag(PState.ZFlag); + { + Operand c = GetFlag(PState.CFlag); + Operand z = GetFlag(PState.ZFlag); - value = context.BitwiseAnd(c, Inverse(z)); + value = context.BitwiseAnd(c, Inverse(z)); - break; - } + break; + } case Condition.LeUn: - { - Operand c = GetFlag(PState.CFlag); - Operand z = GetFlag(PState.ZFlag); + { + Operand c = GetFlag(PState.CFlag); + Operand z = GetFlag(PState.ZFlag); - value = context.BitwiseOr(Inverse(c), z); + value = context.BitwiseOr(Inverse(c), z); - break; - } + break; + } case Condition.Ge: - { - Operand n = GetFlag(PState.NFlag); - Operand v = GetFlag(PState.VFlag); + { + Operand n = GetFlag(PState.NFlag); + Operand v = GetFlag(PState.VFlag); - value = context.ICompareEqual(n, v); + value = context.ICompareEqual(n, v); - break; - } + break; + } case Condition.Lt: - { - Operand n = GetFlag(PState.NFlag); - Operand v = GetFlag(PState.VFlag); + { + Operand n = GetFlag(PState.NFlag); + Operand v = GetFlag(PState.VFlag); - value = context.ICompareNotEqual(n, v); + value = context.ICompareNotEqual(n, v); - break; - } + break; + } case Condition.Gt: - { - Operand n = GetFlag(PState.NFlag); - Operand z = GetFlag(PState.ZFlag); - Operand v = GetFlag(PState.VFlag); + { + Operand n = GetFlag(PState.NFlag); + Operand z = GetFlag(PState.ZFlag); + Operand v = GetFlag(PState.VFlag); - value = context.BitwiseAnd(Inverse(z), context.ICompareEqual(n, v)); + value = context.BitwiseAnd(Inverse(z), context.ICompareEqual(n, v)); - break; - } + break; + } case Condition.Le: - { - Operand n = GetFlag(PState.NFlag); - Operand z = GetFlag(PState.ZFlag); - Operand v = GetFlag(PState.VFlag); + { + Operand n = GetFlag(PState.NFlag); + Operand z = GetFlag(PState.ZFlag); + Operand v = GetFlag(PState.VFlag); - value = context.BitwiseOr(z, context.ICompareNotEqual(n, v)); + value = context.BitwiseOr(z, context.ICompareNotEqual(n, v)); - break; - } + break; + } } return value; diff --git a/src/ARMeilleure/Instructions/InstEmitHashHelper.cs b/src/ARMeilleure/Instructions/InstEmitHashHelper.cs index 55a03a4f6..9218e1ae0 100644 --- a/src/ARMeilleure/Instructions/InstEmitHashHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitHashHelper.cs @@ -45,7 +45,7 @@ namespace ARMeilleure.Instructions } else { - string name = (size, castagnoli) switch + string name = (size, castagnoli) switch { (0, false) => nameof(SoftFallback.Crc32b), (1, false) => nameof(SoftFallback.Crc32h), @@ -55,7 +55,7 @@ namespace ARMeilleure.Instructions (1, true) => nameof(SoftFallback.Crc32ch), (2, true) => nameof(SoftFallback.Crc32cw), (3, true) => nameof(SoftFallback.Crc32cx), - _ => throw new ArgumentOutOfRangeException(nameof(size)) + _ => throw new ArgumentOutOfRangeException(nameof(size)), }; return context.Call(typeof(SoftFallback).GetMethod(name), crc, value); @@ -71,9 +71,15 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: data = context.VectorInsert8(context.VectorZero(), data, 0); break; - case 1: data = context.VectorInsert16(context.VectorZero(), data, 0); break; - case 2: data = context.VectorInsert(context.VectorZero(), data, 0); break; + case 0: + data = context.VectorInsert8(context.VectorZero(), data, 0); + break; + case 1: + data = context.VectorInsert16(context.VectorZero(), data, 0); + break; + case 2: + data = context.VectorInsert(context.VectorZero(), data, 0); + break; } int bitsize = 8 << size; diff --git a/src/ARMeilleure/Instructions/InstEmitHelper.cs b/src/ARMeilleure/Instructions/InstEmitHelper.cs index a22bb3fb7..7a515f94f 100644 --- a/src/ARMeilleure/Instructions/InstEmitHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitHelper.cs @@ -16,13 +16,25 @@ namespace ARMeilleure.Instructions switch (type) { - case IntType.UInt8: value = context.ZeroExtend8 (value.Type, value); break; - case IntType.UInt16: value = context.ZeroExtend16(value.Type, value); break; - case IntType.UInt32: value = context.ZeroExtend32(value.Type, value); break; + case IntType.UInt8: + value = context.ZeroExtend8(value.Type, value); + break; + case IntType.UInt16: + value = context.ZeroExtend16(value.Type, value); + break; + case IntType.UInt32: + value = context.ZeroExtend32(value.Type, value); + break; - case IntType.Int8: value = context.SignExtend8 (value.Type, value); break; - case IntType.Int16: value = context.SignExtend16(value.Type, value); break; - case IntType.Int32: value = context.SignExtend32(value.Type, value); break; + case IntType.Int8: + value = context.SignExtend8(value.Type, value); + break; + case IntType.Int16: + value = context.SignExtend16(value.Type, value); + break; + case IntType.Int32: + value = context.SignExtend32(value.Type, value); + break; } return value; @@ -100,78 +112,51 @@ namespace ARMeilleure.Instructions public static int GetBankedRegisterAlias(Aarch32Mode mode, int regIndex) { - switch (regIndex) + return regIndex switch { - case 8: return mode == Aarch32Mode.Fiq - ? RegisterAlias.R8Fiq - : RegisterAlias.R8Usr; - - case 9: return mode == Aarch32Mode.Fiq - ? RegisterAlias.R9Fiq - : RegisterAlias.R9Usr; - - case 10: return mode == Aarch32Mode.Fiq - ? RegisterAlias.R10Fiq - : RegisterAlias.R10Usr; - - case 11: return mode == Aarch32Mode.Fiq - ? RegisterAlias.R11Fiq - : RegisterAlias.R11Usr; - - case 12: return mode == Aarch32Mode.Fiq - ? RegisterAlias.R12Fiq - : RegisterAlias.R12Usr; - - case 13: - switch (mode) - { - case Aarch32Mode.User: - case Aarch32Mode.System: return RegisterAlias.SpUsr; - case Aarch32Mode.Fiq: return RegisterAlias.SpFiq; - case Aarch32Mode.Irq: return RegisterAlias.SpIrq; - case Aarch32Mode.Supervisor: return RegisterAlias.SpSvc; - case Aarch32Mode.Abort: return RegisterAlias.SpAbt; - case Aarch32Mode.Hypervisor: return RegisterAlias.SpHyp; - case Aarch32Mode.Undefined: return RegisterAlias.SpUnd; - - default: throw new ArgumentException(nameof(mode)); - } - - case 14: - switch (mode) - { - case Aarch32Mode.User: - case Aarch32Mode.Hypervisor: - case Aarch32Mode.System: return RegisterAlias.LrUsr; - case Aarch32Mode.Fiq: return RegisterAlias.LrFiq; - case Aarch32Mode.Irq: return RegisterAlias.LrIrq; - case Aarch32Mode.Supervisor: return RegisterAlias.LrSvc; - case Aarch32Mode.Abort: return RegisterAlias.LrAbt; - case Aarch32Mode.Undefined: return RegisterAlias.LrUnd; - - default: throw new ArgumentException(nameof(mode)); - } - - default: throw new ArgumentOutOfRangeException(nameof(regIndex)); - } +#pragma warning disable IDE0055 // Disable formatting + 8 => mode == Aarch32Mode.Fiq ? RegisterAlias.R8Fiq : RegisterAlias.R8Usr, + 9 => mode == Aarch32Mode.Fiq ? RegisterAlias.R9Fiq : RegisterAlias.R9Usr, + 10 => mode == Aarch32Mode.Fiq ? RegisterAlias.R10Fiq : RegisterAlias.R10Usr, + 11 => mode == Aarch32Mode.Fiq ? RegisterAlias.R11Fiq : RegisterAlias.R11Usr, + 12 => mode == Aarch32Mode.Fiq ? RegisterAlias.R12Fiq : RegisterAlias.R12Usr, + 13 => mode switch + { + Aarch32Mode.User or Aarch32Mode.System => RegisterAlias.SpUsr, + Aarch32Mode.Fiq => RegisterAlias.SpFiq, + Aarch32Mode.Irq => RegisterAlias.SpIrq, + Aarch32Mode.Supervisor => RegisterAlias.SpSvc, + Aarch32Mode.Abort => RegisterAlias.SpAbt, + Aarch32Mode.Hypervisor => RegisterAlias.SpHyp, + Aarch32Mode.Undefined => RegisterAlias.SpUnd, + _ => throw new ArgumentException($"No such AArch32Mode: {mode}", nameof(mode)), + }, + 14 => mode switch + { + Aarch32Mode.User or Aarch32Mode.Hypervisor or Aarch32Mode.System => RegisterAlias.LrUsr, + Aarch32Mode.Fiq => RegisterAlias.LrFiq, + Aarch32Mode.Irq => RegisterAlias.LrIrq, + Aarch32Mode.Supervisor => RegisterAlias.LrSvc, + Aarch32Mode.Abort => RegisterAlias.LrAbt, + Aarch32Mode.Undefined => RegisterAlias.LrUnd, + _ => throw new ArgumentException($"No such AArch32Mode: {mode}", nameof(mode)), + }, + _ => throw new ArgumentOutOfRangeException(nameof(regIndex), regIndex, null), +#pragma warning restore IDE0055 + }; } public static bool IsA32Return(ArmEmitterContext context) { - switch (context.CurrOp) + return context.CurrOp switch { - case IOpCode32MemMult op: - return true; // Setting PC using LDM is nearly always a return. - case OpCode32AluRsImm op: - return op.Rm == RegisterAlias.Aarch32Lr; - case OpCode32AluRsReg op: - return op.Rm == RegisterAlias.Aarch32Lr; - case OpCode32AluReg op: - return op.Rm == RegisterAlias.Aarch32Lr; - case OpCode32Mem op: - return op.Rn == RegisterAlias.Aarch32Sp && op.WBack && !op.Index; // Setting PC to an address stored on the stack is nearly always a return. - } - return false; + IOpCode32MemMult => true, // Setting PC using LDM is nearly always a return. + OpCode32AluRsImm op => op.Rm == RegisterAlias.Aarch32Lr, + OpCode32AluRsReg op => op.Rm == RegisterAlias.Aarch32Lr, + OpCode32AluReg op => op.Rm == RegisterAlias.Aarch32Lr, + OpCode32Mem op => op.Rn == RegisterAlias.Aarch32Sp && op.WBack && !op.Index, // Setting PC to an address stored on the stack is nearly always a return. + _ => false, + }; } public static void EmitBxWritePc(ArmEmitterContext context, Operand pc, int sourceRegister = 0) diff --git a/src/ARMeilleure/Instructions/InstEmitMemory.cs b/src/ARMeilleure/Instructions/InstEmitMemory.cs index 7baed14c8..840099f9c 100644 --- a/src/ARMeilleure/Instructions/InstEmitMemory.cs +++ b/src/ARMeilleure/Instructions/InstEmitMemory.cs @@ -26,7 +26,7 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rd, Const(address)); } - public static void Ldr(ArmEmitterContext context) => EmitLdr(context, signed: false); + public static void Ldr(ArmEmitterContext context) => EmitLdr(context, signed: false); public static void Ldrs(ArmEmitterContext context) => EmitLdr(context, signed: true); private static void EmitLdr(ArmEmitterContext context, bool signed) @@ -89,7 +89,7 @@ namespace ARMeilleure.Instructions Operand address = GetAddress(context); Operand address2 = GetAddress(context, 1L << op.Size); - EmitLoad(op.Rt, address); + EmitLoad(op.Rt, address); EmitLoad(op.Rt2, address2); EmitWBackIfNeeded(context, address); @@ -113,7 +113,7 @@ namespace ARMeilleure.Instructions Operand address = GetAddress(context); Operand address2 = GetAddress(context, 1L << op.Size); - EmitStore(context, address, op.Rt, op.Size); + EmitStore(context, address, op.Rt, op.Size); EmitStore(context, address2, op.Rt2, op.Size); EmitWBackIfNeeded(context, address); @@ -126,42 +126,42 @@ namespace ARMeilleure.Instructions switch (context.CurrOp) { case OpCodeMemImm op: - { - address = context.Copy(GetIntOrSP(context, op.Rn)); - - // Pre-indexing. - if (!op.PostIdx) { - address = context.Add(address, Const(op.Immediate + addend)); - } - else if (addend != 0) - { - address = context.Add(address, Const(addend)); - } + address = context.Copy(GetIntOrSP(context, op.Rn)); - break; - } + // Pre-indexing. + if (!op.PostIdx) + { + address = context.Add(address, Const(op.Immediate + addend)); + } + else if (addend != 0) + { + address = context.Add(address, Const(addend)); + } + + break; + } case OpCodeMemReg op: - { - Operand n = GetIntOrSP(context, op.Rn); - - Operand m = GetExtendedM(context, op.Rm, op.IntType); - - if (op.Shift) { - m = context.ShiftLeft(m, Const(op.Size)); + Operand n = GetIntOrSP(context, op.Rn); + + Operand m = GetExtendedM(context, op.Rm, op.IntType); + + if (op.Shift) + { + m = context.ShiftLeft(m, Const(op.Size)); + } + + address = context.Add(n, m); + + if (addend != 0) + { + address = context.Add(address, Const(addend)); + } + + break; } - - address = context.Add(n, m); - - if (addend != 0) - { - address = context.Add(address, Const(addend)); - } - - break; - } } return address; @@ -181,4 +181,4 @@ namespace ARMeilleure.Instructions } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitMemory32.cs b/src/ARMeilleure/Instructions/InstEmitMemory32.cs index 17ec97aa6..cee06700d 100644 --- a/src/ARMeilleure/Instructions/InstEmitMemory32.cs +++ b/src/ARMeilleure/Instructions/InstEmitMemory32.cs @@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitMemoryHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -12,18 +11,18 @@ namespace ARMeilleure.Instructions { static partial class InstEmit32 { - private const int ByteSizeLog2 = 0; + private const int ByteSizeLog2 = 0; private const int HWordSizeLog2 = 1; - private const int WordSizeLog2 = 2; + private const int WordSizeLog2 = 2; private const int DWordSizeLog2 = 3; [Flags] enum AccessType { - Store = 0, - Signed = 1, - Load = 2, - Ordered = 4, + Store = 0, + Signed = 1, + Load = 2, + Ordered = 4, Exclusive = 8, LoadZx = Load, @@ -47,7 +46,7 @@ namespace ARMeilleure.Instructions SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset))); } - int mask = op.RegisterMask; + int mask = op.RegisterMask; int offset = 0; for (int register = 0; mask != 0; mask >>= 1, register++) @@ -101,7 +100,7 @@ namespace ARMeilleure.Instructions Operand baseAddress = context.Add(n, Const(op.Offset)); - int mask = op.RegisterMask; + int mask = op.RegisterMask; int offset = 0; for (int register = 0; mask != 0; mask >>= 1, register++) @@ -161,7 +160,7 @@ namespace ARMeilleure.Instructions if (op.Index || op.WBack) { temp = op.Add - ? context.Add (n, m) + ? context.Add(n, m) : context.Subtract(n, m); } @@ -200,7 +199,7 @@ namespace ARMeilleure.Instructions if (size == DWordSizeLog2) { Operand lblBigEndian = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); @@ -233,7 +232,7 @@ namespace ARMeilleure.Instructions if (size == DWordSizeLog2) { Operand lblBigEndian = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); @@ -262,4 +261,4 @@ namespace ARMeilleure.Instructions SetIntA32(context, op.Rd, Const(op.Immediate)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitMemoryEx.cs b/src/ARMeilleure/Instructions/InstEmitMemoryEx.cs index c7ed01e34..8c95b33c5 100644 --- a/src/ARMeilleure/Instructions/InstEmitMemoryEx.cs +++ b/src/ARMeilleure/Instructions/InstEmitMemoryEx.cs @@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; using System.Diagnostics; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitMemoryExHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -15,10 +14,10 @@ namespace ARMeilleure.Instructions [Flags] private enum AccessType { - None = 0, - Ordered = 1, + None = 0, + Ordered = 1, Exclusive = 2, - OrderedEx = Ordered | Exclusive + OrderedEx = Ordered | Exclusive, } public static void Clrex(ArmEmitterContext context) @@ -34,10 +33,10 @@ namespace ARMeilleure.Instructions public static void Dmb(ArmEmitterContext context) => EmitBarrier(context); public static void Dsb(ArmEmitterContext context) => EmitBarrier(context); - public static void Ldar(ArmEmitterContext context) => EmitLdr(context, AccessType.Ordered); + public static void Ldar(ArmEmitterContext context) => EmitLdr(context, AccessType.Ordered); public static void Ldaxr(ArmEmitterContext context) => EmitLdr(context, AccessType.OrderedEx); - public static void Ldxr(ArmEmitterContext context) => EmitLdr(context, AccessType.Exclusive); - public static void Ldxp(ArmEmitterContext context) => EmitLdp(context, AccessType.Exclusive); + public static void Ldxr(ArmEmitterContext context) => EmitLdr(context, AccessType.Exclusive); + public static void Ldxp(ArmEmitterContext context) => EmitLdp(context, AccessType.Exclusive); public static void Ldaxp(ArmEmitterContext context) => EmitLdp(context, AccessType.OrderedEx); private static void EmitLdr(ArmEmitterContext context, AccessType accType) @@ -54,7 +53,7 @@ namespace ARMeilleure.Instructions { OpCodeMemEx op = (OpCodeMemEx)context.CurrOp; - bool ordered = (accType & AccessType.Ordered) != 0; + bool ordered = (accType & AccessType.Ordered) != 0; bool exclusive = (accType & AccessType.Exclusive) != 0; if (ordered) @@ -80,17 +79,17 @@ namespace ARMeilleure.Instructions Operand valueHigh = context.ShiftRightUI(value, Const(32)); - SetIntOrZR(context, op.Rt, valueLow); + SetIntOrZR(context, op.Rt, valueLow); SetIntOrZR(context, op.Rt2, valueHigh); } else if (op.Size == 3) { Operand value = EmitLoadExclusive(context, address, exclusive, 4); - Operand valueLow = context.VectorExtract(OperandType.I64, value, 0); + Operand valueLow = context.VectorExtract(OperandType.I64, value, 0); Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1); - SetIntOrZR(context, op.Rt, valueLow); + SetIntOrZR(context, op.Rt, valueLow); SetIntOrZR(context, op.Rt2, valueHigh); } else @@ -112,10 +111,10 @@ namespace ARMeilleure.Instructions // Memory Prefetch, execute as no-op. } - public static void Stlr(ArmEmitterContext context) => EmitStr(context, AccessType.Ordered); + public static void Stlr(ArmEmitterContext context) => EmitStr(context, AccessType.Ordered); public static void Stlxr(ArmEmitterContext context) => EmitStr(context, AccessType.OrderedEx); - public static void Stxr(ArmEmitterContext context) => EmitStr(context, AccessType.Exclusive); - public static void Stxp(ArmEmitterContext context) => EmitStp(context, AccessType.Exclusive); + public static void Stxr(ArmEmitterContext context) => EmitStr(context, AccessType.Exclusive); + public static void Stxp(ArmEmitterContext context) => EmitStp(context, AccessType.Exclusive); public static void Stlxp(ArmEmitterContext context) => EmitStp(context, AccessType.OrderedEx); private static void EmitStr(ArmEmitterContext context, AccessType accType) @@ -132,7 +131,7 @@ namespace ARMeilleure.Instructions { OpCodeMemEx op = (OpCodeMemEx)context.CurrOp; - bool ordered = (accType & AccessType.Ordered) != 0; + bool ordered = (accType & AccessType.Ordered) != 0; bool exclusive = (accType & AccessType.Exclusive) != 0; Operand address = context.Copy(GetIntOrSP(context, op.Rn)); @@ -153,8 +152,8 @@ namespace ARMeilleure.Instructions } else /* if (op.Size == 3) */ { - value = context.VectorInsert(context.VectorZero(), t, 0); - value = context.VectorInsert(value, t2, 1); + value = context.VectorInsert(context.VectorZero(), t, 0); + value = context.VectorInsert(value, t2, 1); } EmitStoreExclusive(context, address, value, exclusive, op.Size + 1, op.Rs, a32: false); @@ -175,4 +174,4 @@ namespace ARMeilleure.Instructions context.MemoryBarrier(); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs b/src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs index 9a69442a6..c9a99a3b5 100644 --- a/src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs @@ -1,7 +1,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -33,7 +32,7 @@ namespace ARMeilleure.Instructions Operand arg0 = context.LoadArgument(OperandType.I64, 0); - Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset())); + Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset())); Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset())); context.Store(exAddrPtr, context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask()))); @@ -118,14 +117,14 @@ namespace ARMeilleure.Instructions 1 => context.Load16(exValuePtr), 2 => context.Load(OperandType.I32, exValuePtr), 3 => context.Load(OperandType.I64, exValuePtr), - _ => context.Load(OperandType.V128, exValuePtr) + _ => context.Load(OperandType.V128, exValuePtr), }; Operand currValue = size switch { 0 => context.CompareAndSwap8(physAddr, exValue, value), 1 => context.CompareAndSwap16(physAddr, exValue, value), - _ => context.CompareAndSwap(physAddr, exValue, value) + _ => context.CompareAndSwap(physAddr, exValue, value), }; // STEP 3: Check if we succeeded by comparing expected and in-memory values. @@ -133,14 +132,14 @@ namespace ARMeilleure.Instructions if (size == 4) { - Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0); + Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0); Operand currValueHigh = context.VectorExtract(OperandType.I64, currValue, 1); - Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0); + Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0); Operand exValueHigh = context.VectorExtract(OperandType.I64, exValue, 1); storeFailed = context.BitwiseOr( - context.ICompareNotEqual(currValueLow, exValueLow), + context.ICompareNotEqual(currValueLow, exValueLow), context.ICompareNotEqual(currValueHigh, exValueHigh)); } else diff --git a/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs index f97e395ce..a807eed51 100644 --- a/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs @@ -5,7 +5,6 @@ using ARMeilleure.Translation; using ARMeilleure.Translation.PTC; using System; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -20,7 +19,7 @@ namespace ARMeilleure.Instructions { Zx, Sx32, - Sx64 + Sx64, } public static void EmitLoadZx(ArmEmitterContext context, Operand address, int rt, int size) @@ -66,9 +65,15 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: value = context.SignExtend8 (destType, value); break; - case 1: value = context.SignExtend16(destType, value); break; - case 2: value = context.SignExtend32(destType, value); break; + case 0: + value = context.SignExtend8(destType, value); + break; + case 1: + value = context.SignExtend16(destType, value); + break; + case 2: + value = context.SignExtend32(destType, value); + break; } } @@ -128,7 +133,7 @@ namespace ARMeilleure.Instructions Operand temp = context.AllocateLocal(size == 3 ? OperandType.I64 : OperandType.I32); Operand lblSlowPath = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size); @@ -136,10 +141,18 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: value = context.Load8 (physAddr); break; - case 1: value = context.Load16(physAddr); break; - case 2: value = context.Load (OperandType.I32, physAddr); break; - case 3: value = context.Load (OperandType.I64, physAddr); break; + case 0: + value = context.Load8(physAddr); + break; + case 1: + value = context.Load16(physAddr); + break; + case 2: + value = context.Load(OperandType.I32, physAddr); + break; + case 3: + value = context.Load(OperandType.I64, physAddr); + break; } context.Copy(temp, value); @@ -161,7 +174,7 @@ namespace ARMeilleure.Instructions private static void EmitReadInt(ArmEmitterContext context, Operand address, int rt, int size) { Operand lblSlowPath = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size); @@ -169,10 +182,18 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: value = context.Load8 (physAddr); break; - case 1: value = context.Load16(physAddr); break; - case 2: value = context.Load (OperandType.I32, physAddr); break; - case 3: value = context.Load (OperandType.I64, physAddr); break; + case 0: + value = context.Load8(physAddr); + break; + case 1: + value = context.Load16(physAddr); + break; + case 2: + value = context.Load(OperandType.I32, physAddr); + break; + case 3: + value = context.Load(OperandType.I64, physAddr); + break; } SetInt(context, rt, value); @@ -204,7 +225,7 @@ namespace ARMeilleure.Instructions 1 => context.Load16(physAddr), 2 => context.Load(OperandType.I32, physAddr), 3 => context.Load(OperandType.I64, physAddr), - _ => context.Load(OperandType.V128, physAddr) + _ => context.Load(OperandType.V128, physAddr), }; } @@ -217,7 +238,7 @@ namespace ARMeilleure.Instructions int size) { Operand lblSlowPath = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size); @@ -225,11 +246,21 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: value = context.VectorInsert8 (vector, context.Load8(physAddr), elem); break; - case 1: value = context.VectorInsert16(vector, context.Load16(physAddr), elem); break; - case 2: value = context.VectorInsert (vector, context.Load(OperandType.I32, physAddr), elem); break; - case 3: value = context.VectorInsert (vector, context.Load(OperandType.I64, physAddr), elem); break; - case 4: value = context.Load (OperandType.V128, physAddr); break; + case 0: + value = context.VectorInsert8(vector, context.Load8(physAddr), elem); + break; + case 1: + value = context.VectorInsert16(vector, context.Load16(physAddr), elem); + break; + case 2: + value = context.VectorInsert(vector, context.Load(OperandType.I32, physAddr), elem); + break; + case 3: + value = context.VectorInsert(vector, context.Load(OperandType.I64, physAddr), elem); + break; + case 4: + value = context.Load(OperandType.V128, physAddr); + break; } context.Copy(GetVec(rt), value); @@ -254,7 +285,7 @@ namespace ARMeilleure.Instructions private static void EmitWriteInt(ArmEmitterContext context, Operand address, int rt, int size) { Operand lblSlowPath = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true, size); @@ -267,10 +298,18 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: context.Store8 (physAddr, value); break; - case 1: context.Store16(physAddr, value); break; - case 2: context.Store (physAddr, value); break; - case 3: context.Store (physAddr, value); break; + case 0: + context.Store8(physAddr, value); + break; + case 1: + context.Store16(physAddr, value); + break; + case 2: + context.Store(physAddr, value); + break; + case 3: + context.Store(physAddr, value); + break; } if (!context.Memory.Type.IsHostMapped()) @@ -321,7 +360,7 @@ namespace ARMeilleure.Instructions int size) { Operand lblSlowPath = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true, size); @@ -329,11 +368,21 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: context.Store8 (physAddr, context.VectorExtract8(value, elem)); break; - case 1: context.Store16(physAddr, context.VectorExtract16(value, elem)); break; - case 2: context.Store (physAddr, context.VectorExtract(OperandType.I32, value, elem)); break; - case 3: context.Store (physAddr, context.VectorExtract(OperandType.I64, value, elem)); break; - case 4: context.Store (physAddr, value); break; + case 0: + context.Store8(physAddr, context.VectorExtract8(value, elem)); + break; + case 1: + context.Store16(physAddr, context.VectorExtract16(value, elem)); + break; + case 2: + context.Store(physAddr, context.VectorExtract(OperandType.I32, value, elem)); + break; + case 3: + context.Store(physAddr, context.VectorExtract(OperandType.I64, value, elem)); + break; + case 4: + context.Store(physAddr, value); + break; } if (!context.Memory.Type.IsHostMapped()) @@ -464,10 +513,18 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break; - case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break; - case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break; - case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break; + case 0: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); + break; + case 1: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); + break; + case 2: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); + break; + case 3: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); + break; } return context.Call(info, address); @@ -485,21 +542,39 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break; - case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break; - case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break; - case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break; - case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)); break; + case 0: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); + break; + case 1: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); + break; + case 2: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); + break; + case 3: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); + break; + case 4: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)); + break; } Operand value = context.Call(info, address); switch (size) { - case 0: value = context.VectorInsert8 (vector, value, elem); break; - case 1: value = context.VectorInsert16(vector, value, elem); break; - case 2: value = context.VectorInsert (vector, value, elem); break; - case 3: value = context.VectorInsert (vector, value, elem); break; + case 0: + value = context.VectorInsert8(vector, value, elem); + break; + case 1: + value = context.VectorInsert16(vector, value, elem); + break; + case 2: + value = context.VectorInsert(vector, value, elem); + break; + case 3: + value = context.VectorInsert(vector, value, elem); + break; } context.Copy(GetVec(rt), value); @@ -511,10 +586,18 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break; - case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break; - case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break; - case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break; + case 0: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); + break; + case 1: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); + break; + case 2: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); + break; + case 3: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); + break; } Operand value = GetInt(context, rt); @@ -538,11 +621,21 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break; - case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break; - case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break; - case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break; - case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)); break; + case 0: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); + break; + case 1: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); + break; + case 2: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); + break; + case 3: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); + break; + case 4: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)); + break; } Operand value = default; @@ -551,10 +644,18 @@ namespace ARMeilleure.Instructions { switch (size) { - case 0: value = context.VectorExtract8 (GetVec(rt), elem); break; - case 1: value = context.VectorExtract16(GetVec(rt), elem); break; - case 2: value = context.VectorExtract (OperandType.I32, GetVec(rt), elem); break; - case 3: value = context.VectorExtract (OperandType.I64, GetVec(rt), elem); break; + case 0: + value = context.VectorExtract8(GetVec(rt), elem); + break; + case 1: + value = context.VectorExtract16(GetVec(rt), elem); + break; + case 2: + value = context.VectorExtract(OperandType.I32, GetVec(rt), elem); + break; + case 3: + value = context.VectorExtract(OperandType.I64, GetVec(rt), elem); + break; } } else @@ -585,18 +686,14 @@ namespace ARMeilleure.Instructions // ARM32 helpers. public static Operand GetMemM(ArmEmitterContext context, bool setCarry = true) { - switch (context.CurrOp) + return context.CurrOp switch { - case IOpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry); - - case IOpCode32MemReg op: return GetIntA32(context, op.Rm); - - case IOpCode32Mem op: return Const(op.Immediate); - - case OpCode32SimdMemImm op: return Const(op.Immediate); - - default: throw InvalidOpCodeType(context.CurrOp); - } + IOpCode32MemRsImm op => GetMShiftedByImmediate(context, op, setCarry), + IOpCode32MemReg op => GetIntA32(context, op.Rm), + IOpCode32Mem op => Const(op.Immediate), + OpCode32SimdMemImm op => Const(op.Immediate), + _ => throw InvalidOpCodeType(context.CurrOp), + }; } private static Exception InvalidOpCodeType(OpCode opCode) @@ -614,9 +711,15 @@ namespace ARMeilleure.Instructions { switch (op.ShiftType) { - case ShiftType.Lsr: shift = 32; break; - case ShiftType.Asr: shift = 32; break; - case ShiftType.Ror: shift = 1; break; + case ShiftType.Lsr: + shift = 32; + break; + case ShiftType.Asr: + shift = 32; + break; + case ShiftType.Ror: + shift = 1; + break; } } @@ -626,9 +729,15 @@ namespace ARMeilleure.Instructions switch (op.ShiftType) { - case ShiftType.Lsl: m = InstEmitAluHelper.GetLslC(context, m, setCarry, shift); break; - case ShiftType.Lsr: m = InstEmitAluHelper.GetLsrC(context, m, setCarry, shift); break; - case ShiftType.Asr: m = InstEmitAluHelper.GetAsrC(context, m, setCarry, shift); break; + case ShiftType.Lsl: + m = InstEmitAluHelper.GetLslC(context, m, setCarry, shift); + break; + case ShiftType.Lsr: + m = InstEmitAluHelper.GetLsrC(context, m, setCarry, shift); + break; + case ShiftType.Asr: + m = InstEmitAluHelper.GetAsrC(context, m, setCarry, shift); + break; case ShiftType.Ror: if (op.Immediate != 0) { diff --git a/src/ARMeilleure/Instructions/InstEmitMove.cs b/src/ARMeilleure/Instructions/InstEmitMove.cs index d551bf2da..f23ac333b 100644 --- a/src/ARMeilleure/Instructions/InstEmitMove.cs +++ b/src/ARMeilleure/Instructions/InstEmitMove.cs @@ -38,4 +38,4 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rd, Const(op.GetOperandType(), op.Immediate)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitMul.cs b/src/ARMeilleure/Instructions/InstEmitMul.cs index 65d11b30d..89dc09938 100644 --- a/src/ARMeilleure/Instructions/InstEmitMul.cs +++ b/src/ARMeilleure/Instructions/InstEmitMul.cs @@ -2,7 +2,7 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; - +using System.Diagnostics.CodeAnalysis; using static ARMeilleure.Instructions.InstEmitHelper; namespace ARMeilleure.Instructions @@ -33,14 +33,15 @@ namespace ARMeilleure.Instructions public static void Umsubl(ArmEmitterContext context) => EmitMull(context, MullFlags.Subtract); [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] private enum MullFlags { Subtract = 0, - Add = 1 << 0, - Signed = 1 << 1, + Add = 1 << 0, + Signed = 1 << 1, - SignedAdd = Signed | Add, - SignedSubtract = Signed | Subtract + SignedAdd = Signed | Add, + SignedSubtract = Signed | Subtract, } private static void EmitMull(ArmEmitterContext context, MullFlags flags) @@ -97,4 +98,4 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rd, d); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitMul32.cs b/src/ARMeilleure/Instructions/InstEmitMul32.cs index 0822f92c3..b9680fb69 100644 --- a/src/ARMeilleure/Instructions/InstEmitMul32.cs +++ b/src/ARMeilleure/Instructions/InstEmitMul32.cs @@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; - using static ARMeilleure.Instructions.InstEmitAluHelper; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -20,7 +19,7 @@ namespace ARMeilleure.Instructions Signed = 1 << 2, SignedAdd = Signed | Add, - SignedSubtract = Signed | Subtract + SignedSubtract = Signed | Subtract, } public static void Mla(ArmEmitterContext context) @@ -287,14 +286,14 @@ namespace ARMeilleure.Instructions { IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp; - Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn)); - Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm)); + Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn)); + Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm)); Operand dHi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)); Operand dLo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)); Operand res = context.Multiply(n, m); - res = context.Add(res, dHi); - res = context.Add(res, dLo); + res = context.Add(res, dHi); + res = context.Add(res, dLo); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand lo = context.ConvertI64ToI32(res); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs index 7e7f26b1a..7b308fa96 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs @@ -7,7 +7,6 @@ using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Diagnostics; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper32; @@ -185,11 +184,12 @@ namespace ARMeilleure.Instructions int eSize = 8 << op.Size; - Operand res = eSize switch { - 8 => Clz_V_I8 (context, GetVec(op.Rn)), + Operand res = eSize switch + { + 8 => Clz_V_I8(context, GetVec(op.Rn)), 16 => Clz_V_I16(context, GetVec(op.Rn)), 32 => Clz_V_I32(context, GetVec(op.Rn)), - _ => default + _ => default, }; if (res != default) @@ -230,14 +230,14 @@ namespace ARMeilleure.Instructions Operand clzTable = X86GetScalar(context, 0x01_01_01_01_02_02_03_04); Operand maskLow = X86GetAllElements(context, 0x0f_0f_0f_0f); - Operand c04 = X86GetAllElements(context, 0x04_04_04_04); + Operand c04 = X86GetAllElements(context, 0x04_04_04_04); // CLZ of low 4 bits of elements in arg. Operand loClz = context.AddIntrinsic(Intrinsic.X86Pshufb, clzTable, arg); // Get the high 4 bits of elements in arg. Operand hiArg = context.AddIntrinsic(Intrinsic.X86Psrlw, arg, Const(4)); - hiArg = context.AddIntrinsic(Intrinsic.X86Pand, hiArg, maskLow); + hiArg = context.AddIntrinsic(Intrinsic.X86Pand, hiArg, maskLow); // CLZ of high 4 bits of elements in arg. Operand hiClz = context.AddIntrinsic(Intrinsic.X86Pshufb, clzTable, hiArg); @@ -257,8 +257,8 @@ namespace ARMeilleure.Instructions } Operand maskSwap = X86GetElements(context, 0x80_0f_80_0d_80_0b_80_09, 0x80_07_80_05_80_03_80_01); - Operand maskLow = X86GetAllElements(context, 0x00ff_00ff); - Operand c0008 = X86GetAllElements(context, 0x0008_0008); + Operand maskLow = X86GetAllElements(context, 0x00ff_00ff); + Operand c0008 = X86GetAllElements(context, 0x0008_0008); // CLZ pair of high 8 and low 8 bits of elements in arg. Operand hiloClz = Clz_V_I8(context, arg); @@ -282,12 +282,14 @@ namespace ARMeilleure.Instructions return default; } +#pragma warning disable IDE0055 // Disable formatting Operand AddVectorI32(Operand op0, Operand op1) => context.AddIntrinsic(Intrinsic.X86Paddd, op0, op1); Operand SubVectorI32(Operand op0, Operand op1) => context.AddIntrinsic(Intrinsic.X86Psubd, op0, op1); Operand ShiftRightVectorUI32(Operand op0, int imm8) => context.AddIntrinsic(Intrinsic.X86Psrld, op0, Const(imm8)); Operand OrVector(Operand op0, Operand op1) => context.AddIntrinsic(Intrinsic.X86Por, op0, op1); Operand AndVector(Operand op0, Operand op1) => context.AddIntrinsic(Intrinsic.X86Pand, op0, op1); Operand NotVector(Operand op0) => context.AddIntrinsic(Intrinsic.X86Pandn, op0, context.VectorOne()); +#pragma warning restore IDE0055 Operand c55555555 = X86GetAllElements(context, 0x55555555); Operand c33333333 = X86GetAllElements(context, 0x33333333); @@ -311,24 +313,24 @@ namespace ARMeilleure.Instructions // Count leading 1s, which is the population count. tmp0 = ShiftRightVectorUI32(res, 1); tmp0 = AndVector(tmp0, c55555555); - res = SubVectorI32(res, tmp0); + res = SubVectorI32(res, tmp0); tmp0 = ShiftRightVectorUI32(res, 2); tmp0 = AndVector(tmp0, c33333333); tmp1 = AndVector(res, c33333333); - res = AddVectorI32(tmp0, tmp1); + res = AddVectorI32(tmp0, tmp1); tmp0 = ShiftRightVectorUI32(res, 4); tmp0 = AddVectorI32(tmp0, res); - res = AndVector(tmp0, c0f0f0f0f); + res = AndVector(tmp0, c0f0f0f0f); tmp0 = ShiftRightVectorUI32(res, 8); - res = AddVectorI32(tmp0, res); + res = AddVectorI32(tmp0, res); tmp0 = ShiftRightVectorUI32(res, 16); - res = AddVectorI32(tmp0, res); + res = AddVectorI32(tmp0, res); - res = AndVector(res, c0000003f); + res = AndVector(res, c0000003f); return res; } @@ -2436,8 +2438,8 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { - Operand maskHalf = X86GetScalar(context, 0.5f); - Operand maskThree = X86GetScalar(context, 3f); + Operand maskHalf = X86GetScalar(context, 0.5f); + Operand maskThree = X86GetScalar(context, 3f); Operand maskOneHalf = X86GetScalar(context, 1.5f); if (Optimizations.UseFma) @@ -2457,8 +2459,8 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand maskHalf = X86GetScalar(context, 0.5d); - Operand maskThree = X86GetScalar(context, 3d); + Operand maskHalf = X86GetScalar(context, 0.5d); + Operand maskThree = X86GetScalar(context, 3d); Operand maskOneHalf = X86GetScalar(context, 1.5d); if (Optimizations.UseFma) @@ -2505,8 +2507,8 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { - Operand maskHalf = X86GetAllElements(context, 0.5f); - Operand maskThree = X86GetAllElements(context, 3f); + Operand maskHalf = X86GetAllElements(context, 0.5f); + Operand maskThree = X86GetAllElements(context, 3f); Operand maskOneHalf = X86GetAllElements(context, 1.5f); if (Optimizations.UseFma) @@ -2519,7 +2521,7 @@ namespace ARMeilleure.Instructions res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res); } - res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res); + res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF); if (op.RegisterSize == RegisterSize.Simd64) @@ -2531,8 +2533,8 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand maskHalf = X86GetAllElements(context, 0.5d); - Operand maskThree = X86GetAllElements(context, 3d); + Operand maskHalf = X86GetAllElements(context, 0.5d); + Operand maskThree = X86GetAllElements(context, 3d); Operand maskOneHalf = X86GetAllElements(context, 1.5d); if (Optimizations.UseFma) @@ -2545,7 +2547,7 @@ namespace ARMeilleure.Instructions res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res); } - res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res); + res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF); context.Copy(GetVec(op.Rd), res); @@ -2824,10 +2826,10 @@ namespace ARMeilleure.Instructions for (int i = 0; i < 8; i++) { Operand mask = context.AddIntrinsic(Intrinsic.X86Psllw, n, Const(15 - i)); - mask = context.AddIntrinsic(Intrinsic.X86Psraw, mask, Const(15)); + mask = context.AddIntrinsic(Intrinsic.X86Psraw, mask, Const(15)); Operand tmp = context.AddIntrinsic(Intrinsic.X86Psllw, m, Const(i)); - tmp = context.AddIntrinsic(Intrinsic.X86Pand, tmp, mask); + tmp = context.AddIntrinsic(Intrinsic.X86Pand, tmp, mask); res = context.AddIntrinsic(Intrinsic.X86Pxor, res, tmp); } @@ -2839,12 +2841,12 @@ namespace ARMeilleure.Instructions for (int i = 0; i < 64; i++) { Operand mask = context.AddIntrinsic(Intrinsic.X86Movlhps, n, n); - mask = context.AddIntrinsic(Intrinsic.X86Psllq, mask, Const(63 - i)); - mask = context.AddIntrinsic(Intrinsic.X86Psrlq, mask, Const(63)); - mask = context.AddIntrinsic(Intrinsic.X86Psubq, zero, mask); + mask = context.AddIntrinsic(Intrinsic.X86Psllq, mask, Const(63 - i)); + mask = context.AddIntrinsic(Intrinsic.X86Psrlq, mask, Const(63)); + mask = context.AddIntrinsic(Intrinsic.X86Psubq, zero, mask); Operand tmp = EmitSse2Sll_128(context, m, i); - tmp = context.AddIntrinsic(Intrinsic.X86Pand, tmp, mask); + tmp = context.AddIntrinsic(Intrinsic.X86Pand, tmp, mask); res = context.AddIntrinsic(Intrinsic.X86Pxor, res, tmp); } @@ -3119,7 +3121,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - Operand res = context.AddIntrinsic(Intrinsic.X86Pand, n, m); + Operand res = context.AddIntrinsic(Intrinsic.X86Pand, n, m); Operand res2 = context.AddIntrinsic(Intrinsic.X86Pxor, n, m); Intrinsic shiftInst = op.Size == 1 ? Intrinsic.X86Psraw : Intrinsic.X86Psrad; @@ -4058,7 +4060,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); - Operand res = context.AddIntrinsic(Intrinsic.X86Pand, n, m); + Operand res = context.AddIntrinsic(Intrinsic.X86Pand, n, m); Operand res2 = context.AddIntrinsic(Intrinsic.X86Pxor, n, m); Intrinsic shiftInst = op.Size == 1 ? Intrinsic.X86Psrlw : Intrinsic.X86Psrld; @@ -4594,7 +4596,7 @@ namespace ARMeilleure.Instructions { int pairIndex = index << 1; - Operand ne0 = EmitVectorExtract(context, op.Rn, pairIndex, op.Size, signed); + Operand ne0 = EmitVectorExtract(context, op.Rn, pairIndex, op.Size, signed); Operand ne1 = EmitVectorExtract(context, op.Rn, pairIndex + 1, op.Size, signed); Operand e = context.Add(ne0, ne1); @@ -4686,7 +4688,7 @@ namespace ARMeilleure.Instructions Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64); Operand cmp = signed - ? context.ICompareGreaterOrEqual (op1, op2) + ? context.ICompareGreaterOrEqual(op1, op2) : context.ICompareGreaterOrEqualUI(op1, op2); return context.ConditionalSelect(cmp, op1, op2); @@ -4697,7 +4699,7 @@ namespace ARMeilleure.Instructions Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64); Operand cmp = signed - ? context.ICompareLessOrEqual (op1, op2) + ? context.ICompareLessOrEqual(op1, op2) : context.ICompareLessOrEqualUI(op1, op2); return context.ConditionalSelect(cmp, op1, op2); @@ -4852,10 +4854,10 @@ namespace ARMeilleure.Instructions Operand mask1 = context.AddIntrinsic(Intrinsic.X86Cmpps, opF, opF, Const((int)CmpCondition.UnorderedQ)); - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask); - mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, mask2, qMask, Const((int)CmpCondition.Equal)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask); + mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, mask2, qMask, Const((int)CmpCondition.Equal)); - qNaNMask = isQNaN == null || (bool)isQNaN ? context.AddIntrinsic(Intrinsic.X86Andps, mask2, mask1) : default; + qNaNMask = isQNaN == null || (bool)isQNaN ? context.AddIntrinsic(Intrinsic.X86Andps, mask2, mask1) : default; sNaNMask = isQNaN == null || !(bool)isQNaN ? context.AddIntrinsic(Intrinsic.X86Andnps, mask2, mask1) : default; } else /* if ((op.Size & 1) == 1) */ @@ -4866,10 +4868,10 @@ namespace ARMeilleure.Instructions Operand mask1 = context.AddIntrinsic(Intrinsic.X86Cmppd, opF, opF, Const((int)CmpCondition.UnorderedQ)); - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask); - mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, mask2, qMask, Const((int)CmpCondition.Equal)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask); + mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, mask2, qMask, Const((int)CmpCondition.Equal)); - qNaNMask = isQNaN == null || (bool)isQNaN ? context.AddIntrinsic(Intrinsic.X86Andpd, mask2, mask1) : default; + qNaNMask = isQNaN == null || (bool)isQNaN ? context.AddIntrinsic(Intrinsic.X86Andpd, mask2, mask1) : default; sNaNMask = isQNaN == null || !(bool)isQNaN ? context.AddIntrinsic(Intrinsic.X86Andnpd, mask2, mask1) : default; } } @@ -4895,11 +4897,11 @@ namespace ARMeilleure.Instructions Operand qMask = scalar ? X86GetScalar(context, 1 << QBit) : X86GetAllElements(context, 1 << QBit); - Operand resNaNMask = context.AddIntrinsic(Intrinsic.X86Pandn, mSNaNMask, nQNaNMask); - resNaNMask = context.AddIntrinsic(Intrinsic.X86Por, resNaNMask, nSNaNMask); + Operand resNaNMask = context.AddIntrinsic(Intrinsic.X86Pandn, mSNaNMask, nQNaNMask); + resNaNMask = context.AddIntrinsic(Intrinsic.X86Por, resNaNMask, nSNaNMask); Operand resNaN = context.AddIntrinsic(Intrinsic.X86Blendvps, mCopy, nCopy, resNaNMask); - resNaN = context.AddIntrinsic(Intrinsic.X86Por, resNaN, qMask); + resNaN = context.AddIntrinsic(Intrinsic.X86Por, resNaN, qMask); Operand resMask = context.AddIntrinsic(Intrinsic.X86Cmpps, nCopy, mCopy, Const((int)CmpCondition.OrderedQ)); @@ -4929,11 +4931,11 @@ namespace ARMeilleure.Instructions Operand qMask = scalar ? X86GetScalar(context, 1L << QBit) : X86GetAllElements(context, 1L << QBit); - Operand resNaNMask = context.AddIntrinsic(Intrinsic.X86Pandn, mSNaNMask, nQNaNMask); - resNaNMask = context.AddIntrinsic(Intrinsic.X86Por, resNaNMask, nSNaNMask); + Operand resNaNMask = context.AddIntrinsic(Intrinsic.X86Pandn, mSNaNMask, nQNaNMask); + resNaNMask = context.AddIntrinsic(Intrinsic.X86Por, resNaNMask, nSNaNMask); Operand resNaN = context.AddIntrinsic(Intrinsic.X86Blendvpd, mCopy, nCopy, resNaNMask); - resNaN = context.AddIntrinsic(Intrinsic.X86Por, resNaN, qMask); + resNaN = context.AddIntrinsic(Intrinsic.X86Por, resNaN, qMask); Operand resMask = context.AddIntrinsic(Intrinsic.X86Cmppd, nCopy, mCopy, Const((int)CmpCondition.OrderedQ)); @@ -4964,10 +4966,10 @@ namespace ARMeilleure.Instructions Operand mask = X86GetAllElements(context, -0f); Operand res = context.AddIntrinsic(isMax ? Intrinsic.X86Maxps : Intrinsic.X86Minps, n, m); - res = context.AddIntrinsic(Intrinsic.X86Andnps, mask, res); + res = context.AddIntrinsic(Intrinsic.X86Andnps, mask, res); Operand resSign = context.AddIntrinsic(isMax ? Intrinsic.X86Pand : Intrinsic.X86Por, n, m); - resSign = context.AddIntrinsic(Intrinsic.X86Andps, mask, resSign); + resSign = context.AddIntrinsic(Intrinsic.X86Andps, mask, resSign); return context.AddIntrinsic(Intrinsic.X86Por, res, resSign); } @@ -4976,10 +4978,10 @@ namespace ARMeilleure.Instructions Operand mask = X86GetAllElements(context, -0d); Operand res = context.AddIntrinsic(isMax ? Intrinsic.X86Maxpd : Intrinsic.X86Minpd, n, m); - res = context.AddIntrinsic(Intrinsic.X86Andnpd, mask, res); + res = context.AddIntrinsic(Intrinsic.X86Andnpd, mask, res); Operand resSign = context.AddIntrinsic(isMax ? Intrinsic.X86Pand : Intrinsic.X86Por, n, m); - resSign = context.AddIntrinsic(Intrinsic.X86Andpd, mask, resSign); + resSign = context.AddIntrinsic(Intrinsic.X86Andpd, mask, resSign); return context.AddIntrinsic(Intrinsic.X86Por, res, resSign); } @@ -5003,7 +5005,7 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { Operand negInfMask = scalar - ? X86GetScalar (context, isMaxNum ? float.NegativeInfinity : float.PositiveInfinity) + ? X86GetScalar(context, isMaxNum ? float.NegativeInfinity : float.PositiveInfinity) : X86GetAllElements(context, isMaxNum ? float.NegativeInfinity : float.PositiveInfinity); Operand nMask = context.AddIntrinsic(Intrinsic.X86Andnps, mQNaNMask, nQNaNMask); @@ -5038,7 +5040,7 @@ namespace ARMeilleure.Instructions else /* if (sizeF == 1) */ { Operand negInfMask = scalar - ? X86GetScalar (context, isMaxNum ? double.NegativeInfinity : double.PositiveInfinity) + ? X86GetScalar(context, isMaxNum ? double.NegativeInfinity : double.PositiveInfinity) : X86GetAllElements(context, isMaxNum ? double.NegativeInfinity : double.PositiveInfinity); Operand nMask = context.AddIntrinsic(Intrinsic.X86Andnpd, mQNaNMask, nQNaNMask); @@ -5072,7 +5074,7 @@ namespace ARMeilleure.Instructions { None, Add, - Subtract + Subtract, } private static void EmitSse41VectorMul_AddSub(ArmEmitterContext context, AddSub addSub) @@ -5187,10 +5189,10 @@ namespace ARMeilleure.Instructions Intrinsic subInst = X86PsubInstruction[size]; - Operand res = context.AddIntrinsic(subInst, n, m); + Operand res = context.AddIntrinsic(subInst, n, m); Operand res2 = context.AddIntrinsic(subInst, m, n); - res = context.AddIntrinsic(Intrinsic.X86Pand, cmpMask, res); + res = context.AddIntrinsic(Intrinsic.X86Pand, cmpMask, res); res2 = context.AddIntrinsic(Intrinsic.X86Pandn, cmpMask, res2); res = context.AddIntrinsic(Intrinsic.X86Por, res, res2); @@ -5214,7 +5216,7 @@ namespace ARMeilleure.Instructions } Operand high = context.AddIntrinsic(Intrinsic.X86Pslldq, op, Const(8)); - high = context.AddIntrinsic(Intrinsic.X86Psrlq, high, Const(64 - shift)); + high = context.AddIntrinsic(Intrinsic.X86Psrlq, high, Const(64 - shift)); Operand low = context.AddIntrinsic(Intrinsic.X86Psllq, op, Const(shift)); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs index a9994e412..1d99cbc3d 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs @@ -2,7 +2,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; - using static ARMeilleure.Instructions.InstEmitFlowHelper; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; @@ -190,7 +189,7 @@ namespace ARMeilleure.Instructions 2 => context.Multiply(context.ZeroExtend32(OperandType.I64, insert), Const(0x0000000100000001u)), 1 => context.Multiply(context.ZeroExtend16(OperandType.I64, insert), Const(0x0001000100010001u)), 0 => context.Multiply(context.ZeroExtend8(OperandType.I64, insert), Const(0x0101010101010101u)), - _ => throw new InvalidOperationException($"Invalid Vdup size \"{op.Size}\".") + _ => throw new InvalidOperationException($"Invalid Vdup size \"{op.Size}\"."), }; InsertScalar(context, op.Vd, insert); @@ -212,7 +211,7 @@ namespace ARMeilleure.Instructions 2 => context.Multiply(context.ZeroExtend32(OperandType.I64, insert), Const(0x0000000100000001u)), 1 => context.Multiply(context.ZeroExtend16(OperandType.I64, insert), Const(0x0001000100010001u)), 0 => context.Multiply(context.ZeroExtend8(OperandType.I64, insert), Const(0x0101010101010101u)), - _ => throw new InvalidOperationException($"Invalid Vdup size \"{op.Size}\".") + _ => throw new InvalidOperationException($"Invalid Vdup size \"{op.Size}\"."), }; InsertScalar(context, op.Vd, insert); @@ -1654,7 +1653,7 @@ namespace ARMeilleure.Instructions { IOpCode32Simd op = (IOpCode32Simd)context.CurrOp; - Func<Operand, Operand, Operand> genericEmit = (n, m) => + Operand genericEmit(Operand n, Operand m) { Operand nNum = context.Copy(n); Operand mNum = context.Copy(m); @@ -1688,7 +1687,7 @@ namespace ARMeilleure.Instructions return context.AddIntrinsic(isMaxNum ? Intrinsic.X86Maxpd : Intrinsic.X86Minpd, nNum, mNum); } - }; + } if (scalar) { diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/src/ARMeilleure/Instructions/InstEmitSimdCmp.cs index c32b64ba1..aab677869 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCmp.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCmp.cs @@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -493,7 +492,7 @@ namespace ARMeilleure.Instructions OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp; Operand lblTrue = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond)); @@ -510,7 +509,7 @@ namespace ARMeilleure.Instructions private static void EmitSetNzcv(ArmEmitterContext context, int nzcv) { - Operand Extract(int value, int bit) + static Operand Extract(int value, int bit) { if (bit != 0) { @@ -532,7 +531,7 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false; + bool cmpWithZero = op is not OpCodeSimdFcond && op.Bit3; if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2)) { diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCvt.cs b/src/ARMeilleure/Instructions/InstEmitSimdCvt.cs index 652ad397c..3363a7c77 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCvt.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCvt.cs @@ -5,7 +5,6 @@ using ARMeilleure.Translation; using System; using System.Diagnostics; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -67,8 +66,8 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, n, Const(X86GetRoundControl(FPRoundingMode.ToNearest))); - res = context.AddIntrinsic(Intrinsic.X86Pslldq, res, Const(14)); // VectorZeroUpper112() - res = context.AddIntrinsic(Intrinsic.X86Psrldq, res, Const(14)); + res = context.AddIntrinsic(Intrinsic.X86Pslldq, res, Const(14)); // VectorZeroUpper112() + res = context.AddIntrinsic(Intrinsic.X86Psrldq, res, Const(14)); context.Copy(GetVec(op.Rd), res); } @@ -92,7 +91,7 @@ namespace ARMeilleure.Instructions Debug.Assert(!Optimizations.ForceLegacySse); Operand res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, GetVec(op.Rn)); - res = context.VectorZeroUpper96(res); + res = context.VectorZeroUpper96(res); context.Copy(GetVec(op.Rd), res); } @@ -116,7 +115,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), n); - res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest))); + res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest))); context.Copy(GetVec(op.Rd), res); } @@ -140,8 +139,8 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, GetVec(op.Rn)); - res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res); - res = context.VectorZeroUpper64(res); + res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res); + res = context.VectorZeroUpper64(res); context.Copy(GetVec(op.Rd), res); } @@ -273,7 +272,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res = op.RegisterSize == RegisterSize.Simd128 ? context.AddIntrinsic(Intrinsic.X86Movhlps, n, n) : n; - res = context.AddIntrinsic(Intrinsic.X86Cvtps2pd, res); + res = context.AddIntrinsic(Intrinsic.X86Cvtps2pd, res); context.Copy(GetVec(op.Rd), res); } @@ -284,7 +283,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand res = op.RegisterSize == RegisterSize.Simd128 ? context.AddIntrinsic(Intrinsic.X86Movhlps, n, n) : n; - res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res); + res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res); context.Copy(GetVec(op.Rd), res); } @@ -387,10 +386,10 @@ namespace ARMeilleure.Instructions Intrinsic movInst = op.RegisterSize == RegisterSize.Simd128 ? Intrinsic.X86Movlhps : Intrinsic.X86Movhlps; Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtpd2ps, GetVec(op.Rn)); - nInt = context.AddIntrinsic(Intrinsic.X86Movlhps, nInt, nInt); + nInt = context.AddIntrinsic(Intrinsic.X86Movlhps, nInt, nInt); Operand res = context.VectorZeroUpper64(d); - res = context.AddIntrinsic(movInst, res, nInt); + res = context.AddIntrinsic(movInst, res, nInt); context.Copy(d, res); } @@ -404,10 +403,10 @@ namespace ARMeilleure.Instructions Intrinsic movInst = op.RegisterSize == RegisterSize.Simd128 ? Intrinsic.X86Movlhps : Intrinsic.X86Movhlps; Operand nInt = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, n, Const(X86GetRoundControl(FPRoundingMode.ToNearest))); - nInt = context.AddIntrinsic(Intrinsic.X86Movlhps, nInt, nInt); + nInt = context.AddIntrinsic(Intrinsic.X86Movlhps, nInt, nInt); Operand res = context.VectorZeroUpper64(d); - res = context.AddIntrinsic(movInst, res, nInt); + res = context.AddIntrinsic(movInst, res, nInt); context.Copy(d, res); } @@ -1225,15 +1224,15 @@ namespace ARMeilleure.Instructions { Debug.Assert(opF.Type == OperandType.V128); - Operand longL = context.AddIntrinsicLong (Intrinsic.X86Cvtsd2si, opF); // opFL - Operand res = context.VectorCreateScalar(longL); + Operand longL = context.AddIntrinsicLong(Intrinsic.X86Cvtsd2si, opF); // opFL + Operand res = context.VectorCreateScalar(longL); if (!scalar) { - Operand opFH = context.AddIntrinsic (Intrinsic.X86Movhlps, res, opF); // res doesn't matter. - Operand longH = context.AddIntrinsicLong (Intrinsic.X86Cvtsd2si, opFH); - Operand resH = context.VectorCreateScalar(longH); - res = context.AddIntrinsic (Intrinsic.X86Movlhps, res, resH); + Operand opFH = context.AddIntrinsic(Intrinsic.X86Movhlps, res, opF); // res doesn't matter. + Operand longH = context.AddIntrinsicLong(Intrinsic.X86Cvtsd2si, opFH); + Operand resH = context.VectorCreateScalar(longH); + res = context.AddIntrinsic(Intrinsic.X86Movlhps, res, resH); } return res; @@ -1244,14 +1243,14 @@ namespace ARMeilleure.Instructions Debug.Assert(op.Type == OperandType.V128); Operand longL = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, op); // opL - Operand res = context.AddIntrinsic (Intrinsic.X86Cvtsi2sd, context.VectorZero(), longL); + Operand res = context.AddIntrinsic(Intrinsic.X86Cvtsi2sd, context.VectorZero(), longL); if (!scalar) { - Operand opH = context.AddIntrinsic (Intrinsic.X86Movhlps, res, op); // res doesn't matter. + Operand opH = context.AddIntrinsic(Intrinsic.X86Movhlps, res, op); // res doesn't matter. Operand longH = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, opH); - Operand resH = context.AddIntrinsic (Intrinsic.X86Cvtsi2sd, res, longH); // res doesn't matter. - res = context.AddIntrinsic (Intrinsic.X86Movlhps, res, resH); + Operand resH = context.AddIntrinsic(Intrinsic.X86Cvtsi2sd, res, longH); // res doesn't matter. + res = context.AddIntrinsic(Intrinsic.X86Movlhps, res, resH); } return res; @@ -1278,7 +1277,7 @@ namespace ARMeilleure.Instructions int fpScaled = 0x3F800000 - fBits * 0x800000; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); res = context.AddIntrinsic(Intrinsic.X86Mulps, res, fpScaledMask); @@ -1307,7 +1306,7 @@ namespace ARMeilleure.Instructions long fpScaled = 0x3FF0000000000000L - fBits * 0x10000000000000L; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); res = context.AddIntrinsic(Intrinsic.X86Mulpd, res, fpScaledMask); @@ -1334,16 +1333,16 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { Operand mask = scalar // 65536.000f (1 << 16) - ? X86GetScalar (context, 0x47800000) + ? X86GetScalar(context, 0x47800000) : X86GetAllElements(context, 0x47800000); Operand res = context.AddIntrinsic(Intrinsic.X86Psrld, n, Const(16)); - res = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res); - res = context.AddIntrinsic(Intrinsic.X86Mulps, res, mask); + res = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res); + res = context.AddIntrinsic(Intrinsic.X86Mulps, res, mask); Operand res2 = context.AddIntrinsic(Intrinsic.X86Pslld, n, Const(16)); - res2 = context.AddIntrinsic(Intrinsic.X86Psrld, res2, Const(16)); - res2 = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res2); + res2 = context.AddIntrinsic(Intrinsic.X86Psrld, res2, Const(16)); + res2 = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res2); res = context.AddIntrinsic(Intrinsic.X86Addps, res, res2); @@ -1355,7 +1354,7 @@ namespace ARMeilleure.Instructions int fpScaled = 0x3F800000 - fBits * 0x800000; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); res = context.AddIntrinsic(Intrinsic.X86Mulps, res, fpScaledMask); @@ -1375,16 +1374,16 @@ namespace ARMeilleure.Instructions else /* if (sizeF == 1) */ { Operand mask = scalar // 4294967296.0000000d (1L << 32) - ? X86GetScalar (context, 0x41F0000000000000L) + ? X86GetScalar(context, 0x41F0000000000000L) : X86GetAllElements(context, 0x41F0000000000000L); - Operand res = context.AddIntrinsic (Intrinsic.X86Psrlq, n, Const(32)); - res = EmitSse2CvtInt64ToDoubleOp(context, res, scalar); - res = context.AddIntrinsic (Intrinsic.X86Mulpd, res, mask); + Operand res = context.AddIntrinsic(Intrinsic.X86Psrlq, n, Const(32)); + res = EmitSse2CvtInt64ToDoubleOp(context, res, scalar); + res = context.AddIntrinsic(Intrinsic.X86Mulpd, res, mask); - Operand res2 = context.AddIntrinsic (Intrinsic.X86Psllq, n, Const(32)); - res2 = context.AddIntrinsic (Intrinsic.X86Psrlq, res2, Const(32)); - res2 = EmitSse2CvtInt64ToDoubleOp(context, res2, scalar); + Operand res2 = context.AddIntrinsic(Intrinsic.X86Psllq, n, Const(32)); + res2 = context.AddIntrinsic(Intrinsic.X86Psrlq, res2, Const(32)); + res2 = EmitSse2CvtInt64ToDoubleOp(context, res2, scalar); res = context.AddIntrinsic(Intrinsic.X86Addpd, res, res2); @@ -1396,7 +1395,7 @@ namespace ARMeilleure.Instructions long fpScaled = 0x3FF0000000000000L - fBits * 0x10000000000000L; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); res = context.AddIntrinsic(Intrinsic.X86Mulpd, res, fpScaledMask); @@ -1423,7 +1422,7 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (op is OpCodeSimdShImm fixedOp) { @@ -1433,7 +1432,7 @@ namespace ARMeilleure.Instructions int fpScaled = 0x3F800000 + fBits * 0x800000; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask); @@ -1451,7 +1450,7 @@ namespace ARMeilleure.Instructions Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes); Operand fpMaxValMask = scalar // 2.14748365E9f (2147483648) - ? X86GetScalar (context, 0x4F000000) + ? X86GetScalar(context, 0x4F000000) : X86GetAllElements(context, 0x4F000000); nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); @@ -1472,7 +1471,7 @@ namespace ARMeilleure.Instructions else /* if (sizeF == 1) */ { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (op is OpCodeSimdShImm fixedOp) { @@ -1482,7 +1481,7 @@ namespace ARMeilleure.Instructions long fpScaled = 0x3FF0000000000000L + fBits * 0x10000000000000L; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask); @@ -1500,7 +1499,7 @@ namespace ARMeilleure.Instructions Operand nLong = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar); Operand fpMaxValMask = scalar // 9.2233720368547760E18d (9223372036854775808) - ? X86GetScalar (context, 0x43E0000000000000L) + ? X86GetScalar(context, 0x43E0000000000000L) : X86GetAllElements(context, 0x43E0000000000000L); nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); @@ -1528,7 +1527,7 @@ namespace ARMeilleure.Instructions if (sizeF == 0) { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (op is OpCodeSimdShImm fixedOp) { @@ -1538,7 +1537,7 @@ namespace ARMeilleure.Instructions int fpScaled = 0x3F800000 + fBits * 0x800000; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask); @@ -1556,10 +1555,10 @@ namespace ARMeilleure.Instructions Operand zero = context.VectorZero(); Operand nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); Operand fpMaxValMask = scalar // 2.14748365E9f (2147483648) - ? X86GetScalar (context, 0x4F000000) + ? X86GetScalar(context, 0x4F000000) : X86GetAllElements(context, 0x4F000000); Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes); @@ -1567,14 +1566,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Subps, nRes, fpMaxValMask); nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); Operand nInt2 = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes); nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); - Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nInt2, nRes); - dRes = context.AddIntrinsic(Intrinsic.X86Paddd, dRes, nInt); + Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nInt2, nRes); + dRes = context.AddIntrinsic(Intrinsic.X86Paddd, dRes, nInt); if (scalar) { @@ -1590,7 +1589,7 @@ namespace ARMeilleure.Instructions else /* if (sizeF == 1) */ { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (op is OpCodeSimdShImm fixedOp) { @@ -1600,7 +1599,7 @@ namespace ARMeilleure.Instructions long fpScaled = 0x3FF0000000000000L + fBits * 0x10000000000000L; Operand fpScaledMask = scalar - ? X86GetScalar (context, fpScaled) + ? X86GetScalar(context, fpScaled) : X86GetAllElements(context, fpScaled); nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask); @@ -1618,10 +1617,10 @@ namespace ARMeilleure.Instructions Operand zero = context.VectorZero(); Operand nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); Operand fpMaxValMask = scalar // 9.2233720368547760E18d (9223372036854775808) - ? X86GetScalar (context, 0x43E0000000000000L) + ? X86GetScalar(context, 0x43E0000000000000L) : X86GetAllElements(context, 0x43E0000000000000L); Operand nLong = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar); @@ -1629,14 +1628,14 @@ namespace ARMeilleure.Instructions nRes = context.AddIntrinsic(Intrinsic.X86Subpd, nRes, fpMaxValMask); nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); Operand nLong2 = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar); nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); - Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nLong2, nRes); - dRes = context.AddIntrinsic(Intrinsic.X86Paddq, dRes, nLong); + Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nLong2, nRes); + dRes = context.AddIntrinsic(Intrinsic.X86Paddq, dRes, nLong); if (scalar) { @@ -1656,7 +1655,7 @@ namespace ARMeilleure.Instructions if (op.Size == 0) { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (isFixed) { @@ -1678,7 +1677,7 @@ namespace ARMeilleure.Instructions } Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes) + ? context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes) : context.AddIntrinsicLong(Intrinsic.X86Cvtss2si, nRes); int fpMaxVal = op.RegisterSize == RegisterSize.Int32 @@ -1703,7 +1702,7 @@ namespace ARMeilleure.Instructions else /* if (op.Size == 1) */ { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (isFixed) { @@ -1725,7 +1724,7 @@ namespace ARMeilleure.Instructions } Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes) + ? context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes) : context.AddIntrinsicLong(Intrinsic.X86Cvtsd2si, nRes); long fpMaxVal = op.RegisterSize == RegisterSize.Int32 @@ -1758,7 +1757,7 @@ namespace ARMeilleure.Instructions if (op.Size == 0) { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (isFixed) { @@ -1782,7 +1781,7 @@ namespace ARMeilleure.Instructions Operand zero = context.VectorZero(); Operand nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); int fpMaxVal = op.RegisterSize == RegisterSize.Int32 ? 0x4F000000 // 2.14748365E9f (2147483648) @@ -1791,16 +1790,16 @@ namespace ARMeilleure.Instructions Operand fpMaxValMask = X86GetScalar(context, fpMaxVal); Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes) + ? context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes) : context.AddIntrinsicLong(Intrinsic.X86Cvtss2si, nRes); nRes = context.AddIntrinsic(Intrinsic.X86Subss, nRes, fpMaxValMask); nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); Operand nIntOrLong2 = op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes) + ? context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes) : context.AddIntrinsicLong(Intrinsic.X86Cvtss2si, nRes); nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); @@ -1813,14 +1812,14 @@ namespace ARMeilleure.Instructions } Operand dRes = context.BitwiseExclusiveOr(nIntOrLong2, nInt); - dRes = context.Add(dRes, nIntOrLong); + dRes = context.Add(dRes, nIntOrLong); SetIntOrZR(context, op.Rd, dRes); } else /* if (op.Size == 1) */ { Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n); if (isFixed) { @@ -1844,7 +1843,7 @@ namespace ARMeilleure.Instructions Operand zero = context.VectorZero(); Operand nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); long fpMaxVal = op.RegisterSize == RegisterSize.Int32 ? 0x41E0000000000000L // 2147483648.0000000d (2147483648) @@ -1853,16 +1852,16 @@ namespace ARMeilleure.Instructions Operand fpMaxValMask = X86GetScalar(context, fpMaxVal); Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes) + ? context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes) : context.AddIntrinsicLong(Intrinsic.X86Cvtsd2si, nRes); nRes = context.AddIntrinsic(Intrinsic.X86Subsd, nRes, fpMaxValMask); nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual)); - nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); + nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp); Operand nIntOrLong2 = op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes) + ? context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes) : context.AddIntrinsicLong(Intrinsic.X86Cvtsd2si, nRes); nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan)); @@ -1875,7 +1874,7 @@ namespace ARMeilleure.Instructions } Operand dRes = context.BitwiseExclusiveOr(nIntOrLong2, nLong); - dRes = context.Add(dRes, nIntOrLong); + dRes = context.Add(dRes, nIntOrLong); SetIntOrZR(context, op.Rd, dRes); } diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index bec36e2d6..dcdcc65de 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -5,7 +5,6 @@ using ARMeilleure.Translation; using System; using System.Diagnostics; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper32; @@ -217,33 +216,22 @@ namespace ARMeilleure.Instructions string name = nameof(Math.Round); MethodInfo info = (op.Size & 1) == 0 - ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) }) - : typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) }); + ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) }) + : typeof(Math).GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) }); return context.Call(info, n, Const((int)roundMode)); } private static FPRoundingMode RMToRoundMode(int rm) { - FPRoundingMode roundMode; - switch (rm) + return rm switch { - case 0b00: - roundMode = FPRoundingMode.ToNearestAway; - break; - case 0b01: - roundMode = FPRoundingMode.ToNearest; - break; - case 0b10: - roundMode = FPRoundingMode.TowardsPlusInfinity; - break; - case 0b11: - roundMode = FPRoundingMode.TowardsMinusInfinity; - break; - default: - throw new ArgumentOutOfRangeException(nameof(rm)); - } - return roundMode; + 0b00 => FPRoundingMode.ToNearestAway, + 0b01 => FPRoundingMode.ToNearest, + 0b10 => FPRoundingMode.TowardsPlusInfinity, + 0b11 => FPRoundingMode.TowardsMinusInfinity, + _ => throw new ArgumentOutOfRangeException(nameof(rm)), + }; } // VCVTA/M/N/P (floating-point). @@ -270,22 +258,24 @@ namespace ARMeilleure.Instructions if (unsigned) { - inst = rm switch { + inst = rm switch + { 0b00 => Intrinsic.Arm64FcvtauGp, 0b01 => Intrinsic.Arm64FcvtnuGp, 0b10 => Intrinsic.Arm64FcvtpuGp, 0b11 => Intrinsic.Arm64FcvtmuGp, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) + _ => throw new InvalidOperationException($"{nameof(rm)} contains an invalid value: {rm}"), }; } else { - inst = rm switch { + inst = rm switch + { 0b00 => Intrinsic.Arm64FcvtasGp, 0b01 => Intrinsic.Arm64FcvtnsGp, 0b10 => Intrinsic.Arm64FcvtpsGp, 0b11 => Intrinsic.Arm64FcvtmsGp, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) + _ => throw new InvalidOperationException($"{nameof(rm)} contains an invalid value: {rm}"), }; } @@ -297,22 +287,24 @@ namespace ARMeilleure.Instructions { if (unsigned) { - inst = rm switch { + inst = rm switch + { 0b00 => Intrinsic.Arm64FcvtauS, 0b01 => Intrinsic.Arm64FcvtnuS, 0b10 => Intrinsic.Arm64FcvtpuS, 0b11 => Intrinsic.Arm64FcvtmuS, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) + _ => throw new InvalidOperationException($"{nameof(rm)} contains an invalid value: {rm}"), }; } else { - inst = rm switch { + inst = rm switch + { 0b00 => Intrinsic.Arm64FcvtasS, 0b01 => Intrinsic.Arm64FcvtnsS, 0b10 => Intrinsic.Arm64FcvtpsS, 0b11 => Intrinsic.Arm64FcvtmsS, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) + _ => throw new InvalidOperationException($"{nameof(rm)} contains an invalid value: {rm}"), }; } @@ -432,12 +424,13 @@ namespace ARMeilleure.Instructions if (Optimizations.UseAdvSimd) { - Intrinsic inst = rm switch { + Intrinsic inst = rm switch + { 0b00 => Intrinsic.Arm64FrintaS, 0b01 => Intrinsic.Arm64FrintnS, 0b10 => Intrinsic.Arm64FrintpS, 0b11 => Intrinsic.Arm64FrintmS, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) + _ => throw new InvalidOperationException($"{nameof(rm)} contains an invalid value: {rm}"), }; InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHash.cs b/src/ARMeilleure/Instructions/InstEmitSimdHash.cs index 4fb048eed..aee12d7dc 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHash.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHash.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.Instructions { static partial class InstEmit { -#region "Sha1" + #region "Sha1" public static void Sha1c_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -89,9 +89,9 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } -#endregion + #endregion -#region "Sha256" + #region "Sha256" public static void Sha256h_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; @@ -142,6 +142,6 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } -#endregion + #endregion } } diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHash32.cs b/src/ARMeilleure/Instructions/InstEmitSimdHash32.cs index 51334608d..c2bb951ab 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHash32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHash32.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.Instructions { static partial class InstEmit32 { -#region "Sha256" + #region "Sha256" public static void Sha256h_V(ArmEmitterContext context) { OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; @@ -59,6 +59,6 @@ namespace ARMeilleure.Instructions context.Copy(GetVecA32(op.Qd), res); } -#endregion + #endregion } } diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs b/src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs index 23e4948d9..a672b159f 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs @@ -18,9 +18,9 @@ namespace ARMeilleure.Instructions Operand round2 = context.AddIntrinsic(Intrinsic.X86Sha256Rnds2, src1, src2, w); Operand round4 = context.AddIntrinsic(Intrinsic.X86Sha256Rnds2, src2, round2, w2); - + Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, round4, round2, Const(part2 ? 0x11 : 0xbb)); - + return res; } @@ -53,4 +53,4 @@ namespace ARMeilleure.Instructions return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)), x, y, z); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs index c44c9b4d9..35052ad11 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -6,7 +6,6 @@ using ARMeilleure.Translation; using System; using System.Diagnostics; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -18,19 +17,19 @@ namespace ARMeilleure.Instructions static class InstEmitSimdHelper { -#region "Masks" + #region "Masks" public static readonly long[] EvenMasks = new long[] { 14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0, // B 13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0, // H - 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0 // S + 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0, // S }; public static readonly long[] OddMasks = new long[] { 15L << 56 | 13L << 48 | 11L << 40 | 09L << 32 | 07L << 24 | 05L << 16 | 03L << 8 | 01L << 0, // B 15L << 56 | 14L << 48 | 11L << 40 | 10L << 32 | 07L << 24 | 06L << 16 | 03L << 8 | 02L << 0, // H - 15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0 // S + 15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0, // S }; public static readonly long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0; @@ -38,19 +37,19 @@ namespace ARMeilleure.Instructions public static ulong X86GetGf2p8LogicalShiftLeft(int shift) { ulong identity = (0b00000001UL << 56) | (0b00000010UL << 48) | (0b00000100UL << 40) | (0b00001000UL << 32) | - (0b00010000UL << 24) | (0b00100000UL << 16) | (0b01000000UL << 8) | (0b10000000UL << 0); + (0b00010000UL << 24) | (0b00100000UL << 16) | (0b01000000UL << 8) | (0b10000000UL << 0); return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8); } -#endregion + #endregion -#region "X86 SSE Intrinsics" + #region "X86 SSE Intrinsics" public static readonly Intrinsic[] X86PaddInstruction = new Intrinsic[] { Intrinsic.X86Paddb, Intrinsic.X86Paddw, Intrinsic.X86Paddd, - Intrinsic.X86Paddq + Intrinsic.X86Paddq, }; public static readonly Intrinsic[] X86PcmpeqInstruction = new Intrinsic[] @@ -58,7 +57,7 @@ namespace ARMeilleure.Instructions Intrinsic.X86Pcmpeqb, Intrinsic.X86Pcmpeqw, Intrinsic.X86Pcmpeqd, - Intrinsic.X86Pcmpeqq + Intrinsic.X86Pcmpeqq, }; public static readonly Intrinsic[] X86PcmpgtInstruction = new Intrinsic[] @@ -66,49 +65,49 @@ namespace ARMeilleure.Instructions Intrinsic.X86Pcmpgtb, Intrinsic.X86Pcmpgtw, Intrinsic.X86Pcmpgtd, - Intrinsic.X86Pcmpgtq + Intrinsic.X86Pcmpgtq, }; public static readonly Intrinsic[] X86PmaxsInstruction = new Intrinsic[] { Intrinsic.X86Pmaxsb, Intrinsic.X86Pmaxsw, - Intrinsic.X86Pmaxsd + Intrinsic.X86Pmaxsd, }; public static readonly Intrinsic[] X86PmaxuInstruction = new Intrinsic[] { Intrinsic.X86Pmaxub, Intrinsic.X86Pmaxuw, - Intrinsic.X86Pmaxud + Intrinsic.X86Pmaxud, }; public static readonly Intrinsic[] X86PminsInstruction = new Intrinsic[] { Intrinsic.X86Pminsb, Intrinsic.X86Pminsw, - Intrinsic.X86Pminsd + Intrinsic.X86Pminsd, }; public static readonly Intrinsic[] X86PminuInstruction = new Intrinsic[] { Intrinsic.X86Pminub, Intrinsic.X86Pminuw, - Intrinsic.X86Pminud + Intrinsic.X86Pminud, }; public static readonly Intrinsic[] X86PmovsxInstruction = new Intrinsic[] { Intrinsic.X86Pmovsxbw, Intrinsic.X86Pmovsxwd, - Intrinsic.X86Pmovsxdq + Intrinsic.X86Pmovsxdq, }; public static readonly Intrinsic[] X86PmovzxInstruction = new Intrinsic[] { Intrinsic.X86Pmovzxbw, Intrinsic.X86Pmovzxwd, - Intrinsic.X86Pmovzxdq + Intrinsic.X86Pmovzxdq, }; public static readonly Intrinsic[] X86PsllInstruction = new Intrinsic[] @@ -116,14 +115,14 @@ namespace ARMeilleure.Instructions 0, Intrinsic.X86Psllw, Intrinsic.X86Pslld, - Intrinsic.X86Psllq + Intrinsic.X86Psllq, }; public static readonly Intrinsic[] X86PsraInstruction = new Intrinsic[] { 0, Intrinsic.X86Psraw, - Intrinsic.X86Psrad + Intrinsic.X86Psrad, }; public static readonly Intrinsic[] X86PsrlInstruction = new Intrinsic[] @@ -131,7 +130,7 @@ namespace ARMeilleure.Instructions 0, Intrinsic.X86Psrlw, Intrinsic.X86Psrld, - Intrinsic.X86Psrlq + Intrinsic.X86Psrlq, }; public static readonly Intrinsic[] X86PsubInstruction = new Intrinsic[] @@ -139,7 +138,7 @@ namespace ARMeilleure.Instructions Intrinsic.X86Psubb, Intrinsic.X86Psubw, Intrinsic.X86Psubd, - Intrinsic.X86Psubq + Intrinsic.X86Psubq, }; public static readonly Intrinsic[] X86PunpckhInstruction = new Intrinsic[] @@ -147,7 +146,7 @@ namespace ARMeilleure.Instructions Intrinsic.X86Punpckhbw, Intrinsic.X86Punpckhwd, Intrinsic.X86Punpckhdq, - Intrinsic.X86Punpckhqdq + Intrinsic.X86Punpckhqdq, }; public static readonly Intrinsic[] X86PunpcklInstruction = new Intrinsic[] @@ -155,9 +154,9 @@ namespace ARMeilleure.Instructions Intrinsic.X86Punpcklbw, Intrinsic.X86Punpcklwd, Intrinsic.X86Punpckldq, - Intrinsic.X86Punpcklqdq + Intrinsic.X86Punpcklqdq, }; -#endregion + #endregion public static void EnterArmFpMode(EmitterContext context, Func<FPState, Operand> getFpFlag) { @@ -310,15 +309,16 @@ namespace ARMeilleure.Instructions public static int X86GetRoundControl(FPRoundingMode roundMode) { - switch (roundMode) + return roundMode switch { - case FPRoundingMode.ToNearest: return 8 | 0; // even - case FPRoundingMode.TowardsPlusInfinity: return 8 | 2; - case FPRoundingMode.TowardsMinusInfinity: return 8 | 1; - case FPRoundingMode.TowardsZero: return 8 | 3; - } - - throw new ArgumentException($"Invalid rounding mode \"{roundMode}\"."); +#pragma warning disable IDE0055 // Disable formatting + FPRoundingMode.ToNearest => 8 | 0, // even + FPRoundingMode.TowardsPlusInfinity => 8 | 2, + FPRoundingMode.TowardsMinusInfinity => 8 | 1, + FPRoundingMode.TowardsZero => 8 | 3, + _ => throw new ArgumentException($"Invalid rounding mode \"{roundMode}\"."), +#pragma warning restore IDE0055 + }; } public static Operand EmitSse41RoundToNearestWithTiesToAwayOpF(ArmEmitterContext context, Operand n, bool scalar) @@ -334,11 +334,11 @@ namespace ARMeilleure.Instructions if ((op.Size & 1) == 0) { Operand signMask = scalar ? X86GetScalar(context, int.MinValue) : X86GetAllElements(context, int.MinValue); - signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy); + signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy); // 0x3EFFFFFF == BitConverter.SingleToInt32Bits(0.5f) - 1 Operand valueMask = scalar ? X86GetScalar(context, 0x3EFFFFFF) : X86GetAllElements(context, 0x3EFFFFFF); - valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask); + valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask); nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addss : Intrinsic.X86Addps, nCopy, valueMask); @@ -347,11 +347,11 @@ namespace ARMeilleure.Instructions else { Operand signMask = scalar ? X86GetScalar(context, long.MinValue) : X86GetAllElements(context, long.MinValue); - signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy); + signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy); // 0x3FDFFFFFFFFFFFFFL == BitConverter.DoubleToInt64Bits(0.5d) - 1L Operand valueMask = scalar ? X86GetScalar(context, 0x3FDFFFFFFFFFFFFFL) : X86GetAllElements(context, 0x3FDFFFFFFFFFFFFFL); - valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask); + valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask); nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addsd : Intrinsic.X86Addpd, nCopy, valueMask); @@ -461,7 +461,7 @@ namespace ARMeilleure.Instructions MethodInfo info = (op.Size & 1) == 0 ? typeof(MathF).GetMethod(name, new Type[] { typeof(float) }) - : typeof(Math). GetMethod(name, new Type[] { typeof(double) }); + : typeof(Math).GetMethod(name, new Type[] { typeof(double) }); return context.Call(info, n); } @@ -473,8 +473,8 @@ namespace ARMeilleure.Instructions string name = nameof(Math.Round); MethodInfo info = (op.Size & 1) == 0 - ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) }) - : typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) }); + ? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) }) + : typeof(Math).GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) }); return context.Call(info, n, Const((int)roundMode)); } @@ -482,7 +482,7 @@ namespace ARMeilleure.Instructions public static Operand EmitGetRoundingMode(ArmEmitterContext context) { Operand rMode = context.ShiftLeft(GetFpFlag(FPState.RMode1Flag), Const(1)); - rMode = context.BitwiseOr(rMode, GetFpFlag(FPState.RMode0Flag)); + rMode = context.BitwiseOr(rMode, GetFpFlag(FPState.RMode0Flag)); return rMode; } @@ -1015,8 +1015,8 @@ namespace ARMeilleure.Instructions for (int index = 0; index < elems; index++) { - Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size + 1, signed); - Operand me = EmitVectorExtract(context, op.Rm, part + index, op.Size, signed); + Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size + 1, signed); + Operand me = EmitVectorExtract(context, op.Rm, part + index, op.Size, signed); res = EmitVectorInsert(context, res, emit(ne, me), index, op.Size + 1); } @@ -1077,9 +1077,9 @@ namespace ARMeilleure.Instructions for (int index = 0; index < elems; index++) { - Operand de = EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); - Operand ne = EmitVectorExtract(context, op.Rn, part + index, op.Size, signed); - Operand me = EmitVectorExtract(context, op.Rm, part + index, op.Size, signed); + Operand de = EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); + Operand ne = EmitVectorExtract(context, op.Rn, part + index, op.Size, signed); + Operand me = EmitVectorExtract(context, op.Rm, part + index, op.Size, signed); res = EmitVectorInsert(context, res, emit(de, ne, me), index, op.Size + 1); } @@ -1143,8 +1143,8 @@ namespace ARMeilleure.Instructions for (int index = 0; index < elems; index++) { - Operand de = EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); - Operand ne = EmitVectorExtract(context, op.Rn, part + index, op.Size, signed); + Operand de = EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); + Operand ne = EmitVectorExtract(context, op.Rn, part + index, op.Size, signed); res = EmitVectorInsert(context, res, emit(de, ne, me), index, op.Size + 1); } @@ -1174,13 +1174,13 @@ namespace ARMeilleure.Instructions { int pairIndex = index << 1; - Operand n0 = EmitVectorExtract(context, op.Rn, pairIndex, op.Size, signed); + Operand n0 = EmitVectorExtract(context, op.Rn, pairIndex, op.Size, signed); Operand n1 = EmitVectorExtract(context, op.Rn, pairIndex + 1, op.Size, signed); - Operand m0 = EmitVectorExtract(context, op.Rm, pairIndex, op.Size, signed); + Operand m0 = EmitVectorExtract(context, op.Rm, pairIndex, op.Size, signed); Operand m1 = EmitVectorExtract(context, op.Rm, pairIndex + 1, op.Size, signed); - res = EmitVectorInsert(context, res, emit(n0, n1), index, op.Size); + res = EmitVectorInsert(context, res, emit(n0, n1), index, op.Size); res = EmitVectorInsert(context, res, emit(m0, m1), pairs + index, op.Size); } @@ -1197,11 +1197,11 @@ namespace ARMeilleure.Instructions if (op.RegisterSize == RegisterSize.Simd64) { Operand zeroEvenMask = X86GetElements(context, ZeroMask, EvenMasks[op.Size]); - Operand zeroOddMask = X86GetElements(context, ZeroMask, OddMasks [op.Size]); + Operand zeroOddMask = X86GetElements(context, ZeroMask, OddMasks[op.Size]); Operand mN = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, n, m); // m:n - Operand left = context.AddIntrinsic(Intrinsic.X86Pshufb, mN, zeroEvenMask); // 0:even from m:n + Operand left = context.AddIntrinsic(Intrinsic.X86Pshufb, mN, zeroEvenMask); // 0:even from m:n Operand right = context.AddIntrinsic(Intrinsic.X86Pshufb, mN, zeroOddMask); // 0:odd from m:n context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst[op.Size], left, right)); @@ -1213,14 +1213,14 @@ namespace ARMeilleure.Instructions Operand oddEvenN = context.AddIntrinsic(Intrinsic.X86Pshufb, n, oddEvenMask); // odd:even from n Operand oddEvenM = context.AddIntrinsic(Intrinsic.X86Pshufb, m, oddEvenMask); // odd:even from m - Operand left = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, oddEvenN, oddEvenM); + Operand left = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, oddEvenN, oddEvenM); Operand right = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, oddEvenN, oddEvenM); context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst[op.Size], left, right)); } else { - Operand left = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, n, m); + Operand left = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, n, m); Operand right = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, n, m); context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst[3], left, right)); @@ -1381,7 +1381,7 @@ namespace ARMeilleure.Instructions Operand m0 = context.VectorExtract(type, GetVec(op.Rm), pairIndex); Operand m1 = context.VectorExtract(type, GetVec(op.Rm), pairIndex + 1); - res = context.VectorInsert(res, emit(n0, n1), index); + res = context.VectorInsert(res, emit(n0, n1), index); res = context.VectorInsert(res, emit(m0, m1), pairs + index); } @@ -1433,18 +1433,18 @@ namespace ARMeilleure.Instructions public enum CmpCondition { // Legacy Sse. - Equal = 0, // Ordered, non-signaling. - LessThan = 1, // Ordered, signaling. - LessThanOrEqual = 2, // Ordered, signaling. - UnorderedQ = 3, // Non-signaling. - NotLessThan = 5, // Unordered, signaling. + Equal = 0, // Ordered, non-signaling. + LessThan = 1, // Ordered, signaling. + LessThanOrEqual = 2, // Ordered, signaling. + UnorderedQ = 3, // Non-signaling. + NotLessThan = 5, // Unordered, signaling. NotLessThanOrEqual = 6, // Unordered, signaling. - OrderedQ = 7, // Non-signaling. + OrderedQ = 7, // Non-signaling. // Vex. GreaterThanOrEqual = 13, // Ordered, signaling. - GreaterThan = 14, // Ordered, signaling. - OrderedS = 23 // Signaling. + GreaterThan = 14, // Ordered, signaling. + OrderedS = 23, // Signaling. } [Flags] @@ -1459,7 +1459,7 @@ namespace ARMeilleure.Instructions Add = 1 << 3, Sub = 1 << 4, - Accumulate = 1 << 5 + Accumulate = 1 << 5, } public static void EmitScalarSaturatingUnaryOpSx(ArmEmitterContext context, Func1I emit) @@ -1579,7 +1579,7 @@ namespace ARMeilleure.Instructions { Operand de; Operand ne = EmitVectorExtract(context, op.Rn, index, op.Size, !signed); - Operand me = EmitVectorExtract(context, op.Rd, index, op.Size, signed); + Operand me = EmitVectorExtract(context, op.Rd, index, op.Size, signed); if (op.Size <= 2) { @@ -1627,7 +1627,7 @@ namespace ARMeilleure.Instructions [Flags] public enum SaturatingNarrowFlags { - Scalar = 1 << 0, + Scalar = 1 << 0, SignedSrc = 1 << 1, SignedDst = 1 << 2, @@ -1637,14 +1637,14 @@ namespace ARMeilleure.Instructions VectorSxSx = SignedSrc | SignedDst, VectorSxZx = SignedSrc, - VectorZxZx = 0 + VectorZxZx = 0, } public static void EmitSaturatingNarrowOp(ArmEmitterContext context, SaturatingNarrowFlags flags) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; - bool scalar = (flags & SaturatingNarrowFlags.Scalar) != 0; + bool scalar = (flags & SaturatingNarrowFlags.Scalar) != 0; bool signedSrc = (flags & SaturatingNarrowFlags.SignedSrc) != 0; bool signedDst = (flags & SaturatingNarrowFlags.SignedDst) != 0; @@ -2034,18 +2034,30 @@ namespace ARMeilleure.Instructions { switch (size) { - case 0: res = context.SignExtend8 (OperandType.I64, res); break; - case 1: res = context.SignExtend16(OperandType.I64, res); break; - case 2: res = context.SignExtend32(OperandType.I64, res); break; + case 0: + res = context.SignExtend8(OperandType.I64, res); + break; + case 1: + res = context.SignExtend16(OperandType.I64, res); + break; + case 2: + res = context.SignExtend32(OperandType.I64, res); + break; } } else { switch (size) { - case 0: res = context.ZeroExtend8 (OperandType.I64, res); break; - case 1: res = context.ZeroExtend16(OperandType.I64, res); break; - case 2: res = context.ZeroExtend32(OperandType.I64, res); break; + case 0: + res = context.ZeroExtend8(OperandType.I64, res); + break; + case 1: + res = context.ZeroExtend16(OperandType.I64, res); + break; + case 2: + res = context.ZeroExtend32(OperandType.I64, res); + break; } } @@ -2063,10 +2075,18 @@ namespace ARMeilleure.Instructions switch (size) { - case 0: vector = context.VectorInsert8 (vector, value, index); break; - case 1: vector = context.VectorInsert16(vector, value, index); break; - case 2: vector = context.VectorInsert (vector, value, index); break; - case 3: vector = context.VectorInsert (vector, value, index); break; + case 0: + vector = context.VectorInsert8(vector, value, index); + break; + case 1: + vector = context.VectorInsert16(vector, value, index); + break; + case 2: + vector = context.VectorInsert(vector, value, index); + break; + case 3: + vector = context.VectorInsert(vector, value, index); + break; } return vector; diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs index 36d27d425..c1c59b87b 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs @@ -4,7 +4,6 @@ using ARMeilleure.Translation; using System; using System.Diagnostics; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -19,18 +18,13 @@ namespace ARMeilleure.Instructions { public static (int, int) GetQuadwordAndSubindex(int index, RegisterSize size) { - switch (size) + return size switch { - case RegisterSize.Simd128: - return (index >> 1, 0); - case RegisterSize.Simd64: - case RegisterSize.Int64: - return (index >> 1, index & 1); - case RegisterSize.Int32: - return (index >> 2, index & 3); - } - - throw new ArgumentException("Unrecognized Vector Register Size."); + RegisterSize.Simd128 => (index >> 1, 0), + RegisterSize.Simd64 or RegisterSize.Int64 => (index >> 1, index & 1), + RegisterSize.Int32 => (index >> 2, index & 3), + _ => throw new ArgumentException("Unrecognized Vector Register Size."), + }; } public static Operand ExtractScalar(ArmEmitterContext context, OperandType type, int reg) @@ -327,7 +321,7 @@ namespace ARMeilleure.Instructions for (int index = 0; index < elems; index++) { Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size + 1, signed); - Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed); + Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed); if (op.Size == 2) { @@ -380,8 +374,8 @@ namespace ARMeilleure.Instructions for (int index = 0; index < elems; index++) { Operand de = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size + 1, signed); - Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed); - Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed); + Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed); + Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed); if (op.Size == 2) { @@ -778,7 +772,10 @@ namespace ARMeilleure.Instructions { // Index into 0, 0 into index. This swap happens at the start of an A32 scalar op if required. int index = reg & (doubleWidth ? 1 : 3); - if (index == 0) return target; + if (index == 0) + { + return target; + } if (doubleWidth) { @@ -974,7 +971,7 @@ namespace ARMeilleure.Instructions Intrinsic inst = (op.Size & 1) != 0 ? inst64 : inst32; - EmitScalarBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m)); + EmitScalarBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m)); } public static void EmitScalarTernaryOpSimd32(ArmEmitterContext context, Func3I scalarFunc) @@ -1195,7 +1192,7 @@ namespace ARMeilleure.Instructions : typeof(SoftFloat64).GetMethod(name); Array.Resize(ref callArgs, callArgs.Length + 1); - callArgs[callArgs.Length - 1] = Const(1); + callArgs[^1] = Const(1); context.ExitArmFpMode(); context.StoreToContext(); @@ -1245,16 +1242,24 @@ namespace ARMeilleure.Instructions { switch (size) { - case 0: res = context.SignExtend8(OperandType.I32, res); break; - case 1: res = context.SignExtend16(OperandType.I32, res); break; + case 0: + res = context.SignExtend8(OperandType.I32, res); + break; + case 1: + res = context.SignExtend16(OperandType.I32, res); + break; } } else { switch (size) { - case 0: res = context.ZeroExtend8(OperandType.I32, res); break; - case 1: res = context.ZeroExtend16(OperandType.I32, res); break; + case 0: + res = context.ZeroExtend8(OperandType.I32, res); + break; + case 1: + res = context.ZeroExtend16(OperandType.I32, res); + break; } } diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs index 804d915c4..568c07122 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs @@ -1,11 +1,9 @@ - using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Diagnostics; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -74,7 +72,10 @@ namespace ARMeilleure.Instructions public static Operand EmitExtractScalar(ArmEmitterContext context, Operand target, int reg, bool doubleWidth) { int index = reg & (doubleWidth ? 1 : 3); - if (index == 0) return target; // Element is already at index 0, so just return the vector directly. + if (index == 0) + { + return target; // Element is already at index 0, so just return the vector directly. + } if (doubleWidth) { @@ -249,7 +250,7 @@ namespace ARMeilleure.Instructions OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; - EmitScalarBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m)); + EmitScalarBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m)); } public static void EmitScalarTernaryOpSimd32(ArmEmitterContext context, Func3I scalarFunc) @@ -336,16 +337,17 @@ namespace ARMeilleure.Instructions CmpCondition.GreaterThanOrEqual => Intrinsic.Arm64FcmgeVz, CmpCondition.LessThan => Intrinsic.Arm64FcmltVz, CmpCondition.LessThanOrEqual => Intrinsic.Arm64FcmleVz, - _ => throw new InvalidOperationException() + _ => throw new InvalidOperationException(), }; } - else { + else + { inst = cond switch { CmpCondition.Equal => Intrinsic.Arm64FcmeqV, CmpCondition.GreaterThan => Intrinsic.Arm64FcmgtV, CmpCondition.GreaterThanOrEqual => Intrinsic.Arm64FcmgeV, - _ => throw new InvalidOperationException() + _ => throw new InvalidOperationException(), }; } @@ -367,4 +369,4 @@ namespace ARMeilleure.Instructions } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs index f0d242ae2..70dfc0fbd 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs @@ -50,7 +50,7 @@ namespace ARMeilleure.Instructions } SetIntOrZR(context, op.Rd, op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (inst, n) + ? context.AddIntrinsicInt(inst, n) : context.AddIntrinsicLong(inst, n)); } @@ -288,7 +288,7 @@ namespace ARMeilleure.Instructions } SetIntOrZR(context, op.Rd, op.RegisterSize == RegisterSize.Int32 - ? context.AddIntrinsicInt (inst, n, Const(fBits)) + ? context.AddIntrinsicInt(inst, n, Const(fBits)) : context.AddIntrinsicLong(inst, n, Const(fBits))); } @@ -695,7 +695,7 @@ namespace ARMeilleure.Instructions { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false; + bool cmpWithZero = op is not OpCodeSimdFcond && op.Bit3; Intrinsic inst = signalNaNs ? Intrinsic.Arm64FcmpeS : Intrinsic.Arm64FcmpS; @@ -717,4 +717,4 @@ namespace ARMeilleure.Instructions SetFlag(context, PState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(31)), one)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs b/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs index 2bf531e6c..97e3da67e 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs @@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; using System.Diagnostics; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -80,10 +79,11 @@ namespace ARMeilleure.Instructions int eSize = 8 << op.Size; Operand d = GetVec(op.Rd); - Operand imm = eSize switch { + Operand imm = eSize switch + { 16 => X86GetAllElements(context, (short)~op.Immediate), 32 => X86GetAllElements(context, (int)~op.Immediate), - _ => throw new InvalidOperationException($"Invalid element size {eSize}.") + _ => throw new InvalidOperationException($"Invalid element size {eSize}."), }; Operand res = context.AddIntrinsic(Intrinsic.X86Pand, d, imm); @@ -380,10 +380,11 @@ namespace ARMeilleure.Instructions int eSize = 8 << op.Size; Operand d = GetVec(op.Rd); - Operand imm = eSize switch { + Operand imm = eSize switch + { 16 => X86GetAllElements(context, (short)op.Immediate), 32 => X86GetAllElements(context, (int)op.Immediate), - _ => throw new InvalidOperationException($"Invalid element size {eSize}.") + _ => throw new InvalidOperationException($"Invalid element size {eSize}."), }; Operand res = context.AddIntrinsic(Intrinsic.X86Por, d, imm); @@ -414,8 +415,8 @@ namespace ARMeilleure.Instructions (0b00010000L << 32) | (0b00001000L << 24) | (0b00000100L << 16) | - (0b00000010L << 8) | - (0b00000001L << 0); + (0b00000010L << 8) | + (0b00000001L << 0); Operand vBitMatrix = X86GetAllElements(context, bitMatrix); @@ -451,13 +452,13 @@ namespace ARMeilleure.Instructions Debug.Assert(op.Type == OperandType.I64); Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaul)), Const(1)), - context.ShiftLeft (context.BitwiseAnd(op, Const(0x55ul)), Const(1))); + context.ShiftLeft(context.BitwiseAnd(op, Const(0x55ul)), Const(1))); val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccul)), Const(2)), - context.ShiftLeft (context.BitwiseAnd(val, Const(0x33ul)), Const(2))); + context.ShiftLeft(context.BitwiseAnd(val, Const(0x33ul)), Const(2))); return context.BitwiseOr(context.ShiftRightUI(val, Const(4)), - context.ShiftLeft (context.BitwiseAnd(val, Const(0x0ful)), Const(4))); + context.ShiftLeft(context.BitwiseAnd(val, Const(0x0ful)), Const(4))); } public static void Rev16_V(ArmEmitterContext context) diff --git a/src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs b/src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs index 68ef4ed17..747acb101 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs @@ -52,9 +52,15 @@ namespace ARMeilleure.Instructions // Replicate fields to fill the 64-bits, if size is < 64-bits. switch (op.Size) { - case 0: immediate *= 0x0101010101010101L; break; - case 1: immediate *= 0x0001000100010001L; break; - case 2: immediate *= 0x0000000100000001L; break; + case 0: + immediate *= 0x0101010101010101L; + break; + case 1: + immediate *= 0x0001000100010001L; + break; + case 2: + immediate *= 0x0000000100000001L; + break; } Operand imm = Const(immediate); @@ -199,9 +205,15 @@ namespace ARMeilleure.Instructions // Replicate fields to fill the 64-bits, if size is < 64-bits. switch (op.Size) { - case 0: immediate *= 0x0101010101010101L; break; - case 1: immediate *= 0x0001000100010001L; break; - case 2: immediate *= 0x0000000100000001L; break; + case 0: + immediate *= 0x0101010101010101L; + break; + case 1: + immediate *= 0x0001000100010001L; + break; + case 2: + immediate *= 0x0000000100000001L; + break; } Operand imm = Const(immediate); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdMemory.cs b/src/ARMeilleure/Instructions/InstEmitSimdMemory.cs index 9b19872af..dedf0fa05 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdMemory.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdMemory.cs @@ -40,6 +40,7 @@ namespace ARMeilleure.Instructions long offset = 0; +#pragma warning disable IDE0055 // Disable formatting for (int rep = 0; rep < op.Reps; rep++) for (int elem = 0; elem < op.Elems; elem++) for (int sElem = 0; sElem < op.SElems; sElem++) @@ -66,6 +67,7 @@ namespace ARMeilleure.Instructions offset += 1 << op.Size; } +#pragma warning restore IDE0055 if (op.WBack) { @@ -157,4 +159,4 @@ namespace ARMeilleure.Instructions context.Copy(n, context.Add(n, m)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/InstEmitSimdMove.cs b/src/ARMeilleure/Instructions/InstEmitSimdMove.cs index b58a32f69..85c98fe3a 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdMove.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdMove.cs @@ -3,7 +3,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System.Collections.Generic; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -12,19 +11,19 @@ namespace ARMeilleure.Instructions { static partial class InstEmit { -#region "Masks" + #region "Masks" private static readonly long[] _masksE0_Uzp = new long[] { 13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0, - 11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0 + 11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0, }; private static readonly long[] _masksE1_Uzp = new long[] { 15L << 56 | 11L << 48 | 07L << 40 | 03L << 32 | 14L << 24 | 10L << 16 | 06L << 8 | 02L << 0, - 15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0 + 15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0, }; -#endregion + #endregion public static void Dup_Gp(ArmEmitterContext context) { @@ -36,9 +35,17 @@ namespace ARMeilleure.Instructions { switch (op.Size) { - case 0: n = context.ZeroExtend8 (n.Type, n); n = context.Multiply(n, Const(n.Type, 0x01010101)); break; - case 1: n = context.ZeroExtend16(n.Type, n); n = context.Multiply(n, Const(n.Type, 0x00010001)); break; - case 2: n = context.ZeroExtend32(n.Type, n); break; + case 0: + n = context.ZeroExtend8(n.Type, n); + n = context.Multiply(n, Const(n.Type, 0x01010101)); + break; + case 1: + n = context.ZeroExtend16(n.Type, n); + n = context.Multiply(n, Const(n.Type, 0x00010001)); + break; + case 2: + n = context.ZeroExtend32(n.Type, n); + break; } Operand res = context.VectorInsert(context.VectorZero(), n, 0); @@ -209,7 +216,7 @@ namespace ARMeilleure.Instructions OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp; Operand lblTrue = Label(); - Operand lblEnd = Label(); + Operand lblEnd = Label(); Operand isTrue = InstEmitFlowHelper.GetCondTrue(context, op.Cond); @@ -353,7 +360,7 @@ namespace ARMeilleure.Instructions { OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp; - Operand d = GetVec(op.Rd); + Operand d = GetVec(op.Rd); Operand ne = EmitVectorExtractZx(context, op.Rn, op.SrcIndex, op.Size); context.Copy(d, EmitVectorInsert(context, d, ne, op.DstIndex, op.Size)); @@ -497,8 +504,12 @@ namespace ARMeilleure.Instructions switch (op.Size) { - case 0: imm *= 0x01010101; break; - case 1: imm *= 0x00010001; break; + case 0: + imm *= 0x01010101; + break; + case 1: + imm *= 0x00010001; + break; } if (not) @@ -543,7 +554,7 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, mask); - mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m); + mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m); res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mMask); } @@ -557,7 +568,7 @@ namespace ARMeilleure.Instructions Operand mSubMask = context.AddIntrinsic(Intrinsic.X86Psubb, m, idxMask); Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, mSubMask, mask); - mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, mSubMask); + mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, mSubMask); Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, ni, mMask); @@ -566,7 +577,7 @@ namespace ARMeilleure.Instructions if (!isTbl) { - Operand idxMask = X86GetAllElements(context, (0x1010101010101010L * op.Size) - 0x0101010101010101L); + Operand idxMask = X86GetAllElements(context, (0x1010101010101010L * op.Size) - 0x0101010101010101L); Operand zeroMask = context.VectorZero(); Operand mPosMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, idxMask); @@ -590,7 +601,7 @@ namespace ARMeilleure.Instructions { Operand d = GetVec(op.Rd); - List<Operand> args = new List<Operand>(); + List<Operand> args = new(); if (!isTbl) { @@ -612,20 +623,36 @@ namespace ARMeilleure.Instructions { switch (op.Size) { - case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)); break; - case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)); break; - case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)); break; - case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)); break; + case 1: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)); + break; + case 2: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)); + break; + case 3: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)); + break; + case 4: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)); + break; } } else { switch (op.Size) { - case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)); break; - case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)); break; - case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)); break; - case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)); break; + case 1: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)); + break; + case 2: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)); + break; + case 3: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)); + break; + case 4: + info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)); + break; } } @@ -644,7 +671,7 @@ namespace ARMeilleure.Instructions if (op.Size < 3) { long maskE0 = EvenMasks[op.Size]; - long maskE1 = OddMasks [op.Size]; + long maskE1 = OddMasks[op.Size]; mask = X86GetScalar(context, maskE0); @@ -691,7 +718,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractZx(context, op.Rn, pairIndex + part, op.Size); Operand me = EmitVectorExtractZx(context, op.Rm, pairIndex + part, op.Size); - res = EmitVectorInsert(context, res, ne, pairIndex, op.Size); + res = EmitVectorInsert(context, res, ne, pairIndex, op.Size); res = EmitVectorInsert(context, res, me, pairIndex + 1, op.Size); } @@ -712,7 +739,7 @@ namespace ARMeilleure.Instructions if (op.Size < 3) { long maskE0 = EvenMasks[op.Size]; - long maskE1 = OddMasks [op.Size]; + long maskE1 = OddMasks[op.Size]; mask = X86GetScalar(context, maskE0); @@ -784,7 +811,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); Operand me = EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); - res = EmitVectorInsert(context, res, ne, index, op.Size); + res = EmitVectorInsert(context, res, ne, index, op.Size); res = EmitVectorInsert(context, res, me, pairs + index, op.Size); } @@ -839,7 +866,7 @@ namespace ARMeilleure.Instructions Operand ne = EmitVectorExtractZx(context, op.Rn, baseIndex + index, op.Size); Operand me = EmitVectorExtractZx(context, op.Rm, baseIndex + index, op.Size); - res = EmitVectorInsert(context, res, ne, pairIndex, op.Size); + res = EmitVectorInsert(context, res, ne, pairIndex, op.Size); res = EmitVectorInsert(context, res, me, pairIndex + 1, op.Size); } diff --git a/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs index b8b91b31d..050d35e9d 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs @@ -2,7 +2,6 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper32; @@ -17,13 +16,13 @@ namespace ARMeilleure.Instructions private static readonly long[] _masksE0_Uzp = new long[] { 13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0, - 11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0 + 11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0, }; private static readonly long[] _masksE1_Uzp = new long[] { 15L << 56 | 11L << 48 | 07L << 40 | 03L << 32 | 14L << 24 | 10L << 16 | 06L << 8 | 02L << 0, - 15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0 + 15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0, }; #endregion @@ -220,7 +219,7 @@ namespace ARMeilleure.Instructions for (int index = 1; index < length; index++) { int newVn = (op.Vn + index) & 0x1F; - (int qn, int ind) = GetQuadwordAndSubindex(newVn, op.RegisterSize); + (int qn, _) = GetQuadwordAndSubindex(newVn, op.RegisterSize); Operand ni = EmitMoveDoubleWordToSide(context, GetVecA32(qn), newVn, 0); Operand idxMask = X86GetAllElements(context, 0x0808080808080808L * index); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdShift.cs b/src/ARMeilleure/Instructions/InstEmitSimdShift.cs index 19e41119b..be0670645 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdShift.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdShift.cs @@ -6,7 +6,6 @@ using ARMeilleure.Translation; using System; using System.Diagnostics; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -17,12 +16,12 @@ namespace ARMeilleure.Instructions static partial class InstEmit { -#region "Masks" + #region "Masks" private static readonly long[] _masks_SliSri = new long[] // Replication masks. { - 0x0101010101010101L, 0x0001000100010001L, 0x0000000100000001L, 0x0000000000000001L + 0x0101010101010101L, 0x0001000100010001L, 0x0000000100000001L, 0x0000000000000001L, }; -#endregion + #endregion public static void Rshrn_V(ArmEmitterContext context) { @@ -51,9 +50,15 @@ namespace ARMeilleure.Instructions switch (op.Size + 1) { - case 1: mask = X86GetAllElements(context, (int)roundConst * 0x00010001); break; - case 2: mask = X86GetAllElements(context, (int)roundConst); break; - case 3: mask = X86GetAllElements(context, roundConst); break; + case 1: + mask = X86GetAllElements(context, (int)roundConst * 0x00010001); + break; + case 2: + mask = X86GetAllElements(context, (int)roundConst); + break; + case 3: + mask = X86GetAllElements(context, roundConst); + break; } Intrinsic addInst = X86PaddInstruction[op.Size + 1]; @@ -1174,14 +1179,14 @@ namespace ARMeilleure.Instructions Scalar = 1 << 0, Signed = 1 << 1, - Round = 1 << 2, + Round = 1 << 2, Accumulate = 1 << 3, ScalarSx = Scalar | Signed, ScalarZx = Scalar, VectorSx = Signed, - VectorZx = 0 + VectorZx = 0, } private static void EmitScalarShrImmOpSx(ArmEmitterContext context, ShrImmFlags flags) @@ -1210,9 +1215,9 @@ namespace ARMeilleure.Instructions Operand res = context.VectorZero(); - bool scalar = (flags & ShrImmFlags.Scalar) != 0; - bool signed = (flags & ShrImmFlags.Signed) != 0; - bool round = (flags & ShrImmFlags.Round) != 0; + bool scalar = (flags & ShrImmFlags.Scalar) != 0; + bool signed = (flags & ShrImmFlags.Signed) != 0; + bool round = (flags & ShrImmFlags.Round) != 0; bool accumulate = (flags & ShrImmFlags.Accumulate) != 0; int shift = GetImmShr(op); @@ -1288,7 +1293,7 @@ namespace ARMeilleure.Instructions [Flags] private enum ShrImmSaturatingNarrowFlags { - Scalar = 1 << 0, + Scalar = 1 << 0, SignedSrc = 1 << 1, SignedDst = 1 << 2, @@ -1300,7 +1305,7 @@ namespace ARMeilleure.Instructions VectorSxSx = SignedSrc | SignedDst, VectorSxZx = SignedSrc, - VectorZxZx = 0 + VectorZxZx = 0, } private static void EmitRoundShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags) @@ -1312,10 +1317,10 @@ namespace ARMeilleure.Instructions { OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp; - bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0; + bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0; bool signedSrc = (flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0; bool signedDst = (flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0; - bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0; + bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0; int shift = GetImmShr(op); @@ -1585,7 +1590,7 @@ namespace ARMeilleure.Instructions Scalar = 1 << 0, Signed = 1 << 1, Round = 1 << 2, - Saturating = 1 << 3 + Saturating = 1 << 3, } private static void EmitShlRegOp(ArmEmitterContext context, ShlRegFlags flags = ShlRegFlags.None) diff --git a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs index 9ac680884..5c7d48287 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs @@ -5,7 +5,6 @@ using ARMeilleure.Translation; using System; using System.Diagnostics; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper; using static ARMeilleure.Instructions.InstEmitSimdHelper32; @@ -291,7 +290,7 @@ namespace ARMeilleure.Instructions VectorSxSx = SignedSrc | SignedDst, VectorSxZx = SignedSrc, - VectorZxZx = 0 + VectorZxZx = 0, } private static void EmitRoundShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags) @@ -303,10 +302,10 @@ namespace ARMeilleure.Instructions { OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp; - bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0; + bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0; bool signedSrc = (flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0; bool signedDst = (flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0; - bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0; + bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0; if (scalar) { diff --git a/src/ARMeilleure/Instructions/InstEmitSystem.cs b/src/ARMeilleure/Instructions/InstEmitSystem.cs index f84829aa1..8c430fc23 100644 --- a/src/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/src/ARMeilleure/Instructions/InstEmitSystem.cs @@ -28,18 +28,39 @@ namespace ARMeilleure.Instructions switch (GetPackedId(op)) { - case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break; - case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break; - case 0b11_011_0100_0010_000: EmitGetNzcv(context); return; - case 0b11_011_0100_0100_000: EmitGetFpcr(context); return; - case 0b11_011_0100_0100_001: EmitGetFpsr(context); return; - case 0b11_011_1101_0000_010: EmitGetTpidrEl0(context); return; - case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return; - case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break; - case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; - case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break; + case 0b11_011_0000_0000_001: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); + break; + case 0b11_011_0000_0000_111: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); + break; + case 0b11_011_0100_0010_000: + EmitGetNzcv(context); + return; + case 0b11_011_0100_0100_000: + EmitGetFpcr(context); + return; + case 0b11_011_0100_0100_001: + EmitGetFpsr(context); + return; + case 0b11_011_1101_0000_010: + EmitGetTpidrEl0(context); + return; + case 0b11_011_1101_0000_011: + EmitGetTpidrroEl0(context); + return; + case 0b11_011_1110_0000_000: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); + break; + case 0b11_011_1110_0000_001: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); + break; + case 0b11_011_1110_0000_010: + info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); + break; - default: throw new NotImplementedException($"Unknown MRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); + default: + throw new NotImplementedException($"Unknown MRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } SetIntOrZR(context, op.Rt, context.Call(info)); @@ -51,12 +72,21 @@ namespace ARMeilleure.Instructions switch (GetPackedId(op)) { - case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; - case 0b11_011_0100_0100_000: EmitSetFpcr(context); return; - case 0b11_011_0100_0100_001: EmitSetFpsr(context); return; - case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return; + case 0b11_011_0100_0010_000: + EmitSetNzcv(context); + return; + case 0b11_011_0100_0100_000: + EmitSetFpcr(context); + return; + case 0b11_011_0100_0100_001: + EmitSetFpsr(context); + return; + case 0b11_011_1101_0000_010: + EmitSetTpidrEl0(context); + return; - default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); + default: + throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } } @@ -75,20 +105,20 @@ namespace ARMeilleure.Instructions switch (GetPackedId(op)) { case 0b11_011_0111_0100_001: - { - // DC ZVA - Operand t = GetIntOrZR(context, op.Rt); - - for (long offset = 0; offset < DczSizeInBytes; offset += 8) { - Operand address = context.Add(t, Const(offset)); + // DC ZVA + Operand t = GetIntOrZR(context, op.Rt); - InstEmitMemoryHelper.EmitStore(context, address, RegisterConsts.ZeroIndex, 3); + for (long offset = 0; offset < DczSizeInBytes; offset += 8) + { + Operand address = context.Add(t, Const(offset)); + + InstEmitMemoryHelper.EmitStore(context, address, RegisterConsts.ZeroIndex, 3); + } + + break; } - break; - } - // No-op case 0b11_011_0111_1110_001: // DC CIVAC break; @@ -104,7 +134,7 @@ namespace ARMeilleure.Instructions { int id; - id = op.Op2 << 0; + id = op.Op2 << 0; id |= op.CRm << 3; id |= op.CRn << 7; id |= op.Op1 << 11; @@ -188,7 +218,7 @@ namespace ARMeilleure.Instructions OpCodeSystem op = (OpCodeSystem)context.CurrOp; Operand nzcv = GetIntOrZR(context, op.Rt); - nzcv = context.ConvertI64ToI32(nzcv); + nzcv = context.ConvertI64ToI32(nzcv); SetFlag(context, PState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.VFlag)), Const(1))); SetFlag(context, PState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.CFlag)), Const(1))); @@ -201,7 +231,7 @@ namespace ARMeilleure.Instructions OpCodeSystem op = (OpCodeSystem)context.CurrOp; Operand fpcr = GetIntOrZR(context, op.Rt); - fpcr = context.ConvertI64ToI32(fpcr); + fpcr = context.ConvertI64ToI32(fpcr); for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++) { @@ -221,7 +251,7 @@ namespace ARMeilleure.Instructions context.ClearQcFlagIfModified(); Operand fpsr = GetIntOrZR(context, op.Rt); - fpsr = context.ConvertI64ToI32(fpsr); + fpsr = context.ConvertI64ToI32(fpsr); for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++) { diff --git a/src/ARMeilleure/Instructions/InstEmitSystem32.cs b/src/ARMeilleure/Instructions/InstEmitSystem32.cs index f2732c998..82e625715 100644 --- a/src/ARMeilleure/Instructions/InstEmitSystem32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSystem32.cs @@ -4,7 +4,6 @@ using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Reflection; - using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -34,7 +33,8 @@ namespace ARMeilleure.Instructions switch (op.Opc2) { case 2: - EmitSetTpidrEl0(context); return; + EmitSetTpidrEl0(context); + return; default: throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); @@ -83,17 +83,13 @@ namespace ARMeilleure.Instructions throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); } - switch (op.Opc2) + result = op.Opc2 switch { - case 2: - result = EmitGetTpidrEl0(context); break; - - case 3: - result = EmitGetTpidrroEl0(context); break; - - default: - throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); - } + 2 => EmitGetTpidrEl0(context), + 3 => EmitGetTpidrroEl0(context), + _ => throw new NotImplementedException( + $"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."), + }; break; @@ -126,27 +122,16 @@ namespace ARMeilleure.Instructions } int opc = op.MrrcOp; - - MethodInfo info; - - switch (op.CRm) + MethodInfo info = op.CRm switch { - case 14: // Timer. - switch (opc) - { - case 0: - info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; - - default: - throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); - } - - break; - - default: - throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X} at 0x{op.Address:X}."); - } - + // Timer. + 14 => opc switch + { + 0 => typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)), + _ => throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."), + }, + _ => throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X} at 0x{op.Address:X}."), + }; Operand result = context.Call(info); SetIntA32(context, op.Rt, context.ConvertI64ToI32(result)); @@ -235,7 +220,8 @@ namespace ARMeilleure.Instructions case 0b0000: // FPSID throw new NotImplementedException("Supervisor Only"); case 0b0001: // FPSCR - EmitGetFpscr(context); return; + EmitGetFpscr(context); + return; case 0b0101: // MVFR2 throw new NotImplementedException("MVFR2"); case 0b0110: // MVFR1 @@ -258,7 +244,8 @@ namespace ARMeilleure.Instructions case 0b0000: // FPSID throw new NotImplementedException("Supervisor Only"); case 0b0001: // FPSCR - EmitSetFpscr(context); return; + EmitSetFpscr(context); + return; case 0b0101: // MVFR2 throw new NotImplementedException("MVFR2"); case 0b0110: // MVFR1 diff --git a/src/ARMeilleure/Instructions/NativeInterface.cs b/src/ARMeilleure/Instructions/NativeInterface.cs index 2c35387a6..d1b2e353c 100644 --- a/src/ARMeilleure/Instructions/NativeInterface.cs +++ b/src/ARMeilleure/Instructions/NativeInterface.cs @@ -64,12 +64,12 @@ namespace ARMeilleure.Instructions #region "System registers" public static ulong GetCtrEl0() { - return (ulong)GetContext().CtrEl0; + return GetContext().CtrEl0; } public static ulong GetDczidEl0() { - return (ulong)GetContext().DczidEl0; + return GetContext().DczidEl0; } public static ulong GetCntfrqEl0() @@ -192,4 +192,4 @@ namespace ARMeilleure.Instructions return Context.Memory; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Instructions/SoftFallback.cs b/src/ARMeilleure/Instructions/SoftFallback.cs index 06d76a67c..c4fe677bf 100644 --- a/src/ARMeilleure/Instructions/SoftFallback.cs +++ b/src/ARMeilleure/Instructions/SoftFallback.cs @@ -5,7 +5,7 @@ namespace ARMeilleure.Instructions { static class SoftFallback { -#region "ShrImm64" + #region "ShrImm64" public static long SignedShrImm64(long value, long roundConst, int shift) { if (roundConst == 0L) @@ -89,12 +89,15 @@ namespace ARMeilleure.Instructions } } } -#endregion + #endregion -#region "Saturation" + #region "Saturation" public static int SatF32ToS32(float value) { - if (float.IsNaN(value)) return 0; + if (float.IsNaN(value)) + { + return 0; + } return value >= int.MaxValue ? int.MaxValue : value <= int.MinValue ? int.MinValue : (int)value; @@ -102,7 +105,10 @@ namespace ARMeilleure.Instructions public static long SatF32ToS64(float value) { - if (float.IsNaN(value)) return 0; + if (float.IsNaN(value)) + { + return 0; + } return value >= long.MaxValue ? long.MaxValue : value <= long.MinValue ? long.MinValue : (long)value; @@ -110,7 +116,10 @@ namespace ARMeilleure.Instructions public static uint SatF32ToU32(float value) { - if (float.IsNaN(value)) return 0; + if (float.IsNaN(value)) + { + return 0; + } return value >= uint.MaxValue ? uint.MaxValue : value <= uint.MinValue ? uint.MinValue : (uint)value; @@ -118,7 +127,10 @@ namespace ARMeilleure.Instructions public static ulong SatF32ToU64(float value) { - if (float.IsNaN(value)) return 0; + if (float.IsNaN(value)) + { + return 0; + } return value >= ulong.MaxValue ? ulong.MaxValue : value <= ulong.MinValue ? ulong.MinValue : (ulong)value; @@ -126,7 +138,10 @@ namespace ARMeilleure.Instructions public static int SatF64ToS32(double value) { - if (double.IsNaN(value)) return 0; + if (double.IsNaN(value)) + { + return 0; + } return value >= int.MaxValue ? int.MaxValue : value <= int.MinValue ? int.MinValue : (int)value; @@ -134,7 +149,10 @@ namespace ARMeilleure.Instructions public static long SatF64ToS64(double value) { - if (double.IsNaN(value)) return 0; + if (double.IsNaN(value)) + { + return 0; + } return value >= long.MaxValue ? long.MaxValue : value <= long.MinValue ? long.MinValue : (long)value; @@ -142,7 +160,10 @@ namespace ARMeilleure.Instructions public static uint SatF64ToU32(double value) { - if (double.IsNaN(value)) return 0; + if (double.IsNaN(value)) + { + return 0; + } return value >= uint.MaxValue ? uint.MaxValue : value <= uint.MinValue ? uint.MinValue : (uint)value; @@ -150,14 +171,17 @@ namespace ARMeilleure.Instructions public static ulong SatF64ToU64(double value) { - if (double.IsNaN(value)) return 0; + if (double.IsNaN(value)) + { + return 0; + } return value >= ulong.MaxValue ? ulong.MaxValue : value <= ulong.MinValue ? ulong.MinValue : (ulong)value; } -#endregion + #endregion -#region "Count" + #region "Count" public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). { value ^= value >> 1; @@ -197,9 +221,9 @@ namespace ARMeilleure.Instructions return (ulong)count; } -#endregion + #endregion -#region "Table" + #region "Table" public static V128 Tbl1(V128 vector, int bytes, V128 tb0) { return TblOrTbx(default, vector, bytes, tb0); @@ -270,21 +294,21 @@ namespace ARMeilleure.Instructions return new V128(res); } -#endregion + #endregion -#region "Crc32" - private const uint Crc32RevPoly = 0xedb88320; + #region "Crc32" + private const uint Crc32RevPoly = 0xedb88320; private const uint Crc32cRevPoly = 0x82f63b78; - public static uint Crc32b(uint crc, byte value) => Crc32 (crc, Crc32RevPoly, value); + public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value); public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value); - public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value); - public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value); + public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value); + public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value); - public static uint Crc32cb(uint crc, byte value) => Crc32 (crc, Crc32cRevPoly, value); + public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value); public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value); - public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value); - public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value); + public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value); + public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value); private static uint Crc32h(uint crc, uint poly, ushort val) { @@ -331,9 +355,9 @@ namespace ARMeilleure.Instructions return crc; } -#endregion + #endregion -#region "Aes" + #region "Aes" public static V128 Decrypt(V128 value, V128 roundKey) { return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey)); @@ -353,9 +377,9 @@ namespace ARMeilleure.Instructions { return CryptoHelper.AesMixColumns(value); } -#endregion + #endregion -#region "Sha1" + #region "Sha1" public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk) { for (int e = 0; e <= 3; e++) @@ -426,7 +450,7 @@ namespace ARMeilleure.Instructions ulong t2 = w4_7.Extract<ulong>(0); ulong t1 = w0_3.Extract<ulong>(1); - V128 result = new V128(t1, t2); + V128 result = new(t1, t2); return result ^ (w0_3 ^ w8_11); } @@ -472,9 +496,9 @@ namespace ARMeilleure.Instructions { return (value << count) | (value >> (32 - count)); } -#endregion + #endregion -#region "Sha256" + #region "Sha256" public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk) { return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true); @@ -487,7 +511,7 @@ namespace ARMeilleure.Instructions public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7) { - V128 result = new V128(); + V128 result = new(); for (int e = 0; e <= 3; e++) { @@ -505,7 +529,7 @@ namespace ARMeilleure.Instructions public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15) { - V128 result = new V128(); + V128 result = new(); ulong t1 = w12_15.Extract<ulong>(1); @@ -602,13 +626,13 @@ namespace ARMeilleure.Instructions ? (uint)(value & 0xFFFFFFFFUL) : (uint)(value >> 32); } -#endregion + #endregion public static V128 PolynomialMult64_128(ulong op1, ulong op2) { V128 result = V128.Zero; - V128 op2_128 = new V128(op2, 0); + V128 op2_128 = new(op2, 0); for (int i = 0; i < 64; i++) { diff --git a/src/ARMeilleure/Instructions/SoftFloat.cs b/src/ARMeilleure/Instructions/SoftFloat.cs index 4af73c6df..e8f44ce30 100644 --- a/src/ARMeilleure/Instructions/SoftFloat.cs +++ b/src/ARMeilleure/Instructions/SoftFloat.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.Instructions { static SoftFloat() { - RecipEstimateTable = BuildRecipEstimateTable(); + RecipEstimateTable = BuildRecipEstimateTable(); RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable(); } @@ -63,7 +63,7 @@ namespace ARMeilleure.Instructions while (src * (aux + 1u) * (aux + 1u) < (1u << 28)) { - aux = aux + 1u; + aux++; } uint dst = (aux + 1u) >> 1; @@ -133,8 +133,8 @@ namespace ARMeilleure.Instructions { sign = (~(uint)valueBits & 0x8000u) == 0u; - uint exp16 = ((uint)valueBits & 0x7C00u) >> 10; - uint frac16 = (uint)valueBits & 0x03FFu; + uint exp16 = ((uint)valueBits & 0x7C00u) >> 10; + uint frac16 = (uint)valueBits & 0x03FFu; double real; @@ -180,17 +180,17 @@ namespace ARMeilleure.Instructions const int e = 5; const int f = 10; - bool sign; + bool sign; double mantissa; if (real < 0d) { - sign = true; + sign = true; mantissa = -real; } else { - sign = false; + sign = false; mantissa = real; } @@ -229,22 +229,22 @@ namespace ARMeilleure.Instructions switch (context.Fpcr.GetRoundingMode()) { case FPRoundingMode.ToNearest: - roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); overflowToInf = true; break; case FPRoundingMode.TowardsPlusInfinity: - roundUp = (error != 0d && !sign); + roundUp = (error != 0d && !sign); overflowToInf = !sign; break; case FPRoundingMode.TowardsMinusInfinity: - roundUp = (error != 0d && sign); + roundUp = (error != 0d && sign); overflowToInf = sign; break; case FPRoundingMode.TowardsZero: - roundUp = false; + roundUp = false; overflowToInf = false; break; @@ -359,17 +359,17 @@ namespace ARMeilleure.Instructions const int e = 8; const int f = 23; - bool sign; + bool sign; double mantissa; if (real < 0d) { - sign = true; + sign = true; mantissa = -real; } else { - sign = false; + sign = false; mantissa = real; } @@ -415,22 +415,22 @@ namespace ARMeilleure.Instructions switch (context.Fpcr.GetRoundingMode()) { case FPRoundingMode.ToNearest: - roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); overflowToInf = true; break; case FPRoundingMode.TowardsPlusInfinity: - roundUp = (error != 0d && !sign); + roundUp = (error != 0d && !sign); overflowToInf = !sign; break; case FPRoundingMode.TowardsMinusInfinity: - roundUp = (error != 0d && sign); + roundUp = (error != 0d && sign); overflowToInf = sign; break; case FPRoundingMode.TowardsZero: - roundUp = false; + roundUp = false; overflowToInf = false; break; @@ -534,17 +534,17 @@ namespace ARMeilleure.Instructions const int e = 11; const int f = 52; - bool sign; + bool sign; double mantissa; if (real < 0d) { - sign = true; + sign = true; mantissa = -real; } else { - sign = false; + sign = false; mantissa = real; } @@ -590,22 +590,22 @@ namespace ARMeilleure.Instructions switch (context.Fpcr.GetRoundingMode()) { case FPRoundingMode.ToNearest: - roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); overflowToInf = true; break; case FPRoundingMode.TowardsPlusInfinity: - roundUp = (error != 0d && !sign); + roundUp = (error != 0d && !sign); overflowToInf = !sign; break; case FPRoundingMode.TowardsMinusInfinity: - roundUp = (error != 0d && sign); + roundUp = (error != 0d && sign); overflowToInf = sign; break; case FPRoundingMode.TowardsZero: - roundUp = false; + roundUp = false; overflowToInf = false; break; @@ -728,8 +728,8 @@ namespace ARMeilleure.Instructions sign = (~valueBits & 0x80000000u) == 0u; - uint exp32 = (valueBits & 0x7F800000u) >> 23; - uint frac32 = valueBits & 0x007FFFFFu; + uint exp32 = (valueBits & 0x7F800000u) >> 23; + uint frac32 = valueBits & 0x007FFFFFu; double real; @@ -798,8 +798,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if (inf1 && inf2 && sign1 == !sign2) { @@ -840,8 +842,8 @@ namespace ARMeilleure.Instructions ExecutionContext context = NativeInterface.GetContext(); FPCR fpcr = context.Fpcr; - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out _, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); int result; @@ -995,8 +997,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && inf2) || (zero1 && zero2)) { @@ -1232,8 +1236,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -1276,11 +1282,13 @@ namespace ARMeilleure.Instructions FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; valueA = valueA.FPUnpack(out FPType typeA, out bool signA, out uint addend, context, fpcr); - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr); - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; float result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, context, fpcr); @@ -1293,10 +1301,11 @@ namespace ARMeilleure.Instructions if (!done) { - bool infA = typeA == FPType.Infinity; bool zeroA = typeA == FPType.Zero; + bool infA = typeA == FPType.Infinity; + bool zeroA = typeA == FPType.Zero; - bool signP = sign1 ^ sign2; - bool infP = inf1 || inf2; + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; bool zeroP = zero1 || zero2; if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) @@ -1359,8 +1368,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -1435,34 +1446,17 @@ namespace ARMeilleure.Instructions } else if (MathF.Abs(value) < MathF.Pow(2f, -128)) { - bool overflowToInf; - - switch (fpcr.GetRoundingMode()) + var overflowToInf = fpcr.GetRoundingMode() switch { - case FPRoundingMode.ToNearest: - overflowToInf = true; - break; - - case FPRoundingMode.TowardsPlusInfinity: - overflowToInf = !sign; - break; - - case FPRoundingMode.TowardsMinusInfinity: - overflowToInf = sign; - break; - - case FPRoundingMode.TowardsZero: - overflowToInf = false; - break; - - default: - throw new ArgumentException($"Invalid rounding mode \"{fpcr.GetRoundingMode()}\"."); - } - + FPRoundingMode.TowardsPlusInfinity => !sign, + FPRoundingMode.TowardsMinusInfinity => sign, + FPRoundingMode.TowardsZero => false, + _ => throw new ArgumentException($"Invalid rounding mode \"{fpcr.GetRoundingMode()}\"."), + }; result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); SoftFloat.FPProcessException(FPException.Overflow, context, fpcr); - SoftFloat.FPProcessException(FPException.Inexact, context, fpcr); + SoftFloat.FPProcessException(FPException.Inexact, context, fpcr); } else if ((fpcr & FPCR.Fz) != 0 && (MathF.Abs(value) >= MathF.Pow(2f, 126))) { @@ -1518,15 +1512,17 @@ namespace ARMeilleure.Instructions ExecutionContext context = NativeInterface.GetContext(); FPCR fpcr = context.StandardFpcrValue; - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out _, out uint op1, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out uint op2, context, fpcr); float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; float product; @@ -1559,8 +1555,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -1691,8 +1689,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if (inf1 && inf2 && sign1 == sign2) { @@ -1733,15 +1733,17 @@ namespace ARMeilleure.Instructions ExecutionContext context = NativeInterface.GetContext(); FPCR fpcr = context.StandardFpcrValue; - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out _, out uint op1, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out uint op2, context, fpcr); float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; float product; @@ -1774,8 +1776,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -1860,8 +1864,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if (inf1 && inf2 && sign1 == sign2) { @@ -1958,7 +1964,7 @@ namespace ARMeilleure.Instructions { if ((valueBits & 0x007FFFFFu) == 0u || (fpcr & FPCR.Fz) != 0) { - type = FPType.Zero; + type = FPType.Zero; value = FPZero(sign); if ((valueBits & 0x007FFFFFu) != 0u) @@ -1979,7 +1985,7 @@ namespace ARMeilleure.Instructions } else { - type = (~valueBits & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; + type = (~valueBits & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; value = FPZero(sign); } } @@ -2153,8 +2159,8 @@ namespace ARMeilleure.Instructions sign = (~valueBits & 0x8000000000000000ul) == 0u; - ulong exp64 = (valueBits & 0x7FF0000000000000ul) >> 52; - ulong frac64 = valueBits & 0x000FFFFFFFFFFFFFul; + ulong exp64 = (valueBits & 0x7FF0000000000000ul) >> 52; + ulong frac64 = valueBits & 0x000FFFFFFFFFFFFFul; double real; @@ -2223,8 +2229,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if (inf1 && inf2 && sign1 == !sign2) { @@ -2265,8 +2273,8 @@ namespace ARMeilleure.Instructions ExecutionContext context = NativeInterface.GetContext(); FPCR fpcr = context.Fpcr; - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out _, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out _, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out _, out _, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out _, context, fpcr); int result; @@ -2420,8 +2428,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && inf2) || (zero1 && zero2)) { @@ -2657,8 +2667,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -2701,11 +2713,13 @@ namespace ARMeilleure.Instructions FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; valueA = valueA.FPUnpack(out FPType typeA, out bool signA, out ulong addend, context, fpcr); - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr); - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; double result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, context, fpcr); @@ -2718,10 +2732,11 @@ namespace ARMeilleure.Instructions if (!done) { - bool infA = typeA == FPType.Infinity; bool zeroA = typeA == FPType.Zero; + bool infA = typeA == FPType.Infinity; + bool zeroA = typeA == FPType.Zero; - bool signP = sign1 ^ sign2; - bool infP = inf1 || inf2; + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; bool zeroP = zero1 || zero2; if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) @@ -2784,8 +2799,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -2860,34 +2877,17 @@ namespace ARMeilleure.Instructions } else if (Math.Abs(value) < Math.Pow(2d, -1024)) { - bool overflowToInf; - - switch (fpcr.GetRoundingMode()) + var overflowToInf = fpcr.GetRoundingMode() switch { - case FPRoundingMode.ToNearest: - overflowToInf = true; - break; - - case FPRoundingMode.TowardsPlusInfinity: - overflowToInf = !sign; - break; - - case FPRoundingMode.TowardsMinusInfinity: - overflowToInf = sign; - break; - - case FPRoundingMode.TowardsZero: - overflowToInf = false; - break; - - default: - throw new ArgumentException($"Invalid rounding mode \"{fpcr.GetRoundingMode()}\"."); - } - + FPRoundingMode.TowardsPlusInfinity => !sign, + FPRoundingMode.TowardsMinusInfinity => sign, + FPRoundingMode.TowardsZero => false, + _ => throw new ArgumentException($"Invalid rounding mode \"{fpcr.GetRoundingMode()}\"."), + }; result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); SoftFloat.FPProcessException(FPException.Overflow, context, fpcr); - SoftFloat.FPProcessException(FPException.Inexact, context, fpcr); + SoftFloat.FPProcessException(FPException.Inexact, context, fpcr); } else if ((fpcr & FPCR.Fz) != 0 && (Math.Abs(value) >= Math.Pow(2d, 1022))) { @@ -2943,15 +2943,17 @@ namespace ARMeilleure.Instructions ExecutionContext context = NativeInterface.GetContext(); FPCR fpcr = context.StandardFpcrValue; - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out _, out ulong op1, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out ulong op2, context, fpcr); double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; double product; @@ -2984,8 +2986,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -3116,8 +3120,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if (inf1 && inf2 && sign1 == sign2) { @@ -3158,15 +3164,17 @@ namespace ARMeilleure.Instructions ExecutionContext context = NativeInterface.GetContext(); FPCR fpcr = context.StandardFpcrValue; - value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr); - value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr); + value1 = value1.FPUnpack(out FPType type1, out _, out ulong op1, context, fpcr); + value2 = value2.FPUnpack(out FPType type2, out _, out ulong op2, context, fpcr); double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, context, fpcr); if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; double product; @@ -3199,8 +3207,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if ((inf1 && zero2) || (zero1 && inf2)) { @@ -3285,8 +3295,10 @@ namespace ARMeilleure.Instructions if (!done) { - bool inf1 = type1 == FPType.Infinity; bool zero1 = type1 == FPType.Zero; - bool inf2 = type2 == FPType.Infinity; bool zero2 = type2 == FPType.Zero; + bool inf1 = type1 == FPType.Infinity; + bool zero1 = type1 == FPType.Zero; + bool inf2 = type2 == FPType.Infinity; + bool zero2 = type2 == FPType.Zero; if (inf1 && inf2 && sign1 == sign2) { @@ -3383,7 +3395,7 @@ namespace ARMeilleure.Instructions { if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul || (fpcr & FPCR.Fz) != 0) { - type = FPType.Zero; + type = FPType.Zero; value = FPZero(sign); if ((valueBits & 0x000FFFFFFFFFFFFFul) != 0ul) @@ -3404,7 +3416,7 @@ namespace ARMeilleure.Instructions } else { - type = (~valueBits & 0x0008000000000000ul) == 0ul ? FPType.QNaN : FPType.SNaN; + type = (~valueBits & 0x0008000000000000ul) == 0ul ? FPType.QNaN : FPType.SNaN; value = FPZero(sign); } } diff --git a/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs b/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs index 07bd8b672..810461d7c 100644 --- a/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs +++ b/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs @@ -10,7 +10,7 @@ namespace ARMeilleure.IntermediateRepresentation private int _succCount; private BasicBlock _succ0; - private BasicBlock _succ1; + private readonly BasicBlock _succ1; private HashSet<BasicBlock> _domFrontiers; public int Index { get; set; } @@ -27,10 +27,7 @@ namespace ARMeilleure.IntermediateRepresentation { get { - if (_domFrontiers == null) - { - _domFrontiers = new HashSet<BasicBlock>(); - } + _domFrontiers ??= new HashSet<BasicBlock>(); return _domFrontiers; } @@ -108,7 +105,7 @@ namespace ARMeilleure.IntermediateRepresentation oldBlock.Predecessors.Remove(this); block.Predecessors.Add(this); - + oldBlock = block; } @@ -156,4 +153,4 @@ namespace ARMeilleure.IntermediateRepresentation return base.GetHashCode(); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs b/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs index 96cfee35a..74aaea6b1 100644 --- a/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs +++ b/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs @@ -3,6 +3,6 @@ enum BasicBlockFrequency { Default, - Cold + Cold, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/Comparison.cs b/src/ARMeilleure/IntermediateRepresentation/Comparison.cs index 628ce1051..e3a68b49d 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Comparison.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Comparison.cs @@ -2,16 +2,16 @@ { enum Comparison { - Equal = 0, - NotEqual = 1, - Greater = 2, - LessOrEqual = 3, - GreaterUI = 4, - LessOrEqualUI = 5, - GreaterOrEqual = 6, - Less = 7, - GreaterOrEqualUI = 8, - LessUI = 9 + Equal = 0, + NotEqual = 1, + Greater = 2, + LessOrEqual = 3, + GreaterUI = 4, + LessOrEqualUI = 5, + GreaterOrEqual = 6, + Less = 7, + GreaterOrEqualUI = 8, + LessUI = 9, } static class ComparisonExtensions diff --git a/src/ARMeilleure/IntermediateRepresentation/Instruction.cs b/src/ARMeilleure/IntermediateRepresentation/Instruction.cs index b55fe1dac..9bae8d1fb 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Instruction.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Instruction.cs @@ -67,6 +67,6 @@ namespace ARMeilleure.IntermediateRepresentation Phi, Spill, SpillArg, - StoreToContext + StoreToContext, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index df5d39ae4..b9cab6674 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace ARMeilleure.IntermediateRepresentation { [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum Intrinsic : ushort { // X86 (SSE and AVX) @@ -634,6 +636,6 @@ namespace ARMeilleure.IntermediateRepresentation Arm64VByte = 0 << Arm64VSizeShift, Arm64VHWord = 1 << Arm64VSizeShift, Arm64VWord = 2 << Arm64VSizeShift, - Arm64VDWord = 3 << Arm64VSizeShift + Arm64VDWord = 3 << Arm64VSizeShift, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs b/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs index 07d2633b4..9b3df8ca4 100644 --- a/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs +++ b/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs @@ -4,11 +4,11 @@ using System.Runtime.CompilerServices; namespace ARMeilleure.IntermediateRepresentation { - unsafe struct MemoryOperand + readonly unsafe struct MemoryOperand { private struct Data { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public byte Kind; public byte Type; #pragma warning restore CS0649 @@ -18,7 +18,7 @@ namespace ARMeilleure.IntermediateRepresentation public int Displacement; } - private Data* _data; + private readonly Data* _data; public MemoryOperand(Operand operand) { @@ -30,13 +30,13 @@ namespace ARMeilleure.IntermediateRepresentation public Operand BaseAddress { get => _data->BaseAddress; - set => _data->BaseAddress = value; + set => _data->BaseAddress = value; } public Operand Index { get => _data->Index; - set => _data->Index = value; + set => _data->Index = value; } public Multiplier Scale @@ -51,4 +51,4 @@ namespace ARMeilleure.IntermediateRepresentation set => _data->Displacement = value; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs b/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs index d6bc7d994..6bcdda014 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs @@ -6,6 +6,6 @@ namespace ARMeilleure.IntermediateRepresentation x2 = 1, x4 = 2, x8 = 3, - x16 = 4 + x16 = 4, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/Operand.cs b/src/ARMeilleure/IntermediateRepresentation/Operand.cs index 9e8de3ba4..89aefacb1 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Operand.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Operand.cs @@ -27,25 +27,25 @@ namespace ARMeilleure.IntermediateRepresentation private Data* _data; - public OperandKind Kind + public readonly OperandKind Kind { get => (OperandKind)_data->Kind; private set => _data->Kind = (byte)value; } - public OperandType Type + public readonly OperandType Type { get => (OperandType)_data->Type; private set => _data->Type = (byte)value; } - public ulong Value + public readonly ulong Value { get => _data->Value; private set => _data->Value = value; } - public Symbol Symbol + public readonly Symbol Symbol { get { @@ -69,7 +69,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public ReadOnlySpan<Operation> Assignments + public readonly ReadOnlySpan<Operation> Assignments { get { @@ -79,7 +79,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public ReadOnlySpan<Operation> Uses + public readonly ReadOnlySpan<Operation> Uses { get { @@ -89,13 +89,13 @@ namespace ARMeilleure.IntermediateRepresentation } } - public int UsesCount => (int)_data->UsesCount; - public int AssignmentsCount => _data->AssignmentsCount; + public readonly int UsesCount => (int)_data->UsesCount; + public readonly int AssignmentsCount => _data->AssignmentsCount; - public bool Relocatable => Symbol.Type != SymbolType.None; + public readonly bool Relocatable => Symbol.Type != SymbolType.None; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Register GetRegister() + public readonly Register GetRegister() { Debug.Assert(Kind == OperandKind.Register); @@ -103,52 +103,52 @@ namespace ARMeilleure.IntermediateRepresentation } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MemoryOperand GetMemory() + public readonly MemoryOperand GetMemory() { Debug.Assert(Kind == OperandKind.Memory); return new MemoryOperand(this); } - public int GetLocalNumber() + public readonly int GetLocalNumber() { Debug.Assert(Kind == OperandKind.LocalVariable); return (int)Value; } - public byte AsByte() + public readonly byte AsByte() { return (byte)Value; } - public short AsInt16() + public readonly short AsInt16() { return (short)Value; } - public int AsInt32() + public readonly int AsInt32() { return (int)Value; } - public long AsInt64() + public readonly long AsInt64() { return (long)Value; } - public float AsFloat() + public readonly float AsFloat() { return BitConverter.Int32BitsToSingle((int)Value); } - public double AsDouble() + public readonly double AsDouble() { return BitConverter.Int64BitsToDouble((long)Value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref ulong GetValueUnsafe() + internal readonly ref ulong GetValueUnsafe() { return ref _data->Value; } @@ -163,7 +163,7 @@ namespace ARMeilleure.IntermediateRepresentation Value = (ulong)number; } - public void AddAssignment(Operation operation) + public readonly void AddAssignment(Operation operation) { if (Kind == OperandKind.LocalVariable) { @@ -187,7 +187,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public void RemoveAssignment(Operation operation) + public readonly void RemoveAssignment(Operation operation) { if (Kind == OperandKind.LocalVariable) { @@ -211,7 +211,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public void AddUse(Operation operation) + public readonly void AddUse(Operation operation) { if (Kind == OperandKind.LocalVariable) { @@ -235,7 +235,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public void RemoveUse(Operation operation) + public readonly void RemoveUse(Operation operation) { if (Kind == OperandKind.LocalVariable) { @@ -259,7 +259,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public Span<Operation> GetUses(ref Span<Operation> buffer) + public readonly Span<Operation> GetUses(ref Span<Operation> buffer) { ReadOnlySpan<Operation> uses = Uses; @@ -270,7 +270,7 @@ namespace ARMeilleure.IntermediateRepresentation uses.CopyTo(buffer); - return buffer.Slice(0, uses.Length); + return buffer[..uses.Length]; } private static void New<T>(ref T* data, ref ushort count, ref ushort capacity, ushort initialCapacity) where T : unmanaged @@ -360,7 +360,7 @@ namespace ARMeilleure.IntermediateRepresentation { if (i + 1 < count) { - span.Slice(i + 1).CopyTo(span.Slice(i)); + span[(i + 1)..].CopyTo(span[i..]); } count--; @@ -380,7 +380,7 @@ namespace ARMeilleure.IntermediateRepresentation { if (i + 1 < count) { - span.Slice(i + 1).CopyTo(span.Slice(i)); + span[(i + 1)..].CopyTo(span[i..]); } count--; @@ -390,17 +390,17 @@ namespace ARMeilleure.IntermediateRepresentation } } - public override int GetHashCode() + public readonly override int GetHashCode() { return ((ulong)_data).GetHashCode(); } - public bool Equals(Operand operand) + public readonly bool Equals(Operand operand) { return operand._data == _data; } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is Operand operand && Equals(operand); } @@ -453,8 +453,10 @@ namespace ARMeilleure.IntermediateRepresentation // Look in the next InternTableProbeLength slots for a match. for (uint i = 0; i < InternTableProbeLength; i++) { - Operand interned = new(); - interned._data = &InternTable[(hash + i) % InternTableSize]; + Operand interned = new() + { + _data = &InternTable[(hash + i) % InternTableSize], + }; // If slot matches the allocation request then return that slot. if (interned.Kind == kind && interned.Type == type && interned.Value == value && interned.Symbol == symbol) @@ -479,11 +481,13 @@ namespace ARMeilleure.IntermediateRepresentation *data = default; - Operand result = new(); - result._data = data; - result.Value = value; - result.Kind = kind; - result.Type = type; + Operand result = new() + { + _data = data, + Value = value, + Kind = kind, + Type = type, + }; if (kind != OperandKind.Memory) { @@ -591,4 +595,4 @@ namespace ARMeilleure.IntermediateRepresentation } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs b/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs index adb835614..2b973f006 100644 --- a/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs +++ b/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs @@ -8,6 +8,6 @@ namespace ARMeilleure.IntermediateRepresentation LocalVariable, Memory, Register, - Undefined + Undefined, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/OperandType.cs b/src/ARMeilleure/IntermediateRepresentation/OperandType.cs index 81b22cf56..67ebdcde4 100644 --- a/src/ARMeilleure/IntermediateRepresentation/OperandType.cs +++ b/src/ARMeilleure/IntermediateRepresentation/OperandType.cs @@ -9,7 +9,7 @@ namespace ARMeilleure.IntermediateRepresentation I64, FP32, FP64, - V128 + V128, } static class OperandTypeExtensions @@ -22,44 +22,41 @@ namespace ARMeilleure.IntermediateRepresentation public static RegisterType ToRegisterType(this OperandType type) { - switch (type) + return type switch { - case OperandType.FP32: return RegisterType.Vector; - case OperandType.FP64: return RegisterType.Vector; - case OperandType.I32: return RegisterType.Integer; - case OperandType.I64: return RegisterType.Integer; - case OperandType.V128: return RegisterType.Vector; - } - - throw new InvalidOperationException($"Invalid operand type \"{type}\"."); + OperandType.FP32 => RegisterType.Vector, + OperandType.FP64 => RegisterType.Vector, + OperandType.I32 => RegisterType.Integer, + OperandType.I64 => RegisterType.Integer, + OperandType.V128 => RegisterType.Vector, + _ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."), + }; } public static int GetSizeInBytes(this OperandType type) { - switch (type) + return type switch { - case OperandType.FP32: return 4; - case OperandType.FP64: return 8; - case OperandType.I32: return 4; - case OperandType.I64: return 8; - case OperandType.V128: return 16; - } - - throw new InvalidOperationException($"Invalid operand type \"{type}\"."); + OperandType.FP32 => 4, + OperandType.FP64 => 8, + OperandType.I32 => 4, + OperandType.I64 => 8, + OperandType.V128 => 16, + _ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."), + }; } public static int GetSizeInBytesLog2(this OperandType type) { - switch (type) + return type switch { - case OperandType.FP32: return 2; - case OperandType.FP64: return 3; - case OperandType.I32: return 2; - case OperandType.I64: return 3; - case OperandType.V128: return 4; - } - - throw new InvalidOperationException($"Invalid operand type \"{type}\"."); + OperandType.FP32 => 2, + OperandType.FP64 => 3, + OperandType.I32 => 2, + OperandType.I64 => 3, + OperandType.V128 => 4, + _ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."), + }; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/Operation.cs b/src/ARMeilleure/IntermediateRepresentation/Operation.cs index c71e143c3..bc3a71b31 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Operation.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Operation.cs @@ -20,60 +20,60 @@ namespace ARMeilleure.IntermediateRepresentation private Data* _data; - public Instruction Instruction + public readonly Instruction Instruction { get => (Instruction)_data->Instruction; private set => _data->Instruction = (ushort)value; } - public Intrinsic Intrinsic + public readonly Intrinsic Intrinsic { get => (Intrinsic)_data->Intrinsic; private set => _data->Intrinsic = (ushort)value; } - public Operation ListPrevious + public readonly Operation ListPrevious { get => _data->ListPrevious; set => _data->ListPrevious = value; } - public Operation ListNext + public readonly Operation ListNext { get => _data->ListNext; set => _data->ListNext = value; } - public Operand Destination + public readonly Operand Destination { get => _data->DestinationsCount != 0 ? GetDestination(0) : default; set => SetDestination(value); } - public int DestinationsCount => _data->DestinationsCount; - public int SourcesCount => _data->SourcesCount; + public readonly int DestinationsCount => _data->DestinationsCount; + public readonly int SourcesCount => _data->SourcesCount; - internal Span<Operand> DestinationsUnsafe => new(_data->Destinations, _data->DestinationsCount); - internal Span<Operand> SourcesUnsafe => new(_data->Sources, _data->SourcesCount); + internal readonly Span<Operand> DestinationsUnsafe => new(_data->Destinations, _data->DestinationsCount); + internal readonly Span<Operand> SourcesUnsafe => new(_data->Sources, _data->SourcesCount); - public PhiOperation AsPhi() + public readonly PhiOperation AsPhi() { Debug.Assert(Instruction == Instruction.Phi); return new PhiOperation(this); } - public Operand GetDestination(int index) + public readonly Operand GetDestination(int index) { return DestinationsUnsafe[index]; } - public Operand GetSource(int index) + public readonly Operand GetSource(int index) { return SourcesUnsafe[index]; } - public void SetDestination(int index, Operand dest) + public readonly void SetDestination(int index, Operand dest) { ref Operand curDest = ref DestinationsUnsafe[index]; @@ -83,7 +83,7 @@ namespace ARMeilleure.IntermediateRepresentation curDest = dest; } - public void SetSource(int index, Operand src) + public readonly void SetSource(int index, Operand src) { ref Operand curSrc = ref SourcesUnsafe[index]; @@ -93,7 +93,7 @@ namespace ARMeilleure.IntermediateRepresentation curSrc = src; } - private void RemoveOldDestinations() + private readonly void RemoveOldDestinations() { for (int i = 0; i < _data->DestinationsCount; i++) { @@ -101,7 +101,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public void SetDestination(Operand dest) + public readonly void SetDestination(Operand dest) { RemoveOldDestinations(); @@ -119,7 +119,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public void SetDestinations(Operand[] dests) + public readonly void SetDestinations(Operand[] dests) { RemoveOldDestinations(); @@ -135,7 +135,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - private void RemoveOldSources() + private readonly void RemoveOldSources() { for (int index = 0; index < _data->SourcesCount; index++) { @@ -143,7 +143,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public void SetSource(Operand src) + public readonly void SetSource(Operand src) { RemoveOldSources(); @@ -161,7 +161,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - public void SetSources(Operand[] srcs) + public readonly void SetSources(Operand[] srcs) { RemoveOldSources(); @@ -184,7 +184,7 @@ namespace ARMeilleure.IntermediateRepresentation SetSource(source); } - private void AddAssignment(Operand op) + private readonly void AddAssignment(Operand op) { if (op != default) { @@ -192,7 +192,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - private void RemoveAssignment(Operand op) + private readonly void RemoveAssignment(Operand op) { if (op != default) { @@ -200,7 +200,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - private void AddUse(Operand op) + private readonly void AddUse(Operand op) { if (op != default) { @@ -208,7 +208,7 @@ namespace ARMeilleure.IntermediateRepresentation } } - private void RemoveUse(Operand op) + private readonly void RemoveUse(Operand op) { if (op != default) { @@ -216,17 +216,17 @@ namespace ARMeilleure.IntermediateRepresentation } } - public bool Equals(Operation operation) + public readonly bool Equals(Operation operation) { return operation._data == _data; } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is Operation operation && Equals(operation); } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine((IntPtr)_data); } @@ -267,9 +267,11 @@ namespace ARMeilleure.IntermediateRepresentation Data* data = Allocators.Operations.Allocate<Data>(); *data = default; - Operation result = new(); - result._data = data; - result.Instruction = inst; + Operation result = new() + { + _data = data, + Instruction = inst, + }; EnsureCapacity(ref result._data->Destinations, ref result._data->DestinationsCount, destCount); EnsureCapacity(ref result._data->Sources, ref result._data->SourcesCount, srcCount); @@ -373,4 +375,4 @@ namespace ARMeilleure.IntermediateRepresentation } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/Register.cs b/src/ARMeilleure/IntermediateRepresentation/Register.cs index 241e4d13d..208f94be1 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Register.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Register.cs @@ -11,7 +11,7 @@ namespace ARMeilleure.IntermediateRepresentation public Register(int index, RegisterType type) { Index = index; - Type = type; + Type = type; } public override int GetHashCode() @@ -37,7 +37,7 @@ namespace ARMeilleure.IntermediateRepresentation public bool Equals(Register other) { return other.Index == Index && - other.Type == Type; + other.Type == Type; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs b/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs index 88ac6c124..2b4c9068c 100644 --- a/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs +++ b/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs @@ -5,6 +5,6 @@ namespace ARMeilleure.IntermediateRepresentation Integer, Vector, Flag, - FpFlag + FpFlag, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Memory/IJitMemoryBlock.cs b/src/ARMeilleure/Memory/IJitMemoryBlock.cs index 9b11e07ff..e94b0a60d 100644 --- a/src/ARMeilleure/Memory/IJitMemoryBlock.cs +++ b/src/ARMeilleure/Memory/IJitMemoryBlock.cs @@ -11,4 +11,4 @@ namespace ARMeilleure.Memory void MapAsRx(ulong offset, ulong size); void MapAsRwx(ulong offset, ulong size); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Memory/IMemoryManager.cs b/src/ARMeilleure/Memory/IMemoryManager.cs index 5eb1fadd6..ec5b81ebe 100644 --- a/src/ARMeilleure/Memory/IMemoryManager.cs +++ b/src/ARMeilleure/Memory/IMemoryManager.cs @@ -74,4 +74,4 @@ namespace ARMeilleure.Memory /// <param name="exemptId">Optional ID of the handles that should not be signalled</param> void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Memory/MemoryManagerType.cs b/src/ARMeilleure/Memory/MemoryManagerType.cs index ce84ccaf3..e897a038f 100644 --- a/src/ARMeilleure/Memory/MemoryManagerType.cs +++ b/src/ARMeilleure/Memory/MemoryManagerType.cs @@ -28,7 +28,7 @@ /// Same as the host mapped memory manager type, but without masking the address within the address space. /// Allows invalid access from JIT code to the rest of the program, but is faster. /// </summary> - HostMappedUnsafe + HostMappedUnsafe, } static class MemoryManagerTypeExtensions diff --git a/src/ARMeilleure/Native/JitSupportDarwin.cs b/src/ARMeilleure/Native/JitSupportDarwin.cs index 7d6a8634a..ed347b9cf 100644 --- a/src/ARMeilleure/Native/JitSupportDarwin.cs +++ b/src/ARMeilleure/Native/JitSupportDarwin.cs @@ -5,7 +5,7 @@ using System.Runtime.Versioning; namespace ARMeilleure.Native { [SupportedOSPlatform("macos")] - public static partial class JitSupportDarwin + internal static partial class JitSupportDarwin { [LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")] public static partial void Copy(IntPtr dst, IntPtr src, ulong n); diff --git a/src/ARMeilleure/Optimizations.cs b/src/ARMeilleure/Optimizations.cs index 13348cec2..8fe478e47 100644 --- a/src/ARMeilleure/Optimizations.cs +++ b/src/ARMeilleure/Optimizations.cs @@ -1,5 +1,3 @@ -using System.Runtime.Intrinsics.Arm; - namespace ARMeilleure { using Arm64HardwareCapabilities = ARMeilleure.CodeGen.Arm64.HardwareCapabilities; @@ -9,31 +7,31 @@ namespace ARMeilleure { public static bool FastFP { get; set; } = true; - public static bool AllowLcqInFunctionTable { get; set; } = true; + public static bool AllowLcqInFunctionTable { get; set; } = true; public static bool UseUnmanagedDispatchLoop { get; set; } = true; - public static bool UseAdvSimdIfAvailable { get; set; } = true; - public static bool UseArm64AesIfAvailable { get; set; } = true; + public static bool UseAdvSimdIfAvailable { get; set; } = true; + public static bool UseArm64AesIfAvailable { get; set; } = true; public static bool UseArm64PmullIfAvailable { get; set; } = true; - public static bool UseSseIfAvailable { get; set; } = true; - public static bool UseSse2IfAvailable { get; set; } = true; - public static bool UseSse3IfAvailable { get; set; } = true; - public static bool UseSsse3IfAvailable { get; set; } = true; - public static bool UseSse41IfAvailable { get; set; } = true; - public static bool UseSse42IfAvailable { get; set; } = true; - public static bool UsePopCntIfAvailable { get; set; } = true; - public static bool UseAvxIfAvailable { get; set; } = true; - public static bool UseAvx512FIfAvailable { get; set; } = true; - public static bool UseAvx512VlIfAvailable { get; set; } = true; - public static bool UseAvx512BwIfAvailable { get; set; } = true; - public static bool UseAvx512DqIfAvailable { get; set; } = true; - public static bool UseF16cIfAvailable { get; set; } = true; - public static bool UseFmaIfAvailable { get; set; } = true; - public static bool UseAesniIfAvailable { get; set; } = true; + public static bool UseSseIfAvailable { get; set; } = true; + public static bool UseSse2IfAvailable { get; set; } = true; + public static bool UseSse3IfAvailable { get; set; } = true; + public static bool UseSsse3IfAvailable { get; set; } = true; + public static bool UseSse41IfAvailable { get; set; } = true; + public static bool UseSse42IfAvailable { get; set; } = true; + public static bool UsePopCntIfAvailable { get; set; } = true; + public static bool UseAvxIfAvailable { get; set; } = true; + public static bool UseAvx512FIfAvailable { get; set; } = true; + public static bool UseAvx512VlIfAvailable { get; set; } = true; + public static bool UseAvx512BwIfAvailable { get; set; } = true; + public static bool UseAvx512DqIfAvailable { get; set; } = true; + public static bool UseF16cIfAvailable { get; set; } = true; + public static bool UseFmaIfAvailable { get; set; } = true; + public static bool UseAesniIfAvailable { get; set; } = true; public static bool UsePclmulqdqIfAvailable { get; set; } = true; - public static bool UseShaIfAvailable { get; set; } = true; - public static bool UseGfniIfAvailable { get; set; } = true; + public static bool UseShaIfAvailable { get; set; } = true; + public static bool UseGfniIfAvailable { get; set; } = true; public static bool ForceLegacySse { @@ -41,6 +39,7 @@ namespace ARMeilleure set => X86HardwareCapabilities.ForceLegacySse = value; } +#pragma warning disable IDE0055 // Disable formatting internal static bool UseAdvSimd => UseAdvSimdIfAvailable && Arm64HardwareCapabilities.SupportsAdvSimd; internal static bool UseArm64Aes => UseArm64AesIfAvailable && Arm64HardwareCapabilities.SupportsAes; internal static bool UseArm64Pmull => UseArm64PmullIfAvailable && Arm64HardwareCapabilities.SupportsPmull; @@ -63,6 +62,7 @@ namespace ARMeilleure internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq; internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha; internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni; +#pragma warning restore IDE0055 internal static bool UseAvx512Ortho => UseAvx512F && UseAvx512Vl; internal static bool UseAvx512OrthoFloat => UseAvx512Ortho && UseAvx512Dq; diff --git a/src/ARMeilleure/Signal/NativeSignalHandler.cs b/src/ARMeilleure/Signal/NativeSignalHandler.cs index 5da0c7726..ed284677d 100644 --- a/src/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/src/ARMeilleure/Signal/NativeSignalHandler.cs @@ -74,7 +74,7 @@ namespace ARMeilleure.Signal private static ulong _pageSize; private static ulong _pageMask; - private static IntPtr _handlerConfig; + private static readonly IntPtr _handlerConfig; private static IntPtr _signalHandlerPtr; private static IntPtr _signalHandlerHandle; @@ -96,11 +96,17 @@ namespace ARMeilleure.Signal public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null) { - if (_initialized) return; + if (_initialized) + { + return; + } lock (_lock) { - if (_initialized) return; + if (_initialized) + { + return; + } _pageSize = pageSize; _pageMask = pageSize - 1; @@ -284,7 +290,7 @@ namespace ARMeilleure.Signal const ulong auxOffset = 464; // uc_mcontext.__reserved const uint esrMagic = 0x45535201; - context.Copy(auxPtr, context.Add(ucontextPtr, Const(auxOffset))); + context.Copy(auxPtr, context.Add(ucontextPtr, Const(auxOffset))); context.MarkLabel(loopLabel); @@ -319,7 +325,7 @@ namespace ARMeilleure.Signal private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr) { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); // (int sig, SigInfo* sigInfo, void* ucontext) Operand sigInfoPtr = context.LoadArgument(OperandType.I64, 1); @@ -367,7 +373,7 @@ namespace ARMeilleure.Signal private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr) { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); // (ExceptionPointers* exceptionInfo) Operand exceptionInfoPtr = context.LoadArgument(OperandType.I64, 0); diff --git a/src/ARMeilleure/Signal/TestMethods.cs b/src/ARMeilleure/Signal/TestMethods.cs index e2ecad242..ec228c850 100644 --- a/src/ARMeilleure/Signal/TestMethods.cs +++ b/src/ARMeilleure/Signal/TestMethods.cs @@ -20,7 +20,7 @@ namespace ARMeilleure.Signal public static DebugPartialUnmap GenerateDebugPartialUnmap() { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); var result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context); @@ -37,7 +37,7 @@ namespace ARMeilleure.Signal public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(IntPtr structPtr) { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); var result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1)); @@ -54,7 +54,7 @@ namespace ARMeilleure.Signal public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop() { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); // Loop a write to the target address until "running" is false. diff --git a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs b/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs index 22009240b..79a8f803d 100644 --- a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs +++ b/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs @@ -47,10 +47,10 @@ namespace ARMeilleure.Signal public static SigAction RegisterExceptionHandler(IntPtr action) { - SigAction sig = new SigAction + SigAction sig = new() { sa_handler = action, - sa_flags = SA_SIGINFO + sa_flags = SA_SIGINFO, }; sigemptyset(ref sig.sa_mask); diff --git a/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs b/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs index 941e36e58..4da1b32dc 100644 --- a/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs +++ b/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs @@ -137,6 +137,7 @@ namespace ARMeilleure.Signal return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset)); } +#pragma warning disable IDE0051 // Remove unused private member private static void EmitThreadLocalMapIntRelease(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand index) { Operand offset = context.Multiply(index, Const(sizeof(int))); @@ -145,6 +146,7 @@ namespace ARMeilleure.Signal context.CompareAndSwap(idPtr, threadId, Const(0)); } +#pragma warning restore IDE0051 private static void EmitAtomicAddI32(EmitterContext context, Operand ptr, Operand additive) { diff --git a/src/ARMeilleure/State/Aarch32Mode.cs b/src/ARMeilleure/State/Aarch32Mode.cs index 395e288aa..add1cd26f 100644 --- a/src/ARMeilleure/State/Aarch32Mode.cs +++ b/src/ARMeilleure/State/Aarch32Mode.cs @@ -2,14 +2,14 @@ namespace ARMeilleure.State { enum Aarch32Mode { - User = 0b10000, - Fiq = 0b10001, - Irq = 0b10010, + User = 0b10000, + Fiq = 0b10001, + Irq = 0b10010, Supervisor = 0b10011, - Monitor = 0b10110, - Abort = 0b10111, + Monitor = 0b10110, + Abort = 0b10111, Hypervisor = 0b11010, - Undefined = 0b11011, - System = 0b11111 + Undefined = 0b11011, + System = 0b11111, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/ExceptionCallback.cs b/src/ARMeilleure/State/ExceptionCallback.cs index 38d6eef78..2a4e9656a 100644 --- a/src/ARMeilleure/State/ExceptionCallback.cs +++ b/src/ARMeilleure/State/ExceptionCallback.cs @@ -2,4 +2,4 @@ namespace ARMeilleure.State { public delegate void ExceptionCallbackNoArgs(ExecutionContext context); public delegate void ExceptionCallback(ExecutionContext context, ulong address, int id); -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs index 859fb3a5d..ce10a591c 100644 --- a/src/ARMeilleure/State/ExecutionContext.cs +++ b/src/ARMeilleure/State/ExecutionContext.cs @@ -7,7 +7,7 @@ namespace ARMeilleure.State { private const int MinCountForCheck = 4000; - private NativeContext _nativeContext; + private readonly NativeContext _nativeContext; internal IntPtr NativeContextPtr => _nativeContext.BasePtr; @@ -17,8 +17,10 @@ namespace ARMeilleure.State public ulong Pc => _nativeContext.GetPc(); +#pragma warning disable CA1822 // Mark member as static public uint CtrEl0 => 0x8444c004; public uint DczidEl0 => 0x00000004; +#pragma warning restore CA1822 public ulong CntfrqEl0 => _counter.Frequency; public ulong CntpctEl0 => _counter.Counter; @@ -170,4 +172,4 @@ namespace ARMeilleure.State _nativeContext.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/ExecutionMode.cs b/src/ARMeilleure/State/ExecutionMode.cs index f43c5569f..e1fb722bd 100644 --- a/src/ARMeilleure/State/ExecutionMode.cs +++ b/src/ARMeilleure/State/ExecutionMode.cs @@ -4,6 +4,6 @@ namespace ARMeilleure.State { Aarch32Arm = 0, Aarch32Thumb = 1, - Aarch64 = 2 + Aarch64 = 2, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/FPCR.cs b/src/ARMeilleure/State/FPCR.cs index 6f707de7d..427300add 100644 --- a/src/ARMeilleure/State/FPCR.cs +++ b/src/ARMeilleure/State/FPCR.cs @@ -13,10 +13,10 @@ namespace ARMeilleure.State Ide = 1u << 15, RMode0 = 1u << 22, RMode1 = 1u << 23, - Fz = 1u << 24, - Dn = 1u << 25, + Fz = 1u << 24, + Dn = 1u << 25, Ahp = 1u << 26, - Mask = Ahp | Dn | Fz | RMode1 | RMode0 | Ide | Ixe | Ufe | Ofe | Dze | Ioe // 0x07C09F00u + Mask = Ahp | Dn | Fz | RMode1 | RMode0 | Ide | Ixe | Ufe | Ofe | Dze | Ioe, // 0x07C09F00u } } diff --git a/src/ARMeilleure/State/FPException.cs b/src/ARMeilleure/State/FPException.cs index e24e07af1..5b13659ab 100644 --- a/src/ARMeilleure/State/FPException.cs +++ b/src/ARMeilleure/State/FPException.cs @@ -2,11 +2,11 @@ namespace ARMeilleure.State { enum FPException { - InvalidOp = 0, + InvalidOp = 0, DivideByZero = 1, - Overflow = 2, - Underflow = 3, - Inexact = 4, - InputDenorm = 7 + Overflow = 2, + Underflow = 3, + Inexact = 4, + InputDenorm = 7, } } diff --git a/src/ARMeilleure/State/FPRoundingMode.cs b/src/ARMeilleure/State/FPRoundingMode.cs index 8d757a151..0913175e7 100644 --- a/src/ARMeilleure/State/FPRoundingMode.cs +++ b/src/ARMeilleure/State/FPRoundingMode.cs @@ -2,10 +2,10 @@ namespace ARMeilleure.State { public enum FPRoundingMode { - ToNearest = 0, // With ties to even. - TowardsPlusInfinity = 1, + ToNearest = 0, // With ties to even. + TowardsPlusInfinity = 1, TowardsMinusInfinity = 2, - TowardsZero = 3, - ToNearestAway = 4 // With ties to away. + TowardsZero = 3, + ToNearestAway = 4, // With ties to away. } } diff --git a/src/ARMeilleure/State/FPSCR.cs b/src/ARMeilleure/State/FPSCR.cs index d6d2fc26a..65a060ebd 100644 --- a/src/ARMeilleure/State/FPSCR.cs +++ b/src/ARMeilleure/State/FPSCR.cs @@ -10,6 +10,6 @@ namespace ARMeilleure.State Z = 1u << 30, N = 1u << 31, - Mask = N | Z | C | V | FPSR.Mask | FPCR.Mask // 0xFFC09F9Fu + Mask = N | Z | C | V | FPSR.Mask | FPCR.Mask, // 0xFFC09F9Fu } } diff --git a/src/ARMeilleure/State/FPSR.cs b/src/ARMeilleure/State/FPSR.cs index 5e66d5ce1..915b2fb31 100644 --- a/src/ARMeilleure/State/FPSR.cs +++ b/src/ARMeilleure/State/FPSR.cs @@ -13,6 +13,6 @@ namespace ARMeilleure.State Idc = 1u << 7, Qc = 1u << 27, - Mask = Qc | Idc | Ixc | Ufc | Ofc | Dzc | Ioc // 0x0800009Fu + Mask = Qc | Idc | Ixc | Ufc | Ofc | Dzc | Ioc, // 0x0800009Fu } } diff --git a/src/ARMeilleure/State/FPState.cs b/src/ARMeilleure/State/FPState.cs index fa6ab9d46..027272eee 100644 --- a/src/ARMeilleure/State/FPState.cs +++ b/src/ARMeilleure/State/FPState.cs @@ -26,6 +26,6 @@ RMode1Flag = 23, FzFlag = 24, DnFlag = 25, - AhpFlag = 26 + AhpFlag = 26, } } diff --git a/src/ARMeilleure/State/FPType.cs b/src/ARMeilleure/State/FPType.cs index 84e0db8da..367082ffc 100644 --- a/src/ARMeilleure/State/FPType.cs +++ b/src/ARMeilleure/State/FPType.cs @@ -6,6 +6,6 @@ namespace ARMeilleure.State Zero, Infinity, QNaN, - SNaN + SNaN, } } diff --git a/src/ARMeilleure/State/ICounter.cs b/src/ARMeilleure/State/ICounter.cs index 93e721ea3..7aa1cce73 100644 --- a/src/ARMeilleure/State/ICounter.cs +++ b/src/ARMeilleure/State/ICounter.cs @@ -15,4 +15,4 @@ namespace ARMeilleure.State /// </summary> ulong Counter { get; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/NativeContext.cs b/src/ARMeilleure/State/NativeContext.cs index 3189bdd8a..5403042ea 100644 --- a/src/ARMeilleure/State/NativeContext.cs +++ b/src/ARMeilleure/State/NativeContext.cs @@ -23,7 +23,7 @@ namespace ARMeilleure.State public int Running; } - private static NativeCtxStorage _dummyStorage = new NativeCtxStorage(); + private static NativeCtxStorage _dummyStorage = new(); private readonly IJitMemoryBlock _block; @@ -266,4 +266,4 @@ namespace ARMeilleure.State public void Dispose() => _block.Dispose(); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/PState.cs b/src/ARMeilleure/State/PState.cs index 9a80bc570..d4ddc8656 100644 --- a/src/ARMeilleure/State/PState.cs +++ b/src/ARMeilleure/State/PState.cs @@ -12,6 +12,6 @@ namespace ARMeilleure.State VFlag = 28, CFlag = 29, ZFlag = 30, - NFlag = 31 + NFlag = 31, } } diff --git a/src/ARMeilleure/State/RegisterAlias.cs b/src/ARMeilleure/State/RegisterAlias.cs index 7ebfa2753..a95740891 100644 --- a/src/ARMeilleure/State/RegisterAlias.cs +++ b/src/ARMeilleure/State/RegisterAlias.cs @@ -2,13 +2,13 @@ namespace ARMeilleure.State { static class RegisterAlias { - public const int R8Usr = 8; - public const int R9Usr = 9; + public const int R8Usr = 8; + public const int R9Usr = 9; public const int R10Usr = 10; public const int R11Usr = 11; public const int R12Usr = 12; - public const int SpUsr = 13; - public const int LrUsr = 14; + public const int SpUsr = 13; + public const int LrUsr = 14; public const int SpHyp = 15; @@ -24,13 +24,13 @@ namespace ARMeilleure.State public const int LrUnd = 22; public const int SpUnd = 23; - public const int R8Fiq = 24; - public const int R9Fiq = 25; + public const int R8Fiq = 24; + public const int R9Fiq = 25; public const int R10Fiq = 26; public const int R11Fiq = 27; public const int R12Fiq = 28; - public const int SpFiq = 29; - public const int LrFiq = 30; + public const int SpFiq = 29; + public const int LrFiq = 30; public const int Aarch32Sp = 13; public const int Aarch32Lr = 14; @@ -39,4 +39,4 @@ namespace ARMeilleure.State public const int Lr = 30; public const int Zr = 31; } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/RegisterConsts.cs b/src/ARMeilleure/State/RegisterConsts.cs index d62940808..b43f8d646 100644 --- a/src/ARMeilleure/State/RegisterConsts.cs +++ b/src/ARMeilleure/State/RegisterConsts.cs @@ -2,14 +2,14 @@ namespace ARMeilleure.State { static class RegisterConsts { - public const int IntRegsCount = 32; - public const int VecRegsCount = 32; - public const int FlagsCount = 32; - public const int FpFlagsCount = 32; + public const int IntRegsCount = 32; + public const int VecRegsCount = 32; + public const int FlagsCount = 32; + public const int FpFlagsCount = 32; public const int IntAndVecRegsCount = IntRegsCount + VecRegsCount; - public const int FpFlagsOffset = IntRegsCount + VecRegsCount + FlagsCount; - public const int TotalCount = IntRegsCount + VecRegsCount + FlagsCount + FpFlagsCount; + public const int FpFlagsOffset = IntRegsCount + VecRegsCount + FlagsCount; + public const int TotalCount = IntRegsCount + VecRegsCount + FlagsCount + FpFlagsCount; public const int ZeroIndex = 31; } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/State/V128.cs b/src/ARMeilleure/State/V128.cs index 441bbfa6d..cbcaddfc2 100644 --- a/src/ARMeilleure/State/V128.cs +++ b/src/ARMeilleure/State/V128.cs @@ -19,7 +19,7 @@ namespace ARMeilleure.State /// <summary> /// Gets a new <see cref="V128"/> with all bits set to zero. /// </summary> - public static V128 Zero => new V128(0, 0); + public static V128 Zero => new(0, 0); /// <summary> /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="double"/> value @@ -55,9 +55,9 @@ namespace ARMeilleure.State /// <param name="e3">Element 3</param> public V128(float e0, float e1, float e2, float e3) { - _e0 = (ulong)(uint)BitConverter.SingleToInt32Bits(e0) << 0; + _e0 = (ulong)(uint)BitConverter.SingleToInt32Bits(e0) << 0; _e0 |= (ulong)(uint)BitConverter.SingleToInt32Bits(e1) << 32; - _e1 = (ulong)(uint)BitConverter.SingleToInt32Bits(e2) << 0; + _e1 = (ulong)(uint)BitConverter.SingleToInt32Bits(e2) << 0; _e1 |= (ulong)(uint)BitConverter.SingleToInt32Bits(e3) << 32; } @@ -98,9 +98,9 @@ namespace ARMeilleure.State /// <param name="e3">Element 3</param> public V128(uint e0, uint e1, uint e2, uint e3) { - _e0 = (ulong)e0 << 0; + _e0 = (ulong)e0 << 0; _e0 |= (ulong)e1 << 32; - _e1 = (ulong)e2 << 0; + _e1 = (ulong)e2 << 0; _e1 |= (ulong)e3 << 32; } @@ -137,7 +137,9 @@ namespace ARMeilleure.State public T Extract<T>(int index) where T : unmanaged { if ((uint)index >= GetElementCount<T>()) + { ThrowIndexOutOfRange(); + } // Performs: // return *((*T)this + index); @@ -156,7 +158,9 @@ namespace ARMeilleure.State public void Insert<T>(int index, T value) where T : unmanaged { if ((uint)index >= GetElementCount<T>()) + { ThrowIndexOutOfRange(); + } // Performs: // *((*T)this + index) = value; @@ -167,13 +171,13 @@ namespace ARMeilleure.State /// Returns a new <see cref="byte"/> array which represents the <see cref="V128"/>. /// </summary> /// <returns>A new <see cref="byte"/> array which represents the <see cref="V128"/></returns> - public byte[] ToArray() + public readonly byte[] ToArray() { - byte[] data = new byte[16]; + byte[] data = new byte[16]; Span<byte> span = data; BitConverter.TryWriteBytes(span, _e0); - BitConverter.TryWriteBytes(span.Slice(8), _e1); + BitConverter.TryWriteBytes(span[8..], _e1); return data; } @@ -225,7 +229,7 @@ namespace ARMeilleure.State /// </summary> /// <param name="x">Target <see cref="V128"/></param> /// <returns>Result of not operation</returns> - public static V128 operator ~(V128 x) => new V128(~x._e0, ~x._e1); + public static V128 operator ~(V128 x) => new(~x._e0, ~x._e1); /// <summary> /// Performs a bitwise and on the specified <see cref="V128"/> instances. @@ -233,7 +237,7 @@ namespace ARMeilleure.State /// <param name="x">First instance</param> /// <param name="y">Second instance</param> /// <returns>Result of and operation</returns> - public static V128 operator &(V128 x, V128 y) => new V128(x._e0 & y._e0, x._e1 & y._e1); + public static V128 operator &(V128 x, V128 y) => new(x._e0 & y._e0, x._e1 & y._e1); /// <summary> /// Performs a bitwise or on the specified <see cref="V128"/> instances. @@ -241,7 +245,7 @@ namespace ARMeilleure.State /// <param name="x">First instance</param> /// <param name="y">Second instance</param> /// <returns>Result of or operation</returns> - public static V128 operator |(V128 x, V128 y) => new V128(x._e0 | y._e0, x._e1 | y._e1); + public static V128 operator |(V128 x, V128 y) => new(x._e0 | y._e0, x._e1 | y._e1); /// <summary> /// Performs a bitwise exlusive or on the specified <see cref="V128"/> instances. @@ -249,7 +253,7 @@ namespace ARMeilleure.State /// <param name="x">First instance</param> /// <param name="y">Second instance</param> /// <returns>Result of exclusive or operation</returns> - public static V128 operator ^(V128 x, V128 y) => new V128(x._e0 ^ y._e0, x._e1 ^ y._e1); + public static V128 operator ^(V128 x, V128 y) => new(x._e0 ^ y._e0, x._e1 ^ y._e1); /// <summary> /// Determines if the specified <see cref="V128"/> instances are equal. @@ -272,7 +276,7 @@ namespace ARMeilleure.State /// </summary> /// <param name="other">Other <see cref="V128"/> instance</param> /// <returns>true if equal; otherwise false</returns> - public bool Equals(V128 other) + public readonly bool Equals(V128 other) { return other._e0 == _e0 && other._e1 == _e1; } @@ -282,24 +286,24 @@ namespace ARMeilleure.State /// </summary> /// <param name="obj">Other <see cref="object"/> instance</param> /// <returns>true if equal; otherwise false</returns> - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is V128 vector && Equals(vector); } /// <inheritdoc/> - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(_e0, _e1); } /// <inheritdoc/> - public override string ToString() + public readonly override string ToString() { return $"0x{_e1:X16}{_e0:X16}"; } - private uint GetElementCount<T>() where T : unmanaged + private static uint GetElementCount<T>() where T : unmanaged { return (uint)(Unsafe.SizeOf<V128>() / Unsafe.SizeOf<T>()); } @@ -309,4 +313,4 @@ namespace ARMeilleure.State throw new ArgumentOutOfRangeException("index"); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Statistics.cs b/src/ARMeilleure/Statistics.cs index fbc647082..2f873bcfa 100644 --- a/src/ARMeilleure/Statistics.cs +++ b/src/ARMeilleure/Statistics.cs @@ -1,4 +1,6 @@ +#if M_PROFILE using System; +#endif using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; @@ -11,12 +13,12 @@ namespace ARMeilleure { private const int ReportMaxFunctions = 100; -#pragma warning disable CS0169 +#if M_PROFILE [ThreadStatic] private static Stopwatch _executionTimer; -#pragma warning restore CS0169 +#endif - private static ConcurrentDictionary<ulong, long> _ticksPerFunction; + private static readonly ConcurrentDictionary<ulong, long> _ticksPerFunction; static Statistics() { @@ -47,7 +49,7 @@ namespace ARMeilleure long ticks = _executionTimer.ElapsedTicks; - _ticksPerFunction.AddOrUpdate(funcAddr, ticks, (key, oldTicks) => oldTicks + ticks); + TicksPerFunction.AddOrUpdate(funcAddr, ticks, (key, oldTicks) => oldTicks + ticks); #endif } @@ -69,7 +71,7 @@ namespace ARMeilleure { int count = 0; - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); sb.AppendLine(" Function address | Time"); sb.AppendLine("--------------------------"); @@ -91,4 +93,4 @@ namespace ARMeilleure return sb.ToString(); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/ArmEmitterContext.cs b/src/ARMeilleure/Translation/ArmEmitterContext.cs index 565d2aada..e24074739 100644 --- a/src/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/src/ARMeilleure/Translation/ArmEmitterContext.cs @@ -55,7 +55,7 @@ namespace ARMeilleure.Translation public Aarch32Mode Mode { get; } private int _ifThenBlockStateIndex = 0; - private Condition[] _ifThenBlockState = { }; + private Condition[] _ifThenBlockState = Array.Empty<Condition>(); public bool IsInIfThenBlock => _ifThenBlockStateIndex < _ifThenBlockState.Length; public Condition CurrentIfThenBlockCond => _ifThenBlockState[_ifThenBlockStateIndex]; @@ -96,7 +96,7 @@ namespace ARMeilleure.Translation OperandType returnType = GetOperandType(info.ReturnType); - Symbol symbol = new Symbol(SymbolType.DelegateTable, (ulong)index); + Symbol symbol = new(SymbolType.DelegateTable, (ulong)index); Symbols.Add((ulong)funcPtr.ToInt64(), info.Name); @@ -219,6 +219,7 @@ namespace ARMeilleure.Translation { switch (condition) { +#pragma warning disable IDE0055 // Disable formatting case Condition.Eq: return ICompareEqual (n, m); case Condition.Ne: return ICompareNotEqual (n, m); case Condition.GeUn: return ICompareGreaterOrEqualUI(n, m); @@ -229,6 +230,7 @@ namespace ARMeilleure.Translation case Condition.Lt: return ICompareLess (n, m); case Condition.Gt: return ICompareGreater (n, m); case Condition.Le: return ICompareLessOrEqual (n, m); +#pragma warning restore IDE0055 } } else if (cmpName == InstName.Adds && _optOpLastCompare is IOpCodeAluImm op) @@ -253,12 +255,14 @@ namespace ARMeilleure.Translation switch (condition) { +#pragma warning disable IDE0055 // Disable formatting case Condition.Eq: return ICompareEqual (n, m); case Condition.Ne: return ICompareNotEqual (n, m); case Condition.Ge: return ICompareGreaterOrEqual(n, m); case Condition.Lt: return ICompareLess (n, m); case Condition.Gt: return ICompareGreater (n, m); case Condition.Le: return ICompareLessOrEqual (n, m); +#pragma warning restore IDE0055 } } @@ -279,4 +283,4 @@ namespace ARMeilleure.Translation } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/Cache/CacheEntry.cs b/src/ARMeilleure/Translation/Cache/CacheEntry.cs index dc5503b18..25b06f781 100644 --- a/src/ARMeilleure/Translation/Cache/CacheEntry.cs +++ b/src/ARMeilleure/Translation/Cache/CacheEntry.cs @@ -7,14 +7,14 @@ namespace ARMeilleure.Translation.Cache readonly struct CacheEntry : IComparable<CacheEntry> { public int Offset { get; } - public int Size { get; } + public int Size { get; } public UnwindInfo UnwindInfo { get; } public CacheEntry(int offset, int size, UnwindInfo unwindInfo) { - Offset = offset; - Size = size; + Offset = offset; + Size = size; UnwindInfo = unwindInfo; } @@ -23,4 +23,4 @@ namespace ARMeilleure.Translation.Cache return Offset.CompareTo(other.Offset); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs b/src/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs index 4c22de40e..dd67e4201 100644 --- a/src/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs +++ b/src/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs @@ -23,7 +23,7 @@ namespace ARMeilleure.Translation.Cache } } - private readonly List<MemoryBlock> _blocks = new List<MemoryBlock>(); + private readonly List<MemoryBlock> _blocks = new(); public CacheMemoryAllocator(int capacity) { diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index 03cba5ade..91a054123 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -13,8 +13,8 @@ namespace ARMeilleure.Translation.Cache { static partial class JitCache { - private static readonly int PageSize = (int)MemoryBlock.GetPageSize(); - private static readonly int PageMask = PageSize - 1; + private static readonly int _pageSize = (int)MemoryBlock.GetPageSize(); + private static readonly int _pageMask = _pageSize - 1; private const int CodeAlignment = 4; // Bytes. private const int CacheSize = 2047 * 1024 * 1024; @@ -24,7 +24,7 @@ namespace ARMeilleure.Translation.Cache private static CacheMemoryAllocator _cacheAllocator; - private static readonly List<CacheEntry> _cacheEntries = new List<CacheEntry>(); + private static readonly List<CacheEntry> _cacheEntries = new(); private static readonly object _lock = new(); private static bool _initialized; @@ -35,11 +35,17 @@ namespace ARMeilleure.Translation.Cache public static void Initialize(IJitMemoryAllocator allocator) { - if (_initialized) return; + if (_initialized) + { + return; + } lock (_lock) { - if (_initialized) return; + if (_initialized) + { + return; + } _jitRegion = new ReservedRegion(allocator, CacheSize); @@ -52,7 +58,7 @@ namespace ARMeilleure.Translation.Cache if (OperatingSystem.IsWindows()) { - JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(PageSize)); + JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize)); } _initialized = true; @@ -75,7 +81,7 @@ namespace ARMeilleure.Translation.Cache { unsafe { - fixed (byte *codePtr = code) + fixed (byte* codePtr = code) { JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length); } @@ -124,8 +130,8 @@ namespace ARMeilleure.Translation.Cache { int endOffs = offset + size; - int regionStart = offset & ~PageMask; - int regionEnd = (endOffs + PageMask) & ~PageMask; + int regionStart = offset & ~_pageMask; + int regionEnd = (endOffs + _pageMask) & ~_pageMask; _jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); } @@ -134,8 +140,8 @@ namespace ARMeilleure.Translation.Cache { int endOffs = offset + size; - int regionStart = offset & ~PageMask; - int regionEnd = (endOffs + PageMask) & ~PageMask; + int regionStart = offset & ~_pageMask; + int regionEnd = (endOffs + _pageMask) & ~_pageMask; _jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); } @@ -163,7 +169,7 @@ namespace ARMeilleure.Translation.Cache private static void Add(int offset, int size, UnwindInfo unwindInfo) { - CacheEntry entry = new CacheEntry(offset, size, unwindInfo); + CacheEntry entry = new(offset, size, unwindInfo); int index = _cacheEntries.BinarySearch(entry); @@ -212,4 +218,4 @@ namespace ARMeilleure.Translation.Cache return false; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs b/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs index 57f7bf121..3aa2e19f1 100644 --- a/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs +++ b/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs @@ -6,7 +6,7 @@ namespace ARMeilleure.Translation.Cache { class JitCacheInvalidation { - private static int[] _invalidationCode = new int[] + private static readonly int[] _invalidationCode = new int[] { unchecked((int)0xd53b0022), // mrs x2, ctr_el0 unchecked((int)0xd3504c44), // ubfx x4, x2, #16, #4 @@ -40,8 +40,8 @@ namespace ARMeilleure.Translation.Cache private delegate void InvalidateCache(ulong start, ulong end); - private InvalidateCache _invalidateCache; - private ReservedRegion _invalidateCacheCodeRegion; + private readonly InvalidateCache _invalidateCache; + private readonly ReservedRegion _invalidateCacheCodeRegion; private readonly bool _needsInvalidation; @@ -76,4 +76,4 @@ namespace ARMeilleure.Translation.Cache } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs index 77727bf16..91fd19c25 100644 --- a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs +++ b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs @@ -29,15 +29,15 @@ namespace ARMeilleure.Translation.Cache private enum UnwindOp { - PushNonvol = 0, - AllocLarge = 1, - AllocSmall = 2, - SetFpreg = 3, - SaveNonvol = 4, + PushNonvol = 0, + AllocLarge = 1, + AllocSmall = 2, + SetFpreg = 3, + SaveNonvol = 4, SaveNonvolFar = 5, - SaveXmm128 = 8, + SaveXmm128 = 8, SaveXmm128Far = 9, - PushMachframe = 10 + PushMachframe = 10, } private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context); @@ -111,72 +111,73 @@ namespace ARMeilleure.Translation.Cache switch (entry.PseudoOp) { case UnwindPseudoOp.SaveXmm128: - { - int stackOffset = entry.StackOffsetOrAllocSize; - - Debug.Assert(stackOffset % 16 == 0); - - if (stackOffset <= 0xFFFF0) { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex); - _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16); - } - else - { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex); - _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0); - _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16); - } + int stackOffset = entry.StackOffsetOrAllocSize; - break; - } + Debug.Assert(stackOffset % 16 == 0); + + if (stackOffset <= 0xFFFF0) + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16); + } + else + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16); + } + + break; + } case UnwindPseudoOp.AllocStack: - { - int allocSize = entry.StackOffsetOrAllocSize; - - Debug.Assert(allocSize % 8 == 0); - - if (allocSize <= 128) { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1); - } - else if (allocSize <= 0x7FFF8) - { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0); - _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8); - } - else - { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1); - _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0); - _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16); - } + int allocSize = entry.StackOffsetOrAllocSize; - break; - } + Debug.Assert(allocSize % 8 == 0); + + if (allocSize <= 128) + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1); + } + else if (allocSize <= 0x7FFF8) + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8); + } + else + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16); + } + + break; + } case UnwindPseudoOp.PushReg: - { - _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex); + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex); - break; - } + break; + } - default: throw new NotImplementedException($"({nameof(entry.PseudoOp)} = {entry.PseudoOp})"); + default: + throw new NotImplementedException($"({nameof(entry.PseudoOp)} = {entry.PseudoOp})"); } } Debug.Assert(codeIndex <= MaxUnwindCodesArraySize); - _unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler. - _unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize; + _unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler. + _unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize; _unwindInfo->CountOfUnwindCodes = (byte)codeIndex; - _unwindInfo->FrameRegister = 0; + _unwindInfo->FrameRegister = 0; _runtimeFunction->BeginAddress = (uint)funcEntry.Offset; - _runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size); - _runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction; + _runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size); + _runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction; return _runtimeFunction; } @@ -186,4 +187,4 @@ namespace ARMeilleure.Translation.Cache return (ushort)(prologOffset | ((int)op << 8) | (opInfo << 12)); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/Compiler.cs b/src/ARMeilleure/Translation/Compiler.cs index d4aa5cd96..293e63496 100644 --- a/src/ARMeilleure/Translation/Compiler.cs +++ b/src/ARMeilleure/Translation/Compiler.cs @@ -11,10 +11,10 @@ namespace ARMeilleure.Translation { public static CompiledFunction Compile( ControlFlowGraph cfg, - OperandType[] argTypes, - OperandType retType, - CompilerOptions options, - Architecture target) + OperandType[] argTypes, + OperandType retType, + CompilerOptions options, + Architecture target) { CompilerContext cctx = new(cfg, argTypes, retType, options); @@ -65,4 +65,4 @@ namespace ARMeilleure.Translation } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/CompilerContext.cs b/src/ARMeilleure/Translation/CompilerContext.cs index 510dec58f..5b10686b3 100644 --- a/src/ARMeilleure/Translation/CompilerContext.cs +++ b/src/ARMeilleure/Translation/CompilerContext.cs @@ -6,21 +6,21 @@ namespace ARMeilleure.Translation { public ControlFlowGraph Cfg { get; } - public OperandType[] FuncArgTypes { get; } - public OperandType FuncReturnType { get; } + public OperandType[] FuncArgTypes { get; } + public OperandType FuncReturnType { get; } public CompilerOptions Options { get; } public CompilerContext( ControlFlowGraph cfg, - OperandType[] funcArgTypes, - OperandType funcReturnType, - CompilerOptions options) + OperandType[] funcArgTypes, + OperandType funcReturnType, + CompilerOptions options) { - Cfg = cfg; - FuncArgTypes = funcArgTypes; + Cfg = cfg; + FuncArgTypes = funcArgTypes; FuncReturnType = funcReturnType; - Options = options; + Options = options; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/CompilerOptions.cs b/src/ARMeilleure/Translation/CompilerOptions.cs index 0a07ed4ab..d454de7f2 100644 --- a/src/ARMeilleure/Translation/CompilerOptions.cs +++ b/src/ARMeilleure/Translation/CompilerOptions.cs @@ -5,13 +5,13 @@ namespace ARMeilleure.Translation [Flags] enum CompilerOptions { - None = 0, - SsaForm = 1 << 0, - Optimize = 1 << 1, - Lsra = 1 << 2, + None = 0, + SsaForm = 1 << 0, + Optimize = 1 << 1, + Lsra = 1 << 2, Relocatable = 1 << 3, MediumCq = SsaForm | Optimize, - HighCq = SsaForm | Optimize | Lsra + HighCq = SsaForm | Optimize | Lsra, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/ControlFlowGraph.cs b/src/ARMeilleure/Translation/ControlFlowGraph.cs index c935f1521..3ead49c93 100644 --- a/src/ARMeilleure/Translation/ControlFlowGraph.cs +++ b/src/ARMeilleure/Translation/ControlFlowGraph.cs @@ -130,7 +130,7 @@ namespace ARMeilleure.Translation public BasicBlock SplitEdge(BasicBlock predecessor, BasicBlock successor) { - BasicBlock splitBlock = new BasicBlock(Blocks.Count); + BasicBlock splitBlock = new(Blocks.Count); for (int i = 0; i < predecessor.SuccessorsCount; i++) { @@ -152,4 +152,4 @@ namespace ARMeilleure.Translation return splitBlock; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/DelegateInfo.cs b/src/ARMeilleure/Translation/DelegateInfo.cs index 36320ac31..27479a003 100644 --- a/src/ARMeilleure/Translation/DelegateInfo.cs +++ b/src/ARMeilleure/Translation/DelegateInfo.cs @@ -5,7 +5,9 @@ namespace ARMeilleure.Translation { class DelegateInfo { +#pragma warning disable IDE0052 // Remove unread private member private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected. +#pragma warning restore IDE0052 public IntPtr FuncPtr { get; } diff --git a/src/ARMeilleure/Translation/Dominance.cs b/src/ARMeilleure/Translation/Dominance.cs index b9b961d15..e2185bd85 100644 --- a/src/ARMeilleure/Translation/Dominance.cs +++ b/src/ARMeilleure/Translation/Dominance.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation cfg.Entry.ImmediateDominator = cfg.Entry; - Debug.Assert(cfg.Entry == cfg.PostOrderBlocks[cfg.PostOrderBlocks.Length - 1]); + Debug.Assert(cfg.Entry == cfg.PostOrderBlocks[^1]); bool modified; @@ -92,4 +92,4 @@ namespace ARMeilleure.Translation } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/EmitterContext.cs b/src/ARMeilleure/Translation/EmitterContext.cs index 8fcb4deec..88bfe1335 100644 --- a/src/ARMeilleure/Translation/EmitterContext.cs +++ b/src/ARMeilleure/Translation/EmitterContext.cs @@ -108,9 +108,9 @@ namespace ARMeilleure.Translation protected static OperandType GetOperandType(Type type) { - if (type == typeof(bool) || type == typeof(byte) || - type == typeof(char) || type == typeof(short) || - type == typeof(int) || type == typeof(sbyte) || + if (type == typeof(bool) || type == typeof(byte) || + type == typeof(char) || type == typeof(short) || + type == typeof(int) || type == typeof(sbyte) || type == typeof(ushort) || type == typeof(uint)) { return OperandType.I32; @@ -635,7 +635,7 @@ namespace ARMeilleure.Translation private void NewNextBlock() { - BasicBlock block = new BasicBlock(_irBlocks.Count); + BasicBlock block = new(_irBlocks.Count); _irBlocks.AddLast(block); diff --git a/src/ARMeilleure/Translation/GuestFunction.cs b/src/ARMeilleure/Translation/GuestFunction.cs index ac131a0d1..6414d6bd0 100644 --- a/src/ARMeilleure/Translation/GuestFunction.cs +++ b/src/ARMeilleure/Translation/GuestFunction.cs @@ -3,4 +3,4 @@ using System; namespace ARMeilleure.Translation { delegate ulong GuestFunction(IntPtr nativeContextPtr); -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/IntervalTree.cs b/src/ARMeilleure/Translation/IntervalTree.cs index 9af01bea0..afd89b930 100644 --- a/src/ARMeilleure/Translation/IntervalTree.cs +++ b/src/ARMeilleure/Translation/IntervalTree.cs @@ -6,15 +6,15 @@ namespace ARMeilleure.Translation /// <summary> /// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges. /// </summary> - /// <typeparam name="K">Key</typeparam> - /// <typeparam name="V">Value</typeparam> - class IntervalTree<K, V> where K : IComparable<K> + /// <typeparam name="TK">Key</typeparam> + /// <typeparam name="TV">Value</typeparam> + class IntervalTree<TK, TV> where TK : IComparable<TK> { private const int ArrayGrowthSize = 32; private const bool Black = true; private const bool Red = false; - private IntervalTreeNode<K, V> _root = null; + private IntervalTreeNode<TK, TV> _root = null; private int _count = 0; public int Count => _count; @@ -27,9 +27,9 @@ namespace ARMeilleure.Translation /// <param name="key">Key of the node value to get</param> /// <param name="value">Value with the given <paramref name="key"/></param> /// <returns>True if the key is on the dictionary, false otherwise</returns> - public bool TryGet(K key, out V value) + public bool TryGet(TK key, out TV value) { - IntervalTreeNode<K, V> node = GetNode(key); + IntervalTreeNode<TK, TV> node = GetNode(key); if (node == null) { @@ -49,7 +49,7 @@ namespace ARMeilleure.Translation /// <param name="overlaps">Overlaps array to place results in</param> /// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param> /// <returns>Number of intervals found</returns> - public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0) + public int Get(TK start, TK end, ref TK[] overlaps, int overlapCount = 0) { GetKeys(_root, start, end, ref overlaps, ref overlapCount); @@ -65,11 +65,11 @@ namespace ARMeilleure.Translation /// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception> /// <returns>True if the value was added, false if the start key was already in the dictionary</returns> - public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback) + public bool AddOrUpdate(TK start, TK end, TV value, Func<TK, TV, TV> updateFactoryCallback) { ArgumentNullException.ThrowIfNull(value); - return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node); + return BSTInsert(start, end, value, updateFactoryCallback, out _); } /// <summary> @@ -80,11 +80,11 @@ namespace ARMeilleure.Translation /// <param name="value">Value to add</param> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception> /// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns> - public V GetOrAdd(K start, K end, V value) + public TV GetOrAdd(TK start, TK end, TV value) { ArgumentNullException.ThrowIfNull(value); - BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node); + BSTInsert(start, end, value, null, out IntervalTreeNode<TK, TV> node); return node.Value; } @@ -93,7 +93,7 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="key">Key of the node to remove</param> /// <returns>Number of deleted values</returns> - public int Remove(K key) + public int Remove(TK key) { int removed = Delete(key); @@ -106,9 +106,9 @@ namespace ARMeilleure.Translation /// Adds all the nodes in the dictionary into <paramref name="list"/>. /// </summary> /// <returns>A list of all values sorted by Key Order</returns> - public List<V> AsList() + public List<TV> AsList() { - List<V> list = new List<V>(); + List<TV> list = new(); AddToList(_root, list); @@ -124,7 +124,7 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">The node to search for values within</param> /// <param name="list">The list to add values to</param> - private void AddToList(IntervalTreeNode<K, V> node, List<V> list) + private void AddToList(IntervalTreeNode<TK, TV> node, List<TV> list) { if (node == null) { @@ -144,11 +144,11 @@ namespace ARMeilleure.Translation /// <param name="key">Key of the node to get</param> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> /// <returns>Node reference in the tree</returns> - private IntervalTreeNode<K, V> GetNode(K key) + private IntervalTreeNode<TK, TV> GetNode(TK key) { ArgumentNullException.ThrowIfNull(key); - IntervalTreeNode<K, V> node = _root; + IntervalTreeNode<TK, TV> node = _root; while (node != null) { int cmp = key.CompareTo(node.Start); @@ -175,7 +175,7 @@ namespace ARMeilleure.Translation /// <param name="end">End of the range</param> /// <param name="overlaps">Overlaps array to place results in</param> /// <param name="overlapCount">Overlaps count to update</param> - private void GetKeys(IntervalTreeNode<K, V> node, K start, K end, ref K[] overlaps, ref int overlapCount) + private void GetKeys(IntervalTreeNode<TK, TV> node, TK start, TK end, ref TK[] overlaps, ref int overlapCount) { if (node == null || start.CompareTo(node.Max) >= 0) { @@ -206,10 +206,10 @@ namespace ARMeilleure.Translation /// This should only be called if the max increases - not for rebalancing or removals. /// </summary> /// <param name="node">The node to start propagating from</param> - private void PropagateIncrease(IntervalTreeNode<K, V> node) + private static void PropagateIncrease(IntervalTreeNode<TK, TV> node) { - K max = node.Max; - IntervalTreeNode<K, V> ptr = node; + TK max = node.Max; + IntervalTreeNode<TK, TV> ptr = node; while ((ptr = ptr.Parent) != null) { @@ -229,13 +229,13 @@ namespace ARMeilleure.Translation /// This fully recalculates the max value from all children when there is potential for it to decrease. /// </summary> /// <param name="node">The node to start propagating from</param> - private void PropagateFull(IntervalTreeNode<K, V> node) + private static void PropagateFull(IntervalTreeNode<TK, TV> node) { - IntervalTreeNode<K, V> ptr = node; + IntervalTreeNode<TK, TV> ptr = node; do { - K max = ptr.End; + TK max = ptr.End; if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0) { @@ -263,10 +263,10 @@ namespace ARMeilleure.Translation /// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param> /// <param name="outNode">Node that was inserted or modified</param> /// <returns>True if <paramref name="start"/> was not yet on the tree, false otherwise</returns> - private bool BSTInsert(K start, K end, V value, Func<K, V, V> updateFactoryCallback, out IntervalTreeNode<K, V> outNode) + private bool BSTInsert(TK start, TK end, TV value, Func<TK, TV, TV> updateFactoryCallback, out IntervalTreeNode<TK, TV> outNode) { - IntervalTreeNode<K, V> parent = null; - IntervalTreeNode<K, V> node = _root; + IntervalTreeNode<TK, TV> parent = null; + IntervalTreeNode<TK, TV> node = _root; while (node != null) { @@ -311,7 +311,7 @@ namespace ARMeilleure.Translation return false; } } - IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent); + IntervalTreeNode<TK, TV> newNode = new(start, end, value, parent); if (newNode.Parent == null) { _root = newNode; @@ -337,16 +337,16 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="key">Key to search for</param> /// <returns>Number of deleted values</returns> - private int Delete(K key) + private int Delete(TK key) { - IntervalTreeNode<K, V> nodeToDelete = GetNode(key); + IntervalTreeNode<TK, TV> nodeToDelete = GetNode(key); if (nodeToDelete == null) { return 0; } - IntervalTreeNode<K, V> replacementNode; + IntervalTreeNode<TK, TV> replacementNode; if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null) { @@ -357,7 +357,7 @@ namespace ARMeilleure.Translation replacementNode = PredecessorOf(nodeToDelete); } - IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode); + IntervalTreeNode<TK, TV> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode); if (tmp != null) { @@ -400,9 +400,9 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">Root Node</param> /// <returns>Node with the maximum key in the tree of <paramref name="node"/></returns> - private static IntervalTreeNode<K, V> Maximum(IntervalTreeNode<K, V> node) + private static IntervalTreeNode<TK, TV> Maximum(IntervalTreeNode<TK, TV> node) { - IntervalTreeNode<K, V> tmp = node; + IntervalTreeNode<TK, TV> tmp = node; while (tmp.Right != null) { tmp = tmp.Right; @@ -416,13 +416,13 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">Node to find the predecessor of</param> /// <returns>Predecessor of <paramref name="node"/></returns> - private static IntervalTreeNode<K, V> PredecessorOf(IntervalTreeNode<K, V> node) + private static IntervalTreeNode<TK, TV> PredecessorOf(IntervalTreeNode<TK, TV> node) { if (node.Left != null) { return Maximum(node.Left); } - IntervalTreeNode<K, V> parent = node.Parent; + IntervalTreeNode<TK, TV> parent = node.Parent; while (parent != null && node == parent.Left) { node = parent; @@ -435,15 +435,15 @@ namespace ARMeilleure.Translation #region Private Methods (RBL) - private void RestoreBalanceAfterRemoval(IntervalTreeNode<K, V> balanceNode) + private void RestoreBalanceAfterRemoval(IntervalTreeNode<TK, TV> balanceNode) { - IntervalTreeNode<K, V> ptr = balanceNode; + IntervalTreeNode<TK, TV> ptr = balanceNode; while (ptr != _root && ColorOf(ptr) == Black) { if (ptr == LeftOf(ParentOf(ptr))) { - IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ptr)); + IntervalTreeNode<TK, TV> sibling = RightOf(ParentOf(ptr)); if (ColorOf(sibling) == Red) { @@ -475,7 +475,7 @@ namespace ARMeilleure.Translation } else { - IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ptr)); + IntervalTreeNode<TK, TV> sibling = LeftOf(ParentOf(ptr)); if (ColorOf(sibling) == Red) { @@ -509,14 +509,14 @@ namespace ARMeilleure.Translation SetColor(ptr, Black); } - private void RestoreBalanceAfterInsertion(IntervalTreeNode<K, V> balanceNode) + private void RestoreBalanceAfterInsertion(IntervalTreeNode<TK, TV> balanceNode) { SetColor(balanceNode, Red); while (balanceNode != null && balanceNode != _root && ColorOf(ParentOf(balanceNode)) == Red) { if (ParentOf(balanceNode) == LeftOf(ParentOf(ParentOf(balanceNode)))) { - IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ParentOf(balanceNode))); + IntervalTreeNode<TK, TV> sibling = RightOf(ParentOf(ParentOf(balanceNode))); if (ColorOf(sibling) == Red) { @@ -539,7 +539,7 @@ namespace ARMeilleure.Translation } else { - IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ParentOf(balanceNode))); + IntervalTreeNode<TK, TV> sibling = LeftOf(ParentOf(ParentOf(balanceNode))); if (ColorOf(sibling) == Red) { @@ -564,17 +564,17 @@ namespace ARMeilleure.Translation SetColor(_root, Black); } - private void RotateLeft(IntervalTreeNode<K, V> node) + private void RotateLeft(IntervalTreeNode<TK, TV> node) { if (node != null) { - IntervalTreeNode<K, V> right = RightOf(node); + IntervalTreeNode<TK, TV> right = RightOf(node); node.Right = LeftOf(right); if (node.Right != null) { node.Right.Parent = node; } - IntervalTreeNode<K, V> nodeParent = ParentOf(node); + IntervalTreeNode<TK, TV> nodeParent = ParentOf(node); right.Parent = nodeParent; if (nodeParent == null) { @@ -595,17 +595,17 @@ namespace ARMeilleure.Translation } } - private void RotateRight(IntervalTreeNode<K, V> node) + private void RotateRight(IntervalTreeNode<TK, TV> node) { if (node != null) { - IntervalTreeNode<K, V> left = LeftOf(node); + IntervalTreeNode<TK, TV> left = LeftOf(node); node.Left = RightOf(left); if (node.Left != null) { node.Left.Parent = node; } - IntervalTreeNode<K, V> nodeParent = ParentOf(node); + IntervalTreeNode<TK, TV> nodeParent = ParentOf(node); left.Parent = nodeParent; if (nodeParent == null) { @@ -637,7 +637,7 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">Node</param> /// <returns>The boolean color of <paramref name="node"/>, or black if null</returns> - private static bool ColorOf(IntervalTreeNode<K, V> node) + private static bool ColorOf(IntervalTreeNode<TK, TV> node) { return node == null || node.Color; } @@ -649,7 +649,7 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">Node to set the color of</param> /// <param name="color">Color (Boolean)</param> - private static void SetColor(IntervalTreeNode<K, V> node, bool color) + private static void SetColor(IntervalTreeNode<TK, TV> node, bool color) { if (node != null) { @@ -662,7 +662,7 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">Node to retrieve the left child from</param> /// <returns>Left child of <paramref name="node"/></returns> - private static IntervalTreeNode<K, V> LeftOf(IntervalTreeNode<K, V> node) + private static IntervalTreeNode<TK, TV> LeftOf(IntervalTreeNode<TK, TV> node) { return node?.Left; } @@ -672,7 +672,7 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">Node to retrieve the right child from</param> /// <returns>Right child of <paramref name="node"/></returns> - private static IntervalTreeNode<K, V> RightOf(IntervalTreeNode<K, V> node) + private static IntervalTreeNode<TK, TV> RightOf(IntervalTreeNode<TK, TV> node) { return node?.Right; } @@ -682,14 +682,14 @@ namespace ARMeilleure.Translation /// </summary> /// <param name="node">Node to retrieve the parent from</param> /// <returns>Parent of <paramref name="node"/></returns> - private static IntervalTreeNode<K, V> ParentOf(IntervalTreeNode<K, V> node) + private static IntervalTreeNode<TK, TV> ParentOf(IntervalTreeNode<TK, TV> node) { return node?.Parent; } #endregion - public bool ContainsKey(K key) + public bool ContainsKey(TK key) { return GetNode(key) != null; } @@ -704,36 +704,36 @@ namespace ARMeilleure.Translation /// <summary> /// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V. /// </summary> - /// <typeparam name="K">Key type of the node</typeparam> - /// <typeparam name="V">Value type of the node</typeparam> - class IntervalTreeNode<K, V> + /// <typeparam name="TK">Key type of the node</typeparam> + /// <typeparam name="TV">Value type of the node</typeparam> + class IntervalTreeNode<TK, TV> { public bool Color = true; - public IntervalTreeNode<K, V> Left = null; - public IntervalTreeNode<K, V> Right = null; - public IntervalTreeNode<K, V> Parent = null; + public IntervalTreeNode<TK, TV> Left = null; + public IntervalTreeNode<TK, TV> Right = null; + public IntervalTreeNode<TK, TV> Parent = null; /// <summary> /// The start of the range. /// </summary> - public K Start; + public TK Start; /// <summary> /// The end of the range. /// </summary> - public K End; + public TK End; /// <summary> /// The maximum end value of this node and all its children. /// </summary> - public K Max; + public TK Max; /// <summary> /// Value stored on this node. /// </summary> - public V Value; + public TV Value; - public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent) + public IntervalTreeNode(TK start, TK end, TV value, IntervalTreeNode<TK, TV> parent) { Start = start; End = end; diff --git a/src/ARMeilleure/Translation/PTC/EncodingCache.cs b/src/ARMeilleure/Translation/PTC/EncodingCache.cs index 90d40c475..d9b38ace7 100644 --- a/src/ARMeilleure/Translation/PTC/EncodingCache.cs +++ b/src/ARMeilleure/Translation/PTC/EncodingCache.cs @@ -6,4 +6,4 @@ namespace ARMeilleure.Translation.PTC { public static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/PTC/IPtcLoadState.cs b/src/ARMeilleure/Translation/PTC/IPtcLoadState.cs index 1b11ac0b5..efff45a9f 100644 --- a/src/ARMeilleure/Translation/PTC/IPtcLoadState.cs +++ b/src/ARMeilleure/Translation/PTC/IPtcLoadState.cs @@ -7,4 +7,4 @@ namespace ARMeilleure.Translation.PTC event Action<PtcLoadingState, int, int> PtcStateChanged; void Continue(); } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 3c697bffe..665f568d2 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -17,13 +17,12 @@ using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; - using static ARMeilleure.Translation.PTC.PtcFormatter; namespace ARMeilleure.Translation.PTC { - using Arm64HardwareCapabilities = ARMeilleure.CodeGen.Arm64.HardwareCapabilities; - using X86HardwareCapabilities = ARMeilleure.CodeGen.X86.HardwareCapabilities; + using Arm64HardwareCapabilities = CodeGen.Arm64.HardwareCapabilities; + using X86HardwareCapabilities = CodeGen.X86.HardwareCapabilities; class Ptc : IPtcLoadState { @@ -187,8 +186,8 @@ namespace ARMeilleure.Translation.PTC string fileNameActual = $"{CachePathActual}.cache"; string fileNameBackup = $"{CachePathBackup}.cache"; - FileInfo fileInfoActual = new FileInfo(fileNameActual); - FileInfo fileInfoBackup = new FileInfo(fileNameBackup); + FileInfo fileInfoActual = new(fileNameActual); + FileInfo fileInfoBackup = new(fileNameBackup); if (fileInfoActual.Exists && fileInfoActual.Length != 0L) { @@ -275,104 +274,102 @@ namespace ARMeilleure.Translation.PTC { intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize)); - using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite)) + using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite); + try { - try - { - deflateStream.CopyTo(stream); - } - catch - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - Debug.Assert(stream.Position == stream.Length); - - stream.Seek(0L, SeekOrigin.Begin); - - InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream); - - if (!innerHeader.IsHeaderValid()) - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - if (innerHeader.Magic != _innerHeaderMagic) - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - ReadOnlySpan<byte> infosBytes = new(stream.PositionPointer, innerHeader.InfosLength); - stream.Seek(innerHeader.InfosLength, SeekOrigin.Current); - - Hash128 infosHash = XXHash128.ComputeHash(infosBytes); - - if (innerHeader.InfosHash != infosHash) - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - ReadOnlySpan<byte> codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan<byte>.Empty; - stream.Seek(innerHeader.CodesLength, SeekOrigin.Current); - - Hash128 codesHash = XXHash128.ComputeHash(codesBytes); - - if (innerHeader.CodesHash != codesHash) - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - ReadOnlySpan<byte> relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength); - stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current); - - Hash128 relocsHash = XXHash128.ComputeHash(relocsBytes); - - if (innerHeader.RelocsHash != relocsHash) - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - ReadOnlySpan<byte> unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength); - stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current); - - Hash128 unwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); - - if (innerHeader.UnwindInfosHash != unwindInfosHash) - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - Debug.Assert(stream.Position == stream.Length); - - stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin); - - _infosStream.Write(infosBytes); - stream.Seek(innerHeader.InfosLength, SeekOrigin.Current); - - _codesList.ReadFrom(stream); - - _relocsStream.Write(relocsBytes); - stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current); - - _unwindInfosStream.Write(unwindInfosBytes); - stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current); - - Debug.Assert(stream.Position == stream.Length); + deflateStream.CopyTo(stream); } + catch + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + Debug.Assert(stream.Position == stream.Length); + + stream.Seek(0L, SeekOrigin.Begin); + + InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream); + + if (!innerHeader.IsHeaderValid()) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (innerHeader.Magic != _innerHeaderMagic) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + ReadOnlySpan<byte> infosBytes = new(stream.PositionPointer, innerHeader.InfosLength); + stream.Seek(innerHeader.InfosLength, SeekOrigin.Current); + + Hash128 infosHash = XXHash128.ComputeHash(infosBytes); + + if (innerHeader.InfosHash != infosHash) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + ReadOnlySpan<byte> codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan<byte>.Empty; + stream.Seek(innerHeader.CodesLength, SeekOrigin.Current); + + Hash128 codesHash = XXHash128.ComputeHash(codesBytes); + + if (innerHeader.CodesHash != codesHash) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + ReadOnlySpan<byte> relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength); + stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current); + + Hash128 relocsHash = XXHash128.ComputeHash(relocsBytes); + + if (innerHeader.RelocsHash != relocsHash) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + ReadOnlySpan<byte> unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength); + stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current); + + Hash128 unwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); + + if (innerHeader.UnwindInfosHash != unwindInfosHash) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + Debug.Assert(stream.Position == stream.Length); + + stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin); + + _infosStream.Write(infosBytes); + stream.Seek(innerHeader.InfosLength, SeekOrigin.Current); + + _codesList.ReadFrom(stream); + + _relocsStream.Write(relocsBytes); + stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current); + + _unwindInfosStream.Write(unwindInfosBytes); + stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current); + + Debug.Assert(stream.Position == stream.Length); } finally { @@ -390,7 +387,7 @@ namespace ARMeilleure.Translation.PTC return true; } - private void InvalidateCompressedStream(FileStream compressedStream) + private static void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); } @@ -404,7 +401,7 @@ namespace ARMeilleure.Translation.PTC string fileNameActual = $"{CachePathActual}.cache"; string fileNameBackup = $"{CachePathBackup}.cache"; - FileInfo fileInfoActual = new FileInfo(fileNameActual); + FileInfo fileInfoActual = new(fileNameActual); if (fileInfoActual.Exists && fileInfoActual.Length != 0L) { @@ -427,32 +424,34 @@ namespace ARMeilleure.Translation.PTC { int translatedFuncsCount; - InnerHeader innerHeader = new InnerHeader(); + InnerHeader innerHeader = new() + { + Magic = _innerHeaderMagic, - innerHeader.Magic = _innerHeaderMagic; + InfosLength = (int)_infosStream.Length, + CodesLength = _codesList.Length(), + RelocsLength = (int)_relocsStream.Length, + UnwindInfosLength = (int)_unwindInfosStream.Length, + }; - innerHeader.InfosLength = (int)_infosStream.Length; - innerHeader.CodesLength = _codesList.Length(); - innerHeader.RelocsLength = (int)_relocsStream.Length; - innerHeader.UnwindInfosLength = (int)_unwindInfosStream.Length; + OuterHeader outerHeader = new() + { + Magic = _outerHeaderMagic, - OuterHeader outerHeader = new OuterHeader(); + CacheFileVersion = InternalVersion, + Endianness = GetEndianness(), + FeatureInfo = GetFeatureInfo(), + MemoryManagerMode = GetMemoryManagerMode(), + OSPlatform = GetOSPlatform(), + Architecture = (uint)RuntimeInformation.ProcessArchitecture, - outerHeader.Magic = _outerHeaderMagic; - - outerHeader.CacheFileVersion = InternalVersion; - outerHeader.Endianness = GetEndianness(); - outerHeader.FeatureInfo = GetFeatureInfo(); - outerHeader.MemoryManagerMode = GetMemoryManagerMode(); - outerHeader.OSPlatform = GetOSPlatform(); - outerHeader.Architecture = (uint)RuntimeInformation.ProcessArchitecture; - - outerHeader.UncompressedStreamSize = + UncompressedStreamSize = (long)Unsafe.SizeOf<InnerHeader>() + innerHeader.InfosLength + innerHeader.CodesLength + innerHeader.RelocsLength + - innerHeader.UnwindInfosLength; + innerHeader.UnwindInfosLength, + }; outerHeader.SetHeaderHash(); @@ -462,58 +461,54 @@ namespace ARMeilleure.Translation.PTC { intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize)); - using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite)) + using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite); + stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin); + + ReadOnlySpan<byte> infosBytes = new(stream.PositionPointer, innerHeader.InfosLength); + _infosStream.WriteTo(stream); + + ReadOnlySpan<byte> codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan<byte>.Empty; + _codesList.WriteTo(stream); + + ReadOnlySpan<byte> relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength); + _relocsStream.WriteTo(stream); + + ReadOnlySpan<byte> unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength); + _unwindInfosStream.WriteTo(stream); + + Debug.Assert(stream.Position == stream.Length); + + innerHeader.InfosHash = XXHash128.ComputeHash(infosBytes); + innerHeader.CodesHash = XXHash128.ComputeHash(codesBytes); + innerHeader.RelocsHash = XXHash128.ComputeHash(relocsBytes); + innerHeader.UnwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); + + innerHeader.SetHeaderHash(); + + stream.Seek(0L, SeekOrigin.Begin); + SerializeStructure(stream, innerHeader); + + translatedFuncsCount = GetEntriesCount(); + + ResetCarriersIfNeeded(); + + using FileStream compressedStream = new(fileName, FileMode.OpenOrCreate); + using DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true); + try { - stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin); - - ReadOnlySpan<byte> infosBytes = new(stream.PositionPointer, innerHeader.InfosLength); - _infosStream.WriteTo(stream); - - ReadOnlySpan<byte> codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan<byte>.Empty; - _codesList.WriteTo(stream); - - ReadOnlySpan<byte> relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength); - _relocsStream.WriteTo(stream); - - ReadOnlySpan<byte> unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength); - _unwindInfosStream.WriteTo(stream); - - Debug.Assert(stream.Position == stream.Length); - - innerHeader.InfosHash = XXHash128.ComputeHash(infosBytes); - innerHeader.CodesHash = XXHash128.ComputeHash(codesBytes); - innerHeader.RelocsHash = XXHash128.ComputeHash(relocsBytes); - innerHeader.UnwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); - - innerHeader.SetHeaderHash(); + SerializeStructure(compressedStream, outerHeader); stream.Seek(0L, SeekOrigin.Begin); - SerializeStructure(stream, innerHeader); + stream.CopyTo(deflateStream); + } + catch + { + compressedStream.Position = 0L; + } - translatedFuncsCount = GetEntriesCount(); - - ResetCarriersIfNeeded(); - - using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate)) - using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true)) - { - try - { - SerializeStructure(compressedStream, outerHeader); - - stream.Seek(0L, SeekOrigin.Begin); - stream.CopyTo(deflateStream); - } - catch - { - compressedStream.Position = 0L; - } - - if (compressedStream.Position < compressedStream.Length) - { - compressedStream.SetLength(compressedStream.Position); - } - } + if (compressedStream.Position < compressedStream.Length) + { + compressedStream.SetLength(compressedStream.Position); } } finally @@ -647,7 +642,7 @@ namespace ARMeilleure.Translation.PTC return _codesList[index]; } - private RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) + private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) { RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount]; @@ -663,7 +658,7 @@ namespace ARMeilleure.Translation.PTC return relocEntries; } - private void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter) + private static void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter) { callCounter = null; @@ -678,7 +673,10 @@ namespace ARMeilleure.Translation.PTC if (translator.FunctionTable.IsValid(guestAddress)) { - unsafe { imm = (IntPtr)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress)); } + unsafe + { + imm = (IntPtr)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress)); + } } } else if (symbol.Type == SymbolType.DelegateTable) @@ -696,12 +694,12 @@ namespace ARMeilleure.Translation.PTC } else if (symbol == CountTableSymbol) { - if (callCounter == null) - { - callCounter = new Counter<uint>(translator.CountTable); - } + callCounter ??= new Counter<uint>(translator.CountTable); - unsafe { imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value); } + unsafe + { + imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value); + } } else if (symbol == DispatchStubSymbol) { @@ -717,7 +715,7 @@ namespace ARMeilleure.Translation.PTC } } - private UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) + private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) { int pushEntriesLength = unwindInfosReader.ReadInt32(); @@ -738,7 +736,7 @@ namespace ARMeilleure.Translation.PTC return new UnwindInfo(pushEntries, prologueSize); } - private TranslatedFunction FastTranslate( + private static TranslatedFunction FastTranslate( byte[] code, Counter<uint> callCounter, ulong guestSize, @@ -809,13 +807,13 @@ namespace ARMeilleure.Translation.PTC PtcStateChanged?.Invoke(PtcLoadingState.Start, _translateCount, _translateTotalCount); - using AutoResetEvent progressReportEvent = new AutoResetEvent(false); + using AutoResetEvent progressReportEvent = new(false); - Thread progressReportThread = new Thread(ReportProgress) + Thread progressReportThread = new(ReportProgress) { Name = "Ptc.ProgressReporter", Priority = ThreadPriority.Lowest, - IsBackground = true + IsBackground = true, }; progressReportThread.Start(progressReportEvent); @@ -845,12 +843,14 @@ namespace ARMeilleure.Translation.PTC } } - List<Thread> threads = new List<Thread>(); + List<Thread> threads = new(); for (int i = 0; i < degreeOfParallelism; i++) { - Thread thread = new Thread(TranslateFuncs); - thread.IsBackground = true; + Thread thread = new(TranslateFuncs) + { + IsBackground = true, + }; threads.Add(thread); } @@ -871,8 +871,10 @@ namespace ARMeilleure.Translation.PTC Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism} in {sw.Elapsed.TotalSeconds} s"); - Thread preSaveThread = new Thread(PreSave); - preSaveThread.IsBackground = true; + Thread preSaveThread = new(PreSave) + { + IsBackground = true, + }; preSaveThread.Start(); } @@ -910,15 +912,16 @@ namespace ARMeilleure.Translation.PTC RelocInfo relocInfo = compiledFunc.RelocInfo; UnwindInfo unwindInfo = compiledFunc.UnwindInfo; - InfoEntry infoEntry = new InfoEntry(); - - infoEntry.Address = address; - infoEntry.GuestSize = guestSize; - infoEntry.Hash = hash; - infoEntry.HighCq = highCq; - infoEntry.Stubbed = false; - infoEntry.CodeLength = code.Length; - infoEntry.RelocEntriesCount = relocInfo.Entries.Length; + InfoEntry infoEntry = new() + { + Address = address, + GuestSize = guestSize, + Hash = hash, + HighCq = highCq, + Stubbed = false, + CodeLength = code.Length, + RelocEntriesCount = relocInfo.Entries.Length, + }; SerializeStructure(_infosStream, infoEntry); @@ -996,10 +999,12 @@ namespace ARMeilleure.Translation.PTC { uint osPlatform = 0u; +#pragma warning disable IDE0055 // Disable formatting osPlatform |= (OperatingSystem.IsFreeBSD() ? 1u : 0u) << 0; osPlatform |= (OperatingSystem.IsLinux() ? 1u : 0u) << 1; osPlatform |= (OperatingSystem.IsMacOS() ? 1u : 0u) << 2; osPlatform |= (OperatingSystem.IsWindows() ? 1u : 0u) << 3; +#pragma warning restore IDE0055 return osPlatform; } @@ -1025,14 +1030,14 @@ namespace ARMeilleure.Translation.PTC { Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())); + HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())]); } public bool IsHeaderValid() { Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash; + return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())]) == HeaderHash; } } @@ -1060,14 +1065,14 @@ namespace ARMeilleure.Translation.PTC { Span<InnerHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<InnerHeader>() - Unsafe.SizeOf<Hash128>())); + HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf<InnerHeader>() - Unsafe.SizeOf<Hash128>())]); } public bool IsHeaderValid() { Span<InnerHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<InnerHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash; + return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf<InnerHeader>() - Unsafe.SizeOf<Hash128>())]) == HeaderHash; } } diff --git a/src/ARMeilleure/Translation/PTC/PtcFormatter.cs b/src/ARMeilleure/Translation/PTC/PtcFormatter.cs index 2f7a9c21f..ddac31338 100644 --- a/src/ARMeilleure/Translation/PTC/PtcFormatter.cs +++ b/src/ARMeilleure/Translation/PTC/PtcFormatter.cs @@ -47,7 +47,7 @@ namespace ARMeilleure.Translation.PTC [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T DeserializeStructure<T>(Stream stream) where T : struct { - T structure = default(T); + T structure = default; Span<T> spanT = MemoryMarshal.CreateSpan(ref structure, 1); int bytesCount = stream.Read(MemoryMarshal.AsBytes(spanT)); @@ -176,4 +176,4 @@ namespace ARMeilleure.Translation.PTC } #endregion } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/PTC/PtcLoadingState.cs b/src/ARMeilleure/Translation/PTC/PtcLoadingState.cs index 526cf91fb..587be7939 100644 --- a/src/ARMeilleure/Translation/PTC/PtcLoadingState.cs +++ b/src/ARMeilleure/Translation/PTC/PtcLoadingState.cs @@ -4,6 +4,6 @@ namespace ARMeilleure.Translation.PTC { Start, Loading, - Loaded + Loaded, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs index 391e29c76..3a4bfcec6 100644 --- a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -12,7 +12,6 @@ using System.IO.Compression; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; - using static ARMeilleure.Translation.PTC.PtcFormatter; namespace ARMeilleure.Translation.PTC @@ -46,7 +45,7 @@ namespace ARMeilleure.Translation.PTC public bool Enabled { get; private set; } public ulong StaticCodeStart { get; set; } - public ulong StaticCodeSize { get; set; } + public ulong StaticCodeSize { get; set; } public PtcProfiler(Ptc ptc) { @@ -129,8 +128,8 @@ namespace ARMeilleure.Translation.PTC string fileNameActual = $"{_ptc.CachePathActual}.info"; string fileNameBackup = $"{_ptc.CachePathBackup}.info"; - FileInfo fileInfoActual = new FileInfo(fileNameActual); - FileInfo fileInfoBackup = new FileInfo(fileNameBackup); + FileInfo fileInfoActual = new(fileNameActual); + FileInfo fileInfoBackup = new(fileNameBackup); if (fileInfoActual.Exists && fileInfoActual.Length != 0L) { @@ -183,42 +182,40 @@ namespace ARMeilleure.Translation.PTC return false; } - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); + + try { - Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); - - try - { - deflateStream.CopyTo(stream); - } - catch - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - Debug.Assert(stream.Position == stream.Length); - - stream.Seek(0L, SeekOrigin.Begin); - - Hash128 expectedHash = DeserializeStructure<Hash128>(stream); - - Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); - - if (actualHash != expectedHash) - { - InvalidateCompressedStream(compressedStream); - - return false; - } - - ProfiledFuncs = Deserialize(stream); - - Debug.Assert(stream.Position == stream.Length); - - _lastHash = actualHash; + deflateStream.CopyTo(stream); } + catch + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + Debug.Assert(stream.Position == stream.Length); + + stream.Seek(0L, SeekOrigin.Begin); + + Hash128 expectedHash = DeserializeStructure<Hash128>(stream); + + Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); + + if (actualHash != expectedHash) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + ProfiledFuncs = Deserialize(stream); + + Debug.Assert(stream.Position == stream.Length); + + _lastHash = actualHash; } long fileSize = new FileInfo(fileName).Length; @@ -233,12 +230,12 @@ namespace ARMeilleure.Translation.PTC return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream)); } - private ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream) + private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream) { return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position); } - private void InvalidateCompressedStream(FileStream compressedStream) + private static void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); } @@ -250,7 +247,7 @@ namespace ARMeilleure.Translation.PTC string fileNameActual = $"{_ptc.CachePathActual}.info"; string fileNameBackup = $"{_ptc.CachePathBackup}.info"; - FileInfo fileInfoActual = new FileInfo(fileNameActual); + FileInfo fileInfoActual = new(fileNameActual); if (fileInfoActual.Exists && fileInfoActual.Length != 0L) { @@ -266,12 +263,13 @@ namespace ARMeilleure.Translation.PTC { int profiledFuncsCount; - OuterHeader outerHeader = new OuterHeader(); + OuterHeader outerHeader = new() + { + Magic = _outerHeaderMagic, - outerHeader.Magic = _outerHeaderMagic; - - outerHeader.InfoFileVersion = InternalVersion; - outerHeader.Endianness = Ptc.GetEndianness(); + InfoFileVersion = InternalVersion, + Endianness = Ptc.GetEndianness(), + }; outerHeader.SetHeaderHash(); @@ -301,28 +299,26 @@ namespace ARMeilleure.Translation.PTC return; } - using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate)) - using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true)) + using FileStream compressedStream = new(fileName, FileMode.OpenOrCreate); + using DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true); + try { - try - { - SerializeStructure(compressedStream, outerHeader); + SerializeStructure(compressedStream, outerHeader); - stream.WriteTo(deflateStream); + stream.WriteTo(deflateStream); - _lastHash = hash; - } - catch - { - compressedStream.Position = 0L; + _lastHash = hash; + } + catch + { + compressedStream.Position = 0L; - _lastHash = default; - } + _lastHash = default; + } - if (compressedStream.Position < compressedStream.Length) - { - compressedStream.SetLength(compressedStream.Position); - } + if (compressedStream.Position < compressedStream.Length) + { + compressedStream.SetLength(compressedStream.Position); } } @@ -334,7 +330,7 @@ namespace ARMeilleure.Translation.PTC } } - private void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs) + private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs) { SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); } @@ -354,14 +350,14 @@ namespace ARMeilleure.Translation.PTC { Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())); + HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())]); } public bool IsHeaderValid() { Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash; + return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())]) == HeaderHash; } } @@ -418,4 +414,4 @@ namespace ARMeilleure.Translation.PTC } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/PTC/PtcState.cs b/src/ARMeilleure/Translation/PTC/PtcState.cs index ca4f41080..f6692e870 100644 --- a/src/ARMeilleure/Translation/PTC/PtcState.cs +++ b/src/ARMeilleure/Translation/PTC/PtcState.cs @@ -5,6 +5,6 @@ namespace ARMeilleure.Translation.PTC Enabled, Continuing, Closing, - Disabled + Disabled, } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/RegisterToLocal.cs b/src/ARMeilleure/Translation/RegisterToLocal.cs index abb9b373c..91372eb00 100644 --- a/src/ARMeilleure/Translation/RegisterToLocal.cs +++ b/src/ARMeilleure/Translation/RegisterToLocal.cs @@ -9,7 +9,7 @@ namespace ARMeilleure.Translation { public static void Rename(ControlFlowGraph cfg) { - Dictionary<Register, Operand> registerToLocalMap = new Dictionary<Register, Operand>(); + Dictionary<Register, Operand> registerToLocalMap = new(); Operand GetLocal(Operand op) { @@ -49,4 +49,4 @@ namespace ARMeilleure.Translation } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/RegisterUsage.cs b/src/ARMeilleure/Translation/RegisterUsage.cs index 3ec0a7b4f..c8c250626 100644 --- a/src/ARMeilleure/Translation/RegisterUsage.cs +++ b/src/ARMeilleure/Translation/RegisterUsage.cs @@ -12,7 +12,7 @@ namespace ARMeilleure.Translation static class RegisterUsage { private const int RegsCount = 32; - private const int RegsMask = RegsCount - 1; + private const int RegsMask = RegsCount - 1; private readonly struct RegisterMask : IEquatable<RegisterMask> { @@ -90,7 +90,7 @@ namespace ARMeilleure.Translation public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode) { // Compute local register inputs and outputs used inside blocks. - RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count]; + RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count]; for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) @@ -119,7 +119,7 @@ namespace ARMeilleure.Translation // Compute global register inputs and outputs used across blocks. RegisterMask[] globalCmnOutputs = new RegisterMask[cfg.Blocks.Count]; - RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Count]; + RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] globalOutputs = new RegisterMask[cfg.Blocks.Count]; bool modified; @@ -286,10 +286,12 @@ namespace ARMeilleure.Translation switch (register.Type) { +#pragma warning disable IDE0055 // Disable formatting case RegisterType.Flag: intMask = (1L << RegsCount) << register.Index; break; case RegisterType.Integer: intMask = 1L << register.Index; break; case RegisterType.FpFlag: vecMask = (1L << RegsCount) << register.Index; break; case RegisterType.Vector: vecMask = 1L << register.Index; break; +#pragma warning restore IDE0055 } return new RegisterMask(intMask, vecMask); @@ -373,15 +375,14 @@ namespace ARMeilleure.Translation private static OperandType GetOperandType(RegisterType type, ExecutionMode mode) { - switch (type) + return type switch { - case RegisterType.Flag: return OperandType.I32; - case RegisterType.FpFlag: return OperandType.I32; - case RegisterType.Integer: return (mode == ExecutionMode.Aarch64) ? OperandType.I64 : OperandType.I32; - case RegisterType.Vector: return OperandType.V128; - } - - throw new ArgumentException($"Invalid register type \"{type}\"."); + RegisterType.Flag => OperandType.I32, + RegisterType.FpFlag => OperandType.I32, + RegisterType.Integer => (mode == ExecutionMode.Aarch64) ? OperandType.I64 : OperandType.I32, + RegisterType.Vector => OperandType.V128, + _ => throw new ArgumentException($"Invalid register type \"{type}\"."), + }; } private static bool EndsWithReturn(BasicBlock block) @@ -391,4 +392,4 @@ namespace ARMeilleure.Translation return last != default && last.Instruction == Instruction.Return; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/SsaConstruction.cs b/src/ARMeilleure/Translation/SsaConstruction.cs index 2b6efc113..cddcfcd4f 100644 --- a/src/ARMeilleure/Translation/SsaConstruction.cs +++ b/src/ARMeilleure/Translation/SsaConstruction.cs @@ -180,7 +180,7 @@ namespace ARMeilleure.Translation } previous = current; - current = current.ImmediateDominator; + current = current.ImmediateDominator; } while (previous != current); @@ -286,4 +286,4 @@ namespace ARMeilleure.Translation return key; } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/SsaDeconstruction.cs b/src/ARMeilleure/Translation/SsaDeconstruction.cs index cd6bcca1f..68af54e5d 100644 --- a/src/ARMeilleure/Translation/SsaDeconstruction.cs +++ b/src/ARMeilleure/Translation/SsaDeconstruction.cs @@ -45,4 +45,4 @@ namespace ARMeilleure.Translation } } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/TranslatedFunction.cs b/src/ARMeilleure/Translation/TranslatedFunction.cs index f007883ef..1446c254a 100644 --- a/src/ARMeilleure/Translation/TranslatedFunction.cs +++ b/src/ARMeilleure/Translation/TranslatedFunction.cs @@ -31,4 +31,4 @@ namespace ARMeilleure.Translation return dispatcher(context.NativeContextPtr, (ulong)FuncPointer); } } -} \ No newline at end of file +} diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index 234be2ac7..dc18038ba 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -22,24 +22,24 @@ namespace ARMeilleure.Translation { public class Translator { - private static readonly AddressTable<ulong>.Level[] Levels64Bit = + private static readonly AddressTable<ulong>.Level[] _levels64Bit = new AddressTable<ulong>.Level[] { new(31, 17), new(23, 8), new(15, 8), new( 7, 8), - new( 2, 5) + new( 2, 5), }; - private static readonly AddressTable<ulong>.Level[] Levels32Bit = + private static readonly AddressTable<ulong>.Level[] _levels32Bit = new AddressTable<ulong>.Level[] { new(31, 17), new(23, 8), new(15, 8), new( 7, 8), - new( 1, 6) + new( 1, 6), }; private readonly IJitMemoryAllocator _allocator; @@ -75,7 +75,7 @@ namespace ARMeilleure.Translation CountTable = new EntryTable<uint>(); Functions = new TranslatorCache<TranslatedFunction>(); - FunctionTable = new AddressTable<ulong>(for64Bits ? Levels64Bit : Levels32Bit); + FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit); Stubs = new TranslatorStubs(this); FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; @@ -126,7 +126,7 @@ namespace ARMeilleure.Translation // TODO: Use physical cores rather than logical. This only really makes sense for processors with // hyperthreading. Requires OS specific code. int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3); - int threadCount = Math.Min(4, unboundedThreadCount); + int threadCount = Math.Min(4, unboundedThreadCount); Thread[] backgroundTranslationThreads = new Thread[threadCount]; @@ -134,10 +134,10 @@ namespace ARMeilleure.Translation { bool last = i != 0 && i == unboundedThreadCount - 1; - backgroundTranslationThreads[i] = new Thread(BackgroundTranslate) + backgroundTranslationThreads[i] = new(BackgroundTranslate) { Name = "CPU.BackgroundTranslatorThread." + i, - Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal + Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal, }; backgroundTranslationThreads[i].Start(); diff --git a/src/ARMeilleure/Translation/TranslatorStubs.cs b/src/ARMeilleure/Translation/TranslatorStubs.cs index 69648df44..eceb1b742 100644 --- a/src/ARMeilleure/Translation/TranslatorStubs.cs +++ b/src/ARMeilleure/Translation/TranslatorStubs.cs @@ -224,7 +224,7 @@ namespace ARMeilleure.Translation /// <param name="context">Emitter context for the method</param> /// <param name="nativeContext">Pointer to the native context</param> /// <param name="enter">True if entering guest code, false otherwise</param> - private void EmitSyncFpContext(EmitterContext context, Operand nativeContext, bool enter) + private static void EmitSyncFpContext(EmitterContext context, Operand nativeContext, bool enter) { if (enter) { diff --git a/src/ARMeilleure/Translation/TranslatorTestMethods.cs b/src/ARMeilleure/Translation/TranslatorTestMethods.cs index ab96019a6..35cd8dc56 100644 --- a/src/ARMeilleure/Translation/TranslatorTestMethods.cs +++ b/src/ARMeilleure/Translation/TranslatorTestMethods.cs @@ -1,7 +1,6 @@ using ARMeilleure.CodeGen.X86; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; -using ARMeilleure.Translation; using System; using System.Runtime.InteropServices; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; @@ -62,7 +61,7 @@ namespace ARMeilleure.Translation public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest() { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand methodAddress = context.Copy(context.LoadArgument(OperandType.I64, 0)); @@ -110,7 +109,7 @@ namespace ARMeilleure.Translation context.MarkLabel(correct2Label); - // Call a managed method. This method should not change Fz state. + // Call a managed method. This method should not change Fz state. context.Call(methodAddress, OperandType.None); From e96299eef5a9e951d9161b5a13bde9feecc2178c Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 26 Jun 2023 07:35:19 +0200 Subject: [PATCH 668/737] [Ryujinx.Horizon.Generators] Address dotnet-format issues (#5383) * dotnet format style --severity info Some changes were manually reverted. * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * dotnet format whitespace after rebase --- src/Ryujinx.Horizon.Generators/CodeGenerator.cs | 2 +- src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Horizon.Generators/CodeGenerator.cs b/src/Ryujinx.Horizon.Generators/CodeGenerator.cs index 29e1c75c8..59052499e 100644 --- a/src/Ryujinx.Horizon.Generators/CodeGenerator.cs +++ b/src/Ryujinx.Horizon.Generators/CodeGenerator.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Horizon.Generators { if (_currentIndentCount - 1 >= 0) { - _currentIndentCount--; + _currentIndentCount--; } } diff --git a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs index 284df4820..47b3c8cfb 100644 --- a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs +++ b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -39,7 +39,7 @@ namespace Ryujinx.Horizon.Generators.Hipc In } - private struct OutParameter + private readonly struct OutParameter { public readonly string Name; public readonly string TypeName; @@ -341,7 +341,7 @@ namespace Ryujinx.Horizon.Generators.Hipc generator.AppendLine($"using var {argName} = {value};"); string spanGenericTypeName = GetCanonicalTypeNameOfGenericArgument(compilation, parameter.Type, 0); - argName = GenerateSpanCast(spanGenericTypeName, $"{argName}.Memory.Span"); ; + argName = GenerateSpanCast(spanGenericTypeName, $"{argName}.Memory.Span"); } else if (isNonSpanBuffer) { @@ -604,7 +604,7 @@ namespace Ryujinx.Horizon.Generators.Hipc return type; } - private static bool IsArgument(Compilation compilation,ParameterSyntax parameter) + private static bool IsArgument(Compilation compilation, ParameterSyntax parameter) { return !IsBuffer(compilation, parameter) && !IsHandle(compilation, parameter) && From 0191e2396a3a6e499f49aa3b7c9937ccd489856c Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 27 Jun 2023 16:35:48 +0200 Subject: [PATCH 669/737] [Ryujinx.Graphics.Host1x] Address dotnet-format issues (#5368) * dotnet format style --severity info Some changes were manually reverted. * Address most dotnet format whitespace warnings * Add comments to disabled warnings * dotnet format whitespace after rebase --- src/Ryujinx.Graphics.Host1x/Devices.cs | 2 +- src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs | 2 +- src/Ryujinx.Graphics.Host1x/Host1xDevice.cs | 2 +- src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs | 2 +- src/Ryujinx.Graphics.Host1x/ThiRegisters.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Host1x/Devices.cs b/src/Ryujinx.Graphics.Host1x/Devices.cs index 5b3bed6b0..95c67c9c4 100644 --- a/src/Ryujinx.Graphics.Host1x/Devices.cs +++ b/src/Ryujinx.Graphics.Host1x/Devices.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Host1x { class Devices : IDisposable { - private readonly Dictionary<ClassId, IDeviceState> _devices = new Dictionary<ClassId, IDeviceState>(); + private readonly Dictionary<ClassId, IDeviceState> _devices = new(); public void RegisterDevice(ClassId classId, IDeviceState device) { diff --git a/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs b/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs index f9f4889be..9eeb18d10 100644 --- a/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs +++ b/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Host1x { struct Host1xClassRegisters { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint IncrSyncpt; public uint IncrSyncptCntrl; public uint IncrSyncptError; diff --git a/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs b/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs index 90dd4fa05..73dabaf42 100644 --- a/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs +++ b/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Host1x private readonly SyncptIncrManager _syncptIncrMgr; private readonly AsyncWorkQueue<Command> _commandQueue; - private readonly Devices _devices = new Devices(); + private readonly Devices _devices = new(); public Host1xClass Class { get; } diff --git a/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs b/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs index 62c499175..fe8c87395 100644 --- a/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs +++ b/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Host1x } } - private readonly List<SyncptIncr> _incrs = new List<SyncptIncr>(); + private readonly List<SyncptIncr> _incrs = new(); private uint _currentId; diff --git a/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs b/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs index 71c485110..74e646952 100644 --- a/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs +++ b/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Host1x { struct ThiRegisters { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint IncrSyncpt; public uint Reserved4; public uint IncrSyncptErr; From b186ec9fc5684bd8fa831a1777f6e936b897c352 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 27 Jun 2023 16:45:33 +0200 Subject: [PATCH 670/737] [Ryujinx.Graphics.Video] Address dotnet-format issues (#5377) * Address most dotnet format whitespace warnings * dotnet format whitespace after rebase --- src/Ryujinx.Graphics.Video/FrameField.cs | 2 +- src/Ryujinx.Graphics.Video/Vp8PictureInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Video/FrameField.cs b/src/Ryujinx.Graphics.Video/FrameField.cs index 2bff0e75f..eafd74e07 100644 --- a/src/Ryujinx.Graphics.Video/FrameField.cs +++ b/src/Ryujinx.Graphics.Video/FrameField.cs @@ -5,4 +5,4 @@ namespace Ryujinx.Graphics.Video Progressive, Interlaced } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Video/Vp8PictureInfo.cs b/src/Ryujinx.Graphics.Video/Vp8PictureInfo.cs index 878674b8e..df398d843 100644 --- a/src/Ryujinx.Graphics.Video/Vp8PictureInfo.cs +++ b/src/Ryujinx.Graphics.Video/Vp8PictureInfo.cs @@ -8,4 +8,4 @@ public ushort FrameWidth; public ushort FrameHeight; } -} \ No newline at end of file +} From fbaf62c2309f2987fa73a2022167ee3e81e31ea9 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 01:18:19 +0200 Subject: [PATCH 671/737] Apply new naming rule to all projects except Vp9 (#5407) --- src/ARMeilleure/CodeGen/X86/CodeGenerator.cs | 10 +- src/ARMeilleure/Decoders/Decoder.cs | 4 +- .../Decoders/Optimizations/TailCallRemover.cs | 6 +- .../Instructions/InstEmitSimdHelper.cs | 32 ++--- .../Instructions/InstEmitSimdLogical.cs | 52 ++++----- src/ARMeilleure/Instructions/SoftFloat.cs | 66 +++++------ src/ARMeilleure/Signal/NativeSignalHandler.cs | 25 ++-- src/ARMeilleure/Translation/PTC/Ptc.cs | 4 +- .../Dsp/Command/CircularBufferSinkCommand.cs | 8 +- .../Renderer/Dsp/Command/DelayCommand.cs | 18 +-- .../Renderer/Dsp/Command/DeviceSinkCommand.cs | 10 +- .../Renderer/Dsp/Command/Reverb3dCommand.cs | 8 +- .../Renderer/Dsp/DataSourceHelper.cs | 10 +- .../Renderer/Dsp/UpsamplerHelper.cs | 16 +-- .../CommandProcessingTimeEstimatorVersion2.cs | 6 +- .../Renderer/Server/MemoryPool/PoolMapper.cs | 8 +- .../Controls/ApplicationContextMenu.axaml.cs | 2 +- src/Ryujinx.Ava/UI/Models/SaveModel.cs | 8 +- .../UI/ViewModels/MainWindowViewModel.cs | 12 +- .../UI/ViewModels/SettingsViewModel.cs | 4 +- .../SystemInfo/MacOSSystemInfo.cs | 6 +- .../SystemInterop/StdErrAdapter.cs | 4 +- .../Memory/PhysicalMemory.cs | 2 +- src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 2 +- .../FileSystem/VirtualFileSystem.cs | 8 +- src/Ryujinx.HLE/HOS/Horizon.cs | 6 +- src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs | 18 +-- src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs | 4 +- .../HOS/Services/Sdb/Pl/SharedFontManager.cs | 6 +- src/Ryujinx.HLE/HOS/Services/ServerBase.cs | 4 +- .../Settings/ISystemSettingsServer.cs | 36 +++--- .../RootService/IApplicationDisplayService.cs | 10 +- .../HOS/Tamper/InstructionHelper.cs | 10 +- .../MultiRegionTrackingTests.cs | 60 +++++----- src/Ryujinx.Tests.Memory/TrackingTests.cs | 36 +++--- src/Ryujinx.Tests/Cpu/CpuTestMisc.cs | 8 +- src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs | 110 +++++++++--------- src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs | 10 +- src/Ryujinx.Ui.Common/App/ApplicationData.cs | 12 +- .../App/ApplicationLibrary.cs | 2 +- .../Ui/Widgets/GameTableContextMenu.cs | 4 +- 42 files changed, 334 insertions(+), 335 deletions(-) diff --git a/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs index 3cab0b6ce..9e94a077f 100644 --- a/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/src/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -1296,11 +1296,11 @@ namespace ARMeilleure.CodeGen.X86 } else { - const byte mask = 0b01_00_11_10; + const byte Mask = 0b01_00_11_10; - context.Assembler.Pshufd(src1, src1, mask); + context.Assembler.Pshufd(src1, src1, Mask); context.Assembler.Movq(dest, src1); - context.Assembler.Pshufd(src1, src1, mask); + context.Assembler.Pshufd(src1, src1, Mask); } } else @@ -1853,9 +1853,9 @@ namespace ARMeilleure.CodeGen.X86 // that the OS will map all pages that we'll use. We do that by // doing a dummy read on those pages, forcing a page fault and // the OS to map them. If they are already mapped, nothing happens. - const int pageMask = PageSize - 1; + const int PageMask = PageSize - 1; - size = (size + pageMask) & ~pageMask; + size = (size + PageMask) & ~PageMask; Operand rsp = Register(X86Register.Rsp); Operand temp = Register(CallingConvention.GetIntReturnRegister()); diff --git a/src/ARMeilleure/Decoders/Decoder.cs b/src/ARMeilleure/Decoders/Decoder.cs index d8abeb9c8..6d07827a2 100644 --- a/src/ARMeilleure/Decoders/Decoder.cs +++ b/src/ARMeilleure/Decoders/Decoder.cs @@ -304,9 +304,9 @@ namespace ARMeilleure.Decoders } else if (opCode is IOpCode32MemMult opMemMult) { - const int pcMask = 1 << RegisterAlias.Aarch32Pc; + const int PCMask = 1 << RegisterAlias.Aarch32Pc; - rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0; + rt = (opMemMult.RegisterMask & PCMask) != 0 ? RegisterAlias.Aarch32Pc : 0; rn = opMemMult.Rn; wBack = opMemMult.PostOffset != 0; isLoad = opMemMult.IsLoad; diff --git a/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs b/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs index ff9a6f271..20759f356 100644 --- a/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs +++ b/src/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs @@ -17,7 +17,7 @@ namespace ARMeilleure.Decoders.Optimizations throw new InvalidOperationException("Function entry point is not contained in a block."); } - const ulong allowance = 4; + const ulong Allowance = 4; Block entryBlock = blocks[entryBlockId]; @@ -31,7 +31,7 @@ namespace ARMeilleure.Decoders.Optimizations { Block block = blocks[i]; - if (endBlock.EndAddress < block.Address - allowance) + if (endBlock.EndAddress < block.Address - Allowance) { break; // End of contiguous function. } @@ -44,7 +44,7 @@ namespace ARMeilleure.Decoders.Optimizations { Block block = blocks[i]; - if (startBlock.Address > block.EndAddress + allowance) + if (startBlock.Address > block.EndAddress + Allowance) { break; // End of contiguous function. } diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs index 35052ad11..0b5527409 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -1299,17 +1299,17 @@ namespace ARMeilleure.Instructions Debug.Assert((op.Size & 1) == 0 && op.RegisterSize == RegisterSize.Simd128); - const int sm0 = 0 << 6 | 0 << 4 | 0 << 2 | 0 << 0; - const int sm1 = 1 << 6 | 1 << 4 | 1 << 2 | 1 << 0; - const int sm2 = 2 << 6 | 2 << 4 | 2 << 2 | 2 << 0; - const int sm3 = 3 << 6 | 3 << 4 | 3 << 2 | 3 << 0; + const int SM0 = 0 << 6 | 0 << 4 | 0 << 2 | 0 << 0; + const int SM1 = 1 << 6 | 1 << 4 | 1 << 2 | 1 << 0; + const int SM2 = 2 << 6 | 2 << 4 | 2 << 2 | 2 << 0; + const int SM3 = 3 << 6 | 3 << 4 | 3 << 2 | 3 << 0; Operand nCopy = context.Copy(GetVec(op.Rn)); - Operand part0 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(sm0)); - Operand part1 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(sm1)); - Operand part2 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(sm2)); - Operand part3 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(sm3)); + Operand part0 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(SM0)); + Operand part1 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(SM1)); + Operand part2 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(SM2)); + Operand part3 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, nCopy, Const(SM3)); Operand res = emit(emit(part0, part1), emit(part2, part3)); @@ -1340,13 +1340,13 @@ namespace ARMeilleure.Instructions if ((op.Size & 1) == 0) { - const int sm0 = 2 << 6 | 2 << 4 | 2 << 2 | 0 << 0; - const int sm1 = 2 << 6 | 2 << 4 | 2 << 2 | 1 << 0; + const int SM0 = 2 << 6 | 2 << 4 | 2 << 2 | 0 << 0; + const int SM1 = 2 << 6 | 2 << 4 | 2 << 2 | 1 << 0; Operand zeroN = context.VectorZeroUpper64(n); - op0 = context.AddIntrinsic(Intrinsic.X86Pshufd, zeroN, Const(sm0)); - op1 = context.AddIntrinsic(Intrinsic.X86Pshufd, zeroN, Const(sm1)); + op0 = context.AddIntrinsic(Intrinsic.X86Pshufd, zeroN, Const(SM0)); + op1 = context.AddIntrinsic(Intrinsic.X86Pshufd, zeroN, Const(SM1)); } else /* if ((op.Size & 1) == 1) */ { @@ -1412,11 +1412,11 @@ namespace ARMeilleure.Instructions } else /* if (op.RegisterSize == RegisterSize.Simd128) */ { - const int sm0 = 2 << 6 | 0 << 4 | 2 << 2 | 0 << 0; - const int sm1 = 3 << 6 | 1 << 4 | 3 << 2 | 1 << 0; + const int SM0 = 2 << 6 | 0 << 4 | 2 << 2 | 0 << 0; + const int SM1 = 3 << 6 | 1 << 4 | 3 << 2 | 1 << 0; - Operand part0 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, mCopy, Const(sm0)); - Operand part1 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, mCopy, Const(sm1)); + Operand part0 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, mCopy, Const(SM0)); + Operand part1 = context.AddIntrinsic(Intrinsic.X86Shufps, nCopy, mCopy, Const(SM1)); context.Copy(GetVec(op.Rd), emit(part0, part1)); } diff --git a/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs b/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs index 97e3da67e..ace8e4c54 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdLogical.cs @@ -408,7 +408,7 @@ namespace ARMeilleure.Instructions if (Optimizations.UseGfni) { - const long bitMatrix = + const long BitMatrix = (0b10000000L << 56) | (0b01000000L << 48) | (0b00100000L << 40) | @@ -418,7 +418,7 @@ namespace ARMeilleure.Instructions (0b00000010L << 8) | (0b00000001L << 0); - Operand vBitMatrix = X86GetAllElements(context, bitMatrix); + Operand vBitMatrix = X86GetAllElements(context, BitMatrix); Operand res = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, GetVec(op.Rn), vBitMatrix, Const(0)); @@ -469,12 +469,12 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); - const long maskE0 = 06L << 56 | 07L << 48 | 04L << 40 | 05L << 32 | 02L << 24 | 03L << 16 | 00L << 8 | 01L << 0; - const long maskE1 = 14L << 56 | 15L << 48 | 12L << 40 | 13L << 32 | 10L << 24 | 11L << 16 | 08L << 8 | 09L << 0; + const long MaskE0 = 06L << 56 | 07L << 48 | 04L << 40 | 05L << 32 | 02L << 24 | 03L << 16 | 00L << 8 | 01L << 0; + const long MaskE1 = 14L << 56 | 15L << 48 | 12L << 40 | 13L << 32 | 10L << 24 | 11L << 16 | 08L << 8 | 09L << 0; - Operand mask = X86GetScalar(context, maskE0); + Operand mask = X86GetScalar(context, MaskE0); - mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3); + mask = EmitVectorInsert(context, mask, Const(MaskE1), 1, 3); Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask); @@ -503,21 +503,21 @@ namespace ARMeilleure.Instructions if (op.Size == 0) { - const long maskE0 = 04L << 56 | 05L << 48 | 06L << 40 | 07L << 32 | 00L << 24 | 01L << 16 | 02L << 8 | 03L << 0; - const long maskE1 = 12L << 56 | 13L << 48 | 14L << 40 | 15L << 32 | 08L << 24 | 09L << 16 | 10L << 8 | 11L << 0; + const long MaskE0 = 04L << 56 | 05L << 48 | 06L << 40 | 07L << 32 | 00L << 24 | 01L << 16 | 02L << 8 | 03L << 0; + const long MaskE1 = 12L << 56 | 13L << 48 | 14L << 40 | 15L << 32 | 08L << 24 | 09L << 16 | 10L << 8 | 11L << 0; - mask = X86GetScalar(context, maskE0); + mask = X86GetScalar(context, MaskE0); - mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3); + mask = EmitVectorInsert(context, mask, Const(MaskE1), 1, 3); } else /* if (op.Size == 1) */ { - const long maskE0 = 05L << 56 | 04L << 48 | 07L << 40 | 06L << 32 | 01L << 24 | 00L << 16 | 03L << 8 | 02L << 0; - const long maskE1 = 13L << 56 | 12L << 48 | 15L << 40 | 14L << 32 | 09L << 24 | 08L << 16 | 11L << 8 | 10L << 0; + const long MaskE0 = 05L << 56 | 04L << 48 | 07L << 40 | 06L << 32 | 01L << 24 | 00L << 16 | 03L << 8 | 02L << 0; + const long MaskE1 = 13L << 56 | 12L << 48 | 15L << 40 | 14L << 32 | 09L << 24 | 08L << 16 | 11L << 8 | 10L << 0; - mask = X86GetScalar(context, maskE0); + mask = X86GetScalar(context, MaskE0); - mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3); + mask = EmitVectorInsert(context, mask, Const(MaskE1), 1, 3); } Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask); @@ -547,30 +547,30 @@ namespace ARMeilleure.Instructions if (op.Size == 0) { - const long maskE0 = 00L << 56 | 01L << 48 | 02L << 40 | 03L << 32 | 04L << 24 | 05L << 16 | 06L << 8 | 07L << 0; - const long maskE1 = 08L << 56 | 09L << 48 | 10L << 40 | 11L << 32 | 12L << 24 | 13L << 16 | 14L << 8 | 15L << 0; + const long MaskE0 = 00L << 56 | 01L << 48 | 02L << 40 | 03L << 32 | 04L << 24 | 05L << 16 | 06L << 8 | 07L << 0; + const long MaskE1 = 08L << 56 | 09L << 48 | 10L << 40 | 11L << 32 | 12L << 24 | 13L << 16 | 14L << 8 | 15L << 0; - mask = X86GetScalar(context, maskE0); + mask = X86GetScalar(context, MaskE0); - mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3); + mask = EmitVectorInsert(context, mask, Const(MaskE1), 1, 3); } else if (op.Size == 1) { - const long maskE0 = 01L << 56 | 00L << 48 | 03L << 40 | 02L << 32 | 05L << 24 | 04L << 16 | 07L << 8 | 06L << 0; - const long maskE1 = 09L << 56 | 08L << 48 | 11L << 40 | 10L << 32 | 13L << 24 | 12L << 16 | 15L << 8 | 14L << 0; + const long MaskE0 = 01L << 56 | 00L << 48 | 03L << 40 | 02L << 32 | 05L << 24 | 04L << 16 | 07L << 8 | 06L << 0; + const long MaskE1 = 09L << 56 | 08L << 48 | 11L << 40 | 10L << 32 | 13L << 24 | 12L << 16 | 15L << 8 | 14L << 0; - mask = X86GetScalar(context, maskE0); + mask = X86GetScalar(context, MaskE0); - mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3); + mask = EmitVectorInsert(context, mask, Const(MaskE1), 1, 3); } else /* if (op.Size == 2) */ { - const long maskE0 = 03L << 56 | 02L << 48 | 01L << 40 | 00L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0; - const long maskE1 = 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 15L << 24 | 14L << 16 | 13L << 8 | 12L << 0; + const long MaskE0 = 03L << 56 | 02L << 48 | 01L << 40 | 00L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0; + const long MaskE1 = 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 15L << 24 | 14L << 16 | 13L << 8 | 12L << 0; - mask = X86GetScalar(context, maskE0); + mask = X86GetScalar(context, MaskE0); - mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3); + mask = EmitVectorInsert(context, mask, Const(MaskE1), 1, 3); } Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask); diff --git a/src/ARMeilleure/Instructions/SoftFloat.cs b/src/ARMeilleure/Instructions/SoftFloat.cs index e8f44ce30..05975d04f 100644 --- a/src/ARMeilleure/Instructions/SoftFloat.cs +++ b/src/ARMeilleure/Instructions/SoftFloat.cs @@ -175,10 +175,10 @@ namespace ARMeilleure.Instructions public static ushort FPRoundCv(double real, ExecutionContext context) { - const int minimumExp = -14; + const int MinimumExp = -14; - const int e = 5; - const int f = 10; + const int E = 5; + const int F = 10; bool sign; double mantissa; @@ -208,15 +208,15 @@ namespace ARMeilleure.Instructions exponent++; } - uint biasedExp = (uint)Math.Max(exponent - minimumExp + 1, 0); + uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0); if (biasedExp == 0u) { - mantissa /= Math.Pow(2d, minimumExp - exponent); + mantissa /= Math.Pow(2d, MinimumExp - exponent); } - uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, f)); - double error = mantissa * Math.Pow(2d, f) - (double)intMant; + uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, F)); + double error = mantissa * Math.Pow(2d, F) - (double)intMant; if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) { @@ -256,12 +256,12 @@ namespace ARMeilleure.Instructions { intMant++; - if (intMant == 1u << f) + if (intMant == 1u << F) { biasedExp = 1u; } - if (intMant == 1u << (f + 1)) + if (intMant == 1u << (F + 1)) { biasedExp++; intMant >>= 1; @@ -272,7 +272,7 @@ namespace ARMeilleure.Instructions if ((context.Fpcr & FPCR.Ahp) == 0) { - if (biasedExp >= (1u << e) - 1u) + if (biasedExp >= (1u << E) - 1u) { resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); @@ -287,7 +287,7 @@ namespace ARMeilleure.Instructions } else { - if (biasedExp >= 1u << e) + if (biasedExp >= 1u << E) { resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); @@ -354,10 +354,10 @@ namespace ARMeilleure.Instructions private static float FPRoundCv(double real, ExecutionContext context) { - const int minimumExp = -126; + const int MinimumExp = -126; - const int e = 8; - const int f = 23; + const int E = 8; + const int F = 23; bool sign; double mantissa; @@ -387,22 +387,22 @@ namespace ARMeilleure.Instructions exponent++; } - if ((context.Fpcr & FPCR.Fz) != 0 && exponent < minimumExp) + if ((context.Fpcr & FPCR.Fz) != 0 && exponent < MinimumExp) { context.Fpsr |= FPSR.Ufc; return SoftFloat32.FPZero(sign); } - uint biasedExp = (uint)Math.Max(exponent - minimumExp + 1, 0); + uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0); if (biasedExp == 0u) { - mantissa /= Math.Pow(2d, minimumExp - exponent); + mantissa /= Math.Pow(2d, MinimumExp - exponent); } - uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, f)); - double error = mantissa * Math.Pow(2d, f) - (double)intMant; + uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, F)); + double error = mantissa * Math.Pow(2d, F) - (double)intMant; if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) { @@ -442,12 +442,12 @@ namespace ARMeilleure.Instructions { intMant++; - if (intMant == 1u << f) + if (intMant == 1u << F) { biasedExp = 1u; } - if (intMant == 1u << (f + 1)) + if (intMant == 1u << (F + 1)) { biasedExp++; intMant >>= 1; @@ -456,7 +456,7 @@ namespace ARMeilleure.Instructions float result; - if (biasedExp >= (1u << e) - 1u) + if (biasedExp >= (1u << E) - 1u) { result = overflowToInf ? SoftFloat32.FPInfinity(sign) : SoftFloat32.FPMaxNormal(sign); @@ -529,10 +529,10 @@ namespace ARMeilleure.Instructions private static double FPRoundCv(double real, ExecutionContext context) { - const int minimumExp = -1022; + const int MinimumExp = -1022; - const int e = 11; - const int f = 52; + const int E = 11; + const int F = 52; bool sign; double mantissa; @@ -562,22 +562,22 @@ namespace ARMeilleure.Instructions exponent++; } - if ((context.Fpcr & FPCR.Fz) != 0 && exponent < minimumExp) + if ((context.Fpcr & FPCR.Fz) != 0 && exponent < MinimumExp) { context.Fpsr |= FPSR.Ufc; return SoftFloat64.FPZero(sign); } - uint biasedExp = (uint)Math.Max(exponent - minimumExp + 1, 0); + uint biasedExp = (uint)Math.Max(exponent - MinimumExp + 1, 0); if (biasedExp == 0u) { - mantissa /= Math.Pow(2d, minimumExp - exponent); + mantissa /= Math.Pow(2d, MinimumExp - exponent); } - ulong intMant = (ulong)Math.Floor(mantissa * Math.Pow(2d, f)); - double error = mantissa * Math.Pow(2d, f) - (double)intMant; + ulong intMant = (ulong)Math.Floor(mantissa * Math.Pow(2d, F)); + double error = mantissa * Math.Pow(2d, F) - (double)intMant; if (biasedExp == 0u && (error != 0d || (context.Fpcr & FPCR.Ufe) != 0)) { @@ -617,12 +617,12 @@ namespace ARMeilleure.Instructions { intMant++; - if (intMant == 1ul << f) + if (intMant == 1ul << F) { biasedExp = 1u; } - if (intMant == 1ul << (f + 1)) + if (intMant == 1ul << (F + 1)) { biasedExp++; intMant >>= 1; @@ -631,7 +631,7 @@ namespace ARMeilleure.Instructions double result; - if (biasedExp >= (1u << e) - 1u) + if (biasedExp >= (1u << E) - 1u) { result = overflowToInf ? SoftFloat64.FPInfinity(sign) : SoftFloat64.FPMaxNormal(sign); diff --git a/src/ARMeilleure/Signal/NativeSignalHandler.cs b/src/ARMeilleure/Signal/NativeSignalHandler.cs index ed284677d..3f0e9e4bf 100644 --- a/src/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/src/ARMeilleure/Signal/NativeSignalHandler.cs @@ -5,7 +5,6 @@ using ARMeilleure.Translation.Cache; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Signal @@ -261,20 +260,20 @@ namespace ARMeilleure.Signal { if (OperatingSystem.IsMacOS()) { - const ulong mcontextOffset = 48; // uc_mcontext - Operand ctxPtr = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(mcontextOffset))); + const ulong McontextOffset = 48; // uc_mcontext + Operand ctxPtr = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(McontextOffset))); if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { - const ulong esrOffset = 8; // __es.__esr - Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(esrOffset))); + const ulong EsrOffset = 8; // __es.__esr + Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset))); return context.BitwiseAnd(esr, Const(0x40ul)); } if (RuntimeInformation.ProcessArchitecture == Architecture.X64) { - const ulong errOffset = 4; // __es.__err - Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(errOffset))); + const ulong ErrOffset = 4; // __es.__err + Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset))); return context.BitwiseAnd(err, Const(2ul)); } } @@ -287,10 +286,10 @@ namespace ARMeilleure.Signal Operand loopLabel = Label(); Operand successLabel = Label(); - const ulong auxOffset = 464; // uc_mcontext.__reserved - const uint esrMagic = 0x45535201; + const ulong AuxOffset = 464; // uc_mcontext.__reserved + const uint EsrMagic = 0x45535201; - context.Copy(auxPtr, context.Add(ucontextPtr, Const(auxOffset))); + context.Copy(auxPtr, context.Add(ucontextPtr, Const(AuxOffset))); context.MarkLabel(loopLabel); @@ -299,7 +298,7 @@ namespace ARMeilleure.Signal // _aarch64_ctx::size Operand size = context.Load(OperandType.I32, context.Add(auxPtr, Const(4ul))); - context.BranchIf(successLabel, magic, Const(esrMagic), Comparison.Equal); + context.BranchIf(successLabel, magic, Const(EsrMagic), Comparison.Equal); context.Copy(auxPtr, context.Add(auxPtr, context.ZeroExtend32(OperandType.I64, size))); @@ -314,8 +313,8 @@ namespace ARMeilleure.Signal if (RuntimeInformation.ProcessArchitecture == Architecture.X64) { - const int errOffset = 192; // uc_mcontext.gregs[REG_ERR] - Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(errOffset))); + const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR] + Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset))); return context.BitwiseAnd(err, Const(2ul)); } } diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 665f568d2..72b60fab0 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -880,7 +880,7 @@ namespace ARMeilleure.Translation.PTC private void ReportProgress(object state) { - const int refreshRate = 50; // ms. + const int RefreshRate = 50; // ms. AutoResetEvent endEvent = (AutoResetEvent)state; @@ -896,7 +896,7 @@ namespace ARMeilleure.Translation.PTC count = newCount; } } - while (!endEvent.WaitOne(refreshRate)); + while (!endEvent.WaitOne(RefreshRate)); } public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs index e50637eb3..59ef70932 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public void Process(CommandList context) { - const int targetChannelCount = 2; + const int TargetChannelCount = 2; ulong currentOffset = CurrentOffset; @@ -59,10 +59,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command for (int y = 0; y < context.SampleCount; y++) { - context.MemoryManager.Write(targetOffset + (ulong)y * targetChannelCount, PcmHelper.Saturate(inputBuffer[y])); + context.MemoryManager.Write(targetOffset + (ulong)y * TargetChannelCount, PcmHelper.Saturate(inputBuffer[y])); } - currentOffset += context.SampleCount * targetChannelCount; + currentOffset += context.SampleCount * TargetChannelCount; if (currentOffset >= CircularBufferSize) { @@ -73,4 +73,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index 6dc766594..e7e179389 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount) { - const ushort channelCount = 1; + const ushort ChannelCount = 1; float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision); float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision); @@ -70,7 +70,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float temp = input * inGain + delayLineValue * feedbackGain; - state.UpdateLowPassFilter(ref temp, channelCount); + state.UpdateLowPassFilter(ref temp, ChannelCount); outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64; } @@ -79,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelayStereo(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { - const ushort channelCount = 2; + const ushort ChannelCount = 2; float delayFeedbackBaseGain = state.DelayFeedbackBaseGain; float delayFeedbackCrossGain = state.DelayFeedbackCrossGain; @@ -106,7 +106,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Vector2 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; - state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount); + state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), ChannelCount); *((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64; *((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64; @@ -116,7 +116,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelayQuadraphonic(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { - const ushort channelCount = 4; + const ushort ChannelCount = 4; float delayFeedbackBaseGain = state.DelayFeedbackBaseGain; float delayFeedbackCrossGain = state.DelayFeedbackCrossGain; @@ -150,7 +150,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Vector4 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; - state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount); + state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), ChannelCount); *((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64; *((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64; @@ -162,7 +162,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void ProcessDelaySurround(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { - const ushort channelCount = 6; + const ushort ChannelCount = 6; float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision); float delayFeedbackBaseGain = state.DelayFeedbackBaseGain; @@ -202,7 +202,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Vector6 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; - state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount); + state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), ChannelCount); *((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64; *((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64; @@ -277,4 +277,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ProcessDelay(context, ref state); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs index 27bb34bf3..19afc66f4 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command int channelCount = (int)device.GetChannelCount(); uint bufferCount = Math.Min(device.GetChannelCount(), InputCount); - const int sampleCount = Constants.TargetSampleCount; + const int SampleCount = Constants.TargetSampleCount; uint inputCount; @@ -79,13 +79,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command inputCount = bufferCount; } - short[] outputBuffer = new short[inputCount * sampleCount]; + short[] outputBuffer = new short[inputCount * SampleCount]; for (int i = 0; i < bufferCount; i++) { - ReadOnlySpan<float> inputBuffer = GetBuffer(InputBufferIndices[i], sampleCount); + ReadOnlySpan<float> inputBuffer = GetBuffer(InputBufferIndices[i], SampleCount); - for (int j = 0; j < sampleCount; j++) + for (int j = 0; j < SampleCount; j++) { outputBuffer[i + j * channelCount] = PcmHelper.Saturate(inputBuffer[j]); } @@ -100,4 +100,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs index 74b53b24b..d1177e60f 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs @@ -96,7 +96,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command private unsafe void ProcessReverb3dGeneric(ref Reverb3dState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount, ReadOnlySpan<int> outputEarlyIndicesTable, ReadOnlySpan<int> targetEarlyDelayLineIndicesTable, ReadOnlySpan<int> targetOutputFeedbackIndicesTable) { - const int delayLineSampleIndexOffset = 1; + const int DelayLineSampleIndexOffset = 1; bool isMono = Parameter.ChannelCount == 1; bool isSurround = Parameter.ChannelCount == 6; @@ -111,14 +111,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { outputValues.Fill(0); - float tapOut = state.PreDelayLine.TapUnsafe(state.ReflectionDelayTime, delayLineSampleIndexOffset); + float tapOut = state.PreDelayLine.TapUnsafe(state.ReflectionDelayTime, DelayLineSampleIndexOffset); for (int i = 0; i < targetEarlyDelayLineIndicesTable.Length; i++) { int earlyDelayIndex = targetEarlyDelayLineIndicesTable[i]; int outputIndex = outputEarlyIndicesTable[earlyDelayIndex]; - float tempTapOut = state.PreDelayLine.TapUnsafe(state.EarlyDelayTime[earlyDelayIndex], delayLineSampleIndexOffset); + float tempTapOut = state.PreDelayLine.TapUnsafe(state.EarlyDelayTime[earlyDelayIndex], DelayLineSampleIndexOffset); outputValues[outputIndex] += tempTapOut * state.EarlyGain[earlyDelayIndex]; } @@ -251,4 +251,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ProcessReverb3d(context, ref state); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs index 721830c9a..12e0f13ff 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs @@ -44,9 +44,9 @@ namespace Ryujinx.Audio.Renderer.Dsp public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span<float> outputBuffer, ref WaveBufferInformation info, Span<WaveBuffer> wavebuffers, ref VoiceUpdateState voiceState, uint targetSampleRate, int sampleCount) { - const int tempBufferSize = 0x3F00; + const int TempBufferSize = 0x3F00; - Span<short> tempBuffer = stackalloc short[tempBufferSize]; + Span<short> tempBuffer = stackalloc short[TempBufferSize]; float sampleRateRatio = (float)info.SourceSampleRate / targetSampleRate * info.Pitch; @@ -60,11 +60,11 @@ namespace Ryujinx.Audio.Renderer.Dsp int totalNeededSize = (int)MathF.Truncate(fraction + sampleRateRatio * sampleCount); - if (totalNeededSize + pitchMaxLength <= tempBufferSize && totalNeededSize >= 0) + if (totalNeededSize + pitchMaxLength <= TempBufferSize && totalNeededSize >= 0) { int sourceSampleCountToProcess = sampleCount; - int maxSampleCountPerIteration = Math.Min((int)MathF.Truncate((tempBufferSize - fraction) / sampleRateRatio), sampleCount); + int maxSampleCountPerIteration = Math.Min((int)MathF.Truncate((TempBufferSize - fraction) / sampleRateRatio), sampleCount); bool isStarving = false; @@ -463,4 +463,4 @@ namespace Ryujinx.Audio.Renderer.Dsp } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs index 6cdab5a7b..54a63ace0 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs @@ -32,13 +32,13 @@ namespace Ryujinx.Audio.Renderer.Dsp float BlackmanWindow(float x) { - const float a = 0.18f; - const float a0 = 0.5f - 0.5f * a; - const float a1 = -0.5f; - const float a2 = 0.5f * a; - return a0 + a1 * MathF.Cos(2 * MathF.PI * x) + a2 * MathF.Cos(4 * MathF.PI * x); + const float A = 0.18f; + const float A0 = 0.5f - 0.5f * A; + const float A1 = -0.5f; + const float A2 = 0.5f * A; + return A0 + A1 * MathF.Cos(2 * MathF.PI * x) + A2 * MathF.Cos(4 * MathF.PI * x); } - + Array20<float> result = new Array20<float>(); for (int i = 0; i < FilterBankLength; i++) @@ -112,7 +112,7 @@ namespace Ryujinx.Audio.Renderer.Dsp int inputBufferIndex = 0; switch (state.Scale) - { + { case 6.0f: for (int i = 0; i < outputSampleCount; i++) { @@ -189,4 +189,4 @@ namespace Ryujinx.Audio.Renderer.Dsp } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs index 7ee491cd6..d4f28a07d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs @@ -60,7 +60,7 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(MixRampGroupedCommand command) { - const float costPerSample = 7.245f; + const float CostPerSample = 7.245f; Debug.Assert(_sampleCount == 160 || _sampleCount == 240); @@ -74,7 +74,7 @@ namespace Ryujinx.Audio.Renderer.Server } } - return (uint)(_sampleCount * costPerSample * volumeCount); + return (uint)(_sampleCount * CostPerSample * volumeCount); } public uint Estimate(MixRampCommand command) @@ -549,4 +549,4 @@ namespace Ryujinx.Audio.Renderer.Server return 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs index 6c79da157..4a29ead3e 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs @@ -256,19 +256,19 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool MemoryPoolUserState outputState; - const uint pageSize = 0x1000; + const uint PageSize = 0x1000; if (inputState != MemoryPoolUserState.RequestAttach && inputState != MemoryPoolUserState.RequestDetach) { return UpdateResult.Success; } - if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % pageSize) != 0) + if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % PageSize) != 0) { return UpdateResult.InvalidParameter; } - if (inParameter.Size == 0 || (inParameter.Size % pageSize) != 0) + if (inParameter.Size == 0 || (inParameter.Size % PageSize) != 0) { return UpdateResult.InvalidParameter; } @@ -363,4 +363,4 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index 73c53e7f6..fba24d68b 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -10,8 +10,8 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Configuration; -using Ryujinx.Ui.App.Common; using Ryujinx.HLE.HOS; +using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; diff --git a/src/Ryujinx.Ava/UI/Models/SaveModel.cs b/src/Ryujinx.Ava/UI/Models/SaveModel.cs index ab919c88d..e8486459b 100644 --- a/src/Ryujinx.Ava/UI/Models/SaveModel.cs +++ b/src/Ryujinx.Ava/UI/Models/SaveModel.cs @@ -40,9 +40,9 @@ namespace Ryujinx.Ava.UI.Models private string GetSizeString() { - const int scale = 1024; + const int Scale = 1024; string[] orders = { "GiB", "MiB", "KiB" }; - long max = (long)Math.Pow(scale, orders.Length); + long max = (long)Math.Pow(Scale, orders.Length); foreach (string order in orders) { @@ -51,7 +51,7 @@ namespace Ryujinx.Ava.UI.Models return $"{decimal.Divide(Size, max):##.##} {order}"; } - max /= scale; + max /= Scale; } return "0 KiB"; @@ -109,4 +109,4 @@ namespace Ryujinx.Ava.UI.Models } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 409ccad3c..0976e2fae 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -622,7 +622,7 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); } } - + public double WindowWidth { get => _windowWidth; @@ -1124,13 +1124,13 @@ namespace Ryujinx.Ava.UI.ViewModels var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>(); - const float colorMultiple = 0.5f; + const float ColorMultiple = 0.5f; Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B); Color progressBgColor = Color.FromRgb( - (byte)(dominantColor.R * colorMultiple), - (byte)(dominantColor.G * colorMultiple), - (byte)(dominantColor.B * colorMultiple)); + (byte)(dominantColor.R * ColorMultiple), + (byte)(dominantColor.G * ColorMultiple), + (byte)(dominantColor.B * ColorMultiple)); ProgressBarForegroundColor = new SolidColorBrush(progressFgColor); ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor); @@ -1677,4 +1677,4 @@ namespace Ryujinx.Ava.UI.ViewModels #endregion } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index d260b6fc7..0a2ffae3b 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -18,14 +18,14 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; +using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Runtime.InteropServices; using System.Net.NetworkInformation; +using System.Runtime.InteropServices; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; -using Silk.NET.Vulkan; namespace Ryujinx.Ava.UI.ViewModels { diff --git a/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs b/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs index 06324a54c..168960f71 100644 --- a/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs +++ b/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs @@ -44,10 +44,10 @@ namespace Ryujinx.Common.SystemInfo return 0; } - const int flavor = 4; // HOST_VM_INFO64 + const int Flavor = 4; // HOST_VM_INFO64 uint count = (uint)(Marshal.SizeOf<VMStatistics64>() / sizeof(int)); // HOST_VM_INFO64_COUNT VMStatistics64 stats = new(); - result = host_statistics64(port, flavor, ref stats, ref count); + result = host_statistics64(port, Flavor, ref stats, ref count); if (result != 0) { @@ -154,4 +154,4 @@ namespace Ryujinx.Common.SystemInfo [LibraryImport(SystemLibraryName, SetLastError = true)] private static partial int host_statistics64(uint host_priv, int host_flavor, ref VMStatistics64 host_info64_out, ref uint host_info64_outCnt); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs b/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs index b1ed7b689..4802178e2 100644 --- a/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs +++ b/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs @@ -29,10 +29,10 @@ namespace Ryujinx.Common.SystemInterop [SupportedOSPlatform("macos")] private void RegisterPosix() { - const int stdErrFileno = 2; + const int StdErrFileno = 2; (int readFd, int writeFd) = MakePipe(); - dup2(writeFd, stdErrFileno); + dup2(writeFd, StdErrFileno); _pipeReader = CreateFileDescriptorStream(readFd); _pipeWriter = CreateFileDescriptorStream(writeFd); diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index 364488aae..cb95b04a8 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -6,8 +6,8 @@ using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Graphics.Gpu.Memory diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index 521a132a7..1b55909d3 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -4,8 +4,8 @@ using Silk.NET.Vulkan; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using VkFormat = Silk.NET.Vulkan.Format; using VkBuffer = Silk.NET.Vulkan.Buffer; +using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan { diff --git a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 1b3968eab..e21c28145 100644 --- a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -354,8 +354,8 @@ namespace Ryujinx.HLE.FileSystem if (info.SpaceId != SaveDataSpaceId.User && info.SpaceId != SaveDataSpaceId.System) return Result.Success; - const string mountName = "SaveDir"; - var mountNameU8 = mountName.ToU8Span(); + const string MountName = "SaveDir"; + var mountNameU8 = MountName.ToU8Span(); BisPartitionId partitionId = info.SpaceId switch { @@ -368,7 +368,7 @@ namespace Ryujinx.HLE.FileSystem if (rc.IsFailure()) return rc; try { - var path = $"{mountName}:/save/{info.SaveDataId:x16}".ToU8Span(); + var path = $"{MountName}:/save/{info.SaveDataId:x16}".ToU8Span(); rc = hos.Fs.GetEntryType(out _, path); @@ -612,4 +612,4 @@ namespace Ryujinx.HLE.FileSystem } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 166761c2c..2f163fa22 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -332,13 +332,13 @@ namespace Ryujinx.HLE.HOS foreach (var service in services) { - const ProcessCreationFlags flags = + const ProcessCreationFlags Flags = ProcessCreationFlags.EnableAslr | ProcessCreationFlags.AddressSpace64Bit | ProcessCreationFlags.Is64Bit | ProcessCreationFlags.PoolPartitionSystem; - ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); + ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); uint[] defaultCapabilities = new uint[] { @@ -554,4 +554,4 @@ namespace Ryujinx.HLE.HOS IsPaused = pause; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs index b1466c787..e4755f787 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs @@ -83,18 +83,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ControllerKeys UpdateStickButtons(JoystickPosition leftStick, JoystickPosition rightStick) { - const int stickButtonThreshold = short.MaxValue / 2; + const int StickButtonThreshold = short.MaxValue / 2; ControllerKeys result = 0; - result |= (leftStick.Dx < -stickButtonThreshold) ? ControllerKeys.LStickLeft : result; - result |= (leftStick.Dx > stickButtonThreshold) ? ControllerKeys.LStickRight : result; - result |= (leftStick.Dy < -stickButtonThreshold) ? ControllerKeys.LStickDown : result; - result |= (leftStick.Dy > stickButtonThreshold) ? ControllerKeys.LStickUp : result; + result |= (leftStick.Dx < -StickButtonThreshold) ? ControllerKeys.LStickLeft : result; + result |= (leftStick.Dx > StickButtonThreshold) ? ControllerKeys.LStickRight : result; + result |= (leftStick.Dy < -StickButtonThreshold) ? ControllerKeys.LStickDown : result; + result |= (leftStick.Dy > StickButtonThreshold) ? ControllerKeys.LStickUp : result; - result |= (rightStick.Dx < -stickButtonThreshold) ? ControllerKeys.RStickLeft : result; - result |= (rightStick.Dx > stickButtonThreshold) ? ControllerKeys.RStickRight : result; - result |= (rightStick.Dy < -stickButtonThreshold) ? ControllerKeys.RStickDown : result; - result |= (rightStick.Dy > stickButtonThreshold) ? ControllerKeys.RStickUp : result; + result |= (rightStick.Dx < -StickButtonThreshold) ? ControllerKeys.RStickLeft : result; + result |= (rightStick.Dx > StickButtonThreshold) ? ControllerKeys.RStickRight : result; + result |= (rightStick.Dy < -StickButtonThreshold) ? ControllerKeys.RStickDown : result; + result |= (rightStick.Dy > StickButtonThreshold) ? ControllerKeys.RStickUp : result; return result; } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs index b02bbfd18..b8dbce155 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii { public static ushort CalculateCrc16(ReadOnlySpan<byte> data, int crc, bool reverseEndianess) { - const ushort poly = 0x1021; + const ushort Poly = 0x1021; for (int i = 0; i < data.Length; i++) { @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii if ((crc & 0x10000) != 0) { - crc = (crc ^ poly) & 0xFFFF; + crc = (crc ^ Poly) & 0xFFFF; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs index fef82cbc4..c0556c316 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs @@ -134,9 +134,9 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl private void WriteMagicAndSize(ulong offset, int size) { - const int key = 0x49621806; + const int Key = 0x49621806; - int encryptedSize = BinaryPrimitives.ReverseEndianness(size ^ key); + int encryptedSize = BinaryPrimitives.ReverseEndianness(size ^ Key); _storage.GetRef<int>(offset + 0) = (int)BFTTFMagic; _storage.GetRef<int>(offset + 4) = encryptedSize; @@ -180,4 +180,4 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index ff6df8a38..f8ce465f9 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -70,13 +70,13 @@ namespace Ryujinx.HLE.HOS.Services Name = name; SmObjectFactory = smObjectFactory; - const ProcessCreationFlags flags = + const ProcessCreationFlags Flags = ProcessCreationFlags.EnableAslr | ProcessCreationFlags.AddressSpace64Bit | ProcessCreationFlags.Is64Bit | ProcessCreationFlags.PoolPartitionSystem; - ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); + ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main); } diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index ef95fa5cb..07c9f6b3b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -43,43 +43,43 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - const byte majorFwVersion = 0x03; - const byte minorFwVersion = 0x00; - const byte microFwVersion = 0x00; - const byte unknown = 0x00; //Build? + const byte MajorFwVersion = 0x03; + const byte MinorFwVersion = 0x00; + const byte MicroFwVersion = 0x00; + const byte Unknown = 0x00; //Build? - const int revisionNumber = 0x0A; + const int RevisionNumber = 0x0A; - const string platform = "NX"; - const string unknownHex = "7fbde2b0bba4d14107bf836e4643043d9f6c8e47"; - const string version = "3.0.0"; - const string build = "NintendoSDK Firmware for NX 3.0.0-10.0"; + const string Platform = "NX"; + const string UnknownHex = "7fbde2b0bba4d14107bf836e4643043d9f6c8e47"; + const string Version = "3.0.0"; + const string Build = "NintendoSDK Firmware for NX 3.0.0-10.0"; // http://switchbrew.org/index.php?title=System_Version_Title using (MemoryStream ms = new MemoryStream(0x100)) { BinaryWriter writer = new BinaryWriter(ms); - writer.Write(majorFwVersion); - writer.Write(minorFwVersion); - writer.Write(microFwVersion); - writer.Write(unknown); + writer.Write(MajorFwVersion); + writer.Write(MinorFwVersion); + writer.Write(MicroFwVersion); + writer.Write(Unknown); - writer.Write(revisionNumber); + writer.Write(RevisionNumber); - writer.Write(Encoding.ASCII.GetBytes(platform)); + writer.Write(Encoding.ASCII.GetBytes(Platform)); ms.Seek(0x28, SeekOrigin.Begin); - writer.Write(Encoding.ASCII.GetBytes(unknownHex)); + writer.Write(Encoding.ASCII.GetBytes(UnknownHex)); ms.Seek(0x68, SeekOrigin.Begin); - writer.Write(Encoding.ASCII.GetBytes(version)); + writer.Write(Encoding.ASCII.GetBytes(Version)); ms.Seek(0x80, SeekOrigin.Begin); - writer.Write(Encoding.ASCII.GetBytes(build)); + writer.Write(Encoding.ASCII.GetBytes(Build)); context.Memory.Write(replyPos, ms.ToArray()); } diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 52ed52220..89eed5b57 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -354,16 +354,16 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService private ulong GetA8B8G8R8LayerSize(int width, int height, out int pitch, out int alignment) { - const int defaultAlignment = 0x1000; - const ulong defaultSize = 0x20000; + const int DefaultAlignment = 0x1000; + const ulong DefaultSize = 0x20000; - alignment = defaultAlignment; + alignment = DefaultAlignment; pitch = BitUtils.AlignUp(BitUtils.DivRoundUp(width * 32, 8), 64); int memorySize = pitch * BitUtils.AlignUp(height, 64); ulong requiredMemorySize = (ulong)BitUtils.AlignUp(memorySize, alignment); - return (requiredMemorySize + defaultSize - 1) / defaultSize * defaultSize; + return (requiredMemorySize + DefaultSize - 1) / DefaultSize * DefaultSize; } [CommandCmif(2450)] @@ -484,4 +484,4 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs b/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs index e85d99c76..a31c055fa 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs @@ -96,14 +96,14 @@ namespace Ryujinx.HLE.HOS.Tamper public static byte[] ParseRawInstruction(string rawInstruction) { - const int wordSize = 2 * sizeof(uint); + const int WordSize = 2 * sizeof(uint); // Instructions are multi-word, with 32bit words. Split the raw instruction // and parse each word into individual nybbles of bits. var words = rawInstruction.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); - byte[] instruction = new byte[wordSize * words.Length]; + byte[] instruction = new byte[WordSize * words.Length]; if (words.Length == 0) { @@ -114,14 +114,14 @@ namespace Ryujinx.HLE.HOS.Tamper { string word = words[wordIndex]; - if (word.Length != wordSize) + if (word.Length != WordSize) { throw new TamperCompilationException($"Invalid word length for {word} in Atmosphere cheat"); } - for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++) + for (int nybbleIndex = 0; nybbleIndex < WordSize; nybbleIndex++) { - int index = wordIndex * wordSize + nybbleIndex; + int index = wordIndex * WordSize + nybbleIndex; instruction[index] = byte.Parse(word.AsSpan(nybbleIndex, 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture); } diff --git a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs index dffbe74d8..a97571293 100644 --- a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs @@ -100,14 +100,14 @@ namespace Ryujinx.Tests.Memory [Test] public void DirtyRegionOrdering([Values] bool smart) { - const int pageCount = 32; - IMultiRegionHandle handle = GetGranular(smart, 0, PageSize * pageCount, PageSize); + const int PageCount = 32; + IMultiRegionHandle handle = GetGranular(smart, 0, PageSize * PageCount, PageSize); Random random = new(); - PreparePages(handle, pageCount); + PreparePages(handle, PageCount); - IEnumerable<int> halfRange = Enumerable.Range(0, pageCount / 2); + IEnumerable<int> halfRange = Enumerable.Range(0, PageCount / 2); List<int> odd = halfRange.Select(x => x * 2 + 1).ToList(); List<int> even = halfRange.Select(x => x * 2).ToList(); @@ -117,9 +117,9 @@ namespace Ryujinx.Tests.Memory _tracking.VirtualMemoryEvent((ulong)i * PageSize, PageSize, true); }); - int oddRegionCount = ExpectQueryInOrder(handle, 0, PageSize * pageCount, (address) => (address / PageSize) % 2 == 1); + int oddRegionCount = ExpectQueryInOrder(handle, 0, PageSize * PageCount, (address) => (address / PageSize) % 2 == 1); - Assert.AreEqual(oddRegionCount, pageCount / 2); // Must have written to all odd pages. + Assert.AreEqual(oddRegionCount, PageCount / 2); // Must have written to all odd pages. // Write to all the even pages. RandomOrder(random, even, (i) => @@ -127,9 +127,9 @@ namespace Ryujinx.Tests.Memory _tracking.VirtualMemoryEvent((ulong)i * PageSize, PageSize, true); }); - int evenRegionCount = ExpectQueryInOrder(handle, 0, PageSize * pageCount, (address) => (address / PageSize) % 2 == 0); + int evenRegionCount = ExpectQueryInOrder(handle, 0, PageSize * PageCount, (address) => (address / PageSize) % 2 == 0); - Assert.AreEqual(evenRegionCount, pageCount / 2); + Assert.AreEqual(evenRegionCount, PageCount / 2); } [Test] @@ -142,14 +142,14 @@ namespace Ryujinx.Tests.Memory // This is useful for situations where we know that the data was complete when the sequence number was set. // ...essentially, when that data can only be updated on a future sequence number. - const int pageCount = 32; - IMultiRegionHandle handle = GetGranular(smart, 0, PageSize * pageCount, PageSize); + const int PageCount = 32; + IMultiRegionHandle handle = GetGranular(smart, 0, PageSize * PageCount, PageSize); - PreparePages(handle, pageCount); + PreparePages(handle, PageCount); Random random = new(); - IEnumerable<int> halfRange = Enumerable.Range(0, pageCount / 2); + IEnumerable<int> halfRange = Enumerable.Range(0, PageCount / 2); List<int> odd = halfRange.Select(x => x * 2 + 1).ToList(); List<int> even = halfRange.Select(x => x * 2).ToList(); @@ -172,29 +172,29 @@ namespace Ryujinx.Tests.Memory }, 1); } - Assert.AreEqual(oddRegionCount, pageCount / 2); // Must have written to all odd pages. + Assert.AreEqual(oddRegionCount, PageCount / 2); // Must have written to all odd pages. // Write to all pages. - _tracking.VirtualMemoryEvent(0, PageSize * pageCount, true); + _tracking.VirtualMemoryEvent(0, PageSize * PageCount, true); // Only the even regions should be reported for sequence number 1. - int evenRegionCount = ExpectQueryInOrder(handle, 0, PageSize * pageCount, (address) => (address / PageSize) % 2 == 0, 1); + int evenRegionCount = ExpectQueryInOrder(handle, 0, PageSize * PageCount, (address) => (address / PageSize) % 2 == 0, 1); - Assert.AreEqual(evenRegionCount, pageCount / 2); // Must have written to all even pages. + Assert.AreEqual(evenRegionCount, PageCount / 2); // Must have written to all even pages. oddRegionCount = 0; - handle.QueryModified(0, PageSize * pageCount, (address, range) => { oddRegionCount++; }, 1); + handle.QueryModified(0, PageSize * PageCount, (address, range) => { oddRegionCount++; }, 1); Assert.AreEqual(oddRegionCount, 0); // Sequence number has not changed, so found no dirty subregions. // With sequence number 2, all all pages should be reported as modified. - oddRegionCount = ExpectQueryInOrder(handle, 0, PageSize * pageCount, (address) => (address / PageSize) % 2 == 1, 2); + oddRegionCount = ExpectQueryInOrder(handle, 0, PageSize * PageCount, (address) => (address / PageSize) % 2 == 1, 2); - Assert.AreEqual(oddRegionCount, pageCount / 2); // Must have written to all odd pages. + Assert.AreEqual(oddRegionCount, PageCount / 2); // Must have written to all odd pages. } [Test] @@ -203,8 +203,8 @@ namespace Ryujinx.Tests.Memory // Smart multi region handles dynamically change their tracking granularity based on QueryMemory calls. // This can save on reprotects on larger resources. - const int pageCount = 32; - IMultiRegionHandle handle = GetGranular(true, 0, PageSize * pageCount, PageSize); + const int PageCount = 32; + IMultiRegionHandle handle = GetGranular(true, 0, PageSize * PageCount, PageSize); // Query some large regions to prep the subdivision of the tracking region. @@ -253,27 +253,27 @@ namespace Ryujinx.Tests.Memory public void DisposeMultiHandles([Values] bool smart) { // Create and initialize two overlapping Multi Region Handles, with PageSize granularity. - const int pageCount = 32; - const int overlapStart = 16; + const int PageCount = 32; + const int OverlapStart = 16; Assert.AreEqual(0, _tracking.GetRegionCount()); - IMultiRegionHandle handleLow = GetGranular(smart, 0, PageSize * pageCount, PageSize); - PreparePages(handleLow, pageCount); + IMultiRegionHandle handleLow = GetGranular(smart, 0, PageSize * PageCount, PageSize); + PreparePages(handleLow, PageCount); - Assert.AreEqual(pageCount, _tracking.GetRegionCount()); + Assert.AreEqual(PageCount, _tracking.GetRegionCount()); - IMultiRegionHandle handleHigh = GetGranular(smart, PageSize * overlapStart, PageSize * pageCount, PageSize); - PreparePages(handleHigh, pageCount, PageSize * overlapStart); + IMultiRegionHandle handleHigh = GetGranular(smart, PageSize * OverlapStart, PageSize * PageCount, PageSize); + PreparePages(handleHigh, PageCount, PageSize * OverlapStart); // Combined pages (and assuming overlapStart <= pageCount) should be pageCount after overlapStart. - int totalPages = overlapStart + pageCount; + int totalPages = OverlapStart + PageCount; Assert.AreEqual(totalPages, _tracking.GetRegionCount()); handleLow.Dispose(); // After disposing one, the pages for the other remain. - Assert.AreEqual(pageCount, _tracking.GetRegionCount()); + Assert.AreEqual(PageCount, _tracking.GetRegionCount()); handleHigh.Dispose(); // After disposing the other, there are no pages left. diff --git a/src/Ryujinx.Tests.Memory/TrackingTests.cs b/src/Ryujinx.Tests.Memory/TrackingTests.cs index dce73cf4f..035f0c228 100644 --- a/src/Ryujinx.Tests.Memory/TrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/TrackingTests.cs @@ -201,11 +201,11 @@ namespace Ryujinx.Tests.Memory // This test should not throw or deadlock due to invalid state. - const int threadCount = 1; - const int handlesPerThread = 16; + const int ThreadCount = 1; + const int HandlesPerThread = 16; long finishedTime = 0; - RegionHandle[] handles = new RegionHandle[threadCount * handlesPerThread]; + RegionHandle[] handles = new RegionHandle[ThreadCount * HandlesPerThread]; Random globalRand = new(); for (int i = 0; i < handles.Length; i++) @@ -218,16 +218,16 @@ namespace Ryujinx.Tests.Memory // Dirty flag consumer threads int dirtyFlagReprotects = 0; - for (int i = 0; i < threadCount; i++) + for (int i = 0; i < ThreadCount; i++) { int randSeed = i; testThreads.Add(new Thread(() => { - int handleBase = randSeed * handlesPerThread; + int handleBase = randSeed * HandlesPerThread; while (Stopwatch.GetTimestamp() < finishedTime) { Random random = new(randSeed); - RegionHandle handle = handles[handleBase + random.Next(handlesPerThread)]; + RegionHandle handle = handles[handleBase + random.Next(HandlesPerThread)]; if (handle.Dirty) { @@ -240,16 +240,16 @@ namespace Ryujinx.Tests.Memory // Write trigger threads int writeTriggers = 0; - for (int i = 0; i < threadCount; i++) + for (int i = 0; i < ThreadCount; i++) { int randSeed = i; testThreads.Add(new Thread(() => { Random random = new(randSeed); - ulong handleBase = (ulong)(randSeed * handlesPerThread * PageSize); + ulong handleBase = (ulong)(randSeed * HandlesPerThread * PageSize); while (Stopwatch.GetTimestamp() < finishedTime) { - _tracking.VirtualMemoryEvent(handleBase + (ulong)random.Next(PageSize * handlesPerThread), PageSize / 2, true); + _tracking.VirtualMemoryEvent(handleBase + (ulong)random.Next(PageSize * HandlesPerThread), PageSize / 2, true); Interlocked.Increment(ref writeTriggers); } })); @@ -257,12 +257,12 @@ namespace Ryujinx.Tests.Memory // Handle create/delete threads int handleLifecycles = 0; - for (int i = 0; i < threadCount; i++) + for (int i = 0; i < ThreadCount; i++) { int randSeed = i; testThreads.Add(new Thread(() => { - int maxAddress = threadCount * handlesPerThread * PageSize; + int maxAddress = ThreadCount * HandlesPerThread * PageSize; Random random = new(randSeed + 512); while (Stopwatch.GetTimestamp() < finishedTime) { @@ -315,17 +315,17 @@ namespace Ryujinx.Tests.Memory }); } - const int threadCount = 16; - const int iterationCount = 10000; - Thread[] signalThreads = new Thread[threadCount]; + const int ThreadCount = 16; + const int IterationCount = 10000; + Thread[] signalThreads = new Thread[ThreadCount]; - for (int i = 0; i < threadCount; i++) + for (int i = 0; i < ThreadCount; i++) { int randSeed = i; signalThreads[i] = new Thread(() => { Random random = new(randSeed); - for (int j = 0; j < iterationCount; j++) + for (int j = 0; j < IterationCount; j++) { _tracking.VirtualMemoryEvent((ulong)random.Next(PageSize), 4, false); } @@ -333,14 +333,14 @@ namespace Ryujinx.Tests.Memory }); } - for (int i = 0; i < threadCount; i++) + for (int i = 0; i < ThreadCount; i++) { signalThreads[i].Start(); } while (signalThreadsDone != -1) { - if (signalThreadsDone == threadCount) + if (signalThreadsDone == ThreadCount) { signalThreadsDone = -1; } diff --git a/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs index b643a1021..c86d3996e 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -359,7 +359,7 @@ namespace Ryujinx.Tests.Cpu [Test] public void MiscR() { - const ulong result = 5; + const ulong Result = 5; /* 0x0000000000001000: MOV X0, #2 @@ -374,7 +374,7 @@ namespace Ryujinx.Tests.Cpu Opcode(0xD65F03C0); ExecuteOpcodes(); - Assert.That(GetContext().GetX(0), Is.EqualTo(result)); + Assert.That(GetContext().GetX(0), Is.EqualTo(Result)); Reset(); @@ -391,7 +391,7 @@ namespace Ryujinx.Tests.Cpu Opcode(0xD65F03C0); ExecuteOpcodes(); - Assert.That(GetContext().GetX(0), Is.EqualTo(result)); + Assert.That(GetContext().GetX(0), Is.EqualTo(Result)); } [Explicit] @@ -479,4 +479,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs index e1e81a000..d5ac3c27b 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs @@ -110,9 +110,9 @@ namespace Ryujinx.Tests.Cpu public void Dup_S_B([ValueSource(nameof(_8B_))] ulong a, [Values(0u, 15u)] uint index) { - const int size = 0; + const int TestSize = 0; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x5E000420; // RESERVED opcode |= (imm5 << 16); @@ -130,9 +130,9 @@ namespace Ryujinx.Tests.Cpu public void Dup_S_H([ValueSource(nameof(_4H_))] ulong a, [Values(0u, 7u)] uint index) { - const int size = 1; + const int TestSize = 1; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x5E000420; // RESERVED opcode |= (imm5 << 16); @@ -150,9 +150,9 @@ namespace Ryujinx.Tests.Cpu public void Dup_S_S([ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { - const int size = 2; + const int TestSize = 2; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x5E000420; // RESERVED opcode |= (imm5 << 16); @@ -170,9 +170,9 @@ namespace Ryujinx.Tests.Cpu public void Dup_S_D([ValueSource(nameof(_1D_))] ulong a, [Values(0u, 1u)] uint index) { - const int size = 3; + const int TestSize = 3; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x5E000420; // RESERVED opcode |= (imm5 << 16); @@ -194,9 +194,9 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 15u)] uint index, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { - const int size = 0; + const int TestSize = 0; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -219,9 +219,9 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 7u)] uint index, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - const int size = 1; + const int TestSize = 1; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -244,9 +244,9 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 1u, 2u, 3u)] uint index, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - const int size = 2; + const int TestSize = 2; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -269,9 +269,9 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 1u)] uint index, [Values(0b1u)] uint q) // <2D> { - const int size = 3; + const int TestSize = 3; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -293,9 +293,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_W_))] uint wn, [Values(0u, 15u)] uint index) { - const int size = 0; + const int TestSize = 0; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E001C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -316,9 +316,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_W_))] uint wn, [Values(0u, 7u)] uint index) { - const int size = 1; + const int TestSize = 1; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E001C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -339,9 +339,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_W_))] uint wn, [Values(0u, 1u, 2u, 3u)] uint index) { - const int size = 2; + const int TestSize = 2; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E001C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -362,9 +362,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_X_))] ulong xn, [Values(0u, 1u)] uint index) { - const int size = 3; + const int TestSize = 3; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E001C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -386,10 +386,10 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 15u)] uint dstIndex, [Values(0u, 15u)] uint srcIndex) { - const int size = 0; + const int TestSize = 0; - uint imm5 = (dstIndex << (size + 1) | 1u << size) & 0x1Fu; - uint imm4 = (srcIndex << size) & 0xFu; + uint imm5 = (dstIndex << (TestSize + 1) | 1u << TestSize) & 0x1Fu; + uint imm4 = (srcIndex << TestSize) & 0xFu; uint opcode = 0x6E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -412,10 +412,10 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 7u)] uint dstIndex, [Values(0u, 7u)] uint srcIndex) { - const int size = 1; + const int TestSize = 1; - uint imm5 = (dstIndex << (size + 1) | 1u << size) & 0x1Fu; - uint imm4 = (srcIndex << size) & 0xFu; + uint imm5 = (dstIndex << (TestSize + 1) | 1u << TestSize) & 0x1Fu; + uint imm4 = (srcIndex << TestSize) & 0xFu; uint opcode = 0x6E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -438,10 +438,10 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 1u, 2u, 3u)] uint dstIndex, [Values(0u, 1u, 2u, 3u)] uint srcIndex) { - const int size = 2; + const int TestSize = 2; - uint imm5 = (dstIndex << (size + 1) | 1u << size) & 0x1Fu; - uint imm4 = (srcIndex << size) & 0xFu; + uint imm5 = (dstIndex << (TestSize + 1) | 1u << TestSize) & 0x1Fu; + uint imm4 = (srcIndex << TestSize) & 0xFu; uint opcode = 0x6E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -464,10 +464,10 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 1u)] uint dstIndex, [Values(0u, 1u)] uint srcIndex) { - const int size = 3; + const int TestSize = 3; - uint imm5 = (dstIndex << (size + 1) | 1u << size) & 0x1Fu; - uint imm4 = (srcIndex << size) & 0xFu; + uint imm5 = (dstIndex << (TestSize + 1) | 1u << TestSize) & 0x1Fu; + uint imm4 = (srcIndex << TestSize) & 0xFu; uint opcode = 0x6E000400; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -488,9 +488,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_8B_))] ulong a, [Values(0u, 15u)] uint index) { - const int size = 0; + const int TestSize = 0; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E002C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -511,9 +511,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_4H_))] ulong a, [Values(0u, 7u)] uint index) { - const int size = 1; + const int TestSize = 1; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E002C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -534,9 +534,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_8B_))] ulong a, [Values(0u, 15u)] uint index) { - const int size = 0; + const int TestSize = 0; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E002C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -556,9 +556,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_4H_))] ulong a, [Values(0u, 7u)] uint index) { - const int size = 1; + const int TestSize = 1; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E002C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -578,9 +578,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { - const int size = 2; + const int TestSize = 2; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E002C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -600,9 +600,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_8B_))] ulong a, [Values(0u, 15u)] uint index) { - const int size = 0; + const int TestSize = 0; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E003C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -623,9 +623,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_4H_))] ulong a, [Values(0u, 7u)] uint index) { - const int size = 1; + const int TestSize = 1; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E003C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -646,9 +646,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { - const int size = 2; + const int TestSize = 2; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x0E003C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -669,9 +669,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_1D_))] ulong a, [Values(0u, 1u)] uint index) { - const int size = 3; + const int TestSize = 3; - uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + uint imm5 = (index << (TestSize + 1) | 1u << TestSize) & 0x1Fu; uint opcode = 0x4E003C00; // RESERVED opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -686,4 +686,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs index c88c02c1b..50915fd34 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs @@ -1,8 +1,8 @@ #define SimdMemory32 using ARMeilleure.State; -using Ryujinx.Memory; using NUnit.Framework; +using Ryujinx.Memory; using System; namespace Ryujinx.Tests.Cpu diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs index 7bafc195e..830a748ab 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs @@ -14,17 +14,17 @@ namespace Ryujinx.Tests.Cpu #region "Helper methods" private static ulong GenIdxsForTbls(int regs) { - const byte idxInRngMin = 0; + const byte IdxInRngMin = 0; byte idxInRngMax = (byte)((16 * regs) - 1); byte idxOutRngMin = (byte) (16 * regs); - const byte idxOutRngMax = 255; + const byte IdxOutRngMax = 255; ulong idxs = 0ul; for (int cnt = 1; cnt <= 8; cnt++) { - ulong idxInRng = TestContext.CurrentContext.Random.NextByte(idxInRngMin, idxInRngMax); - ulong idxOutRng = TestContext.CurrentContext.Random.NextByte(idxOutRngMin, idxOutRngMax); + ulong idxInRng = TestContext.CurrentContext.Random.NextByte(IdxInRngMin, idxInRngMax); + ulong idxOutRng = TestContext.CurrentContext.Random.NextByte(idxOutRngMin, IdxOutRngMax); ulong idx = TestContext.CurrentContext.Random.NextBool() ? idxInRng : idxOutRng; @@ -314,4 +314,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs index f0aa40be2..1081fcf32 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationData.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationData.cs @@ -1,9 +1,9 @@ using LibHac.Common; -using LibHac.Ns; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; using LibHac.Loader; +using LibHac.Ns; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; @@ -146,9 +146,9 @@ namespace Ryujinx.Ui.App.Common return string.Empty; } - const string mainExeFs = "main"; + const string MainExeFs = "main"; - if (!codeFs.FileExists($"/{mainExeFs}")) + if (!codeFs.FileExists($"/{MainExeFs}")) { Logger.Error?.Print(LogClass.Loader, "No main binary ExeFS found in ExeFS"); @@ -157,12 +157,12 @@ namespace Ryujinx.Ui.App.Common using var nsoFile = new UniqueRef<IFile>(); - codeFs.OpenFile(ref nsoFile.Ref, $"/{mainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + codeFs.OpenFile(ref nsoFile.Ref, $"/{MainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); NsoReader reader = new NsoReader(); reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure(); - + return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index f52af611e..28280bd9f 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -18,9 +18,9 @@ using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; using System; using System.Collections.Generic; -using System.Linq; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 74f6043d4..6279891e6 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -389,12 +389,12 @@ namespace Ryujinx.Ui.Widgets using (destHandle) { - const int maxBufferSize = 1024 * 1024; + const int MaxBufferSize = 1024 * 1024; rc = fs.GetFileSize(out long fileSize, sourceHandle); if (rc.IsFailure()) return rc; - int bufferSize = (int)Math.Min(maxBufferSize, fileSize); + int bufferSize = (int)Math.Min(MaxBufferSize, fileSize); byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize); try From e055217292e034e46ebadd2e839b301b996d7064 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 01:27:48 +0200 Subject: [PATCH 672/737] [Ryujinx.Horizon.Kernel.Generators] Address dotnet-format issues (#5376) * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Run dotnet format pass * Remove left-over files and adjust namespaces * Fix alignment --- .../CodeGenerator.cs | 2 +- .../{Kernel => }/SyscallGenerator.cs | 12 ++++++------ .../{Kernel => }/SyscallSyntaxReceiver.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/Ryujinx.Horizon.Kernel.Generators/{Kernel => }/SyscallGenerator.cs (97%) rename src/Ryujinx.Horizon.Kernel.Generators/{Kernel => }/SyscallSyntaxReceiver.cs (97%) diff --git a/src/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs b/src/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs index 80a33c66c..121b6dd58 100644 --- a/src/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs +++ b/src/Ryujinx.Horizon.Kernel.Generators/CodeGenerator.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Ryujinx.Horizon.Generators +namespace Ryujinx.Horizon.Kernel.Generators { class CodeGenerator { diff --git a/src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs similarity index 97% rename from src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs rename to src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs index 51da21878..75f966f39 100644 --- a/src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallGenerator.cs +++ b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -namespace Ryujinx.Horizon.Generators.Kernel +namespace Ryujinx.Horizon.Kernel.Generators { [Generator] class SyscallGenerator : ISourceGenerator @@ -157,10 +157,10 @@ namespace Ryujinx.Horizon.Generators.Kernel GetCanonicalTypeName(context.Compilation, attribute) == TypeSvcAttribute))) { syscalls.AddRange(from attributeArg in attribute.ArgumentList.Arguments - where attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression - select (LiteralExpressionSyntax)attributeArg.Expression - into numericLiteral - select new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text)); + where attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression + select (LiteralExpressionSyntax)attributeArg.Expression + into numericLiteral + select new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text)); } } @@ -369,7 +369,7 @@ namespace Ryujinx.Horizon.Generators.Kernel { generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){ResultVariableName});"); } - + result = GetFormattedLogValue(ResultVariableName, canonicalReturnTypeName); } else diff --git a/src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs b/src/Ryujinx.Horizon.Kernel.Generators/SyscallSyntaxReceiver.cs similarity index 97% rename from src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs rename to src/Ryujinx.Horizon.Kernel.Generators/SyscallSyntaxReceiver.cs index e480a8593..f586ee682 100644 --- a/src/Ryujinx.Horizon.Kernel.Generators/Kernel/SyscallSyntaxReceiver.cs +++ b/src/Ryujinx.Horizon.Kernel.Generators/SyscallSyntaxReceiver.cs @@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Horizon.Generators.Kernel +namespace Ryujinx.Horizon.Kernel.Generators { class SyscallSyntaxReceiver : ISyntaxReceiver { From 9becbd7d728fc2002c176dfd9d1d1aae86f86b12 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 08:59:13 +0200 Subject: [PATCH 673/737] [Ryujinx.Graphics.Shader] Address dotnet-format issues (#5373) * dotnet format style --severity info Some changes were manually reverted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Silence dotnet format IDE0059 warnings * Address or silence dotnet format CA1069 warnings * Address or silence dotnet format CA2211 warnings * Address review comments * Fix formatting for switch expressions * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Fix naming rule violation, Convert shader properties to auto-property and convert values to const * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Run dotnet format after rebase * Address IDE0251 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Run dotnet format after rebase * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Fix naming rule violations * Add trailing commas * Remove unused members and most unnecessary value assignments * Remove more unnecessary assignments * Remove NRE suppressor --- src/Ryujinx.Graphics.Shader/AlphaTestOp.cs | 4 +- src/Ryujinx.Graphics.Shader/AttributeType.cs | 8 +- .../BufferDescriptor.cs | 2 +- .../BufferUsageFlags.cs | 2 +- .../CodeGen/Glsl/CodeGenContext.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 24 +- .../CodeGen/Glsl/DefaultNames.cs | 4 +- .../CodeGen/Glsl/GlslGenerator.cs | 9 +- .../HelperFunctions/HelperFunctionNames.cs | 10 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 2 +- .../Glsl/Instructions/InstGenBallot.cs | 2 +- .../CodeGen/Glsl/Instructions/InstGenCall.cs | 2 +- .../CodeGen/Glsl/Instructions/InstGenFSI.cs | 2 +- .../Glsl/Instructions/InstGenHelper.cs | 8 +- .../Glsl/Instructions/InstGenMemory.cs | 52 +- .../Glsl/Instructions/InstGenPacking.cs | 2 +- .../Glsl/Instructions/InstGenVector.cs | 2 +- .../CodeGen/Glsl/Instructions/InstInfo.cs | 6 +- .../CodeGen/Glsl/Instructions/InstType.cs | 32 +- .../CodeGen/Glsl/Instructions/IoMap.cs | 6 +- .../CodeGen/Glsl/NumberFormatter.cs | 2 +- .../CodeGen/Glsl/OperandManager.cs | 22 +- .../CodeGen/Glsl/TypeConversion.cs | 28 +- .../CodeGen/Spirv/CodeGenContext.cs | 31 +- .../CodeGen/Spirv/Declarations.cs | 21 +- .../CodeGen/Spirv/EnumConversion.cs | 2 +- .../CodeGen/Spirv/Instructions.cs | 124 +- .../CodeGen/Spirv/IoMap.cs | 12 +- .../CodeGen/Spirv/OperationResult.cs | 2 +- .../CodeGen/Spirv/SpirvGenerator.cs | 38 +- .../CodeGen/Spirv/TextureMeta.cs | 2 +- src/Ryujinx.Graphics.Shader/Constants.cs | 2 +- src/Ryujinx.Graphics.Shader/Decoders/Block.cs | 6 +- .../Decoders/DecodedFunction.cs | 2 +- .../Decoders/DecodedProgram.cs | 2 +- .../Decoders/Decoder.cs | 73 +- .../Decoders/InstDecoders.cs | 1180 +++++++++-------- .../Decoders/InstName.cs | 2 +- .../Decoders/InstOp.cs | 2 +- .../Decoders/InstProps.cs | 4 +- .../Decoders/InstTable.cs | 8 +- .../Decoders/Register.cs | 8 +- .../Decoders/RegisterConsts.cs | 6 +- .../Decoders/RegisterType.cs | 2 +- src/Ryujinx.Graphics.Shader/InputTopology.cs | 8 +- .../Instructions/AttributeMap.cs | 9 +- .../Instructions/InstEmit.cs | 102 +- .../Instructions/InstEmitAluHelper.cs | 17 +- .../Instructions/InstEmitAttribute.cs | 6 +- .../Instructions/InstEmitBarrier.cs | 4 +- .../Instructions/InstEmitBitfield.cs | 2 +- .../Instructions/InstEmitConditionCode.cs | 5 +- .../Instructions/InstEmitConversion.cs | 7 +- .../Instructions/InstEmitFloatArithmetic.cs | 5 +- .../Instructions/InstEmitFloatComparison.cs | 29 +- .../Instructions/InstEmitFloatMinMax.cs | 2 +- .../Instructions/InstEmitFlowControl.cs | 26 +- .../Instructions/InstEmitHelper.cs | 64 +- .../Instructions/InstEmitIntegerArithmetic.cs | 4 +- .../Instructions/InstEmitIntegerComparison.cs | 23 +- .../Instructions/InstEmitIntegerLogical.cs | 11 +- .../Instructions/InstEmitIntegerMinMax.cs | 2 +- .../Instructions/InstEmitMemory.cs | 47 +- .../Instructions/InstEmitMove.cs | 4 +- .../Instructions/InstEmitMultifunction.cs | 2 +- .../Instructions/InstEmitNop.cs | 4 +- .../Instructions/InstEmitPredicate.cs | 2 +- .../Instructions/InstEmitShift.cs | 2 +- .../Instructions/InstEmitSurface.cs | 41 +- .../Instructions/InstEmitTexture.cs | 21 +- .../Instructions/InstEmitVideoArithmetic.cs | 5 +- .../Instructions/InstEmitVideoMinMax.cs | 2 +- .../Instructions/InstEmitWarp.cs | 5 +- .../Instructions/InstEmitter.cs | 2 +- .../Instructions/Lop3Expression.cs | 49 +- .../IntermediateRepresentation/BasicBlock.cs | 6 +- .../IntermediateRepresentation/CommentNode.cs | 2 +- .../IntermediateRepresentation/Function.cs | 2 +- .../IntermediateRepresentation/INode.cs | 2 +- .../IntermediateRepresentation/Instruction.cs | 6 +- .../IntermediateRepresentation/IoVariable.cs | 4 +- .../IntermediateRepresentation/IrConsts.cs | 4 +- .../IntermediateRepresentation/Operand.cs | 10 +- .../OperandHelper.cs | 2 +- .../IntermediateRepresentation/OperandType.cs | 4 +- .../IntermediateRepresentation/Operation.cs | 4 +- .../IntermediateRepresentation/PhiNode.cs | 12 +- .../IntermediateRepresentation/StorageKind.cs | 4 +- .../TextureFlags.cs | 44 +- .../TextureOperation.cs | 2 +- src/Ryujinx.Graphics.Shader/OutputTopology.cs | 21 +- src/Ryujinx.Graphics.Shader/SamplerType.cs | 22 +- .../ShaderIdentification.cs | 4 +- src/Ryujinx.Graphics.Shader/ShaderProgram.cs | 2 +- .../ShaderProgramInfo.cs | 2 +- src/Ryujinx.Graphics.Shader/ShaderStage.cs | 4 +- .../StructuredIr/AstAssignment.cs | 4 +- .../StructuredIr/AstBlock.cs | 6 +- .../StructuredIr/AstBlockType.cs | 4 +- .../StructuredIr/AstBlockVisitor.cs | 2 +- .../StructuredIr/AstComment.cs | 2 +- .../StructuredIr/AstHelper.cs | 9 +- .../StructuredIr/AstNode.cs | 2 +- .../StructuredIr/AstOperand.cs | 6 +- .../StructuredIr/AstOperation.cs | 16 +- .../StructuredIr/AstOptimizer.cs | 8 +- .../StructuredIr/AstTextureOperation.cs | 2 +- .../StructuredIr/BufferDefinition.cs | 2 +- .../StructuredIr/BufferLayout.cs | 4 +- .../StructuredIr/GotoElimination.cs | 39 +- .../StructuredIr/GotoStatement.cs | 8 +- .../StructuredIr/HelperFunctionsMask.cs | 14 +- .../StructuredIr/IAstNode.cs | 2 +- .../StructuredIr/InstructionInfo.cs | 6 +- .../StructuredIr/IoDefinition.cs | 2 +- .../StructuredIr/MemoryDefinition.cs | 2 +- .../StructuredIr/OperandInfo.cs | 4 +- .../StructuredIr/PhiFunctions.cs | 4 +- .../StructuredIr/ShaderProperties.cs | 2 +- .../StructuredIr/StructureType.cs | 2 +- .../StructuredIr/StructuredFunction.cs | 2 +- .../StructuredIr/StructuredProgram.cs | 35 +- .../StructuredIr/StructuredProgramContext.cs | 15 +- .../StructuredIr/StructuredProgramInfo.cs | 2 +- src/Ryujinx.Graphics.Shader/SupportBuffer.cs | 24 +- src/Ryujinx.Graphics.Shader/TessPatchType.cs | 6 +- src/Ryujinx.Graphics.Shader/TessSpacing.cs | 6 +- .../TextureDescriptor.cs | 12 +- src/Ryujinx.Graphics.Shader/TextureFormat.cs | 6 +- src/Ryujinx.Graphics.Shader/TextureHandle.cs | 2 +- .../TextureUsageFlags.cs | 2 +- .../Translation/AggregateType.cs | 6 +- .../Translation/AttributeConsts.cs | 2 +- .../Translation/ControlFlowGraph.cs | 16 +- .../Translation/Dominance.cs | 2 +- .../Translation/EmitterContext.cs | 11 +- .../Translation/EmitterContextInsts.cs | 2 +- .../Translation/FeatureFlags.cs | 4 +- .../Translation/FunctionMatch.cs | 39 +- .../Translation/HelperFunctionManager.cs | 23 +- .../Translation/HelperFunctionName.cs | 4 +- .../Optimizations/BindlessElimination.cs | 8 +- .../Optimizations/BindlessToIndexed.cs | 14 +- .../Optimizations/BranchElimination.cs | 4 +- .../Optimizations/ConstantFolding.cs | 14 +- .../Optimizations/DoubleToFloat.cs | 4 +- .../Optimizations/GlobalToStorage.cs | 63 +- .../Translation/Optimizations/Optimizer.cs | 20 +- .../Optimizations/Simplification.cs | 2 +- .../Translation/Optimizations/Utils.cs | 2 +- .../Translation/RegisterUsage.cs | 38 +- .../Translation/ResourceManager.cs | 34 +- .../Translation/Rewriter.cs | 75 +- .../Translation/ShaderConfig.cs | 66 +- .../Translation/ShaderHeader.cs | 20 +- .../Translation/ShaderIdentifier.cs | 4 +- .../Translation/Ssa.cs | 23 +- .../Translation/TargetApi.cs | 2 +- .../Translation/TargetLanguage.cs | 2 +- .../Translation/TranslationFlags.cs | 8 +- .../Translation/Translator.cs | 14 +- .../Translation/TranslatorContext.cs | 11 +- 162 files changed, 1611 insertions(+), 1627 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/AlphaTestOp.cs b/src/Ryujinx.Graphics.Shader/AlphaTestOp.cs index 57c0d1314..13958ea48 100644 --- a/src/Ryujinx.Graphics.Shader/AlphaTestOp.cs +++ b/src/Ryujinx.Graphics.Shader/AlphaTestOp.cs @@ -9,6 +9,6 @@ namespace Ryujinx.Graphics.Shader Greater, NotEqual, GreaterOrEqual, - Always + Always, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/AttributeType.cs b/src/Ryujinx.Graphics.Shader/AttributeType.cs index 4e6cad596..50a39945b 100644 --- a/src/Ryujinx.Graphics.Shader/AttributeType.cs +++ b/src/Ryujinx.Graphics.Shader/AttributeType.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader // Generic types. Float, Sint, - Uint + Uint, } static class AttributeTypeExtensions @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader AttributeType.Float => "vec4", AttributeType.Sint => "ivec4", AttributeType.Uint => "uvec4", - _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") + _ => throw new ArgumentException($"Invalid attribute type \"{type}\"."), }; } @@ -31,8 +31,8 @@ namespace Ryujinx.Graphics.Shader AttributeType.Float => AggregateType.FP32, AttributeType.Sint => AggregateType.S32, AttributeType.Uint => AggregateType.U32, - _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") + _ => throw new ArgumentException($"Invalid attribute type \"{type}\"."), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs index 410c1991d..d1da95393 100644 --- a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -37,4 +37,4 @@ namespace Ryujinx.Graphics.Shader return this; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs b/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs index ab81d5756..a69fa46a4 100644 --- a/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs +++ b/src/Ryujinx.Graphics.Shader/BufferUsageFlags.cs @@ -13,6 +13,6 @@ namespace Ryujinx.Graphics.Shader /// <summary> /// Buffer is written to. /// </summary> - Write = 1 << 0 + Write = 1 << 0, } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs index 9eb20f6f8..551e5cefa 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs @@ -92,4 +92,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return indentation; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 08e8eb195..94b850e7b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -244,16 +244,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static string GetTfLayout(TransformFeedbackOutput tfOutput) - { - if (tfOutput.Valid) - { - return $"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) "; - } - - return string.Empty; - } - public static void DeclareLocals(CodeGenContext context, StructuredFunction function) { foreach (AstOperand decl in function.Locals) @@ -294,7 +284,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AggregateType.Vector4 | AggregateType.FP64 => "dvec4", AggregateType.Vector4 | AggregateType.S32 => "ivec4", AggregateType.Vector4 | AggregateType.U32 => "uvec4", - _ => throw new ArgumentException($"Invalid variable type \"{type}\".") + _ => throw new ArgumentException($"Invalid variable type \"{type}\"."), }; } @@ -315,7 +305,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string layout = buffer.Layout switch { BufferLayout.Std140 => "std140", - _ => "std430" + _ => "std430", }; string set = string.Empty; @@ -507,7 +497,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { PixelImap.Constant => "flat ", PixelImap.ScreenLinear => "noperspective ", - _ => string.Empty + _ => string.Empty, }; } @@ -524,7 +514,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl 2 => "vec2", 3 => "vec3", 4 => "vec4", - _ => "float" + _ => "float", }; context.AppendLine($"layout (location = {attr}) in {type} {name};"); @@ -611,7 +601,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl 2 => "vec2", 3 => "vec3", 4 => "vec4", - _ => "float" + _ => "float", }; string xfb = string.Empty; @@ -647,7 +637,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeType.Sint => "ivec4", AttributeType.Uint => "uvec4", - _ => "vec4" + _ => "vec4", }; if (context.Config.GpuAccessor.QueryHostReducedPrecision() && context.Config.Stage == ShaderStage.Vertex && attr == 0) @@ -721,4 +711,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index e909dcf04..842228edf 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string LocalNamePrefix = "temp"; public const string SamplerNamePrefix = "tex"; - public const string ImageNamePrefix = "img"; + public const string ImageNamePrefix = "img"; public const string PerPatchAttributePrefix = "patch_attr_"; public const string IAttributePrefix = "in_attr"; @@ -15,4 +15,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string UndefinedName = "undef"; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index fe0d275b6..0140c1b93 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string Generate(StructuredProgramInfo info, ShaderConfig config) { - CodeGenContext context = new CodeGenContext(info, config); + CodeGenContext context = new(info, config); Declarations.Declare(context, info); @@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction) { - AstBlockVisitor visitor = new AstBlockVisitor(block); + AstBlockVisitor visitor = new(block); visitor.BlockEntered += (sender, e) => { @@ -96,7 +96,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"if ({GetCondExpr(context, e.Block.Condition)})"); break; - default: throw new InvalidOperationException($"Found unexpected block type \"{e.Block.Type}\"."); + default: + throw new InvalidOperationException($"Found unexpected block type \"{e.Block.Type}\"."); } context.EnterScope(); @@ -173,4 +174,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return ReinterpretCast(context, cond, srcType, AggregateType.Bool); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs index 21c435475..221802727 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs @@ -5,10 +5,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string MultiplyHighS32 = "Helper_MultiplyHighS32"; public static string MultiplyHighU32 = "Helper_MultiplyHighU32"; - public static string Shuffle = "Helper_Shuffle"; + public static string Shuffle = "Helper_Shuffle"; public static string ShuffleDown = "Helper_ShuffleDown"; - public static string ShuffleUp = "Helper_ShuffleUp"; - public static string ShuffleXor = "Helper_ShuffleXor"; - public static string SwizzleAdd = "Helper_SwizzleAdd"; + public static string ShuffleUp = "Helper_ShuffleUp"; + public static string ShuffleXor = "Helper_ShuffleXor"; + public static string SwizzleAdd = "Helper_SwizzleAdd"; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index b2577a999..9208ceead 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -197,4 +197,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions throw new InvalidOperationException($"Unexpected instruction type \"{info.Type}\"."); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs index 68793c5dd..9a2bfef0c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs @@ -24,4 +24,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs index 2df6960d9..0618ba8a3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs @@ -26,4 +26,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"{function.Name}({string.Join(", ", args)})"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs index f61a53cbe..a3d68028f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs @@ -26,4 +26,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 8b0b744ad..c3d52b2c5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { _infoTable = new InstInfo[(int)Instruction.Count]; +#pragma warning disable IDE0055 // Disable formatting Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd"); Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomicAnd"); Add(Instruction.AtomicCompareAndSwap, InstType.AtomicTernary, "atomicCompSwap"); @@ -125,6 +126,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.VoteAll, InstType.CallUnary, "allInvocationsARB"); Add(Instruction.VoteAllEqual, InstType.CallUnary, "allInvocationsEqualARB"); Add(Instruction.VoteAny, InstType.CallUnary, "anyInvocationARB"); +#pragma warning restore IDE0055 } private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0) @@ -163,7 +165,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { // If the node isn't a operation, then it can only be a operand, // and those never needs to be surrounded in parenthesis. - if (!(node is AstOperation operation)) + 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, @@ -208,7 +210,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions private static bool IsNegativeConst(IAstNode node) { - if (!(node is AstOperand operand)) + if (node is not AstOperand operand) { return false; } @@ -216,4 +218,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return operand.Type == OperandType.Constant && operand.Value < 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 99376ffb2..e0faed298 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -3,7 +3,6 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Text; - using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -42,14 +41,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; var texCallBuilder = new StringBuilder(); if (texOp.Inst == Instruction.ImageAtomic) { - texCallBuilder.Append((texOp.Flags & TextureFlags.AtomicMask) switch { + texCallBuilder.Append((texOp.Flags & TextureFlags.AtomicMask) switch + { +#pragma warning disable IDE0055 // Disable formatting TextureFlags.Add => "imageAtomicAdd", TextureFlags.Minimum => "imageAtomicMin", TextureFlags.Maximum => "imageAtomicMax", @@ -61,6 +62,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions TextureFlags.Swap => "imageAtomicExchange", TextureFlags.CAS => "imageAtomicCompSwap", _ => "imageAtomicAdd", +#pragma warning restore IDE0055 }); } else @@ -131,7 +133,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { AggregateType.S32 => NumberFormatter.FormatInt(0), AggregateType.U32 => NumberFormatter.FormatUint(0), - _ => NumberFormatter.FormatFloat(0) + _ => NumberFormatter.FormatFloat(0), }; } } @@ -140,7 +142,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { AggregateType.S32 => "i", AggregateType.U32 => "u", - _ => string.Empty + _ => string.Empty, }; Append($"{prefix}vec4({string.Join(", ", cElems)})"); @@ -159,7 +161,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { TextureFlags.Increment => NumberFormatter.FormatInt(1, type), // TODO: Clamp value TextureFlags.Decrement => NumberFormatter.FormatInt(-1, type), // TODO: Clamp value - _ => Src(type) + _ => Src(type), }; Append(value); @@ -248,25 +250,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { AstTextureOperation texOp = (AstTextureOperation)operation; - bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + 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 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 isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; - bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; + bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; bool colorIsVector = isGather || !isShadow; SamplerType type = texOp.Type & SamplerType.Mask; - bool is2D = type == SamplerType.Texture2D; + bool is2D = type == SamplerType.Texture2D; bool isCube = type == SamplerType.TextureCube; // 2D Array and Cube shadow samplers with LOD level or bias requires an extension. @@ -500,14 +502,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (hasLodBias) { - Append(Src(AggregateType.FP32)); + Append(Src(AggregateType.FP32)); } // textureGather* optional extra component index, // not needed for shadow samplers. if (isGather && !isShadow) { - Append(Src(AggregateType.S32)); + Append(Src(AggregateType.S32)); } texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); @@ -584,7 +586,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { case StorageKind.ConstantBuffer: case StorageKind.StorageBuffer: - if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant) + 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."); } @@ -594,7 +596,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions ? context.Config.Properties.ConstantBuffers[binding] : context.Config.Properties.StorageBuffers[binding]; - if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant) + 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."); } @@ -606,7 +608,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case StorageKind.LocalMemory: case StorageKind.SharedMemory: - if (!(operation.GetSource(srcIndex++) is AstOperand bindingId) || bindingId.Type != OperandType.Constant) + 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."); } @@ -623,7 +625,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case StorageKind.InputPerPatch: case StorageKind.Output: case StorageKind.OutputPerPatch: - if (!(operation.GetSource(srcIndex++) is AstOperand varId) || varId.Type != OperandType.Constant) + 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."); } @@ -636,7 +638,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) { - if (!(operation.GetSource(srcIndex++) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant) + 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."); } @@ -733,4 +735,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return swizzle; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs index 5a888e9c5..ad84c4850 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs @@ -53,4 +53,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $".{"xy".AsSpan(index, 1)}"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs index f09ea2e8a..70174a5ba 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs @@ -29,4 +29,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs index 7b2a6b464..a784e2bb3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs @@ -10,9 +10,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions public InstInfo(InstType type, string opName, int precedence) { - Type = type; - OpName = opName; + Type = type; + OpName = opName; Precedence = precedence; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs index 84e36cdd6..56985ae08 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs @@ -1,33 +1,35 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum InstType { - OpNullary = Op | 0, - OpUnary = Op | 1, - OpBinary = Op | 2, + OpNullary = Op | 0, + OpUnary = Op | 1, + OpBinary = Op | 2, OpBinaryCom = Op | 2 | Commutative, - OpTernary = Op | 3, + OpTernary = Op | 3, - CallNullary = Call | 0, - CallUnary = Call | 1, - CallBinary = Call | 2, - CallTernary = Call | 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, + AtomicBinary = Call | Atomic | 3, AtomicTernary = Call | Atomic | 4, Commutative = 1 << 8, - Op = 1 << 9, - Call = 1 << 10, - Atomic = 1 << 11, - Special = 1 << 12, + Op = 1 << 9, + Call = 1 << 10, + Atomic = 1 << 11, + Special = 1 << 12, - ArityMask = 0xff + ArityMask = 0xff, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs index 2a73b8b07..3f88d2b32 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location), IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32), IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. - IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. + IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool), IoVariable.InstanceId => ("gl_InstanceID", AggregateType.S32), IoVariable.InstanceIndex => ("gl_InstanceIndex", AggregateType.S32), @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions IoVariable.VertexIndex => ("gl_VertexIndex", AggregateType.S32), IoVariable.ViewportIndex => ("gl_ViewportIndex", AggregateType.S32), IoVariable.ViewportMask => ("gl_ViewportMask", AggregateType.Array | AggregateType.S32), - _ => (null, AggregateType.Invalid) + _ => (null, AggregateType.Invalid), }; } @@ -139,4 +139,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return (name, config.GetUserDefinedType(location, isOutput)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs index eb27e9bf1..28e44c900 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs @@ -101,4 +101,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return "0x" + value.ToString("X", CultureInfo.InvariantCulture) + "u"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 4f6ca642c..0ca3b55fc 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -5,7 +5,6 @@ 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.Glsl @@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; - private Dictionary<AstOperand, string> _locals; + private readonly Dictionary<AstOperand, string> _locals; public OperandManager() { @@ -38,7 +37,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.LocalVariable => _locals[operand], OperandType.Undefined => DefaultNames.UndefinedName, - _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") + _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."), }; } @@ -96,11 +95,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return _stagePrefixes[index]; } - private static char GetSwizzleMask(int value) - { - return "xyzw"[value]; - } - public static string GetArgumentName(int argIndex) { return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; @@ -119,12 +113,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { case StorageKind.ConstantBuffer: case StorageKind.StorageBuffer: - if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant) + 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 AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant) + 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."); } @@ -138,7 +132,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl case StorageKind.LocalMemory: case StorageKind.SharedMemory: - if (!(operation.GetSource(0) is AstOperand bindingId) || bindingId.Type != OperandType.Constant) + 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."); } @@ -153,7 +147,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl case StorageKind.InputPerPatch: case StorageKind.Output: case StorageKind.OutputPerPatch: - if (!(operation.GetSource(0) is AstOperand varId) || varId.Type != OperandType.Constant) + 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."); } @@ -166,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) { - if (!(operation.GetSource(1) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant) + 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."); } @@ -232,4 +226,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs index 22c8623ce..3d7d0d0c1 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs @@ -10,9 +10,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public static string ReinterpretCast( CodeGenContext context, - IAstNode node, - AggregateType srcType, - AggregateType dstType) + IAstNode node, + AggregateType srcType, + AggregateType dstType) { if (node is AstOperand operand && operand.Type == OperandType.Constant) { @@ -38,18 +38,24 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { switch (dstType) { - case AggregateType.Bool: return $"(floatBitsToInt({expr}) != 0)"; - case AggregateType.S32: return $"floatBitsToInt({expr})"; - case AggregateType.U32: return $"floatBitsToUint({expr})"; + case AggregateType.Bool: + return $"(floatBitsToInt({expr}) != 0)"; + case AggregateType.S32: + return $"floatBitsToInt({expr})"; + case AggregateType.U32: + return $"floatBitsToUint({expr})"; } } else if (dstType == AggregateType.FP32) { switch (srcType) { - case AggregateType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; - case AggregateType.S32: return $"intBitsToFloat({expr})"; - case AggregateType.U32: return $"uintBitsToFloat({expr})"; + case AggregateType.Bool: + return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; + case AggregateType.S32: + return $"intBitsToFloat({expr})"; + case AggregateType.U32: + return $"uintBitsToFloat({expr})"; } } else if (srcType == AggregateType.Bool) @@ -76,7 +82,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static string ReinterpretBoolToInt(string expr, IAstNode node, AggregateType dstType) { - string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType); + string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType); string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType); expr = InstGenHelper.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false); @@ -84,4 +90,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"({expr} ? {trueExpr} : {falseExpr})"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index a4daaa67e..9956e90a3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -1,13 +1,14 @@ -using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using Spv.Generator; using System; using System.Collections.Generic; using static Spv.Specification; +using Instruction = Spv.Generator.Instruction; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { - using IrConsts = IntermediateRepresentation.IrConsts; using IrOperandType = IntermediateRepresentation.OperandType; partial class CodeGenContext : Module @@ -36,15 +37,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); public StructuredFunction CurrentFunction { get; set; } - private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>(); - private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>(); - private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>(); - private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new Dictionary<int, (StructuredFunction, Instruction)>(); + private readonly Dictionary<AstOperand, Instruction> _locals = new(); + private readonly Dictionary<int, Instruction[]> _localForArgs = new(); + private readonly Dictionary<int, Instruction> _funcArgs = new(); + private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new(); private class BlockState { private int _entryCount; - private readonly List<Instruction> _labels = new List<Instruction>(); + private readonly List<Instruction> _labels = new(); public Instruction GetNextLabel(CodeGenContext context) { @@ -67,7 +68,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - private readonly Dictionary<AstBlock, BlockState> _labels = new Dictionary<AstBlock, BlockState>(); + private readonly Dictionary<AstBlock, BlockState> _labels = new(); public Dictionary<AstBlock, (Instruction, Instruction)> LoopTargets { get; set; } @@ -98,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv InputTopology.LinesAdjacency => 2, InputTopology.Triangles => 3, InputTopology.TrianglesAdjacency => 3, - _ => throw new InvalidOperationException($"Invalid input topology \"{inPrimitive}\".") + _ => throw new InvalidOperationException($"Invalid input topology \"{inPrimitive}\"."), }; } @@ -222,7 +223,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv IrOperandType.Constant => GetConstant(type, operand), IrOperandType.LocalVariable => GetLocal(type, operand), IrOperandType.Undefined => GetUndefined(type), - _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") + _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."), }; } @@ -259,7 +260,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AggregateType.Bool => ConstantFalse(TypeBool()), AggregateType.FP32 => Constant(TypeFP32(), 0f), AggregateType.FP64 => Constant(TypeFP64(), 0d), - _ => Constant(GetType(type), 0) + _ => Constant(GetType(type), 0), }; } @@ -272,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AggregateType.FP64 => Constant(TypeFP64(), (double)BitConverter.Int32BitsToSingle(operand.Value)), AggregateType.S32 => Constant(TypeS32(), operand.Value), AggregateType.U32 => Constant(TypeU32(), (uint)operand.Value), - _ => throw new ArgumentException($"Invalid type \"{type}\".") + _ => throw new ArgumentException($"Invalid type \"{type}\"."), }; } @@ -328,7 +329,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AggregateType.Vector2 => 2, AggregateType.Vector3 => 3, AggregateType.Vector4 => 4, - _ => 1 + _ => 1, }; return TypeVector(GetType(type & ~AggregateType.ElementCountMask), vectorLength); @@ -342,7 +343,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AggregateType.FP64 => TypeFP64(), AggregateType.S32 => TypeS32(), AggregateType.U32 => TypeU32(), - _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") + _ => throw new ArgumentException($"Invalid attribute type \"{type}\"."), }; } @@ -359,7 +360,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else if (srcType == AggregateType.Bool) { - var intTrue = Constant(TypeS32(), IrConsts.True); + var intTrue = Constant(TypeS32(), IrConsts.True); var intFalse = Constant(TypeS32(), IrConsts.False); return BitcastIfNeeded(dstType, AggregateType.S32, Select(TypeS32(), value, intTrue, intFalse)); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 59acea4f6..da1e385a7 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -1,5 +1,4 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using Spv.Generator; @@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { static class Declarations { - private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; + private static readonly string[] _stagePrefixes = { "cp", "vp", "tcp", "tep", "gp", "fp" }; public static void DeclareParameters(CodeGenContext context, StructuredFunction function) { @@ -107,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool isBuffer) { - HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>(); + HashSet<SpvInstruction> decoratedTypes = new(); foreach (BufferDefinition buffer in buffers) { @@ -199,7 +198,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SamplerType.Texture3D => Dim.Dim3D, SamplerType.TextureCube => Dim.Cube, SamplerType.TextureBuffer => Dim.Buffer, - _ => throw new InvalidOperationException($"Invalid sampler type \"{descriptor.Type & SamplerType.Mask}\".") + _ => throw new InvalidOperationException($"Invalid sampler type \"{descriptor.Type & SamplerType.Mask}\"."), }; var imageType = context.TypeImage( @@ -282,7 +281,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SamplerType.Texture3D => Dim.Dim3D, SamplerType.TextureCube => Dim.Cube, SamplerType.TextureBuffer => Dim.Buffer, - _ => throw new ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\".") + _ => throw new ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\"."), }; } @@ -330,7 +329,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv TextureFormat.R10G10B10A2Unorm => ImageFormat.Rgb10A2, TextureFormat.R10G10B10A2Uint => ImageFormat.Rgb10a2ui, TextureFormat.R11G11B10Float => ImageFormat.R11fG11fB10f, - _ => throw new ArgumentException($"Invalid texture format \"{format}\".") + _ => throw new ArgumentException($"Invalid texture format \"{format}\"."), }; } @@ -352,7 +351,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv (_, AggregateType varType) = IoMap.GetSpirvBuiltIn(ioVariable); AggregateType elemType = varType & AggregateType.ElementTypeMask; - if (elemType == AggregateType.S32 || elemType == AggregateType.U32) + if (elemType is AggregateType.S32 or AggregateType.U32) { iq = PixelImap.Constant; } @@ -410,7 +409,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv 2 => AggregateType.Vector2, 3 => AggregateType.Vector3, 4 => AggregateType.Vector4, - _ => AggregateType.Invalid + _ => AggregateType.Invalid, }; } @@ -420,7 +419,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Config.Stage, isOutput)) { int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32; - spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); + spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), arraySize)); if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { @@ -542,7 +541,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static string GetStagePrefix(ShaderStage stage) { - return StagePrefixes[(int)stage]; + return _stagePrefixes[(int)stage]; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs index 72541774d..2bb7e8369 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv ShaderStage.TessellationEvaluation => ExecutionModel.TessellationEvaluation, ShaderStage.Geometry => ExecutionModel.Geometry, ShaderStage.Fragment => ExecutionModel.Fragment, - _ => throw new ArgumentException($"Invalid shader stage \"{stage}\".") + _ => throw new ArgumentException($"Invalid shader stage \"{stage}\"."), }; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index b451f7a48..a53b40b24 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -14,19 +14,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv static class Instructions { - private const MemorySemanticsMask DefaultMemorySemantics = + private const MemorySemanticsMask DefaultMemorySemantics = MemorySemanticsMask.ImageMemory | MemorySemanticsMask.AtomicCounterMemory | MemorySemanticsMask.WorkgroupMemory | MemorySemanticsMask.UniformMemory | MemorySemanticsMask.AcquireRelease; - private static readonly Func<CodeGenContext, AstOperation, OperationResult>[] InstTable; + private static readonly Func<CodeGenContext, AstOperation, OperationResult>[] _instTable; static Instructions() { - InstTable = new Func<CodeGenContext, AstOperation, OperationResult>[(int)Instruction.Count]; + _instTable = new Func<CodeGenContext, AstOperation, OperationResult>[(int)Instruction.Count]; +#pragma warning disable IDE0055 // Disable formatting Add(Instruction.Absolute, GenerateAbsolute); Add(Instruction.Add, GenerateAdd); Add(Instruction.AtomicAdd, GenerateAtomicAdd); @@ -141,16 +142,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.VoteAll, GenerateVoteAll); Add(Instruction.VoteAllEqual, GenerateVoteAllEqual); Add(Instruction.VoteAny, GenerateVoteAny); +#pragma warning restore IDE0055 } private static void Add(Instruction inst, Func<CodeGenContext, AstOperation, OperationResult> handler) { - InstTable[(int)(inst & Instruction.Mask)] = handler; + _instTable[(int)(inst & Instruction.Mask)] = handler; } public static OperationResult Generate(CodeGenContext context, AstOperation operation) { - var handler = InstTable[(int)(operation.Inst & Instruction.Mask)]; + var handler = _instTable[(int)(operation.Inst & Instruction.Mask)]; if (handler != null) { return handler(context, operation); @@ -305,7 +307,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Debug.Assert(funcId.Type == OperandType.Constant); - (var function, var spvFunc) = context.GetFunction(funcId.Value); + var (function, spvFunc) = context.GetFunction(funcId.Value); var args = new SpvInstruction[operation.SourcesCount - 1]; var spvLocals = context.GetLocalForArgsPointers(funcId.Value); @@ -615,7 +617,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv }); } - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; int srcIndex = isBindless ? 1 : 0; @@ -625,11 +627,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - SpvInstruction index = null; - if (isIndexed) { - index = Src(AggregateType.S32); + Src(AggregateType.S32); } int coordsCount = texOp.Type.GetDimensions(); @@ -657,9 +657,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SpvInstruction value = Src(componentType); - (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; + (SpvInstruction imageType, SpvInstruction imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; - var image = context.Load(imageType, imageVariable); + context.Load(imageType, imageVariable); SpvInstruction resultType = context.GetType(componentType); SpvInstruction imagePointerType = context.TypePointer(StorageClass.Image, resultType); @@ -670,21 +670,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var result = (texOp.Flags & TextureFlags.AtomicMask) switch { - TextureFlags.Add => context.AtomicIAdd(resultType, pointer, one, zero, value), - TextureFlags.Minimum => componentType == AggregateType.S32 + TextureFlags.Add => context.AtomicIAdd(resultType, pointer, one, zero, value), + TextureFlags.Minimum => componentType == AggregateType.S32 ? context.AtomicSMin(resultType, pointer, one, zero, value) : context.AtomicUMin(resultType, pointer, one, zero, value), - TextureFlags.Maximum => componentType == AggregateType.S32 + TextureFlags.Maximum => componentType == AggregateType.S32 ? context.AtomicSMax(resultType, pointer, one, zero, value) : context.AtomicUMax(resultType, pointer, one, zero, value), - TextureFlags.Increment => context.AtomicIIncrement(resultType, pointer, one, zero), - TextureFlags.Decrement => context.AtomicIDecrement(resultType, pointer, one, zero), + TextureFlags.Increment => context.AtomicIIncrement(resultType, pointer, one, zero), + TextureFlags.Decrement => context.AtomicIDecrement(resultType, pointer, one, zero), TextureFlags.BitwiseAnd => context.AtomicAnd(resultType, pointer, one, zero, value), - TextureFlags.BitwiseOr => context.AtomicOr(resultType, pointer, one, zero, value), + TextureFlags.BitwiseOr => context.AtomicOr(resultType, pointer, one, zero, value), TextureFlags.BitwiseXor => context.AtomicXor(resultType, pointer, one, zero, value), - TextureFlags.Swap => context.AtomicExchange(resultType, pointer, one, zero, value), - TextureFlags.CAS => context.AtomicCompareExchange(resultType, pointer, one, zero, zero, Src(componentType), value), - _ => context.AtomicIAdd(resultType, pointer, one, zero, value), + TextureFlags.Swap => context.AtomicExchange(resultType, pointer, one, zero, value), + TextureFlags.CAS => context.AtomicCompareExchange(resultType, pointer, one, zero, zero, Src(componentType), value), + _ => context.AtomicIAdd(resultType, pointer, one, zero, value), }; return new OperationResult(componentType, result); @@ -704,7 +704,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return GetZeroOperationResult(context, texOp, componentType, isVector: true); } - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; int srcIndex = isBindless ? 1 : 0; @@ -714,11 +714,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - SpvInstruction index = null; - if (isIndexed) { - index = Src(AggregateType.S32); + Src(AggregateType.S32); } int coordsCount = texOp.Type.GetDimensions(); @@ -744,7 +742,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv pCoords = Src(AggregateType.S32); } - (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; + var (imageType, imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; var image = context.Load(imageType, imageVariable); var imageComponentType = context.GetType(componentType); @@ -768,7 +766,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return OperationResult.Invalid; } - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; int srcIndex = isBindless ? 1 : 0; @@ -778,11 +776,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - SpvInstruction index = null; - if (isIndexed) { - index = Src(AggregateType.S32); + Src(AggregateType.S32); } int coordsCount = texOp.Type.GetDimensions(); @@ -833,7 +829,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType), ComponentsCount), cElems); - (var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; + var (imageType, imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; var image = context.Load(imageType, imageVariable); @@ -886,11 +882,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - SpvInstruction index = null; - if (isIndexed) { - index = Src(AggregateType.S32); + Src(AggregateType.S32); } int pCount = texOp.Type.GetDimensions(); @@ -916,7 +910,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - (_, var sampledImageType, var sampledImageVariable) = context.Samplers[meta]; + var (_, sampledImageType, sampledImageVariable) = context.Samplers[meta]; var image = context.Load(sampledImageType, sampledImageVariable); @@ -973,7 +967,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv loopBlock = loopBlock.Parent; } - (var loopTarget, var continueTarget) = context.LoopTargets[loopBlock]; + (_, SpvInstruction continueTarget) = context.LoopTargets[loopBlock]; context.Branch(continueTarget); @@ -1278,19 +1272,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { AstTextureOperation texOp = (AstTextureOperation)operation; - bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + 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 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 isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; - bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; + bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; bool colorIsVector = isGather || !isShadow; @@ -1307,11 +1301,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return context.Get(type, texOp.GetSource(srcIndex++)); } - SpvInstruction index = null; - if (isIndexed) { - index = Src(AggregateType.S32); + Src(AggregateType.S32); } int coordsCount = texOp.Type.GetDimensions(); @@ -1395,7 +1387,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv derivatives = new[] { AssembleDerivativesVector(coordsCount), // dPdx - AssembleDerivativesVector(coordsCount) // dPdy + AssembleDerivativesVector(coordsCount), // dPdy }; } @@ -1445,7 +1437,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv AssembleOffsetVector(coordsCount), AssembleOffsetVector(coordsCount), AssembleOffsetVector(coordsCount), - AssembleOffsetVector(coordsCount) + AssembleOffsetVector(coordsCount), }; } @@ -1474,7 +1466,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv // not needed for shadow samplers. if (isGather && !isShadow) { - compIdx = Src(AggregateType.S32); + compIdx = Src(AggregateType.S32); } var operandsList = new List<SpvInstruction>(); @@ -1521,7 +1513,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[meta]; + var (imageType, sampledImageType, sampledImageVariable) = context.Samplers[meta]; var image = context.Load(sampledImageType, sampledImageVariable); @@ -1595,16 +1587,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; - SpvInstruction index = null; - if (isIndexed) { - index = context.GetS32(texOp.GetSource(0)); + context.GetS32(texOp.GetSource(0)); } var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[meta]; + (SpvInstruction imageType, SpvInstruction sampledImageType, SpvInstruction sampledImageVariable) = context.Samplers[meta]; var image = context.Load(sampledImageType, sampledImageVariable); image = context.Image(imageType, image); @@ -1809,12 +1799,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { case StorageKind.ConstantBuffer: case StorageKind.StorageBuffer: - if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant) + 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."); } - if (!(operation.GetSource(srcIndex) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant) + 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."); } @@ -1833,7 +1823,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv case StorageKind.LocalMemory: case StorageKind.SharedMemory: - if (!(operation.GetSource(srcIndex++) is AstOperand bindingId) || bindingId.Type != OperandType.Constant) + 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."); } @@ -1856,7 +1846,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv case StorageKind.InputPerPatch: case StorageKind.Output: case StorageKind.OutputPerPatch: - if (!(operation.GetSource(srcIndex++) is AstOperand varId) || varId.Type != OperandType.Constant) + 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."); } @@ -1869,7 +1859,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) { - if (!(operation.GetSource(srcIndex++) is AstOperand vecIndex) || vecIndex.Type != OperandType.Constant) + 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."); } @@ -1964,7 +1954,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable) { - (_, var varType) = IoMap.GetSpirvBuiltIn(ioVariable); + var (_, varType) = IoMap.GetSpirvBuiltIn(ioVariable); varType &= AggregateType.ElementTypeMask; var ioDefinition = new IoDefinition(StorageKind.Input, ioVariable); @@ -2061,10 +2051,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(AggregateType.Bool, emitB(context.TypeBool(), context.Get(AggregateType.Bool, source))); } - private static OperationResult GenerateUnaryFP32( - CodeGenContext context, - AstOperation operation, - Func<SpvInstruction, SpvInstruction, SpvInstruction> emit) + private static OperationResult GenerateUnaryFP32( + CodeGenContext context, + AstOperation operation, + Func<SpvInstruction, SpvInstruction, SpvInstruction> emit) { var source = operation.GetSource(0); return new OperationResult(AggregateType.FP32, emit(context.TypeFP32(), context.GetFP32(source))); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs index d2ff0085c..8a6103237 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv IoVariable.VertexIndex => (BuiltIn.VertexIndex, AggregateType.S32), IoVariable.ViewportIndex => (BuiltIn.ViewportIndex, AggregateType.S32), IoVariable.ViewportMask => (BuiltIn.ViewportMaskNV, AggregateType.Array | AggregateType.S32), - _ => (default, AggregateType.Invalid) + _ => (default, AggregateType.Invalid), }; } @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv IoVariable.TessellationLevelOuter => 4, IoVariable.ViewportMask => 1, IoVariable.UserDefined => MaxAttributes, - _ => 1 + _ => 1, }; } @@ -74,13 +74,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv case IoVariable.ClipDistance: case IoVariable.PointCoord: case IoVariable.ViewportMask: - return !isOutput && - (stage == ShaderStage.TessellationControl || - stage == ShaderStage.TessellationEvaluation || - stage == ShaderStage.Geometry); + return !isOutput && + stage is ShaderStage.TessellationControl or ShaderStage.TessellationEvaluation or ShaderStage.Geometry; } return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs index f80c8110b..a6e8e91f6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { readonly struct OperationResult { - public static OperationResult Invalid => new OperationResult(AggregateType.Invalid, null); + public static OperationResult Invalid => new(AggregateType.Invalid, null); public AggregateType Type { get; } public Instruction Value { get; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 5c736b605..c8fcd75a1 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -17,15 +17,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { // Resource pools for Spirv generation. Note: Increase count when more threads are being used. private const int GeneratorPoolCount = 1; - private static ObjectPool<SpvInstructionPool> InstructionPool; - private static ObjectPool<SpvLiteralIntegerPool> IntegerPool; - private static object PoolLock; + private static readonly ObjectPool<SpvInstructionPool> _instructionPool; + private static readonly ObjectPool<SpvLiteralIntegerPool> _integerPool; + private static readonly object _poolLock; static SpirvGenerator() { - InstructionPool = new (() => new SpvInstructionPool(), GeneratorPoolCount); - IntegerPool = new (() => new SpvLiteralIntegerPool(), GeneratorPoolCount); - PoolLock = new object(); + _instructionPool = new(() => new SpvInstructionPool(), GeneratorPoolCount); + _integerPool = new(() => new SpvLiteralIntegerPool(), GeneratorPoolCount); + _poolLock = new object(); } private const HelperFunctionsMask NeedsInvocationIdMask = @@ -40,13 +40,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SpvInstructionPool instPool; SpvLiteralIntegerPool integerPool; - lock (PoolLock) + lock (_poolLock) { - instPool = InstructionPool.Allocate(); - integerPool = IntegerPool.Allocate(); + instPool = _instructionPool.Allocate(); + integerPool = _integerPool.Allocate(); } - CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool); + CodeGenContext context = new(info, config, instPool, integerPool); context.AddCapability(Capability.GroupNonUniformBallot); context.AddCapability(Capability.GroupNonUniformShuffle); @@ -133,10 +133,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv byte[] result = context.Generate(); - lock (PoolLock) + lock (_poolLock) { - InstructionPool.Release(instPool); - IntegerPool.Release(integerPool); + _instructionPool.Release(instPool); + _integerPool.Release(integerPool); } return result; @@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex) { - (var function, var spvFunc) = context.GetFunction(funcIndex); + var (function, spvFunc) = context.GetFunction(funcIndex); context.CurrentFunction = function; context.AddFunction(spvFunc); @@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Generate(context, function.MainBlock); // Functions must always end with a return. - if (!(function.MainBlock.Last is AstOperation operation) || + if (function.MainBlock.Last is not AstOperation operation || (operation.Inst != Instruction.Return && operation.Inst != Instruction.Discard)) { context.Return(); @@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv InputTopology.LinesAdjacency => ExecutionMode.InputLinesAdjacency, InputTopology.Triangles => ExecutionMode.Triangles, InputTopology.TrianglesAdjacency => ExecutionMode.InputTrianglesAdjacency, - _ => throw new InvalidOperationException($"Invalid input topology \"{inputTopology}\".") + _ => throw new InvalidOperationException($"Invalid input topology \"{inputTopology}\"."), }); context.AddExecutionMode(spvFunc, ExecutionMode.Invocations, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive); @@ -242,7 +242,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv OutputTopology.PointList => ExecutionMode.OutputPoints, OutputTopology.LineStrip => ExecutionMode.OutputLineStrip, OutputTopology.TriangleStrip => ExecutionMode.OutputTriangleStrip, - _ => throw new InvalidOperationException($"Invalid output topology \"{context.Config.OutputTopology}\".") + _ => throw new InvalidOperationException($"Invalid output topology \"{context.Config.OutputTopology}\"."), }); int maxOutputVertices = context.Config.GpPassthrough ? context.InputVertices : context.Config.MaxOutputVertices; @@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void Generate(CodeGenContext context, AstBlock block) { - AstBlockVisitor visitor = new AstBlockVisitor(block); + AstBlockVisitor visitor = new(block); var loopTargets = new Dictionary<AstBlock, (SpvInstruction, SpvInstruction)>(); @@ -346,7 +346,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv // if the condition is true. AstBlock mergeBlock = e.Block.Parent; - (var loopTarget, var continueTarget) = loopTargets[e.Block]; + var (loopTarget, continueTarget) = loopTargets[e.Block]; context.Branch(continueTarget); context.AddLabel(continueTarget); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs index 4de056037..56ea9a2a6 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs @@ -1,4 +1,4 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { readonly record struct TextureMeta(int CbufSlot, int Handle, TextureFormat Format); -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Constants.cs b/src/Ryujinx.Graphics.Shader/Constants.cs index 39d6b2381..cff2c37a0 100644 --- a/src/Ryujinx.Graphics.Shader/Constants.cs +++ b/src/Ryujinx.Graphics.Shader/Constants.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Graphics.Shader public const int TfeBufferBaseBinding = 1; public const int TfeBuffersCount = 4; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/Block.cs b/src/Ryujinx.Graphics.Shader/Decoders/Block.cs index 7d94e3f9a..1a694898b 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/Block.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/Block.cs @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Shader.Decoders pushOpInfo.Consumers.Add(rightBlock, local); } - foreach ((ulong key, SyncTarget value) in SyncTargets) + foreach ((ulong key, SyncTarget value) in SyncTargets) { rightBlock.SyncTargets.Add(key, value); } @@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { if (OpCodes.Count != 0) { - return OpCodes[OpCodes.Count - 1]; + return OpCodes[^1]; } return default; @@ -165,4 +165,4 @@ namespace Ryujinx.Graphics.Shader.Decoders PushOpCodes.Add(new PushOpInfo(op)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs b/src/Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs index 7a172fe6f..49cd3a30a 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/DecodedFunction.cs @@ -45,4 +45,4 @@ namespace Ryujinx.Graphics.Shader.Decoders } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs b/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs index 2dd60155e..7776ccc52 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs @@ -54,4 +54,4 @@ namespace Ryujinx.Graphics.Shader.Decoders return GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 4e6c6a5df..ba31c0205 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Decoders @@ -12,8 +11,8 @@ namespace Ryujinx.Graphics.Shader.Decoders { public static DecodedProgram Decode(ShaderConfig config, ulong startAddress) { - Queue<DecodedFunction> functionsQueue = new Queue<DecodedFunction>(); - Dictionary<ulong, DecodedFunction> functionsVisited = new Dictionary<ulong, DecodedFunction>(); + Queue<DecodedFunction> functionsQueue = new(); + Dictionary<ulong, DecodedFunction> functionsVisited = new(); DecodedFunction EnqueueFunction(ulong address) { @@ -30,9 +29,9 @@ namespace Ryujinx.Graphics.Shader.Decoders while (functionsQueue.TryDequeue(out DecodedFunction currentFunction)) { - List<Block> blocks = new List<Block>(); - Queue<Block> workQueue = new Queue<Block>(); - Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>(); + List<Block> blocks = new(); + Queue<Block> workQueue = new(); + Dictionary<ulong, Block> visited = new(); Block GetBlock(ulong blkAddress) { @@ -168,7 +167,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { index = 0; - int left = 0; + int left = 0; int right = blocks.Count - 1; while (left <= right) @@ -273,12 +272,12 @@ namespace Ryujinx.Graphics.Shader.Decoders int offset; int count = 1; bool isStore = false; - bool indexed = false; + bool indexed; bool perPatch = false; if (name == InstName.Ast) { - InstAst opAst = new InstAst(opCode); + InstAst opAst = new(opCode); count = (int)opAst.AlSize + 1; offset = opAst.Imm11; indexed = opAst.Phys; @@ -287,7 +286,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } else if (name == InstName.Ald) { - InstAld opAld = new InstAld(opCode); + InstAld opAld = new(opCode); count = (int)opAld.AlSize + 1; offset = opAld.Imm11; indexed = opAld.Phys; @@ -296,7 +295,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } else /* if (name == InstName.Ipa) */ { - InstIpa opIpa = new InstIpa(opCode); + InstIpa opIpa = new(opCode); offset = opIpa.Imm10; indexed = opIpa.Idx; } @@ -370,7 +369,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private static bool IsUnconditional(ref InstOp op) { - InstConditional condOp = new InstConditional(op.RawOpCode); + InstConditional condOp = new(op.RawOpCode); if ((op.Name == InstName.Bra || op.Name == InstName.Exit) && condOp.Ccc != Ccc.T) { @@ -391,9 +390,9 @@ namespace Ryujinx.Graphics.Shader.Decoders if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0)) { - HashSet<ulong> visited = new HashSet<ulong>(); + HashSet<ulong> visited = new(); - InstBrx opBrx = new InstBrx(lastOp.RawOpCode); + InstBrx opBrx = new(lastOp.RawOpCode); ulong baseOffset = lastOp.GetAbsoluteAddress(); // An indirect branch could go anywhere, @@ -437,7 +436,7 @@ namespace Ryujinx.Graphics.Shader.Decoders // On a successful match, "BaseOffset" is the offset in bytes where the jump offsets are // located on the constant buffer, and "UpperBound" is the total number of offsets for the BRX, minus 1. - HashSet<Block> visited = new HashSet<Block>(); + HashSet<Block> visited = new(); var ldcLocation = FindFirstRegWrite(visited, new BlockLocation(block, block.OpCodes.Count - 1), brxReg); if (ldcLocation.Block == null || ldcLocation.Block.OpCodes[ldcLocation.Index].Name != InstName.Ldc) @@ -507,7 +506,7 @@ namespace Ryujinx.Graphics.Shader.Decoders private static BlockLocation FindFirstRegWrite(HashSet<Block> visited, BlockLocation location, int regIndex) { - Queue<BlockLocation> toVisit = new Queue<BlockLocation>(); + Queue<BlockLocation> toVisit = new(); toVisit.Enqueue(location); visited.Add(location.Block); @@ -554,10 +553,10 @@ namespace Ryujinx.Graphics.Shader.Decoders { Brk, Cont, - Sync + Sync, } - private struct PathBlockState + private readonly struct PathBlockState { public Block Block { get; } @@ -565,37 +564,37 @@ namespace Ryujinx.Graphics.Shader.Decoders { None, PopPushOp, - PushBranchOp + PushBranchOp, } - private RestoreType _restoreType; + private readonly RestoreType _restoreType; - private ulong _restoreValue; - private MergeType _restoreMergeType; + private readonly ulong _restoreValue; + private readonly MergeType _restoreMergeType; public bool ReturningFromVisit => _restoreType != RestoreType.None; public PathBlockState(Block block) { - Block = block; - _restoreType = RestoreType.None; - _restoreValue = 0; + Block = block; + _restoreType = RestoreType.None; + _restoreValue = 0; _restoreMergeType = default; } public PathBlockState(int oldStackSize) { - Block = null; - _restoreType = RestoreType.PopPushOp; - _restoreValue = (ulong)oldStackSize; + Block = null; + _restoreType = RestoreType.PopPushOp; + _restoreValue = (ulong)oldStackSize; _restoreMergeType = default; } public PathBlockState(ulong syncAddress, MergeType mergeType) { - Block = null; - _restoreType = RestoreType.PushBranchOp; - _restoreValue = syncAddress; + Block = null; + _restoreType = RestoreType.PushBranchOp; + _restoreValue = syncAddress; _restoreMergeType = mergeType; } @@ -622,9 +621,9 @@ namespace Ryujinx.Graphics.Shader.Decoders Block target = blocks[pushOp.GetAbsoluteAddress()]; - Stack<PathBlockState> workQueue = new Stack<PathBlockState>(); - HashSet<Block> visited = new HashSet<Block>(); - Stack<(ulong, MergeType)> branchStack = new Stack<(ulong, MergeType)>(); + Stack<PathBlockState> workQueue = new(); + HashSet<Block> visited = new(); + Stack<(ulong, MergeType)> branchStack = new(); void Push(PathBlockState pbs) { @@ -759,7 +758,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { InstName.Pbk => MergeType.Brk, InstName.Pcnt => MergeType.Cont, - _ => MergeType.Sync + _ => MergeType.Sync, }; } @@ -769,8 +768,8 @@ namespace Ryujinx.Graphics.Shader.Decoders { InstName.Brk => MergeType.Brk, InstName.Cont => MergeType.Cont, - _ => MergeType.Sync + _ => MergeType.Sync, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs index c7c506ec4..8bf5671ac 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs @@ -684,7 +684,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Texture2DLodZeroMultisample = 0x6, Texture3DLodZero = 0x7, Texture2DArrayLodZero = 0x8, - Texture2DLodLevelOffset = 0xc + Texture2DLodLevelOffset = 0xc, } enum TexComp @@ -848,18 +848,18 @@ namespace Ryujinx.Graphics.Shader.Decoders S16h0 = 3, } - struct InstConditional + readonly struct InstConditional { - private ulong _opcode; + private readonly ulong _opcode; public InstConditional(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public Ccc Ccc => (Ccc)(_opcode & 0x1F); } - struct InstAl2p + readonly struct InstAl2p { - private ulong _opcode; + private readonly ulong _opcode; public InstAl2p(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -871,9 +871,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 44) & 0x7); } - struct InstAld + readonly struct InstAld { - private ulong _opcode; + private readonly ulong _opcode; public InstAld(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -887,9 +887,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Phys => !P && Imm11 == 0 && SrcA != RegisterConsts.RegisterZeroIndex; } - struct InstAst + readonly struct InstAst { - private ulong _opcode; + private readonly ulong _opcode; public InstAst(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)(_opcode & 0xFF); @@ -902,9 +902,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Phys => !P && Imm11 == 0 && SrcA != RegisterConsts.RegisterZeroIndex; } - struct InstAtom + readonly struct InstAtom { - private ulong _opcode; + private readonly ulong _opcode; public InstAtom(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -917,9 +917,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool E => (_opcode & 0x1000000000000) != 0; } - struct InstAtomCas + readonly struct InstAtomCas { - private ulong _opcode; + private readonly ulong _opcode; public InstAtomCas(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -930,9 +930,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool E => (_opcode & 0x1000000000000) != 0; } - struct InstAtoms + readonly struct InstAtoms { - private ulong _opcode; + private readonly ulong _opcode; public InstAtoms(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -944,9 +944,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public AtomOp AtomOp => (AtomOp)((_opcode >> 52) & 0xF); } - struct InstAtomsCas + readonly struct InstAtomsCas { - private ulong _opcode; + private readonly ulong _opcode; public InstAtomsCas(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -956,9 +956,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int AtomsBcRz => (int)((_opcode >> 28) & 0x3); } - struct InstB2r + readonly struct InstB2r { - private ulong _opcode; + private readonly ulong _opcode; public InstB2r(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int DestPred => (int)((_opcode >> 45) & 0x7); @@ -968,9 +968,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public BarMode Mode => (BarMode)((_opcode >> 32) & 0x3); } - struct InstBar + readonly struct InstBar { - private ulong _opcode; + private readonly ulong _opcode; public InstBar(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm12 => (int)((_opcode >> 20) & 0xFFF); @@ -984,9 +984,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BFixBar => (_opcode & 0x80000000000) != 0; } - struct InstBfeR + readonly struct InstBfeR { - private ulong _opcode; + private readonly ulong _opcode; public InstBfeR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -998,9 +998,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Brev => (_opcode & 0x10000000000) != 0; } - struct InstBfeI + readonly struct InstBfeI { - private ulong _opcode; + private readonly ulong _opcode; public InstBfeI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1012,9 +1012,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Brev => (_opcode & 0x10000000000) != 0; } - struct InstBfeC + readonly struct InstBfeC { - private ulong _opcode; + private readonly ulong _opcode; public InstBfeC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1027,9 +1027,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Brev => (_opcode & 0x10000000000) != 0; } - struct InstBfiR + readonly struct InstBfiR { - private ulong _opcode; + private readonly ulong _opcode; public InstBfiR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1040,9 +1040,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool WriteCC => (_opcode & 0x800000000000) != 0; } - struct InstBfiI + readonly struct InstBfiI { - private ulong _opcode; + private readonly ulong _opcode; public InstBfiI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1053,9 +1053,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool WriteCC => (_opcode & 0x800000000000) != 0; } - struct InstBfiC + readonly struct InstBfiC { - private ulong _opcode; + private readonly ulong _opcode; public InstBfiC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1067,9 +1067,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool WriteCC => (_opcode & 0x800000000000) != 0; } - struct InstBfiRc + readonly struct InstBfiRc { - private ulong _opcode; + private readonly ulong _opcode; public InstBfiRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1081,17 +1081,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool WriteCC => (_opcode & 0x800000000000) != 0; } - struct InstBpt + readonly struct InstBpt { - private ulong _opcode; + private readonly ulong _opcode; public InstBpt(ulong opcode) => _opcode = opcode; public int Imm20 => (int)((_opcode >> 20) & 0xFFFFF); public Bpt Bpt => (Bpt)((_opcode >> 6) & 0x7); } - struct InstBra + readonly struct InstBra { - private ulong _opcode; + private readonly ulong _opcode; public InstBra(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1102,18 +1102,18 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool U => (_opcode & 0x80) != 0; } - struct InstBrk + readonly struct InstBrk { - private ulong _opcode; + private readonly ulong _opcode; public InstBrk(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public Ccc Ccc => (Ccc)(_opcode & 0x1F); } - struct InstBrx + readonly struct InstBrx { - private ulong _opcode; + private readonly ulong _opcode; public InstBrx(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1124,18 +1124,18 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Lmt => (_opcode & 0x40) != 0; } - struct InstCal + readonly struct InstCal { - private ulong _opcode; + private readonly ulong _opcode; public InstCal(ulong opcode) => _opcode = opcode; public bool Ca => (_opcode & 0x20) != 0; public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Inc => (_opcode & 0x40) != 0; } - struct InstCctl + readonly struct InstCctl { - private ulong _opcode; + private readonly ulong _opcode; public InstCctl(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1146,9 +1146,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public CctlOp CctlOp => (CctlOp)(_opcode & 0xF); } - struct InstCctll + readonly struct InstCctll { - private ulong _opcode; + private readonly ulong _opcode; public InstCctll(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1158,9 +1158,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public CctlOp CctlOp => (CctlOp)(_opcode & 0xF); } - struct InstCctlt + readonly struct InstCctlt { - private ulong _opcode; + private readonly ulong _opcode; public InstCctlt(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1168,26 +1168,26 @@ namespace Ryujinx.Graphics.Shader.Decoders public CctltOp CctltOp => (CctltOp)(_opcode & 0x3); } - struct InstCctltR + readonly struct InstCctltR { - private ulong _opcode; + private readonly ulong _opcode; public InstCctltR(ulong opcode) => _opcode = opcode; public int SrcC => (int)((_opcode >> 39) & 0xFF); public CctltOp CctltOp => (CctltOp)(_opcode & 0x3); } - struct InstCont + readonly struct InstCont { - private ulong _opcode; + private readonly ulong _opcode; public InstCont(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public Ccc Ccc => (Ccc)(_opcode & 0x1F); } - struct InstCset + readonly struct InstCset { - private ulong _opcode; + private readonly ulong _opcode; public InstCset(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1200,9 +1200,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public BoolOp Bop => (BoolOp)((_opcode >> 45) & 0x3); } - struct InstCsetp + readonly struct InstCsetp { - private ulong _opcode; + private readonly ulong _opcode; public InstCsetp(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1215,9 +1215,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public BoolOp Bop => (BoolOp)((_opcode >> 45) & 0x3); } - struct InstCs2r + readonly struct InstCs2r { - private ulong _opcode; + private readonly ulong _opcode; public InstCs2r(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -1225,9 +1225,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SReg SReg => (SReg)((_opcode >> 20) & 0xFF); } - struct InstDaddR + readonly struct InstDaddR { - private ulong _opcode; + private readonly ulong _opcode; public InstDaddR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1242,9 +1242,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode RoundMode => (RoundMode)((_opcode >> 39) & 0x3); } - struct InstDaddI + readonly struct InstDaddI { - private ulong _opcode; + private readonly ulong _opcode; public InstDaddI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1259,9 +1259,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode RoundMode => (RoundMode)((_opcode >> 39) & 0x3); } - struct InstDaddC + readonly struct InstDaddC { - private ulong _opcode; + private readonly ulong _opcode; public InstDaddC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1277,9 +1277,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode RoundMode => (RoundMode)((_opcode >> 39) & 0x3); } - struct InstDepbar + readonly struct InstDepbar { - private ulong _opcode; + private readonly ulong _opcode; public InstDepbar(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1289,9 +1289,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm6 => (int)(_opcode & 0x3F); } - struct InstDfmaR + readonly struct InstDfmaR { - private ulong _opcode; + private readonly ulong _opcode; public InstDfmaR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1305,9 +1305,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegA => (_opcode & 0x1000000000000) != 0; } - struct InstDfmaI + readonly struct InstDfmaI { - private ulong _opcode; + private readonly ulong _opcode; public InstDfmaI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1321,9 +1321,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegA => (_opcode & 0x1000000000000) != 0; } - struct InstDfmaC + readonly struct InstDfmaC { - private ulong _opcode; + private readonly ulong _opcode; public InstDfmaC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1338,9 +1338,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegA => (_opcode & 0x1000000000000) != 0; } - struct InstDfmaRc + readonly struct InstDfmaRc { - private ulong _opcode; + private readonly ulong _opcode; public InstDfmaRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1355,9 +1355,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegA => (_opcode & 0x1000000000000) != 0; } - struct InstDmnmxR + readonly struct InstDmnmxR { - private ulong _opcode; + private readonly ulong _opcode; public InstDmnmxR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1373,9 +1373,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstDmnmxI + readonly struct InstDmnmxI { - private ulong _opcode; + private readonly ulong _opcode; public InstDmnmxI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1391,9 +1391,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstDmnmxC + readonly struct InstDmnmxC { - private ulong _opcode; + private readonly ulong _opcode; public InstDmnmxC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1410,9 +1410,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstDmulR + readonly struct InstDmulR { - private ulong _opcode; + private readonly ulong _opcode; public InstDmulR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1424,9 +1424,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegA => (_opcode & 0x1000000000000) != 0; } - struct InstDmulI + readonly struct InstDmulI { - private ulong _opcode; + private readonly ulong _opcode; public InstDmulI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1438,9 +1438,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegA => (_opcode & 0x1000000000000) != 0; } - struct InstDmulC + readonly struct InstDmulC { - private ulong _opcode; + private readonly ulong _opcode; public InstDmulC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1453,9 +1453,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegA => (_opcode & 0x1000000000000) != 0; } - struct InstDsetR + readonly struct InstDsetR { - private ulong _opcode; + private readonly ulong _opcode; public InstDsetR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1474,9 +1474,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstDsetI + readonly struct InstDsetI { - private ulong _opcode; + private readonly ulong _opcode; public InstDsetI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1495,9 +1495,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstDsetC + readonly struct InstDsetC { - private ulong _opcode; + private readonly ulong _opcode; public InstDsetC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1517,9 +1517,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstDsetpR + readonly struct InstDsetpR { - private ulong _opcode; + private readonly ulong _opcode; public InstDsetpR(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -1537,9 +1537,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPredInv => (int)(_opcode & 0x7); } - struct InstDsetpI + readonly struct InstDsetpI { - private ulong _opcode; + private readonly ulong _opcode; public InstDsetpI(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -1557,9 +1557,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPredInv => (int)(_opcode & 0x7); } - struct InstDsetpC + readonly struct InstDsetpC { - private ulong _opcode; + private readonly ulong _opcode; public InstDsetpC(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -1578,9 +1578,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPredInv => (int)(_opcode & 0x7); } - struct InstExit + readonly struct InstExit { - private ulong _opcode; + private readonly ulong _opcode; public InstExit(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -1588,9 +1588,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool KeepRefCnt => (_opcode & 0x20) != 0; } - struct InstF2fR + readonly struct InstF2fR { - private ulong _opcode; + private readonly ulong _opcode; public InstF2fR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -1607,9 +1607,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Sat => (_opcode & 0x4000000000000) != 0; } - struct InstF2fI + readonly struct InstF2fI { - private ulong _opcode; + private readonly ulong _opcode; public InstF2fI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -1626,9 +1626,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Sat => (_opcode & 0x4000000000000) != 0; } - struct InstF2fC + readonly struct InstF2fC { - private ulong _opcode; + private readonly ulong _opcode; public InstF2fC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -1646,9 +1646,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Sat => (_opcode & 0x4000000000000) != 0; } - struct InstF2iR + readonly struct InstF2iR { - private ulong _opcode; + private readonly ulong _opcode; public InstF2iR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -1664,9 +1664,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode2 RoundMode => (RoundMode2)((_opcode >> 39) & 0x3); } - struct InstF2iI + readonly struct InstF2iI { - private ulong _opcode; + private readonly ulong _opcode; public InstF2iI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -1682,9 +1682,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode2 RoundMode => (RoundMode2)((_opcode >> 39) & 0x3); } - struct InstF2iC + readonly struct InstF2iC { - private ulong _opcode; + private readonly ulong _opcode; public InstF2iC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -1701,9 +1701,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode2 RoundMode => (RoundMode2)((_opcode >> 39) & 0x3); } - struct InstFaddR + readonly struct InstFaddR { - private ulong _opcode; + private readonly ulong _opcode; public InstFaddR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1720,9 +1720,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode RoundMode => (RoundMode)((_opcode >> 39) & 0x3); } - struct InstFaddI + readonly struct InstFaddI { - private ulong _opcode; + private readonly ulong _opcode; public InstFaddI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1739,9 +1739,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode RoundMode => (RoundMode)((_opcode >> 39) & 0x3); } - struct InstFaddC + readonly struct InstFaddC { - private ulong _opcode; + private readonly ulong _opcode; public InstFaddC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1759,9 +1759,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public RoundMode RoundMode => (RoundMode)((_opcode >> 39) & 0x3); } - struct InstFadd32i + readonly struct InstFadd32i { - private ulong _opcode; + private readonly ulong _opcode; public InstFadd32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1776,9 +1776,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x20000000000000) != 0; } - struct InstFchkR + readonly struct InstFchkR { - private ulong _opcode; + private readonly ulong _opcode; public InstFchkR(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1792,9 +1792,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ChkModeF ChkModeF => (ChkModeF)((_opcode >> 39) & 0x3F); } - struct InstFchkI + readonly struct InstFchkI { - private ulong _opcode; + private readonly ulong _opcode; public InstFchkI(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1808,9 +1808,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ChkModeF ChkModeF => (ChkModeF)((_opcode >> 39) & 0x3F); } - struct InstFchkC + readonly struct InstFchkC { - private ulong _opcode; + private readonly ulong _opcode; public InstFchkC(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1825,9 +1825,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ChkModeF ChkModeF => (ChkModeF)((_opcode >> 39) & 0x3F); } - struct InstFcmpR + readonly struct InstFcmpR { - private ulong _opcode; + private readonly ulong _opcode; public InstFcmpR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1839,9 +1839,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x800000000000) != 0; } - struct InstFcmpI + readonly struct InstFcmpI { - private ulong _opcode; + private readonly ulong _opcode; public InstFcmpI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1853,9 +1853,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x800000000000) != 0; } - struct InstFcmpC + readonly struct InstFcmpC { - private ulong _opcode; + private readonly ulong _opcode; public InstFcmpC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1868,9 +1868,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x800000000000) != 0; } - struct InstFcmpRc + readonly struct InstFcmpRc { - private ulong _opcode; + private readonly ulong _opcode; public InstFcmpRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1883,9 +1883,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x800000000000) != 0; } - struct InstFfmaR + readonly struct InstFfmaR { - private ulong _opcode; + private readonly ulong _opcode; public InstFfmaR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1901,9 +1901,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 53) & 0x3); } - struct InstFfmaI + readonly struct InstFfmaI { - private ulong _opcode; + private readonly ulong _opcode; public InstFfmaI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1919,9 +1919,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 53) & 0x3); } - struct InstFfmaC + readonly struct InstFfmaC { - private ulong _opcode; + private readonly ulong _opcode; public InstFfmaC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1938,9 +1938,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 53) & 0x3); } - struct InstFfmaRc + readonly struct InstFfmaRc { - private ulong _opcode; + private readonly ulong _opcode; public InstFfmaRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1957,9 +1957,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 53) & 0x3); } - struct InstFfma32i + readonly struct InstFfma32i { - private ulong _opcode; + private readonly ulong _opcode; public InstFfma32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -1973,9 +1973,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 53) & 0x3); } - struct InstFloR + readonly struct InstFloR { - private ulong _opcode; + private readonly ulong _opcode; public InstFloR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -1987,9 +1987,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstFloI + readonly struct InstFloI { - private ulong _opcode; + private readonly ulong _opcode; public InstFloI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -2001,9 +2001,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstFloC + readonly struct InstFloC { - private ulong _opcode; + private readonly ulong _opcode; public InstFloC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -2016,9 +2016,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstFmnmxR + readonly struct InstFmnmxR { - private ulong _opcode; + private readonly ulong _opcode; public InstFmnmxR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2035,9 +2035,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstFmnmxI + readonly struct InstFmnmxI { - private ulong _opcode; + private readonly ulong _opcode; public InstFmnmxI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2054,9 +2054,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstFmnmxC + readonly struct InstFmnmxC { - private ulong _opcode; + private readonly ulong _opcode; public InstFmnmxC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2074,9 +2074,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstFmulR + readonly struct InstFmulR { - private ulong _opcode; + private readonly ulong _opcode; public InstFmulR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2091,9 +2091,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Sat => (_opcode & 0x4000000000000) != 0; } - struct InstFmulI + readonly struct InstFmulI { - private ulong _opcode; + private readonly ulong _opcode; public InstFmulI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2108,9 +2108,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Sat => (_opcode & 0x4000000000000) != 0; } - struct InstFmulC + readonly struct InstFmulC { - private ulong _opcode; + private readonly ulong _opcode; public InstFmulC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2126,9 +2126,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Sat => (_opcode & 0x4000000000000) != 0; } - struct InstFmul32i + readonly struct InstFmul32i { - private ulong _opcode; + private readonly ulong _opcode; public InstFmul32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2140,9 +2140,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool WriteCC => (_opcode & 0x10000000000000) != 0; } - struct InstFsetR + readonly struct InstFsetR { - private ulong _opcode; + private readonly ulong _opcode; public InstFsetR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2162,9 +2162,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVal => (_opcode & 0x10000000000000) != 0; } - struct InstFsetC + readonly struct InstFsetC { - private ulong _opcode; + private readonly ulong _opcode; public InstFsetC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2185,9 +2185,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVal => (_opcode & 0x10000000000000) != 0; } - struct InstFsetI + readonly struct InstFsetI { - private ulong _opcode; + private readonly ulong _opcode; public InstFsetI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2207,9 +2207,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVal => (_opcode & 0x10000000000000) != 0; } - struct InstFsetpR + readonly struct InstFsetpR { - private ulong _opcode; + private readonly ulong _opcode; public InstFsetpR(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int DestPredInv => (int)(_opcode & 0x7); @@ -2229,9 +2229,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x800000000000) != 0; } - struct InstFsetpI + readonly struct InstFsetpI { - private ulong _opcode; + private readonly ulong _opcode; public InstFsetpI(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int DestPredInv => (int)(_opcode & 0x7); @@ -2251,9 +2251,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x800000000000) != 0; } - struct InstFsetpC + readonly struct InstFsetpC { - private ulong _opcode; + private readonly ulong _opcode; public InstFsetpC(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int DestPredInv => (int)(_opcode & 0x7); @@ -2274,9 +2274,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x800000000000) != 0; } - struct InstFswzadd + readonly struct InstFswzadd { - private ulong _opcode; + private readonly ulong _opcode; public InstFswzadd(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2290,23 +2290,23 @@ namespace Ryujinx.Graphics.Shader.Decoders public int PnWord => (int)((_opcode >> 28) & 0xFF); } - struct InstGetcrsptr + readonly struct InstGetcrsptr { - private ulong _opcode; + private readonly ulong _opcode; public InstGetcrsptr(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); } - struct InstGetlmembase + readonly struct InstGetlmembase { - private ulong _opcode; + private readonly ulong _opcode; public InstGetlmembase(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); } - struct InstHadd2R + readonly struct InstHadd2R { - private ulong _opcode; + private readonly ulong _opcode; public InstHadd2R(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2324,9 +2324,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x8000000000) != 0; } - struct InstHadd2I + readonly struct InstHadd2I { - private ulong _opcode; + private readonly ulong _opcode; public InstHadd2I(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2342,9 +2342,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x8000000000) != 0; } - struct InstHadd2C + readonly struct InstHadd2C { - private ulong _opcode; + private readonly ulong _opcode; public InstHadd2C(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2362,9 +2362,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x8000000000) != 0; } - struct InstHadd232i + readonly struct InstHadd232i { - private ulong _opcode; + private readonly ulong _opcode; public InstHadd232i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2377,9 +2377,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x80000000000000) != 0; } - struct InstHfma2R + readonly struct InstHfma2R { - private ulong _opcode; + private readonly ulong _opcode; public InstHfma2R(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2397,9 +2397,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 37) & 0x3); } - struct InstHfma2I + readonly struct InstHfma2I { - private ulong _opcode; + private readonly ulong _opcode; public InstHfma2I(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2416,9 +2416,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 57) & 0x3); } - struct InstHfma2C + readonly struct InstHfma2C { - private ulong _opcode; + private readonly ulong _opcode; public InstHfma2C(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2436,9 +2436,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 57) & 0x3); } - struct InstHfma2Rc + readonly struct InstHfma2Rc { - private ulong _opcode; + private readonly ulong _opcode; public InstHfma2Rc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2456,9 +2456,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 57) & 0x3); } - struct InstHfma232i + readonly struct InstHfma232i { - private ulong _opcode; + private readonly ulong _opcode; public InstHfma232i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2470,9 +2470,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 57) & 0x3); } - struct InstHmul2R + readonly struct InstHmul2R { - private ulong _opcode; + private readonly ulong _opcode; public InstHmul2R(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2489,9 +2489,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 39) & 0x3); } - struct InstHmul2I + readonly struct InstHmul2I { - private ulong _opcode; + private readonly ulong _opcode; public InstHmul2I(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2507,9 +2507,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 39) & 0x3); } - struct InstHmul2C + readonly struct InstHmul2C { - private ulong _opcode; + private readonly ulong _opcode; public InstHmul2C(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2526,9 +2526,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 39) & 0x3); } - struct InstHmul232i + readonly struct InstHmul232i { - private ulong _opcode; + private readonly ulong _opcode; public InstHmul232i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2540,9 +2540,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Fmz Fmz => (Fmz)((_opcode >> 55) & 0x3); } - struct InstHset2R + readonly struct InstHset2R { - private ulong _opcode; + private readonly ulong _opcode; public InstHset2R(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2563,9 +2563,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x4000000000000) != 0; } - struct InstHset2I + readonly struct InstHset2I { - private ulong _opcode; + private readonly ulong _opcode; public InstHset2I(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2584,9 +2584,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x40000000000000) != 0; } - struct InstHset2C + readonly struct InstHset2C { - private ulong _opcode; + private readonly ulong _opcode; public InstHset2C(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2606,9 +2606,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ftz => (_opcode & 0x40000000000000) != 0; } - struct InstHsetp2R + readonly struct InstHsetp2R { - private ulong _opcode; + private readonly ulong _opcode; public InstHsetp2R(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int DestPredInv => (int)(_opcode & 0x7); @@ -2630,9 +2630,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public HalfSwizzle BSwizzle => (HalfSwizzle)((_opcode >> 28) & 0x3); } - struct InstHsetp2I + readonly struct InstHsetp2I { - private ulong _opcode; + private readonly ulong _opcode; public InstHsetp2I(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int DestPredInv => (int)(_opcode & 0x7); @@ -2652,9 +2652,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public HalfSwizzle ASwizzle => (HalfSwizzle)((_opcode >> 47) & 0x3); } - struct InstHsetp2C + readonly struct InstHsetp2C { - private ulong _opcode; + private readonly ulong _opcode; public InstHsetp2C(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int DestPredInv => (int)(_opcode & 0x7); @@ -2676,9 +2676,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public HalfSwizzle ASwizzle => (HalfSwizzle)((_opcode >> 47) & 0x3); } - struct InstI2fR + readonly struct InstI2fR { - private ulong _opcode; + private readonly ulong _opcode; public InstI2fR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -2693,9 +2693,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public DstFmt DstFmt => (DstFmt)((_opcode >> 8) & 0x3); } - struct InstI2fI + readonly struct InstI2fI { - private ulong _opcode; + private readonly ulong _opcode; public InstI2fI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -2710,9 +2710,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public DstFmt DstFmt => (DstFmt)((_opcode >> 8) & 0x3); } - struct InstI2fC + readonly struct InstI2fC { - private ulong _opcode; + private readonly ulong _opcode; public InstI2fC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -2728,9 +2728,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public DstFmt DstFmt => (DstFmt)((_opcode >> 8) & 0x3); } - struct InstI2iR + readonly struct InstI2iR { - private ulong _opcode; + private readonly ulong _opcode; public InstI2iR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -2745,9 +2745,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ISrcDstFmt ISrcFmt => (ISrcDstFmt)((int)((_opcode >> 11) & 0x4) | (int)((_opcode >> 10) & 0x3)); } - struct InstI2iI + readonly struct InstI2iI { - private ulong _opcode; + private readonly ulong _opcode; public InstI2iI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -2762,9 +2762,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ISrcDstFmt ISrcFmt => (ISrcDstFmt)((int)((_opcode >> 11) & 0x4) | (int)((_opcode >> 10) & 0x3)); } - struct InstI2iC + readonly struct InstI2iC { - private ulong _opcode; + private readonly ulong _opcode; public InstI2iC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -2780,9 +2780,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ISrcDstFmt ISrcFmt => (ISrcDstFmt)((int)((_opcode >> 11) & 0x4) | (int)((_opcode >> 10) & 0x3)); } - struct InstIaddR + readonly struct InstIaddR { - private ulong _opcode; + private readonly ulong _opcode; public InstIaddR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2795,9 +2795,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x80000000000) != 0; } - struct InstIaddI + readonly struct InstIaddI { - private ulong _opcode; + private readonly ulong _opcode; public InstIaddI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2810,9 +2810,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x80000000000) != 0; } - struct InstIaddC + readonly struct InstIaddC { - private ulong _opcode; + private readonly ulong _opcode; public InstIaddC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2826,9 +2826,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x80000000000) != 0; } - struct InstIadd32i + readonly struct InstIadd32i { - private ulong _opcode; + private readonly ulong _opcode; public InstIadd32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2841,9 +2841,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x20000000000000) != 0; } - struct InstIadd3R + readonly struct InstIadd3R { - private ulong _opcode; + private readonly ulong _opcode; public InstIadd3R(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2862,9 +2862,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public HalfSelect Cpart => (HalfSelect)((_opcode >> 31) & 0x3); } - struct InstIadd3I + readonly struct InstIadd3I { - private ulong _opcode; + private readonly ulong _opcode; public InstIadd3I(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2879,9 +2879,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x1000000000000) != 0; } - struct InstIadd3C + readonly struct InstIadd3C { - private ulong _opcode; + private readonly ulong _opcode; public InstIadd3C(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2897,9 +2897,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x1000000000000) != 0; } - struct InstIcmpR + readonly struct InstIcmpR { - private ulong _opcode; + private readonly ulong _opcode; public InstIcmpR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2911,9 +2911,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Signed => (_opcode & 0x1000000000000) != 0; } - struct InstIcmpI + readonly struct InstIcmpI { - private ulong _opcode; + private readonly ulong _opcode; public InstIcmpI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2925,9 +2925,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Signed => (_opcode & 0x1000000000000) != 0; } - struct InstIcmpC + readonly struct InstIcmpC { - private ulong _opcode; + private readonly ulong _opcode; public InstIcmpC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2940,9 +2940,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Signed => (_opcode & 0x1000000000000) != 0; } - struct InstIcmpRc + readonly struct InstIcmpRc { - private ulong _opcode; + private readonly ulong _opcode; public InstIcmpRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2955,17 +2955,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Signed => (_opcode & 0x1000000000000) != 0; } - struct InstIde + readonly struct InstIde { - private ulong _opcode; + private readonly ulong _opcode; public InstIde(ulong opcode) => _opcode = opcode; public int Imm16 => (int)((_opcode >> 20) & 0xFFFF); public bool Di => (_opcode & 0x20) != 0; } - struct InstIdpR + readonly struct InstIdpR { - private ulong _opcode; + private readonly ulong _opcode; public InstIdpR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2979,9 +2979,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcBSign => (_opcode & 0x800000000000) != 0; } - struct InstIdpC + readonly struct InstIdpC { - private ulong _opcode; + private readonly ulong _opcode; public InstIdpC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -2996,9 +2996,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcBSign => (_opcode & 0x800000000000) != 0; } - struct InstImadR + readonly struct InstImadR { - private ulong _opcode; + private readonly ulong _opcode; public InstImadR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3015,9 +3015,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool ASigned => (_opcode & 0x1000000000000) != 0; } - struct InstImadI + readonly struct InstImadI { - private ulong _opcode; + private readonly ulong _opcode; public InstImadI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3034,9 +3034,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool ASigned => (_opcode & 0x1000000000000) != 0; } - struct InstImadC + readonly struct InstImadC { - private ulong _opcode; + private readonly ulong _opcode; public InstImadC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3054,9 +3054,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool ASigned => (_opcode & 0x1000000000000) != 0; } - struct InstImadRc + readonly struct InstImadRc { - private ulong _opcode; + private readonly ulong _opcode; public InstImadRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3074,9 +3074,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool ASigned => (_opcode & 0x1000000000000) != 0; } - struct InstImad32i + readonly struct InstImad32i { - private ulong _opcode; + private readonly ulong _opcode; public InstImad32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3090,9 +3090,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Hilo => (_opcode & 0x20000000000000) != 0; } - struct InstImadspR + readonly struct InstImadspR { - private ulong _opcode; + private readonly ulong _opcode; public InstImadspR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3105,9 +3105,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ImadspASelect CSelect => (ImadspASelect)((int)((_opcode >> 50) & 0x6) | (int)((_opcode >> 48) & 0x1)); } - struct InstImadspI + readonly struct InstImadspI { - private ulong _opcode; + private readonly ulong _opcode; public InstImadspI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3120,9 +3120,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ImadspASelect CSelect => (ImadspASelect)((int)((_opcode >> 50) & 0x6) | (int)((_opcode >> 48) & 0x1)); } - struct InstImadspC + readonly struct InstImadspC { - private ulong _opcode; + private readonly ulong _opcode; public InstImadspC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3136,9 +3136,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ImadspASelect CSelect => (ImadspASelect)((int)((_opcode >> 50) & 0x6) | (int)((_opcode >> 48) & 0x1)); } - struct InstImadspRc + readonly struct InstImadspRc { - private ulong _opcode; + private readonly ulong _opcode; public InstImadspRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3152,9 +3152,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public ImadspASelect CSelect => (ImadspASelect)((int)((_opcode >> 50) & 0x6) | (int)((_opcode >> 48) & 0x1)); } - struct InstImnmxR + readonly struct InstImnmxR { - private ulong _opcode; + private readonly ulong _opcode; public InstImnmxR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3168,9 +3168,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstImnmxI + readonly struct InstImnmxI { - private ulong _opcode; + private readonly ulong _opcode; public InstImnmxI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3184,9 +3184,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstImnmxC + readonly struct InstImnmxC { - private ulong _opcode; + private readonly ulong _opcode; public InstImnmxC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3201,9 +3201,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstImulR + readonly struct InstImulR { - private ulong _opcode; + private readonly ulong _opcode; public InstImulR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3216,9 +3216,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Hilo => (_opcode & 0x8000000000) != 0; } - struct InstImulI + readonly struct InstImulI { - private ulong _opcode; + private readonly ulong _opcode; public InstImulI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3231,9 +3231,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Hilo => (_opcode & 0x8000000000) != 0; } - struct InstImulC + readonly struct InstImulC { - private ulong _opcode; + private readonly ulong _opcode; public InstImulC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3247,9 +3247,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Hilo => (_opcode & 0x8000000000) != 0; } - struct InstImul32i + readonly struct InstImul32i { - private ulong _opcode; + private readonly ulong _opcode; public InstImul32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3262,9 +3262,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool WriteCC => (_opcode & 0x10000000000000) != 0; } - struct InstIpa + readonly struct InstIpa { - private ulong _opcode; + private readonly ulong _opcode; public InstIpa(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3281,9 +3281,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x4000000000000) != 0; } - struct InstIsberd + readonly struct InstIsberd { - private ulong _opcode; + private readonly ulong _opcode; public InstIsberd(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3295,9 +3295,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool P => (_opcode & 0x80000000) != 0; } - struct InstIscaddR + readonly struct InstIscaddR { - private ulong _opcode; + private readonly ulong _opcode; public InstIscaddR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3309,9 +3309,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public AvgMode AvgMode => (AvgMode)((_opcode >> 48) & 0x3); } - struct InstIscaddI + readonly struct InstIscaddI { - private ulong _opcode; + private readonly ulong _opcode; public InstIscaddI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3323,9 +3323,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public AvgMode AvgMode => (AvgMode)((_opcode >> 48) & 0x3); } - struct InstIscaddC + readonly struct InstIscaddC { - private ulong _opcode; + private readonly ulong _opcode; public InstIscaddC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3338,9 +3338,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public AvgMode AvgMode => (AvgMode)((_opcode >> 48) & 0x3); } - struct InstIscadd32i + readonly struct InstIscadd32i { - private ulong _opcode; + private readonly ulong _opcode; public InstIscadd32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3351,9 +3351,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm5 => (int)((_opcode >> 53) & 0x1F); } - struct InstIsetR + readonly struct InstIsetR { - private ulong _opcode; + private readonly ulong _opcode; public InstIsetR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3370,9 +3370,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x80000000000) != 0; } - struct InstIsetI + readonly struct InstIsetI { - private ulong _opcode; + private readonly ulong _opcode; public InstIsetI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3389,9 +3389,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x80000000000) != 0; } - struct InstIsetC + readonly struct InstIsetC { - private ulong _opcode; + private readonly ulong _opcode; public InstIsetC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3409,9 +3409,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool X => (_opcode & 0x80000000000) != 0; } - struct InstIsetpR + readonly struct InstIsetpR { - private ulong _opcode; + private readonly ulong _opcode; public InstIsetpR(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -3427,9 +3427,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPredInv => (int)(_opcode & 0x7); } - struct InstIsetpI + readonly struct InstIsetpI { - private ulong _opcode; + private readonly ulong _opcode; public InstIsetpI(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -3445,9 +3445,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPredInv => (int)(_opcode & 0x7); } - struct InstIsetpC + readonly struct InstIsetpC { - private ulong _opcode; + private readonly ulong _opcode; public InstIsetpC(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -3464,18 +3464,18 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPredInv => (int)(_opcode & 0x7); } - struct InstJcal + readonly struct InstJcal { - private ulong _opcode; + private readonly ulong _opcode; public InstJcal(ulong opcode) => _opcode = opcode; public int Imm32 => (int)(_opcode >> 20); public bool Ca => (_opcode & 0x20) != 0; public bool Inc => (_opcode & 0x40) != 0; } - struct InstJmp + readonly struct InstJmp { - private ulong _opcode; + private readonly ulong _opcode; public InstJmp(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3487,9 +3487,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool U => (_opcode & 0x80) != 0; } - struct InstJmx + readonly struct InstJmx { - private ulong _opcode; + private readonly ulong _opcode; public InstJmx(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -3500,18 +3500,18 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Lmt => (_opcode & 0x40) != 0; } - struct InstKil + readonly struct InstKil { - private ulong _opcode; + private readonly ulong _opcode; public InstKil(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public Ccc Ccc => (Ccc)(_opcode & 0x1F); } - struct InstLd + readonly struct InstLd { - private ulong _opcode; + private readonly ulong _opcode; public InstLd(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3524,9 +3524,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm32 => (int)(_opcode >> 20); } - struct InstLdc + readonly struct InstLdc { - private ulong _opcode; + private readonly ulong _opcode; public InstLdc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3538,9 +3538,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int CbufOffset => (int)((_opcode >> 20) & 0xFFFF); } - struct InstLdg + readonly struct InstLdg { - private ulong _opcode; + private readonly ulong _opcode; public InstLdg(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3552,9 +3552,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); } - struct InstLdl + readonly struct InstLdl { - private ulong _opcode; + private readonly ulong _opcode; public InstLdl(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3565,9 +3565,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); } - struct InstLds + readonly struct InstLds { - private ulong _opcode; + private readonly ulong _opcode; public InstLds(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3578,9 +3578,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); } - struct InstLeaR + readonly struct InstLeaR { - private ulong _opcode; + private readonly ulong _opcode; public InstLeaR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3594,9 +3594,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 48) & 0x7); } - struct InstLeaI + readonly struct InstLeaI { - private ulong _opcode; + private readonly ulong _opcode; public InstLeaI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3610,9 +3610,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 48) & 0x7); } - struct InstLeaC + readonly struct InstLeaC { - private ulong _opcode; + private readonly ulong _opcode; public InstLeaC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3627,9 +3627,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 48) & 0x7); } - struct InstLeaHiR + readonly struct InstLeaHiR { - private ulong _opcode; + private readonly ulong _opcode; public InstLeaHiR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3644,9 +3644,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 48) & 0x7); } - struct InstLeaHiC + readonly struct InstLeaHiC { - private ulong _opcode; + private readonly ulong _opcode; public InstLeaHiC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3662,22 +3662,24 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 48) & 0x7); } - struct InstLepc + readonly struct InstLepc { - private ulong _opcode; +#pragma warning disable IDE0052 // Remove unread private member + private readonly ulong _opcode; +#pragma warning restore IDE0052 public InstLepc(ulong opcode) => _opcode = opcode; } - struct InstLongjmp + readonly struct InstLongjmp { - private ulong _opcode; + private readonly ulong _opcode; public InstLongjmp(ulong opcode) => _opcode = opcode; public Ccc Ccc => (Ccc)(_opcode & 0x1F); } - struct InstLopR + readonly struct InstLopR { - private ulong _opcode; + private readonly ulong _opcode; public InstLopR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3693,9 +3695,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstLopI + readonly struct InstLopI { - private ulong _opcode; + private readonly ulong _opcode; public InstLopI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3711,9 +3713,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstLopC + readonly struct InstLopC { - private ulong _opcode; + private readonly ulong _opcode; public InstLopC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3730,9 +3732,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstLop3R + readonly struct InstLop3R { - private ulong _opcode; + private readonly ulong _opcode; public InstLop3R(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3747,9 +3749,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm => (int)((_opcode >> 28) & 0xFF); } - struct InstLop3I + readonly struct InstLop3I { - private ulong _opcode; + private readonly ulong _opcode; public InstLop3I(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3762,9 +3764,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm => (int)((_opcode >> 48) & 0xFF); } - struct InstLop3C + readonly struct InstLop3C { - private ulong _opcode; + private readonly ulong _opcode; public InstLop3C(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3778,9 +3780,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm => (int)((_opcode >> 48) & 0xFF); } - struct InstLop32i + readonly struct InstLop32i { - private ulong _opcode; + private readonly ulong _opcode; public InstLop32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3794,9 +3796,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x100000000000000) != 0; } - struct InstMembar + readonly struct InstMembar { - private ulong _opcode; + private readonly ulong _opcode; public InstMembar(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3804,9 +3806,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Ivall Ivall => (Ivall)(_opcode & 0x3); } - struct InstMovR + readonly struct InstMovR { - private ulong _opcode; + private readonly ulong _opcode; public InstMovR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 20) & 0xFF); @@ -3815,9 +3817,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int QuadMask => (int)((_opcode >> 39) & 0xF); } - struct InstMovI + readonly struct InstMovI { - private ulong _opcode; + private readonly ulong _opcode; public InstMovI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -3826,9 +3828,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int QuadMask => (int)((_opcode >> 39) & 0xF); } - struct InstMovC + readonly struct InstMovC { - private ulong _opcode; + private readonly ulong _opcode; public InstMovC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -3838,9 +3840,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int QuadMask => (int)((_opcode >> 39) & 0xF); } - struct InstMov32i + readonly struct InstMov32i { - private ulong _opcode; + private readonly ulong _opcode; public InstMov32i(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm32 => (int)(_opcode >> 20); @@ -3849,9 +3851,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int QuadMask => (int)((_opcode >> 12) & 0xF); } - struct InstMufu + readonly struct InstMufu { - private ulong _opcode; + private readonly ulong _opcode; public InstMufu(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3863,9 +3865,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Sat => (_opcode & 0x4000000000000) != 0; } - struct InstNop + readonly struct InstNop { - private ulong _opcode; + private readonly ulong _opcode; public InstNop(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -3874,9 +3876,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public Ccc Ccc => (Ccc)((_opcode >> 8) & 0x1F); } - struct InstOutR + readonly struct InstOutR { - private ulong _opcode; + private readonly ulong _opcode; public InstOutR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3886,9 +3888,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public OutType OutType => (OutType)((_opcode >> 39) & 0x3); } - struct InstOutI + readonly struct InstOutI { - private ulong _opcode; + private readonly ulong _opcode; public InstOutI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3898,9 +3900,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public OutType OutType => (OutType)((_opcode >> 39) & 0x3); } - struct InstOutC + readonly struct InstOutC { - private ulong _opcode; + private readonly ulong _opcode; public InstOutC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3911,9 +3913,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public OutType OutType => (OutType)((_opcode >> 39) & 0x3); } - struct InstP2rR + readonly struct InstP2rR { - private ulong _opcode; + private readonly ulong _opcode; public InstP2rR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3924,9 +3926,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ccpr => (_opcode & 0x10000000000) != 0; } - struct InstP2rI + readonly struct InstP2rI { - private ulong _opcode; + private readonly ulong _opcode; public InstP2rI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3937,9 +3939,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ccpr => (_opcode & 0x10000000000) != 0; } - struct InstP2rC + readonly struct InstP2rC { - private ulong _opcode; + private readonly ulong _opcode; public InstP2rC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3951,32 +3953,32 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ccpr => (_opcode & 0x10000000000) != 0; } - struct InstPbk + readonly struct InstPbk { - private ulong _opcode; + private readonly ulong _opcode; public InstPbk(ulong opcode) => _opcode = opcode; public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Ca => (_opcode & 0x20) != 0; } - struct InstPcnt + readonly struct InstPcnt { - private ulong _opcode; + private readonly ulong _opcode; public InstPcnt(ulong opcode) => _opcode = opcode; public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Ca => (_opcode & 0x20) != 0; } - struct InstPexit + readonly struct InstPexit { - private ulong _opcode; + private readonly ulong _opcode; public InstPexit(ulong opcode) => _opcode = opcode; public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); } - struct InstPixld + readonly struct InstPixld { - private ulong _opcode; + private readonly ulong _opcode; public InstPixld(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -3987,17 +3989,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm8 => (int)((_opcode >> 20) & 0xFF); } - struct InstPlongjmp + readonly struct InstPlongjmp { - private ulong _opcode; + private readonly ulong _opcode; public InstPlongjmp(ulong opcode) => _opcode = opcode; public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Ca => (_opcode & 0x20) != 0; } - struct InstPopcR + readonly struct InstPopcR { - private ulong _opcode; + private readonly ulong _opcode; public InstPopcR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -4006,9 +4008,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstPopcI + readonly struct InstPopcI { - private ulong _opcode; + private readonly ulong _opcode; public InstPopcI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -4017,9 +4019,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstPopcC + readonly struct InstPopcC { - private ulong _opcode; + private readonly ulong _opcode; public InstPopcC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -4029,18 +4031,18 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool NegB => (_opcode & 0x10000000000) != 0; } - struct InstPret + readonly struct InstPret { - private ulong _opcode; + private readonly ulong _opcode; public InstPret(ulong opcode) => _opcode = opcode; public bool Ca => (_opcode & 0x20) != 0; public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Inc => (_opcode & 0x40) != 0; } - struct InstPrmtR + readonly struct InstPrmtR { - private ulong _opcode; + private readonly ulong _opcode; public InstPrmtR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4051,9 +4053,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public PMode PMode => (PMode)((_opcode >> 48) & 0xF); } - struct InstPrmtI + readonly struct InstPrmtI { - private ulong _opcode; + private readonly ulong _opcode; public InstPrmtI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4064,9 +4066,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public PMode PMode => (PMode)((_opcode >> 48) & 0xF); } - struct InstPrmtC + readonly struct InstPrmtC { - private ulong _opcode; + private readonly ulong _opcode; public InstPrmtC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4078,9 +4080,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public PMode PMode => (PMode)((_opcode >> 48) & 0xF); } - struct InstPrmtRc + readonly struct InstPrmtRc { - private ulong _opcode; + private readonly ulong _opcode; public InstPrmtRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4092,9 +4094,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public PMode PMode => (PMode)((_opcode >> 48) & 0xF); } - struct InstPset + readonly struct InstPset { - private ulong _opcode; + private readonly ulong _opcode; public InstPset(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4111,9 +4113,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVal => (_opcode & 0x100000000000) != 0; } - struct InstPsetp + readonly struct InstPsetp { - private ulong _opcode; + private readonly ulong _opcode; public InstPsetp(ulong opcode) => _opcode = opcode; public int DestPred => (int)((_opcode >> 3) & 0x7); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4129,9 +4131,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public BoolOp BoolOpC => (BoolOp)((_opcode >> 45) & 0x3); } - struct InstR2b + readonly struct InstR2b { - private ulong _opcode; + private readonly ulong _opcode; public InstR2b(ulong opcode) => _opcode = opcode; public int SrcB => (int)((_opcode >> 20) & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4140,9 +4142,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Name => (int)((_opcode >> 28) & 0xF); } - struct InstR2pR + readonly struct InstR2pR { - private ulong _opcode; + private readonly ulong _opcode; public InstR2pR(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -4152,9 +4154,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ccpr => (_opcode & 0x10000000000) != 0; } - struct InstR2pI + readonly struct InstR2pI { - private ulong _opcode; + private readonly ulong _opcode; public InstR2pI(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -4164,9 +4166,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ccpr => (_opcode & 0x10000000000) != 0; } - struct InstR2pC + readonly struct InstR2pC { - private ulong _opcode; + private readonly ulong _opcode; public InstR2pC(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -4177,15 +4179,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ccpr => (_opcode & 0x10000000000) != 0; } - struct InstRam + readonly struct InstRam { - private ulong _opcode; +#pragma warning disable IDE0052 // Remove unread private member + private readonly ulong _opcode; +#pragma warning restore IDE0052 public InstRam(ulong opcode) => _opcode = opcode; } - struct InstRed + readonly struct InstRed { - private ulong _opcode; + private readonly ulong _opcode; public InstRed(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)(_opcode & 0xFF); @@ -4197,18 +4201,18 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool E => (_opcode & 0x1000000000000) != 0; } - struct InstRet + readonly struct InstRet { - private ulong _opcode; + private readonly ulong _opcode; public InstRet(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public Ccc Ccc => (Ccc)(_opcode & 0x1F); } - struct InstRroR + readonly struct InstRroR { - private ulong _opcode; + private readonly ulong _opcode; public InstRroR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -4219,9 +4223,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool RroOp => (_opcode & 0x8000000000) != 0; } - struct InstRroI + readonly struct InstRroI { - private ulong _opcode; + private readonly ulong _opcode; public InstRroI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Imm20 => (int)((_opcode >> 37) & 0x80000) | (int)((_opcode >> 20) & 0x7FFFF); @@ -4232,9 +4236,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool RroOp => (_opcode & 0x8000000000) != 0; } - struct InstRroC + readonly struct InstRroC { - private ulong _opcode; + private readonly ulong _opcode; public InstRroC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int CbufSlot => (int)((_opcode >> 34) & 0x1F); @@ -4246,15 +4250,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool RroOp => (_opcode & 0x8000000000) != 0; } - struct InstRtt + readonly struct InstRtt { - private ulong _opcode; +#pragma warning disable IDE0052 // Remove unread private member + private readonly ulong _opcode; +#pragma warning restore IDE0052 public InstRtt(ulong opcode) => _opcode = opcode; } - struct InstS2r + readonly struct InstS2r { - private ulong _opcode; + private readonly ulong _opcode; public InstS2r(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -4262,15 +4268,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public SReg SReg => (SReg)((_opcode >> 20) & 0xFF); } - struct InstSam + readonly struct InstSam { - private ulong _opcode; +#pragma warning disable IDE0052 // Remove unread private member + private readonly ulong _opcode; +#pragma warning restore IDE0052 public InstSam(ulong opcode) => _opcode = opcode; } - struct InstSelR + readonly struct InstSelR { - private ulong _opcode; + private readonly ulong _opcode; public InstSelR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4281,9 +4289,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstSelI + readonly struct InstSelI { - private ulong _opcode; + private readonly ulong _opcode; public InstSelI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4294,9 +4302,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstSelC + readonly struct InstSelC { - private ulong _opcode; + private readonly ulong _opcode; public InstSelC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4308,23 +4316,23 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool SrcPredInv => (_opcode & 0x40000000000) != 0; } - struct InstSetcrsptr + readonly struct InstSetcrsptr { - private ulong _opcode; + private readonly ulong _opcode; public InstSetcrsptr(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); } - struct InstSetlmembase + readonly struct InstSetlmembase { - private ulong _opcode; + private readonly ulong _opcode; public InstSetlmembase(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); } - struct InstShfLR + readonly struct InstShfLR { - private ulong _opcode; + private readonly ulong _opcode; public InstShfLR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4338,9 +4346,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public MaxShift MaxShift => (MaxShift)((_opcode >> 37) & 0x3); } - struct InstShfRR + readonly struct InstShfRR { - private ulong _opcode; + private readonly ulong _opcode; public InstShfRR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4354,9 +4362,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public MaxShift MaxShift => (MaxShift)((_opcode >> 37) & 0x3); } - struct InstShfLI + readonly struct InstShfLI { - private ulong _opcode; + private readonly ulong _opcode; public InstShfLI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4370,9 +4378,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm6 => (int)((_opcode >> 20) & 0x3F); } - struct InstShfRI + readonly struct InstShfRI { - private ulong _opcode; + private readonly ulong _opcode; public InstShfRI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4386,9 +4394,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm6 => (int)((_opcode >> 20) & 0x3F); } - struct InstShfl + readonly struct InstShfl { - private ulong _opcode; + private readonly ulong _opcode; public InstShfl(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4404,9 +4412,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int DestPred => (int)((_opcode >> 48) & 0x7); } - struct InstShlR + readonly struct InstShlR { - private ulong _opcode; + private readonly ulong _opcode; public InstShlR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4418,9 +4426,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool M => (_opcode & 0x8000000000) != 0; } - struct InstShlI + readonly struct InstShlI { - private ulong _opcode; + private readonly ulong _opcode; public InstShlI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4432,9 +4440,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool M => (_opcode & 0x8000000000) != 0; } - struct InstShlC + readonly struct InstShlC { - private ulong _opcode; + private readonly ulong _opcode; public InstShlC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4447,9 +4455,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool M => (_opcode & 0x8000000000) != 0; } - struct InstShrR + readonly struct InstShrR { - private ulong _opcode; + private readonly ulong _opcode; public InstShrR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4463,9 +4471,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool M => (_opcode & 0x8000000000) != 0; } - struct InstShrI + readonly struct InstShrI { - private ulong _opcode; + private readonly ulong _opcode; public InstShrI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4479,9 +4487,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool M => (_opcode & 0x8000000000) != 0; } - struct InstShrC + readonly struct InstShrC { - private ulong _opcode; + private readonly ulong _opcode; public InstShrC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4496,17 +4504,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool M => (_opcode & 0x8000000000) != 0; } - struct InstSsy + readonly struct InstSsy { - private ulong _opcode; + private readonly ulong _opcode; public InstSsy(ulong opcode) => _opcode = opcode; public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); public bool Ca => (_opcode & 0x20) != 0; } - struct InstSt + readonly struct InstSt { - private ulong _opcode; + private readonly ulong _opcode; public InstSt(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4519,9 +4527,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm32 => (int)(_opcode >> 20); } - struct InstStg + readonly struct InstStg { - private ulong _opcode; + private readonly ulong _opcode; public InstStg(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4533,9 +4541,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); } - struct InstStl + readonly struct InstStl { - private ulong _opcode; + private readonly ulong _opcode; public InstStl(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4546,17 +4554,17 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); } - struct InstStp + readonly struct InstStp { - private ulong _opcode; + private readonly ulong _opcode; public InstStp(ulong opcode) => _opcode = opcode; public bool Wait => (_opcode & 0x80000000) != 0; public int Imm8 => (int)((_opcode >> 20) & 0xFF); } - struct InstSts + readonly struct InstSts { - private ulong _opcode; + private readonly ulong _opcode; public InstSts(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4566,9 +4574,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm24 => (int)((_opcode >> 20) & 0xFFFFFF); } - struct InstSuatomB + readonly struct InstSuatomB { - private ulong _opcode; + private readonly ulong _opcode; public InstSuatomB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4583,9 +4591,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ba => (_opcode & 0x10000000) != 0; } - struct InstSuatom + readonly struct InstSuatom { - private ulong _opcode; + private readonly ulong _opcode; public InstSuatom(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4600,9 +4608,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ba => (_opcode & 0x10000000) != 0; } - struct InstSuatomB2 + readonly struct InstSuatomB2 { - private ulong _opcode; + private readonly ulong _opcode; public InstSuatomB2(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4618,9 +4626,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ba => (_opcode & 0x10000000) != 0; } - struct InstSuatomCasB + readonly struct InstSuatomCasB { - private ulong _opcode; + private readonly ulong _opcode; public InstSuatomCasB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4635,9 +4643,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ba => (_opcode & 0x10000000) != 0; } - struct InstSuatomCas + readonly struct InstSuatomCas { - private ulong _opcode; + private readonly ulong _opcode; public InstSuatomCas(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4652,9 +4660,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Ba => (_opcode & 0x10000000) != 0; } - struct InstSuldDB + readonly struct InstSuldDB { - private ulong _opcode; + private readonly ulong _opcode; public InstSuldDB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4669,9 +4677,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuSize Size => (SuSize)((_opcode >> 20) & 0x7); } - struct InstSuldD + readonly struct InstSuldD { - private ulong _opcode; + private readonly ulong _opcode; public InstSuldD(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4686,9 +4694,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuSize Size => (SuSize)((_opcode >> 20) & 0x7); } - struct InstSuldB + readonly struct InstSuldB { - private ulong _opcode; + private readonly ulong _opcode; public InstSuldB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4702,9 +4710,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuRgba Rgba => (SuRgba)((_opcode >> 20) & 0xF); } - struct InstSuld + readonly struct InstSuld { - private ulong _opcode; + private readonly ulong _opcode; public InstSuld(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4718,9 +4726,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuRgba Rgba => (SuRgba)((_opcode >> 20) & 0xF); } - struct InstSuredB + readonly struct InstSuredB { - private ulong _opcode; + private readonly ulong _opcode; public InstSuredB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4734,9 +4742,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuatomSize Size => (SuatomSize)((_opcode >> 20) & 0x7); } - struct InstSured + readonly struct InstSured { - private ulong _opcode; + private readonly ulong _opcode; public InstSured(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4750,9 +4758,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuatomSize Size => (SuatomSize)((_opcode >> 20) & 0x7); } - struct InstSustDB + readonly struct InstSustDB { - private ulong _opcode; + private readonly ulong _opcode; public InstSustDB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4766,9 +4774,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuSize Size => (SuSize)((_opcode >> 20) & 0x7); } - struct InstSustD + readonly struct InstSustD { - private ulong _opcode; + private readonly ulong _opcode; public InstSustD(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4782,9 +4790,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuSize Size => (SuSize)((_opcode >> 20) & 0x7); } - struct InstSustB + readonly struct InstSustB { - private ulong _opcode; + private readonly ulong _opcode; public InstSustB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4797,9 +4805,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuRgba Rgba => (SuRgba)((_opcode >> 20) & 0xF); } - struct InstSust + readonly struct InstSust { - private ulong _opcode; + private readonly ulong _opcode; public InstSust(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4812,18 +4820,18 @@ namespace Ryujinx.Graphics.Shader.Decoders public SuRgba Rgba => (SuRgba)((_opcode >> 20) & 0xF); } - struct InstSync + readonly struct InstSync { - private ulong _opcode; + private readonly ulong _opcode; public InstSync(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; public Ccc Ccc => (Ccc)(_opcode & 0x1F); } - struct InstTex + readonly struct InstTex { - private ulong _opcode; + private readonly ulong _opcode; public InstTex(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4842,9 +4850,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Nodep => (_opcode & 0x2000000000000) != 0; } - struct InstTexB + readonly struct InstTexB { - private ulong _opcode; + private readonly ulong _opcode; public InstTexB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4862,9 +4870,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Nodep => (_opcode & 0x2000000000000) != 0; } - struct InstTexs + readonly struct InstTexs { - private ulong _opcode; + private readonly ulong _opcode; public InstTexs(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4878,9 +4886,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Dest2 => (int)((_opcode >> 28) & 0xFF); } - struct InstTld + readonly struct InstTld { - private ulong _opcode; + private readonly ulong _opcode; public InstTld(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4898,9 +4906,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexDim Dim => (TexDim)((_opcode >> 28) & 0x7); } - struct InstTldB + readonly struct InstTldB { - private ulong _opcode; + private readonly ulong _opcode; public InstTldB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4917,9 +4925,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexDim Dim => (TexDim)((_opcode >> 28) & 0x7); } - struct InstTlds + readonly struct InstTlds { - private ulong _opcode; + private readonly ulong _opcode; public InstTlds(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4933,9 +4941,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Dest2 => (int)((_opcode >> 28) & 0xFF); } - struct InstTld4 + readonly struct InstTld4 { - private ulong _opcode; + private readonly ulong _opcode; public InstTld4(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4954,9 +4962,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Nodep => (_opcode & 0x2000000000000) != 0; } - struct InstTld4B + readonly struct InstTld4B { - private ulong _opcode; + private readonly ulong _opcode; public InstTld4B(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4974,9 +4982,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Nodep => (_opcode & 0x2000000000000) != 0; } - struct InstTld4s + readonly struct InstTld4s { - private ulong _opcode; + private readonly ulong _opcode; public InstTld4s(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -4991,9 +4999,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Dest2 => (int)((_opcode >> 28) & 0xFF); } - struct InstTmml + readonly struct InstTmml { - private ulong _opcode; + private readonly ulong _opcode; public InstTmml(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5007,9 +5015,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexDim Dim => (TexDim)((_opcode >> 28) & 0x7); } - struct InstTmmlB + readonly struct InstTmmlB { - private ulong _opcode; + private readonly ulong _opcode; public InstTmmlB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5022,9 +5030,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexDim Dim => (TexDim)((_opcode >> 28) & 0x7); } - struct InstTxa + readonly struct InstTxa { - private ulong _opcode; + private readonly ulong _opcode; public InstTxa(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5036,9 +5044,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int WMask => (int)((_opcode >> 31) & 0xF); } - struct InstTxd + readonly struct InstTxd { - private ulong _opcode; + private readonly ulong _opcode; public InstTxd(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5054,9 +5062,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexDim Dim => (TexDim)((_opcode >> 28) & 0x7); } - struct InstTxdB + readonly struct InstTxdB { - private ulong _opcode; + private readonly ulong _opcode; public InstTxdB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5071,9 +5079,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexDim Dim => (TexDim)((_opcode >> 28) & 0x7); } - struct InstTxq + readonly struct InstTxq { - private ulong _opcode; + private readonly ulong _opcode; public InstTxq(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5085,9 +5093,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexQuery TexQuery => (TexQuery)((_opcode >> 22) & 0x3F); } - struct InstTxqB + readonly struct InstTxqB { - private ulong _opcode; + private readonly ulong _opcode; public InstTxqB(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5098,9 +5106,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public TexQuery TexQuery => (TexQuery)((_opcode >> 22) & 0x3F); } - struct InstVabsdiff + readonly struct InstVabsdiff { - private ulong _opcode; + private readonly ulong _opcode; public InstVabsdiff(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5117,9 +5125,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstVabsdiff4 + readonly struct InstVabsdiff4 { - private ulong _opcode; + private readonly ulong _opcode; public InstVabsdiff4(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5138,9 +5146,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public BSelect4 Bsel4 => (BSelect4)((_opcode >> 28) & 0xF); } - struct InstVadd + readonly struct InstVadd { - private ulong _opcode; + private readonly ulong _opcode; public InstVadd(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5159,9 +5167,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstVmad + readonly struct InstVmad { - private ulong _opcode; + private readonly ulong _opcode; public InstVmad(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5179,9 +5187,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstVmnmx + readonly struct InstVmnmx { - private ulong _opcode; + private readonly ulong _opcode; public InstVmnmx(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5200,9 +5208,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstVote + readonly struct InstVote { - private ulong _opcode; + private readonly ulong _opcode; public InstVote(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int Pred => (int)((_opcode >> 16) & 0x7); @@ -5213,9 +5221,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int VpDest => (int)((_opcode >> 45) & 0x7); } - struct InstVotevtg + readonly struct InstVotevtg { - private ulong _opcode; + private readonly ulong _opcode; public InstVotevtg(ulong opcode) => _opcode = opcode; public int Pred => (int)((_opcode >> 16) & 0x7); public bool PredInv => (_opcode & 0x80000) != 0; @@ -5223,9 +5231,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Imm28 => (int)((_opcode >> 20) & 0xFFFFFFF); } - struct InstVset + readonly struct InstVset { - private ulong _opcode; + private readonly ulong _opcode; public InstVset(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5241,9 +5249,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstVsetp + readonly struct InstVsetp { - private ulong _opcode; + private readonly ulong _opcode; public InstVsetp(ulong opcode) => _opcode = opcode; public int SrcA => (int)((_opcode >> 8) & 0xFF); public int SrcB => (int)((_opcode >> 20) & 0xFF); @@ -5261,9 +5269,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstVshl + readonly struct InstVshl { - private ulong _opcode; + private readonly ulong _opcode; public InstVshl(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5281,9 +5289,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstVshr + readonly struct InstVshr { - private ulong _opcode; + private readonly ulong _opcode; public InstVshr(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5301,9 +5309,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BVideo => (_opcode & 0x4000000000000) != 0; } - struct InstXmadR + readonly struct InstXmadR { - private ulong _opcode; + private readonly ulong _opcode; public InstXmadR(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5322,9 +5330,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool HiloB => (_opcode & 0x800000000) != 0; } - struct InstXmadI + readonly struct InstXmadI { - private ulong _opcode; + private readonly ulong _opcode; public InstXmadI(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5342,9 +5350,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Psl => (_opcode & 0x1000000000) != 0; } - struct InstXmadC + readonly struct InstXmadC { - private ulong _opcode; + private readonly ulong _opcode; public InstXmadC(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5364,9 +5372,9 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool ASigned => (_opcode & 0x1000000000000) != 0; } - struct InstXmadRc + readonly struct InstXmadRc { - private ulong _opcode; + private readonly ulong _opcode; public InstXmadRc(ulong opcode) => _opcode = opcode; public int Dest => (int)(_opcode & 0xFF); public int SrcA => (int)((_opcode >> 8) & 0xFF); @@ -5383,4 +5391,4 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool BSigned => (_opcode & 0x2000000000000) != 0; public bool ASigned => (_opcode & 0x1000000000000) != 0; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstName.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstName.cs index 9c79b7a5c..04ad9391f 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstName.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstName.cs @@ -185,4 +185,4 @@ namespace Ryujinx.Graphics.Shader.Decoders Vshr, Xmad, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstOp.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstOp.cs index 39244e647..045257dc6 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstOp.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstOp.cs @@ -24,4 +24,4 @@ namespace Ryujinx.Graphics.Shader.Decoders return (ulong)((long)Address + (((int)(RawOpCode >> 20) << 8) >> 8) + 8); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs index 3f39e631d..14cdcd060 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstProps.cs @@ -26,6 +26,6 @@ namespace Ryujinx.Graphics.Shader.Decoders Tex = 1 << 12, TexB = 1 << 13, Bra = 1 << 14, - NoPred = 1 << 15 + NoPred = 1 << 15, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/InstTable.cs b/src/Ryujinx.Graphics.Shader/Decoders/InstTable.cs index eaa77930b..35367b8df 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/InstTable.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/InstTable.cs @@ -24,13 +24,14 @@ namespace Ryujinx.Graphics.Shader.Decoders } } - private static TableEntry[] _opCodes; + private static readonly TableEntry[] _opCodes; static InstTable() { _opCodes = new TableEntry[1 << EncodingBits]; #region Instructions +#pragma warning disable IDE0055 // Disable formatting Add("1110111110100xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Al2p, InstEmit.Al2p, InstProps.Rd | InstProps.Ra); Add("1110111111011xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Ald, InstEmit.Ald, InstProps.Rd | InstProps.Ra); Add("1110111111110xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Ast, InstEmit.Ast, InstProps.Ra | InstProps.Rb2 | InstProps.Rc); @@ -325,6 +326,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Add("0011011x00xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Xmad, InstEmit.XmadI, InstProps.Rd | InstProps.Ra | InstProps.Ib | InstProps.Rc); Add("0100111xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Xmad, InstEmit.XmadC, InstProps.Rd | InstProps.Ra | InstProps.Rc); Add("010100010xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Xmad, InstEmit.XmadRc, InstProps.Rd | InstProps.Ra | InstProps.Rc); +#pragma warning restore IDE0055 #endregion } @@ -357,7 +359,7 @@ namespace Ryujinx.Graphics.Shader.Decoders xMask = ~xMask; - TableEntry entry = new TableEntry(name, emitter, props, xBits); + TableEntry entry = new(name, emitter, props, xBits); for (int index = 0; index < (1 << xBits); index++) { @@ -387,4 +389,4 @@ namespace Ryujinx.Graphics.Shader.Decoders return new InstOp(address, opCode, InstName.Invalid, null, InstProps.None); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/Register.cs b/src/Ryujinx.Graphics.Shader/Decoders/Register.cs index e375096dd..2e6d61996 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/Register.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/Register.cs @@ -8,13 +8,13 @@ namespace Ryujinx.Graphics.Shader.Decoders public RegisterType Type { get; } - public bool IsRZ => Type == RegisterType.Gpr && Index == RegisterConsts.RegisterZeroIndex; + public bool IsRZ => Type == RegisterType.Gpr && Index == RegisterConsts.RegisterZeroIndex; public bool IsPT => Type == RegisterType.Predicate && Index == RegisterConsts.PredicateTrueIndex; public Register(int index, RegisterType type) { Index = index; - Type = type; + Type = type; } public override int GetHashCode() @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public bool Equals(Register other) { return other.Index == Index && - other.Type == Type; + other.Type == Type; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs b/src/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs index d381f9543..416fba966 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs @@ -2,12 +2,12 @@ namespace Ryujinx.Graphics.Shader.Decoders { static class RegisterConsts { - public const int GprsCount = 255; + public const int GprsCount = 255; public const int PredsCount = 7; public const int FlagsCount = 4; public const int TotalCount = GprsCount + PredsCount + FlagsCount; - public const int RegisterZeroIndex = GprsCount; + public const int RegisterZeroIndex = GprsCount; public const int PredicateTrueIndex = PredsCount; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs b/src/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs index 648f816a2..c870464ff 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Graphics.Shader.Decoders Gpr, Predicate, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/InputTopology.cs b/src/Ryujinx.Graphics.Shader/InputTopology.cs index da3329090..ebd2930e4 100644 --- a/src/Ryujinx.Graphics.Shader/InputTopology.cs +++ b/src/Ryujinx.Graphics.Shader/InputTopology.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Shader Lines, LinesAdjacency, Triangles, - TrianglesAdjacency + TrianglesAdjacency, } static class InputTopologyExtensions @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader InputTopology.LinesAdjacency => "lines_adjacency", InputTopology.Triangles => "triangles", InputTopology.TrianglesAdjacency => "triangles_adjacency", - _ => "points" + _ => "points", }; } @@ -33,8 +33,8 @@ namespace Ryujinx.Graphics.Shader InputTopology.LinesAdjacency => 2, InputTopology.Triangles or InputTopology.TrianglesAdjacency => 3, - _ => 1 + _ => 1, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs b/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs index 562fb8d53..5e572f5a7 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System.Collections.Generic; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Instructions @@ -21,10 +20,10 @@ namespace Ryujinx.Graphics.Shader.Instructions Tessellation = TessellationControl | TessellationEvaluation, VertexTessellationGeometry = Vertex | Tessellation | Geometry, TessellationGeometryFragment = Tessellation | Geometry | Fragment, - AllGraphics = Vertex | Tessellation | Geometry | Fragment + AllGraphics = Vertex | Tessellation | Geometry | Fragment, } - private struct AttributeEntry + private readonly struct AttributeEntry { public int BaseOffset { get; } public AggregateType Type { get; } @@ -344,8 +343,8 @@ namespace Ryujinx.Graphics.Shader.Instructions AggregateType.Vector2 => 2, AggregateType.Vector3 => 3, AggregateType.Vector4 => 4, - _ => 1 + _ => 1, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs index 963a5c656..f105505dd 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs @@ -7,352 +7,352 @@ namespace Ryujinx.Graphics.Shader.Instructions { public static void AtomCas(EmitterContext context) { - InstAtomCas op = context.GetOp<InstAtomCas>(); + context.GetOp<InstAtomCas>(); context.Config.GpuAccessor.Log("Shader instruction AtomCas is not implemented."); } public static void AtomsCas(EmitterContext context) { - InstAtomsCas op = context.GetOp<InstAtomsCas>(); + context.GetOp<InstAtomsCas>(); context.Config.GpuAccessor.Log("Shader instruction AtomsCas is not implemented."); } public static void B2r(EmitterContext context) { - InstB2r op = context.GetOp<InstB2r>(); + context.GetOp<InstB2r>(); context.Config.GpuAccessor.Log("Shader instruction B2r is not implemented."); } public static void Bpt(EmitterContext context) { - InstBpt op = context.GetOp<InstBpt>(); + context.GetOp<InstBpt>(); context.Config.GpuAccessor.Log("Shader instruction Bpt is not implemented."); } public static void Cctl(EmitterContext context) { - InstCctl op = context.GetOp<InstCctl>(); + context.GetOp<InstCctl>(); context.Config.GpuAccessor.Log("Shader instruction Cctl is not implemented."); } public static void Cctll(EmitterContext context) { - InstCctll op = context.GetOp<InstCctll>(); + context.GetOp<InstCctll>(); context.Config.GpuAccessor.Log("Shader instruction Cctll is not implemented."); } public static void Cctlt(EmitterContext context) { - InstCctlt op = context.GetOp<InstCctlt>(); + context.GetOp<InstCctlt>(); context.Config.GpuAccessor.Log("Shader instruction Cctlt is not implemented."); } public static void Cs2r(EmitterContext context) { - InstCs2r op = context.GetOp<InstCs2r>(); + context.GetOp<InstCs2r>(); context.Config.GpuAccessor.Log("Shader instruction Cs2r is not implemented."); } public static void FchkR(EmitterContext context) { - InstFchkR op = context.GetOp<InstFchkR>(); + context.GetOp<InstFchkR>(); context.Config.GpuAccessor.Log("Shader instruction FchkR is not implemented."); } public static void FchkI(EmitterContext context) { - InstFchkI op = context.GetOp<InstFchkI>(); + context.GetOp<InstFchkI>(); context.Config.GpuAccessor.Log("Shader instruction FchkI is not implemented."); } public static void FchkC(EmitterContext context) { - InstFchkC op = context.GetOp<InstFchkC>(); + context.GetOp<InstFchkC>(); context.Config.GpuAccessor.Log("Shader instruction FchkC is not implemented."); } public static void Getcrsptr(EmitterContext context) { - InstGetcrsptr op = context.GetOp<InstGetcrsptr>(); + context.GetOp<InstGetcrsptr>(); context.Config.GpuAccessor.Log("Shader instruction Getcrsptr is not implemented."); } public static void Getlmembase(EmitterContext context) { - InstGetlmembase op = context.GetOp<InstGetlmembase>(); + context.GetOp<InstGetlmembase>(); context.Config.GpuAccessor.Log("Shader instruction Getlmembase is not implemented."); } public static void Ide(EmitterContext context) { - InstIde op = context.GetOp<InstIde>(); + context.GetOp<InstIde>(); context.Config.GpuAccessor.Log("Shader instruction Ide is not implemented."); } public static void IdpR(EmitterContext context) { - InstIdpR op = context.GetOp<InstIdpR>(); + context.GetOp<InstIdpR>(); context.Config.GpuAccessor.Log("Shader instruction IdpR is not implemented."); } public static void IdpC(EmitterContext context) { - InstIdpC op = context.GetOp<InstIdpC>(); + context.GetOp<InstIdpC>(); context.Config.GpuAccessor.Log("Shader instruction IdpC is not implemented."); } public static void ImadspR(EmitterContext context) { - InstImadspR op = context.GetOp<InstImadspR>(); + context.GetOp<InstImadspR>(); context.Config.GpuAccessor.Log("Shader instruction ImadspR is not implemented."); } public static void ImadspI(EmitterContext context) { - InstImadspI op = context.GetOp<InstImadspI>(); + context.GetOp<InstImadspI>(); context.Config.GpuAccessor.Log("Shader instruction ImadspI is not implemented."); } public static void ImadspC(EmitterContext context) { - InstImadspC op = context.GetOp<InstImadspC>(); + context.GetOp<InstImadspC>(); context.Config.GpuAccessor.Log("Shader instruction ImadspC is not implemented."); } public static void ImadspRc(EmitterContext context) { - InstImadspRc op = context.GetOp<InstImadspRc>(); + context.GetOp<InstImadspRc>(); context.Config.GpuAccessor.Log("Shader instruction ImadspRc is not implemented."); } public static void Jcal(EmitterContext context) { - InstJcal op = context.GetOp<InstJcal>(); + context.GetOp<InstJcal>(); context.Config.GpuAccessor.Log("Shader instruction Jcal is not implemented."); } public static void Jmp(EmitterContext context) { - InstJmp op = context.GetOp<InstJmp>(); + context.GetOp<InstJmp>(); context.Config.GpuAccessor.Log("Shader instruction Jmp is not implemented."); } public static void Jmx(EmitterContext context) { - InstJmx op = context.GetOp<InstJmx>(); + context.GetOp<InstJmx>(); context.Config.GpuAccessor.Log("Shader instruction Jmx is not implemented."); } public static void Ld(EmitterContext context) { - InstLd op = context.GetOp<InstLd>(); + context.GetOp<InstLd>(); context.Config.GpuAccessor.Log("Shader instruction Ld is not implemented."); } public static void Lepc(EmitterContext context) { - InstLepc op = context.GetOp<InstLepc>(); + context.GetOp<InstLepc>(); context.Config.GpuAccessor.Log("Shader instruction Lepc is not implemented."); } public static void Longjmp(EmitterContext context) { - InstLongjmp op = context.GetOp<InstLongjmp>(); + context.GetOp<InstLongjmp>(); context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented."); } public static void Pexit(EmitterContext context) { - InstPexit op = context.GetOp<InstPexit>(); + context.GetOp<InstPexit>(); context.Config.GpuAccessor.Log("Shader instruction Pexit is not implemented."); } public static void Pixld(EmitterContext context) { - InstPixld op = context.GetOp<InstPixld>(); + context.GetOp<InstPixld>(); context.Config.GpuAccessor.Log("Shader instruction Pixld is not implemented."); } public static void Plongjmp(EmitterContext context) { - InstPlongjmp op = context.GetOp<InstPlongjmp>(); + context.GetOp<InstPlongjmp>(); context.Config.GpuAccessor.Log("Shader instruction Plongjmp is not implemented."); } public static void Pret(EmitterContext context) { - InstPret op = context.GetOp<InstPret>(); + context.GetOp<InstPret>(); context.Config.GpuAccessor.Log("Shader instruction Pret is not implemented."); } public static void PrmtR(EmitterContext context) { - InstPrmtR op = context.GetOp<InstPrmtR>(); + context.GetOp<InstPrmtR>(); context.Config.GpuAccessor.Log("Shader instruction PrmtR is not implemented."); } public static void PrmtI(EmitterContext context) { - InstPrmtI op = context.GetOp<InstPrmtI>(); + context.GetOp<InstPrmtI>(); context.Config.GpuAccessor.Log("Shader instruction PrmtI is not implemented."); } public static void PrmtC(EmitterContext context) { - InstPrmtC op = context.GetOp<InstPrmtC>(); + context.GetOp<InstPrmtC>(); context.Config.GpuAccessor.Log("Shader instruction PrmtC is not implemented."); } public static void PrmtRc(EmitterContext context) { - InstPrmtRc op = context.GetOp<InstPrmtRc>(); + context.GetOp<InstPrmtRc>(); context.Config.GpuAccessor.Log("Shader instruction PrmtRc is not implemented."); } public static void R2b(EmitterContext context) { - InstR2b op = context.GetOp<InstR2b>(); + context.GetOp<InstR2b>(); context.Config.GpuAccessor.Log("Shader instruction R2b is not implemented."); } public static void Ram(EmitterContext context) { - InstRam op = context.GetOp<InstRam>(); + context.GetOp<InstRam>(); context.Config.GpuAccessor.Log("Shader instruction Ram is not implemented."); } public static void Rtt(EmitterContext context) { - InstRtt op = context.GetOp<InstRtt>(); + context.GetOp<InstRtt>(); context.Config.GpuAccessor.Log("Shader instruction Rtt is not implemented."); } public static void Sam(EmitterContext context) { - InstSam op = context.GetOp<InstSam>(); + context.GetOp<InstSam>(); context.Config.GpuAccessor.Log("Shader instruction Sam is not implemented."); } public static void Setcrsptr(EmitterContext context) { - InstSetcrsptr op = context.GetOp<InstSetcrsptr>(); + context.GetOp<InstSetcrsptr>(); context.Config.GpuAccessor.Log("Shader instruction Setcrsptr is not implemented."); } public static void Setlmembase(EmitterContext context) { - InstSetlmembase op = context.GetOp<InstSetlmembase>(); + context.GetOp<InstSetlmembase>(); context.Config.GpuAccessor.Log("Shader instruction Setlmembase is not implemented."); } public static void St(EmitterContext context) { - InstSt op = context.GetOp<InstSt>(); + context.GetOp<InstSt>(); context.Config.GpuAccessor.Log("Shader instruction St is not implemented."); } public static void Stp(EmitterContext context) { - InstStp op = context.GetOp<InstStp>(); + context.GetOp<InstStp>(); context.Config.GpuAccessor.Log("Shader instruction Stp is not implemented."); } public static void Txa(EmitterContext context) { - InstTxa op = context.GetOp<InstTxa>(); + context.GetOp<InstTxa>(); context.Config.GpuAccessor.Log("Shader instruction Txa is not implemented."); } public static void Vabsdiff(EmitterContext context) { - InstVabsdiff op = context.GetOp<InstVabsdiff>(); + context.GetOp<InstVabsdiff>(); context.Config.GpuAccessor.Log("Shader instruction Vabsdiff is not implemented."); } public static void Vabsdiff4(EmitterContext context) { - InstVabsdiff4 op = context.GetOp<InstVabsdiff4>(); + context.GetOp<InstVabsdiff4>(); context.Config.GpuAccessor.Log("Shader instruction Vabsdiff4 is not implemented."); } public static void Vadd(EmitterContext context) { - InstVadd op = context.GetOp<InstVadd>(); + context.GetOp<InstVadd>(); context.Config.GpuAccessor.Log("Shader instruction Vadd is not implemented."); } public static void Votevtg(EmitterContext context) { - InstVotevtg op = context.GetOp<InstVotevtg>(); + context.GetOp<InstVotevtg>(); context.Config.GpuAccessor.Log("Shader instruction Votevtg is not implemented."); } public static void Vset(EmitterContext context) { - InstVset op = context.GetOp<InstVset>(); + context.GetOp<InstVset>(); context.Config.GpuAccessor.Log("Shader instruction Vset is not implemented."); } public static void Vshl(EmitterContext context) { - InstVshl op = context.GetOp<InstVshl>(); + context.GetOp<InstVshl>(); context.Config.GpuAccessor.Log("Shader instruction Vshl is not implemented."); } public static void Vshr(EmitterContext context) { - InstVshr op = context.GetOp<InstVshr>(); + context.GetOp<InstVshr>(); context.Config.GpuAccessor.Log("Shader instruction Vshr is not implemented."); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs index 879075bae..4370560d7 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -18,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.Instructions IDstFmt.S16 => short.MinValue, IDstFmt.U32 => uint.MinValue, IDstFmt.S32 => int.MinValue, - _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type.") + _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type."), }; } @@ -30,7 +29,7 @@ namespace Ryujinx.Graphics.Shader.Instructions IDstFmt.S16 => short.MaxValue, IDstFmt.U32 => uint.MaxValue, IDstFmt.S32 => int.MaxValue, - _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type.") + _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type."), }; } @@ -44,7 +43,7 @@ namespace Ryujinx.Graphics.Shader.Instructions ISrcDstFmt.S16 => short.MinValue, ISrcDstFmt.U32 => uint.MinValue, ISrcDstFmt.S32 => int.MinValue, - _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type.") + _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type."), }; } @@ -58,7 +57,7 @@ namespace Ryujinx.Graphics.Shader.Instructions ISrcDstFmt.S16 => short.MaxValue, ISrcDstFmt.U32 => uint.MaxValue, ISrcDstFmt.S32 => int.MaxValue, - _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type.") + _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type."), }; } @@ -69,7 +68,7 @@ namespace Ryujinx.Graphics.Shader.Instructions BoolOp.And => context.BitwiseAnd(input, pred), BoolOp.Or => context.BitwiseOr(input, pred), BoolOp.Xor => context.BitwiseExclusiveOr(input, pred), - _ => input + _ => input, }; } @@ -89,7 +88,7 @@ namespace Ryujinx.Graphics.Shader.Instructions VectorSelect.S8B3 => SignExtendTo32(context, context.ShiftRightU32(src, Const(24)), 8), VectorSelect.S16H0 => SignExtendTo32(context, context.ShiftRightU32(src, Const(0)), 16), VectorSelect.S16H1 => SignExtendTo32(context, context.ShiftRightU32(src, Const(16)), 16), - _ => src + _ => src, }; } @@ -134,7 +133,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } context.Copy(GetZF(), context.FPCompareEqual(dest, zero, fpType)); - context.Copy(GetNF(), context.FPCompareLess (dest, zero, fpType)); + context.Copy(GetNF(), context.FPCompareLess(dest, zero, fpType)); } } @@ -157,4 +156,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return result; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 76b2e0783..1876847c4 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Shader.Instructions for (int index = 0; index < (int)op.AlSize + 1; index++) { - Register rd = new Register(op.Dest + index, RegisterType.Gpr); + Register rd = new(op.Dest + index, RegisterType.Gpr); if (rd.IsRZ) { @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.Instructions break; } - Register rd = new Register(op.SrcB + index, RegisterType.Gpr); + Register rd = new(op.SrcB + index, RegisterType.Gpr); if (op.Phys) { @@ -380,4 +380,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs index f3114c6e4..ae5e078f6 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs @@ -22,7 +22,9 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Depbar(EmitterContext context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment InstDepbar op = context.GetOp<InstDepbar>(); +#pragma warning restore IDE0059 // No operation. } @@ -41,4 +43,4 @@ namespace Ryujinx.Graphics.Shader.Instructions } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs index 719252696..3a8419698 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs @@ -191,4 +191,4 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(rd), res); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs index 74ac76029..b5580a37a 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -80,8 +79,8 @@ namespace Ryujinx.Graphics.Shader.Instructions Ccc.Oft => GetVF(), Ccc.Rle => context.BitwiseOr(GetNF(), GetZF()), Ccc.Rgt => context.BitwiseNot(context.BitwiseOr(GetNF(), GetZF())), - _ => Const(defaultCond) + _ => Const(defaultCond), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs index bebd96dd9..8d59023ae 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -140,7 +139,7 @@ namespace Ryujinx.Graphics.Shader.Instructions IntegerRound.Floor => context.FPFloor(srcB, srcType.ToInstFPType()), IntegerRound.Ceil => context.FPCeiling(srcB, srcType.ToInstFPType()), IntegerRound.Trunc => context.FPTruncate(srcB, srcType.ToInstFPType()), - _ => srcB + _ => srcB, }; } @@ -191,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Instructions RoundMode2.Floor => context.FPFloor(srcB, fpType), RoundMode2.Ceil => context.FPCeiling(srcB, fpType), RoundMode2.Trunc => context.FPTruncate(srcB, fpType), - _ => srcB + _ => srcB, }; if (!isSignedInt) @@ -422,4 +421,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return type == DstFmt.F64 ? Instruction.FP64 : Instruction.FP32; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs index 29803c31e..ab643b5c6 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -458,7 +457,7 @@ namespace Ryujinx.Graphics.Shader.Instructions MultiplyScale.M2 => ConstF(2f), MultiplyScale.M4 => ConstF(4f), MultiplyScale.M8 => ConstF(8f), - _ => ConstF(1f) // Invalid, behave as if it had no scale. + _ => ConstF(1f), // Invalid, behave as if it had no scale. }; if (scaleConst.AsFloat() == 1f) @@ -529,4 +528,4 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(rd), GetHalfPacked(context, swizzle, res, rd)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs index 8f99ddb3c..59ad7a5de 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -484,8 +483,8 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - Operand low = context.BitwiseAnd(res[0], Const(0xffff)); - Operand high = context.ShiftLeft (res[1], Const(16)); + Operand low = context.BitwiseAnd(res[0], Const(0xffff)); + Operand high = context.ShiftLeft(res[1], Const(16)); Operand packed = context.BitwiseOr(low, high); @@ -546,20 +545,16 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - Instruction inst; - - switch (cond & ~FComp.Nan) + var inst = (cond & ~FComp.Nan) switch { - case FComp.Lt: inst = Instruction.CompareLess; break; - case FComp.Eq: inst = Instruction.CompareEqual; break; - case FComp.Le: inst = Instruction.CompareLessOrEqual; break; - case FComp.Gt: inst = Instruction.CompareGreater; break; - case FComp.Ne: inst = Instruction.CompareNotEqual; break; - case FComp.Ge: inst = Instruction.CompareGreaterOrEqual; break; - - default: throw new ArgumentException($"Unexpected condition \"{cond}\"."); - } - + FComp.Lt => Instruction.CompareLess, + FComp.Eq => Instruction.CompareEqual, + FComp.Le => Instruction.CompareLessOrEqual, + FComp.Gt => Instruction.CompareGreater, + FComp.Ne => Instruction.CompareNotEqual, + FComp.Ge => Instruction.CompareGreaterOrEqual, + _ => throw new ArgumentException($"Unexpected condition \"{cond}\"."), + }; res = context.Add(inst | fpType, Local(), srcA, srcB); if ((cond & FComp.Nan) != 0) @@ -572,4 +567,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return res; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs index 412a5305a..5757e4fb0 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs @@ -103,4 +103,4 @@ namespace Ryujinx.Graphics.Shader.Instructions SetFPZnFlags(context, res, writeCC, fpType); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs index 736963552..fc1a696fa 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs @@ -3,8 +3,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System.Collections.Generic; using System.Linq; - -using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Instructions @@ -13,14 +11,14 @@ namespace Ryujinx.Graphics.Shader.Instructions { public static void Bra(EmitterContext context) { - InstBra op = context.GetOp<InstBra>(); + context.GetOp<InstBra>(); EmitBranch(context, context.CurrBlock.Successors[^1].Address); } public static void Brk(EmitterContext context) { - InstBrk op = context.GetOp<InstBrk>(); + context.GetOp<InstBrk>(); EmitBrkContSync(context); } @@ -123,7 +121,7 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Cal(EmitterContext context) { - InstCal op = context.GetOp<InstCal>(); + context.GetOp<InstCal>(); DecodedFunction function = context.Program.GetFunctionByAddress(context.CurrOp.GetAbsoluteAddress()); @@ -147,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Cont(EmitterContext context) { - InstCont op = context.GetOp<InstCont>(); + context.GetOp<InstCont>(); EmitBrkContSync(context); } @@ -185,28 +183,28 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Kil(EmitterContext context) { - InstKil op = context.GetOp<InstKil>(); + context.GetOp<InstKil>(); context.Discard(); } public static void Pbk(EmitterContext context) { - InstPbk op = context.GetOp<InstPbk>(); + context.GetOp<InstPbk>(); EmitPbkPcntSsy(context); } public static void Pcnt(EmitterContext context) { - InstPcnt op = context.GetOp<InstPcnt>(); + context.GetOp<InstPcnt>(); EmitPbkPcntSsy(context); } public static void Ret(EmitterContext context) { - InstRet op = context.GetOp<InstRet>(); + context.GetOp<InstRet>(); if (context.IsNonMain) { @@ -220,14 +218,14 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Ssy(EmitterContext context) { - InstSsy op = context.GetOp<InstSsy>(); + context.GetOp<InstSsy>(); EmitPbkPcntSsy(context); } public static void Sync(EmitterContext context) { - InstSync op = context.GetOp<InstSync>(); + context.GetOp<InstSync>(); EmitBrkContSync(context); } @@ -275,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.Instructions private static void EmitBranch(EmitterContext context, ulong address) { InstOp op = context.CurrOp; - InstConditional opCond = new InstConditional(op.RawOpCode); + InstConditional opCond = new(op.RawOpCode); // If we're branching to the next instruction, then the branch // is useless and we can ignore it. @@ -321,4 +319,4 @@ namespace Ryujinx.Graphics.Shader.Instructions } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs index 0ba4667ea..8638fb8f2 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs @@ -3,7 +3,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; using System.Runtime.CompilerServices; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Instructions @@ -111,7 +110,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return new Operand[] { ConstF((float)Unsafe.As<ushort, Half>(ref low)), - ConstF((float)Unsafe.As<ushort, Half>(ref high)) + ConstF((float)Unsafe.As<ushort, Half>(ref high)), }; } @@ -123,7 +122,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return new Operand[] { ConstF((float)Unsafe.As<ushort, Half>(ref low)), - ConstF((float)Unsafe.As<ushort, Half>(ref high)) + ConstF((float)Unsafe.As<ushort, Half>(ref high)), }; } @@ -139,56 +138,51 @@ namespace Ryujinx.Graphics.Shader.Instructions public static Operand[] GetHalfUnpacked(EmitterContext context, Operand src, HalfSwizzle swizzle) { - switch (swizzle) + return swizzle switch { - case HalfSwizzle.F16: - return new Operand[] - { + HalfSwizzle.F16 => new Operand[] + { context.UnpackHalf2x16Low (src), - context.UnpackHalf2x16High(src) - }; - - case HalfSwizzle.F32: return new Operand[] { src, src }; - - case HalfSwizzle.H0H0: - return new Operand[] + context.UnpackHalf2x16High(src), + }, + HalfSwizzle.F32 => new Operand[] { src, src }, + HalfSwizzle.H0H0 => new Operand[] { context.UnpackHalf2x16Low(src), - context.UnpackHalf2x16Low(src) - }; - - case HalfSwizzle.H1H1: - return new Operand[] + context.UnpackHalf2x16Low(src), + }, + HalfSwizzle.H1H1 => new Operand[] { context.UnpackHalf2x16High(src), - context.UnpackHalf2x16High(src) - }; - } - - throw new ArgumentException($"Invalid swizzle \"{swizzle}\"."); + context.UnpackHalf2x16High(src), + }, + _ => throw new ArgumentException($"Invalid swizzle \"{swizzle}\"."), + }; } public static Operand GetHalfPacked(EmitterContext context, OFmt swizzle, Operand[] results, int rd) { switch (swizzle) { - case OFmt.F16: return context.PackHalf2x16(results[0], results[1]); + case OFmt.F16: + return context.PackHalf2x16(results[0], results[1]); - case OFmt.F32: return results[0]; + case OFmt.F32: + return results[0]; case OFmt.MrgH0: - { - Operand h1 = GetHalfDest(context, rd, isHigh: true); + { + Operand h1 = GetHalfDest(context, rd, isHigh: true); - return context.PackHalf2x16(results[0], h1); - } + return context.PackHalf2x16(results[0], h1); + } case OFmt.MrgH1: - { - Operand h0 = GetHalfDest(context, rd, isHigh: false); + { + Operand h0 = GetHalfDest(context, rd, isHigh: false); - return context.PackHalf2x16(h0, results[1]); - } + return context.PackHalf2x16(h0, results[1]); + } } throw new ArgumentException($"Invalid swizzle \"{swizzle}\"."); @@ -263,4 +257,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.BitwiseAnd(src, Const(mask)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs index 374e3d614..c06f4671f 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs @@ -510,7 +510,9 @@ namespace Ryujinx.Graphics.Shader.Instructions aLow = context.BitwiseNot(aLow); aHigh = context.BitwiseNot(aHigh); +#pragma warning disable IDE0059 // Remove unnecessary value assignment aLow = AddWithCarry(context, aLow, Const(1), out Operand aLowCOut); +#pragma warning restore IDE0059 aHigh = context.IAdd(aHigh, aLowCOut); } @@ -696,4 +698,4 @@ namespace Ryujinx.Graphics.Shader.Instructions SetZnFlags(context, res, setCC: true, extended: extended); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs index dcdb189fb..18d4e3d19 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -220,7 +219,9 @@ namespace Ryujinx.Graphics.Shader.Instructions else { res = context.ISubtract(srcA, srcB); +#pragma warning disable IDE0059 // Remove unnecessary value assignment res = context.IAdd(res, context.BitwiseNot(GetCF())); +#pragma warning restore IDE0059 switch (cond) { @@ -287,17 +288,25 @@ namespace Ryujinx.Graphics.Shader.Instructions IComp.Gt => Instruction.CompareGreaterU32, IComp.Ne => Instruction.CompareNotEqual, IComp.Ge => Instruction.CompareGreaterOrEqualU32, - _ => throw new InvalidOperationException($"Unexpected condition \"{cond}\".") + _ => throw new InvalidOperationException($"Unexpected condition \"{cond}\"."), }; if (isSigned) { switch (cond) { - case IComp.Lt: inst = Instruction.CompareLess; break; - case IComp.Le: inst = Instruction.CompareLessOrEqual; break; - case IComp.Gt: inst = Instruction.CompareGreater; break; - case IComp.Ge: inst = Instruction.CompareGreaterOrEqual; break; + case IComp.Lt: + inst = Instruction.CompareLess; + break; + case IComp.Le: + inst = Instruction.CompareLessOrEqual; + break; + case IComp.Gt: + inst = Instruction.CompareGreater; + break; + case IComp.Ge: + inst = Instruction.CompareGreaterOrEqual; + break; } } @@ -307,4 +316,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return res; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs index 1f3f66ae4..5993c93dd 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -103,10 +102,10 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand res = logicOp switch { - LogicOp.And => res = context.BitwiseAnd(srcA, srcB), - LogicOp.Or => res = context.BitwiseOr(srcA, srcB), - LogicOp.Xor => res = context.BitwiseExclusiveOr(srcA, srcB), - _ => srcB + LogicOp.And => context.BitwiseAnd(srcA, srcB), + LogicOp.Or => context.BitwiseOr(srcA, srcB), + LogicOp.Xor => context.BitwiseExclusiveOr(srcA, srcB), + _ => srcB, }; EmitLopPredWrite(context, res, predOp, destPred); @@ -164,4 +163,4 @@ namespace Ryujinx.Graphics.Shader.Instructions } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs index 73930ed1f..739e94413 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs @@ -68,4 +68,4 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: X flags. } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 40312f4a4..006c14b54 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System.Numerics; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -48,7 +47,7 @@ namespace Ryujinx.Graphics.Shader.Instructions AtomsSize.S32 => AtomSize.S32, AtomsSize.U64 => AtomSize.U64, AtomsSize.S64 => AtomSize.S64, - _ => AtomSize.U32 + _ => AtomSize.U32, }; Operand id = Const(context.Config.ResourceManager.SharedMemoryId); @@ -85,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.Instructions for (int index = 0; index < count; index++) { - Register dest = new Register(op.Dest + index, RegisterType.Gpr); + Register dest = new(op.Dest + index, RegisterType.Gpr); if (dest.IsRZ) { @@ -309,14 +308,14 @@ namespace Ryujinx.Graphics.Shader.Instructions { LsSize2.B64 => 2, LsSize2.B128 => 4, - _ => 1 + _ => 1, }; Operand baseOffset = context.Copy(srcA); for (int index = 0; index < count; index++) { - Register dest = new Register(rd + index, RegisterType.Gpr); + Register dest = new(rd + index, RegisterType.Gpr); if (dest.IsRZ) { @@ -354,7 +353,7 @@ namespace Ryujinx.Graphics.Shader.Instructions for (int index = 0; index < count; index++) { - Register dest = new Register(rd + index, RegisterType.Gpr); + Register dest = new(rd + index, RegisterType.Gpr); if (dest.IsRZ) { @@ -390,7 +389,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { LsSize2.B64 => 2, LsSize2.B128 => 4, - _ => 1 + _ => 1, }; Operand baseOffset = context.Copy(srcA); @@ -476,22 +475,18 @@ namespace Ryujinx.Graphics.Shader.Instructions LsSize.S8 => StorageKind.GlobalMemoryS8, LsSize.U16 => StorageKind.GlobalMemoryU16, LsSize.S16 => StorageKind.GlobalMemoryS16, - _ => StorageKind.GlobalMemory + _ => StorageKind.GlobalMemory, }; } private static int GetVectorCount(LsSize size) { - switch (size) + return size switch { - case LsSize.B64: - return 2; - case LsSize.B128: - case LsSize.UB128: - return 4; - } - - return 1; + LsSize.B64 => 2, + LsSize.B128 or LsSize.UB128 => 4, + _ => 1, + }; } private static (Operand, Operand) Get40BitsAddress( @@ -544,10 +539,18 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (size) { - case LsSize.U8: value = ZeroExtendTo32(context, value, 8); break; - case LsSize.U16: value = ZeroExtendTo32(context, value, 16); break; - case LsSize.S8: value = SignExtendTo32(context, value, 8); break; - case LsSize.S16: value = SignExtendTo32(context, value, 16); break; + case LsSize.U8: + value = ZeroExtendTo32(context, value, 8); + break; + case LsSize.U16: + value = ZeroExtendTo32(context, value, 16); + break; + case LsSize.S8: + value = SignExtendTo32(context, value, 8); + break; + case LsSize.S16: + value = SignExtendTo32(context, value, 16); + break; } return value; @@ -578,4 +581,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index e12177f7d..f6c3bf6f0 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -212,7 +212,7 @@ namespace Ryujinx.Graphics.Shader.Instructions int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount; RegisterType type = ccpr ? RegisterType.Flag : RegisterType.Predicate; int shift = (int)byteSel * 8; - + for (int bit = 0; bit < count; bit++) { Operand flag = Register(bit, type); @@ -228,4 +228,4 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(rd), res); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs index 1ea7d3214..86f154bdb 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs @@ -94,4 +94,4 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(rd), srcB); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs index 011440071..28ee927d8 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs @@ -7,9 +7,9 @@ namespace Ryujinx.Graphics.Shader.Instructions { public static void Nop(EmitterContext context) { - InstNop op = context.GetOp<InstNop>(); + context.GetOp<InstNop>(); // No operation. } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs index 79919624e..630162ade 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs @@ -113,4 +113,4 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(dest, res); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs index 2873cad88..ee0dac155 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs @@ -246,4 +246,4 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(rd), res); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index 3d94b8938..78fc313d8 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Numerics; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -221,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand destOperand = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null; - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -325,7 +324,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(srcA++, RegisterType.Gpr)); } - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -445,10 +444,18 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (size) { - case SuSize.U8: context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 8)); break; - case SuSize.U16: context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 16)); break; - case SuSize.S8: context.Copy(dests[0], SignExtendTo32(context, dests[0], 8)); break; - case SuSize.S16: context.Copy(dests[0], SignExtendTo32(context, dests[0], 16)); break; + case SuSize.U8: + context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 8)); + break; + case SuSize.U16: + context.Copy(dests[0], ZeroExtendTo32(context, dests[0], 16)); + break; + case SuSize.S8: + context.Copy(dests[0], SignExtendTo32(context, dests[0], 8)); + break; + case SuSize.S16: + context.Copy(dests[0], SignExtendTo32(context, dests[0], 16)); + break; } } } @@ -493,7 +500,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(srcB++, RegisterType.Gpr)); } - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -600,7 +607,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(srcB++, RegisterType.Gpr)); } - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -699,7 +706,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SuatomSize.S64 => 3, SuatomSize.Sd32 => 2, SuatomSize.Sd64 => 3, - _ => 2 + _ => 2, }; } @@ -715,7 +722,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SuatomSize.S64 => TextureFormat.R32G32Uint, SuatomSize.Sd32 => TextureFormat.R32Uint, SuatomSize.Sd64 => TextureFormat.R32G32Uint, - _ => TextureFormat.R32Uint + _ => TextureFormat.R32Uint, }; } @@ -732,7 +739,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SuatomOp.Or => TextureFlags.BitwiseOr, SuatomOp.Xor => TextureFlags.BitwiseXor, SuatomOp.Exch => TextureFlags.Swap, - _ => TextureFlags.Add + _ => TextureFlags.Add, }; } @@ -743,7 +750,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SuSize.B64 => 2, SuSize.B128 => 4, SuSize.UB128 => 4, - _ => 1 + _ => 1, }; } @@ -759,7 +766,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SuSize.B64 => 3, SuSize.B128 => 4, SuSize.UB128 => 4, - _ => 2 + _ => 2, }; } @@ -775,7 +782,7 @@ namespace Ryujinx.Graphics.Shader.Instructions SuSize.B64 => TextureFormat.R32G32Uint, SuSize.B128 => TextureFormat.R32G32B32A32Uint, SuSize.UB128 => TextureFormat.R32G32B32A32Uint, - _ => TextureFormat.R32Uint + _ => TextureFormat.R32Uint, }; } @@ -789,8 +796,8 @@ namespace Ryujinx.Graphics.Shader.Instructions SuDim._2d => SamplerType.Texture2D, SuDim._2dArray => SamplerType.Texture2D | SamplerType.Array, SuDim._3d => SamplerType.Texture3D, - _ => SamplerType.None + _ => SamplerType.None, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index caa9a7759..3701325e2 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Numerics; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Instructions @@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.Instructions private static readonly int[,] _maskLut = new int[,] { { 0b0001, 0b0010, 0b0100, 0b1000, 0b0011, 0b1001, 0b1010, 0b1100 }, - { 0b0111, 0b1011, 0b1101, 0b1110, 0b1111, 0b0000, 0b0000, 0b0000 } + { 0b0111, 0b1011, 0b1101, 0b1110, 0b1111, 0b0000, 0b0000, 0b0000 }, }; public const bool Sample1DAs2D = true; @@ -23,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Texs, Tlds, - Tld4s + Tld4s, } public static void Tex(EmitterContext context) @@ -207,7 +206,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand arrayIndex = isArray ? Ra() : null; - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -353,7 +352,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return; } - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); Operand Ra() { @@ -722,7 +721,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand arrayIndex = isArray ? Ra() : null; - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); SamplerType type = ConvertSamplerType(dimensions); TextureFlags flags = TextureFlags.Gather; @@ -864,7 +863,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureFlags flags = TextureFlags.None; - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -996,7 +995,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureFlags flags = TextureFlags.Derivatives; - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -1126,7 +1125,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(srcA++, RegisterType.Gpr)); } - List<Operand> sourcesList = new List<Operand>(); + List<Operand> sourcesList = new(); if (isBindless) { @@ -1195,7 +1194,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TexDim.Array3d => SamplerType.Texture3D | SamplerType.Array, TexDim.Cube => SamplerType.TextureCube, TexDim.ArrayCube => SamplerType.TextureCube | SamplerType.Array, - _ => throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".") + _ => throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\"."), }; } @@ -1309,4 +1308,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return TextureFlags.None; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs index 2d84c5bdb..a0e9fb384 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -77,7 +76,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { VideoScale.Shr7 => 7, VideoScale.Shr15 => 15, - _ => 0 + _ => 0, }; if (shift != 0) @@ -115,4 +114,4 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: CC. } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs index 67b185ab5..d52c972bc 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs @@ -180,4 +180,4 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs index 3c8336139..67dc3398b 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -39,7 +38,7 @@ namespace Ryujinx.Graphics.Shader.Instructions ShflMode.Up => context.ShuffleUp(srcA, srcB, srcC), ShflMode.Down => context.ShuffleDown(srcA, srcB, srcC), ShflMode.Bfly => context.ShuffleXor(srcA, srcB, srcC), - _ => (null, null) + _ => (null, null), }; context.Copy(GetDest(op.Dest), res); @@ -81,4 +80,4 @@ namespace Ryujinx.Graphics.Shader.Instructions } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs index 91c740b68..e1cef26d8 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs @@ -3,4 +3,4 @@ using Ryujinx.Graphics.Shader.Translation; namespace Ryujinx.Graphics.Shader.Instructions { delegate void InstEmitter(EmitterContext context); -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs b/src/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs index 6217ce530..6846ea8d6 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Instructions @@ -9,27 +8,27 @@ namespace Ryujinx.Graphics.Shader.Instructions { private enum TruthTable : byte { - False = 0x00, // false - True = 0xff, // true - In = 0xf0, // a - And2 = 0xc0, // a & b - Or2 = 0xfc, // a | b - Xor2 = 0x3c, // a ^ b - And3 = 0x80, // a & b & c - Or3 = 0xfe, // a | b | c - XorAnd = 0x60, // a & (b ^ c) - XorOr = 0xf6, // a | (b ^ c) - OrAnd = 0xe0, // a & (b | c) - AndOr = 0xf8, // a | (b & c) - Onehot = 0x16, // (a & !b & !c) | (!a & b & !c) | (!a & !b & c) - Only one value is true. - Majority = 0xe8, // Popcount(a, b, c) >= 2 - Gamble = 0x81, // (a & b & c) | (!a & !b & !c) - All on or all off + False = 0x00, // false + True = 0xff, // true + In = 0xf0, // a + And2 = 0xc0, // a & b + Or2 = 0xfc, // a | b + Xor2 = 0x3c, // a ^ b + And3 = 0x80, // a & b & c + Or3 = 0xfe, // a | b | c + XorAnd = 0x60, // a & (b ^ c) + XorOr = 0xf6, // a | (b ^ c) + OrAnd = 0xe0, // a & (b | c) + AndOr = 0xf8, // a | (b & c) + Onehot = 0x16, // (a & !b & !c) | (!a & b & !c) | (!a & !b & c) - Only one value is true. + Majority = 0xe8, // Popcount(a, b, c) >= 2 + Gamble = 0x81, // (a & b & c) | (!a & !b & !c) - All on or all off InverseGamble = 0x7e, // Inverse of Gamble - Dot = 0x1a, // a ^ (c | (a & b)) - Mux = 0xca, // a ? b : c - AndXor = 0x78, // a ^ (b & c) - OrXor = 0x1e, // a ^ (b | c) - Xor3 = 0x96, // a ^ b ^ c + Dot = 0x1a, // a ^ (c | (a & b)) + Mux = 0xca, // a ? b : c + AndXor = 0x78, // a ^ (b & c) + OrXor = 0x1e, // a ^ (b | c) + Xor3 = 0x96, // a ^ b ^ c } public static Operand GetFromTruthTable(EmitterContext context, Operand srcA, Operand srcB, Operand srcC, int imm) @@ -41,7 +40,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand x = srcA; Operand y = srcB; Operand z = srcC; - + if ((i & 0x01) != 0) { (x, y) = (y, x); @@ -98,6 +97,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { return imm switch { +#pragma warning disable IDE0055 // Disable formatting TruthTable.False => Const(0), TruthTable.True => Const(-1), TruthTable.In => x, @@ -118,7 +118,8 @@ namespace Ryujinx.Graphics.Shader.Instructions TruthTable.AndXor => context.BitwiseExclusiveOr(x, context.BitwiseAnd(y, z)), TruthTable.OrXor => context.BitwiseExclusiveOr(x, context.BitwiseOr(y, z)), TruthTable.Xor3 => context.BitwiseExclusiveOr(x, context.BitwiseExclusiveOr(y, z)), - _ => null + _ => null, +#pragma warning restore IDE0055 }; } @@ -138,4 +139,4 @@ namespace Ryujinx.Graphics.Shader.Instructions return (TruthTable)result; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs index 2aca118b7..637e120e1 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs @@ -83,9 +83,9 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation case Instruction.Discard: case Instruction.Return: return true; + default: + return false; } - - return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs index d4d87b067..1d33a9b02 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Comment = comment; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs index e535c3fc2..a5f3e0a8c 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Function.cs @@ -20,4 +20,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation OutArgumentsCount = outArgumentsCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs index 0f545e56f..d5eae00b7 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs @@ -12,4 +12,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation void SetSource(int index, Operand operand); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index de41a2cf7..808cc7ed6 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum Instruction { Absolute = 1, @@ -130,7 +132,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation FP32 = 1 << 16, FP64 = 1 << 17, - Mask = 0xffff + Mask = 0xffff, } static class InstructionExtensions @@ -161,4 +163,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation return inst == Instruction.Lod || inst == Instruction.TextureSize; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs index fb9b57bdd..fdee83451 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs @@ -43,6 +43,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation VertexId, VertexIndex, ViewportIndex, - ViewportMask + ViewportMask, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs index c264e47d1..cc9d6cc20 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation static class IrConsts { public const int False = 0; - public const int True = -1; + public const int True = -1; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs index 1df88a3d9..6648457f0 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation class Operand { private const int CbufSlotBits = 5; - private const int CbufSlotLsb = 32 - CbufSlotBits; + private const int CbufSlotLsb = 32 - CbufSlotBits; private const int CbufSlotMask = (1 << CbufSlotBits) - 1; public OperandType Type { get; } @@ -30,19 +30,19 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public Operand(OperandType type, int value) : this() { - Type = type; + Type = type; Value = value; } public Operand(Register reg) : this() { - Type = OperandType.Register; + Type = OperandType.Register; Value = PackRegInfo(reg.Index, reg.Type); } public Operand(int slot, int offset) : this() { - Type = OperandType.ConstantBuffer; + Type = OperandType.ConstantBuffer; Value = PackCbufInfo(slot, offset); } @@ -76,4 +76,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation return BitConverter.Int32BitsToSingle(Value); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs index 37c349e82..f88313552 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs @@ -59,4 +59,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation return new Operand(OperandType.Undefined); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs index 4d2da734e..7dbd9d25d 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs @@ -8,6 +8,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Label, LocalVariable, Register, - Undefined + Undefined, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index 425cfd909..f5396a884 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public Operation(Instruction inst, int index, Operand[] dests, Operand[] sources) : this(sources) { - Inst = inst; + Inst = inst; Index = index; if (dests != null) @@ -286,4 +286,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs index 8fa25ae9b..6c95c7bdd 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs @@ -15,21 +15,21 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public int DestsCount => _dest != null ? 1 : 0; - private HashSet<BasicBlock> _blocks; + private readonly HashSet<BasicBlock> _blocks; private class PhiSource { - public BasicBlock Block { get; } - public Operand Operand { get; set; } + public BasicBlock Block { get; } + public Operand Operand { get; set; } public PhiSource(BasicBlock block, Operand operand) { - Block = block; + Block = block; Operand = operand; } } - private List<PhiSource> _sources; + private readonly List<PhiSource> _sources; public int SourcesCount => _sources.Count; @@ -104,4 +104,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation _sources[index].Operand = source; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs index 20576a454..669c12816 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation GlobalMemoryS8, // TODO: Remove this and store type as a field on the Operation class itself. GlobalMemoryS16, // TODO: Remove this and store type as a field on the Operation class itself. GlobalMemoryU8, // TODO: Remove this and store type as a field on the Operation class itself. - GlobalMemoryU16 // TODO: Remove this and store type as a field on the Operation class itself. + GlobalMemoryU16, // TODO: Remove this and store type as a field on the Operation class itself. } static class StorageKindExtensions @@ -42,4 +42,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation storageKind == StorageKind.OutputPerPatch; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs index 6c20e856f..51ff09cf8 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs @@ -1,32 +1,34 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum TextureFlags { - None = 0, - Bindless = 1 << 0, - Gather = 1 << 1, + None = 0, + Bindless = 1 << 0, + Gather = 1 << 1, Derivatives = 1 << 2, - IntCoords = 1 << 3, - LodBias = 1 << 4, - LodLevel = 1 << 5, - Offset = 1 << 6, - Offsets = 1 << 7, - Coherent = 1 << 8, + IntCoords = 1 << 3, + LodBias = 1 << 4, + LodLevel = 1 << 5, + Offset = 1 << 6, + Offsets = 1 << 7, + Coherent = 1 << 8, - AtomicMask = 15 << 16, + AtomicMask = 15 << 16, - Add = 0 << 16, - Minimum = 1 << 16, - Maximum = 2 << 16, - Increment = 3 << 16, - Decrement = 4 << 16, - BitwiseAnd = 5 << 16, - BitwiseOr = 6 << 16, - BitwiseXor = 7 << 16, - Swap = 8 << 16, - CAS = 9 << 16 + Add = 0 << 16, + Minimum = 1 << 16, + Maximum = 2 << 16, + Increment = 3 << 16, + Decrement = 4 << 16, + BitwiseAnd = 5 << 16, + BitwiseOr = 6 << 16, + BitwiseXor = 7 << 16, + Swap = 8 << 16, + CAS = 9 << 16, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index 6ab868cdc..b467fe533 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -66,4 +66,4 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Flags |= TextureFlags.LodLevel; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/OutputTopology.cs b/src/Ryujinx.Graphics.Shader/OutputTopology.cs index 6f977becb..dc4b304ad 100644 --- a/src/Ryujinx.Graphics.Shader/OutputTopology.cs +++ b/src/Ryujinx.Graphics.Shader/OutputTopology.cs @@ -2,23 +2,22 @@ namespace Ryujinx.Graphics.Shader { enum OutputTopology { - PointList = 1, - LineStrip = 6, - TriangleStrip = 7 + PointList = 1, + LineStrip = 6, + TriangleStrip = 7, } static class OutputTopologyExtensions { public static string ToGlslString(this OutputTopology topology) { - switch (topology) + return topology switch { - case OutputTopology.LineStrip: return "line_strip"; - case OutputTopology.PointList: return "points"; - case OutputTopology.TriangleStrip: return "triangle_strip"; - } - - return "points"; + OutputTopology.LineStrip => "line_strip", + OutputTopology.PointList => "points", + OutputTopology.TriangleStrip => "triangle_strip", + _ => "points", + }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index 620f4ccf3..85e97368f 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -15,10 +15,10 @@ namespace Ryujinx.Graphics.Shader Mask = 0xff, - Array = 1 << 8, - Indexed = 1 << 9, + Array = 1 << 8, + Indexed = 1 << 9, Multisample = 1 << 10, - Shadow = 1 << 11 + Shadow = 1 << 11, } static class SamplerTypeExtensions @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Shader SamplerType.Texture2D => 2, SamplerType.Texture3D => 3, SamplerType.TextureCube => 3, - _ => throw new ArgumentException($"Invalid sampler type \"{type}\".") + _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), }; } @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader SamplerType.Texture2D => "sampler2D", SamplerType.Texture3D => "sampler3D", SamplerType.TextureCube => "samplerCube", - _ => throw new ArgumentException($"Invalid sampler type \"{type}\".") + _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), }; if ((type & SamplerType.Multisample) != 0) @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Shader SamplerType.Texture2D => "image2D", SamplerType.Texture3D => "image3D", SamplerType.TextureCube => "imageCube", - _ => throw new ArgumentException($"Invalid sampler type \"{type}\".") + _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."), }; if ((type & SamplerType.Multisample) != 0) @@ -90,11 +90,15 @@ namespace Ryujinx.Graphics.Shader switch (componentType) { - case AggregateType.U32: typeName = 'u' + typeName; break; - case AggregateType.S32: typeName = 'i' + typeName; break; + case AggregateType.U32: + typeName = 'u' + typeName; + break; + case AggregateType.S32: + typeName = 'i' + typeName; + break; } return typeName; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/ShaderIdentification.cs b/src/Ryujinx.Graphics.Shader/ShaderIdentification.cs index 3f0157626..551e318c0 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderIdentification.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderIdentification.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.Shader public enum ShaderIdentification { None, - GeometryLayerPassthrough + GeometryLayerPassthrough, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/ShaderProgram.cs b/src/Ryujinx.Graphics.Shader/ShaderProgram.cs index 29fff21e6..9e62491bf 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderProgram.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderProgram.cs @@ -32,4 +32,4 @@ namespace Ryujinx.Graphics.Shader Code = line + Environment.NewLine + Code; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index 30f0ffaa2..e87769bb9 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -48,4 +48,4 @@ namespace Ryujinx.Graphics.Shader FragmentOutputMap = fragmentOutputMap; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/ShaderStage.cs b/src/Ryujinx.Graphics.Shader/ShaderStage.cs index f16fe3281..f6cfe4bb2 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderStage.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderStage.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader Geometry, Fragment, - Count + Count, } public static class ShaderStageExtensions @@ -24,4 +24,4 @@ namespace Ryujinx.Graphics.Shader return stage == ShaderStage.Vertex || stage == ShaderStage.Fragment || stage == ShaderStage.Compute; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs index bb3fe7af4..efda774c6 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs @@ -27,9 +27,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public AstAssignment(IAstNode destination, IAstNode source) { Destination = destination; - Source = source; + Source = source; AddDef(destination, this); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs index 2f34bee83..826dbff88 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } - private LinkedList<IAstNode> _nodes; + private readonly LinkedList<IAstNode> _nodes; public IAstNode First => _nodes.First?.Value; public IAstNode Last => _nodes.Last?.Value; @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public AstBlock(AstBlockType type, IAstNode condition = null) { - Type = type; + Type = type; Condition = condition; _nodes = new LinkedList<IAstNode>(); @@ -114,4 +114,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs index c12efda90..a7dcc72a1 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Else, ElseIf, Main, - While + While, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs index 10d5dce0a..16efeff72 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs @@ -65,4 +65,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs index dabe623fd..1c82e646f 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Comment = comment; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs index 7aa0409b6..06d13c90b 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs @@ -49,9 +49,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public static AstOperand Local(AggregateType type) { - AstOperand local = new AstOperand(OperandType.LocalVariable); - - local.VarType = type; + AstOperand local = new(OperandType.LocalVariable) + { + VarType = type, + }; return local; } @@ -71,4 +72,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return node.LLNode.Previous?.Value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs index c667aac98..0b8246171 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs @@ -8,4 +8,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public LinkedListNode<IAstNode> LLNode { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs index 473aa2e7b..b64b96b8d 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs @@ -29,10 +29,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Value = operand.Value; } - public AstOperand(OperandType type, int value = 0) : this() + public AstOperand(OperandType type, int value = 0) : this() { - Type = type; + Type = type; Value = value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs index 4cf729d09..46555a85a 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public int Index { get; } - private IAstNode[] _sources; + private readonly IAstNode[] _sources; public int SourcesCount => _sources.Length; @@ -77,12 +77,18 @@ namespace Ryujinx.Graphics.Shader.StructuredIr switch (componentsCount) { - case 2: type |= AggregateType.Vector2; break; - case 3: type |= AggregateType.Vector3; break; - case 4: type |= AggregateType.Vector4; break; + case 2: + type |= AggregateType.Vector2; + break; + case 3: + type |= AggregateType.Vector3; + break; + case 4: + type |= AggregateType.Vector4; + break; } return type; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs index b71ae2c41..4fb5d02b3 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr // (this makes comparison with the disassembly easier). if (!context.Config.Options.Flags.HasFlag(TranslationFlags.DebugMode)) { - AstBlockVisitor visitor = new AstBlockVisitor(mainBlock); + AstBlockVisitor visitor = new(mainBlock); foreach (IAstNode node in visitor.Visit()) { @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr private static bool IsWorthPropagating(IAstNode source) { - if (!(source is AstOperation srcOp)) + if (source is not AstOperation srcOp) { return false; } @@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr private static void RemoveEmptyBlocks(AstBlock mainBlock) { - Queue<AstBlock> pending = new Queue<AstBlock>(); + Queue<AstBlock> pending = new(); pending.Enqueue(mainBlock); @@ -152,4 +152,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index a4e097eb6..4ff2035a8 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -28,4 +28,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Handle = handle; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs index 5afebc75f..e27594804 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Type = type; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs index 43a866626..1c25ed34d 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr enum BufferLayout { Std140, - Std430 + Std430, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs index 8bcf9d9c9..3ca1266f6 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System; using System.Collections.Generic; - using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; namespace Ryujinx.Graphics.Shader.StructuredIr @@ -110,16 +109,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (lLevel > rLevel) { - block = lBlock; + block = lBlock; blockLvl = lLevel; - other = rBlock; + other = rBlock; otherLvl = rLevel; } else /* if (rLevel > lLevel) */ { - block = rBlock; + block = rBlock; blockLvl = rLevel; - other = lBlock; + other = lBlock; otherLvl = lLevel; } @@ -144,7 +143,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AstBlock[] path = BackwardsPath(block, ParentBlock(stmt.Label)); - AstBlock loopFirstStmt = path[path.Length - 1]; + AstBlock loopFirstStmt = path[^1]; if (loopFirstStmt.Type == AstBlockType.Else) { @@ -194,7 +193,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr loopBlock.AddAfter(child, stmt.Goto); - block = loopBlock; + block = loopBlock; gLevel = loopLevel; } } @@ -252,7 +251,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr for (int index = path.Length - 1; index >= 0; index--) { AstBlock child = path[index]; - AstBlock last = child; + AstBlock last = child; if (child.Type == AstBlockType.If) { @@ -265,7 +264,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr else if (child.Type == AstBlockType.Else) { // Modify the matching if condition to force the else to be entered by the goto. - if (!(Previous(child) is AstBlock ifBlock) || ifBlock.Type != AstBlockType.If) + if (Previous(child) is not AstBlock ifBlock || ifBlock.Type != AstBlockType.If) { throw new InvalidOperationException("Found an else without a matching if."); } @@ -332,7 +331,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { AstBlock block = ParentBlock(stmt.Goto); - AstBlock newBlock = new AstBlock(AstBlockType.If, stmt.Condition); + AstBlock newBlock = new(AstBlockType.If, stmt.Condition); block.AddAfter(stmt.Goto, newBlock); @@ -340,11 +339,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } private static AstBlock Enclose( - AstBlock block, + AstBlock block, AstBlockType type, - IAstNode cond, - IAstNode first, - IAstNode last = null) + IAstNode cond, + IAstNode first, + IAstNode last = null) { if (first == last) { @@ -367,7 +366,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return first as AstBlock; } - AstBlock newBlock = new AstBlock(type, cond); + AstBlock newBlock = new(type, cond); block.AddBefore(first, newBlock); @@ -387,7 +386,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr private static bool BlockMatches(IAstNode node, AstBlockType type, IAstNode cond) { - if (!(node is AstBlock block)) + if (node is not AstBlock block) { return false; } @@ -399,7 +398,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { if (lCond is AstOperation lCondOp && lCondOp.Inst == Instruction.LogicalNot) { - if (!(rCond is AstOperation rCondOp) || rCondOp.Inst != lCondOp.Inst) + if (rCond is not AstOperation rCondOp || rCondOp.Inst != lCondOp.Inst) { return false; } @@ -418,7 +417,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return block.Parent; } - while (!(node is AstBlock)) + while (node is not AstBlock) { node = node.Parent; } @@ -430,7 +429,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { AstBlock block = bottom; - List<AstBlock> path = new List<AstBlock>(); + List<AstBlock> path = new(); while (block != top) { @@ -456,4 +455,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return level; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs index 25216e55f..4607a16c1 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { class GotoStatement { - public AstOperation Goto { get; } + public AstOperation Goto { get; } public AstAssignment Label { get; } public IAstNode Condition => Label.Destination; @@ -15,9 +15,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public GotoStatement(AstOperation branch, AstAssignment label, bool isLoop) { - Goto = branch; - Label = label; + Goto = branch; + Label = label; IsLoop = isLoop; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index ed910f96d..73ce90827 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { MultiplyHighS32 = 1 << 2, MultiplyHighU32 = 1 << 3, - Shuffle = 1 << 4, - ShuffleDown = 1 << 5, - ShuffleUp = 1 << 6, - ShuffleXor = 1 << 7, - SwizzleAdd = 1 << 10, - FSI = 1 << 11 + Shuffle = 1 << 4, + ShuffleDown = 1 << 5, + ShuffleUp = 1 << 6, + ShuffleXor = 1 << 7, + SwizzleAdd = 1 << 10, + FSI = 1 << 11, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs index 5ececbb5e..248d8d69f 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs @@ -8,4 +8,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr LinkedListNode<IAstNode> LLNode { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index b08478ad3..6cd0fd086 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -19,12 +19,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } - private static InstInfo[] _infoTbl; + private static readonly InstInfo[] _infoTbl; static InstructionInfo() { _infoTbl = new InstInfo[(int)Instruction.Count]; +#pragma warning disable IDE0055 // Disable formatting // Inst Destination type Source 1 type Source 2 type Source 3 type Source 4 type Add(Instruction.AtomicAdd, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); Add(Instruction.AtomicAnd, AggregateType.U32, AggregateType.S32, AggregateType.S32, AggregateType.U32); @@ -130,6 +131,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.VoteAll, AggregateType.Bool, AggregateType.Bool); Add(Instruction.VoteAllEqual, AggregateType.Bool, AggregateType.Bool); Add(Instruction.VoteAny, AggregateType.Bool, AggregateType.Bool); +#pragma warning restore IDE0055v } private static void Add(Instruction inst, AggregateType destType, params AggregateType[] srcTypes) @@ -201,4 +203,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return _infoTbl[(int)(inst & Instruction.Mask)].SrcTypes.Length == 1; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs index 21a1b3f08..0a0681fa4 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/IoDefinition.cs @@ -41,4 +41,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return $"{StorageKind}.{IoVariable}.{Location}.{Component}"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs index c0bb750e7..3ea69fde1 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs @@ -15,4 +15,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr ArrayLength = arrayLength; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs index 48060f6b9..638a5298f 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs @@ -25,8 +25,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr OperandType.Argument => AggregateType.S32, OperandType.Constant => AggregateType.S32, OperandType.Undefined => AggregateType.S32, - _ => throw new ArgumentException($"Invalid operand type \"{type}\".") + _ => throw new ArgumentException($"Invalid operand type \"{type}\"."), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs index 541ca298e..8b1cb9c56 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr BasicBlock srcBlock = phi.GetBlock(index); - Operation copyOp = new Operation(Instruction.Copy, phi.Dest, src); + Operation copyOp = new(Instruction.Copy, phi.Dest, src); srcBlock.Append(copyOp); } @@ -42,4 +42,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index c6132ef8c..1da5cb657 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -48,4 +48,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return id; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs index 17f497386..fdf824f57 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs @@ -2,7 +2,7 @@ using Ryujinx.Graphics.Shader.Translation; namespace Ryujinx.Graphics.Shader.StructuredIr { - struct StructureField + readonly struct StructureField { public AggregateType Type { get; } public string Name { get; } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs index 61c4fed73..aa5e13867 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredFunction.cs @@ -39,4 +39,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr : InArguments[index]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 9d12a73cd..a4e6444b0 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { public static StructuredProgramInfo MakeStructuredProgram(IReadOnlyList<Function> functions, ShaderConfig config) { - StructuredProgramContext context = new StructuredProgramContext(config); + StructuredProgramContext context = new(config); for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AggregateType returnType = function.ReturnsValue ? AggregateType.S32 : AggregateType.Void; - AggregateType[] inArguments = new AggregateType[function.InArgumentsCount]; + AggregateType[] inArguments = new AggregateType[function.InArgumentsCount]; AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount]; for (int i = 0; i < inArguments.Length; i++) @@ -79,7 +79,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value; bool isOutput = storageKind.IsOutput(); - bool perPatch = storageKind.IsPerPatch(); int location = 0; int component = 0; @@ -169,9 +168,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr switch (componentsCount) { - case 2: destType |= AggregateType.Vector2; break; - case 3: destType |= AggregateType.Vector3; break; - case 4: destType |= AggregateType.Vector4; break; + case 2: + destType |= AggregateType.Vector2; + break; + case 3: + destType |= AggregateType.Vector3; + break; + case 4: + destType |= AggregateType.Vector4; + break; } AstOperand destVec = context.NewTemp(destType); @@ -181,7 +186,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr for (int i = 0; i < operation.DestsCount; i++) { AstOperand dest = context.GetOperand(operation.GetDest(i)); - AstOperand index = new AstOperand(OperandType.Constant, i); + AstOperand index = new(OperandType.Constant, i); dest.VarType = destElemType; @@ -202,7 +207,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } bool isCondSel = inst == Instruction.ConditionalSelect; - bool isCopy = inst == Instruction.Copy; + bool isCopy = inst == Instruction.Copy; if (isCondSel || isCopy) { @@ -304,9 +309,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr private static AggregateType GetVarTypeFromUses(Operand dest) { - HashSet<Operand> visited = new HashSet<Operand>(); + HashSet<Operand> visited = new(); - Queue<Operand> pending = new Queue<Operand>(); + Queue<Operand> pending = new(); bool Enqueue(Operand operand) { @@ -385,7 +390,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { Instruction.ImageLoad or Instruction.TextureSample => true, - _ => false + _ => false, }; } @@ -396,7 +401,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Instruction.Branch or Instruction.BranchIfFalse or Instruction.BranchIfTrue => true, - _ => false + _ => false, }; } @@ -408,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Instruction.BitwiseExclusiveOr or Instruction.BitwiseNot or Instruction.BitwiseOr => true, - _ => false + _ => false, }; } @@ -420,8 +425,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Instruction.BitwiseExclusiveOr => Instruction.LogicalExclusiveOr, Instruction.BitwiseNot => Instruction.LogicalNot, Instruction.BitwiseOr => Instruction.LogicalOr, - _ => throw new ArgumentException($"Unexpected instruction \"{inst}\".") + _ => throw new ArgumentException($"Unexpected instruction \"{inst}\"."), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index a4d079914..019fc332b 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -3,7 +3,6 @@ using Ryujinx.Graphics.Shader.Translation; using System.Collections.Generic; using System.Linq; using System.Numerics; - using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; namespace Ryujinx.Graphics.Shader.StructuredIr @@ -165,7 +164,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr // this is not valid as the loop condition would be evaluated, // and it could erroneously jump back to the start of the loop. bool inRange = - block.Branch.Index < _currEndIndex || + block.Branch.Index < _currEndIndex || (block.Branch.Index == _currEndIndex && block.Branch.Index < _loopEndIndex); bool isLoop = block.Branch.Index <= block.Index; @@ -184,11 +183,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AddNode(Assign(gotoTempAsg.Destination, cond)); - AstOperation branch = new AstOperation(branchOp.Inst); + AstOperation branch = new(branchOp.Inst); AddNode(branch); - GotoStatement gotoStmt = new GotoStatement(branch, gotoTempAsg, isLoop); + GotoStatement gotoStmt = new(branch, gotoTempAsg, isLoop); _gotos.Add(gotoStmt); } @@ -236,13 +235,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr private void NewBlock(AstBlockType type, IAstNode cond, int endIndex) { - AstBlock childBlock = new AstBlock(type, cond); + AstBlock childBlock = new(type, cond); AddNode(childBlock); _blockStack.Push((_currBlock, _currEndIndex, _loopEndIndex)); - _currBlock = childBlock; + _currBlock = childBlock; _currEndIndex = endIndex; if (type == AstBlockType.DoWhile) @@ -316,7 +315,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr new AstOperand(OperandType.Constant, binding), new AstOperand(OperandType.Constant, 0), new AstOperand(OperandType.Constant, vecIndex), - new AstOperand(OperandType.Constant, elemIndex) + new AstOperand(OperandType.Constant, elemIndex), }; return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, false, sources, sources.Length); @@ -349,4 +348,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return astOperand; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index c51041467..4f18c7fd7 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -33,4 +33,4 @@ namespace Ryujinx.Graphics.Shader.StructuredIr IoDefinitions = new HashSet<IoDefinition>(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs index 5eb7fe467..24a99345a 100644 --- a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs +++ b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs @@ -20,22 +20,22 @@ namespace Ryujinx.Graphics.Shader FragmentIsBgra, ViewportInverse, FragmentRenderScaleCount, - RenderScale + RenderScale, } public struct SupportBuffer { internal const int Binding = 0; - public static int FieldSize; - public static int RequiredSize; + public static readonly int FieldSize; + public static readonly int RequiredSize; - public static int FragmentAlphaTestOffset; - public static int FragmentIsBgraOffset; - public static int ViewportInverseOffset; - public static int FragmentRenderScaleCountOffset; - public static int GraphicsRenderScaleOffset; - public static int ComputeRenderScaleOffset; + public static readonly int FragmentAlphaTestOffset; + public static readonly int FragmentIsBgraOffset; + public static readonly int ViewportInverseOffset; + public static readonly int FragmentRenderScaleCountOffset; + public static readonly int GraphicsRenderScaleOffset; + public static readonly int ComputeRenderScaleOffset; public const int FragmentIsBgraCount = 8; // One for the render target, 64 for the textures, and 8 for the images. @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Shader FieldSize = Unsafe.SizeOf<Vector4<float>>(); RequiredSize = Unsafe.SizeOf<SupportBuffer>(); - SupportBuffer instance = new SupportBuffer(); + SupportBuffer instance = new(); FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest); FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra); @@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Shader new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount), new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"), new StructureField(AggregateType.S32, "s_frag_scale_count"), - new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount) + new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount), }); } @@ -81,4 +81,4 @@ namespace Ryujinx.Graphics.Shader // Render scale max count: 1 + 64 + 8. First scale is fragment output scale, others are textures/image inputs. public Array73<Vector4<float>> RenderScale; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/TessPatchType.cs b/src/Ryujinx.Graphics.Shader/TessPatchType.cs index 2361b69f8..76be22fd4 100644 --- a/src/Ryujinx.Graphics.Shader/TessPatchType.cs +++ b/src/Ryujinx.Graphics.Shader/TessPatchType.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Shader { Isolines = 0, Triangles = 1, - Quads = 2 + Quads = 2, } static class TessPatchTypeExtensions @@ -15,8 +15,8 @@ namespace Ryujinx.Graphics.Shader { TessPatchType.Isolines => "isolines", TessPatchType.Quads => "quads", - _ => "triangles" + _ => "triangles", }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/TessSpacing.cs b/src/Ryujinx.Graphics.Shader/TessSpacing.cs index 35c44190c..6035366c1 100644 --- a/src/Ryujinx.Graphics.Shader/TessSpacing.cs +++ b/src/Ryujinx.Graphics.Shader/TessSpacing.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Shader { EqualSpacing = 0, FractionalEventSpacing = 1, - FractionalOddSpacing = 2 + FractionalOddSpacing = 2, } static class TessSpacingExtensions @@ -15,8 +15,8 @@ namespace Ryujinx.Graphics.Shader { TessSpacing.FractionalEventSpacing => "fractional_even_spacing", TessSpacing.FractionalOddSpacing => "fractional_odd_spacing", - _ => "equal_spacing" + _ => "equal_spacing", }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs index 85ea9adbe..626faa695 100644 --- a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs @@ -16,12 +16,12 @@ namespace Ryujinx.Graphics.Shader public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int cbufSlot, int handleIndex) { - Binding = binding; - Type = type; - Format = format; - CbufSlot = cbufSlot; + Binding = binding; + Type = type; + Format = format; + CbufSlot = cbufSlot; HandleIndex = handleIndex; - Flags = TextureUsageFlags.None; + Flags = TextureUsageFlags.None; } public TextureDescriptor SetFlag(TextureUsageFlags flag) @@ -31,4 +31,4 @@ namespace Ryujinx.Graphics.Shader return this; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/TextureFormat.cs b/src/Ryujinx.Graphics.Shader/TextureFormat.cs index d4c8b96be..f6e57fe8f 100644 --- a/src/Ryujinx.Graphics.Shader/TextureFormat.cs +++ b/src/Ryujinx.Graphics.Shader/TextureFormat.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Shader R32G32B32A32Sint, R10G10B10A2Unorm, R10G10B10A2Uint, - R11G11B10Float + R11G11B10Float, } static class TextureFormatExtensions @@ -52,6 +52,7 @@ namespace Ryujinx.Graphics.Shader { return format switch { +#pragma warning disable IDE0055 // Disable formatting TextureFormat.R8Unorm => "r8", TextureFormat.R8Snorm => "r8_snorm", TextureFormat.R8Uint => "r8ui", @@ -91,7 +92,8 @@ namespace Ryujinx.Graphics.Shader TextureFormat.R10G10B10A2Unorm => "rgb10_a2", TextureFormat.R10G10B10A2Uint => "rgb10_a2ui", TextureFormat.R11G11B10Float => "r11f_g11f_b10f", - _ => string.Empty + _ => string.Empty, +#pragma warning restore IDE0055 }; } diff --git a/src/Ryujinx.Graphics.Shader/TextureHandle.cs b/src/Ryujinx.Graphics.Shader/TextureHandle.cs index a59c8cd4a..fc9ab2d67 100644 --- a/src/Ryujinx.Graphics.Shader/TextureHandle.cs +++ b/src/Ryujinx.Graphics.Shader/TextureHandle.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader CombinedSampler = 0, // Must be 0. SeparateSamplerHandle = 1, SeparateSamplerId = 2, - SeparateConstantSamplerHandle = 3 + SeparateConstantSamplerHandle = 3, } public static class TextureHandle diff --git a/src/Ryujinx.Graphics.Shader/TextureUsageFlags.cs b/src/Ryujinx.Graphics.Shader/TextureUsageFlags.cs index 2419a1de4..3ad1685b6 100644 --- a/src/Ryujinx.Graphics.Shader/TextureUsageFlags.cs +++ b/src/Ryujinx.Graphics.Shader/TextureUsageFlags.cs @@ -14,6 +14,6 @@ namespace Ryujinx.Graphics.Shader ResScaleUnsupported = 1 << 0, NeedsScaleValue = 1 << 1, ImageStore = 1 << 2, - ImageCoherent = 1 << 3 + ImageCoherent = 1 << 3, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs index a54eddc59..def8f1a9d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Ryujinx.Graphics.Shader.Translation { [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum AggregateType { Invalid, @@ -23,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.Translation Vector3 = 2 << ElementCountShift, Vector4 = 3 << ElementCountShift, - Array = 1 << 10 + Array = 1 << 10, } static class AggregateTypeExtensions @@ -37,7 +39,7 @@ namespace Ryujinx.Graphics.Shader.Translation AggregateType.S32 or AggregateType.U32 => 4, AggregateType.FP64 => 8, - _ => 0 + _ => 0, }; switch (type & AggregateType.ElementCountMask) diff --git a/src/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/src/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 683b0d8ac..f749cecb8 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -33,4 +33,4 @@ namespace Ryujinx.Graphics.Shader.Translation public const int UserAttributePerPatchBase = 0x18; public const int UserAttributePerPatchEnd = 0x200; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs b/src/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs index 65328fd7f..9b07c28f1 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs @@ -13,11 +13,11 @@ namespace Ryujinx.Graphics.Shader.Translation { Blocks = blocks; - HashSet<BasicBlock> visited = new HashSet<BasicBlock>(); + HashSet<BasicBlock> visited = new(); - Stack<BasicBlock> blockStack = new Stack<BasicBlock>(); + Stack<BasicBlock> blockStack = new(); - List<BasicBlock> postOrderBlocks = new List<BasicBlock>(blocks.Length); + List<BasicBlock> postOrderBlocks = new(blocks.Length); PostOrderMap = new int[blocks.Length]; @@ -50,9 +50,9 @@ namespace Ryujinx.Graphics.Shader.Translation public static ControlFlowGraph Create(Operation[] operations) { - Dictionary<Operand, BasicBlock> labels = new Dictionary<Operand, BasicBlock>(); + Dictionary<Operand, BasicBlock> labels = new(); - List<BasicBlock> blocks = new List<BasicBlock>(); + List<BasicBlock> blocks = new(); BasicBlock currentBlock = null; @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Shader.Translation void NewNextBlock() { - BasicBlock block = new BasicBlock(blocks.Count); + BasicBlock block = new(blocks.Count); blocks.Add(block); @@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Shader.Translation currentBlock.Operations.AddLast(operation); } - needsNewBlock = operation.Inst == Instruction.Branch || + needsNewBlock = operation.Inst == Instruction.Branch || operation.Inst == Instruction.BranchIfTrue || operation.Inst == Instruction.BranchIfFalse; @@ -173,4 +173,4 @@ namespace Ryujinx.Graphics.Shader.Translation return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Dominance.cs b/src/Ryujinx.Graphics.Shader/Translation/Dominance.cs index 09c2eb0fd..cd651ce08 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Dominance.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Dominance.cs @@ -91,4 +91,4 @@ namespace Ryujinx.Graphics.Shader.Translation } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 87e5457f9..9eedc3f91 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Translation @@ -84,7 +83,7 @@ namespace Ryujinx.Graphics.Shader.Translation public Operand Add(Instruction inst, Operand dest = null, params Operand[] sources) { - Operation operation = new Operation(inst, dest, sources); + Operation operation = new(inst, dest, sources); _operations.Add(operation); @@ -93,7 +92,7 @@ namespace Ryujinx.Graphics.Shader.Translation public Operand Add(Instruction inst, StorageKind storageKind, Operand dest = null, params Operand[] sources) { - Operation operation = new Operation(inst, storageKind, dest, sources); + Operation operation = new(inst, storageKind, dest, sources); _operations.Add(operation); @@ -104,7 +103,7 @@ namespace Ryujinx.Graphics.Shader.Translation { Operand[] dests = new[] { dest.Item1, dest.Item2 }; - Operation operation = new Operation(inst, 0, dests, sources); + Operation operation = new(inst, 0, dests, sources); Add(operation); @@ -430,7 +429,7 @@ namespace Ryujinx.Graphics.Shader.Translation AlphaTestOp.Less => Instruction.CompareLess, AlphaTestOp.LessOrEqual => Instruction.CompareLessOrEqual, AlphaTestOp.NotEqual => Instruction.CompareNotEqual, - _ => 0 + _ => 0, }; Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\"."); @@ -532,4 +531,4 @@ namespace Ryujinx.Graphics.Shader.Translation return _operations.ToArray(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 0ba26107c..c2f1b790f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -850,4 +850,4 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.VoteAny, Local(), a); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 59d35d906..9d4d032af 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.Translation // Affected by resolution scaling. IntegerSampling = 1 << 0, - FragCoordXY = 1 << 1, + FragCoordXY = 1 << 1, Bindless = 1 << 2, InstanceId = 1 << 3, @@ -23,6 +23,6 @@ namespace Ryujinx.Graphics.Shader.Translation OaIndexing = 1 << 8, FixedFuncAttr = 1 << 9, LocalMemory = 1 << 10, - SharedMemory = 1 << 11 + SharedMemory = 1 << 11, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs b/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs index 073e120a3..714a9d68c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Shader.Translation { static class FunctionMatch { - private static IPatternTreeNode[] _fsiGetAddressTree = PatternTrees.GetFsiGetAddress(); - private static IPatternTreeNode[] _fsiGetAddressV2Tree = PatternTrees.GetFsiGetAddressV2(); - private static IPatternTreeNode[] _fsiIsLastWarpThreadPatternTree = PatternTrees.GetFsiIsLastWarpThread(); - private static IPatternTreeNode[] _fsiBeginPatternTree = PatternTrees.GetFsiBeginPattern(); - private static IPatternTreeNode[] _fsiEndPatternTree = PatternTrees.GetFsiEndPattern(); + private static readonly IPatternTreeNode[] _fsiGetAddressTree = PatternTrees.GetFsiGetAddress(); + private static readonly IPatternTreeNode[] _fsiGetAddressV2Tree = PatternTrees.GetFsiGetAddressV2(); + private static readonly IPatternTreeNode[] _fsiIsLastWarpThreadPatternTree = PatternTrees.GetFsiIsLastWarpThread(); + private static readonly IPatternTreeNode[] _fsiBeginPatternTree = PatternTrees.GetFsiBeginPattern(); + private static readonly IPatternTreeNode[] _fsiEndPatternTree = PatternTrees.GetFsiEndPattern(); public static void RunPass(DecodedProgram program) { @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.Translation private enum TreeNodeType : byte { Op, - Label + Label, } private class TreeNode @@ -150,9 +150,9 @@ namespace Ryujinx.Graphics.Shader.Translation private static TreeNode[] BuildTree(Block[] blocks) { - List<TreeNode> nodes = new List<TreeNode>(); + List<TreeNode> nodes = new(); - Dictionary<ulong, TreeNode> labels = new Dictionary<ulong, TreeNode>(); + Dictionary<ulong, TreeNode> labels = new(); TreeNodeUse[] predDefs = new TreeNodeUse[RegisterConsts.PredsCount]; TreeNodeUse[] gprDefs = new TreeNodeUse[RegisterConsts.GprsCount]; @@ -223,7 +223,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (block.Predecessors.Count > 1) { - TreeNode label = new TreeNode(order++); + TreeNode label = new(order++); nodes.Add(label); labels.Add(block.Address, label); } @@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.Shader.Translation { InstOp op = block.OpCodes[opIndex]; - TreeNode node = new TreeNode(op, IsOrderDependant(op.Name) ? order : (byte)0); + TreeNode node = new(op, IsOrderDependant(op.Name) ? order : (byte)0); // Add uses. @@ -288,7 +288,7 @@ namespace Ryujinx.Graphics.Shader.Translation InstProps.SPd => 30, InstProps.TPd => 51, InstProps.VPd => 45, - _ => throw new InvalidOperationException($"Table has unknown predicate destination {pdType}.") + _ => throw new InvalidOperationException($"Table has unknown predicate destination {pdType}."), }; byte predIndex = (byte)((op.RawOpCode >> bit) & 7); @@ -350,7 +350,7 @@ namespace Ryujinx.Graphics.Shader.Translation public IPatternTreeNode Node { get; } public int Index { get; } public bool Inverted { get; } - public PatternTreeNodeUse Inv => new PatternTreeNodeUse(Index, !Inverted, Node); + public PatternTreeNodeUse Inv => new(Index, !Inverted, Node); private PatternTreeNodeUse(int index, bool inverted, IPatternTreeNode node) { @@ -373,7 +373,7 @@ namespace Ryujinx.Graphics.Shader.Translation public TreeNodeType Type { get; } public byte Order { get; } public bool IsImm { get; } - public PatternTreeNodeUse Out => new PatternTreeNodeUse(0, this); + public PatternTreeNodeUse Out => new(0, this); public PatternTreeNode(InstName name, Func<T, bool> match, TreeNodeType type = TreeNodeType.Op, byte order = 0, bool isImm = false) { @@ -435,7 +435,7 @@ namespace Ryujinx.Graphics.Shader.Translation } DecodedFunction callTarget = program.GetFunctionByAddress(callOp.GetAbsoluteAddress()); - TreeNode[] callTargetTree = null; + TreeNode[] callTargetTree; if (callTarget == null || !Matches(_fsiIsLastWarpThreadPatternTree, callTargetTree = BuildTree(callTarget.Blocks))) { @@ -548,7 +548,7 @@ namespace Ryujinx.Graphics.Shader.Translation .Use(PT) .Use(orderingTicketValue).Out), Iadd(x: true, 0, 405).Use(PT).Use(RZ), - Ret().Use(PT) + Ret().Use(PT), }; } @@ -576,7 +576,7 @@ namespace Ryujinx.Graphics.Shader.Translation .Use(PT) .Use(orderingTicketValue).Out).Out), Iadd(x: true, 0, 405).Use(PT).Use(RZ), - Ret().Use(PT) + Ret().Use(PT), }; } @@ -603,7 +603,7 @@ namespace Ryujinx.Graphics.Shader.Translation .Use(threadKillValue).OutAt(1)) .Use(RZ).Out).OutAt(1)).Out) .Use(laneIdValue), - Ret().Use(PT) + Ret().Use(PT), }; } @@ -638,7 +638,7 @@ namespace Ryujinx.Graphics.Shader.Translation .Use(PT) .Use(addressLowValue).Out).Inv) .Use(label.Out), - Ret().Use(PT) + Ret().Use(PT), }; } @@ -684,7 +684,7 @@ namespace Ryujinx.Graphics.Shader.Translation .Use(incrementValue) .Use(popcResult) .Use(RZ).Out).Out), - Ret().Use(PT) + Ret().Use(PT), }; } @@ -806,7 +806,6 @@ namespace Ryujinx.Graphics.Shader.Translation private static PatternTreeNodeUse PT => PTOrRZ(); private static PatternTreeNodeUse RZ => PTOrRZ(); - private static PatternTreeNodeUse Undef => new PatternTreeNodeUse(0, null); private static PatternTreeNodeUse CallArg(int index) { diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs index 51a396821..2addff5c0 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System; using System.Collections.Generic; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Translation @@ -65,13 +64,13 @@ namespace Ryujinx.Graphics.Shader.Translation HelperFunctionName.ConvertFloatToDouble => GenerateConvertFloatToDoubleFunction(), HelperFunctionName.TexelFetchScale => GenerateTexelFetchScaleFunction(), HelperFunctionName.TextureSizeUnscale => GenerateTextureSizeUnscaleFunction(), - _ => throw new ArgumentException($"Invalid function name {functionName}") + _ => throw new ArgumentException($"Invalid function name {functionName}"), }; } - private Function GenerateConvertDoubleToFloatFunction() + private static Function GenerateConvertDoubleToFloatFunction() { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand valueLow = Argument(0); Operand valueHigh = Argument(1); @@ -119,9 +118,9 @@ namespace Ryujinx.Graphics.Shader.Translation return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ConvertDoubleToFloat", true, 2, 0); } - private Function GenerateConvertFloatToDoubleFunction() + private static Function GenerateConvertFloatToDoubleFunction() { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand value = Argument(0); @@ -164,13 +163,13 @@ namespace Ryujinx.Graphics.Shader.Translation HelperFunctionName.SharedAtomicMinS32 => GenerateSharedAtomicSigned(id, isMin: true), HelperFunctionName.SharedStore8 => GenerateSharedStore8(id), HelperFunctionName.SharedStore16 => GenerateSharedStore16(id), - _ => throw new ArgumentException($"Invalid function name {functionName}") + _ => throw new ArgumentException($"Invalid function name {functionName}"), }; } private static Function GenerateSharedAtomicSigned(int id, bool isMin) { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand wordOffset = Argument(0); Operand value = Argument(1); @@ -199,7 +198,7 @@ namespace Ryujinx.Graphics.Shader.Translation private static Function GenerateSharedStore(int id, int bitSize) { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand offset = Argument(0); Operand value = Argument(1); @@ -219,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Translation private Function GenerateTexelFetchScaleFunction() { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand input = Argument(0); Operand samplerIndex = Argument(1); @@ -270,7 +269,7 @@ namespace Ryujinx.Graphics.Shader.Translation private Function GenerateTextureSizeUnscaleFunction() { - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand input = Argument(0); Operand samplerIndex = Argument(1); @@ -328,4 +327,4 @@ namespace Ryujinx.Graphics.Shader.Translation } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs index 984f2d047..e5af17355 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs @@ -9,6 +9,6 @@ namespace Ryujinx.Graphics.Shader.Translation SharedStore8, SharedStore16, TexelFetchScale, - TextureSizeUnscale + TextureSizeUnscale, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index 0c196c4d0..bb25c1602 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // - Both sources of the OR operation comes from a constant buffer. for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) { - if (!(node.Value is TextureOperation texOp)) + if (node.Value is not TextureOperation texOp) { continue; } @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - if (!(bindlessHandle.AsgOp is Operation handleCombineOp)) + if (bindlessHandle.AsgOp is not Operation handleCombineOp) { continue; } @@ -66,9 +66,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // and having a "canonical" representation simplifies some checks below. if (src0.Type == OperandType.Constant && src1.Type != OperandType.Constant) { - Operand temp = src1; - src1 = src0; - src0 = temp; + (src0, src1) = (src1, src0); } TextureHandleType handleType = TextureHandleType.SeparateSamplerHandle; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs index 9a3ae1b8f..f966a4fc5 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // The base offset of the array of handles on the constant buffer is the constant offset. for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) { - if (!(node.Value is TextureOperation texOp)) + if (node.Value is not TextureOperation texOp) { continue; } @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - if (!(texOp.GetSource(0).AsgOp is Operation handleAsgOp)) + if (texOp.GetSource(0).AsgOp is not Operation handleAsgOp) { continue; } @@ -64,17 +64,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // Plus this whole transform is fundamentally flawed as-is since we have no way to know the array size. // Eventually, this should be entirely removed in favor of a implementation that supports true bindless // texture access. - if (!(ldcSrc2.AsgOp is Operation shrOp) || shrOp.Inst != Instruction.ShiftRightU32) + if (ldcSrc2.AsgOp is not Operation shrOp || shrOp.Inst != Instruction.ShiftRightU32) { continue; } - if (!(shrOp.GetSource(0).AsgOp is Operation shrOp2) || shrOp2.Inst != Instruction.ShiftRightU32) + if (shrOp.GetSource(0).AsgOp is not Operation shrOp2 || shrOp2.Inst != Instruction.ShiftRightU32) { continue; } - if (!(shrOp2.GetSource(0).AsgOp is Operation addOp) || addOp.Inst != Instruction.Add) + if (shrOp2.GetSource(0).AsgOp is not Operation addOp || addOp.Inst != Instruction.Add) { continue; } @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand source = addOp.GetSource(0); - Operation shrBy3 = new Operation(Instruction.ShiftRightU32, index, source, Const(3)); + Operation shrBy3 = new(Instruction.ShiftRightU32, index, source, Const(3)); block.Operations.AddBefore(node, shrBy3); @@ -106,4 +106,4 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, handle); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs index c87d14748..bd2eceda5 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return false; } - if (!(nextBlock.Operations.First?.Value is Operation next)) + if (nextBlock.Operations.First?.Value is not Operation next) { return false; } @@ -61,4 +61,4 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return block; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs index 4caadb737..0cca0ac6c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs @@ -1,7 +1,6 @@ using Ryujinx.Common.Utilities; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Translation.Optimizations @@ -262,8 +261,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static int GetBitfieldExtractValue(Operation operation) { - int value = operation.GetSource(0).Value; - int lsb = operation.GetSource(1).Value; + int value = operation.GetSource(0).Value; + int lsb = operation.GetSource(1).Value; int length = operation.GetSource(2).Value; return value.Extract(lsb, length); @@ -278,13 +277,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations operation.TurnIntoCopy(ConstF((float)BitConverter.UInt16BitsToHalf((ushort)value))); } - private static void FPNegate(Operation operation) - { - float value = operation.GetSource(0).AsFloat(); - - operation.TurnIntoCopy(ConstF(-value)); - } - private static void EvaluateUnary(Operation operation, Func<int, int> op) { int x = operation.GetSource(0).Value; @@ -356,4 +348,4 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations operation.TurnIntoCopy(ConstF(op(x, y, z))); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs index 42bce5cc2..aec95a9cc 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/DoubleToFloat.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) { - if (node.Value is not Operation operation) + if (node.Value is not Operation) { continue; } @@ -67,4 +67,4 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 9d260c678..2433aeb20 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System; using System.Collections.Generic; using System.Linq; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Translation.Optimizations @@ -14,12 +13,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations enum LsMemoryType { Local, - Shared + Shared, } private class GtsContext { - private struct Entry + private readonly struct Entry { public readonly int FunctionId; public readonly Instruction Inst; @@ -42,7 +41,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - private struct LsKey : IEquatable<LsKey> + private readonly struct LsKey : IEquatable<LsKey> { public readonly Operand BaseOffset; public readonly int ConstOffset; @@ -127,7 +126,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations public void AddMemoryTargetCb(LsMemoryType type, Operand baseOffset, int constOffset, uint targetCb, SearchResult result) { - LsKey key = new LsKey(baseOffset, constOffset, type); + LsKey key = new(baseOffset, constOffset, type); if (!_sharedEntries.TryGetValue(key, out Dictionary<uint, SearchResult> targetCbs)) { @@ -162,7 +161,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations public bool TryGetMemoryTargetCb(LsMemoryType type, Operand baseOffset, int constOffset, out SearchResult result) { - LsKey key = new LsKey(baseOffset, constOffset, type); + LsKey key = new(baseOffset, constOffset, type); if (_sharedEntries.TryGetValue(key, out Dictionary<uint, SearchResult> targetCbs) && targetCbs.Count == 1) { @@ -182,9 +181,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - private struct SearchResult + private readonly struct SearchResult { - public static SearchResult NotFound => new SearchResult(-1, 0); + public static SearchResult NotFound => new(-1, 0); public bool Found => SbCbSlot != -1; public int SbCbSlot { get; } public int SbCbOffset { get; } @@ -208,13 +207,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) { - GtsContext gtsContext = new GtsContext(hfm); + GtsContext gtsContext = new(hfm); foreach (BasicBlock block in blocks) { for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) { - if (!(node.Value is Operation operation)) + if (node.Value is not Operation operation) { continue; } @@ -315,8 +314,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations int alignment = config.GpuAccessor.QueryHostStorageBufferOffsetAlignment(); - Operation maskOp = new Operation(Instruction.BitwiseAnd, baseAddressMasked, new[] { baseAddress, Const(-alignment) }); - Operation subOp = new Operation(Instruction.Subtract, hostOffset, new[] { globalAddress, baseAddressMasked }); + Operation maskOp = new(Instruction.BitwiseAnd, baseAddressMasked, baseAddress, Const(-alignment)); + Operation subOp = new(Instruction.Subtract, hostOffset, globalAddress, baseAddressMasked); node.List.AddBefore(node, maskOp); node.List.AddBefore(node, subOp); @@ -327,7 +326,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { Operand newOffset = Local(); - Operation addOp = new Operation(Instruction.Add, newOffset, new[] { offset, Const(result.ConstOffset) }); + Operation addOp = new(Instruction.Add, newOffset, offset, Const(result.ConstOffset)); node.List.AddBefore(node, addOp); @@ -394,26 +393,26 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (operation.Inst == Instruction.AtomicCompareAndSwap) { - sources = new Operand[] + sources = new[] { Const(binding), Const(0), wordOffset, operation.GetSource(operation.SourcesCount - 2), - operation.GetSource(operation.SourcesCount - 1) + operation.GetSource(operation.SourcesCount - 1), }; } else if (isStore) { - sources = new Operand[] { Const(binding), Const(0), wordOffset, operation.GetSource(operation.SourcesCount - 1) }; + sources = new[] { Const(binding), Const(0), wordOffset, operation.GetSource(operation.SourcesCount - 1) }; } else { - sources = new Operand[] { Const(binding), Const(0), wordOffset }; + sources = new[] { Const(binding), Const(0), wordOffset }; } - Operation shiftOp = new Operation(Instruction.ShiftRightU32, wordOffset, new[] { offset, Const(2) }); - Operation storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources); + Operation shiftOp = new(Instruction.ShiftRightU32, wordOffset, offset, Const(2)); + Operation storageOp = new(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources); node.List.AddBefore(node, shiftOp); LinkedListNode<INode> newNode = node.List.AddBefore(node, storageOp); @@ -455,7 +454,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations bool returnsValue = operation.Dest != null; Operand returnValue = returnsValue ? Local() : null; - Operation callOp = new Operation(Instruction.Call, returnValue, sources); + Operation callOp = new(Instruction.Call, returnValue, sources); LinkedListNode<INode> newNode = node.List.AddBefore(node, callOp); @@ -480,7 +479,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations SearchResult result, out int functionId) { - List<uint> targetCbs = new List<uint>() { PackCbSlotAndOffset(result.SbCbSlot, result.SbCbOffset) }; + List<uint> targetCbs = new() { PackCbSlotAndOffset(result.SbCbSlot, result.SbCbOffset) }; if (gtsContext.TryGetFunctionId(operation, isMultiTarget: false, targetCbs, out functionId)) { @@ -498,7 +497,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations inArgumentsCount = 2; } - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand offset = Argument(0); Operand compare = null; @@ -542,7 +541,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations string functionName = GetFunctionName(operation, isMultiTarget: false, targetCbs); - Function function = new Function( + Function function = new( ControlFlowGraph.Create(context.GetOperations()).Blocks, functionName, returnsValue, @@ -561,9 +560,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operation operation, out int functionId) { - Queue<PhiNode> phis = new Queue<PhiNode>(); - HashSet<PhiNode> visited = new HashSet<PhiNode>(); - List<uint> targetCbs = new List<uint>(); + Queue<PhiNode> phis = new(); + HashSet<PhiNode> visited = new(); + List<uint> targetCbs = new(); Operand globalAddress = operation.GetSource(0); @@ -644,7 +643,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations inArgumentsCount = 3; } - EmitterContext context = new EmitterContext(); + EmitterContext context = new(); Operand globalAddressLow = Argument(0); Operand globalAddressHigh = Argument(1); @@ -684,7 +683,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations value = Argument(2); } - SearchResult result = new SearchResult(sbCbSlot, sbCbOffset); + SearchResult result = new(sbCbSlot, sbCbOffset); int alignment = config.GpuAccessor.QueryHostStorageBufferOffsetAlignment(); @@ -731,7 +730,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations string functionName = GetFunctionName(operation, isMultiTarget: true, targetCbs); - Function function = new Function( + Function function = new( ControlFlowGraph.Create(context.GetOperations()).Blocks, functionName, returnsValue, @@ -763,7 +762,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations StorageKind.GlobalMemoryS16 => "S16", StorageKind.GlobalMemoryU8 => "U8", StorageKind.GlobalMemoryU16 => "U16", - _ => string.Empty + _ => string.Empty, }; if (isMultiTarget) @@ -871,7 +870,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations StorageKind.GlobalMemoryU8 => 8, StorageKind.GlobalMemoryS16 or StorageKind.GlobalMemoryU16 => 16, - _ => 32 + _ => 32, }; if (bitSize < 32) @@ -1137,4 +1136,4 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 8d2669c0d..e7805027f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -60,7 +60,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations bool isUnused = IsUnused(node.Value); - if (!(node.Value is Operation operation) || isUnused) + if (node.Value is not Operation operation || isUnused) { if (node.Value is PhiNode phi && !isUnused) { @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations modified = true; } else if ((operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation)) || - (operation.Inst == Instruction.ShuffleXor && MatchDdxOrDdy(operation))) + (operation.Inst == Instruction.ShuffleXor && MatchDdxOrDdy(operation))) { if (DestHasNoUses(operation)) { @@ -124,7 +124,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // the destination operand. Operand dest = copyOp.Dest; - Operand src = copyOp.GetSource(0); + Operand src = copyOp.GetSource(0); INode[] uses = dest.UseOps.ToArray(); @@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations foreach (INode useNode in uses) { - if (!(useNode is Operation operation) || operation.Inst != Instruction.UnpackHalf2x16) + if (useNode is not Operation operation || operation.Inst != Instruction.UnpackHalf2x16) { continue; } @@ -248,12 +248,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations foreach (INode use in uses) { - if (!(use is Operation test)) + if (use is not Operation test) { continue; } - if (!(use is Operation useOp) || useOp.Inst != Instruction.SwizzleAdd) + if (use is not Operation useOp || useOp.Inst != Instruction.SwizzleAdd) { continue; } @@ -323,7 +323,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand rhs = operation.GetSource(1); // Check LHS of the the main multiplication operation. We expect an input being multiplied by gl_FragCoord.w. - if (!(lhs.AsgOp is Operation attrMulOp) || attrMulOp.Inst != (Instruction.FP32 | Instruction.Multiply)) + if (lhs.AsgOp is not Operation attrMulOp || attrMulOp.Inst != (Instruction.FP32 | Instruction.Multiply)) { return; } @@ -338,7 +338,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } // RHS of the main multiplication should be a reciprocal operation (1.0 / x). - if (!(rhs.AsgOp is Operation reciprocalOp) || reciprocalOp.Inst != (Instruction.FP32 | Instruction.Divide)) + if (rhs.AsgOp is not Operation reciprocalOp || reciprocalOp.Inst != (Instruction.FP32 | Instruction.Divide)) { return; } @@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // from all the use lists on the operands that this node uses. block.Operations.Remove(llNode); - Queue<INode> nodes = new Queue<INode>(); + Queue<INode> nodes = new(); nodes.Enqueue(llNode.Value); @@ -457,4 +457,4 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs index 9b78c8aaa..a509fcb42 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs @@ -202,4 +202,4 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return operand.Value == comparand; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs index ffbd16f85..baf8e66e7 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Utils.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations public static bool IsInputLoad(INode node, IoVariable ioVariable, int elemIndex) { - if (!(node is Operation operation) || + if (node is not Operation operation || operation.Inst != Instruction.Load || operation.StorageKind != StorageKind.Input || operation.SourcesCount != 2) diff --git a/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs b/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs index 9e31831de..e27e47070 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Shader.Translation static class RegisterUsage { private const int RegsCount = 256; - private const int RegsMask = RegsCount - 1; + private const int RegsMask = RegsCount - 1; private const int GprMasks = 4; private const int PredMasks = 1; @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Shader.Translation FlagMask = flagMask; } - public long GetMask(int index) + public readonly long GetMask(int index) { return index switch { @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Shader.Translation 3 => GprMask3, 4 => PredMask, 5 => FlagMask, - _ => throw new ArgumentOutOfRangeException(nameof(index)) + _ => throw new ArgumentOutOfRangeException(nameof(index)), }; } @@ -93,12 +93,12 @@ namespace Ryujinx.Graphics.Shader.Translation return !x.Equals(y); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is RegisterMask regMask && Equals(regMask); } - public bool Equals(RegisterMask other) + public readonly bool Equals(RegisterMask other) { return GprMask0 == other.GprMask0 && GprMask1 == other.GprMask1 && @@ -108,7 +108,7 @@ namespace Ryujinx.Graphics.Shader.Translation FlagMask == other.FlagMask; } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(GprMask0, GprMask1, GprMask2, GprMask3, PredMask, FlagMask); } @@ -121,18 +121,18 @@ namespace Ryujinx.Graphics.Shader.Translation public FunctionRegisterUsage(Register[] inArguments, Register[] outArguments) { - InArguments = inArguments; + InArguments = inArguments; OutArguments = outArguments; } } public static FunctionRegisterUsage RunPass(ControlFlowGraph cfg) { - List<Register> inArguments = new List<Register>(); - List<Register> outArguments = new List<Register>(); + List<Register> inArguments = new(); + List<Register> outArguments = new(); // Compute local register inputs and outputs used inside blocks. - RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Length]; + RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Length]; RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Length]; foreach (BasicBlock block in cfg.Blocks) @@ -165,11 +165,11 @@ namespace Ryujinx.Graphics.Shader.Translation // Compute global register inputs and outputs used across blocks. RegisterMask[] globalCmnOutputs = new RegisterMask[cfg.Blocks.Length]; - RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Length]; + RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Length]; RegisterMask[] globalOutputs = new RegisterMask[cfg.Blocks.Length]; - RegisterMask allOutputs = new RegisterMask(); - RegisterMask allCmnOutputs = new RegisterMask(-1L, -1L, -1L, -1L, -1L, -1L); + RegisterMask allOutputs = new(); + RegisterMask allCmnOutputs = new(-1L, -1L, -1L, -1L, -1L, -1L); bool modified; @@ -389,14 +389,14 @@ namespace Ryujinx.Graphics.Shader.Translation mask &= ~(1L << bit); - Register register = new Register(baseRegIndex + bit, regType); + Register register = new(baseRegIndex + bit, regType); if (fillArgsList) { inArguments.Add(register); } - Operation copyOp = new Operation(Instruction.Copy, OperandHelper.Register(register), OperandHelper.Argument(argIndex++)); + Operation copyOp = new(Instruction.Copy, OperandHelper.Register(register), OperandHelper.Argument(argIndex++)); if (node == null) { @@ -429,14 +429,14 @@ namespace Ryujinx.Graphics.Shader.Translation mask &= ~(1L << bit); - Register register = new Register(baseRegIndex + bit, regType); + Register register = new(baseRegIndex + bit, regType); if (fillArgsList) { outArguments.Add(register); } - Operation copyOp = new Operation(Instruction.Copy, OperandHelper.Argument(argIndex++), OperandHelper.Register(register)); + Operation copyOp = new(Instruction.Copy, OperandHelper.Argument(argIndex++), OperandHelper.Register(register)); if (node == null) { @@ -475,7 +475,7 @@ namespace Ryujinx.Graphics.Shader.Translation private static bool EndsWithReturn(BasicBlock block) { - if (!(block.GetLastOp() is Operation operation)) + if (block.GetLastOp() is not Operation operation) { return false; } @@ -483,4 +483,4 @@ namespace Ryujinx.Graphics.Shader.Translation return operation.Inst == Instruction.Return; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 3a46f6e4e..f3a5ba6c4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -13,10 +13,9 @@ namespace Ryujinx.Graphics.Shader.Translation private const int DefaultLocalMemorySize = 128; private const int DefaultSharedMemorySize = 4096; - private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; + private static readonly string[] _stagePrefixes = { "cp", "vp", "tcp", "tep", "gp", "fp" }; private readonly IGpuAccessor _gpuAccessor; - private readonly ShaderProperties _properties; private readonly string _stagePrefix; private readonly int[] _cbSlotToBindingMap; @@ -31,12 +30,12 @@ namespace Ryujinx.Graphics.Shader.Translation public int LocalMemoryId { get; private set; } public int SharedMemoryId { get; private set; } - public ShaderProperties Properties => _properties; + public ShaderProperties Properties { get; } public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties) { _gpuAccessor = gpuAccessor; - _properties = properties; + Properties = properties; _stagePrefix = GetShaderStagePrefix(stage); _cbSlotToBindingMap = new int[18]; @@ -83,7 +82,7 @@ namespace Ryujinx.Graphics.Shader.Translation size = DefaultSharedMemorySize; } - var smem = new MemoryDefinition("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); } @@ -211,7 +210,7 @@ namespace Ryujinx.Graphics.Shader.Translation (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset) { - Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None + Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None, }; } } @@ -226,39 +225,34 @@ namespace Ryujinx.Graphics.Shader.Translation private void AddNewConstantBuffer(int binding, string name) { - StructureType type = new StructureType(new[] + StructureType type = new(new[] { - new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16) + new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), }); - _properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); + Properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); } private void AddNewStorageBuffer(int binding, string name) { - StructureType type = new StructureType(new[] + StructureType type = new(new[] { - new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), }); - _properties.AddStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type)); + Properties.AddStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type)); } public static string GetShaderStagePrefix(ShaderStage stage) { uint index = (uint)stage; - if (index >= _stagePrefixes.Length) - { - return "invalid"; - } - - return _stagePrefixes[index]; + return index >= _stagePrefixes.Length ? "invalid" : _stagePrefixes[index]; } private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset) { - return sbCbOffset | ((int)sbCbSlot << 16); + return sbCbOffset | (sbCbSlot << 16); } private static (int, int) UnpackSbCbInfo(int key) @@ -266,4 +260,4 @@ namespace Ryujinx.Graphics.Shader.Translation return ((byte)(key >> 16), (ushort)key); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index f5a524a0f..42e3ecee3 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader.Translation.Optimizations; using System.Collections.Generic; using System.Diagnostics; using System.Linq; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Translation @@ -134,7 +133,7 @@ namespace Ryujinx.Graphics.Shader.Translation AggregateType.Vector2 => 2, AggregateType.Vector3 => 3, AggregateType.Vector4 => 4, - _ => 1 + _ => 1, }; if (elemCount == 1) @@ -154,9 +153,9 @@ namespace Ryujinx.Graphics.Shader.Translation inputs[srcIndex] = operation.GetSource(srcIndex); } - inputs[inputs.Length - 1] = Const(i); + inputs[^1] = Const(i); - Operation loadOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, value, inputs); + Operation loadOp = new(Instruction.Load, StorageKind.ConstantBuffer, value, inputs); node.List.AddBefore(node, loadOp); @@ -169,8 +168,8 @@ namespace Ryujinx.Graphics.Shader.Translation Operand isCurrentIndex = Local(); Operand selection = Local(); - Operation compareOp = new Operation(Instruction.CompareEqual, isCurrentIndex, new Operand[] { elemIndex, Const(i) }); - Operation selectOp = new Operation(Instruction.ConditionalSelect, selection, new Operand[] { isCurrentIndex, value, result }); + Operation compareOp = new(Instruction.CompareEqual, isCurrentIndex, new Operand[] { elemIndex, Const(i) }); + Operation selectOp = new(Instruction.ConditionalSelect, selection, new Operand[] { isCurrentIndex, value, result }); node.List.AddBefore(node, compareOp); node.List.AddBefore(node, selectOp); @@ -267,10 +266,8 @@ namespace Ryujinx.Graphics.Shader.Translation { TextureOperation texOp = (TextureOperation)node.Value; - bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; int coordsCount = texOp.Type.GetDimensions(); @@ -318,10 +315,7 @@ namespace Ryujinx.Graphics.Shader.Translation { TextureOperation texOp = (TextureOperation)node.Value; - bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; if (texOp.Inst == Instruction.TextureSize && @@ -383,8 +377,8 @@ namespace Ryujinx.Graphics.Shader.Translation TextureOperation texOp = (TextureOperation)node.Value; - bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); @@ -393,7 +387,6 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; int coordsCount = texOp.Type.GetDimensions(); @@ -453,7 +446,7 @@ namespace Ryujinx.Graphics.Shader.Translation TextureOperation texOp = (TextureOperation)node.Value; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; int gatherBiasPrecision = config.GpuAccessor.QueryHostGatherBiasPrecision(); @@ -462,10 +455,12 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } +#pragma warning disable IDE0059 // Remove unnecessary value assignment bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; +#pragma warning restore IDE0059 int coordsCount = texOp.Type.GetDimensions(); int coordsIndex = isBindless || isIndexed ? 1 : 0; @@ -536,7 +531,7 @@ namespace Ryujinx.Graphics.Shader.Translation TextureOperation texOp = (TextureOperation)node.Value; - bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; + bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QueryHostSupportsNonConstantTextureOffset(); @@ -548,16 +543,16 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + 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 intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0; + bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; - bool isArray = (texOp.Type & SamplerType.Array) != 0; - bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; - bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; + bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; int coordsCount = texOp.Type.GetDimensions(); @@ -647,12 +642,12 @@ namespace Ryujinx.Graphics.Shader.Translation if (hasLodBias) { - sources[dstIndex++] = texOp.GetSource(srcIndex++); + sources[dstIndex++] = texOp.GetSource(srcIndex++); } if (isGather && !isShadow) { - sources[dstIndex++] = texOp.GetSource(srcIndex++); + sources[dstIndex++] = texOp.GetSource(srcIndex++); } int coordsIndex = isBindless || isIndexed ? 1 : 0; @@ -712,7 +707,7 @@ namespace Ryujinx.Graphics.Shader.Translation newSources[coordsIndex + index] = coordPlusOffset; } - TextureOperation newTexOp = new TextureOperation( + TextureOperation newTexOp = new( Instruction.TextureSample, texOp.Type, texOp.Format, @@ -771,7 +766,7 @@ namespace Ryujinx.Graphics.Shader.Translation } } - TextureOperation newTexOp = new TextureOperation( + TextureOperation newTexOp = new( Instruction.TextureSample, texOp.Type, texOp.Format, @@ -862,13 +857,13 @@ namespace Ryujinx.Graphics.Shader.Translation int maxPositive = format switch { - TextureFormat.R8Snorm => sbyte.MaxValue, - TextureFormat.R8G8Snorm => sbyte.MaxValue, - TextureFormat.R8G8B8A8Snorm => sbyte.MaxValue, - TextureFormat.R16Snorm => short.MaxValue, - TextureFormat.R16G16Snorm => short.MaxValue, + TextureFormat.R8Snorm => sbyte.MaxValue, + TextureFormat.R8G8Snorm => sbyte.MaxValue, + TextureFormat.R8G8B8A8Snorm => sbyte.MaxValue, + TextureFormat.R16Snorm => short.MaxValue, + TextureFormat.R16G16Snorm => short.MaxValue, TextureFormat.R16G16B16A16Snorm => short.MaxValue, - _ => 0 + _ => 0, }; // The value being 0 means that the format is not a SNORM format, @@ -886,8 +881,8 @@ namespace Ryujinx.Graphics.Shader.Translation INode[] uses = dest.UseOps.ToArray(); - Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), dest); - Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive)); + Operation convOp = new(Instruction.ConvertS32ToFP32, Local(), dest); + Operation normOp = new(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive)); node = node.List.AddAfter(node, convOp); node = node.List.AddAfter(node, normOp); @@ -990,4 +985,4 @@ namespace Ryujinx.Graphics.Shader.Translation return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index e50c9a845..e93a709c2 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -126,9 +126,9 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize) { - Stage = stage; - GpuAccessor = gpuAccessor; - Options = options; + Stage = stage; + GpuAccessor = gpuAccessor; + Options = options; LocalMemorySize = localMemorySize; _transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>(); @@ -138,35 +138,35 @@ namespace Ryujinx.Graphics.Shader.Translation gpuAccessor.QueryTransformFeedbackEnabled() && gpuAccessor.QueryHostSupportsTransformFeedback(); - UsedInputAttributesPerPatch = new HashSet<int>(); + UsedInputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>(); _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); - _usedImages = new Dictionary<TextureInfo, TextureMeta>(); + _usedImages = new Dictionary<TextureInfo, TextureMeta>(); ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) { - StructureType tfeInfoStruct = new StructureType(new StructureField[] + StructureType tfeInfoStruct = new(new StructureField[] { - new StructureField(AggregateType.Array | AggregateType.U32, "base_offset", 4), - new StructureField(AggregateType.U32, "vertex_count") + new(AggregateType.Array | AggregateType.U32, "base_offset", 4), + new(AggregateType.U32, "vertex_count"), }); - BufferDefinition tfeInfoBuffer = new BufferDefinition(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); + BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); Properties.AddStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); - StructureType tfeDataStruct = new StructureType(new StructureField[] + StructureType tfeDataStruct = new(new StructureField[] { - new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + new(AggregateType.Array | AggregateType.U32, "data", 0), }); for (int i = 0; i < Constants.TfeBuffersCount; i++) { int binding = Constants.TfeBufferBaseBinding + i; - BufferDefinition tfeDataBuffer = new BufferDefinition(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); + BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); Properties.AddStorageBuffer(binding, tfeDataBuffer); } } @@ -180,8 +180,8 @@ namespace Ryujinx.Graphics.Shader.Translation TranslationOptions options) : this(stage, gpuAccessor, options, 0) { ThreadsPerInputPrimitive = 1; - OutputTopology = outputTopology; - MaxOutputVertices = maxOutputVertices; + OutputTopology = outputTopology; + MaxOutputVertices = maxOutputVertices; } public ShaderConfig( @@ -189,15 +189,15 @@ namespace Ryujinx.Graphics.Shader.Translation IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options, GetLocalMemorySize(header)) { - GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; + GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive; - OutputTopology = header.OutputTopology; - MaxOutputVertices = header.MaxOutputVertexCount; - ImapTypes = header.ImapTypes; - OmapTargets = header.OmapTargets; - OmapSampleMask = header.OmapSampleMask; - OmapDepth = header.OmapDepth; - LastInVertexPipeline = header.Stage < ShaderStage.Fragment; + OutputTopology = header.OutputTopology; + MaxOutputVertices = header.MaxOutputVertexCount; + ImapTypes = header.ImapTypes; + OmapTargets = header.OmapTargets; + OmapSampleMask = header.OmapSampleMask; + OmapDepth = header.OmapDepth; + LastInVertexPipeline = header.Stage < ShaderStage.Fragment; } private static int GetLocalMemorySize(ShaderHeader header) @@ -524,7 +524,7 @@ namespace Ryujinx.Graphics.Shader.Translation // Regular and per-patch input/output locations can't overlap, // so we must assign on our location using unused regular input/output locations. - Dictionary<int, int> locationsMap = new Dictionary<int, int>(); + Dictionary<int, int> locationsMap = new(); int freeMask = ~UsedOutputAttributes; @@ -718,7 +718,7 @@ namespace Ryujinx.Graphics.Shader.Translation { AccurateType = accurateType, Type = type, - UsageFlags = usageFlags + UsageFlags = usageFlags, }; if (dict.TryGetValue(info, out var existingMeta)) @@ -797,24 +797,6 @@ namespace Ryujinx.Graphics.Shader.Translation return default; } - private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp) - { - for (int i = 0; i < array.Length; i++) - { - var descriptor = array[i]; - - if (descriptor.Type == texOp.Type && - descriptor.CbufSlot == texOp.CbufSlot && - descriptor.HandleIndex == texOp.Handle && - descriptor.Format == texOp.Format) - { - return i; - } - } - - return -1; - } - private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false) { for (int i = 0; i < array.Length; i++) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs index 01f7f08ad..39f01b1ce 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation Unused = 0, Constant = 1, Perspective = 2, - ScreenLinear = 3 + ScreenLinear = 3, } readonly struct ImapPixelType @@ -29,9 +29,19 @@ namespace Ryujinx.Graphics.Shader.Translation public PixelImap GetFirstUsedType() { - if (X != PixelImap.Unused) return X; - if (Y != PixelImap.Unused) return Y; - if (Z != PixelImap.Unused) return Z; + if (X != PixelImap.Unused) + { + return X; + } + if (Y != PixelImap.Unused) + { + return Y; + } + if (Z != PixelImap.Unused) + { + return Z; + } + return W; } } @@ -155,4 +165,4 @@ namespace Ryujinx.Graphics.Shader.Translation OmapDepth = type2Omap.Extract(1); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs index 684004379..e9c259942 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Shader.Translation foreach (INode node in block.Operations) { - if (!(node is Operation operation)) + if (node is not Operation operation) { continue; } @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.Translation } writesLayer = true; - layerInputAttr = srcAttributeAsgOp.GetSource(1).Value * 4 + srcAttributeAsgOp.GetSource(3).Value;; + layerInputAttr = srcAttributeAsgOp.GetSource(1).Value * 4 + srcAttributeAsgOp.GetSource(3).Value; } else { diff --git a/src/Ryujinx.Graphics.Shader/Translation/Ssa.cs b/src/Ryujinx.Graphics.Shader/Translation/Ssa.cs index 16b8b9240..89aaa3b44 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Ssa.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Ssa.cs @@ -1,7 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System.Collections.Generic; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Translation @@ -12,9 +11,9 @@ namespace Ryujinx.Graphics.Shader.Translation private class DefMap { - private Dictionary<Register, Operand> _map; + private readonly Dictionary<Register, Operand> _map; - private long[] _phiMasks; + private readonly long[] _phiMasks; public DefMap() { @@ -38,7 +37,7 @@ namespace Ryujinx.Graphics.Shader.Translation int key = GetKeyFromRegister(reg); int index = key / 64; - int bit = key & 63; + int bit = key & 63; long mask = 1L << bit; @@ -57,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.Translation int key = GetKeyFromRegister(reg); int index = key / 64; - int bit = key & 63; + int bit = key & 63; return (_phiMasks[index] & (1L << bit)) != 0; } @@ -65,8 +64,8 @@ namespace Ryujinx.Graphics.Shader.Translation private class LocalDefMap { - private Operand[] _map; - private int[] _uses; + private readonly Operand[] _map; + private readonly int[] _uses; public int UseCount { get; private set; } public LocalDefMap() @@ -111,7 +110,7 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly struct Definition { public BasicBlock Block { get; } - public Operand Local { get; } + public Operand Local { get; } public Definition(BasicBlock block, Operand local) { @@ -123,14 +122,14 @@ namespace Ryujinx.Graphics.Shader.Translation public static void Rename(BasicBlock[] blocks) { DefMap[] globalDefs = new DefMap[blocks.Length]; - LocalDefMap localDefs = new LocalDefMap(); + LocalDefMap localDefs = new(); for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { globalDefs[blkIndex] = new DefMap(); } - Queue<BasicBlock> dfPhiBlocks = new Queue<BasicBlock>(); + Queue<BasicBlock> dfPhiBlocks = new(); // First pass, get all defs and locals uses. for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) @@ -303,7 +302,7 @@ namespace Ryujinx.Graphics.Shader.Translation // then use the definition from that Phi. Operand local = Local(); - PhiNode phi = new PhiNode(local); + PhiNode phi = new(local); AddPhi(block, phi); @@ -373,4 +372,4 @@ namespace Ryujinx.Graphics.Shader.Translation } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs index 6ac235a40..519600937 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.Shader.Translation public enum TargetApi { OpenGL, - Vulkan + Vulkan, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs index 8314b223c..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, - Arb + Arb, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs index 1874dec3b..df1e76a1f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs @@ -7,8 +7,8 @@ namespace Ryujinx.Graphics.Shader.Translation { None = 0, - VertexA = 1 << 0, - Compute = 1 << 1, - DebugMode = 1 << 2 + VertexA = 1 << 0, + Compute = 1 << 1, + DebugMode = 1 << 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 255030a4b..010c80db1 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -45,14 +45,14 @@ namespace Ryujinx.Graphics.Shader.Translation } } - List<Function> funcs = new List<Function>(functions.Length); + List<Function> funcs = new(functions.Length); for (int i = 0; i < functions.Length; i++) { funcs.Add(null); } - HelperFunctionManager hfm = new HelperFunctionManager(funcs, config.Stage); + HelperFunctionManager hfm = new(funcs, config.Stage); for (int i = 0; i < functions.Length; i++) { @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Shader.Translation { var fru = frus[i]; - inArgumentsCount = fru.InArguments.Length; + inArgumentsCount = fru.InArguments.Length; outArgumentsCount = fru.OutArguments.Length; } @@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Shader.Translation { TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)), TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)), - _ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()) + _ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()), }; } @@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.Translation for (int index = 0; index < functions.Length; index++) { - EmitterContext context = new EmitterContext(program, config, index != 0); + EmitterContext context = new(program, config, index != 0); if (initializeOutputs && index == 0) { @@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Shader.Translation context.Add(new CommentNode(dbgComment)); } - InstConditional opConditional = new InstConditional(op.RawOpCode); + InstConditional opConditional = new(op.RawOpCode); bool noPred = op.Props.HasFlag(InstProps.NoPred); if (!noPred && opConditional.Pred == RegisterConsts.PredicateTrueIndex && opConditional.PredInv) @@ -367,4 +367,4 @@ namespace Ryujinx.Graphics.Shader.Translation } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 13c5e0e40..40a79c544 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; - using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.Translation.Translator; @@ -16,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.Translation public class TranslatorContext { private readonly DecodedProgram _program; - private ShaderConfig _config; + private readonly ShaderConfig _config; public ulong Address { get; } @@ -59,7 +58,7 @@ namespace Ryujinx.Graphics.Shader.Translation // temporary variable, as long that attribute is written by shader A. FunctionCode[] output = new FunctionCode[a.Length + b.Length - 1]; - List<Operation> ops = new List<Operation>(a.Length + b.Length); + List<Operation> ops = new(a.Length + b.Length); Operand[] temps = new Operand[AttributeConsts.UserAttributesCount * 4]; @@ -205,9 +204,9 @@ namespace Ryujinx.Graphics.Shader.Translation break; } - ShaderConfig config = new ShaderConfig(ShaderStage.Geometry, outputTopology, maxOutputVertices, GpuAccessor, _config.Options); + ShaderConfig config = new(ShaderStage.Geometry, outputTopology, maxOutputVertices, GpuAccessor, _config.Options); - EmitterContext context = new EmitterContext(default, config, false); + EmitterContext context = new(default, config, false); for (int v = 0; v < maxOutputVertices; v++) { @@ -263,7 +262,7 @@ namespace Ryujinx.Graphics.Shader.Translation { TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)), TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)), - _ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()) + _ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()), }; } } From 6aa8d71588af4615019cd91b8a31f69dbfaa815d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 09:26:39 +0200 Subject: [PATCH 674/737] [Ryujinx.Graphics.Nvdec.Vp9] Address dotnet-format issues (#5371) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Address or silence dotnet format IDE1006 warnings * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Address IDE0251 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Fix empty lines before return Co-authored-by: Ac_K <Acoustik666@gmail.com> * Add trailing commas, remove redundant code and remove static modifier from Surface.HighBd * Fix naming rule violations * Fix naming rule violations * Fix empty line before return * Fix comment style Co-authored-by: Ac_K <Acoustik666@gmail.com> * Remove comment alignment * Address review feedback * Separate comments by 2 spaces and fix other formatting issues * Make HighBd an auto-property * Replace if-chain with if-else-chain * Fix new naming rule violations --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs | 2 +- .../Common/BitUtils.cs | 3 +- .../Common/MemoryAllocator.cs | 4 +- src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs | 14 +- src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs | 100 ++- src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs | 138 ++-- src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs | 34 +- src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs | 20 +- .../Dsp/Convolve.cs | 6 +- .../Dsp/IntraPred.cs | 16 +- src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs | 186 +++-- src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs | 10 +- src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs | 27 +- src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs | 112 ++- src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs | 272 +++---- src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs | 759 +++++++++--------- src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs | 62 +- src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs | 45 +- src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs | 37 +- src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs | 136 ++-- .../Types/BModeInfo.cs | 2 +- .../Types/BlockSize.cs | 2 +- .../Types/FrameType.cs | 2 +- .../Types/LoopFilterThresh.cs | 2 +- .../Types/MacroBlockD.cs | 19 +- .../Types/ModeInfo.cs | 12 +- .../Types/MotionVectorContext.cs | 2 +- src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs | 22 +- .../Types/PartitionType.cs | 2 +- .../Types/PlaneType.cs | 2 +- .../Types/PredictionMode.cs | 22 +- .../Types/ReferenceMode.cs | 2 +- .../Types/ScaleFactors.cs | 246 +++--- .../Types/SegLvlFeatures.cs | 10 +- .../Types/Segmentation.cs | 18 +- .../Types/Surface.cs | 48 +- .../Types/TileInfo.cs | 3 +- .../Types/TxMode.cs | 10 +- .../Types/TxSize.cs | 6 +- .../Types/TxType.cs | 8 +- .../Types/Vp9Common.cs | 24 +- 41 files changed, 1240 insertions(+), 1207 deletions(-) diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs index b695fed5b..39a26f66f 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs @@ -51,6 +51,6 @@ /*!\brief An iterator reached the end of list. * */ - CodecListEnd + CodecListEnd, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs index 641188f8a..86981930e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common { 10 => (ushort)Math.Clamp(val, 0, 1023), 12 => (ushort)Math.Clamp(val, 0, 4095), - _ => (ushort)Math.Clamp(val, 0, 255) + _ => (ushort)Math.Clamp(val, 0, 255), }; } @@ -46,6 +46,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common private static int GetMsb(uint n) { Debug.Assert(n != 0); + return 31 ^ BitOperations.LeadingZeroCount(n); } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs index 473dd904a..c75cfeb0f 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common public bool InUse; } - private PoolItem[] _pool = new PoolItem[PoolEntries]; + private readonly PoolItem[] _pool = new PoolItem[PoolEntries]; public ArrayPtr<T> Allocate<T>(int length) where T : unmanaged { @@ -91,4 +91,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common } } } -} \ 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 aaf1d7b98..0ce44b294 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs @@ -14,13 +14,13 @@ public const int MaxRefFrames = 4; public const int MiSizeLog2 = 3; - public const int MiBlockSizeLog2 = 6 - MiSizeLog2; // 64 = 2^6 + public const int MiBlockSizeLog2 = 6 - MiSizeLog2; // 64 = 2^6 - public const int MiSize = 1 << MiSizeLog2; // pixels per mi-unit - public const int MiBlockSize = 1 << MiBlockSizeLog2; // mi-units per max block + public const int MiSize = 1 << MiSizeLog2; // pixels per mi-unit + public const int MiBlockSize = 1 << MiBlockSizeLog2; // mi-units per max block public const int MiMask = MiBlockSize - 1; - public const int PartitionPloffset = 4; // number of probability models per block size + public const int PartitionPloffset = 4; // number of probability models per block size /* Segment Feature Masks */ public const int MaxMvRefCandidates = 2; @@ -48,9 +48,9 @@ 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; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs index cdd645a38..6940d187f 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Mv = Ryujinx.Graphics.Nvdec.Vp9.Types.Mv; namespace Ryujinx.Graphics.Nvdec.Vp9 { @@ -48,7 +47,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 case TxSize.Tx32x32: Idct.HighbdIdct32x32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - default: Debug.Assert(false, "Invalid transform size"); break; + default: + Debug.Assert(false, "Invalid transform size"); + break; } } } @@ -62,11 +63,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { switch (txSize) { - case TxSize.Tx4x4: Idct.Idct4x4Add(dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx8x8: Idct.Idct8x8Add(dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx16x16: Idct.Idct16x16Add(dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx32x32: Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); break; - default: Debug.Assert(false, "Invalid transform size"); return; + case TxSize.Tx4x4: + Idct.Idct4x4Add(dqcoeff.AsSpan(), dst, stride, eob); + break; + case TxSize.Tx8x8: + Idct.Idct8x8Add(dqcoeff.AsSpan(), dst, stride, eob); + break; + case TxSize.Tx16x16: + Idct.Idct16x16Add(dqcoeff.AsSpan(), dst, stride, eob); + break; + case TxSize.Tx32x32: + Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); + break; + default: + Debug.Assert(false, "Invalid transform size"); + return; } } } @@ -79,15 +90,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (txSize <= TxSize.Tx16x16 && eob <= 10) { - dqcoeff.AsSpan().Slice(0, 4 * (4 << (int)txSize)).Fill(0); + dqcoeff.AsSpan()[..(4 * (4 << (int)txSize))].Clear(); } else if (txSize == TxSize.Tx32x32 && eob <= 34) { - dqcoeff.AsSpan().Slice(0, 256).Fill(0); + dqcoeff.AsSpan()[..256].Clear(); } else { - dqcoeff.AsSpan().Slice(0, 16 << ((int)txSize << 1)).Fill(0); + dqcoeff.AsSpan()[..(16 << ((int)txSize << 1))].Clear(); } } } @@ -127,7 +138,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 case TxSize.Tx32x32: Idct.HighbdIdct32x32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - default: Debug.Assert(false, "Invalid transform size"); break; + default: + Debug.Assert(false, "Invalid transform size"); + break; } } } @@ -141,11 +154,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { switch (txSize) { - case TxSize.Tx4x4: Idct.Iht4x4Add(txType, dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx8x8: Idct.Iht8x8Add(txType, dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx16x16: Idct.Iht16x16Add(txType, dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx32x32: Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); break; - default: Debug.Assert(false, "Invalid transform size"); return; + case TxSize.Tx4x4: + Idct.Iht4x4Add(txType, dqcoeff.AsSpan(), dst, stride, eob); + break; + case TxSize.Tx8x8: + Idct.Iht8x8Add(txType, dqcoeff.AsSpan(), dst, stride, eob); + break; + case TxSize.Tx16x16: + Idct.Iht16x16Add(txType, dqcoeff.AsSpan(), dst, stride, eob); + break; + case TxSize.Tx32x32: + Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); + break; + default: + Debug.Assert(false, "Invalid transform size"); + return; } } } @@ -158,15 +181,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (txType == TxType.DctDct && txSize <= TxSize.Tx16x16 && eob <= 10) { - dqcoeff.AsSpan().Slice(0, 4 * (4 << (int)txSize)).Fill(0); + dqcoeff.AsSpan()[..(4 * (4 << (int)txSize))].Clear(); } else if (txSize == TxSize.Tx32x32 && eob <= 34) { - dqcoeff.AsSpan().Slice(0, 256).Fill(0); + dqcoeff.AsSpan()[..256].Clear(); } else { - dqcoeff.AsSpan().Slice(0, 16 << ((int)txSize << 1)).Fill(0); + dqcoeff.AsSpan()[..(16 << ((int)txSize << 1))].Clear(); } } } @@ -184,7 +207,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 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<byte> dstSpan = pd.Dst.Buf.AsSpan().Slice(dstOffset); + Span<byte> dstSpan = pd.Dst.Buf.AsSpan()[dstOffset..]; if (mi.SbType < BlockSize.Block8x8) { @@ -223,7 +246,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref MacroBlockDPlane pd = ref xd.Plane[plane]; var sc = Luts.Vp9DefaultScanOrders[(int)txSize]; int eob = Detokenize.DecodeBlockTokens(ref twd, plane, sc, col, row, txSize, mi.SegmentId); - Span<byte> dst = pd.Dst.Buf.AsSpan().Slice(4 * row * pd.Dst.Stride + 4 * col); + Span<byte> dst = pd.Dst.Buf.AsSpan()[(4 * row * pd.Dst.Stride + 4 * col)..]; if (eob > 0) { @@ -589,9 +612,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 refr, xs, ys); + return; } } + if (xd.CurBuf.HighBd) { ReconInter.HighbdInterPredictor( @@ -793,6 +818,7 @@ 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; } @@ -893,7 +919,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!less8x8 && eobtotal == 0) { - mi.Skip = 1; // Skip loopfilter + mi.Skip = 1; // Skip loopfilter } } } @@ -928,8 +954,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 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.Slice(0, bw).Fill(Luts.PartitionContextLookup[(int)subsize].Above); - leftCtx.Slice(0, bw).Fill(Luts.PartitionContextLookup[(int)subsize].Left); + aboveCtx[..bw].Fill(Luts.PartitionContextLookup[(int)subsize].Above); + leftCtx[..bw].Fill(Luts.PartitionContextLookup[(int)subsize].Left); } private static PartitionType ReadPartition( @@ -1030,7 +1056,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 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"); break; + default: + Debug.Assert(false, "Invalid partition type"); + break; } } @@ -1134,7 +1162,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int alignedCols = TileInfo.MiColsAlignedToSb(cm.MiCols); int tileCols = 1 << cm.Log2TileCols; int tileRows = 1 << cm.Log2TileRows; - Array4<Array64<TileBuffer>> tileBuffers = new Array4<Array64<TileBuffer>>(); + Array4<Array64<TileBuffer>> tileBuffers = new(); int tileRow, tileCol; int miRow, miCol; @@ -1168,7 +1196,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (tileRow = 0; tileRow < tileRows; ++tileRow) { - TileInfo tile = new TileInfo(); + TileInfo tile = new(); tile.SetRow(ref cm, tileRow); for (miRow = tile.MiRowStart; miRow < tile.MiRowEnd; miRow += Constants.MiBlockSize) { @@ -1234,10 +1262,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } while (!tileData.Xd.Corrupted && ++n <= tileData.BufEnd); tileData.DataEnd = bitReaderEnd; + return !tileData.Xd.Corrupted; } - public static unsafe ArrayPtr<byte> DecodeTilesMt(ref Vp9Common cm, ArrayPtr<byte> data, int maxThreads) + public static ArrayPtr<byte> DecodeTilesMt(ref Vp9Common cm, ArrayPtr<byte> data, int maxThreads) { ArrayPtr<byte> bitReaderEnd = ArrayPtr<byte>.Null; @@ -1250,8 +1279,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Debug.Assert(tileCols <= (1 << 6)); Debug.Assert(tileRows == 1); - cm.AboveContext.AsSpan().Fill(0); - cm.AboveSegContext.AsSpan().Fill(0); + cm.AboveContext.AsSpan().Clear(); + cm.AboveSegContext.AsSpan().Clear(); for (n = 0; n < numWorkers; ++n) { @@ -1262,17 +1291,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 tileData.Counts = new Vp9BackwardUpdates(); } - Array64<TileBuffer> tileBuffers = new Array64<TileBuffer>(); + Array64<TileBuffer> tileBuffers = new(); GetTileBuffers(ref cm, data, tileCols, ref tileBuffers); - tileBuffers.AsSpan().Slice(0, tileCols).Sort(CompareTileBuffers); + tileBuffers.AsSpan()[..tileCols].Sort(CompareTileBuffers); if (numWorkers == tileCols) { TileBuffer largest = tileBuffers[0]; Span<TileBuffer> buffers = tileBuffers.AsSpan(); - buffers.Slice(1).CopyTo(buffers.Slice(0, tileBuffers.Length - 1)); + buffers[1..].CopyTo(buffers[..(tileBuffers.Length - 1)]); tileBuffers[tileCols - 1] = largest; } else @@ -1307,9 +1336,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 bufStart += count; } - Ptr<Vp9Common> cmPtr = new Ptr<Vp9Common>(ref cm); + Ptr<Vp9Common> cmPtr = new(ref cm); - Parallel.For(0, numWorkers, (n) => + Parallel.For(0, numWorkers, n => { ref TileWorkerData tileData = ref cmPtr.Value.TileWorkerData[n + totalTiles]; @@ -1335,6 +1364,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } Debug.Assert(!bitReaderEnd.IsNull || cm.Mb.Corrupted); + return bitReaderEnd; } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs index 3281905c1..8c25c700b 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs @@ -5,8 +5,6 @@ using Ryujinx.Graphics.Video; using System; using System.Diagnostics; using System.Runtime.CompilerServices; -using Mv = Ryujinx.Graphics.Nvdec.Vp9.Types.Mv; -using MvRef = Ryujinx.Graphics.Nvdec.Vp9.Types.MvRef; namespace Ryujinx.Graphics.Nvdec.Vp9 { @@ -61,10 +59,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { 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(); - default: Debug.Assert(false, "Invalid maxTxSize."); return ReadOnlySpan<byte>.Empty; + 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<byte>.Empty; } } @@ -72,10 +76,16 @@ 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(); - default: Debug.Assert(false, "Invalid maxTxSize."); return Span<uint>.Empty; + 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<uint>.Empty; } } @@ -110,10 +120,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { return ReadSelectedTxSize(ref cm, ref xd, maxTxSize, ref r); } - else - { - return (TxSize)Math.Min((int)maxTxSize, (int)Luts.TxModeToBiggestTxSize[(int)txMode]); - } + + return (TxSize)Math.Min((int)maxTxSize, (int)Luts.TxModeToBiggestTxSize[(int)txMode]); } private static int DecGetSegmentId(ref Vp9Common cm, ArrayPtr<byte> segmentIds, int miOffset, int xMis, int yMis) @@ -129,6 +137,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } Debug.Assert(segmentId >= 0 && segmentId < Constants.MaxSegments); + return segmentId; } @@ -173,17 +182,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!seg.Enabled) { - return 0; // Default for disabled segmentation + return 0; // Default for disabled segmentation } 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; } @@ -203,7 +214,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!seg.Enabled) { - return 0; // Default for disabled segmentation + return 0; // Default for disabled segmentation } predictedSegmentId = !cm.LastFrameSegMap.IsNull @@ -213,6 +224,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!seg.UpdateMap) { CopySegmentId(ref cm, cm.LastFrameSegMap, cm.CurrentFrameSegMap, miOffset, xMis, yMis); + return predictedSegmentId; } @@ -227,6 +239,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); } SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); + return segmentId; } @@ -236,17 +249,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { return 1; } - else - { - int ctx = xd.GetSkipContext(); - int skip = r.Read(cm.Fc.Value.SkipProb[ctx]); - if (!xd.Counts.IsNull) - { - ++xd.Counts.Value.Skip[ctx][skip]; - } - return skip; + int ctx = xd.GetSkipContext(); + int skip = r.Read(cm.Fc.Value.SkipProb[ctx]); + if (!xd.Counts.IsNull) + { + ++xd.Counts.Value.Skip[ctx][skip]; } + + return skip; } private static int ReadMvComponent(ref Reader r, ref Vp9EntropyProbs fc, int mvcomp, bool usehp) @@ -265,7 +276,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 else { int i; - int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits + int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits d = 0; for (i = 0; i < n; ++i) @@ -284,6 +295,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Result mag += ((d << 3) | (fr << 1) | hp) + 1; + return sign ? -mag : mag; } @@ -297,7 +309,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { MvJointType jointType = (MvJointType)r.ReadTree(Luts.Vp9MvJointTree, fc.Joints.AsSpan()); bool useHP = allowHP && refr.UseMvHp(); - Mv diff = new Mv(); + Mv diff = new(); if (Mv.MvJointVertical(jointType)) { @@ -326,12 +338,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ++xd.Counts.Value.CompInter[ctx][(int)mode]; } - return mode; // SingleReference or CompoundReference - } - else - { - return cm.ReferenceMode; + return mode; // SingleReference or CompoundReference } + + return cm.ReferenceMode; } // Read the referncence frame @@ -434,7 +444,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 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; - default: mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, Luts.SizeGroupLookup[(int)bsize]); break; + default: + mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, Luts.SizeGroupLookup[(int)bsize]); + break; } mi.UvMode = ReadIntraModeUv(ref cm, ref xd, ref r, (byte)mi.Mode); @@ -503,7 +515,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ZeroMvPair(ref mv); break; } - default: return false; + default: + return false; } return ret; } @@ -514,17 +527,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { return cm.Seg.GetSegData(segmentId, SegLvlFeatures.SegLvlRefFrame) != Constants.IntraFrame; } - else - { - int ctx = xd.GetIntraInterContext(); - bool isInter = r.Read(cm.Fc.Value.IntraInterProb[ctx]) != 0; - if (!xd.Counts.IsNull) - { - ++xd.Counts.Value.IntraInter[ctx][isInter ? 1 : 0]; - } - return isInter; + int ctx = xd.GetIntraInterContext(); + bool isInter = r.Read(cm.Fc.Value.IntraInterProb[ctx]) != 0; + if (!xd.Counts.IsNull) + { + ++xd.Counts.Value.IntraInter[ctx][isInter ? 1 : 0]; } + + return isInter; } private static void DecFindBestRefMvs(bool allowHP, Span<Mv> mvlist, ref Mv bestMv, int refmvCount) @@ -547,6 +558,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { mvRefList[refMvCount] = mv; refMvCount++; + return true; } } @@ -605,7 +617,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // This function searches the neighborhood of a given MB/SB // to try and find candidate reference vectors. - private static unsafe int DecFindMvRefs( + private static int DecFindMvRefs( ref Vp9Common cm, ref MacroBlockD xd, PredictionMode mode, @@ -627,7 +639,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 bool earlyBreak = mode != PredictionMode.NearMv; // Blank the reference vector list - mvRefList.Slice(0, Constants.MaxMvRefCandidates).Fill(new Mv()); + mvRefList[..Constants.MaxMvRefCandidates].Clear(); i = 0; if (isSub8X8 != 0) @@ -805,7 +817,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 switch (block) { - case 0: bestSub8x8 = mvList[refmvCount - 1]; break; + case 0: + bestSub8x8 = mvList[refmvCount - 1]; + break; case 1: case 2: if (bMode == PredictionMode.NearestMv) @@ -848,7 +862,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } break; - default: Debug.Assert(false, "Invalid block index."); break; + default: + Debug.Assert(false, "Invalid block index."); + break; } } @@ -883,7 +899,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { BlockSize bsize = mi.SbType; bool allowHP = cm.AllowHighPrecisionMv; - Array2<Mv> bestRefMvs = new Array2<Mv>(); + Array2<Mv> bestRefMvs = new(); int refr, isCompound; byte interModeCtx; Span<Position> mvRefSearch = Luts.MvRefBlocks[(int)bsize]; @@ -898,6 +914,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (bsize < BlockSize.Block8x8) { xd.ErrorInfo.Value.InternalError(CodecErr.CodecUnsupBitstream, "Invalid usage of segement feature on small blocks"); + return; } } @@ -940,11 +957,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int num4X4H = 1 << xd.BmodeBlocksHl; int idx, idy; PredictionMode bMode = 0; - Array2<Mv> bestSub8x8 = new Array2<Mv>(); - const uint invalidMv = 0x80008000; + Array2<Mv> 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<Mv, uint>(ref bestSub8x8[1]) = invalidMv; + Unsafe.As<Mv, uint>(ref bestSub8x8[1]) = InvalidMv; for (idy = 0; idy < 2; idy += num4X4H) { for (idx = 0; idx < 2; idx += num4X4W) @@ -1026,11 +1043,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return leftMi.Value.GetYMode(b + 1); } - else - { - Debug.Assert(b == 1 || b == 3); - return curMi.Value.Bmi[b - 1].Mode; - } + + Debug.Assert(b == 1 || b == 3); + + return curMi.Value.Bmi[b - 1].Mode; } private static PredictionMode AboveBlockMode(Ptr<ModeInfo> curMi, Ptr<ModeInfo> aboveMi, int b) @@ -1044,11 +1060,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return aboveMi.Value.GetYMode(b + 2); } - else - { - Debug.Assert(b == 2 || b == 3); - return curMi.Value.Bmi[b - 2].Mode; - } + + Debug.Assert(b == 2 || b == 3); + + return curMi.Value.Bmi[b - 2].Mode; } private static ReadOnlySpan<byte> GetYModeProbs( @@ -1060,6 +1075,7 @@ 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(); } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs index acebd8ab9..fea184f15 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs @@ -3,7 +3,6 @@ using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Types; using Ryujinx.Graphics.Video; using System; -using Vp9MvRef = Ryujinx.Graphics.Video.Vp9MvRef; namespace Ryujinx.Graphics.Nvdec.Vp9 { @@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { public bool IsHardwareAccelerated => false; - private readonly MemoryAllocator _allocator = new MemoryAllocator(); + private readonly MemoryAllocator _allocator = new(); public ISurface CreateSurface(int width, int height) => new Surface(width, height); @@ -20,7 +19,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Constants.EightTapSmooth, Constants.EightTap, Constants.EightTapSharp, - Constants.Bilinear + Constants.Bilinear, }; public unsafe bool Decode( @@ -30,24 +29,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ReadOnlySpan<Vp9MvRef> mvsIn, Span<Vp9MvRef> mvsOut) { - Vp9Common cm = new Vp9Common(); + Vp9Common cm = new() + { + FrameType = pictureInfo.IsKeyFrame ? FrameType.KeyFrame : FrameType.InterFrame, + IntraOnly = pictureInfo.IntraOnly, - cm.FrameType = pictureInfo.IsKeyFrame ? FrameType.KeyFrame : FrameType.InterFrame; - cm.IntraOnly = pictureInfo.IntraOnly; + Width = output.Width, + Height = output.Height, + SubsamplingX = 1, + SubsamplingY = 1, - cm.Width = output.Width; - cm.Height = output.Height; - cm.SubsamplingX = 1; - cm.SubsamplingY = 1; + UsePrevFrameMvs = pictureInfo.UsePrevInFindMvRefs, - cm.UsePrevFrameMvs = pictureInfo.UsePrevInFindMvRefs; + RefFrameSignBias = pictureInfo.RefFrameSignBias, - cm.RefFrameSignBias = pictureInfo.RefFrameSignBias; - - cm.BaseQindex = pictureInfo.BaseQIndex; - cm.YDcDeltaQ = pictureInfo.YDcDeltaQ; - cm.UvAcDeltaQ = pictureInfo.UvAcDeltaQ; - cm.UvDcDeltaQ = pictureInfo.UvDcDeltaQ; + BaseQindex = pictureInfo.BaseQIndex, + YDcDeltaQ = pictureInfo.YDcDeltaQ, + UvAcDeltaQ = pictureInfo.UvAcDeltaQ, + UvDcDeltaQ = pictureInfo.UvDcDeltaQ, + }; cm.Mb.Lossless = pictureInfo.Lossless; cm.Mb.Bd = 8; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs index 52b1b3dc4..8e5f124b6 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs @@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static int GetCoefContext(ReadOnlySpan<short> neighbors, ReadOnlySpan<byte> tokenCache, int c) { - const int maxNeighbors = 2; + const int MaxNeighbors = 2; - return (1 + tokenCache[neighbors[maxNeighbors * c + 0]] + tokenCache[neighbors[maxNeighbors * c + 1]]) >> 1; + return (1 + tokenCache[neighbors[MaxNeighbors * c + 0]] + tokenCache[neighbors[MaxNeighbors * c + 1]]) >> 1; } private static int ReadCoeff( @@ -57,13 +57,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int band, c = 0; ref Array6<Array6<Array3<byte>>> coefProbs = ref fc.CoefProbs[(int)txSize][(int)type][refr]; Span<byte> tokenCache = stackalloc byte[32 * 32]; - ReadOnlySpan<byte> bandTranslate = Luts.get_band_translate(txSize); + ReadOnlySpan<byte> bandTranslate = Luts.GetBandTranslate(txSize); int dqShift = (txSize == TxSize.Tx32x32) ? 1 : 0; int v; short dqv = dq[0]; ReadOnlySpan<byte> cat6Prob = (xd.Bd == 12) ? Luts.Vp9Cat6ProbHigh12 - : (xd.Bd == 10) ? Luts.Vp9Cat6ProbHigh12.Slice(2) : Luts.Vp9Cat6Prob; + : (xd.Bd == 10) ? Luts.Vp9Cat6ProbHigh12[2..] : Luts.Vp9Cat6Prob; 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. @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int val = -1; band = bandTranslate[0]; - bandTranslate = bandTranslate.Slice(1); + bandTranslate = bandTranslate[1..]; ref Array3<byte> prob = ref coefProbs[band][ctx]; if (!xd.Counts.IsNull) { @@ -107,11 +107,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 r.Value = value; r.Range = range; r.Count = count; - return c; // Zero tokens at the end (no eob token) + + return c; // Zero tokens at the end (no eob token) } ctx = GetCoefContext(nb, tokenCache, c); band = bandTranslate[0]; - bandTranslate = bandTranslate.Slice(1); + bandTranslate = bandTranslate[1..]; prob = ref coefProbs[band][ctx]; } @@ -196,6 +197,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 r.Value = value; r.Range = range; r.Count = count; + return c; } @@ -236,8 +238,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref MacroBlockDPlane pd = ref xd.Plane[plane]; ref Array2<short> dequant = ref pd.SegDequant[segId]; int eob; - Span<sbyte> a = pd.AboveContext.AsSpan().Slice(x); - Span<sbyte> l = pd.LeftContext.AsSpan().Slice(y); + Span<sbyte> a = pd.AboveContext.AsSpan()[x..]; + Span<sbyte> l = pd.LeftContext.AsSpan()[y..]; int ctx; int ctxShiftA = 0; int ctxShiftL = 0; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs index d49a6bf63..5d4c4b840 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs @@ -117,6 +117,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (Sse41.IsSupported && UseIntrinsics && xStepQ4 == 1 << SubpelBits) { ConvolveHorizSse41(src, srcStride, dst, dstStride, xFilters, x0Q4, w, h); + return; } @@ -261,6 +262,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (Avx2.IsSupported && UseIntrinsics && yStepQ4 == 1 << SubpelBits) { ConvolveVertAvx2(src, srcStride, dst, dstStride, yFilters, y0Q4, w, h); + return; } @@ -776,7 +778,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Debug.Assert(yStepQ4 <= 32); Debug.Assert(xStepQ4 <= 32); - HighbdConvolveHoriz(src - srcStride * (SubpelTaps / 2 - 1), srcStride, temp, 64, filter, x0Q4, xStepQ4, w, intermediateHeight, 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); } @@ -811,7 +813,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int h, int bd) { - HighbdConvolveAvgHoriz(src, srcStride, dst, dstStride, filter, x0Q4, xStepQ4, w, h, bd); + HighbdConvolveAvgHoriz(src, srcStride, dst, dstStride, filter, x0Q4, xStepQ4, w, h, bd); } public static unsafe void HighbdConvolve8Vert( diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs index 62b3a9b14..8db78ab03 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs @@ -227,7 +227,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp 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 + 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) @@ -607,13 +607,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 1, 0) = Dst(dst, stride, 0, 2) = Avg2(b, c); Dst(dst, stride, 2, 0) = Dst(dst, stride, 1, 2) = Avg2(c, d); Dst(dst, stride, 3, 0) = Dst(dst, stride, 2, 2) = Avg2(d, e); - Dst(dst, stride, 3, 2) = Avg2(e, f); // Differs from vp8 + Dst(dst, stride, 3, 2) = Avg2(e, f); // Differs from vp8 Dst(dst, stride, 0, 1) = Avg3(a, b, c); Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 3) = Avg3(b, c, d); Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 3) = Avg3(c, d, e); Dst(dst, stride, 3, 1) = Dst(dst, stride, 2, 3) = Avg3(d, e, f); - Dst(dst, stride, 3, 3) = Avg3(e, f, g); // Differs from vp8 + 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) @@ -655,7 +655,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp 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 + Dst(dst, stride, 3, 3) = h; // differs from vp8 } public static unsafe void D45ePredictor4x4(byte* dst, int stride, byte* above, byte* left) @@ -935,7 +935,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp 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 + 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) @@ -1281,13 +1281,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 1, 0) = Dst(dst, stride, 0, 2) = Avg2(b, c); Dst(dst, stride, 2, 0) = Dst(dst, stride, 1, 2) = Avg2(c, d); Dst(dst, stride, 3, 0) = Dst(dst, stride, 2, 2) = Avg2(d, e); - Dst(dst, stride, 3, 2) = Avg2(e, f); // Differs from vp8 + Dst(dst, stride, 3, 2) = Avg2(e, f); // Differs from vp8 Dst(dst, stride, 0, 1) = Avg3(a, b, c); Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 3) = Avg3(b, c, d); Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 3) = Avg3(c, d, e); Dst(dst, stride, 3, 1) = Dst(dst, stride, 2, 3) = Avg3(d, e, f); - Dst(dst, stride, 3, 3) = Avg3(e, f, g); // Differs from vp8 + 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) @@ -1306,7 +1306,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp 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 + Dst(dst, stride, 3, 3) = h; // Differs from vp8 } public static unsafe void HighbdD117Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs index 3fc3c72a7..68da7c496 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs @@ -35,6 +35,7 @@ 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; } @@ -70,6 +71,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp public static byte ClipPixelAdd(byte dest, long trans) { trans = WrapLow(trans); + return BitUtils.ClipPixel(dest + (int)trans); } @@ -77,6 +79,7 @@ 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); } @@ -84,6 +87,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp private static long DctConstRoundShift(long input) { long rv = BitUtils.RoundPowerOfTwo(input, DctConstBits); + return rv; } @@ -115,8 +119,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp op[1] = WrapLow(b1); op[2] = WrapLow(c1); op[3] = WrapLow(d1); - ip = ip.Slice(4); - op = op.Slice(4); + ip = ip[4..]; + op = op[4..]; } Span<int> ip2 = output; @@ -138,8 +142,8 @@ 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.Slice(1); - dest = dest.Slice(1); + ip2 = ip2[1..]; + dest = dest[1..]; } } @@ -167,8 +171,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.Slice(1); - dest = dest.Slice(1); + ip2 = ip2[1..]; + dest = dest[1..]; } } @@ -182,7 +186,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if ((x0 | x1 | x2 | x3) == 0) { - output.Slice(0, 4).Fill(0); + output[..4].Clear(); + return; } @@ -247,8 +252,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (i = 0; i < 4; ++i) { Idct4(input, outptr); - input = input.Slice(4); - outptr = outptr.Slice(4); + input = input[4..]; + outptr = outptr[4..]; } // Columns @@ -282,7 +287,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[1] = ClipPixelAdd(dest[1], a1); dest[2] = ClipPixelAdd(dest[2], a1); dest[3] = ClipPixelAdd(dest[3], a1); - dest = dest.Slice(stride); + dest = dest[stride..]; } } @@ -300,7 +305,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7) == 0) { - output.Slice(0, 8).Fill(0); + output[..8].Clear(); + return; } @@ -434,8 +440,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (i = 0; i < 8; ++i) { Idct8(input, outptr); - input = input.Slice(8); - outptr = outptr.Slice(8); + input = input[8..]; + outptr = outptr[8..]; } // Then transform columns @@ -464,15 +470,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[8]; Span<int> tempOut = stackalloc int[8]; - output.Fill(0); + output.Clear(); // First transform rows // Only first 4 row has non-zero coefs for (i = 0; i < 4; ++i) { Idct8(input, outptr); - input = input.Slice(8); - outptr = outptr.Slice(8); + input = input[8..]; + outptr = outptr[8..]; } // Then transform columns @@ -506,7 +512,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[i] = ClipPixelAdd(dest[i], a1); } - dest = dest.Slice(stride); + dest = dest[stride..]; } } @@ -533,7 +539,8 @@ 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.Slice(0, 16).Fill(0); + output[..16].Clear(); + return; } @@ -860,8 +867,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (i = 0; i < 16; ++i) { Idct16(input, outptr); - input = input.Slice(16); - outptr = outptr.Slice(16); + input = input[16..]; + outptr = outptr[16..]; } // Then transform columns @@ -889,15 +896,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[16]; Span<int> tempOut = stackalloc int[16]; - output.Fill(0); + output.Clear(); // 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) { Idct16(input, outptr); - input = input.Slice(16); - outptr = outptr.Slice(16); + input = input[16..]; + outptr = outptr[16..]; } // Then transform columns @@ -925,15 +932,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[16]; Span<int> tempOut = stackalloc int[16]; - output.Fill(0); + output.Clear(); // 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) { Idct16(input, outptr); - input = input.Slice(16); - outptr = outptr.Slice(16); + input = input[16..]; + outptr = outptr[16..]; } // Then transform columns @@ -967,7 +974,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[i] = ClipPixelAdd(dest[i], a1); } - dest = dest.Slice(stride); + dest = dest[stride..]; } } @@ -1365,11 +1372,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } else { - outptr.Slice(0, 32).Fill(0); + outptr[..32].Clear(); } - input = input.Slice(32); - outptr = outptr.Slice(32); + input = input[32..]; + outptr = outptr[32..]; } // Columns @@ -1397,15 +1404,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[32]; Span<int> tempOut = stackalloc int[32]; - output.Fill(0); + output.Clear(); // Rows // Only upper-left 16x16 has non-zero coeff for (i = 0; i < 16; ++i) { Idct32(input, outptr); - input = input.Slice(32); - outptr = outptr.Slice(32); + input = input[32..]; + outptr = outptr[32..]; } // Columns @@ -1433,15 +1440,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[32]; Span<int> tempOut = stackalloc int[32]; - output.Fill(0); + output.Clear(); // Rows // Only upper-left 8x8 has non-zero coeff for (i = 0; i < 8; ++i) { Idct32(input, outptr); - input = input.Slice(32); - outptr = outptr.Slice(32); + input = input[32..]; + outptr = outptr[32..]; } // Columns @@ -1476,7 +1483,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[i] = ClipPixelAdd(dest[i], a1); } - dest = dest.Slice(stride); + dest = dest[stride..]; } } @@ -1508,8 +1515,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp op[1] = HighbdWrapLow(b1, bd); op[2] = HighbdWrapLow(c1, bd); op[3] = HighbdWrapLow(d1, bd); - ip = ip.Slice(4); - op = op.Slice(4); + ip = ip[4..]; + op = op[4..]; } ReadOnlySpan<int> ip2 = output; @@ -1531,8 +1538,8 @@ 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.Slice(1); - dest = dest.Slice(1); + ip2 = ip2[1..]; + dest = dest[1..]; } } @@ -1560,8 +1567,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.Slice(1); - dest = dest.Slice(1); + ip2 = ip2[1..]; + dest = dest[1..]; } } @@ -1576,13 +1583,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 4) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output.Slice(0, 4).Fill(0); + output[..4].Clear(); + return; } if ((x0 | x1 | x2 | x3) == 0) { - output.Slice(0, 4).Fill(0); + output[..4].Clear(); + return; } @@ -1619,7 +1628,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 4) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output.Slice(0, 4).Fill(0); + output[..4].Clear(); + return; } @@ -1653,8 +1663,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (i = 0; i < 4; ++i) { HighbdIdct4(input, outptr, bd); - input = input.Slice(4); - outptr = outptr.Slice(4); + input = input[4..]; + outptr = outptr[4..]; } // Columns @@ -1688,7 +1698,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[1] = HighbdClipPixelAdd(dest[1], a1, bd); dest[2] = HighbdClipPixelAdd(dest[2], a1, bd); dest[3] = HighbdClipPixelAdd(dest[3], a1, bd); - dest = dest.Slice(stride); + dest = dest[stride..]; } } @@ -1707,13 +1717,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 8) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output.Slice(0, 8).Fill(0); + output[..8].Clear(); + return; } if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7) == 0) { - output.Slice(0, 8).Fill(0); + output[..8].Clear(); + return; } @@ -1786,7 +1798,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 8) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output.Slice(0, 8).Fill(0); + output[..8].Clear(); + return; } @@ -1845,8 +1858,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (i = 0; i < 8; ++i) { HighbdIdct8(input, outptr, bd); - input = input.Slice(8); - outptr = outptr.Slice(8); + input = input[8..]; + outptr = outptr[8..]; } // Then transform columns @@ -1874,15 +1887,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[8]; Span<int> tempOut = stackalloc int[8]; - output.Fill(0); + output.Clear(); // First transform rows // Only first 4 row has non-zero coefs for (i = 0; i < 4; ++i) { HighbdIdct8(input, outptr, bd); - input = input.Slice(8); - outptr = outptr.Slice(8); + input = input[8..]; + outptr = outptr[8..]; } // Then transform columns @@ -1901,7 +1914,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } - public static void vpx_Highbdidct8x8_1_add_c(ReadOnlySpan<int> input, Span<ushort> dest, int stride, int bd) + public static void Vpx_Highbdidct8x8_1_add_c(ReadOnlySpan<int> input, Span<ushort> dest, int stride, int bd) { int i, j; long a1; @@ -1916,7 +1929,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[i] = HighbdClipPixelAdd(dest[i], a1, bd); } - dest = dest.Slice(stride); + dest = dest[stride..]; } } @@ -1940,16 +1953,19 @@ 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.Slice(0, 16).Fill(0); + output[..16].Clear(); + return; } if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15) == 0) { - output.Slice(0, 16).Fill(0); + output[..16].Clear(); + return; } @@ -2105,7 +2121,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 16) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output.Slice(0, 16).Fill(0); + output[..16].Clear(); + return; } @@ -2283,8 +2300,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (i = 0; i < 16; ++i) { HighbdIdct16(input, outptr, bd); - input = input.Slice(16); - outptr = outptr.Slice(16); + input = input[16..]; + outptr = outptr[16..]; } // Then transform columns @@ -2312,15 +2329,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[16]; Span<int> tempOut = stackalloc int[16]; - output.Fill(0); + output.Clear(); // 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) { HighbdIdct16(input, outptr, bd); - input = input.Slice(16); - outptr = outptr.Slice(16); + input = input[16..]; + outptr = outptr[16..]; } // Then transform columns @@ -2336,7 +2353,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (j = 0; j < 16; ++j) { destT[i] = HighbdClipPixelAdd(destT[i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); - destT = destT.Slice(stride); + destT = destT[stride..]; } } } @@ -2350,15 +2367,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[16]; Span<int> tempOut = stackalloc int[16]; - output.Fill(0); + output.Clear(); // 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) { HighbdIdct16(input, outptr, bd); - input = input.Slice(16); - outptr = outptr.Slice(16); + input = input[16..]; + outptr = outptr[16..]; } // Then transform columns @@ -2392,7 +2409,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[i] = HighbdClipPixelAdd(dest[i], a1, bd); } - dest = dest.Slice(stride); + dest = dest[stride..]; } } @@ -2406,7 +2423,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 32) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output.Slice(0, 32).Fill(0); + output[..32].Clear(); + return; } @@ -2797,11 +2815,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } else { - outptr.Slice(0, 32).Fill(0); + outptr[..32].Clear(); } - input = input.Slice(32); - outptr = outptr.Slice(32); + input = input[32..]; + outptr = outptr[32..]; } // Columns @@ -2829,15 +2847,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[32]; Span<int> tempOut = stackalloc int[32]; - output.Fill(0); + output.Clear(); // Rows // Only upper-left 16x16 has non-zero coeff for (i = 0; i < 16; ++i) { HighbdIdct32(input, outptr, bd); - input = input.Slice(32); - outptr = outptr.Slice(32); + input = input[32..]; + outptr = outptr[32..]; } // Columns @@ -2853,7 +2871,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (j = 0; j < 32; ++j) { destT[i] = HighbdClipPixelAdd(destT[i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); - destT = destT.Slice(stride); + destT = destT[stride..]; } } } @@ -2867,15 +2885,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span<int> tempIn = stackalloc int[32]; Span<int> tempOut = stackalloc int[32]; - output.Fill(0); + output.Clear(); // Rows // Only upper-left 8x8 has non-zero coeff for (i = 0; i < 8; ++i) { HighbdIdct32(input, outptr, bd); - input = input.Slice(32); - outptr = outptr.Slice(32); + input = input[32..]; + outptr = outptr[32..]; } // Columns @@ -2910,7 +2928,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[i] = HighbdClipPixelAdd(dest[i], a1, bd); } - dest = dest.Slice(stride); + dest = dest[stride..]; } } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs index 0d5e8b6e3..83b427907 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs @@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp 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; } } @@ -26,10 +27,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } // MODE_MV_MAX_UPDATE_FACTOR (128) * count / MODE_MV_COUNT_SAT; - private static readonly uint[] CountToUpdateFactor = new uint[] - { + 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 + 70, 76, 83, 89, 96, 102, 108, 115, 121, 128, }; private const int ModeMvCountSat = 20; @@ -44,8 +44,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp else { uint count = Math.Min(den, ModeMvCountSat); - uint factor = CountToUpdateFactor[(int)count]; + uint factor = _countToUpdateFactor[(int)count]; byte prob = GetProb(ct0, den); + return WeightedProb(preProb, prob, (int)factor); } } @@ -62,6 +63,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int r = tree[i + 1]; 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; } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs index 050951216..090426e7c 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs @@ -6,8 +6,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { internal struct Reader { - private static readonly byte[] Norm = new byte[] - { + 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, @@ -17,7 +16,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp 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; @@ -44,7 +43,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Count = -8; Range = 255; Fill(); - return ReadBit() != 0; // Marker bit + + return ReadBit() != 0; // Marker bit } } @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp ulong bigEndianValues = BinaryPrimitives.ReadUInt64BigEndian(buffer); nv = bigEndianValues >> (BdValueSize - bits); count += bits; - buffer = buffer.Slice(bits >> 3); + buffer = buffer[(bits >> 3)..]; value = Value | (nv << (shift & 0x7)); } else @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { count += 8; value |= (ulong)buffer[0] << shift; - buffer = buffer.Slice(1); + buffer = buffer[1..]; shift -= 8; } } @@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Count = count; } - public bool HasError() + public readonly bool HasError() { // Check if we have reached the end of the buffer. // @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } { - int shift = Norm[range]; + int shift = _norm[range]; range <<= shift; value <<= shift; count -= shift; @@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp public int ReadBit() { - return Read(128); // vpx_prob_half + return Read(128); // vpx_prob_half } public int ReadLiteral(int bits) @@ -181,7 +181,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp while ((i = tree[i + Read(probs[i >> 1])]) > 0) { - continue; } return -i; @@ -203,10 +202,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (value >= bigsplit) { - range = range - split; - value = value - bigsplit; + range -= split; + value -= bigsplit; { - int shift = Norm[range]; + int shift = _norm[range]; range <<= shift; value <<= shift; count -= shift; @@ -215,7 +214,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } range = split; { - int shift = Norm[range]; + int shift = _norm[range]; range <<= shift; value <<= shift; count -= shift; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs index 9fa5842a6..1ee7f68bc 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private struct Transform2D { - public Transform1D Cols, Rows; // Vertical and horizontal + public Transform1D Cols, Rows; // Vertical and horizontal public Transform2D(Transform1D cols, Transform1D rows) { @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private struct HighbdTransform2D { - public HighbdTransform1D Cols, Rows; // Vertical and horizontal + public HighbdTransform1D Cols, Rows; // Vertical and horizontal public HighbdTransform2D(HighbdTransform1D cols, HighbdTransform1D rows) { @@ -32,12 +32,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static readonly Transform2D[] Iht4 = new Transform2D[] - { - new Transform2D(Idct4, Idct4), // DCT_DCT = 0 - new Transform2D(Iadst4, Idct4), // ADST_DCT = 1 - new Transform2D(Idct4, Iadst4), // DCT_ADST = 2 - new Transform2D(Iadst4, Iadst4) // ADST_ADST = 3 + 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 }; public static void Iht4x416Add(ReadOnlySpan<int> input, Span<byte> dest, int stride, int txType) @@ -51,9 +50,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Inverse transform row vectors for (i = 0; i < 4; ++i) { - Iht4[txType].Rows(input, outptr); - input = input.Slice(4); - outptr = outptr.Slice(4); + _iht4[txType].Rows(input, outptr); + input = input[4..]; + outptr = outptr[4..]; } // Inverse transform column vectors @@ -64,7 +63,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 tempIn[j] = output[j * 4 + i]; } - Iht4[txType].Cols(tempIn, tempOut); + _iht4[txType].Cols(tempIn, tempOut); for (j = 0; j < 4; ++j) { dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4)); @@ -72,12 +71,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static readonly Transform2D[] Iht8 = new Transform2D[] - { - new Transform2D(Idct8, Idct8), // DCT_DCT = 0 - new Transform2D(Iadst8, Idct8), // ADST_DCT = 1 - new Transform2D(Idct8, Iadst8), // DCT_ADST = 2 - new Transform2D(Iadst8, Iadst8) // ADST_ADST = 3 + 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 }; public static void Iht8x864Add(ReadOnlySpan<int> input, Span<byte> dest, int stride, int txType) @@ -87,14 +85,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Span<int> outptr = output; Span<int> tempIn = stackalloc int[8]; Span<int> tempOut = stackalloc int[8]; - Transform2D ht = Iht8[txType]; + Transform2D ht = _iht8[txType]; // Inverse transform row vectors for (i = 0; i < 8; ++i) { ht.Rows(input, outptr); - input = input.Slice(8); - outptr = outptr.Slice(8); + input = input[8..]; + outptr = outptr[8..]; } // Inverse transform column vectors @@ -113,12 +111,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static readonly Transform2D[] Iht16 = new Transform2D[] - { - new Transform2D(Idct16, Idct16), // DCT_DCT = 0 - new Transform2D(Iadst16, Idct16), // ADST_DCT = 1 - new Transform2D(Idct16, Iadst16), // DCT_ADST = 2 - new Transform2D(Iadst16, Iadst16) // ADST_ADST = 3 + 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 }; public static void Iht16x16256Add(ReadOnlySpan<int> input, Span<byte> dest, int stride, int txType) @@ -128,14 +125,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Span<int> outptr = output; Span<int> tempIn = stackalloc int[16]; Span<int> tempOut = stackalloc int[16]; - Transform2D ht = Iht16[txType]; + Transform2D ht = _iht16[txType]; // Rows for (i = 0; i < 16; ++i) { ht.Rows(input, outptr); - input = input.Slice(16); - outptr = outptr.Slice(16); + input = input[16..]; + outptr = outptr[16..]; } // Columns @@ -283,12 +280,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static readonly HighbdTransform2D[] HighbdIht4 = new HighbdTransform2D[] - { - new HighbdTransform2D(HighbdIdct4, HighbdIdct4), // DCT_DCT = 0 - new HighbdTransform2D(HighbdIadst4, HighbdIdct4), // ADST_DCT = 1 - new HighbdTransform2D(HighbdIdct4, HighbdIadst4), // DCT_ADST = 2 - new HighbdTransform2D(HighbdIadst4, HighbdIadst4) // ADST_ADST = 3 + 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 }; public static void HighbdIht4x416Add(ReadOnlySpan<int> input, Span<ushort> dest, int stride, int txType, int bd) @@ -302,9 +298,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Inverse transform row vectors. for (i = 0; i < 4; ++i) { - HighbdIht4[txType].Rows(input, outptr, bd); - input = input.Slice(4); - outptr = outptr.Slice(4); + _highbdIht4[txType].Rows(input, outptr, bd); + input = input[4..]; + outptr = outptr[4..]; } // Inverse transform column vectors. @@ -315,7 +311,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 tempIn[j] = output[j * 4 + i]; } - HighbdIht4[txType].Cols(tempIn, tempOut, bd); + _highbdIht4[txType].Cols(tempIn, tempOut, bd); for (j = 0; j < 4; ++j) { dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4), bd); @@ -323,12 +319,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static readonly HighbdTransform2D[] HighIht8 = new HighbdTransform2D[] - { - new HighbdTransform2D(HighbdIdct8, HighbdIdct8), // DCT_DCT = 0 - new HighbdTransform2D(HighbdIadst8, HighbdIdct8), // ADST_DCT = 1 - new HighbdTransform2D(HighbdIdct8, HighbdIadst8), // DCT_ADST = 2 - new HighbdTransform2D(HighbdIadst8, HighbdIadst8) // ADST_ADST = 3 + 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 }; public static void HighbdIht8x864Add(ReadOnlySpan<int> input, Span<ushort> dest, int stride, int txType, int bd) @@ -338,14 +333,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Span<int> outptr = output; Span<int> tempIn = stackalloc int[8]; Span<int> tempOut = stackalloc int[8]; - HighbdTransform2D ht = HighIht8[txType]; + HighbdTransform2D ht = _highIht8[txType]; // Inverse transform row vectors. for (i = 0; i < 8; ++i) { ht.Rows(input, outptr, bd); - input = input.Slice(8); - outptr = output.Slice(8); + input = input[8..]; + outptr = output[8..]; } // Inverse transform column vectors. @@ -364,12 +359,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static readonly HighbdTransform2D[] HighIht16 = new HighbdTransform2D[] - { - new HighbdTransform2D(HighbdIdct16, HighbdIdct16), // DCT_DCT = 0 - new HighbdTransform2D(HighbdIadst16, HighbdIdct16), // ADST_DCT = 1 - new HighbdTransform2D(HighbdIdct16, HighbdIadst16), // DCT_ADST = 2 - new HighbdTransform2D(HighbdIadst16, HighbdIadst16) // ADST_ADST = 3 + 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 }; public static void HighbdIht16x16256Add(ReadOnlySpan<int> input, Span<ushort> dest, int stride, int txType, int bd) @@ -379,14 +373,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Span<int> outptr = output; Span<int> tempIn = stackalloc int[16]; Span<int> tempOut = stackalloc int[16]; - HighbdTransform2D ht = HighIht16[txType]; + HighbdTransform2D ht = _highIht16[txType]; // Rows for (i = 0; i < 16; ++i) { ht.Rows(input, outptr, bd); - input = input.Slice(16); - outptr = output.Slice(16); + input = input[16..]; + outptr = output[16..]; } // Columns @@ -440,7 +434,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // DC only DCT coefficient if (eob == 1) { - vpx_Highbdidct8x8_1_add_c(input, dest, stride, bd); + Vpx_Highbdidct8x8_1_add_c(input, dest, stride, bd); } else if (eob <= 12) { diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs index 9ecccc64e..1c9f83b49 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs @@ -30,12 +30,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 10101010 // // A loopfilter should be applied to every other 8x8 horizontally. - private static readonly ulong[] Left64X64TxformMask = new ulong[] - { - 0xffffffffffffffffUL, // TX_4X4 - 0xffffffffffffffffUL, // TX_8x8 - 0x5555555555555555UL, // TX_16x16 - 0x1111111111111111UL, // TX_32x32 + private static readonly ulong[] _left64X64TxformMask = { + 0xffffffffffffffffUL, // TX_4X4 + 0xffffffffffffffffUL, // TX_8x8 + 0x5555555555555555UL, // TX_16x16 + 0x1111111111111111UL, // TX_32x32 }; // 64 bit masks for above transform size. Each 1 represents a position where @@ -55,12 +54,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 00000000 // // A loopfilter should be applied to every other 4 the row vertically. - private static readonly ulong[] Above64X64TxformMask = new ulong[] - { - 0xffffffffffffffffUL, // TX_4X4 - 0xffffffffffffffffUL, // TX_8x8 - 0x00ff00ff00ff00ffUL, // TX_16x16 - 0x000000ff000000ffUL, // TX_32x32 + private static readonly ulong[] _above64X64TxformMask = { + 0xffffffffffffffffUL, // TX_4X4 + 0xffffffffffffffffUL, // TX_8x8 + 0x00ff00ff00ff00ffUL, // TX_16x16 + 0x000000ff000000ffUL, // TX_32x32 }; // 64 bit masks for prediction sizes (left). Each 1 represents a position @@ -78,148 +76,143 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 00000000 // 00000000 // 00000000 - private static readonly ulong[] LeftPredictionMask = new ulong[] - { - 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 = new ulong[] - { - 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 = new ulong[] - { - 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 = new ushort[] - { - 0xffff, // TX_4X4 - 0xffff, // TX_8x8 - 0x5555, // TX_16x16 - 0x1111, // TX_32x32 + private static readonly ushort[] _left64X64TxformMaskUv = { + 0xffff, // TX_4X4 + 0xffff, // TX_8x8 + 0x5555, // TX_16x16 + 0x1111, // TX_32x32 }; - private static readonly ushort[] Above64X64TxformMaskUv = new ushort[] - { - 0xffff, // TX_4X4 - 0xffff, // TX_8x8 - 0x0f0f, // TX_16x16 - 0x000f, // TX_32x32 + private static readonly ushort[] _above64X64TxformMaskUv = { + 0xffff, // TX_4X4 + 0xffff, // TX_8x8 + 0x0f0f, // TX_16x16 + 0x000f, // TX_32x32 }; // 16 bit left mask to shift and set for each uv prediction size. - private static readonly ushort[] LeftPredictionMaskUv = new ushort[] - { - 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 = new ushort[] - { - 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 = new ushort[] - { - 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 = new int[] - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // INTRA_MODES - 1, 1, 0, 1 // INTER_MODES (ZEROMV == 0) + 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) }; private static byte GetFilterLevel(ref LoopFilterInfoN lfiN, ref ModeInfo mi) { - return lfiN.Lvl[mi.SegmentId][mi.RefFrame[0]][ModeLfLut[(int)mi.Mode]]; + 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) @@ -229,12 +222,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 8x8 blocks in a superblock. A "1" represents the first block in a 16x16 // or greater area. - private static readonly byte[][] FirstBlockIn16x16 = new byte[][] - { + 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 }, - new byte[] { 1, 0, 1, 0, 1, 0, 1, 0 }, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 } }; // This function sets up the bit masks for a block represented @@ -257,21 +249,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int colInSb = (miCol & 7); int shiftY = colInSb + (rowInSb << 3); int shiftUv = (colInSb >> 1) + ((rowInSb >> 1) << 2); - int buildUv = FirstBlockIn16x16[rowInSb][colInSb]; + int buildUv = _firstBlockIn16X16[rowInSb][colInSb]; if (filterLevel == 0) { return; } - else + + int index = shiftY; + int i; + for (i = 0; i < bh; i++) { - int index = shiftY; - int i; - for (i = 0; i < bh; i++) - { - MemoryMarshal.CreateSpan(ref lfm.LflY[index], 64 - index).Slice(0, bw).Fill((byte)filterLevel); - index += 8; - } + MemoryMarshal.CreateSpan(ref lfm.LflY[index], 64 - index)[..bw].Fill((byte)filterLevel); + index += 8; } // These set 1 in the current block size for the block size edges. @@ -286,13 +276,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // // U and V set things on a 16 bit scale. // - aboveY |= AbovePredictionMask[(int)blockSize] << shiftY; - leftY |= LeftPredictionMask[(int)blockSize] << shiftY; + aboveY |= _abovePredictionMask[(int)blockSize] << shiftY; + leftY |= _leftPredictionMask[(int)blockSize] << shiftY; if (buildUv != 0) { - aboveUv |= (ushort)(AbovePredictionMaskUv[(int)blockSize] << shiftUv); - leftUv |= (ushort)(LeftPredictionMaskUv[(int)blockSize] << shiftUv); + aboveUv |= (ushort)(_abovePredictionMaskUv[(int)blockSize] << shiftUv); + leftUv |= (ushort)(_leftPredictionMaskUv[(int)blockSize] << shiftUv); } // If the block has no coefficients and is not intra we skip applying @@ -305,13 +295,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Add a mask for the transform size. The transform size mask is set to // be correct for a 64x64 prediction block size. Mask to match the size of // the block we are working on and then shift it into place. - aboveY |= (SizeMask[(int)blockSize] & Above64X64TxformMask[(int)txSizeY]) << shiftY; - leftY |= (SizeMask[(int)blockSize] & Left64X64TxformMask[(int)txSizeY]) << shiftY; + aboveY |= (_sizeMask[(int)blockSize] & _above64X64TxformMask[(int)txSizeY]) << shiftY; + leftY |= (_sizeMask[(int)blockSize] & _left64X64TxformMask[(int)txSizeY]) << shiftY; if (buildUv != 0) { - aboveUv |= (ushort)((SizeMaskUv[(int)blockSize] & Above64X64TxformMaskUv[(int)txSizeUv]) << shiftUv); - leftUv |= (ushort)((SizeMaskUv[(int)blockSize] & Left64X64TxformMaskUv[(int)txSizeUv]) << shiftUv); + aboveUv |= (ushort)((_sizeMaskUv[(int)blockSize] & _above64X64TxformMaskUv[(int)txSizeUv]) << shiftUv); + leftUv |= (ushort)((_sizeMaskUv[(int)blockSize] & _left64X64TxformMaskUv[(int)txSizeUv]) << shiftUv); } // Try to determine what to do with the internal 4x4 block boundaries. These @@ -319,12 +309,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // internal ones can be skipped and don't depend on the prediction block size. if (txSizeY == TxSize.Tx4x4) { - int4X4Y |= SizeMask[(int)blockSize] << shiftY; + int4X4Y |= _sizeMask[(int)blockSize] << shiftY; } if (buildUv != 0 && txSizeUv == TxSize.Tx4x4) { - int4X4Uv |= (ushort)((SizeMaskUv[(int)blockSize] & 0xffff) << shiftUv); + int4X4Uv |= (ushort)((_sizeMaskUv[(int)blockSize] & 0xffff) << shiftUv); } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs index 140181ef8..7320c0943 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs @@ -8,175 +8,170 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { public static ReadOnlySpan<byte> SizeGroupLookup => new byte[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 }; - public static readonly BlockSize[][] SubsizeLookup = new BlockSize[][] - { - new BlockSize[] + 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 + BlockSize.Block64x32, BlockSize.Block64x64, }, - new BlockSize[] + 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 + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block64x32, }, - new BlockSize[] + 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 + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32x64, }, - new BlockSize[] + 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 - } + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32x32, + }, }; - public static readonly TxSize[] MaxTxSizeLookup = new TxSize[] - { + 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 + TxSize.Tx16x16, TxSize.Tx16x16, TxSize.Tx32x32, TxSize.Tx32x32, TxSize.Tx32x32, TxSize.Tx32x32, }; - public static readonly TxSize[] TxModeToBiggestTxSize = new TxSize[] - { - 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 = new BlockSize[][][] - { + 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 BlockSize[][] { new BlockSize[] { BlockSize.Block4x4, BlockSize.BlockInvalid }, new BlockSize[] { BlockSize.BlockInvalid, BlockSize.BlockInvalid } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block4x8, BlockSize.Block4x4 }, new BlockSize[] { BlockSize.BlockInvalid, BlockSize.BlockInvalid } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block8x4, BlockSize.BlockInvalid }, new BlockSize[] { BlockSize.Block4x4, BlockSize.BlockInvalid } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block8x8, BlockSize.Block8x4 }, new BlockSize[] { BlockSize.Block4x8, BlockSize.Block4x4 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block8x16, BlockSize.Block8x8 }, new BlockSize[] { BlockSize.BlockInvalid, BlockSize.Block4x8 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block16x8, BlockSize.BlockInvalid }, new BlockSize[] { BlockSize.Block8x8, BlockSize.Block8x4 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block16x16, BlockSize.Block16x8 }, new BlockSize[] { BlockSize.Block8x16, BlockSize.Block8x8 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block16x32, BlockSize.Block16x16 }, new BlockSize[] { BlockSize.BlockInvalid, BlockSize.Block8x16 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block32x16, BlockSize.BlockInvalid }, new BlockSize[] { BlockSize.Block16x16, BlockSize.Block16x8 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block32x32, BlockSize.Block32x16 }, new BlockSize[] { BlockSize.Block16x32, BlockSize.Block16x16 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block32x64, BlockSize.Block32x32 }, new BlockSize[] { BlockSize.BlockInvalid, BlockSize.Block16x32 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block64x32, BlockSize.BlockInvalid }, new BlockSize[] { BlockSize.Block32x32, BlockSize.Block32x16 } }, - new BlockSize[][] { new BlockSize[] { BlockSize.Block64x64, BlockSize.Block64x32 }, new BlockSize[] { BlockSize.Block32x64, BlockSize.Block32x32 } }, + 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 } }, }; - public static readonly TxSize[][][][] UvTxsizeLookup = new TxSize[][][][] - { + 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 TxSize[][][] + new[] { // BLOCK_4X4 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { 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[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, }, - new TxSize[][][] + new[] { // BLOCK_4X8 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { 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[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, }, - new TxSize[][][] + new[] { // BLOCK_8X4 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { 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[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, }, - new TxSize[][][] + new[] { // BLOCK_8X8 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, + 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 TxSize[][][] + new[] { // BLOCK_8X16 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, + 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 TxSize[][][] + new[] { // BLOCK_16X8 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, + 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 TxSize[][][] + new[] { // BLOCK_16X16 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, + 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 TxSize[][][] + new[] { // BLOCK_16X32 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, + 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 TxSize[][][] + new[] { // BLOCK_32X16 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx8x8 } }, + 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 TxSize[][][] + new[] { // BLOCK_32X32 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx32x32, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, + 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 TxSize[][][] + new[] { // BLOCK_32X64 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx32x32, TxSize.Tx32x32 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, + 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 TxSize[][][] + new[] { // BLOCK_64X32 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx32x32, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx32x32, TxSize.Tx16x16 } }, + 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 TxSize[][][] + new[] { // BLOCK_64X64 - new TxSize[][] { new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new TxSize[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new TxSize[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new TxSize[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new TxSize[][] { new TxSize[] { TxSize.Tx32x32, TxSize.Tx32x32 }, new TxSize[] { TxSize.Tx32x32, TxSize.Tx32x32 } }, + 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 } }, }, }; @@ -195,27 +190,25 @@ 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 PartitionContextPair[] - { - new PartitionContextPair(15, 15), // 4X4 - {0b1111, 0b1111} - new PartitionContextPair(15, 14), // 4X8 - {0b1111, 0b1110} - new PartitionContextPair(14, 15), // 8X4 - {0b1110, 0b1111} - new PartitionContextPair(14, 14), // 8X8 - {0b1110, 0b1110} - new PartitionContextPair(14, 12), // 8X16 - {0b1110, 0b1100} - new PartitionContextPair(12, 14), // 16X8 - {0b1100, 0b1110} - new PartitionContextPair(12, 12), // 16X16 - {0b1100, 0b1100} - new PartitionContextPair(12, 8), // 16X32 - {0b1100, 0b1000} - new PartitionContextPair(8, 12), // 32X16 - {0b1000, 0b1100} - new PartitionContextPair(8, 8), // 32X32 - {0b1000, 0b1000} - new PartitionContextPair(8, 0), // 32X64 - {0b1000, 0b0000} - new PartitionContextPair(0, 8), // 64X32 - {0b0000, 0b1000} - new PartitionContextPair(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<short>[] BilinearFilters = new Array8<short>[] - { + private static readonly Array8<short>[] _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), @@ -223,12 +216,11 @@ 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<short>[] SubPelFilters8 = new Array8<short>[] - { + private static readonly Array8<short>[] _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), @@ -236,12 +228,11 @@ 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<short>[] SubPelFilters8S = new Array8<short>[] - { + private static readonly Array8<short>[] _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), @@ -249,12 +240,11 @@ 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<short>[] SubPelFilters8Lp = new Array8<short>[] - { + private static readonly Array8<short>[] _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), @@ -262,12 +252,12 @@ 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<short> NewArray8Short(short e0, short e1, short e2, short e3, short e4, short e5, short e6, short e7) { - Array8<short> output = new Array8<short>(); + Array8<short> output = new(); output[0] = e0; output[1] = e1; @@ -281,54 +271,46 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return output; } - public static readonly Array8<short>[][] Vp9FilterKernels = new Array8<short>[][] - { - SubPelFilters8, SubPelFilters8Lp, SubPelFilters8S, BilinearFilters + public static readonly Array8<short>[][] Vp9FilterKernels = { + _subPelFilters8, _subPelFilters8Lp, _subPelFilters8S, _bilinearFilters, }; // Scan - private static readonly short[] DefaultScan4X4 = new short[] - { + 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 = new short[] - { + 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 = new short[] - { + 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 = new short[] - { + 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, }; - private static readonly short[] ColScan8X8 = new short[] - { + 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, }; - private static readonly short[] RowScan8X8 = new short[] - { + 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, }; - private static readonly short[] DefaultScan16X16 = new short[] - { + 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, @@ -349,8 +331,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 255, }; - private static readonly short[] ColScan16X16 = new short[] - { + 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, @@ -371,8 +352,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 255, }; - private static readonly short[] RowScan16X16 = new short[] - { + 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, @@ -393,8 +373,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 255, }; - private static readonly short[] DefaultScan32X32 = new short[] - { + 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, @@ -478,26 +457,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Neighborhood 2-tuples for various scans and blocksizes, // in {top, left} order for each position in corresponding scan order. - private static readonly short[] DefaultScan4X4Neighbors = new short[] - { + 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, }; - private static readonly short[] ColScan4X4Neighbors = new short[] - { + 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, }; - private static readonly short[] RowScan4X4Neighbors = new short[] - { + 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, }; - private static readonly short[] ColScan8X8Neighbors = new short[] - { + 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, @@ -507,8 +482,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 53, 53, 31, 31, 46, 46, 39, 39, 54, 54, 47, 47, 55, 55, 0, 0, }; - private static readonly short[] RowScan8X8Neighbors = new short[] - { + 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, @@ -518,8 +492,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 38, 38, 60, 60, 46, 46, 53, 53, 54, 54, 61, 61, 62, 62, 0, 0, }; - private static readonly short[] DefaultScan8X8Neighbors = new short[] - { + 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, @@ -529,8 +502,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 31, 38, 53, 60, 46, 53, 39, 46, 54, 61, 47, 54, 55, 62, 0, 0, }; - private static readonly short[] ColScan16X16Neighbors = new short[] - { + 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, @@ -568,8 +540,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 239, 239, 0, 0, }; - private static readonly short[] RowScan16X16Neighbors = new short[] - { + 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, @@ -607,8 +578,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 254, 254, 0, 0, }; - private static readonly short[] DefaultScan16X16Neighbors = new short[] - { + 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, @@ -646,8 +616,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 239, 254, 0, 0, }; - private static readonly short[] DefaultScan32X32Neighbors = new short[] - { + 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, @@ -797,47 +766,40 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 959, 990, 991, 1022, 0, 0, }; - private static readonly short[] Vp9DefaultIscan4X4 = new short[] - { + 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[] Vp9ColIscan4X4 = new short[] - { + 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[] Vp9RowIscan4X4 = new short[] - { + 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[] Vp9ColIscan8X8 = new short[] - { + private static readonly short[] _vp9ColIscan8X8 = { 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, }; - private static readonly short[] Vp9RowIscan8X8 = new short[] - { + private static readonly short[] _vp9RowIscan8X8 = { 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, }; - private static readonly short[] Vp9DefaultIscan8X8 = new short[] - { + private static readonly short[] _vp9DefaultIscan8X8 = { 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, }; - private static readonly short[] Vp9ColIscan16X16 = new short[] - { + private static readonly short[] _vp9ColIscan16X16 = { 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, @@ -856,8 +818,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 65, 88, 107, 124, 139, 152, 163, 177, 185, 199, 221, 234, 243, 248, 252, 255, }; - private static readonly short[] Vp9RowIscan16X16 = new short[] - { + private static readonly short[] _vp9RowIscan16X16 = { 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, @@ -878,8 +839,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 255, }; - private static readonly short[] Vp9DefaultIscan16X16 = new short[] - { + private static readonly short[] _vp9DefaultIscan16X16 = { 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, @@ -900,8 +860,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 255, }; - private static readonly short[] Vp9DefaultIscan32X32 = new short[] - { + private static readonly short[] _vp9DefaultIscan32X32 = { 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, @@ -997,55 +956,51 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - public static readonly ScanOrder[] Vp9DefaultScanOrders = new ScanOrder[] - { - new ScanOrder(DefaultScan4X4, Vp9DefaultIscan4X4, DefaultScan4X4Neighbors), - new ScanOrder(DefaultScan8X8, Vp9DefaultIscan8X8, DefaultScan8X8Neighbors), - new ScanOrder(DefaultScan16X16, Vp9DefaultIscan16X16, DefaultScan16X16Neighbors), - new ScanOrder(DefaultScan32X32, Vp9DefaultIscan32X32, DefaultScan32X32Neighbors) + 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[][] Vp9ScanOrders = new ScanOrder[][] - { + public static readonly ScanOrder[][] Vp9ScanOrders = { new ScanOrder[] { // TX_4X4 - new ScanOrder(DefaultScan4X4, Vp9DefaultIscan4X4, DefaultScan4X4Neighbors), - new ScanOrder(RowScan4X4, Vp9RowIscan4X4, RowScan4X4Neighbors), - new ScanOrder(ColScan4X4, Vp9ColIscan4X4, ColScan4X4Neighbors), - new ScanOrder(DefaultScan4X4, Vp9DefaultIscan4X4, DefaultScan4X4Neighbors) + new(_defaultScan4X4, _vp9DefaultIscan4X4, _defaultScan4X4Neighbors), + new(_rowScan4X4, _vp9RowIscan4X4, _rowScan4X4Neighbors), + new(_colScan4X4, _vp9ColIscan4X4, _colScan4X4Neighbors), + new(_defaultScan4X4, _vp9DefaultIscan4X4, _defaultScan4X4Neighbors), }, new ScanOrder[] { // TX_8X8 - new ScanOrder(DefaultScan8X8, Vp9DefaultIscan8X8, DefaultScan8X8Neighbors), - new ScanOrder(RowScan8X8, Vp9RowIscan8X8, RowScan8X8Neighbors), - new ScanOrder(ColScan8X8, Vp9ColIscan8X8, ColScan8X8Neighbors), - new ScanOrder(DefaultScan8X8, Vp9DefaultIscan8X8, DefaultScan8X8Neighbors) + new(_defaultScan8X8, _vp9DefaultIscan8X8, _defaultScan8X8Neighbors), + new(_rowScan8X8, _vp9RowIscan8X8, _rowScan8X8Neighbors), + new(_colScan8X8, _vp9ColIscan8X8, _colScan8X8Neighbors), + new(_defaultScan8X8, _vp9DefaultIscan8X8, _defaultScan8X8Neighbors), }, new ScanOrder[] { // TX_16X16 - new ScanOrder(DefaultScan16X16, Vp9DefaultIscan16X16, DefaultScan16X16Neighbors), - new ScanOrder(RowScan16X16, Vp9RowIscan16X16, RowScan16X16Neighbors), - new ScanOrder(ColScan16X16, Vp9ColIscan16X16, ColScan16X16Neighbors), - new ScanOrder(DefaultScan16X16, Vp9DefaultIscan16X16, DefaultScan16X16Neighbors) + new(_defaultScan16X16, _vp9DefaultIscan16X16, _defaultScan16X16Neighbors), + new(_rowScan16X16, _vp9RowIscan16X16, _rowScan16X16Neighbors), + new(_colScan16X16, _vp9ColIscan16X16, _colScan16X16Neighbors), + new(_defaultScan16X16, _vp9DefaultIscan16X16, _defaultScan16X16Neighbors), }, new ScanOrder[] { // TX_32X32 - new ScanOrder(DefaultScan32X32, Vp9DefaultIscan32X32, DefaultScan32X32Neighbors), - new ScanOrder(DefaultScan32X32, Vp9DefaultIscan32X32, DefaultScan32X32Neighbors), - new ScanOrder(DefaultScan32X32, Vp9DefaultIscan32X32, DefaultScan32X32Neighbors), - new ScanOrder(DefaultScan32X32, Vp9DefaultIscan32X32, DefaultScan32X32Neighbors) - } + new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), + new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), + new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), + new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), + }, }; // Entropy MV - public static readonly sbyte[] Vp9MvJointTree = new sbyte[] - { - -(sbyte)MvJointType.MvJointZero, 2, -(sbyte)MvJointType.MvJointHnzvz, 4, -(sbyte)MvJointType.MvJointHzvnz, -(sbyte)MvJointType.MvJointHnzvnz + public static readonly sbyte[] Vp9MvJointTree = { + -(sbyte)MvJointType.MvJointZero, 2, -(sbyte)MvJointType.MvJointHnzvz, 4, -(sbyte)MvJointType.MvJointHzvnz, -(sbyte)MvJointType.MvJointHnzvnz, }; - public static readonly sbyte[] Vp9MvClassTree = new sbyte[] - { + public static readonly sbyte[] Vp9MvClassTree = { -(sbyte)MvClassType.MvClass0, 2, -(sbyte)MvClassType.MvClass1, @@ -1081,11 +1036,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public static ReadOnlySpan<byte> Vp9Cat6ProbHigh12 => new byte[] { - 255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 + 255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129, }; - private static readonly byte[] Vp9CoefbandTrans8X8Plus = new byte[] - { + private static readonly byte[] _vp9CoefbandTrans8X8Plus = { 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, @@ -1134,13 +1088,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, }; - public static ReadOnlySpan<byte> get_band_translate(TxSize txSize) + public static ReadOnlySpan<byte> GetBandTranslate(TxSize txSize) { - return txSize == TxSize.Tx4x4 ? Vp9CoefbandTrans4X4 : Vp9CoefbandTrans8X8Plus; + return txSize == TxSize.Tx4x4 ? Vp9CoefbandTrans4X4 : _vp9CoefbandTrans8X8Plus; } - public static readonly byte[][] Vp9Pareto8Full = new byte[][] - { + 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 }, @@ -1398,41 +1351,36 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 new byte[] { 255, 246, 247, 255, 239, 255, 253, 255 }, }; - /* Array indices are identical to previously-existing INTRAMODECONTEXTNODES. */ - public static readonly sbyte[] Vp9IntraModeTree = new sbyte[] - { - -(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[] 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 }; - public static readonly sbyte[] Vp9InterModeTree = new sbyte[] - { + public static readonly sbyte[] Vp9InterModeTree = { -((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 = new sbyte[] - { - -(sbyte)PartitionType.PartitionNone, 2, -(sbyte)PartitionType.PartitionHorz, 4, -(sbyte)PartitionType.PartitionVert, -(sbyte)PartitionType.PartitionSplit + public static readonly sbyte[] Vp9PartitionTree = { + -(sbyte)PartitionType.PartitionNone, 2, -(sbyte)PartitionType.PartitionHorz, 4, -(sbyte)PartitionType.PartitionVert, -(sbyte)PartitionType.PartitionSplit, }; - public static readonly sbyte[] Vp9SwitchableInterpTree = new sbyte[] - { - -Constants.EightTap, 2, -Constants.EightTapSmooth, -Constants.EightTapSharp + public static readonly sbyte[] Vp9SwitchableInterpTree = { + -Constants.EightTap, 2, -Constants.EightTapSmooth, -Constants.EightTapSharp, }; - public static readonly sbyte[] Vp9SegmentTree = new sbyte[] - { - 2, 4, 6, 8, 10, 12, 0, -1, -2, -3, -4, -5, -6, -7 + public static readonly sbyte[] Vp9SegmentTree = { + 2, 4, 6, 8, 10, 12, 0, -1, -2, -3, -4, -5, -6, -7, }; // MV Ref @@ -1442,169 +1390,192 @@ 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 = new int[] - { - 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 = new MotionVectorContext[] - { - 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 = new Position[][] - { + public static readonly Position[][] MvRefBlocks = { // 4X4 - new Position[] { 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 ) }, + 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 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 ) }, + 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 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 ) }, + 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), + }, // 8X8 - new Position[] { 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 ) }, + 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 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 ) }, + 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 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 ) }, + 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), + }, // 16X16 - new Position[] { 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 ) }, + 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 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 ) }, + 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 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 ) }, + 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), + }, // 32X32 - new Position[] { 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 ) }, + 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 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 ) }, + 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 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 ) }, + 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 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 ) } + 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), + }, }; } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs index a9da10425..9b0c44b76 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs @@ -13,7 +13,7 @@ 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) @@ -30,13 +30,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // One of two edges uses comp pred (2/3) ctx = 2 + (xd.LeftMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.LeftMi.Value.IsInterBlock() ? 1 : 0); } - else // Both edges use comp pred (4) + else // Both edges use comp pred (4) { ctx = 4; } } 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,10 +51,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // No edges available (1) + { // No edges available (1) ctx = 1; } Debug.Assert(ctx >= 0 && ctx < Constants.CompInterContexts); + return ctx; } @@ -70,29 +71,29 @@ 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) + if (!edgeMi.HasSecondRef()) // single pred (1/3) { predContext = 1 + 2 * (edgeMi.RefFrame[0] != cm.CompVarRef[1] ? 1 : 0); } - else // Comp pred (1/3) + else // Comp pred (1/3) { 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]; @@ -103,7 +104,7 @@ 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])) { @@ -119,7 +120,7 @@ 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]) @@ -136,7 +137,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else if (vrfa == vrfl) - { // Comp/Comp + { // Comp/Comp predContext = 4; } else @@ -146,7 +147,7 @@ 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()) @@ -166,10 +167,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // No edges available (2) + { // No edges available (2) predContext = 2; } Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); + return predContext; } @@ -181,16 +183,16 @@ 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()) { @@ -203,7 +205,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // Inter/Inter + { // Inter/Inter bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); sbyte above0 = xd.AboveMi.Value.RefFrame[0]; @@ -238,14 +240,14 @@ 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()) - { // Intra + { // Intra predContext = 2; } else - { // Inter + { // Inter if (!edgeMi.HasSecondRef()) { predContext = 4 * (edgeMi.RefFrame[0] == Constants.LastFrame ? 1 : 0); @@ -258,10 +260,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // No edges available + { // No edges available predContext = 2; } Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); + return predContext; } @@ -274,16 +277,16 @@ 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()) { @@ -303,7 +306,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // Inter/Inter + { // Inter/Inter bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); sbyte above0 = xd.AboveMi.Value.RefFrame[0]; @@ -361,7 +364,7 @@ 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() || (edgeMi.RefFrame[0] == Constants.LastFrame && !edgeMi.HasSecondRef())) @@ -379,10 +382,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // No edges available (2) + { // No edges available (2) predContext = 2; } Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); + return predContext; } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs index 5c52c32f5..ea858222e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs @@ -9,8 +9,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public const int MinQ = 0; public const int MaxQ = 255; - private static readonly short[] DcQlookup = new short[] - { + 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, @@ -32,8 +31,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 1184, 1232, 1282, 1336, }; - private static readonly short[] DcQlookup10 = new short[] - { + 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, @@ -56,8 +54,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 3953, 4089, 4236, 4394, 4559, 4737, 4929, 5130, 5347, }; - private static readonly short[] DcQlookup12 = new short[] - { + 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, @@ -84,8 +81,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 19718, 20521, 21387, }; - private static readonly short[] AcQlookup = new short[] - { + 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, @@ -108,8 +104,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 1567, 1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828, }; - private static readonly short[] AcQlookup10 = new short[] - { + 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, @@ -132,8 +127,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 6268, 6388, 6512, 6640, 6768, 6900, 7036, 7172, 7312, }; - private static readonly short[] AcQlookup12 = new short[] - { + 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, @@ -164,11 +158,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { 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"); + return -1; } } @@ -177,11 +175,15 @@ 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"); + return -1; } } @@ -192,12 +194,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int data = seg.GetSegData(segmentId, SegLvlFeatures.SegLvlAltQ); int segQIndex = seg.AbsDelta == Constants.SegmentAbsData ? data : baseQIndex + data; + return Math.Clamp(segQIndex, 0, MaxQ); } - else - { - return baseQIndex; - } + + return baseQIndex; } } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs index a4c295e5f..a357cd15b 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs @@ -84,16 +84,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static Mv MiMvPredQ4(ref ModeInfo mi, int idx) { - Mv res = new Mv() + 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) + mi.Bmi[2].Mv[idx].Col + mi.Bmi[3].Mv[idx].Col), }; - return res; } private static int RoundMvCompQ2(int value) @@ -103,16 +102,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static Mv MiMvPredQ2(ref ModeInfo mi, int idx, int block0, int block1) { - Mv res = new Mv() + 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) + mi.Bmi[block1].Mv[idx].Col), }; - return res; } public static Mv ClampMvToUmvBorderSb(ref MacroBlockD xd, ref Mv srcMv, int bw, int bh, int ssX, int ssY) @@ -124,10 +122,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int spelRight = spelLeft - SubpelShifts; int spelTop = (Constants.Vp9InterpExtend + bh) << SubpelBits; int spelBottom = spelTop - SubpelShifts; - Mv clampedMv = new Mv() + Mv clampedMv = new() { Row = (short)(srcMv.Row * (1 << (1 - ssY))), - Col = (short)(srcMv.Col * (1 << (1 - ssX))) + Col = (short)(srcMv.Col * (1 << (1 - ssX))), }; Debug.Assert(ssX <= 1); @@ -145,14 +143,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public static Mv AverageSplitMvs(ref MacroBlockDPlane pd, ref ModeInfo mi, int refr, int block) { int ssIdx = ((pd.SubsamplingX > 0 ? 1 : 0) << 1) | (pd.SubsamplingY > 0 ? 1 : 0); - Mv res = new Mv(); + Mv res = new(); switch (ssIdx) { - case 0: res = mi.Bmi[block].Mv[refr]; break; - case 1: res = MiMvPredQ2(ref mi, refr, block, block + 2); break; - case 2: res = MiMvPredQ2(ref mi, refr, block, block + 1); break; - case 3: res = MiMvPredQ4(ref mi, refr); break; - default: Debug.Assert(ssIdx <= 3 && ssIdx >= 0); break; + case 0: + res = mi.Bmi[block].Mv[refr]; + break; + case 1: + res = MiMvPredQ2(ref mi, refr, block, block + 2); + break; + case 2: + res = MiMvPredQ2(ref mi, refr, block, block + 1); + break; + case 3: + res = MiMvPredQ4(ref mi, refr); + break; + default: + Debug.Assert(ssIdx <= 3 && ssIdx >= 0); + break; } return res; } @@ -161,6 +169,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; } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs index e346c01d1..270ff8b7e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs @@ -7,18 +7,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { internal static class ReconIntra { - public static readonly TxType[] IntraModeToTxTypeLookup = new TxType[] - { - TxType.DctDct, // DC - TxType.AdstDct, // V - TxType.DctAdst, // H - TxType.DctDct, // D45 - TxType.AdstAdst, // D135 - TxType.AdstDct, // D117 - TxType.DctAdst, // D153 - TxType.DctAdst, // D207 - TxType.AdstDct, // D63 - TxType.AdstAdst // TM + public static readonly TxType[] IntraModeToTxTypeLookup = { + TxType.DctDct, // DC + TxType.AdstDct, // V + TxType.DctAdst, // H + TxType.DctDct, // D45 + TxType.AdstAdst, // D135 + TxType.AdstDct, // D117 + TxType.DctAdst, // D153 + TxType.DctAdst, // D207 + TxType.AdstDct, // D63 + TxType.AdstAdst, // TM }; private const int NeedLeft = 1 << 1; @@ -27,244 +26,240 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static ReadOnlySpan<byte> ExtendModes => new byte[] { - NeedAbove | NeedLeft, // DC - NeedAbove, // V - NeedLeft, // H - NeedAboveRight, // D45 - NeedLeft | NeedAbove, // D135 - NeedLeft | NeedAbove, // D117 - NeedLeft | NeedAbove, // D153 - NeedLeft, // D207 - NeedAboveRight, // D63 - NeedLeft | NeedAbove, // TM + NeedAbove | NeedLeft, // DC + NeedAbove, // V + NeedLeft, // H + NeedAboveRight, // D45 + NeedLeft | NeedAbove, // D135 + NeedLeft | NeedAbove, // D117 + NeedLeft | NeedAbove, // D153 + NeedLeft, // D207 + NeedAboveRight, // D63 + NeedLeft | NeedAbove, // TM }; private unsafe delegate void IntraPredFn(byte* dst, int stride, byte* above, byte* left); - private static unsafe IntraPredFn[][] _pred = new IntraPredFn[][] - { + private static readonly unsafe IntraPredFn[][] _pred = { new IntraPredFn[] { null, null, null, - null + null, }, new IntraPredFn[] { VPredictor4x4, VPredictor8x8, VPredictor16x16, - VPredictor32x32 + VPredictor32x32, }, new IntraPredFn[] { HPredictor4x4, HPredictor8x8, HPredictor16x16, - HPredictor32x32 + HPredictor32x32, }, new IntraPredFn[] { D45Predictor4x4, D45Predictor8x8, D45Predictor16x16, - D45Predictor32x32 + D45Predictor32x32, }, new IntraPredFn[] { D135Predictor4x4, D135Predictor8x8, D135Predictor16x16, - D135Predictor32x32 + D135Predictor32x32, }, new IntraPredFn[] { D117Predictor4x4, D117Predictor8x8, D117Predictor16x16, - D117Predictor32x32 + D117Predictor32x32, }, new IntraPredFn[] { D153Predictor4x4, D153Predictor8x8, D153Predictor16x16, - D153Predictor32x32 + D153Predictor32x32, }, new IntraPredFn[] { D207Predictor4x4, D207Predictor8x8, D207Predictor16x16, - D207Predictor32x32 + D207Predictor32x32, }, new IntraPredFn[] { D63Predictor4x4, D63Predictor8x8, D63Predictor16x16, - D63Predictor32x32 + D63Predictor32x32, }, new IntraPredFn[] { TMPredictor4x4, TMPredictor8x8, TMPredictor16x16, - TMPredictor32x32 - } + TMPredictor32x32, + }, }; - private static unsafe IntraPredFn[][][] _dcPred = new IntraPredFn[][][] - { - new IntraPredFn[][] + private static readonly unsafe IntraPredFn[][][] _dcPred = { + new[] { new IntraPredFn[] { Dc128Predictor4x4, Dc128Predictor8x8, Dc128Predictor16x16, - Dc128Predictor32x32 + Dc128Predictor32x32, }, new IntraPredFn[] { DcTopPredictor4x4, DcTopPredictor8x8, DcTopPredictor16x16, - DcTopPredictor32x32 - } + DcTopPredictor32x32, + }, }, - new IntraPredFn[][] + new[] { new IntraPredFn[] { DcLeftPredictor4x4, DcLeftPredictor8x8, DcLeftPredictor16x16, - DcLeftPredictor32x32 + DcLeftPredictor32x32, }, new IntraPredFn[] { DcPredictor4x4, DcPredictor8x8, DcPredictor16x16, - DcPredictor32x32 - } - } + DcPredictor32x32, + }, + }, }; private unsafe delegate void IntraHighPredFn(ushort* dst, int stride, ushort* above, ushort* left, int bd); - private static unsafe IntraHighPredFn[][] _predHigh = new IntraHighPredFn[][] - { + private static readonly unsafe IntraHighPredFn[][] _predHigh = { new IntraHighPredFn[] { null, null, null, - null + null, }, new IntraHighPredFn[] { HighbdVPredictor4x4, HighbdVPredictor8x8, HighbdVPredictor16x16, - HighbdVPredictor32x32 + HighbdVPredictor32x32, }, new IntraHighPredFn[] { HighbdHPredictor4x4, HighbdHPredictor8x8, HighbdHPredictor16x16, - HighbdHPredictor32x32 + HighbdHPredictor32x32, }, new IntraHighPredFn[] { HighbdD45Predictor4x4, HighbdD45Predictor8x8, HighbdD45Predictor16x16, - HighbdD45Predictor32x32 + HighbdD45Predictor32x32, }, new IntraHighPredFn[] { HighbdD135Predictor4x4, HighbdD135Predictor8x8, HighbdD135Predictor16x16, - HighbdD135Predictor32x32 + HighbdD135Predictor32x32, }, new IntraHighPredFn[] { HighbdD117Predictor4x4, HighbdD117Predictor8x8, HighbdD117Predictor16x16, - HighbdD117Predictor32x32 + HighbdD117Predictor32x32, }, new IntraHighPredFn[] { HighbdD153Predictor4x4, HighbdD153Predictor8x8, HighbdD153Predictor16x16, - HighbdD153Predictor32x32 + HighbdD153Predictor32x32, }, new IntraHighPredFn[] { HighbdD207Predictor4x4, HighbdD207Predictor8x8, HighbdD207Predictor16x16, - HighbdD207Predictor32x32 + HighbdD207Predictor32x32, }, new IntraHighPredFn[] { HighbdD63Predictor4x4, HighbdD63Predictor8x8, HighbdD63Predictor16x16, - HighbdD63Predictor32x32 + HighbdD63Predictor32x32, }, new IntraHighPredFn[] { HighbdTMPredictor4x4, HighbdTMPredictor8x8, HighbdTMPredictor16x16, - HighbdTMPredictor32x32 - } + HighbdTMPredictor32x32, + }, }; - private static unsafe IntraHighPredFn[][][] _dcPredHigh = new IntraHighPredFn[][][] - { - new IntraHighPredFn[][] + private static readonly unsafe IntraHighPredFn[][][] _dcPredHigh = { + new[] { new IntraHighPredFn[] { HighbdDc128Predictor4x4, HighbdDc128Predictor8x8, HighbdDc128Predictor16x16, - HighbdDc128Predictor32x32 + HighbdDc128Predictor32x32, }, new IntraHighPredFn[] { HighbdDcTopPredictor4x4, HighbdDcTopPredictor8x8, HighbdDcTopPredictor16x16, - HighbdDcTopPredictor32x32 - } + HighbdDcTopPredictor32x32, + }, }, - new IntraHighPredFn[][] + new[] { new IntraHighPredFn[] { HighbdDcLeftPredictor4x4, HighbdDcLeftPredictor8x8, HighbdDcLeftPredictor16x16, - HighbdDcLeftPredictor32x32 + HighbdDcLeftPredictor32x32, }, new IntraHighPredFn[] { HighbdDcPredictor4x4, HighbdDcPredictor8x8, HighbdDcPredictor16x16, - HighbdDcPredictor32x32 - } - } + HighbdDcPredictor32x32, + }, + }, }; private static unsafe void BuildIntraPredictorsHigh( @@ -741,6 +736,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 x, y, plane); + return; } BuildIntraPredictors( diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs index 9e1cd8b41..4c8e33c30 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs @@ -5,6 +5,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types internal struct BModeInfo { public PredictionMode Mode; - public Array2<Mv> Mv; // First, second inter predictor motion vectors + public Array2<Mv> Mv; // First, second inter predictor motion vectors } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs index 22a48e207..34fa9487e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs @@ -16,6 +16,6 @@ Block64x32 = 11, Block64x64 = 12, BlockSizes = 13, - BlockInvalid = BlockSizes + BlockInvalid = BlockSizes, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs index a783999ef..3d2093030 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs @@ -3,6 +3,6 @@ internal enum FrameType { KeyFrame = 0, - InterFrame = 1 + InterFrame = 1, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs index edd79af4a..c0af5495e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // passed it can be loaded into vector registers. internal struct LoopFilterThresh { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Array16<byte> Mblim; public Array16<byte> Lim; public Array16<byte> HevThr; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs index f1111528e..5cdc0d2df 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Ptr<InternalErrorInfo> ErrorInfo; - public int GetPredContextSegId() + public readonly int GetPredContextSegId() { sbyte aboveSip = !AboveMi.IsNull ? AboveMi.Value.SegIdPredicted : (sbyte)0; sbyte leftSip = !LeftMi.IsNull ? LeftMi.Value.SegIdPredicted : (sbyte)0; @@ -62,14 +62,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return aboveSip + leftSip; } - public int GetSkipContext() + public readonly int GetSkipContext() { int aboveSkip = !AboveMi.IsNull ? AboveMi.Value.Skip : 0; int leftSkip = !LeftMi.IsNull ? LeftMi.Value.Skip : 0; + return aboveSkip + leftSkip; } - public int GetPredContextSwitchableInterp() + public readonly int GetPredContextSwitchableInterp() { // Note: // The mode info data structure has a one element border above and to the @@ -103,16 +104,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // 1 - intra/inter, inter/intra // 2 - intra/--, --/intra // 3 - intra/intra - public int GetIntraInterContext() + public readonly 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); } - else if (!AboveMi.IsNull || !LeftMi.IsNull) - { // One edge available + + if (!AboveMi.IsNull || !LeftMi.IsNull) + { // One edge available return 2 * (!(!AboveMi.IsNull ? AboveMi.Value : LeftMi.Value).IsInterBlock() ? 1 : 0); } return 0; @@ -122,7 +125,7 @@ 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 int GetTxSizeContext() + public readonly 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; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs index 8ef281d83..8af7b6f90 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public TxSize TxSize; public sbyte Skip; public sbyte SegmentId; - public sbyte SegIdPredicted; // Valid only when TemporalUpdate is enabled + public sbyte SegIdPredicted; // Valid only when TemporalUpdate is enabled // Only for Intra blocks public PredictionMode UvMode; @@ -32,10 +32,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return SbType < BlockSize.Block8x8 ? Bmi[block].Mode : Mode; } - public TxSize GetUvTxSize(ref MacroBlockDPlane pd) + public readonly TxSize GetUvTxSize(ref MacroBlockDPlane pd) { 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]; } @@ -49,9 +50,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return RefFrame[1] > Constants.IntraFrame; } - private static readonly int[][] IdxNColumnToSubblock = new int[][] - { - new int[] { 1, 2 }, new int[] { 1, 3 }, new int[] { 3, 2 }, new int[] { 3, 3 } + private static readonly int[][] _idxNColumnToSubblock = { + new[] { 1, 2 }, new[] { 1, 3 }, new[] { 3, 2 }, new[] { 3, 3 }, }; // This function returns either the appropriate sub block or block's mv @@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Mv GetSubBlockMv(int whichMv, int searchCol, int blockIdx) { return blockIdx >= 0 && SbType < BlockSize.Block8x8 - ? Bmi[IdxNColumnToSubblock[blockIdx][searchCol == 0 ? 1 : 0]].Mv[whichMv] + ? Bmi[_idxNColumnToSubblock[blockIdx][searchCol == 0 ? 1 : 0]].Mv[whichMv] : Mv[whichMv]; } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs index 319c8dba8..a9a603bf0 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs @@ -9,6 +9,6 @@ BothNew = 4, IntraPlusNonIntra = 5, BothIntra = 6, - InvalidCase = 9 + InvalidCase = 9, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs index 815bbb321..c78a80d0d 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs @@ -51,13 +51,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 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 + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, }; - public bool UseMvHp() + public readonly bool UseMvHp() { - const int kMvRefThresh = 64; // Threshold for use of high-precision 1/8 mv - return Math.Abs(Row) < kMvRefThresh && Math.Abs(Col) < kMvRefThresh; + 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) @@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types else { int i; - int b = c + Constants.Class0Bits - 1; // Number of bits + int b = c + Constants.Class0Bits - 1; // Number of bits for (i = 0; i < b; ++i) { counts.Bits[comp][i][((d >> i) & 1)] += (uint)incr; @@ -121,19 +121,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } } - private MvJointType GetMvJoint() + private readonly MvJointType GetMvJoint() { if (Row == 0) { return Col == 0 ? MvJointType.MvJointZero : MvJointType.MvJointHnzvz; } - else - { - return Col == 0 ? MvJointType.MvJointHzvnz : MvJointType.MvJointHnzvnz; - } + + return Col == 0 ? MvJointType.MvJointHzvnz : MvJointType.MvJointHnzvnz; } - internal void IncMv(Ptr<Vp9BackwardUpdates> counts) + internal readonly void IncMv(Ptr<Vp9BackwardUpdates> counts) { if (!counts.IsNull) { @@ -158,7 +156,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types Row = (short)Math.Clamp(Row, minRow, maxRow); } - private const int MvBorder = (16 << 3); // Allow 16 pels in 1/8th pel units + private const int MvBorder = (16 << 3); // Allow 16 pels in 1/8th pel units public void ClampMvRef(ref MacroBlockD xd) { diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs index 096f9818a..78f4fb260 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs @@ -7,6 +7,6 @@ PartitionVert, PartitionSplit, PartitionTypes, - PartitionInvalid = PartitionTypes + PartitionInvalid = PartitionTypes, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs index 790aa2a0c..d62594567 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs @@ -4,6 +4,6 @@ { Y = 0, Uv = 1, - PlaneTypes + PlaneTypes, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs index bbb9be9ad..fbf542043 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs @@ -2,20 +2,20 @@ { 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 + 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 + MbModeCount = 14, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs index 7cbf9f4ef..5d9f189d2 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs @@ -5,6 +5,6 @@ SingleReference = 0, CompoundReference = 1, ReferenceModeSelect = 2, - ReferenceModes = 3 + ReferenceModes = 3, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs index 970f96801..ba89d6e39 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs @@ -38,263 +38,255 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types int h, int bd); - private static readonly unsafe ConvolveFn[][][] PredictX16Y16 = new ConvolveFn[][][] - { - new ConvolveFn[][] + private static readonly unsafe ConvolveFn[][][] _predictX16Y16 = { + new[] { new ConvolveFn[] { ConvolveCopy, - ConvolveAvg + ConvolveAvg, }, new ConvolveFn[] { Convolve8Vert, - Convolve8AvgVert - } + Convolve8AvgVert, + }, }, - new ConvolveFn[][] + new[] { new ConvolveFn[] { Convolve8Horiz, - Convolve8AvgHoriz + Convolve8AvgHoriz, }, new ConvolveFn[] { Convolve8, - Convolve8Avg - } - } + Convolve8Avg, + }, + }, }; - private static readonly unsafe ConvolveFn[][][] PredictX16 = new ConvolveFn[][][] - { - new ConvolveFn[][] + private static readonly unsafe ConvolveFn[][][] _predictX16 = { + new[] { new ConvolveFn[] { ScaledVert, - ScaledAvgVert + ScaledAvgVert, }, new ConvolveFn[] { ScaledVert, - ScaledAvgVert - } + ScaledAvgVert, + }, }, - new ConvolveFn[][] + new[] { new ConvolveFn[] { Scaled2D, - ScaledAvg2D + ScaledAvg2D, }, new ConvolveFn[] { Scaled2D, - ScaledAvg2D - } - } + ScaledAvg2D, + }, + }, }; - private static readonly unsafe ConvolveFn[][][] PredictY16 = new ConvolveFn[][][] - { - new ConvolveFn[][] + private static readonly unsafe ConvolveFn[][][] _predictY16 = { + new[] { new ConvolveFn[] { ScaledHoriz, - ScaledAvgHoriz + ScaledAvgHoriz, }, new ConvolveFn[] { Scaled2D, - ScaledAvg2D - } + ScaledAvg2D, + }, }, - new ConvolveFn[][] + new[] { new ConvolveFn[] { ScaledHoriz, - ScaledAvgHoriz + ScaledAvgHoriz, }, new ConvolveFn[] { Scaled2D, - ScaledAvg2D - } - } - }; - - private static readonly unsafe ConvolveFn[][][] Predict = new ConvolveFn[][][] - { - new ConvolveFn[][] - { - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D + ScaledAvg2D, }, - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D - } }, - new ConvolveFn[][] + }; + + private static readonly unsafe ConvolveFn[][][] _predict = { + new[] { new ConvolveFn[] { Scaled2D, - ScaledAvg2D + ScaledAvg2D, }, new ConvolveFn[] { Scaled2D, - ScaledAvg2D - } - } + ScaledAvg2D, + }, + }, + new[] + { + new ConvolveFn[] + { + Scaled2D, + ScaledAvg2D, + }, + new ConvolveFn[] + { + Scaled2D, + ScaledAvg2D, + }, + }, }; - private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictX16Y16 = new HighbdConvolveFn[][][] - { - new HighbdConvolveFn[][] + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16Y16 = { + new[] { new HighbdConvolveFn[] { HighbdConvolveCopy, - HighbdConvolveAvg + HighbdConvolveAvg, }, new HighbdConvolveFn[] { HighbdConvolve8Vert, - HighbdConvolve8AvgVert - } + HighbdConvolve8AvgVert, + }, }, - new HighbdConvolveFn[][] + new[] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, - HighbdConvolve8AvgHoriz + HighbdConvolve8AvgHoriz, }, new HighbdConvolveFn[] { HighbdConvolve8, - HighbdConvolve8Avg - } - } + HighbdConvolve8Avg, + }, + }, }; - private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictX16 = new HighbdConvolveFn[][][] - { - new HighbdConvolveFn[][] + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16 = { + new[] { new HighbdConvolveFn[] { HighbdConvolve8Vert, - HighbdConvolve8AvgVert + HighbdConvolve8AvgVert, }, new HighbdConvolveFn[] { HighbdConvolve8Vert, - HighbdConvolve8AvgVert - } + HighbdConvolve8AvgVert, + }, }, - new HighbdConvolveFn[][] + new[] { new HighbdConvolveFn[] { HighbdConvolve8, - HighbdConvolve8Avg + HighbdConvolve8Avg, }, new HighbdConvolveFn[] { HighbdConvolve8, - HighbdConvolve8Avg - } - } + HighbdConvolve8Avg, + }, + }, }; - private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictY16 = new HighbdConvolveFn[][][] - { - new HighbdConvolveFn[][] + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictY16 = { + new[] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, - HighbdConvolve8AvgHoriz + HighbdConvolve8AvgHoriz, }, new HighbdConvolveFn[] { HighbdConvolve8, - HighbdConvolve8Avg - } + HighbdConvolve8Avg, + }, }, - new HighbdConvolveFn[][] + new[] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, - HighbdConvolve8AvgHoriz + HighbdConvolve8AvgHoriz, }, new HighbdConvolveFn[] { HighbdConvolve8, - HighbdConvolve8Avg - } - } - }; - - private static readonly unsafe HighbdConvolveFn[][][] HighbdPredict = new HighbdConvolveFn[][][] - { - new HighbdConvolveFn[][] - { - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg + HighbdConvolve8Avg, }, - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg - } }, - new HighbdConvolveFn[][] + }; + + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredict = { + new[] { new HighbdConvolveFn[] { HighbdConvolve8, - HighbdConvolve8Avg + HighbdConvolve8Avg, }, new HighbdConvolveFn[] { HighbdConvolve8, - HighbdConvolve8Avg - } - } + HighbdConvolve8Avg, + }, + }, + new[] + { + new HighbdConvolveFn[] + { + HighbdConvolve8, + HighbdConvolve8Avg, + }, + new HighbdConvolveFn[] + { + 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 int ScaleValueX(int val) + public readonly int ScaleValueX(int val) { return IsScaled() ? ScaledX(val) : val; } - public int ScaleValueY(int val) + public readonly int ScaleValueY(int val) { return IsScaled() ? ScaledY(val) : val; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void InterPredict( + public readonly unsafe void InterPredict( int horiz, int vert, int avg, @@ -315,12 +307,12 @@ 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 @@ -328,18 +320,18 @@ 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 { // Must always scale in both directions. - Predict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); + _predict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void HighbdInterPredict( + public readonly unsafe void HighbdInterPredict( int horiz, int vert, int avg, @@ -361,12 +353,12 @@ 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 @@ -374,22 +366,22 @@ 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 int ScaledX(int val) + private readonly int ScaledX(int val) { return (int)((long)val * XScaleFP >> RefScaleShift); } - private int ScaledY(int val) + private readonly int ScaledY(int val) { return (int)((long)val * YScaleFP >> RefScaleShift); } @@ -407,20 +399,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { int xOffQ4 = ScaledX(x << SubpelBits) & SubpelMask; int yOffQ4 = ScaledY(y << SubpelBits) & SubpelMask; - Mv32 res = new Mv32() + Mv32 res = new() { Row = ScaledY(mv.Row) + yOffQ4, - Col = ScaledX(mv.Col) + xOffQ4 + Col = ScaledX(mv.Col) + xOffQ4, }; + return res; } - public bool IsValidScale() + public readonly bool IsValidScale() { return XScaleFP != RefInvalidScale && YScaleFP != RefInvalidScale; } - public bool IsScaled() + public readonly bool IsScaled() { return IsValidScale() && (XScaleFP != RefNoScale || YScaleFP != RefNoScale); } @@ -439,6 +432,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { XScaleFP = RefInvalidScale; YScaleFP = RefInvalidScale; + return; } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs index c3ea3fd89..8d04d18b8 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs @@ -2,10 +2,10 @@ { 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 + 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 } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs index 53d1f2ccb..c0fbdc51e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal struct Segmentation { - private static readonly int[] SegFeatureDataSigned = new int[] { 1, 1, 0, 0 }; - private static readonly int[] SegFeatureDataMax = new int[] { QuantCommon.MaxQ, Vp9.LoopFilter.MaxLoopFilter, 3, 0 }; + 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; @@ -26,8 +26,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public void ClearAllSegFeatures() { - MemoryMarshal.CreateSpan(ref FeatureData[0][0], 8 * 4).Fill(0); - MemoryMarshal.CreateSpan(ref FeatureMask[0], 8).Fill(0); + MemoryMarshal.CreateSpan(ref FeatureData[0][0], 8 * 4).Clear(); + MemoryMarshal.CreateSpan(ref FeatureMask[0], 8).Clear(); AqAvOffset = 0; } @@ -38,21 +38,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types internal static int FeatureDataMax(SegLvlFeatures featureId) { - return SegFeatureDataMax[(int)featureId]; + return _segFeatureDataMax[(int)featureId]; } internal static int IsSegFeatureSigned(SegLvlFeatures featureId) { - return SegFeatureDataSigned[(int)featureId]; + return _segFeatureDataSigned[(int)featureId]; } internal void SetSegData(int segmentId, SegLvlFeatures featureId, int segData) { - Debug.Assert(segData <= SegFeatureDataMax[(int)featureId]); + Debug.Assert(segData <= _segFeatureDataMax[(int)featureId]); if (segData < 0) { - Debug.Assert(SegFeatureDataSigned[(int)featureId] != 0); - Debug.Assert(-segData <= SegFeatureDataMax[(int)featureId]); + Debug.Assert(_segFeatureDataSigned[(int)featureId] != 0); + Debug.Assert(-segData <= _segFeatureDataMax[(int)featureId]); } FeatureData[segmentId][(int)featureId] = (short)segData; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs index d5b51bc2f..7f34faa5b 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs @@ -11,11 +11,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public ArrayPtr<byte> UBuffer; public ArrayPtr<byte> VBuffer; - public unsafe Plane YPlane => new Plane((IntPtr)YBuffer.ToPointer(), YBuffer.Length); - public unsafe Plane UPlane => new Plane((IntPtr)UBuffer.ToPointer(), UBuffer.Length); - public unsafe Plane VPlane => new Plane((IntPtr)VBuffer.ToPointer(), VBuffer.Length); + public readonly unsafe Plane YPlane => new((IntPtr)YBuffer.ToPointer(), YBuffer.Length); + public readonly unsafe Plane UPlane => new((IntPtr)UBuffer.ToPointer(), UBuffer.Length); + public readonly unsafe Plane VPlane => new((IntPtr)VBuffer.ToPointer(), VBuffer.Length); - public FrameField Field => FrameField.Progressive; + public readonly FrameField Field => FrameField.Progressive; public int Width { get; } public int Height { get; } @@ -27,29 +27,31 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public int UvAlignedWidth { get; } public int UvAlignedHeight { get; } public int UvStride { get; } - public bool HighBd => false; + + public bool HighBd { get; } private readonly IntPtr _pointer; public Surface(int width, int height) { - const int border = 32; - const int ssX = 1; - const int ssY = 1; - const bool highbd = false; + HighBd = false; + + const int Border = 32; + const int SsX = 1; + const int SsY = 1; int alignedWidth = (width + 7) & ~7; int alignedHeight = (height + 7) & ~7; - 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 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 frameSize = (highbd ? 2 : 1) * (yplaneSize + 2 * uvplaneSize); + int frameSize = (HighBd ? 2 : 1) * (yplaneSize + 2 * uvplaneSize); IntPtr pointer = Marshal.AllocHGlobal(frameSize); _pointer = pointer; @@ -58,23 +60,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types AlignedWidth = alignedWidth; AlignedHeight = alignedHeight; Stride = yStride; - UvWidth = (width + ssX) >> ssX; - UvHeight = (height + ssY) >> ssY; + UvWidth = (width + SsX) >> SsX; + UvHeight = (height + SsY) >> SsY; UvAlignedWidth = uvWidth; UvAlignedHeight = uvHeight; UvStride = uvStride; - ArrayPtr<byte> NewPlane(int start, int size, int border) + ArrayPtr<byte> NewPlane(int start, int size, int planeBorder) { - return new ArrayPtr<byte>(pointer + start + border, size - border); + return new ArrayPtr<byte>(pointer + start + planeBorder, size - planeBorder); } - YBuffer = NewPlane(0, yplaneSize, (border * yStride) + border); + YBuffer = NewPlane(0, yplaneSize, (Border * yStride) + Border); UBuffer = NewPlane(yplaneSize, uvplaneSize, (uvBorderH * uvStride) + uvBorderW); VBuffer = NewPlane(yplaneSize + uvplaneSize, uvplaneSize, (uvBorderH * uvStride) + uvBorderW); } - public void Dispose() + public readonly 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 67289c47d..232d4ccb2 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { int sbCols = MiColsAlignedToSb(mis) >> Constants.MiBlockSizeLog2; int offset = ((idx * sbCols) >> log2) << Constants.MiBlockSizeLog2; + return Math.Min(offset, mis); } @@ -44,7 +45,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // Checks that the given miRow, miCol and search point // are inside the borders of the tile. - public bool IsInside(int miCol, int miRow, int miRows, ref Position miPos) + public readonly bool IsInside(int miCol, int miRow, int miRows, ref Position miPos) { return !(miRow + miPos.Row < 0 || miCol + miPos.Col < MiColStart || diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs index db914525c..80c963fe0 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs @@ -2,11 +2,11 @@ { 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 + 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 + TxModes = 5, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs index 994deb2c3..bcc70ebab 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs @@ -2,10 +2,10 @@ { public enum TxSize { - Tx4x4 = 0, // 4x4 transform - Tx8x8 = 1, // 8x8 transform + Tx4x4 = 0, // 4x4 transform + Tx8x8 = 1, // 8x8 transform Tx16x16 = 2, // 16x16 transform Tx32x32 = 3, // 32x32 transform - TxSizes = 4 + TxSizes = 4, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs index dbf7251cd..1ae436e96 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs @@ -2,10 +2,10 @@ { 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 + 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 + TxTypes = 4, } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs index faadd3498..8dd0879eb 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public ArrayPtr<sbyte> AboveSegContext; public ArrayPtr<sbyte> AboveContext; - public bool FrameIsIntraOnly() + public readonly bool FrameIsIntraOnly() { return FrameType == FrameType.KeyFrame || IntraOnly; } @@ -132,7 +132,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types TileWorkerData = allocator.Allocate<TileWorkerData>(tileCols * tileRows + (maxThreads > 1 ? maxThreads : 0)); } - public void FreeTileWorkerData(MemoryAllocator allocator) + public readonly void FreeTileWorkerData(MemoryAllocator allocator) { allocator.Free(TileWorkerData); } @@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } } - private void SetPartitionProbs(ref MacroBlockD xd) + private readonly void SetPartitionProbs(ref MacroBlockD xd) { xd.PartitionProbs = FrameIsIntraOnly() ? new ArrayPtr<Array3<byte>>(ref Fc.Value.KfPartitionProb[0], 16) @@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public void SetupSegmentationDequant() { - const BitDepth bitDepth = BitDepth.Bits8; // TODO: Configurable + const BitDepth BitDepth = BitDepth.Bits8; // TODO: Configurable // Build y/uv dequant values based on segmentation. if (Seg.Enabled) { @@ -301,10 +301,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types for (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); + 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 @@ -312,10 +312,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types 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); } } From 2cdc82cb910d03e13d36b6a31148b5b87e35fde9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jun 2023 09:36:22 +0200 Subject: [PATCH 675/737] nuget: bump Microsoft.NET.Test.Sdk from 17.6.2 to 17.6.3 (#5406) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.2 to 17.6.3. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.2...v17.6.3) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a100f8261..df917cbab 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,7 +21,7 @@ <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> - <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.2" /> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="NUnit" Version="3.13.3" /> From 9288ffd26d0b0b831f62cfc0281c71662d251884 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Wed, 28 Jun 2023 17:36:30 +0200 Subject: [PATCH 676/737] Cpu: Implement VCVT (between floating-point and fixed-point) instruction (#5343) * cpu: Implement VCVT (between floating-point and fixed-point) instruction Rebase, fix and superseed of https://github.com/Ryujinx/Ryujinx/pull/2915 (Since I only have little CPU knowledge, I hope I have done everything good) * Update Ptc.cs * Fix wrong cast * Rename tests * Addresses feedback Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com> * Remove extra empty line --------- Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com> --- .../Decoders/OpCode32SimdCvtFFixed.cs | 23 ++ src/ARMeilleure/Decoders/OpCodeTable.cs | 341 +++++++++--------- .../Instructions/InstEmitSimdCvt32.cs | 29 ++ src/ARMeilleure/Translation/PTC/Ptc.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs | 90 ++++- 5 files changed, 309 insertions(+), 176 deletions(-) create mode 100644 src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs diff --git a/src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs b/src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs new file mode 100644 index 000000000..f8564d0e5 --- /dev/null +++ b/src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs @@ -0,0 +1,23 @@ +namespace ARMeilleure.Decoders +{ + class OpCode32SimdCvtFFixed : OpCode32Simd + { + public int Fbits { get; protected set; } + + public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFFixed(inst, address, opCode, false); + public new static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFFixed(inst, address, opCode, true); + + public OpCode32SimdCvtFFixed(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb) + { + Opc = (opCode >> 8) & 0x1; + + Size = Opc == 1 ? 0 : 2; + Fbits = 64 - ((opCode >> 16) & 0x3f); + + if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm)) + { + Instruction = InstDescriptor.Undefined; + } + } + } +} \ No newline at end of file diff --git a/src/ARMeilleure/Decoders/OpCodeTable.cs b/src/ARMeilleure/Decoders/OpCodeTable.cs index d3fc4ca08..5cfd0bb81 100644 --- a/src/ARMeilleure/Decoders/OpCodeTable.cs +++ b/src/ARMeilleure/Decoders/OpCodeTable.cs @@ -883,174 +883,175 @@ namespace ARMeilleure.Decoders SetVfp("<<<<11100x11xxxxxxxx101xx1x0xxxx", InstName.Vsub, InstEmit32.Vsub_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32); // ASIMD - SetAsimd("111100111x110000xxx0001101x0xxx0", InstName.Aesd_V, InstEmit32.Aesd_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); - SetAsimd("111100111x110000xxx0001100x0xxx0", InstName.Aese_V, InstEmit32.Aese_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); - SetAsimd("111100111x110000xxx0001111x0xxx0", InstName.Aesimc_V, InstEmit32.Aesimc_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); - SetAsimd("111100111x110000xxx0001110x0xxx0", InstName.Aesmc_V, InstEmit32.Aesmc_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); - SetAsimd("111100110x00xxx0xxx01100x1x0xxx0", InstName.Sha256h_V, InstEmit32.Sha256h_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x01xxx0xxx01100x1x0xxx0", InstName.Sha256h2_V, InstEmit32.Sha256h2_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x111010xxx0001111x0xxx0", InstName.Sha256su0_V, InstEmit32.Sha256su0_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); - SetAsimd("111100110x10xxx0xxx01100x1x0xxx0", InstName.Sha256su1_V, InstEmit32.Sha256su1_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx0111xxx0xxxx", InstName.Vabd, InstEmit32.Vabd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x<<xxxxxxxx0111x0x0xxxx", InstName.Vabdl, InstEmit32.Vabdl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); - SetAsimd("111100111x11<<01xxxx00110xx0xxxx", InstName.Vabs, InstEmit32.Vabs_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x111001xxxx01110xx0xxxx", InstName.Vabs, InstEmit32.Vabs_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100100xxxxxxxxxxx1000xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x00xxxxxxxx1101xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx00000x0x0xxxx", InstName.Vaddl, InstEmit32.Vaddl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx00001x0x0xxxx", InstName.Vaddw, InstEmit32.Vaddw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32); - SetAsimd("111100100x00xxxxxxxx0001xxx1xxxx", InstName.Vand, InstEmit32.Vand_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("111100100x01xxxxxxxx0001xxx1xxxx", InstName.Vbic, InstEmit32.Vbic_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("1111001x1x000xxxxxxx<<x10x11xxxx", InstName.Vbic, InstEmit32.Vbic_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); - SetAsimd("111100110x11xxxxxxxx0001xxx1xxxx", InstName.Vbif, InstEmit32.Vbif, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("111100110x10xxxxxxxx0001xxx1xxxx", InstName.Vbit, InstEmit32.Vbit, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("111100110x01xxxxxxxx0001xxx1xxxx", InstName.Vbsl, InstEmit32.Vbsl, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("111100110x<<xxxxxxxx1000xxx1xxxx", InstName.Vceq, InstEmit32.Vceq_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x00xxxxxxxx1110xxx0xxxx", InstName.Vceq, InstEmit32.Vceq_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x11xx01xxxx0x010xx0xxxx", InstName.Vceq, InstEmit32.Vceq_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx0011xxx1xxxx", InstName.Vcge, InstEmit32.Vcge_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x00xxxxxxxx1110xxx0xxxx", InstName.Vcge, InstEmit32.Vcge_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x11xx01xxxx0x001xx0xxxx", InstName.Vcge, InstEmit32.Vcge_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx0011xxx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x10xxxxxxxx1110xxx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x11xx01xxxx0x000xx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x11xx01xxxx0x011xx0xxxx", InstName.Vcle, InstEmit32.Vcle_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x11xx01xxxx0x100xx0xxxx", InstName.Vclt, InstEmit32.Vclt_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x110000xxxx01010xx0xxxx", InstName.Vcnt, InstEmit32.Vcnt, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x111011xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); // FP and integer, vector. - SetAsimd("111100111x11xxxxxxxx11000xx0xxxx", InstName.Vdup, InstEmit32.Vdup_1, OpCode32SimdDupElem.Create, OpCode32SimdDupElem.CreateT32); - SetAsimd("111100110x00xxxxxxxx0001xxx1xxxx", InstName.Veor, InstEmit32.Veor_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("111100101x11xxxxxxxxxxxxxxx0xxxx", InstName.Vext, InstEmit32.Vext, OpCode32SimdExt.Create, OpCode32SimdExt.CreateT32); - SetAsimd("111100100x00xxxxxxxx1100xxx1xxxx", InstName.Vfma, InstEmit32.Vfma_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x10xxxxxxxx1100xxx1xxxx", InstName.Vfms, InstEmit32.Vfms_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx0000xxx0xxxx", InstName.Vhadd, InstEmit32.Vhadd, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111101001x10xxxxxxxx0000xxx0xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx0100xx0xxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1000x000xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1000x011xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx110000x0xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx110001xxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx110010xxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x10xxxxxxxx0111xx0xxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1. - SetAsimd("111101000x10xxxxxxxx1010xx<<xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2. - SetAsimd("111101000x10xxxxxxxx0110xx0xxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 3. - SetAsimd("111101000x10xxxxxxxx0010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 4. - SetAsimd("111101001x10xxxxxxxx0x01xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1001xx0xxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1101<<xxxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x10xxxxxxxx100x<<0xxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). - SetAsimd("111101000x10xxxxxxxx100x<<10xxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). - SetAsimd("111101000x10xxxxxxxx0011<<xxxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2, inc = 2. - SetAsimd("111101001x10xxxxxxxx0x10xxx0xxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1010xx00xxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1110<<x0xxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x10xxxxxxxx010x<<0xxxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). - SetAsimd("111101001x10xxxxxxxx0x11xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1011xx<<xxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x10xxxxxxxx1111<<x>xxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x10xxxxxxxx000x<<xxxxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). - SetAsimd("1111001x0x<<xxxxxxxx0110xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x00xxxxxxxx1111xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx0110xxx1xxxx", InstName.Vmin, InstEmit32.Vmin_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x10xxxxxxxx1111xxx0xxxx", InstName.Vmin, InstEmit32.Vmin_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x0xxxxxxxxx1111xxx1xxxx", InstName.Vmaxnm, InstEmit32.Vmaxnm_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x1xxxxxxxxx1111xxx1xxxx", InstName.Vminnm, InstEmit32.Vminnm_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x<<xxxxxxxx000xx1x0xxxx", InstName.Vmla, InstEmit32.Vmla_1, OpCode32SimdRegElem.Create, OpCode32SimdRegElem.CreateT32); - SetAsimd("111100100xxxxxxxxxxx1001xxx0xxxx", InstName.Vmla, InstEmit32.Vmla_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x00xxxxxxxx1101xxx1xxxx", InstName.Vmla, InstEmit32.Vmla_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx01000x0x0xxxx", InstName.Vmlal, InstEmit32.Vmlal_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); - SetAsimd("1111001x1x<<xxxxxxxx010xx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_1, OpCode32SimdRegElem.Create, OpCode32SimdRegElem.CreateT32); - SetAsimd("111100100x10xxxxxxxx1101xxx1xxxx", InstName.Vmls, InstEmit32.Vmls_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110xxxxxxxxxxx1001xxx0xxxx", InstName.Vmls, InstEmit32.Vmls_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx01010x0x0xxxx", InstName.Vmlsl, InstEmit32.Vmlsl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); - SetAsimd("1111001x1x000xxxxxxx0xx00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q vector I32. - SetAsimd("1111001x1x000xxxxxxx10x00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q I16. - SetAsimd("1111001x1x000xxxxxxx11xx0x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q (dt - from cmode). - SetAsimd("1111001x1x000xxxxxxx11100x11xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q I64. - SetAsimd("1111001x1x001000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create, OpCode32SimdLong.CreateT32); - SetAsimd("1111001x1x010000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create, OpCode32SimdLong.CreateT32); - SetAsimd("1111001x1x100000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create, OpCode32SimdLong.CreateT32); - SetAsimd("111100111x11<<10xxxx001000x0xxx0", InstName.Vmovn, InstEmit32.Vmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); - SetAsimd("1111001x1x<<xxxxxxxx100xx1x0xxxx", InstName.Vmul, InstEmit32.Vmul_1, OpCode32SimdRegElem.Create, OpCode32SimdRegElem.CreateT32); - SetAsimd("111100100x<<xxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x00xxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x00xxxxxxxx1101xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx01010x1x0xxxx", InstName.Vmull, InstEmit32.Vmull_1, OpCode32SimdRegElemLong.Create, OpCode32SimdRegElemLong.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx01100x0x0xxxx", InstName.Vmull, InstEmit32.Vmull_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); - SetAsimd("111100101xx0xxxxxxx01110x0x0xxxx", InstName.Vmull, InstEmit32.Vmull_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); // P8/P64 - SetAsimd("111100111x110000xxxx01011xx0xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("1111001x1x000xxxxxxx0xx00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q vector I32. - SetAsimd("1111001x1x000xxxxxxx10x00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); - SetAsimd("1111001x1x000xxxxxxx110x0x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); - SetAsimd("111100111x11<<01xxxx00111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x111001xxxx01111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100100x11xxxxxxxx0001xxx1xxxx", InstName.Vorn, InstEmit32.Vorn_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("111100100x10xxxxxxxx0001xxx1xxxx", InstName.Vorr, InstEmit32.Vorr_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); - SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); - SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx1010x0x1xxxx", InstName.Vpmin, InstEmit32.Vpmin_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100110x10xxxxxxxx1111x0x0xxxx", InstName.Vpmin, InstEmit32.Vpmin_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x0xxxxxxxxxxx0000xxx1xxxx", InstName.Vqadd, InstEmit32.Vqadd, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x01xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); - SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); - SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); - SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); - SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); - SetAsimd("111100111x>>>xxxxxxx100000x1xxx0", InstName.Vqshrun, InstEmit32.Vqshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); - SetAsimd("1111001x0xxxxxxxxxxx0010xxx1xxxx", InstName.Vqsub, InstEmit32.Vqsub, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x111011xxxx010x0xx0xxxx", InstName.Vrecpe, InstEmit32.Vrecpe, OpCode32SimdSqrte.Create, OpCode32SimdSqrte.CreateT32); - SetAsimd("111100100x00xxxxxxxx1111xxx1xxxx", InstName.Vrecps, InstEmit32.Vrecps, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x11xx00xxxx000<<xx0xxxx", InstName.Vrev, InstEmit32.Vrev, OpCode32SimdRev.Create, OpCode32SimdRev.CreateT32); - SetAsimd("1111001x0x<<xxxxxxxx0001xxx0xxxx", InstName.Vrhadd, InstEmit32.Vrhadd, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x111010xxxx01010xx0xxxx", InstName.Vrinta, InstEmit32.Vrinta_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x111010xxxx01101xx0xxxx", InstName.Vrintm, InstEmit32.Vrintm_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x111010xxxx01000xx0xxxx", InstName.Vrintn, InstEmit32.Vrintn_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x111010xxxx01111xx0xxxx", InstName.Vrintp, InstEmit32.Vrintp_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("1111001x1x>>>xxxxxxx0010>xx1xxxx", InstName.Vrshr, InstEmit32.Vrshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); - SetAsimd("111100101x>>>xxxxxxx100001x1xxx0", InstName.Vrshrn, InstEmit32.Vrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); - SetAsimd("111100111x111011xxxx010x1xx0xxxx", InstName.Vrsqrte, InstEmit32.Vrsqrte, OpCode32SimdSqrte.Create, OpCode32SimdSqrte.CreateT32); - SetAsimd("111100100x10xxxxxxxx1111xxx1xxxx", InstName.Vrsqrts, InstEmit32.Vrsqrts, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x>>>xxxxxxx0011>xx1xxxx", InstName.Vrsra, InstEmit32.Vrsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); - SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); - SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding. - SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); - SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); - SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); - SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x00xxxxxxxx1000x000xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x00xxxxxxxx1000x011xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x00xxxxxxxx0111xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1. - SetAsimd("111101000x00xxxxxxxx1010xx<<xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2. - SetAsimd("111101000x00xxxxxxxx0110xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 3. - SetAsimd("111101000x00xxxxxxxx0010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 4. - SetAsimd("111101001x00xxxxxxxx0x01xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x00xxxxxxxx1001xx0xxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x00xxxxxxxx100x<<0xxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). - SetAsimd("111101000x00xxxxxxxx100x<<10xxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). - SetAsimd("111101000x00xxxxxxxx0011<<xxxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2, inc = 2. - SetAsimd("111101001x00xxxxxxxx0x10xxx0xxxx", InstName.Vst3, InstEmit32.Vst3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x00xxxxxxxx1010xx00xxxx", InstName.Vst3, InstEmit32.Vst3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x00xxxxxxxx010x<<0xxxxx", InstName.Vst3, InstEmit32.Vst3, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). - SetAsimd("111101001x00xxxxxxxx0x11xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101001x00xxxxxxxx1011xx<<xxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); - SetAsimd("111101000x00xxxxxxxx000x<<xxxxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). - SetAsimd("111100110xxxxxxxxxxx1000xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); - SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32); - SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32); - SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); - SetAsimd("111100111x11<<10xxxx00010xx0xxxx", InstName.Vuzp, InstEmit32.Vuzp, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); - SetAsimd("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x110000xxx0001101x0xxx0", InstName.Aesd_V, InstEmit32.Aesd_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); + SetAsimd("111100111x110000xxx0001100x0xxx0", InstName.Aese_V, InstEmit32.Aese_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); + SetAsimd("111100111x110000xxx0001111x0xxx0", InstName.Aesimc_V, InstEmit32.Aesimc_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); + SetAsimd("111100111x110000xxx0001110x0xxx0", InstName.Aesmc_V, InstEmit32.Aesmc_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); + SetAsimd("111100110x00xxx0xxx01100x1x0xxx0", InstName.Sha256h_V, InstEmit32.Sha256h_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x01xxx0xxx01100x1x0xxx0", InstName.Sha256h2_V, InstEmit32.Sha256h2_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x111010xxx0001111x0xxx0", InstName.Sha256su0_V, InstEmit32.Sha256su0_V, OpCode32Simd.Create, OpCode32Simd.CreateT32); + SetAsimd("111100110x10xxx0xxx01100x1x0xxx0", InstName.Sha256su1_V, InstEmit32.Sha256su1_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx0111xxx0xxxx", InstName.Vabd, InstEmit32.Vabd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x<<xxxxxxxx0111x0x0xxxx", InstName.Vabdl, InstEmit32.Vabdl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); + SetAsimd("111100111x11<<01xxxx00110xx0xxxx", InstName.Vabs, InstEmit32.Vabs_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x111001xxxx01110xx0xxxx", InstName.Vabs, InstEmit32.Vabs_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100100xxxxxxxxxxx1000xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x00xxxxxxxx1101xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx00000x0x0xxxx", InstName.Vaddl, InstEmit32.Vaddl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx00001x0x0xxxx", InstName.Vaddw, InstEmit32.Vaddw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32); + SetAsimd("111100100x00xxxxxxxx0001xxx1xxxx", InstName.Vand, InstEmit32.Vand_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("111100100x01xxxxxxxx0001xxx1xxxx", InstName.Vbic, InstEmit32.Vbic_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("1111001x1x000xxxxxxx<<x10x11xxxx", InstName.Vbic, InstEmit32.Vbic_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); + SetAsimd("111100110x11xxxxxxxx0001xxx1xxxx", InstName.Vbif, InstEmit32.Vbif, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("111100110x10xxxxxxxx0001xxx1xxxx", InstName.Vbit, InstEmit32.Vbit, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("111100110x01xxxxxxxx0001xxx1xxxx", InstName.Vbsl, InstEmit32.Vbsl, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("111100110x<<xxxxxxxx1000xxx1xxxx", InstName.Vceq, InstEmit32.Vceq_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x00xxxxxxxx1110xxx0xxxx", InstName.Vceq, InstEmit32.Vceq_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x11xx01xxxx0x010xx0xxxx", InstName.Vceq, InstEmit32.Vceq_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx0011xxx1xxxx", InstName.Vcge, InstEmit32.Vcge_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x00xxxxxxxx1110xxx0xxxx", InstName.Vcge, InstEmit32.Vcge_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x11xx01xxxx0x001xx0xxxx", InstName.Vcge, InstEmit32.Vcge_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx0011xxx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x10xxxxxxxx1110xxx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x11xx01xxxx0x000xx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x11xx01xxxx0x011xx0xxxx", InstName.Vcle, InstEmit32.Vcle_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x11xx01xxxx0x100xx0xxxx", InstName.Vclt, InstEmit32.Vclt_Z, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x110000xxxx01010xx0xxxx", InstName.Vcnt, InstEmit32.Vcnt, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x111011xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); // FP and integer, vector. + SetAsimd("1111001x1x1xxxxxxxxx111x0xx1xxxx", InstName.Vcvt, InstEmit32.Vcvt_V_Fixed, OpCode32SimdCvtFFixed.Create, OpCode32SimdCvtFFixed.CreateT32); // Between floating point and fixed point, vector. + SetAsimd("111100111x11xxxxxxxx11000xx0xxxx", InstName.Vdup, InstEmit32.Vdup_1, OpCode32SimdDupElem.Create, OpCode32SimdDupElem.CreateT32); + SetAsimd("111100110x00xxxxxxxx0001xxx1xxxx", InstName.Veor, InstEmit32.Veor_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("111100101x11xxxxxxxxxxxxxxx0xxxx", InstName.Vext, InstEmit32.Vext, OpCode32SimdExt.Create, OpCode32SimdExt.CreateT32); + SetAsimd("111100100x00xxxxxxxx1100xxx1xxxx", InstName.Vfma, InstEmit32.Vfma_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x10xxxxxxxx1100xxx1xxxx", InstName.Vfms, InstEmit32.Vfms_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx0000xxx0xxxx", InstName.Vhadd, InstEmit32.Vhadd, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111101001x10xxxxxxxx0000xxx0xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx0100xx0xxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1000x000xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1000x011xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx110000x0xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx110001xxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx110010xxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x10xxxxxxxx0111xx0xxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1. + SetAsimd("111101000x10xxxxxxxx1010xx<<xxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2. + SetAsimd("111101000x10xxxxxxxx0110xx0xxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 3. + SetAsimd("111101000x10xxxxxxxx0010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 4. + SetAsimd("111101001x10xxxxxxxx0x01xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1001xx0xxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1101<<xxxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x10xxxxxxxx100x<<0xxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). + SetAsimd("111101000x10xxxxxxxx100x<<10xxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). + SetAsimd("111101000x10xxxxxxxx0011<<xxxxxx", InstName.Vld2, InstEmit32.Vld2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2, inc = 2. + SetAsimd("111101001x10xxxxxxxx0x10xxx0xxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1010xx00xxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1110<<x0xxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x10xxxxxxxx010x<<0xxxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). + SetAsimd("111101001x10xxxxxxxx0x11xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1011xx<<xxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x10xxxxxxxx1111<<x>xxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x10xxxxxxxx000x<<xxxxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). + SetAsimd("1111001x0x<<xxxxxxxx0110xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x00xxxxxxxx1111xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx0110xxx1xxxx", InstName.Vmin, InstEmit32.Vmin_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x10xxxxxxxx1111xxx0xxxx", InstName.Vmin, InstEmit32.Vmin_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x0xxxxxxxxx1111xxx1xxxx", InstName.Vmaxnm, InstEmit32.Vmaxnm_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x1xxxxxxxxx1111xxx1xxxx", InstName.Vminnm, InstEmit32.Vminnm_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x<<xxxxxxxx000xx1x0xxxx", InstName.Vmla, InstEmit32.Vmla_1, OpCode32SimdRegElem.Create, OpCode32SimdRegElem.CreateT32); + SetAsimd("111100100xxxxxxxxxxx1001xxx0xxxx", InstName.Vmla, InstEmit32.Vmla_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x00xxxxxxxx1101xxx1xxxx", InstName.Vmla, InstEmit32.Vmla_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx01000x0x0xxxx", InstName.Vmlal, InstEmit32.Vmlal_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); + SetAsimd("1111001x1x<<xxxxxxxx010xx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_1, OpCode32SimdRegElem.Create, OpCode32SimdRegElem.CreateT32); + SetAsimd("111100100x10xxxxxxxx1101xxx1xxxx", InstName.Vmls, InstEmit32.Vmls_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110xxxxxxxxxxx1001xxx0xxxx", InstName.Vmls, InstEmit32.Vmls_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx01010x0x0xxxx", InstName.Vmlsl, InstEmit32.Vmlsl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); + SetAsimd("1111001x1x000xxxxxxx0xx00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q vector I32. + SetAsimd("1111001x1x000xxxxxxx10x00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q I16. + SetAsimd("1111001x1x000xxxxxxx11xx0x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q (dt - from cmode). + SetAsimd("1111001x1x000xxxxxxx11100x11xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q I64. + SetAsimd("1111001x1x001000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create, OpCode32SimdLong.CreateT32); + SetAsimd("1111001x1x010000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create, OpCode32SimdLong.CreateT32); + SetAsimd("1111001x1x100000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create, OpCode32SimdLong.CreateT32); + SetAsimd("111100111x11<<10xxxx001000x0xxx0", InstName.Vmovn, InstEmit32.Vmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); + SetAsimd("1111001x1x<<xxxxxxxx100xx1x0xxxx", InstName.Vmul, InstEmit32.Vmul_1, OpCode32SimdRegElem.Create, OpCode32SimdRegElem.CreateT32); + SetAsimd("111100100x<<xxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x00xxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x00xxxxxxxx1101xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx01010x1x0xxxx", InstName.Vmull, InstEmit32.Vmull_1, OpCode32SimdRegElemLong.Create, OpCode32SimdRegElemLong.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx01100x0x0xxxx", InstName.Vmull, InstEmit32.Vmull_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); + SetAsimd("111100101xx0xxxxxxx01110x0x0xxxx", InstName.Vmull, InstEmit32.Vmull_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); // P8/P64 + SetAsimd("111100111x110000xxxx01011xx0xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("1111001x1x000xxxxxxx0xx00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); // D/Q vector I32. + SetAsimd("1111001x1x000xxxxxxx10x00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); + SetAsimd("1111001x1x000xxxxxxx110x0x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); + SetAsimd("111100111x11<<01xxxx00111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x111001xxxx01111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100100x11xxxxxxxx0001xxx1xxxx", InstName.Vorn, InstEmit32.Vorn_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("111100100x10xxxxxxxx0001xxx1xxxx", InstName.Vorr, InstEmit32.Vorr_I, OpCode32SimdBinary.Create, OpCode32SimdBinary.CreateT32); + SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32); + SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx1010x0x1xxxx", InstName.Vpmin, InstEmit32.Vpmin_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100110x10xxxxxxxx1111x0x0xxxx", InstName.Vpmin, InstEmit32.Vpmin_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x0xxxxxxxxxxx0000xxx1xxxx", InstName.Vqadd, InstEmit32.Vqadd, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x01xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); + SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); + SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); + SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); + SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); + SetAsimd("111100111x>>>xxxxxxx100000x1xxx0", InstName.Vqshrun, InstEmit32.Vqshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); + SetAsimd("1111001x0xxxxxxxxxxx0010xxx1xxxx", InstName.Vqsub, InstEmit32.Vqsub, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x111011xxxx010x0xx0xxxx", InstName.Vrecpe, InstEmit32.Vrecpe, OpCode32SimdSqrte.Create, OpCode32SimdSqrte.CreateT32); + SetAsimd("111100100x00xxxxxxxx1111xxx1xxxx", InstName.Vrecps, InstEmit32.Vrecps, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x11xx00xxxx000<<xx0xxxx", InstName.Vrev, InstEmit32.Vrev, OpCode32SimdRev.Create, OpCode32SimdRev.CreateT32); + SetAsimd("1111001x0x<<xxxxxxxx0001xxx0xxxx", InstName.Vrhadd, InstEmit32.Vrhadd, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x111010xxxx01010xx0xxxx", InstName.Vrinta, InstEmit32.Vrinta_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x111010xxxx01101xx0xxxx", InstName.Vrintm, InstEmit32.Vrintm_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x111010xxxx01000xx0xxxx", InstName.Vrintn, InstEmit32.Vrintn_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x111010xxxx01111xx0xxxx", InstName.Vrintp, InstEmit32.Vrintp_V, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("1111001x1x>>>xxxxxxx0010>xx1xxxx", InstName.Vrshr, InstEmit32.Vrshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); + SetAsimd("111100101x>>>xxxxxxx100001x1xxx0", InstName.Vrshrn, InstEmit32.Vrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); + SetAsimd("111100111x111011xxxx010x1xx0xxxx", InstName.Vrsqrte, InstEmit32.Vrsqrte, OpCode32SimdSqrte.Create, OpCode32SimdSqrte.CreateT32); + SetAsimd("111100100x10xxxxxxxx1111xxx1xxxx", InstName.Vrsqrts, InstEmit32.Vrsqrts, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x>>>xxxxxxx0011>xx1xxxx", InstName.Vrsra, InstEmit32.Vrsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); + SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); + SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding. + SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); + SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32); + SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32); + SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x00xxxxxxxx1000x000xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x00xxxxxxxx1000x011xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x00xxxxxxxx0111xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1. + SetAsimd("111101000x00xxxxxxxx1010xx<<xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2. + SetAsimd("111101000x00xxxxxxxx0110xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 3. + SetAsimd("111101000x00xxxxxxxx0010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 4. + SetAsimd("111101001x00xxxxxxxx0x01xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x00xxxxxxxx1001xx0xxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x00xxxxxxxx100x<<0xxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). + SetAsimd("111101000x00xxxxxxxx100x<<10xxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 1, inc = 1/2 (itype). + SetAsimd("111101000x00xxxxxxxx0011<<xxxxxx", InstName.Vst2, InstEmit32.Vst2, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Regs = 2, inc = 2. + SetAsimd("111101001x00xxxxxxxx0x10xxx0xxxx", InstName.Vst3, InstEmit32.Vst3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x00xxxxxxxx1010xx00xxxx", InstName.Vst3, InstEmit32.Vst3, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x00xxxxxxxx010x<<0xxxxx", InstName.Vst3, InstEmit32.Vst3, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). + SetAsimd("111101001x00xxxxxxxx0x11xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101001x00xxxxxxxx1011xx<<xxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32); + SetAsimd("111101000x00xxxxxxxx000x<<xxxxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemPair.Create, OpCode32SimdMemPair.CreateT32); // Inc = 1/2 (itype). + SetAsimd("111100110xxxxxxxxxxx1000xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32); + SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32); + SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32); + SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32); + SetAsimd("111100111x11<<10xxxx00010xx0xxxx", InstName.Vuzp, InstEmit32.Vuzp, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); + SetAsimd("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32); #endregion #region "OpCode Table (AArch32, T16)" @@ -1130,7 +1131,7 @@ namespace ARMeilleure.Decoders SetT16("1101<<<xxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT16BImm8.Create); SetT16("11011111xxxxxxxx", InstName.Svc, InstEmit32.Svc, OpCodeT16Exception.Create); SetT16("11100xxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT16BImm11.Create); -#endregion + #endregion #region "OpCode Table (AArch32, T32)" // Base @@ -1298,7 +1299,7 @@ namespace ARMeilleure.Decoders SetT32("11110011101011111000000000000010", InstName.Wfe, InstEmit32.Nop, OpCodeT32.Create); SetT32("11110011101011111000000000000011", InstName.Wfi, InstEmit32.Nop, OpCodeT32.Create); SetT32("11110011101011111000000000000001", InstName.Yield, InstEmit32.Nop, OpCodeT32.Create); -#endregion + #endregion FillFastLookupTable(_instA32FastLookup, _allInstA32, ToFastLookupIndexA); FillFastLookupTable(_instT32FastLookup, _allInstT32, ToFastLookupIndexT); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index dcdcc65de..b03855910 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -114,6 +114,35 @@ namespace ARMeilleure.Instructions } } + public static void Vcvt_V_Fixed(ArmEmitterContext context) + { + OpCode32SimdCvtFFixed op = (OpCode32SimdCvtFFixed)context.CurrOp; + + var toFixed = op.Opc == 1; + int fracBits = op.Fbits; + var unsigned = op.U; + + if (toFixed) // F32 to S32 or U32 (fixed) + { + EmitVectorUnaryOpF32(context, (op1) => + { + var scaledValue = context.Multiply(op1, ConstF(MathF.Pow(2f, fracBits))); + MethodInfo info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)); + + return context.Call(info, scaledValue); + }); + } + else // S32 or U32 (fixed) to F32 + { + EmitVectorUnaryOpI32(context, (op1) => + { + var floatValue = unsigned ? context.ConvertToFPUI(OperandType.FP32, op1) : context.ConvertToFP(OperandType.FP32, op1); + + return context.Multiply(floatValue, ConstF(1f / MathF.Pow(2f, fracBits))); + }, !unsigned); + } + } + public static void Vcvt_FD(ArmEmitterContext context) { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 72b60fab0..14d4e471f 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 5292; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5343; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs index aa68cded3..e9a8ad590 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs @@ -395,11 +395,11 @@ namespace Ryujinx.Tests.Cpu [Explicit] [Test, Pairwise, Description("VCVT<top>.F<size>.F16 <Vd>, <Sm>")] public void Vcvt_F16_Fx([Values(0u, 1u, 2u, 3u)] uint rd, - [Values(0u, 1u, 2u, 3u)] uint rm, - [ValueSource(nameof(_1D_F_))] ulong d0, - [ValueSource(nameof(_1D_F_))] ulong d1, - [Values] bool top, - [Values] bool sz) + [Values(0u, 1u, 2u, 3u)] uint rm, + [ValueSource(nameof(_1D_F_))] ulong d0, + [ValueSource(nameof(_1D_F_))] ulong d1, + [Values] bool top, + [Values] bool sz) { uint opcode = 0xeeb20a40; // VCVTB.F32.F16 S0, S0 @@ -426,6 +426,86 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } + + [Test, Pairwise, Description("VCVT.I32.F32 <Vd>, <Vm>, #<fbits>")] + public void Vcvt_V_Fixed_F32_I32([Values(0u, 1u, 2u, 3u)] uint vd, + [Values(0u, 1u, 2u, 3u)] uint vm, + [ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s0, + [ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s1, + [ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s2, + [ValueSource(nameof(_1S_F_))][Random(RndCnt)] ulong s3, + [Random(32u, 63u, 1)] uint fixImm, + [Values] bool unsigned, + [Values] bool q) + { + uint opcode = 0xF2800F10u; // VCVT.U32.F32 D0, D0, #0 + + if (q) + { + opcode |= 1 << 6; + vm <<= 1; + vd <<= 1; + } + + if (unsigned) + { + opcode |= 1 << 24; + } + + opcode |= ((vm & 0x10) << 1); + opcode |= ((vm & 0xf) << 0); + + opcode |= ((vd & 0x10) << 18); + opcode |= ((vd & 0xf) << 12); + + opcode |= (fixImm & 0x3f) << 16; + + var v0 = new V128((uint)s0, (uint)s1, (uint)s2, (uint)s3); + + SingleOpcode(opcode, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("VCVT.F32.I32 <Vd>, <Vm>, #<fbits>")] + public void Vcvt_V_Fixed_I32_F32([Values(0u, 1u, 2u, 3u)] uint vd, + [Values(0u, 1u, 2u, 3u)] uint vm, + [ValueSource(nameof(_1S_))][Random(RndCnt)] uint s0, + [ValueSource(nameof(_1S_))][Random(RndCnt)] uint s1, + [ValueSource(nameof(_1S_))][Random(RndCnt)] uint s2, + [ValueSource(nameof(_1S_))][Random(RndCnt)] uint s3, + [Range(32u, 63u, 1)] uint fixImm, + [Values] bool unsigned, + [Values] bool q) + { + uint opcode = 0xF2800E10u; // VCVT.F32.U32 D0, D0, #0 + + if (q) + { + opcode |= 1 << 6; + vm <<= 1; + vd <<= 1; + } + + if (unsigned) + { + opcode |= 1 << 24; + } + + opcode |= ((vm & 0x10) << 1); + opcode |= ((vm & 0xf) << 0); + + opcode |= ((vd & 0x10) << 18); + opcode |= ((vd & 0xf) << 12); + + opcode |= (fixImm & 0x3f) << 16; + + var v0 = new V128(s0, s1, s2, s3); + + SingleOpcode(opcode, v0: v0); + + CompareAgainstUnicorn(); + } #endif } } \ No newline at end of file From 40f2bd37e3b025ae3b932d15971d8a8e2f8b9866 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:10:55 +0200 Subject: [PATCH 677/737] [Ryujinx.Graphics.OpenGL] Address dotnet-format issues (#5372) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Address or silence dotnet format IDE1006 warnings * Fix IDE0090 after rebase * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Another rebase, another dotnet format run * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Address review feedback --- .../BackgroundContextWorker.cs | 10 +- src/Ryujinx.Graphics.OpenGL/Debugger.cs | 16 ++- .../DrawTextureEmulation.cs | 8 +- .../Effects/FsrScalingFilter.cs | 4 +- .../Effects/FxaaPostProcessingEffect.cs | 2 +- .../Effects/IPostProcessingEffect.cs | 4 +- .../Effects/IScalingFilter.cs | 2 +- .../Effects/ShaderHelper.cs | 1 - .../Effects/SmaaPostProcessingEffect.cs | 2 +- src/Ryujinx.Graphics.OpenGL/FormatInfo.cs | 42 +++---- src/Ryujinx.Graphics.OpenGL/FormatTable.cs | 8 +- src/Ryujinx.Graphics.OpenGL/Framebuffer.cs | 2 +- src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 103 +++++++++--------- .../Image/IntermmediatePool.cs | 2 +- src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs | 4 +- .../Image/TextureBuffer.cs | 4 +- .../Image/TextureCopy.cs | 32 +++--- .../Image/TextureCopyIncompatible.cs | 6 +- .../Image/TextureCopyMS.cs | 6 +- .../Image/TextureStorage.cs | 2 +- .../Image/TextureView.cs | 8 +- src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 10 +- .../PersistentBuffers.cs | 10 +- src/Ryujinx.Graphics.OpenGL/Pipeline.cs | 26 +++-- src/Ryujinx.Graphics.OpenGL/Program.cs | 2 +- .../Queries/BufferedQuery.cs | 8 +- .../Queries/CounterQueue.cs | 25 ++--- .../Queries/CounterQueueEvent.cs | 4 +- .../Queries/Counters.cs | 4 +- src/Ryujinx.Graphics.OpenGL/ResourcePool.cs | 8 +- src/Ryujinx.Graphics.OpenGL/Sync.cs | 22 ++-- src/Ryujinx.Graphics.OpenGL/VertexArray.cs | 4 +- src/Ryujinx.Graphics.OpenGL/Window.cs | 4 +- 33 files changed, 198 insertions(+), 197 deletions(-) diff --git a/src/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs b/src/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs index 764ea7159..ae647e388 100644 --- a/src/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs +++ b/src/Ryujinx.Graphics.OpenGL/BackgroundContextWorker.cs @@ -9,12 +9,12 @@ namespace Ryujinx.Graphics.OpenGL { [ThreadStatic] public static bool InBackground; - private Thread _thread; + private readonly Thread _thread; private bool _running; - private AutoResetEvent _signal; - private Queue<Action> _work; - private ObjectPool<ManualResetEventSlim> _invokePool; + private readonly AutoResetEvent _signal; + private readonly Queue<Action> _work; + private readonly ObjectPool<ManualResetEventSlim> _invokePool; private readonly IOpenGLContext _backgroundContext; public BackgroundContextWorker(IOpenGLContext backgroundContext) @@ -88,4 +88,4 @@ namespace Ryujinx.Graphics.OpenGL _signal.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/Debugger.cs b/src/Ryujinx.Graphics.OpenGL/Debugger.cs index 9f67cfc67..7606bdbfd 100644 --- a/src/Ryujinx.Graphics.OpenGL/Debugger.cs +++ b/src/Ryujinx.Graphics.OpenGL/Debugger.cs @@ -63,10 +63,18 @@ namespace Ryujinx.Graphics.OpenGL switch (type) { - case DebugType.DebugTypeError : Logger.Error?.Print(LogClass.Gpu, $"{severity}: {msg}\nCallStack={Environment.StackTrace}", "GLERROR"); break; - case DebugType.DebugTypePerformance: Logger.Warning?.Print(LogClass.Gpu, $"{severity}: {msg}", "GLPERF"); break; - case DebugType.DebugTypePushGroup : Logger.Info?.Print(LogClass.Gpu, $"{{ ({id}) {severity}: {msg}", "GLINFO"); break; - case DebugType.DebugTypePopGroup : Logger.Info?.Print(LogClass.Gpu, $"}} ({id}) {severity}: {msg}", "GLINFO"); break; + case DebugType.DebugTypeError: + Logger.Error?.Print(LogClass.Gpu, $"{severity}: {msg}\nCallStack={Environment.StackTrace}", "GLERROR"); + break; + case DebugType.DebugTypePerformance: + Logger.Warning?.Print(LogClass.Gpu, $"{severity}: {msg}", "GLPERF"); + break; + case DebugType.DebugTypePushGroup: + Logger.Info?.Print(LogClass.Gpu, $"{{ ({id}) {severity}: {msg}", "GLINFO"); + break; + case DebugType.DebugTypePopGroup: + Logger.Info?.Print(LogClass.Gpu, $"}} ({id}) {severity}: {msg}", "GLINFO"); + break; default: if (source == DebugSource.DebugSourceApplication) { diff --git a/src/Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs b/src/Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs index 509e20fe2..7a6af95ea 100644 --- a/src/Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs +++ b/src/Ryujinx.Graphics.OpenGL/DrawTextureEmulation.cs @@ -65,16 +65,12 @@ void main() if (x0 > x1) { - float temp = s0; - s0 = s1; - s1 = temp; + (s1, s0) = (s0, s1); } if (y0 > y1) { - float temp = t0; - t0 = t1; - t1 = temp; + (t1, t0) = (t0, t1); } GL.Uniform1(_uniformSrcX0Location, s0); diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs index 16678bb7b..5daaf8c45 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects } } - public FsrScalingFilter(OpenGLRenderer renderer, IPostProcessingEffect filter) + public FsrScalingFilter(OpenGLRenderer renderer) { Initialize(); @@ -174,4 +174,4 @@ namespace Ryujinx.Graphics.OpenGL.Effects GL.ActiveTexture((TextureUnit)previousUnit); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs index 3a2d685b7..c8f170846 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs @@ -78,4 +78,4 @@ namespace Ryujinx.Graphics.OpenGL.Effects return textureView; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs index 7a045a021..154e74921 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs @@ -3,9 +3,9 @@ using System; namespace Ryujinx.Graphics.OpenGL.Effects { - internal interface IPostProcessingEffect : IDisposable + internal interface IPostProcessingEffect : IDisposable { const int LocalGroupSize = 64; TextureView Run(TextureView view, int width, int height); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs b/src/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs index e1e1b2c1d..b5c80878a 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs @@ -15,4 +15,4 @@ namespace Ryujinx.Graphics.OpenGL.Effects Extents2D source, Extents2D destination); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs b/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs index 72c5a98f5..976b3410b 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs @@ -1,5 +1,4 @@ using OpenTK.Graphics.OpenGL; -using System; namespace Ryujinx.Graphics.OpenGL.Effects { diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs index 1ad300c88..eede852f7 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa private int[] _neighbourShaderPrograms; private TextureStorage _edgeOutputTexture; private TextureStorage _blendOutputTexture; - private string[] _qualities; + private readonly string[] _qualities; private int _inputUniform; private int _outputUniform; private int _samplerAreaUniform; diff --git a/src/Ryujinx.Graphics.OpenGL/FormatInfo.cs b/src/Ryujinx.Graphics.OpenGL/FormatInfo.cs index aef4dbad3..c1a97e81a 100644 --- a/src/Ryujinx.Graphics.OpenGL/FormatInfo.cs +++ b/src/Ryujinx.Graphics.OpenGL/FormatInfo.cs @@ -4,42 +4,42 @@ namespace Ryujinx.Graphics.OpenGL { readonly struct FormatInfo { - public int Components { get; } + public int Components { get; } public bool Normalized { get; } - public bool Scaled { get; } + public bool Scaled { get; } public PixelInternalFormat PixelInternalFormat { get; } - public PixelFormat PixelFormat { get; } - public PixelType PixelType { get; } + public PixelFormat PixelFormat { get; } + public PixelType PixelType { get; } public bool IsCompressed { get; } public FormatInfo( - int components, - bool normalized, - bool scaled, - All pixelInternalFormat, + int components, + bool normalized, + bool scaled, + All pixelInternalFormat, PixelFormat pixelFormat, - PixelType pixelType) + PixelType pixelType) { - Components = components; - Normalized = normalized; - Scaled = scaled; + Components = components; + Normalized = normalized; + Scaled = scaled; PixelInternalFormat = (PixelInternalFormat)pixelInternalFormat; - PixelFormat = pixelFormat; - PixelType = pixelType; - IsCompressed = false; + PixelFormat = pixelFormat; + PixelType = pixelType; + IsCompressed = false; } public FormatInfo(int components, bool normalized, bool scaled, All pixelFormat) { - Components = components; - Normalized = normalized; - Scaled = scaled; + Components = components; + Normalized = normalized; + Scaled = scaled; PixelInternalFormat = 0; - PixelFormat = (PixelFormat)pixelFormat; - PixelType = 0; - IsCompressed = true; + PixelFormat = (PixelFormat)pixelFormat; + PixelType = 0; + IsCompressed = true; } } } diff --git a/src/Ryujinx.Graphics.OpenGL/FormatTable.cs b/src/Ryujinx.Graphics.OpenGL/FormatTable.cs index 281f0004f..3dac33b94 100644 --- a/src/Ryujinx.Graphics.OpenGL/FormatTable.cs +++ b/src/Ryujinx.Graphics.OpenGL/FormatTable.cs @@ -4,10 +4,10 @@ using System; namespace Ryujinx.Graphics.OpenGL { - struct FormatTable + readonly struct FormatTable { - private static FormatInfo[] _table; - private static SizedInternalFormat[] _tableImage; + private static readonly FormatInfo[] _table; + private static readonly SizedInternalFormat[] _tableImage; static FormatTable() { @@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.OpenGL _table = new FormatInfo[tableSize]; _tableImage = new SizedInternalFormat[tableSize]; +#pragma warning disable IDE0055 // Disable formatting Add(Format.R8Unorm, new FormatInfo(1, true, false, All.R8, PixelFormat.Red, PixelType.UnsignedByte)); Add(Format.R8Snorm, new FormatInfo(1, true, false, All.R8Snorm, PixelFormat.Red, PixelType.Byte)); Add(Format.R8Uint, new FormatInfo(1, false, false, All.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte)); @@ -200,6 +201,7 @@ namespace Ryujinx.Graphics.OpenGL Add(Format.R10G10B10A2Unorm, (SizedInternalFormat)All.Rgb10A2); Add(Format.R10G10B10A2Uint, (SizedInternalFormat)All.Rgb10A2ui); Add(Format.R11G11B10Float, (SizedInternalFormat)All.R11fG11fB10f); +#pragma warning restore IDE0055 } private static void Add(Format format, FormatInfo info) diff --git a/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs index b180b8578..3b79c5d6b 100644 --- a/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.OpenGL _colorsCount = colorsCount; } - private void SetDrawBuffersImpl(int colorsCount) + private static void SetDrawBuffersImpl(int colorsCount) { DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount]; diff --git a/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index dd0b1501f..d7d993459 100644 --- a/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -5,30 +5,30 @@ namespace Ryujinx.Graphics.OpenGL { static class HwCapabilities { - private static readonly Lazy<bool> _supportsAlphaToCoverageDitherControl = new Lazy<bool>(() => HasExtension("GL_NV_alpha_to_coverage_dither_control")); - private static readonly Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); - private static readonly Lazy<bool> _supportsBlendEquationAdvanced = new Lazy<bool>(() => HasExtension("GL_NV_blend_equation_advanced")); - private static readonly Lazy<bool> _supportsDrawTexture = new Lazy<bool>(() => HasExtension("GL_NV_draw_texture")); - private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new Lazy<bool>(() => HasExtension("GL_ARB_fragment_shader_interlock")); - private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new Lazy<bool>(() => HasExtension("GL_INTEL_fragment_shader_ordering")); - private static readonly Lazy<bool> _supportsGeometryShaderPassthrough = new Lazy<bool>(() => HasExtension("GL_NV_geometry_shader_passthrough")); - private static readonly Lazy<bool> _supportsImageLoadFormatted = new Lazy<bool>(() => HasExtension("GL_EXT_shader_image_load_formatted")); - private static readonly Lazy<bool> _supportsIndirectParameters = new Lazy<bool>(() => HasExtension("GL_ARB_indirect_parameters")); - private static readonly Lazy<bool> _supportsParallelShaderCompile = new Lazy<bool>(() => HasExtension("GL_ARB_parallel_shader_compile")); - private static readonly Lazy<bool> _supportsPolygonOffsetClamp = new Lazy<bool>(() => HasExtension("GL_EXT_polygon_offset_clamp")); - private static readonly Lazy<bool> _supportsQuads = new Lazy<bool>(SupportsQuadsCheck); - private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture")); - private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot")); - private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new Lazy<bool>(() => HasExtension("GL_ARB_shader_viewport_layer_array")); - private static readonly Lazy<bool> _supportsViewportArray2 = new Lazy<bool>(() => HasExtension("GL_NV_viewport_array2")); - private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc")); - private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc")); - private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc")); - private static readonly Lazy<bool> _supportsTextureShadowLod = new Lazy<bool>(() => HasExtension("GL_EXT_texture_shadow_lod")); - private static readonly Lazy<bool> _supportsViewportSwizzle = new Lazy<bool>(() => HasExtension("GL_NV_viewport_swizzle")); + private static readonly Lazy<bool> _supportsAlphaToCoverageDitherControl = new(() => HasExtension("GL_NV_alpha_to_coverage_dither_control")); + private static readonly Lazy<bool> _supportsAstcCompression = new(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); + private static readonly Lazy<bool> _supportsBlendEquationAdvanced = new(() => HasExtension("GL_NV_blend_equation_advanced")); + private static readonly Lazy<bool> _supportsDrawTexture = new(() => HasExtension("GL_NV_draw_texture")); + private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new(() => HasExtension("GL_ARB_fragment_shader_interlock")); + private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new(() => HasExtension("GL_INTEL_fragment_shader_ordering")); + private static readonly Lazy<bool> _supportsGeometryShaderPassthrough = new(() => HasExtension("GL_NV_geometry_shader_passthrough")); + private static readonly Lazy<bool> _supportsImageLoadFormatted = new(() => HasExtension("GL_EXT_shader_image_load_formatted")); + private static readonly Lazy<bool> _supportsIndirectParameters = new(() => HasExtension("GL_ARB_indirect_parameters")); + private static readonly Lazy<bool> _supportsParallelShaderCompile = new(() => HasExtension("GL_ARB_parallel_shader_compile")); + private static readonly Lazy<bool> _supportsPolygonOffsetClamp = new(() => HasExtension("GL_EXT_polygon_offset_clamp")); + private static readonly Lazy<bool> _supportsQuads = new(SupportsQuadsCheck); + private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new(() => HasExtension("GL_ARB_seamless_cubemap_per_texture")); + private static readonly Lazy<bool> _supportsShaderBallot = new(() => HasExtension("GL_ARB_shader_ballot")); + private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new(() => HasExtension("GL_ARB_shader_viewport_layer_array")); + private static readonly Lazy<bool> _supportsViewportArray2 = new(() => HasExtension("GL_NV_viewport_array2")); + private static readonly Lazy<bool> _supportsTextureCompressionBptc = new(() => HasExtension("GL_EXT_texture_compression_bptc")); + private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new(() => HasExtension("GL_EXT_texture_compression_rgtc")); + private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new(() => HasExtension("GL_EXT_texture_compression_s3tc")); + private static readonly Lazy<bool> _supportsTextureShadowLod = new(() => HasExtension("GL_EXT_texture_shadow_lod")); + private static readonly Lazy<bool> _supportsViewportSwizzle = new(() => HasExtension("GL_NV_viewport_swizzle")); - private static readonly Lazy<int> _maximumComputeSharedMemorySize = new Lazy<int>(() => GetLimit(All.MaxComputeSharedMemorySize)); - private static readonly Lazy<int> _storageBufferOffsetAlignment = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment)); + private static readonly Lazy<int> _maximumComputeSharedMemorySize = new(() => GetLimit(All.MaxComputeSharedMemorySize)); + private static readonly Lazy<int> _storageBufferOffsetAlignment = new(() => GetLimit(All.ShaderStorageBufferOffsetAlignment)); public enum GpuVendor { @@ -40,45 +40,44 @@ namespace Ryujinx.Graphics.OpenGL Nvidia } - private static readonly Lazy<GpuVendor> _gpuVendor = new Lazy<GpuVendor>(GetGpuVendor); + private static readonly Lazy<GpuVendor> _gpuVendor = new(GetGpuVendor); - private static bool _isAMD => _gpuVendor.Value == GpuVendor.AmdWindows || _gpuVendor.Value == GpuVendor.AmdUnix; - private static bool _isIntel => _gpuVendor.Value == GpuVendor.IntelWindows || _gpuVendor.Value == GpuVendor.IntelUnix; + private static bool IsIntel => _gpuVendor.Value == GpuVendor.IntelWindows || _gpuVendor.Value == GpuVendor.IntelUnix; public static GpuVendor Vendor => _gpuVendor.Value; - private static Lazy<float> _maxSupportedAnisotropy = new Lazy<float>(GL.GetFloat((GetPName)All.MaxTextureMaxAnisotropy)); + private static readonly Lazy<float> _maxSupportedAnisotropy = new(GL.GetFloat((GetPName)All.MaxTextureMaxAnisotropy)); - public static bool UsePersistentBufferForFlush => _gpuVendor.Value == GpuVendor.AmdWindows || _gpuVendor.Value == GpuVendor.Nvidia; + public static bool UsePersistentBufferForFlush => _gpuVendor.Value == GpuVendor.AmdWindows || _gpuVendor.Value == GpuVendor.Nvidia; public static bool SupportsAlphaToCoverageDitherControl => _supportsAlphaToCoverageDitherControl.Value; - public static bool SupportsAstcCompression => _supportsAstcCompression.Value; - public static bool SupportsBlendEquationAdvanced => _supportsBlendEquationAdvanced.Value; - public static bool SupportsDrawTexture => _supportsDrawTexture.Value; - public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value; - public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value; - public static bool SupportsGeometryShaderPassthrough => _supportsGeometryShaderPassthrough.Value; - public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value; - public static bool SupportsIndirectParameters => _supportsIndirectParameters.Value; - public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value; - public static bool SupportsPolygonOffsetClamp => _supportsPolygonOffsetClamp.Value; - public static bool SupportsQuads => _supportsQuads.Value; - public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value; - public static bool SupportsShaderBallot => _supportsShaderBallot.Value; - public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value; - public static bool SupportsViewportArray2 => _supportsViewportArray2.Value; - public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value; - public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value; - public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value; - public static bool SupportsTextureShadowLod => _supportsTextureShadowLod.Value; - public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value; + public static bool SupportsAstcCompression => _supportsAstcCompression.Value; + public static bool SupportsBlendEquationAdvanced => _supportsBlendEquationAdvanced.Value; + public static bool SupportsDrawTexture => _supportsDrawTexture.Value; + public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value; + public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value; + public static bool SupportsGeometryShaderPassthrough => _supportsGeometryShaderPassthrough.Value; + public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value; + public static bool SupportsIndirectParameters => _supportsIndirectParameters.Value; + public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value; + public static bool SupportsPolygonOffsetClamp => _supportsPolygonOffsetClamp.Value; + public static bool SupportsQuads => _supportsQuads.Value; + public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value; + public static bool SupportsShaderBallot => _supportsShaderBallot.Value; + public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value; + public static bool SupportsViewportArray2 => _supportsViewportArray2.Value; + public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value; + public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value; + public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value; + public static bool SupportsTextureShadowLod => _supportsTextureShadowLod.Value; + public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value; - public static bool SupportsMismatchingViewFormat => _gpuVendor.Value != GpuVendor.AmdWindows && _gpuVendor.Value != GpuVendor.IntelWindows; + public static bool SupportsMismatchingViewFormat => _gpuVendor.Value != GpuVendor.AmdWindows && _gpuVendor.Value != GpuVendor.IntelWindows; public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia; - public static bool RequiresSyncFlush => _gpuVendor.Value == GpuVendor.AmdWindows || _isIntel; + public static bool RequiresSyncFlush => _gpuVendor.Value == GpuVendor.AmdWindows || IsIntel; public static int MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value; - public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value; + public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value; public static float MaximumSupportedAnisotropy => _maxSupportedAnisotropy.Value; @@ -139,4 +138,4 @@ namespace Ryujinx.Graphics.OpenGL return GL.GetError() == ErrorCode.NoError; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs b/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs index 4f167e893..1a08f973a 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs @@ -100,4 +100,4 @@ namespace Ryujinx.Graphics.OpenGL.Image _entries.Clear(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs b/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs index f705aa3e7..18dd1b72a 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs @@ -39,8 +39,8 @@ namespace Ryujinx.Graphics.OpenGL.Image GL.SamplerParameter(Handle, SamplerParameterName.TextureBorderColor, borderColor); } - GL.SamplerParameter(Handle, SamplerParameterName.TextureMinLod, info.MinLod); - GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxLod, info.MaxLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMinLod, info.MinLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxLod, info.MaxLod); GL.SamplerParameter(Handle, SamplerParameterName.TextureLodBias, info.MipLodBias); GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxAnisotropyExt, info.MaxAnisotropy); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index 116f70cc7..c177ae9c6 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { class TextureBuffer : TextureBase, ITexture { - private OpenGLRenderer _renderer; + private readonly OpenGLRenderer _renderer; private int _bufferOffset; private int _bufferSize; private int _bufferCount; @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { var dataSpan = data.AsSpan(); - Buffer.SetData(_buffer, _bufferOffset, dataSpan.Slice(0, Math.Min(dataSpan.Length, _bufferSize))); + Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]); } public void SetData(SpanOrArray<byte> data, int layer, int level) diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index a4b08787b..bb1911e8b 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -26,13 +26,13 @@ namespace Ryujinx.Graphics.OpenGL.Image public void Copy( TextureView src, TextureView dst, - Extents2D srcRegion, - Extents2D dstRegion, - bool linearFilter, - int srcLayer = 0, - int dstLayer = 0, - int srcLevel = 0, - int dstLevel = 0) + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter, + int srcLayer = 0, + int dstLayer = 0, + int srcLevel = 0, + int dstLevel = 0) { int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel); int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer); @@ -43,15 +43,15 @@ namespace Ryujinx.Graphics.OpenGL.Image public void Copy( TextureView src, TextureView dst, - Extents2D srcRegion, - Extents2D dstRegion, - bool linearFilter, - int srcLayer, - int dstLayer, - int srcLevel, - int dstLevel, - int layers, - int levels) + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel, + int layers, + int levels) { TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src; diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs index c8fbfbc6a..6b5fb376b 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs @@ -1,5 +1,4 @@ using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.GAL; using System; using System.Collections.Generic; using System.Globalization; @@ -81,9 +80,6 @@ void main() public void CopyIncompatibleFormats(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int depth, int levels) { - TextureCreateInfo srcInfo = src.Info; - TextureCreateInfo dstInfo = dst.Info; - int srcBpp = src.Info.BytesPerPixel; int dstBpp = dst.Info.BytesPerPixel; @@ -176,7 +172,7 @@ void main() return GetShader(ComputeShaderWidening, _wideningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount); } - private int GetShader( + private static int GetShader( string code, Dictionary<int, int> programHandles, int componentSize, diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs index 9963dc661..39f232723 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs @@ -94,8 +94,8 @@ void main() }"; private readonly OpenGLRenderer _renderer; - private int[] _msToNonMSProgramHandles; - private int[] _nonMSToMSProgramHandles; + private readonly int[] _msToNonMSProgramHandles; + private readonly int[] _nonMSToMSProgramHandles; public TextureCopyMS(OpenGLRenderer renderer) { @@ -219,7 +219,7 @@ void main() return GetShader(ComputeShaderNonMSToMS, _nonMSToMSProgramHandles, bytesPerPixel); } - private int GetShader(string code, int[] programHandles, int bytesPerPixel) + private static int GetShader(string code, int[] programHandles, int bytesPerPixel) { int index = BitOperations.Log2((uint)bytesPerPixel); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs index c058ac885..d714caf38 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.OpenGL.Image public TextureStorage(OpenGLRenderer renderer, TextureCreateInfo info, float scaleFactor) { _renderer = renderer; - Info = info; + Info = info; Handle = GL.GenTexture(); ScaleFactor = scaleFactor; diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 90a2936d0..21d8e449c 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -88,9 +88,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { // Swap B <-> R for BGRA formats, as OpenGL has no support for them // and we need to manually swap the components on read/write on the GPU. - int temp = swizzleRgba[0]; - swizzleRgba[0] = swizzleRgba[2]; - swizzleRgba[2] = temp; + (swizzleRgba[2], swizzleRgba[0]) = (swizzleRgba[0], swizzleRgba[2]); } GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); @@ -186,8 +184,8 @@ namespace Ryujinx.Graphics.OpenGL.Image // This approach uses blit, which causes a resolution loss since some samples will be lost // in the process. - Extents2D srcRegion = new Extents2D(0, 0, Width, Height); - Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); + Extents2D srcRegion = new(0, 0, Width, Height); + Extents2D dstRegion = new(0, 0, destinationView.Width, destinationView.Height); if (destinationView.Target.IsMultisample()) { diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index c7c01a37b..b4c567a81 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -21,13 +21,13 @@ namespace Ryujinx.Graphics.OpenGL public IWindow Window => _window; - private TextureCopy _textureCopy; - private TextureCopy _backgroundTextureCopy; + private readonly TextureCopy _textureCopy; + private readonly TextureCopy _backgroundTextureCopy; internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy; internal TextureCopyIncompatible TextureCopyIncompatible { get; } internal TextureCopyMS TextureCopyMS { get; } - private Sync _sync; + private readonly Sync _sync; public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; @@ -222,9 +222,9 @@ namespace Ryujinx.Graphics.OpenGL private void PrintGpuInformation() { - GpuVendor = GL.GetString(StringName.Vendor); + GpuVendor = GL.GetString(StringName.Vendor); GpuRenderer = GL.GetString(StringName.Renderer); - GpuVersion = GL.GetString(StringName.Version); + GpuVersion = GL.GetString(StringName.Version); Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); } diff --git a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs index aac288b7e..ebfe3ad64 100644 --- a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs +++ b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs @@ -11,10 +11,10 @@ namespace Ryujinx.Graphics.OpenGL { class PersistentBuffers : IDisposable { - private PersistentBuffer _main = new PersistentBuffer(); - private PersistentBuffer _background = new PersistentBuffer(); + private readonly PersistentBuffer _main = new(); + private readonly PersistentBuffer _background = new(); - private Dictionary<BufferHandle, IntPtr> _maps = new Dictionary<BufferHandle, IntPtr>(); + private readonly Dictionary<BufferHandle, IntPtr> _maps = new(); public PersistentBuffer Default => BackgroundContextWorker.InBackground ? _background : _main; @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.OpenGL return _dataMap; } - private void Sync() + private static void Sync() { GL.MemoryBarrier(MemoryBarrierFlags.ClientMappedBufferBarrierBit); @@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.OpenGL Sync(); - return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size).Slice(offset); + return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size)[offset..]; } public unsafe ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 6b6d0289c..df618d5bc 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -44,11 +44,11 @@ namespace Ryujinx.Graphics.OpenGL private CounterQueueEvent _activeConditionalRender; - private Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount]; - private Vector4<float>[] _renderScale = new Vector4<float>[73]; + private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount]; + private readonly Vector4<float>[] _renderScale = new Vector4<float>[73]; private int _fragmentScaleCount; - private (TextureBase, Format)[] _images; + private readonly (TextureBase, Format)[] _images; private TextureBase _unit0Texture; private Sampler _unit0Sampler; @@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.OpenGL PostDraw(); } - private void DrawQuadsImpl( + private static void DrawQuadsImpl( int vertexCount, int instanceCount, int firstVertex, @@ -285,7 +285,7 @@ namespace Ryujinx.Graphics.OpenGL quadsCount); } - private void DrawQuadStripImpl( + private static void DrawQuadStripImpl( int vertexCount, int instanceCount, int firstVertex, @@ -366,8 +366,12 @@ namespace Ryujinx.Graphics.OpenGL switch (_elementsType) { - case DrawElementsType.UnsignedShort: indexElemSize = 2; break; - case DrawElementsType.UnsignedInt: indexElemSize = 4; break; + case DrawElementsType.UnsignedShort: + indexElemSize = 2; + break; + case DrawElementsType.UnsignedInt: + indexElemSize = 4; + break; } IntPtr indexBaseOffset = _indexBaseOffset + firstIndex * indexElemSize; @@ -1471,7 +1475,7 @@ namespace Ryujinx.Graphics.OpenGL GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit); } - private void SetBuffers(ReadOnlySpan<BufferAssignment> buffers, bool isStorage) + private static void SetBuffers(ReadOnlySpan<BufferAssignment> buffers, bool isStorage) { BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer; @@ -1701,11 +1705,9 @@ namespace Ryujinx.Graphics.OpenGL public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) { - if (value is CounterQueueEvent) + // Compare an event and a constant value. + if (value is CounterQueueEvent evt) { - // Compare an event and a constant value. - CounterQueueEvent evt = (CounterQueueEvent)value; - // Easy host conditional rendering when the check matches what GL can do: // - Event is of type samples passed. // - Result is not a combination of multiple queries. diff --git a/src/Ryujinx.Graphics.OpenGL/Program.cs b/src/Ryujinx.Graphics.OpenGL/Program.cs index a6009108a..cc9120c7c 100644 --- a/src/Ryujinx.Graphics.OpenGL/Program.cs +++ b/src/Ryujinx.Graphics.OpenGL/Program.cs @@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.OpenGL if (log.Length > MaxShaderLogLength) { - log = log.Substring(0, MaxShaderLogLength) + "..."; + log = log[..MaxShaderLogLength] + "..."; } Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{log}"); diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs index 9d43f6c3f..1470f5aaa 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs @@ -14,9 +14,9 @@ namespace Ryujinx.Graphics.OpenGL.Queries public int Query { get; } - private int _buffer; - private IntPtr _bufferMap; - private QueryTarget _type; + private readonly int _buffer; + private readonly IntPtr _bufferMap; + private readonly QueryTarget _type; public BufferedQuery(QueryTarget type) { @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } - private bool WaitingForValue(long data) + private static bool WaitingForValue(long data) { return data == DefaultValue || ((ulong)data & HighMask) == (unchecked((ulong)DefaultValue) & HighMask); diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index 648600bc2..ff7e9a3ee 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries private readonly Pipeline _pipeline; - private Queue<CounterQueueEvent> _events = new Queue<CounterQueueEvent>(); + private readonly Queue<CounterQueueEvent> _events = new(); private CounterQueueEvent _current; private ulong _accumulatedCounter; @@ -23,12 +23,12 @@ namespace Ryujinx.Graphics.OpenGL.Queries private readonly object _lock = new(); - private Queue<BufferedQuery> _queryPool; - private AutoResetEvent _queuedEvent = new AutoResetEvent(false); - private AutoResetEvent _wakeSignal = new AutoResetEvent(false); - private AutoResetEvent _eventConsumed = new AutoResetEvent(false); + private readonly Queue<BufferedQuery> _queryPool; + private readonly AutoResetEvent _queuedEvent = new(false); + private readonly AutoResetEvent _wakeSignal = new(false); + private readonly AutoResetEvent _eventConsumed = new(false); - private Thread _consumerThread; + private readonly Thread _consumerThread; internal CounterQueue(Pipeline pipeline, CounterType type) { @@ -148,14 +148,13 @@ namespace Ryujinx.Graphics.OpenGL.Queries private static QueryTarget GetTarget(CounterType type) { - switch (type) + return type switch { - case CounterType.SamplesPassed: return QueryTarget.SamplesPassed; - case CounterType.PrimitivesGenerated: return QueryTarget.PrimitivesGenerated; - case CounterType.TransformFeedbackPrimitivesWritten: return QueryTarget.TransformFeedbackPrimitivesWritten; - } - - return QueryTarget.SamplesPassed; + CounterType.SamplesPassed => QueryTarget.SamplesPassed, + CounterType.PrimitivesGenerated => QueryTarget.PrimitivesGenerated, + CounterType.TransformFeedbackPrimitivesWritten => QueryTarget.TransformFeedbackPrimitivesWritten, + _ => QueryTarget.SamplesPassed, + }; } public void Flush(bool blocking) diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs index ffe1f774f..7d2249068 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs @@ -18,8 +18,8 @@ namespace Ryujinx.Graphics.OpenGL.Queries public ulong DrawIndex { get; } - private CounterQueue _queue; - private BufferedQuery _counter; + private readonly CounterQueue _queue; + private readonly BufferedQuery _counter; private bool _hostAccessReserved = false; private int _refCount = 1; // Starts with a reference from the counter queue. diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index e6c7ab2bd..91554ebad 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries { class Counters : IDisposable { - private CounterQueue[] _counterQueues; + private readonly CounterQueue[] _counterQueues; public Counters() { @@ -54,4 +54,4 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs index 69d2a78e1..bba5c5cb7 100644 --- a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs +++ b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.OpenGL private const int DisposedLiveFrames = 2; private readonly object _lock = new(); - private readonly Dictionary<TextureCreateInfo, List<DisposedTexture>> _textures = new Dictionary<TextureCreateInfo, List<DisposedTexture>>(); + private readonly Dictionary<TextureCreateInfo, List<DisposedTexture>> _textures = new(); /// <summary> /// Add a texture that is not being used anymore to the resource pool to be used later. @@ -32,8 +32,7 @@ namespace Ryujinx.Graphics.OpenGL { lock (_lock) { - List<DisposedTexture> list; - if (!_textures.TryGetValue(view.Info, out list)) + if (!_textures.TryGetValue(view.Info, out List<DisposedTexture> list)) { list = new List<DisposedTexture>(); _textures.Add(view.Info, list); @@ -59,8 +58,7 @@ namespace Ryujinx.Graphics.OpenGL { lock (_lock) { - List<DisposedTexture> list; - if (!_textures.TryGetValue(info, out list)) + if (!_textures.TryGetValue(info, out List<DisposedTexture> list)) { return null; } diff --git a/src/Ryujinx.Graphics.OpenGL/Sync.cs b/src/Ryujinx.Graphics.OpenGL/Sync.cs index 58818e6a7..54e9c6d3f 100644 --- a/src/Ryujinx.Graphics.OpenGL/Sync.cs +++ b/src/Ryujinx.Graphics.OpenGL/Sync.cs @@ -15,13 +15,13 @@ namespace Ryujinx.Graphics.OpenGL } private ulong _firstHandle = 0; - private ClientWaitSyncFlags _syncFlags => HwCapabilities.RequiresSyncFlush ? ClientWaitSyncFlags.None : ClientWaitSyncFlags.SyncFlushCommandsBit; + private static ClientWaitSyncFlags SyncFlags => HwCapabilities.RequiresSyncFlush ? ClientWaitSyncFlags.None : ClientWaitSyncFlags.SyncFlushCommandsBit; - private List<SyncHandle> _handles = new List<SyncHandle>(); + private readonly List<SyncHandle> _handles = new(); public void Create(ulong id) { - SyncHandle handle = new SyncHandle + SyncHandle handle = new() { ID = id, Handle = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None) @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.OpenGL if (handle.ID > lastHandle) { - WaitSyncStatus syncResult = GL.ClientWaitSync(handle.Handle, _syncFlags, 0); + WaitSyncStatus syncResult = GL.ClientWaitSync(handle.Handle, SyncFlags, 0); if (syncResult == WaitSyncStatus.AlreadySignaled) { @@ -101,8 +101,8 @@ namespace Ryujinx.Graphics.OpenGL return; } - WaitSyncStatus syncResult = GL.ClientWaitSync(result.Handle, _syncFlags, 1000000000); - + WaitSyncStatus syncResult = GL.ClientWaitSync(result.Handle, SyncFlags, 1000000000); + if (syncResult == WaitSyncStatus.TimeoutExpired) { Logger.Error?.PrintMsg(LogClass.Gpu, $"GL Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); @@ -123,9 +123,12 @@ namespace Ryujinx.Graphics.OpenGL first = _handles.FirstOrDefault(); } - if (first == null) break; + if (first == null) + { + break; + } - WaitSyncStatus syncResult = GL.ClientWaitSync(first.Handle, _syncFlags, 0); + WaitSyncStatus syncResult = GL.ClientWaitSync(first.Handle, SyncFlags, 0); if (syncResult == WaitSyncStatus.AlreadySignaled) { @@ -140,7 +143,8 @@ namespace Ryujinx.Graphics.OpenGL first.Handle = IntPtr.Zero; } } - } else + } + else { // This sync handle and any following have not been reached yet. break; diff --git a/src/Ryujinx.Graphics.OpenGL/VertexArray.cs b/src/Ryujinx.Graphics.OpenGL/VertexArray.cs index 7d22033ec..32211e783 100644 --- a/src/Ryujinx.Graphics.OpenGL/VertexArray.cs +++ b/src/Ryujinx.Graphics.OpenGL/VertexArray.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.OpenGL private uint _vertexBuffersLimited; private BufferRange _indexBuffer; - private BufferHandle _tempIndexBuffer; + private readonly BufferHandle _tempIndexBuffer; private BufferHandle _tempVertexBuffer; private int _tempVertexBufferSize; @@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.OpenGL } int offset = attrib.Offset; - int size = fmtInfo.Components; + int size = fmtInfo.Components; bool isFloat = fmtInfo.PixelType == PixelType.Float || fmtInfo.PixelType == PixelType.HalfFloat; diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index cc9836e06..a928772fd 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -375,7 +375,7 @@ namespace Ryujinx.Graphics.OpenGL if (_scalingFilter is not FsrScalingFilter) { _scalingFilter?.Dispose(); - _scalingFilter = new FsrScalingFilter(_renderer, _antiAliasing); + _scalingFilter = new FsrScalingFilter(_renderer); } _isLinear = false; _scalingFilter.Level = _scalingFilterLevel; @@ -417,4 +417,4 @@ namespace Ryujinx.Graphics.OpenGL _updateScalingFilter = true; } } -} \ No newline at end of file +} From 46b7c905f5dbb8c7e527281e22d5d2fc36bc666f Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:23:00 +0200 Subject: [PATCH 678/737] [Ryujinx.Input] Address dotnet-format issues (#5384) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Address dotnet format CA1816 warnings * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Remove redundant code, convert to auto-properties and fix naming rule violations * Remove bogus change * Address review feedback --- .../Assigner/GamepadButtonAssigner.cs | 15 +- src/Ryujinx.Input/Assigner/IButtonAssigner.cs | 2 +- .../Assigner/KeyboardKeyAssigner.cs | 4 +- src/Ryujinx.Input/GamepadButtonInputId.cs | 2 +- src/Ryujinx.Input/GamepadFeaturesFlag.cs | 2 +- src/Ryujinx.Input/HLE/InputManager.cs | 3 +- src/Ryujinx.Input/HLE/NpadController.cs | 352 +++++++++--------- src/Ryujinx.Input/HLE/NpadManager.cs | 33 +- src/Ryujinx.Input/HLE/TouchScreenManager.cs | 15 +- src/Ryujinx.Input/IMouse.cs | 4 +- src/Ryujinx.Input/Key.cs | 4 +- src/Ryujinx.Input/KeyboardStateSnapshot.cs | 2 +- src/Ryujinx.Input/Motion/CemuHook/Client.cs | 157 ++++---- .../CemuHook/Protocol/ControllerData.cs | 22 +- .../CemuHook/Protocol/ControllerInfo.cs | 4 +- .../Motion/CemuHook/Protocol/Header.cs | 2 +- .../Motion/CemuHook/Protocol/MessageType.cs | 4 +- .../CemuHook/Protocol/SharedResponse.cs | 18 +- src/Ryujinx.Input/Motion/MotionInput.cs | 14 +- .../Motion/MotionSensorFilter.cs | 14 +- src/Ryujinx.Input/MotionInputId.cs | 2 +- src/Ryujinx.Input/MouseButton.cs | 4 +- src/Ryujinx.Input/MouseStateSnapshot.cs | 6 +- src/Ryujinx.Input/StickInputId.cs | 2 +- 24 files changed, 344 insertions(+), 343 deletions(-) diff --git a/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs b/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs index 8621b3a52..388ebcc07 100644 --- a/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs +++ b/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs @@ -10,15 +10,15 @@ namespace Ryujinx.Input.Assigner /// </summary> public class GamepadButtonAssigner : IButtonAssigner { - private IGamepad _gamepad; + private readonly IGamepad _gamepad; private GamepadStateSnapshot _currState; private GamepadStateSnapshot _prevState; - private JoystickButtonDetector _detector; + private readonly JoystickButtonDetector _detector; - private bool _forStick; + private readonly bool _forStick; public GamepadButtonAssigner(IGamepad gamepad, float triggerThreshold, bool forStick) { @@ -35,7 +35,7 @@ namespace Ryujinx.Input.Assigner { _currState = _gamepad.GetStateSnapshot(); _prevState = _currState; - } + } } public void ReadInput() @@ -116,7 +116,7 @@ namespace Ryujinx.Input.Assigner private class JoystickButtonDetector { - private Dictionary<GamepadButtonInputId, InputSummary> _stats; + private readonly Dictionary<GamepadButtonInputId, InputSummary> _stats; public JoystickButtonDetector() { @@ -135,9 +135,8 @@ namespace Ryujinx.Input.Assigner public void AddInput(GamepadButtonInputId button, float value) { - InputSummary inputSummary; - if (!_stats.TryGetValue(button, out inputSummary)) + if (!_stats.TryGetValue(button, out InputSummary inputSummary)) { inputSummary = new InputSummary(); _stats.Add(button, inputSummary); @@ -148,7 +147,7 @@ namespace Ryujinx.Input.Assigner public override string ToString() { - StringWriter writer = new StringWriter(); + StringWriter writer = new(); foreach (var kvp in _stats) { diff --git a/src/Ryujinx.Input/Assigner/IButtonAssigner.cs b/src/Ryujinx.Input/Assigner/IButtonAssigner.cs index 021736df4..76a9fece4 100644 --- a/src/Ryujinx.Input/Assigner/IButtonAssigner.cs +++ b/src/Ryujinx.Input/Assigner/IButtonAssigner.cs @@ -33,4 +33,4 @@ namespace Ryujinx.Input.Assigner /// <returns>The pressed button that was read</returns> string GetPressedButton(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs index 23ae3655d..e52ef4a2c 100644 --- a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs +++ b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Input.Assigner /// </summary> public class KeyboardKeyAssigner : IButtonAssigner { - private IKeyboard _keyboard; + private readonly IKeyboard _keyboard; private KeyboardStateSnapshot _keyboardState; @@ -47,4 +47,4 @@ namespace Ryujinx.Input.Assigner return !ShouldCancel() ? keyPressed : ""; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/GamepadButtonInputId.cs b/src/Ryujinx.Input/GamepadButtonInputId.cs index d1e4b9ac0..618f8d0a0 100644 --- a/src/Ryujinx.Input/GamepadButtonInputId.cs +++ b/src/Ryujinx.Input/GamepadButtonInputId.cs @@ -52,6 +52,6 @@ SingleLeftTrigger1, SingleRightTrigger1, - Count + Count, } } diff --git a/src/Ryujinx.Input/GamepadFeaturesFlag.cs b/src/Ryujinx.Input/GamepadFeaturesFlag.cs index 87310a322..206b4ea11 100644 --- a/src/Ryujinx.Input/GamepadFeaturesFlag.cs +++ b/src/Ryujinx.Input/GamepadFeaturesFlag.cs @@ -23,6 +23,6 @@ namespace Ryujinx.Input /// Motion /// <remarks>Also named sixaxis</remarks> /// </summary> - Motion + Motion, } } diff --git a/src/Ryujinx.Input/HLE/InputManager.cs b/src/Ryujinx.Input/HLE/InputManager.cs index bc38cf5a2..6dba839b9 100644 --- a/src/Ryujinx.Input/HLE/InputManager.cs +++ b/src/Ryujinx.Input/HLE/InputManager.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Input.HLE { return new NpadManager(KeyboardDriver, GamepadDriver, MouseDriver); } - + public TouchScreenManager CreateTouchScreenManager() { if (MouseDriver == null) @@ -48,6 +48,7 @@ namespace Ryujinx.Input.HLE public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } } diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index 46c0fc33a..c193b45c2 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Concurrent; using System.Numerics; using System.Runtime.CompilerServices; - using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client; using ConfigControllerType = Ryujinx.Common.Configuration.Hid.ControllerType; @@ -28,29 +27,28 @@ namespace Ryujinx.Input.HLE } } - private static readonly HLEButtonMappingEntry[] _hleButtonMapping = new HLEButtonMappingEntry[] - { - new HLEButtonMappingEntry(GamepadButtonInputId.A, ControllerKeys.A), - new HLEButtonMappingEntry(GamepadButtonInputId.B, ControllerKeys.B), - new HLEButtonMappingEntry(GamepadButtonInputId.X, ControllerKeys.X), - new HLEButtonMappingEntry(GamepadButtonInputId.Y, ControllerKeys.Y), - new HLEButtonMappingEntry(GamepadButtonInputId.LeftStick, ControllerKeys.LStick), - new HLEButtonMappingEntry(GamepadButtonInputId.RightStick, ControllerKeys.RStick), - new HLEButtonMappingEntry(GamepadButtonInputId.LeftShoulder, ControllerKeys.L), - new HLEButtonMappingEntry(GamepadButtonInputId.RightShoulder, ControllerKeys.R), - new HLEButtonMappingEntry(GamepadButtonInputId.LeftTrigger, ControllerKeys.Zl), - new HLEButtonMappingEntry(GamepadButtonInputId.RightTrigger, ControllerKeys.Zr), - new HLEButtonMappingEntry(GamepadButtonInputId.DpadUp, ControllerKeys.DpadUp), - new HLEButtonMappingEntry(GamepadButtonInputId.DpadDown, ControllerKeys.DpadDown), - new HLEButtonMappingEntry(GamepadButtonInputId.DpadLeft, ControllerKeys.DpadLeft), - new HLEButtonMappingEntry(GamepadButtonInputId.DpadRight, ControllerKeys.DpadRight), - new HLEButtonMappingEntry(GamepadButtonInputId.Minus, ControllerKeys.Minus), - new HLEButtonMappingEntry(GamepadButtonInputId.Plus, ControllerKeys.Plus), + private static readonly HLEButtonMappingEntry[] _hleButtonMapping = { + new(GamepadButtonInputId.A, ControllerKeys.A), + new(GamepadButtonInputId.B, ControllerKeys.B), + new(GamepadButtonInputId.X, ControllerKeys.X), + new(GamepadButtonInputId.Y, ControllerKeys.Y), + new(GamepadButtonInputId.LeftStick, ControllerKeys.LStick), + new(GamepadButtonInputId.RightStick, ControllerKeys.RStick), + new(GamepadButtonInputId.LeftShoulder, ControllerKeys.L), + new(GamepadButtonInputId.RightShoulder, ControllerKeys.R), + new(GamepadButtonInputId.LeftTrigger, ControllerKeys.Zl), + new(GamepadButtonInputId.RightTrigger, ControllerKeys.Zr), + new(GamepadButtonInputId.DpadUp, ControllerKeys.DpadUp), + new(GamepadButtonInputId.DpadDown, ControllerKeys.DpadDown), + new(GamepadButtonInputId.DpadLeft, ControllerKeys.DpadLeft), + new(GamepadButtonInputId.DpadRight, ControllerKeys.DpadRight), + new(GamepadButtonInputId.Minus, ControllerKeys.Minus), + new(GamepadButtonInputId.Plus, ControllerKeys.Plus), - new HLEButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft), - new HLEButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft), - new HLEButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, ControllerKeys.SlRight), - new HLEButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight), + new(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft), + new(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft), + new(GamepadButtonInputId.SingleLeftTrigger1, ControllerKeys.SlRight), + new(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight), }; private class HLEKeyboardMappingEntry @@ -65,150 +63,147 @@ namespace Ryujinx.Input.HLE } } - private static readonly HLEKeyboardMappingEntry[] KeyMapping = new HLEKeyboardMappingEntry[] - { - new HLEKeyboardMappingEntry(Key.A, 0x4), - new HLEKeyboardMappingEntry(Key.B, 0x5), - new HLEKeyboardMappingEntry(Key.C, 0x6), - new HLEKeyboardMappingEntry(Key.D, 0x7), - new HLEKeyboardMappingEntry(Key.E, 0x8), - new HLEKeyboardMappingEntry(Key.F, 0x9), - new HLEKeyboardMappingEntry(Key.G, 0xA), - new HLEKeyboardMappingEntry(Key.H, 0xB), - new HLEKeyboardMappingEntry(Key.I, 0xC), - new HLEKeyboardMappingEntry(Key.J, 0xD), - new HLEKeyboardMappingEntry(Key.K, 0xE), - new HLEKeyboardMappingEntry(Key.L, 0xF), - new HLEKeyboardMappingEntry(Key.M, 0x10), - new HLEKeyboardMappingEntry(Key.N, 0x11), - new HLEKeyboardMappingEntry(Key.O, 0x12), - new HLEKeyboardMappingEntry(Key.P, 0x13), - new HLEKeyboardMappingEntry(Key.Q, 0x14), - new HLEKeyboardMappingEntry(Key.R, 0x15), - new HLEKeyboardMappingEntry(Key.S, 0x16), - new HLEKeyboardMappingEntry(Key.T, 0x17), - new HLEKeyboardMappingEntry(Key.U, 0x18), - new HLEKeyboardMappingEntry(Key.V, 0x19), - new HLEKeyboardMappingEntry(Key.W, 0x1A), - new HLEKeyboardMappingEntry(Key.X, 0x1B), - new HLEKeyboardMappingEntry(Key.Y, 0x1C), - new HLEKeyboardMappingEntry(Key.Z, 0x1D), + private static readonly HLEKeyboardMappingEntry[] _keyMapping = { + new(Key.A, 0x4), + new(Key.B, 0x5), + new(Key.C, 0x6), + new(Key.D, 0x7), + new(Key.E, 0x8), + new(Key.F, 0x9), + new(Key.G, 0xA), + new(Key.H, 0xB), + new(Key.I, 0xC), + new(Key.J, 0xD), + new(Key.K, 0xE), + new(Key.L, 0xF), + new(Key.M, 0x10), + new(Key.N, 0x11), + new(Key.O, 0x12), + new(Key.P, 0x13), + new(Key.Q, 0x14), + new(Key.R, 0x15), + new(Key.S, 0x16), + new(Key.T, 0x17), + new(Key.U, 0x18), + new(Key.V, 0x19), + new(Key.W, 0x1A), + new(Key.X, 0x1B), + new(Key.Y, 0x1C), + new(Key.Z, 0x1D), - new HLEKeyboardMappingEntry(Key.Number1, 0x1E), - new HLEKeyboardMappingEntry(Key.Number2, 0x1F), - new HLEKeyboardMappingEntry(Key.Number3, 0x20), - new HLEKeyboardMappingEntry(Key.Number4, 0x21), - new HLEKeyboardMappingEntry(Key.Number5, 0x22), - new HLEKeyboardMappingEntry(Key.Number6, 0x23), - new HLEKeyboardMappingEntry(Key.Number7, 0x24), - new HLEKeyboardMappingEntry(Key.Number8, 0x25), - new HLEKeyboardMappingEntry(Key.Number9, 0x26), - new HLEKeyboardMappingEntry(Key.Number0, 0x27), + new(Key.Number1, 0x1E), + new(Key.Number2, 0x1F), + new(Key.Number3, 0x20), + new(Key.Number4, 0x21), + new(Key.Number5, 0x22), + new(Key.Number6, 0x23), + new(Key.Number7, 0x24), + new(Key.Number8, 0x25), + new(Key.Number9, 0x26), + new(Key.Number0, 0x27), - new HLEKeyboardMappingEntry(Key.Enter, 0x28), - new HLEKeyboardMappingEntry(Key.Escape, 0x29), - new HLEKeyboardMappingEntry(Key.BackSpace, 0x2A), - new HLEKeyboardMappingEntry(Key.Tab, 0x2B), - new HLEKeyboardMappingEntry(Key.Space, 0x2C), - new HLEKeyboardMappingEntry(Key.Minus, 0x2D), - new HLEKeyboardMappingEntry(Key.Plus, 0x2E), - new HLEKeyboardMappingEntry(Key.BracketLeft, 0x2F), - new HLEKeyboardMappingEntry(Key.BracketRight, 0x30), - new HLEKeyboardMappingEntry(Key.BackSlash, 0x31), - new HLEKeyboardMappingEntry(Key.Tilde, 0x32), - new HLEKeyboardMappingEntry(Key.Semicolon, 0x33), - new HLEKeyboardMappingEntry(Key.Quote, 0x34), - new HLEKeyboardMappingEntry(Key.Grave, 0x35), - new HLEKeyboardMappingEntry(Key.Comma, 0x36), - new HLEKeyboardMappingEntry(Key.Period, 0x37), - new HLEKeyboardMappingEntry(Key.Slash, 0x38), - new HLEKeyboardMappingEntry(Key.CapsLock, 0x39), + new(Key.Enter, 0x28), + new(Key.Escape, 0x29), + new(Key.BackSpace, 0x2A), + new(Key.Tab, 0x2B), + new(Key.Space, 0x2C), + new(Key.Minus, 0x2D), + new(Key.Plus, 0x2E), + new(Key.BracketLeft, 0x2F), + new(Key.BracketRight, 0x30), + new(Key.BackSlash, 0x31), + new(Key.Tilde, 0x32), + new(Key.Semicolon, 0x33), + new(Key.Quote, 0x34), + new(Key.Grave, 0x35), + new(Key.Comma, 0x36), + new(Key.Period, 0x37), + new(Key.Slash, 0x38), + new(Key.CapsLock, 0x39), - new HLEKeyboardMappingEntry(Key.F1, 0x3a), - new HLEKeyboardMappingEntry(Key.F2, 0x3b), - new HLEKeyboardMappingEntry(Key.F3, 0x3c), - new HLEKeyboardMappingEntry(Key.F4, 0x3d), - new HLEKeyboardMappingEntry(Key.F5, 0x3e), - new HLEKeyboardMappingEntry(Key.F6, 0x3f), - new HLEKeyboardMappingEntry(Key.F7, 0x40), - new HLEKeyboardMappingEntry(Key.F8, 0x41), - new HLEKeyboardMappingEntry(Key.F9, 0x42), - new HLEKeyboardMappingEntry(Key.F10, 0x43), - new HLEKeyboardMappingEntry(Key.F11, 0x44), - new HLEKeyboardMappingEntry(Key.F12, 0x45), + new(Key.F1, 0x3a), + new(Key.F2, 0x3b), + new(Key.F3, 0x3c), + new(Key.F4, 0x3d), + new(Key.F5, 0x3e), + new(Key.F6, 0x3f), + new(Key.F7, 0x40), + new(Key.F8, 0x41), + new(Key.F9, 0x42), + new(Key.F10, 0x43), + new(Key.F11, 0x44), + new(Key.F12, 0x45), - new HLEKeyboardMappingEntry(Key.PrintScreen, 0x46), - new HLEKeyboardMappingEntry(Key.ScrollLock, 0x47), - new HLEKeyboardMappingEntry(Key.Pause, 0x48), - new HLEKeyboardMappingEntry(Key.Insert, 0x49), - new HLEKeyboardMappingEntry(Key.Home, 0x4A), - new HLEKeyboardMappingEntry(Key.PageUp, 0x4B), - new HLEKeyboardMappingEntry(Key.Delete, 0x4C), - new HLEKeyboardMappingEntry(Key.End, 0x4D), - new HLEKeyboardMappingEntry(Key.PageDown, 0x4E), - new HLEKeyboardMappingEntry(Key.Right, 0x4F), - new HLEKeyboardMappingEntry(Key.Left, 0x50), - new HLEKeyboardMappingEntry(Key.Down, 0x51), - new HLEKeyboardMappingEntry(Key.Up, 0x52), + new(Key.PrintScreen, 0x46), + new(Key.ScrollLock, 0x47), + new(Key.Pause, 0x48), + new(Key.Insert, 0x49), + new(Key.Home, 0x4A), + new(Key.PageUp, 0x4B), + new(Key.Delete, 0x4C), + new(Key.End, 0x4D), + new(Key.PageDown, 0x4E), + new(Key.Right, 0x4F), + new(Key.Left, 0x50), + new(Key.Down, 0x51), + new(Key.Up, 0x52), - new HLEKeyboardMappingEntry(Key.NumLock, 0x53), - new HLEKeyboardMappingEntry(Key.KeypadDivide, 0x54), - new HLEKeyboardMappingEntry(Key.KeypadMultiply, 0x55), - new HLEKeyboardMappingEntry(Key.KeypadSubtract, 0x56), - new HLEKeyboardMappingEntry(Key.KeypadAdd, 0x57), - new HLEKeyboardMappingEntry(Key.KeypadEnter, 0x58), - new HLEKeyboardMappingEntry(Key.Keypad1, 0x59), - new HLEKeyboardMappingEntry(Key.Keypad2, 0x5A), - new HLEKeyboardMappingEntry(Key.Keypad3, 0x5B), - new HLEKeyboardMappingEntry(Key.Keypad4, 0x5C), - new HLEKeyboardMappingEntry(Key.Keypad5, 0x5D), - new HLEKeyboardMappingEntry(Key.Keypad6, 0x5E), - new HLEKeyboardMappingEntry(Key.Keypad7, 0x5F), - new HLEKeyboardMappingEntry(Key.Keypad8, 0x60), - new HLEKeyboardMappingEntry(Key.Keypad9, 0x61), - new HLEKeyboardMappingEntry(Key.Keypad0, 0x62), - new HLEKeyboardMappingEntry(Key.KeypadDecimal, 0x63), + new(Key.NumLock, 0x53), + new(Key.KeypadDivide, 0x54), + new(Key.KeypadMultiply, 0x55), + new(Key.KeypadSubtract, 0x56), + new(Key.KeypadAdd, 0x57), + new(Key.KeypadEnter, 0x58), + new(Key.Keypad1, 0x59), + new(Key.Keypad2, 0x5A), + new(Key.Keypad3, 0x5B), + new(Key.Keypad4, 0x5C), + new(Key.Keypad5, 0x5D), + new(Key.Keypad6, 0x5E), + new(Key.Keypad7, 0x5F), + new(Key.Keypad8, 0x60), + new(Key.Keypad9, 0x61), + new(Key.Keypad0, 0x62), + new(Key.KeypadDecimal, 0x63), - new HLEKeyboardMappingEntry(Key.F13, 0x68), - new HLEKeyboardMappingEntry(Key.F14, 0x69), - new HLEKeyboardMappingEntry(Key.F15, 0x6A), - new HLEKeyboardMappingEntry(Key.F16, 0x6B), - new HLEKeyboardMappingEntry(Key.F17, 0x6C), - new HLEKeyboardMappingEntry(Key.F18, 0x6D), - new HLEKeyboardMappingEntry(Key.F19, 0x6E), - new HLEKeyboardMappingEntry(Key.F20, 0x6F), - new HLEKeyboardMappingEntry(Key.F21, 0x70), - new HLEKeyboardMappingEntry(Key.F22, 0x71), - new HLEKeyboardMappingEntry(Key.F23, 0x72), - new HLEKeyboardMappingEntry(Key.F24, 0x73), + new(Key.F13, 0x68), + new(Key.F14, 0x69), + new(Key.F15, 0x6A), + new(Key.F16, 0x6B), + new(Key.F17, 0x6C), + new(Key.F18, 0x6D), + new(Key.F19, 0x6E), + new(Key.F20, 0x6F), + new(Key.F21, 0x70), + new(Key.F22, 0x71), + new(Key.F23, 0x72), + new(Key.F24, 0x73), - new HLEKeyboardMappingEntry(Key.ControlLeft, 0xE0), - new HLEKeyboardMappingEntry(Key.ShiftLeft, 0xE1), - new HLEKeyboardMappingEntry(Key.AltLeft, 0xE2), - new HLEKeyboardMappingEntry(Key.WinLeft, 0xE3), - new HLEKeyboardMappingEntry(Key.ControlRight, 0xE4), - new HLEKeyboardMappingEntry(Key.ShiftRight, 0xE5), - new HLEKeyboardMappingEntry(Key.AltRight, 0xE6), - new HLEKeyboardMappingEntry(Key.WinRight, 0xE7), + new(Key.ControlLeft, 0xE0), + new(Key.ShiftLeft, 0xE1), + new(Key.AltLeft, 0xE2), + new(Key.WinLeft, 0xE3), + new(Key.ControlRight, 0xE4), + new(Key.ShiftRight, 0xE5), + new(Key.AltRight, 0xE6), + new(Key.WinRight, 0xE7), }; - private static readonly HLEKeyboardMappingEntry[] KeyModifierMapping = new HLEKeyboardMappingEntry[] - { - new HLEKeyboardMappingEntry(Key.ControlLeft, 0), - new HLEKeyboardMappingEntry(Key.ShiftLeft, 1), - new HLEKeyboardMappingEntry(Key.AltLeft, 2), - new HLEKeyboardMappingEntry(Key.WinLeft, 3), - new HLEKeyboardMappingEntry(Key.ControlRight, 4), - new HLEKeyboardMappingEntry(Key.ShiftRight, 5), - new HLEKeyboardMappingEntry(Key.AltRight, 6), - new HLEKeyboardMappingEntry(Key.WinRight, 7), - new HLEKeyboardMappingEntry(Key.CapsLock, 8), - new HLEKeyboardMappingEntry(Key.ScrollLock, 9), - new HLEKeyboardMappingEntry(Key.NumLock, 10), + private static readonly HLEKeyboardMappingEntry[] _keyModifierMapping = { + new(Key.ControlLeft, 0), + new(Key.ShiftLeft, 1), + new(Key.AltLeft, 2), + new(Key.WinLeft, 3), + new(Key.ControlRight, 4), + new(Key.ShiftRight, 5), + new(Key.AltRight, 6), + new(Key.WinRight, 7), + new(Key.CapsLock, 8), + new(Key.ScrollLock, 9), + new(Key.NumLock, 10), }; private bool _isValid; - private string _id; private MotionInput _leftMotionInput; private MotionInput _rightMotionInput; @@ -219,14 +214,14 @@ namespace Ryujinx.Input.HLE public IGamepadDriver GamepadDriver { get; private set; } public GamepadStateSnapshot State { get; private set; } - public string Id => _id; + public string Id { get; private set; } - private CemuHookClient _cemuHookClient; + private readonly CemuHookClient _cemuHookClient; public NpadController(CemuHookClient cemuHookClient) { State = default; - _id = null; + Id = null; _isValid = false; _cemuHookClient = cemuHookClient; } @@ -237,8 +232,8 @@ namespace Ryujinx.Input.HLE _gamepad?.Dispose(); - _id = config.Id; - _gamepad = GamepadDriver.GetGamepad(_id); + Id = config.Id; + _gamepad = GamepadDriver.GetGamepad(Id); _isValid = _gamepad != null; UpdateUserConfiguration(config); @@ -278,7 +273,7 @@ namespace Ryujinx.Input.HLE if (motionConfig.MotionBackend != MotionInputBackendType.CemuHook) { _leftMotionInput = new MotionInput(); - } + } else { _leftMotionInput = null; @@ -347,7 +342,7 @@ namespace Ryujinx.Input.HLE public GamepadInput GetHLEInputState() { - GamepadInput state = new GamepadInput(); + GamepadInput state = new(); // First update all buttons foreach (HLEButtonMappingEntry entry in _hleButtonMapping) @@ -366,13 +361,13 @@ namespace Ryujinx.Input.HLE state.LStick = new JoystickPosition { Dx = ClampAxis(leftAxisX), - Dy = ClampAxis(leftAxisY) + Dy = ClampAxis(leftAxisY), }; state.RStick = new JoystickPosition { Dx = ClampAxis(rightAxisX), - Dy = ClampAxis(rightAxisY) + Dy = ClampAxis(rightAxisY), }; } else if (_config is StandardControllerInputConfig controllerConfig) @@ -391,16 +386,16 @@ namespace Ryujinx.Input.HLE private static JoystickPosition ApplyDeadzone(float x, float y, float deadzone) { float magnitudeClamped = Math.Min(MathF.Sqrt(x * x + y * y), 1f); - + if (magnitudeClamped <= deadzone) { - return new JoystickPosition() {Dx = 0, Dy = 0}; + return new JoystickPosition { Dx = 0, Dy = 0 }; } - - return new JoystickPosition() + + return new JoystickPosition { Dx = ClampAxis((x / magnitudeClamped) * ((magnitudeClamped - deadzone) / (1 - deadzone))), - Dy = ClampAxis((y / magnitudeClamped) * ((magnitudeClamped - deadzone) / (1 - deadzone))) + Dy = ClampAxis((y / magnitudeClamped) * ((magnitudeClamped - deadzone) / (1 - deadzone))), }; } @@ -428,7 +423,7 @@ namespace Ryujinx.Input.HLE return new JoystickPosition { Dx = (int)point.X, - Dy = (int)point.Y + Dy = (int)point.Y, }; } @@ -476,12 +471,12 @@ namespace Ryujinx.Input.HLE rotation = new Vector3(); } - return new SixAxisInput() + return new SixAxisInput { Accelerometer = accelerometer, - Gyroscope = gyroscope, - Rotation = rotation, - Orientation = orientationForHLE + Gyroscope = gyroscope, + Rotation = rotation, + Orientation = orientationForHLE, }; } @@ -502,20 +497,20 @@ namespace Ryujinx.Input.HLE { KeyboardStateSnapshot keyboardState = keyboard.GetKeyboardStateSnapshot(); - KeyboardInput hidKeyboard = new KeyboardInput + KeyboardInput hidKeyboard = new() { Modifier = 0, - Keys = new ulong[0x4] + Keys = new ulong[0x4], }; - foreach (HLEKeyboardMappingEntry entry in KeyMapping) + foreach (HLEKeyboardMappingEntry entry in _keyMapping) { ulong value = keyboardState.IsPressed(entry.TargetKey) ? 1UL : 0UL; hidKeyboard.Keys[entry.Target / 0x40] |= (value << (entry.Target % 0x40)); } - foreach (HLEKeyboardMappingEntry entry in KeyModifierMapping) + foreach (HLEKeyboardMappingEntry entry in _keyModifierMapping) { int value = keyboardState.IsPressed(entry.TargetKey) ? 1 : 0; @@ -539,6 +534,7 @@ namespace Ryujinx.Input.HLE public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 89a2f585b..25887748f 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -7,13 +7,15 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client; +using ControllerType = Ryujinx.Common.Configuration.Hid.ControllerType; +using PlayerIndex = Ryujinx.HLE.HOS.Services.Hid.PlayerIndex; using Switch = Ryujinx.HLE.Switch; namespace Ryujinx.Input.HLE { public class NpadManager : IDisposable { - private CemuHookClient _cemuHookClient; + private readonly CemuHookClient _cemuHookClient; private readonly object _lock = new(); @@ -21,7 +23,7 @@ namespace Ryujinx.Input.HLE private const int MaxControllers = 9; - private NpadController[] _controllers; + private readonly NpadController[] _controllers; private readonly IGamepadDriver _keyboardDriver; private readonly IGamepadDriver _gamepadDriver; @@ -51,7 +53,7 @@ namespace Ryujinx.Input.HLE { lock (_lock) { - List<InputConfig> validInputs = new List<InputConfig>(); + List<InputConfig> validInputs = new(); foreach (var inputConfigEntry in _inputConfig) { if (_controllers[(int)inputConfigEntry.PlayerIndex] != null) @@ -96,10 +98,8 @@ namespace Ryujinx.Input.HLE { return controller.UpdateDriverConfiguration(targetDriver, config); } - else - { - return controller.GamepadDriver != null; - } + + return controller.GamepadDriver != null; } public void ReloadConfiguration(List<InputConfig> inputConfig, bool enableKeyboard, bool enableMouse) @@ -112,11 +112,11 @@ namespace Ryujinx.Input.HLE _controllers[i] = null; } - List<InputConfig> validInputs = new List<InputConfig>(); + List<InputConfig> validInputs = new(); foreach (InputConfig inputConfigEntry in inputConfig) { - NpadController controller = new NpadController(_cemuHookClient); + NpadController controller = new(_cemuHookClient); bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry); @@ -131,9 +131,9 @@ namespace Ryujinx.Input.HLE } } - _inputConfig = inputConfig; + _inputConfig = inputConfig; _enableKeyboard = enableKeyboard; - _enableMouse = enableMouse; + _enableMouse = enableMouse; _device.Hid.RefreshInputConfig(validInputs); } @@ -167,8 +167,8 @@ namespace Ryujinx.Input.HLE { lock (_lock) { - List<GamepadInput> hleInputStates = new List<GamepadInput>(); - List<SixAxisInput> hleMotionStates = new List<SixAxisInput>(NpadDevices.MaxControllers); + List<GamepadInput> hleInputStates = new(); + List<SixAxisInput> hleMotionStates = new(NpadDevices.MaxControllers); KeyboardInput? hleKeyboardInput = null; @@ -178,7 +178,7 @@ namespace Ryujinx.Input.HLE (SixAxisInput, SixAxisInput) motionState = default; NpadController controller = _controllers[(int)inputConfig.PlayerIndex]; - Ryujinx.HLE.HOS.Services.Hid.PlayerIndex playerIndex = (Ryujinx.HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex; + PlayerIndex playerIndex = (PlayerIndex)inputConfig.PlayerIndex; bool isJoyconPair = false; @@ -195,7 +195,7 @@ namespace Ryujinx.Input.HLE inputState.Buttons |= _device.Hid.UpdateStickButtons(inputState.LStick, inputState.RStick); - isJoyconPair = inputConfig.ControllerType == Common.Configuration.Hid.ControllerType.JoyconPair; + isJoyconPair = inputConfig.ControllerType == ControllerType.JoyconPair; var altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default; @@ -284,7 +284,7 @@ namespace Ryujinx.Input.HLE { lock (_lock) { - return _inputConfig.Find(x => x.PlayerIndex == (Ryujinx.Common.Configuration.Hid.PlayerIndex)index); + return _inputConfig.Find(x => x.PlayerIndex == (Common.Configuration.Hid.PlayerIndex)index); } } @@ -314,6 +314,7 @@ namespace Ryujinx.Input.HLE public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } } diff --git a/src/Ryujinx.Input/HLE/TouchScreenManager.cs b/src/Ryujinx.Input/HLE/TouchScreenManager.cs index e4b0f8fc7..c613f9281 100644 --- a/src/Ryujinx.Input/HLE/TouchScreenManager.cs +++ b/src/Ryujinx.Input/HLE/TouchScreenManager.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Input.HLE MouseStateSnapshot snapshot = IMouse.GetMouseStateSnapshot(_mouse); var touchPosition = IMouse.GetScreenPosition(snapshot.Position, _mouse.ClientSize, aspectRatio); - TouchPoint currentPoint = new TouchPoint + TouchPoint currentPoint = new() { Attribute = TouchAttribute.End, @@ -41,7 +41,7 @@ namespace Ryujinx.Input.HLE // Placeholder values till more data is acquired DiameterX = 10, DiameterY = 10, - Angle = 90 + Angle = 90, }; _device.Hid.Touchscreen.Update(currentPoint); @@ -71,7 +71,7 @@ namespace Ryujinx.Input.HLE attribute = TouchAttribute.End; } - TouchPoint currentPoint = new TouchPoint + TouchPoint currentPoint = new() { Attribute = attribute, @@ -81,7 +81,7 @@ namespace Ryujinx.Input.HLE // Placeholder values till more data is acquired DiameterX = 10, DiameterY = 10, - Angle = 90 + Angle = 90, }; _device.Hid.Touchscreen.Update(currentPoint); @@ -94,6 +94,9 @@ namespace Ryujinx.Input.HLE return false; } - public void Dispose() { } + public void Dispose() + { + GC.SuppressFinalize(this); + } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/IMouse.cs b/src/Ryujinx.Input/IMouse.cs index fde150fc2..e20e7798d 100644 --- a/src/Ryujinx.Input/IMouse.cs +++ b/src/Ryujinx.Input/IMouse.cs @@ -8,7 +8,9 @@ namespace Ryujinx.Input /// </summary> public interface IMouse : IGamepad { +#pragma warning disable IDE0051 // Remove unused private member private const int SwitchPanelWidth = 1280; +#pragma warning restore IDE0051 private const int SwitchPanelHeight = 720; /// <summary> @@ -101,4 +103,4 @@ namespace Ryujinx.Input return new Vector2(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Key.cs b/src/Ryujinx.Input/Key.cs index 5fa044847..b4229e059 100644 --- a/src/Ryujinx.Input/Key.cs +++ b/src/Ryujinx.Input/Key.cs @@ -137,6 +137,6 @@ BackSlash, Unbound, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/KeyboardStateSnapshot.cs b/src/Ryujinx.Input/KeyboardStateSnapshot.cs index da77a461f..abf85666b 100644 --- a/src/Ryujinx.Input/KeyboardStateSnapshot.cs +++ b/src/Ryujinx.Input/KeyboardStateSnapshot.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Input /// </summary> public class KeyboardStateSnapshot { - private bool[] _keysState; + private readonly bool[] _keysState; /// <summary> /// Create a new <see cref="KeyboardStateSnapshot"/>. diff --git a/src/Ryujinx.Input/Motion/CemuHook/Client.cs b/src/Ryujinx.Input/Motion/CemuHook/Client.cs index a79412a17..b8b936c1e 100644 --- a/src/Ryujinx.Input/Motion/CemuHook/Client.cs +++ b/src/Ryujinx.Input/Motion/CemuHook/Client.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Input.Motion.CemuHook { public class Client : IDisposable { - public const uint Magic = 0x43555344; // DSUC + public const uint Magic = 0x43555344; // DSUC public const ushort Version = 1001; private bool _active; @@ -29,15 +29,15 @@ namespace Ryujinx.Input.Motion.CemuHook private readonly Dictionary<int, UdpClient> _clients; private readonly bool[] _clientErrorStatus = new bool[Enum.GetValues<PlayerIndex>().Length]; - private readonly long[] _clientRetryTimer = new long[Enum.GetValues<PlayerIndex>().Length]; - private NpadManager _npadManager; + private readonly long[] _clientRetryTimer = new long[Enum.GetValues<PlayerIndex>().Length]; + private readonly NpadManager _npadManager; public Client(NpadManager npadManager) { _npadManager = npadManager; - _hosts = new Dictionary<int, IPEndPoint>(); - _motionData = new Dictionary<int, Dictionary<int, MotionInput>>(); - _clients = new Dictionary<int, UdpClient>(); + _hosts = new Dictionary<int, IPEndPoint>(); + _motionData = new Dictionary<int, Dictionary<int, MotionInput>>(); + _clients = new Dictionary<int, UdpClient>(); CloseClients(); } @@ -84,7 +84,7 @@ namespace Ryujinx.Input.Motion.CemuHook try { - IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(host), port); + IPEndPoint endPoint = new(IPAddress.Parse(host), port); client = new UdpClient(host, port); @@ -141,9 +141,9 @@ namespace Ryujinx.Input.Motion.CemuHook { lock (_motionData) { - if (_motionData.ContainsKey(player)) + if (_motionData.TryGetValue(player, out Dictionary<int, MotionInput> value)) { - if (_motionData[player].TryGetValue(slot, out input)) + if (value.TryGetValue(slot, out input)) { return true; } @@ -164,26 +164,26 @@ namespace Ryujinx.Input.Motion.CemuHook private void Send(byte[] data, int clientId) { - if (_clients.TryGetValue(clientId, out UdpClient _client)) + if (_clients.TryGetValue(clientId, out UdpClient client)) { - if (_client != null && _client.Client != null && _client.Client.Connected) + if (client != null && client.Client != null && client.Client.Connected) { try { - _client?.Send(data, data.Length); + client?.Send(data, data.Length); } catch (SocketException socketException) { if (!_clientErrorStatus[clientId]) { - Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to send data request to motion source at {_client.Client.RemoteEndPoint}. Error: {socketException.ErrorCode}"); + Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to send data request to motion source at {client.Client.RemoteEndPoint}. Error: {socketException.ErrorCode}"); } _clientErrorStatus[clientId] = true; RemoveClient(clientId); - _client?.Dispose(); + client?.Dispose(); SetRetryTimer(clientId); } @@ -193,7 +193,7 @@ namespace Ryujinx.Input.Motion.CemuHook RemoveClient(clientId); - _client?.Dispose(); + client?.Dispose(); SetRetryTimer(clientId); } @@ -203,13 +203,13 @@ namespace Ryujinx.Input.Motion.CemuHook private byte[] Receive(int clientId, int timeout = 0) { - if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient _client)) + if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient client)) { - if (_client != null && _client.Client != null && _client.Client.Connected) + if (client != null && client.Client != null && client.Client.Connected) { - _client.Client.ReceiveTimeout = timeout; + client.Client.ReceiveTimeout = timeout; - var result = _client?.Receive(ref endPoint); + var result = client?.Receive(ref endPoint); if (result.Length > 0) { @@ -242,9 +242,9 @@ namespace Ryujinx.Input.Motion.CemuHook public void ReceiveLoop(int clientId) { - if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient _client)) + if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient client)) { - if (_client != null && _client.Client != null && _client.Client.Connected) + if (client != null && client.Client != null && client.Client.Connected) { try { @@ -271,7 +271,7 @@ namespace Ryujinx.Input.Motion.CemuHook RemoveClient(clientId); - _client?.Dispose(); + client?.Dispose(); SetRetryTimer(clientId); } @@ -281,7 +281,7 @@ namespace Ryujinx.Input.Motion.CemuHook RemoveClient(clientId); - _client?.Dispose(); + client?.Dispose(); SetRetryTimer(clientId); } @@ -297,8 +297,8 @@ namespace Ryujinx.Input.Motion.CemuHook data = data.AsSpan()[16..].ToArray(); - using MemoryStream stream = new MemoryStream(data); - using BinaryReader reader = new BinaryReader(stream); + using MemoryStream stream = new(data); + using BinaryReader reader = new(stream); switch (type) { @@ -310,18 +310,18 @@ namespace Ryujinx.Input.Motion.CemuHook case MessageType.Data: ControllerDataResponse inputData = reader.ReadStruct<ControllerDataResponse>(); - Vector3 accelerometer = new Vector3() + Vector3 accelerometer = new() { X = -inputData.AccelerometerX, Y = inputData.AccelerometerZ, - Z = -inputData.AccelerometerY + Z = -inputData.AccelerometerY, }; - Vector3 gyroscrope = new Vector3() + Vector3 gyroscrope = new() { X = inputData.GyroscopePitch, Y = inputData.GyroscopeRoll, - Z = -inputData.GyroscopeYaw + Z = -inputData.GyroscopeYaw, }; ulong timestamp = inputData.MotionTimestamp; @@ -346,7 +346,7 @@ namespace Ryujinx.Input.Motion.CemuHook } else { - MotionInput input = new MotionInput(); + MotionInput input = new(); input.Update(accelerometer, gyroscrope, timestamp, cemuHookConfig.Sensitivity, (float)cemuHookConfig.GyroDeadzone); @@ -355,11 +355,11 @@ namespace Ryujinx.Input.Motion.CemuHook } else { - MotionInput input = new MotionInput(); + MotionInput input = new(); input.Update(accelerometer, gyroscrope, timestamp, cemuHookConfig.Sensitivity, (float)cemuHookConfig.GyroDeadzone); - _motionData.Add(clientId, new Dictionary<int, MotionInput>() { { slot, input } }); + _motionData.Add(clientId, new Dictionary<int, MotionInput> { { slot, input } }); } } else @@ -380,38 +380,37 @@ namespace Ryujinx.Input.Motion.CemuHook Header header = GenerateHeader(clientId); - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); + + writer.WriteStruct(header); + + ControllerInfoRequest request = new() { - writer.WriteStruct(header); + Type = MessageType.Info, + PortsCount = 4, + }; - ControllerInfoRequest request = new ControllerInfoRequest() - { - Type = MessageType.Info, - PortsCount = 4 - }; + request.PortIndices[0] = (byte)slot; - request.PortIndices[0] = (byte)slot; + writer.WriteStruct(request); - writer.WriteStruct(request); + header.Length = (ushort)(stream.Length - 16); - header.Length = (ushort)(stream.Length - 16); + writer.Seek(6, SeekOrigin.Begin); + writer.Write(header.Length); - writer.Seek(6, SeekOrigin.Begin); - writer.Write(header.Length); + Crc32.Hash(stream.ToArray(), header.Crc32.AsSpan()); - Crc32.Hash(stream.ToArray(), header.Crc32.AsSpan()); + writer.Seek(8, SeekOrigin.Begin); + writer.Write(header.Crc32.AsSpan()); - writer.Seek(8, SeekOrigin.Begin); - writer.Write(header.Crc32.AsSpan()); + byte[] data = stream.ToArray(); - byte[] data = stream.ToArray(); - - Send(data, clientId); - } + Send(data, clientId); } - public unsafe void RequestData(int clientId, int slot) + public void RequestData(int clientId, int slot) { if (!_active) { @@ -420,44 +419,43 @@ namespace Ryujinx.Input.Motion.CemuHook Header header = GenerateHeader(clientId); - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); + + writer.WriteStruct(header); + + ControllerDataRequest request = new() { - writer.WriteStruct(header); + Type = MessageType.Data, + Slot = (byte)slot, + SubscriberType = SubscriberType.Slot, + }; - ControllerDataRequest request = new ControllerDataRequest() - { - Type = MessageType.Data, - Slot = (byte)slot, - SubscriberType = SubscriberType.Slot - }; + writer.WriteStruct(request); - writer.WriteStruct(request); + header.Length = (ushort)(stream.Length - 16); - header.Length = (ushort)(stream.Length - 16); + writer.Seek(6, SeekOrigin.Begin); + writer.Write(header.Length); - writer.Seek(6, SeekOrigin.Begin); - writer.Write(header.Length); + Crc32.Hash(stream.ToArray(), header.Crc32.AsSpan()); - Crc32.Hash(stream.ToArray(), header.Crc32.AsSpan()); + writer.Seek(8, SeekOrigin.Begin); + writer.Write(header.Crc32.AsSpan()); - writer.Seek(8, SeekOrigin.Begin); - writer.Write(header.Crc32.AsSpan()); + byte[] data = stream.ToArray(); - byte[] data = stream.ToArray(); - - Send(data, clientId); - } + Send(data, clientId); } - private Header GenerateHeader(int clientId) + private static Header GenerateHeader(int clientId) { - Header header = new Header() + Header header = new() { - Id = (uint)clientId, + Id = (uint)clientId, MagicString = Magic, - Version = Version, - Length = 0 + Version = Version, + Length = 0, }; return header; @@ -465,9 +463,10 @@ namespace Ryujinx.Input.Motion.CemuHook public void Dispose() { + GC.SuppressFinalize(this); _active = false; CloseClients(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs index 7fb72344c..4eee2e8e0 100644 --- a/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs +++ b/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs @@ -16,15 +16,15 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol public struct ControllerDataResponse { public SharedResponse Shared; - public byte Connected; - public uint PacketId; - public byte ExtraButtons; - public byte MainButtons; - public ushort PSExtraInput; - public ushort LeftStickXY; - public ushort RightStickXY; - public uint DPadAnalog; - public ulong MainButtonsAnalog; + public byte Connected; + public uint PacketId; + public byte ExtraButtons; + public byte MainButtons; + public ushort PSExtraInput; + public ushort LeftStickXY; + public ushort RightStickXY; + public uint DPadAnalog; + public ulong MainButtonsAnalog; public Array6<byte> Touch1; public Array6<byte> Touch2; @@ -42,6 +42,6 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol { All, Slot, - Mac + Mac, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs index 63d4524a8..3e51c2915 100644 --- a/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs +++ b/src/Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol public struct ControllerInfoResponse { public SharedResponse Shared; - private byte _zero; + private readonly byte _zero; } [StructLayout(LayoutKind.Sequential, Pack = 1)] @@ -17,4 +17,4 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol public int PortsCount; public Array4<byte> PortIndices; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs index 57f58ff03..142006d17 100644 --- a/src/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs +++ b/src/Ryujinx.Input/Motion/CemuHook/Protocol/Header.cs @@ -12,4 +12,4 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol public Array4<byte> Crc32; public uint Id; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs index de1e5e90d..5ef15a7a3 100644 --- a/src/Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs +++ b/src/Ryujinx.Input/Motion/CemuHook/Protocol/MessageType.cs @@ -4,6 +4,6 @@ { Protocol = 0x100000, Info, - Data + Data, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs b/src/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs index e2e1ee9b4..be37f53b6 100644 --- a/src/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs +++ b/src/Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs @@ -6,11 +6,11 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct SharedResponse { - public MessageType Type; - public byte Slot; - public SlotState State; + public MessageType Type; + public byte Slot; + public SlotState State; public DeviceModelType ModelType; - public ConnectionType ConnectionType; + public ConnectionType ConnectionType; public Array6<byte> MacAddress; public BatteryStatus BatteryStatus; @@ -20,21 +20,21 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol { Disconnected, Reserved, - Connected + Connected, } public enum DeviceModelType : byte { None, PartialGyro, - FullGyro + FullGyro, } public enum ConnectionType : byte { None, USB, - Bluetooth + Bluetooth, } public enum BatteryStatus : byte @@ -46,6 +46,6 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol High, Full, Charging, - Charged + Charged, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Motion/MotionInput.cs b/src/Ryujinx.Input/Motion/MotionInput.cs index 1923d9cbe..2c7af58f6 100644 --- a/src/Ryujinx.Input/Motion/MotionInput.cs +++ b/src/Ryujinx.Input/Motion/MotionInput.cs @@ -6,19 +6,19 @@ namespace Ryujinx.Input { public class MotionInput { - public ulong TimeStamp { get; set; } + public ulong TimeStamp { get; set; } public Vector3 Accelerometer { get; set; } - public Vector3 Gyroscrope { get; set; } - public Vector3 Rotation { get; set; } + public Vector3 Gyroscrope { get; set; } + public Vector3 Rotation { get; set; } private readonly MotionSensorFilter _filter; public MotionInput() { - TimeStamp = 0; + TimeStamp = 0; Accelerometer = new Vector3(); - Gyroscrope = new Vector3(); - Rotation = new Vector3(); + Gyroscrope = new Vector3(); + Rotation = new Vector3(); // TODO: RE the correct filter. _filter = new MotionSensorFilter(0f); @@ -62,4 +62,4 @@ namespace Ryujinx.Input return degree * (MathF.PI / 180); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/Motion/MotionSensorFilter.cs b/src/Ryujinx.Input/Motion/MotionSensorFilter.cs index 440fa7ac5..1a9749682 100644 --- a/src/Ryujinx.Input/Motion/MotionSensorFilter.cs +++ b/src/Ryujinx.Input/Motion/MotionSensorFilter.cs @@ -106,19 +106,19 @@ namespace Ryujinx.Input.Motion float q1 = Quaternion.W; // Estimated direction of gravity. - Vector3 gravity = new Vector3() + Vector3 gravity = new() { X = 2f * (q2 * q4 - q1 * q3), Y = 2f * (q1 * q2 + q3 * q4), - Z = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4 + Z = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4, }; // Error is cross product between estimated direction and measured direction of gravity. - Vector3 error = new Vector3() + Vector3 error = new() { X = accel.Y * gravity.Z - accel.Z * gravity.Y, Y = accel.Z * gravity.X - accel.X * gravity.Z, - Z = accel.X * gravity.Y - accel.Y * gravity.X + Z = accel.X * gravity.Y - accel.Y * gravity.X, }; if (Ki > 0f) @@ -134,7 +134,7 @@ namespace Ryujinx.Input.Motion gyro += (Kp * error) + (Ki * _intergralError); // Integrate rate of change of quaternion. - Vector3 delta = new Vector3(q2, q3, q4); + Vector3 delta = new(q2, q3, q4); q1 += (-q2 * gyro.X - q3 * gyro.Y - q4 * gyro.Z) * (SampleRateCoefficient * SamplePeriod); q2 += (q1 * gyro.X + delta.Y * gyro.Z - delta.Z * gyro.Y) * (SampleRateCoefficient * SamplePeriod); @@ -142,7 +142,7 @@ namespace Ryujinx.Input.Motion q4 += (q1 * gyro.Z + delta.X * gyro.Y - delta.Y * gyro.X) * (SampleRateCoefficient * SamplePeriod); // Normalise quaternion. - Quaternion quaternion = new Quaternion(q2, q3, q4, q1); + Quaternion quaternion = new(q2, q3, q4, q1); norm = 1f / quaternion.Length(); @@ -159,4 +159,4 @@ namespace Ryujinx.Input.Motion Quaternion = Quaternion.Identity; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/MotionInputId.cs b/src/Ryujinx.Input/MotionInputId.cs index 3176a987a..61c7d305c 100644 --- a/src/Ryujinx.Input/MotionInputId.cs +++ b/src/Ryujinx.Input/MotionInputId.cs @@ -20,6 +20,6 @@ /// Gyroscope. /// </summary> /// <remarks>Values are in degrees</remarks> - Gyroscope + Gyroscope, } } diff --git a/src/Ryujinx.Input/MouseButton.cs b/src/Ryujinx.Input/MouseButton.cs index ab7642167..a29910027 100644 --- a/src/Ryujinx.Input/MouseButton.cs +++ b/src/Ryujinx.Input/MouseButton.cs @@ -11,6 +11,6 @@ namespace Ryujinx.Input Button7, Button8, Button9, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/MouseStateSnapshot.cs b/src/Ryujinx.Input/MouseStateSnapshot.cs index ddfdebc6f..9efc9f9c7 100644 --- a/src/Ryujinx.Input/MouseStateSnapshot.cs +++ b/src/Ryujinx.Input/MouseStateSnapshot.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Input /// </summary> public class MouseStateSnapshot { - private bool[] _buttonState; + private readonly bool[] _buttonState; /// <summary> /// The position of the mouse cursor @@ -31,7 +31,7 @@ namespace Ryujinx.Input _buttonState = buttonState; Position = position; - Scroll = scroll; + Scroll = scroll; } /// <summary> @@ -42,4 +42,4 @@ namespace Ryujinx.Input [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsPressed(MouseButton button) => _buttonState[(int)button]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input/StickInputId.cs b/src/Ryujinx.Input/StickInputId.cs index fc9d80434..94c0f25e8 100644 --- a/src/Ryujinx.Input/StickInputId.cs +++ b/src/Ryujinx.Input/StickInputId.cs @@ -9,6 +9,6 @@ Left, Right, - Count + Count, } } From 0a75b73fa43ddadf561ddeb0f923c6f3811c025b Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:34:00 +0200 Subject: [PATCH 679/737] [Ryujinx.Memory] Address dotnet-format issues (#5386) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Silence dotnet format IDE0059 warnings * Address or silence dotnet format IDE1006 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA1069 warnings * Address remaining dotnet format analyzer warnings * Address review comments * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Another rebase, another dotnet format run * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Address review feedback * Assign Decommit to ReplacePlaceholder * Run final dotnet format pass * Organize imports again * Add trailing commas * Add missing newline --- src/Ryujinx.Memory/AddressSpaceManager.cs | 8 ++++---- src/Ryujinx.Memory/MemoryAllocationFlags.cs | 2 +- src/Ryujinx.Memory/MemoryBlock.cs | 6 +++--- src/Ryujinx.Memory/MemoryManagement.cs | 2 +- src/Ryujinx.Memory/MemoryManagementUnix.cs | 14 +++++++------- src/Ryujinx.Memory/MemoryManagementWindows.cs | 2 +- src/Ryujinx.Memory/MemoryManagerUnixHelper.cs | 8 ++++---- src/Ryujinx.Memory/MemoryMapFlags.cs | 2 +- src/Ryujinx.Memory/MemoryNotContiguousException.cs | 2 +- src/Ryujinx.Memory/MemoryPermission.cs | 2 +- src/Ryujinx.Memory/Range/HostMemoryRange.cs | 14 ++++++++++++-- src/Ryujinx.Memory/Range/IRange.cs | 2 +- src/Ryujinx.Memory/Range/MemoryRange.cs | 2 +- src/Ryujinx.Memory/Range/MultiRange.cs | 12 +++++++++++- src/Ryujinx.Memory/Range/RangeList.cs | 8 ++++---- src/Ryujinx.Memory/Tracking/MemoryTracking.cs | 8 ++++---- src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs | 11 +++++++---- src/Ryujinx.Memory/Tracking/RegionHandle.cs | 14 +++++++++----- .../Tracking/SmartMultiRegionHandle.cs | 4 +++- src/Ryujinx.Memory/Tracking/VirtualRegion.cs | 9 ++++++--- src/Ryujinx.Memory/WindowsShared/MappingTree.cs | 2 +- .../WindowsShared/PlaceholderManager.cs | 12 ++++++------ src/Ryujinx.Memory/WindowsShared/WindowsApi.cs | 8 ++++---- .../WindowsShared/WindowsApiException.cs | 2 +- src/Ryujinx.Memory/WindowsShared/WindowsFlags.cs | 8 ++++---- 25 files changed, 98 insertions(+), 66 deletions(-) diff --git a/src/Ryujinx.Memory/AddressSpaceManager.cs b/src/Ryujinx.Memory/AddressSpaceManager.cs index ac89fca6d..65b4d48f2 100644 --- a/src/Ryujinx.Memory/AddressSpaceManager.cs +++ b/src/Ryujinx.Memory/AddressSpaceManager.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Memory { size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - data.Slice(0, size).CopyTo(GetHostSpanContiguous(va, size)); + data[..size].CopyTo(GetHostSpanContiguous(va, size)); offset += size; } @@ -215,7 +215,7 @@ namespace Ryujinx.Memory /// <inheritdoc/> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetPagesCount(ulong va, uint size, out ulong startVa) + private static int GetPagesCount(ulong va, uint size, out ulong startVa) { // WARNING: Always check if ulong does not overflow during the operations. startVa = va & ~(ulong)PageMask; @@ -224,7 +224,7 @@ namespace Ryujinx.Memory return (int)(vaSpan / PageSize); } - private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); + private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); @@ -361,7 +361,7 @@ namespace Ryujinx.Memory { size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - GetHostSpanContiguous(va, size).CopyTo(data.Slice(0, size)); + GetHostSpanContiguous(va, size).CopyTo(data[..size]); offset += size; } diff --git a/src/Ryujinx.Memory/MemoryAllocationFlags.cs b/src/Ryujinx.Memory/MemoryAllocationFlags.cs index 6f0ef1aa9..e5fa9360d 100644 --- a/src/Ryujinx.Memory/MemoryAllocationFlags.cs +++ b/src/Ryujinx.Memory/MemoryAllocationFlags.cs @@ -47,6 +47,6 @@ namespace Ryujinx.Memory /// Indicates that the memory will be used to store JIT generated code. /// On some platforms, this requires special flags to be passed that will allow the memory to be executable. /// </summary> - Jit = 1 << 5 + Jit = 1 << 5, } } diff --git a/src/Ryujinx.Memory/MemoryBlock.cs b/src/Ryujinx.Memory/MemoryBlock.cs index e7fc47516..7d8d7cf05 100644 --- a/src/Ryujinx.Memory/MemoryBlock.cs +++ b/src/Ryujinx.Memory/MemoryBlock.cs @@ -364,9 +364,9 @@ namespace Ryujinx.Memory /// <param name="pointer">Native pointer</param> /// <param name="offset">Offset to add</param> /// <returns>Native pointer with the added offset</returns> - private IntPtr PtrAddr(IntPtr pointer, ulong offset) + private static IntPtr PtrAddr(IntPtr pointer, ulong offset) { - return (IntPtr)(pointer.ToInt64() + (long)offset); + return new IntPtr(pointer.ToInt64() + (long)offset); } /// <summary> @@ -439,4 +439,4 @@ namespace Ryujinx.Memory private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/MemoryManagement.cs b/src/Ryujinx.Memory/MemoryManagement.cs index 7acf8345f..3415ba40e 100644 --- a/src/Ryujinx.Memory/MemoryManagement.cs +++ b/src/Ryujinx.Memory/MemoryManagement.cs @@ -203,4 +203,4 @@ namespace Ryujinx.Memory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs index 4314c3928..9827b59be 100644 --- a/src/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/src/Ryujinx.Memory/MemoryManagementUnix.cs @@ -50,7 +50,7 @@ namespace Ryujinx.Memory } } - IntPtr ptr = mmap(IntPtr.Zero, size, prot, flags, -1, 0); + IntPtr ptr = Mmap(IntPtr.Zero, size, prot, flags, -1, 0); if (ptr == MAP_FAILED) { @@ -115,7 +115,7 @@ namespace Ryujinx.Memory MemoryPermission.ReadAndExecute => MmapProts.PROT_READ | MmapProts.PROT_EXEC, MemoryPermission.ReadWriteExecute => MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC, MemoryPermission.Execute => MmapProts.PROT_EXEC, - _ => throw new MemoryProtectionException(permission) + _ => throw new MemoryProtectionException(permission), }; } @@ -185,12 +185,12 @@ namespace Ryujinx.Memory public static void DestroySharedMemory(IntPtr handle) { - close((int)handle); + close(handle.ToInt32()); } public static IntPtr MapSharedMemory(IntPtr handle, ulong size) { - return mmap(IntPtr.Zero, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_SHARED, (int)handle, 0); + return Mmap(IntPtr.Zero, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_SHARED, handle.ToInt32(), 0); } public static void UnmapSharedMemory(IntPtr address, ulong size) @@ -200,12 +200,12 @@ namespace Ryujinx.Memory public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, ulong size) { - mmap(location, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_FIXED | MmapFlags.MAP_SHARED, (int)sharedMemory, (long)srcOffset); + Mmap(location, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_FIXED | MmapFlags.MAP_SHARED, sharedMemory.ToInt32(), (long)srcOffset); } public static void UnmapView(IntPtr location, ulong size) { - mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0); + Mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/MemoryManagementWindows.cs b/src/Ryujinx.Memory/MemoryManagementWindows.cs index aaed5a637..b5be5b3cf 100644 --- a/src/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/src/Ryujinx.Memory/MemoryManagementWindows.cs @@ -148,4 +148,4 @@ namespace Ryujinx.Memory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs index a7b207ab0..6f36a6d56 100644 --- a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs +++ b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Memory PROT_NONE = 0, PROT_READ = 1, PROT_WRITE = 2, - PROT_EXEC = 4 + PROT_EXEC = 4, } [Flags] @@ -26,7 +26,7 @@ namespace Ryujinx.Memory MAP_NORESERVE = 8, MAP_FIXED = 16, MAP_UNLOCKED = 32, - MAP_JIT_DARWIN = 0x800 + MAP_JIT_DARWIN = 0x800, } [Flags] @@ -164,9 +164,9 @@ namespace Ryujinx.Memory return result; } - public static IntPtr mmap(IntPtr address, ulong length, MmapProts prot, MmapFlags flags, int fd, long offset) + public static IntPtr Mmap(IntPtr address, ulong length, MmapProts prot, MmapFlags flags, int fd, long offset) { return Internal_mmap(address, length, prot, MmapFlagsToSystemFlags(flags), fd, offset); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/MemoryMapFlags.cs b/src/Ryujinx.Memory/MemoryMapFlags.cs index b4c74c8c9..5c023cc12 100644 --- a/src/Ryujinx.Memory/MemoryMapFlags.cs +++ b/src/Ryujinx.Memory/MemoryMapFlags.cs @@ -18,6 +18,6 @@ namespace Ryujinx.Memory /// and allocate its own private storage for the mapping. /// This allows some mappings that would otherwise fail due to host platform restrictions to succeed. /// </summary> - Private = 1 << 0 + Private = 1 << 0, } } diff --git a/src/Ryujinx.Memory/MemoryNotContiguousException.cs b/src/Ryujinx.Memory/MemoryNotContiguousException.cs index 3106955b8..3468adb99 100644 --- a/src/Ryujinx.Memory/MemoryNotContiguousException.cs +++ b/src/Ryujinx.Memory/MemoryNotContiguousException.cs @@ -16,4 +16,4 @@ namespace Ryujinx.Memory { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/MemoryPermission.cs b/src/Ryujinx.Memory/MemoryPermission.cs index 8c3e33cf7..6e71d7b8c 100644 --- a/src/Ryujinx.Memory/MemoryPermission.cs +++ b/src/Ryujinx.Memory/MemoryPermission.cs @@ -46,6 +46,6 @@ namespace Ryujinx.Memory /// <summary> /// Indicates an invalid protection. /// </summary> - Invalid = 255 + Invalid = 255, } } diff --git a/src/Ryujinx.Memory/Range/HostMemoryRange.cs b/src/Ryujinx.Memory/Range/HostMemoryRange.cs index 79c649d85..a4abebb54 100644 --- a/src/Ryujinx.Memory/Range/HostMemoryRange.cs +++ b/src/Ryujinx.Memory/Range/HostMemoryRange.cs @@ -5,12 +5,12 @@ namespace Ryujinx.Memory.Range /// <summary> /// Range of memory composed of an address and size. /// </summary> - public struct HostMemoryRange : IEquatable<HostMemoryRange> + public readonly struct HostMemoryRange : IEquatable<HostMemoryRange> { /// <summary> /// An empty memory range, with a null address and zero size. /// </summary> - public static HostMemoryRange Empty => new HostMemoryRange(0, 0); + public static HostMemoryRange Empty => new(0, 0); /// <summary> /// Start address of the range. @@ -67,5 +67,15 @@ namespace Ryujinx.Memory.Range { return HashCode.Combine(Address, Size); } + + public static bool operator ==(HostMemoryRange left, HostMemoryRange right) + { + return left.Equals(right); + } + + public static bool operator !=(HostMemoryRange left, HostMemoryRange right) + { + return !(left == right); + } } } diff --git a/src/Ryujinx.Memory/Range/IRange.cs b/src/Ryujinx.Memory/Range/IRange.cs index 1685396d1..c85e21d1d 100644 --- a/src/Ryujinx.Memory/Range/IRange.cs +++ b/src/Ryujinx.Memory/Range/IRange.cs @@ -28,4 +28,4 @@ namespace Ryujinx.Memory.Range /// <returns>True if overlapping, false otherwise</returns> bool OverlapsWith(ulong address, ulong size); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/Range/MemoryRange.cs b/src/Ryujinx.Memory/Range/MemoryRange.cs index c7ee6db22..7e7b84b51 100644 --- a/src/Ryujinx.Memory/Range/MemoryRange.cs +++ b/src/Ryujinx.Memory/Range/MemoryRange.cs @@ -8,7 +8,7 @@ /// <summary> /// An empty memory range, with a null address and zero size. /// </summary> - public static MemoryRange Empty => new MemoryRange(0UL, 0); + public static MemoryRange Empty => new(0UL, 0); /// <summary> /// Start address of the range. diff --git a/src/Ryujinx.Memory/Range/MultiRange.cs b/src/Ryujinx.Memory/Range/MultiRange.cs index 42ef24be6..7011e528e 100644 --- a/src/Ryujinx.Memory/Range/MultiRange.cs +++ b/src/Ryujinx.Memory/Range/MultiRange.cs @@ -310,7 +310,7 @@ namespace Ryujinx.Memory.Range return _singleRange.GetHashCode(); } - HashCode hash = new HashCode(); + HashCode hash = new(); foreach (MemoryRange range in _ranges) { @@ -328,5 +328,15 @@ namespace Ryujinx.Memory.Range { return HasSingleRange ? _singleRange.ToString() : string.Join(", ", _ranges); } + + public static bool operator ==(MultiRange left, MultiRange right) + { + return left.Equals(right); + } + + public static bool operator !=(MultiRange left, MultiRange right) + { + return !(left == right); + } } } diff --git a/src/Ryujinx.Memory/Range/RangeList.cs b/src/Ryujinx.Memory/Range/RangeList.cs index 469195973..72cef1de0 100644 --- a/src/Ryujinx.Memory/Range/RangeList.cs +++ b/src/Ryujinx.Memory/Range/RangeList.cs @@ -238,7 +238,7 @@ namespace Ryujinx.Memory.Range if (index < 0) { - return default(T); + return default; } return _items[index].Value; @@ -398,7 +398,7 @@ namespace Ryujinx.Memory.Range /// <returns>List index of the item, or complement index of nearest item with lower value on the list</returns> private int BinarySearch(ulong address) { - int left = 0; + int left = 0; int right = Count - 1; while (left <= right) @@ -435,7 +435,7 @@ namespace Ryujinx.Memory.Range /// <returns>List index of the item, or complement index of nearest item with lower value on the list</returns> private int BinarySearch(ulong address, ulong endAddress) { - int left = 0; + int left = 0; int right = Count - 1; while (left <= right) @@ -480,4 +480,4 @@ namespace Ryujinx.Memory.Range } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/Tracking/MemoryTracking.cs b/src/Ryujinx.Memory/Tracking/MemoryTracking.cs index bf1e0ad34..ab9d98932 100644 --- a/src/Ryujinx.Memory/Tracking/MemoryTracking.cs +++ b/src/Ryujinx.Memory/Tracking/MemoryTracking.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Memory.Tracking /// This lock must be obtained when traversing or updating the region-handle hierarchy. /// It is not required when reading dirty flags. /// </summary> - internal object TrackingLock = new object(); + internal object TrackingLock = new(); /// <summary> /// Create a new tracking structure for the given "physical" memory block, @@ -114,7 +114,7 @@ namespace Ryujinx.Memory.Tracking /// <returns>A list of virtual regions within the given range</returns> internal List<VirtualRegion> GetVirtualRegionsForHandle(ulong va, ulong size) { - List<VirtualRegion> result = new List<VirtualRegion>(); + List<VirtualRegion> result = new(); _virtualRegions.GetOrAddRegions(result, va, size, (va, size) => new VirtualRegion(this, va, size)); return result; @@ -172,7 +172,7 @@ namespace Ryujinx.Memory.Tracking lock (TrackingLock) { bool mapped = _memoryManager.IsRangeMapped(address, size); - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, id, mapped); + RegionHandle handle = new(this, paAddress, paSize, address, size, id, mapped); return handle; } @@ -194,7 +194,7 @@ namespace Ryujinx.Memory.Tracking lock (TrackingLock) { bool mapped = _memoryManager.IsRangeMapped(address, size); - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, id, mapped); + RegionHandle handle = new(this, paAddress, paSize, address, size, bitmap, bit, id, mapped); return handle; } diff --git a/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs index 68fc5e759..5d3f20f4f 100644 --- a/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ b/src/Ryujinx.Memory/Tracking/MultiRegionHandle.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Threading; @@ -21,11 +22,11 @@ namespace Ryujinx.Memory.Tracking private readonly ulong Granularity; private readonly ulong Size; - private ConcurrentBitmap _dirtyBitmap; + private readonly ConcurrentBitmap _dirtyBitmap; private int _sequenceNumber; - private BitMap _sequenceNumberBitmap; - private BitMap _dirtyCheckedBitmap; + private readonly BitMap _sequenceNumberBitmap; + private readonly BitMap _dirtyCheckedBitmap; private int _uncheckedHandles; public bool Dirty { get; private set; } = true; @@ -54,7 +55,7 @@ namespace Ryujinx.Memory.Tracking // It is assumed that the provided handles do not overlap, in order, are on page boundaries, // and don't extend past the requested range. - foreach (RegionHandle handle in handles) + foreach (RegionHandle handle in handles.Cast<RegionHandle>()) { int startIndex = (int)((handle.RealAddress - address) / granularity); @@ -406,6 +407,8 @@ namespace Ryujinx.Memory.Tracking public void Dispose() { + GC.SuppressFinalize(this); + foreach (var handle in _handles) { handle.Dispose(); diff --git a/src/Ryujinx.Memory/Tracking/RegionHandle.cs b/src/Ryujinx.Memory/Tracking/RegionHandle.cs index 777944888..d36207cad 100644 --- a/src/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/src/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection.Metadata; using System.Threading; namespace Ryujinx.Memory.Tracking @@ -50,7 +49,7 @@ namespace Ryujinx.Memory.Tracking internal IMultiRegionHandle Parent { get; set; } - private event Action _onDirty; + private event Action OnDirty; private readonly object _preActionLock = new(); private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access. @@ -269,7 +268,7 @@ namespace Ryujinx.Memory.Tracking Dirty = true; if (!oldDirty) { - _onDirty?.Invoke(); + OnDirty?.Invoke(); } Parent?.SignalWrite(); } @@ -311,7 +310,10 @@ namespace Ryujinx.Memory.Tracking /// <param name="consecutiveCheck">True if this reprotect is the result of consecutive dirty checks</param> public void Reprotect(bool asDirty, bool consecutiveCheck = false) { - if (_volatile) return; + if (_volatile) + { + return; + } Dirty = asDirty; @@ -403,7 +405,7 @@ namespace Ryujinx.Memory.Tracking /// <param name="action">Action to call on dirty</param> public void RegisterDirtyEvent(Action action) { - _onDirty += action; + OnDirty += action; } /// <summary> @@ -461,6 +463,8 @@ namespace Ryujinx.Memory.Tracking { ObjectDisposedException.ThrowIf(_disposed, this); + GC.SuppressFinalize(this); + _disposed = true; lock (_tracking.TrackingLock) diff --git a/src/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs b/src/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs index 4acddefaf..bab00377d 100644 --- a/src/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs +++ b/src/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Memory.Tracking private readonly ulong _address; private readonly ulong _granularity; private readonly ulong _size; - private MemoryTracking _tracking; + private readonly MemoryTracking _tracking; private readonly int _id; public bool Dirty { get; private set; } = true; @@ -271,6 +271,8 @@ namespace Ryujinx.Memory.Tracking public void Dispose() { + GC.SuppressFinalize(this); + foreach (var handle in _handles) { handle?.Dispose(); diff --git a/src/Ryujinx.Memory/Tracking/VirtualRegion.cs b/src/Ryujinx.Memory/Tracking/VirtualRegion.cs index 9651426b3..e595196c7 100644 --- a/src/Ryujinx.Memory/Tracking/VirtualRegion.cs +++ b/src/Ryujinx.Memory/Tracking/VirtualRegion.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Memory.Tracking /// </summary> class VirtualRegion : AbstractRegion { - public List<RegionHandle> Handles = new List<RegionHandle>(); + public List<RegionHandle> Handles = new(); private readonly MemoryTracking _tracking; private MemoryPermission _lastPermission; @@ -86,7 +86,10 @@ namespace Ryujinx.Memory.Tracking foreach (var handle in Handles) { result &= handle.RequiredPermission; - if (result == 0) return result; + if (result == 0) + { + return result; + } } return result; } @@ -128,7 +131,7 @@ namespace Ryujinx.Memory.Tracking public override INonOverlappingRange Split(ulong splitAddress) { - VirtualRegion newRegion = new VirtualRegion(_tracking, splitAddress, EndAddress - splitAddress, _lastPermission); + VirtualRegion newRegion = new(_tracking, splitAddress, EndAddress - splitAddress, _lastPermission); Size = splitAddress - Address; // The new region inherits all of our parents. diff --git a/src/Ryujinx.Memory/WindowsShared/MappingTree.cs b/src/Ryujinx.Memory/WindowsShared/MappingTree.cs index 97758c2b1..9ca84b56b 100644 --- a/src/Ryujinx.Memory/WindowsShared/MappingTree.cs +++ b/src/Ryujinx.Memory/WindowsShared/MappingTree.cs @@ -84,4 +84,4 @@ namespace Ryujinx.Memory.WindowsShared } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs b/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs index 3022b6616..b68a076c4 100644 --- a/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs +++ b/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs @@ -31,9 +31,11 @@ namespace Ryujinx.Memory.WindowsShared _partialUnmapStatePtr = PartialUnmapState.GlobalState; - _partialUnmapTrimThread = new Thread(TrimThreadLocalMapLoop); - _partialUnmapTrimThread.Name = "CPU.PartialUnmapTrimThread"; - _partialUnmapTrimThread.IsBackground = true; + _partialUnmapTrimThread = new Thread(TrimThreadLocalMapLoop) + { + Name = "CPU.PartialUnmapTrimThread", + IsBackground = true, + }; _partialUnmapTrimThread.Start(); } @@ -704,8 +706,6 @@ namespace Ryujinx.Memory.WindowsShared count = _protections.GetNodes(address, endAddress, ref overlaps); } - ulong startAddress = address; - for (int index = 0; index < count; index++) { var protection = overlaps[index]; @@ -733,4 +733,4 @@ namespace Ryujinx.Memory.WindowsShared } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs b/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs index c554e3205..82903c05f 100644 --- a/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs +++ b/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs @@ -7,8 +7,8 @@ namespace Ryujinx.Memory.WindowsShared [SupportedOSPlatform("windows")] static partial class WindowsApi { - public static readonly IntPtr InvalidHandleValue = new IntPtr(-1); - public static readonly IntPtr CurrentProcessHandle = new IntPtr(-1); + public static readonly IntPtr InvalidHandleValue = new(-1); + public static readonly IntPtr CurrentProcessHandle = new(-1); [LibraryImport("kernel32.dll", SetLastError = true)] public static partial IntPtr VirtualAlloc( @@ -96,8 +96,8 @@ namespace Ryujinx.Memory.WindowsShared MemoryPermission.ReadAndExecute => MemoryProtection.ExecuteRead, MemoryPermission.ReadWriteExecute => MemoryProtection.ExecuteReadWrite, MemoryPermission.Execute => MemoryProtection.Execute, - _ => throw new MemoryProtectionException(permission) + _ => throw new MemoryProtectionException(permission), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs b/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs index 330c1842a..308006aba 100644 --- a/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs +++ b/src/Ryujinx.Memory/WindowsShared/WindowsApiException.cs @@ -23,4 +23,4 @@ namespace Ryujinx.Memory.WindowsShared return $"{functionName} returned error code 0x{WindowsApi.GetLastError():X}."; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Memory/WindowsShared/WindowsFlags.cs b/src/Ryujinx.Memory/WindowsShared/WindowsFlags.cs index ca69cfe93..7dd4215ee 100644 --- a/src/Ryujinx.Memory/WindowsShared/WindowsFlags.cs +++ b/src/Ryujinx.Memory/WindowsShared/WindowsFlags.cs @@ -10,14 +10,14 @@ namespace Ryujinx.Memory.WindowsShared Commit = 0x1000, Reserve = 0x2000, Decommit = 0x4000, - ReplacePlaceholder = 0x4000, + ReplacePlaceholder = Decommit, Release = 0x8000, ReservePlaceholder = 0x40000, Reset = 0x80000, Physical = 0x400000, TopDown = 0x100000, WriteWatch = 0x200000, - LargePages = 0x20000000 + LargePages = 0x20000000, } [Flags] @@ -33,7 +33,7 @@ namespace Ryujinx.Memory.WindowsShared ExecuteWriteCopy = 0x80, GuardModifierflag = 0x100, NoCacheModifierflag = 0x200, - WriteCombineModifierflag = 0x400 + WriteCombineModifierflag = 0x400, } [Flags] @@ -47,6 +47,6 @@ namespace Ryujinx.Memory.WindowsShared SectionCommit = 0x8000000, SectionImage = 0x1000000, SectionNoCache = 0x10000000, - SectionReserve = 0x4000000 + SectionReserve = 0x4000000, } } From fc20d9b925b83532a19467293a7cdcbaa4ef3d6b Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:41:38 +0200 Subject: [PATCH 680/737] [Ryujinx.Common] Address dotnet-format issues (#5358) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0059 warnings * Address or silence dotnet format IDE1006 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA2211 warnings * Silence CA1806 and CA1834 issues * Fix formatting for switch expressions * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Revert formatting changes for while and for-loops * Format if-blocks correctly * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format analyzers after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Remove a few unused parameters * Replace MmeShadowScratch with Array256<uint> * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Run dotnet format after rebase * Address IDE0251 warnings * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Second dotnet format pass * Fix build issues * Fix StructArrayHelpers.cs * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Fix return statements * Fix naming rule violations * Update src/Ryujinx.Common/Utilities/StreamUtils.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Add trailing commas * Address review feedback * Address review feedback * Rename remaining type parameters to TKey and TValue * Fix manual formatting for logging levels * Fix spacing before comments --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../Collections/IntervalTree.cs | 104 ++-- .../Collections/IntrusiveRedBlackTree.cs | 13 +- .../Collections/IntrusiveRedBlackTreeImpl.cs | 2 +- .../Collections/IntrusiveRedBlackTreeNode.cs | 2 +- .../Collections/TreeDictionary.cs | 149 ++--- .../Configuration/AntiAliasing.cs | 4 +- .../Configuration/AppDataManager.cs | 8 +- .../Configuration/AspectRatioExtensions.cs | 16 +- .../DownloadableContentContainer.cs | 2 +- ...ownloadableContentJsonSerializerContext.cs | 2 +- .../Configuration/DownloadableContentNca.cs | 6 +- .../Hid/Controller/GamepadInputId.cs | 4 +- .../GenericControllerInputConfig.cs | 6 +- .../Controller/JoyconConfigControllerStick.cs | 6 +- .../JsonMotionConfigControllerConverter.cs | 10 +- .../MotionConfigJsonSerializerContext.cs | 2 +- .../Hid/Controller/StickInputId.cs | 4 +- .../Configuration/Hid/ControllerType.cs | 18 +- .../Hid/GenericInputConfigurationCommon.cs | 6 +- .../Configuration/Hid/InputConfig.cs | 4 +- .../Hid/InputConfigJsonSerializerContext.cs | 2 +- .../Hid/JsonInputConfigConverter.cs | 10 +- src/Ryujinx.Common/Configuration/Hid/Key.cs | 4 +- .../Keyboard/GenericKeyboardInputConfig.cs | 6 +- .../Hid/Keyboard/JoyconConfigKeyboardStick.cs | 12 +- .../Configuration/Hid/KeyboardHotkeys.cs | 2 +- .../Hid/LeftJoyconCommonConfig.cs | 20 +- .../Configuration/Hid/PlayerIndex.cs | 22 +- .../Hid/RightJoyconCommonConfig.cs | 22 +- .../Configuration/HideCursorMode.cs | 4 +- .../Configuration/ScalingFilter.cs | 4 +- .../Configuration/TitleUpdateMetadata.cs | 6 +- ...itleUpdateMetadataJsonSerializerContext.cs | 2 +- .../Extensions/BinaryReaderExtensions.cs | 6 +- .../Extensions/BinaryWriterExtensions.cs | 3 +- .../NVAPI/NvapiUnicodeString.cs | 4 +- .../NVAPI/NvdrsApplicationV4.cs | 2 +- .../GraphicsDriver/NVAPI/NvdrsProfile.cs | 2 +- .../GraphicsDriver/NVAPI/NvdrsSetting.cs | 2 +- .../GraphicsDriver/NVThreadedOptimization.cs | 27 +- src/Ryujinx.Common/Hash128.cs | 8 +- .../Logging/Formatters/DefaultLogFormatter.cs | 8 +- .../Formatters/DynamicObjectFormatter.cs | 10 +- .../Logging/Formatters/ILogFormatter.cs | 2 +- src/Ryujinx.Common/Logging/LogClass.cs | 4 +- src/Ryujinx.Common/Logging/LogEventArgs.cs | 12 +- .../Logging/LogEventArgsJson.cs | 12 +- .../Logging/LogEventJsonSerializerContext.cs | 2 +- src/Ryujinx.Common/Logging/Logger.cs | 102 ++-- .../Logging/Targets/AsyncLogTargetWrapper.cs | 27 +- .../Logging/Targets/ConsoleLogTarget.cs | 20 +- .../Logging/Targets/FileLogTarget.cs | 11 +- .../Logging/Targets/JsonLogTarget.cs | 12 +- src/Ryujinx.Common/Memory/ArrayPtr.cs | 16 +- .../ByteMemoryPool.ByteMemoryPoolBuffer.cs | 4 +- src/Ryujinx.Common/Memory/ByteMemoryPool.cs | 20 +- .../Memory/MemoryStreamManager.cs | 4 +- .../PartialUnmaps/NativeReaderWriterLock.cs | 19 +- .../Memory/PartialUnmaps/PartialUnmapState.cs | 8 +- .../Memory/PartialUnmaps/ThreadLocalMap.cs | 7 +- src/Ryujinx.Common/Memory/Ptr.cs | 12 +- src/Ryujinx.Common/Memory/SpanReader.cs | 20 +- src/Ryujinx.Common/Memory/SpanWriter.cs | 16 +- .../Memory/StructArrayHelpers.cs | 520 +++++++++--------- .../Memory/StructByteArrayHelpers.cs | 12 +- src/Ryujinx.Common/PerformanceCounter.cs | 14 +- src/Ryujinx.Common/Pools/ObjectPool.cs | 2 +- src/Ryujinx.Common/Pools/SharedPools.cs | 2 +- src/Ryujinx.Common/Pools/ThreadStaticArray.cs | 5 +- src/Ryujinx.Common/ReactiveObject.cs | 6 +- src/Ryujinx.Common/ReleaseInformation.cs | 18 +- .../SystemInfo/LinuxSystemInfo.cs | 31 +- .../SystemInfo/MacOSSystemInfo.cs | 18 +- src/Ryujinx.Common/SystemInfo/SystemInfo.cs | 10 +- .../SystemInfo/WindowsSystemInfo.cs | 10 +- .../SystemInterop/DisplaySleep.cs | 6 +- .../SystemInterop/ForceDpiAware.cs | 8 +- .../SystemInterop/GdiPlusHelper.cs | 4 +- .../SystemInterop/StdErrAdapter.cs | 31 +- .../WindowsMultimediaTimerResolution.cs | 4 +- src/Ryujinx.Common/Utilities/BitUtils.cs | 17 +- .../Utilities/BitfieldExtensions.cs | 2 +- src/Ryujinx.Common/Utilities/Buffers.cs | 4 +- .../Utilities/CommonJsonContext.cs | 2 +- .../Utilities/EmbeddedResources.cs | 69 +-- src/Ryujinx.Common/Utilities/HexUtils.cs | 32 +- src/Ryujinx.Common/Utilities/JsonHelper.cs | 14 +- .../Utilities/MessagePackObjectFormatter.cs | 18 +- .../Utilities/NetworkHelpers.cs | 4 +- src/Ryujinx.Common/Utilities/StreamUtils.cs | 24 +- .../Utilities/TypedStringEnumConverter.cs | 2 +- src/Ryujinx.Common/Utilities/UInt128Utils.cs | 2 +- src/Ryujinx.Common/XXHash128.cs | 143 ++--- .../AudioRenderer/AudioRendererServer.cs | 6 +- src/Ryujinx.HLE/HOS/Services/ServerBase.cs | 2 +- .../SurfaceFlinger/IHOSBinderDriver.cs | 6 +- 96 files changed, 965 insertions(+), 969 deletions(-) diff --git a/src/Ryujinx.Common/Collections/IntervalTree.cs b/src/Ryujinx.Common/Collections/IntervalTree.cs index b5188cc7e..2ea260a5f 100644 --- a/src/Ryujinx.Common/Collections/IntervalTree.cs +++ b/src/Ryujinx.Common/Collections/IntervalTree.cs @@ -7,9 +7,9 @@ namespace Ryujinx.Common.Collections /// <summary> /// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges. /// </summary> - /// <typeparam name="K">Key</typeparam> - /// <typeparam name="V">Value</typeparam> - public class IntervalTree<K, V> : IntrusiveRedBlackTreeImpl<IntervalTreeNode<K, V>> where K : IComparable<K> + /// <typeparam name="TKey">Key</typeparam> + /// <typeparam name="TValue">Value</typeparam> + public class IntervalTree<TKey, TValue> : IntrusiveRedBlackTreeImpl<IntervalTreeNode<TKey, TValue>> where TKey : IComparable<TKey> { private const int ArrayGrowthSize = 32; @@ -22,11 +22,11 @@ namespace Ryujinx.Common.Collections /// <param name="overlaps">Overlaps array to place results in</param> /// <returns>Number of values found</returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - public int Get(K key, ref V[] overlaps) + public int Get(TKey key, ref TValue[] overlaps) { ArgumentNullException.ThrowIfNull(key); - IntervalTreeNode<K, V> node = GetNode(key); + IntervalTreeNode<TKey, TValue> node = GetNode(key); if (node == null) { @@ -39,7 +39,7 @@ namespace Ryujinx.Common.Collections } int overlapsCount = 0; - foreach (RangeNode<K, V> value in node.Values) + foreach (RangeNode<TKey, TValue> value in node.Values) { overlaps[overlapsCount++] = value.Value; } @@ -56,7 +56,7 @@ namespace Ryujinx.Common.Collections /// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param> /// <returns>Number of values found</returns> /// <exception cref="ArgumentNullException"><paramref name="start"/> or <paramref name="end"/> is null</exception> - public int Get(K start, K end, ref V[] overlaps, int overlapCount = 0) + public int Get(TKey start, TKey end, ref TValue[] overlaps, int overlapCount = 0) { ArgumentNullException.ThrowIfNull(start); ArgumentNullException.ThrowIfNull(end); @@ -73,7 +73,7 @@ namespace Ryujinx.Common.Collections /// <param name="end">End of the range to insert</param> /// <param name="value">Value to add</param> /// <exception cref="ArgumentNullException"><paramref name="start"/>, <paramref name="end"/> or <paramref name="value"/> are null</exception> - public void Add(K start, K end, V value) + public void Add(TKey start, TKey end, TValue value) { ArgumentNullException.ThrowIfNull(start); ArgumentNullException.ThrowIfNull(end); @@ -89,7 +89,7 @@ namespace Ryujinx.Common.Collections /// <param name="value">Value to remove</param> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> /// <returns>Number of deleted values</returns> - public int Remove(K key, V value) + public int Remove(TKey key, TValue value) { ArgumentNullException.ThrowIfNull(key); @@ -104,9 +104,9 @@ namespace Ryujinx.Common.Collections /// Adds all the nodes in the dictionary into <paramref name="list"/>. /// </summary> /// <returns>A list of all RangeNodes sorted by Key Order</returns> - public List<RangeNode<K, V>> AsList() + public List<RangeNode<TKey, TValue>> AsList() { - List<RangeNode<K, V>> list = new List<RangeNode<K, V>>(); + List<RangeNode<TKey, TValue>> list = new(); AddToList(Root, list); @@ -122,7 +122,7 @@ namespace Ryujinx.Common.Collections /// </summary> /// <param name="node">The node to search for RangeNodes within</param> /// <param name="list">The list to add RangeNodes to</param> - private void AddToList(IntervalTreeNode<K, V> node, List<RangeNode<K, V>> list) + private void AddToList(IntervalTreeNode<TKey, TValue> node, List<RangeNode<TKey, TValue>> list) { if (node == null) { @@ -142,11 +142,11 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key of the node to get</param> /// <returns>Node reference in the tree</returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - private IntervalTreeNode<K, V> GetNode(K key) + private IntervalTreeNode<TKey, TValue> GetNode(TKey key) { ArgumentNullException.ThrowIfNull(key); - IntervalTreeNode<K, V> node = Root; + IntervalTreeNode<TKey, TValue> node = Root; while (node != null) { int cmp = key.CompareTo(node.Start); @@ -173,7 +173,7 @@ namespace Ryujinx.Common.Collections /// <param name="end">End of the range</param> /// <param name="overlaps">Overlaps array to place results in</param> /// <param name="overlapCount">Overlaps count to update</param> - private void GetValues(IntervalTreeNode<K, V> node, K start, K end, ref V[] overlaps, ref int overlapCount) + private void GetValues(IntervalTreeNode<TKey, TValue> node, TKey start, TKey end, ref TValue[] overlaps, ref int overlapCount) { if (node == null || start.CompareTo(node.Max) >= 0) { @@ -188,7 +188,7 @@ namespace Ryujinx.Common.Collections if (start.CompareTo(node.End) < 0) { // Contains this node. Add overlaps to list. - foreach (RangeNode<K,V> overlap in node.Values) + foreach (RangeNode<TKey, TValue> overlap in node.Values) { if (start.CompareTo(overlap.End) < 0) { @@ -212,9 +212,9 @@ namespace Ryujinx.Common.Collections /// <param name="start">Start of the range to insert</param> /// <param name="end">End of the range to insert</param> /// <param name="value">Value to insert</param> - private void Insert(K start, K end, V value) + private void Insert(TKey start, TKey end, TValue value) { - IntervalTreeNode<K, V> newNode = BSTInsert(start, end, value); + IntervalTreeNode<TKey, TValue> newNode = BSTInsert(start, end, value); RestoreBalanceAfterInsertion(newNode); } @@ -223,10 +223,10 @@ namespace Ryujinx.Common.Collections /// This should only be called if the max increases - not for rebalancing or removals. /// </summary> /// <param name="node">The node to start propagating from</param> - private void PropagateIncrease(IntervalTreeNode<K, V> node) + private static void PropagateIncrease(IntervalTreeNode<TKey, TValue> node) { - K max = node.Max; - IntervalTreeNode<K, V> ptr = node; + TKey max = node.Max; + IntervalTreeNode<TKey, TValue> ptr = node; while ((ptr = ptr.Parent) != null) { @@ -246,13 +246,13 @@ namespace Ryujinx.Common.Collections /// This fully recalculates the max value from all children when there is potential for it to decrease. /// </summary> /// <param name="node">The node to start propagating from</param> - private void PropagateFull(IntervalTreeNode<K, V> node) + private static void PropagateFull(IntervalTreeNode<TKey, TValue> node) { - IntervalTreeNode<K, V> ptr = node; + IntervalTreeNode<TKey, TValue> ptr = node; do { - K max = ptr.End; + TKey max = ptr.End; if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0) { @@ -278,10 +278,10 @@ namespace Ryujinx.Common.Collections /// <param name="end">End of the range to insert</param> /// <param name="value">Value to insert</param> /// <returns>The inserted Node</returns> - private IntervalTreeNode<K, V> BSTInsert(K start, K end, V value) + private IntervalTreeNode<TKey, TValue> BSTInsert(TKey start, TKey end, TValue value) { - IntervalTreeNode<K, V> parent = null; - IntervalTreeNode<K, V> node = Root; + IntervalTreeNode<TKey, TValue> parent = null; + IntervalTreeNode<TKey, TValue> node = Root; while (node != null) { @@ -297,7 +297,7 @@ namespace Ryujinx.Common.Collections } else { - node.Values.Add(new RangeNode<K, V>(start, end, value)); + node.Values.Add(new RangeNode<TKey, TValue>(start, end, value)); if (end.CompareTo(node.End) > 0) { @@ -313,7 +313,7 @@ namespace Ryujinx.Common.Collections return node; } } - IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent); + IntervalTreeNode<TKey, TValue> newNode = new(start, end, value, parent); if (newNode.Parent == null) { Root = newNode; @@ -338,9 +338,9 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key to search for</param> /// <param name="value">Value to delete</param> /// <returns>Number of deleted values</returns> - private int Delete(K key, V value) + private int Delete(TKey key, TValue value) { - IntervalTreeNode<K, V> nodeToDelete = GetNode(key); + IntervalTreeNode<TKey, TValue> nodeToDelete = GetNode(key); if (nodeToDelete == null) { @@ -362,7 +362,7 @@ namespace Ryujinx.Common.Collections return removed; } - IntervalTreeNode<K, V> replacementNode; + IntervalTreeNode<TKey, TValue> replacementNode; if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null) { @@ -373,7 +373,7 @@ namespace Ryujinx.Common.Collections replacementNode = PredecessorOf(nodeToDelete); } - IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode); + IntervalTreeNode<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode); if (tmp != null) { @@ -413,7 +413,7 @@ namespace Ryujinx.Common.Collections #endregion - protected override void RotateLeft(IntervalTreeNode<K, V> node) + protected override void RotateLeft(IntervalTreeNode<TKey, TValue> node) { if (node != null) { @@ -423,7 +423,7 @@ namespace Ryujinx.Common.Collections } } - protected override void RotateRight(IntervalTreeNode<K, V> node) + protected override void RotateRight(IntervalTreeNode<TKey, TValue> node) { if (node != null) { @@ -433,7 +433,7 @@ namespace Ryujinx.Common.Collections } } - public bool ContainsKey(K key) + public bool ContainsKey(TKey key) { ArgumentNullException.ThrowIfNull(key); @@ -444,15 +444,15 @@ namespace Ryujinx.Common.Collections /// <summary> /// Represents a value and its start and end keys. /// </summary> - /// <typeparam name="K"></typeparam> - /// <typeparam name="V"></typeparam> - public readonly struct RangeNode<K, V> + /// <typeparam name="TKey"></typeparam> + /// <typeparam name="TValue"></typeparam> + public readonly struct RangeNode<TKey, TValue> { - public readonly K Start; - public readonly K End; - public readonly V Value; + public readonly TKey Start; + public readonly TKey End; + public readonly TValue Value; - public RangeNode(K start, K end, V value) + public RangeNode(TKey start, TKey end, TValue value) { Start = start; End = end; @@ -463,36 +463,36 @@ namespace Ryujinx.Common.Collections /// <summary> /// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V. /// </summary> - /// <typeparam name="K">Key type of the node</typeparam> - /// <typeparam name="V">Value type of the node</typeparam> - public class IntervalTreeNode<K, V> : IntrusiveRedBlackTreeNode<IntervalTreeNode<K, V>> + /// <typeparam name="TKey">Key type of the node</typeparam> + /// <typeparam name="TValue">Value type of the node</typeparam> + public class IntervalTreeNode<TKey, TValue> : IntrusiveRedBlackTreeNode<IntervalTreeNode<TKey, TValue>> { /// <summary> /// The start of the range. /// </summary> - internal K Start; + internal TKey Start; /// <summary> /// The end of the range - maximum of all in the Values list. /// </summary> - internal K End; + internal TKey End; /// <summary> /// The maximum end value of this node and all its children. /// </summary> - internal K Max; + internal TKey Max; /// <summary> /// Values contained on the node that shares a common Start value. /// </summary> - internal List<RangeNode<K, V>> Values; + internal List<RangeNode<TKey, TValue>> Values; - internal IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent) + internal IntervalTreeNode(TKey start, TKey end, TValue value, IntervalTreeNode<TKey, TValue> parent) { Start = start; End = end; Max = end; - Values = new List<RangeNode<K, V>> { new RangeNode<K, V>(start, end, value) }; + Values = new List<RangeNode<TKey, TValue>> { new RangeNode<TKey, TValue>(start, end, value) }; Parent = parent; } } diff --git a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs index 0063d91e4..9e56f707b 100644 --- a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs +++ b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs @@ -180,11 +180,6 @@ namespace Ryujinx.Common.Collections parent.Right = child; } - if (ParentOf(element) == old) - { - parent = element; - } - element.Color = old.Color; element.Left = old.Left; element.Right = old.Right; @@ -258,11 +253,11 @@ namespace Ryujinx.Common.Collections /// <param name="tree">Tree to search at</param> /// <param name="key">Key of the node to be found</param> /// <returns>Node that is equal to <paramref name="key"/></returns> - public static N GetNodeByKey<N, K>(this IntrusiveRedBlackTree<N> tree, K key) - where N : IntrusiveRedBlackTreeNode<N>, IComparable<N>, IComparable<K> - where K : struct + public static TNode GetNodeByKey<TNode, TKey>(this IntrusiveRedBlackTree<TNode> tree, TKey key) + where TNode : IntrusiveRedBlackTreeNode<TNode>, IComparable<TNode>, IComparable<TKey> + where TKey : struct { - N node = tree.RootNode; + TNode node = tree.RootNode; while (node != null) { int cmp = node.CompareTo(key); diff --git a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs index bcb2e2a23..49f97223a 100644 --- a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs +++ b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeImpl.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Common.Collections { protected const bool Black = true; protected const bool Red = false; - protected T Root = null; + protected T Root; internal T RootNode => Root; diff --git a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs index 7143240da..8480d51ad 100644 --- a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs +++ b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs @@ -13,4 +13,4 @@ namespace Ryujinx.Common.Collections public T Predecessor => IntrusiveRedBlackTreeImpl<T>.PredecessorOf((T)this); public T Successor => IntrusiveRedBlackTreeImpl<T>.SuccessorOf((T)this); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Collections/TreeDictionary.cs b/src/Ryujinx.Common/Collections/TreeDictionary.cs index d118a30cc..ff1794883 100644 --- a/src/Ryujinx.Common/Collections/TreeDictionary.cs +++ b/src/Ryujinx.Common/Collections/TreeDictionary.cs @@ -8,9 +8,9 @@ namespace Ryujinx.Common.Collections /// <summary> /// Dictionary that provides the ability for O(logN) Lookups for keys that exist in the Dictionary, and O(logN) lookups for keys immediately greater than or less than a specified key. /// </summary> - /// <typeparam name="K">Key</typeparam> - /// <typeparam name="V">Value</typeparam> - public class TreeDictionary<K, V> : IntrusiveRedBlackTreeImpl<Node<K, V>>, IDictionary<K, V> where K : IComparable<K> + /// <typeparam name="TKey">Key</typeparam> + /// <typeparam name="TValue">Value</typeparam> + public class TreeDictionary<TKey, TValue> : IntrusiveRedBlackTreeImpl<Node<TKey, TValue>>, IDictionary<TKey, TValue> where TKey : IComparable<TKey> { #region Public Methods @@ -20,11 +20,11 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key of the node value to get</param> /// <returns>Value associated w/ <paramref name="key"/></returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - public V Get(K key) + public TValue Get(TKey key) { ArgumentNullException.ThrowIfNull(key); - Node<K, V> node = GetNode(key); + Node<TKey, TValue> node = GetNode(key); if (node == null) { @@ -42,7 +42,7 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key of the node to add</param> /// <param name="value">Value of the node to add</param> /// <exception cref="ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> are null</exception> - public void Add(K key, V value) + public void Add(TKey key, TValue value) { ArgumentNullException.ThrowIfNull(key); ArgumentNullException.ThrowIfNull(value); @@ -55,7 +55,7 @@ namespace Ryujinx.Common.Collections /// </summary> /// <param name="key">Key of the node to remove</param> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - public void Remove(K key) + public void Remove(TKey key) { ArgumentNullException.ThrowIfNull(key); @@ -71,9 +71,9 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key for which to find the floor value of</param> /// <returns>Key of node immediately less than <paramref name="key"/></returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - public K Floor(K key) + public TKey Floor(TKey key) { - Node<K, V> node = FloorNode(key); + Node<TKey, TValue> node = FloorNode(key); if (node != null) { return node.Key; @@ -87,9 +87,9 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key for which to find the ceiling node of</param> /// <returns>Key of node immediately greater than <paramref name="key"/></returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - public K Ceiling(K key) + public TKey Ceiling(TKey key) { - Node<K, V> node = CeilingNode(key); + Node<TKey, TValue> node = CeilingNode(key); if (node != null) { return node.Key; @@ -102,12 +102,12 @@ namespace Ryujinx.Common.Collections /// </summary> /// <param name="key">Key to find the successor of</param> /// <returns>Value</returns> - public K SuccessorOf(K key) + public TKey SuccessorOf(TKey key) { - Node<K, V> node = GetNode(key); + Node<TKey, TValue> node = GetNode(key); if (node != null) { - Node<K, V> successor = SuccessorOf(node); + Node<TKey, TValue> successor = SuccessorOf(node); return successor != null ? successor.Key : default; } @@ -119,12 +119,12 @@ namespace Ryujinx.Common.Collections /// </summary> /// <param name="key">Key to find the predecessor of</param> /// <returns>Value</returns> - public K PredecessorOf(K key) + public TKey PredecessorOf(TKey key) { - Node<K, V> node = GetNode(key); + Node<TKey, TValue> node = GetNode(key); if (node != null) { - Node<K, V> predecessor = PredecessorOf(node); + Node<TKey, TValue> predecessor = PredecessorOf(node); return predecessor != null ? predecessor.Key : default; } @@ -137,19 +137,19 @@ namespace Ryujinx.Common.Collections /// The key/value pairs will be added in Level Order. /// </summary> /// <param name="list">List to add the tree pairs into</param> - public List<KeyValuePair<K, V>> AsLevelOrderList() + public List<KeyValuePair<TKey, TValue>> AsLevelOrderList() { - List<KeyValuePair<K, V>> list = new List<KeyValuePair<K, V>>(); + List<KeyValuePair<TKey, TValue>> list = new(); - Queue<Node<K, V>> nodes = new Queue<Node<K, V>>(); + Queue<Node<TKey, TValue>> nodes = new(); if (this.Root != null) { nodes.Enqueue(this.Root); } - while (nodes.TryDequeue(out Node<K, V> node)) + while (nodes.TryDequeue(out Node<TKey, TValue> node)) { - list.Add(new KeyValuePair<K, V>(node.Key, node.Value)); + list.Add(new KeyValuePair<TKey, TValue>(node.Key, node.Value)); if (node.Left != null) { nodes.Enqueue(node.Left); @@ -166,9 +166,9 @@ namespace Ryujinx.Common.Collections /// Adds all the nodes in the dictionary into <paramref name="list"/>. /// </summary> /// <returns>A list of all KeyValuePairs sorted by Key Order</returns> - public List<KeyValuePair<K, V>> AsList() + public List<KeyValuePair<TKey, TValue>> AsList() { - List<KeyValuePair<K, V>> list = new List<KeyValuePair<K, V>>(); + List<KeyValuePair<TKey, TValue>> list = new(); AddToList(Root, list); @@ -184,7 +184,7 @@ namespace Ryujinx.Common.Collections /// </summary> /// <param name="node">The node to search for nodes within</param> /// <param name="list">The list to add node to</param> - private void AddToList(Node<K, V> node, List<KeyValuePair<K, V>> list) + private void AddToList(Node<TKey, TValue> node, List<KeyValuePair<TKey, TValue>> list) { if (node == null) { @@ -193,7 +193,7 @@ namespace Ryujinx.Common.Collections AddToList(node.Left, list); - list.Add(new KeyValuePair<K, V>(node.Key, node.Value)); + list.Add(new KeyValuePair<TKey, TValue>(node.Key, node.Value)); AddToList(node.Right, list); } @@ -204,11 +204,11 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key of the node to get</param> /// <returns>Node reference in the tree</returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - private Node<K, V> GetNode(K key) + private Node<TKey, TValue> GetNode(TKey key) { ArgumentNullException.ThrowIfNull(key); - Node<K, V> node = Root; + Node<TKey, TValue> node = Root; while (node != null) { int cmp = key.CompareTo(node.Key); @@ -235,9 +235,9 @@ namespace Ryujinx.Common.Collections /// </summary> /// <param name="key">Key of the node to insert</param> /// <param name="value">Value of the node to insert</param> - private void Insert(K key, V value) + private void Insert(TKey key, TValue value) { - Node<K, V> newNode = BSTInsert(key, value); + Node<TKey, TValue> newNode = BSTInsert(key, value); RestoreBalanceAfterInsertion(newNode); } @@ -251,10 +251,10 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key of the node to insert</param> /// <param name="value">Value of the node to insert</param> /// <returns>The inserted Node</returns> - private Node<K, V> BSTInsert(K key, V value) + private Node<TKey, TValue> BSTInsert(TKey key, TValue value) { - Node<K, V> parent = null; - Node<K, V> node = Root; + Node<TKey, TValue> parent = null; + Node<TKey, TValue> node = Root; while (node != null) { @@ -274,7 +274,7 @@ namespace Ryujinx.Common.Collections return node; } } - Node<K, V> newNode = new Node<K, V>(key, value, parent); + Node<TKey, TValue> newNode = new(key, value, parent); if (newNode.Parent == null) { Root = newNode; @@ -296,14 +296,17 @@ namespace Ryujinx.Common.Collections /// </summary> /// <param name="key">Key of the node to delete</param> /// <returns>The deleted Node</returns> - private Node<K, V> Delete(K key) + private Node<TKey, TValue> Delete(TKey key) { // O(1) Retrieval - Node<K, V> nodeToDelete = GetNode(key); + Node<TKey, TValue> nodeToDelete = GetNode(key); - if (nodeToDelete == null) return null; + if (nodeToDelete == null) + { + return null; + } - Node<K, V> replacementNode; + Node<TKey, TValue> replacementNode; if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null) { @@ -314,7 +317,7 @@ namespace Ryujinx.Common.Collections replacementNode = PredecessorOf(nodeToDelete); } - Node<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode); + Node<TKey, TValue> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode); if (tmp != null) { @@ -354,11 +357,11 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key for which to find the floor node of</param> /// <returns>Node whose key is immediately less than or equal to <paramref name="key"/>, or null if no such node is found.</returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - private Node<K, V> FloorNode(K key) + private Node<TKey, TValue> FloorNode(TKey key) { ArgumentNullException.ThrowIfNull(key); - Node<K, V> tmp = Root; + Node<TKey, TValue> tmp = Root; while (tmp != null) { @@ -382,8 +385,8 @@ namespace Ryujinx.Common.Collections } else { - Node<K, V> parent = tmp.Parent; - Node<K, V> ptr = tmp; + Node<TKey, TValue> parent = tmp.Parent; + Node<TKey, TValue> ptr = tmp; while (parent != null && ptr == parent.Left) { ptr = parent; @@ -406,11 +409,11 @@ namespace Ryujinx.Common.Collections /// <param name="key">Key for which to find the ceiling node of</param> /// <returns>Node whose key is immediately greater than or equal to <paramref name="key"/>, or null if no such node is found.</returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> - private Node<K, V> CeilingNode(K key) + private Node<TKey, TValue> CeilingNode(TKey key) { ArgumentNullException.ThrowIfNull(key); - Node<K, V> tmp = Root; + Node<TKey, TValue> tmp = Root; while (tmp != null) { @@ -434,8 +437,8 @@ namespace Ryujinx.Common.Collections } else { - Node<K, V> parent = tmp.Parent; - Node<K, V> ptr = tmp; + Node<TKey, TValue> parent = tmp.Parent; + Node<TKey, TValue> ptr = tmp; while (parent != null && ptr == parent.Right) { ptr = parent; @@ -457,44 +460,44 @@ namespace Ryujinx.Common.Collections #region Interface Implementations // Method descriptions are not provided as they are already included as part of the interface. - public bool ContainsKey(K key) + public bool ContainsKey(TKey key) { ArgumentNullException.ThrowIfNull(key); return GetNode(key) != null; } - bool IDictionary<K, V>.Remove(K key) + bool IDictionary<TKey, TValue>.Remove(TKey key) { int count = Count; Remove(key); return count > Count; } - public bool TryGetValue(K key, [MaybeNullWhen(false)] out V value) + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { ArgumentNullException.ThrowIfNull(key); - Node<K, V> node = GetNode(key); + Node<TKey, TValue> node = GetNode(key); value = node != null ? node.Value : default; return node != null; } - public void Add(KeyValuePair<K, V> item) + public void Add(KeyValuePair<TKey, TValue> item) { ArgumentNullException.ThrowIfNull(item.Key); Add(item.Key, item.Value); } - public bool Contains(KeyValuePair<K, V> item) + public bool Contains(KeyValuePair<TKey, TValue> item) { if (item.Key == null) { return false; } - Node<K, V> node = GetNode(item.Key); + Node<TKey, TValue> node = GetNode(item.Key); if (node != null) { return node.Key.Equals(item.Key) && node.Value.Equals(item.Value); @@ -502,27 +505,27 @@ namespace Ryujinx.Common.Collections return false; } - public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) + public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { if (arrayIndex < 0 || array.Length - arrayIndex < this.Count) { throw new ArgumentOutOfRangeException(nameof(arrayIndex)); } - SortedList<K, V> list = GetKeyValues(); + SortedList<TKey, TValue> list = GetKeyValues(); int offset = 0; for (int i = arrayIndex; i < array.Length && offset < list.Count; i++) { - array[i] = new KeyValuePair<K, V>(list.Keys[i], list.Values[i]); + array[i] = new KeyValuePair<TKey, TValue>(list.Keys[i], list.Values[i]); offset++; } } - public bool Remove(KeyValuePair<K, V> item) + public bool Remove(KeyValuePair<TKey, TValue> item) { - Node<K, V> node = GetNode(item.Key); + Node<TKey, TValue> node = GetNode(item.Key); if (node == null) { @@ -539,7 +542,7 @@ namespace Ryujinx.Common.Collections return false; } - public IEnumerator<KeyValuePair<K, V>> GetEnumerator() + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return GetKeyValues().GetEnumerator(); } @@ -549,13 +552,13 @@ namespace Ryujinx.Common.Collections return GetKeyValues().GetEnumerator(); } - public ICollection<K> Keys => GetKeyValues().Keys; + public ICollection<TKey> Keys => GetKeyValues().Keys; - public ICollection<V> Values => GetKeyValues().Values; + public ICollection<TValue> Values => GetKeyValues().Values; public bool IsReadOnly => false; - public V this[K key] + public TValue this[TKey key] { get => Get(key); set => Add(key, value); @@ -569,16 +572,16 @@ namespace Ryujinx.Common.Collections /// Returns a sorted list of all the node keys / values in the tree. /// </summary> /// <returns>List of node keys</returns> - private SortedList<K, V> GetKeyValues() + private SortedList<TKey, TValue> GetKeyValues() { - SortedList<K, V> set = new SortedList<K, V>(); - Queue<Node<K, V>> queue = new Queue<Node<K, V>>(); + SortedList<TKey, TValue> set = new(); + Queue<Node<TKey, TValue>> queue = new(); if (Root != null) { queue.Enqueue(Root); } - while (queue.TryDequeue(out Node<K, V> node)) + while (queue.TryDequeue(out Node<TKey, TValue> node)) { set.Add(node.Key, node.Value); if (null != node.Left) @@ -600,14 +603,14 @@ namespace Ryujinx.Common.Collections /// <summary> /// Represents a node in the TreeDictionary which contains a key and value of generic type K and V, respectively. /// </summary> - /// <typeparam name="K">Key of the node</typeparam> - /// <typeparam name="V">Value of the node</typeparam> - public class Node<K, V> : IntrusiveRedBlackTreeNode<Node<K, V>> where K : IComparable<K> + /// <typeparam name="TKey">Key of the node</typeparam> + /// <typeparam name="TValue">Value of the node</typeparam> + public class Node<TKey, TValue> : IntrusiveRedBlackTreeNode<Node<TKey, TValue>> where TKey : IComparable<TKey> { - internal K Key; - internal V Value; + internal TKey Key; + internal TValue Value; - internal Node(K key, V value, Node<K, V> parent) + internal Node(TKey key, TValue value, Node<TKey, TValue> parent) { Key = key; Value = value; diff --git a/src/Ryujinx.Common/Configuration/AntiAliasing.cs b/src/Ryujinx.Common/Configuration/AntiAliasing.cs index 159108ae4..9ab0458cd 100644 --- a/src/Ryujinx.Common/Configuration/AntiAliasing.cs +++ b/src/Ryujinx.Common/Configuration/AntiAliasing.cs @@ -11,6 +11,6 @@ namespace Ryujinx.Common.Configuration SmaaLow, SmaaMedium, SmaaHigh, - SmaaUltra + SmaaUltra, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs index b685e7064..1dbc1f0ce 100644 --- a/src/Ryujinx.Common/Configuration/AppDataManager.cs +++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Common.Configuration { UserProfile, Portable, - Custom + Custom, } public static LaunchMode Mode { get; private set; } @@ -34,7 +34,7 @@ namespace Ryujinx.Common.Configuration private const string DefaultModsDir = "mods"; public static string CustomModsPath { get; set; } - public static string CustomSdModsPath {get; set; } + public static string CustomSdModsPath { get; set; } public static string CustomNandPath { get; set; } // TODO: Actually implement this into VFS public static string CustomSdCardPath { get; set; } // TODO: Actually implement this into VFS @@ -151,7 +151,7 @@ namespace Ryujinx.Common.Configuration } } - public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName; + public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName; public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs b/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs index 5e97ed19c..c9bf24650 100644 --- a/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs +++ b/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Common.Configuration Fixed16x10, Fixed21x9, Fixed32x9, - Stretched + Stretched, } public static class AspectRatioExtensions @@ -25,12 +25,14 @@ namespace Ryujinx.Common.Configuration { return aspectRatio switch { +#pragma warning disable IDE0055 // Disable formatting AspectRatio.Fixed4x3 => 4.0f, AspectRatio.Fixed16x9 => 16.0f, AspectRatio.Fixed16x10 => 16.0f, AspectRatio.Fixed21x9 => 21.0f, AspectRatio.Fixed32x9 => 32.0f, - _ => 16.0f + _ => 16.0f, +#pragma warning restore IDE0055 }; } @@ -38,12 +40,14 @@ namespace Ryujinx.Common.Configuration { return aspectRatio switch { +#pragma warning disable IDE0055 // Disable formatting AspectRatio.Fixed4x3 => 3.0f, AspectRatio.Fixed16x9 => 9.0f, AspectRatio.Fixed16x10 => 10.0f, AspectRatio.Fixed21x9 => 9.0f, AspectRatio.Fixed32x9 => 9.0f, - _ => 9.0f + _ => 9.0f, +#pragma warning restore IDE0055 }; } @@ -51,13 +55,15 @@ namespace Ryujinx.Common.Configuration { return aspectRatio switch { +#pragma warning disable IDE0055 // Disable formatting AspectRatio.Fixed4x3 => "4:3", AspectRatio.Fixed16x9 => "16:9", AspectRatio.Fixed16x10 => "16:10", AspectRatio.Fixed21x9 => "21:9", AspectRatio.Fixed32x9 => "32:9", - _ => "Stretched" + _ => "Stretched", +#pragma warning restore IDE0055 }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs b/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs index b6ae2f3fb..f9e68a3c2 100644 --- a/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs +++ b/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs @@ -10,4 +10,4 @@ namespace Ryujinx.Common.Configuration [JsonPropertyName("dlc_nca_list")] public List<DownloadableContentNca> DownloadableContentNcaList { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs index 132c45a44..0dbc0a301 100644 --- a/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs +++ b/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs @@ -8,4 +8,4 @@ namespace Ryujinx.Common.Configuration public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs b/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs index 80b67300e..dded719cf 100644 --- a/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs +++ b/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs @@ -7,8 +7,8 @@ namespace Ryujinx.Common.Configuration [JsonPropertyName("path")] public string FullPath { get; set; } [JsonPropertyName("title_id")] - public ulong TitleId { get; set; } + public ulong TitleId { get; set; } [JsonPropertyName("is_enabled")] - public bool Enabled { get; set; } + public bool Enabled { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs index ad1fa6673..78bc46f21 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs @@ -53,6 +53,6 @@ namespace Ryujinx.Common.Configuration.Hid.Controller SingleLeftTrigger1, SingleRightTrigger1, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs index d7f0e7881..abc245bc4 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs @@ -4,7 +4,7 @@ using System.Text.Json.Serialization; namespace Ryujinx.Common.Configuration.Hid.Controller { - public class GenericControllerInputConfig<Button, Stick> : GenericInputConfigurationCommon<Button> where Button : unmanaged where Stick : unmanaged + public class GenericControllerInputConfig<TButton, TStick> : GenericInputConfigurationCommon<TButton> where TButton : unmanaged where TStick : unmanaged { [JsonIgnore] private float _deadzoneLeft; @@ -16,12 +16,12 @@ namespace Ryujinx.Common.Configuration.Hid.Controller /// <summary> /// Left JoyCon Controller Stick Bindings /// </summary> - public JoyconConfigControllerStick<Button, Stick> LeftJoyconStick { get; set; } + public JoyconConfigControllerStick<TButton, TStick> LeftJoyconStick { get; set; } /// <summary> /// Right JoyCon Controller Stick Bindings /// </summary> - public JoyconConfigControllerStick<Button, Stick> RightJoyconStick { get; set; } + public JoyconConfigControllerStick<TButton, TStick> RightJoyconStick { get; set; } /// <summary> /// Controller Left Analog Stick Deadzone diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs index 869cff4fe..aaa3ef1d9 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs @@ -1,11 +1,11 @@ namespace Ryujinx.Common.Configuration.Hid.Controller { - public class JoyconConfigControllerStick<Button, Stick> where Button: unmanaged where Stick: unmanaged + public class JoyconConfigControllerStick<TButton, TStick> where TButton : unmanaged where TStick : unmanaged { - public Stick Joystick { get; set; } + public TStick Joystick { get; set; } public bool InvertStickX { get; set; } public bool InvertStickY { get; set; } public bool Rotate90CW { get; set; } - public Button StickButton { get; set; } + public TButton StickButton { get; set; } } } diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs index 2b9e0af42..613533718 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController> { - private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly MotionConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader) { @@ -55,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion return motionBackendType switch { - MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController), - MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController), + MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardMotionConfigController), + MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, _serializerContext.CemuHookMotionConfigController), _ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"), }; } @@ -66,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion switch (value.MotionBackend) { case MotionInputBackendType.GamepadDriver: - JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController); + JsonSerializer.Serialize(writer, value as StandardMotionConfigController, _serializerContext.StandardMotionConfigController); break; case MotionInputBackendType.CemuHook: - JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController); + JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, _serializerContext.CemuHookMotionConfigController); break; default: throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}"); diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs index 5cd9e452b..f9d154ffd 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion public partial class MotionConfigJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs index 5fc4d1c87..cbe63e578 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs @@ -10,6 +10,6 @@ namespace Ryujinx.Common.Configuration.Hid.Controller Left, Right, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs index 1c273537c..cf61b79fd 100644 --- a/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs +++ b/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs @@ -10,14 +10,14 @@ namespace Ryujinx.Common.Configuration.Hid public enum ControllerType { None, - ProController = 1 << 0, - Handheld = 1 << 1, - JoyconPair = 1 << 2, - JoyconLeft = 1 << 3, - JoyconRight = 1 << 4, - Invalid = 1 << 5, - Pokeball = 1 << 6, + ProController = 1 << 0, + Handheld = 1 << 1, + JoyconPair = 1 << 2, + JoyconLeft = 1 << 3, + JoyconRight = 1 << 4, + Invalid = 1 << 5, + Pokeball = 1 << 6, SystemExternal = 1 << 29, - System = 1 << 30 + System = 1 << 30, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs b/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs index 3d43817e1..618e20b5c 100644 --- a/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs +++ b/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs @@ -1,15 +1,15 @@ namespace Ryujinx.Common.Configuration.Hid { - public class GenericInputConfigurationCommon<Button> : InputConfig where Button : unmanaged + public class GenericInputConfigurationCommon<TButton> : InputConfig where TButton : unmanaged { /// <summary> /// Left JoyCon Controller Bindings /// </summary> - public LeftJoyconCommonConfig<Button> LeftJoycon { get; set; } + public LeftJoyconCommonConfig<TButton> LeftJoycon { get; set; } /// <summary> /// Right JoyCon Controller Bindings /// </summary> - public RightJoyconCommonConfig<Button> RightJoycon { get; set; } + public RightJoyconCommonConfig<TButton> RightJoycon { get; set; } } } diff --git a/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs index 16c8f8e32..16a1042e3 100644 --- a/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Common.Configuration.Hid /// Player's Index for the controller /// </summary> public PlayerIndex PlayerIndex { get; set; } - + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) @@ -38,4 +38,4 @@ namespace Ryujinx.Common.Configuration.Hid PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs index 254c4feb4..30138049b 100644 --- a/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs +++ b/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs @@ -11,4 +11,4 @@ namespace Ryujinx.Common.Configuration.Hid public partial class InputConfigJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs b/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs index 08bbcbf17..81b0d932d 100644 --- a/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs +++ b/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Common.Configuration.Hid { public class JsonInputConfigConverter : JsonConverter<InputConfig> { - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader) { @@ -57,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid return backendType switch { - InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig), - InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig), + InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardKeyboardInputConfig), + InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardControllerInputConfig), _ => throw new InvalidOperationException($"Unknown backend type {backendType}"), }; } @@ -68,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid switch (value.Backend) { case InputBackendType.WindowKeyboard: - JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig); + JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, _serializerContext.StandardKeyboardInputConfig); break; case InputBackendType.GamepadSDL2: - JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig); + JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, _serializerContext.StandardControllerInputConfig); break; default: throw new ArgumentException($"Unknown backend type {value.Backend}"); diff --git a/src/Ryujinx.Common/Configuration/Hid/Key.cs b/src/Ryujinx.Common/Configuration/Hid/Key.cs index 3501b8aed..a82a99592 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Key.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Key.cs @@ -138,6 +138,6 @@ namespace Ryujinx.Common.Configuration.Hid BackSlash, Unbound, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs index b6c82c931..37819a26d 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs @@ -1,15 +1,15 @@ namespace Ryujinx.Common.Configuration.Hid.Keyboard { - public class GenericKeyboardInputConfig<Key> : GenericInputConfigurationCommon<Key> where Key : unmanaged + public class GenericKeyboardInputConfig<TKey> : GenericInputConfigurationCommon<TKey> where TKey : unmanaged { /// <summary> /// Left JoyCon Controller Stick Bindings /// </summary> - public JoyconConfigKeyboardStick<Key> LeftJoyconStick { get; set; } + public JoyconConfigKeyboardStick<TKey> LeftJoyconStick { get; set; } /// <summary> /// Right JoyCon Controller Stick Bindings /// </summary> - public JoyconConfigKeyboardStick<Key> RightJoyconStick { get; set; } + public JoyconConfigKeyboardStick<TKey> RightJoyconStick { get; set; } } } diff --git a/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs index cadc17e82..e0bdefc5c 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs @@ -1,11 +1,11 @@ namespace Ryujinx.Common.Configuration.Hid.Keyboard { - public class JoyconConfigKeyboardStick<Key> where Key: unmanaged + public class JoyconConfigKeyboardStick<TKey> where TKey : unmanaged { - public Key StickUp { get; set; } - public Key StickDown { get; set; } - public Key StickLeft { get; set; } - public Key StickRight { get; set; } - public Key StickButton { get; set; } + public TKey StickUp { get; set; } + public TKey StickDown { get; set; } + public TKey StickLeft { get; set; } + public TKey StickRight { get; set; } + public TKey StickButton { get; set; } } } diff --git a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 1a10c2a53..f0707c73d 100644 --- a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -14,4 +14,4 @@ public Key VolumeUp { get; set; } public Key VolumeDown { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs index a57240c40..4ae890721 100644 --- a/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs @@ -1,15 +1,15 @@ namespace Ryujinx.Common.Configuration.Hid { - public class LeftJoyconCommonConfig<Button> + public class LeftJoyconCommonConfig<TButton> { - public Button ButtonMinus { get; set; } - public Button ButtonL { get; set; } - public Button ButtonZl { get; set; } - public Button ButtonSl { get; set; } - public Button ButtonSr { get; set; } - public Button DpadUp { get; set; } - public Button DpadDown { get; set; } - public Button DpadLeft { get; set; } - public Button DpadRight { get; set; } + public TButton ButtonMinus { get; set; } + public TButton ButtonL { get; set; } + public TButton ButtonZl { get; set; } + public TButton ButtonSl { get; set; } + public TButton ButtonSr { get; set; } + public TButton DpadUp { get; set; } + public TButton DpadDown { get; set; } + public TButton DpadLeft { get; set; } + public TButton DpadRight { get; set; } } } diff --git a/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs index 4e9a0434d..05e8f3fa4 100644 --- a/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs +++ b/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs @@ -7,16 +7,16 @@ namespace Ryujinx.Common.Configuration.Hid [JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))] public enum PlayerIndex { - Player1 = 0, - Player2 = 1, - Player3 = 2, - Player4 = 3, - Player5 = 4, - Player6 = 5, - Player7 = 6, - Player8 = 7, + Player1 = 0, + Player2 = 1, + Player3 = 2, + Player4 = 3, + Player5 = 4, + Player6 = 5, + Player7 = 6, + Player8 = 7, Handheld = 8, - Unknown = 9, - Auto = 10 // Shouldn't be used directly + Unknown = 9, + Auto = 10, // Shouldn't be used directly } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs index ca2d01767..e121b9a52 100644 --- a/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs @@ -1,15 +1,15 @@ namespace Ryujinx.Common.Configuration.Hid { - public class RightJoyconCommonConfig<Button> + public class RightJoyconCommonConfig<TButton> { - public Button ButtonPlus { get; set; } - public Button ButtonR { get; set; } - public Button ButtonZr { get; set; } - public Button ButtonSl { get; set; } - public Button ButtonSr { get; set; } - public Button ButtonX { get; set; } - public Button ButtonB { get; set; } - public Button ButtonY { get; set; } - public Button ButtonA { get; set; } + public TButton ButtonPlus { get; set; } + public TButton ButtonR { get; set; } + public TButton ButtonZr { get; set; } + public TButton ButtonSl { get; set; } + public TButton ButtonSr { get; set; } + public TButton ButtonX { get; set; } + public TButton ButtonB { get; set; } + public TButton ButtonY { get; set; } + public TButton ButtonA { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/HideCursorMode.cs b/src/Ryujinx.Common/Configuration/HideCursorMode.cs index 895fc1076..f4480268f 100644 --- a/src/Ryujinx.Common/Configuration/HideCursorMode.cs +++ b/src/Ryujinx.Common/Configuration/HideCursorMode.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Common.Configuration { Never, OnIdle, - Always + Always, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/ScalingFilter.cs b/src/Ryujinx.Common/Configuration/ScalingFilter.cs index e38c7d735..1c6a74b3a 100644 --- a/src/Ryujinx.Common/Configuration/ScalingFilter.cs +++ b/src/Ryujinx.Common/Configuration/ScalingFilter.cs @@ -8,6 +8,6 @@ namespace Ryujinx.Common.Configuration { Bilinear, Nearest, - Fsr + Fsr, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs b/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs index ea208e9c2..17a13bf15 100644 --- a/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs +++ b/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Common.Configuration { public struct TitleUpdateMetadata { - public string Selected { get; set; } - public List<string> Paths { get; set; } + public string Selected { get; set; } + public List<string> Paths { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs index 5b661b878..4cfd232c7 100644 --- a/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs +++ b/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Common.Configuration public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/src/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs index 21da6fc01..3b82f96a2 100644 --- a/src/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs +++ b/src/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -7,8 +6,7 @@ namespace Ryujinx.Common { public static class BinaryReaderExtensions { - public unsafe static T ReadStruct<T>(this BinaryReader reader) - where T : unmanaged + public static T ReadStruct<T>(this BinaryReader reader) where T : unmanaged { return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0]; } diff --git a/src/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs b/src/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs index fddc8c1b7..c96bc698f 100644 --- a/src/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs +++ b/src/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs @@ -6,8 +6,7 @@ namespace Ryujinx.Common { public static class BinaryWriterExtensions { - public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value) - where T : unmanaged + public static void WriteStruct<T>(this BinaryWriter writer, T value) where T : unmanaged { ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); diff --git a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs index 6bbff2de1..4a71ce70b 100644 --- a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs +++ b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvapiUnicodeString.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Common.GraphicsDriver.NVAPI Set(text); } - public string Get() + public readonly string Get() { fixed (byte* data = _data) { @@ -29,7 +29,7 @@ namespace Ryujinx.Common.GraphicsDriver.NVAPI } } - public void Set(string text) + public readonly void Set(string text) { text += '\0'; fixed (char* textPtr = text) diff --git a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs index 8b472cd16..78e1b6952 100644 --- a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs +++ b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsApplicationV4.cs @@ -3,7 +3,7 @@ namespace Ryujinx.Common.GraphicsDriver.NVAPI { [StructLayout(LayoutKind.Sequential, Pack = 4)] - unsafe struct NvdrsApplicationV4 + struct NvdrsApplicationV4 { public uint Version; public uint IsPredefined; diff --git a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs index f1bfee821..6d9b8e51b 100644 --- a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs +++ b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsProfile.cs @@ -3,7 +3,7 @@ namespace Ryujinx.Common.GraphicsDriver.NVAPI { [StructLayout(LayoutKind.Sequential, Pack = 1)] - unsafe struct NvdrsProfile + struct NvdrsProfile { public uint Version; public NvapiUnicodeString ProfileName; diff --git a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs index ac188b35d..6cd0360dd 100644 --- a/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs +++ b/src/Ryujinx.Common/GraphicsDriver/NVAPI/NvdrsSetting.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Common.GraphicsDriver.NVAPI } [StructLayout(LayoutKind.Explicit, Size = 0x3020)] - unsafe struct NvdrsSetting + struct NvdrsSetting { [FieldOffset(0x0)] public uint Version; diff --git a/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs b/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs index c5be6e376..cda889d96 100644 --- a/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs +++ b/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs @@ -97,27 +97,26 @@ namespace Ryujinx.Common.GraphicsDriver Check(NvAPI_DRS_LoadSettings(handle)); - IntPtr profileHandle; - // Check if the profile already exists. - int status = NvAPI_DRS_FindProfileByName(handle, new NvapiUnicodeString(ProfileName), out profileHandle); + int status = NvAPI_DRS_FindProfileByName(handle, new NvapiUnicodeString(ProfileName), out nint profileHandle); if (status != 0) { - NvdrsProfile profile = new NvdrsProfile { - Version = MakeVersion<NvdrsProfile>(1), - IsPredefined = 0, - GpuSupport = uint.MaxValue + NvdrsProfile profile = new() + { + Version = MakeVersion<NvdrsProfile>(1), + IsPredefined = 0, + GpuSupport = uint.MaxValue, }; profile.ProfileName.Set(ProfileName); Check(NvAPI_DRS_CreateProfile(handle, ref profile, out profileHandle)); - NvdrsApplicationV4 application = new NvdrsApplicationV4 + NvdrsApplicationV4 application = new() { Version = MakeVersion<NvdrsApplicationV4>(4), IsPredefined = 0, - Flags = 3 // IsMetro, IsCommandLine + Flags = 3, // IsMetro, IsCommandLine }; application.AppName.Set("Ryujinx.exe"); application.UserFriendlyName.Set("Ryujinx"); @@ -127,7 +126,7 @@ namespace Ryujinx.Common.GraphicsDriver Check(NvAPI_DRS_CreateApplication(handle, profileHandle, ref application)); } - NvdrsSetting setting = new NvdrsSetting + NvdrsSetting setting = new() { Version = MakeVersion<NvdrsSetting>(1), SettingId = Nvapi.OglThreadControlId, @@ -136,7 +135,7 @@ namespace Ryujinx.Common.GraphicsDriver IsCurrentPredefined = 0, IsPredefinedValid = 0, CurrentValue = targetValue, - PredefinedValue = targetValue + PredefinedValue = targetValue, }; Check(NvAPI_DRS_SetSetting(handle, profileHandle, ref setting)); @@ -154,10 +153,8 @@ namespace Ryujinx.Common.GraphicsDriver { return Marshal.GetDelegateForFunctionPointer<T>(ptr); } - else - { - return null; - } + + return null; } } } diff --git a/src/Ryujinx.Common/Hash128.cs b/src/Ryujinx.Common/Hash128.cs index 04457bd0d..28c0946b3 100644 --- a/src/Ryujinx.Common/Hash128.cs +++ b/src/Ryujinx.Common/Hash128.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Common High = high; } - public override string ToString() + public readonly override string ToString() { return $"{High:x16}{Low:x16}"; } @@ -30,17 +30,17 @@ namespace Ryujinx.Common return !x.Equals(y); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is Hash128 hash128 && Equals(hash128); } - public bool Equals(Hash128 cmpObj) + public readonly bool Equals(Hash128 cmpObj) { return Low == cmpObj.Low && High == cmpObj.High; } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(Low, High); } diff --git a/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs index 3769b03a7..ed2a8144b 100644 --- a/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs +++ b/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs @@ -5,11 +5,11 @@ namespace Ryujinx.Common.Logging.Formatters { internal class DefaultLogFormatter : ILogFormatter { - private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); + private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>(); public string Format(LogEventArgs args) { - StringBuilder sb = StringBuilderPool.Allocate(); + StringBuilder sb = _stringBuilderPool.Allocate(); try { @@ -44,8 +44,8 @@ namespace Ryujinx.Common.Logging.Formatters } finally { - StringBuilderPool.Release(sb); + _stringBuilderPool.Release(sb); } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs index 6e3b0043d..1ee73acd6 100644 --- a/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs +++ b/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Common.Logging.Formatters { internal static class DynamicObjectFormatter { - private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>(); + private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>(); public static string? Format(object? dynamicObject) { @@ -16,7 +16,7 @@ namespace Ryujinx.Common.Logging.Formatters return null; } - StringBuilder sb = StringBuilderPool.Allocate(); + StringBuilder sb = _stringBuilderPool.Allocate(); try { @@ -26,7 +26,7 @@ namespace Ryujinx.Common.Logging.Formatters } finally { - StringBuilderPool.Release(sb); + _stringBuilderPool.Release(sb); } } @@ -48,7 +48,7 @@ namespace Ryujinx.Common.Logging.Formatters if (typeof(Array).IsAssignableFrom(prop.PropertyType)) { - Array? array = (Array?) prop.GetValue(dynamicObject); + Array? array = (Array?)prop.GetValue(dynamicObject); if (array is not null) { @@ -81,4 +81,4 @@ namespace Ryujinx.Common.Logging.Formatters sb.Append('}'); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs index 25a06d831..5c660a52b 100644 --- a/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs +++ b/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs @@ -4,4 +4,4 @@ { string Format(LogEventArgs args); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/LogClass.cs b/src/Ryujinx.Common/Logging/LogClass.cs index e62676cd3..f277dd06d 100644 --- a/src/Ryujinx.Common/Logging/LogClass.cs +++ b/src/Ryujinx.Common/Logging/LogClass.cs @@ -71,6 +71,6 @@ namespace Ryujinx.Common.Logging SurfaceFlinger, TamperMachine, Ui, - Vic + Vic, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/LogEventArgs.cs b/src/Ryujinx.Common/Logging/LogEventArgs.cs index a27af7809..264f8f3a2 100644 --- a/src/Ryujinx.Common/Logging/LogEventArgs.cs +++ b/src/Ryujinx.Common/Logging/LogEventArgs.cs @@ -6,18 +6,18 @@ namespace Ryujinx.Common.Logging { public readonly LogLevel Level; public readonly TimeSpan Time; - public readonly string ThreadName; + public readonly string ThreadName; public readonly string Message; public readonly object Data; public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null) { - Level = level; - Time = time; + Level = level; + Time = time; ThreadName = threadName; - Message = message; - Data = data; + Message = message; + Data = data; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/LogEventArgsJson.cs b/src/Ryujinx.Common/Logging/LogEventArgsJson.cs index 5203b17a6..9628a757c 100644 --- a/src/Ryujinx.Common/Logging/LogEventArgsJson.cs +++ b/src/Ryujinx.Common/Logging/LogEventArgsJson.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Common.Logging { public LogLevel Level { get; } public TimeSpan Time { get; } - public string ThreadName { get; } + public string ThreadName { get; } public string Message { get; } public string Data { get; } @@ -16,11 +16,11 @@ namespace Ryujinx.Common.Logging [JsonConstructor] public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null) { - Level = level; - Time = time; + Level = level; + Time = time; ThreadName = threadName; - Message = message; - Data = data; + Message = message; + Data = data; } public static LogEventArgsJson FromLogEventArgs(LogEventArgs args) @@ -28,4 +28,4 @@ namespace Ryujinx.Common.Logging return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs b/src/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs index da21f11e8..c5dd6114e 100644 --- a/src/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs +++ b/src/Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Common.Logging internal partial class LogEventJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs index 25f9c5eee..f03a7fd8f 100644 --- a/src/Ryujinx.Common/Logging/Logger.cs +++ b/src/Ryujinx.Common/Logging/Logger.cs @@ -10,11 +10,11 @@ namespace Ryujinx.Common.Logging { public static class Logger { - private static readonly Stopwatch m_Time; + private static readonly Stopwatch _time; - private static readonly bool[] m_EnabledClasses; + private static readonly bool[] _enabledClasses; - private static readonly List<ILogTarget> m_LogTargets; + private static readonly List<ILogTarget> _logTargets; private static readonly StdErrAdapter _stdErrAdapter; @@ -32,27 +32,27 @@ namespace Ryujinx.Common.Logging [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrintMsg(LogClass logClass, string message) { - if (m_EnabledClasses[(int)logClass]) + if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, "", message))); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, "", message))); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Print(LogClass logClass, string message, [CallerMemberName] string caller = "") { - if (m_EnabledClasses[(int)logClass]) + if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message))); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message))); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Print(LogClass logClass, string message, object data, [CallerMemberName] string caller = "") { - if (m_EnabledClasses[(int)logClass]) + if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), data)); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), data)); } } @@ -60,71 +60,71 @@ namespace Ryujinx.Common.Logging [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrintStack(LogClass logClass, string message, [CallerMemberName] string caller = "") { - if (m_EnabledClasses[(int)logClass]) + if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), new StackTrace(true))); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), new StackTrace(true))); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "") { - if (m_EnabledClasses[(int)logClass]) + if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message))); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message))); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrintStub(LogClass logClass, object data, [CallerMemberName] string caller = "") { - if (m_EnabledClasses[(int)logClass]) + if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed."), data)); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed."), data)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrintStub(LogClass logClass, string message, object data, [CallerMemberName] string caller = "") { - if (m_EnabledClasses[(int)logClass]) + if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message), data)); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message), data)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrintRawMsg(string message) { - Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, message)); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, message)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string FormatMessage(LogClass Class, string Caller, string Message) => $"{Class} {Caller}: {Message}"; + private static string FormatMessage(LogClass logClass, string caller, string message) => $"{logClass} {caller}: {message}"; } - public static Log? Debug { get; private set; } - public static Log? Info { get; private set; } - public static Log? Warning { get; private set; } - public static Log? Error { get; private set; } - public static Log? Guest { get; private set; } + public static Log? Debug { get; private set; } + public static Log? Info { get; private set; } + public static Log? Warning { get; private set; } + public static Log? Error { get; private set; } + public static Log? Guest { get; private set; } public static Log? AccessLog { get; private set; } - public static Log? Stub { get; private set; } - public static Log? Trace { get; private set; } - public static Log Notice { get; } // Always enabled + public static Log? Stub { get; private set; } + public static Log? Trace { get; private set; } + public static Log Notice { get; } // Always enabled static Logger() { - m_EnabledClasses = new bool[Enum.GetNames<LogClass>().Length]; + _enabledClasses = new bool[Enum.GetNames<LogClass>().Length]; - for (int index = 0; index < m_EnabledClasses.Length; index++) + for (int index = 0; index < _enabledClasses.Length; index++) { - m_EnabledClasses[index] = true; + _enabledClasses[index] = true; } - m_LogTargets = new List<ILogTarget>(); + _logTargets = new List<ILogTarget>(); - m_Time = Stopwatch.StartNew(); + _time = Stopwatch.StartNew(); // Logger should log to console by default AddTarget(new AsyncLogTargetWrapper( @@ -145,12 +145,12 @@ namespace Ryujinx.Common.Logging public static void RestartTime() { - m_Time.Restart(); + _time.Restart(); } private static ILogTarget GetTarget(string targetName) { - foreach (var target in m_LogTargets) + foreach (var target in _logTargets) { if (target.Name.Equals(targetName)) { @@ -163,7 +163,7 @@ namespace Ryujinx.Common.Logging public static void AddTarget(ILogTarget target) { - m_LogTargets.Add(target); + _logTargets.Add(target); Updated += target.Log; } @@ -176,7 +176,7 @@ namespace Ryujinx.Common.Logging { Updated -= logTarget.Log; - m_LogTargets.Remove(logTarget); + _logTargets.Remove(logTarget); logTarget.Dispose(); } @@ -188,18 +188,18 @@ namespace Ryujinx.Common.Logging _stdErrAdapter.Dispose(); - foreach (var target in m_LogTargets) + foreach (var target in _logTargets) { target.Dispose(); } - m_LogTargets.Clear(); + _logTargets.Clear(); } public static IReadOnlyCollection<LogLevel> GetEnabledLevels() { - var logs = new Log?[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace }; - List<LogLevel> levels = new List<LogLevel>(logs.Length); + var logs = new[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace }; + List<LogLevel> levels = new(logs.Length); foreach (var log in logs) { if (log.HasValue) @@ -215,21 +215,23 @@ namespace Ryujinx.Common.Logging { switch (logLevel) { - case LogLevel.Debug : Debug = enabled ? new Log(LogLevel.Debug) : new Log?(); break; - case LogLevel.Info : Info = enabled ? new Log(LogLevel.Info) : new Log?(); break; - case LogLevel.Warning : Warning = enabled ? new Log(LogLevel.Warning) : new Log?(); break; - case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : new Log?(); break; - case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : new Log?(); break; - case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog): new Log?(); break; - case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : new Log?(); break; - case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : new Log?(); break; +#pragma warning disable IDE0055 // Disable formatting + case LogLevel.Debug : Debug = enabled ? new Log(LogLevel.Debug) : new Log?(); break; + case LogLevel.Info : Info = enabled ? new Log(LogLevel.Info) : new Log?(); break; + case LogLevel.Warning : Warning = enabled ? new Log(LogLevel.Warning) : new Log?(); break; + case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : new Log?(); break; + case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : new Log?(); break; + case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : new Log?(); break; + case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : new Log?(); break; + case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : new Log?(); break; default: throw new ArgumentException("Unknown Log Level"); +#pragma warning restore IDE0055 } } public static void SetEnable(LogClass logClass, bool enabled) { - m_EnabledClasses[(int)logClass] = enabled; + _enabledClasses[(int)logClass] = enabled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs index ddc547acd..226318b0d 100644 --- a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs +++ b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs @@ -14,16 +14,16 @@ namespace Ryujinx.Common.Logging.Targets /// <summary> /// Discard the overflowing item /// </summary> - Discard = 1 + Discard = 1, } public class AsyncLogTargetWrapper : ILogTarget { - private ILogTarget _target; + private readonly ILogTarget _target; - private Thread _messageThread; + private readonly Thread _messageThread; - private BlockingCollection<LogEventArgs> _messageQueue; + private readonly BlockingCollection<LogEventArgs> _messageQueue; private readonly int _overflowTimeout; @@ -35,11 +35,12 @@ namespace Ryujinx.Common.Logging.Targets public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction) { - _target = target; - _messageQueue = new BlockingCollection<LogEventArgs>(queueLimit); + _target = target; + _messageQueue = new BlockingCollection<LogEventArgs>(queueLimit); _overflowTimeout = overflowAction == AsyncLogTargetOverflowAction.Block ? -1 : 0; - _messageThread = new Thread(() => { + _messageThread = new Thread(() => + { while (!_messageQueue.IsCompleted) { try @@ -55,10 +56,11 @@ namespace Ryujinx.Common.Logging.Targets // on the next iteration. } } - }); - - _messageThread.Name = "Logger.MessageThread"; - _messageThread.IsBackground = true; + }) + { + Name = "Logger.MessageThread", + IsBackground = true, + }; _messageThread.Start(); } @@ -72,8 +74,9 @@ namespace Ryujinx.Common.Logging.Targets public void Dispose() { + GC.SuppressFinalize(this); _messageQueue.CompleteAdding(); _messageThread.Join(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs index b5986461e..16735834a 100644 --- a/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs @@ -11,20 +11,21 @@ namespace Ryujinx.Common.Logging.Targets string ILogTarget.Name { get => _name; } - private static ConsoleColor GetLogColor(LogLevel level) => level switch { - LogLevel.Info => ConsoleColor.White, + private static ConsoleColor GetLogColor(LogLevel level) => level switch + { + LogLevel.Info => ConsoleColor.White, LogLevel.Warning => ConsoleColor.Yellow, - LogLevel.Error => ConsoleColor.Red, - LogLevel.Stub => ConsoleColor.DarkGray, - LogLevel.Notice => ConsoleColor.Cyan, - LogLevel.Trace => ConsoleColor.DarkCyan, - _ => ConsoleColor.Gray, + LogLevel.Error => ConsoleColor.Red, + LogLevel.Stub => ConsoleColor.DarkGray, + LogLevel.Notice => ConsoleColor.Cyan, + LogLevel.Trace => ConsoleColor.DarkCyan, + _ => ConsoleColor.Gray, }; public ConsoleLogTarget(string name) { _formatter = new DefaultLogFormatter(); - _name = name; + _name = name; } public void Log(object sender, LogEventArgs args) @@ -36,7 +37,8 @@ namespace Ryujinx.Common.Logging.Targets public void Dispose() { + GC.SuppressFinalize(this); Console.ResetColor(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs index 2cc4a8237..8d1a94e51 100644 --- a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs @@ -7,9 +7,9 @@ namespace Ryujinx.Common.Logging.Targets { public class FileLogTarget : ILogTarget { - private readonly StreamWriter _logWriter; + private readonly StreamWriter _logWriter; private readonly ILogFormatter _formatter; - private readonly string _name; + private readonly string _name; string ILogTarget.Name { get => _name; } @@ -20,7 +20,7 @@ namespace Ryujinx.Common.Logging.Targets public FileLogTarget(string path, string name, FileShare fileShare, FileMode fileMode) { // Ensure directory is present - DirectoryInfo logDir = new DirectoryInfo(Path.Combine(path, "Logs")); + DirectoryInfo logDir = new(Path.Combine(path, "Logs")); logDir.Create(); // Clean up old logs, should only keep 3 @@ -33,9 +33,9 @@ namespace Ryujinx.Common.Logging.Targets string version = ReleaseInformation.GetVersion(); // Get path for the current time - path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.log"); + path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"); - _name = name; + _name = name; _logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare)); _formatter = new DefaultLogFormatter(); } @@ -48,6 +48,7 @@ namespace Ryujinx.Common.Logging.Targets public void Dispose() { + GC.SuppressFinalize(this); _logWriter.WriteLine("---- End of Log ----"); _logWriter.Flush(); _logWriter.Dispose(); diff --git a/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs index ae264e593..1873dc606 100644 --- a/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -1,25 +1,26 @@ using Ryujinx.Common.Utilities; +using System; using System.IO; namespace Ryujinx.Common.Logging.Targets { public class JsonLogTarget : ILogTarget { - private Stream _stream; - private bool _leaveOpen; - private string _name; + private readonly Stream _stream; + private readonly bool _leaveOpen; + private readonly string _name; string ILogTarget.Name { get => _name; } public JsonLogTarget(Stream stream, string name) { _stream = stream; - _name = name; + _name = name; } public JsonLogTarget(Stream stream, bool leaveOpen) { - _stream = stream; + _stream = stream; _leaveOpen = leaveOpen; } @@ -31,6 +32,7 @@ namespace Ryujinx.Common.Logging.Targets public void Dispose() { + GC.SuppressFinalize(this); if (!_leaveOpen) { _stream.Dispose(); diff --git a/src/Ryujinx.Common/Memory/ArrayPtr.cs b/src/Ryujinx.Common/Memory/ArrayPtr.cs index 9e95f75ee..0365a5089 100644 --- a/src/Ryujinx.Common/Memory/ArrayPtr.cs +++ b/src/Ryujinx.Common/Memory/ArrayPtr.cs @@ -16,12 +16,12 @@ namespace Ryujinx.Common.Memory /// <summary> /// Null pointer. /// </summary> - public static ArrayPtr<T> Null => new ArrayPtr<T>() { _ptr = IntPtr.Zero }; + public static ArrayPtr<T> Null => new() { _ptr = IntPtr.Zero }; /// <summary> /// True if the pointer is null, false otherwise. /// </summary> - public bool IsNull => _ptr == IntPtr.Zero; + public readonly bool IsNull => _ptr == IntPtr.Zero; /// <summary> /// Number of elements on the array. @@ -37,7 +37,7 @@ namespace Ryujinx.Common.Memory /// </remarks> /// <param name="index">Index of the element</param> /// <returns>Reference to the element at the given index</returns> - public ref T this[int index] => ref Unsafe.AsRef<T>((T*)_ptr + index); + public readonly ref T this[int index] => ref Unsafe.AsRef<T>((T*)_ptr + index); /// <summary> /// Creates a new array from a given reference. @@ -81,7 +81,7 @@ namespace Ryujinx.Common.Memory /// </summary> /// <param name="start">Index where the new array should start</param> /// <returns>New array starting at the specified position</returns> - public ArrayPtr<T> Slice(int start) => new ArrayPtr<T>(ref this[start], Length - start); + public ArrayPtr<T> Slice(int start) => new(ref this[start], Length - start); /// <summary> /// Gets a span from the array. @@ -93,19 +93,19 @@ namespace Ryujinx.Common.Memory /// Gets the array base pointer. /// </summary> /// <returns>Base pointer</returns> - public T* ToPointer() => (T*)_ptr; + public readonly T* ToPointer() => (T*)_ptr; - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is ArrayPtr<T> other && Equals(other); } - public bool Equals([AllowNull] ArrayPtr<T> other) + public readonly bool Equals([AllowNull] ArrayPtr<T> other) { return _ptr == other._ptr && Length == other.Length; } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(_ptr, Length); } diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs index eda350bdd..8f2a61c85 100644 --- a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs +++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Common.Memory get { byte[] array = _array; - + ObjectDisposedException.ThrowIf(array is null, this); return new Memory<byte>(array, 0, _length); @@ -40,7 +40,7 @@ namespace Ryujinx.Common.Memory public void Dispose() { var array = Interlocked.Exchange(ref _array, null); - + if (array != null) { ArrayPool<byte>.Shared.Return(array); diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs index 2910f408f..b63449f83 100644 --- a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs +++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Common.Memory /// </summary> public sealed partial class ByteMemoryPool { - private static readonly ByteMemoryPool _shared = new ByteMemoryPool(); + private static readonly ByteMemoryPool _shared = new(); /// <summary> /// Constructs a <see cref="ByteMemoryPool"/> instance. Private to force access through @@ -27,7 +27,7 @@ namespace Ryujinx.Common.Memory /// <summary> /// Returns the maximum buffer size supported by this pool. /// </summary> - public int MaxBufferSize => Array.MaxLength; + public static int MaxBufferSize => Array.MaxLength; /// <summary> /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. @@ -36,7 +36,7 @@ namespace Ryujinx.Common.Memory /// <param name="length">The buffer's required length in bytes</param> /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> - public IMemoryOwner<byte> Rent(long length) + public static IMemoryOwner<byte> Rent(long length) => RentImpl(checked((int)length)); /// <summary> @@ -46,7 +46,7 @@ namespace Ryujinx.Common.Memory /// <param name="length">The buffer's required length in bytes</param> /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> - public IMemoryOwner<byte> Rent(ulong length) + public static IMemoryOwner<byte> Rent(ulong length) => RentImpl(checked((int)length)); /// <summary> @@ -56,7 +56,7 @@ namespace Ryujinx.Common.Memory /// <param name="length">The buffer's required length in bytes</param> /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> - public IMemoryOwner<byte> Rent(int length) + public static IMemoryOwner<byte> Rent(int length) => RentImpl(length); /// <summary> @@ -66,7 +66,7 @@ namespace Ryujinx.Common.Memory /// <param name="length">The buffer's required length in bytes</param> /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> - public IMemoryOwner<byte> RentCleared(long length) + public static IMemoryOwner<byte> RentCleared(long length) => RentCleared(checked((int)length)); /// <summary> @@ -76,7 +76,7 @@ namespace Ryujinx.Common.Memory /// <param name="length">The buffer's required length in bytes</param> /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> - public IMemoryOwner<byte> RentCleared(ulong length) + public static IMemoryOwner<byte> RentCleared(ulong length) => RentCleared(checked((int)length)); /// <summary> @@ -86,12 +86,12 @@ namespace Ryujinx.Common.Memory /// <param name="length">The buffer's required length in bytes</param> /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> - public IMemoryOwner<byte> RentCleared(int length) + public static IMemoryOwner<byte> RentCleared(int length) { var buffer = RentImpl(length); - + buffer.Memory.Span.Clear(); - + return buffer; } diff --git a/src/Ryujinx.Common/Memory/MemoryStreamManager.cs b/src/Ryujinx.Common/Memory/MemoryStreamManager.cs index 68b829993..cc3a59682 100644 --- a/src/Ryujinx.Common/Memory/MemoryStreamManager.cs +++ b/src/Ryujinx.Common/Memory/MemoryStreamManager.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Common.Memory { public static class MemoryStreamManager { - private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager(); + private static readonly RecyclableMemoryStreamManager _shared = new(); /// <summary> /// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x @@ -19,7 +19,7 @@ namespace Ryujinx.Common.Memory /// </summary> /// <returns>A <c>RecyclableMemoryStream</c></returns> public static RecyclableMemoryStream GetStream() - => new RecyclableMemoryStream(_shared); + => new(_shared); /// <summary> /// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs index 78eeb16fb..03d48fd8d 100644 --- a/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs @@ -1,6 +1,5 @@ using System.Runtime.InteropServices; using System.Threading; - using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; namespace Ryujinx.Common.Memory.PartialUnmaps @@ -14,15 +13,15 @@ namespace Ryujinx.Common.Memory.PartialUnmaps public int WriteLock; public int ReaderCount; - public static int WriteLockOffset; - public static int ReaderCountOffset; + public static readonly int WriteLockOffset; + public static readonly int ReaderCountOffset; /// <summary> /// Populates the field offsets for use when emitting native code. /// </summary> static NativeReaderWriterLock() { - NativeReaderWriterLock instance = new NativeReaderWriterLock(); + NativeReaderWriterLock instance = new(); WriteLockOffset = OffsetOf(ref instance, ref instance.WriteLock); ReaderCountOffset = OffsetOf(ref instance, ref instance.ReaderCount); @@ -35,7 +34,9 @@ namespace Ryujinx.Common.Memory.PartialUnmaps { // Must take write lock for a very short time to become a reader. - while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0) { } + while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0) + { + } Interlocked.Increment(ref ReaderCount); @@ -60,11 +61,15 @@ namespace Ryujinx.Common.Memory.PartialUnmaps Interlocked.Decrement(ref ReaderCount); - while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0) { } + while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0) + { + } // Wait for reader count to drop to 0, then take the lock again as the only reader. - while (Interlocked.CompareExchange(ref ReaderCount, 1, 0) != 0) { } + while (Interlocked.CompareExchange(ref ReaderCount, 1, 0) != 0) + { + } } /// <summary> diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs index 5b0bc07ec..a583930b7 100644 --- a/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs @@ -1,10 +1,8 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; using System.Runtime.Versioning; using System.Threading; - using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; namespace Ryujinx.Common.Memory.PartialUnmaps @@ -35,7 +33,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] + [return: MarshalAs(UnmanagedType.Bool)] private static partial bool CloseHandle(IntPtr hObject); [SupportedOSPlatform("windows")] @@ -48,7 +46,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps /// </summary> static unsafe PartialUnmapState() { - PartialUnmapState instance = new PartialUnmapState(); + PartialUnmapState instance = new(); PartialUnmapLockOffset = OffsetOf(ref instance, ref instance.PartialUnmapLock); PartialUnmapsCountOffset = OffsetOf(ref instance, ref instance.PartialUnmapsCount); @@ -160,4 +158,4 @@ namespace Ryujinx.Common.Memory.PartialUnmaps } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs index a3bd5be85..a3c3dc57b 100644 --- a/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs @@ -1,6 +1,5 @@ using System.Runtime.InteropServices; using System.Threading; - using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; namespace Ryujinx.Common.Memory.PartialUnmaps @@ -18,15 +17,15 @@ namespace Ryujinx.Common.Memory.PartialUnmaps public Array20<int> ThreadIds; public Array20<T> Structs; - public static int ThreadIdsOffset; - public static int StructsOffset; + public static readonly int ThreadIdsOffset; + public static readonly int StructsOffset; /// <summary> /// Populates the field offsets for use when emitting native code. /// </summary> static ThreadLocalMap() { - ThreadLocalMap<T> instance = new ThreadLocalMap<T>(); + ThreadLocalMap<T> instance = new(); ThreadIdsOffset = OffsetOf(ref instance, ref instance.ThreadIds); StructsOffset = OffsetOf(ref instance, ref instance.Structs); diff --git a/src/Ryujinx.Common/Memory/Ptr.cs b/src/Ryujinx.Common/Memory/Ptr.cs index 66bcf569b..5285d756f 100644 --- a/src/Ryujinx.Common/Memory/Ptr.cs +++ b/src/Ryujinx.Common/Memory/Ptr.cs @@ -15,17 +15,17 @@ namespace Ryujinx.Common.Memory /// <summary> /// Null pointer. /// </summary> - public static Ptr<T> Null => new Ptr<T>() { _ptr = IntPtr.Zero }; + public static Ptr<T> Null => new() { _ptr = IntPtr.Zero }; /// <summary> /// True if the pointer is null, false otherwise. /// </summary> - public bool IsNull => _ptr == IntPtr.Zero; + public readonly bool IsNull => _ptr == IntPtr.Zero; /// <summary> /// Gets a reference to the value. /// </summary> - public ref T Value => ref Unsafe.AsRef<T>((void*)_ptr); + public readonly ref T Value => ref Unsafe.AsRef<T>((void*)_ptr); /// <summary> /// Creates a new pointer to an unmanaged resource. @@ -40,17 +40,17 @@ namespace Ryujinx.Common.Memory _ptr = (IntPtr)Unsafe.AsPointer(ref value); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is Ptr<T> other && Equals(other); } - public bool Equals([AllowNull] Ptr<T> other) + public readonly bool Equals([AllowNull] Ptr<T> other) { return _ptr == other._ptr; } - public override int GetHashCode() + public readonly override int GetHashCode() { return _ptr.GetHashCode(); } diff --git a/src/Ryujinx.Common/Memory/SpanReader.cs b/src/Ryujinx.Common/Memory/SpanReader.cs index 9a1a0a3f0..946439e31 100644 --- a/src/Ryujinx.Common/Memory/SpanReader.cs +++ b/src/Ryujinx.Common/Memory/SpanReader.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Common.Memory { private ReadOnlySpan<byte> _input; - public int Length => _input.Length; + public readonly int Length => _input.Length; public SpanReader(ReadOnlySpan<byte> input) { @@ -19,7 +19,7 @@ namespace Ryujinx.Common.Memory { T value = MemoryMarshal.Cast<byte, T>(_input)[0]; - _input = _input.Slice(Unsafe.SizeOf<T>()); + _input = _input[Unsafe.SizeOf<T>()..]; return value; } @@ -37,16 +37,16 @@ namespace Ryujinx.Common.Memory value = MemoryMarshal.Cast<byte, T>(_input)[0]; - _input = _input.Slice(valueSize); + _input = _input[valueSize..]; return true; } public ReadOnlySpan<byte> GetSpan(int size) { - ReadOnlySpan<byte> data = _input.Slice(0, size); + ReadOnlySpan<byte> data = _input[..size]; - _input = _input.Slice(size); + _input = _input[size..]; return data; } @@ -56,19 +56,19 @@ namespace Ryujinx.Common.Memory return GetSpan((int)Math.Min((uint)_input.Length, (uint)size)); } - public T ReadAt<T>(int offset) where T : unmanaged + public readonly T ReadAt<T>(int offset) where T : unmanaged { - return MemoryMarshal.Cast<byte, T>(_input.Slice(offset))[0]; + return MemoryMarshal.Cast<byte, T>(_input[offset..])[0]; } - public ReadOnlySpan<byte> GetSpanAt(int offset, int size) + public readonly ReadOnlySpan<byte> GetSpanAt(int offset, int size) { return _input.Slice(offset, size); } public void Skip(int size) { - _input = _input.Slice(size); + _input = _input[size..]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Memory/SpanWriter.cs b/src/Ryujinx.Common/Memory/SpanWriter.cs index 5c35569dd..248acc42b 100644 --- a/src/Ryujinx.Common/Memory/SpanWriter.cs +++ b/src/Ryujinx.Common/Memory/SpanWriter.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Common.Memory { private Span<byte> _output; - public int Length => _output.Length; + public readonly int Length => _output.Length; public SpanWriter(Span<byte> output) { @@ -18,28 +18,28 @@ namespace Ryujinx.Common.Memory public void Write<T>(T value) where T : unmanaged { MemoryMarshal.Cast<byte, T>(_output)[0] = value; - _output = _output.Slice(Unsafe.SizeOf<T>()); + _output = _output[Unsafe.SizeOf<T>()..]; } public void Write(ReadOnlySpan<byte> data) { - data.CopyTo(_output.Slice(0, data.Length)); - _output = _output.Slice(data.Length); + data.CopyTo(_output[..data.Length]); + _output = _output[data.Length..]; } - public void WriteAt<T>(int offset, T value) where T : unmanaged + public readonly void WriteAt<T>(int offset, T value) where T : unmanaged { - MemoryMarshal.Cast<byte, T>(_output.Slice(offset))[0] = value; + MemoryMarshal.Cast<byte, T>(_output[offset..])[0] = value; } - public void WriteAt(int offset, ReadOnlySpan<byte> data) + public readonly void WriteAt(int offset, ReadOnlySpan<byte> data) { data.CopyTo(_output.Slice(offset, data.Length)); } public void Skip(int size) { - _output = _output.Slice(size); + _output = _output[size..]; } } } diff --git a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs index a039d04e7..ae5853d1d 100644 --- a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs +++ b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs @@ -1,654 +1,658 @@ using System; using System.Runtime.InteropServices; +#pragma warning disable CS0169, IDE0051 // Remove unused private member namespace Ryujinx.Common.Memory { public struct Array1<T> : IArray<T> where T : unmanaged { T _e0; - public int Length => 1; + public readonly int Length => 1; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 1); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array2<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array1<T> _other; -#pragma warning restore CS0169 - public int Length => 2; + public readonly int Length => 2; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 2); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array3<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array2<T> _other; -#pragma warning restore CS0169 - public int Length => 3; + public readonly int Length => 3; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 3); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array4<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array3<T> _other; -#pragma warning restore CS0169 - public int Length => 4; + public readonly int Length => 4; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 4); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array5<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array4<T> _other; -#pragma warning restore CS0169 - public int Length => 5; + public readonly int Length => 5; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 5); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array6<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array5<T> _other; -#pragma warning restore CS0169 - public int Length => 6; + public readonly int Length => 6; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 6); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array7<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array6<T> _other; -#pragma warning restore CS0169 - public int Length => 7; + public readonly int Length => 7; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 7); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array8<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array7<T> _other; -#pragma warning restore CS0169 - public int Length => 8; + public readonly int Length => 8; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 8); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array9<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array8<T> _other; -#pragma warning restore CS0169 - public int Length => 9; + public readonly int Length => 9; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 9); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array10<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array9<T> _other; -#pragma warning restore CS0169 - public int Length => 10; + public readonly int Length => 10; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 10); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array11<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array10<T> _other; -#pragma warning restore CS0169 - public int Length => 11; + public readonly int Length => 11; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 11); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array12<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array11<T> _other; -#pragma warning restore CS0169 - public int Length => 12; + public readonly int Length => 12; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 12); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array13<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array12<T> _other; -#pragma warning restore CS0169 - public int Length => 13; + public readonly int Length => 13; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 13); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array14<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array13<T> _other; -#pragma warning restore CS0169 - public int Length => 14; + public readonly int Length => 14; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 14); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array15<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array14<T> _other; -#pragma warning restore CS0169 - public int Length => 15; + public readonly int Length => 15; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 15); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array16<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array15<T> _other; -#pragma warning restore CS0169 - public int Length => 16; + public readonly int Length => 16; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 16); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array17<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array16<T> _other; -#pragma warning restore CS0169 - public int Length => 17; + public readonly int Length => 17; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 17); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array18<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array17<T> _other; -#pragma warning restore CS0169 - public int Length => 18; + public readonly int Length => 18; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 18); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array19<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array18<T> _other; -#pragma warning restore CS0169 - public int Length => 19; + public readonly int Length => 19; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 19); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array20<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array19<T> _other; -#pragma warning restore CS0169 - public int Length => 20; + public readonly int Length => 20; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 20); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array21<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array20<T> _other; -#pragma warning restore CS0169 - public int Length => 21; + public readonly int Length => 21; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 21); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array22<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array21<T> _other; -#pragma warning restore CS0169 - public int Length => 22; + public readonly int Length => 22; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 22); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array23<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array22<T> _other; -#pragma warning restore CS0169 - public int Length => 23; + public readonly int Length => 23; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 23); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array24<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array23<T> _other; -#pragma warning restore CS0169 - public int Length => 24; + + public readonly int Length => 24; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 24); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array25<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array24<T> _other; -#pragma warning restore CS0169 - public int Length => 25; + + public readonly int Length => 25; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 25); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array26<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array25<T> _other; -#pragma warning restore CS0169 - public int Length => 26; + + public readonly int Length => 26; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 26); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array27<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array26<T> _other; -#pragma warning restore CS0169 - public int Length => 27; + + public readonly int Length => 27; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 27); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array28<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array27<T> _other; -#pragma warning restore CS0169 - public int Length => 28; + + public readonly int Length => 28; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 28); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array29<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array28<T> _other; -#pragma warning restore CS0169 - public int Length => 29; + + public readonly int Length => 29; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 29); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array30<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array29<T> _other; -#pragma warning restore CS0169 - public int Length => 30; + + public readonly int Length => 30; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 30); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array31<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array30<T> _other; -#pragma warning restore CS0169 - public int Length => 31; + + public readonly int Length => 31; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 31); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array32<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array31<T> _other; -#pragma warning restore CS0169 - public int Length => 32; + + public readonly int Length => 32; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 32); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array33<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array32<T> _other; -#pragma warning restore CS0169 - public int Length => 33; + + public readonly int Length => 33; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 33); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array34<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array33<T> _other; -#pragma warning restore CS0169 - public int Length => 34; + + public readonly int Length => 34; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 34); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array35<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array34<T> _other; -#pragma warning restore CS0169 - public int Length => 35; + + public readonly int Length => 35; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 35); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array36<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array35<T> _other; -#pragma warning restore CS0169 - public int Length => 36; + + public readonly int Length => 36; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 36); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array37<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array36<T> _other; -#pragma warning restore CS0169 - public int Length => 37; + + public readonly int Length => 37; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 37); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array38<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array37<T> _other; -#pragma warning restore CS0169 - public int Length => 38; + + public readonly int Length => 38; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 38); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array39<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array38<T> _other; -#pragma warning restore CS0169 - public int Length => 39; + + public readonly int Length => 39; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 39); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array40<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array39<T> _other; -#pragma warning restore CS0169 - public int Length => 40; + + public readonly int Length => 40; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 40); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array41<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array40<T> _other; -#pragma warning restore CS0169 - public int Length => 41; + + public readonly int Length => 41; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 41); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array42<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array41<T> _other; -#pragma warning restore CS0169 - public int Length => 42; + + public readonly int Length => 42; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 42); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array43<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array42<T> _other; -#pragma warning restore CS0169 - public int Length => 43; + + public readonly int Length => 43; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 43); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array44<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array43<T> _other; -#pragma warning restore CS0169 - public int Length => 44; + + public readonly int Length => 44; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 44); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array45<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array44<T> _other; -#pragma warning restore CS0169 - public int Length => 45; + + public readonly int Length => 45; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 45); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array46<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array45<T> _other; -#pragma warning restore CS0169 - public int Length => 46; + + public readonly int Length => 46; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 46); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array47<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array46<T> _other; -#pragma warning restore CS0169 - public int Length => 47; + + public readonly int Length => 47; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 47); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array48<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array47<T> _other; -#pragma warning restore CS0169 - public int Length => 48; + + public readonly int Length => 48; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 48); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array49<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array48<T> _other; -#pragma warning restore CS0169 - public int Length => 49; + + public readonly int Length => 49; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 49); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array50<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array49<T> _other; -#pragma warning restore CS0169 - public int Length => 50; + + public readonly int Length => 50; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 50); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array51<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array50<T> _other; -#pragma warning restore CS0169 - public int Length => 51; + + public readonly int Length => 51; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 51); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array52<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array51<T> _other; -#pragma warning restore CS0169 - public int Length => 52; + + public readonly int Length => 52; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 52); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array53<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array52<T> _other; -#pragma warning restore CS0169 - public int Length => 53; + + public readonly int Length => 53; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 53); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array54<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array53<T> _other; -#pragma warning restore CS0169 - public int Length => 54; + + public readonly int Length => 54; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 54); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array55<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array54<T> _other; -#pragma warning restore CS0169 - public int Length => 55; + + public readonly int Length => 55; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 55); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array56<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array55<T> _other; -#pragma warning restore CS0169 - public int Length => 56; + + public readonly int Length => 56; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 56); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array57<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array56<T> _other; -#pragma warning restore CS0169 - public int Length => 57; + + public readonly int Length => 57; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 57); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array58<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array57<T> _other; -#pragma warning restore CS0169 - public int Length => 58; + + public readonly int Length => 58; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 58); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array59<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array58<T> _other; -#pragma warning restore CS0169 - public int Length => 59; + + public readonly int Length => 59; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 59); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array60<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array59<T> _other; -#pragma warning restore CS0169 - public int Length => 60; + public readonly int Length => 60; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 60); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array61<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array60<T> _other; -#pragma warning restore CS0169 - public int Length => 61; + public readonly int Length => 61; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 61); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array62<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array61<T> _other; -#pragma warning restore CS0169 - public int Length => 62; + public readonly int Length => 62; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 62); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array63<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array62<T> _other; -#pragma warning restore CS0169 - public int Length => 63; + public readonly int Length => 63; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 63); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array64<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array63<T> _other; -#pragma warning restore CS0169 - public int Length => 64; + public readonly int Length => 64; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 64); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array73<T> : IArray<T> where T : unmanaged { -#pragma warning disable CS0169 T _e0; Array64<T> _other; Array8<T> _other2; -#pragma warning restore CS0169 - public int Length => 73; + public readonly int Length => 73; public ref T this[int index] => ref AsSpan()[index]; - public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 73); + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); + } + + public struct Array127<T> : IArray<T> where T : unmanaged + { + T _e0; + Array64<T> _other; + Array62<T> _other2; + public readonly int Length => 127; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); + } + + public struct Array128<T> : IArray<T> where T : unmanaged + { + T _e0; + Array64<T> _other; + Array63<T> _other2; + public readonly int Length => 128; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); + } + + public struct Array256<T> : IArray<T> where T : unmanaged + { + T _e0; + Array128<T> _other; + Array127<T> _other2; + public readonly int Length => 256; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } } +#pragma warning restore CS0169, IDE0051 diff --git a/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs index 8693f5b84..f0f452730 100644 --- a/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs +++ b/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Common.Memory byte _element; - public int Length => Size; + public readonly int Length => Size; public ref byte this[int index] => ref AsSpan()[index]; public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); } @@ -22,7 +22,7 @@ namespace Ryujinx.Common.Memory byte _element; - public int Length => Size; + public readonly int Length => Size; public ref byte this[int index] => ref AsSpan()[index]; public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); } @@ -34,7 +34,7 @@ namespace Ryujinx.Common.Memory byte _element; - public int Length => Size; + public readonly int Length => Size; public ref byte this[int index] => ref AsSpan()[index]; public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); } @@ -46,7 +46,7 @@ namespace Ryujinx.Common.Memory byte _element; - public int Length => Size; + public readonly int Length => Size; public ref byte this[int index] => ref AsSpan()[index]; public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); } @@ -58,7 +58,7 @@ namespace Ryujinx.Common.Memory byte _element; - public int Length => Size; + public readonly int Length => Size; public ref byte this[int index] => ref AsSpan()[index]; public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); } @@ -70,7 +70,7 @@ namespace Ryujinx.Common.Memory byte _element; - public int Length => Size; + public readonly int Length => Size; public ref byte this[int index] => ref AsSpan()[index]; public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); } diff --git a/src/Ryujinx.Common/PerformanceCounter.cs b/src/Ryujinx.Common/PerformanceCounter.cs index 97ee23a25..4b49c5c9e 100644 --- a/src/Ryujinx.Common/PerformanceCounter.cs +++ b/src/Ryujinx.Common/PerformanceCounter.cs @@ -1,10 +1,10 @@ -using System.Diagnostics; +using System.Diagnostics; namespace Ryujinx.Common { public static class PerformanceCounter { - private static double _ticksToNs; + private static readonly double _ticksToNs; /// <summary> /// Represents the number of ticks in 1 day. @@ -71,12 +71,12 @@ namespace Ryujinx.Common static PerformanceCounter() { TicksPerMillisecond = Stopwatch.Frequency / 1000; - TicksPerSecond = Stopwatch.Frequency; - TicksPerMinute = TicksPerSecond * 60; - TicksPerHour = TicksPerMinute * 60; - TicksPerDay = TicksPerHour * 24; + TicksPerSecond = Stopwatch.Frequency; + TicksPerMinute = TicksPerSecond * 60; + TicksPerHour = TicksPerMinute * 60; + TicksPerDay = TicksPerHour * 24; _ticksToNs = 1000000000.0 / Stopwatch.Frequency; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Pools/ObjectPool.cs b/src/Ryujinx.Common/Pools/ObjectPool.cs index e0bf5df60..a0d3feb95 100644 --- a/src/Ryujinx.Common/Pools/ObjectPool.cs +++ b/src/Ryujinx.Common/Pools/ObjectPool.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Common public ObjectPool(Func<T> factory, int size) { - _items = new T[size - 1]; + _items = new T[size - 1]; _factory = factory; } diff --git a/src/Ryujinx.Common/Pools/SharedPools.cs b/src/Ryujinx.Common/Pools/SharedPools.cs index b4860b85b..272a74180 100644 --- a/src/Ryujinx.Common/Pools/SharedPools.cs +++ b/src/Ryujinx.Common/Pools/SharedPools.cs @@ -5,7 +5,7 @@ private static class DefaultPool<T> where T : class, new() { - public static readonly ObjectPool<T> Instance = new ObjectPool<T>(() => new T(), 20); + public static readonly ObjectPool<T> Instance = new(() => new T(), 20); } public static ObjectPool<T> Default<T>() diff --git a/src/Ryujinx.Common/Pools/ThreadStaticArray.cs b/src/Ryujinx.Common/Pools/ThreadStaticArray.cs index 21434a028..54df50419 100644 --- a/src/Ryujinx.Common/Pools/ThreadStaticArray.cs +++ b/src/Ryujinx.Common/Pools/ThreadStaticArray.cs @@ -9,10 +9,7 @@ namespace Ryujinx.Common.Pools public static ref T[] Get() { - if (_array == null) - { - _array = new T[1]; - } + _array ??= new T[1]; return ref _array; } diff --git a/src/Ryujinx.Common/ReactiveObject.cs b/src/Ryujinx.Common/ReactiveObject.cs index 44897f263..d2624c365 100644 --- a/src/Ryujinx.Common/ReactiveObject.cs +++ b/src/Ryujinx.Common/ReactiveObject.cs @@ -5,8 +5,8 @@ namespace Ryujinx.Common { public class ReactiveObject<T> { - private ReaderWriterLock _readerWriterLock = new ReaderWriterLock(); - private bool _isInitialized = false; + private readonly ReaderWriterLock _readerWriterLock = new(); + private bool _isInitialized; private T _value; public event EventHandler<ReactiveEventArgs<T>> Event; @@ -30,7 +30,7 @@ namespace Ryujinx.Common bool oldIsInitialized = _isInitialized; _isInitialized = true; - _value = value; + _value = value; _readerWriterLock.ReleaseWriterLock(); diff --git a/src/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs index 601c05b17..b561ef03e 100644 --- a/src/Ryujinx.Common/ReleaseInformation.cs +++ b/src/Ryujinx.Common/ReleaseInformation.cs @@ -9,11 +9,11 @@ namespace Ryujinx.Common { private const string FlatHubChannelOwner = "flathub"; - public static string BuildVersion = "%%RYUJINX_BUILD_VERSION%%"; - public static string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%"; - public static string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%"; - public static string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%"; - public static string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%"; + public const string BuildVersion = "%%RYUJINX_BUILD_VERSION%%"; + public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%"; + public const string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%"; + public const string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%"; + public const string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%"; public static bool IsValid() { @@ -34,10 +34,8 @@ namespace Ryujinx.Common { return BuildVersion; } - else - { - return Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; - } + + return Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; } #if FORCE_EXTERNAL_BASE_DIR @@ -57,4 +55,4 @@ namespace Ryujinx.Common } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs b/src/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs index b0c15e491..08aa452eb 100644 --- a/src/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs +++ b/src/Ryujinx.Common/SystemInfo/LinuxSystemInfo.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Common.SystemInfo { ["model name"] = null, ["Processor"] = null, - ["Hardware"] = null + ["Hardware"] = null, }; ParseKeyValues("/proc/cpuinfo", cpuDict); @@ -31,7 +31,7 @@ namespace Ryujinx.Common.SystemInfo var memDict = new Dictionary<string, string>(StringComparer.Ordinal) { ["MemTotal"] = null, - ["MemAvailable"] = null + ["MemAvailable"] = null, }; ParseKeyValues("/proc/meminfo", memDict); @@ -56,25 +56,30 @@ namespace Ryujinx.Common.SystemInfo int count = itemDict.Count; - using (StreamReader file = new StreamReader(filePath)) + using StreamReader file = new(filePath); + + string line; + while ((line = file.ReadLine()) != null) { - string line; - while ((line = file.ReadLine()) != null) + string[] kvPair = line.Split(':', 2, StringSplitOptions.TrimEntries); + + if (kvPair.Length < 2) { - string[] kvPair = line.Split(':', 2, StringSplitOptions.TrimEntries); + continue; + } - if (kvPair.Length < 2) continue; + string key = kvPair[0]; - string key = kvPair[0]; + if (itemDict.TryGetValue(key, out string value) && value == null) + { + itemDict[key] = kvPair[1]; - if (itemDict.TryGetValue(key, out string value) && value == null) + if (--count <= 0) { - itemDict[key] = kvPair[1]; - - if (--count <= 0) break; + break; } } } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs b/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs index 168960f71..98a0d8abf 100644 --- a/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs +++ b/src/Ryujinx.Common/SystemInfo/MacOSSystemInfo.cs @@ -14,14 +14,14 @@ namespace Ryujinx.Common.SystemInfo { string cpuName = GetCpuidCpuName(); - if (cpuName == null && sysctlbyname("machdep.cpu.brand_string", out cpuName) != 0) + if (cpuName == null && SysctlByName("machdep.cpu.brand_string", out cpuName) != 0) { cpuName = "Unknown"; } ulong totalRAM = 0; - if (sysctlbyname("hw.memsize", ref totalRAM) != 0) // Bytes + if (SysctlByName("hw.memsize", ref totalRAM) != 0) // Bytes { totalRAM = 0; } @@ -63,7 +63,7 @@ namespace Ryujinx.Common.SystemInfo [LibraryImport(SystemLibraryName, SetLastError = true)] private static partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize); - private static int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize) + private static int SysctlByName(string name, IntPtr oldValue, ref ulong oldSize) { if (sysctlbyname(name, oldValue, ref oldSize, IntPtr.Zero, 0) == -1) { @@ -77,23 +77,23 @@ namespace Ryujinx.Common.SystemInfo return 0; } - private static int sysctlbyname<T>(string name, ref T oldValue) + private static int SysctlByName<T>(string name, ref T oldValue) { unsafe { ulong oldValueSize = (ulong)Unsafe.SizeOf<T>(); - return sysctlbyname(name, (IntPtr)Unsafe.AsPointer(ref oldValue), ref oldValueSize); + return SysctlByName(name, (IntPtr)Unsafe.AsPointer(ref oldValue), ref oldValueSize); } } - private static int sysctlbyname(string name, out string oldValue) + private static int SysctlByName(string name, out string oldValue) { oldValue = default; ulong strSize = 0; - int res = sysctlbyname(name, IntPtr.Zero, ref strSize); + int res = SysctlByName(name, IntPtr.Zero, ref strSize); if (res == 0) { @@ -103,7 +103,7 @@ namespace Ryujinx.Common.SystemInfo { fixed (byte* rawDataPtr = rawData) { - res = sysctlbyname(name, (IntPtr)rawDataPtr, ref strSize); + res = SysctlByName(name, (IntPtr)rawDataPtr, ref strSize); } if (res == 0) @@ -152,6 +152,6 @@ namespace Ryujinx.Common.SystemInfo } [LibraryImport(SystemLibraryName, SetLastError = true)] - private static partial int host_statistics64(uint host_priv, int host_flavor, ref VMStatistics64 host_info64_out, ref uint host_info64_outCnt); + private static partial int host_statistics64(uint hostPriv, int hostFlavor, ref VMStatistics64 hostInfo64Out, ref uint hostInfo64OutCnt); } } diff --git a/src/Ryujinx.Common/SystemInfo/SystemInfo.cs b/src/Ryujinx.Common/SystemInfo/SystemInfo.cs index e9ce3c58a..55ec0127c 100644 --- a/src/Ryujinx.Common/SystemInfo/SystemInfo.cs +++ b/src/Ryujinx.Common/SystemInfo/SystemInfo.cs @@ -43,12 +43,10 @@ namespace Ryujinx.Common.SystemInfo { return new MacOSSystemInfo(); } - else - { - Logger.Error?.Print(LogClass.Application, "SystemInfo unsupported on this platform"); - return new SystemInfo(); - } + Logger.Error?.Print(LogClass.Application, "SystemInfo unsupported on this platform"); + + return new SystemInfo(); } // x86 exposes a 48 byte ASCII "CPU brand" string via CPUID leaves 0x80000002-0x80000004. @@ -77,4 +75,4 @@ namespace Ryujinx.Common.SystemInfo return string.IsNullOrEmpty(name) ? null : name; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs b/src/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs index c3598a1eb..3b36d6e2e 100644 --- a/src/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs +++ b/src/Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs @@ -17,15 +17,13 @@ namespace Ryujinx.Common.SystemInfo private static (ulong Total, ulong Available) GetMemoryStats() { - MemoryStatusEx memStatus = new MemoryStatusEx(); + MemoryStatusEx memStatus = new(); if (GlobalMemoryStatusEx(ref memStatus)) { return (memStatus.TotalPhys, memStatus.AvailPhys); // Bytes } - else - { - Logger.Error?.Print(LogClass.Application, $"GlobalMemoryStatusEx failed. Error {Marshal.GetLastWin32Error():X}"); - } + + Logger.Error?.Print(LogClass.Application, $"GlobalMemoryStatusEx failed. Error {Marshal.GetLastWin32Error():X}"); return (0, 0); } @@ -86,4 +84,4 @@ namespace Ryujinx.Common.SystemInfo return null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/SystemInterop/DisplaySleep.cs b/src/Ryujinx.Common/SystemInterop/DisplaySleep.cs index 5a1f66f50..4dff5410e 100644 --- a/src/Ryujinx.Common/SystemInterop/DisplaySleep.cs +++ b/src/Ryujinx.Common/SystemInterop/DisplaySleep.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Common.SystemInterop { ES_CONTINUOUS = 0x80000000, ES_DISPLAY_REQUIRED = 0x00000002, - ES_SYSTEM_REQUIRED = 0x00000001 + ES_SYSTEM_REQUIRED = 0x00000001, } [LibraryImport("kernel32.dll", SetLastError = true)] @@ -23,12 +23,12 @@ namespace Ryujinx.Common.SystemInterop SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED); } } - + static public void Restore() { if (OperatingSystem.IsWindows()) { - SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS); + SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS); } } } diff --git a/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs b/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs index f17612a6d..5fa2281db 100644 --- a/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs +++ b/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Common.SystemInterop private static partial int XCloseDisplay(IntPtr display); private static readonly double _standardDpiScale = 96.0; - private static readonly double _maxScaleFactor = 1.25; + private static readonly double _maxScaleFactor = 1.25; /// <summary> /// Marks the application as DPI-Aware when running on the Windows operating system. @@ -63,14 +63,14 @@ namespace Ryujinx.Common.SystemInterop string dpiString = Marshal.PtrToStringAnsi(XGetDefault(display, "Xft", "dpi")); if (dpiString == null || !double.TryParse(dpiString, NumberStyles.Any, CultureInfo.InvariantCulture, out userDpiScale)) { - userDpiScale = (double)XDisplayWidth(display, 0) * 25.4 / (double)XDisplayWidthMM(display, 0); + userDpiScale = XDisplayWidth(display, 0) * 25.4 / XDisplayWidthMM(display, 0); } - XCloseDisplay(display); + _ = XCloseDisplay(display); } else if (xdgSessionType == "wayland") { // TODO - Logger.Warning?.Print(LogClass.Application, $"Couldn't determine monitor DPI: Wayland not yet supported"); + Logger.Warning?.Print(LogClass.Application, "Couldn't determine monitor DPI: Wayland not yet supported"); } else { diff --git a/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs b/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs index 1001424df..bedf56ea4 100644 --- a/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs +++ b/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs @@ -28,14 +28,14 @@ namespace Ryujinx.Common.SystemInterop { public int GdiplusVersion; -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public IntPtr DebugEventCallback; public int SuppressBackgroundThread; public int SuppressExternalCodecs; public int StartupParameters; #pragma warning restore CS0649 - public static StartupInputEx Default => new StartupInputEx + public static StartupInputEx Default => new() { // We assume Windows 8 and upper GdiplusVersion = 2, diff --git a/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs b/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs index 4802178e2..a04c404d8 100644 --- a/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs +++ b/src/Ryujinx.Common/SystemInterop/StdErrAdapter.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Common.SystemInterop { public partial class StdErrAdapter : IDisposable { - private bool _disposable = false; + private bool _disposable; private Stream _pipeReader; private Stream _pipeWriter; private CancellationTokenSource _cancellationTokenSource; @@ -41,7 +41,7 @@ namespace Ryujinx.Common.SystemInterop _worker = Task.Run(async () => await EventWorkerAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token); _disposable = true; } - + [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] private async Task EventWorkerAsync(CancellationToken cancellationToken) @@ -53,13 +53,15 @@ namespace Ryujinx.Common.SystemInterop Logger.Error?.PrintRawMsg(line); } } - - private void Dispose(bool disposing) + + public void Dispose() { + GC.SuppressFinalize(this); + if (_disposable) { _disposable = false; - + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { _cancellationTokenSource.Cancel(); @@ -70,11 +72,6 @@ namespace Ryujinx.Common.SystemInterop } } - public void Dispose() - { - Dispose(true); - } - [LibraryImport("libc", SetLastError = true)] private static partial int dup2(int fd, int fd2); @@ -83,27 +80,25 @@ namespace Ryujinx.Common.SystemInterop private static (int, int) MakePipe() { - Span<int> pipefd = stackalloc int[2]; + Span<int> pipefd = stackalloc int[2]; if (pipe(pipefd) == 0) { return (pipefd[0], pipefd[1]); } - else - { - throw new(); - } + + throw new(); } - + [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] private static Stream CreateFileDescriptorStream(int fd) { return new FileStream( - new SafeFileHandle((IntPtr)fd, ownsHandle: true), + new SafeFileHandle(fd, ownsHandle: true), FileAccess.ReadWrite ); } - + } } diff --git a/src/Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs b/src/Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs index a4fbf0bd4..88acadd57 100644 --- a/src/Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs +++ b/src/Ryujinx.Common/SystemInterop/WindowsMultimediaTimerResolution.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Common.SystemInterop { public uint wPeriodMin; public uint wPeriodMax; - }; + } [LibraryImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)] private static partial uint TimeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps); @@ -111,4 +111,4 @@ namespace Ryujinx.Common.SystemInterop } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/BitUtils.cs b/src/Ryujinx.Common/Utilities/BitUtils.cs index f885ce844..0bf9c8acd 100644 --- a/src/Ryujinx.Common/Utilities/BitUtils.cs +++ b/src/Ryujinx.Common/Utilities/BitUtils.cs @@ -1,4 +1,3 @@ -using System; using System.Numerics; namespace Ryujinx.Common @@ -27,10 +26,10 @@ namespace Ryujinx.Common { value--; - value |= (value >> 1); - value |= (value >> 2); - value |= (value >> 4); - value |= (value >> 8); + value |= (value >> 1); + value |= (value >> 2); + value |= (value >> 4); + value |= (value >> 8); value |= (value >> 16); return ++value; @@ -48,10 +47,10 @@ namespace Ryujinx.Common private static ulong ReverseBits64(ulong value) { - value = ((value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((value & 0x5555555555555555) << 1 ); - value = ((value & 0xcccccccccccccccc) >> 2 ) | ((value & 0x3333333333333333) << 2 ); - value = ((value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((value & 0x0f0f0f0f0f0f0f0f) << 4 ); - value = ((value & 0xff00ff00ff00ff00) >> 8 ) | ((value & 0x00ff00ff00ff00ff) << 8 ); + value = ((value & 0xaaaaaaaaaaaaaaaa) >> 1) | ((value & 0x5555555555555555) << 1); + value = ((value & 0xcccccccccccccccc) >> 2) | ((value & 0x3333333333333333) << 2); + value = ((value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((value & 0x0f0f0f0f0f0f0f0f) << 4); + value = ((value & 0xff00ff00ff00ff00) >> 8) | ((value & 0x00ff00ff00ff00ff) << 8); value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16); return (value >> 32) | (value << 32); diff --git a/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs b/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs index ca429944f..8e9b696ee 100644 --- a/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs +++ b/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs @@ -54,4 +54,4 @@ namespace Ryujinx.Common.Utilities return (value & ~mask) | ((toInsert << lsb) & mask); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/Buffers.cs b/src/Ryujinx.Common/Utilities/Buffers.cs index d614bfc43..c85b87130 100644 --- a/src/Ryujinx.Common/Utilities/Buffers.cs +++ b/src/Ryujinx.Common/Utilities/Buffers.cs @@ -9,8 +9,8 @@ namespace Ryujinx.Common.Utilities [StructLayout(LayoutKind.Sequential, Size = 16)] public struct Buffer16 { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong _dummy0; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong _dummy1; public byte this[int i] { diff --git a/src/Ryujinx.Common/Utilities/CommonJsonContext.cs b/src/Ryujinx.Common/Utilities/CommonJsonContext.cs index d7b3f78cd..93f065122 100644 --- a/src/Ryujinx.Common/Utilities/CommonJsonContext.cs +++ b/src/Ryujinx.Common/Utilities/CommonJsonContext.cs @@ -8,4 +8,4 @@ namespace Ryujinx.Common.Utilities public partial class CommonJsonContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs index 22b55f161..a4facc2e3 100644 --- a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs +++ b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using System; using System.IO; @@ -10,11 +9,11 @@ namespace Ryujinx.Common { public static class EmbeddedResources { - private readonly static Assembly ResourceAssembly; + private readonly static Assembly _resourceAssembly; static EmbeddedResources() { - ResourceAssembly = Assembly.GetAssembly(typeof(EmbeddedResources)); + _resourceAssembly = Assembly.GetAssembly(typeof(EmbeddedResources)); } public static byte[] Read(string filename) @@ -33,28 +32,24 @@ namespace Ryujinx.Common public static byte[] Read(Assembly assembly, string filename) { - using (var stream = GetStream(assembly, filename)) + using var stream = GetStream(assembly, filename); + if (stream == null) { - if (stream == null) - { - return null; - } - - return StreamUtils.StreamToBytes(stream); + return null; } + + return StreamUtils.StreamToBytes(stream); } public async static Task<byte[]> ReadAsync(Assembly assembly, string filename) { - using (var stream = GetStream(assembly, filename)) + using var stream = GetStream(assembly, filename); + if (stream == null) { - if (stream == null) - { - return null; - } - - return await StreamUtils.StreamToBytesAsync(stream); + return null; } + + return await StreamUtils.StreamToBytesAsync(stream); } public static string ReadAllText(string filename) @@ -73,34 +68,26 @@ namespace Ryujinx.Common public static string ReadAllText(Assembly assembly, string filename) { - using (var stream = GetStream(assembly, filename)) + using var stream = GetStream(assembly, filename); + if (stream == null) { - if (stream == null) - { - return null; - } - - using (var reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } + return null; } + + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); } public async static Task<string> ReadAllTextAsync(Assembly assembly, string filename) { - using (var stream = GetStream(assembly, filename)) + using var stream = GetStream(assembly, filename); + if (stream == null) { - if (stream == null) - { - return null; - } - - using (var reader = new StreamReader(stream)) - { - return await reader.ReadToEndAsync(); - } + return null; } + + using var reader = new StreamReader(stream); + return await reader.ReadToEndAsync(); } public static Stream GetStream(string filename) @@ -112,8 +99,8 @@ namespace Ryujinx.Common public static Stream GetStream(Assembly assembly, string filename) { - var namespace_ = assembly.GetName().Name; - var manifestUri = namespace_ + "." + filename.Replace('/', '.'); + var @namespace = assembly.GetName().Name; + var manifestUri = @namespace + "." + filename.Replace('/', '.'); var stream = assembly.GetManifestResourceStream(manifestUri); @@ -142,7 +129,7 @@ namespace Ryujinx.Common } } - return (ResourceAssembly, filename); + return (_resourceAssembly, filename); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/HexUtils.cs b/src/Ryujinx.Common/Utilities/HexUtils.cs index 63587ceaa..4690eaa1f 100644 --- a/src/Ryujinx.Common/Utilities/HexUtils.cs +++ b/src/Ryujinx.Common/Utilities/HexUtils.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Common { public static class HexUtils { - private static readonly char[] HexChars = "0123456789ABCDEF".ToCharArray(); + private static readonly char[] _hexChars = "0123456789ABCDEF".ToCharArray(); private const int HexTableColumnWidth = 8; private const int HexTableColumnSpace = 3; @@ -39,20 +39,20 @@ namespace Ryujinx.Common int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine; - StringBuilder result = new StringBuilder(expectedLines * lineLength); + StringBuilder result = new(expectedLines * lineLength); for (int i = 0; i < bytesLength; i += bytesPerLine) { - line[0] = HexChars[(i >> 28) & 0xF]; - line[1] = HexChars[(i >> 24) & 0xF]; - line[2] = HexChars[(i >> 20) & 0xF]; - line[3] = HexChars[(i >> 16) & 0xF]; - line[4] = HexChars[(i >> 12) & 0xF]; - line[5] = HexChars[(i >> 8) & 0xF]; - line[6] = HexChars[(i >> 4) & 0xF]; - line[7] = HexChars[(i >> 0) & 0xF]; + line[0] = _hexChars[(i >> 28) & 0xF]; + line[1] = _hexChars[(i >> 24) & 0xF]; + line[2] = _hexChars[(i >> 20) & 0xF]; + line[3] = _hexChars[(i >> 16) & 0xF]; + line[4] = _hexChars[(i >> 12) & 0xF]; + line[5] = _hexChars[(i >> 8) & 0xF]; + line[6] = _hexChars[(i >> 4) & 0xF]; + line[7] = _hexChars[(i >> 0) & 0xF]; - int hexColumn = firstHexColumn; + int hexColumn = firstHexColumn; int charColumn = firstCharColumn; for (int j = 0; j < bytesPerLine; j++) @@ -64,17 +64,17 @@ namespace Ryujinx.Common if (i + j >= bytesLength) { - line[hexColumn] = ' '; + line[hexColumn] = ' '; line[hexColumn + 1] = ' '; - line[charColumn] = ' '; + line[charColumn] = ' '; } else { byte b = bytes[i + j]; - line[hexColumn] = HexChars[(b >> 4) & 0xF]; - line[hexColumn + 1] = HexChars[b & 0xF]; - line[charColumn] = (b < 32 ? '·' : (char)b); + line[hexColumn] = _hexChars[(b >> 4) & 0xF]; + line[hexColumn + 1] = _hexChars[b & 0xF]; + line[charColumn] = (b < 32 ? '·' : (char)b); } hexColumn += 3; diff --git a/src/Ryujinx.Common/Utilities/JsonHelper.cs b/src/Ryujinx.Common/Utilities/JsonHelper.cs index 9a2d6f181..d4ecf5e9b 100644 --- a/src/Ryujinx.Common/Utilities/JsonHelper.cs +++ b/src/Ryujinx.Common/Utilities/JsonHelper.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Common.Utilities { public class JsonHelper { - private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy(); + private static readonly JsonNamingPolicy _snakeCasePolicy = new SnakeCaseNamingPolicy(); private const int DefaultFileWriteBufferSize = 4096; /// <summary> @@ -21,11 +21,11 @@ namespace Ryujinx.Common.Utilities { JsonSerializerOptions options = new() { - DictionaryKeyPolicy = SnakeCasePolicy, - PropertyNamingPolicy = SnakeCasePolicy, - WriteIndented = indented, - AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip + DictionaryKeyPolicy = _snakeCasePolicy, + PropertyNamingPolicy = _snakeCasePolicy, + WriteIndented = indented, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, }; return options; @@ -95,4 +95,4 @@ namespace Ryujinx.Common.Utilities } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs b/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs index 3714bec0f..a7c596bae 100644 --- a/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs +++ b/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs @@ -12,10 +12,8 @@ namespace Ryujinx.Common.Utilities { return Format(obj); } - else - { - return obj.ToString(); - } + + return obj.ToString(); } public static string Format(MessagePackObject obj) @@ -179,19 +177,17 @@ namespace Ryujinx.Common.Utilities { return unchecked((char)('0' + b)); } - else - { - return unchecked((char)('A' + (b - 10))); - } + + return unchecked((char)('A' + (b - 10))); } internal class IndentedStringBuilder { const string DefaultIndent = " "; - private int _indentCount = 0; - private int _newLineIndex = 0; - private StringBuilder _builder; + private int _indentCount; + private int _newLineIndex; + private readonly StringBuilder _builder; public string IndentString { get; set; } = DefaultIndent; diff --git a/src/Ryujinx.Common/Utilities/NetworkHelpers.cs b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs index b2f6f45d6..e48ff2693 100644 --- a/src/Ryujinx.Common/Utilities/NetworkHelpers.cs +++ b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Common.Utilities return (null, null); } - IPInterfaceProperties targetProperties = null; + IPInterfaceProperties targetProperties = null; UnicastIPAddressInformation targetAddressInfo = null; NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); @@ -63,4 +63,4 @@ namespace Ryujinx.Common.Utilities return (targetProperties, targetAddressInfo); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/StreamUtils.cs b/src/Ryujinx.Common/Utilities/StreamUtils.cs index da97188d8..4d090fe98 100644 --- a/src/Ryujinx.Common/Utilities/StreamUtils.cs +++ b/src/Ryujinx.Common/Utilities/StreamUtils.cs @@ -1,5 +1,4 @@ -using Microsoft.IO; -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -10,22 +9,21 @@ namespace Ryujinx.Common.Utilities { public static byte[] StreamToBytes(Stream input) { - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - { - input.CopyTo(stream); + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); - return stream.ToArray(); - } + + input.CopyTo(stream); + + return stream.ToArray(); } public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default) { - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - { - await input.CopyToAsync(stream, cancellationToken); + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); - return stream.ToArray(); - } + await input.CopyToAsync(stream, cancellationToken); + + return stream.ToArray(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs b/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs index 73765129e..9c575d69f 100644 --- a/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs +++ b/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs @@ -34,4 +34,4 @@ namespace Ryujinx.Common.Utilities writer.WriteStringValue(value.ToString()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/Utilities/UInt128Utils.cs b/src/Ryujinx.Common/Utilities/UInt128Utils.cs index af8521b4e..113855355 100644 --- a/src/Ryujinx.Common/Utilities/UInt128Utils.cs +++ b/src/Ryujinx.Common/Utilities/UInt128Utils.cs @@ -15,4 +15,4 @@ namespace Ryujinx.Common.Utilities return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Common/XXHash128.cs b/src/Ryujinx.Common/XXHash128.cs index edbc652f9..62e23fc23 100644 --- a/src/Ryujinx.Common/XXHash128.cs +++ b/src/Ryujinx.Common/XXHash128.cs @@ -31,8 +31,7 @@ namespace Ryujinx.Common private const ulong Prime64_4 = 0x85EBCA77C2B2AE63UL; private const ulong Prime64_5 = 0x27D4EB2F165667C5UL; - private static readonly ulong[] Xxh3InitAcc = new ulong[] - { + private static readonly ulong[] _xxh3InitAcc = { Prime32_3, Prime64_1, Prime64_2, @@ -40,7 +39,7 @@ namespace Ryujinx.Common Prime64_4, Prime32_2, Prime64_5, - Prime32_1 + Prime32_1, }; private static ReadOnlySpan<byte> Xxh3KSecret => new byte[] @@ -56,23 +55,24 @@ namespace Ryujinx.Common 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, - 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e + 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, }; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Mult32To64(ulong x, ulong y) { - return (ulong)(uint)x * (ulong)(uint)y; + return (uint)x * (ulong)(uint)y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Hash128 Mult64To128(ulong lhs, ulong rhs) { ulong high = Math.BigMul(lhs, rhs, out ulong low); + return new Hash128 { Low = low, - High = high + High = high, }; } @@ -80,6 +80,7 @@ namespace Ryujinx.Common private static ulong Mul128Fold64(ulong lhs, ulong rhs) { Hash128 product = Mult64To128(lhs, rhs); + return product.Low ^ product.High; } @@ -87,6 +88,7 @@ namespace Ryujinx.Common private static ulong XorShift64(ulong v64, int shift) { Debug.Assert(0 <= shift && shift < 64); + return v64 ^ (v64 >> shift); } @@ -96,6 +98,7 @@ namespace Ryujinx.Common h64 = XorShift64(h64, 37); h64 *= 0x165667919E3779F9UL; h64 = XorShift64(h64, 32); + return h64; } @@ -107,6 +110,7 @@ namespace Ryujinx.Common h64 ^= h64 >> 29; h64 *= Prime64_3; h64 ^= h64 >> 32; + return h64; } @@ -165,8 +169,8 @@ namespace Ryujinx.Common { for (int i = 0; i < AccNb; i++) { - ulong dataVal = BinaryPrimitives.ReadUInt64LittleEndian(input.Slice(i * sizeof(ulong))); - ulong dataKey = dataVal ^ BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(i * sizeof(ulong))); + ulong dataVal = BinaryPrimitives.ReadUInt64LittleEndian(input[(i * sizeof(ulong))..]); + ulong dataKey = dataVal ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); acc[i ^ 1] += dataVal; acc[i] += Mult32To64((uint)dataKey, dataKey >> 32); } @@ -236,7 +240,7 @@ namespace Ryujinx.Common { for (int i = 0; i < AccNb; i++) { - ulong key64 = BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(i * sizeof(ulong))); + ulong key64 = BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); ulong acc64 = acc[i]; acc64 = XorShift64(acc64, 47); acc64 ^= key64; @@ -251,8 +255,8 @@ namespace Ryujinx.Common { for (int n = 0; n < nbStripes; n++) { - ReadOnlySpan<byte> inData = input.Slice(n * StripeLen); - Xxh3Accumulate512(acc, inData, secret.Slice(n * SecretConsumeRate)); + ReadOnlySpan<byte> inData = input[(n * StripeLen)..]; + Xxh3Accumulate512(acc, inData, secret[(n * SecretConsumeRate)..]); } } @@ -266,18 +270,18 @@ namespace Ryujinx.Common for (int n = 0; n < nbBlocks; n++) { - Xxh3Accumulate(acc, input.Slice(n * blockLen), secret, nbStripesPerBlock); - Xxh3ScrambleAcc(acc, secret.Slice(secret.Length - StripeLen)); + Xxh3Accumulate(acc, input[(n * blockLen)..], secret, nbStripesPerBlock); + Xxh3ScrambleAcc(acc, secret[^StripeLen..]); } Debug.Assert(input.Length > StripeLen); int nbStripes = (input.Length - 1 - (blockLen * nbBlocks)) / StripeLen; Debug.Assert(nbStripes <= (secret.Length / SecretConsumeRate)); - Xxh3Accumulate(acc, input.Slice(nbBlocks * blockLen), secret, nbStripes); + Xxh3Accumulate(acc, input[(nbBlocks * blockLen)..], secret, nbStripes); - ReadOnlySpan<byte> p = input.Slice(input.Length - StripeLen); - Xxh3Accumulate512(acc, p, secret.Slice(secret.Length - StripeLen - SecretLastAccStart)); + ReadOnlySpan<byte> p = input[^StripeLen..]; + Xxh3Accumulate512(acc, p, secret[(secret.Length - StripeLen - SecretLastAccStart)..]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -285,7 +289,7 @@ namespace Ryujinx.Common { return Mul128Fold64( acc[0] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret), - acc[1] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(8))); + acc[1] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[8..])); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -295,7 +299,7 @@ namespace Ryujinx.Common for (int i = 0; i < 4; i++) { - result64 += Xxh3Mix2Accs(acc.Slice(2 * i), secret.Slice(16 * i)); + result64 += Xxh3Mix2Accs(acc[(2 * i)..], secret[(16 * i)..]); } return Xxh3Avalanche(result64); @@ -305,7 +309,7 @@ namespace Ryujinx.Common private static Hash128 Xxh3HashLong128bInternal(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret) { Span<ulong> acc = stackalloc ulong[AccNb]; - Xxh3InitAcc.CopyTo(acc); + _xxh3InitAcc.CopyTo(acc); Xxh3HashLongInternalLoop(acc, input, secret); @@ -314,11 +318,11 @@ namespace Ryujinx.Common return new Hash128 { - Low = Xxh3MergeAccs(acc, secret.Slice(SecretMergeAccsStart), (ulong)input.Length * Prime64_1), + Low = Xxh3MergeAccs(acc, secret[SecretMergeAccsStart..], (ulong)input.Length * Prime64_1), High = Xxh3MergeAccs( acc, - secret.Slice(secret.Length - acc.Length * sizeof(ulong) - SecretMergeAccsStart), - ~((ulong)input.Length * Prime64_2)) + secret[(secret.Length - acc.Length * sizeof(ulong) - SecretMergeAccsStart)..], + ~((ulong)input.Length * Prime64_2)), }; } @@ -332,15 +336,15 @@ namespace Ryujinx.Common uint combinedL = ((uint)c1 << 16) | ((uint)c2 << 24) | c3 | ((uint)input.Length << 8); uint combinedH = BitOperations.RotateLeft(BinaryPrimitives.ReverseEndianness(combinedL), 13); - ulong bitFlipL = (BinaryPrimitives.ReadUInt32LittleEndian(secret) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret.Slice(4))) + seed; - ulong bitFlipH = (BinaryPrimitives.ReadUInt32LittleEndian(secret.Slice(8)) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret.Slice(12))) - seed; + ulong bitFlipL = (BinaryPrimitives.ReadUInt32LittleEndian(secret) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[4..])) + seed; + ulong bitFlipH = (BinaryPrimitives.ReadUInt32LittleEndian(secret[8..]) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[12..])) - seed; ulong keyedLo = combinedL ^ bitFlipL; ulong keyedHi = combinedH ^ bitFlipH; return new Hash128 { Low = Xxh64Avalanche(keyedLo), - High = Xxh64Avalanche(keyedHi) + High = Xxh64Avalanche(keyedHi), }; } @@ -351,9 +355,9 @@ namespace Ryujinx.Common seed ^= BinaryPrimitives.ReverseEndianness((uint)seed) << 32; uint inputLo = BinaryPrimitives.ReadUInt32LittleEndian(input); - uint inputHi = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(input.Length - 4)); + uint inputHi = BinaryPrimitives.ReadUInt32LittleEndian(input[^4..]); ulong input64 = inputLo + ((ulong)inputHi << 32); - ulong bitFlip = (BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(16)) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(24))) + seed; + ulong bitFlip = (BinaryPrimitives.ReadUInt64LittleEndian(secret[16..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[24..])) + seed; ulong keyed = input64 ^ bitFlip; Hash128 m128 = Mult64To128(keyed, Prime64_1 + ((ulong)input.Length << 2)); @@ -365,6 +369,7 @@ namespace Ryujinx.Common m128.Low *= 0x9FB21C651E98DF25UL; m128.Low = XorShift64(m128.Low, 28); m128.High = Xxh3Avalanche(m128.High); + return m128; } @@ -372,10 +377,10 @@ namespace Ryujinx.Common { Debug.Assert(9 <= input.Length && input.Length <= 16); - ulong bitFlipL = (BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(32)) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(40))) - seed; - ulong bitFlipH = (BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(48)) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(56))) + seed; + ulong bitFlipL = (BinaryPrimitives.ReadUInt64LittleEndian(secret[32..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[40..])) - seed; + ulong bitFlipH = (BinaryPrimitives.ReadUInt64LittleEndian(secret[48..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[56..])) + seed; ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); - ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input.Slice(input.Length - 8)); + ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[^8..]); Hash128 m128 = Mult64To128(inputLo ^ inputHi ^ bitFlipL, Prime64_1); m128.Low += ((ulong)input.Length - 1) << 54; @@ -387,6 +392,7 @@ namespace Ryujinx.Common h128.High += m128.High * Prime64_2; h128.Low = Xxh3Avalanche(h128.Low); h128.High = Xxh3Avalanche(h128.High); + return h128; } @@ -398,40 +404,43 @@ namespace Ryujinx.Common { return Xxh3Len9To16128b(input, secret, seed); } - else if (input.Length >= 4) + + if (input.Length >= 4) { return Xxh3Len4To8128b(input, secret, seed); } - else if (input.Length != 0) + + if (input.Length != 0) { return Xxh3Len1To3128b(input, secret, seed); } - else - { - Hash128 h128 = new Hash128(); - ulong bitFlipL = BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(64)) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(72)); - ulong bitFlipH = BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(80)) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(88)); - h128.Low = Xxh64Avalanche(seed ^ bitFlipL); - h128.High = Xxh64Avalanche(seed ^ bitFlipH); - return h128; - } + + Hash128 h128 = new(); + ulong bitFlipL = BinaryPrimitives.ReadUInt64LittleEndian(secret[64..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[72..]); + ulong bitFlipH = BinaryPrimitives.ReadUInt64LittleEndian(secret[80..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[88..]); + h128.Low = Xxh64Avalanche(seed ^ bitFlipL); + h128.High = Xxh64Avalanche(seed ^ bitFlipH); + + return h128; } private static ulong Xxh3Mix16b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) { ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); - ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input.Slice(8)); + ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); + return Mul128Fold64( inputLo ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret) + seed), - inputHi ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret.Slice(8)) - seed)); + inputHi ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret[8..]) - seed)); } private static Hash128 Xxh128Mix32b(Hash128 acc, ReadOnlySpan<byte> input, ReadOnlySpan<byte> input2, ReadOnlySpan<byte> secret, ulong seed) { acc.Low += Xxh3Mix16b(input, secret, seed); - acc.Low ^= BinaryPrimitives.ReadUInt64LittleEndian(input2) + BinaryPrimitives.ReadUInt64LittleEndian(input2.Slice(8)); - acc.High += Xxh3Mix16b(input2, secret.Slice(16), seed); - acc.High ^= BinaryPrimitives.ReadUInt64LittleEndian(input) + BinaryPrimitives.ReadUInt64LittleEndian(input.Slice(8)); + acc.Low ^= BinaryPrimitives.ReadUInt64LittleEndian(input2) + BinaryPrimitives.ReadUInt64LittleEndian(input2[8..]); + acc.High += Xxh3Mix16b(input2, secret[16..], seed); + acc.High ^= BinaryPrimitives.ReadUInt64LittleEndian(input) + BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); + return acc; } @@ -440,10 +449,10 @@ namespace Ryujinx.Common Debug.Assert(secret.Length >= SecretSizeMin); Debug.Assert(16 < input.Length && input.Length <= 128); - Hash128 acc = new Hash128 + Hash128 acc = new() { Low = (ulong)input.Length * Prime64_1, - High = 0 + High = 0, }; if (input.Length > 32) @@ -452,21 +461,22 @@ namespace Ryujinx.Common { if (input.Length > 96) { - acc = Xxh128Mix32b(acc, input.Slice(48), input.Slice(input.Length - 64), secret.Slice(96), seed); + acc = Xxh128Mix32b(acc, input[48..], input[^64..], secret[96..], seed); } - acc = Xxh128Mix32b(acc, input.Slice(32), input.Slice(input.Length - 48), secret.Slice(64), seed); + acc = Xxh128Mix32b(acc, input[32..], input[^48..], secret[64..], seed); } - acc = Xxh128Mix32b(acc, input.Slice(16), input.Slice(input.Length - 32), secret.Slice(32), seed); + acc = Xxh128Mix32b(acc, input[16..], input[^32..], secret[32..], seed); } - acc = Xxh128Mix32b(acc, input, input.Slice(input.Length - 16), secret, seed); + acc = Xxh128Mix32b(acc, input, input[^16..], secret, seed); - Hash128 h128 = new Hash128 + Hash128 h128 = new() { Low = acc.Low + acc.High, - High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2 + High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, }; h128.Low = Xxh3Avalanche(h128.Low); h128.High = 0UL - Xxh3Avalanche(h128.High); + return h128; } @@ -475,7 +485,7 @@ namespace Ryujinx.Common Debug.Assert(secret.Length >= SecretSizeMin); Debug.Assert(128 < input.Length && input.Length <= 240); - Hash128 acc = new Hash128(); + Hash128 acc = new(); int nbRounds = input.Length / 32; acc.Low = (ulong)input.Length * Prime64_1; @@ -483,7 +493,7 @@ namespace Ryujinx.Common for (int i = 0; i < 4; i++) { - acc = Xxh128Mix32b(acc, input.Slice(32 * i), input.Slice(32 * i + 16), secret.Slice(32 * i), seed); + acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(32 * i)..], seed); } acc.Low = Xxh3Avalanche(acc.Low); @@ -492,18 +502,19 @@ namespace Ryujinx.Common for (int i = 4; i < nbRounds; i++) { - acc = Xxh128Mix32b(acc, input.Slice(32 * i), input.Slice(32 * i + 16), secret.Slice(MidSizeStartOffset + 32 * (i - 4)), seed); + acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(MidSizeStartOffset + 32 * (i - 4))..], seed); } - acc = Xxh128Mix32b(acc, input.Slice(input.Length - 16), input.Slice(input.Length - 32), secret.Slice(SecretSizeMin - MidSizeLastOffset - 16), 0UL - seed); + acc = Xxh128Mix32b(acc, input[^16..], input[^32..], secret[(SecretSizeMin - MidSizeLastOffset - 16)..], 0UL - seed); - Hash128 h128 = new Hash128 + Hash128 h128 = new() { Low = acc.Low + acc.High, - High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2 + High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, }; h128.Low = Xxh3Avalanche(h128.Low); h128.High = 0UL - Xxh3Avalanche(h128.High); + return h128; } @@ -515,18 +526,18 @@ namespace Ryujinx.Common { return Xxh3Len0To16128b(input, secret, seed); } - else if (input.Length <= 128) + + if (input.Length <= 128) { return Xxh3Len17To128128b(input, secret, seed); } - else if (input.Length <= 240) + + if (input.Length <= 240) { return Xxh3Len129To240128b(input, secret, seed); } - else - { - return Xxh3HashLong128bInternal(input, secret); - } + + return Xxh3HashLong128bInternal(input, secret); } public static Hash128 ComputeHash(ReadOnlySpan<byte> input) diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index a137c4133..5d8e086d0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -69,8 +69,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); - using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.Shared.RentCleared(outputSize)) - using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Shared.RentCleared(performanceOutputSize)) + using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.RentCleared(outputSize)) + using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.RentCleared(performanceOutputSize)) { Memory<byte> output = outputOwner.Memory; Memory<byte> performanceOutput = performanceOutputOwner.Memory; @@ -214,4 +214,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index f8ce465f9..4cd55a2e9 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -439,7 +439,7 @@ namespace Ryujinx.HLE.HOS.Services { const int messageSize = 0x100; - using IMemoryOwner<byte> reqDataOwner = ByteMemoryPool.Shared.Rent(messageSize); + using IMemoryOwner<byte> reqDataOwner = ByteMemoryPool.Rent(messageSize); Span<byte> reqDataSpan = reqDataOwner.Memory.Span; diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index 42fc27612..2f9f42914 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -85,10 +85,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); - using (IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.Shared.RentCleared(replySize)) + using (IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.RentCleared(replySize)) { Span<byte> outputParcel = outputParcelOwner.Memory.Span; - + ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel); if (result == ResultCode.Success) @@ -106,4 +106,4 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger protected abstract ResultCode OnTransact(int binderId, uint code, uint flags, ReadOnlySpan<byte> inputParcel, Span<byte> outputParcel); } -} \ No newline at end of file +} From cebfa54467231f0d1ca43021608b8b1ab03f39fb Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:46:18 +0200 Subject: [PATCH 681/737] [Ryujinx.Graphics.Texture] Address dotnet-format issues (#5375) * dotnet format style --severity info Some changes were manually reverted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0059 warnings * Address or silence dotnet format CA2208 warnings * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Address IDE0251 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Address review feedback * Update src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../Astc/AstcDecoder.cs | 936 ++++++++++-------- .../Astc/AstcDecoderException.cs | 2 +- .../Astc/AstcPixel.cs | 2 +- .../Astc/BitStream128.cs | 9 +- src/Ryujinx.Graphics.Texture/Astc/Bits.cs | 16 +- .../Astc/IntegerEncoded.cs | 54 +- src/Ryujinx.Graphics.Texture/BC6Decoder.cs | 2 +- src/Ryujinx.Graphics.Texture/BC7Decoder.cs | 17 +- src/Ryujinx.Graphics.Texture/BCnDecoder.cs | 64 +- src/Ryujinx.Graphics.Texture/BCnEncoder.cs | 8 +- .../BlockLinearConstants.cs | 2 +- .../BlockLinearLayout.cs | 24 +- src/Ryujinx.Graphics.Texture/Bpp12Pixel.cs | 6 +- .../Encoders/BC7Encoder.cs | 18 +- .../LayoutConverter.cs | 30 +- .../OffsetCalculator.cs | 34 +- .../PixelConverter.cs | 22 +- src/Ryujinx.Graphics.Texture/Size.cs | 10 +- .../SizeCalculator.cs | 25 +- src/Ryujinx.Graphics.Texture/SizeInfo.cs | 44 +- .../Utils/BC67Utils.cs | 16 +- .../Utils/BC7ModeInfo.cs | 2 +- src/Ryujinx.Graphics.Texture/Utils/Block.cs | 4 +- .../Utils/RgbaColor32.cs | 16 +- .../Utils/RgbaColor8.cs | 8 +- 25 files changed, 741 insertions(+), 630 deletions(-) diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index 80683d17b..6f633e4ae 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -94,8 +94,8 @@ namespace Ryujinx.Graphics.Texture.Astc public int StartBlock { get; set; } public int OutputByteOffset { get; set; } - public int TotalBlockCount => BlockCountX * BlockCountY * ImageSizeZ; - public int PixelCount => ImageSizeX * ImageSizeY * ImageSizeZ; + public readonly int TotalBlockCount => BlockCountX * BlockCountY * ImageSizeZ; + public readonly int PixelCount => ImageSizeX * ImageSizeY * ImageSizeZ; } public static int QueryDecompressedSize(int sizeX, int sizeY, int sizeZ, int levelCount, int layerCount) @@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Texture.Astc AstcLevel levelInfo = GetLevelInfo(index); - WriteDecompressedBlock(decompressedBytes, OutputBuffer.Span.Slice(levelInfo.OutputByteOffset), + WriteDecompressedBlock(decompressedBytes, OutputBuffer.Span[levelInfo.OutputByteOffset..], index - levelInfo.StartBlock, levelInfo); } @@ -171,7 +171,7 @@ namespace Ryujinx.Graphics.Texture.Astc for (int i = 0; i < outputPixelsY; i++) { ReadOnlySpan<byte> blockRow = block.Slice(inputOffset, outputPixelsX * 4); - Span<byte> outputRow = outputBuffer.Slice(outputOffset); + Span<byte> outputRow = outputBuffer[outputOffset..]; blockRow.CopyTo(outputRow); inputOffset += BlockSizeX * 4; @@ -189,7 +189,7 @@ namespace Ryujinx.Graphics.Texture.Astc public bool VoidExtentLdr; public bool VoidExtentHdr; - public int GetPackedBitSize() + public readonly int GetPackedBitSize() { // How many indices do we have? int indices = Height * Width; @@ -204,7 +204,7 @@ namespace Ryujinx.Graphics.Texture.Astc return intEncoded.GetBitLength(indices); } - public int GetNumWeightValues() + public readonly int GetNumWeightValues() { int ret = Width * Height; @@ -230,7 +230,7 @@ namespace Ryujinx.Graphics.Texture.Astc { byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels, layers)]; - AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels, layers); + AstcDecoder decoder = new(data, output, blockWidth, blockHeight, width, height, depth, levels, layers); for (int i = 0; i < decoder.TotalBlockCount; i++) { @@ -253,7 +253,7 @@ namespace Ryujinx.Graphics.Texture.Astc int levels, int layers) { - AstcDecoder decoder = new AstcDecoder(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels, layers); + AstcDecoder decoder = new(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels, layers); for (int i = 0; i < decoder.TotalBlockCount; i++) { @@ -274,7 +274,7 @@ namespace Ryujinx.Graphics.Texture.Astc int levels, int layers) { - AstcDecoder decoder = new AstcDecoder(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels, layers); + AstcDecoder decoder = new(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels, layers); // Lazy parallelism Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x)); @@ -295,7 +295,7 @@ namespace Ryujinx.Graphics.Texture.Astc { byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels, layers)]; - AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels, layers); + AstcDecoder decoder = new(data, output, blockWidth, blockHeight, width, height, depth, levels, layers); Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x)); @@ -310,7 +310,7 @@ namespace Ryujinx.Graphics.Texture.Astc int blockWidth, int blockHeight) { - BitStream128 bitStream = new BitStream128(inputBlock); + BitStream128 bitStream = new(inputBlock); DecodeBlockInfo(ref bitStream, out TexelWeightParams texelParams); @@ -359,7 +359,7 @@ namespace Ryujinx.Graphics.Texture.Astc Span<uint> colorEndpointMode = stackalloc uint[4]; - BitStream128 colorEndpointStream = new BitStream128(); + BitStream128 colorEndpointStream = new(); // Read extra config data... uint baseColorEndpointMode = 0; @@ -388,10 +388,18 @@ namespace Ryujinx.Graphics.Texture.Astc { switch (numberPartitions) { - case 2: extraColorEndpointModeBits += 2; break; - case 3: extraColorEndpointModeBits += 5; break; - case 4: extraColorEndpointModeBits += 8; break; - default: Debug.Assert(false); break; + case 2: + extraColorEndpointModeBits += 2; + break; + case 3: + extraColorEndpointModeBits += 5; + break; + case 4: + extraColorEndpointModeBits += 8; + break; + default: + Debug.Assert(false); + break; } } @@ -448,7 +456,12 @@ namespace Ryujinx.Graphics.Texture.Astc for (int i = 0; i < numberPartitions; i++) { colorEndpointMode[i] = baseMode; - if (!(c[i])) colorEndpointMode[i] -= 1; + + if (!(c[i])) + { + colorEndpointMode[i] -= 1; + } + colorEndpointMode[i] <<= 2; colorEndpointMode[i] |= m[i]; } @@ -475,7 +488,12 @@ namespace Ryujinx.Graphics.Texture.Astc DecodeColorValues(colorValues, ref colorEndpointStream, colorEndpointMode, numberPartitions, colorDataBits); EndPointSet endPoints; - unsafe { _ = &endPoints; } // Skip struct initialization + + unsafe + { + // Skip struct initialization + _ = &endPoints; + } int colorValuesPosition = 0; @@ -502,19 +520,33 @@ namespace Ryujinx.Graphics.Texture.Astc texelWeightData[clearByteStart - 1] &= (byte)((1 << (texelParams.GetPackedBitSize() % 8)) - 1); int cLen = 16 - clearByteStart; - for (int i = clearByteStart; i < clearByteStart + cLen; i++) texelWeightData[i] = 0; + for (int i = clearByteStart; i < clearByteStart + cLen; i++) + { + texelWeightData[i] = 0; + } IntegerSequence texelWeightValues; - unsafe { _ = &texelWeightValues; } // Skip struct initialization + + unsafe + { + // Skip struct initialization + _ = &texelWeightValues; + } + texelWeightValues.Reset(); - BitStream128 weightBitStream = new BitStream128(texelWeightData); + BitStream128 weightBitStream = new(texelWeightData); IntegerEncoded.DecodeIntegerSequence(ref texelWeightValues, ref weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues()); // Blocks can be at most 12x12, so we can have as many as 144 weights Weights weights; - unsafe { _ = &weights; } // Skip struct initialization + + unsafe + { + // Skip struct initialization + _ = &weights; + } UnquantizeTexelWeights(ref weights, ref texelWeightValues, ref texelParams, blockWidth, blockHeight); @@ -529,7 +561,7 @@ namespace Ryujinx.Graphics.Texture.Astc int partition = Select2dPartition(partitionIndex, i, j, numberPartitions, ((blockHeight * blockWidth) < 32)); Debug.Assert(partition < numberPartitions); - AstcPixel pixel = new AstcPixel(); + AstcPixel pixel = new(); for (int component = 0; component < 4; component++) { int component0 = endPoints.Get(partition)[0].GetComponent(component); @@ -579,7 +611,7 @@ namespace Ryujinx.Graphics.Texture.Astc { if ((uint)index >= Count) { - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index), index, null); } ref int start = ref Unsafe.Add(ref _start, index * 144); @@ -632,12 +664,18 @@ namespace Ryujinx.Graphics.Texture.Astc byte seed11 = (byte)((rightNum >> 26) & 0xF); byte seed12 = (byte)(((rightNum >> 30) | (rightNum << 2)) & 0xF); - seed01 *= seed01; seed02 *= seed02; - seed03 *= seed03; seed04 *= seed04; - seed05 *= seed05; seed06 *= seed06; - seed07 *= seed07; seed08 *= seed08; - seed09 *= seed09; seed10 *= seed10; - seed11 *= seed11; seed12 *= seed12; + seed01 *= seed01; + seed02 *= seed02; + seed03 *= seed03; + seed04 *= seed04; + seed05 *= seed05; + seed06 *= seed06; + seed07 *= seed07; + seed08 *= seed08; + seed09 *= seed09; + seed10 *= seed10; + seed11 *= seed11; + seed12 *= seed12; int seedHash1, seedHash2, seedHash3; @@ -654,31 +692,67 @@ namespace Ryujinx.Graphics.Texture.Astc seedHash3 = (seed & 0x10) != 0 ? seedHash1 : seedHash2; - seed01 >>= seedHash1; seed02 >>= seedHash2; seed03 >>= seedHash1; seed04 >>= seedHash2; - seed05 >>= seedHash1; seed06 >>= seedHash2; seed07 >>= seedHash1; seed08 >>= seedHash2; - seed09 >>= seedHash3; seed10 >>= seedHash3; seed11 >>= seedHash3; seed12 >>= seedHash3; + seed01 >>= seedHash1; + seed02 >>= seedHash2; + seed03 >>= seedHash1; + seed04 >>= seedHash2; + seed05 >>= seedHash1; + seed06 >>= seedHash2; + seed07 >>= seedHash1; + seed08 >>= seedHash2; + seed09 >>= seedHash3; + seed10 >>= seedHash3; + seed11 >>= seedHash3; + seed12 >>= seedHash3; int a = seed01 * x + seed02 * y + seed11 * z + (rightNum >> 14); int b = seed03 * x + seed04 * y + seed12 * z + (rightNum >> 10); int c = seed05 * x + seed06 * y + seed09 * z + (rightNum >> 6); int d = seed07 * x + seed08 * y + seed10 * z + (rightNum >> 2); - a &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F; + a &= 0x3F; + b &= 0x3F; + c &= 0x3F; + d &= 0x3F; - if (partitionCount < 4) d = 0; - if (partitionCount < 3) c = 0; + if (partitionCount < 4) + { + d = 0; + } + + if (partitionCount < 3) + { + c = 0; + } + + if (a >= b && a >= c && a >= d) + { + return 0; + } + else if (b >= c && b >= d) + { + return 1; + } + else if (c >= d) + { + return 2; + } - if (a >= b && a >= c && a >= d) return 0; - else if (b >= c && b >= d) return 1; - else if (c >= d) return 2; return 3; } static int Hash52(uint val) { - val ^= val >> 15; val -= val << 17; val += val << 7; val += val << 4; - val ^= val >> 5; val += val << 16; val ^= val >> 7; val ^= val >> 3; - val ^= val << 6; val ^= val >> 17; + val ^= val >> 15; + val -= val << 17; + val += val << 7; + val += val << 4; + val ^= val >> 5; + val += val << 16; + val ^= val >> 7; + val ^= val >> 3; + val ^= val << 6; + val ^= val >> 17; return (int)val; } @@ -692,7 +766,12 @@ namespace Ryujinx.Graphics.Texture.Astc { int weightIndices = 0; Weights unquantized; - unsafe { _ = &unquantized; } // Skip struct initialization + + unsafe + { + // Skip struct initialization + _ = &unquantized; + } Span<IntegerEncoded> weightsList = weights.List; Span<int> unquantized0 = unquantized[0]; @@ -713,7 +792,10 @@ namespace Ryujinx.Graphics.Texture.Astc } } - if (++weightIndices >= texelParams.Width * texelParams.Height) break; + if (++weightIndices >= texelParams.Width * texelParams.Height) + { + break; + } } // Do infill if necessary (Section C.2.18) ... @@ -794,100 +876,100 @@ namespace Ryujinx.Graphics.Texture.Astc break; case IntegerEncoded.EIntegerEncoding.Trit: - { - d = intEncoded.TritValue; - Debug.Assert(d < 3); - - switch (bitLength) { - case 0: - { - result = d switch - { - 0 => 0, - 1 => 32, - 2 => 63, - _ => 0 - }; + d = intEncoded.TritValue; + Debug.Assert(d < 3); - break; + switch (bitLength) + { + case 0: + { + result = d switch + { + 0 => 0, + 1 => 32, + 2 => 63, + _ => 0 + }; + + break; + } + + case 1: + { + c = 50; + break; + } + + case 2: + { + c = 23; + int b2 = (bitValue >> 1) & 1; + b = (b2 << 6) | (b2 << 2) | b2; + + break; + } + + case 3: + { + c = 11; + int cb = (bitValue >> 1) & 3; + b = (cb << 5) | cb; + + break; + } + + default: + throw new AstcDecoderException("Invalid trit encoding for texel weight."); } - case 1: - { - c = 50; - break; - } - - case 2: - { - c = 23; - int b2 = (bitValue >> 1) & 1; - b = (b2 << 6) | (b2 << 2) | b2; - - break; - } - - case 3: - { - c = 11; - int cb = (bitValue >> 1) & 3; - b = (cb << 5) | cb; - - break; - } - - default: - throw new AstcDecoderException("Invalid trit encoding for texel weight."); + break; } - break; - } - case IntegerEncoded.EIntegerEncoding.Quint: - { - d = intEncoded.QuintValue; - Debug.Assert(d < 5); - - switch (bitLength) { - case 0: - { - result = d switch - { - 0 => 0, - 1 => 16, - 2 => 32, - 3 => 47, - 4 => 63, - _ => 0 - }; + d = intEncoded.QuintValue; + Debug.Assert(d < 5); - break; + switch (bitLength) + { + case 0: + { + result = d switch + { + 0 => 0, + 1 => 16, + 2 => 32, + 3 => 47, + 4 => 63, + _ => 0 + }; + + break; + } + + case 1: + { + c = 28; + + break; + } + + case 2: + { + c = 13; + int b2 = (bitValue >> 1) & 1; + b = (b2 << 6) | (b2 << 1); + + break; + } + + default: + throw new AstcDecoderException("Invalid quint encoding for texel weight."); } - case 1: - { - c = 28; - - break; - } - - case 2: - { - c = 13; - int b2 = (bitValue >> 1) & 1; - b = (b2 << 6) | (b2 << 1); - - break; - } - - default: - throw new AstcDecoderException("Invalid quint encoding for texel weight."); + break; } - - break; - } } if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && bitLength > 0) @@ -942,160 +1024,160 @@ namespace Ryujinx.Graphics.Texture.Astc switch (colorEndpointMode) { case 0: - { - Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); + { + Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); - endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[0], (short)val[0]); - endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[1], (short)val[1]); + endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[0], (short)val[0]); + endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[1], (short)val[1]); - break; - } + break; + } case 1: - { - Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); - int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); - int l1 = (int)Math.Min(l0 + (val[1] & 0x3F), 0xFFU); + { + Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); + int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); + int l1 = (int)Math.Min(l0 + (val[1] & 0x3F), 0xFFU); - endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0); - endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1); + endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0); + endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1); - break; - } + break; + } case 4: - { - Span<uint> val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); + { + Span<uint> val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); - endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); - endPoints[1] = new AstcPixel((short)val[3], (short)val[1], (short)val[1], (short)val[1]); + endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); + endPoints[1] = new AstcPixel((short)val[3], (short)val[1], (short)val[1], (short)val[1]); - break; - } + break; + } case 5: - { - Span<int> val = ReadIntColorValues(4, colorValues, ref colorValuesPosition); + { + Span<int> val = ReadIntColorValues(4, colorValues, ref colorValuesPosition); - Bits.BitTransferSigned(ref val[1], ref val[0]); - Bits.BitTransferSigned(ref val[3], ref val[2]); + Bits.BitTransferSigned(ref val[1], ref val[0]); + Bits.BitTransferSigned(ref val[3], ref val[2]); - endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); - endPoints[1] = new AstcPixel((short)(val[2] + val[3]), (short)(val[0] + val[1]), (short)(val[0] + val[1]), (short)(val[0] + val[1])); + endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); + endPoints[1] = new AstcPixel((short)(val[2] + val[3]), (short)(val[0] + val[1]), (short)(val[0] + val[1]), (short)(val[0] + val[1])); - endPoints[0].ClampByte(); - endPoints[1].ClampByte(); + endPoints[0].ClampByte(); + endPoints[1].ClampByte(); - break; - } + break; + } case 6: - { - Span<uint> val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); + { + Span<uint> val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); - endPoints[0] = new AstcPixel(0xFF, (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); - endPoints[1] = new AstcPixel(0xFF, (short)val[0], (short)val[1], (short)val[2]); + endPoints[0] = new AstcPixel(0xFF, (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); + endPoints[1] = new AstcPixel(0xFF, (short)val[0], (short)val[1], (short)val[2]); - break; - } + break; + } case 8: - { - Span<uint> val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); - - if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) { - endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]); - endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[3], (short)val[5]); - } - else - { - endPoints[0] = AstcPixel.BlueContract(0xFF, (short)val[1], (short)val[3], (short)val[5]); - endPoints[1] = AstcPixel.BlueContract(0xFF, (short)val[0], (short)val[2], (short)val[4]); - } + Span<uint> val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); - break; - } + if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) + { + endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[3], (short)val[5]); + } + else + { + endPoints[0] = AstcPixel.BlueContract(0xFF, (short)val[1], (short)val[3], (short)val[5]); + endPoints[1] = AstcPixel.BlueContract(0xFF, (short)val[0], (short)val[2], (short)val[4]); + } + + break; + } case 9: - { - Span<int> val = ReadIntColorValues(6, colorValues, ref colorValuesPosition); - - Bits.BitTransferSigned(ref val[1], ref val[0]); - Bits.BitTransferSigned(ref val[3], ref val[2]); - Bits.BitTransferSigned(ref val[5], ref val[4]); - - if (val[1] + val[3] + val[5] >= 0) { - endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]); - endPoints[1] = new AstcPixel(0xFF, (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5])); - } - else - { - endPoints[0] = AstcPixel.BlueContract(0xFF, val[0] + val[1], val[2] + val[3], val[4] + val[5]); - endPoints[1] = AstcPixel.BlueContract(0xFF, val[0], val[2], val[4]); - } + Span<int> val = ReadIntColorValues(6, colorValues, ref colorValuesPosition); - endPoints[0].ClampByte(); - endPoints[1].ClampByte(); + Bits.BitTransferSigned(ref val[1], ref val[0]); + Bits.BitTransferSigned(ref val[3], ref val[2]); + Bits.BitTransferSigned(ref val[5], ref val[4]); - break; - } + if (val[1] + val[3] + val[5] >= 0) + { + endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel(0xFF, (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5])); + } + else + { + endPoints[0] = AstcPixel.BlueContract(0xFF, val[0] + val[1], val[2] + val[3], val[4] + val[5]); + endPoints[1] = AstcPixel.BlueContract(0xFF, val[0], val[2], val[4]); + } + + endPoints[0].ClampByte(); + endPoints[1].ClampByte(); + + break; + } case 10: - { - Span<uint> val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); + { + Span<uint> val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); - endPoints[0] = new AstcPixel((short)val[4], (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); - endPoints[1] = new AstcPixel((short)val[5], (short)val[0], (short)val[1], (short)val[2]); + endPoints[0] = new AstcPixel((short)val[4], (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); + endPoints[1] = new AstcPixel((short)val[5], (short)val[0], (short)val[1], (short)val[2]); - break; - } + break; + } case 12: - { - Span<uint> val = ReadUintColorValues(8, colorValues, ref colorValuesPosition); - - if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) { - endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]); - endPoints[1] = new AstcPixel((short)val[7], (short)val[1], (short)val[3], (short)val[5]); - } - else - { - endPoints[0] = AstcPixel.BlueContract((short)val[7], (short)val[1], (short)val[3], (short)val[5]); - endPoints[1] = AstcPixel.BlueContract((short)val[6], (short)val[0], (short)val[2], (short)val[4]); - } + Span<uint> val = ReadUintColorValues(8, colorValues, ref colorValuesPosition); - break; - } + if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) + { + endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel((short)val[7], (short)val[1], (short)val[3], (short)val[5]); + } + else + { + endPoints[0] = AstcPixel.BlueContract((short)val[7], (short)val[1], (short)val[3], (short)val[5]); + endPoints[1] = AstcPixel.BlueContract((short)val[6], (short)val[0], (short)val[2], (short)val[4]); + } + + break; + } case 13: - { - Span<int> val = ReadIntColorValues(8, colorValues, ref colorValuesPosition); - - Bits.BitTransferSigned(ref val[1], ref val[0]); - Bits.BitTransferSigned(ref val[3], ref val[2]); - Bits.BitTransferSigned(ref val[5], ref val[4]); - Bits.BitTransferSigned(ref val[7], ref val[6]); - - if (val[1] + val[3] + val[5] >= 0) { - endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]); - endPoints[1] = new AstcPixel((short)(val[7] + val[6]), (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5])); - } - else - { - endPoints[0] = AstcPixel.BlueContract(val[6] + val[7], val[0] + val[1], val[2] + val[3], val[4] + val[5]); - endPoints[1] = AstcPixel.BlueContract(val[6], val[0], val[2], val[4]); - } + Span<int> val = ReadIntColorValues(8, colorValues, ref colorValuesPosition); - endPoints[0].ClampByte(); - endPoints[1].ClampByte(); + Bits.BitTransferSigned(ref val[1], ref val[0]); + Bits.BitTransferSigned(ref val[3], ref val[2]); + Bits.BitTransferSigned(ref val[5], ref val[4]); + Bits.BitTransferSigned(ref val[7], ref val[6]); - break; - } + if (val[1] + val[3] + val[5] >= 0) + { + endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel((short)(val[7] + val[6]), (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5])); + } + else + { + endPoints[0] = AstcPixel.BlueContract(val[6] + val[7], val[0] + val[1], val[2] + val[3], val[4] + val[5]); + endPoints[1] = AstcPixel.BlueContract(val[6], val[0], val[2], val[4]); + } + + endPoints[0].ClampByte(); + endPoints[1].ClampByte(); + + break; + } default: throw new AstcDecoderException("Unsupported color endpoint mode (is it HDR?)"); @@ -1146,7 +1228,13 @@ namespace Ryujinx.Graphics.Texture.Astc // We now have enough to decode our integer sequence. IntegerSequence integerEncodedSequence; - unsafe { _ = &integerEncodedSequence; } // Skip struct initialization + + unsafe + { + // Skip struct initialization + _ = &integerEncodedSequence; + } + integerEncodedSequence.Reset(); IntegerEncoded.DecodeIntegerSequence(ref integerEncodedSequence, ref colorBitStream, range, numberValues); @@ -1162,148 +1250,148 @@ namespace Ryujinx.Graphics.Texture.Astc Debug.Assert(bitLength >= 1); - int a = 0, b = 0, c = 0, d = 0; + int b = 0, c = 0, d = 0; // A is just the lsb replicated 9 times. - a = Bits.Replicate(bitValue & 1, 1, 9); + int a = Bits.Replicate(bitValue & 1, 1, 9); switch (intEncoded.GetEncoding()) { case IntegerEncoded.EIntegerEncoding.JustBits: - { - outputValues[outputIndices++] = Bits.Replicate(bitValue, bitLength, 8); + { + outputValues[outputIndices++] = Bits.Replicate(bitValue, bitLength, 8); - break; - } + break; + } case IntegerEncoded.EIntegerEncoding.Trit: - { - d = intEncoded.TritValue; - - switch (bitLength) { - case 1: - { - c = 204; + d = intEncoded.TritValue; - break; + switch (bitLength) + { + case 1: + { + c = 204; + + break; + } + + case 2: + { + c = 93; + // B = b000b0bb0 + int b2 = (bitValue >> 1) & 1; + b = (b2 << 8) | (b2 << 4) | (b2 << 2) | (b2 << 1); + + break; + } + + case 3: + { + c = 44; + // B = cb000cbcb + int cb = (bitValue >> 1) & 3; + b = (cb << 7) | (cb << 2) | cb; + + break; + } + + + case 4: + { + c = 22; + // B = dcb000dcb + int dcb = (bitValue >> 1) & 7; + b = (dcb << 6) | dcb; + + break; + } + + case 5: + { + c = 11; + // B = edcb000ed + int edcb = (bitValue >> 1) & 0xF; + b = (edcb << 5) | (edcb >> 2); + + break; + } + + case 6: + { + c = 5; + // B = fedcb000f + int fedcb = (bitValue >> 1) & 0x1F; + b = (fedcb << 4) | (fedcb >> 4); + + break; + } + + default: + throw new AstcDecoderException("Unsupported trit encoding for color values."); } - case 2: - { - c = 93; - // B = b000b0bb0 - int b2 = (bitValue >> 1) & 1; - b = (b2 << 8) | (b2 << 4) | (b2 << 2) | (b2 << 1); - - break; - } - - case 3: - { - c = 44; - // B = cb000cbcb - int cb = (bitValue >> 1) & 3; - b = (cb << 7) | (cb << 2) | cb; - - break; - } - - - case 4: - { - c = 22; - // B = dcb000dcb - int dcb = (bitValue >> 1) & 7; - b = (dcb << 6) | dcb; - - break; - } - - case 5: - { - c = 11; - // B = edcb000ed - int edcb = (bitValue >> 1) & 0xF; - b = (edcb << 5) | (edcb >> 2); - - break; - } - - case 6: - { - c = 5; - // B = fedcb000f - int fedcb = (bitValue >> 1) & 0x1F; - b = (fedcb << 4) | (fedcb >> 4); - - break; - } - - default: - throw new AstcDecoderException("Unsupported trit encoding for color values."); + break; } - break; - } - case IntegerEncoded.EIntegerEncoding.Quint: - { - d = intEncoded.QuintValue; - - switch (bitLength) { - case 1: + d = intEncoded.QuintValue; + + switch (bitLength) { - c = 113; + case 1: + { + c = 113; - break; + break; + } + + case 2: + { + c = 54; + // B = b0000bb00 + int b2 = (bitValue >> 1) & 1; + b = (b2 << 8) | (b2 << 3) | (b2 << 2); + + break; + } + + case 3: + { + c = 26; + // B = cb0000cbc + int cb = (bitValue >> 1) & 3; + b = (cb << 7) | (cb << 1) | (cb >> 1); + + break; + } + + case 4: + { + c = 13; + // B = dcb0000dc + int dcb = (bitValue >> 1) & 7; + b = (dcb << 6) | (dcb >> 1); + + break; + } + + case 5: + { + c = 6; + // B = edcb0000e + int edcb = (bitValue >> 1) & 0xF; + b = (edcb << 5) | (edcb >> 3); + + break; + } + + default: + throw new AstcDecoderException("Unsupported quint encoding for color values."); } - - case 2: - { - c = 54; - // B = b0000bb00 - int b2 = (bitValue >> 1) & 1; - b = (b2 << 8) | (b2 << 3) | (b2 << 2); - - break; - } - - case 3: - { - c = 26; - // B = cb0000cbc - int cb = (bitValue >> 1) & 3; - b = (cb << 7) | (cb << 1) | (cb >> 1); - - break; - } - - case 4: - { - c = 13; - // B = dcb0000dc - int dcb = (bitValue >> 1) & 7; - b = (dcb << 6) | (dcb >> 1); - - break; - } - - case 5: - { - c = 6; - // B = edcb0000e - int edcb = (bitValue >> 1) & 0xF; - b = (edcb << 5) | (edcb >> 3); - - break; - } - - default: - throw new AstcDecoderException("Unsupported quint encoding for color values."); + break; } - break; - } } if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits) @@ -1493,105 +1581,105 @@ namespace Ryujinx.Graphics.Texture.Astc switch (layout) { case 0: - { - int a = (modeBits >> 5) & 0x3; - int b = (modeBits >> 7) & 0x3; + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x3; - texelParams.Width = b + 4; - texelParams.Height = a + 2; + texelParams.Width = b + 4; + texelParams.Height = a + 2; - break; - } + break; + } case 1: - { - int a = (modeBits >> 5) & 0x3; - int b = (modeBits >> 7) & 0x3; + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x3; - texelParams.Width = b + 8; - texelParams.Height = a + 2; + texelParams.Width = b + 8; + texelParams.Height = a + 2; - break; - } + break; + } case 2: - { - int a = (modeBits >> 5) & 0x3; - int b = (modeBits >> 7) & 0x3; + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x3; - texelParams.Width = a + 2; - texelParams.Height = b + 8; + texelParams.Width = a + 2; + texelParams.Height = b + 8; - break; - } + break; + } case 3: - { - int a = (modeBits >> 5) & 0x3; - int b = (modeBits >> 7) & 0x1; + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x1; - texelParams.Width = a + 2; - texelParams.Height = b + 6; + texelParams.Width = a + 2; + texelParams.Height = b + 6; - break; - } + break; + } case 4: - { - int a = (modeBits >> 5) & 0x3; - int b = (modeBits >> 7) & 0x1; + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x1; - texelParams.Width = b + 2; - texelParams.Height = a + 2; + texelParams.Width = b + 2; + texelParams.Height = a + 2; - break; - } + break; + } case 5: - { - int a = (modeBits >> 5) & 0x3; + { + int a = (modeBits >> 5) & 0x3; - texelParams.Width = 12; - texelParams.Height = a + 2; + texelParams.Width = 12; + texelParams.Height = a + 2; - break; - } + break; + } case 6: - { - int a = (modeBits >> 5) & 0x3; + { + int a = (modeBits >> 5) & 0x3; - texelParams.Width = a + 2; - texelParams.Height = 12; + texelParams.Width = a + 2; + texelParams.Height = 12; - break; - } + break; + } case 7: - { - texelParams.Width = 6; - texelParams.Height = 10; + { + texelParams.Width = 6; + texelParams.Height = 10; - break; - } + break; + } case 8: - { - texelParams.Width = 10; - texelParams.Height = 6; - break; - } + { + texelParams.Width = 10; + texelParams.Height = 6; + break; + } case 9: - { - int a = (modeBits >> 5) & 0x3; - int b = (modeBits >> 9) & 0x3; + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 9) & 0x3; - texelParams.Width = a + 6; - texelParams.Height = b + 6; + texelParams.Width = a + 6; + texelParams.Height = b + 6; - break; - } + break; + } default: // Don't know this layout... diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs index fdc482671..9ff3ddb22 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Graphics.Texture.Astc { public AstcDecoderException(string exMsg) : base(exMsg) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs index 13197714e..e6ca1035c 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Texture.Astc Components[index] = (short)value; } - public int Pack() + public readonly int Pack() { return A << 24 | B << 16 | diff --git a/src/Ryujinx.Graphics.Texture/Astc/BitStream128.cs b/src/Ryujinx.Graphics.Texture/Astc/BitStream128.cs index 3bf9769f5..f98a714d1 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/BitStream128.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/BitStream128.cs @@ -6,7 +6,9 @@ namespace Ryujinx.Graphics.Texture.Astc { public struct BitStream128 { +#pragma warning disable IDE0044 // Make field readonly private Buffer16 _data; +#pragma warning restore IDE0044 public int BitsLeft { get; set; } public BitStream128(Buffer16 data) @@ -42,7 +44,10 @@ namespace Ryujinx.Graphics.Texture.Astc { Debug.Assert(bitCount < 32); - if (bitCount == 0) return; + if (bitCount == 0) + { + return; + } ulong maskedValue = (uint)(value & ((1 << bitCount) - 1)); @@ -69,4 +74,4 @@ namespace Ryujinx.Graphics.Texture.Astc BitsLeft += bitCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/Astc/Bits.cs b/src/Ryujinx.Graphics.Texture/Astc/Bits.cs index b140a20a0..91652bf14 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/Bits.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/Bits.cs @@ -29,8 +29,15 @@ public static int Replicate(int value, int numberBits, int toBit) { - if (numberBits == 0) return 0; - if (toBit == 0) return 0; + if (numberBits == 0) + { + return 0; + } + + if (toBit == 0) + { + return 0; + } int tempValue = value & ((1 << numberBits) - 1); int retValue = tempValue; @@ -60,7 +67,10 @@ b |= a & 0x80; a >>= 1; a &= 0x3F; - if ((a & 0x20) != 0) a -= 0x40; + if ((a & 0x20) != 0) + { + a -= 0x40; + } } } } diff --git a/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs b/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs index 065de46be..56e78cc36 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Texture.Astc internal struct IntegerEncoded { internal const int StructSize = 8; - private static readonly IntegerEncoded[] Encodings; + private static readonly IntegerEncoded[] _encodings; public enum EIntegerEncoding : byte { @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Texture.Astc Trit } - EIntegerEncoding _encoding; + readonly EIntegerEncoding _encoding; public byte NumberBits { get; private set; } public byte TritValue { get; private set; } public byte QuintValue { get; private set; } @@ -23,11 +23,11 @@ namespace Ryujinx.Graphics.Texture.Astc static IntegerEncoded() { - Encodings = new IntegerEncoded[0x100]; + _encodings = new IntegerEncoded[0x100]; - for (int i = 0; i < Encodings.Length; i++) + for (int i = 0; i < _encodings.Length; i++) { - Encodings[i] = CreateEncodingCalc(i); + _encodings[i] = CreateEncodingCalc(i); } } @@ -40,17 +40,17 @@ namespace Ryujinx.Graphics.Texture.Astc QuintValue = 0; } - public bool MatchesEncoding(IntegerEncoded other) + public readonly bool MatchesEncoding(IntegerEncoded other) { return _encoding == other._encoding && NumberBits == other.NumberBits; } - public EIntegerEncoding GetEncoding() + public readonly EIntegerEncoding GetEncoding() { return _encoding; } - public int GetBitLength(int numberVals) + public readonly int GetBitLength(int numberVals) { int totalBits = NumberBits * numberVals; if (_encoding == EIntegerEncoding.Trit) @@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Texture.Astc public static IntegerEncoded CreateEncoding(int maxVal) { - return Encodings[maxVal]; + return _encodings[maxVal]; } private static IntegerEncoded CreateEncodingCalc(int maxVal) @@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Texture.Astc ReadOnlySpan<byte> encodings = GetTritEncoding(encoded); - IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue); + IntegerEncoded intEncoded = new(EIntegerEncoding.Trit, numberBitsPerValue); for (int i = 0; i < 5; i++) { @@ -159,7 +159,7 @@ namespace Ryujinx.Graphics.Texture.Astc for (int i = 0; i < 3; i++) { - IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Quint, numberBitsPerValue) + IntegerEncoded intEncoded = new(EIntegerEncoding.Quint, numberBitsPerValue) { BitValue = m[i], QuintValue = encodings[i] @@ -185,29 +185,29 @@ namespace Ryujinx.Graphics.Texture.Astc switch (intEncoded.GetEncoding()) { case EIntegerEncoding.Quint: - { - DecodeQuintBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); - numberValuesDecoded += 3; + { + DecodeQuintBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); + numberValuesDecoded += 3; - break; - } + break; + } case EIntegerEncoding.Trit: - { - DecodeTritBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); - numberValuesDecoded += 5; + { + DecodeTritBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); + numberValuesDecoded += 5; - break; - } + break; + } case EIntegerEncoding.JustBits: - { - intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits); - decodeIntegerSequence.Add(ref intEncoded); - numberValuesDecoded++; + { + intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits); + decodeIntegerSequence.Add(ref intEncoded); + numberValuesDecoded++; - break; - } + break; + } } } } diff --git a/src/Ryujinx.Graphics.Texture/BC6Decoder.cs b/src/Ryujinx.Graphics.Texture/BC6Decoder.cs index 819bf022f..f761a4222 100644 --- a/src/Ryujinx.Graphics.Texture/BC6Decoder.cs +++ b/src/Ryujinx.Graphics.Texture/BC6Decoder.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Texture int x2 = x * 4; int bw = Math.Min(4, width - x2); - DecodeBlock(blocks[y * wInBlocks + x], output64.Slice(y2 * width + x2), bw, bh, width, signed); + DecodeBlock(blocks[y * wInBlocks + x], output64[(y2 * width + x2)..], bw, bh, width, signed); } } } diff --git a/src/Ryujinx.Graphics.Texture/BC7Decoder.cs b/src/Ryujinx.Graphics.Texture/BC7Decoder.cs index b865a5593..c1c704291 100644 --- a/src/Ryujinx.Graphics.Texture/BC7Decoder.cs +++ b/src/Ryujinx.Graphics.Texture/BC7Decoder.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Texture int x2 = x * 4; int bw = Math.Min(4, width - x2); - DecodeBlock(blocks[y * wInBlocks + x], output32.Slice(y2 * width + x2), bw, bh, width); + DecodeBlock(blocks[y * wInBlocks + x], output32[(y2 * width + x2)..], bw, bh, width); } } } @@ -177,9 +177,18 @@ namespace Ryujinx.Graphics.Texture switch (rotation) { - case 1: color.A = color.R; color.R = a; break; - case 2: color.A = color.G; color.G = a; break; - case 3: color.A = color.B; color.B = a; break; + case 1: + color.A = color.R; + color.R = a; + break; + case 2: + color.A = color.G; + color.G = a; + break; + case 3: + color.A = color.B; + color.B = a; + break; } } diff --git a/src/Ryujinx.Graphics.Texture/BCnDecoder.cs b/src/Ryujinx.Graphics.Texture/BCnDecoder.cs index b21fa4d10..2d68ca346 100644 --- a/src/Ryujinx.Graphics.Texture/BCnDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/BCnDecoder.cs @@ -54,10 +54,10 @@ namespace Ryujinx.Graphics.Texture if (copyHeight == 4) { - outputLine0 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs)); - outputLine1 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width)); - outputLine2 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width * 2)); - outputLine3 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width * 3)); + outputLine0 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[lineBaseOOffs..]); + outputLine1 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width)..]); + outputLine2 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width * 2)..]); + outputLine3 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width * 3)..]); } for (int x = 0; x < w; x++) @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Texture } } - data = data.Slice(8); + data = data[8..]; } } @@ -142,10 +142,10 @@ namespace Ryujinx.Graphics.Texture if (copyHeight == 4) { - outputLine0 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs)); - outputLine1 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width)); - outputLine2 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width * 2)); - outputLine3 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width * 3)); + outputLine0 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[lineBaseOOffs..]); + outputLine1 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width)..]); + outputLine2 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width * 2)..]); + outputLine3 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width * 3)..]); } for (int x = 0; x < w; x++) @@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Texture int baseX = x * BlockWidth; int copyWidth = Math.Min(BlockWidth, width - baseX); - BC23DecodeTileRgb(tile, data.Slice(8)); + BC23DecodeTileRgb(tile, data[8..]); ulong block = BinaryPrimitives.ReadUInt64LittleEndian(data); @@ -179,7 +179,7 @@ namespace Ryujinx.Graphics.Texture } } - data = data.Slice(16); + data = data[16..]; } } @@ -238,10 +238,10 @@ namespace Ryujinx.Graphics.Texture if (copyHeight == 4) { - outputLine0 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs)); - outputLine1 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width)); - outputLine2 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width * 2)); - outputLine3 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint.Slice(lineBaseOOffs + width * 3)); + outputLine0 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[lineBaseOOffs..]); + outputLine1 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width)..]); + outputLine2 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width * 2)..]); + outputLine3 = MemoryMarshal.Cast<uint, Vector128<byte>>(outputAsUint[(lineBaseOOffs + width * 3)..]); } for (int x = 0; x < w; x++) @@ -249,7 +249,7 @@ namespace Ryujinx.Graphics.Texture int baseX = x * BlockWidth; int copyWidth = Math.Min(BlockWidth, width - baseX); - BC23DecodeTileRgb(tile, data.Slice(8)); + BC23DecodeTileRgb(tile, data[8..]); ulong block = BinaryPrimitives.ReadUInt64LittleEndian(data); @@ -276,7 +276,7 @@ namespace Ryujinx.Graphics.Texture } } - data = data.Slice(16); + data = data[16..]; } } @@ -305,7 +305,7 @@ namespace Ryujinx.Graphics.Texture int alignedWidth = BitUtils.AlignUp(width, 4); byte[] output = new byte[size]; - Span<byte> outputSpan = new Span<byte>(output); + Span<byte> outputSpan = new(output); ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data); @@ -338,10 +338,10 @@ namespace Ryujinx.Graphics.Texture if (copyHeight == 4) { - outputLine0 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs)); - outputLine1 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth)); - outputLine2 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth * 2)); - outputLine3 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth * 3)); + outputLine0 = MemoryMarshal.Cast<byte, uint>(outputSpan[lineBaseOOffs..]); + outputLine1 = MemoryMarshal.Cast<byte, uint>(outputSpan[(lineBaseOOffs + alignedWidth)..]); + outputLine2 = MemoryMarshal.Cast<byte, uint>(outputSpan[(lineBaseOOffs + alignedWidth * 2)..]); + outputLine3 = MemoryMarshal.Cast<byte, uint>(outputSpan[(lineBaseOOffs + alignedWidth * 3)..]); } for (int x = 0; x < w; x++) @@ -382,7 +382,7 @@ namespace Ryujinx.Graphics.Texture } } - data64 = data64.Slice(1); + data64 = data64[1..]; } } @@ -450,10 +450,10 @@ namespace Ryujinx.Graphics.Texture if (copyHeight == 4) { - outputLine0 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs)); - outputLine1 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth)); - outputLine2 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth * 2)); - outputLine3 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth * 3)); + outputLine0 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort[lineBaseOOffs..]); + outputLine1 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort[(lineBaseOOffs + alignedWidth)..]); + outputLine2 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort[(lineBaseOOffs + alignedWidth * 2)..]); + outputLine3 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort[(lineBaseOOffs + alignedWidth * 3)..]); } for (int x = 0; x < w; x++) @@ -507,7 +507,7 @@ namespace Ryujinx.Graphics.Texture } } - data64 = data64.Slice(2); + data64 = data64[2..]; } } @@ -548,7 +548,7 @@ namespace Ryujinx.Graphics.Texture { for (int z = 0; z < depth; z++) { - BC6Decoder.Decode(output.AsSpan().Slice(outputOffset), data.Slice(inputOffset), width, height, signed); + BC6Decoder.Decode(output.AsSpan()[outputOffset..], data[inputOffset..], width, height, signed); inputOffset += w * h * 16; outputOffset += width * height * 8; @@ -586,7 +586,7 @@ namespace Ryujinx.Graphics.Texture { for (int z = 0; z < depth; z++) { - BC7Decoder.Decode(output.AsSpan().Slice(outputOffset), data.Slice(inputOffset), width, height); + BC7Decoder.Decode(output.AsSpan()[outputOffset..], data[inputOffset..], width, height); inputOffset += w * h * 16; outputOffset += width * height * 4; @@ -813,7 +813,7 @@ namespace Ryujinx.Graphics.Texture { Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output); - uint indices = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(4)); + uint indices = BinaryPrimitives.ReadUInt32LittleEndian(input[4..]); for (int i = 0; i < BlockWidth * BlockHeight; i++, indices >>= 2) { @@ -891,4 +891,4 @@ namespace Ryujinx.Graphics.Texture return r | (g & 0xff00) | (b & 0xff0000); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/BCnEncoder.cs b/src/Ryujinx.Graphics.Texture/BCnEncoder.cs index 02b79c1b8..8103990ff 100644 --- a/src/Ryujinx.Graphics.Texture/BCnEncoder.cs +++ b/src/Ryujinx.Graphics.Texture/BCnEncoder.cs @@ -28,8 +28,6 @@ namespace Ryujinx.Graphics.Texture for (int l = 0; l < levels; l++) { - int rgba8Size = width * height * depth * layers * 4; - int w = BitUtils.DivRoundUp(width, BlockWidth); int h = BitUtils.DivRoundUp(height, BlockHeight); @@ -38,8 +36,8 @@ namespace Ryujinx.Graphics.Texture for (int z = 0; z < depth; z++) { BC7Encoder.Encode( - output.AsMemory().Slice(imageBaseOOffs), - data.AsMemory().Slice(imageBaseIOffs), + output.AsMemory()[imageBaseOOffs..], + data.AsMemory()[imageBaseIOffs..], width, height, EncodeMode.Fast | EncodeMode.Multithreaded); @@ -57,4 +55,4 @@ namespace Ryujinx.Graphics.Texture return output; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/BlockLinearConstants.cs b/src/Ryujinx.Graphics.Texture/BlockLinearConstants.cs index d95691cf6..ad0dda851 100644 --- a/src/Ryujinx.Graphics.Texture/BlockLinearConstants.cs +++ b/src/Ryujinx.Graphics.Texture/BlockLinearConstants.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Texture public const int GobSize = GobStride * GobHeight; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/BlockLinearLayout.cs b/src/Ryujinx.Graphics.Texture/BlockLinearLayout.cs index e098e959c..06d984ace 100644 --- a/src/Ryujinx.Graphics.Texture/BlockLinearLayout.cs +++ b/src/Ryujinx.Graphics.Texture/BlockLinearLayout.cs @@ -15,24 +15,24 @@ namespace Ryujinx.Graphics.Texture public RobAndSliceSizes(int robSize, int sliceSize) { - RobSize = robSize; + RobSize = robSize; SliceSize = sliceSize; } } - private int _texBpp; + private readonly int _texBpp; - private int _bhMask; - private int _bdMask; + private readonly int _bhMask; + private readonly int _bdMask; - private int _bhShift; - private int _bdShift; - private int _bppShift; + private readonly int _bhShift; + private readonly int _bdShift; + private readonly int _bppShift; - private int _xShift; + private readonly int _xShift; - private int _robSize; - private int _sliceSize; + private readonly int _robSize; + private readonly int _sliceSize; // Variables for built in iteration. private int _yPart; @@ -60,7 +60,7 @@ namespace Ryujinx.Graphics.Texture RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gobBlocksInY, gobBlocksInZ); - _robSize = rsSizes.RobSize; + _robSize = rsSizes.RobSize; _sliceSize = rsSizes.SliceSize; } @@ -192,4 +192,4 @@ namespace Ryujinx.Graphics.Texture return offset + _yzPart; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/Bpp12Pixel.cs b/src/Ryujinx.Graphics.Texture/Bpp12Pixel.cs index 5a38259e2..6e2cb8a79 100644 --- a/src/Ryujinx.Graphics.Texture/Bpp12Pixel.cs +++ b/src/Ryujinx.Graphics.Texture/Bpp12Pixel.cs @@ -3,9 +3,9 @@ namespace Ryujinx.Graphics.Texture { [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 12)] - public struct Bpp12Pixel + public readonly struct Bpp12Pixel { - private ulong _elem1; - private uint _elem2; + private readonly ulong _elem1; + private readonly uint _elem2; } } diff --git a/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs b/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs index 35d36bcef..d0d1666a0 100644 --- a/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs +++ b/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs @@ -119,12 +119,12 @@ namespace Ryujinx.Graphics.Texture.Encoders { uint c = tile[i]; - if (!uniqueRGB.Slice(0, uniqueRGBCount).Contains(c & rgbMask)) + if (!uniqueRGB[..uniqueRGBCount].Contains(c & rgbMask)) { uniqueRGB[uniqueRGBCount++] = c & rgbMask; } - if (!uniqueAlpha.Slice(0, uniqueAlphaCount).Contains(c & alphaMask)) + if (!uniqueAlpha[..uniqueAlphaCount].Contains(c & alphaMask)) { uniqueAlpha[uniqueAlphaCount++] = c & alphaMask; } @@ -356,7 +356,7 @@ namespace Ryujinx.Graphics.Texture.Encoders bool alphaSwapSubset = alphaIndices[0] >= (alphaIndexCount >> 1); - Block block = new Block(); + Block block = new(); int offset = 0; @@ -591,7 +591,7 @@ namespace Ryujinx.Graphics.Texture.Encoders RgbaColor32 e132 = RgbaColor8.FromUInt32(c1).GetColor32(); palette[0] = e032; - palette[palette.Length - 1] = e132; + palette[^1] = e132; for (int i = 1; i < palette.Length - 1; i++) { @@ -888,7 +888,7 @@ namespace Ryujinx.Graphics.Texture.Encoders int distRange = Math.Max(1, maxDist - minDist); - RgbaColor32 nV = new RgbaColor32(n); + RgbaColor32 nV = new(n); int bestErrorSum = int.MaxValue; RgbaColor8 bestE0 = default; @@ -922,8 +922,8 @@ namespace Ryujinx.Graphics.Texture.Encoders for (int start = 0; start < numInterpolatedColors - maxIndex; start++) { - RgbaColor32 sumY = new RgbaColor32(0); - RgbaColor32 sumXY = new RgbaColor32(0); + RgbaColor32 sumY = new(0); + RgbaColor32 sumXY = new(0); for (int i = 0; i < indices.Length; i++) { @@ -933,8 +933,8 @@ namespace Ryujinx.Graphics.Texture.Encoders sumXY += new RgbaColor32(start + indices[i]) * y; } - RgbaColor32 sumXV = new RgbaColor32(sumX); - RgbaColor32 sumXXV = new RgbaColor32(sumXX); + RgbaColor32 sumXV = new(sumX); + RgbaColor32 sumXXV = new(sumXX); RgbaColor32 m = RgbaColor32.DivideGuarded((nV * sumXY - sumXV * sumY) << 6, nV * sumXXV - sumXV * sumXV, 0); RgbaColor32 b = ((sumY << 6) - m * sumXV) / nV; diff --git a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs index 503985c98..0f7c304e0 100644 --- a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Texture int wAligned = BitUtils.AlignUp(width, alignment); - BlockLinearLayout layoutConverter = new BlockLinearLayout(wAligned, height, gobBlocksInY, 1, bytesPerPixel); + BlockLinearLayout layoutConverter = new(wAligned, height, gobBlocksInY, 1, bytesPerPixel); unsafe bool Convert<T>(Span<byte> output, ReadOnlySpan<byte> data) where T : unmanaged { @@ -126,14 +126,14 @@ namespace Ryujinx.Graphics.Texture int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInZ = gobBlocksInZ; - int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; + int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; int gobHeight = gobBlocksInY * GobHeight; for (int level = 0; level < levels; level++) { - int w = Math.Max(1, width >> level); + int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); - int d = Math.Max(1, depth >> level); + int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Texture int wAligned = BitUtils.AlignUp(w, alignment); - BlockLinearLayout layoutConverter = new BlockLinearLayout( + BlockLinearLayout layoutConverter = new( wAligned, h, mipGobBlocksInY, @@ -256,7 +256,7 @@ namespace Ryujinx.Graphics.Texture int bytesPerPixel, ReadOnlySpan<byte> data) { - int w = BitUtils.DivRoundUp(width, blockWidth); + int w = BitUtils.DivRoundUp(width, blockWidth); int h = BitUtils.DivRoundUp(height, blockHeight); int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment); @@ -301,7 +301,7 @@ namespace Ryujinx.Graphics.Texture int wAligned = BitUtils.AlignUp(width, alignment); - BlockLinearLayout layoutConverter = new BlockLinearLayout(wAligned, height, gobBlocksInY, 1, bytesPerPixel); + BlockLinearLayout layoutConverter = new(wAligned, height, gobBlocksInY, 1, bytesPerPixel); unsafe bool Convert<T>(Span<byte> output, ReadOnlySpan<byte> data) where T : unmanaged { @@ -390,14 +390,14 @@ namespace Ryujinx.Graphics.Texture int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInZ = gobBlocksInZ; - int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; + int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; int gobHeight = gobBlocksInY * GobHeight; for (int level = 0; level < levels; level++) { - int w = Math.Max(1, width >> level); + int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); - int d = Math.Max(1, depth >> level); + int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); @@ -430,7 +430,7 @@ namespace Ryujinx.Graphics.Texture int wAligned = BitUtils.AlignUp(w, alignment); - BlockLinearLayout layoutConverter = new BlockLinearLayout( + BlockLinearLayout layoutConverter = new( wAligned, h, mipGobBlocksInY, @@ -521,7 +521,7 @@ namespace Ryujinx.Graphics.Texture int bytesPerPixel, ReadOnlySpan<byte> data) { - int w = BitUtils.DivRoundUp(width, blockWidth); + int w = BitUtils.DivRoundUp(width, blockWidth); int h = BitUtils.DivRoundUp(height, blockHeight); int inStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment); @@ -573,9 +573,9 @@ namespace Ryujinx.Graphics.Texture for (int level = 0; level < levels; level++) { - int w = Math.Max(1, width >> level); + int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); - int d = Math.Max(1, depth >> level); + int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); @@ -588,4 +588,4 @@ namespace Ryujinx.Graphics.Texture return layerSize * layers; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs b/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs index d7472e2f1..48d36cdfb 100644 --- a/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs +++ b/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs @@ -7,30 +7,30 @@ namespace Ryujinx.Graphics.Texture { public class OffsetCalculator { - private int _width; - private int _height; - private int _stride; - private bool _isLinear; - private int _bytesPerPixel; + private readonly int _width; + private readonly int _height; + private readonly int _stride; + private readonly bool _isLinear; + private readonly int _bytesPerPixel; - private BlockLinearLayout _layoutConverter; + private readonly BlockLinearLayout _layoutConverter; // Variables for built in iteration. private int _yPart; public OffsetCalculator( - int width, - int height, - int stride, + int width, + int height, + int stride, bool isLinear, - int gobBlocksInY, - int gobBlocksInZ, - int bytesPerPixel) + int gobBlocksInY, + int gobBlocksInZ, + int bytesPerPixel) { - _width = width; - _height = height; - _stride = stride; - _isLinear = isLinear; + _width = width; + _height = height; + _stride = stride; + _isLinear = isLinear; _bytesPerPixel = bytesPerPixel; int wAlignment = GobStride / bytesPerPixel; @@ -138,4 +138,4 @@ namespace Ryujinx.Graphics.Texture } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/PixelConverter.cs b/src/Ryujinx.Graphics.Texture/PixelConverter.cs index add25cd35..7955aed3f 100644 --- a/src/Ryujinx.Graphics.Texture/PixelConverter.cs +++ b/src/Ryujinx.Graphics.Texture/PixelConverter.cs @@ -87,9 +87,9 @@ namespace Ryujinx.Graphics.Texture { uint packed = inputSpan[offset++]; - uint outputPacked = 0xff000000; - outputPacked |= (packed << 3) & 0x000000f8; - outputPacked |= (packed << 8) & 0x00f80000; + uint outputPacked = 0xff000000; + outputPacked |= (packed << 3) & 0x000000f8; + outputPacked |= (packed << 8) & 0x00f80000; // Replicate 5 bit components. outputPacked |= (outputPacked >> 5) & 0x00070007; @@ -126,10 +126,10 @@ namespace Ryujinx.Graphics.Texture uint a = forceAlpha ? 1 : (packed >> 15); - uint outputPacked = a * 0xff000000; - outputPacked |= (packed << 3) & 0x000000f8; - outputPacked |= (packed << 6) & 0x0000f800; - outputPacked |= (packed << 9) & 0x00f80000; + uint outputPacked = a * 0xff000000; + outputPacked |= (packed << 3) & 0x000000f8; + outputPacked |= (packed << 6) & 0x0000f800; + outputPacked |= (packed << 9) & 0x00f80000; // Replicate 5 bit components. outputPacked |= (outputPacked >> 5) & 0x00070707; @@ -198,10 +198,10 @@ namespace Ryujinx.Graphics.Texture { uint packed = inputSpan[offset++]; - uint outputPacked = packed & 0x0000000f; - outputPacked |= (packed << 4) & 0x00000f00; - outputPacked |= (packed << 8) & 0x000f0000; - outputPacked |= (packed << 12) & 0x0f000000; + uint outputPacked = packed & 0x0000000f; + outputPacked |= (packed << 4) & 0x00000f00; + outputPacked |= (packed << 8) & 0x000f0000; + outputPacked |= (packed << 12) & 0x0f000000; outputSpan[outOffset++] = outputPacked * 0x11; } diff --git a/src/Ryujinx.Graphics.Texture/Size.cs b/src/Ryujinx.Graphics.Texture/Size.cs index 21c45b386..78d3cb579 100644 --- a/src/Ryujinx.Graphics.Texture/Size.cs +++ b/src/Ryujinx.Graphics.Texture/Size.cs @@ -2,15 +2,15 @@ namespace Ryujinx.Graphics.Texture { public readonly struct Size { - public int Width { get; } + public int Width { get; } public int Height { get; } - public int Depth { get; } + public int Depth { get; } public Size(int width, int height, int depth) { - Width = width; + Width = width; Height = height; - Depth = depth; + Depth = depth; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/SizeCalculator.cs b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs index 0120bd7ac..7fe89e7e2 100644 --- a/src/Ryujinx.Graphics.Texture/SizeCalculator.cs +++ b/src/Ryujinx.Graphics.Texture/SizeCalculator.cs @@ -1,6 +1,5 @@ using Ryujinx.Common; using System; - using static Ryujinx.Graphics.Texture.BlockLinearConstants; namespace Ryujinx.Graphics.Texture @@ -48,16 +47,16 @@ namespace Ryujinx.Graphics.Texture int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInZ = gobBlocksInZ; - int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; + int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; int gobHeight = gobBlocksInY * GobHeight; int depthLevelOffset = 0; for (int level = 0; level < levels; level++) { - int w = Math.Max(1, width >> level); + int w = Math.Max(1, width >> level); int h = Math.Max(1, height >> level); - int d = Math.Max(1, depth >> level); + int d = Math.Max(1, depth >> level); w = BitUtils.DivRoundUp(w, blockWidth); h = BitUtils.DivRoundUp(h, blockHeight); @@ -104,7 +103,7 @@ namespace Ryujinx.Graphics.Texture for (int z = 0; z < d; z++) { - int zLow = z & mask; + int zLow = z & mask; int zHigh = z & ~mask; allOffsets[z + depthLevelOffset] = baseOffset + zLow * gobSize + zHigh * sliceSize; @@ -159,7 +158,7 @@ namespace Ryujinx.Graphics.Texture { for (int layer = 0; layer < layers; layer++) { - int baseIndex = layer * levels; + int baseIndex = layer * levels; int baseOffset = layer * layerSize; for (int level = 0; level < levels; level++) @@ -234,10 +233,10 @@ namespace Ryujinx.Graphics.Texture int gobBlocksInZ, int gobBlocksInTileX) { - width = BitUtils.DivRoundUp(width, blockWidth); + width = BitUtils.DivRoundUp(width, blockWidth); height = BitUtils.DivRoundUp(height, blockHeight); - int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; + int gobWidth = (GobStride / bytesPerPixel) * gobBlocksInTileX; int gobHeight = gobBlocksInY * GobHeight; int alignment = gobWidth; @@ -251,11 +250,11 @@ namespace Ryujinx.Graphics.Texture (gobBlocksInY, gobBlocksInZ) = GetMipGobBlockSizes(height, depth, 1, gobBlocksInY, gobBlocksInZ); int blockOfGobsHeight = gobBlocksInY * GobHeight; - int blockOfGobsDepth = gobBlocksInZ; + int blockOfGobsDepth = gobBlocksInZ; - width = BitUtils.AlignUp(width, alignment); + width = BitUtils.AlignUp(width, alignment); height = BitUtils.AlignUp(height, blockOfGobsHeight); - depth = BitUtils.AlignUp(depth, blockOfGobsDepth); + depth = BitUtils.AlignUp(depth, blockOfGobsDepth); return new Size(width, height, depth); } @@ -267,7 +266,7 @@ namespace Ryujinx.Graphics.Texture int blockHeight, int bytesPerPixel) { - width = BitUtils.DivRoundUp(width, blockWidth); + width = BitUtils.DivRoundUp(width, blockWidth); height = BitUtils.DivRoundUp(height, blockHeight); int widthAlignment = StrideAlignment / bytesPerPixel; @@ -300,4 +299,4 @@ namespace Ryujinx.Graphics.Texture return (gobBlocksInY, gobBlocksInZ); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/SizeInfo.cs b/src/Ryujinx.Graphics.Texture/SizeInfo.cs index eb5737283..3bec1203a 100644 --- a/src/Ryujinx.Graphics.Texture/SizeInfo.cs +++ b/src/Ryujinx.Graphics.Texture/SizeInfo.cs @@ -20,14 +20,14 @@ namespace Ryujinx.Graphics.Texture public SizeInfo(int size) { _mipOffsets = new int[] { 0 }; - AllOffsets = new int[] { 0 }; - SliceSizes = new int[] { size }; - LevelSizes = new int[] { size }; - _depth = 1; - _levels = 1; - LayerSize = size; - TotalSize = size; - _is3D = false; + AllOffsets = new int[] { 0 }; + SliceSizes = new int[] { size }; + LevelSizes = new int[] { size }; + _depth = 1; + _levels = 1; + LayerSize = size; + TotalSize = size; + _is3D = false; } internal SizeInfo( @@ -35,21 +35,21 @@ namespace Ryujinx.Graphics.Texture int[] allOffsets, int[] sliceSizes, int[] levelSizes, - int depth, - int levels, - int layerSize, - int totalSize, - bool is3D) + int depth, + int levels, + int layerSize, + int totalSize, + bool is3D) { _mipOffsets = mipOffsets; - AllOffsets = allOffsets; - SliceSizes = sliceSizes; - LevelSizes = levelSizes; - _depth = depth; - _levels = levels; - LayerSize = layerSize; - TotalSize = totalSize; - _is3D = is3D; + AllOffsets = allOffsets; + SliceSizes = sliceSizes; + LevelSizes = levelSizes; + _depth = depth; + _levels = levels; + LayerSize = layerSize; + TotalSize = totalSize; + _is3D = is3D; } public int GetMipOffset(int level) @@ -116,4 +116,4 @@ namespace Ryujinx.Graphics.Texture } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs b/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs index e6c3f6e76..3f69cb4c1 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs @@ -8,8 +8,8 @@ namespace Ryujinx.Graphics.Texture.Utils { static class BC67Utils { - private static byte[][] _quantizationLut; - private static byte[][] _quantizationLutNoPBit; + private static readonly byte[][] _quantizationLut; + private static readonly byte[][] _quantizationLutNoPBit; static BC67Utils() { @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Texture.Utils } else { - RgbaColor8 minColor = new RgbaColor8(255, 255, 255, 255); + RgbaColor8 minColor = new(255, 255, 255, 255); RgbaColor8 maxColor = default; for (int i = 0; i < tile.Length; i++) @@ -1176,8 +1176,8 @@ namespace Ryujinx.Graphics.Texture.Utils int weight = (((weightIndex << 7) / ((1 << indexBitCount) - 1)) + 1) >> 1; - RgbaColor32 weightV = new RgbaColor32(weight); - RgbaColor32 invWeightV = new RgbaColor32(64 - weight); + RgbaColor32 weightV = new(weight); + RgbaColor32 invWeightV = new(64 - weight); return (color1 * invWeightV + color2 * weightV + new RgbaColor32(32)) >> 6; } @@ -1197,8 +1197,10 @@ namespace Ryujinx.Graphics.Texture.Utils int colorWeight = BC67Tables.Weights[colorIndexBitCount - 2][colorWeightIndex]; int alphaWeight = BC67Tables.Weights[alphaIndexBitCount - 2][alphaWeightIndex]; - RgbaColor32 weightV = new RgbaColor32(colorWeight); - weightV.A = alphaWeight; + RgbaColor32 weightV = new(colorWeight) + { + A = alphaWeight + }; RgbaColor32 invWeightV = new RgbaColor32(64) - weightV; return (color1 * invWeightV + color2 * weightV + new RgbaColor32(32)) >> 6; diff --git a/src/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs b/src/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs index 687df22c5..91236f1bc 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs @@ -34,4 +34,4 @@ namespace Ryujinx.Graphics.Texture.Utils AlphaDepth = alphaDepth; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/Utils/Block.cs b/src/Ryujinx.Graphics.Texture/Utils/Block.cs index a8bae077d..3a1d50cd4 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/Block.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/Block.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Texture.Utils offset += bits; } - public ulong Decode(ref int offset, int bits) + public readonly ulong Decode(ref int offset, int bits) { ulong value; ulong mask = bits == 64 ? ulong.MaxValue : (1UL << bits) - 1; @@ -52,4 +52,4 @@ namespace Ryujinx.Graphics.Texture.Utils return value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs index 582044d97..de7c9262d 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor32.cs @@ -11,25 +11,25 @@ namespace Ryujinx.Graphics.Texture.Utils public int R { - get => _color.GetElement(0); + readonly get => _color.GetElement(0); set => _color = _color.WithElement(0, value); } public int G { - get => _color.GetElement(1); + readonly get => _color.GetElement(1); set => _color = _color.WithElement(1, value); } public int B { - get => _color.GetElement(2); + readonly get => _color.GetElement(2); set => _color = _color.WithElement(2, value); } public int A { - get => _color.GetElement(3); + readonly get => _color.GetElement(3); set => _color = _color.WithElement(3, value); } @@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Texture.Utils } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RgbaColor8 GetColor8() + public readonly RgbaColor8 GetColor8() { if (Sse41.IsSupported) { @@ -211,17 +211,17 @@ namespace Ryujinx.Graphics.Texture.Utils return (byte)Math.Clamp(value, 0, 255); } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(R, G, B, A); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is RgbaColor32 other && Equals(other); } - public bool Equals(RgbaColor32 other) + public readonly bool Equals(RgbaColor32 other) { return _color.Equals(other._color); } diff --git a/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs index 0edf1cce7..401bec387 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs @@ -54,22 +54,22 @@ namespace Ryujinx.Graphics.Texture.Utils return Unsafe.As<RgbaColor8, uint>(ref this); } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(R, G, B, A); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is RgbaColor8 other && Equals(other); } - public bool Equals(RgbaColor8 other) + public readonly bool Equals(RgbaColor8 other) { return R == other.R && G == other.G && B == other.B && A == other.A; } - public byte GetComponent(int index) + public readonly byte GetComponent(int index) { return index switch { From 981e0c082d8b55e65b7bd9ef07c92ed761b88ab7 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:54:20 +0200 Subject: [PATCH 682/737] [Spv.Generator] Address dotnet-format issues (#5394) * dotnet format style --severity info Some changes were manually reverted. * Restore a few unused methods and variables * Silence dotnet format IDE0052 warnings * Address or silence dotnet format IDE1006 warnings * Address or silence dotnet format CA1069 warnings * Address review comments * Address most dotnet format whitespace warnings * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Address IDE0251 warnings * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Rename Operand.cs to IOperand.cs * Update src/Spv.Generator/Module.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Remove NotNullWhen attribute and use conditional access to avoid NRE * Fix duplicated enum values * Remove unread member --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../Autogenerated/CoreGrammar.cs | 22 +- .../Autogenerated/GlslStd450Grammar.cs | 164 +++++---- .../Autogenerated/OpenClGrammar.cs | 322 +++++++++--------- src/Spv.Generator/ConstantKey.cs | 4 +- src/Spv.Generator/DeterministicStringKey.cs | 7 +- src/Spv.Generator/GeneratorPool.cs | 8 +- src/Spv.Generator/{Operand.cs => IOperand.cs} | 2 +- src/Spv.Generator/Instruction.cs | 30 +- src/Spv.Generator/InstructionOperands.cs | 26 +- src/Spv.Generator/LiteralInteger.cs | 4 +- src/Spv.Generator/LiteralString.cs | 4 +- src/Spv.Generator/Module.cs | 221 ++++++------ src/Spv.Generator/TypeDeclarationKey.cs | 4 +- src/Spv.Generator/spirv.cs | 169 +++++---- 14 files changed, 490 insertions(+), 497 deletions(-) rename src/Spv.Generator/{Operand.cs => IOperand.cs} (78%) diff --git a/src/Spv.Generator/Autogenerated/CoreGrammar.cs b/src/Spv.Generator/Autogenerated/CoreGrammar.cs index 3b2f6fa65..37936b8ef 100644 --- a/src/Spv.Generator/Autogenerated/CoreGrammar.cs +++ b/src/Spv.Generator/Autogenerated/CoreGrammar.cs @@ -180,7 +180,7 @@ namespace Spv.Generator return result; } - public Instruction Decorate(Instruction target, Decoration decoration, Operand parameter) + public Instruction Decorate(Instruction target, Decoration decoration, IOperand parameter) { Instruction result = NewInstruction(Op.OpDecorate); @@ -192,7 +192,7 @@ namespace Spv.Generator return result; } - public Instruction Decorate(Instruction target, Decoration decoration, params Operand[] parameters) + public Instruction Decorate(Instruction target, Decoration decoration, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpDecorate); @@ -216,7 +216,7 @@ namespace Spv.Generator return result; } - public Instruction MemberDecorate(Instruction structureType, LiteralInteger member, Decoration decoration, Operand parameter) + public Instruction MemberDecorate(Instruction structureType, LiteralInteger member, Decoration decoration, IOperand parameter) { Instruction result = NewInstruction(Op.OpMemberDecorate); @@ -229,7 +229,7 @@ namespace Spv.Generator return result; } - public Instruction MemberDecorate(Instruction structureType, LiteralInteger member, Decoration decoration, params Operand[] parameters) + public Instruction MemberDecorate(Instruction structureType, LiteralInteger member, Decoration decoration, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpMemberDecorate); @@ -262,7 +262,7 @@ namespace Spv.Generator return result; } - public Instruction GroupMemberDecorate(Instruction decorationGroup, params Operand[] targets) + public Instruction GroupMemberDecorate(Instruction decorationGroup, params IOperand[] targets) { Instruction result = NewInstruction(Op.OpGroupMemberDecorate); @@ -273,7 +273,7 @@ namespace Spv.Generator return result; } - public Instruction DecorateId(Instruction target, Decoration decoration, params Operand[] parameters) + public Instruction DecorateId(Instruction target, Decoration decoration, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpDecorateId); @@ -285,7 +285,7 @@ namespace Spv.Generator return result; } - public Instruction DecorateString(Instruction target, Decoration decoration, params Operand[] parameters) + public Instruction DecorateString(Instruction target, Decoration decoration, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpDecorateString); @@ -297,7 +297,7 @@ namespace Spv.Generator return result; } - public Instruction DecorateStringGOOGLE(Instruction target, Decoration decoration, params Operand[] parameters) + public Instruction DecorateStringGOOGLE(Instruction target, Decoration decoration, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpDecorateStringGOOGLE); @@ -309,7 +309,7 @@ namespace Spv.Generator return result; } - public Instruction MemberDecorateString(Instruction structType, LiteralInteger member, Decoration decoration, params Operand[] parameters) + public Instruction MemberDecorateString(Instruction structType, LiteralInteger member, Decoration decoration, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpMemberDecorateString); @@ -322,7 +322,7 @@ namespace Spv.Generator return result; } - public Instruction MemberDecorateStringGOOGLE(Instruction structType, LiteralInteger member, Decoration decoration, params Operand[] parameters) + public Instruction MemberDecorateStringGOOGLE(Instruction structType, LiteralInteger member, Decoration decoration, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpMemberDecorateStringGOOGLE); @@ -2815,7 +2815,7 @@ namespace Spv.Generator return result; } - public Instruction Switch(Instruction selector, Instruction defaultObj, params Operand[] target) + public Instruction Switch(Instruction selector, Instruction defaultObj, params IOperand[] target) { Instruction result = NewInstruction(Op.OpSwitch); diff --git a/src/Spv.Generator/Autogenerated/GlslStd450Grammar.cs b/src/Spv.Generator/Autogenerated/GlslStd450Grammar.cs index 4722d2e49..fa01e94c0 100644 --- a/src/Spv.Generator/Autogenerated/GlslStd450Grammar.cs +++ b/src/Spv.Generator/Autogenerated/GlslStd450Grammar.cs @@ -26,8 +26,6 @@ // IN THE MATERIALS. #endregion -using static Spv.Specification; - namespace Spv.Generator { public partial class Module @@ -36,406 +34,406 @@ namespace Spv.Generator { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 1, x); } - + public Instruction GlslRoundEven(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 2, x); } - + public Instruction GlslTrunc(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 3, x); } - + public Instruction GlslFAbs(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 4, x); } - + public Instruction GlslSAbs(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 5, x); } - + public Instruction GlslFSign(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 6, x); } - + public Instruction GlslSSign(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 7, x); } - + public Instruction GlslFloor(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 8, x); } - + public Instruction GlslCeil(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 9, x); } - + public Instruction GlslFract(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 10, x); } - + public Instruction GlslRadians(Instruction resultType, Instruction degrees) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 11, degrees); } - + public Instruction GlslDegrees(Instruction resultType, Instruction radians) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 12, radians); } - + public Instruction GlslSin(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 13, x); } - + public Instruction GlslCos(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 14, x); } - + public Instruction GlslTan(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 15, x); } - + public Instruction GlslAsin(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 16, x); } - + public Instruction GlslAcos(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 17, x); } - + public Instruction GlslAtan(Instruction resultType, Instruction y_over_x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 18, y_over_x); } - + public Instruction GlslSinh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 19, x); } - + public Instruction GlslCosh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 20, x); } - + public Instruction GlslTanh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 21, x); } - + public Instruction GlslAsinh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 22, x); } - + public Instruction GlslAcosh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 23, x); } - + public Instruction GlslAtanh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 24, x); } - + public Instruction GlslAtan2(Instruction resultType, Instruction y, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 25, y, x); } - + public Instruction GlslPow(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 26, x, y); } - + public Instruction GlslExp(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 27, x); } - + public Instruction GlslLog(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 28, x); } - + public Instruction GlslExp2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 29, x); } - + public Instruction GlslLog2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 30, x); } - + public Instruction GlslSqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 31, x); } - + public Instruction GlslInverseSqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 32, x); } - + public Instruction GlslDeterminant(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 33, x); } - + public Instruction GlslMatrixInverse(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 34, x); } - + public Instruction GlslModf(Instruction resultType, Instruction x, Instruction i) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 35, x, i); } - + public Instruction GlslModfStruct(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 36, x); } - + public Instruction GlslFMin(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 37, x, y); } - + public Instruction GlslUMin(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 38, x, y); } - + public Instruction GlslSMin(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 39, x, y); } - + public Instruction GlslFMax(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 40, x, y); } - + public Instruction GlslUMax(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 41, x, y); } - + public Instruction GlslSMax(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 42, x, y); } - + public Instruction GlslFClamp(Instruction resultType, Instruction x, Instruction minVal, Instruction maxVal) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 43, x, minVal, maxVal); } - + public Instruction GlslUClamp(Instruction resultType, Instruction x, Instruction minVal, Instruction maxVal) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 44, x, minVal, maxVal); } - + public Instruction GlslSClamp(Instruction resultType, Instruction x, Instruction minVal, Instruction maxVal) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 45, x, minVal, maxVal); } - + public Instruction GlslFMix(Instruction resultType, Instruction x, Instruction y, Instruction a) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 46, x, y, a); } - + public Instruction GlslIMix(Instruction resultType, Instruction x, Instruction y, Instruction a) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 47, x, y, a); } - + public Instruction GlslStep(Instruction resultType, Instruction edge, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 48, edge, x); } - + public Instruction GlslSmoothStep(Instruction resultType, Instruction edge0, Instruction edge1, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 49, edge0, edge1, x); } - + public Instruction GlslFma(Instruction resultType, Instruction a, Instruction b, Instruction c) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 50, a, b, c); } - + public Instruction GlslFrexp(Instruction resultType, Instruction x, Instruction exp) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 51, x, exp); } - + public Instruction GlslFrexpStruct(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 52, x); } - + public Instruction GlslLdexp(Instruction resultType, Instruction x, Instruction exp) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 53, x, exp); } - + public Instruction GlslPackSnorm4x8(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 54, v); } - + public Instruction GlslPackUnorm4x8(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 55, v); } - + public Instruction GlslPackSnorm2x16(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 56, v); } - + public Instruction GlslPackUnorm2x16(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 57, v); } - + public Instruction GlslPackHalf2x16(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 58, v); } - + public Instruction GlslPackDouble2x32(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 59, v); } - + public Instruction GlslUnpackSnorm2x16(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 60, p); } - + public Instruction GlslUnpackUnorm2x16(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 61, p); } - + public Instruction GlslUnpackHalf2x16(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 62, v); } - + public Instruction GlslUnpackSnorm4x8(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 63, p); } - + public Instruction GlslUnpackUnorm4x8(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 64, p); } - + public Instruction GlslUnpackDouble2x32(Instruction resultType, Instruction v) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 65, v); } - + public Instruction GlslLength(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 66, x); } - + public Instruction GlslDistance(Instruction resultType, Instruction p0, Instruction p1) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 67, p0, p1); } - + public Instruction GlslCross(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 68, x, y); } - + public Instruction GlslNormalize(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 69, x); } - + public Instruction GlslFaceForward(Instruction resultType, Instruction n, Instruction i, Instruction nref) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 70, n, i, nref); } - + public Instruction GlslReflect(Instruction resultType, Instruction i, Instruction n) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 71, i, n); } - + public Instruction GlslRefract(Instruction resultType, Instruction i, Instruction n, Instruction eta) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 72, i, n, eta); } - + public Instruction GlslFindILsb(Instruction resultType, Instruction value) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 73, value); } - + public Instruction GlslFindSMsb(Instruction resultType, Instruction value) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 74, value); } - + public Instruction GlslFindUMsb(Instruction resultType, Instruction value) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 75, value); } - + public Instruction GlslInterpolateAtCentroid(Instruction resultType, Instruction interpolant) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 76, interpolant); } - + public Instruction GlslInterpolateAtSample(Instruction resultType, Instruction interpolant, Instruction sample) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 77, interpolant, sample); } - + public Instruction GlslInterpolateAtOffset(Instruction resultType, Instruction interpolant, Instruction offset) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 78, interpolant, offset); } - + public Instruction GlslNMin(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 79, x, y); } - + public Instruction GlslNMax(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 80, x, y); } - + public Instruction GlslNClamp(Instruction resultType, Instruction x, Instruction minVal, Instruction maxVal) { return ExtInst(resultType, AddExtInstImport("GLSL.std.450"), 81, x, minVal, maxVal); } - + } } diff --git a/src/Spv.Generator/Autogenerated/OpenClGrammar.cs b/src/Spv.Generator/Autogenerated/OpenClGrammar.cs index ac990fbc8..03eb5ccbd 100644 --- a/src/Spv.Generator/Autogenerated/OpenClGrammar.cs +++ b/src/Spv.Generator/Autogenerated/OpenClGrammar.cs @@ -36,806 +36,806 @@ namespace Spv.Generator { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 0, x); } - + public Instruction OpenClAcosh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 1, x); } - + public Instruction OpenClAcospi(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 2, x); } - + public Instruction OpenClAsin(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 3, x); } - + public Instruction OpenClAsinh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 4, x); } - + public Instruction OpenClAsinpi(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 5, x); } - + public Instruction OpenClAtan(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 6, x); } - + public Instruction OpenClAtan2(Instruction resultType, Instruction y, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 7, y, x); } - + public Instruction OpenClAtanh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 8, x); } - + public Instruction OpenClAtanpi(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 9, x); } - + public Instruction OpenClAtan2pi(Instruction resultType, Instruction y, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 10, y, x); } - + public Instruction OpenClCbrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 11, x); } - + public Instruction OpenClCeil(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 12, x); } - + public Instruction OpenClCopysign(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 13, x, y); } - + public Instruction OpenClCos(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 14, x); } - + public Instruction OpenClCosh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 15, x); } - + public Instruction OpenClCospi(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 16, x); } - + public Instruction OpenClErfc(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 17, x); } - + public Instruction OpenClErf(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 18, x); } - + public Instruction OpenClExp(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 19, x); } - + public Instruction OpenClExp2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 20, x); } - + public Instruction OpenClExp10(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 21, x); } - + public Instruction OpenClExpm1(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 22, x); } - + public Instruction OpenClFabs(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 23, x); } - + public Instruction OpenClFdim(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 24, x, y); } - + public Instruction OpenClFloor(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 25, x); } - + public Instruction OpenClFma(Instruction resultType, Instruction a, Instruction b, Instruction c) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 26, a, b, c); } - + public Instruction OpenClFmax(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 27, x, y); } - + public Instruction OpenClFmin(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 28, x, y); } - + public Instruction OpenClFmod(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 29, x, y); } - + public Instruction OpenClFract(Instruction resultType, Instruction x, Instruction ptr) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 30, x, ptr); } - + public Instruction OpenClFrexp(Instruction resultType, Instruction x, Instruction exp) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 31, x, exp); } - + public Instruction OpenClHypot(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 32, x, y); } - + public Instruction OpenClIlogb(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 33, x); } - + public Instruction OpenClLdexp(Instruction resultType, Instruction x, Instruction k) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 34, x, k); } - + public Instruction OpenClLgamma(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 35, x); } - + public Instruction OpenClLgamma_r(Instruction resultType, Instruction x, Instruction signp) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 36, x, signp); } - + public Instruction OpenClLog(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 37, x); } - + public Instruction OpenClLog2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 38, x); } - + public Instruction OpenClLog10(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 39, x); } - + public Instruction OpenClLog1p(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 40, x); } - + public Instruction OpenClLogb(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 41, x); } - + public Instruction OpenClMad(Instruction resultType, Instruction a, Instruction b, Instruction c) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 42, a, b, c); } - + public Instruction OpenClMaxmag(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 43, x, y); } - + public Instruction OpenClMinmag(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 44, x, y); } - + public Instruction OpenClModf(Instruction resultType, Instruction x, Instruction iptr) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 45, x, iptr); } - + public Instruction OpenClNan(Instruction resultType, Instruction nancode) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 46, nancode); } - + public Instruction OpenClNextafter(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 47, x, y); } - + public Instruction OpenClPow(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 48, x, y); } - + public Instruction OpenClPown(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 49, x, y); } - + public Instruction OpenClPowr(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 50, x, y); } - + public Instruction OpenClRemainder(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 51, x, y); } - + public Instruction OpenClRemquo(Instruction resultType, Instruction x, Instruction y, Instruction quo) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 52, x, y, quo); } - + public Instruction OpenClRint(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 53, x); } - + public Instruction OpenClRootn(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 54, x, y); } - + public Instruction OpenClRound(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 55, x); } - + public Instruction OpenClRsqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 56, x); } - + public Instruction OpenClSin(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 57, x); } - + public Instruction OpenClSincos(Instruction resultType, Instruction x, Instruction cosval) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 58, x, cosval); } - + public Instruction OpenClSinh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 59, x); } - + public Instruction OpenClSinpi(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 60, x); } - + public Instruction OpenClSqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 61, x); } - + public Instruction OpenClTan(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 62, x); } - + public Instruction OpenClTanh(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 63, x); } - + public Instruction OpenClTanpi(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 64, x); } - + public Instruction OpenClTgamma(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 65, x); } - + public Instruction OpenClTrunc(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 66, x); } - + public Instruction OpenClHalf_cos(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 67, x); } - + public Instruction OpenClHalf_divide(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 68, x, y); } - + public Instruction OpenClHalf_exp(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 69, x); } - + public Instruction OpenClHalf_exp2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 70, x); } - + public Instruction OpenClHalf_exp10(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 71, x); } - + public Instruction OpenClHalf_log(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 72, x); } - + public Instruction OpenClHalf_log2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 73, x); } - + public Instruction OpenClHalf_log10(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 74, x); } - + public Instruction OpenClHalf_powr(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 75, x, y); } - + public Instruction OpenClHalf_recip(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 76, x); } - + public Instruction OpenClHalf_rsqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 77, x); } - + public Instruction OpenClHalf_sin(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 78, x); } - + public Instruction OpenClHalf_sqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 79, x); } - + public Instruction OpenClHalf_tan(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 80, x); } - + public Instruction OpenClNative_cos(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 81, x); } - + public Instruction OpenClNative_divide(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 82, x, y); } - + public Instruction OpenClNative_exp(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 83, x); } - + public Instruction OpenClNative_exp2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 84, x); } - + public Instruction OpenClNative_exp10(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 85, x); } - + public Instruction OpenClNative_log(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 86, x); } - + public Instruction OpenClNative_log2(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 87, x); } - + public Instruction OpenClNative_log10(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 88, x); } - + public Instruction OpenClNative_powr(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 89, x, y); } - + public Instruction OpenClNative_recip(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 90, x); } - + public Instruction OpenClNative_rsqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 91, x); } - + public Instruction OpenClNative_sin(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 92, x); } - + public Instruction OpenClNative_sqrt(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 93, x); } - + public Instruction OpenClNative_tan(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 94, x); } - + public Instruction OpenClS_abs(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 141, x); } - + public Instruction OpenClS_abs_diff(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 142, x, y); } - + public Instruction OpenClS_add_sat(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 143, x, y); } - + public Instruction OpenClU_add_sat(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 144, x, y); } - + public Instruction OpenClS_hadd(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 145, x, y); } - + public Instruction OpenClU_hadd(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 146, x, y); } - + public Instruction OpenClS_rhadd(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 147, x, y); } - + public Instruction OpenClU_rhadd(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 148, x, y); } - + public Instruction OpenClS_clamp(Instruction resultType, Instruction x, Instruction minval, Instruction maxval) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 149, x, minval, maxval); } - + public Instruction OpenClU_clamp(Instruction resultType, Instruction x, Instruction minval, Instruction maxval) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 150, x, minval, maxval); } - + public Instruction OpenClClz(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 151, x); } - + public Instruction OpenClCtz(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 152, x); } - + public Instruction OpenClS_mad_hi(Instruction resultType, Instruction a, Instruction b, Instruction c) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 153, a, b, c); } - + public Instruction OpenClU_mad_sat(Instruction resultType, Instruction x, Instruction y, Instruction z) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 154, x, y, z); } - + public Instruction OpenClS_mad_sat(Instruction resultType, Instruction x, Instruction y, Instruction z) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 155, x, y, z); } - + public Instruction OpenClS_max(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 156, x, y); } - + public Instruction OpenClU_max(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 157, x, y); } - + public Instruction OpenClS_min(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 158, x, y); } - + public Instruction OpenClU_min(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 159, x, y); } - + public Instruction OpenClS_mul_hi(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 160, x, y); } - + public Instruction OpenClRotate(Instruction resultType, Instruction v, Instruction i) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 161, v, i); } - + public Instruction OpenClS_sub_sat(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 162, x, y); } - + public Instruction OpenClU_sub_sat(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 163, x, y); } - + public Instruction OpenClU_upsample(Instruction resultType, Instruction hi, Instruction lo) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 164, hi, lo); } - + public Instruction OpenClS_upsample(Instruction resultType, Instruction hi, Instruction lo) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 165, hi, lo); } - + public Instruction OpenClPopcount(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 166, x); } - + public Instruction OpenClS_mad24(Instruction resultType, Instruction x, Instruction y, Instruction z) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 167, x, y, z); } - + public Instruction OpenClU_mad24(Instruction resultType, Instruction x, Instruction y, Instruction z) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 168, x, y, z); } - + public Instruction OpenClS_mul24(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 169, x, y); } - + public Instruction OpenClU_mul24(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 170, x, y); } - + public Instruction OpenClU_abs(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 201, x); } - + public Instruction OpenClU_abs_diff(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 202, x, y); } - + public Instruction OpenClU_mul_hi(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 203, x, y); } - + public Instruction OpenClU_mad_hi(Instruction resultType, Instruction a, Instruction b, Instruction c) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 204, a, b, c); } - + public Instruction OpenClFclamp(Instruction resultType, Instruction x, Instruction minval, Instruction maxval) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 95, x, minval, maxval); } - + public Instruction OpenClDegrees(Instruction resultType, Instruction radians) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 96, radians); } - + public Instruction OpenClFmax_common(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 97, x, y); } - + public Instruction OpenClFmin_common(Instruction resultType, Instruction x, Instruction y) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 98, x, y); } - + public Instruction OpenClMix(Instruction resultType, Instruction x, Instruction y, Instruction a) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 99, x, y, a); } - + public Instruction OpenClRadians(Instruction resultType, Instruction degrees) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 100, degrees); } - + public Instruction OpenClStep(Instruction resultType, Instruction edge, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 101, edge, x); } - + public Instruction OpenClSmoothstep(Instruction resultType, Instruction edge0, Instruction edge1, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 102, edge0, edge1, x); } - + public Instruction OpenClSign(Instruction resultType, Instruction x) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 103, x); } - + public Instruction OpenClCross(Instruction resultType, Instruction p0, Instruction p1) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 104, p0, p1); } - + public Instruction OpenClDistance(Instruction resultType, Instruction p0, Instruction p1) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 105, p0, p1); } - + public Instruction OpenClLength(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 106, p); } - + public Instruction OpenClNormalize(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 107, p); } - + public Instruction OpenClFast_distance(Instruction resultType, Instruction p0, Instruction p1) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 108, p0, p1); } - + public Instruction OpenClFast_length(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 109, p); } - + public Instruction OpenClFast_normalize(Instruction resultType, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 110, p); } - + public Instruction OpenClBitselect(Instruction resultType, Instruction a, Instruction b, Instruction c) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 186, a, b, c); } - + public Instruction OpenClSelect(Instruction resultType, Instruction a, Instruction b, Instruction c) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 187, a, b, c); } - + public Instruction OpenClVloadn(Instruction resultType, Instruction offset, Instruction p, LiteralInteger n) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 171, offset, p, n); } - + public Instruction OpenClVstoren(Instruction resultType, Instruction data, Instruction offset, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 172, data, offset, p); } - + public Instruction OpenClVload_half(Instruction resultType, Instruction offset, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 173, offset, p); } - + public Instruction OpenClVload_halfn(Instruction resultType, Instruction offset, Instruction p, LiteralInteger n) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 174, offset, p, n); } - + public Instruction OpenClVstore_half(Instruction resultType, Instruction data, Instruction offset, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 175, data, offset, p); } - + public Instruction OpenClVstore_half_r(Instruction resultType, Instruction data, Instruction offset, Instruction p, FPRoundingMode mode) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 176, data, offset, p, LiteralInteger.CreateForEnum(mode)); } - + public Instruction OpenClVstore_halfn(Instruction resultType, Instruction data, Instruction offset, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 177, data, offset, p); } - + public Instruction OpenClVstore_halfn_r(Instruction resultType, Instruction data, Instruction offset, Instruction p, FPRoundingMode mode) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 178, data, offset, p, LiteralInteger.CreateForEnum(mode)); } - + public Instruction OpenClVloada_halfn(Instruction resultType, Instruction offset, Instruction p, LiteralInteger n) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 179, offset, p, n); } - + public Instruction OpenClVstorea_halfn(Instruction resultType, Instruction data, Instruction offset, Instruction p) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 180, data, offset, p); } - + public Instruction OpenClVstorea_halfn_r(Instruction resultType, Instruction data, Instruction offset, Instruction p, FPRoundingMode mode) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 181, data, offset, p, LiteralInteger.CreateForEnum(mode)); } - + public Instruction OpenClShuffle(Instruction resultType, Instruction x, Instruction shufflemask) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 182, x, shufflemask); } - + public Instruction OpenClShuffle2(Instruction resultType, Instruction x, Instruction y, Instruction shufflemask) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 183, x, y, shufflemask); } - + public Instruction OpenClPrefetch(Instruction resultType, Instruction ptr, Instruction numelements) { return ExtInst(resultType, AddExtInstImport("OpenCL.std"), 185, ptr, numelements); } - + } } diff --git a/src/Spv.Generator/ConstantKey.cs b/src/Spv.Generator/ConstantKey.cs index e7758b408..9fd255988 100644 --- a/src/Spv.Generator/ConstantKey.cs +++ b/src/Spv.Generator/ConstantKey.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; namespace Spv.Generator { - internal struct ConstantKey : IEquatable<ConstantKey> + internal readonly struct ConstantKey : IEquatable<ConstantKey> { private readonly Instruction _constant; @@ -24,7 +24,7 @@ namespace Spv.Generator public override bool Equals([NotNullWhen(true)] object obj) { - return obj is ConstantKey && Equals((ConstantKey)obj); + return obj is ConstantKey key && Equals(key); } } } diff --git a/src/Spv.Generator/DeterministicStringKey.cs b/src/Spv.Generator/DeterministicStringKey.cs index cab4dbcc2..398aa6c8c 100644 --- a/src/Spv.Generator/DeterministicStringKey.cs +++ b/src/Spv.Generator/DeterministicStringKey.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; namespace Spv.Generator { @@ -19,12 +18,12 @@ namespace Spv.Generator public bool Equals(DeterministicStringKey other) { - return _value == other._value; + return _value == other?._value; } - public override bool Equals([NotNullWhen(true)] object obj) + public override bool Equals(object obj) { - return obj is DeterministicStringKey && Equals((DeterministicStringKey)obj); + return obj is DeterministicStringKey key && Equals(key); } } } diff --git a/src/Spv.Generator/GeneratorPool.cs b/src/Spv.Generator/GeneratorPool.cs index f6c92918e..e4266eee9 100644 --- a/src/Spv.Generator/GeneratorPool.cs +++ b/src/Spv.Generator/GeneratorPool.cs @@ -4,17 +4,15 @@ namespace Spv.Generator { public class GeneratorPool<T> where T : class, new() { - private List<T[]> _pool; + private readonly List<T[]> _pool; private int _chunkIndex = -1; private int _poolIndex = -1; - private int _initialSize; - private int _poolSizeIncrement; + private readonly int _poolSizeIncrement; - public GeneratorPool(): this(1000, 200) { } + public GeneratorPool() : this(1000, 200) { } public GeneratorPool(int chunkSizeLimit, int poolSizeIncrement) { - _initialSize = chunkSizeLimit; _poolSizeIncrement = poolSizeIncrement; _pool = new(chunkSizeLimit * 2); diff --git a/src/Spv.Generator/Operand.cs b/src/Spv.Generator/IOperand.cs similarity index 78% rename from src/Spv.Generator/Operand.cs rename to src/Spv.Generator/IOperand.cs index eaa2e13e0..bda2ed694 100644 --- a/src/Spv.Generator/Operand.cs +++ b/src/Spv.Generator/IOperand.cs @@ -3,7 +3,7 @@ using System.IO; namespace Spv.Generator { - public interface Operand : IEquatable<Operand> + public interface IOperand : IEquatable<IOperand> { OperandType Type { get; } diff --git a/src/Spv.Generator/Instruction.cs b/src/Spv.Generator/Instruction.cs index 8ecfe6839..1c92d4991 100644 --- a/src/Spv.Generator/Instruction.cs +++ b/src/Spv.Generator/Instruction.cs @@ -5,7 +5,7 @@ using System.IO; namespace Spv.Generator { - public sealed class Instruction : Operand, IEquatable<Instruction> + public sealed class Instruction : IOperand, IEquatable<Instruction> { public const uint InvalidId = uint.MaxValue; @@ -47,7 +47,7 @@ namespace Spv.Generator result += _resultType.WordCount; } - Span<Operand> operands = _operands.AsSpan(); + Span<IOperand> operands = _operands.AsSpan(); for (int i = 0; i < operands.Length; i++) { result += operands[i].WordCount; @@ -58,15 +58,15 @@ namespace Spv.Generator public ushort WordCount => 1; - public void AddOperand(Operand value) + public void AddOperand(IOperand value) { Debug.Assert(value != null); _operands.Add(value); } - public void AddOperand(Operand[] value) + public void AddOperand(IOperand[] value) { - foreach (Operand instruction in value) + foreach (IOperand instruction in value) { AddOperand(instruction); } @@ -82,7 +82,7 @@ namespace Spv.Generator public void AddOperand(LiteralInteger value) { - AddOperand((Operand)value); + AddOperand((IOperand)value); } public void AddOperand(Instruction[] value) @@ -95,7 +95,7 @@ namespace Spv.Generator public void AddOperand(Instruction value) { - AddOperand((Operand)value); + AddOperand((IOperand)value); } public void AddOperand(string value) @@ -103,7 +103,7 @@ namespace Spv.Generator AddOperand(new LiteralString(value)); } - public void AddOperand<T>(T value) where T: Enum + public void AddOperand<T>(T value) where T : Enum { AddOperand(LiteralInteger.CreateForEnum(value)); } @@ -121,7 +121,7 @@ namespace Spv.Generator writer.Write(Id); } - Span<Operand> operands = _operands.AsSpan(); + Span<IOperand> operands = _operands.AsSpan(); for (int i = 0; i < operands.Length; i++) { operands[i].WriteOperand(writer); @@ -186,8 +186,8 @@ namespace Spv.Generator public bool EqualsContent(Instruction cmpObj) { - Span<Operand> thisOperands = _operands.AsSpan(); - Span<Operand> cmpOperands = cmpObj._operands.AsSpan(); + Span<IOperand> thisOperands = _operands.AsSpan(); + Span<IOperand> cmpOperands = cmpObj._operands.AsSpan(); if (thisOperands.Length != cmpOperands.Length) { @@ -212,7 +212,7 @@ namespace Spv.Generator public int GetHashCodeContent() { - return DeterministicHashCode.Combine<Operand>(_operands.AsSpan()); + return DeterministicHashCode.Combine<IOperand>(_operands.AsSpan()); } public int GetHashCodeResultType() @@ -222,14 +222,14 @@ namespace Spv.Generator public override int GetHashCode() { - return DeterministicHashCode.Combine(Opcode, Id, _resultType, DeterministicHashCode.Combine<Operand>(_operands.AsSpan())); + return DeterministicHashCode.Combine(Opcode, Id, _resultType, DeterministicHashCode.Combine<IOperand>(_operands.AsSpan())); } - public bool Equals(Operand obj) + public bool Equals(IOperand obj) { return obj is Instruction instruction && Equals(instruction); } - + private static readonly Dictionary<Specification.Op, string[]> _operandLabels = new() { { Specification.Op.OpConstant, new [] { "Value" } }, diff --git a/src/Spv.Generator/InstructionOperands.cs b/src/Spv.Generator/InstructionOperands.cs index c48b004fa..dfabe3071 100644 --- a/src/Spv.Generator/InstructionOperands.cs +++ b/src/Spv.Generator/InstructionOperands.cs @@ -10,14 +10,14 @@ namespace Spv.Generator private const int InternalCount = 5; public int Count; - public Operand Operand1; - public Operand Operand2; - public Operand Operand3; - public Operand Operand4; - public Operand Operand5; - public Operand[] Overflow; + public IOperand Operand1; + public IOperand Operand2; + public IOperand Operand3; + public IOperand Operand4; + public IOperand Operand5; + public IOperand[] Overflow; - public Span<Operand> AsSpan() + public Span<IOperand> AsSpan() { if (Count > InternalCount) { @@ -29,7 +29,7 @@ namespace Spv.Generator } } - public void Add(Operand operand) + public void Add(IOperand operand) { if (Count < InternalCount) { @@ -40,7 +40,7 @@ namespace Spv.Generator { if (Overflow == null) { - Overflow = new Operand[InternalCount * 2]; + Overflow = new IOperand[InternalCount * 2]; MemoryMarshal.CreateSpan(ref this.Operand1, InternalCount).CopyTo(Overflow.AsSpan()); } else if (Count == Overflow.Length) @@ -52,16 +52,16 @@ namespace Spv.Generator } } - private IEnumerable<Operand> AllOperands => new[] { Operand1, Operand2, Operand3, Operand4, Operand5 } - .Concat(Overflow ?? Array.Empty<Operand>()) + private readonly IEnumerable<IOperand> AllOperands => new[] { Operand1, Operand2, Operand3, Operand4, Operand5 } + .Concat(Overflow ?? Array.Empty<IOperand>()) .Take(Count); - public override string ToString() + public readonly override string ToString() { return $"({string.Join(", ", AllOperands)})"; } - public string ToString(string[] labels) + public readonly string ToString(string[] labels) { var labeledParams = AllOperands.Zip(labels, (op, label) => $"{label}: {op}"); var unlabeledParams = AllOperands.Skip(labels.Length).Select(op => op.ToString()); diff --git a/src/Spv.Generator/LiteralInteger.cs b/src/Spv.Generator/LiteralInteger.cs index 22cb5484b..4d5f801e3 100644 --- a/src/Spv.Generator/LiteralInteger.cs +++ b/src/Spv.Generator/LiteralInteger.cs @@ -3,7 +3,7 @@ using System.IO; namespace Spv.Generator { - public class LiteralInteger : Operand, IEquatable<LiteralInteger> + public class LiteralInteger : IOperand, IEquatable<LiteralInteger> { [ThreadStatic] private static GeneratorPool<LiteralInteger> _pool; @@ -95,7 +95,7 @@ namespace Spv.Generator return DeterministicHashCode.Combine(Type, _data); } - public bool Equals(Operand obj) + public bool Equals(IOperand obj) { return obj is LiteralInteger literalInteger && Equals(literalInteger); } diff --git a/src/Spv.Generator/LiteralString.cs b/src/Spv.Generator/LiteralString.cs index 741d922bb..1ced040a0 100644 --- a/src/Spv.Generator/LiteralString.cs +++ b/src/Spv.Generator/LiteralString.cs @@ -4,7 +4,7 @@ using System.Text; namespace Spv.Generator { - public class LiteralString : Operand, IEquatable<LiteralString> + public class LiteralString : IOperand, IEquatable<LiteralString> { public OperandType Type => OperandType.String; @@ -44,7 +44,7 @@ namespace Spv.Generator return DeterministicHashCode.Combine(Type, DeterministicHashCode.GetHashCode(_value)); } - public bool Equals(Operand obj) + public bool Equals(IOperand obj) { return obj is LiteralString literalString && Equals(literalString); } diff --git a/src/Spv.Generator/Module.cs b/src/Spv.Generator/Module.cs index 34ad60366..5e1a91232 100644 --- a/src/Spv.Generator/Module.cs +++ b/src/Spv.Generator/Module.cs @@ -15,30 +15,30 @@ namespace Spv.Generator private uint _bound; // Follow spec order here while keeping it as simple as possible. - private List<Capability> _capabilities; - private List<string> _extensions; - private Dictionary<DeterministicStringKey, Instruction> _extInstImports; + private readonly List<Capability> _capabilities; + private readonly List<string> _extensions; + private readonly Dictionary<DeterministicStringKey, Instruction> _extInstImports; private AddressingModel _addressingModel; private MemoryModel _memoryModel; - private List<Instruction> _entrypoints; - private List<Instruction> _executionModes; - private List<Instruction> _debug; - private List<Instruction> _annotations; + private readonly List<Instruction> _entrypoints; + private readonly List<Instruction> _executionModes; + private readonly List<Instruction> _debug; + private readonly List<Instruction> _annotations; // In the declaration block. - private Dictionary<TypeDeclarationKey, Instruction> _typeDeclarations; + private readonly Dictionary<TypeDeclarationKey, Instruction> _typeDeclarations; // In the declaration block. - private List<Instruction> _globals; + private readonly List<Instruction> _globals; // In the declaration block. - private Dictionary<ConstantKey, Instruction> _constants; + private readonly Dictionary<ConstantKey, Instruction> _constants; // In the declaration block, for function that aren't defined in the module. - private List<Instruction> _functionsDeclarations; + private readonly List<Instruction> _functionsDeclarations; - private List<Instruction> _functionsDefinitions; + private readonly List<Instruction> _functionsDefinitions; - private GeneratorPool<Instruction> _instPool; - private GeneratorPool<LiteralInteger> _integerPool; + private readonly GeneratorPool<Instruction> _instPool; + private readonly GeneratorPool<LiteralInteger> _integerPool; public Module(uint version, GeneratorPool<Instruction> instPool = null, GeneratorPool<LiteralInteger> integerPool = null) { @@ -143,7 +143,7 @@ namespace Spv.Generator _entrypoints.Add(entryPoint); } - public void AddExecutionMode(Instruction function, ExecutionMode mode, params Operand[] parameters) + public void AddExecutionMode(Instruction function, ExecutionMode mode, params IOperand[] parameters) { Debug.Assert(function.Opcode == Op.OpFunction); @@ -225,7 +225,7 @@ namespace Spv.Generator _constants.Add(key, constant); } - public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params Operand[] parameters) + public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params IOperand[] parameters) { Instruction result = NewInstruction(Op.OpExtInst, GetNewId(), resultType); @@ -262,104 +262,103 @@ namespace Spv.Generator // Estimate the size needed for the generated code, to avoid expanding the MemoryStream. int sizeEstimate = 1024 + _functionsDefinitions.Count * 32; - using (MemoryStream stream = new MemoryStream(sizeEstimate)) + using MemoryStream stream = new(sizeEstimate); + + BinaryWriter writer = new(stream, System.Text.Encoding.ASCII); + + // Header + writer.Write(MagicNumber); + writer.Write(_version); + writer.Write(GeneratorId); + writer.Write(_bound); + writer.Write(0u); + + // 1. + foreach (Capability capability in _capabilities) { - BinaryWriter writer = new BinaryWriter(stream, System.Text.Encoding.ASCII); + Instruction capabilityInstruction = NewInstruction(Op.OpCapability); - // Header - writer.Write(MagicNumber); - writer.Write(_version); - writer.Write(GeneratorId); - writer.Write(_bound); - writer.Write(0u); - - // 1. - foreach (Capability capability in _capabilities) - { - Instruction capabilityInstruction = NewInstruction(Op.OpCapability); - - capabilityInstruction.AddOperand(capability); - capabilityInstruction.Write(writer); - } - - // 2. - foreach (string extension in _extensions) - { - Instruction extensionInstruction = NewInstruction(Op.OpExtension); - - extensionInstruction.AddOperand(extension); - extensionInstruction.Write(writer); - } - - // 3. - foreach (Instruction extInstImport in _extInstImports.Values) - { - extInstImport.Write(writer); - } - - // 4. - Instruction memoryModelInstruction = NewInstruction(Op.OpMemoryModel); - memoryModelInstruction.AddOperand(_addressingModel); - memoryModelInstruction.AddOperand(_memoryModel); - memoryModelInstruction.Write(writer); - - // 5. - foreach (Instruction entrypoint in _entrypoints) - { - entrypoint.Write(writer); - } - - // 6. - foreach (Instruction executionMode in _executionModes) - { - executionMode.Write(writer); - } - - // 7. - // TODO: Order debug information correctly. - foreach (Instruction debug in _debug) - { - debug.Write(writer); - } - - // 8. - foreach (Instruction annotation in _annotations) - { - annotation.Write(writer); - } - - // Ensure that everything is in the right order in the declarations section. - List<Instruction> declarations = new List<Instruction>(); - declarations.AddRange(_typeDeclarations.Values); - declarations.AddRange(_globals); - declarations.AddRange(_constants.Values); - declarations.Sort((Instruction x, Instruction y) => x.Id.CompareTo(y.Id)); - - // 9. - foreach (Instruction declaration in declarations) - { - declaration.Write(writer); - } - - // 10. - foreach (Instruction functionDeclaration in _functionsDeclarations) - { - functionDeclaration.Write(writer); - } - - // 11. - foreach (Instruction functionDefinition in _functionsDefinitions) - { - functionDefinition.Write(writer); - } - - _instPool.Clear(); - _integerPool.Clear(); - - LiteralInteger.UnregisterPool(); - - return stream.ToArray(); + capabilityInstruction.AddOperand(capability); + capabilityInstruction.Write(writer); } + + // 2. + foreach (string extension in _extensions) + { + Instruction extensionInstruction = NewInstruction(Op.OpExtension); + + extensionInstruction.AddOperand(extension); + extensionInstruction.Write(writer); + } + + // 3. + foreach (Instruction extInstImport in _extInstImports.Values) + { + extInstImport.Write(writer); + } + + // 4. + Instruction memoryModelInstruction = NewInstruction(Op.OpMemoryModel); + memoryModelInstruction.AddOperand(_addressingModel); + memoryModelInstruction.AddOperand(_memoryModel); + memoryModelInstruction.Write(writer); + + // 5. + foreach (Instruction entrypoint in _entrypoints) + { + entrypoint.Write(writer); + } + + // 6. + foreach (Instruction executionMode in _executionModes) + { + executionMode.Write(writer); + } + + // 7. + // TODO: Order debug information correctly. + foreach (Instruction debug in _debug) + { + debug.Write(writer); + } + + // 8. + foreach (Instruction annotation in _annotations) + { + annotation.Write(writer); + } + + // Ensure that everything is in the right order in the declarations section. + List<Instruction> declarations = new(); + declarations.AddRange(_typeDeclarations.Values); + declarations.AddRange(_globals); + declarations.AddRange(_constants.Values); + declarations.Sort((Instruction x, Instruction y) => x.Id.CompareTo(y.Id)); + + // 9. + foreach (Instruction declaration in declarations) + { + declaration.Write(writer); + } + + // 10. + foreach (Instruction functionDeclaration in _functionsDeclarations) + { + functionDeclaration.Write(writer); + } + + // 11. + foreach (Instruction functionDefinition in _functionsDefinitions) + { + functionDefinition.Write(writer); + } + + _instPool.Clear(); + _integerPool.Clear(); + + LiteralInteger.UnregisterPool(); + + return stream.ToArray(); } } } diff --git a/src/Spv.Generator/TypeDeclarationKey.cs b/src/Spv.Generator/TypeDeclarationKey.cs index e4fd5fd57..1f59331f0 100644 --- a/src/Spv.Generator/TypeDeclarationKey.cs +++ b/src/Spv.Generator/TypeDeclarationKey.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; namespace Spv.Generator { - internal struct TypeDeclarationKey : IEquatable<TypeDeclarationKey> + internal readonly struct TypeDeclarationKey : IEquatable<TypeDeclarationKey> { private readonly Instruction _typeDeclaration; @@ -24,7 +24,7 @@ namespace Spv.Generator public override bool Equals([NotNullWhen(true)] object obj) { - return obj is TypeDeclarationKey && Equals((TypeDeclarationKey)obj); + return obj is TypeDeclarationKey key && Equals(key); } } } diff --git a/src/Spv.Generator/spirv.cs b/src/Spv.Generator/spirv.cs index df28438bb..bbcda330b 100644 --- a/src/Spv.Generator/spirv.cs +++ b/src/Spv.Generator/spirv.cs @@ -75,17 +75,17 @@ namespace Spv TaskNV = 5267, MeshNV = 5268, RayGenerationKHR = 5313, - RayGenerationNV = 5313, + RayGenerationNV = RayGenerationKHR, IntersectionKHR = 5314, - IntersectionNV = 5314, + IntersectionNV = IntersectionKHR, AnyHitKHR = 5315, - AnyHitNV = 5315, + AnyHitNV = AnyHitKHR, ClosestHitKHR = 5316, - ClosestHitNV = 5316, + ClosestHitNV = ClosestHitKHR, MissKHR = 5317, - MissNV = 5317, + MissNV = MissKHR, CallableKHR = 5318, - CallableNV = 5318, + CallableNV = CallableKHR, } public enum AddressingModel @@ -94,7 +94,7 @@ namespace Spv Physical32 = 1, Physical64 = 2, PhysicalStorageBuffer64 = 5348, - PhysicalStorageBuffer64EXT = 5348, + PhysicalStorageBuffer64EXT = PhysicalStorageBuffer64, } public enum MemoryModel @@ -103,7 +103,7 @@ namespace Spv GLSL450 = 1, OpenCL = 2, Vulkan = 3, - VulkanKHR = 3, + VulkanKHR = Vulkan, } public enum ExecutionMode @@ -186,19 +186,19 @@ namespace Spv Image = 11, StorageBuffer = 12, CallableDataKHR = 5328, - CallableDataNV = 5328, + CallableDataNV = CallableDataKHR, IncomingCallableDataKHR = 5329, - IncomingCallableDataNV = 5329, + IncomingCallableDataNV = IncomingCallableDataKHR, RayPayloadKHR = 5338, - RayPayloadNV = 5338, + RayPayloadNV = RayPayloadKHR, HitAttributeKHR = 5339, - HitAttributeNV = 5339, + HitAttributeNV = HitAttributeKHR, IncomingRayPayloadKHR = 5342, - IncomingRayPayloadNV = 5342, + IncomingRayPayloadNV = IncomingRayPayloadKHR, ShaderRecordBufferKHR = 5343, - ShaderRecordBufferNV = 5343, + ShaderRecordBufferNV = ShaderRecordBufferKHR, PhysicalStorageBuffer = 5349, - PhysicalStorageBufferEXT = 5349, + PhysicalStorageBufferEXT = PhysicalStorageBuffer, CodeSectionINTEL = 5605, } @@ -330,13 +330,13 @@ namespace Spv Sample = 6, MinLod = 7, MakeTexelAvailable = 8, - MakeTexelAvailableKHR = 8, + MakeTexelAvailableKHR = MakeTexelAvailable, MakeTexelVisible = 9, - MakeTexelVisibleKHR = 9, + MakeTexelVisibleKHR = MakeTexelVisible, NonPrivateTexel = 10, - NonPrivateTexelKHR = 10, + NonPrivateTexelKHR = NonPrivateTexel, VolatileTexel = 11, - VolatileTexelKHR = 11, + VolatileTexelKHR = VolatileTexel, SignExtend = 12, ZeroExtend = 13, } @@ -353,13 +353,13 @@ namespace Spv Sample = 0x00000040, MinLod = 0x00000080, MakeTexelAvailable = 0x00000100, - MakeTexelAvailableKHR = 0x00000100, + MakeTexelAvailableKHR = MakeTexelAvailable, MakeTexelVisible = 0x00000200, - MakeTexelVisibleKHR = 0x00000200, + MakeTexelVisibleKHR = MakeTexelVisible, NonPrivateTexel = 0x00000400, - NonPrivateTexelKHR = 0x00000400, + NonPrivateTexelKHR = NonPrivateTexel, VolatileTexel = 0x00000800, - VolatileTexelKHR = 0x00000800, + VolatileTexelKHR = VolatileTexel, SignExtend = 0x00001000, ZeroExtend = 0x00002000, Offsets = 0x00010000, @@ -478,16 +478,16 @@ namespace Spv PerTaskNV = 5273, PerVertexNV = 5285, NonUniform = 5300, - NonUniformEXT = 5300, + NonUniformEXT = NonUniform, RestrictPointer = 5355, - RestrictPointerEXT = 5355, + RestrictPointerEXT = RestrictPointer, AliasedPointer = 5356, - AliasedPointerEXT = 5356, + AliasedPointerEXT = AliasedPointer, ReferencedIndirectlyINTEL = 5602, CounterBuffer = 5634, - HlslCounterBufferGOOGLE = 5634, + HlslCounterBufferGOOGLE = CounterBuffer, HlslSemanticGOOGLE = 5635, - UserSemantic = 5635, + UserSemantic = HlslSemanticGOOGLE, UserTypeGOOGLE = 5636, RegisterINTEL = 5825, MemoryINTEL = 5826, @@ -547,15 +547,15 @@ namespace Spv VertexIndex = 42, InstanceIndex = 43, SubgroupEqMask = 4416, - SubgroupEqMaskKHR = 4416, + SubgroupEqMaskKHR = SubgroupEqMask, SubgroupGeMask = 4417, - SubgroupGeMaskKHR = 4417, + SubgroupGeMaskKHR = SubgroupGeMask, SubgroupGtMask = 4418, - SubgroupGtMaskKHR = 4418, + SubgroupGtMaskKHR = SubgroupGtMask, SubgroupLeMask = 4419, - SubgroupLeMaskKHR = 4419, + SubgroupLeMaskKHR = SubgroupLeMask, SubgroupLtMask = 4420, - SubgroupLtMaskKHR = 4420, + SubgroupLtMaskKHR = SubgroupLtMask, BaseVertex = 4424, BaseInstance = 4425, DrawIndex = 4426, @@ -588,36 +588,36 @@ namespace Spv BaryCoordNV = 5286, BaryCoordNoPerspNV = 5287, FragSizeEXT = 5292, - FragmentSizeNV = 5292, + FragmentSizeNV = FragSizeEXT, FragInvocationCountEXT = 5293, - InvocationsPerPixelNV = 5293, + InvocationsPerPixelNV = FragInvocationCountEXT, LaunchIdKHR = 5319, - LaunchIdNV = 5319, + LaunchIdNV = LaunchIdKHR, LaunchSizeKHR = 5320, - LaunchSizeNV = 5320, + LaunchSizeNV = LaunchSizeKHR, WorldRayOriginKHR = 5321, - WorldRayOriginNV = 5321, + WorldRayOriginNV = WorldRayOriginKHR, WorldRayDirectionKHR = 5322, - WorldRayDirectionNV = 5322, + WorldRayDirectionNV = WorldRayDirectionKHR, ObjectRayOriginKHR = 5323, - ObjectRayOriginNV = 5323, + ObjectRayOriginNV = ObjectRayOriginKHR, ObjectRayDirectionKHR = 5324, - ObjectRayDirectionNV = 5324, + ObjectRayDirectionNV = ObjectRayDirectionKHR, RayTminKHR = 5325, - RayTminNV = 5325, + RayTminNV = RayTminKHR, RayTmaxKHR = 5326, - RayTmaxNV = 5326, + RayTmaxNV = RayTmaxKHR, InstanceCustomIndexKHR = 5327, - InstanceCustomIndexNV = 5327, + InstanceCustomIndexNV = InstanceCustomIndexKHR, ObjectToWorldKHR = 5330, - ObjectToWorldNV = 5330, + ObjectToWorldNV = ObjectToWorldKHR, WorldToObjectKHR = 5331, - WorldToObjectNV = 5331, + WorldToObjectNV = WorldToObjectKHR, HitTNV = 5332, HitKindKHR = 5333, - HitKindNV = 5333, + HitKindNV = HitKindKHR, IncomingRayFlagsKHR = 5351, - IncomingRayFlagsNV = 5351, + IncomingRayFlagsNV = IncomingRayFlagsKHR, RayGeometryIndexKHR = 5352, WarpsPerSMNV = 5374, SMCountNV = 5375, @@ -709,11 +709,11 @@ namespace Spv AtomicCounterMemory = 10, ImageMemory = 11, OutputMemory = 12, - OutputMemoryKHR = 12, + OutputMemoryKHR = OutputMemory, MakeAvailable = 13, - MakeAvailableKHR = 13, + MakeAvailableKHR = MakeAvailable, MakeVisible = 14, - MakeVisibleKHR = 14, + MakeVisibleKHR = MakeVisible, Volatile = 15, } @@ -731,11 +731,11 @@ namespace Spv AtomicCounterMemory = 0x00000400, ImageMemory = 0x00000800, OutputMemory = 0x00001000, - OutputMemoryKHR = 0x00001000, + OutputMemoryKHR = OutputMemory, MakeAvailable = 0x00002000, - MakeAvailableKHR = 0x00002000, + MakeAvailableKHR = MakeAvailable, MakeVisible = 0x00004000, - MakeVisibleKHR = 0x00004000, + MakeVisibleKHR = MakeVisible, Volatile = 0x00008000, } @@ -745,11 +745,11 @@ namespace Spv Aligned = 1, Nontemporal = 2, MakePointerAvailable = 3, - MakePointerAvailableKHR = 3, + MakePointerAvailableKHR = MakePointerAvailable, MakePointerVisible = 4, - MakePointerVisibleKHR = 4, + MakePointerVisibleKHR = MakePointerVisible, NonPrivatePointer = 5, - NonPrivatePointerKHR = 5, + NonPrivatePointerKHR = NonPrivatePointer, } public enum MemoryAccessMask @@ -759,11 +759,11 @@ namespace Spv Aligned = 0x00000002, Nontemporal = 0x00000004, MakePointerAvailable = 0x00000008, - MakePointerAvailableKHR = 0x00000008, + MakePointerAvailableKHR = MakePointerAvailable, MakePointerVisible = 0x00000010, - MakePointerVisibleKHR = 0x00000010, + MakePointerVisibleKHR = MakePointerVisible, NonPrivatePointer = 0x00000020, - NonPrivatePointerKHR = 0x00000020, + NonPrivatePointerKHR = NonPrivatePointer, } public enum Scope @@ -774,7 +774,7 @@ namespace Spv Subgroup = 3, Invocation = 4, QueueFamily = 5, - QueueFamilyKHR = 5, + QueueFamilyKHR = QueueFamily, ShaderCallKHR = 6, } @@ -883,9 +883,9 @@ namespace Spv DrawParameters = 4427, SubgroupVoteKHR = 4431, StorageBuffer16BitAccess = 4433, - StorageUniformBufferBlock16 = 4433, + StorageUniformBufferBlock16 = StorageBuffer16BitAccess, StorageUniform16 = 4434, - UniformAndStorageBuffer16BitAccess = 4434, + UniformAndStorageBuffer16BitAccess = StorageUniform16, StoragePushConstant16 = 4435, StorageInputOutput16 = 4436, DeviceGroup = 4437, @@ -916,7 +916,7 @@ namespace Spv SampleMaskOverrideCoverageNV = 5249, GeometryShaderPassthroughNV = 5251, ShaderViewportIndexLayerEXT = 5254, - ShaderViewportIndexLayerNV = 5254, + ShaderViewportIndexLayerNV = ShaderViewportIndexLayerEXT, ShaderViewportMaskNV = 5255, ShaderStereoViewNV = 5259, PerViewAttributesNV = 5260, @@ -926,39 +926,39 @@ namespace Spv FragmentBarycentricNV = 5284, ComputeDerivativeGroupQuadsNV = 5288, FragmentDensityEXT = 5291, - ShadingRateNV = 5291, + ShadingRateNV = FragmentDensityEXT, GroupNonUniformPartitionedNV = 5297, ShaderNonUniform = 5301, - ShaderNonUniformEXT = 5301, + ShaderNonUniformEXT = ShaderNonUniform, RuntimeDescriptorArray = 5302, - RuntimeDescriptorArrayEXT = 5302, + RuntimeDescriptorArrayEXT = RuntimeDescriptorArray, InputAttachmentArrayDynamicIndexing = 5303, - InputAttachmentArrayDynamicIndexingEXT = 5303, + InputAttachmentArrayDynamicIndexingEXT = InputAttachmentArrayDynamicIndexing, UniformTexelBufferArrayDynamicIndexing = 5304, - UniformTexelBufferArrayDynamicIndexingEXT = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = UniformTexelBufferArrayDynamicIndexing, StorageTexelBufferArrayDynamicIndexing = 5305, - StorageTexelBufferArrayDynamicIndexingEXT = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = StorageTexelBufferArrayDynamicIndexing, UniformBufferArrayNonUniformIndexing = 5306, - UniformBufferArrayNonUniformIndexingEXT = 5306, + UniformBufferArrayNonUniformIndexingEXT = UniformBufferArrayNonUniformIndexing, SampledImageArrayNonUniformIndexing = 5307, - SampledImageArrayNonUniformIndexingEXT = 5307, + SampledImageArrayNonUniformIndexingEXT = SampledImageArrayNonUniformIndexing, StorageBufferArrayNonUniformIndexing = 5308, - StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageBufferArrayNonUniformIndexingEXT = StorageBufferArrayNonUniformIndexing, StorageImageArrayNonUniformIndexing = 5309, - StorageImageArrayNonUniformIndexingEXT = 5309, + StorageImageArrayNonUniformIndexingEXT = StorageImageArrayNonUniformIndexing, InputAttachmentArrayNonUniformIndexing = 5310, - InputAttachmentArrayNonUniformIndexingEXT = 5310, + InputAttachmentArrayNonUniformIndexingEXT = InputAttachmentArrayNonUniformIndexing, UniformTexelBufferArrayNonUniformIndexing = 5311, - UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = UniformTexelBufferArrayNonUniformIndexing, StorageTexelBufferArrayNonUniformIndexing = 5312, - StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = StorageTexelBufferArrayNonUniformIndexing, RayTracingNV = 5340, VulkanMemoryModel = 5345, - VulkanMemoryModelKHR = 5345, + VulkanMemoryModelKHR = VulkanMemoryModel, VulkanMemoryModelDeviceScope = 5346, - VulkanMemoryModelDeviceScopeKHR = 5346, + VulkanMemoryModelDeviceScopeKHR = VulkanMemoryModelDeviceScope, PhysicalStorageBufferAddresses = 5347, - PhysicalStorageBufferAddressesEXT = 5347, + PhysicalStorageBufferAddressesEXT = PhysicalStorageBufferAddresses, ComputeDerivativeGroupLinearNV = 5350, RayTracingProvisionalKHR = 5353, CooperativeMatrixNV = 5357, @@ -1433,12 +1433,12 @@ namespace Spv OpGroupNonUniformPartitionNV = 5296, OpWritePackedPrimitiveIndices4x8NV = 5299, OpReportIntersectionKHR = 5334, - OpReportIntersectionNV = 5334, + OpReportIntersectionNV = OpReportIntersectionKHR, OpIgnoreIntersectionNV = 5335, OpTerminateRayNV = 5336, OpTraceNV = 5337, OpTypeAccelerationStructureKHR = 5341, - OpTypeAccelerationStructureNV = 5341, + OpTypeAccelerationStructureNV = OpTypeAccelerationStructureKHR, OpExecuteCallableNV = 5344, OpTypeCooperativeMatrixNV = 5358, OpCooperativeMatrixLoadNV = 5359, @@ -1476,9 +1476,9 @@ namespace Spv OpFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, OpDecorateString = 5632, - OpDecorateStringGOOGLE = 5632, + OpDecorateStringGOOGLE = OpDecorateString, OpMemberDecorateString = 5633, - OpMemberDecorateStringGOOGLE = 5633, + OpMemberDecorateStringGOOGLE = OpMemberDecorateString, OpVmeImageINTEL = 5699, OpTypeVmeImageINTEL = 5700, OpTypeAvcImePayloadINTEL = 5701, @@ -1622,4 +1622,3 @@ namespace Spv } } } - From 40daca5684afdc8a62dfcb1beb27c58b5262c9e7 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:03:27 +0200 Subject: [PATCH 683/737] [Ryujinx.Headless.SDL2] Address dotnet-format issues (#5379) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Add trailing commas * Fix naming and formatting issues --- .../HeadlessDynamicTextInputHandler.cs | 6 +- .../HeadlessHostUiTheme.cs | 14 +- .../OpenGL/OpenGLWindow.cs | 54 ++-- src/Ryujinx.Headless.SDL2/Options.cs | 14 +- src/Ryujinx.Headless.SDL2/Program.cs | 234 +++++++++--------- src/Ryujinx.Headless.SDL2/SDL2Mouse.cs | 2 +- src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs | 33 ++- .../Vulkan/VulkanWindow.cs | 16 +- src/Ryujinx.Headless.SDL2/WindowBase.cs | 53 ++-- 9 files changed, 218 insertions(+), 208 deletions(-) diff --git a/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs b/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs index 7e624152f..aae01a0ce 100644 --- a/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs +++ b/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs @@ -12,8 +12,8 @@ namespace Ryujinx.Headless.SDL2 private bool _canProcessInput; public event DynamicTextChangedHandler TextChangedEvent; - public event KeyPressedHandler KeyPressedEvent { add { } remove { } } - public event KeyReleasedHandler KeyReleasedEvent { add { } remove { } } + public event KeyPressedHandler KeyPressedEvent { add { } remove { } } + public event KeyReleasedHandler KeyReleasedEvent { add { } remove { } } public bool TextProcessingEnabled { @@ -48,4 +48,4 @@ namespace Ryujinx.Headless.SDL2 public void Dispose() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs b/src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs index 4ef00b3c4..361ef0900 100644 --- a/src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs +++ b/src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs @@ -6,12 +6,10 @@ namespace Ryujinx.Headless.SDL2 { public string FontFamily => "sans-serif"; - public ThemeColor DefaultBackgroundColor => new ThemeColor(1, 0, 0, 0); - public ThemeColor DefaultForegroundColor => new ThemeColor(1, 1, 1, 1); - public ThemeColor DefaultBorderColor => new ThemeColor(1, 1, 1, 1); - public ThemeColor SelectionBackgroundColor => new ThemeColor(1, 1, 1, 1); - public ThemeColor SelectionForegroundColor => new ThemeColor(1, 0, 0, 0); - - public HeadlessHostUiTheme() { } + public ThemeColor DefaultBackgroundColor => new(1, 0, 0, 0); + public ThemeColor DefaultForegroundColor => new(1, 1, 1, 1); + public ThemeColor DefaultBorderColor => new(1, 1, 1, 1); + public ThemeColor SelectionBackgroundColor => new(1, 1, 1, 1); + public ThemeColor SelectionForegroundColor => new(1, 0, 0, 0); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs index dc7d811b9..199f723e9 100644 --- a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs @@ -11,23 +11,31 @@ namespace Ryujinx.Headless.SDL2.OpenGL { class OpenGLWindow : WindowBase { + private static void CheckResult(int result) + { + if (result < 0) + { + throw new InvalidOperationException($"SDL_GL function returned an error: {SDL_GetError()}"); + } + } + private static void SetupOpenGLAttributes(bool sharedContext, GraphicsDebugLevel debugLevel) { - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 3)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0)); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ALPHA_SIZE, 8); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 0); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STEREO, 0); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ALPHA_SIZE, 8)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 0)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1)); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STEREO, 0)); } private class OpenToolkitBindingsContext : IBindingsContext @@ -40,9 +48,9 @@ namespace Ryujinx.Headless.SDL2.OpenGL private class SDL2OpenGLContext : IOpenGLContext { - private IntPtr _context; - private IntPtr _window; - private bool _shouldDisposeWindow; + private readonly IntPtr _context; + private readonly IntPtr _window; + private readonly bool _shouldDisposeWindow; public SDL2OpenGLContext(IntPtr context, IntPtr window, bool shouldDisposeWindow = true) { @@ -62,9 +70,9 @@ namespace Ryujinx.Headless.SDL2.OpenGL GL.LoadBindings(new OpenToolkitBindingsContext()); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0); + CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0)); - SDL_GL_MakeCurrent(windowHandle, IntPtr.Zero); + CheckResult(SDL_GL_MakeCurrent(windowHandle, IntPtr.Zero)); return new SDL2OpenGLContext(context, windowHandle); } @@ -99,7 +107,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL } } - private GraphicsDebugLevel _glLogLevel; + private readonly GraphicsDebugLevel _glLogLevel; private SDL2OpenGLContext _openGLContext; public OpenGLWindow( @@ -120,7 +128,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL // Ensure to not share this context with other contexts before this point. SetupOpenGLAttributes(false, _glLogLevel); IntPtr context = SDL_GL_CreateContext(WindowHandle); - SDL_GL_SetSwapInterval(1); + CheckResult(SDL_GL_SetSwapInterval(1)); if (context == IntPtr.Zero) { @@ -157,7 +165,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL Device.DisposeGpu(); // Unbind context and destroy everything - SDL_GL_MakeCurrent(WindowHandle, IntPtr.Zero); + CheckResult(SDL_GL_MakeCurrent(WindowHandle, IntPtr.Zero)); _openGLContext.Dispose(); } @@ -166,4 +174,4 @@ namespace Ryujinx.Headless.SDL2.OpenGL SDL_GL_SwapWindow(WindowHandle); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index d7ad8cf79..86a6c63c4 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Headless.SDL2 // System [Option("disable-ptc", Required = false, HelpText = "Disables profiled persistent translation cache.")] - public bool DisablePtc { get; set; } + public bool DisablePTC { get; set; } [Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")] public bool EnableInternetAccess { get; set; } @@ -100,7 +100,7 @@ namespace Ryujinx.Headless.SDL2 public int FsGlobalAccessLogMode { get; set; } [Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")] - public bool DisableVsync { get; set; } + public bool DisableVSync { get; set; } [Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")] public bool DisableShaderCache { get; set; } @@ -126,7 +126,7 @@ namespace Ryujinx.Headless.SDL2 [Option("memory-manager-mode", Required = false, Default = MemoryManagerMode.HostMappedUnsafe, HelpText = "The selected memory manager mode.")] public MemoryManagerMode MemoryManagerMode { get; set; } - [Option("audio-volume", Required = false, Default = 1.0f, HelpText ="The audio level (0 to 1).")] + [Option("audio-volume", Required = false, Default = 1.0f, HelpText = "The audio level (0 to 1).")] public float AudioVolume { get; set; } [Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")] @@ -181,7 +181,7 @@ namespace Ryujinx.Headless.SDL2 [Option("backend-threading", Required = false, Default = BackendThreading.Auto, HelpText = "Whether or not backend threading is enabled. The \"Auto\" setting will determine whether threading should be enabled at runtime.")] public BackendThreading BackendThreading { get; set; } - [Option("disable-macro-hle", Required= false, HelpText = "Disables high-level emulation of Macro code. Leaving this enabled improves performance but may cause graphical glitches in some games.")] + [Option("disable-macro-hle", Required = false, HelpText = "Disables high-level emulation of Macro code. Leaving this enabled improves performance but may cause graphical glitches in some games.")] public bool DisableMacroHLE { get; set; } [Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")] @@ -191,12 +191,12 @@ namespace Ryujinx.Headless.SDL2 public GraphicsBackend GraphicsBackend { get; set; } [Option("preferred-gpu-vendor", Required = false, Default = "", HelpText = "When using the Vulkan backend, prefer using the GPU from the specified vendor.")] - public string PreferredGpuVendor { get; set; } + public string PreferredGPUVendor { get; set; } // Hacks [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")] - public bool ExpandRam { get; set; } + public bool ExpandRAM { get; set; } [Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] public bool IgnoreMissingServices { get; set; } @@ -206,4 +206,4 @@ namespace Ryujinx.Headless.SDL2 [Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)] public string InputPath { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 6e6b4a7f3..39eae14a8 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -28,6 +28,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.Input; using Ryujinx.Input.HLE; using Ryujinx.Input.SDL2; +using Ryujinx.SDL2.Common; using Silk.NET.Vulkan; using System; using System.Collections.Generic; @@ -57,7 +58,7 @@ namespace Ryujinx.Headless.SDL2 private static bool _enableKeyboard; private static bool _enableMouse; - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); static void Main(string[] args) { @@ -67,10 +68,10 @@ namespace Ryujinx.Headless.SDL2 if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) { - AutoResetEvent invoked = new AutoResetEvent(false); + AutoResetEvent invoked = new(false); // MacOS must perform SDL polls from the main thread. - Ryujinx.SDL2.Common.SDL2Driver.MainThreadDispatcher = (Action action) => + SDL2Driver.MainThreadDispatcher = action => { invoked.Reset(); @@ -140,53 +141,53 @@ namespace Ryujinx.Headless.SDL2 { config = new StandardKeyboardInputConfig { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = null, - ControllerType = ControllerType.JoyconPair, - LeftJoycon = new LeftJoyconCommonConfig<Key> + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.WindowKeyboard, + Id = null, + ControllerType = ControllerType.JoyconPair, + LeftJoycon = new LeftJoyconCommonConfig<Key> { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound + DpadUp = Key.Up, + DpadDown = Key.Down, + DpadLeft = Key.Left, + DpadRight = Key.Right, + ButtonMinus = Key.Minus, + ButtonL = Key.E, + ButtonZl = Key.Q, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, }, - LeftJoyconStick = new JoyconConfigKeyboardStick<Key> + LeftJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, + StickUp = Key.W, + StickDown = Key.S, + StickLeft = Key.A, + StickRight = Key.D, + StickButton = Key.F, }, - RightJoycon = new RightJoyconCommonConfig<Key> + RightJoycon = new RightJoyconCommonConfig<Key> { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound + ButtonA = Key.Z, + ButtonB = Key.X, + ButtonX = Key.C, + ButtonY = Key.V, + ButtonPlus = Key.Plus, + ButtonR = Key.U, + ButtonZr = Key.O, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - } + StickUp = Key.I, + StickDown = Key.K, + StickLeft = Key.J, + StickRight = Key.L, + StickButton = Key.H, + }, }; } else @@ -195,72 +196,72 @@ namespace Ryujinx.Headless.SDL2 config = new StandardControllerInputConfig { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.GamepadSDL2, - Id = null, - ControllerType = ControllerType.JoyconPair, - DeadzoneLeft = 0.1f, - DeadzoneRight = 0.1f, - RangeLeft = 1.0f, - RangeRight = 1.0f, + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.GamepadSDL2, + Id = null, + ControllerType = ControllerType.JoyconPair, + DeadzoneLeft = 0.1f, + DeadzoneRight = 0.1f, + RangeLeft = 1.0f, + RangeRight = 1.0f, TriggerThreshold = 0.5f, LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId> { - DpadUp = ConfigGamepadInputId.DpadUp, - DpadDown = ConfigGamepadInputId.DpadDown, - DpadLeft = ConfigGamepadInputId.DpadLeft, - DpadRight = ConfigGamepadInputId.DpadRight, - ButtonMinus = ConfigGamepadInputId.Minus, - ButtonL = ConfigGamepadInputId.LeftShoulder, - ButtonZl = ConfigGamepadInputId.LeftTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, + DpadUp = ConfigGamepadInputId.DpadUp, + DpadDown = ConfigGamepadInputId.DpadDown, + DpadLeft = ConfigGamepadInputId.DpadLeft, + DpadRight = ConfigGamepadInputId.DpadRight, + ButtonMinus = ConfigGamepadInputId.Minus, + ButtonL = ConfigGamepadInputId.LeftShoulder, + ButtonZl = ConfigGamepadInputId.LeftTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, }, LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { - Joystick = ConfigStickInputId.Left, - StickButton = ConfigGamepadInputId.LeftStick, + Joystick = ConfigStickInputId.Left, + StickButton = ConfigGamepadInputId.LeftStick, InvertStickX = false, InvertStickY = false, - Rotate90CW = false, + Rotate90CW = false, }, RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, - ButtonPlus = ConfigGamepadInputId.Plus, - ButtonR = ConfigGamepadInputId.RightShoulder, - ButtonZr = ConfigGamepadInputId.RightTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonPlus = ConfigGamepadInputId.Plus, + ButtonR = ConfigGamepadInputId.RightShoulder, + ButtonZr = ConfigGamepadInputId.RightTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, }, RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { - Joystick = ConfigStickInputId.Right, - StickButton = ConfigGamepadInputId.RightStick, + Joystick = ConfigStickInputId.Right, + StickButton = ConfigGamepadInputId.RightStick, InvertStickX = false, InvertStickY = false, - Rotate90CW = false, + Rotate90CW = false, }, Motion = new StandardMotionConfigController { MotionBackend = MotionInputBackendType.GamepadDriver, EnableMotion = true, - Sensitivity = 100, + Sensitivity = 100, GyroDeadzone = 1, }, Rumble = new RumbleConfigController { StrongRumble = 1f, WeakRumble = 1f, - EnableRumble = false - } + EnableRumble = false, + }, }; } } @@ -288,7 +289,7 @@ namespace Ryujinx.Headless.SDL2 try { - config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); + config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); } catch (JsonException) { @@ -310,7 +311,7 @@ namespace Ryujinx.Headless.SDL2 { if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f) { - controllerConfig.RangeLeft = 1.0f; + controllerConfig.RangeLeft = 1.0f; controllerConfig.RangeRight = 1.0f; Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); @@ -387,7 +388,7 @@ namespace Ryujinx.Headless.SDL2 _enableKeyboard = option.EnableKeyboard; _enableMouse = option.EnableMouse; - void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) + static void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) { InputConfig inputConfig = HandlePlayerConfiguration(inputProfileName, inputId, index); @@ -468,19 +469,12 @@ namespace Ryujinx.Headless.SDL2 private static void ProgressHandler<T>(T state, int current, int total) where T : Enum { - string label; - - switch (state) + string label = state switch { - case LoadState ptcState: - label = $"PTC : {current}/{total}"; - break; - case ShaderCacheState shaderCacheState: - label = $"Shaders : {current}/{total}"; - break; - default: - throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}"); - } + LoadState => $"PTC : {current}/{total}", + ShaderCacheState => $"Shaders : {current}/{total}", + _ => throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}"), + }; Logger.Info?.Print(LogClass.Application, label); } @@ -499,9 +493,9 @@ namespace Ryujinx.Headless.SDL2 string preferredGpuId = string.Empty; Vk api = Vk.GetApi(); - if (!string.IsNullOrEmpty(options.PreferredGpuVendor)) + if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) { - string preferredGpuVendor = options.PreferredGpuVendor.ToLowerInvariant(); + string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant(); var devices = VulkanRenderer.GetPhysicalDevices(api); foreach (var device in devices) @@ -520,10 +514,8 @@ namespace Ryujinx.Headless.SDL2 vulkanWindow.GetRequiredInstanceExtensions, preferredGpuId); } - else - { - return new OpenGLRenderer(); - } + + return new OpenGLRenderer(); } private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) @@ -537,31 +529,31 @@ namespace Ryujinx.Headless.SDL2 renderer = new ThreadedRenderer(renderer); } - HLEConfiguration configuration = new HLEConfiguration(_virtualFileSystem, - _libHacHorizonManager, - _contentManager, - _accountManager, - _userChannelPersistence, - renderer, - new SDL2HardwareDeviceDriver(), - options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, - window, - options.SystemLanguage, - options.SystemRegion, - !options.DisableVsync, - !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); + HLEConfiguration configuration = new(_virtualFileSystem, + _libHacHorizonManager, + _contentManager, + _accountManager, + _userChannelPersistence, + renderer, + new SDL2HardwareDeviceDriver(), + options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, + window, + options.SystemLanguage, + options.SystemRegion, + !options.DisableVSync, + !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); return new Switch(configuration); } @@ -713,4 +705,4 @@ namespace Ryujinx.Headless.SDL2 return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/SDL2Mouse.cs b/src/Ryujinx.Headless.SDL2/SDL2Mouse.cs index 4ce8eafd9..684433688 100644 --- a/src/Ryujinx.Headless.SDL2/SDL2Mouse.cs +++ b/src/Ryujinx.Headless.SDL2/SDL2Mouse.cs @@ -87,4 +87,4 @@ namespace Ryujinx.Headless.SDL2 _driver = null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs b/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs index 7b88e2655..8983091f5 100644 --- a/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs +++ b/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs @@ -1,4 +1,5 @@ -using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Input; using System; using System.Diagnostics; @@ -14,7 +15,7 @@ namespace Ryujinx.Headless.SDL2 private const int CursorHideIdleTime = 5; // seconds private bool _isDisposed; - private HideCursorMode _hideCursorMode; + private readonly HideCursorMode _hideCursorMode; private bool _isHidden; private long _lastCursorMoveTime; @@ -22,7 +23,7 @@ namespace Ryujinx.Headless.SDL2 public Vector2 CurrentPosition { get; private set; } public Vector2 Scroll { get; private set; } - public Size _clientSize; + public Size ClientSize; public SDL2MouseDriver(HideCursorMode hideCursorMode) { @@ -31,7 +32,11 @@ namespace Ryujinx.Headless.SDL2 if (_hideCursorMode == HideCursorMode.Always) { - SDL_ShowCursor(SDL_DISABLE); + if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE) + { + Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor."); + } + _isHidden = true; } } @@ -46,7 +51,7 @@ namespace Ryujinx.Headless.SDL2 public void UpdatePosition() { - SDL_GetMouseState(out int posX, out int posY); + _ = SDL_GetMouseState(out int posX, out int posY); Vector2 position = new(posX, posY); if (CurrentPosition != position) @@ -71,7 +76,11 @@ namespace Ryujinx.Headless.SDL2 { if (!_isHidden) { - SDL_ShowCursor(SDL_DISABLE); + if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE) + { + Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor."); + } + _isHidden = true; } } @@ -79,7 +88,11 @@ namespace Ryujinx.Headless.SDL2 { if (_isHidden) { - SDL_ShowCursor(SDL_ENABLE); + if (SDL_ShowCursor(SDL_ENABLE) != SDL_ENABLE) + { + Logger.Error?.PrintMsg(LogClass.Application, "Failed to enable the cursor."); + } + _isHidden = false; } } @@ -118,7 +131,7 @@ namespace Ryujinx.Headless.SDL2 public void SetClientSize(int width, int height) { - _clientSize = new Size(width, height); + ClientSize = new Size(width, height); } public bool IsButtonPressed(MouseButton button) @@ -128,7 +141,7 @@ namespace Ryujinx.Headless.SDL2 public Size GetClientSize() { - return _clientSize; + return ClientSize; } public string DriverName => "SDL2"; @@ -162,4 +175,4 @@ namespace Ryujinx.Headless.SDL2 _isDisposed = true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs index 5d048da1f..f2f337a53 100644 --- a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Headless.SDL2.Vulkan { class VulkanWindow : WindowBase { - private GraphicsDebugLevel _glLogLevel; + private readonly GraphicsDebugLevel _glLogLevel; public VulkanWindow( InputManager inputManager, @@ -33,16 +33,16 @@ namespace Ryujinx.Headless.SDL2.Vulkan MouseDriver.SetClientSize(DefaultWidth, DefaultHeight); } - private void BasicInvoke(Action action) + private static void BasicInvoke(Action action) { action(); } - public unsafe IntPtr CreateWindowSurface(IntPtr instance) + public IntPtr CreateWindowSurface(IntPtr instance) { ulong surfaceHandle = 0; - Action createSurface = () => + void CreateSurface() { if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out surfaceHandle) == SDL_bool.SDL_FALSE) { @@ -52,15 +52,15 @@ namespace Ryujinx.Headless.SDL2.Vulkan throw new Exception(errorMessage); } - }; + } if (SDL2Driver.MainThreadDispatcher != null) { - SDL2Driver.MainThreadDispatcher(createSurface); + SDL2Driver.MainThreadDispatcher(CreateSurface); } else { - createSurface(); + CreateSurface(); } return (IntPtr)surfaceHandle; @@ -101,4 +101,4 @@ namespace Ryujinx.Headless.SDL2.Vulkan protected override void SwapBuffers() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index d163da229..2fcd00f86 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -4,6 +4,8 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; +using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.OpenGL; using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; using Ryujinx.HLE.Ui; @@ -30,7 +32,7 @@ namespace Ryujinx.Headless.SDL2 private const SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL_WindowFlags.SDL_WINDOW_SHOWN; private const int TargetFps = 60; - private static ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>(); + private static readonly ConcurrentQueue<Action> _mainThreadActions = new(); [LibraryImport("SDL2")] // TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly @@ -38,7 +40,7 @@ namespace Ryujinx.Headless.SDL2 public static void QueueMainThreadAction(Action action) { - MainThreadActions.Enqueue(action); + _mainThreadActions.Enqueue(action); } public NpadManager NpadManager { get; } @@ -55,9 +57,9 @@ namespace Ryujinx.Headless.SDL2 public int Height { get; private set; } protected SDL2MouseDriver MouseDriver; - private InputManager _inputManager; - private IKeyboard _keyboardInterface; - private GraphicsDebugLevel _glLogLevel; + private readonly InputManager _inputManager; + private readonly IKeyboard _keyboardInterface; + private readonly GraphicsDebugLevel _glLogLevel; private readonly Stopwatch _chrono; private readonly long _ticksPerFrame; private readonly CancellationTokenSource _gpuCancellationTokenSource; @@ -71,8 +73,8 @@ namespace Ryujinx.Headless.SDL2 private string _gpuVendorName; - private AspectRatio _aspectRatio; - private bool _enableMouse; + private readonly AspectRatio _aspectRatio; + private readonly bool _enableMouse; public WindowBase( InputManager inputManager, @@ -192,9 +194,6 @@ namespace Ryujinx.Headless.SDL2 case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE: Exit(); break; - - default: - break; } } else @@ -260,7 +259,7 @@ namespace Ryujinx.Headless.SDL2 if (_ticks >= _ticksPerFrame) { string dockedMode = Device.System.State.DockedMode ? "Docked" : "Handheld"; - float scale = Graphics.Gpu.GraphicsConfig.ResScale; + float scale = GraphicsConfig.ResScale; if (scale != 1) { dockedMode += $" ({scale}x)"; @@ -309,9 +308,9 @@ namespace Ryujinx.Headless.SDL2 _exitEvent.Dispose(); } - public void ProcessMainThreadQueue() + public static void ProcessMainThreadQueue() { - while (MainThreadActions.TryDequeue(out Action action)) + while (_mainThreadActions.TryDequeue(out Action action)) { action(); } @@ -334,7 +333,7 @@ namespace Ryujinx.Headless.SDL2 _exitEvent.Set(); } - private void NVStutterWorkaround() + private void NvidiaStutterWorkaround() { while (_isActive) { @@ -348,7 +347,7 @@ namespace Ryujinx.Headless.SDL2 // TODO: This should be removed when the issue with the GateThread is resolved. - ThreadPool.QueueUserWorkItem((state) => { }); + ThreadPool.QueueUserWorkItem(state => { }); Thread.Sleep(300); } } @@ -396,20 +395,20 @@ namespace Ryujinx.Headless.SDL2 InitializeWindow(); - Thread renderLoopThread = new Thread(Render) + Thread renderLoopThread = new(Render) { - Name = "GUI.RenderLoop" + Name = "GUI.RenderLoop", }; renderLoopThread.Start(); - Thread nvStutterWorkaround = null; - if (Renderer is Graphics.OpenGL.OpenGLRenderer) + Thread nvidiaStutterWorkaround = null; + if (Renderer is OpenGLRenderer) { - nvStutterWorkaround = new Thread(NVStutterWorkaround) + nvidiaStutterWorkaround = new Thread(NvidiaStutterWorkaround) { - Name = "GUI.NVStutterWorkaround" + Name = "GUI.NvidiaStutterWorkaround", }; - nvStutterWorkaround.Start(); + nvidiaStutterWorkaround.Start(); } MainLoop(); @@ -418,7 +417,7 @@ namespace Ryujinx.Headless.SDL2 // We only need to wait for all commands submitted during the main gpu loop to be processed. _gpuDoneEvent.WaitOne(); _gpuDoneEvent.Dispose(); - nvStutterWorkaround?.Join(); + nvidiaStutterWorkaround?.Join(); Exit(); } @@ -465,13 +464,13 @@ namespace Ryujinx.Headless.SDL2 public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText) { - SDL_MessageBoxData data = new SDL_MessageBoxData + SDL_MessageBoxData data = new() { title = title, message = message, buttons = new SDL_MessageBoxButtonData[buttonsText.Length], numbuttons = buttonsText.Length, - window = WindowHandle + window = WindowHandle, }; for (int i = 0; i < buttonsText.Length; i++) @@ -479,7 +478,7 @@ namespace Ryujinx.Headless.SDL2 data.buttons[i] = new SDL_MessageBoxButtonData { buttonid = i, - text = buttonsText[i] + text = buttonsText[i], }; } @@ -509,4 +508,4 @@ namespace Ryujinx.Headless.SDL2 } } } -} \ No newline at end of file +} From 16fa98370472f843ebdae5addbb00e26899ebe55 Mon Sep 17 00:00:00 2001 From: Ac_K <Acoustik666@gmail.com> Date: Wed, 28 Jun 2023 19:09:48 +0200 Subject: [PATCH 684/737] macOS: Fix warning in some shell scripts (#5398) * macOS: Fix warning in some shell scripts In a way to continue the cleaning of the project, there are some warnings which can be easily fixed. * Try to fix CI * Fix APP_ARGUMENTS * Addresses feedback --- distribution/macos/create_macos_build.sh | 16 ++++++++-------- distribution/macos/updater.sh | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/distribution/macos/create_macos_build.sh b/distribution/macos/create_macos_build.sh index 7405073d2..80594a40a 100755 --- a/distribution/macos/create_macos_build.sh +++ b/distribution/macos/create_macos_build.sh @@ -35,12 +35,12 @@ EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx rm -rf "$TEMP_DIRECTORY" mkdir -p "$TEMP_DIRECTORY" -DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true $EXTRA_ARGS" +DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS) dotnet restore -dotnet build -c $CONFIGURATION src/Ryujinx.Ava -dotnet publish -c $CONFIGURATION -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava -dotnet publish -c $CONFIGURATION -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava +dotnet build -c "$CONFIGURATION" src/Ryujinx.Ava +dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava +dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Ava # Get rid of the support library for ARMeilleure for x64 (that's only for arm64) rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib" @@ -104,10 +104,10 @@ fi echo "Creating archive" pushd "$OUTPUT_DIRECTORY" -tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf $RELEASE_TAR_FILE_NAME Ryujinx.app 1> /dev/null -python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx" -gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz -rm $RELEASE_TAR_FILE_NAME +tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf "$RELEASE_TAR_FILE_NAME" Ryujinx.app 1> /dev/null +python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx" +gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz" +rm "$RELEASE_TAR_FILE_NAME" popd echo "Done" \ No newline at end of file diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh index 4d7dcdf23..12e4c3aa1 100755 --- a/distribution/macos/updater.sh +++ b/distribution/macos/updater.sh @@ -5,7 +5,7 @@ set -e INSTALL_DIRECTORY=$1 NEW_APP_DIRECTORY=$2 APP_PID=$3 -APP_ARGUMENTS="${@:4}" +APP_ARGUMENTS=("${@:4}") error_handler() { local lineno="$1" @@ -33,7 +33,7 @@ trap 'error_handler ${LINENO}' ERR attempt=0 while true; do - if lsof -p $APP_PID +r 1 &>/dev/null || ps -p "$APP_PID" &>/dev/null; then + if lsof -p "$APP_PID" +r 1 &>/dev/null || ps -p "$APP_PID" &>/dev/null; then if [ "$attempt" -eq 4 ]; then exit 1 fi @@ -53,5 +53,5 @@ mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY" if [ "$#" -le 3 ]; then open -a "$INSTALL_DIRECTORY" else - open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS" -fi + open -a "$INSTALL_DIRECTORY" --args "${APP_ARGUMENTS[@]}" +fi \ No newline at end of file From 7c989f88bdab65dfa7783e824a180220ba829bd0 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 28 Jun 2023 20:20:10 +0200 Subject: [PATCH 685/737] [Ryujinx.Graphics.GAL] Address dotnet-format issues (#5366) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0052 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA1069 warnings * Address remaining dotnet format analyzer warnings * Address review comments * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Revert formatting changes for while and for-loops * Another rebase, another dotnet format run * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format analyzers after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Address IDE0251 warnings * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Address review feedback * Add trailing commas * Remove SuppressMessage for IDE0066 * Make explicit Equals implementation implicit --- src/Ryujinx.Graphics.GAL/AddressMode.cs | 4 +- .../AdvancedBlendDescriptor.cs | 2 +- src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs | 4 +- .../AdvancedBlendOverlap.cs | 2 +- src/Ryujinx.Graphics.GAL/AntiAliasing.cs | 2 +- src/Ryujinx.Graphics.GAL/BlendDescriptor.cs | 22 +++--- src/Ryujinx.Graphics.GAL/BlendFactor.cs | 36 ++++----- src/Ryujinx.Graphics.GAL/BlendOp.cs | 10 +-- src/Ryujinx.Graphics.GAL/BufferAccess.cs | 2 +- src/Ryujinx.Graphics.GAL/BufferAssignment.cs | 2 +- src/Ryujinx.Graphics.GAL/BufferHandle.cs | 2 +- src/Ryujinx.Graphics.GAL/BufferRange.cs | 8 +- src/Ryujinx.Graphics.GAL/Capabilities.cs | 2 +- src/Ryujinx.Graphics.GAL/CompareMode.cs | 4 +- src/Ryujinx.Graphics.GAL/CompareOp.cs | 16 ++-- src/Ryujinx.Graphics.GAL/CounterType.cs | 4 +- src/Ryujinx.Graphics.GAL/DepthMode.cs | 4 +- src/Ryujinx.Graphics.GAL/DepthStencilMode.cs | 4 +- .../DepthTestDescriptor.cs | 10 +-- src/Ryujinx.Graphics.GAL/DeviceInfo.cs | 2 +- src/Ryujinx.Graphics.GAL/Extents2D.cs | 6 +- src/Ryujinx.Graphics.GAL/Extents2DF.cs | 2 +- src/Ryujinx.Graphics.GAL/Face.cs | 8 +- src/Ryujinx.Graphics.GAL/Format.cs | 4 +- src/Ryujinx.Graphics.GAL/FrontFace.cs | 6 +- src/Ryujinx.Graphics.GAL/ISampler.cs | 2 +- src/Ryujinx.Graphics.GAL/ITexture.cs | 3 +- src/Ryujinx.Graphics.GAL/ImageCrop.cs | 44 +++++------ src/Ryujinx.Graphics.GAL/IndexType.cs | 4 +- src/Ryujinx.Graphics.GAL/LogicalOp.cs | 2 +- src/Ryujinx.Graphics.GAL/MagFilter.cs | 4 +- src/Ryujinx.Graphics.GAL/MinFilter.cs | 4 +- .../Multithreading/BufferMap.cs | 24 +++--- .../Multithreading/CommandHelper.cs | 6 +- .../Multithreading/CommandType.cs | 2 +- .../Multithreading/Commands/BarrierCommand.cs | 2 +- .../Commands/BeginTransformFeedbackCommand.cs | 2 +- .../Commands/Buffer/BufferDisposeCommand.cs | 2 +- .../Commands/Buffer/BufferGetDataCommand.cs | 3 +- .../Commands/Buffer/BufferSetDataCommand.cs | 2 +- .../Commands/ClearBufferCommand.cs | 2 +- .../Commands/ClearRenderTargetColorCommand.cs | 2 +- .../ClearRenderTargetDepthStencilCommand.cs | 2 +- .../Commands/CommandBufferBarrierCommand.cs | 2 +- .../Commands/CopyBufferCommand.cs | 2 +- .../CounterEventDisposeCommand.cs | 2 +- .../CounterEvent/CounterEventFlushCommand.cs | 2 +- .../Commands/DispatchComputeCommand.cs | 2 +- .../Multithreading/Commands/DrawCommand.cs | 2 +- .../Commands/DrawIndexedCommand.cs | 2 +- .../Commands/DrawIndexedIndirectCommand.cs | 2 +- .../DrawIndexedIndirectCountCommand.cs | 2 +- .../Commands/DrawIndirectCommand.cs | 2 +- .../Commands/DrawIndirectCountCommand.cs | 2 +- .../Commands/DrawTextureCommand.cs | 2 +- .../EndHostConditionalRenderingCommand.cs | 2 +- .../Commands/EndTransformFeedbackCommand.cs | 2 +- .../Program/ProgramCheckLinkCommand.cs | 2 +- .../Commands/Program/ProgramDisposeCommand.cs | 2 +- .../Program/ProgramGetBinaryCommand.cs | 2 +- .../Commands/Renderer/ActionCommand.cs | 2 +- .../Renderer/CreateBufferAccessCommand.cs | 2 +- .../Commands/Renderer/CreateBufferCommand.cs | 2 +- .../Renderer/CreateHostBufferCommand.cs | 2 +- .../Commands/Renderer/CreateProgramCommand.cs | 2 +- .../Commands/Renderer/CreateSamplerCommand.cs | 2 +- .../Commands/Renderer/CreateSyncCommand.cs | 2 +- .../Commands/Renderer/CreateTextureCommand.cs | 2 +- .../Renderer/GetCapabilitiesCommand.cs | 2 +- .../Commands/Renderer/PreFrameCommand.cs | 2 +- .../Commands/Renderer/ReportCounterCommand.cs | 2 +- .../Commands/Renderer/ResetCounterCommand.cs | 2 +- .../Renderer/UpdateCountersCommand.cs | 2 +- .../Commands/Sampler/SamplerDisposeCommand.cs | 2 +- .../Commands/SetAlphaTestCommand.cs | 2 +- .../Commands/SetBlendStateAdvancedCommand.cs | 2 +- .../Commands/SetBlendStateCommand.cs | 2 +- .../Commands/SetDepthBiasCommand.cs | 2 +- .../Commands/SetDepthClampCommand.cs | 2 +- .../Commands/SetDepthModeCommand.cs | 2 +- .../Commands/SetDepthTestCommand.cs | 2 +- .../Commands/SetFaceCullingCommand.cs | 2 +- .../Commands/SetFrontFaceCommand.cs | 2 +- .../Commands/SetImageCommand.cs | 2 +- .../Commands/SetIndexBufferCommand.cs | 2 +- .../Commands/SetLineParametersCommand.cs | 2 +- .../Commands/SetLogicOpStateCommand.cs | 2 +- .../Commands/SetMultisampleStateCommand.cs | 2 +- .../Commands/SetPatchParametersCommand.cs | 2 +- .../Commands/SetPointParametersCommand.cs | 2 +- .../Commands/SetPolygonModeCommand.cs | 2 +- .../Commands/SetPrimitiveRestartCommand.cs | 2 +- .../Commands/SetPrimitiveTopologyCommand.cs | 2 +- .../Commands/SetProgramCommand.cs | 2 +- .../Commands/SetRasterizerDiscardCommand.cs | 2 +- .../SetRenderTargetColorMasksCommand.cs | 2 +- .../Commands/SetRenderTargetScaleCommand.cs | 2 +- .../Commands/SetRenderTargetsCommand.cs | 2 +- .../Commands/SetScissorsCommand.cs | 2 +- .../Commands/SetStencilTestCommand.cs | 2 +- .../Commands/SetStorageBuffersCommand.cs | 2 +- .../Commands/SetTextureAndSamplerCommand.cs | 2 +- .../SetTransformFeedbackBuffersCommand.cs | 2 +- .../Commands/SetUniformBuffersCommand.cs | 2 +- .../Commands/SetUserClipDistanceCommand.cs | 2 +- .../Commands/SetVertexAttribsCommand.cs | 2 +- .../Commands/SetVertexBuffersCommand.cs | 2 +- .../Commands/SetViewportsCommand.cs | 2 +- .../Texture/TextureCopyToBufferCommand.cs | 2 +- .../Commands/Texture/TextureCopyToCommand.cs | 2 +- .../Texture/TextureCopyToScaledCommand.cs | 2 +- .../Texture/TextureCopyToSliceCommand.cs | 2 +- .../Texture/TextureCreateViewCommand.cs | 2 +- .../Commands/Texture/TextureGetDataCommand.cs | 3 +- .../Texture/TextureGetDataSliceCommand.cs | 3 +- .../Commands/Texture/TextureReleaseCommand.cs | 2 +- .../Commands/Texture/TextureSetDataCommand.cs | 2 +- .../Texture/TextureSetDataSliceCommand.cs | 2 +- .../TextureSetDataSliceRegionCommand.cs | 2 +- .../Texture/TextureSetStorageCommand.cs | 2 +- .../Commands/TextureBarrierCommand.cs | 2 +- .../Commands/TextureBarrierTiledCommand.cs | 2 +- .../TryHostConditionalRenderingCommand.cs | 2 +- ...TryHostConditionalRenderingFlushCommand.cs | 2 +- .../Commands/UpdateRenderScaleCommand.cs | 2 +- .../Commands/Window/WindowPresentCommand.cs | 2 +- .../Multithreading/Model/CircularSpanPool.cs | 8 +- .../Multithreading/Model/SpanRef.cs | 4 +- .../Multithreading/Model/TableRef.cs | 4 +- .../Multithreading/Resources/ProgramQueue.cs | 6 +- .../Programs/BinaryProgramRequest.cs | 4 +- .../Programs/SourceProgramRequest.cs | 2 +- .../Resources/ThreadedCounterEvent.cs | 2 +- .../Resources/ThreadedProgram.cs | 6 +- .../Resources/ThreadedSampler.cs | 2 +- .../Resources/ThreadedTexture.cs | 11 ++- .../Multithreading/SyncMap.cs | 4 +- .../Multithreading/ThreadedPipeline.cs | 8 +- .../Multithreading/ThreadedRenderer.cs | 52 ++++++------ .../Multithreading/ThreadedWindow.cs | 4 +- src/Ryujinx.Graphics.GAL/Origin.cs | 2 +- src/Ryujinx.Graphics.GAL/PinnedSpan.cs | 8 +- src/Ryujinx.Graphics.GAL/PolygonMode.cs | 2 +- src/Ryujinx.Graphics.GAL/PolygonModeMask.cs | 4 +- src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs | 2 +- src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs | 2 +- src/Ryujinx.Graphics.GAL/ResourceLayout.cs | 38 ++++++++- src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs | 52 ++++++------ .../ScreenCaptureImageInfo.cs | 22 +++--- src/Ryujinx.Graphics.GAL/ShaderInfo.cs | 2 +- src/Ryujinx.Graphics.GAL/ShaderSource.cs | 2 +- src/Ryujinx.Graphics.GAL/StencilOp.cs | 14 ++-- .../StencilTestDescriptor.cs | 70 ++++++++-------- .../SupportBufferUpdater.cs | 7 +- src/Ryujinx.Graphics.GAL/SwizzleComponent.cs | 4 +- src/Ryujinx.Graphics.GAL/Target.cs | 4 +- src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs | 79 +++++++++++-------- src/Ryujinx.Graphics.GAL/UpscaleType.cs | 4 +- .../VertexBufferDescriptor.cs | 6 +- src/Ryujinx.Graphics.GAL/Viewport.cs | 26 +++--- src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs | 5 +- 161 files changed, 481 insertions(+), 439 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/AddressMode.cs b/src/Ryujinx.Graphics.GAL/AddressMode.cs index 153925b10..bf7d03938 100644 --- a/src/Ryujinx.Graphics.GAL/AddressMode.cs +++ b/src/Ryujinx.Graphics.GAL/AddressMode.cs @@ -9,6 +9,6 @@ namespace Ryujinx.Graphics.GAL Clamp, MirrorClampToEdge, MirrorClampToBorder, - MirrorClamp + MirrorClamp, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs index 1f1f7c3f1..e91d9caa4 100644 --- a/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs +++ b/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.GAL { - public struct AdvancedBlendDescriptor + public readonly struct AdvancedBlendDescriptor { public AdvancedBlendOp Op { get; } public AdvancedBlendOverlap Overlap { get; } diff --git a/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs index 4140bf497..2c7654f04 100644 --- a/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs +++ b/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs @@ -47,6 +47,6 @@ namespace Ryujinx.Graphics.GAL HslHue, HslSaturation, HslColor, - HslLuminosity + HslLuminosity, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs index d4feb2b30..abd957049 100644 --- a/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs +++ b/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.GAL { Uncorrelated, Disjoint, - Conjoint + Conjoint, } } diff --git a/src/Ryujinx.Graphics.GAL/AntiAliasing.cs b/src/Ryujinx.Graphics.GAL/AntiAliasing.cs index d4e5754d8..9f526909b 100644 --- a/src/Ryujinx.Graphics.GAL/AntiAliasing.cs +++ b/src/Ryujinx.Graphics.GAL/AntiAliasing.cs @@ -7,6 +7,6 @@ SmaaLow, SmaaMedium, SmaaHigh, - SmaaUltra + SmaaUltra, } } diff --git a/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs b/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs index 83b8afe24..d9cf3e49c 100644 --- a/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs +++ b/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs @@ -4,30 +4,30 @@ namespace Ryujinx.Graphics.GAL { public bool Enable { get; } - public ColorF BlendConstant { get; } - public BlendOp ColorOp { get; } + public ColorF BlendConstant { get; } + public BlendOp ColorOp { get; } public BlendFactor ColorSrcFactor { get; } public BlendFactor ColorDstFactor { get; } - public BlendOp AlphaOp { get; } + public BlendOp AlphaOp { get; } public BlendFactor AlphaSrcFactor { get; } public BlendFactor AlphaDstFactor { get; } public BlendDescriptor( - bool enable, - ColorF blendConstant, - BlendOp colorOp, + bool enable, + ColorF blendConstant, + BlendOp colorOp, BlendFactor colorSrcFactor, BlendFactor colorDstFactor, - BlendOp alphaOp, + BlendOp alphaOp, BlendFactor alphaSrcFactor, BlendFactor alphaDstFactor) { - Enable = enable; - BlendConstant = blendConstant; - ColorOp = colorOp; + Enable = enable; + BlendConstant = blendConstant; + ColorOp = colorOp; ColorSrcFactor = colorSrcFactor; ColorDstFactor = colorDstFactor; - AlphaOp = alphaOp; + AlphaOp = alphaOp; AlphaSrcFactor = alphaSrcFactor; AlphaDstFactor = alphaDstFactor; } diff --git a/src/Ryujinx.Graphics.GAL/BlendFactor.cs b/src/Ryujinx.Graphics.GAL/BlendFactor.cs index 4149ad514..1dba229d6 100644 --- a/src/Ryujinx.Graphics.GAL/BlendFactor.cs +++ b/src/Ryujinx.Graphics.GAL/BlendFactor.cs @@ -22,21 +22,21 @@ namespace Ryujinx.Graphics.GAL ConstantAlpha, OneMinusConstantAlpha, - ZeroGl = 0x4000, - OneGl = 0x4001, - SrcColorGl = 0x4300, - OneMinusSrcColorGl = 0x4301, - SrcAlphaGl = 0x4302, - OneMinusSrcAlphaGl = 0x4303, - DstAlphaGl = 0x4304, - OneMinusDstAlphaGl = 0x4305, - DstColorGl = 0x4306, - OneMinusDstColorGl = 0x4307, - SrcAlphaSaturateGl = 0x4308, - Src1ColorGl = 0xc900, - OneMinusSrc1ColorGl = 0xc901, - Src1AlphaGl = 0xc902, - OneMinusSrc1AlphaGl = 0xc903 + ZeroGl = 0x4000, + OneGl = 0x4001, + SrcColorGl = 0x4300, + OneMinusSrcColorGl = 0x4301, + SrcAlphaGl = 0x4302, + OneMinusSrcAlphaGl = 0x4303, + DstAlphaGl = 0x4304, + OneMinusDstAlphaGl = 0x4305, + DstColorGl = 0x4306, + OneMinusDstColorGl = 0x4307, + SrcAlphaSaturateGl = 0x4308, + Src1ColorGl = 0xc900, + OneMinusSrc1ColorGl = 0xc901, + Src1AlphaGl = 0xc902, + OneMinusSrc1AlphaGl = 0xc903, } public static class BlendFactorExtensions @@ -54,9 +54,9 @@ namespace Ryujinx.Graphics.GAL case BlendFactor.OneMinusSrc1Alpha: case BlendFactor.OneMinusSrc1AlphaGl: return true; + default: + return false; } - - return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/BlendOp.cs b/src/Ryujinx.Graphics.GAL/BlendOp.cs index b4a5a9309..c3b670730 100644 --- a/src/Ryujinx.Graphics.GAL/BlendOp.cs +++ b/src/Ryujinx.Graphics.GAL/BlendOp.cs @@ -8,10 +8,10 @@ namespace Ryujinx.Graphics.GAL Minimum, Maximum, - AddGl = 0x8006, - MinimumGl = 0x8007, - MaximumGl = 0x8008, - SubtractGl = 0x800a, - ReverseSubtractGl = 0x800b + AddGl = 0x8006, + MinimumGl = 0x8007, + MaximumGl = 0x8008, + SubtractGl = 0x800a, + ReverseSubtractGl = 0x800b, } } diff --git a/src/Ryujinx.Graphics.GAL/BufferAccess.cs b/src/Ryujinx.Graphics.GAL/BufferAccess.cs index 3a2d6c9b6..2f7d994fe 100644 --- a/src/Ryujinx.Graphics.GAL/BufferAccess.cs +++ b/src/Ryujinx.Graphics.GAL/BufferAccess.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.GAL public enum BufferAccess { Default, - FlushPersistent + FlushPersistent, } } diff --git a/src/Ryujinx.Graphics.GAL/BufferAssignment.cs b/src/Ryujinx.Graphics.GAL/BufferAssignment.cs index d803d90ba..d129135e2 100644 --- a/src/Ryujinx.Graphics.GAL/BufferAssignment.cs +++ b/src/Ryujinx.Graphics.GAL/BufferAssignment.cs @@ -11,4 +11,4 @@ Range = range; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/BufferHandle.cs b/src/Ryujinx.Graphics.GAL/BufferHandle.cs index 5ba50d195..a2adea56a 100644 --- a/src/Ryujinx.Graphics.GAL/BufferHandle.cs +++ b/src/Ryujinx.Graphics.GAL/BufferHandle.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.GAL { private readonly ulong _value; - public static BufferHandle Null => new BufferHandle(0); + public static BufferHandle Null => new(0); private BufferHandle(ulong value) => _value = value; } diff --git a/src/Ryujinx.Graphics.GAL/BufferRange.cs b/src/Ryujinx.Graphics.GAL/BufferRange.cs index ad9ebee48..483747f10 100644 --- a/src/Ryujinx.Graphics.GAL/BufferRange.cs +++ b/src/Ryujinx.Graphics.GAL/BufferRange.cs @@ -2,20 +2,20 @@ namespace Ryujinx.Graphics.GAL { public readonly struct BufferRange { - private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0); + private static readonly BufferRange _empty = new(BufferHandle.Null, 0, 0); public static BufferRange Empty => _empty; public BufferHandle Handle { get; } public int Offset { get; } - public int Size { get; } + public int Size { get; } public BufferRange(BufferHandle handle, int offset, int size) { Handle = handle; Offset = offset; - Size = size; + Size = size; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index ef2da0b77..3c49a7dcf 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -149,4 +149,4 @@ namespace Ryujinx.Graphics.GAL GatherBiasPrecision = gatherBiasPrecision; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/CompareMode.cs b/src/Ryujinx.Graphics.GAL/CompareMode.cs index 7a64d9bb9..42ab14d66 100644 --- a/src/Ryujinx.Graphics.GAL/CompareMode.cs +++ b/src/Ryujinx.Graphics.GAL/CompareMode.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.GAL public enum CompareMode { None, - CompareRToTexture + CompareRToTexture, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/CompareOp.cs b/src/Ryujinx.Graphics.GAL/CompareOp.cs index 358ed2b46..8141ecca9 100644 --- a/src/Ryujinx.Graphics.GAL/CompareOp.cs +++ b/src/Ryujinx.Graphics.GAL/CompareOp.cs @@ -11,13 +11,13 @@ namespace Ryujinx.Graphics.GAL GreaterOrEqual, Always, - NeverGl = 0x200, - LessGl = 0x201, - EqualGl = 0x202, - LessOrEqualGl = 0x203, - GreaterGl = 0x204, - NotEqualGl = 0x205, + NeverGl = 0x200, + LessGl = 0x201, + EqualGl = 0x202, + LessOrEqualGl = 0x203, + GreaterGl = 0x204, + NotEqualGl = 0x205, GreaterOrEqualGl = 0x206, - AlwaysGl = 0x207, + AlwaysGl = 0x207, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/CounterType.cs b/src/Ryujinx.Graphics.GAL/CounterType.cs index 9d7b3b981..58a4254ba 100644 --- a/src/Ryujinx.Graphics.GAL/CounterType.cs +++ b/src/Ryujinx.Graphics.GAL/CounterType.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.GAL { SamplesPassed, PrimitivesGenerated, - TransformFeedbackPrimitivesWritten + TransformFeedbackPrimitivesWritten, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/DepthMode.cs b/src/Ryujinx.Graphics.GAL/DepthMode.cs index aafbb65a6..52a3d9cf7 100644 --- a/src/Ryujinx.Graphics.GAL/DepthMode.cs +++ b/src/Ryujinx.Graphics.GAL/DepthMode.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.GAL public enum DepthMode { MinusOneToOne, - ZeroToOne + ZeroToOne, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs b/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs index e80d0d4b3..1031355ff 100644 --- a/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs +++ b/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.GAL public enum DepthStencilMode { Depth, - Stencil + Stencil, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs b/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs index 4c593392b..d0edb603f 100644 --- a/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs +++ b/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs @@ -2,19 +2,19 @@ namespace Ryujinx.Graphics.GAL { public readonly struct DepthTestDescriptor { - public bool TestEnable { get; } + public bool TestEnable { get; } public bool WriteEnable { get; } public CompareOp Func { get; } public DepthTestDescriptor( - bool testEnable, - bool writeEnable, + bool testEnable, + bool writeEnable, CompareOp func) { - TestEnable = testEnable; + TestEnable = testEnable; WriteEnable = writeEnable; - Func = func; + Func = func; } } } diff --git a/src/Ryujinx.Graphics.GAL/DeviceInfo.cs b/src/Ryujinx.Graphics.GAL/DeviceInfo.cs index eb4a016ff..04258460a 100644 --- a/src/Ryujinx.Graphics.GAL/DeviceInfo.cs +++ b/src/Ryujinx.Graphics.GAL/DeviceInfo.cs @@ -15,4 +15,4 @@ namespace Ryujinx.Graphics.GAL IsDiscrete = isDiscrete; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Extents2D.cs b/src/Ryujinx.Graphics.GAL/Extents2D.cs index bac44f83a..96fbc0f79 100644 --- a/src/Ryujinx.Graphics.GAL/Extents2D.cs +++ b/src/Ryujinx.Graphics.GAL/Extents2D.cs @@ -20,12 +20,12 @@ namespace Ryujinx.Graphics.GAL public Extents2D Reduce(int level) { int div = 1 << level; - + return new Extents2D( - X1 >> level, + X1 >> level, Y1 >> level, BitUtils.DivRoundUp(X2, div), BitUtils.DivRoundUp(Y2, div)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Extents2DF.cs b/src/Ryujinx.Graphics.GAL/Extents2DF.cs index 43f0e25e1..116e998e1 100644 --- a/src/Ryujinx.Graphics.GAL/Extents2DF.cs +++ b/src/Ryujinx.Graphics.GAL/Extents2DF.cs @@ -15,4 +15,4 @@ namespace Ryujinx.Graphics.GAL Y2 = y2; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Face.cs b/src/Ryujinx.Graphics.GAL/Face.cs index 0587641fa..73e43cf23 100644 --- a/src/Ryujinx.Graphics.GAL/Face.cs +++ b/src/Ryujinx.Graphics.GAL/Face.cs @@ -2,8 +2,8 @@ namespace Ryujinx.Graphics.GAL { public enum Face { - Front = 0x404, - Back = 0x405, - FrontAndBack = 0x408 + Front = 0x404, + Back = 0x405, + FrontAndBack = 0x408, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index 7e0e07d48..f6feec1c7 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.GAL B5G5R5A1Unorm, A1B5G5R5Unorm, B8G8R8A8Unorm, - B8G8R8A8Srgb + B8G8R8A8Srgb, } public static class FormatExtensions @@ -665,4 +665,4 @@ namespace Ryujinx.Graphics.GAL return format.IsUint() || format.IsSint(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/FrontFace.cs b/src/Ryujinx.Graphics.GAL/FrontFace.cs index aa6bfdc5b..5dade2766 100644 --- a/src/Ryujinx.Graphics.GAL/FrontFace.cs +++ b/src/Ryujinx.Graphics.GAL/FrontFace.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL { public enum FrontFace { - Clockwise = 0x900, - CounterClockwise = 0x901 + Clockwise = 0x900, + CounterClockwise = 0x901, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/ISampler.cs b/src/Ryujinx.Graphics.GAL/ISampler.cs index 3aefc6a78..de70f6636 100644 --- a/src/Ryujinx.Graphics.GAL/ISampler.cs +++ b/src/Ryujinx.Graphics.GAL/ISampler.cs @@ -3,4 +3,4 @@ using System; namespace Ryujinx.Graphics.GAL { public interface ISampler : IDisposable { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs index 2f9f5fbff..d39b59499 100644 --- a/src/Ryujinx.Graphics.GAL/ITexture.cs +++ b/src/Ryujinx.Graphics.GAL/ITexture.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Memory; -using System; namespace Ryujinx.Graphics.GAL { @@ -25,4 +24,4 @@ namespace Ryujinx.Graphics.GAL void SetStorage(BufferRange buffer); void Release(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/ImageCrop.cs b/src/Ryujinx.Graphics.GAL/ImageCrop.cs index e8220974b..511b74f67 100644 --- a/src/Ryujinx.Graphics.GAL/ImageCrop.cs +++ b/src/Ryujinx.Graphics.GAL/ImageCrop.cs @@ -2,36 +2,36 @@ namespace Ryujinx.Graphics.GAL { public readonly struct ImageCrop { - public int Left { get; } - public int Right { get; } - public int Top { get; } - public int Bottom { get; } - public bool FlipX { get; } - public bool FlipY { get; } - public bool IsStretched { get; } + public int Left { get; } + public int Right { get; } + public int Top { get; } + public int Bottom { get; } + public bool FlipX { get; } + public bool FlipY { get; } + public bool IsStretched { get; } public float AspectRatioX { get; } public float AspectRatioY { get; } public ImageCrop( - int left, - int right, - int top, - int bottom, - bool flipX, - bool flipY, - bool isStretched, + int left, + int right, + int top, + int bottom, + bool flipX, + bool flipY, + bool isStretched, float aspectRatioX, float aspectRatioY) { - Left = left; - Right = right; - Top = top; - Bottom = bottom; - FlipX = flipX; - FlipY = flipY; - IsStretched = isStretched; + Left = left; + Right = right; + Top = top; + Bottom = bottom; + FlipX = flipX; + FlipY = flipY; + IsStretched = isStretched; AspectRatioX = aspectRatioX; AspectRatioY = aspectRatioY; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/IndexType.cs b/src/Ryujinx.Graphics.GAL/IndexType.cs index 4abf28d9c..8bbefc831 100644 --- a/src/Ryujinx.Graphics.GAL/IndexType.cs +++ b/src/Ryujinx.Graphics.GAL/IndexType.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.GAL { UByte, UShort, - UInt + UInt, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/LogicalOp.cs b/src/Ryujinx.Graphics.GAL/LogicalOp.cs index 848215d0c..8fe6c782d 100644 --- a/src/Ryujinx.Graphics.GAL/LogicalOp.cs +++ b/src/Ryujinx.Graphics.GAL/LogicalOp.cs @@ -17,6 +17,6 @@ CopyInverted = 0x150C, OrInverted = 0x150D, Nand = 0x150E, - Set = 0x150F + Set = 0x150F, } } diff --git a/src/Ryujinx.Graphics.GAL/MagFilter.cs b/src/Ryujinx.Graphics.GAL/MagFilter.cs index f20d095e7..d5cd6c7c2 100644 --- a/src/Ryujinx.Graphics.GAL/MagFilter.cs +++ b/src/Ryujinx.Graphics.GAL/MagFilter.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.GAL public enum MagFilter { Nearest = 1, - Linear + Linear, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/MinFilter.cs b/src/Ryujinx.Graphics.GAL/MinFilter.cs index b7a0740be..f089947bd 100644 --- a/src/Ryujinx.Graphics.GAL/MinFilter.cs +++ b/src/Ryujinx.Graphics.GAL/MinFilter.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Graphics.GAL NearestMipmapNearest, LinearMipmapNearest, NearestMipmapLinear, - LinearMipmapLinear + LinearMipmapLinear, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs b/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs index 24b0af2d7..c30df0465 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs @@ -15,9 +15,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading { private ulong _bufferHandle = 0; - private Dictionary<BufferHandle, BufferHandle> _bufferMap = new Dictionary<BufferHandle, BufferHandle>(); - private HashSet<BufferHandle> _inFlight = new HashSet<BufferHandle>(); - private AutoResetEvent _inFlightChanged = new AutoResetEvent(false); + private readonly Dictionary<BufferHandle, BufferHandle> _bufferMap = new(); + private readonly HashSet<BufferHandle> _inFlight = new(); + private readonly AutoResetEvent _inFlightChanged = new(false); internal BufferHandle CreateBufferHandle() { @@ -59,14 +59,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading internal BufferHandle MapBuffer(BufferHandle handle) { // Maps a threaded buffer to a backend one. - // Threaded buffers are returned on creation as the buffer + // Threaded buffers are returned on creation as the buffer // isn't actually created until the queue runs the command. - BufferHandle result; - lock (_bufferMap) { - if (!_bufferMap.TryGetValue(handle, out result)) + if (!_bufferMap.TryGetValue(handle, out BufferHandle result)) { result = BufferHandle.Null; } @@ -79,11 +77,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading { // Blocks until the handle is available. - BufferHandle result; lock (_bufferMap) { - if (_bufferMap.TryGetValue(handle, out result)) + if (_bufferMap.TryGetValue(handle, out BufferHandle result)) { return result; } @@ -128,9 +125,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading for (int i = 0; i < ranges.Length; i++) { ref BufferRange range = ref ranges[i]; - BufferHandle result; - if (!_bufferMap.TryGetValue(range.Handle, out result)) + if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result)) { result = BufferHandle.Null; } @@ -152,9 +148,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading { ref BufferAssignment assignment = ref ranges[i]; BufferRange range = assignment.Range; - BufferHandle result; - if (!_bufferMap.TryGetValue(range.Handle, out result)) + if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result)) { result = BufferHandle.Null; } @@ -175,9 +170,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading for (int i = 0; i < ranges.Length; i++) { BufferRange range = ranges[i].Buffer; - BufferHandle result; - if (!_bufferMap.TryGetValue(range.Handle, out result)) + if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result)) { result = BufferHandle.Null; } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs index 9f6e483cd..12c5245a5 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -17,8 +17,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading { private delegate void CommandDelegate(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer); - private static int _totalCommands = (int)Enum.GetValues<CommandType>().Max() + 1; - private static CommandDelegate[] _lookup = new CommandDelegate[_totalCommands]; + private static readonly int _totalCommands = (int)Enum.GetValues<CommandType>().Max() + 1; + private static readonly CommandDelegate[] _lookup = new CommandDelegate[_totalCommands]; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ref T GetCommand<T>(Span<byte> memory) @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void RunCommand(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) { - _lookup[memory[memory.Length - 1]](memory, threaded, renderer); + _lookup[memory[^1]](memory, threaded, renderer); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs index 8d9c1ec8d..1e73fba62 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -100,6 +100,6 @@ TextureBarrierTiled, TryHostConditionalRendering, TryHostConditionalRenderingFlush, - UpdateRenderScale + UpdateRenderScale, } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs index 4f8e1b088..4a73a3bc7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs @@ -2,7 +2,7 @@ { struct BarrierCommand : IGALCommand, IGALCommand<BarrierCommand> { - public CommandType CommandType => CommandType.Barrier; + public readonly CommandType CommandType => CommandType.Barrier; public static void Run(ref BarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs index 500326352..538ff98d7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs @@ -2,7 +2,7 @@ { struct BeginTransformFeedbackCommand : IGALCommand, IGALCommand<BeginTransformFeedbackCommand> { - public CommandType CommandType => CommandType.BeginTransformFeedback; + public readonly CommandType CommandType => CommandType.BeginTransformFeedback; private PrimitiveTopology _topology; public void Set(PrimitiveTopology topology) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs index 5be42fff6..03ee8560f 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs @@ -2,7 +2,7 @@ { struct BufferDisposeCommand : IGALCommand, IGALCommand<BufferDisposeCommand> { - public CommandType CommandType => CommandType.BufferDispose; + public readonly CommandType CommandType => CommandType.BufferDispose; private BufferHandle _buffer; public void Set(BufferHandle buffer) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs index 031c6153e..0e3950229 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs @@ -1,11 +1,10 @@ using Ryujinx.Graphics.GAL.Multithreading.Model; -using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer { struct BufferGetDataCommand : IGALCommand, IGALCommand<BufferGetDataCommand> { - public CommandType CommandType => CommandType.BufferGetData; + public readonly CommandType CommandType => CommandType.BufferGetData; private BufferHandle _buffer; private int _offset; private int _size; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs index dcb8c2f21..30daf382a 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer { struct BufferSetDataCommand : IGALCommand, IGALCommand<BufferSetDataCommand> { - public CommandType CommandType => CommandType.BufferSetData; + public readonly CommandType CommandType => CommandType.BufferSetData; private BufferHandle _buffer; private int _offset; private SpanRef<byte> _data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs index 1d70460ab..777a8460b 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs @@ -2,7 +2,7 @@ { struct ClearBufferCommand : IGALCommand, IGALCommand<ClearBufferCommand> { - public CommandType CommandType => CommandType.ClearBuffer; + public readonly CommandType CommandType => CommandType.ClearBuffer; private BufferHandle _destination; private int _offset; private int _size; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs index f8c2bdfe6..3a24e181a 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs @@ -2,7 +2,7 @@ { struct ClearRenderTargetColorCommand : IGALCommand, IGALCommand<ClearRenderTargetColorCommand> { - public CommandType CommandType => CommandType.ClearRenderTargetColor; + public readonly CommandType CommandType => CommandType.ClearRenderTargetColor; private int _index; private int _layer; private int _layerCount; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs index ca86673ed..43c0e89a4 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs @@ -2,7 +2,7 @@ { struct ClearRenderTargetDepthStencilCommand : IGALCommand, IGALCommand<ClearRenderTargetDepthStencilCommand> { - public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; + public readonly CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; private int _layer; private int _layerCount; private float _depthValue; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs index ad3ab0f84..975562333 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs @@ -2,7 +2,7 @@ { struct CommandBufferBarrierCommand : IGALCommand, IGALCommand<CommandBufferBarrierCommand> { - public CommandType CommandType => CommandType.CommandBufferBarrier; + public readonly CommandType CommandType => CommandType.CommandBufferBarrier; public static void Run(ref CommandBufferBarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs index 43111bce6..8cd9e7f10 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs @@ -2,7 +2,7 @@ { struct CopyBufferCommand : IGALCommand, IGALCommand<CopyBufferCommand> { - public CommandType CommandType => CommandType.CopyBuffer; + public readonly CommandType CommandType => CommandType.CopyBuffer; private BufferHandle _source; private BufferHandle _destination; private int _srcOffset; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs index e5250212e..fd9868b14 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent { struct CounterEventDisposeCommand : IGALCommand, IGALCommand<CounterEventDisposeCommand> { - public CommandType CommandType => CommandType.CounterEventDispose; + public readonly CommandType CommandType => CommandType.CounterEventDispose; private TableRef<ThreadedCounterEvent> _event; public void Set(TableRef<ThreadedCounterEvent> evt) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs index 608cf8f9d..590169bc4 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent { struct CounterEventFlushCommand : IGALCommand, IGALCommand<CounterEventFlushCommand> { - public CommandType CommandType => CommandType.CounterEventFlush; + public readonly CommandType CommandType => CommandType.CounterEventFlush; private TableRef<ThreadedCounterEvent> _event; public void Set(TableRef<ThreadedCounterEvent> evt) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs index 29568837c..1a7c94f4a 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs @@ -2,7 +2,7 @@ { struct DispatchComputeCommand : IGALCommand, IGALCommand<DispatchComputeCommand> { - public CommandType CommandType => CommandType.DispatchCompute; + public readonly CommandType CommandType => CommandType.DispatchCompute; private int _groupsX; private int _groupsY; private int _groupsZ; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs index 804eaa497..bd3cb89b7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs @@ -2,7 +2,7 @@ { struct DrawIndexedCommand : IGALCommand, IGALCommand<DrawIndexedCommand> { - public CommandType CommandType => CommandType.DrawIndexed; + public readonly CommandType CommandType => CommandType.DrawIndexed; private int _indexCount; private int _instanceCount; private int _firstIndex; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs index 1b28afcd3..c4d5ce661 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs @@ -2,7 +2,7 @@ { struct DrawCommand : IGALCommand, IGALCommand<DrawCommand> { - public CommandType CommandType => CommandType.Draw; + public readonly CommandType CommandType => CommandType.Draw; private int _vertexCount; private int _instanceCount; private int _firstVertex; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs index 521b2f0c2..e4156e2be 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct DrawIndexedIndirectCommand : IGALCommand, IGALCommand<DrawIndexedIndirectCommand> { - public CommandType CommandType => CommandType.DrawIndexedIndirect; + public readonly CommandType CommandType => CommandType.DrawIndexedIndirect; private BufferRange _indirectBuffer; public void Set(BufferRange indirectBuffer) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs index 6bdf376d0..1f6966226 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs @@ -2,7 +2,7 @@ { struct DrawIndexedIndirectCountCommand : IGALCommand, IGALCommand<DrawIndexedIndirectCountCommand> { - public CommandType CommandType => CommandType.DrawIndexedIndirectCount; + public readonly CommandType CommandType => CommandType.DrawIndexedIndirectCount; private BufferRange _indirectBuffer; private BufferRange _parameterBuffer; private int _maxDrawCount; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs index e1947084c..e2e93d3c4 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct DrawIndirectCommand : IGALCommand, IGALCommand<DrawIndirectCommand> { - public CommandType CommandType => CommandType.DrawIndirect; + public readonly CommandType CommandType => CommandType.DrawIndirect; private BufferRange _indirectBuffer; public void Set(BufferRange indirectBuffer) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs index ef56ffb26..eae98e4bd 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs @@ -2,7 +2,7 @@ { struct DrawIndirectCountCommand : IGALCommand, IGALCommand<DrawIndirectCountCommand> { - public CommandType CommandType => CommandType.DrawIndirectCount; + public readonly CommandType CommandType => CommandType.DrawIndirectCount; private BufferRange _indirectBuffer; private BufferRange _parameterBuffer; private int _maxDrawCount; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs index b3e9c4b5b..1e9cfd837 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct DrawTextureCommand : IGALCommand, IGALCommand<DrawTextureCommand> { - public CommandType CommandType => CommandType.DrawTexture; + public readonly CommandType CommandType => CommandType.DrawTexture; private TableRef<ITexture> _texture; private TableRef<ISampler> _sampler; private Extents2DF _srcRegion; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs index 877af23bd..5d30a901d 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs @@ -2,7 +2,7 @@ { struct EndHostConditionalRenderingCommand : IGALCommand, IGALCommand<EndHostConditionalRenderingCommand> { - public CommandType CommandType => CommandType.EndHostConditionalRendering; + public readonly CommandType CommandType => CommandType.EndHostConditionalRendering; public static void Run(ref EndHostConditionalRenderingCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs index 33df325fd..d050f477a 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs @@ -2,7 +2,7 @@ { struct EndTransformFeedbackCommand : IGALCommand, IGALCommand<EndTransformFeedbackCommand> { - public CommandType CommandType => CommandType.EndTransformFeedback; + public readonly CommandType CommandType => CommandType.EndTransformFeedback; public static void Run(ref EndTransformFeedbackCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs index f36624240..b9c6e2866 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program { struct ProgramCheckLinkCommand : IGALCommand, IGALCommand<ProgramCheckLinkCommand> { - public CommandType CommandType => CommandType.ProgramCheckLink; + public readonly CommandType CommandType => CommandType.ProgramCheckLink; private TableRef<ThreadedProgram> _program; private bool _blocking; private TableRef<ResultBox<ProgramLinkStatus>> _result; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs index d1ec42985..839a05bc7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program { struct ProgramDisposeCommand : IGALCommand, IGALCommand<ProgramDisposeCommand> { - public CommandType CommandType => CommandType.ProgramDispose; + public readonly CommandType CommandType => CommandType.ProgramDispose; private TableRef<ThreadedProgram> _program; public void Set(TableRef<ThreadedProgram> program) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs index 16963245f..b53fc8ac4 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program { struct ProgramGetBinaryCommand : IGALCommand, IGALCommand<ProgramGetBinaryCommand> { - public CommandType CommandType => CommandType.ProgramGetBinary; + public readonly CommandType CommandType => CommandType.ProgramGetBinary; private TableRef<ThreadedProgram> _program; private TableRef<ResultBox<byte[]>> _result; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs index 41987da1e..309a9cd0c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { struct ActionCommand : IGALCommand, IGALCommand<ActionCommand> { - public CommandType CommandType => CommandType.Action; + public readonly CommandType CommandType => CommandType.Action; private TableRef<Action> _action; public void Set(TableRef<Action> action) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs index ece98b70f..5efa0a7af 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferAccessCommand.cs @@ -2,7 +2,7 @@ { struct CreateBufferAccessCommand : IGALCommand, IGALCommand<CreateBufferAccessCommand> { - public CommandType CommandType => CommandType.CreateBufferAccess; + public readonly CommandType CommandType => CommandType.CreateBufferAccess; private BufferHandle _threadedHandle; private int _size; private BufferAccess _access; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs index b36d8bbed..214b3589d 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs @@ -2,7 +2,7 @@ { struct CreateBufferCommand : IGALCommand, IGALCommand<CreateBufferCommand> { - public CommandType CommandType => CommandType.CreateBuffer; + public readonly CommandType CommandType => CommandType.CreateBuffer; private BufferHandle _threadedHandle; private int _size; private BufferHandle _storageHint; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs index e25f84687..1036e997e 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateHostBufferCommand.cs @@ -2,7 +2,7 @@ { struct CreateHostBufferCommand : IGALCommand, IGALCommand<CreateHostBufferCommand> { - public CommandType CommandType => CommandType.CreateHostBuffer; + public readonly CommandType CommandType => CommandType.CreateHostBuffer; private BufferHandle _threadedHandle; private nint _pointer; private int _size; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs index 19563e124..b0d24b99c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { struct CreateProgramCommand : IGALCommand, IGALCommand<CreateProgramCommand> { - public CommandType CommandType => CommandType.CreateProgram; + public readonly CommandType CommandType => CommandType.CreateProgram; private TableRef<IProgramRequest> _request; public void Set(TableRef<IProgramRequest> request) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs index 6ab862d44..761882688 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { struct CreateSamplerCommand : IGALCommand, IGALCommand<CreateSamplerCommand> { - public CommandType CommandType => CommandType.CreateSampler; + public readonly CommandType CommandType => CommandType.CreateSampler; private TableRef<ThreadedSampler> _sampler; private SamplerCreateInfo _info; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs index 32afb051e..138a45915 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs @@ -2,7 +2,7 @@ { struct CreateSyncCommand : IGALCommand, IGALCommand<CreateSyncCommand> { - public CommandType CommandType => CommandType.CreateSync; + public readonly CommandType CommandType => CommandType.CreateSync; private ulong _id; private bool _strict; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs index 0347ded46..f52db30ae 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { struct CreateTextureCommand : IGALCommand, IGALCommand<CreateTextureCommand> { - public CommandType CommandType => CommandType.CreateTexture; + public readonly CommandType CommandType => CommandType.CreateTexture; private TableRef<ThreadedTexture> _texture; private TextureCreateInfo _info; private float _scale; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs index 4111dcfd4..ade13ddfa 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { struct GetCapabilitiesCommand : IGALCommand, IGALCommand<GetCapabilitiesCommand> { - public CommandType CommandType => CommandType.GetCapabilities; + public readonly CommandType CommandType => CommandType.GetCapabilities; private TableRef<ResultBox<Capabilities>> _result; public void Set(TableRef<ResultBox<Capabilities>> result) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs index 820908f39..cf26194b7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs @@ -2,7 +2,7 @@ { struct PreFrameCommand : IGALCommand, IGALCommand<PreFrameCommand> { - public CommandType CommandType => CommandType.PreFrame; + public readonly CommandType CommandType => CommandType.PreFrame; public static void Run(ref PreFrameCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs index 4b0210cbf..2a373f5b7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { struct ReportCounterCommand : IGALCommand, IGALCommand<ReportCounterCommand> { - public CommandType CommandType => CommandType.ReportCounter; + public readonly CommandType CommandType => CommandType.ReportCounter; private TableRef<ThreadedCounterEvent> _event; private CounterType _type; private TableRef<EventHandler<ulong>> _resultHandler; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs index 3d7960417..a20a6ca2e 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs @@ -2,7 +2,7 @@ { struct ResetCounterCommand : IGALCommand, IGALCommand<ResetCounterCommand> { - public CommandType CommandType => CommandType.ResetCounter; + public readonly CommandType CommandType => CommandType.ResetCounter; private CounterType _type; public void Set(CounterType type) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs index c7076c0ec..e04304c11 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs @@ -2,7 +2,7 @@ { struct UpdateCountersCommand : IGALCommand, IGALCommand<UpdateCountersCommand> { - public CommandType CommandType => CommandType.UpdateCounters; + public readonly CommandType CommandType => CommandType.UpdateCounters; public static void Run(ref UpdateCountersCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs index 9485e9a10..4a68a35d2 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler { struct SamplerDisposeCommand : IGALCommand, IGALCommand<SamplerDisposeCommand> { - public CommandType CommandType => CommandType.SamplerDispose; + public readonly CommandType CommandType => CommandType.SamplerDispose; private TableRef<ThreadedSampler> _sampler; public void Set(TableRef<ThreadedSampler> sampler) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs index a96879ffa..10bd3c5cd 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs @@ -2,7 +2,7 @@ { struct SetAlphaTestCommand : IGALCommand, IGALCommand<SetAlphaTestCommand> { - public CommandType CommandType => CommandType.SetAlphaTest; + public readonly CommandType CommandType => CommandType.SetAlphaTest; private bool _enable; private float _reference; private CompareOp _op; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs index 2ec10a503..8405a8ee2 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetBlendStateAdvancedCommand : IGALCommand, IGALCommand<SetBlendStateAdvancedCommand> { - public CommandType CommandType => CommandType.SetBlendStateAdvanced; + public readonly CommandType CommandType => CommandType.SetBlendStateAdvanced; private AdvancedBlendDescriptor _blend; public void Set(AdvancedBlendDescriptor blend) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs index 68e48da57..de430b782 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs @@ -2,7 +2,7 @@ { struct SetBlendStateCommand : IGALCommand, IGALCommand<SetBlendStateCommand> { - public CommandType CommandType => CommandType.SetBlendState; + public readonly CommandType CommandType => CommandType.SetBlendState; private int _index; private BlendDescriptor _blend; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs index eb8d4a72a..c8bb8472f 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs @@ -2,7 +2,7 @@ { struct SetDepthBiasCommand : IGALCommand, IGALCommand<SetDepthBiasCommand> { - public CommandType CommandType => CommandType.SetDepthBias; + public readonly CommandType CommandType => CommandType.SetDepthBias; private PolygonModeMask _enables; private float _factor; private float _units; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs index 15159cb44..b7cd2db99 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs @@ -2,7 +2,7 @@ { struct SetDepthClampCommand : IGALCommand, IGALCommand<SetDepthClampCommand> { - public CommandType CommandType => CommandType.SetDepthClamp; + public readonly CommandType CommandType => CommandType.SetDepthClamp; private bool _clamp; public void Set(bool clamp) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs index 3e1691641..94bb6d490 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs @@ -2,7 +2,7 @@ { struct SetDepthModeCommand : IGALCommand, IGALCommand<SetDepthModeCommand> { - public CommandType CommandType => CommandType.SetDepthMode; + public readonly CommandType CommandType => CommandType.SetDepthMode; private DepthMode _mode; public void Set(DepthMode mode) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs index 2abaeb787..1b316c1af 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs @@ -2,7 +2,7 @@ { struct SetDepthTestCommand : IGALCommand, IGALCommand<SetDepthTestCommand> { - public CommandType CommandType => CommandType.SetDepthTest; + public readonly CommandType CommandType => CommandType.SetDepthTest; private DepthTestDescriptor _depthTest; public void Set(DepthTestDescriptor depthTest) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs index 54311e95c..7823ab054 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs @@ -2,7 +2,7 @@ { struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand> { - public CommandType CommandType => CommandType.SetFaceCulling; + public readonly CommandType CommandType => CommandType.SetFaceCulling; private bool _enable; private Face _face; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs index e4d7b8147..5d4694477 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs @@ -2,7 +2,7 @@ { struct SetFrontFaceCommand : IGALCommand, IGALCommand<SetFrontFaceCommand> { - public CommandType CommandType => CommandType.SetFrontFace; + public readonly CommandType CommandType => CommandType.SetFrontFace; private FrontFace _frontFace; public void Set(FrontFace frontFace) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs index 7836acd7e..d24174fea 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand> { - public CommandType CommandType => CommandType.SetImage; + public readonly CommandType CommandType => CommandType.SetImage; private int _binding; private TableRef<ITexture> _texture; private Format _imageFormat; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs index ded44c552..6a06ab9b3 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs @@ -2,7 +2,7 @@ { struct SetIndexBufferCommand : IGALCommand, IGALCommand<SetIndexBufferCommand> { - public CommandType CommandType => CommandType.SetIndexBuffer; + public readonly CommandType CommandType => CommandType.SetIndexBuffer; private BufferRange _buffer; private IndexType _type; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs index 683319323..4928360af 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs @@ -2,7 +2,7 @@ { struct SetLineParametersCommand : IGALCommand, IGALCommand<SetLineParametersCommand> { - public CommandType CommandType => CommandType.SetLineParameters; + public readonly CommandType CommandType => CommandType.SetLineParameters; private float _width; private bool _smooth; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs index 2d7fc1698..5aecc7773 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs @@ -2,7 +2,7 @@ { struct SetLogicOpStateCommand : IGALCommand, IGALCommand<SetLogicOpStateCommand> { - public CommandType CommandType => CommandType.SetLogicOpState; + public readonly CommandType CommandType => CommandType.SetLogicOpState; private bool _enable; private LogicalOp _op; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs index f7b4969a5..f30140c74 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetMultisampleStateCommand : IGALCommand, IGALCommand<SetMultisampleStateCommand> { - public CommandType CommandType => CommandType.SetMultisampleState; + public readonly CommandType CommandType => CommandType.SetMultisampleState; private MultisampleDescriptor _multisample; public void Set(MultisampleDescriptor multisample) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs index 815bc3c22..6dbe4b7be 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetPatchParametersCommand : IGALCommand, IGALCommand<SetPatchParametersCommand> { - public CommandType CommandType => CommandType.SetPatchParameters; + public readonly CommandType CommandType => CommandType.SetPatchParameters; private int _vertices; private Array4<float> _defaultOuterLevel; private Array2<float> _defaultInnerLevel; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs index e3fad0f82..a0993fb25 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs @@ -2,7 +2,7 @@ { struct SetPointParametersCommand : IGALCommand, IGALCommand<SetPointParametersCommand> { - public CommandType CommandType => CommandType.SetPointParameters; + public readonly CommandType CommandType => CommandType.SetPointParameters; private float _size; private bool _isProgramPointSize; private bool _enablePointSprite; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs index ea2f838b9..caa362c06 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetPolygonModeCommand : IGALCommand, IGALCommand<SetPolygonModeCommand> { - public CommandType CommandType => CommandType.SetPolygonMode; + public readonly CommandType CommandType => CommandType.SetPolygonMode; private PolygonMode _frontMode; private PolygonMode _backMode; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs index 26b88b01d..2289d8bdb 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs @@ -2,7 +2,7 @@ { struct SetPrimitiveRestartCommand : IGALCommand, IGALCommand<SetPrimitiveRestartCommand> { - public CommandType CommandType => CommandType.SetPrimitiveRestart; + public readonly CommandType CommandType => CommandType.SetPrimitiveRestart; private bool _enable; private int _index; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs index 062c4e57c..1458a1fc7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs @@ -2,7 +2,7 @@ { struct SetPrimitiveTopologyCommand : IGALCommand, IGALCommand<SetPrimitiveTopologyCommand> { - public CommandType CommandType => CommandType.SetPrimitiveTopology; + public readonly CommandType CommandType => CommandType.SetPrimitiveTopology; private PrimitiveTopology _topology; public void Set(PrimitiveTopology topology) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs index fa2e9a8a5..5b0114330 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetProgramCommand : IGALCommand, IGALCommand<SetProgramCommand> { - public CommandType CommandType => CommandType.SetProgram; + public readonly CommandType CommandType => CommandType.SetProgram; private TableRef<IProgram> _program; public void Set(TableRef<IProgram> program) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs index d2095a4f1..640911009 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs @@ -2,7 +2,7 @@ { struct SetRasterizerDiscardCommand : IGALCommand, IGALCommand<SetRasterizerDiscardCommand> { - public CommandType CommandType => CommandType.SetRasterizerDiscard; + public readonly CommandType CommandType => CommandType.SetRasterizerDiscard; private bool _discard; public void Set(bool discard) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs index c247ff3af..78d357aec 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetRenderTargetColorMasksCommand : IGALCommand, IGALCommand<SetRenderTargetColorMasksCommand> { - public CommandType CommandType => CommandType.SetRenderTargetColorMasks; + public readonly CommandType CommandType => CommandType.SetRenderTargetColorMasks; private SpanRef<uint> _componentMask; public void Set(SpanRef<uint> componentMask) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs index 7cb5ec114..7207fd9da 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs @@ -2,7 +2,7 @@ { struct SetRenderTargetScaleCommand : IGALCommand, IGALCommand<SetRenderTargetScaleCommand> { - public CommandType CommandType => CommandType.SetRenderTargetScale; + public readonly CommandType CommandType => CommandType.SetRenderTargetScale; private float _scale; public void Set(float scale) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs index 0b175a72e..13ef32b5b 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand> { - public CommandType CommandType => CommandType.SetRenderTargets; + public readonly CommandType CommandType => CommandType.SetRenderTargets; private TableRef<ITexture[]> _colors; private TableRef<ITexture> _depthStencil; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs index 985d775e0..9d7d59e24 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetScissorsCommand : IGALCommand, IGALCommand<SetScissorsCommand> { - public CommandType CommandType => CommandType.SetScissor; + public readonly CommandType CommandType => CommandType.SetScissor; private SpanRef<Rectangle<int>> _scissors; public void Set(SpanRef<Rectangle<int>> scissors) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs index 41bff97e3..23bea88b8 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs @@ -2,7 +2,7 @@ { struct SetStencilTestCommand : IGALCommand, IGALCommand<SetStencilTestCommand> { - public CommandType CommandType => CommandType.SetStencilTest; + public readonly CommandType CommandType => CommandType.SetStencilTest; private StencilTestDescriptor _stencilTest; public void Set(StencilTestDescriptor stencilTest) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs index 6ecb0989b..525576c64 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetStorageBuffersCommand : IGALCommand, IGALCommand<SetStorageBuffersCommand> { - public CommandType CommandType => CommandType.SetStorageBuffers; + public readonly CommandType CommandType => CommandType.SetStorageBuffers; private SpanRef<BufferAssignment> _buffers; public void Set(SpanRef<BufferAssignment> buffers) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs index 5e8e08544..673da496a 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetTextureAndSamplerCommand : IGALCommand, IGALCommand<SetTextureAndSamplerCommand> { - public CommandType CommandType => CommandType.SetTextureAndSampler; + public readonly CommandType CommandType => CommandType.SetTextureAndSampler; private ShaderStage _stage; private int _binding; private TableRef<ITexture> _texture; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs index e0d4ef2d2..cac837d91 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetTransformFeedbackBuffersCommand : IGALCommand, IGALCommand<SetTransformFeedbackBuffersCommand> { - public CommandType CommandType => CommandType.SetTransformFeedbackBuffers; + public readonly CommandType CommandType => CommandType.SetTransformFeedbackBuffers; private SpanRef<BufferRange> _buffers; public void Set(SpanRef<BufferRange> buffers) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs index 9e93db9ee..516a2bd3f 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetUniformBuffersCommand : IGALCommand, IGALCommand<SetUniformBuffersCommand> { - public CommandType CommandType => CommandType.SetUniformBuffers; + public readonly CommandType CommandType => CommandType.SetUniformBuffers; private SpanRef<BufferAssignment> _buffers; public void Set(SpanRef<BufferAssignment> buffers) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs index 4336ce49f..d7500900c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs @@ -2,7 +2,7 @@ { struct SetUserClipDistanceCommand : IGALCommand, IGALCommand<SetUserClipDistanceCommand> { - public CommandType CommandType => CommandType.SetUserClipDistance; + public readonly CommandType CommandType => CommandType.SetUserClipDistance; private int _index; private bool _enableClip; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs index e442c72d1..18decb0b2 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetVertexAttribsCommand : IGALCommand, IGALCommand<SetVertexAttribsCommand> { - public CommandType CommandType => CommandType.SetVertexAttribs; + public readonly CommandType CommandType => CommandType.SetVertexAttribs; private SpanRef<VertexAttribDescriptor> _vertexAttribs; public void Set(SpanRef<VertexAttribDescriptor> vertexAttribs) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs index 585da2a4a..bcfb553d8 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetVertexBuffersCommand : IGALCommand, IGALCommand<SetVertexBuffersCommand> { - public CommandType CommandType => CommandType.SetVertexBuffers; + public readonly CommandType CommandType => CommandType.SetVertexBuffers; private SpanRef<VertexBufferDescriptor> _vertexBuffers; public void Set(SpanRef<VertexBufferDescriptor> vertexBuffers) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs index c18bd811e..a0ad026a0 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct SetViewportsCommand : IGALCommand, IGALCommand<SetViewportsCommand> { - public CommandType CommandType => CommandType.SetViewports; + public readonly CommandType CommandType => CommandType.SetViewports; private SpanRef<Viewport> _viewports; private bool _disableTransform; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs index ac0e07d67..d22cc9b71 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToBufferCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureCopyToBufferCommand : IGALCommand, IGALCommand<TextureCopyToBufferCommand> { - public CommandType CommandType => CommandType.TextureCopyToBuffer; + public readonly CommandType CommandType => CommandType.TextureCopyToBuffer; private TableRef<ThreadedTexture> _texture; private BufferRange _range; private int _layer; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs index 02d0b6396..ddbf0e679 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureCopyToCommand : IGALCommand, IGALCommand<TextureCopyToCommand> { - public CommandType CommandType => CommandType.TextureCopyTo; + public readonly CommandType CommandType => CommandType.TextureCopyTo; private TableRef<ThreadedTexture> _texture; private TableRef<ThreadedTexture> _destination; private int _firstLayer; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs index 6b83d3f80..b43ffea56 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureCopyToScaledCommand : IGALCommand, IGALCommand<TextureCopyToScaledCommand> { - public CommandType CommandType => CommandType.TextureCopyToScaled; + public readonly CommandType CommandType => CommandType.TextureCopyToScaled; private TableRef<ThreadedTexture> _texture; private TableRef<ThreadedTexture> _destination; private Extents2D _srcRegion; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs index 2a340a704..4f5ab36f9 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureCopyToSliceCommand : IGALCommand, IGALCommand<TextureCopyToSliceCommand> { - public CommandType CommandType => CommandType.TextureCopyToSlice; + public readonly CommandType CommandType => CommandType.TextureCopyToSlice; private TableRef<ThreadedTexture> _texture; private TableRef<ThreadedTexture> _destination; private int _srcLayer; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs index 09e9ca2f7..9216e9689 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureCreateViewCommand : IGALCommand, IGALCommand<TextureCreateViewCommand> { - public CommandType CommandType => CommandType.TextureCreateView; + public readonly CommandType CommandType => CommandType.TextureCreateView; private TableRef<ThreadedTexture> _texture; private TableRef<ThreadedTexture> _destination; private TextureCreateInfo _info; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs index 91320d455..38010467e 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs @@ -1,12 +1,11 @@ using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; -using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureGetDataCommand : IGALCommand, IGALCommand<TextureGetDataCommand> { - public CommandType CommandType => CommandType.TextureGetData; + public readonly CommandType CommandType => CommandType.TextureGetData; private TableRef<ThreadedTexture> _texture; private TableRef<ResultBox<PinnedSpan<byte>>> _result; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs index ec06cc4dd..e84cf2d2b 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs @@ -1,12 +1,11 @@ using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; -using System; namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureGetDataSliceCommand : IGALCommand, IGALCommand<TextureGetDataSliceCommand> { - public CommandType CommandType => CommandType.TextureGetDataSlice; + public readonly CommandType CommandType => CommandType.TextureGetDataSlice; private TableRef<ThreadedTexture> _texture; private TableRef<ResultBox<PinnedSpan<byte>>> _result; private int _layer; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs index 61486e091..a9c528aee 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureReleaseCommand : IGALCommand, IGALCommand<TextureReleaseCommand> { - public CommandType CommandType => CommandType.TextureRelease; + public readonly CommandType CommandType => CommandType.TextureRelease; private TableRef<ThreadedTexture> _texture; public void Set(TableRef<ThreadedTexture> texture) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs index cfbaffd3e..9aa2e4eef 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureSetDataCommand : IGALCommand, IGALCommand<TextureSetDataCommand> { - public CommandType CommandType => CommandType.TextureSetData; + public readonly CommandType CommandType => CommandType.TextureSetData; private TableRef<ThreadedTexture> _texture; private TableRef<byte[]> _data; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs index a7126f617..14fecadfa 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureSetDataSliceCommand : IGALCommand, IGALCommand<TextureSetDataSliceCommand> { - public CommandType CommandType => CommandType.TextureSetDataSlice; + public readonly CommandType CommandType => CommandType.TextureSetDataSlice; private TableRef<ThreadedTexture> _texture; private TableRef<byte[]> _data; private int _layer; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs index 4df83e084..4c80d9bc3 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureSetDataSliceRegionCommand : IGALCommand, IGALCommand<TextureSetDataSliceRegionCommand> { - public CommandType CommandType => CommandType.TextureSetDataSliceRegion; + public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion; private TableRef<ThreadedTexture> _texture; private TableRef<byte[]> _data; private int _layer; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs index 2a1943a9c..fafaa5578 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture { struct TextureSetStorageCommand : IGALCommand, IGALCommand<TextureSetStorageCommand> { - public CommandType CommandType => CommandType.TextureSetStorage; + public readonly CommandType CommandType => CommandType.TextureSetStorage; private TableRef<ThreadedTexture> _texture; private BufferRange _storage; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs index ce1a83a78..8b62d1eff 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs @@ -2,7 +2,7 @@ { struct TextureBarrierCommand : IGALCommand, IGALCommand<TextureBarrierCommand> { - public CommandType CommandType => CommandType.TextureBarrier; + public readonly CommandType CommandType => CommandType.TextureBarrier; public static void Run(ref TextureBarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs index c65ffe2ec..1cae4109c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs @@ -2,7 +2,7 @@ { struct TextureBarrierTiledCommand : IGALCommand, IGALCommand<TextureBarrierTiledCommand> { - public CommandType CommandType => CommandType.TextureBarrierTiled; + public readonly CommandType CommandType => CommandType.TextureBarrierTiled; public static void Run(ref TextureBarrierTiledCommand command, ThreadedRenderer threaded, IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs index 9124ca1f0..a06f0b5ad 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct TryHostConditionalRenderingCommand : IGALCommand, IGALCommand<TryHostConditionalRenderingCommand> { - public CommandType CommandType => CommandType.TryHostConditionalRendering; + public readonly CommandType CommandType => CommandType.TryHostConditionalRendering; private TableRef<ThreadedCounterEvent> _value; private ulong _compare; private bool _isEqual; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs index a5d076400..eb920788b 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct TryHostConditionalRenderingFlushCommand : IGALCommand, IGALCommand<TryHostConditionalRenderingFlushCommand> { - public CommandType CommandType => CommandType.TryHostConditionalRenderingFlush; + public readonly CommandType CommandType => CommandType.TryHostConditionalRenderingFlush; private TableRef<ThreadedCounterEvent> _value; private TableRef<ThreadedCounterEvent> _compare; private bool _isEqual; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs index ebe141508..c3c240455 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { struct UpdateRenderScaleCommand : IGALCommand, IGALCommand<UpdateRenderScaleCommand> { - public CommandType CommandType => CommandType.UpdateRenderScale; + public readonly CommandType CommandType => CommandType.UpdateRenderScale; private SpanRef<float> _scales; private int _totalCount; private int _fragmentCount; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs index 6a24cd35b..67ec35213 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Window { struct WindowPresentCommand : IGALCommand, IGALCommand<WindowPresentCommand> { - public CommandType CommandType => CommandType.WindowPresent; + public readonly CommandType CommandType => CommandType.WindowPresent; private TableRef<ThreadedTexture> _texture; private ImageCrop _crop; private TableRef<Action> _swapBuffersCallback; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs index 4ea1a2c7a..10cab84ce 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs @@ -12,9 +12,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Model /// </summary> class CircularSpanPool { - private ThreadedRenderer _renderer; - private byte[] _pool; - private int _size; + private readonly ThreadedRenderer _renderer; + private readonly byte[] _pool; + private readonly int _size; private int _producerPtr; private int _producerSkipPosition = -1; @@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Model { int size = length * Unsafe.SizeOf<T>(); - _consumerPtr = _consumerPtr + size; + _consumerPtr += size; } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs index 7dbebc761..1ed719a08 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs @@ -2,9 +2,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Model { - struct SpanRef<T> where T : unmanaged + readonly struct SpanRef<T> where T : unmanaged { - private int _packedLengthId; + private readonly int _packedLengthId; public SpanRef(ThreadedRenderer renderer, T[] data) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs index 166aa71a2..e454df214 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs @@ -1,8 +1,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Model { - struct TableRef<T> + readonly struct TableRef<T> { - private int _index; + private readonly int _index; public TableRef(ThreadedRenderer renderer, T reference) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs index 3f982d31b..87ab2f1b2 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs @@ -12,10 +12,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources { private const int MaxConcurrentCompilations = 8; - private IRenderer _renderer; + private readonly IRenderer _renderer; - private Queue<IProgramRequest> _toCompile; - private List<ThreadedProgram> _inProgress; + private readonly Queue<IProgramRequest> _toCompile; + private readonly List<ThreadedProgram> _inProgress; public ProgramQueue(IRenderer renderer) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs index b4c6853f2..e4df1c1be 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs @@ -4,8 +4,8 @@ { public ThreadedProgram Threaded { get; set; } - private byte[] _data; - private bool _hasFragmentShader; + private readonly byte[] _data; + private readonly bool _hasFragmentShader; private ShaderInfo _info; public BinaryProgramRequest(ThreadedProgram program, byte[] data, bool hasFragmentShader, ShaderInfo info) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs index ff06abb12..744b77e1b 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs @@ -4,7 +4,7 @@ { public ThreadedProgram Threaded { get; set; } - private ShaderSource[] _shaders; + private readonly ShaderSource[] _shaders; private ShaderInfo _info; public SourceProgramRequest(ThreadedProgram program, ShaderSource[] shaders, ShaderInfo info) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs index 4b7471d6a..e4e197ebf 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources { class ThreadedCounterEvent : ICounterEvent { - private ThreadedRenderer _renderer; + private readonly ThreadedRenderer _renderer; public ICounterEvent Base; public bool Invalid { get; set; } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs index 068d058ea..7cbbce45d 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources { class ThreadedProgram : IProgram { - private ThreadedRenderer _renderer; + private readonly ThreadedRenderer _renderer; public IProgram Base; @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources public byte[] GetBinary() { - ResultBox<byte[]> box = new ResultBox<byte[]>(); + ResultBox<byte[]> box = new(); _renderer.New<ProgramGetBinaryCommand>().Set(Ref(this), Ref(box)); _renderer.InvokeCommand(); @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources public ProgramLinkStatus CheckProgramLink(bool blocking) { - ResultBox<ProgramLinkStatus> box = new ResultBox<ProgramLinkStatus>(); + ResultBox<ProgramLinkStatus> box = new(); _renderer.New<ProgramCheckLinkCommand>().Set(Ref(this), blocking, Ref(box)); _renderer.InvokeCommand(); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs index d8de9a70e..62e628a76 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources { class ThreadedSampler : ISampler { - private ThreadedRenderer _renderer; + private readonly ThreadedRenderer _renderer; public ISampler Base; public ThreadedSampler(ThreadedRenderer renderer) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index bb0b05fb8..b82e286ad 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -1,7 +1,6 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; using Ryujinx.Graphics.GAL.Multithreading.Model; -using System; namespace Ryujinx.Graphics.GAL.Multithreading.Resources { @@ -10,8 +9,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources /// </summary> class ThreadedTexture : ITexture { - private ThreadedRenderer _renderer; - private TextureCreateInfo _info; + private readonly ThreadedRenderer _renderer; + private readonly TextureCreateInfo _info; public ITexture Base; public int Width => _info.Width; @@ -65,7 +64,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - ThreadedTexture newTex = new ThreadedTexture(_renderer, info, ScaleFactor); + ThreadedTexture newTex = new(_renderer, info, ScaleFactor); _renderer.New<TextureCreateViewCommand>().Set(Ref(this), Ref(newTex), info, firstLayer, firstLevel); _renderer.QueueCommand(); @@ -76,7 +75,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources { if (_renderer.IsGpuThread()) { - ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + ResultBox<PinnedSpan<byte>> box = new(); _renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box)); _renderer.InvokeCommand(); @@ -94,7 +93,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources { if (_renderer.IsGpuThread()) { - ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + ResultBox<PinnedSpan<byte>> box = new(); _renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level); _renderer.InvokeCommand(); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs b/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs index ae09e852c..d6e416ef2 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading { class SyncMap : IDisposable { - private HashSet<ulong> _inFlight = new HashSet<ulong>(); - private AutoResetEvent _inFlightChanged = new AutoResetEvent(false); + private readonly HashSet<ulong> _inFlight = new(); + private readonly AutoResetEvent _inFlightChanged = new(false); internal void CreateSyncHandle(ulong id) { diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 1bdc9cf48..de6ba18d5 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -9,13 +9,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading { public class ThreadedPipeline : IPipeline { - private ThreadedRenderer _renderer; - private IPipeline _impl; + private readonly ThreadedRenderer _renderer; - public ThreadedPipeline(ThreadedRenderer renderer, IPipeline impl) + public ThreadedPipeline(ThreadedRenderer renderer) { _renderer = renderer; - _impl = impl; } private TableRef<T> Ref<T>(T reference) @@ -373,7 +371,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount) { - _renderer.New<UpdateRenderScaleCommand>().Set(_renderer.CopySpan(scales.Slice(0, totalCount)), totalCount, fragmentCount); + _renderer.New<UpdateRenderScaleCommand>().Set(_renderer.CopySpan(scales[..totalCount]), totalCount, fragmentCount); _renderer.QueueCommand(); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index bc96f2225..dc7aab36c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -26,24 +26,24 @@ namespace Ryujinx.Graphics.GAL.Multithreading private const int MaxRefsPerCommand = 2; private const int QueueCount = 10000; - private int _elementSize; - private IRenderer _baseRenderer; + private readonly int _elementSize; + private readonly IRenderer _baseRenderer; private Thread _gpuThread; private Thread _backendThread; private bool _running; - private AutoResetEvent _frameComplete = new AutoResetEvent(true); + private readonly AutoResetEvent _frameComplete = new(true); - private ManualResetEventSlim _galWorkAvailable; - private CircularSpanPool _spanPool; + private readonly ManualResetEventSlim _galWorkAvailable; + private readonly CircularSpanPool _spanPool; - private ManualResetEventSlim _invokeRun; - private AutoResetEvent _interruptRun; + private readonly ManualResetEventSlim _invokeRun; + private readonly AutoResetEvent _interruptRun; private bool _lastSampleCounterClear = true; - private byte[] _commandQueue; - private object[] _refQueue; + private readonly byte[] _commandQueue; + private readonly object[] _refQueue; private int _consumerPtr; private int _commandCount; @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info); renderer.SetInterruptAction(Interrupt); - Pipeline = new ThreadedPipeline(this, renderer.Pipeline); + Pipeline = new ThreadedPipeline(this); Window = new ThreadedWindow(this, renderer); Buffers = new BufferMap(); Sync = new SyncMap(); @@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading _gpuThread = new Thread(gpuLoop) { - Name = "GPU.MainThread" + Name = "GPU.MainThread", }; _gpuThread.Start(); @@ -137,7 +137,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading { int commandPtr = _consumerPtr; - Span<byte> command = new Span<byte>(_commandQueue, commandPtr * _elementSize, _elementSize); + Span<byte> command = new(_commandQueue, commandPtr * _elementSize, _elementSize); // Run the command. @@ -180,10 +180,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading _producerPtr = (_producerPtr + 1) % QueueCount; - Span<byte> memory = new Span<byte>(_commandQueue, taken * _elementSize, _elementSize); + Span<byte> memory = new(_commandQueue, taken * _elementSize, _elementSize); ref T result = ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(memory)); - memory[memory.Length - 1] = (byte)((IGALCommand)result).CommandType; + memory[^1] = (byte)((IGALCommand)result).CommandType; return ref result; } @@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading { var program = new ThreadedProgram(this); - SourceProgramRequest request = new SourceProgramRequest(program, shaders, info); + SourceProgramRequest request = new(program, shaders, info); Programs.Add(request); @@ -332,8 +332,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading } else { - var texture = new ThreadedTexture(this, info, scale); - texture.Base = _baseRenderer.CreateTexture(info, scale); + var texture = new ThreadedTexture(this, info, scale) + { + Base = _baseRenderer.CreateTexture(info, scale), + }; return texture; } @@ -349,7 +351,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading { if (IsGpuThread()) { - ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + ResultBox<PinnedSpan<byte>> box = new(); New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box)); InvokeCommand(); @@ -363,7 +365,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading public Capabilities GetCapabilities() { - ResultBox<Capabilities> box = new ResultBox<Capabilities>(); + ResultBox<Capabilities> box = new(); New<GetCapabilitiesCommand>().Set(Ref(box)); InvokeCommand(); @@ -393,7 +395,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading { var program = new ThreadedProgram(this); - BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary, hasFragmentShader, info); + BinaryProgramRequest request = new(program, programBinary, hasFragmentShader, info); Programs.Add(request); New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); @@ -410,7 +412,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) { - ThreadedCounterEvent evt = new ThreadedCounterEvent(this, type, _lastSampleCounterClear); + ThreadedCounterEvent evt = new(this, type, _lastSampleCounterClear); New<ReportCounterCommand>().Set(Ref(evt), type, Ref(resultHandler), hostReserved); QueueCommand(); @@ -466,7 +468,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading { lock (_interruptLock) { - while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { } + while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) + { + } _galWorkAvailable.Set(); @@ -497,6 +501,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading public void Dispose() { + GC.SuppressFinalize(this); + // Dispose must happen from the render thread, after all commands have completed. // Stop the GPU thread. @@ -520,4 +526,4 @@ namespace Ryujinx.Graphics.GAL.Multithreading Sync.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs index a647d37eb..3c4d54149 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -7,8 +7,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading { public class ThreadedWindow : IWindow { - private ThreadedRenderer _renderer; - private IRenderer _impl; + private readonly ThreadedRenderer _renderer; + private readonly IRenderer _impl; public ThreadedWindow(ThreadedRenderer renderer, IRenderer impl) { diff --git a/src/Ryujinx.Graphics.GAL/Origin.cs b/src/Ryujinx.Graphics.GAL/Origin.cs index d1b69cfd3..bc2a21f1c 100644 --- a/src/Ryujinx.Graphics.GAL/Origin.cs +++ b/src/Ryujinx.Graphics.GAL/Origin.cs @@ -3,6 +3,6 @@ public enum Origin { UpperLeft, - LowerLeft + LowerLeft, } } diff --git a/src/Ryujinx.Graphics.GAL/PinnedSpan.cs b/src/Ryujinx.Graphics.GAL/PinnedSpan.cs index 275b3b866..d028f07c3 100644 --- a/src/Ryujinx.Graphics.GAL/PinnedSpan.cs +++ b/src/Ryujinx.Graphics.GAL/PinnedSpan.cs @@ -4,11 +4,11 @@ using System.Runtime.InteropServices; namespace Ryujinx.Graphics.GAL { - public unsafe struct PinnedSpan<T> : IDisposable where T : unmanaged + public readonly unsafe struct PinnedSpan<T> : IDisposable where T : unmanaged { - private void* _ptr; - private int _size; - private Action _disposeAction; + private readonly void* _ptr; + private readonly int _size; + private readonly Action _disposeAction; /// <summary> /// Creates a new PinnedSpan from an existing ReadOnlySpan. The span *must* be pinned in memory. diff --git a/src/Ryujinx.Graphics.GAL/PolygonMode.cs b/src/Ryujinx.Graphics.GAL/PolygonMode.cs index d6110c1b3..841a6f224 100644 --- a/src/Ryujinx.Graphics.GAL/PolygonMode.cs +++ b/src/Ryujinx.Graphics.GAL/PolygonMode.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.GAL { Point = 0x1b00, Line = 0x1b01, - Fill = 0x1b02 + Fill = 0x1b02, } } diff --git a/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs b/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs index 514b42317..c6ac45099 100644 --- a/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs +++ b/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL public enum PolygonModeMask { Point = 1 << 0, - Line = 1 << 1, - Fill = 1 << 2 + Line = 1 << 1, + Fill = 1 << 2, } } diff --git a/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs b/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs index ed567a68a..91e6a68aa 100644 --- a/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs +++ b/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs @@ -16,6 +16,6 @@ namespace Ryujinx.Graphics.GAL LineStripAdjacency, TrianglesAdjacency, TriangleStripAdjacency, - Patches + Patches, } } diff --git a/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs b/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs index 5ca1be8c3..4dd3b4d5d 100644 --- a/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs +++ b/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs @@ -4,6 +4,6 @@ { Incomplete, Success, - Failure + Failure, } } diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs index 3cde281fc..200292eee 100644 --- a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs +++ b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.GAL TextureAndSampler, Image, BufferTexture, - BufferImage + BufferImage, } public enum ResourceAccess : byte @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.GAL None = 0, Read = 1, Write = 2, - ReadWrite = Read | Write + ReadWrite = Read | Write, } [Flags] @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.GAL TessellationControl = 1 << 2, TessellationEvaluation = 1 << 3, Geometry = 1 << 4, - Fragment = 1 << 5 + Fragment = 1 << 5, } public readonly struct ResourceDescriptor : IEquatable<ResourceDescriptor> @@ -64,6 +64,16 @@ namespace Ryujinx.Graphics.GAL { return Binding == other.Binding && Count == other.Count && Type == other.Type && Stages == other.Stages; } + + public static bool operator ==(ResourceDescriptor left, ResourceDescriptor right) + { + return left.Equals(right); + } + + public static bool operator !=(ResourceDescriptor left, ResourceDescriptor right) + { + return !(left == right); + } } public readonly struct ResourceUsage : IEquatable<ResourceUsage> @@ -95,6 +105,16 @@ namespace Ryujinx.Graphics.GAL { return Binding == other.Binding && Type == other.Type && Stages == other.Stages && Access == other.Access; } + + public static bool operator ==(ResourceUsage left, ResourceUsage right) + { + return left.Equals(right); + } + + public static bool operator !=(ResourceUsage left, ResourceUsage right) + { + return !(left == right); + } } public readonly struct ResourceDescriptorCollection @@ -108,7 +128,7 @@ namespace Ryujinx.Graphics.GAL public override int GetHashCode() { - HashCode hasher = new HashCode(); + HashCode hasher = new(); if (Descriptors != null) { @@ -151,6 +171,16 @@ namespace Ryujinx.Graphics.GAL return true; } + + public static bool operator ==(ResourceDescriptorCollection left, ResourceDescriptorCollection right) + { + return left.Equals(right); + } + + public static bool operator !=(ResourceDescriptorCollection left, ResourceDescriptorCollection right) + { + return !(left == right); + } } public readonly struct ResourceUsageCollection diff --git a/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs b/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs index 990c302e2..2374a0662 100644 --- a/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs +++ b/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs @@ -12,43 +12,43 @@ namespace Ryujinx.Graphics.GAL public AddressMode AddressP { get; } public CompareMode CompareMode { get; } - public CompareOp CompareOp { get; } + public CompareOp CompareOp { get; } public ColorF BorderColor { get; } - public float MinLod { get; } - public float MaxLod { get; } - public float MipLodBias { get; } + public float MinLod { get; } + public float MaxLod { get; } + public float MipLodBias { get; } public float MaxAnisotropy { get; } public SamplerCreateInfo( - MinFilter minFilter, - MagFilter magFilter, - bool seamlessCubemap, + MinFilter minFilter, + MagFilter magFilter, + bool seamlessCubemap, AddressMode addressU, AddressMode addressV, AddressMode addressP, CompareMode compareMode, - CompareOp compareOp, - ColorF borderColor, - float minLod, - float maxLod, - float mipLodBias, - float maxAnisotropy) + CompareOp compareOp, + ColorF borderColor, + float minLod, + float maxLod, + float mipLodBias, + float maxAnisotropy) { - MinFilter = minFilter; - MagFilter = magFilter; + MinFilter = minFilter; + MagFilter = magFilter; SeamlessCubemap = seamlessCubemap; - AddressU = addressU; - AddressV = addressV; - AddressP = addressP; - CompareMode = compareMode; - CompareOp = compareOp; - BorderColor = borderColor; - MinLod = minLod; - MaxLod = maxLod; - MipLodBias = mipLodBias; - MaxAnisotropy = maxAnisotropy; + AddressU = addressU; + AddressV = addressV; + AddressP = addressP; + CompareMode = compareMode; + CompareOp = compareOp; + BorderColor = borderColor; + MinLod = minLod; + MaxLod = maxLod; + MipLodBias = mipLodBias; + MaxAnisotropy = maxAnisotropy; } public static SamplerCreateInfo Create(MinFilter minFilter, MagFilter magFilter) @@ -69,4 +69,4 @@ namespace Ryujinx.Graphics.GAL 1f); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs b/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs index 129913ec1..31ad1fecb 100644 --- a/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs +++ b/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs @@ -4,19 +4,19 @@ namespace Ryujinx.Graphics.GAL { public ScreenCaptureImageInfo(int width, int height, bool isBgra, byte[] data, bool flipX, bool flipY) { - Width = width; + Width = width; Height = height; IsBgra = isBgra; - Data = data; - FlipX = flipX; - FlipY = flipY; + Data = data; + FlipX = flipX; + FlipY = flipY; } - public int Width { get; } - public int Height { get; } - public byte[] Data { get; } - public bool IsBgra { get; } - public bool FlipX { get; } - public bool FlipY { get; } + public int Width { get; } + public int Height { get; } + public byte[] Data { get; } + public bool IsBgra { get; } + public bool FlipX { get; } + public bool FlipY { get; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs index 643f1bc5f..2fd3227dc 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -23,4 +23,4 @@ namespace Ryujinx.Graphics.GAL FromCache = fromCache; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/ShaderSource.cs b/src/Ryujinx.Graphics.GAL/ShaderSource.cs index 773c0a8a1..bf678e609 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderSource.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderSource.cs @@ -26,4 +26,4 @@ namespace Ryujinx.Graphics.GAL { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/StencilOp.cs b/src/Ryujinx.Graphics.GAL/StencilOp.cs index fe999b0fc..fa87f0b56 100644 --- a/src/Ryujinx.Graphics.GAL/StencilOp.cs +++ b/src/Ryujinx.Graphics.GAL/StencilOp.cs @@ -11,13 +11,13 @@ namespace Ryujinx.Graphics.GAL IncrementAndWrap, DecrementAndWrap, - ZeroGl = 0x0, - InvertGl = 0x150a, - KeepGl = 0x1e00, - ReplaceGl = 0x1e01, + ZeroGl = 0x0, + InvertGl = 0x150a, + KeepGl = 0x1e00, + ReplaceGl = 0x1e01, IncrementAndClampGl = 0x1e02, DecrementAndClampGl = 0x1e03, - IncrementAndWrapGl = 0x8507, - DecrementAndWrapGl = 0x8508 + IncrementAndWrapGl = 0x8507, + DecrementAndWrapGl = 0x8508, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs b/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs index db46c9579..4309aa957 100644 --- a/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs +++ b/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs @@ -4,53 +4,53 @@ namespace Ryujinx.Graphics.GAL { public bool TestEnable { get; } - public CompareOp FrontFunc { get; } - public StencilOp FrontSFail { get; } - public StencilOp FrontDpPass { get; } - public StencilOp FrontDpFail { get; } - public int FrontFuncRef { get; } - public int FrontFuncMask { get; } - public int FrontMask { get; } - public CompareOp BackFunc { get; } - public StencilOp BackSFail { get; } - public StencilOp BackDpPass { get; } - public StencilOp BackDpFail { get; } - public int BackFuncRef { get; } - public int BackFuncMask { get; } - public int BackMask { get; } + public CompareOp FrontFunc { get; } + public StencilOp FrontSFail { get; } + public StencilOp FrontDpPass { get; } + public StencilOp FrontDpFail { get; } + public int FrontFuncRef { get; } + public int FrontFuncMask { get; } + public int FrontMask { get; } + public CompareOp BackFunc { get; } + public StencilOp BackSFail { get; } + public StencilOp BackDpPass { get; } + public StencilOp BackDpFail { get; } + public int BackFuncRef { get; } + public int BackFuncMask { get; } + public int BackMask { get; } public StencilTestDescriptor( - bool testEnable, + bool testEnable, CompareOp frontFunc, StencilOp frontSFail, StencilOp frontDpPass, StencilOp frontDpFail, - int frontFuncRef, - int frontFuncMask, - int frontMask, + int frontFuncRef, + int frontFuncMask, + int frontMask, CompareOp backFunc, StencilOp backSFail, StencilOp backDpPass, StencilOp backDpFail, - int backFuncRef, - int backFuncMask, - int backMask) + int backFuncRef, + int backFuncMask, + int backMask) { - TestEnable = testEnable; - FrontFunc = frontFunc; - FrontSFail = frontSFail; - FrontDpPass = frontDpPass; - FrontDpFail = frontDpFail; - FrontFuncRef = frontFuncRef; + TestEnable = testEnable; + FrontFunc = frontFunc; + FrontSFail = frontSFail; + FrontDpPass = frontDpPass; + FrontDpFail = frontDpFail; + FrontFuncRef = frontFuncRef; FrontFuncMask = frontFuncMask; - FrontMask = frontMask; - BackFunc = backFunc; - BackSFail = backSFail; - BackDpPass = backDpPass; - BackDpFail = backDpFail; - BackFuncRef = backFuncRef; - BackFuncMask = backFuncMask; - BackMask = backMask; + FrontMask = frontMask; + BackFunc = backFunc; + BackSFail = backSFail; + BackDpPass = backDpPass; + BackDpFail = backDpFail; + BackFuncRef = backFuncRef; + BackFuncMask = backFuncMask; + BackMask = backMask; } } } diff --git a/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs index 6eeddb6c0..84936e547 100644 --- a/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs +++ b/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.GAL public SupportBuffer Data; public BufferHandle Handle; - private IRenderer _renderer; + private readonly IRenderer _renderer; private int _startOffset = -1; private int _endOffset = -1; @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.GAL private void UpdateGenericField<T>(int baseOffset, ReadOnlySpan<T> data, Span<T> target, int offset, int count) where T : unmanaged { - data.Slice(0, count).CopyTo(target.Slice(offset)); + data[..count].CopyTo(target[offset..]); int elemSize = Unsafe.SizeOf<T>(); @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.GAL { ReadOnlySpan<byte> data = MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref Data, 1)); - _renderer.SetBufferData(Handle, _startOffset, data.Slice(_startOffset, _endOffset - _startOffset)); + _renderer.SetBufferData(Handle, _startOffset, data[_startOffset.._endOffset]); _startOffset = -1; _endOffset = -1; @@ -95,6 +95,7 @@ namespace Ryujinx.Graphics.GAL public void Dispose() { + GC.SuppressFinalize(this); _renderer.DeleteBuffer(Handle); } } diff --git a/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs b/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs index a405bd139..ee65e010e 100644 --- a/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs +++ b/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Graphics.GAL Red, Green, Blue, - Alpha + Alpha, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/Target.cs b/src/Ryujinx.Graphics.GAL/Target.cs index 711eea248..73497546e 100644 --- a/src/Ryujinx.Graphics.GAL/Target.cs +++ b/src/Ryujinx.Graphics.GAL/Target.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.GAL Texture2DMultisampleArray, Cubemap, CubemapArray, - TextureBuffer + TextureBuffer, } public static class TargetExtensions @@ -31,4 +31,4 @@ namespace Ryujinx.Graphics.GAL target == Target.CubemapArray; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs index 52b3b11f9..44090291d 100644 --- a/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs +++ b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs @@ -6,13 +6,13 @@ namespace Ryujinx.Graphics.GAL { public readonly struct TextureCreateInfo : IEquatable<TextureCreateInfo> { - public int Width { get; } - public int Height { get; } - public int Depth { get; } - public int Levels { get; } - public int Samples { get; } - public int BlockWidth { get; } - public int BlockHeight { get; } + public int Width { get; } + public int Height { get; } + public int Depth { get; } + public int Levels { get; } + public int Samples { get; } + public int BlockWidth { get; } + public int BlockHeight { get; } public int BytesPerPixel { get; } public bool IsCompressed => (BlockWidth | BlockHeight) != 1; @@ -29,37 +29,37 @@ namespace Ryujinx.Graphics.GAL public SwizzleComponent SwizzleA { get; } public TextureCreateInfo( - int width, - int height, - int depth, - int levels, - int samples, - int blockWidth, - int blockHeight, - int bytesPerPixel, - Format format, + int width, + int height, + int depth, + int levels, + int samples, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, DepthStencilMode depthStencilMode, - Target target, + Target target, SwizzleComponent swizzleR, SwizzleComponent swizzleG, SwizzleComponent swizzleB, SwizzleComponent swizzleA) { - Width = width; - Height = height; - Depth = depth; - Levels = levels; - Samples = samples; - BlockWidth = blockWidth; - BlockHeight = blockHeight; - BytesPerPixel = bytesPerPixel; - Format = format; + Width = width; + Height = height; + Depth = depth; + Levels = levels; + Samples = samples; + BlockWidth = blockWidth; + BlockHeight = blockHeight; + BytesPerPixel = bytesPerPixel; + Format = format; DepthStencilMode = depthStencilMode; - Target = target; - SwizzleR = swizzleR; - SwizzleG = swizzleG; - SwizzleB = swizzleB; - SwizzleA = swizzleA; + Target = target; + SwizzleR = swizzleR; + SwizzleG = swizzleG; + SwizzleB = swizzleB; + SwizzleA = swizzleA; } public int GetMipSize(int level) @@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.GAL return HashCode.Combine(Width, Height); } - bool IEquatable<TextureCreateInfo>.Equals(TextureCreateInfo other) + public bool Equals(TextureCreateInfo other) { return Width == other.Width && Height == other.Height && @@ -160,5 +160,20 @@ namespace Ryujinx.Graphics.GAL SwizzleB == other.SwizzleB && SwizzleA == other.SwizzleA; } + + public override bool Equals(object obj) + { + return obj is TextureCreateInfo info && this.Equals(info); + } + + public static bool operator ==(TextureCreateInfo left, TextureCreateInfo right) + { + return left.Equals(right); + } + + public static bool operator !=(TextureCreateInfo left, TextureCreateInfo right) + { + return !(left == right); + } } } diff --git a/src/Ryujinx.Graphics.GAL/UpscaleType.cs b/src/Ryujinx.Graphics.GAL/UpscaleType.cs index 442b65f24..ca24199c4 100644 --- a/src/Ryujinx.Graphics.GAL/UpscaleType.cs +++ b/src/Ryujinx.Graphics.GAL/UpscaleType.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.GAL { Bilinear, Nearest, - Fsr + Fsr, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs b/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs index 15f0dff85..8fd3c816b 100644 --- a/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs +++ b/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs @@ -4,13 +4,13 @@ namespace Ryujinx.Graphics.GAL { public BufferRange Buffer { get; } - public int Stride { get; } + public int Stride { get; } public int Divisor { get; } public VertexBufferDescriptor(BufferRange buffer, int stride, int divisor) { - Buffer = buffer; - Stride = stride; + Buffer = buffer; + Stride = stride; Divisor = divisor; } } diff --git a/src/Ryujinx.Graphics.GAL/Viewport.cs b/src/Ryujinx.Graphics.GAL/Viewport.cs index 94012c003..12d13b7c8 100644 --- a/src/Ryujinx.Graphics.GAL/Viewport.cs +++ b/src/Ryujinx.Graphics.GAL/Viewport.cs @@ -10,24 +10,24 @@ namespace Ryujinx.Graphics.GAL public ViewportSwizzle SwizzleW { get; } public float DepthNear { get; } - public float DepthFar { get; } + public float DepthFar { get; } public Viewport( Rectangle<float> region, - ViewportSwizzle swizzleX, - ViewportSwizzle swizzleY, - ViewportSwizzle swizzleZ, - ViewportSwizzle swizzleW, - float depthNear, - float depthFar) + ViewportSwizzle swizzleX, + ViewportSwizzle swizzleY, + ViewportSwizzle swizzleZ, + ViewportSwizzle swizzleW, + float depthNear, + float depthFar) { - Region = region; - SwizzleX = swizzleX; - SwizzleY = swizzleY; - SwizzleZ = swizzleZ; - SwizzleW = swizzleW; + Region = region; + SwizzleX = swizzleX; + SwizzleY = swizzleY; + SwizzleZ = swizzleZ; + SwizzleW = swizzleW; DepthNear = depthNear; - DepthFar = depthFar; + DepthFar = depthFar; } } } diff --git a/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs b/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs index c24a22464..9352c8162 100644 --- a/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs +++ b/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Ryujinx.Graphics.GAL { + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] public enum ViewportSwizzle { PositiveX = 0, @@ -11,6 +14,6 @@ namespace Ryujinx.Graphics.GAL PositiveW = 6, NegativeW = 7, - NegativeFlag = 1 + NegativeFlag = 1, } } From 6e28a4dd13df0ab866e6a178086abe36ca4a2b25 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 29 Jun 2023 02:39:22 +0200 Subject: [PATCH 686/737] [Ryujinx.Ui.Common] Address dotnet-format issues (#5392) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Silence dotnet format IDE0060 warnings * Address dotnet format CA1401 warnings * dotnet-format fixes after rebase * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Another rebase, another dotnet format run * Run dotnet format style after rebase * Add comments to disabled warnings * Remove a few unused parameters * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Address IDE0251 warnings * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Small optimizations * Remove alignment * Apply formatting * Fix build issues * Final pass for dotnet format * Add trailing commas Co-authored-by: Ac_K <Acoustik666@gmail.com> * Add trailing commas --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- src/Ryujinx.Ava/AppHost.cs | 3 +- .../Controls/ApplicationContextMenu.axaml.cs | 2 +- src/Ryujinx.Ava/UI/Models/SaveModel.cs | 3 +- src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 24 +- .../App/ApplicationAddedEventArgs.cs | 2 +- .../App/ApplicationCountUpdatedEventArgs.cs | 4 +- src/Ryujinx.Ui.Common/App/ApplicationData.cs | 28 +- .../App/ApplicationJsonSerializerContext.cs | 2 +- .../App/ApplicationLibrary.cs | 68 +- .../App/ApplicationMetadata.cs | 4 +- .../Configuration/AudioBackend.cs | 4 +- .../Configuration/ConfigurationFileFormat.cs | 2 +- .../ConfigurationFileFormatSettings.cs | 2 +- .../ConfigurationJsonSerializerContext.cs | 2 +- .../Configuration/ConfigurationLoadResult.cs | 4 +- .../Configuration/ConfigurationState.cs | 968 +++++++++--------- .../Configuration/LoggerModule.cs | 18 +- .../Configuration/System/Language.cs | 2 +- .../Configuration/System/Region.cs | 2 +- .../Configuration/Ui/ColumnSort.cs | 4 +- .../Configuration/Ui/GuiColumns.cs | 16 +- .../Configuration/Ui/ShownFileTypes.cs | 10 +- .../DiscordIntegrationModule.cs | 46 +- .../Extensions/FileTypeExtensions.cs | 2 +- .../Helper/CommandLineState.cs | 14 +- src/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs | 2 +- .../Helper/FileAssociationHelper.cs | 4 +- src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs | 6 +- src/Ryujinx.Ui.Common/Helper/ObjectiveC.cs | 97 +- src/Ryujinx.Ui.Common/Helper/OpenHelper.cs | 36 +- .../Helper/SetupValidator.cs | 16 +- .../Models/Amiibo/AmiiboApi.cs | 22 +- .../Models/Amiibo/AmiiboApiGamesSwitch.cs | 2 +- .../Models/Amiibo/AmiiboApiUsage.cs | 2 +- .../Models/Amiibo/AmiiboJson.cs | 2 +- .../Amiibo/AmiiboJsonSerializerContext.cs | 2 +- .../Github/GithubReleaseAssetJsonResponse.cs | 2 +- .../Github/GithubReleasesJsonResponse.cs | 2 +- .../GithubReleasesJsonSerializerContext.cs | 2 +- src/Ryujinx.Ui.Common/UserError.cs | 4 +- src/Ryujinx/Ui/MainWindow.cs | 20 +- 41 files changed, 761 insertions(+), 696 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index a25713796..c2b1064be 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -32,6 +32,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.Input; using Ryujinx.Input.HLE; +using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; @@ -692,7 +693,7 @@ namespace Ryujinx.Ava DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name); - _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => + ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow; }); diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index fba24d68b..b01c7c5e3 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Controls { viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite; - viewModel.ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata => + ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata => { appMetadata.Favorite = viewModel.SelectedApplication.Favorite; }); diff --git a/src/Ryujinx.Ava/UI/Models/SaveModel.cs b/src/Ryujinx.Ava/UI/Models/SaveModel.cs index e8486459b..cc9b67681 100644 --- a/src/Ryujinx.Ava/UI/Models/SaveModel.cs +++ b/src/Ryujinx.Ava/UI/Models/SaveModel.cs @@ -3,6 +3,7 @@ using LibHac.Ncm; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.HLE.FileSystem; +using Ryujinx.Ui.App.Common; using System; using System.IO; using System.Linq; @@ -74,7 +75,7 @@ namespace Ryujinx.Ava.UI.Models } else { - var appMetadata = MainWindow.MainWindowViewModel.ApplicationLibrary.LoadAndSaveMetaData(TitleIdString); + var appMetadata = ApplicationLibrary.LoadAndSaveMetaData(TitleIdString); Title = appMetadata.Title ?? TitleIdString; } diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs index 3b2c32e3f..c5dd9332c 100644 --- a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs +++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -239,28 +239,28 @@ namespace Ryujinx.Ava.UI.Renderer IPlatformHandle CreateMacOS() { // Create a new CAMetalLayer. - IntPtr layerClass = ObjectiveC.objc_getClass("CAMetalLayer"); - IntPtr metalLayer = ObjectiveC.IntPtr_objc_msgSend(layerClass, "alloc"); - ObjectiveC.objc_msgSend(metalLayer, "init"); + ObjectiveC.Object layerObject = new("CAMetalLayer"); + ObjectiveC.Object metalLayer = layerObject.GetFromMessage("alloc"); + metalLayer.SendMessage("init"); // Create a child NSView to render into. - IntPtr nsViewClass = ObjectiveC.objc_getClass("NSView"); - IntPtr child = ObjectiveC.IntPtr_objc_msgSend(nsViewClass, "alloc"); - ObjectiveC.objc_msgSend(child, "init", new ObjectiveC.NSRect(0, 0, 0, 0)); + ObjectiveC.Object nsViewObject = new("NSView"); + ObjectiveC.Object child = nsViewObject.GetFromMessage("alloc"); + child.SendMessage("init", new ObjectiveC.NSRect(0, 0, 0, 0)); // Make its renderer our metal layer. - ObjectiveC.objc_msgSend(child, "setWantsLayer:", 1); - ObjectiveC.objc_msgSend(child, "setLayer:", metalLayer); - ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor); + child.SendMessage("setWantsLayer:", 1); + child.SendMessage("setLayer:", metalLayer); + metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor); // Ensure the scale factor is up to date. _updateBoundsCallback = rect => { - ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor); + metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor); }; - IntPtr nsView = child; - MetalLayer = metalLayer; + IntPtr nsView = child.ObjPtr; + MetalLayer = metalLayer.ObjPtr; NsView = nsView; return new PlatformHandle(nsView, "NSView"); diff --git a/src/Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs b/src/Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs index 6ae7409af..78eed7afd 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationAddedEventArgs.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Ui.App.Common { public ApplicationData AppData { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs b/src/Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs index 981a82d53..6a8e465e0 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationCountUpdatedEventArgs.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Ui.App.Common { public class ApplicationCountUpdatedEventArgs : EventArgs { - public int NumAppsFound { get; set; } + public int NumAppsFound { get; set; } public int NumAppsLoaded { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs index 1081fcf32..e6130bdac 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationData.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationData.cs @@ -18,19 +18,19 @@ namespace Ryujinx.Ui.App.Common { public class ApplicationData { - public bool Favorite { get; set; } - public byte[] Icon { get; set; } - public string TitleName { get; set; } - public string TitleId { get; set; } - public string Developer { get; set; } - public string Version { get; set; } - public string TimePlayed { get; set; } - public double TimePlayedNum { get; set; } - public DateTime? LastPlayed { get; set; } - public string FileExtension { get; set; } - public string FileSize { get; set; } - public double FileSizeBytes { get; set; } - public string Path { get; set; } + public bool Favorite { get; set; } + public byte[] Icon { get; set; } + public string TitleName { get; set; } + public string TitleId { get; set; } + public string Developer { get; set; } + public string Version { get; set; } + public string TimePlayed { get; set; } + public double TimePlayedNum { get; set; } + public DateTime? LastPlayed { get; set; } + public string FileExtension { get; set; } + public string FileSize { get; set; } + public double FileSizeBytes { get; set; } + public string Path { get; set; } public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } [JsonIgnore] @@ -159,7 +159,7 @@ namespace Ryujinx.Ui.App.Common codeFs.OpenFile(ref nsoFile.Ref, $"/{MainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - NsoReader reader = new NsoReader(); + NsoReader reader = new(); reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure(); return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16]; diff --git a/src/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs index f81121c28..76eea33ca 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Ui.App.Common internal partial class ApplicationJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 28280bd9f..33e6c4aad 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -26,12 +26,13 @@ using System.Text; using System.Text.Json; using System.Threading; using Path = System.IO.Path; +using TimeSpan = System.TimeSpan; namespace Ryujinx.Ui.App.Common { public class ApplicationLibrary { - public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded; + public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded; public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated; private readonly byte[] _nspIcon; @@ -41,11 +42,11 @@ namespace Ryujinx.Ui.App.Common private readonly byte[] _nsoIcon; private readonly VirtualFileSystem _virtualFileSystem; - private Language _desiredTitleLanguage; - private CancellationTokenSource _cancellationToken; + private Language _desiredTitleLanguage; + private CancellationTokenSource _cancellationToken; - private static readonly ApplicationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public ApplicationLibrary(VirtualFileSystem virtualFileSystem) { @@ -60,7 +61,7 @@ namespace Ryujinx.Ui.App.Common private static byte[] GetResourceBytes(string resourceName) { - Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName); + Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName); byte[] resourceByteArray = new byte[resourceStream.Length]; resourceStream.Read(resourceByteArray); @@ -83,7 +84,7 @@ namespace Ryujinx.Ui.App.Common public void LoadApplications(List<string> appDirs, Language desiredTitleLanguage) { - int numApplicationsFound = 0; + int numApplicationsFound = 0; int numApplicationsLoaded = 0; _desiredTitleLanguage = desiredTitleLanguage; @@ -114,14 +115,14 @@ namespace Ryujinx.Ui.App.Common IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories).Where(file => { return - (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value) || + (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value) || (Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value) || - (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value) || - (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value) || - (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value) || - (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value); + (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value) || + (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value) || + (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value) || + (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value); }); - + foreach (string app in files) { if (_cancellationToken.Token.IsCancellationRequested) @@ -459,27 +460,27 @@ namespace Ryujinx.Ui.App.Common FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + " MiB" : fileSize.ToString("0.##") + " GiB", FileSizeBytes = fileSize, Path = applicationPath, - ControlHolder = controlHolder + ControlHolder = controlHolder, }; numApplicationsLoaded++; - OnApplicationAdded(new ApplicationAddedEventArgs() + OnApplicationAdded(new ApplicationAddedEventArgs { - AppData = data + AppData = data, }); - OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() + OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs { NumAppsFound = numApplicationsFound, - NumAppsLoaded = numApplicationsLoaded + NumAppsLoaded = numApplicationsLoaded, }); } - OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() + OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs { NumAppsFound = numApplicationsFound, - NumAppsLoaded = numApplicationsLoaded + NumAppsLoaded = numApplicationsLoaded, }); } finally @@ -505,13 +506,13 @@ namespace Ryujinx.Ui.App.Common // Return the ControlFS controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); - titleId = controlNca?.Header.TitleId.ToString("x16"); + titleId = controlNca?.Header.TitleId.ToString("x16"); } - public ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null) + public static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null) { string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui"); - string metadataFile = Path.Combine(metadataFolder, "metadata.json"); + string metadataFile = Path.Combine(metadataFolder, "metadata.json"); ApplicationMetadata appMetadata; @@ -521,12 +522,12 @@ namespace Ryujinx.Ui.App.Common appMetadata = new ApplicationMetadata(); - JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); + JsonHelper.SerializeToFile(metadataFile, appMetadata, _serializerContext.ApplicationMetadata); } try { - appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata); + appMetadata = JsonHelper.DeserializeFromFile(metadataFile, _serializerContext.ApplicationMetadata); } catch (JsonException) { @@ -539,7 +540,7 @@ namespace Ryujinx.Ui.App.Common { modifyFunction(appMetadata); - JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); + JsonHelper.SerializeToFile(metadataFile, appMetadata, _serializerContext.ApplicationMetadata); } return appMetadata; @@ -703,7 +704,7 @@ namespace Ryujinx.Ui.App.Common } } } - catch(Exception) + catch (Exception) { Logger.Warning?.Print(LogClass.Application, $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}"); } @@ -713,7 +714,7 @@ namespace Ryujinx.Ui.App.Common private static string ConvertSecondsToFormattedString(double seconds) { - System.TimeSpan time = System.TimeSpan.FromSeconds(seconds); + TimeSpan time = TimeSpan.FromSeconds(seconds); string timeString; if (time.Days != 0) @@ -840,7 +841,7 @@ namespace Ryujinx.Ui.App.Common pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); + Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage()); int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); @@ -884,7 +885,7 @@ namespace Ryujinx.Ui.App.Common pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); + Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage()); int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); @@ -925,12 +926,12 @@ namespace Ryujinx.Ui.App.Common if (File.Exists(titleUpdateMetadataPath)) { - updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; + updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected; if (File.Exists(updatePath)) { - FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); + FileStream file = new(updatePath, FileMode.Open, FileAccess.Read); + PartitionFileSystem nsp = new(file.AsStorage()); return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex); } @@ -941,4 +942,3 @@ namespace Ryujinx.Ui.App.Common } } } - diff --git a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs index 0abd4680d..01b857a62 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Ui.App.Common public class ApplicationMetadata { public string Title { get; set; } - public bool Favorite { get; set; } + public bool Favorite { get; set; } public double TimePlayed { get; set; } [JsonPropertyName("last_played_utc")] @@ -16,4 +16,4 @@ namespace Ryujinx.Ui.App.Common [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string LastPlayedOld { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Configuration/AudioBackend.cs b/src/Ryujinx.Ui.Common/Configuration/AudioBackend.cs index 1f9bd0baf..1147a0a6b 100644 --- a/src/Ryujinx.Ui.Common/Configuration/AudioBackend.cs +++ b/src/Ryujinx.Ui.Common/Configuration/AudioBackend.cs @@ -9,6 +9,6 @@ namespace Ryujinx.Ui.Common.Configuration Dummy, OpenAl, SoundIo, - SDL2 + SDL2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index f0489915d..434894328 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -395,4 +395,4 @@ namespace Ryujinx.Ui.Common.Configuration JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs index 6ce2ef01a..a1727df51 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Ui.Common.Configuration { public static readonly ConfigurationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs index bb8dfb499..2aa7e5366 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Ui.Common.Configuration internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs index 6b08baa65..71366ba7c 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationLoadResult.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Ui.Common.Configuration { Success = 0, NotLoaded = 1, - MigratedFromPreVulkan = 1 << 8 + MigratedFromPreVulkan = 1 << 8, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index 146a9b500..7ab20e329 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -22,40 +22,40 @@ namespace Ryujinx.Ui.Common.Configuration { public class Columns { - public ReactiveObject<bool> FavColumn { get; private set; } - public ReactiveObject<bool> IconColumn { get; private set; } - public ReactiveObject<bool> AppColumn { get; private set; } - public ReactiveObject<bool> DevColumn { get; private set; } - public ReactiveObject<bool> VersionColumn { get; private set; } + public ReactiveObject<bool> FavColumn { get; private set; } + public ReactiveObject<bool> IconColumn { get; private set; } + public ReactiveObject<bool> AppColumn { get; private set; } + public ReactiveObject<bool> DevColumn { get; private set; } + public ReactiveObject<bool> VersionColumn { get; private set; } public ReactiveObject<bool> TimePlayedColumn { get; private set; } public ReactiveObject<bool> LastPlayedColumn { get; private set; } - public ReactiveObject<bool> FileExtColumn { get; private set; } - public ReactiveObject<bool> FileSizeColumn { get; private set; } - public ReactiveObject<bool> PathColumn { get; private set; } + public ReactiveObject<bool> FileExtColumn { get; private set; } + public ReactiveObject<bool> FileSizeColumn { get; private set; } + public ReactiveObject<bool> PathColumn { get; private set; } public Columns() { - FavColumn = new ReactiveObject<bool>(); - IconColumn = new ReactiveObject<bool>(); - AppColumn = new ReactiveObject<bool>(); - DevColumn = new ReactiveObject<bool>(); - VersionColumn = new ReactiveObject<bool>(); + FavColumn = new ReactiveObject<bool>(); + IconColumn = new ReactiveObject<bool>(); + AppColumn = new ReactiveObject<bool>(); + DevColumn = new ReactiveObject<bool>(); + VersionColumn = new ReactiveObject<bool>(); TimePlayedColumn = new ReactiveObject<bool>(); LastPlayedColumn = new ReactiveObject<bool>(); - FileExtColumn = new ReactiveObject<bool>(); - FileSizeColumn = new ReactiveObject<bool>(); - PathColumn = new ReactiveObject<bool>(); + FileExtColumn = new ReactiveObject<bool>(); + FileSizeColumn = new ReactiveObject<bool>(); + PathColumn = new ReactiveObject<bool>(); } } public class ColumnSortSettings { - public ReactiveObject<int> SortColumnId { get; private set; } + public ReactiveObject<int> SortColumnId { get; private set; } public ReactiveObject<bool> SortAscending { get; private set; } public ColumnSortSettings() { - SortColumnId = new ReactiveObject<int>(); + SortColumnId = new ReactiveObject<int>(); SortAscending = new ReactiveObject<bool>(); } } @@ -74,12 +74,12 @@ namespace Ryujinx.Ui.Common.Configuration public ShownFileTypeSettings() { - NSP = new ReactiveObject<bool>(); + NSP = new ReactiveObject<bool>(); PFS0 = new ReactiveObject<bool>(); - XCI = new ReactiveObject<bool>(); - NCA = new ReactiveObject<bool>(); - NRO = new ReactiveObject<bool>(); - NSO = new ReactiveObject<bool>(); + XCI = new ReactiveObject<bool>(); + NCA = new ReactiveObject<bool>(); + NRO = new ReactiveObject<bool>(); + NSO = new ReactiveObject<bool>(); } } @@ -186,22 +186,22 @@ namespace Ryujinx.Ui.Common.Configuration public UiSection() { - GuiColumns = new Columns(); - ColumnSort = new ColumnSortSettings(); - GameDirs = new ReactiveObject<List<string>>(); - ShownFileTypes = new ShownFileTypeSettings(); - WindowStartup = new WindowStartupSettings(); + GuiColumns = new Columns(); + ColumnSort = new ColumnSortSettings(); + GameDirs = new ReactiveObject<List<string>>(); + ShownFileTypes = new ShownFileTypeSettings(); + WindowStartup = new WindowStartupSettings(); EnableCustomTheme = new ReactiveObject<bool>(); - CustomThemePath = new ReactiveObject<string>(); - BaseStyle = new ReactiveObject<string>(); - StartFullscreen = new ReactiveObject<bool>(); - GameListViewMode = new ReactiveObject<int>(); - ShowNames = new ReactiveObject<bool>(); - GridSize = new ReactiveObject<int>(); - ApplicationSort = new ReactiveObject<int>(); - IsAscendingOrder = new ReactiveObject<bool>(); - LanguageCode = new ReactiveObject<string>(); - ShowConsole = new ReactiveObject<bool>(); + CustomThemePath = new ReactiveObject<string>(); + BaseStyle = new ReactiveObject<string>(); + StartFullscreen = new ReactiveObject<bool>(); + GameListViewMode = new ReactiveObject<int>(); + ShowNames = new ReactiveObject<bool>(); + GridSize = new ReactiveObject<int>(); + ApplicationSort = new ReactiveObject<int>(); + IsAscendingOrder = new ReactiveObject<bool>(); + LanguageCode = new ReactiveObject<string>(); + ShowConsole = new ReactiveObject<bool>(); ShowConsole.Event += static (s, e) => { ConsoleHelper.SetConsoleWindowState(e.NewValue); }; } } @@ -268,18 +268,18 @@ namespace Ryujinx.Ui.Common.Configuration public LoggerSection() { - EnableDebug = new ReactiveObject<bool>(); - EnableStub = new ReactiveObject<bool>(); - EnableInfo = new ReactiveObject<bool>(); - EnableWarn = new ReactiveObject<bool>(); - EnableError = new ReactiveObject<bool>(); - EnableTrace = new ReactiveObject<bool>(); - EnableGuest = new ReactiveObject<bool>(); - EnableFsAccessLog = new ReactiveObject<bool>(); - FilteredClasses = new ReactiveObject<LogClass[]>(); - EnableFileLog = new ReactiveObject<bool>(); - EnableFileLog.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableFileLog)); - GraphicsDebugLevel = new ReactiveObject<GraphicsDebugLevel>(); + EnableDebug = new ReactiveObject<bool>(); + EnableStub = new ReactiveObject<bool>(); + EnableInfo = new ReactiveObject<bool>(); + EnableWarn = new ReactiveObject<bool>(); + EnableError = new ReactiveObject<bool>(); + EnableTrace = new ReactiveObject<bool>(); + EnableGuest = new ReactiveObject<bool>(); + EnableFsAccessLog = new ReactiveObject<bool>(); + FilteredClasses = new ReactiveObject<LogClass[]>(); + EnableFileLog = new ReactiveObject<bool>(); + EnableFileLog.Event += static (sender, e) => LogValueChange(e, nameof(EnableFileLog)); + GraphicsDebugLevel = new ReactiveObject<GraphicsDebugLevel>(); } } @@ -365,32 +365,32 @@ namespace Ryujinx.Ui.Common.Configuration public SystemSection() { - Language = new ReactiveObject<Language>(); - Region = new ReactiveObject<Region>(); - TimeZone = new ReactiveObject<string>(); - SystemTimeOffset = new ReactiveObject<long>(); - EnableDockedMode = new ReactiveObject<bool>(); - EnableDockedMode.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableDockedMode)); - EnablePtc = new ReactiveObject<bool>(); - EnablePtc.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnablePtc)); - EnableInternetAccess = new ReactiveObject<bool>(); - EnableInternetAccess.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableInternetAccess)); - EnableFsIntegrityChecks = new ReactiveObject<bool>(); - EnableFsIntegrityChecks.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableFsIntegrityChecks)); - FsGlobalAccessLogMode = new ReactiveObject<int>(); - FsGlobalAccessLogMode.Event += static (sender, e) => LogValueChange(sender, e, nameof(FsGlobalAccessLogMode)); - AudioBackend = new ReactiveObject<AudioBackend>(); - AudioBackend.Event += static (sender, e) => LogValueChange(sender, e, nameof(AudioBackend)); - MemoryManagerMode = new ReactiveObject<MemoryManagerMode>(); - MemoryManagerMode.Event += static (sender, e) => LogValueChange(sender, e, nameof(MemoryManagerMode)); - ExpandRam = new ReactiveObject<bool>(); - ExpandRam.Event += static (sender, e) => LogValueChange(sender, e, nameof(ExpandRam)); - IgnoreMissingServices = new ReactiveObject<bool>(); - IgnoreMissingServices.Event += static (sender, e) => LogValueChange(sender, e, nameof(IgnoreMissingServices)); - AudioVolume = new ReactiveObject<float>(); - AudioVolume.Event += static (sender, e) => LogValueChange(sender, e, nameof(AudioVolume)); - UseHypervisor = new ReactiveObject<bool>(); - UseHypervisor.Event += static (sender, e) => LogValueChange(sender, e, nameof(UseHypervisor)); + Language = new ReactiveObject<Language>(); + Region = new ReactiveObject<Region>(); + TimeZone = new ReactiveObject<string>(); + SystemTimeOffset = new ReactiveObject<long>(); + EnableDockedMode = new ReactiveObject<bool>(); + EnableDockedMode.Event += static (sender, e) => LogValueChange(e, nameof(EnableDockedMode)); + EnablePtc = new ReactiveObject<bool>(); + EnablePtc.Event += static (sender, e) => LogValueChange(e, nameof(EnablePtc)); + EnableInternetAccess = new ReactiveObject<bool>(); + EnableInternetAccess.Event += static (sender, e) => LogValueChange(e, nameof(EnableInternetAccess)); + EnableFsIntegrityChecks = new ReactiveObject<bool>(); + EnableFsIntegrityChecks.Event += static (sender, e) => LogValueChange(e, nameof(EnableFsIntegrityChecks)); + FsGlobalAccessLogMode = new ReactiveObject<int>(); + FsGlobalAccessLogMode.Event += static (sender, e) => LogValueChange(e, nameof(FsGlobalAccessLogMode)); + AudioBackend = new ReactiveObject<AudioBackend>(); + AudioBackend.Event += static (sender, e) => LogValueChange(e, nameof(AudioBackend)); + MemoryManagerMode = new ReactiveObject<MemoryManagerMode>(); + MemoryManagerMode.Event += static (sender, e) => LogValueChange(e, nameof(MemoryManagerMode)); + ExpandRam = new ReactiveObject<bool>(); + ExpandRam.Event += static (sender, e) => LogValueChange(e, nameof(ExpandRam)); + IgnoreMissingServices = new ReactiveObject<bool>(); + IgnoreMissingServices.Event += static (sender, e) => LogValueChange(e, nameof(IgnoreMissingServices)); + AudioVolume = new ReactiveObject<float>(); + AudioVolume.Event += static (sender, e) => LogValueChange(e, nameof(AudioVolume)); + UseHypervisor = new ReactiveObject<bool>(); + UseHypervisor.Event += static (sender, e) => LogValueChange(e, nameof(UseHypervisor)); } } @@ -424,9 +424,9 @@ namespace Ryujinx.Ui.Common.Configuration public HidSection() { EnableKeyboard = new ReactiveObject<bool>(); - EnableMouse = new ReactiveObject<bool>(); - Hotkeys = new ReactiveObject<KeyboardHotkeys>(); - InputConfig = new ReactiveObject<List<InputConfig>>(); + EnableMouse = new ReactiveObject<bool>(); + Hotkeys = new ReactiveObject<KeyboardHotkeys>(); + InputConfig = new ReactiveObject<List<InputConfig>>(); } } @@ -512,35 +512,35 @@ namespace Ryujinx.Ui.Common.Configuration public GraphicsSection() { - BackendThreading = new ReactiveObject<BackendThreading>(); - BackendThreading.Event += static (sender, e) => LogValueChange(sender, e, nameof(BackendThreading)); - ResScale = new ReactiveObject<int>(); - ResScale.Event += static (sender, e) => LogValueChange(sender, e, nameof(ResScale)); - ResScaleCustom = new ReactiveObject<float>(); - ResScaleCustom.Event += static (sender, e) => LogValueChange(sender, e, nameof(ResScaleCustom)); - MaxAnisotropy = new ReactiveObject<float>(); - MaxAnisotropy.Event += static (sender, e) => LogValueChange(sender, e, nameof(MaxAnisotropy)); - AspectRatio = new ReactiveObject<AspectRatio>(); - AspectRatio.Event += static (sender, e) => LogValueChange(sender, e, nameof(AspectRatio)); - ShadersDumpPath = new ReactiveObject<string>(); - EnableVsync = new ReactiveObject<bool>(); - EnableVsync.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableVsync)); - EnableShaderCache = new ReactiveObject<bool>(); - EnableShaderCache.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableShaderCache)); - EnableTextureRecompression = new ReactiveObject<bool>(); - EnableTextureRecompression.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableTextureRecompression)); - GraphicsBackend = new ReactiveObject<GraphicsBackend>(); - GraphicsBackend.Event += static (sender, e) => LogValueChange(sender, e, nameof(GraphicsBackend)); - PreferredGpu = new ReactiveObject<string>(); - PreferredGpu.Event += static (sender, e) => LogValueChange(sender, e, nameof(PreferredGpu)); - EnableMacroHLE = new ReactiveObject<bool>(); - EnableMacroHLE.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableMacroHLE)); - AntiAliasing = new ReactiveObject<AntiAliasing>(); - AntiAliasing.Event += static (sender, e) => LogValueChange(sender, e, nameof(AntiAliasing)); - ScalingFilter = new ReactiveObject<ScalingFilter>(); - ScalingFilter.Event += static (sender, e) => LogValueChange(sender, e, nameof(ScalingFilter)); - ScalingFilterLevel = new ReactiveObject<int>(); - ScalingFilterLevel.Event += static (sender, e) => LogValueChange(sender, e, nameof(ScalingFilterLevel)); + BackendThreading = new ReactiveObject<BackendThreading>(); + BackendThreading.Event += static (sender, e) => LogValueChange(e, nameof(BackendThreading)); + ResScale = new ReactiveObject<int>(); + ResScale.Event += static (sender, e) => LogValueChange(e, nameof(ResScale)); + ResScaleCustom = new ReactiveObject<float>(); + ResScaleCustom.Event += static (sender, e) => LogValueChange(e, nameof(ResScaleCustom)); + MaxAnisotropy = new ReactiveObject<float>(); + MaxAnisotropy.Event += static (sender, e) => LogValueChange(e, nameof(MaxAnisotropy)); + AspectRatio = new ReactiveObject<AspectRatio>(); + AspectRatio.Event += static (sender, e) => LogValueChange(e, nameof(AspectRatio)); + ShadersDumpPath = new ReactiveObject<string>(); + EnableVsync = new ReactiveObject<bool>(); + EnableVsync.Event += static (sender, e) => LogValueChange(e, nameof(EnableVsync)); + EnableShaderCache = new ReactiveObject<bool>(); + EnableShaderCache.Event += static (sender, e) => LogValueChange(e, nameof(EnableShaderCache)); + EnableTextureRecompression = new ReactiveObject<bool>(); + EnableTextureRecompression.Event += static (sender, e) => LogValueChange(e, nameof(EnableTextureRecompression)); + GraphicsBackend = new ReactiveObject<GraphicsBackend>(); + GraphicsBackend.Event += static (sender, e) => LogValueChange(e, nameof(GraphicsBackend)); + PreferredGpu = new ReactiveObject<string>(); + PreferredGpu.Event += static (sender, e) => LogValueChange(e, nameof(PreferredGpu)); + EnableMacroHLE = new ReactiveObject<bool>(); + EnableMacroHLE.Event += static (sender, e) => LogValueChange(e, nameof(EnableMacroHLE)); + AntiAliasing = new ReactiveObject<AntiAliasing>(); + AntiAliasing.Event += static (sender, e) => LogValueChange(e, nameof(AntiAliasing)); + ScalingFilter = new ReactiveObject<ScalingFilter>(); + ScalingFilter.Event += static (sender, e) => LogValueChange(e, nameof(ScalingFilter)); + ScalingFilterLevel = new ReactiveObject<int>(); + ScalingFilterLevel.Event += static (sender, e) => LogValueChange(e, nameof(ScalingFilterLevel)); } } @@ -617,86 +617,86 @@ namespace Ryujinx.Ui.Common.Configuration private ConfigurationState() { - Ui = new UiSection(); - Logger = new LoggerSection(); - System = new SystemSection(); - Graphics = new GraphicsSection(); - Hid = new HidSection(); - Multiplayer = new MultiplayerSection(); + Ui = new UiSection(); + Logger = new LoggerSection(); + System = new SystemSection(); + Graphics = new GraphicsSection(); + Hid = new HidSection(); + Multiplayer = new MultiplayerSection(); EnableDiscordIntegration = new ReactiveObject<bool>(); - CheckUpdatesOnStart = new ReactiveObject<bool>(); - ShowConfirmExit = new ReactiveObject<bool>(); - HideCursor = new ReactiveObject<HideCursorMode>(); + CheckUpdatesOnStart = new ReactiveObject<bool>(); + ShowConfirmExit = new ReactiveObject<bool>(); + HideCursor = new ReactiveObject<HideCursorMode>(); } public ConfigurationFileFormat ToFileFormat() { - ConfigurationFileFormat configurationFile = new ConfigurationFileFormat + ConfigurationFileFormat configurationFile = new() { - Version = ConfigurationFileFormat.CurrentVersion, - BackendThreading = Graphics.BackendThreading, - EnableFileLog = Logger.EnableFileLog, - ResScale = Graphics.ResScale, - ResScaleCustom = Graphics.ResScaleCustom, - MaxAnisotropy = Graphics.MaxAnisotropy, - AspectRatio = Graphics.AspectRatio, - AntiAliasing = Graphics.AntiAliasing, - ScalingFilter = Graphics.ScalingFilter, - ScalingFilterLevel = Graphics.ScalingFilterLevel, - GraphicsShadersDumpPath = Graphics.ShadersDumpPath, - LoggingEnableDebug = Logger.EnableDebug, - LoggingEnableStub = Logger.EnableStub, - LoggingEnableInfo = Logger.EnableInfo, - LoggingEnableWarn = Logger.EnableWarn, - LoggingEnableError = Logger.EnableError, - LoggingEnableTrace = Logger.EnableTrace, - LoggingEnableGuest = Logger.EnableGuest, - LoggingEnableFsAccessLog = Logger.EnableFsAccessLog, - LoggingFilteredClasses = Logger.FilteredClasses, - LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel, - SystemLanguage = System.Language, - SystemRegion = System.Region, - SystemTimeZone = System.TimeZone, - SystemTimeOffset = System.SystemTimeOffset, - DockedMode = System.EnableDockedMode, - EnableDiscordIntegration = EnableDiscordIntegration, - CheckUpdatesOnStart = CheckUpdatesOnStart, - ShowConfirmExit = ShowConfirmExit, - HideCursor = HideCursor, - EnableVsync = Graphics.EnableVsync, - EnableShaderCache = Graphics.EnableShaderCache, + Version = ConfigurationFileFormat.CurrentVersion, + BackendThreading = Graphics.BackendThreading, + EnableFileLog = Logger.EnableFileLog, + ResScale = Graphics.ResScale, + ResScaleCustom = Graphics.ResScaleCustom, + MaxAnisotropy = Graphics.MaxAnisotropy, + AspectRatio = Graphics.AspectRatio, + AntiAliasing = Graphics.AntiAliasing, + ScalingFilter = Graphics.ScalingFilter, + ScalingFilterLevel = Graphics.ScalingFilterLevel, + GraphicsShadersDumpPath = Graphics.ShadersDumpPath, + LoggingEnableDebug = Logger.EnableDebug, + LoggingEnableStub = Logger.EnableStub, + LoggingEnableInfo = Logger.EnableInfo, + LoggingEnableWarn = Logger.EnableWarn, + LoggingEnableError = Logger.EnableError, + LoggingEnableTrace = Logger.EnableTrace, + LoggingEnableGuest = Logger.EnableGuest, + LoggingEnableFsAccessLog = Logger.EnableFsAccessLog, + LoggingFilteredClasses = Logger.FilteredClasses, + LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel, + SystemLanguage = System.Language, + SystemRegion = System.Region, + SystemTimeZone = System.TimeZone, + SystemTimeOffset = System.SystemTimeOffset, + DockedMode = System.EnableDockedMode, + EnableDiscordIntegration = EnableDiscordIntegration, + CheckUpdatesOnStart = CheckUpdatesOnStart, + ShowConfirmExit = ShowConfirmExit, + HideCursor = HideCursor, + EnableVsync = Graphics.EnableVsync, + EnableShaderCache = Graphics.EnableShaderCache, EnableTextureRecompression = Graphics.EnableTextureRecompression, - EnableMacroHLE = Graphics.EnableMacroHLE, - EnablePtc = System.EnablePtc, - EnableInternetAccess = System.EnableInternetAccess, - EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, - FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, - AudioBackend = System.AudioBackend, - AudioVolume = System.AudioVolume, - MemoryManagerMode = System.MemoryManagerMode, - ExpandRam = System.ExpandRam, - IgnoreMissingServices = System.IgnoreMissingServices, - UseHypervisor = System.UseHypervisor, - GuiColumns = new GuiColumns + EnableMacroHLE = Graphics.EnableMacroHLE, + EnablePtc = System.EnablePtc, + EnableInternetAccess = System.EnableInternetAccess, + EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, + FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, + AudioBackend = System.AudioBackend, + AudioVolume = System.AudioVolume, + MemoryManagerMode = System.MemoryManagerMode, + ExpandRam = System.ExpandRam, + IgnoreMissingServices = System.IgnoreMissingServices, + UseHypervisor = System.UseHypervisor, + GuiColumns = new GuiColumns { - FavColumn = Ui.GuiColumns.FavColumn, - IconColumn = Ui.GuiColumns.IconColumn, - AppColumn = Ui.GuiColumns.AppColumn, - DevColumn = Ui.GuiColumns.DevColumn, - VersionColumn = Ui.GuiColumns.VersionColumn, + FavColumn = Ui.GuiColumns.FavColumn, + IconColumn = Ui.GuiColumns.IconColumn, + AppColumn = Ui.GuiColumns.AppColumn, + DevColumn = Ui.GuiColumns.DevColumn, + VersionColumn = Ui.GuiColumns.VersionColumn, TimePlayedColumn = Ui.GuiColumns.TimePlayedColumn, LastPlayedColumn = Ui.GuiColumns.LastPlayedColumn, - FileExtColumn = Ui.GuiColumns.FileExtColumn, - FileSizeColumn = Ui.GuiColumns.FileSizeColumn, - PathColumn = Ui.GuiColumns.PathColumn + FileExtColumn = Ui.GuiColumns.FileExtColumn, + FileSizeColumn = Ui.GuiColumns.FileSizeColumn, + PathColumn = Ui.GuiColumns.PathColumn, }, - ColumnSort = new ColumnSort + ColumnSort = new ColumnSort { - SortColumnId = Ui.ColumnSort.SortColumnId, - SortAscending = Ui.ColumnSort.SortAscending + SortColumnId = Ui.ColumnSort.SortColumnId, + SortAscending = Ui.ColumnSort.SortAscending, }, - GameDirs = Ui.GameDirs, - ShownFileTypes = new ShownFileTypes + GameDirs = Ui.GameDirs, + ShownFileTypes = new ShownFileTypes { NSP = Ui.ShownFileTypes.NSP, PFS0 = Ui.ShownFileTypes.PFS0, @@ -705,7 +705,7 @@ namespace Ryujinx.Ui.Common.Configuration NRO = Ui.ShownFileTypes.NRO, NSO = Ui.ShownFileTypes.NSO, }, - WindowStartup = new WindowStartup + WindowStartup = new WindowStartup { WindowSizeWidth = Ui.WindowStartup.WindowSizeWidth, WindowSizeHeight = Ui.WindowStartup.WindowSizeHeight, @@ -713,26 +713,26 @@ namespace Ryujinx.Ui.Common.Configuration WindowPositionY = Ui.WindowStartup.WindowPositionY, WindowMaximized = Ui.WindowStartup.WindowMaximized, }, - LanguageCode = Ui.LanguageCode, - EnableCustomTheme = Ui.EnableCustomTheme, - CustomThemePath = Ui.CustomThemePath, - BaseStyle = Ui.BaseStyle, - GameListViewMode = Ui.GameListViewMode, - ShowNames = Ui.ShowNames, - GridSize = Ui.GridSize, - ApplicationSort = Ui.ApplicationSort, - IsAscendingOrder = Ui.IsAscendingOrder, - StartFullscreen = Ui.StartFullscreen, - ShowConsole = Ui.ShowConsole, - EnableKeyboard = Hid.EnableKeyboard, - EnableMouse = Hid.EnableMouse, - Hotkeys = Hid.Hotkeys, - KeyboardConfig = new List<JsonObject>(), - ControllerConfig = new List<JsonObject>(), - InputConfig = Hid.InputConfig, - GraphicsBackend = Graphics.GraphicsBackend, - PreferredGpu = Graphics.PreferredGpu, - MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId + LanguageCode = Ui.LanguageCode, + EnableCustomTheme = Ui.EnableCustomTheme, + CustomThemePath = Ui.CustomThemePath, + BaseStyle = Ui.BaseStyle, + GameListViewMode = Ui.GameListViewMode, + ShowNames = Ui.ShowNames, + GridSize = Ui.GridSize, + ApplicationSort = Ui.ApplicationSort, + IsAscendingOrder = Ui.IsAscendingOrder, + StartFullscreen = Ui.StartFullscreen, + ShowConsole = Ui.ShowConsole, + EnableKeyboard = Hid.EnableKeyboard, + EnableMouse = Hid.EnableMouse, + Hotkeys = Hid.Hotkeys, + KeyboardConfig = new List<JsonObject>(), + ControllerConfig = new List<JsonObject>(), + InputConfig = Hid.InputConfig, + GraphicsBackend = Graphics.GraphicsBackend, + PreferredGpu = Graphics.PreferredGpu, + MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId, }; return configurationFile; @@ -740,89 +740,89 @@ namespace Ryujinx.Ui.Common.Configuration public void LoadDefault() { - Logger.EnableFileLog.Value = true; - Graphics.BackendThreading.Value = BackendThreading.Auto; - Graphics.ResScale.Value = 1; - Graphics.ResScaleCustom.Value = 1.0f; - Graphics.MaxAnisotropy.Value = -1.0f; - Graphics.AspectRatio.Value = AspectRatio.Fixed16x9; - Graphics.GraphicsBackend.Value = OperatingSystem.IsMacOS() ? GraphicsBackend.Vulkan : GraphicsBackend.OpenGl; - Graphics.PreferredGpu.Value = ""; - Graphics.ShadersDumpPath.Value = ""; - Logger.EnableDebug.Value = false; - Logger.EnableStub.Value = true; - Logger.EnableInfo.Value = true; - Logger.EnableWarn.Value = true; - Logger.EnableError.Value = true; - Logger.EnableTrace.Value = false; - Logger.EnableGuest.Value = true; - Logger.EnableFsAccessLog.Value = false; - Logger.FilteredClasses.Value = Array.Empty<LogClass>(); - Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None; - System.Language.Value = Language.AmericanEnglish; - System.Region.Value = Region.USA; - System.TimeZone.Value = "UTC"; - System.SystemTimeOffset.Value = 0; - System.EnableDockedMode.Value = true; - EnableDiscordIntegration.Value = true; - CheckUpdatesOnStart.Value = true; - ShowConfirmExit.Value = true; - HideCursor.Value = Ryujinx.Common.Configuration.HideCursorMode.Never; - Graphics.EnableVsync.Value = true; - Graphics.EnableShaderCache.Value = true; + Logger.EnableFileLog.Value = true; + Graphics.BackendThreading.Value = BackendThreading.Auto; + Graphics.ResScale.Value = 1; + Graphics.ResScaleCustom.Value = 1.0f; + Graphics.MaxAnisotropy.Value = -1.0f; + Graphics.AspectRatio.Value = AspectRatio.Fixed16x9; + Graphics.GraphicsBackend.Value = OperatingSystem.IsMacOS() ? GraphicsBackend.Vulkan : GraphicsBackend.OpenGl; + Graphics.PreferredGpu.Value = ""; + Graphics.ShadersDumpPath.Value = ""; + Logger.EnableDebug.Value = false; + Logger.EnableStub.Value = true; + Logger.EnableInfo.Value = true; + Logger.EnableWarn.Value = true; + Logger.EnableError.Value = true; + Logger.EnableTrace.Value = false; + Logger.EnableGuest.Value = true; + Logger.EnableFsAccessLog.Value = false; + Logger.FilteredClasses.Value = Array.Empty<LogClass>(); + Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None; + System.Language.Value = Language.AmericanEnglish; + System.Region.Value = Region.USA; + System.TimeZone.Value = "UTC"; + System.SystemTimeOffset.Value = 0; + System.EnableDockedMode.Value = true; + EnableDiscordIntegration.Value = true; + CheckUpdatesOnStart.Value = true; + ShowConfirmExit.Value = true; + HideCursor.Value = HideCursorMode.Never; + Graphics.EnableVsync.Value = true; + Graphics.EnableShaderCache.Value = true; Graphics.EnableTextureRecompression.Value = false; - Graphics.EnableMacroHLE.Value = true; - Graphics.AntiAliasing.Value = AntiAliasing.None; - Graphics.ScalingFilter.Value = ScalingFilter.Bilinear; - Graphics.ScalingFilterLevel.Value = 80; - System.EnablePtc.Value = true; - System.EnableInternetAccess.Value = false; - System.EnableFsIntegrityChecks.Value = true; - System.FsGlobalAccessLogMode.Value = 0; - System.AudioBackend.Value = AudioBackend.SDL2; - System.AudioVolume.Value = 1; - System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe; - System.ExpandRam.Value = false; - System.IgnoreMissingServices.Value = false; - System.UseHypervisor.Value = true; - Multiplayer.LanInterfaceId.Value = "0"; - Ui.GuiColumns.FavColumn.Value = true; - Ui.GuiColumns.IconColumn.Value = true; - Ui.GuiColumns.AppColumn.Value = true; - Ui.GuiColumns.DevColumn.Value = true; - Ui.GuiColumns.VersionColumn.Value = true; - Ui.GuiColumns.TimePlayedColumn.Value = true; - Ui.GuiColumns.LastPlayedColumn.Value = true; - Ui.GuiColumns.FileExtColumn.Value = true; - Ui.GuiColumns.FileSizeColumn.Value = true; - Ui.GuiColumns.PathColumn.Value = true; - Ui.ColumnSort.SortColumnId.Value = 0; - Ui.ColumnSort.SortAscending.Value = false; - Ui.GameDirs.Value = new List<string>(); - Ui.ShownFileTypes.NSP.Value = true; - Ui.ShownFileTypes.PFS0.Value = true; - Ui.ShownFileTypes.XCI.Value = true; - Ui.ShownFileTypes.NCA.Value = true; - Ui.ShownFileTypes.NRO.Value = true; - Ui.ShownFileTypes.NSO.Value = true; - Ui.EnableCustomTheme.Value = true; - Ui.LanguageCode.Value = "en_US"; - Ui.CustomThemePath.Value = ""; - Ui.BaseStyle.Value = "Dark"; - Ui.GameListViewMode.Value = 0; - Ui.ShowNames.Value = true; - Ui.GridSize.Value = 2; - Ui.ApplicationSort.Value = 0; - Ui.IsAscendingOrder.Value = true; - Ui.StartFullscreen.Value = false; - Ui.ShowConsole.Value = true; - Ui.WindowStartup.WindowSizeWidth.Value = 1280; - Ui.WindowStartup.WindowSizeHeight.Value = 760; - Ui.WindowStartup.WindowPositionX.Value = 0; - Ui.WindowStartup.WindowPositionY.Value = 0; - Ui.WindowStartup.WindowMaximized.Value = false; - Hid.EnableKeyboard.Value = false; - Hid.EnableMouse.Value = false; + Graphics.EnableMacroHLE.Value = true; + Graphics.AntiAliasing.Value = AntiAliasing.None; + Graphics.ScalingFilter.Value = ScalingFilter.Bilinear; + Graphics.ScalingFilterLevel.Value = 80; + System.EnablePtc.Value = true; + System.EnableInternetAccess.Value = false; + System.EnableFsIntegrityChecks.Value = true; + System.FsGlobalAccessLogMode.Value = 0; + System.AudioBackend.Value = AudioBackend.SDL2; + System.AudioVolume.Value = 1; + System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe; + System.ExpandRam.Value = false; + System.IgnoreMissingServices.Value = false; + System.UseHypervisor.Value = true; + Multiplayer.LanInterfaceId.Value = "0"; + Ui.GuiColumns.FavColumn.Value = true; + Ui.GuiColumns.IconColumn.Value = true; + Ui.GuiColumns.AppColumn.Value = true; + Ui.GuiColumns.DevColumn.Value = true; + Ui.GuiColumns.VersionColumn.Value = true; + Ui.GuiColumns.TimePlayedColumn.Value = true; + Ui.GuiColumns.LastPlayedColumn.Value = true; + Ui.GuiColumns.FileExtColumn.Value = true; + Ui.GuiColumns.FileSizeColumn.Value = true; + Ui.GuiColumns.PathColumn.Value = true; + Ui.ColumnSort.SortColumnId.Value = 0; + Ui.ColumnSort.SortAscending.Value = false; + Ui.GameDirs.Value = new List<string>(); + Ui.ShownFileTypes.NSP.Value = true; + Ui.ShownFileTypes.PFS0.Value = true; + Ui.ShownFileTypes.XCI.Value = true; + Ui.ShownFileTypes.NCA.Value = true; + Ui.ShownFileTypes.NRO.Value = true; + Ui.ShownFileTypes.NSO.Value = true; + Ui.EnableCustomTheme.Value = true; + Ui.LanguageCode.Value = "en_US"; + Ui.CustomThemePath.Value = ""; + Ui.BaseStyle.Value = "Dark"; + Ui.GameListViewMode.Value = 0; + Ui.ShowNames.Value = true; + Ui.GridSize.Value = 2; + Ui.ApplicationSort.Value = 0; + Ui.IsAscendingOrder.Value = true; + Ui.StartFullscreen.Value = false; + Ui.ShowConsole.Value = true; + Ui.WindowStartup.WindowSizeWidth.Value = 1280; + Ui.WindowStartup.WindowSizeHeight.Value = 760; + Ui.WindowStartup.WindowPositionX.Value = 0; + Ui.WindowStartup.WindowPositionY.Value = 0; + Ui.WindowStartup.WindowMaximized.Value = false; + Hid.EnableKeyboard.Value = false; + Hid.EnableMouse.Value = false; Hid.Hotkeys.Value = new KeyboardHotkeys { ToggleVsync = Key.F1, @@ -833,61 +833,58 @@ namespace Ryujinx.Ui.Common.Configuration ResScaleUp = Key.Unbound, ResScaleDown = Key.Unbound, VolumeUp = Key.Unbound, - VolumeDown = Key.Unbound + VolumeDown = Key.Unbound, }; Hid.InputConfig.Value = new List<InputConfig> { - new StandardKeyboardInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = "0", - PlayerIndex = PlayerIndex.Player1, - ControllerType = ControllerType.JoyconPair, - LeftJoycon = new LeftJoyconCommonConfig<Key> - { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound - }, - - LeftJoyconStick = new JoyconConfigKeyboardStick<Key> - { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, - }, - - RightJoycon = new RightJoyconCommonConfig<Key> - { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound - }, - - RightJoyconStick = new JoyconConfigKeyboardStick<Key> - { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - } - } + new StandardKeyboardInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.WindowKeyboard, + Id = "0", + PlayerIndex = PlayerIndex.Player1, + ControllerType = ControllerType.JoyconPair, + LeftJoycon = new LeftJoyconCommonConfig<Key> + { + DpadUp = Key.Up, + DpadDown = Key.Down, + DpadLeft = Key.Left, + DpadRight = Key.Right, + ButtonMinus = Key.Minus, + ButtonL = Key.E, + ButtonZl = Key.Q, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + LeftJoyconStick = new JoyconConfigKeyboardStick<Key> + { + StickUp = Key.W, + StickDown = Key.S, + StickLeft = Key.A, + StickRight = Key.D, + StickButton = Key.F, + }, + RightJoycon = new RightJoyconCommonConfig<Key> + { + ButtonA = Key.Z, + ButtonB = Key.X, + ButtonX = Key.C, + ButtonY = Key.V, + ButtonPlus = Key.Plus, + ButtonR = Key.U, + ButtonZr = Key.O, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + RightJoyconStick = new JoyconConfigKeyboardStick<Key> + { + StickUp = Key.I, + StickDown = Key.K, + StickLeft = Key.J, + StickRight = Key.L, + StickButton = Key.H, + }, + }, }; } @@ -957,13 +954,13 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.ColumnSort = new ColumnSort { - SortColumnId = 0, - SortAscending = false + SortColumnId = 0, + SortAscending = false, }; configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = Key.F1 + ToggleVsync = Key.F1, }; configurationFileUpdated = true; @@ -1057,57 +1054,54 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.InputConfig = new List<InputConfig> { - new StandardKeyboardInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = "0", - PlayerIndex = PlayerIndex.Player1, - ControllerType = ControllerType.JoyconPair, - LeftJoycon = new LeftJoyconCommonConfig<Key> - { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound - }, - - LeftJoyconStick = new JoyconConfigKeyboardStick<Key> - { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, - }, - - RightJoycon = new RightJoyconCommonConfig<Key> - { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound - }, - - RightJoyconStick = new JoyconConfigKeyboardStick<Key> - { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - } - } + new StandardKeyboardInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.WindowKeyboard, + Id = "0", + PlayerIndex = PlayerIndex.Player1, + ControllerType = ControllerType.JoyconPair, + LeftJoycon = new LeftJoyconCommonConfig<Key> + { + DpadUp = Key.Up, + DpadDown = Key.Down, + DpadLeft = Key.Left, + DpadRight = Key.Right, + ButtonMinus = Key.Minus, + ButtonL = Key.E, + ButtonZl = Key.Q, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + LeftJoyconStick = new JoyconConfigKeyboardStick<Key> + { + StickUp = Key.W, + StickDown = Key.S, + StickLeft = Key.A, + StickRight = Key.D, + StickButton = Key.F, + }, + RightJoycon = new RightJoyconCommonConfig<Key> + { + ButtonA = Key.Z, + ButtonB = Key.X, + ButtonX = Key.C, + ButtonY = Key.V, + ButtonPlus = Key.Plus, + ButtonR = Key.U, + ButtonZr = Key.O, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + RightJoyconStick = new JoyconConfigKeyboardStick<Key> + { + StickUp = Key.I, + StickDown = Key.K, + StickLeft = Key.J, + StickRight = Key.L, + StickButton = Key.H, + }, + }, }; configurationFileUpdated = true; @@ -1145,7 +1139,7 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.Hotkeys = new KeyboardHotkeys { ToggleVsync = Key.F1, - Screenshot = Key.F8 + Screenshot = Key.F8, }; configurationFileUpdated = true; @@ -1159,7 +1153,7 @@ namespace Ryujinx.Ui.Common.Configuration { ToggleVsync = Key.F1, Screenshot = Key.F8, - ShowUi = Key.F4 + ShowUi = Key.F4, }; configurationFileUpdated = true; @@ -1169,7 +1163,7 @@ namespace Ryujinx.Ui.Common.Configuration { Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 30."); - foreach(InputConfig config in configurationFileFormat.InputConfig) + foreach (InputConfig config in configurationFileFormat.InputConfig) { if (config is StandardControllerInputConfig controllerConfig) { @@ -1177,7 +1171,7 @@ namespace Ryujinx.Ui.Common.Configuration { EnableRumble = false, StrongRumble = 1f, - WeakRumble = 1f + WeakRumble = 1f, }; } } @@ -1203,7 +1197,7 @@ namespace Ryujinx.Ui.Common.Configuration ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, - Pause = Key.F5 + Pause = Key.F5, }; configurationFileUpdated = true; @@ -1219,7 +1213,7 @@ namespace Ryujinx.Ui.Common.Configuration Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, Pause = configurationFileFormat.Hotkeys.Pause, - ToggleMute = Key.F2 + ToggleMute = Key.F2, }; configurationFileFormat.AudioVolume = 1; @@ -1244,7 +1238,7 @@ namespace Ryujinx.Ui.Common.Configuration { if (config is StandardControllerInputConfig controllerConfig) { - controllerConfig.RangeLeft = 1.0f; + controllerConfig.RangeLeft = 1.0f; controllerConfig.RangeRight = 1.0f; } } @@ -1274,11 +1268,11 @@ namespace Ryujinx.Ui.Common.Configuration { Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 38."); - configurationFileFormat.BaseStyle = "Dark"; + configurationFileFormat.BaseStyle = "Dark"; configurationFileFormat.GameListViewMode = 0; - configurationFileFormat.ShowNames = true; - configurationFileFormat.GridSize = 2; - configurationFileFormat.LanguageCode = "en_US"; + configurationFileFormat.ShowNames = true; + configurationFileFormat.GridSize = 2; + configurationFileFormat.LanguageCode = "en_US"; configurationFileUpdated = true; } @@ -1295,7 +1289,7 @@ namespace Ryujinx.Ui.Common.Configuration Pause = configurationFileFormat.Hotkeys.Pause, ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, ResScaleUp = Key.Unbound, - ResScaleDown = Key.Unbound + ResScaleDown = Key.Unbound, }; configurationFileUpdated = true; @@ -1326,7 +1320,7 @@ namespace Ryujinx.Ui.Common.Configuration ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp, ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown, VolumeUp = Key.Unbound, - VolumeDown = Key.Unbound + VolumeDown = Key.Unbound, }; } @@ -1361,12 +1355,12 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileFormat.ShownFileTypes = new ShownFileTypes { - NSP = true, + NSP = true, PFS0 = true, - XCI = true, - NCA = true, - NRO = true, - NSO = true + XCI = true, + NCA = true, + NRO = true, + NSO = true, }; configurationFileUpdated = true; @@ -1393,94 +1387,94 @@ namespace Ryujinx.Ui.Common.Configuration WindowSizeWidth = 1280, WindowMaximized = false, }; - + configurationFileUpdated = true; } - Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; - Graphics.ResScale.Value = configurationFileFormat.ResScale; - Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; - Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy; - Graphics.AspectRatio.Value = configurationFileFormat.AspectRatio; - Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath; - Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading; - Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend; - Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu; - Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing; - Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter; - Graphics.ScalingFilterLevel.Value = configurationFileFormat.ScalingFilterLevel; - Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug; - Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub; - Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo; - Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn; - Logger.EnableError.Value = configurationFileFormat.LoggingEnableError; - Logger.EnableTrace.Value = configurationFileFormat.LoggingEnableTrace; - Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest; - Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog; - Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses; - Logger.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel; - System.Language.Value = configurationFileFormat.SystemLanguage; - System.Region.Value = configurationFileFormat.SystemRegion; - System.TimeZone.Value = configurationFileFormat.SystemTimeZone; - System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset; - System.EnableDockedMode.Value = configurationFileFormat.DockedMode; - EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; - CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; - ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; - HideCursor.Value = configurationFileFormat.HideCursor; - Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; - Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; + Graphics.ResScale.Value = configurationFileFormat.ResScale; + Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; + Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy; + Graphics.AspectRatio.Value = configurationFileFormat.AspectRatio; + Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath; + Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading; + Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend; + Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu; + Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing; + Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter; + Graphics.ScalingFilterLevel.Value = configurationFileFormat.ScalingFilterLevel; + Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug; + Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub; + Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo; + Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn; + Logger.EnableError.Value = configurationFileFormat.LoggingEnableError; + Logger.EnableTrace.Value = configurationFileFormat.LoggingEnableTrace; + Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest; + Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog; + Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses; + Logger.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel; + System.Language.Value = configurationFileFormat.SystemLanguage; + System.Region.Value = configurationFileFormat.SystemRegion; + System.TimeZone.Value = configurationFileFormat.SystemTimeZone; + System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset; + System.EnableDockedMode.Value = configurationFileFormat.DockedMode; + EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; + CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; + ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; + HideCursor.Value = configurationFileFormat.HideCursor; + Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; + Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; - Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; - System.EnablePtc.Value = configurationFileFormat.EnablePtc; - System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; - System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; - System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; - System.AudioBackend.Value = configurationFileFormat.AudioBackend; - System.AudioVolume.Value = configurationFileFormat.AudioVolume; - System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode; - System.ExpandRam.Value = configurationFileFormat.ExpandRam; - System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices; - System.UseHypervisor.Value = configurationFileFormat.UseHypervisor; - Ui.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn; - Ui.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn; - Ui.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn; - Ui.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn; - Ui.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn; - Ui.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn; - Ui.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn; - Ui.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn; - Ui.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn; - Ui.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn; - Ui.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId; - Ui.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending; - Ui.GameDirs.Value = configurationFileFormat.GameDirs; - Ui.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; - Ui.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; - Ui.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; - Ui.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA; - Ui.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO; - Ui.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO; - Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme; - Ui.LanguageCode.Value = configurationFileFormat.LanguageCode; - Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath; - Ui.BaseStyle.Value = configurationFileFormat.BaseStyle; - Ui.GameListViewMode.Value = configurationFileFormat.GameListViewMode; - Ui.ShowNames.Value = configurationFileFormat.ShowNames; - Ui.IsAscendingOrder.Value = configurationFileFormat.IsAscendingOrder; - Ui.GridSize.Value = configurationFileFormat.GridSize; - Ui.ApplicationSort.Value = configurationFileFormat.ApplicationSort; - Ui.StartFullscreen.Value = configurationFileFormat.StartFullscreen; - Ui.ShowConsole.Value = configurationFileFormat.ShowConsole; - Ui.WindowStartup.WindowSizeWidth.Value = configurationFileFormat.WindowStartup.WindowSizeWidth; - Ui.WindowStartup.WindowSizeHeight.Value = configurationFileFormat.WindowStartup.WindowSizeHeight; - Ui.WindowStartup.WindowPositionX.Value = configurationFileFormat.WindowStartup.WindowPositionX; - Ui.WindowStartup.WindowPositionY.Value = configurationFileFormat.WindowStartup.WindowPositionY; - Ui.WindowStartup.WindowMaximized.Value = configurationFileFormat.WindowStartup.WindowMaximized; - Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard; - Hid.EnableMouse.Value = configurationFileFormat.EnableMouse; - Hid.Hotkeys.Value = configurationFileFormat.Hotkeys; - Hid.InputConfig.Value = configurationFileFormat.InputConfig; + Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; + System.EnablePtc.Value = configurationFileFormat.EnablePtc; + System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; + System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; + System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; + System.AudioBackend.Value = configurationFileFormat.AudioBackend; + System.AudioVolume.Value = configurationFileFormat.AudioVolume; + System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode; + System.ExpandRam.Value = configurationFileFormat.ExpandRam; + System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices; + System.UseHypervisor.Value = configurationFileFormat.UseHypervisor; + Ui.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn; + Ui.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn; + Ui.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn; + Ui.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn; + Ui.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn; + Ui.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn; + Ui.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn; + Ui.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn; + Ui.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn; + Ui.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn; + Ui.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId; + Ui.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending; + Ui.GameDirs.Value = configurationFileFormat.GameDirs; + Ui.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; + Ui.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; + Ui.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; + Ui.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA; + Ui.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO; + Ui.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO; + Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme; + Ui.LanguageCode.Value = configurationFileFormat.LanguageCode; + Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath; + Ui.BaseStyle.Value = configurationFileFormat.BaseStyle; + Ui.GameListViewMode.Value = configurationFileFormat.GameListViewMode; + Ui.ShowNames.Value = configurationFileFormat.ShowNames; + Ui.IsAscendingOrder.Value = configurationFileFormat.IsAscendingOrder; + Ui.GridSize.Value = configurationFileFormat.GridSize; + Ui.ApplicationSort.Value = configurationFileFormat.ApplicationSort; + Ui.StartFullscreen.Value = configurationFileFormat.StartFullscreen; + Ui.ShowConsole.Value = configurationFileFormat.ShowConsole; + Ui.WindowStartup.WindowSizeWidth.Value = configurationFileFormat.WindowStartup.WindowSizeWidth; + Ui.WindowStartup.WindowSizeHeight.Value = configurationFileFormat.WindowStartup.WindowSizeHeight; + Ui.WindowStartup.WindowPositionX.Value = configurationFileFormat.WindowStartup.WindowPositionX; + Ui.WindowStartup.WindowPositionY.Value = configurationFileFormat.WindowStartup.WindowPositionY; + Ui.WindowStartup.WindowMaximized.Value = configurationFileFormat.WindowStartup.WindowMaximized; + Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard; + Hid.EnableMouse.Value = configurationFileFormat.EnableMouse; + Hid.Hotkeys.Value = configurationFileFormat.Hotkeys; + Hid.InputConfig.Value = configurationFileFormat.InputConfig; if (Hid.InputConfig.Value == null) { @@ -1499,7 +1493,7 @@ namespace Ryujinx.Ui.Common.Configuration return result; } - private static void LogValueChange<T>(object sender, ReactiveEventArgs<T> eventArgs, string valueName) + private static void LogValueChange<T>(ReactiveEventArgs<T> eventArgs, string valueName) { Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, $"{valueName} set to: {eventArgs.NewValue}"); } diff --git a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs index 85b50e763..e18b2bfa3 100644 --- a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs +++ b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs @@ -9,16 +9,16 @@ namespace Ryujinx.Ui.Common.Configuration { public static void Initialize() { - ConfigurationState.Instance.Logger.EnableDebug.Event += ReloadEnableDebug; - ConfigurationState.Instance.Logger.EnableStub.Event += ReloadEnableStub; - ConfigurationState.Instance.Logger.EnableInfo.Event += ReloadEnableInfo; - ConfigurationState.Instance.Logger.EnableWarn.Event += ReloadEnableWarning; - ConfigurationState.Instance.Logger.EnableError.Event += ReloadEnableError; - ConfigurationState.Instance.Logger.EnableTrace.Event += ReloadEnableTrace; - ConfigurationState.Instance.Logger.EnableGuest.Event += ReloadEnableGuest; + ConfigurationState.Instance.Logger.EnableDebug.Event += ReloadEnableDebug; + ConfigurationState.Instance.Logger.EnableStub.Event += ReloadEnableStub; + ConfigurationState.Instance.Logger.EnableInfo.Event += ReloadEnableInfo; + ConfigurationState.Instance.Logger.EnableWarn.Event += ReloadEnableWarning; + ConfigurationState.Instance.Logger.EnableError.Event += ReloadEnableError; + ConfigurationState.Instance.Logger.EnableTrace.Event += ReloadEnableTrace; + ConfigurationState.Instance.Logger.EnableGuest.Event += ReloadEnableGuest; ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ReloadEnableFsAccessLog; - ConfigurationState.Instance.Logger.FilteredClasses.Event += ReloadFilteredClasses; - ConfigurationState.Instance.Logger.EnableFileLog.Event += ReloadFileLogger; + ConfigurationState.Instance.Logger.FilteredClasses.Event += ReloadFilteredClasses; + ConfigurationState.Instance.Logger.EnableFileLog.Event += ReloadFileLogger; } private static void ReloadEnableDebug(object sender, ReactiveEventArgs<bool> e) diff --git a/src/Ryujinx.Ui.Common/Configuration/System/Language.cs b/src/Ryujinx.Ui.Common/Configuration/System/Language.cs index 404f8063d..2eb9ef5c1 100644 --- a/src/Ryujinx.Ui.Common/Configuration/System/Language.cs +++ b/src/Ryujinx.Ui.Common/Configuration/System/Language.cs @@ -23,6 +23,6 @@ namespace Ryujinx.Ui.Common.Configuration.System LatinAmericanSpanish, SimplifiedChinese, TraditionalChinese, - BrazilianPortuguese + BrazilianPortuguese, } } diff --git a/src/Ryujinx.Ui.Common/Configuration/System/Region.cs b/src/Ryujinx.Ui.Common/Configuration/System/Region.cs index 7dfac6388..03aed7d50 100644 --- a/src/Ryujinx.Ui.Common/Configuration/System/Region.cs +++ b/src/Ryujinx.Ui.Common/Configuration/System/Region.cs @@ -12,6 +12,6 @@ namespace Ryujinx.Ui.Common.Configuration.System Australia, China, Korea, - Taiwan + Taiwan, } } diff --git a/src/Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs b/src/Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs index 95bb43f82..cbc13b857 100644 --- a/src/Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs +++ b/src/Ryujinx.Ui.Common/Configuration/Ui/ColumnSort.cs @@ -2,7 +2,7 @@ { public struct ColumnSort { - public int SortColumnId { get; set; } + public int SortColumnId { get; set; } public bool SortAscending { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs b/src/Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs index 89264cff2..aff654599 100644 --- a/src/Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs +++ b/src/Ryujinx.Ui.Common/Configuration/Ui/GuiColumns.cs @@ -2,15 +2,15 @@ { public struct GuiColumns { - public bool FavColumn { get; set; } - public bool IconColumn { get; set; } - public bool AppColumn { get; set; } - public bool DevColumn { get; set; } - public bool VersionColumn { get; set; } + public bool FavColumn { get; set; } + public bool IconColumn { get; set; } + public bool AppColumn { get; set; } + public bool DevColumn { get; set; } + public bool VersionColumn { get; set; } public bool TimePlayedColumn { get; set; } public bool LastPlayedColumn { get; set; } - public bool FileExtColumn { get; set; } - public bool FileSizeColumn { get; set; } - public bool PathColumn { get; set; } + public bool FileExtColumn { get; set; } + public bool FileSizeColumn { get; set; } + public bool PathColumn { get; set; } } } diff --git a/src/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs b/src/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs index c0b76e85c..1b14fd467 100644 --- a/src/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs +++ b/src/Ryujinx.Ui.Common/Configuration/Ui/ShownFileTypes.cs @@ -2,11 +2,11 @@ namespace Ryujinx.Ui.Common.Configuration.Ui { public struct ShownFileTypes { - public bool NSP { get; set; } + public bool NSP { get; set; } public bool PFS0 { get; set; } - public bool XCI { get; set; } - public bool NCA { get; set; } - public bool NRO { get; set; } - public bool NSO { get; set; } + public bool XCI { get; set; } + public bool NCA { get; set; } + public bool NRO { get; set; } + public bool NSO { get; set; } } } diff --git a/src/Ryujinx.Ui.Common/DiscordIntegrationModule.cs b/src/Ryujinx.Ui.Common/DiscordIntegrationModule.cs index 5ae886aca..69d725967 100644 --- a/src/Ryujinx.Ui.Common/DiscordIntegrationModule.cs +++ b/src/Ryujinx.Ui.Common/DiscordIntegrationModule.cs @@ -7,10 +7,10 @@ namespace Ryujinx.Ui.Common public static class DiscordIntegrationModule { private const string Description = "A simple, experimental Nintendo Switch emulator."; - private const string CliendId = "568815339807309834"; + private const string CliendId = "568815339807309834"; private static DiscordRpcClient _discordClient; - private static RichPresence _discordPresenceMain; + private static RichPresence _discordPresenceMain; public static void Initialize() { @@ -18,20 +18,20 @@ namespace Ryujinx.Ui.Common { Assets = new Assets { - LargeImageKey = "ryujinx", - LargeImageText = Description + LargeImageKey = "ryujinx", + LargeImageText = Description, }, - Details = "Main Menu", - State = "Idling", + Details = "Main Menu", + State = "Idling", Timestamps = Timestamps.Now, - Buttons = new Button[] + Buttons = new[] { - new Button() - { - Label = "Website", - Url = "https://ryujinx.org/" - } - } + new Button + { + Label = "Website", + Url = "https://ryujinx.org/", + }, + }, }; ConfigurationState.Instance.EnableDiscordIntegration.Event += Update; @@ -66,22 +66,22 @@ namespace Ryujinx.Ui.Common { Assets = new Assets { - LargeImageKey = "game", + LargeImageKey = "game", LargeImageText = titleName, - SmallImageKey = "ryujinx", + SmallImageKey = "ryujinx", SmallImageText = Description, }, - Details = $"Playing {titleName}", - State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(), + Details = $"Playing {titleName}", + State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(), Timestamps = Timestamps.Now, - Buttons = new Button[] + Buttons = new[] { - new Button() + new Button { Label = "Website", - Url = "https://ryujinx.org/" - } - } + Url = "https://ryujinx.org/", + }, + }, }); } @@ -95,4 +95,4 @@ namespace Ryujinx.Ui.Common _discordClient?.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs b/src/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs index e2ac49504..e60fa78cd 100644 --- a/src/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs +++ b/src/Ryujinx.Ui.Common/Extensions/FileTypeExtensions.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Ui.Common FileTypes.NCA => config.NCA.Value, FileTypes.NRO => config.NRO.Value, FileTypes.NSO => config.NSO.Value, - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null), }; } } diff --git a/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs b/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs index 660a4ce92..714cf2f0a 100644 --- a/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs +++ b/src/Ryujinx.Ui.Common/Helper/CommandLineState.cs @@ -7,13 +7,13 @@ namespace Ryujinx.Ui.Common.Helper { public static string[] Arguments { get; private set; } - public static bool? OverrideDockedMode { get; private set; } + public static bool? OverrideDockedMode { get; private set; } public static string OverrideGraphicsBackend { get; private set; } - public static string OverrideHideCursor { get; private set; } - public static string BaseDirPathArg { get; private set; } - public static string Profile { get; private set; } - public static string LaunchPathArg { get; private set; } - public static bool StartFullscreenArg { get; private set; } + public static string OverrideHideCursor { get; private set; } + public static string BaseDirPathArg { get; private set; } + public static string Profile { get; private set; } + public static string LaunchPathArg { get; private set; } + public static bool StartFullscreenArg { get; private set; } public static void ParseArguments(string[] args) { @@ -96,4 +96,4 @@ namespace Ryujinx.Ui.Common.Helper Arguments = arguments.ToArray(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs b/src/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs index 4eb3b79c1..160c6390f 100644 --- a/src/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs +++ b/src/Ryujinx.Ui.Common/Helper/ConsoleHelper.cs @@ -47,4 +47,4 @@ namespace Ryujinx.Ui.Common.Helper [return: MarshalAs(UnmanagedType.Bool)] private static partial bool ShowWindow(IntPtr hWnd, int nCmdShow); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs index 542db9a7e..570fb91e2 100644 --- a/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs +++ b/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Ui.Common.Helper private static readonly string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); private const int SHCNE_ASSOCCHANGED = 0x8000000; - private const int SHCNF_FLUSH = 0x1000; + private const int SHCNF_FLUSH = 0x1000; [LibraryImport("shell32.dll", SetLastError = true)] public static partial void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2); @@ -199,4 +199,4 @@ namespace Ryujinx.Ui.Common.Helper return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs b/src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs index cfbf4b57d..bf647719a 100644 --- a/src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs +++ b/src/Ryujinx.Ui.Common/Helper/LinuxHelper.cs @@ -49,8 +49,8 @@ namespace Ryujinx.Ui.Common.Helper StartInfo = { FileName = PkExecPath, - ArgumentList = { "sh", "-c", command } - } + ArgumentList = { "sh", "-c", command }, + }, }; process.Start(); @@ -59,4 +59,4 @@ namespace Ryujinx.Ui.Common.Helper return process.ExitCode; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Helper/ObjectiveC.cs b/src/Ryujinx.Ui.Common/Helper/ObjectiveC.cs index 234f7597a..af8723e2f 100644 --- a/src/Ryujinx.Ui.Common/Helper/ObjectiveC.cs +++ b/src/Ryujinx.Ui.Common/Helper/ObjectiveC.cs @@ -1,8 +1,6 @@ using System; -using System.IO; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Text; namespace Ryujinx.Ui.Common.Helper { @@ -12,44 +10,109 @@ namespace Ryujinx.Ui.Common.Helper private const string ObjCRuntime = "/usr/lib/libobjc.A.dylib"; [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] - private static unsafe partial IntPtr sel_getUid(string name); + private static partial IntPtr sel_getUid(string name); [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] - public static partial IntPtr objc_getClass(string name); + private static partial IntPtr objc_getClass(string name); [LibraryImport(ObjCRuntime)] - public static partial void objc_msgSend(IntPtr receiver, Selector selector); + private static partial void objc_msgSend(IntPtr receiver, Selector selector); [LibraryImport(ObjCRuntime)] - public static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); + private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); [LibraryImport(ObjCRuntime)] - public static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); + private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); [LibraryImport(ObjCRuntime)] - public static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); + private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); [LibraryImport(ObjCRuntime)] - public static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); + private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] - public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); + private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] - public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); + private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend", StringMarshalling = StringMarshalling.Utf8)] - public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, string param); + private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, string param); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool bool_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); + private static partial bool bool_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); - public struct Selector + public readonly struct Object + { + public readonly IntPtr ObjPtr; + + private Object(IntPtr pointer) + { + ObjPtr = pointer; + } + + public Object(string name) + { + ObjPtr = objc_getClass(name); + } + + public void SendMessage(Selector selector) + { + objc_msgSend(ObjPtr, selector); + } + + public void SendMessage(Selector selector, byte value) + { + objc_msgSend(ObjPtr, selector, value); + } + + public void SendMessage(Selector selector, Object obj) + { + objc_msgSend(ObjPtr, selector, obj.ObjPtr); + } + + public void SendMessage(Selector selector, NSRect point) + { + objc_msgSend(ObjPtr, selector, point); + } + + public void SendMessage(Selector selector, double value) + { + objc_msgSend(ObjPtr, selector, value); + } + + public Object GetFromMessage(Selector selector) + { + return new Object(IntPtr_objc_msgSend(ObjPtr, selector)); + } + + public Object GetFromMessage(Selector selector, Object obj) + { + return new Object(IntPtr_objc_msgSend(ObjPtr, selector, obj.ObjPtr)); + } + + public Object GetFromMessage(Selector selector, NSString nsString) + { + return new Object(IntPtr_objc_msgSend(ObjPtr, selector, nsString.StrPtr)); + } + + public Object GetFromMessage(Selector selector, string param) + { + return new Object(IntPtr_objc_msgSend(ObjPtr, selector, param)); + } + + public bool GetBoolFromMessage(Selector selector, Object obj) + { + return bool_objc_msgSend(ObjPtr, selector, obj.ObjPtr); + } + } + + public readonly struct Selector { public readonly IntPtr SelPtr; - public unsafe Selector(string name) + private Selector(string name) { SelPtr = sel_getUid(name); } @@ -57,7 +120,7 @@ namespace Ryujinx.Ui.Common.Helper public static implicit operator Selector(string value) => new(value); } - public struct NSString + public readonly struct NSString { public readonly IntPtr StrPtr; @@ -94,4 +157,4 @@ namespace Ryujinx.Ui.Common.Helper } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Helper/OpenHelper.cs b/src/Ryujinx.Ui.Common/Helper/OpenHelper.cs index 5b2e86635..49a53ae48 100644 --- a/src/Ryujinx.Ui.Common/Helper/OpenHelper.cs +++ b/src/Ryujinx.Ui.Common/Helper/OpenHelper.cs @@ -9,13 +9,13 @@ namespace Ryujinx.Ui.Common.Helper public static partial class OpenHelper { [LibraryImport("shell32.dll", SetLastError = true)] - public static partial int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr apidl, uint dwFlags); + private static partial int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr apidl, uint dwFlags); [LibraryImport("shell32.dll", SetLastError = true)] - public static partial void ILFree(IntPtr pidlList); + private static partial void ILFree(IntPtr pidlList); [LibraryImport("shell32.dll", SetLastError = true)] - public static partial IntPtr ILCreateFromPathW([MarshalAs(UnmanagedType.LPWStr)] string pszPath); + private static partial IntPtr ILCreateFromPathW([MarshalAs(UnmanagedType.LPWStr)] string pszPath); public static void OpenFolder(string path) { @@ -23,9 +23,9 @@ namespace Ryujinx.Ui.Common.Helper { Process.Start(new ProcessStartInfo { - FileName = path, + FileName = path, UseShellExecute = true, - Verb = "open" + Verb = "open", }); } else @@ -56,16 +56,16 @@ namespace Ryujinx.Ui.Common.Helper else if (OperatingSystem.IsMacOS()) { ObjectiveC.NSString nsStringPath = new(path); - IntPtr nsUrl = ObjectiveC.objc_getClass("NSURL"); - var urlPtr = ObjectiveC.IntPtr_objc_msgSend(nsUrl, "fileURLWithPath:", nsStringPath); + ObjectiveC.Object nsUrl = new("NSURL"); + var urlPtr = nsUrl.GetFromMessage("fileURLWithPath:", nsStringPath); - IntPtr nsArray = ObjectiveC.objc_getClass("NSArray"); - IntPtr urlArray = ObjectiveC.IntPtr_objc_msgSend(nsArray, "arrayWithObject:", urlPtr); + ObjectiveC.Object nsArray = new("NSArray"); + ObjectiveC.Object urlArray = nsArray.GetFromMessage("arrayWithObject:", urlPtr); - IntPtr nsWorkspace = ObjectiveC.objc_getClass("NSWorkspace"); - IntPtr sharedWorkspace = ObjectiveC.IntPtr_objc_msgSend(nsWorkspace, "sharedWorkspace"); + ObjectiveC.Object nsWorkspace = new("NSWorkspace"); + ObjectiveC.Object sharedWorkspace = nsWorkspace.GetFromMessage("sharedWorkspace"); - ObjectiveC.objc_msgSend(sharedWorkspace, "activateFileViewerSelectingURLs:", urlArray); + sharedWorkspace.SendMessage("activateFileViewerSelectingURLs:", urlArray); } else if (OperatingSystem.IsLinux()) { @@ -95,13 +95,13 @@ namespace Ryujinx.Ui.Common.Helper else if (OperatingSystem.IsMacOS()) { ObjectiveC.NSString nsStringPath = new(url); - IntPtr nsUrl = ObjectiveC.objc_getClass("NSURL"); - var urlPtr = ObjectiveC.IntPtr_objc_msgSend(nsUrl, "URLWithString:", nsStringPath); + ObjectiveC.Object nsUrl = new("NSURL"); + var urlPtr = nsUrl.GetFromMessage("URLWithString:", nsStringPath); - IntPtr nsWorkspace = ObjectiveC.objc_getClass("NSWorkspace"); - IntPtr sharedWorkspace = ObjectiveC.IntPtr_objc_msgSend(nsWorkspace, "sharedWorkspace"); + ObjectiveC.Object nsWorkspace = new("NSWorkspace"); + ObjectiveC.Object sharedWorkspace = nsWorkspace.GetFromMessage("sharedWorkspace"); - ObjectiveC.bool_objc_msgSend(sharedWorkspace, "openURL:", urlPtr); + sharedWorkspace.GetBoolFromMessage("openURL:", urlPtr); } else { @@ -109,4 +109,4 @@ namespace Ryujinx.Ui.Common.Helper } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Helper/SetupValidator.cs b/src/Ryujinx.Ui.Common/Helper/SetupValidator.cs index 3d779fdf2..4a7dac474 100644 --- a/src/Ryujinx.Ui.Common/Helper/SetupValidator.cs +++ b/src/Ryujinx.Ui.Common/Helper/SetupValidator.cs @@ -20,12 +20,10 @@ namespace Ryujinx.Ui.Common.Helper return true; } - else - { - error = UserError.NoFirmware; - return false; - } + error = UserError.NoFirmware; + + return false; } public static bool CanFixStartApplication(ContentManager contentManager, string baseApplicationPath, UserError error, out SystemVersion firmwareVersion) @@ -107,12 +105,10 @@ namespace Ryujinx.Ui.Common.Helper return IsFirmwareValid(contentManager, out error); } - else - { - error = UserError.ApplicationNotFound; - return false; - } + error = UserError.ApplicationNotFound; + + return false; } } } diff --git a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs index f412b9504..e8eba6d38 100644 --- a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs +++ b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs @@ -29,29 +29,39 @@ namespace Ryujinx.Ui.Common.Models.Amiibo [JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; } - public override string ToString() + public readonly override string ToString() { return Name; } - public string GetId() + public readonly string GetId() { return Head + Tail; } - public bool Equals(AmiiboApi other) + public readonly bool Equals(AmiiboApi other) { return Head + Tail == other.Head + other.Tail; } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is AmiiboApi other && Equals(other); } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(Head, Tail); } + + public static bool operator ==(AmiiboApi left, AmiiboApi right) + { + return left.Equals(right); + } + + public static bool operator !=(AmiiboApi left, AmiiboApi right) + { + return !(left == right); + } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs index def7d1bcc..6a0442f08 100644 --- a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs +++ b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs @@ -12,4 +12,4 @@ namespace Ryujinx.Ui.Common.Models.Amiibo [JsonPropertyName("gameName")] public string GameName { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs index 814573c22..d9d3a18f6 100644 --- a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs +++ b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Ui.Common.Models.Amiibo [JsonPropertyName("write")] public bool Write { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs index feb7993c1..bcfe230dd 100644 --- a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs +++ b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs @@ -11,4 +11,4 @@ namespace Ryujinx.Ui.Common.Models.Amiibo [JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs index 4cbb5a7b4..09888d37b 100644 --- a/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs +++ b/src/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Ui.Common.Models.Amiibo public partial class AmiiboJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs b/src/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs index 10d014783..ed0a49f6c 100644 --- a/src/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs +++ b/src/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs @@ -6,4 +6,4 @@ public string State { get; set; } public string BrowserDownloadUrl { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs b/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs index 954d03e31..3fa7cf817 100644 --- a/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs +++ b/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Ui.Common.Models.Github public string Name { get; set; } public List<GithubReleaseAssetJsonResponse> Assets { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs b/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs index e5fd9d094..c6dd88755 100644 --- a/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs +++ b/src/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Ui.Common.Models.Github public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ui.Common/UserError.cs b/src/Ryujinx.Ui.Common/UserError.cs index f4cfa26dc..63be1e264 100644 --- a/src/Ryujinx.Ui.Common/UserError.cs +++ b/src/Ryujinx.Ui.Common/UserError.cs @@ -34,6 +34,6 @@ /// <summary> /// An unknown error. /// </summary> - Unknown = 0xDEAD + Unknown = 0xDEAD, } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index 1918594ce..628bcff4f 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -874,7 +874,7 @@ namespace Ryujinx.Ui DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText, _emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString()); - _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => + ApplicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow; }); @@ -1017,7 +1017,7 @@ namespace Ryujinx.Ui { if (_gameLoaded) { - _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => + ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { if (appMetadata.LastPlayed.HasValue) { @@ -1156,7 +1156,7 @@ namespace Ryujinx.Ui _tableStore.SetValue(treeIter, 0, newToggleValue); - _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => + ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { appMetadata.Favorite = newToggleValue; }); @@ -1321,7 +1321,7 @@ namespace Ryujinx.Ui { if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) { - SaveWindowSizePosition(); + SaveWindowSizePosition(); End(); } else @@ -1337,9 +1337,9 @@ namespace Ryujinx.Ui Move(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); - if (ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized) - { - Maximize(); + if (ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized) + { + Maximize(); } } @@ -1354,7 +1354,7 @@ namespace Ryujinx.Ui ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX.Value = windowXPos; ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY.Value = windowYPos; - SaveConfig(); + SaveConfig(); } private void StopEmulation_Pressed(object sender, EventArgs args) @@ -1633,7 +1633,7 @@ namespace Ryujinx.Ui _virtualFileSystem, _emulationContext.Processes.ActiveApplication.ProgramId, _emulationContext.Processes.ActiveApplication.ApplicationControlProperties - .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(), + .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(), _currentEmulatedGamePath); window.Destroyed += CheatWindow_Destroyed; @@ -1861,4 +1861,4 @@ namespace Ryujinx.Ui UpdateGameTable(); } } -} +} \ No newline at end of file From e9848339ddac3d6fe32a0ce0fbe6029c4ad40429 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 1 Jul 2023 04:14:34 +0200 Subject: [PATCH 687/737] [Ryujinx.Tests] Address dotnet-format issues (#5389) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Fix new dotnet-format issues after rebase * Address review comments * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * cpu tests: Disable CA2211 for CodeBaseAddress and DataBaseAddress * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * First dotnet format pass * Fix naming rule violations * Remove naming rule violation exceptions * Fix comment style * Use targeted new * Remove redundant code * Remove comment alignment * Remove naming rule exceptions * Add trailing commas * Use nameof expression * Reformat to add remaining trailing commas --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- .../Audio/Renderer/Dsp/ResamplerTests.cs | 19 +- .../Audio/Renderer/Dsp/UpsamplerTests.cs | 17 +- .../Effect/CompressorParameterTests.cs | 1 - .../Parameter/Effect/LimiterParameterTests.cs | 1 - .../Effect/LimiterStatisticsTests.cs | 1 - .../Renderer/Server/BehaviourContextTests.cs | 26 +- .../Audio/Renderer/Server/PoolMapperTests.cs | 23 +- .../Cpu/Arm64CodeGenCommonTests.cs | 4 +- src/Ryujinx.Tests/Cpu/CpuTest.cs | 217 +++--- src/Ryujinx.Tests/Cpu/CpuTest32.cs | 57 +- src/Ryujinx.Tests/Cpu/CpuTestAlu.cs | 56 +- src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs | 32 +- src/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs | 8 +- src/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs | 6 +- src/Ryujinx.Tests/Cpu/CpuTestAluImm.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs | 10 +- src/Ryujinx.Tests/Cpu/CpuTestAluRs.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs | 14 +- src/Ryujinx.Tests/Cpu/CpuTestAluRx.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestBf32.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestBfm.cs | 4 +- src/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestCsel.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestMisc.cs | 131 ++-- src/Ryujinx.Tests/Cpu/CpuTestMisc32.cs | 24 +- src/Ryujinx.Tests/Cpu/CpuTestMov.cs | 4 +- src/Ryujinx.Tests/Cpu/CpuTestMul.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestMul32.cs | 14 +- src/Ryujinx.Tests/Cpu/CpuTestSimd.cs | 720 ++++++++++-------- src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs | 73 +- src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs | 25 +- src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs | 21 +- src/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs | 178 +++-- src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs | 53 +- src/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs | 16 +- src/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs | 48 +- src/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs | 14 +- src/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs | 81 +- src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs | 116 +-- src/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs | 51 +- src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs | 49 +- src/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs | 103 +-- src/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 582 +++++++------- src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs | 180 +++-- src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs | 36 +- src/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs | 84 +- src/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs | 192 ++--- src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs | 51 +- src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs | 56 +- src/Ryujinx.Tests/Cpu/CpuTestSystem.cs | 16 +- src/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs | 320 ++++---- src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs | 195 ++--- src/Ryujinx.Tests/Cpu/CpuTestThumb.cs | 202 ++--- src/Ryujinx.Tests/Cpu/EnvironmentTests.cs | 12 +- .../Cpu/PrecomputedMemoryThumbTestCase.cs | 4 +- .../Cpu/PrecomputedThumbTestCase.cs | 2 +- .../HLE/SoftwareKeyboardTests.cs | 3 +- src/Ryujinx.Tests/Memory/MockMemoryManager.cs | 2 +- src/Ryujinx.Tests/Memory/PartialUnmaps.cs | 12 +- src/Ryujinx.Tests/TreeDictionaryTests.cs | 8 +- 62 files changed, 2263 insertions(+), 1929 deletions(-) diff --git a/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs index 364837ee0..0624f3401 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Dsp/ResamplerTests.cs @@ -1,14 +1,7 @@ using NUnit.Framework; using Ryujinx.Audio.Renderer.Dsp; using Ryujinx.Audio.Renderer.Parameter; -using Ryujinx.Audio.Renderer.Server.Upsampler; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; namespace Ryujinx.Tests.Audio.Renderer.Dsp { @@ -41,8 +34,8 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp /// <param name="quality">The resampler quality to use</param> private static void DoResamplingTest(int inputRate, int outputRate, VoiceInParameter.SampleRateConversionQuality quality) { - float inputSampleRate = (float)inputRate; - float outputSampleRate = (float)outputRate; + float inputSampleRate = inputRate; + float outputSampleRate = outputRate; int inputSampleCount = inputRate; int outputSampleCount = outputRate; short[] inputBuffer = new short[inputSampleCount + 100]; // add some safety buffer at the end @@ -50,7 +43,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp for (int sample = 0; sample < inputBuffer.Length; sample++) { // 440 hz sine wave with amplitude = 0.5f at input sample rate - inputBuffer[sample] = (short)(32767 * MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f); + inputBuffer[sample] = (short)(32767 * MathF.Sin((440 / inputSampleRate) * sample * MathF.PI * 2f) * 0.5f); } float fraction = 0; @@ -70,14 +63,14 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp { VoiceInParameter.SampleRateConversionQuality.High => 3, VoiceInParameter.SampleRateConversionQuality.Default => 1, - _ => 0 + _ => 0, }; for (int sample = 0; sample < outputSampleCount; sample++) { outputBuffer[sample] /= 32767; // 440 hz sine wave with amplitude = 0.5f at output sample rate - expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample + delay) * MathF.PI * 2f) * 0.5f; + expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (sample + delay) * MathF.PI * 2f) * 0.5f; float thisDelta = Math.Abs(expectedOutput[sample] - outputBuffer[sample]); // Ensure no discontinuities @@ -85,7 +78,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp sumDifference += thisDelta; } - sumDifference = sumDifference / (float)outputSampleCount; + sumDifference /= outputSampleCount; // Expect the output to be 99% similar to the expected resampled sine wave Assert.IsTrue(sumDifference < 0.01f); } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs index 2018752b3..9b01b3ca6 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Dsp/UpsamplerTests.cs @@ -1,14 +1,7 @@ using NUnit.Framework; using Ryujinx.Audio.Renderer.Dsp; -using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Server.Upsampler; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; namespace Ryujinx.Tests.Audio.Renderer.Dsp { @@ -17,7 +10,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp [Test] public void TestUpsamplerConsistency() { - UpsamplerBufferState bufferState = new UpsamplerBufferState(); + UpsamplerBufferState bufferState = new(); int inputBlockSize = 160; int numInputSamples = 32000; int numOutputSamples = 48000; @@ -28,14 +21,14 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp for (int sample = 0; sample < inputBuffer.Length; sample++) { // 440 hz sine wave with amplitude = 0.5f at input sample rate - inputBuffer[sample] = MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f; + inputBuffer[sample] = MathF.Sin((440 / inputSampleRate) * sample * MathF.PI * 2f) * 0.5f; } int inputIdx = 0; int outputIdx = 0; while (inputIdx + inputBlockSize < numInputSamples) { - int outputBufLength = (int)Math.Round((float)(inputIdx + inputBlockSize) * outputSampleRate / inputSampleRate) - outputIdx; + int outputBufLength = (int)Math.Round((inputIdx + inputBlockSize) * outputSampleRate / inputSampleRate) - outputIdx; UpsamplerHelper.Upsample( outputBuffer.AsSpan(outputIdx), inputBuffer.AsSpan(inputIdx), @@ -52,11 +45,11 @@ namespace Ryujinx.Tests.Audio.Renderer.Dsp for (int sample = 0; sample < numOutputSamples; sample++) { // 440 hz sine wave with amplitude = 0.5f at output sample rate with an offset of 15 - expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample - 15) * MathF.PI * 2f) * 0.5f; + expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (sample - 15) * MathF.PI * 2f) * 0.5f; sumDifference += Math.Abs(expectedOutput[sample] - outputBuffer[sample]); } - sumDifference = sumDifference / (float)expectedOutput.Length; + sumDifference /= expectedOutput.Length; // Expect the output to be 98% similar to the expected resampled sine wave Assert.IsTrue(sumDifference < 0.02f); } diff --git a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs index 24b834fcb..016338dc7 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/CompressorParameterTests.cs @@ -13,4 +13,3 @@ namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect } } } - diff --git a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs index 8512ebd47..e176cc62f 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterParameterTests.cs @@ -13,4 +13,3 @@ namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect } } } - diff --git a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs index 43645ae42..13ad34ebd 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Parameter/Effect/LimiterStatisticsTests.cs @@ -13,4 +13,3 @@ namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect } } } - diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs index df946a12f..557581881 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestCheckFeature() { - int latestRevision = BehaviourContext.BaseRevisionMagic + BehaviourContext.LastRevision; + int latestRevision = BehaviourContext.BaseRevisionMagic + BehaviourContext.LastRevision; int previousRevision = BehaviourContext.BaseRevisionMagic + (BehaviourContext.LastRevision - 1); int invalidRevision = BehaviourContext.BaseRevisionMagic + (BehaviourContext.LastRevision + 1); @@ -22,7 +22,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestsMemoryPoolForceMappingEnabled() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision1); @@ -36,7 +36,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision1() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision1); @@ -62,7 +62,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision2() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision2); @@ -88,7 +88,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision3() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision3); @@ -114,7 +114,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision4() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision4); @@ -140,7 +140,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision5() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision5); @@ -166,7 +166,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision6() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision6); @@ -192,7 +192,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision7() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision7); @@ -218,7 +218,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision8() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision8); @@ -244,7 +244,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision9() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision9); @@ -270,7 +270,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestRevision10() { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision10); @@ -293,4 +293,4 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs index d2c2e6cb9..4c931d9ee 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs @@ -15,13 +15,13 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestInitializeSystemPool() { - PoolMapper poolMapper = new PoolMapper(DummyProcessHandle, true); + PoolMapper poolMapper = new(DummyProcessHandle, true); MemoryPoolState memoryPoolDsp = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); MemoryPoolState memoryPoolCpu = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); const CpuAddress CpuAddress = 0x20000; const DspAddress DspAddress = CpuAddress; // TODO: DSP LLE - const ulong CpuSize = 0x1000; + const ulong CpuSize = 0x1000; Assert.IsFalse(poolMapper.InitializeSystemPool(ref memoryPoolCpu, CpuAddress, CpuSize)); Assert.IsTrue(poolMapper.InitializeSystemPool(ref memoryPoolDsp, CpuAddress, CpuSize)); @@ -34,7 +34,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestGetProcessHandle() { - PoolMapper poolMapper = new PoolMapper(DummyProcessHandle, true); + PoolMapper poolMapper = new(DummyProcessHandle, true); MemoryPoolState memoryPoolDsp = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); MemoryPoolState memoryPoolCpu = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); @@ -45,13 +45,13 @@ namespace Ryujinx.Tests.Audio.Renderer.Server [Test] public void TestMappings() { - PoolMapper poolMapper = new PoolMapper(DummyProcessHandle, true); + PoolMapper poolMapper = new(DummyProcessHandle, true); MemoryPoolState memoryPoolDsp = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); MemoryPoolState memoryPoolCpu = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu); const CpuAddress CpuAddress = 0x20000; const DspAddress DspAddress = CpuAddress; // TODO: DSP LLE - const ulong CpuSize = 0x1000; + const ulong CpuSize = 0x1000; memoryPoolDsp.SetCpuAddress(CpuAddress, CpuSize); memoryPoolCpu.SetCpuAddress(CpuAddress, CpuSize); @@ -72,10 +72,10 @@ namespace Ryujinx.Tests.Audio.Renderer.Server { const CpuAddress CpuAddress = 0x20000; const DspAddress DspAddress = CpuAddress; // TODO: DSP LLE - const ulong CpuSize = 0x1000; + const ulong CpuSize = 0x1000; - const int MemoryPoolStateArraySize = 0x10; - const CpuAddress CpuAddressRegionEnding = CpuAddress * MemoryPoolStateArraySize; + const int MemoryPoolStateArraySize = 0x10; + const CpuAddress CpuAddressRegionEnding = CpuAddress * MemoryPoolStateArraySize; MemoryPoolState[] memoryPoolStateArray = new MemoryPoolState[MemoryPoolStateArraySize]; @@ -85,13 +85,12 @@ namespace Ryujinx.Tests.Audio.Renderer.Server memoryPoolStateArray[i].SetCpuAddress(CpuAddress + (ulong)i * CpuSize, CpuSize); } - ErrorInfo errorInfo; AddressInfo addressInfo = AddressInfo.Create(); - PoolMapper poolMapper = new PoolMapper(DummyProcessHandle, true); + PoolMapper poolMapper = new(DummyProcessHandle, true); - Assert.IsTrue(poolMapper.TryAttachBuffer(out errorInfo, ref addressInfo, 0, 0)); + Assert.IsTrue(poolMapper.TryAttachBuffer(out ErrorInfo errorInfo, ref addressInfo, 0, 0)); Assert.AreEqual(ResultCode.InvalidAddressInfo, errorInfo.ErrorCode); Assert.AreEqual(0, errorInfo.ExtraErrorInfo); @@ -105,7 +104,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server poolMapper = new PoolMapper(DummyProcessHandle, false); - Assert.IsFalse(poolMapper.TryAttachBuffer(out errorInfo, ref addressInfo, 0, 0)); + Assert.IsFalse(poolMapper.TryAttachBuffer(out _, ref addressInfo, 0, 0)); addressInfo.ForceMappedDspAddress = 0; diff --git a/src/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs b/src/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs index e16361bbc..0092d9a11 100644 --- a/src/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs +++ b/src/Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Tests.Cpu new() { Value = 0xffff8fffffff8fff, Valid = true, ImmN = 0, ImmS = 0x1c, ImmR = 17 }, new() { Value = 0x000000000ffff800, Valid = true, ImmN = 1, ImmS = 0x10, ImmR = 53 }, }; - + [Test] public void BitImmTests([ValueSource(nameof(TestCases))] TestCase test) { @@ -43,4 +43,4 @@ namespace Ryujinx.Tests.Cpu Assert.That(immR, Is.EqualTo(test.ImmR)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTest.cs b/src/Ryujinx.Tests/Cpu/CpuTest.cs index ad4ba539b..35158c0b4 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTest.cs @@ -14,13 +14,15 @@ namespace Ryujinx.Tests.Cpu public class CpuTest { protected static readonly ulong Size = MemoryBlock.GetPageSize(); +#pragma warning disable CA2211 // Non-constant fields should not be visible protected static ulong CodeBaseAddress = Size; protected static ulong DataBaseAddress = CodeBaseAddress + Size; +#pragma warning restore CA2211 - private static bool Ignore_FpcrFz = false; - private static bool Ignore_FpcrDn = false; + private static readonly bool _ignoreFpcrFz = false; + private static readonly bool _ignoreFpcrDn = false; - private static bool IgnoreAllExcept_FpsrQc = false; + private static readonly bool _ignoreAllExceptFpsrQc = false; private ulong _currAddress; @@ -84,8 +86,8 @@ namespace Ryujinx.Tests.Cpu _context.Dispose(); _ram.Dispose(); - _memory = null; - _context = null; + _memory = null; + _context = null; _cpuContext = null; _unicornEmu = null; @@ -109,38 +111,38 @@ namespace Ryujinx.Tests.Cpu protected ExecutionContext GetContext() => _context; - protected void SetContext(ulong x0 = 0, - ulong x1 = 0, - ulong x2 = 0, - ulong x3 = 0, - ulong x31 = 0, - V128 v0 = default, - V128 v1 = default, - V128 v2 = default, - V128 v3 = default, - V128 v4 = default, - V128 v5 = default, - V128 v30 = default, - V128 v31 = default, - bool overflow = false, - bool carry = false, - bool zero = false, - bool negative = false, - int fpcr = 0, - int fpsr = 0) + protected void SetContext(ulong x0 = 0, + ulong x1 = 0, + ulong x2 = 0, + ulong x3 = 0, + ulong x31 = 0, + V128 v0 = default, + V128 v1 = default, + V128 v2 = default, + V128 v3 = default, + V128 v4 = default, + V128 v5 = default, + V128 v30 = default, + V128 v31 = default, + bool overflow = false, + bool carry = false, + bool zero = false, + bool negative = false, + int fpcr = 0, + int fpsr = 0) { - _context.SetX(0, x0); - _context.SetX(1, x1); - _context.SetX(2, x2); - _context.SetX(3, x3); + _context.SetX(0, x0); + _context.SetX(1, x1); + _context.SetX(2, x2); + _context.SetX(3, x3); _context.SetX(31, x31); - _context.SetV(0, v0); - _context.SetV(1, v1); - _context.SetV(2, v2); - _context.SetV(3, v3); - _context.SetV(4, v4); - _context.SetV(5, v5); + _context.SetV(0, v0); + _context.SetV(1, v1); + _context.SetV(2, v2); + _context.SetV(3, v3); + _context.SetV(4, v4); + _context.SetV(5, v5); _context.SetV(30, v30); _context.SetV(31, v31); @@ -156,20 +158,20 @@ namespace Ryujinx.Tests.Cpu _unicornEmu.X[1] = x1; _unicornEmu.X[2] = x2; _unicornEmu.X[3] = x3; - _unicornEmu.SP = x31; + _unicornEmu.SP = x31; - _unicornEmu.Q[0] = V128ToSimdValue(v0); - _unicornEmu.Q[1] = V128ToSimdValue(v1); - _unicornEmu.Q[2] = V128ToSimdValue(v2); - _unicornEmu.Q[3] = V128ToSimdValue(v3); - _unicornEmu.Q[4] = V128ToSimdValue(v4); - _unicornEmu.Q[5] = V128ToSimdValue(v5); + _unicornEmu.Q[0] = V128ToSimdValue(v0); + _unicornEmu.Q[1] = V128ToSimdValue(v1); + _unicornEmu.Q[2] = V128ToSimdValue(v2); + _unicornEmu.Q[3] = V128ToSimdValue(v3); + _unicornEmu.Q[4] = V128ToSimdValue(v4); + _unicornEmu.Q[5] = V128ToSimdValue(v5); _unicornEmu.Q[30] = V128ToSimdValue(v30); _unicornEmu.Q[31] = V128ToSimdValue(v31); _unicornEmu.OverflowFlag = overflow; - _unicornEmu.CarryFlag = carry; - _unicornEmu.ZeroFlag = zero; + _unicornEmu.CarryFlag = carry; + _unicornEmu.ZeroFlag = zero; _unicornEmu.NegativeFlag = negative; _unicornEmu.Fpcr = fpcr; @@ -186,34 +188,34 @@ namespace Ryujinx.Tests.Cpu } } - protected ExecutionContext SingleOpcode(uint opcode, - ulong x0 = 0, - ulong x1 = 0, - ulong x2 = 0, - ulong x3 = 0, - ulong x31 = 0, - V128 v0 = default, - V128 v1 = default, - V128 v2 = default, - V128 v3 = default, - V128 v4 = default, - V128 v5 = default, - V128 v30 = default, - V128 v31 = default, - bool overflow = false, - bool carry = false, - bool zero = false, - bool negative = false, - int fpcr = 0, - int fpsr = 0, - bool runUnicorn = true) + protected ExecutionContext SingleOpcode(uint opcode, + ulong x0 = 0, + ulong x1 = 0, + ulong x2 = 0, + ulong x3 = 0, + ulong x31 = 0, + V128 v0 = default, + V128 v1 = default, + V128 v2 = default, + V128 v3 = default, + V128 v4 = default, + V128 v5 = default, + V128 v30 = default, + V128 v31 = default, + bool overflow = false, + bool carry = false, + bool zero = false, + bool negative = false, + int fpcr = 0, + int fpsr = 0, + bool runUnicorn = true) { - if (Ignore_FpcrFz) + if (_ignoreFpcrFz) { fpcr &= ~(1 << (int)Fpcr.Fz); } - if (Ignore_FpcrDn) + if (_ignoreFpcrDn) { fpcr &= ~(1 << (int)Fpcr.Dn); } @@ -254,8 +256,8 @@ namespace Ryujinx.Tests.Cpu /// <summary>Round towards Minus Infinity mode.</summary> Rm, /// <summary>Round towards Zero mode.</summary> - Rz - }; + Rz, + } /// <summary>Floating-point Control Register.</summary> protected enum Fpcr @@ -263,15 +265,16 @@ namespace Ryujinx.Tests.Cpu /// <summary>Rounding Mode control field.</summary> RMode = 22, /// <summary>Flush-to-zero mode control bit.</summary> - Fz = 24, + Fz = 24, /// <summary>Default NaN mode control bit.</summary> - Dn = 25, + Dn = 25, /// <summary>Alternative half-precision control bit.</summary> - Ahp = 26 + Ahp = 26, } /// <summary>Floating-point Status Register.</summary> - [Flags] protected enum Fpsr + [Flags] + protected enum Fpsr { None = 0, @@ -289,10 +292,11 @@ namespace Ryujinx.Tests.Cpu Idc = 1 << 7, /// <summary>Cumulative saturation bit.</summary> - Qc = 1 << 27 + Qc = 1 << 27, } - [Flags] protected enum FpSkips + [Flags] + protected enum FpSkips { None = 0, @@ -300,7 +304,7 @@ namespace Ryujinx.Tests.Cpu IfNaND = 2, IfUnderflow = 4, - IfOverflow = 8 + IfOverflow = 8, } protected enum FpTolerances @@ -308,15 +312,15 @@ namespace Ryujinx.Tests.Cpu None, UpToOneUlpsS, - UpToOneUlpsD + UpToOneUlpsD, } protected void CompareAgainstUnicorn( - Fpsr fpsrMask = Fpsr.None, - FpSkips fpSkips = FpSkips.None, + Fpsr fpsrMask = Fpsr.None, + FpSkips fpSkips = FpSkips.None, FpTolerances fpTolerances = FpTolerances.None) { - if (IgnoreAllExcept_FpsrQc) + if (_ignoreAllExceptFpsrQc) { fpsrMask &= Fpsr.Qc; } @@ -326,6 +330,7 @@ namespace Ryujinx.Tests.Cpu ManageFpSkips(fpSkips); } +#pragma warning disable IDE0055 // Disable formatting Assert.That(_context.GetX(0), Is.EqualTo(_unicornEmu.X[0]), "X0"); Assert.That(_context.GetX(1), Is.EqualTo(_unicornEmu.X[1]), "X1"); Assert.That(_context.GetX(2), Is.EqualTo(_unicornEmu.X[2]), "X2"); @@ -358,6 +363,7 @@ namespace Ryujinx.Tests.Cpu Assert.That(_context.GetX(29), Is.EqualTo(_unicornEmu.X[29])); Assert.That(_context.GetX(30), Is.EqualTo(_unicornEmu.X[30])); Assert.That(_context.GetX(31), Is.EqualTo(_unicornEmu.SP), "X31"); +#pragma warning restore IDE0055 if (fpTolerances == FpTolerances.None) { @@ -367,6 +373,8 @@ namespace Ryujinx.Tests.Cpu { ManageFpTolerances(fpTolerances); } + +#pragma warning disable IDE0055 // Disable formatting Assert.That(V128ToSimdValue(_context.GetV(1)), Is.EqualTo(_unicornEmu.Q[1]), "V1"); Assert.That(V128ToSimdValue(_context.GetV(2)), Is.EqualTo(_unicornEmu.Q[2]), "V2"); Assert.That(V128ToSimdValue(_context.GetV(3)), Is.EqualTo(_unicornEmu.Q[3]), "V3"); @@ -409,6 +417,7 @@ namespace Ryujinx.Tests.Cpu Assert.That((int)_context.Fpcr, Is.EqualTo(_unicornEmu.Fpcr), "Fpcr"); Assert.That((int)_context.Fpsr & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpsr & (int)fpsrMask), "Fpsr"); +#pragma warning restore IDE0055 if (_usingMemory) { @@ -455,7 +464,7 @@ namespace Ryujinx.Tests.Cpu private void ManageFpTolerances(FpTolerances fpTolerances) { - bool IsNormalOrSubnormalS(float f) => float.IsNormal(f) || float.IsSubnormal(f); + bool IsNormalOrSubnormalS(float f) => float.IsNormal(f) || float.IsSubnormal(f); bool IsNormalOrSubnormalD(double d) => double.IsNormal(d) || double.IsSubnormal(d); if (!Is.EqualTo(_unicornEmu.Q[0]).ApplyTo(V128ToSimdValue(_context.GetV(0))).IsSuccess) @@ -467,13 +476,13 @@ namespace Ryujinx.Tests.Cpu { Assert.Multiple(() => { - Assert.That (_context.GetV(0).Extract<float>(0), + Assert.That(_context.GetV(0).Extract<float>(0), Is.EqualTo(_unicornEmu.Q[0].GetFloat(0)).Within(1).Ulps, "V0[0]"); - Assert.That (_context.GetV(0).Extract<float>(1), + Assert.That(_context.GetV(0).Extract<float>(1), Is.EqualTo(_unicornEmu.Q[0].GetFloat(1)).Within(1).Ulps, "V0[1]"); - Assert.That (_context.GetV(0).Extract<float>(2), + Assert.That(_context.GetV(0).Extract<float>(2), Is.EqualTo(_unicornEmu.Q[0].GetFloat(2)).Within(1).Ulps, "V0[2]"); - Assert.That (_context.GetV(0).Extract<float>(3), + Assert.That(_context.GetV(0).Extract<float>(3), Is.EqualTo(_unicornEmu.Q[0].GetFloat(3)).Within(1).Ulps, "V0[3]"); }); @@ -492,9 +501,9 @@ namespace Ryujinx.Tests.Cpu { Assert.Multiple(() => { - Assert.That (_context.GetV(0).Extract<double>(0), + Assert.That(_context.GetV(0).Extract<double>(0), Is.EqualTo(_unicornEmu.Q[0].GetDouble(0)).Within(1).Ulps, "V0[0]"); - Assert.That (_context.GetV(0).Extract<double>(1), + Assert.That(_context.GetV(0).Extract<double>(1), Is.EqualTo(_unicornEmu.Q[0].GetDouble(1)).Within(1).Ulps, "V0[1]"); }); @@ -513,13 +522,13 @@ namespace Ryujinx.Tests.Cpu return new SimdValue(value.Extract<ulong>(0), value.Extract<ulong>(1)); } - protected static V128 MakeVectorScalar(float value) => new V128(value); - protected static V128 MakeVectorScalar(double value) => new V128(value); + protected static V128 MakeVectorScalar(float value) => new(value); + protected static V128 MakeVectorScalar(double value) => new(value); - protected static V128 MakeVectorE0(ulong e0) => new V128(e0, 0); - protected static V128 MakeVectorE1(ulong e1) => new V128(0, e1); + protected static V128 MakeVectorE0(ulong e0) => new(e0, 0); + protected static V128 MakeVectorE1(ulong e1) => new(0, e1); - protected static V128 MakeVectorE0E1(ulong e0, ulong e1) => new V128(e0, e1); + protected static V128 MakeVectorE0E1(ulong e0, ulong e1) => new(e0, e1); protected static ulong GetVectorE0(V128 vector) => vector.Extract<ulong>(0); protected static ulong GetVectorE1(V128 vector) => vector.Extract<ulong>(1); @@ -528,8 +537,9 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUShort(); - while (( rnd & 0x7C00u) == 0u || + do + rnd = TestContext.CurrentContext.Random.NextUShort(); + while ((rnd & 0x7C00u) == 0u || (~rnd & 0x7C00u) == 0u); return (ushort)rnd; @@ -539,7 +549,8 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUShort(); + do + rnd = TestContext.CurrentContext.Random.NextUShort(); while ((rnd & 0x03FFu) == 0u); return (ushort)(rnd & 0x83FFu); @@ -549,8 +560,9 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUInt(); - while (( rnd & 0x7F800000u) == 0u || + do + rnd = TestContext.CurrentContext.Random.NextUInt(); + while ((rnd & 0x7F800000u) == 0u || (~rnd & 0x7F800000u) == 0u); return rnd; @@ -560,7 +572,8 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUInt(); + do + rnd = TestContext.CurrentContext.Random.NextUInt(); while ((rnd & 0x007FFFFFu) == 0u); return rnd & 0x807FFFFFu; @@ -570,8 +583,9 @@ namespace Ryujinx.Tests.Cpu { ulong rnd; - do rnd = TestContext.CurrentContext.Random.NextULong(); - while (( rnd & 0x7FF0000000000000ul) == 0ul || + do + rnd = TestContext.CurrentContext.Random.NextULong(); + while ((rnd & 0x7FF0000000000000ul) == 0ul || (~rnd & 0x7FF0000000000000ul) == 0ul); return rnd; @@ -581,10 +595,11 @@ namespace Ryujinx.Tests.Cpu { ulong rnd; - do rnd = TestContext.CurrentContext.Random.NextULong(); + do + rnd = TestContext.CurrentContext.Random.NextULong(); while ((rnd & 0x000FFFFFFFFFFFFFul) == 0ul); return rnd & 0x800FFFFFFFFFFFFFul; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTest32.cs b/src/Ryujinx.Tests/Cpu/CpuTest32.cs index a1f6431cf..05a4d3bee 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -14,8 +14,10 @@ namespace Ryujinx.Tests.Cpu public class CpuTest32 { protected static readonly uint Size = (uint)MemoryBlock.GetPageSize(); +#pragma warning disable CA2211 // Non-constant fields should not be visible protected static uint CodeBaseAddress = Size; protected static uint DataBaseAddress = CodeBaseAddress + Size; +#pragma warning restore CA2211 private uint _currAddress; @@ -79,8 +81,8 @@ namespace Ryujinx.Tests.Cpu _context.Dispose(); _ram.Dispose(); - _memory = null; - _context = null; + _memory = null; + _context = null; _cpuContext = null; _unicornEmu = null; @@ -288,16 +290,17 @@ namespace Ryujinx.Tests.Cpu SetWorkingMemory(0, testMem); - RunPrecomputedTestCase(new PrecomputedThumbTestCase(){ + RunPrecomputedTestCase(new PrecomputedThumbTestCase + { Instructions = test.Instructions, StartRegs = test.StartRegs, FinalRegs = test.FinalRegs, }); - foreach (var delta in test.MemoryDelta) + foreach (var (address, value) in test.MemoryDelta) { - testMem[delta.Address - DataBaseAddress + 0] = (byte)(delta.Value >> 0); - testMem[delta.Address - DataBaseAddress + 1] = (byte)(delta.Value >> 8); + testMem[address - DataBaseAddress + 0] = (byte)(value >> 0); + testMem[address - DataBaseAddress + 1] = (byte)(value >> 8); } byte[] mem = _memory.GetSpan(DataBaseAddress, (int)Size).ToArray(); @@ -324,8 +327,8 @@ namespace Ryujinx.Tests.Cpu /// <summary>Round towards Minus Infinity mode.</summary> Rm, /// <summary>Round towards Zero mode.</summary> - Rz - }; + Rz, + } /// <summary>Floating-point Control Register.</summary> protected enum Fpcr @@ -337,7 +340,7 @@ namespace Ryujinx.Tests.Cpu /// <summary>Default NaN mode control bit.</summary> Dn = 25, /// <summary>Alternative half-precision control bit.</summary> - Ahp = 26 + Ahp = 26, } /// <summary>Floating-point Status Register.</summary> @@ -363,7 +366,7 @@ namespace Ryujinx.Tests.Cpu Qc = 1 << 27, /// <summary>NZCV flags.</summary> - Nzcv = (1 << 31) | (1 << 30) | (1 << 29) | (1 << 28) + Nzcv = (1 << 31) | (1 << 30) | (1 << 29) | (1 << 28), } [Flags] @@ -375,7 +378,7 @@ namespace Ryujinx.Tests.Cpu IfNaND = 2, IfUnderflow = 4, - IfOverflow = 8 + IfOverflow = 8, } protected enum FpTolerances @@ -383,7 +386,7 @@ namespace Ryujinx.Tests.Cpu None, UpToOneUlpsS, - UpToOneUlpsD + UpToOneUlpsD, } protected void CompareAgainstUnicorn( @@ -554,13 +557,13 @@ namespace Ryujinx.Tests.Cpu return new SimdValue(value.Extract<ulong>(0), value.Extract<ulong>(1)); } - protected static V128 MakeVectorScalar(float value) => new V128(value); - protected static V128 MakeVectorScalar(double value) => new V128(value); + protected static V128 MakeVectorScalar(float value) => new(value); + protected static V128 MakeVectorScalar(double value) => new(value); - protected static V128 MakeVectorE0(ulong e0) => new V128(e0, 0); - protected static V128 MakeVectorE1(ulong e1) => new V128(0, e1); + protected static V128 MakeVectorE0(ulong e0) => new(e0, 0); + protected static V128 MakeVectorE1(ulong e1) => new(0, e1); - protected static V128 MakeVectorE0E1(ulong e0, ulong e1) => new V128(e0, e1); + protected static V128 MakeVectorE0E1(ulong e0, ulong e1) => new(e0, e1); protected static V128 MakeVectorE0E1E2E3(uint e0, uint e1, uint e2, uint e3) { @@ -574,7 +577,8 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUShort(); + do + rnd = TestContext.CurrentContext.Random.NextUShort(); while ((rnd & 0x7C00u) == 0u || (~rnd & 0x7C00u) == 0u); @@ -585,7 +589,8 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUShort(); + do + rnd = TestContext.CurrentContext.Random.NextUShort(); while ((rnd & 0x03FFu) == 0u); return (ushort)(rnd & 0x83FFu); @@ -595,7 +600,8 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUInt(); + do + rnd = TestContext.CurrentContext.Random.NextUInt(); while ((rnd & 0x7F800000u) == 0u || (~rnd & 0x7F800000u) == 0u); @@ -606,7 +612,8 @@ namespace Ryujinx.Tests.Cpu { uint rnd; - do rnd = TestContext.CurrentContext.Random.NextUInt(); + do + rnd = TestContext.CurrentContext.Random.NextUInt(); while ((rnd & 0x007FFFFFu) == 0u); return rnd & 0x807FFFFFu; @@ -616,7 +623,8 @@ namespace Ryujinx.Tests.Cpu { ulong rnd; - do rnd = TestContext.CurrentContext.Random.NextULong(); + do + rnd = TestContext.CurrentContext.Random.NextULong(); while ((rnd & 0x7FF0000000000000ul) == 0ul || (~rnd & 0x7FF0000000000000ul) == 0ul); @@ -627,10 +635,11 @@ namespace Ryujinx.Tests.Cpu { ulong rnd; - do rnd = TestContext.CurrentContext.Random.NextULong(); + do + rnd = TestContext.CurrentContext.Random.NextULong(); while ((rnd & 0x000FFFFFFFFFFFFFul) == 0ul); return rnd & 0x800FFFFFFFFFFFFFul; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/src/Ryujinx.Tests/Cpu/CpuTestAlu.cs index 7318d9793..9c1876c8c 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAlu.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Tests.Cpu { #if Alu -#region "Helper methods" + #region "Helper methods" private static uint GenLeadingSignsMinus32(int cnt) // 0 <= cnt <= 31 { return ~GenLeadingZeros32(cnt + 1); @@ -33,29 +33,43 @@ namespace Ryujinx.Tests.Cpu private static uint GenLeadingZeros32(int cnt) // 0 <= cnt <= 32 { - if (cnt == 32) return 0u; - if (cnt == 31) return 1u; + if (cnt == 32) + { + return 0u; + } - uint rnd = TestContext.CurrentContext.Random.NextUInt(); - int mask = int.MinValue; + if (cnt == 31) + { + return 1u; + } + + uint rnd = TestContext.CurrentContext.Random.NextUInt(); + int mask = int.MinValue; return (rnd >> (cnt + 1)) | ((uint)mask >> cnt); } private static ulong GenLeadingZeros64(int cnt) // 0 <= cnt <= 64 { - if (cnt == 64) return 0ul; - if (cnt == 63) return 1ul; + if (cnt == 64) + { + return 0ul; + } - ulong rnd = TestContext.CurrentContext.Random.NextULong(); - long mask = long.MinValue; + if (cnt == 63) + { + return 1ul; + } + + ulong rnd = TestContext.CurrentContext.Random.NextULong(); + long mask = long.MinValue; return (rnd >> (cnt + 1)) | ((ulong)mask >> cnt); } -#endregion + #endregion -#region "ValueSource (Types)" - private static IEnumerable<ulong> _GenLeadingSignsX_() + #region "ValueSource (Types)" + private static IEnumerable<ulong> GenLeadingSignsX() { for (int cnt = 0; cnt <= 63; cnt++) { @@ -64,7 +78,7 @@ namespace Ryujinx.Tests.Cpu } } - private static IEnumerable<uint> _GenLeadingSignsW_() + private static IEnumerable<uint> GenLeadingSignsW() { for (int cnt = 0; cnt <= 31; cnt++) { @@ -73,7 +87,7 @@ namespace Ryujinx.Tests.Cpu } } - private static IEnumerable<ulong> _GenLeadingZerosX_() + private static IEnumerable<ulong> GenLeadingZerosX() { for (int cnt = 0; cnt <= 64; cnt++) { @@ -81,19 +95,19 @@ namespace Ryujinx.Tests.Cpu } } - private static IEnumerable<uint> _GenLeadingZerosW_() + private static IEnumerable<uint> GenLeadingZerosW() { for (int cnt = 0; cnt <= 32; cnt++) { yield return GenLeadingZeros32(cnt); } } -#endregion + #endregion [Test, Pairwise, Description("CLS <Xd>, <Xn>")] public void Cls_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource(nameof(_GenLeadingSignsX_))] ulong xn) + [ValueSource(nameof(GenLeadingSignsX))] ulong xn) { uint opcode = 0xDAC01400; // CLS X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -108,7 +122,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLS <Wd>, <Wn>")] public void Cls_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource(nameof(_GenLeadingSignsW_))] uint wn) + [ValueSource(nameof(GenLeadingSignsW))] uint wn) { uint opcode = 0x5AC01400; // CLS W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -123,7 +137,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ <Xd>, <Xn>")] public void Clz_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource(nameof(_GenLeadingZerosX_))] ulong xn) + [ValueSource(nameof(GenLeadingZerosX))] ulong xn) { uint opcode = 0xDAC01000; // CLZ X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -138,7 +152,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ <Wd>, <Wn>")] public void Clz_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [ValueSource(nameof(_GenLeadingZerosW_))] uint wn) + [ValueSource(nameof(GenLeadingZerosW))] uint wn) { uint opcode = 0x5AC01000; // CLZ W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -263,4 +277,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs index 0d009e90e..404dfd7de 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs @@ -9,8 +9,8 @@ namespace Ryujinx.Tests.Cpu { #if Alu32 -#region "ValueSource (Opcodes)" - private static uint[] _SU_H_AddSub_8_() + #region "ValueSource (Opcodes)" + private static uint[] SuHAddSub8() { return new[] { @@ -21,22 +21,22 @@ namespace Ryujinx.Tests.Cpu 0xe6500f90u, // UADD8 R0, R0, R0 0xe6500ff0u, // USUB8 R0, R0, R0 0xe6700f90u, // UHADD8 R0, R0, R0 - 0xe6700ff0u // UHSUB8 R0, R0, R0 + 0xe6700ff0u, // UHSUB8 R0, R0, R0 }; } - private static uint[] _Ssat_Usat_() + private static uint[] SsatUsat() { return new[] { 0xe6a00010u, // SSAT R0, #1, R0, LSL #0 0xe6a00050u, // SSAT R0, #1, R0, ASR #32 0xe6e00010u, // USAT R0, #0, R0, LSL #0 - 0xe6e00050u // USAT R0, #0, R0, ASR #32 + 0xe6e00050u, // USAT R0, #0, R0, ASR #32 }; } - private static uint[] _Ssat16_Usat16_() + private static uint[] Ssat16Usat16() { return new[] { @@ -45,17 +45,17 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _Lsr_Lsl_Asr_Ror_() + private static uint[] LsrLslAsrRor() { return new[] { 0xe1b00030u, // LSRS R0, R0, R0 0xe1b00010u, // LSLS R0, R0, R0 0xe1b00050u, // ASRS R0, R0, R0 - 0xe1b00070u // RORS R0, R0, R0 + 0xe1b00070u, // RORS R0, R0, R0 }; } -#endregion + #endregion private const int RndCnt = 2; @@ -76,7 +76,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Lsr_Lsl_Asr_Ror([ValueSource(nameof(_Lsr_Lsl_Asr_Ror_))] uint opcode, + public void Lsr_Lsl_Asr_Ror([ValueSource(nameof(LsrLslAsrRor))] uint opcode, [Values(0x00000000u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu)] uint shiftValue, [Range(0, 31)] int shiftAmount) @@ -130,7 +130,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Ssat_Usat([ValueSource(nameof(_Ssat_Usat_))] uint opcode, + public void Ssat_Usat([ValueSource(nameof(SsatUsat))] uint opcode, [Values(0u, 0xdu)] uint rd, [Values(1u, 0xdu)] uint rn, [Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint sat, @@ -148,7 +148,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Ssat16_Usat16([ValueSource(nameof(_Ssat16_Usat16_))] uint opcode, + public void Ssat16_Usat16([ValueSource(nameof(Ssat16Usat16))] uint opcode, [Values(0u, 0xdu)] uint rd, [Values(1u, 0xdu)] uint rn, [Values(0u, 7u, 8u, 0xfu)] uint sat, @@ -165,7 +165,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void SU_H_AddSub_8([ValueSource(nameof(_SU_H_AddSub_8_))] uint opcode, + public void SU_H_AddSub_8([ValueSource(nameof(SuHAddSub8))] uint opcode, [Values(0u, 0xdu)] uint rd, [Values(1u)] uint rm, [Values(2u)] uint rn, @@ -191,9 +191,9 @@ namespace Ryujinx.Tests.Cpu [Random(RndCnt)] uint w2) { uint opUadd8 = 0xE6500F90; // UADD8 R0, R0, R0 - uint opSel = 0xE6800FB0; // SEL R0, R0, R0 + uint opSel = 0xE6800FB0; // SEL R0, R0, R0 - opUadd8 |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rn & 15) << 16); + opUadd8 |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rn & 15) << 16); opSel |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rn & 15) << 16); SetContext(r0: w0, r1: w1, r2: w2); @@ -206,4 +206,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs index 0265e5230..1e48086b2 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAluBinary.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Tests.Cpu } } -#region "ValueSource (CRC32)" + #region "ValueSource (CRC32)" private static CrcTest[] _CRC32_Test_Values_() { // Created with http://www.sunshine2k.de/coding/javascript/crc/crc_js.html, with: @@ -48,10 +48,10 @@ namespace Ryujinx.Tests.Cpu new CrcTest(0xffffffffu, 0x7f_ff_ff_ff_ff_ff_ff_ffu, false, 0x00ffffff, 0x0000ffff, 0x00000000, 0x3303a3c3), new CrcTest(0xffffffffu, 0x80_00_00_00_00_00_00_00u, false, 0x2dfd1072, 0xbe26ed00, 0xdebb20e3, 0x7765a3b6), new CrcTest(0xffffffffu, 0xff_ff_ff_ff_ff_ff_ff_ffu, false, 0x00ffffff, 0x0000ffff, 0x00000000, 0xdebb20e3), - new CrcTest(0xffffffffu, 0xa0_02_f1_ca_52_78_8c_1cu, false, 0x39fc4c3d, 0xbc5f7f56, 0x4ed8e906, 0x12cb419c) + new CrcTest(0xffffffffu, 0xa0_02_f1_ca_52_78_8c_1cu, false, 0x39fc4c3d, 0xbc5f7f56, 0x4ed8e906, 0x12cb419c), }; } -#endregion + #endregion [Test, Combinatorial] public void Crc32_b_h_w_x([Values(0u)] uint rd, @@ -304,4 +304,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs index d92a95224..4e355358a 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAluBinary32.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Tests.Cpu } } -#region "ValueSource (CRC32/CRC32C)" + #region "ValueSource (CRC32/CRC32C)" private static CrcTest32[] _CRC32_Test_Values_() { // Created with http://www.sunshine2k.de/coding/javascript/crc/crc_js.html, with: @@ -60,10 +60,10 @@ namespace Ryujinx.Tests.Cpu new CrcTest32(0xffffffffu, 0x7f_ff_ff_ffu, true, 0x00ffffff, 0x0000ffff, 0x82f63b78), new CrcTest32(0xffffffffu, 0x80_00_00_00u, true, 0xad82acae, 0x0e9e882d, 0x356e8f40), new CrcTest32(0xffffffffu, 0xff_ff_ff_ffu, true, 0x00ffffff, 0x0000ffff, 0x00000000), - new CrcTest32(0xffffffffu, 0x9d_cb_12_f0u, true, 0x5eecc3db, 0xbb6111cb, 0xcfb54fc9) + new CrcTest32(0xffffffffu, 0x9d_cb_12_f0u, true, 0x5eecc3db, 0xbb6111cb, 0xcfb54fc9), }; } -#endregion + #endregion [Test, Combinatorial] public void Crc32_Crc32c_b_h_w([Values(0u)] uint rd, diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAluImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluImm.cs index c97ef9ed0..5c00cbc45 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAluImm.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAluImm.cs @@ -430,4 +430,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs index cc12f3871..eeeef085c 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAluImm32.cs @@ -9,8 +9,8 @@ namespace Ryujinx.Tests.Cpu { #if AluRs32 -#region "ValueSource (Opcodes)" - private static uint[] _opcodes() + #region "ValueSource (Opcodes)" + private static uint[] Opcodes() { return new[] { @@ -30,12 +30,12 @@ namespace Ryujinx.Tests.Cpu 0xe2500000u, // SUBS R0, R0, #0 }; } -#endregion + #endregion private const int RndCnt = 2; [Test, Pairwise] - public void TestCpuTestAluImm32([ValueSource(nameof(_opcodes))] uint opcode, + public void TestCpuTestAluImm32([ValueSource(nameof(Opcodes))] uint opcode, [Values(0u, 13u)] uint rd, [Values(1u, 13u)] uint rn, [Random(RndCnt)] uint imm, @@ -52,4 +52,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAluRs.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluRs.cs index 20e0e396e..f63360c21 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAluRs.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAluRs.cs @@ -892,4 +892,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs index d241aac48..f8fb013d5 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAluRs32.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu { #if AluRs32 -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Add_Adds_Rsb_Rsbs_() { return new[] @@ -17,7 +17,7 @@ namespace Ryujinx.Tests.Cpu 0xe0800000u, // ADD R0, R0, R0, LSL #0 0xe0900000u, // ADDS R0, R0, R0, LSL #0 0xe0600000u, // RSB R0, R0, R0, LSL #0 - 0xe0700000u // RSBS R0, R0, R0, LSL #0 + 0xe0700000u, // RSBS R0, R0, R0, LSL #0 }; } @@ -30,14 +30,14 @@ namespace Ryujinx.Tests.Cpu 0xe0e00000u, // RSC R0, R0, R0 0xe0f00000u, // RSCS R0, R0, R0 0xe0c00000u, // SBC R0, R0, R0 - 0xe0d00000u // SBCS R0, R0, R0 + 0xe0d00000u, // SBCS R0, R0, R0 }; } -#endregion + #endregion [Test, Pairwise] - public void Adc_Adcs_Rsc_Rscs_Sbc_Sbcs([ValueSource("_Adc_Adcs_Rsc_Rscs_Sbc_Sbcs_")] uint opcode, + public void Adc_Adcs_Rsc_Rscs_Sbc_Sbcs([ValueSource(nameof(_Adc_Adcs_Rsc_Rscs_Sbc_Sbcs_))] uint opcode, [Values(0u, 13u)] uint rd, [Values(1u, 13u)] uint rn, [Values(2u, 13u)] uint rm, @@ -57,7 +57,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] - public void Add_Adds_Rsb_Rsbs([ValueSource("_Add_Adds_Rsb_Rsbs_")] uint opcode, + public void Add_Adds_Rsb_Rsbs([ValueSource(nameof(_Add_Adds_Rsb_Rsbs_))] uint opcode, [Values(0u, 13u)] uint rd, [Values(1u, 13u)] uint rn, [Values(2u, 13u)] uint rm, @@ -79,4 +79,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAluRx.cs b/src/Ryujinx.Tests/Cpu/CpuTestAluRx.cs index d51e76209..9897bdba3 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestAluRx.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestAluRx.cs @@ -720,4 +720,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestBf32.cs b/src/Ryujinx.Tests/Cpu/CpuTestBf32.cs index 871e7649f..5afefd62c 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestBf32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestBf32.cs @@ -103,4 +103,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestBfm.cs b/src/Ryujinx.Tests/Cpu/CpuTestBfm.cs index c169ee41c..ed911d058 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestBfm.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestBfm.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestBfm : CpuTest { #if Bfm - private const int RndCnt = 2; + private const int RndCnt = 2; [Test, Pairwise, Description("BFM <Xd>, <Xn>, #<immr>, #<imms>")] public void Bfm_64bit([Values(0u, 31u)] uint rd, @@ -127,4 +127,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs index 2c431fb23..1bad4c87d 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs @@ -99,4 +99,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs b/src/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs index 1021de930..27fe24118 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs @@ -107,4 +107,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestCsel.cs b/src/Ryujinx.Tests/Cpu/CpuTestCsel.cs index 379cdfd8e..86b1f092a 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestCsel.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestCsel.cs @@ -202,4 +202,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs index c86d3996e..aab009766 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu { #if Misc -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static IEnumerable<ulong> _1S_F_() { yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) @@ -24,19 +24,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -54,15 +54,15 @@ namespace Ryujinx.Tests.Cpu yield return (grbg << 32) | rnd2; } } -#endregion + #endregion - private const int RndCnt = 2; + private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; -#region "AluImm & Csel" + #region "AluImm & Csel" [Test, Pairwise] public void Adds_Csinc_64bit([Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn, @@ -73,10 +73,10 @@ namespace Ryujinx.Tests.Cpu 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, 0b1100u, 0b1101u)] uint cond) // GT, LE> { - uint opCmn = 0xB100001F; // ADDS X31, X0, #0, LSL #0 -> CMN X0, #0, LSL #0 + uint opCmn = 0xB100001F; // ADDS X31, X0, #0, LSL #0 -> CMN X0, #0, LSL #0 uint opCset = 0x9A9F07E0; // CSINC X0, X31, X31, EQ -> CSET X0, NE - opCmn |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCmn |= ((shift & 3) << 22) | ((imm & 4095) << 10); opCset |= ((cond & 15) << 12); SetContext(x0: xn); @@ -98,10 +98,10 @@ namespace Ryujinx.Tests.Cpu 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, 0b1100u, 0b1101u)] uint cond) // GT, LE> { - uint opCmn = 0x3100001F; // ADDS W31, W0, #0, LSL #0 -> CMN W0, #0, LSL #0 + uint opCmn = 0x3100001F; // ADDS W31, W0, #0, LSL #0 -> CMN W0, #0, LSL #0 uint opCset = 0x1A9F07E0; // CSINC W0, W31, W31, EQ -> CSET W0, NE - opCmn |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCmn |= ((shift & 3) << 22) | ((imm & 4095) << 10); opCset |= ((cond & 15) << 12); SetContext(x0: wn); @@ -123,10 +123,10 @@ namespace Ryujinx.Tests.Cpu 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, 0b1100u, 0b1101u)] uint cond) // GT, LE> { - uint opCmp = 0xF100001F; // SUBS X31, X0, #0, LSL #0 -> CMP X0, #0, LSL #0 + uint opCmp = 0xF100001F; // SUBS X31, X0, #0, LSL #0 -> CMP X0, #0, LSL #0 uint opCset = 0x9A9F07E0; // CSINC X0, X31, X31, EQ -> CSET X0, NE - opCmp |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCmp |= ((shift & 3) << 22) | ((imm & 4095) << 10); opCset |= ((cond & 15) << 12); SetContext(x0: xn); @@ -148,10 +148,10 @@ namespace Ryujinx.Tests.Cpu 0b1000u, 0b1001u, 0b1010u, 0b1011u, // HI, LS, GE, LT, 0b1100u, 0b1101u)] uint cond) // GT, LE> { - uint opCmp = 0x7100001F; // SUBS W31, W0, #0, LSL #0 -> CMP W0, #0, LSL #0 + uint opCmp = 0x7100001F; // SUBS W31, W0, #0, LSL #0 -> CMP W0, #0, LSL #0 uint opCset = 0x1A9F07E0; // CSINC W0, W31, W31, EQ -> CSET W0, NE - opCmp |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCmp |= ((shift & 3) << 22) | ((imm & 4095) << 10); opCset |= ((cond & 15) << 12); SetContext(x0: wn); @@ -162,10 +162,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } -#endregion + #endregion + // Roots. [Explicit] - [TestCase(0xFFFFFFFDu)] // Roots. + [TestCase(0xFFFFFFFDu)] [TestCase(0x00000005u)] public void Misc1(uint a) { @@ -196,25 +197,26 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetContext().GetX(0), Is.Zero); } + // 18 integer solutions. [Explicit] - [TestCase(-20f, -5f)] // 18 integer solutions. - [TestCase(-12f, -6f)] - [TestCase(-12f, 3f)] - [TestCase( -8f, -8f)] - [TestCase( -6f, -12f)] - [TestCase( -5f, -20f)] - [TestCase( -4f, 2f)] - [TestCase( -3f, 12f)] - [TestCase( -2f, 4f)] - [TestCase( 2f, -4f)] - [TestCase( 3f, -12f)] - [TestCase( 4f, -2f)] - [TestCase( 5f, 20f)] - [TestCase( 6f, 12f)] - [TestCase( 8f, 8f)] - [TestCase( 12f, -3f)] - [TestCase( 12f, 6f)] - [TestCase( 20f, 5f)] + [TestCase(-20f, -5f)] + [TestCase(-12f, -6f)] + [TestCase(-12f, 3f)] + [TestCase(-8f, -8f)] + [TestCase(-6f, -12f)] + [TestCase(-5f, -20f)] + [TestCase(-4f, 2f)] + [TestCase(-3f, 12f)] + [TestCase(-2f, 4f)] + [TestCase(2f, -4f)] + [TestCase(3f, -12f)] + [TestCase(4f, -2f)] + [TestCase(5f, 20f)] + [TestCase(6f, 12f)] + [TestCase(8f, 8f)] + [TestCase(12f, -3f)] + [TestCase(12f, 6f)] + [TestCase(20f, 5f)] public void Misc2(float a, float b) { // 1 / ((1 / a + 1 / b) ^ 2) = 16 @@ -242,25 +244,26 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetContext().GetV(0).As<float>(), Is.EqualTo(16f)); } + // 18 integer solutions. [Explicit] - [TestCase(-20d, -5d)] // 18 integer solutions. - [TestCase(-12d, -6d)] - [TestCase(-12d, 3d)] - [TestCase( -8d, -8d)] - [TestCase( -6d, -12d)] - [TestCase( -5d, -20d)] - [TestCase( -4d, 2d)] - [TestCase( -3d, 12d)] - [TestCase( -2d, 4d)] - [TestCase( 2d, -4d)] - [TestCase( 3d, -12d)] - [TestCase( 4d, -2d)] - [TestCase( 5d, 20d)] - [TestCase( 6d, 12d)] - [TestCase( 8d, 8d)] - [TestCase( 12d, -3d)] - [TestCase( 12d, 6d)] - [TestCase( 20d, 5d)] + [TestCase(-20d, -5d)] + [TestCase(-12d, -6d)] + [TestCase(-12d, 3d)] + [TestCase(-8d, -8d)] + [TestCase(-6d, -12d)] + [TestCase(-5d, -20d)] + [TestCase(-4d, 2d)] + [TestCase(-3d, 12d)] + [TestCase(-2d, 4d)] + [TestCase(2d, -4d)] + [TestCase(3d, -12d)] + [TestCase(4d, -2d)] + [TestCase(5d, 20d)] + [TestCase(6d, 12d)] + [TestCase(8d, 8d)] + [TestCase(12d, -3d)] + [TestCase(12d, 6d)] + [TestCase(20d, 5d)] public void Misc3(double a, double b) { // 1 / ((1 / a + 1 / b) ^ 2) = 16 @@ -291,7 +294,7 @@ namespace Ryujinx.Tests.Cpu [Test, Ignore("The Tester supports only one return point.")] public void MiscF([Range(0u, 92u, 1u)] uint a) { - ulong Fn(uint n) + static ulong Fn(uint n) { ulong x = 0, y = 1, z; @@ -395,9 +398,9 @@ namespace Ryujinx.Tests.Cpu } [Explicit] - [TestCase( 0ul)] - [TestCase( 1ul)] - [TestCase( 2ul)] + [TestCase(0ul)] + [TestCase(1ul)] + [TestCase(2ul)] [TestCase(42ul)] public void SanityCheck(ulong a) { @@ -439,8 +442,8 @@ namespace Ryujinx.Tests.Cpu v1: MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()), v2: MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()), overflow: TestContext.CurrentContext.Random.NextBool(), - carry: TestContext.CurrentContext.Random.NextBool(), - zero: TestContext.CurrentContext.Random.NextBool(), + carry: TestContext.CurrentContext.Random.NextBool(), + zero: TestContext.CurrentContext.Random.NextBool(), negative: TestContext.CurrentContext.Random.NextBool()); Opcode(0xBD400001); // LDR S1, [X0,#0] @@ -463,8 +466,8 @@ namespace Ryujinx.Tests.Cpu v0: MakeVectorE0E1(a, TestContext.CurrentContext.Random.NextULong()), v1: MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()), overflow: TestContext.CurrentContext.Random.NextBool(), - carry: TestContext.CurrentContext.Random.NextBool(), - zero: TestContext.CurrentContext.Random.NextBool(), + carry: TestContext.CurrentContext.Random.NextBool(), + zero: TestContext.CurrentContext.Random.NextBool(), negative: TestContext.CurrentContext.Random.NextBool()); Opcode(0x1E202008); // FCMP S0, #0.0 diff --git a/src/Ryujinx.Tests/Cpu/CpuTestMisc32.cs b/src/Ryujinx.Tests/Cpu/CpuTestMisc32.cs index e61150132..e984a1584 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestMisc32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestMisc32.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Cpu { #if Misc32 -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static IEnumerable<ulong> _1S_F_() { yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) @@ -23,19 +23,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -53,13 +53,13 @@ namespace Ryujinx.Tests.Cpu yield return (grbg << 32) | rnd2; } } -#endregion + #endregion private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; [Test, Pairwise] public void Vmsr_Vcmp_Vmrs([ValueSource(nameof(_1S_F_))] ulong a, @@ -75,10 +75,10 @@ namespace Ryujinx.Tests.Cpu ? TestContext.CurrentContext.Random.NextUInt(0xf) << 28 : TestContext.CurrentContext.Random.NextUInt(); - bool v = mode3 ? TestContext.CurrentContext.Random.NextBool() : false; - bool c = mode3 ? TestContext.CurrentContext.Random.NextBool() : false; - bool z = mode3 ? TestContext.CurrentContext.Random.NextBool() : false; - bool n = mode3 ? TestContext.CurrentContext.Random.NextBool() : false; + bool v = mode3 && TestContext.CurrentContext.Random.NextBool(); + bool c = mode3 && TestContext.CurrentContext.Random.NextBool(); + bool z = mode3 && TestContext.CurrentContext.Random.NextBool(); + bool n = mode3 && TestContext.CurrentContext.Random.NextBool(); int fpscr = mode1 ? (int)TestContext.CurrentContext.Random.NextUInt() diff --git a/src/Ryujinx.Tests/Cpu/CpuTestMov.cs b/src/Ryujinx.Tests/Cpu/CpuTestMov.cs index c437560a2..c8ee3857e 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestMov.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestMov.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestMov : CpuTest { #if Mov - private const int RndCnt = 2; + private const int RndCnt = 2; [Test, Pairwise, Description("MOVK <Xd>, #<imm>{, LSL #<shift>}")] public void Movk_64bit([Values(0u, 31u)] uint rd, @@ -109,4 +109,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestMul.cs b/src/Ryujinx.Tests/Cpu/CpuTestMul.cs index c94bcbdb6..164ed9773 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestMul.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestMul.cs @@ -223,4 +223,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestMul32.cs b/src/Ryujinx.Tests/Cpu/CpuTestMul32.cs index 0743e913c..58da762dd 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestMul32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestMul32.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu { #if Mul32 -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Smlabb_Smlabt_Smlatb_Smlatt_() { return new[] @@ -49,10 +49,10 @@ namespace Ryujinx.Tests.Cpu 0xe12000e0u, // SMULWT R0, R0, R0 }; } -#endregion + #endregion [Test, Pairwise, Description("SMLA<x><y> <Rd>, <Rn>, <Rm>, <Ra>")] - public void Smla___32bit([ValueSource("_Smlabb_Smlabt_Smlatb_Smlatt_")] uint opcode, + public void Smla___32bit([ValueSource(nameof(_Smlabb_Smlabt_Smlatb_Smlatt_))] uint opcode, [Values(0u, 0xdu)] uint rn, [Values(1u, 0xdu)] uint rm, [Values(2u, 0xdu)] uint ra, @@ -74,7 +74,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SMLAW<x> <Rd>, <Rn>, <Rm>, <Ra>")] - public void Smlaw__32bit([ValueSource("_Smlawb_Smlawt_")] uint opcode, + public void Smlaw__32bit([ValueSource(nameof(_Smlawb_Smlawt_))] uint opcode, [Values(0u, 0xdu)] uint rn, [Values(1u, 0xdu)] uint rm, [Values(2u, 0xdu)] uint ra, @@ -96,7 +96,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SMUL<x><y> <Rd>, <Rn>, <Rm>")] - public void Smul___32bit([ValueSource("_Smulbb_Smulbt_Smultb_Smultt_")] uint opcode, + public void Smul___32bit([ValueSource(nameof(_Smulbb_Smulbt_Smultb_Smultt_))] uint opcode, [Values(0u, 0xdu)] uint rn, [Values(1u, 0xdu)] uint rm, [Values(2u, 0xdu)] uint rd, @@ -115,7 +115,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SMULW<x> <Rd>, <Rn>, <Rm>")] - public void Smulw__32bit([ValueSource("_Smulwb_Smulwt_")] uint opcode, + public void Smulw__32bit([ValueSource(nameof(_Smulwb_Smulwt_))] uint opcode, [Values(0u, 0xdu)] uint rn, [Values(1u, 0xdu)] uint rm, [Values(2u, 0xdu)] uint rd, @@ -134,4 +134,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 7c68c0fa1..4c568a8f4 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu { #if Simd -#region "Helper methods" + #region "Helper methods" private static byte GenLeadingSignsMinus8(int cnt) // 0 <= cnt <= 7 { return (byte)(~(uint)GenLeadingZeros8(cnt + 1)); @@ -45,10 +45,17 @@ namespace Ryujinx.Tests.Cpu private static byte GenLeadingZeros8(int cnt) // 0 <= cnt <= 8 { - if (cnt == 8) return 0; - if (cnt == 7) return 1; + if (cnt == 8) + { + return 0; + } - byte rnd = TestContext.CurrentContext.Random.NextByte(); + if (cnt == 7) + { + return 1; + } + + byte rnd = TestContext.CurrentContext.Random.NextByte(); sbyte mask = sbyte.MinValue; return (byte)(((uint)rnd >> (cnt + 1)) | ((uint)((byte)mask) >> cnt)); @@ -56,120 +63,160 @@ namespace Ryujinx.Tests.Cpu private static ushort GenLeadingZeros16(int cnt) // 0 <= cnt <= 16 { - if (cnt == 16) return 0; - if (cnt == 15) return 1; + if (cnt == 16) + { + return 0; + } - ushort rnd = TestContext.CurrentContext.Random.NextUShort(); - short mask = short.MinValue; + if (cnt == 15) + { + return 1; + } + + ushort rnd = TestContext.CurrentContext.Random.NextUShort(); + short mask = short.MinValue; return (ushort)(((uint)rnd >> (cnt + 1)) | ((uint)((ushort)mask) >> cnt)); } private static uint GenLeadingZeros32(int cnt) // 0 <= cnt <= 32 { - if (cnt == 32) return 0u; - if (cnt == 31) return 1u; + if (cnt == 32) + { + return 0u; + } - uint rnd = TestContext.CurrentContext.Random.NextUInt(); - int mask = int.MinValue; + if (cnt == 31) + { + return 1u; + } + + uint rnd = TestContext.CurrentContext.Random.NextUInt(); + int mask = int.MinValue; return (rnd >> (cnt + 1)) | ((uint)mask >> cnt); } -#endregion + #endregion -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _1B1H1S1D_() { - return new[] { 0x0000000000000000ul, 0x000000000000007Ful, - 0x0000000000000080ul, 0x00000000000000FFul, - 0x0000000000007FFFul, 0x0000000000008000ul, - 0x000000000000FFFFul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul, - 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, - 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x000000000000007Ful, + 0x0000000000000080ul, 0x00000000000000FFul, + 0x0000000000007FFFul, 0x0000000000008000ul, + 0x000000000000FFFFul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, + 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _1D_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _1H1S1D_() { - return new[] { 0x0000000000000000ul, 0x0000000000007FFFul, - 0x0000000000008000ul, 0x000000000000FFFFul, - 0x000000007FFFFFFFul, 0x0000000080000000ul, - 0x00000000FFFFFFFFul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul, + 0x000000007FFFFFFFul, 0x0000000080000000ul, + 0x00000000FFFFFFFFul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _1S_() { - return new[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + }; } private static ulong[] _2S_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H2S1D_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H2S_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H2S1D_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static uint[] _W_() { - return new[] { 0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { + 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu, + }; } private static ulong[] _X_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<ulong> _1H_F_() @@ -183,19 +230,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000000003FFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000000008000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x000000000000FC00ul; // -Infinity yield return 0x0000000000007C00ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x000000000000FE00ul; // -QNaN (all zeros payload) yield return 0x000000000000FDFFul; // -SNaN (all ones payload) @@ -225,19 +272,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x03FF03FF03FF03FFul; // +Max Subnormal yield return 0x0001000100010001ul; // +Min Subnormal - if (!NoZeros) + if (!_noZeros) { yield return 0x8000800080008000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFC00FC00FC00FC00ul; // -Infinity yield return 0x7C007C007C007C00ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFE00FE00FE00FE00ul; // -QNaN (all zeros payload) yield return 0xFDFFFDFFFDFFFDFFul; // -SNaN (all ones payload) @@ -266,19 +313,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -321,19 +368,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -370,19 +417,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -424,19 +471,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -473,19 +520,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -527,19 +574,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -642,9 +689,9 @@ namespace Ryujinx.Tests.Cpu (cnt << 24) | (cnt << 16) | (cnt << 08) | cnt; } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _SU_Add_Max_Min_V_V_8BB_4HH_() { return new[] @@ -653,7 +700,7 @@ namespace Ryujinx.Tests.Cpu 0x0E30A800u, // SMAXV B0, V0.8B 0x0E31A800u, // SMINV B0, V0.8B 0x2E30A800u, // UMAXV B0, V0.8B - 0x2E31A800u // UMINV B0, V0.8B + 0x2E31A800u, // UMINV B0, V0.8B }; } @@ -665,7 +712,7 @@ namespace Ryujinx.Tests.Cpu 0x4E30A800u, // SMAXV B0, V0.16B 0x4E31A800u, // SMINV B0, V0.16B 0x6E30A800u, // UMAXV B0, V0.16B - 0x6E31A800u // UMINV B0, V0.16B + 0x6E31A800u, // UMINV B0, V0.16B }; } @@ -676,7 +723,7 @@ namespace Ryujinx.Tests.Cpu 0x1E20C020u, // FABS S0, S1 0x1E214020u, // FNEG S0, S1 0x5EA1F820u, // FRECPX S0, S1 - 0x1E21C020u // FSQRT S0, S1 + 0x1E21C020u, // FSQRT S0, S1 }; } @@ -687,7 +734,7 @@ namespace Ryujinx.Tests.Cpu 0x1E60C020u, // FABS D0, D1 0x1E614020u, // FNEG D0, D1 0x5EE1F820u, // FRECPX D0, D1 - 0x1E61C020u // FSQRT D0, D1 + 0x1E61C020u, // FSQRT D0, D1 }; } @@ -697,7 +744,7 @@ namespace Ryujinx.Tests.Cpu { 0x0EA0F800u, // FABS V0.2S, V0.2S 0x2EA0F800u, // FNEG V0.2S, V0.2S - 0x2EA1F800u // FSQRT V0.2S, V0.2S + 0x2EA1F800u, // FSQRT V0.2S, V0.2S }; } @@ -707,7 +754,7 @@ namespace Ryujinx.Tests.Cpu { 0x4EE0F800u, // FABS V0.2D, V0.2D 0x6EE0F800u, // FNEG V0.2D, V0.2D - 0x6EE1F800u // FSQRT V0.2D, V0.2D + 0x6EE1F800u, // FSQRT V0.2D, V0.2D }; } @@ -717,7 +764,7 @@ namespace Ryujinx.Tests.Cpu { 0x7E30D820u, // FADDP S0, V1.2S 0x7E30C820u, // FMAXNMP S0, V1.2S - 0x7EB0C820u // FMINNMP S0, V1.2S + 0x7EB0C820u, // FMINNMP S0, V1.2S }; } @@ -727,7 +774,7 @@ namespace Ryujinx.Tests.Cpu { 0x7E70D820u, // FADDP D0, V1.2D 0x7E70C820u, // FMAXNMP D0, V1.2D - 0x7EF0C820u // FMINNMP D0, V1.2D + 0x7EF0C820u, // FMINNMP D0, V1.2D }; } @@ -739,7 +786,7 @@ namespace Ryujinx.Tests.Cpu 0x7EA0C820u, // FCMGE S0, S1, #0.0 0x5EA0C820u, // FCMGT S0, S1, #0.0 0x7EA0D820u, // FCMLE S0, S1, #0.0 - 0x5EA0E820u // FCMLT S0, S1, #0.0 + 0x5EA0E820u, // FCMLT S0, S1, #0.0 }; } @@ -751,7 +798,7 @@ namespace Ryujinx.Tests.Cpu 0x7EE0C820u, // FCMGE D0, D1, #0.0 0x5EE0C820u, // FCMGT D0, D1, #0.0 0x7EE0D820u, // FCMLE D0, D1, #0.0 - 0x5EE0E820u // FCMLT D0, D1, #0.0 + 0x5EE0E820u, // FCMLT D0, D1, #0.0 }; } @@ -763,7 +810,7 @@ namespace Ryujinx.Tests.Cpu 0x2EA0C800u, // FCMGE V0.2S, V0.2S, #0.0 0x0EA0C800u, // FCMGT V0.2S, V0.2S, #0.0 0x2EA0D800u, // FCMLE V0.2S, V0.2S, #0.0 - 0x0EA0E800u // FCMLT V0.2S, V0.2S, #0.0 + 0x0EA0E800u, // FCMLT V0.2S, V0.2S, #0.0 }; } @@ -775,7 +822,7 @@ namespace Ryujinx.Tests.Cpu 0x6EE0C800u, // FCMGE V0.2D, V0.2D, #0.0 0x4EE0C800u, // FCMGT V0.2D, V0.2D, #0.0 0x6EE0D800u, // FCMLE V0.2D, V0.2D, #0.0 - 0x4EE0E800u // FCMLT V0.2D, V0.2D, #0.0 + 0x4EE0E800u, // FCMLT V0.2D, V0.2D, #0.0 }; } @@ -784,7 +831,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E202028u, // FCMP S1, #0.0 - 0x1E202038u // FCMPE S1, #0.0 + 0x1E202038u, // FCMPE S1, #0.0 }; } @@ -793,7 +840,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E602028u, // FCMP D1, #0.0 - 0x1E602038u // FCMPE D1, #0.0 + 0x1E602038u, // FCMPE D1, #0.0 }; } @@ -801,7 +848,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E22C020u // FCVT D0, S1 + 0x1E22C020u, // FCVT D0, S1 }; } @@ -809,7 +856,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E624020u // FCVT S0, D1 + 0x1E624020u, // FCVT S0, D1 }; } @@ -817,7 +864,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E23C020u // FCVT H0, S1 + 0x1E23C020u, // FCVT H0, S1 }; } @@ -825,7 +872,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E63C020u // FCVT H0, D1 + 0x1E63C020u, // FCVT H0, D1 }; } @@ -833,7 +880,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1EE24020u // FCVT S0, H1 + 0x1EE24020u, // FCVT S0, H1 }; } @@ -841,7 +888,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1EE2C020u // FCVT D0, H1 + 0x1EE2C020u, // FCVT D0, H1 }; } @@ -854,7 +901,7 @@ namespace Ryujinx.Tests.Cpu 0x5E21A820u, // FCVTNS S0, S1 0x7E21A820u, // FCVTNU S0, S1 0x5EA1B820u, // FCVTZS S0, S1 - 0x7EA1B820u // FCVTZU S0, S1 + 0x7EA1B820u, // FCVTZU S0, S1 }; } @@ -867,7 +914,7 @@ namespace Ryujinx.Tests.Cpu 0x5E61A820u, // FCVTNS D0, D1 0x7E61A820u, // FCVTNU D0, D1 0x5EE1B820u, // FCVTZS D0, D1 - 0x7EE1B820u // FCVTZU D0, D1 + 0x7EE1B820u, // FCVTZU D0, D1 }; } @@ -881,7 +928,7 @@ namespace Ryujinx.Tests.Cpu 0x0E21A800u, // FCVTNS V0.2S, V0.2S 0x2E21A800u, // FCVTNU V0.2S, V0.2S 0x0EA1B800u, // FCVTZS V0.2S, V0.2S - 0x2EA1B800u // FCVTZU V0.2S, V0.2S + 0x2EA1B800u, // FCVTZU V0.2S, V0.2S }; } @@ -895,7 +942,7 @@ namespace Ryujinx.Tests.Cpu 0x4E61A800u, // FCVTNS V0.2D, V0.2D 0x6E61A800u, // FCVTNU V0.2D, V0.2D 0x4EE1B800u, // FCVTZS V0.2D, V0.2D - 0x6EE1B800u // FCVTZU V0.2D, V0.2D + 0x6EE1B800u, // FCVTZU V0.2D, V0.2D }; } @@ -903,7 +950,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x0E217800u // FCVTL V0.4S, V0.4H + 0x0E217800u, // FCVTL V0.4S, V0.4H }; } @@ -911,7 +958,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x0E617800u // FCVTL V0.2D, V0.2S + 0x0E617800u, // FCVTL V0.2D, V0.2S }; } @@ -919,7 +966,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x0E216800u // FCVTN V0.4H, V0.4S + 0x0E216800u, // FCVTN V0.4H, V0.4S }; } @@ -927,7 +974,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x0E616800u // FCVTN V0.2S, V0.2D + 0x0E616800u, // FCVTN V0.2S, V0.2D }; } @@ -938,7 +985,7 @@ namespace Ryujinx.Tests.Cpu 0x6E30C800u, // FMAXNMV S0, V0.4S 0x6E30F800u, // FMAXV S0, V0.4S 0x6EB0C800u, // FMINNMV S0, V0.4S - 0x6EB0F800u // FMINV S0, V0.4S + 0x6EB0F800u, // FMINV S0, V0.4S }; } @@ -946,7 +993,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E260000u // FMOV W0, S0 + 0x1E260000u, // FMOV W0, S0 }; } @@ -954,7 +1001,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x9E660000u // FMOV X0, D0 + 0x9E660000u, // FMOV X0, D0 }; } @@ -962,7 +1009,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x9EAE0000u // FMOV X0, V0.D[1] + 0x9EAE0000u, // FMOV X0, V0.D[1] }; } @@ -970,7 +1017,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E270000u // FMOV S0, W0 + 0x1E270000u, // FMOV S0, W0 }; } @@ -978,7 +1025,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x9E670000u // FMOV D0, X0 + 0x9E670000u, // FMOV D0, X0 }; } @@ -986,7 +1033,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x9EAF0000u // FMOV V0.D[1], X0 + 0x9EAF0000u, // FMOV V0.D[1], X0 }; } @@ -994,7 +1041,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E204020u // FMOV S0, S1 + 0x1E204020u, // FMOV S0, S1 }; } @@ -1002,7 +1049,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E604020u // FMOV D0, D1 + 0x1E604020u, // FMOV D0, D1 }; } @@ -1011,7 +1058,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5EA1D820u, // FRECPE S0, S1 - 0x7EA1D820u // FRSQRTE S0, S1 + 0x7EA1D820u, // FRSQRTE S0, S1 }; } @@ -1020,7 +1067,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5EE1D820u, // FRECPE D0, D1 - 0x7EE1D820u // FRSQRTE D0, D1 + 0x7EE1D820u, // FRSQRTE D0, D1 }; } @@ -1029,7 +1076,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0EA1D800u, // FRECPE V0.2S, V0.2S - 0x2EA1D800u // FRSQRTE V0.2S, V0.2S + 0x2EA1D800u, // FRSQRTE V0.2S, V0.2S }; } @@ -1038,7 +1085,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4EE1D800u, // FRECPE V0.2D, V0.2D - 0x6EE1D800u // FRSQRTE V0.2D, V0.2D + 0x6EE1D800u, // FRSQRTE V0.2D, V0.2D }; } @@ -1050,7 +1097,7 @@ namespace Ryujinx.Tests.Cpu 0x1E254020u, // FRINTM S0, S1 0x1E244020u, // FRINTN S0, S1 0x1E24C020u, // FRINTP S0, S1 - 0x1E25C020u // FRINTZ S0, S1 + 0x1E25C020u, // FRINTZ S0, S1 }; } @@ -1062,7 +1109,7 @@ namespace Ryujinx.Tests.Cpu 0x1E654020u, // FRINTM D0, D1 0x1E644020u, // FRINTN D0, D1 0x1E64C020u, // FRINTP D0, D1 - 0x1E65C020u // FRINTZ D0, D1 + 0x1E65C020u, // FRINTZ D0, D1 }; } @@ -1074,7 +1121,7 @@ namespace Ryujinx.Tests.Cpu 0x0E219800u, // FRINTM V0.2S, V0.2S 0x0E218800u, // FRINTN V0.2S, V0.2S 0x0EA18800u, // FRINTP V0.2S, V0.2S - 0x0EA19800u // FRINTZ V0.2S, V0.2S + 0x0EA19800u, // FRINTZ V0.2S, V0.2S }; } @@ -1086,7 +1133,7 @@ namespace Ryujinx.Tests.Cpu 0x4E619800u, // FRINTM V0.2D, V0.2D 0x4E618800u, // FRINTN V0.2D, V0.2D 0x4EE18800u, // FRINTP V0.2D, V0.2D - 0x4EE19800u // FRINTZ V0.2D, V0.2D + 0x4EE19800u, // FRINTZ V0.2D, V0.2D }; } @@ -1095,7 +1142,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E27C020u, // FRINTI S0, S1 - 0x1E274020u // FRINTX S0, S1 + 0x1E274020u, // FRINTX S0, S1 }; } @@ -1104,7 +1151,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E67C020u, // FRINTI D0, D1 - 0x1E674020u // FRINTX D0, D1 + 0x1E674020u, // FRINTX D0, D1 }; } @@ -1113,7 +1160,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x2EA19800u, // FRINTI V0.2S, V0.2S - 0x2E219800u // FRINTX V0.2S, V0.2S + 0x2E219800u, // FRINTX V0.2S, V0.2S }; } @@ -1122,7 +1169,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x6EE19800u, // FRINTI V0.2D, V0.2D - 0x6E619800u // FRINTX V0.2D, V0.2D + 0x6E619800u, // FRINTX V0.2D, V0.2D }; } @@ -1131,7 +1178,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0E303800u, // SADDLV H0, V0.8B - 0x2E303800u // UADDLV H0, V0.8B + 0x2E303800u, // UADDLV H0, V0.8B }; } @@ -1140,7 +1187,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4E303800u, // SADDLV H0, V0.16B - 0x6E303800u // UADDLV H0, V0.16B + 0x6E303800u, // UADDLV H0, V0.16B }; } @@ -1149,7 +1196,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5E21D820u, // SCVTF S0, S1 - 0x7E21D820u // UCVTF S0, S1 + 0x7E21D820u, // UCVTF S0, S1 }; } @@ -1158,7 +1205,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5E61D820u, // SCVTF D0, D1 - 0x7E61D820u // UCVTF D0, D1 + 0x7E61D820u, // UCVTF D0, D1 }; } @@ -1167,7 +1214,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0E21D800u, // SCVTF V0.2S, V0.2S - 0x2E21D800u // UCVTF V0.2S, V0.2S + 0x2E21D800u, // UCVTF V0.2S, V0.2S }; } @@ -1176,7 +1223,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4E61D800u, // SCVTF V0.2D, V0.2D - 0x6E61D800u // UCVTF V0.2D, V0.2D + 0x6E61D800u, // UCVTF V0.2D, V0.2D }; } @@ -1185,7 +1232,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5E280800u, // SHA1H S0, S0 - 0x5E281800u // SHA1SU1 V0.4S, V0.4S + 0x5E281800u, // SHA1SU1 V0.4S, V0.4S }; } @@ -1193,19 +1240,19 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x5E282800u // SHA256SU0 V0.4S, V0.4S + 0x5E282800u, // SHA256SU0 V0.4S, V0.4S }; } -#endregion + #endregion private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; [Test, Pairwise, Description("ABS <V><d>, <V><n>")] - public void Abs_S_D([Values(0u)] uint rd, + public void Abs_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -1222,7 +1269,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")] - public void Abs_V_8B_4H_2S([Values(0u)] uint rd, + public void Abs_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -1241,7 +1288,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")] - public void Abs_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Abs_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -1260,7 +1307,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDP <V><d>, <Vn>.<T>")] - public void Addp_S_2DD([Values(0u)] uint rd, + public void Addp_S_2DD([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -1278,7 +1325,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Add_Max_Min_V_V_8BB_4HH([ValueSource(nameof(_SU_Add_Max_Min_V_V_8BB_4HH_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H_))] ulong z, [ValueSource(nameof(_8B4H_))] ulong a, @@ -1297,7 +1344,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Add_Max_Min_V_V_16BB_8HH_4SS([ValueSource(nameof(_SU_Add_Max_Min_V_V_16BB_8HH_4SS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -1315,9 +1362,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")] - public void Cls_V_8B_16B([Values(0u)] uint rd, + public void Cls_V_8B_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_8B_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenLeadingSigns8B_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { @@ -1334,9 +1381,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")] - public void Cls_V_4H_8H([Values(0u)] uint rd, + public void Cls_V_4H_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_4H_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_4H_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenLeadingSigns4H_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { @@ -1353,9 +1400,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")] - public void Cls_V_2S_4S([Values(0u)] uint rd, + public void Cls_V_2S_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_2S_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_2S_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenLeadingSigns2S_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -1372,9 +1419,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")] - public void Clz_V_8B_16B([Values(0u)] uint rd, + public void Clz_V_8B_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_8B_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenLeadingZeros8B_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { @@ -1391,9 +1438,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")] - public void Clz_V_4H_8H([Values(0u)] uint rd, + public void Clz_V_4H_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_4H_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_4H_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenLeadingZeros4H_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { @@ -1410,9 +1457,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")] - public void Clz_V_2S_4S([Values(0u)] uint rd, + public void Clz_V_2S_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_2S_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_2S_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenLeadingZeros2S_))] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { @@ -1429,7 +1476,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <V><d>, <V><n>, #0")] - public void Cmeq_S_D([Values(0u)] uint rd, + public void Cmeq_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -1446,7 +1493,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -1465,7 +1512,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -1484,7 +1531,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <V><d>, <V><n>, #0")] - public void Cmge_S_D([Values(0u)] uint rd, + public void Cmge_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -1501,7 +1548,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -1520,7 +1567,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -1539,7 +1586,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <V><d>, <V><n>, #0")] - public void Cmgt_S_D([Values(0u)] uint rd, + public void Cmgt_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -1556,7 +1603,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -1575,7 +1622,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -1594,7 +1641,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMLE <V><d>, <V><n>, #0")] - public void Cmle_S_D([Values(0u)] uint rd, + public void Cmle_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -1611,7 +1658,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMLE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmle_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmle_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -1630,7 +1677,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMLE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -1649,7 +1696,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMLT <V><d>, <V><n>, #0")] - public void Cmlt_S_D([Values(0u)] uint rd, + public void Cmlt_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -1666,7 +1713,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMLT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmlt_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmlt_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -1685,7 +1732,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMLT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -1704,9 +1751,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CNT <Vd>.<T>, <Vn>.<T>")] - public void Cnt_V_8B([Values(0u)] uint rd, + public void Cnt_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_8B_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenPopCnt8B_))] ulong a) { uint opcode = 0x0E205800; // CNT V0.8B, V0.8B @@ -1721,9 +1768,9 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CNT <Vd>.<T>, <Vn>.<T>")] - public void Cnt_V_16B([Values(0u)] uint rd, + public void Cnt_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource(nameof(_8B_))] [Random(RndCnt)] ulong z, + [ValueSource(nameof(_8B_))][Random(RndCnt)] ulong z, [ValueSource(nameof(_GenPopCnt8B_))] ulong a) { uint opcode = 0x4E205800; // CNT V0.16B, V0.16B @@ -1737,7 +1784,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abs_Neg_Recpx_Sqrt_S_S([ValueSource(nameof(_F_Abs_Neg_Recpx_Sqrt_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a) { @@ -1755,7 +1803,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abs_Neg_Recpx_Sqrt_S_D([ValueSource(nameof(_F_Abs_Neg_Recpx_Sqrt_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a) { @@ -1773,9 +1822,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abs_Neg_Sqrt_V_2S_4S([ValueSource(nameof(_F_Abs_Neg_Sqrt_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a, @@ -1797,9 +1847,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abs_Neg_Sqrt_V_2D([ValueSource(nameof(_F_Abs_Neg_Sqrt_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong z, [ValueSource(nameof(_1D_F_))] ulong a) @@ -1819,7 +1870,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Add_Max_Min_Nm_P_S_2SS([ValueSource(nameof(_F_Add_Max_Min_Nm_P_S_2SS_))] uint opcodes, [ValueSource(nameof(_2S_F_))] ulong a) { @@ -1838,7 +1890,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Add_Max_Min_Nm_P_S_2DD([ValueSource(nameof(_F_Add_Max_Min_Nm_P_S_2DD_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a0, [ValueSource(nameof(_1D_F_))] ulong a1) @@ -1858,7 +1911,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cm_EqGeGtLeLt_S_S([ValueSource(nameof(_F_Cm_EqGeGtLeLt_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a) { @@ -1875,7 +1929,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cm_EqGeGtLeLt_S_D([ValueSource(nameof(_F_Cm_EqGeGtLeLt_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a) { @@ -1892,9 +1947,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cm_EqGeGtLeLt_V_2S_4S([ValueSource(nameof(_F_Cm_EqGeGtLeLt_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a, @@ -1915,9 +1971,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cm_EqGeGtLeLt_V_2D([ValueSource(nameof(_F_Cm_EqGeGtLeLt_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong z, [ValueSource(nameof(_1D_F_))] ulong a) @@ -1936,7 +1993,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cmp_Cmpe_S_S([ValueSource(nameof(_F_Cmp_Cmpe_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a) { @@ -1952,7 +2010,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cmp_Cmpe_S_D([ValueSource(nameof(_F_Cmp_Cmpe_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a) { @@ -1968,7 +2027,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_S_SD([ValueSource(nameof(_F_Cvt_S_SD_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a) { @@ -1981,7 +2041,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_S_DS([ValueSource(nameof(_F_Cvt_S_DS_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a) { @@ -1994,7 +2055,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] // Unicorn seems to default all rounding modes to RMode.Rn. + // Unicorn seems to default all rounding modes to RMode.Rn. + [Test, Pairwise] + [Explicit] public void F_Cvt_S_SH([ValueSource(nameof(_F_Cvt_S_SH_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) @@ -2010,7 +2073,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_S_DH([ValueSource(nameof(_F_Cvt_S_DH_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) @@ -2026,7 +2090,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_S_HS([ValueSource(nameof(_F_Cvt_S_HS_))] uint opcodes, [ValueSource(nameof(_1H_F_))] ulong a) { @@ -2039,7 +2104,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_S_HD([ValueSource(nameof(_F_Cvt_S_HD_))] uint opcodes, [ValueSource(nameof(_1H_F_))] ulong a) { @@ -2052,7 +2118,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_ANZ_SU_S_S([ValueSource(nameof(_F_Cvt_ANZ_SU_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_W_))] ulong a) { @@ -2065,7 +2132,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_ANZ_SU_S_D([ValueSource(nameof(_F_Cvt_ANZ_SU_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_X_))] ulong a) { @@ -2078,9 +2146,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_ANZ_SU_V_2S_4S([ValueSource(nameof(_F_Cvt_ANZ_SU_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_W_))] ulong z, [ValueSource(nameof(_2S_F_W_))] ulong a, @@ -2097,9 +2166,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_ANZ_SU_V_2D([ValueSource(nameof(_F_Cvt_ANZ_SU_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_X_))] ulong z, [ValueSource(nameof(_1D_F_X_))] ulong a) @@ -2114,9 +2184,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvtl_V_4H4S_8H4S([ValueSource(nameof(_F_Cvtl_V_4H4S_8H4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_F_))] ulong z, [ValueSource(nameof(_4H_F_))] ulong a, @@ -2141,9 +2212,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvtl_V_2S2D_4S2D([ValueSource(nameof(_F_Cvtl_V_2S2D_4S2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a, @@ -2160,9 +2232,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] // Unicorn seems to default all rounding modes to RMode.Rn. + // Unicorn seems to default all rounding modes to RMode.Rn. + [Test, Pairwise] + [Explicit] public void F_Cvtn_V_4S4H_4S8H([ValueSource(nameof(_F_Cvtn_V_4S4H_4S8H_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a, @@ -2187,9 +2261,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvtn_V_2D2S_2D4S([ValueSource(nameof(_F_Cvtn_V_2D2S_2D4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong z, [ValueSource(nameof(_1D_F_))] ulong a, @@ -2206,9 +2281,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Max_Min_Nm_V_V_4SS([ValueSource(nameof(_F_Max_Min_Nm_V_V_4SS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a) @@ -2228,10 +2304,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Ftoi_SW([ValueSource(nameof(_F_Mov_Ftoi_SW_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1S_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2245,10 +2322,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Ftoi_DX([ValueSource(nameof(_F_Mov_Ftoi_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2261,10 +2339,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Ftoi1_DX([ValueSource(nameof(_F_Mov_Ftoi1_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2277,16 +2356,17 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Itof_WS([ValueSource(nameof(_F_Mov_Itof_WS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_W_))] uint wn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); @@ -2294,16 +2374,17 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Itof_XD([ValueSource(nameof(_F_Mov_Itof_XD_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); @@ -2311,16 +2392,17 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Itof1_XD([ValueSource(nameof(_F_Mov_Itof1_XD_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0(z); SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); @@ -2328,7 +2410,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_S_S([ValueSource(nameof(_F_Mov_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a) { @@ -2341,7 +2424,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_S_D([ValueSource(nameof(_F_Mov_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a) { @@ -2354,7 +2438,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Recpe_Rsqrte_S_S([ValueSource(nameof(_F_Recpe_Rsqrte_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) @@ -2374,7 +2459,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Recpe_Rsqrte_S_D([ValueSource(nameof(_F_Recpe_Rsqrte_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [Values(RMode.Rn)] RMode rMode) @@ -2394,9 +2480,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Recpe_Rsqrte_V_2S_4S([ValueSource(nameof(_F_Recpe_Rsqrte_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a, @@ -2420,9 +2507,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Recpe_Rsqrte_V_2D([ValueSource(nameof(_F_Recpe_Rsqrte_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong z, [ValueSource(nameof(_1D_F_))] ulong a, @@ -2444,7 +2532,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_AMNPZ_S_S([ValueSource(nameof(_F_Rint_AMNPZ_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a) { @@ -2457,7 +2546,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_AMNPZ_S_D([ValueSource(nameof(_F_Rint_AMNPZ_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a) { @@ -2470,9 +2560,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_AMNPZ_V_2S_4S([ValueSource(nameof(_F_Rint_AMNPZ_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a, @@ -2489,9 +2580,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_AMNPZ_V_2D([ValueSource(nameof(_F_Rint_AMNPZ_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong z, [ValueSource(nameof(_1D_F_))] ulong a) @@ -2506,7 +2598,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_IX_S_S([ValueSource(nameof(_F_Rint_IX_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [Values] RMode rMode) @@ -2522,7 +2615,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_IX_S_D([ValueSource(nameof(_F_Rint_IX_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [Values] RMode rMode) @@ -2538,9 +2632,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_IX_V_2S_4S([ValueSource(nameof(_F_Rint_IX_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_))] ulong z, [ValueSource(nameof(_2S_F_))] ulong a, @@ -2560,9 +2655,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Rint_IX_V_2D([ValueSource(nameof(_F_Rint_IX_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_))] ulong z, [ValueSource(nameof(_1D_F_))] ulong a, @@ -2581,7 +2677,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("NEG <V><d>, <V><n>")] - public void Neg_S_D([Values(0u)] uint rd, + public void Neg_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -2598,7 +2694,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")] - public void Neg_V_8B_4H_2S([Values(0u)] uint rd, + public void Neg_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2617,7 +2713,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")] - public void Neg_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Neg_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -2636,7 +2732,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("NOT <Vd>.<T>, <Vn>.<T>")] - public void Not_V_8B([Values(0u)] uint rd, + public void Not_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a) @@ -2653,7 +2749,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("NOT <Vd>.<T>, <Vn>.<T>")] - public void Not_V_16B([Values(0u)] uint rd, + public void Not_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a) @@ -2670,7 +2766,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RBIT <Vd>.<T>, <Vn>.<T>")] - public void Rbit_V_8B([Values(0u)] uint rd, + public void Rbit_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a) @@ -2687,7 +2783,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RBIT <Vd>.<T>, <Vn>.<T>")] - public void Rbit_V_16B([Values(0u)] uint rd, + public void Rbit_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a) @@ -2704,7 +2800,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("REV16 <Vd>.<T>, <Vn>.<T>")] - public void Rev16_V_8B([Values(0u)] uint rd, + public void Rev16_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a) @@ -2721,7 +2817,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("REV16 <Vd>.<T>, <Vn>.<T>")] - public void Rev16_V_16B([Values(0u)] uint rd, + public void Rev16_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a) @@ -2738,7 +2834,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("REV32 <Vd>.<T>, <Vn>.<T>")] - public void Rev32_V_8B_4H([Values(0u)] uint rd, + public void Rev32_V_8B_4H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H_))] ulong z, [ValueSource(nameof(_8B4H_))] ulong a, @@ -2757,7 +2853,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("REV32 <Vd>.<T>, <Vn>.<T>")] - public void Rev32_V_16B_8H([Values(0u)] uint rd, + public void Rev32_V_16B_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H_))] ulong z, [ValueSource(nameof(_8B4H_))] ulong a, @@ -2776,7 +2872,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("REV64 <Vd>.<T>, <Vn>.<T>")] - public void Rev64_V_8B_4H_2S([Values(0u)] uint rd, + public void Rev64_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2795,7 +2891,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("REV64 <Vd>.<T>, <Vn>.<T>")] - public void Rev64_V_16B_8H_4S([Values(0u)] uint rd, + public void Rev64_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2814,7 +2910,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADALP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Sadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + public void Sadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2833,7 +2929,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADALP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Sadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Sadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2852,7 +2948,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDLP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Saddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + public void Saddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2871,7 +2967,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDLP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Saddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Saddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2891,7 +2987,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Addl_V_V_8BH_4HS([ValueSource(nameof(_SU_Addl_V_V_8BH_4HS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H_))] ulong z, [ValueSource(nameof(_8B4H_))] ulong a, @@ -2910,7 +3006,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Addl_V_V_16BH_8HS_4SD([ValueSource(nameof(_SU_Addl_V_V_16BH_8HS_4SD_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -2927,7 +3023,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_S_S([ValueSource(nameof(_SU_Cvt_F_S_S_))] uint opcodes, [ValueSource(nameof(_1S_))] ulong a) { @@ -2940,7 +3037,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_S_D([ValueSource(nameof(_SU_Cvt_F_S_D_))] uint opcodes, [ValueSource(nameof(_1D_))] ulong a) { @@ -2953,9 +3051,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_V_2S_4S([ValueSource(nameof(_SU_Cvt_F_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -2972,9 +3071,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_V_2D([ValueSource(nameof(_SU_Cvt_F_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a) @@ -2991,7 +3091,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Sha1h_Sha1su1_V([ValueSource(nameof(_Sha1h_Sha1su1_V_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, [Random(RndCnt / 2)] ulong a0, [Random(RndCnt / 2)] ulong a1) @@ -3008,7 +3108,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Sha256su0_V([ValueSource(nameof(_Sha256su0_V_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, [Random(RndCnt / 2)] ulong a0, [Random(RndCnt / 2)] ulong a1) @@ -3024,7 +3124,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SHLL{2} <Vd>.<Ta>, <Vn>.<Tb>, #<shift>")] - public void Shll_V([Values(0u)] uint rd, + public void Shll_V([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3045,7 +3145,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQABS <V><d>, <V><n>")] - public void Sqabs_S_B_H_S_D([Values(0u)] uint rd, + public void Sqabs_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1B1H1S1D_))] ulong z, [ValueSource(nameof(_1B1H1S1D_))] ulong a, @@ -3064,7 +3164,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQABS <Vd>.<T>, <Vn>.<T>")] - public void Sqabs_V_8B_4H_2S([Values(0u)] uint rd, + public void Sqabs_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3083,7 +3183,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQABS <Vd>.<T>, <Vn>.<T>")] - public void Sqabs_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Sqabs_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -3102,7 +3202,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQNEG <V><d>, <V><n>")] - public void Sqneg_S_B_H_S_D([Values(0u)] uint rd, + public void Sqneg_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1B1H1S1D_))] ulong z, [ValueSource(nameof(_1B1H1S1D_))] ulong a, @@ -3121,7 +3221,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQNEG <Vd>.<T>, <Vn>.<T>")] - public void Sqneg_V_8B_4H_2S([Values(0u)] uint rd, + public void Sqneg_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3140,7 +3240,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQNEG <Vd>.<T>, <Vn>.<T>")] - public void Sqneg_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Sqneg_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -3159,7 +3259,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQXTN <Vb><d>, <Va><n>")] - public void Sqxtn_S_HB_SH_DS([Values(0u)] uint rd, + public void Sqxtn_S_HB_SH_DS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1H1S1D_))] ulong z, [ValueSource(nameof(_1H1S1D_))] ulong a, @@ -3178,7 +3278,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3197,7 +3297,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3216,7 +3316,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQXTUN <Vb><d>, <Va><n>")] - public void Sqxtun_S_HB_SH_DS([Values(0u)] uint rd, + public void Sqxtun_S_HB_SH_DS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1H1S1D_))] ulong z, [ValueSource(nameof(_1H1S1D_))] ulong a, @@ -3235,7 +3335,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQXTUN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3254,7 +3354,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQXTUN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3273,7 +3373,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUQADD <V><d>, <V><n>")] - public void Suqadd_S_B_H_S_D([Values(0u)] uint rd, + public void Suqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1B1H1S1D_))] ulong z, [ValueSource(nameof(_1B1H1S1D_))] ulong a, @@ -3292,7 +3392,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUQADD <Vd>.<T>, <Vn>.<T>")] - public void Suqadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Suqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3311,7 +3411,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUQADD <Vd>.<T>, <Vn>.<T>")] - public void Suqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Suqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -3330,7 +3430,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADALP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Uadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + public void Uadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3349,7 +3449,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADALP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Uadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Uadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3368,7 +3468,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDLP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Uaddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + public void Uaddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3387,7 +3487,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDLP <Vd>.<Ta>, <Vn>.<Tb>")] - public void Uaddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Uaddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3406,7 +3506,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQXTN <Vb><d>, <Va><n>")] - public void Uqxtn_S_HB_SH_DS([Values(0u)] uint rd, + public void Uqxtn_S_HB_SH_DS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1H1S1D_))] ulong z, [ValueSource(nameof(_1H1S1D_))] ulong a, @@ -3425,7 +3525,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3444,7 +3544,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3463,7 +3563,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USQADD <V><d>, <V><n>")] - public void Usqadd_S_B_H_S_D([Values(0u)] uint rd, + public void Usqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1B1H1S1D_))] ulong z, [ValueSource(nameof(_1B1H1S1D_))] ulong a, @@ -3482,7 +3582,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USQADD <Vd>.<T>, <Vn>.<T>")] - public void Usqadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Usqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S_))] ulong z, [ValueSource(nameof(_8B4H2S_))] ulong a, @@ -3501,7 +3601,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USQADD <Vd>.<T>, <Vn>.<T>")] - public void Usqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Usqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B4H2S1D_))] ulong z, [ValueSource(nameof(_8B4H2S1D_))] ulong a, @@ -3520,7 +3620,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("XTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3539,7 +3639,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("XTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H2S1D_))] ulong z, [ValueSource(nameof(_4H2S1D_))] ulong a, @@ -3558,4 +3658,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs index 42bc2ac55..6087a6834 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs @@ -11,14 +11,14 @@ namespace Ryujinx.Tests.Cpu { #if Simd32 -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Vabs_Vneg_Vpaddl_I_() { return new[] { 0xf3b10300u, // VABS.S8 D0, D0 0xf3b10380u, // VNEG.S8 D0, D0 - 0xf3b00200u // VPADDL.S8 D0, D0 + 0xf3b00200u, // VPADDL.S8 D0, D0 }; } @@ -27,18 +27,20 @@ namespace Ryujinx.Tests.Cpu return new[] { 0xf3b90700u, // VABS.F32 D0, D0 - 0xf3b90780u // VNEG.F32 D0, D0 + 0xf3b90780u, // VNEG.F32 D0, D0 }; } -#endregion + #endregion -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _8B4H2S_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<ulong> _1S_F_() @@ -52,19 +54,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -94,19 +96,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -135,19 +137,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -173,13 +175,13 @@ namespace Ryujinx.Tests.Cpu (cnt << 24) | (cnt << 16) | (cnt << 08) | cnt; } } -#endregion + #endregion private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; [Test, Pairwise, Description("SHA256SU0.32 <Qd>, <Qm>")] public void Sha256su0_V([Values(0xF3BA03C0u)] uint opcode, @@ -193,7 +195,7 @@ namespace Ryujinx.Tests.Cpu [Values(0x74CED221E2793F07ul)] ulong resultH) { opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(z0, z1); V128 v1 = MakeVectorE0E1(a0, a1); @@ -223,12 +225,14 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rm >>= 1; + rm <<= 1; } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= (size & 0x3) << 18; @@ -252,12 +256,14 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rm >>= 1; + rm <<= 1; } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(z, ~z); V128 v1 = MakeVectorE0E1(b, ~b); @@ -286,7 +292,7 @@ namespace Ryujinx.Tests.Cpu } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(d0, d1); @@ -303,12 +309,13 @@ namespace Ryujinx.Tests.Cpu [Values(0u, 1u, 2u, 3u)] uint op, [Values(0u, 1u, 2u)] uint size) // <S8, S16, S32> { - rm >>= 1; rm <<= 1; + rm >>= 1; + rm <<= 1; uint opcode = 0xf3b20200u; // VMOVN.S16 D0, Q0 opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= (op & 0x3) << 6; opcode |= (size & 0x3) << 18; @@ -322,4 +329,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs index fd8ec9c57..80612f1c7 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs @@ -1,7 +1,6 @@ // https://www.intel.com/content/dam/doc/white-paper/advanced-encryption-standard-new-instructions-set-paper.pdf using ARMeilleure.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu @@ -13,8 +12,8 @@ namespace Ryujinx.Tests.Cpu [Values(1u)] uint rn, [Values(0x7B5B546573745665ul)] ulong valueH, [Values(0x63746F725D53475Dul)] ulong valueL, - [Random(2)] ulong roundKeyH, - [Random(2)] ulong roundKeyL, + [Random(2)] ulong roundKeyH, + [Random(2)] ulong roundKeyL, [Values(0x8DCAB9BC035006BCul)] ulong resultH, [Values(0x8F57161E00CAFD8Dul)] ulong resultL) { @@ -22,7 +21,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); V128 v0 = MakeVectorE0E1(roundKeyL ^ valueL, roundKeyH ^ valueH); - V128 v1 = MakeVectorE0E1(roundKeyL, roundKeyH); + V128 v1 = MakeVectorE0E1(roundKeyL, roundKeyH); ExecutionContext context = SingleOpcode(opcode, v0: v0, v1: v1); @@ -45,8 +44,8 @@ namespace Ryujinx.Tests.Cpu [Values(1u)] uint rn, [Values(0x7B5B546573745665ul)] ulong valueH, [Values(0x63746F725D53475Dul)] ulong valueL, - [Random(2)] ulong roundKeyH, - [Random(2)] ulong roundKeyL, + [Random(2)] ulong roundKeyH, + [Random(2)] ulong roundKeyL, [Values(0x8F92A04DFBED204Dul)] ulong resultH, [Values(0x4C39B1402192A84Cul)] ulong resultL) { @@ -54,7 +53,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); V128 v0 = MakeVectorE0E1(roundKeyL ^ valueL, roundKeyH ^ valueH); - V128 v1 = MakeVectorE0E1(roundKeyL, roundKeyH); + V128 v1 = MakeVectorE0E1(roundKeyL, roundKeyH); ExecutionContext context = SingleOpcode(opcode, v0: v0, v1: v1); @@ -73,7 +72,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("AESIMC <Vd>.16B, <Vn>.16B")] - public void Aesimc_V([Values(0u)] uint rd, + public void Aesimc_V([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(0x8DCAB9DC035006BCul)] ulong valueH, [Values(0x8F57161E00CAFD8Dul)] ulong valueL, @@ -87,8 +86,8 @@ namespace Ryujinx.Tests.Cpu ExecutionContext context = SingleOpcode( opcode, - v0: rn == 0u ? v : default(V128), - v1: rn == 1u ? v : default(V128)); + v0: rn == 0u ? v : default, + v1: rn == 1u ? v : default); Assert.Multiple(() => { @@ -108,7 +107,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("AESMC <Vd>.16B, <Vn>.16B")] - public void Aesmc_V([Values(0u)] uint rd, + public void Aesmc_V([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(0x627A6F6644B109C8ul)] ulong valueH, [Values(0x2B18330A81C3B3E5ul)] ulong valueL, @@ -122,8 +121,8 @@ namespace Ryujinx.Tests.Cpu ExecutionContext context = SingleOpcode( opcode, - v0: rn == 0u ? v : default(V128), - v1: rn == 1u ? v : default(V128)); + v0: rn == 0u ? v : default, + v1: rn == 1u ? v : default); Assert.Multiple(() => { diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs index 0bcb7a1f1..c82b17978 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdCrypto32.cs @@ -1,7 +1,6 @@ // https://www.intel.com/content/dam/doc/white-paper/advanced-encryption-standard-new-instructions-set-paper.pdf using ARMeilleure.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu @@ -13,8 +12,8 @@ namespace Ryujinx.Tests.Cpu [Values(2u)] uint rm, [Values(0x7B5B546573745665ul)] ulong valueH, [Values(0x63746F725D53475Dul)] ulong valueL, - [Random(2)] ulong roundKeyH, - [Random(2)] ulong roundKeyL, + [Random(2)] ulong roundKeyH, + [Random(2)] ulong roundKeyL, [Values(0x8DCAB9BC035006BCul)] ulong resultH, [Values(0x8F57161E00CAFD8Dul)] ulong resultL) { @@ -47,8 +46,8 @@ namespace Ryujinx.Tests.Cpu [Values(2u)] uint rm, [Values(0x7B5B546573745665ul)] ulong valueH, [Values(0x63746F725D53475Dul)] ulong valueL, - [Random(2)] ulong roundKeyH, - [Random(2)] ulong roundKeyL, + [Random(2)] ulong roundKeyH, + [Random(2)] ulong roundKeyL, [Values(0x8F92A04DFBED204Dul)] ulong resultH, [Values(0x4C39B1402192A84Cul)] ulong resultL) { @@ -77,7 +76,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("AESIMC.8 <Qd>, <Qm>")] - public void Aesimc_V([Values(0u)] uint rd, + public void Aesimc_V([Values(0u)] uint rd, [Values(2u, 0u)] uint rm, [Values(0x8DCAB9DC035006BCul)] ulong valueH, [Values(0x8F57161E00CAFD8Dul)] ulong valueL, @@ -92,8 +91,8 @@ namespace Ryujinx.Tests.Cpu ExecutionContext context = SingleOpcode( opcode, - v0: rm == 0u ? v : default(V128), - v1: rm == 2u ? v : default(V128), + v0: rm == 0u ? v : default, + v1: rm == 2u ? v : default, runUnicorn: false); Assert.Multiple(() => @@ -115,7 +114,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("AESMC.8 <Qd>, <Qm>")] - public void Aesmc_V([Values(0u)] uint rd, + public void Aesmc_V([Values(0u)] uint rd, [Values(2u, 0u)] uint rm, [Values(0x627A6F6644B109C8ul)] ulong valueH, [Values(0x2B18330A81C3B3E5ul)] ulong valueL, @@ -130,8 +129,8 @@ namespace Ryujinx.Tests.Cpu ExecutionContext context = SingleOpcode( opcode, - v0: rm == 0u ? v : default(V128), - v1: rm == 2u ? v : default(V128), + v0: rm == 0u ? v : default, + v1: rm == 2u ? v : default, runUnicorn: false); Assert.Multiple(() => diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs index dbb9410cd..007c0f8cb 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs @@ -12,17 +12,21 @@ namespace Ryujinx.Tests.Cpu { #if SimdCvt -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static uint[] _W_() { - return new[] { 0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { + 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu, + }; } private static ulong[] _X_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<ulong> _1S_F_WX_() @@ -62,19 +66,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -145,19 +149,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -188,9 +192,9 @@ namespace Ryujinx.Tests.Cpu yield return rnd6; } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _F_Cvt_AMPZ_SU_Gp_SW_() { return new[] @@ -203,7 +207,7 @@ namespace Ryujinx.Tests.Cpu 0x1E280000u, // FCVTPS W0, S0 0x1E290000u, // FCVTPU W0, S0 0x1E380000u, // FCVTZS W0, S0 - 0x1E390000u // FCVTZU W0, S0 + 0x1E390000u, // FCVTZU W0, S0 }; } @@ -219,7 +223,7 @@ namespace Ryujinx.Tests.Cpu 0x9E280000u, // FCVTPS X0, S0 0x9E290000u, // FCVTPU X0, S0 0x9E380000u, // FCVTZS X0, S0 - 0x9E390000u // FCVTZU X0, S0 + 0x9E390000u, // FCVTZU X0, S0 }; } @@ -235,7 +239,7 @@ namespace Ryujinx.Tests.Cpu 0x1E680000u, // FCVTPS W0, D0 0x1E690000u, // FCVTPU W0, D0 0x1E780000u, // FCVTZS W0, D0 - 0x1E790000u // FCVTZU W0, D0 + 0x1E790000u, // FCVTZU W0, D0 }; } @@ -251,7 +255,7 @@ namespace Ryujinx.Tests.Cpu 0x9E680000u, // FCVTPS X0, D0 0x9E690000u, // FCVTPU X0, D0 0x9E780000u, // FCVTZS X0, D0 - 0x9E790000u // FCVTZU X0, D0 + 0x9E790000u, // FCVTZU X0, D0 }; } @@ -260,7 +264,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E188000u, // FCVTZS W0, S0, #32 - 0x1E198000u // FCVTZU W0, S0, #32 + 0x1E198000u, // FCVTZU W0, S0, #32 }; } @@ -269,7 +273,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x9E180000u, // FCVTZS X0, S0, #64 - 0x9E190000u // FCVTZU X0, S0, #64 + 0x9E190000u, // FCVTZU X0, S0, #64 }; } @@ -278,7 +282,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E588000u, // FCVTZS W0, D0, #32 - 0x1E598000u // FCVTZU W0, D0, #32 + 0x1E598000u, // FCVTZU W0, D0, #32 }; } @@ -287,7 +291,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x9E580000u, // FCVTZS X0, D0, #64 - 0x9E590000u // FCVTZU X0, D0, #64 + 0x9E590000u, // FCVTZU X0, D0, #64 }; } @@ -296,7 +300,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E220000u, // SCVTF S0, W0 - 0x1E230000u // UCVTF S0, W0 + 0x1E230000u, // UCVTF S0, W0 }; } @@ -305,7 +309,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E620000u, // SCVTF D0, W0 - 0x1E630000u // UCVTF D0, W0 + 0x1E630000u, // UCVTF D0, W0 }; } @@ -314,7 +318,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x9E220000u, // SCVTF S0, X0 - 0x9E230000u // UCVTF S0, X0 + 0x9E230000u, // UCVTF S0, X0 }; } @@ -323,7 +327,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x9E620000u, // SCVTF D0, X0 - 0x9E630000u // UCVTF D0, X0 + 0x9E630000u, // UCVTF D0, X0 }; } @@ -332,7 +336,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E028000u, // SCVTF S0, W0, #32 - 0x1E038000u // UCVTF S0, W0, #32 + 0x1E038000u, // UCVTF S0, W0, #32 }; } @@ -341,7 +345,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E428000u, // SCVTF D0, W0, #32 - 0x1E438000u // UCVTF D0, W0, #32 + 0x1E438000u, // UCVTF D0, W0, #32 }; } @@ -350,7 +354,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x9E020000u, // SCVTF S0, X0, #64 - 0x9E030000u // UCVTF S0, X0, #64 + 0x9E030000u, // UCVTF S0, X0, #64 }; } @@ -359,21 +363,22 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x9E420000u, // SCVTF D0, X0, #64 - 0x9E430000u // UCVTF D0, X0, #64 + 0x9E430000u, // UCVTF D0, X0, #64 }; } -#endregion + #endregion - private const int RndCnt = 2; + private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_AMPZ_SU_Gp_SW([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_SW_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1S_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -387,10 +392,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_AMPZ_SU_Gp_SX([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_SX_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1S_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -403,10 +409,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_AMPZ_SU_Gp_DW([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_DW_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1D_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -420,10 +427,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_AMPZ_SU_Gp_DX([ValueSource(nameof(_F_Cvt_AMPZ_SU_Gp_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1D_F_WX_))] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -436,10 +444,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_Z_SU_Gp_Fixed_SW([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_SW_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1S_F_WX_))] ulong a, [Values(1u, 32u)] uint fBits) { @@ -457,10 +466,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_Z_SU_Gp_Fixed_SX([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_SX_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1S_F_WX_))] ulong a, [Values(1u, 64u)] uint fBits) { @@ -477,10 +487,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_Z_SU_Gp_Fixed_DW([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_DW_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1D_F_WX_))] ulong a, [Values(1u, 32u)] uint fBits) { @@ -498,10 +509,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_Z_SU_Gp_Fixed_DX([ValueSource(nameof(_F_Cvt_Z_SU_Gp_Fixed_DX_))] uint opcodes, [Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1D_F_WX_))] ulong a, [Values(1u, 64u)] uint fBits) { @@ -518,16 +530,17 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_WS([ValueSource(nameof(_SU_Cvt_F_Gp_WS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_W_))] uint wn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); @@ -535,16 +548,17 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_WD([ValueSource(nameof(_SU_Cvt_F_Gp_WD_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_W_))] uint wn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); @@ -552,16 +566,17 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_XS([ValueSource(nameof(_SU_Cvt_F_Gp_XS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); @@ -569,16 +584,17 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_XD([ValueSource(nameof(_SU_Cvt_F_Gp_XD_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_X_))] ulong xn) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); @@ -586,9 +602,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_Fixed_WS([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_WS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_W_))] uint wn, [Values(1u, 32u)] uint fBits) @@ -598,8 +615,8 @@ namespace Ryujinx.Tests.Cpu opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); opcodes |= (scale << 10); - uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); @@ -607,9 +624,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_Fixed_WD([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_WD_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_W_))] uint wn, [Values(1u, 32u)] uint fBits) @@ -619,8 +637,8 @@ namespace Ryujinx.Tests.Cpu opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); opcodes |= (scale << 10); - uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); @@ -628,9 +646,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_Fixed_XS([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_XS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_X_))] ulong xn, [Values(1u, 64u)] uint fBits) @@ -641,7 +660,7 @@ namespace Ryujinx.Tests.Cpu opcodes |= (scale << 10); ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); @@ -649,9 +668,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_Gp_Fixed_XD([ValueSource(nameof(_SU_Cvt_F_Gp_Fixed_XD_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_X_))] ulong xn, [Values(1u, 64u)] uint fBits) @@ -662,7 +682,7 @@ namespace Ryujinx.Tests.Cpu opcodes |= (scale << 10); ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE1(z); SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); @@ -671,4 +691,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs index e9a8ad590..5b24432bb 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu { #if SimdCvt32 -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Vrint_AMNP_V_F32_() { return new[] @@ -20,16 +20,18 @@ namespace Ryujinx.Tests.Cpu 0xf3ba0500u, // VRINTA.F32 Q0, Q0 0xf3ba0680u, // VRINTM.F32 Q0, Q0 0xf3ba0400u, // VRINTN.F32 Q0, Q0 - 0xf3ba0780u // VRINTP.F32 Q0, Q0 + 0xf3ba0780u, // VRINTP.F32 Q0, Q0 }; } -#endregion + #endregion -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static uint[] _1S_() { - return new[] { 0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { + 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu, + }; } private static IEnumerable<ulong> _1S_F_() @@ -43,19 +45,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -85,19 +87,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -126,19 +128,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -155,13 +157,13 @@ namespace Ryujinx.Tests.Cpu yield return rnd2; } } -#endregion + #endregion private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; [Explicit] [Test, Pairwise, Description("VCVT.<dt>.F32 <Sd>, <Sm>")] @@ -275,7 +277,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void Vrint_AMNP_V_F32([ValueSource(nameof(_Vrint_AMNP_V_F32_))] uint opcode, [Values(0u, 1u, 2u, 3u)] uint rd, [Values(0u, 1u, 2u, 3u)] uint rm, @@ -289,12 +292,14 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rm >>= 1; + rm <<= 1; } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(d0, d1); V128 v1 = MakeVectorE0E1(d2, d3); @@ -508,4 +513,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs index dae09a160..59bc4cb7e 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs @@ -10,16 +10,18 @@ namespace Ryujinx.Tests.Cpu { #if SimdExt -#region "ValueSource" + #region "ValueSource" private static ulong[] _8B_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul, + }; } -#endregion + #endregion [Test, Pairwise, Description("EXT <Vd>.8B, <Vn>.8B, <Vm>.8B, #<index>")] - public void Ext_V_8B([Values(0u)] uint rd, + public void Ext_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -43,7 +45,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("EXT <Vd>.16B, <Vn>.16B, <Vm>.16B, #<index>")] - public void Ext_V_16B([Values(0u)] uint rd, + public void Ext_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -67,4 +69,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs index bf13236e9..d6d12b278 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Cpu { #if SimdFcond -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static IEnumerable<ulong> _1S_F_() { yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) @@ -23,19 +23,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -65,19 +65,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -94,15 +94,15 @@ namespace Ryujinx.Tests.Cpu yield return rnd2; } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _F_Ccmp_Ccmpe_S_S_() { return new[] { 0x1E220420u, // FCCMP S1, S2, #0, EQ - 0x1E220430u // FCCMPE S1, S2, #0, EQ + 0x1E220430u, // FCCMPE S1, S2, #0, EQ }; } @@ -111,7 +111,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E620420u, // FCCMP D1, D2, #0, EQ - 0x1E620430u // FCCMPE D1, D2, #0, EQ + 0x1E620430u, // FCCMPE D1, D2, #0, EQ }; } @@ -119,7 +119,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E220C20u // FCSEL S0, S1, S2, EQ + 0x1E220C20u, // FCSEL S0, S1, S2, EQ }; } @@ -127,19 +127,20 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E620C20u // FCSEL D0, D1, D2, EQ + 0x1E620C20u, // FCSEL D0, D1, D2, EQ }; } -#endregion + #endregion - private const int RndCnt = 2; + private const int RndCnt = 2; private const int RndCntNzcv = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Ccmp_Ccmpe_S_S([ValueSource(nameof(_F_Ccmp_Ccmpe_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b, @@ -164,7 +165,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Ccmp_Ccmpe_S_D([ValueSource(nameof(_F_Ccmp_Ccmpe_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b, @@ -189,7 +191,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Csel_S_S([ValueSource(nameof(_F_Csel_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b, @@ -210,7 +213,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Csel_S_D([ValueSource(nameof(_F_Csel_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b, diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs index aa6e0c944..0c2582695 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdFmov.cs @@ -10,12 +10,12 @@ namespace Ryujinx.Tests.Cpu { #if SimdFmov -#region "ValueSource" + #region "ValueSource" private static uint[] _F_Mov_Si_S_() { return new[] { - 0x1E201000u // FMOV S0, #2.0 + 0x1E201000u, // FMOV S0, #2.0 }; } @@ -23,12 +23,13 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x1E601000u // FMOV D0, #2.0 + 0x1E601000u, // FMOV D0, #2.0 }; } -#endregion + #endregion - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Si_S([ValueSource(nameof(_F_Mov_Si_S_))] uint opcodes, [Range(0u, 255u, 1u)] uint imm8) { @@ -42,7 +43,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Si_D([ValueSource(nameof(_F_Mov_Si_D_))] uint opcodes, [Range(0u, 255u, 1u)] uint imm8) { diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs index d946b4330..27e3b41a0 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Cpu { #if SimdImm -#region "Helper methods" + #region "Helper methods" // abcdefgh -> aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh private static ulong ExpandImm8(byte imm8) { @@ -43,19 +43,23 @@ namespace Ryujinx.Tests.Cpu return imm8; } -#endregion + #endregion -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _2S_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<byte> _8BIT_IMM_() @@ -87,15 +91,15 @@ namespace Ryujinx.Tests.Cpu yield return ExpandImm8(imm8); } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Bic_Orr_Vi_16bit_() { return new[] { 0x2F009400u, // BIC V0.4H, #0 - 0x0F009400u // ORR V0.4H, #0 + 0x0F009400u, // ORR V0.4H, #0 }; } @@ -104,7 +108,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x2F001400u, // BIC V0.2S, #0 - 0x0F001400u // ORR V0.2S, #0 + 0x0F001400u, // ORR V0.2S, #0 }; } @@ -112,7 +116,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x0F00F400u // FMOV V0.2S, #2.0 + 0x0F00F400u, // FMOV V0.2S, #2.0 }; } @@ -120,7 +124,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x4F00F400u // FMOV V0.4S, #2.0 + 0x4F00F400u, // FMOV V0.4S, #2.0 }; } @@ -128,7 +132,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x6F00F400u // FMOV V0.2D, #2.0 + 0x6F00F400u, // FMOV V0.2D, #2.0 }; } @@ -136,7 +140,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x0F00E400u // MOVI V0.8B, #0 + 0x0F00E400u, // MOVI V0.8B, #0 }; } @@ -145,7 +149,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F008400u, // MOVI V0.4H, #0 - 0x2F008400u // MVNI V0.4H, #0 + 0x2F008400u, // MVNI V0.4H, #0 }; } @@ -154,7 +158,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F000400u, // MOVI V0.2S, #0 - 0x2F000400u // MVNI V0.2S, #0 + 0x2F000400u, // MVNI V0.2S, #0 }; } @@ -163,7 +167,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F00C400u, // MOVI V0.2S, #0, MSL #8 - 0x2F00C400u // MVNI V0.2S, #0, MSL #8 + 0x2F00C400u, // MVNI V0.2S, #0, MSL #8 }; } @@ -171,7 +175,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x2F00E400u // MOVI D0, #0 + 0x2F00E400u, // MOVI D0, #0 }; } @@ -179,12 +183,12 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0x6F00E400u // MOVI V0.2D, #0 + 0x6F00E400u, // MOVI V0.2D, #0 }; } -#endregion + #endregion - private const int RndCntImm8 = 2; + private const int RndCntImm8 = 2; private const int RndCntImm64 = 2; [Test, Pairwise] @@ -194,7 +198,7 @@ namespace Ryujinx.Tests.Cpu [Values(0b0u, 0b1u)] uint amount, // <0, 8> [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -215,7 +219,7 @@ namespace Ryujinx.Tests.Cpu [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint amount, // <0, 8, 16, 24> [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -229,11 +233,12 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Vi_2S([ValueSource(nameof(_F_Mov_Vi_2S_))] uint opcodes, [Range(0u, 255u, 1u)] uint abcdefgh) { - uint abc = (abcdefgh & 0xE0u) >> 5; + uint abc = (abcdefgh & 0xE0u) >> 5; uint defgh = (abcdefgh & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -246,11 +251,12 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Vi_4S([ValueSource(nameof(_F_Mov_Vi_4S_))] uint opcodes, [Range(0u, 255u, 1u)] uint abcdefgh) { - uint abc = (abcdefgh & 0xE0u) >> 5; + uint abc = (abcdefgh & 0xE0u) >> 5; uint defgh = (abcdefgh & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -260,11 +266,12 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mov_Vi_2D([ValueSource(nameof(_F_Mov_Vi_2D_))] uint opcodes, [Range(0u, 255u, 1u)] uint abcdefgh) { - uint abc = (abcdefgh & 0xE0u) >> 5; + uint abc = (abcdefgh & 0xE0u) >> 5; uint defgh = (abcdefgh & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -279,7 +286,7 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_8BIT_IMM_))] byte imm8, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -299,7 +306,7 @@ namespace Ryujinx.Tests.Cpu [Values(0b0u, 0b1u)] uint amount, // <0, 8> [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -320,7 +327,7 @@ namespace Ryujinx.Tests.Cpu [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint amount, // <0, 8, 16, 24> [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -341,7 +348,7 @@ namespace Ryujinx.Tests.Cpu [Values(0b0u, 0b1u)] uint amount, // <8, 16> [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -362,7 +369,7 @@ namespace Ryujinx.Tests.Cpu { byte imm8 = ShrinkImm64(imm); - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -381,7 +388,7 @@ namespace Ryujinx.Tests.Cpu { byte imm8 = ShrinkImm64(imm); - uint abc = (imm8 & 0xE0u) >> 5; + uint abc = (imm8 & 0xE0u) >> 5; uint defgh = (imm8 & 0x1Fu); opcodes |= (abc << 16) | (defgh << 5); @@ -392,4 +399,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs index d5ac3c27b..83dc07707 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs @@ -10,64 +10,80 @@ namespace Ryujinx.Tests.Cpu { #if SimdIns -#region "ValueSource" + #region "ValueSource" private static ulong[] _1D_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _2S_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H2S_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static uint[] _W_() { - return new[] { 0x00000000u, 0x0000007Fu, - 0x00000080u, 0x000000FFu, - 0x00007FFFu, 0x00008000u, - 0x0000FFFFu, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu }; + return new[] { + 0x00000000u, 0x0000007Fu, + 0x00000080u, 0x000000FFu, + 0x00007FFFu, 0x00008000u, + 0x0000FFFFu, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu, + }; } private static ulong[] _X_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } -#endregion + #endregion [Test, Pairwise, Description("DUP <Vd>.<T>, W<n>")] - public void Dup_Gp_W([Values(0u)] uint rd, + public void Dup_Gp_W([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_W_))] uint wn, [Values(0, 1, 2)] int size, // Q0: <8B, 4H, 2S> @@ -80,8 +96,8 @@ namespace Ryujinx.Tests.Cpu opcode |= (imm5 << 16); opcode |= ((q & 1) << 30); - uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); SingleOpcode(opcode, x1: wn, x31: w31, v0: v0); @@ -90,7 +106,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP <Vd>.<T>, X<n>")] - public void Dup_Gp_X([Values(0u)] uint rd, + public void Dup_Gp_X([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_X_))] ulong xn) { @@ -98,7 +114,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ulong z = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); V128 v0 = MakeVectorE0E1(z, z); SingleOpcode(opcode, x1: xn, x31: x31, v0: v0); @@ -187,7 +203,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.B[<index>]")] - public void Dup_V_8B_16B([Values(0u)] uint rd, + public void Dup_V_8B_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a, @@ -212,7 +228,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.H[<index>]")] - public void Dup_V_4H_8H([Values(0u)] uint rd, + public void Dup_V_4H_8H([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_4H_))] ulong a, @@ -237,7 +253,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.S[<index>]")] - public void Dup_V_2S_4S([Values(0u)] uint rd, + public void Dup_V_2S_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -262,7 +278,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("DUP <Vd>.<T>, <Vn>.D[<index>]")] - public void Dup_V_2D([Values(0u)] uint rd, + public void Dup_V_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -287,7 +303,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.B[<index>], W<n>")] - public void Ins_Gp_WB([Values(0u)] uint rd, + public void Ins_Gp_WB([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_W_))] uint wn, @@ -310,7 +326,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.H[<index>], W<n>")] - public void Ins_Gp_WH([Values(0u)] uint rd, + public void Ins_Gp_WH([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_W_))] uint wn, @@ -333,7 +349,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.S[<index>], W<n>")] - public void Ins_Gp_WS([Values(0u)] uint rd, + public void Ins_Gp_WS([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_W_))] uint wn, @@ -356,7 +372,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.D[<index>], X<n>")] - public void Ins_Gp_XD([Values(0u)] uint rd, + public void Ins_Gp_XD([Values(0u)] uint rd, [Values(1u, 31u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_X_))] ulong xn, @@ -379,7 +395,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.B[<index1>], <Vn>.B[<index2>]")] - public void Ins_V_BB([Values(0u)] uint rd, + public void Ins_V_BB([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a, @@ -405,7 +421,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.H[<index1>], <Vn>.H[<index2>]")] - public void Ins_V_HH([Values(0u)] uint rd, + public void Ins_V_HH([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_4H_))] ulong a, @@ -431,7 +447,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.S[<index1>], <Vn>.S[<index2>]")] - public void Ins_V_SS([Values(0u)] uint rd, + public void Ins_V_SS([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -457,7 +473,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("INS <Vd>.D[<index1>], <Vn>.D[<index2>]")] - public void Ins_V_DD([Values(0u)] uint rd, + public void Ins_V_DD([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -484,7 +500,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Wd>, <Vn>.B[<index>]")] public void Smov_S_BW([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_8B_))] ulong a, [Values(0u, 15u)] uint index) { @@ -507,7 +523,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Wd>, <Vn>.H[<index>]")] public void Smov_S_HW([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_4H_))] ulong a, [Values(0u, 7u)] uint index) { @@ -530,7 +546,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Xd>, <Vn>.B[<index>]")] public void Smov_S_BX([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_8B_))] ulong a, [Values(0u, 15u)] uint index) { @@ -552,7 +568,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Xd>, <Vn>.H[<index>]")] public void Smov_S_HX([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_4H_))] ulong a, [Values(0u, 7u)] uint index) { @@ -574,7 +590,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SMOV <Xd>, <Vn>.S[<index>]")] public void Smov_S_SX([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { @@ -596,7 +612,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Wd>, <Vn>.B[<index>]")] public void Umov_S_BW([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_8B_))] ulong a, [Values(0u, 15u)] uint index) { @@ -619,7 +635,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Wd>, <Vn>.H[<index>]")] public void Umov_S_HW([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_4H_))] ulong a, [Values(0u, 7u)] uint index) { @@ -642,7 +658,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Wd>, <Vn>.S[<index>]")] public void Umov_S_SW([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_2S_))] ulong a, [Values(0u, 1u, 2u, 3u)] uint index) { @@ -665,7 +681,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("UMOV <Xd>, <Vn>.D[<index>]")] public void Umov_S_DX([Values(0u, 31u)] uint rd, - [Values(1u)] uint rn, + [Values(1u)] uint rn, [ValueSource(nameof(_1D_))] ulong a, [Values(0u, 1u)] uint index) { diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs index 2cc1a0943..65cac9dee 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdLogical32.cs @@ -10,17 +10,19 @@ namespace Ryujinx.Tests.Cpu { #if SimdLogical32 -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _8B4H2S_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I_() { return new[] @@ -32,7 +34,7 @@ namespace Ryujinx.Tests.Cpu 0xf2000110u, // VAND D0, D0, D0 0xf2300110u, // VORN D0, D0, D0 0xf2200110u, // VORR D0, D0, D0 - 0xf3000110u // VEOR D0, D0, D0 + 0xf3000110u, // VEOR D0, D0, D0 }; } @@ -43,10 +45,10 @@ namespace Ryujinx.Tests.Cpu 0xf2800130u, // VBIC.I32 D0, #0 (A1) 0xf2800930u, // VBIC.I16 D0, #0 (A2) 0xf2800110u, // VORR.I32 D0, #0 (A1) - 0xf2800910u // VORR.I16 D0, #0 (A2) + 0xf2800910u, // VORR.I16 D0, #0 (A2) }; } - #endregion + #endregion [Test, Pairwise] public void Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I([ValueSource(nameof(_Vbic_Vbif_Vbit_Vbsl_Vand_Vorn_Vorr_Veor_I_))] uint opcode, @@ -62,14 +64,17 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; - rn >>= 1; rn <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rn >>= 1; + rn <<= 1; + rm >>= 1; + rm <<= 1; } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(z, ~z); V128 v1 = MakeVectorE0E1(a, ~a); @@ -97,10 +102,11 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; + rd >>= 1; + rd <<= 1; } - opcode |= ((uint)imm & 0xf) << 0; + opcode |= ((uint)imm & 0xf) << 0; opcode |= ((uint)imm & 0x70) << 12; opcode |= ((uint)imm & 0x80) << 17; opcode |= (cMode & 0x3) << 9; @@ -129,14 +135,17 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; - rn >>= 1; rn <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rn >>= 1; + rn <<= 1; + rm >>= 1; + rm <<= 1; } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= (size & 0x3) << 20; @@ -150,4 +159,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs index 50915fd34..28f00d58e 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs @@ -10,10 +10,10 @@ namespace Ryujinx.Tests.Cpu [Category("SimdMemory32")] public sealed class CpuTestSimdMemory32 : CpuTest32 { - private static readonly uint TestOffset = DataBaseAddress + 0x500; + private static readonly uint _testOffset = DataBaseAddress + 0x500; #if SimdMemory32 - private uint[] _ldStModes = + private readonly uint[] _ldStModes = { // LD1 0b0111, @@ -32,7 +32,7 @@ namespace Ryujinx.Tests.Cpu // LD4 0b0000, - 0b0001 + 0b0001, }; [Test, Pairwise, Description("VLDn.<size> <list>, [<Rn> {:<align>}]{ /!/, <Rm>} (single n element structure)")] @@ -51,16 +51,16 @@ namespace Ryujinx.Tests.Cpu opcode |= ((size & 3) << 10) | ((rn & 15) << 16) | (rm & 15); - uint index_align = (index << (int)(1 + size)) & 15; + uint indexAlign = (index << (int)(1 + size)) & 15; - opcode |= (index_align) << 4; + opcode |= (indexAlign) << 4; opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. - SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); + SingleOpcode(opcode, r0: _testOffset, r1: offset, sp: _testOffset); CompareAgainstUnicorn(); } @@ -85,9 +85,12 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0xf) << 12); opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. - if (t) opcode |= 1 << 5; + if (t) + { + opcode |= 1 << 5; + } - SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); + SingleOpcode(opcode, r0: _testOffset, r1: offset, sp: _testOffset); CompareAgainstUnicorn(); } @@ -116,7 +119,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); - SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); + SingleOpcode(opcode, r0: _testOffset, r1: offset, sp: _testOffset); CompareAgainstUnicorn(); } @@ -139,16 +142,16 @@ namespace Ryujinx.Tests.Cpu opcode |= ((size & 3) << 10) | ((rn & 15) << 16) | (rm & 15); - uint index_align = (index << (int)(1 + size)) & 15; + uint indexAlign = (index << (int)(1 + size)) & 15; - opcode |= (index_align) << 4; + opcode |= (indexAlign) << 4; opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); opcode |= (n & 3) << 8; // ST1 is 0, ST2 is 1 etc. - SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); + SingleOpcode(opcode, r0: _testOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: _testOffset); CompareAgainstUnicorn(); } @@ -179,7 +182,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); - SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); + SingleOpcode(opcode, r0: _testOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: _testOffset); CompareAgainstUnicorn(); } @@ -201,7 +204,7 @@ namespace Ryujinx.Tests.Cpu // Note: 3rd 0 leaves a space for "D". 0b0100, // Increment after. 0b0101, // Increment after. (!) - 0b1001 // Decrement before. (!) + 0b1001, // Decrement before. (!) }; opcode |= ((vldmModes[mode] & 15) << 21); @@ -212,7 +215,11 @@ namespace Ryujinx.Tests.Cpu opcode |= ((uint)(single ? 0 : 1) << 8); - if (!single) regs = (regs << 1); // Low bit must be 0 - must be even number of registers. + if (!single) + { + regs <<= 1; // Low bit must be 0 - must be even number of registers. + } + uint regSize = single ? 1u : 2u; if (vd + (regs / regSize) > 32) // Can't address further than S31 or D31. @@ -227,7 +234,7 @@ namespace Ryujinx.Tests.Cpu opcode |= regs & 0xff; - SingleOpcode(opcode, r0: TestOffset, sp: TestOffset); + SingleOpcode(opcode, r0: _testOffset, sp: _testOffset); CompareAgainstUnicorn(); } @@ -262,7 +269,7 @@ namespace Ryujinx.Tests.Cpu } opcode |= imm & 0xff; - SingleOpcode(opcode, r0: TestOffset); + SingleOpcode(opcode, r0: _testOffset); CompareAgainstUnicorn(); } @@ -299,12 +306,12 @@ namespace Ryujinx.Tests.Cpu (V128 vec1, V128 vec2, _, _) = GenerateTestVectors(); - SingleOpcode(opcode, r0: TestOffset, v0: vec1, v1: vec2); + SingleOpcode(opcode, r0: _testOffset, v0: vec1, v1: vec2); CompareAgainstUnicorn(); } - private (V128, V128, V128, V128) GenerateTestVectors() + private static (V128, V128, V128, V128) GenerateTestVectors() { return ( new V128(-12.43f, 1872.23f, 4456.23f, -5622.2f), @@ -314,7 +321,7 @@ namespace Ryujinx.Tests.Cpu ); } - private byte[] GenerateVectorSequence(int length) + private static byte[] GenerateVectorSequence(int length) { int floatLength = length >> 2; float[] data = new float[floatLength]; @@ -330,4 +337,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs index 399b058f2..0566df20f 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdMov32.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Tests.Cpu 0b1110_0, 0b1111_0, - 0b1110_1 + 0b1110_1, }; uint opcode = 0xf2800010u; // VMOV.I32 D0, #0 @@ -97,7 +97,10 @@ namespace Ryujinx.Tests.Cpu opcode |= (vn & 0x1e) << 15; opcode |= (rt & 0xf) << 12; - if (op) opcode |= 1 << 20; + if (op) + { + opcode |= 1 << 20; + } SingleOpcode(opcode, r0: valueRn, r1: valueRn, r2: valueRn, r3: valueRn, v0: new V128(valueVn1, valueVn2)); @@ -217,10 +220,10 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); - V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3); @@ -247,10 +250,10 @@ namespace Ryujinx.Tests.Cpu opcode |= 1 << 24; } - V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3); @@ -279,10 +282,10 @@ namespace Ryujinx.Tests.Cpu opcode |= (vd & 0x10) << 18; opcode |= (vd & 0xf) << 12; - V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3); @@ -348,18 +351,20 @@ namespace Ryujinx.Tests.Cpu if (q) { opcode |= 1 << 6; - vd <<= 1; vm <<= 1; + vd <<= 1; + vm <<= 1; } + opcode |= (vm & 0x10) << 1; opcode |= (vm & 0xf); opcode |= (vd & 0x10) << 18; opcode |= (vd & 0xf) << 12; opcode |= (size & 0x3) << 18; - V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3); @@ -381,18 +386,20 @@ namespace Ryujinx.Tests.Cpu if (q) { opcode |= 1 << 6; - vd <<= 1; vm <<= 1; + vd <<= 1; + vm <<= 1; } + opcode |= (vm & 0x10) << 1; opcode |= (vm & 0xf); opcode |= (vd & 0x10) << 18; opcode |= (vd & 0xf) << 12; opcode |= (size & 0x3) << 18; - V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3); @@ -414,18 +421,20 @@ namespace Ryujinx.Tests.Cpu if (q) { opcode |= 1 << 6; - vd <<= 1; vm <<= 1; + vd <<= 1; + vm <<= 1; } + opcode |= (vm & 0x10) << 1; opcode |= (vm & 0xf); opcode |= (vd & 0x10) << 18; opcode |= (vd & 0xf) << 12; opcode |= (size & 0x3) << 18; - V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3); @@ -459,22 +468,22 @@ namespace Ryujinx.Tests.Cpu opcode |= (length & 0x3) << 8; var rnd = TestContext.CurrentContext.Random; - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v4 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v5 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v4 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v5 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); byte maxIndex = (byte)(length * 8 - 1); byte[] b0 = new byte[16]; byte[] b1 = new byte[16]; - for (int i=0; i<16; i++) + for (int i = 0; i < 16; i++) { b0[i] = rnd.NextByte(maxIndex); b1[i] = rnd.NextByte(maxIndex); } - V128 v0 = new V128(b0); - V128 v1 = new V128(b1); + V128 v0 = new(b0); + V128 v1 = new(b1); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3, v4: v4, v5: v5); @@ -493,7 +502,9 @@ namespace Ryujinx.Tests.Cpu if (q) { opcode |= 1 << 6; - vd <<= 1; vm <<= 1; vn <<= 1; + vd <<= 1; + vm <<= 1; + vn <<= 1; } else if (imm4 > 7) { @@ -507,10 +518,10 @@ namespace Ryujinx.Tests.Cpu opcode |= (vn & 0xf) << 16; opcode |= (imm4 & 0xf) << 8; - V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3); @@ -541,7 +552,7 @@ namespace Ryujinx.Tests.Cpu opcode |= (size & 1) << 5; // E opcode |= (size & 2) << 21; // B - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, r0: valueRn, r1: valueRn, r2: valueRn, r3: valueRn, v0: new V128(valueVn1, valueVn2), v1: v1); @@ -586,9 +597,9 @@ namespace Ryujinx.Tests.Cpu opcode |= imm4 << 16; - V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); - V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v2 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); + V128 v3 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()); SingleOpcode(opcode, v0: new V128(valueVn1, valueVn2), v1: v1, v2: v2, v3: v3); @@ -596,4 +607,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 1f2a6bc81..207f76089 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -11,75 +11,93 @@ namespace Ryujinx.Tests.Cpu { #if SimdReg -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _1B1H1S1D_() { - return new[] { 0x0000000000000000ul, 0x000000000000007Ful, - 0x0000000000000080ul, 0x00000000000000FFul, - 0x0000000000007FFFul, 0x0000000000008000ul, - 0x000000000000FFFFul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul, - 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, - 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x000000000000007Ful, + 0x0000000000000080ul, 0x00000000000000FFul, + 0x0000000000007FFFul, 0x0000000000008000ul, + 0x000000000000FFFFul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, + 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _1D_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _1H1S_() { - return new[] { 0x0000000000000000ul, 0x0000000000007FFFul, - 0x0000000000008000ul, 0x000000000000FFFFul, - 0x000000007FFFFFFFul, 0x0000000080000000ul, - 0x00000000FFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul, + 0x000000007FFFFFFFul, 0x0000000080000000ul, + 0x00000000FFFFFFFFul, + }; } private static ulong[] _4H2S_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H2S1D_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B1D_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H2S_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H2S1D_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<ulong> _1S_F_() @@ -93,19 +111,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -135,19 +153,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -176,19 +194,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -205,9 +223,9 @@ namespace Ryujinx.Tests.Cpu yield return rnd2; } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S_() { return new[] @@ -218,7 +236,7 @@ namespace Ryujinx.Tests.Cpu 0x1E220820u, // FMUL S0, S1, S2 0x5E22DC20u, // FMULX S0, S1, S2 0x1E228820u, // FNMUL S0, S1, S2 - 0x1E223820u // FSUB S0, S1, S2 + 0x1E223820u, // FSUB S0, S1, S2 }; } @@ -232,7 +250,7 @@ namespace Ryujinx.Tests.Cpu 0x1E620820u, // FMUL D0, D1, D2 0x5E62DC20u, // FMULX D0, D1, D2 0x1E628820u, // FNMUL D0, D1, D2 - 0x1E623820u // FSUB D0, D1, D2 + 0x1E623820u, // FSUB D0, D1, D2 }; } @@ -246,7 +264,7 @@ namespace Ryujinx.Tests.Cpu 0x2E20FC00u, // FDIV V0.2S, V0.2S, V0.2S 0x2E20DC00u, // FMUL V0.2S, V0.2S, V0.2S 0x0E20DC00u, // FMULX V0.2S, V0.2S, V0.2S - 0x0EA0D400u // FSUB V0.2S, V0.2S, V0.2S + 0x0EA0D400u, // FSUB V0.2S, V0.2S, V0.2S }; } @@ -260,7 +278,7 @@ namespace Ryujinx.Tests.Cpu 0x6E60FC00u, // FDIV V0.2D, V0.2D, V0.2D 0x6E60DC00u, // FMUL V0.2D, V0.2D, V0.2D 0x4E60DC00u, // FMULX V0.2D, V0.2D, V0.2D - 0x4EE0D400u // FSUB V0.2D, V0.2D, V0.2D + 0x4EE0D400u, // FSUB V0.2D, V0.2D, V0.2D }; } @@ -272,7 +290,7 @@ namespace Ryujinx.Tests.Cpu 0x7EA2EC20u, // FACGT S0, S1, S2 0x5E22E420u, // FCMEQ S0, S1, S2 0x7E22E420u, // FCMGE S0, S1, S2 - 0x7EA2E420u // FCMGT S0, S1, S2 + 0x7EA2E420u, // FCMGT S0, S1, S2 }; } @@ -284,7 +302,7 @@ namespace Ryujinx.Tests.Cpu 0x7EE2EC20u, // FACGT D0, D1, D2 0x5E62E420u, // FCMEQ D0, D1, D2 0x7E62E420u, // FCMGE D0, D1, D2 - 0x7EE2E420u // FCMGT D0, D1, D2 + 0x7EE2E420u, // FCMGT D0, D1, D2 }; } @@ -296,7 +314,7 @@ namespace Ryujinx.Tests.Cpu 0x2EA0EC00u, // FACGT V0.2S, V0.2S, V0.2S 0x0E20E400u, // FCMEQ V0.2S, V0.2S, V0.2S 0x2E20E400u, // FCMGE V0.2S, V0.2S, V0.2S - 0x2EA0E400u // FCMGT V0.2S, V0.2S, V0.2S + 0x2EA0E400u, // FCMGT V0.2S, V0.2S, V0.2S }; } @@ -308,7 +326,7 @@ namespace Ryujinx.Tests.Cpu 0x6EE0EC00u, // FACGT V0.2D, V0.2D, V0.2D 0x4E60E400u, // FCMEQ V0.2D, V0.2D, V0.2D 0x6E60E400u, // FCMGE V0.2D, V0.2D, V0.2D - 0x6EE0E400u // FCMGT V0.2D, V0.2D, V0.2D + 0x6EE0E400u, // FCMGT V0.2D, V0.2D, V0.2D }; } @@ -317,7 +335,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E222020u, // FCMP S1, S2 - 0x1E222030u // FCMPE S1, S2 + 0x1E222030u, // FCMPE S1, S2 }; } @@ -326,7 +344,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x1E622020u, // FCMP D1, D2 - 0x1E622030u // FCMPE D1, D2 + 0x1E622030u, // FCMPE D1, D2 }; } @@ -337,7 +355,7 @@ namespace Ryujinx.Tests.Cpu 0x1F020C20u, // FMADD S0, S1, S2, S3 0x1F028C20u, // FMSUB S0, S1, S2, S3 0x1F220C20u, // FNMADD S0, S1, S2, S3 - 0x1F228C20u // FNMSUB S0, S1, S2, S3 + 0x1F228C20u, // FNMSUB S0, S1, S2, S3 }; } @@ -348,7 +366,7 @@ namespace Ryujinx.Tests.Cpu 0x1F420C20u, // FMADD D0, D1, D2, D3 0x1F428C20u, // FMSUB D0, D1, D2, D3 0x1F620C20u, // FNMADD D0, D1, D2, D3 - 0x1F628C20u // FNMSUB D0, D1, D2, D3 + 0x1F628C20u, // FNMSUB D0, D1, D2, D3 }; } @@ -359,7 +377,7 @@ namespace Ryujinx.Tests.Cpu 0x1E224820u, // FMAX S0, S1, S2 0x1E226820u, // FMAXNM S0, S1, S2 0x1E225820u, // FMIN S0, S1, S2 - 0x1E227820u // FMINNM S0, S1, S2 + 0x1E227820u, // FMINNM S0, S1, S2 }; } @@ -370,7 +388,7 @@ namespace Ryujinx.Tests.Cpu 0x1E624820u, // FMAX D0, D1, D2 0x1E626820u, // FMAXNM D0, D1, D2 0x1E625820u, // FMIN D0, D1, D2 - 0x1E627820u // FMINNM D0, D1, D2 + 0x1E627820u, // FMINNM D0, D1, D2 }; } @@ -385,7 +403,7 @@ namespace Ryujinx.Tests.Cpu 0x0EA0F400u, // FMIN V0.2S, V0.2S, V0.2S 0x0EA0C400u, // FMINNM V0.2S, V0.2S, V0.2S 0x2EA0C400u, // FMINNMP V0.2S, V0.2S, V0.2S - 0x2EA0F400u // FMINP V0.2S, V0.2S, V0.2S + 0x2EA0F400u, // FMINP V0.2S, V0.2S, V0.2S }; } @@ -400,7 +418,7 @@ namespace Ryujinx.Tests.Cpu 0x4EE0F400u, // FMIN V0.2D, V0.2D, V0.2D 0x4EE0C400u, // FMINNM V0.2D, V0.2D, V0.2D 0x6EE0C400u, // FMINNMP V0.2D, V0.2D, V0.2D - 0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D + 0x6EE0F400u, // FMINP V0.2D, V0.2D, V0.2D }; } @@ -409,7 +427,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0E20CC00u, // FMLA V0.2S, V0.2S, V0.2S - 0x0EA0CC00u // FMLS V0.2S, V0.2S, V0.2S + 0x0EA0CC00u, // FMLS V0.2S, V0.2S, V0.2S }; } @@ -418,7 +436,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4E60CC00u, // FMLA V0.2D, V0.2D, V0.2D - 0x4EE0CC00u // FMLS V0.2D, V0.2D, V0.2D + 0x4EE0CC00u, // FMLS V0.2D, V0.2D, V0.2D }; } @@ -427,7 +445,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5E22FC20u, // FRECPS S0, S1, S2 - 0x5EA2FC20u // FRSQRTS S0, S1, S2 + 0x5EA2FC20u, // FRSQRTS S0, S1, S2 }; } @@ -436,7 +454,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5E62FC20u, // FRECPS D0, D1, D2 - 0x5EE2FC20u // FRSQRTS D0, D1, D2 + 0x5EE2FC20u, // FRSQRTS D0, D1, D2 }; } @@ -445,7 +463,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0E20FC00u, // FRECPS V0.2S, V0.2S, V0.2S - 0x0EA0FC00u // FRSQRTS V0.2S, V0.2S, V0.2S + 0x0EA0FC00u, // FRSQRTS V0.2S, V0.2S, V0.2S }; } @@ -454,7 +472,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4E60FC00u, // FRECPS V0.2D, V0.2D, V0.2D - 0x4EE0FC00u // FRSQRTS V0.2D, V0.2D, V0.2D + 0x4EE0FC00u, // FRSQRTS V0.2D, V0.2D, V0.2D }; } @@ -464,7 +482,7 @@ namespace Ryujinx.Tests.Cpu { 0x0E209400u, // MLA V0.8B, V0.8B, V0.8B 0x2E209400u, // MLS V0.8B, V0.8B, V0.8B - 0x0E209C00u // MUL V0.8B, V0.8B, V0.8B + 0x0E209C00u, // MUL V0.8B, V0.8B, V0.8B }; } @@ -474,7 +492,7 @@ namespace Ryujinx.Tests.Cpu { 0x4E209400u, // MLA V0.16B, V0.16B, V0.16B 0x6E209400u, // MLS V0.16B, V0.16B, V0.16B - 0x4E209C00u // MUL V0.16B, V0.16B, V0.16B + 0x4E209C00u, // MUL V0.16B, V0.16B, V0.16B }; } @@ -485,7 +503,7 @@ namespace Ryujinx.Tests.Cpu 0x5E000000u, // SHA1C Q0, S0, V0.4S 0x5E002000u, // SHA1M Q0, S0, V0.4S 0x5E001000u, // SHA1P Q0, S0, V0.4S - 0x5E003000u // SHA1SU0 V0.4S, V0.4S, V0.4S + 0x5E003000u, // SHA1SU0 V0.4S, V0.4S, V0.4S }; } @@ -495,7 +513,7 @@ namespace Ryujinx.Tests.Cpu { 0x5E004000u, // SHA256H Q0, Q0, V0.4S 0x5E005000u, // SHA256H2 Q0, Q0, V0.4S - 0x5E006000u // SHA256SU1 V0.4S, V0.4S, V0.4S + 0x5E006000u, // SHA256SU1 V0.4S, V0.4S, V0.4S }; } @@ -510,7 +528,7 @@ namespace Ryujinx.Tests.Cpu 0x2E206400u, // UMAX V0.8B, V0.8B, V0.8B 0x2E20A400u, // UMAXP V0.8B, V0.8B, V0.8B 0x2E206C00u, // UMIN V0.8B, V0.8B, V0.8B - 0x2E20AC00u // UMINP V0.8B, V0.8B, V0.8B + 0x2E20AC00u, // UMINP V0.8B, V0.8B, V0.8B }; } @@ -523,7 +541,7 @@ namespace Ryujinx.Tests.Cpu 0x0E20C000u, // SMULL V0.8H, V0.8B, V0.8B 0x2E208000u, // UMLAL V0.8H, V0.8B, V0.8B 0x2E20A000u, // UMLSL V0.8H, V0.8B, V0.8B - 0x2E20C000u // UMULL V0.8H, V0.8B, V0.8B + 0x2E20C000u, // UMULL V0.8H, V0.8B, V0.8B }; } @@ -536,7 +554,7 @@ namespace Ryujinx.Tests.Cpu 0x4E20C000u, // SMULL2 V0.8H, V0.16B, V0.16B 0x6E208000u, // UMLAL2 V0.8H, V0.16B, V0.16B 0x6E20A000u, // UMLSL2 V0.8H, V0.16B, V0.16B - 0x6E20C000u // UMULL2 V0.8H, V0.16B, V0.16B + 0x6E20C000u, // UMULL2 V0.8H, V0.16B, V0.16B }; } @@ -545,7 +563,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5EE04400u, // SSHL D0, D0, D0 - 0x7EE04400u // USHL D0, D0, D0 + 0x7EE04400u, // USHL D0, D0, D0 }; } @@ -560,7 +578,7 @@ namespace Ryujinx.Tests.Cpu 0x2E205C00u, // UQRSHL V0.8B, V0.8B, V0.8B 0x2E204C00u, // UQSHL V0.8B, V0.8B, V0.8B 0x2E205400u, // URSHL V0.8B, V0.8B, V0.8B - 0x2E204400u // USHL V0.8B, V0.8B, V0.8B + 0x2E204400u, // USHL V0.8B, V0.8B, V0.8B }; } @@ -575,19 +593,19 @@ namespace Ryujinx.Tests.Cpu 0x6E205C00u, // UQRSHL V0.16B, V0.16B, V0.16B 0x6E204C00u, // UQSHL V0.16B, V0.16B, V0.16B 0x6E205400u, // URSHL V0.16B, V0.16B, V0.16B - 0x6E204400u // USHL V0.16B, V0.16B, V0.16B + 0x6E204400u, // USHL V0.16B, V0.16B, V0.16B }; } -#endregion + #endregion private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; [Test, Pairwise, Description("ADD <V><d>, <V><n>, <V><m>")] - public void Add_S_D([Values(0u)] uint rd, + public void Add_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -607,7 +625,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Add_V_8B_4H_2S([Values(0u)] uint rd, + public void Add_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -629,7 +647,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Add_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Add_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -651,7 +669,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Addhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Addhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -673,7 +691,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Addhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Addhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -695,7 +713,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Addp_V_8B_4H_2S([Values(0u)] uint rd, + public void Addp_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -717,7 +735,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Addp_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Addp_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -739,7 +757,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("AND <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void And_V_8B([Values(0u)] uint rd, + public void And_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -759,7 +777,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("AND <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void And_V_16B([Values(0u)] uint rd, + public void And_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -779,7 +797,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIC <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bic_V_8B([Values(0u)] uint rd, + public void Bic_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -799,7 +817,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIC <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bic_V_16B([Values(0u)] uint rd, + public void Bic_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -819,7 +837,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIF <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bif_V_8B([Values(0u)] uint rd, + public void Bif_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -839,7 +857,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIF <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bif_V_16B([Values(0u)] uint rd, + public void Bif_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -859,7 +877,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bit_V_8B([Values(0u)] uint rd, + public void Bit_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -879,7 +897,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bit_V_16B([Values(0u)] uint rd, + public void Bit_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -899,7 +917,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BSL <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bsl_V_8B([Values(0u)] uint rd, + public void Bsl_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -919,7 +937,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BSL <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bsl_V_16B([Values(0u)] uint rd, + public void Bsl_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -939,7 +957,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <V><d>, <V><n>, <V><m>")] - public void Cmeq_S_D([Values(0u)] uint rd, + public void Cmeq_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -959,7 +977,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -981,7 +999,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -1003,7 +1021,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <V><d>, <V><n>, <V><m>")] - public void Cmge_S_D([Values(0u)] uint rd, + public void Cmge_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -1023,7 +1041,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -1045,7 +1063,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -1067,7 +1085,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <V><d>, <V><n>, <V><m>")] - public void Cmgt_S_D([Values(0u)] uint rd, + public void Cmgt_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -1087,7 +1105,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -1109,7 +1127,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -1131,7 +1149,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHI <V><d>, <V><n>, <V><m>")] - public void Cmhi_S_D([Values(0u)] uint rd, + public void Cmhi_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -1151,7 +1169,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHI <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhi_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmhi_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -1173,7 +1191,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHI <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhi_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmhi_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -1195,7 +1213,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHS <V><d>, <V><n>, <V><m>")] - public void Cmhs_S_D([Values(0u)] uint rd, + public void Cmhs_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -1215,7 +1233,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHS <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhs_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmhs_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -1237,7 +1255,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHS <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhs_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmhs_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -1259,7 +1277,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMTST <V><d>, <V><n>, <V><m>")] - public void Cmtst_S_D([Values(0u)] uint rd, + public void Cmtst_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -1279,7 +1297,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMTST <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmtst_V_8B_4H_2S([Values(0u)] uint rd, + public void Cmtst_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -1301,7 +1319,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMTST <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmtst_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Cmtst_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -1323,7 +1341,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("EOR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Eor_V_8B([Values(0u)] uint rd, + public void Eor_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -1343,7 +1361,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("EOR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Eor_V_16B([Values(0u)] uint rd, + public void Eor_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -1362,7 +1380,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b) @@ -1382,7 +1401,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_D([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Nmul_Sub_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b) @@ -1402,9 +1422,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2S_4S([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_F_))] ulong z, @@ -1429,9 +1450,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2D([ValueSource(nameof(_F_Abd_Add_Div_Mul_Mulx_Sub_P_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_F_))] ulong z, @@ -1454,7 +1476,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_AcCm_EqGeGt_S_S([ValueSource(nameof(_F_AcCm_EqGeGt_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b) @@ -1473,7 +1496,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_AcCm_EqGeGt_S_D([ValueSource(nameof(_F_AcCm_EqGeGt_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b) @@ -1492,9 +1516,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_AcCm_EqGeGt_V_2S_4S([ValueSource(nameof(_F_AcCm_EqGeGt_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_F_))] ulong z, @@ -1518,9 +1543,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_AcCm_EqGeGt_V_2D([ValueSource(nameof(_F_AcCm_EqGeGt_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_F_))] ulong z, @@ -1542,7 +1568,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cmp_Cmpe_S_S([ValueSource(nameof(_F_Cmp_Cmpe_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b) @@ -1560,7 +1587,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cmp_Cmpe_S_D([ValueSource(nameof(_F_Cmp_Cmpe_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b) @@ -1578,7 +1606,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Madd_Msub_Nmadd_Nmsub_S_S([ValueSource(nameof(_F_Madd_Msub_Nmadd_Nmsub_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b, @@ -1600,7 +1630,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Madd_Msub_Nmadd_Nmsub_S_D([ValueSource(nameof(_F_Madd_Msub_Nmadd_Nmsub_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b, @@ -1622,7 +1654,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Max_Min_Nm_S_S([ValueSource(nameof(_F_Max_Min_Nm_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b) @@ -1642,7 +1675,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Max_Min_Nm_S_D([ValueSource(nameof(_F_Max_Min_Nm_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b) @@ -1662,9 +1696,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Max_Min_Nm_P_V_2S_4S([ValueSource(nameof(_F_Max_Min_Nm_P_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_F_))] ulong z, @@ -1689,9 +1724,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Max_Min_Nm_P_V_2D([ValueSource(nameof(_F_Max_Min_Nm_P_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_F_))] ulong z, @@ -1714,9 +1750,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Mla_Mls_V_2S_4S([ValueSource(nameof(_F_Mla_Mls_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_F_))] ulong z, @@ -1741,9 +1779,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Mla_Mls_V_2D([ValueSource(nameof(_F_Mla_Mls_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_F_))] ulong z, @@ -1766,7 +1806,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Recps_Rsqrts_S_S([ValueSource(nameof(_F_Recps_Rsqrts_S_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_1S_F_))] ulong b) @@ -1786,7 +1828,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Recps_Rsqrts_S_D([ValueSource(nameof(_F_Recps_Rsqrts_S_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b) @@ -1806,9 +1850,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Recps_Rsqrts_V_2S_4S([ValueSource(nameof(_F_Recps_Rsqrts_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_F_))] ulong z, @@ -1833,9 +1879,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Recps_Rsqrts_V_2D([ValueSource(nameof(_F_Recps_Rsqrts_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_F_))] ulong z, @@ -1860,7 +1908,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Mla_Mls_Mul_V_8B_4H_2S([ValueSource(nameof(_Mla_Mls_Mul_V_8B_4H_2S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -1882,7 +1930,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Mla_Mls_Mul_V_16B_8H_4S([ValueSource(nameof(_Mla_Mls_Mul_V_16B_8H_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -1903,7 +1951,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orn_V_8B([Values(0u)] uint rd, + public void Orn_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -1923,7 +1971,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orn_V_16B([Values(0u)] uint rd, + public void Orn_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -1943,7 +1991,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orr_V_8B([Values(0u)] uint rd, + public void Orr_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -1963,7 +2011,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orr_V_16B([Values(0u)] uint rd, + public void Orr_V_16B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, @@ -1983,7 +2031,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("PMULL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Pmull_V([Values(0u)] uint rd, + public void Pmull_V([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B1D_))] ulong z0, @@ -1993,7 +2041,7 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_8B1D_))] ulong b0, [ValueSource(nameof(_8B1D_))] ulong b1, [Values(0b00u, 0b11u)] uint size, // Q0: <8B, 1D> => <8H, 1Q> - [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 2D> => <8H, 1Q> + [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 2D> => <8H, 1Q> { uint opcode = 0x0E20E000; // PMULL V0.8H, V0.8B, V0.8B opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); @@ -2010,7 +2058,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Raddhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Raddhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -2032,7 +2080,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Raddhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Raddhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -2054,7 +2102,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Rsubhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Rsubhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -2076,7 +2124,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Rsubhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Rsubhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -2098,7 +2146,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Saba_V_8B_4H_2S([Values(0u)] uint rd, + public void Saba_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2120,7 +2168,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Saba_V_16B_8H_4S([Values(0u)] uint rd, + public void Saba_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2142,7 +2190,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Sabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2164,7 +2212,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Sabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2186,7 +2234,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sabd_V_8B_4H_2S([Values(0u)] uint rd, + public void Sabd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2208,7 +2256,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sabd_V_16B_8H_4S([Values(0u)] uint rd, + public void Sabd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2230,7 +2278,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Sabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2252,7 +2300,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Sabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2274,7 +2322,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Saddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Saddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2296,7 +2344,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Saddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Saddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2318,12 +2366,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x0E201000; // SADDW V0.8H, V0.8H, V0.8B @@ -2340,12 +2388,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Saddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + public void Saddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x4E201000; // SADDW2 V0.8H, V0.8H, V0.16B @@ -2363,7 +2411,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Sha1c_Sha1m_Sha1p_Sha1su0_V([ValueSource(nameof(_Sha1c_Sha1m_Sha1p_Sha1su0_V_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, @@ -2383,7 +2431,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Sha256h_Sha256h2_Sha256su1_V([ValueSource(nameof(_Sha256h_Sha256h2_Sha256su1_V_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, @@ -2402,7 +2450,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Shadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Shadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2424,7 +2472,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Shadd_V_16B_8H_4S([Values(0u)] uint rd, + public void Shadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2446,7 +2494,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SHSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Shsub_V_8B_4H_2S([Values(0u)] uint rd, + public void Shsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2468,7 +2516,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SHSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Shsub_V_16B_8H_4S([Values(0u)] uint rd, + public void Shsub_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2491,7 +2539,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Max_Min_P_V([ValueSource(nameof(_SU_Max_Min_P_V_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2515,7 +2563,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Mlal_Mlsl_Mull_V_8B8H_4H4S_2S2D([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_V_8B8H_4H4S_2S2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2537,7 +2585,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Mlal_Mlsl_Mull_V_16B8H_8H4S_4S2D([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_V_16B8H_8H4S_4S2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2558,7 +2606,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQADD <V><d>, <V><n>, <V><m>")] - public void Sqadd_S_B_H_S_D([Values(0u)] uint rd, + public void Sqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1B1H1S1D_))] ulong z, @@ -2580,7 +2628,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Sqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2602,7 +2650,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Sqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -2624,7 +2672,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQDMULH <V><d>, <V><n>, <V><m>")] - public void Sqdmulh_S_H_S([Values(0u)] uint rd, + public void Sqdmulh_S_H_S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1H1S_))] ulong z, @@ -2646,7 +2694,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqdmulh_V_4H_2S([Values(0u)] uint rd, + public void Sqdmulh_V_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S_))] ulong z, @@ -2668,7 +2716,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqdmulh_V_8H_4S([Values(0u)] uint rd, + public void Sqdmulh_V_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S_))] ulong z, @@ -2690,7 +2738,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQRDMULH <V><d>, <V><n>, <V><m>")] - public void Sqrdmulh_S_H_S([Values(0u)] uint rd, + public void Sqrdmulh_S_H_S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1H1S_))] ulong z, @@ -2712,7 +2760,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQRDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqrdmulh_V_4H_2S([Values(0u)] uint rd, + public void Sqrdmulh_V_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S_))] ulong z, @@ -2734,7 +2782,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQRDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqrdmulh_V_8H_4S([Values(0u)] uint rd, + public void Sqrdmulh_V_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S_))] ulong z, @@ -2756,7 +2804,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQSUB <V><d>, <V><n>, <V><m>")] - public void Sqsub_S_B_H_S_D([Values(0u)] uint rd, + public void Sqsub_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1B1H1S1D_))] ulong z, @@ -2778,7 +2826,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqsub_V_8B_4H_2S([Values(0u)] uint rd, + public void Sqsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2800,7 +2848,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Sqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -2822,7 +2870,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SRHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Srhadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Srhadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2844,7 +2892,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SRHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Srhadd_V_16B_8H_4S([Values(0u)] uint rd, + public void Srhadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2867,7 +2915,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShlReg_S_D([ValueSource(nameof(_ShlReg_S_D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -2887,7 +2935,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShlReg_V_8B_4H_2S([ValueSource(nameof(_ShlReg_V_8B_4H_2S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2909,7 +2957,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShlReg_V_16B_8H_4S_2D([ValueSource(nameof(_ShlReg_V_16B_8H_4S_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -2930,7 +2978,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SSUBL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Ssubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Ssubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2952,7 +3000,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SSUBL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Ssubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Ssubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -2974,12 +3022,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SSUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x0E203000; // SSUBW V0.8H, V0.8H, V0.8B @@ -2996,12 +3044,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SSUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Ssubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + public void Ssubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x4E203000; // SSUBW2 V0.8H, V0.8H, V0.16B @@ -3018,7 +3066,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUB <V><d>, <V><n>, <V><m>")] - public void Sub_S_D([Values(0u)] uint rd, + public void Sub_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_))] ulong z, @@ -3038,7 +3086,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sub_V_8B_4H_2S([Values(0u)] uint rd, + public void Sub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3060,7 +3108,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sub_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Sub_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3082,7 +3130,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Subhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + public void Subhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -3104,7 +3152,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Subhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + public void Subhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H2S1D_))] ulong z, @@ -3126,7 +3174,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn1_V_8B_4H_2S([Values(0u)] uint rd, + public void Trn1_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3148,7 +3196,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn1_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Trn1_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3170,7 +3218,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn2_V_8B_4H_2S([Values(0u)] uint rd, + public void Trn2_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3192,7 +3240,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn2_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Trn2_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3214,7 +3262,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uaba_V_8B_4H_2S([Values(0u)] uint rd, + public void Uaba_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3236,7 +3284,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uaba_V_16B_8H_4S([Values(0u)] uint rd, + public void Uaba_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3258,7 +3306,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Uabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3280,7 +3328,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Uabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3302,7 +3350,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uabd_V_8B_4H_2S([Values(0u)] uint rd, + public void Uabd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3324,7 +3372,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uabd_V_16B_8H_4S([Values(0u)] uint rd, + public void Uabd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3346,7 +3394,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Uabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3368,7 +3416,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Uabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3390,7 +3438,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uaddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Uaddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3412,7 +3460,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uaddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Uaddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3434,12 +3482,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x2E201000; // UADDW V0.8H, V0.8H, V0.8B @@ -3456,12 +3504,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Uaddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + public void Uaddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x6E201000; // UADDW2 V0.8H, V0.8H, V0.16B @@ -3478,7 +3526,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uhadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Uhadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3500,7 +3548,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uhadd_V_16B_8H_4S([Values(0u)] uint rd, + public void Uhadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3522,7 +3570,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UHSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uhsub_V_8B_4H_2S([Values(0u)] uint rd, + public void Uhsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3544,7 +3592,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UHSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uhsub_V_16B_8H_4S([Values(0u)] uint rd, + public void Uhsub_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3566,7 +3614,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQADD <V><d>, <V><n>, <V><m>")] - public void Uqadd_S_B_H_S_D([Values(0u)] uint rd, + public void Uqadd_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1B1H1S1D_))] ulong z, @@ -3588,7 +3636,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uqadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Uqadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3610,7 +3658,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Uqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3632,7 +3680,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQSUB <V><d>, <V><n>, <V><m>")] - public void Uqsub_S_B_H_S_D([Values(0u)] uint rd, + public void Uqsub_S_B_H_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1B1H1S1D_))] ulong z, @@ -3654,7 +3702,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uqsub_V_8B_4H_2S([Values(0u)] uint rd, + public void Uqsub_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3676,7 +3724,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Uqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3698,7 +3746,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("URHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Urhadd_V_8B_4H_2S([Values(0u)] uint rd, + public void Urhadd_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3720,7 +3768,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("URHADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Urhadd_V_16B_8H_4S([Values(0u)] uint rd, + public void Urhadd_V_16B_8H_4S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3742,7 +3790,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USUBL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Usubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + public void Usubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3764,7 +3812,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USUBL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Usubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + public void Usubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3786,12 +3834,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { uint opcode = 0x2E203000; // USUBW V0.8H, V0.8H, V0.8B @@ -3808,12 +3856,12 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Usubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + public void Usubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, - [ValueSource(nameof(_4H2S1D_))] [Random(RndCnt)] ulong a, - [ValueSource(nameof(_8B4H2S_))] [Random(RndCnt)] ulong b, + [ValueSource(nameof(_4H2S1D_))][Random(RndCnt)] ulong a, + [ValueSource(nameof(_8B4H2S_))][Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { uint opcode = 0x6E203000; // USUBW2 V0.8H, V0.8H, V0.16B @@ -3830,7 +3878,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp1_V_8B_4H_2S([Values(0u)] uint rd, + public void Uzp1_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3852,7 +3900,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp1_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Uzp1_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3874,7 +3922,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp2_V_8B_4H_2S([Values(0u)] uint rd, + public void Uzp2_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3896,7 +3944,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp2_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Uzp2_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3918,7 +3966,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip1_V_8B_4H_2S([Values(0u)] uint rd, + public void Zip1_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3940,7 +3988,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip1_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Zip1_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -3962,7 +4010,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip2_V_8B_4H_2S([Values(0u)] uint rd, + public void Zip2_V_8B_4H_2S([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S_))] ulong z, @@ -3984,7 +4032,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip2_V_16B_8H_4S_2D([Values(0u)] uint rd, + public void Zip2_V_16B_8H_4S_2D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_8B4H2S1D_))] ulong z, @@ -4006,4 +4054,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs index 603e2a559..1db90bfa4 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu { #if SimdReg32 -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _V_Add_Sub_Long_Wide_I_() { return new[] @@ -20,7 +20,7 @@ namespace Ryujinx.Tests.Cpu 0xf2800000u, // VADDL.S8 Q0, D0, D0 0xf2800100u, // VADDW.S8 Q0, Q0, D0 0xf2800200u, // VSUBL.S8 Q0, D0, D0 - 0xf2800300u // VSUBW.S8 Q0, Q0, D0 + 0xf2800300u, // VSUBW.S8 Q0, Q0, D0 }; } @@ -31,7 +31,7 @@ namespace Ryujinx.Tests.Cpu 0xEEA00A00u, // VFMA. F32 S0, S0, S0 0xEEA00A40u, // VFMS. F32 S0, S0, S0 0xEE900A40u, // VFNMA.F32 S0, S0, S0 - 0xEE900A00u // VFNMS.F32 S0, S0, S0 + 0xEE900A00u, // VFNMS.F32 S0, S0, S0 }; } @@ -42,7 +42,7 @@ namespace Ryujinx.Tests.Cpu 0xEEA00B00u, // VFMA. F64 D0, D0, D0 0xEEA00B40u, // VFMS. F64 D0, D0, D0 0xEE900B40u, // VFNMA.F64 D0, D0, D0 - 0xEE900B00u // VFNMS.F64 D0, D0, D0 + 0xEE900B00u, // VFNMS.F64 D0, D0, D0 }; } @@ -51,7 +51,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0xF2000C10u, // VFMA.F32 D0, D0, D0 - 0xF2200C10u // VFMS.F32 D0, D0, D0 + 0xF2200C10u, // VFMS.F32 D0, D0, D0 }; } @@ -62,7 +62,7 @@ namespace Ryujinx.Tests.Cpu 0xEE000A00u, // VMLA. F32 S0, S0, S0 0xEE000A40u, // VMLS. F32 S0, S0, S0 0xEE100A40u, // VNMLA.F32 S0, S0, S0 - 0xEE100A00u // VNMLS.F32 S0, S0, S0 + 0xEE100A00u, // VNMLS.F32 S0, S0, S0 }; } @@ -73,7 +73,7 @@ namespace Ryujinx.Tests.Cpu 0xEE000B00u, // VMLA. F64 D0, D0, D0 0xEE000B40u, // VMLS. F64 D0, D0, D0 0xEE100B40u, // VNMLA.F64 D0, D0, D0 - 0xEE100B00u // VNMLS.F64 D0, D0, D0 + 0xEE100B00u, // VNMLS.F64 D0, D0, D0 }; } @@ -82,7 +82,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0xf2800800u, // VMLAL.S8 Q0, D0, D0 - 0xf2800a00u // VMLSL.S8 Q0, D0, D0 + 0xf2800a00u, // VMLSL.S8 Q0, D0, D0 }; } @@ -92,7 +92,7 @@ namespace Ryujinx.Tests.Cpu { 0xf3000d00u, // VPADD.F32 D0, D0, D0 0xf3000f00u, // VPMAX.F32 D0, D0, D0 - 0xf3200f00u // VPMIN.F32 D0, D0, D0 + 0xf3200f00u, // VPMIN.F32 D0, D0, D0 }; } @@ -100,7 +100,7 @@ namespace Ryujinx.Tests.Cpu { return new[] { - 0xf2000b10u // VPADD.I8 D0, D0, D0 + 0xf2000b10u, // VPADD.I8 D0, D0, D0 }; } @@ -119,26 +119,30 @@ namespace Ryujinx.Tests.Cpu return new[] { 0xf2000050u, // VQADD.S8 Q0, Q0, Q0 - 0xf2000250u // VQSUB.S8 Q0, Q0, Q0 + 0xf2000250u, // VQSUB.S8 Q0, Q0, Q0 }; } -#endregion + #endregion -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _8B1D_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B4H2S1D_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<ulong> _1S_F_() @@ -152,19 +156,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -194,19 +198,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -235,19 +239,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -264,13 +268,13 @@ namespace Ryujinx.Tests.Cpu yield return rnd2; } } -#endregion + #endregion private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; [Test, Pairwise, Description("SHA256H.32 <Qd>, <Qn>, <Qm>")] public void Sha256h_V([Values(0xF3000C40u)] uint opcode, @@ -288,7 +292,7 @@ namespace Ryujinx.Tests.Cpu { opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(z0, z1); V128 v1 = MakeVectorE0E1(a0, a1); @@ -322,7 +326,7 @@ namespace Ryujinx.Tests.Cpu { opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(z0, z1); V128 v1 = MakeVectorE0E1(a0, a1); @@ -356,7 +360,7 @@ namespace Ryujinx.Tests.Cpu { opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(z0, z1); V128 v1 = MakeVectorE0E1(a0, a1); @@ -396,7 +400,7 @@ namespace Ryujinx.Tests.Cpu rd <<= 1; } - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -425,12 +429,14 @@ namespace Ryujinx.Tests.Cpu opcode |= 1 << 24; } - rd >>= 1; rd <<= 1; - rn >>= 1; rn <<= 1; + rd >>= 1; + rd <<= 1; + rn >>= 1; + rn <<= 1; opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= (size & 0x3) << 20; @@ -455,12 +461,12 @@ namespace Ryujinx.Tests.Cpu if (size == 3) { - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); } else { - opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); + opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); } @@ -480,7 +486,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Nzcv); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void Vfma_Vfms_Vfnma_Vfnms_S_F32([ValueSource(nameof(_Vfma_Vfms_Vfnma_Vfnms_S_F32_))] uint opcode, [Values(0u, 1u, 2u, 3u)] uint rd, [Values(0u, 1u, 2u, 3u)] uint rn, @@ -491,8 +499,8 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_1S_F_))] ulong s3) { opcode |= (((rd & 0x1) << 22) | (rd & 0x1e) << 11); - opcode |= (((rn & 0x1) << 7) | (rn & 0x1e) << 15); - opcode |= (((rm & 0x1) << 5) | (rm & 0x1e) >> 1); + opcode |= (((rn & 0x1) << 7) | (rn & 0x1e) << 15); + opcode |= (((rm & 0x1) << 5) | (rm & 0x1e) >> 1); V128 v0 = MakeVectorE0E1E2E3((uint)s0, (uint)s1, (uint)s2, (uint)s3); @@ -501,7 +509,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void Vfma_Vfms_Vfnma_Vfnms_S_F64([ValueSource(nameof(_Vfma_Vfms_Vfnma_Vfnms_S_F64_))] uint opcode, [Values(0u, 1u)] uint rd, [Values(0u, 1u)] uint rn, @@ -510,8 +520,8 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_1D_F_))] ulong d1) { opcode |= (((rd & 0x10) << 18) | (rd & 0xf) << 12); - opcode |= (((rn & 0x10) << 3) | (rn & 0xf) << 16); - opcode |= (((rm & 0x10) << 1) | (rm & 0xf) << 0); + opcode |= (((rn & 0x10) << 3) | (rn & 0xf) << 16); + opcode |= (((rm & 0x10) << 1) | (rm & 0xf) << 0); V128 v0 = MakeVectorE0E1(d0, d1); @@ -520,7 +530,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void Vfma_Vfms_V_F32([ValueSource(nameof(_Vfma_Vfms_V_F32_))] uint opcode, [Values(0u, 1u, 2u, 3u)] uint rd, [Values(0u, 1u, 2u, 3u)] uint rn, @@ -535,14 +547,17 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; - rn >>= 1; rn <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rn >>= 1; + rn <<= 1; + rm >>= 1; + rm <<= 1; } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); V128 v0 = MakeVectorE0E1(d0, d1); V128 v1 = MakeVectorE0E1(d2, d3); @@ -552,7 +567,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void Vmla_Vmls_Vnmla_Vnmls_S_F32([ValueSource(nameof(_Vmla_Vmls_Vnmla_Vnmls_S_F32_))] uint opcode, [Values(0u, 1u, 2u, 3u)] uint rd, [Values(0u, 1u, 2u, 3u)] uint rn, @@ -563,8 +579,8 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_1S_F_))] ulong s3) { opcode |= (((rd & 0x1) << 22) | (rd & 0x1e) << 11); - opcode |= (((rn & 0x1) << 7) | (rn & 0x1e) << 15); - opcode |= (((rm & 0x1) << 5) | (rm & 0x1e) >> 1); + opcode |= (((rn & 0x1) << 7) | (rn & 0x1e) << 15); + opcode |= (((rm & 0x1) << 5) | (rm & 0x1e) >> 1); V128 v0 = MakeVectorE0E1E2E3((uint)s0, (uint)s1, (uint)s2, (uint)s3); @@ -573,7 +589,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void Vmla_Vmls_Vnmla_Vnmls_S_F64([ValueSource(nameof(_Vmla_Vmls_Vnmla_Vnmls_S_F64_))] uint opcode, [Values(0u, 1u)] uint rd, [Values(0u, 1u)] uint rn, @@ -582,8 +599,8 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_1D_F_))] ulong d1) { opcode |= (((rd & 0x10) << 18) | (rd & 0xf) << 12); - opcode |= (((rn & 0x10) << 3) | (rn & 0xf) << 16); - opcode |= (((rm & 0x10) << 1) | (rm & 0xf) << 0); + opcode |= (((rn & 0x10) << 3) | (rn & 0xf) << 16); + opcode |= (((rm & 0x10) << 1) | (rm & 0xf) << 0); V128 v0 = MakeVectorE0E1(d0, d1); @@ -603,7 +620,7 @@ namespace Ryujinx.Tests.Cpu [Random(RndCnt)] ulong b, [Values] bool u) { - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -636,7 +653,7 @@ namespace Ryujinx.Tests.Cpu { uint opcode = 0xf2800c00u; // VMULL.S8 Q0, D0, D0 - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -678,11 +695,12 @@ namespace Ryujinx.Tests.Cpu uint opcode = 0xf2800e00u; // VMULL.P8 Q0, D0, D0 - rd >>= 1; rd <<= 1; + rd >>= 1; + rd <<= 1; opcode |= (((rd & 0x10) << 18) | (rd & 0xf) << 12); - opcode |= (((rn & 0x10) << 3) | (rn & 0xf) << 16); - opcode |= (((rm & 0x10) << 1) | (rm & 0xf) << 0); + opcode |= (((rn & 0x10) << 3) | (rn & 0xf) << 16); + opcode |= (((rm & 0x10) << 1) | (rm & 0xf) << 0); opcode |= (size & 0x3) << 20; @@ -723,7 +741,7 @@ namespace Ryujinx.Tests.Cpu opcode |= 1 << 24; } - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -751,7 +769,7 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_2S_F_))] ulong b0, [ValueSource(nameof(_2S_F_))] ulong b1) { - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -774,7 +792,7 @@ namespace Ryujinx.Tests.Cpu [Random(RndCnt)] ulong a, [Random(RndCnt)] ulong b) { - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -805,7 +823,7 @@ namespace Ryujinx.Tests.Cpu opcode |= 1 << 24; } - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -836,13 +854,16 @@ namespace Ryujinx.Tests.Cpu opcode |= 1 << 24; } - rd >>= 1; rd <<= 1; - rn >>= 1; rn <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rn >>= 1; + rn <<= 1; + rm >>= 1; + rm <<= 1; opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= (size & 0x3) << 20; @@ -864,15 +885,18 @@ namespace Ryujinx.Tests.Cpu [ValueSource(nameof(_8B4H2S1D_))] ulong b, [Values(1u, 2u)] uint size) // <S16, S32> { - rd >>= 1; rd <<= 1; - rn >>= 1; rn <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rn >>= 1; + rn <<= 1; + rm >>= 1; + rm <<= 1; uint opcode = 0xf2100b40u & ~(3u << 20); // VQDMULH.S16 Q0, Q0, Q0 opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= (size & 0x3) << 20; @@ -886,4 +910,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs index 7f5f6d17c..23c6961f9 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs @@ -10,21 +10,25 @@ namespace Ryujinx.Tests.Cpu { #if SimdRegElem -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _2S_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_4H_8H_() { return new[] @@ -33,7 +37,7 @@ namespace Ryujinx.Tests.Cpu 0x2F404000u, // MLS V0.4H, V0.4H, V0.H[0] 0x0F408000u, // MUL V0.4H, V0.4H, V0.H[0] 0x0F40C000u, // SQDMULH V0.4H, V0.4H, V0.H[0] - 0x0F40D000u // SQRDMULH V0.4H, V0.4H, V0.H[0] + 0x0F40D000u, // SQRDMULH V0.4H, V0.4H, V0.H[0] }; } @@ -45,7 +49,7 @@ namespace Ryujinx.Tests.Cpu 0x2F804000u, // MLS V0.2S, V0.2S, V0.S[0] 0x0F808000u, // MUL V0.2S, V0.2S, V0.S[0] 0x0F80C000u, // SQDMULH V0.2S, V0.2S, V0.S[0] - 0x0F80D000u // SQRDMULH V0.2S, V0.2S, V0.S[0] + 0x0F80D000u, // SQRDMULH V0.2S, V0.2S, V0.S[0] }; } @@ -58,7 +62,7 @@ namespace Ryujinx.Tests.Cpu 0x0F40A000u, // SMULL V0.4S, V0.4H, V0.H[0] 0x2F402000u, // UMLAL V0.4S, V0.4H, V0.H[0] 0x2F406000u, // UMLSL V0.4S, V0.4H, V0.H[0] - 0x2F40A000u // UMULL V0.4S, V0.4H, V0.H[0] + 0x2F40A000u, // UMULL V0.4S, V0.4H, V0.H[0] }; } @@ -71,15 +75,15 @@ namespace Ryujinx.Tests.Cpu 0x0F80A000u, // SMULL V0.2D, V0.2S, V0.S[0] 0x2F802000u, // UMLAL V0.2D, V0.2S, V0.S[0] 0x2F806000u, // UMLSL V0.2D, V0.2S, V0.S[0] - 0x2F80A000u // UMULL V0.2D, V0.2S, V0.S[0] + 0x2F80A000u, // UMULL V0.2D, V0.2S, V0.S[0] }; } -#endregion + #endregion [Test, Pairwise] public void Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_4H_8H([ValueSource(nameof(_Mla_Mls_Mul_Sqdmulh_Sqrdmulh_Ve_4H_8H_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H_))] ulong z, @@ -134,7 +138,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_4H_))] ulong z, @@ -162,7 +166,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D([ValueSource(nameof(_SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_))] ulong z, @@ -188,4 +192,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs index 31b236041..1b670da76 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Cpu { #if SimdRegElemF -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static IEnumerable<ulong> _1S_F_() { yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) @@ -23,19 +23,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x00000000007FFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x0000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0x00000000FF800000ul; // -Infinity yield return 0x000000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) @@ -65,19 +65,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -106,19 +106,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -135,15 +135,15 @@ namespace Ryujinx.Tests.Cpu yield return rnd2; } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _F_Mla_Mls_Se_S_() { return new[] { 0x5F821020u, // FMLA S0, S1, V2.S[0] - 0x5F825020u // FMLS S0, S1, V2.S[0] + 0x5F825020u, // FMLS S0, S1, V2.S[0] }; } @@ -152,7 +152,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5FC21020u, // FMLA D0, D1, V2.D[0] - 0x5FC25020u // FMLS D0, D1, V2.D[0] + 0x5FC25020u, // FMLS D0, D1, V2.D[0] }; } @@ -161,7 +161,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F801000u, // FMLA V0.2S, V0.2S, V0.S[0] - 0x0F805000u // FMLS V0.2S, V0.2S, V0.S[0] + 0x0F805000u, // FMLS V0.2S, V0.2S, V0.S[0] }; } @@ -170,7 +170,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4FC01000u, // FMLA V0.2D, V0.2D, V0.D[0] - 0x4FC05000u // FMLS V0.2D, V0.2D, V0.D[0] + 0x4FC05000u, // FMLS V0.2D, V0.2D, V0.D[0] }; } @@ -179,7 +179,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5F829020u, // FMUL S0, S1, V2.S[0] - 0x7F829020u // FMULX S0, S1, V2.S[0] + 0x7F829020u, // FMULX S0, S1, V2.S[0] }; } @@ -188,7 +188,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5FC29020u, // FMUL D0, D1, V2.D[0] - 0x7FC29020u // FMULX D0, D1, V2.D[0] + 0x7FC29020u, // FMULX D0, D1, V2.D[0] }; } @@ -197,7 +197,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F809000u, // FMUL V0.2S, V0.2S, V0.S[0] - 0x2F809000u // FMULX V0.2S, V0.2S, V0.S[0] + 0x2F809000u, // FMULX V0.2S, V0.2S, V0.S[0] }; } @@ -206,18 +206,20 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4FC09000u, // FMUL V0.2D, V0.2D, V0.D[0] - 0x6FC09000u // FMULX V0.2D, V0.2D, V0.D[0] + 0x6FC09000u, // FMULX V0.2D, V0.2D, V0.D[0] }; } -#endregion + #endregion private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Mla_Mls_Se_S([ValueSource(nameof(_F_Mla_Mls_Se_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong z, [ValueSource(nameof(_1S_F_))] ulong a, @@ -243,7 +245,9 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Mla_Mls_Se_D([ValueSource(nameof(_F_Mla_Mls_Se_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong z, [ValueSource(nameof(_1D_F_))] ulong a, @@ -268,9 +272,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Mla_Mls_Ve_2S_4S([ValueSource(nameof(_F_Mla_Mls_Ve_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_F_))] ulong z, @@ -300,9 +306,11 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); } - [Test, Pairwise] [Explicit] // Fused. + // Fused. + [Test, Pairwise] + [Explicit] public void F_Mla_Mls_Ve_2D([ValueSource(nameof(_F_Mla_Mls_Ve_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_F_))] ulong z, @@ -329,7 +337,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mul_Mulx_Se_S([ValueSource(nameof(_F_Mul_Mulx_Se_S_))] uint opcodes, [ValueSource(nameof(_1S_F_))] ulong a, [ValueSource(nameof(_2S_F_))] ulong b, @@ -355,7 +364,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mul_Mulx_Se_D([ValueSource(nameof(_F_Mul_Mulx_Se_D_))] uint opcodes, [ValueSource(nameof(_1D_F_))] ulong a, [ValueSource(nameof(_1D_F_))] ulong b, @@ -380,9 +390,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mul_Mulx_Ve_2S_4S([ValueSource(nameof(_F_Mul_Mulx_Ve_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_2S_F_))] ulong z, @@ -412,9 +423,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Mul_Mulx_Ve_2D([ValueSource(nameof(_F_Mul_Mulx_Ve_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [Values(2u, 0u)] uint rm, [ValueSource(nameof(_1D_F_))] ulong z, diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs index 4a49814ef..fbac54c8c 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs @@ -12,41 +12,53 @@ namespace Ryujinx.Tests.Cpu { #if SimdShImm -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _1D_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _1H_() { - return new[] { 0x0000000000000000ul, 0x0000000000007FFFul, - 0x0000000000008000ul, 0x000000000000FFFFul }; + return new[] { + 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul, + }; } private static ulong[] _1S_() { - return new[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, - 0x0000000080000000ul, 0x00000000FFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + }; } private static ulong[] _2S_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, - 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, - 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<ulong> _2S_F_W_() @@ -73,19 +85,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x007FFFFF007FFFFFul; // +Max Subnormal yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000080000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFF800000FF800000ul; // -Infinity yield return 0x7F8000007F800000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) @@ -133,19 +145,19 @@ namespace Ryujinx.Tests.Cpu yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) - if (!NoZeros) + if (!_noZeros) { yield return 0x8000000000000000ul; // -Zero yield return 0x0000000000000000ul; // +Zero } - if (!NoInfs) + if (!_noInfs) { yield return 0xFFF0000000000000ul; // -Infinity yield return 0x7FF0000000000000ul; // +Infinity } - if (!NoNaNs) + if (!_noNaNs) { yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) @@ -170,15 +182,15 @@ namespace Ryujinx.Tests.Cpu yield return rnd4; } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _F_Cvt_Z_SU_V_Fixed_2S_4S_() { return new[] { 0x0F20FC00u, // FCVTZS V0.2S, V0.2S, #32 - 0x2F20FC00u // FCVTZU V0.2S, V0.2S, #32 + 0x2F20FC00u, // FCVTZU V0.2S, V0.2S, #32 }; } @@ -187,7 +199,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4F40FC00u, // FCVTZS V0.2D, V0.2D, #64 - 0x6F40FC00u // FCVTZU V0.2D, V0.2D, #64 + 0x6F40FC00u, // FCVTZU V0.2D, V0.2D, #64 }; } @@ -196,7 +208,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5F20E420u, // SCVTF S0, S1, #32 - 0x7F20E420u // UCVTF S0, S1, #32 + 0x7F20E420u, // UCVTF S0, S1, #32 }; } @@ -205,7 +217,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5F40E420u, // SCVTF D0, D1, #64 - 0x7F40E420u // UCVTF D0, D1, #64 + 0x7F40E420u, // UCVTF D0, D1, #64 }; } @@ -214,7 +226,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F20E400u, // SCVTF V0.2S, V0.2S, #32 - 0x2F20E400u // UCVTF V0.2S, V0.2S, #32 + 0x2F20E400u, // UCVTF V0.2S, V0.2S, #32 }; } @@ -223,7 +235,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4F40E400u, // SCVTF V0.2D, V0.2D, #64 - 0x6F40E400u // UCVTF V0.2D, V0.2D, #64 + 0x6F40E400u, // UCVTF V0.2D, V0.2D, #64 }; } @@ -232,7 +244,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x5F405400u, // SHL D0, D0, #0 - 0x7F405400u // SLI D0, D0, #0 + 0x7F405400u, // SLI D0, D0, #0 }; } @@ -241,7 +253,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F085400u, // SHL V0.8B, V0.8B, #0 - 0x2F085400u // SLI V0.8B, V0.8B, #0 + 0x2F085400u, // SLI V0.8B, V0.8B, #0 }; } @@ -250,7 +262,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F105400u, // SHL V0.4H, V0.4H, #0 - 0x2F105400u // SLI V0.4H, V0.4H, #0 + 0x2F105400u, // SLI V0.4H, V0.4H, #0 }; } @@ -259,7 +271,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F205400u, // SHL V0.2S, V0.2S, #0 - 0x2F205400u // SLI V0.2S, V0.2S, #0 + 0x2F205400u, // SLI V0.2S, V0.2S, #0 }; } @@ -268,7 +280,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x4F405400u, // SHL V0.2D, V0.2D, #0 - 0x6F405400u // SLI V0.2D, V0.2D, #0 + 0x6F405400u, // SLI V0.2D, V0.2D, #0 }; } @@ -277,7 +289,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F08A400u, // SSHLL V0.8H, V0.8B, #0 - 0x2F08A400u // USHLL V0.8H, V0.8B, #0 + 0x2F08A400u, // USHLL V0.8H, V0.8B, #0 }; } @@ -286,7 +298,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F10A400u, // SSHLL V0.4S, V0.4H, #0 - 0x2F10A400u // USHLL V0.4S, V0.4H, #0 + 0x2F10A400u, // USHLL V0.4S, V0.4H, #0 }; } @@ -295,7 +307,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F20A400u, // SSHLL V0.2D, V0.2S, #0 - 0x2F20A400u // USHLL V0.2D, V0.2S, #0 + 0x2F20A400u, // USHLL V0.2D, V0.2S, #0 }; } @@ -311,7 +323,7 @@ namespace Ryujinx.Tests.Cpu 0x7F402400u, // URSHR D0, D0, #64 0x7F403400u, // URSRA D0, D0, #64 0x7F400400u, // USHR D0, D0, #64 - 0x7F401400u // USRA D0, D0, #64 + 0x7F401400u, // USRA D0, D0, #64 }; } @@ -327,7 +339,7 @@ namespace Ryujinx.Tests.Cpu 0x2F082400u, // URSHR V0.8B, V0.8B, #8 0x2F083400u, // URSRA V0.8B, V0.8B, #8 0x2F080400u, // USHR V0.8B, V0.8B, #8 - 0x2F081400u // USRA V0.8B, V0.8B, #8 + 0x2F081400u, // USRA V0.8B, V0.8B, #8 }; } @@ -343,7 +355,7 @@ namespace Ryujinx.Tests.Cpu 0x2F102400u, // URSHR V0.4H, V0.4H, #16 0x2F103400u, // URSRA V0.4H, V0.4H, #16 0x2F100400u, // USHR V0.4H, V0.4H, #16 - 0x2F101400u // USRA V0.4H, V0.4H, #16 + 0x2F101400u, // USRA V0.4H, V0.4H, #16 }; } @@ -359,7 +371,7 @@ namespace Ryujinx.Tests.Cpu 0x2F202400u, // URSHR V0.2S, V0.2S, #32 0x2F203400u, // URSRA V0.2S, V0.2S, #32 0x2F200400u, // USHR V0.2S, V0.2S, #32 - 0x2F201400u // USRA V0.2S, V0.2S, #32 + 0x2F201400u, // USRA V0.2S, V0.2S, #32 }; } @@ -375,7 +387,7 @@ namespace Ryujinx.Tests.Cpu 0x6F402400u, // URSHR V0.2D, V0.2D, #64 0x6F403400u, // URSRA V0.2D, V0.2D, #64 0x6F400400u, // USHR V0.2D, V0.2D, #64 - 0x6F401400u // USRA V0.2D, V0.2D, #64 + 0x6F401400u, // USRA V0.2D, V0.2D, #64 }; } @@ -384,7 +396,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F088C00u, // RSHRN V0.8B, V0.8H, #8 - 0x0F088400u // SHRN V0.8B, V0.8H, #8 + 0x0F088400u, // SHRN V0.8B, V0.8H, #8 }; } @@ -393,7 +405,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F108C00u, // RSHRN V0.4H, V0.4S, #16 - 0x0F108400u // SHRN V0.4H, V0.4S, #16 + 0x0F108400u, // SHRN V0.4H, V0.4S, #16 }; } @@ -402,7 +414,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0F208C00u, // RSHRN V0.2S, V0.2D, #32 - 0x0F208400u // SHRN V0.2S, V0.2D, #32 + 0x0F208400u, // SHRN V0.2S, V0.2D, #32 }; } @@ -415,7 +427,7 @@ namespace Ryujinx.Tests.Cpu 0x7F088C00u, // SQRSHRUN B0, H0, #8 0x5F089400u, // SQSHRN B0, H0, #8 0x7F089400u, // UQSHRN B0, H0, #8 - 0x7F088400u // SQSHRUN B0, H0, #8 + 0x7F088400u, // SQSHRUN B0, H0, #8 }; } @@ -428,7 +440,7 @@ namespace Ryujinx.Tests.Cpu 0x7F108C00u, // SQRSHRUN H0, S0, #16 0x5F109400u, // SQSHRN H0, S0, #16 0x7F109400u, // UQSHRN H0, S0, #16 - 0x7F108400u // SQSHRUN H0, S0, #16 + 0x7F108400u, // SQSHRUN H0, S0, #16 }; } @@ -441,7 +453,7 @@ namespace Ryujinx.Tests.Cpu 0x7F208C00u, // SQRSHRUN S0, D0, #32 0x5F209400u, // SQSHRN S0, D0, #32 0x7F209400u, // UQSHRN S0, D0, #32 - 0x7F208400u // SQSHRUN S0, D0, #32 + 0x7F208400u, // SQSHRUN S0, D0, #32 }; } @@ -454,7 +466,7 @@ namespace Ryujinx.Tests.Cpu 0x2F088C00u, // SQRSHRUN V0.8B, V0.8H, #8 0x0F089400u, // SQSHRN V0.8B, V0.8H, #8 0x2F089400u, // UQSHRN V0.8B, V0.8H, #8 - 0x2F088400u // SQSHRUN V0.8B, V0.8H, #8 + 0x2F088400u, // SQSHRUN V0.8B, V0.8H, #8 }; } @@ -467,7 +479,7 @@ namespace Ryujinx.Tests.Cpu 0x2F108C00u, // SQRSHRUN V0.4H, V0.4S, #16 0x0F109400u, // SQSHRN V0.4H, V0.4S, #16 0x2F109400u, // UQSHRN V0.4H, V0.4S, #16 - 0x2F108400u // SQSHRUN V0.4H, V0.4S, #16 + 0x2F108400u, // SQSHRUN V0.4H, V0.4S, #16 }; } @@ -480,20 +492,21 @@ namespace Ryujinx.Tests.Cpu 0x2F208C00u, // SQRSHRUN V0.2S, V0.2D, #32 0x0F209400u, // SQSHRN V0.2S, V0.2D, #32 0x2F209400u, // UQSHRN V0.2S, V0.2D, #32 - 0x2F208400u // SQSHRUN V0.2S, V0.2D, #32 + 0x2F208400u, // SQSHRUN V0.2S, V0.2D, #32 }; } -#endregion + #endregion - private const int RndCnt = 2; + private const int RndCnt = 2; - private static readonly bool NoZeros = false; - private static readonly bool NoInfs = false; - private static readonly bool NoNaNs = false; + private static readonly bool _noZeros = false; + private static readonly bool _noInfs = false; + private static readonly bool _noNaNs = false; - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_Z_SU_V_Fixed_2S_4S([ValueSource(nameof(_F_Cvt_Z_SU_V_Fixed_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_F_W_))] ulong z, [ValueSource(nameof(_2S_F_W_))] ulong a, @@ -514,9 +527,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void F_Cvt_Z_SU_V_Fixed_2D([ValueSource(nameof(_F_Cvt_Z_SU_V_Fixed_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_F_X_))] ulong z, [ValueSource(nameof(_1D_F_X_))] ulong a, @@ -535,7 +549,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_S_Fixed_S([ValueSource(nameof(_SU_Cvt_F_S_Fixed_S_))] uint opcodes, [ValueSource(nameof(_1S_))] ulong a, [Values(1u, 32u)] uint fBits) @@ -553,7 +568,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_S_Fixed_D([ValueSource(nameof(_SU_Cvt_F_S_Fixed_D_))] uint opcodes, [ValueSource(nameof(_1D_))] ulong a, [Values(1u, 64u)] uint fBits) @@ -571,9 +587,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_V_Fixed_2S_4S([ValueSource(nameof(_SU_Cvt_F_V_Fixed_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -594,9 +611,10 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] [Explicit] + [Test, Pairwise] + [Explicit] public void SU_Cvt_F_V_Fixed_2D([ValueSource(nameof(_SU_Cvt_F_V_Fixed_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -617,7 +635,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Shl_Sli_S_D([ValueSource(nameof(_Shl_Sli_S_D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -638,7 +656,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Shl_Sli_V_8B_16B([ValueSource(nameof(_Shl_Sli_V_8B_16B_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a, @@ -661,7 +679,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Shl_Sli_V_4H_8H([ValueSource(nameof(_Shl_Sli_V_4H_8H_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_4H_))] ulong a, @@ -684,7 +702,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Shl_Sli_V_2S_4S([ValueSource(nameof(_Shl_Sli_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -707,7 +725,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Shl_Sli_V_2D([ValueSource(nameof(_Shl_Sli_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -728,7 +746,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Shll_V_8B8H_16B8H([ValueSource(nameof(_SU_Shll_V_8B8H_16B8H_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a, @@ -751,7 +769,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Shll_V_4H4S_8H4S([ValueSource(nameof(_SU_Shll_V_4H4S_8H4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_4H_))] ulong a, @@ -774,7 +792,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void SU_Shll_V_2S2D_4S2D([ValueSource(nameof(_SU_Shll_V_2S2D_4S2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -797,7 +815,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImm_Sri_S_D([ValueSource(nameof(_ShrImm_Sri_S_D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -818,7 +836,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImm_Sri_V_8B_16B([ValueSource(nameof(_ShrImm_Sri_V_8B_16B_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong a, @@ -841,7 +859,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImm_Sri_V_4H_8H([ValueSource(nameof(_ShrImm_Sri_V_4H_8H_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_4H_))] ulong a, @@ -864,7 +882,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImm_Sri_V_2S_4S([ValueSource(nameof(_ShrImm_Sri_V_2S_4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -887,7 +905,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImm_Sri_V_2D([ValueSource(nameof(_ShrImm_Sri_V_2D_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -908,7 +926,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmNarrow_V_8H8B_8H16B([ValueSource(nameof(_ShrImmNarrow_V_8H8B_8H16B_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_4H_))] ulong a, @@ -931,7 +949,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmNarrow_V_4S4H_4S8H([ValueSource(nameof(_ShrImmNarrow_V_4S4H_4S8H_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -954,7 +972,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmNarrow_V_2D2S_2D4S([ValueSource(nameof(_ShrImmNarrow_V_2D2S_2D4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -977,7 +995,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmSaturatingNarrow_S_HB([ValueSource(nameof(_ShrImmSaturatingNarrow_S_HB_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1H_))] ulong z, [ValueSource(nameof(_1H_))] ulong a, @@ -998,7 +1016,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmSaturatingNarrow_S_SH([ValueSource(nameof(_ShrImmSaturatingNarrow_S_SH_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1S_))] ulong z, [ValueSource(nameof(_1S_))] ulong a, @@ -1019,7 +1037,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmSaturatingNarrow_S_DS([ValueSource(nameof(_ShrImmSaturatingNarrow_S_DS_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -1040,7 +1058,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmSaturatingNarrow_V_8H8B_8H16B([ValueSource(nameof(_ShrImmSaturatingNarrow_V_8H8B_8H16B_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_4H_))] ulong z, [ValueSource(nameof(_4H_))] ulong a, @@ -1063,7 +1081,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmSaturatingNarrow_V_4S4H_4S8H([ValueSource(nameof(_ShrImmSaturatingNarrow_V_4S4H_4S8H_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_2S_))] ulong z, [ValueSource(nameof(_2S_))] ulong a, @@ -1086,7 +1104,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void ShrImmSaturatingNarrow_V_2D2S_2D4S([ValueSource(nameof(_ShrImmSaturatingNarrow_V_2D2S_2D4S_))] uint opcodes, - [Values(0u)] uint rd, + [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource(nameof(_1D_))] ulong z, [ValueSource(nameof(_1D_))] ulong a, @@ -1108,4 +1126,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs index e7fad89f9..f81d22c21 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs @@ -10,30 +10,41 @@ namespace Ryujinx.Tests.Cpu { #if SimdShImm32 -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _1D_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _2S_() { - return new[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] + { + 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _4H_() { - return new[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] + { + 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static ulong[] _8B_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] + { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul, + }; } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _Vshr_Imm_SU8_() { return new[] @@ -41,7 +52,7 @@ namespace Ryujinx.Tests.Cpu 0xf2880010u, // VSHR.S8 D0, D0, #8 0xf2880110u, // VSRA.S8 D0, D0, #8 0xf2880210u, // VRSHR.S8 D0, D0, #8 - 0xf2880310u // VRSRA.S8 D0, D0, #8 + 0xf2880310u, // VRSRA.S8 D0, D0, #8 }; } @@ -52,7 +63,7 @@ namespace Ryujinx.Tests.Cpu 0xf2900010u, // VSHR.S16 D0, D0, #16 0xf2900110u, // VSRA.S16 D0, D0, #16 0xf2900210u, // VRSHR.S16 D0, D0, #16 - 0xf2900310u // VRSRA.S16 D0, D0, #16 + 0xf2900310u, // VRSRA.S16 D0, D0, #16 }; } @@ -63,7 +74,7 @@ namespace Ryujinx.Tests.Cpu 0xf2a00010u, // VSHR.S32 D0, D0, #32 0xf2a00110u, // VSRA.S32 D0, D0, #32 0xf2a00210u, // VRSHR.S32 D0, D0, #32 - 0xf2a00310u // VRSRA.S32 D0, D0, #32 + 0xf2a00310u, // VRSRA.S32 D0, D0, #32 }; } @@ -73,7 +84,7 @@ namespace Ryujinx.Tests.Cpu { 0xf2800190u, // VSRA.S64 D0, D0, #64 0xf2800290u, // VRSHR.S64 D0, D0, #64 - 0xf2800090u // VSHR.S64 D0, D0, #64 + 0xf2800090u, // VSHR.S64 D0, D0, #64 }; } @@ -83,7 +94,7 @@ namespace Ryujinx.Tests.Cpu { 0xf2800910u, // VORR.I16 D0, #0 (immediate value changes it into QSHRN) 0xf2800950u, // VORR.I16 Q0, #0 (immediate value changes it into QRSHRN) - 0xf2800850u // VMOV.I16 Q0, #0 (immediate value changes it into RSHRN) + 0xf2800850u, // VMOV.I16 Q0, #0 (immediate value changes it into RSHRN) }; } @@ -92,10 +103,10 @@ namespace Ryujinx.Tests.Cpu return new[] { 0xf3800810u, // VMOV.I16 D0, #0x80 (immediate value changes it into QSHRUN) - 0xf3800850u // VMOV.I16 Q0, #0x80 (immediate value changes it into QRSHRUN) + 0xf3800850u, // VMOV.I16 Q0, #0x80 (immediate value changes it into QRSHRUN) }; } -#endregion + #endregion private const int RndCnt = 2; private const int RndCntShiftImm = 2; @@ -171,12 +182,14 @@ namespace Ryujinx.Tests.Cpu { opcode |= 1 << 6; - rd >>= 1; rd <<= 1; - rm >>= 1; rm <<= 1; + rd >>= 1; + rd <<= 1; + rm >>= 1; + rm <<= 1; } opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= (imm6 & 0x3f) << 16; @@ -312,4 +325,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs index 830a748ab..78af6fe4e 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs @@ -11,19 +11,19 @@ namespace Ryujinx.Tests.Cpu { #if SimdTbl -#region "Helper methods" + #region "Helper methods" private static ulong GenIdxsForTbls(int regs) { const byte IdxInRngMin = 0; - byte idxInRngMax = (byte)((16 * regs) - 1); - byte idxOutRngMin = (byte) (16 * regs); + byte idxInRngMax = (byte)((16 * regs) - 1); + byte idxOutRngMin = (byte)(16 * regs); const byte IdxOutRngMax = 255; ulong idxs = 0ul; for (int cnt = 1; cnt <= 8; cnt++) { - ulong idxInRng = TestContext.CurrentContext.Random.NextByte(IdxInRngMin, idxInRngMax); + ulong idxInRng = TestContext.CurrentContext.Random.NextByte(IdxInRngMin, idxInRngMax); ulong idxOutRng = TestContext.CurrentContext.Random.NextByte(idxOutRngMin, IdxOutRngMax); ulong idx = TestContext.CurrentContext.Random.NextBool() ? idxInRng : idxOutRng; @@ -33,13 +33,15 @@ namespace Ryujinx.Tests.Cpu return idxs; } -#endregion + #endregion -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static ulong[] _8B_() { - return new[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, - 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + return new[] { + 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul, + }; } private static IEnumerable<ulong> _GenIdxsForTbl1_() @@ -93,15 +95,15 @@ namespace Ryujinx.Tests.Cpu yield return GenIdxsForTbls(regs: 4); } } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _SingleRegisterTable_V_8B_16B_() { return new[] { 0x0E000000u, // TBL V0.8B, { V0.16B }, V0.8B - 0x0E001000u // TBX V0.8B, { V0.16B }, V0.8B + 0x0E001000u, // TBX V0.8B, { V0.16B }, V0.8B }; } @@ -110,7 +112,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0E002000u, // TBL V0.8B, { V0.16B, V1.16B }, V0.8B - 0x0E003000u // TBX V0.8B, { V0.16B, V1.16B }, V0.8B + 0x0E003000u, // TBX V0.8B, { V0.16B, V1.16B }, V0.8B }; } @@ -119,7 +121,7 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0E004000u, // TBL V0.8B, { V0.16B, V1.16B, V2.16B }, V0.8B - 0x0E005000u // TBX V0.8B, { V0.16B, V1.16B, V2.16B }, V0.8B + 0x0E005000u, // TBX V0.8B, { V0.16B, V1.16B, V2.16B }, V0.8B }; } @@ -128,10 +130,10 @@ namespace Ryujinx.Tests.Cpu return new[] { 0x0E006000u, // TBL V0.8B, { V0.16B, V1.16B, V2.16B, V3.16B }, V0.8B - 0x0E006000u // TBX V0.8B, { V0.16B, V1.16B, V2.16B, V3.16B }, V0.8B + 0x0E006000u, // TBX V0.8B, { V0.16B, V1.16B, V2.16B, V3.16B }, V0.8B }; } -#endregion + #endregion private const int RndCntIdxs = 2; @@ -184,7 +186,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Mod_TwoRegisterTable_V_8B_16B([ValueSource(nameof(_TwoRegisterTable_V_8B_16B_))] uint opcodes, [Values(30u, 1u)] uint rd, - [Values(31u)] uint rn, + [Values(31u)] uint rn, [Values(1u, 30u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong table0, @@ -197,8 +199,8 @@ namespace Ryujinx.Tests.Cpu V128 v30 = MakeVectorE0E1(z, z); V128 v31 = MakeVectorE0E1(table0, table0); - V128 v0 = MakeVectorE0E1(table1, table1); - V128 v1 = MakeVectorE0E1(indexes, indexes); + V128 v0 = MakeVectorE0E1(table1, table1); + V128 v1 = MakeVectorE0E1(indexes, indexes); SingleOpcode(opcodes, v0: v0, v1: v1, v30: v30, v31: v31); @@ -234,7 +236,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Mod_ThreeRegisterTable_V_8B_16B([ValueSource(nameof(_ThreeRegisterTable_V_8B_16B_))] uint opcodes, [Values(30u, 2u)] uint rd, - [Values(31u)] uint rn, + [Values(31u)] uint rn, [Values(2u, 30u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong table0, @@ -248,9 +250,9 @@ namespace Ryujinx.Tests.Cpu V128 v30 = MakeVectorE0E1(z, z); V128 v31 = MakeVectorE0E1(table0, table0); - V128 v0 = MakeVectorE0E1(table1, table1); - V128 v1 = MakeVectorE0E1(table2, table2); - V128 v2 = MakeVectorE0E1(indexes, indexes); + V128 v0 = MakeVectorE0E1(table1, table1); + V128 v1 = MakeVectorE0E1(table2, table2); + V128 v2 = MakeVectorE0E1(indexes, indexes); SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v30: v30, v31: v31); @@ -288,7 +290,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] public void Mod_FourRegisterTable_V_8B_16B([ValueSource(nameof(_FourRegisterTable_V_8B_16B_))] uint opcodes, [Values(30u, 3u)] uint rd, - [Values(31u)] uint rn, + [Values(31u)] uint rn, [Values(3u, 30u)] uint rm, [ValueSource(nameof(_8B_))] ulong z, [ValueSource(nameof(_8B_))] ulong table0, @@ -303,10 +305,10 @@ namespace Ryujinx.Tests.Cpu V128 v30 = MakeVectorE0E1(z, z); V128 v31 = MakeVectorE0E1(table0, table0); - V128 v0 = MakeVectorE0E1(table1, table1); - V128 v1 = MakeVectorE0E1(table2, table2); - V128 v2 = MakeVectorE0E1(table3, table3); - V128 v3 = MakeVectorE0E1(indexes, indexes); + V128 v0 = MakeVectorE0E1(table1, table1); + V128 v1 = MakeVectorE0E1(table2, table2); + V128 v2 = MakeVectorE0E1(table3, table3); + V128 v3 = MakeVectorE0E1(indexes, indexes); SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v3: v3, v30: v30, v31: v31); diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSystem.cs b/src/Ryujinx.Tests/Cpu/CpuTestSystem.cs index cb8d9ce14..6c498ef0f 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSystem.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSystem.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Cpu { #if System -#region "ValueSource (Types)" + #region "ValueSource (Types)" private static IEnumerable<ulong> _GenNzcv_() { yield return 0x0000000000000000ul; @@ -33,23 +33,23 @@ namespace Ryujinx.Tests.Cpu yield return rnd; } -#endregion + #endregion -#region "ValueSource (Opcodes)" + #region "ValueSource (Opcodes)" private static uint[] _MrsMsr_Nzcv_() { return new[] { 0xD53B4200u, // MRS X0, NZCV - 0xD51B4200u // MSR NZCV, X0 + 0xD51B4200u, // MSR NZCV, X0 }; } -#endregion + #endregion [Test, Pairwise] - public void MrsMsr_Nzcv([ValueSource("_MrsMsr_Nzcv_")] uint opcodes, + public void MrsMsr_Nzcv([ValueSource(nameof(_MrsMsr_Nzcv_))] uint opcodes, [Values(0u, 1u, 31u)] uint rt, - [ValueSource("_GenNzcv_")] ulong xt) + [ValueSource(nameof(_GenNzcv_))] ulong xt) { opcodes |= (rt & 31) << 0; @@ -66,4 +66,4 @@ namespace Ryujinx.Tests.Cpu } #endif } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs index caef53d85..8046d581b 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestT32Alu.cs @@ -20,490 +20,490 @@ namespace Ryujinx.Tests.Cpu public static readonly PrecomputedThumbTestCase[] RsImmTestCases = { // TST (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea18, 0x4f03, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x15a99211, 0x08a56ba3, 0x3c588032, 0xdac302ae, 0x6b7d5b2d, 0x4fe1d8dd, 0x04a574ba, 0x7873779d, 0x17a565d1, 0x63a4bf95, 0xd62594fb, 0x2b9aa84b, 0x20448ccd, 0x70b2197e, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0x15a99211, 0x08a56ba3, 0x3c588032, 0xdac302ae, 0x6b7d5b2d, 0x4fe1d8dd, 0x04a574ba, 0x7873779d, 0x17a565d1, 0x63a4bf95, 0xd62594fb, 0x2b9aa84b, 0x20448ccd, 0x70b2197e, 0x00000000, 0x300001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea11, 0x5f67, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc9754393, 0xec511f2a, 0xc365b8f1, 0xa024565a, 0x089ae8e2, 0xf0c91f23, 0x290f83f4, 0x48f2f445, 0xd3288f2b, 0x7d7b2e44, 0xe80dd37e, 0xb000697f, 0x95be1027, 0x74702206, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0xc9754393, 0xec511f2a, 0xc365b8f1, 0xa024565a, 0x089ae8e2, 0xf0c91f23, 0x290f83f4, 0x48f2f445, 0xd3288f2b, 0x7d7b2e44, 0xe80dd37e, 0xb000697f, 0x95be1027, 0x74702206, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x2fc9, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe9c49eb7, 0x2ca13a97, 0x3fded5a8, 0x30e203e9, 0x811a9ee5, 0x504f95f2, 0x746794b4, 0xfe92b6d6, 0x7608d3c4, 0xf3c5ea36, 0x6290c8f2, 0x45a4a521, 0x359a615c, 0x25674915, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0xe9c49eb7, 0x2ca13a97, 0x3fded5a8, 0x30e203e9, 0x811a9ee5, 0x504f95f2, 0x746794b4, 0xfe92b6d6, 0x7608d3c4, 0xf3c5ea36, 0x6290c8f2, 0x45a4a521, 0x359a615c, 0x25674915, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea15, 0x0f85, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3130c8d7, 0x5917d350, 0xdf48eedb, 0x23025883, 0x076175bb, 0x5402cc6c, 0x54a95806, 0x7f59c691, 0x9c3eeebf, 0x4b52b4d1, 0xb4eb9626, 0x21fa7996, 0x0ff0a95a, 0x6beb27fd, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0x3130c8d7, 0x5917d350, 0xdf48eedb, 0x23025883, 0x076175bb, 0x5402cc6c, 0x54a95806, 0x7f59c691, 0x9c3eeebf, 0x4b52b4d1, 0xb4eb9626, 0x21fa7996, 0x0ff0a95a, 0x6beb27fd, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x6feb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x39889074, 0xbea8978e, 0x0331cc7a, 0x448e3b19, 0x33285e9e, 0xdf295408, 0x8444676e, 0xe6998904, 0x819e4da4, 0xb099272c, 0x101385a7, 0x71728a87, 0x76f95b3a, 0x8d5012e4, 0x00000000, 0xc00001f0 }, FinalRegs = new uint[] { 0x39889074, 0xbea8978e, 0x0331cc7a, 0x448e3b19, 0x33285e9e, 0xdf295408, 0x8444676e, 0xe6998904, 0x819e4da4, 0xb099272c, 0x101385a7, 0x71728a87, 0x76f95b3a, 0x8d5012e4, 0x00000000, 0x000001d0 }, }, // AND (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea18, 0x1f52, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xcbe174f1, 0x44be318c, 0x4a8a1a70, 0x1f3c8883, 0x33b316ee, 0x0591a3c5, 0x0ceff4a5, 0xd74988e2, 0xa5ef1873, 0xbd35a940, 0x52a9f4d8, 0xf8662781, 0xda558ea8, 0x4c7d50bc, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0xcbe174f1, 0x44be318c, 0x4a8a1a70, 0x1f3c8883, 0x33b316ee, 0x0591a3c5, 0x0ceff4a5, 0xd74988e2, 0xa5ef1873, 0xbd35a940, 0x52a9f4d8, 0xf8662781, 0xda558ea8, 0x4c7d50bc, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea19, 0x4f6b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x97b9423c, 0x1c25286b, 0x50f84fef, 0xd917c24e, 0x2a5116af, 0xcc65ba10, 0xf5e9dc41, 0xf9f61d10, 0x9876cfe5, 0xd0fdd4bc, 0x95913be0, 0x844c820f, 0xfdaf9519, 0xf3fb09b6, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x97b9423c, 0x1c25286b, 0x50f84fef, 0xd917c24e, 0x2a5116af, 0xcc65ba10, 0xf5e9dc41, 0xf9f61d10, 0x9876cfe5, 0xd0fdd4bc, 0x95913be0, 0x844c820f, 0xfdaf9519, 0xf3fb09b6, 0x00000000, 0x900001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x3f52, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x1900757b, 0x6914c62d, 0x5eaa28ed, 0xd927c1f7, 0xfd2052ac, 0x146bcb99, 0x604f9b1d, 0xb395bf46, 0x3723ba84, 0xb909d3ec, 0x3db4365e, 0x42df68cd, 0x5fdc10cb, 0x4955b8be, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x1900757b, 0x6914c62d, 0x5eaa28ed, 0xd927c1f7, 0xfd2052ac, 0x146bcb99, 0x604f9b1d, 0xb395bf46, 0x3723ba84, 0xb909d3ec, 0x3db4365e, 0x42df68cd, 0x5fdc10cb, 0x4955b8be, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x0f17, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6134093f, 0x115a1456, 0xa7877f6e, 0x2070e9eb, 0x9ddf4a73, 0x14266482, 0x7f98e557, 0xbaa854e0, 0xa37f89a6, 0x641325de, 0xae2dc79b, 0x5b3f2af2, 0x476476d2, 0xb99cc9fd, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x6134093f, 0x115a1456, 0xa7877f6e, 0x2070e9eb, 0x9ddf4a73, 0x14266482, 0x7f98e557, 0xbaa854e0, 0xa37f89a6, 0x641325de, 0xae2dc79b, 0x5b3f2af2, 0x476476d2, 0xb99cc9fd, 0x00000000, 0x700001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x5f17, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xbaa2bc1a, 0xee3e86d4, 0x3179d65a, 0x8a63cf55, 0x48ea14f4, 0xf85c5d5b, 0x6af50974, 0xf3ded3e9, 0xdab4d6e6, 0x930c07eb, 0x8084b2dd, 0xf6518695, 0x4a3e0f7a, 0x581bd56a, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0xbaa2bc1a, 0xee3e86d4, 0x3179d65a, 0x8a63cf55, 0x48ea14f4, 0xf85c5d5b, 0x6af50974, 0xf3ded3e9, 0xdab4d6e6, 0x930c07eb, 0x8084b2dd, 0xf6518695, 0x4a3e0f7a, 0x581bd56a, 0x00000000, 0x300001d0 }, }, // BIC (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1c, 0x0fbc, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xfc7b7a8b, 0xc1186d54, 0x0a83eda1, 0x88fed37c, 0x5438e8ea, 0xe0af3690, 0x6dba7b9f, 0xa7395bd6, 0xd43af274, 0xbb46f4c2, 0xb65dbcd5, 0xa6bd08b0, 0xb55971c7, 0x2244572e, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0xfc7b7a8b, 0xc1186d54, 0x0a83eda1, 0x88fed37c, 0x5438e8ea, 0xe0af3690, 0x6dba7b9f, 0xa7395bd6, 0xd43af274, 0xbb46f4c2, 0xb65dbcd5, 0xa6bd08b0, 0xb55971c7, 0x2244572e, 0x00000000, 0xb00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x5fe7, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x75f617c8, 0x12ac7ccd, 0x85e6d881, 0x30967bdd, 0xdf66b387, 0xb3d59ccf, 0xe3c824b4, 0xada7a9e4, 0x225da86f, 0x18e008ac, 0x51854224, 0xf3b43823, 0xde37f151, 0x6764b34a, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x75f617c8, 0x12ac7ccd, 0x85e6d881, 0x30967bdd, 0xdf66b387, 0xb3d59ccf, 0xe3c824b4, 0xada7a9e4, 0x225da86f, 0x18e008ac, 0x51854224, 0xf3b43823, 0xde37f151, 0x6764b34a, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x1fc3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xde174255, 0x3968e364, 0xf1efd73b, 0x9a159a4e, 0x2b906c3e, 0xf1dfb847, 0x34e3e8f0, 0x39c33745, 0xc368a812, 0x8f3fe175, 0xe3da055f, 0x7737a5d5, 0x7464344a, 0xdb3ac192, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0xde174255, 0x3968e364, 0xf1efd73b, 0x9a159a4e, 0x2b906c3e, 0xf1dfb847, 0x34e3e8f0, 0x39c33745, 0xc368a812, 0x8f3fe175, 0xe3da055f, 0x7737a5d5, 0x7464344a, 0xdb3ac192, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea18, 0x6f66, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x368ef4f3, 0x18583461, 0x94f6e104, 0x21e1c1b0, 0x009c85df, 0xe6bddfb2, 0x118e9dad, 0xcdf92eb5, 0xae18b093, 0xe24a54ab, 0x55d1a1a0, 0x0eed1bad, 0x8b6bce47, 0x20b1fdc2, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x368ef4f3, 0x18583461, 0x94f6e104, 0x21e1c1b0, 0x009c85df, 0xe6bddfb2, 0x118e9dad, 0xcdf92eb5, 0xae18b093, 0xe24a54ab, 0x55d1a1a0, 0x0eed1bad, 0x8b6bce47, 0x20b1fdc2, 0x00000000, 0x700001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x3fc6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6f913e40, 0xd1814933, 0x181eb63c, 0x287a5050, 0xe5925dd9, 0x712ee261, 0xcca2e51d, 0x0e88a1ba, 0xa4c8d4c3, 0x26887e3e, 0x83b8de36, 0xc5a5d439, 0x8d2ace7a, 0x9df36292, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x6f913e40, 0xd1814933, 0x181eb63c, 0x287a5050, 0xe5925dd9, 0x712ee261, 0xcca2e51d, 0x0e88a1ba, 0xa4c8d4c3, 0x26887e3e, 0x83b8de36, 0xc5a5d439, 0x8d2ace7a, 0x9df36292, 0x00000000, 0x200001d0 }, }, // MOV (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x2fdd, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x89fcb953, 0xafbf8db2, 0xad96137f, 0x7901360c, 0x513b561b, 0x2345a005, 0x0ece889b, 0xc8bb918f, 0x270458ce, 0x73bea675, 0xab735592, 0xf68e00e5, 0x88bf2dc1, 0x98601074, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x89fcb953, 0xafbf8db2, 0xad96137f, 0x7901360c, 0x513b561b, 0x2345a005, 0x0ece889b, 0xc8bb918f, 0x270458ce, 0x73bea675, 0xab735592, 0xf68e00e5, 0x88bf2dc1, 0x98601074, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea19, 0x6fd6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x35f5eea1, 0x75fe4252, 0x71165923, 0x13ad82d2, 0x01f69a1c, 0x33ff5351, 0x869c335f, 0x70ce9266, 0xf58868ad, 0x4f58e982, 0x89f7df88, 0xd0ba8d45, 0xf45e6e03, 0x7f653972, 0x00000000, 0x800001f0 }, FinalRegs = new uint[] { 0x35f5eea1, 0x75fe4252, 0x71165923, 0x13ad82d2, 0x01f69a1c, 0x33ff5351, 0x869c335f, 0x70ce9266, 0xf58868ad, 0x4f58e982, 0x89f7df88, 0xd0ba8d45, 0xf45e6e03, 0x7f653972, 0x00000000, 0x600001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x6f5d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x1e83719a, 0x1b6405c5, 0x25d9d1d6, 0x3e5fc7f3, 0xd157d610, 0x271b5c46, 0xb65c2838, 0xe4590643, 0x2f2623d7, 0xf1155f93, 0xfa676221, 0x6fac2a1d, 0xc1fa1d8d, 0x8cfa89e1, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x1e83719a, 0x1b6405c5, 0x25d9d1d6, 0x3e5fc7f3, 0xd157d610, 0x271b5c46, 0xb65c2838, 0xe4590643, 0x2f2623d7, 0xf1155f93, 0xfa676221, 0x6fac2a1d, 0xc1fa1d8d, 0x8cfa89e1, 0x00000000, 0x500001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1c, 0x2fa2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x431278f1, 0xd3fffe52, 0xbfb4d877, 0x10af0eeb, 0xd375b791, 0xbd19aa81, 0x45eb7ba3, 0x30e47d42, 0xc274e032, 0x6da10d33, 0xfeda1ba4, 0x3dc6205e, 0xc275197e, 0x6c8b86d1, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x431278f1, 0xd3fffe52, 0xbfb4d877, 0x10af0eeb, 0xd375b791, 0xbd19aa81, 0x45eb7ba3, 0x30e47d42, 0xc274e032, 0x6da10d33, 0xfeda1ba4, 0x3dc6205e, 0xc275197e, 0x6c8b86d1, 0x00000000, 0x900001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x7f7b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x31f0830d, 0x69dda3f6, 0x983fc927, 0x0407652a, 0x32ceab65, 0xe76a77fd, 0x8a7dd0a6, 0x4892a02f, 0xeab00585, 0xa78bf230, 0x896dd5a9, 0xe3c44398, 0xc2d743d0, 0x42b03803, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x31f0830d, 0x69dda3f6, 0x983fc927, 0x0407652a, 0x32ceab65, 0xe76a77fd, 0x8a7dd0a6, 0x4892a02f, 0xeab00585, 0xa78bf230, 0x896dd5a9, 0xe3c44398, 0xc2d743d0, 0x42b03803, 0x00000000, 0x100001d0 }, }, // ORR (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea10, 0x5f72, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5834d41e, 0x7092ed2e, 0x8994242e, 0x7fac6d96, 0x4d896829, 0x1a578dec, 0x98649fd8, 0x3b713450, 0xca430792, 0xd68d5176, 0xfe0b5c4f, 0xd9caf416, 0xb0e9d5fa, 0x62c57422, 0x00000000, 0x800001f0 }, FinalRegs = new uint[] { 0x5834d41e, 0x7092ed2e, 0x8994242e, 0x7fac6d96, 0x4d896829, 0x1a578dec, 0x98649fd8, 0x3b713450, 0xca430792, 0xd68d5176, 0xfe0b5c4f, 0xd9caf416, 0xb0e9d5fa, 0x62c57422, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x0fb4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6842aa84, 0x0711ecb6, 0xebae7374, 0x6ea58edd, 0xa6d2837c, 0xbcc1e0d1, 0xe52c9d6c, 0x7bb5fa1c, 0xa7cd6f8a, 0x4558ddb7, 0x7adb449c, 0x95986dd8, 0x7432562c, 0x80d2595c, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x6842aa84, 0x0711ecb6, 0xebae7374, 0x6ea58edd, 0xa6d2837c, 0xbcc1e0d1, 0xe52c9d6c, 0x7bb5fa1c, 0xa7cd6f8a, 0x4558ddb7, 0x7adb449c, 0x95986dd8, 0x7432562c, 0x80d2595c, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x2f78, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6be94e4c, 0x0569589b, 0xc7e6e127, 0xe5537aea, 0x323e7a85, 0x895e9a94, 0x2341f9b6, 0x9632a18a, 0xa790766f, 0x53533cf3, 0x83cec3aa, 0xa1d042af, 0xabff7e58, 0x614f9bc0, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x6be94e4c, 0x0569589b, 0xc7e6e127, 0xe5537aea, 0x323e7a85, 0x895e9a94, 0x2341f9b6, 0x9632a18a, 0xa790766f, 0x53533cf3, 0x83cec3aa, 0xa1d042af, 0xabff7e58, 0x614f9bc0, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea12, 0x4fbc, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2e43ac9a, 0xa6a8d4b6, 0xf5853279, 0xf152f284, 0xce9656e5, 0x07642918, 0xd6e25d4a, 0xdebc7fa6, 0x8c3af5e0, 0x3d00cd4c, 0x7e744bb4, 0x2a4b8015, 0x602ea481, 0xdef7571b, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x2e43ac9a, 0xa6a8d4b6, 0xf5853279, 0xf152f284, 0xce9656e5, 0x07642918, 0xd6e25d4a, 0xdebc7fa6, 0x8c3af5e0, 0x3d00cd4c, 0x7e744bb4, 0x2a4b8015, 0x602ea481, 0xdef7571b, 0x00000000, 0xb00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x7f4c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x67be4dae, 0xff0f74a8, 0xd769f9e1, 0xb4a98e0a, 0x2988a7dc, 0xb5726464, 0xb7b3fb27, 0x077e539c, 0x9c817cd4, 0xa8cc3981, 0xbe5a7591, 0xc753850a, 0xb8c612a7, 0x6d913c9b, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x67be4dae, 0xff0f74a8, 0xd769f9e1, 0xb4a98e0a, 0x2988a7dc, 0xb5726464, 0xb7b3fb27, 0x077e539c, 0x9c817cd4, 0xa8cc3981, 0xbe5a7591, 0xc753850a, 0xb8c612a7, 0x6d913c9b, 0x00000000, 0x100001d0 }, }, // MVN (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea15, 0x0ffb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4a0a7b4c, 0x4e58d907, 0x386b8207, 0xcd71b0c4, 0x86dcf525, 0x8ae9dba4, 0xf5d6a418, 0xfac79f2e, 0x44cf918b, 0x5d38193b, 0xc17adeaf, 0xa4ad8a86, 0x69527ece, 0x69b75c61, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x4a0a7b4c, 0x4e58d907, 0x386b8207, 0xcd71b0c4, 0x86dcf525, 0x8ae9dba4, 0xf5d6a418, 0xfac79f2e, 0x44cf918b, 0x5d38193b, 0xc17adeaf, 0xa4ad8a86, 0x69527ece, 0x69b75c61, 0x00000000, 0xb00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x4f01, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xce60a6df, 0xb1127c3f, 0x410d7b4a, 0xd9cfc917, 0xd1b2fc52, 0x8be1e03c, 0xde9b256d, 0xff989abd, 0x07e3c46a, 0x780e7d7c, 0xd807ce82, 0x5e5c8f2b, 0x09232f6d, 0x00746338, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0xce60a6df, 0xb1127c3f, 0x410d7b4a, 0xd9cfc917, 0xd1b2fc52, 0x8be1e03c, 0xde9b256d, 0xff989abd, 0x07e3c46a, 0x780e7d7c, 0xd807ce82, 0x5e5c8f2b, 0x09232f6d, 0x00746338, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea18, 0x2f5e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x18b1240a, 0xa896f734, 0xcd4a40bc, 0x28346a77, 0xbdf09586, 0x3c74ed70, 0x3e255ea3, 0xe55679b4, 0xcc602510, 0x9cd73bfb, 0xf21a6ddb, 0x263a4338, 0x06beb332, 0x0790ac93, 0x00000000, 0xa00001f0 }, FinalRegs = new uint[] { 0x18b1240a, 0xa896f734, 0xcd4a40bc, 0x28346a77, 0xbdf09586, 0x3c74ed70, 0x3e255ea3, 0xe55679b4, 0xcc602510, 0x9cd73bfb, 0xf21a6ddb, 0x263a4338, 0x06beb332, 0x0790ac93, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x7f41, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0c25f69d, 0xc32dc28a, 0xf5e2fe71, 0xe46af209, 0x2d1b6ac8, 0xccac564c, 0x567cc561, 0x63707d28, 0xeae934c8, 0xab78e6f6, 0x2d78d86d, 0x76471cdc, 0x9b909f76, 0xa2cc099d, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x0c25f69d, 0xc32dc28a, 0xf5e2fe71, 0xe46af209, 0x2d1b6ac8, 0xccac564c, 0x567cc561, 0x63707d28, 0xeae934c8, 0xab78e6f6, 0x2d78d86d, 0x76471cdc, 0x9b909f76, 0xa2cc099d, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea19, 0x6ff6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6e79c449, 0xe9449bf7, 0x51f8fcb8, 0x138e0b80, 0x715312f2, 0x601ea894, 0xb01f9369, 0x02738c29, 0xee35545f, 0xb61ae4a2, 0xba412f08, 0x1d349e02, 0x56a0dfc0, 0x68cd5bfe, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0x6e79c449, 0xe9449bf7, 0x51f8fcb8, 0x138e0b80, 0x715312f2, 0x601ea894, 0xb01f9369, 0x02738c29, 0xee35545f, 0xb61ae4a2, 0xba412f08, 0x1d349e02, 0x56a0dfc0, 0x68cd5bfe, 0x00000000, 0x100001d0 }, }, // ORN (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x3fd0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x77034e34, 0xd0727e58, 0x4748dbf2, 0x2becd551, 0x0a650329, 0x005548fa, 0xcfb963c2, 0x9561c965, 0xf157c850, 0x180a1a6c, 0x0252e103, 0x29d0f25a, 0xbd9bbecd, 0xbfd1347c, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x77034e34, 0xd0727e58, 0x4748dbf2, 0x2becd551, 0x0a650329, 0x005548fa, 0xcfb963c2, 0x9561c965, 0xf157c850, 0x180a1a6c, 0x0252e103, 0x29d0f25a, 0xbd9bbecd, 0xbfd1347c, 0x00000000, 0x300001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x4f72, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x71cc7ddf, 0x67d4ce81, 0x60b04501, 0xcc90b805, 0xc5f34081, 0x5e83e9f7, 0xb5a78fa9, 0xc2497a71, 0xb20cdf14, 0x4de9f773, 0xf79525ec, 0x26534abd, 0xcd7b59d1, 0x5cfc9554, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x71cc7ddf, 0x67d4ce81, 0x60b04501, 0xcc90b805, 0xc5f34081, 0x5e83e9f7, 0xb5a78fa9, 0xc2497a71, 0xb20cdf14, 0x4de9f773, 0xf79525ec, 0x26534abd, 0xcd7b59d1, 0x5cfc9554, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1d, 0x4fa7, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x73259d94, 0xc85643db, 0xbf238eb1, 0x51648d99, 0xce2971c9, 0xf9e0e440, 0x90de33c9, 0xcf8ac8e9, 0xda964c21, 0x539eb057, 0x3a681b87, 0x11993d47, 0x05a1358f, 0xa8282529, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x73259d94, 0xc85643db, 0xbf238eb1, 0x51648d99, 0xce2971c9, 0xf9e0e440, 0x90de33c9, 0xcf8ac8e9, 0xda964c21, 0x539eb057, 0x3a681b87, 0x11993d47, 0x05a1358f, 0xa8282529, 0x00000000, 0xb00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea12, 0x3fdb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd274d46b, 0x8937836f, 0x33b78178, 0xc250b807, 0xd3323d2f, 0x82e03ba2, 0xf93bf1a6, 0xb31e0c74, 0xc9238070, 0x957331d1, 0xfaadd1ee, 0x073d40fb, 0x05b3e8b4, 0x93e5233b, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0xd274d46b, 0x8937836f, 0x33b78178, 0xc250b807, 0xd3323d2f, 0x82e03ba2, 0xf93bf1a6, 0xb31e0c74, 0xc9238070, 0x957331d1, 0xfaadd1ee, 0x073d40fb, 0x05b3e8b4, 0x93e5233b, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea15, 0x5f92, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x24755d61, 0xb65b742d, 0xb46476cf, 0x771a9fcd, 0x465b367f, 0x3daa2a47, 0x6984eeb8, 0x238e3187, 0xa9717261, 0x4592be1d, 0x46d19147, 0x6a6e4dc8, 0x4ddd896f, 0x2f899425, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x24755d61, 0xb65b742d, 0xb46476cf, 0x771a9fcd, 0x465b367f, 0x3daa2a47, 0x6984eeb8, 0x238e3187, 0xa9717261, 0x4592be1d, 0x46d19147, 0x6a6e4dc8, 0x4ddd896f, 0x2f899425, 0x00000000, 0x300001d0 }, }, // TEQ (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x2f54, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x36675cac, 0xd9259257, 0x0b8ab9ad, 0xbfef324e, 0xf9623cd6, 0xfc1919ff, 0x616b25f5, 0x2d26a3d3, 0x61eb12c8, 0xbb8d48f0, 0xbfb9c232, 0x10383506, 0x31d10885, 0xf29cb615, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0x36675cac, 0xd9259257, 0x0b8ab9ad, 0xbfef324e, 0xf9623cd6, 0xfc1919ff, 0x616b25f5, 0x2d26a3d3, 0x61eb12c8, 0xbb8d48f0, 0xbfb9c232, 0x10383506, 0x31d10885, 0xf29cb615, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea17, 0x1f43, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf7dce25d, 0xbe296478, 0xe1aee674, 0x0414c126, 0xa258cf11, 0x5347cc5f, 0x6f8ed2c9, 0xed554dbe, 0xd3073560, 0x627dbd64, 0xca8bb3fc, 0x9590e3a9, 0xe4bea6bc, 0x557934a6, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0xf7dce25d, 0xbe296478, 0xe1aee674, 0x0414c126, 0xa258cf11, 0x5347cc5f, 0x6f8ed2c9, 0xed554dbe, 0xd3073560, 0x627dbd64, 0xca8bb3fc, 0x9590e3a9, 0xe4bea6bc, 0x557934a6, 0x00000000, 0x900001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea13, 0x1f5b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x84ad5535, 0xc1f15e65, 0x5ea0078b, 0x79df457d, 0x1c735fe5, 0x06dcfd95, 0x6db96dae, 0x572f572d, 0xac88a919, 0x56d850a6, 0xd5ce3a30, 0x2be992e8, 0x497a47ce, 0x38a74019, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x84ad5535, 0xc1f15e65, 0x5ea0078b, 0x79df457d, 0x1c735fe5, 0x06dcfd95, 0x6db96dae, 0x572f572d, 0xac88a919, 0x56d850a6, 0xd5ce3a30, 0x2be992e8, 0x497a47ce, 0x38a74019, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x3ff3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xa85ea277, 0x6028643d, 0xa5a85f15, 0x47f0f2a5, 0x9b6eebdb, 0x9f064bc7, 0xab59939f, 0x1a278260, 0xb9f91cfa, 0xf913c49c, 0x2b5c0052, 0x1bf2d6dc, 0x81da80a4, 0xced90006, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0xa85ea277, 0x6028643d, 0xa5a85f15, 0x47f0f2a5, 0x9b6eebdb, 0x9f064bc7, 0xab59939f, 0x1a278260, 0xb9f91cfa, 0xf913c49c, 0x2b5c0052, 0x1bf2d6dc, 0x81da80a4, 0xced90006, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x3f09, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xdf494128, 0xbc975c16, 0x62c66823, 0x95be3737, 0xa07e8778, 0x6ce80cc7, 0xfea03385, 0x4c5bf35a, 0x5cd0bcdf, 0xc47451ab, 0x3849af70, 0x1329c14a, 0xb1f96f79, 0x321eaf12, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0xdf494128, 0xbc975c16, 0x62c66823, 0x95be3737, 0xa07e8778, 0x6ce80cc7, 0xfea03385, 0x4c5bf35a, 0x5cd0bcdf, 0xc47451ab, 0x3849af70, 0x1329c14a, 0xb1f96f79, 0x321eaf12, 0x00000000, 0x300001d0 }, }, // EOR (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea17, 0x6fc0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x9c803ce5, 0x38e325f9, 0x4d32aea8, 0x0120f77b, 0x8e34b507, 0xee41aabf, 0x7e6d8a0c, 0x761a3f21, 0x99b57f1d, 0x32a4bbf3, 0x9902c1f4, 0xd5e2dd41, 0xe2a08209, 0x2896ceba, 0x00000000, 0xc00001f0 }, FinalRegs = new uint[] { 0x9c803ce5, 0x38e325f9, 0x4d32aea8, 0x0120f77b, 0x8e34b507, 0xee41aabf, 0x7e6d8a0c, 0x761a3f21, 0x99b57f1d, 0x32a4bbf3, 0x9902c1f4, 0xd5e2dd41, 0xe2a08209, 0x2896ceba, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1c, 0x4f58, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe9c3e9b7, 0x26c9e052, 0x708b6153, 0x72dbdc3c, 0xdd3e922d, 0xd0260aca, 0x38dcf6be, 0x4164575f, 0x5d8e03dc, 0x30bfa694, 0xe72a6609, 0xba632c43, 0x1f768178, 0x6b4f56a6, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0xe9c3e9b7, 0x26c9e052, 0x708b6153, 0x72dbdc3c, 0xdd3e922d, 0xd0260aca, 0x38dcf6be, 0x4164575f, 0x5d8e03dc, 0x30bfa694, 0xe72a6609, 0xba632c43, 0x1f768178, 0x6b4f56a6, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x7f31, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x9205c1f5, 0x80dd7e44, 0x8ecd5272, 0x3d8d3691, 0x35d45cca, 0x4b2d9eb3, 0xa1652285, 0x6a1cb7f1, 0x8e08b99f, 0xdf8f0c57, 0x28dd0dfa, 0xf2c0abbd, 0x167a6539, 0x75163a9d, 0x00000000, 0xa00001f0 }, FinalRegs = new uint[] { 0x9205c1f5, 0x80dd7e44, 0x8ecd5272, 0x3d8d3691, 0x35d45cca, 0x4b2d9eb3, 0xa1652285, 0x6a1cb7f1, 0x8e08b99f, 0xdf8f0c57, 0x28dd0dfa, 0xf2c0abbd, 0x167a6539, 0x75163a9d, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x4f80, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x96547d6c, 0x7afda83c, 0xb3edea2d, 0x49d05904, 0xb661ef76, 0xf1cce5ff, 0x2986ab1b, 0xcb39b044, 0x88937ad3, 0x962cf736, 0x80d6f109, 0xb73dd0d6, 0xb93f9f60, 0xb93a02c9, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x96547d6c, 0x7afda83c, 0xb3edea2d, 0x49d05904, 0xb661ef76, 0xf1cce5ff, 0x2986ab1b, 0xcb39b044, 0x88937ad3, 0x962cf736, 0x80d6f109, 0xb73dd0d6, 0xb93f9f60, 0xb93a02c9, 0x00000000, 0xb00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea12, 0x1f35, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xb1a94a51, 0xa8679784, 0xd7558ceb, 0x58d63d95, 0x6e5cf7eb, 0x0d40398d, 0xf88fa339, 0xbe88a56f, 0x7180f980, 0x0795ba21, 0x0732b252, 0xa51be7c8, 0x47c02749, 0xb0fbbd9f, 0x00000000, 0x800001f0 }, FinalRegs = new uint[] { 0xb1a94a51, 0xa8679784, 0xd7558ceb, 0x58d63d95, 0x6e5cf7eb, 0x0d40398d, 0xf88fa339, 0xbe88a56f, 0x7180f980, 0x0795ba21, 0x0732b252, 0xa51be7c8, 0x47c02749, 0xb0fbbd9f, 0x00000000, 0xa00001d0 }, }, // CMN (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x4fc5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf4440521, 0x26b151d9, 0x90053d26, 0x8c3bbde1, 0x4a757fa1, 0x34b63513, 0xd1d1a182, 0xa9123bc1, 0xadfbf652, 0xec28d3e6, 0x6ca54af1, 0x385d5637, 0x46280bac, 0x18f38d39, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0xf4440521, 0x26b151d9, 0x90053d26, 0x8c3bbde1, 0x4a757fa1, 0x34b63513, 0xd1d1a182, 0xa9123bc1, 0xadfbf652, 0xec28d3e6, 0x6ca54af1, 0x385d5637, 0x46280bac, 0x18f38d39, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x4fe4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x20abeba4, 0x77f9bd90, 0x5c09b098, 0xdebadd51, 0x6e114dcf, 0xd8212cf8, 0x2a6a57d2, 0xb9667ed8, 0x93817fef, 0x639cd8b4, 0x52b67bce, 0x681ee61c, 0x50bb5414, 0x9f297765, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x20abeba4, 0x77f9bd90, 0x5c09b098, 0xdebadd51, 0x6e114dcf, 0xd8212cf8, 0x2a6a57d2, 0xb9667ed8, 0x93817fef, 0x639cd8b4, 0x52b67bce, 0x681ee61c, 0x50bb5414, 0x9f297765, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1d, 0x6f51, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x12b54404, 0x9ad5df58, 0x8a73af2a, 0xd89dd454, 0x57f5a14b, 0xcee0a06f, 0xb53e67ca, 0x92730368, 0xab12a843, 0x929ae15d, 0xea1e4f49, 0xd7fadfbc, 0x9defdd99, 0xff22c9c8, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0x12b54404, 0x9ad5df58, 0x8a73af2a, 0xd89dd454, 0x57f5a14b, 0xcee0a06f, 0xb53e67ca, 0x92730368, 0xab12a843, 0x929ae15d, 0xea1e4f49, 0xd7fadfbc, 0x9defdd99, 0xff22c9c8, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea15, 0x5f77, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x64dfdc90, 0xd570bc25, 0xae804ff7, 0x491ad040, 0xfe5f6d58, 0x850c1223, 0x39afac7b, 0xcc5a165a, 0x956a9473, 0x89d3941f, 0x6e520e57, 0x804dca75, 0xbd40cde8, 0xff68c0e7, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0x64dfdc90, 0xd570bc25, 0xae804ff7, 0x491ad040, 0xfe5f6d58, 0x850c1223, 0x39afac7b, 0xcc5a165a, 0x956a9473, 0x89d3941f, 0x6e520e57, 0x804dca75, 0xbd40cde8, 0xff68c0e7, 0x00000000, 0xb00001d0 }, }, // ADD (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1c, 0x4f0b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x8a81d956, 0x65b2b25b, 0x34dd981e, 0x924542f4, 0xeed4b95a, 0x096d832c, 0x8ddcb715, 0x2df1897b, 0x696d0d5c, 0xfa6853c1, 0xcbb52912, 0xe37a3fda, 0x54dd595d, 0x652e5a2b, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0x8a81d956, 0x65b2b25b, 0x34dd981e, 0x924542f4, 0xeed4b95a, 0x096d832c, 0x8ddcb715, 0x2df1897b, 0x696d0d5c, 0xfa6853c1, 0xcbb52912, 0xe37a3fda, 0x54dd595d, 0x652e5a2b, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea12, 0x6faa, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4b774bbf, 0xe1f3168c, 0xfcdf56d4, 0x0a9feca9, 0x8d832cd1, 0x27af5bb2, 0xe7123c8f, 0x5ae971a8, 0x7c86287f, 0x5e69f0a7, 0x43e672d3, 0xb552a0f4, 0xb8b4fc17, 0xa9cc9a9d, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x4b774bbf, 0xe1f3168c, 0xfcdf56d4, 0x0a9feca9, 0x8d832cd1, 0x27af5bb2, 0xe7123c8f, 0x5ae971a8, 0x7c86287f, 0x5e69f0a7, 0x43e672d3, 0xb552a0f4, 0xb8b4fc17, 0xa9cc9a9d, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea18, 0x2f2c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x907613be, 0x807d314f, 0x10328bb5, 0xf3433f78, 0x0fa6c190, 0x473e8ac5, 0x0019b12e, 0xa24d7590, 0x0fdac8d5, 0x24e4feea, 0xf5eadcbf, 0xdfd73f71, 0xee2c8957, 0xaef12e15, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x907613be, 0x807d314f, 0x10328bb5, 0xf3433f78, 0x0fa6c190, 0x473e8ac5, 0x0019b12e, 0xa24d7590, 0x0fdac8d5, 0x24e4feea, 0xf5eadcbf, 0xdfd73f71, 0xee2c8957, 0xaef12e15, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x3f00, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2f9a7f7c, 0xc6ad7d01, 0x12b220a4, 0x57b4e83c, 0x2132b566, 0xb4afd045, 0x2b5d39bf, 0xceeecd89, 0x724bff21, 0xb527620e, 0xa9fba943, 0xd2d70658, 0x4e69f57b, 0x55df6b8f, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x2f9a7f7c, 0xc6ad7d01, 0x12b220a4, 0x57b4e83c, 0x2132b566, 0xb4afd045, 0x2b5d39bf, 0xceeecd89, 0x724bff21, 0xb527620e, 0xa9fba943, 0xd2d70658, 0x4e69f57b, 0x55df6b8f, 0x00000000, 0x300001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x2fdb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x854d0c91, 0x895ded29, 0x3e81cd89, 0x9ed269cf, 0x8a7354fa, 0x95cfe79f, 0x07248663, 0x3ec81b86, 0x6f1086e0, 0x51b4c91c, 0xb2d0946b, 0x1b81a616, 0x2b03fe57, 0xfbde03fd, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0x854d0c91, 0x895ded29, 0x3e81cd89, 0x9ed269cf, 0x8a7354fa, 0x95cfe79f, 0x07248663, 0x3ec81b86, 0x6f1086e0, 0x51b4c91c, 0xb2d0946b, 0x1b81a616, 0x2b03fe57, 0xfbde03fd, 0x00000000, 0x300001d0 }, }, // ADC (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x3fe4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4333cbde, 0xb61e8731, 0x2121c6ec, 0x796ecc75, 0xb570472d, 0x203aa9ea, 0xdad7bf7e, 0x93654919, 0x9e4c4e08, 0x1e352004, 0x104a06d7, 0x6b9a4b8a, 0xa0bcd372, 0x1713789a, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x4333cbde, 0xb61e8731, 0x2121c6ec, 0x796ecc75, 0xb570472d, 0x203aa9ea, 0xdad7bf7e, 0x93654919, 0x9e4c4e08, 0x1e352004, 0x104a06d7, 0x6b9a4b8a, 0xa0bcd372, 0x1713789a, 0x00000000, 0x300001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x3f40, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xad935c4c, 0x4c7dcbfc, 0x802965ca, 0x1dccd2e8, 0xe960e9e1, 0x3bff0055, 0x204f6a43, 0xe31b010d, 0x33c8f3c5, 0x8e27b912, 0x1351e4a6, 0x90195167, 0xd9112661, 0xee319b51, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0xad935c4c, 0x4c7dcbfc, 0x802965ca, 0x1dccd2e8, 0xe960e9e1, 0x3bff0055, 0x204f6a43, 0xe31b010d, 0x33c8f3c5, 0x8e27b912, 0x1351e4a6, 0x90195167, 0xd9112661, 0xee319b51, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea15, 0x3fe8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x26deb79f, 0x01ac9258, 0x5982ae34, 0x2c3c6df6, 0xabf6a749, 0x319d4b48, 0xce8cca6d, 0xe4b70851, 0x135c049c, 0xd8839d5e, 0xd3171a30, 0xfb09e096, 0x06d67a32, 0x0bab9825, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x26deb79f, 0x01ac9258, 0x5982ae34, 0x2c3c6df6, 0xabf6a749, 0x319d4b48, 0xce8cca6d, 0xe4b70851, 0x135c049c, 0xd8839d5e, 0xd3171a30, 0xfb09e096, 0x06d67a32, 0x0bab9825, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea12, 0x2fec, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd2c9ebca, 0x3ace22dc, 0x41603f45, 0x8cc7e2c2, 0xa160b815, 0xac1e7eb8, 0x57c49232, 0x62547a9b, 0xa18407fd, 0x5c549424, 0xf3eec0e1, 0x4185299f, 0x329a9063, 0x649d9b44, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0xd2c9ebca, 0x3ace22dc, 0x41603f45, 0x8cc7e2c2, 0xa160b815, 0xac1e7eb8, 0x57c49232, 0x62547a9b, 0xa18407fd, 0x5c549424, 0xf3eec0e1, 0x4185299f, 0x329a9063, 0x649d9b44, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea15, 0x2f73, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf3f8be7b, 0x05e02ec4, 0x78bd9fce, 0x6fb329c8, 0x3094f103, 0x7c93c61d, 0xaade3d9f, 0x381bf77c, 0x738389cd, 0xc68dc0e2, 0x60a06e86, 0x9b717afd, 0x9e51e4eb, 0xf7966699, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0xf3f8be7b, 0x05e02ec4, 0x78bd9fce, 0x6fb329c8, 0x3094f103, 0x7c93c61d, 0xaade3d9f, 0x381bf77c, 0x738389cd, 0xc68dc0e2, 0x60a06e86, 0x9b717afd, 0x9e51e4eb, 0xf7966699, 0x00000000, 0x300001d0 }, }, // SBC (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1d, 0x3faa, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd839dfb1, 0x7cc5425c, 0xc55b5255, 0x37b845aa, 0x59f892d4, 0x7ef8e6ec, 0x3491a7fb, 0x87b88546, 0x72b4c444, 0x24cf48d7, 0x7f530fb7, 0x5b243902, 0x6c39c38f, 0x10e3165c, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0xd839dfb1, 0x7cc5425c, 0xc55b5255, 0x37b845aa, 0x59f892d4, 0x7ef8e6ec, 0x3491a7fb, 0x87b88546, 0x72b4c444, 0x24cf48d7, 0x7f530fb7, 0x5b243902, 0x6c39c38f, 0x10e3165c, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea12, 0x1f7d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xcd24aba8, 0x1d7d0523, 0xc150da45, 0x2f7eb96b, 0x9c1ed9af, 0x75056b89, 0x91c818d1, 0x8a07d574, 0x67ff1d4a, 0x6aca4429, 0xc4b5fb7c, 0x21e9ca50, 0xb95cbd15, 0xce3752e7, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0xcd24aba8, 0x1d7d0523, 0xc150da45, 0x2f7eb96b, 0x9c1ed9af, 0x75056b89, 0x91c818d1, 0x8a07d574, 0x67ff1d4a, 0x6aca4429, 0xc4b5fb7c, 0x21e9ca50, 0xb95cbd15, 0xce3752e7, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea10, 0x2fc2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x401285a0, 0x7ab1e348, 0xf48a2615, 0x46d49913, 0xff5e9911, 0x4b4d7920, 0x8e1f921e, 0x05075455, 0x24e4acea, 0x8652e355, 0x11d0fe46, 0x0cfe7c08, 0xf326adee, 0x7fcde7ac, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0x401285a0, 0x7ab1e348, 0xf48a2615, 0x46d49913, 0xff5e9911, 0x4b4d7920, 0x8e1f921e, 0x05075455, 0x24e4acea, 0x8652e355, 0x11d0fe46, 0x0cfe7c08, 0xf326adee, 0x7fcde7ac, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x3f22, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5bb504b3, 0x3dd293c9, 0x3b2b2d7c, 0x30c2876a, 0x1c99a70e, 0x741294e7, 0xfd5f7315, 0x0149b9db, 0x3975aa1c, 0x9269e207, 0xdc42fd14, 0xea6a1c89, 0xa03e7d65, 0x171c30ad, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x5bb504b3, 0x3dd293c9, 0x3b2b2d7c, 0x30c2876a, 0x1c99a70e, 0x741294e7, 0xfd5f7315, 0x0149b9db, 0x3975aa1c, 0x9269e207, 0xdc42fd14, 0xea6a1c89, 0xa03e7d65, 0x171c30ad, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x1f86, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe0f5bbe3, 0xd96f3c62, 0x11944b25, 0x372e4b0e, 0x7c956b35, 0x03df46ac, 0x8f11684b, 0x3044502e, 0x6ebf2992, 0x4f3a0366, 0x9f36f014, 0x4c55f6aa, 0x6473e494, 0x8b6310d6, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0xe0f5bbe3, 0xd96f3c62, 0x11944b25, 0x372e4b0e, 0x7c956b35, 0x03df46ac, 0x8f11684b, 0x3044502e, 0x6ebf2992, 0x4f3a0366, 0x9f36f014, 0x4c55f6aa, 0x6473e494, 0x8b6310d6, 0x00000000, 0x200001d0 }, }, // CMP (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x6f45, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe72e848a, 0x97499b66, 0xcde944bc, 0xf6a7e4e1, 0xd8860029, 0xc55c7e43, 0x58dc13d7, 0x5e1cf6ac, 0x8094a819, 0xdba64363, 0xd8f5423f, 0x6ae843f0, 0x69766600, 0x2814e4e6, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0xe72e848a, 0x97499b66, 0xcde944bc, 0xf6a7e4e1, 0xd8860029, 0xc55c7e43, 0x58dc13d7, 0x5e1cf6ac, 0x8094a819, 0xdba64363, 0xd8f5423f, 0x6ae843f0, 0x69766600, 0x2814e4e6, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x7fd8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x323e5ef9, 0x46e23bdf, 0x8d69d89a, 0x9ffddd37, 0x53f4a07b, 0xe923f9bb, 0x5ea62678, 0x1709127c, 0xc0c20492, 0x0ee47a0c, 0xe137cc2e, 0x7d72db37, 0xca9eb971, 0x4447b224, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x323e5ef9, 0x46e23bdf, 0x8d69d89a, 0x9ffddd37, 0x53f4a07b, 0xe923f9bb, 0x5ea62678, 0x1709127c, 0xc0c20492, 0x0ee47a0c, 0xe137cc2e, 0x7d72db37, 0xca9eb971, 0x4447b224, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea10, 0x3f43, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4cce7ac7, 0x03055e03, 0x479ec669, 0x8b1d9783, 0xa59509e1, 0xa46866ef, 0x654578c4, 0x700e322b, 0xa4191329, 0xb1b8479a, 0xe555a2ce, 0x1ef22472, 0xd41fb2ae, 0x2d794684, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x4cce7ac7, 0x03055e03, 0x479ec669, 0x8b1d9783, 0xa59509e1, 0xa46866ef, 0x654578c4, 0x700e322b, 0xa4191329, 0xb1b8479a, 0xe555a2ce, 0x1ef22472, 0xd41fb2ae, 0x2d794684, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea18, 0x7fd2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xeecfbfb2, 0xbe6288fd, 0x34c0fc94, 0x3a01b105, 0xe7dc6252, 0xc05813fa, 0x6613d82d, 0x90dc7a0c, 0x34637299, 0x58f6d0e7, 0xb151d65e, 0xca975eca, 0xf83b6533, 0x10177f01, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0xeecfbfb2, 0xbe6288fd, 0x34c0fc94, 0x3a01b105, 0xe7dc6252, 0xc05813fa, 0x6613d82d, 0x90dc7a0c, 0x34637299, 0x58f6d0e7, 0xb151d65e, 0xca975eca, 0xf83b6533, 0x10177f01, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x2f6e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x54dcd0c5, 0x629131da, 0xc4010f0b, 0xf28a7f0f, 0x866d92a3, 0xbb9a1b32, 0xc8c7f425, 0x8d13d61f, 0x1f9a5d13, 0x83e0b2b7, 0x7ef44e14, 0x24c291a3, 0x851cc882, 0x31a056cb, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x54dcd0c5, 0x629131da, 0xc4010f0b, 0xf28a7f0f, 0x866d92a3, 0xbb9a1b32, 0xc8c7f425, 0x8d13d61f, 0x1f9a5d13, 0x83e0b2b7, 0x7ef44e14, 0x24c291a3, 0x851cc882, 0x31a056cb, 0x00000000, 0x500001d0 }, }, // SUB (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x6f56, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf997cd9f, 0xf94c5bd7, 0x5411a289, 0x21311b8f, 0xee8a1fe4, 0x73808b62, 0x4daadf68, 0x14a1c57c, 0x92d98c4c, 0x31f999c9, 0x953b94b9, 0x108acc75, 0xcc38ea73, 0x5dc27e61, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0xf997cd9f, 0xf94c5bd7, 0x5411a289, 0x21311b8f, 0xee8a1fe4, 0x73808b62, 0x4daadf68, 0x14a1c57c, 0x92d98c4c, 0x31f999c9, 0x953b94b9, 0x108acc75, 0xcc38ea73, 0x5dc27e61, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea19, 0x0f94, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5f8de6f4, 0x09e82020, 0x480dc701, 0xd3303ca3, 0x8739e87a, 0x3da0b6d2, 0x10093787, 0xd30606fc, 0xd81d45da, 0xa66f5e86, 0xd8ddf48e, 0xa8321bd1, 0x62a75c1c, 0x3cffac30, 0x00000000, 0x800001f0 }, FinalRegs = new uint[] { 0x5f8de6f4, 0x09e82020, 0x480dc701, 0xd3303ca3, 0x8739e87a, 0x3da0b6d2, 0x10093787, 0xd30606fc, 0xd81d45da, 0xa66f5e86, 0xd8ddf48e, 0xa8321bd1, 0x62a75c1c, 0x3cffac30, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea14, 0x7fc6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x001f39cf, 0x76b925c8, 0x292b283a, 0x9d142282, 0x2cda04fa, 0x87f29de5, 0x9e9a98e4, 0x9d48ddbb, 0x9ea329fd, 0x653f2346, 0xfc116785, 0x6e565e16, 0x9a7f8c11, 0x46f1ecbb, 0x00000000, 0xd00001f0 }, FinalRegs = new uint[] { 0x001f39cf, 0x76b925c8, 0x292b283a, 0x9d142282, 0x2cda04fa, 0x87f29de5, 0x9e9a98e4, 0x9d48ddbb, 0x9ea329fd, 0x653f2346, 0xfc116785, 0x6e565e16, 0x9a7f8c11, 0x46f1ecbb, 0x00000000, 0x500001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea19, 0x5fa5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xfb6a4a50, 0xd074ee0e, 0x599131ef, 0x5db48236, 0xf287fcd1, 0xadea3b9f, 0xf2529f30, 0x6717a5af, 0xe1a3bc40, 0xd92e291b, 0x9b0337eb, 0xcab803ed, 0x255dd8a9, 0xea0e7824, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0xfb6a4a50, 0xd074ee0e, 0x599131ef, 0x5db48236, 0xf287fcd1, 0xadea3b9f, 0xf2529f30, 0x6717a5af, 0xe1a3bc40, 0xd92e291b, 0x9b0337eb, 0xcab803ed, 0x255dd8a9, 0xea0e7824, 0x00000000, 0xb00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1c, 0x6f86, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3a000492, 0xc16be6fa, 0x20053393, 0x597617c9, 0xc30c0ac0, 0x0ed34739, 0xf964a3d4, 0x4dcf9b40, 0x93109692, 0x7ed22040, 0x1f57a26e, 0x008d29d2, 0x99b2dae8, 0xe8a14948, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x3a000492, 0xc16be6fa, 0x20053393, 0x597617c9, 0xc30c0ac0, 0x0ed34739, 0xf964a3d4, 0x4dcf9b40, 0x93109692, 0x7ed22040, 0x1f57a26e, 0x008d29d2, 0x99b2dae8, 0xe8a14948, 0x00000000, 0x200001d0 }, }, // RSB (reg) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x6f72, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x9a603e20, 0x10004fe3, 0x8c33bcef, 0x8a23db09, 0x47244c0c, 0x53417661, 0x6486ac8b, 0x5276c43b, 0x577f49a7, 0x34542492, 0xb4ac7c99, 0x5de5cb55, 0x8f6e1d72, 0x077d4a02, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x9a603e20, 0x10004fe3, 0x8c33bcef, 0x8a23db09, 0x47244c0c, 0x53417661, 0x6486ac8b, 0x5276c43b, 0x577f49a7, 0x34542492, 0xb4ac7c99, 0x5de5cb55, 0x8f6e1d72, 0x077d4a02, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x0ff3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6fdd73d7, 0xc6c4c438, 0x772312e2, 0xa57de93f, 0xa1edd64b, 0x8ee41d33, 0x85849a41, 0xac34953a, 0xb3d7c6b5, 0x439ceff1, 0xa3096172, 0x5d8f0654, 0x2e2993a3, 0xca221149, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x6fdd73d7, 0xc6c4c438, 0x772312e2, 0xa57de93f, 0xa1edd64b, 0x8ee41d33, 0x85849a41, 0xac34953a, 0xb3d7c6b5, 0x439ceff1, 0xa3096172, 0x5d8f0654, 0x2e2993a3, 0xca221149, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1b, 0x1f34, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xbefd78a5, 0x6d071150, 0xe9ce2c88, 0x2251ed54, 0x30610b17, 0x6428697e, 0xf6e940a4, 0x2395634f, 0xdabff1a3, 0x89988d57, 0x85dd20b0, 0x2ca1311d, 0xcd0748d9, 0xedf55a6f, 0x00000000, 0x800001f0 }, FinalRegs = new uint[] { 0xbefd78a5, 0x6d071150, 0xe9ce2c88, 0x2251ed54, 0x30610b17, 0x6428697e, 0xf6e940a4, 0x2395634f, 0xdabff1a3, 0x89988d57, 0x85dd20b0, 0x2ca1311d, 0xcd0748d9, 0xedf55a6f, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea16, 0x5f83, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x57018e40, 0xc4027d19, 0x33a32bd7, 0x6a75787a, 0x18f8569a, 0xbbf3a50d, 0x7f35656f, 0x66fbdad7, 0x3aa48c57, 0x39709ea2, 0x5972e4ba, 0xb2c2c772, 0x52f35620, 0x7ef9f1d6, 0x00000000, 0xd00001f0 }, FinalRegs = new uint[] { 0x57018e40, 0xc4027d19, 0x33a32bd7, 0x6a75787a, 0x18f8569a, 0xbbf3a50d, 0x7f35656f, 0x66fbdad7, 0x3aa48c57, 0x39709ea2, 0x5972e4ba, 0xb2c2c772, 0x52f35620, 0x7ef9f1d6, 0x00000000, 0x100001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xea1a, 0x0fd8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x79108ff6, 0x0cb1e662, 0x9eb9ffed, 0x1ee4d3de, 0x7a8fa20a, 0x1db7e216, 0x6fc42752, 0x9cb6cdad, 0xa497a582, 0x654c446f, 0xcbb31efc, 0x601e6995, 0xe328af35, 0x824026e7, 0x00000000, 0xd00001f0 }, @@ -514,496 +514,496 @@ namespace Ryujinx.Tests.Cpu public static readonly PrecomputedThumbTestCase[] ImmTestCases = { // TST (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf018, 0x0fd4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf5a1b919, 0x37ee0ad4, 0xec1bbb30, 0x8345ecb1, 0xf733e93e, 0x76668927, 0xa9b16176, 0x34b9678e, 0xa6167f8b, 0xea4f20a9, 0x45345e75, 0xc8a2ea55, 0xae108472, 0x67b5e3a4, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0xf5a1b919, 0x37ee0ad4, 0xec1bbb30, 0x8345ecb1, 0xf733e93e, 0x76668927, 0xa9b16176, 0x34b9678e, 0xa6167f8b, 0xea4f20a9, 0x45345e75, 0xc8a2ea55, 0xae108472, 0x67b5e3a4, 0x00000001, 0x300001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf41b, 0x1fff, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xfc928b9b, 0xeff1f0a6, 0xe1cd22ba, 0xef1e4a75, 0x10779ef3, 0x3b003004, 0x7a532842, 0x2e71a8c4, 0x62b71ce6, 0x5ffcf3ce, 0xdbe8efa1, 0x86822f2b, 0x560da6b6, 0x46550850, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0xfc928b9b, 0xeff1f0a6, 0xe1cd22ba, 0xef1e4a75, 0x10779ef3, 0x3b003004, 0x7a532842, 0x2e71a8c4, 0x62b71ce6, 0x5ffcf3ce, 0xdbe8efa1, 0x86822f2b, 0x560da6b6, 0x46550850, 0x00000001, 0x100001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf416, 0x7f97, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd0ba5d0a, 0xc8fa5c53, 0xac3069cb, 0x4be76d89, 0xcc9b4f47, 0x36984914, 0xd49fe0a5, 0x7d80c756, 0x8210fb6d, 0xcb498541, 0xc366597f, 0xacef4405, 0xdf6341a9, 0x6a1124b8, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0xd0ba5d0a, 0xc8fa5c53, 0xac3069cb, 0x4be76d89, 0xcc9b4f47, 0x36984914, 0xd49fe0a5, 0x7d80c756, 0x8210fb6d, 0xcb498541, 0xc366597f, 0xacef4405, 0xdf6341a9, 0x6a1124b8, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf016, 0x0f12, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xb8568ac2, 0x67a7ee09, 0x266abe3b, 0x9d93101d, 0x504b4adb, 0x45838822, 0x62126cc4, 0xf4198159, 0xf24a524c, 0x163fa3e9, 0x3c6d489e, 0xacef0dff, 0x73fc8fdd, 0x9d34fc09, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0xb8568ac2, 0x67a7ee09, 0x266abe3b, 0x9d93101d, 0x504b4adb, 0x45838822, 0x62126cc4, 0xf4198159, 0xf24a524c, 0x163fa3e9, 0x3c6d489e, 0xacef0dff, 0x73fc8fdd, 0x9d34fc09, 0x00000001, 0x400001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf017, 0x2fd1, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5548cee0, 0x59a88eb4, 0x3624d775, 0x98fe9a19, 0x84d1b83f, 0xed2c476a, 0x046a6aea, 0x0c92fadb, 0xdff5abe1, 0x91a16e82, 0xbb0f8ba4, 0x87c1888c, 0xa2df958e, 0x6cebba03, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0x5548cee0, 0x59a88eb4, 0x3624d775, 0x98fe9a19, 0x84d1b83f, 0xed2c476a, 0x046a6aea, 0x0c92fadb, 0xdff5abe1, 0x91a16e82, 0xbb0f8ba4, 0x87c1888c, 0xa2df958e, 0x6cebba03, 0x00000001, 0x000001f0 }, }, // AND (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf403, 0x3ce5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xddd90378, 0xc3a2891f, 0x3d5007a2, 0x0f0c0756, 0xbdc5c113, 0xeee78000, 0x90693126, 0x8763b349, 0xbf6814b4, 0x40160bf9, 0xfff4a26d, 0x16a11d59, 0x26b3b8cc, 0xeb09487f, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0xddd90378, 0xc3a2891f, 0x3d5007a2, 0x0f0c0756, 0xbdc5c113, 0xeee78000, 0x90693126, 0x8763b349, 0xbf6814b4, 0x40160bf9, 0xfff4a26d, 0x16a11d59, 0x00000200, 0xeb09487f, 0x00000001, 0x200001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf00a, 0x177d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xce4db266, 0x67f21be8, 0x0de0c290, 0x2615cfc5, 0x6e3c46cb, 0x44a52240, 0xb12e0470, 0x903e0182, 0x61c32a9a, 0x58bf0753, 0xa9b3c209, 0x68ec1f37, 0x9320a3c9, 0xab952fd9, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0xce4db266, 0x67f21be8, 0x0de0c290, 0x2615cfc5, 0x6e3c46cb, 0x44a52240, 0xb12e0470, 0x00310009, 0x61c32a9a, 0x58bf0753, 0xa9b3c209, 0x68ec1f37, 0x9320a3c9, 0xab952fd9, 0x00000001, 0xa00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf014, 0x2913, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4257c8eb, 0xd9eaf199, 0xe9381b0a, 0x39fcb309, 0xb1f24181, 0xebb2b7e4, 0x73799a0f, 0xc70a2fc7, 0xe2af6496, 0xb9014f5b, 0xe22ff568, 0x12dd4afe, 0x6d8544ac, 0x9293d043, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x4257c8eb, 0xd9eaf199, 0xe9381b0a, 0x39fcb309, 0xb1f24181, 0xebb2b7e4, 0x73799a0f, 0xc70a2fc7, 0xe2af6496, 0x11000100, 0xe22ff568, 0x12dd4afe, 0x6d8544ac, 0x9293d043, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf004, 0x27c2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x8d7ceb35, 0x000e9260, 0x6825b561, 0xbcf66952, 0x3fbc7775, 0xd5afaa83, 0xe4fde261, 0xa35fa71e, 0xbefc5c9f, 0x667d9163, 0x8c2543b0, 0xd8489b89, 0x661ffec5, 0x45ccdaa8, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x8d7ceb35, 0x000e9260, 0x6825b561, 0xbcf66952, 0x3fbc7775, 0xd5afaa83, 0xe4fde261, 0x02004200, 0xbefc5c9f, 0x667d9163, 0x8c2543b0, 0xd8489b89, 0x661ffec5, 0x45ccdaa8, 0x00000001, 0xd00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf008, 0x6ce5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x78a2a638, 0x54ac2bd7, 0x60ad5509, 0x9c38b11f, 0x8dd109de, 0xd07aea77, 0xf5a0bcfc, 0xbcd81a17, 0x2f159ebd, 0xcc7e8454, 0x04621cce, 0xd0e9eca5, 0xb33f4ba6, 0x1e2bb5b2, 0x00000001, 0xf00001f0 }, FinalRegs = new uint[] { 0x78a2a638, 0x54ac2bd7, 0x60ad5509, 0x9c38b11f, 0x8dd109de, 0xd07aea77, 0xf5a0bcfc, 0xbcd81a17, 0x2f159ebd, 0xcc7e8454, 0x04621cce, 0xd0e9eca5, 0x07000000, 0x1e2bb5b2, 0x00000001, 0xf00001f0 }, }, // BIC (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf420, 0x6425, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x28c2ef44, 0x206bdfed, 0x30c780a9, 0x440ab4ab, 0x666fc882, 0x92a4aa1d, 0x3ceb6b36, 0xca757a75, 0xdf2f77b7, 0xae012305, 0x06b5c956, 0x0ff05e78, 0xad918973, 0x73778e28, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x28c2ef44, 0x206bdfed, 0x30c780a9, 0x440ab4ab, 0x28c2e504, 0x92a4aa1d, 0x3ceb6b36, 0xca757a75, 0xdf2f77b7, 0xae012305, 0x06b5c956, 0x0ff05e78, 0xad918973, 0x73778e28, 0x00000001, 0xe00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf430, 0x44ed, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5cd96673, 0xa1c27ac5, 0xbb205490, 0xa844d844, 0x1e66662f, 0x0f259402, 0xbe81472f, 0x36d55b13, 0x02c6d2a2, 0xc39c31b1, 0x59b71936, 0xf1914252, 0xef8188b8, 0x0c18bea1, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0x5cd96673, 0xa1c27ac5, 0xbb205490, 0xa844d844, 0x5cd90073, 0x0f259402, 0xbe81472f, 0x36d55b13, 0x02c6d2a2, 0xc39c31b1, 0x59b71936, 0xf1914252, 0xef8188b8, 0x0c18bea1, 0x00000001, 0x100001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf038, 0x1334, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe0afc0e0, 0x7ffd49d1, 0x6fe858fa, 0x7564882e, 0xaa895bf1, 0x725f4071, 0x612b9956, 0xb28fd700, 0xf0acd15c, 0xb62cf0bb, 0x1d9d5c1f, 0xdc3942a2, 0x9b3248ea, 0x3b7593ca, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0xe0afc0e0, 0x7ffd49d1, 0x6fe858fa, 0xf088d148, 0xaa895bf1, 0x725f4071, 0x612b9956, 0xb28fd700, 0xf0acd15c, 0xb62cf0bb, 0x1d9d5c1f, 0xdc3942a2, 0x9b3248ea, 0x3b7593ca, 0x00000001, 0xb00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf438, 0x51d7, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x75f070b0, 0x24e410c6, 0x128174fa, 0x04a10755, 0x6728fd35, 0xf6007f21, 0x0cb9efa3, 0x260e061c, 0xc5e02c94, 0x4aaa3354, 0x00796ab8, 0x897274d2, 0xe87dcffc, 0xa47bd3ab, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x75f070b0, 0xc5e02414, 0x128174fa, 0x04a10755, 0x6728fd35, 0xf6007f21, 0x0cb9efa3, 0x260e061c, 0xc5e02c94, 0x4aaa3354, 0x00796ab8, 0x897274d2, 0xe87dcffc, 0xa47bd3ab, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf438, 0x1db2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x62dcbe9e, 0x55f42016, 0x6689f461, 0x31e32805, 0x1fc90a1e, 0x02e3a47f, 0xf236bafd, 0x65006290, 0x0065bd7f, 0xc1752579, 0x59528615, 0x6ef68c79, 0x138b8bb3, 0x0761d66c, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x62dcbe9e, 0x55f42016, 0x6689f461, 0x31e32805, 0x1fc90a1e, 0x02e3a47f, 0xf236bafd, 0x65006290, 0x0065bd7f, 0xc1752579, 0x59528615, 0x6ef68c79, 0x138b8bb3, 0x0061bd7f, 0x00000001, 0x000001f0 }, }, // MOV (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf45f, 0x5032, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6ed98807, 0x536a9cb7, 0x79c2d5bb, 0xc2e9a860, 0x185b4e57, 0x77c1c99f, 0x99a24897, 0xc6cc4ea1, 0xe3d294a6, 0xb8e525ae, 0xca245840, 0x27943892, 0xa76ed6fe, 0xecbcffe8, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x00002c80, 0x536a9cb7, 0x79c2d5bb, 0xc2e9a860, 0x185b4e57, 0x77c1c99f, 0x99a24897, 0xc6cc4ea1, 0xe3d294a6, 0xb8e525ae, 0xca245840, 0x27943892, 0xa76ed6fe, 0xecbcffe8, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf04f, 0x23f9, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x7e6bf90e, 0x87533a8f, 0x29389323, 0x96a37f30, 0x63e6e58e, 0xb8bb21d0, 0x5bd9ae04, 0x26b7a586, 0xfa359510, 0x131a4e95, 0x5d0adb02, 0xa8148f64, 0xbfe74669, 0xea2cdf2d, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0x7e6bf90e, 0x87533a8f, 0x29389323, 0xf900f900, 0x63e6e58e, 0xb8bb21d0, 0x5bd9ae04, 0x26b7a586, 0xfa359510, 0x131a4e95, 0x5d0adb02, 0xa8148f64, 0xbfe74669, 0xea2cdf2d, 0x00000001, 0xb00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf45f, 0x5351, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd8b5d8b2, 0x7ae44f2b, 0xda909eb2, 0xdb2fe423, 0xb2486971, 0x23427a2c, 0x96c88749, 0xb88d6d78, 0x2f4aa092, 0xbf40760f, 0x88d72a3f, 0x88854e62, 0x8d459486, 0x82a8ba9f, 0x00000001, 0x300001f0 }, FinalRegs = new uint[] { 0xd8b5d8b2, 0x7ae44f2b, 0xda909eb2, 0x00003440, 0xb2486971, 0x23427a2c, 0x96c88749, 0xb88d6d78, 0x2f4aa092, 0xbf40760f, 0x88d72a3f, 0x88854e62, 0x8d459486, 0x82a8ba9f, 0x00000001, 0x100001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf45f, 0x207c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x73063041, 0x038b5f4a, 0xf7e85421, 0xe935f110, 0x9a5c34de, 0xbc3dbed9, 0x1c57c517, 0x3294067f, 0x01cd78b5, 0xe7bfe428, 0x6e297fce, 0xccb2c833, 0x2e8bb930, 0xeb6e2004, 0x00000001, 0x300001f0 }, FinalRegs = new uint[] { 0x000fc000, 0x038b5f4a, 0xf7e85421, 0xe935f110, 0x9a5c34de, 0xbc3dbed9, 0x1c57c517, 0x3294067f, 0x01cd78b5, 0xe7bfe428, 0x6e297fce, 0xccb2c833, 0x2e8bb930, 0xeb6e2004, 0x00000001, 0x100001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf45f, 0x5073, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd0d531fe, 0xeb1676df, 0x0acf5912, 0x6061b3fe, 0xecac2ae2, 0x40075143, 0x88a47781, 0x3ecb7baa, 0x6aee3603, 0x53133f32, 0x1e891e57, 0x4d7f8f94, 0xd09c727a, 0x28a79c93, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0x00003cc0, 0xeb1676df, 0x0acf5912, 0x6061b3fe, 0xecac2ae2, 0x40075143, 0x88a47781, 0x3ecb7baa, 0x6aee3603, 0x53133f32, 0x1e891e57, 0x4d7f8f94, 0xd09c727a, 0x28a79c93, 0x00000001, 0x000001f0 }, }, // ORR (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf45c, 0x03c9, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc7d9e145, 0xc2a6bd5b, 0x92c8ca0b, 0x1fde4d2f, 0xa4705c7f, 0x47559e93, 0x9f9d3e22, 0x7b3719c0, 0x3746ffc9, 0xa1476ae8, 0x88f45e36, 0x0fc7d2a7, 0xaa94b64c, 0xe9fee33b, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0xc7d9e145, 0xc2a6bd5b, 0x92c8ca0b, 0xaaf4b64c, 0xa4705c7f, 0x47559e93, 0x9f9d3e22, 0x7b3719c0, 0x3746ffc9, 0xa1476ae8, 0x88f45e36, 0x0fc7d2a7, 0xaa94b64c, 0xe9fee33b, 0x00000001, 0x900001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf441, 0x60ec, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2262c23f, 0xf8ba9254, 0x2a870feb, 0xa66d2c1a, 0xa3bb8f6d, 0x2f754de2, 0xb3b0b9be, 0xc3cf59e8, 0xebaa6300, 0x22ea8a3d, 0xf3bcf0f4, 0xffb0aae8, 0x4982d5ab, 0x4c945119, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0xf8ba9774, 0xf8ba9254, 0x2a870feb, 0xa66d2c1a, 0xa3bb8f6d, 0x2f754de2, 0xb3b0b9be, 0xc3cf59e8, 0xebaa6300, 0x22ea8a3d, 0xf3bcf0f4, 0xffb0aae8, 0x4982d5ab, 0x4c945119, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf44c, 0x5343, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x844cd5c4, 0x5a244353, 0xd74ff677, 0x25eefc9f, 0xa040f56f, 0x06e237a6, 0x7ccb1c91, 0xc9aa6d32, 0xf9e18bd6, 0xc0780954, 0x955d8f60, 0xa9cb014e, 0x64d583e2, 0x3e50533a, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x844cd5c4, 0x5a244353, 0xd74ff677, 0x64d5b3e2, 0xa040f56f, 0x06e237a6, 0x7ccb1c91, 0xc9aa6d32, 0xf9e18bd6, 0xc0780954, 0x955d8f60, 0xa9cb014e, 0x64d583e2, 0x3e50533a, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf040, 0x48e2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x24423eae, 0x40f7667e, 0x017f283e, 0x72887399, 0x063f4da0, 0x9b57a1c5, 0x5500c630, 0x6a304cac, 0xf9f10e9a, 0x02cdd193, 0x3f42bccd, 0x3c52ef2e, 0x15858a11, 0x25fd30bf, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x24423eae, 0x40f7667e, 0x017f283e, 0x72887399, 0x063f4da0, 0x9b57a1c5, 0x5500c630, 0x6a304cac, 0x75423eae, 0x02cdd193, 0x3f42bccd, 0x3c52ef2e, 0x15858a11, 0x25fd30bf, 0x00000001, 0xc00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf455, 0x1de0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc8c22d0e, 0x98a19d05, 0x61b4ea5e, 0x52f6f9a0, 0x2f8ceae4, 0x15649771, 0x61953174, 0x45b9d93f, 0x4e0629af, 0x30f43259, 0x863e8e5c, 0x3310b69e, 0xae5e5b9d, 0xf00e065a, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0xc8c22d0e, 0x98a19d05, 0x61b4ea5e, 0x52f6f9a0, 0x2f8ceae4, 0x15649771, 0x61953174, 0x45b9d93f, 0x4e0629af, 0x30f43259, 0x863e8e5c, 0x3310b69e, 0xae5e5b9d, 0x157c9771, 0x00000001, 0x100001f0 }, }, // MVN (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf46f, 0x1681, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xb1267a38, 0xe72b03aa, 0x50dc392a, 0xaff74b0d, 0xf83a17ba, 0xb8edf09d, 0x799df56d, 0x1ecbd371, 0xb4a74b9a, 0xe79f52fb, 0xbcec8b62, 0xbb0b01ea, 0x26d72e8c, 0x1d2ac349, 0x00000001, 0x900001f0 }, FinalRegs = new uint[] { 0xb1267a38, 0xe72b03aa, 0x50dc392a, 0xaff74b0d, 0xf83a17ba, 0xb8edf09d, 0xffefdfff, 0x1ecbd371, 0xb4a74b9a, 0xe79f52fb, 0xbcec8b62, 0xbb0b01ea, 0x26d72e8c, 0x1d2ac349, 0x00000001, 0x900001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf07f, 0x572f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xa95387ad, 0x256c4ece, 0x32084d7a, 0x84935d58, 0x12f6880b, 0x3b386e47, 0xbeb69796, 0xdcf3fac5, 0xee2f9386, 0x25372541, 0x56499ba6, 0x06fa7586, 0xd114f908, 0x3442736e, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0xa95387ad, 0x256c4ece, 0x32084d7a, 0x84935d58, 0x12f6880b, 0x3b386e47, 0xbeb69796, 0xd43fffff, 0xee2f9386, 0x25372541, 0x56499ba6, 0x06fa7586, 0xd114f908, 0x3442736e, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf46f, 0x17e3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd7f2d1e1, 0x1d12b22c, 0x2c26620c, 0xeadb8ead, 0x73560a2e, 0xf521b384, 0x4094f3d2, 0x17ed0f6f, 0x79d30498, 0x6d47211a, 0x8fdfef1d, 0xce6cbfa7, 0x75dc1c1b, 0x2ffd5d28, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0xd7f2d1e1, 0x1d12b22c, 0x2c26620c, 0xeadb8ead, 0x73560a2e, 0xf521b384, 0x4094f3d2, 0xffe39fff, 0x79d30498, 0x6d47211a, 0x8fdfef1d, 0xce6cbfa7, 0x75dc1c1b, 0x2ffd5d28, 0x00000001, 0x700001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf07f, 0x1431, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4154dce7, 0x66c452e9, 0xff9bea1b, 0x228a4a5e, 0xe9fee66b, 0xddd7117f, 0x303cdcb6, 0x4bdf78a2, 0xfbcca92c, 0x2f628d24, 0x51816529, 0xcdea5042, 0x77a1e4a2, 0x8a745cb4, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0x4154dce7, 0x66c452e9, 0xff9bea1b, 0x228a4a5e, 0xffceffce, 0xddd7117f, 0x303cdcb6, 0x4bdf78a2, 0xfbcca92c, 0x2f628d24, 0x51816529, 0xcdea5042, 0x77a1e4a2, 0x8a745cb4, 0x00000001, 0xa00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf07f, 0x73ac, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd7b60274, 0x1ff3baba, 0xfdc8fa51, 0xcfacae9d, 0xd27a8214, 0xbbfb1abf, 0x3766111f, 0x89af2196, 0x4bd14cd6, 0x5af84659, 0xd279ed2f, 0x7abdf656, 0x868a6980, 0xd343d52a, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0xd7b60274, 0x1ff3baba, 0xfdc8fa51, 0xfea7ffff, 0xd27a8214, 0xbbfb1abf, 0x3766111f, 0x89af2196, 0x4bd14cd6, 0x5af84659, 0xd279ed2f, 0x7abdf656, 0x868a6980, 0xd343d52a, 0x00000001, 0x900001f0 }, }, // ORN (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf464, 0x0976, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x02e1c999, 0x40c2ff04, 0x16f00059, 0xd360cd62, 0xcb34f9d2, 0x303b434a, 0x53e0151f, 0x188b36bc, 0x84868958, 0xebad0ada, 0xdcd0cb74, 0x64bc056c, 0xd17a7256, 0xb71ddae3, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x02e1c999, 0x40c2ff04, 0x16f00059, 0xd360cd62, 0xcb34f9d2, 0x303b434a, 0x53e0151f, 0x188b36bc, 0x84868958, 0xff3dffff, 0xdcd0cb74, 0x64bc056c, 0xd17a7256, 0xb71ddae3, 0x00000001, 0x500001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf477, 0x3c66, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x71910713, 0xd17f8e75, 0x2652c7ac, 0xfad0527a, 0xc52b726d, 0x29e66793, 0xa1011225, 0x00c8ecc1, 0x48af4edd, 0x5c4e2e67, 0xc5393bd5, 0x702fcda1, 0x4549b1cf, 0x72d5a971, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x71910713, 0xd17f8e75, 0x2652c7ac, 0xfad0527a, 0xc52b726d, 0x29e66793, 0xa1011225, 0x00c8ecc1, 0x48af4edd, 0x5c4e2e67, 0xc5393bd5, 0x702fcda1, 0xfffcefff, 0x72d5a971, 0x00000001, 0x900001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf479, 0x1270, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x91060c85, 0x9b9c9033, 0x771ac325, 0x001e17c8, 0xb1adee43, 0xbaa9ec02, 0xf57f9f83, 0x3fed4e5c, 0x198cc3ea, 0x1a40edde, 0x6844391b, 0xa03319a0, 0xf741e11b, 0xc1892487, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x91060c85, 0x9b9c9033, 0xffc3ffff, 0x001e17c8, 0xb1adee43, 0xbaa9ec02, 0xf57f9f83, 0x3fed4e5c, 0x198cc3ea, 0x1a40edde, 0x6844391b, 0xa03319a0, 0xf741e11b, 0xc1892487, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf46f, 0x19d4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4fd5b2bd, 0x1c8f29ae, 0x12803c79, 0x93683874, 0xccd779c1, 0x6978c335, 0x06eb789d, 0xc8b74ef8, 0x51ca145a, 0x242d8047, 0x5036f51f, 0x13a4a4a2, 0x08818ae4, 0xe1687e67, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x4fd5b2bd, 0x1c8f29ae, 0x12803c79, 0x93683874, 0xccd779c1, 0x6978c335, 0x06eb789d, 0xc8b74ef8, 0x51ca145a, 0xffe57fff, 0x5036f51f, 0x13a4a4a2, 0x08818ae4, 0xe1687e67, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf07f, 0x614f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x83c9ef5a, 0xb5933c7e, 0x2dc23d71, 0x5723ae27, 0x1218bc2c, 0x456f3dbd, 0xf6ee7d22, 0xde4df878, 0x3e800973, 0x39c4c131, 0x0676384d, 0xef62a558, 0x2acc92f2, 0x9cd71aa1, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0x83c9ef5a, 0xf30fffff, 0x2dc23d71, 0x5723ae27, 0x1218bc2c, 0x456f3dbd, 0xf6ee7d22, 0xde4df878, 0x3e800973, 0x39c4c131, 0x0676384d, 0xef62a558, 0x2acc92f2, 0x9cd71aa1, 0x00000001, 0x900001f0 }, }, // TEQ (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf49b, 0x2fe4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc87047c3, 0x99001273, 0xa963adc7, 0xaba3d1a1, 0x4b9c13a0, 0xc42566ba, 0xee0b7ab1, 0x3e4423ec, 0x5d874e97, 0xfffb5799, 0xdb88f462, 0xbdc4a9e2, 0x3933e52b, 0xe1839111, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0xc87047c3, 0x99001273, 0xa963adc7, 0xaba3d1a1, 0x4b9c13a0, 0xc42566ba, 0xee0b7ab1, 0x3e4423ec, 0x5d874e97, 0xfffb5799, 0xdb88f462, 0xbdc4a9e2, 0x3933e52b, 0xe1839111, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf09b, 0x0f59, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6d2c2ac7, 0xdd2b59f4, 0x3fc013f4, 0x567e744e, 0xc4feb096, 0x188454f3, 0xae13338b, 0x66a0a40b, 0xac995945, 0x7e27f097, 0x547cbd54, 0xd2abf0ab, 0x02c08b3e, 0xe6d1283f, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x6d2c2ac7, 0xdd2b59f4, 0x3fc013f4, 0x567e744e, 0xc4feb096, 0x188454f3, 0xae13338b, 0x66a0a40b, 0xac995945, 0x7e27f097, 0x547cbd54, 0xd2abf0ab, 0x02c08b3e, 0xe6d1283f, 0x00000001, 0x900001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf494, 0x6f3d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x16342d21, 0xff794fb0, 0x513ba230, 0x7b9e4b2b, 0x9a2d1ba9, 0xebce0dae, 0xe792f2b8, 0xf4932236, 0x0bcd9542, 0x12bcab94, 0x0110b845, 0xdde237b0, 0xa401d5b9, 0xc3162f6d, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x16342d21, 0xff794fb0, 0x513ba230, 0x7b9e4b2b, 0x9a2d1ba9, 0xebce0dae, 0xe792f2b8, 0xf4932236, 0x0bcd9542, 0x12bcab94, 0x0110b845, 0xdde237b0, 0xa401d5b9, 0xc3162f6d, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf09c, 0x6f59, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x8d9e2002, 0xfa519294, 0x700740d6, 0x29220c73, 0x8f0ad8b2, 0x6ce9d5e8, 0x12f9da7a, 0x286a9813, 0x2be49d73, 0x16241aa1, 0xe096f43b, 0x1fd0d3e2, 0x31791bb5, 0xa4943f4e, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x8d9e2002, 0xfa519294, 0x700740d6, 0x29220c73, 0x8f0ad8b2, 0x6ce9d5e8, 0x12f9da7a, 0x286a9813, 0x2be49d73, 0x16241aa1, 0xe096f43b, 0x1fd0d3e2, 0x31791bb5, 0xa4943f4e, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf094, 0x6f35, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x222e0e7c, 0xa89d1fdf, 0xa7d67bc3, 0x658e1ee9, 0x10b41780, 0x5cd566a4, 0xce03a58a, 0x63fb9a9e, 0x4f5cb2bd, 0x14e72619, 0x296a9bd5, 0xbf7b1fb1, 0x705a45cc, 0xba8540ae, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x222e0e7c, 0xa89d1fdf, 0xa7d67bc3, 0x658e1ee9, 0x10b41780, 0x5cd566a4, 0xce03a58a, 0x63fb9a9e, 0x4f5cb2bd, 0x14e72619, 0x296a9bd5, 0xbf7b1fb1, 0x705a45cc, 0xba8540ae, 0x00000001, 0x000001f0 }, }, // EOR (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf496, 0x54fb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6267728b, 0xc834f7c7, 0xa136a1d6, 0xfd9533e9, 0x096db729, 0x8fff8a73, 0x6a45348e, 0xd52111ed, 0xa5640aff, 0xa4cf82a6, 0x5ab70b5c, 0x5b3c4563, 0xf1a91ab7, 0x5718fdd1, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x6267728b, 0xc834f7c7, 0xa136a1d6, 0xfd9533e9, 0x6a452bee, 0x8fff8a73, 0x6a45348e, 0xd52111ed, 0xa5640aff, 0xa4cf82a6, 0x5ab70b5c, 0x5b3c4563, 0xf1a91ab7, 0x5718fdd1, 0x00000001, 0x100001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf08a, 0x339d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xbf1e6da6, 0x2c10408a, 0xe961ddde, 0x5add8306, 0xc266064d, 0xa79569e1, 0x945c28ed, 0xb996f578, 0x68082b6e, 0x14cdd2c7, 0x7d0cc6a2, 0x8d6edfbf, 0x9151e24c, 0x63eaee32, 0x00000001, 0x300001f0 }, FinalRegs = new uint[] { 0xbf1e6da6, 0x2c10408a, 0xe961ddde, 0xe0915b3f, 0xc266064d, 0xa79569e1, 0x945c28ed, 0xb996f578, 0x68082b6e, 0x14cdd2c7, 0x7d0cc6a2, 0x8d6edfbf, 0x9151e24c, 0x63eaee32, 0x00000001, 0x300001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf490, 0x27d8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd6826a86, 0x39aa35f5, 0x2a8a913e, 0xd9dbf560, 0xcb1a9957, 0xe6779d2f, 0x0eeab3f9, 0xa463d4c2, 0xb3187660, 0xa51778c3, 0x73817179, 0x6d6dae92, 0x864a3e80, 0x43d8f181, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0xd6826a86, 0x39aa35f5, 0x2a8a913e, 0xd9dbf560, 0xcb1a9957, 0xe6779d2f, 0x0eeab3f9, 0xd684aa86, 0xb3187660, 0xa51778c3, 0x73817179, 0x6d6dae92, 0x864a3e80, 0x43d8f181, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf485, 0x3d32, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x063885c0, 0xa183a44d, 0x5cb2f961, 0xe44b8670, 0x8ec25495, 0xb8f5a831, 0x1c2fecb4, 0xfc15fcff, 0x28dd902e, 0xf0c875f4, 0x0af03bb5, 0xefe4ba8b, 0x10e57000, 0x4cd51767, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0x063885c0, 0xa183a44d, 0x5cb2f961, 0xe44b8670, 0x8ec25495, 0xb8f5a831, 0x1c2fecb4, 0xfc15fcff, 0x28dd902e, 0xf0c875f4, 0x0af03bb5, 0xefe4ba8b, 0x10e57000, 0xb8f76031, 0x00000001, 0xb00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf095, 0x58e8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5a60610b, 0x4d178413, 0x3b12edd0, 0x23afc7fc, 0x47f0647d, 0x327bd294, 0x52351d80, 0x36733323, 0x490a0d2a, 0x75d5888c, 0x9b45f4e6, 0x89ebf7dc, 0xd278dd78, 0x1b9b0bbd, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0x5a60610b, 0x4d178413, 0x3b12edd0, 0x23afc7fc, 0x47f0647d, 0x327bd294, 0x52351d80, 0x36733323, 0x2f7bd294, 0x75d5888c, 0x9b45f4e6, 0x89ebf7dc, 0xd278dd78, 0x1b9b0bbd, 0x00000001, 0x000001f0 }, }, // CMN (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf514, 0x6f12, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xec864396, 0xe2f483b8, 0x18df08c9, 0xae7780ba, 0xd16bc913, 0x892037de, 0x84a3589e, 0x3a468960, 0x004f92e4, 0x6fd793c2, 0x81b048c6, 0xe044e7cf, 0x2199ccda, 0x4667415d, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0xec864396, 0xe2f483b8, 0x18df08c9, 0xae7780ba, 0xd16bc913, 0x892037de, 0x84a3589e, 0x3a468960, 0x004f92e4, 0x6fd793c2, 0x81b048c6, 0xe044e7cf, 0x2199ccda, 0x4667415d, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf517, 0x2f38, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x154548b0, 0x28aed64c, 0x533306b3, 0x8eace432, 0x9a6523f1, 0x22b08ccb, 0xe7fceaf6, 0x45429c2c, 0xf58378c1, 0x0ef49416, 0x88dbd472, 0xf6a35b6c, 0x46b19364, 0x52e4982d, 0x00000001, 0x900001f0 }, FinalRegs = new uint[] { 0x154548b0, 0x28aed64c, 0x533306b3, 0x8eace432, 0x9a6523f1, 0x22b08ccb, 0xe7fceaf6, 0x45429c2c, 0xf58378c1, 0x0ef49416, 0x88dbd472, 0xf6a35b6c, 0x46b19364, 0x52e4982d, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf116, 0x7fe2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x30b90186, 0xec7b038f, 0xcb392feb, 0x10c09c2f, 0x8619521d, 0xcf8d7075, 0x108f8f49, 0x6e44275d, 0x1728faed, 0xf2a0b2a4, 0x783cf97f, 0x201d6d0b, 0x317f276d, 0x5a7186e2, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x30b90186, 0xec7b038f, 0xcb392feb, 0x10c09c2f, 0x8619521d, 0xcf8d7075, 0x108f8f49, 0x6e44275d, 0x1728faed, 0xf2a0b2a4, 0x783cf97f, 0x201d6d0b, 0x317f276d, 0x5a7186e2, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf51b, 0x7f4a, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xedd3c03d, 0xd7cce5a2, 0xfc40b40a, 0x6a9c96f3, 0x40ca8c2d, 0xaa2973e1, 0xd7953408, 0xfa11d2df, 0x7cec28c2, 0x4e523380, 0x007a4ac6, 0x03890c29, 0xd1495b3e, 0xdf1af969, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0xedd3c03d, 0xd7cce5a2, 0xfc40b40a, 0x6a9c96f3, 0x40ca8c2d, 0xaa2973e1, 0xd7953408, 0xfa11d2df, 0x7cec28c2, 0x4e523380, 0x007a4ac6, 0x03890c29, 0xd1495b3e, 0xdf1af969, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf11c, 0x5f9c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd8c0c360, 0xd50bcc87, 0xe265e8b2, 0xca49cc71, 0xa6bb11c8, 0x13649388, 0x034a4c8c, 0xa3b4c570, 0x014d32ac, 0x1847d102, 0x7fc3678d, 0xb0e0f469, 0x9508a619, 0x2a2372e0, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0xd8c0c360, 0xd50bcc87, 0xe265e8b2, 0xca49cc71, 0xa6bb11c8, 0x13649388, 0x034a4c8c, 0xa3b4c570, 0x014d32ac, 0x1847d102, 0x7fc3678d, 0xb0e0f469, 0x9508a619, 0x2a2372e0, 0x00000001, 0x800001f0 }, }, // ADD (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf10b, 0x00e0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xadc2fe68, 0xa8a14518, 0x5baf5e87, 0x17e2b502, 0x638227c2, 0xba11428f, 0x98c5b963, 0x5b9cbcd3, 0xb4c11f97, 0x0ca6832e, 0xea26efa6, 0x7bb19ec8, 0x8ea04a89, 0x62d597c2, 0x00000001, 0x300001f0 }, FinalRegs = new uint[] { 0x7bb19fa8, 0xa8a14518, 0x5baf5e87, 0x17e2b502, 0x638227c2, 0xba11428f, 0x98c5b963, 0x5b9cbcd3, 0xb4c11f97, 0x0ca6832e, 0xea26efa6, 0x7bb19ec8, 0x8ea04a89, 0x62d597c2, 0x00000001, 0x300001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf114, 0x7b41, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x9366f694, 0x02670e58, 0x6f3e74b8, 0x567d3e30, 0xeebb29c4, 0xc25ce8e6, 0x942b94c8, 0xc7dccdd9, 0xccfe17a9, 0xeacc4db1, 0xbbbc0fde, 0x248b7093, 0x7f66c92d, 0xfc063cb6, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x9366f694, 0x02670e58, 0x6f3e74b8, 0x567d3e30, 0xeebb29c4, 0xc25ce8e6, 0x942b94c8, 0xc7dccdd9, 0xccfe17a9, 0xeacc4db1, 0xbbbc0fde, 0xf1bf29c4, 0x7f66c92d, 0xfc063cb6, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf51c, 0x21d1, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2b53ae1e, 0x733046c3, 0xbcc33a3a, 0x2f7bbd50, 0xed2a39f2, 0xfee631ec, 0xeb6d3bc3, 0x9f9b502d, 0x30d20f7b, 0xdc75211b, 0xdb234e2b, 0x85008c86, 0x43beb508, 0x6a8303d5, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x2b53ae1e, 0x43c53d08, 0xbcc33a3a, 0x2f7bbd50, 0xed2a39f2, 0xfee631ec, 0xeb6d3bc3, 0x9f9b502d, 0x30d20f7b, 0xdc75211b, 0xdb234e2b, 0x85008c86, 0x43beb508, 0x6a8303d5, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf513, 0x22e8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xbb3b52c1, 0x40ff59f3, 0x05ca09c5, 0x440114be, 0xec3a4022, 0x0ff93d8c, 0x38868879, 0x824d36d8, 0xf513a9d8, 0xf1d0ad5a, 0xc453fdd8, 0xe3dc8d52, 0x1fc5a9ef, 0x809dbe9b, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0xbb3b52c1, 0x40ff59f3, 0x440854be, 0x440114be, 0xec3a4022, 0x0ff93d8c, 0x38868879, 0x824d36d8, 0xf513a9d8, 0xf1d0ad5a, 0xc453fdd8, 0xe3dc8d52, 0x1fc5a9ef, 0x809dbe9b, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf518, 0x68c7, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf9c00a78, 0xf47ee408, 0xdc31e40b, 0x167902da, 0x03b23f2f, 0x6d41efdc, 0x9cb99b17, 0x21bfbf63, 0x9fbe8105, 0x250087d0, 0xe0588965, 0x0f0f669c, 0x2ed04b37, 0xc65c6e2e, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0xf9c00a78, 0xf47ee408, 0xdc31e40b, 0x167902da, 0x03b23f2f, 0x6d41efdc, 0x9cb99b17, 0x21bfbf63, 0x9fbe873d, 0x250087d0, 0xe0588965, 0x0f0f669c, 0x2ed04b37, 0xc65c6e2e, 0x00000001, 0x800001f0 }, }, // ADC (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf54d, 0x379a, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x09eb57e5, 0xc9981095, 0x94b0bf26, 0x27080c39, 0x9fba115a, 0xde0e1533, 0xaa5916aa, 0x1bfc2313, 0x32a96f13, 0x5b8f2d6c, 0x9098dcf2, 0x86143a3f, 0x5c004908, 0xd233cd08, 0x00000001, 0x300001f0 }, FinalRegs = new uint[] { 0x09eb57e5, 0xc9981095, 0x94b0bf26, 0x27080c39, 0x9fba115a, 0xde0e1533, 0xaa5916aa, 0xd2350109, 0x32a96f13, 0x5b8f2d6c, 0x9098dcf2, 0x86143a3f, 0x5c004908, 0xd233cd08, 0x00000001, 0x300001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf149, 0x3a77, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe32aaf45, 0x05fe0eac, 0x9c782c15, 0x9301164b, 0xa2f59aea, 0xe6b2618b, 0xfceb237a, 0xcfeb98bd, 0xaaa75e8d, 0xbb57f750, 0xd282f40d, 0xa181d4d7, 0x93313b48, 0x9a64c67f, 0x00000001, 0xf00001f0 }, FinalRegs = new uint[] { 0xe32aaf45, 0x05fe0eac, 0x9c782c15, 0x9301164b, 0xa2f59aea, 0xe6b2618b, 0xfceb237a, 0xcfeb98bd, 0xaaa75e8d, 0xbb57f750, 0x32cf6ec8, 0xa181d4d7, 0x93313b48, 0x9a64c67f, 0x00000001, 0xf00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf549, 0x57c8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x67da941e, 0x5d744410, 0x1f93bf8f, 0xb52727e0, 0x77ce10fe, 0xe7a40291, 0x40ac5a1f, 0x127e801f, 0x68233546, 0xdbe8086f, 0x82b65e68, 0xcf35c09b, 0x8846e02d, 0x5fd54256, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x67da941e, 0x5d744410, 0x1f93bf8f, 0xb52727e0, 0x77ce10fe, 0xe7a40291, 0x40ac5a1f, 0xdbe82170, 0x68233546, 0xdbe8086f, 0x82b65e68, 0xcf35c09b, 0x8846e02d, 0x5fd54256, 0x00000001, 0x200001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf15c, 0x1649, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x86cf07b1, 0x1c86e00f, 0x8dc39789, 0xe8fafb40, 0xb837bf22, 0xe9c2c765, 0xb9e8b84b, 0xdbc9663e, 0x979b81da, 0xfb7a5636, 0x9012981d, 0xf52ec47c, 0xf98f6294, 0xaf70ff24, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x86cf07b1, 0x1c86e00f, 0x8dc39789, 0xe8fafb40, 0xb837bf22, 0xe9c2c765, 0xf9d862de, 0xdbc9663e, 0x979b81da, 0xfb7a5636, 0x9012981d, 0xf52ec47c, 0xf98f6294, 0xaf70ff24, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf144, 0x6ab6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x151549e7, 0xbdfa6ced, 0x47ba5025, 0xaba24048, 0x17c38ef8, 0xf92095ec, 0xdccd5b6f, 0xcb3878a5, 0x30d25594, 0x94886d84, 0xaec74633, 0xbe39725f, 0x439d8ef1, 0xcd66a204, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x151549e7, 0xbdfa6ced, 0x47ba5025, 0xaba24048, 0x17c38ef8, 0xf92095ec, 0xdccd5b6f, 0xcb3878a5, 0x30d25594, 0x94886d84, 0x1d738ef8, 0xbe39725f, 0x439d8ef1, 0xcd66a204, 0x00000001, 0x000001f0 }, }, // SBC (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf565, 0x3beb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x23efd21b, 0x78e2f658, 0x37a4e044, 0x8feab92a, 0x9795995f, 0x66c7ddab, 0x1c29040f, 0x10034172, 0x2eede540, 0x961c1400, 0x34cf45b9, 0xdb736f38, 0xd601c8ed, 0x99a714af, 0x00000001, 0xf00001f0 }, FinalRegs = new uint[] { 0x23efd21b, 0x78e2f658, 0x37a4e044, 0x8feab92a, 0x9795995f, 0x66c7ddab, 0x1c29040f, 0x10034172, 0x2eede540, 0x961c1400, 0x34cf45b9, 0x66c607ab, 0xd601c8ed, 0x99a714af, 0x00000001, 0xf00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf172, 0x1b0d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x596b63ec, 0xb659c798, 0x300ca58e, 0x52200fa5, 0x0db74ebe, 0x01e5b394, 0xed83d480, 0x1a524b19, 0x593d9bd1, 0x1152a751, 0xf3e1cb1c, 0xfb9392e3, 0x08fc2cd9, 0xc3910cf3, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x596b63ec, 0xb659c798, 0x300ca58e, 0x52200fa5, 0x0db74ebe, 0x01e5b394, 0xed83d480, 0x1a524b19, 0x593d9bd1, 0x1152a751, 0xf3e1cb1c, 0x2fffa580, 0x08fc2cd9, 0xc3910cf3, 0x00000001, 0x200001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf57c, 0x14da, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5eab5df9, 0x3cfdd390, 0xcfd20097, 0xc8986688, 0xa714c17c, 0xc9eee620, 0x6626498e, 0x2de48d3c, 0xc27c794f, 0xf7d0c67f, 0x75b6b9d9, 0xbaf9f630, 0x7bd89fad, 0xe5a2e298, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x5eab5df9, 0x3cfdd390, 0xcfd20097, 0xc8986688, 0x7bbd5fad, 0xc9eee620, 0x6626498e, 0x2de48d3c, 0xc27c794f, 0xf7d0c67f, 0x75b6b9d9, 0xbaf9f630, 0x7bd89fad, 0xe5a2e298, 0x00000001, 0x200001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf57a, 0x6bbf, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xaee56760, 0xa9f9b7d4, 0x9dd85a8c, 0x4c8cea6b, 0x7807b53d, 0xd1349b90, 0xcf320f62, 0x7af6d0c9, 0xc61fac5f, 0x23b43bbd, 0xef7466b3, 0x98e322a8, 0x1e10ae81, 0xb6987dcc, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0xaee56760, 0xa9f9b7d4, 0x9dd85a8c, 0x4c8cea6b, 0x7807b53d, 0xd1349b90, 0xcf320f62, 0x7af6d0c9, 0xc61fac5f, 0x23b43bbd, 0xef7466b3, 0xef7460bb, 0x1e10ae81, 0xb6987dcc, 0x00000001, 0xa00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf171, 0x47e8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4164d035, 0x72eecb21, 0xbb63329c, 0x8883a249, 0x230b524b, 0x40c059ae, 0x529e2950, 0xd0f7b958, 0xae900a4a, 0xa5a3f2b5, 0xe68da7f3, 0x68fececb, 0x91a2f476, 0x3986b8a0, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0x4164d035, 0x72eecb21, 0xbb63329c, 0x8883a249, 0x230b524b, 0x40c059ae, 0x529e2950, 0xfeeecb20, 0xae900a4a, 0xa5a3f2b5, 0xe68da7f3, 0x68fececb, 0x91a2f476, 0x3986b8a0, 0x00000001, 0x800001f0 }, }, // CMP (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5ba, 0x7f0c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x32876eff, 0x3127746f, 0x25274f4b, 0x50ba4fa5, 0xa3013fb5, 0x4985e2cb, 0x43dad09c, 0xfb6e47f2, 0x673ee708, 0x3beee172, 0x4866bb83, 0x9368060a, 0x565ecf8e, 0xecc22394, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x32876eff, 0x3127746f, 0x25274f4b, 0x50ba4fa5, 0xa3013fb5, 0x4985e2cb, 0x43dad09c, 0xfb6e47f2, 0x673ee708, 0x3beee172, 0x4866bb83, 0x9368060a, 0x565ecf8e, 0xecc22394, 0x00000001, 0x200001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf1b4, 0x5f0c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xabb3ffca, 0x7dbdda85, 0xe413a0d4, 0xf2ea8958, 0x81be2593, 0x8b0997e0, 0x5319660b, 0xd4edc3d0, 0x4b147c71, 0xa60a6a5f, 0x9984a94a, 0xbabe5540, 0x24df8017, 0x1e97e9f5, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0xabb3ffca, 0x7dbdda85, 0xe413a0d4, 0xf2ea8958, 0x81be2593, 0x8b0997e0, 0x5319660b, 0xd4edc3d0, 0x4b147c71, 0xa60a6a5f, 0x9984a94a, 0xbabe5540, 0x24df8017, 0x1e97e9f5, 0x00000001, 0x300001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5b1, 0x0f4b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf6edbf76, 0xd3f53e21, 0x37679835, 0x6af58147, 0x143dd6be, 0x4f6339d1, 0x0261fa88, 0x38fe033f, 0x1b503fb3, 0x802af22b, 0x22901e74, 0xae61d40e, 0xe1e850ee, 0xe353701c, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0xf6edbf76, 0xd3f53e21, 0x37679835, 0x6af58147, 0x143dd6be, 0x4f6339d1, 0x0261fa88, 0x38fe033f, 0x1b503fb3, 0x802af22b, 0x22901e74, 0xae61d40e, 0xe1e850ee, 0xe353701c, 0x00000001, 0xa00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5b2, 0x7f57, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x350b2e14, 0xcc603c9e, 0xa7a56491, 0x1f4fe90b, 0x6bb14aba, 0x325154ef, 0xc7655249, 0xe1a6077b, 0x145fc2f0, 0x21e0bc5e, 0x18275d8b, 0x0d8f37f0, 0xfdb56518, 0x405f5649, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x350b2e14, 0xcc603c9e, 0xa7a56491, 0x1f4fe90b, 0x6bb14aba, 0x325154ef, 0xc7655249, 0xe1a6077b, 0x145fc2f0, 0x21e0bc5e, 0x18275d8b, 0x0d8f37f0, 0xfdb56518, 0x405f5649, 0x00000001, 0xa00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf1b7, 0x0fd0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5a7f551b, 0x624d7cb7, 0xdc3e4dab, 0xd242610e, 0x8b7213db, 0x3c4f81df, 0x353e713e, 0x0ffdfd5c, 0xe56efdf9, 0x59330bc2, 0x1b91689c, 0x5497152e, 0x7ce02ab7, 0x0127aeca, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x5a7f551b, 0x624d7cb7, 0xdc3e4dab, 0xd242610e, 0x8b7213db, 0x3c4f81df, 0x353e713e, 0x0ffdfd5c, 0xe56efdf9, 0x59330bc2, 0x1b91689c, 0x5497152e, 0x7ce02ab7, 0x0127aeca, 0x00000001, 0x200001f0 }, }, // SUB (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5a6, 0x2902, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x688a6dd6, 0xcabb9832, 0xa187c464, 0xe4474634, 0x19316c88, 0x8b99d147, 0xd67bc441, 0x48cfa0cf, 0x4cd8b792, 0x9593d34d, 0x66b5a570, 0x9065cc35, 0x6ddf1e6f, 0xd49a2985, 0x00000001, 0xf00001f0 }, FinalRegs = new uint[] { 0x688a6dd6, 0xcabb9832, 0xa187c464, 0xe4474634, 0x19316c88, 0x8b99d147, 0xd67bc441, 0x48cfa0cf, 0x4cd8b792, 0xd673a441, 0x66b5a570, 0x9065cc35, 0x6ddf1e6f, 0xd49a2985, 0x00000001, 0xf00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf1a5, 0x4730, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x69e8d900, 0x3ca9d66e, 0x91788f4e, 0x6e821399, 0xd710747f, 0xc8e72a37, 0xf9f9702f, 0x8e689c3f, 0x87ef1e3c, 0xc8270c3e, 0xd76f0d87, 0x5482900c, 0xec43f474, 0x72617560, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x69e8d900, 0x3ca9d66e, 0x91788f4e, 0x6e821399, 0xd710747f, 0xc8e72a37, 0xf9f9702f, 0x18e72a37, 0x87ef1e3c, 0xc8270c3e, 0xd76f0d87, 0x5482900c, 0xec43f474, 0x72617560, 0x00000001, 0x000001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5bd, 0x7d6b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x56f27741, 0xdf3a0328, 0x49864f87, 0xd8b84caa, 0xd7a4cc2b, 0x85467faf, 0x6e972a47, 0xc2440b53, 0xa56fc6fa, 0xe86c3322, 0x19e1532d, 0x2984be63, 0xd7302738, 0xbf00369c, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0x56f27741, 0xdf3a0328, 0x49864f87, 0xd8b84caa, 0xd7a4cc2b, 0x85467faf, 0x6e972a47, 0xc2440b53, 0xa56fc6fa, 0xe86c3322, 0x19e1532d, 0x2984be63, 0xd7302738, 0xbf0032f0, 0x00000001, 0xa00001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5aa, 0x048c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc48ce68c, 0x33c654cc, 0xa31ea382, 0x398c4095, 0xfff680a5, 0x5886b5f4, 0xb1debf0b, 0x8bd529bb, 0x1354ba05, 0xcf80960a, 0x18582cbe, 0x37ca8996, 0x08f95e3c, 0xc87fdb04, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0xc48ce68c, 0x33c654cc, 0xa31ea382, 0x398c4095, 0x18122cbe, 0x5886b5f4, 0xb1debf0b, 0x8bd529bb, 0x1354ba05, 0xcf80960a, 0x18582cbe, 0x37ca8996, 0x08f95e3c, 0xc87fdb04, 0x00000001, 0x200001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5ba, 0x13aa, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd2de6567, 0x993624bf, 0xcfbd492f, 0x7b922424, 0x9fa01912, 0x04225225, 0x3a812a6d, 0xe62792b8, 0xb47cee9a, 0x5694288e, 0x6c669666, 0x213701a6, 0xe423ad2d, 0xc7d5362b, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0xd2de6567, 0x993624bf, 0xcfbd492f, 0x6c515666, 0x9fa01912, 0x04225225, 0x3a812a6d, 0xe62792b8, 0xb47cee9a, 0x5694288e, 0x6c669666, 0x213701a6, 0xe423ad2d, 0xc7d5362b, 0x00000001, 0x200001f0 }, }, // RSB (imm) - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5dc, 0x767d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x8496100e, 0x93007a60, 0x0d33d3dc, 0xd932c4e1, 0x6e05ad8d, 0xde3cc68e, 0x74400ff8, 0xce309ee7, 0x188e0ebd, 0xe10837ab, 0x6b2534e2, 0x280add20, 0x3adc0489, 0x8ef32355, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x8496100e, 0x93007a60, 0x0d33d3dc, 0xd932c4e1, 0x6e05ad8d, 0xde3cc68e, 0xc523ff6b, 0xce309ee7, 0x188e0ebd, 0xe10837ab, 0x6b2534e2, 0x280add20, 0x3adc0489, 0x8ef32355, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf1dc, 0x377d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc5d7fe20, 0xade81daf, 0xba65ccf8, 0xa101ee00, 0x3a2b70d9, 0xc90238d9, 0xc3b54049, 0x436bf83f, 0x99c96b58, 0xd134cb19, 0x4de47e7f, 0x6a175e2d, 0xd9e49229, 0x174d24ac, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0xc5d7fe20, 0xade81daf, 0xba65ccf8, 0xa101ee00, 0x3a2b70d9, 0xc90238d9, 0xc3b54049, 0xa398eb54, 0x99c96b58, 0xd134cb19, 0x4de47e7f, 0x6a175e2d, 0xd9e49229, 0x174d24ac, 0x00000001, 0x900001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5c5, 0x34bd, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xcea4f214, 0xdc15a8f8, 0xd22be9ef, 0x42c400c5, 0x2fd1fc9b, 0xca724b52, 0x5582071d, 0xd01b7816, 0xa4f5a435, 0xcfd50db5, 0x24e0c80b, 0x7b52178d, 0x11cd0449, 0xd6daa84a, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0xcea4f214, 0xdc15a8f8, 0xd22be9ef, 0x42c400c5, 0x358f2eae, 0xca724b52, 0x5582071d, 0xd01b7816, 0xa4f5a435, 0xcfd50db5, 0x24e0c80b, 0x7b52178d, 0x11cd0449, 0xd6daa84a, 0x00000001, 0x800001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf1ce, 0x7846, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3c676ff3, 0x511ea0cb, 0x15e79c80, 0x51a3a8c1, 0x535cc233, 0x6ae729a3, 0x4e5726da, 0x81260fb9, 0x24dd423a, 0x9e81d6c0, 0x812b3bd1, 0x55bd0f44, 0x1871ec65, 0x87087126, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x3c676ff3, 0x511ea0cb, 0x15e79c80, 0x51a3a8c1, 0x535cc233, 0x6ae729a3, 0x4e5726da, 0x81260fb9, 0x0317ffff, 0x9e81d6c0, 0x812b3bd1, 0x55bd0f44, 0x1871ec65, 0x87087126, 0x00000001, 0x200001f0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xf5c5, 0x2418, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2bb00694, 0x1c56a4c0, 0xc5cc4a3e, 0xc627c1ab, 0x4e4a8dfc, 0x1f3d71a4, 0x897d57b8, 0x0d4a7208, 0x433b7b88, 0xaaf24fd6, 0x2438f5f8, 0x9875e64a, 0xda475f22, 0x66d5e2e7, 0x00000001, 0x700001f0 }, @@ -1011,4 +1011,4 @@ namespace Ryujinx.Tests.Cpu }, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs index 03d90a1f1..f3832a8c3 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs @@ -164,4 +164,4 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetContext().GetPstateFlag(PState.TFlag), Is.EqualTo(false)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs index e3f8d7ecf..ca47d8e36 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestT32Mem.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using System; namespace Ryujinx.Tests.Cpu { @@ -14,70 +15,70 @@ namespace Ryujinx.Tests.Cpu public static readonly PrecomputedMemoryThumbTestCase[] ImmTestCases = { // STRB (imm8) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf80c, 0x1b2f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000023bd, 0x000027bb, 0x00002715, 0x000028f5, 0x0000233f, 0x0000213b, 0x00002eea, 0x0000282b, 0x000021e1, 0x0000264c, 0x000029e0, 0x00002ae7, 0x000021ff, 0x000026e3, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0x000023bd, 0x000027bb, 0x00002715, 0x000028f5, 0x0000233f, 0x0000213b, 0x00002eea, 0x0000282b, 0x000021e1, 0x0000264c, 0x000029e0, 0x00002ae7, 0x0000222e, 0x000026e3, 0x00000001, 0x800001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x21fe, Value: 0xbbfe) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf80a, 0x2f81, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000223c, 0x00002db9, 0x00002900, 0x0000247c, 0x00002b0a, 0x0000266b, 0x000026df, 0x00002447, 0x000024bb, 0x00002687, 0x0000266f, 0x00002a80, 0x000025ff, 0x00002881, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0x0000223c, 0x00002db9, 0x00002900, 0x0000247c, 0x00002b0a, 0x0000266b, 0x000026df, 0x00002447, 0x000024bb, 0x00002687, 0x000026f0, 0x00002a80, 0x000025ff, 0x00002881, 0x00000001, 0xa00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x26f0, Value: 0x2600) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf803, 0x6968, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000026ed, 0x00002685, 0x00002cd1, 0x00002dac, 0x00002a23, 0x00002626, 0x00002ec9, 0x0000245c, 0x000024ef, 0x00002319, 0x000026ce, 0x0000214d, 0x00002401, 0x000028b4, 0x00000001, 0x300001f0 }, FinalRegs = new uint[] { 0x000026ed, 0x00002685, 0x00002cd1, 0x00002d44, 0x00002a23, 0x00002626, 0x00002ec9, 0x0000245c, 0x000024ef, 0x00002319, 0x000026ce, 0x0000214d, 0x00002401, 0x000028b4, 0x00000001, 0x300001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2dac, Value: 0x2dc9) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf804, 0x89ad, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000265d, 0x00002b9c, 0x00002360, 0x000029ec, 0x00002413, 0x00002d8e, 0x00002aad, 0x00002d29, 0x00002bca, 0x00002a44, 0x00002980, 0x00002710, 0x000022fa, 0x0000222e, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x0000265d, 0x00002b9c, 0x00002360, 0x000029ec, 0x00002366, 0x00002d8e, 0x00002aad, 0x00002d29, 0x00002bca, 0x00002a44, 0x00002980, 0x00002710, 0x000022fa, 0x0000222e, 0x00000001, 0xc00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2412, Value: 0xca12) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf80d, 0xa9fe, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000298d, 0x00002e6c, 0x00002986, 0x00002ebb, 0x0000213e, 0x00002e39, 0x0000246f, 0x00002b6c, 0x00002ee2, 0x0000259e, 0x0000250a, 0x000029f6, 0x000021e7, 0x00002d9d, 0x00000001, 0x900001f0 }, FinalRegs = new uint[] { 0x0000298d, 0x00002e6c, 0x00002986, 0x00002ebb, 0x0000213e, 0x00002e39, 0x0000246f, 0x00002b6c, 0x00002ee2, 0x0000259e, 0x0000250a, 0x000029f6, 0x000021e7, 0x00002c9f, 0x00000001, 0x900001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2d9c, Value: 0x0a9c) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf80d, 0x3c46, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002c6f, 0x000028cc, 0x000025f0, 0x000022cc, 0x00002de3, 0x0000243c, 0x000025fb, 0x00002e88, 0x00002985, 0x000023ee, 0x00002120, 0x00002d50, 0x0000270a, 0x00002bbd, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0x00002c6f, 0x000028cc, 0x000025f0, 0x000022cc, 0x00002de3, 0x0000243c, 0x000025fb, 0x00002e88, 0x00002985, 0x000023ee, 0x00002120, 0x00002d50, 0x0000270a, 0x00002bbd, 0x00000001, 0xa00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2b76, Value: 0xcc76) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf801, 0x6c56, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002d6e, 0x00002530, 0x00002e6d, 0x00002942, 0x00002985, 0x00002d64, 0x00002a73, 0x00002ac6, 0x00002955, 0x00002881, 0x0000221d, 0x00002cb0, 0x0000225f, 0x00002534, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0x00002d6e, 0x00002530, 0x00002e6d, 0x00002942, 0x00002985, 0x00002d64, 0x00002a73, 0x00002ac6, 0x00002955, 0x00002881, 0x0000221d, 0x00002cb0, 0x0000225f, 0x00002534, 0x00000001, 0x100001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x24da, Value: 0x2473) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf809, 0xcc76, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002d50, 0x000025f2, 0x0000250a, 0x0000214c, 0x000023d1, 0x00002115, 0x00002c27, 0x00002540, 0x0000222b, 0x00002d03, 0x00002679, 0x00002b52, 0x00002eee, 0x00002b2a, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x00002d50, 0x000025f2, 0x0000250a, 0x0000214c, 0x000023d1, 0x00002115, 0x00002c27, 0x00002540, 0x0000222b, 0x00002d03, 0x00002679, 0x00002b52, 0x00002eee, 0x00002b2a, 0x00000001, 0xd00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2c8c, Value: 0xee8c) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf808, 0x1c8d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002844, 0x00002b78, 0x000028b0, 0x000026ff, 0x0000280b, 0x00002e0b, 0x00002de4, 0x00002b53, 0x00002ecd, 0x000021b5, 0x000026bc, 0x00002e9d, 0x00002d33, 0x000027f0, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0x00002844, 0x00002b78, 0x000028b0, 0x000026ff, 0x0000280b, 0x00002e0b, 0x00002de4, 0x00002b53, 0x00002ecd, 0x000021b5, 0x000026bc, 0x00002e9d, 0x00002d33, 0x000027f0, 0x00000001, 0x800001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2e40, Value: 0x2e78) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf80b, 0xbc26, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002244, 0x000025ad, 0x00002434, 0x00002b06, 0x00002ebd, 0x0000292b, 0x00002431, 0x00002e12, 0x0000289b, 0x0000265a, 0x00002747, 0x00002bac, 0x00002dae, 0x00002582, 0x00000001, 0xf00001f0 }, @@ -85,35 +86,35 @@ namespace Ryujinx.Tests.Cpu MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2b86, Value: 0x2bac) }, }, // STRB (imm12) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf887, 0x67c2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x700001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x27c2, Value: 0x2700) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf883, 0x9fda, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xc00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2fda, Value: 0x2f00) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf889, 0xd200, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x400001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf88c, 0x1c5b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x500001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2c5a, Value: 0x005a) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf887, 0x9fe2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xe00001f0 }, @@ -121,70 +122,70 @@ namespace Ryujinx.Tests.Cpu MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2fe2, Value: 0x2f00) }, }, // STRH (imm8) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf826, 0x0b0a, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000025a2, 0x000024d5, 0x00002ca1, 0x0000238a, 0x0000279c, 0x0000244c, 0x00002620, 0x00002c0e, 0x0000233e, 0x0000285f, 0x000021ab, 0x00002bd0, 0x0000281f, 0x00002be7, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x000025a2, 0x000024d5, 0x00002ca1, 0x0000238a, 0x0000279c, 0x0000244c, 0x0000262a, 0x00002c0e, 0x0000233e, 0x0000285f, 0x000021ab, 0x00002bd0, 0x0000281f, 0x00002be7, 0x00000001, 0x600001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2620, Value: 0x25a2) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf827, 0xcf61, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002555, 0x0000238f, 0x00002829, 0x000028c8, 0x00002399, 0x00002aab, 0x00002d6f, 0x000029eb, 0x000029e0, 0x00002d33, 0x0000292a, 0x00002b33, 0x00002e29, 0x00002ca4, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0x00002555, 0x0000238f, 0x00002829, 0x000028c8, 0x00002399, 0x00002aab, 0x00002d6f, 0x00002a4c, 0x000029e0, 0x00002d33, 0x0000292a, 0x00002b33, 0x00002e29, 0x00002ca4, 0x00000001, 0x100001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2a4c, Value: 0x2e29) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf821, 0x9b00, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000027ba, 0x00002514, 0x00002b07, 0x00002daf, 0x00002790, 0x0000274b, 0x00002379, 0x00002a98, 0x000024c8, 0x00002398, 0x000021ba, 0x00002959, 0x00002821, 0x00002d09, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x000027ba, 0x00002514, 0x00002b07, 0x00002daf, 0x00002790, 0x0000274b, 0x00002379, 0x00002a98, 0x000024c8, 0x00002398, 0x000021ba, 0x00002959, 0x00002821, 0x00002d09, 0x00000001, 0x500001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2514, Value: 0x2398) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf82c, 0xa927, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000226a, 0x00002792, 0x00002870, 0x00002918, 0x00002757, 0x00002679, 0x00002546, 0x000027f5, 0x00002edc, 0x00002cd3, 0x0000274a, 0x00002562, 0x000029a1, 0x00002976, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0x0000226a, 0x00002792, 0x00002870, 0x00002918, 0x00002757, 0x00002679, 0x00002546, 0x000027f5, 0x00002edc, 0x00002cd3, 0x0000274a, 0x00002562, 0x0000297a, 0x00002976, 0x00000001, 0x100001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x29a0, Value: 0x4aa0), (Address: 0x29a2, Value: 0x2927) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf824, 0xcfe4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000238b, 0x00002d22, 0x00002476, 0x000028ae, 0x00002442, 0x0000212b, 0x000026de, 0x00002a1a, 0x00002a02, 0x00002e47, 0x00002b2d, 0x00002427, 0x00002d1c, 0x000026d4, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x0000238b, 0x00002d22, 0x00002476, 0x000028ae, 0x00002526, 0x0000212b, 0x000026de, 0x00002a1a, 0x00002a02, 0x00002e47, 0x00002b2d, 0x00002427, 0x00002d1c, 0x000026d4, 0x00000001, 0xd00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2526, Value: 0x2d1c) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf820, 0x1c3d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002227, 0x00002b29, 0x0000232a, 0x0000214e, 0x000029ef, 0x00002522, 0x000029d3, 0x0000286c, 0x000029b2, 0x00002147, 0x00002c65, 0x00002891, 0x000029c2, 0x000028a5, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0x00002227, 0x00002b29, 0x0000232a, 0x0000214e, 0x000029ef, 0x00002522, 0x000029d3, 0x0000286c, 0x000029b2, 0x00002147, 0x00002c65, 0x00002891, 0x000029c2, 0x000028a5, 0x00000001, 0x800001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x21ea, Value: 0x2b29) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf826, 0x1cdf, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002232, 0x000029a1, 0x00002938, 0x00002ae7, 0x000029a4, 0x00002366, 0x0000273a, 0x000023f6, 0x00002601, 0x00002919, 0x000028e3, 0x00002907, 0x000023c1, 0x00002138, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0x00002232, 0x000029a1, 0x00002938, 0x00002ae7, 0x000029a4, 0x00002366, 0x0000273a, 0x000023f6, 0x00002601, 0x00002919, 0x000028e3, 0x00002907, 0x000023c1, 0x00002138, 0x00000001, 0x100001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x265a, Value: 0xa15a), (Address: 0x265c, Value: 0x2629) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf82b, 0x3c66, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002974, 0x00002372, 0x0000276c, 0x000021df, 0x00002272, 0x00002928, 0x00002c50, 0x0000290e, 0x00002319, 0x000021d1, 0x00002a82, 0x000027ff, 0x00002730, 0x000027b2, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0x00002974, 0x00002372, 0x0000276c, 0x000021df, 0x00002272, 0x00002928, 0x00002c50, 0x0000290e, 0x00002319, 0x000021d1, 0x00002a82, 0x000027ff, 0x00002730, 0x000027b2, 0x00000001, 0x700001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2798, Value: 0xdf98), (Address: 0x279a, Value: 0x2721) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf822, 0x3c06, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000021b8, 0x00002357, 0x00002b00, 0x00002207, 0x00002648, 0x0000219c, 0x000021d2, 0x000023b0, 0x00002368, 0x00002a41, 0x000026ac, 0x00002a86, 0x00002879, 0x00002c1d, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0x000021b8, 0x00002357, 0x00002b00, 0x00002207, 0x00002648, 0x0000219c, 0x000021d2, 0x000023b0, 0x00002368, 0x00002a41, 0x000026ac, 0x00002a86, 0x00002879, 0x00002c1d, 0x00000001, 0x700001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2afa, Value: 0x2207) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf824, 0xac84, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002796, 0x000027c8, 0x0000241b, 0x0000214d, 0x0000220b, 0x00002587, 0x00002130, 0x00002910, 0x00002ac2, 0x00002e74, 0x000028f8, 0x000024bf, 0x0000263a, 0x00002625, 0x00000001, 0x600001f0 }, @@ -192,35 +193,35 @@ namespace Ryujinx.Tests.Cpu MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2186, Value: 0xf886), (Address: 0x2188, Value: 0x2128) }, }, // STRH (imm12) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8a5, 0x59d4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x000001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x29d4, Value: 0x2000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8ac, 0xc533, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xe00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2532, Value: 0x0032), (Address: 0x2534, Value: 0x2520) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8a3, 0xb559, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x000001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2558, Value: 0x0058), (Address: 0x255a, Value: 0x2520) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8a5, 0xdb3a, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xb00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2b3a, Value: 0x2000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8a9, 0x02cc, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xc00001f0 }, @@ -228,70 +229,70 @@ namespace Ryujinx.Tests.Cpu MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x22cc, Value: 0x2000) }, }, // STR (imm8) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf846, 0x1fb4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002b17, 0x0000272f, 0x00002483, 0x0000284c, 0x0000287f, 0x0000238f, 0x0000222d, 0x00002259, 0x0000249d, 0x00002e3f, 0x00002323, 0x00002729, 0x000025c1, 0x00002866, 0x00000001, 0x900001f0 }, FinalRegs = new uint[] { 0x00002b17, 0x0000272f, 0x00002483, 0x0000284c, 0x0000287f, 0x0000238f, 0x000022e1, 0x00002259, 0x0000249d, 0x00002e3f, 0x00002323, 0x00002729, 0x000025c1, 0x00002866, 0x00000001, 0x900001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x22e0, Value: 0x2fe0), (Address: 0x22e2, Value: 0x0027), (Address: 0x22e4, Value: 0x2200) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf844, 0x3f11, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000028e1, 0x00002d48, 0x000027d6, 0x000023ac, 0x000027bb, 0x000026cf, 0x000023c1, 0x00002633, 0x0000214b, 0x00002434, 0x0000239a, 0x000025c6, 0x00002148, 0x00002d1f, 0x00000001, 0x300001f0 }, FinalRegs = new uint[] { 0x000028e1, 0x00002d48, 0x000027d6, 0x000023ac, 0x000027cc, 0x000026cf, 0x000023c1, 0x00002633, 0x0000214b, 0x00002434, 0x0000239a, 0x000025c6, 0x00002148, 0x00002d1f, 0x00000001, 0x300001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x27cc, Value: 0x23ac), (Address: 0x27ce, Value: 0x0000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf847, 0x09c2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000248b, 0x00002396, 0x000023c5, 0x00002be0, 0x0000237d, 0x00002191, 0x00002da0, 0x0000211c, 0x00002d24, 0x000021e6, 0x000024ff, 0x00002268, 0x00002968, 0x0000244d, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0x0000248b, 0x00002396, 0x000023c5, 0x00002be0, 0x0000237d, 0x00002191, 0x00002da0, 0x0000205a, 0x00002d24, 0x000021e6, 0x000024ff, 0x00002268, 0x00002968, 0x0000244d, 0x00000001, 0x800001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x211c, Value: 0x248b), (Address: 0x211e, Value: 0x0000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf84d, 0x7f23, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000025b0, 0x0000260e, 0x00002343, 0x00002e36, 0x000024c5, 0x000029bc, 0x0000278e, 0x00002b63, 0x00002ce7, 0x000029af, 0x000023bf, 0x00002475, 0x00002197, 0x00002c33, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x000025b0, 0x0000260e, 0x00002343, 0x00002e36, 0x000024c5, 0x000029bc, 0x0000278e, 0x00002b63, 0x00002ce7, 0x000029af, 0x000023bf, 0x00002475, 0x00002197, 0x00002c56, 0x00000001, 0x200001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2c56, Value: 0x2b63), (Address: 0x2c58, Value: 0x0000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf843, 0x9d24, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002ce4, 0x00002e0e, 0x000026d5, 0x000025fb, 0x00002b78, 0x0000215a, 0x00002af7, 0x0000259c, 0x00002645, 0x000027dc, 0x00002163, 0x000028f5, 0x000029df, 0x0000230b, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x00002ce4, 0x00002e0e, 0x000026d5, 0x000025d7, 0x00002b78, 0x0000215a, 0x00002af7, 0x0000259c, 0x00002645, 0x000027dc, 0x00002163, 0x000028f5, 0x000029df, 0x0000230b, 0x00000001, 0x500001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x25d6, Value: 0xdcd6), (Address: 0x25d8, Value: 0x0027), (Address: 0x25da, Value: 0x2500) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf849, 0xdc1a, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002d98, 0x0000254a, 0x00002540, 0x00002324, 0x0000264e, 0x00002523, 0x0000271f, 0x00002875, 0x000023b3, 0x00002680, 0x00002223, 0x000022bf, 0x000025f4, 0x00002d81, 0x00000001, 0x700001f0 }, FinalRegs = new uint[] { 0x00002d98, 0x0000254a, 0x00002540, 0x00002324, 0x0000264e, 0x00002523, 0x0000271f, 0x00002875, 0x000023b3, 0x00002680, 0x00002223, 0x000022bf, 0x000025f4, 0x00002d81, 0x00000001, 0x700001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2666, Value: 0x2d81), (Address: 0x2668, Value: 0x0000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf849, 0x0cd1, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000255a, 0x00002655, 0x00002276, 0x000022e4, 0x00002eef, 0x00002e99, 0x00002b55, 0x00002a40, 0x00002661, 0x00002dbd, 0x00002687, 0x000024e1, 0x000023ea, 0x00002b88, 0x00000001, 0xc00001f0 }, FinalRegs = new uint[] { 0x0000255a, 0x00002655, 0x00002276, 0x000022e4, 0x00002eef, 0x00002e99, 0x00002b55, 0x00002a40, 0x00002661, 0x00002dbd, 0x00002687, 0x000024e1, 0x000023ea, 0x00002b88, 0x00000001, 0xc00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2cec, Value: 0x255a), (Address: 0x2cee, Value: 0x0000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf847, 0x7c96, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000027f6, 0x0000222a, 0x000024e1, 0x00002a2d, 0x00002ee8, 0x000023f2, 0x000029de, 0x00002a53, 0x000029da, 0x00002d2c, 0x00002d6f, 0x000026b8, 0x00002777, 0x00002e3a, 0x00000001, 0xf00001f0 }, FinalRegs = new uint[] { 0x000027f6, 0x0000222a, 0x000024e1, 0x00002a2d, 0x00002ee8, 0x000023f2, 0x000029de, 0x00002a53, 0x000029da, 0x00002d2c, 0x00002d6f, 0x000026b8, 0x00002777, 0x00002e3a, 0x00000001, 0xf00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x29bc, Value: 0x53bc), (Address: 0x29be, Value: 0x002a), (Address: 0x29c0, Value: 0x2900) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf84d, 0x8cbd, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002a58, 0x00002a59, 0x00002dfd, 0x00002ba8, 0x00002929, 0x00002146, 0x00002706, 0x000025f3, 0x000023d7, 0x0000221f, 0x000027ae, 0x00002a6e, 0x00002824, 0x00002357, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x00002a58, 0x00002a59, 0x00002dfd, 0x00002ba8, 0x00002929, 0x00002146, 0x00002706, 0x000025f3, 0x000023d7, 0x0000221f, 0x000027ae, 0x00002a6e, 0x00002824, 0x00002357, 0x00000001, 0x600001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x229a, Value: 0x23d7), (Address: 0x229c, Value: 0x0000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf846, 0xacaf, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000284f, 0x00002def, 0x0000292f, 0x000021e8, 0x0000274e, 0x00002518, 0x00002538, 0x00002375, 0x00002d28, 0x0000229a, 0x0000255f, 0x00002eca, 0x00002e15, 0x000021aa, 0x00000001, 0x100001f0 }, @@ -299,35 +300,35 @@ namespace Ryujinx.Tests.Cpu MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2488, Value: 0x5f88), (Address: 0x248a, Value: 0x0025), (Address: 0x248c, Value: 0x2400) }, }, // STR (imm12) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8cc, 0x1a6e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x500001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2a6e, Value: 0x2000), (Address: 0x2a70, Value: 0x0000) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8c9, 0xcfc1, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xe00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x2fc0, Value: 0x00c0), (Address: 0x2fc2, Value: 0x0020), (Address: 0x2fc4, Value: 0x2f00) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8c3, 0xb5dd, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x600001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x25dc, Value: 0x00dc), (Address: 0x25de, Value: 0x0020), (Address: 0x25e0, Value: 0x2500) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8c0, 0x69e9, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xe00001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xe00001f0 }, MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x29e8, Value: 0x00e8), (Address: 0x29ea, Value: 0x0020), (Address: 0x29ec, Value: 0x2900) }, }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8cd, 0x028f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x600001f0 }, @@ -335,186 +336,186 @@ namespace Ryujinx.Tests.Cpu MemoryDelta = new (ulong Address, ushort Value)[] { (Address: 0x228e, Value: 0x008e), (Address: 0x2290, Value: 0x0020), (Address: 0x2292, Value: 0x2200) }, }, // LDRB (imm8) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf816, 0x1c48, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002cb8, 0x00002345, 0x00002ebc, 0x00002db8, 0x000021d4, 0x000026e4, 0x00002458, 0x000029e3, 0x000028d2, 0x000027f4, 0x000023d6, 0x00002def, 0x0000285c, 0x00002d06, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x00002cb8, 0x00000010, 0x00002ebc, 0x00002db8, 0x000021d4, 0x000026e4, 0x00002458, 0x000029e3, 0x000028d2, 0x000027f4, 0x000023d6, 0x00002def, 0x0000285c, 0x00002d06, 0x00000001, 0x600001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf815, 0x2d6e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000021e4, 0x00002425, 0x00002e42, 0x00002a58, 0x00002708, 0x00002965, 0x00002a1d, 0x00002ed5, 0x00002cc4, 0x000026e1, 0x00002b4b, 0x00002ade, 0x00002824, 0x00002975, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0x000021e4, 0x00002425, 0x00000028, 0x00002a58, 0x00002708, 0x000028f7, 0x00002a1d, 0x00002ed5, 0x00002cc4, 0x000026e1, 0x00002b4b, 0x00002ade, 0x00002824, 0x00002975, 0x00000001, 0x100001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf818, 0x0d33, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002492, 0x0000214d, 0x00002827, 0x000021af, 0x0000215e, 0x000028d6, 0x000024ec, 0x00002984, 0x0000297b, 0x000024b5, 0x000024ca, 0x0000298f, 0x00002339, 0x00002b7e, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x00000048, 0x0000214d, 0x00002827, 0x000021af, 0x0000215e, 0x000028d6, 0x000024ec, 0x00002984, 0x00002948, 0x000024b5, 0x000024ca, 0x0000298f, 0x00002339, 0x00002b7e, 0x00000001, 0xd00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf810, 0xbff3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002ea6, 0x000024fa, 0x00002346, 0x00002748, 0x0000283f, 0x00002770, 0x000023e3, 0x000021aa, 0x0000214a, 0x00002d58, 0x00002159, 0x000022e7, 0x00002242, 0x00002728, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x00002f99, 0x000024fa, 0x00002346, 0x00002748, 0x0000283f, 0x00002770, 0x000023e3, 0x000021aa, 0x0000214a, 0x00002d58, 0x00002159, 0x0000002f, 0x00002242, 0x00002728, 0x00000001, 0x600001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, // LDRB (imm12) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf892, 0xcc8f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x0000002c, 0x00002000, 0x00000001, 0x100001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf89a, 0x7fdc, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x000000dc, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x200001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf890, 0x5f9f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x0000002f, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x800001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf894, 0xdda1, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x900001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x0000002d, 0x00000001, 0x900001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf890, 0xc281, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x100001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000022, 0x00002000, 0x00000001, 0x100001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, // LDRH (imm8) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf834, 0x89d8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002a9e, 0x00002d84, 0x00002e9b, 0x00002e7f, 0x000024a2, 0x00002b7b, 0x00002e3b, 0x0000299a, 0x00002dff, 0x00002a9e, 0x000027b2, 0x00002a90, 0x00002883, 0x0000288d, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x00002a9e, 0x00002d84, 0x00002e9b, 0x00002e7f, 0x000023ca, 0x00002b7b, 0x00002e3b, 0x0000299a, 0x000024a2, 0x00002a9e, 0x000027b2, 0x00002a90, 0x00002883, 0x0000288d, 0x00000001, 0x500001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf833, 0x6be4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000028bd, 0x00002b0e, 0x00002bc1, 0x00002a83, 0x00002293, 0x00002c7c, 0x00002bfe, 0x00002eb7, 0x0000299b, 0x000026e6, 0x0000219c, 0x00002d5e, 0x00002cd4, 0x000026cf, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x000028bd, 0x00002b0e, 0x00002bc1, 0x00002b67, 0x00002293, 0x00002c7c, 0x0000842a, 0x00002eb7, 0x0000299b, 0x000026e6, 0x0000219c, 0x00002d5e, 0x00002cd4, 0x000026cf, 0x00000001, 0xd00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf83d, 0x1bca, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000250e, 0x00002776, 0x000029e5, 0x0000276e, 0x00002c6b, 0x00002712, 0x00002a85, 0x00002d56, 0x000024c0, 0x00002d86, 0x0000254a, 0x00002549, 0x00002795, 0x00002e97, 0x00000001, 0x200001f0 }, FinalRegs = new uint[] { 0x0000250e, 0x0000982e, 0x000029e5, 0x0000276e, 0x00002c6b, 0x00002712, 0x00002a85, 0x00002d56, 0x000024c0, 0x00002d86, 0x0000254a, 0x00002549, 0x00002795, 0x00002f61, 0x00000001, 0x200001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, // LDRH (imm12) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8b7, 0x92fc, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x000022fc, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xa00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8ba, 0xadd9, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xa00001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x0000da2d, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xa00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8bb, 0x0bb0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x00002bb0, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0xd00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8b8, 0xc3f8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x600001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x000023f8, 0x00002000, 0x00000001, 0x600001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, // LDR (imm8) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf85b, 0x3fd1, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002a19, 0x00002e5b, 0x0000231b, 0x000021fa, 0x00002e95, 0x00002bd5, 0x00002e9c, 0x00002dfa, 0x000021d8, 0x00002ce1, 0x00002318, 0x00002735, 0x0000247d, 0x00002436, 0x00000001, 0xf00001f0 }, FinalRegs = new uint[] { 0x00002a19, 0x00002e5b, 0x0000231b, 0x28082806, 0x00002e95, 0x00002bd5, 0x00002e9c, 0x00002dfa, 0x000021d8, 0x00002ce1, 0x00002318, 0x00002806, 0x0000247d, 0x00002436, 0x00000001, 0xf00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf854, 0xab9e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0000214f, 0x00002578, 0x00002a98, 0x000021b0, 0x00002ebb, 0x0000284a, 0x00002319, 0x00002581, 0x00002179, 0x00002594, 0x00002373, 0x000028f4, 0x00002ec5, 0x00002e0a, 0x00000001, 0xb00001f0 }, FinalRegs = new uint[] { 0x0000214f, 0x00002578, 0x00002a98, 0x000021b0, 0x00002f59, 0x0000284a, 0x00002319, 0x00002581, 0x00002179, 0x00002594, 0xbe2ebc2e, 0x000028f4, 0x00002ec5, 0x00002e0a, 0x00000001, 0xb00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf852, 0x6d2d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002e27, 0x00002676, 0x00002bde, 0x000022d9, 0x00002362, 0x00002d4b, 0x00002dab, 0x000022b6, 0x0000229c, 0x00002507, 0x00002848, 0x0000225f, 0x00002ac2, 0x000023c3, 0x00000001, 0xf00001f0 }, FinalRegs = new uint[] { 0x00002e27, 0x00002676, 0x00002bb1, 0x000022d9, 0x00002362, 0x00002d4b, 0xb42bb22b, 0x000022b6, 0x0000229c, 0x00002507, 0x00002848, 0x0000225f, 0x00002ac2, 0x000023c3, 0x00000001, 0xf00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf850, 0x8da5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002559, 0x0000285e, 0x000021de, 0x00002223, 0x000023ff, 0x00002e05, 0x00002bf3, 0x000024a5, 0x00002124, 0x00002768, 0x00002a14, 0x0000219e, 0x00002739, 0x00002e3c, 0x00000001, 0xd00001f0 }, FinalRegs = new uint[] { 0x000024b4, 0x0000285e, 0x000021de, 0x00002223, 0x000023ff, 0x00002e05, 0x00002bf3, 0x000024a5, 0x24b624b4, 0x00002768, 0x00002a14, 0x0000219e, 0x00002739, 0x00002e3c, 0x00000001, 0xd00001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf857, 0x19f6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x000027f5, 0x0000285e, 0x000025f6, 0x00002e22, 0x00002224, 0x00002870, 0x00002ecc, 0x000024cf, 0x00002711, 0x0000241b, 0x00002ddf, 0x00002545, 0x000028ca, 0x000023c5, 0x00000001, 0x400001f0 }, FinalRegs = new uint[] { 0x000027f5, 0xd224d024, 0x000025f6, 0x00002e22, 0x00002224, 0x00002870, 0x00002ecc, 0x000023d9, 0x00002711, 0x0000241b, 0x00002ddf, 0x00002545, 0x000028ca, 0x000023c5, 0x00000001, 0x400001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, // LDR (imm12) - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8d1, 0xc65e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x000001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x2660265e, 0x00002000, 0x00000001, 0x000001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8db, 0xd09b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x800001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x9e209c20, 0x00000001, 0x800001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8d2, 0x6fde, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x900001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x2fe02fde, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x900001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, - new PrecomputedMemoryThumbTestCase() + new() { Instructions = new ushort[] { 0xf8dc, 0x3de5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x500001f0 }, FinalRegs = new uint[] { 0x00002000, 0x00002000, 0x00002000, 0xe82de62d, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00002000, 0x00000001, 0x500001f0 }, - MemoryDelta = new (ulong Address, ushort Value)[] {}, + MemoryDelta = Array.Empty<(ulong Address, ushort Value)>(), }, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs b/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs index 3d13ff733..625ce2d82 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs @@ -279,601 +279,601 @@ namespace Ryujinx.Tests.Cpu public static readonly PrecomputedThumbTestCase[] RandomTestCases = { - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x42ab, 0xabed, 0x43fc, 0x4360, 0x40c1, 0x40b5, 0x4353, 0x43ac, 0xba74, 0x4613, 0xb03a, 0xbf2a, 0xa5bd, 0x187b, 0x410a, 0x4405, 0x15b3, 0x1472, 0xb248, 0x43a6, 0x41af, 0xbaf4, 0x1a6d, 0x4684, 0xbf04, 0x4068, 0xb26e, 0x014b, 0x4064, 0xba15, 0x3d2d, 0xb22d, 0x4378, 0x2501, 0x4279, 0xb299, 0x1586, 0x42e7, 0x2f55, 0xba55, 0x1f6f, 0x443d, 0x4194, 0xbfcb, 0xbacd, 0xb2a0, 0x406b, 0x1b65, 0xbfd0, 0x1bf7, 0x41e7, 0xba76, 0x436f, 0x46f8, 0x4042, 0x1ffe, 0x447c, 0xba50, 0x341e, 0x42fd, 0x409c, 0xbf09, 0xb2cf, 0x1c99, 0x41f7, 0x41a6, 0x4278, 0x4040, 0x3747, 0x301a, 0x01cc, 0x4304, 0x1ca3, 0xab1d, 0x43dd, 0x41e3, 0x35ad, 0x43f5, 0xb091, 0x3214, 0x44f1, 0x41d7, 0xb244, 0xb2a2, 0x41e1, 0x3d7a, 0xbf24, 0xbac1, 0x0059, 0xba3b, 0xabe5, 0x4550, 0x0a9e, 0xbfe2, 0x034e, 0xb0b7, 0x0aec, 0x1ee6, 0x3276, 0x0866, 0xa763, 0x1e45, 0x4275, 0xad25, 0x416d, 0x0d6d, 0x0b50, 0xb200, 0x1eba, 0x4378, 0x0547, 0x43bb, 0xb2f2, 0xb2af, 0x1ea0, 0x25b0, 0x4287, 0x1d55, 0x2225, 0xbf46, 0xb276, 0xba57, 0x4253, 0x19e0, 0xbf32, 0x1b06, 0x40ab, 0xb2a4, 0x1eda, 0x1dfd, 0xaabd, 0x1b08, 0xba0e, 0x4014, 0xa079, 0x119b, 0x1d17, 0x41b1, 0x005a, 0xb2ba, 0x419a, 0x4361, 0x2d2a, 0xba36, 0x1b49, 0xbf0c, 0x423c, 0xbae6, 0x2bc0, 0x409c, 0xbfa0, 0xba02, 0x2496, 0xb279, 0xbace, 0xb2e0, 0xbf07, 0x3b17, 0x40a9, 0x3154, 0x41ab, 0x284e, 0xb03c, 0xbf06, 0x41ff, 0xba45, 0x4144, 0xab32, 0x4000, 0xb2b2, 0x3729, 0x46fb, 0xb042, 0x32d0, 0x0557, 0x3583, 0xb249, 0x43f5, 0x0a54, 0x416f, 0x1fc6, 0x4560, 0xb2f6, 0xa114, 0x1dd7, 0x40b4, 0x4304, 0xbfc2, 0x41f2, 0xb2e2, 0xb0d4, 0xbaca, 0xbf65, 0x4065, 0xa1e5, 0xb043, 0x4004, 0xb237, 0x41e9, 0xba0f, 0xaa8e, 0x421d, 0x1d12, 0x435e, 0x434b, 0x4356, 0xacaf, 0x4551, 0x1935, 0x1c1a, 0xba67, 0x2441, 0xbf9a, 0x41a0, 0xb2d8, 0x1c00, 0xad68, 0x4185, 0xb272, 0x43ba, 0x436c, 0xb08b, 0xb2f4, 0x4379, 0x1d0f, 0x46f8, 0x4379, 0x1b8f, 0x4260, 0x40d8, 0xbfa7, 0x0730, 0xb27d, 0xb242, 0x43d3, 0x46fd, 0xbf92, 0xb067, 0xaa15, 0x428c, 0xb2e1, 0x43e5, 0xb2f6, 0x1da1, 0x023e, 0x1a86, 0xba01, 0xb203, 0x13ae, 0xa6a6, 0x45d8, 0xba4c, 0xb228, 0x438c, 0xbae8, 0x4378, 0x3b55, 0xbf5c, 0xb036, 0xb261, 0x1bea, 0x41b8, 0x43f5, 0xba70, 0xa959, 0xb2a9, 0x41fd, 0x19ad, 0x41cd, 0x4171, 0xb046, 0xbf37, 0x431e, 0x4302, 0xb04f, 0x396f, 0x44f0, 0x36d3, 0x01b0, 0x18f5, 0x424b, 0xb27a, 0x43a9, 0xba49, 0x4357, 0x41e5, 0x42d4, 0x0f27, 0x4325, 0x1f9b, 0x447a, 0x46b8, 0xbf60, 0x43c5, 0xa78b, 0x46b2, 0xb2c4, 0x4322, 0xbfad, 0xbf90, 0x4275, 0x40c4, 0x1e91, 0xba0c, 0xbfd0, 0xafd3, 0xbfa0, 0xb2ff, 0xb2dc, 0x43a1, 0xb0db, 0xbf1e, 0x269d, 0xb21a, 0x405d, 0x1822, 0x3dc1, 0xb247, 0x119e, 0xb2b9, 0x4056, 0xaedd, 0xba59, 0x40ed, 0x43c0, 0xbf5f, 0xba35, 0xad1b, 0xb0db, 0x274a, 0x4042, 0x404c, 0xb0a6, 0x2983, 0x41b9, 0x4226, 0x455b, 0xbad3, 0xba5c, 0x42e3, 0xbf8f, 0x3b28, 0xadf6, 0x0321, 0x41c0, 0x223b, 0x40ca, 0x1ea5, 0x4256, 0x409a, 0x4339, 0xba7c, 0x43cf, 0x40bb, 0xb217, 0x427a, 0x289f, 0xb28b, 0xb250, 0xbf2a, 0xb084, 0x410c, 0x416b, 0x4079, 0xb2c9, 0x4149, 0x4143, 0x0c11, 0x461f, 0x18ed, 0x41e1, 0x4651, 0xba1c, 0x422b, 0x44fb, 0xb2b4, 0xbfde, 0x45e1, 0x4601, 0x4483, 0xa182, 0x3311, 0x40bd, 0x446a, 0x431c, 0x2f1e, 0xbac8, 0x02c3, 0x43d5, 0x415d, 0x42f7, 0x1cad, 0x1597, 0xb2cd, 0x1cbd, 0x42df, 0x069e, 0x0986, 0x435d, 0xbad5, 0xbf39, 0x1044, 0x417c, 0x3afb, 0x3be4, 0xb2b8, 0xb0b6, 0x401b, 0x1e4f, 0x4270, 0x4278, 0xacb6, 0xbad1, 0xbaee, 0x1920, 0xba01, 0x4367, 0xb24d, 0xb2db, 0x05da, 0xa2c6, 0x4627, 0xba2b, 0xbfa3, 0x42aa, 0x3637, 0xbafa, 0x4364, 0x43f3, 0xaaea, 0x40a5, 0x424f, 0x45a3, 0x44e9, 0x4477, 0xb09e, 0x42ea, 0x44b9, 0x4694, 0x4629, 0xba4b, 0x42c8, 0x412e, 0xbf9e, 0xba0a, 0x420a, 0xb2d0, 0xbae8, 0xbfb3, 0x40f8, 0x36d0, 0x40bd, 0x4395, 0x0d0f, 0xbafa, 0xb20b, 0x41c6, 0x40ea, 0xbf62, 0x429b, 0x4142, 0x43a6, 0x1f1b, 0xa9b4, 0x4455, 0xb0b1, 0xbae8, 0xb24c, 0x0396, 0x4360, 0xb26d, 0x0693, 0x1842, 0xba1e, 0xb29b, 0xba44, 0x1c45, 0x456f, 0x1a6a, 0x439c, 0xb24d, 0xac7d, 0x410b, 0x419f, 0xbf7e, 0x4057, 0x4377, 0x1ea6, 0x2f69, 0xbfc0, 0x1a75, 0xb2cf, 0x3759, 0x4187, 0x429b, 0x40d7, 0x18e7, 0x2eaa, 0x2bc6, 0xbf0d, 0x028c, 0x40f2, 0x421c, 0xbad1, 0xbf70, 0x41c5, 0xb012, 0x42fe, 0xb2ef, 0xbf96, 0xba10, 0x4103, 0x4434, 0x449a, 0x0b03, 0x40d5, 0x4221, 0x345a, 0x1491, 0xb2fc, 0x46e1, 0x456c, 0xb0f0, 0xb2ba, 0x4254, 0x415f, 0x4269, 0x434f, 0xb04c, 0x4316, 0xb049, 0xba18, 0x1988, 0x4194, 0x4412, 0xbfb3, 0x43f8, 0xba4f, 0xb218, 0x4033, 0x38a4, 0x3d99, 0xa6b2, 0xb076, 0xbfc0, 0x42c6, 0x40ad, 0x432a, 0x4226, 0x1ee9, 0x1aa5, 0xbf13, 0x3370, 0x2820, 0x1191, 0x432c, 0x40b8, 0x464e, 0x1e58, 0x462f, 0xb022, 0x40ba, 0x464f, 0x4083, 0x4683, 0x16c7, 0xbf43, 0x42a0, 0xba56, 0x2f3d, 0x417b, 0x43ff, 0xac7d, 0xb285, 0x4041, 0xba51, 0xbf31, 0x4041, 0xb281, 0xb2d8, 0x1a99, 0x4342, 0x402f, 0x4185, 0x40f3, 0xbfd0, 0x4274, 0x40a3, 0x4064, 0x39a4, 0xba04, 0x408e, 0x3250, 0x3c3d, 0xa2bb, 0xba1c, 0x1a4b, 0x40e5, 0x3ca6, 0xbf67, 0x4285, 0xa9f3, 0x46bc, 0x420d, 0xba05, 0xb009, 0x417c, 0xb27e, 0x1c48, 0x41c8, 0x42e3, 0x1e8c, 0x42b4, 0xba08, 0x3598, 0xb263, 0xb2f2, 0x1053, 0x40da, 0x37c7, 0x370d, 0x1853, 0x41e9, 0xb061, 0x1af7, 0x4015, 0x44aa, 0x1db0, 0x4165, 0xbf27, 0x3d1f, 0x42f4, 0x432f, 0x407c, 0x1457, 0x43c4, 0xb226, 0x40d3, 0x4317, 0x409d, 0x4024, 0xb062, 0xaeba, 0x3c71, 0x43a1, 0xada2, 0x4115, 0xba3f, 0x4163, 0x4386, 0xb2c9, 0x1b5a, 0xba22, 0xbf1e, 0xbaea, 0xbadc, 0x0ff5, 0x1102, 0x41a5, 0x0122, 0x43b3, 0x30c2, 0x43cb, 0x41c8, 0x0365, 0xaa50, 0xb200, 0x4347, 0x4049, 0x4101, 0x0ba9, 0x325b, 0x296c, 0x4364, 0x43d7, 0xbaf1, 0xbfb4, 0x436e, 0xb28f, 0x0163, 0x42d8, 0x4016, 0x33c6, 0x23f4, 0x40e6, 0x25d3, 0x40cf, 0x3a7c, 0x445c, 0x3781, 0x4083, 0xbf16, 0x46a5, 0xae3c, 0x43d1, 0xbf2f, 0x41d9, 0x41b5, 0xbac7, 0x4381, 0x434b, 0x099c, 0x2c64, 0x4202, 0x40c8, 0x4194, 0x4625, 0xbf19, 0x423b, 0x4098, 0x40f0, 0x437f, 0x2643, 0xbaee, 0x40a0, 0x00b2, 0xb06f, 0x42d2, 0x4047, 0xa2b2, 0xb284, 0x4008, 0xaeed, 0x46a9, 0x40d4, 0x42fe, 0x4084, 0x41d0, 0x4304, 0x0437, 0xbad6, 0x4247, 0x4068, 0x43db, 0xbf8e, 0x1fcf, 0x278a, 0xb202, 0x4304, 0x0bf0, 0x4389, 0x3f6d, 0xb266, 0xb243, 0x152d, 0x4630, 0xaee7, 0x0104, 0x40c3, 0x40b9, 0xa96c, 0xb2e0, 0xa0f9, 0xb273, 0x0d05, 0x402b, 0x408f, 0x0b31, 0xa305, 0xbf65, 0x4200, 0xb2e0, 0x43e8, 0x43a2, 0xb206, 0x436f, 0xba61, 0x4037, 0xba4c, 0xbad3, 0x46ed, 0x3501, 0x4321, 0x4137, 0x41c3, 0x17ef, 0x42b7, 0xba13, 0x4246, 0xbf58, 0xb210, 0xb04f, 0x40e1, 0x42e3, 0xa428, 0x404e, 0xb2d1, 0x4043, 0x3f7b, 0x1d55, 0x35c0, 0x1b98, 0x1f9b, 0x44db, 0xbf17, 0xb240, 0x1e91, 0xb0a7, 0x402a, 0x4045, 0x414b, 0x415c, 0x08b1, 0xba10, 0x188b, 0x4219, 0x32e9, 0x427a, 0x2242, 0x1f51, 0x43f8, 0x409c, 0xbaff, 0x1f4a, 0x418f, 0x4566, 0x4151, 0xbaca, 0x4083, 0x409f, 0x44e4, 0xbf70, 0x341c, 0xbf0e, 0x400f, 0x40b5, 0x466d, 0x01a2, 0x4493, 0x43e4, 0x40e4, 0xba5b, 0xb289, 0xbad8, 0x0d68, 0x424f, 0xb20e, 0x418b, 0xb2c3, 0xade0, 0x4066, 0xbf95, 0x2228, 0x4110, 0x1db8, 0xa1e2, 0x0589, 0x4203, 0x4371, 0xbf77, 0x3288, 0x42f8, 0x4341, 0x413e, 0xb027, 0xbaf9, 0xbf70, 0x157d, 0x1d0d, 0xa3c2, 0x1e40, 0x448c, 0x405b, 0x14c2, 0x433f, 0x43b6, 0xba18, 0x1689, 0xbf82, 0x40aa, 0x4048, 0x320d, 0x4620, 0x42c9, 0x1670, 0x409f, 0xba26, 0xbad0, 0x43a5, 0x31b7, 0xbac8, 0x4405, 0xa60d, 0x1e58, 0x2989, 0xb22b, 0x3c62, 0x409f, 0x4295, 0xb287, 0xad38, 0xb025, 0x43eb, 0xbfbf, 0x0126, 0x4253, 0x42df, 0xa723, 0xa96e, 0x42f7, 0x3748, 0xac86, 0x408a, 0xae5c, 0x3778, 0xb2e5, 0xbfc1, 0x418b, 0xbac3, 0xb0fb, 0x45a6, 0x4293, 0x426b, 0xb07e, 0x43c3, 0x34f3, 0x08bc, 0x02c7, 0xbfbc, 0x42af, 0x421d, 0x2675, 0x428b, 0xba07, 0x4107, 0xb07a, 0x433d, 0x438e, 0x1195, 0x403d, 0x4545, 0xbad3, 0x1b5e, 0xbf8f, 0xb289, 0x23df, 0x402d, 0x413b, 0x2af8, 0x003b, 0x46ad, 0xbf9a, 0x4271, 0x433d, 0x43c0, 0x2f27, 0xa5e3, 0x179d, 0x1ced, 0x445d, 0x43c0, 0x3eb2, 0x40e8, 0x39ba, 0x4088, 0x4117, 0x3137, 0x189b, 0xb024, 0xa52f, 0x41b4, 0x1f27, 0x439d, 0x42e2, 0x4265, 0xbfe0, 0x252f, 0xbf7f, 0x3bd7, 0x40dd, 0x2cc1, 0x1c04, 0x41a6, 0x1403, 0x408c, 0x46bd, 0x2a99, 0x4230, 0x42f9, 0x04e8, 0xba5d, 0x284d, 0x43c7, 0x4332, 0x3921, 0x443c, 0xbf08, 0x4390, 0x1edf, 0xa980, 0x40a7, 0xb250, 0xb074, 0x41fd, 0xa12a, 0x1edd, 0xbad8, 0xb231, 0xbfd7, 0x1951, 0x4337, 0x2393, 0x4491, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xfb8e51a4, 0xf9f21a25, 0x2a13c422, 0x57b1ba81, 0xf344e297, 0x1706c70a, 0xb7e92cf4, 0x512917e4, 0x5a86f7df, 0x755562e5, 0xae714612, 0x2c5f02dc, 0x0dcdaeee, 0x75c86709, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x00000000, 0xffffff4b, 0xffffff4e, 0x00000093, 0xffffffff, 0xfffffffd, 0xffffff4e, 0x00000000, 0x00000000, 0x03f9e62a, 0x00000082, 0x000007de, 0xffff8cdd, 0x000042ac, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x18f0, 0x405d, 0x4152, 0x40a4, 0x426b, 0xb201, 0x1a63, 0x4623, 0x41a9, 0x1a48, 0x4165, 0xbf7d, 0xba5c, 0xbf00, 0xb06d, 0x43b7, 0x44ca, 0x41b2, 0x2b08, 0xb2c5, 0xb224, 0xb224, 0xb217, 0x2985, 0xba24, 0xb080, 0x43e8, 0x1d56, 0x45f2, 0x4226, 0x3d7c, 0x41bd, 0xbf9f, 0x4315, 0xbafe, 0x400f, 0x463e, 0x22db, 0x4143, 0x42c2, 0x43e0, 0x43e7, 0x40c5, 0xb221, 0xbfc2, 0xb0e4, 0xba4a, 0xbf90, 0x437a, 0x41e2, 0x403c, 0x2581, 0xa403, 0xa17c, 0x4322, 0x1bed, 0x01f8, 0xbf92, 0x3c28, 0x1ed9, 0x0c6a, 0xb219, 0xbf44, 0x416f, 0x18b5, 0xb2ce, 0x405e, 0x31f9, 0x1afe, 0xb068, 0xa488, 0xbaf0, 0x429e, 0xb265, 0x4154, 0x435c, 0x3f65, 0x4132, 0x400f, 0xbfcb, 0xb2b4, 0x4302, 0xa661, 0x429b, 0xba09, 0xb0bd, 0x1c5d, 0x4396, 0xb205, 0x4210, 0x4184, 0x4114, 0x30e9, 0x4694, 0x4281, 0xba3b, 0xb02c, 0x3c7c, 0xb068, 0x1f5f, 0xbad2, 0x2fc9, 0xb2fa, 0x422c, 0x40d1, 0x427a, 0xb00b, 0x1bd1, 0x1959, 0xbf94, 0x35f7, 0x415f, 0x4230, 0x4491, 0xbada, 0x4390, 0xa1d1, 0x4201, 0x4109, 0x3b9a, 0x4279, 0x4372, 0xbff0, 0xb2b4, 0x1c29, 0x0bba, 0x42ff, 0x0b07, 0x431a, 0x35b1, 0x2674, 0x1d4b, 0x43e4, 0xbfdc, 0x4385, 0x00d9, 0x33f4, 0x4010, 0xab8a, 0x42e7, 0xb093, 0xba39, 0x4140, 0x2120, 0xb2e8, 0x2254, 0x4354, 0x430e, 0xba16, 0x441b, 0x1b54, 0xaf2b, 0x4144, 0x424f, 0x3e6a, 0x0c2f, 0x4330, 0x44cc, 0x42a9, 0x433f, 0xbf93, 0x4367, 0x3de9, 0x4203, 0x4120, 0xa589, 0x1a1d, 0xaa95, 0x4068, 0x02ff, 0x4071, 0x44da, 0x181f, 0xbf8a, 0x2c8c, 0x183e, 0x40fd, 0x3711, 0xb2c1, 0x40d1, 0x0e30, 0x42e0, 0xba5f, 0xb00f, 0x0e5e, 0xb2b0, 0x419e, 0x42df, 0x4240, 0xa9a1, 0xaeba, 0x4319, 0x45f1, 0xbae7, 0x4390, 0x43ad, 0x40a0, 0x1e6e, 0x0c84, 0x2ddb, 0xbf45, 0xbfb0, 0x4405, 0x1ca9, 0x39cb, 0xb2e8, 0x0a6f, 0xba79, 0x4044, 0x4491, 0x3a70, 0xa45d, 0x425d, 0x3a30, 0x4107, 0xa248, 0x4055, 0x40f8, 0x43fb, 0x4078, 0x351f, 0x41a3, 0x439f, 0xba6a, 0x426a, 0x3f2f, 0xbf5d, 0x40ee, 0x43b9, 0x1b4a, 0xa56a, 0x2fec, 0xba54, 0x4613, 0x46c5, 0x4237, 0x3dd2, 0xb037, 0x3e1a, 0x04e2, 0x415d, 0x426c, 0xb2ed, 0x42c3, 0x416f, 0x1d8a, 0x42cd, 0x42c8, 0x422a, 0xbf7f, 0xa9b0, 0x4303, 0x43ea, 0x359a, 0x4058, 0x418f, 0x436e, 0xb289, 0x2731, 0x432b, 0x40c4, 0xb298, 0x401e, 0x011e, 0x4132, 0x43b6, 0x4148, 0x41a9, 0x413b, 0x4191, 0x1cb7, 0x0612, 0x4333, 0x3dfe, 0x4264, 0x4261, 0xaabe, 0xba60, 0x40d6, 0xbfe8, 0xa1fc, 0x025e, 0x4249, 0x33b2, 0x2d28, 0x428c, 0xb22d, 0xbae2, 0x449c, 0x40ac, 0x41b2, 0x193a, 0x435b, 0x4155, 0x4060, 0x42b8, 0x4363, 0x18d6, 0x4162, 0x145b, 0x439a, 0xbf2b, 0xba23, 0x41fa, 0x311d, 0xb2bf, 0x3045, 0xba6f, 0xbf06, 0x213a, 0x4565, 0xb0af, 0xb2cf, 0xa9db, 0x41b9, 0x19e3, 0x427d, 0xa296, 0x440c, 0x4363, 0x411f, 0x425b, 0x421a, 0x4207, 0x1840, 0x435e, 0x4285, 0xba48, 0xb2de, 0x420c, 0xba22, 0xbf5d, 0x41d3, 0x1db2, 0x400a, 0xb2b3, 0x42f1, 0x4014, 0x42a5, 0xae6b, 0x37bf, 0xa4b4, 0x437b, 0x4089, 0xbf23, 0x41ca, 0x43c7, 0x45b4, 0xb2d0, 0x418f, 0x4086, 0x03a8, 0x401c, 0x42b0, 0xb270, 0x41df, 0x0b28, 0x428e, 0x1cd2, 0x4666, 0x426a, 0x275d, 0x4189, 0x42a3, 0x0223, 0x40a6, 0xb041, 0x16a8, 0x2e46, 0xba19, 0xb2e6, 0x1b94, 0xbfc1, 0x40cd, 0x1074, 0xb29a, 0x411c, 0x41e6, 0xb052, 0x18f6, 0x4356, 0x43fc, 0x19b9, 0x4045, 0x1a37, 0x4582, 0x43ba, 0x462b, 0x2e1b, 0x4052, 0x0a1a, 0x43ae, 0x31a1, 0x41f2, 0x42dd, 0x17ea, 0x43ee, 0x42f3, 0x2cbf, 0xbf66, 0xb023, 0x40f6, 0x407e, 0x4499, 0x4234, 0x44f4, 0x404a, 0xb2ed, 0x40d4, 0xbaea, 0x0ea6, 0x439a, 0xae0e, 0x1334, 0x4558, 0x336a, 0xb03b, 0x0b94, 0x41cf, 0x4256, 0x4188, 0x31f8, 0x404b, 0x410e, 0xbf69, 0x2a6c, 0xbacb, 0x1e69, 0x011a, 0xb23e, 0x44d0, 0x3916, 0xb295, 0xa0d9, 0xbf84, 0x3b79, 0xa6c3, 0xb089, 0xbfb0, 0x4303, 0x331b, 0xb229, 0x4341, 0x0bbb, 0x1ad9, 0x4047, 0x1ae3, 0x431c, 0xa448, 0x4179, 0x432c, 0xb281, 0x348d, 0x4170, 0xbf47, 0x43e2, 0x41a1, 0x419f, 0xa0fb, 0xb2d7, 0xb2d1, 0x412f, 0x467f, 0xba40, 0xb2e0, 0xbad8, 0x423b, 0x4628, 0x43ea, 0x41b9, 0x41eb, 0xb2b3, 0xbf28, 0x4350, 0xa460, 0x433f, 0x4032, 0x4390, 0xb287, 0x1865, 0x4668, 0x448c, 0x41b5, 0xbf75, 0x41ce, 0xbacd, 0x18ba, 0x1c8d, 0x41fc, 0x42eb, 0xbf18, 0x401d, 0x3d5d, 0x4122, 0xb01a, 0x40c4, 0x4321, 0xa7b0, 0xba1f, 0x4035, 0x0f4e, 0x2829, 0x031f, 0x464a, 0x404f, 0xbf70, 0x4093, 0x4321, 0x42ad, 0x4230, 0x4124, 0x4218, 0x00a8, 0xa451, 0xb2b3, 0xbf7c, 0xb26f, 0x44d3, 0x423b, 0xbfd0, 0x04af, 0xb28f, 0x4335, 0x39ab, 0x42c8, 0xbf24, 0xbf70, 0x3665, 0xb24e, 0xb2d8, 0xa20d, 0x10a4, 0x407e, 0xb0c1, 0xbfc5, 0xb281, 0x400e, 0x4352, 0x46c8, 0xbfb8, 0x4074, 0x4410, 0x4249, 0x4335, 0x248a, 0x43aa, 0x4074, 0x4171, 0x43fd, 0x4060, 0x1fe9, 0xbfae, 0x4477, 0x2eb5, 0x410a, 0x187d, 0x41aa, 0x4137, 0x434c, 0x4353, 0x1ff7, 0xb215, 0x3c25, 0x183f, 0xb2ad, 0xbf4b, 0xb2c7, 0xb00b, 0x4339, 0x417b, 0x44f3, 0xaa2f, 0x4304, 0x43e5, 0x00a0, 0xba0a, 0x41b8, 0x4250, 0xbf22, 0x45bd, 0x41f0, 0xbaf1, 0x419a, 0x1d5c, 0x2eae, 0x42e7, 0x43e3, 0x4680, 0x433b, 0xb290, 0x26f8, 0x4398, 0xbff0, 0x41e6, 0x230e, 0x4173, 0xba5c, 0x077b, 0x4202, 0x40ff, 0xb2f9, 0x4137, 0xba2e, 0xbf90, 0xb08f, 0xb052, 0x42fe, 0xbf9a, 0xb046, 0xb023, 0x4348, 0x286e, 0x1c5b, 0x40af, 0x1c83, 0x41ae, 0xb2d7, 0x4198, 0xbafd, 0x42fd, 0x43ae, 0x3637, 0x30a2, 0x42e9, 0x34b2, 0x409b, 0x1fe7, 0x07b7, 0x19ea, 0xbf46, 0x2813, 0x1bb9, 0x42d9, 0x433b, 0xa211, 0xb24a, 0x24a3, 0xa199, 0x1fde, 0xb2c6, 0x4337, 0xbfce, 0x2ee1, 0x3710, 0xb20a, 0x41d4, 0x4026, 0x4361, 0x0eaf, 0x2d39, 0x1542, 0x04c4, 0x4361, 0x419b, 0x40e2, 0x417d, 0x4060, 0x1f09, 0x3473, 0x46ab, 0x430c, 0x400c, 0xb2d7, 0x4334, 0x4054, 0x0f0c, 0x188e, 0xbf92, 0xbaed, 0xb299, 0x4381, 0x0ac1, 0x0c56, 0x4267, 0x401f, 0x33e1, 0x4304, 0x3db4, 0xb0a2, 0x4635, 0x41f5, 0xbf92, 0x3fad, 0x4115, 0x1cf4, 0xb2bb, 0x4229, 0x437c, 0x4097, 0x412e, 0xa0b9, 0x4356, 0xb2ca, 0x43b0, 0x42f0, 0xba39, 0x4348, 0x4263, 0x4335, 0x42dc, 0x44f0, 0x42c6, 0xaa4f, 0x42bc, 0x43ed, 0xbf87, 0xb03d, 0x41cc, 0x1a30, 0x415e, 0xb257, 0x0f62, 0x4469, 0x4051, 0xa912, 0x4303, 0x4214, 0xb07d, 0x4067, 0xb264, 0x1dbe, 0xbf97, 0x41b8, 0x415c, 0x42d6, 0x414d, 0x13ac, 0x401c, 0x40ed, 0x25d9, 0x1883, 0x41e9, 0x410b, 0x1a38, 0xbfaa, 0x19e7, 0x19f6, 0x19db, 0x4173, 0x41a5, 0x17bc, 0x412d, 0x44d9, 0x4010, 0x1a2b, 0xbaf4, 0x393a, 0xba48, 0xb22d, 0xb0f5, 0xbaf2, 0xa232, 0xba3e, 0x41ce, 0x22dd, 0xbf8c, 0xb21b, 0x1ecd, 0x43a2, 0xba6b, 0x4044, 0x401e, 0x404b, 0x1854, 0xaafa, 0x4120, 0xa640, 0x43f7, 0x40c6, 0xb037, 0xa95e, 0xb2c5, 0xa3a2, 0x41f0, 0xb0e9, 0x4250, 0x4233, 0x438a, 0x401c, 0x46c5, 0x42ca, 0x4366, 0xbfd3, 0xba2b, 0x4029, 0x4060, 0x400f, 0x36eb, 0x404d, 0x400b, 0x171b, 0xb0c3, 0x4027, 0xb2eb, 0x0c68, 0xb04c, 0xa245, 0xb252, 0xb252, 0xb2e1, 0x414a, 0xbf0d, 0x426e, 0x1efb, 0xb2ba, 0x1865, 0xbafc, 0xb005, 0x41da, 0x40ce, 0x3cbd, 0x043e, 0x404c, 0x3d77, 0x4685, 0x1b3d, 0x2daa, 0xa0d8, 0xb2e3, 0x43b3, 0x4181, 0x2bba, 0x4316, 0x41dd, 0x4197, 0xbfc1, 0x0e0d, 0x430f, 0x11cd, 0x3a11, 0x413a, 0xb2b7, 0x4013, 0x409d, 0xb209, 0x281f, 0xba08, 0x41f6, 0xb024, 0x428b, 0xbafb, 0x42eb, 0x40c5, 0x11a0, 0x1876, 0xb009, 0x4004, 0xb224, 0x4395, 0xb04c, 0x3b7a, 0xbf01, 0x439b, 0xb247, 0x4162, 0x2a56, 0x1a27, 0x1d9a, 0x4065, 0xbf11, 0x05ff, 0xb25a, 0x1c63, 0xba4c, 0x3bdb, 0x40da, 0x33b3, 0x36fc, 0xbf7e, 0x41c5, 0xb0e1, 0x408f, 0x43a6, 0x187d, 0x4080, 0x009a, 0x41a1, 0x29d0, 0x41ad, 0x1c45, 0x37ad, 0x3de5, 0x1d62, 0x09f7, 0x402d, 0x43e5, 0xba78, 0xb221, 0xbad6, 0x1e4b, 0x289e, 0x43f0, 0x1bee, 0xb2dd, 0x43cc, 0xbfc2, 0x1d46, 0x40c2, 0xaaf5, 0x171e, 0xbfdf, 0x419c, 0xb2e8, 0xb20d, 0xbad1, 0xbadf, 0x4581, 0x27ba, 0x406b, 0x1a29, 0x436a, 0x466d, 0x121a, 0xb2e4, 0x4220, 0xba0f, 0x19d6, 0x1950, 0xb2ff, 0xbfb0, 0x1a2c, 0xb215, 0x4162, 0xbfcd, 0x05c0, 0x42cc, 0x40f1, 0xb2da, 0x05a2, 0x2929, 0xb0c1, 0xbf14, 0x4381, 0xbad8, 0x4480, 0xb0b6, 0x192e, 0x1f16, 0xba09, 0xbfc0, 0x4313, 0xb279, 0x40e9, 0x41af, 0x4233, 0x45e4, 0x4238, 0xb248, 0xb2a8, 0xb0f9, 0x3043, 0x4107, 0x070c, 0xba6a, 0x25de, 0x4212, 0x19d1, 0x41a4, 0xa17f, 0xbf36, 0x41fe, 0x08d3, 0x405f, 0xbff0, 0x41b2, 0x43e3, 0x4182, 0x08f4, 0x4293, 0x4267, 0x4030, 0x1822, 0xb2d6, 0x0575, 0x2c57, 0xb250, 0x166f, 0xaa81, 0x1c30, 0x4205, 0xab93, 0xbf60, 0xac46, 0x4328, 0xbf79, 0xb201, 0x33c5, 0x2c7f, 0x410c, 0x424e, 0x2676, 0xb0ce, 0x1f23, 0xbaf2, 0xb043, 0x392d, 0xb019, 0x04fb, 0x4227, 0xac6a, 0x27c0, 0x42a5, 0xba24, 0xbaf8, 0x4566, 0xb04f, 0x4299, 0x1cc3, 0x06e8, 0xb249, 0xbf00, 0xb0ba, 0x1be0, 0xbf96, 0x1251, 0xb2da, 0x3640, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x32016fcd, 0xf25a55e0, 0xc71b8a9f, 0x506131e5, 0xb5f08c7b, 0x8bec972b, 0x7ac97655, 0x7a33d75d, 0x0edf0fa3, 0x7cb7ebe2, 0x7905d5a9, 0x340efd29, 0x6029b34a, 0xfe1edb24, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0xef04ff40, 0x00000013, 0x00000003, 0xffffc003, 0xef050000, 0x08000000, 0x00000076, 0x000000c0, 0xe86c0001, 0x61d69c09, 0x29ccbeb4, 0xffffcd3f, 0x63b7d8f7, 0x0000049b, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1e27, 0x1948, 0x1be1, 0x43de, 0x0cb5, 0x25a1, 0x4599, 0x0a02, 0xb22d, 0xb224, 0xba71, 0x420e, 0x1e8e, 0xb09a, 0xb2db, 0x40d0, 0xbf5f, 0x3342, 0x1e2b, 0x42b0, 0x43a6, 0xb2ab, 0x43dc, 0xaad3, 0x186c, 0x1b0b, 0xa519, 0xa22e, 0x2d3c, 0x1bde, 0x138c, 0x42ff, 0x4225, 0xba3f, 0xa44f, 0x4009, 0x2ad5, 0x417b, 0xb262, 0x43db, 0x41c3, 0x436b, 0xba2c, 0x40c9, 0x42ee, 0xba09, 0xbf75, 0x408d, 0x2dc2, 0xb27b, 0xb09e, 0x4468, 0x40da, 0x334e, 0x42f9, 0x40c7, 0x414b, 0x42b9, 0x4476, 0x40b3, 0x41b8, 0x4267, 0x41ac, 0xb25c, 0x4095, 0xbfcc, 0xb2e8, 0xb249, 0x1942, 0x4156, 0x1fae, 0xb27f, 0xb2ca, 0xbf60, 0x14ca, 0x408b, 0x12db, 0xb202, 0x40cf, 0x4037, 0xb2a7, 0x43f7, 0x41c0, 0xbf8a, 0x4340, 0xba72, 0x0994, 0x1910, 0x4442, 0xb073, 0x4351, 0x41fe, 0xbac0, 0x4415, 0x1f23, 0xb0ba, 0x3850, 0x4334, 0xbf75, 0x40bb, 0x1e9f, 0x40f5, 0x415c, 0xb046, 0x0819, 0x4222, 0xaae3, 0x3564, 0xb2ca, 0xba1d, 0x0578, 0xb26f, 0x4392, 0x127c, 0xa13e, 0x4307, 0xbfc5, 0x38f9, 0x40ae, 0x4013, 0xb24a, 0x422f, 0x436e, 0xb23a, 0x403c, 0x1d4e, 0xad67, 0xb28e, 0xbaf9, 0xba44, 0xb25d, 0x43be, 0x1ba1, 0x402e, 0x43c7, 0xba0e, 0xba5e, 0xbfad, 0x43ec, 0x42c4, 0x41f0, 0x1e15, 0x1ada, 0x42e2, 0xa13f, 0x086d, 0x4427, 0x4249, 0x416e, 0xb29c, 0x4388, 0x42c6, 0x42a9, 0x43e5, 0x41f1, 0x4024, 0x44fb, 0x40ba, 0xba65, 0x4143, 0x2a73, 0x21c3, 0x401d, 0x0307, 0xbf52, 0x2b55, 0x4412, 0xaf31, 0xbf99, 0x4374, 0x41c6, 0x4194, 0x187d, 0x03ca, 0x42a7, 0xbaca, 0x1c5c, 0x44a9, 0xba03, 0x1951, 0xaa90, 0xba3d, 0x0779, 0x1c92, 0xbf64, 0x1f8a, 0x3e68, 0xb2e4, 0xb078, 0x0aac, 0x1c35, 0x4105, 0x1fd2, 0x4260, 0x4119, 0x3a45, 0x43e5, 0x42e8, 0xa547, 0xb277, 0x3210, 0x2663, 0x4271, 0xb228, 0x4242, 0x4671, 0x40b7, 0x4328, 0xba4a, 0xbf22, 0xba26, 0x3a73, 0xad7d, 0x1ef1, 0x4114, 0xbf00, 0x420a, 0x4287, 0x40c6, 0xa529, 0x1c0e, 0x43c5, 0x0179, 0x305b, 0x4323, 0x1e26, 0x4261, 0x0a63, 0x45ae, 0x404d, 0x2fd0, 0x4341, 0xb0fb, 0xbfd5, 0x2fc4, 0x1f1e, 0x42d8, 0xbac4, 0xbae0, 0xb2ea, 0x281e, 0x17d1, 0x18fe, 0x4243, 0x4265, 0xb278, 0x4193, 0xaccc, 0x41e2, 0x4129, 0x4111, 0x41e7, 0xb02b, 0x42b1, 0xb252, 0x405e, 0x2ea6, 0x4355, 0xb29d, 0x408d, 0x4272, 0xbfbe, 0x4301, 0x425c, 0x43ef, 0x1be2, 0x1c16, 0x2a79, 0x3f7d, 0x446c, 0x1c40, 0x1890, 0xb20f, 0x19bc, 0x3f0f, 0x4109, 0xba4a, 0x1f0f, 0xb2b4, 0xba7e, 0x418a, 0x43e7, 0x094e, 0x4367, 0xaaf4, 0xbade, 0xbac4, 0x4082, 0xbfa4, 0x3bab, 0x4172, 0xb27e, 0x1bb3, 0x16c0, 0xb217, 0x408d, 0xbad1, 0xb202, 0x0c8a, 0x41d6, 0x432f, 0x1f7a, 0x1ab8, 0x43aa, 0xbae5, 0xbf8a, 0x4342, 0x0d85, 0x2184, 0xb2d6, 0x24ec, 0x4281, 0x4393, 0xbfba, 0x4542, 0x4368, 0xb05f, 0xb22b, 0xa6d1, 0x2917, 0x4071, 0x427a, 0xb2d9, 0x0c5b, 0xb0e2, 0xba75, 0x410d, 0x3696, 0x4078, 0x4203, 0xb2da, 0xb225, 0x4073, 0x408e, 0x43ec, 0x4335, 0x10a4, 0x33f6, 0x402e, 0x4264, 0xb240, 0xbfcb, 0x4314, 0x1b15, 0x41d7, 0xb0f6, 0x0643, 0xba6d, 0x4073, 0xad70, 0xbad7, 0x4646, 0x4275, 0xae3f, 0x46d5, 0xbf35, 0x4390, 0x0321, 0x06c6, 0x4219, 0x02fe, 0x4243, 0x41dd, 0x1e18, 0xb226, 0xbf67, 0x432d, 0x15c5, 0xb273, 0x4043, 0x1fb3, 0xb28e, 0x1df2, 0x42f4, 0x187f, 0xbfc2, 0xba73, 0x4356, 0x3703, 0xbfb2, 0x423d, 0x2afd, 0x412e, 0x42ff, 0xba54, 0xba15, 0xba18, 0x186a, 0xbf70, 0xba3e, 0x43bb, 0x0d94, 0x0940, 0xb015, 0x2f56, 0x4196, 0x4227, 0x437d, 0x40da, 0xb261, 0x4312, 0x4061, 0xbf32, 0x0d8e, 0x4203, 0x4308, 0xbac4, 0x437a, 0x4074, 0xbf5d, 0x427b, 0xb2c7, 0x43e4, 0x39b8, 0xb272, 0x42d4, 0x4183, 0x423e, 0x44ba, 0x428a, 0x2b49, 0x463f, 0x410a, 0x0679, 0x4194, 0xb0dd, 0x41d8, 0x43c4, 0x0a84, 0xbfd7, 0x3b2c, 0xb285, 0x41db, 0x40e2, 0x0e78, 0x43df, 0x4212, 0xbf00, 0x4072, 0x433f, 0xb26b, 0x4389, 0x431a, 0x1890, 0x4264, 0x1f58, 0xbf8a, 0x46d8, 0xa682, 0x1b9e, 0x41d6, 0xbfa2, 0x438e, 0x42f7, 0x46b9, 0x416d, 0xba21, 0xb015, 0x428c, 0x1135, 0x4221, 0x42eb, 0x2cbd, 0x40a3, 0xb033, 0xb2de, 0x4562, 0x43c1, 0x433d, 0x406a, 0xb273, 0x0ddf, 0x104a, 0x440a, 0x2b0d, 0x40ff, 0x40af, 0x43d5, 0xbf42, 0xbacd, 0x143b, 0x43f2, 0x4267, 0xbf7f, 0xb009, 0x1925, 0xb054, 0x1c76, 0x0fb2, 0xb2bd, 0x42cd, 0x43f9, 0x40b1, 0x42af, 0x40df, 0x42fb, 0x421a, 0x4259, 0x311c, 0x4057, 0xba52, 0xbf9f, 0x1f33, 0xba70, 0x44c3, 0xbae6, 0x3643, 0xbaf1, 0x4242, 0x00ff, 0x43ba, 0xb01b, 0x40c8, 0x4551, 0xb26b, 0x14dd, 0x43cd, 0xbfa8, 0xb29f, 0x31ff, 0xb2cd, 0xb2b9, 0x43ce, 0xa0da, 0x46e8, 0x43cd, 0x42a5, 0x4346, 0x431b, 0xba29, 0xb229, 0xbf44, 0x1ed8, 0x42a0, 0x42ed, 0xb067, 0x4217, 0x418a, 0xb020, 0x0ba2, 0xb25e, 0x1498, 0x238f, 0x1f2c, 0x1c97, 0xbf53, 0x41b2, 0x19de, 0x41ae, 0xb050, 0xbf25, 0x4139, 0x41dd, 0x4378, 0xa7a1, 0x1a9e, 0x0941, 0xb22f, 0x2449, 0x4048, 0xba34, 0xb2dd, 0xb2b3, 0xbfa7, 0x438d, 0xba40, 0x03a0, 0x0a6d, 0x11d7, 0x2d4f, 0xb2b8, 0x43c7, 0xb010, 0xb04e, 0x4335, 0xb233, 0x0886, 0x184c, 0x45a9, 0x42a7, 0x42cf, 0x1a82, 0x0bf2, 0x043d, 0x40e7, 0x428b, 0xbf3f, 0x3742, 0x1ac4, 0x4112, 0xbfd0, 0x41db, 0x4128, 0xab3c, 0xbaee, 0x4108, 0xba1b, 0x4298, 0x296b, 0x405a, 0xacd9, 0x4439, 0x392f, 0xbad4, 0x46d5, 0x4349, 0xbfa5, 0x18bd, 0x15c3, 0x4124, 0x1e91, 0xba64, 0xb26b, 0x45a9, 0x1e72, 0xb2e1, 0xa04c, 0x0fa8, 0x418c, 0xabfa, 0xaafd, 0x420f, 0x243b, 0xb0d6, 0xbfa0, 0x4485, 0x4360, 0x41f9, 0x418e, 0x41b2, 0xbf7c, 0xb22a, 0x421e, 0xaa51, 0x40cd, 0x41b0, 0xbfbb, 0x1e7f, 0x4224, 0x43be, 0x420a, 0xbfe1, 0x46d3, 0x4364, 0x427e, 0x404a, 0x422b, 0x3a5f, 0x4280, 0xba77, 0xb0ee, 0x41e9, 0xbfca, 0x4230, 0x4264, 0x2b53, 0xba07, 0x4136, 0xba67, 0xbfd7, 0xb0d5, 0x059a, 0x406a, 0x4216, 0x43d9, 0x3325, 0xa91c, 0xb048, 0x415c, 0xb20e, 0xb2c7, 0xba6f, 0x18d9, 0xba46, 0x1abb, 0x433a, 0x41d0, 0xbf55, 0x4300, 0x4030, 0x412b, 0x37b3, 0xbfd0, 0xa03a, 0x40c0, 0x05ba, 0xb26d, 0x437e, 0x1049, 0x43c6, 0xb00f, 0x44d0, 0x441b, 0x31f0, 0x40a5, 0x199e, 0x40ec, 0xbfa5, 0x1983, 0x4377, 0x40de, 0x2838, 0x1b98, 0x1385, 0x409b, 0x417a, 0x460c, 0x436d, 0x3510, 0xba26, 0x3883, 0x4110, 0x4495, 0x409e, 0xbfae, 0xb297, 0x4678, 0x143a, 0x40a3, 0xb221, 0x333b, 0x1f02, 0xb031, 0x1d7a, 0x4061, 0x4138, 0x41dd, 0xb2dc, 0xb2ed, 0xba39, 0xbf28, 0xb2e8, 0x3fbf, 0x411c, 0x3271, 0x14a9, 0x414f, 0xa624, 0x1d2c, 0xbfa0, 0x4195, 0x4361, 0xb234, 0xb052, 0x4097, 0x43be, 0x4083, 0xba41, 0xbf34, 0xad4e, 0x4109, 0xb256, 0xb2e5, 0x39b3, 0x43ae, 0xb2e3, 0x2479, 0x40ba, 0xb229, 0x40d2, 0xbf7f, 0x3365, 0x41ba, 0xba78, 0x1b0c, 0x40db, 0x4358, 0xa8af, 0x42d7, 0x4573, 0xb29d, 0x15fb, 0xb00a, 0xb2ee, 0xb25c, 0xbfc1, 0x428b, 0x0d95, 0x4121, 0x43d1, 0x424c, 0x2cb8, 0xbf4c, 0xa326, 0x42b0, 0x14f3, 0x43eb, 0x379b, 0x41b8, 0xba3d, 0x1d51, 0x42c0, 0x1012, 0x414d, 0x221b, 0x434d, 0x38f4, 0x19d3, 0xb22e, 0xbfcb, 0x41d2, 0x4124, 0x3cb4, 0x1be3, 0x40ae, 0xba40, 0xb0a4, 0x1bec, 0xbac0, 0x41da, 0x43a7, 0xbf21, 0x41f5, 0x406f, 0xb090, 0x2a79, 0x4314, 0x1e36, 0x1a8e, 0x4163, 0x336b, 0xb0c0, 0x43e4, 0x4266, 0x4312, 0x1e45, 0x42ba, 0x4215, 0x0214, 0xa1c8, 0x4032, 0x43ea, 0xba10, 0x43c3, 0x41cd, 0x46e5, 0x3272, 0x21ec, 0x26d0, 0xbf2f, 0x4266, 0xba19, 0x419e, 0x3453, 0xb2b1, 0xb21b, 0xb2ad, 0x400a, 0x40f5, 0x1c18, 0x433e, 0x40a5, 0x1e4e, 0xb0ec, 0x4354, 0x3d65, 0xaac5, 0xba57, 0x4649, 0x42ab, 0x4123, 0x4434, 0x43eb, 0x430c, 0x2549, 0x0da8, 0x4309, 0x4119, 0xbfa3, 0x4380, 0x24cb, 0x410d, 0x43da, 0x431a, 0x404c, 0xb008, 0x41f2, 0x4588, 0x4112, 0x4572, 0xb231, 0x4196, 0x14a7, 0xa3f8, 0x407d, 0x42a1, 0x4074, 0xbf9a, 0x40ef, 0x4450, 0xb2a4, 0x463d, 0xb0cb, 0xbfa0, 0x4141, 0x316d, 0x19f4, 0x1273, 0x4433, 0x3c10, 0x4228, 0x441e, 0xbfd6, 0x42bc, 0x4088, 0x430e, 0xb01d, 0x1fb4, 0x4054, 0xb287, 0x40ce, 0x4012, 0xba62, 0xb26d, 0x3cfc, 0x43d4, 0x2128, 0xba0d, 0x1837, 0x460b, 0x1add, 0xba23, 0x1abf, 0x437f, 0xbf2d, 0x43bb, 0x407e, 0xb09e, 0x428d, 0x4263, 0x18d9, 0x2f7a, 0x405d, 0x406b, 0x4293, 0xb251, 0x4151, 0x4300, 0x4383, 0xb240, 0xb2f2, 0x2fc6, 0x2bc6, 0xb234, 0xba56, 0xbf17, 0xba48, 0x41e6, 0xba28, 0x4035, 0x4324, 0xb050, 0x43e3, 0x1fb1, 0x42ab, 0x42d4, 0x0549, 0x456b, 0x41d0, 0xb0c4, 0xb07a, 0x42b5, 0x2d57, 0xba52, 0x40c0, 0x43f1, 0xb288, 0xb2db, 0xb231, 0xba37, 0xb2fd, 0x11f8, 0x416d, 0xbfa4, 0x41de, 0x3da7, 0x4157, 0x3ddb, 0x4083, 0x40a4, 0x43ea, 0xb004, 0x295b, 0x416b, 0xbfb6, 0x4695, 0xb275, 0x41db, 0x19b8, 0xb2ad, 0x402e, 0xbf7b, 0x44a4, 0xbfa0, 0x4035, 0x404c, 0xb034, 0x41e9, 0x46f0, 0xbfb2, 0x0925, 0xb022, 0xb0df, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3ca5ddec, 0x567ac17b, 0x5090d856, 0x31dc27ca, 0xce87e6ed, 0x44ad92e1, 0xe67fdf6b, 0xfc9f1f92, 0x0b5c7af3, 0x2abf17e7, 0xcbcf4b4e, 0xcccdc713, 0x7e5f0f46, 0x63825539, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x00048400, 0x00001000, 0x00000181, 0xffffdebf, 0x00060440, 0x0000fe7e, 0x00008000, 0x00040400, 0x00000000, 0xfffffbe7, 0xcbcf4b30, 0xcbcf4b30, 0x7e650f86, 0x0000015d, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x3a7c, 0x0655, 0x41a9, 0x3921, 0x4147, 0x429a, 0x13d5, 0x415e, 0x43ad, 0x373d, 0xb2e7, 0xa82f, 0x45b2, 0xb299, 0x4174, 0xbf15, 0x1155, 0xba32, 0x3a9c, 0x4037, 0x437e, 0xb01c, 0x4245, 0xbf92, 0x4029, 0x4111, 0x3fd8, 0x4299, 0x1b95, 0x404a, 0x4057, 0x4315, 0x4047, 0x423f, 0xaf03, 0xb27d, 0x22a0, 0xb012, 0xa3cf, 0x40d9, 0x40c6, 0x0e90, 0x4694, 0x40fd, 0x418b, 0x03c9, 0x44e0, 0xa932, 0x4397, 0xba5b, 0xbf3d, 0x4223, 0x1ff6, 0xb296, 0x4322, 0x28dc, 0x32c9, 0x4108, 0xbfdb, 0x433f, 0xb249, 0xb00c, 0x1d27, 0x46cd, 0x1aa1, 0xba2b, 0x1c1d, 0xa28d, 0xbfd5, 0x0057, 0xbac6, 0x4308, 0x1a2a, 0x42d1, 0x18ff, 0x432f, 0xbae0, 0x415c, 0xb032, 0x3c6b, 0x43e3, 0xb265, 0x42a1, 0x4013, 0x430f, 0x1c8c, 0x1fa1, 0x33f7, 0x4168, 0xbf21, 0x2136, 0x431e, 0x422d, 0xbae0, 0x432d, 0xb0ed, 0xb2d8, 0x0a2c, 0x41e8, 0xbf80, 0xbf88, 0xba28, 0xb2aa, 0xb267, 0x430e, 0xbf47, 0x4091, 0x40a5, 0xb2b3, 0x1b9e, 0x41e0, 0x420f, 0x1bfd, 0x1a1f, 0x1e8e, 0xbf7e, 0xbaeb, 0xa08a, 0x437e, 0x4339, 0xb006, 0x221f, 0x42b5, 0xb2c8, 0xb202, 0xbaef, 0x1902, 0x4161, 0x413c, 0x42b3, 0x18ba, 0x2f5c, 0xb252, 0xba3d, 0x4166, 0x43ee, 0x438d, 0xbf8d, 0x46e2, 0x0540, 0x303a, 0x43f5, 0x42a2, 0x4081, 0x34fa, 0x024c, 0x11d5, 0x2ac3, 0xbf00, 0xbfcc, 0x45c3, 0x439d, 0x4019, 0x401d, 0x40b2, 0x0891, 0xa357, 0x0a3d, 0xbf00, 0x0e7b, 0xb29d, 0xae6d, 0x438f, 0x4364, 0x4311, 0x42bd, 0x19ad, 0x44a4, 0x4624, 0xb2e5, 0x4134, 0x43e5, 0xbf16, 0x402d, 0x4391, 0xba51, 0x438f, 0x18f7, 0x455a, 0xafda, 0xbf6f, 0x4125, 0xbf60, 0x2490, 0xba2a, 0x038d, 0x410b, 0x42e6, 0x4224, 0x40b0, 0x1a97, 0xa5ee, 0x4073, 0xbf90, 0x4362, 0x1fbf, 0x4314, 0x4051, 0xb234, 0x4336, 0xbf60, 0xb046, 0xba05, 0xa6cc, 0xb2e1, 0x3022, 0xbfe0, 0x4608, 0xb2b0, 0xbaf1, 0xbfc3, 0x2620, 0x45b5, 0xbad8, 0x408d, 0x24c8, 0xb242, 0xb0f6, 0x4284, 0x4251, 0xaed4, 0x4302, 0x4197, 0xba77, 0x1678, 0x45a8, 0xba65, 0x4333, 0x1e84, 0x1af4, 0x4212, 0xbaff, 0xa4d9, 0xac32, 0xbfd0, 0x2306, 0x42ac, 0xbfaf, 0xb04c, 0xb285, 0x21dc, 0x42a6, 0x4121, 0xbfc0, 0x432f, 0xbf99, 0x3be7, 0xbf90, 0x40e6, 0xb288, 0xb27f, 0x4328, 0x245d, 0x41ef, 0x43bd, 0xb291, 0x40bd, 0xb0b5, 0xbf90, 0xbfe4, 0xb209, 0x1860, 0x4270, 0x42f8, 0xbfb0, 0x40c2, 0xbaeb, 0x09fb, 0x44f1, 0x41fc, 0x406a, 0x4134, 0xbfb0, 0x332a, 0xb26e, 0x0619, 0xba45, 0xbf89, 0x432c, 0xb2c3, 0xac47, 0x1d59, 0x2714, 0xb098, 0x433a, 0xa38a, 0x4573, 0x40b7, 0x41d6, 0x4180, 0xb259, 0xad42, 0x46c1, 0xb076, 0x406b, 0x4131, 0x40fb, 0x243f, 0x4285, 0x1b92, 0xb256, 0xa70f, 0x4168, 0x40bd, 0xb290, 0x12c5, 0xbfb8, 0x3068, 0x42d4, 0x410b, 0x1ef1, 0x4258, 0x0368, 0x46b5, 0x406b, 0x40aa, 0x4321, 0x1f9d, 0x4173, 0x430c, 0x43e5, 0x4026, 0xb2d8, 0xbf01, 0x3733, 0xbac9, 0x4275, 0x4276, 0x43d1, 0x1b8c, 0x4169, 0x4146, 0xa9aa, 0x4302, 0x1316, 0x4416, 0x43ca, 0x02f5, 0x42bb, 0xa7f7, 0xb204, 0x4632, 0x416c, 0xb030, 0x409a, 0x404b, 0x4385, 0x41cd, 0xb2b5, 0xb287, 0x4390, 0xbfce, 0x1b41, 0xb279, 0x1df6, 0x4220, 0xb020, 0x40b1, 0x45d3, 0x1d7b, 0x4162, 0x4234, 0x4201, 0x41cb, 0x30ba, 0x4039, 0xba5e, 0x4402, 0x429d, 0xba4e, 0x4652, 0xb2e8, 0x19cc, 0x38fd, 0x41b3, 0xbfd0, 0x43d6, 0xbf01, 0x43ee, 0x42c8, 0x07a7, 0x1893, 0x43c5, 0x4013, 0x1288, 0xba4a, 0x1534, 0x4091, 0x429b, 0x426b, 0xb234, 0xbf3a, 0x2bf6, 0x1e2f, 0xb28a, 0xa71a, 0xba32, 0x41e8, 0xbf65, 0x43c9, 0xacce, 0x403b, 0xb250, 0xaa0b, 0xabc5, 0xa194, 0xb214, 0x421d, 0x40cc, 0xa0e9, 0xb2d7, 0x1eff, 0x43d1, 0x417e, 0x42f1, 0x4275, 0xba1b, 0x3012, 0x13d4, 0x1fdd, 0x0af4, 0x4255, 0x4264, 0xbf9b, 0x1f56, 0x43e9, 0x418b, 0xbad9, 0x410a, 0x083d, 0x30af, 0xba48, 0x2c36, 0xbfdb, 0xb0fa, 0xb216, 0x3970, 0xba5f, 0x0556, 0x435a, 0x41d2, 0xb2db, 0x4395, 0xb020, 0xa2df, 0x43f7, 0x1dca, 0xb017, 0xba46, 0xb2aa, 0xbfa0, 0x07b6, 0x4040, 0xbf60, 0xa3c8, 0xbf69, 0xb232, 0x3ac1, 0x42bc, 0x44a3, 0xbadf, 0x0651, 0xb27d, 0x1f97, 0xbf93, 0x1625, 0xb2f8, 0xb2b8, 0x1244, 0xbf00, 0x41ac, 0xba03, 0x4030, 0x3d8e, 0x08b1, 0x41c7, 0xb04e, 0x3332, 0x1f10, 0x2903, 0x2272, 0x462e, 0xb204, 0xbf3f, 0xb2d0, 0x4375, 0x4204, 0x4062, 0x180c, 0x4084, 0xb298, 0x0fc2, 0xb270, 0x4085, 0xbf9b, 0x243f, 0x4390, 0x4221, 0x4445, 0x43e3, 0x45a9, 0x43d4, 0xbfdc, 0x402e, 0xbaec, 0xa8b2, 0x40bd, 0x2eb6, 0x4281, 0xb206, 0xb268, 0x30ef, 0x4043, 0x434e, 0x1a1d, 0x14c5, 0x43a9, 0x1fa9, 0x4376, 0x41bd, 0x423c, 0x43ab, 0x1aa7, 0x42d6, 0x0513, 0x4033, 0x31c5, 0x410f, 0x1db1, 0x40d3, 0xbf0c, 0x422c, 0x427b, 0x4019, 0x4066, 0x419e, 0x1cc6, 0x3571, 0xa6f0, 0x1813, 0x40d3, 0xb099, 0x035f, 0x19c3, 0x42ad, 0x42fb, 0x1d9e, 0xb25f, 0x1a90, 0xb28f, 0xbf3b, 0x3c2e, 0xba0a, 0xb067, 0xb2d9, 0x401c, 0x406e, 0x1b33, 0x4320, 0x43f7, 0x0df9, 0xbf41, 0x4166, 0x42ca, 0x43e7, 0xba7c, 0xb077, 0x1792, 0x43d3, 0x4085, 0xb27a, 0x4293, 0x403d, 0x410c, 0xbf08, 0x11e3, 0x41c2, 0x4083, 0x4364, 0xa113, 0x4010, 0x207c, 0x42aa, 0x1b60, 0x25fe, 0x4097, 0xb202, 0x40fc, 0xb07c, 0xbfdb, 0x041e, 0x40e1, 0x4046, 0x43e8, 0x42a3, 0x424a, 0xba73, 0x4002, 0x4350, 0xb2ae, 0x05a7, 0xb244, 0xb2a4, 0x19b4, 0xbf81, 0x4146, 0x431b, 0x4137, 0x46f8, 0xbfb4, 0x423f, 0xba3c, 0x1c8a, 0x40b6, 0x416f, 0xbade, 0x0565, 0x4027, 0xb2e7, 0x42a7, 0x0c0a, 0x4213, 0x40e5, 0x4058, 0xba68, 0x41eb, 0x4234, 0xbf84, 0x4333, 0xba18, 0x4073, 0x39be, 0x4339, 0x1e78, 0x42e2, 0x1396, 0xba2e, 0x4051, 0x3e8c, 0x40a6, 0xbf5e, 0xbaef, 0x36c9, 0x4060, 0xbfe0, 0x19fb, 0xbf35, 0x40b3, 0xba2d, 0x417a, 0x425b, 0x0bc2, 0xa2c7, 0x43ef, 0x4550, 0x427f, 0x402d, 0x18b3, 0x4104, 0xba2e, 0x1a9a, 0x4243, 0xbacd, 0x4116, 0xb2dc, 0xba6c, 0x410d, 0xbfad, 0x4189, 0x40fb, 0x437f, 0xba6b, 0x1023, 0x4063, 0xb2af, 0xaa18, 0x2514, 0x41c0, 0x32e8, 0x01c7, 0x35a9, 0xbfad, 0x4029, 0xb083, 0xba10, 0xb2be, 0x3276, 0xaf71, 0x4086, 0xb277, 0xbf6b, 0xb2d6, 0xb2f3, 0xb2ae, 0x19c3, 0x4366, 0x443b, 0x4124, 0x43bf, 0xa919, 0x434e, 0x12bf, 0x009f, 0x4006, 0xbff0, 0x43c5, 0xbf1a, 0xb0f6, 0x416d, 0x4114, 0x4624, 0x4161, 0x4089, 0x43b1, 0x4155, 0xbad4, 0xb000, 0xb042, 0x191e, 0x41c1, 0x4013, 0xb24f, 0xbf6c, 0x1dee, 0xb294, 0x1ce2, 0x435d, 0x41b1, 0xad83, 0x11c0, 0x40ce, 0x317f, 0x43c6, 0xb21a, 0xb002, 0xb298, 0x41ae, 0xb20f, 0x05ef, 0xb2a6, 0xb2fe, 0x1ad7, 0x45a0, 0x030c, 0x4089, 0xb26b, 0x4088, 0x4079, 0x3dc9, 0xbf87, 0x42bb, 0x1efc, 0x4018, 0xb254, 0x40ce, 0x3abc, 0xb0be, 0x4017, 0x1890, 0x414e, 0x4670, 0x1c83, 0xbf70, 0xbf41, 0xb277, 0x4283, 0x42c1, 0x41b9, 0x4006, 0xb260, 0x40c6, 0x455f, 0x4169, 0xb09d, 0x19ba, 0x263d, 0x40f9, 0xb07c, 0x41f8, 0x41c3, 0xbf97, 0x4031, 0x40a4, 0x1d03, 0xb21e, 0x4051, 0x003a, 0x4191, 0x409f, 0xb240, 0xbfd0, 0x0eaa, 0x43c4, 0x4262, 0x112b, 0x4264, 0xba2c, 0x4239, 0xbfc1, 0x43d7, 0x422e, 0xb291, 0x323a, 0xbae2, 0xb22f, 0xb0da, 0xb26e, 0x1a25, 0x43e1, 0x4208, 0x407c, 0x1d1e, 0xb27e, 0x1c0c, 0x0edc, 0x43ae, 0x4064, 0x459c, 0xa866, 0xb06f, 0x437a, 0x411e, 0x0a15, 0x1aeb, 0xb228, 0xb21b, 0x426f, 0x42f6, 0xbf34, 0x44bc, 0x016d, 0xac00, 0xb2e4, 0xbafc, 0x41d6, 0x44eb, 0x4063, 0x41bd, 0x409d, 0x39be, 0x438f, 0x45d0, 0x172e, 0x45bb, 0xbf6e, 0xb28f, 0x4262, 0xb2b8, 0x1c01, 0x45c4, 0x188e, 0x432d, 0x3671, 0x4039, 0x4450, 0x0adc, 0x44eb, 0xbf2f, 0xbaec, 0xbfa0, 0x0c02, 0x4293, 0x43c3, 0x19b8, 0xb251, 0x22f4, 0x1f80, 0xba39, 0x42c6, 0x1caa, 0xa3ae, 0x3100, 0x41dc, 0x4252, 0xb00b, 0xba1e, 0x421b, 0x437e, 0x1770, 0x4094, 0xa2d9, 0x1b0d, 0x148d, 0x4328, 0x4569, 0xbf46, 0x4283, 0x43a3, 0x3399, 0x3025, 0x40ce, 0xb284, 0x432a, 0x03d0, 0xbf90, 0xbafd, 0xbfcb, 0x43ad, 0x4013, 0xba27, 0x3d2a, 0x466f, 0x1653, 0xb204, 0x225c, 0xb213, 0x21e4, 0x43d3, 0x0eb4, 0xbae8, 0x415e, 0x4244, 0x4239, 0x4692, 0x2014, 0x411f, 0x1dfb, 0x42a8, 0xbfb6, 0x42f3, 0x1138, 0x22d0, 0xbfbf, 0xbacc, 0x0049, 0xbfa0, 0x4315, 0xba7a, 0x4182, 0x142f, 0x42b7, 0xbaf4, 0x465e, 0x4030, 0xb21d, 0x1a68, 0xb240, 0x2a11, 0xa788, 0x4088, 0x373f, 0xbfc9, 0x4347, 0x4088, 0x40ad, 0xb24d, 0xba22, 0xba5b, 0xb2f6, 0x4294, 0x406d, 0x428c, 0x1e96, 0xba49, 0xb0bc, 0x41a1, 0x4298, 0xb099, 0x410b, 0xbf38, 0xb21e, 0x43a8, 0x41d9, 0x4316, 0x4308, 0xbf07, 0xba54, 0x402a, 0x045d, 0x4356, 0x4077, 0x1573, 0xb208, 0x4069, 0x4100, 0x42f4, 0xaffb, 0x284f, 0xae93, 0x4348, 0x33ec, 0xb2b1, 0x41ce, 0x40a1, 0x1683, 0x427f, 0x42c6, 0x18f3, 0xb29e, 0xb2c0, 0x3d0f, 0xba15, 0x4116, 0xbf12, 0x43bf, 0x1c9d, 0xbae6, 0x4167, 0x42e5, 0x40ff, 0x404f, 0x1a21, 0x4028, 0x4289, 0x438d, 0xbfa3, 0x182c, 0x1cd9, 0x4304, 0x45a0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x916cb869, 0x845ddb9e, 0xc71efbae, 0xb5b0ddd7, 0xc96a166f, 0x0896734c, 0xe975a539, 0xda33958c, 0xf0631e50, 0x552ee4b2, 0x94f77f64, 0x1bf67a8f, 0x4cd88fc1, 0xd2a84a20, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x00000000, 0x0990000d, 0xffa3ffff, 0x0990000a, 0x00000000, 0x00000000, 0xffffffa3, 0x00000000, 0xf0631ef0, 0xf0631ef0, 0x0000005c, 0x1bf68b67, 0x000000a0, 0x00000744, 0x00000000, 0x600001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1e86, 0x39ce, 0xb015, 0x4241, 0xba7a, 0x0ca5, 0xba1c, 0xb2c7, 0x1ade, 0x40e0, 0xbfc0, 0x18d9, 0x4195, 0x43e7, 0x144f, 0x1aa8, 0x402c, 0x19e3, 0x0f08, 0xbf33, 0x0146, 0x0896, 0x4298, 0x4011, 0x40b9, 0x4225, 0xae23, 0xa371, 0xb2ea, 0xb293, 0xaa59, 0xbacb, 0xb203, 0x40e4, 0x42a3, 0x158d, 0x42d3, 0x4435, 0xa63f, 0x1cb7, 0xae3d, 0x1b9c, 0x1ae6, 0x43a5, 0x4682, 0x38c3, 0x4199, 0x40e3, 0x4462, 0xbfd6, 0x1737, 0x4034, 0x413d, 0x1491, 0x40bd, 0x40d4, 0x1b2a, 0xbf9f, 0xbafa, 0xb2e1, 0x435b, 0x4057, 0x414d, 0x43c2, 0x46b2, 0x4247, 0x41a7, 0x422e, 0x4082, 0xb207, 0x1a3a, 0x1515, 0x301f, 0x42a4, 0x391a, 0x41af, 0x4102, 0xb246, 0xbf1c, 0xb2a7, 0x1fcb, 0xbad6, 0x41e6, 0x290a, 0x42c7, 0x1a97, 0x4616, 0x42f6, 0x1668, 0x4144, 0xb229, 0x43de, 0x1abb, 0xaeb6, 0x4658, 0xb2fd, 0xbf80, 0xb027, 0x4068, 0xac56, 0x0b56, 0xba3e, 0xb0ae, 0x2699, 0x4175, 0xbf39, 0x4367, 0x39a9, 0x4377, 0x3b13, 0x0504, 0x41cb, 0x43fe, 0x43a9, 0xb037, 0x4226, 0x4090, 0xb219, 0x4236, 0x4043, 0x435b, 0x163b, 0x41b5, 0x42ff, 0x03d5, 0xbf4e, 0xbad9, 0x42d2, 0x4063, 0x18c2, 0x4441, 0x1dac, 0xb287, 0x40b8, 0x4007, 0xb28d, 0x41a5, 0x4230, 0xbfa0, 0x419f, 0x0084, 0x4042, 0x1fe7, 0x42e3, 0x03a8, 0x2f05, 0x4641, 0xba39, 0xb266, 0x42cd, 0xb0f9, 0xb012, 0x4066, 0xb276, 0xbf3c, 0xb2b6, 0x197f, 0x4091, 0x1871, 0xbf11, 0xba70, 0xa8b5, 0x404b, 0x1bce, 0x43bb, 0x3ac6, 0xb021, 0xb2f4, 0x41f0, 0x4402, 0x2c5a, 0x45cc, 0x437c, 0xb023, 0xb23c, 0x37b9, 0x412d, 0x42b2, 0x43a1, 0xb089, 0x417f, 0x43ad, 0x4220, 0x42b4, 0x4176, 0x2e55, 0xb228, 0xbf0d, 0x3038, 0x4315, 0x4302, 0xba33, 0x43e0, 0x43cd, 0x0765, 0xbfc3, 0x41b6, 0x1bbb, 0x4007, 0xafba, 0x1c38, 0xb25e, 0xb2f2, 0x1ad5, 0x4196, 0xba2c, 0x40b4, 0x1a04, 0xb01a, 0xba71, 0xb2a8, 0x4685, 0xb0b2, 0xb20c, 0xba54, 0x46c5, 0xbf86, 0xa737, 0x4309, 0xb230, 0x03e0, 0xb282, 0x45ae, 0xb293, 0x40d7, 0x40c5, 0x40da, 0xb2fc, 0x40fb, 0x42c4, 0xb217, 0x367a, 0x41bd, 0xba6f, 0xba59, 0xba7a, 0x08b7, 0xbf67, 0x13e5, 0x408c, 0x12cf, 0xa5fd, 0x44ad, 0x34f1, 0xb2eb, 0x407c, 0x2b7a, 0xbfe0, 0x4481, 0xb286, 0x18f7, 0x42c5, 0x1fa6, 0x41b2, 0x42de, 0x40a0, 0x2f21, 0x4090, 0x4309, 0x4071, 0x3c32, 0x4203, 0x42f0, 0xba32, 0x41d8, 0x1daa, 0xbfbf, 0x402f, 0xb2d2, 0x435b, 0x4158, 0x407e, 0x20ff, 0xba0a, 0x1a6a, 0xba33, 0x4093, 0x43f0, 0xbf57, 0x43f9, 0xb2a1, 0x432f, 0xb2f5, 0x41cc, 0x4244, 0x1dc6, 0xb2b1, 0xba27, 0xbfa7, 0x347c, 0xb042, 0xba06, 0x41b2, 0xbaeb, 0x199f, 0xbfa7, 0x432f, 0xb2dd, 0x1a37, 0xb2dc, 0x434d, 0xbac5, 0xbaf8, 0x420e, 0x3f3f, 0xb0b2, 0x4005, 0x2b8a, 0x1c8d, 0x46e8, 0xade4, 0x425b, 0x4152, 0x0240, 0x4175, 0x0d64, 0x449a, 0x4018, 0x4366, 0xb2d5, 0x226b, 0x40ae, 0xb20f, 0xbf57, 0xa2c7, 0x42ac, 0xaf3b, 0x461f, 0xba67, 0x2af4, 0x4230, 0x4087, 0x1543, 0x43a9, 0x4232, 0x432e, 0xba6b, 0x46da, 0xba31, 0xbacb, 0x431a, 0x4343, 0xbfa9, 0x40c7, 0x18fb, 0xb2c6, 0x4066, 0xbf13, 0x4399, 0x4480, 0x3d8f, 0x111f, 0xbf1f, 0xbad3, 0x42be, 0x22b8, 0x438a, 0x0dd7, 0xa490, 0x400b, 0x424b, 0x4285, 0x407a, 0xae75, 0x17ab, 0x41a9, 0x1d62, 0xb214, 0xbf29, 0x4084, 0xa46a, 0x23a2, 0x44d2, 0xb0e4, 0x4203, 0x45ac, 0x4266, 0x1e81, 0x1a45, 0x10c8, 0xb0e2, 0x0833, 0x40aa, 0xb2cf, 0x1866, 0x1948, 0x435b, 0x18e0, 0xb230, 0x3774, 0x41b7, 0x1d3c, 0xb2b5, 0xbf90, 0x4265, 0xbfba, 0x4008, 0xba33, 0x1e27, 0x42d4, 0x1eef, 0xaf74, 0x41b5, 0x4069, 0x0d28, 0x0e57, 0x4254, 0x406f, 0xaac3, 0x1f94, 0x14a1, 0x4290, 0x4141, 0x4216, 0xb01f, 0x081b, 0xbfa1, 0xbfb0, 0x412b, 0xb24b, 0xb035, 0xb21f, 0x4248, 0x3144, 0xba32, 0x41d9, 0x461c, 0x1bb8, 0x40c8, 0xb266, 0xb258, 0xb2d6, 0xbadf, 0x42d4, 0x1a11, 0xbae0, 0x4629, 0xbf80, 0x4268, 0x0cef, 0x410f, 0xba0f, 0xba5e, 0xbf16, 0x42b3, 0xa03d, 0xb2b4, 0xb030, 0x3899, 0x4541, 0xb2e3, 0x1803, 0x16d3, 0xbfc2, 0xb216, 0x41dd, 0x42fa, 0xbf80, 0xafdf, 0x42da, 0x40e8, 0x1c5b, 0xb291, 0x1afb, 0xbf31, 0x41e2, 0x3e9d, 0x3a13, 0x11f2, 0x40ae, 0x400e, 0xba28, 0x40b8, 0x409d, 0x417c, 0x1ee6, 0x4252, 0xaa67, 0x0485, 0x43c7, 0xb0f3, 0x410c, 0x28df, 0xba12, 0x2ed5, 0xbf9f, 0xba74, 0x1d89, 0x44ac, 0x437a, 0x1e57, 0xbff0, 0x40cf, 0x415b, 0xb0a7, 0x414b, 0x402c, 0x4495, 0x29be, 0xa885, 0x39bb, 0x43c7, 0xbfb8, 0xb233, 0x1b91, 0x438e, 0xb21c, 0x1b36, 0xbf61, 0x4378, 0x4393, 0xba2b, 0x34c6, 0x1fef, 0xaa51, 0xa22d, 0xb270, 0x0598, 0xbfb0, 0xbad6, 0x42d9, 0xba35, 0x1bb7, 0x45f3, 0x4115, 0x1cfd, 0x42e9, 0x402f, 0xb20c, 0xbfd0, 0xae0a, 0x40c1, 0xb09d, 0xba08, 0x1fad, 0x41a9, 0x4387, 0x461c, 0xbf53, 0x186f, 0x2e59, 0x42b8, 0x425d, 0xbf11, 0x436c, 0xba65, 0x43b9, 0x4626, 0x44c3, 0x41b6, 0x1884, 0xb057, 0x1e30, 0xbfdd, 0x43a6, 0x405e, 0xa060, 0x423e, 0xb014, 0xba47, 0xad06, 0x1f36, 0xbf75, 0x464a, 0x3179, 0xa268, 0x463e, 0xb086, 0x2610, 0x401f, 0x2f67, 0x4409, 0x42c7, 0x1f54, 0x409b, 0x4111, 0x424a, 0xbf5d, 0x43b2, 0x467c, 0x40dd, 0x4085, 0xa14a, 0xa7d5, 0xba37, 0x4378, 0x4152, 0x43f6, 0x022e, 0x22b8, 0xb219, 0xba5b, 0xbf27, 0x424a, 0x0809, 0x4413, 0xb046, 0xbf60, 0x10bf, 0x1d41, 0x41bd, 0x1ed3, 0xac2b, 0xb2e2, 0xbfa0, 0x0d1b, 0x1fa2, 0xbf0e, 0xba7c, 0x40e4, 0x404e, 0x4593, 0xb276, 0x4170, 0x3ab1, 0xaad4, 0x2013, 0x3e57, 0xb0b8, 0xba09, 0x463b, 0xa62e, 0x43ac, 0x4390, 0x1c3e, 0x41f4, 0x01b9, 0x43b4, 0xb20d, 0xbf69, 0x4246, 0x4183, 0x42f6, 0x1bae, 0x4081, 0xabe8, 0x4013, 0x44c1, 0xa15e, 0x4271, 0x41a5, 0x42e1, 0x1cc6, 0x4302, 0x1176, 0x0ade, 0x0697, 0x41ad, 0x42ab, 0x41c5, 0x4260, 0x454a, 0xba34, 0xbf02, 0xb035, 0xb04b, 0x430d, 0x4418, 0x1bdd, 0xbfba, 0xb26f, 0x1b87, 0x42f2, 0x403c, 0x420a, 0x40b0, 0x1c57, 0xb061, 0x438a, 0x4173, 0x2e49, 0xb2f9, 0xbfc6, 0x41db, 0x1ad3, 0xba5d, 0x456c, 0xba02, 0x4245, 0xb098, 0x4267, 0x4141, 0x4665, 0x44a4, 0x05b1, 0xb2c1, 0xb001, 0x4205, 0x43fb, 0x40df, 0x19be, 0x43a4, 0x405a, 0x463f, 0x4130, 0x41ee, 0xb2da, 0xbf97, 0xac39, 0x4601, 0x41e1, 0x4175, 0xa137, 0x43d0, 0x4182, 0x1bf3, 0xbfd9, 0x409d, 0xb2a1, 0xba51, 0x013e, 0xbac1, 0x12a3, 0x4069, 0xabd0, 0x4220, 0xb0f8, 0xa4f4, 0x198b, 0x2d94, 0x43ee, 0x1fce, 0xba06, 0x022e, 0x42ea, 0x4229, 0x1d1e, 0xb0e2, 0xbfa3, 0x0bed, 0x4095, 0xba7c, 0x0052, 0x403c, 0xbadb, 0x4180, 0xba66, 0x4028, 0x427c, 0x2473, 0xba2c, 0xbf9b, 0xb208, 0xba24, 0x41f2, 0x4377, 0x1dd5, 0x40c1, 0xbf90, 0xb2c3, 0x1b1d, 0x42ac, 0x45ca, 0xbf8a, 0x1edd, 0x276c, 0x46d5, 0x32fc, 0xa25c, 0xb2d8, 0x4063, 0xbfb0, 0x02b4, 0x1c84, 0x2bcf, 0x429e, 0xb0f0, 0x43c5, 0x0fe7, 0x4323, 0xb2af, 0x1c79, 0x42c9, 0xa5ef, 0x41e9, 0x039f, 0x46ec, 0x1cd3, 0x1747, 0xbf6e, 0x42ba, 0xbad7, 0x4048, 0xb25a, 0x1930, 0x4296, 0xb08c, 0x406a, 0xb066, 0x191b, 0x3bd6, 0xba7c, 0x416e, 0x0db1, 0x4273, 0xbff0, 0xbf19, 0xba02, 0xbaf5, 0x4297, 0x41cd, 0x42d0, 0x411a, 0x1adb, 0x3814, 0xbf68, 0xb0b9, 0x415a, 0x4658, 0xb215, 0x4198, 0x4082, 0x43f7, 0xb097, 0xba0e, 0x1ac2, 0x431a, 0xb075, 0x4253, 0xb2a6, 0xbf9a, 0x1816, 0x41d5, 0xb2ef, 0xb0fa, 0x428f, 0x1b75, 0xba57, 0x4241, 0x43b8, 0x41de, 0xb2c4, 0xbaf0, 0xa9d9, 0xbfd4, 0x3b4a, 0xbacd, 0x426c, 0x1fa9, 0x3d6f, 0xbf07, 0xa213, 0x42c7, 0x0aa3, 0x43a1, 0xb090, 0xba23, 0x42ff, 0xa8ce, 0x4313, 0x41f5, 0x4691, 0xa85a, 0xac75, 0xbfe0, 0x0459, 0xaabc, 0x42c0, 0xbf1a, 0x41e2, 0xb03b, 0x1b5e, 0x2210, 0x4263, 0xbfc0, 0x2f43, 0xa745, 0x1bcc, 0x403c, 0x443a, 0x44bd, 0xbfc0, 0xb25c, 0x400f, 0x42f4, 0xbfe4, 0x4446, 0xb23d, 0x4166, 0xba55, 0xb2b2, 0xb2aa, 0x19ad, 0xa192, 0xb0d9, 0xa1b6, 0x42d5, 0x429e, 0x402b, 0xbf37, 0xb2c3, 0x2823, 0x425c, 0x0c29, 0x42a0, 0x0885, 0x4077, 0x43db, 0xb2c9, 0x2cd3, 0x4249, 0xbf81, 0xb212, 0x400a, 0x133a, 0x03ef, 0x30dc, 0x2ebf, 0x1b95, 0x1abb, 0xbfb0, 0xb2e7, 0x41e0, 0x401b, 0x400f, 0x3cd1, 0x3f45, 0xbf5a, 0x43be, 0x44c1, 0x4662, 0xbfc0, 0x4019, 0x4357, 0xb298, 0x4278, 0x4338, 0x2492, 0x4279, 0xb0a0, 0x436e, 0x4378, 0x4272, 0x19be, 0x4243, 0x43dc, 0x41aa, 0x1938, 0xbf22, 0x4202, 0xb0b5, 0xbac3, 0x1989, 0xa4aa, 0xb21e, 0x43ad, 0x4495, 0xbfde, 0x2891, 0xb287, 0xb2bf, 0x182f, 0x436c, 0x0e88, 0xb2fd, 0x4207, 0xba23, 0xb0ac, 0xbf39, 0x3a14, 0xb2a6, 0x42f3, 0x13e5, 0xb097, 0x4008, 0x4286, 0x421c, 0xbf4a, 0x1dc9, 0x1a6a, 0x41bf, 0xba55, 0x2f4b, 0xaa67, 0x4175, 0x4003, 0x4189, 0xb279, 0xbf3e, 0x4102, 0x45dd, 0xbfd0, 0xbad2, 0x0cb3, 0xb06f, 0x43f0, 0x44ed, 0x1af4, 0x40e7, 0xbfa1, 0x1ea1, 0x461d, 0x42b7, 0xb0a6, 0x44c0, 0xa90f, 0x280c, 0x42ff, 0x39ac, 0x4334, 0x4395, 0xba55, 0x4119, 0x1c16, 0xbfa0, 0x40d1, 0xb24e, 0x41f0, 0x3292, 0xba52, 0x4016, 0x43ce, 0x0873, 0x3836, 0xb074, 0xb08c, 0xbacc, 0xbf8b, 0x4301, 0xb274, 0xbfa0, 0x41f3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5a5bb2e2, 0x70b44505, 0xf439f718, 0x7601686e, 0x506efe14, 0xe57877bf, 0xae23b7eb, 0x04b61cf8, 0x3e1feb19, 0x3ee9a3be, 0x31b2d471, 0x4f5928f4, 0x9e0f7156, 0x0e22cfed, 0x00000000, 0xc00001f0 }, FinalRegs = new uint[] { 0xffffffc9, 0xffffffc9, 0x00009100, 0x7fffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x7c404452, 0x8d794b1c, 0x4f5928f4, 0x8d794b1d, 0xfb201937, 0xf1ddb58c, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbac1, 0x4158, 0x40ea, 0x242c, 0x4288, 0xb29e, 0x418a, 0xbf23, 0xb2fa, 0x4148, 0x3f4d, 0xba28, 0x43a5, 0xb0fb, 0x1ba0, 0x4000, 0x2c42, 0xbac7, 0x40bf, 0xa072, 0x43fc, 0x1202, 0x40a6, 0xba20, 0xbf96, 0xb0ff, 0x0d2b, 0xb221, 0xb274, 0xb237, 0x4048, 0x43ef, 0x45a1, 0x1d08, 0x1c73, 0xbf35, 0x44c0, 0xbacb, 0xb0bf, 0xa4a5, 0x06ac, 0xa673, 0x4076, 0x42de, 0x411c, 0x4316, 0xba5a, 0xb222, 0xbf60, 0x1f22, 0x4047, 0x4599, 0x43eb, 0x3d4e, 0x4684, 0x4159, 0x42d6, 0x4397, 0x4019, 0x37f0, 0x0c64, 0xba56, 0x404f, 0x419b, 0xbfab, 0x0a60, 0x1678, 0xb2e1, 0x4087, 0x4113, 0x1c75, 0x43e2, 0x4580, 0xb2f4, 0x1a65, 0x41bf, 0xa5c4, 0xbf00, 0x023e, 0xba5b, 0x40ec, 0x423f, 0x4265, 0x4159, 0xba17, 0x43d5, 0xbfa0, 0x419d, 0x0690, 0x4613, 0x4026, 0x4237, 0x419a, 0xbfbd, 0xaf55, 0xb248, 0x1543, 0x43c3, 0x1eac, 0x4208, 0xb224, 0xbacd, 0x4290, 0x40dd, 0xb2bb, 0x4551, 0x40a6, 0x4595, 0x4118, 0x1940, 0xbf41, 0x3427, 0x18e1, 0x405d, 0x4086, 0x42c3, 0xaaaf, 0x39b0, 0x40a6, 0x2091, 0xbf1f, 0xb29c, 0x0054, 0x3139, 0x40a2, 0x3fcc, 0x2890, 0x4073, 0x4378, 0x1fe5, 0x00f7, 0x1a7b, 0xbad5, 0x401d, 0x4229, 0x424e, 0x1e92, 0xa256, 0x40ed, 0xa108, 0xbfa8, 0x41b1, 0x40ad, 0x1f7c, 0xb2ce, 0xb27e, 0xbf7b, 0x4066, 0x305d, 0xba36, 0xbafb, 0x2265, 0xba43, 0xb213, 0x43fe, 0x40ba, 0xa9a2, 0xbfc5, 0x1c2e, 0x4254, 0xa6ce, 0x40c7, 0xb0d1, 0x416d, 0x425f, 0xa0b5, 0x4365, 0x1da1, 0xb204, 0x3eca, 0x430c, 0x4646, 0x43f9, 0x42c3, 0xbf68, 0x4058, 0x416d, 0x1e07, 0x1e89, 0x40ec, 0x4041, 0x40b4, 0xb01d, 0xb234, 0x4023, 0xb019, 0x4679, 0xb0ff, 0x4624, 0xad8f, 0x188f, 0x412e, 0xaf18, 0x1adc, 0xbf11, 0x1f44, 0x436d, 0x436b, 0xa7ad, 0x0ba2, 0xb28b, 0x4273, 0xb22d, 0x4097, 0x4105, 0x426c, 0xb25a, 0xbf90, 0x428c, 0x138c, 0x44e1, 0x418a, 0x1aff, 0x219f, 0xb269, 0xbf15, 0x0b01, 0x4201, 0x4008, 0xbafe, 0x3265, 0xb0b0, 0x4359, 0x45ba, 0x425f, 0xb214, 0x42e9, 0x4456, 0x404d, 0xbfd7, 0x40a2, 0xb0f8, 0x4032, 0x18b3, 0x3009, 0xb2ab, 0x41b7, 0x44c9, 0x4419, 0xb096, 0x429f, 0x433d, 0x38f2, 0xbfe0, 0x4083, 0x414b, 0x4055, 0xba74, 0xbf75, 0x4072, 0x41fb, 0xb04a, 0x43e3, 0x43f7, 0xad8d, 0xb251, 0xba67, 0x4300, 0x4259, 0x4206, 0xb0a2, 0xb242, 0x4699, 0x1c9e, 0xbf73, 0x434f, 0x0744, 0xa414, 0x4099, 0x4661, 0xba62, 0x2e9f, 0x431a, 0x05c6, 0x465d, 0xb2ee, 0xbae1, 0x093a, 0xbf90, 0x4399, 0xb21d, 0x40dd, 0xb2b5, 0x0057, 0xbf60, 0x1f1c, 0xbf18, 0xb28b, 0x41f3, 0xbadb, 0xbf9c, 0xb261, 0x129d, 0x42fb, 0x42b7, 0x4306, 0xb06f, 0xb2d6, 0x41b6, 0xa264, 0x4001, 0x2ef9, 0x2dc8, 0x40c6, 0x40f3, 0x456f, 0x414e, 0x26ae, 0x2625, 0x41bf, 0x40b0, 0x1a12, 0x31a8, 0xb000, 0x1a30, 0xbf31, 0x1198, 0x44fd, 0x043c, 0x4301, 0x1a9d, 0x10cc, 0xbfac, 0x401e, 0x135e, 0xb2df, 0x4356, 0xae2b, 0x40e0, 0x43f7, 0x322f, 0xba6b, 0xbfde, 0x1e91, 0x21a5, 0x413f, 0x4081, 0x134a, 0x43d9, 0x417b, 0xb089, 0xadcd, 0xb220, 0x43ab, 0x1aed, 0xa819, 0x1ee7, 0xb0de, 0xb2dd, 0x2249, 0x4122, 0x29d1, 0xa87d, 0xb0b2, 0x128c, 0x33a1, 0x0d9c, 0xbf16, 0x15ea, 0x4607, 0xbf70, 0x2f6c, 0x1900, 0x4060, 0x1abd, 0xb2f2, 0xba65, 0x4166, 0x3593, 0xbfcf, 0x1a90, 0x4308, 0x4447, 0xba58, 0x1bf2, 0x419c, 0x181c, 0x2f88, 0x4141, 0x418a, 0x4067, 0x12a7, 0xb0c5, 0x4197, 0x0a82, 0x0774, 0x34e5, 0x4456, 0x4109, 0x4209, 0x445d, 0x4029, 0x0d4e, 0x424b, 0x42ee, 0x41a0, 0x4673, 0xbf70, 0xbf3c, 0x18f1, 0x3e63, 0xa7f4, 0x097a, 0x4586, 0x4163, 0x40f1, 0xba57, 0x284b, 0x420e, 0x41cd, 0x4092, 0xba0d, 0x4230, 0x43ce, 0x445c, 0x44cd, 0xb23f, 0x40a6, 0x41ce, 0x1d86, 0xb235, 0xb251, 0xb289, 0x43ba, 0xbfc5, 0x408f, 0xba1b, 0xb0fe, 0x41c0, 0x4107, 0x36ad, 0x3817, 0x2df3, 0xab39, 0x223f, 0x4253, 0xbf67, 0x4148, 0x40d9, 0x42f0, 0xb0e5, 0x4314, 0x404d, 0xb2c4, 0x2d43, 0x41eb, 0x2b28, 0x4630, 0x21d0, 0x1f98, 0xb28a, 0x43c0, 0xbf80, 0x1d10, 0xacb5, 0x45db, 0xbf59, 0x1132, 0x1ef9, 0x429c, 0x1cce, 0x41af, 0xa433, 0x15e1, 0x42b5, 0x419c, 0x1b1f, 0xb2e5, 0x4012, 0xb28c, 0x1d9b, 0xa11e, 0x1a24, 0x423a, 0x40e8, 0xb257, 0x41f4, 0x4452, 0x24ad, 0xbf9a, 0x1809, 0xb2dd, 0xb2be, 0x4104, 0x19b0, 0x43c4, 0xb037, 0x43ea, 0xae35, 0x04ab, 0xb2f8, 0x2c34, 0x2827, 0x40bd, 0x466b, 0xbf59, 0xb029, 0xb219, 0x42fd, 0x434f, 0x1cfa, 0xa456, 0x44c4, 0x415b, 0x1e44, 0xbfbe, 0x4648, 0x4263, 0x41bc, 0x186f, 0xba0f, 0x1cda, 0x1a0c, 0xb0b6, 0x1c21, 0x143a, 0x4284, 0xbae2, 0x4083, 0x1fbb, 0x436d, 0x4286, 0x0262, 0x1fcd, 0x4368, 0x40b5, 0x4339, 0x360f, 0x420a, 0x40e0, 0xb29e, 0xbf61, 0x0a3d, 0x40d3, 0x433b, 0xb20b, 0x4588, 0x401e, 0xb2cf, 0x0ab7, 0x46ab, 0xbfcd, 0x42b0, 0x4199, 0x1deb, 0x16b0, 0x43bb, 0xa042, 0x0943, 0x42d8, 0x4298, 0x412d, 0xbf3c, 0xb01b, 0x43b0, 0xbafe, 0xb02c, 0x4086, 0xbfd1, 0x3145, 0x40aa, 0x1e27, 0xbaff, 0x2464, 0xbf60, 0x435d, 0x43cf, 0x41d7, 0x4408, 0xba3f, 0x40bb, 0x1900, 0x2295, 0xb2d8, 0x2325, 0x46d1, 0xb0f8, 0x438a, 0x43ea, 0x460f, 0x4205, 0x41a7, 0x0b21, 0x1c41, 0xbaeb, 0x3f2f, 0x4340, 0xbf08, 0x1d1a, 0x2ee5, 0xb0b8, 0x432e, 0xbf0f, 0x1edc, 0x4365, 0x1bda, 0xba31, 0x186c, 0xbf95, 0x4027, 0x4268, 0x4136, 0x41a1, 0x407d, 0x1d56, 0x420d, 0xbf11, 0x4246, 0x1bd6, 0x41eb, 0x41b7, 0xb073, 0x40bb, 0x1804, 0x4146, 0xb234, 0x1ca5, 0x40e5, 0xba68, 0xaa9b, 0x2f01, 0x4102, 0x40d0, 0x1c95, 0xb2be, 0x402f, 0xb264, 0xb29b, 0x46fb, 0xb288, 0x418b, 0xbf38, 0xa198, 0x0caa, 0x14ba, 0xadde, 0x033f, 0x40ae, 0x1b2b, 0xb0a9, 0x067e, 0x1fc9, 0x35ab, 0xbad2, 0xac87, 0x1001, 0xb2d3, 0x01a8, 0x407f, 0xbac0, 0xbf23, 0xb2d0, 0x45a6, 0x430b, 0x44e0, 0x4223, 0x42dd, 0xb2bf, 0xb209, 0x462b, 0xb2d5, 0x18a0, 0x4280, 0x1d75, 0x4261, 0xb2fc, 0xb2f7, 0x37a6, 0xba4c, 0x42ee, 0xbaf7, 0x2e93, 0xbf17, 0x4295, 0x43fc, 0xb06f, 0xb20b, 0x4065, 0x4068, 0xbfde, 0x40b8, 0x17c8, 0x43bd, 0x40c4, 0x4159, 0x118d, 0x412a, 0xaebf, 0xbf04, 0x2ba2, 0xb2d5, 0x0b54, 0x0295, 0x438a, 0xbaf1, 0x32b1, 0x2c88, 0x414a, 0x19c9, 0x4026, 0x434d, 0x2396, 0x1d91, 0xb24d, 0xad96, 0x418b, 0x37e2, 0x436c, 0x42ee, 0xbf8b, 0x4166, 0x433a, 0x40b5, 0xbae9, 0x41dd, 0x4303, 0x431a, 0x41f6, 0xbaef, 0x411a, 0xb2ab, 0x113c, 0x1a84, 0xb03e, 0x063d, 0xbf1e, 0xb22a, 0x3926, 0x40e6, 0xba22, 0x2aca, 0x2561, 0xba47, 0xba3b, 0x0a0a, 0xb05d, 0xb2fc, 0xbf95, 0x467e, 0x17a2, 0xba2d, 0xb031, 0x4227, 0x1c1d, 0x427c, 0x3211, 0xb086, 0x43af, 0x4041, 0xad6c, 0x1877, 0x202b, 0x405a, 0x446b, 0x34ef, 0x4084, 0xb252, 0x1ef7, 0x41c8, 0xbf6f, 0xb06f, 0x02fc, 0x409f, 0xba72, 0xb030, 0x4221, 0x4139, 0xbf87, 0x42d1, 0x19be, 0x42de, 0x46eb, 0xba3c, 0x4078, 0xbfb7, 0xba51, 0x43e8, 0x4194, 0x42c7, 0xbf04, 0x4085, 0x42de, 0x40aa, 0x303e, 0x4234, 0x1a9f, 0x1abc, 0x42bc, 0x3b66, 0x361d, 0x01fd, 0x41d2, 0x427e, 0x4332, 0xb21f, 0xb01b, 0xb2de, 0xba46, 0x40bb, 0x1f62, 0x419c, 0x43b1, 0xb2bf, 0xbf7a, 0x1f92, 0xa922, 0x018a, 0xbadf, 0xb229, 0xb02f, 0x195a, 0xbae1, 0x400f, 0x182c, 0xb252, 0x412a, 0x40d9, 0xbf7c, 0xbad9, 0x06da, 0x43e5, 0xb0ab, 0x431f, 0xba39, 0xb016, 0x1ac4, 0x406a, 0x414e, 0x42b8, 0x4237, 0xb28a, 0x44eb, 0x1aa1, 0x34f8, 0xba2a, 0x4568, 0xb210, 0x42b8, 0x1836, 0x434f, 0x4253, 0x21fd, 0x43ec, 0x4181, 0x4239, 0xbf79, 0x43eb, 0x4061, 0xb2c0, 0x29fd, 0x1ac6, 0x3e8c, 0xacfe, 0x43a7, 0xb257, 0xba3d, 0xb06b, 0x40ce, 0xbf61, 0x4205, 0x430d, 0x43ad, 0x02a9, 0x07c4, 0x41c3, 0x1c78, 0x4018, 0x4286, 0xbfd4, 0xbac1, 0x41e3, 0xba27, 0x1e65, 0xb20f, 0x4001, 0xbf68, 0x4642, 0x43af, 0x0ff7, 0x43c4, 0x0a6f, 0xbf19, 0x405d, 0x401c, 0xa368, 0x1c07, 0x06eb, 0x42d9, 0x1863, 0xb0ae, 0x42a9, 0x41ac, 0xb0b6, 0xbfa0, 0xbf90, 0xb2db, 0x4075, 0x4284, 0x431f, 0x1f38, 0xbf2d, 0x4385, 0xb00f, 0x40af, 0x1b7a, 0xaaa3, 0xa6de, 0xba15, 0x4159, 0x41a1, 0x3aee, 0x4072, 0xb0bb, 0x40fb, 0x442b, 0x3328, 0x1e7d, 0x1d86, 0xb232, 0x1b14, 0xb00d, 0x41b9, 0x40fe, 0x1a56, 0x1c3d, 0x2f3c, 0x09ad, 0x4111, 0xbf71, 0x4630, 0x030d, 0x4117, 0x4034, 0x3845, 0x0b31, 0xbae2, 0x462f, 0x0e01, 0xb225, 0x2df4, 0x4651, 0xbfa0, 0x4381, 0x1bcc, 0x432d, 0x4265, 0x42ad, 0x4379, 0x410a, 0xbf04, 0xb28c, 0x42a8, 0x414c, 0x4065, 0x1586, 0x43aa, 0x336d, 0x4211, 0x1bee, 0x42a7, 0x4026, 0x263a, 0x1fa1, 0x4354, 0xbfa9, 0x1d2b, 0x4337, 0x432e, 0xb2d6, 0x18a9, 0xb0a0, 0xba29, 0x4131, 0x4348, 0x403f, 0x0ab3, 0x4214, 0x4175, 0x4259, 0x43d7, 0x33cf, 0xba42, 0x11bb, 0xbf3a, 0xba77, 0x4098, 0x4232, 0xb2a7, 0x4375, 0x0786, 0x1f14, 0xa4fd, 0x01a8, 0xa0ad, 0x411e, 0x2763, 0xba0a, 0xb05f, 0xbac6, 0x4123, 0xbf46, 0x41b5, 0xb20d, 0xac2e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x4ccb59f7, 0x5e2a5ea7, 0x0598ceb9, 0xfde9560f, 0xaeee5f97, 0x81c7018e, 0x8dfd005d, 0xf0809e08, 0xf2345361, 0x7be970a4, 0xd526e1ae, 0xd985c732, 0x2702a40c, 0xf395aaaa, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x00001a78, 0x00000000, 0x00000000, 0xffffffff, 0x00001bb4, 0x00000000, 0x0000781a, 0x00000063, 0xc8d14d87, 0xd526e1ae, 0xd526e1ae, 0xf296cb30, 0xe468a6c5, 0xf296b6a4, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x30ba, 0x3e17, 0xb2b6, 0x0e7a, 0x1ad3, 0x16d7, 0x3d09, 0xb2d4, 0x4149, 0x4388, 0xbf70, 0x403e, 0x4081, 0x2f93, 0xb263, 0x421d, 0x4136, 0x1fbc, 0xbadd, 0x42e0, 0x41df, 0x42f2, 0xb2ac, 0x4188, 0x1f29, 0xbf0c, 0x46e4, 0x0f06, 0x4220, 0xb090, 0xb2be, 0xa4e6, 0x4047, 0xbfe0, 0x465b, 0x3199, 0x4216, 0x1b5d, 0x0645, 0xb04b, 0x1ae2, 0xaec8, 0x1942, 0x4225, 0x4214, 0x3aa9, 0xba12, 0x41b2, 0x30a1, 0xb27b, 0xba72, 0xba69, 0x15c8, 0xbfdd, 0xb204, 0x427c, 0x42e0, 0xb260, 0x4169, 0xa447, 0xb204, 0x424e, 0x465d, 0x3fce, 0xabb3, 0x43f3, 0xba44, 0x418d, 0x42b9, 0xb045, 0x20a4, 0xb285, 0xb2c1, 0x424d, 0x0575, 0xb081, 0x0475, 0xb273, 0x43db, 0x4297, 0xbf16, 0x41d7, 0x1b1c, 0x1a44, 0x4220, 0x29a3, 0x4069, 0x4143, 0x464e, 0xb2e9, 0x4120, 0xb28b, 0xacb2, 0xac86, 0x2c6e, 0x43c5, 0x42d4, 0x42e7, 0xbf15, 0x1c4d, 0x41a9, 0x4300, 0x401c, 0x32f0, 0x431e, 0x4117, 0x43f3, 0xb041, 0x02d0, 0x443d, 0xbfc9, 0xb292, 0x436c, 0xb236, 0xb022, 0xb023, 0x430b, 0xbfc0, 0x4318, 0x456a, 0x2809, 0x4006, 0x432a, 0xba3e, 0x4396, 0x406b, 0xbf16, 0xba55, 0x4163, 0x4279, 0x035d, 0x4380, 0x4078, 0x1ccc, 0xba46, 0x4064, 0xb21a, 0x0727, 0x1a5e, 0xba7f, 0xa4ed, 0xbac7, 0x0244, 0xb26b, 0x23f1, 0x3321, 0xa490, 0x4284, 0xba64, 0xa24a, 0x4057, 0xbaff, 0x1877, 0x417c, 0x0d9c, 0xbf9c, 0x1c58, 0x42c3, 0x1c7f, 0xba5f, 0x0daa, 0x42d8, 0x41be, 0x40d2, 0x4298, 0x4118, 0x4085, 0x4353, 0xbf06, 0x402f, 0xb0fc, 0x45ab, 0x41da, 0x11d6, 0xbf04, 0xb025, 0x40a2, 0xb26c, 0x40ae, 0x40c5, 0x08ba, 0xb227, 0x4356, 0x423b, 0xb20c, 0x230b, 0x40e1, 0xb0fa, 0x4013, 0x14bd, 0x43de, 0x3daf, 0x0368, 0x407f, 0xb298, 0x41f8, 0x43de, 0x4048, 0x18a4, 0x08f7, 0x42b9, 0x41a8, 0xbf33, 0x404f, 0x4174, 0x4261, 0x43f1, 0x1609, 0x4281, 0x00ee, 0x4291, 0x43b1, 0x359a, 0xb0d0, 0xb09f, 0x40fe, 0xb2b5, 0x4466, 0xb050, 0xb25c, 0xba0b, 0xb03a, 0x04a6, 0xb247, 0x4390, 0xb291, 0x4198, 0xb02a, 0x421f, 0x40b3, 0xbf7d, 0x41f6, 0x23ee, 0x329a, 0x43d2, 0x421c, 0x4199, 0xb253, 0xb0c6, 0x3180, 0xb0b7, 0x43a0, 0xbfbf, 0x43e6, 0x1e76, 0x157f, 0x3290, 0x1952, 0x428a, 0x1b3f, 0x05ce, 0x0e2e, 0xb0bf, 0xb264, 0x41a3, 0x0a74, 0x4181, 0x4112, 0x3e9f, 0x42c4, 0x4214, 0x46b9, 0x1a96, 0xbf0f, 0xa787, 0x42fb, 0xb008, 0x40a0, 0x2262, 0xb261, 0x26a5, 0xa01b, 0x01fa, 0x40a4, 0xba0f, 0xa676, 0xb26b, 0xb262, 0xba1f, 0x4075, 0xbfc2, 0x415d, 0xb095, 0x1ead, 0x410a, 0x4328, 0x0c46, 0xb2e9, 0x430f, 0xb274, 0x415d, 0x420f, 0x197b, 0x46fb, 0x125d, 0x43ca, 0xa547, 0x4064, 0x425e, 0xbf2f, 0xb036, 0xb292, 0x40e9, 0x3d9e, 0xb227, 0xa088, 0xb0e0, 0xb21c, 0xafc4, 0xbace, 0x40a2, 0x100d, 0x405d, 0x135d, 0x4045, 0x054f, 0x430c, 0x415c, 0x3b73, 0x1c6b, 0x0905, 0x4213, 0x4471, 0xa947, 0xb269, 0xbf77, 0xba2b, 0x42c8, 0x119f, 0x1d18, 0x4031, 0x4657, 0x4240, 0x4147, 0x4191, 0x44ad, 0x410b, 0xbad7, 0x40d8, 0xa692, 0x1bd6, 0x4137, 0xb05c, 0x4204, 0xbf72, 0x22bb, 0xa447, 0x1ec3, 0xb07f, 0xbfd0, 0x438d, 0x4220, 0xbf4a, 0x40eb, 0x42f2, 0x2d2f, 0x40e1, 0xa238, 0x41fa, 0xb24e, 0xb2e5, 0xbfa0, 0x441c, 0xb056, 0x4365, 0x1cca, 0x1a9a, 0x4346, 0x2fbc, 0x4222, 0xb2c2, 0xbfda, 0x416c, 0xbfe0, 0xba4f, 0x40e8, 0x4386, 0xbfc0, 0x06e0, 0x4370, 0x419e, 0x3b0e, 0xae3e, 0x413b, 0xb0b1, 0xb02f, 0x1f7c, 0x377d, 0xbac4, 0x45d2, 0xb25d, 0x0d8c, 0x46c4, 0x4304, 0xa6dd, 0x1a5d, 0x4050, 0x410c, 0x4269, 0xb284, 0xbf2d, 0x414f, 0x044d, 0xba0a, 0x19a1, 0x4107, 0x4273, 0xbafa, 0xb222, 0x42ba, 0x45c0, 0x408b, 0x4081, 0x4102, 0x070a, 0xb2eb, 0xb056, 0x385b, 0x1b53, 0x046b, 0xba7b, 0x41a3, 0xbaf9, 0x4553, 0x313a, 0xb028, 0xbf42, 0x418c, 0x05c5, 0x4161, 0xa924, 0x4026, 0xba41, 0x40a4, 0x4285, 0x3ff7, 0x1c6a, 0xb207, 0x42ae, 0x4371, 0x116e, 0xbf35, 0x1d23, 0x429f, 0x1a06, 0x44fb, 0xb2ac, 0x40d4, 0x46e5, 0x403e, 0x4387, 0xb099, 0x4109, 0x40d0, 0x2f0a, 0x38cd, 0x02a6, 0xbf38, 0x46ac, 0x30d9, 0x087d, 0xb27b, 0x41ff, 0x1537, 0x4101, 0xba15, 0xb294, 0x41d6, 0x2af8, 0xba0e, 0x41d9, 0x3f8b, 0xba63, 0xb238, 0x4003, 0x1c25, 0x41c7, 0x43a7, 0x0ad6, 0xbf2b, 0x1ee5, 0xb2c4, 0xa57f, 0xb06e, 0x1e2e, 0x4039, 0x409b, 0x4102, 0x416d, 0xa855, 0xb235, 0x3847, 0xa008, 0x4411, 0xb26a, 0xa9fe, 0xbfd7, 0x41d7, 0xbf70, 0xbae6, 0xba06, 0x35df, 0xb219, 0x2f49, 0xb0cd, 0x35d3, 0xb0ca, 0x4383, 0xb233, 0x4381, 0x3a8f, 0x460c, 0x1df7, 0x0edb, 0x43d4, 0x1a05, 0x1318, 0xbac2, 0xb2b9, 0xb2d1, 0x4230, 0xbf3e, 0x422d, 0xb213, 0xb061, 0xa09a, 0x4057, 0x4077, 0x426c, 0x0376, 0x42a5, 0x19c9, 0xbf1d, 0x422d, 0xb2f2, 0xb04b, 0x41a5, 0x410e, 0xb2ac, 0x0819, 0x415b, 0x3316, 0x4314, 0x1fd3, 0xb075, 0xbfb3, 0xb08b, 0xa994, 0xbf80, 0xbae1, 0x40b4, 0xbfb8, 0x409d, 0x01ee, 0x43ec, 0x09d0, 0x2384, 0x4183, 0x416b, 0x411d, 0xbf78, 0xba54, 0xba72, 0x4397, 0x1eb9, 0x1efa, 0xb2ec, 0x183e, 0x18e1, 0x0a6c, 0x42c6, 0x232a, 0xba57, 0xbff0, 0x41f5, 0x42c9, 0x3b22, 0x421f, 0xb294, 0xb277, 0xa937, 0xbf8e, 0x462f, 0x20b5, 0x2e40, 0x4306, 0x43f1, 0x036f, 0xa324, 0xbf23, 0x3670, 0xba5c, 0x4050, 0x0906, 0x4389, 0x4684, 0x40b0, 0x3eb0, 0x1f2e, 0x1aba, 0x1963, 0x4145, 0x122d, 0xb2c1, 0x4555, 0x403d, 0xb28b, 0x4252, 0x1a41, 0x4077, 0x434f, 0x4459, 0x4360, 0xbf22, 0x1b7f, 0xb24e, 0x1433, 0x40b1, 0x41a3, 0xb2cd, 0xb021, 0x4251, 0x42e3, 0xb0b5, 0xb2d0, 0xbf55, 0xa034, 0x41e3, 0x05fb, 0x4067, 0x41c2, 0x4059, 0x40ea, 0x3288, 0x4328, 0x4268, 0xbf2d, 0xb05b, 0xb234, 0x4220, 0xba68, 0xbfc0, 0x42e8, 0x406d, 0x4371, 0x12db, 0x4157, 0x448c, 0x162f, 0x3d79, 0x4190, 0x4050, 0x0c23, 0x2cef, 0x415b, 0xbaf1, 0x4005, 0xbf57, 0x404d, 0x214b, 0xa974, 0x404c, 0x40cb, 0x03ca, 0x1675, 0x36c7, 0xbfda, 0xaecc, 0x4576, 0x2c9c, 0xa7ec, 0x423e, 0x1d8c, 0x4172, 0x437a, 0x0b36, 0xa6b8, 0x4160, 0x4648, 0x45c4, 0x1d48, 0x1cfb, 0xa650, 0x4265, 0xb2b3, 0x414c, 0x443a, 0x4378, 0xba29, 0x464e, 0x1d73, 0xbfa7, 0x121c, 0x2eb2, 0xba30, 0xb0e6, 0x40d8, 0x4108, 0xbf70, 0xbad4, 0xb27e, 0xbaca, 0x4694, 0x320f, 0x1605, 0xba48, 0xafdb, 0x41e1, 0x4095, 0x3649, 0x00ef, 0xbf25, 0x4233, 0x4226, 0x4390, 0x46b8, 0x4231, 0x43d0, 0x45d2, 0x42d7, 0x3871, 0x1a1d, 0x1d3e, 0xbad1, 0x418b, 0xb072, 0x1843, 0xb215, 0x1ae0, 0x0898, 0x4106, 0x41a2, 0xba0a, 0xbf43, 0x41c9, 0x4022, 0xa59a, 0xb035, 0x4549, 0xbfa7, 0x441f, 0x4167, 0x3b3a, 0x1bd7, 0xbf75, 0x3d63, 0x1bba, 0xb2ae, 0x401b, 0x0e2e, 0x42cd, 0x1ccd, 0x4028, 0xba22, 0x4343, 0x3458, 0x2dd4, 0xa814, 0x1985, 0x469a, 0x43c1, 0x4562, 0x444c, 0x19b5, 0x42ab, 0xbf7a, 0xb2a1, 0x4394, 0x4008, 0x0f19, 0xbae8, 0xb266, 0x4141, 0xae9b, 0x4349, 0x45ad, 0x1af6, 0x40c0, 0x40a2, 0xba1a, 0x439b, 0x1fb8, 0x11da, 0xa672, 0xb2d0, 0x4223, 0x449d, 0x431d, 0x42e8, 0x45ec, 0x18d7, 0xbf5e, 0x41df, 0xba7f, 0x423b, 0xa7ae, 0x28f4, 0x414b, 0x43fa, 0xb216, 0x190b, 0xa562, 0x4391, 0xb252, 0x03f3, 0xbac0, 0x40fe, 0x2da8, 0x4449, 0x0adc, 0x4103, 0xb0dd, 0x403f, 0x34fa, 0xbf00, 0x45a6, 0x4357, 0xbf8a, 0xb2a2, 0x4109, 0xadc3, 0x407b, 0x40ac, 0xb227, 0x41fa, 0xaccd, 0x46a8, 0x40fa, 0x1f95, 0xb069, 0x444a, 0x4037, 0xb230, 0x419b, 0x1426, 0x424d, 0xb05f, 0x107b, 0x4055, 0x3be8, 0x0e2b, 0xb0f2, 0x1793, 0x46cb, 0x1fe7, 0xbfc6, 0x4300, 0x1e82, 0x43cc, 0x34b4, 0x173b, 0xb290, 0x36a7, 0x4348, 0x0f9f, 0xba7e, 0x0bef, 0xb22d, 0x42bf, 0x4541, 0xbf70, 0x4193, 0xbff0, 0xb0f2, 0x4420, 0xb01f, 0x41b7, 0x4251, 0x4301, 0xbf0e, 0xbad0, 0xbafe, 0x1f39, 0x2717, 0x4651, 0x1858, 0xb0e6, 0xbf88, 0x0dc3, 0x162d, 0x1947, 0xb24e, 0x24fa, 0xba59, 0x18a4, 0x42f6, 0x4018, 0x42a2, 0x407c, 0x42c6, 0x30d6, 0x4299, 0xb081, 0x4288, 0x41f8, 0x01b4, 0xb00a, 0xb26d, 0x436c, 0xbf0c, 0xb27d, 0xb2a6, 0x45ae, 0x04d9, 0xa8af, 0xb050, 0xbf61, 0x3332, 0x27cd, 0x0c39, 0xb28b, 0xb0e7, 0x2bfa, 0x4231, 0x43cc, 0x4387, 0x1d3e, 0x2fcc, 0x0863, 0x43eb, 0xba00, 0x41bd, 0x4139, 0xbf80, 0x1a3d, 0xba4b, 0x1a7f, 0x41d5, 0x4695, 0x41eb, 0x4003, 0x4457, 0x4312, 0x3a69, 0xb253, 0xbf68, 0x4143, 0xba2f, 0xbaf5, 0xb2e5, 0xbf00, 0x0f33, 0x418a, 0xb254, 0xbf3a, 0xba72, 0x404a, 0xb297, 0xbf46, 0x4645, 0x41e7, 0x4320, 0x1a06, 0x44e0, 0x3e22, 0x41d4, 0xbaf8, 0x4226, 0x41a5, 0x4291, 0xbae8, 0xbaf5, 0x4199, 0x45d1, 0xbf0e, 0x1f1d, 0x3bb4, 0xba40, 0x0793, 0x4645, 0x0312, 0xb262, 0x41b9, 0xb20f, 0x42e3, 0x4018, 0x4570, 0x10d0, 0x038a, 0x434f, 0xb2d2, 0x4430, 0xa2b1, 0x042c, 0x194b, 0x45db, 0x43b7, 0xbf5e, 0x1b6d, 0xb282, 0x40a5, 0x4076, 0x2065, 0x1c00, 0x462d, 0xaded, 0x3907, 0x1fe4, 0x429a, 0x40bd, 0xba32, 0x43b0, 0x1b1d, 0x43ef, 0x41c0, 0x427a, 0xba34, 0xba7b, 0x2307, 0x008d, 0x46c3, 0x4373, 0x40f3, 0xbfb9, 0x4169, 0x4135, 0x413f, 0x2474, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x7602de1d, 0x625d8c21, 0x6c16bc87, 0xecb33bf3, 0xf2e0d596, 0x9ed96ef4, 0xc82738a1, 0xf563157e, 0x31d024d5, 0x481b545e, 0xbb853a8f, 0x828b1a9a, 0x77119f7e, 0x41a530b7, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x28000003, 0xf00ebff8, 0xd843d7d2, 0x00000000, 0x00000074, 0xc03affe0, 0x00000000, 0x00000000, 0x000017cb, 0xffffffae, 0x001b0000, 0x000017cb, 0xffffffff, 0xfffffffe, 0x00000000, 0x600001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x183a, 0xb2ba, 0xbff0, 0x4236, 0x4329, 0x416b, 0xb0ab, 0xbfc0, 0x461c, 0x1cc6, 0x438a, 0x1dfb, 0x43ef, 0x1b4e, 0xbaee, 0x36a4, 0xb2e2, 0x4169, 0xb048, 0x1d98, 0xa51c, 0x40cc, 0x4332, 0x42e2, 0xbf01, 0xa8ff, 0xb2c5, 0x4322, 0xba13, 0xb017, 0x278e, 0xba57, 0x43ca, 0x4097, 0xb239, 0x41e7, 0x43df, 0x1fe8, 0x4152, 0x34e7, 0xba3e, 0xb211, 0x41ce, 0xb229, 0x4194, 0xbfc8, 0x1c04, 0x403a, 0x43d2, 0xbf41, 0x434d, 0xba36, 0xba4f, 0x40f1, 0x12a2, 0xb2a6, 0x0ba3, 0x1430, 0xb2cf, 0xaf47, 0x4380, 0x215d, 0x18bc, 0xb0ff, 0x4088, 0x41b3, 0x4131, 0xb0ae, 0x1cf1, 0xbfdd, 0x42f0, 0xb2a4, 0x4245, 0x43dc, 0x19b7, 0x4322, 0xba58, 0x1c79, 0x4387, 0x4596, 0x1ece, 0x41fc, 0xbf01, 0x092d, 0xb016, 0x40d1, 0xbad3, 0xb2bf, 0x40a6, 0x4247, 0x402f, 0xa9b7, 0xb244, 0x19fa, 0x4244, 0x1b06, 0x18ef, 0x4181, 0x144c, 0x18b1, 0x4197, 0x4123, 0x3a3e, 0xb204, 0xbfbe, 0xbaea, 0x1c38, 0x436a, 0x2039, 0x1ccf, 0xaff3, 0x1847, 0xaecc, 0xb292, 0xb20f, 0x1f5a, 0xb2f2, 0x4183, 0x4004, 0x43b8, 0xab48, 0x43f9, 0x4234, 0x0ca8, 0x3607, 0x4118, 0x4206, 0xbfbe, 0x4073, 0x4688, 0x391a, 0x40b5, 0xba2f, 0xbf00, 0x3d16, 0x1b41, 0x421c, 0x434a, 0x413c, 0x405c, 0x41d8, 0x4375, 0x415c, 0x101b, 0x4540, 0x42ce, 0xb0aa, 0xbf9e, 0x42d0, 0x4215, 0x2cc7, 0x43db, 0xb23e, 0x2a9f, 0x461e, 0xbacf, 0x4344, 0xb2dd, 0x41d1, 0x4233, 0x2398, 0xa583, 0xb09d, 0xa90c, 0x3974, 0x407f, 0x41fe, 0xb03d, 0xb08a, 0x4283, 0x429a, 0x1730, 0x2b83, 0x414d, 0x3df6, 0x41ed, 0xbf0e, 0x19fd, 0x178e, 0x4158, 0xba46, 0x418b, 0x1030, 0xb033, 0x405b, 0x1e2f, 0x4063, 0xbfca, 0xbaf3, 0xb21d, 0x43dc, 0x412f, 0x42f0, 0xba72, 0xb040, 0x426e, 0x3339, 0xba6f, 0x10b4, 0x466c, 0x426b, 0xb276, 0x26bb, 0x42ea, 0x1338, 0x41f2, 0x4252, 0x41a3, 0x3467, 0x445c, 0x1034, 0x4577, 0x4277, 0x446c, 0x1933, 0x4032, 0xbfc6, 0x469c, 0xba1b, 0x2658, 0x4049, 0xb24d, 0x43ae, 0x42c8, 0x4316, 0x40cc, 0xb22b, 0xbf60, 0x3670, 0xba72, 0xb0e2, 0x40b8, 0xba6b, 0xb214, 0xbfd1, 0x428b, 0x1876, 0x1f3a, 0xb06f, 0x435e, 0x447a, 0x427d, 0xba75, 0xa04f, 0xaaee, 0x4222, 0x194f, 0x2702, 0xb05d, 0x015b, 0x4129, 0x425b, 0x4275, 0xaced, 0xb228, 0x04a5, 0x0d2c, 0x4140, 0xa285, 0xbfb3, 0x4208, 0x42c9, 0x380a, 0xbad3, 0x41c1, 0x4290, 0x4294, 0xba2b, 0xba5a, 0xbaed, 0x1fef, 0x4548, 0xb209, 0x43fd, 0x4233, 0xb0dd, 0xbff0, 0x418f, 0x4132, 0x4081, 0xae38, 0xb0e6, 0x4343, 0x427b, 0x1b87, 0xbf3c, 0xb051, 0x296c, 0xba3f, 0xa2de, 0xbf89, 0x4031, 0x1e9c, 0x429e, 0xb085, 0xbf90, 0x11c4, 0x40dc, 0x1dc6, 0x1d74, 0x4331, 0x4014, 0xb29f, 0x3445, 0x14a4, 0x4216, 0xbf51, 0x40f2, 0x4303, 0x4592, 0x42e2, 0x2058, 0xb296, 0xbfbc, 0xbfa0, 0x42d4, 0xbac6, 0x0f05, 0x4584, 0xba26, 0x4059, 0x26b3, 0x419d, 0xb2ac, 0x4261, 0xb200, 0x4201, 0x41cf, 0x4148, 0x4101, 0x43b7, 0xbf6f, 0x1c26, 0xb2f8, 0x426c, 0x1cce, 0x4438, 0xbaf2, 0x193a, 0x44d3, 0x1bf6, 0xba76, 0x39d2, 0x1223, 0x4243, 0xae1b, 0x405d, 0x40a8, 0x187c, 0x420f, 0xb226, 0xb050, 0x41ea, 0x418d, 0x42c6, 0xbf54, 0xa0f9, 0x38d5, 0x185c, 0x432a, 0xbfb4, 0xb2c7, 0x11a8, 0xb277, 0x4201, 0x41ab, 0xbf27, 0xb2ac, 0x1ab3, 0x3b89, 0x40f5, 0x2c46, 0x42ea, 0x43f4, 0x16c3, 0x4264, 0x3e2e, 0x4258, 0x4111, 0x40af, 0x01fe, 0x40b5, 0x0924, 0x1cb3, 0xba0f, 0xa039, 0x32d7, 0xbf80, 0x0949, 0x1bbe, 0xba79, 0xba3e, 0x41da, 0x4395, 0x4408, 0x18e5, 0xbf9e, 0x1ef5, 0x2b17, 0xa1b6, 0x1cb7, 0x42bb, 0x3992, 0x4109, 0x419c, 0x4445, 0x41d9, 0xbafc, 0x412d, 0x01bc, 0x4433, 0x434c, 0xba5a, 0xb207, 0xa3d7, 0xb090, 0x43bb, 0x42b1, 0xbfd7, 0x42e9, 0x4025, 0x331b, 0xad80, 0x41be, 0x430b, 0xb0c8, 0xbacd, 0x3b83, 0x1eca, 0xb073, 0x417d, 0x421a, 0x2cbc, 0x460d, 0xb224, 0x0041, 0xbfd0, 0xba2c, 0xb0d7, 0x42aa, 0x4360, 0xb239, 0xbf13, 0x4232, 0x43dc, 0x1d4d, 0x4172, 0x432f, 0xb217, 0x4014, 0xaf57, 0xb269, 0x4214, 0x401f, 0x4615, 0xba6d, 0x45f5, 0x3366, 0xba10, 0x41a7, 0x4320, 0x433c, 0x41aa, 0x4447, 0x43c4, 0x06b7, 0xbf13, 0x421a, 0x431e, 0x4337, 0xba43, 0x403e, 0xb281, 0x0bbe, 0x41ee, 0xa920, 0x4234, 0xb2f2, 0x43f3, 0x35f2, 0x41aa, 0xb247, 0x378e, 0xb214, 0x3c80, 0xba55, 0xb2fc, 0xb2ea, 0x402b, 0xb2bd, 0x4004, 0x4434, 0x443b, 0x0d49, 0xbfc5, 0x2a68, 0x4333, 0x4616, 0x421e, 0xb2b9, 0x4359, 0x43f8, 0x421a, 0x41bd, 0x16fe, 0xa8e2, 0xb25a, 0x435a, 0xb0d4, 0x416b, 0x1ea0, 0x4035, 0xb0ff, 0x4280, 0x31ab, 0xbf0c, 0x4043, 0x1d5c, 0xb01e, 0x414d, 0x43d1, 0x4283, 0x4008, 0x35e1, 0xb2c5, 0x4080, 0x26fa, 0x35a4, 0x2fee, 0xae2d, 0xbf94, 0x332b, 0x1c4b, 0xb234, 0xba72, 0x1f99, 0x4375, 0xb09b, 0xbfb1, 0x23e8, 0x1a82, 0xbad3, 0x4229, 0x40fa, 0x4271, 0x4454, 0x3f9c, 0xbf7f, 0x43dd, 0x43cf, 0x4051, 0x3ddd, 0x201d, 0xb293, 0xb260, 0xba02, 0x0725, 0x434e, 0x46b5, 0x46e9, 0x1748, 0x428b, 0xbac6, 0xb0b7, 0x41c3, 0xbaf9, 0xbf96, 0xb26d, 0x40bf, 0x1c2a, 0x41ee, 0x3251, 0x402b, 0x436d, 0xb012, 0x28c6, 0x3e10, 0x4472, 0xb064, 0x4254, 0x4008, 0x4326, 0x4187, 0x04fd, 0xb0f8, 0x4656, 0x400f, 0x1277, 0x3d1b, 0xba3a, 0x42bf, 0x43f3, 0xbf69, 0x4153, 0x43ad, 0x4695, 0x0135, 0x436b, 0x4167, 0xba22, 0xa2a9, 0x42f8, 0xbf0e, 0x437a, 0x1d74, 0xb01f, 0x017c, 0x1991, 0x42bc, 0x1faa, 0x43d2, 0x401f, 0xbf35, 0x43fc, 0xa8a4, 0x1b70, 0xb016, 0x0929, 0x2e6b, 0x4325, 0x4558, 0xb05d, 0xbf2e, 0x414c, 0x42cd, 0x4391, 0x433b, 0x4301, 0xbaeb, 0x1b75, 0xbfbb, 0xae43, 0x44aa, 0x41b4, 0x42a7, 0x1b64, 0x18d4, 0x2cac, 0xb222, 0xb2c7, 0x42c9, 0xbf90, 0xa53a, 0xbad0, 0x41b7, 0xb099, 0x46e3, 0xba47, 0x438d, 0x4244, 0xb0b5, 0x20f6, 0x456b, 0x433d, 0x4648, 0xba3f, 0x3fc9, 0xbf31, 0x194d, 0xac62, 0x42bb, 0x4315, 0x40ce, 0xaedd, 0xad17, 0x4026, 0x1270, 0xb0b2, 0x17c4, 0x1fbc, 0x417c, 0x1a36, 0x3eb7, 0xb004, 0xb03e, 0x4163, 0xba06, 0x424b, 0x43b4, 0xa91d, 0x4127, 0xb016, 0x4041, 0xb2db, 0x1abd, 0x2772, 0xbf2b, 0x410c, 0x1bc0, 0xbae4, 0x407f, 0x412b, 0x1900, 0x410f, 0x4255, 0xba25, 0x464a, 0xbf02, 0xbad9, 0x418a, 0x431a, 0xbad0, 0xb0b3, 0x4379, 0x442d, 0x4104, 0x13b2, 0x1b63, 0xbfc0, 0x1a55, 0x4271, 0x1d04, 0xb009, 0x42bd, 0x405c, 0x4175, 0xb268, 0x437d, 0xbfa3, 0x3b47, 0xbaec, 0xbae7, 0x0eca, 0xb275, 0x42d0, 0xb24a, 0x4369, 0x404d, 0x086e, 0x409c, 0x418f, 0x45b8, 0x3e1e, 0xbf81, 0x1f68, 0x435e, 0xba1b, 0x43b7, 0x40b5, 0x4064, 0x14b1, 0x13ed, 0x1849, 0xbfc9, 0xb05d, 0x40c3, 0x3623, 0xb2a0, 0x184d, 0x411c, 0x417e, 0xbf7a, 0x46e8, 0x41dc, 0x4329, 0x1cd8, 0xa148, 0xb0f5, 0x2e2a, 0x414d, 0x19ce, 0x41ee, 0x44cd, 0xa6a0, 0x412f, 0x4374, 0x43b9, 0xb06e, 0x1b62, 0x3bb0, 0x4336, 0x2103, 0xbf1f, 0x4291, 0xbadc, 0x4282, 0x4122, 0xab33, 0x4368, 0xbfb0, 0xbaf9, 0xb25f, 0xb0b5, 0xba09, 0xb271, 0x23c2, 0x4565, 0xa12c, 0xb24f, 0xbf3b, 0x2a8a, 0xa3fe, 0x09ff, 0xb288, 0x42b8, 0x40c4, 0xbac9, 0xba34, 0x425a, 0xba04, 0x42be, 0x0208, 0xb003, 0x29cc, 0xb23d, 0xa865, 0x4086, 0x2faa, 0xb0ab, 0xb0a2, 0xbfbb, 0x1955, 0x43af, 0xb2ba, 0x4211, 0x2327, 0x1d02, 0x4347, 0xb28d, 0x4126, 0xba03, 0x42f0, 0x4386, 0x4322, 0xa906, 0x0326, 0xba39, 0xba27, 0x1cfb, 0x4270, 0x419b, 0x0e80, 0x4662, 0x4303, 0x2737, 0x427d, 0x1a84, 0xbf7d, 0x4603, 0x1c74, 0xb27a, 0x4170, 0x346b, 0xba00, 0x38f6, 0x2096, 0x41aa, 0x1c51, 0x2ce4, 0xb097, 0x401d, 0xbf97, 0x4132, 0x439f, 0xb2dc, 0x43fc, 0x41a6, 0x1dc0, 0x4377, 0xb2c3, 0x4326, 0x4240, 0x42bd, 0x185d, 0x1a08, 0xba26, 0x4360, 0x4307, 0x093f, 0x23f5, 0x402e, 0x4423, 0x36b8, 0xacdd, 0x16a5, 0x4210, 0x421d, 0x4317, 0x42cb, 0x281f, 0xbf00, 0xbf38, 0x429c, 0x43f5, 0x4342, 0x44e4, 0xb217, 0x1af3, 0x40b5, 0x423f, 0x4095, 0xb0f7, 0x4593, 0xbf82, 0xbae0, 0x4132, 0xae4e, 0x2512, 0x4088, 0x4355, 0x43b2, 0xb24d, 0x3d61, 0x41ed, 0x1134, 0xb252, 0xb26b, 0x2517, 0x431f, 0xaf24, 0x42da, 0xbf31, 0xa58f, 0x3660, 0x227e, 0x43ef, 0x42de, 0x4203, 0x40fc, 0x42b6, 0x4158, 0x37ff, 0x366d, 0x4002, 0x1805, 0x466d, 0xb230, 0x423a, 0xb21e, 0xba0f, 0x41b2, 0xbad7, 0xb25e, 0x1948, 0xb029, 0xbf4b, 0x423f, 0xb2c8, 0xb25f, 0xba2d, 0x46b8, 0xb220, 0xbf60, 0x421e, 0xb20f, 0xba65, 0x4475, 0xbae1, 0x3874, 0x4031, 0xbf84, 0xb2d8, 0xbfe0, 0x2aa6, 0xbfa0, 0x4287, 0x4372, 0xba1e, 0x4250, 0xba77, 0x4619, 0x4098, 0x408f, 0x432d, 0xba12, 0xbfa2, 0x2b04, 0xba75, 0xb04d, 0x4251, 0x40c4, 0x4034, 0xba30, 0xba2a, 0x4010, 0x4139, 0x43ca, 0xba41, 0x18d1, 0x43ed, 0xb2c1, 0x4167, 0x2288, 0xba05, 0xad26, 0x0d4d, 0x4376, 0xbf53, 0x4139, 0xb2e0, 0xba4f, 0x4254, 0xbf98, 0x4331, 0xbf16, 0xa567, 0x420c, 0x081b, 0x1c4e, 0x03c9, 0x4220, 0xb097, 0x0871, 0x43da, 0xb06c, 0x427e, 0xb006, 0x42e0, 0xbf70, 0x42db, 0x06dc, 0x420f, 0x40eb, 0x41a4, 0xbfbc, 0x42d6, 0xb24a, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd3cfc4df, 0x5f6feaeb, 0x1bacfb37, 0xe934848c, 0x07b4b459, 0x8beb3310, 0x3e769455, 0x1dc2cbe1, 0xaa106f96, 0xf071655c, 0xa0cd784c, 0xed40276a, 0xb836b593, 0x485f4321, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x54ca3e07, 0x419af099, 0x485f4374, 0x90be86e8, 0xa9947992, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4499, 0x4021, 0x1d2e, 0x1c30, 0xb2ac, 0x44b1, 0x4148, 0xba7c, 0x26b5, 0x42af, 0x4151, 0xa7ea, 0x1bc7, 0x10ed, 0x439b, 0xa19b, 0x4201, 0x3aff, 0x2dc8, 0xba14, 0xb244, 0xbf0c, 0xbff0, 0x45c9, 0x418e, 0xbfc0, 0x4300, 0x282d, 0x4432, 0x41fc, 0xa442, 0x400d, 0xbaff, 0x1ed1, 0x1faa, 0x1e48, 0x433d, 0xbf5b, 0x4661, 0xb2ef, 0x45b9, 0x420e, 0xb0f9, 0x24c6, 0xb2aa, 0x429a, 0x403c, 0xb24c, 0xb0a2, 0xb205, 0xabd2, 0x42a8, 0x4660, 0x4340, 0x44ac, 0x46d9, 0x407c, 0x1b8f, 0xbf18, 0x440e, 0x46e1, 0x4184, 0x4210, 0x421f, 0x1bb3, 0x437a, 0xb272, 0x4320, 0x4230, 0x15b4, 0x110f, 0x2278, 0x418f, 0x1069, 0x0cde, 0x3a07, 0x420d, 0xb2d3, 0x4073, 0x40ee, 0xb0db, 0x3342, 0x4073, 0xbf31, 0xbaee, 0x4359, 0x43c6, 0x42e6, 0x0503, 0xba7b, 0xa23c, 0x42c8, 0x1b6c, 0xbf98, 0x434f, 0x1caa, 0x43f3, 0x4143, 0x41ca, 0x19c6, 0xbff0, 0x105d, 0x42a1, 0x3045, 0x419e, 0xb2c4, 0x4395, 0x40df, 0xa984, 0x41a6, 0x18c1, 0x41cc, 0x43bc, 0x211d, 0x429f, 0x324a, 0x102b, 0x40f3, 0x404a, 0xbf17, 0x40d2, 0x0930, 0xafec, 0x25ca, 0x43f6, 0x1ddd, 0x418d, 0x41d7, 0x43de, 0x18e2, 0x43b2, 0x43a8, 0xa27b, 0xba5e, 0xba38, 0x42fa, 0x4176, 0x1e51, 0xb28a, 0xac01, 0x093b, 0x42a1, 0xbff0, 0xb0fe, 0x29c7, 0x0dae, 0x403b, 0x44c9, 0xbf4c, 0x3212, 0x0a25, 0x43a6, 0x14ea, 0xb095, 0x4293, 0x42bf, 0xba44, 0x2636, 0xbaf8, 0x446a, 0x1967, 0x428c, 0xb0e5, 0xba08, 0xb0d3, 0x4081, 0x4293, 0x4208, 0xbfb0, 0x038e, 0x3581, 0x4484, 0x40b4, 0xba43, 0x424e, 0x4000, 0xbf38, 0x4054, 0xb2ff, 0x44e5, 0x41d8, 0x4291, 0x45eb, 0x43c2, 0xba56, 0x1c4e, 0x44bc, 0x41d0, 0x3ee3, 0x00fb, 0x4038, 0xa75b, 0xbae2, 0xbf23, 0x44a4, 0x404f, 0xb277, 0xba23, 0x4178, 0x4620, 0x410e, 0x4028, 0x4040, 0xa3e9, 0x2281, 0x2238, 0xb211, 0xb285, 0x1adf, 0x4138, 0x240f, 0x4353, 0x428c, 0x4347, 0xbf24, 0x4360, 0x1ffc, 0xbf57, 0x412c, 0x40c9, 0x3611, 0x4062, 0xa2f2, 0x4332, 0xb079, 0x42e9, 0x15c2, 0x40af, 0x4409, 0xba47, 0x423b, 0x1b23, 0x1fc7, 0xbf3b, 0x23f9, 0x40f3, 0x4044, 0x41fa, 0x438d, 0x4081, 0x436f, 0x429a, 0x266b, 0x4277, 0x4544, 0x3a28, 0xba6e, 0x4131, 0x1d81, 0xbaf7, 0x011a, 0x415c, 0x09a7, 0x41de, 0x40cb, 0xbfb0, 0x40b7, 0xbfad, 0x42c9, 0x1993, 0x426b, 0x1201, 0x41d7, 0x42db, 0xb258, 0x41db, 0x435c, 0x447d, 0x461d, 0x43dc, 0x2410, 0x4287, 0xb08b, 0xb2c9, 0x438e, 0x433a, 0xb233, 0xad57, 0x44b8, 0x40fc, 0x4459, 0x1c30, 0x4118, 0x3dfe, 0xba53, 0x1ee0, 0x4130, 0xbfa6, 0xbac3, 0x0f62, 0xa018, 0x424b, 0x4109, 0x4351, 0x407e, 0xbf70, 0x1a92, 0x4289, 0x2f97, 0x41bb, 0x414d, 0x419a, 0xb0ab, 0x40a6, 0xb20d, 0x405e, 0x43fe, 0x422e, 0x4033, 0x41b9, 0x027e, 0x45e6, 0xbfe2, 0x4365, 0x4301, 0xa5e0, 0x430e, 0x4260, 0x40a4, 0x44cb, 0x0824, 0x2e66, 0x4490, 0x1efc, 0xbade, 0x228a, 0xbf60, 0x4048, 0x43b7, 0xbfce, 0x3516, 0x1e5d, 0xaabb, 0x2dbd, 0x36a6, 0xb2fe, 0x459a, 0xb0af, 0x2212, 0xb2f3, 0x42a2, 0x4346, 0x18fb, 0xbfda, 0xb2bb, 0xba39, 0xb261, 0xb231, 0x40f7, 0x4221, 0xb0d0, 0x427e, 0x4270, 0x463e, 0x4332, 0x40ff, 0xb086, 0x43a2, 0x41fc, 0x42d8, 0xbf33, 0xba11, 0x41f6, 0xb23d, 0xb004, 0x41ef, 0x2b68, 0x41a9, 0xbfc8, 0x41af, 0xb0bf, 0x2d51, 0xb08a, 0x41e4, 0xba33, 0x0fbb, 0x445b, 0x3c22, 0x4404, 0x42b7, 0x42da, 0x396d, 0xbfd9, 0x4069, 0x46d9, 0x40c9, 0x424b, 0xa8a1, 0x4399, 0x0771, 0x1ff4, 0xba29, 0x4232, 0x2361, 0xbf1c, 0xb248, 0x40b0, 0x45a2, 0x4171, 0x40b1, 0xb281, 0x4187, 0xb2ff, 0x1a83, 0x160c, 0x1f34, 0xb049, 0xbf2d, 0x430b, 0x4024, 0x2813, 0xbfb0, 0x4146, 0x2134, 0xb2a3, 0x42ca, 0xb0e6, 0x42c3, 0xb2aa, 0x462d, 0x41ee, 0x3025, 0xbf41, 0x152d, 0xb2ab, 0x22cc, 0x22c3, 0xb2ae, 0xba61, 0x430d, 0xbaf2, 0x42ef, 0x40f0, 0x2d16, 0x19da, 0xbf98, 0xba66, 0x1b36, 0xb202, 0x41ba, 0x43ca, 0x28ca, 0x406c, 0x458d, 0xbf5f, 0xba62, 0x42a5, 0xbf70, 0x412a, 0x42e1, 0xa8e9, 0xb000, 0xb0b3, 0x16fc, 0x45e4, 0x4345, 0xbf67, 0x4115, 0x4226, 0xaf4f, 0x41cf, 0x29e1, 0xa321, 0x42e8, 0x426e, 0x3646, 0x42ea, 0xb2f7, 0x42de, 0xac68, 0x341c, 0xb21f, 0x4352, 0xba35, 0xbfe0, 0xb0be, 0xbf24, 0x40f8, 0x1ace, 0xa6be, 0x4627, 0x445b, 0x4130, 0x30b1, 0x26c4, 0xbf00, 0xb08c, 0xb2ed, 0xba6f, 0xb219, 0xb2d1, 0x1ce1, 0x0ae6, 0xb25f, 0xbfd4, 0x05e1, 0x4648, 0xba06, 0xabfd, 0x4068, 0xb27f, 0x1a01, 0x0851, 0x42ff, 0x4691, 0x3367, 0x436c, 0xb20c, 0x4096, 0x43bf, 0x1f4b, 0x10e5, 0xb24f, 0x4325, 0xbfc0, 0x4113, 0xbf44, 0x41ee, 0x4045, 0x43d5, 0xbfbb, 0xb2cb, 0x42e1, 0x3336, 0x43e5, 0x0914, 0xb245, 0xba06, 0x198e, 0xadcb, 0x4079, 0xb23c, 0xbf3f, 0xa03a, 0x4155, 0x41d7, 0xba45, 0x37fe, 0x2b2e, 0x404e, 0xbaf3, 0x405a, 0x18c6, 0x42c6, 0x40f1, 0x402d, 0x184d, 0xa630, 0x42f2, 0xbfac, 0xb2ff, 0xb26a, 0xb2fe, 0xabc6, 0xb288, 0x4180, 0xbaf8, 0x4239, 0x1a7f, 0xb2c1, 0x40cc, 0x3a53, 0x34be, 0x4273, 0x435d, 0xb009, 0x1f2d, 0x4392, 0x424b, 0x459e, 0x43d3, 0x409d, 0x41d6, 0x3faf, 0xa208, 0x4107, 0xbf82, 0x4442, 0xa1e1, 0x1bc0, 0x42ed, 0x4068, 0x0b47, 0xb04b, 0x43fa, 0xba1a, 0xac7d, 0x1899, 0xa69d, 0x4327, 0xad06, 0x1f24, 0x1a75, 0xa2d1, 0x4257, 0x4106, 0x411f, 0x43b0, 0x4406, 0x45f1, 0xb2d5, 0x41a7, 0xb2f6, 0xa9a5, 0xbf25, 0x402e, 0x46a4, 0x41de, 0x4323, 0x1e92, 0x2b9b, 0xbfe0, 0xbfa0, 0xa5cb, 0x4276, 0x19cd, 0x0928, 0x40a9, 0xb2da, 0x4224, 0x42ca, 0xb095, 0x4399, 0x1917, 0xb260, 0xba50, 0xbf2d, 0xa243, 0xb29f, 0x42a1, 0x45aa, 0xa358, 0xb299, 0x4411, 0x4056, 0x4322, 0x1261, 0x40cc, 0x4153, 0x43d7, 0x4492, 0xba2d, 0x42fa, 0x437e, 0x40d1, 0xb0b6, 0xb28a, 0xb2c8, 0x07f6, 0x45ad, 0x4063, 0xb27b, 0x41b8, 0x1df7, 0xbf6e, 0xb225, 0xbf90, 0x1823, 0xb0a6, 0xbaf9, 0xba4b, 0x441d, 0x1bb0, 0x46bd, 0xa111, 0x43cc, 0x40bb, 0x41f1, 0xbf1d, 0x1c87, 0x409b, 0xba0a, 0x41ac, 0xaec5, 0xb24c, 0xbfab, 0x41f9, 0x45d3, 0x2e14, 0xb0fa, 0x156b, 0x4346, 0xb2b6, 0x19af, 0xbaf0, 0x418d, 0xb29e, 0xba47, 0x4342, 0x19aa, 0x3a55, 0x42b5, 0x4623, 0x42ac, 0xb0bb, 0xb246, 0xbfd0, 0x40af, 0x420e, 0x1f14, 0x250e, 0x1e9b, 0xbfca, 0x1d20, 0xbf70, 0xba73, 0x3e76, 0x1f68, 0x1b7b, 0x41d5, 0xbadb, 0x18ab, 0x42a0, 0x4024, 0x03ef, 0xbfd2, 0x1d77, 0xb298, 0x40b2, 0xb20f, 0x4102, 0x4046, 0x42a3, 0xbf8f, 0xb2b4, 0x3410, 0x1aca, 0x4032, 0x1bfc, 0x3a0b, 0xbf1f, 0x40b4, 0xb2d8, 0xb02c, 0x4288, 0x425d, 0x2c05, 0xab89, 0xbaec, 0x43d7, 0xb22b, 0xb20d, 0x4013, 0xb22d, 0x1819, 0xbf0c, 0xaba0, 0x43c0, 0xbac0, 0xba66, 0x4013, 0xb065, 0x18aa, 0x4139, 0x42bc, 0x45c5, 0x4128, 0x405e, 0x41eb, 0xba01, 0x4302, 0xaf34, 0x402e, 0x1181, 0xba70, 0x40c3, 0x191d, 0x4037, 0x40e4, 0xba4b, 0x09b6, 0x4561, 0xbfb9, 0x43ea, 0x02ee, 0x4311, 0x40ea, 0x4394, 0x4367, 0x4059, 0x4399, 0x43b5, 0xb077, 0x40af, 0x4394, 0x42d0, 0xb2b5, 0xb0f4, 0x3bf1, 0x44dc, 0x1887, 0x401f, 0x3da4, 0xbf7a, 0xb26f, 0x4421, 0x2540, 0x4107, 0x4229, 0x4389, 0xba20, 0x3378, 0xbae3, 0xba3e, 0x1c59, 0x40c5, 0x40a4, 0x1a2f, 0x1f4a, 0x432b, 0xa962, 0x41a1, 0x4631, 0xbf3b, 0x4245, 0xb295, 0xb28d, 0x42be, 0xbae8, 0x1b8a, 0x1a0c, 0x44fc, 0x469a, 0x42b7, 0x1ba4, 0x41b3, 0x4361, 0x4241, 0x4034, 0x4099, 0x433d, 0x438f, 0x41c4, 0xb2e4, 0xbf92, 0x426d, 0x4023, 0xba0b, 0xba3c, 0x40d4, 0x4152, 0xb23a, 0x425e, 0x43ac, 0x4146, 0xbfcd, 0x1617, 0x41a8, 0x08af, 0x4364, 0xbadc, 0x4348, 0xb295, 0xb2ee, 0xb21f, 0x400c, 0x4214, 0x4253, 0x4193, 0x4544, 0x1ff0, 0x43bc, 0x2489, 0x4145, 0x414c, 0x409c, 0xa728, 0xb09b, 0xbf19, 0x431f, 0xba0d, 0xba6c, 0x4201, 0x4063, 0x418e, 0x2b73, 0x0d77, 0x4574, 0x408f, 0x31b6, 0x077e, 0xbf48, 0xb2ac, 0xb2be, 0x1088, 0x411e, 0x42e5, 0x410d, 0x428d, 0x4319, 0xbf36, 0x354d, 0x0e3b, 0xb25f, 0x4316, 0xbfa3, 0x419a, 0x039b, 0x41b6, 0x04fd, 0x4583, 0x4284, 0x43c1, 0xbf24, 0x14e3, 0x4366, 0x438f, 0x4388, 0x4335, 0x423b, 0x4181, 0xb227, 0x0e84, 0x0fa9, 0x43a6, 0x1264, 0x0b9d, 0xb0e9, 0xb283, 0x46f2, 0x0ca3, 0x4364, 0x4311, 0xbac1, 0x4115, 0xb0d0, 0x2aa3, 0x1923, 0xbf5b, 0xb04e, 0x35af, 0xa9d4, 0x42c9, 0x4315, 0xbf60, 0x3bfd, 0x0a60, 0xbad1, 0x405a, 0x18b4, 0xb20a, 0xb075, 0x432b, 0x43ea, 0x4272, 0xb2ab, 0x4218, 0x430a, 0x13ee, 0xba22, 0xbf8e, 0x17e6, 0xb260, 0x43ed, 0x439a, 0xb29d, 0x1e98, 0x4355, 0xba54, 0xaffd, 0xb2d2, 0x4394, 0x404e, 0xb08f, 0x3bdc, 0x0f8b, 0x4243, 0x4382, 0xbf48, 0x4138, 0x44fd, 0x422b, 0xb08f, 0x1150, 0xbf73, 0x41d6, 0x1b03, 0xb0bf, 0x432e, 0x283d, 0x45a8, 0x0fa2, 0x0992, 0x40c4, 0x46d2, 0x43d8, 0x42a4, 0x4119, 0x4016, 0x427d, 0xb0c3, 0xb29e, 0xa0df, 0x4258, 0xba3a, 0x1df4, 0x0f25, 0x2f1c, 0x435a, 0x421c, 0x41d4, 0xbf07, 0xb2f7, 0x1a9e, 0x43a2, 0x43d8, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5cc0f53c, 0x72fcd234, 0xde457376, 0x49d4e05a, 0x758c22b2, 0x79c1668a, 0x3635ed63, 0xeac80e34, 0x347d2ad7, 0xd42d60b3, 0xe8a20e81, 0x381745c5, 0x0ec66078, 0xe13b4962, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0x0000b3bc, 0x00000000, 0x8e4f0000, 0xffff4c43, 0x00004c4a, 0x00000000, 0x00004c43, 0x0000051b, 0x6c94709c, 0x00090000, 0x00000000, 0x55a4c7ed, 0x3cb8de89, 0x0000173b, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb2ea, 0x42ba, 0x46c5, 0x422b, 0x1be5, 0x02d1, 0x1caa, 0x41bc, 0x43bd, 0x1dfd, 0xa8ec, 0xb044, 0x0ec5, 0x0aa4, 0x4022, 0x45c5, 0xb00b, 0x42da, 0x04c3, 0x00d9, 0x1e50, 0xbf03, 0x4184, 0x4254, 0xb282, 0x00fc, 0xb041, 0xb27c, 0xbafd, 0x18fc, 0x417c, 0x41d6, 0x4313, 0xba21, 0xb237, 0xbfb4, 0xb29f, 0xb260, 0x1acf, 0x21b0, 0xb062, 0xbac1, 0xbacb, 0x41aa, 0x1399, 0xb255, 0xb28d, 0x1f91, 0x408a, 0x1391, 0xb200, 0x40ba, 0x4234, 0xbad2, 0x4031, 0x417a, 0x43fe, 0x40ff, 0x4131, 0x44c2, 0xbf6d, 0xba23, 0xb22a, 0x1a46, 0x432b, 0x1a26, 0x1525, 0x4148, 0x421b, 0x3ac6, 0xba73, 0xb007, 0x448c, 0xb211, 0x40e9, 0xba0c, 0x2a7e, 0x409b, 0x4478, 0x4495, 0xba61, 0x40e5, 0x465d, 0x4110, 0xb07e, 0xbf04, 0x4174, 0x1ce1, 0xb2b7, 0xb26a, 0x40e2, 0x11ea, 0x091b, 0x46d5, 0x1def, 0x4351, 0x4244, 0x405a, 0x4109, 0xa87b, 0xb249, 0x44b1, 0xba4f, 0xb038, 0xb083, 0xbff0, 0x4016, 0x43f0, 0xb092, 0x03f6, 0x19ad, 0x4287, 0xbf76, 0x43b0, 0x4356, 0x3e6a, 0x1aa0, 0x41c5, 0xb2d4, 0x1cb6, 0xba0c, 0x43ce, 0x45f2, 0xb066, 0x361b, 0x294f, 0x4133, 0xaa78, 0x4659, 0x4019, 0x4310, 0x4158, 0x412d, 0x4077, 0x41d9, 0xba7e, 0xbf96, 0x112e, 0x3932, 0xb0cf, 0x4395, 0x4237, 0x2cd3, 0x35bb, 0x03ff, 0x3c3f, 0xa63b, 0x4352, 0x4110, 0x04e4, 0xaad1, 0xb05a, 0xb2af, 0x4101, 0x4309, 0x43c7, 0xa7cc, 0x1b1d, 0xb2bc, 0x42b2, 0x446d, 0x42e2, 0x4044, 0xbfae, 0xbfd0, 0xa92b, 0x2ca8, 0xba7a, 0x0c15, 0x18af, 0x10c6, 0x4125, 0x411c, 0xb290, 0x40c7, 0x38e2, 0x425a, 0x26d4, 0x45ca, 0x46b5, 0xa9af, 0xbfab, 0x401a, 0x3480, 0x43e2, 0xb205, 0xb06b, 0xb0ce, 0x4213, 0xba35, 0x42d3, 0x1d9c, 0x40c8, 0x43dd, 0x42cc, 0x264c, 0x0f86, 0xbf8a, 0x18b3, 0xb0b6, 0xbff0, 0x08c3, 0x146a, 0x43a1, 0xbfad, 0xb024, 0x419b, 0xada4, 0xb207, 0xa205, 0xb2b0, 0x4013, 0x1ffc, 0xb2dc, 0xb2a6, 0x4189, 0xa033, 0x373b, 0xa54c, 0x4229, 0x1f08, 0xbf17, 0x426b, 0xbf90, 0x18ca, 0xa154, 0x41c6, 0x3c30, 0x426a, 0x431a, 0x1add, 0xb0d0, 0xad69, 0x1d25, 0xbf1f, 0xb2c7, 0x26fe, 0x1dc6, 0xacb6, 0xba38, 0x4554, 0x19cd, 0x4167, 0xa82c, 0xb2d0, 0x15fe, 0x2a17, 0x43cf, 0x45bc, 0x4002, 0x438b, 0xb24a, 0x1bca, 0xb00a, 0x40e5, 0x4367, 0xbfa5, 0x425c, 0x1a88, 0x0641, 0x41e3, 0xb22c, 0xb245, 0x42d5, 0xb23f, 0x4615, 0x43fc, 0xbaed, 0x4272, 0x414c, 0xbf31, 0x41c1, 0xb2df, 0xb2cb, 0x402b, 0x42a6, 0x4356, 0xbf3e, 0xb2e9, 0x2814, 0xbfa0, 0x4143, 0xbfe0, 0x0b79, 0x1b81, 0x38a3, 0x412f, 0x20d2, 0x4068, 0x42cb, 0xbae8, 0x2a4f, 0x1d57, 0xb09d, 0x4168, 0xb208, 0x41e9, 0x408c, 0x357e, 0x469d, 0x43eb, 0x4189, 0xbac2, 0x40d8, 0x4580, 0xbf7a, 0x0b11, 0x4070, 0x4471, 0x43f4, 0x43b0, 0x4586, 0xba4a, 0x346a, 0xa7e9, 0x43b2, 0xbac3, 0x0423, 0x40d6, 0x248d, 0x40a5, 0x420a, 0x438f, 0x427b, 0x4202, 0x31ec, 0x2e50, 0xba26, 0xbf5e, 0x41f2, 0xb08f, 0x42ab, 0x2f2e, 0x4111, 0xbfd0, 0x4324, 0x40de, 0x4236, 0xba1b, 0x02e1, 0x3f0c, 0x0a8d, 0xa1e9, 0x092b, 0x42d4, 0x45dd, 0x43f4, 0x3b18, 0xaff9, 0xba2f, 0xba68, 0xb0de, 0x19b1, 0xbf37, 0x43c5, 0x1642, 0x41d7, 0xbf00, 0x311f, 0xbfd0, 0x4254, 0x423a, 0x4124, 0x1eb4, 0x4364, 0x4365, 0x40e0, 0xbf3e, 0x3c12, 0x43c1, 0x2fbc, 0xb0bd, 0xbac3, 0x4014, 0x41f6, 0x40dc, 0x40ac, 0x4330, 0x1e86, 0x438b, 0xb0d0, 0xac1f, 0x055f, 0x378c, 0x1f34, 0x1f9c, 0x0795, 0xb0c8, 0x410b, 0x4050, 0xbfdb, 0x41eb, 0x3e25, 0x4171, 0x432d, 0xb2d0, 0xb25f, 0xb281, 0x41e1, 0x432f, 0x4073, 0xbf60, 0x40fc, 0xbaf7, 0xb0ba, 0xaac8, 0x4318, 0x410c, 0xa369, 0xb08a, 0x42f8, 0x1c9b, 0xbf78, 0x34a5, 0x3209, 0xadae, 0x4272, 0x0d71, 0xa43d, 0x1180, 0x4229, 0x0689, 0x4313, 0xb208, 0x444c, 0x3a48, 0xba19, 0x4007, 0x4107, 0x2a6a, 0x41be, 0xb2ee, 0xbf42, 0x4332, 0x1870, 0x10cb, 0xb078, 0x411f, 0x29f2, 0xbfe0, 0x20df, 0x41f2, 0x39f3, 0x3ecf, 0x42fc, 0x1623, 0x43a9, 0xbf73, 0x40e3, 0x43ff, 0x40d3, 0x0ef5, 0x43a4, 0xb2aa, 0x4187, 0x403a, 0x1b1c, 0xb0be, 0x354e, 0xb09c, 0x402c, 0x2b3a, 0x0e11, 0x1335, 0xbf60, 0x44f1, 0x2ecf, 0x1b37, 0xb235, 0xb244, 0x428b, 0xb0bb, 0xb2d6, 0x19a6, 0x4589, 0xbf04, 0x1a8c, 0xb277, 0x13d1, 0x4394, 0x1f6f, 0xab11, 0x4335, 0x40f2, 0xb0b6, 0x1fb6, 0x403c, 0x40b0, 0x4274, 0x4345, 0x4282, 0x2a39, 0xb2e2, 0x4327, 0x4278, 0x44ab, 0xb014, 0x2313, 0x425e, 0x41e4, 0xbf6f, 0x1cd2, 0x40c1, 0x3319, 0x40dc, 0x43a4, 0x1839, 0x0638, 0x42b7, 0xbf17, 0x42b9, 0x0c5b, 0xb0e4, 0xb29a, 0xba78, 0xb290, 0x1ed9, 0xbf05, 0xb2ff, 0x42dc, 0x4253, 0x417a, 0x4304, 0x16d8, 0x3184, 0xb257, 0x3bd3, 0x419f, 0x3bd5, 0x4068, 0x10b7, 0x0f9e, 0x40a9, 0x4263, 0x414f, 0x43cd, 0xbfd0, 0xb0d9, 0x411e, 0xb26b, 0x3f5f, 0xbad3, 0x148b, 0x187a, 0x42ce, 0xb296, 0x1d75, 0xbf15, 0x4637, 0x42d9, 0x44d8, 0x4089, 0x05a3, 0x43d8, 0x43ad, 0x4091, 0xb21a, 0xb2bd, 0x4027, 0x1a6a, 0x40a2, 0x41ac, 0xa8f3, 0x41f9, 0xbf31, 0x4345, 0x3a56, 0xa529, 0x4215, 0x2faa, 0xb2b4, 0x195b, 0xb275, 0x4181, 0xba12, 0x43a6, 0x43f5, 0x315d, 0x4629, 0x1d9f, 0xbf94, 0x4212, 0x417b, 0x417c, 0xbfd1, 0x4614, 0x427d, 0x4140, 0xb2ae, 0x41ad, 0x1f90, 0x4286, 0x42ff, 0xac6c, 0xa113, 0xb29a, 0xb2e0, 0x0304, 0x46ed, 0xb2b4, 0xbf2c, 0x1079, 0x4056, 0x424b, 0x40b0, 0x43c2, 0x400d, 0xb2ba, 0xa574, 0x4354, 0x1145, 0x4567, 0x46a9, 0xba63, 0x4684, 0x4259, 0x4396, 0xb231, 0x4388, 0x4047, 0x40c5, 0x42b9, 0xba19, 0x31fc, 0xb012, 0x4154, 0x1a7b, 0x435c, 0xbf3b, 0x4211, 0x4136, 0xafba, 0x1a25, 0x4348, 0xb049, 0xba0e, 0xba6d, 0x43bd, 0xb231, 0xb2b6, 0x464e, 0x17cd, 0x4050, 0x438a, 0x410a, 0x41f7, 0x0d50, 0x41f6, 0x433c, 0xb0a6, 0xbaca, 0x40c2, 0xbf48, 0x4210, 0xbfc6, 0xba0c, 0xba18, 0x4641, 0xb2b3, 0x404f, 0x44f0, 0x4317, 0x2b5f, 0x4077, 0x458e, 0x1005, 0xb2a4, 0xbf42, 0x1ce5, 0x10f8, 0x4340, 0x43ca, 0xbfe0, 0x182e, 0x38a8, 0xa2ad, 0x42ed, 0x4592, 0x2346, 0xbf70, 0x43a2, 0x4037, 0x1360, 0xbafe, 0xb0bb, 0x43b2, 0x19c0, 0xbfa1, 0x4336, 0x4397, 0x1f3f, 0xba20, 0xb0e4, 0x4134, 0x1e98, 0x1926, 0xba44, 0x434d, 0xb087, 0x4673, 0xb20e, 0xba0f, 0x262a, 0xa308, 0x2693, 0x1e06, 0x424b, 0xb096, 0x439f, 0x36fa, 0x42d6, 0xb06a, 0x40fb, 0xbf13, 0xaa9f, 0x1d6c, 0xb2e0, 0x4363, 0x10e8, 0x1c58, 0xb290, 0xb072, 0x2deb, 0x421e, 0x1897, 0x4154, 0xab61, 0xba24, 0xbf70, 0x4326, 0x406a, 0x4595, 0x4169, 0xb228, 0x1e7f, 0xb20d, 0x188c, 0xb2a2, 0x1263, 0x0cbc, 0x46b0, 0xbf37, 0x42b0, 0x4374, 0x4312, 0x2515, 0xaa4a, 0x4150, 0x41fb, 0x434e, 0x42e0, 0x42f6, 0x408c, 0x26be, 0x42ea, 0x1edf, 0x1c47, 0x402c, 0x435e, 0xbf59, 0x3faf, 0x4462, 0xba3f, 0xb27d, 0xbf9d, 0x4207, 0xaede, 0xb223, 0x437f, 0x1786, 0x42a8, 0x4262, 0xb294, 0x1a29, 0xb2d6, 0x40cf, 0x1804, 0x42cb, 0xb025, 0xb265, 0x4298, 0xba0b, 0x4627, 0xb2d2, 0x40b4, 0xbf9b, 0x407c, 0x42b8, 0x18d0, 0xb2d0, 0xa7c8, 0x3b60, 0xb259, 0x2e49, 0xb228, 0x42dd, 0xb2b1, 0x237d, 0x43cd, 0x4001, 0x1a2b, 0xba71, 0xbf3f, 0x15f9, 0xb095, 0x01dc, 0x365f, 0x142e, 0xab9e, 0x4242, 0x4629, 0x424d, 0xb229, 0x404c, 0x0a33, 0x41a0, 0x455f, 0x077d, 0x1eb8, 0x1ba4, 0x13d8, 0x3aba, 0x1dbd, 0x4289, 0xbfca, 0x4253, 0x31bd, 0xb025, 0x27c1, 0x42dc, 0x1909, 0x37cd, 0xb28f, 0x43f0, 0x42af, 0x4185, 0xba6f, 0x466a, 0x15c5, 0x42e2, 0x3d8e, 0xba2d, 0x27ef, 0x45e6, 0xbfa3, 0x4204, 0x41b6, 0xb2bc, 0x4025, 0xbf67, 0x0886, 0x3457, 0x1cc4, 0x276c, 0x42a4, 0x3f63, 0xbad3, 0xbf16, 0xba62, 0xb087, 0x1e84, 0x4015, 0xb068, 0x31ca, 0x411e, 0x1fd1, 0xb01f, 0x4607, 0x1d77, 0x461c, 0x43cc, 0x401f, 0xb2b5, 0xa43d, 0xba66, 0xb28b, 0xbf16, 0xb209, 0x4097, 0x417e, 0x40bb, 0xa1b2, 0x4443, 0x4042, 0x405c, 0x1bfc, 0xba06, 0xb098, 0xb014, 0x40ec, 0x4159, 0x417a, 0xb2a6, 0x2ff6, 0xb21b, 0x44f8, 0x420a, 0xb239, 0x4156, 0xbf12, 0x1d1f, 0x42b6, 0xba5f, 0xb0a9, 0xbfe2, 0x43bd, 0x4384, 0x44bd, 0x4085, 0x43e4, 0x199f, 0x41bc, 0x186b, 0x429b, 0xb285, 0x1bf3, 0xb0ec, 0x4155, 0x40ff, 0x4019, 0x439f, 0xb0ad, 0x35bd, 0x21d8, 0x417f, 0x434d, 0x180b, 0x1a8d, 0xab25, 0xba3e, 0x1832, 0xbf37, 0x40f9, 0x427a, 0x4184, 0xba66, 0xba18, 0x4022, 0xbf60, 0xb2cc, 0xb2f2, 0x4127, 0x433b, 0xb0bc, 0xba3c, 0x4189, 0x439d, 0xb0e3, 0x4685, 0x3f24, 0x1d66, 0xbf08, 0x43c5, 0xb2de, 0x40f3, 0x183e, 0x430c, 0x40ea, 0x19e2, 0x1cd2, 0x1cf6, 0x3a92, 0xb0d3, 0xb2c3, 0x4236, 0x4313, 0x3162, 0x406d, 0xbf90, 0x42d3, 0x46d4, 0x40e9, 0x41a1, 0xa0cc, 0x4071, 0xba15, 0xbfa3, 0x3d16, 0x4361, 0x4247, 0x04b4, 0xb0ee, 0x1f85, 0x3042, 0x41a7, 0xb242, 0x428b, 0x4322, 0x420e, 0x38df, 0x4083, 0x41f9, 0x06c0, 0x420b, 0xb2a2, 0xb20f, 0xbf63, 0x4148, 0x0544, 0x4066, 0xa5e5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x1ad1dd6f, 0x8a780aab, 0x92d86fe7, 0x3dcc1243, 0x715b8d62, 0xdd61381a, 0x5655e1f0, 0x38a9fd45, 0x5503834e, 0x5c5e12c5, 0xc6ca1316, 0xf112ee7a, 0x4c667450, 0xbace516f, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x78000000, 0xe27ff7b4, 0x00000000, 0x00000000, 0xff7c0000, 0x00001b68, 0xa713ffdf, 0xfffff7b4, 0x05001840, 0x00000000, 0x1bcd9664, 0xf112ee7a, 0x1bcd9664, 0xa713fcfc, 0x00000000, 0x600001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x43ea, 0xb039, 0xb0f0, 0x4163, 0x08a9, 0xba5c, 0x410d, 0x06ef, 0x42b8, 0xb095, 0x4045, 0x456a, 0xbf74, 0xabfb, 0x4084, 0x159e, 0xb230, 0x4372, 0xb005, 0x404f, 0x4017, 0x43f0, 0x43da, 0x40c7, 0xbf9f, 0x4207, 0x44c0, 0x0047, 0x0b05, 0xbf7b, 0xbfe0, 0x4057, 0x1d47, 0x1f0f, 0x40b2, 0x403c, 0xb09f, 0x03e8, 0x4246, 0xb26d, 0xb26c, 0xbf04, 0x42d6, 0xb040, 0x4631, 0x4092, 0x430e, 0xbf00, 0x40cc, 0xb243, 0x214e, 0x06a1, 0xbfc6, 0x40b0, 0x4631, 0x43cd, 0x4340, 0x13ca, 0x14d1, 0x4603, 0x4079, 0x2c83, 0x421d, 0xbacb, 0xafa3, 0x409c, 0xb2f9, 0xa17d, 0x30ee, 0xba61, 0x4207, 0x0a66, 0x420d, 0x4189, 0x41ea, 0x20f3, 0x42b9, 0xb20d, 0xa046, 0x4388, 0xb2bc, 0xbf7c, 0xb010, 0x43a3, 0xb21d, 0xb22f, 0xb2c8, 0x40bb, 0x19aa, 0xa4a9, 0xac73, 0x4296, 0x2be3, 0x0f78, 0x4107, 0x421a, 0x16f8, 0x427c, 0x444b, 0xbacb, 0x40b4, 0x0480, 0xb0b8, 0xba71, 0x402b, 0x40b5, 0x46a8, 0xbf34, 0xb25d, 0x2418, 0xaac1, 0xb251, 0x2ca1, 0xbf60, 0x4012, 0xbae5, 0xba4d, 0xba63, 0x4172, 0x41bd, 0xa74e, 0xbad9, 0x1f15, 0xb2d0, 0xb26e, 0x2e39, 0x413e, 0x4026, 0x4136, 0xb0eb, 0xb2fe, 0x4211, 0xbf76, 0x44fb, 0x43da, 0x3f14, 0x01c4, 0x41d7, 0x41ee, 0x0326, 0x237a, 0xb026, 0xbfbf, 0xb286, 0x40d8, 0x183c, 0xb2fc, 0x43cd, 0x41d4, 0x1b4c, 0x42c7, 0x03da, 0x4156, 0x40ce, 0x1888, 0x02d2, 0xba79, 0x40ef, 0x4081, 0xb23b, 0x4099, 0x417b, 0xbfa0, 0x4152, 0x4158, 0xbff0, 0xad95, 0x4477, 0x4176, 0x4132, 0xbadb, 0xbf0c, 0xba3a, 0x43e2, 0x1e39, 0x438f, 0x2393, 0x413b, 0x423d, 0xba05, 0x30e8, 0x4383, 0x4081, 0x1dc3, 0xba17, 0xbf9b, 0x294d, 0xac43, 0x18c2, 0x4362, 0xb2d4, 0x4349, 0x420d, 0x42a7, 0x42df, 0x2363, 0x40c8, 0x226c, 0x417a, 0xbfd0, 0x435c, 0x411d, 0xb07f, 0xba54, 0x1da1, 0xa883, 0x1b72, 0x4022, 0x43cf, 0x129b, 0x0952, 0x1a40, 0x1aaf, 0xbf3a, 0x406e, 0xb274, 0xb0a9, 0xbfe0, 0xba4e, 0x4004, 0x38de, 0x41cc, 0x4167, 0xb2a4, 0x413b, 0x1b27, 0xb072, 0x1ed4, 0x3b32, 0xb237, 0xb0fe, 0x1821, 0x414d, 0x41f1, 0x42b0, 0xbf1b, 0x426e, 0x460d, 0x4246, 0x41b1, 0x1c38, 0x4473, 0x413c, 0x4143, 0x24b8, 0x43bf, 0x43dc, 0x3a1b, 0x41fd, 0x4386, 0xba70, 0x41e7, 0x2256, 0x4086, 0x4341, 0x1af4, 0xb21b, 0xbfa0, 0x1f37, 0x4598, 0x431d, 0xbf63, 0x2db5, 0x416c, 0x428d, 0xb2a5, 0x4558, 0xb2f6, 0x41a8, 0x4014, 0x43f5, 0x33d6, 0x41cb, 0xb268, 0x4275, 0xb023, 0x438e, 0xbfe4, 0x415e, 0x19b9, 0x468a, 0xb252, 0x3626, 0x43ea, 0x2d17, 0x40aa, 0x42aa, 0xbfe0, 0x43a3, 0x1b9b, 0xba61, 0x4330, 0x0dbd, 0xa126, 0x40c7, 0x41de, 0xbfc6, 0x404d, 0x46a4, 0x4647, 0x4340, 0x40c8, 0xbf80, 0x42c4, 0x1df3, 0xbf90, 0xa7ac, 0xba4e, 0x4308, 0x0dac, 0xba47, 0x1d7a, 0xb06e, 0x3e91, 0x2e8b, 0x4008, 0x1c78, 0xb219, 0x4161, 0x410d, 0x4076, 0xbf9d, 0xa660, 0x422a, 0x42f6, 0x19e9, 0xbf9a, 0x43fb, 0x421e, 0xb28a, 0x40a0, 0xa3a0, 0x4081, 0x1990, 0xba24, 0xbf02, 0x41d5, 0x4078, 0x4176, 0xba52, 0x1f86, 0xb042, 0x430f, 0x2816, 0x428f, 0x4084, 0xa29f, 0xb019, 0xbfc8, 0x432c, 0x40a9, 0x40a3, 0x1b29, 0x43f5, 0x3867, 0x40cd, 0x4330, 0xb277, 0x3830, 0x41d4, 0xb083, 0x4164, 0x36a6, 0x4082, 0x4141, 0x1167, 0xb2fa, 0x1a54, 0x4284, 0x43e4, 0xa27e, 0xa4e3, 0x413d, 0xbf9a, 0x08da, 0x1b35, 0x4385, 0xb232, 0x4166, 0x422c, 0xbf90, 0x412f, 0xbf2f, 0x41b9, 0x1b51, 0xba0c, 0xbfb0, 0xba34, 0xbf8f, 0x1d3d, 0x30c1, 0x439c, 0x4543, 0xb2d0, 0x1fef, 0x4359, 0x4167, 0xb29f, 0x4331, 0xb2f5, 0x4308, 0xa8a7, 0xba06, 0x400f, 0x43e8, 0x246f, 0x4206, 0x416f, 0xb22c, 0x4043, 0x42b3, 0xbacd, 0x41ac, 0xb280, 0x27e3, 0xbf44, 0x402b, 0x4009, 0xb22b, 0x423d, 0xb28a, 0xb234, 0x4114, 0x42c3, 0x2c01, 0x4344, 0x40a0, 0x41e8, 0x180b, 0x259d, 0x4296, 0x0859, 0x4004, 0xbf6e, 0x43a3, 0x42ca, 0x409f, 0xbaca, 0x1c37, 0x4085, 0x40c7, 0x4113, 0xbac5, 0xb02d, 0x1c91, 0x3cb2, 0x0819, 0x4352, 0x432f, 0x3f57, 0x3c39, 0x43a0, 0xb063, 0x430f, 0x4374, 0x4131, 0xbf52, 0x43d4, 0x432d, 0x07d6, 0xa457, 0x431b, 0xad5a, 0xb24f, 0x16dd, 0xb026, 0x22aa, 0x432c, 0xa6c9, 0x41f6, 0xac8c, 0xb07d, 0xa40a, 0x4137, 0xbf95, 0x3196, 0x41e4, 0xb2c0, 0x4362, 0xb24b, 0x407a, 0x4093, 0xbfdf, 0x0cd1, 0x3b7c, 0x427f, 0x4312, 0xa0a2, 0x20fd, 0x41cc, 0xba31, 0x424e, 0x1aa4, 0xbacb, 0xb26a, 0x4048, 0x4416, 0x43e5, 0x42d4, 0xba20, 0xbf8f, 0x4079, 0xb27e, 0x4271, 0x40ee, 0x466d, 0x2d6c, 0x1843, 0x4083, 0x4327, 0x0cc7, 0x4582, 0xbf6f, 0x424c, 0xafb3, 0x41a0, 0x4239, 0x4404, 0xbf80, 0x418d, 0x43e0, 0x1ab2, 0xbfaf, 0x43ab, 0x4357, 0x42cb, 0xb0bf, 0xba12, 0x1396, 0x41de, 0x4116, 0x2112, 0x1b76, 0x4007, 0x44e9, 0xb224, 0x14d0, 0x402d, 0x412e, 0xb2d1, 0x2afc, 0x42f2, 0x1eca, 0x3412, 0x4472, 0x4252, 0x40cd, 0xb0fa, 0x0898, 0xbf79, 0xbaca, 0x3752, 0x0b3b, 0x1f37, 0x3758, 0x42b8, 0x43bd, 0x01e7, 0x42d1, 0x400a, 0xb044, 0xb2c6, 0x40bb, 0x4384, 0x3f19, 0x43ae, 0xba2c, 0x05ef, 0xb211, 0x1e3a, 0x437d, 0xbfa0, 0xbf7b, 0xb0a2, 0x4326, 0x1c73, 0x425b, 0x412d, 0x44d0, 0x11c1, 0x1b69, 0xb2c3, 0x1f89, 0xa6b4, 0x2ed9, 0x418d, 0x1ccb, 0xaa53, 0x402d, 0x1a6b, 0x3c2d, 0x4622, 0xb2c7, 0xa4b8, 0x4066, 0x45cb, 0x10a2, 0x4010, 0x41bb, 0x4300, 0xbf15, 0xb0b0, 0xba4e, 0xb29f, 0x1d46, 0xba36, 0x43b8, 0x0c37, 0x4499, 0x1bcf, 0xae12, 0xb26a, 0x42d5, 0x4362, 0x25db, 0xba02, 0xb29e, 0x3650, 0x4564, 0x4158, 0x1d0e, 0x4132, 0x4237, 0xb06f, 0x4377, 0xbf92, 0xbfb0, 0x4151, 0x443e, 0x439f, 0x43af, 0xaf28, 0x4151, 0x40d7, 0x40c7, 0x070e, 0x467f, 0xb226, 0x4418, 0xb016, 0x4288, 0xb009, 0x3e9f, 0x2434, 0x45f5, 0xb0a5, 0x1b5f, 0xb270, 0xbf39, 0xb2b4, 0x4093, 0x169a, 0x43c0, 0x29c4, 0xbf61, 0x4206, 0x2bcc, 0x401c, 0x43b0, 0x432e, 0x07bf, 0x40a7, 0x1b14, 0xb09b, 0x1a2b, 0xa498, 0xbaf3, 0x4204, 0x415b, 0xb02f, 0x42c9, 0x4264, 0x40eb, 0x40b9, 0x2f8a, 0x162c, 0x1b93, 0x18c7, 0x40d6, 0x4484, 0xbf63, 0x407b, 0x40cc, 0x19bd, 0x4106, 0xbaec, 0x4076, 0x4271, 0xadcc, 0x42b6, 0xbf12, 0xb2fc, 0xb267, 0x40ea, 0x40c4, 0x34ed, 0xba7e, 0x0e23, 0xae59, 0xbf4a, 0x0743, 0x43d7, 0x42dd, 0x4097, 0x40ed, 0x40a8, 0x45ed, 0x1e93, 0xb213, 0x1c3b, 0x0ea9, 0x420d, 0xb038, 0xba5a, 0x42ed, 0x41e0, 0x41b2, 0x4327, 0x19c5, 0x42d8, 0x26fb, 0x43fd, 0x0f88, 0x43a7, 0x1c9b, 0xb207, 0xbfc9, 0xb2c5, 0x430a, 0x2512, 0x4356, 0x41ed, 0x4078, 0x40c0, 0xbf99, 0x4141, 0xba72, 0x4226, 0x42d0, 0xb2b2, 0x4150, 0x42d1, 0xb04c, 0xba33, 0x2f36, 0x45b0, 0x18b0, 0x1d78, 0xbf43, 0x426a, 0x4350, 0x4287, 0x406a, 0x1911, 0x4199, 0x4074, 0xbf9a, 0x43f2, 0x4344, 0x41f6, 0x0efc, 0x30a0, 0x2e3e, 0x4359, 0xb261, 0x46b5, 0x4164, 0x25dd, 0x0839, 0xa6d1, 0x4181, 0x4627, 0x419a, 0xba3a, 0xbf16, 0x3ee3, 0x158a, 0x407d, 0x424f, 0xba32, 0xb212, 0x41f9, 0xb019, 0xbf80, 0xbfa0, 0xb2af, 0xb2bb, 0xb21e, 0x427c, 0x441b, 0x40e7, 0x3e04, 0xba75, 0xbfde, 0x40dc, 0x4278, 0x2b7d, 0x3966, 0xb06e, 0x1733, 0x465b, 0x4185, 0xb2f9, 0x42dd, 0xb246, 0xbff0, 0xb019, 0x42e4, 0xb259, 0x42a0, 0xba79, 0x18d5, 0x2f05, 0x4093, 0xbf7d, 0x2891, 0x423a, 0xb201, 0xb001, 0x1ce7, 0x1de1, 0xbafd, 0xb2b0, 0x42d6, 0x41f4, 0x206e, 0xb0b6, 0x3d9b, 0x3156, 0xbff0, 0x4483, 0x46d2, 0x41bb, 0x22a6, 0x413e, 0x4180, 0x3d93, 0x46f3, 0xb2e1, 0x4064, 0x42b4, 0xb279, 0xbf6c, 0x41fd, 0x42a1, 0x396b, 0x1383, 0x42ec, 0x1e32, 0x4564, 0x312c, 0x416f, 0x43be, 0x2633, 0x1e58, 0xb29b, 0x415a, 0x384f, 0x43ec, 0x428a, 0xbad6, 0x40bb, 0xba2d, 0xbf3b, 0x41fb, 0xb26f, 0x4208, 0x407e, 0xbacd, 0x1bb6, 0xbf6c, 0x435d, 0x41db, 0x2381, 0x402e, 0x4278, 0x40cb, 0xba40, 0xbac3, 0x3c20, 0x3b4a, 0x1a38, 0x437d, 0x4100, 0x40d5, 0xb0fd, 0x46c4, 0xbfdf, 0x4251, 0xba51, 0x42c7, 0x0106, 0x0ab7, 0x1cf3, 0xa039, 0x1e55, 0x42b5, 0x43c6, 0x4614, 0x409c, 0x4383, 0xbf76, 0x45f3, 0x01b8, 0xb045, 0xba1d, 0x4000, 0xbfe0, 0x41ac, 0x42e8, 0xb055, 0xa8af, 0x4413, 0x3917, 0xba4b, 0x42a6, 0xb097, 0x3a05, 0x410d, 0x43a3, 0xa1f5, 0xba69, 0x4164, 0x295b, 0xb0d1, 0x43d5, 0xbf6d, 0x0b74, 0x41c4, 0xba32, 0x1c4d, 0x40cf, 0x41c7, 0x4591, 0xa4d4, 0x4376, 0x131b, 0x42f1, 0x4308, 0x42a9, 0x1d4d, 0x41bc, 0x425f, 0x42b1, 0xacd1, 0xaa4f, 0x45de, 0x0d5f, 0x270c, 0x1e62, 0x4353, 0x4113, 0x2cc0, 0xa57c, 0x1eba, 0x4153, 0xbfa8, 0x413a, 0x41be, 0xbf2f, 0x22f9, 0xada0, 0x4171, 0xb2fc, 0x458e, 0x405a, 0x4119, 0x42a9, 0xae09, 0x435e, 0x1c93, 0xbf32, 0x1d79, 0x401d, 0x4098, 0xbfa0, 0x3409, 0x41d9, 0x46a1, 0xbfa3, 0x2470, 0x4583, 0x41be, 0xb295, 0x1aab, 0x319a, 0xbf97, 0x1b2d, 0x0205, 0x424c, 0x41a8, 0x433f, 0xbae9, 0xba25, 0xbfd0, 0x43ab, 0x401c, 0x2ddb, 0x3ade, 0x404a, 0x4203, 0xb2b9, 0x4559, 0xba40, 0xaa0b, 0x3a83, 0xa48e, 0x4288, 0x10f8, 0x40fd, 0xbfd2, 0xb09f, 0x4280, 0x40bf, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3dfcb14d, 0x03113440, 0x43d4feab, 0xf4773a5f, 0x012ea932, 0x9dad9d54, 0xf24b822f, 0x4f0bd97c, 0x95955220, 0xe3991301, 0xb50130a1, 0xe8c03445, 0x19b5cca3, 0x0afb31b9, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0x00000001, 0x0000000c, 0x3bae2320, 0x00000000, 0x00001a18, 0x0006677f, 0x54cd640e, 0x0000000c, 0x001bcc0d, 0x3bae26c4, 0x001c3ffd, 0x00000000, 0x001bcc0d, 0x3bae2377, 0x00000000, 0x600001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1ba6, 0x4015, 0xb0f7, 0xba63, 0x1f03, 0xb220, 0x42c4, 0x1831, 0x2745, 0x42db, 0xb2af, 0x455f, 0x0c95, 0x2125, 0x454b, 0x3a14, 0x17d0, 0xb250, 0x4237, 0x1fb6, 0xbf79, 0x43dd, 0x4336, 0x407f, 0x1b4f, 0x305a, 0x4094, 0x3a18, 0xa357, 0xadef, 0xbaee, 0x3be9, 0x438a, 0xbf90, 0x4316, 0xba54, 0x40a1, 0x46e8, 0x1b13, 0xba2d, 0x4083, 0xbad4, 0xbf13, 0x1852, 0xb064, 0x1935, 0x4494, 0x4159, 0x4433, 0x2b74, 0xba35, 0x212c, 0x41a2, 0x41f0, 0x4078, 0xb24f, 0xb213, 0x4049, 0x439b, 0x1e45, 0x417c, 0xbff0, 0x17c1, 0x4304, 0x4363, 0xbf71, 0xb267, 0x41ef, 0x4213, 0x46f9, 0x1c2a, 0xba31, 0x40b6, 0xbfce, 0x43c3, 0x4341, 0x42f4, 0x4159, 0x42ea, 0x4233, 0x427f, 0xb2fa, 0x439b, 0x43b9, 0xba25, 0x1f55, 0x41bd, 0xbf8d, 0x3fdf, 0x40fd, 0x3e6e, 0xb219, 0x04ff, 0x2c3e, 0x44e2, 0x409c, 0x403c, 0x0d36, 0x441e, 0x01ff, 0x0059, 0x41db, 0x1af3, 0x0cd0, 0x1db2, 0x4384, 0x42bf, 0x1c2b, 0x2068, 0x4648, 0xb201, 0x3ab3, 0x380e, 0xba27, 0x4160, 0x0100, 0x1adc, 0xbf1c, 0x10ff, 0x0ce6, 0x4100, 0x4211, 0x4242, 0x417a, 0x419c, 0x4026, 0x4247, 0xb26e, 0x0b32, 0xb022, 0xbf82, 0xa679, 0x4307, 0xba4f, 0xbfce, 0x3af9, 0x1cf8, 0x0647, 0xbf59, 0xb225, 0x423b, 0xbff0, 0xba38, 0x3523, 0x3a16, 0x15c0, 0xb290, 0xa1f9, 0x4369, 0x422a, 0x42c8, 0x1897, 0xabe5, 0xbfc1, 0xb205, 0x22e3, 0x42cf, 0xb091, 0x30d5, 0xb244, 0x266b, 0x402b, 0x4076, 0x1cfb, 0x466d, 0xa4e3, 0xa5a0, 0x40f9, 0xb229, 0x433f, 0xb075, 0xb2b0, 0x4216, 0x4295, 0xbf4d, 0xb04e, 0x1ca6, 0x44e8, 0x4136, 0x42a2, 0x3f2b, 0x42a7, 0x425f, 0x4240, 0x46d2, 0x416b, 0x0590, 0x4118, 0xbae6, 0xb058, 0xae7a, 0xbfab, 0x19a7, 0xba3a, 0x15dc, 0x0445, 0x1dbf, 0x14eb, 0xbfb9, 0x4159, 0x0825, 0x40e2, 0x43a5, 0xb2dc, 0xb2c5, 0x4382, 0x4389, 0xb0c7, 0x4196, 0x2221, 0x4133, 0xba0e, 0xbfb0, 0xbf49, 0x1baa, 0x1239, 0xb2b2, 0x4078, 0x1b33, 0xba6b, 0x3c77, 0x412e, 0x397b, 0x1e15, 0xbfd2, 0xa6f7, 0xb23f, 0x3196, 0x43bf, 0x40a7, 0xab47, 0x43a0, 0xb028, 0xba0f, 0x4557, 0xa960, 0xb05a, 0x4004, 0x41b9, 0x41be, 0xbad8, 0xbf17, 0x42fb, 0x467f, 0x42fd, 0xba23, 0x1a1f, 0xb291, 0x1c6a, 0x4190, 0x23a3, 0x448b, 0x4047, 0x4284, 0xb2ab, 0xb052, 0x429a, 0x4108, 0x1114, 0x4498, 0x401c, 0xb24b, 0x415b, 0xb2e8, 0xbfb0, 0x45d9, 0xbadf, 0xbfb2, 0xbff0, 0x41d4, 0xb09a, 0x431a, 0x405b, 0x1d7d, 0x1d8c, 0x4388, 0x060c, 0x1f0b, 0xba03, 0x4167, 0xb2af, 0xb2e8, 0x4095, 0x1b2a, 0x4142, 0xb038, 0x1e28, 0x2d5a, 0xb007, 0x37ae, 0x432f, 0x43f9, 0x41f5, 0xbf72, 0x4377, 0x0585, 0xba7a, 0x2893, 0x3546, 0x1b6b, 0x4315, 0x3494, 0x4000, 0x4305, 0x3758, 0xbf05, 0x4298, 0x43ca, 0x4299, 0x0b40, 0x433c, 0xba6f, 0x41e3, 0x29c3, 0xa073, 0x176f, 0x41fa, 0x465e, 0x28ab, 0xbf04, 0x40f8, 0x4347, 0x4357, 0x4269, 0x43f7, 0x42f1, 0x41b9, 0x4373, 0x41ac, 0x419f, 0xb03a, 0x0ff1, 0xba12, 0x469d, 0x233d, 0x440a, 0xbac6, 0x43ca, 0x2379, 0x1332, 0x43d0, 0xb2cc, 0x4281, 0xbf0e, 0xb24d, 0xbf90, 0xb291, 0x4043, 0x2e1b, 0x02b8, 0xbf74, 0xb2ea, 0x4399, 0x408e, 0x42ed, 0x456a, 0x414d, 0xba4d, 0x1929, 0x039e, 0xb22d, 0x38ee, 0x408e, 0x40b8, 0xb20f, 0x410a, 0x4553, 0x189f, 0x110e, 0x17e1, 0x414a, 0x1ec4, 0x4499, 0xba68, 0xb060, 0x42c3, 0x4369, 0xbf0f, 0x3aec, 0x4157, 0x3771, 0x46cc, 0xb230, 0x4169, 0xb054, 0x10b7, 0x14f3, 0x33ae, 0x40cc, 0x1f4e, 0xba61, 0x4356, 0x422e, 0x4047, 0x409d, 0x41b5, 0x4193, 0x2017, 0xb013, 0x4559, 0xb20d, 0xba6c, 0x41db, 0xba06, 0x4167, 0x4252, 0xbf09, 0x4360, 0x188d, 0xb239, 0x4385, 0x413a, 0xb2e8, 0x4303, 0x0c1d, 0xbff0, 0xb2ea, 0xb06b, 0xba28, 0xb217, 0x4330, 0xa300, 0x16ab, 0x1053, 0x0c0c, 0x414b, 0x43dd, 0x0a6d, 0x428d, 0x4378, 0x1b7a, 0xbf80, 0xb265, 0xa0df, 0x4079, 0xbfbb, 0x3808, 0x1e91, 0xa711, 0xbaf8, 0x4665, 0x2c9b, 0x03fb, 0x2a72, 0x4186, 0x4052, 0x43ff, 0x407b, 0xa0ed, 0x4207, 0xbf70, 0x43d7, 0x1367, 0xb2b4, 0xb207, 0xa2b7, 0x0b98, 0xba72, 0xbf44, 0xb05e, 0x41db, 0x4231, 0xba15, 0xba3d, 0xbfe0, 0x3b3c, 0x434f, 0x1a8a, 0x4373, 0x437e, 0x19db, 0x45f0, 0xb291, 0xbfa8, 0x40c4, 0xbada, 0xb2a6, 0x410c, 0x2f9d, 0x4499, 0xb254, 0xb251, 0x4688, 0x36c7, 0xb28b, 0x3ca1, 0x2de1, 0x401d, 0xb233, 0xba5a, 0xb03a, 0xb2c0, 0x429c, 0x43b8, 0x40e6, 0xbfdc, 0x415f, 0x40c8, 0xb295, 0x46d0, 0x449a, 0xa9e5, 0x17e0, 0x4127, 0x4165, 0x4213, 0x4324, 0x3d81, 0x413b, 0x41c3, 0x1fb5, 0x4041, 0x1dce, 0xb2aa, 0xbaee, 0x2b6b, 0xa9ff, 0xbfaa, 0x1e69, 0xb056, 0xba20, 0xbaf9, 0x3584, 0x1ce5, 0x4170, 0xbfd6, 0x4029, 0x3811, 0x43c3, 0xba36, 0x4136, 0x0fcd, 0xa452, 0xa18a, 0x42f8, 0xb236, 0x46d1, 0x180f, 0x42b5, 0x43ac, 0xb2c3, 0xba0f, 0xb004, 0x4638, 0xb2bb, 0xba5f, 0xba19, 0xbf11, 0x4640, 0x1f23, 0xba63, 0xb2d6, 0x4159, 0xbf6d, 0x4242, 0x3570, 0xb2b6, 0x410c, 0x4243, 0x4340, 0x4347, 0x23c7, 0xb257, 0x1e21, 0xb2b1, 0x402c, 0x429e, 0xb2fc, 0x3a4c, 0xbfa0, 0x41bc, 0x4659, 0x02de, 0x40c0, 0xb21e, 0xbfd8, 0x40fb, 0x37f9, 0x17e4, 0xb0ab, 0x4144, 0x4158, 0x4182, 0xaaae, 0x400a, 0x41ac, 0x40ff, 0x4207, 0x414f, 0x41e8, 0xbf09, 0xb2c8, 0xb0a2, 0xb229, 0x41a3, 0x4210, 0xb20a, 0x11b2, 0x2fdf, 0xb242, 0x34cc, 0x430a, 0x4462, 0xb24f, 0x42ed, 0x3e95, 0x4214, 0xa49d, 0x1a98, 0x42ae, 0x407f, 0x1c46, 0xb260, 0x46d2, 0xbf38, 0x45a2, 0x4105, 0xb25a, 0x406b, 0x0e70, 0x1c2e, 0xbf6c, 0x42c7, 0x1e6b, 0x40f0, 0x1edc, 0x418f, 0x1c63, 0x4403, 0xaeec, 0x1d5e, 0xb289, 0xb20d, 0x0fbd, 0x40f0, 0xb0a7, 0xba3d, 0x43e8, 0xbfa4, 0x12ff, 0x437b, 0x43dc, 0xba54, 0x416d, 0x42b7, 0xb2e8, 0x1d0a, 0xbf60, 0x1ac0, 0x1972, 0xb07d, 0xba21, 0xbf5f, 0x45b1, 0xa40b, 0x42b3, 0x4008, 0x20cf, 0x184a, 0x41fe, 0x2f74, 0x4129, 0xba6e, 0x43f7, 0x438e, 0x459a, 0xba5f, 0x1d12, 0x1c06, 0xba3b, 0x4338, 0x4319, 0x412e, 0x14df, 0xbac7, 0xbf06, 0x0b43, 0xa015, 0x1998, 0x1ae9, 0xb251, 0x1b33, 0x29d5, 0x1def, 0x4306, 0x4170, 0x4674, 0x4036, 0xb29a, 0x415e, 0x4367, 0xbf36, 0xba3a, 0x400f, 0x1aab, 0x41d9, 0x0d1c, 0x4598, 0x44b5, 0xa285, 0x406f, 0xbf07, 0xba2b, 0xba21, 0x41ae, 0x4488, 0xbf81, 0x24c3, 0xbf00, 0x080d, 0x3f1d, 0x45c8, 0x01cb, 0xa168, 0xbf88, 0x1d8a, 0x4469, 0x0868, 0x1fb2, 0xb28d, 0x40c5, 0xb23b, 0xba1b, 0x4075, 0x43fd, 0x30ab, 0xb2d8, 0x3f10, 0x1c49, 0xab69, 0xbf27, 0x1415, 0x0643, 0x46f1, 0x1c17, 0xbad5, 0x4405, 0xa383, 0xa1ed, 0x420c, 0x1866, 0x41e3, 0x43c0, 0xba28, 0x428f, 0xb08f, 0xba7b, 0x4576, 0x0987, 0x4266, 0x42a8, 0xb278, 0xba0b, 0xbfdb, 0x1ae6, 0x19bc, 0x191d, 0x4019, 0x4358, 0x2895, 0x10c4, 0x33fe, 0x1eb2, 0x41bd, 0x42fd, 0x4230, 0x1c37, 0x445d, 0x1970, 0x420a, 0x4325, 0x4179, 0x4053, 0x0bb2, 0x4373, 0x3cd8, 0x40c0, 0x4194, 0xad38, 0xbf9e, 0x144c, 0x215a, 0x4100, 0xb29f, 0xbfe8, 0x146d, 0x1a76, 0x37cd, 0x2bc4, 0xba24, 0x4359, 0x0417, 0xbfcd, 0x0ffa, 0x4100, 0xbac8, 0x41c8, 0x350d, 0xb2d9, 0x41b3, 0xbf5f, 0xba5a, 0x1f92, 0x442f, 0x4342, 0xb2cc, 0x42f7, 0x42ec, 0xbfa0, 0xa018, 0x3c0a, 0x4650, 0x464a, 0x1d7b, 0xbf11, 0xba23, 0x421c, 0x42ea, 0xb23c, 0xb2cf, 0x131a, 0xbf8f, 0x00af, 0xb29d, 0xba50, 0xbaee, 0x4607, 0x4645, 0xb2a8, 0xa65e, 0xb0ae, 0x42c2, 0x4153, 0xbfb5, 0x1f5c, 0x2a64, 0xb04d, 0xb221, 0x2bcf, 0x4102, 0x4115, 0x4301, 0x1a80, 0x38b0, 0x41da, 0xbaf9, 0x42a2, 0xba22, 0x42dc, 0xba39, 0x41c0, 0x46bb, 0x3fbd, 0x1f57, 0x412f, 0x4150, 0x4319, 0xbf01, 0x4162, 0x4211, 0xba0b, 0x2ec4, 0x1345, 0x4336, 0x4064, 0x4622, 0x4372, 0x422b, 0x43b3, 0x427e, 0xb0f6, 0xbf46, 0x333c, 0x43d0, 0xb2fd, 0x4219, 0xb06a, 0x114c, 0xb287, 0x44e3, 0x41a7, 0xb089, 0x422e, 0x4059, 0xb066, 0xabef, 0x190a, 0x4263, 0x1dd3, 0x4209, 0xbf67, 0x42ca, 0x409b, 0x4286, 0xba45, 0x42a3, 0xbae1, 0x2947, 0x414f, 0x43b7, 0x0fc6, 0x4350, 0xbfab, 0x40e6, 0x45b6, 0x1dab, 0x4132, 0x4289, 0xb060, 0xbf97, 0x4212, 0xba0b, 0x068c, 0x4390, 0x4181, 0x03ac, 0x406a, 0x4044, 0xaa37, 0xb247, 0x46ab, 0x41dc, 0xa6bd, 0xbf6d, 0x4007, 0xb2fe, 0x40ee, 0x1047, 0x41d3, 0xb29b, 0x428d, 0x4275, 0x4392, 0xb2da, 0x40fa, 0xb0e0, 0xbf4c, 0xb0b4, 0x05a7, 0x414a, 0x1c39, 0xb2e9, 0x4080, 0x430f, 0x3d9b, 0x1ef4, 0x4167, 0xbf19, 0xb01b, 0x42dc, 0xb04b, 0x44cd, 0xb284, 0xb046, 0x3574, 0xb2c5, 0x41b5, 0xae2e, 0xb216, 0x1fe5, 0x10e6, 0xb2b8, 0x18a6, 0x4600, 0x4050, 0x406f, 0x420a, 0x41da, 0x43c4, 0xbf14, 0x202f, 0x43ea, 0x41e7, 0x4044, 0x3472, 0xb2f3, 0x42f8, 0x1937, 0x1ffd, 0xbf07, 0x420a, 0x0351, 0xbad2, 0xba49, 0x43be, 0x41eb, 0xbf70, 0x40b7, 0x4479, 0x0ed9, 0x4261, 0xb066, 0xb22d, 0x1b22, 0x4225, 0x1edd, 0x4180, 0x4090, 0x409c, 0x46ab, 0x413c, 0x3858, 0x4127, 0x42c6, 0xbfb0, 0xa6c1, 0xbfd4, 0x46e0, 0x423f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xcb6e3399, 0x779d0a27, 0xb05aed8d, 0x68a1a047, 0x971f3c1b, 0xd982dff6, 0x911b2b51, 0x3af672e5, 0xc8b54557, 0xb853ba8e, 0x82d6c3bf, 0x67250d2b, 0x982aa4b0, 0x611a482f, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0xffffffa8, 0x845c5046, 0x00000000, 0x00006500, 0x7ba3afba, 0x000064fd, 0x00001ae0, 0x00000000, 0x982aa4b0, 0x00000000, 0x1b016935, 0x000064fd, 0x982aa4b0, 0xffd0f718, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1604, 0x0a21, 0x4319, 0x4564, 0xb217, 0x459a, 0x4428, 0xb20d, 0x1b2c, 0x2a6d, 0x403e, 0x410b, 0xba30, 0x1e1d, 0xbad8, 0x309a, 0xbf63, 0xbfa0, 0xb283, 0x1430, 0x468c, 0xb071, 0xb082, 0xb0c2, 0xbfa0, 0x1caf, 0xb278, 0x4270, 0x307d, 0xbacc, 0xb0d7, 0x40ec, 0xbacf, 0x33cb, 0xa1ac, 0x1b5a, 0xbf80, 0xb2e6, 0xba4b, 0x1e68, 0xbfe0, 0xb2dc, 0x4085, 0xbf7f, 0x2943, 0x1edf, 0x4346, 0x32fb, 0x410d, 0x44f4, 0x1e4f, 0xa16f, 0xb25d, 0x4348, 0xbf18, 0xbac6, 0x0b17, 0xbac6, 0xba0c, 0xb232, 0x3f9e, 0xb22f, 0xa529, 0xbfe0, 0x43b4, 0x2768, 0x427a, 0xb2fd, 0x408f, 0x4269, 0x4388, 0x42c0, 0x3a6c, 0xbf25, 0x4219, 0x4311, 0x1de4, 0x2564, 0x41c2, 0x1963, 0xb239, 0x42c3, 0x421f, 0xb0a4, 0x403f, 0x1c19, 0x1f37, 0x40f1, 0xbfc8, 0x40ac, 0x43aa, 0x0762, 0x4672, 0xb226, 0x3a12, 0x1aa7, 0xbad5, 0xb21a, 0x42ff, 0xbfdb, 0x4302, 0x1a52, 0x16ad, 0x42cd, 0x43fa, 0xb2f5, 0x413b, 0x411c, 0xb044, 0xb056, 0xa3cf, 0x40a9, 0x11c8, 0x455b, 0x41f6, 0xb04e, 0x3cea, 0x4257, 0x4251, 0x42e9, 0x2352, 0x3e76, 0x1aa5, 0x4086, 0x4355, 0x40e8, 0x3a84, 0x103c, 0xbfb5, 0x4227, 0xb07d, 0x1056, 0x4078, 0x4166, 0xba60, 0x1dd5, 0xbfdc, 0xb294, 0x4020, 0xa37a, 0x433d, 0xb2ab, 0xb284, 0x4223, 0xbf22, 0x43c2, 0x18f8, 0x4181, 0x40f5, 0xb2fb, 0x38ea, 0x4151, 0xac89, 0xbff0, 0x40a4, 0xa65b, 0x098e, 0x4020, 0x415c, 0x244e, 0x4034, 0x27e0, 0xbf94, 0xb2ec, 0xb0d0, 0x4255, 0xbf41, 0x22ee, 0x42c3, 0x139b, 0xa16a, 0xbfc0, 0x18b8, 0x4020, 0xb05e, 0xa1b8, 0xbad3, 0xbf2e, 0x460e, 0xbaf4, 0x23aa, 0x1c22, 0x34b3, 0xba2c, 0xa046, 0x223a, 0x4236, 0x1f81, 0x40e4, 0x43ea, 0x065e, 0x4251, 0x4266, 0x409b, 0x2271, 0x43be, 0x4026, 0x4394, 0x2c3b, 0x1dfa, 0xb287, 0xa1f2, 0xaed8, 0x4044, 0x42d9, 0xbf33, 0xb2db, 0xaaec, 0x1a26, 0xa731, 0x41be, 0x4203, 0xa536, 0x4663, 0x09c1, 0x4236, 0x1bcd, 0x234a, 0x3426, 0x420c, 0x43fa, 0x42e2, 0x1fcf, 0x43f1, 0x43ef, 0x4122, 0xab1c, 0x40cb, 0x41ac, 0x40ba, 0xb2f7, 0x46b0, 0xa3fb, 0xb037, 0x4583, 0xbf1e, 0x40a3, 0x4005, 0xafe0, 0xba7e, 0x42a9, 0x38ab, 0xb2b3, 0x43bd, 0x43c4, 0x1ca1, 0x4208, 0x410d, 0x4319, 0xba00, 0x43a2, 0xbae2, 0x194f, 0x316c, 0xba00, 0xbfc1, 0x41af, 0x40eb, 0x2d1e, 0x0f59, 0xba37, 0x40ec, 0x454f, 0x23a9, 0x0eb7, 0x4130, 0x410c, 0x4273, 0xba56, 0xa4e0, 0x42d8, 0x439b, 0xa5a8, 0x43e4, 0x41ab, 0x463e, 0x4097, 0x407f, 0xbf83, 0x4203, 0x0ecb, 0x41d5, 0x423b, 0x413e, 0x41aa, 0xbf5f, 0x14be, 0x40f8, 0xbada, 0xb294, 0x1a4d, 0x41c1, 0x120e, 0x424b, 0x1ac7, 0x42e8, 0x2300, 0x4386, 0x46f2, 0x41f1, 0x413f, 0x4078, 0x4193, 0x42b0, 0x07d0, 0x194c, 0x4647, 0x4216, 0x3906, 0x1e78, 0x4038, 0x045d, 0xb2be, 0x2603, 0x416d, 0xbf4b, 0xb26e, 0x403f, 0x45c3, 0x0349, 0xba7a, 0x4186, 0x4438, 0xb078, 0xbfe0, 0x4314, 0xb236, 0xb0fc, 0x4467, 0x420b, 0x407c, 0x43d2, 0x4252, 0xba6b, 0xbadc, 0xbf4c, 0x4021, 0xba6d, 0xb2c7, 0x1f82, 0xb07b, 0x1706, 0xbf1c, 0xbfd0, 0x1d27, 0x4176, 0xb284, 0x380c, 0x4642, 0x4607, 0xb0cc, 0x44d9, 0xb2a7, 0x40b5, 0x42c2, 0xb2d6, 0xba22, 0x1ae8, 0x28fa, 0x4030, 0x0012, 0x19af, 0x4545, 0x407f, 0x43fd, 0x4603, 0xbfd6, 0x41de, 0x1ede, 0xba4c, 0x41a1, 0x41c3, 0x4371, 0x408e, 0x41bc, 0x42cd, 0x42be, 0x4172, 0xab14, 0x4068, 0xa34b, 0x1bfc, 0x1929, 0xb2a6, 0xbf76, 0xb2eb, 0x410c, 0x1fa5, 0x4241, 0x469a, 0x1eec, 0x408b, 0xbf15, 0x4304, 0x464f, 0x1424, 0x431e, 0xbf66, 0xa1af, 0xa8a3, 0xba6d, 0xb2c3, 0xba52, 0x0e61, 0x40a3, 0x4348, 0xbaf0, 0x412c, 0x1aae, 0x42a3, 0x1da6, 0x4404, 0x424c, 0x336d, 0x4044, 0x40bc, 0xb2cb, 0x3f79, 0x416e, 0xbf5f, 0x2954, 0xba01, 0xba05, 0xaef8, 0x1e85, 0x4035, 0x41c5, 0xb062, 0x1500, 0x4008, 0xbf85, 0x0e11, 0x279e, 0x4125, 0x42c2, 0xbac4, 0x438a, 0x46f2, 0x0210, 0xba56, 0xba5b, 0x11f8, 0xba09, 0xbf5f, 0xb2bd, 0x1e57, 0x3541, 0xba26, 0x1e26, 0x10de, 0x20bb, 0xbf90, 0xbf3c, 0x4182, 0x43cc, 0x40cb, 0x4275, 0x4150, 0x4164, 0x41c0, 0x42d8, 0x40c8, 0x3c64, 0xa413, 0xa6e0, 0x18fa, 0xa514, 0x464f, 0x40f1, 0x43a0, 0x1131, 0x4067, 0x4017, 0x0294, 0xb2f3, 0x4159, 0xba10, 0x3e05, 0xbf8b, 0x042f, 0x431c, 0x40c7, 0xba5a, 0x1cfe, 0x40d2, 0x4178, 0xb2ad, 0x2175, 0x41df, 0x226c, 0x426e, 0xb0a0, 0x4081, 0x42ec, 0x4035, 0x40b2, 0xbfd7, 0x4165, 0x4419, 0x43cd, 0x46ba, 0xbfc0, 0x4384, 0x0cc7, 0x1e7a, 0x466a, 0x04ed, 0x0fdc, 0x42e2, 0x42a3, 0x2874, 0x42df, 0xb2e6, 0x223a, 0x3d2a, 0x349c, 0x41b0, 0x1fb5, 0x2ef3, 0xbfa3, 0x14e3, 0xbac0, 0x4357, 0xb2a5, 0xba2a, 0x4161, 0x4218, 0xb0b3, 0x4110, 0x15d7, 0x412e, 0xbfaf, 0xb2bb, 0x1930, 0xba71, 0x1dc0, 0xbf53, 0x4363, 0x4237, 0x206b, 0x1a83, 0x1f94, 0x41fb, 0x4071, 0x4632, 0x41ca, 0xb000, 0x2298, 0x4191, 0xa626, 0xb215, 0x24be, 0x40b3, 0x02a0, 0xb209, 0x2ff4, 0x0d5d, 0x43ef, 0x21e6, 0x1915, 0x401f, 0xbf84, 0x461f, 0x4244, 0x421c, 0xba26, 0x43f2, 0xba11, 0x42b4, 0x17d4, 0x43e7, 0x072e, 0x41a1, 0xaee1, 0x4090, 0xb20e, 0x40e9, 0xb207, 0x1dd0, 0x1cc6, 0xbfb1, 0xb045, 0x2731, 0x4271, 0xbade, 0xb217, 0x403f, 0x35ea, 0x1eea, 0xaa77, 0x2632, 0x19c7, 0xba1a, 0x433f, 0x439e, 0xbf15, 0x1a25, 0x40b9, 0x14f0, 0x3dd4, 0x3c2a, 0x3be5, 0x43c9, 0x40e1, 0x435d, 0x41eb, 0xb236, 0x46ec, 0x1701, 0x403b, 0xbaee, 0xb030, 0x43d4, 0xa1a0, 0x0433, 0xb0ed, 0xbaee, 0xbf47, 0xb25d, 0x410c, 0x41e2, 0x1d4a, 0x1fa0, 0x3d98, 0x1f8f, 0x3a41, 0x41c6, 0x456d, 0x04a1, 0xb2ca, 0x1d5e, 0x40ca, 0xa917, 0x40d1, 0xa2c5, 0x428f, 0x225d, 0x11fe, 0x42d3, 0xbfd9, 0xb2d2, 0x1ca9, 0xafda, 0xbf80, 0xb252, 0x2a1a, 0xbaf3, 0x430a, 0xb244, 0x1c06, 0xa3c7, 0x1c7b, 0x407f, 0xba7a, 0xb2c9, 0x43af, 0x4266, 0xba74, 0xbfbd, 0x1822, 0xba00, 0xb218, 0xb23a, 0x42da, 0x1ec2, 0x412b, 0xb257, 0x2422, 0xbf68, 0xbac7, 0x43fa, 0x1072, 0x4102, 0xb2f7, 0xba43, 0x4303, 0xbac6, 0xb296, 0x1cd6, 0x4379, 0x180e, 0x3654, 0x432f, 0xbf80, 0x228d, 0x03ca, 0x43d0, 0x43b1, 0x2ddd, 0x41fa, 0x35b8, 0x35fe, 0x4212, 0x3dc5, 0xbf59, 0x446f, 0xba34, 0xb234, 0x45c0, 0x461c, 0x1c69, 0xa91c, 0xbfab, 0x43e0, 0x098b, 0x4052, 0x4389, 0xb2b5, 0x1ed5, 0x4129, 0x0b1b, 0x2abf, 0x1b81, 0x32cd, 0x431f, 0xbf60, 0x1adb, 0x42fb, 0x40f0, 0x43d0, 0xb272, 0x4041, 0x421b, 0x43f7, 0xbf2c, 0x46a2, 0x43d8, 0x43bd, 0x439c, 0xba01, 0x3e66, 0x19dc, 0xbf28, 0xb04a, 0x4286, 0x429d, 0x25ec, 0x4381, 0xba19, 0xb2f8, 0x4042, 0x1f44, 0x1ea0, 0x42dc, 0x438d, 0x40f1, 0x1f2f, 0xba50, 0x4311, 0xa700, 0xb0ed, 0x1c9b, 0xb0c5, 0x2742, 0x431c, 0xbf1e, 0x409e, 0xaf81, 0xba68, 0x4364, 0x4078, 0x0ac1, 0xab14, 0xbaec, 0x4499, 0x0a77, 0x0594, 0x2e04, 0xba67, 0x2c60, 0x41c8, 0x443e, 0x4273, 0xb00f, 0x41b1, 0x43b1, 0x1f25, 0xbf34, 0x4282, 0xb2fc, 0xbf44, 0x400d, 0xb275, 0xaf07, 0xb06d, 0x44db, 0x12f2, 0x4082, 0x0b28, 0xb0ac, 0xbfc0, 0xb2ca, 0xb2cb, 0x4462, 0x45b6, 0x4328, 0xb24a, 0x2a77, 0x425e, 0xbf78, 0xb24c, 0x43f5, 0x17b8, 0x2ab7, 0xb285, 0x40b9, 0xa7d7, 0x45e4, 0xb0e1, 0x4395, 0x4020, 0x2b72, 0x4200, 0xb069, 0x40db, 0x41af, 0x42dc, 0x41c6, 0x16a0, 0xb268, 0xbf68, 0xb0e3, 0x421b, 0x4208, 0xa3e3, 0x3cd0, 0xbf54, 0x3d55, 0xba7c, 0x4399, 0xb213, 0x43d0, 0x160a, 0xbfe4, 0x4659, 0x4327, 0x107b, 0xb2f7, 0x42ea, 0xbf00, 0x43d4, 0x2fa0, 0x41ad, 0x195b, 0xb2c5, 0x05dc, 0x43c7, 0x4074, 0x426e, 0xb084, 0x4354, 0x1993, 0xbf44, 0x4226, 0x118b, 0x0a67, 0xbaca, 0x43ea, 0x2159, 0x416e, 0x190e, 0x0629, 0xbf68, 0x4155, 0x41f9, 0x2408, 0xbf2c, 0x1a7a, 0x4137, 0x17b6, 0x4092, 0xb039, 0x404c, 0x4324, 0x40de, 0x40df, 0xbf35, 0x400e, 0x1853, 0x4246, 0x3887, 0xb2b8, 0x44b3, 0x0cc0, 0x294e, 0x0158, 0xb24b, 0xba1c, 0x1f56, 0x0aff, 0xb298, 0xbf69, 0x32c4, 0x44ad, 0x1804, 0x4189, 0x40ff, 0x1993, 0xada4, 0x1f03, 0xbaec, 0x43ed, 0x40e9, 0x1957, 0x40af, 0x46dc, 0xad97, 0x4368, 0x4271, 0x4156, 0xbf52, 0x41ba, 0x4348, 0x4550, 0x0bf9, 0x4039, 0x4692, 0x2bed, 0x40d5, 0xba49, 0x0c78, 0x1ba4, 0xba44, 0x0087, 0x4045, 0x42fe, 0xb299, 0xba7b, 0xbf86, 0x46e9, 0x1a43, 0x0677, 0x3bf7, 0x445e, 0x1c2d, 0x4208, 0x43e0, 0x18ff, 0x1077, 0x2711, 0x425a, 0xba76, 0xb04f, 0xb2a9, 0x1a42, 0x449a, 0xb2e8, 0x4019, 0x1195, 0x42c2, 0xb208, 0x449c, 0xbacc, 0xba2f, 0xbf90, 0xbf44, 0xbacb, 0xbaeb, 0x1d0c, 0x4119, 0xb2e4, 0xb209, 0x45c3, 0x4580, 0xba39, 0x41f0, 0xbf7b, 0xb239, 0x42ed, 0x412f, 0x3276, 0xb0e3, 0xb285, 0x146e, 0xb064, 0xa6c5, 0xbf12, 0x4037, 0xbaf9, 0xb2ba, 0x4345, 0x156c, 0x404b, 0x407a, 0x1c20, 0x44f2, 0xba73, 0x404e, 0x4338, 0x2e25, 0xba5e, 0x1b5b, 0xb2f5, 0x4247, 0xbf5a, 0xa67b, 0xb257, 0x1c8e, 0x2226, 0x43d4, 0xb045, 0x4197, 0x41f6, 0x08df, 0xbf5c, 0x42b0, 0x4170, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x51ade2cc, 0xec524c8c, 0x38eff947, 0x36d7c854, 0xe5629cca, 0xa8d08a62, 0x7a884da5, 0xfbdda384, 0xdb38fbe9, 0xf6976dfd, 0x005fd422, 0x93c6813c, 0xb8f2dcc9, 0x6cfd2716, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0x40ffffff, 0xffffffff, 0x00000026, 0xff767516, 0xffffffd9, 0x000000b4, 0x80000000, 0x1feecea2, 0x6cfd1de1, 0xf75b1a7b, 0xffffff09, 0x278d022c, 0x278d0135, 0x6cfd2fc6, 0x00000000, 0x900001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xa6c2, 0x4488, 0xb2d1, 0xb06a, 0x4083, 0x44ca, 0xba42, 0x1d14, 0x420d, 0xbade, 0x4103, 0x40f9, 0x4003, 0xb0d9, 0x4552, 0xbfcc, 0xa2db, 0xa663, 0xba1e, 0x2e6c, 0x43d1, 0x407f, 0x401a, 0xb2c1, 0x4631, 0x0681, 0xa7bb, 0x4649, 0xb05b, 0xbfe1, 0x28a0, 0x1af0, 0x16fa, 0x45c8, 0xab5a, 0x4399, 0x4423, 0x1bdd, 0xb044, 0x2499, 0x4440, 0x3ee0, 0xb02f, 0x0ddd, 0x4127, 0xbf08, 0xba20, 0xba1c, 0x03ac, 0xb0f7, 0x40e5, 0x4116, 0x4116, 0x468a, 0x422d, 0xb2d9, 0x43c9, 0x41ea, 0x4054, 0xbace, 0x437c, 0xbf91, 0x40a4, 0xb247, 0x18af, 0x16e1, 0xb255, 0xbade, 0xb0a3, 0x1f17, 0xbf47, 0xb0be, 0x1037, 0xb2b8, 0x175f, 0x18fc, 0xb286, 0x41bd, 0x46f3, 0x42b4, 0x0b47, 0x4359, 0x18c2, 0xadd5, 0xbf2c, 0x1c76, 0xba76, 0x4132, 0xba48, 0x43f4, 0x168c, 0x416e, 0xbfab, 0x4194, 0x1bc0, 0x28da, 0x42c5, 0x4423, 0x41ff, 0xb2ff, 0xb008, 0x20d3, 0x437e, 0x4034, 0xba40, 0x425e, 0xa06c, 0xba68, 0xba72, 0x00eb, 0x41b2, 0x411e, 0x1b3d, 0x2f08, 0xb089, 0x411a, 0x14ff, 0xb2be, 0x4633, 0xbf7d, 0x4150, 0x40c2, 0xb28a, 0x1e72, 0xba3d, 0x0aae, 0x435e, 0x205c, 0xb067, 0x4353, 0x21c0, 0x43e1, 0x4141, 0x0572, 0x43ae, 0x427c, 0x42ae, 0x4026, 0x2df2, 0x405c, 0x37ec, 0x4074, 0x1559, 0xb2e7, 0xbfe0, 0xb2b4, 0x41cb, 0x0d6c, 0x433a, 0xbf64, 0x1d3e, 0xb2f3, 0xba2e, 0x4020, 0xba4f, 0xb2b6, 0x43ff, 0xa82c, 0x1929, 0x421e, 0x18af, 0x4415, 0xbf33, 0x4698, 0x1b4a, 0x27e0, 0x40f2, 0xb25e, 0xba7b, 0xb26a, 0x4309, 0x4064, 0x402b, 0x4098, 0xb245, 0x43bd, 0x0c9f, 0x42de, 0x015f, 0x4321, 0x1a23, 0xb28e, 0xba29, 0x406b, 0x1998, 0x4265, 0xbf38, 0x4314, 0xb2b6, 0x165c, 0xbafc, 0xbf70, 0xb048, 0xba43, 0x46bb, 0x4001, 0x0cb8, 0x43fb, 0x270f, 0xb2fc, 0x0ae9, 0x4223, 0x423f, 0x26fd, 0x419e, 0xa390, 0xb0ff, 0xb2cf, 0x0bc0, 0xbf4b, 0x4135, 0x4559, 0xb2bf, 0xba2e, 0xa3e9, 0x4310, 0xba44, 0x43ff, 0x4394, 0x401c, 0x41f6, 0x42d0, 0xbf84, 0xa7d9, 0x42c0, 0x0a4b, 0x4285, 0x42a0, 0xb040, 0x1ba8, 0xa9fa, 0xba04, 0x358a, 0x4003, 0x46d4, 0x41fe, 0xba48, 0x2c12, 0x41a3, 0x4161, 0x4372, 0x448b, 0xbf98, 0x0627, 0xb23c, 0x4170, 0x0a61, 0xb279, 0x4260, 0x06b6, 0x3f4f, 0x4319, 0x40a8, 0x24ea, 0xbfe8, 0x412c, 0x3af2, 0x415d, 0x42f8, 0x27fc, 0xb04c, 0x2615, 0x432f, 0x447e, 0xb0b6, 0xb0e8, 0xbf15, 0x4034, 0xbadf, 0xba1e, 0x43d6, 0x00f1, 0x2101, 0xb229, 0x270d, 0x0d8f, 0x1c5a, 0x3665, 0x4364, 0xbad0, 0x435f, 0x1ae3, 0x20f4, 0x42f6, 0x4606, 0x38db, 0x1391, 0x42ee, 0x4550, 0xb233, 0xa680, 0x4310, 0x4005, 0x41a2, 0x42dc, 0x43e8, 0xbf0e, 0xb2ad, 0xa2f4, 0x1508, 0xae86, 0x4320, 0xbf55, 0x2d19, 0x43ef, 0x42db, 0xb0a9, 0x3556, 0xb01e, 0x4176, 0x43b8, 0x4462, 0x077f, 0xba0b, 0x42d6, 0xb2c2, 0x4336, 0x4384, 0xb2bf, 0x4002, 0x4262, 0x1f32, 0x46db, 0x4239, 0xb200, 0x1f3c, 0x4167, 0xb23b, 0xb0d4, 0x43cd, 0x0623, 0xbfb5, 0x2020, 0x1c56, 0x1cf6, 0x433f, 0x43b3, 0x429e, 0x41e3, 0xb2aa, 0x43fd, 0x1cac, 0x3d18, 0x4077, 0x411f, 0x4058, 0x43dd, 0xb244, 0x41ee, 0x4268, 0x2fa0, 0x243a, 0xb00a, 0xb2e4, 0x3b24, 0xa3df, 0xbfa6, 0x42c4, 0xb072, 0x1cbb, 0x4090, 0xbf9f, 0xba2a, 0x3835, 0xb27c, 0x4217, 0x4283, 0xb232, 0xbf1c, 0x43c9, 0xb2a2, 0xb2a3, 0x4194, 0xb25c, 0xbf92, 0x4085, 0x4278, 0x4073, 0x40b4, 0x423c, 0x1d1a, 0xbac1, 0x43b0, 0x4330, 0x1c88, 0xb210, 0x2e6a, 0x4018, 0x41ac, 0x0ac5, 0xb2ea, 0x4569, 0x141c, 0xbf8b, 0xbf70, 0x42ab, 0x4031, 0xbacb, 0x41ec, 0x4019, 0x3670, 0x2cf6, 0x401f, 0x26a1, 0x41eb, 0xb23f, 0xb221, 0x4671, 0xb2bb, 0xbf60, 0x363e, 0x420b, 0xab50, 0xbf77, 0x1e0f, 0xae4e, 0xb025, 0xba19, 0x1f73, 0x37a5, 0x1c37, 0xbf90, 0xbf93, 0xb058, 0x1c46, 0x1c5d, 0x19a0, 0x0b41, 0xbfa0, 0xb2ae, 0x1f46, 0xb237, 0x14c9, 0x405c, 0x2f26, 0x46e8, 0x41a5, 0x46b0, 0xb22d, 0xb03f, 0x1fc2, 0x3b4f, 0xbfa6, 0x1faa, 0x432c, 0x19ea, 0x40f5, 0x4246, 0x1f94, 0x4385, 0xa1df, 0x38f5, 0x049a, 0x455f, 0xa132, 0x08ee, 0x1c37, 0xbfa0, 0x1e5e, 0x401a, 0x43ac, 0x402c, 0x4222, 0x3931, 0x2124, 0x44aa, 0x42a8, 0xb0f0, 0xa8c8, 0x1e5e, 0xa92c, 0xbf65, 0x4045, 0xbaeb, 0xba57, 0x44ad, 0xac8a, 0x420b, 0x353f, 0xb23e, 0xa0ff, 0x1872, 0x41cb, 0xb22e, 0xb0cc, 0x4213, 0x40e3, 0x0aef, 0x411c, 0xb0bc, 0x1d52, 0xa778, 0xb0d0, 0xbf56, 0x43d1, 0x42f4, 0x1d5b, 0x1dc2, 0x0f45, 0x4381, 0xb214, 0xb09f, 0x4175, 0x4680, 0x24ea, 0xb231, 0xb264, 0xa2ed, 0xbace, 0x181b, 0xbf08, 0x42a5, 0xa7c2, 0x4018, 0xbfbf, 0xb2b2, 0x4309, 0x4264, 0x02c3, 0xa5f0, 0x1d34, 0xb209, 0x4090, 0x3005, 0x431d, 0xb223, 0x434e, 0x0cec, 0x0970, 0x43d2, 0x402e, 0x403d, 0x1385, 0x1eac, 0x1fe2, 0x427b, 0x0159, 0x0702, 0x0390, 0xbf6b, 0x40e8, 0x403d, 0xa17c, 0x2fb8, 0x41ec, 0x4112, 0xba4c, 0x2c3a, 0xaecc, 0x458a, 0x435a, 0x1950, 0x429d, 0x1d8e, 0xaf4e, 0x36e9, 0xb205, 0xb0d3, 0x4394, 0x4489, 0x425e, 0x01cf, 0xbae3, 0x4131, 0xbae9, 0x4001, 0x0f9b, 0x431c, 0xbf91, 0xabc7, 0x40b6, 0xba5c, 0xa210, 0xbaf8, 0x0f46, 0x43d3, 0x3404, 0xaaea, 0x430e, 0x4248, 0x1d7e, 0x42e1, 0x41ff, 0x011a, 0xb230, 0x4037, 0x0e79, 0x3a4a, 0xb0d9, 0xbf97, 0xb21f, 0xafa8, 0xa731, 0x4185, 0x1b7d, 0xb095, 0x4156, 0xb2cb, 0x14d5, 0x41e0, 0x40b7, 0x31e8, 0x42ee, 0x2060, 0x3a39, 0x419b, 0x40a7, 0x1e06, 0x39bc, 0x1930, 0x4238, 0x412b, 0x43e6, 0xbfcb, 0xbaf1, 0x3518, 0x27a0, 0x42d2, 0xb2a4, 0xbfdc, 0x4293, 0x4337, 0xae16, 0x4429, 0x4050, 0xb079, 0x4031, 0x1638, 0x420b, 0x1f17, 0x43a4, 0x1bf4, 0x433b, 0x178f, 0x4060, 0xbf7b, 0x4339, 0x43d1, 0x40c6, 0xbaea, 0x154c, 0x4247, 0x412a, 0x41a0, 0x41d1, 0x4260, 0xbacf, 0x20af, 0x4285, 0x126a, 0xbf44, 0xbfb0, 0x4425, 0xb22f, 0x1a6f, 0x427c, 0x4196, 0x4367, 0x4213, 0x1620, 0xbf7b, 0x4565, 0x32f9, 0x40b2, 0x395c, 0x4105, 0xbf70, 0xbfb5, 0x412d, 0x409e, 0x180e, 0x1a80, 0x4136, 0x27e3, 0xbae3, 0x1c3a, 0xb213, 0x1301, 0xb2fb, 0xb098, 0xbfd0, 0x324c, 0x2876, 0xab33, 0x1c23, 0x248e, 0xb256, 0x1dd9, 0x0d3e, 0x01e8, 0x403b, 0x40fa, 0x41a4, 0x41f6, 0xbf69, 0x4371, 0x40dd, 0x46a1, 0x402c, 0x41e8, 0xba49, 0x40d7, 0x4605, 0x1aa1, 0x37d2, 0xba62, 0xbf61, 0xb28b, 0x4136, 0x36ee, 0x40fc, 0x3ead, 0x412c, 0x43aa, 0x233d, 0x3ac3, 0x421a, 0x4117, 0x091d, 0xb2c9, 0x163a, 0x408b, 0x4273, 0x141e, 0xa5b2, 0x40da, 0x4082, 0xbfd8, 0x4080, 0x4347, 0x1830, 0x414c, 0xbafe, 0xbf07, 0x21d3, 0x435b, 0x415c, 0xbf00, 0xb285, 0x0453, 0x1b3f, 0x383a, 0x40db, 0x36f2, 0xba4d, 0x4128, 0xbf72, 0x428f, 0xa6c7, 0x19c1, 0x2878, 0x1f51, 0xba30, 0xbae9, 0x4346, 0x43c2, 0x0c82, 0x1d23, 0x466b, 0x40e0, 0xba10, 0xac8c, 0x1ff2, 0x1adf, 0x4356, 0x401c, 0xbaf8, 0xac9d, 0xb000, 0x41e9, 0xb297, 0x4155, 0xbfdb, 0xb04e, 0xb281, 0x43a2, 0x428c, 0xb2d8, 0x42bb, 0x2cfb, 0xb2de, 0x1fc8, 0x4301, 0x45f2, 0x1ee3, 0x04c6, 0xba61, 0x4002, 0x0798, 0xb29a, 0x433d, 0x1e61, 0x42fb, 0xbf56, 0x411f, 0xb0ed, 0xbf90, 0x456d, 0x1437, 0x413c, 0x4126, 0x42ca, 0xbad4, 0x1917, 0x4350, 0xbae2, 0x4135, 0x0247, 0x418b, 0x151d, 0x173d, 0x4054, 0xb284, 0xb22f, 0x4296, 0xb28f, 0x2e6c, 0xb2c0, 0x1f1c, 0xbfa9, 0x0906, 0xba7e, 0x1b04, 0xba58, 0xbaee, 0x4000, 0xb200, 0xb0d5, 0x414f, 0x14ff, 0xb0a0, 0x4220, 0x4249, 0xba6a, 0x4187, 0xbf53, 0x43bd, 0x1de3, 0x467d, 0x3a73, 0x18c3, 0x4263, 0x42cd, 0x42ec, 0x44b5, 0x089b, 0x1ce1, 0x18d2, 0x41d3, 0x3636, 0x1de6, 0xba4c, 0xb08d, 0x1b88, 0x1d9e, 0x3a85, 0xbfd3, 0xb051, 0xb2cd, 0x1c2b, 0xb07a, 0xb261, 0x07f4, 0x3ffa, 0xb0a1, 0x42a9, 0x40f3, 0xba2c, 0x40d5, 0xb026, 0x08bd, 0x415d, 0x1e67, 0x4377, 0x43c9, 0x1d0c, 0xb278, 0x437e, 0x4159, 0x456c, 0xbf90, 0x4191, 0x428e, 0xbfa9, 0x2e5f, 0x43af, 0x41d7, 0xb259, 0x42e0, 0x4221, 0xb2d4, 0xb07e, 0x4186, 0xbf80, 0x40a3, 0x45bd, 0x416a, 0x4324, 0x4068, 0x3029, 0x43dc, 0x41d1, 0xa79d, 0xbf1d, 0x2443, 0xba2b, 0x4111, 0x1a70, 0xa102, 0x43a3, 0xb0c8, 0xba62, 0x41b6, 0x41b6, 0x09a5, 0x41a6, 0x427a, 0x3e4d, 0x419e, 0x062f, 0xb24e, 0x1110, 0x41c9, 0x41ad, 0x2a40, 0xbf6b, 0x4279, 0x4347, 0xb242, 0xb2b0, 0x097c, 0x460e, 0x1e67, 0x4144, 0xa11e, 0x1ee4, 0x351d, 0xb247, 0x43f5, 0x1a26, 0x4357, 0x41fa, 0x40cb, 0x41ac, 0xba63, 0x1d28, 0xba76, 0x28e1, 0x38e7, 0x4098, 0x42b3, 0xbfbd, 0xb2bc, 0xbadc, 0xbad0, 0xba1a, 0x40fd, 0x42ca, 0x40ad, 0x42d4, 0xb251, 0xb2f2, 0xb2fd, 0x4138, 0x1b82, 0xba23, 0x1efb, 0x11d5, 0x413c, 0xb273, 0x1ce6, 0xa2a1, 0xbf79, 0x4197, 0x43e3, 0xbae9, 0x4015, 0x43e9, 0xa21c, 0xb0d0, 0x4059, 0x408f, 0x4067, 0x4387, 0xb2f2, 0x461b, 0x4019, 0x40a4, 0x4619, 0x41f1, 0xb04c, 0x46ac, 0x43d7, 0xa438, 0x4108, 0x0e03, 0xba59, 0x41e4, 0x43db, 0xb2e0, 0xb2b8, 0x1f88, 0xbf55, 0x432d, 0xb223, 0x09e7, 0xaea6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x76fb2e83, 0x2c9a97ce, 0x4afe2dc9, 0x2a8a6d29, 0xf34a1d2a, 0xe92c22a4, 0xcd859d8b, 0xab9291ff, 0xdca83fbb, 0x24a63f86, 0xf4d326a0, 0xc55ab8ca, 0x50c313fb, 0x47bdd172, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0xfffffffa, 0x00000000, 0x00000003, 0x0000018a, 0x4000018a, 0xff91f802, 0x47bdd252, 0xfffffffc, 0x000017c0, 0x24a35e06, 0x20022b04, 0x47bdd732, 0xff91f802, 0x47bdcfba, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb247, 0xbac6, 0xb2bc, 0xba00, 0x4236, 0x4542, 0xbf84, 0x4353, 0x454b, 0x2d7d, 0xb214, 0xbf73, 0x2346, 0x1da1, 0xa600, 0x41de, 0xb0cf, 0x45c5, 0xb057, 0xbf11, 0x43f5, 0x4171, 0x026e, 0x2a0c, 0xafe4, 0x4215, 0xba19, 0x3837, 0x4677, 0x428a, 0xb281, 0xb2e1, 0x43af, 0x427f, 0xbf45, 0x41ef, 0xb289, 0x2c54, 0x4128, 0x4076, 0xb2d4, 0x41c2, 0x191b, 0x438c, 0xa81f, 0xb075, 0xb2e1, 0xbfb9, 0x4396, 0x2ac3, 0x4052, 0x4453, 0xb2c7, 0x40b6, 0x4273, 0x40b6, 0x424e, 0x449d, 0x0849, 0x42c1, 0xa3b4, 0x2413, 0x1e42, 0x40c1, 0x41f8, 0x1e05, 0xbf6e, 0xbae1, 0x1e62, 0x445c, 0x3418, 0x4363, 0x4094, 0x445c, 0x4283, 0x4611, 0xa556, 0x42fb, 0x1f16, 0x43f5, 0x1a7a, 0x020f, 0xba79, 0xbf24, 0x44cb, 0x0902, 0x4376, 0x19ed, 0x4181, 0x41b4, 0xb222, 0x4391, 0xbfcd, 0x05b5, 0x1fa3, 0x422c, 0xb210, 0x18d5, 0xbf70, 0x40c1, 0x42b9, 0x107f, 0xb281, 0xa90c, 0xbfba, 0x4301, 0x4282, 0xb254, 0x2d6a, 0x42d5, 0x42c1, 0x3ff4, 0xb09d, 0x0856, 0x41a4, 0x4357, 0x42d8, 0xbfd9, 0x3dcf, 0xba42, 0x206a, 0x4168, 0x1f5e, 0x460a, 0x0f10, 0xa577, 0x456c, 0x3967, 0x4643, 0xbf4e, 0x4183, 0x36fb, 0x41ea, 0xb0dc, 0x32d3, 0x43ee, 0x31b0, 0x4030, 0x407c, 0xb22d, 0x4455, 0x3342, 0xb0d7, 0xbf83, 0x1c18, 0x35e6, 0xb063, 0x4315, 0x45ec, 0x1f25, 0x18e9, 0xb264, 0xa7c2, 0x3e41, 0x43b8, 0x3363, 0x0c12, 0x0d27, 0x3e41, 0xbfb0, 0xba45, 0xa29a, 0xabaa, 0x3d44, 0xb2f5, 0xa599, 0x3ed6, 0x423f, 0x45da, 0x28ca, 0x40aa, 0x4288, 0x2acc, 0xbfc6, 0x21e5, 0x41a9, 0x4140, 0x4236, 0x0189, 0xb21a, 0x197d, 0xbf93, 0x13b7, 0x44a5, 0x3404, 0x443b, 0x421b, 0xba46, 0xba41, 0xb2d1, 0x022f, 0xb276, 0xba18, 0xb26a, 0xb20e, 0xb294, 0x42c5, 0x42f5, 0x1b96, 0x11dc, 0x1c7e, 0xbfb5, 0x46db, 0xab1b, 0x416e, 0xb0a2, 0xb2ab, 0x43dc, 0x3076, 0xba40, 0x2e15, 0xba09, 0xb2df, 0x4100, 0x4139, 0x004d, 0x40fe, 0xbf38, 0xa914, 0x42b4, 0x43cb, 0x43e8, 0x407c, 0x21cf, 0x2d7f, 0xb2e4, 0xb26f, 0xb2e2, 0x18d5, 0xb03e, 0x45a8, 0x433a, 0xb235, 0x44b4, 0xb000, 0x43ea, 0x313a, 0x4641, 0xb268, 0x3771, 0x40e5, 0xbff0, 0xbf9c, 0x4051, 0xba56, 0x07db, 0xb2b3, 0xb0e7, 0x40ed, 0xb264, 0x4193, 0xb08b, 0x422e, 0x4079, 0x26bc, 0x42e8, 0xba16, 0xa565, 0xba1c, 0xb06c, 0x409b, 0x43f5, 0x41fb, 0x42fa, 0xb2ea, 0x4184, 0x23d2, 0x431b, 0x4125, 0x1ef6, 0xbfa3, 0xaecf, 0x44b8, 0xb2a2, 0x1b75, 0x4145, 0x46f4, 0x42d4, 0x11f0, 0xb255, 0x411d, 0x121a, 0x42d1, 0x1a78, 0xbfae, 0x1d7b, 0xb0e1, 0x40e4, 0x422b, 0x428a, 0xba68, 0x18d6, 0x2f33, 0x4093, 0x433d, 0xbf32, 0x45c9, 0x4227, 0x423b, 0x41a1, 0x0f2d, 0x2021, 0x4679, 0x1f9b, 0x418c, 0x436b, 0xbf87, 0x41e4, 0x41bd, 0x407b, 0xa9d5, 0x1e4b, 0x00d3, 0x4389, 0x42b6, 0x46bd, 0x401e, 0x4067, 0x41d4, 0x4341, 0x4146, 0x2898, 0x463d, 0x1ece, 0x324f, 0xbaed, 0x4186, 0xbf31, 0x1dc1, 0xb275, 0x3cd7, 0xbf00, 0x4162, 0x4202, 0x431b, 0xba19, 0xb281, 0x40f0, 0xb2d9, 0x1ebc, 0xb2ae, 0x40ec, 0x42c1, 0xb2b9, 0x4241, 0x1eec, 0x081f, 0x4225, 0x3497, 0x4371, 0xac55, 0xad9c, 0xbf7e, 0xa740, 0x1a53, 0xb2ee, 0x0406, 0x403a, 0x40de, 0x1ea4, 0x4252, 0xa544, 0x41b5, 0xba14, 0x4225, 0x42c2, 0x430d, 0xbf43, 0x40db, 0xa456, 0x1b59, 0x4347, 0xb2b6, 0xbff0, 0xbae4, 0x4363, 0xb200, 0x40dd, 0xba74, 0x420a, 0xbf2e, 0xa9e7, 0x3c0c, 0x4285, 0x13d7, 0xb28b, 0xb05e, 0x1826, 0x42bd, 0x2d51, 0x4242, 0x4227, 0xbfd3, 0x43fb, 0x41ce, 0x4171, 0x416e, 0x4313, 0xa531, 0x4302, 0x010c, 0x201b, 0x2549, 0x45b4, 0x1366, 0x41a2, 0xba6f, 0xb2f8, 0x4049, 0x1e75, 0x4471, 0x45be, 0x1814, 0x4430, 0x415e, 0xbf4f, 0x187a, 0x418d, 0x2a18, 0xb278, 0x4002, 0x2b61, 0xbf6f, 0x06cc, 0x4265, 0x41fb, 0x408d, 0xbf56, 0x3963, 0x1e83, 0x45c0, 0xa06b, 0xa6e9, 0xb252, 0x1e7d, 0xb000, 0x4024, 0xb0ab, 0x4394, 0x418b, 0x1873, 0xaeb9, 0xbfaf, 0x4331, 0x1e39, 0x4405, 0x1ccb, 0x274d, 0x4344, 0xb0be, 0x41ca, 0x4268, 0x431f, 0x44c9, 0x41bf, 0x40d0, 0x42f3, 0xb09a, 0x1c08, 0x2223, 0xbf24, 0x0599, 0x46a3, 0x4207, 0x4114, 0xbf48, 0x1e64, 0xb0fe, 0x2c19, 0x4074, 0xab7a, 0x41d7, 0x4262, 0x46b0, 0x15eb, 0x42ae, 0xb23b, 0x35c8, 0x4567, 0x4177, 0x42a7, 0x4219, 0xb018, 0xb231, 0x42f7, 0xa4cc, 0x4414, 0x2023, 0x1c10, 0xbf3d, 0x1242, 0x4258, 0xb046, 0x4159, 0xba45, 0x4585, 0x4276, 0x4123, 0xb243, 0x443d, 0x409b, 0xa4f1, 0x426d, 0x42e9, 0x3228, 0x4645, 0x4543, 0x2747, 0x42ed, 0x4316, 0xbf7e, 0x4120, 0x41ec, 0x41eb, 0x4174, 0x424f, 0xbfb1, 0x03c4, 0x2fe1, 0xb25d, 0x4068, 0x1b8f, 0xb270, 0xb058, 0x1c88, 0xbf6f, 0x1fce, 0x1bb9, 0xb23a, 0x4300, 0x4480, 0xaf8f, 0xb21d, 0xba5e, 0xba13, 0x1e5e, 0xbad9, 0x41a4, 0xb2c8, 0x40dd, 0xb27c, 0x382e, 0x089a, 0x429a, 0x4076, 0xae8a, 0xbf72, 0x1e10, 0x433d, 0xa7c9, 0xb0b4, 0xb034, 0xb0d4, 0x41b6, 0x1858, 0xbf7e, 0x1ec5, 0x40e7, 0x02cc, 0x41ce, 0x06fb, 0x42e7, 0xa6f3, 0x1a85, 0x40e9, 0x427f, 0x0051, 0xb0ca, 0xb28e, 0x1888, 0x4682, 0x4344, 0x1f2e, 0x429e, 0x426a, 0x324f, 0x0eae, 0xbf9d, 0xb00a, 0x4081, 0xab91, 0x465e, 0xbf2b, 0xaf28, 0x4091, 0x428f, 0x45c4, 0x191d, 0x191b, 0x42bb, 0x0d5a, 0x428a, 0x463e, 0xbf44, 0x400a, 0x40b3, 0x0aa6, 0x4085, 0xba42, 0xbf1f, 0x425e, 0xbfa0, 0xb008, 0x4315, 0xb232, 0xba77, 0x1412, 0x41dc, 0x43c2, 0x0566, 0xbf6e, 0x34aa, 0xb272, 0x1b03, 0x4171, 0x1b82, 0x0772, 0x2458, 0xab4d, 0x1c39, 0x43f9, 0x429b, 0x40ad, 0x42a2, 0xba19, 0xad15, 0xb078, 0x42f2, 0x1e62, 0x43a8, 0x3a59, 0x425a, 0xb00c, 0x0ce3, 0x29fc, 0xb291, 0x3847, 0x1bc4, 0xbf86, 0x431b, 0x1b36, 0xb26f, 0x40ee, 0x43aa, 0x124f, 0xbac5, 0x0f90, 0x402d, 0xb2d0, 0xb2b6, 0x46b1, 0x4327, 0xa182, 0xbf9a, 0x406e, 0x43a9, 0x430a, 0x05fc, 0x41d8, 0x4072, 0xbf12, 0x4287, 0x43d0, 0x2863, 0x4392, 0x1bf0, 0xa512, 0xbf92, 0x442c, 0x187c, 0x4429, 0xbad3, 0x43a2, 0x42cb, 0xa016, 0x422c, 0xbfb1, 0xb245, 0x1b85, 0x42a3, 0xba1d, 0xb023, 0x4185, 0x40ce, 0x43e7, 0x1c32, 0x4222, 0xa312, 0x34a2, 0xbfc0, 0x42a0, 0xb26c, 0x1e0d, 0x19fd, 0x434a, 0xba0c, 0x41be, 0x40a1, 0x1009, 0x410c, 0xbf9c, 0x441c, 0x41ca, 0xbf0b, 0x088d, 0xbf60, 0x2873, 0x1968, 0x41b9, 0x411f, 0x06ff, 0x463f, 0x40f2, 0x4220, 0x4278, 0x41bb, 0x06d0, 0x42d7, 0x42e2, 0x43c5, 0x1ece, 0x3ef3, 0x4218, 0xb2ae, 0x41bc, 0xa77e, 0x1068, 0xbf28, 0xbf00, 0xb296, 0x11b9, 0x4309, 0xbafd, 0xb286, 0x40e8, 0xbfd1, 0x437a, 0x35d5, 0x42d1, 0x424e, 0x4548, 0x40a7, 0x4056, 0x40ff, 0x44c9, 0xb26a, 0xba07, 0x43ca, 0x4310, 0xb2b3, 0x40ce, 0x1810, 0x462f, 0xb28f, 0xb276, 0x4266, 0x4669, 0x1e7a, 0xb21a, 0x21ce, 0x4611, 0xbf53, 0xbac4, 0x4477, 0xb2ff, 0x1eda, 0xb2de, 0x0819, 0xb28b, 0x4029, 0xbaeb, 0x050d, 0x43bb, 0x4327, 0xb23b, 0x27fd, 0xbf98, 0x1d5c, 0x4275, 0xaa35, 0x186d, 0x2879, 0xbaf5, 0x4011, 0x401c, 0x43a0, 0x3fd9, 0xba7a, 0x1b93, 0xb288, 0xaad0, 0xbf22, 0x3c38, 0xb26b, 0x2763, 0x3f20, 0x4003, 0x1a28, 0xb26d, 0xabec, 0x0d07, 0x0bf5, 0x4117, 0xbac5, 0x434a, 0xb0ee, 0xb028, 0x433d, 0x4695, 0xba77, 0x3ea1, 0x4177, 0x446f, 0xbad3, 0xa161, 0x11de, 0x1a2b, 0xb23f, 0xb2cd, 0x3450, 0xbf61, 0x3f26, 0xb291, 0x34e5, 0x46c1, 0x1fce, 0x4215, 0x423b, 0xab8c, 0x1627, 0x1011, 0x40e9, 0xb0d3, 0x1f42, 0x4164, 0xbfb3, 0x1886, 0x4018, 0x2218, 0xbadc, 0x4245, 0x42ab, 0x1f0c, 0xb2ec, 0xaa74, 0xad5c, 0x413a, 0xbf79, 0xb07a, 0x4213, 0x43ed, 0x41ac, 0x423d, 0x06e5, 0x0ad8, 0x1aaf, 0x4388, 0xb280, 0xbae9, 0x403a, 0xa331, 0xb2d9, 0xbfdf, 0x421e, 0xb0ad, 0x424e, 0x4065, 0x422c, 0x1001, 0x1a77, 0x4017, 0xbf65, 0x14cb, 0x4573, 0xb26b, 0x4371, 0x4026, 0x46fc, 0x42e2, 0xa06e, 0xae37, 0x0f59, 0xb045, 0x42d9, 0x4136, 0xba1b, 0xb2cf, 0xb2da, 0x3ce5, 0x3b2e, 0x44f9, 0x46b9, 0x3256, 0x4265, 0xbff0, 0x410d, 0xbff0, 0x2599, 0x4287, 0xbf74, 0x42ae, 0x4009, 0x2b2f, 0x4167, 0x4321, 0x459b, 0x42da, 0x416f, 0x406c, 0x42c4, 0xb2c2, 0x4630, 0x40db, 0x39ab, 0x0d75, 0xbf55, 0xb2df, 0x3c88, 0xb084, 0xb220, 0x2296, 0xbae0, 0x3e6b, 0xb002, 0x1d1f, 0x2309, 0xb26d, 0xba77, 0x2e13, 0x4084, 0xb29b, 0x10cb, 0x4397, 0xbf3b, 0xb0da, 0x42ee, 0xb2e6, 0x42c4, 0x4603, 0x1c1b, 0xbfd0, 0xbfc0, 0x4297, 0x1f20, 0x3f0e, 0x4246, 0x27a5, 0x42ca, 0xb2d0, 0x02d7, 0xaf51, 0x1d35, 0x41c5, 0x43cc, 0xb251, 0xb24f, 0x428f, 0x4101, 0x3bfb, 0x1f9d, 0xbf46, 0x4314, 0x469c, 0x1932, 0x40bf, 0xafff, 0x1cb1, 0x4141, 0xa9be, 0x43ed, 0x41a7, 0x4035, 0xba21, 0x1f1b, 0x1f56, 0x4054, 0x1554, 0x456b, 0x4249, 0xbf41, 0x4327, 0xba1f, 0x41ab, 0x407a, 0xb050, 0xbaf9, 0xb272, 0xabcc, 0x40c8, 0x4078, 0x1bcf, 0x42ae, 0xba1c, 0x42fc, 0x40b0, 0xb2ef, 0xb0c6, 0x401d, 0x46fd, 0x29e9, 0xb2a4, 0x1805, 0x42b5, 0xb01d, 0xbf47, 0x40e8, 0xb0d8, 0x056d, 0x4271, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x1a5fd34d, 0x3103aa3c, 0x3e4f9ef4, 0xf82df4a2, 0x3ba27697, 0x32d28d47, 0x5158d643, 0x5cf29a73, 0x8ce84a47, 0x4e93790b, 0xc7f42d1c, 0x6b5dd957, 0x35ca5f7d, 0xde453ad4, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x00000000, 0xffff9c02, 0xffffff91, 0x000004b0, 0x00000000, 0x00000000, 0x00000091, 0x00000000, 0x00000844, 0x00000000, 0x19860000, 0x6b5dd957, 0xffff8204, 0x000016e4, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xa14c, 0x4132, 0x41bc, 0x4020, 0x0b92, 0x157b, 0x41d2, 0x4033, 0x0ac1, 0x1a9f, 0x4029, 0x27ea, 0xb2ec, 0xbff0, 0x1c03, 0xb249, 0xbfba, 0x2af5, 0x063a, 0xb2f7, 0x1fde, 0x4650, 0x419d, 0x4254, 0x423c, 0x45c5, 0x43f5, 0xbfb7, 0xb22c, 0x439d, 0x41ae, 0xa021, 0x405d, 0xb2f5, 0xa199, 0x4328, 0x43c1, 0xa29f, 0x3ace, 0x40cd, 0xb29a, 0x4192, 0x4165, 0xbaf5, 0x281e, 0x2f02, 0xa053, 0xb2a9, 0x414c, 0x2319, 0x40bb, 0xbfdb, 0xbf80, 0xa7d7, 0xbff0, 0x424b, 0xb031, 0x4063, 0xbf29, 0x443d, 0xba16, 0x2f1f, 0x2cae, 0x4108, 0xb0e1, 0x4362, 0x3931, 0xbac3, 0xbf8d, 0xb286, 0xb2bd, 0x43b0, 0xb01c, 0x42f2, 0xbfb5, 0xba58, 0x4066, 0x42a6, 0x40f4, 0x25d9, 0xba15, 0xba76, 0x4173, 0x32e9, 0x4184, 0x45f3, 0xb0e2, 0xb2e3, 0x32b6, 0xbf81, 0x4136, 0x448a, 0xba7b, 0xba55, 0xb23c, 0x4153, 0x4323, 0xb2cb, 0x18e5, 0x4161, 0xba7b, 0x363d, 0x408c, 0xbf39, 0x400f, 0x406c, 0x41c0, 0x420d, 0x32e5, 0xb24a, 0x2bb8, 0xaa8c, 0xb053, 0xb276, 0xbf4d, 0x4109, 0xae28, 0xba33, 0x429e, 0xb055, 0x2c5a, 0x43c2, 0xb283, 0x05b1, 0xbfd3, 0x19f9, 0x4081, 0xbace, 0x43c3, 0x455e, 0x42aa, 0xbfcf, 0x42d3, 0x4000, 0xb23a, 0x42ee, 0x41b4, 0xba39, 0x2cd1, 0x303c, 0x43a4, 0xbfbc, 0xb2db, 0x2de9, 0xbaf6, 0xafed, 0xba77, 0x42f4, 0x4163, 0x41b5, 0x41b5, 0x3a75, 0x4298, 0x4215, 0xba5c, 0x42bc, 0xb2c8, 0x41ec, 0x2f90, 0x42b1, 0xb065, 0xbfbc, 0xba6f, 0x456e, 0xb234, 0xbf42, 0x44b4, 0x46f9, 0x1b3f, 0x1d00, 0x4201, 0xb223, 0xa1f4, 0x27e7, 0x41c0, 0x1e5c, 0x46b1, 0x412b, 0xa523, 0xbf00, 0x4033, 0x151c, 0x3328, 0xba10, 0x420f, 0x45b4, 0x1217, 0xbf6f, 0xa77d, 0x2c63, 0x1985, 0x411a, 0xb262, 0xbf1f, 0xb260, 0x1a16, 0x43ac, 0x41d4, 0x440c, 0x41f6, 0x3409, 0xb2a6, 0xbfe1, 0x2013, 0x43e1, 0x43fd, 0x46d1, 0x4160, 0xbf73, 0x4132, 0x429b, 0x23b3, 0xbae9, 0x10bd, 0xba7f, 0xba1b, 0x1886, 0x109c, 0x41a6, 0xb201, 0x1be2, 0xbf95, 0x410e, 0x46a2, 0x430a, 0x02f1, 0xb2d5, 0xb206, 0xbae9, 0x1b5c, 0x40f3, 0x44da, 0xbf43, 0xabc8, 0x0f66, 0x1883, 0x458b, 0x426d, 0xa283, 0x4033, 0x416e, 0x4173, 0x4461, 0xb2f6, 0x1139, 0xb0b0, 0xbfe0, 0x437a, 0x41f9, 0xb226, 0x4359, 0x4160, 0x4618, 0xb27e, 0x41ab, 0xbfb4, 0xb0f4, 0x4603, 0x401e, 0x2dd7, 0x1b5c, 0xb2ec, 0x16ab, 0xb00d, 0x41b9, 0xa2eb, 0xb00d, 0x40bf, 0x2e03, 0x46b2, 0xbf13, 0x38c1, 0x1ed1, 0xbac9, 0x4036, 0xb0bf, 0x41bc, 0xba14, 0x02dc, 0x41c1, 0x4383, 0xba42, 0xb223, 0xba53, 0x4037, 0xba00, 0x43d0, 0x4362, 0x42ea, 0xb03f, 0x41a3, 0x4111, 0x001b, 0x0080, 0xbf7f, 0xb299, 0x40e7, 0x4158, 0x4196, 0x1a5d, 0x07f6, 0x41ee, 0x0c49, 0xb2c8, 0xb243, 0x409a, 0x392d, 0xb2bf, 0xa325, 0x4629, 0xbfd4, 0x4334, 0x4305, 0x2dd2, 0xbfe0, 0xb01d, 0x36a2, 0x4211, 0xbfe4, 0x4151, 0xbfb0, 0xb2da, 0xbf70, 0xb2ee, 0x43a8, 0xbf85, 0x448d, 0x43e6, 0x1b34, 0x18e6, 0x43f7, 0x1d9f, 0xb251, 0x40b6, 0x0822, 0x4048, 0xb086, 0x0161, 0xbaca, 0xb211, 0x0500, 0xb076, 0x400d, 0xbf9c, 0x40bd, 0x41bd, 0x22d5, 0x421d, 0x3876, 0x1a86, 0x408e, 0x357b, 0x4567, 0x41f6, 0xb29f, 0x43f4, 0x439b, 0x431b, 0x4115, 0x3962, 0xb0cc, 0x4198, 0x0afa, 0x445d, 0xbac5, 0xaa18, 0x41e1, 0x43e4, 0xbf59, 0x43b8, 0x431e, 0x1849, 0x4233, 0x4393, 0x42dd, 0x4275, 0x430f, 0x420b, 0xbf90, 0x4116, 0x43b5, 0xb226, 0xad7b, 0x45e8, 0x4202, 0x43c7, 0x2c0c, 0x4070, 0x4185, 0x43ce, 0xbad1, 0x1a16, 0x1c88, 0x413b, 0x4163, 0x2d64, 0xb03b, 0xbf63, 0x1d22, 0x40c5, 0x285b, 0x1f82, 0x41ba, 0x417d, 0xba1b, 0x4219, 0x40e2, 0x4297, 0x4307, 0x1873, 0x4050, 0xbfa0, 0x229d, 0x4055, 0x436a, 0x3545, 0x1a18, 0x42ef, 0xb280, 0x40a1, 0x1c13, 0xb05e, 0x408e, 0x42d4, 0x2688, 0x40d3, 0xbfc3, 0xa2eb, 0x421d, 0x440e, 0x090b, 0xb264, 0x4160, 0xb2b3, 0xbad4, 0xba36, 0x40f8, 0xb2f5, 0x3be0, 0xb096, 0x1da7, 0x1f4e, 0xba52, 0x4155, 0x4273, 0xbf8e, 0x42f4, 0x4247, 0x4148, 0x428e, 0x4481, 0x1cd6, 0x436c, 0xb22a, 0x4189, 0x27ea, 0x42c1, 0x435f, 0x41ca, 0x24f5, 0x40a8, 0x4304, 0x4575, 0x416a, 0x42d4, 0x41d8, 0x1cf2, 0x430a, 0x4249, 0xa6d5, 0x4208, 0x4361, 0xbf77, 0xb2bf, 0x4061, 0x42f5, 0x1df1, 0xb2fb, 0x2eb2, 0x1f36, 0x0636, 0x4233, 0xb051, 0x0d22, 0x437a, 0x187c, 0x431d, 0x2642, 0x4329, 0xba5e, 0x402e, 0x1a7d, 0xb27c, 0xbfc0, 0x4382, 0x430d, 0xaad4, 0x4033, 0x423d, 0x4266, 0xbfc1, 0x1a59, 0x4349, 0x416c, 0xb236, 0x4337, 0x2973, 0x43cb, 0x41d8, 0xb031, 0x431b, 0x1bce, 0x1553, 0x40ea, 0x0c9e, 0xba3b, 0x1192, 0x41a9, 0x4261, 0x4086, 0x1582, 0x4637, 0x4056, 0xbf8c, 0x4600, 0x429b, 0xbfac, 0x2ba6, 0x2e02, 0xbf2d, 0x463a, 0x41d0, 0x1d05, 0x43f9, 0x2c9c, 0x40af, 0xb00f, 0x2d8b, 0x4201, 0x4354, 0xba21, 0x1e4b, 0x41ae, 0xb2ba, 0xb28c, 0xa6c2, 0xbafd, 0x27e8, 0xa499, 0xbf8f, 0x4272, 0x076d, 0x4342, 0x4337, 0x1f5e, 0x41a3, 0x37d5, 0x417e, 0x4215, 0x4678, 0x143e, 0xbafc, 0xab29, 0x4095, 0xb28d, 0x0ed2, 0xbf80, 0xabb9, 0x433d, 0xba51, 0x0bba, 0xaa69, 0xa93d, 0x4117, 0xbfc7, 0x312f, 0xb099, 0x119d, 0x4393, 0x40e9, 0xbf94, 0x1cb6, 0x4294, 0x43f5, 0x42b8, 0xb26f, 0x2772, 0xb0a6, 0x413b, 0xadeb, 0x408e, 0xbfbc, 0x4542, 0xba61, 0xb066, 0x40f9, 0x45d5, 0xabc9, 0xba51, 0x19c0, 0x4202, 0x4245, 0x4162, 0xb256, 0x0fff, 0x1ca8, 0xbaf9, 0x3ce8, 0x45ee, 0x1bd9, 0xbfa0, 0xa474, 0xbfb3, 0x42c1, 0x39dc, 0xb08f, 0x303f, 0xba67, 0x1ec2, 0x43bd, 0xb2b5, 0x42df, 0x0cbd, 0x240e, 0x402b, 0x411d, 0xba53, 0xb20e, 0x41b8, 0xbf78, 0x413f, 0xbff0, 0x0612, 0xbf36, 0x4685, 0x3a69, 0x4318, 0x4137, 0xba21, 0x42ba, 0x4259, 0x4207, 0x41d7, 0xb2bc, 0x3b7e, 0x40d0, 0x423b, 0xab19, 0xb2ae, 0xb2b8, 0x43a4, 0xba50, 0xba2f, 0x21c8, 0x24c1, 0x4110, 0x422d, 0xbf4d, 0xa1c2, 0x1745, 0x18d8, 0x095c, 0xb260, 0x4070, 0xb23f, 0xa815, 0x346e, 0xb21f, 0x418c, 0x407f, 0xb259, 0xba4e, 0x3d84, 0xb2ff, 0x40d6, 0x44b3, 0x1bfb, 0xa6b7, 0x41f2, 0x412c, 0x4262, 0xbafd, 0x4460, 0xbfb0, 0x414c, 0xbfa9, 0x129a, 0x43d9, 0x09f1, 0x4262, 0x2b67, 0x4369, 0x40b7, 0xbf4c, 0x4473, 0xba7f, 0xba3f, 0xba30, 0xb039, 0x4378, 0x431a, 0xbf71, 0xb20b, 0xba76, 0x410a, 0x4284, 0xa2fb, 0xb283, 0xb2c3, 0xa762, 0x3e09, 0xba6c, 0xbf65, 0x4387, 0x218b, 0x2a1c, 0x4184, 0xb2b6, 0x41c0, 0x4298, 0x4249, 0x4397, 0x1ed0, 0xbafe, 0x4659, 0x1fc5, 0xa570, 0x4684, 0x40d0, 0xb012, 0x2c6b, 0xb22b, 0x105a, 0x4229, 0x409a, 0x43ce, 0xb06d, 0x4085, 0x4280, 0x12a1, 0xbf4d, 0xb09d, 0x436c, 0x281c, 0x40ff, 0x0cc9, 0xbfad, 0x1195, 0x42b4, 0x43f7, 0x415b, 0x40ad, 0x43e6, 0xb016, 0x4086, 0x4037, 0x0149, 0xbfd0, 0x41f5, 0x3cc4, 0xad52, 0x2422, 0xa9f5, 0x41c6, 0x26b2, 0x4226, 0xb27e, 0xbf3e, 0x02a3, 0xbac8, 0xb225, 0xbf31, 0x0663, 0xaea1, 0x4010, 0x00b1, 0x4201, 0xb0ce, 0xba4c, 0x400b, 0x4442, 0x436d, 0x0722, 0x42f6, 0x4321, 0x4001, 0x1f25, 0x411e, 0x401d, 0xba3a, 0x0ba3, 0xb2e1, 0x0141, 0xb01f, 0xb26c, 0x41a7, 0x435a, 0xaa86, 0xbfbb, 0x459c, 0x1caf, 0x441e, 0x43e5, 0x3643, 0x4240, 0xb292, 0x3097, 0xb2c2, 0x1b2f, 0x0db8, 0x40ab, 0xba0c, 0xb21c, 0x415e, 0x42ba, 0x4182, 0x25a2, 0x42c0, 0xb08f, 0x2808, 0xbf97, 0x400b, 0x43af, 0xb22e, 0x416e, 0xba40, 0xbf80, 0x43fe, 0x4349, 0xbada, 0x4003, 0xbff0, 0x0a40, 0x3d0b, 0x412c, 0xbf2f, 0x430f, 0x101c, 0x1a47, 0x41ce, 0xb2de, 0x4359, 0x4297, 0x41ad, 0x1d4a, 0x44eb, 0xbf80, 0x436f, 0xa5d7, 0x1cb2, 0xbf1f, 0x2907, 0x1b41, 0x43c7, 0x42a7, 0x41d0, 0xb2d9, 0x429f, 0x43d2, 0xa0eb, 0x4106, 0xb2d9, 0xa5a9, 0xa43e, 0x43d7, 0x12d3, 0x1e49, 0x41e3, 0x4589, 0x433d, 0x430f, 0xab65, 0xb22d, 0xbaee, 0xb22a, 0xaec2, 0xb265, 0xba6c, 0x1a12, 0xbfb2, 0x4137, 0xa12b, 0x4115, 0x4306, 0x43e9, 0x1c8b, 0x1542, 0x2360, 0x45ea, 0x4387, 0xba7a, 0x1fab, 0xb245, 0xbfd8, 0x2725, 0xbf78, 0x4146, 0x428a, 0xb21b, 0x19f9, 0xb282, 0xb2da, 0x4622, 0x40ae, 0x46ac, 0x41ac, 0xbf33, 0xb213, 0xb22d, 0xba56, 0x40b7, 0xab93, 0x18e7, 0x4372, 0x40ce, 0xb23d, 0x1a8c, 0xb281, 0x42a2, 0x41ce, 0xb22e, 0x43ee, 0xb221, 0xbf2f, 0xb2c3, 0x4081, 0x23a1, 0x4162, 0x42b6, 0xb004, 0x1e59, 0xb2d0, 0x4121, 0x435d, 0xb2af, 0xb2f0, 0x42ab, 0xbfe0, 0x1cb8, 0x44f3, 0x4605, 0x1e0e, 0x4625, 0x31e0, 0x4570, 0x40ec, 0xbf7a, 0x4173, 0xa0f7, 0xb2f3, 0xba19, 0x4149, 0x1f51, 0xba44, 0x29ac, 0x402d, 0x1808, 0x01f2, 0x41bf, 0x4186, 0x42e4, 0x10e4, 0x1f7a, 0x416a, 0xb0b8, 0x4130, 0x4467, 0xbf4a, 0xba17, 0x4237, 0xb2bd, 0x1cb4, 0x1955, 0x3010, 0xbf6c, 0x419f, 0xbf90, 0x4326, 0x423d, 0x40b3, 0xb24d, 0x413a, 0x414f, 0x4317, 0x4018, 0xb254, 0x0dd2, 0x4281, 0x4175, 0x4590, 0x19ad, 0x405f, 0x1d33, 0xbf2b, 0x3132, 0x1412, 0x1bbc, 0x1bb8, 0x4259, 0x426e, 0x414f, 0x4358, 0x43df, 0xb056, 0x4677, 0xa879, 0xade8, 0xba78, 0x436a, 0xb211, 0x4013, 0x40e9, 0x4280, 0xbff0, 0x41e8, 0x068f, 0x185e, 0x43c5, 0x43c6, 0xb021, 0xbfa7, 0x42da, 0x1aa9, 0x4214, 0xa017, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x371597f2, 0x76cf74c6, 0xf6f07bf9, 0x14bc3ff2, 0x44d46dd4, 0x85b9d3ca, 0x44827883, 0x4ba02ac3, 0x45d4fe43, 0x55355bd7, 0xa1571ee2, 0x4065eef1, 0x6a318c25, 0x4295c0bf, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x45d4fe43, 0xa1571ee2, 0x00000000, 0x82fbba0c, 0x00000064, 0x4295cc27, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x200c, 0xbf4b, 0x3eb3, 0x4389, 0x4279, 0xb2c4, 0x432f, 0xb2ad, 0x1a23, 0xa8ec, 0x4396, 0x424d, 0x4177, 0xb0e4, 0x30b3, 0x4175, 0x42f2, 0x43e7, 0x40d1, 0x1e00, 0xb20a, 0x1f46, 0x42a6, 0x401c, 0xbf26, 0x4342, 0x4082, 0x38b3, 0x01dc, 0xbfe0, 0x429c, 0x4192, 0xb294, 0x1c89, 0xbf8a, 0x4290, 0xba28, 0xba38, 0x4255, 0xa885, 0x41bc, 0x1e2c, 0x4188, 0xb25d, 0xb245, 0x0f0d, 0x1af7, 0x1a13, 0x00b2, 0x23e6, 0x05cc, 0x350b, 0x43da, 0xb2ee, 0xa17e, 0x4290, 0x11a2, 0x4151, 0x1629, 0xbfaa, 0x44a8, 0x3802, 0x2ddb, 0x3c35, 0x4328, 0x1c04, 0x41cc, 0x4010, 0x4031, 0x41b9, 0xbfcd, 0x420c, 0x43fe, 0xb209, 0x4070, 0x4009, 0xb04f, 0x1b1a, 0x1ad0, 0xba64, 0xab75, 0xbaf7, 0xb01e, 0x41f5, 0x423f, 0x46d9, 0x0581, 0x40aa, 0x2fd3, 0x19d7, 0x1d89, 0x357c, 0xb0e6, 0x1ef7, 0xbf29, 0x44e1, 0x066d, 0x3b50, 0xac4d, 0x19a8, 0x449d, 0x0215, 0x0662, 0x4348, 0x4168, 0x4392, 0xac1f, 0xb02d, 0xbf88, 0x1835, 0xa8cb, 0xb06d, 0x426f, 0x3a1f, 0x1aeb, 0xb01d, 0xb014, 0x4221, 0x1e94, 0xbf53, 0xb287, 0xadbc, 0xb275, 0x42b6, 0x46c4, 0xab25, 0x394f, 0x1a27, 0x1dc2, 0xbff0, 0x43bc, 0xb062, 0xb2bd, 0x4109, 0x4305, 0xbf37, 0xb09e, 0x40a7, 0xb2ca, 0x42dd, 0xb248, 0xb0c0, 0x311c, 0x40cd, 0x40e7, 0x428f, 0x422c, 0x4213, 0x4101, 0x1838, 0x1f0a, 0x1b05, 0xbad3, 0xb28d, 0xbf70, 0x40bc, 0x4383, 0x43f6, 0x0bb7, 0x4036, 0x4582, 0x3552, 0xbf0f, 0x4329, 0xb244, 0xb2cf, 0x4095, 0xb261, 0x41f2, 0x083b, 0xb278, 0x0ce4, 0x415c, 0xbf3f, 0x4001, 0xb0ec, 0x0cbe, 0x1347, 0xbf1d, 0x4254, 0x199b, 0x1f28, 0xb2c1, 0x071c, 0x4234, 0xba53, 0xb074, 0xba5d, 0x4391, 0x3240, 0xb2e7, 0x4320, 0x19b5, 0x020e, 0x439b, 0x2782, 0xba6a, 0x094a, 0xb044, 0x40f5, 0xb21a, 0x40cd, 0xb274, 0xbf9a, 0x424f, 0x4624, 0x4009, 0xb23e, 0x42ef, 0x4284, 0x4548, 0x1da1, 0x19f5, 0x4100, 0xb2b2, 0xad48, 0x40a6, 0x1f3d, 0x45e1, 0xb22d, 0xbf12, 0x4008, 0x08ae, 0x420c, 0xbae7, 0x4273, 0x4225, 0x1c10, 0xbf68, 0x4070, 0x4113, 0xb08a, 0x0a0a, 0xa398, 0x08f0, 0xbfe0, 0x0f55, 0x41c3, 0x1c12, 0x385a, 0xa0f9, 0x1e68, 0x13ce, 0x1ffe, 0x4032, 0x435f, 0xba10, 0xb231, 0xb27f, 0xbf13, 0x42e5, 0x16d2, 0xb2e2, 0x4379, 0xba75, 0x428e, 0x4239, 0x4311, 0x42df, 0x43ed, 0x29dd, 0xbfa4, 0xb05e, 0xaf16, 0x1b1d, 0x4101, 0x415a, 0xb2a8, 0x40ed, 0x3eb0, 0x4346, 0x412b, 0xb28f, 0x415a, 0xbfd9, 0x425f, 0x4182, 0xb249, 0x43e6, 0xb2b9, 0x1400, 0x4073, 0x1272, 0x42d9, 0x4391, 0x407b, 0x43ba, 0xb07c, 0x41b6, 0xba09, 0x4419, 0xbfba, 0x40ae, 0x44f2, 0x1e03, 0x3d31, 0x4313, 0x417c, 0x1f4a, 0x40e1, 0x1b00, 0x425e, 0x46d2, 0x406c, 0xbfc3, 0x1ef0, 0x4266, 0x407f, 0x2f1e, 0x2462, 0x43ae, 0xb077, 0x4187, 0x4624, 0x4366, 0xba60, 0x43ed, 0x4429, 0xb20c, 0x2bc8, 0x181e, 0xbfb0, 0xb02b, 0xb26f, 0xa896, 0x42e8, 0x1cc9, 0x404d, 0x2159, 0xb27e, 0xbf2e, 0xbff0, 0x0378, 0x4598, 0x45ec, 0xbfda, 0xba4e, 0xb250, 0xb2b4, 0xb0f9, 0x43d1, 0xb027, 0x41e3, 0x4076, 0x43df, 0xbafd, 0x4397, 0x4046, 0x4567, 0xb251, 0x454f, 0x44e5, 0xbf8d, 0xba25, 0x461d, 0x415d, 0xae84, 0x4577, 0xbaeb, 0x0cd3, 0x4201, 0x025f, 0x4126, 0x2412, 0x1896, 0x424a, 0x1daf, 0xb22e, 0x4311, 0xba23, 0xbaf4, 0x4243, 0x3d58, 0xb0c3, 0xb0dc, 0xb230, 0x424f, 0x401f, 0xbf32, 0xa999, 0x42d4, 0x4306, 0xbfd2, 0x45f4, 0xaab3, 0xb0d5, 0x34d6, 0xb280, 0xb229, 0x4068, 0x1e55, 0x4368, 0x40bd, 0xba7f, 0xba63, 0x4108, 0x1fd9, 0xb000, 0xb295, 0x40b9, 0x2f6d, 0x447c, 0x43b9, 0xbf7c, 0xb059, 0xba47, 0x40d6, 0x1e41, 0x378b, 0xbfa0, 0x25c8, 0x2b2f, 0x014f, 0xbf03, 0x4139, 0xb238, 0x01a8, 0x3707, 0x1878, 0x4301, 0x19e6, 0xab18, 0xba2e, 0x4126, 0x43eb, 0xb29f, 0x42a6, 0x41bb, 0x0eca, 0x08f5, 0x414a, 0x41c5, 0x4305, 0x43bd, 0x40ed, 0x0b88, 0x4387, 0x4380, 0xb0fd, 0x31e5, 0xba59, 0xbf90, 0xbf57, 0x40f1, 0x435f, 0x385f, 0x403b, 0xbadc, 0x301d, 0xb265, 0x440c, 0xbac3, 0xb2a3, 0x402c, 0x45bb, 0x43e4, 0x2833, 0x4109, 0xae7c, 0xb0d2, 0x458e, 0x40ec, 0x14c5, 0x405a, 0x2480, 0x0070, 0xbf24, 0x41b2, 0xba46, 0x401c, 0xbf27, 0x2583, 0xb24d, 0x41ff, 0x32fc, 0xb0d4, 0x4045, 0x4145, 0x00ca, 0x4067, 0x42ac, 0x4094, 0x3145, 0x4120, 0x1e2a, 0xba20, 0x4156, 0x3e8c, 0x40e0, 0x1c02, 0xba0d, 0xb061, 0x46fd, 0x42e6, 0xbf6b, 0x411b, 0x1fbf, 0xb070, 0x1846, 0xb22b, 0x4695, 0x407e, 0xba6e, 0x10bc, 0xbf94, 0x42fd, 0x43b9, 0xac56, 0x414a, 0x0183, 0x41a6, 0x42f3, 0x433d, 0x19c1, 0x4053, 0x4204, 0xb282, 0xb241, 0x1e8c, 0xb287, 0x421b, 0xb270, 0xb036, 0xb285, 0x4220, 0xbaca, 0xb289, 0xb275, 0x2849, 0x410d, 0xa4a3, 0xb231, 0xbfb4, 0x1e9b, 0x413e, 0xa953, 0xb289, 0x408c, 0x4237, 0x3547, 0xb0fe, 0xbf4c, 0x45d8, 0x424e, 0x40a3, 0x1e5c, 0x41c3, 0xbafe, 0x32ef, 0x4191, 0x41f2, 0x1d7d, 0xaacc, 0x43f2, 0x418f, 0xb203, 0xb0cb, 0x3ac6, 0xa494, 0x41fe, 0x4015, 0xbace, 0x34f5, 0x4385, 0xbf1c, 0x323a, 0x414f, 0x4312, 0x402a, 0x426f, 0x41b7, 0x4257, 0x2fda, 0xb2d1, 0x4251, 0xbf34, 0x405a, 0x44c0, 0xbfbc, 0x45e2, 0x4285, 0x40b7, 0x4293, 0xb0fa, 0xba14, 0x15bf, 0x4350, 0x420a, 0x418c, 0x33de, 0xba58, 0xa868, 0x1eb6, 0xb202, 0x43a4, 0x439c, 0x42a7, 0x4323, 0x45e6, 0x1e1b, 0xa2d3, 0x1833, 0x1896, 0x4174, 0x411b, 0xbf6d, 0x13be, 0x2d69, 0x41eb, 0x429c, 0xb2d2, 0xb2a0, 0x2ec9, 0x41ee, 0x1b0a, 0x1fbf, 0x423a, 0x1db5, 0xba10, 0x40df, 0x41dc, 0xb25f, 0xb2b9, 0xb2dd, 0x45dd, 0x403d, 0x31a2, 0x4190, 0x402b, 0xbf5c, 0x43ce, 0xa551, 0x386b, 0xbae4, 0x09fd, 0x4318, 0xbf46, 0x413d, 0x1c10, 0x41e9, 0x42b9, 0x1792, 0x419e, 0x1f8d, 0xbfb2, 0xb2be, 0x426b, 0x12da, 0xa7d4, 0x19a3, 0x40c7, 0xbad2, 0x43ee, 0x45cb, 0x0e6b, 0xabd7, 0x094f, 0xbac9, 0x40a0, 0x43aa, 0xbfe2, 0x4375, 0x2acd, 0x41de, 0x43e1, 0x1035, 0x067c, 0x40e4, 0x43cd, 0x1499, 0x19fd, 0xb09a, 0x43eb, 0x3b91, 0x0573, 0x4043, 0x1edc, 0xba7a, 0x4341, 0x37ca, 0x4030, 0x1037, 0xbae7, 0xb2b5, 0x4339, 0xbf03, 0x1731, 0x461f, 0xb040, 0x417d, 0x41dc, 0xb013, 0x1e18, 0x05e4, 0xbfb8, 0x41d7, 0x1c58, 0x0e05, 0x41ed, 0x431f, 0x406f, 0x43f3, 0x19df, 0x4058, 0x412a, 0x1e46, 0x4248, 0x4561, 0xbf6d, 0x4073, 0x429a, 0x4280, 0x1bd4, 0xb0fe, 0x4024, 0xbfde, 0x41ca, 0x4143, 0x424d, 0x4137, 0x434d, 0x4206, 0x2331, 0x434b, 0x41d2, 0x412d, 0x3037, 0x423c, 0x4582, 0xbaed, 0x41f8, 0x4113, 0x4584, 0xbf2a, 0xb234, 0x1de1, 0xb2cd, 0x4099, 0xa0ca, 0x4167, 0xbf00, 0x42e3, 0x2ee1, 0x1d91, 0x0fa0, 0x400d, 0x3248, 0xbfde, 0x25d6, 0x4332, 0x0ca8, 0x18fd, 0xb25f, 0xba37, 0x4121, 0x4402, 0x42c8, 0xbfc0, 0x3ff9, 0x41c3, 0xb2a8, 0x184f, 0x37a4, 0x439d, 0x17f7, 0x4273, 0x0b72, 0x428d, 0x4293, 0xad65, 0xbf60, 0x1be0, 0xba20, 0x43e8, 0x4033, 0x219f, 0xbf27, 0x425b, 0x1cab, 0x42c0, 0xa753, 0x411b, 0x42ad, 0x14cd, 0xba71, 0x4221, 0xba79, 0xbfbf, 0x423f, 0x437b, 0x418d, 0x43c5, 0x4062, 0x0c46, 0x3c2f, 0x442a, 0xbf26, 0x1a90, 0xb2b7, 0x07b4, 0xbf52, 0x04df, 0x42e1, 0x1d76, 0x418b, 0x333f, 0x1f08, 0x4007, 0x1246, 0xb259, 0x4084, 0x4223, 0xb257, 0xb26b, 0x2ba1, 0xb232, 0x11ce, 0xb25e, 0xb213, 0xbf5e, 0xb26f, 0x1bdd, 0x426c, 0x4375, 0x18e4, 0x403c, 0x42b8, 0xb02b, 0x4197, 0x1861, 0x2f7a, 0xbfb8, 0xa15b, 0x43d1, 0x3df1, 0xb2e7, 0x43dd, 0x425d, 0xba27, 0x4281, 0xbaf7, 0x4644, 0xbf4d, 0xa7c4, 0x4208, 0x411c, 0x43f3, 0xbacf, 0xb04f, 0x4091, 0x41d0, 0x2fd4, 0x4332, 0xb205, 0x45ee, 0xb2b7, 0xb0a1, 0xb235, 0x214e, 0x4232, 0x41e1, 0xbada, 0x2e04, 0xb2ba, 0x40f8, 0x3e46, 0xbf05, 0x3862, 0x0057, 0x426a, 0x401e, 0xb014, 0xba19, 0x0d65, 0xba7d, 0x41cc, 0x34c8, 0x4201, 0x4202, 0x43a6, 0xb004, 0xbf2c, 0xa057, 0x0d9f, 0x431a, 0xba38, 0x40b7, 0xb277, 0x4275, 0x27a1, 0x4222, 0xb098, 0xb02e, 0xb2de, 0x40e5, 0xbf27, 0x42a3, 0xad53, 0x4627, 0xbfe0, 0xba06, 0x4098, 0xb24c, 0x40e0, 0x0ec3, 0x411a, 0x403f, 0xb256, 0x42eb, 0xba5f, 0xbfb6, 0x42ac, 0x41cd, 0xb2ec, 0xbf85, 0x0e6f, 0x164c, 0xb207, 0x434c, 0xbfe0, 0x40f4, 0xbaed, 0x4056, 0xb20a, 0x1834, 0x4659, 0x1d2e, 0x0587, 0xbfb6, 0xaaf8, 0x45c6, 0x401b, 0xba6a, 0xba64, 0x40b0, 0xba31, 0x0731, 0x41ae, 0x427d, 0x4021, 0x272c, 0xba2e, 0x409f, 0xbae6, 0x4048, 0x4065, 0xbf06, 0x40e6, 0x430c, 0xb20a, 0xba68, 0xba40, 0xbf95, 0x4171, 0x43f2, 0xb25a, 0x43cb, 0x42d0, 0x46db, 0xa3e7, 0x43e8, 0x402c, 0x053e, 0x422a, 0x4261, 0x429e, 0x417d, 0xb234, 0x41f5, 0x2f6f, 0x44d2, 0xbf6a, 0x1fff, 0x1a5b, 0xba78, 0x29e0, 0x43ad, 0x4200, 0xb2dd, 0x0096, 0x3a21, 0xb247, 0x382e, 0xba2e, 0x4336, 0x4021, 0x4221, 0xbf59, 0x43cd, 0x1810, 0xa198, 0x2e75, 0xba5d, 0x42dd, 0x42a6, 0x422b, 0xbf07, 0xa0f8, 0x4206, 0x41d6, 0x3238, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3aa91aa4, 0x4c5784a8, 0x59e0657d, 0x51ef61ac, 0x760b00f1, 0x0da5a30b, 0x69a772e3, 0x96f320f6, 0x01c12c2c, 0xe5449e38, 0x74e0fd57, 0xcf8b32f0, 0x484f3e23, 0x911d3cda, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0xffffffb0, 0x00000000, 0x00000017, 0x00001b1c, 0x00000000, 0x00001c1b, 0x38000000, 0xffffffff, 0x0382586e, 0x17da7113, 0xe9c1faae, 0xcf8b32f0, 0x01c12c37, 0xfffffbd4, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb251, 0x317d, 0xb2ae, 0x2093, 0xb04a, 0xb0b8, 0x1e16, 0x405a, 0xbfb8, 0xb0cf, 0x4099, 0x431e, 0x41e5, 0xbf70, 0xb0e1, 0xa18f, 0x1f25, 0xba65, 0x1d38, 0x1119, 0xba41, 0xb23a, 0x03ae, 0x01a6, 0x1dfc, 0x1e5c, 0x4181, 0xba71, 0x401c, 0x3c40, 0x4154, 0x1895, 0xbfd0, 0xbf7c, 0x1af1, 0x429c, 0x1148, 0x41b5, 0x432d, 0x43f4, 0x461b, 0x41d6, 0x1ef7, 0x1d2d, 0x41c8, 0x3021, 0xb25c, 0x431b, 0xa59d, 0x4355, 0xbae7, 0xbff0, 0xb2fa, 0x39aa, 0x0c26, 0x4144, 0x0954, 0x419b, 0x03a0, 0xbfd1, 0x45e9, 0x439a, 0x4127, 0xb23d, 0x4132, 0x1ae1, 0x4671, 0x4613, 0x45e2, 0xbac3, 0x421c, 0x1379, 0x1ea9, 0x16a8, 0x406d, 0x3d2b, 0x4267, 0xb205, 0x4042, 0x4232, 0x45e4, 0xb2ba, 0x1a6c, 0xb2a8, 0xb0fe, 0x2dc3, 0xb0d7, 0xbf4a, 0x407c, 0x40f2, 0xb21b, 0x0c04, 0x2884, 0x1b10, 0x42fb, 0x41b3, 0x0235, 0x2120, 0xb062, 0x438b, 0x444c, 0x43d0, 0x41d9, 0x1dc3, 0xb02b, 0x1b8e, 0x4231, 0xba4a, 0x4372, 0x42d1, 0xb246, 0x1cfa, 0x2420, 0x41ec, 0x41a2, 0x43cf, 0xbf6c, 0x0d46, 0xba57, 0x4266, 0xaca4, 0xb220, 0x33fb, 0xb2da, 0x40ac, 0x4207, 0xb2ca, 0x40fa, 0xb2ce, 0x2f83, 0x426d, 0x1f13, 0x42f0, 0x1902, 0x4374, 0x1fff, 0xa5f3, 0x40e8, 0x42ed, 0x43c6, 0x40de, 0x4244, 0x0f5d, 0x40f3, 0xbfb6, 0x36a3, 0x418e, 0xb0f3, 0x46c5, 0xbac8, 0x1cc6, 0x1495, 0x4121, 0xb286, 0x43d1, 0x431b, 0xad3e, 0xb240, 0xbaed, 0xba30, 0x41af, 0x400c, 0xb216, 0x08b2, 0x4426, 0x4321, 0xb28e, 0x439c, 0xbfa4, 0x2883, 0xba2e, 0x094c, 0x4382, 0x4118, 0x19a5, 0x05b0, 0x4000, 0x4064, 0x19b5, 0x435f, 0xb2e0, 0x4399, 0x1ec1, 0xb09a, 0x28c9, 0x0895, 0xba6b, 0x41c7, 0x4481, 0x42f6, 0x1dc4, 0xbfb8, 0xb249, 0xba32, 0xa293, 0xba7e, 0x40c7, 0x4369, 0x4211, 0x4198, 0x4294, 0x4256, 0x1be2, 0x1f8a, 0xb20f, 0x40c6, 0x409c, 0xbfd4, 0x4197, 0x42ab, 0x4149, 0x4284, 0x4610, 0xb2fd, 0x41f9, 0xb260, 0x427d, 0x18bf, 0x45f4, 0xa24d, 0xba4e, 0xba66, 0xb2dd, 0xbaed, 0xbf25, 0x1a95, 0x1d6a, 0xb2c6, 0x4005, 0x0738, 0x1930, 0xba7b, 0xb289, 0x14bb, 0x13be, 0x4368, 0x4005, 0xba63, 0x18f7, 0x078c, 0x464a, 0xbf84, 0x0f5a, 0x1e55, 0x4262, 0x26de, 0x4132, 0x421e, 0xb04b, 0x0632, 0xb2a5, 0x43a4, 0xb03d, 0x18a6, 0x41f7, 0xb2dc, 0xb226, 0xbf34, 0x4427, 0x4019, 0x4448, 0xbaeb, 0x43de, 0x42d8, 0x4345, 0x407f, 0x4691, 0x4245, 0x4200, 0x1c1d, 0x4130, 0xa717, 0x417e, 0x42eb, 0x3c1a, 0xa401, 0xbf24, 0x0fd2, 0x424e, 0xb26c, 0x4315, 0x4064, 0xb025, 0x4431, 0xb2d9, 0x0876, 0xb2df, 0x0bb1, 0xbf48, 0x259e, 0x3f2b, 0x38c9, 0x44fb, 0x4216, 0xb0ab, 0x4138, 0x4203, 0xbfab, 0xa3bb, 0xba73, 0x3abe, 0x4136, 0x4212, 0x129e, 0xbfb0, 0xbf67, 0xb2f7, 0xbafe, 0x4066, 0x4329, 0xb0dc, 0xb050, 0x42d8, 0xb05c, 0x421d, 0x43c2, 0x42c6, 0x2ed9, 0x4088, 0xbf7e, 0x43c3, 0xba22, 0x40b5, 0x18ed, 0x4064, 0xbfd1, 0xaf28, 0xb0d5, 0x03d5, 0xb2d3, 0x2eb3, 0x10a5, 0x4298, 0xba28, 0x1329, 0x3160, 0xb01f, 0xaea8, 0x41fa, 0xb24a, 0x44c4, 0x05f4, 0x1eaf, 0xba36, 0xbf60, 0x1f18, 0x2c2c, 0x408b, 0x3269, 0x436e, 0xb2c0, 0x1392, 0xbfa1, 0x4220, 0x41f7, 0xbacc, 0xbad9, 0x0d2a, 0xb0bb, 0x4175, 0xb20e, 0x4007, 0xb225, 0x4152, 0xbf62, 0xb0f3, 0x4127, 0x1e7d, 0x4615, 0x375f, 0x4545, 0x4064, 0x40ed, 0x4015, 0x4082, 0x404a, 0xb211, 0x4073, 0x41f6, 0x42ae, 0x409d, 0x4348, 0x068b, 0x1364, 0x42af, 0x45c6, 0x1fdc, 0x25a7, 0xb24c, 0xa940, 0x1cc5, 0x40d4, 0x4006, 0xbfca, 0x4352, 0xb288, 0x2129, 0x438b, 0xb2f9, 0xa900, 0x40a9, 0xbf03, 0x456d, 0x2efc, 0xbad4, 0x1f74, 0x4204, 0x421f, 0xba1f, 0x42a2, 0x404d, 0x2d74, 0x406b, 0x2fb9, 0x4334, 0xbfd6, 0x40c5, 0x4105, 0x42a6, 0xbf00, 0x4024, 0x406a, 0x43c7, 0x4456, 0xb26d, 0x4034, 0x4230, 0x0f59, 0x4065, 0x4037, 0x0ee0, 0xadf8, 0xbf7c, 0xb2e3, 0x43b7, 0x429c, 0x40e0, 0x4338, 0xba25, 0x424e, 0x005a, 0x400e, 0x407e, 0x43e8, 0xbf0d, 0x4270, 0x1dd0, 0x4021, 0x1eb2, 0x4381, 0xae1b, 0x4122, 0x0dc4, 0xba5e, 0xbf79, 0x46da, 0x1bb1, 0x1117, 0xb03a, 0xa9c5, 0xb0de, 0x1e18, 0x42c2, 0x4043, 0x42f9, 0x4151, 0x013b, 0xbf00, 0xbf76, 0xb2c1, 0x4263, 0x40fd, 0xb02f, 0x41e1, 0x4484, 0xbfd8, 0x27ae, 0xba27, 0x430f, 0x283f, 0x4331, 0xb254, 0x4183, 0x1f9f, 0x1a56, 0xb264, 0xbf70, 0x2af2, 0x4390, 0x032a, 0x4383, 0x31cd, 0x18b8, 0x32ac, 0x41de, 0x43b5, 0xb2a0, 0x434f, 0xbf68, 0x466f, 0x3cd0, 0x4398, 0xb2a7, 0x4225, 0xb204, 0x4445, 0x4446, 0xba17, 0x4102, 0x11e8, 0x1f7d, 0xbf29, 0x438c, 0xb242, 0xb23c, 0x4016, 0x41ee, 0xbf15, 0x0f9b, 0x2845, 0x431f, 0x4012, 0xa905, 0x43bd, 0x40a7, 0xb21c, 0xbfa0, 0xb218, 0x41fb, 0x21c7, 0x1e5c, 0x2eba, 0x26a0, 0xba1e, 0x42a2, 0xbf23, 0x1c9b, 0x3ddb, 0x1ade, 0x421e, 0xb2ae, 0xa855, 0xbacc, 0x4067, 0x4670, 0xb299, 0x4633, 0x4009, 0xbfe2, 0x4005, 0x37ee, 0xbfc0, 0x0bf7, 0x439b, 0x4099, 0x425a, 0x1ebc, 0x445b, 0x4373, 0xba1c, 0x41b7, 0xb2d8, 0xb2b6, 0xbf34, 0x4273, 0x4121, 0x435f, 0x4158, 0x4021, 0x4231, 0x0c75, 0x4369, 0x3a94, 0xb247, 0x43c7, 0x4556, 0x4186, 0xb22d, 0xb25c, 0x42f0, 0x31d2, 0xbf46, 0x4062, 0x2ee4, 0x0296, 0x41f3, 0x4173, 0x4295, 0xb2db, 0x1d70, 0xbf84, 0x29a9, 0x2a9d, 0x41a0, 0x400d, 0xbf80, 0xb2a8, 0x1d70, 0x430d, 0xb22b, 0x4299, 0x0a23, 0x402a, 0xb275, 0xbf8c, 0x4405, 0x41bc, 0x43dd, 0xbf80, 0xb258, 0x4231, 0x1842, 0xa26e, 0x1e27, 0xb2ad, 0xb064, 0x4057, 0x46a1, 0x43e5, 0x439b, 0x4011, 0x41ce, 0x194e, 0xba1f, 0xb004, 0xb26b, 0x43ff, 0xb253, 0x4287, 0x402a, 0x40b4, 0x3a6d, 0xbfd4, 0xba49, 0x4172, 0x4043, 0x1e30, 0xb24c, 0xbad8, 0x4061, 0x418f, 0x1940, 0x4180, 0xbf7d, 0x23b5, 0x42a2, 0x40f9, 0x1e06, 0xbf99, 0xba15, 0x4164, 0xbac4, 0xb246, 0xa06a, 0x1923, 0x4553, 0xba1e, 0xbfa0, 0xab04, 0x4290, 0x4298, 0x3dc9, 0x4031, 0x40f5, 0x43aa, 0x329f, 0x45ae, 0xb209, 0xb2cf, 0xbf9d, 0x4123, 0x400e, 0xbae6, 0xb0c4, 0x05a3, 0x4353, 0x4057, 0xb060, 0x424c, 0x4189, 0xb2ec, 0x401f, 0xb2be, 0x1e8c, 0x4656, 0xb05d, 0x0fb9, 0x4069, 0x42b0, 0xb21f, 0xb277, 0x43f0, 0x4071, 0x419c, 0x22b1, 0xb02f, 0xa8c1, 0xb2ae, 0xbf84, 0x2d2c, 0x1df6, 0x4617, 0xae87, 0xb20f, 0x06bb, 0x420e, 0x0ba4, 0x4440, 0x422d, 0x4207, 0xb0ac, 0x431e, 0x1829, 0xb23b, 0xb23a, 0x41ed, 0x4016, 0xb023, 0x19db, 0x4351, 0xb2b8, 0x4151, 0xbf73, 0x4607, 0xaa70, 0x465a, 0x4242, 0xba3e, 0xb27f, 0x43ce, 0x0c36, 0xa72f, 0xb29d, 0x415d, 0xbfb3, 0xb0a3, 0xb2da, 0x429d, 0xb2df, 0x408c, 0x445a, 0x43fc, 0x4022, 0x4262, 0x1782, 0x4424, 0x40ef, 0x39b4, 0x1cf4, 0xb22d, 0x41a7, 0xba1e, 0x40be, 0x40ac, 0x36a1, 0xbae3, 0xbfb1, 0xac8c, 0xb23c, 0x40ca, 0xb203, 0x1d66, 0x117b, 0xb2a0, 0xba12, 0x401b, 0xaafd, 0xbaed, 0x1f75, 0x1c02, 0x42b4, 0x1f7f, 0x1d3c, 0xbad4, 0xbf24, 0x020b, 0x4226, 0x425f, 0xb27e, 0x3ed4, 0xba40, 0x2724, 0x4006, 0x107b, 0x2d4b, 0x464d, 0xb215, 0x41b4, 0x4172, 0x42dd, 0xa957, 0xba2e, 0x3f84, 0x1fdb, 0x290d, 0x43e3, 0xbf99, 0xb231, 0x422c, 0x02c8, 0xbacb, 0x432c, 0x1b38, 0xa296, 0x424f, 0x4640, 0x4175, 0xbf5b, 0x436b, 0x428b, 0x40a1, 0x4629, 0x40cd, 0xbfa4, 0x097e, 0x42e0, 0x4566, 0x4063, 0x3ef3, 0x464e, 0xb25c, 0x04b0, 0x4272, 0x41b1, 0x422d, 0xb070, 0x4254, 0xb034, 0x40b5, 0xb2a4, 0x41f0, 0x3546, 0xbf36, 0x40be, 0xba51, 0x437e, 0xba32, 0xbf60, 0x04b2, 0x16eb, 0x4092, 0x444f, 0x4634, 0x405a, 0x420b, 0x4342, 0x46b0, 0x1e8d, 0x4206, 0xba4c, 0xba29, 0x45d9, 0x1cd4, 0xbf8f, 0x42e7, 0xb0c7, 0x047c, 0x1f41, 0x4132, 0x4125, 0x23aa, 0x26ae, 0x294f, 0xae1f, 0x408d, 0x1aaa, 0xb052, 0xb2d6, 0xb2bf, 0x437e, 0xaf3c, 0x42ab, 0x2fe4, 0xb0d7, 0xbfc0, 0x38cb, 0x428e, 0xb2c3, 0xb081, 0x429f, 0xb220, 0xbf03, 0x4018, 0x1e76, 0x4167, 0x14fc, 0x42a0, 0x4642, 0x4543, 0xba6d, 0x1e81, 0x42fa, 0xbaeb, 0xbae0, 0xbac6, 0x0364, 0x0be5, 0xb2f1, 0x456b, 0xb225, 0xb2ba, 0x426a, 0x4359, 0x40db, 0xbf05, 0x4255, 0x147a, 0x4072, 0x0e8c, 0x0c55, 0x4178, 0xb28d, 0x4272, 0xba3b, 0x38c8, 0x41af, 0x46b8, 0x4075, 0x435c, 0x1b06, 0xbaeb, 0x3f2b, 0x0271, 0x310e, 0x424e, 0x013d, 0x4038, 0x2416, 0x35e0, 0x428d, 0xb0b8, 0x424a, 0x4380, 0x43e2, 0xbf3e, 0x4115, 0xb079, 0x4134, 0x3588, 0x20b7, 0xaa55, 0x43d3, 0x1ff2, 0x1ce9, 0x420e, 0x1ba2, 0x4007, 0x4327, 0xb0f2, 0xb082, 0xbfd4, 0x4390, 0xb233, 0xb2b2, 0xaa54, 0xb2d9, 0x4142, 0x4065, 0x41a9, 0x43fc, 0x42c6, 0x0daa, 0xb29e, 0xa5ae, 0xb288, 0x4241, 0xb237, 0x4303, 0x189b, 0xbf1e, 0x0a78, 0xbace, 0x0a87, 0x4154, 0x31e7, 0xbf3b, 0xba28, 0x419c, 0xba17, 0x3f48, 0x3ef8, 0x42f1, 0x4104, 0x4605, 0x413c, 0x4558, 0xb22a, 0xb287, 0xba47, 0x33a1, 0x430d, 0x42aa, 0xbf32, 0xb270, 0x42b3, 0x4392, 0x0bbe, 0x43bf, 0x41fc, 0x19ee, 0x2639, 0x42ca, 0x4219, 0xb2ec, 0x4600, 0x436c, 0xb011, 0xbaea, 0xb218, 0x412a, 0x41d8, 0x2cb9, 0x41b7, 0x4001, 0x0d3f, 0x1bc5, 0xbf03, 0x42d0, 0x4091, 0x40bd, 0x1ecd, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xaf3cc1f1, 0x05fac305, 0x3db327e1, 0x36ea8c1c, 0x589f9f57, 0xa4ce47b1, 0x8ee4ab13, 0xd31f4de3, 0xa8944d30, 0xd6eab546, 0xbc8af831, 0x69b223cd, 0x24711ddc, 0x33c795e8, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0x00059360, 0x00000060, 0x00000000, 0x00002c9b, 0x00003d09, 0x0000005d, 0x00000039, 0x00000fff, 0xa89457bc, 0xffffffdb, 0x69b23619, 0x69b23619, 0xcd056b3d, 0xa89454e4, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x2427, 0x3f90, 0x2091, 0x2633, 0x40e5, 0x429e, 0xb272, 0x4572, 0xbacb, 0x358e, 0xb054, 0x426b, 0xb2ad, 0x17b5, 0xbae7, 0x403b, 0xb2af, 0x199f, 0xa0e1, 0x148a, 0xb068, 0x40ac, 0x1c34, 0x41cd, 0x4387, 0xbfa8, 0x42c7, 0xa9ac, 0x2d10, 0xbae3, 0x419c, 0xba60, 0x004e, 0xb0c7, 0x18c0, 0x42fd, 0x4021, 0x4592, 0x4365, 0x4591, 0xba50, 0x4440, 0xa24c, 0x438b, 0x1aea, 0x404f, 0x4171, 0xbf8f, 0x27e3, 0x4170, 0xb024, 0x4156, 0xb2c5, 0x4361, 0x2b01, 0x42af, 0x43a7, 0x1c67, 0xb088, 0xaf29, 0xba76, 0x45a6, 0x1daf, 0x06d5, 0x40b0, 0x40d8, 0xba5c, 0x1cb8, 0x185a, 0xa518, 0x43ce, 0xbf76, 0x4378, 0x4216, 0x43cc, 0x3321, 0x40af, 0xbae8, 0x41a2, 0x40ac, 0x4602, 0xbf00, 0x1919, 0x40d8, 0x1df2, 0x1eac, 0xbf07, 0x18df, 0x41ea, 0xba54, 0x4211, 0x2835, 0xb230, 0x2c47, 0xb2aa, 0x4118, 0x4013, 0x4327, 0x3e32, 0xbfd0, 0x4351, 0x41ba, 0x40c9, 0x4174, 0x181a, 0x423a, 0xb2ad, 0x424d, 0x4296, 0x4232, 0xbaf5, 0xba6d, 0xb0ef, 0xbf48, 0xbafe, 0x428b, 0xba5f, 0x3c65, 0xba32, 0x0379, 0x3701, 0x40bb, 0xbae9, 0x4164, 0x42f8, 0x435a, 0x042a, 0xb2d2, 0x4159, 0x425d, 0x46cb, 0x1db3, 0x4463, 0x4299, 0xbf0c, 0x37c3, 0x0331, 0x19be, 0x406b, 0x41ad, 0xb22c, 0x43bc, 0x4195, 0x4351, 0x4227, 0xb097, 0x42b8, 0xb2a9, 0x408e, 0xb0c5, 0x411b, 0xbfcd, 0xb200, 0x4113, 0x40d2, 0x35f4, 0x1bb3, 0xbf97, 0x1d53, 0x41ad, 0x1c7d, 0xb283, 0x43c9, 0x419b, 0x4263, 0xb0a4, 0x1f68, 0xa4bb, 0x4005, 0xbac2, 0x3f7c, 0x2f53, 0x4192, 0xb05d, 0x03fb, 0x4619, 0xba2e, 0xb2ad, 0xb038, 0x0315, 0xbf1c, 0x43ba, 0xb0da, 0xbfb0, 0x1836, 0xb08d, 0xb066, 0xb0a2, 0x4132, 0x1883, 0x4688, 0xac7b, 0x35bc, 0x4016, 0x40b4, 0x40fe, 0x09dd, 0xb2b4, 0x41b9, 0x4331, 0x46d8, 0xbfd4, 0x1b67, 0x427f, 0x0d12, 0x4141, 0x3ca3, 0x1bf4, 0x406f, 0xbf04, 0x425f, 0xa73c, 0x1fd5, 0x441b, 0xb04a, 0x0fa5, 0xbf23, 0x4407, 0xbfb0, 0x24d0, 0xb073, 0x4283, 0xbf16, 0x061a, 0x41d7, 0x2266, 0x41ef, 0xb2ef, 0x4324, 0x40ba, 0x0c29, 0xb08c, 0x4160, 0x43e0, 0x439a, 0x3f33, 0xb03e, 0xb264, 0x4275, 0x45f2, 0x070e, 0x41b4, 0x43d4, 0x4127, 0x2d76, 0x0c90, 0x4207, 0xbf97, 0x4250, 0x4221, 0x4017, 0xbac8, 0xaf4c, 0xbac6, 0x1d9f, 0x4148, 0xa3e9, 0xa244, 0x4013, 0x1ffe, 0xbf6a, 0x4031, 0x41d6, 0xaa58, 0x40cc, 0x328a, 0x437e, 0xb06f, 0xbaeb, 0xbf15, 0x4106, 0x06a9, 0x41fd, 0x0967, 0x3300, 0x22da, 0x41c8, 0x45d4, 0x4030, 0xba6e, 0xb042, 0x1fc5, 0xba63, 0xa4e4, 0x2c03, 0xbfa0, 0x3858, 0xb2c5, 0xb0d9, 0x4067, 0x42c8, 0x4225, 0x0778, 0xbf07, 0x42a6, 0xb273, 0x4048, 0x42a1, 0x1125, 0x41ea, 0x051b, 0x19e4, 0x42c7, 0x09cd, 0x1a48, 0x0be5, 0xb271, 0xbf9e, 0x4167, 0x4471, 0xb2ed, 0x4058, 0x423d, 0x219d, 0x43bc, 0x1bbb, 0x22c4, 0x1e06, 0x4153, 0xb267, 0x2bde, 0x1d6b, 0x40db, 0x41cf, 0x437e, 0xba1a, 0x166c, 0xb068, 0xb23a, 0xbf77, 0xb238, 0x1fde, 0x4335, 0x4640, 0x409e, 0x42b4, 0x434f, 0xbad5, 0x2036, 0x4314, 0xb2e2, 0xb2fa, 0xb020, 0x41f7, 0xba26, 0xb002, 0x1200, 0x4326, 0x32db, 0x156e, 0xb2e7, 0x424f, 0x4079, 0xbfab, 0x3d18, 0x447a, 0x10c9, 0x454b, 0xa020, 0xbf70, 0xbfa9, 0x4336, 0x3cc4, 0x2dc9, 0x423d, 0x46ab, 0x4150, 0x1847, 0x40ec, 0x0a6d, 0xbf19, 0x36e4, 0x436a, 0x436c, 0x1efc, 0x4200, 0xbfc5, 0xa281, 0xbac7, 0xb0e5, 0x41bf, 0x4119, 0x13e8, 0x4142, 0x43e0, 0x40bd, 0x0769, 0x41d6, 0xa91c, 0xb2db, 0x4078, 0x41e3, 0x40e5, 0x1b36, 0xb247, 0x42cb, 0xbf75, 0x4175, 0x4258, 0x4276, 0x4572, 0x4237, 0x1eac, 0xb2ca, 0x4171, 0x40b8, 0x42d1, 0x4293, 0x41bd, 0xbfe0, 0x34d8, 0x4024, 0x4128, 0xa7bb, 0x43c5, 0x4342, 0x42a0, 0x0306, 0x42cb, 0x4082, 0xbf70, 0x0fa2, 0x43ef, 0x45d6, 0x4300, 0xbf94, 0x077c, 0xbaf3, 0xa980, 0x33d4, 0x3b98, 0x18ab, 0x435f, 0x4310, 0xb0a1, 0x40e1, 0x149a, 0xaab9, 0x422a, 0xb2df, 0xbf80, 0x4592, 0x086f, 0x337d, 0x1dd5, 0x09b8, 0x197f, 0x45a0, 0x422d, 0xb084, 0xbf19, 0xbaf5, 0x04e9, 0xba29, 0x194a, 0x1a57, 0x32bd, 0xa80e, 0xb2c8, 0x3542, 0x2086, 0x422c, 0xba36, 0x46ab, 0x14bc, 0x20b4, 0x402f, 0x40ff, 0x46e1, 0x40a0, 0x43fc, 0x41ce, 0xbf4f, 0xb26f, 0x415b, 0x1523, 0x061f, 0xb090, 0x06ec, 0xb2eb, 0xb292, 0x426f, 0x42db, 0x167a, 0x43a3, 0x424f, 0xb247, 0x1b40, 0x1e25, 0x4087, 0xbad4, 0x431f, 0x435d, 0x006f, 0xbfb1, 0x40b3, 0x39d4, 0x4291, 0x165f, 0x42b8, 0x4200, 0x42aa, 0x43f5, 0x154e, 0xa5de, 0xba50, 0x3da7, 0x0d4e, 0xa9bf, 0xba1c, 0x402b, 0x418c, 0x4360, 0xb056, 0x3ef6, 0xbfcd, 0x4038, 0x1a87, 0x2473, 0x4317, 0x4285, 0x40c7, 0x40f6, 0xbf8b, 0x4077, 0x431a, 0x05ad, 0xba42, 0x4040, 0x16d8, 0x1913, 0xb265, 0x4436, 0x4102, 0x40fd, 0xa3f4, 0xba27, 0x4109, 0x40b2, 0xb035, 0x437e, 0xbf84, 0xba37, 0x189e, 0x0bee, 0xba76, 0xb2d0, 0xb204, 0xa150, 0x41a7, 0x41bb, 0xb295, 0x33f3, 0xbf80, 0x2f8a, 0x459b, 0xabe4, 0x11a0, 0x426b, 0xba78, 0xb272, 0x3718, 0x43c8, 0xbfb7, 0xb224, 0x1ec8, 0x06d7, 0x4327, 0x273c, 0xb2fe, 0x4294, 0x179f, 0x42a5, 0x4131, 0x43f0, 0xb25d, 0x4298, 0x42b2, 0x40b1, 0xbff0, 0x0bbe, 0xbf22, 0x2944, 0x4147, 0x0969, 0x433c, 0x1b91, 0x419b, 0xa0b4, 0x429a, 0xaa98, 0xa7ae, 0xbfad, 0x430e, 0xb2b8, 0x40dc, 0x403f, 0x19b8, 0x41f4, 0x1da0, 0x406c, 0x40aa, 0x1e72, 0xbfc0, 0x41e6, 0x43a2, 0x40d6, 0x1a1a, 0x191d, 0x1b1b, 0x2f8f, 0xb0d0, 0x1ce2, 0x4375, 0xb04b, 0xba0c, 0x4146, 0x4305, 0x40b4, 0xbf1b, 0x42d1, 0xb2ae, 0x4347, 0xb22e, 0x42ad, 0x41df, 0x1d6d, 0xb2b8, 0x409b, 0x38ce, 0xba78, 0x2add, 0x4377, 0x3fea, 0x42d5, 0xb2a0, 0xac6f, 0x3f00, 0x42e0, 0x42e4, 0x121d, 0x411a, 0xba59, 0x43b2, 0xb069, 0x4310, 0xb297, 0xbf88, 0x41ac, 0xb033, 0x1884, 0xb030, 0x42fa, 0x2a9b, 0x419a, 0xb270, 0x014f, 0x4100, 0x414a, 0x42b2, 0xb264, 0x43a7, 0x2e7d, 0x417d, 0x30eb, 0x4367, 0x429d, 0x3747, 0x3eda, 0x3129, 0xbfb4, 0x1f57, 0x0b56, 0x4039, 0xb26f, 0x462f, 0xb26f, 0x4181, 0x445f, 0x46c3, 0xb033, 0xbfb0, 0xb201, 0x422b, 0x4017, 0x431d, 0x4274, 0x40b4, 0x4481, 0x44a2, 0xaeb3, 0x42ad, 0x41ec, 0x40f8, 0xb2a7, 0x294e, 0xbf64, 0x41b6, 0xbac6, 0x423e, 0xb0b6, 0xba73, 0x45cc, 0x1d55, 0xb2f8, 0x3cee, 0x187b, 0x0e9e, 0x41b7, 0x42ce, 0xa773, 0xb2d4, 0x18dc, 0x207b, 0x4050, 0x4198, 0x3ee7, 0x40ea, 0xb243, 0x18a8, 0xbfe2, 0xba17, 0xbf00, 0x2883, 0xb06f, 0x40ce, 0x42fc, 0xba28, 0x4201, 0x1c4e, 0xbf80, 0xbf19, 0xae7d, 0x406a, 0x0bf7, 0xb0a2, 0xbf70, 0x4683, 0x4319, 0xbad2, 0xa731, 0x43ae, 0xba75, 0x426d, 0x2679, 0x2eeb, 0x149d, 0x45c5, 0x2848, 0xb229, 0xada6, 0x3a2c, 0x4002, 0x4098, 0xbf58, 0x4004, 0x43dc, 0x4585, 0x28bf, 0x4291, 0x4025, 0xba3d, 0xb242, 0x310b, 0xba76, 0xb2da, 0x4275, 0x41a4, 0x44b2, 0xb283, 0xb251, 0x3af7, 0x4188, 0x424d, 0xbf47, 0x38f5, 0x4602, 0x402d, 0x15f2, 0xb0e8, 0x4364, 0x2c36, 0x14be, 0x40a0, 0xbf4b, 0x40c8, 0x4150, 0x3428, 0x3375, 0xa475, 0x43c8, 0x4129, 0xbf00, 0x40de, 0x415d, 0x4235, 0x43fb, 0xb0f8, 0x4120, 0xbad0, 0x43b6, 0x4220, 0xb0d3, 0xba72, 0x0b42, 0xbf00, 0x421d, 0xb2d2, 0x0ccc, 0xb2ee, 0x2fb3, 0xaa42, 0xb0b1, 0xbf54, 0x4182, 0x40ce, 0x1fc6, 0xb291, 0x133c, 0x40ac, 0x405f, 0xbfca, 0xac9d, 0x4024, 0x0355, 0x43d6, 0x46b4, 0xb254, 0x1e21, 0x1824, 0x41f8, 0xbf56, 0xb0f4, 0x4396, 0x3777, 0xb01f, 0x42a3, 0xb2c6, 0x41ef, 0x0db1, 0xb0fb, 0xba3f, 0xa7bf, 0x4217, 0x0bf8, 0x4327, 0x4368, 0xbfc0, 0x42e1, 0xb0fe, 0xba62, 0x427f, 0xbf80, 0x3777, 0xb2f7, 0xbf16, 0x42a0, 0xaa78, 0x42d0, 0x41d5, 0xa44e, 0xbadd, 0xb0be, 0xb2e0, 0xba54, 0x4121, 0xbfd0, 0x2647, 0x4574, 0xba1c, 0x20a2, 0x1ae6, 0x2e30, 0x43e5, 0xb27d, 0xb0ee, 0x4053, 0xb2b6, 0x1c72, 0xbf70, 0x1562, 0xbfa9, 0x1f12, 0x4198, 0x4297, 0xb09b, 0x440e, 0x1eb9, 0x2ec0, 0xbfa4, 0x1c9d, 0xba34, 0x428e, 0x22cb, 0x4313, 0xb29f, 0x418d, 0xb27b, 0x4664, 0x1daa, 0xba05, 0xb230, 0xbf07, 0x314a, 0x42c9, 0x03a2, 0x4198, 0x1c5f, 0xb0aa, 0xb078, 0x404d, 0xb2b7, 0x4204, 0x4378, 0xbf09, 0xbfe0, 0x4307, 0x41fa, 0xba3f, 0xba74, 0xb042, 0x0f95, 0xb251, 0x44d8, 0x0fa5, 0x42a6, 0x0927, 0x1e0f, 0xbfd9, 0xb0ac, 0xa99d, 0x3a4b, 0x3435, 0x4154, 0x4046, 0xb231, 0xb201, 0x19c1, 0x418b, 0x0d1a, 0x4000, 0xb032, 0xbfc1, 0xb257, 0x2c04, 0xb029, 0xba42, 0x42ab, 0x2783, 0x1c91, 0x2f6d, 0x40cb, 0x1ac3, 0x4093, 0x45da, 0x434d, 0x4429, 0xbf55, 0x419c, 0x41c1, 0x43f2, 0x3a64, 0x4041, 0x183f, 0x4103, 0x4057, 0x41ec, 0x4268, 0x20b0, 0xb294, 0xba16, 0xb238, 0xbf0a, 0x4622, 0x432e, 0x3ed4, 0x46a2, 0xb239, 0xb2ca, 0xb2bb, 0xbfa3, 0x41fb, 0x400b, 0x42f9, 0x4296, 0xb215, 0x11b5, 0xb292, 0xbfcb, 0xab70, 0x4088, 0x18fc, 0x40fa, 0x31cf, 0x44c0, 0x1a3e, 0x20a8, 0x36f0, 0x421a, 0x4547, 0x4101, 0xb259, 0x4094, 0xa222, 0x1d0f, 0xbfc0, 0x0582, 0xb080, 0xb214, 0x4605, 0x407d, 0x1a4a, 0x400d, 0x4168, 0xb091, 0x1ef9, 0x424b, 0xba65, 0xbfc3, 0x4033, 0x4258, 0xb043, 0x4095, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x9f18068a, 0x03643f9c, 0x4d4c0930, 0xb5ba15e8, 0xff9da78d, 0x8731d796, 0x094b9e5e, 0x291d98f7, 0x1392fdc3, 0x4fe11ddf, 0x2f0cc7b5, 0x2d5bda6f, 0x2d85b32d, 0x081e2031, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0xffffffa0, 0xffffff96, 0x00000000, 0x00000060, 0x00000000, 0x00000000, 0x000000f0, 0xffffff99, 0x2fc23bbc, 0x2d85b418, 0x0000d42f, 0xc7ffffff, 0xf7e1d472, 0x081e259d, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb235, 0x3f52, 0xb2ed, 0xa9b5, 0xbf53, 0x2bef, 0x1829, 0x2f6e, 0xba3a, 0xba63, 0x42c9, 0x41fa, 0xbf22, 0x415e, 0xa64f, 0xb072, 0xb2fe, 0x2efd, 0x4080, 0x0976, 0xb299, 0x4673, 0xb22c, 0x436d, 0xb2f3, 0x41f3, 0x02e9, 0x1955, 0x43fa, 0x411d, 0x4197, 0x41b0, 0x40cd, 0xbae5, 0xbadf, 0xbf42, 0x41ed, 0x41ea, 0xb240, 0xba2a, 0xb0eb, 0x4049, 0x40e4, 0xb223, 0x4361, 0x430c, 0x4144, 0x17db, 0x4053, 0xb284, 0xb0f5, 0x4074, 0x401b, 0x213c, 0x40eb, 0x4331, 0xb27f, 0xb2ba, 0xb2c6, 0x428e, 0x40c1, 0xbf8b, 0xb239, 0x41c2, 0x43a2, 0xb2f8, 0x4193, 0x4005, 0x432e, 0x4596, 0x44b9, 0x43a5, 0xb288, 0x3e1e, 0x449d, 0xb2b7, 0x1b4f, 0x2bc5, 0x43f4, 0xba10, 0x409c, 0x4090, 0x414c, 0x0672, 0xbfd4, 0x4356, 0x44b3, 0x424e, 0x1fb8, 0x4434, 0xb00d, 0x4045, 0x2fcd, 0x40fb, 0xba20, 0x41ae, 0x4266, 0xba12, 0x407b, 0x420a, 0x1a0f, 0x2281, 0x31c6, 0x1abf, 0x1d76, 0x41b8, 0x4055, 0x45c4, 0x43cc, 0x40c8, 0x4165, 0x163e, 0xbf1f, 0xba6c, 0xacaf, 0xba04, 0x412c, 0xb2a6, 0x42fb, 0xbaea, 0xb0ac, 0x446b, 0x4381, 0xa37d, 0xbf27, 0x4163, 0x42f8, 0xb24c, 0x41d5, 0x182b, 0x426e, 0x40e5, 0x3fa2, 0xbf9f, 0xba3c, 0x2e88, 0x4362, 0x3a89, 0xb2f3, 0xbacc, 0xb218, 0x3a16, 0x2c8f, 0x42f4, 0x43a4, 0xbf2c, 0x2016, 0x0105, 0x2c60, 0x425d, 0xbf17, 0x443d, 0x19cd, 0x21e8, 0x2803, 0x402d, 0x40e0, 0x1330, 0x1a90, 0x42e5, 0x151c, 0xb022, 0xbf47, 0x1f76, 0xba71, 0xb207, 0x40f2, 0x4172, 0x0965, 0x42af, 0x407f, 0x4162, 0x4355, 0x13d1, 0xbfa0, 0x0910, 0x4574, 0x42f4, 0x206f, 0x431d, 0x437c, 0x42a0, 0x4108, 0x4469, 0x21dc, 0x42d4, 0x3b95, 0xbad0, 0x420f, 0x1295, 0x428b, 0xbfd6, 0x05f7, 0xbf00, 0x4685, 0x4193, 0x463c, 0x4190, 0xbf5e, 0xba3e, 0xb2b8, 0xbafb, 0x07a5, 0x4320, 0x4161, 0x43d8, 0x004d, 0x40f4, 0x4040, 0xb253, 0x412b, 0xb230, 0x408f, 0xbf00, 0x021f, 0xbfa0, 0x42aa, 0x3978, 0x3bc6, 0x1e67, 0x406f, 0x4161, 0x4628, 0xad0c, 0x4232, 0xba05, 0xb2ea, 0xbf45, 0x2bbd, 0x224d, 0x43ce, 0x4262, 0xbfe1, 0x0ac9, 0x42d2, 0x4592, 0x2030, 0x3981, 0xbf7d, 0x3d39, 0xae51, 0xbfd0, 0x4161, 0x41e6, 0x414d, 0xba32, 0x4680, 0x311a, 0x18e4, 0x1f85, 0xbf64, 0x1a28, 0xbadf, 0x46e4, 0xa5c9, 0x4199, 0x3d07, 0x0825, 0xbf2d, 0x40e5, 0x21ec, 0x0b46, 0x430a, 0x1cd7, 0xa4e2, 0x4396, 0x4370, 0x2c8a, 0xbf04, 0xb273, 0xba2e, 0x423f, 0x428d, 0x1f54, 0xbfc6, 0x41cc, 0x1aa1, 0x402c, 0x4397, 0x4175, 0xb287, 0x4222, 0x46c3, 0x305a, 0x421f, 0x41c1, 0xb283, 0x43a0, 0x431f, 0x1ec0, 0x43fa, 0x0821, 0xba45, 0x405d, 0xb0e4, 0x407e, 0xbfbf, 0x00ab, 0xb22b, 0x309d, 0x4269, 0x4021, 0xb09e, 0xbf0d, 0xb251, 0xbae7, 0x42de, 0x41d1, 0x4132, 0xb08a, 0xa1ec, 0x1fc5, 0xb09a, 0x4332, 0xbfd0, 0x26f4, 0x4069, 0x42e6, 0xb275, 0x1ae3, 0x4350, 0xbf31, 0x3b04, 0x1f7a, 0x4152, 0xbaca, 0x1f2f, 0xb032, 0x1d51, 0x44cc, 0x4016, 0x18ce, 0x41fc, 0x1d3e, 0x409d, 0xbf4b, 0x4241, 0x2393, 0x4139, 0x4374, 0x43c6, 0x40fb, 0x41ba, 0x370d, 0xb205, 0x42d5, 0x4231, 0xb2c5, 0x4253, 0x42c6, 0x40ea, 0x38fd, 0xb2f0, 0x1002, 0x1676, 0x42bc, 0xbfdb, 0x1c6c, 0x0b4f, 0xb230, 0x41ee, 0x4233, 0x29f8, 0x191e, 0xba4a, 0x408e, 0x45bd, 0xb20f, 0x3a1d, 0x2891, 0x3659, 0x127e, 0x43a2, 0x167c, 0xb2b5, 0xb298, 0x43b5, 0xb2ba, 0x4344, 0x426e, 0xb259, 0x1293, 0xbf25, 0x4134, 0x2a59, 0x40d7, 0xafc9, 0x2b8d, 0x42da, 0xb03d, 0xb000, 0x4656, 0x420a, 0x2c8d, 0x065e, 0x4252, 0x43c6, 0x458b, 0xa5fd, 0x4111, 0x42fd, 0xbf85, 0xbaff, 0x4032, 0xb2b7, 0xb236, 0xb044, 0xb205, 0xbae5, 0x416e, 0x42e0, 0x412d, 0x1d40, 0x0fc4, 0x42e7, 0x428e, 0x4376, 0x1b9e, 0x263a, 0x42c3, 0x43a4, 0x08bf, 0x409a, 0x2124, 0x40f9, 0xb29c, 0xbf9d, 0x415e, 0x4003, 0x275f, 0xaf43, 0xba41, 0xa013, 0xb02a, 0x4398, 0x424f, 0x411f, 0x42ee, 0xa2da, 0x45f3, 0x440d, 0xb2ed, 0x429c, 0x4263, 0x1f66, 0x0f8d, 0x3500, 0xbf68, 0x40a6, 0xb2de, 0x43df, 0x40a1, 0xba52, 0xb0fb, 0x145d, 0x19ce, 0x43a1, 0x427d, 0x4493, 0x40cf, 0x4103, 0x463b, 0xb0cf, 0x4113, 0x458c, 0x1ac8, 0x030e, 0x42ef, 0x4381, 0x19ae, 0xa923, 0x4209, 0xbf91, 0xb01d, 0x199e, 0x1a59, 0x41d7, 0x4390, 0x414f, 0x4144, 0x19df, 0xa99a, 0x43d8, 0x4059, 0x400b, 0xba42, 0x4327, 0xb20f, 0x2ad1, 0xbfa6, 0x4335, 0xb2ef, 0x10be, 0x1845, 0xbfd8, 0xb2a8, 0x40f2, 0x405d, 0x42bd, 0x43c2, 0xabd0, 0x40f0, 0x4361, 0x413b, 0x1dcf, 0xb20d, 0x3168, 0xba5b, 0x43e1, 0x43a3, 0xb053, 0xba1f, 0xa340, 0xba35, 0x27b0, 0x45c6, 0x4093, 0x40d7, 0x43fd, 0x1f45, 0xbfb9, 0x424e, 0xaf65, 0x1215, 0xb247, 0xb220, 0x44d0, 0xb27c, 0xbf82, 0x232d, 0xa8e7, 0x42cb, 0x3cd3, 0x1aea, 0x4110, 0xbaee, 0x2c77, 0x4028, 0xb2f8, 0x2376, 0xb262, 0x0ccd, 0x4059, 0x40bc, 0xba54, 0x4367, 0xb2dc, 0x4012, 0x4012, 0x4221, 0x182a, 0x195a, 0xbf02, 0x4361, 0x04db, 0x427f, 0x44a2, 0x43c3, 0x41d8, 0x427f, 0x017f, 0xaffb, 0xbfc9, 0x426e, 0xb09d, 0x11ce, 0xb21b, 0xba66, 0x0ab4, 0x420b, 0x075f, 0xb25e, 0xbf95, 0x42bc, 0xb23a, 0x4070, 0x41b8, 0x43c6, 0xa5d7, 0x4271, 0x438a, 0xbf05, 0xac31, 0xbadf, 0x43e3, 0x1eb1, 0xb225, 0x4237, 0x4015, 0x425d, 0x07fc, 0xa809, 0x43b6, 0x43f8, 0x4330, 0x4028, 0xbf5a, 0x42c3, 0x1ce7, 0xb2eb, 0x42ef, 0x34ec, 0x4334, 0xba3d, 0x1d32, 0xb232, 0xbaca, 0xb23c, 0x4039, 0xadc4, 0xb0a5, 0xb2f2, 0x199a, 0x41f9, 0xb26c, 0x404a, 0xba1e, 0x40ac, 0xb044, 0xba2a, 0xbf35, 0xb015, 0xba51, 0xa8ac, 0x1963, 0x20b1, 0xbf39, 0x40ad, 0x40ed, 0x4151, 0x2d49, 0xb20b, 0xba2d, 0x1edd, 0xb211, 0x1f04, 0x4339, 0x4349, 0xb218, 0x4286, 0x43d1, 0xbfd3, 0xba2d, 0x41ce, 0x18d1, 0x4543, 0x1d0b, 0xa053, 0xbfe0, 0x15da, 0x2d45, 0x3d82, 0x4115, 0x04a6, 0x43b4, 0xb065, 0x282f, 0x0267, 0xa36f, 0x422f, 0x011c, 0xbf0e, 0x029e, 0xba71, 0xad75, 0x43c6, 0x4288, 0x42f7, 0x4616, 0xbf2b, 0x404e, 0x1a44, 0x30f1, 0xb09e, 0x4354, 0xb2c7, 0x4000, 0x3fd3, 0x18f4, 0x2e42, 0xb279, 0x4257, 0xb0f8, 0x42e9, 0x4065, 0x4021, 0xba0f, 0x4121, 0x419d, 0x40e4, 0x41c7, 0x259c, 0x4023, 0x435a, 0x1afd, 0x1f12, 0xba21, 0xbf4e, 0x4313, 0x254b, 0xb27e, 0x41f6, 0x4557, 0x4287, 0xb270, 0x42cf, 0xb07e, 0xbf94, 0xb2c9, 0x432f, 0x4397, 0xafbd, 0x3d40, 0xa8ae, 0x3690, 0x40b8, 0xafdc, 0xb064, 0x26fe, 0x43a3, 0x466f, 0x39e6, 0xb2ee, 0x0008, 0x4273, 0xba2e, 0x1d7b, 0x39d9, 0xbf48, 0xba57, 0x1c17, 0x0edb, 0x415e, 0xbfc6, 0x14c0, 0xbacf, 0xbacc, 0xacb0, 0x4359, 0xb21a, 0xbf5d, 0x415e, 0x41cf, 0x4423, 0x42f2, 0xb27b, 0x41ac, 0x4571, 0xbf00, 0xba7e, 0xb01c, 0x42db, 0x2818, 0x3d11, 0xbf90, 0x4186, 0xb0f6, 0xb0c3, 0xba49, 0x1c79, 0xbac8, 0x19b8, 0x407a, 0xa439, 0x1af9, 0x414e, 0xb227, 0xa3ab, 0xbf83, 0x4107, 0xba3e, 0x1e2f, 0xbfe0, 0x0b3b, 0x437d, 0x40b5, 0xba44, 0x45f2, 0x447f, 0xb2f9, 0xb2f6, 0x44a0, 0x4470, 0x4634, 0x42df, 0x1b5d, 0x426f, 0xbfb0, 0x4056, 0x1e3d, 0x1ad9, 0xa59d, 0xbfc0, 0x1404, 0xbf48, 0xb2f1, 0xbf61, 0x099c, 0x43c9, 0xba5a, 0xb278, 0x4002, 0x4083, 0x08c7, 0x42d0, 0x44eb, 0xa734, 0x462e, 0x4121, 0x4218, 0x4060, 0x402f, 0x415b, 0x464b, 0x441c, 0xabc9, 0xb255, 0xbaf4, 0xb2c5, 0x43c2, 0xbf7b, 0x1f61, 0x1dda, 0x42f6, 0xb22d, 0xbf72, 0x4604, 0x4384, 0x1ef3, 0x1d46, 0xbf05, 0xbafd, 0xb207, 0xaf8f, 0x4561, 0xba57, 0x406d, 0x421e, 0x420f, 0x41c5, 0x45e5, 0x1dbd, 0x1fc6, 0x41f5, 0x4203, 0x422f, 0x42c3, 0xb247, 0xb069, 0xbf5c, 0x414b, 0x41c3, 0x41fa, 0xbf00, 0xafae, 0x4215, 0xbf4a, 0x4000, 0x41b2, 0x1e3b, 0x2842, 0x133b, 0x42d9, 0x4165, 0x4262, 0x43b9, 0x2626, 0x41b8, 0x4295, 0xacbc, 0x2fcf, 0xbfe2, 0x2b38, 0x4020, 0x09e3, 0xbfae, 0x40bb, 0x4289, 0xb226, 0xbf17, 0xa215, 0x4326, 0x2a3b, 0x0daa, 0xbf98, 0xb045, 0x42b7, 0x462d, 0x43d2, 0xa357, 0xb20e, 0x401b, 0x0d93, 0xba3d, 0xba27, 0x3a82, 0xbf60, 0x1844, 0x4211, 0x4004, 0x42ac, 0xba4b, 0x282a, 0xb272, 0x42f8, 0xbf90, 0xa34e, 0xbf0f, 0x43f0, 0x2f72, 0xb06f, 0x461e, 0x1d80, 0x19fb, 0xada0, 0xa8f2, 0x3b75, 0xbf02, 0xb294, 0x0e2f, 0x0a11, 0xb21e, 0x1af3, 0x43b1, 0xa521, 0xb264, 0xbad0, 0x43fc, 0x42c9, 0x1877, 0x403d, 0x43fc, 0x0217, 0xbf0c, 0xb0cd, 0xb2a4, 0x4196, 0xb2ee, 0x2b9c, 0xb007, 0x41a1, 0xbf2c, 0xba44, 0xb06d, 0xba5d, 0x4063, 0x1c68, 0x45c8, 0xbf71, 0x4100, 0xb274, 0xb0c0, 0x4421, 0x3a36, 0x0655, 0x204b, 0x1fc2, 0xb2b6, 0x1c68, 0xb050, 0x1e86, 0x3b90, 0x3b9e, 0x2bb7, 0xa64d, 0xaf8e, 0xbf14, 0x4219, 0x074a, 0x43c7, 0x4179, 0x42ae, 0xb026, 0x4551, 0xbfc9, 0x427e, 0xa12c, 0x465b, 0x42c1, 0x419c, 0x2a70, 0x4200, 0x4217, 0x4437, 0x1842, 0xbf70, 0xb03c, 0xa47b, 0x40fd, 0x4623, 0x1ee8, 0xbf23, 0xb23b, 0x41a1, 0x40ff, 0x42be, 0x1b0b, 0x43a1, 0x4193, 0x4008, 0xba0d, 0x4392, 0x0f39, 0x46bc, 0x2b84, 0x462e, 0xbf0d, 0x426a, 0xb093, 0x3ef0, 0x2b53, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x284dbe99, 0x058f67cb, 0xb9dae013, 0xa1c1f55b, 0x52670afd, 0x96fcee8f, 0x8a2497af, 0xbd5ee36d, 0x01bd25ee, 0x57094880, 0x25509dd6, 0xeb081cb3, 0x37cda5c3, 0x8213e3ef, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x01ffe449, 0x00000000, 0x00000000, 0x65ffccb7, 0x000019a4, 0x49e4ff65, 0x49e4fe75, 0x00000000, 0x25508003, 0x57094880, 0x25509e4c, 0x000070de, 0x00000000, 0xffffa158, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1d00, 0x407e, 0x3525, 0x2982, 0x43ea, 0x4352, 0x4151, 0xbf5e, 0x09a3, 0x4196, 0xa5c3, 0x3298, 0xb061, 0x1d31, 0x4086, 0xbf05, 0xb040, 0x1336, 0x1c1e, 0xa99b, 0x1174, 0x3dc2, 0x4117, 0x40bb, 0x43a0, 0xbaec, 0xb0de, 0xb229, 0x4450, 0xbf05, 0x4142, 0x1f56, 0x4160, 0xb258, 0x438b, 0x4160, 0x2c59, 0x433f, 0x13c8, 0x35ae, 0x4398, 0x1eb1, 0x236d, 0x406a, 0x41cf, 0xbfd4, 0x417a, 0x41a3, 0x42d5, 0xb2f5, 0x410e, 0x41b9, 0x45bb, 0x4083, 0x4189, 0x41b7, 0x01e2, 0x1f77, 0xba53, 0x2388, 0x4561, 0x40c8, 0xbf1f, 0x401d, 0x007d, 0x26a6, 0x24e5, 0xb209, 0x4623, 0x4346, 0x403a, 0xb0b1, 0xba2d, 0x41d9, 0x1af2, 0x444e, 0xb0ea, 0x40f1, 0x01d5, 0xa4bd, 0xbf91, 0x41d9, 0x425c, 0x414e, 0x40d0, 0xba56, 0xb2ce, 0x0d2b, 0x1984, 0xb0bb, 0x3257, 0x2ba1, 0x43d1, 0x1f95, 0x43d6, 0x40d2, 0x1fa9, 0xba3f, 0xbaca, 0x4294, 0x174e, 0x40ef, 0x434e, 0x4089, 0x445b, 0x19e7, 0x42a7, 0xbfb9, 0x4056, 0xac80, 0x2696, 0x43f6, 0x465f, 0x425d, 0xba41, 0x4092, 0x251d, 0x1e2e, 0x40af, 0x4397, 0xbfb0, 0xbf9f, 0x4166, 0x444d, 0x41c4, 0xbaf0, 0x316d, 0x43db, 0x414b, 0x32c1, 0x325c, 0x41ab, 0xbaf1, 0xbf60, 0x08d1, 0x313c, 0x4387, 0x4613, 0xbf0b, 0x19f0, 0x411d, 0x4141, 0x11c6, 0xb2c3, 0x1f54, 0x41d1, 0xb030, 0xb227, 0x1d06, 0x355b, 0xbf21, 0x0f72, 0x41b6, 0x1f75, 0xb048, 0x4382, 0x43ff, 0x411f, 0x1bbf, 0xb263, 0x434d, 0x40c1, 0x1f58, 0x2d9e, 0x403d, 0x4341, 0x4379, 0xb2a8, 0x30d6, 0xbf08, 0x4027, 0x4312, 0xbacf, 0x4021, 0x414c, 0x4304, 0x1878, 0x2a19, 0x4688, 0x40a8, 0x146a, 0x410c, 0xb02d, 0xb2bc, 0x2063, 0x3346, 0x438f, 0x43fd, 0x4160, 0x1a4f, 0x4373, 0x4128, 0x4132, 0xb2c2, 0xb0ce, 0xa0e0, 0xbf3f, 0x4282, 0x423e, 0x41fc, 0xac39, 0xbfb8, 0xbfc0, 0x1996, 0x3e39, 0x0f2b, 0x1ece, 0xb069, 0x40fc, 0xbaf6, 0xbfc2, 0x0f93, 0xb0f4, 0x4346, 0x08d7, 0xba06, 0xb200, 0xba43, 0xba72, 0x0fc7, 0x1b92, 0xba08, 0x41f0, 0x19fe, 0xb256, 0x1bac, 0x4222, 0xb062, 0xbade, 0xb0aa, 0x436d, 0x1faa, 0x0663, 0xbf80, 0xbf53, 0x4195, 0x408b, 0x4034, 0xb2c6, 0x1a0f, 0xb2e8, 0xb291, 0x40b8, 0xbaec, 0x117c, 0x27ef, 0x4084, 0x405a, 0x40e7, 0xafea, 0x40d1, 0x1157, 0x4121, 0xb06f, 0x4135, 0x2090, 0x16e4, 0x4318, 0x1c80, 0x310f, 0xb0e2, 0x40cf, 0xbf8e, 0x407a, 0xb09c, 0x388f, 0x4299, 0x1fdd, 0xb23f, 0xbf15, 0x1f0a, 0x40b2, 0x42de, 0x2ca0, 0x424f, 0xa934, 0x40be, 0x1dda, 0xb04d, 0x0c79, 0xbac7, 0x18ec, 0x4300, 0x1a08, 0xb219, 0x2eab, 0xbf8e, 0x1a3c, 0xb228, 0xad72, 0x403c, 0xb000, 0x44b2, 0x23a8, 0x41a6, 0x43d7, 0x404d, 0x0805, 0x43e8, 0x459b, 0xa1b1, 0x45b3, 0x4159, 0x44b4, 0x09a2, 0x42a1, 0xbf7e, 0xb2d5, 0xb007, 0x391a, 0x1bb2, 0x45d1, 0x4179, 0x420f, 0xba1f, 0x1ad5, 0xb22f, 0xb05f, 0xb0a6, 0xb269, 0x279d, 0x41c9, 0x4012, 0x0e08, 0xbf11, 0xba49, 0x417d, 0x3e9d, 0x436e, 0x4189, 0x42fb, 0x43e7, 0x4229, 0x40f3, 0x1a1e, 0x1ef2, 0x1061, 0x133f, 0xbac4, 0xb211, 0x42f1, 0x46ab, 0xb25e, 0x4387, 0x404f, 0x400e, 0xb038, 0x4167, 0x4564, 0xb201, 0x4073, 0xb03c, 0x234a, 0xbfae, 0x46e0, 0xb20d, 0x1862, 0xbf88, 0x42b6, 0xb029, 0xa541, 0x406d, 0x4236, 0x413e, 0x45e5, 0x1b98, 0x4224, 0x40ad, 0x41d4, 0x4197, 0x0ec7, 0xbac8, 0x425b, 0x0254, 0xb28b, 0x328c, 0xba1a, 0x42db, 0xba4f, 0x418b, 0x4104, 0xb265, 0x40d9, 0xbf39, 0x41df, 0xb23c, 0x43bd, 0x43b3, 0x4312, 0x41de, 0xbf2b, 0x365d, 0x195c, 0x40ac, 0x0fea, 0x1dc7, 0x2d8d, 0x420f, 0x43bc, 0x41ac, 0xb0d0, 0xb279, 0x25fb, 0x4386, 0xb095, 0xa6c5, 0x4106, 0x3591, 0x41c0, 0xb06c, 0x43f2, 0x4037, 0x4388, 0x0cab, 0x4065, 0xa28e, 0xbf02, 0x4209, 0x1874, 0xb0c1, 0x4085, 0xb2c9, 0xb099, 0xb045, 0x4183, 0x13ed, 0x1dd4, 0xa5f9, 0x42d7, 0x1691, 0x436c, 0xa84e, 0x40e4, 0x439e, 0xb2cc, 0x0e65, 0xb238, 0x42b6, 0x3c33, 0xbf73, 0x1e26, 0xb2f2, 0x4376, 0x43dc, 0xbacf, 0x40dd, 0x431d, 0xba58, 0x2b96, 0x463a, 0x3767, 0x42cb, 0xb0ec, 0x40d7, 0xb007, 0x454f, 0xb28f, 0x1ee1, 0xbf1c, 0xb20e, 0x454f, 0x46dc, 0x4380, 0xa1b6, 0xbfd2, 0x07c6, 0x0f93, 0x4294, 0x4159, 0xbf39, 0xb27e, 0x4396, 0x40b8, 0x27c1, 0x410a, 0xb05f, 0x4364, 0x0507, 0x41a0, 0xb2ea, 0x1c8d, 0x007f, 0x214d, 0x4191, 0x11b4, 0x4096, 0x01bb, 0xbf2a, 0xaff8, 0x2725, 0xb29d, 0x1c3a, 0x41a2, 0x0bea, 0x4337, 0x413b, 0x3f7a, 0x4493, 0x4058, 0x1f5f, 0x29c8, 0x1b7d, 0xba6b, 0x41a1, 0x1142, 0x1b05, 0xba5a, 0x422a, 0xb23e, 0xbfd3, 0x4171, 0x43c0, 0xb04b, 0x4417, 0x2814, 0x4068, 0x40b9, 0x4123, 0xa999, 0x44ba, 0x4375, 0x3e79, 0x42d2, 0x4104, 0x1b76, 0x3c21, 0x4150, 0x431e, 0xb2fe, 0xba47, 0xb279, 0xbf85, 0x415b, 0x1c20, 0x4477, 0xac9a, 0x28ba, 0x433f, 0xbfd0, 0xb0fb, 0x409b, 0x4299, 0xba02, 0x418e, 0xa2a0, 0x3aae, 0xb0ad, 0xa584, 0x1a84, 0xa29f, 0x3a28, 0xa99b, 0xba7b, 0xb0d9, 0xba18, 0x32f2, 0x460b, 0x441a, 0x3bb7, 0xbfd5, 0x4324, 0xb2bb, 0x4117, 0x4563, 0x1fd6, 0xba34, 0x429e, 0x4078, 0xaba3, 0xa4bb, 0x0e12, 0xb259, 0xb2f0, 0x443a, 0x4215, 0x42f5, 0xa264, 0xbaed, 0xba2b, 0xbf1b, 0xba59, 0x0098, 0xb205, 0x414e, 0x1e4e, 0x40a9, 0x42f0, 0x4052, 0x4485, 0xbf93, 0x15d8, 0x3ddc, 0x407d, 0x4381, 0x418a, 0xa2dd, 0xb29f, 0xb27d, 0xb0f2, 0x304b, 0x1d56, 0x46e5, 0xa925, 0x2fb8, 0x419e, 0x45ec, 0x41b4, 0x406d, 0x13e1, 0x426b, 0x4095, 0x4262, 0x15d2, 0x443f, 0x41d1, 0xbfcb, 0x2b00, 0x4319, 0xb229, 0xb21e, 0x4277, 0xbf35, 0xb28c, 0xb22d, 0x4558, 0xbad2, 0x43b9, 0xbac8, 0x404d, 0xba27, 0x4236, 0xb217, 0xacb6, 0x3991, 0xb271, 0xac00, 0xb2ba, 0x3bf7, 0x438b, 0xb2b9, 0x26a3, 0x431d, 0x2cf2, 0x421f, 0x23ac, 0x41db, 0x40c1, 0xb2c7, 0x1678, 0x427c, 0xbf8b, 0x3bd6, 0x43b7, 0x041a, 0x42ab, 0x21b7, 0x3236, 0x1892, 0x149e, 0xb27f, 0xb28e, 0x0325, 0xba7c, 0x43a8, 0x4242, 0x441c, 0xbaf4, 0x4210, 0x417f, 0x401e, 0x4252, 0xb2d2, 0x4321, 0x4062, 0xb2d4, 0x3dcc, 0xbfac, 0x40ab, 0xb00f, 0xb08f, 0x4148, 0x41e1, 0xb0a5, 0x41b9, 0x0630, 0x0865, 0x43e9, 0xb0c2, 0x4551, 0x4131, 0x41c5, 0x4014, 0x2bfc, 0x42a0, 0xbf4e, 0x407f, 0x41f6, 0x1b7a, 0x0220, 0x41ac, 0x099e, 0x46b4, 0x42d8, 0xb223, 0x423d, 0x42d1, 0x1d10, 0x41bd, 0x4188, 0x4165, 0x12c7, 0x4174, 0x4018, 0x41e2, 0x414e, 0xbae3, 0x4250, 0x43f9, 0x1ed9, 0x28a1, 0x0e3f, 0x408e, 0x43db, 0xbf9b, 0x4009, 0xb295, 0x4131, 0xb275, 0x4099, 0xb2b9, 0x0ea5, 0x456c, 0x1eaa, 0xa4ed, 0xb085, 0x4194, 0x4197, 0x0d72, 0xaffc, 0x4031, 0x0194, 0xadd6, 0xb270, 0x1eac, 0x1981, 0x2bdd, 0xba1f, 0xbf70, 0xbfbf, 0xbad7, 0x4073, 0x4454, 0xba1d, 0x446b, 0x419e, 0x4021, 0xbfd0, 0x40a7, 0x1e5e, 0xbf63, 0xb2cf, 0x462a, 0xba68, 0x0c00, 0xb02a, 0xb070, 0x1318, 0x2880, 0xb061, 0x0995, 0x4273, 0xb283, 0xbad8, 0x1b22, 0x40a1, 0x15d5, 0xbf84, 0x419e, 0xb274, 0xba71, 0x46d4, 0x45b1, 0x432e, 0x1fb3, 0x381a, 0x42d1, 0x4183, 0x4588, 0x408b, 0x4599, 0x43f9, 0x3324, 0x4338, 0xbf60, 0xbff0, 0xbff0, 0x1fb8, 0xbafc, 0x421c, 0x40d7, 0x40fb, 0xbf79, 0xbade, 0xba55, 0x2335, 0xb047, 0xbfb8, 0x403b, 0xb0a0, 0xbad2, 0x1c8b, 0xba65, 0x3c31, 0x407d, 0x1992, 0x2c0f, 0x0e6d, 0x2f2a, 0x4119, 0xb218, 0x0128, 0x4216, 0xa76e, 0x4390, 0x4045, 0x466f, 0xba55, 0x43e0, 0x3c13, 0x41f9, 0xbf91, 0x3368, 0x1acc, 0x4040, 0x4349, 0x406d, 0x408e, 0xbaf5, 0x42f7, 0x400c, 0xb21a, 0x4306, 0xb253, 0x41e1, 0x32fa, 0x3fdf, 0xa3d0, 0xb207, 0x402c, 0xb25a, 0xba45, 0xbfcf, 0xa252, 0x443a, 0x40c1, 0x0341, 0xbf05, 0xabce, 0x4354, 0x4559, 0xb000, 0x40c2, 0x02ff, 0xb288, 0x425d, 0x40ec, 0x4599, 0xba76, 0x4198, 0x1188, 0x385c, 0x193e, 0x411e, 0xba18, 0xbf47, 0x41ff, 0x1b0c, 0x4298, 0x40c5, 0xb235, 0xba44, 0x3560, 0x4346, 0x415a, 0x199e, 0x1b6e, 0xbad1, 0x45b6, 0x42a7, 0x4253, 0xa5a4, 0x4384, 0x420a, 0xb0aa, 0xbf11, 0x3731, 0x12c0, 0xadb1, 0x426f, 0x4310, 0x2243, 0x25ec, 0xb2e4, 0x0845, 0xbf97, 0x426f, 0x425f, 0x40d3, 0xb2c2, 0x3293, 0x4610, 0xa6f4, 0x4376, 0x1838, 0x4143, 0x179e, 0x4381, 0x1922, 0x15ff, 0x41b3, 0xb2fb, 0x188a, 0xbf3e, 0xb028, 0x3da2, 0x4227, 0x43ef, 0x4079, 0xb055, 0x429d, 0xba59, 0x2586, 0xb0ad, 0x19bf, 0x41fd, 0x4023, 0xb2a1, 0x43e8, 0xa823, 0x4266, 0x41f9, 0x42a1, 0xb228, 0x2c34, 0x41da, 0xb211, 0x16da, 0x2895, 0x4392, 0x2eea, 0xbf24, 0x4255, 0x1922, 0x40c6, 0xbaf3, 0xb2ff, 0x40dc, 0x4141, 0x437f, 0x43c4, 0xb283, 0x1b59, 0x4198, 0x3aba, 0x1f81, 0x1f5b, 0x429a, 0x4204, 0x4037, 0xaefc, 0x183d, 0x1309, 0x4563, 0x09a5, 0xb207, 0xba1f, 0xbfa0, 0x1cd7, 0xbfa3, 0x4258, 0xb205, 0x427a, 0x43b2, 0x41cc, 0x1797, 0x43de, 0xb227, 0x1a3c, 0x4038, 0x4355, 0x4033, 0xbf6b, 0x42f5, 0xb083, 0x4396, 0x3d30, 0x0b59, 0x38cb, 0x4319, 0x37ab, 0x425a, 0x251a, 0x41fd, 0xb2c8, 0x454b, 0xbaea, 0x40ec, 0x43fd, 0x4286, 0x445e, 0x4689, 0xb270, 0x4011, 0x2d71, 0xb0df, 0x4026, 0xa569, 0xbf2e, 0x4121, 0x4393, 0x40e1, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x0542461d, 0xf5f04e12, 0x8bc7b1fe, 0xbd178d21, 0x26456cae, 0xca50d85d, 0xf3453942, 0x72f63230, 0x00bbda65, 0xae6250a2, 0xb5896c69, 0xd8e9e883, 0x00e97798, 0xcb5e9574, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0xffffffc9, 0x00000000, 0x00000000, 0x00000000, 0xfffff79f, 0x00001998, 0xfffff389, 0xfffff84a, 0x00e9e597, 0x00000000, 0xb58955e6, 0xfffffff5, 0xb58955e6, 0x000000c1, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x0039, 0x3a23, 0x427e, 0x40e9, 0xb280, 0x0a50, 0xbfc9, 0x4114, 0x0bf0, 0x1254, 0xbf90, 0x2ad8, 0x440a, 0x414d, 0xbae8, 0xb243, 0xb01e, 0x2da6, 0xae3f, 0x4280, 0x445e, 0x4080, 0x4080, 0x3da8, 0x29dc, 0x2d10, 0x4373, 0x426e, 0xbf22, 0xb24b, 0x424a, 0x4332, 0x40a5, 0x46e3, 0xb28a, 0x42af, 0x4574, 0x1219, 0x1a9c, 0x43df, 0x438a, 0x43dc, 0xbfa9, 0x4318, 0xb28d, 0xb278, 0x0f00, 0xa9a1, 0x4359, 0xb0d5, 0xbf9a, 0x4283, 0xb2bf, 0x1f9a, 0xbfd0, 0x4054, 0x439c, 0xbfd0, 0x4243, 0xb026, 0x0245, 0xbf3b, 0x1b0d, 0x21ca, 0x1fff, 0x4199, 0x1a16, 0x425a, 0xb05a, 0x43cb, 0xbfd2, 0xba68, 0x434e, 0xb237, 0x438c, 0xa6ac, 0xbfcb, 0xbad2, 0x3246, 0x4255, 0xb263, 0x0abb, 0xaec5, 0x2008, 0xb049, 0xa1ea, 0xaa8d, 0xb2d9, 0x2152, 0xb27a, 0x4121, 0xb05a, 0xac70, 0x4138, 0xae5b, 0x2ee8, 0x18cd, 0xb253, 0x4143, 0xba11, 0x4483, 0x41b1, 0xbfcd, 0x1bac, 0x4222, 0x41e5, 0xa4ae, 0x1904, 0x4070, 0x1ff3, 0xb290, 0x40b3, 0xb031, 0x4098, 0x4624, 0x1a74, 0x4284, 0x05c1, 0x401d, 0x416b, 0xb238, 0xbf25, 0x434d, 0x1fa2, 0x40b2, 0x11e3, 0xb2cc, 0x413a, 0x41b7, 0xbf94, 0x43d4, 0x189b, 0xbacf, 0x4571, 0x430f, 0x1056, 0xba57, 0xb202, 0x1be7, 0x42eb, 0xb20e, 0x4544, 0x3f1d, 0xbfbf, 0x18ff, 0xbafa, 0x1a37, 0x4352, 0x0bfe, 0x1eec, 0x42e9, 0xbfd0, 0x419b, 0xbf6c, 0x3b65, 0x4115, 0x4051, 0xbfe0, 0x0eb4, 0x40b3, 0xa454, 0x4399, 0xb05f, 0x39c5, 0x4061, 0x1cb2, 0xbf0a, 0x369f, 0x210e, 0xb0b8, 0xbaf7, 0xa0a5, 0x4197, 0x45ce, 0x4073, 0x1918, 0xb2b8, 0x02a5, 0xb2e8, 0xbf79, 0xb24a, 0x43c6, 0x4034, 0xb08e, 0x46cc, 0x42be, 0x050b, 0x414a, 0x432e, 0x422b, 0xba14, 0x18f5, 0x372a, 0x239e, 0xb0e7, 0x1843, 0x4148, 0x465c, 0xb0b9, 0x3b44, 0x420e, 0xbf9a, 0x4299, 0x39a6, 0x2008, 0x2573, 0x4154, 0xb022, 0xae69, 0x417c, 0x3cbf, 0xb29d, 0x2b2d, 0xba06, 0x4175, 0xb222, 0xbfe4, 0xb205, 0x430a, 0xb211, 0x43ff, 0x0a2c, 0x41e5, 0x1575, 0x0b8d, 0xaf72, 0x1b99, 0x1e09, 0x4149, 0x40b8, 0x1d9c, 0x0c47, 0xba36, 0xb279, 0x4001, 0xb0d3, 0x1b1d, 0xbf13, 0xb219, 0x25db, 0x2745, 0x40c1, 0x1830, 0x4117, 0x428b, 0xb297, 0xb0b8, 0x406e, 0x40ec, 0x41f2, 0x42a1, 0x40da, 0x1df0, 0xaad8, 0x0ad3, 0x40f2, 0x42ae, 0xbf04, 0xba01, 0xae3e, 0xb26a, 0x1dbf, 0x442a, 0xb01f, 0x4051, 0x4147, 0xadb7, 0x420a, 0x1b8a, 0x18b9, 0xb0ab, 0xab57, 0xb072, 0x425d, 0x434a, 0xb2e0, 0xb280, 0xba33, 0x2c1d, 0x430f, 0x1080, 0xbf34, 0x41af, 0xba02, 0xb09a, 0x43d2, 0x1971, 0x3a78, 0x42e3, 0x4300, 0x4482, 0x1bbf, 0x02dd, 0x422a, 0x41a2, 0xbfdd, 0x4592, 0x41af, 0xba74, 0x15b3, 0x42cf, 0xba5b, 0xb2e1, 0xba39, 0x1e81, 0x461f, 0x415f, 0x4173, 0x4253, 0x4041, 0x405a, 0x1f00, 0xbf92, 0xa1f5, 0xb260, 0x456a, 0xb2ff, 0x3029, 0x45a5, 0x418f, 0xb01b, 0xba4d, 0x2658, 0x4384, 0xba5c, 0x3e1b, 0x35d4, 0x174a, 0x09ac, 0xb009, 0xb2ee, 0xb255, 0xbfda, 0x2f7e, 0xa637, 0x41d5, 0x1d37, 0xb0cc, 0x100a, 0x288d, 0x1d1f, 0x4129, 0xba7f, 0x19c8, 0xbf5f, 0x43ee, 0xb0d3, 0x4111, 0x4142, 0xb283, 0x1fd6, 0x4498, 0x43b5, 0x194a, 0x4124, 0x3b97, 0x4670, 0x427c, 0x45dd, 0x410c, 0x4377, 0xbadd, 0x4184, 0x424c, 0x40f7, 0xb25c, 0x4052, 0x2cdd, 0xbfa6, 0xbfe0, 0x416f, 0xb048, 0x4126, 0x40f3, 0xb0f5, 0x4153, 0x13cf, 0xbfd0, 0x41fd, 0x4072, 0x0639, 0x22bf, 0x3c4b, 0x0edb, 0x401d, 0x1f77, 0x4126, 0x4580, 0xbfe8, 0x1bd0, 0xb0fd, 0x2d03, 0x43e4, 0x4418, 0x405a, 0x41cd, 0x28aa, 0x425c, 0x4107, 0xa420, 0x4678, 0xba4f, 0xa155, 0x41e3, 0x4430, 0x400f, 0xba18, 0xba08, 0xb219, 0xbad8, 0xb2e6, 0xbfa2, 0x0925, 0x40f6, 0xba5f, 0xb0e6, 0xbf70, 0xb253, 0x40fd, 0x430e, 0x3042, 0x40ad, 0x4016, 0xba0f, 0xbf5e, 0x4544, 0x3faf, 0x464f, 0x1cb3, 0xbf90, 0x432c, 0x277c, 0x41e9, 0x42d3, 0x4181, 0x4149, 0xb27c, 0x19f6, 0x1e2f, 0x4118, 0x426f, 0xb214, 0xba78, 0x418b, 0xa945, 0x15e6, 0xba23, 0xb242, 0xbfd0, 0x4322, 0xbfab, 0x41cb, 0x4194, 0xba49, 0x4199, 0x42c0, 0xb2b0, 0x43d2, 0xb21d, 0x3ad9, 0x3b7a, 0xba1c, 0xbff0, 0x1b38, 0xbfc6, 0x42a1, 0xba7b, 0x4051, 0xba14, 0x413e, 0xbf85, 0xbfc0, 0xba1a, 0x43cc, 0x44f5, 0xb013, 0xba6c, 0x3c27, 0x4057, 0xb2f1, 0x1bb7, 0x1d41, 0xb28f, 0x42a1, 0xbfda, 0x1d46, 0xb0da, 0xb244, 0xa4aa, 0xb2b6, 0x1b67, 0x2f35, 0x1f03, 0x41cb, 0xb271, 0xbac4, 0x059f, 0x423a, 0xba22, 0x40e9, 0xb0e1, 0xb01b, 0xa845, 0xb063, 0xbf8c, 0x43a3, 0x40b4, 0x39d6, 0x4175, 0x4282, 0xb0de, 0x4037, 0x41e0, 0x42aa, 0xba1c, 0x41ae, 0x1d11, 0xbfa4, 0x186a, 0x4310, 0x432e, 0x1856, 0xb229, 0x4079, 0xa1d2, 0xbf80, 0x4665, 0x4225, 0xbfe2, 0x4372, 0x42a3, 0x448c, 0xbf7c, 0x4293, 0xba76, 0x2941, 0x42ff, 0x15ef, 0xbaee, 0x1735, 0x407c, 0x40c1, 0x1fab, 0xbf44, 0x43cd, 0xb2a0, 0xb2ad, 0x405e, 0x43d4, 0x40a5, 0x4128, 0x2340, 0x43c0, 0x027b, 0x434d, 0x1e76, 0xba2d, 0x4119, 0xbf0d, 0x1c53, 0x0600, 0x40dd, 0x4183, 0x1a96, 0x4167, 0x1c55, 0x4333, 0x46ad, 0xb22e, 0xb244, 0x41ee, 0x40da, 0x4063, 0x022b, 0xb26b, 0x43f3, 0x435a, 0x186e, 0xba0f, 0x1894, 0x4212, 0x4073, 0x41ca, 0x4368, 0xbfb9, 0x2971, 0xbf60, 0xb2f6, 0x43bc, 0xbf9e, 0x4651, 0xb224, 0xa186, 0x40ba, 0x415f, 0x42b0, 0xb044, 0x410c, 0x4035, 0x46c3, 0x3fa3, 0x400a, 0xbf60, 0xbf07, 0xb00a, 0xb263, 0x1cd7, 0x4119, 0x463a, 0x42c9, 0x4307, 0x1f80, 0x030c, 0xb2f0, 0x427c, 0x4619, 0x438a, 0xbfb1, 0x4178, 0x4197, 0x425b, 0x41fd, 0xb26f, 0x4101, 0xb08d, 0x2e09, 0xb072, 0x413a, 0xb281, 0x1eee, 0xaac3, 0x4175, 0x405d, 0x41d8, 0x4665, 0x418b, 0x458a, 0x4059, 0xac9b, 0x3661, 0x4332, 0xbf3a, 0xba5d, 0x4332, 0x4458, 0xb290, 0x1aa8, 0x42db, 0xa756, 0xb2df, 0x284d, 0x1cf5, 0x4148, 0x416b, 0x4445, 0x4114, 0x44aa, 0x4455, 0x40b5, 0xb01a, 0xb249, 0xbad0, 0x1fec, 0x4025, 0x19c2, 0x2014, 0x40c4, 0x42b8, 0xbf93, 0x415b, 0xb08a, 0x4363, 0x43ee, 0xba7e, 0xb2ec, 0xb28d, 0xb0e9, 0xb069, 0x43f1, 0xb2d9, 0xb254, 0x436c, 0x4479, 0x4345, 0xb0d7, 0xba17, 0x4213, 0x46d1, 0x30a5, 0x2e55, 0xbf4f, 0xba63, 0x4426, 0x363e, 0xa9e7, 0x4294, 0xb257, 0x20d4, 0xb22a, 0x1b17, 0x43e6, 0x419e, 0x40cc, 0xaf14, 0xba0b, 0xa0e6, 0xb086, 0x42a3, 0x28ed, 0x43a1, 0x427c, 0xb2f0, 0x1ccc, 0xba6c, 0x41b9, 0xb022, 0xba4f, 0x19a4, 0xbf12, 0x40a4, 0x412b, 0x412d, 0x1e6a, 0x4122, 0x412c, 0x4365, 0x0f73, 0xb2b8, 0x40ca, 0x07ee, 0x3940, 0x200e, 0x377a, 0x4167, 0x40d2, 0x4311, 0x4351, 0x433c, 0x1749, 0x1c33, 0x4137, 0xbf90, 0x1fc8, 0xbfc7, 0x43d6, 0x4294, 0x4338, 0xb27b, 0x4268, 0x2cd4, 0xbf1f, 0x41af, 0xbac7, 0x413b, 0x4099, 0x411f, 0x4270, 0x43e7, 0x40bc, 0xbfdc, 0x425a, 0x4489, 0x4004, 0x4162, 0x2867, 0x454d, 0x1607, 0xba54, 0xb2e4, 0x40c2, 0xbff0, 0x1641, 0xb2b1, 0xbf1d, 0x4560, 0x4371, 0xb2ca, 0x2dc0, 0x41ae, 0x4290, 0x43a2, 0x02d6, 0x1e8f, 0xa801, 0xb054, 0xb03f, 0x4204, 0xba5e, 0x1d4b, 0x42ba, 0x11a0, 0x1f7f, 0xba40, 0x421d, 0xba0d, 0xbacc, 0x434c, 0x4369, 0xbfb5, 0x0d27, 0x2c32, 0x4057, 0x4215, 0x438a, 0xb26c, 0xb028, 0x438a, 0x432d, 0x40a6, 0x0e5c, 0xb224, 0x410c, 0x40f8, 0xa486, 0x421f, 0x0fd8, 0xbf51, 0xab22, 0x135b, 0x1bad, 0x1afa, 0x43de, 0x43cc, 0x2f57, 0x41ff, 0xb27f, 0x15b7, 0x4362, 0xbf03, 0x4084, 0x444e, 0xb21c, 0x4285, 0x41d1, 0x199f, 0x4199, 0x1b47, 0xb2ee, 0x4150, 0xbfd5, 0x432e, 0xa445, 0x40b2, 0x41f0, 0xbac4, 0x45a9, 0x1ee8, 0x0504, 0x410d, 0x34f6, 0x4096, 0x34d1, 0xb26e, 0x439d, 0x24b8, 0x4299, 0x1226, 0x4009, 0xbf46, 0xbff0, 0x42a0, 0x40f7, 0x42b7, 0x43f7, 0x4164, 0xaa14, 0x424a, 0x45d8, 0x414c, 0xbf9d, 0xb218, 0x4223, 0x4430, 0x4036, 0x4163, 0xb0ac, 0xbfaa, 0x409d, 0xb2db, 0x0ae2, 0xb23b, 0xb218, 0x1a2c, 0x441b, 0xa0d2, 0x0e6a, 0xbfb9, 0x3fec, 0x22c2, 0x3012, 0x43ec, 0x1c2c, 0x4264, 0x42f9, 0xb2a2, 0xba13, 0xb270, 0x41dd, 0xb28f, 0x3315, 0x4267, 0x404a, 0x40b8, 0x4077, 0xbf8a, 0x4287, 0x0ce2, 0x406e, 0xbf2b, 0x1ab6, 0xb0aa, 0xbf90, 0xb2c4, 0x4666, 0x1af4, 0xba17, 0x4049, 0x4179, 0xb01b, 0x4154, 0x42e8, 0xb2dc, 0x431f, 0x4029, 0x4442, 0x19ff, 0x3b2a, 0x1a44, 0x1ac3, 0x160c, 0xb06d, 0x31fd, 0xb073, 0x4313, 0x3f1b, 0x43a2, 0xb22f, 0xa562, 0xbfb1, 0xba65, 0xb0d2, 0x1c51, 0x4158, 0xb27e, 0x40b8, 0x43fd, 0x4294, 0xb042, 0xba17, 0xb2ff, 0xbfdf, 0x400f, 0x3598, 0x404c, 0x4101, 0xb202, 0x2e65, 0x417a, 0xb05b, 0x1cd6, 0xab8e, 0x0d74, 0xbff0, 0x3f0d, 0xbadb, 0x0dce, 0x435f, 0xb269, 0xb291, 0x42e0, 0x46bd, 0x41c9, 0xbf63, 0xac82, 0x194d, 0x1cb3, 0x42db, 0xba49, 0x41dd, 0x40b3, 0x1c57, 0x43c3, 0xbf9c, 0x1d84, 0xb2bd, 0xb2c2, 0xbfe2, 0x1df6, 0x405c, 0x4022, 0x4173, 0x423f, 0x40ce, 0x4198, 0x42d3, 0x1cac, 0xbf65, 0x42bf, 0xba1c, 0x1889, 0x42c2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xaa459540, 0xb7417607, 0x90b1ad8e, 0x16822de0, 0x187003f9, 0x872131ae, 0x3224d4e1, 0x42eca858, 0x011e7d28, 0x6dc708e1, 0xb65556a7, 0xb63bce08, 0xc50698a6, 0x40eb158e, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x023ec6b9, 0x0080b091, 0x00000060, 0xfee09ca6, 0x00006364, 0x00006362, 0x00000000, 0x00006362, 0x011f635a, 0xc060e558, 0xc060e558, 0x011f635a, 0x6dc72045, 0x00028578, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbac5, 0x1cc4, 0x3c3f, 0x43b2, 0x408c, 0x4133, 0x4354, 0x4190, 0xbf13, 0x4393, 0xba2e, 0x41f1, 0xb2db, 0xaaa2, 0x4338, 0xbf80, 0xba55, 0x211b, 0x37fa, 0x407b, 0x4103, 0x3bfe, 0xb08f, 0xbf09, 0x413b, 0xa520, 0x4223, 0x1e36, 0xb047, 0x4388, 0x44ed, 0x43b3, 0x180a, 0x41fd, 0x1940, 0x4037, 0x4136, 0x4075, 0xa15f, 0x3b50, 0xb2e2, 0x0a6b, 0x343e, 0xb0b8, 0xae5e, 0x1666, 0xa2f2, 0x42fa, 0x4045, 0xbf3b, 0x4490, 0xb250, 0x4197, 0x406f, 0x1dd6, 0xa912, 0x41b6, 0xa73c, 0x41a3, 0xbf70, 0x40be, 0x1c04, 0x42b0, 0x2e98, 0x27d6, 0xba6a, 0x1eed, 0xb21d, 0x05ea, 0xbfa8, 0xa64d, 0x41e5, 0x42f8, 0x4241, 0xbf90, 0x40b6, 0x4301, 0x3ef6, 0x2451, 0xb076, 0xbf6e, 0x45a5, 0x1fc7, 0x2e04, 0x2934, 0x4098, 0x42e1, 0x102b, 0xbfcb, 0x2cb4, 0xb2ca, 0x1679, 0x2d5a, 0xba39, 0xb280, 0xa5d9, 0x1f1e, 0x3bd4, 0xb20d, 0x4089, 0xba3f, 0x0ef3, 0x392f, 0x40b4, 0xba51, 0x44c8, 0x4340, 0x402b, 0x4400, 0xbf3c, 0x4655, 0x4011, 0xa8bd, 0x057f, 0x432a, 0x1827, 0x434b, 0xbfab, 0xb25c, 0x1467, 0x4101, 0x40b2, 0x43e0, 0x43e1, 0x404c, 0x46bc, 0xb23b, 0x3c33, 0x081d, 0xb006, 0xbaca, 0xb2dc, 0xb202, 0xa346, 0x43a2, 0xb072, 0x4092, 0x1e9d, 0xa2b5, 0x4111, 0xbfac, 0xaf78, 0x2c58, 0x1f71, 0xa7c8, 0xb2ac, 0x3fd4, 0x45cb, 0x44b2, 0x196d, 0x4007, 0x42b5, 0x1953, 0x4049, 0xb0d6, 0xb2e3, 0x4384, 0x1f22, 0x163d, 0xb288, 0xba2f, 0x4226, 0xb292, 0xbf66, 0xba2b, 0xbac8, 0xb0fa, 0xb079, 0x1205, 0xb2f9, 0x43de, 0x3cff, 0x424e, 0x40e0, 0x0cf1, 0x4073, 0x4390, 0x4082, 0x4000, 0x4084, 0xabf0, 0xabd0, 0x0886, 0x466a, 0x41ca, 0xb0ed, 0xb2ec, 0x41fc, 0xbf4b, 0x4478, 0xba51, 0xb2df, 0x419b, 0x1e34, 0xb2f8, 0xbf49, 0xaa3d, 0x41c6, 0x41a3, 0x1aba, 0x40b2, 0x44bd, 0x3d3a, 0xb2bf, 0x28a6, 0x4012, 0x438e, 0x43bc, 0x0ad5, 0xbaca, 0xbf5e, 0x4445, 0x42dc, 0xba1a, 0xa19c, 0x42c9, 0xbfd5, 0x40a1, 0x4048, 0x1893, 0xabd3, 0x404f, 0x4244, 0x4187, 0x4621, 0x4330, 0xb01f, 0x46d3, 0x0861, 0xb2e6, 0x415f, 0x3a61, 0xb24d, 0x1e93, 0x1bac, 0x42d8, 0xb280, 0x40af, 0x40b7, 0x4045, 0xbfbf, 0x40ec, 0xba4c, 0x4494, 0x1454, 0x3538, 0xb2c8, 0xba51, 0x0e63, 0xb276, 0x1d0e, 0x20ee, 0x4337, 0x4204, 0x401e, 0x43b4, 0xb23f, 0x12a9, 0x229b, 0x44ed, 0x444d, 0x407a, 0x1ec5, 0x45c4, 0xba1a, 0xbfc3, 0x3dad, 0x40f6, 0x4085, 0x2377, 0x4418, 0x43a7, 0x2f7d, 0x4071, 0xbfaa, 0x129b, 0xbae5, 0x4691, 0x1be4, 0x1d41, 0x43ce, 0x35f0, 0x1bda, 0x411e, 0xba1a, 0xb2dc, 0x40f8, 0xa3d4, 0x431c, 0x4368, 0xba34, 0x1c65, 0xba13, 0x439f, 0x1b26, 0x4477, 0x40e3, 0x43a2, 0xbf00, 0xaab3, 0xba61, 0x43ab, 0xbf19, 0x4145, 0x40af, 0x182b, 0xb2b4, 0xba74, 0xb0a9, 0x4111, 0x448c, 0x4107, 0x4069, 0x0494, 0x41ef, 0x1af8, 0x42c0, 0x424d, 0x40a4, 0x43da, 0x4246, 0xb260, 0x41cb, 0x403a, 0x424c, 0xba33, 0x43a4, 0x37d0, 0x4390, 0x189b, 0xb2a0, 0xb052, 0xbf83, 0xa25c, 0x24d7, 0xb01e, 0x34d1, 0x3654, 0xb239, 0x1874, 0xba5e, 0xb27a, 0xb24e, 0x406b, 0x43a9, 0x45e2, 0xbafa, 0x4258, 0x393e, 0xbf7b, 0xb2d2, 0xa24e, 0xb293, 0x4027, 0x4268, 0xbf60, 0x4100, 0xb08b, 0x4062, 0x16d0, 0xbf71, 0x4132, 0x4398, 0x41d6, 0x4074, 0x059c, 0x437d, 0xbaf1, 0x400f, 0x4239, 0x0f8a, 0x4387, 0x1e07, 0x40b2, 0xb0a9, 0x4168, 0x43f8, 0xbf84, 0x0189, 0x1606, 0xb0fe, 0x41fa, 0xbfc5, 0x418e, 0x1a73, 0x42bf, 0x4269, 0x41b7, 0xbf95, 0x1abf, 0xac8e, 0x41b7, 0x4249, 0x41e2, 0xb06d, 0x186f, 0x4097, 0x4333, 0x4223, 0x424e, 0x46c4, 0x435b, 0x1c82, 0x416c, 0x40f2, 0xb000, 0xb09b, 0x434a, 0xbac7, 0x4255, 0x365a, 0xb27e, 0x4223, 0xbf2a, 0x4113, 0x411f, 0x4234, 0x3feb, 0xb2bf, 0x436a, 0xb0cd, 0x18e6, 0xa251, 0xbf72, 0x196c, 0xbac9, 0x190f, 0xb065, 0x4222, 0xbf9f, 0x0e34, 0xb234, 0x04ae, 0x401a, 0x19e3, 0xa8be, 0xba60, 0xb0b5, 0xbfd6, 0x4288, 0x375c, 0xb2d6, 0x4269, 0x419e, 0x46c5, 0x228a, 0x467e, 0xba01, 0x2f41, 0x415e, 0xb0d3, 0xb0de, 0x306a, 0x41b2, 0xb251, 0x432f, 0x430c, 0x189f, 0xbf69, 0x4061, 0x4423, 0x0dcf, 0xb272, 0x43e9, 0xa0e9, 0x03b4, 0x4606, 0xba5c, 0x42e5, 0x442d, 0x428f, 0x4044, 0x2c64, 0x4188, 0x4250, 0xa133, 0xbfe1, 0x4467, 0x4379, 0xb209, 0x43ef, 0x10af, 0x42b5, 0x43cd, 0xbad7, 0x4385, 0x1434, 0x4371, 0x44a9, 0xbaff, 0xb2c5, 0xb028, 0x46b1, 0x249e, 0x1a7c, 0x3608, 0x1561, 0x19da, 0x420f, 0x38a2, 0xa417, 0xbf8f, 0x1805, 0x18f3, 0x2abc, 0x44d3, 0xb210, 0x422d, 0xb29e, 0xb2d8, 0x4463, 0x4148, 0xa528, 0x407c, 0x40a0, 0x433e, 0xbfc1, 0x43dc, 0x38b7, 0xb232, 0x43f0, 0x3de3, 0x3077, 0xbf9d, 0x3ccd, 0xb0be, 0xb2c7, 0xba66, 0x420c, 0xb21e, 0x22d8, 0x2e67, 0xba0c, 0x4293, 0x2f4a, 0xba64, 0x413f, 0x1a2d, 0x4177, 0x424a, 0x4258, 0x4280, 0xa986, 0x3839, 0x4171, 0xb055, 0x4330, 0x4233, 0x41a5, 0x1f63, 0xbf78, 0x43c6, 0xb24c, 0x4199, 0xb2bd, 0x42a6, 0x0c98, 0x1f23, 0xb0ce, 0x4159, 0x1e07, 0xbad3, 0xbae3, 0x42db, 0x41bc, 0xb227, 0xba6d, 0x42f3, 0xbf52, 0xb0f4, 0x45c9, 0x4358, 0x4012, 0x411e, 0x41e5, 0x0b04, 0x41af, 0x43d1, 0xbf1e, 0x2603, 0x466d, 0xba17, 0x1fb1, 0x4363, 0xb21e, 0x4221, 0xb093, 0xbadd, 0xba2c, 0xb2a4, 0x43a0, 0xb2f0, 0xb221, 0x4601, 0xbf2b, 0x4629, 0xb07b, 0x0cb6, 0xb262, 0x439e, 0x40d0, 0xb02b, 0x337d, 0xbf03, 0x4683, 0xba7e, 0x18b9, 0x425b, 0xb0e9, 0x467f, 0x22c2, 0xbfba, 0x400a, 0x426b, 0x43b5, 0x3e16, 0x417e, 0xa4af, 0x40c7, 0x4379, 0x4079, 0x2d9d, 0x4374, 0x4333, 0x412e, 0x2fb4, 0xbf70, 0x4295, 0xae48, 0x1be3, 0x41f9, 0xbfd8, 0xb074, 0x4275, 0x45b5, 0x4133, 0x40a1, 0x402a, 0x405f, 0xbfbd, 0x423b, 0x463c, 0xba19, 0x439b, 0x3b6f, 0xb23f, 0x0f1d, 0xba56, 0xb261, 0xba54, 0xa2ae, 0x46bd, 0x43f6, 0x4017, 0xb2b0, 0x4435, 0x40bd, 0x1e76, 0x413e, 0x410b, 0x411b, 0x1a0e, 0x12dc, 0x1d50, 0x42d7, 0x420f, 0x40e3, 0xb2d6, 0xbfa6, 0xb242, 0xb2eb, 0x40e3, 0x4368, 0x24cc, 0x455d, 0x06b0, 0xbf49, 0xb0a0, 0x3dec, 0xba24, 0x432a, 0x4296, 0x40f9, 0xbff0, 0xa816, 0xb27b, 0x4053, 0xb095, 0x42a4, 0x46e2, 0x19c6, 0x421d, 0x406d, 0xbfc1, 0x4226, 0x4101, 0xbafc, 0x427e, 0x29ba, 0x2f83, 0x41b6, 0x4315, 0x4252, 0xbfa0, 0x45f0, 0x0cbd, 0x40dd, 0x1a3c, 0x43c3, 0x4020, 0xbacd, 0x414d, 0x41e7, 0x4323, 0xb25a, 0x1983, 0x17c3, 0xbf8a, 0x40ab, 0x4601, 0x44f9, 0x4080, 0x3d43, 0x09f7, 0x43de, 0x4637, 0x4052, 0x40dc, 0xbaeb, 0x446a, 0x1e28, 0x4007, 0x359a, 0x45ae, 0xab99, 0x23d8, 0x1cb7, 0xb29f, 0x41c3, 0x4463, 0xbf4f, 0x1f1a, 0x42b4, 0xbacd, 0xb27c, 0xb25b, 0x1aa8, 0xadce, 0xbfd0, 0x352d, 0x4383, 0xb26b, 0x43c0, 0xb201, 0x425c, 0x4356, 0x40a4, 0x43f7, 0x40f8, 0xbf6a, 0x4149, 0x3093, 0xba79, 0xba61, 0x431d, 0x1bb2, 0x405d, 0x09e3, 0x43c3, 0xb0da, 0x4116, 0x4561, 0x4094, 0xbfe0, 0xba18, 0xbaf1, 0xbf8c, 0xb225, 0x12db, 0x1aa6, 0x40f3, 0x327f, 0xb2c3, 0x15eb, 0xb22c, 0x27c4, 0xb229, 0x4015, 0xa837, 0xa0aa, 0x4045, 0x44c9, 0xb2cd, 0x416d, 0xbf58, 0x441a, 0xba16, 0x44aa, 0x1be5, 0xb2dd, 0x456e, 0x2da2, 0x4485, 0x3a61, 0x43d4, 0x127d, 0x0f03, 0x4046, 0xa7c2, 0x43ba, 0x422d, 0xb2f8, 0xa357, 0x40bc, 0x1f75, 0xa5db, 0xbf90, 0x42a1, 0x41bc, 0xbf87, 0x1bc0, 0xbf60, 0x3130, 0x40a2, 0x416d, 0x4171, 0x43d6, 0x180c, 0x46c4, 0x1efb, 0xbfad, 0x118b, 0xb211, 0x41cd, 0xba1f, 0xa45a, 0x400a, 0xbfc1, 0xba49, 0x4103, 0x4212, 0xb20b, 0x42d7, 0x42e0, 0x413a, 0xba68, 0x42d4, 0x4241, 0x41b2, 0x427f, 0x42e9, 0xbfa7, 0xb09c, 0x20ae, 0x3704, 0xb2ce, 0x1f73, 0xa18a, 0xbf80, 0x4266, 0xb0d8, 0x419a, 0xbf41, 0x4270, 0x1810, 0x4264, 0x39da, 0x1d65, 0x41a7, 0xa97c, 0x1288, 0xba61, 0x0e71, 0x425c, 0x41e5, 0x4542, 0x42fe, 0x4183, 0xb283, 0x401d, 0x00d1, 0x4232, 0x4246, 0x226e, 0x1918, 0x4299, 0x15ac, 0xbfad, 0x1ce1, 0x2a0a, 0x40a6, 0xb2fd, 0x3635, 0x1933, 0x412c, 0x1439, 0xa558, 0x4220, 0xbf33, 0x43c6, 0x416d, 0x26aa, 0xb213, 0x44e4, 0xbfe4, 0x29dc, 0x1d40, 0x42a2, 0x1ea9, 0x3f4b, 0x2e37, 0xb0e2, 0x0098, 0x427a, 0x4347, 0xbfe0, 0xb0a6, 0x053b, 0xb236, 0xba79, 0x444d, 0x429b, 0xbf94, 0xba27, 0xb2a0, 0x42aa, 0xb2e1, 0x43d0, 0xbf90, 0xba6b, 0xba4d, 0xa3ce, 0xb266, 0x43e6, 0x23cf, 0x422e, 0x4102, 0xaaf3, 0xa539, 0x419a, 0x137f, 0x45d8, 0x4067, 0xbfc2, 0x40f6, 0x416f, 0x08f8, 0x1f01, 0xb22b, 0x40a3, 0x40f9, 0x3a0e, 0x4370, 0xb2ad, 0x437a, 0x16d6, 0x0960, 0x1f6d, 0xa78d, 0x0385, 0xb2c2, 0x41cf, 0xb2f5, 0x1f83, 0xbaef, 0x4077, 0x1b8c, 0xba12, 0x4386, 0x40d0, 0xbf12, 0x40a8, 0xb237, 0x4237, 0xba4f, 0x4296, 0x45c8, 0x1f18, 0x1d76, 0x43ab, 0x3e32, 0x43f0, 0xbf72, 0x1a78, 0x1b64, 0x4028, 0x40cf, 0x43bb, 0x1f2c, 0x438f, 0x1fb8, 0x4282, 0x4669, 0x41d5, 0x1db5, 0xbafd, 0x41d4, 0x11c4, 0x405f, 0x1fb3, 0xb24b, 0x034f, 0x4345, 0x0970, 0x44fd, 0xb06b, 0xbf87, 0xa48d, 0x4492, 0xb095, 0x40b4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xec6fcb87, 0x37fe5171, 0x476101c9, 0xb4b6dc30, 0x961e2dee, 0xdd91c1a4, 0xedf2ecd8, 0xdc6fc622, 0x228a66a9, 0x3398789a, 0xa6476515, 0xda1e950b, 0x49ed8832, 0xb877df50, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x07fffffe, 0x0000285e, 0x00000000, 0x0000005e, 0x00001a14, 0x00000000, 0xffffffd3, 0x050bc000, 0x5622f36b, 0x00002e90, 0x5622f36b, 0xa6476511, 0xac45e6d6, 0x000041e6, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x42f8, 0x07a3, 0xba44, 0x42a4, 0xbff0, 0x43f8, 0x2211, 0xb240, 0x40ef, 0x3564, 0xbad8, 0x45a3, 0x41ca, 0xba31, 0x4163, 0xbf47, 0x0b13, 0x42a0, 0xb288, 0x4498, 0x40a7, 0x4388, 0x3a67, 0xba1f, 0x4109, 0x4267, 0xb250, 0x437a, 0x46d4, 0x1fc3, 0x44cb, 0x421f, 0x36f5, 0xba5b, 0x1805, 0x46f8, 0x39bb, 0x170b, 0xb245, 0x4671, 0x40a1, 0x4175, 0xbf8e, 0xaafd, 0x4046, 0x415e, 0x41ad, 0x4342, 0x43b2, 0x182a, 0x1fea, 0xbfce, 0xa26c, 0x4232, 0x4184, 0xbfcb, 0x421c, 0x4350, 0xbf00, 0x420a, 0x4073, 0x434b, 0x43c6, 0xba76, 0xba57, 0x43fa, 0x422a, 0xb0d1, 0x42ff, 0xb2fe, 0x4327, 0xbf52, 0x41ef, 0x0772, 0xb293, 0x4371, 0xb0d4, 0x3aea, 0x3a19, 0xbac1, 0x408c, 0x4177, 0xbf00, 0xa92b, 0xba79, 0x4317, 0x4231, 0x4139, 0xbae8, 0x4107, 0xba54, 0x43c4, 0x4066, 0x1a61, 0xba23, 0xb0b9, 0x41a5, 0x410a, 0x43f4, 0xb027, 0xbfbe, 0xb02e, 0xaa85, 0xa3ba, 0x1c41, 0x3ab9, 0x1700, 0x44db, 0x1e0f, 0x40fc, 0xbaeb, 0xb015, 0xbf5c, 0x433d, 0x3457, 0x408e, 0x443e, 0xa97f, 0x19f0, 0xbf6a, 0x2099, 0x41a3, 0x400d, 0x41b8, 0x42d7, 0x4049, 0x416f, 0x428f, 0xbad9, 0xbf65, 0xb066, 0xa894, 0x18db, 0x11e7, 0x4351, 0x4113, 0x4182, 0xbae8, 0x1ea4, 0x3b34, 0xbf8d, 0xba5c, 0x4198, 0x436b, 0x421d, 0xbaf3, 0x435b, 0x12bd, 0x40cd, 0x2ba7, 0x180e, 0x43a7, 0x409b, 0xba76, 0x20b6, 0xba6e, 0x22f8, 0x184a, 0x41c9, 0x2f18, 0x43bb, 0xb28a, 0x3e03, 0x42bd, 0x1873, 0x4076, 0xbf42, 0xbaf0, 0x40b4, 0x185e, 0xbfaa, 0xbf00, 0x14e1, 0x27ae, 0x16e9, 0x43d5, 0x412c, 0xbf8f, 0x1bc8, 0x4048, 0x403e, 0x176a, 0x4126, 0x469d, 0xba7f, 0x0dc0, 0x4363, 0x436d, 0x1848, 0x1944, 0x422e, 0xb273, 0xbf00, 0x1c50, 0xbfc2, 0xb2b1, 0x421b, 0xbfd0, 0x320b, 0x300e, 0x1cd6, 0xb2ae, 0x433e, 0xb2f0, 0x1f18, 0x2045, 0x3f7d, 0xb2ac, 0x31e0, 0x1882, 0x1c64, 0xb2e2, 0x1d30, 0xbf90, 0x4316, 0x4429, 0x3b53, 0xba3c, 0x1c65, 0x21a2, 0x4246, 0xb070, 0xbfc6, 0xa834, 0xb2c4, 0xa89e, 0xb24a, 0xb292, 0x1341, 0xb20c, 0xbad2, 0x40a1, 0x425e, 0x0227, 0x4294, 0x2458, 0x4278, 0x4130, 0xad67, 0xb2dc, 0x4112, 0x4329, 0xb037, 0xa54d, 0x031f, 0x4105, 0xbfca, 0x400f, 0x233b, 0x4108, 0x1a02, 0xbf44, 0xb0a7, 0x416d, 0xbfdb, 0xb2d8, 0x2ed4, 0x4046, 0x1bd0, 0x1e0e, 0x426a, 0xa1db, 0x1fb1, 0x40b9, 0x4304, 0x424a, 0x02d1, 0xb245, 0x4153, 0x41cb, 0xbf22, 0x43f8, 0x41c8, 0x18fc, 0xa2a2, 0x42cf, 0x411e, 0xbfc6, 0x41db, 0xb2ed, 0xae81, 0x43c9, 0xbaef, 0x40fa, 0x0fd7, 0x430f, 0x072e, 0x3c69, 0xa74f, 0xbfa0, 0xba11, 0x410c, 0x000b, 0x4028, 0x1c4d, 0xba4c, 0x0ca2, 0x0b17, 0x43bd, 0x0149, 0xbfa7, 0x1c43, 0xbae0, 0x4245, 0x14f5, 0xba01, 0x4231, 0x421e, 0x4390, 0xa298, 0x4200, 0x2a55, 0x416f, 0x1a4f, 0x42b7, 0x097a, 0x1d13, 0x2dc8, 0xb2f7, 0x3cbf, 0x4270, 0x1f05, 0xa2c0, 0x057e, 0x40bb, 0xbac7, 0x1fef, 0xbfc6, 0x2651, 0x1ef8, 0x41fb, 0x382d, 0x40b0, 0xa7ea, 0xb034, 0x439a, 0xba7d, 0x4387, 0xb2f0, 0x4319, 0xbad5, 0x4427, 0x19c9, 0xb27d, 0xbf70, 0x2c31, 0x4260, 0x098c, 0x40de, 0x4220, 0x418c, 0x435b, 0x4589, 0xb27d, 0xb290, 0x1d46, 0xbf13, 0xb21d, 0xb2b3, 0x42cc, 0xba42, 0x40c0, 0x02a9, 0x4345, 0x40f4, 0xbfe4, 0x436f, 0xb25e, 0x1f19, 0xba1a, 0x2f2c, 0xb23a, 0xb238, 0xb239, 0x436b, 0x4287, 0xbac9, 0xbaf9, 0x1b47, 0xb245, 0x4338, 0x0077, 0x4179, 0xbae7, 0x431d, 0x4108, 0xb06f, 0x418a, 0x4253, 0xb02a, 0xa84c, 0xbfc2, 0xba77, 0x1a6c, 0x4018, 0x2ed1, 0xabb2, 0x409b, 0xa523, 0x4335, 0x4319, 0x4250, 0xb256, 0xb228, 0xa371, 0x1b22, 0x4350, 0x4273, 0xb09d, 0x18a4, 0x4297, 0x0814, 0xbf58, 0x27b9, 0x22c1, 0xb053, 0xa1b1, 0x414c, 0x1e1a, 0xbfd2, 0x4246, 0x42af, 0x4340, 0x4069, 0x4236, 0x4334, 0x425c, 0x41e5, 0x43ff, 0x4349, 0x3950, 0x237f, 0xba38, 0x42f8, 0x40f8, 0x034e, 0xb2c9, 0xb255, 0xba46, 0x426d, 0xa40b, 0x1ed1, 0xbf19, 0xb26f, 0x158c, 0x43a4, 0x402a, 0xb205, 0xbaf6, 0x45db, 0x4167, 0xb28a, 0x218e, 0x189d, 0x1d70, 0x458c, 0x072f, 0x424e, 0x1eff, 0xbacd, 0xb2ca, 0x45c1, 0x0ef0, 0xac77, 0xbfb2, 0x4099, 0x3d38, 0xad0b, 0xbfa4, 0x07ea, 0x41cb, 0x4262, 0x428f, 0x117b, 0xbfc0, 0x33fb, 0xbfc0, 0x413d, 0x4414, 0x45c1, 0x15b1, 0x01b3, 0x419c, 0x4257, 0xbfc6, 0x1c88, 0x406f, 0xa0e7, 0x4068, 0x4292, 0xb295, 0x1e8d, 0x41e8, 0x3710, 0x42da, 0x4234, 0xb0b0, 0xb06b, 0xbf77, 0x0b97, 0x4060, 0xba38, 0x1cb0, 0xba36, 0x427b, 0x4281, 0x411f, 0xb04b, 0x4026, 0xa71a, 0xbf9f, 0x41a0, 0xba1c, 0xb289, 0xadd3, 0xba4b, 0x43d7, 0xb0c9, 0xbafb, 0xba0a, 0x414e, 0x401b, 0x2a82, 0x4300, 0x43a9, 0xbfdd, 0x40e4, 0x4327, 0x1c8d, 0x1c33, 0x4260, 0x43de, 0x4669, 0x433f, 0xb253, 0x1d24, 0x107c, 0xba13, 0x40f4, 0x43f5, 0xbf90, 0x438e, 0x1c5e, 0x41e1, 0x3f39, 0x1bd4, 0xbfd9, 0xb2c4, 0x418e, 0xb2e7, 0x15b8, 0xac3d, 0x2430, 0xb291, 0x42c9, 0x4003, 0xb2fd, 0x34aa, 0x4680, 0x2a30, 0x42f9, 0x43eb, 0xbf41, 0x41eb, 0x2f26, 0xaa5f, 0x42b7, 0x42e0, 0x3e1d, 0xba1c, 0x4261, 0x0676, 0xbfe8, 0x423c, 0x3bd6, 0xba24, 0xbf70, 0x4152, 0x228a, 0x1848, 0x120b, 0xb262, 0x4309, 0x402e, 0x4106, 0xa354, 0x200b, 0x415e, 0xbfd6, 0x407f, 0x4563, 0x41b5, 0x212e, 0xb0e2, 0xbfb6, 0xae17, 0xb021, 0x2a99, 0xb0cd, 0xa019, 0xb090, 0x42ad, 0x42ad, 0x15ca, 0x43d3, 0x43cf, 0xb2e3, 0x43dd, 0xb2be, 0xac16, 0x41bb, 0x249a, 0x42b0, 0x083b, 0x4435, 0x4349, 0x40b3, 0x463a, 0x1a4e, 0xba55, 0xbf19, 0x40fe, 0x4300, 0xba69, 0x4299, 0x1f04, 0x013a, 0xb276, 0x09e9, 0x0926, 0x402a, 0x46c9, 0x1243, 0x4347, 0x412e, 0xb26f, 0xb2d5, 0x4388, 0x41cb, 0xba1e, 0xbfa2, 0xbac4, 0x4254, 0xb2e2, 0x0ffb, 0x20b7, 0x40b7, 0xb2b1, 0x432e, 0x466a, 0x40b0, 0xb2a7, 0x43c7, 0x18b4, 0x4279, 0xb272, 0x41c0, 0x4561, 0x40bb, 0xb047, 0x1fcc, 0xa0e0, 0x437a, 0xbf36, 0x2ab2, 0xb202, 0xad4d, 0xbad7, 0x0427, 0x1995, 0x31b3, 0x1235, 0x1f89, 0x340f, 0x45b5, 0x4556, 0x1941, 0x4203, 0x4593, 0x4071, 0x40a0, 0x387d, 0x3c40, 0x15de, 0x4215, 0xb219, 0xbf5a, 0x16e9, 0x44ed, 0xa529, 0x1cd4, 0x428b, 0x414a, 0xb2d6, 0x4572, 0xbf41, 0x414d, 0x40e1, 0x1862, 0x44d9, 0x414a, 0x4292, 0x425e, 0xb061, 0x436c, 0x3ba1, 0xb247, 0x40cb, 0xbaed, 0x412e, 0xba4b, 0xb298, 0x431c, 0xbf36, 0x0bfe, 0x3111, 0xba26, 0x1fb8, 0x4215, 0x1841, 0x40b7, 0xb0f5, 0xbf80, 0x42f8, 0x08a3, 0x45ee, 0x40d2, 0xb2f2, 0x33d3, 0xbf0d, 0x4200, 0x4083, 0x42af, 0x1a3b, 0xbfd1, 0xb060, 0xb0a4, 0x3ab1, 0xb23d, 0xba06, 0x4296, 0xb29f, 0x421c, 0xb2bd, 0xbae4, 0x46fa, 0x467e, 0xb019, 0x40f0, 0xb216, 0xbf35, 0x107c, 0xb296, 0x42c3, 0x4418, 0xba0c, 0xb22f, 0x0196, 0x42bc, 0x27fa, 0xa5b8, 0xbf02, 0x407c, 0x2e4e, 0xb28c, 0x091b, 0x1c4e, 0x1a4e, 0x4320, 0x40f6, 0x419b, 0xbac2, 0x43ab, 0xba4e, 0x4331, 0xb2ba, 0x43aa, 0x437f, 0x3ace, 0x27c8, 0xb246, 0x265d, 0x1ceb, 0x2cc8, 0xbff0, 0x2612, 0xbf1b, 0x1fd7, 0x43b0, 0xa909, 0x418b, 0xae30, 0xbfa2, 0x4563, 0x37cc, 0x4396, 0x4303, 0x4129, 0x1d9b, 0xbfcd, 0xb228, 0x43d6, 0x417e, 0xb00c, 0x4142, 0xba08, 0x41b2, 0x4260, 0x4066, 0x4362, 0x4210, 0xbf81, 0x40a6, 0x3a50, 0x40ac, 0x305e, 0xba75, 0x41e3, 0xb09c, 0xbf5a, 0x42e9, 0xbae5, 0x1a1b, 0x418d, 0xa430, 0x41ae, 0x4265, 0x2f35, 0x0f5f, 0x41b7, 0xbf71, 0xb056, 0x3e3a, 0xb2b8, 0x4227, 0x16be, 0xb213, 0x3ea4, 0xbf0c, 0xa864, 0xb25c, 0x42e2, 0x4083, 0x23de, 0x07bc, 0x42ca, 0x42cb, 0x1a5f, 0x3d6f, 0xb266, 0x3a16, 0x43ba, 0x434f, 0x41db, 0xb0b8, 0x427d, 0xb0b7, 0xbfe1, 0x1e76, 0x400e, 0xba71, 0xa62e, 0xb23b, 0x4358, 0x18ae, 0xb292, 0x4254, 0xbfbb, 0xba59, 0xb28e, 0x33cb, 0x46aa, 0x4219, 0xa5a6, 0x1ed8, 0x42d0, 0x150b, 0x4214, 0x42ed, 0x2da7, 0xb22b, 0x0e19, 0x3532, 0x43f0, 0x42a1, 0x439e, 0x40b3, 0x4361, 0x2193, 0x4451, 0x3894, 0x2d7e, 0x43c2, 0x1f33, 0xb0d9, 0x4440, 0xbf39, 0x1af5, 0x3c98, 0x42b7, 0x2c63, 0x1bb5, 0x06ac, 0x415c, 0x4012, 0x0d2f, 0x43cf, 0x43d1, 0x4272, 0x411d, 0x1dbc, 0xa93d, 0x1f13, 0x4222, 0x1904, 0xb24e, 0x44b1, 0xa1e6, 0x4074, 0x1a21, 0x4046, 0x4095, 0x4047, 0xbf8a, 0xba15, 0xb0ae, 0xa64e, 0xb26f, 0x4387, 0x05f8, 0x4548, 0xba5d, 0xb227, 0x407e, 0x43e8, 0xb238, 0x1f65, 0x45a2, 0x40f5, 0xba4a, 0x35d1, 0x403f, 0x4637, 0xbf49, 0x1dc2, 0x128b, 0xa29c, 0x41c0, 0xba48, 0x1adb, 0xb286, 0xb250, 0xb0af, 0x4132, 0x43e3, 0x1895, 0xba7b, 0x169e, 0x113d, 0x4589, 0x1fa1, 0xbfb7, 0x412d, 0x43fe, 0xb232, 0x3c71, 0x2e4a, 0xa936, 0x417e, 0x410c, 0xa245, 0x0a3e, 0x16c3, 0x0a0b, 0x0823, 0x3518, 0xba3e, 0x4135, 0x45f0, 0x412c, 0x4217, 0x4068, 0x4376, 0xb232, 0x416d, 0x42a2, 0x46db, 0x40a5, 0xb269, 0xb070, 0xbfbf, 0x2ff5, 0x2a6b, 0xb2a0, 0x45be, 0x1ab5, 0x1e69, 0x0d07, 0x4018, 0x1fcb, 0x414a, 0xb065, 0x08e8, 0xbaff, 0xbad4, 0x4332, 0xa6fd, 0x441e, 0xba61, 0xbfc2, 0xba53, 0xa9e9, 0x4260, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x618c22bd, 0x5bef2a39, 0xee6c28df, 0x55cc73d5, 0xfa7df287, 0x5074f3f0, 0x2ce650c2, 0xead87231, 0x32e2b3c0, 0xe82f84ef, 0x182094bd, 0x98581b15, 0x94fdcdeb, 0xe3d0b1ff, 0x00000000, 0x800001f0 }, FinalRegs = new uint[] { 0x00006f62, 0x6525d096, 0x68d99e90, 0xd968909e, 0xffff909e, 0x68da0000, 0x68da1bcc, 0xffffff0f, 0xb3350301, 0xe82f84f5, 0x000000df, 0x010f4008, 0x182094bd, 0x6525ccf2, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x40a3, 0x4088, 0x431e, 0x40a5, 0xb02c, 0xba79, 0xb24d, 0x40f5, 0x43df, 0x43c1, 0xaef2, 0x4008, 0x414b, 0x41fc, 0xb235, 0xbfc0, 0xafbb, 0x1c05, 0x196a, 0x40b4, 0xb072, 0x38d1, 0xbf43, 0x46b1, 0xa0e6, 0x45a4, 0x4043, 0xbf90, 0x4626, 0xb2fc, 0xbf7e, 0xba61, 0xa1ca, 0x2175, 0xaf95, 0x4312, 0x1d8a, 0x190e, 0xb2df, 0x1698, 0x2d52, 0x1a9a, 0xb0ed, 0x407f, 0xa460, 0x4254, 0xb066, 0xbf89, 0xb0a9, 0xb2b8, 0x1a5d, 0x4194, 0x4211, 0x41a0, 0x40e0, 0x4139, 0x4362, 0x4336, 0x42a4, 0x447b, 0xabbd, 0x443a, 0x4459, 0xaf35, 0xbfb2, 0x45cc, 0x1e31, 0x1b94, 0xb07a, 0xb29e, 0x4212, 0xbae6, 0x427e, 0xb034, 0x2b28, 0x400a, 0x3ca6, 0x43a4, 0xb04a, 0xb248, 0x409a, 0xab1f, 0x4278, 0xb04b, 0x407f, 0x4291, 0xbf75, 0x415f, 0x4087, 0xb298, 0x41d4, 0x423e, 0x1b3f, 0x4206, 0xba6d, 0xb2d7, 0x458c, 0x319d, 0x4222, 0x40c1, 0x43ac, 0x4340, 0x1308, 0x4011, 0x4118, 0x40b3, 0x4379, 0xba4b, 0xb23e, 0xbf74, 0x40b5, 0x3319, 0xb07b, 0x41cb, 0xb2f9, 0x431d, 0x02c4, 0x1996, 0x419a, 0x435b, 0x255c, 0xb2f5, 0x33ad, 0xb04a, 0x4408, 0x0974, 0x42f7, 0x4339, 0xb280, 0x1b82, 0xb247, 0x464c, 0x4211, 0xbf0b, 0x4030, 0x43e6, 0x4087, 0x3611, 0x0981, 0x416e, 0x4315, 0x43c1, 0x2147, 0x4090, 0x4136, 0xbf66, 0x2933, 0x2ecf, 0x43eb, 0xb283, 0x42af, 0x1c96, 0xb09f, 0x41f2, 0xb21c, 0xbf09, 0xb087, 0x4463, 0xbaf1, 0x419d, 0xa7b0, 0xb211, 0x41a6, 0x3f86, 0xba54, 0x3385, 0x03b0, 0xb211, 0x4331, 0xaf96, 0x4119, 0x0c9e, 0x419b, 0xb2dc, 0x43d0, 0xb24c, 0x4042, 0xbf00, 0x4162, 0x1fc3, 0x4189, 0xb25a, 0x06f9, 0xb274, 0xbf6c, 0xbacf, 0x407e, 0x4204, 0x140f, 0x4309, 0x4176, 0x407e, 0x4130, 0x1c8a, 0x4354, 0x2411, 0x2605, 0xb057, 0xbf70, 0x406b, 0x3cd9, 0x437f, 0xa2e1, 0xba57, 0x069e, 0xbf76, 0x46ec, 0xbaf9, 0xb2ae, 0x436a, 0xadd9, 0xb09d, 0x40db, 0x4244, 0x1a39, 0xac08, 0x408d, 0x4021, 0x4263, 0xb281, 0x4418, 0x4116, 0x42f6, 0xa919, 0x1470, 0x1f33, 0x4245, 0x434f, 0x43ff, 0x43a1, 0x0d92, 0x4051, 0x43af, 0x30c5, 0xbf46, 0x425e, 0x4008, 0x4678, 0x42db, 0x1c2c, 0x4410, 0x1c4e, 0x4282, 0xba0d, 0x4075, 0x194f, 0x41ab, 0xb065, 0x1871, 0x412f, 0x0399, 0xa0db, 0xb239, 0x4559, 0x43d6, 0x1faf, 0x411f, 0xb0b7, 0x4606, 0x43c2, 0xbf81, 0x0823, 0x2a43, 0x18c5, 0x1a77, 0xad27, 0x43ff, 0x4212, 0x0e3c, 0x411a, 0x43a6, 0x1916, 0x4394, 0xb225, 0x1eb5, 0x449b, 0x438e, 0x40ca, 0x4623, 0xb2e3, 0x41ef, 0x4493, 0x3e02, 0x4069, 0x1c2b, 0xb234, 0x1e69, 0x41df, 0xb2b8, 0x41d8, 0xbf56, 0x05ad, 0xba1c, 0x42c2, 0x4051, 0x42b2, 0x40f4, 0x43ce, 0x44f0, 0x43f3, 0x4229, 0x213e, 0xb2c2, 0x3200, 0x4214, 0x42b6, 0x005f, 0x4075, 0xb272, 0xbaf8, 0xb224, 0xb29f, 0xbfd5, 0x40c1, 0x438e, 0x43c4, 0x0aa3, 0xa689, 0xb22f, 0x2428, 0x45bd, 0x4004, 0x43f9, 0xb29e, 0x4679, 0xb21b, 0x40bb, 0x4027, 0x40f4, 0x4074, 0xbf7b, 0x45c2, 0x43f6, 0xb2a0, 0x416c, 0xba07, 0x1da5, 0x416e, 0xba1c, 0x406a, 0x0523, 0xa9b7, 0x352b, 0x35c0, 0xbf97, 0xb26c, 0xbf60, 0x40f8, 0xba7a, 0x423f, 0x02f2, 0x4068, 0x3e52, 0x42c7, 0x4050, 0x1d10, 0x4211, 0x42d9, 0x4565, 0xb2a5, 0x416d, 0xba27, 0x420a, 0xb228, 0xbf80, 0xa3ea, 0x437b, 0xb2cf, 0xb2cb, 0x1473, 0xbf7c, 0x4285, 0x41b6, 0x4280, 0x3a9f, 0xba06, 0xba22, 0x3f7e, 0x1822, 0x1663, 0xbf80, 0x182b, 0x1d47, 0x37c4, 0xaa51, 0x1efc, 0xbfb7, 0x43b7, 0xba12, 0xbaf0, 0x40e9, 0xbf80, 0xb09e, 0x4448, 0x1f27, 0x4083, 0xac6a, 0x400e, 0x1c32, 0xba54, 0xba14, 0x249e, 0x3f80, 0x4610, 0x4293, 0x4396, 0x1e57, 0xb23f, 0xb093, 0x4161, 0x42c0, 0x3494, 0x40d5, 0xbf56, 0x1ee5, 0x2143, 0x18b0, 0x4360, 0x46a4, 0x1930, 0xba5a, 0x44d0, 0x1ad6, 0x371e, 0xb2ce, 0x1534, 0xb09c, 0x1b0b, 0x095d, 0x2f9f, 0x1608, 0x40f7, 0xbada, 0x05c1, 0xbf3e, 0xa521, 0xb29a, 0x42af, 0x404a, 0x0f8c, 0x41c9, 0xb089, 0x1a99, 0xba0a, 0x1fc9, 0xb25b, 0x41a2, 0x4109, 0x40b7, 0x364f, 0x2648, 0x011f, 0x4157, 0xbf47, 0xb037, 0x1b4a, 0x4096, 0x41a8, 0x4246, 0xba0f, 0x40f0, 0xb250, 0xbf59, 0x42b7, 0xb251, 0xba5b, 0xb0d1, 0x4208, 0x4103, 0x40a7, 0x4355, 0xb216, 0x4476, 0x1962, 0xb03d, 0xba1a, 0x1928, 0x1f0b, 0xbf71, 0x2d76, 0x40f6, 0x4369, 0x29d5, 0x42f6, 0x1dd5, 0xb25e, 0xaf1a, 0x42fd, 0x40b9, 0x41bb, 0xa259, 0x40b7, 0x45f6, 0x4283, 0xbf3e, 0xba6c, 0xbae8, 0x41cb, 0xbf08, 0x42f3, 0x15e3, 0x1eb6, 0x386c, 0xb090, 0x1d6c, 0x3160, 0x43ca, 0x1a41, 0xb260, 0x4252, 0x3a39, 0xae9f, 0x41cf, 0xb2ae, 0x40ed, 0x4347, 0xb2ae, 0xba31, 0xbfda, 0x43a6, 0x4125, 0xb036, 0x408f, 0xba00, 0x1ddf, 0x4062, 0xb0ba, 0x4176, 0xba23, 0x2c17, 0x42e6, 0x3bc2, 0x43fb, 0x11b5, 0x400e, 0x439d, 0x3079, 0xb0a7, 0xbf7b, 0xb0e1, 0x4271, 0x44d1, 0xbfb0, 0xb028, 0xb015, 0x43d1, 0x4322, 0x1dcf, 0x42cc, 0xbac0, 0x1847, 0xbfd1, 0xb036, 0xb060, 0x2bea, 0x4020, 0x4193, 0x43d5, 0xaa64, 0x41c7, 0x427c, 0x41ec, 0xbf0b, 0x4398, 0x4200, 0x4283, 0x4000, 0x1f81, 0x44d1, 0x42d6, 0x402c, 0x0e29, 0x4606, 0xb0b9, 0x4093, 0x40fa, 0x42b6, 0xba48, 0x43a9, 0xbf72, 0x1aba, 0xbf00, 0x369e, 0xbf00, 0x4109, 0x251e, 0xba6f, 0x40bc, 0x4464, 0x40d4, 0x3776, 0xb2de, 0xb01a, 0xb05d, 0x27ed, 0xb252, 0x368f, 0xb044, 0x45e0, 0xba62, 0x4135, 0xbf23, 0x4188, 0x41f4, 0xb223, 0x418e, 0x40e4, 0x4256, 0xbf01, 0x411b, 0x1f16, 0x4253, 0xbadd, 0x43e0, 0xaeab, 0x4431, 0x24cb, 0x30b8, 0x406e, 0xb0e9, 0x41aa, 0x43a6, 0x438e, 0x4342, 0x4307, 0x419c, 0x4053, 0x4311, 0x41c7, 0x4152, 0x1ba5, 0x41db, 0x42a0, 0x40b8, 0xbf2e, 0xb01c, 0x423b, 0x1d13, 0xb262, 0xba11, 0x1803, 0x433e, 0x287b, 0x1977, 0xb0ba, 0x297d, 0xbf01, 0xb294, 0x40bf, 0x4391, 0x43e8, 0x232d, 0x431a, 0x4228, 0x375a, 0x36d7, 0x424d, 0xbf60, 0x4071, 0x40e8, 0x3242, 0xb0fe, 0x4214, 0x41a3, 0xbf62, 0x2756, 0x40f0, 0xba17, 0x3696, 0x427f, 0xaaa1, 0xbfa9, 0x4285, 0x427c, 0xa783, 0x4369, 0x274e, 0xb299, 0x411c, 0x40f0, 0x073b, 0x40d4, 0x43f7, 0xba1b, 0xb22d, 0x1c83, 0x1f11, 0x4277, 0x32b1, 0xbf52, 0x424b, 0x438f, 0xbfd0, 0x4118, 0x39e7, 0xba37, 0x3e6d, 0x41f3, 0x4239, 0x2daa, 0xbfa6, 0x432e, 0xb015, 0xb2f6, 0xbaf2, 0x4184, 0x4324, 0x1c78, 0xbaf1, 0xba29, 0xbfca, 0x1b4d, 0xb28b, 0x4577, 0x3ade, 0x43b3, 0xbf64, 0x401f, 0x41d7, 0x4060, 0x1be9, 0x4453, 0xba44, 0x37d4, 0x424a, 0xb006, 0x1882, 0xbac4, 0xba5b, 0x3c59, 0x0c92, 0x42a0, 0xba4e, 0x2d9b, 0xbfe4, 0x441b, 0x2199, 0x05fd, 0x43b2, 0xba42, 0x4301, 0x1ec4, 0x4656, 0xb0e4, 0xb06e, 0xbf82, 0x40a6, 0xaebe, 0x30ca, 0x4181, 0x42a4, 0xb24d, 0x4080, 0x43ca, 0x3592, 0x1943, 0x4217, 0x43ee, 0x08c3, 0xb003, 0x2cbd, 0x4412, 0x2e2f, 0x26db, 0x4179, 0x42a4, 0x36d3, 0xbfb2, 0x4386, 0x431c, 0x44ba, 0x4464, 0xb281, 0x40b3, 0x4152, 0xb25b, 0x41f2, 0xb059, 0xb278, 0x0dfe, 0x425f, 0x4215, 0x3ca0, 0x4234, 0xbad2, 0x41f0, 0x3f2d, 0x43ce, 0x4218, 0xbf12, 0x45e9, 0x1a54, 0x1fff, 0xacd6, 0x410e, 0xbf8f, 0xb0e1, 0x352d, 0x1fed, 0x1af4, 0x428e, 0x3c91, 0xb039, 0x4079, 0x4347, 0x3c04, 0x4424, 0x4260, 0xa798, 0x41b9, 0x4033, 0x4238, 0x2419, 0x4227, 0x0d53, 0xb2c3, 0x42d7, 0xbf14, 0x46a5, 0x0852, 0xa387, 0x13d9, 0x41c7, 0x1f38, 0x43e8, 0x1dcc, 0x43e6, 0x465a, 0x419a, 0x4387, 0x4338, 0x43a0, 0x033e, 0xba5d, 0x4176, 0x3d63, 0x3285, 0x1d0b, 0xacee, 0x4105, 0x44e8, 0x3c90, 0xa4a5, 0xbf17, 0x432e, 0x4020, 0x4367, 0x43ee, 0x43f8, 0x3f58, 0x1f04, 0x08fa, 0x43cf, 0xb097, 0xba07, 0xb228, 0x45c4, 0x09ae, 0xbf31, 0x31a7, 0x3a09, 0x42cf, 0x4019, 0x2390, 0x4145, 0x4179, 0x40bd, 0xb243, 0xbf5d, 0x3f0c, 0x3406, 0x1f8a, 0xba3b, 0xbfb5, 0x43b0, 0x3120, 0xb26c, 0xa3b0, 0x4272, 0x465d, 0x3214, 0xba0e, 0x3a60, 0x4230, 0x2d1c, 0x43ea, 0xbf63, 0x43ae, 0x14fa, 0x38d6, 0x423d, 0xac84, 0xb210, 0x402e, 0xb273, 0x441e, 0x43b0, 0xab4e, 0xb275, 0x42c8, 0x41f1, 0xb2ba, 0x42fd, 0x40b9, 0x4244, 0x42ed, 0x420d, 0x4190, 0xba13, 0xba1f, 0x2758, 0x40d7, 0x1012, 0x41f7, 0xbff0, 0xbfaf, 0x1aee, 0x425b, 0x0e1a, 0x3ddb, 0xb2e9, 0xba50, 0x3fd8, 0x1ba9, 0xb002, 0x437d, 0x4279, 0x34fb, 0xba3b, 0x43e3, 0x4185, 0xba1f, 0xa0db, 0x4218, 0x3d58, 0x0f00, 0xb2b0, 0x429f, 0xb063, 0xbf0d, 0xba52, 0xba07, 0xa5c8, 0x409c, 0x431f, 0xb2f9, 0x1ff7, 0xb0e6, 0x463f, 0x3ac2, 0xba14, 0xba7c, 0xbf43, 0xbff0, 0x40b3, 0x2e64, 0xa3a0, 0x1bac, 0xb2c7, 0x1f9a, 0x31d1, 0x387c, 0x1993, 0x4224, 0xba20, 0x4044, 0xb2f2, 0x1983, 0x428f, 0x11fc, 0x00a5, 0xb071, 0x12ae, 0x4007, 0x15cc, 0xbfcf, 0x416e, 0x0667, 0x1b46, 0xb250, 0x4285, 0x41ea, 0xa38d, 0x43dd, 0x43a7, 0xa77b, 0x0711, 0xba66, 0x445c, 0x1b40, 0x4095, 0x41b8, 0x400b, 0x4079, 0x3567, 0x43a6, 0x461d, 0x435d, 0x4036, 0x42fa, 0xa9a6, 0x43b7, 0xb295, 0x427e, 0x4300, 0xbf54, 0xb0f4, 0x2b0b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x36179bbd, 0x4c84b624, 0xf2f6b902, 0xcc71b138, 0x0afbc809, 0xac7ce4df, 0xb01ce320, 0xe9d87f98, 0xc61c700a, 0x6f44ba87, 0x76d17376, 0x6c662875, 0x88e99f9f, 0x5ec4aa7f, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0xffffffeb, 0x00000415, 0x6000000a, 0x00000000, 0xf16324f3, 0x0000000a, 0xffffe660, 0x000019a0, 0x3cede399, 0xd596226d, 0xe4d0754a, 0xf16324f3, 0x00000132, 0x0000017d, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4194, 0x41e1, 0x426d, 0xabea, 0xa7e7, 0x181f, 0x4209, 0x38b4, 0x4330, 0x42fd, 0x40db, 0xbfb4, 0x1903, 0x4570, 0xba65, 0xa18b, 0x1868, 0x41bc, 0xb269, 0x3a0f, 0xb2ef, 0x43d0, 0x301c, 0x1b59, 0x4185, 0xa33d, 0x43c3, 0x448a, 0xa735, 0x1c22, 0x4273, 0x4584, 0xb215, 0xbfb5, 0xb059, 0x43ed, 0x412b, 0x1c26, 0xb0cb, 0xbae5, 0x40ca, 0xb28a, 0x19c5, 0x4120, 0x4274, 0x4594, 0xba53, 0x42c7, 0xbf71, 0x4182, 0x43e9, 0x412a, 0x28e9, 0x4057, 0x4310, 0x41a1, 0x40cd, 0xbfac, 0xba50, 0xaa8a, 0x26a9, 0x432c, 0x46b1, 0x454c, 0x1542, 0x4222, 0x02f8, 0xb2f9, 0x4014, 0xb23d, 0xbf55, 0xbfd0, 0x404a, 0x3680, 0x40a4, 0x4275, 0x42b9, 0x4249, 0xbfa7, 0x2da6, 0xa070, 0x4377, 0x10a2, 0x411c, 0x1f50, 0x466a, 0xa9fd, 0x237a, 0x43e4, 0xb2b7, 0x4457, 0x4157, 0x2ec4, 0x4606, 0x41f1, 0x4432, 0x1376, 0x4143, 0x4663, 0x43ae, 0xbf8a, 0x426f, 0xb299, 0x422f, 0x1c31, 0x415c, 0x39de, 0xbf60, 0x25cc, 0x4097, 0xb24e, 0xbac7, 0xb2c8, 0x4030, 0x43f2, 0x41af, 0x4012, 0xb2d2, 0x4415, 0xbf9e, 0x4208, 0xba30, 0x1d45, 0x0615, 0xb2d8, 0xba11, 0x40f9, 0x4011, 0x42c7, 0xb262, 0x41df, 0x429f, 0x0b94, 0x1fbf, 0xbf94, 0xb2c3, 0x4073, 0x40e2, 0x4166, 0xa2e0, 0x4475, 0x4026, 0xb0a2, 0xb20a, 0x1c3b, 0xa334, 0xa04c, 0xaae3, 0xbf85, 0xba51, 0x42c5, 0xa2df, 0x43c6, 0x42f2, 0x4252, 0x43bb, 0xbf96, 0xa5a2, 0x41ae, 0xa663, 0x11de, 0xbaeb, 0x1b6f, 0x42f3, 0xba22, 0xbfe1, 0x438c, 0xba76, 0xb2e7, 0xa84c, 0x4389, 0x14e8, 0x316f, 0x1e56, 0xa1ea, 0x4270, 0x0962, 0x1f5d, 0x40f4, 0xbf69, 0xbae7, 0x4019, 0x1c1e, 0x43fe, 0x4245, 0x1a32, 0x418f, 0x10f5, 0x1a47, 0x1d6f, 0xa720, 0xb0e0, 0x43ae, 0x0ac4, 0x4232, 0x4044, 0x42fe, 0xbaf8, 0xba6b, 0x19ef, 0x408b, 0x4287, 0x41a5, 0xbfc6, 0x1d24, 0xb2f7, 0xb291, 0xaf34, 0x40f3, 0x1a78, 0xba54, 0x08f7, 0x19b6, 0x3b08, 0xba23, 0x318f, 0x1fe9, 0x4646, 0xbf41, 0x1b4a, 0x4224, 0xb201, 0x221a, 0x4027, 0xb09c, 0xbada, 0x438d, 0xa29c, 0xba71, 0x43ed, 0x40ee, 0x408a, 0x4366, 0xbf02, 0xb2f8, 0xb09a, 0x35f4, 0xb2db, 0x0d06, 0xba58, 0x42c8, 0x4374, 0x40f9, 0x42c4, 0xbaee, 0x4014, 0xba77, 0x4330, 0xb0f9, 0x43b5, 0x3ee4, 0xba5c, 0xa37c, 0x18f9, 0xba01, 0x43e4, 0x45ad, 0x3b98, 0x431c, 0xae45, 0xb0e9, 0xba6a, 0xbf3f, 0xb093, 0x40cf, 0x31f1, 0xbae3, 0x18e3, 0xafbb, 0x46fb, 0x4467, 0x4377, 0x4439, 0x46fd, 0x01e6, 0x43ca, 0x423d, 0xbf80, 0xb2f8, 0x439c, 0x4158, 0xbf9e, 0x4343, 0x433d, 0x41a2, 0x2ced, 0xbae8, 0x1b42, 0x3e7d, 0x41ba, 0x4022, 0x43a7, 0x424b, 0x4253, 0xb0e9, 0x42b3, 0x1d29, 0x2454, 0x412b, 0x4253, 0x4156, 0xbfd3, 0x423a, 0x400b, 0xba35, 0x1cf9, 0x00db, 0x04bb, 0x1b4c, 0xbafc, 0x1fb0, 0xb034, 0xb2a7, 0x4328, 0x4248, 0xbf67, 0x1bc5, 0x42c7, 0x157d, 0xb206, 0x4040, 0xa039, 0xaf24, 0x41b8, 0xa314, 0x2db8, 0x1a50, 0xb2df, 0x35ca, 0x423d, 0xb23f, 0x41ee, 0x456c, 0x4365, 0xb2d6, 0x421a, 0xba7d, 0x43be, 0x43a1, 0xbf39, 0x2460, 0xb271, 0x2223, 0x3e58, 0x1ac1, 0x4225, 0x422b, 0xb0af, 0xb2df, 0xba76, 0x4085, 0x4226, 0x1f02, 0xb2a0, 0x41a6, 0xa49b, 0xbfb0, 0x4097, 0x4145, 0xbf82, 0x414e, 0xb240, 0x246d, 0xba4b, 0xaca3, 0x35f0, 0x42c9, 0x3d4b, 0x46db, 0x43fe, 0xba60, 0x1736, 0x100c, 0xbaf4, 0xa17b, 0x42ae, 0xbfdd, 0x0867, 0xb0b5, 0x4236, 0x420b, 0x4256, 0x4166, 0x4653, 0x4372, 0x1a16, 0xa649, 0x43e3, 0x42a6, 0xbf5f, 0x3fd3, 0x31db, 0x41fa, 0x2e10, 0x3186, 0xa1ae, 0xb012, 0xb2f5, 0x3260, 0x44a2, 0x43dc, 0x05d4, 0x41a8, 0xb030, 0x443b, 0x2d52, 0xba2f, 0x44f2, 0x4200, 0xba05, 0x42f4, 0x431b, 0x1ae3, 0xb2db, 0x4405, 0xbaeb, 0x3772, 0x423d, 0xbf1f, 0x20f7, 0x40be, 0x1b66, 0x1b9f, 0x439e, 0x0d71, 0xa52b, 0x1fd0, 0x2322, 0xbff0, 0xac82, 0xbfa0, 0x274e, 0x4079, 0xafe0, 0x1f38, 0x4235, 0x1de4, 0x403d, 0x434b, 0x343f, 0xba6d, 0xba07, 0xbf02, 0x439f, 0x46c1, 0xba20, 0xb206, 0x4127, 0xba1f, 0x4226, 0xb295, 0x436b, 0xb2aa, 0xa2b9, 0xba5e, 0x423f, 0xb008, 0xb00f, 0xb081, 0x0a56, 0xbfa3, 0x3f44, 0xb0a9, 0x4022, 0x244d, 0xb07d, 0xba2b, 0xbf90, 0xbfa0, 0x41a1, 0xba26, 0x2997, 0xa317, 0xb2ce, 0x410e, 0xba51, 0x40a8, 0x4285, 0x20bd, 0x411d, 0x1eaa, 0xbfcd, 0x4027, 0x4032, 0x42f8, 0x286a, 0xb212, 0x4191, 0x184a, 0x43a3, 0x4097, 0xba16, 0x369b, 0x4336, 0x46e2, 0xa6ed, 0x41f2, 0x0322, 0xb2ab, 0xbf48, 0x4139, 0x4077, 0x4096, 0x1da1, 0x4266, 0x4365, 0xb051, 0x413b, 0x45cb, 0x1533, 0x425c, 0x423e, 0xba34, 0x4679, 0x3ad5, 0x439b, 0x420d, 0x4228, 0x4406, 0xbf42, 0x4211, 0x4272, 0xb2a1, 0xb272, 0xb2d0, 0xa24b, 0xb0b4, 0x36f9, 0x1c6f, 0x405c, 0x36ad, 0x411f, 0x41d2, 0x426f, 0xb2f9, 0x29f9, 0x4572, 0x43cf, 0x4016, 0x4068, 0x4262, 0x151a, 0xbfa5, 0xafd2, 0x4382, 0x4308, 0x46e4, 0x43cb, 0x18af, 0x428e, 0xac0f, 0x1f7c, 0xbfc1, 0x425a, 0x424e, 0x120e, 0x43fa, 0x0332, 0x19c4, 0x4044, 0xbf0a, 0xbac9, 0x435b, 0x4255, 0xbf92, 0x41b7, 0x42a9, 0x08e3, 0x4114, 0x42ad, 0xb27b, 0x1ab9, 0x0adf, 0xbaed, 0x40b7, 0x307f, 0x4457, 0x40f1, 0x4296, 0xb218, 0xb21d, 0xb2d0, 0x4054, 0x40b4, 0x41ee, 0x45d1, 0x1f57, 0x011c, 0x4174, 0xbf02, 0x417a, 0x4124, 0x2931, 0x4021, 0xbac1, 0xb2ac, 0x4034, 0x4138, 0x44a1, 0x41a5, 0x4034, 0x30f3, 0x1952, 0xb003, 0x3fb2, 0x4624, 0xb04f, 0x3f0f, 0xbf54, 0xa9c0, 0x43af, 0xb2b0, 0xb0f0, 0xb2d8, 0x4288, 0x17b6, 0x2e75, 0x416f, 0x1f51, 0x1a93, 0xbfe4, 0x410c, 0x4465, 0x407b, 0x426d, 0x46a2, 0x37d7, 0xbfc7, 0xb2ee, 0x09c2, 0x41b0, 0x41a5, 0xba50, 0x4081, 0x459d, 0x4437, 0x433e, 0x1473, 0x435d, 0xbf00, 0x43fa, 0x1863, 0xb021, 0x4585, 0x4096, 0x1b2a, 0xbfb2, 0x067e, 0x18e2, 0xb20f, 0x43af, 0x4080, 0x1b32, 0xb29e, 0x0a11, 0x434a, 0x4392, 0x424e, 0xb2e3, 0x40a6, 0xab7a, 0x09d9, 0x1fed, 0x43c1, 0x19d4, 0x4089, 0xb236, 0x1f21, 0xb2e0, 0x4289, 0x461d, 0xaaf9, 0xbf3c, 0x1183, 0x0293, 0xa59b, 0x43bc, 0xb047, 0x403e, 0xb26a, 0xbfd2, 0x11e9, 0x4672, 0xbfa0, 0x414f, 0x39c9, 0xb2e5, 0x43a6, 0x43f4, 0xa04b, 0xa4bb, 0x4359, 0x4297, 0x41cb, 0x44e3, 0x2f11, 0xb237, 0x1971, 0x1c47, 0xbf3a, 0x424d, 0x448d, 0xb211, 0x412c, 0x41c1, 0x41e1, 0x41cc, 0x4265, 0xb08b, 0x4224, 0xba4b, 0xb240, 0xba27, 0xba7b, 0xbaca, 0xbfac, 0xb27c, 0x1d89, 0x43be, 0x1f77, 0xbf00, 0x43ea, 0x4326, 0xba22, 0x0315, 0xbf02, 0x42a2, 0x405a, 0xba05, 0x4152, 0xbfb0, 0xba4c, 0x42c6, 0x4334, 0x18b9, 0x4250, 0x40c8, 0xbf77, 0x33a6, 0xa4d7, 0xba55, 0x4361, 0x274f, 0xba03, 0x1ae7, 0x4038, 0xbf8d, 0x40fe, 0x1b4b, 0x4063, 0x40b7, 0xb2c8, 0x40ce, 0x38c1, 0xba40, 0x410f, 0x4445, 0x33b7, 0xba40, 0x4378, 0x3c3e, 0x4286, 0x41b1, 0xb219, 0xb23e, 0x437b, 0x2d6d, 0xbf9f, 0x1a09, 0xb00d, 0x436b, 0x41a5, 0x2188, 0xb29e, 0xb02f, 0x4255, 0x40ff, 0x4095, 0x4226, 0x1baf, 0x46ad, 0x262a, 0xa6a5, 0x1bf9, 0x41f0, 0x42fb, 0x43b9, 0xbf4a, 0x1d63, 0x1a48, 0x3668, 0x2ad8, 0xa836, 0xb0ad, 0xb205, 0xbf96, 0x1e1d, 0x27ba, 0x4677, 0xbac0, 0xa376, 0x40be, 0xbf05, 0xb2e9, 0x4164, 0x3e9d, 0x43b9, 0x4330, 0x1136, 0xba46, 0x41ae, 0x41c7, 0xb20f, 0xbafd, 0x42a2, 0x4007, 0xa269, 0x434e, 0xac40, 0x42da, 0x4553, 0xb2f3, 0xbfce, 0x433f, 0xa7f6, 0x4294, 0xb2e6, 0x41f3, 0x4355, 0x10e0, 0xb226, 0xb239, 0x1a8c, 0xb014, 0x431c, 0x1391, 0xba2d, 0x41ba, 0x402f, 0xb025, 0xb2bb, 0x35c5, 0xa0c6, 0x4042, 0xb27b, 0x4120, 0x250f, 0x4272, 0xbf32, 0x1a7c, 0x1e48, 0xbaf6, 0x1f12, 0x432a, 0x4608, 0x40f1, 0x1b8e, 0x1e05, 0x4021, 0x4053, 0xbf48, 0x41aa, 0xb2e4, 0xb230, 0x43ea, 0x1f4c, 0xa125, 0x1ef8, 0x4139, 0x4376, 0xbf6d, 0x42db, 0x32da, 0x4222, 0x2930, 0x2189, 0x4116, 0xb077, 0x429a, 0x40db, 0x45ac, 0x1a86, 0x1546, 0xb21d, 0x463a, 0x4062, 0xbf3e, 0x1a52, 0x407c, 0xba38, 0x454e, 0xa28b, 0x42a2, 0x3760, 0x1e45, 0x19ab, 0x292e, 0x0f80, 0x1d7e, 0x4572, 0x19be, 0x4318, 0xb0e2, 0x2ead, 0xbf15, 0x43d3, 0xb297, 0x4156, 0x4274, 0x3215, 0x43d5, 0x4101, 0x4009, 0x14bb, 0x4217, 0xb228, 0xba72, 0x1a16, 0x41af, 0x40c8, 0x4314, 0xb22a, 0x43a4, 0xb212, 0x4139, 0xb23d, 0x1b80, 0x1b4f, 0xb064, 0xbfb1, 0xb0ea, 0x419e, 0xa853, 0x406b, 0x41ba, 0xbfbd, 0x4353, 0x4018, 0x45e1, 0xba2b, 0xb264, 0x42e9, 0xb2e9, 0x40c2, 0x395e, 0x437f, 0x43f4, 0x406e, 0x0807, 0xba3e, 0xbf88, 0x430a, 0x43af, 0x4417, 0x426d, 0x410c, 0xbff0, 0x4683, 0xb25f, 0xbf00, 0xbf5c, 0xba47, 0x4033, 0x1942, 0x43e0, 0x12e6, 0x3c46, 0x436c, 0xba3c, 0x0145, 0x43cc, 0xb2d6, 0x4477, 0xb2d5, 0xb0d1, 0x4189, 0x43eb, 0x1ac6, 0xb258, 0xb0b6, 0xbf57, 0xbf60, 0x4285, 0x4364, 0x416f, 0x4111, 0x0d56, 0xb286, 0x4066, 0xb2dd, 0x4156, 0x4217, 0xa9c0, 0x43c6, 0xaf85, 0xb258, 0x41cb, 0xbfc4, 0x4220, 0x43bc, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5163634f, 0x69953bc6, 0xc471194b, 0xd797bd76, 0xca82e30c, 0xd0e3d0b1, 0x315d5946, 0x74209518, 0xbfc706cf, 0x907cff0b, 0x9e3f1e57, 0x892343bb, 0x37dc0fed, 0xbc49e0f0, 0x00000000, 0xa00001f0 }, FinalRegs = new uint[] { 0x00000025, 0x9eb0014e, 0xfffec9da, 0xfc97ffff, 0x00000e10, 0x00000025, 0xffffffda, 0x9eb00062, 0xbfc706cf, 0x000000ad, 0x00000000, 0xfffee373, 0x37dc0fed, 0x9eaffe4e, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbacf, 0xbfa6, 0xb202, 0x40b6, 0x4165, 0x42b2, 0xb299, 0x40d5, 0x4183, 0xb09b, 0x4018, 0xb2a2, 0x4123, 0x0034, 0x45b6, 0x05e6, 0xaadf, 0x400e, 0x44da, 0xb220, 0xbfc8, 0x4147, 0x089b, 0x40e1, 0xa64f, 0xac9d, 0xb2cc, 0x39d6, 0xbf46, 0x42c9, 0x4314, 0x2b9a, 0x42cc, 0x19d2, 0x187f, 0x3b09, 0xba5f, 0xb2ef, 0x4281, 0x4298, 0x4253, 0xb284, 0x40ef, 0x4148, 0xaa53, 0x2d5e, 0x4301, 0xbf49, 0x4365, 0x3271, 0xb278, 0x10ca, 0x3100, 0x4298, 0xbfaa, 0xb27e, 0x46b9, 0x1972, 0xb08a, 0x428c, 0x4398, 0x1610, 0x4160, 0x40e9, 0x403b, 0x0964, 0xbf0d, 0x40e6, 0x40b1, 0x4183, 0x43af, 0x4195, 0x3dda, 0xa5d0, 0x2ba7, 0xb0d0, 0x1486, 0x4312, 0x42c5, 0xa382, 0x016e, 0xbf5c, 0x428b, 0xb2f2, 0x4367, 0xbf27, 0xba7b, 0x4676, 0x4229, 0x41f9, 0xb0b1, 0x4371, 0x40e3, 0x41f3, 0x1d96, 0x4019, 0xbad5, 0x0714, 0xbaf8, 0x3df6, 0x1b08, 0xb2ad, 0x32ce, 0xb26b, 0x120d, 0x438c, 0x41a6, 0x1e12, 0x421c, 0x416b, 0xb2ca, 0x4298, 0x31dc, 0xbf0c, 0x27c0, 0x41ea, 0x18b6, 0x4097, 0x42ed, 0x059a, 0x42fd, 0x428a, 0x4216, 0x1846, 0x43ec, 0x4194, 0x247c, 0x4347, 0xbf8f, 0x45b4, 0xb2cd, 0x41e7, 0xb2b8, 0xaf7f, 0xbf6c, 0xbae3, 0x4117, 0x43ae, 0x41b9, 0xbfe0, 0x144c, 0xbf13, 0x4292, 0x0fcd, 0x4329, 0x43f7, 0x1aba, 0x2109, 0x41e3, 0x4426, 0x401c, 0x40ea, 0xae86, 0x42c2, 0xbf02, 0x43c1, 0x2e2f, 0x42c3, 0x40a1, 0x4161, 0x43ff, 0x1a3e, 0x4334, 0xb234, 0xb256, 0x4037, 0xbaf8, 0x40e5, 0xba12, 0x2f78, 0x4019, 0x4144, 0x4305, 0x21a3, 0xbfcf, 0x0a76, 0x412b, 0xad51, 0x01b7, 0x4276, 0x1aa1, 0x42eb, 0x405b, 0xa6b4, 0x40d1, 0x410a, 0x41f4, 0x1a37, 0x1bd6, 0x38ea, 0xb27b, 0x4120, 0x4238, 0xb250, 0x4099, 0x437a, 0xbfce, 0xba3b, 0xbfb0, 0xb211, 0xa07d, 0x1deb, 0xbad9, 0x448d, 0x1528, 0xbf00, 0xb2d4, 0x44b3, 0x1efb, 0x408d, 0x41c8, 0x43f3, 0x4289, 0x43fa, 0x2672, 0xb2cc, 0x4253, 0x4234, 0xbadc, 0x4049, 0x430a, 0xb280, 0x42ef, 0xbfb7, 0xb276, 0xbac8, 0x416f, 0xb2aa, 0x1bc1, 0x0b1a, 0xb0ee, 0x4022, 0xb273, 0x2575, 0x1867, 0x1e00, 0xb245, 0x41d9, 0x446e, 0xba5f, 0xb07a, 0xb263, 0x2274, 0x135c, 0x4045, 0xb29a, 0x19c5, 0xbf56, 0x4361, 0x422a, 0x4193, 0x4138, 0x0c7c, 0x40b9, 0x2b3b, 0xb2e2, 0xb075, 0x40b4, 0x434e, 0x4363, 0x43d6, 0x10cf, 0x400d, 0x1fc8, 0xbf3d, 0x41ee, 0x4244, 0x2a6c, 0x1e41, 0x3e24, 0x4675, 0x43b5, 0x4032, 0xb080, 0xbf95, 0x431d, 0x4159, 0x43d6, 0xba46, 0xb025, 0xbfae, 0x4024, 0x14c7, 0x2339, 0x41f7, 0x445f, 0x43e8, 0x12db, 0xba11, 0x42bb, 0x42d0, 0x1b2e, 0x1d0b, 0xba72, 0x1c00, 0x1672, 0xbf5a, 0x40bc, 0x1094, 0xb068, 0xb053, 0xb027, 0xb22a, 0x213b, 0x4133, 0xb0e5, 0x1c94, 0xb244, 0x4228, 0x41ec, 0xb2a8, 0x440a, 0x3ea0, 0x3cd2, 0xb28b, 0x431a, 0x4372, 0x4084, 0xb2b5, 0x4135, 0xbf3b, 0x0936, 0x4385, 0x3455, 0x1f02, 0xb2b5, 0x00b2, 0x0a84, 0x43b7, 0x41a9, 0x42ca, 0x2e91, 0xbf18, 0xb242, 0xbfb0, 0x4220, 0xb2bf, 0x44ba, 0x41e7, 0x4181, 0x1eaa, 0x18c0, 0x1a7a, 0x4123, 0x3ffe, 0xa60e, 0x434f, 0x082b, 0x428f, 0xbf4b, 0x3482, 0x18c0, 0x4158, 0x4454, 0xbf5e, 0xbae3, 0x18cf, 0x4221, 0x42e8, 0x2748, 0xbfbd, 0x424f, 0x41f9, 0x41d8, 0x40e2, 0x285a, 0x426a, 0x43e0, 0x4170, 0x4387, 0xaf96, 0x3f64, 0x4066, 0xbf28, 0x4089, 0x2a20, 0x43c9, 0x43fd, 0x4096, 0x460b, 0xbf7b, 0xbadd, 0x1a8d, 0x1c50, 0x401c, 0x41ea, 0xa29b, 0xba76, 0xbae3, 0xb2f9, 0x4240, 0x4193, 0x0dd9, 0xbf9a, 0xba6e, 0x4362, 0x2f82, 0x41b2, 0x4111, 0x442b, 0xa3ab, 0x4069, 0xba77, 0x3e1a, 0xbfa0, 0xaac3, 0x1fa1, 0x4122, 0x4601, 0x43f2, 0x4031, 0x43b3, 0x1305, 0xb2ff, 0x1da3, 0x40a3, 0x4019, 0xbfba, 0x40a3, 0xba43, 0x41f9, 0xb2a0, 0xae39, 0x41d2, 0x0268, 0xb2b4, 0x4082, 0x4197, 0x1ed8, 0x4323, 0x43a4, 0xbaef, 0x082d, 0x4065, 0x4188, 0x42c9, 0xa7c3, 0xbf45, 0x4269, 0x401e, 0x45b1, 0x427b, 0x43cf, 0x4242, 0xba2b, 0xbf0f, 0x4392, 0xb09c, 0xb284, 0x4117, 0x17e3, 0x414a, 0xb0ac, 0xbfd7, 0xba34, 0xbf70, 0xb0ba, 0x4243, 0xb20e, 0x431e, 0x441b, 0x43e7, 0x4005, 0xacd8, 0xba47, 0xbf85, 0xa91c, 0x42c8, 0x0b69, 0xa1d4, 0x1f9d, 0x4100, 0x41c3, 0x40a5, 0xb2a1, 0x1f71, 0xb2d3, 0x4424, 0x4176, 0x439e, 0xb2e0, 0x42f8, 0x40d1, 0xb204, 0x4617, 0x462b, 0x40e6, 0x416f, 0x4197, 0xb2ea, 0x42cf, 0x0d0d, 0xbfa7, 0xb2b5, 0x13da, 0xbaec, 0x4351, 0x42af, 0xbaf1, 0xb2e7, 0x0ff2, 0x45d2, 0xbf9a, 0x414d, 0x1ddb, 0x4047, 0x4106, 0xa497, 0x40b7, 0x2043, 0xbfe1, 0x40e4, 0x41e4, 0xb207, 0xb255, 0x4134, 0x285e, 0xb24d, 0xb28c, 0x40ab, 0x4359, 0xbfc9, 0xb25f, 0x3c0a, 0x40d3, 0x1b61, 0x41fe, 0x4443, 0x45cc, 0x4242, 0xb275, 0xb24b, 0x228c, 0x42c1, 0x437a, 0x40d9, 0x27fc, 0x38fc, 0x43e4, 0x418f, 0xbf3c, 0xb043, 0x18c6, 0x4246, 0xbfab, 0xb248, 0x08df, 0xba5e, 0x419f, 0x41d2, 0x41c1, 0x4084, 0x42a7, 0x46e4, 0x40d0, 0x43e2, 0xaef6, 0x40f3, 0xb2f6, 0x422b, 0xa7b8, 0x4596, 0x405a, 0xb203, 0xad67, 0xb023, 0xbf0b, 0x3f5c, 0x46d9, 0x29a6, 0x42c9, 0x41c7, 0xbaf7, 0x1173, 0x41cf, 0x4388, 0xbf5b, 0x45c3, 0xba74, 0xbad9, 0xb279, 0x40ad, 0x4591, 0x4332, 0x40ae, 0x1963, 0x1ced, 0x0f02, 0xba62, 0x42b6, 0x4260, 0xbaed, 0x42e7, 0xb2f9, 0x312b, 0x43d8, 0x1dfa, 0x1440, 0xa31d, 0x44c2, 0xba08, 0xb01a, 0xbac7, 0xbf9d, 0x45dc, 0x43ca, 0xaad2, 0x0c3a, 0xaa18, 0x1024, 0x3f34, 0x46fb, 0xb0e3, 0xb2e8, 0x331a, 0xbf80, 0x2092, 0x4257, 0x4328, 0xb0e0, 0xb07d, 0x4268, 0xbf76, 0x0232, 0x42df, 0xb2a0, 0xb2ab, 0x4022, 0x402f, 0x42d3, 0x1ec6, 0xb2f6, 0x18a4, 0xbaed, 0x41a9, 0xbad1, 0x4175, 0xb06b, 0x405f, 0x4189, 0xaa10, 0x42c4, 0xba1a, 0xa362, 0x2a89, 0x4022, 0xb0df, 0x0583, 0xba04, 0xbf98, 0x46b4, 0x0d9a, 0xbaec, 0xba1c, 0x2507, 0x43be, 0x43d0, 0x407c, 0xbf80, 0x0db8, 0x4114, 0x4329, 0xbf4a, 0x442b, 0xb0d9, 0xb02a, 0x4063, 0x068e, 0x40a1, 0xbfcb, 0x4398, 0x1aab, 0x42ec, 0x41b6, 0xba5a, 0x411e, 0x456b, 0x42a1, 0xba39, 0x4183, 0x4222, 0xbfb1, 0x43ca, 0xb05a, 0x4474, 0x4546, 0x1140, 0x18cc, 0x43e2, 0xba0c, 0x2c60, 0x428a, 0x426c, 0x1909, 0x42b3, 0xb049, 0x425a, 0x4234, 0x406a, 0x02de, 0x0a53, 0x4038, 0x4079, 0xbfe0, 0x40d4, 0xba20, 0x4010, 0xbaeb, 0xb2b8, 0x3c73, 0x384d, 0xbf5f, 0xb22c, 0x3e1f, 0xb2f5, 0xa11e, 0x1d16, 0x1cd4, 0xbfaa, 0x0d49, 0x4112, 0x09c1, 0x4249, 0xb211, 0x4393, 0xb210, 0xb295, 0xbf43, 0x08a4, 0x40ad, 0x4635, 0xbf70, 0x462d, 0x07ae, 0x4357, 0xb07c, 0x433c, 0x1d49, 0x394d, 0x4340, 0x4310, 0x3c5b, 0xae19, 0xb044, 0xb0c1, 0x4675, 0x3437, 0xb225, 0x4013, 0xba48, 0x42e5, 0x382d, 0x42f8, 0x01ac, 0xbf28, 0x426a, 0x424b, 0x45ce, 0xb0f4, 0x40fc, 0xb28a, 0xb2aa, 0xb098, 0xb2ec, 0x4193, 0x1b94, 0x40c9, 0x4145, 0x22d4, 0x14ab, 0xbf32, 0x3629, 0x46b5, 0x454b, 0x1c0c, 0xbaf7, 0x1078, 0xb20f, 0x434d, 0xbaff, 0x4327, 0x4282, 0x4251, 0x3560, 0x419b, 0x4694, 0x1f84, 0x42e6, 0xa35c, 0x4194, 0xa25f, 0x2f49, 0xbace, 0x1cdf, 0xb062, 0x2c47, 0x41b0, 0xbf77, 0x429e, 0xb280, 0x42e6, 0x40e5, 0xb282, 0x4002, 0x08db, 0xba3f, 0x46fc, 0xb00b, 0xba3b, 0x41dd, 0xb286, 0x1677, 0x1eff, 0xb254, 0xb2c2, 0x0100, 0x1699, 0x1c80, 0x00fd, 0x0604, 0x3bfd, 0x15b4, 0xba0c, 0x25fc, 0x42db, 0x4354, 0xbf0b, 0x1c85, 0x40cc, 0xb2f8, 0xb01c, 0x43f9, 0xb2e3, 0x438f, 0x45cb, 0x1d66, 0x430c, 0x46c0, 0x42bc, 0x41d6, 0xbfaf, 0xba63, 0x078d, 0x42c2, 0x4266, 0x4591, 0x2b98, 0x4478, 0x1fa1, 0x3b41, 0xb0ea, 0xb032, 0x4033, 0xb093, 0x0e51, 0x4357, 0xba31, 0xba29, 0xaa85, 0x40e3, 0x1e97, 0x1f3a, 0x067b, 0x428d, 0x4266, 0x430c, 0xb2ee, 0xbfcb, 0xac4b, 0xba04, 0xb009, 0x4290, 0x12a4, 0x40a2, 0x1d68, 0x4140, 0x07d3, 0x1fcb, 0xb226, 0x03c1, 0x459d, 0x42a3, 0x40a1, 0x409f, 0x1a49, 0x4040, 0x39e8, 0x4214, 0x40c5, 0xb066, 0xbf4e, 0x43ec, 0x01da, 0x420f, 0x43d5, 0xba21, 0x0941, 0xba04, 0x401a, 0xba44, 0x421c, 0xbf98, 0xbac2, 0xa729, 0xbf80, 0x0966, 0x1970, 0x402f, 0x22fc, 0x432b, 0x4026, 0x434b, 0x43e7, 0xb06f, 0xbf08, 0x403d, 0x1454, 0x44e8, 0xbf4c, 0xbafd, 0x42cd, 0x104f, 0x4361, 0x4253, 0x34fe, 0x1d4d, 0xa421, 0x21da, 0x4488, 0xbafb, 0x418f, 0x41ec, 0xb203, 0x4190, 0x45d6, 0x3a4f, 0xbac0, 0xbf9c, 0x45a4, 0x43c1, 0xba5c, 0x0eaa, 0x4241, 0xb230, 0xb21a, 0xb070, 0x43f9, 0x40a0, 0x42ec, 0x423c, 0xba04, 0x41b0, 0xb25d, 0xb09e, 0x462e, 0x41ae, 0x40f5, 0x4338, 0x28dc, 0xb004, 0xaf65, 0xb29b, 0x40d0, 0x2689, 0x4302, 0xbf37, 0x1467, 0x40ff, 0xab16, 0x40cf, 0x45a8, 0x403c, 0x4340, 0x05f5, 0x2cca, 0x42d3, 0x1583, 0x1a0e, 0x40ae, 0x3d53, 0x412e, 0x4354, 0x4328, 0x461a, 0x4208, 0x413c, 0x366a, 0xbad5, 0x41be, 0x1b57, 0xbf3f, 0x43d2, 0x425f, 0x324a, 0xb0b4, 0x3dbb, 0x4395, 0x4168, 0x4381, 0xb0af, 0x1b4b, 0x43b9, 0xb272, 0xb290, 0x3ca5, 0xba31, 0xba02, 0x43a2, 0xba37, 0xbf00, 0x1d08, 0x4442, 0x4261, 0x3c8b, 0x09bd, 0x4051, 0x294a, 0x435e, 0xbf9a, 0x426b, 0xb0de, 0xba13, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xb365a6c0, 0x01723518, 0x0009419f, 0x745fc87d, 0x5ed42207, 0x1f49801a, 0x138d5de7, 0xd2498a44, 0x0115d59c, 0x1cc12bbe, 0x5e000be4, 0xe36b4a94, 0x3974216e, 0x5afb38c7, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x69000004, 0x5c11201c, 0x5c1120b9, 0xb920115c, 0xfffffed0, 0x01a40000, 0x00004ffb, 0x69000000, 0x5c1120b9, 0xf9076780, 0x42812c14, 0x000014ca, 0x00001650, 0x5afb4adf, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x3e9f, 0x423a, 0xb038, 0x1a66, 0x2ab4, 0xbf88, 0x41f2, 0x43ed, 0x46e2, 0xbafa, 0xb277, 0xacc3, 0x3d1f, 0xb2f5, 0x008e, 0x4288, 0x298f, 0x4058, 0x1fc4, 0x4234, 0x4372, 0x422f, 0xb260, 0x41ca, 0x4030, 0x4004, 0x40a9, 0x40c8, 0xb2e9, 0xb24e, 0x1d5a, 0xbf9b, 0xb062, 0x4160, 0x3cb1, 0xba5f, 0x43a1, 0x4362, 0x4553, 0x4260, 0x4329, 0x1061, 0xba58, 0xb2f4, 0x40ad, 0x41be, 0x436a, 0x43a3, 0xbfc0, 0xb0f6, 0x401a, 0x0c62, 0x1513, 0xb205, 0xb215, 0x090b, 0x1c21, 0x28af, 0x4092, 0xb294, 0xbfbd, 0x2cb1, 0x1b1e, 0x0984, 0x4175, 0xa7e8, 0xb273, 0x2636, 0x410b, 0x45c0, 0x41ec, 0xb2eb, 0xb0d0, 0x42ca, 0xba52, 0x4185, 0x1455, 0x4600, 0xb215, 0x1947, 0x24af, 0xba32, 0xbfab, 0x40ac, 0xba0b, 0xba5b, 0x07fa, 0x0723, 0xb2c4, 0x2163, 0x42d5, 0x4004, 0x40a7, 0x45d8, 0x43af, 0x42b4, 0x465d, 0xba27, 0x4395, 0x2d9c, 0x192f, 0x1a6a, 0xb28d, 0xbfc7, 0x458b, 0x4281, 0xadc4, 0x4092, 0x125e, 0xbfac, 0x43c8, 0xadde, 0x2713, 0xb265, 0xbf62, 0x3095, 0xbff0, 0x1bd1, 0x40a8, 0x43e3, 0x18c1, 0x41b0, 0x3fb9, 0x18ae, 0x4314, 0x347e, 0x4258, 0x45b5, 0x4660, 0x401d, 0x1dcc, 0x4667, 0x420b, 0x4172, 0xac70, 0x39f3, 0x4097, 0x19b6, 0x41fe, 0xb0c1, 0x4623, 0x1394, 0xbf25, 0xba65, 0x4101, 0x439d, 0x42e1, 0x4333, 0xb0e5, 0x41dd, 0xb268, 0x22c7, 0x2674, 0xba71, 0x421f, 0x4450, 0x0cd7, 0x40e4, 0x1b43, 0x3ea7, 0x1311, 0xbf4e, 0x411b, 0x4112, 0xa8fa, 0xbf19, 0x4015, 0x3844, 0x40d4, 0x4087, 0xbfa7, 0x1f9b, 0x0c01, 0x213b, 0x3a56, 0x4242, 0x40bf, 0x4233, 0xb046, 0x42b5, 0x4556, 0x3058, 0x42b8, 0x36be, 0x437b, 0x1eb6, 0xbacd, 0x4173, 0x433f, 0x42a2, 0x41f3, 0xba23, 0x412f, 0xacfb, 0x4221, 0xbf25, 0x40b0, 0x40b4, 0xba72, 0xb0f2, 0x461f, 0x3123, 0x037e, 0x4580, 0xa9da, 0x2938, 0x42a6, 0x3aaa, 0x4125, 0x2721, 0xbfa4, 0x2a82, 0xb215, 0x41c0, 0xa1c3, 0xb27f, 0x4223, 0xba05, 0x1e73, 0x4054, 0x4629, 0x4415, 0x1cae, 0xb222, 0x1ac2, 0x424f, 0x206a, 0x4080, 0xb2c3, 0x412a, 0xa37e, 0x4037, 0xb26d, 0xb09a, 0xb211, 0x442d, 0xaca9, 0xbf0d, 0x34d4, 0xbfa0, 0x196a, 0x408f, 0x4313, 0x41ea, 0x1560, 0x46ea, 0x449c, 0x3a47, 0x40a6, 0x440a, 0xb267, 0x33b8, 0xb22c, 0x46eb, 0x1824, 0x3843, 0xbfca, 0xbfd0, 0x0716, 0x1031, 0xba00, 0x0daf, 0xba7e, 0xba72, 0xba11, 0x4375, 0xad64, 0xa1db, 0xb263, 0x42e7, 0x46f3, 0xa375, 0x4097, 0xa116, 0x4302, 0x19f5, 0x40ce, 0xbaf9, 0x269e, 0xbf0b, 0x407e, 0xb0f0, 0x45f4, 0x40fc, 0x1e2e, 0xba56, 0x4136, 0xba41, 0x4222, 0xbf1c, 0xb00c, 0x433f, 0xba7a, 0x41a5, 0xbfd0, 0x4260, 0xaf36, 0x42f3, 0x4376, 0xb0e8, 0x42e7, 0x0b6f, 0x4315, 0x1e04, 0xa4ef, 0x135b, 0xa6cd, 0x29d1, 0x40b6, 0x42d0, 0x4596, 0x4383, 0x466d, 0xbf7d, 0x46e4, 0x3014, 0xba02, 0x2ad7, 0xb2fe, 0x3de9, 0x1aa7, 0x2d8c, 0x150c, 0x4233, 0x40a0, 0xba40, 0x3a7a, 0x4361, 0x4447, 0xae8e, 0x4268, 0x4391, 0x41c8, 0x29dc, 0x4174, 0xb23c, 0x1bfc, 0x2c34, 0xbfc0, 0x194b, 0xbf9c, 0x43a6, 0xba78, 0x1b4b, 0x43ce, 0xbad7, 0xbfad, 0x067f, 0x4117, 0x4298, 0x4380, 0xb206, 0xba22, 0x4553, 0x3d55, 0x42f4, 0x1c7d, 0x419e, 0x206f, 0x1af1, 0xbff0, 0x3531, 0x1d92, 0x4150, 0x3fb8, 0x1e5f, 0x4250, 0x1fa1, 0x4202, 0xb0a1, 0xbf11, 0x45d9, 0x4676, 0x40fa, 0x421e, 0x1f6a, 0x0bf8, 0x29cc, 0xbf9d, 0x412e, 0xbf60, 0x3489, 0x4007, 0xba3b, 0x1836, 0x297c, 0x4284, 0x42e1, 0x4409, 0x429f, 0xba1a, 0xbac9, 0xb2b9, 0x3794, 0xa128, 0xa5c4, 0x42fb, 0x4593, 0x4184, 0xb0de, 0x0384, 0xbf3a, 0x3416, 0x3ea6, 0x411c, 0x42cd, 0xbfc0, 0xbaf3, 0xaa2e, 0x4317, 0xb2b2, 0x42d2, 0xb025, 0xb2d1, 0x091b, 0x423a, 0xb094, 0x468d, 0x4426, 0x31f9, 0xbf2e, 0x1e6b, 0x4388, 0x1c68, 0xba48, 0x3089, 0x4607, 0xba01, 0x416d, 0x1d85, 0x4424, 0x40d4, 0x41fa, 0xb28e, 0x1d5f, 0x4177, 0xb0d1, 0x1c5e, 0x4203, 0x0cf5, 0xb24b, 0x1cde, 0x1d65, 0x4281, 0xbfa6, 0xba36, 0x42a4, 0x4243, 0x44fa, 0x43b6, 0xa162, 0x41be, 0xb05d, 0x001b, 0x4283, 0xb2fd, 0x0395, 0xb28d, 0x40fb, 0xba1f, 0x43b1, 0x41af, 0x4157, 0xb245, 0x42f4, 0xb06b, 0xb27c, 0x1d2f, 0x45ee, 0x4435, 0xbfe2, 0x190a, 0x43a7, 0x4565, 0x41c3, 0xba46, 0x42d3, 0xb014, 0x23e7, 0x43a1, 0x436d, 0x4386, 0x1b31, 0x44e9, 0x427e, 0x1b44, 0xba66, 0xb02b, 0x404f, 0x42df, 0x438c, 0xba6f, 0x4083, 0xb02c, 0xba59, 0xaf47, 0x456d, 0xbfbd, 0xb2bd, 0x1e66, 0xbaee, 0x41af, 0xb05e, 0xbf70, 0x41eb, 0x4099, 0x42f1, 0x41d3, 0x40ed, 0xaefe, 0x4223, 0xa7ab, 0x02cb, 0x4393, 0xb258, 0xbfa3, 0x4311, 0x43f3, 0xb2f5, 0x43f4, 0xbf5e, 0x1854, 0x2a65, 0x1c8d, 0x4363, 0x4087, 0x40b2, 0x410a, 0xa11a, 0xb2ca, 0x1efa, 0x20d8, 0x419c, 0x41fd, 0x4283, 0x43f4, 0x4264, 0x420a, 0x469c, 0x1961, 0xb2fc, 0x429a, 0xb2b7, 0x4293, 0xa373, 0xbf98, 0xab6d, 0xb266, 0x01e3, 0x4176, 0xa2e0, 0x0265, 0x42ba, 0x40ce, 0x0695, 0x42a8, 0x3b85, 0x1ea1, 0x428b, 0x442a, 0x4452, 0xbf3c, 0x1bcb, 0xba76, 0x466b, 0x41bb, 0x44b8, 0x401d, 0xba78, 0x4020, 0x46a2, 0xb2ce, 0xb24f, 0x4202, 0xba02, 0x46d1, 0xbf3b, 0x4329, 0x0e1d, 0x40f7, 0x0daa, 0x40b6, 0xb2f0, 0xb208, 0x45a8, 0xbf0a, 0x43d7, 0xb238, 0x43e5, 0x43f3, 0x1dee, 0x3982, 0xbaf5, 0xbf70, 0x022f, 0x42d8, 0x4270, 0xae68, 0x1b50, 0xb2fa, 0x43ce, 0xa22b, 0x4103, 0xb0fe, 0xbf0a, 0xba03, 0xba58, 0xb21c, 0x4242, 0x428b, 0xb21b, 0x46fc, 0xbad7, 0x44f9, 0x409d, 0x1fa0, 0x422f, 0x4045, 0x0cb9, 0xbf37, 0x4168, 0x42e2, 0x1d17, 0x4146, 0x404b, 0x4397, 0x4023, 0x4218, 0x1be7, 0xb20c, 0xbfd0, 0x4166, 0x4677, 0x442e, 0x0bc1, 0x462e, 0x4103, 0x1b0b, 0xb0b5, 0xbf39, 0x3c04, 0xb2a6, 0x4132, 0x031d, 0xbac8, 0xb0d9, 0x436b, 0xb208, 0x4127, 0xbad3, 0x0705, 0x116f, 0xb23b, 0xba75, 0xaccc, 0xba6c, 0x19b5, 0x42c1, 0xb296, 0xbf98, 0x28ab, 0x1165, 0x4195, 0xb06b, 0x4356, 0xbf21, 0x2e2c, 0xba6e, 0xbf70, 0x1b50, 0x1d56, 0x4299, 0x41d0, 0xb2b1, 0x4097, 0x436b, 0x442b, 0xba7f, 0x409b, 0x2918, 0x40a4, 0x2155, 0x444c, 0x3bce, 0x4417, 0x1d3d, 0xb0b1, 0x4414, 0xb28b, 0xbf9e, 0x26d9, 0xba2e, 0x1b11, 0xbf60, 0x40a3, 0xb244, 0xb2b3, 0x4357, 0x40fd, 0x4253, 0x1b39, 0x408f, 0x43e5, 0x2a5f, 0x467b, 0x0fc0, 0x43fa, 0x1ad5, 0x0dca, 0x414e, 0x4401, 0x1cb1, 0xb229, 0x0cdd, 0x19a8, 0x29c7, 0xb2d4, 0x45b6, 0xbf6a, 0x085b, 0x0aa6, 0x195f, 0x4178, 0x0eae, 0x42af, 0xbfa2, 0x287d, 0x40e7, 0x45cb, 0x4233, 0xa5e9, 0x4005, 0xba4a, 0x42c7, 0xbf99, 0xba16, 0x402b, 0xb0a5, 0xb286, 0x40cb, 0x436b, 0x4326, 0x42da, 0x1762, 0x41f8, 0xb2c4, 0x4172, 0x3324, 0x1be2, 0xb210, 0x0a29, 0x2800, 0x225c, 0x02b0, 0xa200, 0x4125, 0x43c4, 0x4055, 0xb22c, 0x2b85, 0x42bc, 0x3465, 0xa4e5, 0xbfba, 0x4268, 0x33b2, 0x1ef1, 0x1d70, 0x4314, 0x4335, 0x4112, 0x2e63, 0x1fc5, 0xb091, 0x0ce5, 0x42a8, 0x17b0, 0x407a, 0xbf5d, 0x407a, 0x3b90, 0x0908, 0xb282, 0x1327, 0xbf00, 0x4376, 0x4278, 0x4075, 0x1de1, 0x4209, 0xb2c6, 0x42fd, 0x0018, 0x1d05, 0x46a3, 0x4290, 0xba7b, 0x0fc7, 0x1652, 0xbac1, 0x4309, 0x41b7, 0x36d6, 0xbf34, 0x43d6, 0x449c, 0xb2a3, 0xb269, 0x41d7, 0x24c0, 0xba4e, 0x40a0, 0xb005, 0x4454, 0x1542, 0x4311, 0x40ef, 0xb28f, 0x42c9, 0x2a56, 0x44f9, 0x407f, 0x454b, 0x2eff, 0x43e9, 0xba66, 0x4332, 0x4344, 0xb0a0, 0xbf31, 0x3bdd, 0x1e29, 0x1289, 0x3d48, 0x4077, 0xace3, 0x43ee, 0xb085, 0x1d32, 0x43b9, 0x411b, 0xb2ee, 0xb06f, 0xb26a, 0xb0ed, 0x0de4, 0x2e64, 0x36cd, 0x44d9, 0xbf26, 0x2866, 0xb271, 0x4132, 0x435c, 0xab9f, 0xa5f0, 0x40e8, 0x1dc4, 0x44e5, 0x409e, 0x15a3, 0x2a03, 0x4032, 0x43c2, 0xb29d, 0x3611, 0xbf02, 0x1a71, 0xb2bd, 0x44a5, 0x44dd, 0x2f63, 0x1176, 0x4017, 0x1c57, 0x42c5, 0xb257, 0xb01a, 0xb210, 0xba3e, 0x1033, 0xbaea, 0x2913, 0xb216, 0xbfa0, 0x414a, 0xbf01, 0x45dc, 0xba34, 0x43a4, 0x09ca, 0x44cc, 0x1fe2, 0xba21, 0x1c5d, 0x1a65, 0xba3f, 0xbfa4, 0x4344, 0xb24f, 0x1d4a, 0x4191, 0x4444, 0xba06, 0x413b, 0xbf58, 0x2509, 0xba2b, 0xb2a8, 0x1f68, 0x3a63, 0x467a, 0x05de, 0x4186, 0xba34, 0x42db, 0x1e2d, 0xbaef, 0xbfd0, 0x1c2d, 0x4176, 0x26bd, 0x4214, 0xa8fb, 0x4187, 0xba36, 0x42aa, 0x4073, 0x46d8, 0xb2f4, 0x40b5, 0x43fa, 0xbf87, 0x4225, 0x4336, 0x4376, 0x1c37, 0x4142, 0xb2e3, 0xbfe0, 0x3578, 0x41e3, 0x41cb, 0xb246, 0x402f, 0x2890, 0x422c, 0x40e9, 0x1892, 0x422d, 0xb209, 0xb2d6, 0x4045, 0xa2a6, 0x43bc, 0xbf37, 0x43ee, 0x4239, 0x326a, 0x3538, 0xb240, 0x43e8, 0xaae3, 0x3b5c, 0xa277, 0xb26f, 0xbf6d, 0xaf28, 0x4372, 0x1e45, 0x40f4, 0xb09f, 0x46e9, 0xb272, 0x4275, 0x2cb5, 0x42df, 0x03a3, 0x413f, 0xbac0, 0x40fa, 0xbad1, 0x0fba, 0xb2ca, 0x42dc, 0x4119, 0xb299, 0x41b9, 0x415f, 0xb240, 0xbf9c, 0xb224, 0x41de, 0x42f1, 0xbace, 0x34a5, 0xb073, 0xb2e6, 0x4073, 0x4185, 0xba32, 0x429b, 0x037d, 0xbfb0, 0x1855, 0xba71, 0x4199, 0x425a, 0x2faf, 0x410d, 0x41f6, 0x4349, 0x1eb6, 0x4333, 0x40dd, 0x40e9, 0xbf8b, 0x431c, 0x1eb9, 0x40c9, 0xb2ca, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x034bf2c4, 0xca960aa8, 0x9456a116, 0xb988fc86, 0x50cab48a, 0xa5759d59, 0x0dfb191d, 0x2b41a1c1, 0x1b2df3ef, 0x1463f435, 0x89ed0b15, 0x9d5b9a9c, 0x39b4b3b7, 0x23d2d921, 0x00000000, 0xc00001f0 }, FinalRegs = new uint[] { 0xffffffc7, 0xfffffffd, 0x000000fd, 0x280000a7, 0x000000a5, 0x00000000, 0x28000003, 0xffffffff, 0x00001dec, 0x00003494, 0x000000ac, 0x00001dec, 0x00005e9c, 0x00003660, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x404a, 0x1e38, 0x41ce, 0x41f6, 0xa61e, 0x43e1, 0xba33, 0x0e8f, 0x4366, 0x1fcf, 0x1f02, 0x417c, 0xb24c, 0xbf09, 0xadc6, 0x1cfb, 0x2652, 0x3b78, 0x43dd, 0x4058, 0x435c, 0x418d, 0x41e9, 0xba1e, 0x461a, 0x417c, 0x4070, 0x2e53, 0xbf46, 0xb213, 0x0a50, 0x43ce, 0x403c, 0xb0fb, 0xba32, 0x43c9, 0x408b, 0x4240, 0xba6c, 0x4345, 0x43c8, 0x422f, 0x1b87, 0xb288, 0x1b5c, 0xba0a, 0xbf8c, 0x4371, 0xba11, 0xbf45, 0xa04e, 0xb290, 0x430b, 0x43e5, 0x1ae7, 0x41e3, 0x4260, 0x4631, 0x3e11, 0x46e1, 0xbae9, 0x4207, 0x40ac, 0x404c, 0x2f8f, 0xbf2e, 0x43db, 0x0ef2, 0x41bd, 0x0426, 0xb2e5, 0x3c47, 0xb092, 0x4331, 0xae0c, 0xa2f5, 0x420d, 0x4245, 0x436a, 0x1e7b, 0xac86, 0xb221, 0xb0d1, 0x38f0, 0xbfe0, 0x420b, 0x428d, 0x46c3, 0xa967, 0xb236, 0xbac2, 0x41fc, 0x4026, 0xbf4a, 0x1a93, 0xa7fd, 0x43e8, 0x0d91, 0x42f0, 0xb013, 0x1cb9, 0xb2bd, 0x039a, 0x4307, 0xbfb2, 0x049f, 0x1b17, 0x0a41, 0x18b0, 0xb23e, 0x4126, 0x14d5, 0x3efd, 0x42cd, 0xa767, 0x431e, 0x41f3, 0xb29c, 0x06c1, 0x29be, 0x363e, 0xb26a, 0xa0f2, 0x1e42, 0xbad5, 0xbaca, 0x1ab0, 0xba62, 0xb29d, 0xba6a, 0xbf27, 0x4385, 0x4135, 0x4637, 0x429e, 0xb04e, 0xbf90, 0x432d, 0x4199, 0x40ff, 0x4009, 0x4229, 0xb250, 0x055e, 0x2803, 0xb27c, 0x40f2, 0x4060, 0x41de, 0xb087, 0x2667, 0x439e, 0x466d, 0x4060, 0x1c10, 0x4003, 0xa2e9, 0xb0fc, 0xbfca, 0x4230, 0x42db, 0x1a8c, 0xb2dc, 0x42d5, 0x40d2, 0xba72, 0xbad8, 0x2197, 0x4029, 0x434b, 0x1b03, 0x20bf, 0xb2d0, 0x45e6, 0x4248, 0xac8e, 0x45d8, 0xb23f, 0x431c, 0x2e97, 0x4041, 0x413f, 0xbf92, 0x42b5, 0x4351, 0x46c5, 0x4098, 0x40d4, 0xb2cd, 0x43af, 0x41a1, 0xb035, 0x3e50, 0xbf45, 0x419c, 0x3771, 0xb270, 0x0cda, 0x1f3d, 0xb2a1, 0x18d4, 0x3595, 0xa300, 0x2257, 0x4230, 0xb267, 0xbf63, 0x22b5, 0x334c, 0x4008, 0xb2fb, 0xba14, 0x435b, 0x4253, 0x2bbd, 0x4375, 0x114d, 0xa458, 0x2764, 0x4073, 0x437d, 0x32d1, 0xb034, 0xba67, 0x40b2, 0xa45b, 0xa541, 0x417e, 0xbf93, 0x449d, 0x19af, 0xbada, 0x1c8f, 0xb288, 0x44b9, 0x35bc, 0x407f, 0x4389, 0x099d, 0x1daf, 0x433e, 0x07cc, 0x31b7, 0xb0e3, 0xa4fb, 0x4095, 0xb207, 0x40ec, 0xb2e8, 0x0ec7, 0x0c90, 0xb218, 0xbf6d, 0xa111, 0x1cb3, 0x2234, 0x1fa6, 0x4242, 0x2c82, 0x41d4, 0x2053, 0xbac3, 0x0cd9, 0x2ead, 0x425d, 0xb0c0, 0x44b0, 0xb2b5, 0xb01f, 0x1ce8, 0x46b1, 0x4156, 0xb2e2, 0x41b7, 0xae46, 0x1ee3, 0x444a, 0x415f, 0x4375, 0x1251, 0xbaee, 0xbf13, 0xb22f, 0x401b, 0x19ba, 0x4290, 0xa67b, 0xbac7, 0x1b71, 0x4156, 0x241a, 0x4577, 0x3cbe, 0x1b08, 0x40b2, 0x2f7d, 0x1a27, 0x4335, 0xbaf1, 0x2f52, 0x1850, 0x2414, 0x4129, 0xb24d, 0xbfbb, 0xb283, 0x07e8, 0x399e, 0xbac4, 0xb0af, 0x374d, 0x1bc1, 0x0f91, 0xb09d, 0x22a6, 0x419f, 0x3131, 0x1bdc, 0x42f7, 0x43d7, 0x29ac, 0xbf4c, 0xa2c6, 0xac1c, 0x1d92, 0x4127, 0xaa2a, 0xba5b, 0x4159, 0x41b8, 0x4320, 0x1a6f, 0x249a, 0x3fec, 0xbf5c, 0x41c0, 0x42ed, 0x1a95, 0x1ce2, 0xa1a3, 0x41e9, 0x4014, 0x05f4, 0xb03d, 0x4177, 0x3a2b, 0x4230, 0xabc9, 0x115d, 0x4308, 0x41c6, 0xb273, 0xb2cf, 0xa77c, 0xac87, 0x417e, 0x300c, 0x28a6, 0x42a9, 0xbf39, 0xaeb8, 0x1d9b, 0x1aed, 0x4346, 0xbaf9, 0x462a, 0x4199, 0x0418, 0x45cc, 0x4371, 0x04e7, 0x43f4, 0xb204, 0x4398, 0x438c, 0x43eb, 0xb201, 0x4155, 0x0a6e, 0x2d49, 0x42a3, 0xbac0, 0x43f4, 0x4245, 0xbfa3, 0x1edc, 0x446c, 0x3da5, 0x42e3, 0x42ae, 0xbf2c, 0xb25a, 0x40e7, 0xbfae, 0xb2ec, 0xba4a, 0x4301, 0x43c0, 0x0f46, 0x4182, 0x4233, 0x4084, 0xa36e, 0xbf9f, 0x410e, 0x42a0, 0x1879, 0x43d8, 0x44ea, 0xba40, 0xbfc5, 0x43fe, 0x42b9, 0x42b1, 0x4172, 0x4151, 0x42d4, 0xb25a, 0xbff0, 0xb026, 0x4116, 0x1ac9, 0x435b, 0xbfa2, 0x4482, 0x188e, 0x46f0, 0x26c3, 0x4103, 0x4491, 0xbf80, 0x2b1b, 0x41a2, 0x4022, 0x4132, 0x4235, 0x4214, 0x43d6, 0x4025, 0xa55b, 0x4040, 0x2d18, 0xac95, 0x4316, 0xbadd, 0x41c9, 0xbf9c, 0x2e9b, 0x34ac, 0x40d6, 0x1f75, 0xbfc0, 0x43a8, 0x3e7b, 0x20b6, 0xb0d7, 0xb22f, 0x41d0, 0x1c3a, 0x23a0, 0x1a3a, 0x426e, 0x4178, 0x45c8, 0x426a, 0xbf18, 0x42d7, 0x42e9, 0x40c1, 0x40da, 0xb273, 0xba11, 0xb209, 0xb07b, 0x18af, 0x40bf, 0x4253, 0x0e6f, 0xbf9d, 0x42a7, 0xa767, 0xba35, 0xad49, 0x1d61, 0x1dca, 0x436a, 0x2f36, 0x40e9, 0x1cf2, 0x4061, 0xba3e, 0x099e, 0xac24, 0x4559, 0x467b, 0x1a05, 0x08ce, 0xb295, 0xbfb3, 0x4057, 0x4384, 0x1f5a, 0x13e2, 0x4561, 0x442b, 0xbf02, 0x1563, 0xba37, 0xb230, 0xb0be, 0x41a4, 0x4235, 0xbf3c, 0x4008, 0xb04a, 0x4297, 0xb20d, 0x2cca, 0xb0d8, 0x4386, 0x4054, 0x402c, 0xbf60, 0xbf82, 0x41d9, 0x18bf, 0x1a14, 0x1236, 0x2e49, 0x429d, 0xa669, 0x4120, 0xbfdb, 0x3c29, 0x04af, 0x3169, 0x425f, 0xa558, 0xbf0d, 0xaf86, 0xb0ef, 0x0707, 0x41ba, 0xb263, 0xbfb9, 0x42eb, 0x3db5, 0x3ac5, 0xb214, 0x40ab, 0x42c6, 0x1d21, 0x439c, 0x2200, 0xb0fb, 0x4695, 0x41ef, 0x4178, 0x42ea, 0x43ec, 0xb297, 0x419d, 0x4091, 0x405b, 0x43a6, 0xb2de, 0x21ab, 0xba6d, 0x4002, 0xabb4, 0x400f, 0x40a2, 0xbf2f, 0xb243, 0xa1a2, 0x460c, 0xb2e0, 0xbf9f, 0x1ad6, 0x41d4, 0xb0a0, 0x1b7f, 0x43d7, 0xb228, 0xb2ca, 0x2655, 0x3efc, 0xb249, 0x27dc, 0x428c, 0x40f9, 0x4354, 0x429d, 0xb266, 0x4595, 0xb0cf, 0xb2c9, 0x401e, 0x4264, 0x42b4, 0x0c35, 0x0e87, 0x407f, 0x4336, 0x403d, 0x1df2, 0x41c1, 0xbf39, 0x43bd, 0x4174, 0x4329, 0x44a2, 0x1932, 0xbf4b, 0xb282, 0x1ded, 0xbacf, 0x404a, 0xb246, 0x4630, 0x1ac0, 0x43d7, 0x40a2, 0x4270, 0xb2e1, 0x0cf0, 0x1ecc, 0xbac3, 0x408b, 0x4358, 0x1ca6, 0xbf6c, 0x416b, 0x2d6e, 0xb017, 0x4385, 0xa144, 0xb2b5, 0x41fd, 0xbf28, 0x1aef, 0xb239, 0xb2ba, 0x430e, 0xb0c0, 0x42cb, 0xbfdd, 0xb020, 0x44d9, 0x414d, 0x402b, 0xb033, 0xba4c, 0x1ace, 0x401b, 0xb200, 0x40ae, 0xa013, 0x1fb4, 0x42a2, 0xba53, 0x4006, 0xbfc5, 0xba13, 0xba0a, 0xb21c, 0x406c, 0x0f32, 0x0d61, 0xb28a, 0xadd9, 0x2943, 0x42d8, 0xb212, 0x1e8f, 0xb273, 0x426f, 0xb08b, 0xa7ca, 0xbac8, 0x43b2, 0xa2d0, 0x0940, 0xbaee, 0x42b3, 0x42fe, 0x0239, 0xbf6c, 0x4082, 0xb272, 0x1219, 0x4101, 0x43a8, 0x403e, 0xb251, 0xb251, 0xb035, 0x1abe, 0x1853, 0x4373, 0x4086, 0x4330, 0xb22d, 0x4491, 0x42cb, 0x442d, 0x073a, 0x1abd, 0xb23c, 0xbf6e, 0x43b5, 0x1cd5, 0x1cac, 0x1a26, 0x42c9, 0x4312, 0x41f6, 0x034a, 0xbf1b, 0xba22, 0x43c1, 0x408b, 0x3efd, 0xbf8e, 0x45b1, 0x1ff9, 0xb0b4, 0x42a4, 0x40ff, 0xbae4, 0xa80c, 0xac90, 0x42fd, 0x41e9, 0x434a, 0xba62, 0x4263, 0xafea, 0x42a8, 0x1ebd, 0x0ace, 0x41e2, 0x448b, 0xb231, 0x4186, 0x40cf, 0xbf93, 0xba41, 0x4355, 0xbaca, 0xba6e, 0xb2c3, 0xa584, 0x421d, 0x42c3, 0x0b33, 0xb24a, 0x459b, 0x406a, 0x44c3, 0x1c47, 0x1dbe, 0x1b02, 0xa03c, 0x43af, 0xba58, 0x41b4, 0x43f2, 0x1e2f, 0xbfb8, 0x2fcb, 0x43d3, 0x425e, 0x2f90, 0xb2eb, 0xbfaf, 0x4446, 0x42d0, 0xb0e1, 0x3d22, 0x40fe, 0x425a, 0x439f, 0x4190, 0x417d, 0xb23f, 0x0e6c, 0xbf5d, 0xbfc0, 0xbfb0, 0x4136, 0x3dbb, 0x4548, 0x1ed0, 0x2e7c, 0x427e, 0x420b, 0x1a4a, 0x1b24, 0x405e, 0x42c8, 0x1e86, 0xb2b6, 0x4369, 0x02c5, 0x41c0, 0x4586, 0x417c, 0xb23c, 0x13d1, 0x2eb6, 0xbf9a, 0x4406, 0xbae8, 0x0fc1, 0x4302, 0x37a4, 0x3676, 0x16fc, 0x4292, 0xbf8b, 0x4022, 0x4122, 0xb2d8, 0x3706, 0x4258, 0xbf91, 0x4310, 0x4631, 0x408d, 0x19e5, 0xb01d, 0x4087, 0xa7ab, 0x0ffe, 0xba00, 0x37e6, 0x41ec, 0x407c, 0x406d, 0x1896, 0x43c4, 0x1666, 0x4023, 0x26e4, 0xbf7d, 0xad97, 0x43cd, 0x41cd, 0x215e, 0x41f7, 0x40e6, 0xb270, 0x414c, 0x18d9, 0x3998, 0xb078, 0xb2f5, 0x42bd, 0x0eef, 0x42c4, 0x0f53, 0xaf6b, 0xb29b, 0x11f7, 0x1f08, 0x4030, 0x4384, 0x3b0a, 0x3012, 0x4004, 0x43f4, 0x4636, 0x403f, 0x4484, 0xbfd4, 0xba40, 0xb049, 0x4608, 0x40c4, 0xbf92, 0x1898, 0x36dd, 0x403d, 0x1b5e, 0x4136, 0xbac7, 0x0f85, 0x1f13, 0x4337, 0xbae7, 0x416d, 0xa007, 0xbfcf, 0xaa8b, 0x2329, 0xb28f, 0x4380, 0x431c, 0xa918, 0xadc2, 0xad91, 0x250b, 0x4127, 0x11a2, 0x4395, 0xba07, 0x2356, 0xb21a, 0x4367, 0xb25b, 0x32db, 0x435e, 0x0285, 0xa8d6, 0x4164, 0x045d, 0x3a1c, 0x4191, 0xb0f5, 0x192a, 0x43c2, 0xb278, 0xbf1f, 0xb2e9, 0x456c, 0x4308, 0xb2f4, 0x4261, 0xbf2c, 0xbfe0, 0x4592, 0x431b, 0x402c, 0x1e54, 0x427f, 0x1f79, 0x4052, 0xa524, 0x4132, 0x2bc5, 0x0dfc, 0x1b3d, 0x4053, 0x42e1, 0xbaea, 0x3163, 0x42bc, 0xbfb1, 0x3240, 0x34fb, 0x42b0, 0xb034, 0xadf0, 0x4069, 0x413f, 0xbad6, 0x4335, 0xba39, 0x428e, 0x3b25, 0x41f9, 0x1816, 0x2ec7, 0x1e92, 0x4307, 0xb2d1, 0x405c, 0x41a0, 0x0efc, 0xa329, 0xbfc3, 0x435e, 0x438f, 0x4352, 0xb0c0, 0xb259, 0xae76, 0x40bf, 0x18f7, 0xbae0, 0xb2e7, 0xa6fe, 0x46c3, 0xbf69, 0x4613, 0x406b, 0xb2eb, 0x43ce, 0xa9e3, 0x10eb, 0x46ec, 0xba10, 0x427a, 0x412b, 0xbf9e, 0x41c1, 0x4353, 0xb2c7, 0x42f8, 0xafae, 0x1ab7, 0xb062, 0x407d, 0x4330, 0xb2af, 0x3801, 0xbfab, 0x43c8, 0x423d, 0x4159, 0x41e0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x7059abe5, 0xb415f906, 0x8490eae9, 0x0ff183c6, 0xf39f6a2c, 0xb4413795, 0xee05d88d, 0x50185d92, 0xf621a094, 0x8902e42e, 0x9ecca830, 0x152bbe7f, 0x038e4ec7, 0xeb5d3b32, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0x89283ba8, 0x000001bb, 0xfffffff1, 0x00000000, 0x0000000f, 0x0000240e, 0x00001ba8, 0x0000240e, 0x00000000, 0x000013e8, 0x94ee481b, 0x00000000, 0xffffffe8, 0x00000170, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xba51, 0x414a, 0x4030, 0x4380, 0x4328, 0xb07b, 0x425a, 0xb214, 0x42fd, 0x2b6a, 0xaf72, 0x0da2, 0xb260, 0xb0a2, 0x4017, 0x4242, 0x38f4, 0x40d8, 0xb2b5, 0xbf43, 0x40f1, 0xb29b, 0x4240, 0x412b, 0x1c5d, 0xb27f, 0x2483, 0x4322, 0xba3d, 0x380c, 0xba4e, 0x1a5d, 0x4387, 0xb21d, 0xb2b6, 0xbf29, 0x2514, 0x43b9, 0x424b, 0x3cb5, 0xbf90, 0x40b2, 0x2a94, 0x42ce, 0x42df, 0xb23a, 0x12c2, 0xb232, 0x3230, 0x194f, 0xbf9e, 0xac89, 0x40d9, 0x4023, 0x424a, 0x2736, 0x0a55, 0x401a, 0x1835, 0x402e, 0xb20b, 0xbf6f, 0x2155, 0xbaf4, 0x39c6, 0x0161, 0x4217, 0x33de, 0x379d, 0x1dbc, 0x0b38, 0xb2ff, 0x23eb, 0x4072, 0xbf8a, 0x4201, 0x4240, 0x41b1, 0x1916, 0x40c4, 0xb26a, 0x4345, 0x0e81, 0xbf17, 0x4359, 0x4396, 0x4185, 0xa8f1, 0x43ce, 0x0472, 0x424d, 0x438d, 0x4018, 0x3684, 0xb244, 0x4349, 0x40a9, 0x400c, 0x41ac, 0x42ff, 0xaea0, 0xa7e6, 0xb2ec, 0x31eb, 0xb211, 0x42c8, 0x2649, 0x4064, 0xbad9, 0xbf05, 0x4169, 0x4664, 0x4679, 0x4576, 0x415b, 0xb200, 0x1f31, 0x2abf, 0xb0f4, 0x46d5, 0x4387, 0x40fa, 0x42dd, 0x18c0, 0xb021, 0x3d89, 0x403f, 0x4257, 0x1f9a, 0x43ed, 0x3c5c, 0x1c96, 0x41d4, 0xb201, 0x2ff5, 0x43ab, 0x0f0f, 0xbf69, 0x4637, 0x1895, 0x1d62, 0xa75b, 0xb0c3, 0x45d2, 0x432b, 0xb217, 0x18c7, 0x41dd, 0xa705, 0x4219, 0x433d, 0xa9c2, 0xb29d, 0x40f1, 0xbaf1, 0x420c, 0xb256, 0x4112, 0xbfbd, 0x1ab8, 0xba34, 0xbfe0, 0xbadf, 0xa4ed, 0x4323, 0x41a6, 0x40d6, 0x40c3, 0x4361, 0x0974, 0x1990, 0x42dc, 0x4377, 0xb2d8, 0x44d4, 0xba4e, 0x46e9, 0x188c, 0xbfd5, 0x429b, 0x4374, 0x4551, 0xb259, 0x418c, 0x4588, 0x43fe, 0xbfd0, 0x4175, 0x4227, 0x44b8, 0xbfe0, 0x2e9a, 0x406a, 0x2535, 0x4168, 0x0ad7, 0x1d42, 0x4264, 0xb086, 0xbada, 0xb00f, 0xb26f, 0x1b8d, 0x1c5c, 0x44c1, 0x3f77, 0x0f15, 0xb24c, 0xbf0e, 0x42d1, 0x44eb, 0x4253, 0x4233, 0x4203, 0xbfcd, 0x427a, 0x18a6, 0x4011, 0x29a1, 0x40cc, 0x448c, 0x1d4f, 0x0d2b, 0xb2dc, 0x4026, 0x12ff, 0x434c, 0x06a7, 0x415f, 0x41e4, 0x1842, 0xbf3a, 0xba5a, 0x4082, 0x43d2, 0x42a7, 0xa3c3, 0x41f5, 0x4341, 0x35dd, 0x425e, 0xb2c6, 0x4396, 0x420d, 0xb298, 0xbfd0, 0xa02a, 0x060e, 0x009f, 0x0988, 0x22b2, 0x4168, 0xbfbd, 0x437e, 0x38d0, 0x08f1, 0x41eb, 0x41c6, 0x21e9, 0x40d5, 0x1495, 0x436d, 0x41a6, 0x43f7, 0x21f7, 0xb04a, 0x4349, 0xb2d4, 0x0baa, 0xb034, 0x4087, 0x41f3, 0x40ff, 0xbf48, 0x41e7, 0xa4cb, 0x3b1b, 0xa68b, 0x18b1, 0xbaeb, 0xb045, 0xb231, 0x4194, 0x4425, 0x40f8, 0x1fda, 0xa62a, 0x4302, 0xb2ee, 0x40f9, 0x1bb8, 0x4365, 0x42ff, 0x434d, 0xba42, 0xb2ad, 0xbfb3, 0xba16, 0xb2bd, 0x41df, 0x42c9, 0x4367, 0x0235, 0x34d6, 0x4176, 0x40cb, 0x4295, 0x42b9, 0x0de1, 0x41c8, 0x41be, 0xb2ea, 0x1312, 0x4230, 0x1907, 0xb2f6, 0x4215, 0xba0a, 0xba32, 0x42a1, 0xbf07, 0x397f, 0xbfc0, 0x4206, 0x124e, 0x40cb, 0x440d, 0xb0a0, 0xb27c, 0x3960, 0x411d, 0x4217, 0x463a, 0x3a91, 0x3950, 0x438c, 0x1c16, 0xbfcb, 0xb207, 0x435b, 0x40ee, 0xbfb0, 0x2ebf, 0x3df7, 0xb21e, 0xba74, 0x1b05, 0x46fb, 0x428d, 0x43c1, 0x4196, 0x4379, 0x4239, 0x19e7, 0x411d, 0xbfd2, 0x3b6c, 0x42a7, 0x1ab9, 0xbafe, 0x40f8, 0x0e6a, 0x4422, 0xba1f, 0xbfa5, 0xba17, 0x43cb, 0x3d92, 0x43f7, 0x18a0, 0xbf21, 0x2196, 0x1fb2, 0x2491, 0x402b, 0xbf6a, 0x244e, 0x43b9, 0x03b7, 0x4228, 0x40e1, 0x42e3, 0x4353, 0x00d9, 0x41a9, 0xbac9, 0xbf74, 0x1b6a, 0x4381, 0x412b, 0x43d6, 0xbf26, 0x4194, 0x19f9, 0xb28a, 0x4000, 0xba61, 0x41a6, 0x1f6d, 0x19da, 0xb211, 0xbfa5, 0x1bf6, 0x4070, 0x1f03, 0xb275, 0x413d, 0x40f0, 0x45c8, 0x4036, 0x1844, 0x400a, 0x408b, 0x1cf3, 0xb2f6, 0x4302, 0xbf23, 0x16bb, 0x40c6, 0xba47, 0xba62, 0x42b8, 0xb24d, 0x4088, 0xba35, 0x43f5, 0xbade, 0x434f, 0x1ae1, 0x1df1, 0xb2da, 0xbfcc, 0x42ab, 0x1ece, 0x4252, 0x1e4c, 0x3aed, 0x4175, 0xb2c9, 0x422d, 0x4306, 0x3dd4, 0x4247, 0x413f, 0x3a6f, 0xb0c0, 0xb052, 0x41eb, 0xb222, 0xb05d, 0xbf16, 0x4332, 0x4142, 0xb29f, 0xb26d, 0x43b0, 0x2c2f, 0xa515, 0x43e4, 0x3d7a, 0xbf80, 0x2e76, 0x45e0, 0x1b4e, 0xbfd2, 0x45e2, 0x4033, 0x4305, 0xbf47, 0x1ad6, 0xbff0, 0x150c, 0x3e9a, 0x1ea8, 0x4120, 0x19ce, 0xbf79, 0x43ba, 0xbfe0, 0x3779, 0x42db, 0x39fe, 0xac2d, 0x1c17, 0x43b7, 0x4338, 0x29d6, 0x4069, 0xb0ba, 0xbf90, 0xa22d, 0x423c, 0x1df9, 0x40ba, 0xb24a, 0xbf87, 0x1999, 0xb277, 0x465f, 0x41da, 0xba31, 0xb2b8, 0xba07, 0x1950, 0x2ad8, 0x03a6, 0x4598, 0x1e2e, 0x4291, 0x439e, 0x43b4, 0xb0dd, 0x407d, 0x1859, 0xbf36, 0x196c, 0xb2f4, 0x19c2, 0x426f, 0xb2ed, 0xba27, 0xb209, 0x44e1, 0x2f28, 0x412a, 0x1f41, 0x26d7, 0xba26, 0x2bcb, 0x43ce, 0xbfa0, 0x1ee7, 0x307b, 0x1f27, 0x1438, 0x3bae, 0xb20a, 0x2af0, 0x1747, 0xbf03, 0xb272, 0x42c1, 0x445d, 0x1032, 0x43ac, 0x4048, 0x40c7, 0x43eb, 0x20f9, 0x4167, 0x4257, 0x419d, 0xb254, 0x020a, 0x0824, 0xbf60, 0x1bc9, 0xaa64, 0x4306, 0x41a8, 0x4368, 0x2461, 0x4412, 0x0a06, 0x426b, 0x380f, 0x429d, 0xb2b6, 0xb06a, 0xbf2c, 0x44a2, 0x4216, 0x42c3, 0xb24d, 0x455a, 0x417b, 0x4237, 0x1a76, 0xba20, 0xb08b, 0xba73, 0x178c, 0x144c, 0x1477, 0x3fff, 0xbac6, 0x40c6, 0x416b, 0xb221, 0x2221, 0x14a6, 0x3de1, 0xbf8f, 0x426c, 0xb28f, 0x4094, 0xb21a, 0x371b, 0x4654, 0x46ec, 0xba7f, 0x086c, 0x159f, 0xbaf8, 0x4094, 0xb2d1, 0x412e, 0x4087, 0xb246, 0x4190, 0x4318, 0xb2f2, 0x0736, 0xb269, 0xbf60, 0x4569, 0x466c, 0x4271, 0x4591, 0xbf91, 0x1b68, 0x4287, 0xba47, 0x4338, 0x42c7, 0xb27a, 0x4046, 0x4280, 0xbf77, 0x42e7, 0x456a, 0x117c, 0x4216, 0xbf51, 0x443e, 0x4362, 0x4036, 0x4336, 0x1e6b, 0xb25e, 0x0281, 0x4258, 0x456f, 0x4247, 0xb253, 0x358c, 0x4225, 0xb209, 0x4292, 0x2ac6, 0xb2c5, 0x2643, 0xad8c, 0x432d, 0xba18, 0x4155, 0x2239, 0x2027, 0xb27b, 0x4161, 0xb224, 0x0e38, 0xbf6f, 0x316c, 0xba5f, 0xad65, 0x4059, 0x1cd7, 0x4174, 0x325d, 0x1c2f, 0x41f3, 0xbaeb, 0x4065, 0xbf18, 0x4107, 0xb04b, 0xb2e6, 0x4060, 0x4058, 0x43e0, 0x13fe, 0x3d85, 0x4240, 0xbf58, 0x41ac, 0xbacd, 0xb218, 0x422f, 0xb228, 0x04cc, 0x1ed0, 0x43c1, 0x414a, 0x21dd, 0x1de5, 0x40d8, 0x4174, 0x4607, 0x430e, 0x4303, 0x40fd, 0xbf80, 0xbf96, 0x0173, 0xb21f, 0x44bb, 0xba35, 0x1e73, 0x40cf, 0xb0a8, 0x412d, 0x43ee, 0x42de, 0xb258, 0x411f, 0x4139, 0xb2aa, 0xae46, 0x0246, 0x421c, 0x18d7, 0x4258, 0xba6e, 0xb09e, 0x4024, 0xbaec, 0xba26, 0x41aa, 0x1e8d, 0x2a8c, 0xbf2e, 0x43e9, 0x46a9, 0x42ef, 0x43a1, 0x409c, 0x4176, 0x4133, 0xba70, 0x4649, 0xb0c6, 0x41a3, 0x4285, 0xb2e9, 0x41d2, 0x3f71, 0x403e, 0x4322, 0x4627, 0xbfae, 0xaa92, 0x43c6, 0x42d2, 0xac63, 0x0a31, 0xb258, 0xbfc6, 0x417a, 0xbaf1, 0xba1f, 0x43b8, 0x41fb, 0x4253, 0x1b4b, 0x4419, 0xbf4e, 0x4012, 0xb245, 0x3e4e, 0x44f8, 0x368f, 0x4324, 0x416d, 0x409b, 0x4254, 0xbf80, 0x27fa, 0xa534, 0xbf90, 0x1ab2, 0xb2ec, 0x4335, 0x1eb9, 0xba45, 0x4333, 0xbfb9, 0x1c4d, 0x2c8a, 0x2e8a, 0x423a, 0xb0e3, 0xb21c, 0x422d, 0xbf8e, 0x2e30, 0x42a7, 0xb0bf, 0x43b5, 0x1af2, 0x43c7, 0x1aef, 0x466c, 0xbf13, 0x3802, 0xba0b, 0x4043, 0xb089, 0xba08, 0x4153, 0x1a77, 0xb2fd, 0xa20a, 0x43b8, 0x402e, 0x4031, 0x18ce, 0x400a, 0xbf60, 0x32d5, 0x2169, 0xba6d, 0x41f3, 0x1722, 0x4396, 0xb266, 0x4165, 0xb2ad, 0x402a, 0x40ca, 0xb254, 0xbf15, 0x1b9e, 0x32eb, 0x4148, 0xba7a, 0x3889, 0x1cf3, 0x4117, 0xbad5, 0x40aa, 0x41e2, 0xbf67, 0x30d2, 0x1428, 0x42ab, 0x3d03, 0xa881, 0xbfb0, 0xb2bd, 0x1759, 0x4550, 0x0672, 0x1807, 0x300d, 0x40ad, 0x2e4a, 0xb237, 0x290f, 0xb228, 0x433e, 0x4273, 0x0bf7, 0xb0ac, 0xbaf3, 0xb00c, 0x0408, 0x4009, 0xbf3d, 0x411e, 0x433e, 0x4079, 0x41fb, 0x4375, 0x43b5, 0x4365, 0xb02c, 0x4654, 0x44e5, 0x4271, 0xb216, 0x41c5, 0x42c0, 0x4630, 0x3d54, 0xbfde, 0xba79, 0x1dba, 0xb0b7, 0x402a, 0x4279, 0x4120, 0x1e83, 0x4383, 0x18e9, 0x462c, 0x4178, 0x40a4, 0x4285, 0xb058, 0x1e53, 0xbf27, 0xb025, 0x29c0, 0x40d3, 0xb2e7, 0xba77, 0xbae1, 0x2061, 0x425a, 0x42e6, 0xbf71, 0x4018, 0x4337, 0x4287, 0xbafd, 0x42eb, 0x418e, 0x4177, 0xb01f, 0x4274, 0x1e4b, 0xad9b, 0xb2d2, 0xbad4, 0x40aa, 0x4302, 0x18be, 0x4214, 0x427c, 0x41ed, 0x18e8, 0xbfb5, 0x41ee, 0x41c8, 0x43f5, 0x401a, 0xaacd, 0x402a, 0x0dae, 0xb05e, 0x40af, 0x4019, 0x1c2f, 0x4273, 0x28ca, 0x3e2e, 0x4636, 0xba00, 0x41cf, 0xbfcd, 0x4393, 0x4225, 0x42c6, 0x08dd, 0x4085, 0x41df, 0x43e0, 0x1ef3, 0x2fc5, 0x1893, 0x407c, 0x4263, 0xa557, 0xb0ad, 0xbfbd, 0x2c87, 0xb254, 0xba35, 0x404b, 0x1f02, 0x4128, 0xba23, 0x21ae, 0x04df, 0x4097, 0xb06f, 0x4630, 0xb2d9, 0x4076, 0x4118, 0x34d4, 0x402c, 0x4014, 0x4660, 0x1b57, 0x1ed9, 0x1d79, 0x0656, 0x1a34, 0x3ed4, 0xbfde, 0x1cbc, 0x4318, 0xba0c, 0x4485, 0x3cb7, 0x1be9, 0x411e, 0x0c28, 0x4320, 0x4048, 0xba28, 0xba50, 0xb0ce, 0x43d5, 0x409f, 0x454c, 0xa404, 0x41ce, 0xbf25, 0x402a, 0x4621, 0x4193, 0x41d2, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc53d8d03, 0xff04be91, 0xd7eb41e1, 0xf292368c, 0xc17640e7, 0x57b5fdc3, 0x3ca7f7e5, 0x438f9293, 0xbf87ea5c, 0x7ed4e28f, 0xf51a6864, 0x539c9c7c, 0x62d8cca2, 0xc0701359, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0xfffffcff, 0x000017f4, 0x00000000, 0x00000800, 0x000017f4, 0x00000003, 0xf2cf7fff, 0xffffe718, 0xc0464ce0, 0x0d53d3ba, 0xf51a6864, 0x000012ba, 0xf51a6b68, 0xdf4f4290, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x2af4, 0x4229, 0x413f, 0xb264, 0x4327, 0x4106, 0x402a, 0xb0a1, 0x45b0, 0xb2fa, 0x1ebf, 0x441e, 0x43f6, 0xbf8f, 0x4628, 0x3760, 0x4360, 0x42af, 0x4179, 0xbf1f, 0x41d3, 0x1991, 0x4181, 0xb293, 0x4235, 0x1f56, 0x4650, 0xaf8a, 0xadfe, 0xb20e, 0x1abd, 0xba65, 0x4210, 0xb2ea, 0xa27b, 0xbafe, 0xbf00, 0x41ca, 0x1a78, 0x1668, 0xb2a2, 0xb061, 0x400f, 0x43a0, 0xbf58, 0x41af, 0x413f, 0x183a, 0x1e39, 0xbf15, 0x41e3, 0x4322, 0x41ed, 0xb0d3, 0x37ae, 0x120e, 0xb229, 0x417e, 0xb2dd, 0x4181, 0x08cd, 0xb2e2, 0x42b3, 0x43f4, 0x0bb2, 0x40ca, 0x412b, 0xb066, 0xbad4, 0x1c95, 0x4128, 0xa2c0, 0xbfa1, 0x4165, 0x40a1, 0xb2fa, 0x4054, 0x439d, 0xb29b, 0xb219, 0xb0b8, 0xbae4, 0x10ce, 0x43c6, 0xba78, 0x4163, 0xb2b7, 0x4151, 0x416f, 0x4323, 0xb205, 0x4443, 0x421b, 0x4378, 0x439d, 0x4306, 0x11f7, 0x41ce, 0xb270, 0x4040, 0x3828, 0xbf4a, 0xa7f5, 0x4036, 0xbfb0, 0x46fb, 0x4638, 0x41ce, 0x4018, 0xb2f7, 0x3777, 0x4297, 0x4372, 0x1b90, 0x215f, 0x4096, 0x436b, 0x0217, 0xb26a, 0xb200, 0xa480, 0x4406, 0x2fbe, 0x0701, 0x426c, 0xb2f6, 0x41b3, 0x431f, 0xb2aa, 0x406a, 0xbf3a, 0x427b, 0x064a, 0x1ad3, 0xb265, 0xac12, 0xb0a2, 0x4100, 0xbf7e, 0x1d85, 0x43a1, 0x4009, 0x1338, 0x4315, 0xba41, 0x3f3c, 0x4211, 0x40a4, 0x055d, 0x455a, 0xba61, 0x439e, 0xbf80, 0x43ab, 0x45ed, 0x3ab9, 0xb2e2, 0x3618, 0xbf76, 0x2654, 0x4555, 0xa5a8, 0x4349, 0xaa9c, 0x41c5, 0x4356, 0x3c41, 0x40c2, 0x085c, 0x40a7, 0xbfba, 0xbae7, 0x42b5, 0x42a0, 0x21d7, 0x420f, 0x1a68, 0x41f7, 0xbfa0, 0x43ab, 0xbf9a, 0x41b4, 0x397b, 0xb23b, 0x1b68, 0x4260, 0xb256, 0x432a, 0xb2b4, 0x44c8, 0xb2cc, 0x409c, 0xbff0, 0x416e, 0xbf13, 0x4312, 0xb0bb, 0x342f, 0x43c9, 0x4107, 0x1a19, 0x223f, 0xabb8, 0x43f4, 0xbf83, 0x2d30, 0x4155, 0x424b, 0xb2b6, 0x4296, 0xb21c, 0x3cca, 0x41fd, 0xba12, 0x424c, 0xbf2e, 0xba38, 0xbacd, 0xa7e7, 0x00be, 0x43f9, 0x1a9f, 0x2f15, 0x1eb0, 0x42c1, 0x4576, 0x418c, 0x4203, 0x1428, 0x1b1a, 0xbaff, 0x430a, 0x41d1, 0x42ee, 0x42d1, 0xb0dc, 0x437a, 0xbf6e, 0x4009, 0xb2f8, 0x43e9, 0x4188, 0x3d63, 0x1f9c, 0xa47d, 0xba6a, 0xbadd, 0x45d4, 0xbfb1, 0xb2bf, 0x1fdf, 0x41c9, 0xbf90, 0xba05, 0xb2e1, 0x4012, 0x4353, 0x438d, 0x462c, 0xb019, 0x41c6, 0x4092, 0xb2dd, 0xb21e, 0xba72, 0x2b0f, 0x401e, 0xba33, 0x0636, 0xb213, 0x3216, 0x41a6, 0xbf99, 0x3934, 0x4290, 0xbff0, 0xba7f, 0x0aa5, 0x1b9d, 0x1b5e, 0xba5d, 0xbfd8, 0x39cd, 0x41ea, 0x427e, 0x42ea, 0xb292, 0xb2c5, 0x422d, 0x413d, 0xba0d, 0xafa1, 0x324a, 0xbf26, 0xb243, 0x1cb1, 0x4017, 0x4430, 0x42ce, 0xbf51, 0xb23e, 0xba00, 0xba27, 0x18b7, 0x4084, 0xb0d7, 0xb229, 0x423c, 0x429e, 0xbad8, 0xba28, 0x430c, 0xb070, 0x4091, 0x4191, 0x4038, 0x436d, 0x4285, 0x41de, 0x4037, 0xbfbb, 0x45a5, 0x1c02, 0x2189, 0x0fae, 0xb25d, 0x1d7c, 0x43ef, 0xa694, 0x10c2, 0xbfc8, 0xba12, 0xad26, 0x1501, 0x41c6, 0x4212, 0x197b, 0x4350, 0x403b, 0xbfe0, 0xb29d, 0x4558, 0x4205, 0x439c, 0x444f, 0xb01c, 0x1e76, 0xb2d5, 0x44f9, 0x46d3, 0x4036, 0x41de, 0xba6d, 0x2a32, 0xbf38, 0x4160, 0xbf37, 0xb0ec, 0xba41, 0xb28f, 0x41f1, 0xbf86, 0x2b4a, 0xb209, 0x4338, 0x1eb8, 0xbf04, 0x4336, 0xb210, 0xb0d2, 0x41de, 0xaa40, 0xb00f, 0xb025, 0x424e, 0x4454, 0x43c9, 0x42c6, 0x4158, 0x42e4, 0xbf22, 0x40d0, 0x40b8, 0xb2cf, 0x1b2e, 0xba36, 0xb2df, 0xbf4e, 0xbad7, 0x4172, 0x41ab, 0x46e0, 0x048d, 0x40d0, 0x42d6, 0x4302, 0xbacf, 0x42b1, 0x30ee, 0xb2ce, 0x43e0, 0x3e75, 0x402b, 0x0cb9, 0xba34, 0x0d88, 0xbfce, 0x0bf1, 0x1510, 0x4316, 0x463d, 0x458e, 0x1925, 0xaec3, 0xbf4c, 0x408b, 0xb08c, 0xb268, 0x4283, 0x410e, 0xbfc8, 0x261d, 0x1efb, 0x4309, 0x43ca, 0x4080, 0x423b, 0x4099, 0x4566, 0x4000, 0x10e8, 0xb20e, 0xba40, 0xbad2, 0x21b3, 0x4310, 0x4023, 0x1c67, 0x418e, 0xa05b, 0x4275, 0xb00a, 0xbf7c, 0x1ae8, 0xba49, 0x1039, 0x438d, 0x435f, 0x40ad, 0xb287, 0x282a, 0xaa2d, 0x427f, 0x42c7, 0x4354, 0x435d, 0x41b9, 0xb06f, 0x46e9, 0xba23, 0x40e8, 0x408d, 0x4013, 0xbf0a, 0x435c, 0x2384, 0x4542, 0xa98c, 0xb20b, 0xb27c, 0xbafa, 0x4303, 0x4097, 0x41f9, 0x42ea, 0x407e, 0x3c9a, 0x4097, 0x4359, 0x1e96, 0x0020, 0x3c97, 0xba3c, 0xb2ff, 0x42ca, 0x4471, 0x4076, 0xb250, 0xbf7e, 0xbfd0, 0xb23f, 0x4365, 0x4638, 0x1d42, 0x21a7, 0x1325, 0x4367, 0xb038, 0x4379, 0x4304, 0x4574, 0x410c, 0xbf8e, 0x23da, 0x40ad, 0xb202, 0x42a5, 0x4321, 0x44f4, 0x1a22, 0xba30, 0x40f6, 0x40a1, 0xa786, 0xb0b2, 0x414a, 0x43cd, 0x42ef, 0x034a, 0xbf3d, 0x4029, 0x463e, 0x0ac8, 0x43f0, 0x1ac0, 0x401e, 0xbaea, 0x0d5e, 0xbfd3, 0xb2a2, 0x42b9, 0xb2ff, 0x4151, 0x42e3, 0x4083, 0x1864, 0x42f9, 0x19ba, 0xb058, 0x1a60, 0xa486, 0x2355, 0x2060, 0xbf0b, 0x42bd, 0x418e, 0x1ceb, 0x4290, 0x1f52, 0x1c67, 0xa739, 0x437c, 0x189e, 0x43e6, 0xbf0f, 0x415d, 0xb208, 0xb267, 0xa399, 0x411f, 0x1954, 0x346b, 0x19f7, 0x43e8, 0x433b, 0x3630, 0xba34, 0xbad2, 0xbf2c, 0xb291, 0x42b5, 0x4102, 0x1bbe, 0x4033, 0x4632, 0x1ffe, 0x436a, 0x4364, 0xbaf1, 0xbf94, 0xb289, 0xb0fc, 0x432a, 0x1ced, 0xba74, 0x2483, 0xbf5b, 0x426c, 0xb207, 0x41d8, 0x4157, 0x42e7, 0x42df, 0xb26e, 0x13cd, 0x4120, 0x1d31, 0xa70b, 0xbff0, 0xb03f, 0xb272, 0x4388, 0x370d, 0x1d74, 0x1ce9, 0x40ee, 0x403b, 0x1c1b, 0xbac0, 0x1007, 0x4008, 0x1996, 0xbf32, 0x1b8b, 0x23d1, 0x4556, 0x1cac, 0xa867, 0x3b4b, 0xb22c, 0x4419, 0xba1d, 0x4384, 0x4038, 0xba49, 0x4173, 0x3e55, 0xba7a, 0x4041, 0x421a, 0x4475, 0x409a, 0xbfd0, 0xb023, 0xb2be, 0xb2b7, 0x46ad, 0x4027, 0xa0cf, 0xbfe1, 0x3b58, 0x4156, 0xb213, 0x4388, 0x4443, 0xb21a, 0x42a6, 0xb2bd, 0xbace, 0x415c, 0x19b0, 0xbf97, 0x0d21, 0x1f02, 0x4133, 0x0a3b, 0xb027, 0x39b7, 0x429d, 0xb2b5, 0x4243, 0x00bf, 0x42a1, 0x45eb, 0xbf99, 0x4124, 0x2790, 0x4250, 0xbae9, 0x1d8c, 0x041e, 0x4222, 0x4243, 0x421a, 0x41e6, 0x1efc, 0x4611, 0xbfd6, 0x1e2d, 0x41a8, 0x4220, 0x42ee, 0x40ae, 0xba77, 0x4177, 0xae2a, 0x4190, 0x43f0, 0x1dc7, 0xb036, 0x464f, 0x4654, 0x4100, 0xbad3, 0x420e, 0x2f13, 0x42c7, 0xb00b, 0x4387, 0x12cc, 0x406e, 0x419b, 0x420e, 0x1e6c, 0x2d42, 0xb24d, 0xbf11, 0x421e, 0x43a3, 0x18d2, 0x3178, 0x4093, 0x42cd, 0x1c0a, 0x4173, 0x1821, 0x1c38, 0x43d8, 0x44e5, 0x4239, 0x13f2, 0xbf11, 0x42df, 0xba20, 0x4020, 0x4190, 0x40d4, 0x438a, 0x403e, 0xb22b, 0x400a, 0x2101, 0x223e, 0x0d56, 0xbf9b, 0x4328, 0xb276, 0xb09a, 0xb229, 0x1c8c, 0x09ad, 0x1cc6, 0x0e1f, 0x43f6, 0x411c, 0x41b2, 0x265b, 0xba5c, 0x0f83, 0x41be, 0x2897, 0xba67, 0x40eb, 0x413c, 0x1c6c, 0x4010, 0x1829, 0x43ce, 0x4292, 0xbf06, 0x4156, 0x43d6, 0x431b, 0xb2e6, 0x4167, 0x0318, 0xbfe0, 0x4224, 0x13e7, 0x4165, 0x4402, 0x41be, 0xa3ad, 0xb22e, 0xba0d, 0x1fae, 0xaf2b, 0x13bb, 0xb299, 0x422d, 0x4113, 0x3fef, 0x41ea, 0x1ebd, 0x12b6, 0x1b80, 0xbf4e, 0xb2f6, 0x33be, 0x4075, 0xbf35, 0xb24b, 0x42fc, 0x4039, 0x18d0, 0x2d6f, 0x1e21, 0x1cd0, 0xba62, 0xab70, 0xa93f, 0xba33, 0xbf92, 0xb23f, 0x41fc, 0x176e, 0x436f, 0x4246, 0x4119, 0x445f, 0xba22, 0xbf60, 0x4075, 0x45da, 0xbf70, 0x4011, 0xb29b, 0x1d67, 0x413f, 0x0a8d, 0x464c, 0xac75, 0x432d, 0xa2cf, 0x0872, 0x18ae, 0x4315, 0xbf66, 0xb249, 0xba19, 0x4074, 0xa674, 0x4009, 0xbf90, 0x4373, 0x4072, 0x1f85, 0x1968, 0x3a9a, 0x1e06, 0x44cd, 0x4152, 0x433c, 0xba71, 0x1835, 0x40bf, 0xbf80, 0x4150, 0xbf05, 0xb241, 0xba5b, 0x41a7, 0x4445, 0x4232, 0xb2c9, 0x418b, 0xb20c, 0x436c, 0x46b9, 0xba16, 0x411b, 0x40d0, 0x1af2, 0xb06c, 0xbf3f, 0x429d, 0x4296, 0xac36, 0x40b1, 0x19f1, 0x1a06, 0x131c, 0x403f, 0x19e2, 0xa234, 0xbf62, 0xba62, 0x2ad5, 0x1b63, 0x4048, 0xb00f, 0xb265, 0xb03b, 0xb2da, 0x17c2, 0xb0c6, 0x1c3e, 0x41c4, 0x42af, 0xbfae, 0x2515, 0xb2e3, 0x42f6, 0x4065, 0x21bc, 0x2a1c, 0x2d3e, 0x43ba, 0x40e0, 0x435e, 0xb24d, 0x0e36, 0x406f, 0x404b, 0x1c48, 0xbf97, 0xbf70, 0x4153, 0xb20f, 0xb25e, 0x43c9, 0x4226, 0x40dd, 0x405c, 0x43cb, 0x0d09, 0x19fa, 0x1d04, 0x430c, 0x4029, 0xb2fa, 0xbf1f, 0x0aa8, 0xb223, 0xb2ca, 0x413f, 0x1b91, 0x36e0, 0x19dc, 0x005f, 0x1420, 0x40fc, 0x439f, 0xbfb3, 0x0279, 0x1824, 0x1879, 0xa189, 0xba14, 0x33bb, 0x4266, 0x0602, 0x41c4, 0xba73, 0x40eb, 0x419b, 0xbfc3, 0x4179, 0x183d, 0x4339, 0x41bb, 0xa7fd, 0xb257, 0x42bf, 0x4551, 0x425d, 0xba0a, 0x1e0f, 0x200a, 0x4204, 0x42ca, 0xa1de, 0x0124, 0x4282, 0x1d1d, 0x411c, 0x18bd, 0x406d, 0xba1f, 0xbad9, 0x0714, 0x421f, 0xbf5f, 0x409f, 0x4317, 0x419c, 0xba34, 0x40e1, 0x1b14, 0xbf21, 0x38e6, 0x1016, 0x4387, 0x42c9, 0x10ca, 0x101a, 0x43e1, 0xb2a6, 0x1c7d, 0x4318, 0xbf49, 0x4573, 0x1e86, 0xb256, 0x40df, 0x43fb, 0xbfd3, 0xa2c5, 0x4175, 0xb22e, 0x18cb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xff38a7fb, 0x4577c13c, 0xbafd4bd9, 0x608edce2, 0x0b12ce77, 0xdd0d292c, 0xd713d1ca, 0x0199fdd1, 0xee4843e5, 0x33f7a879, 0xf40bded8, 0x113e3e39, 0xfa00e8ce, 0xeb295429, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0xffffffbe, 0x3ffeffff, 0x00001ae0, 0x3ffefffe, 0xc0010000, 0x000000dc, 0xffffffff, 0x00000000, 0xfa00e8ce, 0x5b129380, 0xf40bded8, 0xf40bded8, 0xfa00e8ce, 0x9b2a428a, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbae5, 0xb227, 0x44d8, 0x43d3, 0x41d7, 0x40a2, 0x41d7, 0x42f3, 0x42eb, 0xb065, 0x42f8, 0xbfd8, 0x1e0b, 0x4085, 0x4121, 0x4130, 0xbf3d, 0x4042, 0x43f3, 0xa770, 0x4317, 0xb214, 0x4005, 0x2688, 0xb26b, 0x4044, 0x41b2, 0x2c6e, 0x1b18, 0x1d98, 0x1f7c, 0xb0fc, 0x427e, 0xbfa4, 0x0b8c, 0xb2a3, 0x1991, 0x3cc3, 0xa055, 0x1bcb, 0x42a4, 0xaa13, 0xbf8c, 0x400e, 0x1fcd, 0x420e, 0x437f, 0xb009, 0x44e0, 0x40d3, 0x45ad, 0x4242, 0x435b, 0x24d1, 0xa139, 0x3049, 0xba6c, 0x1689, 0x2538, 0x009e, 0x12e4, 0x4226, 0x1db2, 0xa704, 0x41ff, 0xb252, 0xbf25, 0xb201, 0x45a3, 0xb2f1, 0xb06a, 0x10c5, 0x24cf, 0x42ed, 0x4420, 0x40d0, 0xb29b, 0xbf0a, 0x2ff2, 0x46d3, 0xba69, 0x44da, 0x0515, 0xba24, 0xb263, 0x1e73, 0x43ce, 0x4269, 0xbfb0, 0x4322, 0x41e8, 0xb229, 0x1d20, 0x46aa, 0xb2c3, 0x4613, 0x41d9, 0x0ac4, 0x4352, 0x4031, 0x40dd, 0xa769, 0xb008, 0xb09e, 0x29dd, 0xbf87, 0x437b, 0x0284, 0x4337, 0x4224, 0x417f, 0x3e53, 0x44a4, 0x400d, 0x1cf0, 0x4388, 0xbaf0, 0x4181, 0x4399, 0x2de9, 0x1200, 0xbf97, 0x4200, 0x41ea, 0xb0e3, 0x40f6, 0x4436, 0x4077, 0xa2c4, 0xbf6c, 0x1095, 0x1f06, 0x05cd, 0xb2e0, 0x40a7, 0xb0e6, 0x4090, 0xa9ac, 0x1fff, 0x4303, 0xb08d, 0xbad3, 0x413b, 0xbf2a, 0xbae2, 0xbfd0, 0x41d7, 0x437e, 0x42f2, 0xa025, 0x42d1, 0x3ebf, 0x1acc, 0xb28a, 0xa56f, 0x407b, 0x1ffc, 0x4124, 0x400b, 0x41fb, 0x1d4e, 0x40a0, 0xba67, 0x4383, 0x4239, 0x418c, 0x43e7, 0xba5c, 0xb03e, 0x4260, 0x404d, 0xbf8d, 0x1f0c, 0x1e74, 0x4132, 0x41ce, 0x4263, 0xb204, 0x1be7, 0x426d, 0x40ca, 0x408d, 0x4298, 0x183a, 0x1b8f, 0x4063, 0xb207, 0xbf7d, 0x4259, 0x2680, 0x44e4, 0x414f, 0x4390, 0x4218, 0x4111, 0x4477, 0xa2f6, 0xaac4, 0x40f7, 0xb24e, 0x026d, 0x1dae, 0x4296, 0x4151, 0xb0b1, 0x439b, 0x4264, 0x0f33, 0x1cee, 0xba12, 0x4026, 0x32d6, 0xbf26, 0xba1d, 0x4310, 0x1da4, 0xbfa5, 0xa234, 0x4551, 0xba63, 0xad72, 0x38dd, 0x421f, 0x418b, 0x4198, 0xba0c, 0xb063, 0xba67, 0xbf00, 0xa1f0, 0x419d, 0x459e, 0x2726, 0x40c7, 0xbad0, 0x4105, 0x1835, 0x4245, 0x4088, 0x411d, 0x0545, 0x42fa, 0x4549, 0xbf68, 0x0932, 0x3b74, 0x27a2, 0xbad1, 0x403e, 0x1edf, 0x125f, 0x4020, 0xba01, 0xb243, 0x40cc, 0x40c3, 0x431a, 0xba6a, 0x1e8f, 0xb297, 0x3805, 0x4385, 0x3068, 0x1ffa, 0x4568, 0x4240, 0x41c8, 0x449d, 0xbfda, 0x423a, 0x1853, 0xb217, 0xa4c3, 0x4260, 0x41e0, 0x405f, 0x4258, 0x4348, 0xb0ad, 0x407e, 0x42e6, 0x1c88, 0xbafc, 0x4112, 0x42f5, 0xba7c, 0xb200, 0xb243, 0x4050, 0xbf8c, 0x409c, 0x16eb, 0x4324, 0xb26a, 0xb0e0, 0xbf11, 0xba41, 0x4276, 0x3fbd, 0xb227, 0x46f9, 0x4096, 0x4115, 0x428f, 0x4341, 0xba68, 0x430d, 0x439c, 0x0258, 0xb0ac, 0x3247, 0xb2fd, 0x18dc, 0x4196, 0x1bc4, 0xbf69, 0x1fc3, 0x17db, 0x4028, 0x426b, 0xb045, 0x3fc7, 0x403b, 0x42b2, 0x4155, 0x4027, 0x4055, 0x4397, 0x40ca, 0xb2e4, 0xbfe0, 0x4067, 0x4213, 0xb019, 0x42f1, 0x18c4, 0xb26e, 0x2a83, 0x40d4, 0x43a9, 0x3df6, 0xb2d0, 0xbfd0, 0x1aa2, 0xbf38, 0x4283, 0x41a3, 0x4261, 0x07ee, 0x406e, 0x4323, 0x24cd, 0x413d, 0xb2d9, 0x4217, 0x41a7, 0x0450, 0x40e0, 0xb0f8, 0xb2cd, 0xbf6f, 0xa598, 0x36ad, 0xbafa, 0x4247, 0x428c, 0x419b, 0x2ddd, 0x2ff4, 0x42a3, 0x420b, 0x0665, 0xb09f, 0x25aa, 0xb222, 0x424b, 0x41d5, 0x084b, 0xbfdc, 0x448c, 0x1ee0, 0x427a, 0x42ec, 0xb24b, 0xb203, 0x4277, 0x40d6, 0x43b7, 0xbfce, 0x40fd, 0xb05a, 0x443e, 0x4319, 0xaf57, 0x4372, 0xbfb3, 0x0ea7, 0x1c4d, 0xa3d4, 0x4445, 0x424c, 0x0597, 0xb2e2, 0x44bb, 0xba22, 0x448a, 0x40b2, 0x0495, 0x1d64, 0x4227, 0xbaf8, 0xb2eb, 0x40e2, 0x350e, 0x38b4, 0x19de, 0x4351, 0x43d9, 0x4078, 0x0edb, 0x42b0, 0xbf08, 0x07cf, 0x41f1, 0xbff0, 0xbad4, 0x45a0, 0xbae6, 0xb001, 0xbad4, 0x430d, 0x425b, 0xbf6d, 0x0b2e, 0xa8b3, 0xb282, 0xbaff, 0xaf30, 0x0047, 0x18af, 0x2845, 0xbf0a, 0xba43, 0xb2e9, 0x445d, 0x42b1, 0x415c, 0x428b, 0x4305, 0x4298, 0x432a, 0x43ed, 0x142c, 0xab21, 0x4167, 0x211c, 0x4346, 0xbf7f, 0xb0a2, 0xb2a2, 0x23cf, 0xb22b, 0x41d2, 0x4187, 0x3931, 0x1ad7, 0x402b, 0x41d2, 0x4128, 0x0ad0, 0x4170, 0x1dac, 0xba34, 0x402d, 0x40e6, 0x41d2, 0x0a0b, 0x45f2, 0xa738, 0xa730, 0x4076, 0xbf74, 0x44f0, 0x2744, 0x1bf9, 0xbafa, 0xb258, 0xb215, 0xbf9f, 0x46b2, 0xb083, 0xb2d0, 0x18ba, 0xba65, 0x43ee, 0xbfdc, 0x0348, 0xb2a8, 0xbf72, 0x4345, 0x1fa2, 0xb08b, 0x433a, 0xbf14, 0xba2d, 0xbacf, 0x400b, 0xbac3, 0x4203, 0x443c, 0x40e6, 0x1f06, 0x43ab, 0xb2c5, 0xba4f, 0x417d, 0x43b4, 0x4044, 0x1f8a, 0x4299, 0xaa10, 0x183b, 0xbfd4, 0x454a, 0x3c00, 0xb2c6, 0x0d40, 0x411f, 0x4292, 0x11dc, 0xb2e2, 0x422f, 0xb2cc, 0x41f2, 0x438b, 0xbac1, 0x423e, 0x4329, 0x4390, 0xb019, 0x40e1, 0x458c, 0x4305, 0x09ce, 0x4666, 0x4459, 0x46dd, 0x0581, 0x4368, 0x34e4, 0xbf5c, 0xb22a, 0x4199, 0x4330, 0x42e2, 0x46ec, 0x3b8e, 0x2edc, 0x4173, 0x4356, 0x41e0, 0x43ab, 0x0b77, 0x4222, 0x1f4c, 0xba39, 0x407d, 0xb298, 0xb26d, 0x4088, 0xb0b2, 0x4055, 0xbf59, 0x4689, 0xb004, 0x4380, 0x406b, 0x19ab, 0x2018, 0x447c, 0xbf1f, 0x41e0, 0xb24f, 0x406b, 0x2d16, 0x05be, 0x4252, 0x008b, 0x1d45, 0x424b, 0x41fe, 0xb282, 0x4089, 0xbf25, 0xb252, 0x430c, 0xba72, 0x42e9, 0x3df2, 0x4351, 0x40af, 0xbf79, 0x4375, 0x1be2, 0x43b9, 0x28e9, 0xbf08, 0xb203, 0x4201, 0x40b3, 0x0265, 0xb060, 0x4236, 0x41dc, 0xbfe0, 0x1d87, 0x41bf, 0x4040, 0xb27e, 0x0ad0, 0x3812, 0x4141, 0x1d57, 0x43fc, 0x1e44, 0xba47, 0xa9bb, 0xbfbc, 0x414d, 0x4386, 0x1cd0, 0x4445, 0x4014, 0xba62, 0x19ed, 0x41d1, 0xbfbf, 0x41bc, 0xb0d9, 0x42b1, 0xb281, 0x3f21, 0xbf65, 0x409f, 0x4051, 0x004b, 0xba2b, 0x463d, 0xaa52, 0xa84c, 0xb0ec, 0x06ee, 0xa560, 0x463c, 0xb007, 0x2991, 0xb22c, 0xbf80, 0x3632, 0xb296, 0x3c62, 0x43cc, 0x120c, 0x42e2, 0x4030, 0xbfd0, 0x43a0, 0x0c17, 0x3416, 0x4624, 0xaae3, 0xbfd2, 0x454e, 0x430d, 0xba74, 0x41b8, 0x37e3, 0x3b51, 0x411f, 0x4200, 0x437a, 0x1e3e, 0x4098, 0xbf4b, 0x19a0, 0x4544, 0xa8a3, 0x41ec, 0xb0de, 0xbf80, 0x3899, 0x0994, 0x1c6c, 0x42d6, 0xba74, 0x43c6, 0xba20, 0x429d, 0x4010, 0x118b, 0x402c, 0xbf62, 0x42b4, 0x4638, 0x4269, 0x2880, 0xb015, 0x36fd, 0x4179, 0x1d14, 0x46da, 0x1a37, 0x21a6, 0xb0b8, 0xb2a7, 0x1f43, 0xba10, 0x1ea2, 0xba07, 0xb09e, 0x28a5, 0x426e, 0x3bc9, 0x41b8, 0x465e, 0x1cfe, 0xbf72, 0x41e6, 0x1bc3, 0xb04a, 0xbae4, 0x43df, 0x4281, 0x2e57, 0x408f, 0xb08e, 0x4069, 0xbfb0, 0x440a, 0xbfb0, 0xb230, 0xb0f2, 0x45b4, 0x402b, 0xb2e8, 0x4207, 0xbac1, 0x4186, 0xba45, 0x43d8, 0x43b2, 0x42cf, 0x41ad, 0x432d, 0xbf66, 0x401f, 0x43f5, 0x44f8, 0x42af, 0xa978, 0x439a, 0xb2fd, 0xb259, 0xb24a, 0x42b1, 0xba6d, 0x398d, 0x4190, 0x43cf, 0xb2a3, 0xba25, 0x26d9, 0x43db, 0xba1f, 0x1d49, 0x197a, 0xb0bc, 0x40ae, 0xa9b4, 0x3109, 0xbf03, 0x4125, 0x31de, 0x459b, 0xb2e0, 0xba14, 0x41bb, 0x437a, 0x425a, 0x1a58, 0x288b, 0x1d8f, 0xb084, 0x4235, 0x4349, 0x1631, 0x4043, 0x2199, 0x075f, 0xb071, 0x0a46, 0xba55, 0x40a5, 0x26e7, 0xbf00, 0x3d20, 0xa8f9, 0x4025, 0x44c4, 0xbf2a, 0x40eb, 0xba2a, 0xba0b, 0x4115, 0x42c2, 0x1d34, 0xba10, 0xba7c, 0x4079, 0x1b5d, 0xba50, 0x4369, 0x3eba, 0x400b, 0xb0b8, 0xbae1, 0x41f2, 0x43ef, 0x42a4, 0x4559, 0x242c, 0xacb2, 0xad93, 0xbf72, 0x4297, 0x42fc, 0x4269, 0x43b5, 0x1e97, 0x40dc, 0x35e6, 0xbacf, 0x1a73, 0x411a, 0xbae3, 0xbf33, 0xb2b2, 0x34a3, 0x425a, 0x405e, 0x3399, 0x21cc, 0x195d, 0x435e, 0x4099, 0xbfa0, 0xae2f, 0x43d9, 0x0109, 0x4025, 0x4155, 0x4462, 0x1602, 0x43bd, 0x4248, 0x4194, 0x434a, 0xabb9, 0xb2c8, 0xbf4d, 0x413d, 0xba58, 0x03e3, 0x40f5, 0x403c, 0x1598, 0x4265, 0x1f54, 0x4188, 0xb0aa, 0x18e2, 0x4223, 0xbfb0, 0x410b, 0x40f5, 0x1ab4, 0x23b7, 0x19ba, 0xbf7f, 0x18fe, 0x199f, 0x4011, 0x410d, 0x27b0, 0x4339, 0x1d01, 0x2979, 0x31af, 0xa4a3, 0x1dfb, 0x401b, 0x40b7, 0x3d6e, 0xb22d, 0xbf64, 0xb040, 0x3846, 0xbfbf, 0xb24b, 0x41fd, 0x2226, 0x43cf, 0x3af2, 0x4365, 0xb003, 0x4333, 0xaa10, 0x1ca0, 0xb0f2, 0xbf9f, 0x1fed, 0xb09e, 0x41b3, 0x468b, 0x40f3, 0x4398, 0x413c, 0x40b8, 0xb017, 0xb226, 0x43f4, 0x4340, 0x41d7, 0x01f2, 0x4374, 0x410a, 0x28e5, 0xb07f, 0x0cf8, 0x40c0, 0x422f, 0xbfdd, 0x4023, 0x085c, 0xb22c, 0x41c8, 0x4433, 0x45c9, 0x43d7, 0x4378, 0xbf06, 0x4570, 0x40e0, 0xb004, 0xb292, 0x449d, 0x1d73, 0xb24e, 0x4347, 0x4071, 0xb2d4, 0x1aed, 0x43ec, 0x1f32, 0x43e5, 0xa386, 0x3bc4, 0x4418, 0x40d4, 0x4031, 0xbfd5, 0x41f7, 0x401c, 0x41cd, 0x2c18, 0xb04b, 0x43da, 0xbafd, 0x41de, 0x4169, 0x406e, 0x43ee, 0x204a, 0xbf41, 0xbaff, 0x42ab, 0xbac3, 0x4691, 0xb269, 0x188e, 0x4277, 0xbf81, 0x43c8, 0xb223, 0x1dfd, 0x2342, 0x12d6, 0xb254, 0x19ce, 0x359d, 0x4343, 0x1cc8, 0x00c8, 0x26e1, 0xa58f, 0xb2c4, 0x4047, 0x3780, 0x4157, 0x3829, 0xbf48, 0x447c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xf96275eb, 0x02fe41e7, 0xeefa2e22, 0x4b46bc43, 0x003dddf6, 0xa6272337, 0xb656ca8c, 0x7ccb9d65, 0xf8d3063c, 0x09bff66f, 0x1d231647, 0xe7c3d8f9, 0x0fbd2fe9, 0x8a2c763d, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0xffffffd7, 0x00000000, 0xffffe71f, 0x000730c0, 0x000017ea, 0x00001a18, 0x000000e1, 0x00000080, 0xf054250c, 0x00000000, 0x1d231647, 0x00000a54, 0x0d773b53, 0x1d23118b, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x409f, 0x4371, 0x4362, 0xb217, 0xbf00, 0x43fa, 0xbf8c, 0x18bb, 0x42a0, 0xba1a, 0xada9, 0x403c, 0x00d4, 0xbf02, 0xb01d, 0x43d0, 0xb264, 0xb2aa, 0xb0dd, 0x425d, 0x1dc1, 0x0378, 0xb072, 0x1d92, 0x2fa8, 0x4140, 0x40ac, 0x4620, 0x0a08, 0x42a6, 0xba07, 0xbafe, 0x4171, 0x40c2, 0x4376, 0x41db, 0x40e1, 0x4305, 0xbf97, 0x14b7, 0xba36, 0x4393, 0x1f2f, 0x4483, 0xbf46, 0x2407, 0xba63, 0x4096, 0x4103, 0x4650, 0x2684, 0x41aa, 0x4377, 0x43fc, 0x11ca, 0x4595, 0x3398, 0x43ef, 0x4634, 0x436d, 0xb060, 0x0ea8, 0xb2b5, 0x2045, 0x43ef, 0x46a4, 0x40b6, 0x40bd, 0x127f, 0x290e, 0x4087, 0x422c, 0x1856, 0xbf8c, 0x4065, 0x3e07, 0xb004, 0x44ec, 0xbf4c, 0x193a, 0xbaf4, 0xafa0, 0xb2c6, 0x406a, 0x1865, 0xbfc0, 0x19b2, 0x43d6, 0x1b78, 0xb039, 0x407a, 0x4163, 0x411a, 0x1d8c, 0x04e2, 0x1988, 0xb215, 0x40a0, 0x13bd, 0x38a5, 0xb2ce, 0x403b, 0x4291, 0x0562, 0x4680, 0x402c, 0xbf2e, 0x409c, 0xbaf0, 0x433f, 0xa329, 0x4031, 0x3824, 0x403f, 0xb2c0, 0x428b, 0x0991, 0x4153, 0x3e46, 0x4097, 0x2e7d, 0x05f3, 0xb2ee, 0xbade, 0x2267, 0xba3a, 0x198c, 0xbf6d, 0x461a, 0x337e, 0x43f0, 0xb225, 0x42a7, 0xb2ab, 0x45f3, 0x1f4b, 0x421a, 0xbac9, 0xbf47, 0x42c3, 0x44b4, 0x1c39, 0x0574, 0x4051, 0x4010, 0xb20e, 0xb2b3, 0x4575, 0x2bae, 0x1821, 0x366d, 0xb273, 0xb237, 0xbf4e, 0x425c, 0x4340, 0x191e, 0xbaf4, 0xbadf, 0x1a8b, 0x118f, 0xbfb0, 0x1ee0, 0x0041, 0xbfd9, 0xa940, 0xba2a, 0x4389, 0xba3a, 0x410e, 0xb03c, 0xba6a, 0x0b96, 0xb2d5, 0x1cb5, 0x026c, 0x4176, 0x1c59, 0x43d8, 0xb291, 0x3bf6, 0x1cff, 0x0650, 0xb2f6, 0x4348, 0x460d, 0x411d, 0x413c, 0x42fe, 0x0242, 0x415c, 0x088e, 0xbfb4, 0x136d, 0x2a54, 0x4183, 0xb2ae, 0x4457, 0xb0ff, 0x2d0f, 0x1f2b, 0x437a, 0x2ba1, 0xbfb4, 0x439e, 0x4196, 0x43e3, 0xb23e, 0x1be7, 0x4181, 0x4250, 0xbfd0, 0x413d, 0xbf80, 0x4289, 0xb058, 0xbac1, 0x432b, 0x40b4, 0xbf8a, 0x400d, 0x236d, 0x42a4, 0xa46c, 0xb053, 0x2598, 0x015b, 0x0966, 0x4335, 0xb29d, 0xbf51, 0x4185, 0xb2f6, 0xac2b, 0xb2fa, 0x3432, 0x4628, 0x2d72, 0xb053, 0xa1e1, 0x059e, 0xb225, 0x42e2, 0x4383, 0x400f, 0x42bc, 0x190e, 0x4094, 0x43f7, 0xb245, 0x403e, 0x41b3, 0x419d, 0xbf65, 0x4045, 0xb013, 0x417b, 0x0ea0, 0xaf84, 0x42d0, 0xb228, 0x40f7, 0xba01, 0xbfc0, 0xb2d9, 0x1dc9, 0xbfc6, 0x1aca, 0x4375, 0x1b51, 0x1f9c, 0x1b0a, 0x0661, 0x447e, 0x436a, 0x4051, 0xad93, 0x41b5, 0x4213, 0x006d, 0x40da, 0x4619, 0x42d5, 0xbaf4, 0xaabe, 0xbf70, 0x38e2, 0xbf8b, 0x40ce, 0xb21f, 0xbfd0, 0xb24e, 0xab0e, 0xbae3, 0xb25d, 0xb2c7, 0x443c, 0x42f8, 0x4173, 0x420a, 0xb2d7, 0x42dc, 0xb283, 0xa7ba, 0xbfb0, 0x4074, 0xb2d4, 0xbfb2, 0x40b8, 0x40bb, 0x1964, 0x40ff, 0x43e7, 0x4290, 0xb26c, 0xb226, 0x240c, 0xb090, 0x4321, 0xb26b, 0x4234, 0xba39, 0xba20, 0x40e5, 0x4153, 0x0199, 0xb262, 0x0485, 0x40f7, 0x2228, 0xbfe0, 0x4241, 0x4287, 0x4352, 0xb233, 0x1f2a, 0xbf07, 0xb0fb, 0x19b1, 0x3a72, 0x41c3, 0x1d5c, 0x0cfd, 0xbf90, 0xb293, 0xbaca, 0xbf6d, 0xba19, 0xb06f, 0x2275, 0xb246, 0xae73, 0x405f, 0xb247, 0x40c7, 0x1cb3, 0xb2ef, 0x4125, 0x413d, 0x1e18, 0x28fe, 0xb0a7, 0xb0af, 0xbfb5, 0xba0e, 0x40ee, 0x41f1, 0x424b, 0x4308, 0x17c4, 0xb00a, 0x42ff, 0xbf49, 0x21f1, 0x409f, 0x43b7, 0x1990, 0xbfa6, 0x4602, 0x1a37, 0xb28e, 0x4223, 0x401b, 0x0ee5, 0x4382, 0x4017, 0xba24, 0x402e, 0x4220, 0x41a7, 0x411b, 0xb2e5, 0x42f2, 0x41f2, 0xb247, 0xbf90, 0x430e, 0x2206, 0x3b7b, 0x4253, 0x4376, 0x4017, 0xba4d, 0x4257, 0xbf28, 0xb2a0, 0x4194, 0x42b6, 0x1919, 0x40c2, 0x467a, 0x4374, 0xb2cd, 0x1d2c, 0x4462, 0x40d7, 0x402a, 0xba75, 0xb0f9, 0x44c0, 0x428a, 0x3166, 0x0ecd, 0xbf07, 0x0e7f, 0xb03c, 0xb023, 0x416d, 0x4106, 0xba15, 0x072f, 0x1f48, 0x1d5c, 0x45b6, 0x3b35, 0x4003, 0xbff0, 0x3519, 0x4268, 0x40a9, 0x4364, 0x40a1, 0x412e, 0x4393, 0x462b, 0xa299, 0x2497, 0xb286, 0xacdc, 0xb027, 0xbf70, 0xbaee, 0xb213, 0xbf5c, 0xb00c, 0x4376, 0x4541, 0x33d7, 0x41d9, 0x43a6, 0x2c34, 0x44fc, 0xb001, 0x05ac, 0x40d4, 0x4201, 0x2dc1, 0xbaf3, 0xb201, 0xba73, 0x1f51, 0xb20a, 0x43ab, 0x4100, 0x4389, 0xba77, 0xbf3f, 0xa8c2, 0xba34, 0x41ce, 0x43fa, 0xb059, 0x46db, 0xbf7f, 0xb2e4, 0x43d5, 0xba10, 0x41c4, 0xbad6, 0x41df, 0x1918, 0x4117, 0x117b, 0xbf00, 0x42ed, 0x442e, 0x4229, 0x4393, 0x422e, 0xba58, 0x4058, 0xb270, 0xba2f, 0x40a7, 0x36a0, 0x405b, 0x2f9e, 0xbfb9, 0x41df, 0x4201, 0x3389, 0x405f, 0xa198, 0x43f0, 0x4151, 0x410c, 0x2c84, 0x2f92, 0x4206, 0xba04, 0x462b, 0xbf1a, 0x41eb, 0x2a6c, 0x4245, 0xa394, 0xb28e, 0xb24e, 0x04f0, 0x46e1, 0x42b3, 0xb03f, 0x30ef, 0x1d17, 0xbaec, 0xb2f8, 0x4168, 0x1b83, 0xbfac, 0x2cce, 0x4159, 0xb227, 0x45a2, 0x43d6, 0x43ca, 0x4124, 0xbf07, 0x0220, 0x40f0, 0x4201, 0xb224, 0x32db, 0x4340, 0x4145, 0x42c3, 0xbfa0, 0xb2e1, 0x42ee, 0xb0ed, 0x40e9, 0x414b, 0x42bd, 0x4555, 0x09ca, 0xb076, 0x1fcf, 0x09e5, 0xaf80, 0xb209, 0x40f0, 0xb04a, 0x4015, 0x41b1, 0x0408, 0xbfa8, 0x177c, 0xbf60, 0x41f9, 0x4251, 0xbf1f, 0x464a, 0xbf70, 0xba33, 0x425c, 0x4233, 0x454e, 0x4374, 0xbadb, 0x4105, 0x431b, 0x32b3, 0xb042, 0xb242, 0x4357, 0x28d3, 0x20b8, 0x2d3a, 0xba3a, 0xba75, 0x409b, 0xb2e3, 0x2cf2, 0x4078, 0xb257, 0xbf55, 0x427d, 0xb29a, 0xbad7, 0xbff0, 0x4207, 0x3766, 0x1fff, 0x1e14, 0x3528, 0x41c9, 0x27a9, 0x4114, 0x44f8, 0x18b7, 0x1c48, 0x4278, 0x2785, 0x1db7, 0x4473, 0x41f3, 0x18f7, 0x2d3c, 0xbfd8, 0x1c68, 0x402d, 0x43db, 0x4017, 0xb049, 0x401a, 0xba2f, 0xb090, 0x4558, 0x42e2, 0xba3b, 0x444c, 0xb01f, 0xad32, 0x4391, 0xbafc, 0xbf18, 0x418c, 0xb224, 0xb2bd, 0x1d32, 0x4076, 0xac73, 0x3419, 0x4128, 0x435b, 0x44bb, 0x42ed, 0xba33, 0xaaf8, 0xba11, 0xbf80, 0xb04f, 0x4253, 0x3fd6, 0x43df, 0xba71, 0xb255, 0xbf23, 0xbad5, 0xb01d, 0x4054, 0x4178, 0x4213, 0xbad9, 0xbf5f, 0x43e8, 0xb204, 0x45bd, 0x311b, 0x4286, 0xb261, 0x2ba0, 0x1a16, 0xb042, 0x4417, 0x1915, 0x4220, 0x429f, 0x4223, 0x41b3, 0xbac5, 0xb06a, 0x1b2c, 0x1452, 0xbf74, 0xb251, 0x423d, 0x1d7d, 0x43f3, 0xba14, 0x230c, 0x41e8, 0x436a, 0xb2c1, 0x40fd, 0x413f, 0x420a, 0xb2dd, 0xb28c, 0x46a5, 0xb291, 0xb2ee, 0x1f83, 0xb2e8, 0xb2cf, 0xa1bd, 0xb0c7, 0x1ba4, 0xbae4, 0x425a, 0x41f3, 0xb04b, 0xbfdc, 0x0f78, 0xba29, 0xb0e8, 0x42c9, 0xb291, 0xba18, 0x432b, 0xbfac, 0x4109, 0xab55, 0x436f, 0x4349, 0x4132, 0x41d9, 0xb240, 0x4227, 0x43c5, 0x403c, 0x1d71, 0x4182, 0x403c, 0x40e0, 0x4077, 0xb27b, 0x45bc, 0xa153, 0xb017, 0x429e, 0x42ee, 0x4453, 0x4064, 0xb0c1, 0xbf43, 0x0c27, 0x0f14, 0x3796, 0x433a, 0xad1c, 0xb27f, 0x431e, 0x1f68, 0xbf00, 0x3852, 0x43b8, 0x32f1, 0x4380, 0x1ec3, 0x1500, 0xa820, 0xa8b8, 0xbf73, 0xb28d, 0xac37, 0x1ab0, 0x0759, 0xaf00, 0xb28d, 0x40e7, 0x4314, 0xbaf9, 0xb05d, 0x33b3, 0x372a, 0x430c, 0x421d, 0x432d, 0xbfc7, 0x14a2, 0xba0b, 0x2f72, 0x45d5, 0x40da, 0xbf94, 0x4398, 0xb2c2, 0x10af, 0xba46, 0xaedb, 0x41ed, 0xba66, 0xba70, 0xb28d, 0x4130, 0x4106, 0x225a, 0x42d1, 0xba7f, 0xb07b, 0x41f3, 0x18bc, 0xa31f, 0x1c7e, 0x2979, 0xb267, 0x430c, 0xbfb3, 0x4005, 0x45b6, 0x1d0a, 0xb29c, 0xbfe8, 0x413f, 0x4212, 0x1a6c, 0xb2cd, 0x41a3, 0x1b19, 0xad5e, 0xba4a, 0x155b, 0x0e84, 0x4041, 0x43fc, 0x41ac, 0xba0c, 0x41ab, 0x38f2, 0x43c9, 0x1f49, 0xb006, 0xb218, 0x0657, 0x0076, 0xb071, 0xb292, 0xbf33, 0x42d9, 0x419a, 0x430a, 0x40d0, 0x43ed, 0xae7a, 0xba09, 0x1dee, 0x4172, 0xbac3, 0x40b6, 0x418f, 0x4102, 0xba3a, 0xb255, 0xa8b8, 0x28b4, 0xbf2a, 0xbac3, 0x46b3, 0xb029, 0x402b, 0x18ac, 0x14fb, 0xbf26, 0x42ff, 0x4545, 0x413c, 0x43f9, 0xb099, 0xb2ea, 0xa112, 0x4037, 0xac7a, 0x42d3, 0xb2d3, 0x4136, 0xba7d, 0x4081, 0x40fb, 0x4421, 0x41ea, 0x46eb, 0x43d5, 0x1956, 0x3dd2, 0x1e1c, 0x4323, 0x1833, 0xb0c3, 0x4267, 0x428e, 0xbad9, 0xbfb5, 0x1cb6, 0x4248, 0x42b6, 0x19fb, 0xb0a6, 0x1a7f, 0xba5a, 0x1a28, 0x4446, 0x4207, 0x3fd4, 0x4119, 0x4069, 0xb215, 0xa19b, 0x4392, 0xbaef, 0xb2de, 0x1c2a, 0x46f5, 0xbf73, 0x4286, 0xbff0, 0x4006, 0xb279, 0xb27f, 0x428e, 0x416f, 0x411a, 0xbfd8, 0x0c5e, 0x444e, 0x43c3, 0x156d, 0x4175, 0xb2fb, 0x3877, 0xb2d7, 0x400a, 0x4429, 0x38fe, 0xb27a, 0x1868, 0x429f, 0xb0d5, 0x1515, 0x42ce, 0x45a1, 0x43e7, 0xbf55, 0xba1e, 0x4365, 0x1d96, 0x42fc, 0x43f5, 0x41ed, 0x4020, 0x4390, 0x1fa2, 0xbfd6, 0xb25f, 0x4128, 0x43ad, 0x1f47, 0x4373, 0x41e2, 0x4040, 0x4102, 0xad91, 0xb2ea, 0x4064, 0x44f9, 0x1cc9, 0x42d0, 0x2e1d, 0xb094, 0xbad1, 0xb0bd, 0x4144, 0x4074, 0xbfa0, 0x00b7, 0x4556, 0xb21d, 0xba47, 0x42e0, 0xb0fb, 0xbfcd, 0x193b, 0x4266, 0xb230, 0x41ec, 0x4271, 0x416e, 0xa323, 0x4225, 0xa208, 0x4057, 0x356c, 0x2a9d, 0x0db2, 0xbfc1, 0x19d8, 0x4242, 0x4240, 0x41eb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x1ecc668c, 0x2e9c60ff, 0x9990f0ee, 0xf1a38886, 0x3f294482, 0xc54ff2ce, 0x36359d5d, 0x6e0e101b, 0xc58ac456, 0x564992fe, 0xcc932f92, 0x05cbf818, 0x7c022c30, 0xc34238ff, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0xffffcfb8, 0xc342cf05, 0xffffcfb8, 0x80000185, 0xc342cf05, 0x000013a4, 0x3cbd4433, 0x000017f0, 0x0000138c, 0xc34266a3, 0xcc932f92, 0x00000371, 0xc3424f05, 0xfffffb7c, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x430e, 0x1b60, 0x2a48, 0xbadf, 0xb061, 0x420b, 0x41bf, 0xba6c, 0xb2dd, 0x3070, 0xb2ae, 0xb21f, 0x4226, 0x4260, 0xbaef, 0xb233, 0x4645, 0x439f, 0x2b19, 0x394a, 0xba1e, 0xba60, 0x40fe, 0xba31, 0xbf48, 0x4296, 0x433d, 0x4353, 0x26f9, 0x42c3, 0x4286, 0x2142, 0xba78, 0xa6be, 0x2ec6, 0xba58, 0xb2e3, 0x43c8, 0x43ce, 0x3eb2, 0x1c58, 0xbf61, 0xb24d, 0x1a44, 0xb2f5, 0xa49d, 0xba3d, 0xba1c, 0x420f, 0x0ec6, 0xbf1f, 0x4636, 0xad48, 0x4181, 0x4155, 0x3050, 0x4001, 0xba03, 0x422b, 0x0266, 0xb0c4, 0xba50, 0xa8b4, 0x1e73, 0x41f5, 0xa645, 0x436a, 0x277c, 0x432a, 0x1b94, 0x1d44, 0xb294, 0x4080, 0x42a6, 0xb256, 0xbf2d, 0x1926, 0x4000, 0x4396, 0xb221, 0xb071, 0x0912, 0xba74, 0xb2d9, 0x3bd3, 0x45f5, 0x35e1, 0xb2a0, 0x42f2, 0x1e6b, 0x1595, 0x0e57, 0x42a0, 0x45d6, 0xba44, 0x41a2, 0xb26d, 0x43d3, 0xa79e, 0x434c, 0xbfd9, 0x40a3, 0x418c, 0xb230, 0x2774, 0x414a, 0x4385, 0xba77, 0x4019, 0x4202, 0x4251, 0xbafe, 0x4243, 0xb0e9, 0x1fc2, 0x4196, 0x2115, 0x4570, 0x420b, 0x4344, 0x40fc, 0x1820, 0xbf13, 0x40f3, 0xbaf1, 0x4280, 0x4110, 0x43ba, 0x25f9, 0x4175, 0xbfa0, 0x09ba, 0x35e7, 0x41ec, 0x12ba, 0xb249, 0xbade, 0x3bf7, 0x3c20, 0xba5f, 0x4094, 0x3b99, 0x2bb1, 0xb2e3, 0x4166, 0x402f, 0x414c, 0x425d, 0x415d, 0x43ba, 0x40da, 0xbfdf, 0x45e1, 0x412c, 0xb2da, 0x44d5, 0x4180, 0x3b39, 0x4221, 0xbf62, 0xba33, 0x42c8, 0xadf9, 0xb2af, 0xb28f, 0x4254, 0x4121, 0x393f, 0xa074, 0x4320, 0xb0ef, 0x400e, 0xbf37, 0x41a4, 0xbfb0, 0x39e8, 0x4226, 0xafd2, 0x400d, 0x435e, 0x42f0, 0xb223, 0x403e, 0xba0c, 0xb09d, 0x42a0, 0x2b3a, 0x4016, 0x4308, 0xae2c, 0x3e99, 0x06a6, 0x45cb, 0x0741, 0xba55, 0xbf93, 0x1c12, 0x40e2, 0x24c4, 0x1fb7, 0x41ca, 0x2258, 0xbf5d, 0x3ccc, 0x420f, 0x41d4, 0xba60, 0x402d, 0x40e3, 0xb261, 0xbfcd, 0x415d, 0xb01f, 0x1901, 0x420b, 0x1a54, 0x3fa6, 0x4021, 0x1dcc, 0xb260, 0x425f, 0xb038, 0xba79, 0xba11, 0x103d, 0x1f3c, 0xba7c, 0xb283, 0xbfba, 0xb24d, 0xb273, 0x3b72, 0x3c0a, 0x40ca, 0xb2c3, 0x3882, 0xb282, 0xba42, 0x402a, 0x4655, 0x43bf, 0x2362, 0x43c6, 0x4148, 0xb08d, 0x4072, 0xa1f3, 0x3784, 0xb2a2, 0x438a, 0xbf3c, 0x22a4, 0x401c, 0x4201, 0xa508, 0xbae9, 0x4490, 0xb2ea, 0x1de4, 0xbae9, 0x43ab, 0x3fcb, 0x4028, 0xa9ca, 0x1a68, 0x2cfc, 0xb01d, 0xbfb8, 0x406b, 0x3db1, 0x41ff, 0x2386, 0x3663, 0xb206, 0x421c, 0xab7f, 0x41c8, 0xba13, 0x2a54, 0x41db, 0x407d, 0xb066, 0xbf1f, 0x1a65, 0x1d6f, 0x066a, 0x428d, 0x4065, 0x404f, 0x1ac2, 0x44db, 0x0b90, 0x4488, 0x4255, 0x4550, 0xb0c3, 0x4291, 0x414c, 0xb2e4, 0x4231, 0x430e, 0xb240, 0x4003, 0x1210, 0x43b1, 0xb246, 0x4344, 0xbf4f, 0x44f9, 0xb218, 0xba07, 0x4053, 0xbf60, 0x4248, 0x1432, 0x45c3, 0x4083, 0x4403, 0x2f70, 0xbfb9, 0xaecd, 0xb050, 0x40e7, 0x409f, 0xaeeb, 0x40d6, 0x4315, 0x125b, 0x400b, 0xb0a3, 0x44db, 0xbf37, 0x4202, 0x4179, 0x1d0e, 0x2d2d, 0xa705, 0x43ee, 0x3206, 0x1586, 0xb25f, 0x37da, 0x423c, 0x4151, 0x401f, 0x04c3, 0x219b, 0x22dd, 0x402d, 0x1c87, 0x4201, 0x41bb, 0xbfa6, 0xb040, 0xaf1a, 0x4372, 0xbadd, 0xbf02, 0x0c72, 0xb20c, 0x41cd, 0xb2e9, 0x4115, 0x1f3c, 0x4000, 0x44e1, 0xb2c9, 0x31aa, 0xbac6, 0xb083, 0x2dc0, 0x432d, 0x2a5c, 0xb2d3, 0x4019, 0x46ed, 0xbf6f, 0x4322, 0x4466, 0xb249, 0x2cf1, 0x4387, 0x4170, 0x43e0, 0x40a7, 0x16ff, 0x1793, 0x29d4, 0xba1d, 0x2029, 0x4173, 0xb217, 0x434f, 0xb231, 0xa7fa, 0xbf5e, 0x43ce, 0xb0ae, 0x3cf2, 0x3a2e, 0x43c6, 0x2f25, 0x4304, 0x0394, 0x43de, 0x4312, 0x429c, 0x1fd2, 0xb2c4, 0x3066, 0x4159, 0x4192, 0x46f8, 0xbae6, 0x412d, 0x400d, 0x4272, 0x3c14, 0x427f, 0x447c, 0xb294, 0x4609, 0xbf88, 0x43c4, 0x35ea, 0x4308, 0xbacc, 0x42b6, 0x433f, 0xa5b2, 0x43f6, 0xb212, 0x0a9d, 0xbae2, 0x118e, 0x4214, 0x4344, 0x1ff4, 0x18ec, 0xb025, 0x1865, 0x04b5, 0xbf63, 0x4102, 0xbae5, 0x41a5, 0x40fc, 0x1fee, 0x42f4, 0x438e, 0x1d08, 0xb27c, 0xba61, 0x40b7, 0x03e2, 0x1bcf, 0x34ad, 0xb2b0, 0x007d, 0x3c4f, 0x41d8, 0x45d1, 0x41ac, 0x442b, 0x424d, 0x4279, 0xa121, 0x44a9, 0x41f3, 0x01c8, 0x40bd, 0x414f, 0xbfcf, 0xba19, 0x42ea, 0x2383, 0x00d8, 0xba6a, 0xba41, 0xb01c, 0x317c, 0x40a8, 0x401d, 0xb054, 0x400b, 0x1d1a, 0x1fd3, 0x1b51, 0xbfa8, 0x403c, 0xb003, 0x4336, 0x3dd6, 0x40d2, 0x410b, 0xafc3, 0xabd6, 0xa7d5, 0x02c0, 0x4201, 0x4242, 0xa5f6, 0xb061, 0xba2d, 0x1be8, 0x0262, 0x434c, 0xa56e, 0xbf95, 0xba08, 0x22fd, 0xb22d, 0xa383, 0x1876, 0x024b, 0x4134, 0x2c98, 0x4232, 0xa829, 0x41cc, 0x4237, 0x4287, 0x44db, 0x46fa, 0x415c, 0x432b, 0x416d, 0x4231, 0xaba6, 0xb208, 0x402f, 0xba7d, 0xa377, 0x4005, 0xacdb, 0xbfc2, 0x1ce5, 0xbf70, 0xb26c, 0x3bb5, 0x05cf, 0xb0f3, 0xb222, 0x4398, 0x4632, 0x4132, 0x4371, 0x1bd3, 0xb212, 0xbfac, 0xbf80, 0x41e2, 0xbf3e, 0x431e, 0x0ade, 0x4225, 0x26de, 0x4062, 0x4660, 0x4054, 0xbf5a, 0x43b4, 0x4282, 0x4255, 0x1cb3, 0x286c, 0xba6f, 0xabbd, 0xb0f6, 0x4206, 0x4154, 0x40f2, 0x42e7, 0x30f7, 0x4005, 0x45b6, 0xbf9f, 0x40a2, 0xb2b8, 0xba26, 0x2711, 0xad55, 0x4382, 0xb0b4, 0x1990, 0xba12, 0xb2e0, 0xba00, 0xa4f2, 0x4356, 0xb211, 0x435a, 0x4052, 0xaaa8, 0x404c, 0x41d0, 0x1cb9, 0xb28e, 0x41dc, 0x2275, 0x4492, 0x0359, 0xb2e5, 0xbf4e, 0x1301, 0x44d5, 0x4122, 0x39cf, 0x347b, 0x43cd, 0x28be, 0x1851, 0x1a41, 0x40e7, 0xba0e, 0x18bd, 0xb095, 0xb269, 0x4029, 0x1879, 0x1b1b, 0x4156, 0x4210, 0xbf3b, 0x44ac, 0x405e, 0x42b1, 0x4306, 0xb297, 0x4198, 0xb002, 0x4232, 0x3704, 0x3643, 0x4220, 0xbf9a, 0x3986, 0xb2a7, 0x412d, 0x416f, 0x3c6c, 0xba06, 0x1bda, 0x435d, 0x421d, 0x41cf, 0xa2e4, 0x1cf3, 0x442b, 0x40ec, 0xb281, 0x1ebc, 0x4373, 0x40c9, 0xac98, 0xbff0, 0xa100, 0x409f, 0xb290, 0x41f9, 0x1733, 0xb255, 0x1efe, 0x4004, 0xbf3e, 0xb0c3, 0x4023, 0x09b0, 0x0995, 0x1ec1, 0x2998, 0xba06, 0x1e93, 0x43cc, 0xb2de, 0x42d3, 0x41ac, 0x4354, 0x4013, 0xa9a8, 0xbfd4, 0xb241, 0x4022, 0x1dcf, 0xb0ee, 0x43cc, 0x1a03, 0xbac9, 0x1b7e, 0x4311, 0x439e, 0xbf8a, 0x025e, 0x43b7, 0xa7ed, 0x4276, 0x4200, 0x42fa, 0xb04a, 0xa648, 0x418e, 0x40bd, 0x09bd, 0xbaec, 0x42d1, 0x2ede, 0x1b6e, 0x06b5, 0x08c6, 0xb2ba, 0xb2f6, 0x1d2f, 0x1aac, 0xba61, 0xbf2d, 0xb283, 0x4311, 0xb203, 0x4193, 0xa79a, 0x1eb0, 0x436e, 0xba53, 0x4612, 0x42d8, 0xb202, 0x3e0c, 0xaf8a, 0xba34, 0x4084, 0xabe0, 0x19fe, 0x4331, 0x4337, 0xb247, 0x4647, 0x407b, 0x31ff, 0xbf1c, 0x4148, 0x385b, 0x3fa5, 0xb2e5, 0x1e35, 0x087e, 0xaef2, 0xb093, 0x424c, 0xb227, 0x43dc, 0x102d, 0x4612, 0xb2c3, 0xb221, 0xb2ac, 0xbf24, 0x4359, 0x42b6, 0xb25d, 0x3d87, 0x42e8, 0xbaea, 0x436d, 0x1a91, 0xbafc, 0x42db, 0xa79c, 0x1946, 0x4140, 0x40af, 0x4095, 0x1ce6, 0x12fc, 0x2a6a, 0x40fd, 0x1e44, 0xbf75, 0x1ecd, 0x4114, 0x1fb8, 0xb07d, 0x41e1, 0x4306, 0x4040, 0x4007, 0xba64, 0xbf70, 0x4011, 0xbf39, 0x40be, 0xb257, 0xb05a, 0xb000, 0x2cf7, 0x0fae, 0xaa47, 0xbf1f, 0x4095, 0x43a6, 0xbfc0, 0x34a2, 0x3dc5, 0x42fe, 0x4329, 0x4135, 0xbae2, 0x46b9, 0xbfdb, 0x1c14, 0x4482, 0x432a, 0xb29a, 0x1fb4, 0x1869, 0x199f, 0x43b6, 0x3cd5, 0x437e, 0x2331, 0xb201, 0x4348, 0x2663, 0x41c9, 0x421e, 0xb2a6, 0x41a1, 0xbfc4, 0x3897, 0x4458, 0x41a9, 0xb2c6, 0xbfbf, 0xbf70, 0x1c52, 0x4132, 0x1e34, 0x0d87, 0xbae6, 0xb2b4, 0x2385, 0x41c1, 0x42ab, 0x1a6a, 0x07b7, 0x40aa, 0xbf21, 0x403b, 0xbaee, 0x43d9, 0x43ef, 0x41ab, 0x426c, 0x4280, 0x4019, 0xbf60, 0x4245, 0x1f77, 0x4061, 0xba66, 0x4086, 0x337a, 0x1942, 0x2580, 0x0667, 0x40dd, 0x4135, 0xbf56, 0x408c, 0xb2b1, 0xba18, 0xb2ac, 0xbf1b, 0x42d0, 0xb0c8, 0x418c, 0xb283, 0xaf4c, 0x1bab, 0xb047, 0xb092, 0x16b1, 0xbae1, 0xa058, 0xa49b, 0x4007, 0xaca0, 0x1e8e, 0x41dc, 0x4065, 0x3fbb, 0x4192, 0x191a, 0xb263, 0xbfa9, 0x4384, 0x438c, 0x46da, 0x1e3b, 0x1cc2, 0x43a3, 0xba39, 0xbfa5, 0xb0bf, 0x1e42, 0x1a0a, 0x1cad, 0x401c, 0x439f, 0xa4ab, 0x4302, 0x4484, 0x40d3, 0xbfd0, 0x41cd, 0x3bc6, 0x1fc1, 0x41b9, 0xb255, 0x41c4, 0xba13, 0xb298, 0x2459, 0x054c, 0xbfd4, 0x41d4, 0x1b7d, 0x1d2c, 0x2806, 0x1bec, 0xb2f3, 0x268c, 0x4131, 0x42cb, 0x4146, 0xbf8a, 0x42c8, 0x4057, 0xa7d7, 0x36f5, 0xb082, 0x439c, 0x42cf, 0x454e, 0x403b, 0xb225, 0x1e75, 0x4357, 0x2f15, 0x4096, 0x314f, 0xa555, 0xba10, 0x4003, 0x3197, 0xba21, 0x40a5, 0xbf9f, 0x41cd, 0x24ad, 0xaf2c, 0x42d0, 0x38f1, 0x407c, 0x429e, 0xb03e, 0xbf1f, 0x40c7, 0xb294, 0xa6a8, 0x387d, 0x1236, 0x4148, 0x3c48, 0xbf76, 0x00f5, 0xb2bf, 0x12a8, 0x423b, 0x41c2, 0x4206, 0xb0a4, 0x0c83, 0x40ff, 0x4138, 0xbfb0, 0x1b14, 0x4228, 0x3697, 0x3a11, 0x4107, 0xbafd, 0x447c, 0x43a3, 0xb28f, 0x4246, 0x454f, 0x432e, 0xbfdc, 0xb2d1, 0x437e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xcc2ef87d, 0xae95a3fd, 0x5e6e0ca5, 0x1ef09cca, 0x00ea6c00, 0x963e3b79, 0xc2107f27, 0x7b8c8e2a, 0xa52501a4, 0xdd47e07c, 0x58514dc3, 0xb401ec82, 0x88632001, 0xabb2221b, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x00000000, 0x0000003e, 0x0000183e, 0x00000000, 0x544dfc0a, 0x00000000, 0x00000000, 0x0000ffff, 0x0000133c, 0xffffffff, 0x0000148f, 0xa00f6410, 0x88633849, 0xabb2349a, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1a9a, 0xaba5, 0x42a3, 0x4339, 0x43b9, 0xb263, 0xb077, 0xbf80, 0x409b, 0x4178, 0xba22, 0x439a, 0x428e, 0xb0b6, 0x404e, 0x1ae0, 0xba1e, 0x0e81, 0x41c2, 0xbfb8, 0x40e2, 0x18d2, 0x462c, 0x43c9, 0x1e7d, 0xb21b, 0x43ce, 0x45b5, 0x42d0, 0xb038, 0x42c9, 0x4238, 0x1e37, 0x42b5, 0x41cc, 0x42f9, 0xb072, 0x4154, 0x030b, 0x4384, 0x42d6, 0x46a8, 0xbf6c, 0x0ce2, 0xb24b, 0x4305, 0x4418, 0xb006, 0x404c, 0x438f, 0x438f, 0x0532, 0x41e4, 0x4290, 0x4019, 0xba4e, 0xbf54, 0x412d, 0x4202, 0x4208, 0x4614, 0x193b, 0xbf57, 0x3ef2, 0x4692, 0xbaf4, 0xad07, 0xba72, 0x4549, 0xb22f, 0x40a5, 0x4184, 0x1cf9, 0x416c, 0x421a, 0x4371, 0xba66, 0xba38, 0x1c55, 0xaa65, 0xaa8c, 0x4070, 0x0cf2, 0x1ff4, 0x1a0b, 0xbfad, 0xb219, 0x400b, 0x2178, 0x1d28, 0x43f7, 0x419f, 0x421b, 0x1f66, 0x40a0, 0x4224, 0x4370, 0x4325, 0x37c4, 0xa62d, 0xb080, 0x43e0, 0xb2a1, 0x00db, 0xab3b, 0xbf15, 0x40ae, 0x109e, 0x3d73, 0x1fc1, 0x19fb, 0x3e2e, 0x42d5, 0x1946, 0x419c, 0x4135, 0x02c7, 0x46fb, 0xb22c, 0x40b6, 0x43a1, 0x4229, 0x4382, 0x42c3, 0x422d, 0x3ecb, 0x4289, 0x407d, 0xb090, 0xbf12, 0x0c06, 0xba06, 0x4324, 0x439b, 0x440d, 0x4177, 0x41a8, 0x41d5, 0x4282, 0xbaff, 0xbfc0, 0x3148, 0x4294, 0xbfb0, 0x1b88, 0x1f7c, 0xb2ba, 0xbf9f, 0x4085, 0x421a, 0xb2d2, 0x1e81, 0x40e0, 0x41c4, 0xbade, 0x19d2, 0x4289, 0xb237, 0xba1c, 0xba3c, 0x41e1, 0xba5e, 0xb2b8, 0x41f3, 0x423a, 0xb272, 0x40d7, 0x41d2, 0x4046, 0xbf88, 0x40d6, 0x4045, 0x43ff, 0x18df, 0xa909, 0x40cf, 0x42bc, 0x4198, 0x0130, 0x071f, 0x427e, 0x45a3, 0x1b5c, 0xbf0a, 0xb222, 0x3b72, 0x4039, 0x43af, 0x1dfe, 0x4338, 0xbfc2, 0x43f5, 0x41f6, 0x4012, 0x416e, 0x1d8b, 0x1ff4, 0xb2e2, 0x29ae, 0xa123, 0xb2ef, 0xbf26, 0x1f7f, 0x347b, 0x3277, 0x41e4, 0x43b0, 0x41b1, 0x4193, 0xbf05, 0xba7f, 0xb26d, 0x2ee4, 0x1eb0, 0x43dd, 0x413e, 0x4072, 0x1b4b, 0x4047, 0xb2ae, 0xbaf6, 0x41d7, 0xa6ec, 0xbfdb, 0xb250, 0x4057, 0x2618, 0xb282, 0x4321, 0x4288, 0x400d, 0x4138, 0xa678, 0xa9a6, 0xba47, 0xb2e9, 0x1995, 0x43d3, 0x4237, 0xb03d, 0x4144, 0x46a2, 0xb206, 0x1d19, 0x1b25, 0x4389, 0x4104, 0xb066, 0x1d21, 0x1f7e, 0x2adb, 0xbfd2, 0xa0ea, 0xa87a, 0x4382, 0xb018, 0x44ba, 0x425f, 0x3c44, 0x40eb, 0x43b6, 0xb0a6, 0xba7c, 0xba0c, 0xaeec, 0x4592, 0xb036, 0xbf15, 0xba48, 0xb076, 0x4042, 0xac7c, 0x4560, 0x4003, 0xb03b, 0x1bc1, 0x0ad5, 0x4293, 0xb262, 0x1c32, 0x199d, 0x435d, 0xbf80, 0x2273, 0xb2b2, 0x427b, 0x23cc, 0xb251, 0xb09b, 0x42c1, 0x37da, 0x437a, 0xb022, 0x4384, 0x4280, 0x2b09, 0x4396, 0xbfb8, 0xb09d, 0xaebd, 0xbf34, 0x4346, 0x169e, 0x425d, 0xb036, 0x1e3d, 0x1384, 0xb00e, 0x1cce, 0x135c, 0x4266, 0x025b, 0x46cd, 0x3cf3, 0xbf9d, 0x43d8, 0x41be, 0x2ba8, 0x1ba7, 0x43b3, 0x1ae9, 0xbad7, 0x4019, 0x1deb, 0x292d, 0x413c, 0x4096, 0xb203, 0x4033, 0x1e9e, 0x40c4, 0x2339, 0xb228, 0xb090, 0xbf2a, 0x4172, 0xb22f, 0x4418, 0x162b, 0x4061, 0xa58e, 0xba40, 0x42e7, 0x41e1, 0xb27b, 0x1c28, 0x45ae, 0x41a1, 0x4383, 0x4149, 0x4382, 0x1e4c, 0xbf14, 0x40bd, 0x4209, 0x401c, 0x0482, 0x42f0, 0x4681, 0x43d3, 0x4226, 0xadee, 0x1e44, 0xb0ad, 0x417e, 0x1efc, 0xbf49, 0xb22f, 0x2bad, 0x42a4, 0x42b2, 0xba1f, 0x4562, 0x40d3, 0x4619, 0xbae8, 0xba17, 0x4154, 0xbf6a, 0x407f, 0x40e1, 0x4359, 0x42f3, 0x137f, 0xb2b2, 0x419e, 0xbf0a, 0x4277, 0x41ca, 0x4025, 0x41f8, 0x11b6, 0xb015, 0x42bc, 0xb098, 0xbf26, 0x4382, 0xba65, 0x2399, 0x44e3, 0x1b58, 0x4395, 0x30cc, 0x41c1, 0x41fa, 0x4632, 0xa687, 0x192f, 0x3f96, 0x45d8, 0x41b2, 0x4075, 0x4080, 0xbf9f, 0x43b9, 0x1902, 0x1a58, 0xb003, 0xa201, 0xb28f, 0x405c, 0x41d8, 0xb0cb, 0x42ba, 0x43c2, 0x40fa, 0x4331, 0xb294, 0xb205, 0xb0a7, 0xbfc2, 0x4268, 0x403f, 0x40bf, 0x41e1, 0x407b, 0x4013, 0x2de8, 0xbf75, 0xb039, 0x1f48, 0x428a, 0x4226, 0xba11, 0x0c28, 0x338a, 0x1d9a, 0xb075, 0x3f72, 0x3f5e, 0x4165, 0x4585, 0x3a3d, 0x427e, 0xb0b8, 0xa4b0, 0x436a, 0x4385, 0xbf02, 0x45ba, 0xb07c, 0x42b1, 0xb26b, 0xb22a, 0x4297, 0x1bda, 0x4634, 0x0a08, 0x43e7, 0x169b, 0xa98d, 0x43e2, 0x42f6, 0x42c1, 0x41c2, 0x1cfd, 0xbf48, 0x40b7, 0x40de, 0x1f98, 0xb246, 0xb03d, 0x1e78, 0x3ac4, 0x407d, 0x1862, 0xb238, 0x2451, 0xba4a, 0x445e, 0x1a72, 0x4252, 0x1d66, 0xbf41, 0x42df, 0x31d5, 0xb095, 0x41d9, 0x434b, 0x4303, 0x2470, 0x1dff, 0x469a, 0x45b8, 0x418e, 0x456e, 0x4417, 0xba49, 0xb2b7, 0x4228, 0xa06c, 0x41e7, 0xa99b, 0x36cf, 0x46c4, 0x1852, 0x142a, 0xbfce, 0x06af, 0x4135, 0xb260, 0x3bfc, 0x410a, 0xbf90, 0xb0ca, 0x4165, 0xbf2d, 0x4001, 0x42d4, 0x2d2d, 0x46e0, 0x190d, 0xbfba, 0x18a6, 0xb240, 0x4309, 0x1378, 0x466f, 0x41e6, 0xb079, 0x188f, 0x4012, 0xba77, 0x082f, 0x4018, 0xba48, 0x1786, 0xbac1, 0x1734, 0xbf73, 0x1a6e, 0x4295, 0x1bc2, 0x1956, 0x4196, 0x0623, 0x3a43, 0x4197, 0xbf91, 0xb283, 0x4234, 0x0324, 0x4663, 0x4050, 0x4364, 0xb237, 0xb22e, 0x362c, 0xb21f, 0x2ab6, 0x3262, 0x1b32, 0xbf49, 0xaf0d, 0xb2d7, 0xba6d, 0x43e4, 0xb2a6, 0xb20d, 0x390c, 0xbfcf, 0x1a4a, 0xb2df, 0x447c, 0x4555, 0x437d, 0x412f, 0xbf90, 0xb0fe, 0x43bc, 0xb210, 0xb22b, 0xba63, 0xba2e, 0x4293, 0xb015, 0x0ba7, 0xbf92, 0x1956, 0x413e, 0x40db, 0xa728, 0x4360, 0x04c0, 0xb060, 0x41fa, 0x460d, 0x4168, 0xb24f, 0xaff6, 0x4284, 0x4018, 0x002e, 0x43ea, 0xbfb4, 0x3528, 0x40d5, 0xba2d, 0x4296, 0x1e62, 0x44d9, 0x189c, 0xb0c5, 0x4485, 0xb2b2, 0x42e6, 0x432f, 0x26d4, 0xa09d, 0x0530, 0xb0df, 0x42af, 0x4367, 0x1aff, 0x40c6, 0x2ceb, 0xba26, 0x4337, 0x4013, 0x42a1, 0xbf47, 0x1a0a, 0x435d, 0x4602, 0x26ee, 0x4313, 0x4128, 0x4135, 0x1ada, 0x1f63, 0x40be, 0x1a3d, 0xac4c, 0x40dd, 0x2cc1, 0x206b, 0x4224, 0xbf4e, 0xb2b9, 0x2ace, 0x1fba, 0xbad6, 0x42b6, 0xbf3e, 0xba6f, 0x41a1, 0xb0c5, 0x20d2, 0xb243, 0x1113, 0xba49, 0x04a8, 0xb253, 0xb0e5, 0xb27b, 0xba5e, 0x1f73, 0x4165, 0xb2e0, 0xb239, 0x4044, 0x431f, 0xb2ed, 0x047d, 0x2407, 0x464b, 0x1298, 0xbfc3, 0x4216, 0x1e84, 0x40c9, 0x4383, 0xb0de, 0x410b, 0xb03b, 0x41a5, 0x409b, 0x1186, 0xbfd2, 0x442c, 0xb0ed, 0x1b84, 0x06a9, 0x43d7, 0xb2ee, 0x4370, 0x420f, 0x41a9, 0x4281, 0xbf00, 0xbac5, 0xad27, 0x1f67, 0x4304, 0xbfe0, 0x4079, 0xba16, 0x425e, 0xbf2a, 0xb00d, 0xa749, 0x3c0b, 0xb00e, 0x19d6, 0xb264, 0x433f, 0x2049, 0x406c, 0xbf61, 0x420e, 0x4223, 0x41b5, 0x418e, 0x4032, 0x4012, 0x4379, 0x4323, 0x3185, 0x43ba, 0x4190, 0xb093, 0xafab, 0x4592, 0xb0b0, 0xbfae, 0x173a, 0x412d, 0x4273, 0x2c58, 0x183c, 0x4389, 0x420c, 0xb211, 0x4304, 0x43e2, 0x41eb, 0x335a, 0x431e, 0x1a7d, 0x4119, 0x12ff, 0xbf3a, 0x42c1, 0x1ea5, 0xb060, 0x4222, 0xb0d4, 0xa12d, 0x4217, 0x0a9a, 0xaa17, 0x4206, 0x445f, 0x4348, 0x20b4, 0xb209, 0xbf11, 0x4072, 0x4023, 0xbadf, 0x41f3, 0x43bf, 0xa351, 0xb266, 0x43a0, 0x1e38, 0x43a9, 0xb24b, 0x2ade, 0x4167, 0x3e49, 0x41f3, 0x4103, 0x0576, 0x433e, 0x42d4, 0xbf91, 0xb2f7, 0x45c9, 0x41b5, 0x4040, 0x3aff, 0xbac7, 0xb213, 0x4192, 0xbf00, 0xb205, 0x43e9, 0x4264, 0x1c65, 0x41e6, 0x45f4, 0xbf7b, 0x4290, 0x42dd, 0x1e56, 0xbadf, 0xb03a, 0xadad, 0x42f2, 0x404b, 0x4635, 0x1eaf, 0x46a5, 0xb25c, 0x2d7c, 0x4053, 0x4005, 0xb068, 0x42b6, 0x1a03, 0x4346, 0xa2f9, 0x402f, 0x40ed, 0xbf51, 0x4694, 0x23a6, 0x1a50, 0x29c4, 0x404b, 0x1283, 0x425c, 0x1de7, 0x40ad, 0xb205, 0x402f, 0x4399, 0x1cb5, 0x430e, 0xb2ba, 0x19d9, 0xbf1f, 0x43f4, 0x41df, 0x1cc0, 0x407d, 0x430f, 0xbf06, 0x43e0, 0xad43, 0x065c, 0x42ed, 0x260a, 0xba07, 0x4415, 0xb0c3, 0x419b, 0x180a, 0xb049, 0x38a3, 0xbf93, 0xb0f3, 0x1c0b, 0xb241, 0x417f, 0xbf9f, 0x428f, 0x39fb, 0x45e5, 0x407d, 0x4664, 0x4202, 0xbaf6, 0x43c1, 0x42a5, 0x409e, 0x4049, 0x0925, 0xadf3, 0x0581, 0x4043, 0x1a2c, 0x42ca, 0x1b73, 0xbfaa, 0xb248, 0x42bb, 0xb253, 0xb254, 0x4432, 0x43ac, 0xbaf8, 0xba1e, 0x27ea, 0xbf55, 0x17bc, 0x4193, 0x0a07, 0x198a, 0x41f9, 0xbf95, 0x4370, 0x2713, 0x3500, 0x4093, 0x41df, 0xba7c, 0x19ec, 0xb21f, 0x357a, 0x0226, 0x3727, 0x43db, 0xb0a4, 0x4017, 0xa52a, 0x4381, 0x40ae, 0xb258, 0x2d00, 0x2206, 0x4286, 0xa01c, 0x4452, 0x43b3, 0xaf31, 0x4208, 0x4342, 0x1cc8, 0x41e0, 0xbf88, 0x02a7, 0xb002, 0x394d, 0xb0f4, 0x43c2, 0x42f8, 0x413a, 0x411c, 0xbace, 0x3d27, 0x42d9, 0x1410, 0x3a6d, 0x4106, 0x027a, 0x4346, 0x408a, 0x43bb, 0x43c6, 0x41b2, 0xbf08, 0x1c77, 0x421b, 0x1322, 0x4425, 0xbace, 0x2ecb, 0x43ad, 0x4415, 0x414a, 0x420e, 0xba66, 0x41a3, 0x40eb, 0x3809, 0x2732, 0xb0b1, 0xb2dc, 0x3668, 0xbf89, 0x413a, 0xb03e, 0x3a5e, 0x41cd, 0xbf37, 0x4372, 0xb2db, 0xacdb, 0x086c, 0x42be, 0x4275, 0xbafd, 0x422b, 0x1bcc, 0x4272, 0x41c1, 0x0e46, 0x433d, 0xbfc1, 0x0ce0, 0xaaad, 0x1982, 0x42cb, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x46ce3787, 0x91b9dd82, 0x5c30a10f, 0x98b32b04, 0x61635373, 0xdfb71ffc, 0x105be440, 0x6bd3228d, 0x9979b985, 0xe4135b49, 0x78e8170d, 0x00193fd1, 0xf7f4c7a5, 0x0e660f37, 0x00000000, 0xd00001f0 }, FinalRegs = new uint[] { 0x00001fff, 0xfffecfff, 0x0000207e, 0xe413586f, 0xffffff81, 0x00003232, 0x0000007f, 0x00000032, 0x6bd3228c, 0xf7f4ed77, 0x00000000, 0xf7f4d893, 0x00001a68, 0x1beca537, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x40de, 0xbf27, 0x1dbc, 0x42ee, 0x414f, 0xab56, 0x4170, 0x4225, 0x1b8e, 0xa89e, 0x00b6, 0x3e79, 0x46d8, 0xb2dc, 0x410d, 0x4058, 0x4395, 0x08c6, 0x43ce, 0xb000, 0xb07f, 0xa5ad, 0xbf14, 0x1da0, 0xb283, 0xbf2e, 0xb0a5, 0x425c, 0xb0ff, 0x4097, 0x3cc7, 0xb2ea, 0x406a, 0x401a, 0x4189, 0x1d12, 0x4265, 0x4167, 0x409d, 0x19c6, 0x40ef, 0xba26, 0x43e0, 0xbf94, 0xbf80, 0x1afd, 0xb067, 0xbfa0, 0xb227, 0x18c1, 0x4584, 0x249c, 0x4037, 0x1d6e, 0x462f, 0x42c7, 0xbf0d, 0xaf08, 0x1c64, 0xba1d, 0x40b0, 0x41a4, 0xa66a, 0x41b1, 0x4164, 0x1108, 0xbfb0, 0x096a, 0xb25f, 0x41aa, 0x1af7, 0x28de, 0x44a1, 0xb261, 0x4084, 0x43bd, 0xbfd3, 0x423d, 0x2de7, 0x1d14, 0x4079, 0xa83f, 0x41fb, 0xb216, 0x0c50, 0x1b4f, 0x42ec, 0xb212, 0x4085, 0x4211, 0x42a1, 0x1fdd, 0x4288, 0xba4e, 0x1029, 0x1d39, 0x3e7c, 0x4175, 0x4065, 0xb045, 0x40a2, 0xa278, 0xba5b, 0xbfd0, 0xad9f, 0xafe7, 0xbfdd, 0x1c5c, 0xb055, 0x3b51, 0x420c, 0xa911, 0x45b2, 0x43f3, 0xbf14, 0x403f, 0x3b14, 0x406f, 0x43c3, 0x41f5, 0x0da8, 0x4083, 0x09b3, 0xb20d, 0xaa12, 0xb022, 0x4693, 0xbf87, 0xba59, 0x165c, 0xb299, 0x19e1, 0x410a, 0x072b, 0xb236, 0x427d, 0x4310, 0xb2b1, 0x43c9, 0xaf02, 0x1ff3, 0xbf27, 0xadf3, 0xaec5, 0x42ee, 0x426e, 0xbfb6, 0x4153, 0x143e, 0x4354, 0x3bde, 0x2571, 0xaaa9, 0xbfb2, 0x401a, 0x4014, 0x186d, 0x1b1d, 0x1d49, 0xb0ed, 0xbfe2, 0x0f4b, 0xb28e, 0x4353, 0x2d51, 0xb219, 0x2806, 0x4614, 0x4046, 0x0727, 0x439d, 0xb2d1, 0xa936, 0xb21c, 0x2643, 0x4338, 0x4115, 0x2663, 0x1fab, 0xb234, 0x4484, 0x46ad, 0x1de8, 0xb291, 0x4379, 0x3751, 0x4672, 0xb23e, 0xbfa3, 0x411f, 0xb217, 0xb0af, 0xba5f, 0xb287, 0xba1a, 0x40b7, 0x428f, 0x4179, 0xbaf8, 0x42ba, 0xb01d, 0xb216, 0x432e, 0x411c, 0x405c, 0x10dd, 0x4358, 0x4277, 0x1b1d, 0x1afd, 0x442f, 0x408e, 0xbf1a, 0xb2d1, 0x20f4, 0x42e0, 0x1865, 0x4360, 0x419b, 0xad85, 0xbf47, 0x42dc, 0x1c28, 0x099a, 0xb021, 0x42c6, 0x3001, 0x4204, 0xb04c, 0x43cc, 0x4011, 0x4383, 0x42c0, 0x42b8, 0x426c, 0x431e, 0x4073, 0xb0de, 0xbf7d, 0xbadf, 0x1b97, 0x44dd, 0xba7a, 0x43a2, 0x2527, 0x1e82, 0x17ea, 0x1ae3, 0x1f1f, 0x40ad, 0xaf9c, 0x44f8, 0xa34e, 0x19ef, 0x3993, 0xbf9a, 0xb246, 0xba3a, 0x2ae0, 0x40b1, 0x230a, 0x41c9, 0x41d8, 0x4612, 0x4217, 0xb2af, 0x0e46, 0x469b, 0x06c2, 0xbfbd, 0xb2d8, 0xb20c, 0xb278, 0xa4ee, 0x4220, 0x2bee, 0xbf03, 0x0713, 0x46a4, 0xb296, 0x45c9, 0x381e, 0x26f9, 0x2596, 0xb20d, 0x1855, 0x31ca, 0xabe7, 0x1e1f, 0xbf95, 0x42ec, 0x4335, 0xb261, 0xba75, 0xba1e, 0xb2e6, 0xb277, 0x41ff, 0x408f, 0x1968, 0x401a, 0xb03f, 0x02b7, 0xb000, 0x3dd6, 0x4165, 0x42c7, 0x4159, 0xbfa8, 0x433e, 0x0a1e, 0x42bf, 0x145b, 0xbaf9, 0x4327, 0x4355, 0xafcf, 0x2661, 0x3e86, 0x415c, 0xa1d9, 0xbae1, 0x1dba, 0xa6de, 0x3bcb, 0xbfd0, 0x1fe8, 0x0997, 0x4268, 0x42d9, 0x432c, 0x3238, 0x1bbb, 0x4076, 0xbfc7, 0x0e40, 0x078c, 0x42ad, 0x2739, 0x3255, 0xbf88, 0x4096, 0x1d9a, 0xb228, 0xbfc1, 0x4025, 0x4021, 0xb21a, 0x41ac, 0x410a, 0x40bb, 0x1b99, 0x22c4, 0xaf4c, 0x21f4, 0x40fc, 0xb0d0, 0xbf18, 0x1ce1, 0xba1c, 0x1d77, 0xb08c, 0xb2ca, 0x160e, 0x1660, 0x0400, 0x41c4, 0x4038, 0x40d4, 0x19d5, 0x40de, 0x4216, 0xb0f3, 0x05c6, 0xba08, 0x2094, 0x18df, 0x43a2, 0x0d66, 0x42b6, 0xbf5d, 0x4088, 0x4150, 0x4207, 0x1fc0, 0x434d, 0xba10, 0x404c, 0x43da, 0xbfd4, 0xa24f, 0xba3a, 0xba01, 0x0721, 0xba0d, 0x41eb, 0xb2d4, 0xb2f9, 0x40d5, 0x423a, 0x430f, 0x41d1, 0x19f0, 0x179f, 0x42a6, 0x4325, 0x37cd, 0x1943, 0x43a7, 0xbf38, 0x3469, 0xbf55, 0xb024, 0x415e, 0xb24e, 0xb042, 0xb29f, 0xb257, 0x43d5, 0x421c, 0x017e, 0x33bd, 0x3636, 0x4102, 0x40b7, 0x4562, 0xb092, 0x42dd, 0xbac5, 0x4352, 0xbf64, 0x4608, 0xa1f5, 0x1810, 0xb080, 0xa0f4, 0x4373, 0x40bb, 0x43f3, 0x42bb, 0x4372, 0x0653, 0x41c3, 0x4205, 0xbafa, 0x0977, 0xbaff, 0xb2c9, 0x43e7, 0xba3e, 0x443d, 0x4390, 0xbf80, 0x409d, 0x42ae, 0x1d3a, 0xb0dd, 0xbfa7, 0xba14, 0x43c7, 0x4064, 0xab20, 0x41a3, 0xb22f, 0x41f3, 0x4338, 0x4088, 0xa77f, 0x3c19, 0x4212, 0x41a2, 0x407b, 0x0a0e, 0x41df, 0x1fba, 0xba60, 0x1432, 0x1fdf, 0x4082, 0x1b87, 0x2588, 0x430b, 0xaf0a, 0xbf44, 0xba6c, 0xbace, 0x16c6, 0x1fb1, 0xa6d9, 0x1b20, 0x4246, 0x2523, 0x00c0, 0x43b2, 0x4612, 0xb0a6, 0x4180, 0x46bb, 0x42da, 0x430f, 0xb2ac, 0xbafe, 0x4655, 0xb2d3, 0xba32, 0xbfb8, 0x4259, 0x1f8c, 0x40d1, 0x415f, 0x404d, 0x4418, 0x40db, 0x43d8, 0x08aa, 0x1e2b, 0x1ef5, 0x41fe, 0x4346, 0x4269, 0xadc3, 0xb2f5, 0x42ab, 0xb0c9, 0xb034, 0x28d6, 0x4276, 0xba16, 0x4294, 0xad99, 0x418f, 0x06b5, 0xbfba, 0x427c, 0x4542, 0xb2fa, 0xa6ac, 0x418d, 0x4564, 0xb04e, 0xbf80, 0x3973, 0x3f72, 0x1830, 0xbfd2, 0xacbf, 0xbaf1, 0x4546, 0x203c, 0x1a47, 0x28e1, 0xbf2e, 0xb235, 0x1eae, 0x4195, 0x1476, 0x43da, 0x4450, 0x428b, 0xb22d, 0xbfd0, 0x1805, 0xba04, 0x1a83, 0x297b, 0x403a, 0x1fca, 0xbaf8, 0x40b6, 0x43c5, 0x3560, 0x43f8, 0x416d, 0xba13, 0x4329, 0xa727, 0x159f, 0x4212, 0xbad7, 0x4085, 0xbfb5, 0x33d8, 0x4388, 0xb280, 0xba44, 0x41cd, 0x1f18, 0x40ae, 0x437a, 0xb081, 0x4340, 0x4163, 0x1a20, 0xbad1, 0xbf03, 0x4390, 0x41d0, 0x40c1, 0x1ff4, 0xb299, 0xbfd0, 0x1bc9, 0xbad6, 0x16cb, 0x4062, 0x1c0c, 0x41df, 0x401b, 0x40e3, 0x4103, 0xa549, 0xbfb0, 0x3b0b, 0x4405, 0x40ca, 0xb290, 0x4100, 0x433c, 0x424b, 0x2d35, 0x46eb, 0xbf70, 0x40ae, 0xbf9d, 0x425c, 0x41c5, 0x4168, 0x434a, 0xbfd6, 0x25f5, 0x43b9, 0x3278, 0x432d, 0x3711, 0x181a, 0x1b7e, 0xba70, 0x437d, 0xb2ed, 0x259f, 0x19f0, 0x1c30, 0x1b3b, 0x43df, 0x443a, 0xb29a, 0x4091, 0xad24, 0xbfaa, 0x419b, 0x25e8, 0x20d2, 0xb2f9, 0xba50, 0x42ef, 0x42b5, 0x368e, 0x3a76, 0xb240, 0x1e37, 0xbafb, 0x42ac, 0x1a73, 0x2848, 0xb0a9, 0x4656, 0x4138, 0xbad8, 0x4115, 0x42ba, 0x425b, 0x414f, 0x430f, 0xb2f1, 0x1ec6, 0xbf37, 0x4171, 0x41a4, 0x41bd, 0x1911, 0xb268, 0x44e9, 0xac0d, 0x3861, 0xb2cf, 0x425a, 0x0c98, 0x1799, 0x3eca, 0x1076, 0xba36, 0x35c9, 0x1a07, 0x4131, 0x4312, 0xbf14, 0x41df, 0x40fc, 0x1d3f, 0x424b, 0x4332, 0x4186, 0xbfc2, 0x42cb, 0x4202, 0xb202, 0xb2d1, 0x1331, 0x2071, 0xb2a9, 0x41ea, 0x4129, 0xab06, 0x4396, 0x41aa, 0x1d8b, 0xba52, 0xb23f, 0x07dd, 0xa183, 0x19f1, 0x05cf, 0x1809, 0x434c, 0xbf84, 0x4008, 0x4286, 0x4289, 0x0abd, 0x20ef, 0xb2ed, 0x1a86, 0x43f6, 0xbadc, 0x4257, 0x3cd6, 0xb207, 0x43ab, 0xbf90, 0xb2d2, 0x4208, 0x405f, 0x1bad, 0x4315, 0x0b32, 0xbf74, 0xbfd0, 0x3ed1, 0x4074, 0x423c, 0x40fd, 0x441c, 0xa2af, 0x2616, 0x2ca8, 0x11e7, 0x438b, 0x43d2, 0x046c, 0xbfbd, 0x18f0, 0x1c25, 0x42fc, 0x43c8, 0x03e7, 0x409d, 0x4277, 0x46f3, 0x4162, 0xbf14, 0xa46b, 0xaf5f, 0x16cb, 0x422b, 0xabe0, 0x0653, 0x0cb8, 0xb2ca, 0xb286, 0x1b6d, 0x4134, 0x215e, 0x437f, 0x4049, 0x41c9, 0x1b35, 0x4214, 0x4048, 0xbf42, 0x43ca, 0xb2ac, 0x43de, 0xa5f1, 0x42b0, 0xb075, 0x413f, 0xba3c, 0x41f2, 0x1d5b, 0x1b0f, 0x3cad, 0x4175, 0x43df, 0xbfd0, 0xb081, 0x0b73, 0x4330, 0xb079, 0x41ec, 0xb232, 0x4621, 0xb06e, 0x400f, 0xa68f, 0x2c56, 0x42f3, 0x1a96, 0xbfab, 0x418c, 0x43b6, 0x4360, 0x4310, 0x1012, 0x4315, 0x42c4, 0x1df4, 0x41ed, 0x2de9, 0x4277, 0x46c8, 0x411e, 0xbf9a, 0xba26, 0xbfd0, 0x4229, 0x1deb, 0x42b0, 0xb245, 0x40bc, 0x426e, 0x42af, 0xb2a0, 0x427c, 0x4262, 0x43ae, 0x4648, 0x4346, 0x40a4, 0x4135, 0xba30, 0x1a4b, 0x437f, 0x402b, 0x4064, 0x2f49, 0x4699, 0xa05f, 0xbaeb, 0x08a5, 0xbf32, 0xba5a, 0x19b5, 0x1807, 0x43d6, 0x4243, 0x4548, 0x015d, 0x09fa, 0x4161, 0x40c3, 0x1528, 0x22e7, 0x43e1, 0x40c1, 0x42f5, 0x4219, 0x1719, 0x4391, 0xba42, 0x1aa8, 0x42de, 0xba05, 0x40e1, 0x42b8, 0x46e2, 0xb07e, 0xbf16, 0x435c, 0x4255, 0x42a5, 0x414d, 0x0353, 0x423e, 0xb0ac, 0xaf6a, 0xbf6d, 0x423a, 0x4303, 0x42b4, 0x42ff, 0xbf09, 0x4043, 0x4052, 0x42cb, 0x439d, 0x3cae, 0x1752, 0xbff0, 0x4120, 0x1b16, 0x42cd, 0xb0bd, 0x4109, 0xbf2b, 0xba63, 0x415b, 0x1f93, 0xbaff, 0xb25c, 0x4327, 0xb0ea, 0x456f, 0xb280, 0x41da, 0x2a24, 0x184d, 0x420f, 0x400b, 0x42d8, 0xb200, 0xb270, 0xb050, 0xba5b, 0xb20b, 0x1eb7, 0x432e, 0xbfd1, 0x4367, 0x2e30, 0xb225, 0x43d0, 0x4211, 0xb2e7, 0x40b4, 0x41b3, 0xba16, 0x409e, 0x1452, 0x28cd, 0x4342, 0x335d, 0x25d5, 0x1fcb, 0x0ab1, 0x4179, 0x40a2, 0xb213, 0x4113, 0x298a, 0x415c, 0x4209, 0x42dd, 0x41c1, 0xb0f2, 0x423a, 0x4021, 0xbf8c, 0x0529, 0xa789, 0xbae0, 0xb217, 0x4195, 0x42dd, 0x424a, 0x40dc, 0x429b, 0x18b2, 0xb228, 0x4288, 0x171a, 0x438a, 0xb2a5, 0x4453, 0xb04f, 0x0047, 0xb27d, 0xbfbf, 0x3cd5, 0xb294, 0x1550, 0x402f, 0xb027, 0x1e0f, 0x43b7, 0x42bb, 0xbf9c, 0xa295, 0x43c2, 0xb2b3, 0xaec7, 0x4168, 0x43bd, 0xb09b, 0xbae4, 0x40dc, 0x4351, 0x4266, 0x0b06, 0xb24c, 0x18ee, 0x41b7, 0xbf7c, 0x43fc, 0x420c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x1c45d6ba, 0x53e984f2, 0x3a0df554, 0xdcee8efa, 0x8d61a15b, 0x42a11c1a, 0xe46650af, 0x77ca9117, 0x8878f892, 0x1c01e917, 0x9521508e, 0x1cb29f47, 0xca2a8d7c, 0xe5798906, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x0000007d, 0x00000000, 0x00000000, 0x00000000, 0xffffffa8, 0xffffffa8, 0xffffffa8, 0x00000057, 0x017b7293, 0x00000000, 0xca2a8d7b, 0x00000000, 0xca2a8d7b, 0xe5798e79, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1ed2, 0xb283, 0x434a, 0x3937, 0x4179, 0x4253, 0x4017, 0x023f, 0x42bc, 0x408d, 0xb22a, 0x4268, 0xba5b, 0x1a31, 0x40a2, 0x1dd9, 0x2161, 0x4449, 0x4000, 0x402e, 0x41ce, 0xbf83, 0xba1e, 0x44b0, 0xba18, 0x425e, 0xa764, 0xbfc0, 0x406f, 0x40bd, 0xb0b7, 0xa99c, 0x4005, 0xa0c2, 0x41a4, 0x3ff8, 0x402c, 0x036d, 0xbf57, 0x3e68, 0x4153, 0xb0e0, 0x4689, 0x42e0, 0x406f, 0x40c8, 0xb0b1, 0x4593, 0x40f8, 0xb2d1, 0xa877, 0x4379, 0x434e, 0xba1e, 0xbf71, 0xba71, 0xb2b1, 0xb0c1, 0xb232, 0x41fc, 0x463f, 0x4069, 0xb25b, 0x4241, 0xb2c0, 0x405a, 0x4048, 0xba32, 0x4258, 0x41ef, 0xbf78, 0x0754, 0xb277, 0xbf60, 0x4089, 0xba4a, 0x4074, 0xb252, 0xb2cb, 0x4134, 0x434c, 0x43e1, 0x1e9c, 0x1c02, 0x40a7, 0xb24e, 0x1e02, 0xbf17, 0x3511, 0xb2a3, 0xba19, 0xaccd, 0x4091, 0x1fc1, 0xb057, 0x43b8, 0xb2c8, 0x4180, 0x377e, 0x43d3, 0x09e2, 0x4266, 0xb26c, 0xbf1f, 0x4260, 0xb24e, 0xb24a, 0xb2ac, 0xbfd0, 0xba7a, 0x45b2, 0x4038, 0x418f, 0x15f9, 0xba63, 0xba2d, 0x42f3, 0x403e, 0x4038, 0x41c9, 0x4152, 0x2ee7, 0x4259, 0x0226, 0xbf41, 0x21d2, 0x4430, 0x4007, 0x17ed, 0xbfbd, 0x4058, 0x3e16, 0x407a, 0x40e3, 0x41a0, 0x43d8, 0x404a, 0x40f2, 0x1605, 0x42a3, 0x4631, 0x44f0, 0xbfe0, 0xa21c, 0x290b, 0xaade, 0x40aa, 0x43ae, 0x4126, 0x29c9, 0x420d, 0xbfa4, 0xba6b, 0x41cc, 0x2a49, 0xa316, 0x4308, 0xb04c, 0x4261, 0x0a3b, 0x27f8, 0x422b, 0x02f7, 0x401f, 0x4084, 0xa4c4, 0x1d89, 0xaf46, 0x4159, 0xbad0, 0xb287, 0x41f4, 0x0ae8, 0x423b, 0x3f2b, 0xb210, 0x41bf, 0xbfa5, 0x4333, 0x447e, 0x4403, 0xb24d, 0xbf80, 0x4201, 0x40ff, 0xb2da, 0x41df, 0xb2fa, 0x4257, 0x4366, 0x43e5, 0xbfba, 0x431d, 0x4558, 0xb296, 0xba51, 0xbacf, 0x4313, 0xbadd, 0x40e6, 0x25fe, 0x4223, 0xb2e4, 0x400a, 0x362d, 0x40bd, 0x1e5c, 0x4010, 0xa3e8, 0x4695, 0x22de, 0x3070, 0x1c9a, 0x2d98, 0xb0b1, 0x1f41, 0xb2e6, 0x415c, 0xbac2, 0xbf69, 0x3816, 0x1f3a, 0x1f46, 0xb06c, 0x10f7, 0x1ee5, 0x400e, 0xbf08, 0x2a02, 0x179e, 0xadb5, 0x42f2, 0x410d, 0x4258, 0xb24d, 0x4424, 0xbaf2, 0x4143, 0x40e8, 0x4339, 0xb231, 0x437a, 0xb2f1, 0x42fd, 0x1dff, 0xbf91, 0x43ed, 0x468c, 0x427d, 0xb211, 0x4388, 0x4101, 0xba6d, 0x3708, 0x19fc, 0xb2fe, 0x410f, 0x1fdc, 0x2d26, 0x0b81, 0xb23e, 0x46b2, 0x4194, 0xacb8, 0x40ec, 0x3012, 0xbfb0, 0x45b1, 0xb2b7, 0x1ea0, 0xb246, 0x4007, 0x23e5, 0xbf92, 0x426f, 0x1eab, 0x4371, 0x0c6a, 0xb21d, 0x4442, 0x1954, 0x1957, 0xba05, 0xb27f, 0xb00c, 0x437f, 0xb022, 0x4223, 0x1d0e, 0xbf97, 0xbaf4, 0x1c45, 0xba0e, 0x469d, 0x4049, 0x2a26, 0xb2f0, 0x4272, 0x441a, 0x465b, 0xb2b7, 0x1885, 0x45ca, 0x43b9, 0x1b66, 0x42fe, 0x467e, 0x3529, 0x36da, 0x43fb, 0x1fea, 0x4249, 0x281b, 0xbf81, 0x3ce3, 0xb25a, 0x4119, 0x41d7, 0x1ba6, 0x426c, 0x41ad, 0x4041, 0x41af, 0x44b2, 0x41c6, 0xbf86, 0x41b6, 0xac9f, 0x2b28, 0x4001, 0xba03, 0xbfb0, 0x40b8, 0x3572, 0xba6e, 0x19d2, 0xbf22, 0xacf7, 0xab27, 0x2775, 0x40d1, 0x438f, 0x4214, 0x17c4, 0x40f8, 0x42ce, 0xa942, 0x3854, 0x44cd, 0x1cf3, 0xbfb8, 0x1a6f, 0x1817, 0x2f34, 0x42f3, 0xaa11, 0xba6c, 0x4560, 0x2208, 0x43ac, 0x45bb, 0x45f5, 0x09eb, 0xbf06, 0xb251, 0x1def, 0xbff0, 0x0c9f, 0x40e4, 0x4453, 0x1aab, 0x4365, 0xb2e1, 0x46a4, 0x4000, 0x12f4, 0x3204, 0x4360, 0x4350, 0x4185, 0x41ba, 0x3fca, 0x31d6, 0xa211, 0x42eb, 0x42a0, 0xadb7, 0xbaec, 0x41d4, 0x409c, 0xbaf5, 0xbfa1, 0x15b0, 0x4234, 0x4345, 0x4215, 0x43fc, 0xba60, 0xb034, 0x25ec, 0x4108, 0xb250, 0x2343, 0x44aa, 0xb2ed, 0x4234, 0x46ba, 0x420a, 0x0fcf, 0xbf2e, 0xb0aa, 0xa5a5, 0xb0e2, 0x41d3, 0xb292, 0x1d9c, 0xbf75, 0x190f, 0x2337, 0xb245, 0x080c, 0xb2cf, 0xa08b, 0x40ce, 0x40c3, 0x4121, 0x192b, 0x327a, 0x4341, 0x0fb8, 0xbfe0, 0xb2a8, 0x26ea, 0x3b79, 0x41eb, 0x18ea, 0xb28a, 0x3412, 0xbff0, 0xbf2d, 0x43a0, 0xaba3, 0x1b78, 0x43f6, 0x406c, 0x43ad, 0x3e65, 0xbac1, 0x45ae, 0x3dce, 0x42da, 0x43c0, 0x42d3, 0x412a, 0xbfc7, 0x0784, 0x41db, 0xb288, 0x209d, 0x117f, 0x404f, 0x2dca, 0x2fa2, 0x0b73, 0x414d, 0x406e, 0xbad2, 0x431b, 0xbafd, 0x466b, 0x429e, 0x4198, 0xb2cb, 0xb2e6, 0xbf9e, 0x404a, 0xb2ba, 0xb096, 0xbac1, 0xba3b, 0x037e, 0x40fa, 0xb26c, 0x416c, 0xb2cb, 0xb014, 0x14dc, 0x412c, 0x2d78, 0x1d2e, 0x199d, 0xb2e8, 0x2adc, 0x426c, 0x1e46, 0x4305, 0x1c4a, 0x43a9, 0x1c02, 0x43e3, 0x466d, 0xbf6a, 0x3376, 0x11a5, 0x37df, 0x416c, 0x1bb3, 0xb27b, 0x4385, 0x0a41, 0x1dd5, 0xb2c3, 0x409e, 0x4112, 0x1cb0, 0x428d, 0x0dfe, 0x4391, 0xbf90, 0xba2b, 0xb2a9, 0x4159, 0x426e, 0x2534, 0x4260, 0xbafc, 0xb294, 0x4332, 0x3fed, 0xbfa9, 0x42a7, 0x422b, 0x1f0f, 0xbaf5, 0x21f3, 0xba2f, 0x15fb, 0xb2fa, 0xb025, 0x4024, 0x4014, 0x3c14, 0xbfac, 0x3734, 0xba6e, 0x435b, 0xba12, 0x2012, 0x4058, 0x423e, 0xabd2, 0x3645, 0x40c3, 0x20d0, 0x1d9d, 0x43c2, 0x41b3, 0x4693, 0x40b4, 0x2296, 0x409b, 0xb291, 0x437a, 0x424b, 0xbaf7, 0x4104, 0x437f, 0xbf96, 0x4102, 0x1886, 0x0fb9, 0x426e, 0x40b8, 0xb20e, 0x431d, 0x4291, 0x4291, 0x0c05, 0xb2d4, 0x199c, 0x428f, 0x44db, 0x413d, 0xbf31, 0xb289, 0x43f0, 0x332d, 0x4569, 0x4299, 0xb014, 0xba7d, 0xb0f3, 0x402e, 0x4045, 0x06a4, 0x43ca, 0x4094, 0x31bd, 0x4081, 0x427b, 0xb20d, 0xb2e6, 0x4632, 0x162d, 0x41c4, 0x2895, 0x40e7, 0x4653, 0xa661, 0xbf66, 0xbf90, 0x42b2, 0xaab5, 0xb090, 0x1583, 0x4639, 0xb28d, 0xb270, 0x40de, 0xbf82, 0xb222, 0x4270, 0x435a, 0x0de4, 0xb092, 0x40a8, 0x429e, 0x3997, 0x414e, 0x4399, 0x21a7, 0xb0e8, 0x4137, 0x46c0, 0x2806, 0x4172, 0x421d, 0x4322, 0x412e, 0x423d, 0x4127, 0x135d, 0x40da, 0x422f, 0x1a75, 0x40f4, 0xbaf0, 0xbf18, 0xba57, 0x0d36, 0xb2bb, 0x0ca6, 0x42fd, 0x425b, 0xbfe2, 0x4373, 0xbfc0, 0xa74d, 0xbfb6, 0xb262, 0xba79, 0x1e98, 0xa253, 0x0db2, 0x4160, 0x29eb, 0x0771, 0x4360, 0x1f61, 0xb2ac, 0x4196, 0x433c, 0x412b, 0x4054, 0x4197, 0xb04b, 0x240f, 0xb2fb, 0x4277, 0x4292, 0xb0a4, 0x1b9f, 0x4007, 0xb01d, 0xbf3d, 0xbfe0, 0x45e0, 0x4297, 0x420b, 0x4659, 0x4051, 0x4012, 0xbf78, 0x468a, 0x4083, 0x2755, 0x42be, 0x4699, 0x40fc, 0xb0e4, 0xaead, 0x458e, 0xa47e, 0x43cd, 0xac1b, 0x4401, 0x4182, 0x45f1, 0xbad0, 0x277d, 0x4580, 0x43f0, 0x1e04, 0x4001, 0x41f1, 0x42a9, 0xbf31, 0x2018, 0x435d, 0xb097, 0x0bdd, 0x449c, 0x4553, 0x1052, 0x41e1, 0x42ef, 0x43a3, 0x04ac, 0xae49, 0x4346, 0x1de8, 0x1ce6, 0x44a3, 0x432c, 0x4646, 0xb240, 0xb29f, 0x1d48, 0x436d, 0xbf36, 0x06c9, 0xbfa0, 0xb231, 0xbf52, 0x1d57, 0x40df, 0x40de, 0xb23c, 0x39bb, 0x2829, 0x4313, 0xa2f6, 0xb003, 0x446e, 0x1c7c, 0x3a4f, 0x007b, 0x427e, 0x083a, 0x1d84, 0xb088, 0x4176, 0x433b, 0x439c, 0xbf05, 0x44c2, 0x42ae, 0x4211, 0xa9ac, 0x1a23, 0xb20a, 0x46e4, 0xa4cc, 0xba2b, 0x4655, 0x2b20, 0x4070, 0xbfc0, 0x40ff, 0x4071, 0x1e59, 0x43a5, 0xbfdf, 0x1c22, 0x4315, 0x462d, 0x4183, 0x4560, 0x43b1, 0x1dc0, 0x4089, 0x17b2, 0x416c, 0x4345, 0x4302, 0xbf1d, 0x42a9, 0xbaf4, 0x40ea, 0x418d, 0xbad4, 0x1f28, 0x4035, 0x4255, 0x2404, 0x43a6, 0x4167, 0xba28, 0x2c13, 0x1a6a, 0x1bb3, 0x4186, 0x430e, 0x4556, 0x1a01, 0xbacc, 0xba0c, 0x410c, 0xb2e3, 0xbf1a, 0x1fc7, 0x4229, 0x462a, 0x4295, 0x1a63, 0xbf00, 0xbf70, 0xb26c, 0x1360, 0xbf95, 0x4151, 0x1132, 0x432b, 0x42d3, 0x0fb4, 0xba25, 0x1505, 0xbfde, 0x4338, 0x41dc, 0x4223, 0x3524, 0x422f, 0x4104, 0x414f, 0x3667, 0x1993, 0x4174, 0x40c2, 0xba13, 0xba2b, 0x289e, 0xba20, 0x406e, 0x20ae, 0xbacb, 0xa364, 0x40d6, 0xbfb8, 0x404f, 0x4398, 0xae33, 0x4023, 0x4333, 0x40c9, 0x43e8, 0x1b35, 0xb0c3, 0xbafb, 0x08c2, 0x4139, 0x42b3, 0xb089, 0xb242, 0x4129, 0xb008, 0xbf47, 0x2a1c, 0xb092, 0x2c58, 0x43c8, 0x41ca, 0x40c9, 0x32e6, 0x4182, 0x1769, 0x363c, 0x1639, 0x4231, 0xb286, 0x4075, 0xbf7f, 0x424c, 0x0094, 0x1c3d, 0x43a3, 0x40b6, 0x41ca, 0x435a, 0x3614, 0x378c, 0xb213, 0xbf85, 0x1839, 0xbae4, 0x1dd2, 0x414c, 0x4040, 0x41d3, 0x4040, 0xb254, 0x4110, 0x1e0a, 0x42e6, 0x095a, 0x0a7b, 0xbf46, 0x1375, 0xba1e, 0x4224, 0x41ee, 0xa734, 0x40dd, 0x42ee, 0x2906, 0x397f, 0xba5b, 0x41eb, 0xa422, 0x3dd1, 0x1dfc, 0x4444, 0x1c7b, 0x4068, 0x4258, 0x1350, 0xb2a6, 0x4155, 0x4213, 0x43ab, 0xbf37, 0xa368, 0x0c7a, 0x4175, 0x1cf7, 0x4260, 0x04fe, 0x18dc, 0xba1c, 0xa450, 0x2a7c, 0x351b, 0x416f, 0xbf55, 0x3b8b, 0x401f, 0x4282, 0xa41b, 0x41e1, 0xa299, 0xa6f3, 0xaca9, 0x1c37, 0x0145, 0x4285, 0xb269, 0x438b, 0x138c, 0x1eb7, 0x1d6e, 0xa51c, 0xb2d2, 0xae4b, 0xb2fc, 0xac6e, 0x4176, 0x1937, 0x42f2, 0x4293, 0xbf48, 0x4280, 0x4685, 0x421f, 0x4394, 0x050c, 0xbfc4, 0x372a, 0x401d, 0xbae7, 0x42f6, 0x40b6, 0xa1e8, 0x44aa, 0x3def, 0x429c, 0x3117, 0x1dba, 0x1a58, 0x415a, 0xa67a, 0x43b3, 0xbf21, 0xbf90, 0xa973, 0xba48, 0xba58, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2222d05c, 0xcfc689d4, 0xc2140172, 0x47506221, 0xe5f77550, 0x38359c85, 0x4e95a963, 0xe20bac00, 0x9f1922b1, 0x5e07b7dc, 0x32b31e2b, 0x9b89a1e5, 0x70e8b6e8, 0xae4936fc, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0xffffe4fa, 0x00001b77, 0x00000077, 0x00000041, 0xf8000000, 0x00001719, 0x000019b8, 0x00000000, 0x9f1922b1, 0x00000057, 0x00001666, 0xfffffe5e, 0x00007157, 0x60e6c544, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x439f, 0x404c, 0x454a, 0x41ed, 0xbf7a, 0x1535, 0x428d, 0x1c71, 0x4125, 0xb281, 0x40a4, 0xb25a, 0x40f7, 0xaab2, 0xb256, 0xb236, 0xb08c, 0x4180, 0x1249, 0x3e60, 0x235c, 0x40f0, 0xba68, 0xbaf7, 0x45e3, 0x4314, 0x411a, 0xb2f1, 0xbf35, 0x4689, 0xb26e, 0xba71, 0x4072, 0x1cd9, 0x4451, 0x4343, 0x4275, 0x1acd, 0x3026, 0x43af, 0x4279, 0xb2fb, 0xb24f, 0x41c1, 0x4101, 0x40d8, 0x4171, 0x436e, 0xb092, 0xbf9b, 0x4190, 0x40ab, 0x1bbc, 0x1ad7, 0x45a3, 0x412f, 0x1374, 0x45c8, 0x1e94, 0x40c4, 0xb0d8, 0x41c8, 0x4044, 0x1d6f, 0x4134, 0x45d8, 0x426b, 0x404c, 0x1986, 0x401c, 0xb2aa, 0x4375, 0x423e, 0x1ac8, 0xbfb3, 0xb296, 0xba27, 0x3e43, 0x4315, 0xbfb0, 0x1914, 0x43f8, 0x42a1, 0x426a, 0x0cfa, 0x4027, 0x4270, 0xadfb, 0x45dc, 0x309c, 0x407d, 0xa010, 0x1d78, 0x34b3, 0x1b29, 0xb2c0, 0x463a, 0x196a, 0x41b1, 0x42c5, 0x40a5, 0x454d, 0xb28f, 0xbf37, 0xba22, 0x1c18, 0x2832, 0x41fe, 0x1a1b, 0x1dd0, 0x4634, 0x4058, 0x2da1, 0x427b, 0xb2c7, 0x4280, 0x1e11, 0x40a9, 0xba2d, 0xba19, 0xbf44, 0xba11, 0x4068, 0x180e, 0x4178, 0x41ee, 0x4651, 0x407e, 0x4353, 0x4245, 0xbae4, 0xa4d0, 0x0dd5, 0xb27b, 0x1be0, 0x4062, 0xb220, 0x4352, 0x40e3, 0x41c9, 0xb21f, 0xbf00, 0xbf03, 0x29da, 0x40c0, 0x4179, 0x1f95, 0x187d, 0x43d8, 0x1ad8, 0xb223, 0x4648, 0x3fb2, 0x2ec5, 0xba50, 0x42c2, 0x3b0b, 0x1fbf, 0xb240, 0x41e0, 0x3a20, 0x032c, 0xb21a, 0xa161, 0x42bb, 0xbf26, 0xbf80, 0x446c, 0x4305, 0xb068, 0xba5f, 0x130a, 0x2736, 0xabf8, 0x1967, 0x2038, 0xb260, 0xba40, 0x4241, 0xaf27, 0x4381, 0x40af, 0x438c, 0x437c, 0x4672, 0xbae3, 0x3e4c, 0xb228, 0x1cc4, 0x432e, 0xbf3a, 0x1c2b, 0x1678, 0xa23f, 0x1dd4, 0x42a9, 0xbff0, 0x397e, 0x42a1, 0xbf54, 0x41a6, 0x4124, 0xb2f5, 0x1d50, 0x42a9, 0xba79, 0xba33, 0xa80d, 0xbf60, 0x4329, 0x4161, 0x3a12, 0x40ed, 0x4562, 0x4303, 0x1853, 0xbf6b, 0x4379, 0x4307, 0x436e, 0x41b0, 0xba44, 0x4178, 0xbf90, 0xb2db, 0xa99c, 0x4671, 0x46a9, 0x44b3, 0x40fc, 0xab9e, 0xb21d, 0x42a7, 0x40f3, 0xb2cd, 0x24b3, 0x41b7, 0x1db8, 0xbf4c, 0x4207, 0xba4b, 0x4227, 0xb064, 0x405a, 0x416a, 0x1222, 0xb0e1, 0x0ff4, 0xba13, 0xbf8a, 0x423f, 0x440b, 0x4007, 0xbadf, 0xb25b, 0xae38, 0x229d, 0xbfcb, 0x3f66, 0x259e, 0xb092, 0x4248, 0x422f, 0x18b3, 0xbfd2, 0x147a, 0x43ce, 0xac74, 0xa9c0, 0x454c, 0xba1f, 0x0865, 0x40bf, 0xb291, 0xb241, 0x4311, 0x1e25, 0xb2e5, 0x3038, 0x4647, 0x4254, 0x4007, 0x4276, 0x42cb, 0x4024, 0x1ee1, 0x206a, 0x0c99, 0x3aea, 0xbfc8, 0xbf60, 0x1eea, 0x410e, 0x4365, 0x03ee, 0x1d17, 0xb2e5, 0x4244, 0x0fc2, 0x1bf2, 0x1b74, 0xbac0, 0x1770, 0xbfcd, 0x02d1, 0x4251, 0x4200, 0xb2c5, 0xbafb, 0x43b8, 0xbf87, 0x425a, 0xbfd0, 0x18bb, 0x4186, 0xb0de, 0xba16, 0x199f, 0x444c, 0x4265, 0xa487, 0x41cf, 0x40e4, 0xb093, 0xbfa0, 0x4164, 0x0082, 0x433b, 0x40a3, 0x1957, 0x0653, 0xba10, 0x1011, 0x43cc, 0xb08c, 0xb24a, 0x2f88, 0xbfe4, 0xb291, 0x4251, 0xb0ec, 0xba67, 0x3fec, 0x401c, 0x2df3, 0xb225, 0x1ed0, 0x22cf, 0x41c4, 0xbfdc, 0xaa7d, 0x1e25, 0x40a5, 0x43a0, 0x41bf, 0xba39, 0x40cc, 0x4117, 0x46b9, 0x407b, 0xb0d5, 0x1cfb, 0x43ed, 0xb2ce, 0x4229, 0x2a4a, 0x1317, 0x41f3, 0x1a08, 0x1aae, 0x1737, 0xbf8a, 0x1ee8, 0x430e, 0x401b, 0x419c, 0x4121, 0xba1d, 0x109a, 0x18d9, 0x1dc3, 0x4331, 0xbac3, 0x00f7, 0x4178, 0x41c8, 0xa2e7, 0xb2be, 0x18bc, 0xb288, 0x1527, 0x43e5, 0x1c45, 0x2f37, 0x423d, 0xba43, 0x4238, 0xbf75, 0x22e5, 0xbfb0, 0x4073, 0x34ad, 0xa802, 0xb084, 0x4154, 0x4333, 0xa7e3, 0x4170, 0x43d9, 0xbfa0, 0x43c6, 0x43d0, 0xb292, 0xa2aa, 0x42e5, 0xbace, 0x42a1, 0x4361, 0x42f9, 0xbf4c, 0x423e, 0x0e80, 0xb233, 0xb2c7, 0xa44f, 0x1a00, 0xbf00, 0x1d47, 0xb265, 0xb203, 0x4330, 0x4268, 0x40c9, 0xb299, 0xba4d, 0xb23a, 0x2d3b, 0xba5e, 0xbf00, 0x43cb, 0xbf28, 0x1595, 0x43e5, 0x0dd2, 0xb287, 0x429c, 0x4683, 0xbfe0, 0x410c, 0x4363, 0x117d, 0x0706, 0x377b, 0xbfa4, 0xb2e7, 0x42e5, 0x45e1, 0x441b, 0x42bf, 0xb08c, 0x427e, 0xb05a, 0x09e5, 0xbf69, 0x438c, 0xbafe, 0x45f6, 0xaad2, 0x0016, 0xbf0b, 0x419d, 0x4024, 0x4146, 0xb0b5, 0x461d, 0x41f8, 0xb044, 0x1cc0, 0xbae5, 0xb086, 0x44cc, 0x420f, 0xb0b0, 0x4679, 0x4009, 0x1a55, 0x404c, 0x41cb, 0x4035, 0x4313, 0x4085, 0x404f, 0x000e, 0x1544, 0xada3, 0xbf09, 0x2288, 0xb215, 0x40ff, 0x426a, 0x40a6, 0xb26e, 0x305e, 0xb206, 0x3b04, 0xa30a, 0xba3a, 0x459e, 0xba24, 0x418b, 0x40ad, 0x440a, 0x435d, 0xb262, 0x42ad, 0xb0d9, 0xb08c, 0xbf5c, 0x4351, 0xb2d0, 0xbf12, 0x1b96, 0xb27a, 0x4154, 0xa009, 0x1c94, 0xa32e, 0x1c08, 0x38a7, 0x43af, 0x418b, 0x43c7, 0x296c, 0x273c, 0x4276, 0x25d3, 0x42c3, 0xba0c, 0xb284, 0xbf19, 0xb2ea, 0xba0e, 0x4069, 0xb003, 0x469c, 0xb0ee, 0xb219, 0x43cb, 0x424b, 0x41f6, 0x3973, 0xb271, 0x401c, 0x23a6, 0x40ea, 0xba16, 0x194a, 0xa210, 0x1c5b, 0xb07a, 0xbfa1, 0x4271, 0x190b, 0xba0c, 0x0783, 0xa0dc, 0x46d0, 0xbf54, 0x42fc, 0x0ea4, 0x1cde, 0x0a0a, 0x4115, 0xb2b7, 0xb2fa, 0xbf2f, 0x436e, 0x4374, 0xba73, 0x43cd, 0x4460, 0xad56, 0xb2cd, 0x4157, 0x40c1, 0x1a23, 0x4011, 0x42de, 0x42ab, 0x41f5, 0x4299, 0x43d1, 0x36ef, 0x4135, 0xade3, 0xbfcc, 0x1d9e, 0x425d, 0x43d0, 0xb26e, 0xb26d, 0xb0fe, 0xbaf7, 0x4106, 0x43d0, 0x4311, 0xbfdc, 0x2ac1, 0xb048, 0x434f, 0xb2b4, 0x12bc, 0x1ff4, 0x1aa2, 0xbad4, 0x428d, 0xb28c, 0x44e3, 0x4323, 0x407e, 0x350d, 0x4600, 0x4304, 0x40b5, 0x4253, 0xb2d9, 0xbfd7, 0xb062, 0xb0bd, 0x4567, 0x465d, 0xba1f, 0x0580, 0x46da, 0xb073, 0x3249, 0xb2fc, 0x41a4, 0x4235, 0x2daa, 0xb097, 0xbf5a, 0x41aa, 0x44c3, 0x42c0, 0x0536, 0xb239, 0x42c5, 0xbf6e, 0x19da, 0x296c, 0x324a, 0x395f, 0x40c6, 0x40fe, 0x4023, 0x00f5, 0x281c, 0x2646, 0x3352, 0xb2b7, 0xb2fb, 0xb27b, 0x44a0, 0x41ee, 0xbf81, 0xa1c3, 0x43e2, 0x4047, 0x43c2, 0xba7c, 0x2eae, 0xb281, 0x1807, 0x23c0, 0x1b92, 0x4116, 0xa4a6, 0x43f3, 0x4333, 0x1ae6, 0x25ec, 0x373d, 0xbfc3, 0xafa6, 0x409b, 0x1ffa, 0x183a, 0x46d5, 0x42e4, 0x418e, 0x422e, 0x43ad, 0xad75, 0x18e8, 0x4087, 0x0fb4, 0x0dc6, 0x1913, 0xabed, 0x4331, 0x40fa, 0xbf95, 0x18ab, 0x1aa0, 0x33e6, 0x4354, 0x4338, 0xb2cf, 0x4683, 0x4289, 0x42a1, 0x1ed3, 0x43c5, 0x434e, 0xb2bc, 0xa1bd, 0x4098, 0x418a, 0x25fe, 0x4348, 0x27b1, 0xa5ce, 0x121a, 0x4229, 0xbfa4, 0xb2d8, 0xb290, 0xbfdd, 0xa03c, 0x4217, 0x41b7, 0x41c0, 0xb0e8, 0x19ee, 0x41ea, 0x4294, 0x418d, 0x4075, 0xb04c, 0xbfce, 0xb083, 0x3289, 0x40a7, 0x429d, 0x4277, 0x454a, 0x43a4, 0x1f9a, 0xb2cf, 0x40a6, 0x42b7, 0xb277, 0x27de, 0x43cd, 0xba20, 0xb255, 0x4301, 0x43de, 0x41f0, 0xba42, 0x40b3, 0x05ae, 0xbafb, 0xbaf1, 0x443b, 0x4022, 0xbf68, 0xb286, 0xb2f3, 0xac85, 0x4129, 0x435d, 0x4344, 0x42c3, 0x41d8, 0xb247, 0xbf65, 0x42b9, 0x0ff6, 0x412c, 0xb2e8, 0x4406, 0x42e1, 0x3032, 0x416c, 0xb298, 0xab81, 0xb090, 0x434d, 0xbf87, 0x1cb0, 0x404c, 0xaf0e, 0x33b1, 0x3a35, 0x1c47, 0x43e5, 0xbacc, 0x323d, 0x336a, 0x4125, 0xb062, 0xba38, 0x429b, 0x4173, 0xbf95, 0x40bd, 0x164b, 0xb232, 0xb292, 0x4283, 0xa0d8, 0xb0a5, 0x197f, 0x43d2, 0x1b4f, 0x092b, 0x40d5, 0x1abc, 0x468d, 0xb0ac, 0xb289, 0xb231, 0x1f75, 0xba2c, 0x3a4e, 0x429d, 0x3a08, 0x414a, 0x400f, 0xbfc5, 0x42db, 0x4041, 0x4204, 0xb225, 0x022c, 0x41a9, 0x1def, 0x1cf5, 0x4217, 0x4047, 0x0cb3, 0x3896, 0x4383, 0x28ec, 0x4036, 0x434c, 0x424d, 0x392c, 0x0f8f, 0x2d25, 0x1ca0, 0x40d1, 0x4273, 0x4365, 0x41ce, 0xbf27, 0xb22d, 0x1762, 0x430b, 0xb23a, 0x419f, 0x40f7, 0x421c, 0x44f9, 0x41fa, 0x43d0, 0x428a, 0xb010, 0xb0b8, 0x402a, 0x40ba, 0xbf65, 0x3a3b, 0x4205, 0x41d3, 0xb2df, 0xb019, 0x422b, 0x4207, 0xba0a, 0x430c, 0x18fd, 0xbaf8, 0x4162, 0x400e, 0x4099, 0x40bd, 0x268f, 0xbaee, 0x42ed, 0x1a55, 0x42a5, 0x19bf, 0xb229, 0xb2de, 0x4169, 0xba79, 0x0f16, 0xb2d5, 0x4193, 0xbf2f, 0x0861, 0x4315, 0x05f0, 0xba76, 0xb2e6, 0xbf75, 0x0d63, 0x4580, 0x41dc, 0x4196, 0xbad8, 0x042b, 0x1a1c, 0x1e88, 0xba71, 0x19b1, 0x08a3, 0x314e, 0xba39, 0x40f9, 0x23e3, 0xb0e9, 0x11cb, 0x41b8, 0xbf1c, 0x1a4f, 0xa122, 0x416d, 0xb045, 0xb05f, 0x456c, 0x2fbd, 0x1eae, 0x43ef, 0x1264, 0x41be, 0x2531, 0x4240, 0xbf8c, 0x40fa, 0xb2ef, 0xaa96, 0x4398, 0x40fe, 0xb2e5, 0x4331, 0x419d, 0xba00, 0x4618, 0x4219, 0x408f, 0x438c, 0xb2a7, 0x41f6, 0xb272, 0xab3a, 0x1bd0, 0x2ece, 0x40ff, 0x1f10, 0xbf34, 0x2253, 0x40a3, 0x426e, 0x4206, 0x4262, 0x4354, 0x2793, 0xae78, 0x427a, 0x4370, 0x1911, 0x387b, 0x46e4, 0x18dc, 0x4259, 0x4013, 0xad55, 0x443c, 0x4231, 0xb015, 0x42d5, 0xa0d6, 0x41b8, 0xbf92, 0x4689, 0x4305, 0x3f97, 0x4356, 0xba74, 0x40a2, 0x43b8, 0x46bc, 0x4002, 0x0f62, 0xbacf, 0xbadb, 0x4562, 0xbf3c, 0xbfd0, 0x1eb6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xb6e12d7d, 0x2bc8f2a0, 0xe074d4cb, 0x76b33514, 0x92975e84, 0xc4f82ea7, 0xeb560f6d, 0x9507ee5e, 0xd48a048b, 0x1aa04244, 0xe9639443, 0xe7dafbbf, 0x050a37ec, 0x91c1b356, 0x00000000, 0xd00001f0 }, FinalRegs = new uint[] { 0x00000001, 0xffffff18, 0x00000007, 0x00006800, 0xfeff60ec, 0x00001b7d, 0xfffeec5e, 0x000018ff, 0xe9639443, 0x000016c5, 0x00001547, 0xf400171a, 0xfffffffc, 0x00000054, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb29c, 0xba51, 0x43ec, 0x4103, 0x372e, 0x2b03, 0x42b6, 0x41ad, 0xbac4, 0x463c, 0x2969, 0x24f2, 0x2801, 0x4274, 0xb048, 0x438e, 0x3129, 0x41e5, 0xb223, 0x40f8, 0xb004, 0xbac2, 0xba3e, 0x1aa2, 0xbf3b, 0x427c, 0x458c, 0x4062, 0x1d1d, 0x4184, 0x16db, 0x4429, 0x1dae, 0x4326, 0xbacf, 0x404a, 0xb2cc, 0xbf0a, 0xac02, 0x2e4d, 0x29e9, 0x298b, 0x30b9, 0xb0c0, 0xbf43, 0x1d40, 0x0fd8, 0xb21a, 0x2ed1, 0xb23d, 0x1fc5, 0x4639, 0x4308, 0x41f1, 0xba0c, 0x0fb2, 0xbff0, 0xbfd4, 0x40c3, 0xba36, 0x42d3, 0xba79, 0x42f6, 0x44c1, 0x414d, 0x430a, 0x4163, 0xbf0d, 0x3df5, 0x4390, 0xb280, 0xa4a3, 0x1918, 0x4075, 0x40f7, 0x4254, 0x431b, 0x40f2, 0x4272, 0xb252, 0x43c5, 0xb21f, 0xba24, 0xbfaa, 0x4096, 0xbac4, 0x4469, 0xbf28, 0xb098, 0xb24b, 0x42b8, 0x3c87, 0xb0bf, 0xba48, 0xa742, 0x41ed, 0xba57, 0xbff0, 0x42dd, 0x43f2, 0xb2e8, 0x1f6e, 0x2f4d, 0xbfc8, 0xafdb, 0xbaf9, 0x4313, 0x2895, 0xb06b, 0x180c, 0xb247, 0x1a0e, 0x4002, 0xb2be, 0x3206, 0x43e3, 0x40ec, 0x4609, 0xbf00, 0x410e, 0x4328, 0x45c5, 0xbf21, 0x169f, 0x1a33, 0xa416, 0xb2af, 0x42d6, 0xbfa4, 0x439f, 0xacea, 0x40c3, 0x43d7, 0x4184, 0x1ea5, 0xbade, 0x41b2, 0xbf96, 0xbac9, 0x1703, 0x18d6, 0xbf60, 0x1cb9, 0x2c88, 0x3e09, 0xba5f, 0x3f42, 0xb2d1, 0xb2a6, 0x42cf, 0xb2be, 0x2c25, 0x4315, 0xba76, 0xb029, 0x41fe, 0x437c, 0x4186, 0x41ce, 0x1adf, 0xba71, 0xb0d3, 0xbf76, 0x4399, 0x42b3, 0x418d, 0x4258, 0x1fe3, 0x4150, 0x4595, 0x1b75, 0x4255, 0x40d4, 0x42ee, 0x4207, 0xba40, 0x41c3, 0x030d, 0x2a47, 0x1f8f, 0x40a9, 0xb0c1, 0xbfae, 0x4080, 0x4358, 0x4005, 0xbfe2, 0x1724, 0xa1be, 0x183c, 0x408e, 0x3b25, 0xb255, 0x4222, 0xb2e2, 0x1244, 0xb0bd, 0xa6fa, 0x1fe0, 0xb05f, 0xb237, 0x07aa, 0xbaef, 0xa553, 0x3698, 0x41c0, 0x41a0, 0x4354, 0xbfcc, 0x46d0, 0xb0fd, 0x1374, 0x42ec, 0x4077, 0xa062, 0x1e82, 0x40fe, 0xb227, 0x172b, 0xbfa3, 0x0541, 0xb093, 0xba2e, 0x4359, 0x3053, 0x4163, 0x4190, 0x01d8, 0x434b, 0x420a, 0x0cdf, 0x1b4d, 0x4045, 0x09df, 0xb268, 0x1d2c, 0xb008, 0xbfe2, 0x4342, 0x4138, 0x2a50, 0x4197, 0x43d7, 0xbafc, 0x443a, 0xa855, 0xb00a, 0x43b9, 0xbaf2, 0xbf4d, 0x1f75, 0x42d8, 0x431a, 0x407c, 0xbac1, 0x42b1, 0x08e3, 0x4217, 0x2206, 0x436d, 0x429d, 0x43d8, 0xb061, 0x4280, 0x4164, 0x422c, 0xbfd8, 0x405c, 0x1ae9, 0x1cce, 0x4346, 0xb079, 0x2ad0, 0xafb0, 0x10f5, 0x430d, 0x41ad, 0xbfe2, 0x0a34, 0x0894, 0x1b3e, 0x429b, 0x46a8, 0xb2e6, 0x40cb, 0x3e07, 0x40ea, 0x2248, 0x4096, 0xba45, 0x43b5, 0x40c4, 0x40c0, 0x1a05, 0x4044, 0x4303, 0x42ab, 0x15f7, 0x4679, 0x4321, 0x447e, 0x429b, 0xbfc2, 0x1d66, 0x42f6, 0xb079, 0x24c9, 0x189d, 0xbad0, 0x20cb, 0x43d4, 0x1572, 0x4352, 0x2b09, 0xb2b0, 0x0800, 0xba78, 0x1e22, 0x1973, 0xb27e, 0xb2d0, 0xb21c, 0x3d33, 0x2ea5, 0x0470, 0x419e, 0x066e, 0xbadb, 0x43a1, 0x412f, 0xbf52, 0xba60, 0xb2a5, 0xba7f, 0xb019, 0x4117, 0x43cf, 0x1f1f, 0x42ca, 0x40ab, 0xa704, 0x18b4, 0x3160, 0x435a, 0xb2da, 0xbaee, 0x252b, 0xbf94, 0x374f, 0xb0e2, 0x3612, 0xbf90, 0x1c26, 0xa209, 0x4173, 0x4119, 0x4375, 0xb09a, 0x2b4a, 0x41d0, 0x404e, 0x4337, 0xa05b, 0xbad3, 0x3b56, 0x1b76, 0xba1d, 0xb217, 0x41de, 0xbf1a, 0x1e6f, 0xb234, 0xbae1, 0x45c5, 0x2c47, 0x41e8, 0x1fe5, 0x42bb, 0xbacb, 0xb007, 0x43e8, 0x423a, 0x3f88, 0x0af3, 0x1cbb, 0x408f, 0x427d, 0x403e, 0xb253, 0x43b4, 0x436a, 0xb252, 0x2ae0, 0x3ae8, 0x435e, 0xb06f, 0xbf84, 0x34ff, 0x1acd, 0xba06, 0x22dc, 0x4022, 0x4343, 0x45f4, 0x4361, 0x18d9, 0x40fc, 0xb024, 0x4208, 0x1820, 0x4391, 0x41ea, 0x37a3, 0x46c8, 0xbf60, 0xb04f, 0x41dd, 0x0cfa, 0x0e15, 0xa479, 0xbac6, 0xbf54, 0x46c2, 0x288a, 0x182e, 0xba13, 0x1f09, 0x40dd, 0xa3e4, 0x426a, 0xb27d, 0x2af6, 0xbafe, 0x41e7, 0xba35, 0xb07c, 0x050a, 0xb2e1, 0xabb5, 0x1423, 0x3493, 0x40be, 0x2644, 0x1da5, 0x1bae, 0x1e6c, 0x41c0, 0x44c9, 0xbfc3, 0x40c4, 0x21e1, 0xaeb3, 0x1fdc, 0xb254, 0x418a, 0xbf0b, 0xb224, 0xae41, 0x42e5, 0x42f6, 0x2d3f, 0x4137, 0xb093, 0x4105, 0x420d, 0x40ac, 0xb210, 0x42dd, 0x2bf7, 0x43bc, 0x417d, 0xb2f3, 0xac37, 0x4031, 0xb2cc, 0x405b, 0x415f, 0x23b1, 0xb215, 0xba5e, 0xa2d0, 0x4415, 0x2582, 0xbf1c, 0x408a, 0x42a5, 0x4118, 0xb292, 0x413e, 0x4472, 0x406b, 0xb2c3, 0x3639, 0x40e3, 0xb25e, 0xbfd0, 0x40f2, 0x1fd9, 0xbfb0, 0x1e92, 0x1e5f, 0x40df, 0x4665, 0xbf37, 0x4479, 0x1602, 0x1a33, 0x437a, 0x2900, 0x4411, 0xbf00, 0xbf60, 0x4108, 0x41ef, 0xb2d3, 0x42f9, 0x38a6, 0x43f4, 0xb2d5, 0x292a, 0xba42, 0x2eb4, 0xbae0, 0xa21b, 0x1a60, 0xbf8a, 0xb239, 0x1dea, 0x1be6, 0x4042, 0x18b4, 0x4217, 0x0c3d, 0x1826, 0x41d1, 0xba1c, 0x36b3, 0xa241, 0x43f6, 0x4435, 0x425e, 0x3c53, 0x2281, 0x25fe, 0x409d, 0x42c6, 0xba3b, 0x43ba, 0xbf86, 0xba54, 0x2522, 0x43df, 0xbfd7, 0x18f1, 0x4308, 0x1fb2, 0x429d, 0xb010, 0x4445, 0x4169, 0x4054, 0x2abe, 0xa0b6, 0x3317, 0x1f53, 0x42d4, 0x4272, 0x46ab, 0x2008, 0x1d36, 0xbf73, 0x42d6, 0xb238, 0x425f, 0x411c, 0x4098, 0xb044, 0xb23a, 0x420d, 0x1f38, 0xb20e, 0x0203, 0xacf3, 0xb2e9, 0x4055, 0x1667, 0x42a1, 0x4073, 0x3006, 0x4229, 0x41e2, 0x4630, 0xb2e0, 0xa326, 0x4146, 0x1d00, 0xbf35, 0xb2d1, 0x14a1, 0xb2e9, 0x31e8, 0x0f16, 0x41ec, 0x4245, 0x1a7a, 0x421a, 0x2429, 0x4017, 0x0882, 0x3395, 0x00f1, 0xbad4, 0x42b0, 0x43d9, 0x293c, 0xbf98, 0x1938, 0x40a9, 0xb26c, 0x1bf5, 0x1a28, 0x4016, 0x432b, 0xb21e, 0x43e9, 0x4457, 0x417a, 0x41b0, 0x4052, 0xbae8, 0xa883, 0xb266, 0xba3a, 0x431e, 0x1b96, 0x4238, 0x43ac, 0xbfc0, 0x4157, 0xbf04, 0xaa02, 0x42a0, 0xba3b, 0x4114, 0x4145, 0x3e46, 0x0cc6, 0x1885, 0x4102, 0x43ba, 0x430e, 0xb2d1, 0xb286, 0x3084, 0x01e7, 0xa8bb, 0x42d1, 0xba6c, 0x402b, 0x427c, 0x4279, 0xbf61, 0x4177, 0x40f1, 0x424e, 0x01c2, 0x11e7, 0x369e, 0x413e, 0x1a54, 0xb090, 0x1844, 0x43c1, 0x42dc, 0x4038, 0xbadb, 0xbf1c, 0x4345, 0xbaf7, 0x4103, 0xb297, 0xb030, 0xba42, 0xac35, 0x4078, 0xba2a, 0xbfb9, 0x416f, 0xba16, 0xa940, 0x41f8, 0xbf65, 0x43c9, 0x45a4, 0xb2fe, 0x4020, 0xba2b, 0x400e, 0xb282, 0x40ec, 0x1afb, 0x4584, 0xbf70, 0x3323, 0x41c6, 0x1bfb, 0xb288, 0xbfac, 0xba01, 0xa344, 0x3658, 0xba58, 0x408e, 0x1efe, 0xb0bf, 0x119d, 0x0af0, 0x4194, 0x4162, 0x419b, 0x4311, 0xb2f7, 0x4312, 0x411a, 0xbf70, 0x405d, 0x405d, 0x1b4b, 0xbad8, 0x416e, 0x4327, 0xbf63, 0x4311, 0x1fcc, 0x41f8, 0x2cae, 0x41d4, 0x422b, 0xbf6b, 0x4260, 0x43cb, 0x1cd2, 0xa5a3, 0xbfb0, 0xb030, 0xbfac, 0xbfc0, 0x4148, 0x4264, 0x3326, 0x41cb, 0x4132, 0x2736, 0xb012, 0x11c0, 0x3453, 0xbfa9, 0x1e76, 0xb0f2, 0xb2b5, 0xba0e, 0x09f7, 0x1b71, 0x46a5, 0x415a, 0xb29f, 0x4493, 0xb0f9, 0x3943, 0x317b, 0x44e4, 0x438c, 0xbf0b, 0x4144, 0x07a1, 0x4392, 0xadf3, 0xaf15, 0x1422, 0x212f, 0xa165, 0x44db, 0x070c, 0x4381, 0xb0e2, 0x1f2b, 0x40a3, 0x4647, 0x3bf5, 0x1b02, 0x42c9, 0x2996, 0x43a7, 0x0d6b, 0x46eb, 0xb0e6, 0x4160, 0x41be, 0xb053, 0xb083, 0x1e27, 0xbf2a, 0x22b5, 0xbaf4, 0x08fb, 0x4556, 0x464b, 0x4333, 0x1337, 0x1a48, 0xb29e, 0x4167, 0x41ee, 0xb2d8, 0xbfa9, 0x1cc6, 0x4351, 0xb28b, 0x4308, 0x43f7, 0x432e, 0x1f58, 0x43b0, 0x4057, 0x41e8, 0x411c, 0x22d8, 0x3135, 0xba7c, 0x43a2, 0x2df5, 0x42cb, 0xb2d6, 0x441d, 0xb2ef, 0x2d14, 0x436b, 0xaed5, 0x322f, 0x1da6, 0xb08e, 0xbf2e, 0xb271, 0x33f0, 0x33be, 0x424c, 0x1be6, 0x4319, 0x2dc9, 0xb06b, 0x1f95, 0x3107, 0xb2ca, 0x439b, 0x1965, 0x2ed1, 0x45ba, 0x2066, 0xbf64, 0x1aca, 0x411b, 0xae5e, 0xadca, 0xb0cf, 0xba28, 0xbf85, 0xb278, 0x41c5, 0xb287, 0xb26e, 0xa4a7, 0x1aeb, 0xba16, 0x41b8, 0x3a44, 0xbf80, 0x42e4, 0x0580, 0x3e82, 0xb2f4, 0x1afe, 0xb244, 0xb25b, 0xbafb, 0x1961, 0x41f9, 0xbfb0, 0xba5f, 0x0ba4, 0x40d9, 0xb21b, 0xbf29, 0x01dd, 0xa878, 0xbfd0, 0xb2ad, 0x1ba2, 0x1c18, 0xaf6a, 0x39a8, 0xbfc0, 0x3400, 0x40b3, 0x192a, 0x4128, 0x3f37, 0x42c0, 0x14c8, 0x42d4, 0x402d, 0x21a6, 0xb25b, 0x4045, 0x184f, 0x406f, 0xb0de, 0x4046, 0xbf8a, 0xba50, 0xaae3, 0x2719, 0xb2d4, 0xb22b, 0x35ad, 0x40fb, 0x0e62, 0x43ac, 0x439a, 0x0f5d, 0x4063, 0x1ca1, 0x40a4, 0x406a, 0x18fb, 0x27b5, 0x409a, 0xbf6b, 0x43c7, 0x293d, 0x074b, 0x336f, 0x456f, 0x41e1, 0x40a7, 0x2b00, 0x3c88, 0x4616, 0x1f8d, 0xb2b5, 0x129a, 0x4224, 0x407e, 0xbf53, 0x3fae, 0xbacf, 0x4313, 0x46ec, 0x1a1c, 0x4268, 0xb07b, 0x433e, 0x4407, 0x4189, 0x45ce, 0x428c, 0xb068, 0x413f, 0xbfac, 0xb200, 0xbae2, 0x1f79, 0x4236, 0x417c, 0xa561, 0xbaf0, 0xb2f6, 0x1290, 0xb243, 0xba63, 0x40c8, 0xbfa0, 0xafcf, 0x43f2, 0x1bdd, 0x42bf, 0xbfa3, 0x42c0, 0xba51, 0x1c8c, 0xba37, 0xb239, 0x4008, 0xb2e7, 0xb290, 0xbaee, 0x431d, 0x421f, 0x4364, 0x437c, 0x01b9, 0x43ad, 0x4544, 0x2c61, 0x4640, 0xa354, 0x2ad2, 0x2d4b, 0x4052, 0xbf2f, 0xaff9, 0xaa46, 0xae6b, 0x0c27, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x60ab798e, 0xeb64755b, 0x32fad41e, 0x906517a9, 0x6b2d648d, 0x9c5a9d43, 0xefba7776, 0x297a6da6, 0x3750e535, 0xf070ec03, 0x5dff78cd, 0x69746ba1, 0xc40e9ad7, 0xa94af44d, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x27c1d138, 0x00000040, 0x00000374, 0x0000192c, 0x82cf9201, 0x00000000, 0x00000408, 0x000082cf, 0x27c1d138, 0x4f83a270, 0x27c1d138, 0x00000068, 0xfffffed0, 0x0000025c, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb234, 0x4215, 0x2ab0, 0x43d9, 0x42a0, 0x432b, 0x0bb3, 0x191b, 0x086d, 0x4251, 0x42d3, 0xba6f, 0xa2e4, 0x4069, 0x42ec, 0xae3e, 0xb293, 0xbf16, 0xa0fb, 0x433a, 0x4366, 0x45a5, 0x41d4, 0xb2ca, 0x1ed4, 0xb06d, 0x2a1c, 0x1fbf, 0xbaf6, 0xb266, 0xb293, 0x1c35, 0x3693, 0x2df7, 0xb031, 0x1c23, 0xad6c, 0x36e1, 0x4116, 0xbfc0, 0x32a2, 0xba37, 0x455d, 0x40e1, 0x4047, 0xbf01, 0xbaf8, 0xbad3, 0x19a9, 0x4255, 0xb074, 0x4360, 0xba2b, 0x1e38, 0x18c5, 0x44ed, 0x44e5, 0x187b, 0xb22a, 0x40a3, 0x1852, 0xb276, 0x40dc, 0x42dc, 0x2cf9, 0xb041, 0x18c1, 0xba29, 0xbff0, 0xa748, 0x42ab, 0xbf7d, 0x253d, 0x4352, 0x0c85, 0x438f, 0x4274, 0x3cf4, 0xafce, 0x1974, 0xb074, 0xbfa6, 0x4257, 0xba5b, 0x4037, 0xa515, 0xb0bd, 0x04fe, 0x1cf8, 0x420c, 0x4119, 0x4184, 0x4312, 0x45d3, 0x41ef, 0x4011, 0xb00a, 0xb027, 0xb0f9, 0x41bc, 0x4087, 0x41bd, 0xa595, 0x4126, 0x45d8, 0xbfbd, 0x2aaa, 0xb22b, 0x4361, 0xb2ad, 0x02e9, 0x43f5, 0xb21b, 0x41c2, 0x0d25, 0x03aa, 0x3663, 0x4404, 0x45ce, 0xbf8a, 0x21db, 0x4362, 0x1af0, 0xbf68, 0x415b, 0x4223, 0x2828, 0x4075, 0xbf42, 0xb262, 0xacd1, 0xb011, 0x1da5, 0x43fc, 0x408c, 0x43b3, 0x43c5, 0xb0c7, 0xa5b2, 0xbf17, 0x409d, 0xbac7, 0x4440, 0x0ba9, 0x4025, 0x2022, 0xa67c, 0x4214, 0x40e4, 0xba71, 0xb28c, 0x40ea, 0x41cf, 0x41e2, 0xbfbf, 0xb2bc, 0x3e8c, 0x40eb, 0xb2d4, 0xa13f, 0x4311, 0xbf52, 0x19f8, 0xbf70, 0xb2f4, 0xb06c, 0x0ab2, 0x0a8e, 0x40ae, 0xba23, 0x1c9c, 0x4211, 0x17ac, 0x1c16, 0x1235, 0xab09, 0x1a59, 0x41f7, 0x46a3, 0x1b42, 0x416e, 0x0f01, 0x0b26, 0x407f, 0x4228, 0x0d9f, 0x43c2, 0x435d, 0xbf22, 0x4270, 0x1893, 0x4246, 0xbf06, 0x30d0, 0xba13, 0xae81, 0x0378, 0x4088, 0x463e, 0x4101, 0x4243, 0x1ddd, 0x41bf, 0xba17, 0xae45, 0x1c8e, 0xb014, 0xa8ef, 0x4167, 0x4207, 0x40c6, 0xb018, 0xa950, 0x4073, 0x049b, 0x41a2, 0x45c5, 0x424b, 0x439a, 0x1a0a, 0x3231, 0xbf32, 0xb20b, 0x433b, 0xba2b, 0xbf5d, 0x430c, 0xb2f5, 0x4131, 0x43e4, 0x1e5f, 0x42e8, 0x1f98, 0x115d, 0x4268, 0x41eb, 0x05f8, 0x28e7, 0x06ea, 0xb002, 0xb2d0, 0x1cac, 0x43ca, 0x0c9b, 0x425b, 0xbfe0, 0x40d1, 0x42a1, 0x3faf, 0x403d, 0xb011, 0xb204, 0x4204, 0x4274, 0xbf7f, 0x0588, 0xa465, 0xbadf, 0x1860, 0x0720, 0x4115, 0xbaf0, 0x417e, 0x4569, 0xb211, 0x40a2, 0x43d8, 0x38ea, 0x4061, 0x42df, 0xb2d6, 0x415e, 0x432c, 0xba38, 0x4353, 0x4352, 0x45a5, 0xbf05, 0x4078, 0x3f2a, 0xba41, 0x44e8, 0xb209, 0x1da4, 0xb2c8, 0x41e8, 0xbf29, 0xbafe, 0x1a67, 0x1829, 0x1bf3, 0x0647, 0xb2c6, 0x0c9f, 0x41f5, 0x11dc, 0xbf87, 0x4124, 0xbacc, 0x424a, 0x2a70, 0x403e, 0xb25a, 0x1d42, 0xa62a, 0x4097, 0x43d5, 0xbfa0, 0x3791, 0xba00, 0x1644, 0xba57, 0xb215, 0x4251, 0x07ef, 0xbf4a, 0x2bd0, 0x2e96, 0x4355, 0x4393, 0x4376, 0xbf59, 0x411a, 0x400e, 0xba18, 0x43f5, 0xbf65, 0x4054, 0xae5d, 0xb2e8, 0x4230, 0xa533, 0x4134, 0xb2d9, 0x4149, 0x3cee, 0xbf80, 0x40a1, 0x1745, 0x1b9c, 0x1b90, 0x41be, 0x4468, 0xab8c, 0x4618, 0x1d05, 0x1c91, 0x259f, 0x4228, 0x414b, 0x4028, 0xbfd9, 0x4298, 0x4139, 0x1f8a, 0x4355, 0x4302, 0x42f9, 0x1951, 0xbf9c, 0xba05, 0x400c, 0xacba, 0x44b1, 0xb216, 0x4151, 0x2013, 0x40df, 0x3de1, 0x1f4a, 0x4036, 0x401e, 0x42a0, 0x42cd, 0x40dd, 0x428c, 0xbfd2, 0x4012, 0x1b19, 0x4099, 0x437f, 0x0289, 0x40ef, 0x1f98, 0x418f, 0x19e0, 0x40c5, 0xb2ea, 0x4366, 0x46ea, 0x427a, 0x4464, 0x4262, 0x4280, 0xbafa, 0xbf6f, 0x21d0, 0x1e60, 0x0a92, 0x0f02, 0x2122, 0x434c, 0x40c6, 0xba0c, 0xbf64, 0xa3fa, 0x10b4, 0xbac4, 0x41f5, 0x4079, 0x4197, 0x226a, 0xb2bf, 0x209e, 0x4085, 0xb07d, 0x437c, 0x3671, 0x2d5b, 0x4090, 0x44d0, 0xbf3b, 0x4259, 0xbaf9, 0x4329, 0x418d, 0x4292, 0x4485, 0x4694, 0x4059, 0xb21e, 0x41e6, 0xad2c, 0x3caf, 0xbf0b, 0xb29f, 0x4133, 0x4366, 0x04e2, 0xba2f, 0x1f7a, 0x41a6, 0x0e62, 0xb2f1, 0x43a3, 0xb09c, 0x443d, 0x025f, 0x4128, 0x1fb1, 0x1dea, 0x3733, 0xbfc5, 0x4263, 0xbad3, 0x42f0, 0xb238, 0xba0d, 0x3c1f, 0x4206, 0x41bb, 0x430a, 0x43ad, 0x1984, 0x4303, 0x0cb0, 0xba11, 0x1c1f, 0xb2df, 0xba43, 0x415b, 0xbfa4, 0xbf90, 0x1c9d, 0x1d4a, 0xab5e, 0x46f0, 0xb22e, 0xba16, 0x09f6, 0xba6a, 0x408e, 0x4380, 0xac19, 0x439f, 0x43d2, 0xbfa0, 0x4258, 0xa366, 0x4051, 0xbf36, 0x438a, 0x429f, 0x43f3, 0xb2c5, 0x3bd3, 0x1fa1, 0xb23c, 0x443d, 0xba27, 0xb089, 0x4354, 0x0f0f, 0x1da0, 0x4384, 0x4228, 0xb201, 0xbfe2, 0x4227, 0x395f, 0x447c, 0x43b0, 0x3a46, 0x1ba2, 0xbf80, 0x1c7c, 0xb200, 0x42e2, 0xba2b, 0xb279, 0x43e7, 0xb275, 0x3d99, 0x23a0, 0x41c6, 0x020a, 0x456b, 0x41ea, 0xb0dc, 0x466d, 0x3dec, 0xa2f3, 0x0a1d, 0xbfcc, 0x2a39, 0x215f, 0xbade, 0xb2a5, 0xb22a, 0x406c, 0x4093, 0x33ea, 0x117d, 0xabbf, 0x4291, 0x4055, 0x425e, 0x43e6, 0x4202, 0x4383, 0xbf3e, 0xae2d, 0x4165, 0x404b, 0x45e2, 0xbf60, 0xb0c6, 0x405b, 0xbff0, 0x0ed3, 0xb256, 0x4305, 0x02d6, 0x10a9, 0xac5f, 0xba55, 0xad0d, 0x1b53, 0x42c5, 0x40b6, 0xb211, 0x40cc, 0x1d30, 0x4098, 0x1df1, 0xbf66, 0x4553, 0x41af, 0x0cff, 0x4307, 0x40fa, 0x318f, 0x1b3e, 0xb2f6, 0x442e, 0xb229, 0x0b45, 0xb2de, 0x1948, 0xba36, 0x1a5b, 0x40ae, 0x0dc0, 0x4316, 0x1e5a, 0x258e, 0x4299, 0x1e58, 0x42b1, 0x4322, 0xb058, 0xbf0b, 0x080d, 0xba66, 0x4326, 0x096f, 0x4366, 0x42eb, 0x1b82, 0x43af, 0x4032, 0xb20e, 0x42a5, 0x1ed5, 0xa3f5, 0x410f, 0xba5d, 0x4120, 0xbf5f, 0x2aa9, 0xba1b, 0x0051, 0x40ad, 0x04a1, 0x420e, 0x3c42, 0x42e0, 0x42bb, 0x43d1, 0x447f, 0xbf14, 0x0ca2, 0xba56, 0x42ed, 0x4372, 0x2ffe, 0x42bd, 0x411e, 0x400a, 0x4285, 0x4652, 0xb079, 0x40ee, 0x0a3a, 0x11c7, 0x403f, 0x14df, 0x40eb, 0xb236, 0x323f, 0x43d7, 0x05c3, 0xbfca, 0xb0cc, 0xba51, 0x1d2c, 0xbf72, 0xba3a, 0x2c3a, 0x4154, 0xbf31, 0xba27, 0x19d1, 0x43b4, 0x4167, 0xba36, 0x42bb, 0x0e3a, 0x4011, 0x4363, 0xbae0, 0x186e, 0x40d1, 0x3dff, 0x38c3, 0x12b7, 0x0292, 0x4561, 0xb037, 0x1dda, 0x401e, 0x43d8, 0xacf4, 0x3e49, 0xbf81, 0x1a75, 0x1d50, 0xb2e7, 0x4080, 0x108c, 0x0fbc, 0x41c5, 0x4189, 0xbad6, 0x1d8f, 0x3b0a, 0xa7a7, 0xbfe0, 0x4072, 0x43a6, 0x19e2, 0x4376, 0x09a4, 0x42fe, 0x0780, 0x4158, 0xbfc9, 0x435d, 0xa29f, 0x4111, 0x01c5, 0x435f, 0x41b2, 0xba73, 0x46cd, 0xbf1b, 0xba2a, 0x161c, 0xb0f2, 0xbff0, 0x00c3, 0xa332, 0x2528, 0x19e9, 0x26a9, 0x1d5c, 0xb2ff, 0x42d8, 0xbf70, 0x175e, 0xbf3c, 0x40db, 0x43af, 0x2e2b, 0xb207, 0x421b, 0x4172, 0x0976, 0x410d, 0x439f, 0x1933, 0x1b15, 0x0093, 0x43df, 0x41f1, 0x40c6, 0x1c52, 0x1e4d, 0x4600, 0x0be1, 0xbf7d, 0x2de0, 0x43e4, 0x4319, 0x428c, 0x41bd, 0x4155, 0x46ab, 0x2730, 0x3a48, 0x4365, 0x403d, 0x1ad8, 0x0537, 0x41a0, 0x1994, 0x401b, 0x4088, 0xb2a0, 0x4050, 0x1dc2, 0x435d, 0x437d, 0xb0b7, 0x4060, 0x4109, 0x44aa, 0x41c1, 0xbf8b, 0x43f8, 0xb2bc, 0xba6e, 0xb023, 0x215b, 0x43c9, 0x43c4, 0x1feb, 0x00f7, 0x40e7, 0xb267, 0xbfc5, 0xb229, 0x42e8, 0xbad3, 0x4171, 0x400c, 0xbadf, 0x42ac, 0x431d, 0x4393, 0x4380, 0xa188, 0xbf81, 0xa3d8, 0x442b, 0x42d3, 0x41ce, 0x1e19, 0x4143, 0x4005, 0xb0c1, 0x4211, 0x455c, 0x25ba, 0x0780, 0x422e, 0x400f, 0x45d4, 0x2e74, 0x41cb, 0x1c7e, 0xbfcb, 0xb26a, 0x46f5, 0x3ee4, 0xa482, 0x18c4, 0x44b2, 0x4204, 0x43db, 0x3545, 0x421f, 0xb2f0, 0x463e, 0x2001, 0x43eb, 0x42d4, 0xb28a, 0x3886, 0x4191, 0xadd9, 0x415b, 0x4138, 0x4103, 0x45c9, 0x111b, 0xb02b, 0xb0e6, 0xbf3d, 0x23cd, 0x40ad, 0xb2d2, 0xb0cb, 0xb2e1, 0x21e4, 0xba28, 0xba05, 0xbf48, 0xbfb0, 0x060e, 0x1fa5, 0xba0d, 0x45dd, 0x4017, 0xbf1b, 0x30fd, 0x210c, 0x42b3, 0x17b5, 0x401f, 0x0b91, 0x264c, 0x40af, 0x40c2, 0xbf7d, 0x0302, 0xa7b3, 0x43e0, 0xba6c, 0xbf18, 0x0add, 0x1fe5, 0x0f9f, 0xb073, 0x381f, 0x1b07, 0xb0fa, 0x40d8, 0x1852, 0x4211, 0x42a6, 0x4036, 0xbac4, 0xa27b, 0x435d, 0x18eb, 0x4171, 0x4318, 0xb08e, 0x4296, 0xb2f4, 0xb2ea, 0x3e89, 0xbf32, 0x43b9, 0x1a0b, 0xb2d2, 0x429c, 0x2aad, 0xbf90, 0x18a6, 0x4360, 0x1a4a, 0x4435, 0xba4e, 0x4071, 0x1b83, 0x465d, 0x2ed8, 0x1a91, 0xbada, 0xbfb1, 0x41a8, 0x278e, 0x4182, 0x42c1, 0x1821, 0xbfa0, 0xbad2, 0xb0fe, 0x42e1, 0x1964, 0x46b9, 0xb2a2, 0x40f9, 0xbacc, 0x4221, 0x425a, 0x4464, 0x418e, 0xb0e6, 0x4392, 0x3a7b, 0x4206, 0x1970, 0xbf67, 0x4241, 0xb2f2, 0xbad7, 0x429e, 0x4332, 0x42e6, 0xa4a7, 0x42f6, 0x4011, 0xa32e, 0x40bb, 0x4651, 0x4458, 0x4309, 0x43f5, 0x438c, 0x2ca1, 0xba00, 0x438e, 0xba55, 0x1c48, 0x43f6, 0x41d3, 0xb2a4, 0x4034, 0xba2f, 0xbf0f, 0xbf60, 0xbaf7, 0x42fc, 0xbacb, 0x4217, 0x421e, 0x4113, 0x1863, 0x4103, 0xb052, 0x43b2, 0xb2ab, 0x440f, 0xb22a, 0x4207, 0x40df, 0x449b, 0x185d, 0xbf33, 0x0307, 0x1f5c, 0xb22f, 0x365e, 0x415a, 0xb218, 0x44fc, 0x3db0, 0x41bf, 0x41e6, 0x21d8, 0x412b, 0x4065, 0xbf4d, 0x1013, 0xa00f, 0x444e, 0x4341, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x81c55787, 0xada11b20, 0x3e10df97, 0x7b862743, 0x1f0517ba, 0x4d0933c2, 0x10df7fe1, 0x2dec0c53, 0xcbcf889f, 0x3e17e3e5, 0x8eacf129, 0xd8f1b147, 0xb8b631ab, 0x63405cb4, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x0000181c, 0x000000d8, 0x0000fffe, 0x00000000, 0x00000a00, 0x7f37fe5f, 0xfffffffc, 0x00000000, 0x00000000, 0x0000008e, 0x7f36f510, 0xfb5e1489, 0x0000183a, 0xbf82a5e0, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x43e9, 0xb2ae, 0x2b63, 0x4354, 0xbf91, 0xb23c, 0x4231, 0xb075, 0xb2fc, 0xba3a, 0x4162, 0x1cd2, 0x42af, 0x426e, 0x43fa, 0x0099, 0x1acc, 0x4434, 0x411d, 0x4624, 0xb25e, 0xbfc4, 0x1ef8, 0x45ac, 0x0b2d, 0xb2b0, 0x424d, 0x365f, 0x4336, 0xb0ac, 0x2a8b, 0xaa5c, 0x1bc1, 0x40ca, 0x18db, 0x3c16, 0x4181, 0x4348, 0x1cdf, 0x0413, 0xb2ad, 0xbfce, 0x1e96, 0x4304, 0x405b, 0x42ed, 0x1cea, 0x4159, 0x4057, 0x1c6b, 0x4256, 0x372b, 0x41d8, 0x4268, 0xa6ea, 0x430f, 0x1e36, 0x42bc, 0x40f5, 0xa28f, 0x1ae4, 0xb2a0, 0x4282, 0xa134, 0x4355, 0x42ba, 0x4395, 0x1faa, 0x154b, 0x434a, 0xbf6e, 0x43d1, 0xb2cd, 0x40b8, 0xbfe0, 0x0c97, 0x2541, 0x350e, 0x41c6, 0x40b5, 0x1e4d, 0x4048, 0x4657, 0x428a, 0x07dc, 0x42bb, 0x3923, 0x430d, 0x41a0, 0xbaed, 0xbf4e, 0x40ee, 0x0acb, 0x1f73, 0xbae9, 0xa18e, 0x41a8, 0x1947, 0x181b, 0x4248, 0x4349, 0xbf11, 0x1e48, 0x41d8, 0x18dc, 0x2073, 0xb20f, 0x41df, 0x41bf, 0x1ba3, 0xbf90, 0x4144, 0x428d, 0x41f9, 0x43b8, 0xb239, 0x4180, 0x432a, 0x4119, 0x46f8, 0x4329, 0x4091, 0x4302, 0xb228, 0x4094, 0x40b1, 0xbfcd, 0x2d26, 0x454f, 0x18a4, 0x42fb, 0x1b0e, 0x0d5a, 0x1ddb, 0x14c4, 0x43c2, 0xa374, 0x426b, 0xb0c9, 0x4262, 0xbf80, 0xbf8f, 0x46d3, 0x40cb, 0x4377, 0xb2b3, 0x3a47, 0xba60, 0xbfd9, 0xb267, 0x429a, 0x1806, 0x43d3, 0x3142, 0xb2be, 0x1487, 0x18ab, 0x1cd7, 0xba36, 0x1027, 0xb028, 0x1742, 0x435b, 0x4147, 0x2501, 0x41f0, 0xbadb, 0x40af, 0x444b, 0xb24c, 0xbae8, 0x40aa, 0xb2fe, 0xa75f, 0xbfcb, 0x4162, 0x4146, 0x4328, 0x2a69, 0x4247, 0x286e, 0x0b98, 0x24fb, 0xb061, 0x43a8, 0x409b, 0x1a0b, 0x41b6, 0x435a, 0xb244, 0x242e, 0xbad4, 0x0f4e, 0xbf0b, 0x4069, 0x273a, 0x4422, 0x4208, 0x4248, 0x43fa, 0x4406, 0xb2c1, 0x4046, 0x40bb, 0x4267, 0xb2b6, 0x03b8, 0xb2fd, 0x3dd7, 0xba6c, 0xb29b, 0xba17, 0x4109, 0x1b66, 0xb2df, 0xb005, 0x3496, 0x4598, 0xbf48, 0x40b2, 0x2979, 0x40d4, 0x4269, 0xb2ba, 0x3b3b, 0x408f, 0x336d, 0x1b00, 0x42c9, 0x2a70, 0xbf00, 0xaf87, 0x4386, 0x40c3, 0x4601, 0x461a, 0xbf90, 0xb217, 0x1d8e, 0x1c52, 0x42eb, 0x403f, 0xa25c, 0xa437, 0xbfcd, 0x19b1, 0x413f, 0x404a, 0x19f3, 0xbae3, 0x20a3, 0xbf4e, 0x3dd9, 0x4157, 0xb2ee, 0x29e2, 0xbad3, 0x42ab, 0x40af, 0xbf0d, 0xbaea, 0x43c8, 0xbae8, 0x0451, 0x424a, 0x1c6b, 0x400e, 0x4144, 0x3a3a, 0x412f, 0xbf04, 0x42df, 0x3821, 0x40ec, 0x433e, 0x4196, 0x43fd, 0x42e0, 0xb00f, 0x44dc, 0x2af8, 0x157c, 0x4316, 0xb095, 0xbf4b, 0x1a70, 0x1aaf, 0xb263, 0x41a3, 0x42ae, 0x44ba, 0x289a, 0xb293, 0x40d8, 0x3bc3, 0x42a1, 0x41e8, 0xbaf7, 0x403d, 0x418d, 0x42cf, 0xb023, 0xb05c, 0x4029, 0x40e4, 0x4209, 0x40fc, 0x108c, 0x40d5, 0xbf14, 0xb0e2, 0x425c, 0x4220, 0x244c, 0x0736, 0x1d18, 0x401d, 0x40ac, 0x4045, 0x4051, 0xaf3a, 0xbaef, 0x4451, 0x439a, 0x43ba, 0x406b, 0x1daf, 0x409e, 0x44ad, 0xb09c, 0x4087, 0x205e, 0x4540, 0x40fc, 0xb27d, 0xbf7a, 0x28c4, 0x42c5, 0x4178, 0x42eb, 0x40d3, 0xb273, 0x45f3, 0x401b, 0x43ca, 0x43f4, 0x43d1, 0xaed9, 0xba70, 0x41c3, 0x11e8, 0x41bb, 0x40e2, 0x41c0, 0xa5a2, 0x09c4, 0xbfc0, 0x43aa, 0x388c, 0x411e, 0x189f, 0xbf81, 0x4339, 0x1f88, 0x1b87, 0xa26a, 0xbf0a, 0x4366, 0x415a, 0x42ac, 0x41bc, 0xbf60, 0xba4b, 0xba12, 0x41b7, 0x413d, 0xb02f, 0x4235, 0x0548, 0x0509, 0x41de, 0x42c6, 0x16c5, 0x3d8d, 0xbf0d, 0x2de3, 0x40b7, 0x417f, 0x40f6, 0xa413, 0x4232, 0xba60, 0x4126, 0x4044, 0x1bd0, 0x2d4b, 0x20bd, 0xb215, 0x40d9, 0x415c, 0xbf08, 0x1d75, 0x1e01, 0x411c, 0x0b49, 0x410e, 0xbfcf, 0x1455, 0x0ee7, 0x05cd, 0x4394, 0xb2b1, 0x432c, 0x25fb, 0x4671, 0x4053, 0x3e48, 0x0be7, 0x405f, 0x4276, 0x41f6, 0x440e, 0x40f6, 0x435a, 0x41a5, 0xb2ef, 0x3d3f, 0x443a, 0x332a, 0xb00d, 0x4432, 0x1d1c, 0x428e, 0x4296, 0x42ef, 0xbfaf, 0x4062, 0x1918, 0x08bd, 0x4241, 0xba2f, 0x40b9, 0x1e32, 0x42be, 0x2983, 0xbf99, 0x402b, 0x423e, 0x41e5, 0x403a, 0xbf70, 0x41d1, 0x42cf, 0x434f, 0x41ea, 0x1dd7, 0x418c, 0x418f, 0xb248, 0x1e0f, 0x2ec4, 0x42f5, 0x40e2, 0x4330, 0x4118, 0xad76, 0xb089, 0x41b9, 0x40de, 0x42fa, 0xba54, 0xb2c6, 0xbf09, 0x4102, 0x41e7, 0xb08b, 0x41bd, 0x1dbf, 0x4140, 0x4009, 0x42fc, 0x46db, 0xbad8, 0x45ae, 0x40e0, 0x4212, 0x43fd, 0x43cb, 0x435f, 0x4276, 0x15a5, 0xb222, 0x409b, 0x40cb, 0xbf48, 0xad26, 0x40e7, 0x3491, 0x36b5, 0xbfa6, 0x4192, 0x43e1, 0x430d, 0xb28b, 0xaa14, 0x42eb, 0x4492, 0x41a8, 0xa8d2, 0x436e, 0x41c9, 0x4211, 0x42cf, 0x0d26, 0x41ea, 0x40bd, 0x0f79, 0x40d2, 0x394c, 0x4072, 0x40c9, 0xbf84, 0xb278, 0x2591, 0x43a9, 0x13e4, 0xb287, 0x02ad, 0x3290, 0x43ec, 0x4141, 0x431a, 0x46a9, 0x4629, 0x2e09, 0x1a14, 0x41f5, 0xba19, 0x35d8, 0x2c4a, 0x43dc, 0xb28e, 0x40b7, 0x41db, 0x41fe, 0x2f4d, 0xbfe8, 0x4459, 0x46d8, 0x42c5, 0x1d4f, 0x1a9e, 0x4203, 0x42b2, 0x4299, 0x428d, 0xbf29, 0x4129, 0xb223, 0x427a, 0x41c5, 0x089e, 0xad9a, 0xb24c, 0x1f2a, 0xb023, 0xb291, 0x42b3, 0xb07a, 0x1e3c, 0xa3d6, 0x4194, 0xbaf7, 0x370e, 0x1faa, 0x197f, 0x409e, 0x19c8, 0x4616, 0xbf7a, 0x42a2, 0x437e, 0xba3e, 0x4312, 0x428a, 0x418a, 0x433b, 0x03d8, 0x0250, 0xb29a, 0x1dd0, 0x33c0, 0x4334, 0xb0fd, 0xbfa0, 0xb279, 0x4067, 0x405b, 0xb23e, 0x2224, 0x4375, 0x2e15, 0x406f, 0x43b4, 0xbf94, 0xba6d, 0x04eb, 0x42f4, 0x42d7, 0x4184, 0x40cb, 0x40c5, 0x422d, 0x19c7, 0x415e, 0x426f, 0x4124, 0xb2e7, 0xa9a4, 0x40ed, 0x412f, 0x43c9, 0x3ca8, 0x1b05, 0x32fc, 0x4276, 0x4037, 0x447f, 0x40c3, 0xb267, 0xbf81, 0x43bb, 0xb243, 0x4570, 0x2b22, 0x0e9c, 0x4063, 0x1bea, 0x4055, 0x2367, 0xba35, 0x1e69, 0x0926, 0x44f5, 0xb00b, 0x18f8, 0x4007, 0xb255, 0x4350, 0xb0a5, 0x4095, 0xba26, 0xa26b, 0x14b9, 0x4346, 0xbfae, 0x4312, 0x4446, 0xbf80, 0xa9c8, 0xba7d, 0x410a, 0xbfa2, 0x2319, 0xb282, 0x4185, 0x425f, 0x41c1, 0x409c, 0x1a10, 0x1003, 0x284b, 0xbae6, 0xb221, 0xb242, 0x4262, 0xbad8, 0xbafe, 0xaa78, 0x4223, 0x41d8, 0xb0f9, 0x4091, 0x445c, 0x321e, 0x42c1, 0x4023, 0x411a, 0x438f, 0x43fd, 0xbf29, 0x434f, 0xa7a0, 0x1d7a, 0x40f0, 0xba4f, 0x2879, 0x4093, 0xa50b, 0x42b5, 0xb289, 0x43dd, 0xb014, 0xba26, 0x0450, 0x413b, 0x1828, 0xba63, 0x4156, 0x292f, 0x428f, 0xb053, 0xbf68, 0x42c4, 0x059b, 0x14ec, 0x4207, 0x43c8, 0x43b7, 0xba72, 0xb297, 0x1ab9, 0x1525, 0x416d, 0x420c, 0x446b, 0xb205, 0x466b, 0x416f, 0x19c5, 0x4351, 0x22d5, 0x4670, 0xb06b, 0x0374, 0x066b, 0xbfb9, 0x0c25, 0xabee, 0x4143, 0x41f2, 0x181b, 0x42f7, 0x40ab, 0x4130, 0xba4a, 0x1a6d, 0xba02, 0x4165, 0xbf60, 0x417e, 0x340c, 0xba72, 0xbaff, 0x3ddc, 0xbf35, 0x415a, 0x4011, 0x42cb, 0x437c, 0x3d03, 0x41d4, 0x1855, 0x0b24, 0xa86b, 0x41ef, 0xb262, 0x41a4, 0xb272, 0x43a4, 0x030c, 0x4365, 0xbf52, 0x46b5, 0x464d, 0x42b3, 0x1b6e, 0xba3d, 0x20f7, 0x35dc, 0x2ee9, 0xbf4e, 0x4378, 0xaf6a, 0x142e, 0xb290, 0x42d9, 0x40d8, 0xb069, 0x1fb9, 0x430a, 0x0594, 0x4231, 0xbafd, 0x3a53, 0xb2b1, 0xb0e0, 0x001d, 0xa85d, 0x17cd, 0x42b5, 0x4330, 0xb012, 0xbacd, 0x0ed9, 0xbf86, 0xba22, 0x4068, 0x263e, 0x0048, 0xb2a4, 0xba4a, 0xbf3a, 0x4224, 0x426e, 0x0a8d, 0xa5f2, 0x14f4, 0x4164, 0x1e8e, 0x324b, 0xba1c, 0x40ed, 0x4108, 0x170d, 0x30b8, 0x04a7, 0xb073, 0x2d88, 0x4171, 0x1b68, 0xbfa4, 0x40fe, 0xbac1, 0xba57, 0xb2ce, 0x330f, 0x2614, 0x070e, 0xb0c2, 0x4244, 0xad7d, 0x06ac, 0xa5ef, 0x0d63, 0x42aa, 0xb2df, 0xb2ce, 0x42d1, 0x2ea4, 0xbf4d, 0x4185, 0x41bb, 0x45e1, 0x326c, 0x42ea, 0x443c, 0xb219, 0x41a7, 0xbf31, 0x42af, 0x43f5, 0x4137, 0x1efb, 0x4175, 0x40fa, 0x4340, 0x40f0, 0x4342, 0x43f6, 0xbafe, 0x17c7, 0xbfcc, 0x4485, 0x43e3, 0x4085, 0x4003, 0x4187, 0x07dd, 0x3c2d, 0x1acf, 0x40a2, 0xbae2, 0x417c, 0xbfe0, 0x198c, 0xbf8c, 0x1a73, 0xbafc, 0x2c1d, 0x0cf6, 0x43a9, 0x403d, 0xbf34, 0x4297, 0xb000, 0xb27b, 0xab7d, 0x05c9, 0xb245, 0x4326, 0x426d, 0x4226, 0x42df, 0xb0d8, 0x46f2, 0x43ca, 0x4096, 0x283c, 0x4406, 0x41bc, 0xbf6d, 0x433b, 0x458b, 0x41be, 0xb019, 0x4029, 0x4167, 0x42a1, 0x4024, 0xbaf1, 0xb26c, 0x1e3d, 0xb292, 0x41ea, 0x241b, 0x4024, 0x1aec, 0x431e, 0x4606, 0x4209, 0x356f, 0x4308, 0xa65e, 0xbf7d, 0xbf70, 0x1a6b, 0xbaf8, 0x42f3, 0x1dac, 0xb2bf, 0x26b6, 0xba4a, 0x460b, 0x4390, 0x422d, 0x0a84, 0xb0d1, 0x43d7, 0x4022, 0xb0ee, 0x409e, 0xba72, 0x2a10, 0x429f, 0xbf70, 0x304b, 0xa8bc, 0xbf2c, 0x4288, 0x41cc, 0xbf28, 0xba26, 0x419b, 0x42af, 0x4357, 0xab5e, 0x4307, 0xbae7, 0x4449, 0x2dca, 0x46d9, 0xa4d7, 0x4384, 0xb0ae, 0x39ca, 0x4153, 0xba66, 0x43ae, 0xba2b, 0xbfe1, 0x189b, 0x4062, 0xbaf2, 0xba5c, 0xb041, 0x431f, 0x0f58, 0x4474, 0xbf19, 0x421c, 0x09c9, 0x0947, 0xb013, 0xba56, 0x3cdb, 0x43d0, 0xbf7a, 0x40d6, 0x079a, 0x030a, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x69b72ece, 0x9ea70faf, 0x0bef8eae, 0x8b8f6dd5, 0x4fde440e, 0x8c63dba4, 0xee0baa0f, 0xb2812116, 0x764ead9f, 0xda04bbb5, 0xc49fd907, 0xf371f841, 0xb64410f1, 0x71a52826, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0xffffef27, 0x00002035, 0x00000000, 0x4e010000, 0x014dff25, 0x0000014e, 0x00000000, 0x00000000, 0xf371f841, 0xf371f841, 0x00000000, 0xf371f841, 0xa9b60932, 0x41f962cf, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x40c2, 0x4026, 0x4391, 0x4114, 0x43fc, 0x4369, 0x4099, 0x0707, 0xbfe0, 0xbf8f, 0x3fa9, 0x4220, 0x1d09, 0x4261, 0xba25, 0xb018, 0x2131, 0x41ab, 0x404f, 0x4031, 0xbaf3, 0xb2b6, 0x1e5f, 0x4202, 0x1b23, 0x42e2, 0x2214, 0x4247, 0xba0a, 0x4146, 0xbf5f, 0x0974, 0xa6cd, 0x1ecc, 0x41a9, 0x403b, 0x4312, 0xb08f, 0x4074, 0x4191, 0x4381, 0x404f, 0x45a0, 0xb2e0, 0x0258, 0x1eb3, 0x057e, 0x444f, 0x259e, 0xbfc7, 0x1e86, 0xb080, 0x43cd, 0x1c81, 0x41cb, 0xba3f, 0x423a, 0x4627, 0x4040, 0x32a8, 0x3811, 0x463d, 0x284f, 0x0180, 0xb29e, 0x2fcb, 0x18ee, 0xba4b, 0x4239, 0xba51, 0x42b7, 0xbad0, 0x429c, 0xbf2c, 0x438a, 0x41aa, 0x0bf1, 0x23b2, 0x4393, 0xb27f, 0xafd2, 0xb233, 0x4434, 0x2e78, 0x41f2, 0xbfac, 0xb27e, 0x1ace, 0x40b0, 0x45d2, 0x400e, 0x1fed, 0xb071, 0x239f, 0x4171, 0x18f1, 0xa24f, 0xb2b1, 0x421b, 0x0b5c, 0x464b, 0xba55, 0x424a, 0x4541, 0xbf90, 0x440d, 0xb2de, 0x42a9, 0xb05b, 0xbfce, 0x431a, 0x465b, 0x1108, 0x411b, 0xbafd, 0x4141, 0x40fd, 0x174d, 0x46a8, 0xba6f, 0x2829, 0x0e03, 0x19ed, 0x26d3, 0x4379, 0x40a2, 0xb21b, 0x40bc, 0x185d, 0x4352, 0xb09a, 0xb280, 0x40ae, 0x42a9, 0x4218, 0xba0a, 0x1aa9, 0xbf86, 0xaf41, 0x40df, 0xb2f9, 0x429a, 0xa93c, 0x26b5, 0x22a0, 0xa0ec, 0x1def, 0xb2ea, 0xb231, 0x437b, 0xab30, 0x41ee, 0x1f12, 0x423f, 0x43b6, 0x4218, 0xb0e1, 0x40b9, 0x4175, 0x41aa, 0xbf9f, 0xb04a, 0xb28d, 0xba0e, 0x40f5, 0x1bda, 0x4460, 0x45ce, 0xb253, 0x1fbc, 0x2b3b, 0xb054, 0x216b, 0x1ef5, 0xb239, 0x4117, 0x3e6a, 0x1c59, 0xb212, 0x1e71, 0x435d, 0xb0f6, 0x40af, 0x426f, 0xb267, 0xbfac, 0xb29a, 0x4231, 0x1064, 0xb2bf, 0x42ee, 0xbf90, 0x4194, 0xb2cf, 0x4029, 0x1651, 0x43f7, 0x4091, 0xa737, 0x1c29, 0x36ec, 0x2620, 0x4664, 0xb210, 0x4630, 0xaa7e, 0x4254, 0x4571, 0xbf04, 0x41ad, 0x40b9, 0x407d, 0x06a0, 0xb29d, 0x43af, 0xb2b2, 0xbafc, 0xb2bc, 0xb278, 0xb235, 0x40d5, 0x1e08, 0x3d80, 0x42f6, 0xb01d, 0x4383, 0x08db, 0x428b, 0x43d1, 0x1835, 0x1efa, 0x221b, 0xb057, 0x4051, 0x0216, 0xbfb5, 0xad04, 0x424a, 0x42b2, 0x427a, 0xa99f, 0x2987, 0xb2a1, 0x0a0e, 0xb2d1, 0xbf97, 0x41b9, 0x41d7, 0x18b3, 0x1e5f, 0x3969, 0x323f, 0x1a8e, 0x4643, 0x46e1, 0x4005, 0x435e, 0x4353, 0x35c5, 0x41af, 0x43c6, 0xbaf6, 0x05c4, 0xba7f, 0x41dc, 0x464f, 0x454e, 0xbfe1, 0x3f23, 0x46e4, 0x42e1, 0xb26b, 0x1fe7, 0x44a3, 0x22da, 0x02a8, 0x4366, 0x0239, 0xb2ba, 0x431e, 0x4267, 0xba59, 0x2155, 0x41ff, 0x43b6, 0x4112, 0xb289, 0xb28d, 0xa0b5, 0x415d, 0x4234, 0xbf81, 0x442b, 0x3b05, 0x4359, 0xb2f1, 0xad7d, 0xb2c7, 0x411f, 0x414b, 0x1ce1, 0x41dc, 0xb2ed, 0x4580, 0xba56, 0xb259, 0x407a, 0x1c86, 0xba38, 0x422a, 0x40c4, 0xbf81, 0x411b, 0x1dd0, 0x1eba, 0x3194, 0x3435, 0xb225, 0x43a8, 0x1b6d, 0x231b, 0xbf7d, 0x46ab, 0xb2ed, 0xb0f3, 0xb254, 0xb0e0, 0x41f3, 0x2773, 0x1869, 0x40c3, 0xb25f, 0x1820, 0xbf97, 0x3435, 0x41d2, 0xb2f2, 0x1c37, 0x176a, 0x425f, 0x43f8, 0x400b, 0x23a7, 0xb09a, 0xbff0, 0x414d, 0x4221, 0x283e, 0x43bd, 0x41b9, 0xbaeb, 0x3c41, 0x2790, 0x0b0a, 0xb2e4, 0x1fca, 0xbf80, 0xb0f0, 0xbac8, 0xbfc0, 0xbf7c, 0xba70, 0x41d4, 0x4655, 0x22f4, 0x40bc, 0x4223, 0x42e7, 0x39e7, 0x437c, 0xa249, 0x0634, 0x1fac, 0x413f, 0x2aa4, 0xa70a, 0x4651, 0x424e, 0x4074, 0xbfca, 0x405f, 0x1201, 0xba3f, 0x40ee, 0x40d4, 0x46ac, 0xa9d0, 0x1dc7, 0x41b3, 0x1d14, 0x41c8, 0x332d, 0xb2be, 0x4290, 0x4625, 0xa891, 0xbf70, 0x4335, 0xbf38, 0x42bc, 0xac43, 0x4180, 0x4357, 0x41c2, 0x3ce3, 0x368c, 0x414b, 0x40d4, 0x40a4, 0x4072, 0xbadd, 0x436c, 0x4102, 0x34c9, 0x4430, 0x43e2, 0x407d, 0x018e, 0xa86d, 0x431e, 0x4072, 0xbf74, 0x4271, 0xba6f, 0x4333, 0x180b, 0xb2c8, 0x18ca, 0x40a4, 0x2445, 0xaf56, 0x43a6, 0x438d, 0x41b5, 0xb20a, 0xb0ec, 0x410c, 0x012a, 0x40d4, 0x43e2, 0x40c9, 0x41ce, 0xb2c3, 0xba5e, 0xbf95, 0x4135, 0x1f74, 0x4107, 0x46e4, 0xbfd8, 0x42ed, 0x0c9a, 0x0919, 0x43f9, 0xbfc4, 0x4035, 0x0f3f, 0xbac2, 0xab07, 0x2db8, 0x0c01, 0x4012, 0x1ef4, 0xaae2, 0xb02a, 0xbaf4, 0xbfbc, 0x41d3, 0x4416, 0x40c7, 0x44e4, 0xb2c9, 0x40fa, 0x38e7, 0x42da, 0xba4a, 0x4145, 0x3e87, 0xb018, 0x42d3, 0x07f6, 0x42ec, 0xb2b7, 0x4379, 0xb060, 0xba0d, 0x40ff, 0x2f57, 0xba5b, 0x4263, 0xbf8b, 0x426a, 0x42e1, 0x41b0, 0x461c, 0x438c, 0x33c7, 0x43ef, 0x46b8, 0x1dd3, 0xb233, 0xbf1f, 0x439e, 0x4459, 0x4411, 0xbfd0, 0xab1b, 0xb067, 0xb277, 0x01d0, 0x415b, 0x416e, 0xb20e, 0x4309, 0x423b, 0x0763, 0xbf7e, 0xb04d, 0xb2b9, 0x1c91, 0x4399, 0x4341, 0xba55, 0x40ae, 0xb288, 0xb06e, 0xb2a7, 0x4095, 0x44e8, 0x19a4, 0x42d0, 0x4318, 0xb063, 0x41c6, 0xb284, 0x415f, 0xba44, 0x3c51, 0x4388, 0xb0d2, 0x433a, 0x418e, 0x430b, 0x40bb, 0x4075, 0xbf96, 0x4191, 0xba78, 0x4380, 0x402f, 0xbaec, 0x45da, 0xa7ba, 0xb280, 0x42cd, 0x0e15, 0xbfc0, 0x4292, 0x46c9, 0x44ad, 0xb280, 0x4246, 0xb262, 0xb247, 0x4082, 0x402b, 0xb230, 0xba1b, 0xb077, 0x41f3, 0xbf0c, 0x1901, 0xb016, 0x1edf, 0x3519, 0x4058, 0x418b, 0x42bb, 0xbfd0, 0x014d, 0x43dd, 0xbf52, 0x1b22, 0x434a, 0x41f1, 0x4181, 0xa9a5, 0xb20d, 0xb0cb, 0xbf42, 0x3727, 0xb271, 0x4088, 0xabfb, 0xbf61, 0x1ab9, 0x0344, 0x40cd, 0x41d4, 0x1f44, 0x43b7, 0x298e, 0xbf72, 0x1c94, 0xb0ed, 0x2b19, 0x4427, 0x4337, 0x40c3, 0x3601, 0x19a2, 0x414d, 0x18c7, 0x4006, 0x4313, 0x4253, 0x1d49, 0xbf94, 0xba13, 0xa1dc, 0xb2d7, 0x0b0b, 0x41c8, 0x41b5, 0x1b21, 0x3242, 0x1d53, 0x3ac5, 0xb0ec, 0xba22, 0xa9c9, 0x40ef, 0x4027, 0xb0bf, 0xb00f, 0x211f, 0xb018, 0x1fb8, 0x14c1, 0x410c, 0x4081, 0x4604, 0x3ab7, 0xb20f, 0x4301, 0xbf54, 0x42dc, 0x1861, 0x41bf, 0x405e, 0x1dbd, 0x17ce, 0x46e1, 0x4287, 0xa83c, 0x3e4d, 0xb03a, 0xb2bb, 0x41cd, 0x40a4, 0x430e, 0xb288, 0xb25d, 0x4312, 0xb0de, 0x18b3, 0xb2c9, 0x08aa, 0xba4f, 0xbf61, 0x42aa, 0xb249, 0x2856, 0xa13d, 0x4092, 0x424b, 0x2284, 0xb206, 0x40c5, 0x2801, 0x443b, 0x43df, 0x0c18, 0xbf35, 0x42f2, 0x4031, 0x4048, 0xb225, 0x4215, 0x449c, 0x1ecf, 0x43fd, 0x421a, 0x4584, 0xb03e, 0x2473, 0x16d8, 0x42c8, 0x44db, 0x43bb, 0xba55, 0xba18, 0xb06e, 0x1a1d, 0xb281, 0x23c5, 0xbf72, 0x4288, 0x176a, 0x431a, 0x4392, 0xb0d3, 0x43d6, 0x0263, 0x43e7, 0xbfde, 0x0aab, 0x22c9, 0x4203, 0xba25, 0xb2ed, 0x0d84, 0x3b27, 0x17d9, 0x1c5e, 0xb027, 0x19d8, 0xb22d, 0x4284, 0xada7, 0x44c3, 0x431d, 0x1549, 0xb293, 0xb20b, 0xbaf3, 0x419b, 0x2a68, 0x3d99, 0xbf3f, 0x411d, 0xb2c7, 0x4346, 0x1ea1, 0xbf22, 0x1c0c, 0x16ab, 0x1a2e, 0xb0c8, 0x4253, 0x432b, 0xa1c3, 0xbadb, 0x408b, 0x4339, 0x064c, 0x43be, 0x421d, 0x439c, 0x4042, 0x4197, 0x11d2, 0xbf18, 0xbad3, 0x1e4a, 0x4239, 0x2672, 0xb267, 0xa0f4, 0x4240, 0xba29, 0x42a2, 0x4225, 0xbf73, 0x4263, 0x0e6d, 0x43a7, 0x1be0, 0x4460, 0xba4b, 0x42ad, 0x406a, 0x4002, 0x40c9, 0x41e8, 0xb237, 0x4281, 0x4378, 0x4473, 0xb297, 0x3829, 0xbf60, 0x4110, 0x426d, 0x1b20, 0x1ade, 0xbac1, 0x4227, 0xb216, 0x41ed, 0x430d, 0xbf46, 0x4200, 0xb22b, 0x46ed, 0x1496, 0x180b, 0x3e53, 0x4601, 0x4404, 0xbf37, 0x42a3, 0x24ec, 0x3704, 0x2ead, 0xb041, 0xb239, 0xb0f5, 0x418b, 0xbaef, 0xba37, 0x413c, 0x4076, 0xbada, 0x35ef, 0x40cf, 0xbf8b, 0x4379, 0x3548, 0x1edb, 0x41ea, 0x1d56, 0xba6a, 0xba46, 0xb0ac, 0x33d6, 0x40e1, 0x21ce, 0xbf8f, 0xba17, 0xb257, 0xba54, 0xba5f, 0x26c7, 0x40fc, 0x4283, 0xbf04, 0xb280, 0x43de, 0x1eed, 0x3a39, 0x401d, 0x4310, 0xba5b, 0x4656, 0x404c, 0xbfd9, 0x465b, 0xba4b, 0x4656, 0x438a, 0x4347, 0xa967, 0x4049, 0x41c0, 0x1930, 0xb040, 0x433f, 0x1c50, 0xbf54, 0x41bf, 0x4027, 0xbf03, 0xb0dd, 0x42b3, 0x435a, 0xb292, 0x404c, 0xb290, 0xba28, 0x400b, 0x4092, 0x406e, 0x4318, 0x442c, 0x0c48, 0xa5ad, 0x4148, 0x424d, 0x4279, 0x40b1, 0x4331, 0xb27b, 0xb049, 0x1cae, 0x45ab, 0x4319, 0xb06e, 0x40b7, 0xbf1c, 0x4484, 0x1ce2, 0x439b, 0x41d4, 0xb28f, 0x4038, 0x45c1, 0xaf03, 0xa786, 0x409e, 0x0b05, 0x1295, 0x3ba6, 0xa9df, 0x42df, 0xba11, 0xb2d0, 0xba4e, 0xbf38, 0x43a1, 0x3a98, 0x4163, 0x41e4, 0x40e0, 0x4619, 0x4259, 0x1b6a, 0xbf81, 0x396c, 0x3ebc, 0xb2e6, 0xad08, 0x4350, 0x13f0, 0x1898, 0x1f15, 0xbfaa, 0x4335, 0x321b, 0x4189, 0x40b1, 0x0a31, 0xbfa3, 0x3fc0, 0x2d58, 0x43ad, 0xa85a, 0x40af, 0x423d, 0x02b8, 0x1dfe, 0xbac5, 0x4342, 0x40e1, 0x41fb, 0xab7c, 0x28a1, 0x4219, 0x098d, 0x4085, 0xb20e, 0x2e27, 0x0b99, 0x4403, 0xb2c0, 0x405d, 0xb0bc, 0xb0cb, 0xbf98, 0x42c0, 0xb207, 0xb250, 0x40d5, 0x19ff, 0xb2bb, 0x42cc, 0x42d6, 0xae29, 0xba31, 0xbfdd, 0x3003, 0x0f3d, 0xb091, 0xba54, 0x41ed, 0x40e3, 0x20e4, 0xb012, 0xb2c2, 0x409d, 0xb28e, 0x080e, 0xaedd, 0x06d2, 0xb226, 0x4160, 0x4059, 0xb229, 0x040c, 0x4223, 0x422f, 0x3772, 0xb2b6, 0x417e, 0xb21b, 0xbacb, 0xb2d4, 0x2fb1, 0x45e2, 0xbfc9, 0x41f2, 0x42c9, 0x0d4f, 0x4300, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x82916b79, 0x515ecb9e, 0x9eeb80a7, 0x4448bdb6, 0x9fbc4379, 0x7f419a33, 0x42be67d1, 0x712a0dae, 0x4382a2e5, 0x7d2ed79f, 0xe8c9993b, 0x9fd1abb1, 0xed4e4991, 0xf7cf8b5c, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0xa000b5cd, 0x00000000, 0x00000008, 0x00000000, 0x00000000, 0x00000000, 0x0000b55a, 0x00000000, 0xf7cf8df7, 0xd1933276, 0xe8c9993b, 0xf7cf8df7, 0xd1942582, 0xf7cf8df0, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x45f6, 0xa7e3, 0x4240, 0x4552, 0x19ea, 0x1ee9, 0x4223, 0xb029, 0x03fc, 0x41ff, 0x4699, 0x4543, 0x40eb, 0x1189, 0xb2eb, 0x2ed0, 0x46c5, 0x152e, 0xbf8b, 0x42ee, 0x454e, 0x2a1e, 0x3168, 0xa0a7, 0x1fa6, 0x415b, 0xb286, 0x3cff, 0x35a2, 0x405c, 0x4228, 0x40e1, 0x43e2, 0x40c6, 0x37cc, 0xbfda, 0x4320, 0xba70, 0xb067, 0x43f0, 0xb2b7, 0x4213, 0x4247, 0xb229, 0xb2f5, 0x42e7, 0x40cd, 0x1bdf, 0x43f8, 0x3887, 0xa60a, 0xba2e, 0x1b0d, 0x1e22, 0xba51, 0x437e, 0x45e4, 0x466e, 0xb017, 0x29df, 0x4057, 0x19e9, 0xb0cd, 0xbf34, 0x4466, 0x1283, 0xb0de, 0x42ff, 0x1c85, 0x4430, 0x42b0, 0xba35, 0x4274, 0x41e6, 0x4331, 0x42dd, 0x40ec, 0xbf43, 0xb23f, 0x405d, 0xba5f, 0x4563, 0x432c, 0xba0d, 0x43a2, 0x2a71, 0x439d, 0xb26e, 0x4685, 0xb086, 0xb217, 0xb264, 0x4412, 0xbff0, 0x4388, 0x18ff, 0x43c7, 0x407f, 0x4174, 0x410b, 0x4324, 0x469c, 0x410a, 0x4302, 0xbaff, 0xbfa5, 0x4371, 0xb26b, 0xb079, 0x42f6, 0xbf8f, 0xb251, 0x411a, 0x42e3, 0x21bb, 0xba73, 0xb2c3, 0x3374, 0x0cd7, 0x4128, 0x1ca9, 0x40fd, 0xae9f, 0x40fc, 0x1df9, 0x44a8, 0x1d1d, 0x4061, 0xb08f, 0x40f0, 0x4375, 0xb244, 0x45f3, 0x404a, 0x406e, 0xbf5f, 0x4146, 0x41ff, 0x4065, 0x4071, 0x4391, 0x0932, 0x42c2, 0xba62, 0x4146, 0x402b, 0x424f, 0x4263, 0xba47, 0xb0df, 0xbf79, 0x419b, 0x40d5, 0xbfa0, 0x4157, 0xb2e6, 0x42f3, 0x4216, 0x4174, 0x40b0, 0x3c8e, 0xa763, 0x0bb7, 0xba04, 0x45de, 0x4598, 0x1c37, 0xb250, 0x414b, 0xb299, 0xba17, 0xb0c3, 0xa7f5, 0x1e74, 0xbfc7, 0xb23b, 0x42ad, 0x4235, 0xb270, 0x11c2, 0x436f, 0x42ac, 0x45f4, 0x1221, 0xb26e, 0x46a4, 0xb283, 0x2610, 0xb05b, 0x098e, 0x1946, 0x439c, 0xa1ce, 0x4057, 0x16ac, 0x4357, 0xb2d1, 0x4231, 0x4300, 0xae54, 0xbf70, 0x40f3, 0xb2e8, 0xbf2d, 0x1b23, 0xba0a, 0xb0fc, 0x280d, 0x423d, 0xba50, 0xb29c, 0x416e, 0x41f3, 0x42f3, 0xb06a, 0xabca, 0x24ba, 0x1ba6, 0xb2ef, 0x41ec, 0xb22a, 0x1b17, 0xaa10, 0x41b2, 0xba10, 0x1fa7, 0x3414, 0xbacb, 0x4439, 0xbf90, 0xae5e, 0x41a1, 0xbfd4, 0xba4d, 0xb271, 0x0305, 0x422a, 0x411a, 0x4194, 0xb20e, 0xb29b, 0x41b3, 0x46a1, 0xb2c4, 0x3014, 0x40dc, 0x36e7, 0x432d, 0x09b3, 0x430c, 0xb01f, 0x12d7, 0x40aa, 0xb2d9, 0x055b, 0xbaee, 0x42d2, 0x43b4, 0x1875, 0xbf51, 0xa093, 0x226a, 0x284d, 0xb29f, 0x44a4, 0x3de8, 0x35aa, 0x465e, 0xb266, 0xb0a1, 0xb2f5, 0x437f, 0x4250, 0xa47f, 0xbfb4, 0x273b, 0x1d3e, 0x15c1, 0x1c60, 0x0830, 0x0bcd, 0x4255, 0x44db, 0xbf1b, 0x434d, 0x2582, 0xb094, 0x1fa4, 0xa542, 0x05e2, 0xb29f, 0xba52, 0xb00f, 0xb2f3, 0x4285, 0x1a85, 0x4636, 0xb09e, 0x2a91, 0xbf90, 0xb28f, 0xbf80, 0xb234, 0x18fc, 0xbf2d, 0xb07c, 0x41bf, 0x425d, 0x419e, 0x4327, 0x4026, 0x4241, 0xa452, 0x0cbc, 0x41f0, 0x46f4, 0x42d6, 0x4464, 0x1f9f, 0xba60, 0xbfe4, 0x42a5, 0x40a2, 0xb2bf, 0x405f, 0xb2f5, 0x4333, 0x43af, 0x435d, 0x191d, 0x4002, 0xbf68, 0x42a3, 0xb0ee, 0x26e9, 0xbfcc, 0x3226, 0x4342, 0x4280, 0x1715, 0x19cc, 0xb2ed, 0x4407, 0x2653, 0xbaf6, 0xb2c4, 0x43c4, 0xb2b1, 0x4131, 0x1090, 0x41e3, 0x4217, 0xbf90, 0xba7f, 0x401d, 0xb268, 0x408f, 0x4287, 0x4257, 0xbfce, 0x4185, 0x1e28, 0x4265, 0x45ec, 0x43da, 0x422e, 0x2405, 0x00ae, 0xb269, 0xb2c5, 0xbfb9, 0x3700, 0xb067, 0x41ab, 0x408c, 0x4053, 0x4023, 0xbfc6, 0x1c83, 0x4078, 0xace2, 0xbf00, 0x390d, 0x4247, 0x4343, 0x4291, 0x44e4, 0x18eb, 0x4214, 0x4044, 0xb0ae, 0xbacd, 0x4171, 0xb0b7, 0x42f1, 0x4045, 0xb277, 0x40dc, 0x19a8, 0xbf91, 0x09fd, 0xb21c, 0xba3e, 0x41a5, 0xbad5, 0x1abb, 0xbfbd, 0x3179, 0x22a8, 0x1555, 0x31a9, 0xbfe0, 0x420d, 0xbf7d, 0x428d, 0xbaea, 0x1e6b, 0x2b66, 0xb05d, 0x1ac3, 0x4176, 0x41c6, 0x41c5, 0x421b, 0xb2ec, 0x41e9, 0x2cf0, 0x4386, 0x4108, 0xbf1f, 0x4066, 0x181d, 0xb2a1, 0x43f5, 0x43a1, 0x00a7, 0x42ff, 0x1f65, 0x43aa, 0xb24d, 0x0ab5, 0x42ee, 0x2d96, 0x456b, 0xbaec, 0x3863, 0xbaf2, 0xbafd, 0x1f60, 0xb2f5, 0x4144, 0x2be1, 0xaca3, 0xb2a9, 0xbf96, 0xaed3, 0xac10, 0x404d, 0x4040, 0xbac2, 0x228d, 0x4144, 0x43d7, 0x45e5, 0x419f, 0x1d89, 0x4463, 0x311c, 0xbf4a, 0x1cd6, 0x4618, 0xb2db, 0x43fa, 0x40ae, 0xb0af, 0x32a0, 0xb2fe, 0xaef6, 0x1de3, 0x4302, 0x42a0, 0xabb6, 0x411e, 0x439b, 0x464b, 0x406f, 0x2a8b, 0x38cd, 0xbfc2, 0x422d, 0xb068, 0x4327, 0x4328, 0x4109, 0xbf4b, 0x0a21, 0x44b3, 0x4089, 0x40bd, 0x18ca, 0x4112, 0xba1f, 0x0bf6, 0x31e8, 0x41bf, 0x45c1, 0x4354, 0x2f9f, 0x0993, 0xba25, 0x4010, 0x1a4b, 0x43ab, 0xbf8e, 0xbacb, 0xa49a, 0x42f5, 0xb029, 0xbfc8, 0x4376, 0x40af, 0x464f, 0x01c2, 0x43da, 0x4251, 0x40a8, 0x422c, 0x438e, 0xbacf, 0x0b64, 0x437d, 0xa181, 0x40ca, 0x411f, 0x4677, 0x4353, 0x2f02, 0x43f2, 0x05fe, 0xb2e2, 0xb083, 0xbf3a, 0x303c, 0xb007, 0x24d0, 0xb27d, 0xb216, 0x3049, 0xba34, 0x4399, 0xb27a, 0x2f9c, 0xb006, 0x223e, 0x0844, 0x4044, 0x436a, 0xb276, 0xb2b6, 0xba1e, 0xbf52, 0x425c, 0xbaee, 0x42d5, 0x417d, 0x4187, 0xbaf2, 0x4238, 0x402b, 0x4206, 0x41cd, 0x05b2, 0x402d, 0x408b, 0x4681, 0x419d, 0xb0b5, 0xb28d, 0xbfe8, 0x27bb, 0x25c4, 0x02ee, 0x40e2, 0x435b, 0x40ad, 0x43ea, 0x1fd2, 0x4065, 0x1d5a, 0x4347, 0xb2d5, 0x436f, 0xae55, 0xba71, 0x0f2c, 0x4187, 0xbf73, 0xb20a, 0x44eb, 0xa5dd, 0x40ed, 0xba53, 0xba13, 0x4191, 0x42e5, 0x4193, 0x4168, 0x40ab, 0x4476, 0x4135, 0x41c3, 0x4145, 0x353e, 0xbfd0, 0xb220, 0xa3e1, 0x42a1, 0xb2d3, 0xb2af, 0x433d, 0x412a, 0x28d7, 0x411d, 0x42bc, 0x4155, 0xbfd5, 0xac98, 0x4353, 0x4142, 0x4435, 0x03bd, 0x402c, 0xb056, 0xb08b, 0x460f, 0xb284, 0x45e0, 0x4240, 0x265c, 0xba4a, 0xa052, 0xa53d, 0xbae5, 0xb2a0, 0xbf31, 0x413b, 0x1ecb, 0x4234, 0xb27a, 0xb204, 0xac49, 0x4387, 0xb064, 0xbf80, 0xbfd1, 0xb229, 0x4342, 0x41b5, 0x43f9, 0x43a7, 0x405e, 0x449d, 0x43c1, 0xba4e, 0x4181, 0x40a6, 0x238d, 0x4280, 0x422f, 0x401a, 0xbf70, 0x09df, 0xa4e7, 0x4040, 0x37cc, 0xbfa2, 0x300b, 0x013f, 0x423e, 0x42ab, 0x412a, 0x45b3, 0xb2f0, 0x405b, 0x12ca, 0x40a4, 0xabdf, 0x43ab, 0x4383, 0x43c4, 0xba14, 0x4105, 0xbf05, 0x14da, 0x4294, 0x4065, 0x40f4, 0xb246, 0x4179, 0x1d11, 0xb240, 0x283b, 0xb2df, 0xbf75, 0x1b5a, 0x435a, 0x05db, 0xbaf6, 0x4399, 0x4394, 0x42b3, 0x1fdc, 0x40e4, 0x41ba, 0x432e, 0x438f, 0xb2e0, 0x3f84, 0xb003, 0x137f, 0xb051, 0x419f, 0xba3e, 0xbad0, 0xb208, 0x417b, 0x431f, 0x2355, 0xb25c, 0x266c, 0x43f6, 0x4478, 0xbfa8, 0x42af, 0x4167, 0x2ef4, 0x42b2, 0x411a, 0x4151, 0xba77, 0x4391, 0x27b8, 0xba78, 0x0a0c, 0xb2d7, 0x4595, 0x41e3, 0x40dc, 0x41e5, 0x41ec, 0x1c70, 0xbfe4, 0x41bc, 0x4109, 0xb2fd, 0xb292, 0x0159, 0x240f, 0x4363, 0x4398, 0x4371, 0x1ba9, 0x4088, 0x3e3e, 0x4016, 0x4363, 0xb2c2, 0x05b5, 0xbfb0, 0xba63, 0x24f5, 0xb23c, 0xbf03, 0x4146, 0xb2a1, 0x380d, 0xb267, 0x3bde, 0x4092, 0xb274, 0x251e, 0x421c, 0x0687, 0xba79, 0x059e, 0x429b, 0x45b9, 0x0d1c, 0x45a6, 0x1185, 0x46a3, 0x0230, 0x4128, 0x1b96, 0x3e30, 0xbaca, 0x418f, 0x43d4, 0xbfda, 0xb0e8, 0xbf80, 0x3cf6, 0x4264, 0x4005, 0xa0bc, 0x3a93, 0x1777, 0x454d, 0xbad4, 0x4066, 0x414f, 0x181b, 0x1b26, 0x40d9, 0x42bd, 0xba42, 0x1930, 0x0023, 0x405b, 0xbf3b, 0xad73, 0xb278, 0x4111, 0xbadc, 0x4276, 0xaa86, 0x4310, 0x441a, 0x0ee1, 0x2eb8, 0x4379, 0x4268, 0x43ed, 0xb268, 0x187e, 0x4221, 0x2188, 0xb05a, 0x43e6, 0x43ed, 0x4148, 0xb2cf, 0x4491, 0xbf3f, 0x0b27, 0x1cc0, 0xb236, 0x1a4a, 0x43fe, 0x43c3, 0xba58, 0x4326, 0xb0d9, 0x1a6c, 0xa71b, 0x1461, 0x40cc, 0x41ba, 0x1d96, 0x42aa, 0x19d1, 0x1413, 0xba22, 0xb2a4, 0xba5b, 0x441f, 0x0936, 0x3137, 0x4491, 0xbf76, 0xb0dd, 0xa93b, 0xb0a4, 0xb09d, 0x1677, 0x1dd3, 0x40fa, 0xa6a6, 0x4054, 0x424f, 0xb008, 0x4650, 0x401f, 0xab43, 0x43a7, 0x1a59, 0x4034, 0xbacd, 0xaff8, 0xb062, 0xbfc3, 0x4081, 0xba07, 0x459e, 0x4037, 0x1f32, 0xba28, 0x43b7, 0x0c12, 0x4133, 0x1b17, 0xbf94, 0x4286, 0x4212, 0x41cd, 0x1953, 0x4049, 0xba1c, 0x43cc, 0x1c4e, 0x43ac, 0x432d, 0x4616, 0x45c3, 0x3f22, 0x0c24, 0x2bbe, 0x414e, 0xbf51, 0x41ba, 0x3fed, 0x42b0, 0xb2eb, 0x1a7a, 0x1d81, 0x43d0, 0x4358, 0x1f1a, 0x4362, 0xba60, 0x42a1, 0x40e8, 0x43f5, 0x1c51, 0x4249, 0xba7b, 0xbf27, 0x4169, 0x4296, 0xbf70, 0x4344, 0x4356, 0xbaff, 0x436f, 0x42d8, 0x0bc9, 0xba0d, 0x428e, 0x4038, 0x40f8, 0x1ba6, 0x428e, 0x417a, 0x44b1, 0xb0cc, 0x4328, 0x4187, 0x4090, 0x41d3, 0x458c, 0xba28, 0xbfba, 0x42e5, 0x459b, 0xb27c, 0x42ec, 0x2a3e, 0x408c, 0x3be5, 0x2aec, 0xbf0d, 0xb282, 0xba27, 0x43fa, 0x419b, 0x43c8, 0x1934, 0x445c, 0xbf86, 0xb0e9, 0x4240, 0x4282, 0x044a, 0xb240, 0xaa3d, 0x424a, 0x39f5, 0x0ee1, 0x41ab, 0x1a6e, 0x4324, 0x0e64, 0x41e5, 0xbf8c, 0x4170, 0x19eb, 0x3ff4, 0x4404, 0xb021, 0x435a, 0x4380, 0x46b0, 0xb2ea, 0x24d2, 0x40ed, 0x42d8, 0x40e1, 0x4245, 0x4108, 0x19c7, 0x428d, 0x422e, 0x4244, 0x260e, 0xb045, 0xb060, 0x1ab1, 0x40b1, 0xb0b9, 0x42eb, 0xbf9e, 0x1a35, 0xb24a, 0x4411, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xdb27f4a8, 0x745a69a1, 0xc76c58a7, 0xcb93d578, 0x59551d1e, 0xdd275e29, 0xa41a07ab, 0xcd4584a9, 0x8cd3e811, 0x92d54b8c, 0x7a714058, 0xd6ca1b2d, 0xbb97bd53, 0x4fb916ee, 0x00000000, 0xc00001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00034000, 0x00000000, 0x0382fe19, 0x00000000, 0x0000000e, 0x0000000e, 0xffffff0c, 0xf47d00e9, 0x1f5a553b, 0x7a714058, 0x00000000, 0x00000000, 0x605fe8c0, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4283, 0xa90f, 0x4210, 0x1dab, 0x4172, 0x40c4, 0x40b1, 0x44d5, 0x2a9b, 0xba65, 0x4002, 0x1842, 0x308f, 0xb226, 0x42c5, 0x425c, 0x40fd, 0xb279, 0xba34, 0x4284, 0xbf60, 0x3d5d, 0x420c, 0x401a, 0x0e39, 0xbf79, 0x4395, 0x1ccb, 0x41ae, 0x1ddc, 0x4062, 0x44db, 0x2e06, 0x4389, 0x1d42, 0x1c39, 0x4347, 0xb298, 0xb007, 0x43a7, 0xb22a, 0xba22, 0xb22c, 0xba54, 0x4304, 0xa8d3, 0x42a8, 0x2c0a, 0x40a3, 0x2b03, 0x1b34, 0xb2ef, 0x06d3, 0xbf3a, 0x1d56, 0x1ef0, 0x0fa3, 0xb2a8, 0x1939, 0xbff0, 0x422c, 0x019c, 0x1f8e, 0x42cb, 0xbae4, 0x17be, 0x404e, 0xb2d3, 0x1fa5, 0xb209, 0x42ae, 0x2bb5, 0x424e, 0xb273, 0x4202, 0xba33, 0xb2af, 0x1d35, 0x1a2f, 0x1cf0, 0xb266, 0x2255, 0xbf3f, 0xac6b, 0x42f7, 0xb092, 0x1263, 0x46a9, 0x4099, 0x105a, 0x18b5, 0x34aa, 0x1b91, 0x4129, 0x1449, 0x4096, 0xb27a, 0x437a, 0x40f5, 0xb27d, 0x1628, 0xb242, 0x42da, 0x404b, 0xbfbb, 0xb077, 0x4297, 0x0570, 0x4152, 0x2525, 0x1be3, 0xbfe0, 0x4633, 0x12e2, 0x4547, 0xba67, 0x06a6, 0x40c9, 0x371b, 0xbfb0, 0x4201, 0x0101, 0xba0d, 0xbfd9, 0x4192, 0x2123, 0x4117, 0x428e, 0x4084, 0xba04, 0x417c, 0x4390, 0x41e5, 0x41ad, 0x462e, 0x46da, 0x4268, 0x40b4, 0xb279, 0xa5df, 0xb26a, 0xbf4d, 0x356c, 0x09bf, 0xb06e, 0xb292, 0xb2f2, 0x1897, 0x0d15, 0xb261, 0xb29b, 0x40e6, 0x3e3a, 0x46ba, 0x3342, 0x1d7b, 0x401c, 0xb0ce, 0x177f, 0x430d, 0x0189, 0x43ba, 0x4309, 0xb20f, 0xb228, 0xac9c, 0x086d, 0x13e5, 0xbf61, 0x1fc8, 0x41b5, 0x2273, 0x40ab, 0x4334, 0x43fd, 0xbf6d, 0x4304, 0x4286, 0x4372, 0xb213, 0x4426, 0x02e4, 0x40da, 0xb2bd, 0x441a, 0xbf79, 0x40c6, 0x1e05, 0x1b15, 0x420d, 0x4248, 0xbf60, 0xb093, 0x39aa, 0x40c8, 0x085d, 0x37e8, 0x400a, 0x15dc, 0x218d, 0xb223, 0x1910, 0xb248, 0xb00d, 0x4168, 0x44c2, 0x418d, 0xb0e5, 0xb0b2, 0x33e7, 0xa4e7, 0x43e3, 0x0066, 0x165b, 0x4231, 0xbfa7, 0x2e38, 0xb249, 0x431a, 0x4089, 0xba16, 0xa64d, 0x4181, 0xbfd0, 0x3a3f, 0xb258, 0x44cd, 0xaa78, 0x41d7, 0xa0cb, 0x4279, 0x4061, 0x41de, 0xb2bc, 0x4125, 0xae63, 0x41bc, 0x418f, 0xba71, 0xa347, 0xbf4f, 0x4666, 0xb204, 0x4202, 0x42db, 0xb006, 0xbf7b, 0x0978, 0x1c50, 0x1bc7, 0x1818, 0x053b, 0xbfb5, 0xa485, 0x4134, 0x27f9, 0x2199, 0xae03, 0x4126, 0x46e5, 0x1a12, 0x1ee6, 0xb22d, 0x2692, 0x4361, 0x411d, 0x4064, 0x4091, 0x3d71, 0x3936, 0xb2a0, 0x34de, 0xbfdf, 0x4552, 0x1bb7, 0x1a4a, 0x1264, 0x40d4, 0x1d83, 0x4227, 0xbfe0, 0x0a4d, 0x429b, 0x3aa5, 0x4055, 0x43f3, 0x1c83, 0xbf5a, 0x4567, 0x43f6, 0xa01f, 0x1cdc, 0x40bd, 0x41c3, 0x4223, 0x3fea, 0x422b, 0x3d0a, 0x18f7, 0x432d, 0x1e00, 0xbf04, 0xaf5d, 0x1b42, 0xb0e2, 0x46e4, 0xbf09, 0x463a, 0x4355, 0xb0dd, 0x44d2, 0xb24c, 0xba2b, 0xa14c, 0xbfc0, 0xb2b7, 0xba12, 0x43f1, 0xb294, 0x41b6, 0xb247, 0x0688, 0x43e8, 0x42c7, 0x4155, 0x43c4, 0x44fd, 0xa8d2, 0x19a7, 0xb2d5, 0xb035, 0xb2a8, 0x43fd, 0x455d, 0x4314, 0x1f85, 0xbf2c, 0x4287, 0x4154, 0x4204, 0xbf06, 0x4201, 0x408b, 0x40f5, 0x43f3, 0x43b4, 0x4329, 0x42ab, 0x45d0, 0xb093, 0x434c, 0x0f4f, 0x1d8c, 0x402f, 0xba58, 0x444c, 0xb294, 0x41b1, 0x42a7, 0x4177, 0xbfc7, 0x4560, 0xba10, 0xb28a, 0x41f7, 0x40c1, 0xb21b, 0x4068, 0xacc3, 0x41b5, 0x0829, 0x42eb, 0x408f, 0x46f9, 0x1c2f, 0xa7ff, 0xb27e, 0xb2a2, 0x194b, 0x4182, 0x3acc, 0x1527, 0x4143, 0x40f4, 0x3a3c, 0xb2ea, 0x44b5, 0x4076, 0xbf69, 0x34c1, 0x43ce, 0xb299, 0x18d5, 0xbfa0, 0xb24d, 0xbf15, 0x427e, 0xb003, 0x4289, 0xba4b, 0x356d, 0xb2a7, 0xa497, 0xba5a, 0x405e, 0xb249, 0xb2a7, 0x4176, 0x426b, 0xb204, 0xaea9, 0x4110, 0x2479, 0x3502, 0x46ca, 0xbfbc, 0x4321, 0x41f4, 0x4170, 0xba41, 0x1d2c, 0xba4a, 0x11a4, 0x009a, 0x28c5, 0x43d3, 0xbfcf, 0x4034, 0xabeb, 0xaac6, 0xa608, 0x4056, 0x338f, 0x0394, 0xb02f, 0x4274, 0xa5c6, 0x1be8, 0x40fc, 0x4029, 0xb2b5, 0x0227, 0x090b, 0xbacc, 0x1116, 0x2c45, 0x08ee, 0x14fa, 0xbf82, 0x405a, 0x423f, 0xba2a, 0x4052, 0x4200, 0xa671, 0x0efd, 0x42de, 0x1e89, 0xb049, 0xb264, 0x3e26, 0x41d5, 0x4445, 0x32af, 0xb0e8, 0xbafb, 0xb0f7, 0x4093, 0xb034, 0xb2e8, 0x2e69, 0xb259, 0x2479, 0x439d, 0xb2de, 0x1b5b, 0xbf8c, 0x437b, 0xbad1, 0x217d, 0x4093, 0xb2e6, 0x1972, 0xb2e5, 0x1d51, 0x4210, 0x4310, 0xb20c, 0xad06, 0x1879, 0x40b7, 0x4077, 0xbfbd, 0x1fa9, 0xb00b, 0x40bf, 0xbaf2, 0xa690, 0x4298, 0xa00d, 0x2c47, 0x1e51, 0x41c2, 0x42dc, 0xbf6e, 0xb0c0, 0x2ddd, 0x43b9, 0xba69, 0xa0f1, 0x1c40, 0xbfe0, 0xba04, 0x4263, 0xba26, 0xa397, 0x1731, 0x0de3, 0x40bc, 0xa1e7, 0x411c, 0x4005, 0x417a, 0xb043, 0x43d3, 0x42a1, 0x4053, 0x42e8, 0xb0eb, 0xbf16, 0x1c21, 0x4442, 0xbf80, 0x4164, 0x401e, 0x41e5, 0x18d4, 0xb241, 0x45ed, 0x4037, 0xba05, 0x415a, 0xbf25, 0xa2a9, 0x0e62, 0x26af, 0x182b, 0x4134, 0xa7ee, 0x4212, 0x42bd, 0x4243, 0x402a, 0x4182, 0xbf2e, 0x16a0, 0xb213, 0x42b4, 0xb2fe, 0xb20f, 0xb23c, 0x0bb8, 0x43da, 0x4265, 0x42cb, 0xb02a, 0xb226, 0x43db, 0x421e, 0x41e7, 0x1f20, 0x22ca, 0x41d9, 0x1e79, 0xb00b, 0x41cf, 0x401b, 0x3174, 0xb0f4, 0x404a, 0xbf03, 0x4674, 0xb243, 0x43cb, 0x431a, 0x43cd, 0xb0af, 0x0481, 0xaca0, 0x419a, 0x3433, 0x43dc, 0x4197, 0x3d98, 0x19f0, 0x21f1, 0x42d2, 0xba2f, 0xa60c, 0x4337, 0x4102, 0x2851, 0xba4b, 0x44db, 0x4432, 0x422c, 0x4339, 0x4002, 0x1e4d, 0xbf7d, 0x434f, 0xb20d, 0xb00e, 0x3545, 0x41a7, 0x40bb, 0x03ab, 0xba58, 0x2faa, 0x45eb, 0x0643, 0x4098, 0xb2ff, 0xb038, 0xa185, 0x3d65, 0x062b, 0xb010, 0xb20e, 0x1fab, 0x417d, 0x46bc, 0x080c, 0x1913, 0x4146, 0xbfd3, 0x42af, 0x064d, 0xbaeb, 0xba5c, 0x2be1, 0xbf90, 0x404a, 0x0e2c, 0xba56, 0x434e, 0x4320, 0x4180, 0x4658, 0x42d3, 0x4281, 0x1986, 0x424a, 0x4281, 0xb28f, 0x42db, 0xbf6b, 0x41da, 0x42d3, 0xbaed, 0x43de, 0x1d8d, 0x04df, 0x1e09, 0x421f, 0xb21a, 0xb2eb, 0x41cf, 0xbff0, 0x44bb, 0x1ce0, 0x185f, 0x2ceb, 0x42a9, 0x1d0f, 0x4000, 0x1c0c, 0x4127, 0xba21, 0xbac3, 0x3822, 0x15bb, 0x3d53, 0xbf47, 0xb064, 0xb075, 0xb258, 0xba4e, 0x4314, 0x466c, 0x41d2, 0x1a9c, 0xb056, 0x413e, 0x4089, 0xb0d0, 0x40c2, 0x4202, 0x421f, 0x1ca7, 0xb2bc, 0x3348, 0xb280, 0xbf6a, 0xb0ae, 0x016e, 0xb0a7, 0x4672, 0x4147, 0x3098, 0xa236, 0xb00f, 0xbf1a, 0x4330, 0x42ec, 0xbf00, 0xba37, 0xb2b5, 0x4182, 0x4075, 0xb038, 0x413e, 0xba59, 0x45eb, 0xba2b, 0xbaf7, 0x44b1, 0x1dd1, 0x417b, 0x1a2c, 0x40d5, 0x438a, 0xb03a, 0x1d73, 0xb21e, 0xb25d, 0xb27f, 0xbf1e, 0x430a, 0xb0ec, 0x4159, 0x2733, 0x4247, 0x424a, 0xbf4a, 0xb263, 0x1b5f, 0x40ae, 0xb2fe, 0xbf76, 0x407d, 0xb28a, 0x1f72, 0x1e7c, 0x463c, 0x42c2, 0x40e7, 0x4166, 0x4380, 0x1603, 0x1b69, 0xb212, 0x1739, 0x4377, 0x3103, 0xbf49, 0x40ac, 0x278e, 0xb07d, 0x45c1, 0x1ef5, 0xbff0, 0xb2a0, 0x4379, 0x1956, 0xb233, 0xb26d, 0xbfdf, 0x463b, 0x4096, 0xaeb4, 0x17d4, 0x4663, 0xba6b, 0x4640, 0x3004, 0x404c, 0x46aa, 0xbaed, 0x3318, 0x44e5, 0x1d45, 0x1c9c, 0x1883, 0x425e, 0x0c46, 0xba40, 0xa97f, 0xbff0, 0x0b6c, 0x425e, 0x4207, 0x420c, 0x4257, 0xbf86, 0xabef, 0x40d0, 0x4050, 0x43bf, 0x38f6, 0xb240, 0x3692, 0xbf9c, 0x4288, 0x0c04, 0x41a9, 0xac55, 0xb0b2, 0x414c, 0x1b78, 0xb003, 0x3531, 0xb280, 0x0064, 0x4437, 0x1c72, 0x1f4c, 0xbf1c, 0x435e, 0x40fd, 0x3a0e, 0xbfa0, 0xba1b, 0x4060, 0xba2e, 0x3a97, 0xbaf7, 0x39bd, 0x4264, 0x4472, 0x4328, 0xbf49, 0x1acf, 0x42f6, 0x42b4, 0xb294, 0xb272, 0xb090, 0x4654, 0x4017, 0x467f, 0x468a, 0xace4, 0x44ed, 0x4557, 0x466a, 0x1257, 0x4153, 0xb05e, 0x4121, 0x41b0, 0x4393, 0x410b, 0xbaf3, 0x4556, 0x4242, 0x1f43, 0xbf1d, 0x42c3, 0xb240, 0x4038, 0x404b, 0x43e7, 0xb0fa, 0x420a, 0xbf9d, 0x4323, 0xb0a3, 0x4340, 0x42e9, 0x0052, 0x1cc4, 0x4305, 0x020b, 0xb09d, 0x411d, 0x2c14, 0xb0ee, 0x416c, 0x38f9, 0x4357, 0xb2b9, 0xb21f, 0x4152, 0xbfa4, 0xa537, 0x1f40, 0x0f0b, 0xa47e, 0x4040, 0xbade, 0x401b, 0xb006, 0x0394, 0x2d32, 0xbfba, 0x438b, 0x42b3, 0xb2b0, 0xa573, 0x02fc, 0x1ac5, 0x28b6, 0x43c8, 0x2d0f, 0x3b61, 0x423d, 0x262f, 0x411e, 0x43c5, 0x435d, 0x03e2, 0xae57, 0x1b6e, 0x4238, 0x4110, 0xbf31, 0x2052, 0x38bb, 0x454c, 0x2b69, 0x4009, 0x0ac4, 0x0dad, 0x41f9, 0x1f6e, 0x414d, 0x30e6, 0xb2ea, 0xbfa0, 0x0fbf, 0x42e4, 0xbfe0, 0x400e, 0x061f, 0x00c8, 0xba25, 0x427c, 0x38db, 0xbf5a, 0x44bd, 0x42c9, 0x417e, 0x032e, 0x4205, 0x2143, 0xaf50, 0xbfb2, 0x36cb, 0xb2f9, 0x402c, 0x427a, 0x4350, 0x4315, 0xb0a9, 0x402c, 0x0802, 0x3632, 0x4495, 0x4179, 0x1c57, 0x1bd3, 0xbfd8, 0xa483, 0x3213, 0x4113, 0x4341, 0xb2de, 0xba5c, 0x4246, 0xba0f, 0x0363, 0x3661, 0x41b0, 0xb0ae, 0x4138, 0x4143, 0xba45, 0xb2aa, 0xba01, 0x1824, 0xb049, 0x0a3a, 0x4096, 0xb0ea, 0x274a, 0x43eb, 0xb274, 0xbfde, 0x1aea, 0x417b, 0x0b2f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd2fc5a9a, 0xcbe6f391, 0xc5271121, 0x66c4d03a, 0xce34ff10, 0xe796f79c, 0x37623fb3, 0x4d059abb, 0xf938c94f, 0x8d0659ac, 0xc525e62e, 0xad6bb1ea, 0xe561afeb, 0x8226b8c9, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0xfffec5fe, 0xfec5feff, 0x00807dfa, 0x0100013a, 0x00000000, 0xfefffec5, 0x00000000, 0x0000004a, 0xf938c94f, 0x0002e84e, 0xec28f749, 0xb5aec7a8, 0x000000c3, 0x69c37868, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1d4f, 0xbfc5, 0xb068, 0x40ba, 0x3da9, 0xaeb3, 0x40b6, 0xa066, 0x13be, 0x4028, 0x4049, 0xba69, 0x4602, 0xbfaf, 0x21d5, 0x1879, 0xa68c, 0xa2f5, 0x41f6, 0x0953, 0xbfd0, 0x2aeb, 0x1d85, 0x3904, 0x38c6, 0xb07e, 0x4079, 0x04e6, 0x1634, 0xbf3c, 0x40cd, 0x4237, 0x41ca, 0xbf72, 0x41ee, 0x1cc9, 0xac16, 0x435a, 0xa938, 0x1f53, 0x3c3e, 0xbf9b, 0xb0b9, 0xb0fa, 0x2372, 0x031a, 0xb011, 0xa5c9, 0x2022, 0xb062, 0x40d8, 0xb223, 0x2f62, 0x46a8, 0xb0f8, 0x4201, 0x4398, 0x19ce, 0x404a, 0xa2b4, 0x2542, 0x41c1, 0xbfc1, 0x42af, 0xb065, 0x20ad, 0x1db5, 0x405a, 0x4008, 0x42bd, 0x0eb3, 0x19c3, 0x414f, 0x4288, 0xb2fe, 0x423d, 0x41ff, 0x4285, 0x4249, 0x42a4, 0xbfe2, 0x4226, 0x1c2a, 0x1323, 0xaf72, 0xb008, 0x42a1, 0x14d3, 0x12cd, 0x4655, 0xb2b7, 0xadf9, 0xba1c, 0x40b0, 0xb21d, 0x4045, 0x1ba6, 0x41d3, 0x41ee, 0xbf6a, 0x416b, 0x3ed9, 0x424d, 0xb238, 0x420e, 0xb22f, 0x4013, 0x3404, 0x41bc, 0x08d8, 0xba7f, 0x407c, 0x1924, 0x424b, 0xb2cc, 0xb232, 0x0896, 0x2a78, 0x43b1, 0x437c, 0xa967, 0x4546, 0xbf8f, 0x4039, 0xa773, 0x40f6, 0xb236, 0xb2e8, 0x43fa, 0x1b34, 0x420e, 0xa787, 0x43a8, 0x2f7a, 0x40b1, 0x46d1, 0x1c90, 0x41ad, 0x4200, 0xba78, 0xbf9f, 0x406d, 0x23c7, 0x0b7f, 0xb2ed, 0x3622, 0x1dd2, 0x425a, 0x43bc, 0xb0ea, 0x4544, 0xa6be, 0x460e, 0x1722, 0x42e9, 0x273e, 0x4366, 0x4691, 0xa936, 0x41a2, 0xbf2e, 0x3231, 0x42a6, 0x43da, 0x2f37, 0x1d1a, 0xb26c, 0xbaff, 0x404c, 0x4132, 0x4076, 0x43fc, 0xb218, 0xba40, 0x4337, 0x385b, 0xb265, 0xb018, 0x462a, 0xba63, 0x41e6, 0xbf59, 0x4289, 0x1fa7, 0x40d7, 0xba59, 0xba5a, 0x0422, 0x07af, 0x428d, 0xb211, 0x4355, 0x431d, 0xbf51, 0x39c4, 0x1ee0, 0x0119, 0x414d, 0xa186, 0x1809, 0x42b4, 0x4065, 0xb2db, 0x43e6, 0xb26c, 0x45b6, 0x1be2, 0xb2ac, 0x42a9, 0x4067, 0xaf6c, 0xb2df, 0xbf1b, 0x4299, 0xb015, 0xb064, 0x44c9, 0x43f8, 0x2914, 0x1c11, 0x2d02, 0x1ebc, 0x43bb, 0x400e, 0x1cb3, 0x4246, 0xb00a, 0xb003, 0xbfb0, 0x1909, 0x3166, 0x30fd, 0xb25f, 0x1c45, 0x1e5e, 0xba68, 0xbf5a, 0x43a4, 0x1a5e, 0x4080, 0x050c, 0xbfc0, 0x14c5, 0x41c0, 0x438e, 0x416e, 0xba10, 0x1a22, 0xb0c6, 0x3aaf, 0x43a3, 0x0345, 0xbf72, 0xb248, 0x44c3, 0x42a4, 0xba19, 0x1e6b, 0x4475, 0x4347, 0xb29a, 0xb285, 0xb2dc, 0x40e7, 0x18bd, 0x4022, 0xb26f, 0xb01e, 0x4329, 0x4592, 0xa298, 0x433b, 0x2c82, 0x413d, 0xbf6e, 0x4554, 0x418b, 0xb0ab, 0x1a76, 0x4015, 0x4050, 0x4613, 0x42a6, 0x435b, 0x467c, 0x0354, 0x3662, 0x093c, 0xb2a8, 0xb2f2, 0x42db, 0x41ff, 0x4030, 0x420c, 0xb063, 0x4580, 0xbf1f, 0x07e2, 0x408b, 0xb0af, 0xb096, 0xba7c, 0xb273, 0x4336, 0x4628, 0x2ede, 0x4249, 0x41fa, 0x3d4c, 0x43b4, 0x40b6, 0x4132, 0xb2c7, 0x138e, 0x2c2a, 0xafa8, 0xb284, 0x1fcc, 0xbad1, 0xba35, 0x40d2, 0xae8d, 0x40b7, 0xb0ef, 0xb245, 0xbf88, 0x1aa3, 0x36d9, 0x4217, 0x0d64, 0x4247, 0xb07b, 0x4093, 0x4398, 0xbf95, 0xbaeb, 0x41db, 0x009d, 0x2ac3, 0xb298, 0x3a13, 0x4361, 0x072c, 0x01ba, 0x1b20, 0x1bc5, 0x404b, 0x0f91, 0x4129, 0xb01a, 0x0794, 0x4001, 0x406a, 0x273d, 0x40a3, 0x4021, 0x441a, 0x418d, 0x4161, 0xbf2f, 0x40d9, 0xbfe0, 0x0fdc, 0x1ae0, 0x37ff, 0x42ed, 0xb0a6, 0x2e0f, 0xa6c5, 0xb0ba, 0x19cc, 0xb21f, 0x220f, 0xb2d3, 0x1dbf, 0x433e, 0x44f4, 0x36f5, 0x422f, 0x1a45, 0x052c, 0x40fd, 0x4285, 0x4294, 0xbae3, 0xba73, 0x42e7, 0xbfab, 0x40f3, 0xba5f, 0x168e, 0x405c, 0x4200, 0x4267, 0xba7d, 0x42ca, 0xb294, 0x42a6, 0x43b9, 0xb2d1, 0x40b4, 0xa04c, 0x18b5, 0x4627, 0x3b0d, 0x4292, 0x460c, 0x4133, 0xb2c5, 0x40f7, 0x42cd, 0xbf21, 0x1c7e, 0x2cb5, 0x2027, 0x4689, 0xb20f, 0xbfd4, 0x1f88, 0xa0aa, 0x1a08, 0xa41d, 0xb2dc, 0x1cae, 0x423e, 0x40e5, 0x43ea, 0x182c, 0xbadd, 0x4349, 0x41b3, 0x1a46, 0x4545, 0xbff0, 0x19b3, 0x3943, 0x0759, 0x36e9, 0x42ff, 0x27f6, 0x0bda, 0x22f4, 0xbf9e, 0x405a, 0x2a35, 0xb2bf, 0x432c, 0x0307, 0xab18, 0x4128, 0x4350, 0x1eae, 0xac20, 0x411d, 0xb2bc, 0x39f4, 0x43b9, 0x4065, 0x1f99, 0x1fc1, 0x4245, 0xa9e3, 0x456e, 0x41ae, 0x4251, 0x4292, 0x4277, 0xbf3a, 0xba5e, 0xb205, 0xb27b, 0x40ca, 0xb233, 0xb037, 0x411f, 0x2fdc, 0xba7a, 0xb0d7, 0x42fa, 0x2a0d, 0x29bf, 0x46ad, 0xb062, 0xa1f5, 0x1f2f, 0x434b, 0x41f3, 0x2f98, 0xbf72, 0x41c9, 0x40da, 0xa73f, 0x4388, 0x42bf, 0xb201, 0x342d, 0x41d8, 0xb2e2, 0xb2a8, 0x2e8d, 0x4096, 0xb280, 0xba42, 0x40fd, 0x418e, 0x4616, 0x1902, 0x4162, 0x1a4b, 0xbfbc, 0xb22c, 0x4108, 0x432f, 0x46bb, 0x46c9, 0xba32, 0x1a44, 0xae82, 0x45c4, 0xba4d, 0x12e4, 0x4273, 0xbaf9, 0xbfd0, 0x43ab, 0x193e, 0x46e9, 0xb09f, 0x116a, 0x4062, 0xbf4b, 0x3063, 0x4186, 0x4022, 0x4413, 0x1e60, 0x4221, 0xb067, 0x42cc, 0x43b5, 0xbaec, 0x2f0a, 0xba21, 0x40f1, 0xb021, 0x42c5, 0x41b5, 0x4408, 0x019a, 0x1bce, 0x4445, 0xbade, 0x3ba5, 0x1b4c, 0x3db9, 0x407c, 0xba29, 0x438a, 0x28d3, 0xbf36, 0x464d, 0x426d, 0x3ef8, 0xb2d1, 0x41b4, 0x1b02, 0xb2b2, 0x18f4, 0x436a, 0x1c88, 0x3013, 0xb2e5, 0xb0fb, 0xb20a, 0x1f07, 0xaaf3, 0xb09b, 0x4150, 0xbf44, 0x425f, 0xb26c, 0xba7d, 0xbfda, 0x1a16, 0x2eb2, 0x4127, 0x4358, 0xbf25, 0x1e43, 0xb26e, 0x46b5, 0xb2f1, 0xb20f, 0x4190, 0xba77, 0xb2d0, 0xb256, 0x192d, 0x1c18, 0x0155, 0x4019, 0xbaf7, 0x424e, 0xba74, 0xb28c, 0xba1e, 0x1a15, 0xb084, 0x3be4, 0xbf44, 0x4244, 0xba70, 0x4109, 0x0237, 0xb2fb, 0x43fb, 0x4113, 0xbae0, 0xa7a3, 0x4552, 0x422f, 0xb291, 0xb263, 0xbfd4, 0xb22b, 0x2765, 0xbaf4, 0xb049, 0x1a38, 0x405e, 0x1b6f, 0xb25e, 0xba65, 0x0639, 0x4581, 0xbaca, 0x41a7, 0x1c50, 0x4030, 0x4272, 0x435e, 0xbf43, 0x4225, 0x180c, 0x42f2, 0x416f, 0x429f, 0xbad2, 0x40bd, 0x448d, 0x417e, 0xb220, 0x10d2, 0xbf06, 0x4068, 0x3614, 0x1307, 0x432a, 0x46d0, 0x4233, 0x41c4, 0x46f3, 0x1c04, 0x40a0, 0xaa36, 0xbaf7, 0xb2e8, 0x425c, 0x42ba, 0x3767, 0xb20e, 0x400b, 0xb087, 0x1886, 0x2c3e, 0x2840, 0xb06a, 0xbf05, 0x43a1, 0xb250, 0x4543, 0x412c, 0x22a1, 0x41c1, 0x4133, 0x1976, 0x404f, 0x425a, 0xaad6, 0x41ed, 0x40a4, 0x4333, 0x4278, 0xb04c, 0x41c5, 0x42c3, 0x336a, 0x4004, 0x411b, 0xb24b, 0x4399, 0x43b7, 0x4098, 0x4180, 0xbf7b, 0x41d0, 0x423c, 0x4301, 0x416c, 0x432b, 0xb2a3, 0xb2a2, 0xb0d9, 0x42df, 0x1237, 0x43d1, 0x437d, 0x41fd, 0x4352, 0xba00, 0xbf6b, 0x1c45, 0x0c59, 0x21e6, 0x1d8a, 0x1a4b, 0x43cf, 0x098f, 0x2f7b, 0x4307, 0xba68, 0x4093, 0xb2cf, 0x4106, 0x19a1, 0x0fd2, 0xbac5, 0x1a26, 0x43da, 0x4279, 0x40dd, 0xbf01, 0xb0b5, 0x41e6, 0x3dc6, 0x406f, 0xb258, 0x19fe, 0x40e1, 0xba16, 0x4267, 0x4093, 0xb2e8, 0x42c0, 0xb282, 0x4083, 0xb269, 0x40f8, 0x3805, 0xbfc3, 0x437a, 0x425d, 0xb0f4, 0x2503, 0x4074, 0xb021, 0xbfaa, 0x42f4, 0x413d, 0x42c7, 0x0bf2, 0xb06c, 0x1949, 0x40e4, 0x40b4, 0x1a5d, 0xbf13, 0xb2a1, 0x230b, 0xb0e9, 0xb294, 0x46b8, 0x2508, 0xb09d, 0xb24f, 0xb0d1, 0xad5e, 0xbfb2, 0x4037, 0xba4b, 0x1f76, 0x1ed9, 0x412a, 0xbf90, 0x4694, 0xb0aa, 0x39bf, 0x4070, 0x4492, 0x0b7e, 0xa95a, 0xb08c, 0x0ebd, 0x4381, 0xa285, 0x3e4b, 0xbf07, 0x4159, 0xae4d, 0x4269, 0xb277, 0x46d3, 0x29f9, 0x45be, 0x4362, 0x1925, 0xba2a, 0x4573, 0x0196, 0xb2ea, 0x434c, 0xb26d, 0xbae2, 0x1e42, 0x411c, 0x416e, 0x4259, 0xb28b, 0xbfe2, 0xbac6, 0x437e, 0x076e, 0x41ba, 0x407a, 0x45f1, 0x4205, 0xb289, 0x1840, 0x4027, 0x4190, 0x408f, 0xb23c, 0x419a, 0x001e, 0xb0b6, 0x4081, 0xba25, 0xa216, 0xb2ff, 0x405c, 0x18ad, 0x426a, 0x4179, 0xb21a, 0xbf8a, 0x4312, 0x44d3, 0xba1d, 0x42a8, 0x283f, 0x4169, 0x13bc, 0xb00d, 0xaac5, 0x403a, 0x439d, 0xaaca, 0x4369, 0x1c07, 0x4256, 0x434e, 0xb054, 0x1634, 0xbfdf, 0x1ab7, 0x3f94, 0x1b06, 0x42f4, 0x407e, 0x058b, 0x409a, 0xba4b, 0x180e, 0xaf82, 0x43f8, 0x161d, 0x44e0, 0xbf14, 0x4215, 0x1acc, 0x401a, 0x402e, 0x41e4, 0x4678, 0x1d6b, 0x1d43, 0x427b, 0x015f, 0xaf31, 0x41de, 0x0fa6, 0xbf45, 0xb2c8, 0x417c, 0x438b, 0x45be, 0x4334, 0xba6f, 0x429a, 0xbf00, 0xb2ad, 0x4342, 0x4154, 0xbf26, 0xb2ad, 0x4347, 0x16db, 0x4423, 0x40ac, 0x4384, 0xb270, 0xb005, 0x42b8, 0x4305, 0xba31, 0x1d89, 0x2ca0, 0xb21f, 0xba55, 0xba38, 0xb26a, 0x30c9, 0xb20e, 0xb224, 0x4069, 0x431a, 0x43fa, 0xa8e2, 0x41a8, 0x40f1, 0x4253, 0x1eae, 0xbf0b, 0x405d, 0x3e93, 0x43ec, 0x45a3, 0xb267, 0x1c8a, 0x19fc, 0x417c, 0xb214, 0x4350, 0x4083, 0x2458, 0x42cc, 0x40f4, 0x40f0, 0x31dd, 0x43b0, 0xb2bf, 0x419f, 0xb2bb, 0x41f0, 0xbfbd, 0x42bd, 0x1c0f, 0xb06e, 0x4085, 0x43ea, 0xb2c7, 0xb0c4, 0x428a, 0x4294, 0xb2ea, 0x44d1, 0x4374, 0x4361, 0x41bf, 0xa12e, 0x02fa, 0xbac8, 0xbf15, 0x4004, 0x3b3a, 0x05be, 0x41c9, 0x40ab, 0xbadf, 0x3b7c, 0xb075, 0x2c0e, 0x02fb, 0x18b4, 0x19f6, 0x43eb, 0xb254, 0x431d, 0x40e1, 0x40f1, 0x0f2a, 0x4248, 0xbf95, 0xa360, 0x4095, 0x1a15, 0x4387, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x14152dbc, 0xac42cb3d, 0x80d39deb, 0xf842bf41, 0xc80af0cb, 0xb2924a15, 0xe20c7a41, 0xd0c9f409, 0xfd5e5b81, 0x4d1dced7, 0x6694566b, 0x954b09bd, 0x823d8b85, 0xd32e765b, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0xffffe7a0, 0x00001860, 0x0000000f, 0x00001958, 0x00000000, 0x0000186f, 0xffc00000, 0x00000000, 0x00000000, 0x669456ff, 0x6694566b, 0xcd28acd6, 0x00000000, 0x000060e9, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4379, 0x2480, 0x4389, 0x4462, 0xb2b1, 0xb229, 0x4064, 0xb2ce, 0xbf8a, 0xb233, 0x4052, 0xbafd, 0x40a1, 0x047d, 0xb2a8, 0xbac2, 0x43fc, 0x41d7, 0xb0c2, 0xba50, 0x41ac, 0x24ec, 0x1cde, 0x4678, 0xb0e8, 0x46f0, 0x438c, 0x4034, 0x3f2b, 0xa9c5, 0xa5d8, 0x1472, 0xbfc6, 0xb2f0, 0x223e, 0x0dc1, 0x408e, 0x4220, 0x417f, 0xa4bb, 0x4347, 0xa2fa, 0xbf8a, 0x1a45, 0x0d2e, 0xb2ed, 0xbaf2, 0x41cf, 0x437f, 0xb077, 0xb271, 0xb256, 0x4138, 0x0a13, 0x439d, 0x275a, 0xb0ad, 0x4094, 0x4133, 0xbfbc, 0xb0d7, 0x290e, 0x43ec, 0x41df, 0x259f, 0x402a, 0x42c6, 0x4148, 0xa7d3, 0x2e2c, 0xb2c3, 0xb223, 0x439b, 0x4209, 0x1e5b, 0xb213, 0x421f, 0x0902, 0xb2b4, 0xba54, 0x4340, 0x1988, 0xbfcb, 0x43b0, 0xba07, 0x436e, 0x41b2, 0xb0ff, 0x43f6, 0x42a5, 0x29d5, 0x4325, 0x22f7, 0x403b, 0x4363, 0x1a03, 0x08bc, 0x1a8e, 0x458a, 0x1328, 0xba5c, 0x315f, 0x40af, 0xbadc, 0x1b66, 0x1bfa, 0x1a5d, 0xb06a, 0x3d85, 0xb077, 0x434c, 0x40c6, 0xbf59, 0x40ec, 0x40a9, 0x4491, 0x4274, 0xba68, 0x1b59, 0x2148, 0xba32, 0x288d, 0x41ba, 0x2e3b, 0x4561, 0xbf5a, 0x42e1, 0x445d, 0x4181, 0x1f97, 0x436c, 0xb005, 0xba1d, 0xab7e, 0x38a1, 0x3f8a, 0x4139, 0x463c, 0xb047, 0xbafd, 0x43c1, 0x40a5, 0x429c, 0xba1c, 0xad94, 0x420a, 0xb275, 0xba3e, 0x447c, 0x1b00, 0xba5e, 0xb282, 0xbf78, 0xb226, 0xb293, 0x1e34, 0x1cf0, 0xae4a, 0xbfa7, 0x40d6, 0xba23, 0x46a0, 0x2172, 0x44eb, 0xbf2f, 0x4095, 0x42cb, 0x2e23, 0x312c, 0xb2e8, 0x1a06, 0x431b, 0xb2b4, 0x42ae, 0x424d, 0xb2ec, 0x1bbd, 0x45f2, 0x137e, 0x204b, 0x18e0, 0x40be, 0xb2c0, 0x25a8, 0x4319, 0xb277, 0x2318, 0x4181, 0xb29e, 0x43a4, 0x1b2b, 0xa73b, 0xbfc7, 0x074b, 0x2a98, 0xb07d, 0xba4b, 0xb256, 0x4088, 0xa8c4, 0xa053, 0x4448, 0x41ca, 0x42f1, 0xb239, 0xbf2d, 0x40a8, 0x3a69, 0x2ffe, 0x3bec, 0x23ee, 0x43f1, 0x2309, 0x43c7, 0x431c, 0x42a9, 0xb29d, 0x41dc, 0x39ff, 0x412f, 0xb2c0, 0xba55, 0xa7bf, 0x41cc, 0xbad7, 0x4104, 0x018a, 0xb2bf, 0x43c6, 0x4170, 0xb28f, 0x41f2, 0x409b, 0x4372, 0xb007, 0xbf2e, 0x01f0, 0xba38, 0x409d, 0xb0ad, 0x4363, 0xb042, 0x2a86, 0x4140, 0x415f, 0xbfb8, 0x41e5, 0x28f2, 0x4035, 0x42ce, 0xb0ae, 0x43d6, 0x4357, 0x44a9, 0x40a8, 0x4117, 0x433b, 0x4213, 0x3385, 0x09e7, 0xbf80, 0xb063, 0xaa00, 0xb013, 0xbf90, 0x409a, 0x4007, 0xbf04, 0x4060, 0x4377, 0xb0f3, 0xb0ab, 0x4279, 0x1a56, 0xba2b, 0x1931, 0x434c, 0xb090, 0x0500, 0x1b0f, 0x1a85, 0x4681, 0x3e3a, 0x43ce, 0xb2fa, 0x430b, 0x4043, 0xbfb0, 0x3a7d, 0xbf76, 0xb009, 0xbace, 0xbf70, 0x403a, 0x438c, 0x41fd, 0x433c, 0x4202, 0x2d3d, 0x4277, 0x428e, 0x4156, 0xb2d2, 0xbf68, 0x3ab5, 0x409c, 0x1f03, 0x1d6d, 0x42f8, 0x0aaf, 0x2d64, 0x2022, 0xbf12, 0x137d, 0xba6f, 0x45a2, 0xa7a7, 0x40f5, 0x45d3, 0xb2e4, 0x3f2e, 0xba5e, 0x1df2, 0xafc9, 0x42be, 0x4683, 0x4001, 0xba00, 0xb2ae, 0xba1c, 0x089d, 0xb052, 0x1bb8, 0x198b, 0x2162, 0x1f49, 0x3d1b, 0x46fa, 0xbf68, 0x4018, 0xa1e0, 0x40fa, 0x1bdd, 0xbf7c, 0x1a6b, 0x435a, 0xb240, 0x434e, 0x2af4, 0x438b, 0x40f3, 0x4049, 0x43df, 0xb231, 0x0e09, 0x434a, 0x1a40, 0x4079, 0x34eb, 0x41b5, 0x4033, 0x431b, 0x28b1, 0x4575, 0xb022, 0x4221, 0x41b5, 0xbf05, 0x1e57, 0x41d7, 0xb26b, 0x40e3, 0x18d7, 0x40c4, 0x4204, 0xb249, 0x1c5a, 0xbfa4, 0x4063, 0x36b9, 0x4414, 0x42ca, 0x1c8b, 0x42d0, 0x458c, 0xb04f, 0xbf79, 0x4369, 0x1d45, 0x383b, 0x41af, 0x132e, 0x1a3c, 0x0552, 0x467b, 0xba03, 0xb09d, 0xb0ed, 0x43a8, 0x2abd, 0x181a, 0x43c3, 0xb2f9, 0xaa2e, 0x38d6, 0xba77, 0x427b, 0x41f9, 0xbf21, 0x42e7, 0xb282, 0x4224, 0xb2e2, 0x1fab, 0x40f1, 0xbfd0, 0xbf84, 0x01fc, 0x42d2, 0x4379, 0xb000, 0x4246, 0xbfbd, 0xba44, 0x42ec, 0x43e8, 0x0d71, 0x238f, 0xbfd8, 0x46d4, 0x4068, 0x430e, 0xa79c, 0xa11e, 0x4375, 0x0698, 0xb241, 0x4343, 0xbaf7, 0x1af4, 0x4399, 0x4227, 0x43c3, 0xb2e3, 0xb08b, 0x39ed, 0xb07b, 0xb2d1, 0x4162, 0x425e, 0x0066, 0x4046, 0x4025, 0x0499, 0xbf48, 0x430d, 0xba16, 0x215d, 0x2992, 0x4078, 0xb226, 0x126c, 0x406e, 0x43cc, 0xb23f, 0x43db, 0xbf37, 0x2e15, 0xbaec, 0x4356, 0x3c9d, 0x4393, 0x245d, 0x411b, 0xbf00, 0x2629, 0xb21e, 0x423e, 0x27ca, 0x4001, 0x46e2, 0x4191, 0x439b, 0xba57, 0x465a, 0x1d3a, 0x1cb4, 0x4377, 0xbfc5, 0x40f1, 0x43b1, 0x42ea, 0x4249, 0xba57, 0x439e, 0x4263, 0x42cf, 0x4204, 0xb296, 0x4022, 0xa2ce, 0x4155, 0x07a1, 0x4005, 0x41bc, 0x430f, 0xbf0c, 0x4388, 0x1383, 0xb0da, 0x2956, 0x44e3, 0x432a, 0x4061, 0x421b, 0x4215, 0x455a, 0x41fa, 0x1d49, 0x4165, 0x40b8, 0x1258, 0x208a, 0xbf12, 0xa283, 0x1d11, 0x41bf, 0xbf55, 0xba7b, 0x4204, 0xba24, 0xbff0, 0xbf61, 0x40e1, 0x1ed0, 0x43c5, 0x42c8, 0x4215, 0xbad5, 0x4220, 0xb029, 0x439a, 0xb07b, 0xa83b, 0xb2ef, 0x1fb5, 0x4298, 0xb071, 0x40ef, 0x42e1, 0x2792, 0x1a2b, 0x1fb9, 0x32df, 0x209b, 0x3ecf, 0xba10, 0x409e, 0xb253, 0x4041, 0x196f, 0x443c, 0xbfd9, 0x189c, 0x441c, 0xbac0, 0xaa98, 0x0b73, 0xb229, 0x42d2, 0x4225, 0xbff0, 0x4226, 0x404c, 0x43b7, 0x4039, 0x4348, 0x411d, 0x40d0, 0x401b, 0x4187, 0xa008, 0xb2fa, 0xa858, 0xbf35, 0x420b, 0xb2c3, 0xb2fb, 0xb03c, 0x4063, 0xb0e0, 0xbfb0, 0x1b44, 0xbf5d, 0x4034, 0x4427, 0x43a2, 0xba5a, 0x46d8, 0x46d1, 0x3045, 0x33da, 0x43bb, 0x4018, 0x0618, 0x4192, 0x4357, 0x430e, 0x414b, 0x3656, 0xb2b3, 0xb03f, 0xbf90, 0x460b, 0x43c5, 0xbf1c, 0x4368, 0xba32, 0xbfc0, 0x415b, 0x4113, 0x1e66, 0x1eef, 0x4217, 0xba3a, 0xb280, 0x4355, 0x44a0, 0x4340, 0xb097, 0x423c, 0x43f6, 0x2cf6, 0xb250, 0x4627, 0x42cc, 0x2090, 0xba59, 0x4277, 0x24e1, 0x1f43, 0x14a1, 0xa852, 0xbfb6, 0x45f4, 0x46ec, 0x2c9b, 0x3866, 0xb2a8, 0x1d24, 0x4311, 0xb051, 0x4082, 0x2e93, 0x40f7, 0x40e3, 0xb010, 0xac6c, 0x1928, 0xbfdd, 0x0c95, 0xba78, 0x4409, 0x41af, 0x3367, 0x4204, 0x436a, 0x1d3c, 0x4043, 0xb0f9, 0x417f, 0x42ea, 0x42c7, 0x1cd8, 0x1db7, 0xa9fc, 0x1d0c, 0xbf60, 0x4589, 0x41e8, 0x25ec, 0x422c, 0xb239, 0x3b17, 0x45f5, 0xbf7b, 0x0725, 0x40b4, 0x4005, 0xb2f4, 0xbff0, 0x40a6, 0x412e, 0x4167, 0x30c3, 0x403b, 0xbfb8, 0x43c5, 0x402b, 0x1fa2, 0x3aca, 0xbfc5, 0xbafa, 0x4232, 0x4632, 0xb2b6, 0x4639, 0x4061, 0x4256, 0xb2e8, 0x17be, 0x4346, 0x4313, 0xbfb6, 0xba6f, 0x4103, 0xb030, 0x4196, 0xb03a, 0x0a90, 0xbf90, 0x4610, 0x40ae, 0xba66, 0x1d15, 0xa4b5, 0x4210, 0x42a7, 0xbac1, 0xbfb4, 0xaa8b, 0xb2e7, 0x405f, 0x4013, 0xa7f6, 0x1a48, 0x38c2, 0x420c, 0x210c, 0x40ad, 0x4309, 0x2e0a, 0xba29, 0xbad8, 0x0cb0, 0x4333, 0xba55, 0x1e96, 0xb21b, 0x2b22, 0x3f84, 0xbfa1, 0xa0a2, 0x419a, 0xb2c8, 0x40c3, 0x42ef, 0x4132, 0x41f4, 0xbfc6, 0x4650, 0x4322, 0x341e, 0xbf8b, 0x426b, 0x46b2, 0xb271, 0x422c, 0x4205, 0x4329, 0x419d, 0xa66e, 0xb28b, 0x409d, 0x3f5c, 0x37f3, 0x4135, 0xb25e, 0x4032, 0x4368, 0x44ad, 0x4089, 0x468a, 0x4309, 0x19f0, 0xb2ec, 0x419a, 0x4642, 0xaade, 0x221c, 0xbf12, 0xa8f7, 0xb251, 0x43cd, 0x4189, 0xbadc, 0x0a21, 0xb02b, 0x43a2, 0x4136, 0xbf07, 0x005c, 0x1ab6, 0x4192, 0x4084, 0x36b5, 0xb239, 0x19f8, 0xb236, 0x1a91, 0x40f4, 0x1c8b, 0x4142, 0x015e, 0x0894, 0x4229, 0xb090, 0xb051, 0x4073, 0x42f0, 0x1f44, 0x0dc9, 0x447f, 0xba48, 0x4150, 0x1af6, 0xba5e, 0x2fa7, 0xb088, 0x421c, 0xbf92, 0x43b1, 0x4007, 0x1992, 0x411c, 0x4021, 0x4004, 0x26a4, 0x0e50, 0x43ad, 0x3588, 0x1d58, 0x426c, 0xb0c6, 0x2222, 0xb2ef, 0x4440, 0xbf59, 0xb05b, 0x42fb, 0xb277, 0x4314, 0xbacf, 0x17b8, 0x4192, 0x40c9, 0x1d08, 0x43a8, 0xba2a, 0xb069, 0x43b9, 0x44d5, 0x10b0, 0xb2cd, 0x46e9, 0x4054, 0x4417, 0x2d2c, 0x42fb, 0x4094, 0xbf9c, 0x4598, 0x41d7, 0x4257, 0x4284, 0x4277, 0xaf28, 0xba78, 0xb0d1, 0x40f2, 0x43d2, 0x41e7, 0xb25d, 0x420c, 0x40e6, 0x1fa1, 0xb00c, 0x4066, 0x40ee, 0xbf08, 0x4387, 0xbf70, 0xb048, 0x42af, 0xbac4, 0x44cb, 0x40dd, 0x2044, 0x4247, 0x1dda, 0x250b, 0xbadd, 0x45a8, 0x4195, 0xbf38, 0x42d6, 0x4613, 0xb26d, 0x4581, 0x2f5e, 0xb2c5, 0x464e, 0xba66, 0x41c2, 0x2431, 0x4301, 0x4063, 0x2637, 0x46a0, 0xb059, 0x1d7b, 0x1f71, 0xb089, 0xbf15, 0x4111, 0x4372, 0xba3f, 0x1869, 0x13c5, 0x42cd, 0xb216, 0xb2b9, 0x4324, 0xbf70, 0x418d, 0x405a, 0x130b, 0xbfd7, 0x43e3, 0x131f, 0x42d2, 0x42b0, 0x0eb5, 0xba07, 0xb261, 0x4173, 0x4124, 0x3d11, 0x410c, 0xb268, 0xb23b, 0xb219, 0x4044, 0x011f, 0x33bd, 0x1f80, 0xb2c8, 0x4133, 0x4352, 0x1aad, 0xbfc2, 0x3647, 0xb2ef, 0x1964, 0x41f0, 0x2fd2, 0x4291, 0x438f, 0x1518, 0x4184, 0x4138, 0x1c5b, 0xa1de, 0xae02, 0x40f0, 0xbfdc, 0xba47, 0x40eb, 0x42f7, 0x2577, 0x1ba5, 0x431d, 0x4227, 0x1e88, 0xbf28, 0x1cfb, 0x409d, 0xbf5d, 0x4039, 0x4328, 0xb23c, 0x0f79, 0xb03e, 0x4177, 0x44c3, 0x4334, 0xbf4d, 0x40bc, 0x4343, 0x402a, 0x40c6, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2facc260, 0xdcf5a5c6, 0x996babf4, 0xcf26a6f8, 0x2df7e7e5, 0xee4ea13b, 0xf856da3e, 0x6c32216c, 0x0f115238, 0x504a66b5, 0xb0cc8c19, 0x7ac767c7, 0x4550fd6f, 0x45a84035, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x00001b12, 0x00000000, 0x60000d99, 0x00000003, 0xfffc0000, 0xd2bdaee8, 0x0000116a, 0x45a84a12, 0x00000031, 0x45a848bd, 0x00000000, 0x8af9467f, 0x45a844ad, 0x45a84b01, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb2fe, 0x3eee, 0x4080, 0x4136, 0xb2e1, 0xb2b8, 0xba6e, 0x4298, 0x4164, 0x427d, 0x42cc, 0x416f, 0xbf4b, 0xba4a, 0x1e12, 0x43ca, 0x1c32, 0x41dd, 0x40be, 0x4374, 0x401a, 0xb0ad, 0x1506, 0x19bc, 0x11fe, 0xb298, 0x44c2, 0x1c97, 0xbf29, 0x4123, 0x4052, 0x4092, 0x4160, 0x209c, 0x42d7, 0x413b, 0x4333, 0xad04, 0x1147, 0x4054, 0x437a, 0xbaf8, 0x427a, 0x4197, 0x2636, 0xbfa2, 0x1958, 0x41b0, 0x40a3, 0x1ee7, 0x4132, 0x4306, 0x404f, 0xba70, 0x432f, 0x4551, 0xb0d9, 0xa5fd, 0x2460, 0x06fc, 0x3f2e, 0x29f1, 0x4233, 0x4318, 0x1044, 0xb277, 0xbf09, 0xb28f, 0x439f, 0x43ba, 0x06a4, 0x43b8, 0xba39, 0x1ebb, 0xb21f, 0x41e4, 0x4280, 0xbfb9, 0x3ebe, 0x11aa, 0x1b41, 0x1f7b, 0x4407, 0x2a7a, 0x4211, 0x41bb, 0xb0ae, 0x418b, 0x1665, 0x4177, 0xbfc0, 0xb280, 0x1905, 0x4267, 0xbfc6, 0x43a0, 0x42e5, 0x45b4, 0xba2e, 0x43a3, 0x2486, 0x40eb, 0x4297, 0x23fa, 0xbf71, 0x41e5, 0x4449, 0xb051, 0x0d5b, 0x4027, 0xba23, 0x3126, 0xbf57, 0x40b8, 0x41cf, 0x464f, 0x4082, 0x4075, 0x416a, 0x18df, 0xb0e9, 0xba0a, 0x4172, 0x14b5, 0x4394, 0x4232, 0x0ff1, 0xbf96, 0x406f, 0x43d7, 0x42e3, 0x0dc2, 0x4025, 0x46a0, 0x1d27, 0xb2f3, 0x42e4, 0x4099, 0x1a00, 0xb0b2, 0x1b48, 0x40a8, 0x420f, 0x43cd, 0xb270, 0xbf92, 0x412d, 0x4178, 0x4400, 0xb2f4, 0x40f1, 0x409f, 0x124a, 0x4584, 0x4312, 0x40f4, 0x1b70, 0x441e, 0x1866, 0x40ba, 0x4337, 0xbf1f, 0xb235, 0x41a4, 0xbf80, 0xb250, 0x40f9, 0xb2f5, 0x4237, 0xba2e, 0xbf1a, 0x433c, 0xb2b5, 0xbad6, 0xb0c9, 0xbf41, 0x43b3, 0x43c4, 0x3c17, 0x414f, 0x4194, 0xaf3b, 0x42cb, 0x1b27, 0x41bf, 0x418c, 0x050e, 0x44e5, 0x41ac, 0x45db, 0x431e, 0x43e8, 0x421e, 0x41f6, 0x18a1, 0x417f, 0xbf7f, 0x412b, 0x1dc5, 0xbacb, 0x46e1, 0x1321, 0x1e34, 0x42c6, 0x413f, 0x3725, 0x3245, 0x43cd, 0x1de1, 0x40d4, 0x35bb, 0x42c1, 0x4100, 0x4438, 0x028c, 0xb208, 0x4449, 0xbae6, 0xb08b, 0x0936, 0x433c, 0x412e, 0xb095, 0x41b8, 0x1d8c, 0x41b3, 0xbfa8, 0x4616, 0x4043, 0x45b3, 0x4214, 0x1ceb, 0x4133, 0x410a, 0x1c15, 0xbf16, 0xb2fc, 0xba2d, 0xab9f, 0x4037, 0x1f6d, 0x2f24, 0x4309, 0x4431, 0xbace, 0xba3d, 0x43cc, 0xb2e1, 0x137d, 0x43d5, 0x3f6c, 0x41e8, 0x3089, 0x42bf, 0x45d3, 0x4049, 0xbf89, 0x0e07, 0x2da2, 0x427e, 0x43c9, 0xb2cd, 0x1f71, 0xadba, 0xb2e3, 0xba77, 0x4250, 0x46fc, 0x24c9, 0x43ce, 0xb02f, 0x468d, 0xba17, 0x2ee2, 0x448b, 0xbfbd, 0x195a, 0x41a3, 0x33fb, 0x437a, 0xbf65, 0x4004, 0x43ab, 0x42c3, 0xaa5c, 0x2944, 0x1baf, 0x2a27, 0x4266, 0xbf91, 0x41c6, 0x42b8, 0xba74, 0x41b7, 0x355c, 0xbae4, 0x405f, 0x40be, 0xbaf5, 0x437e, 0xbac0, 0x4204, 0x436f, 0x46f3, 0x1f70, 0x4277, 0x4053, 0xbf80, 0xb2ee, 0xbfd5, 0xb2b0, 0x41e9, 0x424b, 0x4135, 0x4561, 0xbae0, 0xb200, 0xba21, 0x4360, 0xb0c1, 0xb207, 0x40a1, 0x4373, 0xb09e, 0x0929, 0x3cad, 0x1997, 0x411a, 0xb020, 0xbfa2, 0x3fca, 0xb23d, 0x087e, 0x055d, 0x4149, 0xb289, 0x2152, 0x1be1, 0x43b1, 0x40a1, 0xbf0d, 0x431d, 0xa7b2, 0x3d9a, 0x40a1, 0x42cf, 0x43e4, 0xbfc2, 0xaf46, 0xb23e, 0xb2e6, 0xb279, 0xa8af, 0xb076, 0xb0e7, 0x184a, 0x41bf, 0x42c8, 0x3d7b, 0xb02c, 0x1bd0, 0x1866, 0x14fe, 0x19c0, 0x409b, 0x435c, 0x4169, 0x405d, 0x4556, 0x4052, 0x435b, 0x412b, 0xbf73, 0x1ed8, 0x419f, 0x198b, 0x43da, 0x42f3, 0xba35, 0x1db6, 0xb012, 0x43d6, 0x0534, 0xabbe, 0x2ce2, 0x427f, 0x435f, 0x4169, 0x4265, 0xbf58, 0xb280, 0xbf90, 0x404e, 0xba75, 0x4162, 0x4105, 0xba5d, 0x40c4, 0x4044, 0x3603, 0x43c0, 0xb2ba, 0xb0b5, 0x247f, 0xba45, 0x4330, 0xb21a, 0x40ba, 0xba1d, 0xb24f, 0x43e3, 0xb09c, 0xbf5e, 0xb07d, 0xba5a, 0xb093, 0xb244, 0xba35, 0x419d, 0x4303, 0x09ce, 0x1af4, 0x4584, 0xb215, 0x4385, 0xb2ef, 0x46a4, 0x413d, 0x4389, 0x1d06, 0x07b8, 0x4558, 0x4494, 0x460a, 0xbf3c, 0x4158, 0xb225, 0xb2f1, 0x41ce, 0xbf2e, 0xb054, 0x4249, 0x45c6, 0x433e, 0x4685, 0x0ade, 0x4172, 0x42dc, 0x0fbd, 0x24d2, 0x4101, 0xbfd0, 0xb2a9, 0x4155, 0x416a, 0x4186, 0xba42, 0xb269, 0x1b7d, 0x4132, 0xa644, 0x1fc0, 0x43a1, 0xbf23, 0x02c3, 0x280b, 0x40b4, 0x4149, 0x4029, 0x217e, 0x10d4, 0x422a, 0x43ba, 0xba4b, 0x4291, 0x04ca, 0x1b38, 0x439a, 0x2d58, 0x42ca, 0x1e61, 0x2260, 0x4639, 0x46ca, 0x45be, 0xbf26, 0xb26d, 0x422e, 0x28d7, 0x4055, 0x3584, 0x41ff, 0x4322, 0xb27a, 0x40ee, 0x42f5, 0xba76, 0x40cc, 0xba68, 0x41b2, 0x1053, 0xbf7a, 0x4351, 0xb2a9, 0x4209, 0x2895, 0xb013, 0xbfd0, 0xa94c, 0x32bd, 0x41d8, 0xb283, 0x405c, 0x195d, 0xb25d, 0x1f07, 0xb249, 0x1a63, 0xb079, 0xaa92, 0x140c, 0xa9f0, 0x2d54, 0xa645, 0x37b2, 0x0ae8, 0xbf57, 0xbaf4, 0xba7c, 0x0abd, 0x42b9, 0x405c, 0x3f47, 0x43df, 0xb2db, 0x4329, 0x4134, 0x298d, 0xbaf3, 0xbfd5, 0xb2e3, 0x193e, 0xb232, 0xb276, 0xa3a0, 0x1d92, 0x41f6, 0x420f, 0x424d, 0xba32, 0x426a, 0xa208, 0x37fd, 0xba7e, 0x4363, 0x405f, 0x2b88, 0x1094, 0xbf1b, 0x1f17, 0xa26c, 0xbf70, 0xb27a, 0xb03a, 0x415b, 0x436d, 0xacf7, 0x42b2, 0xa2a5, 0x42f9, 0x414b, 0x4236, 0x1c8d, 0xbfa9, 0x41d8, 0x45a8, 0x43f5, 0x3226, 0xb0ec, 0x4248, 0x1e80, 0x4192, 0x1c5b, 0xb287, 0x4107, 0x467b, 0x4391, 0x406b, 0x4489, 0x18c6, 0x44dc, 0x4126, 0x1c8d, 0xb029, 0x42ac, 0x4055, 0x413e, 0x408e, 0xbf76, 0x052a, 0xbf90, 0xb261, 0xb2ae, 0x42b4, 0x26af, 0x43d5, 0x4084, 0xba6b, 0xb002, 0x09f3, 0x1f9b, 0x4300, 0xbf92, 0xa86c, 0x4187, 0x46f3, 0xb0cb, 0x41c1, 0x400e, 0x04b6, 0xbaf0, 0x10bc, 0xb0ff, 0xbfc1, 0xb244, 0x3bf7, 0x45a2, 0x4173, 0x415b, 0xba34, 0x3976, 0x1837, 0x40ad, 0x197e, 0x284b, 0x459a, 0x17e0, 0x41bf, 0x408f, 0x1ae0, 0x13a0, 0xbacd, 0x40c1, 0x420b, 0xba22, 0x44e8, 0x41a9, 0xa4b1, 0xb2ba, 0x41b2, 0x2d95, 0x4320, 0x4663, 0xbf37, 0xa9ae, 0xb2c5, 0x2310, 0x4329, 0x4239, 0xb0ef, 0x2d06, 0x4418, 0x42f3, 0xba76, 0xa639, 0x45d8, 0xb263, 0x2dc3, 0x1ce6, 0xbf4b, 0x43e6, 0xa2b7, 0x1c0c, 0x0252, 0xbf9b, 0x4151, 0x4062, 0x4148, 0x4360, 0x43bf, 0x07ca, 0x15e8, 0x40e2, 0x34b3, 0x433a, 0x42e4, 0x38ce, 0x34cc, 0x1796, 0xb04d, 0x3fc2, 0x46d3, 0x422e, 0xb09d, 0xbaf0, 0x4319, 0x4057, 0x435e, 0xbfe0, 0x4393, 0xbf64, 0x1a89, 0x422e, 0xb01e, 0x400c, 0xb07d, 0x1d17, 0x426c, 0x46ab, 0x454f, 0x44e5, 0x4085, 0xb209, 0x194d, 0x1f97, 0x411d, 0x4051, 0x1fd4, 0x3a6e, 0xbf4a, 0xb27a, 0x0cda, 0xae68, 0x4026, 0xb2cc, 0xba30, 0xb24b, 0x1e97, 0xbf77, 0x4231, 0xa0c7, 0xb268, 0x268b, 0xb0ad, 0xb209, 0x1b9f, 0x4035, 0x4237, 0x0231, 0x1aaf, 0xb0cb, 0x1d8e, 0x42e4, 0x42e4, 0x21cf, 0xb2d2, 0x420d, 0x4041, 0xac5b, 0xa18a, 0x43f6, 0xbaf7, 0x388e, 0x1c7f, 0xa574, 0xbf89, 0x43bd, 0x1ab6, 0xba61, 0x4218, 0x1ea0, 0x42c9, 0x432d, 0x4376, 0x1933, 0x4134, 0x1d9b, 0x27df, 0x43c4, 0xbfd5, 0x4203, 0xa8dc, 0x0ae8, 0x440a, 0xba0c, 0x3294, 0x4174, 0x42c9, 0xa2d7, 0x42ed, 0xbad3, 0x1bfe, 0x4326, 0x4035, 0x4348, 0xb292, 0xbf9e, 0x18f3, 0x43f1, 0x1367, 0x411b, 0x4477, 0x1e0b, 0x1d22, 0x1c39, 0x40bd, 0x435a, 0x1be5, 0x13c2, 0x1b7c, 0x433c, 0xa9d8, 0xa165, 0xbf69, 0xb057, 0x413d, 0x40f9, 0xb202, 0x3e95, 0x4185, 0xbf29, 0x1f39, 0x19fd, 0x42f1, 0xa2f2, 0xb2d2, 0xb070, 0x415c, 0xbf24, 0x43ba, 0x3aca, 0x1a2f, 0x4150, 0x43de, 0xb2df, 0x436d, 0xbfba, 0xad69, 0x1080, 0xb0f5, 0x4307, 0x0ab6, 0x4002, 0x41f4, 0xba63, 0x4220, 0xb28f, 0xba7e, 0xa3b6, 0xa952, 0xba6a, 0x447f, 0x1cd0, 0xb055, 0x4392, 0xb231, 0x1af3, 0x4354, 0x41cf, 0xa231, 0xbfde, 0x35ca, 0xab3b, 0xb0e7, 0x43ea, 0xbae7, 0x4375, 0x43ce, 0x31e5, 0x393c, 0x42f9, 0x4135, 0xb260, 0x409b, 0xba67, 0xb26d, 0x3fac, 0xb26c, 0x425d, 0x43de, 0xbf6d, 0x435b, 0x2efd, 0x1ae3, 0x429f, 0x187d, 0xb0a5, 0x41d7, 0x40b9, 0xbad6, 0x15d3, 0x4666, 0x46ad, 0xbf6f, 0x2348, 0x4006, 0x1221, 0xba18, 0x4271, 0x4057, 0x1829, 0x1e42, 0xa497, 0x1dc7, 0x41eb, 0x4225, 0x43b5, 0x1603, 0xb04f, 0xbf84, 0x3451, 0xb055, 0x4499, 0xb2e1, 0x41cd, 0xb25a, 0x41d8, 0x4305, 0x40ee, 0x411e, 0xba6f, 0x0452, 0x4160, 0x413d, 0x408c, 0x4232, 0x4047, 0x4452, 0xba4c, 0xa758, 0xbf21, 0x4616, 0x1990, 0x422a, 0x1dae, 0x1f74, 0x0f3f, 0x1f61, 0xbac5, 0x1d9e, 0xbaf1, 0xbf12, 0x1bd8, 0x2b26, 0xb05d, 0x09a5, 0x408e, 0x1ca7, 0x0a35, 0xa904, 0xaed0, 0x16fb, 0xbf80, 0xa941, 0xae2a, 0x1e3e, 0xb256, 0xa51e, 0xb2c4, 0x435e, 0xb288, 0x403f, 0xb2a9, 0xa590, 0x40a3, 0x4112, 0xbf99, 0x1c5f, 0x426e, 0x40c1, 0xb26c, 0xbfae, 0xb2a3, 0x43a7, 0xb213, 0x420f, 0x429f, 0x4080, 0x38a0, 0xba39, 0x1afa, 0xb027, 0x36e3, 0x37ef, 0x42c4, 0x40c9, 0x42de, 0xb0ec, 0xbf0b, 0xbac5, 0x241d, 0x25fb, 0x4182, 0xb251, 0xaa91, 0x4383, 0xbfb3, 0xba70, 0xb0a0, 0xba77, 0x4068, 0x1c8f, 0x12d3, 0xb27f, 0xb2c8, 0xbada, 0x3b8f, 0xbf32, 0xbaec, 0xb092, 0xbae3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x989fe979, 0xeb6d1dc2, 0x0199a793, 0xfd6ddabf, 0x2337d8f4, 0x10d088ef, 0x6abc19fa, 0x03f7a6b3, 0x8efc124d, 0x26bfcdfd, 0x162448d8, 0x1f47e67e, 0xbfa7bf3d, 0xf5019635, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x000000ea, 0xffffffea, 0xfffffbff, 0x00001d00, 0x0000001d, 0x000019b4, 0x000000a6, 0xffffffec, 0x3fffff72, 0xffd83f36, 0xbfa7bf3d, 0x000000d6, 0x020003a0, 0xffffdcc8, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x432c, 0x0c6d, 0xb075, 0x4339, 0x43c2, 0xb043, 0x4305, 0x42c6, 0x0cb5, 0x3d42, 0x3249, 0x40c7, 0xbf9f, 0x0213, 0x4268, 0x4192, 0x342f, 0x20a6, 0xb2bf, 0x0700, 0x41ae, 0x17af, 0x1c55, 0xbfd1, 0x437d, 0x3f9d, 0x40a9, 0x41e4, 0xba3d, 0x415d, 0xb2bf, 0x4211, 0x2b93, 0x4161, 0x412a, 0x41e7, 0x2540, 0x41a9, 0xbacf, 0x34a4, 0x1847, 0x448b, 0x448a, 0x4187, 0x40cc, 0xa4d4, 0xb2fd, 0xb2d5, 0x1e75, 0x0762, 0x43ac, 0x4020, 0xbf8f, 0xb207, 0x4351, 0xb296, 0xb230, 0xb0e4, 0xbf4c, 0x44e2, 0x4043, 0xbac6, 0xb26b, 0xb2c3, 0x3a43, 0x4182, 0x46d9, 0x312b, 0x32a1, 0x3341, 0x4136, 0x4385, 0xbf77, 0x30bb, 0x4083, 0x4621, 0xba7a, 0x46c0, 0xb2eb, 0x42c5, 0xb27f, 0x1e4f, 0x416f, 0x199e, 0x4463, 0xbf8b, 0x4117, 0xbaf4, 0x45f2, 0x3e2f, 0xb248, 0x41d0, 0xb068, 0x467f, 0xa251, 0xb26e, 0x29aa, 0x2bbf, 0xb2cf, 0x439e, 0x1e50, 0x41f9, 0xbf80, 0x456d, 0xbf48, 0x1ff7, 0x46f9, 0x28ba, 0xbae5, 0x455a, 0xb23a, 0x4234, 0x4224, 0x3582, 0x41d8, 0x4038, 0x420d, 0x42a4, 0x2823, 0x2019, 0xbf98, 0x289f, 0xb027, 0x3f10, 0x2356, 0x43d4, 0x4327, 0x2b77, 0xab61, 0x43dc, 0x41fe, 0x1560, 0x01d5, 0x43cc, 0x4310, 0xbf04, 0x4053, 0x093b, 0x43b5, 0x4311, 0xb033, 0x30ba, 0xae34, 0xbf78, 0x4051, 0xba31, 0x4113, 0x4364, 0xaa6d, 0x2548, 0xb254, 0x1979, 0x4399, 0xb2d1, 0x2a2e, 0xb22e, 0x40a2, 0x1026, 0x428d, 0xb29f, 0x18b9, 0xb0b5, 0x422a, 0x4138, 0x2e00, 0x402b, 0xbf2c, 0xb06a, 0x187c, 0x402f, 0xba21, 0x407d, 0x008f, 0x4119, 0xb2ef, 0x4260, 0x4185, 0x43f8, 0x4028, 0xbae3, 0x4005, 0x1f3a, 0xad44, 0xb28e, 0xa699, 0xa201, 0x4243, 0xb2b5, 0x1978, 0x40d2, 0xbf48, 0x430a, 0x1965, 0xb222, 0x123c, 0x40ab, 0x0239, 0xb2b0, 0xbf9d, 0x36b8, 0x2273, 0xafdb, 0xb2dd, 0x1b98, 0x3bd9, 0x2680, 0xba1b, 0x1485, 0x4387, 0x42e2, 0x3bc3, 0x43c9, 0xb005, 0x363c, 0xbf07, 0xba48, 0x3851, 0xba45, 0x4288, 0x41ea, 0x19f6, 0xb2e6, 0x1a33, 0x413c, 0x4002, 0x4304, 0x42de, 0xbadf, 0x1b8a, 0x4032, 0xb219, 0x4423, 0x4634, 0x4089, 0x4640, 0x1b8d, 0x4207, 0x3e42, 0x41f1, 0x42fa, 0xbfa7, 0x4601, 0x1f59, 0xb2cf, 0x40bf, 0x4559, 0x1eb2, 0x1bdb, 0x42af, 0xb226, 0x44f4, 0x43a2, 0x1aea, 0x3d70, 0x190d, 0x436a, 0xb2b9, 0x17f6, 0xba64, 0x44d3, 0x400c, 0xb033, 0x4015, 0x0c75, 0xba2c, 0xbae8, 0xbf64, 0x1a67, 0x411e, 0xb2dc, 0x41ca, 0x45ec, 0x08d1, 0x4039, 0x42b9, 0x40f1, 0xb076, 0xae19, 0xb256, 0x3711, 0x4067, 0x410b, 0xa4c0, 0x1cd5, 0x1c2e, 0x1f01, 0x426c, 0x4274, 0x075d, 0xbf66, 0x2825, 0x1fca, 0xb266, 0xbaf2, 0x424a, 0x42f6, 0x236d, 0xb27f, 0xb2fa, 0x45eb, 0xbf0e, 0x1cdd, 0xaf37, 0x43a7, 0xb08e, 0x4258, 0x41f3, 0x211e, 0x40b6, 0xba7a, 0xbf76, 0x467c, 0x417d, 0x4173, 0x4623, 0x4058, 0xbad5, 0x423c, 0xbf80, 0x42e7, 0x3860, 0x2a24, 0x0276, 0xb033, 0xbfdc, 0xb2c3, 0xb224, 0xb27e, 0x2369, 0xba38, 0x4277, 0xb07a, 0x42c2, 0x41c2, 0x4668, 0xa9f2, 0xbae0, 0xbfc2, 0x41c2, 0x414e, 0x4322, 0x42d9, 0xbaf8, 0x169e, 0x4229, 0x2d30, 0x402b, 0x42ef, 0xb2e0, 0x42c4, 0x3c0f, 0x33ac, 0x117b, 0xbacd, 0x1961, 0x41af, 0x4041, 0xba1d, 0xbfa0, 0xbf7c, 0x403d, 0x3710, 0x429d, 0x41e8, 0xba30, 0xbf80, 0xacb2, 0x196a, 0x40b1, 0x416c, 0x1a81, 0xbf90, 0x41ef, 0xb079, 0x412c, 0xb00a, 0xbf80, 0x03d7, 0xb28b, 0x44ed, 0x4153, 0xbf7f, 0x413a, 0x4553, 0x416b, 0x4030, 0x408a, 0x1fc8, 0xb0d4, 0x3ad1, 0x4153, 0x4188, 0x409e, 0x45a0, 0x390b, 0x0957, 0x105d, 0x2a7c, 0x437c, 0xbf4f, 0xba1b, 0x1847, 0xb22a, 0x4574, 0xba75, 0x4162, 0x17f6, 0x4357, 0x42e1, 0x4186, 0x4378, 0x4035, 0xa34c, 0x43e1, 0x1d89, 0x46a1, 0x3132, 0x443e, 0x4173, 0x45c2, 0x1fd2, 0x41f9, 0xbfb4, 0x460c, 0x1f6e, 0x3143, 0x435a, 0xb2bc, 0x4272, 0x1719, 0xbacc, 0xbf46, 0x427d, 0x40d9, 0x437e, 0x03aa, 0x41fd, 0x4187, 0x3d10, 0x40b6, 0xb2d2, 0xbfd0, 0x427d, 0x421c, 0x430a, 0xba14, 0x18cb, 0xba2c, 0xb087, 0xbfb3, 0x3e14, 0x4305, 0x4134, 0xb291, 0x165c, 0x406b, 0xbf1e, 0x40cd, 0xb2dc, 0x403c, 0x029a, 0x0386, 0x406e, 0x429d, 0x1e23, 0x441c, 0x0b65, 0x460e, 0xb224, 0x4158, 0x3c93, 0xa9dc, 0xbfbd, 0x0dfc, 0x46ba, 0xb07f, 0xba01, 0x414d, 0x1744, 0x1cce, 0x40fa, 0x4120, 0x2b23, 0x4465, 0x1c51, 0x0aef, 0x4234, 0xb04a, 0x4392, 0x2c8e, 0xb20e, 0xbf61, 0xbae5, 0xba31, 0x4055, 0x42b4, 0xb28b, 0x02b5, 0x02a8, 0xa873, 0x417e, 0x41fe, 0x42c0, 0x4259, 0x41bc, 0xbff0, 0xbfc1, 0x1987, 0x40d2, 0x1b97, 0x41fc, 0x0019, 0x4139, 0xb212, 0x151d, 0x4231, 0xbfb0, 0xbafb, 0x06e7, 0x1817, 0x4270, 0x006c, 0x409c, 0x3797, 0x1801, 0x4116, 0xb242, 0x4028, 0xb2e8, 0x0e08, 0x426b, 0xbf8c, 0x1eb2, 0xb27e, 0xb2e9, 0x3814, 0x0b2a, 0xb2e7, 0xbf87, 0x2e03, 0x1bd0, 0x43f5, 0x4356, 0x42b1, 0xa47c, 0xb0f4, 0x427d, 0xb26a, 0xb287, 0x1b09, 0x0484, 0xbf85, 0x4112, 0xb057, 0xbfc0, 0x2044, 0x435f, 0x41a3, 0x3ba9, 0x4141, 0xb2a4, 0x4060, 0x40af, 0xb0fd, 0x4243, 0x4170, 0x422a, 0xba39, 0x416d, 0xbf81, 0xb2ef, 0xb061, 0xb015, 0x437e, 0xbfe2, 0x4168, 0x13c9, 0xb02d, 0x296a, 0x42d2, 0x3994, 0x426b, 0x46c3, 0x1a5d, 0x4336, 0x4133, 0x43a4, 0xb202, 0x42dd, 0x3226, 0xb2a9, 0xb0e7, 0x23db, 0xb072, 0x1bf5, 0x43f7, 0x44c5, 0xb223, 0x4312, 0xbf47, 0xba56, 0x1089, 0x27e8, 0xb208, 0xabdd, 0x3972, 0x41ce, 0x4371, 0x41c1, 0x4082, 0x2c57, 0x1ef7, 0x428c, 0x4053, 0x21b6, 0x447d, 0xb015, 0x40d2, 0x4110, 0x0df3, 0xbf8d, 0xb28d, 0x4350, 0xbada, 0x4058, 0xbaf3, 0x1e42, 0xbad4, 0xabca, 0x2d58, 0x41b7, 0x421f, 0x40a4, 0x0c09, 0xb022, 0x4170, 0x1811, 0x3af8, 0x30c1, 0xba50, 0x41e6, 0x4477, 0x425d, 0x4076, 0x42ca, 0x43a5, 0x1834, 0xbf7a, 0x31ad, 0xbafe, 0x1bc6, 0xb247, 0x1db0, 0x418f, 0x4163, 0x4008, 0x4674, 0xb2f5, 0xb269, 0xb215, 0x066e, 0x19dc, 0x24a4, 0xba0e, 0x4100, 0xbac7, 0xa0f0, 0x46ec, 0xa3c8, 0x311e, 0x0490, 0xb04b, 0x4016, 0x4085, 0x4024, 0xbf5b, 0x4028, 0xb2e5, 0xb054, 0x4189, 0xb267, 0xa751, 0x42b0, 0x40ca, 0xaf76, 0xbace, 0xbad2, 0xbf99, 0x1ef3, 0x435f, 0x4384, 0x3383, 0x4247, 0x4338, 0x1a84, 0x416a, 0xbaea, 0x41a6, 0x4002, 0xbf90, 0xb05a, 0xa518, 0x3010, 0x43eb, 0x4350, 0x352f, 0x43c9, 0xba2b, 0x3361, 0x421e, 0xb268, 0x407d, 0xbfb4, 0xba4e, 0x44b5, 0x41a2, 0x419f, 0x410f, 0x46c9, 0x19d8, 0x0eec, 0xbad3, 0xa0a0, 0xbf60, 0xbfda, 0x188a, 0x129a, 0x400d, 0xba64, 0x4310, 0x41af, 0xba79, 0xba55, 0xb063, 0xbf80, 0xbf0e, 0x442d, 0x1826, 0x42d1, 0x1bf8, 0x435f, 0xba15, 0x2f8f, 0x1945, 0xa28c, 0x41f4, 0xbf93, 0x1eab, 0xaaf6, 0xba0b, 0x2fda, 0xbfb0, 0x4228, 0xa802, 0xb2f6, 0x425d, 0xb2b8, 0xbf33, 0x418e, 0x435b, 0x1feb, 0x4252, 0x4359, 0x462b, 0xba17, 0x40bf, 0x43f9, 0x43d5, 0x03c1, 0x40bb, 0x40f7, 0x4391, 0xaa2c, 0x45b8, 0x4678, 0x42ad, 0x4088, 0x03c6, 0x4573, 0x40e9, 0xb282, 0x41cd, 0xb2f6, 0x1913, 0x40cd, 0x41e9, 0xbfa5, 0x262c, 0x42c9, 0x28d4, 0x3fec, 0x3b30, 0x42a4, 0x41ab, 0xbff0, 0x43f8, 0xb225, 0x41dc, 0xb2ff, 0x1866, 0x2870, 0x4286, 0xab63, 0x3889, 0x407d, 0x40fa, 0x25a1, 0x4488, 0x416c, 0x433a, 0xbfbf, 0x0155, 0x43f6, 0xb225, 0xb22e, 0x403f, 0x1355, 0x428f, 0xba2e, 0x438b, 0xb074, 0x445a, 0xb2de, 0xb0f1, 0x4241, 0x387f, 0xa5ab, 0x401c, 0xb02a, 0xb23a, 0xb22c, 0x2cba, 0x43c6, 0x1755, 0x4313, 0xbfb1, 0xa2de, 0x085c, 0xb0d9, 0x2705, 0xbf81, 0xb2af, 0xba09, 0x4314, 0xb2fa, 0xbad6, 0xbfb0, 0xbaf6, 0x1ba9, 0x4699, 0xb058, 0x43de, 0x32d1, 0x1c10, 0x41cb, 0xb281, 0x41be, 0x4360, 0xb296, 0x01d1, 0xb227, 0xafe4, 0x3965, 0x19d5, 0x40b6, 0xb09d, 0xb2e4, 0x42c3, 0x426a, 0xab02, 0xbf48, 0x4143, 0xbf1e, 0xb091, 0x0169, 0x09c9, 0xba62, 0x1f2b, 0xb060, 0xb0c4, 0x4353, 0x4377, 0x1c67, 0x4083, 0x1ba9, 0x43cb, 0x435f, 0x1b26, 0xb2ed, 0x3409, 0x2f3c, 0x41d0, 0x4306, 0x19f8, 0xb035, 0x4401, 0x076b, 0x4474, 0x1add, 0x41a4, 0xbf6a, 0x2302, 0x401a, 0x44f1, 0xbf6c, 0x426f, 0xb214, 0xb25d, 0xb226, 0x010f, 0x4261, 0xbacc, 0xbf25, 0x434e, 0x2220, 0xa694, 0x0089, 0x421d, 0x4083, 0x4092, 0x4333, 0xb22f, 0x4217, 0x432b, 0xb0d5, 0x4637, 0xab70, 0x42b4, 0x3cef, 0xb21a, 0x09ba, 0xb2fb, 0x4193, 0x41eb, 0xba2a, 0xba50, 0xa277, 0x4198, 0x4075, 0xbfbc, 0xaba3, 0x1814, 0x1dbd, 0xb260, 0x4305, 0xbfc0, 0x4334, 0x43cb, 0x2690, 0x1dd1, 0x42f4, 0xbf3f, 0x4294, 0x034c, 0x31f5, 0x4616, 0x4340, 0x224f, 0xba03, 0x4063, 0xb2ef, 0x4026, 0x4182, 0x4321, 0xaa72, 0x4242, 0x4265, 0xb085, 0xbaf2, 0xbfc9, 0xb2dc, 0x432c, 0x0a3b, 0xba19, 0x40fa, 0x4580, 0x1ec1, 0xba1b, 0x1a32, 0xa367, 0xbfe0, 0xacd3, 0x4179, 0x13e8, 0x2574, 0xb25c, 0xb228, 0x1852, 0xbfcb, 0xb2b6, 0x415c, 0x42bc, 0xba53, 0x439d, 0xbf2a, 0x15c3, 0xb2ac, 0x0d9c, 0xbf1c, 0xba32, 0xbaed, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x73229dce, 0x0f640776, 0x9c25ec11, 0x66a54f6d, 0xca4c2372, 0x617f5393, 0xfc67eb08, 0x99c5589c, 0x6805463b, 0x1f6ada41, 0x7ed9c21c, 0x50147fab, 0x6e783256, 0x5296ab63, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x00000074, 0x00000135, 0x10000000, 0x00000000, 0x00000000, 0x00003000, 0x00000010, 0x00000017, 0x6805463b, 0x0d36c137, 0x0a3e95fc, 0x6805463b, 0x0d32b38d, 0x0d36bf7f, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x42c2, 0x2d74, 0x4116, 0x4185, 0x40fd, 0x428a, 0x1707, 0x406c, 0xb298, 0xb252, 0x41ac, 0x1a53, 0xba5c, 0x411c, 0x4253, 0x3541, 0xbf27, 0x015a, 0xba76, 0x1226, 0x4251, 0xba16, 0xbfd0, 0x352c, 0x417b, 0xb009, 0x34c2, 0x41a9, 0x1d34, 0x438e, 0xb083, 0xb24a, 0x41d0, 0x1f5b, 0x1c0b, 0x4322, 0x1ae0, 0x234a, 0xbfd2, 0x43c9, 0x4212, 0x1556, 0x456d, 0x07bd, 0x4336, 0x43ab, 0xba1f, 0x3fd4, 0x4210, 0x41e2, 0x18d9, 0x1d4d, 0x4572, 0x4199, 0xb25e, 0xb207, 0x43fd, 0x3dee, 0x4382, 0x42fb, 0xb0cc, 0xba09, 0x2beb, 0x00e1, 0xba2a, 0xb068, 0xbf31, 0x40f0, 0x3c42, 0xb236, 0xb274, 0x44da, 0xa2d8, 0xb211, 0xba22, 0x43f8, 0x4102, 0x42e7, 0x46ca, 0x24ab, 0x4423, 0xb0f0, 0xb221, 0x421f, 0xb20d, 0x1dbe, 0x460c, 0x4040, 0xb026, 0xba47, 0x404d, 0xb22b, 0xbfb3, 0x43a8, 0x107b, 0xbf60, 0xb2d0, 0x430a, 0x3e97, 0xb272, 0x1c2b, 0x42b5, 0x4053, 0xb286, 0x42e0, 0xba44, 0x4114, 0x421c, 0xb2bf, 0x1eb5, 0x41de, 0x4097, 0xb273, 0x4121, 0x198c, 0xb281, 0xb03c, 0x409c, 0xbf46, 0xb2db, 0x443a, 0x41bf, 0xb244, 0x43a6, 0xaa45, 0xb03b, 0x43c8, 0x1d75, 0x400b, 0x1c22, 0xbae4, 0x0c72, 0x4130, 0xbae0, 0x46cc, 0xbfb1, 0x43ad, 0xba49, 0x41c2, 0x0327, 0x4027, 0x41ff, 0x4085, 0xbfcb, 0x412a, 0x1157, 0x43ab, 0xb23c, 0xb278, 0x421c, 0x416c, 0x4173, 0x43b1, 0x41c4, 0x40e9, 0x420e, 0x1bdf, 0xbfd0, 0x4672, 0xba6d, 0x07d1, 0x43c0, 0xb2aa, 0x40d1, 0x41d4, 0xa179, 0x44b2, 0xb052, 0x464a, 0xb24e, 0x4476, 0xa5a9, 0xbf0d, 0xbf90, 0x40c4, 0x44d9, 0x43cc, 0xbf00, 0x42bd, 0x4123, 0xb0ef, 0xba5f, 0x3212, 0x26f0, 0x4357, 0xb227, 0x43ef, 0x28d3, 0x1a18, 0x1e20, 0xade4, 0x42b0, 0xae3b, 0xa4cb, 0x1e56, 0x41c0, 0x434c, 0xbf51, 0x411e, 0xbfe0, 0xba57, 0xb06f, 0x4278, 0xbadd, 0xa0b7, 0x43b7, 0x41fe, 0x4284, 0x41a9, 0x40d7, 0xb0e4, 0x4418, 0x2466, 0x401f, 0xbfa4, 0xb258, 0x1aa7, 0x427b, 0xb2ec, 0x40e4, 0x40f3, 0x1f6b, 0x4462, 0xb29d, 0xb09d, 0x3a5d, 0x43d9, 0xb253, 0x40a7, 0xbac5, 0x4193, 0x0e8b, 0x43c0, 0x4214, 0x2268, 0xb2fc, 0x24b2, 0xb20c, 0x431e, 0x31a0, 0x43de, 0xbfc7, 0xad2f, 0xb0f3, 0x1fc6, 0xb272, 0x40fe, 0xb24f, 0x4115, 0x456e, 0x3ea3, 0x4122, 0x4323, 0x41b7, 0x414b, 0x4624, 0x33c0, 0x1eb4, 0x4333, 0x419e, 0x1a49, 0x1cc2, 0x35b5, 0x423e, 0xbf7c, 0x1f87, 0xb2cd, 0x43a1, 0xbf1c, 0x4215, 0xba79, 0xbfd7, 0xbad0, 0xba62, 0x4213, 0x429a, 0xbfbb, 0x43d6, 0x4197, 0x426d, 0x43f5, 0x4267, 0x40cd, 0xb2ad, 0x4071, 0xa27f, 0x3837, 0x408f, 0x41f2, 0x4167, 0x1d86, 0xaa44, 0xbae5, 0x1f57, 0x456a, 0x45c6, 0x42fb, 0xb2bc, 0xbf21, 0x199d, 0x1f8b, 0xba71, 0x443f, 0x1192, 0x4240, 0x43c2, 0xba2b, 0x4196, 0xb242, 0x3996, 0x0844, 0x4340, 0x4691, 0xbfe4, 0x1911, 0xb0fd, 0x402d, 0x419f, 0xad1e, 0x4038, 0x3877, 0x2e52, 0xb2a3, 0x2181, 0x35e8, 0xa034, 0x4115, 0x1b67, 0x43c1, 0x1c10, 0x2cac, 0x2a71, 0xb2f6, 0xa5dc, 0x4184, 0x4013, 0x19f5, 0x40c7, 0x4085, 0xbfc8, 0x456d, 0x4235, 0xba27, 0xbac8, 0xb298, 0xb29c, 0xbf00, 0xb051, 0x42d1, 0xb220, 0xbf03, 0xa71c, 0x44c0, 0x4327, 0x195d, 0x4092, 0x3e66, 0x40d0, 0x4345, 0x42fe, 0x41f3, 0x11cf, 0x4043, 0x1c92, 0xbacc, 0xbf04, 0x406f, 0x1da6, 0xba6a, 0x4059, 0xb01b, 0x43b6, 0x3fce, 0x43ff, 0x403e, 0x43da, 0xafac, 0x409e, 0x2481, 0x4148, 0x439e, 0x4220, 0x44a2, 0x437c, 0xbf3f, 0xb24d, 0x0870, 0x1cbd, 0x41bc, 0x4035, 0xb27f, 0xbf0a, 0x411c, 0x40f0, 0x1740, 0x4449, 0x4254, 0x430e, 0xb209, 0x41b6, 0x43f3, 0xb244, 0xbf70, 0x19bf, 0x412d, 0xa727, 0x1c54, 0x41e4, 0x43ee, 0xb093, 0xbf2b, 0xba16, 0x4063, 0x1e36, 0x35fc, 0xbae6, 0x4198, 0xbfb5, 0x01d4, 0x3182, 0x1fe1, 0x40f2, 0xbfc9, 0xb248, 0x2caa, 0x36f3, 0x40ac, 0x18b3, 0xb2a0, 0x43bc, 0xbfc0, 0xbf81, 0xb044, 0x43e6, 0x1fc6, 0xa96e, 0xb2fd, 0xa63b, 0xb002, 0x1aa8, 0xba59, 0x4307, 0x401a, 0x3bc9, 0xb2b3, 0x0cde, 0x4183, 0x4163, 0x4493, 0x430f, 0x4302, 0x42a0, 0x1d82, 0xbfd0, 0xb21e, 0x432c, 0xbfd9, 0x30c9, 0x406e, 0xa56b, 0xb05a, 0x3184, 0xba02, 0x43a5, 0xb204, 0xb229, 0xa861, 0x0096, 0x2731, 0xbf48, 0x13cf, 0x424a, 0x4564, 0xb2e8, 0x1c0d, 0x03f4, 0x434c, 0xb051, 0x0c36, 0x4286, 0xb2a2, 0xb082, 0x11ca, 0x195d, 0x35c8, 0xa2bb, 0x4649, 0x41a1, 0x423c, 0x458e, 0xb28a, 0xb0e0, 0x1dea, 0x365e, 0xbfdc, 0x33a6, 0xba77, 0x4318, 0x09c2, 0xbf9e, 0x1d19, 0x1660, 0x4213, 0x3a84, 0x1c5a, 0x1e48, 0x4174, 0x4585, 0x1cb4, 0xbf3f, 0x1280, 0x4215, 0x4036, 0x43f3, 0x4361, 0x4119, 0xb26a, 0x40a1, 0xb0e1, 0x4609, 0x3d49, 0x3257, 0xb2c3, 0xbfc0, 0x41c0, 0x41ff, 0x40ff, 0x3cec, 0x1c4c, 0x4217, 0xb20e, 0x411c, 0xb07c, 0x41ce, 0xbf79, 0x4349, 0x4392, 0x1b61, 0xba4f, 0x415e, 0x3dc1, 0x1623, 0xb0d5, 0x4099, 0x41c4, 0xbf0d, 0x1e73, 0x43e9, 0x4461, 0xb2ab, 0xba3a, 0x43a1, 0x4083, 0x428c, 0x4277, 0x0587, 0xb29d, 0xae40, 0xbad2, 0x42ad, 0x45e1, 0x407a, 0x4290, 0x409c, 0x42b5, 0x4001, 0xb2af, 0x25ef, 0x32ea, 0x1ad7, 0xbfc7, 0x42a0, 0x1cf5, 0x3c74, 0xbf00, 0x41f7, 0xb270, 0x4009, 0xaf02, 0x4331, 0x4627, 0x42ac, 0x4020, 0xbfb3, 0x42c2, 0xa284, 0xab3b, 0x40b2, 0x4319, 0xb051, 0x42cc, 0xb0ae, 0x206b, 0x413b, 0xb241, 0x2001, 0xb2b6, 0xbfa2, 0x408d, 0x34d3, 0x43db, 0xba01, 0x1a1f, 0x4396, 0x42b9, 0x4335, 0xa054, 0xa317, 0x1c89, 0x4186, 0xb01f, 0xba56, 0x4616, 0xb204, 0xb24e, 0x028d, 0x41d0, 0xb217, 0x2943, 0x402d, 0x1de0, 0x1988, 0xba51, 0xbfdd, 0xa8d1, 0x41d9, 0x40e2, 0x441a, 0x41fc, 0x4018, 0x4354, 0x430c, 0x404a, 0xb2a3, 0x42dc, 0xb230, 0xa56c, 0xbf05, 0x4121, 0x434e, 0xbae3, 0x401c, 0x2b71, 0xb2bb, 0x43b2, 0x084a, 0x19e1, 0x1f23, 0x444a, 0x40cf, 0xbf21, 0x41ef, 0xb243, 0x43ab, 0x404d, 0xb229, 0x00d5, 0xb2ce, 0x0a30, 0x41f0, 0x1c86, 0x419b, 0x1f9f, 0xa91e, 0x4164, 0xb0a2, 0x318d, 0x414a, 0xa788, 0xbf60, 0x42aa, 0x432f, 0x1754, 0xba1d, 0x4148, 0xbf0e, 0x44bc, 0xba0e, 0xabb9, 0x18ad, 0x0df7, 0x46e3, 0xbad6, 0x432d, 0x42c3, 0xb21e, 0x1ee1, 0x43be, 0x437d, 0xb23d, 0x43a1, 0x42cb, 0x0e87, 0xb226, 0xba14, 0x1cd1, 0x0971, 0x1a7d, 0x2824, 0x40bb, 0xb2bb, 0x0c39, 0xbf9f, 0xb2a2, 0x43b1, 0x0463, 0x426a, 0x18bb, 0xba13, 0x4311, 0xabcc, 0xba1e, 0x41d1, 0x3d05, 0x43f5, 0xafc8, 0x3d69, 0x405e, 0x38ae, 0xa74e, 0x401b, 0x417b, 0x378f, 0x4204, 0xbf60, 0x1c69, 0x378e, 0x0c89, 0xbfb4, 0xbfc0, 0xb235, 0x4032, 0xb218, 0x4011, 0x4188, 0x3b23, 0x4277, 0x186e, 0x3f07, 0x3860, 0x44f5, 0x46ac, 0xb019, 0x42fc, 0x41ef, 0x2835, 0x45e4, 0xbf77, 0x4038, 0x2bca, 0x42fc, 0x4303, 0xbad5, 0x00fc, 0x197c, 0x2b44, 0x39e8, 0xb2aa, 0x4674, 0xa0f7, 0xb2b3, 0xa01d, 0x1b99, 0x429e, 0x101a, 0x41c5, 0xb085, 0x4320, 0x3520, 0x1c04, 0x4283, 0x420c, 0x3cf3, 0x42f2, 0x414f, 0xb2c6, 0xbfdf, 0x41db, 0x4139, 0x18e3, 0xba4c, 0x43b7, 0xb0c2, 0x051f, 0x42cc, 0x4300, 0x1caf, 0x1ef3, 0x417a, 0x1aef, 0xbfa1, 0x43d4, 0x18cd, 0xb24c, 0x2610, 0xb22b, 0x4355, 0xb213, 0x1c9b, 0xb266, 0xbf82, 0x422b, 0xb048, 0x03e0, 0x4312, 0x4325, 0xa9c3, 0x43a1, 0xb083, 0x295f, 0x4257, 0xb221, 0x1ab7, 0xba30, 0x43b0, 0x27d6, 0x03ce, 0x403e, 0x4247, 0x1a77, 0x179e, 0x1ecd, 0x42b2, 0x1fcb, 0x3acb, 0xbf93, 0x1fdb, 0xb06b, 0xb2ec, 0x4116, 0xbad3, 0x0437, 0xb209, 0x2cc6, 0x1355, 0x402d, 0x4424, 0xbff0, 0xb0df, 0xb2ae, 0x4373, 0xbf75, 0x448c, 0x1c21, 0x1ebf, 0x40f4, 0x427d, 0x417f, 0xb205, 0x40fb, 0x406a, 0xba49, 0x40b8, 0xba45, 0x45d0, 0x419a, 0x41d2, 0x43c6, 0x1ada, 0x42bb, 0x44dd, 0xbf87, 0x1c48, 0x1116, 0x02ee, 0x41db, 0xb225, 0x3137, 0x0f02, 0xaf6e, 0x1ba4, 0x4085, 0xb2fd, 0x4699, 0xbf70, 0x4401, 0x03f6, 0xb0d6, 0xa069, 0x42f2, 0x1a31, 0x1c36, 0x421c, 0x403f, 0x3545, 0x1f3f, 0x4255, 0x1cae, 0xb0ef, 0x3f75, 0x41bc, 0xbfde, 0x40f2, 0x42cc, 0x1dbe, 0xb240, 0xb00b, 0x0f1b, 0xba61, 0x41da, 0x424a, 0xba4b, 0xbf0d, 0x2f8d, 0x418f, 0x189f, 0xb045, 0xa835, 0x2001, 0x28d8, 0xb2e9, 0x4018, 0x441d, 0xb0ff, 0x156f, 0xb240, 0x1d6c, 0xb209, 0xb067, 0xbf60, 0x4280, 0xbaee, 0xbafb, 0x41dd, 0xb040, 0xa92d, 0xbfac, 0x00f2, 0x1d85, 0x320b, 0xb2f5, 0x415e, 0x42d2, 0xb23b, 0x0f07, 0x436d, 0x4142, 0xb2fa, 0xb218, 0xba0a, 0x4088, 0xbf80, 0x19da, 0x1356, 0x43c8, 0x2171, 0x43e1, 0xbfda, 0x4215, 0x42bd, 0x4364, 0x424e, 0xb228, 0x4009, 0xbaee, 0x1bce, 0x0502, 0xb2ae, 0xb217, 0xa6c0, 0x2900, 0x4033, 0x40e8, 0xba45, 0x2caf, 0xb21f, 0xbae6, 0x37ae, 0x2684, 0x43c8, 0x31de, 0xb26f, 0x40eb, 0x4293, 0xb209, 0xbf0e, 0x41ea, 0x4290, 0x40c1, 0xb282, 0x1aa9, 0x28c0, 0x1b96, 0x1d5d, 0x13c2, 0x437f, 0xafd5, 0x41fa, 0xba21, 0x0efd, 0x412a, 0xb08c, 0x4011, 0x46ed, 0xbaee, 0x0121, 0xb250, 0x4075, 0x4386, 0x41a3, 0xbfbe, 0x21d1, 0x34ae, 0x2fce, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xebd6df8c, 0x38bf55a7, 0xab137ca7, 0x96f9989a, 0x94c70e1c, 0x27865596, 0x517c3a55, 0x121297cb, 0xb921216d, 0x142c6285, 0xbbeafbb7, 0x8e13fca7, 0x38c24dd1, 0x08c88e6c, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x00000061, 0x000000d1, 0x0ffffc61, 0xbd9eb746, 0x42616367, 0x00000303, 0x00000300, 0x1cf4f061, 0xb921216d, 0x00000000, 0x142c6306, 0x142c6285, 0x0000589c, 0x1cf4ecdd, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4338, 0x4299, 0x454c, 0x40a5, 0x4298, 0xbfa8, 0x3d94, 0x19e4, 0x345c, 0x45c0, 0x41ff, 0x1ab0, 0x19a5, 0x420a, 0x41df, 0xba39, 0x3566, 0x410a, 0x4337, 0x444a, 0x4290, 0x0663, 0xabc7, 0xa261, 0x324d, 0xbf41, 0xb222, 0x31c6, 0x10f2, 0xaee3, 0x43e0, 0x297b, 0xb284, 0x42f3, 0xbf60, 0x419e, 0x1577, 0xb2a4, 0x43a9, 0x1fcf, 0x44f2, 0x4404, 0xbaf0, 0x1f57, 0x4047, 0x1e49, 0x4187, 0xbf56, 0x40f3, 0x1380, 0x1fe3, 0x41a4, 0x4320, 0x407d, 0x428a, 0x1547, 0x439d, 0x40b0, 0x3a5b, 0x430b, 0x43f6, 0x4256, 0x195f, 0x423f, 0xb225, 0x4616, 0x4041, 0xb0c4, 0x43e0, 0xbfa6, 0xa0f5, 0xb077, 0x419f, 0xb2da, 0x1d2d, 0x40c0, 0xbfd0, 0xb243, 0x41f6, 0x41ff, 0x4390, 0x435b, 0x416e, 0x3f68, 0x0b87, 0x435d, 0xba48, 0x1af2, 0x410f, 0xa9b6, 0xbfbc, 0x4608, 0x1e08, 0x0178, 0xbfa8, 0x4019, 0xbae4, 0xb2f8, 0xa945, 0x1037, 0x421e, 0xb083, 0x422d, 0xa323, 0x34de, 0x439b, 0x407a, 0x4076, 0x4343, 0x4150, 0xb242, 0xba63, 0x302d, 0xbfd6, 0x06a7, 0x2697, 0x41d0, 0x415d, 0x4205, 0xbf70, 0x40c4, 0x1aa6, 0x42c8, 0x43b3, 0x4044, 0x4250, 0x3f43, 0x25e4, 0xbfc5, 0x19c7, 0xbf60, 0x43f1, 0xa2d9, 0xba5c, 0x4112, 0x461e, 0x1fc7, 0x1ad9, 0x425a, 0x4100, 0x2df6, 0xb284, 0x4295, 0x01fe, 0x0e8d, 0x42c2, 0x401f, 0xb277, 0xba3c, 0x4231, 0xbf43, 0x1e0b, 0x2c65, 0x12e5, 0x4119, 0x463a, 0x408e, 0x4358, 0x4356, 0x40d5, 0x4329, 0xbf71, 0x11c0, 0x0e12, 0x4022, 0x41e7, 0x467d, 0x460e, 0xb221, 0x41a2, 0x40bf, 0xbf27, 0xaeb8, 0xb2fc, 0xb0f1, 0x42ad, 0x1a47, 0x0111, 0x1a9e, 0xb04f, 0x40de, 0x4146, 0x3ad4, 0xa618, 0x408d, 0x1a2f, 0x1b24, 0x1dcd, 0xbac0, 0x3909, 0x3e34, 0x43e0, 0x4328, 0x434d, 0x4268, 0x404b, 0x1493, 0x435b, 0x45b9, 0xbfc9, 0x4439, 0x412a, 0x1de7, 0x0b4e, 0x436b, 0x4173, 0x304b, 0x447e, 0xa9c9, 0x403b, 0x42d6, 0x42a2, 0x43da, 0x1d54, 0x42eb, 0x4378, 0x464b, 0x1a7b, 0x292c, 0x3a9a, 0xbf75, 0x1ab2, 0x2b8f, 0x1db2, 0xb2d8, 0xb288, 0xbaed, 0x4202, 0x1df2, 0xb282, 0x0d5f, 0xbf05, 0x2cb7, 0xb293, 0x09e9, 0x4016, 0x403d, 0x43aa, 0x42bb, 0x4218, 0x44a0, 0x40e0, 0x4079, 0x1b6b, 0x41b3, 0x429e, 0x469c, 0x42fe, 0x1a84, 0x4481, 0xb2c7, 0xa3da, 0x42e6, 0xbfb9, 0x1f14, 0xb2ef, 0xba2b, 0x38bf, 0xadd9, 0x42fd, 0x1918, 0x1bc9, 0x0495, 0x08f9, 0x40a9, 0x3a59, 0x1821, 0x45f5, 0x4050, 0x427c, 0xb234, 0x45d0, 0xbaef, 0x43cc, 0xbfaf, 0xbad3, 0xb20a, 0xba2a, 0x43fb, 0xb26d, 0x1913, 0xb074, 0x1e82, 0x427c, 0x1bb6, 0x40d6, 0xbf04, 0xb202, 0x1ad3, 0x4041, 0xb26d, 0xb284, 0x405f, 0x1e47, 0x421f, 0x4036, 0x40e8, 0x4117, 0x414b, 0x421c, 0x40bb, 0xb2c4, 0x189b, 0xb20e, 0x4262, 0xabcd, 0xbf8c, 0xba15, 0x40ec, 0x406d, 0xb032, 0xb288, 0x2202, 0x1574, 0xb280, 0xbfa2, 0x4229, 0x0a10, 0x25cd, 0xb0bb, 0x442d, 0x41d1, 0x400e, 0x40d7, 0x41da, 0xb0f5, 0x439e, 0x1c27, 0xbfa5, 0x1037, 0xa2f5, 0x1d46, 0xbfe0, 0x4118, 0x2e5b, 0x1d76, 0xb2eb, 0x413e, 0x422e, 0x2f5d, 0xb277, 0x4255, 0x293d, 0x42ba, 0xa3c3, 0x3e34, 0xbfa9, 0x4372, 0x459b, 0xb2a6, 0xbfb0, 0x418f, 0x4039, 0x420b, 0xa411, 0x3f81, 0xbf71, 0x463b, 0x0320, 0x4136, 0xba7e, 0xa4fa, 0x41cd, 0xba36, 0xbf36, 0xb0f5, 0x415e, 0x1e0c, 0x44cb, 0xba29, 0x1e27, 0x04f0, 0x1d30, 0xbfe0, 0x41c2, 0x412d, 0xb270, 0x1d49, 0x1c0f, 0xb04c, 0x434d, 0x3c92, 0x1f4e, 0x30be, 0xade7, 0x4344, 0xba1f, 0xa02e, 0x4113, 0x1e0d, 0xb25b, 0xbfa4, 0xb001, 0x3134, 0x409f, 0x4150, 0x1b5c, 0x4007, 0xa6fa, 0x2ffd, 0x42d5, 0xba53, 0xb23f, 0x40e3, 0xb0ca, 0xa7f4, 0x18ef, 0x0936, 0x43c7, 0xbfc2, 0xab87, 0x4378, 0x3710, 0x40f6, 0x4622, 0x456a, 0x46ca, 0x46a4, 0x41b8, 0x412b, 0xba2f, 0x1e94, 0xba4e, 0xb2e4, 0x43e0, 0x1a71, 0x411d, 0x4164, 0x0b2b, 0x40bb, 0x43c1, 0x4267, 0xba1d, 0xb2ab, 0xbfc0, 0x0624, 0xbfbd, 0xb02d, 0x43e9, 0x18ad, 0x3146, 0xa882, 0x227f, 0x4382, 0x4314, 0x19cc, 0xb25d, 0x465b, 0x43c9, 0x1800, 0x4169, 0x426f, 0x427a, 0x182c, 0x41ed, 0x2dfb, 0x1239, 0x41a2, 0x1aae, 0x3887, 0x42c2, 0x4352, 0x46d2, 0x4362, 0xb27a, 0xbf5b, 0x3437, 0xb09b, 0x41fa, 0x1fa2, 0xb2ee, 0x3c06, 0x437b, 0x2315, 0x410e, 0x404e, 0xbf78, 0x4231, 0x4387, 0x4258, 0x32ba, 0xb264, 0x43c0, 0xbfc0, 0x4244, 0x46b2, 0x408a, 0xba45, 0xbf07, 0x1a65, 0x19c0, 0x448b, 0x3b5f, 0x419d, 0x4653, 0xbaf2, 0x423f, 0x4118, 0x4138, 0xbaf1, 0xb2ed, 0xb26c, 0x2947, 0x41d4, 0xb265, 0xa04c, 0x2b65, 0xba2d, 0xbad6, 0x418c, 0x3bc0, 0xb26b, 0x1ebd, 0x43e5, 0xbfd4, 0x43ae, 0xb271, 0x4226, 0xb03c, 0x409a, 0xb21f, 0x42e8, 0xbf1b, 0x449b, 0x4310, 0xb2a4, 0x245b, 0x4322, 0xa98a, 0x41eb, 0xb284, 0xb2ac, 0x410f, 0xb2cc, 0x439f, 0x437f, 0x4120, 0xba47, 0x190f, 0x4313, 0x424d, 0x36a7, 0x432a, 0x45e3, 0xb263, 0xbf13, 0xb0c7, 0x423a, 0x437b, 0x41a1, 0x42b1, 0xba59, 0x43e4, 0x40a7, 0x4679, 0x438a, 0xad17, 0x43b4, 0x1420, 0xb229, 0x42b8, 0xb2cf, 0x1a2e, 0x3665, 0x08c0, 0xbf9d, 0x32d9, 0x0937, 0xb0be, 0x43c4, 0x4220, 0x1d47, 0x40f6, 0x4373, 0x4257, 0xafdc, 0x23cf, 0x41f4, 0x3b0e, 0x40ce, 0xb230, 0xb255, 0x4664, 0x1dcb, 0xb291, 0x42fa, 0xbf91, 0x1d7c, 0xb020, 0xba72, 0x4007, 0x3978, 0x4054, 0xbaed, 0x1bf6, 0x31ff, 0x46cc, 0xa2f6, 0x3bd0, 0xba7e, 0x193e, 0xb287, 0xb031, 0xbae0, 0x40d3, 0xb0d4, 0xb0ae, 0x02d5, 0x434e, 0x403d, 0xafe6, 0x3faa, 0x411c, 0xbfbd, 0x0cd3, 0x119f, 0x4394, 0xbacc, 0xb2e1, 0x4180, 0x4351, 0xbaeb, 0x43a4, 0x43f9, 0x4138, 0x2b8b, 0x2caf, 0x413e, 0x41a8, 0xbf48, 0x42c4, 0x012d, 0x4172, 0x422d, 0xb08b, 0x4295, 0x19b4, 0x1fd9, 0x40d1, 0x45e2, 0x4228, 0x424b, 0xbad8, 0x40ee, 0x40e9, 0x439d, 0xbaef, 0xbadf, 0xbf2a, 0x40c8, 0x192c, 0x4408, 0x401c, 0x2766, 0x30bd, 0x40f5, 0x4387, 0x3c4a, 0xbf0f, 0xba2e, 0xb242, 0x4624, 0x1a7b, 0xb27e, 0xba6c, 0x4169, 0x419f, 0x406b, 0x4427, 0x0415, 0x3065, 0x4015, 0xb2eb, 0x1cdd, 0x4222, 0xb225, 0xa0ed, 0x1826, 0xba16, 0x437d, 0x0dae, 0xb0fe, 0xbfb6, 0xb256, 0x1666, 0x42df, 0x45b4, 0x0c1b, 0xba18, 0xb29b, 0x4586, 0xbf32, 0x1bfe, 0xbad5, 0x422c, 0xbf2d, 0x1cc6, 0x1e0d, 0xb0c9, 0x437c, 0x342f, 0x429d, 0x4103, 0xbf7c, 0x41a5, 0xb02f, 0x4035, 0x0a5d, 0xbac1, 0xac84, 0x4071, 0x0d2f, 0x4235, 0x4203, 0x44b8, 0x0677, 0xba51, 0xb0d1, 0xbae3, 0xae14, 0xbf49, 0xb29a, 0x41ff, 0x1298, 0x1e6a, 0xb286, 0x4156, 0x3c1e, 0x4088, 0x075d, 0x414c, 0x4343, 0x4197, 0xa990, 0xb085, 0xacb8, 0x1984, 0xbfe0, 0xa3f4, 0x423f, 0xb2e3, 0x41d6, 0x1d5d, 0xbf65, 0x407c, 0x4269, 0x348b, 0xb2af, 0x43e5, 0x4169, 0x42e6, 0x4258, 0x434b, 0xbfc0, 0x4385, 0x3551, 0x4452, 0x41fd, 0x461c, 0xb2c5, 0x1e89, 0xba0b, 0xbf00, 0xacdf, 0xb2c0, 0x4241, 0x4247, 0x421b, 0x436f, 0x43d2, 0x14e5, 0x45a5, 0xbf1b, 0x1e6b, 0x4195, 0x45f2, 0xb27b, 0x4125, 0xb29f, 0xb26e, 0x4010, 0x422a, 0xa1a0, 0xbafc, 0xaa80, 0x1c8f, 0xb06f, 0x43a8, 0x42ac, 0xbae1, 0x415f, 0x117d, 0x4339, 0xb250, 0xbfe2, 0x4263, 0xb2bd, 0x409c, 0xb269, 0x19a4, 0xbfc0, 0x0fdf, 0x41f6, 0x4621, 0x4044, 0x40f5, 0x2e6e, 0x4221, 0x46d3, 0xbf9a, 0xba60, 0x41bf, 0x424a, 0x43bf, 0xbf9c, 0x26c7, 0x4242, 0x1d65, 0xb050, 0x4612, 0xa5c5, 0x1da4, 0x43a3, 0xb0fb, 0x444d, 0xba09, 0x3649, 0x4062, 0x1293, 0xba5b, 0x2db9, 0x43dc, 0x4333, 0x1a29, 0xbf0d, 0x0c2d, 0x4306, 0x4001, 0xbf70, 0xb04a, 0x43b5, 0x32dc, 0xba2b, 0x4367, 0x412e, 0x191f, 0x1a9a, 0x4027, 0x4139, 0x1951, 0xbf00, 0x4239, 0x40e2, 0x42c2, 0x40f2, 0x0880, 0x43f8, 0x1da1, 0xbf99, 0xb245, 0x464e, 0xbaf4, 0x4220, 0x4653, 0x43b9, 0x3e09, 0xbfc5, 0xba26, 0xb287, 0x1747, 0xb231, 0xb2ff, 0x21a4, 0xba1e, 0x44b4, 0x44a3, 0x4281, 0x4113, 0x40ab, 0x02cd, 0xb0f6, 0x40c1, 0xba59, 0x42a6, 0xb2e1, 0x1ed0, 0x3728, 0xba7f, 0x425d, 0xbf82, 0x2b31, 0xb03e, 0x4133, 0x1ce6, 0x413d, 0x4027, 0xb2c5, 0x1e77, 0x424d, 0x4051, 0xa275, 0xb00b, 0x4090, 0x408c, 0xbf79, 0x0e9e, 0x0767, 0x196e, 0x424a, 0x46fa, 0x2756, 0x24c8, 0x2108, 0x09cd, 0x1b81, 0x421a, 0xb256, 0xb007, 0x0ad0, 0x40cd, 0x4114, 0x404a, 0x400e, 0x40d8, 0xbf70, 0x4476, 0x01e4, 0x40e0, 0x2e5d, 0xbf9a, 0x2d03, 0x45c2, 0xbf00, 0xb0c5, 0x4585, 0x40e6, 0x42dd, 0xbac3, 0x41c0, 0x1a33, 0x1f44, 0x4347, 0x41ec, 0x20fe, 0xbf69, 0x3e36, 0x41e0, 0xadd6, 0x1aee, 0x2879, 0xb258, 0x432a, 0x2c20, 0x1ba8, 0x0ba8, 0x4361, 0x42bc, 0x408c, 0xbf9a, 0x4127, 0x42e9, 0xba00, 0x4127, 0x0bd8, 0x421a, 0x1d0b, 0xb2c6, 0xb0b8, 0xbf65, 0x2eb1, 0x43ee, 0x43e4, 0x4103, 0xb041, 0x40e0, 0xa3f4, 0x421a, 0xb284, 0xba0c, 0xae98, 0x42b7, 0x2272, 0x1c87, 0x3cf3, 0x41c5, 0x423b, 0xb202, 0x1f4a, 0x43a6, 0x124a, 0xb20e, 0xb0b4, 0x40e4, 0x40e9, 0x4135, 0xa232, 0x18a6, 0xbf48, 0x461c, 0xba4a, 0xba16, 0xa76b, 0x4106, 0x4289, 0x43fe, 0xba65, 0x4127, 0x0a75, 0xaddb, 0x2441, 0x1904, 0x1705, 0xb2c6, 0x1503, 0xbff0, 0x43c1, 0xbf52, 0xbac9, 0xb241, 0xbf80, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x2fdaaeb9, 0xb79696f8, 0x5b75e8b3, 0xb8bc6fba, 0xcb255eac, 0xe7fd35fd, 0xddb0f0b1, 0x6a6b7524, 0x61dac898, 0x5adfbd3a, 0xe9b18555, 0xf3de49cb, 0x123c8a0c, 0x745a0959, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x00001400, 0x00000000, 0x00000000, 0x00000000, 0x00001441, 0x00000000, 0x00000000, 0x00000000, 0x61dac89a, 0x5adfbff0, 0x0000171e, 0x00000900, 0x5adfbff0, 0x745a0295, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4177, 0x1819, 0x2414, 0x1b72, 0x43dc, 0xbf80, 0xba41, 0xa052, 0xb20b, 0xb083, 0xa090, 0x4329, 0xa61b, 0x43d1, 0x46cc, 0x1a14, 0xbf68, 0x46fa, 0xb233, 0x1a2a, 0xb2b8, 0xa819, 0xb037, 0xba10, 0x4042, 0xb298, 0x41fb, 0x409a, 0xa96a, 0xbfbd, 0x42cf, 0x4253, 0x408a, 0x405b, 0x42f3, 0x4059, 0xba0f, 0x4567, 0x1cdc, 0x46fc, 0x408c, 0x4422, 0x35df, 0x1b9c, 0x2518, 0x40fd, 0xbfc0, 0x427e, 0x088c, 0x12b3, 0x420e, 0x1f21, 0xb2db, 0x4082, 0xba04, 0x43d5, 0x417d, 0x40cc, 0xbfe1, 0xb2e4, 0x41cb, 0xb26a, 0x0fc6, 0x41fd, 0xa87b, 0x19ba, 0xb082, 0x4007, 0x403f, 0xb051, 0xb2ae, 0xbfb4, 0x41ae, 0x30da, 0x3846, 0x428b, 0x42d0, 0x4346, 0x42c7, 0x432c, 0x4130, 0x1e5c, 0x456b, 0x40d8, 0x4220, 0x40aa, 0x4158, 0x431d, 0x44bb, 0x1bcc, 0x04bf, 0x1cfa, 0x4025, 0x119d, 0xbfbb, 0x44ed, 0xa46d, 0x405d, 0x444c, 0x1c30, 0x4203, 0x45c5, 0x41d8, 0xb032, 0xbaf2, 0x0ea6, 0x45da, 0x4201, 0xb08f, 0xba40, 0x46fd, 0xb036, 0x4299, 0xb204, 0x41b6, 0x2913, 0x3bba, 0xba0b, 0x0540, 0x417a, 0xbafd, 0xbf73, 0x4048, 0xb25b, 0x1f4c, 0x429e, 0x3765, 0x1070, 0x1cd1, 0xa19b, 0x462d, 0xae77, 0x4138, 0x4417, 0xb26e, 0xade1, 0x1fc2, 0xb066, 0x43e2, 0x45e4, 0xba67, 0xb2dc, 0x1ce1, 0x4618, 0x41f8, 0x41cc, 0xbfd9, 0x21de, 0xba18, 0xbad6, 0x1ced, 0x4268, 0x408e, 0xbf42, 0xb203, 0xb22f, 0x41a0, 0xb01e, 0x148e, 0xb213, 0x1f18, 0xb066, 0x4220, 0x402f, 0x2024, 0xb0d0, 0xb029, 0x4013, 0xb2e6, 0xaad2, 0x4412, 0x2186, 0x08b7, 0xb017, 0x2c68, 0xac81, 0x03a5, 0x410d, 0x1ad9, 0x4385, 0xbf8f, 0x423e, 0x43a9, 0x4540, 0xb206, 0x1913, 0x45f4, 0x410a, 0xba27, 0xb03a, 0xa1a3, 0x1bab, 0x40a9, 0x1e67, 0x1d41, 0x45a1, 0x43a2, 0x433c, 0xbf3e, 0x447b, 0x40fa, 0x42f1, 0xbfba, 0x3ad7, 0x1a64, 0x436a, 0x4236, 0xb244, 0x423e, 0x46ca, 0x20e3, 0xb2e8, 0x433e, 0xb2e5, 0x2bee, 0x42bb, 0xa98d, 0xad9c, 0x4386, 0x2257, 0xbff0, 0x42c6, 0xbf33, 0x4493, 0xb283, 0x42fe, 0x2141, 0x4036, 0x45ee, 0x4021, 0x4094, 0x1804, 0x415d, 0xbf83, 0x1901, 0xba69, 0x40b4, 0x2756, 0xa4e3, 0x46f5, 0x44e4, 0xbf08, 0x409c, 0xb28d, 0x432e, 0x40b3, 0x2d20, 0x3e1f, 0xbfb6, 0xada3, 0xba6b, 0x1a51, 0xb230, 0x417d, 0xb08f, 0x4147, 0xb204, 0x1c74, 0xbfc0, 0x40d6, 0x297f, 0x42da, 0x4647, 0x0785, 0x41ed, 0x43e8, 0xbfc3, 0x1814, 0x40f2, 0x44d1, 0x4336, 0x40cd, 0x4310, 0x0796, 0x0448, 0x0afd, 0x2b10, 0x454c, 0x409c, 0x4071, 0x4190, 0x022b, 0x42aa, 0x4077, 0x3044, 0x43c2, 0xb201, 0xbf8d, 0xba41, 0x4057, 0xb057, 0x1b40, 0xb24a, 0xa1a7, 0x4307, 0x0539, 0x416a, 0xba50, 0xbf87, 0xba7b, 0x43a4, 0x2500, 0xba5b, 0x43bb, 0x42f3, 0x4611, 0x4280, 0xbac7, 0x41ae, 0x1ca3, 0x43b2, 0xa44d, 0xba30, 0x0ab4, 0x1dcd, 0x461a, 0x435a, 0x025a, 0x432a, 0x20e9, 0xa954, 0x4392, 0xba42, 0x424c, 0x41b8, 0xb235, 0xbf51, 0x4185, 0xb006, 0x4049, 0xb26a, 0x417b, 0xb0e2, 0x19d5, 0x3021, 0xbf00, 0x19dd, 0x4005, 0x4153, 0x1c3e, 0x411b, 0xb272, 0x4222, 0xb249, 0x45e0, 0xb258, 0xb0fc, 0x193e, 0x42eb, 0xb2b2, 0x3edb, 0x4186, 0xb0cb, 0x302a, 0xbf36, 0xb289, 0x4139, 0x4193, 0xb08c, 0x42b8, 0xb254, 0x4421, 0x411c, 0x1ef1, 0xb041, 0x46e3, 0xbfb8, 0x43c0, 0x4015, 0xa898, 0xb20e, 0x42ce, 0x42ac, 0x4067, 0x43d6, 0x42a2, 0xba33, 0xbafe, 0x4208, 0x4142, 0x4159, 0x328e, 0x31d0, 0x40f7, 0x318d, 0x191f, 0x4393, 0x21e0, 0xbf15, 0x4350, 0x19a5, 0x44b5, 0x1a52, 0x18d9, 0x4298, 0xb2f1, 0xb283, 0x1d3c, 0x184a, 0x1919, 0x4185, 0x389d, 0x1fcf, 0x4075, 0x4354, 0x426d, 0x412b, 0x08c8, 0xaa20, 0xb0fc, 0x430a, 0x4371, 0xb036, 0xba55, 0x401d, 0x0a8a, 0x4216, 0xbf76, 0x418a, 0x438a, 0x4239, 0x41f2, 0x43bf, 0x1183, 0x163f, 0x1d04, 0x44d4, 0x4142, 0x40ec, 0x2292, 0x4216, 0x114a, 0x402c, 0x4182, 0x4274, 0x4175, 0xbf34, 0x0866, 0xba17, 0x425d, 0x408c, 0x455f, 0x4226, 0x352f, 0xbfd0, 0x41aa, 0xba23, 0xb22e, 0x4332, 0xb2dc, 0xb0f0, 0x41c1, 0x4244, 0x1935, 0xba0a, 0x434a, 0x4102, 0xbf3b, 0x4006, 0x40df, 0xb0f8, 0x4056, 0x400f, 0x43f9, 0x43ce, 0x42fe, 0x40d4, 0x28b0, 0x429b, 0xbf74, 0x182a, 0x41cb, 0xb286, 0x43e0, 0xbaf2, 0x4048, 0x4290, 0xb01d, 0x42a0, 0x4607, 0xb05c, 0x4350, 0x1c44, 0x332b, 0x4472, 0x41e1, 0x4367, 0x4326, 0x246e, 0x424d, 0xacbc, 0xb25d, 0x42fc, 0x1cfd, 0xbfba, 0x1862, 0x4434, 0x4642, 0x1a03, 0xac03, 0xb011, 0xb268, 0x0d38, 0x4269, 0x447e, 0x40d4, 0xb02c, 0x08bf, 0xb2b2, 0xbfa0, 0x410b, 0xb284, 0x1c4a, 0x414b, 0xbaf7, 0x4177, 0x3a36, 0xbf4a, 0x1dd8, 0x41c6, 0x4094, 0x4586, 0xbfb9, 0xb0e7, 0x43c0, 0x45dc, 0x0f5d, 0x164f, 0x0ba3, 0x1ddc, 0x0097, 0xb29b, 0xba38, 0xba74, 0x4381, 0xb0da, 0x4250, 0xb2a1, 0x41d4, 0x4081, 0x43bb, 0x4406, 0x345b, 0x3396, 0x1de6, 0x1962, 0x1b0e, 0xbf09, 0xb297, 0x44a0, 0x2a36, 0x3650, 0x427f, 0xb26b, 0xb271, 0x438f, 0x4339, 0x4129, 0x376e, 0x4296, 0x1ea5, 0xbfd8, 0x4225, 0x41e9, 0xbadc, 0x4033, 0x06d5, 0xb204, 0x4004, 0x43db, 0xb056, 0x1a4d, 0x168e, 0xbf13, 0xb2c3, 0x40b7, 0x4099, 0x0e40, 0x04b2, 0x2491, 0xbfb0, 0xba29, 0x40ab, 0x45c5, 0xb235, 0x4682, 0x44cd, 0x4336, 0xa9f5, 0x3173, 0xb2ba, 0xb2ae, 0x410b, 0x2490, 0xba68, 0xb05a, 0x4243, 0x4046, 0xb29e, 0xb26d, 0x412d, 0xbfc5, 0x3c36, 0x43ad, 0x4233, 0xb276, 0x1bbe, 0x4130, 0xad7d, 0x43e8, 0xbf86, 0x42b4, 0xb201, 0x39a2, 0xa298, 0x422d, 0x420e, 0xbae0, 0x42ba, 0x419b, 0x4373, 0x3c8a, 0x1b84, 0x2f19, 0x4215, 0xbae3, 0x4301, 0xbfd4, 0xb271, 0x4071, 0x184c, 0xbaf9, 0x1a13, 0x46d8, 0xbfc7, 0xb230, 0x46e9, 0x3397, 0x44d2, 0x2603, 0xba1b, 0x217b, 0xb0fd, 0x42c2, 0x0680, 0x1e31, 0xb203, 0xbac1, 0x2ed6, 0x41cc, 0xb232, 0x423d, 0x24a3, 0xbf43, 0x3980, 0x4273, 0x4234, 0xa0f4, 0xb270, 0x401e, 0xad39, 0xb045, 0x1bbb, 0x1f36, 0xb0ac, 0xba3f, 0xb227, 0x4433, 0xb001, 0xb060, 0x1679, 0x287b, 0x4134, 0xb2b5, 0xabaa, 0x1a26, 0x44a2, 0x42fe, 0xb293, 0xbf8d, 0x4198, 0x1f37, 0xb2c9, 0x425c, 0xb2e3, 0xaaa2, 0xbf26, 0x4306, 0xb013, 0x1e28, 0x1668, 0x411a, 0x4370, 0x0257, 0x1efc, 0x4060, 0x4243, 0x4419, 0xb211, 0x1ade, 0x413e, 0xbfd6, 0x4329, 0x4305, 0x423c, 0x1a58, 0x4075, 0x4377, 0xb24c, 0x4055, 0x1b29, 0xb212, 0xba6d, 0x4011, 0x265d, 0xaa17, 0xa9c1, 0xb2ed, 0x4011, 0x31b0, 0x421d, 0x4399, 0xb0da, 0x4040, 0x4160, 0xbfd0, 0xbad9, 0xbfb4, 0x45f3, 0x4329, 0x434a, 0x40fe, 0x432e, 0xb290, 0x4043, 0x4077, 0xba4d, 0x238f, 0x40ea, 0x46da, 0x3def, 0xbf0e, 0x4565, 0x180b, 0x25be, 0xba29, 0xb0cf, 0x421a, 0xba15, 0x0c57, 0x1ca8, 0x4202, 0x4369, 0x3400, 0xb230, 0xb010, 0x1e9e, 0x4185, 0xaa1b, 0x4069, 0x41f7, 0xba0a, 0x1bbf, 0x401d, 0x1bff, 0xbf2a, 0xb24b, 0xb06f, 0x24ba, 0xa781, 0x42f6, 0xb26b, 0x2a8a, 0xba6c, 0x2c03, 0xb2e8, 0xb24a, 0x42aa, 0x45e3, 0x40a0, 0x40e2, 0x4044, 0x406e, 0x4011, 0xb2b0, 0xbfa7, 0x4155, 0xb20c, 0x421a, 0xb011, 0x06e8, 0x1c54, 0xb20d, 0xafeb, 0xbf90, 0xbfa1, 0x1d96, 0xb2c9, 0xbae8, 0x4123, 0xb29f, 0xac33, 0xbfc0, 0xb2d6, 0x119e, 0x41fa, 0xba79, 0x436a, 0x41f6, 0xbadc, 0x406d, 0x08c6, 0xbafc, 0x4247, 0x419d, 0x40e4, 0x4032, 0xbf9d, 0xb2b5, 0x4301, 0x1fd1, 0x1878, 0x190b, 0x41b6, 0x1a06, 0x1884, 0x413e, 0x1e69, 0xb26e, 0x400f, 0xba25, 0xacaa, 0xaa90, 0xb2f7, 0x41be, 0x414d, 0x2fe7, 0xb01b, 0xbf6b, 0xbf80, 0x4424, 0x1ac3, 0x2b49, 0x42d4, 0xba6d, 0x00b4, 0xaccd, 0x2bba, 0x42b0, 0x4317, 0x408d, 0x4088, 0x1c63, 0x41f2, 0x340f, 0x41be, 0xbfbc, 0xba04, 0xb2a4, 0xb03e, 0x41f3, 0x43d5, 0x3fb9, 0x4130, 0x40b7, 0x1a94, 0xba2c, 0xb24d, 0x40ac, 0x4397, 0x1e2d, 0x0f84, 0x1ac9, 0x4094, 0xba79, 0x4014, 0x345f, 0x4369, 0xa53c, 0xb236, 0x417e, 0xba2d, 0x0ddf, 0x41eb, 0xbfd8, 0xb236, 0x4430, 0x1c1f, 0x408b, 0xb260, 0xab37, 0xb252, 0x40e5, 0x41fe, 0xb289, 0x4157, 0x416e, 0x1cc9, 0xbfb0, 0x40ba, 0x4358, 0x1f8c, 0x40fb, 0xb009, 0xb296, 0x3130, 0x4042, 0x43ca, 0x433c, 0xbf12, 0x4349, 0xbfa0, 0x4092, 0xb0f5, 0xb2a1, 0x4112, 0x2f71, 0xba43, 0x4480, 0x4075, 0x4066, 0x09fb, 0x404b, 0xb263, 0xbfa9, 0xb224, 0x41dd, 0xb03f, 0x19ba, 0x45e5, 0x1a02, 0x40ef, 0x3fa2, 0x1ee5, 0x0f21, 0x4175, 0x4009, 0x4364, 0x0066, 0x4243, 0x1b6e, 0x3b34, 0x40fd, 0x058f, 0x4021, 0xa4f2, 0xb2c8, 0xbf3a, 0xbfe0, 0x402b, 0x4633, 0x2f79, 0xb027, 0xb066, 0x4046, 0x186b, 0x4288, 0x41ef, 0x28df, 0x3cb6, 0x0efe, 0xba01, 0x42a7, 0x3d58, 0xbae9, 0x41eb, 0x1c69, 0xbae0, 0x434d, 0x44f4, 0x41fc, 0x32c2, 0xb028, 0xbf71, 0x40a0, 0x2fd0, 0x1eea, 0xb02c, 0x4393, 0x3037, 0x2793, 0x405e, 0x25a9, 0x2f3c, 0x3e1b, 0xb2fe, 0x41ba, 0xbfd7, 0xba39, 0xb249, 0x4150, 0x43c2, 0x43e1, 0xba14, 0x412c, 0xb003, 0x0b24, 0x4243, 0x306c, 0x436a, 0x1a6a, 0x1cff, 0x1599, 0x43b2, 0x4306, 0x4264, 0x424f, 0x22b4, 0xb2bb, 0x418e, 0x4021, 0xbf0f, 0xb243, 0x1971, 0x43d7, 0x435d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xa631da32, 0x8b25e0fa, 0xc036f7d7, 0x14e1473e, 0x0d42c45b, 0x1272500d, 0xe6b2d752, 0xafd08c78, 0xdf628cd7, 0xcc7f4ab5, 0xc596f8ca, 0x035ba176, 0x9004931a, 0xcc587fb1, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x000000a3, 0x00000000, 0x000000b4, 0xffffffa3, 0x00000000, 0x000000a9, 0x000000b3, 0x00000001, 0xe32471b4, 0xcc7f08d4, 0x000020a4, 0x000020a4, 0xcc7f6b59, 0xcc7f0a40, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1eba, 0xbf6c, 0x40f9, 0x42e2, 0xbf00, 0x4352, 0xba71, 0x415f, 0x4121, 0x4370, 0x18bf, 0xb228, 0xb21f, 0x437a, 0x410e, 0xa9be, 0x4094, 0xbf01, 0x1bfc, 0x417c, 0x1d9a, 0x342e, 0xb058, 0xa3e9, 0x15d8, 0x42da, 0xbfd6, 0x414f, 0x43b2, 0x46dd, 0x41c9, 0x46d0, 0x46f2, 0xba5f, 0x1558, 0x4221, 0x4156, 0x42f2, 0x38cc, 0x1fa0, 0x436a, 0xb062, 0x4070, 0x19c3, 0xb205, 0x4206, 0xba6a, 0x42be, 0x4388, 0x42ae, 0xb29c, 0xbf1b, 0xbae8, 0x00da, 0xa33e, 0x1ca7, 0x40fd, 0xbf81, 0xb2d7, 0xba69, 0x1ea1, 0xb0bb, 0x0645, 0x1cb6, 0xb27a, 0xbfb0, 0x28b8, 0xbad7, 0x40e0, 0x4253, 0xbaf6, 0x1466, 0x318a, 0xa4ad, 0x1f98, 0xbf4a, 0xbaf1, 0x4350, 0x08cd, 0xbfb6, 0xb047, 0x0b95, 0xb250, 0x405b, 0x4125, 0x42f5, 0x1da1, 0xa969, 0xbfc4, 0x400d, 0xb2b6, 0x4321, 0x43ec, 0xb279, 0x41e6, 0x066a, 0x1e01, 0xbf6c, 0xb260, 0xb099, 0x4237, 0x4192, 0x4453, 0xb258, 0x40c4, 0x1f86, 0x1f54, 0x1da2, 0xb0cf, 0x460b, 0x40e3, 0xbfc0, 0xb27b, 0xba0a, 0x424d, 0xb099, 0x1e52, 0xb213, 0x4073, 0xb091, 0x4456, 0x4549, 0xbfc0, 0xbfb5, 0x435a, 0x1eb5, 0xb2fc, 0xb2b8, 0x1ccd, 0x0313, 0xbacd, 0x1835, 0x4048, 0xba3e, 0x423e, 0xb0ef, 0x33e7, 0x406b, 0xb294, 0x4176, 0xaaa3, 0x43e9, 0xba3e, 0x4592, 0xb2b8, 0x41ee, 0x4166, 0x40d4, 0x407a, 0x41b4, 0xb08c, 0x466c, 0x466b, 0xbf84, 0xb256, 0x05dd, 0x402d, 0x400d, 0xae81, 0x448a, 0x409e, 0xb2bb, 0x4330, 0xb2a2, 0x3a1d, 0x155f, 0x4375, 0x42a3, 0x4042, 0x1e6c, 0x4043, 0x437f, 0xbf5d, 0x0a83, 0x0278, 0xbaef, 0xb225, 0xb2c6, 0xa084, 0x42c4, 0xb044, 0xb276, 0x4254, 0x1ff5, 0x3ecd, 0x42d5, 0x40ab, 0x1d0b, 0xbf5b, 0xa84c, 0x42fa, 0x4153, 0xb20f, 0x43f5, 0xb0ae, 0x40a6, 0xba43, 0x40d1, 0x4031, 0xb201, 0x40a2, 0xa821, 0x41ba, 0x057c, 0x4158, 0xbfdc, 0x4058, 0x0e46, 0x41b1, 0x1ede, 0x430b, 0x427a, 0x2579, 0xb210, 0x10b9, 0x40ac, 0x144d, 0x277e, 0x4361, 0x407b, 0x4292, 0x438e, 0x1c52, 0xbf29, 0x43da, 0x4175, 0x40cd, 0x422e, 0x1bce, 0xbafe, 0x4048, 0x4392, 0x1f58, 0xb06f, 0x4172, 0x140a, 0x42c1, 0x4129, 0x3dc7, 0x43c9, 0xbf62, 0x1cfc, 0x1c76, 0xb258, 0xb2a7, 0x0c27, 0x428d, 0x419d, 0x4247, 0xbfa0, 0x4003, 0x462a, 0xb20e, 0xba7f, 0x410b, 0xbf49, 0xb063, 0x406f, 0x1d81, 0x0426, 0x1ecc, 0x37da, 0xba5c, 0x265f, 0xba45, 0x4623, 0xb2ce, 0x437a, 0x3263, 0x4216, 0x46d2, 0xbf60, 0xb2b3, 0x438b, 0x43a6, 0x127c, 0x2a4a, 0xb087, 0x418d, 0xbfdc, 0xbaf9, 0x2d49, 0xbad8, 0x4555, 0x4034, 0xbf3c, 0x4163, 0x4195, 0x1af2, 0x436b, 0xba0c, 0x4645, 0xb006, 0xba20, 0x2774, 0x0b44, 0x4191, 0x40c4, 0x3255, 0x4316, 0xad9e, 0xb281, 0x2148, 0x19bf, 0x2cb2, 0x41da, 0x4245, 0x4304, 0xbf1c, 0x4266, 0x40d7, 0x1d42, 0x420e, 0x0277, 0x4072, 0x1f21, 0x4117, 0xbfcb, 0x4020, 0xbaf0, 0x3750, 0x438c, 0xb249, 0xb232, 0x42bf, 0x087b, 0xbf60, 0x20a3, 0x2159, 0x4376, 0x401f, 0xac09, 0x41b6, 0xb2eb, 0x44e0, 0x44f5, 0x4180, 0xbf70, 0x4027, 0x4376, 0x4249, 0xbf76, 0x4013, 0xa685, 0x4178, 0x1b24, 0x3bf1, 0x43fc, 0x4564, 0x4359, 0xbf60, 0x3001, 0xbf7a, 0x42b3, 0x431f, 0x0fb3, 0xba60, 0xab2c, 0x4084, 0x4382, 0x1b93, 0x445d, 0x0fa1, 0x42c8, 0xaf44, 0x326c, 0x2fce, 0x41fa, 0x46ed, 0x435a, 0x43e7, 0x3b92, 0x41ad, 0x4387, 0x4148, 0xb243, 0xba0f, 0x468d, 0x41d3, 0x4283, 0x3a35, 0xbf74, 0xb280, 0xb26e, 0x4288, 0x1b27, 0x3713, 0x460d, 0x4608, 0xa4ad, 0x40af, 0x2de0, 0x111b, 0xbafa, 0x421f, 0x432d, 0xba3b, 0x4353, 0x418e, 0xbfa8, 0x41f0, 0xba53, 0xb0e1, 0xb0b1, 0x13bc, 0xba4a, 0x42d6, 0xb26d, 0x468c, 0x4064, 0xb048, 0xa308, 0x4171, 0xbf7a, 0xa025, 0x4079, 0x287b, 0xb2d9, 0xafc6, 0x1287, 0x3e24, 0xbfc0, 0x4383, 0x0175, 0xb229, 0xb098, 0xba56, 0x1cdf, 0xb216, 0x3b28, 0x4083, 0x41cd, 0x4233, 0xb0fb, 0xba6a, 0x40f7, 0xb092, 0x4151, 0x4332, 0xbf01, 0x431e, 0x4064, 0x3e5b, 0xbae3, 0x41a0, 0xbad8, 0x4396, 0x16ef, 0xb2ac, 0x43db, 0xba05, 0x42a4, 0x287a, 0xbfe4, 0x423c, 0x4093, 0x4363, 0x428c, 0x4245, 0xbf1d, 0xb288, 0x4170, 0x44fd, 0xb20d, 0x1ba5, 0x41df, 0x404e, 0x1bf4, 0x363f, 0x4235, 0x2879, 0x3929, 0x1c19, 0x462f, 0x40ea, 0xb291, 0x1ba1, 0x40fa, 0x1144, 0xb291, 0xbf16, 0x4355, 0x410a, 0x4359, 0x2f7f, 0xbf4a, 0x4315, 0x18ae, 0xb28b, 0x013c, 0x2be1, 0x423a, 0x426b, 0x2686, 0x0bda, 0xb070, 0x1a04, 0x433b, 0x4000, 0x4374, 0x122a, 0xab72, 0x240a, 0x43cd, 0x1fcc, 0xb24a, 0x3817, 0xb28a, 0x43a1, 0xbfbf, 0x41ad, 0x4353, 0x1ca2, 0x0f5f, 0xb2cc, 0x335c, 0xbfdc, 0x4564, 0x42f9, 0xb27b, 0x4334, 0x4164, 0x3681, 0x42f9, 0xbf4e, 0x4638, 0x4449, 0x43ed, 0x19bb, 0xb215, 0xbfe0, 0x423f, 0x2108, 0x4383, 0xb228, 0x4652, 0x1dc3, 0xba3f, 0xbfc4, 0x438b, 0x40e8, 0x4567, 0x418c, 0x40fc, 0xba28, 0x40b1, 0x1e4c, 0xbfba, 0x3458, 0xb0ab, 0x2ad2, 0x45e9, 0x1a0c, 0x43b5, 0x4198, 0xab8d, 0x41f9, 0x1d52, 0x4436, 0x2e46, 0x436d, 0xba14, 0x449b, 0x1c84, 0xb2bc, 0xba6a, 0xbf28, 0x382e, 0x41c1, 0x4630, 0x40fb, 0xb25a, 0xa5ff, 0xbf66, 0x407f, 0x1ba9, 0x40f7, 0xba4d, 0xad6a, 0x42d4, 0x416a, 0x41ec, 0x058b, 0xb03f, 0x400b, 0x41de, 0x42f8, 0xb239, 0x43a1, 0xb0bc, 0xb278, 0x3723, 0x2aba, 0x08ce, 0xbac4, 0x4117, 0xba71, 0x43e7, 0x4160, 0x40f9, 0xbfb5, 0xb204, 0x46aa, 0x2031, 0x427d, 0x434d, 0xbac1, 0x41c2, 0xba36, 0x3418, 0x4154, 0x0077, 0xbf8f, 0x1ce1, 0xb299, 0x403b, 0x4327, 0x417e, 0x2a98, 0x41f9, 0x44fb, 0x4284, 0x3e49, 0x4338, 0xb20a, 0x0cc0, 0xb24c, 0x4379, 0x0318, 0x4361, 0xaf1c, 0x4374, 0x4050, 0x18ef, 0xb060, 0x2d77, 0x1924, 0x41bd, 0xbfa8, 0xb0e8, 0x4190, 0x42dc, 0x415d, 0x40ea, 0xb23b, 0x400a, 0x22d5, 0xb25d, 0x1dd6, 0x1d96, 0x2ce9, 0xbf91, 0x4038, 0xb066, 0x43e7, 0x1822, 0xb269, 0x4593, 0xb2f5, 0x19bd, 0xa3f6, 0x0e7e, 0xb24b, 0xba7d, 0x42ca, 0x4663, 0xbfad, 0x4299, 0x1e65, 0x4225, 0x2788, 0x43e8, 0xb0d3, 0xba16, 0x1e5d, 0x404a, 0x1f58, 0x4365, 0xaa39, 0xb2bb, 0xb220, 0x4582, 0x43a7, 0xb256, 0xb209, 0xb2ab, 0xb0d2, 0x43e4, 0x42c8, 0x413f, 0xb243, 0xbf53, 0x2e4e, 0x466d, 0x4037, 0x41b3, 0x4440, 0xbf49, 0x42be, 0xb2cd, 0x4600, 0x32a9, 0x4148, 0x446c, 0x4220, 0xb0de, 0x0ce2, 0xb0b4, 0xabd7, 0x42ff, 0xb026, 0x419a, 0x40b3, 0x0b25, 0xb2cd, 0x3c35, 0x19ab, 0x1f78, 0xbfb0, 0x0e94, 0x1e9d, 0x258f, 0xa68b, 0xbf65, 0xb08c, 0x4419, 0x079e, 0xb268, 0x0e32, 0xba48, 0xb263, 0x404f, 0x0184, 0x4363, 0x40ac, 0x2bea, 0x4333, 0x4212, 0x1b18, 0x2632, 0x419e, 0xaddd, 0x26d7, 0xab35, 0x43ae, 0x44a1, 0x4553, 0xa3ae, 0xb035, 0x43eb, 0x431a, 0x3b78, 0xbf5f, 0x44f2, 0xa8a4, 0x2b62, 0x41b5, 0xb2a0, 0xb26b, 0xbf60, 0x408a, 0x42f5, 0xb248, 0x4186, 0x0be3, 0xba08, 0xba09, 0x43b1, 0x32eb, 0x435e, 0x1a5f, 0x46a1, 0xa636, 0x40a4, 0x1cc4, 0xb205, 0x4008, 0x1ab0, 0x43dd, 0x44ba, 0x1a0a, 0x41e8, 0xbf0b, 0x4107, 0x43fb, 0xb278, 0x0d1b, 0xb24f, 0x4072, 0xb0fe, 0x1f22, 0x41d6, 0xa4b7, 0xa2e7, 0xbace, 0x1986, 0x41ea, 0xbf8f, 0xb2bd, 0xa889, 0x2c1e, 0x4377, 0x43fe, 0x4208, 0x0d22, 0x1fb7, 0x13a5, 0x1dc7, 0xb2a7, 0x4322, 0xb0e3, 0x20ce, 0x2c78, 0xb02e, 0x1dd1, 0x4260, 0x409c, 0x4072, 0x41c0, 0x43b5, 0xb229, 0x42ae, 0xb2fb, 0x41c4, 0x0949, 0xbf46, 0x4240, 0x0cde, 0x1cd9, 0x3d63, 0xba31, 0x4182, 0x46c9, 0x207c, 0xbfd0, 0xba2e, 0xb03a, 0xa2cf, 0x430a, 0xbf00, 0xbfa3, 0x2012, 0x1aef, 0xbae5, 0x429b, 0x42a3, 0x40d8, 0xb27e, 0x43e3, 0x00d0, 0xbfd0, 0xb0f6, 0x235a, 0x4206, 0x28e5, 0x12df, 0x44c8, 0x29d4, 0x07dc, 0xb0ee, 0x2a46, 0x4684, 0x41c8, 0x4256, 0x419f, 0x1840, 0xbfb6, 0x2d6c, 0xbf00, 0x4111, 0xb2a4, 0x2ac4, 0x4357, 0x2969, 0xbfdb, 0x06e7, 0x1834, 0x4303, 0x1301, 0x4349, 0x4359, 0x4199, 0x42d7, 0x43df, 0x1c72, 0xbf29, 0x42e9, 0x0dc8, 0x444f, 0xb2b3, 0xbade, 0x4096, 0xbf39, 0x4222, 0x4132, 0xbaf8, 0x423a, 0x411a, 0x3d0e, 0x27cd, 0xa5b1, 0xba60, 0x402c, 0x1c06, 0x1e7f, 0x4125, 0x437c, 0x2d39, 0x41e8, 0x43b0, 0x1aec, 0x410f, 0xbf70, 0x4151, 0xbfd7, 0xb24b, 0x434a, 0x420a, 0xb0bb, 0x40b5, 0x2a48, 0x42f7, 0x4357, 0xa542, 0x4377, 0x456a, 0xba32, 0x256c, 0x4293, 0x420d, 0x41b2, 0x4116, 0x0db1, 0xb2fd, 0x2b59, 0xbf5e, 0x4386, 0x46a9, 0x44f0, 0x403d, 0x42a2, 0xbfad, 0x4332, 0xb278, 0x33f7, 0x4154, 0x42d1, 0x45a4, 0xbf2c, 0x43d3, 0x4328, 0x1b3b, 0x2fb0, 0xb2fc, 0xba1f, 0x4081, 0xbac6, 0x43a8, 0xaf84, 0x41f1, 0x4242, 0x417b, 0xb210, 0xbf2e, 0x40d4, 0x43d6, 0x1ccf, 0x187b, 0x308a, 0x4377, 0x42ba, 0x31d7, 0xaffe, 0x1fcd, 0x44e8, 0xb28b, 0x1bb6, 0x441a, 0xa875, 0x40b0, 0x40b8, 0xa3ff, 0xbaea, 0xbf0f, 0xb025, 0xb00c, 0x400b, 0xb242, 0xba32, 0x46f3, 0x4637, 0xba20, 0xa645, 0x42df, 0x40a8, 0x42ed, 0xbf5c, 0x1a0c, 0x1899, 0xbaf9, 0xb259, 0x430a, 0xb2f9, 0x026c, 0x4245, 0xb2d4, 0x2e6f, 0x4339, 0xa74d, 0xafb6, 0x410a, 0xb20b, 0x431a, 0x41cc, 0xaa3b, 0x4138, 0xb0c4, 0x4481, 0xb22d, 0x4048, 0xbf81, 0x29ef, 0x44eb, 0x4173, 0xbff0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x229975ae, 0x62ce2e1e, 0xd2564bd4, 0x39d45247, 0xd49d4418, 0x1318e716, 0x729bdcf6, 0x3efcaaa5, 0xa00a7b25, 0x4ae3af8e, 0x4525a887, 0x9586bb86, 0x0c982ef9, 0x7b92ae66, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0xfffff740, 0x00000000, 0x000000a8, 0x00000000, 0x000018d8, 0xfffff92c, 0x51bdcd40, 0x00000000, 0x00000005, 0x00000000, 0xfffffff8, 0xfffff544, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4140, 0xb2b3, 0x4323, 0xba15, 0x4344, 0xbff0, 0xbfbf, 0x43a6, 0x4559, 0x4223, 0x44dc, 0x4127, 0xb0a9, 0xbf8a, 0xaaf8, 0x46c3, 0x4581, 0x40c3, 0x34c1, 0x0454, 0x4391, 0x1926, 0x0506, 0x409b, 0xb240, 0xb2d2, 0xb2bc, 0x41a3, 0x401e, 0x467e, 0x461e, 0x418a, 0xbfa2, 0x4106, 0xb2d4, 0xba41, 0x41e2, 0xbf60, 0xa5bd, 0xb0f2, 0x0ae4, 0xaf92, 0x4178, 0x4288, 0xaafb, 0x2c49, 0x4297, 0x42aa, 0xb279, 0x417e, 0x4030, 0xb092, 0x43af, 0xb2dd, 0x40f2, 0x4248, 0x4185, 0x424d, 0x1bde, 0x42be, 0xbfa3, 0xb2fb, 0x4202, 0xb284, 0x1e62, 0x4435, 0x1a75, 0xad47, 0x1f63, 0xbfc9, 0x4248, 0x42a4, 0x4043, 0x406f, 0x4235, 0x2e18, 0xba30, 0x0eb5, 0xb010, 0x4284, 0x1c3d, 0xba47, 0x1ab5, 0x40c5, 0x42bb, 0x42b3, 0xb2bd, 0x40cb, 0xbf66, 0x42b1, 0xba69, 0xa9e3, 0xba74, 0xba6b, 0x362a, 0xbaf3, 0x41fa, 0x3157, 0x43e1, 0x197e, 0x2357, 0x2121, 0x4399, 0x40e8, 0xba4e, 0x46a4, 0xb24a, 0x1e4c, 0xbf14, 0x434f, 0xba7e, 0x0794, 0x41a9, 0x4271, 0x0540, 0xb245, 0x4034, 0xa808, 0xa761, 0x1e1b, 0x432a, 0xbfd0, 0x4056, 0x0f35, 0x42dc, 0x41e8, 0x28b7, 0x41e4, 0x42d3, 0x4003, 0xb266, 0x23b0, 0x40f3, 0xbfc2, 0xba05, 0x37dc, 0xa115, 0x2d82, 0x427d, 0x4267, 0xb231, 0x425b, 0x4333, 0x15c9, 0x4365, 0x33cc, 0x4245, 0xb258, 0x4456, 0x418d, 0xbfda, 0xa73d, 0x4151, 0xb06a, 0x113e, 0x40a9, 0x0bd6, 0x4011, 0x0e0d, 0x428b, 0xb085, 0x1887, 0x4306, 0x1e58, 0xba34, 0x0264, 0x1425, 0xbf75, 0xbf80, 0x063a, 0x4075, 0xb0ad, 0x4301, 0x41a9, 0xb2de, 0xb06d, 0xbf4e, 0x2c08, 0x195b, 0x1f78, 0x4665, 0x18a8, 0x427d, 0x1e10, 0x1902, 0x4184, 0xb26c, 0x4202, 0xabcc, 0xa09e, 0xb240, 0xbf97, 0xb2f9, 0x0e5d, 0x4016, 0x41e6, 0x0fa0, 0xb03e, 0xba09, 0xba2d, 0x1d8d, 0x4236, 0x4169, 0x42e1, 0xbfa2, 0x1c7a, 0x4209, 0x46d8, 0x40ba, 0x131e, 0x43e9, 0x4322, 0x2c4c, 0xb0f2, 0x420d, 0x43cf, 0xbfe0, 0xbaf7, 0x11fd, 0x3c3c, 0xb074, 0xa603, 0x40c0, 0xba24, 0xbfdf, 0xba1f, 0x4355, 0x1c3e, 0x3c1f, 0x1771, 0x435e, 0x43f0, 0xbfe0, 0xbf1e, 0x2909, 0x4388, 0xb283, 0xbfb0, 0x4345, 0xb037, 0x4422, 0xba30, 0xb299, 0xb27c, 0xba4e, 0x439c, 0x402a, 0xbfb7, 0x42b5, 0x4306, 0x12ed, 0x4036, 0xa7ea, 0x3301, 0x00dd, 0x43ff, 0xbf80, 0x43a5, 0xa662, 0xb2cc, 0x1fd1, 0xb2f3, 0xb2e5, 0xba3b, 0x41d9, 0x19bb, 0xbf53, 0xb2d3, 0x4060, 0x40a0, 0x41e4, 0x430e, 0x1ace, 0xb292, 0x4126, 0x14a7, 0x4213, 0xba11, 0xba68, 0xbaea, 0x40f4, 0x1efb, 0xad4e, 0xb219, 0x403a, 0xa6a1, 0xbac6, 0x400e, 0x4179, 0x27cf, 0xbf2c, 0xb00d, 0x41da, 0xba16, 0x43f4, 0x419b, 0xae62, 0x40e8, 0xba76, 0xa541, 0xba4f, 0x42c2, 0x41aa, 0x446e, 0x423c, 0x400e, 0xbf45, 0x411e, 0xba27, 0x4150, 0xbff0, 0xbfb0, 0x1d0a, 0x4110, 0x0264, 0xb08d, 0x4094, 0xb237, 0x433d, 0xb2df, 0x445a, 0x18bd, 0x232b, 0x42aa, 0x4049, 0x41e8, 0x4241, 0x43c0, 0xabef, 0xbf27, 0x466a, 0xb235, 0xb210, 0x40c1, 0x1b03, 0x34b9, 0x330b, 0x0d21, 0x1c91, 0x411e, 0x4309, 0x24b1, 0x4694, 0x4352, 0xb06b, 0x43fe, 0x4335, 0x4062, 0xb262, 0xb2cf, 0x41ba, 0x1f6a, 0xbf19, 0xaa47, 0x41d2, 0x3d1a, 0x4214, 0xb041, 0xba7d, 0x4256, 0x44cc, 0x443b, 0x0975, 0x40f5, 0xb218, 0x3e53, 0xb20d, 0x3524, 0xb266, 0x3cde, 0xb268, 0x4488, 0x4242, 0x0d85, 0x4359, 0xbfc0, 0xb2c5, 0xbf42, 0xb2ac, 0x30e2, 0x40f1, 0x0e8c, 0x4253, 0x41f5, 0x4338, 0x23ee, 0x4452, 0x435b, 0xbfe0, 0x4322, 0xba03, 0x4434, 0x425e, 0x4138, 0xb0f5, 0xb027, 0x1244, 0x4199, 0x069e, 0x1cd5, 0x43e7, 0xb251, 0x2463, 0x1b4f, 0x369f, 0xbf07, 0x3825, 0xb2a9, 0x425d, 0x01f8, 0x459d, 0x41f8, 0x1c1a, 0xbfb0, 0x4036, 0x400b, 0x4020, 0x42a5, 0x1c96, 0x4282, 0x1e80, 0x1a86, 0xa400, 0xbfd0, 0x437f, 0x42de, 0x4071, 0x43a6, 0x403d, 0xb2c8, 0x36f0, 0xbf7d, 0xa9ca, 0x4321, 0x4397, 0x3f2d, 0xb0ff, 0x1db9, 0x4327, 0x42ee, 0x4102, 0x1929, 0x1a60, 0x42ae, 0x43fa, 0x4238, 0x335e, 0x4195, 0x41a0, 0x434f, 0x07df, 0x0b88, 0x415c, 0x1bed, 0x1239, 0x2484, 0xbfe0, 0x429a, 0x43f1, 0xbf00, 0xbf81, 0x1d1a, 0x03ca, 0xb001, 0x1144, 0xba4c, 0x4588, 0x4298, 0x1adc, 0x0bf3, 0x425c, 0x40bc, 0x46d0, 0x4196, 0x3c97, 0x1a0e, 0x40d8, 0x4689, 0x4211, 0xb2c7, 0x3858, 0xbf94, 0x1f8b, 0x1dd1, 0x43fc, 0x4221, 0x4134, 0x4091, 0xbf45, 0xb216, 0xa369, 0x425f, 0x41f1, 0x1d77, 0x4351, 0x4052, 0x43b8, 0xaab0, 0x41bd, 0x436a, 0x10b8, 0x1be7, 0xb0a0, 0x4408, 0xba5c, 0x46ed, 0x4280, 0x43e5, 0xb24f, 0x413e, 0x40de, 0x4072, 0x0af6, 0xb0fc, 0x182b, 0xbf18, 0x40a2, 0x4274, 0xbf7b, 0x40e1, 0x4374, 0x1980, 0x446c, 0x3816, 0xb011, 0x1a4f, 0x432b, 0x3edd, 0x443b, 0x4100, 0x4089, 0x1aab, 0xadf1, 0xb27b, 0x41b2, 0x4654, 0x14fc, 0x42be, 0x0988, 0xbade, 0x01d9, 0xbfb2, 0x4495, 0xa451, 0xb236, 0x40a0, 0xa32b, 0x160f, 0x158c, 0x1b5a, 0x1bf9, 0x415f, 0xa9d3, 0x2d3b, 0x0186, 0x2ee4, 0xb2ee, 0x4203, 0x4282, 0xbf25, 0x42fb, 0x41cd, 0x4005, 0x412e, 0x4166, 0xb060, 0x419a, 0xb096, 0x421a, 0xbf9e, 0xbaea, 0xb24b, 0xb266, 0x04cc, 0x4346, 0x1cf3, 0x3752, 0x4191, 0x40df, 0x2bc9, 0xba3f, 0x418a, 0xa35c, 0x40bf, 0xb202, 0xbf75, 0xb022, 0xb2af, 0x4494, 0xb2b3, 0x42ff, 0x4234, 0xba3d, 0x4294, 0xafd7, 0xbf5e, 0x42dd, 0xb06e, 0xb289, 0x16e1, 0x4296, 0xba26, 0xb23d, 0x3187, 0x4257, 0x43b4, 0xb055, 0x43f0, 0xba3b, 0xba4f, 0x4264, 0x1d45, 0x4139, 0x2e94, 0x43c5, 0xbf3a, 0xa887, 0x1db3, 0xb2ed, 0x4155, 0x403a, 0x459c, 0xbf16, 0x4185, 0x435d, 0x4233, 0x4040, 0x195b, 0x3b21, 0x3ac0, 0x41d6, 0xb0d6, 0xbaf9, 0x32b7, 0xb25a, 0xa09f, 0x403a, 0x36e7, 0x01a5, 0x0cbb, 0x19d8, 0x432c, 0x4144, 0xb210, 0x40a9, 0x369e, 0xb208, 0xbfe0, 0xbf74, 0xba77, 0x43fd, 0x430e, 0x431d, 0x0f9e, 0x4093, 0x41ca, 0xac30, 0x1d21, 0x435a, 0xba6a, 0x4070, 0xa76e, 0x40a2, 0x3100, 0x433f, 0x1c9a, 0x447d, 0x0792, 0x28d8, 0xb2ae, 0x309b, 0xa509, 0xb2c1, 0x43d3, 0x1ab7, 0xbf39, 0xb261, 0xa83d, 0x0040, 0x43f9, 0x43aa, 0x0d1a, 0xb2f5, 0x1f4f, 0xa00a, 0x1b3a, 0xbae6, 0xbfba, 0x1dc5, 0x4244, 0x41c2, 0xb2ab, 0x4202, 0xb29c, 0xb257, 0xa703, 0x427e, 0xbf89, 0xb275, 0x4019, 0x46ca, 0x1d3d, 0x1a9b, 0x1f9c, 0x06a0, 0x2760, 0xb258, 0x42d9, 0xbfe0, 0xb284, 0x4693, 0xb028, 0xba10, 0x4070, 0xb0d6, 0x4117, 0x4144, 0x1cb9, 0xbafd, 0x414d, 0x428b, 0x404c, 0x40a9, 0xbfc1, 0xbad9, 0x1cd4, 0x4193, 0x2175, 0x4424, 0x4353, 0x41fb, 0x1085, 0xbfd9, 0x40d9, 0xb226, 0xba45, 0x4397, 0xba74, 0xb04a, 0x0585, 0x0166, 0x43fc, 0x0cb9, 0x4163, 0xa2c8, 0x4167, 0x4298, 0xb2f4, 0x4178, 0x25dd, 0xbf78, 0x4219, 0xba57, 0x2cec, 0x41eb, 0xb2b4, 0x42e3, 0x41d4, 0x4028, 0x19fc, 0x25f6, 0x42c6, 0x411a, 0xb2fc, 0x405f, 0x2db0, 0xba12, 0xba0d, 0x4086, 0x190a, 0xb0d4, 0xb0c7, 0x1a0e, 0x1865, 0x43d6, 0x40f3, 0xbf53, 0x4346, 0x2cce, 0x1b64, 0x4251, 0x4132, 0x3b90, 0x4106, 0x4068, 0x42c8, 0xba69, 0x435b, 0x409c, 0xb23c, 0x40d8, 0xba3a, 0x41f8, 0x0255, 0xba6c, 0x4057, 0x154c, 0x423d, 0xb295, 0xbf5e, 0x39c1, 0xb2c6, 0xb2aa, 0x42ba, 0x41e1, 0x2188, 0xba24, 0xba62, 0xb07c, 0x21ef, 0xb0f3, 0x43ec, 0x4167, 0x16d4, 0x03a1, 0x042a, 0x41bd, 0x41ef, 0x0ce7, 0x40b1, 0x1945, 0x4412, 0x416d, 0xbf73, 0x0cc6, 0x43be, 0x406c, 0x4650, 0x434e, 0xb09f, 0x0557, 0x13bd, 0x1647, 0x1a01, 0x40eb, 0x4257, 0x165b, 0x39b5, 0xba40, 0xbf8d, 0xbf00, 0x45cc, 0xacf1, 0xb21a, 0xb045, 0xba06, 0x42c3, 0x434c, 0x22f5, 0x4361, 0x4168, 0x2d5a, 0x4197, 0x42c2, 0x429f, 0x420f, 0x41ce, 0x4072, 0xb2e6, 0x42b7, 0x0373, 0x0dd4, 0xb2b9, 0x426e, 0x4398, 0x41d1, 0x4391, 0x4388, 0xbfbb, 0x1116, 0x41d9, 0x43a3, 0xb01c, 0x423c, 0xbf23, 0x31dc, 0x4363, 0x1dda, 0x41cb, 0xbf57, 0xba39, 0xa456, 0xb210, 0xb2c3, 0xbacd, 0xb2ae, 0x19df, 0x0c65, 0xbf3c, 0x42c9, 0x4073, 0x46a9, 0x41b0, 0xa506, 0xafdf, 0x4279, 0x416e, 0xa894, 0x15c3, 0x4182, 0x17b8, 0x41c0, 0x2e9c, 0x40bf, 0x4297, 0x42c9, 0x221d, 0xbfe0, 0x27c7, 0xb072, 0xbf13, 0x42e0, 0x1610, 0x43b9, 0x388e, 0x0521, 0xb29e, 0x38ac, 0x1a33, 0x4163, 0x40ae, 0x0262, 0x19ad, 0x1987, 0xbf04, 0x4029, 0x1f7d, 0x4358, 0x046c, 0x1f40, 0xbf00, 0x4052, 0x4228, 0xb0a8, 0xb094, 0xbfdc, 0x4183, 0x1e1d, 0xb006, 0xb244, 0x46b2, 0x40e0, 0x438a, 0x1cf2, 0x09e7, 0xb077, 0x3b0c, 0x42c6, 0x411d, 0x40f4, 0x1d92, 0xb230, 0x3ef5, 0x25ff, 0x40d3, 0xb2fa, 0x0906, 0x33f4, 0x4616, 0xbf93, 0x3405, 0x401a, 0xafb7, 0x4653, 0xb2f2, 0xba5f, 0x43cd, 0x433e, 0xaa58, 0xba2f, 0xb21f, 0x44f2, 0xb2c9, 0xb27f, 0xb219, 0x1f6e, 0x4234, 0xb241, 0x41fc, 0xb299, 0x4262, 0x4332, 0x43c9, 0x1ef1, 0xb209, 0xbf31, 0x4169, 0x4561, 0x439f, 0x43bb, 0x416e, 0x3af5, 0x435e, 0x2c99, 0x407a, 0x41eb, 0xa890, 0x4362, 0xb278, 0xba0b, 0x3322, 0x40ee, 0x4381, 0x3041, 0x434d, 0xbf33, 0xb0f7, 0x40a3, 0x46bd, 0xac4b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3b6339cb, 0xac4b9998, 0x998fa8a0, 0x40e73eaf, 0xf51f8ebf, 0x6f72ff20, 0xf89a82e1, 0x902e336b, 0xebc6f449, 0x4b10b8cd, 0x4c95ab80, 0xffe2804d, 0xe91ff5c2, 0x37fd5281, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0x00000041, 0xfffffff7, 0x0c803106, 0xf8000021, 0x37fd52a9, 0x02400009, 0x00000000, 0x00000000, 0x4c95ab80, 0x00000000, 0x06f00000, 0xc802acfb, 0x4af3391b, 0x37fd517d, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xa5fa, 0x42f7, 0xb2c5, 0x40ad, 0x440d, 0x430e, 0x1a77, 0xbadb, 0x4199, 0x40e9, 0xa24d, 0xb221, 0x1a13, 0x403c, 0x4128, 0xba46, 0xbf7c, 0x43e8, 0x1a3e, 0xba00, 0x1aaf, 0x42f7, 0x433a, 0xbacb, 0x46c1, 0x4312, 0x43a5, 0x2370, 0x40f9, 0x195f, 0x4133, 0xb06b, 0xba1a, 0xba11, 0x09a0, 0x42e4, 0x4269, 0x42f2, 0xb28d, 0x0ba3, 0x05ab, 0xb298, 0xbfcf, 0xbaff, 0x4302, 0x438e, 0x4286, 0xb06a, 0x42ae, 0xbae0, 0x049b, 0x39b9, 0x4570, 0x4222, 0x0878, 0xbf66, 0x3af7, 0xbf90, 0x45e6, 0x4321, 0x400b, 0x0fdf, 0x42ff, 0xba07, 0x4424, 0x427d, 0x2f2b, 0xb23e, 0x4233, 0xbad7, 0xba58, 0xb293, 0x4430, 0xa78b, 0x424f, 0xb2f0, 0x1cab, 0xbf4f, 0x431c, 0x4199, 0xb0fa, 0x29cf, 0x405a, 0x150b, 0xb2fb, 0x43a7, 0x41c5, 0x31b3, 0xb258, 0x40dc, 0xbfb0, 0xb0b5, 0xa486, 0x4338, 0xba40, 0xbfe0, 0xba52, 0x4157, 0xa730, 0x431c, 0x188f, 0xbf92, 0x4306, 0x415b, 0x251b, 0x407f, 0xaef4, 0x4127, 0x0478, 0x0bcd, 0xb2b8, 0xa743, 0x4220, 0x420f, 0x1bfb, 0x4340, 0xa62a, 0x4329, 0xbf70, 0x2233, 0x43d6, 0x46da, 0x06d3, 0x1a7b, 0xb2fb, 0x08eb, 0x4372, 0xb2cc, 0x20d8, 0x0227, 0xbfc2, 0x1f6f, 0x2906, 0xb2c8, 0x4676, 0xb086, 0xb21f, 0x43e1, 0x4094, 0xbf0b, 0x4269, 0x4153, 0x322b, 0x05ec, 0xb00f, 0x41e5, 0xab98, 0xb03e, 0xb2f7, 0x44dd, 0xb29b, 0x1b5f, 0xb223, 0x1136, 0xb222, 0xb0d3, 0xb2c3, 0x413d, 0xba0c, 0x40c4, 0xbaf2, 0x355b, 0x4352, 0xbaec, 0x428a, 0x3477, 0xbf07, 0x427f, 0x193c, 0x4101, 0x4047, 0x42bd, 0x316c, 0x1da0, 0x460e, 0x18ac, 0x442d, 0x4489, 0xb261, 0xbf6a, 0xb265, 0x4134, 0x430b, 0xb280, 0xb007, 0x146b, 0x0779, 0xb07d, 0x4478, 0x42b5, 0xb25c, 0xbfaa, 0xa1d2, 0x4139, 0xb206, 0xb212, 0xba5e, 0xb261, 0xb237, 0xba11, 0xba18, 0x4357, 0x4178, 0x43cd, 0x4234, 0x4134, 0xb2cc, 0x4388, 0x4374, 0xa39f, 0x43e8, 0x411e, 0xb274, 0xbf5f, 0x1e46, 0x1ddd, 0x404b, 0xb279, 0x37e1, 0x44c4, 0xb093, 0xbf6c, 0xbf70, 0xb068, 0x1584, 0x4018, 0xb256, 0x00cb, 0x4205, 0xa715, 0xbf70, 0x4253, 0x44c5, 0x1f85, 0x189e, 0xbfa0, 0x400c, 0x3a61, 0x424c, 0x46dc, 0xab38, 0xbfb7, 0x0388, 0xba10, 0x1f7f, 0xb052, 0x43f1, 0xbf80, 0xb26b, 0xb2c1, 0x4249, 0x40d6, 0x26e0, 0xbae6, 0x4132, 0x404f, 0x461b, 0x4062, 0x1a87, 0x23fe, 0xb216, 0xba38, 0x4141, 0xa221, 0x40ab, 0x19c9, 0x1f3c, 0xbf65, 0x40fe, 0xb268, 0x413a, 0xb0b3, 0x44e4, 0xa369, 0x4211, 0x2341, 0xbfb0, 0xbfb2, 0xb2aa, 0x2549, 0x4034, 0xb269, 0xa110, 0x4140, 0x4089, 0x4028, 0x2609, 0x4218, 0x43e4, 0xbf00, 0xb2e3, 0xba33, 0x42db, 0x40d0, 0x18fb, 0x3271, 0xbf19, 0x3067, 0x3c16, 0x4130, 0x3563, 0x072b, 0xb043, 0xb0df, 0xb077, 0x4326, 0x42cc, 0x24bd, 0x37cb, 0x41c6, 0x4350, 0x18da, 0xb267, 0x44da, 0x0379, 0xaf9b, 0x42c5, 0xb04f, 0x4012, 0x401e, 0x433c, 0x23d8, 0x4130, 0x1b4a, 0xb264, 0x416a, 0xbf18, 0xb2e9, 0xb056, 0x1944, 0xba2c, 0x4110, 0x2e24, 0x3a58, 0xb291, 0xba4a, 0x322e, 0x45d4, 0x43a9, 0xba4d, 0xbfe1, 0xba67, 0xa1ba, 0x0aab, 0x41d7, 0x1393, 0xaff4, 0xbfc0, 0xaa25, 0x4004, 0x4646, 0x044a, 0x42f6, 0x407f, 0x432c, 0x4394, 0xbf60, 0x409a, 0x4174, 0x0bde, 0x41b7, 0x42e1, 0x42e0, 0x19fd, 0x072c, 0xb005, 0x23da, 0xba36, 0x013b, 0x02b8, 0xbf83, 0x1896, 0x38ab, 0x1802, 0xa075, 0xba64, 0xb23b, 0x40e7, 0x4245, 0x42f4, 0xa000, 0xb27a, 0xbaef, 0x4052, 0x06e6, 0x3e0f, 0xb02a, 0x29cd, 0x0a60, 0x42ad, 0x25d3, 0x4220, 0xb24c, 0x1ad2, 0x4077, 0xb218, 0x4030, 0x4273, 0xbfc1, 0x4288, 0xb0e5, 0x42d9, 0xb219, 0x4303, 0xb2a7, 0x1845, 0x43d0, 0x4378, 0x3c9c, 0x33b3, 0x4156, 0xb0ec, 0xbaf1, 0x3ee5, 0xbada, 0xb2ff, 0x4571, 0x41dc, 0xbfc9, 0xb043, 0x0309, 0xa14d, 0x411d, 0x0eb5, 0x41da, 0x406f, 0x43a7, 0xba70, 0xba57, 0x1bbf, 0x42bb, 0x3d25, 0x1b41, 0xb0f2, 0xba0c, 0x4127, 0xbfcf, 0x4157, 0x1e08, 0xb2a9, 0x4615, 0x41bd, 0x4279, 0xab6d, 0xbaeb, 0x05c6, 0x41de, 0xb24b, 0x0bf1, 0x42ba, 0x42be, 0x392a, 0x4251, 0xba79, 0x0623, 0xbac2, 0x43c4, 0x4279, 0x4222, 0x1d6e, 0x43a2, 0x10dc, 0xb0d9, 0x18c8, 0xb25c, 0xbfa8, 0x402c, 0x1b45, 0x42bd, 0x4367, 0xb0c8, 0x4375, 0x4341, 0xb2d6, 0x42ee, 0xa002, 0x4279, 0xb21b, 0x4397, 0x4057, 0x40c4, 0x2547, 0xa55a, 0x41ed, 0xb06b, 0x43c9, 0x45d2, 0x41b8, 0x4338, 0xbfc9, 0xb249, 0x066a, 0xa8d4, 0xbfa0, 0x41e7, 0x013f, 0x40e1, 0xb27d, 0xba48, 0x19f8, 0xb0d6, 0xb02a, 0xb022, 0x41e3, 0x4363, 0xb200, 0xbf46, 0x431d, 0x0a44, 0x419c, 0x42ce, 0x2bd3, 0x42c1, 0xb0d5, 0x0d26, 0x44d8, 0x1cfa, 0x4054, 0xba30, 0xb29a, 0xb241, 0xbfd0, 0xb297, 0xb2b9, 0x43f8, 0xb23e, 0xb2ad, 0xba6f, 0xb23d, 0xb013, 0xba77, 0x3e95, 0xba62, 0xb0cc, 0xba22, 0xbf79, 0xba6e, 0x1ac2, 0x339f, 0xb041, 0x415b, 0xb289, 0x42b5, 0xb246, 0x0107, 0x406c, 0x4219, 0x41ae, 0x1dfa, 0xba38, 0xb217, 0xb217, 0x432f, 0x43fe, 0x1e95, 0xbfcc, 0x43df, 0x06a8, 0x4158, 0xad6e, 0x43d8, 0x1902, 0x43ba, 0x4289, 0xbf55, 0xba62, 0xbafa, 0x41dc, 0x3893, 0x41a6, 0x3bd9, 0x0066, 0x4433, 0xbae0, 0x4373, 0x4367, 0x3924, 0x4193, 0xb295, 0xb23e, 0x19cf, 0xb24b, 0x42a2, 0x3c87, 0xb015, 0xbf55, 0x1b93, 0x46bd, 0x423e, 0x43f0, 0xb0b4, 0x4036, 0xb2ef, 0x0fc6, 0xb225, 0x45c6, 0xa8dd, 0xb00a, 0x4091, 0xb265, 0x4211, 0xbacb, 0xbf68, 0xb21d, 0x1058, 0x4385, 0xaa47, 0x32ae, 0x46f0, 0xbf29, 0x1858, 0xab7c, 0xba78, 0x44d3, 0x464d, 0x42dd, 0x43d2, 0x17b7, 0xb0f1, 0x4138, 0x408d, 0xbf6f, 0x41b8, 0xb25c, 0x1b45, 0x40b6, 0x4084, 0x1a56, 0x1c02, 0xb2c7, 0x41df, 0xb0f9, 0x3625, 0x0a77, 0xbf80, 0x1997, 0x441f, 0x410a, 0xb209, 0x4323, 0x2e9c, 0xade2, 0xb211, 0x40aa, 0x4618, 0x4160, 0x18d1, 0x4398, 0xb25a, 0x409d, 0x4311, 0xbf05, 0x29a5, 0x438b, 0xb003, 0xa5db, 0x42b5, 0xbae1, 0x4011, 0x4415, 0x416c, 0xbfce, 0x08cd, 0xb29b, 0xb26c, 0x40c6, 0xbf8f, 0x41de, 0xb09d, 0x3a51, 0x2fba, 0x4341, 0xb27d, 0xb250, 0x438d, 0x43a3, 0x417a, 0xb2b8, 0xba3b, 0x40c8, 0x4349, 0x4323, 0xb2b1, 0x45c1, 0x41a5, 0xba36, 0x4138, 0x401f, 0xbfd0, 0xb20b, 0xb27d, 0x4267, 0x42e2, 0x45a8, 0xbf9e, 0xbadd, 0x41e8, 0x1c5e, 0x4133, 0xbaef, 0xbf6e, 0xa20d, 0xba19, 0x4194, 0x283b, 0x1d04, 0xb2bf, 0x42aa, 0x2109, 0x433c, 0x45ca, 0xa3e1, 0xbff0, 0xb0cc, 0x3304, 0x40af, 0xa1b6, 0x38e2, 0xba5f, 0x3c6d, 0xb2c9, 0x44f3, 0xb23c, 0x3f80, 0xbf60, 0x419c, 0x427c, 0x4207, 0xbfc5, 0x1e21, 0xa5e6, 0x40b7, 0x4020, 0xbf70, 0xa166, 0xbf2b, 0xb065, 0x10e9, 0x4355, 0xb262, 0x43ad, 0x40aa, 0x0291, 0x1958, 0x1c0b, 0x02ce, 0x4046, 0x3833, 0x1b88, 0xbfd0, 0xbf60, 0xbf66, 0xb09b, 0x1abf, 0x4381, 0x40fe, 0x4175, 0x3d96, 0x1bcb, 0x0a61, 0x43ee, 0x2678, 0xa7a5, 0x40d8, 0xb283, 0x4419, 0x40df, 0x1f45, 0x436f, 0x4085, 0x40c5, 0x413d, 0xb251, 0xbf15, 0x0cf4, 0x4205, 0xba59, 0xb239, 0x4394, 0x41ba, 0x4162, 0x17bb, 0xb2c0, 0x404c, 0x4155, 0x41e9, 0xbf2c, 0x42f5, 0xb254, 0x43a2, 0xbad4, 0xb266, 0xbaf9, 0x1a34, 0x2cef, 0xba72, 0x411d, 0xb0d2, 0x423e, 0x4437, 0x460a, 0xbfb2, 0x38b1, 0x28e2, 0xb285, 0xbfd0, 0x0d57, 0xb0a5, 0x439c, 0xb21f, 0x0547, 0x2a77, 0x43a2, 0x3c57, 0xb223, 0x1a5c, 0x12e5, 0xaa5a, 0xbf97, 0xba6b, 0x1f64, 0xba18, 0x431a, 0xb2fc, 0x4245, 0x438c, 0x4248, 0xbf60, 0x403c, 0x436e, 0x42c7, 0xb218, 0x4169, 0xbfc0, 0xba72, 0x4285, 0x4493, 0x4296, 0x4255, 0xb0e8, 0x45c9, 0x4083, 0xbfaf, 0xbf60, 0x4323, 0xbfd0, 0xb260, 0x45e4, 0x40e6, 0x43b3, 0xbf73, 0x440f, 0x1b4e, 0x430c, 0x43aa, 0xba3a, 0xb27b, 0xb251, 0x2bbe, 0xb2c2, 0xae3d, 0xb2d6, 0xba1b, 0x3599, 0x40ce, 0xb060, 0x40bc, 0xb237, 0x4087, 0x0927, 0x41a4, 0xb03b, 0x2497, 0x4485, 0x41c7, 0x426c, 0x4097, 0xbfaf, 0x41ed, 0xba17, 0x43fb, 0x40a4, 0xbf4e, 0xa776, 0x206d, 0x288a, 0x4300, 0x4111, 0x40cf, 0x4287, 0x404e, 0x426f, 0x2cfd, 0xb26a, 0xabd6, 0x43c3, 0xbfa9, 0x259b, 0x2856, 0x41ef, 0x196c, 0xb242, 0x45e4, 0x41ad, 0xa510, 0xb2ba, 0x16fe, 0x44e8, 0x42f3, 0x40a6, 0x1d3a, 0x2313, 0x4167, 0x44c5, 0x43e2, 0xa74d, 0x42a4, 0x257a, 0x1d63, 0xb202, 0x1ccf, 0x46b2, 0x2a5f, 0x4354, 0x39a8, 0xbf65, 0xb09e, 0x43e6, 0xa4e6, 0x1cb6, 0x1f46, 0x1ff3, 0x348a, 0xb220, 0xb2d8, 0xbfa0, 0xbf5d, 0x415d, 0xb265, 0x4299, 0x413f, 0xba62, 0x031b, 0x4473, 0x0ba1, 0x1acf, 0x41ce, 0x4041, 0x403b, 0xb276, 0xba7e, 0x0190, 0x0272, 0x408e, 0xbf13, 0xb2ea, 0x4216, 0x43a0, 0x4216, 0x4034, 0x401f, 0x43c0, 0x4147, 0xba0b, 0x42c1, 0xba30, 0xbaf4, 0x416a, 0x4178, 0xbad5, 0xbf90, 0xa8a6, 0x4192, 0xbfc3, 0x40c7, 0x4377, 0x2630, 0xb0eb, 0x45a1, 0x43dd, 0x1a55, 0xb229, 0xb2dc, 0x406a, 0x40f7, 0xb075, 0x43d3, 0xb2aa, 0x0a60, 0x07d8, 0x42ab, 0x4266, 0xbf69, 0x436f, 0xbfe0, 0x45d9, 0x1de6, 0x4003, 0x435b, 0xa29c, 0xb255, 0x2ad4, 0x41c4, 0x458d, 0xbf04, 0xba6b, 0x1aad, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xa3e9d705, 0xe9aa48da, 0xc172d1f9, 0x9b3d01b0, 0x01afea06, 0xeb3a9b8a, 0x12cc8d93, 0x01c88d27, 0x58e8c5c1, 0xdc241641, 0x7eb66cf7, 0x4ab2dd86, 0x10ad69fd, 0x3b68308b, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x00000000, 0xffffff9e, 0x00001a40, 0x00000000, 0x00000000, 0x00000040, 0x00000000, 0xffe68b7f, 0x008fee88, 0x58e8c62c, 0x00000000, 0x22b65886, 0x9565bb0c, 0x011fdd38, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x43cc, 0x407a, 0x415b, 0x42d2, 0xb20d, 0x41de, 0x1b41, 0x19b6, 0xb051, 0x1dcf, 0x36bc, 0x29fd, 0xbf00, 0x15eb, 0x415d, 0x2be9, 0x1bd3, 0xba76, 0x41a0, 0x45b0, 0xb26e, 0x0c41, 0x3dc0, 0xa863, 0x08ac, 0xbf87, 0x01a2, 0xba51, 0x430e, 0x1090, 0x425e, 0x428c, 0xba79, 0xb0b7, 0x1c66, 0x1c0e, 0x1bf3, 0x40df, 0x4663, 0x4346, 0x1b2f, 0x19fe, 0xbfdd, 0x0720, 0xa14b, 0x1a70, 0xba79, 0xba54, 0xba53, 0x42ef, 0x42c0, 0x409f, 0xbfd6, 0x4276, 0x41df, 0x2a4b, 0x43d6, 0x4094, 0x403d, 0x456a, 0x4027, 0x40b1, 0xac6c, 0x4201, 0x426d, 0x40f2, 0x3d25, 0xb26c, 0x2c76, 0x4193, 0xb0ae, 0x4048, 0x46f4, 0x0f9b, 0xbf00, 0x435b, 0xbfd0, 0x4166, 0x42da, 0xbf78, 0x42a6, 0x455c, 0x428d, 0xbafc, 0x140b, 0xbf8d, 0xb201, 0x4348, 0x4198, 0x1ad2, 0x41fb, 0xba39, 0xb295, 0xbf07, 0x43cb, 0x1d7c, 0xb285, 0x4223, 0x41a0, 0x2f9b, 0xbfa0, 0xb0ab, 0xb00d, 0x411f, 0x463a, 0x43d7, 0xb240, 0xbac6, 0x4067, 0xaa78, 0xb25f, 0x41f2, 0x438d, 0x42ed, 0x4217, 0xbf36, 0x4110, 0x4127, 0xb2dd, 0x4033, 0xae52, 0x4633, 0x401e, 0x1060, 0xb026, 0x3426, 0x4328, 0x4223, 0x3866, 0x4665, 0x1c0d, 0x05a2, 0xbf5c, 0xb2e6, 0xa188, 0xac49, 0x424b, 0x261f, 0x41c5, 0x41e9, 0xad0a, 0x2738, 0xba47, 0xa121, 0xb095, 0x45ee, 0x18ca, 0xbf80, 0x030f, 0x4279, 0x46e2, 0x418d, 0x44c5, 0xb250, 0x4248, 0xb026, 0xbfa8, 0x41d3, 0xa696, 0x426f, 0x45da, 0xb294, 0x40e2, 0x1ccd, 0xa595, 0x420e, 0xaf3b, 0x10ee, 0xb0ae, 0x3c78, 0x413a, 0xb0b4, 0x23f4, 0xbf5d, 0x18be, 0xb077, 0x4070, 0x022e, 0x41be, 0xb2a9, 0x42c3, 0xbf1f, 0x4194, 0x037b, 0xbfd0, 0x42ef, 0x025e, 0xb21c, 0xbf91, 0x4016, 0x43e7, 0x4280, 0x409e, 0xb25b, 0xb023, 0xb224, 0xaa0f, 0xba4b, 0x1b56, 0xba31, 0xa177, 0x4118, 0x08be, 0x433b, 0xb023, 0x40cb, 0x2d55, 0xb0a3, 0x40ef, 0x44d9, 0x420f, 0xb0a7, 0x39b0, 0x412b, 0xbfa8, 0x1e44, 0x4602, 0xbfa3, 0xba1e, 0xb2c6, 0x429a, 0xb205, 0x047a, 0xb0fe, 0x4373, 0xbad8, 0x434c, 0x30e0, 0x1ee5, 0xbfb0, 0x410e, 0xb21e, 0xa97d, 0x42b1, 0x4671, 0x433f, 0xbf0d, 0x0ee6, 0x42ac, 0x19e3, 0x41a3, 0xbfa0, 0xba44, 0xa51c, 0xb241, 0xb2af, 0x415a, 0x0a3c, 0xbf1c, 0x28a0, 0x43d5, 0x4200, 0x43f3, 0x4240, 0x419c, 0x3d0b, 0xb09a, 0xa807, 0x42fa, 0x41e0, 0xbad5, 0x3438, 0xba09, 0xb0f9, 0x4628, 0x4167, 0x1b7b, 0xba0a, 0xa17a, 0xb276, 0x40b8, 0x121a, 0x133c, 0xbf34, 0x3613, 0xb259, 0x4105, 0x41de, 0x4155, 0xa2c3, 0x189d, 0xbf65, 0xb017, 0xa019, 0xb082, 0x40c1, 0x4319, 0x1806, 0x1131, 0xbf87, 0x402b, 0x2960, 0x1969, 0x4232, 0x465a, 0xa10c, 0x464a, 0x37eb, 0x45a3, 0x401d, 0x1f11, 0xb0f1, 0x4210, 0x439a, 0x4048, 0x44a1, 0x28fb, 0x44f0, 0xb0b6, 0x41bf, 0x199b, 0xb268, 0x421a, 0x0b58, 0x40ad, 0x18b6, 0xbf09, 0x402a, 0x137c, 0x4046, 0x175f, 0xb0cf, 0x419b, 0xbf52, 0x419d, 0x4213, 0x04a6, 0xba01, 0xbff0, 0x01d4, 0x1932, 0x42d7, 0x4123, 0x4160, 0x0e55, 0x40c5, 0x195a, 0x43bc, 0x413d, 0x1d89, 0x40e8, 0xb21b, 0xb2a3, 0x43e1, 0x417e, 0x423c, 0x24d1, 0xbf6d, 0xa57c, 0x2ff1, 0x05fa, 0x4200, 0xb26f, 0x05f6, 0x064f, 0xbfcd, 0xb2dd, 0x40ed, 0x360f, 0xbafa, 0xb0e6, 0x2f81, 0x09cc, 0x4072, 0x408c, 0x0899, 0xb26c, 0x1ee4, 0x465f, 0x4175, 0xa5b6, 0xba63, 0x1ffc, 0x4094, 0x1a4f, 0xba23, 0xbf2b, 0x25dc, 0xba1f, 0x413b, 0x4105, 0x4180, 0x4186, 0xb2f9, 0x4194, 0xbfae, 0x1da7, 0x1afc, 0x4330, 0x3083, 0x43e5, 0x1f44, 0xba0a, 0x4469, 0x4062, 0xbaeb, 0x16ab, 0x363c, 0x1bdd, 0x40b0, 0xbfa8, 0x4346, 0x4129, 0xb080, 0x4450, 0x40d1, 0x12e2, 0x40a6, 0xae28, 0xbf74, 0xb08f, 0xb280, 0x0fb5, 0xb287, 0xb28e, 0x4238, 0x296f, 0x4398, 0x43b3, 0xb0b7, 0xba78, 0xbf73, 0x41be, 0x40ec, 0x1602, 0xb227, 0xb2c4, 0xbf28, 0x4298, 0x2f81, 0xbadc, 0x1dd0, 0xb255, 0x2521, 0xbac6, 0x03e6, 0xa5ac, 0xba11, 0x401e, 0x4172, 0x1912, 0x43a1, 0xbfa0, 0x41da, 0xb283, 0x2be3, 0xbaff, 0x24bf, 0x415a, 0x1da5, 0x4212, 0x436f, 0x41b8, 0xb29e, 0xbf28, 0xb2e8, 0x1ff8, 0xabd8, 0xb064, 0xba4e, 0x4136, 0x425c, 0x41b5, 0xba40, 0x19bb, 0x26a6, 0xbf26, 0xbaf6, 0xba55, 0x4169, 0x40c2, 0xb2c0, 0x0da2, 0x41fc, 0x3d98, 0x43d1, 0x19af, 0x4023, 0x0a9d, 0xb003, 0xb046, 0x1866, 0x40b2, 0x41af, 0xbafa, 0x1bd8, 0xbfa8, 0x4450, 0x427a, 0x4190, 0x1aa2, 0xa784, 0x1069, 0xb095, 0x181a, 0x4450, 0x41b6, 0x1bf7, 0x1d12, 0x347a, 0x41a3, 0xb263, 0x4215, 0x431c, 0x433c, 0xbf61, 0xb200, 0xb2db, 0x1c1d, 0xb2e5, 0xbf60, 0x12e2, 0xa6a8, 0x45aa, 0xb2bc, 0x41ca, 0xb2d7, 0xab7e, 0x0d22, 0xbf9e, 0x40b8, 0x2c08, 0x1db6, 0x3d47, 0x42ee, 0x4081, 0x412b, 0xa807, 0x350a, 0x43c6, 0xaea7, 0xbf5d, 0xa806, 0xb2cc, 0x4143, 0x30fb, 0x0043, 0x46b9, 0x1c7f, 0x1e13, 0x0da5, 0xb214, 0x3da5, 0x437f, 0x3532, 0x458a, 0x4131, 0x424b, 0x144f, 0x433a, 0x3f9b, 0x0dfc, 0x1faf, 0xb253, 0x4137, 0xbfd0, 0xbf49, 0x4107, 0x24b5, 0x4324, 0x40fb, 0x30c9, 0x1ea2, 0x324f, 0x1a34, 0x0dc7, 0x40e7, 0x1d48, 0x41dd, 0xae7c, 0x43a3, 0x299f, 0xbf01, 0xb077, 0x3770, 0x4140, 0xbfe0, 0x4265, 0x4019, 0x40ed, 0x0afe, 0x1b29, 0x1afc, 0xb23c, 0x04db, 0xb214, 0x439d, 0x2938, 0x0092, 0x40b0, 0x43c0, 0x43d5, 0xb0c1, 0xa37e, 0x1e61, 0xb021, 0x4022, 0xbf6f, 0x4114, 0x407d, 0x428c, 0x415e, 0x4256, 0xb0ea, 0x41e6, 0xbaf7, 0x4060, 0x1ee1, 0x1100, 0x42d5, 0xb25f, 0xb063, 0xb253, 0x1945, 0x30ee, 0x4210, 0x3373, 0x42a7, 0xbf72, 0x42d1, 0xb038, 0xbae9, 0x43ea, 0x40c7, 0xbfa0, 0x299d, 0xb294, 0x404e, 0x085d, 0x4341, 0x4172, 0x1c2a, 0x4183, 0xbf2b, 0xb2b7, 0x40ab, 0xb279, 0x43a9, 0x1cc5, 0x4208, 0x402f, 0x43b8, 0x43ee, 0x41e4, 0x3928, 0xba55, 0x4235, 0x2c5d, 0xbf90, 0x4278, 0xab33, 0x4338, 0x1631, 0x43b3, 0xbfc0, 0x058f, 0xba1c, 0x424d, 0x40e6, 0x3bc1, 0x43a9, 0xba00, 0xbf3e, 0x41fc, 0xa8a3, 0xba2b, 0x1e86, 0x23a6, 0x41f5, 0x40e4, 0x40c8, 0x430d, 0x46b2, 0xba5d, 0x415b, 0x32b1, 0x43ec, 0x2faf, 0xbfda, 0x43ac, 0xa3e2, 0xb283, 0x1c45, 0x4341, 0x4133, 0x40b4, 0x36da, 0x1c1e, 0x4395, 0xba7e, 0x4070, 0x1aeb, 0x467a, 0xb29e, 0x345f, 0x4364, 0xbf00, 0x412e, 0xb2e8, 0x1b7e, 0x2b8a, 0x42c7, 0x412b, 0x4097, 0xb2bf, 0xbf0c, 0x1914, 0x443e, 0x3c97, 0x310b, 0x4091, 0x4367, 0x409f, 0xba38, 0xbf33, 0x403d, 0x4024, 0x1d51, 0x43ed, 0xba4c, 0x4057, 0x46fc, 0x4356, 0x4095, 0x4119, 0x4019, 0xb232, 0xba07, 0x22e0, 0x4082, 0xb038, 0x19e5, 0x1171, 0xbfc3, 0x1cf0, 0x1e78, 0xb2bd, 0x250a, 0x4128, 0x42bf, 0x43c4, 0xb0cf, 0xb269, 0xba09, 0x42dd, 0x1ad6, 0x41c3, 0x2cab, 0x411d, 0xbf95, 0xaf53, 0x45b5, 0x4255, 0x41d7, 0x43c1, 0x4447, 0xb2c7, 0xb284, 0x1c1e, 0xba76, 0xaada, 0x1344, 0x40ac, 0x426f, 0xaa92, 0x41d1, 0x261f, 0x189e, 0x1904, 0x41fe, 0xb283, 0xa695, 0xbfa3, 0xb0f1, 0x41dd, 0xb2c2, 0xb2fd, 0x431b, 0x438d, 0xb2a1, 0x4240, 0x42c9, 0xa2b2, 0x345e, 0x435e, 0xbf1f, 0xba7f, 0x41d4, 0x1e3f, 0x0eee, 0x1d12, 0x42d2, 0xbfb0, 0x41b0, 0xa79a, 0xbf80, 0x32a0, 0xb20d, 0x3e30, 0xb2ad, 0xb273, 0x41a4, 0xb2c3, 0x404c, 0xba3e, 0x4377, 0x4197, 0xbfd9, 0xba3a, 0xb23e, 0x41c3, 0xb042, 0xb2ee, 0xb24c, 0x43ce, 0xbade, 0xa3fb, 0x04f6, 0xb293, 0xa149, 0xb25e, 0x4319, 0x19e5, 0xbf1c, 0x1878, 0x1bb1, 0x4061, 0x4657, 0x414d, 0x1f05, 0xba55, 0x1a6a, 0x4065, 0x0674, 0xab5f, 0xa70a, 0xbf87, 0x4095, 0xb089, 0x4121, 0x4300, 0x4012, 0xbfbf, 0x40a1, 0x436c, 0x4032, 0xbad1, 0xa20b, 0x1d95, 0xa4be, 0x1deb, 0x419f, 0x4324, 0x4418, 0x4363, 0x42ce, 0x403c, 0xbfb3, 0xb07a, 0x4179, 0xbaf5, 0x1daa, 0xb22a, 0x4671, 0xba28, 0x188a, 0x41cd, 0xb298, 0x19e9, 0x428b, 0x1e5c, 0x413a, 0x3a1f, 0x41d5, 0xa080, 0x43d2, 0x28e1, 0x436e, 0x40ce, 0xb27c, 0x26cf, 0x407e, 0x456a, 0x43c7, 0xb253, 0x4667, 0xbf34, 0x411e, 0x43f8, 0x4152, 0xbaff, 0x42ce, 0x4147, 0x128c, 0x4095, 0x1a0a, 0x4392, 0x1c9b, 0x41ed, 0x2a7a, 0x01f2, 0xbf00, 0x004d, 0xb2b3, 0x1d3b, 0xb0e9, 0xb2f3, 0xbf93, 0x10bb, 0x41bf, 0x1f9e, 0x1d2d, 0xb2d4, 0x08c2, 0x4190, 0xbadc, 0xba46, 0x42cc, 0x446d, 0x424e, 0x4146, 0x40ef, 0xa359, 0x1a54, 0xb066, 0xba55, 0xbacf, 0x41f3, 0x4689, 0xbf0f, 0x1492, 0xbae7, 0xb2b4, 0xb2a0, 0x413f, 0x00d2, 0x406e, 0x40dd, 0xb2c9, 0x0345, 0xb20e, 0x432d, 0x4227, 0x34a0, 0x429c, 0x416b, 0xb08d, 0xbf4f, 0x19d9, 0x42bd, 0xa315, 0x442a, 0x1a15, 0x445e, 0xaea8, 0x0a9d, 0x35e8, 0xb29f, 0x1a1e, 0x3022, 0xb050, 0xa914, 0x0e5a, 0x4267, 0x4445, 0x4367, 0x1ff7, 0x40f8, 0x23dd, 0x4283, 0x454d, 0x415e, 0x4199, 0xbfe4, 0xbfc0, 0x4185, 0x4564, 0xb059, 0x409f, 0xbf55, 0x446b, 0x2c78, 0x43b4, 0x435a, 0xa547, 0x411d, 0x2c11, 0xb0a8, 0x0e79, 0xb23c, 0x4111, 0x42e8, 0x1a3e, 0x1e15, 0xbf06, 0x0cce, 0x1e92, 0x0485, 0xba74, 0x0a52, 0xa544, 0x4120, 0xb087, 0x41e8, 0xba64, 0xbf25, 0x3059, 0xb2f3, 0x4301, 0xb2e3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5b8d957d, 0xda740f74, 0xf45748f5, 0x13975c8e, 0xd433eb4e, 0xa749743f, 0x6c798277, 0xd9c1885d, 0xc3623606, 0xea7f747e, 0x8bf0542c, 0xfe1fe0b1, 0xd0d3fbf2, 0xed311e85, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0x007fffff, 0xb0934dd8, 0x00000000, 0x000018e4, 0x00000000, 0x00000000, 0xc3623606, 0xffff8cd5, 0xb0934e5d, 0xfe1fe0b1, 0x000015a6, 0xb0934c3f, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x2722, 0x4544, 0x4378, 0x437b, 0x33fe, 0x41f0, 0x1814, 0x1583, 0x0945, 0xa03e, 0xb24c, 0x1ba2, 0x1e8e, 0x4291, 0xb2d6, 0x1b2d, 0x1637, 0xbfca, 0x3736, 0xb2a1, 0x41d7, 0x42e1, 0xb2b3, 0x4007, 0xb073, 0x1d95, 0xb219, 0x3d08, 0x415a, 0x4282, 0x4245, 0x43c9, 0x1a86, 0xb2bf, 0x463e, 0x40af, 0x1cd7, 0x43b1, 0x4108, 0x0da6, 0x430f, 0x418b, 0xbf2b, 0x4608, 0x441c, 0x41b7, 0xb254, 0xba40, 0x42b7, 0x1de0, 0xbf70, 0x1d62, 0xba76, 0xb045, 0x4138, 0x4067, 0x0654, 0x1e1a, 0xb015, 0x447a, 0x430b, 0x4154, 0x2823, 0x4685, 0xbf75, 0x4080, 0x426a, 0x43a2, 0x40eb, 0x437c, 0x4048, 0x4285, 0xba0d, 0x1eea, 0x4364, 0xbf29, 0x41b9, 0x4351, 0x4204, 0x1c9d, 0xa86b, 0x1174, 0x4064, 0x00d4, 0x4021, 0xb047, 0xba13, 0x4113, 0xb2a0, 0x12fd, 0x4171, 0x4178, 0x410e, 0xba24, 0x432b, 0xb2b0, 0x3ef8, 0xb2ea, 0x410e, 0x1db0, 0x18de, 0x1ffa, 0x1bc9, 0xbf27, 0x43af, 0x4021, 0xb2f6, 0x4307, 0x400e, 0xba4a, 0x44f4, 0x43e4, 0x22a9, 0xba03, 0x1c54, 0x4356, 0x1c4c, 0xbf8a, 0x426e, 0x4136, 0xac9b, 0xb011, 0xba49, 0x4458, 0x1230, 0x4551, 0x4003, 0xbf41, 0x1ff7, 0xb2a2, 0x19dc, 0xabbe, 0xbf28, 0xa7a7, 0x407c, 0x4019, 0xa9c5, 0x4479, 0xb07b, 0x00e2, 0xbf16, 0xb2a7, 0x323b, 0xbfe0, 0x42fd, 0xb2b7, 0x40fd, 0x4140, 0x421e, 0x41dc, 0x179a, 0x2e91, 0xb25a, 0xb0af, 0x4384, 0x4157, 0x45ad, 0x280d, 0xbfa2, 0x467b, 0xba07, 0x4262, 0x36bd, 0xb0d2, 0x289b, 0x4211, 0x4152, 0x41ef, 0x426e, 0xb2f2, 0x4356, 0x4614, 0xbaf0, 0x4229, 0x314a, 0x4182, 0xbf1e, 0x1fd7, 0xba5d, 0xad77, 0x43aa, 0xbae9, 0xbfd0, 0x0a05, 0xa8b2, 0x419a, 0xb23c, 0x4367, 0x4090, 0xbf70, 0xbf1c, 0xbf00, 0xb0f9, 0x4274, 0x4299, 0xb210, 0xb215, 0x1e80, 0x08e8, 0x43ee, 0x412f, 0x379d, 0x402e, 0x1919, 0xaff5, 0x4106, 0xbae6, 0x42e4, 0x45aa, 0x40e4, 0x1b18, 0x43b9, 0xbf1d, 0xbaca, 0x3c0c, 0xb090, 0x463c, 0x1fe5, 0xb21c, 0x4129, 0x3dd7, 0xb24f, 0xaeb3, 0x4651, 0xb0f0, 0x0629, 0xba16, 0x42be, 0x4147, 0x1d46, 0x2698, 0x436f, 0xb080, 0xbff0, 0x4337, 0x3d59, 0x465e, 0x2c6d, 0x4262, 0xb088, 0x417d, 0x41b8, 0xbf12, 0xbac4, 0x40c5, 0x1406, 0x3e5f, 0x3f1c, 0x04b0, 0xa681, 0x469a, 0x433a, 0x443c, 0x26c9, 0x41eb, 0x4198, 0xbfd3, 0xb2d5, 0x4380, 0xb24d, 0xba72, 0x4366, 0x42af, 0x1ae5, 0x43ff, 0x43ed, 0x412e, 0x1d52, 0x405a, 0x41ed, 0x45cd, 0x41ae, 0xb2b8, 0xbfda, 0x2331, 0x4430, 0x0f27, 0x4036, 0x1f45, 0x40b2, 0xb0a7, 0x4230, 0x0a6e, 0x44f8, 0x460d, 0xb29f, 0xa318, 0x430b, 0x3556, 0xae3e, 0xb034, 0xbf57, 0xb2d5, 0x3b8c, 0x44ea, 0xb05a, 0x42f4, 0xa5f5, 0x2209, 0x416c, 0x4466, 0x422b, 0x4263, 0x43d2, 0x1468, 0xb265, 0xbfcb, 0xbad4, 0xb28f, 0xbadb, 0xa163, 0x443b, 0x3b15, 0x197c, 0x261a, 0x43a1, 0x4028, 0xb24b, 0x4182, 0x1e7a, 0xb2d8, 0x4108, 0xba42, 0xbfe0, 0x42ba, 0x4212, 0x1849, 0x1a51, 0x1a9f, 0x15e7, 0xba28, 0xb27f, 0xbf23, 0x41c9, 0x3442, 0xad53, 0x406f, 0x0712, 0x1cd0, 0x467c, 0x1da8, 0x42fe, 0x43e1, 0xba5f, 0xb2fa, 0x41be, 0x3f24, 0xbfb0, 0x426b, 0xbf05, 0x2ee5, 0xbade, 0x43f9, 0x43ed, 0x4195, 0x4176, 0x4294, 0x4286, 0x00e1, 0xacdb, 0x1e04, 0x1f46, 0xbfc4, 0xa533, 0x4165, 0x41a6, 0x4344, 0xbf97, 0xba71, 0xa713, 0xbafc, 0x40a1, 0x00da, 0xb0c8, 0xba61, 0x4161, 0x1da3, 0x4053, 0x19d0, 0x40dc, 0x4036, 0x4075, 0x40a6, 0xab72, 0x08d1, 0xb21b, 0xaddc, 0x416e, 0x1c32, 0x41b8, 0x0b12, 0x1434, 0xb042, 0x408a, 0xb24d, 0x1743, 0xb287, 0xbf37, 0x40cd, 0x417f, 0x34ae, 0x427e, 0x42d5, 0x4039, 0x360f, 0x413f, 0x3864, 0x27c9, 0x40f8, 0x4318, 0x422e, 0x3c63, 0x1bd9, 0x424c, 0x1384, 0x1c1e, 0xa82e, 0xbfc8, 0xa049, 0x079e, 0xa832, 0x46e8, 0xba13, 0xbfc0, 0x1af3, 0xb2a3, 0xb074, 0x19b9, 0x2c75, 0xbacf, 0x3dca, 0x0f0b, 0x2979, 0x4007, 0x43be, 0xba62, 0xbadc, 0xbf87, 0x125a, 0x435f, 0x1c1a, 0x13ab, 0x42b7, 0xbf70, 0x42cc, 0x1978, 0x41b2, 0xba50, 0x42be, 0x4346, 0x4336, 0xb2a5, 0x45bd, 0xa15a, 0x2038, 0xba4c, 0x1a84, 0x430b, 0xb2b4, 0xbfae, 0x41c1, 0x42d3, 0x1ef4, 0xb01a, 0x1c63, 0x210b, 0xb272, 0xa49d, 0x1be2, 0x18b4, 0x1c08, 0xa4d3, 0x1f6b, 0x113c, 0x4663, 0x41c6, 0x4247, 0x402f, 0x4379, 0x0949, 0x4008, 0x42c6, 0x41fc, 0x0b89, 0xbf7c, 0x4370, 0x422d, 0x4362, 0x0b78, 0x040c, 0x43da, 0x462d, 0xb251, 0x1e56, 0x4123, 0xaaef, 0x43a6, 0x0408, 0x2a06, 0xb276, 0xb091, 0x42a4, 0x4319, 0x3ce5, 0xb297, 0x40df, 0xba2d, 0x3233, 0x29dc, 0x407f, 0xbf4a, 0x4685, 0xba15, 0x1404, 0xba06, 0x42a9, 0x262a, 0x3e76, 0x0ba0, 0xb2e8, 0x4482, 0x4047, 0xb037, 0xb06b, 0x05a4, 0x40ad, 0x435d, 0x4057, 0x4280, 0x4049, 0x42b3, 0xbf6d, 0xba7a, 0x42f6, 0x0984, 0x2d9f, 0x44d5, 0x4374, 0x4047, 0x1a23, 0x435b, 0x1f11, 0xb213, 0xba1a, 0x4030, 0x43ca, 0x0f9c, 0x4682, 0xaed1, 0x45be, 0x42eb, 0x41ef, 0xa19a, 0xa571, 0x43e4, 0xbf4c, 0x4255, 0x43bc, 0x40d9, 0x0a28, 0x2786, 0xbf33, 0x42c9, 0x1b40, 0xb2c6, 0xb0a8, 0xafac, 0x40a8, 0x1c68, 0x4184, 0x1420, 0xb2d5, 0xbf34, 0x433a, 0xb24b, 0x45e4, 0x0183, 0xb22d, 0xb091, 0xb2cf, 0x4038, 0x41a3, 0xbf60, 0x40ef, 0x4360, 0x45c4, 0xb080, 0x339d, 0xba16, 0x4115, 0xbf96, 0x406e, 0xaf42, 0xb28f, 0x413d, 0xbfbb, 0x4146, 0xb038, 0x4285, 0x216e, 0xb264, 0x431e, 0x42b9, 0x2f56, 0x0e0c, 0x4217, 0xa12d, 0xac5c, 0x4328, 0x40a2, 0x1a29, 0xb0e9, 0x442f, 0x41bc, 0x37c6, 0xb0cc, 0x18ce, 0xb042, 0x1b4e, 0xbf90, 0xbf9d, 0x419f, 0x414f, 0x1eb1, 0x1f9e, 0x4294, 0xa197, 0x1896, 0xbaf5, 0x4272, 0x45c8, 0x0da9, 0x424c, 0x2c62, 0x4022, 0x1bfa, 0x4091, 0x0417, 0xb099, 0xba6b, 0x40c9, 0x1d35, 0x43c1, 0xb0e2, 0x443b, 0x4137, 0x0c33, 0x418c, 0xbf7e, 0x4196, 0xba14, 0x4585, 0xbfca, 0x41f0, 0x3bb0, 0xb044, 0xb2e7, 0x2b49, 0x4428, 0xbfca, 0xb092, 0x4051, 0x4201, 0x1a8d, 0xbf90, 0xba5c, 0xb0bf, 0x4151, 0x19c8, 0xb293, 0x437a, 0xb2a9, 0xbace, 0xbf71, 0x40da, 0xb24e, 0x0bb8, 0x1879, 0xba3b, 0x402b, 0x1e0d, 0xbad2, 0xb204, 0xba07, 0x3165, 0xb08b, 0x3c16, 0x1e6d, 0x40f9, 0x1379, 0x17f9, 0xaf70, 0x4287, 0xbf2b, 0xbaf2, 0xba11, 0x41e7, 0x0360, 0xb237, 0x43d9, 0x1db8, 0xba48, 0xba5d, 0xbac7, 0xbfdd, 0x0be8, 0x1c5b, 0x20b0, 0x4099, 0x3a34, 0x4254, 0xb236, 0x419a, 0x1b31, 0xbf2d, 0x42a2, 0x4271, 0xbad5, 0xb254, 0x1bb2, 0x43b2, 0xb2b2, 0xbae9, 0xb22b, 0xa066, 0xba09, 0x4028, 0x400a, 0xb212, 0x42a3, 0x4047, 0xbf4a, 0x2bc0, 0xa346, 0x40a3, 0x42ee, 0x138f, 0x1b40, 0xba12, 0x1b0e, 0xa916, 0xb241, 0x400b, 0xbfdb, 0xba28, 0x41a1, 0x4390, 0x3d2b, 0x443e, 0x4673, 0x1d4d, 0x4120, 0x4269, 0x419d, 0x46f2, 0x4682, 0x41c5, 0xb2d2, 0xb234, 0x4036, 0xbf7e, 0xba39, 0xb2e8, 0x466f, 0x441e, 0x1b36, 0x3f5d, 0xba69, 0x20de, 0x1fdf, 0x18d0, 0x095f, 0x416b, 0x437d, 0x4331, 0x40de, 0x0e6a, 0x353b, 0x13c7, 0x4221, 0x437c, 0x1f97, 0x3a6d, 0x435d, 0x42cc, 0x43c4, 0x310b, 0x428c, 0x2383, 0xbfc7, 0xb0c1, 0x1fd1, 0xa1ce, 0x41d6, 0x45a6, 0xb275, 0x4257, 0xb2b3, 0x43ab, 0x4278, 0x1ecd, 0x359e, 0xb2e9, 0xba0c, 0xbad3, 0x1dc2, 0x419e, 0x424a, 0x41aa, 0xb2c1, 0x44e5, 0x40f0, 0x1778, 0x4088, 0xbfd6, 0x4246, 0x4197, 0x1c5c, 0x1b2e, 0xb28b, 0x0f3d, 0x1e81, 0x4122, 0x2e73, 0x1db5, 0x42fa, 0x1d43, 0x43f2, 0xba18, 0x0068, 0x42ff, 0x0dd0, 0x4247, 0x45ce, 0xb2ba, 0xa73d, 0x428f, 0x4329, 0xb225, 0xbf62, 0xba24, 0x43a0, 0x204c, 0x1466, 0xb2dc, 0x3f46, 0x42a7, 0x43a4, 0xba61, 0xb0f1, 0x40bb, 0x40c9, 0x4192, 0x4113, 0x1d47, 0x448d, 0xaa2a, 0xbaf6, 0xb2db, 0x4294, 0x4206, 0x0429, 0xb2b2, 0x41ee, 0x4081, 0x44b3, 0xbfd1, 0xbadd, 0x424e, 0x2a0e, 0x334e, 0x41f0, 0x1b77, 0xbae8, 0x4067, 0x42eb, 0x00b1, 0x423e, 0x4073, 0x4106, 0x40a9, 0x416b, 0x128f, 0x0281, 0xba3c, 0xbac5, 0x0c2f, 0xb012, 0x1c56, 0x40a6, 0x4096, 0x40e4, 0x42b5, 0xb090, 0x42ea, 0xbf49, 0x4287, 0x4051, 0x43d7, 0xba1d, 0x41dc, 0x4161, 0x2296, 0x4251, 0x4166, 0x43ad, 0xba47, 0x1b9f, 0xb026, 0xb2d0, 0xb2ec, 0x420d, 0x4355, 0x434c, 0xb08f, 0xb252, 0x1b04, 0xa364, 0xbafc, 0x19a4, 0x3c98, 0xbf9f, 0x41c0, 0x34d7, 0x456e, 0x42d5, 0xb264, 0x43df, 0x4542, 0xba15, 0x40de, 0x42d1, 0x1a20, 0xbf2f, 0x4103, 0xacdc, 0x40cc, 0xb099, 0x43eb, 0x43df, 0x317c, 0xba70, 0x1e2c, 0xbf84, 0x4158, 0x1c12, 0x40cb, 0x46d1, 0x408a, 0x037c, 0xb071, 0x4347, 0x4320, 0x25fa, 0x4078, 0xb25d, 0x1e50, 0xa645, 0x2831, 0xbf51, 0x4092, 0x19b4, 0xa3f1, 0xbada, 0x4036, 0x42a7, 0x09ac, 0x1f55, 0xb247, 0x2753, 0x426b, 0x40a5, 0x4223, 0x40f3, 0xbfae, 0x292e, 0x1f9b, 0x46cc, 0x181d, 0x40ca, 0x413f, 0x0b7e, 0x423a, 0xb264, 0x43b4, 0x214a, 0xad2e, 0x215a, 0xba7f, 0x409b, 0x4568, 0xaf04, 0x2c49, 0xa536, 0x425e, 0x42fe, 0xa0ba, 0x4107, 0xbf77, 0xb019, 0x1640, 0x4336, 0x2631, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x7f732ac7, 0xd652158c, 0xc810dc10, 0x451cfb90, 0x698e290f, 0x67d2fe9c, 0xfa98fc70, 0xc792f08a, 0x938d63b5, 0xf0a64fe6, 0xd6444518, 0x197a89a8, 0x8b76fcf7, 0x660046dd, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x00001ab4, 0x0000005a, 0x00000000, 0x00000000, 0x00000000, 0x0000189c, 0x00000031, 0xffffffff, 0x000000ac, 0x00000000, 0x00000000, 0x197a89a7, 0x00000000, 0x8b76fcc7, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4143, 0x4476, 0x3a03, 0x419e, 0x43b2, 0x0275, 0x1d6f, 0x4247, 0x28b0, 0x412b, 0x405c, 0x4317, 0xb2f8, 0xb0d0, 0x4669, 0xbf17, 0xb2cf, 0x2c9b, 0x0e98, 0xba5c, 0x4230, 0x2d3b, 0x28af, 0x46bd, 0x423d, 0x429e, 0xbae4, 0x4683, 0xba7e, 0x4359, 0xb0cb, 0xbfcd, 0x0cd3, 0xa0ac, 0x41a5, 0x1bcb, 0xba6f, 0x44f1, 0x4058, 0x0d71, 0x46cb, 0xbfb0, 0x4310, 0x0701, 0x4026, 0x4320, 0xb2fe, 0x4317, 0xb28d, 0xb269, 0xb2d7, 0x43f5, 0xbaed, 0x40d1, 0x42cd, 0xbfdc, 0x075b, 0x4183, 0x12da, 0x4646, 0xb258, 0x2fa1, 0xba16, 0xba7f, 0xb2f9, 0xa1b7, 0x4198, 0x3436, 0x428a, 0x41da, 0x0f61, 0x43c0, 0xb2a7, 0xba6e, 0xb210, 0x1820, 0x1923, 0xb278, 0x4136, 0x2035, 0x429e, 0xb2c2, 0xbf77, 0xb260, 0x4616, 0x1d26, 0x4176, 0x42fc, 0x43c9, 0xa2cf, 0x454d, 0x4342, 0x2e4c, 0xbf16, 0xb09b, 0xbaea, 0x438f, 0xbf65, 0xb0a5, 0xa6ba, 0x1f3d, 0x415f, 0xb296, 0x4113, 0x03e8, 0x2fa2, 0x19e9, 0x2348, 0x44e3, 0xb0d0, 0x43b4, 0x28e6, 0x1b96, 0x41d4, 0x0f21, 0x439d, 0xb07b, 0xbf74, 0x3b03, 0xba3f, 0x1942, 0x459e, 0x194f, 0xb0a7, 0x174e, 0xbf92, 0xb00d, 0x3171, 0x1f1d, 0x436c, 0xbf86, 0x15ba, 0x3e2f, 0x1711, 0x4351, 0x2b18, 0x418b, 0x0a50, 0x40a0, 0xba20, 0x4154, 0x4174, 0x1d34, 0x3da5, 0x41f4, 0x403a, 0x4201, 0xba51, 0x33d0, 0x1823, 0xb0b7, 0x4396, 0x40c1, 0xbf96, 0xb212, 0x23d4, 0xa987, 0x4344, 0x3df9, 0x32bf, 0x4082, 0xb297, 0x4174, 0xac14, 0x4297, 0xb2e2, 0xb2e8, 0xbfaa, 0x429e, 0xb0b8, 0x465f, 0xba46, 0x433d, 0xa49c, 0x432d, 0x3e9f, 0x1e23, 0xb2a2, 0xa5aa, 0xb0cf, 0xba2f, 0x41f9, 0xbfce, 0x41a6, 0x41af, 0x29a7, 0xbf59, 0xb004, 0x4003, 0xbf00, 0x3449, 0x40b1, 0x116e, 0xb027, 0xb25c, 0xabd8, 0x1bad, 0x0806, 0x0827, 0x46e1, 0x41b0, 0x419d, 0x1e88, 0xb213, 0xbad2, 0x341a, 0xafb2, 0x4416, 0x429e, 0x2857, 0x40a7, 0xba0c, 0x4205, 0x0eeb, 0x43b9, 0xbf2d, 0x41e5, 0x1a16, 0x45a0, 0x423c, 0xb00d, 0x1f94, 0xbae0, 0xbf1b, 0x42d0, 0xb071, 0x4345, 0x4340, 0x41ab, 0xb067, 0x3432, 0xbf1d, 0xb2ef, 0xb061, 0xba7f, 0x3047, 0xa46e, 0x429e, 0xbaf6, 0x41b3, 0x45ec, 0xb20a, 0xb2d5, 0x4360, 0x2af9, 0xb061, 0x2ed2, 0x1329, 0x1c60, 0x4151, 0x1351, 0x465d, 0x40f1, 0x4388, 0x408b, 0xba1e, 0xbfd0, 0xbfb6, 0x4132, 0x074b, 0x18de, 0x420a, 0x43ec, 0xb2e0, 0x4319, 0x41a9, 0xb0e2, 0x4190, 0x400d, 0x4159, 0xba5f, 0xa852, 0x4346, 0xb0b5, 0xbf44, 0xa4e4, 0x1b56, 0x1a5d, 0xba77, 0xbae3, 0xb2be, 0x405a, 0x41e3, 0x3cc6, 0x1ebf, 0x19c2, 0x41ed, 0x4498, 0x1c91, 0x3ae6, 0x42b5, 0xb2e3, 0x18f6, 0xaa8b, 0x45d8, 0x414a, 0xbf6a, 0x4172, 0x4017, 0x1a6f, 0x45d8, 0xa35e, 0x4259, 0xba00, 0x1ede, 0x4125, 0xba04, 0xb2bc, 0x419b, 0x18ef, 0xb20b, 0x18bc, 0x449a, 0xbad5, 0x40b3, 0x42d1, 0x2138, 0x1f04, 0xb266, 0x410f, 0xb04f, 0xba03, 0xbf44, 0x1a63, 0xb276, 0xb2ff, 0x408e, 0x2b75, 0x468d, 0x402e, 0x4265, 0x1e0a, 0x1e17, 0xba0d, 0x0383, 0x44d5, 0x1333, 0x1bf7, 0x408e, 0x0079, 0x4227, 0x43ba, 0x41f2, 0xa9a9, 0x4072, 0x42ca, 0x124f, 0xba40, 0xba14, 0x4146, 0xbf03, 0x426f, 0xba4f, 0xba49, 0xb28b, 0x4368, 0xbf47, 0x4315, 0x3f70, 0x43ed, 0x310d, 0xb013, 0xbacc, 0xbfc0, 0xbf90, 0x4272, 0xbf85, 0xb06d, 0x4162, 0x4155, 0x1d1a, 0xa425, 0x08d3, 0x1f77, 0xb0a4, 0x1e2b, 0xbf00, 0x40f9, 0x2f9d, 0x3eea, 0xb035, 0x42a1, 0xbaf2, 0x2410, 0xa6e0, 0xbf6f, 0x4335, 0x4319, 0x43bc, 0x1f9b, 0x41e0, 0x1b46, 0xbfe0, 0x4297, 0x4177, 0x19ef, 0x42bb, 0x4075, 0x40d4, 0x42cb, 0x1db4, 0xb251, 0x400a, 0xbfa3, 0x441c, 0xb2ce, 0x4187, 0x4365, 0xba30, 0x43a9, 0x42b6, 0x4330, 0x41eb, 0x1341, 0xb016, 0xba5b, 0x4208, 0xbff0, 0x42ba, 0x4001, 0x42b4, 0xb243, 0xb2bd, 0x1cca, 0x420a, 0xa277, 0x4129, 0x40c6, 0x187b, 0x3b6a, 0xbfc9, 0x4083, 0xb2a0, 0x07bb, 0x413d, 0x4116, 0xae4f, 0xb097, 0x39d7, 0xba11, 0x43d3, 0x42b9, 0x2daf, 0x43cd, 0xb21e, 0x4185, 0x4203, 0xba2e, 0xb21a, 0x4095, 0xbf2d, 0x4230, 0x42c8, 0x4277, 0x1e62, 0x19ae, 0x4309, 0xba43, 0xba09, 0x415b, 0x40b8, 0x4271, 0xaf78, 0xb298, 0x434f, 0x2fe1, 0x414a, 0x43b1, 0xbf72, 0x4043, 0xbff0, 0x0c11, 0x4161, 0x40e6, 0x191d, 0xb222, 0xa0d6, 0x4247, 0x2b58, 0x1941, 0x4157, 0x4223, 0xac08, 0xb0a0, 0x1aa4, 0x42fa, 0x18bb, 0x4080, 0x1a11, 0xb26c, 0x3e43, 0x4089, 0x41ac, 0xba1c, 0x4326, 0x42b3, 0xbf9c, 0x21eb, 0xba4a, 0xbf3e, 0x0c75, 0x2d1e, 0x43a7, 0xb200, 0x4005, 0x42a7, 0x1848, 0xb2db, 0x05fb, 0xbae2, 0xbf65, 0x407e, 0xb2f1, 0x418a, 0x43db, 0xb2a6, 0x4244, 0xbf2a, 0xa9d3, 0x0194, 0x42dd, 0x25d1, 0xb2d1, 0x41f3, 0x42da, 0x3808, 0x412a, 0xb287, 0x44d3, 0x1977, 0x41c2, 0x1091, 0xbf28, 0xbae4, 0x4144, 0xba62, 0x4643, 0x40f8, 0x4194, 0x1bec, 0x407a, 0xb0f3, 0xb234, 0xba45, 0xb0d3, 0xba4e, 0x404e, 0xbf76, 0xb2e2, 0xbaec, 0x4665, 0xaa4c, 0xb20e, 0xba08, 0xa8b3, 0xb072, 0x1b10, 0xbacb, 0xb064, 0x41ef, 0x44f2, 0xaaa2, 0x4362, 0x1eb0, 0xa948, 0x4236, 0x46c4, 0x1b32, 0x40f1, 0x2641, 0xab44, 0x4222, 0x42be, 0x1bc0, 0x4639, 0x4135, 0xbfe4, 0x434a, 0xba33, 0x1ee0, 0xb229, 0xb06d, 0x44e9, 0x04af, 0x40d1, 0x4456, 0x46e9, 0x4112, 0x18ce, 0x2066, 0xbfe8, 0x4240, 0xbaf0, 0xbfcf, 0x4691, 0x079d, 0x438f, 0x406e, 0x4075, 0x2821, 0xa06e, 0x4167, 0x4385, 0x3a13, 0xbac8, 0x45e4, 0x429d, 0x42b0, 0xb20b, 0xbfb1, 0xa2ee, 0x35d9, 0x1d65, 0x152c, 0xb04e, 0x0f48, 0x254e, 0x1ce5, 0x2e0f, 0x3304, 0xb0bf, 0xb2de, 0x1c11, 0x1e33, 0x1957, 0xbf60, 0x19b6, 0x400b, 0x401a, 0xbfd9, 0x4621, 0x19a6, 0x2a35, 0x26e2, 0xb294, 0x412b, 0x4165, 0x1b4f, 0xad1b, 0x2f76, 0x1a42, 0xbf65, 0x3406, 0xb20a, 0x43f5, 0xba32, 0x45db, 0x23fe, 0xbad4, 0x1ab0, 0x222a, 0xb22a, 0xb206, 0x43cd, 0x13e5, 0x1c19, 0x2ac8, 0x268a, 0x4319, 0x1c7b, 0x1f02, 0x45f3, 0xb25c, 0x400e, 0xa29c, 0xb041, 0xbfd2, 0x4261, 0xb0dc, 0xa298, 0x447b, 0xbfc3, 0x2c44, 0xba23, 0x41f3, 0x43a1, 0x41ec, 0x43ad, 0x41e2, 0x401d, 0xb096, 0x40dd, 0x3336, 0x415c, 0x4037, 0x42e5, 0x4323, 0xb257, 0xb2a7, 0x1cc7, 0xba28, 0x40de, 0x40dd, 0x4069, 0x1bda, 0x46e4, 0xbf4d, 0x010c, 0x1533, 0x41f8, 0x42c3, 0x41ff, 0x14cd, 0x4291, 0x1da6, 0x16e2, 0x3752, 0xafe8, 0x4192, 0x46c4, 0x4204, 0x40b8, 0x222c, 0x23e3, 0xaca3, 0xbf5c, 0x02c8, 0x4219, 0xb078, 0x205d, 0x4267, 0x41c0, 0xba36, 0x1c8d, 0x35be, 0xb2d4, 0xb2fe, 0x40e7, 0x404f, 0x43a1, 0x3017, 0x0f83, 0xb08b, 0x40ff, 0xbf68, 0x40f7, 0x2d84, 0x4072, 0xb2a9, 0x089e, 0x1c2f, 0x1ccf, 0x412c, 0xba2b, 0x1cda, 0x19d2, 0xb04a, 0xb053, 0x2419, 0x422f, 0x4269, 0x1997, 0x0701, 0x1326, 0x0e29, 0x4105, 0x425b, 0x4111, 0x4677, 0x4373, 0xbf6c, 0x1ad8, 0x4305, 0xba7c, 0x064b, 0x34a4, 0x4241, 0xb2b5, 0xbf0e, 0x4282, 0x187e, 0xbaf4, 0x460d, 0x417c, 0xba19, 0xbf90, 0x1e52, 0x42f2, 0x41c9, 0x23d2, 0xb221, 0xb2cc, 0x1f32, 0xba0d, 0x43b3, 0x408e, 0x4040, 0x43d5, 0xb05f, 0x40bb, 0x44f8, 0x179c, 0xa95e, 0x18de, 0xb229, 0x42ce, 0x4310, 0xbf45, 0x426a, 0xab45, 0x4339, 0x40a8, 0xabbe, 0x43d3, 0x3ba2, 0x1a98, 0x43f4, 0x4364, 0xaa92, 0x1878, 0xaf21, 0x4422, 0xa6c1, 0xa127, 0xba16, 0x4032, 0x1ed2, 0x4354, 0x46c2, 0x4275, 0x3beb, 0xb240, 0x4029, 0x417c, 0x41a4, 0x42c1, 0xbf5b, 0x411a, 0x4134, 0x3a11, 0x39e1, 0x2cc5, 0xb0f1, 0x35af, 0xb2b3, 0xb214, 0x4312, 0xbfc3, 0x4151, 0xb227, 0x4190, 0xa889, 0x402e, 0xaa1c, 0x4236, 0xbf72, 0x1ab4, 0x40f2, 0xab93, 0x33d2, 0xb26d, 0xa602, 0xb090, 0xb0af, 0x4333, 0xba5e, 0x4036, 0x411a, 0x3860, 0xb2dd, 0x4322, 0x41f9, 0xbff0, 0x031c, 0x4093, 0xb05a, 0x4284, 0x40fe, 0xb031, 0x31d3, 0xb060, 0xbf7f, 0xb02b, 0xb254, 0x42e4, 0xb247, 0xbf00, 0xb221, 0xb040, 0xb2e4, 0x40b1, 0x1dff, 0x400c, 0xba6e, 0x4457, 0x1e3f, 0xb2bf, 0x40de, 0xbfc3, 0x1e09, 0x42ed, 0xb2a3, 0xba5d, 0xbfe0, 0xba5f, 0xba77, 0x1976, 0x42c1, 0xb278, 0x46e5, 0x40b7, 0x4495, 0x43b4, 0xb00e, 0x40f8, 0x27fa, 0x4170, 0x422a, 0x411e, 0x1b39, 0x41e0, 0x447f, 0xbf94, 0xa87e, 0x41a6, 0x45f5, 0xb204, 0x43bb, 0xb270, 0xb0f6, 0xb0f3, 0x0fcf, 0x2554, 0xb202, 0x4009, 0x4056, 0x37e1, 0x1d32, 0x41e1, 0x1988, 0xba4e, 0x468b, 0x4186, 0x42d5, 0x42a4, 0xbf21, 0x3a5e, 0xbff0, 0x13c6, 0x2352, 0x1def, 0xb26e, 0x41ed, 0x429b, 0x4253, 0xb2d1, 0x4180, 0xbf6f, 0x427b, 0x0ff2, 0xa388, 0x42e5, 0xb2aa, 0x1c71, 0x429b, 0xb072, 0xb22d, 0xb211, 0xb066, 0x173e, 0x1d80, 0x4253, 0xa2d6, 0x4033, 0x404a, 0xb2a5, 0x413b, 0xbade, 0x417c, 0x40c2, 0xb257, 0x1fd5, 0x0bb8, 0x42aa, 0x1d98, 0x43e6, 0xbfba, 0x1e34, 0x020a, 0xb238, 0x3c80, 0x4023, 0x4000, 0x437c, 0xbf80, 0x15e7, 0x1366, 0x42d7, 0x44bd, 0xbf2b, 0x4274, 0x28cf, 0x4376, 0x46f5, 0x0319, 0x465b, 0x4087, 0x4106, 0x4135, 0x0ef5, 0xb2e9, 0xb07d, 0x415a, 0xba1f, 0x4368, 0x40af, 0x469c, 0x41bf, 0x2d0c, 0x4305, 0x4222, 0x41e3, 0xbf98, 0x4649, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xbb7d36f6, 0x99698622, 0x49d98852, 0x36cb9669, 0x824878bf, 0xc93e95e5, 0x63323f2d, 0xc8f7f318, 0x96c967ec, 0x5a2d76e7, 0x8dfbec30, 0x6fa00b5f, 0x2d920f82, 0x5d28d234, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0x004003e8, 0xfa000000, 0x000022ea, 0x00000000, 0x00000000, 0xffffffff, 0x9681941b, 0x00000000, 0x9681941b, 0x000003e8, 0x000003e8, 0x000001f4, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x43de, 0x4327, 0xb263, 0xa931, 0x4228, 0xb061, 0xbac6, 0x434d, 0xa868, 0xbf90, 0xba1c, 0x43bf, 0xbf70, 0x4229, 0x42b0, 0x40f0, 0x32dd, 0xba5d, 0xbf85, 0x43a4, 0xb2af, 0xb2b1, 0x4021, 0x2d77, 0xa666, 0x4176, 0x4089, 0x426e, 0x41de, 0x4345, 0x400b, 0x4056, 0x1bee, 0x19d7, 0xa0e7, 0x40b1, 0x2328, 0xbf06, 0xa385, 0x465d, 0xb277, 0x4664, 0x1a4b, 0xbad8, 0x0428, 0x0685, 0x4028, 0x300a, 0x4349, 0xba0f, 0xb2e3, 0x4662, 0x40d7, 0x435e, 0x0b39, 0x421f, 0x4670, 0xb2d6, 0x17aa, 0x4652, 0x4688, 0x1d08, 0x0544, 0x1fd9, 0xbfb3, 0x4166, 0x40ef, 0xbff0, 0xb0c9, 0x047f, 0x4063, 0x1011, 0x1f38, 0x4475, 0x427b, 0x4149, 0xba6c, 0x428f, 0x43da, 0xb2c2, 0x1f1a, 0x40d0, 0x4283, 0x1b46, 0x2aad, 0xb26d, 0x42b6, 0xa275, 0xba02, 0x40bf, 0x047c, 0xb2de, 0xbf60, 0x41a9, 0xbf28, 0x43ae, 0xba2b, 0x1dbc, 0x0735, 0x4234, 0x408b, 0xba25, 0x1086, 0x420d, 0xb074, 0xb290, 0x41e9, 0x410d, 0x4256, 0x42a2, 0x40bd, 0xabce, 0x4380, 0x4015, 0x4169, 0x405d, 0x3fbd, 0xb253, 0x433a, 0xbfd7, 0x41b2, 0x15be, 0x188a, 0x0fc1, 0x2952, 0x35a4, 0x40f1, 0x2a4c, 0xbfbd, 0xb2e4, 0xb23a, 0x1f28, 0x4630, 0x388c, 0x4147, 0x40f0, 0x4567, 0xbac7, 0xbfa0, 0x436c, 0x3e60, 0xb2d1, 0xb0e4, 0xb26a, 0x1830, 0xba33, 0xb26f, 0x2b61, 0x400b, 0x4346, 0xb069, 0x4039, 0x0aaa, 0xba75, 0x43d5, 0x417b, 0x42ec, 0xae57, 0xbf6e, 0x41cf, 0x3075, 0xb29b, 0x15ef, 0x1b99, 0x435d, 0xb215, 0x42fd, 0x4234, 0x2e88, 0xba7d, 0xbade, 0xba31, 0xbad9, 0xa2a3, 0x42f1, 0x1bb3, 0xbafa, 0xa940, 0x40a6, 0x3689, 0xb2ba, 0xbfaf, 0x4052, 0x1a92, 0xb2be, 0x462e, 0x41d1, 0x4145, 0x1619, 0x46c2, 0x3167, 0x409e, 0xba38, 0xb20f, 0xbacc, 0xb238, 0x4297, 0xbfa1, 0xbaf4, 0x38a8, 0xb293, 0xa847, 0x4138, 0x4175, 0x40fa, 0x259a, 0x4244, 0x4029, 0x3497, 0x15f3, 0xb07c, 0xbacb, 0x433f, 0x42e5, 0x3baf, 0x4029, 0x1aab, 0x20de, 0xa582, 0xbf87, 0xb2ff, 0x4098, 0xbace, 0x415c, 0x40cf, 0xb272, 0xbfdf, 0x40e0, 0x42dd, 0x4122, 0x41ba, 0xb2e2, 0x2632, 0x4153, 0xbf7d, 0x3131, 0x3b4d, 0x43ba, 0x41ab, 0x3b6c, 0xb2b3, 0x31e5, 0xba05, 0xb268, 0x1431, 0xba04, 0x4556, 0x43ab, 0x421c, 0xb2a4, 0xb23c, 0x410e, 0x42c9, 0x4063, 0x1da5, 0x402f, 0xb288, 0x0b26, 0x0570, 0xb03b, 0x4260, 0x4096, 0xbf8e, 0x10ad, 0x3f3a, 0x418e, 0x0b8b, 0x2f4f, 0x405a, 0xb00a, 0xb07e, 0x04ce, 0x41fa, 0x4271, 0x30c8, 0x40e5, 0x43f5, 0x3887, 0xa453, 0x4608, 0xbf71, 0x0220, 0x4540, 0x4188, 0xbff0, 0x434c, 0x3c0b, 0x41eb, 0x2d26, 0x4675, 0x4149, 0x4614, 0xb238, 0x308c, 0x4005, 0xbf5f, 0x4041, 0x0a0d, 0xb258, 0x43f4, 0x4316, 0x4170, 0xb2dc, 0xbfb6, 0x2875, 0xb052, 0x40dd, 0xbfb4, 0x4208, 0xba45, 0x409b, 0x4385, 0xa8f3, 0xb213, 0x42cb, 0x3f92, 0x3ae4, 0xb2f3, 0x3366, 0xb236, 0x4399, 0x400d, 0x4128, 0x4419, 0x34de, 0xa079, 0x43d7, 0xbaf8, 0x4277, 0x0594, 0x42fd, 0x4413, 0x43fe, 0xbf78, 0xb29b, 0x41ee, 0x185b, 0x19f6, 0xba51, 0x4012, 0x44d8, 0x05fc, 0xa2ea, 0xb2f6, 0x415a, 0xb2a7, 0x310d, 0x19e3, 0x408f, 0x19c7, 0xa5d4, 0xb05a, 0x1485, 0x4162, 0x41f0, 0x4200, 0x402a, 0xbf49, 0x0fdf, 0x185a, 0x1e02, 0xb2ab, 0xba13, 0xbaca, 0x46ed, 0x1e91, 0x42f5, 0xbf77, 0x459b, 0x42ae, 0x40eb, 0x19c3, 0x4213, 0x2acb, 0x1080, 0x41c1, 0x42fb, 0xb211, 0xab6c, 0x309e, 0xba6e, 0xb03e, 0x43fa, 0x42c5, 0xa06c, 0x1777, 0x3986, 0xbf51, 0x40d2, 0x4691, 0x22f9, 0x43fe, 0x40c2, 0x4215, 0xb028, 0x43a4, 0x42ae, 0xb2bd, 0x429e, 0x433d, 0x42e7, 0x42f5, 0xa94c, 0xbfaa, 0x34b2, 0xba3d, 0x424d, 0x4291, 0x4120, 0x1bb9, 0x43c2, 0x1ff7, 0x40fa, 0x342f, 0x43fe, 0x42bf, 0x433e, 0xbfb5, 0x4076, 0x3aaa, 0x4158, 0xb2bf, 0x40d7, 0x4068, 0xbf6f, 0xb036, 0x40ed, 0xbaec, 0x18fe, 0x00f2, 0xbadd, 0x439b, 0xa23d, 0x42a5, 0x411a, 0x41d8, 0x16cf, 0xba28, 0xbfe2, 0xba20, 0x43cf, 0xb2ae, 0xb2bc, 0xb2f7, 0x448c, 0x43dc, 0x43ae, 0xb2a8, 0xa5a6, 0x4216, 0x4295, 0x344b, 0x1da4, 0x4273, 0x408f, 0xb21d, 0xa9a9, 0x43db, 0x42b0, 0xbf5a, 0x4640, 0xb20a, 0xa7bb, 0x417c, 0xb26b, 0xbfd7, 0x1e2c, 0x4250, 0xb053, 0x4071, 0x424f, 0xb0d4, 0x4373, 0x429a, 0xba12, 0x4078, 0xba4e, 0x2762, 0xa5a4, 0xb200, 0x44b3, 0x2257, 0x0339, 0x42b5, 0x1a8f, 0x45b2, 0x4033, 0xbaed, 0x1f10, 0x1b45, 0xbae3, 0x3bae, 0x40d9, 0x03ff, 0x4175, 0xbf45, 0x32f9, 0x4053, 0x3ad1, 0x27bf, 0x40c4, 0xbf60, 0x40d2, 0x4202, 0x41f2, 0xb2be, 0x4341, 0xb25c, 0x1b18, 0x40bd, 0xb089, 0xbae2, 0xbf5f, 0x4403, 0x4607, 0xa684, 0xa2f3, 0x40ae, 0xba3b, 0x4358, 0x422c, 0x4167, 0x40a7, 0x4215, 0x4440, 0xb219, 0xbfd0, 0x40d5, 0x4038, 0xb2ca, 0x42e9, 0x43af, 0x42cb, 0x413c, 0x4158, 0x44a3, 0x422f, 0xbadf, 0xb279, 0xb286, 0x1f1e, 0xbf09, 0x4319, 0xba13, 0xba31, 0xa86a, 0x4367, 0xbf00, 0xb0d8, 0x423f, 0x413a, 0x44d5, 0xbac4, 0xad13, 0x4093, 0x4149, 0x41fb, 0x422b, 0x1e34, 0x3f5e, 0xb246, 0x41b8, 0xbf70, 0x435d, 0xbfdd, 0x01b2, 0x420b, 0xbfc0, 0x464f, 0xb034, 0x1c2d, 0x1a10, 0xbfd5, 0xa298, 0x412d, 0x41a3, 0x42de, 0x42b2, 0xba1e, 0xb298, 0x4170, 0x1827, 0x4093, 0xb015, 0xba4a, 0x2cf0, 0xb2d9, 0xb067, 0x0c34, 0x35ed, 0x3b0a, 0x085a, 0x46ab, 0x158d, 0x3889, 0x42ab, 0x1c3a, 0xbfc6, 0x372b, 0x17bc, 0x3899, 0x4611, 0x1a8a, 0xbf5d, 0x44d8, 0xb042, 0x1ab4, 0xb0ac, 0x438a, 0x41af, 0xba06, 0xb25e, 0x164a, 0x4067, 0xb01c, 0x43eb, 0xbf2e, 0x1b94, 0xba3c, 0x1494, 0xbf77, 0x41da, 0x09e3, 0xbadc, 0xa22f, 0x3fcc, 0x1f3d, 0xba4a, 0x400a, 0x432e, 0xb289, 0x1b35, 0xb07e, 0x3cc2, 0x428c, 0x2025, 0xafee, 0x251a, 0xbfb3, 0x44cb, 0x45a8, 0xb294, 0x4328, 0xb08c, 0xb0b4, 0xba26, 0x4097, 0xaa3d, 0x40a6, 0x434b, 0x4560, 0x4660, 0x1f5f, 0x0839, 0x42f0, 0xb2b9, 0x465b, 0x41b9, 0x194f, 0x405a, 0x400d, 0xa719, 0x2c50, 0x1e4e, 0xb242, 0xba30, 0x42ae, 0xb24f, 0xbf43, 0x4681, 0x1a48, 0x1d26, 0xb0fd, 0x4004, 0x423e, 0xa9f0, 0xba40, 0xbaff, 0x43e1, 0xa99b, 0x4432, 0x41f4, 0xbf26, 0x0a13, 0x1821, 0x2ba4, 0x44b9, 0xba22, 0x322b, 0x1c9a, 0x432c, 0xba70, 0xb234, 0x3ca7, 0x4547, 0x4078, 0x4144, 0x40ec, 0xad9a, 0x04d0, 0x4327, 0x37e6, 0x09ea, 0x3f50, 0x1526, 0x407d, 0x1835, 0x4402, 0x1e38, 0x0896, 0xbf04, 0x1909, 0xb2db, 0xb297, 0xb2a2, 0x012d, 0x389d, 0x0b0f, 0x0c37, 0xb0af, 0x4460, 0x4057, 0x44cb, 0x41c7, 0xbaef, 0x429c, 0xaf1a, 0x46dd, 0x4305, 0xb00e, 0x4071, 0x1817, 0xbf34, 0x4349, 0x01c4, 0xb0f7, 0xb209, 0xbfc0, 0x4362, 0xa7c2, 0x43bc, 0x45cd, 0x2edb, 0xb28f, 0xbf53, 0x4102, 0x44cc, 0x431a, 0x1c7c, 0xb207, 0x445e, 0x432c, 0xb099, 0x1de3, 0x46b2, 0x2163, 0x429a, 0x4108, 0x1361, 0xb09d, 0xbac4, 0xb285, 0x0eaa, 0xbf65, 0x437f, 0x41b1, 0x4197, 0x1ff3, 0x2b09, 0x403b, 0x41b8, 0xb231, 0x42c8, 0x42cf, 0x43e6, 0x0f35, 0x1989, 0x1ff2, 0x41c6, 0xbfd0, 0x1e9b, 0x434a, 0x42e3, 0xa90c, 0x40a4, 0x4208, 0x4200, 0xbf8a, 0x1b32, 0x410f, 0x2ee4, 0x33ed, 0xb240, 0xb0f4, 0x40c3, 0x4250, 0x2be7, 0xb019, 0xb2dd, 0xbfd7, 0xb0d4, 0xba08, 0xb2fb, 0x41b7, 0x1308, 0x4267, 0x41c0, 0xbfd0, 0x434c, 0xb06c, 0x4540, 0xb204, 0x1a34, 0x4140, 0xbad7, 0x1cc9, 0x3e9e, 0x390a, 0x40fa, 0x404e, 0xbfaa, 0x08cb, 0x4261, 0x4583, 0x339d, 0x1e37, 0x42fc, 0x437e, 0x435b, 0x3be3, 0x1891, 0xb222, 0x4245, 0x4130, 0x08b8, 0x447a, 0xb226, 0x034a, 0x4392, 0x4379, 0x40ec, 0xa38a, 0xba18, 0x42d9, 0x02f5, 0x4337, 0x41da, 0xbfc6, 0x4491, 0x4071, 0xb215, 0x1b51, 0x41dd, 0x41cd, 0x4223, 0x00af, 0xb2a7, 0x42e5, 0xb020, 0x43e6, 0xb27b, 0x439a, 0x4213, 0xbac3, 0x426a, 0x3d19, 0xa5b8, 0xb0f3, 0xbfc0, 0xba34, 0xba5e, 0xbfc6, 0x42f6, 0x103e, 0x45c1, 0x4482, 0x42ab, 0x2d50, 0x1f4e, 0x411b, 0x4316, 0xb01d, 0x45ee, 0x1db2, 0xb0a5, 0xbf6e, 0x0cf1, 0x1ac8, 0xba74, 0x43ca, 0xba28, 0xa979, 0x18a9, 0xbf01, 0xa761, 0x41fb, 0xb23a, 0x4242, 0x4358, 0x4163, 0xaabf, 0x13cb, 0xbf08, 0x189a, 0xb257, 0x43ee, 0x4085, 0x46f2, 0xb281, 0x45e3, 0x1a1f, 0xa8aa, 0x45e8, 0xba58, 0xb24c, 0x1e6b, 0xb252, 0x41b6, 0x43f7, 0xbf0c, 0xbad8, 0xb276, 0x43b7, 0x4205, 0x283f, 0x4208, 0x2afa, 0x46f1, 0x387c, 0x4269, 0x433a, 0x201c, 0x4072, 0x45bb, 0xbf55, 0x27d4, 0xb0b8, 0x4049, 0x426f, 0x400d, 0x4031, 0x41da, 0x41f1, 0x42c2, 0x4336, 0x432c, 0x4187, 0x445d, 0x423d, 0xb0cc, 0x408d, 0x43d5, 0x401b, 0x4397, 0xb26d, 0xba34, 0x41e3, 0x4326, 0x4355, 0xb01e, 0xbad7, 0xbfdd, 0xba3c, 0xba46, 0xba70, 0xa0a6, 0x1e83, 0x41cd, 0x41f0, 0x4256, 0xbf00, 0xb2df, 0x41c4, 0x45d3, 0xb2c6, 0xa98f, 0xbfde, 0x3ce1, 0x4263, 0x40b3, 0x45b6, 0x445f, 0xbf0b, 0x1d50, 0xb238, 0x2de2, 0x2b7f, 0xb0b8, 0x1629, 0x411b, 0xaeab, 0x4306, 0x2c95, 0x02e2, 0x4208, 0xba37, 0x25ef, 0xb08d, 0x4352, 0x2571, 0x425e, 0xba43, 0xb21d, 0xbfd2, 0x412b, 0x4644, 0x260f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd8508733, 0x37686f1d, 0x97ff025a, 0x7e27c2d9, 0xe771ba18, 0xaba51d16, 0x78d8fc78, 0x633a3766, 0xf31e6a39, 0xaa1cca97, 0x3f7fc03c, 0x49e22f13, 0xd6f3fcdf, 0x6bb27080, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0xffffcb9d, 0x00000000, 0xe1000000, 0xffffffff, 0xffffff1e, 0xffff9dcb, 0xe0000000, 0xdfcfffff, 0x49e23000, 0x00000000, 0x00000000, 0xaa1ccb83, 0xd6f3fcdf, 0xaa1cc3e3, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x46f3, 0xbfa0, 0xba72, 0x422f, 0x4194, 0x403f, 0x418a, 0x1b2f, 0x1a10, 0xb282, 0xb004, 0xad90, 0xa257, 0x43cc, 0x1e2d, 0x44a9, 0x4093, 0xba76, 0x4158, 0xbfe0, 0x2347, 0x434f, 0x0059, 0x45eb, 0xbf72, 0x40e7, 0xb086, 0x1feb, 0x4339, 0x40e6, 0x42bd, 0x409b, 0x4000, 0x4420, 0xba49, 0x4276, 0xb210, 0x2b38, 0xba5c, 0x1a1e, 0xab1f, 0x2962, 0x20bd, 0x42ae, 0xb0ab, 0x2234, 0x3607, 0x0b88, 0x29e7, 0x4378, 0xb06b, 0x40ec, 0xbf28, 0x43f2, 0x4135, 0xb00e, 0x1c37, 0x4025, 0x3570, 0xbf80, 0x42db, 0x249a, 0x4328, 0x4211, 0x41c7, 0x4239, 0x2924, 0xa5ef, 0x0387, 0x1a9f, 0x4352, 0x1f8f, 0xb031, 0x41bd, 0x1cdd, 0xb2ed, 0x4002, 0x4182, 0x07c7, 0xbf77, 0x406f, 0x431f, 0x42d9, 0x3e9c, 0xbf60, 0x4377, 0x4277, 0x4314, 0xb001, 0xbf6b, 0x4070, 0x41f7, 0xbadf, 0x0139, 0xb246, 0xba0a, 0x439e, 0x433b, 0x18e3, 0x320f, 0x0f77, 0x45a2, 0x1d6b, 0x1a0b, 0x42eb, 0xbf01, 0x1a8f, 0x4286, 0xa09a, 0xbae4, 0x431c, 0x4307, 0x3e46, 0x430d, 0x42ce, 0x433f, 0x109e, 0xa573, 0xba79, 0x1ef6, 0x41cf, 0x29c8, 0x0dca, 0x0e56, 0xbae7, 0x3d0d, 0x4374, 0x1e44, 0x0b81, 0xba51, 0x4087, 0x4425, 0x4283, 0x420e, 0xbf94, 0x409f, 0x42be, 0x4353, 0x436d, 0x4291, 0xbfc4, 0x4365, 0x40b3, 0x418f, 0xbf3e, 0x1e3d, 0x46ba, 0x1f8c, 0x42ea, 0x1c25, 0x4135, 0xb21a, 0xb29b, 0x16e9, 0x401d, 0x4653, 0xa607, 0x43e2, 0x0ec5, 0x4201, 0xbae0, 0xba26, 0xa713, 0x43c5, 0x23cf, 0x10a8, 0xb08e, 0x4232, 0x43cb, 0x43d9, 0x0774, 0xb03b, 0xbf62, 0x4205, 0xbfa0, 0x4661, 0x403b, 0xbf17, 0x40bb, 0xbaee, 0x42a1, 0x42b3, 0x463e, 0xafdf, 0x3543, 0x3528, 0x1eea, 0x4038, 0x1dd7, 0x38ba, 0x4092, 0x41b0, 0xba32, 0x4416, 0x44fd, 0xbfc8, 0x439f, 0x4319, 0xa47d, 0xb275, 0xb244, 0xbf95, 0x1453, 0xb04f, 0x1c40, 0x3477, 0xbf76, 0xaeb7, 0x0406, 0xbad8, 0xbf5e, 0x1a0d, 0xb2d6, 0xba5e, 0x437b, 0x0b3c, 0x1d54, 0x3c58, 0x404e, 0x427b, 0x25f8, 0xbfa0, 0x4053, 0x127f, 0x42e6, 0x438b, 0xbf23, 0xb2d9, 0x4389, 0x4350, 0xade8, 0x439e, 0x4485, 0x3ec6, 0x19a8, 0x0fe2, 0x42fc, 0x4262, 0xada2, 0x40e6, 0xb0e5, 0x4251, 0xb221, 0x1f4c, 0x127c, 0xbf56, 0x25d6, 0x20f9, 0x4329, 0x4123, 0x413d, 0xb2df, 0xb23b, 0x4653, 0xb2e4, 0xb0a9, 0x42ea, 0xb215, 0x4130, 0x4249, 0x4391, 0xb28f, 0x4127, 0x420e, 0x16ab, 0xa580, 0xb26c, 0x29b2, 0xa29e, 0xb09c, 0xb20f, 0xbfd2, 0x42e1, 0x42cf, 0x0a23, 0x4049, 0x433c, 0x45e0, 0xbf46, 0x407f, 0x408f, 0x4165, 0x4148, 0x19e5, 0x4594, 0xb0f1, 0x438d, 0x4000, 0x42b7, 0x4136, 0x31af, 0x40ea, 0xb0b9, 0x4323, 0x0710, 0xbfa2, 0x38a1, 0x40d2, 0x41a9, 0x4149, 0x0c49, 0x4168, 0x3f89, 0x433c, 0xb2fd, 0x42e3, 0x4353, 0x40a7, 0x43e8, 0xba3a, 0xb266, 0x4397, 0x0a3a, 0x40ab, 0x1fc5, 0x1f5f, 0x4117, 0x4573, 0xb01a, 0x418c, 0x44a9, 0xaa7b, 0x13a2, 0xbfe4, 0xb213, 0x41c3, 0xb28f, 0x2ff1, 0x2600, 0x2452, 0x438c, 0xa2cc, 0x1b55, 0x42ad, 0xb2fc, 0x1067, 0xb2b8, 0x41f0, 0x4625, 0x0ac8, 0xb25e, 0x441f, 0x40a8, 0x43d6, 0x18c7, 0xbadb, 0xb292, 0x4453, 0xbf8b, 0x19d8, 0x1f32, 0x18f9, 0x46b5, 0xb22a, 0x088b, 0xba5a, 0xba7f, 0xb068, 0x03a6, 0x444b, 0xb21c, 0x3014, 0x41cb, 0xbf26, 0x4015, 0xb2c5, 0x1a29, 0x1a7f, 0x0b53, 0x4219, 0x3afd, 0xba1c, 0x0def, 0x3b5b, 0x423c, 0x4003, 0x42a3, 0xb0f8, 0x42ce, 0x4357, 0x41ed, 0x4070, 0x42c4, 0x1c75, 0xa517, 0xb0ab, 0xba3f, 0x4341, 0x2c35, 0x1bd9, 0xbf76, 0x07be, 0x4173, 0x4145, 0xb0a5, 0x11a1, 0x4068, 0x43d8, 0x42e2, 0x40f6, 0xbafd, 0x1fc1, 0x407e, 0xba38, 0x4109, 0xb016, 0x40a6, 0x43d5, 0x4274, 0xbf1e, 0x2433, 0x289d, 0x43ca, 0xb25d, 0xba5d, 0x0a85, 0x43d7, 0x3e24, 0x0e1d, 0x1723, 0x444a, 0x18e3, 0xb2ba, 0xba2c, 0xba55, 0x075d, 0x43e8, 0x0247, 0x40fc, 0x1e94, 0xbfa6, 0x1c10, 0x3a69, 0x4023, 0x4581, 0x1925, 0xb0d7, 0x1f3f, 0xb0a9, 0x45e6, 0x2ec7, 0x41c3, 0x046b, 0x1f29, 0xbf4e, 0xb289, 0xb250, 0x425b, 0x40df, 0xb2b8, 0xab56, 0x1cb4, 0xbac1, 0x4271, 0xbf64, 0x4373, 0x3c6a, 0xbf5a, 0x43bb, 0x429f, 0x1f9e, 0xbac2, 0x404a, 0x0103, 0x4213, 0x46f1, 0x287a, 0x4088, 0xbacf, 0x4331, 0xba4b, 0x32fc, 0x43bb, 0xb02b, 0x4176, 0xa3dc, 0x44e1, 0x1f3a, 0xbfce, 0x3dc6, 0x4227, 0xba11, 0x4148, 0x09b4, 0xbfd3, 0xb245, 0x4394, 0x40ce, 0x07e5, 0x1956, 0x0db9, 0xb238, 0x40f2, 0x412b, 0x1af5, 0xa1f7, 0x466b, 0xba34, 0x4038, 0xa130, 0x404e, 0x19ca, 0x3a04, 0x1d1c, 0x4077, 0x439c, 0xbadb, 0x1f1d, 0x36ff, 0xbf4e, 0x42d1, 0xba5d, 0x4020, 0x0dd2, 0x42a9, 0x1943, 0x405d, 0xba7f, 0x41a1, 0x447d, 0x4181, 0x0963, 0xb038, 0x436e, 0x412f, 0x404f, 0xae5c, 0x41e7, 0x40e5, 0x434b, 0x20c5, 0x1eb2, 0x4010, 0x3a97, 0xbfb1, 0xb047, 0x0ba6, 0x3abe, 0xb252, 0x4104, 0x4346, 0xb206, 0xad63, 0xbf46, 0x4144, 0x41e0, 0x422b, 0xb0ed, 0x4389, 0x423b, 0xba65, 0xbaf3, 0xaaed, 0xb219, 0xba43, 0x243c, 0x197c, 0x421c, 0x18fd, 0x4313, 0x1cae, 0x140a, 0x42a0, 0xbf4e, 0x4015, 0x18f5, 0x42c6, 0x4115, 0x42c0, 0xba0a, 0x437a, 0xbf5f, 0xb0ae, 0x1d9d, 0x4085, 0x4350, 0x1a00, 0x369b, 0xb22b, 0x24f9, 0x4253, 0x360c, 0x41cd, 0x0fb1, 0xb0a6, 0x025d, 0x18c4, 0xb251, 0x40c6, 0x4349, 0xb2cc, 0xa0be, 0x4377, 0x43da, 0x16b2, 0x413d, 0xbf80, 0x44ac, 0xbfdb, 0xb266, 0x0d95, 0x420e, 0xb083, 0x1b6c, 0x4305, 0xbae4, 0xb28f, 0xb236, 0xba09, 0x2da7, 0xb252, 0xba09, 0xa115, 0x447d, 0xb272, 0xbf75, 0x40f6, 0x0b5a, 0xb086, 0x466b, 0x4632, 0xbf0a, 0x43e0, 0x1f0f, 0xb2e7, 0xba3d, 0x36d4, 0x07bc, 0x4172, 0x3697, 0xb221, 0x0e04, 0x28f3, 0x42e6, 0x198a, 0x4089, 0xbf90, 0x3694, 0x2837, 0x4082, 0x4263, 0x4142, 0x4074, 0x2fc6, 0x1e01, 0xab23, 0x0cb5, 0x4033, 0x0067, 0xbfca, 0x111a, 0x4261, 0x29e3, 0x462d, 0xb299, 0x4072, 0xbfe0, 0x4342, 0xbf3d, 0x0830, 0x1afe, 0x419c, 0x1ce9, 0x42e6, 0x40d5, 0x41cc, 0xb072, 0x3bd5, 0xbfdf, 0x1807, 0x44c2, 0xba38, 0x4693, 0x41a2, 0x4072, 0x42e7, 0xbaef, 0x01b3, 0x25c5, 0x4006, 0x096e, 0x4115, 0x0622, 0x43ac, 0xb00c, 0x1f03, 0xbf48, 0x41c4, 0x43dc, 0x415a, 0x20b3, 0x4064, 0xba24, 0x40f8, 0xb23f, 0x0b4b, 0x42ec, 0xb014, 0x4119, 0xbf78, 0x4393, 0x3aa5, 0x410a, 0x0b23, 0x442f, 0xba39, 0x4298, 0xba42, 0x42c5, 0xa365, 0xbfba, 0x40a8, 0x17e3, 0x4057, 0x417c, 0xaa59, 0xafc2, 0x33ad, 0xa425, 0xbfb9, 0x2e0c, 0x0192, 0xaa5d, 0x0448, 0x421e, 0x4357, 0xb2f0, 0x2a8b, 0x378b, 0xb219, 0x416c, 0xbf8e, 0x43ff, 0x4348, 0x4262, 0xba30, 0x202d, 0xa784, 0xadcc, 0xba34, 0x121b, 0x41da, 0xbfd1, 0xb293, 0x435f, 0x31bc, 0xb263, 0x40a0, 0xbf1c, 0x46cd, 0x22d6, 0x3198, 0x250f, 0x2fc5, 0x4100, 0x4090, 0xbfd2, 0xb207, 0xba6f, 0x436b, 0xba5f, 0x4618, 0xb2d4, 0x1123, 0xba54, 0x33e5, 0x43eb, 0x1e8d, 0xb21b, 0xaf42, 0xab0f, 0xb2cb, 0x4101, 0x4026, 0xb2b9, 0xaaf3, 0x1f93, 0x4208, 0x1d01, 0x400d, 0xbf99, 0x3677, 0x182f, 0x3f84, 0x363f, 0x185c, 0xb2d0, 0x0c54, 0x43dd, 0xa014, 0xb23f, 0x41f8, 0xba0b, 0xba39, 0x255a, 0x4313, 0x4449, 0x1934, 0x4317, 0x4102, 0x156d, 0x01fd, 0x41ec, 0x41e1, 0xb023, 0xbf62, 0x41b9, 0x3f1c, 0x3b9c, 0x1930, 0x1e90, 0x429a, 0x1a6c, 0x1dda, 0x1834, 0x440c, 0xb284, 0xb225, 0xae4e, 0x4080, 0xbfca, 0x1cfd, 0x264f, 0x4298, 0x185e, 0x1dc1, 0xbf17, 0x244c, 0x4546, 0x1b6d, 0xbaf7, 0xb286, 0x2054, 0x3c0d, 0x3570, 0x25a7, 0xb261, 0xb21e, 0xa108, 0x13c0, 0x4274, 0x4310, 0xbf42, 0x4212, 0x40c5, 0x438f, 0x0e16, 0x424f, 0xbf90, 0x438e, 0x37c7, 0xbf70, 0x1c3a, 0x437c, 0x06c9, 0x3d63, 0xba3c, 0x1fc0, 0xb223, 0xb245, 0x4003, 0x43aa, 0x0028, 0xba31, 0x038a, 0xba46, 0x1c2d, 0x4435, 0x0726, 0x41d5, 0xbfa6, 0xb2a3, 0xad24, 0x4662, 0x1fdc, 0xb2f7, 0x4012, 0x37dc, 0x418c, 0x1bf3, 0x42b6, 0x44ca, 0x03ed, 0xba5a, 0x431e, 0xbfdf, 0x4315, 0x437b, 0x42ea, 0xbad9, 0xad16, 0xb24d, 0x0b0b, 0xb24e, 0xbfc0, 0xb05b, 0x435f, 0xb255, 0x424e, 0xbae9, 0x4148, 0xb05f, 0xb243, 0x4182, 0x0e5a, 0xb205, 0xbad7, 0x2fd5, 0xbf21, 0x40ac, 0xb2a3, 0xba5c, 0xb2e0, 0x4061, 0x402e, 0xb0a7, 0x3380, 0x42b8, 0xa8cf, 0x2a06, 0x42ab, 0xb296, 0x42c7, 0x41fd, 0xb25b, 0x4333, 0x419f, 0x437b, 0x2ab1, 0xbaf1, 0x17dd, 0x43a2, 0x36ea, 0x4153, 0xbf91, 0x4023, 0x410e, 0xb2b9, 0xb0e9, 0xace2, 0x00d9, 0x2367, 0xb2f9, 0x41df, 0x2087, 0x3147, 0x41f5, 0xb086, 0x40e6, 0x43ef, 0xb21a, 0x41fa, 0xbf6b, 0x311c, 0xb297, 0xba54, 0xa3cd, 0x4189, 0xb252, 0x4186, 0x43d4, 0x4312, 0x4096, 0xb20e, 0x45f6, 0xba7c, 0x3889, 0x4085, 0x4067, 0x4254, 0x1829, 0x414f, 0x4447, 0x2373, 0x4184, 0xb24e, 0x408b, 0x404b, 0xa39a, 0xa843, 0x4007, 0x401e, 0xbf68, 0xba09, 0xb0f7, 0x46a1, 0xb068, 0x427a, 0x42c9, 0x43f4, 0x41e2, 0x40fa, 0xbac6, 0x4377, 0xbf70, 0x3cb1, 0x41b3, 0x43a8, 0xbf74, 0x1f66, 0xb25c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x320c594d, 0xb070758d, 0x7506c560, 0x186ad0f5, 0x6d69bf75, 0xb7cad7d5, 0x7a31742f, 0xa62baad6, 0x3f33b7a4, 0x3237b374, 0x612a830d, 0xd1ce3d3c, 0xdf52c51d, 0x8c1be78a, 0x00000000, 0xa00001f0 }, FinalRegs = new uint[] { 0xdf52c8e9, 0xfffffffe, 0x0077f7e0, 0x00003048, 0xffffe53e, 0x00000000, 0xffffe539, 0xd3cd7808, 0x3f33b7a4, 0xffffff9b, 0x407d482a, 0x00000000, 0xdf52c51d, 0xdf52c7a1, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x43ce, 0xba1b, 0xba2e, 0x02ec, 0x4241, 0xb244, 0x0d54, 0x40ed, 0x2352, 0xb025, 0x4172, 0x3f8e, 0x1fc3, 0x423a, 0x43ee, 0x41e7, 0x4155, 0xa91e, 0xbaee, 0x41fb, 0x35a0, 0x1a31, 0x414f, 0x4075, 0xbf8f, 0x39ec, 0x4032, 0x29b2, 0x2093, 0x4196, 0x1b62, 0x4411, 0x3f4e, 0xb242, 0x226a, 0xb2b0, 0x3fc2, 0xba70, 0x43b8, 0xbf7b, 0xa6be, 0x41ab, 0xbae6, 0x401e, 0x46a4, 0xbf2e, 0xb09d, 0x435c, 0x423a, 0xb2a8, 0xac17, 0x4382, 0xb2e8, 0x33df, 0x45a1, 0x11b9, 0x4435, 0xbacf, 0xbf23, 0x2fe4, 0x4137, 0x1be5, 0x1528, 0x41c1, 0xb241, 0xb2e2, 0x2601, 0xb2c8, 0xbace, 0x3e99, 0x417c, 0x4362, 0x426b, 0xb2cb, 0xac16, 0x42dd, 0xab93, 0xbad6, 0x44fb, 0x1c86, 0x445b, 0xa008, 0x41ca, 0xbfe0, 0x45d3, 0xb20a, 0xb06e, 0x443d, 0xbfce, 0x43cf, 0x4278, 0x406a, 0xb23d, 0xac1d, 0x4317, 0x14b6, 0x1aba, 0x4437, 0x409d, 0x40d8, 0x1c08, 0xad81, 0x2e13, 0x43da, 0x354d, 0x418a, 0x1911, 0x3024, 0x40cb, 0xbf4c, 0x190c, 0x400b, 0x4090, 0xbfcf, 0xb09d, 0x3f1b, 0xb235, 0x1f6d, 0x1a67, 0x4078, 0x4045, 0x066d, 0x404d, 0x4148, 0xba75, 0x1c4a, 0xb219, 0x2277, 0x41f9, 0xb0c6, 0xb2a5, 0x0907, 0xbf4b, 0xb244, 0x0cdc, 0xb2ea, 0x4093, 0x42a8, 0x40e0, 0x3381, 0x0521, 0x41dd, 0xb290, 0x4064, 0xb08d, 0x400b, 0x43fa, 0x4349, 0xbf2b, 0xab6c, 0xb0ed, 0x43df, 0x0ae3, 0x24ae, 0xbf67, 0x407d, 0x1041, 0xbae3, 0x43fe, 0xbf63, 0x4185, 0xb033, 0x40df, 0x4362, 0x30bd, 0x455d, 0xb090, 0x411a, 0x44b3, 0x40da, 0xbf80, 0x41d1, 0xb2eb, 0x3e13, 0x4112, 0x40b0, 0x1f92, 0x407e, 0x36db, 0x41cf, 0x4397, 0x1982, 0xbad6, 0xb0be, 0x240e, 0x1c50, 0xba3f, 0x4224, 0xbfdf, 0xa4fd, 0x42c3, 0x4022, 0x4021, 0x1e68, 0xb23d, 0x43c1, 0x46fc, 0x4667, 0xba1c, 0x381b, 0x4189, 0x4186, 0x42da, 0xba72, 0x4230, 0x2b23, 0x40b4, 0x431c, 0x414c, 0xa472, 0x419e, 0xba03, 0x429b, 0x0c07, 0x1985, 0x412a, 0x3faa, 0x4689, 0xbf8f, 0x41e3, 0x427b, 0xb28d, 0xa220, 0xa8e1, 0x42ad, 0x4302, 0x41d7, 0xb224, 0x4240, 0x4063, 0x418d, 0xb2f7, 0x422d, 0x2633, 0x46f8, 0x13db, 0xbf84, 0x3ef1, 0x434c, 0x115c, 0xbf98, 0xba27, 0x1d98, 0xbf95, 0x4273, 0x1cdc, 0x428c, 0xb215, 0x3d32, 0xb219, 0xba4a, 0x4640, 0x4349, 0x4123, 0xba50, 0x447f, 0x3398, 0x4017, 0x325e, 0x1afd, 0xb20b, 0x1f41, 0xb200, 0x422e, 0x16a1, 0x43b4, 0x1f01, 0xbf58, 0x42f1, 0xb2c7, 0xb269, 0x41c9, 0x4445, 0x42fd, 0x4228, 0x1796, 0x1d3a, 0x40d8, 0xbf23, 0x44ac, 0x433c, 0x41d0, 0x280a, 0x4555, 0x428d, 0xbf48, 0xb219, 0x40e5, 0x4178, 0x4072, 0x438f, 0x4351, 0x45a5, 0x42ed, 0xa654, 0xb27b, 0xbfab, 0x1c9b, 0x42b1, 0xb03b, 0xba2d, 0x19b2, 0x4279, 0x429a, 0x4025, 0x41d4, 0xb228, 0x426f, 0xb2dd, 0xb26b, 0x4377, 0x18e1, 0x1a2d, 0x1e4f, 0x40b3, 0x42e2, 0x4249, 0x1b7c, 0xbaec, 0x40c5, 0x193f, 0x414c, 0xba00, 0x437b, 0x40d7, 0xba26, 0xbf4c, 0x43b5, 0x3b21, 0xab9e, 0x44ac, 0x18b3, 0x40c8, 0xbaed, 0x17c8, 0x3a04, 0x1751, 0x41c0, 0x41b2, 0x41c4, 0xb039, 0xbf01, 0x43de, 0x41f5, 0x1b0f, 0x4336, 0xba08, 0xb03b, 0x41d8, 0x366c, 0x0cf1, 0x4615, 0x412e, 0x4541, 0x030a, 0x28f4, 0x419c, 0x45e3, 0x3ee7, 0x3be2, 0xb031, 0x4301, 0x4327, 0x403a, 0xbad8, 0x425e, 0x42f5, 0xbfaa, 0x413e, 0x448c, 0xafe4, 0x40de, 0x417c, 0xbfb0, 0x22f0, 0xb2de, 0x4197, 0x421f, 0x410f, 0xb2d4, 0x40e4, 0x4117, 0x40ef, 0xbf54, 0x415e, 0xa3a1, 0x42bb, 0x40b0, 0x43b0, 0x4176, 0xb0b5, 0xb2e8, 0x3f9f, 0xba16, 0x4130, 0x412a, 0xbf5b, 0x1a0c, 0x42ba, 0x43e9, 0xa069, 0xb287, 0x41fd, 0xb0f8, 0x4275, 0x4443, 0x2643, 0x41ee, 0xba6b, 0xb25d, 0x32bc, 0xacb5, 0x45e2, 0x1049, 0xbf01, 0x4093, 0x4349, 0xaf09, 0xba72, 0xbadf, 0xbf00, 0xbf3c, 0xb0ff, 0x4190, 0xbf70, 0x4246, 0x1fb4, 0x439b, 0x0a5c, 0x265f, 0x409e, 0x1bae, 0xbae4, 0x20cd, 0x1f9b, 0x1ec0, 0x3cf8, 0xbae2, 0xb0d1, 0xaeaf, 0xbac7, 0x44f2, 0x416f, 0xbf1a, 0x415a, 0x2deb, 0x42f6, 0xa78e, 0x26f7, 0x429c, 0x30db, 0xb0d6, 0x4348, 0x4161, 0x409a, 0x432f, 0xbae7, 0x4105, 0x41b1, 0x4103, 0x1f6d, 0x12bd, 0x4362, 0x42d4, 0xbfa2, 0xbaf3, 0x4287, 0x1c0e, 0xb27a, 0x4628, 0x1861, 0x0bdf, 0x4139, 0x3f0a, 0xbfe0, 0x431b, 0x40bf, 0x403c, 0x24d3, 0x11e4, 0x1802, 0x2b5b, 0xa8ce, 0x15cd, 0x0a73, 0x1f3d, 0x1aa4, 0x41e8, 0xbf7c, 0xba36, 0x1a63, 0x1d63, 0x255d, 0x437d, 0x0969, 0xbac2, 0xba01, 0x4053, 0x40a7, 0x46ec, 0xb2f4, 0x1eab, 0xb243, 0x1a9f, 0x40a3, 0x4488, 0xb217, 0x434b, 0xb22e, 0x36e8, 0xb0a1, 0xb2b3, 0xbf25, 0x33da, 0x37e0, 0x43ad, 0x4418, 0x38ba, 0xa987, 0x4139, 0xbf2a, 0xa07a, 0x39a8, 0xb282, 0xba45, 0x43af, 0xba0b, 0x4275, 0x4273, 0xbad6, 0x435a, 0xb088, 0x402a, 0x40a6, 0xb029, 0x4304, 0xaa79, 0xb263, 0x4094, 0x08e2, 0x42cb, 0x42e7, 0x40dc, 0x1604, 0x43ca, 0x2b37, 0xbfc2, 0x4398, 0x300a, 0x41f9, 0xbf9d, 0x4485, 0xa8ec, 0x3bfb, 0xb23f, 0x0380, 0x3972, 0x40d9, 0x4019, 0x1c09, 0x1091, 0xbadf, 0x40c8, 0x43d0, 0x45aa, 0xbf90, 0xb224, 0x43fb, 0xb25c, 0xbf99, 0xbaf7, 0xb08e, 0x1b57, 0xb202, 0xb26b, 0x4160, 0xb03b, 0x1b50, 0x1f3c, 0x4107, 0x4027, 0x446f, 0x4183, 0x323e, 0xa035, 0x41ff, 0x415d, 0xbaff, 0x4634, 0x430a, 0x434f, 0x414d, 0xba3f, 0x1c05, 0xb0e7, 0xbf6b, 0x43e5, 0x3391, 0x1fed, 0x4341, 0x46f3, 0x4280, 0x4103, 0x401e, 0x1e8c, 0xba73, 0x4110, 0x404e, 0x2e56, 0x1b29, 0x1fc5, 0x4562, 0x418f, 0xbfa1, 0x414a, 0xb2fd, 0x42e9, 0xb2c3, 0x4170, 0xb242, 0x4371, 0x160c, 0xb0da, 0x3438, 0xba7e, 0xba02, 0x40ac, 0xbf92, 0x438b, 0x4009, 0x41b3, 0xbae1, 0xb26c, 0x060c, 0x4023, 0x4377, 0x35b8, 0x413d, 0x411a, 0x43b2, 0x0ad3, 0x42a6, 0xbf00, 0x4279, 0x1fe4, 0xb228, 0x42f0, 0x0ab4, 0x41bf, 0x41f0, 0x24fe, 0xbf67, 0x2bbb, 0x0f1e, 0x4101, 0x4402, 0xb2f6, 0x435e, 0xba00, 0xbfb0, 0x41bd, 0x4540, 0xbfb8, 0x1ba4, 0xb25c, 0x1478, 0x415b, 0xb01a, 0x4067, 0xa8f1, 0x429b, 0x1211, 0x42d8, 0xbf6b, 0x42b0, 0xba0f, 0x4175, 0x42a3, 0x3eba, 0x418b, 0x3558, 0x4044, 0x1c92, 0x40ee, 0x4345, 0x429a, 0x42f0, 0xbac9, 0x46b4, 0x429d, 0xbf0a, 0x4338, 0x418b, 0x43c0, 0x4224, 0xbf0f, 0xaa79, 0xba2e, 0x2cfb, 0xa092, 0x4003, 0x2293, 0xb2fb, 0x1a02, 0x4298, 0x4204, 0x41f3, 0x255f, 0x1884, 0xbfc0, 0x403a, 0x407b, 0xb044, 0xbf5d, 0x12b1, 0x3384, 0x4336, 0xb05d, 0x42be, 0x419e, 0xb2ad, 0xba4a, 0x4117, 0xba5b, 0xb292, 0xbafd, 0x428e, 0x43f5, 0x40f8, 0x4601, 0x406b, 0xae07, 0x42e5, 0x4053, 0x4068, 0xb0b4, 0x3961, 0x4340, 0x37c8, 0xbfcb, 0x423c, 0xa03f, 0x4342, 0x423c, 0xbf88, 0x0fdd, 0x4193, 0xb2d9, 0x43da, 0xbfa0, 0x1396, 0x40af, 0x4317, 0xa51a, 0x211a, 0xadb9, 0x433e, 0xba22, 0x41f9, 0x180e, 0x411c, 0xba61, 0x4001, 0x4268, 0x03fc, 0xbf00, 0xb281, 0xb23f, 0xbae2, 0xbf80, 0xbf65, 0x43e1, 0x394f, 0x35a0, 0x329b, 0x422f, 0x4164, 0x0d8f, 0x4237, 0x465a, 0x43b6, 0xbaf0, 0xbfe0, 0xbf11, 0x4267, 0xba2f, 0x41a6, 0xb210, 0x4221, 0x4319, 0x1482, 0x3205, 0xb256, 0x2dfd, 0xba01, 0x402a, 0xb2ca, 0x163f, 0x432d, 0x41fd, 0x4161, 0xb0b4, 0x42f8, 0x40c2, 0x1aa5, 0x4167, 0xb051, 0x0978, 0x4149, 0xbf1f, 0x4175, 0x43fa, 0xb252, 0x4140, 0x4215, 0xba55, 0xb2d2, 0x4210, 0x406a, 0xaf57, 0xacee, 0xa935, 0xba5d, 0x4259, 0x05bf, 0xb015, 0x43e6, 0x4192, 0xa8db, 0x44e2, 0x1588, 0x0d4d, 0x437f, 0x0618, 0xbfdb, 0x41e3, 0x4108, 0xa17e, 0x416f, 0x1b33, 0x42df, 0x438e, 0x41d0, 0xbfb1, 0x0d0f, 0xb28f, 0xba4b, 0x3c20, 0x42e3, 0x0f9b, 0x41af, 0x40fd, 0x415c, 0x40f5, 0x436d, 0xa5a7, 0xa82e, 0xba46, 0x42ac, 0xb204, 0x4067, 0x438d, 0x40e0, 0x09d3, 0xaaf7, 0xbf79, 0x4386, 0x1b23, 0x41ea, 0x4010, 0xbff0, 0x4491, 0xa083, 0xb227, 0xb2d3, 0x4196, 0x1c87, 0xb22a, 0xb09b, 0x01cd, 0x1c6f, 0xafc3, 0x43be, 0xa9d2, 0x42c6, 0x0ad5, 0x4341, 0x432b, 0xba5c, 0xb0bc, 0x4120, 0x412e, 0x4475, 0x4284, 0xb01b, 0xbfda, 0xa893, 0x1afa, 0xa310, 0x40f9, 0xb25d, 0x42b4, 0xb00e, 0xb06b, 0xb26e, 0x43eb, 0x4237, 0x40a9, 0x4369, 0x40cc, 0x4233, 0x40fc, 0x3d00, 0x43aa, 0xb2d7, 0x40bc, 0xbf00, 0xb296, 0xb223, 0x0c49, 0xaaff, 0x4180, 0x18bc, 0xbf70, 0xbf6c, 0x430a, 0xb208, 0x0bd5, 0x1bcd, 0x421c, 0xb289, 0xb250, 0xbf1e, 0x41b7, 0x44fa, 0xb0f3, 0x2760, 0x1c87, 0x189c, 0xb217, 0x41a3, 0xaf06, 0xbfb4, 0x422b, 0x2caf, 0x1b6f, 0x429c, 0x406b, 0x417d, 0x41c5, 0xa01c, 0xb258, 0x43ee, 0x1c6b, 0x4171, 0xb0ab, 0xa87d, 0x40e3, 0x1b3f, 0xba4a, 0xbf46, 0x3418, 0x19da, 0xa67e, 0x4223, 0x3401, 0x3b45, 0x2a8f, 0x45ab, 0xb249, 0xb2d7, 0x416d, 0xbf80, 0x1b06, 0xba61, 0x070c, 0xb2e8, 0x4394, 0x3fb6, 0x41b7, 0xbf56, 0x4087, 0x42fa, 0xb01d, 0x438c, 0x1fb3, 0xbae0, 0xb01a, 0xa6da, 0x4396, 0xb009, 0x3260, 0x4347, 0x4097, 0xb06f, 0x42b9, 0x2e15, 0xb033, 0x4253, 0x43a3, 0x0261, 0x1e9b, 0x4657, 0xb2fb, 0xb288, 0xbf9e, 0x42ed, 0x43d9, 0xba33, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3e63c940, 0xd2f40362, 0xbb77737e, 0x677685e4, 0x97ccf408, 0x3dfa0433, 0x43332c01, 0xa5fc83ef, 0x5b5a10e7, 0x93fd7fe6, 0xe271cf59, 0xac70a2fd, 0xc4be1954, 0x93de22cc, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0xfffff05f, 0x00000059, 0x00000000, 0x00000020, 0x00001000, 0xe271cf59, 0x49f6f315, 0x93de1f28, 0xe271cf59, 0x00000000, 0x00000000, 0x93de1f1c, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4010, 0x421c, 0x3d78, 0x4344, 0x43d7, 0x401a, 0x1ce0, 0x4051, 0xb2b6, 0x0a48, 0xbad9, 0x4206, 0x4163, 0x1efa, 0x402d, 0x1e69, 0x4221, 0xbfba, 0x4154, 0xb2b8, 0x19e2, 0xb243, 0x15ea, 0x406b, 0x46d3, 0x4546, 0x417a, 0x41b7, 0xba61, 0x41fd, 0x41d5, 0x45bd, 0xb0fa, 0x0951, 0x454f, 0x0377, 0x4262, 0xb097, 0xb04b, 0xb277, 0x37da, 0xbfcc, 0x15dc, 0x4385, 0x18c8, 0xb22d, 0x412b, 0xb029, 0xba75, 0x411d, 0x14c5, 0x456f, 0xbaf2, 0xba3d, 0x45f3, 0xb0da, 0xb087, 0x4253, 0x411a, 0xb2fa, 0x469d, 0x44cd, 0x0459, 0x4410, 0x1378, 0x1239, 0xbf2d, 0x423d, 0x070b, 0x4353, 0x43a9, 0xb015, 0x4301, 0xbae3, 0x0c4b, 0x46bd, 0x4013, 0x3765, 0x4147, 0xbf59, 0x1d79, 0x2918, 0xb014, 0xb223, 0x4202, 0x1ceb, 0x18bb, 0x4274, 0xaa53, 0x4201, 0xbacc, 0xa37f, 0xb0af, 0x411f, 0x41cf, 0x186b, 0x40cd, 0x14ec, 0x4018, 0x435d, 0x327d, 0x43eb, 0x4305, 0xbf64, 0x13ef, 0x40d6, 0x4095, 0x369f, 0x4065, 0x41b3, 0x1cc7, 0xbf8b, 0xb297, 0x432e, 0x4268, 0x4002, 0x430a, 0x4479, 0xb048, 0x421e, 0x1ca3, 0xa979, 0xb01d, 0x4459, 0x07f7, 0xb262, 0x1862, 0xb289, 0xb211, 0xbac9, 0x43cd, 0xbfdf, 0xb072, 0x421a, 0x412d, 0x2962, 0x46ec, 0xb09e, 0x1e20, 0x44c1, 0xbfd1, 0xb00c, 0x437b, 0x18ee, 0x41e2, 0xba0c, 0xba2c, 0x04b5, 0xb05b, 0x4596, 0xa87e, 0x422a, 0x40cc, 0x4002, 0x40a3, 0xb041, 0xbf8b, 0x40d4, 0xb0c0, 0x41b8, 0x45e3, 0x3460, 0xb24b, 0xb285, 0xbf04, 0x4225, 0x4064, 0x420a, 0xbf34, 0x43ed, 0x0cab, 0x4194, 0x0ffb, 0x41b8, 0x43e0, 0x4173, 0xbf9a, 0x3fab, 0x210e, 0xa820, 0x426a, 0x4395, 0x43ee, 0x3657, 0xbfc0, 0x430b, 0x438b, 0x1b6d, 0x1c7b, 0xb231, 0x42f2, 0x439e, 0x4389, 0x41b5, 0x4082, 0x1de1, 0x1ca6, 0xbfd0, 0x34cc, 0x1dda, 0xae7c, 0x4181, 0xbf2f, 0x410f, 0xbaf2, 0x40aa, 0x409e, 0x13c8, 0x4076, 0x1bd4, 0x4020, 0x4128, 0xba1f, 0xb24b, 0xa578, 0xaabf, 0x4049, 0xa063, 0x418b, 0xbafd, 0x407c, 0xb234, 0x4380, 0x4288, 0x43e2, 0xbf38, 0x422b, 0xbf80, 0x43b2, 0x43fc, 0x3c13, 0x4282, 0xab63, 0x41db, 0x298d, 0xba1e, 0x1f37, 0x41e6, 0x464d, 0xb031, 0x4380, 0x467c, 0x1b44, 0xbf07, 0x1891, 0x0bcc, 0x420c, 0x4330, 0x4038, 0x4155, 0x4162, 0x4421, 0x4071, 0xbafd, 0x1b7a, 0x42b8, 0x37f8, 0x46aa, 0x0159, 0xb28a, 0x43cf, 0x43ba, 0xb056, 0x43b4, 0xb067, 0xbf35, 0x46fa, 0x40e7, 0x45c4, 0x42dd, 0xbfa3, 0x1995, 0x407b, 0x3862, 0x43bd, 0x0793, 0xbaf8, 0x1f40, 0x40fd, 0x1a53, 0xbf7f, 0x384d, 0x261a, 0x416d, 0x4200, 0x204f, 0x40e1, 0xb0eb, 0x0a31, 0x43ac, 0xb266, 0x1b0e, 0x4127, 0x27e7, 0x4570, 0x2786, 0x40e4, 0xb23c, 0x44dd, 0x4125, 0xbf6a, 0x402a, 0x40f1, 0x4161, 0x45f1, 0x18cf, 0x1e84, 0xba1e, 0x42dd, 0x406e, 0x09d5, 0xbac2, 0xaa6d, 0x4128, 0x226e, 0xb26e, 0xaf3c, 0x0e65, 0xbf75, 0x45d1, 0x18a2, 0x433e, 0x2402, 0x41e3, 0x43cb, 0x427f, 0xbac3, 0x4180, 0x1df8, 0x41c7, 0x1bf7, 0x439f, 0xab0c, 0x4463, 0xbf4a, 0x43d8, 0x44ba, 0x415a, 0x40d1, 0x4167, 0x4026, 0x0966, 0x423e, 0x1de0, 0x41f5, 0xb07c, 0x4628, 0x4288, 0x460a, 0x1376, 0xbf94, 0x4364, 0x41f7, 0x43de, 0xba74, 0x4027, 0x4453, 0x1dd1, 0x1a65, 0xa7ce, 0xb2c5, 0x4658, 0xb29b, 0xb2b8, 0x18be, 0xb2bc, 0xb022, 0x402c, 0xba7a, 0x0b2a, 0x40d6, 0xba03, 0xbf27, 0x4613, 0x1739, 0x1c8d, 0xb258, 0x1f23, 0xba74, 0x45e6, 0x1b3c, 0x4090, 0x0567, 0xbf4c, 0x1e05, 0xba36, 0xb2a9, 0xbfa5, 0xbae7, 0xafde, 0xba56, 0xb2c6, 0x08f6, 0xb0e6, 0x42dd, 0xbf74, 0xb0a8, 0x2a22, 0x1c73, 0x0bf3, 0xa2ab, 0x4359, 0x07b8, 0x40ea, 0x4207, 0xba17, 0x3f61, 0x1f21, 0x4040, 0x41ed, 0xbae2, 0xba23, 0x42d9, 0xb2e5, 0xa1b9, 0xb0a8, 0xa31a, 0xbf02, 0x1e10, 0xba2a, 0xb20d, 0x4261, 0x4013, 0x1b84, 0x40ce, 0xb218, 0xabc9, 0x1aa3, 0xba7f, 0x42e1, 0x4322, 0x41a1, 0x2a78, 0x4245, 0x46f0, 0x4189, 0x4164, 0xb080, 0x4003, 0x145d, 0xbf67, 0xba42, 0xa206, 0x1c5f, 0x4353, 0x441d, 0xb220, 0xbf3f, 0xba17, 0x416a, 0xb04e, 0x40e8, 0x40f1, 0x41ca, 0xbfb5, 0xba7b, 0xb2e2, 0x1300, 0xb0d2, 0xb03d, 0x435e, 0xb297, 0x1980, 0x4074, 0xbfcc, 0xa9ef, 0x0790, 0x464b, 0x3062, 0x4238, 0x3c74, 0x1fad, 0x34e8, 0x467f, 0xaa62, 0xa96d, 0xbf13, 0xbad9, 0x1c8e, 0xb21f, 0x4236, 0x4249, 0x40d1, 0x4371, 0x4130, 0x4066, 0x274a, 0x4073, 0xb2ee, 0x2f2d, 0x1a18, 0x26db, 0xb206, 0xb26b, 0x44d0, 0xb21e, 0x0cc4, 0xba08, 0x401b, 0xb0a5, 0xbf3b, 0x4362, 0xbf70, 0x4205, 0x432f, 0xba36, 0x4015, 0xb264, 0x4021, 0xb2e6, 0x40ff, 0x4323, 0xbadb, 0x22db, 0x0a58, 0xbf90, 0xb2a7, 0x378b, 0xbfc0, 0x4334, 0x41ff, 0x41a6, 0xab67, 0x4149, 0x42bf, 0x4295, 0xbf00, 0xbf82, 0xa510, 0xb2ac, 0x429a, 0x1e48, 0x2779, 0x4316, 0x1d29, 0x41b7, 0x408a, 0x4213, 0xba3c, 0x1423, 0xbfd2, 0x4647, 0x41ed, 0x219f, 0xbf8f, 0xba7d, 0x1ada, 0x1e7f, 0x3e40, 0x4325, 0x4166, 0x4206, 0x4335, 0x33d2, 0xb22b, 0x1f30, 0x42da, 0x402e, 0x43c6, 0xbfd6, 0x4312, 0xb2d3, 0x41c1, 0xb22a, 0x438d, 0x02af, 0xbf4a, 0x41d6, 0x0ac5, 0x40eb, 0xb00d, 0x41c8, 0x09ce, 0xb276, 0xbfd3, 0x24d8, 0x44b9, 0x4581, 0x4156, 0x071a, 0xba20, 0xb2ee, 0x41cd, 0xa9ca, 0xbf80, 0x2ad3, 0x4242, 0x4040, 0x1ba8, 0xb2c0, 0x41df, 0xbfab, 0xbad7, 0x1efd, 0x418e, 0xba0d, 0x2484, 0x41d0, 0x1b17, 0x402f, 0xb2c3, 0xb062, 0x02be, 0xbfbe, 0x0c82, 0x41c5, 0xba53, 0xa095, 0xa516, 0x1939, 0x43bf, 0x1e52, 0x408a, 0x2dff, 0x4001, 0x4146, 0x439e, 0x4397, 0xbf00, 0x40db, 0xba6a, 0x4445, 0x429d, 0xb2f1, 0x1d3d, 0x43a0, 0xbf74, 0x4246, 0xb096, 0xa063, 0xb269, 0x3cbc, 0x4552, 0xb221, 0xba52, 0xbadc, 0x4211, 0x3825, 0xb2db, 0xbfa0, 0xba0c, 0x3ad0, 0xb261, 0x4170, 0x430c, 0x41ec, 0x1dd5, 0x0d4d, 0x4140, 0x1a8b, 0x1904, 0xbfa5, 0x41f9, 0xba0c, 0x436d, 0x1d2a, 0x3b51, 0xba39, 0x4014, 0x40f5, 0x40a8, 0x415d, 0x2e7c, 0x3625, 0xb283, 0x421b, 0x2b20, 0x205e, 0x2a8e, 0x43be, 0xba39, 0xbf33, 0x4173, 0x2675, 0x10d5, 0x284b, 0x4227, 0xaaa0, 0x1bcb, 0x1c0e, 0x2a8d, 0x43fa, 0x417e, 0x4379, 0x4096, 0x1d43, 0x409a, 0x42b3, 0xba76, 0x0be7, 0x4316, 0xaed0, 0xbf1d, 0x407c, 0x42da, 0x18c3, 0x1f4d, 0x420e, 0x2a5f, 0xbfa9, 0x40a8, 0x1eb0, 0x4273, 0x40ea, 0x43d0, 0x07e3, 0xb210, 0xba4c, 0x41a8, 0xb294, 0xbf5d, 0x1a32, 0xbafd, 0x4031, 0xa0dc, 0xb2d0, 0x40bb, 0x431e, 0xbf73, 0x4186, 0x4054, 0x36b8, 0x40f2, 0x18fa, 0x1dc6, 0x431a, 0x4298, 0x43a4, 0x25c7, 0x1976, 0x1aa5, 0x41e8, 0x43cb, 0xbfb0, 0xbf58, 0x4328, 0xbf02, 0x43ad, 0x4118, 0xb0b3, 0x40cc, 0x24f8, 0x40e8, 0x4127, 0xa3a1, 0x30df, 0xb238, 0x3b0a, 0xb274, 0x4210, 0xb296, 0x4131, 0xb2dc, 0x37ca, 0x4243, 0x405c, 0x45ab, 0xbf2b, 0x270b, 0xbae4, 0x1bcd, 0x4396, 0x438b, 0x1ed0, 0xbf80, 0x2b6e, 0x436a, 0xb2ea, 0x437f, 0x4606, 0x1f3f, 0x4269, 0x0f94, 0xb223, 0x0628, 0xbf98, 0xbf60, 0xb234, 0xb23d, 0x43be, 0x4137, 0x40fe, 0x41f4, 0x40f5, 0xa3f3, 0xb2ba, 0xba6a, 0x4126, 0x4364, 0xb2f1, 0x42e2, 0xba6b, 0xb0b8, 0x199e, 0xb22a, 0x41e9, 0x461f, 0x431f, 0x44cd, 0x1bdb, 0x1b8e, 0x1deb, 0xbf3c, 0x253b, 0x4355, 0x41ad, 0x4340, 0x40ab, 0x1952, 0x40d0, 0x1cee, 0x43cd, 0xbad6, 0x2461, 0xbf28, 0x28e7, 0xbad3, 0x4420, 0xb29b, 0x436d, 0xb01c, 0xa3bc, 0x4173, 0x4194, 0xb2f1, 0x4298, 0xb0d3, 0xbfb0, 0xb200, 0x415d, 0x406e, 0xba4e, 0x4182, 0x41a4, 0x4207, 0x40dc, 0xbaec, 0x193c, 0x415a, 0x4317, 0xbf83, 0x1e0b, 0x3d29, 0x40a1, 0x4333, 0xb046, 0xaf27, 0x1988, 0xb28a, 0xb204, 0x16d6, 0xac6b, 0x425b, 0x1f0d, 0x4251, 0x408e, 0x42de, 0x464c, 0xbfae, 0x40e7, 0x44b8, 0x43fc, 0x4065, 0x4333, 0x4479, 0x0559, 0xbaeb, 0xb252, 0x41f3, 0x4380, 0xb230, 0x46c0, 0x44a0, 0xbf8e, 0xb023, 0x4288, 0x4017, 0x406b, 0x4288, 0x4398, 0x18cc, 0xabdd, 0x429a, 0x42ab, 0xbf95, 0x43f2, 0x4057, 0x4442, 0x40ee, 0x4111, 0x4358, 0xa518, 0x34fa, 0x4180, 0x3ecd, 0x4223, 0xb265, 0x42a4, 0x1f7c, 0xb296, 0x1d3c, 0x430f, 0x442d, 0x4307, 0x437a, 0xbf27, 0x1d82, 0x391c, 0xb2a8, 0xbae1, 0x40a0, 0x41b3, 0x40e2, 0xb27e, 0x43a4, 0xafdf, 0x43ad, 0xb024, 0x40e0, 0x4048, 0x3c42, 0x1d9d, 0x1954, 0x0cd9, 0xbf4b, 0x4236, 0xa2ef, 0x424e, 0xbac2, 0xba67, 0x4103, 0x43eb, 0x1d60, 0x459e, 0x4059, 0x41b5, 0xbf83, 0x4310, 0xba42, 0x3684, 0xb299, 0x43ca, 0x1f66, 0x43e1, 0x1358, 0xb08d, 0x41ee, 0x1b3a, 0x4386, 0x40ad, 0x43fd, 0x4653, 0xb0bc, 0xb2ac, 0x405b, 0x037e, 0x418c, 0x1887, 0x42bd, 0x05ee, 0x46ed, 0x4238, 0x4062, 0x43f9, 0xbfad, 0x461f, 0xb0fa, 0x3e21, 0x411c, 0x403b, 0x2342, 0x3259, 0x076d, 0xbf81, 0x4009, 0x4152, 0xba08, 0x1811, 0x4312, 0x32f2, 0x1b6f, 0x408c, 0x43d8, 0x437e, 0x40c1, 0x40a1, 0x40cd, 0x43af, 0x2042, 0x4342, 0x4082, 0x4029, 0xa300, 0xbf49, 0xb076, 0x421e, 0x45a2, 0x185f, 0xba07, 0x4283, 0x4306, 0x3c84, 0x454f, 0x42d5, 0x4071, 0xae63, 0xb2b0, 0xab50, 0x21b6, 0x4024, 0x4485, 0xb292, 0xb266, 0x424d, 0x1559, 0x0623, 0x42e7, 0xb2e3, 0x4094, 0xbf38, 0x4378, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xfbee3e06, 0xf7bc3994, 0x7ccee582, 0x65c6dfd8, 0x7d765d73, 0x2669bec2, 0x4d20d1a8, 0x414bdcb0, 0x53f02dc6, 0xad07dc25, 0xbd4dd249, 0x070bd4e7, 0x1404f4cb, 0x80985f65, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0x000061d2, 0xfffffdef, 0x00000000, 0x0000007c, 0xffffff7c, 0xffffff4a, 0x0000007c, 0x42000000, 0x0b273264, 0x00a885eb, 0x0b273265, 0xbd4dd249, 0x00000322, 0xbdf6c218, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xba5b, 0x07d6, 0x17ae, 0x42a8, 0x465e, 0x4335, 0x121d, 0xa28d, 0x4605, 0x4319, 0x4115, 0x1eed, 0x40b7, 0x43ba, 0x4680, 0x4079, 0x4604, 0xb239, 0x41f3, 0x40fa, 0xba5e, 0x4247, 0xba6d, 0x42ce, 0xbfb7, 0xba11, 0x4025, 0xbfa0, 0x4034, 0xb096, 0x1948, 0x4194, 0x42a7, 0x43b5, 0x416e, 0x40a9, 0x42db, 0x4601, 0xbf38, 0xb2e6, 0x426f, 0x444a, 0x40ee, 0x44ed, 0x44d1, 0x4000, 0xb215, 0x30a0, 0xba5a, 0xb261, 0x41bb, 0xb00b, 0xbf6c, 0xbae1, 0x0ef2, 0x431c, 0x00d4, 0x446c, 0x4357, 0x42d7, 0x2fd0, 0xb224, 0xb099, 0x424c, 0x1b9f, 0x01d3, 0x40e7, 0x3e10, 0x4648, 0xbfcb, 0x0411, 0xba0b, 0x40bb, 0xbac8, 0x24c9, 0x42cf, 0xba3d, 0x01fa, 0x1a3f, 0x03a8, 0x28e0, 0x42e1, 0x40b4, 0xb24b, 0x1cd5, 0xb2c8, 0x43ef, 0x2f59, 0x18d0, 0xa4b2, 0xba71, 0x2bb4, 0xb088, 0x21d1, 0xb2db, 0xb2b4, 0xbfb7, 0x1d84, 0x1c34, 0x438f, 0x40d3, 0x4542, 0xaf8e, 0xbad0, 0x4106, 0x28c9, 0x4102, 0x40e9, 0x42d2, 0xbf2c, 0xb203, 0xb295, 0x4262, 0x42ab, 0xbfca, 0x4477, 0x43df, 0x425f, 0xb2b8, 0x425d, 0xb2f7, 0x1d27, 0x40e0, 0x41d8, 0x40e2, 0x3bf3, 0x40f4, 0x416c, 0x17a2, 0xa33f, 0xb254, 0xba60, 0xb056, 0xbfd7, 0x4208, 0xb025, 0x4590, 0x18f9, 0xb20c, 0xba78, 0xb059, 0xba72, 0x2cea, 0x0a1e, 0xbf00, 0xaaee, 0x4651, 0x4315, 0xbaf9, 0x42c3, 0x4176, 0xa362, 0x0638, 0x2acd, 0x1a26, 0xb251, 0x40c7, 0x4469, 0x410c, 0xbfd7, 0x43a1, 0x419c, 0x4093, 0x43d5, 0x3b5f, 0x33cf, 0xa83a, 0x4160, 0xb222, 0x41bf, 0xb2c9, 0x4324, 0x1fd8, 0x1cd4, 0xb0ab, 0x427b, 0xbaf5, 0x407f, 0xbf63, 0x19af, 0xa6ef, 0x438b, 0x43cb, 0xbfc0, 0x4367, 0x428f, 0x4088, 0x4219, 0xb273, 0x0619, 0x4385, 0x420f, 0x46e3, 0x4466, 0xb019, 0x4366, 0x402f, 0xbf36, 0xb2b2, 0x2705, 0x1202, 0x0143, 0xbfa7, 0xbfc0, 0xad30, 0x42ee, 0x4279, 0x4273, 0x1d83, 0x432e, 0xb2fd, 0x42af, 0xbfd7, 0x4304, 0xba66, 0x1f02, 0x04b7, 0x4547, 0x0537, 0x4478, 0x4396, 0x421a, 0xbf64, 0x421e, 0x2be3, 0x41cd, 0x43fd, 0x435c, 0x4205, 0xa4ee, 0x439a, 0x4270, 0x42cd, 0x435b, 0x4057, 0x2f2c, 0xb22c, 0x27ee, 0x43df, 0xb2bd, 0x40ce, 0x17f1, 0xbf58, 0x4179, 0xba3d, 0x4036, 0x4150, 0xa831, 0xba54, 0x4278, 0x4270, 0x1454, 0x42e3, 0x4099, 0xb253, 0x41b7, 0x1c6d, 0x4148, 0x4158, 0xb096, 0x4495, 0xb07e, 0xbaca, 0xb2b8, 0x1b78, 0xbf53, 0xb2a8, 0xb2a0, 0x3743, 0xba20, 0x41cc, 0x1435, 0x438c, 0x4074, 0xb09f, 0xb291, 0x1fb0, 0x4040, 0xbafc, 0x1ebc, 0x14da, 0xbfa0, 0x4235, 0xb22c, 0x41b4, 0x3a86, 0x433a, 0xbfa8, 0xb047, 0x4639, 0xbadf, 0x4225, 0x43f8, 0x0dab, 0xa751, 0xbff0, 0x4347, 0xbfb6, 0xba06, 0xbadf, 0xb084, 0x06c0, 0x2b46, 0x42b3, 0x014f, 0x0b86, 0xba4f, 0x44ab, 0xb2c4, 0x154e, 0xb24e, 0xbaf5, 0xa94c, 0x30d0, 0x435b, 0xbaeb, 0xbfa3, 0x41fc, 0xba0e, 0x4180, 0xba0b, 0x1810, 0x0d4e, 0xba04, 0x0f96, 0xb0db, 0x4117, 0x4335, 0x4314, 0x3157, 0xb26d, 0x4554, 0xb23a, 0xb2ad, 0x3ee7, 0x3bcf, 0xba46, 0x42cc, 0x415a, 0x421b, 0x43bf, 0xbf80, 0x1b5d, 0xba10, 0x099c, 0x421d, 0xbf32, 0x20d0, 0xb21d, 0xacf5, 0xba23, 0x4120, 0x4102, 0xbaf4, 0xbfa8, 0x2f4b, 0x1913, 0x2795, 0x43fe, 0x4472, 0x4419, 0x4224, 0x423b, 0xba6a, 0x33f4, 0x466a, 0x0602, 0x42e6, 0x45c5, 0x0a3f, 0xb279, 0xb294, 0x42cb, 0x4013, 0xb2f7, 0x41ce, 0x1fbe, 0x4229, 0x41eb, 0xbf5d, 0x0f87, 0x4120, 0x4233, 0x410a, 0x3a7d, 0x1365, 0x4633, 0x42f0, 0xba0c, 0x415a, 0x438f, 0xb062, 0x42f1, 0x4205, 0xba13, 0xbfa5, 0xb221, 0xb2eb, 0x436a, 0xb2cf, 0xb241, 0x4120, 0x4233, 0x446e, 0x40c9, 0x3943, 0xb2c5, 0x0b33, 0x13f7, 0xb236, 0xa84f, 0x415a, 0xb09b, 0x43e2, 0x4105, 0xbfc0, 0x438e, 0x1901, 0x4146, 0xbf52, 0xa619, 0xba67, 0x4354, 0x3f22, 0x430e, 0x44d5, 0x1fbf, 0x413b, 0xa872, 0x4304, 0xba65, 0xb27e, 0xb20f, 0x41a5, 0xbade, 0x4157, 0xaeb4, 0x423d, 0x42fa, 0xbfc1, 0x4416, 0x434d, 0x44ea, 0xb239, 0x4594, 0x0037, 0xbf1b, 0x1864, 0x42d5, 0xba2f, 0x017e, 0x4065, 0xb0bc, 0x13a1, 0xbfa0, 0x43b2, 0x41a4, 0xb269, 0x46d1, 0xb27b, 0x410e, 0xb24b, 0xbf06, 0xb061, 0x0c0c, 0xb294, 0x4010, 0x1e48, 0x435f, 0xa4ab, 0xb2db, 0x248f, 0xa37f, 0x403a, 0xb0df, 0x4023, 0x42ba, 0x42dd, 0x1e2d, 0x4299, 0x41a2, 0x2d1b, 0x2b1b, 0x41f5, 0x194d, 0x422c, 0xb23b, 0x427d, 0xb2a0, 0xbf1a, 0xab55, 0x43ca, 0xa750, 0xa102, 0xbaed, 0xbff0, 0xba5f, 0x4274, 0xb28e, 0xa5be, 0x40e5, 0xb20e, 0x4173, 0x2778, 0xb26b, 0xa8ff, 0x18f0, 0x4036, 0xbf65, 0x1b9b, 0x46ec, 0xb0bf, 0x4017, 0x4126, 0x2f2d, 0x3504, 0xb05a, 0x45e5, 0x4053, 0x41f7, 0x43d1, 0x417c, 0x421c, 0xbfe2, 0x4377, 0x1723, 0xbff0, 0x46c2, 0xa232, 0xaec0, 0xba6c, 0xb251, 0xb2f6, 0x4060, 0x404f, 0x1ce5, 0xbf88, 0x4655, 0x1a5a, 0x403d, 0xba12, 0x19c7, 0xba50, 0x4169, 0x4106, 0x4250, 0xb291, 0x42c7, 0x4347, 0x27bd, 0x2c35, 0x0626, 0x1ec8, 0x0280, 0x400e, 0x2bf8, 0xb263, 0xb2af, 0xb01d, 0xba2b, 0x1cf0, 0xbfdf, 0x431f, 0x4604, 0xb295, 0xba0c, 0xb218, 0x4325, 0x213d, 0x415a, 0xb0aa, 0x410d, 0x41d0, 0x18ec, 0x445d, 0x4031, 0x1a49, 0xbaf3, 0x4089, 0x4001, 0x42c8, 0x40d9, 0x1fd4, 0xbf3f, 0x41ab, 0xa638, 0x42fa, 0x1c8f, 0xa077, 0x1b97, 0xb204, 0x4210, 0x42f2, 0x1b51, 0xb0ed, 0x1bc5, 0xb25f, 0xba2d, 0x335d, 0x3031, 0x15d4, 0x403d, 0xa32c, 0xbadb, 0x41be, 0xbf33, 0xba5e, 0xba5f, 0x45ba, 0xb01a, 0x4214, 0xbad1, 0xbf82, 0x429c, 0x171a, 0x1728, 0xbfb2, 0x413e, 0xac11, 0x42bc, 0x4149, 0x41b5, 0x433a, 0xb26f, 0x1d40, 0x461b, 0x4046, 0xb243, 0xb2fc, 0x4259, 0x1b06, 0x4253, 0x4179, 0x4369, 0xba48, 0x403d, 0xb2b1, 0xb06f, 0xb21c, 0x4122, 0x2dc7, 0xbf9d, 0x43c5, 0xb2cd, 0xa4b1, 0x417e, 0xb232, 0x42e0, 0xbfdc, 0xb073, 0xb2a0, 0x1caa, 0xb2d4, 0x4037, 0x16f9, 0x05b2, 0xbf00, 0x43f5, 0x4135, 0x43d8, 0x4115, 0x42e9, 0xb248, 0x4037, 0xb274, 0xb249, 0x4284, 0x3542, 0x3c24, 0xa2e5, 0x1a2f, 0x40d1, 0xb2e3, 0x41da, 0x4255, 0xbf0f, 0x2d1a, 0x46d8, 0x422c, 0x28a3, 0x1c9e, 0x1f60, 0xb0a9, 0xb08f, 0x4056, 0x19b4, 0xb20b, 0xba7b, 0x4308, 0x40de, 0xb204, 0x4684, 0x4179, 0x0f4b, 0xb2e6, 0x45f0, 0x43f6, 0x43f6, 0x4181, 0xb2ab, 0xbf82, 0xb242, 0x2355, 0xb227, 0x1e77, 0x403d, 0x4381, 0x404c, 0x427e, 0x4220, 0xbfd0, 0x3630, 0xb282, 0xbf75, 0x1fe1, 0x1f1f, 0x288b, 0x40f3, 0x46f9, 0x39bb, 0x4328, 0x407f, 0x4245, 0x434a, 0xae95, 0x41c9, 0xba43, 0x09d6, 0x4179, 0xa589, 0x1d18, 0x1cdd, 0x42dc, 0xbf02, 0x35d5, 0x43c8, 0x4270, 0x403c, 0xba6d, 0x42ea, 0xbf65, 0x1ade, 0x2288, 0x4660, 0x437f, 0x4281, 0xba01, 0xb2e8, 0x4248, 0x46d1, 0xba30, 0x41b0, 0xb21e, 0x4017, 0x024f, 0x4305, 0x40cb, 0xb29c, 0x40d0, 0x0b3f, 0x0661, 0xb2fc, 0x4083, 0x196d, 0xb2aa, 0x3149, 0xbfc9, 0x41ae, 0x40ea, 0x4126, 0x462a, 0x42e4, 0x434f, 0x4242, 0xb2e1, 0x43de, 0x43e2, 0x41cb, 0x4112, 0xba58, 0x42c6, 0x426d, 0xb082, 0x43e7, 0x419e, 0x43ad, 0x4576, 0x42a1, 0x40a6, 0xbf54, 0x40a9, 0xbac0, 0xb254, 0x1bc9, 0x3ef4, 0xba5a, 0xa96a, 0x40a2, 0x4688, 0xb2a4, 0xba68, 0xb21e, 0xb2a2, 0xbf38, 0x0405, 0xb219, 0x2f28, 0x40fb, 0xb23c, 0xb29e, 0x4035, 0xb0cc, 0x4653, 0x4006, 0x460b, 0xbf3c, 0x427b, 0x4182, 0x4274, 0xbff0, 0x4252, 0x1272, 0xbfa4, 0xae65, 0x0ca1, 0x14f9, 0x00b0, 0x40b8, 0xba23, 0x4349, 0xbfa3, 0x4461, 0x063b, 0xb229, 0x41eb, 0xb224, 0x4124, 0xb0cc, 0x4342, 0x3167, 0x42c9, 0x1d8e, 0x4626, 0x3374, 0x4167, 0x4060, 0x45dd, 0x1ad6, 0x3446, 0x4237, 0x442f, 0x4214, 0x1d7c, 0x46c5, 0x42c7, 0x4491, 0x3e38, 0x1d56, 0x42cc, 0xaec2, 0xbf8e, 0x4090, 0xba77, 0x45d4, 0x40e8, 0x190e, 0x1b87, 0x0e51, 0xba73, 0x0806, 0xb206, 0xbf46, 0x4326, 0x111b, 0xba4a, 0x1b79, 0xa71d, 0x42a8, 0x4294, 0x423c, 0x4667, 0xba77, 0x4110, 0x43a9, 0x1d4d, 0x42c5, 0xb2f7, 0xb216, 0x40d9, 0xb06f, 0x43ba, 0x41b9, 0x467f, 0xa135, 0xbaee, 0x40e7, 0xbfc6, 0xb241, 0xa4a6, 0x423b, 0x408e, 0x364b, 0xa098, 0x4005, 0xb275, 0xb0f2, 0xba79, 0xbfa0, 0x4387, 0x41d9, 0x0761, 0xb251, 0x418c, 0xba33, 0xbf51, 0x4007, 0xb256, 0x44c3, 0xb212, 0x434f, 0x40b4, 0x0edf, 0x4546, 0x4343, 0x45b3, 0xb29c, 0x40fe, 0x341e, 0x1af1, 0x42dc, 0x1c07, 0xba79, 0x08bb, 0xbfc7, 0x16a1, 0x4655, 0x1fe7, 0x4235, 0xbf3d, 0x338e, 0x43a2, 0x36ad, 0x2e82, 0xa991, 0x423e, 0xba76, 0xba17, 0x1a36, 0xbad3, 0x42e4, 0x41d8, 0xb206, 0x41fd, 0x3bb8, 0x42a0, 0xb2ec, 0xbacd, 0x43c3, 0x1bde, 0xba65, 0x4188, 0x41a4, 0x40fb, 0x4225, 0x25ce, 0xbf97, 0xb22b, 0x19e4, 0x43cc, 0x4101, 0x27f0, 0x1f7e, 0x1d8d, 0xb23d, 0x4280, 0x1d99, 0x41ca, 0x4170, 0x440d, 0xbae8, 0x4114, 0x0519, 0x41db, 0x214d, 0x4255, 0xb25f, 0x1b50, 0x14bd, 0xba46, 0xbf0f, 0x43b1, 0x20c0, 0xa4da, 0xb273, 0xbf65, 0x0636, 0x43e3, 0xb006, 0x410e, 0x161c, 0xba02, 0x4471, 0x2f5a, 0x40e6, 0xbf55, 0x4030, 0xba21, 0x412c, 0xb2a7, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd10f75bd, 0x744765d0, 0x4700bcdf, 0x41a4f9ee, 0x0164f011, 0x5d046666, 0xcc6ba3ee, 0xcaed7f40, 0xc754ff08, 0xa741fc6a, 0x1e4ce65a, 0x41d5f9d4, 0xf0c8c8a5, 0x9b837a0d, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x00000000, 0x0000004d, 0x00000000, 0x03380000, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x5553dda0, 0xd10f75bd, 0xd10f75bd, 0xf0c8c8a5, 0xffffff86, 0x5553ddac, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xa199, 0x43c5, 0x4111, 0x1dd7, 0x4284, 0x4103, 0x41a4, 0xbf24, 0x0c40, 0x44e5, 0x448a, 0x1c19, 0x4275, 0x1e0c, 0xa7fd, 0x1c09, 0x009a, 0x1ee8, 0x459c, 0x43d0, 0x40b2, 0x426f, 0x402e, 0x4421, 0xbae9, 0x2640, 0x402a, 0x1fcd, 0x2ff9, 0x3926, 0xbf1b, 0xb281, 0xaacb, 0x45b0, 0x1c61, 0xba01, 0xb22d, 0x277d, 0x4042, 0x4374, 0x13b1, 0x146d, 0x410f, 0xb09b, 0x179d, 0x421b, 0x1ae6, 0x4085, 0x2920, 0x425a, 0x43df, 0xb244, 0x4080, 0x42dd, 0x4178, 0x43a1, 0x4057, 0xa843, 0xb2ea, 0x4195, 0xbf3f, 0x4058, 0x4042, 0xa1e7, 0x4263, 0x4060, 0x45ea, 0xba42, 0x40c5, 0xbf63, 0x0953, 0x4287, 0x11ec, 0xba1b, 0x2900, 0x1d63, 0x425b, 0xb209, 0x427d, 0x2ffd, 0x40a6, 0xba7b, 0xbfa1, 0xb227, 0x1390, 0xb2ae, 0x413f, 0xb230, 0x1ea6, 0xb063, 0x44d9, 0x43ed, 0x38e6, 0xb266, 0x11c6, 0x4353, 0x4395, 0x423d, 0x4038, 0xb0dd, 0x418d, 0x405b, 0x4165, 0xbf39, 0x1373, 0x4200, 0x458b, 0x42d4, 0x4307, 0x1f3f, 0xbf95, 0x405d, 0x43cd, 0xbaf2, 0xa1f9, 0x40f1, 0x40cf, 0x40ec, 0xb203, 0xbfcc, 0x420f, 0x1ebf, 0x2b1b, 0x4186, 0x1e1f, 0x464a, 0xba6a, 0x423d, 0x4453, 0x1f67, 0x434e, 0x27b3, 0x15d6, 0xb2d8, 0xbf53, 0xba22, 0x413b, 0x187f, 0x42fd, 0xa8c3, 0xba09, 0x41fd, 0x4442, 0x420c, 0xb296, 0xbac5, 0x4215, 0x40f2, 0x4082, 0x1a04, 0x11e2, 0x1cbf, 0x43d9, 0xb216, 0x4026, 0x3608, 0x43ea, 0x4022, 0x4092, 0x0e1f, 0x429b, 0xb2e8, 0x42df, 0xbfad, 0x349f, 0xb2ff, 0x4212, 0x4168, 0xbad1, 0x390c, 0x41e0, 0xa34a, 0x40cf, 0x419f, 0x40db, 0x2fbd, 0xb0a9, 0x4329, 0x4351, 0x4129, 0x4104, 0x4268, 0xb2c4, 0xbf13, 0x1afa, 0x2de8, 0xb0e4, 0x4191, 0xb234, 0x4041, 0xb0b4, 0xb05c, 0xba7f, 0x2b96, 0xbfc6, 0xba54, 0x434b, 0x447f, 0xbfc2, 0x4113, 0xb2ea, 0xae3d, 0x417a, 0x42f8, 0x1884, 0x1f4a, 0xba51, 0xa2aa, 0xbf15, 0x4386, 0xba75, 0x40e8, 0x1f84, 0x4202, 0x42d7, 0x19e7, 0xbf80, 0x4168, 0x45e6, 0xbff0, 0x4152, 0xbf0c, 0xabb2, 0xa8e2, 0x42f0, 0x43d5, 0x405c, 0x4229, 0x42b1, 0xb0d1, 0xbf98, 0xba2d, 0x43c0, 0x024c, 0x3d62, 0xb22e, 0x400d, 0xba54, 0x4002, 0x2696, 0x1c64, 0x1d10, 0xb2f5, 0x40b8, 0xb04f, 0x24dd, 0x43fb, 0x4042, 0x182b, 0xbafd, 0x46d2, 0xbf78, 0x4317, 0x2901, 0xadeb, 0xa020, 0x43de, 0x352d, 0x4025, 0x4067, 0x4184, 0x42af, 0x4197, 0x410b, 0x4240, 0x42ef, 0xbfcc, 0x3013, 0x454d, 0xbf58, 0x4317, 0x0df1, 0x1877, 0x422f, 0xb01b, 0xb083, 0x402d, 0xb24c, 0x3837, 0xb0ad, 0x1a40, 0x0cad, 0xa457, 0x1c71, 0xb0c5, 0x40c7, 0xbf24, 0x4115, 0x4613, 0x41da, 0x1e59, 0x1099, 0xa19d, 0x25c9, 0xba1f, 0x0da3, 0xa9c4, 0x4263, 0x4128, 0xaae7, 0xb051, 0x427c, 0xba0b, 0x41fc, 0xb234, 0x1cec, 0x40be, 0xb221, 0xba4c, 0x1fcd, 0xb21e, 0xbf4c, 0xb0fd, 0x4172, 0x1d00, 0x1b78, 0x424e, 0xbf1e, 0x1f40, 0x0324, 0x4271, 0x41f0, 0xa81e, 0x4692, 0xb21e, 0x4114, 0xbf39, 0x46aa, 0x19a7, 0x2315, 0xbadf, 0xab7d, 0x1edf, 0xba3a, 0x1a7a, 0x1ede, 0xba56, 0xb074, 0xb207, 0x460f, 0xb20f, 0xba15, 0x40de, 0x40d3, 0x4029, 0x3388, 0xb265, 0x4274, 0xba28, 0xbfd4, 0xbac1, 0x403a, 0xb011, 0xb282, 0xb212, 0xbf37, 0x4134, 0x401e, 0x42b3, 0xbaf5, 0xbfbe, 0x417e, 0x4009, 0x14f9, 0xba76, 0xb2d3, 0x40de, 0x43ee, 0x446f, 0x4238, 0xbf90, 0x4302, 0x1f23, 0x0475, 0xb256, 0x40a3, 0x4008, 0xb0d8, 0x4147, 0x3b2a, 0x00cf, 0x4381, 0xb233, 0xbff0, 0xba1c, 0x43ff, 0x41a2, 0x4376, 0xb20f, 0xbf49, 0xb2b4, 0x0dbc, 0x3189, 0x418e, 0xba32, 0x1bd8, 0xbf6f, 0xba48, 0xb207, 0xa2ad, 0x411d, 0x1a87, 0x2df4, 0x03c2, 0x2dfd, 0x4046, 0xbf90, 0x3240, 0xb015, 0x4003, 0xb29c, 0x44a2, 0x43b7, 0x4378, 0x0fd8, 0x42fe, 0x466c, 0x41a6, 0x1940, 0x4087, 0xbfba, 0x4106, 0x3ee3, 0x4219, 0x409b, 0x1976, 0x18e7, 0x24f6, 0x3f8d, 0xbad5, 0x40d9, 0x1c9a, 0x2916, 0x1cd9, 0x41e2, 0x4069, 0x1c0c, 0x45c3, 0xb0e6, 0xbfb9, 0x40dc, 0xb055, 0x0946, 0x349c, 0x4004, 0x42d3, 0x1e78, 0x43f0, 0x427c, 0xb20b, 0xb2da, 0x4206, 0x42aa, 0x1d43, 0x428a, 0x19a6, 0xb04f, 0x1f39, 0x2fdc, 0x0fa5, 0x067d, 0x2bbc, 0x405a, 0x0080, 0xbfdd, 0x1ab5, 0xba61, 0x46ec, 0x2e41, 0x43aa, 0x4080, 0x437a, 0x4112, 0x428a, 0x1efc, 0xbaf3, 0xbfc3, 0x4203, 0x45ed, 0x42d6, 0x4552, 0x0588, 0x2ab8, 0x19d9, 0x4391, 0x1c52, 0xbf4f, 0x422e, 0x43c2, 0x43be, 0x13f3, 0x40da, 0xb2ba, 0x3a04, 0xb2cf, 0x0bb2, 0x40ab, 0x42fd, 0x3dd3, 0x417d, 0xb08a, 0x4494, 0xa6be, 0xb229, 0x426d, 0x341a, 0x4312, 0x4212, 0x4258, 0x4195, 0xb2ff, 0x4011, 0x410a, 0x42c5, 0xbfc3, 0xadc5, 0x4367, 0xb29b, 0x4272, 0x416b, 0x42d9, 0xb0bb, 0x458d, 0x4290, 0xa66c, 0x1b4c, 0xac1d, 0x4092, 0xbfd5, 0xb081, 0x438b, 0x4029, 0x00df, 0xb009, 0x449c, 0x432c, 0x425f, 0x08fe, 0xb2dd, 0xba3c, 0x4053, 0x4111, 0x43e5, 0x42fe, 0xba44, 0xb26b, 0x3581, 0x439c, 0x354a, 0x0240, 0x3da2, 0x1bad, 0x4143, 0x407e, 0xbf76, 0x40ac, 0x403e, 0x402b, 0x1fdf, 0xb2c3, 0x40cf, 0x33fb, 0x42cb, 0xb2de, 0x4075, 0x1f1c, 0x43f7, 0x4081, 0x4386, 0xb248, 0x1b37, 0xb237, 0xb251, 0xbae9, 0xb2fb, 0x43da, 0x41d1, 0xba24, 0xbfc3, 0xad4b, 0x40ad, 0x0935, 0xb2b5, 0x43f4, 0xa290, 0xb24c, 0xaea0, 0xbaf1, 0xba23, 0xab45, 0x1e3d, 0x4219, 0x4350, 0x12e3, 0x0d50, 0x4545, 0xbf70, 0x1c23, 0xb28c, 0xb2df, 0x4375, 0x44d4, 0x42b2, 0x43dd, 0xbaf0, 0x41ef, 0xbf91, 0xb24b, 0xab23, 0x1a7b, 0x285e, 0xb09a, 0x40d1, 0x4149, 0x4084, 0x1cc5, 0xba75, 0x4348, 0xba27, 0xa8ac, 0xa7d2, 0x42b9, 0x334a, 0x4594, 0x40c1, 0x42e5, 0x41a1, 0x43f8, 0x4378, 0xa603, 0xb264, 0xba4d, 0xbf53, 0xb073, 0x1f6a, 0x1d2f, 0x1c93, 0x46b3, 0x4289, 0xa678, 0x1899, 0x434b, 0x438d, 0x43e2, 0xa5b7, 0x1f65, 0x3ea4, 0x44dd, 0x43ba, 0xb2b5, 0x2490, 0x4316, 0x18d2, 0xb257, 0x4472, 0xbfb4, 0x403c, 0x4036, 0x1989, 0xa6ae, 0xb211, 0x1d91, 0x43ea, 0x404a, 0x30c3, 0x43ac, 0x124b, 0x164d, 0x40e4, 0x4271, 0xb2c4, 0x2d41, 0x417b, 0xbf14, 0x40cc, 0xba78, 0x426e, 0x31c8, 0x44c3, 0xafc8, 0x43d0, 0x1d72, 0x0d4e, 0x1c65, 0xa4d5, 0xbf7d, 0x2ea8, 0xb0c3, 0x4424, 0x466c, 0x16b6, 0x43c5, 0x181f, 0x1f13, 0x398f, 0x42e2, 0x4354, 0x18b0, 0x2c22, 0x43c8, 0xae35, 0x44d4, 0x4113, 0x1834, 0x4590, 0xbf27, 0x400e, 0x42e3, 0x4289, 0xb28d, 0x0cf4, 0x32b2, 0x0f02, 0x414b, 0x40d5, 0x43c8, 0x4565, 0x376a, 0x330d, 0x1a0e, 0xbf53, 0xb0bd, 0x42b4, 0x422e, 0xb05a, 0x41bb, 0xa3cb, 0x0947, 0xb064, 0x42c4, 0x4156, 0x1bb2, 0x0104, 0x42c6, 0x4181, 0x4096, 0xba72, 0xb0da, 0x42e1, 0xa882, 0x427e, 0x4078, 0x4174, 0x4165, 0x406c, 0xbf01, 0xb0e3, 0x40ec, 0x29ba, 0xba6a, 0x183d, 0xb22c, 0x1b41, 0xb20d, 0xbf18, 0x027e, 0x42de, 0x429a, 0x4326, 0xbace, 0x412b, 0x4394, 0x43cf, 0xbfa1, 0xaeee, 0x4398, 0x18da, 0x42d5, 0x1c5e, 0x3817, 0x312b, 0x145c, 0xb294, 0x42c0, 0x40b9, 0x42fb, 0x3eb7, 0x44e1, 0x43ef, 0x4137, 0x0b6e, 0x4248, 0x4260, 0x4130, 0x43ea, 0x413f, 0x1ae0, 0x41b9, 0x43dd, 0x40dc, 0xbf8f, 0x42bb, 0x425a, 0x40cf, 0xbaca, 0x1f8a, 0xb243, 0x1802, 0x1e4f, 0x035e, 0x411a, 0x4160, 0xae4e, 0x1d60, 0xbfdb, 0x32ef, 0x42b2, 0x46d4, 0xbf70, 0x1ffa, 0x40bc, 0xb05a, 0xbaef, 0xb2ef, 0xb2aa, 0xbf8c, 0x194e, 0x42e9, 0x46d3, 0x4388, 0x40d0, 0x200f, 0xba17, 0xba0d, 0x443e, 0x2066, 0x410d, 0x40ca, 0xa869, 0xb07f, 0x425e, 0x432d, 0xb2b5, 0x43db, 0x1d95, 0x435a, 0xaf6a, 0xb2b0, 0x1b4d, 0x41de, 0xba5f, 0x1217, 0xbf81, 0xb217, 0xb224, 0x1db7, 0x1a1b, 0x2695, 0x406a, 0x3710, 0xb291, 0x3615, 0x3e59, 0x4181, 0x188d, 0x41fd, 0xbace, 0x46b2, 0x431a, 0x4121, 0x43bf, 0x186f, 0x4178, 0x00f5, 0xbf39, 0xa8c3, 0x0415, 0xba21, 0xbad8, 0x3fc6, 0x4116, 0x41f6, 0x443e, 0x0825, 0x43cf, 0x4388, 0x3e82, 0x171a, 0xb0e0, 0xbfa7, 0x42eb, 0x4006, 0x4029, 0x4128, 0x422a, 0x4279, 0x44a1, 0x45cc, 0x1913, 0x4174, 0xbf54, 0xb2a7, 0x4123, 0x4236, 0xba06, 0x425d, 0x1d1c, 0xbf12, 0x12b2, 0xba57, 0x19cf, 0xba6a, 0xb29e, 0xbfb6, 0x2718, 0xaf18, 0xba0b, 0xb2d7, 0x405e, 0x3f04, 0x268f, 0x426e, 0x1a97, 0xa68b, 0x1dab, 0x4104, 0x4661, 0x280a, 0x4550, 0x29ff, 0x41c9, 0xbf70, 0x432b, 0x1fb6, 0x2eb9, 0x419c, 0xb2cc, 0x2b88, 0x0da0, 0xb2b1, 0x42a6, 0x1ad6, 0xbfc1, 0x4599, 0x40b2, 0xb291, 0x43d2, 0x421a, 0x4209, 0x432a, 0xb04f, 0xbaf6, 0x16c6, 0x46d3, 0xbac0, 0x1dbe, 0xbff0, 0x40e7, 0x2de7, 0xbf21, 0x1d42, 0x0f07, 0x4274, 0x4151, 0x46e4, 0xb00a, 0xbad8, 0xbf34, 0x40a1, 0x2ef9, 0x4396, 0xbf53, 0x40e9, 0x4239, 0x42da, 0x405f, 0xba0c, 0x4368, 0xb2de, 0x4449, 0x4278, 0xb2e9, 0x4317, 0x405f, 0xb063, 0x4100, 0x4698, 0x40b0, 0x45c5, 0xb2df, 0x454f, 0x2aca, 0x42ea, 0x43b0, 0x3976, 0x4327, 0x2cbd, 0xbf7d, 0x1c79, 0x2d95, 0x41cd, 0x1af1, 0x408a, 0x2eae, 0x419d, 0x1859, 0x42bf, 0x4272, 0x412a, 0x42a9, 0x0694, 0x1f68, 0x4653, 0x3a31, 0x4277, 0xa6d1, 0x413c, 0xb23c, 0xbfc0, 0x42f6, 0x4377, 0x418f, 0x464c, 0x4133, 0x4129, 0xb294, 0xbfa9, 0x247d, 0x1b16, 0x21cc, 0xbf70, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x181236f9, 0x459e9e0f, 0xd961342f, 0x122ddd5e, 0x38b78514, 0x9508b2ee, 0x97896aed, 0xad92e120, 0xfd37fa46, 0x2e0a81f8, 0x8cf97fb4, 0x8d4b1243, 0x5e1eda21, 0x54da9be4, 0x00000000, 0x500001f0 }, FinalRegs = new uint[] { 0x00fffff3, 0x000000cc, 0xffffffce, 0xffffffff, 0x0000007d, 0x00fffff8, 0x00001b20, 0xffff4210, 0x00000007, 0x6e4f1c33, 0xfffffaff, 0xfffffaff, 0x000000c5, 0x54dab658, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4239, 0xba45, 0xa273, 0xbacd, 0xb2e4, 0x0b51, 0x406c, 0x43a9, 0x1ae2, 0xbad9, 0x1eff, 0xbfc2, 0x42b3, 0xb0ef, 0xb0ab, 0x2572, 0x4344, 0x4165, 0xb279, 0x127a, 0xb2e2, 0xac76, 0x35b9, 0xb2d5, 0x41d2, 0x3d37, 0x43f2, 0xbf9a, 0xb207, 0x40b9, 0xba37, 0x21f3, 0x03e4, 0x430f, 0x444d, 0x1d73, 0x4354, 0x07a6, 0x1b8f, 0xb284, 0xb239, 0xbaed, 0xb2b2, 0xb2a6, 0x45da, 0xb20a, 0x1f8f, 0xb21e, 0x40fa, 0xa7cd, 0xb237, 0xbfd7, 0x444e, 0x4113, 0xbad8, 0x34c6, 0xbf43, 0xa315, 0x14bc, 0x416b, 0x4246, 0x3ae5, 0x46a3, 0xac12, 0xba29, 0xad08, 0x11c5, 0xb28e, 0x1443, 0xbf60, 0x41cb, 0xba32, 0x40a7, 0x40a0, 0x4206, 0xb250, 0xb268, 0x435b, 0xbf8f, 0x429d, 0xbfc0, 0x422b, 0x43e4, 0xb070, 0x1d90, 0x405b, 0x40f8, 0x435b, 0xb232, 0x4251, 0xbfac, 0x424d, 0x39e5, 0xb2b8, 0x46e3, 0x0171, 0x46bd, 0xbf90, 0x40c3, 0x36af, 0x429a, 0x41e9, 0xb25d, 0xb000, 0x3c9d, 0x096e, 0x4030, 0xbfd1, 0x1f7e, 0x27fe, 0xb073, 0x41e0, 0xa276, 0xb0ae, 0xb062, 0x1d63, 0xb203, 0xbfaf, 0x4323, 0x4376, 0xbac1, 0xb0e5, 0x43b8, 0x26f1, 0x4120, 0x37ee, 0xbf7a, 0xb232, 0x43ca, 0x43b9, 0xb2c2, 0x46c0, 0x3ea3, 0x152b, 0x4202, 0x4232, 0xb2b0, 0xbf15, 0xb299, 0xba6a, 0x4199, 0x102a, 0x4023, 0x401a, 0xaa93, 0x4190, 0xba4c, 0xa1ab, 0xb2bd, 0xb017, 0x4375, 0x40b6, 0x43b8, 0x18cf, 0x18c8, 0x406c, 0xac65, 0x2956, 0x0fe6, 0x4001, 0x1c31, 0x423a, 0x42c6, 0x41fa, 0x1bf9, 0x40b7, 0x21bc, 0xbf43, 0x405e, 0x1ce7, 0x41a4, 0x415e, 0x41eb, 0x40a9, 0x41fc, 0xb004, 0x42e1, 0x4370, 0x42db, 0xbaee, 0x42df, 0x0d78, 0xb0f8, 0x4394, 0xb2a4, 0x3923, 0x3c97, 0x4206, 0xb0fb, 0xb21c, 0x10cb, 0x4050, 0x3634, 0xbf03, 0xb0ad, 0x46ed, 0x38c3, 0x1945, 0x4236, 0x17c2, 0x282d, 0xbae2, 0x40b6, 0x22e8, 0x42ac, 0x1b6d, 0xba53, 0x414e, 0xb013, 0xb0c2, 0x41ff, 0xb281, 0x1cd3, 0x41da, 0x4141, 0x420f, 0xa91b, 0x35d5, 0xa6d8, 0xba09, 0x4156, 0x278e, 0xbf11, 0x45be, 0xa4ee, 0xb28b, 0x3d9d, 0xb2b3, 0x0dea, 0xa044, 0x43d1, 0x2843, 0x425b, 0xa5dc, 0x26b9, 0x1ef7, 0xba61, 0x028d, 0xa302, 0xb061, 0x4306, 0x2b7d, 0x1b33, 0x4215, 0x1dbe, 0x40cc, 0x41af, 0x1fef, 0xbf77, 0x4308, 0xba38, 0x4306, 0x4099, 0xb275, 0xbf6d, 0xbae5, 0x1f9f, 0x1cc6, 0x1101, 0x42ca, 0x2e58, 0x41a7, 0xb235, 0x29fa, 0x43dd, 0x41d8, 0x402c, 0x4138, 0x41e8, 0x3d38, 0xba10, 0x41f6, 0xb26f, 0x1e35, 0xba03, 0xb2b3, 0x1c33, 0x40f7, 0x3fbf, 0x4359, 0x45ae, 0xbf1f, 0x405e, 0x43f4, 0x419a, 0x207e, 0xa253, 0xb084, 0xac7c, 0xb232, 0xbf00, 0xb01e, 0x1b16, 0x318e, 0xae09, 0x447c, 0x1a2c, 0xaf1f, 0x0953, 0xaf93, 0xb23f, 0xb286, 0x43b0, 0x1cdb, 0xba74, 0x2e38, 0xbf3d, 0x3640, 0x1eba, 0x3f29, 0x4339, 0x43b4, 0x257a, 0xba48, 0xb20d, 0x188c, 0x412b, 0x1232, 0xba24, 0x464e, 0xb2f5, 0x42fe, 0x40d8, 0xa5b3, 0xb210, 0x43ad, 0xaf54, 0xb273, 0x0770, 0x4261, 0x415a, 0x42cf, 0x3104, 0x1c29, 0x368d, 0xba20, 0xbfd2, 0x416b, 0xb2ed, 0x40a7, 0x1ff4, 0xbaf4, 0x1d6a, 0x1d89, 0x4398, 0x1839, 0xb0d7, 0x458e, 0xba38, 0x4019, 0x432c, 0x028b, 0x4323, 0xb09c, 0x1444, 0x423d, 0xb286, 0xbfe0, 0xba19, 0x1439, 0x346c, 0xbfa5, 0xb0ab, 0x2da0, 0x403d, 0x40b0, 0x404a, 0x105c, 0x1c9c, 0x408b, 0x4239, 0x2701, 0x226e, 0x41ba, 0xb246, 0x4123, 0xb0ba, 0x40b6, 0xbfcf, 0xb065, 0x4220, 0x4200, 0x410e, 0x0e39, 0x415f, 0xba6e, 0x43ef, 0x424c, 0x3ba0, 0x3e8d, 0x45b6, 0x4037, 0x4179, 0x4241, 0xb0dd, 0xbf2f, 0x40ff, 0x434a, 0x4336, 0xbada, 0xb279, 0xb0f4, 0x11c7, 0x41c2, 0x1e64, 0x4301, 0x1e52, 0x1892, 0xbfd2, 0x409a, 0xba57, 0x4197, 0x432d, 0xbf25, 0x40a2, 0xac2d, 0xaaed, 0xb257, 0x34e1, 0xba04, 0x4258, 0x18b0, 0x4384, 0x1397, 0xa3ae, 0xb0d7, 0xb28f, 0x425e, 0x422b, 0x4314, 0x222d, 0x3b0e, 0xbf97, 0x41c3, 0x4209, 0x43b1, 0x2442, 0xa92c, 0xb02c, 0xbf35, 0xb293, 0x2e27, 0x41f5, 0x4322, 0x0a11, 0x40ee, 0x0966, 0xbad5, 0xb28e, 0x19b3, 0x4015, 0x45be, 0xb25d, 0x415d, 0x41e0, 0x1bc8, 0xbfd9, 0xb08a, 0x1c28, 0x4281, 0x4309, 0x430a, 0x402c, 0xb2c2, 0x40e4, 0xba22, 0xb00d, 0x418c, 0x32ee, 0x40a5, 0xb2bb, 0x43fb, 0xb2f9, 0x43bc, 0x41b8, 0x4098, 0x1a85, 0x4162, 0x41d2, 0x40be, 0x4613, 0x2b71, 0x0ee4, 0xb0ef, 0xbae0, 0xbf17, 0x43cb, 0x4082, 0xb20f, 0x41a0, 0x198a, 0x2530, 0x2c43, 0x3981, 0x1f52, 0x423e, 0xbf04, 0x43a2, 0x4043, 0x4079, 0x1bb9, 0x4268, 0x2138, 0x1aee, 0xbf88, 0x419c, 0xb2cd, 0x1cf4, 0x03df, 0x1acf, 0x4589, 0xba06, 0x4207, 0x43d8, 0xba09, 0xbfa0, 0x4606, 0x4243, 0x417c, 0x3043, 0xbf6e, 0x4365, 0x4488, 0x405d, 0x2d15, 0x41c4, 0x4111, 0xbf62, 0x4321, 0x4135, 0x3c6e, 0x0893, 0x260f, 0x400c, 0x4091, 0x4135, 0x40a7, 0x116e, 0x467d, 0xbf2e, 0x0fae, 0x4091, 0xa2a7, 0x0bed, 0x4550, 0x4268, 0x4305, 0xbf46, 0xb2e8, 0x3fe3, 0x1942, 0x439f, 0xa9d4, 0xbf9b, 0x4236, 0x1d7a, 0x28a1, 0xb0d2, 0x3a43, 0x40f6, 0x42c5, 0x4275, 0xb245, 0x43c2, 0x3019, 0x1eff, 0xa434, 0x4360, 0x3af7, 0x42bb, 0xbf80, 0x44ab, 0xbf4d, 0xab4f, 0xb068, 0xaf79, 0x4291, 0xb281, 0x4621, 0x42fa, 0xb2fe, 0x10f6, 0xbf80, 0x4018, 0x41ed, 0xb24f, 0xb02e, 0x456f, 0x41da, 0xbf1d, 0xbf90, 0x4118, 0x4307, 0x422d, 0x430e, 0x41c3, 0x0fe8, 0x315d, 0xb2b8, 0x1d2f, 0x1e41, 0x0a83, 0x41ac, 0x1d8c, 0x43b9, 0x4691, 0x45c2, 0xba25, 0x1cf0, 0xba01, 0x0098, 0xbfb2, 0x05fd, 0x404f, 0x4268, 0x0194, 0xabd2, 0xbfd0, 0xbf93, 0x1489, 0xb274, 0x2bec, 0x4225, 0x2a29, 0xba47, 0x23e9, 0xa627, 0xba14, 0x40ea, 0xa9ef, 0x27b9, 0xba57, 0xbf5d, 0x1a24, 0x465d, 0x4319, 0xba31, 0x4170, 0x04ed, 0xb295, 0xbaca, 0x417b, 0x4130, 0xb28a, 0x406b, 0xb252, 0x4090, 0x371e, 0x43fd, 0x1b99, 0x222a, 0x40f2, 0x4607, 0xbf91, 0xa406, 0x0d68, 0x410b, 0x41cb, 0xbfca, 0x43b5, 0x1cc7, 0x3cf1, 0xb275, 0x438c, 0x409d, 0xa26c, 0x433d, 0x1e60, 0xb24f, 0xbf63, 0x1b7e, 0x423d, 0x129a, 0xb0d1, 0x40b2, 0x1f17, 0x1fa4, 0x4095, 0xb068, 0xbf70, 0x40aa, 0x2399, 0x012b, 0x00cb, 0x410b, 0x2251, 0x1eed, 0x1f84, 0xaab2, 0xb064, 0x4090, 0xb043, 0x4180, 0x46a0, 0x2617, 0x4203, 0x4122, 0x136f, 0xbf46, 0x195a, 0x43d9, 0xb025, 0xb02b, 0x42db, 0x41e2, 0xa62b, 0x404e, 0xba7f, 0xbfa9, 0x31f0, 0x43db, 0x46b1, 0x404a, 0x1862, 0x425a, 0xb21e, 0x41a0, 0x4573, 0x40f0, 0xb228, 0x423b, 0x1f8f, 0x410f, 0x0667, 0x44a3, 0x02bf, 0xb280, 0x4008, 0x44d0, 0x4216, 0xbf61, 0x3b93, 0x420f, 0x0b94, 0xa28c, 0x432b, 0x1fa5, 0xbf5e, 0x4305, 0x42dc, 0x42ce, 0x4335, 0x1c24, 0x4615, 0x4295, 0x11a7, 0xba38, 0x42e6, 0x46aa, 0x1faa, 0xb2b0, 0xb222, 0x1112, 0x094a, 0x43d4, 0x4034, 0x1b1b, 0xbf66, 0x1fa4, 0x4082, 0x4228, 0x1b74, 0xb252, 0x3875, 0xb2f6, 0x23ec, 0x420b, 0x40b2, 0x46a8, 0x06f2, 0x1954, 0xb206, 0xbfac, 0x421d, 0xbac3, 0xbf80, 0xb0e5, 0x4274, 0xbfaf, 0xbf70, 0x36bc, 0xb2ae, 0xba44, 0x405c, 0xbf70, 0xb0be, 0xbadf, 0x304b, 0x4231, 0xb250, 0x18ca, 0xba6b, 0x4144, 0xb248, 0x2d0a, 0xbf90, 0x4474, 0x43e4, 0x4307, 0xa8c6, 0x4153, 0x41c6, 0xbf5c, 0x4161, 0x0875, 0x432f, 0xb234, 0xb2f0, 0x417e, 0xb2b2, 0x3ea9, 0x42d7, 0xba5e, 0xb04b, 0x1d7c, 0x4227, 0xbfbd, 0x422f, 0x43db, 0x3b1d, 0x41a3, 0x431b, 0xb29f, 0x433a, 0xb275, 0xa6ed, 0x4035, 0x07e4, 0x1978, 0x4096, 0x1f30, 0x386d, 0x4132, 0x41e5, 0xb254, 0x04e9, 0x1f81, 0xb2c5, 0x1507, 0xbf49, 0x4464, 0x431d, 0xa8cc, 0x41ab, 0x42df, 0x4158, 0x43e5, 0x4243, 0xb230, 0xb24b, 0xb262, 0x0f27, 0x42c5, 0x4321, 0xbad1, 0x0b26, 0x24b1, 0x18e4, 0xb285, 0xbf8d, 0x434a, 0x42f9, 0x4007, 0x4061, 0xbfc3, 0x3a04, 0x45ed, 0xa055, 0x44c8, 0xa20f, 0x423d, 0x1ee2, 0x44b5, 0x1e41, 0xae96, 0x0d96, 0x411b, 0x4384, 0x45ab, 0xbf15, 0x1f8a, 0x0b2d, 0x42e4, 0x437d, 0xb23c, 0x3ca6, 0x1d27, 0x429f, 0xbad6, 0x4056, 0x429a, 0xbf02, 0xb0c7, 0xbfb0, 0x4300, 0x413c, 0x40ca, 0x42e3, 0x426b, 0x08df, 0xa9a2, 0x44ec, 0x42e1, 0x4295, 0x0436, 0xb2e8, 0xbf53, 0x4499, 0xa2c7, 0x4000, 0x4274, 0x4299, 0xb211, 0x40c7, 0x31f0, 0xb25e, 0xb2f4, 0x413b, 0x1d10, 0x3f32, 0xbf22, 0x4381, 0x2c17, 0xb2a3, 0x4075, 0x3d15, 0x4455, 0x1a65, 0x40c1, 0x2389, 0x41eb, 0x1238, 0xb2ab, 0x1fea, 0x4022, 0x1f25, 0x4072, 0x403e, 0xb021, 0x45c5, 0x1c07, 0x421a, 0x4557, 0xbf60, 0x416b, 0xb2f3, 0xbf7d, 0x4005, 0x3e21, 0xbf60, 0xb225, 0x409b, 0x4340, 0x00db, 0x413d, 0x4275, 0x4574, 0x44b1, 0x412f, 0x4049, 0x2295, 0x20e6, 0xbf0c, 0x4021, 0x185e, 0x4317, 0x41a6, 0xb24e, 0x41c4, 0x2d7b, 0xb07b, 0x0b1e, 0x2317, 0x4254, 0x295f, 0x42f9, 0x4644, 0x1a5e, 0xb208, 0xba1e, 0x400d, 0x1bc5, 0xa2fc, 0x194f, 0xbfb5, 0xb26e, 0x40fd, 0x37c1, 0x4314, 0x432e, 0xaa7f, 0x17da, 0x43e1, 0x1ad4, 0xb06b, 0x4001, 0x071f, 0xba54, 0x1eeb, 0x1ddb, 0x2da8, 0x432c, 0x307f, 0x41c2, 0x2eea, 0xbad1, 0x43d7, 0xbf5c, 0x1edb, 0xbaed, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x7050d002, 0x2ad824dc, 0xcda44a5a, 0xbe440822, 0x6fa909fc, 0x5324049c, 0xb3f8f834, 0x42ed399f, 0x207b9830, 0xa4b5347a, 0x1f876040, 0xfb4d4a81, 0x16ce792a, 0x219b1d90, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x0000007f, 0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000000, 0x17000000, 0xffffffff, 0x00001631, 0x0000160f, 0x00000001, 0x16ce8d4b, 0x16cfdbf9, 0x000166eb, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1d91, 0xbae9, 0x3565, 0xb2f7, 0x4627, 0x41f1, 0xbfe8, 0x4294, 0x0fb8, 0x2445, 0x0e33, 0x29fa, 0x116e, 0x345c, 0x44b5, 0xb2d2, 0x4013, 0x4246, 0x2612, 0x4133, 0x42bc, 0xb28a, 0xbf14, 0x4370, 0x194f, 0x4319, 0x4264, 0xb0d1, 0x1c87, 0x4329, 0xbfc4, 0x441e, 0x4212, 0x1b80, 0x4261, 0x0436, 0x46e1, 0x444e, 0x1a0b, 0x40d3, 0xa4e6, 0x407b, 0x4201, 0x426d, 0x43a3, 0x436d, 0x1a3c, 0x1b83, 0xad83, 0xbadf, 0x4016, 0xb058, 0xacc4, 0xbf90, 0x43ea, 0x4445, 0xb2fe, 0x08da, 0xbf29, 0x41a9, 0x0835, 0x0f8d, 0x1e32, 0x0a48, 0x41f4, 0xbaca, 0x41e4, 0x41ac, 0xba02, 0x1db6, 0xb099, 0x1f86, 0xbfd0, 0xa3b6, 0x463a, 0x4108, 0x2cb1, 0xb0ce, 0xb261, 0xba1b, 0x1edc, 0xba7a, 0x4285, 0xb206, 0x4078, 0x4252, 0xa63c, 0x107d, 0xbf9b, 0x4331, 0x40c1, 0x415c, 0x4062, 0x186b, 0x1e9d, 0x4014, 0x32c3, 0x43e7, 0x40e6, 0x4382, 0xba6e, 0x4332, 0x1cea, 0x0bb1, 0x1de5, 0x429c, 0x41f1, 0x4243, 0x0059, 0x41db, 0xa558, 0xbf7c, 0x4203, 0x43a7, 0x43d8, 0x4460, 0x413e, 0x439e, 0x424d, 0x1ae1, 0xbadd, 0x1cb2, 0x415a, 0xb240, 0x311d, 0x41de, 0x155b, 0xa4c4, 0xb25d, 0xb2b4, 0x4329, 0x445c, 0x40be, 0xa150, 0x4284, 0x44eb, 0x456d, 0x40a2, 0x439d, 0xbf6d, 0x4073, 0xa3d2, 0xb036, 0x41c9, 0xbad3, 0x41a2, 0x432a, 0xba1c, 0xbafe, 0x42a1, 0x43e8, 0x4120, 0x414b, 0xb202, 0x251f, 0xba64, 0xbac8, 0x1977, 0x4291, 0x19a1, 0x1459, 0x1af3, 0xb0ca, 0x3165, 0x1a2f, 0xb224, 0xa3d5, 0xbfd5, 0xb20e, 0xb2df, 0x4689, 0x4143, 0xbae5, 0x4169, 0x40e7, 0xa294, 0xaa9c, 0xba66, 0x2969, 0x35a9, 0x4363, 0xbf69, 0x40e8, 0xa271, 0x41f2, 0x4391, 0x426b, 0x40c5, 0x411c, 0x46ed, 0x1839, 0x4345, 0x4202, 0x4056, 0xb26c, 0x406d, 0x16ec, 0x4276, 0x1249, 0xba23, 0x4301, 0x409a, 0xbfa4, 0x408e, 0xb2d2, 0x0054, 0x4092, 0x421f, 0x4313, 0xb2f2, 0x4115, 0x1c95, 0x1ba7, 0xba42, 0x0497, 0x0d14, 0x1c8d, 0x41ca, 0x400e, 0x436d, 0x17de, 0x1cc0, 0x417c, 0x1e15, 0x1250, 0x4288, 0x0add, 0x2783, 0x40d7, 0xbfce, 0xbfa0, 0x41e4, 0x3706, 0x33b9, 0xba5f, 0xb063, 0xb215, 0x421c, 0x04a2, 0xbf0b, 0xa42c, 0xba4a, 0x425f, 0xb2f4, 0x0891, 0xbf60, 0x1796, 0x43d7, 0x1520, 0xb26d, 0xbf9c, 0xb28d, 0x1b2e, 0x4137, 0x41be, 0xa19f, 0x40eb, 0x4247, 0x2c5d, 0x40fd, 0x41e9, 0xb260, 0x4069, 0x2417, 0x407b, 0xbf62, 0x2e6e, 0x42e9, 0x1f8b, 0xbae8, 0x1d37, 0x3234, 0x4287, 0xba75, 0x4232, 0x245c, 0x1176, 0xb025, 0x3f5c, 0xbfd9, 0xba5c, 0x40ad, 0x43d1, 0x189d, 0xba3c, 0x41c0, 0x4240, 0x41f1, 0x4276, 0x42ed, 0x4242, 0x4398, 0x40de, 0x444b, 0xb295, 0x411c, 0x0bf0, 0x3c69, 0x40d3, 0xbf1d, 0x41e0, 0x320b, 0x43bd, 0x02b9, 0x41da, 0xbaf3, 0x1052, 0xb27b, 0x4116, 0x1de7, 0xb2d3, 0x41b7, 0x1b37, 0x15a2, 0xb074, 0x4246, 0x428b, 0x1dbe, 0x1dd3, 0x1a49, 0xba20, 0x04d8, 0xbfe1, 0xb0fc, 0x400a, 0xbaf7, 0xbae1, 0xb043, 0x2790, 0xb273, 0xabe4, 0x405b, 0xa232, 0xbf33, 0x2d1d, 0x40f3, 0x4129, 0x133f, 0x41a7, 0xa789, 0x4264, 0x292e, 0x4056, 0x2f67, 0x40c9, 0x0a78, 0x41b6, 0x43cc, 0x0601, 0x4177, 0x3fd1, 0x42bf, 0x1e48, 0x4256, 0x426d, 0x1342, 0x4279, 0x4341, 0xab7e, 0x43f5, 0xb293, 0x2783, 0x2ef7, 0xbfdb, 0x43e2, 0x4098, 0x41eb, 0x4115, 0x1b25, 0x1f61, 0x43b5, 0x0cf3, 0x3f68, 0x0ce3, 0x004b, 0xb2de, 0x41e2, 0xb07e, 0x1ed0, 0x2e59, 0xba46, 0xba44, 0x3742, 0x17bf, 0x3e63, 0xbf07, 0xa4a5, 0xba1f, 0x4249, 0x3421, 0x435b, 0x434f, 0xba74, 0x0c5d, 0x4121, 0x3700, 0xb226, 0x3869, 0xb279, 0x46dd, 0x441c, 0x43da, 0x40ab, 0xbf2d, 0x1e81, 0x422c, 0x1c93, 0x4090, 0x424c, 0x4108, 0xb213, 0xa996, 0x3006, 0x438e, 0x41f8, 0x1c12, 0x20d7, 0x41d7, 0x0620, 0x4216, 0x22be, 0xba02, 0xbf60, 0x18b1, 0x431f, 0xbad4, 0x0f22, 0x4607, 0xb257, 0x38ed, 0xbfca, 0xb25c, 0xb245, 0x401b, 0x4009, 0x4303, 0x1615, 0xbf23, 0x42e0, 0x42db, 0x428f, 0x42f0, 0xb205, 0x1d0b, 0x404b, 0x2453, 0xba3d, 0x0eb6, 0x4319, 0xb073, 0x0b49, 0x43e2, 0xba38, 0xbf41, 0x1dc4, 0x1ea7, 0x1ffc, 0x408f, 0x3064, 0x189c, 0xa006, 0xbaef, 0x405e, 0x0d23, 0x2a88, 0x43a7, 0x401c, 0x092a, 0x4023, 0x440f, 0x41aa, 0x407f, 0x353c, 0x1de4, 0x4250, 0x4431, 0x4606, 0x4160, 0x42c3, 0xbf5e, 0x1f23, 0x3650, 0x4652, 0xa61a, 0x44db, 0x446f, 0x418b, 0xba3a, 0xb217, 0x03a3, 0xb03c, 0xb00e, 0x42bf, 0x4582, 0x41af, 0xb22c, 0x1c40, 0x42d8, 0xa1af, 0x42a8, 0x40d6, 0x40c3, 0xb27b, 0xbf88, 0xa181, 0x18bd, 0x1d56, 0xb2c3, 0x3309, 0xbfd9, 0x0e90, 0x1b15, 0xb2cf, 0xbafd, 0x446c, 0x4166, 0x427f, 0x2cd4, 0xbff0, 0xb20b, 0xba7f, 0x4374, 0xbf44, 0xb0fa, 0xb2a7, 0x4595, 0x4240, 0x4405, 0xb253, 0xba4d, 0x1879, 0xb231, 0x2937, 0x2c54, 0x1b7c, 0x0fca, 0xb0be, 0xbf00, 0x428c, 0x2f47, 0x4248, 0x4075, 0x121d, 0x40bc, 0xbad3, 0xba4e, 0x44f8, 0xbf7e, 0x42f5, 0x4319, 0xb2d6, 0xa214, 0x41ca, 0x41b6, 0xb001, 0xbf88, 0xa731, 0x4452, 0x1ee4, 0xb2ab, 0x39e6, 0xb092, 0x4097, 0x407b, 0xba6b, 0x464d, 0x44c1, 0x40d8, 0x100e, 0x4010, 0x46eb, 0xbafb, 0x07e0, 0xb2d3, 0x4332, 0x41ab, 0x4022, 0x46b9, 0x3214, 0xbf8d, 0xba7d, 0x407a, 0x42c2, 0x195d, 0x2988, 0xb23d, 0x4382, 0xbf46, 0xb236, 0x44c2, 0x1f13, 0x0599, 0x1edf, 0x1984, 0xba34, 0x25fe, 0xb202, 0x2128, 0xb227, 0xbf5e, 0x1bd5, 0x4265, 0x454a, 0x1485, 0xa79d, 0x1484, 0x409e, 0x431e, 0x4028, 0x4272, 0xba7d, 0xb2d3, 0xb228, 0x4093, 0x3c65, 0xba5d, 0x3bb9, 0xb2cf, 0xbf76, 0xb053, 0x4272, 0x41fd, 0xb052, 0x3908, 0xb042, 0x407f, 0xb29e, 0x43cd, 0x2c83, 0x4310, 0x4680, 0xb210, 0x0762, 0x4350, 0xbf77, 0xb243, 0x1b98, 0xb0f5, 0x4041, 0xb287, 0x4038, 0xbad5, 0x408d, 0xabd0, 0xba34, 0xbf71, 0xb2cd, 0x4168, 0x1b2e, 0x42e3, 0x1824, 0xb21f, 0x3ab2, 0x025b, 0xb23f, 0x45f3, 0xbaf0, 0x1d87, 0x40e1, 0x4177, 0xb21c, 0x4362, 0x43c5, 0x3708, 0x0554, 0x1d4d, 0xb073, 0xbfe4, 0xac1d, 0x4172, 0x0cf2, 0x4048, 0xbaf8, 0x442b, 0x1a0b, 0xbf67, 0x426e, 0x1c1c, 0x40a9, 0xbf60, 0x434d, 0xbae5, 0x4426, 0xb280, 0x150b, 0xb2ac, 0x4421, 0xbf2d, 0x1ede, 0x42c3, 0x426f, 0xbf90, 0x40aa, 0x4175, 0xb205, 0xb2f0, 0xaf19, 0xbfa7, 0x43b3, 0xba7f, 0xada2, 0x40ff, 0x4263, 0x0093, 0x42a8, 0xb2d3, 0x4114, 0x381b, 0x417b, 0xb2e3, 0xb206, 0xa446, 0xb204, 0x4192, 0x2850, 0xbfc0, 0x4078, 0x0d01, 0xbf53, 0x1bf6, 0x4218, 0x4065, 0x435e, 0x016b, 0x3bcd, 0x4326, 0x4335, 0xadc9, 0x1c41, 0x2724, 0xbad3, 0x4125, 0x42f7, 0xae86, 0xb2ac, 0xaced, 0x42fb, 0x4572, 0xba46, 0x4178, 0xbfc0, 0x0a8a, 0x02c0, 0xa6fb, 0x4117, 0x430b, 0xbf73, 0x1ee6, 0x1b3a, 0x42e0, 0x4279, 0x40d8, 0x45a4, 0xb204, 0x40e3, 0x3114, 0x4274, 0xa303, 0x421b, 0xbac2, 0x42c5, 0x4284, 0xbfca, 0x4142, 0x1e76, 0x41f2, 0xb272, 0x1ec0, 0xb2fa, 0x4494, 0x1ec1, 0x1ec0, 0x414a, 0xba7d, 0x4647, 0x439b, 0xbfa0, 0x40fa, 0xbaef, 0x0dd9, 0xb26c, 0xa054, 0x4032, 0x40aa, 0xba37, 0x456f, 0xbfb3, 0x40a4, 0x4027, 0x12fe, 0x25e9, 0x259e, 0x2bbc, 0x4223, 0xba2c, 0x2553, 0xbf24, 0xa784, 0xb2db, 0xb23c, 0x4362, 0x40c8, 0x24fc, 0xa161, 0x08af, 0xb26a, 0x43cc, 0xb2f7, 0x41ad, 0xbfb6, 0x44a3, 0xbfb0, 0x1c2a, 0xb2cc, 0x456f, 0x040a, 0x400e, 0xb270, 0x288c, 0x4056, 0x29c3, 0x4330, 0x1244, 0x128a, 0xbfa6, 0x1f86, 0x3fb5, 0x40ec, 0x40ab, 0x4078, 0xba5d, 0x43a9, 0xbad0, 0x4052, 0x40e3, 0x428e, 0x4603, 0xbf03, 0x416b, 0x0d03, 0xa3b5, 0x1f7e, 0x3e32, 0xb04e, 0x4085, 0xb2d4, 0xb211, 0x4394, 0x3b55, 0xb2d7, 0xbf3b, 0x1379, 0x41d9, 0x4121, 0xb250, 0x43e4, 0xb231, 0x14c6, 0x4245, 0x40ce, 0xa90a, 0x45f4, 0x43bd, 0xb28c, 0xb058, 0x41ac, 0xb276, 0x322f, 0x42b0, 0xa84f, 0x42f0, 0x43da, 0x299e, 0xbfc1, 0x13ef, 0x4298, 0x4304, 0x1aea, 0xb006, 0x4352, 0x41cc, 0x1c38, 0x2f37, 0x439e, 0x4329, 0x45f6, 0x41d2, 0x4253, 0xb209, 0x1129, 0xb27f, 0xbaec, 0x40fd, 0xbf74, 0xa497, 0x372a, 0xbaea, 0xb06a, 0xb24c, 0xba25, 0x45d2, 0x1638, 0xb0b8, 0xba4e, 0x406b, 0xbaf5, 0xbf35, 0x4340, 0x1ffe, 0x4075, 0x1ebb, 0x4029, 0xba36, 0x3d21, 0x0bf9, 0x1dd1, 0x43f4, 0x4316, 0xb241, 0xb212, 0xb2d7, 0x42cc, 0x4379, 0x1e94, 0xb03b, 0x3bc7, 0xbfd0, 0xba35, 0xa8cd, 0x009f, 0x1f3f, 0x436e, 0xbaf2, 0x4369, 0x1f29, 0x4164, 0xbf39, 0x433f, 0x403d, 0x316c, 0x4252, 0x4204, 0xb2b4, 0xba22, 0x19f7, 0x4120, 0x412c, 0x05e0, 0xb2c5, 0x3aa5, 0xbf67, 0x46a0, 0x4076, 0xb2aa, 0x436f, 0x43a9, 0x4183, 0x1c21, 0x12f9, 0x408a, 0x41bb, 0x42a6, 0x220c, 0xb2cf, 0x3de8, 0xbfb5, 0xbf00, 0x2586, 0xb2ce, 0x40ba, 0xa486, 0xa26e, 0x1b02, 0xb063, 0x40d3, 0x07ab, 0x26fd, 0xba10, 0x43ac, 0xbf24, 0x3285, 0x3eb9, 0x1f75, 0xb202, 0x2771, 0xacd9, 0xba3d, 0x4326, 0xbf60, 0xb263, 0x4013, 0x3cb8, 0xbaef, 0x4074, 0x42f5, 0x43f8, 0x40d9, 0x20ec, 0x42f6, 0xbf2b, 0x4336, 0x4321, 0xb06c, 0x2f28, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x45444af8, 0x3713ac06, 0xc0dfa961, 0xa4318add, 0x64c2ab43, 0xd5a78f68, 0x13e65e07, 0x035cff70, 0xf5623146, 0xb8ad178f, 0xade3820c, 0x0224fa96, 0x9850b729, 0xe11a6537, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x000000ec, 0x00000000, 0xffffffff, 0x0000006b, 0x0000014c, 0x71000000, 0xe1eca9ff, 0x00000000, 0x00003ff7, 0x00000000, 0xade3820c, 0xe1ec9c83, 0x9850b74d, 0xe1eca7b7, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xa520, 0xb223, 0x4254, 0x04d2, 0xb2bb, 0x0b78, 0x4241, 0x3e94, 0x4367, 0xbf06, 0x40db, 0x409c, 0xbfb0, 0x40bd, 0xb297, 0x44fa, 0xb2b3, 0x406c, 0x4348, 0xb2ac, 0x4158, 0x40b0, 0x2122, 0x3cd3, 0x433b, 0xbf01, 0x42bb, 0xb2b2, 0x3a8d, 0x43f1, 0x4200, 0x1c8e, 0xada7, 0x40f6, 0x403d, 0x40db, 0x422b, 0xbad1, 0xa3da, 0x4319, 0xa9a2, 0xb21f, 0x4231, 0x41b5, 0x4104, 0x043a, 0xba2f, 0xbfb6, 0x41b2, 0x05c3, 0xb0be, 0x1de0, 0xa730, 0xba13, 0x05fe, 0x41ea, 0xbf4a, 0xa3f1, 0x182e, 0xb217, 0xad55, 0x44d9, 0xb0c0, 0x4389, 0x401f, 0xba25, 0x18ab, 0x02e1, 0x46dd, 0xba26, 0xba21, 0xb2bc, 0x42a2, 0x124d, 0xbfd0, 0xb271, 0x01b0, 0x42e0, 0x4011, 0xa094, 0xb02f, 0x43dd, 0xbf74, 0x41b0, 0x41bc, 0x4133, 0x105d, 0xb28f, 0xb0d0, 0xaf65, 0xaf83, 0xbf71, 0x4196, 0xb06b, 0x4122, 0x400d, 0x41ac, 0x1863, 0xb2e0, 0xb218, 0x41b8, 0x4588, 0xb2a0, 0xbfdb, 0x436c, 0x0f45, 0x0994, 0x43ab, 0x4106, 0x2c4b, 0x01db, 0xb048, 0x4495, 0x42a5, 0x40ae, 0x419c, 0x41ca, 0x40be, 0xa8ed, 0x3fe9, 0x42ea, 0x4127, 0x425f, 0x4499, 0x1c46, 0xb258, 0x3f68, 0xa832, 0xbf0c, 0x42cf, 0xa86c, 0x42ce, 0x4113, 0xb24f, 0xb0ec, 0xb0e0, 0x3d7f, 0xa273, 0x40cd, 0xb03f, 0x41ef, 0x4266, 0x413e, 0xb20c, 0x43b9, 0xb019, 0x4097, 0x05f4, 0x3445, 0x408b, 0x409a, 0x2673, 0xbf4f, 0x417b, 0xbaee, 0x443c, 0xbae3, 0x426c, 0xbf2d, 0x4185, 0x4366, 0x431c, 0x3f62, 0x42bd, 0x46c5, 0x416f, 0xb205, 0x4011, 0x07d0, 0x06d4, 0x413a, 0xbf5b, 0xb2b5, 0x1ae2, 0xa9a9, 0xba0e, 0x401c, 0xb22e, 0x44f0, 0x17c2, 0xb036, 0x1cf0, 0x4096, 0x4299, 0xba11, 0xa27d, 0x1e0a, 0xbf7f, 0xb285, 0x4342, 0x24a7, 0x1a9e, 0x2cbc, 0x4218, 0x0e6d, 0xb21d, 0x437c, 0x4392, 0x41e9, 0xb05d, 0x4101, 0xa9d4, 0x4371, 0x41ab, 0x1bc2, 0x4241, 0x4002, 0xb2d4, 0xb016, 0xbac0, 0xb201, 0x4398, 0x40a2, 0xbf49, 0x430c, 0x1c44, 0x3eed, 0xab02, 0xb20f, 0xbf3f, 0x17a7, 0x418f, 0x4151, 0xba01, 0xbf9b, 0x41a4, 0x0136, 0xa343, 0xb29b, 0x2019, 0xab1a, 0x43d2, 0xbf5e, 0x42b3, 0x297f, 0xb2ce, 0x2057, 0x1d80, 0x1633, 0x4310, 0x337b, 0x40bc, 0x4077, 0xb224, 0xb298, 0x2a78, 0x4233, 0x0d0e, 0x32a6, 0x42cb, 0x4396, 0x40a2, 0xb00f, 0x4050, 0xb271, 0x40ee, 0x410a, 0xbfc8, 0xb230, 0x1a49, 0x4360, 0xb03d, 0x0d57, 0x4276, 0x406d, 0xb223, 0x40b9, 0x40b7, 0x1e40, 0xb257, 0x07e4, 0xa579, 0xb217, 0xbf7c, 0xbae9, 0x4333, 0xbae6, 0xb23c, 0xbae4, 0x423d, 0x4121, 0xbacc, 0x41eb, 0xaaae, 0x1c89, 0x1960, 0x42af, 0x2b33, 0x4287, 0x4000, 0xb27b, 0x436d, 0x458d, 0x42aa, 0x43fd, 0x423a, 0x43fd, 0xbfc3, 0x1e10, 0x4317, 0xb272, 0xbaec, 0x4152, 0x4173, 0x30c9, 0x404e, 0x2173, 0x46c8, 0xb260, 0x4084, 0xb234, 0x4217, 0x1fbc, 0xbf13, 0x46b1, 0x41fd, 0xa309, 0x1f35, 0x418e, 0xb21a, 0x43a6, 0x41f1, 0x4219, 0x45d4, 0x45c6, 0x1ac2, 0x1c3e, 0xbad6, 0xbfc0, 0xba74, 0x4342, 0x41e9, 0x43c2, 0x4051, 0xbf88, 0xb2ce, 0xbfb0, 0xb03b, 0x248a, 0x42c8, 0xa7f9, 0xb2dc, 0x4284, 0xb27d, 0x4207, 0x43d4, 0x3a09, 0x43b6, 0xbfd2, 0x4636, 0x431f, 0x359a, 0x4035, 0xa58b, 0xb293, 0xba72, 0x40b4, 0x42fa, 0x08f7, 0x415c, 0x16ac, 0x438c, 0x42d5, 0x4069, 0x18ce, 0xa2a3, 0x4259, 0x245a, 0x2c8c, 0x4104, 0xbf52, 0xa8d4, 0x1a52, 0xae71, 0xb0dc, 0xa6c6, 0xb240, 0x33b4, 0x3314, 0x4183, 0x183d, 0x1fe5, 0xba4f, 0x1b3b, 0x41b3, 0x3897, 0xb209, 0x4380, 0x417f, 0xbaef, 0xb2e6, 0x42a0, 0x4177, 0xbfb2, 0x1e3c, 0x4250, 0xba34, 0x402c, 0x2e43, 0x435b, 0xbf8f, 0x3463, 0x40a0, 0x4174, 0x44dd, 0x40cf, 0x400b, 0x2fca, 0x3ccd, 0x2e2e, 0x3764, 0xa626, 0x40bc, 0xa3ba, 0x44ca, 0xbfa7, 0x43ed, 0xb278, 0x412a, 0xba1a, 0x01d4, 0x185c, 0x4365, 0x389a, 0x37b6, 0x4111, 0x40f7, 0x4114, 0x06a0, 0x4167, 0x3736, 0x42dc, 0x40c3, 0x32cb, 0x41b0, 0x412b, 0x2c08, 0xbacd, 0x1806, 0xb247, 0x3a1f, 0xbf70, 0x4137, 0xbf9e, 0x42a3, 0x3af2, 0x424f, 0x2f4b, 0xba6c, 0x3b5b, 0xbff0, 0xb2bc, 0x414b, 0x4570, 0xb216, 0xb2a1, 0xbad1, 0x40ae, 0x40d0, 0x41ca, 0x4059, 0xacfd, 0x43ae, 0x43de, 0x428c, 0x19ac, 0x2ed4, 0xba26, 0xbfc5, 0x1e66, 0x403a, 0x41a7, 0x3900, 0x432b, 0x4386, 0x3670, 0x1674, 0x0a49, 0x41b9, 0x438a, 0x42b9, 0x41aa, 0x40a8, 0x43c9, 0xba1c, 0x4167, 0xb0ed, 0x4359, 0xa917, 0xbf59, 0xb040, 0x4205, 0x43eb, 0x1811, 0x1e8e, 0x41cd, 0x41f2, 0x4190, 0x41b0, 0x368c, 0x387e, 0x1545, 0x18d3, 0x40d8, 0x4279, 0x438b, 0x0c33, 0xbf28, 0x4218, 0x41c7, 0xa71d, 0x4373, 0x229d, 0x4605, 0x2a5d, 0xb265, 0xbf15, 0x1c05, 0xb006, 0x193f, 0xba38, 0x4338, 0xbf4e, 0x4177, 0x4138, 0x22ae, 0xb268, 0x40e2, 0xbfa0, 0x42a9, 0x4612, 0x267c, 0x41c9, 0xb291, 0xbad0, 0xba3a, 0x45c2, 0xbac5, 0x43df, 0xb00c, 0x43e4, 0x1faf, 0x1c18, 0x1f38, 0x42f0, 0x1ac4, 0xbf2d, 0x4158, 0x40dc, 0x4329, 0xba24, 0x4009, 0xbace, 0x4206, 0x0e36, 0x3b42, 0x023c, 0x0ab2, 0x42f3, 0xb2d2, 0x4165, 0x236b, 0x4125, 0xb239, 0x45db, 0xbf5e, 0x4198, 0x444c, 0x1601, 0x19a8, 0xbaf6, 0xb251, 0x4035, 0x4169, 0x4265, 0x406f, 0x403c, 0x4319, 0xb29f, 0x4052, 0xb273, 0x1bfe, 0x4588, 0x40fe, 0x41cc, 0x40bb, 0x46f3, 0x42ff, 0xa67e, 0x1fff, 0x10c0, 0xbf33, 0x4286, 0x412d, 0xb26d, 0x41bd, 0x436e, 0x439d, 0x417e, 0x0746, 0x42c6, 0x04ce, 0x406d, 0x4249, 0xb22a, 0x43ff, 0x27d4, 0xb2a7, 0xbf95, 0xb287, 0x366b, 0x1cc0, 0x1802, 0xba5c, 0x43e7, 0x4061, 0x4153, 0x150c, 0x4681, 0x44a1, 0x45cd, 0xbad6, 0xbfc9, 0x42f2, 0x1bfc, 0x4180, 0x25a3, 0x42d3, 0x409b, 0xb0e1, 0x4676, 0xbfa1, 0x436a, 0x42b6, 0xba61, 0x424c, 0x4354, 0x340e, 0x43b7, 0x0ce6, 0x45bd, 0xa0d6, 0x42e8, 0xba77, 0x4037, 0x42b2, 0x446d, 0xbae6, 0x439f, 0xb0a1, 0xba6e, 0x4231, 0xa05f, 0x46ca, 0xb081, 0xba3e, 0x1d40, 0x435a, 0x196f, 0x4168, 0x405e, 0xbf9f, 0x4056, 0x30ab, 0x43be, 0x1956, 0xb01d, 0x41d5, 0xba3b, 0xbf48, 0x08f2, 0x42d2, 0x42a7, 0x4291, 0x423d, 0x405e, 0xb2a9, 0x2cb9, 0x43bd, 0x4270, 0xb2d2, 0x420d, 0x1494, 0x4098, 0xa21a, 0x43d1, 0x442e, 0x4559, 0x3192, 0x43d2, 0xbad2, 0x4379, 0x4298, 0xb2d6, 0xbafd, 0xbfc5, 0xbaca, 0xbaf2, 0x1ba9, 0x422c, 0xb2b0, 0x096c, 0x0443, 0xbafe, 0x44e4, 0xba0a, 0x1d8f, 0x15f2, 0xba7b, 0xbad0, 0xb295, 0x41f3, 0xb212, 0xb0dc, 0x24d5, 0x40c8, 0xba3a, 0x4023, 0xb2cd, 0xa317, 0xa11d, 0x4374, 0x1e3e, 0x414d, 0x401b, 0xbf85, 0x418b, 0xae75, 0xb226, 0x37f7, 0x4392, 0x432b, 0x426c, 0x4312, 0x0f46, 0xbfd0, 0x406c, 0xa729, 0x4136, 0x0f4f, 0x40dd, 0x1d25, 0x1a81, 0x41e9, 0x36ab, 0x4694, 0x4558, 0x1868, 0xb220, 0xbfa3, 0x1ecd, 0x4274, 0x1b66, 0x2988, 0x41e0, 0x08f1, 0x4214, 0xb28a, 0xb243, 0x4071, 0x2b56, 0x4038, 0x438f, 0x2441, 0x41bd, 0x41ba, 0x31cf, 0x408b, 0xb036, 0x1db4, 0x19c7, 0xb2c0, 0xbf55, 0x0c41, 0xbaf0, 0xb2ae, 0x4007, 0x1b06, 0xb08f, 0x1d3d, 0x430e, 0x2937, 0x41a0, 0x4209, 0x4448, 0x402a, 0xba03, 0x3597, 0xb20b, 0x45b3, 0x4376, 0x4663, 0x437c, 0x417e, 0xb088, 0xbf9c, 0x14f2, 0xb29a, 0x2554, 0x18c1, 0x180a, 0x417f, 0x0c42, 0xba14, 0xb05c, 0x41ba, 0xbf5e, 0x2562, 0x4243, 0x420f, 0x0a72, 0x438c, 0xb0ab, 0x4210, 0x1dca, 0xb2d9, 0x434a, 0xbf60, 0xad83, 0xbf35, 0xbadb, 0x407a, 0xb2a5, 0xa130, 0x429e, 0xa4e3, 0x142b, 0x35b8, 0xbfc0, 0x40a6, 0xbaf6, 0x3089, 0x4280, 0xba19, 0x43a6, 0x0fef, 0x442e, 0x1cf6, 0xb2ac, 0x18b2, 0xb0c3, 0xbad1, 0x42e0, 0x391a, 0x435c, 0xbf87, 0xb272, 0x05bc, 0x4226, 0xb2f2, 0x4313, 0x459e, 0x4326, 0x4495, 0x4236, 0x1239, 0xbf55, 0x43f8, 0xb289, 0xbf70, 0xb249, 0xb2cd, 0xb224, 0x4017, 0x1d5d, 0x4026, 0x423e, 0x1214, 0x431a, 0x351a, 0x40d3, 0xbf88, 0xba4e, 0x434f, 0x3815, 0x15ac, 0x1693, 0x40ad, 0xbf90, 0x43ac, 0x42c3, 0x060d, 0x4013, 0x3896, 0x428c, 0x2d3e, 0x435b, 0xaeac, 0x42b7, 0xbfbc, 0x42c5, 0x31b5, 0x1b10, 0x0f85, 0xbaf0, 0xbfa5, 0x09a7, 0x0762, 0xb2cb, 0xab8a, 0xba7c, 0x3c28, 0x40a2, 0x173c, 0xa6f8, 0x0788, 0x4623, 0x4324, 0x4310, 0xb208, 0x3297, 0x4667, 0x41c1, 0xbfc7, 0xbf00, 0xb222, 0x0499, 0x422f, 0x46f8, 0xa72c, 0x2e9e, 0xa41b, 0x4370, 0xbf08, 0xb073, 0xbfc0, 0x04bb, 0x2e56, 0xba6a, 0xba56, 0x4206, 0x4306, 0x427b, 0xb060, 0x4260, 0x4298, 0x4360, 0x434f, 0xbf70, 0xa2d2, 0xbae6, 0xbf80, 0xac39, 0x441b, 0xb2e7, 0x40d7, 0x42a2, 0x42b5, 0x08fe, 0xbf05, 0x195f, 0x414a, 0x40a5, 0x4344, 0xbafe, 0x4212, 0xb225, 0x429d, 0x428b, 0x22bc, 0x43e8, 0xb27d, 0xb2ac, 0x4108, 0xb2d2, 0x1d5f, 0x23c3, 0x4257, 0xb060, 0x4105, 0xb208, 0x441f, 0x2f98, 0x1f2b, 0xbf17, 0x43d2, 0x413e, 0xba24, 0x40ca, 0x4101, 0xbf90, 0x43dc, 0x42b6, 0x431d, 0x42db, 0x43e6, 0xac1f, 0x42c9, 0xba0b, 0x46a4, 0xba41, 0x42f3, 0x45a2, 0xbf2e, 0xb088, 0x40fd, 0x0d9d, 0x1a17, 0x2ee0, 0x1b8c, 0x41cc, 0x42b4, 0xb294, 0x436d, 0xabb1, 0xa7ed, 0xba2c, 0x4002, 0x4214, 0x417c, 0x03a3, 0x1b3a, 0x4645, 0xa99a, 0x4661, 0x41b7, 0xbacd, 0x2cb1, 0x1dca, 0x1d24, 0x42c5, 0xba4c, 0xbf9c, 0x1e2b, 0xb24b, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6858fdc0, 0x2c117adb, 0x4e91c10b, 0x490c37fa, 0xdc3cc5ba, 0x19d843cc, 0x38054938, 0xa1f0a710, 0x540c538f, 0x8e7e0cd6, 0x8458328c, 0x917b025c, 0x75c492d1, 0x64488841, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0xffffa800, 0x540c5724, 0x540c572b, 0x00000024, 0x0c542457, 0x00002457, 0xfffffffc, 0x00001b93, 0x00001730, 0xffffff42, 0xffffff42, 0x00000000, 0x540c5724, 0x540c5688, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xab86, 0x436f, 0xb06c, 0x401b, 0x4278, 0x4283, 0x4243, 0x1d91, 0x427d, 0x312e, 0xad98, 0x4070, 0x4267, 0x4481, 0x438f, 0xba4e, 0xb280, 0xb2d0, 0x4139, 0x40e3, 0x42f9, 0xbf7f, 0x242c, 0x43f9, 0x40cf, 0xb0cc, 0x410e, 0xb250, 0xb083, 0x2759, 0x434c, 0xbad5, 0x0875, 0xb036, 0x426c, 0x3721, 0x412f, 0xbf4c, 0x1bd7, 0x4137, 0xb04a, 0xb2d2, 0xb017, 0xbae5, 0xb258, 0x41ec, 0x1aa9, 0x1e1b, 0x42d2, 0x42a6, 0xb2a0, 0xbf36, 0xbae1, 0x426e, 0x4011, 0x41fc, 0x3afd, 0x4289, 0x355c, 0xb24f, 0x0e2c, 0x4391, 0xb2b5, 0x4059, 0x4030, 0xb299, 0xb281, 0xb04f, 0xb0d6, 0xbf33, 0x0a54, 0xbfe0, 0x41a6, 0x1b05, 0xbae0, 0xac71, 0xbf00, 0xb042, 0x18f7, 0x4576, 0x4056, 0x0b0d, 0xb224, 0xbf62, 0xba1e, 0x4629, 0xad90, 0x44e5, 0x1a37, 0x41c5, 0xbae3, 0xba68, 0x4436, 0x45b4, 0x4203, 0xba1c, 0x026e, 0x4649, 0x42c6, 0x4166, 0xba1d, 0x1d0b, 0x4006, 0x12f0, 0x0f55, 0x425a, 0xbfe1, 0xb264, 0x4672, 0xb031, 0xbac4, 0x2a1f, 0x42ec, 0x4147, 0x437f, 0x4165, 0x407e, 0x1e5e, 0x429e, 0xb03d, 0x262b, 0x4181, 0xa9bc, 0x1b17, 0xbf60, 0x1a3b, 0xa1c0, 0x18d3, 0xb03d, 0xbf25, 0xbad5, 0x421b, 0xbaeb, 0x18d4, 0x3660, 0x42ad, 0xbad0, 0xbfc0, 0x42b4, 0x0065, 0xba05, 0xb242, 0x41d4, 0x43f1, 0x427a, 0xbaf9, 0x4594, 0x41ba, 0xb004, 0xbf11, 0x3700, 0xb272, 0x4242, 0x1bab, 0xa2be, 0x43af, 0x418a, 0xb2c7, 0x0201, 0xb2c8, 0x425c, 0x405e, 0x4119, 0xb20b, 0x22a8, 0xb0bf, 0x1fd3, 0x0867, 0x4324, 0xbf89, 0xb01e, 0xb0a3, 0x3674, 0x29de, 0x4349, 0xb070, 0x00b9, 0xb27a, 0x4396, 0x45f2, 0x280b, 0x4288, 0xbf9e, 0x42fb, 0x0703, 0x40e5, 0x44e5, 0x1bd1, 0x46a2, 0xb2ca, 0x18e5, 0x010b, 0x16dc, 0x4389, 0x440c, 0x4134, 0x0684, 0x3025, 0x400b, 0x4272, 0xba24, 0x2c2f, 0x43c7, 0x2148, 0x151a, 0x433b, 0x41a2, 0xa6d0, 0xbf57, 0x085a, 0xb2c6, 0x44ba, 0x40a1, 0x43cd, 0xb2bc, 0xba3e, 0xb2d1, 0xba61, 0x0c3e, 0x1cfa, 0xbf84, 0x1b40, 0x372a, 0x3b42, 0xb06c, 0x43b7, 0x1d1f, 0x41fc, 0x41f6, 0x4552, 0xb29d, 0x43dc, 0x1a4e, 0x2de6, 0x4204, 0x407e, 0x16a1, 0xb276, 0x40ba, 0xb223, 0x4377, 0x4073, 0xb007, 0xbfdc, 0x41e3, 0x3889, 0x4117, 0x43bb, 0xba05, 0xb2ac, 0x274e, 0xb0fe, 0x01ee, 0x0c90, 0x46ab, 0x1775, 0x426c, 0xbaf1, 0x1030, 0x2822, 0xbad0, 0x089f, 0x4036, 0xbaf4, 0x1e1b, 0xba3b, 0x428e, 0x420c, 0xbf4e, 0x4228, 0x1bf3, 0x40dd, 0x40c8, 0x4010, 0xa54e, 0x4646, 0xba62, 0xbf84, 0x1d90, 0xb25f, 0xbf0f, 0xbacd, 0x410b, 0xb25c, 0x43ec, 0x25fc, 0xa882, 0x29ac, 0x4339, 0x2a4f, 0x431a, 0x032a, 0x4384, 0x3a9f, 0xb2e9, 0x401d, 0x1d5e, 0xbf64, 0x1da6, 0x062e, 0x4150, 0x0935, 0xa143, 0x2840, 0xbac7, 0xbac8, 0x44dc, 0x1af0, 0x434a, 0xba37, 0x432e, 0x4223, 0xbf00, 0x40be, 0x41ac, 0xb2d9, 0x4373, 0x19dc, 0x402c, 0x3f03, 0x43f9, 0xbf44, 0x4057, 0x07ef, 0xb2e1, 0x4308, 0xba47, 0x4033, 0x4069, 0x1590, 0xb2d7, 0x2ff9, 0xb281, 0x07a2, 0x0caa, 0x4022, 0x42fa, 0x425b, 0xba05, 0x2f80, 0xbadf, 0xa0c2, 0xbf14, 0x41ca, 0xbafb, 0x3b0a, 0x084d, 0x41c2, 0x43d6, 0x016f, 0xba73, 0x40ff, 0x1e2f, 0x4634, 0xa32a, 0x4212, 0x4234, 0x193e, 0xb036, 0xb2d3, 0xb2b5, 0x40f7, 0x40a5, 0x43f4, 0xbf84, 0x4322, 0x431a, 0x40e3, 0x3a14, 0xb2ef, 0xba6a, 0x439a, 0x43e2, 0x40db, 0xba35, 0x279e, 0x41f9, 0xb053, 0x400d, 0x4442, 0x45d4, 0xa582, 0xbf6a, 0x4249, 0x05f6, 0x439d, 0xb29f, 0x41d6, 0xa1a1, 0x1849, 0xbf8e, 0x4215, 0x130c, 0x43c9, 0xbfe0, 0xb2ff, 0x1d87, 0x4378, 0xb062, 0x42de, 0x4335, 0xb0b6, 0x416d, 0x4241, 0x08b9, 0x04fb, 0xba2d, 0x44f2, 0x0af9, 0xba57, 0x17c9, 0x4188, 0x1f59, 0x02cc, 0x42fb, 0xbf8f, 0xb289, 0x4148, 0x0ee2, 0x43ef, 0xba1c, 0x40df, 0x456e, 0xb235, 0x1d34, 0x43b0, 0x44ed, 0x4010, 0x43a1, 0x4096, 0x1a2d, 0xbf9d, 0xb01c, 0x417a, 0x429d, 0xb226, 0x19eb, 0xb20e, 0x434a, 0x46ba, 0x4374, 0xbf48, 0x41ea, 0x23aa, 0x41f7, 0xaef1, 0x4331, 0x41c3, 0x1dff, 0x349b, 0x220e, 0x43ec, 0xaf93, 0xbf7d, 0xba45, 0x33db, 0x41e2, 0x465f, 0x42d7, 0xb268, 0xba51, 0x42bf, 0xba33, 0xb03c, 0x42b2, 0x101c, 0x422b, 0xbf05, 0xb2a6, 0x348a, 0xba46, 0x1691, 0xb055, 0xbf60, 0xb058, 0x40f3, 0x404b, 0x3a1b, 0x44b1, 0x18d2, 0x165e, 0x439a, 0x401c, 0x1860, 0x44ca, 0xba4f, 0xbf7a, 0x1f7f, 0x410a, 0x44e0, 0xa367, 0xa9b9, 0x43e5, 0x41a3, 0x112b, 0xba5f, 0xbf01, 0xbf80, 0x4288, 0x4613, 0x1f10, 0x2223, 0xb2ab, 0xb231, 0xba03, 0x415d, 0x38a5, 0xba64, 0x4287, 0x42c7, 0x3c02, 0x1b36, 0x444e, 0xbf0c, 0x42ec, 0x419b, 0xb08b, 0x0e22, 0xbf58, 0x45a0, 0x3d48, 0xa1d0, 0xba09, 0x2ae7, 0x1240, 0xa3da, 0x430b, 0x41d5, 0xb267, 0x4173, 0xab95, 0x4323, 0xb24f, 0x436b, 0x12fc, 0xbf06, 0xbac0, 0x184d, 0xb2c8, 0x1e2e, 0x1b5c, 0x1f35, 0x414f, 0xb2f4, 0x41c8, 0x1bf7, 0x1c02, 0x4179, 0x054d, 0xa9e5, 0x19de, 0x40d4, 0xba00, 0xbae8, 0x406d, 0x4090, 0x16e9, 0xba19, 0xbf17, 0x4135, 0x2fdd, 0xa4f3, 0x4007, 0x43be, 0x4103, 0x46a4, 0xb201, 0x4061, 0xba27, 0xb056, 0x1c97, 0xb294, 0xba6c, 0x1ed4, 0xbacf, 0x35d1, 0x3094, 0x1c4d, 0xba09, 0x28a4, 0xb0a4, 0x2f5f, 0x4263, 0xb03c, 0x0667, 0x31f0, 0xbfc7, 0x259c, 0x41a9, 0x3f6d, 0x4345, 0x4240, 0x1b3e, 0x40d3, 0xba05, 0x4066, 0xbad7, 0xb0e7, 0x1f93, 0xbff0, 0x1c32, 0x41c5, 0x36e4, 0xb086, 0x3d60, 0x1927, 0x4339, 0x41fc, 0xb27d, 0x4253, 0xa158, 0xba4f, 0x0102, 0xbf2a, 0x353f, 0x1eb5, 0x400c, 0xab8b, 0x402c, 0x44fc, 0x384f, 0xb253, 0xacde, 0xaec7, 0x446b, 0xb2dc, 0x413e, 0x1d6c, 0x4206, 0xb298, 0x4297, 0xb2cf, 0x4008, 0x41b4, 0x2866, 0x4115, 0x4369, 0x46e5, 0xbf70, 0x4006, 0xa904, 0x40c4, 0xbf56, 0xa695, 0x42a2, 0x40c0, 0x4152, 0x409a, 0x411b, 0x408e, 0xbf80, 0xb074, 0x43be, 0x072c, 0x40a7, 0x41e3, 0xb0d3, 0x455e, 0xb2a5, 0x1be1, 0x4178, 0x405a, 0x4066, 0xb242, 0x42da, 0xb205, 0xba4a, 0xbfde, 0xba32, 0x45ce, 0x1dc4, 0x42be, 0x4257, 0xb22d, 0xaad1, 0x41f9, 0xb298, 0x4033, 0xb26c, 0xb08a, 0x4210, 0x4094, 0x40d3, 0x4105, 0x41be, 0x02d9, 0x405f, 0xba3a, 0xbac2, 0xbfde, 0x436d, 0x41f2, 0x42cb, 0x4225, 0x40d2, 0x44e5, 0x42a0, 0x41f1, 0x41b7, 0x1e7e, 0x4385, 0x114c, 0x1e14, 0xbf26, 0x37d0, 0xb00b, 0x2124, 0xb276, 0xbac4, 0x41ad, 0x4077, 0x44d0, 0xba27, 0xbf13, 0x2327, 0x21a0, 0x22e5, 0xb069, 0xba4a, 0xb22a, 0xad1e, 0x4207, 0x2a4e, 0x1a9e, 0xb0a8, 0x4095, 0xb0b5, 0x419e, 0xb2fd, 0x422c, 0x33c7, 0xbfb1, 0x4050, 0xb2ed, 0x29d5, 0x191a, 0x1c19, 0xbfbe, 0x1823, 0x447c, 0x41ee, 0x0ba8, 0x46d3, 0xbfb0, 0xaf6e, 0xbfa3, 0x42cb, 0xb043, 0xaf56, 0x4122, 0x405e, 0x1e9e, 0xb2f6, 0x4171, 0x18c9, 0xba2d, 0x0c08, 0x4200, 0x402a, 0x401b, 0xadfe, 0x4365, 0x1fd0, 0x411b, 0xb03b, 0x43d2, 0x42d3, 0xba5c, 0xbfcf, 0x420b, 0x253c, 0xaf62, 0xbafc, 0x4223, 0x4484, 0x41bd, 0x424a, 0xba4b, 0x41be, 0x2525, 0x4262, 0x432b, 0x3897, 0x29b2, 0xbf42, 0xa188, 0x4004, 0x449b, 0x46e5, 0x42ec, 0x45be, 0x3f6a, 0xbf60, 0x436d, 0x412f, 0xbf60, 0x4284, 0xb227, 0xba46, 0x4329, 0xa61f, 0xb21d, 0xbfb7, 0x43aa, 0x1d94, 0xafd1, 0x4102, 0xa3bc, 0xb240, 0x42d0, 0x1b46, 0x4140, 0x1d73, 0x19e9, 0xb0b2, 0x1ac2, 0xb2be, 0xbf57, 0x42e1, 0x455c, 0xb27d, 0x42fa, 0x3cb0, 0xb218, 0x41fc, 0x3d3a, 0x2b79, 0xb2c3, 0x1853, 0xbfa0, 0x4556, 0x4035, 0x1d83, 0x33c4, 0x131e, 0xbad3, 0x428c, 0x41ff, 0x456a, 0xbf56, 0x41f6, 0x4115, 0x4654, 0xa539, 0x43ae, 0x41d6, 0xba1c, 0x3b02, 0x2859, 0xba41, 0xa4f5, 0x43de, 0x41d6, 0xb251, 0x401d, 0xbfe4, 0x42dc, 0xa953, 0x4129, 0x406e, 0x09c1, 0x3105, 0x2ce2, 0xa65c, 0x42e6, 0x42a8, 0x34ac, 0x4270, 0x42ef, 0x3dc8, 0xb2dd, 0x189c, 0xb2bd, 0x1d7d, 0x44d8, 0xb224, 0xbf6a, 0xba40, 0x4194, 0x0198, 0x436f, 0x423c, 0x429f, 0x4077, 0xbad1, 0x1ac3, 0xb0be, 0x2bdf, 0xb051, 0x435d, 0x1573, 0x1d5f, 0x0b67, 0xb0e6, 0x430f, 0x4025, 0xbad2, 0xbfa7, 0x1896, 0x4386, 0x1e61, 0xb250, 0x2733, 0x13f6, 0xba6c, 0x4246, 0x1d98, 0x41f0, 0x4362, 0xb213, 0x41f1, 0xb074, 0xb050, 0x4139, 0xbac9, 0x420e, 0x416e, 0x0a81, 0xbf61, 0xba0f, 0x41c5, 0x40f4, 0xb2e3, 0x2f84, 0xb278, 0xbf67, 0x46c5, 0xba2e, 0x40f1, 0x1036, 0xbff0, 0x3a3b, 0xbaee, 0x3276, 0xb093, 0x1b72, 0x400d, 0x3cda, 0xbf24, 0x197a, 0x42e5, 0x4623, 0xbfbb, 0x1cc0, 0x42cd, 0x41dd, 0x2127, 0x1817, 0x404e, 0x4207, 0x4331, 0x1bf1, 0xbfd7, 0xa8e1, 0x46eb, 0x4372, 0x406b, 0x4670, 0xbf70, 0x401c, 0x403d, 0x4087, 0x4039, 0x43ce, 0x266c, 0x42c2, 0x1b06, 0x2d2d, 0xb0ad, 0x4613, 0x4029, 0x1cbb, 0x1878, 0x4294, 0x4154, 0xb253, 0xbf9b, 0xba0f, 0x4400, 0x422a, 0x34f1, 0xbfd3, 0xb20e, 0x41bd, 0x1c19, 0x418d, 0x3be1, 0xb056, 0x43cc, 0x40e0, 0x133b, 0x43fa, 0x1a07, 0x4086, 0xb26c, 0xbf46, 0x1caf, 0xbae1, 0xb0bf, 0xb2cc, 0xb024, 0x4345, 0x4474, 0xb031, 0x4553, 0x1c60, 0x2731, 0xb2be, 0x297d, 0x4367, 0x40ff, 0x401c, 0xba33, 0x2258, 0xbfce, 0xb280, 0x40c7, 0x0b15, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x38d6f90d, 0xcd984b26, 0xc8b29dc9, 0x67c34614, 0x14951b86, 0xae2631e0, 0x418035e3, 0x147814c2, 0xb4096d8b, 0xc30ae4ae, 0xb5c6061f, 0x1b3e3fa0, 0x0b9ebe6d, 0x746bc3e8, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0x00000100, 0xffffffff, 0x00000058, 0x31000000, 0x00000000, 0x00000000, 0x00000031, 0x00000000, 0x00fc342b, 0x2eb36651, 0x26796350, 0x00002db3, 0x00002d03, 0x00002fab, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1971, 0x439c, 0x401d, 0x1924, 0x1fc0, 0x3352, 0xba14, 0x4306, 0x432b, 0xb268, 0x1c6f, 0x4158, 0x4633, 0x37a0, 0xbfbe, 0x4286, 0x410c, 0x41c5, 0xb209, 0xb2f5, 0xa3dd, 0x40f7, 0xb087, 0xae09, 0xb2de, 0xba4c, 0x4349, 0xa6c5, 0x436c, 0xb05c, 0x4575, 0x2190, 0xbf3c, 0x1ed6, 0xa383, 0x4192, 0xbf60, 0xba2a, 0x36db, 0x4116, 0x2df4, 0x41a7, 0x299f, 0x406c, 0xa653, 0xbaca, 0xb282, 0xbac8, 0x40f7, 0x4669, 0xb274, 0xb022, 0xb2eb, 0x0ccf, 0x41b7, 0x20ed, 0x047a, 0xb083, 0xbf33, 0xb2bf, 0xa78d, 0x3ae6, 0x3882, 0xba1b, 0x455f, 0xbaeb, 0xaa73, 0x435d, 0xb201, 0x42d4, 0x4183, 0x415e, 0xba41, 0x41a3, 0xbfa5, 0x43ac, 0x4255, 0x1b2d, 0x422c, 0x4217, 0xbafe, 0x420c, 0x43e3, 0x40e3, 0x4199, 0x42fc, 0xbf4c, 0x41c6, 0x1001, 0x4674, 0x41d0, 0x404c, 0x4193, 0x4301, 0x16cb, 0x2399, 0x3509, 0x419b, 0x43d0, 0x18c3, 0x43d3, 0x40b9, 0x43ed, 0x137b, 0x40be, 0x2372, 0x3e64, 0x0ccb, 0xbf9b, 0x41a1, 0x26ed, 0x4359, 0x1326, 0xa32a, 0x4364, 0x3046, 0x410d, 0x416d, 0x3711, 0x41db, 0x40b5, 0x429f, 0x43a8, 0x42d0, 0xb2f9, 0x0758, 0xadfe, 0x421e, 0xb247, 0x2f6a, 0xbf0e, 0xb2e4, 0x2aad, 0x43a4, 0xb2d4, 0x439a, 0x4206, 0xbad9, 0x4153, 0x0dc3, 0xb217, 0xa3ea, 0x1a60, 0x1768, 0x4555, 0x40f9, 0x4205, 0x463d, 0x3e44, 0x4016, 0x3638, 0xba58, 0xb2c2, 0xba3a, 0xba04, 0x4224, 0x4359, 0x0001, 0x422a, 0xbf3c, 0x4342, 0x1b5e, 0xb22b, 0xb2ef, 0x36ae, 0xb204, 0xbfb0, 0x1858, 0x41da, 0x241b, 0xb04f, 0xb28d, 0xba31, 0x42f0, 0x42e7, 0x4028, 0x1dde, 0x45cd, 0xba2a, 0x40c0, 0x18b0, 0x0b4c, 0xbfbd, 0x1b99, 0x0418, 0x4239, 0x3b1d, 0x23c6, 0xba4f, 0x1f5d, 0xb2b0, 0xbac5, 0x1c2a, 0x45f1, 0x422b, 0x43bd, 0xa17c, 0xbf33, 0xba74, 0xb235, 0x0c06, 0x43b2, 0x40b3, 0xb0d2, 0xbf39, 0x4182, 0x46d9, 0xb2f1, 0x4271, 0x43e5, 0xb2b0, 0x41de, 0xbf00, 0x317f, 0x41e1, 0xa9e4, 0x18b0, 0x2a87, 0x4151, 0x43a1, 0x4179, 0x4204, 0x42c1, 0x44f8, 0x4389, 0x40f0, 0x2442, 0x43af, 0xbfa8, 0x3cac, 0x4365, 0x2149, 0x41ff, 0x443f, 0x4190, 0xb0ba, 0x216f, 0xb225, 0xb248, 0xb0f5, 0xba3e, 0x0b19, 0x1bc6, 0x41a7, 0xb0d7, 0xb2dc, 0xb20b, 0x1da9, 0x432d, 0x4293, 0x4035, 0x01ab, 0x2725, 0xbf93, 0xb264, 0x42b6, 0xb074, 0xb2db, 0xb2b6, 0x1dd3, 0x40fc, 0xba52, 0x4301, 0x1e30, 0xba78, 0x413c, 0xbfab, 0xa981, 0x414a, 0x439f, 0x4015, 0x0769, 0xb23d, 0x43de, 0x2bb2, 0xba4a, 0x4261, 0x412c, 0xbf9c, 0x4135, 0xb2ce, 0x4224, 0x1fcf, 0x42bc, 0x18cd, 0x1b98, 0x2bdb, 0x3605, 0x43df, 0xa64c, 0xbf9a, 0x32a3, 0x43ec, 0xb2d2, 0x43a1, 0xba7d, 0x43ee, 0x0e94, 0x44c9, 0x01d6, 0x4140, 0x2921, 0x424d, 0xb0a4, 0x0caa, 0xbf85, 0x41d4, 0x19a8, 0x0dc9, 0x4161, 0x1b35, 0x43ee, 0xa171, 0x4477, 0x4385, 0xba16, 0x420f, 0x3013, 0x0573, 0x4281, 0x3bf9, 0x181a, 0xb0bd, 0xb046, 0x3254, 0x4654, 0x43fd, 0xba34, 0x42b0, 0x4215, 0xb022, 0x42cf, 0x4225, 0x4281, 0xbf0e, 0x4021, 0x4217, 0x4351, 0x4283, 0xb2d9, 0xb223, 0xbfe0, 0x4209, 0xbfb6, 0xbaeb, 0x078c, 0xb21e, 0x1bc3, 0x2e6e, 0x43d9, 0x41c8, 0x40e0, 0x4361, 0x4225, 0x4078, 0x37fa, 0xb065, 0x4390, 0x40bd, 0x418c, 0xbfcf, 0xba18, 0x40e6, 0x4039, 0xbf00, 0x1bf0, 0x441a, 0x4345, 0x42d6, 0x2c06, 0x238c, 0x4284, 0x413b, 0x4295, 0xb291, 0x21de, 0xaabe, 0xba14, 0x4253, 0x3c60, 0xb29b, 0xbf00, 0xbfbc, 0x4081, 0xb2d5, 0xb2f1, 0x4253, 0x04c1, 0xb055, 0x1f26, 0x1d48, 0xbf88, 0x401d, 0x2150, 0x40ff, 0x08dd, 0xbf45, 0x18ce, 0x4341, 0x4217, 0x40b9, 0x09c6, 0x43a1, 0xbac8, 0xb28f, 0x420c, 0x41ea, 0xb2f7, 0x0e48, 0xa96a, 0xb29e, 0x0631, 0x3cc9, 0x43cb, 0x3771, 0xb23c, 0x46f9, 0xa882, 0xb2d3, 0xba10, 0x4375, 0x17f2, 0x03ab, 0xb2da, 0x43b0, 0x43ef, 0xbfbb, 0x2f20, 0xb051, 0x1ffa, 0x42c9, 0x2d15, 0x2157, 0xa778, 0x432c, 0x421a, 0x4430, 0x43f6, 0x4271, 0x436f, 0xbf62, 0xbac5, 0x4288, 0x24a8, 0x4361, 0xad27, 0x433f, 0x3a4e, 0xba0e, 0x41ed, 0xb2b6, 0xb27a, 0xbf53, 0xa105, 0x1f58, 0xa2f2, 0x43ff, 0xb074, 0x1ac5, 0x421e, 0x4241, 0xb2b7, 0x2ad2, 0x40d9, 0xa5fd, 0x25d8, 0xb2b6, 0xa3c8, 0xb2bf, 0xb226, 0xba6d, 0x07ed, 0x43db, 0x4261, 0x40e7, 0x41a5, 0x4619, 0x3e15, 0xb25a, 0x427f, 0xbff0, 0x1a2f, 0xbfd6, 0xbac4, 0x42ba, 0x0922, 0x11a7, 0x439f, 0x44d0, 0x43ff, 0x3c72, 0xba33, 0x4386, 0x1f3a, 0x40c0, 0x418f, 0x411a, 0x4212, 0x4110, 0x428f, 0x447c, 0xb097, 0xbf77, 0x42b7, 0x0621, 0xbacb, 0x46ca, 0x43af, 0x01f4, 0xba26, 0x1815, 0x41e1, 0x4150, 0x4171, 0x43b4, 0xa937, 0x412f, 0x42df, 0x429a, 0x415e, 0x431c, 0xad19, 0x4282, 0x4073, 0xbf87, 0x43d0, 0xba54, 0x2db6, 0x4350, 0x4237, 0x4164, 0xbae5, 0xb22b, 0x0a33, 0x43f8, 0xbf33, 0x40f2, 0xb2bc, 0xb2a4, 0x40e6, 0x41c9, 0x326c, 0x41c8, 0x42e9, 0x42af, 0xba3b, 0x359d, 0x4109, 0x4028, 0xb269, 0xba16, 0xb2fc, 0x3acb, 0x4063, 0xbf16, 0x09a3, 0xb2d5, 0xba22, 0x4142, 0xbf8b, 0x1c3c, 0x3e0e, 0xb29c, 0x1164, 0x331c, 0x1ce8, 0xbff0, 0x43d7, 0x4137, 0x429b, 0x18a9, 0x4193, 0xb0cc, 0xba49, 0xba38, 0x411f, 0xb205, 0xbfa6, 0x4173, 0x4142, 0x43e0, 0x4089, 0x182e, 0xba01, 0x41ea, 0x3a41, 0x2930, 0x4025, 0x0594, 0x1556, 0x0b43, 0x46f2, 0x248c, 0x4340, 0xbf7b, 0x4257, 0x4328, 0xba28, 0x04e8, 0x42c7, 0x4207, 0x1d28, 0x40d1, 0x2167, 0x43a3, 0x284d, 0xbf05, 0xb2a2, 0x1605, 0xb276, 0x412d, 0x40f3, 0x378b, 0xba41, 0xbaf7, 0x42a3, 0x425d, 0x45b4, 0xbf9b, 0xa2c2, 0xba13, 0x410c, 0x45da, 0x4372, 0x4228, 0x0a0b, 0x4550, 0x4295, 0x06c1, 0xb2cd, 0x1487, 0x31fb, 0xbac1, 0xb200, 0xba13, 0x2c7f, 0x4449, 0x42f8, 0xbaf4, 0xb2b4, 0x24b1, 0x4166, 0xba16, 0xbf7c, 0x4171, 0x4311, 0x32bc, 0x4572, 0x3a43, 0x4690, 0xb2b3, 0xb037, 0x41c0, 0x4198, 0x4459, 0x41e3, 0x42e3, 0x2d4e, 0x05da, 0xb20a, 0x1cfd, 0xb28f, 0xb080, 0xa6d7, 0x4268, 0x243a, 0x43b9, 0xbf27, 0x14b0, 0x2410, 0xba40, 0x1f3a, 0x4008, 0xbfd9, 0x1b4b, 0x40a4, 0x2a6f, 0x14aa, 0x407b, 0xba5c, 0x41ec, 0x4245, 0x1b03, 0x4315, 0x4139, 0x2c74, 0xbfc0, 0x1806, 0x0ae6, 0xbae8, 0x3d2f, 0xb244, 0x0bac, 0xb245, 0x4304, 0xbf90, 0xbf9a, 0xb218, 0x408a, 0x42e1, 0x0fa0, 0xb06d, 0x43d5, 0x41b2, 0xb20e, 0x222f, 0xbfd3, 0x3289, 0xb2d2, 0x45e1, 0xb0ab, 0xbf8b, 0xba5c, 0x41a9, 0x41e2, 0x42ee, 0x446d, 0x41d9, 0x4362, 0x4265, 0xba7d, 0x43c5, 0x2cce, 0xb284, 0xb2fb, 0x40fa, 0x3ddf, 0x4188, 0xb251, 0xba1d, 0xbf26, 0x424b, 0x4566, 0x14d4, 0x3784, 0xbf70, 0x4225, 0xb22b, 0x1f12, 0x4022, 0xb061, 0x4145, 0x42d3, 0xb2c9, 0x2199, 0x42e5, 0x1a4d, 0x25c8, 0xbf56, 0x401e, 0x1c37, 0xba38, 0x1498, 0x4052, 0x4181, 0xba67, 0x41ab, 0x1bc7, 0x43d8, 0x4379, 0x42d0, 0x4211, 0x3a14, 0x00c0, 0x4068, 0x41dd, 0x4138, 0x40cb, 0xb21f, 0xb0c9, 0xba5c, 0x4004, 0xbfd5, 0x43bf, 0x4344, 0x06d0, 0x40ea, 0x2d6a, 0xbfe4, 0x42bc, 0x40a0, 0x40c6, 0xaa16, 0x42bd, 0x421a, 0xb2d7, 0x41ca, 0xb016, 0x43ef, 0xae2d, 0x4341, 0xb0c0, 0xba1b, 0x40c7, 0xb0cd, 0x3c18, 0x2ab4, 0x41d2, 0x0506, 0x2144, 0x183c, 0xba30, 0xb266, 0xb2e5, 0xbf90, 0xbf4a, 0xa979, 0x4300, 0xb26e, 0xbfd0, 0xba66, 0x13dc, 0xb28e, 0x1a28, 0x4158, 0x43ab, 0x4075, 0x40f2, 0x4593, 0x198f, 0x38c5, 0x4061, 0xbf23, 0x3e56, 0xa0ef, 0xba50, 0x4433, 0xba0b, 0xad1b, 0x1df6, 0xbad9, 0xb253, 0x40cc, 0x424c, 0x1a18, 0x1b36, 0x186b, 0xbfdf, 0x4167, 0x0075, 0x431c, 0x400c, 0x3e22, 0x434d, 0xba3b, 0x4388, 0x2f78, 0x407f, 0x4254, 0x1610, 0xba4b, 0x43f8, 0x4089, 0xb231, 0x4050, 0xb29d, 0x0c5a, 0x3afd, 0x42e8, 0x1c7a, 0xb241, 0x0cad, 0xb268, 0xbfc0, 0xbf6a, 0xb2cc, 0x37b0, 0x46eb, 0x4129, 0x1e5b, 0xb216, 0x0748, 0x41d1, 0x4308, 0x45ac, 0xb2a5, 0x43d5, 0x4049, 0x4076, 0xb28d, 0x2611, 0xb26d, 0xba2e, 0x34d4, 0xbafb, 0x3761, 0x417a, 0x4156, 0x4575, 0xb210, 0xbf4d, 0x42ba, 0xba26, 0xa039, 0x422c, 0x4357, 0xaee4, 0x41ab, 0x43eb, 0x1d12, 0x434c, 0x1818, 0x0a20, 0xb208, 0xb2a2, 0x4187, 0xba69, 0x43de, 0xa5ce, 0xbfac, 0xb0db, 0xb274, 0x4347, 0x4151, 0x1f64, 0x1c6b, 0x1db7, 0xbad5, 0x1e0a, 0x198e, 0x416f, 0xbacf, 0x42fe, 0x4173, 0x183b, 0x44f4, 0x4075, 0x43a0, 0xbaf0, 0xbf41, 0x1e71, 0x39a2, 0x1dc5, 0xb212, 0x0159, 0xbaea, 0x1fdd, 0x42d7, 0x4453, 0xb2dc, 0x0817, 0x4698, 0xba22, 0xbf58, 0x04db, 0xb216, 0x1b55, 0x42e8, 0xba7c, 0xbf88, 0xa1f0, 0xba12, 0xb21f, 0x2628, 0x4091, 0x4554, 0x0c86, 0x4098, 0xb0fb, 0x43e1, 0xbaed, 0xb2c1, 0x1f65, 0x4019, 0x4365, 0x0c81, 0x40dd, 0xaf49, 0x40f2, 0x40a3, 0xba56, 0x4378, 0xa9c0, 0xbf81, 0xba7f, 0x404b, 0xaec3, 0x3eec, 0xbadf, 0xacb7, 0x434a, 0xba10, 0xbaeb, 0x1ea3, 0xbf19, 0x413d, 0xb07a, 0x1d45, 0x42ff, 0xb08d, 0x1a6b, 0xa7c4, 0xa3d1, 0x40bb, 0xba7f, 0x460a, 0x45cb, 0x4490, 0xb097, 0x40f8, 0x00c0, 0x3d1a, 0x42bb, 0x30ae, 0x1c4f, 0xad13, 0xbafd, 0x40c3, 0x40e1, 0x4046, 0x434e, 0xbf3a, 0x185c, 0x409a, 0x26fc, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6e009d47, 0x4404ee01, 0xc475426e, 0xc7651e4f, 0x99397e2c, 0xe2311d1e, 0x13b3b0d5, 0x94ee1dcc, 0x8a96e390, 0xfb8d253a, 0x3cd58f7e, 0xf938ad79, 0x61646a13, 0x14d0ae7f, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x000000ae, 0x00000000, 0x14d0af83, 0x00000000, 0x00000000, 0xffff84af, 0x00000000, 0x14d0af84, 0x14d0b083, 0x00001344, 0x00000000, 0xf938ad79, 0x61646a13, 0x14d0addb, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb08d, 0xba58, 0x1eea, 0x4241, 0x439f, 0x27f1, 0x40eb, 0xb27f, 0x1bed, 0x407c, 0x4327, 0x41ea, 0x27fd, 0x410a, 0xbf6d, 0x4038, 0xa806, 0xba0f, 0xb265, 0xb22d, 0x1b44, 0x1c14, 0x431d, 0xaad5, 0x3ee7, 0x4034, 0x2d0b, 0x2dcc, 0x4334, 0x3e1c, 0xbf00, 0x11eb, 0xb2db, 0xa20c, 0xb058, 0xbfab, 0x4390, 0xb042, 0xb051, 0x2fcb, 0x415c, 0x0a57, 0x408c, 0x1d6e, 0xa072, 0xba55, 0x2581, 0x447b, 0x43a5, 0x440b, 0x1e63, 0xb2cc, 0xa334, 0xba69, 0xabd9, 0x1d2d, 0xbf62, 0x4140, 0x024e, 0xb2ac, 0x40ac, 0x4567, 0xba10, 0x1eb6, 0x3178, 0x0a5f, 0xb2bc, 0x40b0, 0xbfd4, 0x4285, 0x0c35, 0xb21e, 0xbacd, 0x1a41, 0x3ab5, 0x117a, 0x4469, 0xb2a3, 0x2dc3, 0x416d, 0x4541, 0xba7f, 0x1a93, 0x4389, 0xbf57, 0x301f, 0x4320, 0x28ae, 0x4020, 0xbac7, 0x1192, 0xbf45, 0x42ba, 0x43c3, 0x42b2, 0x0233, 0x1fa2, 0xb0b2, 0x41f2, 0xbaf6, 0xbf92, 0x0ec4, 0xb28d, 0x406b, 0xbff0, 0x403f, 0xba61, 0xb204, 0xbaeb, 0xba0b, 0x402a, 0x1ee1, 0x431b, 0x4331, 0x2513, 0x2108, 0xbf62, 0x4257, 0xa533, 0x3fd8, 0x42c7, 0x2195, 0xb2df, 0x42ab, 0x2830, 0x4373, 0xb286, 0x3afb, 0x42a2, 0x2e11, 0x4188, 0xb0c4, 0xba0e, 0x1d2e, 0x4158, 0xa422, 0x1dec, 0x417a, 0xb239, 0x43c5, 0x4074, 0xbf1c, 0x459e, 0x417c, 0x1b2b, 0xb2d6, 0xb296, 0x43ca, 0xbfd6, 0x449d, 0x23aa, 0x4198, 0xb267, 0xa248, 0xbf00, 0x4368, 0xb04a, 0x0734, 0x1ae6, 0x42d9, 0x414f, 0x1ed1, 0x4291, 0x1cde, 0xba3d, 0x4103, 0xa7df, 0x4586, 0xb257, 0x2302, 0x46a2, 0x43e6, 0xba41, 0x4128, 0xb2dc, 0x268d, 0xb235, 0xbfb7, 0x183a, 0x432d, 0xb0be, 0x40c4, 0x46c2, 0x42ae, 0x1cef, 0x16a2, 0x432a, 0x034c, 0xa8cc, 0x1aa9, 0x4635, 0x33d5, 0x42be, 0xb225, 0x4054, 0x1856, 0xb02b, 0x4683, 0xbfd6, 0x3dcb, 0x45ad, 0x4149, 0x42ec, 0xa5a3, 0xbfb0, 0xb04d, 0xb266, 0x405c, 0x40ef, 0xb23d, 0xb2fe, 0x432d, 0x41fe, 0x43ad, 0xb268, 0xa0d6, 0x41d9, 0x023a, 0xb291, 0x4397, 0xb2fb, 0x035e, 0xbf3f, 0x43aa, 0x4371, 0x3540, 0x4013, 0x42bc, 0xba4d, 0xb04e, 0x4156, 0x43be, 0x40b7, 0x40a9, 0x409c, 0x4288, 0x415e, 0x0f3e, 0xb009, 0xbfd7, 0xb0d2, 0x4263, 0x3228, 0x26dd, 0x1b78, 0x4170, 0x408e, 0xbfc7, 0x1ed8, 0x459d, 0x4199, 0x41af, 0x4340, 0x434a, 0x43a6, 0xbfc1, 0x2986, 0xbadc, 0x1d21, 0x00cc, 0x42e0, 0xae51, 0xa7dc, 0xb24e, 0x42fb, 0x1ba5, 0x1a40, 0x1ce0, 0x1e2b, 0x355f, 0x4357, 0x40c2, 0xba61, 0x4182, 0x4383, 0x430e, 0x1593, 0x41d6, 0xb0fa, 0x410f, 0xb21c, 0x4075, 0xbf8f, 0xb2c4, 0x4285, 0x4003, 0x1281, 0x414a, 0x408b, 0x403e, 0x2363, 0x412a, 0x430e, 0xbf45, 0x2282, 0xb24a, 0x330a, 0x4145, 0x4285, 0x43ed, 0x4031, 0x18ea, 0x4227, 0xbf80, 0x2468, 0xba52, 0x41ce, 0x4659, 0x4095, 0xb28c, 0xa9d4, 0x4439, 0x1b5b, 0x1349, 0x41d5, 0xbac3, 0xb09a, 0xba48, 0x4216, 0xbfa1, 0x0eec, 0x205a, 0x1cee, 0x44f9, 0x43c9, 0x0ee8, 0x0e50, 0x4258, 0x4029, 0x403f, 0xb226, 0x42bc, 0x1ba6, 0xbf48, 0x42a7, 0x40f2, 0x4260, 0x4499, 0xbfd3, 0x38e7, 0x40f2, 0x2087, 0x4189, 0x2243, 0x4111, 0x3fa8, 0x40bb, 0xb0be, 0x4026, 0x1b74, 0xbf0e, 0xb047, 0xb20e, 0x1961, 0x4241, 0x4063, 0xba4f, 0x40dd, 0x411b, 0xafbd, 0x39a3, 0x4254, 0x4262, 0x41e0, 0xbacd, 0x2264, 0x40bc, 0xa651, 0xb20c, 0xba0d, 0xb261, 0x4484, 0x432a, 0xbfdf, 0xb01a, 0xb20e, 0x2256, 0xbac1, 0x1ac5, 0xb2eb, 0xb2ee, 0x2927, 0x009a, 0x4291, 0x3575, 0xb21a, 0xb2d1, 0x1a67, 0xbaff, 0x4115, 0x42ef, 0xbf26, 0x0fa4, 0x415d, 0xb253, 0x010c, 0xb0aa, 0x2d7b, 0xb020, 0xb0da, 0xae00, 0x2f87, 0xb095, 0x0ca0, 0x42f8, 0x4037, 0x1c6f, 0x436b, 0x42b4, 0x1d3d, 0x3251, 0x43ff, 0x411a, 0x4394, 0xbf71, 0x1ac3, 0x42b2, 0x37b3, 0x3db9, 0x4388, 0xb080, 0x4314, 0x406c, 0x40ad, 0xb0ac, 0xb047, 0xbaff, 0x4091, 0x4372, 0xb29b, 0x4119, 0x45ab, 0x405d, 0xbf95, 0x3f6d, 0x1d73, 0x1896, 0x4074, 0xbad7, 0x211b, 0xa48f, 0x436d, 0x4181, 0x1e91, 0x1d3a, 0x42d0, 0xb25b, 0x43c6, 0xba43, 0xb284, 0xbfe0, 0x1eed, 0x0218, 0x40a2, 0x42a8, 0x40a7, 0x1c26, 0xbf00, 0xbf38, 0x447c, 0xbae0, 0x40d9, 0x1c9a, 0x42ec, 0x43f7, 0x386f, 0xbf59, 0x189b, 0x4037, 0x4323, 0xb281, 0xba6c, 0x1a55, 0xb2a2, 0x4218, 0x41d1, 0x4215, 0x383f, 0x429f, 0xbf89, 0x2d17, 0x46b9, 0xb0d1, 0xbf70, 0xba01, 0xbf2e, 0x1f89, 0x45a3, 0xb07c, 0xadd1, 0x42dd, 0x400c, 0x1adc, 0x43b9, 0x4072, 0x418c, 0x0a53, 0xad90, 0x1f17, 0x418f, 0xbacf, 0x1fc4, 0xa559, 0xba37, 0x1b9c, 0x461c, 0x42c8, 0x401e, 0x1971, 0x43b5, 0xba29, 0xa499, 0x4366, 0xbf7d, 0x1b05, 0xbadb, 0xbad4, 0x1b87, 0x43f7, 0x45b4, 0xa2ab, 0x43a8, 0xb08b, 0x18c0, 0xb2fe, 0x435d, 0x0129, 0x4200, 0x43fa, 0x33ac, 0xb2f5, 0x411c, 0x423b, 0x418e, 0x3876, 0xb22f, 0xbad0, 0x4464, 0xbf0e, 0x24f1, 0xb21c, 0x42dc, 0x42c1, 0x40f5, 0xa9ad, 0x4283, 0xb2a1, 0x432e, 0xbfb0, 0x428b, 0x42d3, 0x08fd, 0x0f6c, 0x437c, 0x0f32, 0x43ea, 0x0768, 0x4192, 0x4001, 0x01af, 0x22ab, 0x43df, 0x41d1, 0xa87c, 0x1cc1, 0x0e0e, 0xbf55, 0x1b17, 0x4265, 0xb0e6, 0x4359, 0x43ca, 0x43bd, 0x40ff, 0x4110, 0x1cf1, 0x4037, 0xb254, 0x1957, 0xbf41, 0x0fec, 0x45b3, 0xb21a, 0x41e8, 0xb21e, 0x417f, 0xaf50, 0x0c9d, 0x4395, 0x4122, 0x43ef, 0xb01c, 0x42fa, 0x41da, 0x43a3, 0x41c8, 0x3d6b, 0xb06d, 0x41c4, 0xba07, 0x391d, 0x4214, 0xb2f7, 0x44dc, 0xa011, 0x42fa, 0x4483, 0x1ddc, 0xbf7a, 0xb24d, 0x4270, 0x269c, 0xba2a, 0x435a, 0xb29d, 0x1ce6, 0x1852, 0x093e, 0xb0ed, 0xbaf1, 0x1d00, 0x02b5, 0x17ec, 0x42d8, 0xba16, 0x4624, 0x26af, 0x41b1, 0x1631, 0xb2fe, 0x4361, 0x1058, 0x41db, 0x1992, 0xbf1e, 0x41d8, 0x40c2, 0x43a3, 0x42ba, 0x41b7, 0x1d22, 0x43d5, 0xb213, 0x0005, 0x4004, 0x4171, 0x4038, 0x4364, 0x3420, 0x4135, 0x445c, 0x42f6, 0x4282, 0x4110, 0xbf5a, 0x3781, 0xb2f5, 0x40f8, 0xb2bc, 0xb264, 0x4591, 0x1812, 0xb06d, 0xba2f, 0xbf43, 0xa219, 0x46c0, 0xb2f3, 0x40f0, 0xbf5d, 0xbae7, 0xa3f0, 0x423e, 0xb246, 0x36e6, 0x402e, 0x400e, 0xb2af, 0xbfde, 0xba53, 0x085b, 0xab40, 0x43ac, 0x431a, 0x2778, 0x1da4, 0xb029, 0x14e4, 0x439f, 0x15ca, 0xa275, 0x1f63, 0x41ad, 0x3dee, 0xbf97, 0xbff0, 0x40e4, 0x448d, 0xb2d7, 0xbf8a, 0x41e5, 0xba2e, 0x2351, 0x1b2d, 0xbfe0, 0x4261, 0x414d, 0xbf55, 0xb28e, 0x1bb7, 0x439d, 0x41f4, 0x04a2, 0x040e, 0xb2a2, 0x4313, 0x1df1, 0xbac7, 0x41cc, 0x41d6, 0xbfb5, 0x432e, 0x42c0, 0x1cdd, 0x1ccb, 0xbf07, 0xb052, 0x1ead, 0x4281, 0x431d, 0x4365, 0x4206, 0x1b51, 0x43b9, 0x16d4, 0x0878, 0xbf89, 0x40d5, 0xa72b, 0xbad8, 0x3d59, 0x43b9, 0x42ff, 0x454d, 0xbae8, 0x11ea, 0xb2d7, 0x1455, 0xb2bc, 0xb29d, 0xba69, 0x461c, 0x19b1, 0xb282, 0xb054, 0x08a5, 0x45cc, 0x40ec, 0xbf9b, 0x0e9d, 0x1b02, 0x4099, 0x4080, 0x4322, 0x1313, 0x236c, 0x19cb, 0xa05e, 0x4350, 0x4299, 0xba45, 0x1afa, 0x1a45, 0x41f1, 0x058f, 0x18aa, 0x4190, 0xb0b6, 0xbfc8, 0xba6c, 0xb019, 0x4260, 0x40cb, 0x40e2, 0x1c47, 0xae93, 0x340d, 0x0e55, 0xba34, 0xaacd, 0xbfae, 0x416d, 0xb0ef, 0x4056, 0xba07, 0x41eb, 0x4490, 0xba61, 0x402c, 0x469a, 0x3dbe, 0x4015, 0x4560, 0x439c, 0x2497, 0x3ceb, 0x4196, 0xbf2d, 0x3df9, 0xb029, 0x1aa2, 0x1f04, 0xbfb3, 0xb006, 0xb001, 0x4067, 0x405f, 0xbaf8, 0xba17, 0xbf6f, 0xb2a3, 0x32e4, 0x27be, 0x4297, 0x3a02, 0x438b, 0x422b, 0xb0bc, 0x437e, 0x1f3a, 0xba0e, 0x40fa, 0xbf00, 0xa05d, 0x4052, 0x4302, 0x3ae8, 0xb207, 0xb01a, 0x4317, 0x440d, 0x44ca, 0xbad5, 0xa59a, 0x462c, 0xb22a, 0xa1e3, 0x46c4, 0x417a, 0xbf14, 0xbaec, 0xb296, 0x43c8, 0xb213, 0x2bbb, 0xb20d, 0x0e03, 0x43a6, 0x139c, 0xba1a, 0xacf0, 0x406e, 0x0198, 0xbf60, 0x400f, 0x428d, 0x40dc, 0x1881, 0xba24, 0x4222, 0xb0f8, 0x4380, 0x4093, 0x40a8, 0x3f1c, 0xbf49, 0xb2ae, 0xb238, 0x42d6, 0xb20c, 0x24d6, 0x1e47, 0xbf1b, 0x396a, 0x1a86, 0x4184, 0x402e, 0x3053, 0xb26c, 0x0b64, 0x4109, 0x46c2, 0x424b, 0xba29, 0x1c0e, 0x41bd, 0x4459, 0xb21f, 0xb20a, 0x43af, 0x424a, 0x432c, 0xb0db, 0x41c0, 0x4454, 0xbf88, 0x37f0, 0x4129, 0x4137, 0x436b, 0x44b8, 0x4043, 0x310e, 0xba09, 0x258e, 0x40a9, 0x4205, 0x4269, 0xba39, 0xb011, 0xbf00, 0x1e11, 0xba64, 0x1c52, 0x4113, 0x2228, 0xb2bf, 0xb058, 0x2bfe, 0x430f, 0x1d24, 0xbf0e, 0x43f1, 0x4113, 0xb268, 0x189e, 0x43a1, 0xb21a, 0x4193, 0xb287, 0x346a, 0x419b, 0x42a4, 0xad64, 0x41ac, 0x459d, 0x30a2, 0x2363, 0x40ad, 0x462f, 0x459c, 0x3579, 0x298b, 0x4105, 0xbfab, 0x3d00, 0x42dd, 0x4390, 0x1c75, 0x4156, 0x1cb5, 0x4349, 0x43f2, 0x45d6, 0x40f5, 0x4328, 0x41d2, 0x4165, 0xb00f, 0x4023, 0xba7b, 0xb0d4, 0x41f4, 0xbff0, 0x4085, 0x40a0, 0xbf78, 0x1892, 0xb067, 0x4055, 0x4094, 0x42d6, 0xba35, 0x42b0, 0x41e8, 0xac04, 0x42d6, 0x41aa, 0xba6c, 0xb273, 0x1c21, 0x40cf, 0x4492, 0x46d5, 0x1965, 0x4221, 0xb2c4, 0x43e7, 0x415a, 0x432f, 0x407f, 0xbf8c, 0xb283, 0x46a5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd25485a3, 0x65f9ab76, 0x3fac21bc, 0xb38c4c4b, 0x9ae6ab78, 0xff30a4a6, 0x01b8bb18, 0x9f4c9ead, 0xa09d6abf, 0x3ea9cd0d, 0x60d7d350, 0x04406247, 0x923282a2, 0xf465cdc9, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00280000, 0xd7ff6025, 0x00000028, 0x00000000, 0x28280000, 0x00000028, 0x00000000, 0x55f83e89, 0x3eaa3cd7, 0x2df79e85, 0xb55ae7e5, 0x55f83e88, 0x00000000, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbad5, 0x1d59, 0xa4c4, 0x1a8d, 0x1b22, 0xa8c9, 0x45cc, 0x422b, 0x3e2e, 0x4264, 0x4080, 0x3475, 0x4121, 0x41b2, 0x4344, 0xa434, 0x45f0, 0x1e00, 0xbf34, 0x4409, 0x4310, 0x43ce, 0x43ed, 0x4044, 0x4019, 0x41dc, 0x1ea8, 0x4076, 0xbaf0, 0x42b2, 0x41ce, 0x406e, 0xb2e7, 0xbfc0, 0xb202, 0x1d55, 0xbf74, 0x42b3, 0xa20f, 0x41e6, 0x1551, 0x29e3, 0x417b, 0x4037, 0x2314, 0x4311, 0x4163, 0xb2d6, 0x416c, 0xadaa, 0xb258, 0x3667, 0xbfcd, 0x42b1, 0x42ab, 0xbfb0, 0x4591, 0xa1d1, 0xb05f, 0x442c, 0x1b90, 0x42c0, 0xb29a, 0x43e5, 0xb283, 0xba53, 0xb228, 0xbad6, 0x4031, 0x185e, 0xba00, 0xb2d1, 0x4298, 0xb2c9, 0xbfbd, 0x4229, 0xb297, 0xb016, 0x42a1, 0x4022, 0x120d, 0xba0f, 0x1dce, 0x435a, 0x4039, 0x183a, 0xbf70, 0x3a82, 0xa4d5, 0x250f, 0x4341, 0x413b, 0x4209, 0x467a, 0xbfa0, 0x1dc9, 0xbad6, 0x4106, 0x43dd, 0xbf8b, 0x45a0, 0x4253, 0x44d5, 0x1d62, 0x4128, 0xaaf0, 0x40be, 0x4373, 0xba76, 0x4314, 0xbf35, 0x400c, 0x019c, 0x43aa, 0x42dd, 0xba6c, 0xb23f, 0x4437, 0xbfd8, 0x4419, 0x2e1a, 0x4270, 0x429f, 0xba18, 0x2478, 0x3fa1, 0x40b6, 0x4130, 0x43c6, 0xbf9f, 0x401f, 0x01ae, 0x4264, 0xb264, 0x41db, 0xbac5, 0xbfd6, 0x4678, 0xb20e, 0x4244, 0x44c5, 0x1729, 0xba6d, 0x418b, 0x46a2, 0xb0bf, 0x38e1, 0x4386, 0xba49, 0x4387, 0x1dd4, 0x1e09, 0x40dd, 0x45e6, 0x40c6, 0xbf81, 0x05a7, 0xb2a8, 0xba43, 0x0104, 0x0c94, 0x42d5, 0x4153, 0x407a, 0xbfb6, 0x406f, 0x4146, 0x1c98, 0x43cf, 0x40c8, 0x40bf, 0x432b, 0x1ab7, 0xbf0b, 0x0f94, 0x42bb, 0xba05, 0xba0f, 0x43dd, 0x40d1, 0xb28d, 0x1d03, 0xb02f, 0x43f1, 0x430f, 0xbf19, 0xb05c, 0xba78, 0x423d, 0x41d4, 0x44d0, 0xa90f, 0x120b, 0x4649, 0x40b5, 0x3754, 0x42d1, 0x40a8, 0x2a90, 0xb20b, 0x41e6, 0xb066, 0xb290, 0x4188, 0x40c2, 0xb273, 0xbf41, 0x00f2, 0x427b, 0x1c07, 0x4654, 0xab5e, 0x03e3, 0x441a, 0x40c2, 0x4273, 0xb0a5, 0xba56, 0x45bd, 0x42a5, 0x4369, 0x049c, 0x23de, 0xbf76, 0x1a8a, 0x424c, 0xbff0, 0xbfc0, 0x1e0a, 0xb2c1, 0x4238, 0x4388, 0x4485, 0x34b5, 0xa687, 0x4235, 0xb295, 0xb0a0, 0x4175, 0x4280, 0xadc3, 0xa75e, 0x467c, 0x4169, 0xb20c, 0x42cb, 0xbf12, 0x1ad9, 0x1acf, 0x46e8, 0xba62, 0x4180, 0x1e87, 0x4028, 0x093f, 0x1810, 0xb015, 0x42d2, 0x1ba6, 0xbf04, 0xb0d5, 0x4156, 0x43e8, 0x4246, 0xbac6, 0x4204, 0xb272, 0xba2f, 0xb24e, 0x422c, 0x40e9, 0x43ec, 0x4019, 0x41a1, 0x1cad, 0x007a, 0x41b7, 0x4231, 0xbfc1, 0x3779, 0x1010, 0xbafe, 0x4311, 0x45b9, 0x469a, 0x41fe, 0xb205, 0x17e7, 0x0096, 0x166f, 0x4090, 0xb260, 0x4241, 0xb0ad, 0x1c8e, 0xbf4f, 0xb2a7, 0x432d, 0xba6a, 0x4358, 0xb26d, 0x429c, 0x40c8, 0x1de3, 0x244f, 0xa9e5, 0x412a, 0x1bff, 0x40b1, 0xb2c6, 0x469b, 0x0d3c, 0x1ef1, 0xbfb6, 0xa11f, 0x4011, 0xb298, 0x0847, 0x400c, 0xb255, 0xb0a0, 0xbae9, 0xba33, 0x413e, 0xb2a9, 0x40c4, 0x199a, 0xb2b8, 0xbf8b, 0x437d, 0x18dc, 0x19c1, 0x43ee, 0x0ce7, 0x0e99, 0x287c, 0x2f2a, 0x1e51, 0xbf90, 0xb217, 0x41a9, 0xb230, 0xb02b, 0xbf7b, 0x4319, 0x12d0, 0x4279, 0xba78, 0x1950, 0xbf90, 0x0410, 0xb08d, 0x260b, 0xbfa8, 0x3d18, 0x4338, 0x4185, 0x4061, 0x4042, 0x41ad, 0xbaef, 0x419e, 0x194f, 0x249b, 0xb2c1, 0xbf19, 0x1f07, 0x4370, 0x1cd7, 0x439a, 0xab32, 0x4256, 0xbf2f, 0x43cb, 0x412b, 0x41a7, 0x1b51, 0xb20f, 0xb03a, 0x4000, 0x3858, 0x3170, 0x2475, 0xae13, 0xb231, 0x4498, 0xbf9a, 0x45ad, 0x404e, 0x2df8, 0xb25b, 0x3db3, 0xa424, 0xb211, 0xb208, 0x43fb, 0x4241, 0x1937, 0x1c73, 0x40f4, 0x0729, 0x41cd, 0x2eff, 0x4288, 0xb0cc, 0x1be4, 0xbafc, 0xbf21, 0x1150, 0xba5b, 0x41ec, 0x1e5d, 0x4324, 0x430d, 0x189e, 0x416b, 0xb0ed, 0x1958, 0x43c4, 0x439c, 0x40aa, 0x42e3, 0xba03, 0x4255, 0x4014, 0x4583, 0xb2b2, 0xb278, 0xb233, 0x2f6f, 0x31e0, 0x41bb, 0xbfce, 0x439f, 0xa48e, 0x42cf, 0x434c, 0xb25d, 0x4317, 0xb281, 0x439b, 0x4098, 0x4201, 0xae51, 0x4281, 0x43c0, 0x417b, 0xb014, 0x4561, 0xba03, 0xb26b, 0x1e97, 0xb2f8, 0x4131, 0x4191, 0x40fe, 0xbf78, 0x227e, 0x4692, 0xba49, 0x4175, 0x4495, 0xb252, 0xba21, 0x43d7, 0x4317, 0x214c, 0xba01, 0x46d9, 0x41a8, 0x4023, 0xbf4b, 0x28eb, 0xb055, 0x431e, 0x40cb, 0x40d2, 0x1bce, 0x1a62, 0x4004, 0x438d, 0x1f3c, 0xb20b, 0x4025, 0x410f, 0xa075, 0xb262, 0xbf37, 0xbade, 0xb264, 0x1a2d, 0x19a6, 0x1811, 0xbfdd, 0xbad4, 0x4331, 0x2bc6, 0x42d3, 0x42ff, 0x23d1, 0x442b, 0x28f2, 0x1597, 0xbfa5, 0xb0ad, 0x41fc, 0x446c, 0xba71, 0x45d1, 0x24a8, 0x1f6f, 0xba30, 0x4228, 0x40dc, 0x402e, 0x43d7, 0xae6c, 0x403b, 0x4027, 0x4391, 0xbf69, 0xb2c4, 0x45ec, 0xba6a, 0x406c, 0x46a5, 0xba6a, 0xb2dd, 0xbad4, 0x41e5, 0x40a9, 0x41e5, 0x1abf, 0xbf74, 0x1c83, 0x42d6, 0x43d1, 0xba0f, 0x43f6, 0x042d, 0x4341, 0x42b2, 0xbf14, 0x1fe1, 0x4275, 0x4128, 0x401c, 0xb20d, 0x02b4, 0x4493, 0x41ac, 0xb21a, 0x417f, 0x4283, 0x2bd4, 0x40a7, 0xb293, 0x4028, 0xb2d5, 0xbac8, 0xb250, 0xbf2f, 0x426d, 0x424e, 0x433a, 0x1e87, 0x4352, 0xb295, 0xbadd, 0x4369, 0x05ca, 0x415d, 0xb215, 0x4247, 0x4220, 0x3a43, 0x0025, 0x4166, 0xbac9, 0xba2a, 0x4084, 0x0d4f, 0xb07c, 0x43b7, 0x45e5, 0x42de, 0x401b, 0x4382, 0xbf6b, 0x45ae, 0x279c, 0x4117, 0x4138, 0x0998, 0x425f, 0xbf24, 0x08b8, 0xb016, 0x1f7d, 0x2512, 0x19cd, 0xba6d, 0x42d8, 0xba0c, 0x30ef, 0x4407, 0x2342, 0x0c6e, 0x432c, 0x41d3, 0x13e8, 0x1350, 0x363a, 0xb072, 0x3631, 0xbf6f, 0x1a7d, 0x2ed0, 0x43d7, 0x28be, 0x3dc7, 0x15f5, 0x4118, 0xba6c, 0x4604, 0xa826, 0x423a, 0xb2bc, 0xb201, 0xbf0d, 0x41bc, 0x1afd, 0xb061, 0x3a1d, 0x4126, 0x42fe, 0x2aa7, 0x4165, 0xba5b, 0xb078, 0xbfc0, 0x4351, 0x42e4, 0x4183, 0x4211, 0x4651, 0x40b3, 0x4328, 0x43d0, 0x207b, 0xb24b, 0x1018, 0xae51, 0x18f3, 0x4037, 0xba1d, 0x1529, 0x3c02, 0xbf14, 0x0859, 0x40a1, 0x1daf, 0xb296, 0xb0b8, 0x2b0a, 0x4221, 0xbacc, 0x185d, 0x402d, 0x18ff, 0x42a7, 0xb28b, 0xbf6e, 0x43af, 0x419c, 0x3a08, 0x4230, 0x2623, 0x1903, 0x0462, 0x0149, 0xba1a, 0x1eba, 0x436d, 0xb0b8, 0x40df, 0xb279, 0xba60, 0x44f1, 0xba16, 0x4085, 0xb2c5, 0x1e6e, 0x40e0, 0xbf8c, 0x422a, 0x42ea, 0x42fe, 0x4544, 0xbf53, 0x430b, 0x3121, 0x4012, 0x4291, 0xb007, 0xb223, 0xb2a1, 0xb2ce, 0x09bd, 0x1d76, 0x4171, 0x40d3, 0x08b5, 0xacc9, 0x2e88, 0xb2dd, 0xb2e7, 0x42f5, 0x408d, 0x41a0, 0x41ef, 0x1c3f, 0x464e, 0x2766, 0xbf4a, 0x4087, 0xafb7, 0x461c, 0x43d1, 0xa153, 0x34a3, 0xbfa0, 0xba05, 0x0f87, 0x4095, 0xbfbf, 0x4252, 0x4160, 0x41d3, 0xb071, 0x09c2, 0xb05b, 0x4368, 0x40ab, 0x07d6, 0x45dd, 0xb047, 0x43b8, 0x42f8, 0x23c1, 0x19d1, 0x3c53, 0x42a3, 0x2e39, 0x4208, 0x04ed, 0x411c, 0x29ec, 0x43ac, 0xa6b8, 0x403b, 0xba30, 0xbf11, 0xb2fb, 0x1810, 0x05a6, 0x40b2, 0xa664, 0xbfaa, 0x434c, 0xb280, 0x2bd7, 0x22b9, 0x42ca, 0x0fe6, 0x2a39, 0xa493, 0x40ce, 0x424a, 0xba10, 0x42e2, 0xa50a, 0x43b9, 0x456b, 0xb2a1, 0x432b, 0x43ee, 0x05a5, 0x4142, 0x4417, 0x1ccf, 0x40a5, 0x43d8, 0xbfa1, 0xb268, 0x4092, 0xb20f, 0x401f, 0x40ea, 0x1b3d, 0xbaed, 0x0264, 0x0b78, 0x42fb, 0x0836, 0xb2ed, 0x424a, 0x40d1, 0x2cfd, 0x101f, 0x4293, 0x46a9, 0x46cb, 0xbfa3, 0x4058, 0x41d6, 0x40c1, 0xba3a, 0x45dc, 0x11ed, 0x3aa0, 0x468d, 0xbf80, 0xa4dc, 0x1936, 0x1766, 0x280e, 0x1ae8, 0xbfde, 0x33a9, 0x17d0, 0xa82c, 0xba1a, 0x43d1, 0x4310, 0x41b3, 0xb21a, 0x4270, 0xb285, 0x4055, 0xba60, 0x1c37, 0x224c, 0x43ce, 0x43c3, 0x234e, 0x43ca, 0xb213, 0xb29c, 0x4294, 0xba5c, 0xb2b3, 0x2d4b, 0xa6db, 0xbf1e, 0x4240, 0xa2f2, 0x138e, 0x1d24, 0x4235, 0x4569, 0x432e, 0x41ef, 0xa704, 0x25b2, 0x13bc, 0xb0eb, 0x41e4, 0x401e, 0xa704, 0x41ad, 0x41dd, 0x4298, 0x1b03, 0x423c, 0xb248, 0x405c, 0x0b80, 0x40c7, 0xba75, 0x43b0, 0xbfb6, 0x10b0, 0x41ec, 0xa1b4, 0xb26b, 0xb295, 0x412c, 0x44e5, 0x2796, 0x2eb7, 0x4165, 0xbfc9, 0x42b4, 0xac49, 0x40da, 0xba7f, 0x40c1, 0xbf00, 0x4376, 0x0fd1, 0x1a0f, 0x4356, 0x41ac, 0xb00e, 0x45f3, 0x2458, 0x333c, 0x0171, 0xb273, 0x4304, 0xbfe2, 0x45cb, 0xa2d9, 0x40d8, 0x4348, 0x1760, 0x412f, 0x42d4, 0x40a9, 0x424f, 0xbf18, 0xb07a, 0x40b1, 0x00cf, 0x43fa, 0x4299, 0x4367, 0x222f, 0x1212, 0x4401, 0x4388, 0x2451, 0xb29b, 0x4319, 0xb210, 0x420f, 0x40fd, 0x45b0, 0x32f6, 0x43b9, 0x1ca3, 0x1863, 0xbfac, 0xb083, 0x4385, 0x42d0, 0x422c, 0x1fe8, 0x2eaa, 0x1075, 0xb2a0, 0xbf0b, 0x42d9, 0xa9de, 0xb093, 0x42ce, 0x41eb, 0x40a4, 0x4242, 0x43df, 0xb254, 0xa2a0, 0x421b, 0x4305, 0xbf55, 0x10f1, 0xabe9, 0x41fb, 0x4333, 0x2b35, 0x405e, 0x1e82, 0x2a61, 0xbf46, 0x4234, 0xb26c, 0x1935, 0xbad9, 0x4164, 0xb241, 0x43fe, 0xb25b, 0x1df3, 0x4304, 0x1a0c, 0xba35, 0xbf0e, 0x336a, 0x3265, 0x068a, 0xba61, 0x06fd, 0x1c50, 0xb2c0, 0x4124, 0x4418, 0x41c6, 0xabd2, 0x429d, 0xba40, 0x4225, 0xb0e6, 0xb2c8, 0x4148, 0xb2b2, 0x400d, 0x1bc1, 0x24ff, 0xbf6c, 0x43f2, 0xba30, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xb95fc095, 0x46c9af8c, 0x44f999c1, 0xf75c725c, 0xeef9897b, 0x7583419d, 0xd7d5f784, 0xf2f60e6f, 0xa9e0afa9, 0x25abe41c, 0x5498383c, 0x7f430b81, 0xac6b7408, 0xf3f13b36, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x00401400, 0x00000052, 0x00004000, 0xac6b75d0, 0x000000ff, 0x00000000, 0x00144000, 0xffffffae, 0x2206c8a0, 0x00000000, 0x0000007e, 0x00000000, 0xac6b7408, 0xac6b70f0, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x185d, 0x4287, 0x405b, 0xb0b0, 0x192e, 0xb012, 0x414b, 0x436f, 0xa9b7, 0x438f, 0x108e, 0xb067, 0x418e, 0x4013, 0x3736, 0x4549, 0x4259, 0xb2c8, 0x42a8, 0x415c, 0x43a9, 0xb28b, 0xbfb3, 0xb2f8, 0x43c9, 0x0a66, 0x4216, 0xbf64, 0x1ee1, 0x42e3, 0xb2bd, 0x00ef, 0xbaf4, 0x40be, 0x4353, 0x44a9, 0x4104, 0x1b6b, 0x403d, 0xb266, 0x078d, 0xba6b, 0x3c0d, 0x05b8, 0xbfbd, 0xbaee, 0x4277, 0x42c0, 0x0958, 0x4254, 0x40a3, 0x4250, 0xa6f7, 0x4050, 0x4541, 0xb211, 0x14d3, 0x41ae, 0x46c8, 0xba25, 0x3f96, 0x0fc8, 0xb246, 0xbf45, 0x45ce, 0x130d, 0xbf00, 0x2176, 0x404f, 0x435a, 0xafd3, 0x4093, 0xb2b3, 0x4202, 0x41df, 0x4089, 0x43d4, 0x0739, 0xaa0d, 0x3f8f, 0xba47, 0x1a4c, 0xb224, 0xbf5d, 0xb20e, 0x1e5c, 0x46a1, 0xba65, 0x40af, 0x41e6, 0x4076, 0xb23a, 0x3fa5, 0x438d, 0xba57, 0xae3f, 0x4141, 0xb2f9, 0xbfe0, 0x43d0, 0x1ec1, 0xb2b6, 0xb28b, 0x1ded, 0x055e, 0x41f0, 0x40f8, 0x0692, 0x4051, 0xbf97, 0x4388, 0xacb2, 0xb04b, 0x19ce, 0x32d5, 0x0b66, 0x4368, 0x1627, 0xb288, 0xb241, 0x4042, 0x2698, 0xb0b7, 0x429f, 0x262d, 0x40b3, 0xb065, 0x42f4, 0xbf14, 0x4009, 0x1661, 0x4304, 0x430d, 0xbfb5, 0x417a, 0x428d, 0x1d15, 0x0175, 0x4282, 0x1183, 0xb22c, 0x1fbc, 0x4352, 0x0e63, 0xa412, 0xba64, 0xa285, 0x41f2, 0x41de, 0x4372, 0xbafe, 0x00ef, 0x40d8, 0xb29d, 0x41a1, 0x44b5, 0xbfaa, 0x4668, 0xbaf8, 0x4180, 0x4462, 0x1dbb, 0x4492, 0x4022, 0xba5c, 0x4070, 0xb2b6, 0x4321, 0x4314, 0x4330, 0xa81d, 0x38c2, 0x427a, 0x417c, 0x4691, 0x4111, 0x4065, 0x1c3d, 0xb02f, 0x424a, 0x4047, 0x4349, 0x411a, 0x40fa, 0xbac1, 0xbf7e, 0x36e7, 0x4189, 0x233b, 0xb032, 0x4071, 0x2ae4, 0x2b36, 0x4290, 0x310b, 0xb22c, 0x430a, 0x4204, 0xbf7f, 0x172e, 0xba71, 0x08ee, 0x43bf, 0x1b31, 0xb068, 0x42b5, 0xbf00, 0x4545, 0xb08e, 0xa37d, 0xb234, 0xb011, 0xbfe4, 0xb208, 0xa6ee, 0x0e43, 0x107e, 0x405d, 0x1c5f, 0x0f55, 0x0342, 0x44cd, 0x2b29, 0xb086, 0xa40a, 0xb21d, 0x4028, 0x426b, 0x2a20, 0x4267, 0x406f, 0x419f, 0x4274, 0x4006, 0xa492, 0x18a2, 0x4177, 0x4140, 0x42a6, 0xbf83, 0x2b7b, 0x4242, 0x40a5, 0x079e, 0x46e0, 0x1907, 0x44f5, 0x094d, 0xb295, 0x438d, 0x409c, 0x465c, 0x43db, 0x36e9, 0xba2d, 0xbf95, 0x430f, 0x4129, 0xb2d0, 0x46dc, 0xa2f1, 0xbf16, 0x433f, 0xb27b, 0x4348, 0xaf01, 0xbf35, 0xba7d, 0x1b43, 0xbaf0, 0x4006, 0x2e62, 0x1e1d, 0xae4d, 0xa032, 0x40fd, 0x42bb, 0x4395, 0x423b, 0x420c, 0x41c1, 0x42fc, 0x466f, 0x4604, 0x41be, 0x4283, 0x426c, 0xbfd3, 0xb2d1, 0x4025, 0x42e2, 0x1d04, 0x228c, 0xb052, 0x37a0, 0x1c67, 0x403a, 0xbfa6, 0x0a6e, 0xb06e, 0xb0b7, 0xaeb4, 0x30fd, 0x423a, 0x41ed, 0x4040, 0x4116, 0x3637, 0x4302, 0xb2f8, 0x1e49, 0xb2e1, 0x437a, 0x05e7, 0x408c, 0xb02a, 0xba09, 0x402c, 0x43db, 0x4062, 0x4664, 0x38d1, 0xbfcb, 0xb012, 0x4595, 0xb2d4, 0xb224, 0xba4c, 0xbf06, 0x088c, 0x1077, 0x414a, 0xafd7, 0x1496, 0xbfa0, 0x45b0, 0x1c0c, 0x3354, 0x43e0, 0x1eab, 0x4281, 0x45c1, 0x406b, 0x075b, 0x3898, 0x4242, 0x430f, 0x4612, 0x40fb, 0xb03f, 0xb0f6, 0x400e, 0x4316, 0x418c, 0x270c, 0xbf93, 0xbac5, 0x18ed, 0xa70b, 0x42f6, 0x4238, 0xbf76, 0x3431, 0x4200, 0x46b8, 0x411c, 0x43a6, 0x439b, 0xbaff, 0x1a05, 0x404b, 0xb238, 0xb2f6, 0xba00, 0x0df6, 0x4021, 0x409b, 0xba4f, 0x1fb3, 0x252f, 0x4018, 0xa721, 0x0f4b, 0x40b9, 0x4211, 0x40d7, 0xba5c, 0xb0f1, 0xbfdf, 0x2384, 0xba0a, 0x435a, 0x42d7, 0x4653, 0x401e, 0xac83, 0x4073, 0xba4b, 0xb0df, 0x0b60, 0x4316, 0x4624, 0xa092, 0x0bf5, 0xbfb0, 0xb057, 0x42d6, 0x42fe, 0x156c, 0x41d6, 0xb0bb, 0x13d3, 0x4248, 0xbfbb, 0x437d, 0x0a8b, 0x4317, 0x40db, 0x42b3, 0x40cd, 0x4095, 0x4362, 0x42d2, 0x4475, 0x4137, 0xae4f, 0x400e, 0x0014, 0xbf7c, 0x420f, 0x1b5a, 0xbf70, 0x18ef, 0xb07e, 0x44fb, 0x43cf, 0x404b, 0x4171, 0xb0e2, 0xba20, 0xa7cc, 0x4580, 0xbf0e, 0x385a, 0x43d1, 0xac36, 0x434c, 0xb0d6, 0x4284, 0x12a5, 0x43bd, 0xb26e, 0x438a, 0xb059, 0x4370, 0x18bd, 0x362f, 0x4309, 0xb0fc, 0xb2d3, 0xbf13, 0xb23e, 0x4285, 0xb0fb, 0xba0b, 0x41b6, 0x04d1, 0xb249, 0x1ee9, 0xb2aa, 0x4340, 0x4285, 0x43e9, 0xbac6, 0xba62, 0x40d2, 0xb23d, 0xb0f0, 0x1cca, 0x083a, 0xb0b2, 0x43c6, 0x18d1, 0x2883, 0xbf66, 0x4121, 0xbaeb, 0x402e, 0x40c5, 0xbaf2, 0x41a9, 0x1839, 0xba61, 0xb0cf, 0xb2ae, 0x40ef, 0x405a, 0xb207, 0x1bb5, 0xab4a, 0x4572, 0x41f5, 0x33d6, 0x40a5, 0x1a06, 0xb2c3, 0x403c, 0x4341, 0xa0bd, 0x42a7, 0xb276, 0x1daf, 0x37ad, 0xbf5e, 0x3150, 0x43eb, 0x4213, 0xbf66, 0xb21d, 0x429b, 0x4004, 0x4012, 0x41ab, 0x467e, 0xb2d5, 0x4213, 0x4229, 0x0fbc, 0xa2a8, 0xbfd8, 0x4042, 0x32a7, 0x3304, 0xbf8a, 0x125d, 0x3c49, 0x4596, 0x4060, 0xa368, 0x448b, 0x409c, 0x2f24, 0xb0ca, 0x42ff, 0xb22a, 0x0971, 0x469a, 0xb260, 0x4243, 0x2673, 0x0f84, 0xbf36, 0xa7f5, 0xa907, 0xb081, 0xb2b0, 0x4157, 0x1ad4, 0x4360, 0x1c8f, 0xba6a, 0x40ac, 0x429a, 0x425f, 0xb062, 0xb2cd, 0x4228, 0x1806, 0x1d41, 0xbf0d, 0xba56, 0xbf60, 0xb255, 0x1952, 0x19ca, 0x2d41, 0xb0a0, 0x45e8, 0x3a49, 0xba58, 0x41cc, 0xbf00, 0xb283, 0x462e, 0x18ad, 0xbaf6, 0x26f5, 0x1680, 0x41bb, 0xb2d7, 0x40a9, 0xbf59, 0x40a2, 0xba67, 0x0b0a, 0xa067, 0x1d7b, 0x42ad, 0x2159, 0xbf2a, 0xa8ac, 0x421b, 0x1c35, 0x4381, 0x424c, 0x4216, 0xba6b, 0x4262, 0x432d, 0xb2f3, 0x402b, 0xb29f, 0xb032, 0x0bae, 0x40ba, 0x41a6, 0x4063, 0x405b, 0xb051, 0xbf9e, 0x4242, 0xb2cd, 0x2966, 0x4388, 0xb281, 0xb24f, 0x442a, 0xbae4, 0xb0d7, 0x25c2, 0x4013, 0x308b, 0x4231, 0x3ca8, 0x1fc9, 0x4563, 0x4420, 0xbaf2, 0x0a8b, 0xa349, 0xbada, 0x4188, 0x04c9, 0xb0c5, 0x1db0, 0xb079, 0x4204, 0x4305, 0xbf53, 0xa0e7, 0x434e, 0x1fb1, 0xb079, 0x1c64, 0x43e0, 0x1c51, 0x3ea3, 0x428e, 0x409d, 0x4323, 0x43b9, 0x42fd, 0x22fb, 0xb228, 0x41bb, 0x4320, 0x40f4, 0x2bec, 0x45cc, 0x419d, 0xb2d8, 0x325d, 0x44c3, 0x201f, 0x41d5, 0xbf59, 0xbf70, 0x2ef4, 0xb27b, 0xb03b, 0xa979, 0x24f4, 0xba21, 0x44a8, 0x028d, 0xb207, 0xa84c, 0x409d, 0xbf80, 0xbf26, 0xb2ef, 0xb2c9, 0x2659, 0x430f, 0xb2eb, 0x41d1, 0x10fa, 0x2baa, 0x09a6, 0xb02f, 0xb2c9, 0xba2f, 0xba67, 0x4211, 0xbf57, 0x10f3, 0x185c, 0x419a, 0x4271, 0xad46, 0xb077, 0x4627, 0xb2e2, 0x1a5e, 0x42a8, 0x422f, 0xb27a, 0x40f5, 0x4085, 0x0255, 0xa249, 0x4270, 0x4392, 0xb0ff, 0xb2e3, 0x46b2, 0xb2d1, 0x4001, 0x4639, 0x1fa7, 0x42f2, 0xa470, 0xbaf5, 0xbfdd, 0x1d21, 0x3f09, 0x2d80, 0x1aa9, 0xbf1a, 0x4061, 0x400e, 0xae29, 0xa3a0, 0x4010, 0x1f99, 0x121b, 0x41a0, 0xb2af, 0xbad1, 0xbf1c, 0xb298, 0x3db6, 0xb05b, 0xba33, 0xa3d9, 0xa2e3, 0x4162, 0x4188, 0x413b, 0x2805, 0x40a0, 0x4151, 0x2a6d, 0xbae7, 0x444f, 0x430e, 0x413a, 0x1767, 0xb05b, 0x303f, 0x001c, 0x43b9, 0x28e3, 0x3dad, 0x420e, 0x4205, 0xbf03, 0xb21b, 0x022e, 0x44d1, 0xb283, 0xb2c7, 0x0c1a, 0x4107, 0x198e, 0x43e8, 0x1c24, 0x3d5c, 0xa5ee, 0xbfd0, 0xbf43, 0x2a0c, 0x40ac, 0x4414, 0xa9ce, 0xbf2c, 0xbac9, 0x396d, 0x42fa, 0xbf0b, 0x4292, 0x43ba, 0x424a, 0xb25f, 0x42d6, 0x4188, 0x2f30, 0xb081, 0x38a0, 0x4213, 0x4130, 0x415f, 0x2c4c, 0xadab, 0xba23, 0xadc1, 0xb25f, 0xabf8, 0x45cd, 0x416d, 0x40d1, 0x403b, 0x1dc9, 0x4150, 0xba03, 0xb076, 0xbaf1, 0xbf2d, 0x40dc, 0x0d13, 0x18a9, 0x43a4, 0xb076, 0xba07, 0xb27d, 0x0b40, 0x3d88, 0x40a7, 0x0533, 0x2c71, 0x41cc, 0x42f9, 0x1ef9, 0x4583, 0x41e2, 0xba3b, 0xba44, 0x41b1, 0x42f2, 0xbfdf, 0xb23d, 0xbfc0, 0x071e, 0x19b6, 0x1a1f, 0x417f, 0xb268, 0x4132, 0xbaf6, 0x3af3, 0x1f37, 0x4352, 0x437d, 0xbfa2, 0x05b2, 0x43cb, 0x42a5, 0x43e5, 0x42ac, 0xa525, 0x18ad, 0xbace, 0x2d15, 0x1f1e, 0x01c6, 0xa4ad, 0x433a, 0x436a, 0x1e2a, 0x4389, 0x1898, 0xb2a5, 0x1a20, 0x42ec, 0xa6ed, 0xb038, 0xbacd, 0xbf8e, 0x41b9, 0x43ea, 0x42ba, 0x30cd, 0xaff4, 0x42fb, 0x460d, 0xbfa0, 0x1f87, 0x122b, 0x1b3f, 0x1cd8, 0x4109, 0x1cf9, 0x4146, 0x27b9, 0x4637, 0x43f7, 0xba68, 0x459b, 0xbf8d, 0xbf60, 0x1be6, 0x4691, 0xb267, 0x4062, 0xb2b4, 0xb2ca, 0x4198, 0x4331, 0x2b7d, 0xbf60, 0xbadc, 0xb221, 0x1ca0, 0xb200, 0x4007, 0x1e11, 0x43f6, 0x43af, 0x2b4b, 0x41d0, 0x21a9, 0x43d0, 0x43d1, 0xbfd2, 0x410a, 0xb0a3, 0xb266, 0x4153, 0x42fa, 0x371e, 0x4037, 0x1c33, 0x43de, 0xb0ef, 0xb059, 0x43fe, 0x410f, 0x0ed6, 0xb2ef, 0xbf33, 0xb080, 0xb023, 0x416c, 0x4390, 0x4484, 0x059f, 0x03c7, 0x4176, 0x425b, 0x42b5, 0xbf0b, 0x4680, 0x4202, 0x1ee9, 0x456a, 0xaae9, 0x4333, 0x1c96, 0x20f2, 0x3d1d, 0x434e, 0x39dd, 0x40c9, 0x433b, 0xbf21, 0x444b, 0xb295, 0x3462, 0x3275, 0x4110, 0xb214, 0x1edc, 0x4136, 0xbad1, 0x43a5, 0xb2ae, 0xadf4, 0x40ac, 0xa037, 0x4199, 0x41d7, 0x426e, 0xb2de, 0x43eb, 0xba38, 0xb253, 0x4092, 0xbfba, 0x413c, 0x4238, 0x41a0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd919f6a3, 0x9e176573, 0x3795cb44, 0x666e776e, 0x370d6712, 0xb9f52030, 0x59d84426, 0xcda6b649, 0x3c751555, 0x13a5d9a9, 0x68b254f2, 0x4c783b43, 0xb4ebd830, 0x01180a73, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x0080fb0e, 0x005002e4, 0x00000000, 0x00000024, 0x00000000, 0x011021db, 0x0000003d, 0x0ffb8000, 0x0001470c, 0xfff80698, 0x00000003, 0x4c784f07, 0xb4ebd7a0, 0x01101e0b, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4139, 0x0239, 0x4470, 0x43b0, 0xa91a, 0xb068, 0x1c4a, 0x2666, 0x43c6, 0xa8cd, 0x4184, 0xbafc, 0xb29e, 0x42dd, 0x1781, 0x4033, 0x35f4, 0x31c2, 0xa3f5, 0xbf3a, 0x1884, 0xbafc, 0x424b, 0x42ab, 0x0955, 0x432a, 0x43f5, 0x410f, 0x4694, 0x401b, 0x41c7, 0xb058, 0x419f, 0x41f2, 0xb2fc, 0x1fd1, 0x464b, 0xbfb0, 0x41c5, 0x2b5f, 0x4330, 0x4152, 0xbfbf, 0xbf70, 0x43fb, 0x0114, 0x439b, 0x4376, 0x35bb, 0xa42b, 0x4130, 0x25df, 0x2ee3, 0x42a8, 0x4128, 0x42df, 0x4220, 0x43ec, 0x2ecd, 0xb291, 0xbf4f, 0xab48, 0x41da, 0x4198, 0x4214, 0x40c7, 0x3fb9, 0x424e, 0x414e, 0x01e3, 0x379f, 0x44b2, 0x4166, 0x42e8, 0x4208, 0xb2aa, 0xbf6f, 0xb05e, 0x40da, 0x463a, 0x193e, 0x42ef, 0x40ed, 0x1cc4, 0x4272, 0x1550, 0x42b1, 0x3bbb, 0x227a, 0x449c, 0x42b0, 0x41bf, 0xa310, 0xb0a6, 0x439e, 0x42a2, 0x4392, 0xbf1d, 0x2af5, 0xb2b1, 0x426b, 0x43dd, 0x4204, 0x466b, 0xb060, 0xbac4, 0xb0d2, 0x4320, 0x4006, 0x4303, 0xbf83, 0xa242, 0x42e1, 0xba65, 0xa4c1, 0xbfe0, 0xb26a, 0x3dc0, 0x1e3f, 0x423d, 0x439e, 0xba4e, 0xab3b, 0xba29, 0x4606, 0x41f2, 0xbac9, 0xb23d, 0xaac3, 0x19e2, 0xbf78, 0x1ad9, 0x4089, 0xba78, 0x46a3, 0xa084, 0xb27c, 0xbf72, 0x1439, 0x4000, 0xb275, 0x40cc, 0x4252, 0xbf7b, 0xbad4, 0x43d2, 0x1ce1, 0x4562, 0xb20b, 0x2fbd, 0xbadd, 0xba61, 0x2a48, 0x405a, 0x4343, 0xbf42, 0xb233, 0x4261, 0x423a, 0x42d4, 0x4330, 0x46f2, 0xbaf3, 0xb240, 0xba69, 0xba78, 0x091c, 0xb296, 0xbae4, 0x2223, 0x40f9, 0x42db, 0x1838, 0xb21f, 0x03b3, 0x43a0, 0xb2eb, 0xbf0f, 0x42ac, 0xb22e, 0xb013, 0xa332, 0xb2d0, 0xb271, 0x110f, 0x2fc7, 0x4668, 0x43dc, 0x41ef, 0x3a07, 0xbacf, 0x4158, 0x4129, 0x00ac, 0xb254, 0x416a, 0x4312, 0xad39, 0x4070, 0x1d12, 0x43e3, 0xaf2e, 0xb2fb, 0x250c, 0xa0c5, 0xbf96, 0x3811, 0x43a2, 0xa7d3, 0x1a37, 0x41b5, 0x414d, 0x43b6, 0xbacc, 0xa587, 0xbf35, 0x1ea8, 0x3706, 0xb0d5, 0x42f9, 0x405f, 0x426e, 0x1c33, 0x4369, 0xb01e, 0x4611, 0xa7cc, 0xbf9d, 0x1e4f, 0x1939, 0xbfe0, 0x43e7, 0x445d, 0xbfbc, 0x4146, 0x4093, 0xb255, 0x0325, 0x42bf, 0xbfe1, 0x45cb, 0x1a8f, 0xb2ca, 0x1912, 0x007c, 0xba33, 0x3771, 0x4355, 0xaa43, 0xbf00, 0xbad0, 0xbf93, 0x1e05, 0x1bb5, 0xb28c, 0x296d, 0xbf9c, 0xafa4, 0xb2e5, 0x4672, 0xbf27, 0xba7a, 0x419e, 0x4047, 0x4025, 0xb238, 0x4046, 0xb211, 0x3e6a, 0x4058, 0xbf32, 0x45c6, 0x3629, 0x42f1, 0x41da, 0xb2c1, 0x4441, 0x412d, 0xb09f, 0x43d8, 0x41c0, 0x40cb, 0x25d4, 0xa604, 0xa1e9, 0xb2b3, 0x42a5, 0x42c7, 0xb223, 0xb2f4, 0x403d, 0x41f9, 0x417e, 0xbf90, 0xbf3c, 0x1adc, 0x41c3, 0x0b57, 0xa98b, 0xace1, 0x19d8, 0x42bc, 0x141e, 0xbfb6, 0xb0bf, 0xba07, 0x425b, 0x3db1, 0x4186, 0x4052, 0x42c9, 0x114a, 0xb2c7, 0x4461, 0xbac5, 0x40f0, 0xa656, 0xa678, 0xb2a0, 0x1fdb, 0x186e, 0x4549, 0xb0c9, 0x2198, 0xb048, 0x4316, 0xa29b, 0xb2df, 0xb269, 0x1cf6, 0xbf8a, 0x418e, 0x012f, 0x1ff4, 0xb060, 0xad7c, 0x42a8, 0x42dd, 0x420e, 0x4153, 0xae8b, 0x03f1, 0x439f, 0x41f3, 0xbf83, 0x1395, 0xb281, 0x41f3, 0x19e3, 0x4002, 0x2ac4, 0x3a17, 0x4225, 0x4007, 0x4201, 0x45b4, 0xb29c, 0x410f, 0xa3ad, 0x43d4, 0xb2b9, 0x1d09, 0xb082, 0x4335, 0xbf58, 0xb297, 0xb0c3, 0x4240, 0xba79, 0xb230, 0xba2a, 0xbf57, 0xb0a6, 0xa975, 0x1f5b, 0x42e8, 0x190f, 0xbfc3, 0x420a, 0xb21a, 0x2c87, 0x417e, 0xbfe0, 0x40b1, 0x339a, 0x2f4c, 0x4353, 0x1b7c, 0x4349, 0x4335, 0x41c6, 0x4283, 0xa0bc, 0xa2da, 0x413b, 0x42c5, 0xbf8b, 0x4572, 0x061e, 0xbf90, 0x4159, 0xb280, 0xb0ef, 0x204a, 0x1dfe, 0x068c, 0x4122, 0x43be, 0x4286, 0xba1e, 0x4333, 0xb272, 0xa671, 0x4002, 0x42ed, 0x14b2, 0x32d9, 0xbf2c, 0x358d, 0x4615, 0x410b, 0x3ecd, 0xb223, 0x2f09, 0x1b6c, 0xbf2c, 0xbae5, 0x1cee, 0xb2fe, 0x42ea, 0xb045, 0x41ca, 0x4016, 0x10c6, 0x0828, 0xabe4, 0x123d, 0xa82a, 0xb04d, 0x45b0, 0xbfb0, 0x4118, 0xba15, 0x442d, 0x4098, 0x2145, 0x1a40, 0x1935, 0x394e, 0x45c2, 0xbf19, 0x402e, 0xb2ba, 0x42cb, 0x40d9, 0x4193, 0x41e8, 0x4261, 0x167e, 0xbad3, 0xbf1a, 0xb24d, 0x09c9, 0xb27f, 0x0295, 0x417d, 0x4362, 0x40a0, 0x4636, 0xba62, 0x2a28, 0x405d, 0x1ac1, 0x2e7e, 0x4027, 0x4222, 0xba7d, 0x429c, 0x40d6, 0x424b, 0x4430, 0xb042, 0xa73d, 0x406e, 0x3722, 0xb2a9, 0x4014, 0x44b1, 0xbfac, 0x4035, 0x4326, 0x0c54, 0x4007, 0xba2c, 0x43a7, 0x4333, 0x4092, 0xa7c2, 0xbf01, 0x27cb, 0xb047, 0x1a86, 0xbade, 0xb2b7, 0x2147, 0x0e71, 0xb295, 0xba38, 0x4388, 0x159c, 0x416b, 0xba1a, 0xb0d2, 0x0162, 0xba33, 0x40cd, 0x465c, 0x420f, 0x368f, 0x42bf, 0xbf60, 0x40f3, 0x4002, 0x0fed, 0x4165, 0x415c, 0xba29, 0xbfcd, 0x42b2, 0x40d4, 0xb0ac, 0x43f7, 0x12b0, 0x416d, 0x2be8, 0x4103, 0x408e, 0x43e6, 0x40d5, 0x427a, 0xba11, 0x1c74, 0x40b9, 0x4066, 0x4254, 0x4081, 0x2821, 0x4003, 0x44ab, 0x431a, 0x4282, 0x4569, 0x2f84, 0xb2ef, 0x4340, 0xb218, 0xbf8f, 0xb024, 0x1c87, 0x289f, 0x43d9, 0xba0a, 0x421d, 0x44f0, 0x42a2, 0x41b4, 0xa51b, 0x1c28, 0xa535, 0x40cb, 0x44b9, 0x40ed, 0x0bbe, 0x413e, 0x1eb8, 0x41ad, 0x1dac, 0x3f25, 0xb0ec, 0x12e7, 0xab9a, 0xbf77, 0xbae9, 0x1b49, 0x40a5, 0x40d7, 0x4392, 0x43e6, 0x190e, 0x40b0, 0xb2dc, 0x425f, 0x41ad, 0x10b1, 0x40f4, 0x43b7, 0x4037, 0xb03c, 0x1a72, 0x407b, 0xba50, 0x466e, 0xbf11, 0xbad4, 0xa393, 0xaa42, 0x41c5, 0x413c, 0x410a, 0xba1f, 0xba17, 0x41f4, 0x2138, 0xbfe0, 0x1ace, 0xba65, 0x40cc, 0x44e1, 0x1646, 0x4228, 0x43b7, 0xa7cf, 0x4160, 0xbfa8, 0xba18, 0x43f1, 0x1ed8, 0x4355, 0x2fce, 0xbacb, 0x41c5, 0x42c2, 0x44f8, 0x1f13, 0xbf5f, 0xaa2a, 0xb2fd, 0x1405, 0x25d1, 0xa644, 0x4134, 0x4280, 0x19f2, 0x0a34, 0x1b5e, 0x4137, 0xba1e, 0xba4c, 0x424c, 0x43d3, 0xb062, 0xb2b4, 0x43c3, 0x42a4, 0xb200, 0x4219, 0x4646, 0xa445, 0x4248, 0xbfe0, 0xbf05, 0x415f, 0x1832, 0x439d, 0xaaa6, 0xb234, 0x43b7, 0xb01a, 0xba02, 0x1f57, 0x43c9, 0x46c0, 0x4558, 0x4116, 0x1707, 0xb0b8, 0xbfc0, 0x41c0, 0x4032, 0x112c, 0x44e4, 0x4290, 0x04f6, 0xa289, 0x4226, 0x43bb, 0x1ee1, 0x434e, 0xbf66, 0x4210, 0x425f, 0xa705, 0x1aff, 0x31fd, 0xbf0e, 0x4295, 0x4107, 0xb283, 0x4067, 0x1dec, 0xb0b8, 0xb089, 0x2a04, 0x4172, 0xbfc0, 0x4217, 0xb2ea, 0x41a8, 0x41a2, 0x1610, 0x4294, 0xb016, 0x406e, 0x4281, 0x42a7, 0x1f7a, 0x1866, 0x4047, 0x4672, 0x20bc, 0x420b, 0x198a, 0xbf8a, 0x43f3, 0x3aed, 0xbfb0, 0x4612, 0x408c, 0x43e6, 0x0f02, 0x4214, 0xbfb5, 0xae8e, 0x408a, 0x41d9, 0x02f0, 0x341b, 0xb07d, 0x413b, 0xb0d9, 0xb2c5, 0x42f0, 0xb2f4, 0xba45, 0xa723, 0x42f1, 0x19aa, 0x4005, 0xb00b, 0x42ec, 0x4239, 0x43c4, 0x40ed, 0xaaa1, 0x1ff0, 0x4215, 0xb005, 0x414d, 0xb037, 0x43d1, 0xbf5b, 0xb24a, 0xb2cc, 0xb0e1, 0xb25e, 0x4298, 0x427a, 0xb0e5, 0x42be, 0x1cf1, 0x43b3, 0xb0b9, 0x4242, 0xbf0b, 0x435b, 0x14c6, 0x43d8, 0xb2e2, 0x414c, 0x4165, 0xad8d, 0xb2f3, 0xb053, 0x42a1, 0x4301, 0xa3c4, 0x43fe, 0x2e37, 0xb019, 0x4112, 0xba58, 0x40c9, 0x4204, 0x1870, 0x4592, 0x4360, 0x24a0, 0x42ab, 0x4066, 0x404b, 0x4144, 0x4252, 0x422c, 0xbfa5, 0x1caa, 0x02c7, 0x09fa, 0x4364, 0x43fe, 0x3b92, 0x4616, 0x4376, 0x1be2, 0x40ee, 0x34a4, 0x2c90, 0xba42, 0x42de, 0x435b, 0xb272, 0x413d, 0x1f64, 0x4099, 0x42b2, 0x43bb, 0x43fb, 0xbfd1, 0xba53, 0xb067, 0x42a2, 0x0f18, 0x43cc, 0x1f8e, 0x1dfe, 0xb28b, 0xbad5, 0x188f, 0x4363, 0x4299, 0x12b3, 0x0831, 0x41fb, 0x40fd, 0xbfa9, 0xba30, 0x4183, 0x42ad, 0x40b2, 0x418b, 0x42ea, 0xb0fb, 0x42aa, 0x42df, 0x1f34, 0x0bd2, 0x3df7, 0xb215, 0xba60, 0xb28d, 0x30e7, 0xb2a0, 0x4567, 0x42a8, 0xbfc3, 0xb0a0, 0x41c7, 0x0f8a, 0x16a1, 0x4168, 0x445e, 0xb236, 0x4271, 0xbf39, 0x4312, 0xbad2, 0x42e4, 0x1cb2, 0xb2c7, 0xbaee, 0x431c, 0x42dd, 0x42f2, 0xb262, 0x403a, 0x1dfb, 0x4330, 0x36a0, 0xba46, 0xb224, 0xbf56, 0x1b15, 0x3160, 0x40e6, 0xb025, 0x1390, 0x42a7, 0x078b, 0x4263, 0x141e, 0x0633, 0x4281, 0x40f5, 0x422b, 0x45f1, 0xba2b, 0x1a8a, 0x41a8, 0xb275, 0xb2a3, 0xb22a, 0xbf7a, 0x4540, 0x43da, 0x42da, 0xaa06, 0xb284, 0x3f4f, 0x42ae, 0x40a2, 0xbfb1, 0x4382, 0xbaf0, 0x1db0, 0x43af, 0x0b87, 0x433d, 0x461a, 0x2502, 0xb234, 0x3130, 0xb2b7, 0x43d2, 0xba2f, 0x37df, 0x3ab5, 0xafcb, 0x0f62, 0x46f8, 0x404f, 0x4560, 0x43fd, 0xbf59, 0x4237, 0x414e, 0x3b8d, 0x43e4, 0x4370, 0x2d12, 0xbf2b, 0x42cd, 0xba1a, 0xb2f9, 0xb2b0, 0xb0ea, 0xbac2, 0xb284, 0xaa89, 0x4041, 0x22ca, 0xb229, 0xafec, 0x438d, 0xbae3, 0x4314, 0x0739, 0xb229, 0xb2f4, 0xbf45, 0x189c, 0x43db, 0x4116, 0x409d, 0xb2b0, 0x4227, 0xb297, 0x469b, 0x31d0, 0x2360, 0x462f, 0xbacf, 0x4277, 0x305e, 0x2d0f, 0x381c, 0xbfcf, 0xb2f4, 0x42db, 0x435f, 0x3aaa, 0xb0ee, 0x44f4, 0x1b64, 0x4372, 0xb284, 0x40cd, 0x46d4, 0x13bf, 0xba5d, 0x4092, 0xba52, 0x41e2, 0x1dc6, 0xbf7d, 0x1eb2, 0x2606, 0x401f, 0x1d2f, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xb856fb9b, 0x192a6db9, 0x8500700e, 0x51377f3b, 0xcf8d1a8e, 0xeb924976, 0xf20bcc21, 0xd216b99a, 0xee51a075, 0x8bdc3aa5, 0x4c0f0144, 0xdedd3f79, 0xfe5590cd, 0x20762c18, 0x00000000, 0x600001f0 }, FinalRegs = new uint[] { 0x00010041, 0x000000d0, 0x00010046, 0x00000060, 0x00000041, 0x00006000, 0x00000006, 0x00000000, 0x00001756, 0xad5388b8, 0x00000000, 0x000058ca, 0x00000000, 0x20762a7c, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x404c, 0x424a, 0x2986, 0xb245, 0x4373, 0x4073, 0x4006, 0x41df, 0x02dc, 0x3691, 0x42c5, 0xb2bf, 0x4020, 0x422c, 0x396a, 0xa360, 0xbf6f, 0x1d2b, 0x2d8e, 0x1815, 0x42f8, 0x41e0, 0x0084, 0x42c0, 0x43a4, 0x3d65, 0x11d0, 0xb2ee, 0x21d7, 0xbf8b, 0x3e83, 0x406a, 0x42ba, 0x4370, 0x446d, 0x3c88, 0x0c4e, 0x3ac9, 0x4284, 0xb2a9, 0x3c89, 0xb21a, 0x42df, 0x43e9, 0x4079, 0x1879, 0x40ea, 0xbafd, 0x435f, 0x40c2, 0x2c52, 0xba2c, 0xba4d, 0x45ba, 0xb288, 0x4228, 0x40cd, 0xbf67, 0x4092, 0x41bf, 0xba76, 0xb25f, 0x1c56, 0xb23c, 0xba28, 0x4063, 0x4083, 0xba20, 0xbad0, 0x40c5, 0xb245, 0xba73, 0x37df, 0x405d, 0x3e0c, 0xba79, 0xb241, 0x425a, 0x40c9, 0x4031, 0xb2b4, 0x4288, 0x4333, 0xbf95, 0xb20d, 0x42d8, 0xb0e6, 0x41c3, 0xbad8, 0x417b, 0x1c10, 0x030e, 0x403c, 0xb0e2, 0x12a0, 0x4451, 0x420b, 0xb20c, 0x3174, 0xbf60, 0xbada, 0xb2fd, 0xbf4a, 0xb22e, 0xb0ea, 0x19bf, 0x41cd, 0xbfbc, 0x412a, 0xba0d, 0xba38, 0x4092, 0x42af, 0x1db0, 0x41b5, 0xb281, 0x4059, 0xba6f, 0x410d, 0x4421, 0x410a, 0xa37d, 0xb289, 0x2b11, 0xad56, 0x383f, 0xb20c, 0xbfb3, 0x428f, 0x1f82, 0xba34, 0xb0ef, 0x1c73, 0x401e, 0xba6a, 0x40c8, 0x4554, 0xb0b6, 0x1d62, 0xb092, 0xb000, 0xbf7c, 0x40c0, 0x3aca, 0x1db0, 0x4124, 0x461c, 0xba2f, 0x1a4d, 0x1f44, 0xbfd9, 0x33dc, 0xb2c9, 0x41fe, 0x1921, 0xba75, 0x40ea, 0x424e, 0x43ca, 0xa25a, 0x423e, 0x4175, 0x410a, 0x41e1, 0x41c2, 0xbfca, 0x1f2c, 0xb2ce, 0xae78, 0xb262, 0x4045, 0x4287, 0xb24e, 0x1d5e, 0x4092, 0x2e56, 0xbacd, 0x400e, 0xba42, 0x40f4, 0xbafd, 0x4482, 0x40fc, 0x2f63, 0xb01c, 0x408d, 0x4008, 0x4250, 0xa846, 0xb2a7, 0x376b, 0xb012, 0xbad0, 0xbf7c, 0x4382, 0xb23e, 0x414c, 0x4091, 0xb0ab, 0x40fe, 0x4076, 0x458c, 0xa397, 0x445a, 0xb267, 0x43d7, 0x42c5, 0xa924, 0x41c9, 0x431d, 0x4207, 0x4182, 0x4012, 0x0c79, 0xb26a, 0x1b23, 0xabb2, 0x40df, 0xbf4a, 0x27b0, 0x4467, 0xa236, 0xba10, 0x4386, 0x42c2, 0x41d5, 0x4665, 0x41bb, 0x41d0, 0xba31, 0x4388, 0x1d9c, 0x2d56, 0x420d, 0x432e, 0xba31, 0x1d22, 0x4428, 0x419b, 0x1cd7, 0xb081, 0x4327, 0x4162, 0x42d8, 0x1947, 0xbf2c, 0x43cb, 0xb250, 0x4012, 0x3734, 0x4214, 0xa5bb, 0x4611, 0xba60, 0x2aed, 0xba05, 0x43a1, 0x431d, 0x0608, 0xbade, 0xa596, 0x4312, 0xb0b8, 0xb249, 0xbf6d, 0x42ac, 0xbfe0, 0x42e8, 0xb284, 0x42cd, 0x380e, 0x405b, 0x42e8, 0xb07d, 0xbad9, 0x43ca, 0x4282, 0xb283, 0x4353, 0x1ca4, 0xba3e, 0x41f3, 0x18a3, 0xb2da, 0x41d5, 0x416a, 0x2f3e, 0x46ec, 0xbf9c, 0x430f, 0x434e, 0xb2dd, 0xb27d, 0x1064, 0x43f0, 0xb2c9, 0x15a6, 0x28ab, 0xa5f9, 0x08db, 0xbfbe, 0x41ed, 0x41f9, 0x4385, 0x4197, 0x0b37, 0x418b, 0x133a, 0xb0f9, 0xba40, 0x434b, 0xb2b2, 0xa303, 0x464d, 0x1c7a, 0xbfe0, 0x46ac, 0x028d, 0x445c, 0xb034, 0xbf4f, 0x1af3, 0x1e0d, 0x4148, 0xaf26, 0xb01e, 0x1575, 0xba61, 0x29a4, 0xb238, 0x40ca, 0xbf5e, 0xab8b, 0x351a, 0x4121, 0xba67, 0xbfd2, 0xacfe, 0xba11, 0x4381, 0x186f, 0x36ef, 0xbae7, 0x1389, 0x1a97, 0x42be, 0x4123, 0x21ca, 0xb2ee, 0xb2b4, 0x463f, 0x2825, 0x449c, 0x4225, 0x4271, 0x1f0c, 0xba51, 0x43c4, 0x4148, 0x017d, 0xbf7b, 0x43e6, 0x41ee, 0xb255, 0x43e6, 0xb048, 0x4033, 0x4282, 0x412e, 0xbae8, 0xaede, 0x4125, 0x41c2, 0x4229, 0xb014, 0x403a, 0xb001, 0xba7c, 0xbada, 0x1dc4, 0x25c1, 0xa5f1, 0x43c5, 0xb293, 0x4011, 0x2dea, 0x1912, 0x43cc, 0xa25f, 0xba41, 0xbfaa, 0x41db, 0x4060, 0x1be4, 0x09de, 0x3dd2, 0x1ca8, 0xb200, 0x460d, 0x4066, 0x02af, 0x432e, 0x1dce, 0xb20f, 0x4468, 0x4058, 0xb037, 0x4137, 0x4158, 0xbf96, 0x39fe, 0x40a2, 0x417e, 0xa421, 0x3a98, 0x12e7, 0x4106, 0xb0fc, 0x2564, 0x40e0, 0xb0a6, 0x0a30, 0x42eb, 0x0ad5, 0x42ec, 0xbacc, 0xbff0, 0x13cc, 0x1bdc, 0x33ae, 0xbf90, 0xba18, 0x42f5, 0xb084, 0x410c, 0x4571, 0xbf94, 0x422b, 0x424b, 0x43a2, 0x433b, 0xba17, 0x1b36, 0x4221, 0x40e2, 0x4247, 0x1e2f, 0x3746, 0x43ef, 0xbf26, 0x1a41, 0x40ae, 0xb2f1, 0x4348, 0x43ce, 0x4128, 0xb27e, 0x400f, 0x41c4, 0xb023, 0x4054, 0x40cb, 0xb260, 0x41c9, 0x3d51, 0x42bf, 0x36f6, 0x1d00, 0x2cc0, 0x412b, 0xbf08, 0x03d1, 0xb280, 0x432e, 0xb0a1, 0xb2d1, 0xa60e, 0xb229, 0x418b, 0xbfe2, 0x438a, 0xba58, 0xa4a8, 0x4393, 0xb2ce, 0xbfd0, 0x447d, 0xb0fc, 0x42c8, 0xba09, 0x43eb, 0xbf42, 0xb24c, 0x19f5, 0x426b, 0xb2a7, 0x1baf, 0x1b6b, 0x4276, 0x0857, 0x22f0, 0xb238, 0x41fc, 0x440b, 0xbf88, 0x2db9, 0x1b79, 0x4117, 0xba00, 0x04c4, 0xbafa, 0x42fa, 0x0f86, 0x439c, 0xa156, 0xb274, 0x4223, 0xbfcf, 0x10c5, 0xb28b, 0x43df, 0x41f3, 0xbf8d, 0x43f8, 0x42b4, 0xb031, 0x0817, 0xb261, 0xbfe0, 0x4137, 0x1da3, 0x404b, 0xbae6, 0xb254, 0x412f, 0xbad5, 0xb295, 0x444e, 0xb28e, 0xb279, 0x406f, 0x45ea, 0xb2db, 0x1d07, 0xbfb3, 0x3b0e, 0x4138, 0x2a53, 0x421f, 0x40bd, 0x43cb, 0x430f, 0x21b6, 0x18d6, 0x43a4, 0xb04e, 0x43a3, 0x420e, 0x467e, 0x4134, 0xb060, 0x44fc, 0x41ed, 0xbff0, 0x43a6, 0x217f, 0xbad4, 0x417d, 0x42e7, 0x3065, 0x1ce3, 0x4016, 0xb218, 0x4219, 0xbf1d, 0x3fb0, 0x2c2e, 0x089c, 0xbaf3, 0x42ef, 0xb26b, 0xbf3d, 0x39ec, 0x4375, 0xb2e2, 0x191e, 0x433f, 0x38b8, 0x4217, 0xb074, 0xb272, 0xbfa4, 0x1bb0, 0x223f, 0xbaf3, 0x4192, 0xba64, 0x4278, 0xbace, 0x01b2, 0x431a, 0xa920, 0x40bf, 0x4498, 0x4331, 0x43fa, 0x4212, 0x41d4, 0x0257, 0x4136, 0x3ca6, 0x4229, 0xbad2, 0x199e, 0x08c5, 0x406e, 0x4480, 0xb2c0, 0xbf70, 0xbf23, 0x4295, 0x46d2, 0x159c, 0x40ca, 0x281b, 0x4333, 0xbfab, 0x1fdd, 0xba6f, 0x3ac7, 0xba26, 0x1da2, 0x4216, 0x1ca9, 0xb263, 0x408e, 0x4491, 0xb230, 0xac9f, 0x4098, 0x4302, 0x41ea, 0x181d, 0xb289, 0xb2fa, 0x1e16, 0x424c, 0x414f, 0x443e, 0x1c8d, 0x428f, 0x2079, 0x44d4, 0x437d, 0xbae2, 0x0ba5, 0xbf43, 0x435f, 0x4446, 0x4383, 0x1b2e, 0xb2c1, 0x1ac6, 0x45d5, 0xaa8d, 0xa9ff, 0x4280, 0xab67, 0xb222, 0xba68, 0x435d, 0x43a2, 0x1957, 0x425a, 0xb03f, 0x42dc, 0xba2c, 0x423f, 0x4071, 0xa66f, 0x06e5, 0xbf51, 0x40e0, 0x30d9, 0x4263, 0xba77, 0x18c4, 0x424f, 0x2054, 0x4136, 0xb09d, 0x3276, 0xb0c2, 0x0886, 0xb20e, 0x3afb, 0x41db, 0x31d9, 0x4062, 0x41cb, 0x1813, 0x40fc, 0x4389, 0xb0b2, 0xbaf4, 0xba68, 0xbf8f, 0x40c3, 0x4068, 0x4017, 0x131f, 0x4310, 0x113a, 0x1dad, 0x43ae, 0xb230, 0x2a6e, 0xb221, 0xb274, 0xbadf, 0xbfc9, 0x23d7, 0xba0a, 0xba0c, 0x40fa, 0xb2b1, 0xbfd0, 0xbf0c, 0x06f7, 0x18c9, 0x46ba, 0xba47, 0x422f, 0x04b2, 0xb2f1, 0x0069, 0x4037, 0xb243, 0x41a8, 0xb253, 0x1a27, 0xb069, 0x42e3, 0x007f, 0xa0c1, 0xaafc, 0xbf87, 0xaa98, 0xb2b1, 0x421a, 0xbaf5, 0x3e9b, 0xbfa0, 0xba22, 0x16cb, 0x408a, 0x1721, 0xb2a9, 0xb2c6, 0x158e, 0x40dd, 0x3ac2, 0xbf5a, 0xafd9, 0x4334, 0xba57, 0x3f09, 0xb0e9, 0xb01e, 0x1bb9, 0xbfc7, 0xb2d0, 0xbac9, 0xb042, 0x4138, 0xbfaa, 0x4429, 0x0e51, 0x424d, 0x423e, 0x3928, 0x415a, 0xa9ee, 0x4049, 0x1bfd, 0xb030, 0x417f, 0x1335, 0x4151, 0x41ad, 0xb2e4, 0x1b22, 0x42cb, 0x34dd, 0x4104, 0x186f, 0x3a3b, 0x437a, 0x42c9, 0x43c3, 0xb275, 0xb281, 0xbfb7, 0x1a57, 0x4257, 0xb06e, 0x215e, 0xa0cf, 0xa019, 0x4438, 0xb2ad, 0x446b, 0xba48, 0x4595, 0x44e3, 0xbf33, 0x4078, 0x4366, 0x1a43, 0x4078, 0x43e9, 0xb0c3, 0x44e9, 0xb008, 0x4000, 0x4015, 0x40f3, 0xbf7c, 0x4063, 0x41e7, 0xbf7d, 0xb0ee, 0xb27e, 0xa702, 0xb2b0, 0x414e, 0xbf00, 0xb2d7, 0x4033, 0x1fb8, 0xb06b, 0x2c22, 0xbfb0, 0x42ca, 0xb220, 0xb019, 0x4274, 0x125e, 0x426d, 0xab1b, 0x4046, 0x465a, 0xb2ca, 0xba26, 0xba09, 0xb07a, 0x4565, 0x4253, 0xbaf0, 0x1b4f, 0xbf67, 0x1ace, 0x403a, 0x1e60, 0x3a08, 0xb030, 0xba66, 0x4176, 0x4003, 0x4227, 0xb000, 0xba2d, 0xbf4b, 0x46a2, 0x4343, 0xba26, 0x438b, 0x4217, 0xb23c, 0x41c3, 0x44a0, 0x40dc, 0x40f1, 0xb29a, 0x1be5, 0xb085, 0x421e, 0x10ec, 0x41da, 0xbfd0, 0x423f, 0x40c7, 0x1689, 0xb2bd, 0x4005, 0xbf7e, 0xba14, 0xb234, 0x41a4, 0x2b94, 0x43f5, 0xb048, 0xb059, 0x0639, 0x4255, 0x14a6, 0x400e, 0x1f55, 0x41ad, 0x3822, 0x4218, 0x1cb8, 0xb22a, 0x4410, 0x40c0, 0xa6c5, 0xb03b, 0xbf46, 0x2d61, 0x43f2, 0x15b7, 0x4347, 0xacc9, 0x422d, 0xb2f4, 0xbfd0, 0xbfe4, 0x42fd, 0xb28c, 0x1d63, 0xba3f, 0x432e, 0x40bc, 0x4337, 0xbfa0, 0x05a9, 0x4160, 0xb030, 0x0da5, 0x0cad, 0x42e0, 0xb2fa, 0x3352, 0x4184, 0x4309, 0x4262, 0x1cfc, 0x43b2, 0xbf25, 0xbaf4, 0x1eb8, 0xba24, 0x3854, 0xb2af, 0x4074, 0x16ab, 0xb28c, 0xa7af, 0x348f, 0x415d, 0x2104, 0xa0dc, 0xba00, 0x129c, 0x413e, 0x40a6, 0x439a, 0x407c, 0x1c34, 0x1b66, 0xb256, 0x425d, 0x423c, 0xbf71, 0xa64f, 0x412c, 0xb257, 0x4035, 0x420d, 0x45da, 0x11e7, 0x403b, 0xb0a8, 0xb2c9, 0x420c, 0xba22, 0xb0f6, 0x211b, 0x41f9, 0xb2b1, 0xbf00, 0xba40, 0xbf45, 0xbada, 0xbade, 0xb24b, 0x424c, 0x4050, 0x40a3, 0x4669, 0xaaff, 0xb089, 0x4335, 0x4104, 0x401d, 0x418d, 0x4000, 0x42f2, 0xa8da, 0x4151, 0xbf38, 0x45a4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x10ecc80c, 0x68d834e9, 0x195770d2, 0xf92fff26, 0x0d6457be, 0xa5d13c2e, 0x614b9061, 0xbd8cee14, 0x4cf5750a, 0x29eac697, 0xd29e8d3c, 0x1f3a1fb0, 0xc8c54efa, 0x28709a39, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x2870a341, 0x50e143f6, 0x2870a3f9, 0x00000000, 0xffffffff, 0xd78f6003, 0x000018dc, 0xffffffff, 0x4cf575b9, 0x525b6130, 0xffffcfff, 0x1bc387ef, 0xfc89683f, 0x28709fd9, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x155d, 0x212d, 0x4196, 0x4176, 0x42cb, 0x4436, 0xba13, 0x41cd, 0x4049, 0x42e3, 0x435c, 0xa81a, 0xba0c, 0xba6d, 0xbfa0, 0xb2d5, 0x42bc, 0xbfc0, 0xbf00, 0x45a9, 0xbf19, 0xba0c, 0xb256, 0xba51, 0x393b, 0xb048, 0xbf0f, 0xb2fc, 0x406e, 0x2068, 0x4007, 0xbf15, 0x4022, 0x4100, 0x4268, 0x408b, 0x42f9, 0xba0d, 0x41f9, 0x1b65, 0x4226, 0xb24a, 0x4677, 0x43ab, 0x3cba, 0x43ad, 0x3eae, 0x42df, 0xbf2a, 0x07ab, 0xb27e, 0x1e70, 0x440d, 0xb280, 0xba74, 0xb0a6, 0x1992, 0x275c, 0xb25e, 0xb027, 0x43c7, 0x4274, 0x4399, 0x40cb, 0x420e, 0x40be, 0x411c, 0x3e42, 0x1e52, 0x44ca, 0xb0b2, 0x4499, 0x418b, 0x3ecd, 0xac43, 0xbf76, 0xa566, 0xa33d, 0x4011, 0xbf22, 0x46ba, 0x45a1, 0x431d, 0x43a3, 0x4288, 0xbf02, 0x4157, 0x25bb, 0x24da, 0xbaf0, 0x4260, 0x12c9, 0x215f, 0xbf32, 0x432a, 0x419b, 0x413e, 0x1b77, 0xb0d4, 0x41a6, 0x0aae, 0xbff0, 0xb00f, 0x4091, 0x400e, 0x4036, 0xba1d, 0x1f20, 0x4194, 0xba73, 0x1a36, 0xbfc0, 0x405c, 0xbf93, 0x381b, 0x42d2, 0xba26, 0x41c8, 0xba43, 0xb03a, 0x410a, 0x4142, 0xb2b6, 0x46e1, 0x4227, 0xab57, 0xb29d, 0xbf3d, 0x195a, 0xba11, 0x42b7, 0x418a, 0x40b3, 0x420f, 0xb285, 0xbf41, 0x1aec, 0x10d6, 0x41eb, 0x2252, 0xbf80, 0xb0db, 0x1e20, 0x43c7, 0x41f8, 0xb24a, 0x4605, 0xbadb, 0x41b6, 0x4267, 0x3473, 0x42c0, 0x42e1, 0x42d2, 0x45aa, 0x4240, 0xbf90, 0x43d7, 0xb03a, 0x411f, 0x42ee, 0x4370, 0x4238, 0x4646, 0x1ce2, 0xbf65, 0x2fa6, 0x41fc, 0xba40, 0x40e0, 0x10fd, 0xb0b7, 0x1851, 0x4300, 0xbaf7, 0xb030, 0xb031, 0xa1cf, 0x135c, 0xb09e, 0x4131, 0xb039, 0x4548, 0x4022, 0xbfb2, 0x35c4, 0xb2a0, 0xb05a, 0xbfc3, 0x40bb, 0x4694, 0x419f, 0xbf80, 0x436f, 0x19c2, 0xb210, 0x1efd, 0x4104, 0x0fff, 0xbfc3, 0x4271, 0x4282, 0x2373, 0xb22e, 0x1d4c, 0x41a8, 0x41e0, 0xb264, 0xb0f2, 0xb202, 0x4087, 0xba73, 0x4351, 0x43cb, 0xae08, 0x40d4, 0x2529, 0xb22b, 0x412e, 0xa5ec, 0x42ce, 0x0a48, 0x3422, 0x186a, 0x2588, 0xba79, 0x40dc, 0x041b, 0xbf65, 0x434f, 0x1ca9, 0xb262, 0x299b, 0x4371, 0x43a0, 0x433d, 0x0586, 0x2f06, 0xb267, 0x4695, 0x4287, 0xbf7d, 0x1d88, 0x4665, 0xb282, 0x1912, 0x401e, 0x40cf, 0x1883, 0x4195, 0xba42, 0x45b4, 0xbf53, 0xb22f, 0xa9fe, 0xba1a, 0x3a48, 0xbaea, 0x4000, 0x07fe, 0x405f, 0x4351, 0xbf1d, 0x40f7, 0xb098, 0xb0a6, 0x3be8, 0x1980, 0xb014, 0x405d, 0x42bb, 0xac85, 0x06b7, 0x42a8, 0x42c8, 0x421e, 0xbf19, 0xb2ba, 0xb0b2, 0x4154, 0x42bc, 0x25fb, 0x4612, 0x4112, 0xb290, 0x40a0, 0x4195, 0x1e8b, 0x3ac1, 0x438d, 0x4340, 0x1a9e, 0x426a, 0x402a, 0x2d5c, 0xba5d, 0xb281, 0xb2ca, 0x4365, 0x191e, 0x2171, 0x3a69, 0x409a, 0x4304, 0xbfe1, 0x4316, 0xb2d2, 0xa9b9, 0x42e7, 0x4684, 0x2f78, 0xbaed, 0xbf81, 0x166e, 0x465e, 0xbadd, 0x4662, 0xbacb, 0x4003, 0x428b, 0x3668, 0x42ce, 0x42ab, 0xb22a, 0x40a0, 0x1cd8, 0x4029, 0xb2a7, 0xb061, 0x160d, 0x424f, 0xba7f, 0x4446, 0x3ea4, 0x0dac, 0xbfca, 0x0a49, 0xbae0, 0xb2d8, 0xabe7, 0xba1f, 0x40b2, 0xbafc, 0x4679, 0x37aa, 0x40e8, 0x12a2, 0xb0fa, 0x3fe1, 0x42a3, 0x1866, 0xba6b, 0x00a0, 0x3304, 0xbf5b, 0x4431, 0x40d8, 0x0a5a, 0x42e0, 0x41c7, 0x1980, 0xb096, 0xbfc3, 0xba10, 0x1efd, 0x2704, 0x419c, 0xb235, 0xbf97, 0x4391, 0x0740, 0x4057, 0x0d91, 0x4122, 0x1f06, 0x42d4, 0x42ce, 0x4211, 0x439a, 0x1d59, 0xbf98, 0xba68, 0x4275, 0x413f, 0x1d15, 0x4180, 0x414a, 0x421e, 0x438a, 0xa29c, 0x3fb4, 0xbf0b, 0x4025, 0x41ab, 0x428d, 0xb2b5, 0xb067, 0xbf90, 0xb258, 0xbf18, 0x2023, 0x43e4, 0x44d5, 0x37d4, 0xb09e, 0x427d, 0x29dc, 0x4171, 0x041a, 0xbfba, 0xb29f, 0xb26a, 0x427f, 0x4238, 0x4381, 0x15bc, 0x430b, 0x2760, 0x1aab, 0xb2ff, 0x4282, 0x103e, 0xa10f, 0x0891, 0x4056, 0x408e, 0x0276, 0xbfb6, 0x094c, 0x2c6a, 0xba4e, 0x403b, 0xb24c, 0xabc2, 0xb0f6, 0x4366, 0x1ded, 0x408a, 0x40bf, 0x4352, 0x10a3, 0xb0bc, 0x42e6, 0x40a5, 0x4551, 0x37ad, 0xbf02, 0xb0f1, 0x43e0, 0xb213, 0x4105, 0xb2bc, 0x40c4, 0xb20e, 0x1117, 0x40b2, 0x412b, 0x1dd0, 0x43a6, 0x43f9, 0x4391, 0x26fd, 0x07df, 0xbf13, 0x4152, 0x22a3, 0x1936, 0x1915, 0x40bb, 0x21c5, 0x2a65, 0x2572, 0x4117, 0x19ba, 0x43c3, 0x41ad, 0xb20a, 0x1fd6, 0x37bd, 0xb00b, 0x41b3, 0xa0b2, 0xb2f5, 0xb209, 0x40f3, 0xb244, 0x4380, 0xb20a, 0x41c7, 0xbf9c, 0xba2b, 0x4064, 0x4048, 0xb2f9, 0xbff0, 0x4399, 0x42bb, 0x1c85, 0x31be, 0x322d, 0x102a, 0x4614, 0x1860, 0x42fb, 0x1fd4, 0x1ba5, 0x4395, 0x3c83, 0xa80f, 0x439b, 0xba4a, 0xbf98, 0x40a3, 0x4065, 0x04dc, 0xbf38, 0x4260, 0xaa25, 0x220a, 0x05e2, 0x40ec, 0xbf60, 0x41b3, 0x127f, 0x40de, 0xb0a8, 0x43ba, 0x41be, 0xbf93, 0x2f9a, 0xb059, 0xb0a5, 0x4196, 0x4343, 0x400e, 0xbfb4, 0x4324, 0xb0fd, 0x09ed, 0xbad8, 0xb02d, 0x150b, 0x4016, 0xbfa6, 0xba3e, 0x439a, 0x3d0b, 0x1b7a, 0xba18, 0x241e, 0xa47d, 0xb22f, 0xb281, 0x40be, 0x4096, 0xb046, 0x43c2, 0x0c15, 0xb079, 0xb21e, 0xbf54, 0x42ab, 0xb234, 0x46b0, 0x1c7b, 0x4297, 0x43f4, 0x029f, 0x3405, 0x42d0, 0x42d2, 0x41b3, 0x40fe, 0x45c4, 0xb0db, 0x43ba, 0x4219, 0x4269, 0x1612, 0x41ac, 0x40ce, 0x3511, 0x4339, 0xb023, 0x130e, 0xb281, 0x1d1d, 0xbfcd, 0x419a, 0xba2b, 0x4011, 0xb295, 0x43f1, 0x40f7, 0xb264, 0xba37, 0xb2d0, 0xbf46, 0x18b3, 0x34c0, 0x040f, 0x401a, 0x138a, 0xa805, 0xb259, 0x440a, 0x1bee, 0xa5a7, 0x43f8, 0x407e, 0x424a, 0xb2f0, 0x433a, 0x39aa, 0x19d8, 0x42d2, 0xa041, 0x41c5, 0x214f, 0xb08f, 0x4231, 0x40b5, 0x05df, 0xbfa6, 0xa527, 0x455f, 0x2dc3, 0x1f02, 0xbaf8, 0x45e2, 0x466d, 0xa17d, 0x39d3, 0x1cdb, 0x4353, 0x43d0, 0x1b82, 0x0fc7, 0x4367, 0xba0b, 0xa21c, 0xbf4d, 0xba44, 0xa48c, 0x4185, 0x4168, 0x27d1, 0x21e1, 0x4316, 0x309b, 0x1859, 0x3d8d, 0x41fa, 0xba63, 0x2168, 0x0966, 0x43f5, 0x35ce, 0x3894, 0x412f, 0xbae5, 0x4557, 0x45b2, 0x4601, 0xb240, 0x4301, 0x4237, 0x4608, 0x1707, 0x08e5, 0xbf57, 0x3831, 0x41fe, 0x1722, 0x1f29, 0x45dd, 0x4233, 0x4550, 0xba2f, 0x3c89, 0xbfc0, 0xbf0c, 0xb2ad, 0x4042, 0x3c09, 0x1f76, 0xb29c, 0x43ce, 0x28fc, 0x4661, 0xb28c, 0x432b, 0x4277, 0xa32e, 0x42ae, 0x41c3, 0x1cad, 0x4203, 0x40bc, 0x43c6, 0xbf78, 0xbae9, 0x4222, 0x0094, 0x4350, 0x41d9, 0x415e, 0xb2b7, 0x4088, 0xb060, 0x40bd, 0xbf6b, 0x1a14, 0xa78c, 0xb2cf, 0x4021, 0x1adf, 0xba10, 0x1bfb, 0x41fe, 0x1b0e, 0x4369, 0x40e6, 0xbfab, 0x4077, 0x4673, 0x2c2d, 0x1df1, 0xb2bd, 0x42b0, 0xbf90, 0x03a8, 0xae03, 0x40ea, 0xbf15, 0x388f, 0x43ef, 0x2e69, 0x4130, 0x40b8, 0x3597, 0x4688, 0x1a86, 0x08aa, 0x4479, 0x45ea, 0x433c, 0xbf60, 0x1e2d, 0x1a2d, 0xb001, 0x1932, 0xaf95, 0x41aa, 0x4477, 0xb050, 0x42f6, 0xb219, 0xb03e, 0xb27e, 0xb0e7, 0xbae9, 0x4189, 0x4077, 0xbfb1, 0x407f, 0x454d, 0x044d, 0x410f, 0x46fa, 0x4265, 0x43aa, 0x41e0, 0x19da, 0x3196, 0x403d, 0x435c, 0x40f5, 0x4067, 0x3122, 0x1ea4, 0x40d7, 0x40dd, 0x1cb3, 0xb241, 0xb20d, 0xae94, 0xb20c, 0x42bb, 0x41a6, 0x4084, 0xa88d, 0xbfb8, 0x42c2, 0xb260, 0xba2d, 0x4119, 0xba63, 0xb2cf, 0x2aaa, 0xa2c2, 0x32e4, 0x42eb, 0xb24c, 0x04eb, 0x43fc, 0xb2c5, 0x4158, 0xb26f, 0x4664, 0x2f2a, 0xb0f7, 0x0290, 0x40d7, 0x1955, 0x1cc8, 0x405c, 0x1d87, 0x40b5, 0xbf89, 0xa488, 0xb2db, 0x401e, 0xb25d, 0xb2c8, 0xba28, 0xb09b, 0x0813, 0x06b2, 0xba07, 0x416e, 0x2476, 0x40c1, 0x1902, 0xb0c8, 0x3d59, 0x26cd, 0xbad0, 0x3338, 0xb2d5, 0x41c1, 0x19e0, 0xbfdf, 0x1e0b, 0x4301, 0x00f0, 0xb252, 0xb2be, 0x4614, 0x1c4b, 0xb00a, 0x44b3, 0xba75, 0x416c, 0x4283, 0x0e46, 0xae89, 0xba0e, 0x4287, 0x3290, 0x4296, 0x43fe, 0xa47a, 0x4243, 0xba58, 0x4076, 0xb2d3, 0xb2ee, 0x42c8, 0xbf8a, 0x4087, 0x465d, 0x0812, 0xb0d0, 0x1b04, 0x41c3, 0xba2c, 0x461b, 0x43c2, 0x1d5b, 0x439d, 0x41d2, 0xbf62, 0x4283, 0x41d3, 0x2cd1, 0x19da, 0xba70, 0xba7d, 0xbf4c, 0x4024, 0xa8a8, 0x3533, 0x4356, 0x4360, 0x40e8, 0x1a0c, 0x4217, 0x46ad, 0x2bcf, 0x4136, 0x41ba, 0xa279, 0x25e8, 0xbfe0, 0xb2a5, 0xa2f0, 0xbf15, 0x41e7, 0xbac9, 0x1f73, 0x410f, 0x0d8a, 0x40d3, 0xab54, 0x4219, 0x1984, 0xbada, 0x4384, 0x1b68, 0x44a8, 0x11d9, 0x4048, 0xbfa6, 0xb2f9, 0x42a0, 0x4237, 0x1298, 0xb0ce, 0x42e5, 0xb2c3, 0x40ce, 0x43eb, 0x29fe, 0x437a, 0xb238, 0xb2e5, 0xa899, 0xb05c, 0xbf91, 0x1947, 0x446b, 0x2586, 0x407f, 0xb275, 0x4193, 0x40eb, 0x439a, 0x2b6e, 0x4378, 0xb2b0, 0xba74, 0x438a, 0x1949, 0xab0c, 0xb208, 0xbae5, 0x4631, 0xbfcd, 0x4165, 0xba67, 0x4038, 0xb007, 0x177e, 0x4275, 0x1b62, 0x2fff, 0x45c6, 0x31d9, 0x429d, 0x18c1, 0x46dc, 0xb2c2, 0x41a9, 0x4390, 0x4362, 0xb0a3, 0xbae3, 0x102a, 0xb08c, 0x27c5, 0xbf0d, 0x1b86, 0xadce, 0xb0bb, 0x1d65, 0x412e, 0xb0d9, 0x2bf0, 0x320b, 0x433e, 0xadb6, 0x406a, 0x20e1, 0x1c81, 0x40ba, 0x13e1, 0x19cd, 0xb2a4, 0xbf5a, 0x46aa, 0x43b8, 0xb211, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x35faf52d, 0x178d83c4, 0xd9eed1e5, 0x2d403c23, 0x142e3ba1, 0xfdb96242, 0xb8568fae, 0xd9e9fcd8, 0xdb303d2f, 0xb29babc4, 0x02ba22df, 0x57781ed2, 0x79ab341b, 0x84723a31, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0x00000020, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000c5, 0x000000c5, 0x000000c5, 0x0fa51006, 0x79ab341b, 0x000000c5, 0x57781ed2, 0x57781ed2, 0xfffffd5f, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x0968, 0x41a2, 0x1b65, 0xb07e, 0xba6a, 0x403d, 0x25a7, 0xba28, 0x0ab4, 0x3baf, 0xbf63, 0x300c, 0x16a4, 0x3997, 0xb26e, 0xb094, 0xb27b, 0x4184, 0x26c7, 0x4019, 0xbf84, 0x437f, 0x1beb, 0x40ff, 0x4103, 0x4642, 0x41df, 0x4310, 0xa2cb, 0xbfbf, 0x33f8, 0x4387, 0x3837, 0x41fb, 0x26da, 0xb076, 0x459d, 0x044e, 0xbf19, 0x4041, 0x021c, 0x1051, 0x17ba, 0xba72, 0xb26d, 0x4418, 0x0d16, 0x4235, 0xb299, 0x2c02, 0xb27c, 0xbaf1, 0xac4f, 0x4434, 0xb20c, 0x0a23, 0x41dc, 0xb00f, 0xbf14, 0x3ed6, 0x404c, 0x45c2, 0x3528, 0x2bdb, 0xa5c7, 0x40b6, 0xbf60, 0x4042, 0x43ae, 0xa0f5, 0xbf62, 0x1fdf, 0xba2b, 0xb2eb, 0xa7ef, 0x1c7a, 0x02de, 0x43c3, 0xbfde, 0xb247, 0xba79, 0x4010, 0x43d3, 0x41eb, 0x4211, 0x43a9, 0x238e, 0x44ec, 0x3f4b, 0xb0e7, 0x4338, 0xb2b1, 0x40d2, 0x438c, 0x409e, 0xb285, 0x191c, 0xb2f6, 0x4047, 0x1d63, 0x213a, 0x400d, 0xb2c8, 0xa8b9, 0x04ea, 0x40ca, 0x407b, 0xbfdc, 0xb295, 0x265d, 0x404a, 0x427d, 0x42d7, 0xb03e, 0x1647, 0x4325, 0x183d, 0x0b07, 0x1c1a, 0xa8e1, 0x4147, 0xae6f, 0x2492, 0x10cf, 0x1af3, 0x411e, 0xa991, 0x12a1, 0xbfa1, 0x40df, 0xa2c7, 0x4275, 0x17de, 0x0093, 0x151f, 0x427d, 0x40d9, 0x4171, 0x429b, 0x3fac, 0x4170, 0x0994, 0x4140, 0xb04b, 0xad99, 0xb257, 0x3c21, 0x4301, 0xba22, 0xbae2, 0x3a3d, 0x4290, 0xbf5a, 0x41e3, 0x424d, 0xba3a, 0xb264, 0xb2e8, 0xbfe1, 0x42b7, 0xb217, 0x42a0, 0x404d, 0xb200, 0x42fd, 0x40c3, 0x410d, 0xb231, 0x434c, 0x41a2, 0x2c01, 0x02ff, 0x432f, 0x4335, 0x42a8, 0x426c, 0x4180, 0x446b, 0xbf80, 0xa9b8, 0x4223, 0xbf45, 0x02ae, 0xb2c5, 0xb291, 0xb0a2, 0x1e0f, 0xbaca, 0x44f9, 0x2594, 0xb02c, 0x1697, 0x1a0f, 0xba47, 0xa098, 0x411b, 0xbae1, 0x45eb, 0x42c2, 0xba6b, 0x409f, 0xb02c, 0x4382, 0xba24, 0xba79, 0x2dda, 0x4141, 0x41fc, 0x462d, 0xb0bd, 0xbfa8, 0x43d8, 0x2cda, 0x43dc, 0x401c, 0xba12, 0xb228, 0x436f, 0x423b, 0xb20a, 0x2089, 0xa5eb, 0x421a, 0xb2dc, 0x406c, 0xbf71, 0x439c, 0x43d2, 0xb27d, 0x43a6, 0xaa82, 0x4195, 0x403d, 0x1811, 0x41c9, 0xbafe, 0x41a8, 0x3d5e, 0x414b, 0xbf61, 0x1955, 0x374f, 0x2d82, 0xb23b, 0xb080, 0x0c12, 0x14c7, 0xba50, 0xb25c, 0x40ab, 0x4132, 0x4270, 0x4375, 0x184b, 0xbf3e, 0xbaec, 0x3645, 0x4045, 0x40e8, 0x1939, 0x40de, 0xbf03, 0x406e, 0x4201, 0x4544, 0x40fc, 0x427d, 0x2550, 0x0046, 0xa85e, 0x39dc, 0x419f, 0x401f, 0xb0a7, 0x4299, 0x1635, 0x430f, 0x40e4, 0x4163, 0x40b5, 0x4304, 0xbf80, 0x07b0, 0x1b13, 0xb207, 0x4218, 0x437c, 0xbf86, 0x400f, 0x4334, 0xb26a, 0x288d, 0x4235, 0x4462, 0xb000, 0xa2a9, 0xba54, 0x42e0, 0xb29e, 0x4273, 0xb09b, 0x341f, 0xb281, 0x33e0, 0x38e9, 0x40da, 0x464c, 0xbf9e, 0xa1f8, 0x40a4, 0x3a31, 0x4190, 0x459d, 0x40ec, 0x2a92, 0x463e, 0xba46, 0x014a, 0x4001, 0x1903, 0x428b, 0x38c6, 0x40d0, 0xb27c, 0x4361, 0x4467, 0x411d, 0x4044, 0x4205, 0x196f, 0xbf2b, 0xb2d9, 0x4351, 0xb29b, 0xbaef, 0x15fc, 0x410a, 0x1ac1, 0xb2c9, 0x150f, 0x419a, 0xb266, 0x0f11, 0x46a2, 0x412d, 0xb2da, 0xb259, 0x42ab, 0x0bad, 0x40a5, 0x42a6, 0x42a8, 0x1d2c, 0x42d0, 0xb2a3, 0x1ac2, 0xbf0a, 0x1c92, 0xb0a7, 0xaaa2, 0xbf5a, 0x4210, 0x428a, 0x1d36, 0xbfd4, 0x42d9, 0xb25f, 0x426e, 0x4198, 0x4200, 0x01dd, 0x1a2b, 0x4216, 0x4482, 0xbadf, 0x406f, 0x1839, 0x41f5, 0x43df, 0xb28f, 0xba7f, 0x402b, 0x412b, 0xb031, 0xbf1f, 0x419c, 0x43dc, 0xa31c, 0x43de, 0xb21c, 0x07e8, 0xa414, 0x41e8, 0x190a, 0x1974, 0x43a7, 0x43ec, 0xba51, 0x42a3, 0x4174, 0x1c77, 0x1cce, 0xb217, 0x0fd5, 0xbf4a, 0x07cb, 0x403c, 0x4011, 0xb20f, 0x2d7e, 0x4049, 0x4131, 0x1a6a, 0x187a, 0x420b, 0x4395, 0x24f5, 0x4009, 0xbf44, 0x0822, 0x0457, 0x137e, 0x1dd1, 0xb093, 0x1543, 0xb2df, 0x4022, 0xb0ca, 0xbf0a, 0xba0d, 0x44f5, 0x433e, 0xb274, 0x41ce, 0xb26d, 0x413e, 0x442b, 0xb0b6, 0x405b, 0x45b9, 0xb271, 0x454f, 0x3b98, 0xb250, 0x42ea, 0xbf41, 0x3ab9, 0x18ac, 0x42c8, 0x1f9c, 0x4342, 0x456d, 0x40d6, 0x32e8, 0x1e55, 0x40af, 0x40f6, 0x406d, 0xb2b0, 0x4081, 0xba3f, 0x45c3, 0xbfc9, 0x1197, 0x46e8, 0xb25f, 0x336f, 0x0655, 0xbf47, 0x460b, 0x467f, 0xaad1, 0xb0d9, 0x3dc1, 0xba3c, 0x1986, 0xbf18, 0x410f, 0x42de, 0x18db, 0xb284, 0xb2cb, 0x433c, 0x2e9e, 0x1c28, 0x4316, 0xbf0c, 0xa396, 0x44e4, 0x043d, 0x421d, 0x24f3, 0x46d8, 0xbfdb, 0x43ba, 0xbfe0, 0x3e5d, 0x42ba, 0x4141, 0x42d3, 0xba66, 0x4113, 0xbae7, 0xbfd2, 0x209b, 0xba1f, 0x430b, 0x40da, 0x25ba, 0x4183, 0x1e9b, 0x4160, 0x4321, 0x41ab, 0x1ddb, 0x1c06, 0x41c9, 0x44e5, 0xba67, 0x2f30, 0x42ad, 0x43be, 0xa2cc, 0x41d7, 0x434e, 0x41a1, 0x3017, 0x34dd, 0xbf42, 0xb253, 0x3bb3, 0x42a0, 0x40f5, 0x1c73, 0x42d9, 0xba2a, 0x42aa, 0xb2f7, 0x424f, 0xbf61, 0x427f, 0x319f, 0x4281, 0x0e25, 0x0bf2, 0x40df, 0x4367, 0xbf16, 0x128a, 0x42b4, 0x43af, 0x1d3c, 0x20d8, 0x42d0, 0x4374, 0xb291, 0x3c17, 0x413e, 0xbf67, 0xba0e, 0xb09e, 0x432c, 0x4355, 0x422b, 0x468a, 0xba1f, 0x414c, 0x4092, 0xb24a, 0x4085, 0x0c34, 0xb0e7, 0x055e, 0x4052, 0x350c, 0x00c1, 0x1b27, 0x42ff, 0xb258, 0x46db, 0xb204, 0xbf9a, 0x204b, 0xb228, 0x4438, 0x42aa, 0x1acc, 0x43c5, 0x40ea, 0x4028, 0x1ff9, 0x0634, 0xba3c, 0x41fe, 0x4205, 0x4310, 0x18b9, 0x1c8a, 0x4390, 0x23ab, 0x4131, 0x4632, 0xbf4e, 0x4110, 0x4337, 0x3ef8, 0xa221, 0xa445, 0x3505, 0x4016, 0xb0be, 0x41ca, 0xbff0, 0x40e0, 0xaf82, 0x43d3, 0x4004, 0xbaf2, 0x44fa, 0x43d3, 0xbadb, 0x1a50, 0x42ba, 0x07db, 0xba13, 0xb230, 0xbadf, 0x43a0, 0x18c3, 0xb2a0, 0xb283, 0xbf41, 0x1d95, 0x1070, 0x2958, 0xb047, 0x42fb, 0xb21f, 0x04f0, 0x43b4, 0xba7e, 0x4200, 0x41f2, 0x42ea, 0x42d1, 0x4270, 0x1f13, 0x434b, 0xb24d, 0x2b14, 0x1577, 0x1eae, 0x4207, 0x46d4, 0x41a0, 0x41d7, 0x0088, 0x4069, 0x1ee5, 0x40a1, 0xbf44, 0x4053, 0x440d, 0x1fe8, 0x427b, 0x4048, 0x40fb, 0x4340, 0xb270, 0x25d9, 0xba7a, 0x1859, 0x393f, 0xb2b3, 0x150b, 0x4332, 0x0152, 0x0589, 0xb2e5, 0x4088, 0x20a7, 0xba4d, 0xa406, 0x37d6, 0x40b6, 0xbadd, 0xbfd7, 0x409b, 0x4116, 0x031c, 0x417b, 0x438a, 0xbff0, 0x40a8, 0x1d5e, 0x426d, 0xb2ee, 0x4380, 0xbf0d, 0xb212, 0x1b52, 0x2108, 0x1b29, 0x1b85, 0x43cb, 0x379e, 0x43b6, 0xbfb0, 0xb0ce, 0x0e2c, 0x02eb, 0x223d, 0x4185, 0x400d, 0xb200, 0x435e, 0x4205, 0xba20, 0xbf5f, 0xb06d, 0x18b9, 0x4235, 0x42e5, 0xbaf4, 0xb0fe, 0x0de1, 0x458c, 0x463b, 0x1a59, 0x1efc, 0xb2de, 0x41df, 0x1f03, 0x3cb2, 0x1c76, 0xa923, 0x462f, 0x1abc, 0xbf0d, 0x41e6, 0x42ad, 0x4625, 0x0d75, 0x45bc, 0xba5a, 0x42b3, 0xba0b, 0x4250, 0x4123, 0x1956, 0x275c, 0xbaf5, 0x3f77, 0xba2f, 0x1b93, 0x4209, 0x4370, 0xb2d5, 0x4164, 0xbfe8, 0x40c2, 0x4399, 0x40ee, 0xbacf, 0x4230, 0x4055, 0x4342, 0x428e, 0x4372, 0x1af5, 0xb0f6, 0x4096, 0x1a4a, 0x1c74, 0x1b7b, 0x27d4, 0x1dfb, 0x3d43, 0x0c87, 0x2649, 0xbaf2, 0x40dc, 0xbf70, 0xbff0, 0xb24a, 0x40f6, 0xbf62, 0x42da, 0x46d3, 0x407b, 0x432d, 0xb207, 0x427a, 0x417b, 0x4156, 0x291d, 0x41c1, 0x41c8, 0x42d8, 0xb27c, 0xb0a5, 0x42cf, 0x2f18, 0x4661, 0x426a, 0x4316, 0x3f27, 0xbade, 0x42fd, 0x43e9, 0xbf5b, 0x4172, 0xac9d, 0x429e, 0x444b, 0xa3d2, 0x19ac, 0x411e, 0x211e, 0x40dc, 0x4332, 0x405f, 0x2ae6, 0x1bc6, 0xba2b, 0x1f21, 0x4124, 0x404b, 0x42b0, 0x4217, 0xb2b4, 0xb0cc, 0x2e76, 0x1f91, 0x23f0, 0xb206, 0x0d78, 0x4323, 0x312a, 0xbf46, 0x4198, 0x42de, 0x430c, 0x433a, 0x40f4, 0x41ea, 0x1c1d, 0x447e, 0xbae1, 0xa070, 0x415a, 0x4111, 0xb026, 0x4323, 0xbf72, 0x4419, 0x410e, 0x04c5, 0x422f, 0x16fa, 0x3627, 0x24fc, 0x42b9, 0xbf6e, 0x42ef, 0x1589, 0x05b3, 0xbf84, 0x3920, 0x01a7, 0x0d74, 0x4240, 0x241c, 0x1b1f, 0x408d, 0x1f58, 0x40d2, 0xb072, 0xbf29, 0x1994, 0x16a6, 0x403f, 0x4145, 0x2d03, 0xa4f8, 0x1b74, 0x4195, 0xba79, 0x4418, 0x4037, 0x40f8, 0xba19, 0xba7c, 0x44c8, 0x435a, 0x40e3, 0x43c3, 0x431b, 0x4124, 0x409b, 0x4355, 0x42db, 0x4300, 0x17a9, 0x3dfd, 0xbf0b, 0x41eb, 0xbac5, 0x0b64, 0x1f66, 0x098c, 0xbf60, 0x456a, 0xb0c2, 0x223c, 0x4301, 0xb20e, 0x4628, 0x4062, 0x1d7d, 0x43b7, 0x4369, 0x424c, 0xbaf7, 0xbfa9, 0xbace, 0x41c5, 0x4390, 0x420e, 0x40ff, 0x41f4, 0x4186, 0x4022, 0xbfa0, 0x4151, 0xa81d, 0x4019, 0x4220, 0x41f5, 0xbfcf, 0x43b6, 0xb290, 0xbfc0, 0x4311, 0x437d, 0xbfa0, 0xb2a4, 0xba2a, 0x41e6, 0xbf81, 0x0d08, 0x1e47, 0x1cb0, 0x4370, 0x12b8, 0xb261, 0xb26a, 0x437c, 0xbafe, 0x42b8, 0x43e5, 0xb0e5, 0xba58, 0x4158, 0x42a0, 0x4311, 0x4144, 0x1fc0, 0x1dc0, 0x08a7, 0x171f, 0x4637, 0x445d, 0x0a52, 0x1f8e, 0x1a78, 0xb2e5, 0xbf42, 0xba74, 0xba51, 0x1dd6, 0x45b6, 0x4278, 0x4389, 0x4230, 0xa3e1, 0x0f67, 0x42d0, 0xba44, 0xbfb0, 0x4383, 0x4583, 0xad13, 0x404e, 0x43b2, 0x4295, 0x0b4d, 0x42e6, 0x42bf, 0x107b, 0xbfb8, 0x1fa3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x5384f6cd, 0x93b1eb86, 0x519fcf8e, 0x564cda67, 0xcde58997, 0xf3ec3c43, 0x6c08d4ea, 0x6e3c1d46, 0xe8c48cc6, 0xcea89f8e, 0x2f68758c, 0x0e162d4d, 0xc7597952, 0x77abbef9, 0x00000000, 0xa00001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000007, 0x00000000, 0xdcbede57, 0xcea8b10a, 0x00011309, 0x0e162d4d, 0x00011309, 0xf5b62f3f, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1cdd, 0x1c98, 0xa82c, 0x22bd, 0xabb4, 0x4105, 0x3601, 0x432b, 0x406b, 0xb08c, 0xbacf, 0xb2ee, 0x382b, 0xb055, 0x4418, 0x16af, 0x4171, 0xb284, 0xb2c7, 0x4273, 0x43c5, 0xbf28, 0x4374, 0xb22c, 0xa063, 0x4242, 0x43f9, 0xba6a, 0x42a1, 0x4311, 0x4298, 0xb2b4, 0xbfcf, 0xba03, 0x41ed, 0xba7d, 0x444d, 0x1db4, 0x1de9, 0x192c, 0xbac1, 0x2cd3, 0xbfc9, 0xb224, 0xba09, 0x4098, 0x3ae8, 0x4310, 0xba6a, 0x4366, 0xb05a, 0xbf43, 0x406f, 0xb0a2, 0x0a86, 0x41c1, 0x413c, 0x1e56, 0x224c, 0xbf0e, 0xb016, 0x40d3, 0x40eb, 0x3b28, 0x4131, 0x30c8, 0xb23a, 0x40db, 0x2ff3, 0xb293, 0x1ea8, 0x4283, 0x4071, 0x40a5, 0x40db, 0x1539, 0xb2ff, 0xb0d4, 0xb2a4, 0xb2f4, 0xbf60, 0x433c, 0xbf0b, 0x3bac, 0x134e, 0xb241, 0xba6a, 0x1bf6, 0xb28b, 0xba3f, 0x43b1, 0x1f92, 0xba68, 0xb21e, 0x4272, 0x40a4, 0x2fde, 0xaec9, 0xad72, 0x42f0, 0x433f, 0xaa74, 0x1032, 0xa843, 0x19d7, 0xaf1b, 0xbf23, 0x14cb, 0x41a3, 0x1ff5, 0x40ca, 0xba1e, 0x4205, 0x4123, 0x401d, 0x1f2f, 0xbfc0, 0x3d68, 0x4368, 0x3441, 0x43e1, 0x04bf, 0x1934, 0xb299, 0x41bc, 0x4331, 0x00c2, 0xbf74, 0x4424, 0xb0de, 0x3563, 0xb2ea, 0x4325, 0x425f, 0x4331, 0x1516, 0x2bfa, 0xaa20, 0x4001, 0xb2dd, 0x1b4a, 0x435d, 0x1e15, 0x46fb, 0x1ca3, 0xb240, 0xba1e, 0x43ac, 0x04d1, 0x4288, 0xb053, 0x0649, 0x40ab, 0xbf6a, 0xa873, 0x41bd, 0x40bf, 0x0734, 0x410d, 0xbaf5, 0x431d, 0xb21d, 0x43bc, 0x1a8d, 0x43d1, 0x182f, 0xb2a7, 0xba53, 0xa1e5, 0x1f14, 0x40fa, 0x4595, 0x4373, 0x3a9e, 0x4316, 0x4382, 0x2233, 0x3f1e, 0xbfac, 0x285f, 0x435e, 0xb2e4, 0x1a0c, 0xbae3, 0x432c, 0xbf96, 0x3dd0, 0x4388, 0xb233, 0x1dd2, 0x0628, 0x40f6, 0xad19, 0xbfb0, 0xb01e, 0x3f19, 0x42fb, 0x4397, 0x42fb, 0x4380, 0x088d, 0x1c55, 0x4125, 0x41db, 0x4343, 0x285a, 0xb297, 0x0708, 0x1742, 0x438f, 0x40c9, 0x41ba, 0xb2d7, 0x42f4, 0xbf84, 0xb2be, 0x0ed1, 0xaa8e, 0x1766, 0xb2a2, 0xb23f, 0xb246, 0x42e7, 0xa98e, 0x2ed6, 0xba33, 0x4275, 0x3d19, 0xba01, 0xaf24, 0xba0e, 0x10fe, 0xbf70, 0x40ae, 0x0b85, 0x1b11, 0xba72, 0x1dbd, 0x4218, 0x420f, 0x43df, 0xba42, 0xbf27, 0xba3d, 0x4318, 0x4152, 0x431f, 0x417d, 0xbfaa, 0x1b13, 0x401f, 0x426e, 0xbfb1, 0xb098, 0x1cb5, 0xba05, 0x2f51, 0xa7e9, 0x4238, 0xb2de, 0x4374, 0x3239, 0xb0c3, 0xb2c4, 0xb2db, 0x43d1, 0x17fa, 0x1918, 0xba45, 0x4073, 0x19dc, 0x433b, 0x095c, 0x1f3f, 0x2dfd, 0x2105, 0x4120, 0x4174, 0x42cf, 0x32ac, 0xbf4a, 0xb28e, 0x401d, 0x445e, 0xbf00, 0xbf0d, 0x4353, 0xbfb0, 0x406e, 0x3f79, 0x2e94, 0xb225, 0x40fe, 0xa79f, 0x43fa, 0xb21f, 0x3c1d, 0xbaf9, 0x4178, 0xb224, 0x455b, 0x2da3, 0x36c0, 0x410b, 0xb2bd, 0x1fd4, 0x43ed, 0x4658, 0xbf45, 0xa2bb, 0xba4b, 0x1848, 0x407f, 0x0d0c, 0xb0d2, 0xbfb8, 0xb013, 0x41ff, 0xba13, 0x4246, 0x4413, 0x3ac4, 0xbf25, 0x0c28, 0xba3d, 0xbf00, 0xba77, 0x4115, 0x425e, 0xad5a, 0x44b9, 0x16b5, 0x445c, 0x4030, 0x41ba, 0x4337, 0x435f, 0x25e8, 0xbfe0, 0x43bf, 0x2e8c, 0x2603, 0x1d02, 0x339f, 0xb2da, 0xbf12, 0x4073, 0x46ed, 0x408a, 0x42a4, 0xbfa0, 0x428c, 0x3f60, 0xb264, 0x4656, 0xbf54, 0x4212, 0x1b30, 0x2cc3, 0xb2d3, 0x1c32, 0x4279, 0x2647, 0x4222, 0xba14, 0x1f8d, 0x414a, 0x4244, 0x4316, 0xa3df, 0x42d7, 0x42fc, 0xbf90, 0x4183, 0xa6b7, 0x4002, 0x2fb3, 0x4304, 0xba10, 0xbf80, 0xb21c, 0x414e, 0x1bc7, 0xbfad, 0xb2cd, 0x2f6f, 0xa397, 0x180a, 0xab69, 0xb0db, 0x0d11, 0xba35, 0xbfdb, 0x00c5, 0xa4b1, 0x400a, 0xba74, 0xa723, 0x050c, 0x4318, 0xaa07, 0x4135, 0x4181, 0xba71, 0x4090, 0x41a2, 0x412a, 0x4230, 0x4398, 0xbf18, 0x42c6, 0x441a, 0xb297, 0xb2fa, 0x41b7, 0x4134, 0xb290, 0x41c4, 0xb290, 0x195c, 0x4586, 0x433e, 0x402c, 0x34a4, 0x1add, 0x4316, 0x403b, 0xba51, 0x4257, 0x3d75, 0xba2d, 0x170f, 0x45bb, 0xbf26, 0x43ca, 0xb247, 0x427e, 0x4109, 0x1284, 0xbaf7, 0xbae0, 0xb200, 0x428b, 0x4376, 0x1cfc, 0x4091, 0x2b38, 0x1f17, 0x41c4, 0x40f0, 0xba52, 0x0c05, 0xbfaa, 0x4278, 0x41b2, 0x4334, 0x43b9, 0x42e4, 0x4338, 0x46cb, 0xaecc, 0x2b5b, 0x2407, 0xbace, 0x1ff5, 0x41ba, 0xb013, 0xb283, 0xbf94, 0x1c7b, 0x4444, 0xaf5b, 0x1e40, 0xbfe0, 0x4401, 0x40be, 0xa46a, 0x4229, 0x40a3, 0x43b4, 0x43e9, 0xb268, 0x419f, 0x41b5, 0x43b9, 0x4203, 0xbfa7, 0xb0a5, 0x46b9, 0x42ec, 0x1b9e, 0x400b, 0x06d9, 0xb0b2, 0x4230, 0xb0c6, 0x420e, 0x19d4, 0xbfe1, 0x40f8, 0x1f60, 0x19b8, 0x43c1, 0xb2a7, 0x4357, 0x4364, 0x4320, 0xba6a, 0x15cc, 0x40a4, 0x4378, 0xb0fe, 0x2e96, 0x4183, 0xba02, 0x4387, 0xb27f, 0x42cf, 0xaa6a, 0x404c, 0x4269, 0x036c, 0x42cf, 0x43df, 0x42a2, 0xbfda, 0x078b, 0x402b, 0x1d71, 0x43ee, 0x1a55, 0x1d58, 0x4077, 0x4021, 0x4165, 0xbf4a, 0x42c7, 0x4322, 0xb00e, 0x435a, 0x1fd9, 0xb227, 0x40da, 0x123a, 0x4323, 0x4353, 0xaa7b, 0xbf16, 0x40b0, 0x42df, 0x4334, 0x054d, 0x422d, 0xba0b, 0x46da, 0x44f2, 0xb214, 0x1a61, 0x20d7, 0x43b2, 0xb062, 0x369a, 0xa256, 0xb2f1, 0xb20f, 0x43ba, 0x3b86, 0x4278, 0x1b0f, 0x2c13, 0x4211, 0x46a0, 0xbf88, 0x43b1, 0x4094, 0xba06, 0x45d8, 0x410e, 0x1fc8, 0x1e0c, 0x429a, 0x3c95, 0x45ec, 0x42a6, 0xb2d2, 0x4039, 0x42c0, 0x40a9, 0x41f3, 0x1eb9, 0x04a4, 0xba5d, 0x4256, 0x4469, 0x40f4, 0x1687, 0x4244, 0x400d, 0x2eb0, 0xbf6f, 0xb018, 0xba24, 0x1911, 0x115e, 0x43c1, 0x406a, 0x4008, 0xb076, 0xba5f, 0xb0f5, 0x40a6, 0xb2f4, 0x0d52, 0xba72, 0x4172, 0xbf91, 0x1310, 0x4572, 0xad5e, 0x4399, 0x4081, 0x2c80, 0x258f, 0x1d8c, 0xb067, 0x426e, 0x40c0, 0xb2d0, 0x4544, 0xbf70, 0x4236, 0x43d2, 0x422f, 0x43e9, 0xb0a2, 0xbf3a, 0xa951, 0xb051, 0x1995, 0x42af, 0x4099, 0x40bb, 0x4221, 0x40b8, 0x4234, 0x422e, 0x444a, 0x2d03, 0x145f, 0x0cf6, 0x438c, 0x4358, 0x41e5, 0x4336, 0x0b76, 0x43e5, 0x4305, 0xb0fe, 0x412c, 0xa59e, 0x3a6d, 0xb23e, 0x4010, 0xbf23, 0x3890, 0x00ee, 0x40ec, 0x1a97, 0x1810, 0x436c, 0x43df, 0x4019, 0xb20f, 0xbfc0, 0x17e0, 0x0e47, 0xbfc8, 0x4068, 0x43bf, 0xbafa, 0x41be, 0x355a, 0xbfc3, 0x18e7, 0xbff0, 0x40e2, 0xbff0, 0x4132, 0x4207, 0xb042, 0x26ad, 0x4329, 0xb285, 0x425e, 0x46da, 0x100a, 0x070c, 0x40d9, 0xb2d3, 0xa9b2, 0x44fa, 0x3edc, 0x1d54, 0x435f, 0x3e4f, 0x2d4a, 0xbfcc, 0x4070, 0x4111, 0x42b6, 0x40f8, 0x210e, 0x3003, 0x41f4, 0x41e1, 0x1e19, 0x4152, 0xba39, 0xbaf6, 0x42aa, 0x4086, 0xa1ff, 0x42d5, 0x4294, 0x226a, 0x407c, 0x1f25, 0x3bea, 0x4158, 0x45bc, 0x445a, 0x29b5, 0xb2ed, 0xba7d, 0xbfa9, 0x41af, 0x31bf, 0x1f06, 0xb277, 0x4600, 0x40a6, 0xba24, 0x4023, 0x413b, 0xbac2, 0x1cdb, 0xba24, 0x00b5, 0x400b, 0x14e9, 0xba47, 0x42a3, 0x1eb7, 0x41b1, 0x42d2, 0x3742, 0xbfdc, 0x17d4, 0x1b82, 0x409c, 0x3640, 0x1a72, 0x4055, 0xba27, 0x4357, 0x2367, 0x43b5, 0x4607, 0xbf59, 0xb0c5, 0x1926, 0x4121, 0xba51, 0xb203, 0xbaf7, 0x066a, 0xb2b3, 0x43a6, 0x432b, 0x40b0, 0x085e, 0x0b22, 0xa46e, 0xbae7, 0xb2f0, 0x1d0e, 0xa37d, 0xba21, 0x40eb, 0x1083, 0x1bb3, 0x4223, 0x43a5, 0xb253, 0x402e, 0x4108, 0xbf37, 0xba0c, 0x43c0, 0xb079, 0x422d, 0x4081, 0x40e0, 0x45b9, 0x43b2, 0x4685, 0x189b, 0x400c, 0x0dbd, 0x425a, 0x0e61, 0xba61, 0x1ad3, 0x4335, 0x41a4, 0xbf64, 0x3714, 0x409a, 0x428d, 0x4421, 0xa9d1, 0xb298, 0x1dd4, 0xb028, 0x4600, 0x128e, 0x42da, 0x1a55, 0xb0da, 0xbf16, 0x423b, 0xba54, 0x42fa, 0x17f3, 0x0753, 0x4299, 0x414a, 0x1e9a, 0xb06c, 0x432b, 0xa23d, 0xba29, 0x4367, 0x19f0, 0x40c1, 0x1e5f, 0xb249, 0x1e21, 0x3078, 0x36fa, 0x2c53, 0xb033, 0x4362, 0x43cd, 0xa40c, 0xbf53, 0xba37, 0x4057, 0xb004, 0x411f, 0xbae2, 0x4671, 0x4276, 0x2505, 0x1044, 0x2743, 0x44cb, 0xaaed, 0x44e5, 0x04b3, 0x41bf, 0x1864, 0x4195, 0x4598, 0xb01d, 0xbf49, 0x4264, 0x4005, 0x0615, 0x43d3, 0xbf21, 0x4175, 0xb218, 0xba29, 0x4168, 0x1b8c, 0x407e, 0xbfa0, 0x4181, 0xb22d, 0x4350, 0x0803, 0x04f2, 0x1a2b, 0xba50, 0xbac6, 0xbf8d, 0x14a4, 0x459a, 0x41d1, 0xba19, 0xba51, 0x1cdb, 0xb283, 0xadd2, 0xb0a3, 0x4140, 0x1e4e, 0xb05a, 0x40ee, 0x428d, 0x37d3, 0x401b, 0x029e, 0x4351, 0x4230, 0x40ae, 0x40ab, 0x430f, 0x4121, 0x197e, 0x4307, 0x43ae, 0xb282, 0xab6a, 0xbf96, 0xb083, 0x41dc, 0x1d8b, 0x4131, 0x43f3, 0xb2af, 0x43e5, 0xb0bb, 0xb06d, 0x10ae, 0x42ef, 0x0d29, 0x4252, 0x1ef1, 0x400f, 0x4287, 0x1f0a, 0x0879, 0x38bd, 0x4306, 0x1094, 0x1b2a, 0x42ec, 0x26b2, 0x42fb, 0xbfbf, 0x1e68, 0x400f, 0x4261, 0x4248, 0x4301, 0x03a6, 0x401d, 0x43a6, 0x42ff, 0x412f, 0xb078, 0x2f4c, 0x059e, 0x404d, 0x40d0, 0x4033, 0x4067, 0x41d5, 0x40f7, 0x4044, 0xbff0, 0x4183, 0xbf23, 0xb208, 0x1922, 0x43cb, 0x2b84, 0xbf46, 0x1c9d, 0x2a1d, 0x41f7, 0x43d2, 0x4297, 0x11e1, 0x3929, 0xba74, 0x1b10, 0xb267, 0x1dc6, 0x403a, 0x420d, 0xb246, 0xb2e7, 0x0450, 0x04fc, 0xbf0e, 0x4680, 0xb015, 0x0f61, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x9761e594, 0x04a76bb3, 0xdf9b4478, 0xd3a7ec97, 0xb0bb8660, 0x52cb52ca, 0x60d713aa, 0x30959c7c, 0xca01d692, 0xd5efbf25, 0x11a77f9a, 0x39816f99, 0x60fa61fd, 0xf4de7402, 0x00000000, 0x200001f0 }, FinalRegs = new uint[] { 0x00000000, 0xffffffd5, 0x00000000, 0x9e100020, 0x00000000, 0x9e100022, 0xffffffa5, 0x00000000, 0x00000000, 0xf4de7411, 0xd5efb38f, 0xcace1224, 0x60fa61fd, 0x60fa67ad, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x203c, 0x4138, 0xb29a, 0x402d, 0xbf0a, 0x4151, 0x42a8, 0x4489, 0x4091, 0xb2e9, 0xbf8d, 0xb205, 0x41e3, 0x08fb, 0x048b, 0x401f, 0xb2c6, 0x4106, 0xbf00, 0xba13, 0x391f, 0xba43, 0x4000, 0x407c, 0xba09, 0xb2bd, 0xbf25, 0x13a2, 0xb2ca, 0xaf42, 0x4327, 0xb2d4, 0xb2be, 0xaf98, 0x416c, 0x2cbd, 0x41c7, 0x401e, 0xb281, 0x4107, 0x42d2, 0xbfd2, 0x1939, 0xb248, 0xa7cd, 0x1ac5, 0xbf00, 0x2d7b, 0xb260, 0x43c6, 0xb28f, 0xb29a, 0x3844, 0x3701, 0xb08b, 0x1d56, 0xbff0, 0x42aa, 0xb2d2, 0xbfd8, 0x21a3, 0x34fc, 0x4617, 0x41a3, 0x284d, 0x447c, 0x42dc, 0xba10, 0x42ad, 0x1858, 0x1b99, 0x009e, 0xba48, 0xaf30, 0x413b, 0xbf6b, 0xba13, 0x1e8b, 0x29ae, 0x40bb, 0x1f0d, 0x1bf6, 0xb0fa, 0xb085, 0x1c93, 0xae0c, 0x44ad, 0xb294, 0x4327, 0x1ea8, 0x0fbb, 0x4172, 0x43e1, 0xb274, 0xab3a, 0x421e, 0x4003, 0x4370, 0x189a, 0x4289, 0xbf1d, 0xba66, 0x1d6f, 0xb2da, 0xbfb0, 0x4017, 0xa419, 0xba6f, 0x127d, 0x43f9, 0x0b3c, 0x1a9d, 0x1c19, 0x2d38, 0xbfa0, 0x38c1, 0x406f, 0x181c, 0x431a, 0x4069, 0x4359, 0xaa68, 0x1e23, 0x0012, 0x047a, 0xbf6e, 0x15c1, 0x4572, 0x4344, 0x0ff8, 0x46e4, 0x41db, 0x3216, 0x421c, 0x4195, 0x23cb, 0x4261, 0xa31a, 0xbad8, 0x06e8, 0x4197, 0xbf69, 0x420d, 0x228b, 0xbae0, 0x302d, 0x4046, 0x4016, 0x4137, 0xbf79, 0x2c74, 0x407b, 0xa65d, 0x401b, 0x43ea, 0x09f7, 0x41ad, 0x401d, 0x3324, 0xb2cb, 0xa421, 0x40b4, 0x43f1, 0xbfbe, 0x3ae0, 0x45c1, 0x4014, 0x41c2, 0x4596, 0xbaf1, 0xbf1f, 0x4035, 0xb29e, 0x4070, 0xb0cc, 0xbfb4, 0xbad1, 0xba09, 0x1b93, 0x414a, 0xbf43, 0x40b8, 0xb0ef, 0x41c2, 0x4157, 0x36c8, 0x42b7, 0x40dd, 0x43ab, 0x0d39, 0x32b9, 0x1cfc, 0x1c79, 0xa753, 0x4613, 0x418a, 0x40e8, 0xb25e, 0x0bf3, 0xbf9f, 0x4100, 0x3e0f, 0xb205, 0xb2d7, 0x12b8, 0x430e, 0x4032, 0x42a7, 0x18fe, 0x1ac1, 0x221c, 0xb086, 0x0cf7, 0x4367, 0x426e, 0xabea, 0x4282, 0x1da5, 0xaca3, 0x4277, 0xbf24, 0x4308, 0xb035, 0x41c0, 0x1434, 0xaf0d, 0xbfa0, 0xb20a, 0xbacd, 0xb2e8, 0x0248, 0x40cb, 0x41be, 0x43c5, 0x43a2, 0x40cb, 0x0c9a, 0x2979, 0x0138, 0x1f54, 0x4166, 0xb2de, 0x1e7e, 0x4132, 0x4194, 0xb2a8, 0xbfc3, 0x419d, 0xbadc, 0x43db, 0x4363, 0x42a6, 0xb02d, 0x40c1, 0x32dc, 0x467d, 0x43b0, 0x4581, 0x431c, 0x4232, 0x3daa, 0x436d, 0x4288, 0x40f7, 0x1554, 0x43f1, 0xbf0d, 0x0509, 0x2371, 0x4387, 0x42bc, 0x1f69, 0xb07f, 0x43e2, 0x29d5, 0x41d7, 0x297a, 0x4285, 0x09c3, 0xb2e8, 0x417d, 0xba00, 0x42d1, 0x4286, 0xb203, 0x40c7, 0x46e4, 0x4558, 0x40d4, 0x12b2, 0x3bf4, 0xb09a, 0x1a12, 0xbf48, 0xbaf4, 0x1858, 0x412f, 0x051a, 0x21cf, 0x4118, 0xbaf9, 0x1a50, 0xb245, 0xbf78, 0x43bd, 0x416a, 0x4178, 0xae37, 0x425a, 0x4038, 0xa7bb, 0x414d, 0x1bcd, 0x395a, 0x411d, 0xba0a, 0x41d4, 0xbf80, 0x059e, 0xb0bb, 0x0522, 0xb276, 0xb2f9, 0x2920, 0xa495, 0xbac2, 0xbfe4, 0xbfe0, 0x4081, 0xb0f1, 0xb258, 0x45b1, 0xa68e, 0x428f, 0xb015, 0x11ae, 0x0d68, 0xb255, 0x44e4, 0x43d2, 0x42ae, 0x310f, 0x1e7e, 0xb030, 0x2a4f, 0xbfa1, 0x1f11, 0xb213, 0x4237, 0x1e41, 0x43d5, 0xb2e0, 0x425f, 0xbf00, 0x432a, 0x0299, 0x18de, 0x434f, 0x4304, 0xb06a, 0x33aa, 0x1e4d, 0xba46, 0x42ca, 0x4115, 0x4116, 0xa597, 0x30fc, 0x10be, 0x41a3, 0xbf48, 0xa37b, 0x40eb, 0xb0e4, 0xb28b, 0x4002, 0x416c, 0x40aa, 0x461b, 0x43b9, 0x18d6, 0x4276, 0x4447, 0x4692, 0x464b, 0x427f, 0xba74, 0xba05, 0x2045, 0x1bad, 0x4097, 0xa1f9, 0x41a4, 0xb2e0, 0xbf34, 0xba77, 0x1ef1, 0xba6f, 0x435f, 0x44fb, 0xbf74, 0x4247, 0x4275, 0x1e8d, 0x40bc, 0x2747, 0x4001, 0x419c, 0x20f8, 0x428d, 0x02eb, 0xbac8, 0x0ba6, 0x44ac, 0x41ce, 0x4314, 0x0bdb, 0x1251, 0xb256, 0x423b, 0x4089, 0x4066, 0xbf63, 0x1cd3, 0x4095, 0xba21, 0x430c, 0x3453, 0x42b6, 0x0ace, 0x4181, 0x40ff, 0x4215, 0x43a8, 0x4244, 0x1aa6, 0x1a97, 0xa74d, 0x428c, 0x400a, 0xbf99, 0x1c2b, 0x2b70, 0x437c, 0x408d, 0x4367, 0xbf68, 0x3c30, 0x4382, 0x4227, 0x4266, 0x0e7d, 0xbae3, 0x1ab4, 0x43aa, 0x45f4, 0x4248, 0x15a1, 0x4152, 0xb2e6, 0x1a40, 0xa73a, 0x424e, 0x0b31, 0x1b75, 0xae32, 0xba41, 0x1b3a, 0x45d9, 0x1b7a, 0xbfd7, 0x4326, 0x46aa, 0x40b1, 0xb233, 0x42c9, 0x1a6b, 0xa894, 0x43ca, 0x433d, 0xbf45, 0x2f43, 0x421c, 0x1234, 0xb0be, 0xba6d, 0x1cc2, 0x43d3, 0xb248, 0x1f29, 0xb2e3, 0x4365, 0xbace, 0x31e2, 0xbf63, 0xa256, 0x1be2, 0x4420, 0x4339, 0xba0b, 0x1d57, 0x4694, 0xb28d, 0xbfad, 0x407f, 0xa64a, 0x4308, 0xbfd0, 0x443f, 0x05d3, 0xbfa3, 0xb27f, 0x3293, 0x40df, 0x41ff, 0x42e4, 0x4213, 0xb04d, 0x3ee4, 0x41d8, 0x1e59, 0xa2f5, 0x049c, 0xb2e5, 0x406f, 0xab82, 0x428f, 0xba3f, 0x41db, 0x415f, 0xbfe0, 0x406c, 0x46c8, 0xb29e, 0x1834, 0x067a, 0x4142, 0x433c, 0xb2d4, 0x045c, 0xbfdd, 0x43cb, 0x4105, 0x307f, 0x42a5, 0xaa0d, 0x1152, 0x4082, 0x338b, 0x1c40, 0x44a3, 0xbf67, 0xb28c, 0x1cfd, 0x1a7c, 0xb289, 0xbaca, 0x4056, 0x0316, 0x459d, 0x188c, 0x4341, 0x424e, 0xaaac, 0x0908, 0x41b4, 0xb233, 0x4150, 0xb2d1, 0x2241, 0x179d, 0xbfc7, 0x4249, 0xa363, 0x4179, 0x4125, 0x40d2, 0x429e, 0x41a1, 0xb23f, 0x386c, 0x0051, 0xba75, 0xba3d, 0xbfce, 0xb000, 0xbae5, 0x4402, 0x4366, 0x4374, 0x4546, 0x4185, 0x3555, 0xbf7b, 0x40ed, 0x39b8, 0x43f7, 0xb05d, 0x40cd, 0x4176, 0xbfd0, 0x2425, 0x1f85, 0x1b56, 0x4265, 0x403c, 0x4043, 0x4308, 0x4164, 0xb20f, 0xb2f7, 0x4336, 0x26a7, 0x026a, 0x45da, 0x236e, 0x4342, 0xa109, 0xa913, 0xbfd7, 0x2392, 0xba4b, 0xb270, 0xba5a, 0xb02f, 0xa1c0, 0xba4a, 0x41c4, 0x411e, 0x20bf, 0x4331, 0x1519, 0x1987, 0x1931, 0x44c5, 0x1546, 0xbac4, 0xbf48, 0xb2eb, 0x403f, 0x237f, 0x435e, 0xb229, 0x1cac, 0xbae2, 0xbacf, 0x42c9, 0x42d2, 0x0630, 0x22af, 0x407b, 0x43d6, 0x1a64, 0xba0e, 0x3269, 0x364a, 0xba0d, 0xae94, 0x4310, 0x357b, 0x41f1, 0xbfc9, 0xb2d1, 0x434d, 0x4223, 0x41b8, 0xb2b9, 0xbf80, 0x41af, 0x401a, 0xbf9e, 0x062f, 0x39e3, 0x4007, 0x41ff, 0x419a, 0x4379, 0x39f6, 0x43be, 0x2e46, 0xbadb, 0x1dc4, 0xa8b1, 0x1e25, 0x431d, 0x42af, 0x41f8, 0x232f, 0xba70, 0x4018, 0x4111, 0x4160, 0x40a1, 0xbfe8, 0x4001, 0x42a3, 0xab81, 0x420a, 0x41c6, 0x4366, 0x40f4, 0x3117, 0x4065, 0xba39, 0xb2f4, 0x1a3c, 0xb2e3, 0x435e, 0xb246, 0x423d, 0x43de, 0xbfcf, 0x42f2, 0x424c, 0x328b, 0xba0e, 0x1f34, 0x462b, 0x456b, 0xbadb, 0xb277, 0xbfb4, 0x40d1, 0xbafa, 0x4248, 0x127c, 0x324a, 0x1b77, 0x40c6, 0xba06, 0x42f0, 0x4043, 0x0313, 0x4542, 0x4084, 0x368e, 0xbf24, 0x4294, 0x1c3c, 0x408d, 0xb2b2, 0x314f, 0x1c72, 0x4267, 0x430a, 0xa020, 0xb0fa, 0x1aa7, 0xb229, 0x41af, 0x404a, 0x12c8, 0x4302, 0xb2b8, 0x406d, 0xbac6, 0x1a94, 0x4306, 0xb284, 0x41a1, 0xbf77, 0xb206, 0x41af, 0x3aea, 0x438f, 0xadb4, 0x1cc3, 0x11cd, 0xbac1, 0x1c4a, 0xa4b3, 0x415b, 0x3cba, 0x43e8, 0x41c0, 0xb29a, 0xb2a1, 0xa433, 0xbf73, 0x4553, 0x4198, 0x4243, 0x21e5, 0xb2a4, 0xbf90, 0x4319, 0x40e5, 0x433b, 0x423d, 0x42a7, 0x4149, 0x4346, 0x1abf, 0xba72, 0xb086, 0x40bf, 0x2f64, 0x43e8, 0x064e, 0xb2bd, 0x4617, 0xbfb5, 0xb2cf, 0x3673, 0x4608, 0xba4b, 0x1f3b, 0x1bcf, 0xbfb0, 0x42f8, 0x42d4, 0x4389, 0x40f2, 0x0335, 0x4337, 0x425b, 0xb26c, 0x422e, 0xb241, 0x42a4, 0x42c0, 0xbf01, 0x362f, 0x38e0, 0x42ee, 0x1944, 0x4312, 0xb2db, 0x12e0, 0x46f0, 0xba2e, 0xb0c2, 0x42c5, 0x1838, 0xbff0, 0x1d87, 0x462f, 0x428f, 0x411a, 0x1dc6, 0xa2d1, 0x45ea, 0x4261, 0x17a2, 0x4012, 0x400d, 0x1ae6, 0x4284, 0x1abf, 0xbf36, 0x40f0, 0x4389, 0x0c78, 0x1d1d, 0xb203, 0x407f, 0x14b5, 0xb2b3, 0x0fce, 0xba7c, 0xb2c9, 0xb25e, 0x2199, 0x419a, 0x4188, 0x430b, 0xb0ef, 0xba69, 0x4171, 0xb212, 0x41f1, 0xba4c, 0xbf8c, 0x45c4, 0xb211, 0x4266, 0x436a, 0x43de, 0xb0b4, 0x41e7, 0xb0d5, 0x424a, 0xba7f, 0xb212, 0x40c1, 0x4323, 0x4241, 0x42f0, 0x42d5, 0x4269, 0x433e, 0x1b80, 0x3715, 0xaafc, 0xba72, 0x4611, 0x1d89, 0xbfaa, 0xb0f9, 0x3a65, 0xb220, 0xb256, 0x4498, 0x40b6, 0x43af, 0x4334, 0x3daf, 0x43ee, 0xb2b8, 0x40fb, 0x3140, 0x4092, 0x412a, 0x4242, 0xaa96, 0x43b0, 0xb0b3, 0x1ccc, 0xbfe8, 0x4139, 0x4377, 0x41ad, 0x187d, 0xbfa4, 0xba15, 0x4166, 0x41a4, 0x3fb1, 0x4005, 0xb2c7, 0x413d, 0x3917, 0xbfe0, 0x41a5, 0xb211, 0x433d, 0x05fd, 0x4409, 0x4475, 0x41cc, 0x1e64, 0xbf89, 0xb28f, 0x1a04, 0xbf90, 0x42b3, 0xb2cc, 0x41a2, 0xa53c, 0xbfc0, 0x19b0, 0x4337, 0xbf48, 0xb282, 0x1f0e, 0x4370, 0x4358, 0xb21a, 0xbad7, 0x4043, 0x431f, 0x4551, 0x4685, 0x19cf, 0x4053, 0x408a, 0xb020, 0x43fd, 0x2dc1, 0x4603, 0xbf0f, 0x0b17, 0x415b, 0xac11, 0x407c, 0x4362, 0xba64, 0x4216, 0x1384, 0x09e0, 0xbad7, 0xba17, 0x426d, 0x438e, 0x33d3, 0x406d, 0x3b44, 0xb210, 0x2ec5, 0x40ab, 0x42e8, 0x195e, 0x1577, 0x425c, 0x4159, 0x42d0, 0x14ae, 0x1cb1, 0x4081, 0x1a6a, 0xbf57, 0xb2f2, 0x4301, 0x2184, 0x41b0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x67423155, 0xe3599f1a, 0xf8825cc1, 0xb4b9fc16, 0xcf447746, 0x4ebf74b8, 0x62e4bf2f, 0x5a51ed64, 0xce8f8c9f, 0x5b8b5b0d, 0xed67892d, 0xb797f36a, 0xb91b1a79, 0x89af40a1, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000002, 0xfffffffe, 0x43e44438, 0xbc1bbbc8, 0x00000000, 0x00000000, 0x0000021f, 0xff1bffff, 0x5b8b5b0d, 0xfffffff5, 0x5738068c, 0x89af3e59, 0x21f22254, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x351e, 0xb20f, 0x43d6, 0xba50, 0x40d6, 0x40c0, 0xb0f4, 0x41e8, 0xb0e2, 0x4618, 0xb07e, 0x4103, 0xba3c, 0xbafb, 0x0552, 0x41d7, 0xbfab, 0x41e3, 0x40ae, 0xa6e2, 0x2bec, 0xb23a, 0x007d, 0x4264, 0xb07d, 0x432c, 0xbfe8, 0x4279, 0x44d1, 0x225c, 0x21f0, 0x0f23, 0x426d, 0x0b9f, 0x429c, 0x4275, 0xba66, 0x0a83, 0x4227, 0x31d8, 0x1ca1, 0xbfc5, 0x1fc2, 0xbadf, 0x4359, 0x4171, 0x4122, 0x0782, 0xbf51, 0xb06b, 0x4121, 0x408c, 0x3364, 0xb00d, 0xba44, 0x1aaa, 0x43b5, 0x4640, 0xbaf9, 0xaf1b, 0xb2eb, 0xba59, 0x423a, 0x0a7a, 0xb0fa, 0xb2cf, 0x45c8, 0x42e2, 0x2a0c, 0x1c8f, 0xbac4, 0xbf08, 0xb22e, 0x425b, 0xb08a, 0x43f2, 0xbfb7, 0x42fa, 0x4212, 0x1a85, 0x4292, 0xbacc, 0x34d2, 0xb260, 0x1fb1, 0xa08c, 0xbae8, 0x31bf, 0x40fb, 0xa24b, 0xb270, 0x4042, 0x43a4, 0x44b4, 0xb0a3, 0x26c1, 0xbf34, 0x43dc, 0x151d, 0x1be3, 0x40ec, 0xb224, 0xb280, 0x29f4, 0x1afe, 0x4248, 0xbf1a, 0x4282, 0x3389, 0xb25a, 0x1c1f, 0xbf48, 0x3fe4, 0xb25f, 0xa162, 0x2921, 0xbafa, 0x401a, 0x410b, 0xbf0b, 0x42e9, 0xb28a, 0xb270, 0x1a56, 0x2b14, 0x1d39, 0x431f, 0x1f4e, 0xb28e, 0x4177, 0x1a5f, 0xa81b, 0xa73a, 0x4392, 0x416c, 0x420b, 0x4119, 0x40ff, 0xbf65, 0x4039, 0x41cb, 0xb098, 0x41f1, 0x4126, 0xba70, 0xa037, 0x0686, 0x31f0, 0xba02, 0x2c1b, 0xa41f, 0xbf9f, 0x4037, 0x417c, 0x4371, 0x1c48, 0x39f5, 0xb0a2, 0x4271, 0xb201, 0x3107, 0xbf1b, 0x42a8, 0x42c0, 0x465b, 0xb20c, 0x2b6d, 0xbf6c, 0xb2d6, 0xb274, 0x1344, 0x4605, 0xb2a4, 0x359b, 0xba1d, 0xb2fb, 0xbf1b, 0x1dd4, 0x1d7e, 0x4679, 0x4591, 0xba75, 0xbf60, 0x42ce, 0xba57, 0x160d, 0x415e, 0x085d, 0x18d1, 0x1f0b, 0xbfd6, 0x429a, 0x411f, 0x4079, 0x4682, 0xbfbc, 0x44a4, 0x416f, 0x4029, 0x1975, 0x294b, 0xbad1, 0x43f0, 0x2017, 0x4083, 0x416e, 0x41f7, 0xbfd0, 0x45e0, 0x3c14, 0x4647, 0x44f1, 0x4681, 0xb034, 0x4433, 0x4192, 0x4281, 0xb08e, 0xba31, 0xbfb7, 0x427f, 0x4630, 0xbad4, 0x4379, 0x40c7, 0x4318, 0x3267, 0xb2e5, 0x4280, 0x42ee, 0x059f, 0x4054, 0x4585, 0x456d, 0xa855, 0x42f3, 0x0953, 0xba44, 0xb040, 0xb2af, 0x426d, 0x4635, 0x41a1, 0x405a, 0x1d67, 0x1941, 0x43fe, 0x425a, 0xbfba, 0x32e7, 0x1621, 0x41ef, 0x3fae, 0x1be4, 0x036f, 0x40d0, 0x430c, 0xb02e, 0xbace, 0x4060, 0x4299, 0xba14, 0x4557, 0xb22d, 0xbf4f, 0x4188, 0xba3a, 0xb2a1, 0x419f, 0x408e, 0x0dbf, 0xb2de, 0xaa6f, 0x401e, 0x4315, 0xb2f2, 0xbad9, 0xb0c6, 0x0710, 0xb025, 0x431e, 0x42b7, 0x1f99, 0xbf5d, 0x4273, 0x45f4, 0xb2ba, 0x410a, 0x19da, 0xb21e, 0xbafe, 0x32a4, 0xb24e, 0xbafc, 0xa511, 0x4175, 0x4209, 0x15ec, 0x4203, 0x4016, 0x4451, 0xbafd, 0x25f3, 0x409d, 0x1c68, 0xbfd7, 0x43b2, 0x41c1, 0x432d, 0x428c, 0xba10, 0x403a, 0xa089, 0x245f, 0xbf59, 0xb2a4, 0x151d, 0x38b5, 0xa654, 0x44f9, 0x44a3, 0xbfca, 0xb216, 0x1314, 0xba41, 0x289e, 0x0797, 0x40df, 0x409a, 0x430f, 0x40a5, 0x1e10, 0x40d4, 0x41a7, 0x11f6, 0x4384, 0xbf90, 0x42f6, 0x42c5, 0x403c, 0x43a9, 0xa104, 0x1d69, 0x417b, 0xa553, 0xbfe1, 0xb2e2, 0x24d6, 0x4095, 0x1fc7, 0xb243, 0x19ca, 0x419d, 0x40ca, 0x4039, 0x4145, 0xbfa0, 0xbfbb, 0xb233, 0xbf00, 0x40cf, 0x0ac0, 0xa120, 0x1f27, 0x094c, 0x4121, 0xbad4, 0x18a6, 0xbf05, 0x4333, 0xaca2, 0x19b2, 0x0f87, 0xbadf, 0x1653, 0xb251, 0x0c0e, 0xb027, 0x423d, 0x44f3, 0x43cf, 0xb06d, 0xaa68, 0x43d7, 0xaf3d, 0xaac6, 0x30bc, 0xae02, 0x0af9, 0x0a91, 0x40ba, 0x42eb, 0xb271, 0x42a8, 0xbfb8, 0x46d5, 0xa671, 0xb0ea, 0x43d2, 0x1a79, 0x35a2, 0xba41, 0x1a5a, 0x02ce, 0x4339, 0x4053, 0xbfd9, 0xa3d6, 0x2e6e, 0x416f, 0x43f7, 0xbafd, 0x427c, 0xbf76, 0xa945, 0x20fd, 0xba28, 0xb0b4, 0x4062, 0x21e7, 0xb09c, 0x4078, 0xa702, 0x41ce, 0x417a, 0xb219, 0x4196, 0xbf3d, 0xbaf7, 0x192c, 0xa208, 0x425e, 0xb2c4, 0x400a, 0x40f1, 0x4304, 0xb0e2, 0x4031, 0xb211, 0x42c3, 0x0eca, 0x46f1, 0x4072, 0x1c40, 0x40f6, 0x1b35, 0xb2f8, 0x43e3, 0x28f6, 0x43a3, 0x419a, 0x4337, 0x4311, 0x40ff, 0xbfb9, 0x312e, 0x4189, 0xa2f4, 0x45e9, 0x42f6, 0x1db4, 0x0c19, 0x44f1, 0xba6c, 0x425f, 0x4108, 0x41ba, 0x0903, 0xba53, 0x16c7, 0x4112, 0x044b, 0xb02f, 0xb21d, 0x389c, 0x420b, 0x424e, 0x309a, 0xbf87, 0x4161, 0x1fa5, 0x40cf, 0x4060, 0xbfdd, 0x4183, 0x1107, 0x3376, 0x433b, 0xa8eb, 0x352d, 0x1918, 0x0664, 0x4225, 0x401b, 0x42e0, 0xb208, 0x46a8, 0xbf38, 0xb277, 0x0fd4, 0x434e, 0x16bf, 0x43ea, 0xb2f3, 0x4124, 0x198f, 0xba5b, 0x43be, 0x1e44, 0xb2ae, 0xa745, 0xbfde, 0x405d, 0x4151, 0x41ce, 0x1c07, 0x41a7, 0x4166, 0x40fc, 0x38fe, 0x41d9, 0x4084, 0x4128, 0x1817, 0x433a, 0xbf18, 0xb269, 0x420f, 0x442e, 0xb020, 0x42e4, 0x182e, 0x2237, 0xb067, 0x4225, 0x41b4, 0x40cc, 0xbf88, 0x45d2, 0xba2b, 0x2749, 0xadb9, 0x1b34, 0x40e8, 0x0ff1, 0xb20a, 0xba70, 0x3396, 0xb261, 0x4296, 0x4387, 0x4002, 0x436b, 0x435f, 0x434c, 0xbaf7, 0x4077, 0xbfe1, 0x1f0d, 0xba30, 0xba16, 0x4085, 0xb2ad, 0x4029, 0xba54, 0x1c2f, 0xb27b, 0x0dea, 0x4233, 0xb202, 0x0952, 0xba7e, 0x31f6, 0x43fc, 0x4117, 0x45c9, 0xae39, 0xbf3f, 0x4226, 0x43b7, 0x4221, 0x44f2, 0x4157, 0x1efc, 0xbf32, 0x40ab, 0xbfc0, 0x2669, 0x4388, 0xb0a9, 0x406c, 0x28ca, 0x15e3, 0x4337, 0xba26, 0xad3c, 0x106b, 0x340e, 0xbff0, 0xba04, 0x412d, 0x415d, 0x410e, 0xbfbd, 0x427f, 0xb232, 0xac33, 0x4042, 0x40cc, 0xa690, 0x42ce, 0x2cf1, 0xbaf3, 0x40ae, 0xba6b, 0xba26, 0x0b5f, 0xb2e8, 0x4105, 0xbaf2, 0x4348, 0x4240, 0x1f5b, 0x408f, 0xbf38, 0xb282, 0x1c4d, 0x45ab, 0x43c5, 0xb253, 0xb26d, 0x1bfe, 0xbfb5, 0xb018, 0x366a, 0x4065, 0x122e, 0x408b, 0x313d, 0xba7e, 0x4002, 0xbacc, 0x4040, 0x40c3, 0x42dd, 0xb276, 0x0d1e, 0x43ab, 0xba2d, 0x190e, 0x4082, 0xb29e, 0xba38, 0x2855, 0xb074, 0x1ca5, 0x4337, 0x1d60, 0x1cfd, 0xbad9, 0x046c, 0xbfc1, 0xbfd0, 0x42d1, 0xb211, 0x1c31, 0x4694, 0xafe8, 0x43c6, 0x4303, 0x468d, 0xb214, 0x00fa, 0xb03c, 0x4280, 0x43c6, 0x4637, 0xbfde, 0x07a2, 0x4044, 0xba6b, 0xb096, 0xa983, 0x4592, 0x413a, 0x0518, 0x46e2, 0xa242, 0x25c1, 0x4103, 0xba62, 0xb001, 0x455f, 0xbf47, 0x11f2, 0xacab, 0x3992, 0xbad6, 0x439e, 0x4388, 0xb2b8, 0xba17, 0x432a, 0xb221, 0xba03, 0x1730, 0x43cf, 0x4265, 0xba50, 0x1b03, 0x428f, 0xbf2e, 0xb20c, 0x458c, 0x4390, 0x1afe, 0x42b9, 0x1d65, 0x4463, 0x42b5, 0x0dff, 0x4095, 0x42f6, 0x3496, 0x4441, 0x0c8a, 0xba61, 0x4325, 0x20f4, 0xb09c, 0x1c27, 0x420f, 0x40e0, 0xbf70, 0x21c7, 0x4240, 0x459b, 0x40a0, 0xb2ae, 0xbfd2, 0xac93, 0xb0b0, 0x31e9, 0x4131, 0x185d, 0x417c, 0xba60, 0xb24b, 0x2874, 0x40ef, 0xb206, 0xb2e9, 0x44fc, 0xb074, 0xb03b, 0x43a8, 0x426a, 0xba38, 0x43ce, 0x443f, 0xbf4b, 0x4141, 0x1f3c, 0xb2b6, 0x430b, 0xbfcf, 0x4292, 0xba52, 0x4172, 0xa9f9, 0x3050, 0x44e0, 0x43b0, 0x43e8, 0x40b3, 0x4257, 0xba6f, 0x4006, 0x4036, 0x4320, 0x46f5, 0x41f6, 0xa8e5, 0xba6a, 0x429d, 0x0a20, 0x19d7, 0x467f, 0xb20c, 0x400d, 0x4007, 0xbfaa, 0x4367, 0x21b7, 0x4142, 0x428b, 0x4369, 0x45a0, 0x2abb, 0x1af5, 0x4216, 0x1aee, 0xb02b, 0x121e, 0xb241, 0xbaee, 0x2d8b, 0x40d2, 0xbf69, 0x4117, 0x4366, 0x2bbd, 0xba10, 0xb09f, 0x4172, 0x426c, 0x23fc, 0x338e, 0xbf43, 0x4015, 0x42ee, 0xa3f1, 0x0369, 0x42e7, 0x062e, 0x1667, 0xb23b, 0x4433, 0x438e, 0xb0c9, 0x418e, 0xb0d8, 0xba4c, 0xbf13, 0x42e0, 0x1c20, 0x19f8, 0xb232, 0x4083, 0x410f, 0xad20, 0x412c, 0x3662, 0x2a58, 0x4089, 0xb275, 0x117c, 0x3b0c, 0xbf5d, 0x41c1, 0x4011, 0x4063, 0x2178, 0xab2f, 0x42a1, 0x439e, 0x2b3e, 0x45e2, 0xb084, 0xadfb, 0xa056, 0xbaf7, 0x3e15, 0x4464, 0xb046, 0xb24d, 0xb09d, 0x4202, 0x19f1, 0x4211, 0xbf5e, 0x4035, 0x400f, 0x1e45, 0x13ab, 0x40aa, 0x1ad0, 0x429f, 0xb0a4, 0xb05b, 0x0f39, 0xaa55, 0x2864, 0x437f, 0x43ca, 0xb2d1, 0x3846, 0x46e0, 0xb0d4, 0x4411, 0x1ce1, 0xb06d, 0x1246, 0x187d, 0x08ca, 0x46da, 0xbf95, 0xb273, 0xba11, 0x4211, 0x40d3, 0xb040, 0x05fd, 0x1e61, 0xa78d, 0x46f3, 0xb0e7, 0x4168, 0xba4c, 0xba0b, 0x000f, 0x41a0, 0x340c, 0x29e0, 0x2c2e, 0x42ab, 0xb01b, 0x421e, 0x2607, 0x1b58, 0xba0f, 0x442a, 0x4219, 0xb2ac, 0xbfd4, 0x414a, 0x0524, 0x3d35, 0xb0a0, 0x4343, 0x40e8, 0x40a9, 0x18f6, 0x14df, 0x41d4, 0x4542, 0x434a, 0x4306, 0x36f2, 0x1d38, 0x4595, 0x41a2, 0x42fd, 0xbf24, 0x4596, 0xa565, 0x4650, 0xb264, 0x4139, 0xb039, 0x4261, 0x41ae, 0xbaf5, 0x4112, 0x43c3, 0x0409, 0x4168, 0x40e4, 0xbfa8, 0xba31, 0xa175, 0x41ac, 0x2089, 0x423e, 0x40fe, 0x411e, 0x2535, 0x42e2, 0xa13b, 0x44c0, 0xb20f, 0xbf6e, 0x43be, 0xb2a5, 0x40bb, 0x4068, 0x19bb, 0x459d, 0x2ab5, 0x192c, 0x427e, 0x429e, 0x4286, 0x43ee, 0x415e, 0xb0cd, 0x46c3, 0x45a3, 0x4345, 0x4170, 0x064c, 0xb2bd, 0xbf19, 0xb091, 0x02aa, 0x3a96, 0xb2a8, 0x41a0, 0xb0c3, 0x4327, 0x18c6, 0x014d, 0x402a, 0xb299, 0xb207, 0x0b66, 0x3ced, 0xb2bb, 0x4188, 0x3513, 0xbaff, 0x4356, 0x437c, 0x42f2, 0xba74, 0xb239, 0xba67, 0x42b4, 0xbf3a, 0xb2d8, 0x1c3f, 0x2787, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3be9d470, 0x621bd961, 0x561548a7, 0x32c98a3d, 0x487719ff, 0x1c15d826, 0x34d9255e, 0xaae8da54, 0xf65e2145, 0x5153589f, 0x032f5367, 0xf13554cd, 0xda5c80c1, 0x8ef09f26, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0xffffffff, 0xffff8018, 0x00031000, 0x00001880, 0x00000000, 0x00031013, 0x00000000, 0x00000087, 0x00012ad4, 0x00000000, 0xf135552c, 0x00012ad4, 0x0000956a, 0xfffffd74, 0x00000000, 0x600001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb2f7, 0x4028, 0x42dd, 0x4043, 0x422e, 0xbf5c, 0xa6cf, 0xb289, 0x06ce, 0xb229, 0x4065, 0x430d, 0x1986, 0x466b, 0x2c11, 0x0fcb, 0xba22, 0x412a, 0x40f3, 0xbf9d, 0x0cd5, 0x415a, 0x310f, 0xb07e, 0xb2da, 0xbf12, 0x3f1e, 0x3e0e, 0xb22c, 0x4414, 0xb0a3, 0x1a93, 0xbf15, 0x33ee, 0x1c22, 0x4184, 0x416b, 0x43cd, 0x4625, 0xba70, 0x438f, 0x4175, 0x4031, 0xbf53, 0x0d6a, 0x4234, 0x2c94, 0x4288, 0x4196, 0xb0ef, 0x3a5b, 0xb02b, 0xa285, 0x438c, 0x1914, 0xbfb0, 0xbf82, 0x4636, 0x4550, 0x23ad, 0x466e, 0xb04e, 0x4303, 0x4301, 0x1efc, 0xb267, 0xba31, 0x4247, 0xb01d, 0xbfa3, 0x4026, 0x11c3, 0x41fd, 0x40cc, 0x4150, 0x435d, 0xbf3f, 0xa514, 0xbfc0, 0x4293, 0x1e27, 0x42af, 0x405f, 0x3ad4, 0x40a2, 0x0423, 0x406a, 0x4236, 0xb2a5, 0x1ec3, 0x1f33, 0x29aa, 0x2865, 0x0b77, 0x2536, 0x1e74, 0x40a6, 0x43cb, 0x42e1, 0x402c, 0x4414, 0x25f3, 0x0c28, 0xbf7c, 0x0042, 0x4154, 0xbad1, 0x2d5b, 0x45c1, 0x4399, 0x3a0d, 0x41ea, 0x4398, 0xbfa0, 0xba3a, 0x41b8, 0x1f6c, 0xbf75, 0x1579, 0xb29c, 0x1d42, 0x18ce, 0x16f3, 0x44a3, 0x46b1, 0xbadc, 0x1943, 0xb2d6, 0x24b9, 0x4061, 0x2103, 0x1e64, 0x41c5, 0x41f9, 0xbf59, 0xba55, 0x40c0, 0x414f, 0xa5c1, 0x4189, 0x1f1d, 0x1afd, 0x41b6, 0xba58, 0xba3b, 0xb0b7, 0x2acf, 0x4370, 0x41e3, 0xb2d2, 0xb265, 0xb28b, 0x4133, 0xa98a, 0x403b, 0x3a58, 0x4271, 0x447e, 0x41fa, 0x40c7, 0xba58, 0x4319, 0x41e5, 0xb030, 0xbf49, 0x4074, 0xa3ca, 0xbfb0, 0x1928, 0x41a9, 0xba74, 0x4070, 0xb00f, 0x4225, 0xa687, 0x00f0, 0x406c, 0x4148, 0xbf83, 0x19c0, 0x4197, 0xb2da, 0x424c, 0xb2bc, 0xba23, 0x2e66, 0x4089, 0x28dd, 0xb2e4, 0x05cd, 0x4184, 0x05c6, 0x404d, 0xba2e, 0x0f8a, 0x4175, 0x412a, 0x44cc, 0xb0ef, 0xb042, 0xafe8, 0x420a, 0x4253, 0xbf83, 0x1b27, 0x4062, 0x424b, 0xae9c, 0xba7e, 0xb265, 0xb28a, 0x148f, 0xb2d3, 0x2e6d, 0x2efc, 0xba46, 0x40f7, 0x41fd, 0x411b, 0x4566, 0xb2b1, 0x4209, 0xb218, 0x0aeb, 0xb20d, 0x40c9, 0x226c, 0x429a, 0x22d3, 0xbfb0, 0xb286, 0x4275, 0x42a6, 0xbf96, 0x4235, 0x4222, 0x42e3, 0xb237, 0xb220, 0x4624, 0x1d89, 0xb031, 0x389e, 0x4196, 0xb0b4, 0x426e, 0x1ae5, 0x140f, 0x4398, 0x4667, 0x40e5, 0x0fe5, 0x14fe, 0x18e6, 0x44b2, 0x4285, 0x4044, 0x4233, 0x2a71, 0xb0c8, 0xb06f, 0xa688, 0xbfcb, 0x435e, 0x1af2, 0x1de1, 0x0607, 0xabf8, 0xbfc0, 0x42ef, 0x4220, 0xbf70, 0xb271, 0x3965, 0x4132, 0xab6b, 0x1665, 0xb2c5, 0x1fa6, 0x32f2, 0xb0aa, 0x41ca, 0x2064, 0x0cbe, 0x41a5, 0xbf60, 0x1fa4, 0xb02d, 0x4046, 0xa9a9, 0x41b7, 0xbf43, 0x4167, 0x0b2e, 0xa33a, 0x4202, 0xa8c2, 0x42c0, 0x425b, 0x3f6c, 0xbada, 0x1b7a, 0x40ae, 0x18c0, 0x4132, 0x407a, 0x40cb, 0xba02, 0xad3f, 0x4275, 0x4271, 0x1f2e, 0x1dec, 0xbfde, 0x4544, 0x0f82, 0x214e, 0xb284, 0x4089, 0x4334, 0x4251, 0x2dd9, 0x08e2, 0x431d, 0x4036, 0x09d7, 0x432b, 0x434c, 0xb203, 0xbf70, 0x3ce7, 0x3620, 0xbfa0, 0x155a, 0xbfb4, 0x198a, 0xb28e, 0xbf71, 0x047b, 0x1e2d, 0x42c4, 0xaa34, 0xba55, 0x4073, 0x2661, 0x4106, 0x3527, 0x1a73, 0x333f, 0x430d, 0xb26b, 0x3a6a, 0xb2f2, 0xbade, 0x42c5, 0x1d3a, 0xbf2f, 0xa17c, 0x4156, 0x4267, 0xb207, 0x4581, 0xb0f1, 0x4385, 0x1869, 0x4045, 0xb258, 0x0b70, 0x1f7d, 0xad43, 0x1f8d, 0x4235, 0xb24e, 0xb04c, 0xb0e0, 0x0d27, 0x3468, 0xbf9d, 0x189e, 0x1ccd, 0x43bc, 0x4060, 0xb2d5, 0x45bd, 0x1b74, 0x44dc, 0xbaf8, 0xab61, 0xb2d2, 0xb21c, 0x1e49, 0xafa9, 0x432a, 0x4374, 0x1b4d, 0xb24a, 0x4092, 0xb29b, 0xbf62, 0xb2d2, 0x36fc, 0x4131, 0x1d9b, 0x45f1, 0x0d6c, 0x398b, 0xb2f7, 0x424a, 0x4044, 0x03c7, 0xba2e, 0x1adb, 0x43eb, 0x40e2, 0x06a9, 0x07d8, 0x419c, 0xb291, 0xba06, 0xbf3e, 0xb28b, 0x268a, 0x41b9, 0x1119, 0x432f, 0xb29f, 0xab98, 0x4691, 0xb28e, 0x1c91, 0x128e, 0x4432, 0x435b, 0x1ae9, 0x418b, 0x05fb, 0x426f, 0xbfb2, 0x4306, 0x3ff6, 0x42f0, 0x40a1, 0x0abe, 0xba16, 0x0068, 0x4095, 0x09b3, 0x36a2, 0x4362, 0x4226, 0x407a, 0x2f81, 0xbfe2, 0x3c7d, 0x425b, 0x43f0, 0x218b, 0x3826, 0x00fc, 0x19cf, 0xb2a4, 0xba31, 0x4390, 0x43f4, 0x1d18, 0xb28e, 0xbaeb, 0x41a1, 0xbf80, 0x41e0, 0x1c8a, 0xba1e, 0x4212, 0x435d, 0x2bbc, 0xbf90, 0x432c, 0xbfae, 0x4211, 0xba70, 0x43f9, 0x42d5, 0x1ffa, 0x413d, 0xba40, 0xbfb2, 0x232a, 0x429c, 0x4490, 0x02b5, 0x4059, 0x404c, 0xba41, 0xa995, 0x415b, 0x40f7, 0x4041, 0x43e6, 0xba3b, 0xb2d7, 0x425d, 0x197f, 0x411d, 0xbf68, 0xba27, 0x1922, 0x1f2b, 0xbafe, 0x4454, 0x4310, 0x434c, 0xad88, 0x414d, 0xa4a5, 0xba15, 0xbfbc, 0x435d, 0x41ff, 0x41bc, 0x43fd, 0x1b7d, 0x3bc0, 0xb237, 0x1cf3, 0xba44, 0x4019, 0x439a, 0x4151, 0x4265, 0x406d, 0x4247, 0x2b08, 0x410e, 0x2a27, 0x42c5, 0xb2d0, 0xbf8c, 0x40d2, 0x42b5, 0xb075, 0x4324, 0x40d6, 0x431f, 0x40c8, 0x11fe, 0xa8ea, 0xbf5e, 0x4055, 0x1462, 0x091d, 0x194f, 0x0447, 0x045d, 0x4655, 0x37cb, 0xbf78, 0xba5e, 0x419f, 0x3835, 0x1c9d, 0x404a, 0x427e, 0x43c9, 0xbf60, 0x4276, 0x3259, 0x439b, 0x08a9, 0xb071, 0x4117, 0x44d3, 0xbad4, 0x42f7, 0xba45, 0x4469, 0x18e6, 0x42cd, 0x434f, 0x3f51, 0x433e, 0x45cb, 0xbf6d, 0xb268, 0x43ff, 0xbf90, 0x42c4, 0x40cc, 0x3fd6, 0x3d3e, 0x0b24, 0x40e1, 0xab12, 0xb2bf, 0xb0fc, 0xb05f, 0x43b7, 0x1dc3, 0xbf60, 0x44d8, 0xbf60, 0x43a2, 0xb2af, 0x4137, 0x41d8, 0x403e, 0x42ee, 0x32c0, 0x42a1, 0xba3b, 0x39e8, 0xbf01, 0x1a35, 0x43f6, 0x1e36, 0x4382, 0x40e2, 0xba04, 0x06a1, 0x1cc8, 0xa943, 0x4171, 0x415e, 0x4006, 0xba30, 0x411b, 0xbfc5, 0xac02, 0xb29c, 0xba4c, 0xb232, 0xba60, 0x4198, 0x42fc, 0x1a5e, 0x4269, 0xad0e, 0x0366, 0xa38a, 0x4045, 0x0a7b, 0x3899, 0x1bcc, 0xba2c, 0xb2b3, 0x45c5, 0x2575, 0x44ca, 0x4185, 0x06b8, 0x4185, 0x4018, 0xbfcd, 0x403f, 0x391d, 0xa99c, 0x17d1, 0xb280, 0xba7d, 0xba66, 0x1e75, 0x3ad1, 0x1304, 0xbfcb, 0x1e01, 0xbad0, 0x43cf, 0x4425, 0xb230, 0x43d8, 0xbae1, 0xbad7, 0x13f0, 0x42a0, 0x438c, 0x28f8, 0xb0a4, 0xb0a6, 0x196a, 0x1cc0, 0xbfc5, 0x0c99, 0x40e4, 0x1935, 0x45b4, 0xb011, 0x40ec, 0x40e9, 0x3f82, 0x42c8, 0x1aa3, 0x43be, 0x4034, 0xbf1d, 0xa56d, 0x44da, 0x19e3, 0xb2b1, 0xb2bb, 0x40e0, 0x40ef, 0x3c5d, 0x43f5, 0xb26e, 0xbfd5, 0x402f, 0x4023, 0xba3e, 0x38d4, 0xb255, 0x406d, 0x4244, 0xb06b, 0x42e1, 0xb045, 0x408a, 0x4382, 0x4102, 0x17cb, 0x4670, 0xbf21, 0xb28e, 0x4282, 0xb27c, 0x1f26, 0xb091, 0x42c9, 0x1e3a, 0xbad0, 0xbfe2, 0x1b74, 0x42a6, 0xba44, 0x42b2, 0x4291, 0xb21e, 0x404d, 0x4328, 0x4343, 0xad78, 0x42b3, 0x4169, 0x3557, 0x1b59, 0x4372, 0x403d, 0x41f5, 0xba79, 0x0e81, 0xa713, 0x2378, 0xba21, 0x4050, 0x40d7, 0xb050, 0xa3af, 0x417d, 0x4001, 0xbf95, 0xa8cc, 0x437c, 0x436e, 0x408e, 0x43aa, 0x3957, 0xb021, 0x4209, 0xb272, 0xba1e, 0x42a2, 0x4351, 0x467a, 0x13cc, 0x2db1, 0x466e, 0x4234, 0x43c0, 0xb0dc, 0x1fcc, 0xbf5e, 0x41e2, 0x17ab, 0x4173, 0x4110, 0x427b, 0x41b2, 0xa5af, 0x411e, 0xbaf2, 0xba49, 0x4197, 0x4448, 0xb068, 0x4283, 0x4354, 0xbfa0, 0x40f2, 0x1f52, 0x41d3, 0x4571, 0x1aab, 0xb2b5, 0xaa74, 0x4372, 0xbf26, 0xb2cd, 0x42e7, 0x18a8, 0x373f, 0xba7c, 0x40a7, 0x40ec, 0x1deb, 0x4380, 0xb072, 0x43b6, 0xab0a, 0xbf54, 0x42d3, 0xa8e9, 0x1d8b, 0x1f92, 0x4046, 0x1ae7, 0xafbf, 0x20de, 0x45cc, 0x189d, 0x42f4, 0x43e8, 0xb093, 0x292d, 0x412b, 0x430a, 0xbae8, 0x0ffa, 0x4080, 0x3294, 0x4665, 0x4284, 0x22c5, 0x25bf, 0xb27b, 0xbf3c, 0x3912, 0x2d61, 0xbadb, 0x44a3, 0xbf42, 0xb2a4, 0x411f, 0x1d60, 0x40f5, 0x21a9, 0x0250, 0x0a3c, 0x31e6, 0xbf77, 0x43ea, 0x1ff8, 0xbac4, 0xbac2, 0xb209, 0xbf1b, 0x43b9, 0x0791, 0x3579, 0x4447, 0xba5f, 0xbf92, 0x3c03, 0x4383, 0x4044, 0xa95a, 0xba71, 0xb229, 0x4303, 0x316a, 0x4389, 0x1ff4, 0x422e, 0xb2b3, 0x40e3, 0x4085, 0x3342, 0x42a2, 0x1d76, 0xb200, 0xae67, 0x45b2, 0xbf1d, 0xbadc, 0x27d9, 0x18e9, 0x41dd, 0x294b, 0xbf41, 0x0d48, 0x4137, 0x4015, 0x43f4, 0x317c, 0x42bf, 0x4391, 0x1774, 0x0d7f, 0x40b4, 0xba0f, 0x4111, 0xba30, 0x30fe, 0xb2b2, 0x438e, 0x4012, 0x460d, 0xbfc0, 0x2de0, 0xb2aa, 0x00da, 0x42bd, 0x4385, 0xbfa8, 0x1f7e, 0xb203, 0xb293, 0x2c9a, 0x1ea5, 0x2d34, 0x46aa, 0xb288, 0xbf7d, 0xba02, 0x2b0a, 0xb0a2, 0x43ae, 0x435c, 0x406e, 0xb09f, 0x1484, 0x416b, 0x46b3, 0x4314, 0x422f, 0xa751, 0x44e8, 0x43ae, 0xa901, 0xba66, 0x41ca, 0xbf0b, 0x43af, 0x1faf, 0x1fbf, 0xac1d, 0xac02, 0x235c, 0xbfc1, 0x4145, 0x1b22, 0x1c5c, 0x404a, 0xa600, 0x43b0, 0xbad1, 0xbf4b, 0x1b24, 0xa8e3, 0xba03, 0xaf8e, 0x295f, 0x1b40, 0x4178, 0xb205, 0x146f, 0x09ca, 0x2e3c, 0x43f7, 0x439b, 0x3295, 0x408a, 0xbf1a, 0x454f, 0x3bc1, 0xba5b, 0x4100, 0x40f2, 0x1af2, 0x1c9b, 0xa85c, 0xaf0f, 0xbfb0, 0xa8dc, 0x4218, 0x414a, 0xb205, 0xb2a2, 0x4160, 0xa22c, 0x00b4, 0x42e3, 0xba5b, 0xbf52, 0xba23, 0x40bd, 0xb01d, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe4527cc5, 0xffa5922e, 0xca434c0d, 0x2c3146e6, 0x89b4f1a8, 0xd1d30b0d, 0x90e04460, 0x4d312bcd, 0xd0bf479f, 0x23e96a47, 0x987a7688, 0x7251199f, 0x0415194f, 0xea9d51e2, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0xea9d5adb, 0x00001257, 0x0000187c, 0x105e0000, 0x00005e10, 0x00005a7e, 0x00001784, 0xea9d574a, 0xc6274cdf, 0x00000000, 0xfffffffe, 0x8aff0005, 0x766633dc, 0xea9d570e, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4166, 0xba66, 0x4108, 0x43d1, 0x2c5e, 0xb2fb, 0x4067, 0xafc6, 0x1915, 0x25ef, 0xaadd, 0xb016, 0xbac4, 0xba6e, 0x4415, 0x455e, 0xba35, 0xbf09, 0x4108, 0x0586, 0x42f9, 0x41e2, 0x15df, 0x279d, 0x18cb, 0x3d65, 0x40cd, 0xb0fe, 0x430b, 0xb006, 0x40a5, 0xb21d, 0x409f, 0xa0e2, 0xbf8f, 0x4328, 0x4444, 0xbf70, 0xb2be, 0x40b1, 0xb294, 0x4068, 0x414b, 0x13ae, 0x41e2, 0xb21d, 0x43ce, 0xba3d, 0xb26b, 0x1830, 0xb2b0, 0x440c, 0x29fa, 0x4290, 0xba40, 0x37eb, 0x43db, 0x422c, 0x4337, 0x30cc, 0x41cc, 0x4309, 0xb2f7, 0x401d, 0xbf1a, 0x4322, 0x43b8, 0x41b3, 0x33c5, 0x29e3, 0x0c91, 0x4240, 0x419f, 0xbf31, 0x4059, 0x407f, 0xa4cc, 0x179f, 0x42a2, 0xb28b, 0x413f, 0x40b7, 0xb276, 0x42f8, 0x416c, 0xba03, 0x287a, 0xbfa0, 0x3811, 0x4374, 0x44fd, 0x4147, 0x1d8b, 0x2828, 0x13cb, 0x4166, 0x45e0, 0xb206, 0x184e, 0x414d, 0xbfb9, 0xba1c, 0x40c7, 0x41fb, 0x41f5, 0x433c, 0x400e, 0x42da, 0x43a5, 0x1c04, 0x4202, 0x1c75, 0xb20a, 0x41d0, 0x405a, 0xba78, 0x3bb3, 0xbf19, 0xba6a, 0xb223, 0x35a6, 0x40a4, 0x0707, 0xbf61, 0xb2ac, 0x443e, 0x408c, 0x421c, 0x4128, 0x1975, 0xa7e9, 0x09d5, 0x1ffb, 0xb25d, 0x448a, 0x41e9, 0xaf03, 0x1ea5, 0x40bd, 0xb25f, 0x13f0, 0x4639, 0xba67, 0x404e, 0x1701, 0x423f, 0x3cf4, 0x1f10, 0x431e, 0xbfb6, 0xba7c, 0xb2b0, 0x1f0e, 0xba3e, 0x37c8, 0xba46, 0xba74, 0xba30, 0x41b1, 0x4291, 0x40ad, 0xb0ae, 0x4268, 0xb0a1, 0xba4e, 0x0d33, 0x3511, 0x03b1, 0x4383, 0xbf2e, 0x1a28, 0x43ce, 0x0d7a, 0x40e1, 0x4078, 0x13ce, 0xba1b, 0xb0fe, 0x2b59, 0x402c, 0xbfb6, 0xb288, 0xb25a, 0x41ee, 0x4343, 0x1c53, 0xb21d, 0x41af, 0xb2b4, 0x423d, 0xad88, 0x43cd, 0x4382, 0x40fd, 0x1376, 0x3a63, 0x4162, 0x4091, 0xb2fc, 0x43f8, 0x4247, 0x43b5, 0x4622, 0x41da, 0x1e63, 0x40e3, 0x435f, 0x4630, 0xbfd1, 0x19a2, 0x4237, 0x433c, 0x42eb, 0x3f17, 0xbf6a, 0xbacb, 0x4340, 0xb236, 0xbace, 0x43a4, 0xb20b, 0x1f3a, 0x393d, 0x42a8, 0x22da, 0x1b3d, 0x1884, 0x33e2, 0xa02b, 0xb087, 0x2942, 0xb038, 0xbad6, 0x4055, 0x418a, 0x4373, 0x411b, 0x4382, 0x4041, 0x41cf, 0x42fe, 0xbf79, 0x43bf, 0x1c07, 0xbac0, 0xb263, 0xba01, 0x429b, 0x0774, 0xb011, 0x4135, 0x42b9, 0xbf72, 0x4466, 0xbf60, 0x422b, 0x3aea, 0xbf1f, 0x1986, 0xb2f1, 0xa844, 0x41f7, 0xb029, 0x4319, 0x3b38, 0x4017, 0xbf34, 0x1bd1, 0xbfc0, 0x4014, 0x4304, 0xb2e2, 0x1824, 0xb28b, 0x430b, 0xa850, 0x2fdd, 0x1eb3, 0x46f2, 0x1974, 0x428c, 0x43c9, 0x42f1, 0xbf6a, 0x1d2a, 0x1f8c, 0x41ef, 0x440e, 0x4440, 0x40e9, 0x43a1, 0xaa22, 0x15b9, 0xbf25, 0xb2b8, 0x43f6, 0x40a6, 0x42f1, 0xb28a, 0xb23f, 0x4350, 0x418d, 0x42ea, 0xae84, 0xb062, 0xb297, 0x405c, 0x4132, 0x1943, 0x43da, 0xb012, 0x1d91, 0x3514, 0xbf6a, 0x4202, 0x00f2, 0x4243, 0xafb2, 0x414f, 0xbf11, 0x42aa, 0x40bd, 0x416f, 0x4196, 0xb061, 0x4119, 0x1225, 0x439e, 0x0de1, 0x2cc9, 0x1cdd, 0x3081, 0x431b, 0xb21e, 0x1e40, 0x3105, 0xbf98, 0xb0b5, 0xba65, 0xb2e7, 0x42d3, 0x0bab, 0x1fc2, 0xb0ca, 0x436f, 0xb296, 0x330d, 0x4078, 0xb25d, 0x4285, 0x3335, 0x2608, 0x34e2, 0x411d, 0x38b9, 0x1ab8, 0x023f, 0x10e9, 0x095b, 0x41d9, 0xbf60, 0xbf61, 0x4363, 0x1d56, 0x4658, 0x43f8, 0x41ac, 0x3b35, 0x4203, 0x204d, 0xbfb0, 0x4609, 0x413a, 0x4029, 0xba79, 0xba33, 0x456d, 0xb278, 0xa065, 0x40d8, 0x1c95, 0xb0ee, 0xb20f, 0xbf82, 0x404e, 0x4102, 0x4005, 0xafb3, 0x43ae, 0x1adb, 0x1ef9, 0x04e7, 0xb2b1, 0xbf90, 0x3eae, 0x44d4, 0x10e7, 0xbfaf, 0x42b4, 0x41b9, 0x4661, 0xb0ba, 0xbaf0, 0x4310, 0xba07, 0xadfc, 0x370b, 0x4078, 0xa86c, 0x43bf, 0x46a9, 0xb2c3, 0x438a, 0x0e58, 0x2d29, 0xb2c2, 0x0d85, 0x2025, 0x43be, 0xbf95, 0x43e2, 0x0190, 0xb03b, 0x43af, 0xba48, 0xbf60, 0x280a, 0x45c0, 0xb225, 0xbac3, 0x4171, 0x4286, 0x27a7, 0x4084, 0x1b18, 0x3e85, 0x1d9e, 0x222f, 0x2477, 0x1cd9, 0xba7e, 0xbfd1, 0x0c6e, 0x419b, 0xb284, 0x466c, 0x4076, 0x427f, 0x4657, 0x415f, 0x1d87, 0x0221, 0x346e, 0x4217, 0xbadb, 0x41f4, 0x41e9, 0xbac1, 0x42b2, 0x4177, 0x4118, 0x4128, 0x3828, 0x04a2, 0xbac4, 0xba30, 0xb0f2, 0xbf8a, 0xa678, 0x18bf, 0xb0ad, 0x40dd, 0x416e, 0x43d7, 0x44b9, 0xa0fd, 0x434b, 0xb097, 0x1f28, 0x4320, 0xb00e, 0x4377, 0x1187, 0xba24, 0x4160, 0xb027, 0xb20e, 0x43aa, 0x43cf, 0xbfd4, 0x39e1, 0x1d32, 0x40f2, 0x404a, 0xba4f, 0x00ca, 0xbaee, 0x426d, 0xb2f1, 0x16fd, 0x3012, 0x415e, 0x412c, 0x4221, 0xbf62, 0x1eaf, 0xab08, 0x40b8, 0x0851, 0x3a0d, 0x0010, 0xa39c, 0x0bbb, 0x438e, 0x115e, 0x2965, 0x43c4, 0x448a, 0x447b, 0x4389, 0x42a7, 0xba70, 0x46a5, 0x437f, 0x1a53, 0xb2d0, 0xb2b4, 0x417a, 0xb20f, 0x3ddf, 0x4213, 0xbf03, 0x419b, 0xb229, 0x1cc5, 0x42d9, 0x4307, 0xb0b0, 0x424e, 0xb0c5, 0x2b34, 0x430e, 0x42a2, 0x409c, 0x41eb, 0x4143, 0x4092, 0xbfab, 0x419d, 0x4370, 0x173a, 0xbac9, 0x44ba, 0xba24, 0x4292, 0xbf83, 0x10f4, 0x1352, 0x435f, 0x430a, 0x4113, 0x4556, 0xba04, 0x43b0, 0x43cc, 0x3369, 0xb0c6, 0x43ca, 0x42e2, 0xbf43, 0xb04a, 0xb0cf, 0xb2c0, 0xbff0, 0x436f, 0x40e7, 0xbf72, 0x1cda, 0xba12, 0xb2b6, 0x1294, 0xb083, 0x44f3, 0xbf4a, 0x4379, 0x4206, 0x4168, 0x18aa, 0x43b3, 0xb2d4, 0x442d, 0x4363, 0x4219, 0x3fba, 0xb2f9, 0x4226, 0xbff0, 0xb050, 0x1882, 0x4143, 0x4602, 0x3889, 0x19a3, 0xbf35, 0x4110, 0x429e, 0xb2dd, 0x4087, 0xba4b, 0x4303, 0x415b, 0xb031, 0xb003, 0x4062, 0x1aa9, 0x4066, 0x1ca6, 0xbf80, 0x1970, 0x0840, 0xa6a7, 0xaf8d, 0x40ea, 0x2ad2, 0xb2b7, 0x41aa, 0xbae6, 0x393e, 0x4264, 0x40ae, 0xbaf7, 0xbf6d, 0xb244, 0xba21, 0xba7b, 0x23d2, 0x400a, 0x4059, 0xba4d, 0x1505, 0x1d64, 0x42b9, 0x4197, 0x41f8, 0x4437, 0x41ac, 0x0a56, 0x1d3d, 0x4555, 0xbf8f, 0x4319, 0xbade, 0xba6e, 0x03a1, 0xb2b4, 0x40ac, 0xa7c5, 0xbf14, 0xac04, 0x221e, 0x4246, 0xb2d9, 0x4020, 0xba06, 0xbff0, 0x40d0, 0x2539, 0xba4f, 0x4283, 0x43b5, 0x42f1, 0x43a9, 0xb215, 0xba78, 0x4353, 0xb2ad, 0x0eb2, 0x40b9, 0x05f6, 0x1b03, 0x037a, 0x1922, 0x1d2d, 0x4218, 0xbf42, 0x2674, 0x44fd, 0x41e2, 0xba4d, 0x43f9, 0x10d1, 0x4121, 0x4309, 0xb25a, 0xba68, 0xb2d2, 0x402c, 0x27fb, 0x4143, 0xba48, 0xbfc6, 0x42cd, 0x438f, 0xb23a, 0x253a, 0x19ff, 0x40ed, 0x429a, 0x03f5, 0x412e, 0xba52, 0x43e5, 0xb2bf, 0x1bae, 0x43a5, 0x4389, 0x4253, 0x1bc9, 0xbf8c, 0xb20d, 0xb2be, 0x43ea, 0xa6fd, 0x3b1d, 0x40ad, 0x43d6, 0x43fd, 0x448a, 0x17ff, 0x430b, 0x41e0, 0xba42, 0xbf57, 0xaf35, 0x423c, 0x40b0, 0x43f4, 0xb245, 0x1a09, 0x04e0, 0x398e, 0x4049, 0x4389, 0x43b2, 0xbf00, 0xb0b9, 0x1f41, 0x41a5, 0x4143, 0x403e, 0xa712, 0xab59, 0xbac2, 0x422b, 0x21e4, 0xbfdd, 0xb273, 0x2b57, 0xa6e7, 0xa357, 0xa329, 0x427e, 0xb29d, 0x0b10, 0xbae1, 0xa29b, 0x2e57, 0x41dd, 0x43b3, 0xb2ff, 0xb201, 0xa480, 0x111a, 0x434f, 0x3828, 0x0c1e, 0x40bd, 0x1ddc, 0x4120, 0xbf90, 0x1f65, 0x4387, 0x4307, 0x4235, 0xbf87, 0xbff0, 0x41c6, 0x36e9, 0x4120, 0x40ca, 0x4383, 0xb211, 0x41f5, 0x4309, 0xbfd5, 0x4332, 0xb23a, 0x4227, 0x4358, 0x2229, 0x41a0, 0xb09b, 0x4162, 0x00af, 0x4017, 0xb270, 0xbf60, 0x4289, 0xba44, 0x415f, 0x3745, 0x1d25, 0xb24f, 0x415b, 0x1dde, 0x41d5, 0x421d, 0x4061, 0xb23d, 0xb0e1, 0xb231, 0xbfce, 0x4479, 0x18ae, 0x429b, 0x3aa6, 0xb031, 0xbf08, 0x4173, 0x4025, 0xbf01, 0xbf80, 0x429a, 0xa140, 0x4241, 0x4004, 0xbaf0, 0x1950, 0xbf00, 0x4069, 0xbf90, 0x42f8, 0x4149, 0x4352, 0x40ee, 0x1a2e, 0x1888, 0x2be6, 0xa75e, 0xb040, 0x42ed, 0x4158, 0xbf7a, 0x45aa, 0x0e95, 0x1014, 0x0d0c, 0x22e9, 0x1dc7, 0xbfa0, 0xb092, 0xbfb0, 0x41c7, 0x0377, 0xb2c9, 0x4260, 0x4371, 0x1ba6, 0x4273, 0x426c, 0x1a58, 0x1e6b, 0x0f52, 0x1c50, 0x3528, 0x4157, 0x4296, 0xbf43, 0x345e, 0x40f5, 0x429c, 0x42bc, 0x4312, 0xa05d, 0x1ea4, 0x40e2, 0xbf92, 0x38a7, 0x43b2, 0xbfb0, 0x1d58, 0xba54, 0xb256, 0xb255, 0x442e, 0x4293, 0xba67, 0x1804, 0xb27c, 0x425a, 0x4173, 0xba62, 0x4221, 0x41c7, 0x3475, 0xbfb8, 0x4002, 0xb0b4, 0x412c, 0x4589, 0xa77d, 0xb020, 0xbaff, 0xa2d3, 0x4344, 0xb205, 0x46b8, 0x1801, 0x1ee2, 0x4131, 0x43ef, 0x408a, 0x1107, 0x4168, 0xbf41, 0xb20c, 0x432c, 0x2c41, 0x3ee4, 0xa926, 0x40aa, 0xb0b8, 0x3a6f, 0x469a, 0x4379, 0xbf7c, 0x1f14, 0xb2c5, 0xb2bb, 0x1c36, 0xb265, 0xba1d, 0x41af, 0x374d, 0xb28e, 0x42b4, 0xbae7, 0x4258, 0x40f9, 0xa16f, 0xae72, 0x1f30, 0xbad1, 0x1e7c, 0x4319, 0xbfd9, 0xbae6, 0xb0d2, 0xb28f, 0xb29e, 0xb03f, 0x41eb, 0x416e, 0x4095, 0x3136, 0xa0cf, 0x1dd8, 0x40e6, 0x06b2, 0xb2a3, 0x0458, 0xb036, 0x435d, 0x43f2, 0x43b0, 0x315c, 0xba07, 0xb0ba, 0x439a, 0xb21d, 0x1079, 0xba14, 0xbf22, 0x433f, 0x40cd, 0xbfe0, 0xa72f, 0xbac2, 0x4134, 0xb0ed, 0xbfda, 0xb2d7, 0x41e2, 0xba61, 0x1b57, 0x406a, 0x4217, 0x4064, 0xba66, 0x3ffd, 0x41b3, 0x343a, 0xb233, 0xbfd3, 0x4111, 0x4213, 0x4093, 0x40c3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x6a1fb324, 0x37d1732c, 0x1194c5d3, 0x5a48240b, 0xf3985d54, 0xf7ed5c2f, 0xf3b4ec29, 0x13619425, 0x3b477912, 0x36e07690, 0x5d185c63, 0x32e603e4, 0xdff550bc, 0xbcb83fd1, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x1a1c0000, 0x00000e0d, 0xffff8d0e, 0x00000000, 0x0000003a, 0xffff8d0e, 0x00000000, 0x000071f5, 0x00001419, 0x868c5020, 0xffffffff, 0x32e603e4, 0xdff550bc, 0x000217f8, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb2dc, 0x425f, 0xb2d1, 0xb20b, 0x1b94, 0xbf39, 0x1983, 0x4350, 0x426e, 0x43a5, 0x4377, 0x1ee2, 0x1e44, 0x1b2d, 0x1ad2, 0x1cc0, 0x402d, 0xb2f6, 0x02f0, 0x3eb0, 0x4037, 0x0a3e, 0x418c, 0xbf8a, 0x405e, 0xa376, 0xb075, 0x420d, 0xb2b6, 0xb022, 0x440a, 0xb0cc, 0x46a9, 0x40e7, 0x21fc, 0xb27b, 0x437c, 0xa9bc, 0x1c9d, 0x42bd, 0x4197, 0xbf9f, 0x441e, 0xba11, 0x424e, 0xbff0, 0x41cb, 0xb21c, 0x428b, 0x1a18, 0x4007, 0x1795, 0x2000, 0x4006, 0x43b6, 0xbf3b, 0x42ec, 0x422d, 0x2e16, 0x29b0, 0x1bee, 0x407d, 0xb02d, 0x4075, 0xbf64, 0x407e, 0x4547, 0x4159, 0xb0eb, 0xb21d, 0x41f2, 0x09e7, 0x410d, 0x462b, 0x4583, 0xb2f8, 0x409e, 0x40a4, 0xbfd0, 0xb051, 0x4029, 0x3874, 0x4642, 0xbfba, 0x40d1, 0xb217, 0xb083, 0x2a1f, 0x4263, 0x468a, 0x322b, 0xba31, 0x0de5, 0x4091, 0xb243, 0xb296, 0x4259, 0xac7d, 0x2a5a, 0x4010, 0xbf98, 0x4116, 0x4166, 0x19d7, 0xba43, 0x0e2e, 0xbff0, 0x430f, 0x439b, 0xa2f3, 0x1914, 0x3b48, 0x18d2, 0x4065, 0xb07d, 0xb02c, 0xb2a7, 0xb2c8, 0x03b0, 0xa9b3, 0x45de, 0xbf00, 0xb27c, 0x4347, 0x1fc8, 0x40cc, 0x4095, 0xbf74, 0x2dfe, 0x4223, 0x42f8, 0x3a83, 0x1b47, 0x1be1, 0x1281, 0x42da, 0x03dc, 0x40f1, 0xb2c1, 0x4648, 0x44b0, 0xba72, 0xba36, 0x0d2d, 0x4001, 0xbf9b, 0x1890, 0x45d4, 0x42b6, 0x40f9, 0x420a, 0x410e, 0xaa69, 0xb263, 0xbf77, 0x00be, 0x4092, 0x40e7, 0xb2b1, 0x435a, 0x42b4, 0xb09f, 0x420d, 0xb06a, 0xba63, 0x4272, 0x42d0, 0x32b7, 0x43ce, 0x1af8, 0xb06d, 0x430c, 0x404c, 0x097a, 0xb2c9, 0xbf3c, 0x2533, 0xbadd, 0x46b0, 0x441f, 0xb0f8, 0x4384, 0x45da, 0x42fb, 0x4380, 0xbac6, 0x35c4, 0x1564, 0x0e66, 0xbfe0, 0x3645, 0xba64, 0x4229, 0xa6c0, 0xbf14, 0xb07b, 0x40fe, 0x4353, 0x1856, 0xa5a8, 0x1bb5, 0xaa8b, 0x40bf, 0xabf0, 0x4389, 0xbac0, 0xba7b, 0x43e4, 0x1ee2, 0x42ce, 0xbf0e, 0x46a0, 0x4279, 0x45ae, 0x4096, 0x0e54, 0x4106, 0xb25d, 0x40ec, 0x3483, 0x4090, 0x42cf, 0x1a7a, 0x406b, 0xb02c, 0x43b1, 0x4029, 0x2399, 0x19ce, 0xb233, 0x4303, 0xbfd0, 0x2c0c, 0x43d7, 0xb254, 0x27fa, 0x2ea7, 0x0a4b, 0x40b1, 0xbfc4, 0x1b4d, 0x420e, 0xb2b7, 0xb234, 0x4210, 0x43f8, 0x466b, 0x4134, 0xb2f1, 0x1f53, 0xb06b, 0x40a2, 0xb0f1, 0x40ea, 0xb0f5, 0x421d, 0x42df, 0x28d3, 0xba66, 0x42fc, 0xbfd5, 0xbfe0, 0xbfa0, 0xba2f, 0x45c1, 0xbae4, 0xb290, 0x4030, 0x436c, 0x466e, 0x44d9, 0xba3a, 0xba5f, 0xa987, 0x247e, 0x1273, 0x1925, 0xb240, 0xa2a8, 0x1d6a, 0xbf94, 0x42e4, 0x43e1, 0x42ff, 0xbf49, 0x454d, 0x442d, 0xb26b, 0xb054, 0x41d3, 0x402d, 0x40b2, 0x43e0, 0xbf2e, 0x0098, 0x4047, 0xb2af, 0x42e4, 0x4315, 0x41c2, 0xab7f, 0xb279, 0xb253, 0x4101, 0xb2fe, 0xa41d, 0xb2f0, 0x4357, 0x1b5e, 0x3f79, 0xb26e, 0x41bd, 0xbf69, 0x1813, 0x0a71, 0x0efc, 0x4215, 0x16f2, 0x4108, 0x4191, 0xbf2a, 0xb2a0, 0x1c23, 0xaeff, 0x21f2, 0x2a43, 0xbad6, 0xac3a, 0x43ee, 0x4226, 0xbf42, 0x412a, 0x04ce, 0x3044, 0x42fd, 0x430f, 0x3e1c, 0x4063, 0xbfac, 0x3557, 0x4258, 0x06d8, 0x4250, 0x43fa, 0xbaf8, 0x40e1, 0x11e8, 0x4000, 0x40ff, 0x1adf, 0x4693, 0x4257, 0x46d0, 0xbfba, 0xb2b9, 0xb23d, 0xba49, 0x4266, 0x4210, 0xa557, 0x1df4, 0xba45, 0x454c, 0xa556, 0x434e, 0x1a7a, 0x4380, 0xb09a, 0x4087, 0x1937, 0xb20f, 0x4067, 0xbfd3, 0x42b7, 0xb227, 0x41f9, 0xbaee, 0x4191, 0x4011, 0x2bf8, 0x41c0, 0x4599, 0x428a, 0x40ba, 0x1e9f, 0x12b1, 0x2cdc, 0xb285, 0xaa95, 0xbfbe, 0x4022, 0x466c, 0xba77, 0xb041, 0x42bc, 0x4016, 0x434b, 0x41c6, 0xba0a, 0x416d, 0x3ddc, 0xba28, 0x40f3, 0x3d50, 0x41fb, 0x437d, 0x42e3, 0x4343, 0x143b, 0xbf4b, 0x1a96, 0x43cd, 0x05a6, 0x41bf, 0x413e, 0x1de5, 0xb0c7, 0x436d, 0xbac4, 0x43cf, 0x42e9, 0x4294, 0x07b5, 0xa1ea, 0x4577, 0xbae6, 0x4387, 0x42eb, 0x2ba4, 0x4117, 0x4229, 0x4171, 0xb0b0, 0xbade, 0x4226, 0x43a4, 0xa8c8, 0x4213, 0xbf00, 0xbf88, 0xb07b, 0x4229, 0x322d, 0x2ed2, 0x42d6, 0x2426, 0x427b, 0xbf28, 0x039a, 0x4377, 0x1baf, 0x2d7d, 0x42fd, 0x4699, 0x4004, 0x4166, 0x4164, 0x0761, 0xb0da, 0xb2c5, 0x4178, 0x19b8, 0x42a1, 0xbfb9, 0x4299, 0x4192, 0x40f5, 0x42d0, 0xbf7f, 0x4353, 0x40d7, 0x4261, 0x09af, 0x1c71, 0x4492, 0x421b, 0xba64, 0x41a2, 0x4391, 0xbfe0, 0x18ad, 0x4114, 0x42ba, 0xbafa, 0xb0e0, 0xbf12, 0x3c22, 0x13d3, 0xb270, 0x1297, 0x4256, 0x4227, 0x4148, 0xbfa1, 0xbf80, 0x1863, 0x075c, 0xa63a, 0x192a, 0xb285, 0x415f, 0x371f, 0x36d4, 0x41f9, 0xb09f, 0x4246, 0xb2f3, 0xb000, 0x420d, 0x4315, 0xb2b3, 0x0bc4, 0x2021, 0x0dda, 0xb0c5, 0xbf7a, 0x432f, 0xa889, 0x4112, 0x42ea, 0x4310, 0xbf3e, 0xa3e2, 0x4291, 0x1be6, 0x43bd, 0x40b7, 0x205f, 0x4014, 0x31dc, 0xa38c, 0x4117, 0x4230, 0x4381, 0xbf66, 0x4002, 0x41df, 0x42f0, 0xbaf0, 0x4171, 0xba79, 0x1339, 0x45cb, 0x409d, 0xbf18, 0x4163, 0xb07c, 0x4172, 0x40c2, 0x2cb7, 0x4309, 0x4068, 0x1788, 0xba35, 0x42b2, 0x43a5, 0x43c5, 0x442f, 0xba0c, 0x014d, 0x432c, 0xb2d3, 0x3076, 0x4274, 0xb283, 0xa8fe, 0x1c15, 0x46d1, 0xb075, 0xbf1e, 0x42b9, 0x31df, 0xba3b, 0x414e, 0xbae5, 0xa161, 0xb2c0, 0x436c, 0xbf9c, 0x43c1, 0xbadb, 0x18b0, 0xbfe2, 0xaf82, 0x39f6, 0x41b3, 0x2696, 0x38ad, 0x42b5, 0xb043, 0x43dd, 0x40ca, 0x42f0, 0xba54, 0x4369, 0x0412, 0x4349, 0x438f, 0x4555, 0x4022, 0x412f, 0x41f4, 0x4139, 0xa343, 0x41c4, 0xba2c, 0xad94, 0xbfb4, 0x0d58, 0x3a42, 0x43a9, 0x41fb, 0xbaf7, 0x463c, 0x0151, 0x44e8, 0x43bf, 0x42b2, 0x42e1, 0x0479, 0x1801, 0xba2e, 0x1614, 0x41fe, 0x4204, 0xbad0, 0x18ad, 0x4056, 0xb0e2, 0xb2a8, 0x06e3, 0xb260, 0xbf3f, 0xba59, 0x4620, 0xba29, 0x3348, 0x45c4, 0xbaef, 0xb293, 0xbadb, 0x40d2, 0x4374, 0xba7f, 0x35fa, 0x1b6c, 0xb23b, 0x2a15, 0x1ed7, 0xa687, 0xa2c0, 0x01fc, 0xa940, 0xba6e, 0x459a, 0x4044, 0x437d, 0xb2ab, 0x3190, 0x2415, 0x2092, 0xbf87, 0x427d, 0xb271, 0x2f34, 0x21c7, 0x404d, 0xb08a, 0x1ce7, 0x30cd, 0x433c, 0x45ab, 0xb234, 0x438d, 0x3eae, 0x4062, 0xb224, 0x3c04, 0xb2ce, 0xb23a, 0x17a3, 0xbfbc, 0x1af8, 0x430c, 0x4264, 0x43d9, 0xb2b5, 0x40dc, 0xbad1, 0x1ad9, 0x41d7, 0x40b1, 0xb0e2, 0x43b9, 0x41ca, 0x352f, 0xbf70, 0x33cb, 0xac4b, 0xba11, 0xb0d1, 0x42c3, 0xbf0e, 0xb277, 0x1c5f, 0xb2f3, 0x4278, 0x4005, 0x40d3, 0xa013, 0x441a, 0x44b4, 0x089a, 0xad41, 0x1c00, 0xb238, 0x46a9, 0x40a7, 0xba4e, 0x43ab, 0x2692, 0x1f89, 0xb021, 0x41a7, 0xbfa2, 0x106c, 0x4213, 0xb251, 0x423c, 0x43fd, 0x423d, 0x406b, 0x40b7, 0x1cc0, 0xacca, 0x433f, 0x3dda, 0xba4a, 0x3137, 0x2707, 0x4167, 0x4016, 0xbf45, 0x40d7, 0xba6a, 0x403b, 0xae6b, 0x42ae, 0x4351, 0xb0bf, 0x420c, 0x2052, 0xb037, 0x1b19, 0x43cf, 0x42aa, 0x40cd, 0x4329, 0x0139, 0xbf32, 0x2fda, 0x1320, 0x256b, 0x421c, 0x41e4, 0x4080, 0x40b8, 0x07be, 0x3f93, 0x4270, 0xb2d8, 0x4295, 0x41ba, 0xb013, 0x440f, 0x4157, 0x42b1, 0x40f4, 0xb0f6, 0x1b7d, 0x408b, 0xbfe0, 0xba53, 0x433a, 0x464f, 0xbf25, 0x406e, 0xae0f, 0x1c12, 0xb230, 0xbacf, 0xbacb, 0x1fe3, 0xbf90, 0x4602, 0xbf82, 0xba73, 0x4481, 0x23b5, 0x4394, 0x4320, 0xbad1, 0x1aec, 0x27c6, 0x41bd, 0x4157, 0x1de8, 0xbfe0, 0x435e, 0x41de, 0x25df, 0x01fe, 0x40d6, 0x467f, 0xb081, 0xb0b2, 0x3885, 0x192d, 0x2974, 0x31f9, 0x458d, 0x2fc1, 0xb048, 0xbf18, 0x10c7, 0x4272, 0xb25a, 0x4198, 0xb221, 0x400f, 0xa6c2, 0x42d2, 0x1b23, 0x417b, 0x4456, 0x4465, 0x1fdf, 0x1c70, 0x4068, 0xb2d8, 0x4582, 0xb279, 0x40b9, 0xba17, 0x4243, 0x4060, 0x1936, 0xbf0f, 0xa1bc, 0xb0fa, 0x439d, 0x41e8, 0x39be, 0xb2e1, 0x4430, 0x40ff, 0x1a28, 0x42de, 0x1e90, 0x417e, 0x03bc, 0x4240, 0x435c, 0xbad7, 0x1de9, 0xa31a, 0x1135, 0x4198, 0x441f, 0x4276, 0xbf70, 0x1d98, 0xbfa4, 0x418f, 0xba2f, 0x41f1, 0x4182, 0x41e0, 0x425b, 0xb269, 0x1ff1, 0x4330, 0xb29e, 0x2039, 0x41b9, 0xa5d6, 0x42bb, 0xb2dd, 0x4025, 0x2734, 0x469b, 0x4368, 0x1c15, 0x41c3, 0xbfb1, 0x0bef, 0x4469, 0xb214, 0x3073, 0x423d, 0x1ecf, 0x437f, 0x41e6, 0xac31, 0xbf6d, 0x18aa, 0x4641, 0x2805, 0x43e1, 0xa531, 0x42a7, 0xb246, 0x1f48, 0x42a0, 0x1e10, 0x4263, 0x43eb, 0xb090, 0x4020, 0xa807, 0x4149, 0x42b1, 0xb291, 0xbf90, 0x2fbf, 0x1edd, 0x3d40, 0x433f, 0x3eb8, 0xbf5f, 0xb212, 0x4151, 0x43b7, 0x4415, 0x03b7, 0x40cb, 0xba3f, 0x438d, 0x41fd, 0x0518, 0x42cf, 0xa79a, 0xa354, 0x2ac3, 0xb25a, 0x4145, 0xa5e8, 0x400e, 0x1851, 0xbf87, 0x1c05, 0x4377, 0x405d, 0xba00, 0xb2d5, 0x4232, 0x319c, 0x4409, 0x1f3d, 0x41e6, 0x4069, 0xbfb4, 0x41d9, 0x18d0, 0x4307, 0x4567, 0x433e, 0x421c, 0x429c, 0x055c, 0x41ab, 0x40b1, 0x40f1, 0x353d, 0xaae0, 0x4237, 0xaadc, 0x23f3, 0xb0b0, 0xbf47, 0x401a, 0xaeea, 0x4077, 0xa7bd, 0x4209, 0x41a4, 0x46eb, 0x437f, 0x4226, 0xb2e2, 0xb04a, 0x4233, 0x4387, 0xbfc8, 0x46dc, 0x4381, 0xbada, 0x0d3d, 0x44cb, 0x405c, 0x4544, 0x438e, 0x419a, 0x40f9, 0x442a, 0xb000, 0xb2da, 0x1aa8, 0x1e51, 0x1dd4, 0x1319, 0xba33, 0x4176, 0x40eb, 0xbff0, 0xbf62, 0x42d7, 0x40e7, 0x413e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xec5af9f4, 0x9436eb06, 0x9ba4ad42, 0x6f255f58, 0x3f12d10f, 0x0928b6c9, 0x415b7bf8, 0x054f2f3e, 0x41a3cd86, 0x6af229e8, 0xc9f61355, 0xeb58ff68, 0x0c0e2ab2, 0xe150fa69, 0x00000000, 0xe00001f0 }, FinalRegs = new uint[] { 0xffffff39, 0x00000000, 0x000000f3, 0x00000000, 0x000000fa, 0x0000002c, 0x2eb370e8, 0x02c5a110, 0xe151009d, 0xe150fdb5, 0xffffffff, 0xc2a1f61a, 0xe150f865, 0xe150f98d, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x368f, 0x4181, 0xba43, 0x438d, 0xb2ff, 0x43b4, 0x1f09, 0xb23a, 0x4230, 0x293c, 0x4083, 0x243f, 0x2f0b, 0xba43, 0x4022, 0xbf99, 0x2d22, 0xb03a, 0xb2bd, 0x44d3, 0x08ec, 0x425e, 0xb00c, 0x407d, 0xba24, 0xafc8, 0x1de9, 0xbf70, 0x41a6, 0xbf58, 0x42e9, 0x264c, 0x45c3, 0x40bd, 0x4029, 0xba3a, 0x1fda, 0x405c, 0x4005, 0xb2cd, 0x4079, 0x3285, 0xa715, 0x411f, 0x4560, 0xbfb1, 0x406c, 0x4091, 0xa6a9, 0xb22f, 0x41d6, 0xb20e, 0x40ad, 0xbff0, 0xad3c, 0x443a, 0xbf80, 0xb256, 0x4025, 0x43b3, 0x1fd2, 0x00f7, 0x4160, 0xbf1f, 0x36b9, 0x19d7, 0xb26e, 0x4101, 0x41d5, 0x1d7a, 0xba56, 0x43a6, 0xbf83, 0xb2dc, 0x424b, 0x4124, 0xba59, 0x4307, 0xb0cf, 0x4328, 0x4065, 0xbafa, 0xb0c7, 0xb0af, 0xbf83, 0x43b6, 0xb2b8, 0x408c, 0x0730, 0xbfb4, 0xbff0, 0x02a9, 0xb286, 0x4258, 0x45b2, 0x42dd, 0x427e, 0x1faf, 0x1af5, 0x40fb, 0xb092, 0x4615, 0xbf29, 0x1b44, 0xbf60, 0xa826, 0xb250, 0x40cc, 0xb0ad, 0x23d8, 0x10b2, 0x4183, 0x426a, 0x4152, 0xbaf9, 0xacfe, 0x1cd8, 0xbfa2, 0x2419, 0x3078, 0x4083, 0xb2c7, 0x45ae, 0x38db, 0x1879, 0x40cf, 0x4220, 0xb23d, 0x20b8, 0xba32, 0x4031, 0x455b, 0x105c, 0xb295, 0x1af1, 0x12d9, 0xbfc9, 0x403c, 0x4133, 0x4261, 0x41aa, 0x4369, 0x032b, 0xb049, 0xad38, 0x27b3, 0xb20c, 0x424c, 0x4238, 0x1923, 0xb283, 0xb050, 0x424a, 0x1c55, 0x0a49, 0xb0f6, 0x43e2, 0x0e4f, 0x4219, 0x400a, 0x42ac, 0x414e, 0xa73b, 0xbf0f, 0x437b, 0x41ef, 0xa1bd, 0x4051, 0x4308, 0x16a2, 0xbfa0, 0xb26d, 0xba3c, 0x181e, 0xba7e, 0x42c2, 0xba45, 0xac7d, 0x087f, 0x4225, 0xba6c, 0x188b, 0xbf0a, 0xa98e, 0xb2da, 0x1f20, 0x19c1, 0xbf08, 0x40fe, 0x4137, 0xb23a, 0xbfb0, 0x4098, 0x1814, 0x4026, 0x2027, 0x4269, 0xb060, 0x1807, 0xb28e, 0x1ae8, 0xb2a0, 0x4308, 0x414a, 0x416e, 0x1358, 0x43c5, 0xb257, 0x4412, 0xbfc0, 0x2c84, 0x4212, 0xbf7f, 0x40b3, 0xb0cc, 0x404b, 0x0a2d, 0x1a49, 0x1eb1, 0x4342, 0x43b1, 0x1bc8, 0xa36f, 0x42b3, 0x1995, 0x43d8, 0x41c3, 0x407f, 0x410b, 0x40b9, 0x416c, 0xac31, 0x4010, 0xb08d, 0x4007, 0xb209, 0xb091, 0x4383, 0xb21e, 0x411d, 0xb02b, 0x44ad, 0xbf82, 0x18f7, 0x422a, 0xb01d, 0x3f8a, 0xb05f, 0x4589, 0x45a6, 0x4161, 0xba59, 0x41e8, 0xb280, 0x4052, 0xae0d, 0x43b8, 0x4297, 0xb2e8, 0xb260, 0x44a9, 0xbad8, 0x429b, 0x1271, 0x412a, 0x43ef, 0x403f, 0x1f4c, 0xbf7e, 0xa769, 0x40fa, 0xb2a7, 0x3cc5, 0xbf70, 0x17d1, 0x41de, 0x414f, 0xbf7f, 0xba4d, 0xa1df, 0x022b, 0xa268, 0x4237, 0x1a45, 0x40ce, 0x4324, 0x129a, 0x4087, 0x1820, 0x4287, 0x401a, 0x43a3, 0x268b, 0x1b43, 0xa18b, 0x1855, 0x4046, 0x406f, 0x43ee, 0x140b, 0xba3c, 0xb27d, 0x4227, 0xb2fc, 0xab9c, 0x418c, 0xbf2d, 0x42bc, 0x41a9, 0x02ae, 0xb203, 0x401d, 0x4250, 0x431a, 0x40b4, 0xba7c, 0x40db, 0x20f2, 0xb200, 0xa5a0, 0x31f7, 0x028e, 0xbfa0, 0xb2c0, 0x1584, 0xbadf, 0x4278, 0x43ab, 0x0582, 0x414a, 0x1a67, 0x3b4c, 0x1c8a, 0x42c3, 0x40e1, 0x4272, 0xbf41, 0xba3c, 0x42b7, 0x40fa, 0x0974, 0x42b7, 0x1c59, 0xbf17, 0x1c3b, 0x42b2, 0x42c7, 0x1feb, 0x419f, 0x24bb, 0x0932, 0x46c4, 0x42fd, 0x16d0, 0x42f3, 0xbac8, 0x125a, 0xb275, 0x18b1, 0x4432, 0x2522, 0xbf6c, 0x43e3, 0xba24, 0xb053, 0x439c, 0xa9a6, 0x1aa2, 0x3eaf, 0x402d, 0x0b2b, 0xa543, 0xbfa0, 0x4055, 0xbf12, 0x4168, 0x437e, 0x1ea7, 0x4022, 0xa4e7, 0xba4b, 0x394d, 0x195c, 0x1ab5, 0x41fd, 0x436d, 0xaa65, 0x2437, 0xbac6, 0x424a, 0xbfca, 0xba0e, 0xbaec, 0x4038, 0x1252, 0x258d, 0x4186, 0x45d2, 0x42a3, 0x45b8, 0x4160, 0xbf3e, 0x3d8d, 0xb2a1, 0xb277, 0xb2b9, 0xba2a, 0x42e5, 0x4149, 0x4345, 0xba37, 0x43a0, 0x4104, 0x20f0, 0xbac7, 0x43d0, 0x42a9, 0x4215, 0x4576, 0x0a5a, 0xb21c, 0x0657, 0xba38, 0x0f44, 0x1b98, 0x1deb, 0x43af, 0x4198, 0x40bf, 0x3af8, 0xbfd4, 0x03ec, 0x2f69, 0xb05c, 0x2d36, 0x1505, 0xb2be, 0x40ba, 0xb016, 0x40e9, 0x0007, 0xbf66, 0xb284, 0xb2fa, 0x19c8, 0x11bc, 0xba48, 0x4172, 0xa0c2, 0x41de, 0x42d9, 0xb224, 0x1e05, 0x42df, 0x1bf1, 0x272b, 0x4143, 0xbf97, 0xba04, 0x43a9, 0x16d4, 0xb2b7, 0xbafe, 0x033a, 0xba53, 0xb2a6, 0x432c, 0x425e, 0x35c4, 0x43d7, 0x43a1, 0xb209, 0x40d7, 0xbad6, 0x434c, 0x4380, 0xbf61, 0x43db, 0xa3c6, 0x4202, 0x41a0, 0x4370, 0x42d2, 0xb241, 0x0725, 0xb24a, 0x41b8, 0x1e71, 0x42dc, 0x419c, 0x1ccf, 0xb267, 0x4427, 0xb2f7, 0xb235, 0x2c94, 0x19eb, 0xb2b2, 0xbf76, 0x3401, 0x41d7, 0x4212, 0x1e3e, 0xae23, 0x3934, 0x4128, 0xb245, 0x1fcb, 0xbfcb, 0x1a32, 0x44dc, 0x41a4, 0x3ec2, 0x4381, 0x318a, 0x408b, 0x4076, 0x42be, 0x4214, 0x0530, 0x4351, 0x2735, 0xad1b, 0x436c, 0x43dc, 0xba34, 0x4276, 0x41eb, 0xb01f, 0x3246, 0x0717, 0x437e, 0x4021, 0x4043, 0xb237, 0x1876, 0x417d, 0x41d4, 0xbf57, 0xba07, 0xb2e7, 0x1f8b, 0xb28c, 0xbf21, 0x4243, 0x4157, 0x3d59, 0x40be, 0xb22b, 0xb21f, 0x2afc, 0x0525, 0x4291, 0x465e, 0x402d, 0x45ce, 0x0f5f, 0x3952, 0xaaf9, 0x414a, 0xbacc, 0x0081, 0x2e90, 0x4280, 0xbfa5, 0x404c, 0x413e, 0xb2d4, 0x38c8, 0xbf0a, 0x3124, 0x402c, 0x404e, 0x3c59, 0x41f1, 0xba4a, 0xbadc, 0x4117, 0xa1c4, 0x414e, 0x46b4, 0x40f7, 0xb251, 0x46a9, 0xa050, 0xb28c, 0x4369, 0x029c, 0x424c, 0x469b, 0x4266, 0x43b8, 0x2c0e, 0xbfbc, 0x1825, 0xadd6, 0x1b78, 0xba75, 0x10ef, 0x26bd, 0xb09b, 0x42b8, 0xba65, 0x3f0f, 0x0476, 0xb0dc, 0xb21f, 0xb02a, 0x1cb4, 0x468d, 0x3cbe, 0xbf29, 0xbad6, 0x439d, 0xb2d8, 0x1fc1, 0x4260, 0x4541, 0xb271, 0xbac0, 0x1d46, 0xb273, 0x197c, 0x4280, 0x426a, 0x1f19, 0x426c, 0xb284, 0x4230, 0xbf60, 0xb2fd, 0xb203, 0x18b1, 0xbf58, 0x39ee, 0x1ff4, 0x27de, 0xbfa0, 0xb27d, 0xbfe0, 0xb21c, 0x4165, 0x404d, 0xb2d9, 0x45c5, 0x4333, 0x1ba5, 0x4568, 0x43d8, 0xb277, 0x1ef0, 0x124f, 0xb251, 0x1d1d, 0xbf4a, 0x409b, 0xb23c, 0x4400, 0x4391, 0x293e, 0x1a99, 0x40be, 0x0314, 0x43bb, 0x41f5, 0xb28c, 0x1db7, 0x40c4, 0xb00c, 0x4175, 0x40af, 0x4291, 0x3db2, 0xa7cb, 0x4031, 0xba26, 0x43fa, 0x188c, 0x403e, 0x0de1, 0x432b, 0xb027, 0x2431, 0xbf74, 0x41e4, 0x432b, 0xb20f, 0x2b39, 0x4352, 0xaff4, 0xb2eb, 0x45ca, 0x4273, 0x4142, 0x4366, 0x40c1, 0x44fb, 0x1930, 0xb2a5, 0x1bf9, 0xb294, 0x42ec, 0x431c, 0x4345, 0xbf55, 0x0d4b, 0xb07f, 0x0095, 0x2cff, 0xb284, 0x14a1, 0x42bb, 0x4643, 0x436c, 0xb2f1, 0x44e8, 0xb23f, 0x4207, 0x2f1e, 0xb2d7, 0xa512, 0xac75, 0x3a91, 0x231d, 0x3c3d, 0x4151, 0x3341, 0x01ad, 0x44d5, 0xbf3c, 0x1763, 0xa0da, 0x41af, 0x1a77, 0x406d, 0x40fa, 0x18ed, 0x429f, 0x1a22, 0x295a, 0x2dbf, 0x4007, 0x0072, 0xbae7, 0x4343, 0x425d, 0xb2db, 0xb2d4, 0xac1e, 0x40cc, 0x1068, 0x46ca, 0x40c0, 0x417f, 0x4025, 0x4121, 0xbf1d, 0xb297, 0x1c35, 0x43f0, 0xba3f, 0x1e51, 0x43c9, 0x40de, 0x435b, 0xba05, 0x42b9, 0xb270, 0xbf05, 0x3d22, 0x4206, 0x1c39, 0xa97f, 0xba7e, 0xba1b, 0x31a6, 0x414f, 0x39ef, 0xb26a, 0xbad8, 0x2fd1, 0x1abe, 0x1f4e, 0x43a4, 0xb289, 0xb069, 0xb256, 0x4150, 0x181b, 0xbf6a, 0xa9e8, 0xb299, 0x260b, 0x1b81, 0x1356, 0x09f9, 0x16e5, 0x4353, 0x0218, 0x41e3, 0x42c2, 0x2b68, 0xba77, 0x2cc6, 0x4577, 0x42d2, 0x212f, 0x42b8, 0x4063, 0xb235, 0x195a, 0x3085, 0xbf69, 0x4208, 0x2c24, 0x0117, 0x27bc, 0x421d, 0x4165, 0x4033, 0xbf65, 0x38d0, 0x3e41, 0x46c4, 0x40d8, 0xb2d5, 0xbf60, 0x1ab8, 0x402b, 0x23ec, 0xbfc0, 0xbf21, 0xba39, 0x4176, 0x22b6, 0xa1ca, 0x4041, 0x1e24, 0x2334, 0xb079, 0x1ac9, 0x428c, 0x43a1, 0x4556, 0x1ebd, 0xa512, 0xba5c, 0x2319, 0x1ffe, 0xb25d, 0x421b, 0xbf9b, 0x4012, 0x41de, 0x41ad, 0xbaee, 0x42c6, 0x41d7, 0xb20a, 0x33f2, 0x42de, 0x41a2, 0x0d16, 0xba7a, 0x4289, 0xba26, 0xbf49, 0xb2ae, 0x129d, 0xb2eb, 0x4223, 0x1864, 0x0aad, 0xbf57, 0x412c, 0x404f, 0xaa07, 0x41c6, 0x40ef, 0x114a, 0x46f8, 0xbac8, 0x45f1, 0xa8a5, 0x4338, 0x43ab, 0x0833, 0x2140, 0x41b6, 0x2a3e, 0xb018, 0x4215, 0xbf05, 0xba24, 0xa61c, 0xbae2, 0x3218, 0x1078, 0xa5bb, 0x3cf2, 0x34b0, 0xbacd, 0xb085, 0xbf6d, 0xbac5, 0x46d2, 0x4335, 0x421e, 0xb2e6, 0x1df5, 0x412e, 0xbf08, 0x4060, 0x1c63, 0x1c85, 0xba0b, 0xb25a, 0x430b, 0xbfbe, 0xb072, 0x4307, 0x43f1, 0x436c, 0x463f, 0x0028, 0x4311, 0xa4ad, 0x4648, 0x4361, 0x4175, 0x228c, 0x42e9, 0x40e8, 0x0f81, 0x081b, 0x4146, 0xbf81, 0x42a1, 0xac30, 0x40f1, 0x4492, 0x4336, 0xb2fb, 0x1b1d, 0xb2ac, 0x23c8, 0x44ad, 0x1f91, 0x43fe, 0x42b7, 0x1bed, 0x1cb1, 0x3518, 0x0864, 0x4183, 0x42de, 0x4307, 0xb283, 0x43af, 0xb2cc, 0xba5f, 0x1411, 0xb298, 0x4071, 0xbf16, 0x40c9, 0x4637, 0x4307, 0xbf53, 0x1a70, 0x40ca, 0xb26b, 0x42e6, 0xb079, 0xa782, 0x1c70, 0x4623, 0x40b1, 0x41f2, 0x46a4, 0x2ac3, 0x4229, 0x3f2d, 0x4302, 0x030f, 0xa7bd, 0xbf81, 0x4040, 0xbafc, 0xa376, 0xb05e, 0xbf6b, 0x42ec, 0xa754, 0x41e9, 0x433d, 0x4325, 0x2462, 0x4205, 0xa968, 0xbff0, 0x00c8, 0x4378, 0x430f, 0x4136, 0x41f5, 0x2f8e, 0x44fa, 0x413c, 0xbfb9, 0xb097, 0xbaf6, 0xb03e, 0x43d0, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x8b2100eb, 0x3af85a8f, 0x0bb662e6, 0x7ac5449f, 0x6c8b9706, 0x2d86ea00, 0xd9186e8c, 0xb3755866, 0x7227be38, 0x7f97e5fb, 0xc176d2f9, 0x1ff0913a, 0xcb8e1a87, 0x6b0408ce, 0x00000000, 0x800001f0 }, FinalRegs = new uint[] { 0xffffff72, 0xc176c228, 0x0000008d, 0x00000002, 0x00000000, 0xffffff36, 0x00000000, 0xc176db3c, 0x000016e4, 0x00000000, 0x000017e2, 0x0000206e, 0x00000002, 0xc176c180, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbac5, 0x426b, 0x16bc, 0x2d44, 0x43aa, 0xbf7b, 0x43f8, 0x42b9, 0x458b, 0x1f7e, 0xb0ac, 0xb292, 0x42e3, 0xb251, 0xb2dd, 0x1b86, 0x35fd, 0x410c, 0xac53, 0x3357, 0x35fb, 0x4355, 0x413e, 0xb2b9, 0x42f8, 0xbfa3, 0x408f, 0x1d27, 0xb0dd, 0xbf70, 0x2c41, 0x1bb6, 0xbf0d, 0xb208, 0x2cad, 0xb296, 0x4132, 0x4119, 0x411d, 0x403a, 0x4332, 0xb2cb, 0xbac4, 0x4074, 0x403d, 0x18a4, 0x45ae, 0x42c9, 0xb043, 0xb0b4, 0x40c4, 0xba53, 0x409b, 0xbf63, 0x1a75, 0xb040, 0x4166, 0x3f7e, 0xa55e, 0x428d, 0x4235, 0x459c, 0x42d5, 0x4111, 0x42dc, 0x43ca, 0x4049, 0x4091, 0x29d5, 0xbf61, 0x40cd, 0x422d, 0x4170, 0x4541, 0x1062, 0x1cf3, 0x4248, 0x422e, 0x1cdc, 0xabfc, 0xa3f2, 0x0677, 0x28d6, 0x143c, 0xb2d6, 0xba55, 0xb261, 0x41f7, 0x439c, 0x2b7b, 0x1c82, 0xb008, 0x2432, 0xbf62, 0xa8ee, 0x37a9, 0x1e86, 0xa85b, 0xb25c, 0x434c, 0xbf96, 0x423e, 0x4093, 0x44e1, 0x42ff, 0x41bf, 0xa8c3, 0x43cb, 0x3a77, 0x2b99, 0x0976, 0x3160, 0x4030, 0xb2db, 0xba36, 0x3ef4, 0x4617, 0x1ab5, 0x02d3, 0xb018, 0x43ad, 0x40cd, 0xbf89, 0x4390, 0x15d6, 0x4255, 0x46dc, 0xac0e, 0x11b4, 0xbf05, 0xac46, 0x4392, 0xbac5, 0x1842, 0x40df, 0x4113, 0x4126, 0x1cac, 0x3e49, 0x4051, 0xb2cf, 0x43b7, 0xbf78, 0x07e7, 0xb210, 0xb26b, 0x1f7b, 0x03b0, 0x42db, 0x2d35, 0x43eb, 0xb23a, 0x418a, 0xaddb, 0x1c63, 0xbf33, 0x35a5, 0x4349, 0x0da2, 0x428e, 0x4132, 0x40f8, 0xba40, 0x41e8, 0x40e8, 0x2f5d, 0x417c, 0x1bc0, 0x41b3, 0xaa64, 0x1b12, 0x0f9c, 0x435b, 0x2204, 0x42d7, 0x19ce, 0x4059, 0x40b3, 0xad3a, 0x43a1, 0x44b9, 0x42f0, 0xb2d8, 0x2938, 0xbf8c, 0x0c53, 0x41c7, 0x4177, 0xbaca, 0x40a7, 0x4607, 0x4238, 0x4100, 0x14e5, 0x4271, 0x4148, 0x24b6, 0x108d, 0xb234, 0xafb1, 0x20a6, 0xbf2e, 0x1ff8, 0xba48, 0x42a8, 0x43a6, 0x40f7, 0x3723, 0x1e21, 0x4004, 0x14b3, 0xbae8, 0x43f2, 0x4223, 0xa5a7, 0x4021, 0xbf60, 0xb2dd, 0xb2fa, 0x4144, 0xb284, 0x135c, 0xbf8b, 0x40a8, 0x4238, 0xb26e, 0x42b4, 0x43c5, 0x3a5f, 0x43e5, 0x2fe1, 0xbf5e, 0x039d, 0x3102, 0x4076, 0x40ec, 0x1e43, 0xbfc1, 0x1f10, 0x212d, 0xa889, 0xb2af, 0x1ccd, 0xb22b, 0x4373, 0x09c1, 0xbf49, 0x40ab, 0xb2f7, 0x4037, 0xb28e, 0x41fd, 0x0334, 0x1a33, 0x41ce, 0x462f, 0x41a4, 0x4181, 0x1ac3, 0xbf46, 0xb054, 0xaf5f, 0x41f6, 0x3343, 0x42e6, 0x4281, 0x0710, 0x0255, 0x41fa, 0x43ca, 0x1bf1, 0x4138, 0x2a43, 0x4388, 0xb0b1, 0xb2d3, 0x41ef, 0x43f1, 0x4090, 0x4195, 0x4151, 0x15b9, 0x1eb1, 0x1923, 0x1b16, 0x4352, 0xbfde, 0x42e7, 0x4130, 0xb248, 0x43a6, 0x460a, 0xbf39, 0x1a10, 0x4375, 0x4089, 0xbacc, 0x26f9, 0x4582, 0xbf6d, 0xb20a, 0xba36, 0x41c7, 0xb22a, 0xb05e, 0xba3f, 0x1ffe, 0x34c2, 0xb244, 0x2181, 0xb2f0, 0x3c08, 0x08a6, 0xba2d, 0x4221, 0x33fb, 0x4162, 0x439d, 0x43e9, 0x1eb8, 0x430e, 0x1f07, 0xbadb, 0xba6e, 0x02d2, 0xbf4e, 0xb294, 0xb2a7, 0xba6c, 0x41a4, 0x4647, 0xb231, 0x41c2, 0xb29e, 0x0648, 0x0ca8, 0x4273, 0x425d, 0xbf33, 0xa38e, 0xba5e, 0x407a, 0x1415, 0x3523, 0x4023, 0x2600, 0x4029, 0x426a, 0xb25a, 0xb2c1, 0x1b6e, 0x42aa, 0x4040, 0xb2bd, 0xab1b, 0x40de, 0xb05c, 0x427f, 0xbf2e, 0xb004, 0xba52, 0x1b44, 0x413d, 0x4147, 0x41a7, 0xbac9, 0xb27d, 0x1c52, 0x42aa, 0x1c6e, 0x43d7, 0xb04a, 0x4676, 0x1f18, 0x41fe, 0x42db, 0x4252, 0xbf4d, 0xb098, 0x3625, 0xb29f, 0x43c0, 0xa08e, 0xbf8c, 0x4317, 0xba78, 0x40ae, 0x3641, 0x42c7, 0xaf31, 0x40ae, 0x4360, 0x4066, 0x1a32, 0x4138, 0xba1f, 0xba21, 0x23aa, 0xba5f, 0x45e9, 0xba0c, 0xbfc6, 0xb2ad, 0x4389, 0x0aea, 0x0da6, 0x17ad, 0x1de4, 0x4247, 0x4040, 0xb2e0, 0x43d1, 0x1a71, 0xb088, 0xba62, 0x4034, 0x4077, 0x40dc, 0x1a7b, 0x05a4, 0xba3d, 0x4424, 0xbf55, 0xbfb0, 0x1408, 0x408c, 0x43c3, 0x42dd, 0x4464, 0xa818, 0x1161, 0x19ea, 0xb0ce, 0x24ce, 0xb2af, 0x15fa, 0x027b, 0xba01, 0xa369, 0x014f, 0x4036, 0x43ed, 0x40e5, 0x320e, 0x4214, 0xbaed, 0x275f, 0x400c, 0xba13, 0xa866, 0xbfb0, 0x41b7, 0xbfcd, 0x2c19, 0xbaea, 0x421e, 0xb296, 0xbf42, 0x42cc, 0x1726, 0xb08d, 0x4314, 0xbf87, 0x4107, 0x0425, 0x4586, 0x3b4d, 0x40c1, 0xbafc, 0xb0d5, 0xbf1e, 0x0b9a, 0xba4c, 0xb0e4, 0x3056, 0xbf80, 0xafd4, 0xb2fb, 0x4395, 0x4669, 0xb296, 0x1914, 0x4009, 0x2ac8, 0x4102, 0x4116, 0x40c1, 0x4130, 0x4493, 0x1bc0, 0xb250, 0x463b, 0x0469, 0x424b, 0x1b6b, 0x3e46, 0xb2fa, 0x4151, 0xbfac, 0x44d9, 0x1c7a, 0x1a00, 0x420b, 0xbaf5, 0x407e, 0xb21e, 0xad7b, 0x425c, 0xbf7a, 0x1db5, 0xae92, 0x1b35, 0x1ea1, 0x2cf5, 0x1af1, 0x4210, 0xbaf4, 0x42a0, 0x4124, 0x41dd, 0x4215, 0x13b4, 0x43e6, 0xbfa4, 0x416b, 0x1e6a, 0xbfc0, 0x0d58, 0x40c0, 0x454e, 0x0202, 0x40b5, 0xb276, 0x4333, 0x4574, 0xa2ac, 0x2a76, 0x425d, 0x40fc, 0x40da, 0xba47, 0x2428, 0xbf19, 0xb2a3, 0xb269, 0x2843, 0x3a89, 0x1f48, 0xbf7f, 0x416c, 0x4208, 0x4356, 0x43f2, 0xbae5, 0xb274, 0x4037, 0xb237, 0xaa79, 0x42d2, 0x1d34, 0x426f, 0xb236, 0x43a3, 0x4200, 0xbf60, 0x18b8, 0x1d7f, 0xbf8d, 0xb273, 0x4190, 0x3d1e, 0x415c, 0xba38, 0xbfc0, 0x13f8, 0x4117, 0xba3e, 0xb274, 0xbfb5, 0x4077, 0xb279, 0x4009, 0x3ac8, 0xaa7c, 0x0983, 0xbf8b, 0xba24, 0xb27b, 0x1e7e, 0xb0d1, 0x42c2, 0xaba6, 0xba50, 0x1eea, 0x056b, 0xa171, 0x43b3, 0x35ca, 0xb0e3, 0xb0c5, 0x08e0, 0xb22e, 0x414b, 0x41e6, 0x40bc, 0x2800, 0x402f, 0x409f, 0x164c, 0x44b3, 0x4154, 0x4158, 0x418a, 0x4028, 0xbf94, 0xb28b, 0xaff1, 0x41ec, 0x0d8d, 0x41a2, 0x1b3b, 0xaf95, 0xba4a, 0x1ee5, 0x40cd, 0x1840, 0xae03, 0xb0bc, 0x4643, 0x434f, 0x0c96, 0xba07, 0x3748, 0x46d8, 0xbf26, 0x4195, 0x0b00, 0x41ab, 0x4315, 0x405a, 0x4145, 0x14f4, 0xa261, 0x42dc, 0x3318, 0x424c, 0x1fdc, 0x0500, 0x1acf, 0xba4d, 0x1f49, 0x40c1, 0x1825, 0x42cf, 0x4175, 0x4097, 0x293b, 0xba0d, 0x4014, 0x207a, 0x407d, 0x4361, 0x437a, 0xbf4b, 0x4086, 0x41a0, 0x3c12, 0xb0ca, 0x460c, 0x4162, 0xb24d, 0x425e, 0xb2cd, 0x0dcb, 0x13ba, 0x40c2, 0x41af, 0x29cb, 0x0956, 0x41aa, 0xb216, 0x4112, 0x4349, 0x4431, 0x2734, 0x422e, 0xbf2a, 0x40b8, 0x4649, 0xb068, 0x438c, 0x426d, 0xabc5, 0xb0a7, 0xbf9c, 0x4671, 0x42e1, 0x43ee, 0xb02a, 0x4217, 0xb0ce, 0x4026, 0x4269, 0x4374, 0x3931, 0x02f5, 0x42c6, 0x43aa, 0x2532, 0x2c53, 0x4231, 0xb2dd, 0x43a9, 0x1c6d, 0x4274, 0x2948, 0x4162, 0xbfa3, 0x427f, 0xb01c, 0x0396, 0x4174, 0x1327, 0x36ab, 0x41f4, 0x41b6, 0x3fda, 0x4648, 0x4095, 0x1c98, 0x41a0, 0xbf56, 0x4144, 0xb0dd, 0xb28c, 0x42d5, 0x4280, 0xbf81, 0xaccd, 0x45ee, 0x4145, 0x4342, 0xbfb7, 0x4261, 0xba45, 0xb01f, 0x418a, 0x439f, 0x42bf, 0x43fc, 0xba22, 0xbfe0, 0x2446, 0xbfde, 0x1c19, 0xb0f5, 0xba68, 0x1b4a, 0x00d7, 0x4377, 0x425d, 0x46db, 0x411e, 0xb2b8, 0x406a, 0x1945, 0x43b9, 0xa423, 0x42af, 0xbf76, 0x412e, 0x1fa8, 0xb25f, 0x4591, 0xb27f, 0x461b, 0x41e3, 0x0cc4, 0x0236, 0x4157, 0x288b, 0x05d7, 0xbf87, 0xba66, 0x43bf, 0xba33, 0x18c4, 0x416c, 0xbacb, 0x0077, 0x398d, 0x414d, 0x1fc4, 0x447d, 0xbaef, 0xb2a8, 0x1833, 0xba42, 0xbf51, 0x41c6, 0x43ae, 0x420b, 0x2a87, 0x437e, 0x4206, 0x42ff, 0x4221, 0xa594, 0x2023, 0x4311, 0xb241, 0x416f, 0x40e2, 0x0418, 0x422b, 0x43c2, 0x4385, 0xbf81, 0xb284, 0x408d, 0x40ee, 0x4377, 0x43b3, 0x416d, 0x4353, 0x432b, 0x44a1, 0x43d3, 0xbf81, 0xbaf1, 0x40a6, 0x42c2, 0x0cf8, 0x4068, 0x1ff8, 0xb231, 0x41f9, 0x430f, 0x43a0, 0x1705, 0x0110, 0x09ac, 0xbace, 0x4230, 0x46d4, 0xaec3, 0x4353, 0x416e, 0xb20e, 0x43fc, 0xba7d, 0xbf17, 0x41b0, 0x33b5, 0xba1c, 0x4046, 0x400a, 0xbf8f, 0xb0bd, 0x42d2, 0xacb0, 0x1ddc, 0xb2f1, 0x40a3, 0x4082, 0x4318, 0x2d71, 0x19ec, 0xb0fd, 0x4336, 0xb2cd, 0x40cc, 0xaba4, 0x1a86, 0x42f0, 0x4668, 0x38ff, 0x1e05, 0x4040, 0x184a, 0x4015, 0xbf38, 0x4242, 0x4626, 0xad8d, 0x43c2, 0xb277, 0x4030, 0xbae2, 0x41a5, 0x4215, 0x1513, 0xbf6a, 0x4142, 0x43ba, 0x44f0, 0x4178, 0x2703, 0x428d, 0x42c0, 0x2ffb, 0x438b, 0x42cd, 0x40b5, 0x4435, 0x445d, 0xa385, 0x4082, 0x4148, 0xb2eb, 0x022f, 0x423b, 0x42f5, 0xbf5f, 0x04f0, 0xb098, 0x40a5, 0xba1f, 0x44c1, 0x3b57, 0x4094, 0x41ed, 0xb208, 0x3d9b, 0x0060, 0xb0d1, 0x4352, 0x2507, 0x4282, 0x4273, 0xa9fa, 0xa561, 0xb2b5, 0x3a86, 0x4076, 0xa2e9, 0xbfc3, 0x2a84, 0xbad6, 0xba74, 0x4361, 0x074c, 0x4169, 0xba42, 0xb086, 0xb070, 0xb23e, 0x3b35, 0x387f, 0x4303, 0x1fb5, 0x4267, 0xba3a, 0xbade, 0xbfa8, 0x41aa, 0x41e6, 0xbf80, 0x43fb, 0xbf90, 0x1deb, 0x43bc, 0x4019, 0xb21d, 0x409f, 0xb24c, 0x197a, 0x4264, 0xb2c1, 0xba06, 0xba7d, 0xb00e, 0x43e2, 0x4624, 0xabe7, 0x4348, 0x42b3, 0xbfc5, 0xb209, 0x40ef, 0x43e5, 0x3ada, 0x3431, 0x414a, 0x41de, 0x136c, 0xb01d, 0x413b, 0xba68, 0xbfa0, 0x1ecb, 0x402f, 0x43df, 0x43ca, 0x1de1, 0xbf34, 0x1d54, 0x2ffe, 0x43bf, 0x1dcb, 0x41fd, 0x438d, 0xb2c1, 0xbf8c, 0xaf57, 0x46d3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x83fd023f, 0x982a2ada, 0x932883b1, 0x8b07a16b, 0x6509d713, 0x387a315c, 0x306e7acf, 0x817d06d5, 0xc0872b3a, 0x0acac3f9, 0x3a05fe03, 0x78554f05, 0x868e89dd, 0x6a74d709, 0x00000000, 0x400001f0 }, FinalRegs = new uint[] { 0x80000000, 0x00000000, 0xffffff4a, 0x0000040e, 0xffffff4f, 0x00800000, 0x5a9b0180, 0x00000000, 0x78554db0, 0x8204e12c, 0x3a05fe03, 0x3a05fe03, 0x3a05fe03, 0x6a74d1d9, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x3ec5, 0x42d3, 0x06b4, 0xbf80, 0x430d, 0x40b3, 0x3a02, 0x43fd, 0x1ef3, 0x4051, 0x426b, 0xa108, 0xbf48, 0xba7c, 0xba08, 0x2831, 0xb055, 0xbad1, 0xb278, 0x0b30, 0xbff0, 0xa224, 0x1beb, 0x24d4, 0x1873, 0xb2c9, 0x44d9, 0x1eb9, 0x4086, 0xb2b6, 0x4172, 0x40e2, 0x1f7c, 0x403a, 0xbf9c, 0xa542, 0x19da, 0x429c, 0x42c9, 0x431d, 0x1d07, 0xb059, 0x3791, 0x306d, 0xa7cc, 0xb28a, 0x43c8, 0xbf62, 0xb02d, 0xba06, 0x1b78, 0x21ea, 0x41b6, 0xbfae, 0x0ca8, 0x1ae7, 0x4307, 0x39e3, 0x1e5e, 0x03f8, 0x04c8, 0x427d, 0x0676, 0x3256, 0x1b57, 0xb080, 0xb2cb, 0x418d, 0xb2ae, 0xbf51, 0x412f, 0xba3c, 0x1916, 0x4198, 0x40da, 0x3425, 0x43bb, 0x4339, 0xbf59, 0x42ff, 0x3058, 0x4108, 0xba09, 0x1c3b, 0x1f5b, 0x3703, 0x404a, 0x4259, 0x40b3, 0x40a8, 0x3e59, 0xa7ab, 0xbf66, 0x404f, 0x42bc, 0xbaf0, 0x22c3, 0xbfc0, 0xb03d, 0x4018, 0x1a56, 0x4383, 0x4446, 0x42c1, 0xb292, 0x246e, 0x4426, 0x1aec, 0x1fa7, 0xbf07, 0x3231, 0x4116, 0x1177, 0x45c6, 0x3f2b, 0xa87c, 0x4170, 0xba41, 0x436e, 0x4012, 0x4553, 0xadc9, 0xae57, 0x4034, 0xbaf0, 0xa094, 0x293a, 0x412d, 0x4145, 0xbfc7, 0xb0ed, 0x164e, 0x2376, 0x19ae, 0x404e, 0x338a, 0x42a5, 0xbad0, 0xb275, 0x4217, 0x40c3, 0x40a8, 0x4015, 0x1ce6, 0xb2f5, 0x435d, 0x403b, 0xaaed, 0x41e0, 0x1b07, 0x4390, 0x08eb, 0x2e05, 0x41c0, 0x193f, 0x421f, 0x2648, 0x4074, 0xa3c0, 0xbf0d, 0x0eb2, 0x29cd, 0x403b, 0xafe2, 0xba0a, 0xbff0, 0x40db, 0x1f18, 0xba01, 0xbf90, 0xb0e6, 0xb096, 0xbaf6, 0xbf90, 0x18a5, 0xbfbb, 0x40eb, 0x1d3b, 0x4150, 0x1993, 0xbf9d, 0x413c, 0xb2e0, 0x1b5d, 0x415a, 0xb244, 0x0152, 0xb2ed, 0x19ce, 0xba30, 0xba2e, 0xb277, 0xb271, 0x0387, 0xa74d, 0x4088, 0x4141, 0x4226, 0xbf5c, 0x46e2, 0x0897, 0x41c1, 0x428a, 0xb272, 0x1e00, 0x42d3, 0xb297, 0xba46, 0x40e1, 0x18fb, 0xbf0b, 0xba08, 0xb222, 0xbfb0, 0xbacf, 0x1a87, 0x428a, 0x42ee, 0x414c, 0x4606, 0x4375, 0x45db, 0x4171, 0x40f1, 0xb297, 0x4133, 0x3d66, 0x4206, 0x43ec, 0x43f8, 0xbf2e, 0xbfc0, 0xba27, 0x43ef, 0x407f, 0xbaef, 0x4306, 0x4350, 0x1e3c, 0x4085, 0xb25f, 0x4039, 0xb2dd, 0x40e1, 0x462b, 0x4633, 0x4048, 0x17ca, 0xba44, 0xbfa3, 0x4208, 0xb282, 0xba26, 0xb2da, 0x00d2, 0x4151, 0x40f7, 0x42e9, 0x4203, 0xba5a, 0x203f, 0x465b, 0xb20a, 0xbfdc, 0xa24b, 0x2188, 0x144b, 0xbfc0, 0x4001, 0xbf74, 0x41c4, 0x15cf, 0x42fc, 0x4269, 0xaad7, 0x4353, 0x308d, 0xb27f, 0x4242, 0x4276, 0x43b6, 0x4243, 0xa999, 0xba34, 0xa8cb, 0xbfe8, 0xb0e3, 0x40c0, 0x402e, 0xb22d, 0xb06b, 0x1b53, 0xb0bb, 0x447f, 0x459a, 0x4083, 0x31f0, 0xb018, 0x3814, 0x43e1, 0x1c61, 0x417d, 0xa3ab, 0xbac9, 0x0d83, 0xa3e9, 0xbfcb, 0x1c34, 0x0847, 0x012c, 0x34c5, 0x412f, 0xb21c, 0x4286, 0xb24c, 0x40eb, 0x1dc0, 0x4006, 0x437d, 0xb01b, 0xbf45, 0x38bb, 0x4092, 0x41ab, 0xb283, 0x424c, 0xb036, 0xa4ec, 0x1f66, 0x1f00, 0xbac8, 0xbf8a, 0x42d7, 0x42aa, 0x4140, 0x09ec, 0x183a, 0x1e06, 0x405c, 0xb05b, 0xb0ef, 0x1d87, 0x1807, 0x41bf, 0x411c, 0x4168, 0xbf19, 0xb050, 0xad8f, 0x1cd4, 0x0d12, 0x411f, 0x4071, 0x187d, 0xa451, 0x4354, 0x4170, 0x17d0, 0x419b, 0x41ca, 0x4293, 0x1d2e, 0x41e0, 0x4131, 0xba25, 0xbf4c, 0x4313, 0x22f1, 0xbf91, 0x4601, 0xb2d8, 0x39d6, 0xb2d3, 0x1e51, 0x46c0, 0xba66, 0x2408, 0xb0aa, 0xb0b7, 0xbf5d, 0xbfc0, 0x438d, 0x24bb, 0x23a9, 0x4285, 0x1d19, 0x431b, 0xa1e4, 0x43b2, 0x1015, 0xbad8, 0x408d, 0x40c5, 0x401d, 0xbf77, 0xb0a3, 0x40d9, 0x3dd4, 0x4291, 0xb2ad, 0x4611, 0x4243, 0x42d3, 0x4274, 0x430f, 0x1f8f, 0x4458, 0xbf43, 0xbaf1, 0xb05f, 0xba34, 0xb2bd, 0xb2f2, 0xa7ef, 0x4368, 0xb2b9, 0xbfae, 0xbf00, 0xbad8, 0xbaf8, 0x40f2, 0x403e, 0xbf3d, 0xbaff, 0x4352, 0xb0b8, 0x414a, 0xa4e6, 0x1278, 0x419d, 0x41be, 0x1ef6, 0x4611, 0xb240, 0x4300, 0x4151, 0xba3a, 0x2800, 0x40c0, 0x40f1, 0x433b, 0x1e6b, 0xbf83, 0x40e0, 0x43cc, 0xb089, 0x42ff, 0xafd5, 0x4288, 0x1bd6, 0xbfc0, 0xb2e9, 0x403b, 0x40e1, 0x1ff6, 0x408d, 0xb284, 0x40f6, 0x41eb, 0x1c1c, 0x411c, 0x439d, 0x4546, 0x418d, 0xb288, 0x4026, 0x31bb, 0xbfb9, 0xb032, 0x421e, 0x0492, 0x4349, 0xac8e, 0xb272, 0x40d2, 0x4017, 0xbf90, 0xbf00, 0x2e71, 0xba43, 0x421b, 0x4094, 0x4223, 0xbf49, 0xb2a3, 0xb286, 0x02b5, 0x424a, 0x426f, 0xb21c, 0xb277, 0x4137, 0x43fe, 0xbf4d, 0x418a, 0xba41, 0x05a2, 0x4005, 0xbf34, 0x4591, 0xb209, 0xba3e, 0x418c, 0x4190, 0xb2ab, 0x4243, 0x04ac, 0x40b1, 0x3fd9, 0xbfcf, 0x1bd4, 0xb2b3, 0x42ba, 0xb0e0, 0x1cf2, 0x3fc6, 0x042d, 0xbf26, 0xba72, 0xa972, 0x018f, 0x42e3, 0xab4b, 0xb253, 0x40a4, 0x43bc, 0xbad4, 0x444a, 0xb298, 0x4095, 0x05a4, 0x3e20, 0x41dc, 0xba4d, 0xb29a, 0x4210, 0xba2d, 0x40e4, 0x3b94, 0xbfe0, 0x405d, 0x43cc, 0x4282, 0xbfca, 0x4296, 0x41d1, 0x4319, 0xba33, 0x417f, 0xbfd9, 0xb24d, 0xa08c, 0x405c, 0x4397, 0x4201, 0xb2ff, 0x2561, 0x00f1, 0x3ee1, 0x43d3, 0x4177, 0xb289, 0x407f, 0xbacb, 0x30e3, 0x37a9, 0xba34, 0x432c, 0x43d7, 0x4017, 0x428b, 0xa7e4, 0x42be, 0xbf3c, 0xba26, 0xb25c, 0x414c, 0x3494, 0xaeb7, 0x42f3, 0x1325, 0xbf96, 0x089b, 0xbf60, 0x4147, 0x0189, 0x42fc, 0xbfa4, 0x4137, 0xbac5, 0x1f1c, 0x42ea, 0xb2a0, 0x39d2, 0x1068, 0xb2a7, 0x4371, 0x4086, 0xbad1, 0x418e, 0x211d, 0x1284, 0x0ab4, 0x359e, 0x4161, 0x1eab, 0x0245, 0x430b, 0x42ea, 0xba51, 0x4082, 0x43a9, 0x03f5, 0xa5cb, 0x1316, 0xbf36, 0x4611, 0xbaee, 0xba0a, 0xbfd2, 0x409c, 0xb091, 0x43b3, 0x4248, 0x426e, 0xba02, 0x0db8, 0x1a5b, 0xbfb5, 0x43b0, 0xb2ad, 0x09aa, 0x1205, 0xb266, 0x415b, 0x434d, 0x1fe3, 0xb0a3, 0xbf93, 0x4559, 0x4439, 0x212b, 0x3af6, 0x189c, 0x1be0, 0x4248, 0xb285, 0xbf96, 0x41c3, 0xb254, 0x0c55, 0x4274, 0x3ed3, 0x431c, 0x407a, 0x43fe, 0x1e2d, 0x18a3, 0x4126, 0x42ad, 0x06fe, 0x29b3, 0x144b, 0x4051, 0x4114, 0x1f76, 0x1e83, 0x1855, 0xbadb, 0xbf34, 0xb291, 0xb2d4, 0xb253, 0x414e, 0xb282, 0xba7a, 0x41f3, 0xba13, 0xb20a, 0xbf36, 0x428d, 0x1d7b, 0xbad0, 0xb2bf, 0xb254, 0xb0a6, 0xb078, 0x4053, 0x43d9, 0x4162, 0xb274, 0x41fd, 0x184a, 0x4399, 0x2dd4, 0x4307, 0x40f4, 0xba36, 0x43c2, 0x4197, 0x436c, 0x4203, 0x1fbe, 0xbf31, 0x408d, 0xb2ee, 0x4226, 0x414e, 0xbf3a, 0x23bf, 0x46c9, 0x3892, 0xba42, 0x41b5, 0x1b55, 0x4282, 0x401d, 0x1d0c, 0x1a9c, 0x257a, 0xb2b4, 0x42c1, 0x4201, 0xb23b, 0x43a1, 0x43bd, 0x407e, 0x43d6, 0x1b28, 0x13fb, 0x4064, 0x1cb2, 0x314e, 0x2ae2, 0xba18, 0xbf0b, 0xa9ec, 0x4165, 0x43d1, 0xb2ab, 0x1879, 0xb0fe, 0xb290, 0x380d, 0xb22c, 0x1d2e, 0x17c1, 0x4011, 0x43f2, 0xbf87, 0xba26, 0xb293, 0x1854, 0x34a5, 0xb2f5, 0x43c1, 0x2ea9, 0xa962, 0xb266, 0x0e46, 0xb055, 0x412d, 0x43e8, 0x419a, 0xb23e, 0x1b36, 0x180b, 0x434f, 0x1a50, 0xbac0, 0xbaf0, 0x09f6, 0x18d9, 0xba51, 0xb2ee, 0x32fe, 0x403b, 0x1db0, 0x0fa6, 0xbf97, 0xb26b, 0x4315, 0x42a2, 0xba2a, 0x431b, 0x1a42, 0x1f3a, 0xbf4e, 0x40cc, 0x41da, 0x4109, 0x422d, 0x1bd6, 0x439f, 0xb203, 0xbfd1, 0x462d, 0x404b, 0x420b, 0x4298, 0x153f, 0xb0c7, 0xba66, 0xb258, 0xb27a, 0x417e, 0x1ca0, 0x46f9, 0x0c87, 0x401d, 0x42e3, 0xbacc, 0x2a1c, 0x21ff, 0x4187, 0xb001, 0x4054, 0xbae9, 0xbf98, 0x411c, 0xb27f, 0x2f39, 0x42cc, 0x4602, 0xb07f, 0xb21b, 0x418b, 0x03f0, 0xb021, 0xb03f, 0xbf5a, 0x410c, 0x40ac, 0xa162, 0x4021, 0xba5b, 0xa977, 0x204c, 0x43f6, 0x40a9, 0x1e9c, 0x4283, 0xbf2d, 0x2abd, 0x4249, 0xba02, 0xa9c6, 0x4008, 0x4424, 0x2c94, 0xbf61, 0x18ae, 0xb07d, 0x46c3, 0x185f, 0xb286, 0xb2b7, 0x1ada, 0x4147, 0x403a, 0x2702, 0x4065, 0x404c, 0x4274, 0x42aa, 0x434f, 0xb0a5, 0x4315, 0x0ac8, 0x19eb, 0xa9ea, 0x2541, 0x460f, 0x4634, 0xbf25, 0x436c, 0x42a5, 0xb256, 0x425f, 0xb20d, 0x4225, 0x4669, 0x23f8, 0xab09, 0x43c7, 0x34cf, 0x0f49, 0x415d, 0xba25, 0x4230, 0xac19, 0xb243, 0x42d7, 0x42e5, 0x182d, 0x424d, 0x4213, 0xba2c, 0x1ad0, 0x4301, 0x2446, 0x2b9e, 0xabb5, 0x426d, 0xbfad, 0xa4d2, 0x41c1, 0x1de8, 0x28af, 0xb252, 0x18f0, 0x342f, 0x2d6e, 0xb240, 0x427e, 0xbf5a, 0x41c0, 0x446a, 0x1a36, 0xb223, 0x1b1a, 0x4319, 0xba13, 0x4332, 0x4293, 0x1cc3, 0xb092, 0x4339, 0x41b6, 0x21e9, 0xb237, 0xbaeb, 0xbf06, 0x1d64, 0xa016, 0x41ec, 0x413a, 0x1abc, 0x42fc, 0xb274, 0xb0bc, 0x4016, 0x42d6, 0xb220, 0x43b0, 0x44fa, 0x44cb, 0xbf1c, 0x1ed5, 0x4229, 0x45f4, 0xbf67, 0x4142, 0x1e8e, 0x434c, 0x418e, 0xb0fb, 0x4369, 0x46b2, 0xae1c, 0xbfa6, 0xb294, 0xba4c, 0x0ff8, 0x149e, 0x4201, 0xba72, 0x44a9, 0x465f, 0x42c9, 0xbad2, 0x3fc6, 0x4379, 0x1969, 0x417e, 0x409b, 0x46e8, 0x40a2, 0xba58, 0x1c75, 0x42f0, 0x40ba, 0x12ae, 0x32c5, 0x348d, 0x402a, 0x2812, 0x40e9, 0x41b1, 0xbf89, 0x1914, 0x41ac, 0x07c5, 0x406e, 0x43a2, 0x42f0, 0x1150, 0x3395, 0x3055, 0xbfb6, 0x00f6, 0x40d4, 0x1c99, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x12c1cca9, 0x2d72bbb5, 0x1bcf8ad0, 0x4c840915, 0x42730230, 0xb7a8e9c4, 0xcb0a2f1b, 0xf9eb0e6a, 0x3ce2afc4, 0x2182885a, 0x1ed96c99, 0x0cf55f95, 0xb8e6e008, 0xecaa7d5b, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x00000055, 0xfffcc2a2, 0x00000001, 0x00000795, 0x79854540, 0x0cf57523, 0x00033d5d, 0x0cf57521, 0xecaa795b, 0x0000164f, 0xffffff17, 0x0cf575e7, 0xb8e6e008, 0xecaa795b, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4393, 0x413f, 0x4166, 0xa433, 0x409c, 0xb2e8, 0x1cc5, 0xba5a, 0xaf1d, 0x4249, 0x158a, 0x400c, 0xbf02, 0xba4d, 0x1b2a, 0x0683, 0x409b, 0x4033, 0x2766, 0xbf00, 0x43bc, 0x4593, 0x4623, 0x40f0, 0xba2d, 0x433c, 0xbfcc, 0x0712, 0xa0ee, 0x301a, 0x400e, 0x4218, 0x428a, 0xa5a0, 0x4362, 0xb28f, 0x4270, 0x3fd6, 0x40b4, 0x2c32, 0x4494, 0xb0e0, 0xba73, 0x2cfd, 0x4118, 0xba2a, 0xb24c, 0x2d2c, 0x1a18, 0x00d8, 0xbacb, 0x4396, 0xbf0b, 0xb244, 0x1eb5, 0x4154, 0xbacc, 0xb258, 0x1b71, 0xbfb7, 0xb2f8, 0xb0c5, 0x01d6, 0xbad7, 0x240f, 0x1dd7, 0x42f5, 0x4691, 0xbfb0, 0x1ca2, 0x15b5, 0x3be3, 0x4127, 0x4122, 0x403e, 0x4065, 0x4314, 0x1a38, 0x1e93, 0xba0d, 0x1ea0, 0xbaf7, 0xa3e8, 0x42c5, 0x1f42, 0xbf1b, 0x40c5, 0x461d, 0xb013, 0x40a2, 0x1903, 0x4388, 0xb090, 0xbfc1, 0x42fe, 0x42a9, 0x4353, 0x41c2, 0xba5c, 0x406b, 0x4351, 0x41ba, 0xbfe0, 0xb254, 0x1057, 0x41ba, 0xba06, 0xb259, 0x067d, 0x40f4, 0xba18, 0x45b0, 0x3200, 0x405a, 0xba03, 0xbfde, 0x4127, 0x4379, 0x400c, 0xb20e, 0x0f5d, 0x41b4, 0x1f75, 0x416d, 0x413c, 0x1d18, 0x3c86, 0x41e6, 0x1d1e, 0xb281, 0x1f3c, 0x4218, 0x40f8, 0xbf29, 0x286f, 0xa620, 0xb2bb, 0x0150, 0xbac4, 0xac02, 0x439c, 0xb0fa, 0x40df, 0x1f98, 0x4316, 0x3d6d, 0x414e, 0x4159, 0xba17, 0xbfb1, 0x1a85, 0x4214, 0x423d, 0x0134, 0x4669, 0xb254, 0xbf95, 0xb2ed, 0x428b, 0x4328, 0x4256, 0x12d9, 0x40cc, 0xb23e, 0xb025, 0x42fe, 0x4022, 0xb0da, 0x42a9, 0xad61, 0x1f53, 0x46f9, 0x4462, 0xba7b, 0x3985, 0x4142, 0xa656, 0x21c4, 0x4255, 0xba64, 0xb20b, 0xa593, 0x43be, 0xbf8f, 0x463b, 0xb038, 0xb030, 0x4055, 0x17ca, 0xbacb, 0x1f67, 0x4055, 0x1923, 0x1a46, 0xb2a0, 0x41af, 0x4285, 0xbfa4, 0x3160, 0x42c8, 0x29ec, 0x1c50, 0x412c, 0xbaec, 0xb225, 0x44d4, 0xba56, 0xb23d, 0xbfdb, 0x275b, 0x415f, 0xb0ea, 0x21fb, 0x40d9, 0x4325, 0xb20c, 0x43d8, 0xa916, 0x4239, 0x438b, 0x418f, 0x4275, 0xb25f, 0x2252, 0xb262, 0x4688, 0x1c27, 0xb234, 0x19e4, 0x41b6, 0xbfb4, 0x3226, 0xa8bd, 0x40b1, 0xb24f, 0x1848, 0x436f, 0x4372, 0x436f, 0x426f, 0xb2b6, 0xba5a, 0xb2c6, 0x2bac, 0x4160, 0x4002, 0x40b0, 0xba46, 0x319b, 0xb251, 0x2a9a, 0x428b, 0x4239, 0x28d5, 0x46e9, 0x4030, 0x4167, 0xbf2e, 0xbf00, 0x41cc, 0xb2e1, 0xba20, 0x4080, 0x1179, 0x4484, 0xbaf4, 0xbf48, 0xb2f8, 0x4320, 0x426b, 0xb2ee, 0x350c, 0xb241, 0x4386, 0x3af4, 0x42b4, 0xa8e5, 0x4369, 0x4315, 0x4110, 0x407b, 0xbaff, 0x419e, 0xbf52, 0x40f2, 0x45b5, 0x41ec, 0x402a, 0xb21b, 0xbf6c, 0xb0ad, 0x40cd, 0x4094, 0xb07c, 0x1fbb, 0x286b, 0x422d, 0x1f2b, 0x401a, 0x42aa, 0x406b, 0x4473, 0x41d8, 0xb08d, 0xba59, 0xb03e, 0x1d7d, 0xaf13, 0x45e8, 0x4169, 0x418a, 0x4366, 0xb001, 0x3913, 0x40ef, 0x40fa, 0xbf74, 0x1bcd, 0x42e7, 0x40cf, 0x1959, 0xb20a, 0xb2ec, 0x1261, 0x40ab, 0x4062, 0x34eb, 0x40fb, 0x4255, 0xb052, 0x40e4, 0x414f, 0x4241, 0xb21f, 0xb2f9, 0x2894, 0x4546, 0x2e67, 0xbf0a, 0x2dcd, 0x3df4, 0x1431, 0x426d, 0x162d, 0xbfd8, 0x4255, 0xbaf2, 0x4263, 0xa5ca, 0x43e9, 0xb06b, 0x2b34, 0xba1b, 0xbf60, 0x42bc, 0x0a25, 0x4584, 0x40f2, 0xb07f, 0x3185, 0xb0b6, 0xbaf2, 0xbf86, 0xafbf, 0xb2a6, 0x10ea, 0x4379, 0x43ec, 0x4291, 0x2f80, 0xb2a8, 0x400b, 0xb2a7, 0x4007, 0xb23c, 0x454b, 0x40ab, 0xbad0, 0xbf84, 0x43ab, 0x4252, 0x40c8, 0x400f, 0x43ae, 0xa451, 0x458e, 0xbae0, 0x4683, 0x412d, 0x3ea7, 0x2233, 0x4564, 0x3aa8, 0x421a, 0x1fc8, 0xa2ab, 0xba49, 0x18b2, 0x2a32, 0x40b7, 0xb25e, 0xba1f, 0xb025, 0xb2d7, 0x41f2, 0xbfe1, 0x1fa1, 0xb0a2, 0x43bd, 0x1812, 0x0fdf, 0xba2f, 0x42e5, 0xb238, 0x197b, 0x1d3f, 0xbfa1, 0x1def, 0x1a08, 0x41f5, 0x352c, 0xb021, 0x1891, 0x004f, 0x427c, 0x402a, 0x41fb, 0x4303, 0x4396, 0x3d52, 0xbac4, 0xba78, 0x40b0, 0x1d86, 0xb249, 0x40c1, 0x4030, 0xbac9, 0xbf3a, 0x412d, 0x4335, 0x3fe3, 0xb2f3, 0x0403, 0xb24c, 0x4194, 0x402f, 0x4245, 0x43a7, 0x4298, 0x2c2c, 0x1d25, 0x0a9b, 0xba72, 0x41f4, 0x4083, 0x1161, 0xba17, 0xac8c, 0x4595, 0x42b2, 0x4457, 0x2b24, 0xba1b, 0x4135, 0xb0ba, 0xbf14, 0x41e8, 0x2741, 0x1bcb, 0x3afa, 0x438c, 0xbf36, 0x4028, 0x40d6, 0x42dd, 0xb2cf, 0x4334, 0x4390, 0x41f0, 0xbf8d, 0x4576, 0x2bc5, 0x46b2, 0xa44d, 0x438e, 0x2439, 0xb2c1, 0x181c, 0xa1d4, 0x4448, 0xbf67, 0x4360, 0xb00a, 0xbfa0, 0x423f, 0x32f8, 0x3dbe, 0x4301, 0x43aa, 0x1ba3, 0x4258, 0xbf80, 0x41df, 0xb242, 0x1e9a, 0x0f0a, 0x389a, 0xb2fa, 0x4220, 0x401f, 0x4561, 0x1a77, 0xbf5b, 0xb2e7, 0x1879, 0x4132, 0x46db, 0xb2e1, 0x1aa2, 0x4100, 0x25d0, 0x403b, 0x13ba, 0xbf69, 0x4238, 0xba3f, 0xa5ad, 0x1b87, 0x4406, 0xa157, 0x006d, 0xbfc0, 0x4349, 0x16fb, 0x18ca, 0x406f, 0xb049, 0x43cb, 0x435e, 0xabbe, 0xba66, 0xbf8f, 0x29de, 0xbfe0, 0x35d1, 0x21e8, 0xb2b1, 0x4019, 0x1f16, 0xb07f, 0x4124, 0x437b, 0x324a, 0x4641, 0x4192, 0x40cb, 0xbf5b, 0xb257, 0x1a8b, 0x0f49, 0x0eeb, 0x4244, 0x4038, 0x4499, 0x43ce, 0xbf18, 0x1b06, 0x1fb0, 0xbfa0, 0x2f5c, 0x44dd, 0x4381, 0x4245, 0xb223, 0x4260, 0x1c09, 0x12f5, 0x0433, 0x4285, 0xbf48, 0x43fa, 0x05a8, 0x3557, 0x42ed, 0xb03e, 0xba76, 0x4124, 0x1ab2, 0xbf95, 0x4010, 0x4581, 0x459a, 0xb229, 0x0b3c, 0x4452, 0xa755, 0x4476, 0xb066, 0x13df, 0xb0b4, 0x41f9, 0xb2ac, 0x432a, 0x409f, 0x2410, 0xba48, 0xbade, 0x4291, 0x085f, 0xb2dd, 0x4346, 0xbfc6, 0x4069, 0x408a, 0x45ed, 0x4370, 0x2b4f, 0x463d, 0x1f7a, 0xba33, 0x4119, 0x4119, 0x4277, 0x08b3, 0x24d8, 0x018b, 0x1dd1, 0x4190, 0xb259, 0x408e, 0xb094, 0x434b, 0xa16e, 0x417b, 0xb28c, 0xbf92, 0x1ae1, 0x14f3, 0x1b4a, 0x4549, 0xbf48, 0x3edd, 0x0f59, 0x3f27, 0x18da, 0xb2ee, 0x412b, 0xa606, 0xba3a, 0xb236, 0x41c3, 0xb024, 0xb0d3, 0x40f2, 0x4301, 0xb0eb, 0x40a1, 0x284b, 0x40bd, 0x1e28, 0x41d3, 0x4243, 0xbfca, 0xa24e, 0x4105, 0x0a58, 0x1fd6, 0x41f4, 0xbfc8, 0xb268, 0x42ac, 0x413b, 0xb285, 0xb0d4, 0x400b, 0x43e2, 0x3133, 0x4316, 0x4319, 0x4398, 0xb27b, 0x2780, 0x4343, 0x17d6, 0xb242, 0x1282, 0x1f3e, 0x4285, 0x42a2, 0xb0ed, 0x40a5, 0x439a, 0x1c5f, 0x1d3b, 0xb032, 0xbfbd, 0xb26f, 0x42e2, 0x4350, 0x4353, 0x1d6d, 0xb2af, 0x40ad, 0x41e6, 0x4080, 0x43b6, 0x4064, 0x4007, 0x40a8, 0x4302, 0x11e1, 0x41dd, 0xb2cd, 0x4350, 0xba24, 0x4208, 0xb24d, 0x1c89, 0x4262, 0xba71, 0xbf89, 0x4357, 0x42ef, 0xb2d6, 0xb26e, 0xb208, 0xb275, 0xbf1d, 0xb2ee, 0x1944, 0xb2ce, 0xba7f, 0x36b1, 0x388f, 0x40ce, 0x4368, 0x19b9, 0x01c7, 0x24f5, 0xba7b, 0x4267, 0x4143, 0xbf32, 0x4205, 0xba59, 0x40c0, 0x3dbe, 0x46d5, 0x1ac7, 0x4195, 0x41db, 0xb2bb, 0x43c4, 0x1fcd, 0x42f1, 0x42d8, 0x1df1, 0xb2fa, 0x41d5, 0xbf0c, 0x41a8, 0x4201, 0xa7e8, 0xbf02, 0x42d1, 0x4116, 0x1c10, 0x43f6, 0x40df, 0x0a6b, 0x41ac, 0x42f3, 0xb2b6, 0x43bb, 0x1010, 0xba5d, 0x43c6, 0xb022, 0x3144, 0x41f9, 0xac9e, 0x43b2, 0x3daf, 0xa1b3, 0x19fa, 0xbfae, 0x42b3, 0x467d, 0x4000, 0xaa59, 0xba2c, 0x090f, 0x1860, 0xb022, 0x2209, 0x1d3a, 0xba15, 0x4138, 0x43fb, 0x424b, 0xbaf1, 0x41fd, 0xba49, 0x461b, 0x41b4, 0xae22, 0xa412, 0x1d38, 0xbf4a, 0x4239, 0xb25f, 0x258a, 0xab14, 0x4021, 0x33b7, 0x400d, 0x44eb, 0x439c, 0x1ff6, 0x4056, 0x414b, 0x18e1, 0x34b5, 0xb23e, 0x465d, 0xa16f, 0x41a9, 0x1047, 0x2a0d, 0x290c, 0xa552, 0x403b, 0xbf43, 0x46d2, 0xb07c, 0x42ca, 0xb007, 0xbae7, 0x45e5, 0x41aa, 0xa900, 0x4148, 0xb203, 0x411a, 0x44d0, 0xb236, 0x40bf, 0x2b7a, 0xbfd1, 0x423c, 0x4323, 0xba6d, 0x4325, 0x45b0, 0x4173, 0xbf46, 0xbada, 0x430e, 0x42d7, 0x415f, 0x4335, 0x445e, 0x4038, 0x4170, 0x4041, 0x422f, 0x4206, 0x4306, 0xba01, 0xb080, 0x0116, 0x1e6f, 0x31c7, 0xb210, 0xba4a, 0xb26a, 0xb2a4, 0x3127, 0x4140, 0xafb4, 0xb28c, 0xb279, 0xbf31, 0x4456, 0x40c0, 0x344c, 0x43eb, 0x0763, 0x042b, 0x4160, 0x0f4f, 0x4484, 0xa6f6, 0x4115, 0x436f, 0x3b87, 0xb283, 0x39a6, 0x4013, 0x2aca, 0x414d, 0x4008, 0xb29b, 0xbfe4, 0x466c, 0x43db, 0x42c5, 0xbac7, 0x22cb, 0x4678, 0x4017, 0xb218, 0x1bfe, 0x43a6, 0xb2e2, 0x1b61, 0x4423, 0x2a87, 0x430c, 0x1d9a, 0xb206, 0x425d, 0xb21c, 0xbaeb, 0x1d87, 0x4123, 0xb030, 0xbfc8, 0xb25a, 0xa621, 0x16b0, 0xba19, 0x411f, 0x43b7, 0x26fe, 0x41c8, 0x419f, 0x40cc, 0xa286, 0xba7d, 0xbf4e, 0xb2a6, 0xb21e, 0x19e8, 0x22f9, 0x43e2, 0x1b9a, 0xb0d5, 0xbae9, 0x403a, 0xb2d7, 0x420a, 0x4345, 0xbfd8, 0x434e, 0x2382, 0x40dc, 0xb2dc, 0x1efb, 0xbfaf, 0xbfb0, 0xbaed, 0xbaea, 0x4249, 0x1806, 0x4459, 0xba0d, 0x1be8, 0xb2c6, 0x4346, 0x4302, 0x1483, 0x36d3, 0xb25d, 0x1e58, 0x0383, 0x42e7, 0xb0c2, 0x2be8, 0x1172, 0x1f26, 0x40d3, 0x1c1f, 0x42f0, 0x0b5c, 0xbf41, 0xa6e6, 0x4318, 0x3b45, 0x3690, 0x43c9, 0x40c7, 0x43c9, 0xbf38, 0x44f5, 0xbf00, 0x1d4e, 0xb055, 0x40d0, 0x298a, 0x4352, 0xba74, 0xa308, 0x1c72, 0x44f4, 0xb28c, 0xba4a, 0x438c, 0x4208, 0x1ce7, 0x4581, 0xbf93, 0x445f, 0x4041, 0xb06f, 0x40e4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xd71e5741, 0xc26ca9a0, 0xc815b4b8, 0x2e87799c, 0xd6d7ab03, 0x28643399, 0xee47b812, 0x4d2e6ed2, 0xb0352da7, 0xaedffe24, 0x03886f34, 0xf8a78ddb, 0x3400fdbb, 0x65c78639, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0x00000000, 0x0388d025, 0x880325d0, 0x000017f8, 0x00000000, 0x00000074, 0x0388d02a, 0x00000003, 0x694ff1d5, 0x65c78249, 0x03886f34, 0x0388b858, 0x7789f72c, 0x038871d4, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x45a4, 0x08a7, 0xa344, 0xba0d, 0xba6a, 0x18aa, 0xb2ed, 0x027f, 0x414e, 0x3b58, 0xb06c, 0x4019, 0x443a, 0x43d5, 0x43d9, 0x3988, 0xb2fb, 0x3731, 0x42e1, 0x4198, 0xbade, 0x1852, 0x414d, 0xb2bf, 0xb272, 0xbfb3, 0x4083, 0x42cb, 0x4084, 0x023d, 0x4218, 0xba3e, 0x434c, 0xb2bb, 0x41ee, 0x1d8f, 0xbf08, 0xb2d7, 0x1ade, 0x20d7, 0x0a8f, 0x4679, 0xba77, 0x4156, 0x44c8, 0xbf0f, 0xba07, 0xb234, 0x4326, 0xb018, 0xb28c, 0x159a, 0xb050, 0x442f, 0xa0b3, 0xb0b2, 0x1cae, 0xbfc3, 0x42bf, 0xb23d, 0x4296, 0x409b, 0x46fa, 0x40c7, 0x41a1, 0xbf0f, 0x44f9, 0x28e2, 0x3304, 0x41f9, 0xbf00, 0x462c, 0x4233, 0x1f2f, 0xbad2, 0x2961, 0x2bad, 0xb0ab, 0xbf60, 0x40d6, 0x1633, 0x4280, 0xbf8f, 0x445a, 0x4398, 0x42e2, 0x40f3, 0xb05e, 0x1e98, 0x467e, 0x11ff, 0x1a98, 0x42c5, 0x4038, 0xb223, 0x1e66, 0xbae3, 0x1dcf, 0x23d7, 0xbfd1, 0x4431, 0x43ee, 0x2d7b, 0x4275, 0x07dc, 0x4320, 0x41ea, 0xb25b, 0x0fe5, 0x4131, 0x4108, 0x4338, 0xba61, 0x42d0, 0x0f3d, 0x1a3d, 0x1c5e, 0x4244, 0x40e1, 0xb094, 0xb207, 0x4499, 0xbf6e, 0xb209, 0x41a2, 0x37d8, 0xb018, 0xa0e3, 0xb217, 0x4626, 0x4466, 0x15ef, 0x433b, 0x1cf5, 0x0846, 0x191b, 0xb203, 0x4396, 0x4217, 0xbf69, 0xb090, 0x436a, 0x1b63, 0x426f, 0x0022, 0x4110, 0x2019, 0xb26d, 0x2962, 0xaf0d, 0x43d9, 0xb246, 0x085b, 0xb049, 0x43df, 0x412e, 0x42a5, 0x027f, 0xbf89, 0xb237, 0x42ae, 0x42a7, 0x36db, 0xb25a, 0x43cf, 0x1d28, 0xb0ca, 0x404e, 0xb2a9, 0x402d, 0x4268, 0xad94, 0xbf32, 0x2cef, 0x40c2, 0xba2e, 0x43fd, 0x4168, 0x43d1, 0xb2a8, 0xba30, 0x1b80, 0x40dc, 0x4035, 0x4676, 0xb08c, 0x4267, 0x409b, 0x462f, 0x408d, 0x3b28, 0x18fc, 0x4344, 0x4016, 0x42b4, 0x41a1, 0xbfdb, 0x4156, 0xa1fb, 0xb2b5, 0x1860, 0xb2aa, 0x033d, 0x42cf, 0x4399, 0x430f, 0x410f, 0xba6d, 0xbf51, 0x41fe, 0x2855, 0x45c1, 0x1740, 0x4321, 0xa3e5, 0x422e, 0x4289, 0x01e5, 0x14b8, 0xaef7, 0x4012, 0x1c72, 0x4261, 0xa856, 0x069e, 0xba61, 0x40c1, 0x0edc, 0x4040, 0xba69, 0x4012, 0x4157, 0xba5b, 0x45d6, 0xba32, 0xb000, 0x1efb, 0x4351, 0xbf36, 0x4548, 0xb0b2, 0x344a, 0xba1b, 0x1ff8, 0x4104, 0x43c9, 0xac71, 0x3da1, 0xb095, 0x423a, 0x410a, 0x43c1, 0x10b8, 0x431c, 0x42c6, 0x4100, 0xbfda, 0x3ae2, 0x46d0, 0x4241, 0xbae3, 0x0dba, 0x4297, 0xaa48, 0x286a, 0x42b0, 0x43d8, 0x00e7, 0x10ae, 0x0281, 0xb013, 0x4556, 0x1b24, 0x4093, 0x2ab6, 0x40cd, 0x41aa, 0x193f, 0x2446, 0x4344, 0x4161, 0xb2e5, 0x42e9, 0xbfb4, 0x25e1, 0x40c6, 0x4320, 0x4214, 0x4185, 0x076d, 0xb273, 0x4149, 0x0f14, 0x43bd, 0x109a, 0x4636, 0x411c, 0x43f5, 0xbfe1, 0x1ed8, 0x437e, 0x40b7, 0x0795, 0x1f94, 0x4678, 0x00c4, 0x0040, 0x1f3a, 0xbaef, 0x096f, 0x3ce4, 0x4061, 0x4241, 0x42ca, 0xb250, 0x458e, 0x308f, 0x45ba, 0x41f6, 0xb22d, 0xb0a2, 0xbaf1, 0xbfb6, 0x0318, 0x0f57, 0xac8a, 0xb2bf, 0xbfd0, 0x438d, 0x1a76, 0x4447, 0x42c0, 0xbfae, 0x182a, 0x1469, 0xa377, 0x1b30, 0xb2f1, 0x1354, 0x42e2, 0xaf4d, 0xa156, 0x40f8, 0xb26a, 0x416e, 0x2cdc, 0x2741, 0xb06b, 0x4322, 0x4689, 0x41d1, 0x462a, 0x1f1d, 0xb297, 0x415d, 0x26de, 0x4089, 0xbf78, 0x4359, 0x42f6, 0xa6c6, 0x19f5, 0x29df, 0x4285, 0x459e, 0xb297, 0x2047, 0xbf4c, 0x0551, 0x1b21, 0xb27c, 0x413a, 0x4369, 0x4052, 0xbaeb, 0x3da1, 0x06a6, 0x40de, 0x4154, 0x18f7, 0x45ea, 0x44e4, 0x43e9, 0xba2e, 0x30a4, 0xaf60, 0x430e, 0xbac3, 0x1af2, 0x4158, 0xb21a, 0xbf2b, 0xbadf, 0x4055, 0x41d9, 0x4018, 0x44cd, 0xbfd0, 0x3e73, 0xa07c, 0xbad5, 0xb2a1, 0x1a51, 0x41e0, 0xbfe0, 0x462f, 0x409f, 0x4095, 0xba69, 0x04c2, 0x426e, 0xbf00, 0x40ae, 0x409d, 0x4312, 0x417f, 0x4312, 0x098f, 0xb0e9, 0xbfa4, 0x416a, 0xb074, 0x44f1, 0x421b, 0xb02c, 0x3394, 0xaced, 0x4636, 0x43b6, 0x2b1c, 0x1f46, 0x1ab5, 0x4235, 0x4030, 0x40e0, 0xba47, 0xb207, 0xb25c, 0x1786, 0xbaf3, 0x25bc, 0x430b, 0x4179, 0x436c, 0xbf7b, 0x4616, 0xb033, 0xa35a, 0x0a2f, 0x433c, 0x434f, 0xb254, 0xb2b7, 0x40a2, 0x140c, 0x1d7a, 0xb0df, 0xbf9b, 0x1396, 0x4323, 0x04ee, 0x1ad3, 0x1df6, 0x4001, 0x0825, 0xa444, 0x4221, 0x2085, 0xbfb0, 0x42fc, 0xa67e, 0x419c, 0xba0a, 0xa828, 0x40ec, 0x28b6, 0x4364, 0x4362, 0x2892, 0x419c, 0xb206, 0xb2f1, 0x1795, 0xbf6d, 0x0f94, 0x43e5, 0x1e8c, 0xa665, 0xb0e9, 0x06c5, 0xb22e, 0xbac4, 0x1f02, 0xba21, 0x0048, 0x43f7, 0x402f, 0xbf69, 0x43e2, 0x39d0, 0x1c2b, 0x0c3d, 0x41f5, 0x40a7, 0x4254, 0x4303, 0xa98f, 0x19bb, 0x27cd, 0x43bc, 0xb219, 0x4208, 0x43db, 0xb0e1, 0x42ac, 0x43c3, 0x400d, 0x4151, 0x45e1, 0x4097, 0xba76, 0x0cb1, 0x420d, 0x42eb, 0x466a, 0xbf9b, 0x409e, 0x46c0, 0x4168, 0xaba2, 0x01b0, 0x358b, 0x42e1, 0xbad1, 0x43e7, 0x415f, 0x42bf, 0xb09d, 0x1b7c, 0x38eb, 0xb212, 0xb298, 0x1b3e, 0x3148, 0xbfbd, 0xb038, 0xb003, 0x40b0, 0x1604, 0xbf5b, 0x45a4, 0x1e35, 0x2f5a, 0x0a27, 0x40ff, 0x42d9, 0x4196, 0x0bf3, 0xb0cf, 0x43c3, 0x14c9, 0x41c2, 0x419c, 0xb05f, 0xba3d, 0x407e, 0x4118, 0xbaed, 0x2f0a, 0x066f, 0x409c, 0x4132, 0x40c5, 0xbff0, 0x4592, 0xbf26, 0x1bfb, 0x1004, 0x439a, 0x41bb, 0xbf5e, 0x4543, 0x4243, 0x1faa, 0x4099, 0xb2c4, 0x1c03, 0xb2a7, 0xb200, 0x42e0, 0x01fa, 0x4083, 0x40dd, 0x19d7, 0xb2a3, 0xb2e3, 0xbf8a, 0x19a1, 0xa995, 0x40ad, 0x4564, 0x1a6c, 0x44b1, 0xbf82, 0x29f0, 0x407f, 0x4032, 0x0d94, 0x43c7, 0x1cd1, 0xbfdc, 0xb02a, 0xba7c, 0xa53e, 0x191e, 0x3274, 0x06ff, 0xbf0d, 0xa643, 0x19ed, 0x1ab6, 0x4292, 0xade2, 0x4138, 0x35a7, 0x42a6, 0x2df3, 0xbfc8, 0x410c, 0x194b, 0x40d9, 0x4350, 0xbac2, 0x42b8, 0xba17, 0x408f, 0x430c, 0xb2b4, 0x425b, 0x4319, 0x423d, 0x42c0, 0xb2cf, 0xb233, 0x4601, 0xba6b, 0x448d, 0x435f, 0x4133, 0x421b, 0xbfd7, 0xb263, 0xba08, 0x4386, 0x40c0, 0xba74, 0xb283, 0x4275, 0x405a, 0xa597, 0x3eab, 0xa03e, 0x4317, 0x4194, 0x46d5, 0x40d6, 0x4626, 0x407a, 0x4618, 0xba4b, 0x4185, 0x1fd4, 0xbac4, 0x4676, 0xbad6, 0xbade, 0x42a0, 0x425b, 0x43ee, 0xbf5a, 0x4137, 0xba4e, 0x40aa, 0x43e6, 0xb283, 0x4295, 0xa6de, 0x418f, 0x4314, 0x0bb1, 0x4278, 0xbaea, 0xb256, 0xb0c2, 0x0164, 0x218f, 0xbfb1, 0x40d5, 0x40d7, 0x19d6, 0x4197, 0x4255, 0x42cf, 0x4220, 0x41f8, 0x455e, 0x42fc, 0x42b1, 0x40e2, 0xb01b, 0xb207, 0x4362, 0x0dc2, 0x2f3b, 0x2d06, 0xb259, 0x46ec, 0x46c9, 0xba27, 0x400c, 0xba15, 0xba27, 0x28db, 0x416d, 0x1f98, 0xbf66, 0x4101, 0x4379, 0x4008, 0x26ab, 0x2a72, 0x14da, 0x4675, 0x40b5, 0xbaf2, 0xb2a0, 0x26d3, 0x44c5, 0x094c, 0x41cc, 0xbad0, 0xb255, 0x40f8, 0xbaf0, 0x41d1, 0x1a2c, 0x19e9, 0xb213, 0x263a, 0x429b, 0x4443, 0x1d60, 0x401d, 0xb2b7, 0xbf25, 0x1edf, 0x437a, 0xb0e0, 0x1fdb, 0x1eff, 0xbf08, 0x40c6, 0x418d, 0xac60, 0x41c8, 0x4311, 0xb056, 0x43fd, 0xba3a, 0x1e0a, 0x1c07, 0x4110, 0xba6f, 0xba05, 0x465e, 0x198e, 0xba67, 0x40eb, 0xbfae, 0xb2af, 0x4272, 0xa551, 0xb29a, 0x43dc, 0x42df, 0xba5c, 0x400b, 0x1e9c, 0xae12, 0x447c, 0x429a, 0x456d, 0x41f1, 0x4650, 0xba05, 0x40b7, 0x4385, 0xb2de, 0x4067, 0xbf2a, 0x4065, 0xb20c, 0xbf80, 0x26f3, 0x4060, 0xb27a, 0xba1e, 0xb2e5, 0x1006, 0xbfd0, 0xb226, 0xb225, 0x430a, 0x412a, 0xbf83, 0x1af2, 0x1ce9, 0x413e, 0xa670, 0x43ef, 0x43c2, 0x24d3, 0x1cdf, 0xba14, 0xbf07, 0x4635, 0x432c, 0x2b26, 0x0b9d, 0x443a, 0x1dff, 0x1813, 0xb232, 0xbf77, 0x4308, 0x0d17, 0x1c3f, 0xba68, 0x1bac, 0x445e, 0xb228, 0x43a3, 0x1d1d, 0x1fa3, 0x0765, 0x421b, 0x4133, 0x422d, 0x4113, 0x4169, 0xba43, 0x404c, 0x416f, 0x423d, 0x442f, 0xba0a, 0xbacb, 0x201f, 0xb2d1, 0x42ec, 0x32d0, 0x419e, 0xbf83, 0xbac6, 0x41a1, 0x09d4, 0x1866, 0x422b, 0x4190, 0x422c, 0x4120, 0x1b77, 0x1aa2, 0x43b8, 0x400c, 0x43c2, 0x20d1, 0xb283, 0x421d, 0x1c4a, 0x4105, 0xba4e, 0xb2d5, 0xb257, 0x420e, 0x1800, 0x434b, 0xb256, 0x4365, 0xbf33, 0x412e, 0x1fb4, 0x18f4, 0x4175, 0x4122, 0x1309, 0x41dd, 0xb2a7, 0x4093, 0x2718, 0x223b, 0xb2cd, 0xba41, 0xbf57, 0x435c, 0xa9d4, 0x40c6, 0x4628, 0xa687, 0x1abd, 0xba66, 0x42ef, 0x1edb, 0xb2a1, 0x4174, 0xac29, 0x1832, 0x3f4b, 0x40c1, 0xba29, 0x428f, 0xa663, 0xbf29, 0x437a, 0x44d5, 0x3e25, 0x1e7c, 0x46d4, 0x42d5, 0x0605, 0x3dfc, 0xb25f, 0x415b, 0xbfc3, 0x26e3, 0x3d32, 0x43a2, 0xb063, 0x4216, 0xba27, 0x4147, 0xbfc1, 0xb203, 0x40bf, 0x404c, 0x4498, 0x28ea, 0xba56, 0x456c, 0xb0f5, 0xbf80, 0x230a, 0xb21b, 0x1e7c, 0x3ee1, 0x435f, 0x1e9a, 0x417d, 0x404c, 0xbf0a, 0xba00, 0x41c0, 0xb21d, 0x4261, 0x1f4a, 0x4043, 0x41ec, 0x438f, 0xb0f5, 0xb275, 0xbfc6, 0xb0e6, 0x2fb0, 0x430d, 0x06b2, 0x435a, 0xa453, 0x46a8, 0x4074, 0x43a7, 0x0363, 0x404d, 0xbaf1, 0xba12, 0x1a9d, 0x420c, 0x447c, 0x41f8, 0x434c, 0xb292, 0xbf18, 0xb2c0, 0x4294, 0x4258, 0x4060, 0xbf9f, 0xb2b3, 0x3a02, 0xae29, 0xba20, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3cccdcbb, 0xb8b42a16, 0x5515dd66, 0x79ed5e7e, 0x48648228, 0x836897c4, 0x72bc80f9, 0xe9ea3611, 0xd38985d1, 0x943bed5a, 0x921c041b, 0x24eee228, 0x42062aa5, 0xb425effb, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0x5058e007, 0x00001f30, 0x000000d6, 0x0000301f, 0x07e05850, 0x051ddf28, 0x00001ba0, 0x01000000, 0x0000001f, 0x000010d8, 0x00001080, 0x24eee228, 0x00001080, 0x00001afc, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4037, 0x4485, 0x422f, 0x1977, 0x42be, 0x400d, 0x43d8, 0x4058, 0x423d, 0x2582, 0x1864, 0xbfc1, 0x4207, 0x4056, 0x1ff5, 0xb2e1, 0x4389, 0xac7e, 0x45ac, 0x41ad, 0x408a, 0x429a, 0x1539, 0xba4c, 0xa1e6, 0xba01, 0xba09, 0xba5e, 0x421d, 0x4005, 0x247d, 0x40f4, 0xbf52, 0x42ac, 0xb214, 0xba5d, 0x100c, 0x448b, 0x080c, 0x4371, 0xbac9, 0xb2db, 0x4319, 0x43a1, 0x4219, 0x43fe, 0x422b, 0x3cd5, 0xb080, 0x4107, 0x403e, 0x4196, 0x0c01, 0xb2a8, 0x2e67, 0xb242, 0x439e, 0x42c0, 0x047e, 0xbfaf, 0x1b5d, 0x43ec, 0x406f, 0x11e0, 0xb2c9, 0x2097, 0xba48, 0x43a2, 0xbf23, 0xb213, 0x43d8, 0xba5c, 0x0e20, 0x43e3, 0x4052, 0x43ad, 0x2a92, 0x1b79, 0x421d, 0xba10, 0x4612, 0x4408, 0xba35, 0x411e, 0xb273, 0xb241, 0x43ec, 0xb017, 0x2b0d, 0x41fb, 0x3ce9, 0xb2b0, 0x40b7, 0xbff0, 0x4191, 0xb226, 0x2331, 0xbf3f, 0xb0e7, 0xba0c, 0xb016, 0xba4c, 0x4323, 0x4241, 0x40ad, 0xbf23, 0x426d, 0x4083, 0x1bd2, 0xa309, 0x1e36, 0xa492, 0xb0c1, 0x1ba7, 0xba32, 0xb2e5, 0x4340, 0x43a3, 0x41af, 0x1493, 0x11be, 0xba11, 0x4274, 0xba14, 0xb056, 0x1c8c, 0x2879, 0x4028, 0x077b, 0x406e, 0x3ff9, 0xbf85, 0x4284, 0x46b9, 0xb247, 0xa8d2, 0x409f, 0xb2c5, 0xba69, 0x050c, 0x1b37, 0x1086, 0x4247, 0x40d0, 0xac04, 0x41e5, 0x41b2, 0x40ff, 0xac10, 0x431d, 0xa41c, 0x4147, 0x44c9, 0xb2b5, 0x4333, 0xbf51, 0x46a9, 0x4598, 0x4110, 0x403c, 0xb26d, 0xb0aa, 0x2212, 0x449a, 0x2adb, 0x1f40, 0xb0c6, 0xba43, 0x3c79, 0x4152, 0xba29, 0x414a, 0x4285, 0x01b1, 0x3d68, 0x1ff0, 0x43ae, 0x410e, 0xbf6f, 0xba00, 0xa19f, 0xb071, 0x4007, 0x41f6, 0xb28f, 0x435e, 0x0d65, 0xa66a, 0x41db, 0x42d0, 0xb006, 0xb246, 0x4142, 0xbf00, 0xb28b, 0x0d98, 0xb297, 0xbf24, 0x409d, 0x4090, 0x434a, 0x4085, 0x42df, 0x40f4, 0x4009, 0x41af, 0x4157, 0x416f, 0x423d, 0x4281, 0xb05e, 0x4370, 0xba4f, 0x19da, 0xb206, 0xba78, 0xba32, 0xbfa5, 0x43f3, 0x2473, 0x28a5, 0x42ca, 0xbf78, 0xb229, 0xbaee, 0xba52, 0x4157, 0x40e9, 0xaa96, 0x409d, 0x4090, 0xb269, 0x4361, 0x466d, 0x434c, 0x43ab, 0x45b9, 0xba6b, 0x1c45, 0x18f8, 0xbfc7, 0x0236, 0x42ff, 0x35f7, 0x1e55, 0x4148, 0xb2f9, 0x16a8, 0x417e, 0x402d, 0x40cd, 0xb0a4, 0x313e, 0x225a, 0x1136, 0xa497, 0x45f4, 0xbfb6, 0x418e, 0x4639, 0x1c68, 0x419e, 0x3bee, 0x1b1a, 0xb030, 0x0ae1, 0x40bd, 0x41c8, 0xa186, 0xbf13, 0x35f5, 0x4172, 0xb2a5, 0x433d, 0x41de, 0xba70, 0x4194, 0x4157, 0x1c65, 0x2daa, 0xbfd0, 0xbf07, 0x4303, 0x4226, 0xba1d, 0xb0ad, 0xbf4c, 0x40b3, 0xbad6, 0xafc9, 0x13c7, 0x0dfe, 0xbad5, 0x4008, 0x1b04, 0x1317, 0xba12, 0x40a5, 0x43c5, 0xb045, 0x18e6, 0x414c, 0x0165, 0xb23a, 0x1c08, 0x305f, 0x3885, 0x4310, 0x4179, 0x4093, 0xb236, 0x407c, 0xb096, 0x405f, 0xbf4e, 0x1d98, 0x17f0, 0x41d5, 0x404c, 0x466b, 0x41e0, 0x41c7, 0xbfd0, 0x4302, 0xba29, 0x428b, 0x429c, 0xb01c, 0x2dec, 0x415c, 0x43d0, 0xb29e, 0x0b28, 0x2572, 0x41c2, 0xb23e, 0xbfb3, 0x1fcc, 0xb23b, 0xb29b, 0xb253, 0x4133, 0x42f0, 0x4555, 0xb00e, 0x3f9e, 0x4352, 0xba62, 0x458a, 0xbf0a, 0xb21b, 0xb2f8, 0x2bd1, 0x4347, 0xbfa8, 0x4140, 0x09a8, 0x16d2, 0x42ad, 0x40a0, 0xb29b, 0x056b, 0xa54c, 0x4224, 0xb2d8, 0x4360, 0xba52, 0x4606, 0x1ea5, 0xb20d, 0xb26c, 0x273f, 0x4062, 0x409c, 0x454c, 0xbf7f, 0x2edf, 0x4290, 0xb06e, 0x422c, 0x4244, 0x1f4d, 0xbf64, 0x432a, 0x1595, 0x413c, 0xbf2d, 0xb0c2, 0x4133, 0x41f7, 0x412b, 0x4482, 0x4347, 0xae85, 0x434d, 0x41a1, 0x42e2, 0xbae1, 0x42ed, 0xb29b, 0x4330, 0x42ba, 0x40df, 0x41eb, 0x3c9f, 0x43a2, 0x3e6f, 0x41e3, 0x229a, 0x41e4, 0x1d5e, 0xbfb6, 0x402f, 0xb201, 0x4024, 0xb030, 0x195a, 0xba4d, 0x464b, 0x4025, 0x0420, 0x1a5a, 0xb25e, 0x1a8d, 0x4133, 0x3e2c, 0x15ef, 0x45be, 0x1cb5, 0x0ec0, 0x1d99, 0x1419, 0x430b, 0x4009, 0x0d89, 0x0030, 0xb22e, 0xbfb3, 0xa6c3, 0x1c87, 0x2728, 0x2023, 0xbfb8, 0x1ee3, 0x413e, 0x41c4, 0x4384, 0x4433, 0xb04b, 0x28c5, 0xb003, 0xbf49, 0x0673, 0x1a62, 0xb2e5, 0x316f, 0xb2bb, 0xb2fe, 0x2ed3, 0x1c7c, 0x1b45, 0x41a2, 0xb29d, 0x16cb, 0x4267, 0xb284, 0x4235, 0x42d2, 0x1dff, 0x1d0a, 0x1ca3, 0x414b, 0xadb4, 0x43cd, 0x3db7, 0x410a, 0xb082, 0x1d92, 0x0461, 0xa273, 0x29f0, 0xbf77, 0x0edf, 0x1957, 0x1d7e, 0x4676, 0x4085, 0x43aa, 0xbff0, 0xb0f1, 0x4249, 0x4045, 0xb2ab, 0xad44, 0x41b4, 0x4129, 0xba75, 0x1dda, 0xaf01, 0x1aa6, 0x1e4d, 0xb27c, 0xb2eb, 0xaf98, 0x35e7, 0xbf07, 0x2f6f, 0x44f9, 0xb2b3, 0x1ed8, 0x3699, 0xbae5, 0x2d3b, 0x4017, 0x26dc, 0x2d2b, 0xba0e, 0xbf60, 0x1718, 0xbf00, 0x2c1a, 0x411e, 0x1a13, 0xb2b4, 0x219d, 0xbf07, 0x424b, 0x43bf, 0x4252, 0x098e, 0x4302, 0x250d, 0x4064, 0xb017, 0x3af3, 0x4222, 0xadfc, 0x41e5, 0xbad6, 0xa529, 0x4085, 0x41e1, 0xba1c, 0x4279, 0x43ba, 0x03fb, 0x2279, 0xb224, 0xb28e, 0x2e71, 0xb230, 0x3733, 0xb085, 0x41cb, 0xb2a8, 0xbf77, 0x4113, 0xa77d, 0x403b, 0x1d4f, 0x3e00, 0xa3df, 0xb243, 0xb2b5, 0x4396, 0x19ea, 0xba1c, 0xbfbc, 0xb23e, 0x410e, 0xba2e, 0x405f, 0x1df6, 0x1b93, 0x4148, 0x401a, 0x41ec, 0xb06d, 0x4277, 0x0f45, 0xb2c0, 0x42b2, 0x4326, 0x1426, 0x2c0d, 0xbf47, 0xba26, 0xb296, 0x1907, 0x43a4, 0x407c, 0x406f, 0x4332, 0x2146, 0x403c, 0x4173, 0x43c5, 0x1463, 0x3d72, 0xa539, 0x02d3, 0xbf85, 0xb2e9, 0x080e, 0x3996, 0xb0c8, 0x3133, 0x422c, 0xbaf6, 0x1e96, 0x414d, 0xb236, 0x4071, 0x1b45, 0x41a3, 0x1b5a, 0x04f2, 0xaaed, 0x43d5, 0x40c1, 0x43ee, 0x4631, 0xb0c0, 0x4616, 0x432f, 0x06c9, 0x1dca, 0xbfe1, 0x2a04, 0x106d, 0x40fd, 0x4417, 0xb245, 0x40c4, 0x4279, 0x1ab5, 0x424d, 0x1c8c, 0x2adf, 0xb215, 0x41fb, 0x1014, 0x41bc, 0x43a2, 0x428a, 0xba77, 0x2e89, 0xba5f, 0xb285, 0xb26f, 0x4071, 0x3346, 0xb22b, 0x4350, 0xbf01, 0x403f, 0x42ef, 0x2d94, 0xba29, 0x421b, 0xbf5c, 0x4164, 0x4616, 0x2ad2, 0xb22d, 0x1fea, 0xbf70, 0x42be, 0x409e, 0x42ef, 0x42fa, 0xb28e, 0xba3b, 0xba70, 0xb25a, 0x07ec, 0x405c, 0x40bf, 0x404e, 0x1bc1, 0xba1a, 0xba63, 0xb2e8, 0x3845, 0x4072, 0x1cc5, 0xbf6f, 0x1fd3, 0x1fd9, 0xb2f7, 0x4077, 0x423b, 0x402d, 0x1abd, 0xbf99, 0x4192, 0xbaf0, 0x3122, 0x2e08, 0x4029, 0xbafe, 0xb263, 0x42e5, 0x38e5, 0x43b0, 0xa37a, 0x3088, 0x421d, 0x2339, 0x1d50, 0xb232, 0x2ba9, 0x0059, 0x442f, 0x1e1a, 0x403d, 0x4223, 0x406b, 0xb005, 0xb2e7, 0xbf21, 0x4391, 0xbad2, 0x40bb, 0x04c5, 0x1e4a, 0x424a, 0x415e, 0x0467, 0xbad2, 0x40c5, 0xa1c5, 0x45d3, 0x43d3, 0xb24f, 0x3261, 0xb05b, 0x4364, 0x420b, 0x19f9, 0x2464, 0xbfd7, 0x240f, 0x411a, 0x0a1e, 0xbaf0, 0xb0bb, 0x40fa, 0xb232, 0x4480, 0x401a, 0x40ea, 0xaa93, 0xbff0, 0x0dd7, 0x43ce, 0x4279, 0xba2c, 0xb20b, 0x3d38, 0xbae1, 0xa965, 0x4201, 0x425a, 0x4217, 0x4469, 0x03f7, 0xbf3e, 0xb0d5, 0xb297, 0x42c4, 0x42b9, 0x42d7, 0x417f, 0x194b, 0x3e69, 0x409f, 0xbf60, 0x4219, 0xa318, 0x3a6a, 0xbf29, 0xb2a3, 0x4649, 0x467b, 0x1528, 0xb0b6, 0x40d0, 0xa445, 0xa6a6, 0x4153, 0x15db, 0x0d2b, 0xa059, 0x3ca7, 0x0813, 0x43fe, 0x199d, 0xbfd4, 0x4138, 0xb00a, 0x1936, 0x4111, 0xa06a, 0x43f8, 0x4041, 0xb2f4, 0x4293, 0xba58, 0x437f, 0x155d, 0x0878, 0xbad2, 0xb0cb, 0x1c3e, 0xbf19, 0x427a, 0x1a09, 0x17be, 0x1742, 0x1f52, 0xb282, 0x404e, 0xb263, 0xba0d, 0x425c, 0xb095, 0x41cd, 0x1d19, 0x0352, 0x1841, 0x26ff, 0xbf7c, 0xbadf, 0x1407, 0x42a3, 0x4025, 0x4330, 0xbaf6, 0xbf91, 0xa711, 0x4002, 0xa488, 0x1a65, 0x4052, 0x19a3, 0x1819, 0x43e7, 0xb239, 0x40a4, 0x31d2, 0xa11f, 0x42e6, 0x43d7, 0x4110, 0x4012, 0x2c2f, 0x43b5, 0x45a1, 0xb2dc, 0x12f9, 0x268b, 0x1993, 0x4228, 0xb20b, 0x419b, 0xb27b, 0x42bb, 0x46a2, 0xbfa9, 0x24ed, 0x41a1, 0x0d0a, 0x1559, 0x4114, 0x42f0, 0xa900, 0x4413, 0x4008, 0xa9f6, 0x05d6, 0xb2a4, 0x42e2, 0x3bdb, 0x435c, 0x1d9f, 0x183f, 0x4063, 0x0c6b, 0x30e3, 0xbf7c, 0x430f, 0x1169, 0xb20a, 0x1ce1, 0x3691, 0xa65e, 0xb20f, 0x40d9, 0x28c6, 0xba56, 0x403d, 0x43e8, 0x1e4e, 0xa2a3, 0xb21f, 0xa649, 0x4428, 0xa8cf, 0x4068, 0xb0f4, 0xb2b0, 0xbfcb, 0xb275, 0x1a56, 0x409b, 0x1ed9, 0x3908, 0x41ca, 0xb0d0, 0x1a06, 0x4230, 0x1f36, 0xbf90, 0xba26, 0x4020, 0xbf78, 0x42fe, 0x46b9, 0x3e8c, 0xa5e7, 0x4272, 0x0d53, 0x4159, 0x41b4, 0x4484, 0xbfc0, 0x4281, 0x414a, 0xad25, 0x195b, 0x43b8, 0x1f77, 0xbf67, 0x404e, 0xba33, 0xa665, 0x45ba, 0xb0ec, 0x41dd, 0x3f03, 0x343b, 0x43a5, 0xa8f0, 0x0ac1, 0x46ab, 0x4223, 0xb060, 0x4240, 0x41fc, 0x0d95, 0x1df4, 0x4299, 0xbff0, 0x0e19, 0xbaf4, 0x4137, 0x40e0, 0x4317, 0x3f08, 0xbf49, 0x1ad3, 0x1977, 0x1e1b, 0x434f, 0xb2ab, 0x22ae, 0xbfcc, 0x4435, 0x0e38, 0x414b, 0x2b42, 0x400f, 0x36e1, 0x41dd, 0xbade, 0x4190, 0xbf6f, 0xa805, 0x4073, 0x41bd, 0x05d3, 0x41b6, 0x1a96, 0x4075, 0x35a1, 0x3336, 0xa35c, 0x4332, 0x431f, 0x26bc, 0xb228, 0x0f68, 0x41ff, 0x438a, 0xb227, 0xbac0, 0xba78, 0xb073, 0x4083, 0x1d03, 0xbf41, 0x4159, 0x2f5c, 0x4081, 0x4337, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x3a4c2871, 0xacd9f7e9, 0x7bcd3cce, 0xadebfcc6, 0xde673e7c, 0x7dd2cf41, 0x7006af38, 0x2bb0bab5, 0xe2b978ed, 0x142f0bcb, 0x8bc78472, 0x492faf1e, 0x7421150a, 0x1ef52200, 0x00000000, 0xf00001f0 }, FinalRegs = new uint[] { 0xffff18fc, 0x00000000, 0x000000a6, 0xffff1900, 0xfffffc18, 0x00063f47, 0x000000bc, 0xfffffcbc, 0xe2b978f1, 0x00000000, 0x000000b8, 0xaca0a530, 0x7421150a, 0x59414be9, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xb25e, 0x4201, 0xb0f6, 0x437b, 0xbf28, 0x1a61, 0x41f3, 0xb2af, 0xba52, 0xb0be, 0x0ee9, 0x18cb, 0x2ddc, 0x3e47, 0x4216, 0xb2e8, 0x40c7, 0x33d7, 0x42a9, 0x412a, 0x4322, 0x4313, 0x42b0, 0xbfbc, 0x42d1, 0x4309, 0xb2bc, 0xa626, 0xbfaf, 0x41e6, 0x4026, 0x4028, 0xa235, 0x40c3, 0xb030, 0xbfa0, 0x4331, 0xacd4, 0x2a6c, 0x4058, 0x353b, 0x42eb, 0x199e, 0x1a78, 0x1e98, 0x1222, 0x4677, 0x180d, 0x1b21, 0x36a8, 0x1d5e, 0xba01, 0x42b0, 0x4110, 0x416f, 0x4007, 0x1bb0, 0xab34, 0xbf31, 0x40f0, 0x46d4, 0x422e, 0xb2db, 0xa831, 0x2443, 0x40a8, 0xb002, 0xba2f, 0x41df, 0x4363, 0xa95d, 0xba57, 0xb2bf, 0x43b9, 0x427a, 0x4550, 0x1af8, 0xbad9, 0xb2cf, 0xbfb0, 0x4300, 0x4301, 0xbf4f, 0x42f3, 0xb2c5, 0xba77, 0x4157, 0xb2cc, 0x12ef, 0x187c, 0x46d9, 0xa1ec, 0x1c3f, 0xa4c0, 0x34a8, 0xa8ec, 0xb25d, 0x4253, 0x40d1, 0x4122, 0xbafd, 0x42fe, 0x19fb, 0x4566, 0xba75, 0x3820, 0x4115, 0x412e, 0x1f3f, 0xbf8d, 0x10fe, 0x4231, 0x40d3, 0xb054, 0x1750, 0xb07a, 0x1be5, 0x41a9, 0xb227, 0x431e, 0x41a3, 0xbae7, 0x43c0, 0x2338, 0x41c9, 0x4026, 0x4368, 0x4371, 0x42ad, 0xb2be, 0x1d31, 0x1cfb, 0x41f2, 0x3242, 0xbf33, 0x3a13, 0xbae1, 0xb03a, 0x4698, 0x4130, 0x44e0, 0xb238, 0x417b, 0x40f2, 0xbfbe, 0x4233, 0x4115, 0x45a8, 0x41cb, 0x46c3, 0x30b0, 0x42df, 0xa26e, 0xb2b7, 0x4205, 0xbf5b, 0xba12, 0xa0ae, 0x4247, 0x46e0, 0xb2c5, 0x41ed, 0x4068, 0x41e9, 0x19fd, 0x435a, 0x41e7, 0xb2f1, 0xb2b0, 0x41a1, 0x43c4, 0x45dc, 0x4387, 0x40b8, 0x2a85, 0x19fc, 0x4360, 0x4545, 0x1930, 0x41b3, 0x24ec, 0xbfa0, 0x42bc, 0x403f, 0xbf7d, 0xaf68, 0xb2d1, 0x4238, 0xb2a9, 0x4346, 0xb09b, 0xa482, 0x41d9, 0xba36, 0x4108, 0xb281, 0xba17, 0x28a4, 0x16f0, 0x10b0, 0x22f2, 0x467d, 0xac2a, 0x133e, 0x4195, 0x0338, 0x41dc, 0xbf03, 0x41c7, 0x4375, 0x41db, 0x4608, 0x45a0, 0x401a, 0x1b82, 0x42ca, 0xb01b, 0x206b, 0x4610, 0x1db7, 0x44dd, 0xa4e6, 0x0c75, 0x4064, 0x41ec, 0x27c6, 0x4371, 0x42f1, 0x418f, 0xb2d6, 0x237a, 0x209a, 0x364f, 0x4183, 0x4252, 0xbac8, 0xbf48, 0x32a8, 0x1805, 0x42d8, 0x1fab, 0x18b3, 0x41fa, 0x04ab, 0xbf80, 0x19b6, 0x1a4d, 0x41c2, 0x1b8f, 0xb23a, 0x40a9, 0xbaff, 0x40ec, 0x4313, 0x40d2, 0xb09a, 0xb218, 0x42da, 0x1f49, 0x41ab, 0xb22b, 0xbf58, 0xba62, 0x4020, 0x1f31, 0xb293, 0x4136, 0x24c0, 0x088e, 0x4252, 0x2136, 0x3252, 0x2efa, 0xa1bf, 0x04ad, 0x1c79, 0xb01c, 0x43f7, 0x42e6, 0x426d, 0x4364, 0x4236, 0xb298, 0xbf24, 0x26e7, 0xb27d, 0xae82, 0x414b, 0xa71c, 0x44f0, 0x4395, 0x421c, 0xb29b, 0x409f, 0xbfd9, 0xb206, 0x43c8, 0x19f8, 0xb24d, 0x4265, 0x419f, 0x414d, 0x429b, 0x45c5, 0x435a, 0x11d9, 0xb259, 0x4188, 0xba06, 0x204d, 0x1b38, 0x4296, 0xa47f, 0xbf03, 0x45ad, 0x167f, 0x23eb, 0xb237, 0xb289, 0x1c1c, 0x43ef, 0x42ad, 0xa496, 0x438e, 0xb0a6, 0x3afb, 0xb229, 0x2661, 0x4159, 0x4384, 0x3b07, 0x45b8, 0x09ae, 0x3811, 0xbfa1, 0x3db5, 0xb217, 0x2dff, 0x43a2, 0xb28a, 0xbfa3, 0xba3f, 0xb2ec, 0xb2e0, 0xb067, 0x0a10, 0xb20a, 0xa2ad, 0x446b, 0xb27d, 0x05f3, 0x4248, 0xb02b, 0x43be, 0xba20, 0xbf90, 0x2133, 0x0c37, 0x254d, 0x4144, 0xb213, 0x403f, 0x1356, 0xa35e, 0x4219, 0x438d, 0xb211, 0x42ae, 0xbfbd, 0x1e52, 0x3cf3, 0x1d3c, 0xb287, 0x0781, 0x405e, 0x41a9, 0x436e, 0x1ebf, 0xbf01, 0x0041, 0x29a5, 0x422e, 0xba5a, 0xb0f3, 0x4355, 0x41b5, 0x421c, 0x4191, 0x4276, 0x43cb, 0xbae3, 0x42d1, 0xba32, 0xbfc0, 0xb232, 0xbf6a, 0x4013, 0x4256, 0xba73, 0x40a9, 0xba05, 0x1de9, 0xbf04, 0x4170, 0x42da, 0x40a6, 0x13a4, 0x4084, 0x42da, 0xbfa0, 0x4081, 0xbfd9, 0x220b, 0xb26f, 0x41ee, 0xba5b, 0xba52, 0x43e1, 0xb2b4, 0x1ffd, 0xbf6b, 0xb278, 0x4690, 0x4370, 0x3669, 0xb21e, 0xb017, 0x375d, 0x4131, 0x407e, 0x40e7, 0x4638, 0x0d44, 0x4254, 0x3476, 0x41ec, 0x42fd, 0x1ccd, 0xb295, 0xbfd0, 0x401c, 0x3235, 0x4645, 0x155b, 0x0311, 0x41db, 0x40c0, 0x396d, 0x0ba6, 0xbfaf, 0x0e6d, 0x43fd, 0xa6bc, 0x401a, 0xbafc, 0x421e, 0xbf18, 0x1886, 0x4106, 0xbf27, 0x12cb, 0x43c8, 0xba28, 0x4011, 0xba7f, 0x1103, 0xbac5, 0x193b, 0x466d, 0x42c5, 0x184a, 0x0d38, 0xbf62, 0x3f92, 0x43e9, 0xaa66, 0xb093, 0x334d, 0xba06, 0x1ebb, 0xb2ff, 0x465e, 0xb24e, 0xbade, 0xbfa3, 0x2abf, 0x3186, 0x430a, 0x448a, 0x434f, 0xb291, 0x4215, 0x1952, 0xb003, 0x41ec, 0x44ed, 0x438f, 0x42f3, 0x424c, 0x4025, 0xb0d7, 0x18f0, 0xb25d, 0x416e, 0x4120, 0x45a9, 0xb0f9, 0xb2fd, 0xbaed, 0xba7d, 0xbfd3, 0x41ec, 0xb22b, 0x428a, 0x42e5, 0xb02c, 0x1b7c, 0x37e0, 0x430a, 0x190a, 0x25db, 0x2eff, 0xb276, 0x0b11, 0xbfe2, 0xab47, 0x251c, 0xb223, 0x31c7, 0xbaf2, 0xb001, 0xb212, 0x30f1, 0xa44c, 0x41ae, 0x418d, 0x40a4, 0xb0b6, 0x4331, 0x425a, 0x4040, 0x1849, 0xb03a, 0x1959, 0xb092, 0x429d, 0x4188, 0xbf12, 0x4619, 0xba68, 0xbf60, 0xbf80, 0x42b8, 0x0a84, 0xb2fe, 0x456c, 0xb0f3, 0x4194, 0xb272, 0xb0d7, 0xb2de, 0x4353, 0x0e33, 0x1c0a, 0xbac8, 0x42c6, 0x19ff, 0x43af, 0x4089, 0xbfb8, 0x3b9b, 0x096d, 0x31ac, 0x0234, 0x40cf, 0x24b8, 0x4278, 0x417a, 0x4309, 0x427e, 0xb070, 0x42cf, 0x40a9, 0x433b, 0x418d, 0xb2be, 0x1969, 0x429e, 0x43af, 0xbf1f, 0x0b1a, 0x4286, 0x41ce, 0x426d, 0x4136, 0x1ec7, 0x3425, 0xa663, 0x4457, 0xb2a4, 0x43b1, 0x41e0, 0xb2cc, 0x26cc, 0xbad6, 0xbacc, 0xb2e0, 0x4018, 0x3a50, 0xb2bf, 0xba3b, 0xbadb, 0xb0c4, 0xbf09, 0x1167, 0x4099, 0x044b, 0xba75, 0x43d6, 0xba3a, 0x43f5, 0x2c1c, 0xba5a, 0x43bc, 0x42a9, 0x1c95, 0xad4c, 0x43e4, 0x46cc, 0x42cc, 0x2605, 0x4009, 0x1871, 0x4069, 0xba37, 0x411d, 0x1807, 0x426d, 0x423b, 0x0fab, 0x4159, 0xbf83, 0x4290, 0x1d71, 0x3601, 0xa5d0, 0xb23a, 0x1cfb, 0x42fc, 0x435f, 0x4003, 0xa2b5, 0x4163, 0x4185, 0x403c, 0xb0d0, 0x1ae1, 0xb271, 0xafbc, 0x40cb, 0x4174, 0xbf69, 0xb258, 0xb2ec, 0x42c0, 0x41dd, 0x197d, 0xbf14, 0xa735, 0x38c4, 0x4188, 0xbfb0, 0xbf0d, 0x4117, 0x41ae, 0xb0e8, 0x411b, 0x4155, 0x43bb, 0xba44, 0x4198, 0xb2cd, 0xbfc7, 0xbae2, 0xb2c4, 0x43d7, 0x1e4d, 0x34ca, 0xb223, 0x439c, 0xb294, 0xbae1, 0x1d72, 0xbfe0, 0xb2ef, 0x4363, 0xbfb9, 0x413e, 0x408b, 0xa5be, 0x2675, 0x42d3, 0xb204, 0x1ff1, 0x096f, 0x41dd, 0x3c1e, 0xb2bd, 0x40c6, 0x2575, 0xb2b1, 0x1910, 0x4607, 0x42b8, 0x43dd, 0x4329, 0x4112, 0x1ea0, 0x1b9b, 0xa27c, 0xbadf, 0xbf81, 0x4088, 0x41a7, 0xada0, 0x0db4, 0xb2fe, 0x408a, 0x18da, 0xb026, 0xb0b3, 0x43f1, 0x4467, 0xbfaf, 0x4367, 0xbad4, 0x405f, 0x4286, 0x1a0d, 0xb2b7, 0x4571, 0x183e, 0x43cf, 0x1a0d, 0x4036, 0x424f, 0x41a4, 0x410f, 0x46a2, 0xaf85, 0xa17c, 0x433b, 0xbf0e, 0x4022, 0x43f0, 0x11a4, 0x40c4, 0xba2c, 0x18a6, 0x1a93, 0x41d1, 0xa8a3, 0xbaf8, 0x4584, 0xbff0, 0xb23a, 0x4385, 0x43d8, 0xb29b, 0x4362, 0xb20f, 0xb0a7, 0xad67, 0xb252, 0xbf8a, 0x1cb1, 0xb215, 0x41a4, 0x40b3, 0x2a7b, 0x0d23, 0x4199, 0x18ce, 0x4618, 0x144a, 0x3082, 0x1c3e, 0xbf57, 0x32de, 0x412d, 0xb298, 0x4648, 0x1c4d, 0x1df9, 0x3e54, 0xbf8e, 0x4488, 0x4092, 0xb200, 0xba13, 0x41a1, 0xba23, 0x1d7d, 0xba09, 0x2b1c, 0x4691, 0x397f, 0x4032, 0xbf0e, 0xba67, 0x40e5, 0xb2f3, 0xbff0, 0xba04, 0x3ec6, 0x1a0f, 0x408b, 0xb2e2, 0xbfe2, 0x4224, 0x195a, 0x3204, 0x430d, 0x39cb, 0x1c36, 0xb23f, 0x1977, 0xb030, 0x46f4, 0xb2fe, 0xba77, 0x4141, 0xb276, 0x4217, 0xbf60, 0xb223, 0xb2ca, 0x43bd, 0x4204, 0x41d0, 0xa049, 0x4352, 0xb035, 0xbf05, 0x4396, 0x429b, 0xa066, 0x0975, 0x409b, 0x46bd, 0x2aa5, 0xb2b9, 0xba09, 0xb05e, 0x422d, 0x1d65, 0x42c5, 0x42d4, 0x0533, 0x221b, 0x42c2, 0xa9dd, 0x2ac2, 0xad89, 0x4263, 0x4378, 0x40ff, 0x429b, 0x4282, 0x421f, 0xb0c4, 0xbf56, 0xb0c4, 0xb070, 0xbac9, 0x43ff, 0x1bd4, 0x418f, 0x09b8, 0xb0a8, 0xbfc7, 0x455a, 0x04bf, 0xba69, 0x4283, 0x404a, 0x43a2, 0x449b, 0x1e46, 0x4130, 0x4305, 0xb272, 0x1203, 0x43f4, 0x437e, 0xb066, 0x4273, 0x0f4c, 0x4612, 0xaa6e, 0x08d1, 0x437c, 0x461d, 0x44ac, 0xa824, 0xb2cd, 0xb01f, 0xbf53, 0xbfa0, 0xb283, 0x4151, 0xb284, 0x41f4, 0x406e, 0xb038, 0x4330, 0x420d, 0x44b5, 0xb2b2, 0xa730, 0xb2e0, 0x2b11, 0x4698, 0x4142, 0xb2f9, 0x42ec, 0xb08e, 0x3c8b, 0x42bc, 0xb22e, 0x4226, 0x400b, 0xb219, 0xbf25, 0x43c3, 0xbf00, 0x4345, 0x40aa, 0xb0a0, 0x1a70, 0x429e, 0x4074, 0x41af, 0xb272, 0xb0af, 0xb0f4, 0x42ff, 0xba34, 0x4342, 0x399a, 0x46cd, 0x19e4, 0x3c64, 0xbfcf, 0x40e9, 0x40c2, 0x3ac5, 0x323c, 0x307e, 0xba37, 0xba17, 0xb2f7, 0x207f, 0x431e, 0xac1e, 0xb0c8, 0x41b1, 0x436b, 0x40d1, 0xb02b, 0xbfe8, 0x4335, 0x41ba, 0x1cad, 0xa482, 0xbacd, 0xb285, 0x401c, 0xa85b, 0x4199, 0x4392, 0xbfbb, 0xb045, 0x0935, 0x30e0, 0x4226, 0xb22e, 0x282a, 0xb2e5, 0x40c8, 0x4326, 0x2bf9, 0x4197, 0xbf16, 0x4371, 0xb2ea, 0x40c4, 0x3f3c, 0xb285, 0x4170, 0x40ce, 0x2706, 0x308f, 0xbfbe, 0x4113, 0x0553, 0xbad5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x9b4ed9e3, 0xfb4d9b4f, 0xf2becf95, 0xee40ea2c, 0x9b566ffa, 0x1e8d47a2, 0xeeda6eed, 0x8e4fe94e, 0x87b89292, 0x5bc99f48, 0xcdbc076e, 0x49f2a792, 0x65012e66, 0x2d75e18d, 0x00000000, 0xa00001f0 }, FinalRegs = new uint[] { 0x40001b7a, 0x000019ff, 0x00000000, 0xffffffff, 0x00000000, 0x000000ec, 0x00000000, 0x00000006, 0xef680aac, 0x80000000, 0xffffffff, 0xc2cc9a01, 0xef680aac, 0x7fffff8c, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xba64, 0xba1a, 0x40a5, 0xba40, 0x1307, 0xb2a5, 0xb24d, 0x4102, 0xbfc0, 0xa0b9, 0xb20f, 0x181b, 0x439c, 0xa7dd, 0x1c89, 0xb011, 0x43c1, 0x1c4a, 0x418e, 0x1924, 0xa84d, 0xb235, 0x1f2a, 0x422d, 0xbf81, 0xb23e, 0x4057, 0x4036, 0xb2a5, 0xbf48, 0xb013, 0x42cf, 0x432a, 0xbfb6, 0x437b, 0x436d, 0x418a, 0x41cb, 0x2910, 0x1797, 0x424a, 0xa593, 0xb0aa, 0x429d, 0x40e6, 0x45c0, 0xb201, 0x4317, 0x4349, 0xb0a8, 0x4148, 0x19d4, 0x4255, 0x412a, 0xa20f, 0x1e2b, 0xbf1d, 0x403e, 0x42b4, 0x1a38, 0x4476, 0x436a, 0xab06, 0xb0e1, 0xbf82, 0x410d, 0xb2d0, 0x401a, 0x4356, 0xba0e, 0x40cc, 0x43e6, 0xba47, 0x408c, 0x43ae, 0xbf4a, 0x42cd, 0x1b95, 0x4046, 0x4299, 0x4380, 0x27bd, 0x431c, 0x4129, 0xbacc, 0x18be, 0x4380, 0xbf7c, 0x41ec, 0x19d7, 0xbf22, 0x40e2, 0x24c3, 0xb294, 0x4430, 0xb27b, 0x4648, 0x4360, 0xb24b, 0xb003, 0x4051, 0x42e7, 0x43d3, 0x41d4, 0x1bf1, 0xb035, 0xb2d4, 0xb2e9, 0x40e4, 0x435d, 0x3083, 0x464b, 0x41dd, 0x1b19, 0xb27e, 0x4224, 0x2dc0, 0x4181, 0x1624, 0xbf7d, 0xbae9, 0x4317, 0xabcc, 0x43d7, 0x438e, 0x4254, 0xb287, 0x40d0, 0xbf4e, 0xbaf3, 0x43f9, 0x4350, 0xa956, 0x1fc1, 0x4374, 0x44e5, 0xa792, 0xb2f3, 0x412b, 0xb201, 0xb241, 0xb2d3, 0xbfc0, 0xba76, 0x4246, 0xb019, 0x40e3, 0x198a, 0x2b9a, 0x3b90, 0x46d9, 0x072a, 0xba25, 0x3d43, 0x4097, 0x41e1, 0xbfc6, 0x4205, 0x04d8, 0x40f9, 0x1cd1, 0x1f55, 0x1f1e, 0xaa31, 0xb23e, 0x4598, 0x13f6, 0x4179, 0xaf2e, 0xbfae, 0x4357, 0x1374, 0x1ad4, 0xad33, 0x4156, 0x427e, 0xbfe4, 0x44e0, 0x4331, 0x43f4, 0x42c9, 0xbf85, 0x1a24, 0xa2f6, 0x41f2, 0xa107, 0x4232, 0xbf90, 0xa2a0, 0xb23e, 0x1f9e, 0x4187, 0x1bb0, 0xb26d, 0x426f, 0xb223, 0x4088, 0xacf5, 0x405e, 0x309e, 0x41ef, 0x22b8, 0xbfaa, 0x41bd, 0xbac2, 0x1c97, 0x4673, 0xb257, 0x41bf, 0x4055, 0xb287, 0x1bef, 0x44d5, 0x1b5a, 0xb06d, 0x42f0, 0x3c79, 0x420f, 0x1cc2, 0xbf2f, 0x44ca, 0x40ec, 0x41e5, 0xba3c, 0x4118, 0xba6d, 0xa231, 0xb266, 0xae71, 0xba56, 0xa854, 0x21c1, 0x39da, 0x18f7, 0x162a, 0x0af5, 0x4246, 0x43c3, 0x1839, 0xb0f9, 0x40d0, 0x1f0f, 0x46ac, 0x186e, 0x44a1, 0x40aa, 0xbfd1, 0x404c, 0x2780, 0x43f3, 0x4391, 0xbfc0, 0x439d, 0x0446, 0xba07, 0x411e, 0x4358, 0x40a1, 0x42eb, 0x408f, 0xa392, 0x25c7, 0x4289, 0xb203, 0x4220, 0xbfe0, 0xba43, 0xa384, 0x10d1, 0xbf4c, 0xba61, 0xbf90, 0x2fe4, 0x4339, 0x419e, 0xa2e4, 0xbf90, 0xa1b8, 0xb2ca, 0x431e, 0xa96f, 0xbaeb, 0x0eab, 0xba3f, 0x42da, 0x4092, 0xba50, 0x419f, 0x42ae, 0x428f, 0xbf7e, 0x43b6, 0x3644, 0xb0e6, 0xa3fd, 0x46c8, 0x4049, 0xb22e, 0x434c, 0x1dec, 0xb233, 0xb2de, 0x42f1, 0xb2d6, 0xb2ed, 0x3db5, 0x4326, 0x4279, 0x22e0, 0x2801, 0x42ac, 0x4331, 0x4192, 0x2136, 0xbf80, 0x4358, 0xbf48, 0xb09e, 0x0395, 0xb232, 0x41d8, 0xbfb0, 0x18fd, 0xba3e, 0x1da0, 0x45d5, 0x43a3, 0x41c0, 0x4028, 0x446a, 0x00dc, 0xbfa8, 0x2ac3, 0x292e, 0x439b, 0x421c, 0x1924, 0xb25b, 0x464b, 0x427e, 0x40a9, 0xbae4, 0x42ed, 0xb0be, 0x4650, 0x4205, 0x4062, 0x400c, 0xb283, 0x40a1, 0xb26e, 0x4438, 0x4210, 0xaf94, 0xb270, 0x18ad, 0xbf3c, 0x3d88, 0x41c3, 0x42eb, 0xbf51, 0x40f4, 0xba01, 0x4119, 0x42b6, 0x13e7, 0x2c7e, 0xb248, 0x3088, 0x2d04, 0xb22c, 0x45ce, 0x006d, 0x446f, 0x1a24, 0x4059, 0x40f6, 0x4360, 0xa82e, 0xb240, 0x42e0, 0xba21, 0x2130, 0x42b4, 0xb033, 0xbf04, 0x4020, 0x4093, 0x409d, 0x0fb2, 0x455f, 0x40d7, 0x168b, 0xba34, 0x2d4c, 0x426e, 0xba7b, 0x1b47, 0xb0a4, 0x4252, 0x38dd, 0x3e12, 0xb034, 0x429f, 0x4195, 0xbfd9, 0xb2f8, 0x427a, 0x3ecb, 0xaba8, 0xb27d, 0x44a4, 0xb2d5, 0x42cc, 0x43f0, 0x4369, 0x4281, 0x2b77, 0xbaf3, 0x1827, 0x3984, 0xb008, 0xba71, 0xbaeb, 0xbf68, 0xba04, 0xbf48, 0x40da, 0xb025, 0x324b, 0x41b9, 0xb0a9, 0xb02e, 0x45dc, 0x2fa9, 0x435c, 0x1b90, 0xbf7a, 0x41da, 0x1990, 0xba5a, 0xb206, 0x40c1, 0x4188, 0x4063, 0x4182, 0xb2d3, 0xba04, 0xbad2, 0x1f08, 0x031c, 0x179d, 0x1dcc, 0x1ed3, 0xa42e, 0x416d, 0x2dae, 0x05e5, 0x3b23, 0x09b7, 0x40c8, 0x1aa2, 0xb247, 0x1462, 0x40d1, 0x4049, 0xbf77, 0xb277, 0x1eee, 0xb298, 0xbf00, 0x42bb, 0x1f09, 0x423a, 0xba7f, 0x4217, 0xbaf5, 0xba2f, 0x3e9d, 0xbf3e, 0x40c8, 0x2bf6, 0xba24, 0xba43, 0x31ec, 0x2cae, 0x4211, 0xbf21, 0x1433, 0x467f, 0x1eab, 0xb2f5, 0x437e, 0x4143, 0xba44, 0xafe7, 0x4173, 0x418d, 0xad21, 0x0b5f, 0xa2f0, 0xba57, 0xbfaf, 0xb2ef, 0x3b63, 0x4155, 0xba08, 0xb20c, 0x4565, 0x407e, 0x4006, 0x27ce, 0xba15, 0xb2f7, 0x403e, 0xbafe, 0x4385, 0xb263, 0x4092, 0x43ba, 0xb2a9, 0x43bd, 0x424f, 0x4101, 0x45e3, 0x3773, 0xbfba, 0x1dbc, 0xb2ea, 0xb242, 0x40c7, 0xb057, 0xb2ee, 0x41d9, 0xb20d, 0x4052, 0x4391, 0xa641, 0xa730, 0xb20d, 0x3c10, 0x4477, 0x3eab, 0xa994, 0x2ce4, 0xbaf4, 0xbf62, 0xb252, 0xb2d1, 0x14c9, 0xbfc2, 0x4064, 0x4185, 0x1baf, 0x4298, 0xb2ab, 0xa44e, 0x40ea, 0xa273, 0x439d, 0x44d5, 0xba28, 0x41b2, 0x0318, 0x1e74, 0x4310, 0xbf0d, 0x1b3e, 0xba2c, 0x3286, 0x412d, 0x199a, 0xa2d6, 0x41de, 0xbfb0, 0x43e6, 0xa815, 0x162b, 0x16a3, 0x429e, 0xb210, 0x130d, 0xba4b, 0xba20, 0xbfbd, 0x2828, 0x00c1, 0x425e, 0x0047, 0x4324, 0x41af, 0x2141, 0x42c6, 0x42a2, 0xad20, 0x163f, 0x4385, 0x288d, 0xb0ad, 0x40a2, 0xba7a, 0xba4b, 0xb0ba, 0x4044, 0x0501, 0x33f5, 0x1845, 0xa3c5, 0xa7d3, 0x4274, 0xb270, 0xbafc, 0x4485, 0xbf35, 0x421e, 0x41a2, 0x4250, 0x421b, 0x04da, 0xb222, 0x1cd3, 0x0958, 0x4085, 0xa75d, 0x1d39, 0x059a, 0x43cb, 0xba2c, 0x40e8, 0x41e4, 0x4664, 0x40d9, 0xbf18, 0x43f1, 0x4350, 0x4267, 0x4027, 0xbf21, 0x1afb, 0x1264, 0xb258, 0x4575, 0x4124, 0xb215, 0x466d, 0x1870, 0x4319, 0xaa64, 0xbfc1, 0x4376, 0xb2fb, 0x437b, 0x438f, 0xb011, 0x2417, 0xb206, 0xbf00, 0xbaf8, 0xbfc5, 0xba1e, 0xa11d, 0xb27d, 0xadf2, 0x1b0f, 0x40c8, 0xbf05, 0x46d4, 0x43b0, 0x43e4, 0x4008, 0x426d, 0x1f5a, 0x423b, 0x1b37, 0x1df6, 0x2bde, 0xbadc, 0x1ff8, 0xbf3b, 0x43a8, 0x41de, 0x4239, 0x1def, 0x4184, 0x43ee, 0x431b, 0x41f0, 0x43f5, 0x4290, 0x422c, 0x3066, 0x19fd, 0xaeba, 0x409f, 0x1f41, 0x2c3e, 0x1f68, 0x40c7, 0x4146, 0x2ff8, 0xb2f6, 0xb026, 0xba41, 0xb220, 0x443f, 0x205a, 0xb2e6, 0x44cb, 0xbf3b, 0xbf90, 0xb2de, 0x0b90, 0x0e8f, 0xba26, 0x06c0, 0xbaed, 0x0acd, 0x408e, 0x31fb, 0x048f, 0x4577, 0x40db, 0xbfd5, 0x4181, 0xaa2b, 0x16a1, 0x401d, 0xbf19, 0x2bed, 0x4053, 0x3875, 0xb265, 0xad06, 0x4044, 0x11f9, 0xb289, 0x4602, 0xb0da, 0x461b, 0x40a1, 0xba3d, 0x13fd, 0x41a4, 0x41b3, 0x41eb, 0xb000, 0x4385, 0x46ec, 0x2780, 0x45be, 0x33bd, 0xbf34, 0x1963, 0x4266, 0x422e, 0x1c54, 0x1b2a, 0xa37d, 0xba7c, 0xba54, 0x4221, 0xba78, 0x1ef2, 0x425a, 0x40c8, 0x24fa, 0xbfcb, 0xb237, 0x420a, 0xb0bb, 0x1eeb, 0x40c3, 0x0071, 0x0ce2, 0xbf2e, 0x4297, 0x4227, 0x432c, 0xb01b, 0xae0a, 0xa328, 0x09dc, 0xb2cb, 0x4014, 0x1897, 0xbf54, 0xb07e, 0x234a, 0x284d, 0x44b1, 0x46fb, 0xb26b, 0x0044, 0xaf0c, 0xa318, 0x43b4, 0xa43d, 0xb2b1, 0x4082, 0x42ab, 0x108f, 0x41d8, 0xba0c, 0x4318, 0xbf12, 0x31db, 0x4296, 0x4376, 0x466e, 0x1702, 0xb08c, 0x1446, 0xb2c3, 0xb262, 0xb06b, 0x33ac, 0xb285, 0x1c39, 0x0554, 0xb254, 0x4010, 0x0807, 0x406d, 0x3fd4, 0xb2f0, 0x41d8, 0xb03d, 0xbf31, 0x0072, 0x4300, 0xabf2, 0x3778, 0x41ce, 0x425d, 0xa931, 0xb088, 0x104d, 0x1902, 0xb237, 0x1adb, 0x40fb, 0x4310, 0x03b3, 0x409c, 0xb0af, 0xb2e6, 0x06ba, 0x439d, 0x41c4, 0xbfc5, 0x4290, 0xb086, 0xb274, 0x1ff7, 0xbfca, 0x179a, 0x24c7, 0x407e, 0xba61, 0xbf1f, 0xb281, 0x41b6, 0x2aec, 0x41f5, 0x40fe, 0x27f0, 0x03f3, 0x1a45, 0x2020, 0x0c37, 0x18c6, 0x4420, 0xb225, 0x42fd, 0xbf87, 0x429e, 0x3085, 0x4240, 0x18df, 0x3b5f, 0x3e59, 0x42ba, 0x4369, 0x093c, 0x43df, 0xb2a3, 0x1f17, 0x427c, 0x1c1f, 0x17ae, 0x188a, 0x418f, 0xa2c1, 0x1b58, 0x4160, 0xbff0, 0x404f, 0x1985, 0xbf90, 0x1915, 0x434c, 0xbf11, 0x4177, 0x408b, 0x0d62, 0x186b, 0x1168, 0x18e3, 0xb235, 0x3037, 0x4695, 0xba1b, 0xa33d, 0xba31, 0xb2c3, 0x40f3, 0xb0c4, 0x08b1, 0x4207, 0x4398, 0x41d5, 0x2398, 0x440a, 0x2c54, 0xba66, 0x1c94, 0xbaf5, 0xbf9c, 0x437b, 0xb2f5, 0x4674, 0x40c3, 0x435f, 0x41d5, 0x4233, 0xac76, 0xbae1, 0xba0c, 0xba07, 0x46ba, 0xb220, 0x22a5, 0xb25d, 0xb204, 0x41f3, 0x04e6, 0x46b9, 0x43a2, 0x41bd, 0x40b6, 0x1d71, 0xbfb1, 0x41cf, 0x32a2, 0x1e0c, 0x43e4, 0xb05c, 0xbf8c, 0xb273, 0x4072, 0x462b, 0x2863, 0x415a, 0x4000, 0xb22a, 0x42f5, 0x41c1, 0xa19b, 0x436f, 0xa5ad, 0x0a9d, 0x40e3, 0x4066, 0xa244, 0x41c8, 0x40a8, 0x43e4, 0x4301, 0xbf64, 0x36fb, 0x3220, 0x45e5, 0xa505, 0xb222, 0xb2a2, 0xb09c, 0x4418, 0xb02c, 0x1dcc, 0x40ad, 0x0709, 0xa73b, 0x4374, 0x33ea, 0xbad1, 0xbfa2, 0x4021, 0x4082, 0x4449, 0x41b6, 0x1df3, 0x42a8, 0x0838, 0x41e1, 0x4104, 0x218c, 0x4384, 0x2d58, 0x006d, 0x4561, 0x4374, 0xb299, 0x4177, 0x4064, 0xa457, 0x3187, 0x1b34, 0xbf63, 0x4261, 0x4158, 0x2bf6, 0x435e, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xddc07271, 0x377926ce, 0x9a93827d, 0xcbef0283, 0x5bc182ba, 0xa9bfdede, 0x4f15e679, 0xd23d8725, 0x59c353e8, 0x9a2b180a, 0x2b318b54, 0xd61dea1b, 0x8cc70559, 0xca220005, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x00000000, 0x0000008d, 0x00000000, 0x00000006, 0xffffe6b3, 0x00000000, 0xfffffffa, 0x000018ab, 0xc35cf61b, 0x00010000, 0x00010000, 0x00001630, 0xad4c19c4, 0x00001aa0, 0x00000000, 0xa00001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x418a, 0x18b4, 0xb262, 0x1dbf, 0xb028, 0x2f39, 0xba6e, 0x1e05, 0x41b8, 0x1a37, 0xbfc2, 0x3a0d, 0xaacb, 0x435a, 0x411a, 0x43c3, 0xbfc1, 0x4241, 0x1c8d, 0x46db, 0x2c85, 0x420a, 0xb29c, 0x35a9, 0xad4a, 0xb007, 0x433d, 0x42a9, 0x2e9d, 0x42dc, 0xba41, 0xba2b, 0xb236, 0x40ad, 0x2a1f, 0xa550, 0xb2ae, 0x0dd7, 0xbaea, 0x456a, 0x4299, 0x40d9, 0xa992, 0xbf0a, 0xbaff, 0xbad1, 0x4667, 0xaf90, 0x4281, 0x4310, 0xba51, 0xbf7c, 0x30f1, 0x43ff, 0x41c9, 0xb2f2, 0x412e, 0x43f1, 0xba1d, 0x45da, 0x275c, 0xb2d2, 0x337c, 0x4200, 0xbfb5, 0xb292, 0xba70, 0x4174, 0x40b0, 0x41e1, 0x42ed, 0x41e6, 0x439f, 0x224e, 0xba2c, 0x12e8, 0x411c, 0x1dea, 0x40f4, 0x4199, 0x43ac, 0x40fb, 0xb23b, 0x0f39, 0x4093, 0x45cb, 0x431a, 0x13c1, 0xbf73, 0xb271, 0x398f, 0xb018, 0x402a, 0xb002, 0xaac0, 0x1bc9, 0x2243, 0x0ec2, 0x3f51, 0xbf1b, 0x408c, 0xb0d0, 0x1c15, 0x03fc, 0x39ff, 0xb2a6, 0x02ca, 0x46ba, 0x3b17, 0x19ea, 0x40ef, 0x064f, 0x43f8, 0xbfda, 0x4268, 0x4074, 0x35bd, 0x44fa, 0x1ceb, 0xbf2d, 0x4325, 0x4157, 0x415c, 0x431a, 0xbaf1, 0xa1b0, 0x1b83, 0x01c9, 0x4306, 0x42e5, 0x172c, 0x0b7d, 0x405a, 0x4140, 0x4393, 0x4056, 0x1e6e, 0x414e, 0x40f4, 0x43a2, 0xb292, 0xbf3f, 0x4445, 0x190a, 0x431b, 0x1b32, 0xbf59, 0x4625, 0xad4e, 0x45bb, 0x446d, 0x44cc, 0x195c, 0x4619, 0xafd9, 0x0a89, 0x23e7, 0xa8ec, 0x425c, 0x41d0, 0x362d, 0x4257, 0xb036, 0xbf39, 0x44ec, 0xb00c, 0xb214, 0x1f74, 0xba33, 0x4574, 0x44f9, 0x41aa, 0x3e33, 0xbf7e, 0xb089, 0x4260, 0x44fd, 0xbfa8, 0x4262, 0x4348, 0xa90d, 0xaee0, 0xbf97, 0xba70, 0xba28, 0x4370, 0xb2c6, 0x41d4, 0xb21b, 0xb281, 0x43fc, 0xbfd3, 0x1a9f, 0xbac9, 0xbf60, 0x3f4b, 0x25a2, 0x4149, 0xbaeb, 0x4009, 0x238f, 0xba6d, 0xba55, 0xbfa0, 0x3191, 0x22ce, 0x3c36, 0xa50f, 0xaba5, 0xb0dc, 0x4216, 0xb2ff, 0x4322, 0x40fa, 0x418f, 0xba57, 0x4553, 0x1f57, 0xbf89, 0x0fe2, 0x3db5, 0xb201, 0x4111, 0xa77b, 0x42f0, 0x18c1, 0xbfa1, 0x1797, 0xbf80, 0x415c, 0xa873, 0x1a6f, 0x43c4, 0x41dc, 0x42d8, 0x4107, 0x40fa, 0x44d3, 0x401b, 0x0e14, 0xbfe0, 0xbacb, 0x0018, 0xb029, 0x403e, 0x4259, 0x408c, 0xb021, 0x0d72, 0x409f, 0x3e97, 0x0710, 0x422a, 0xbf23, 0xb08d, 0x1777, 0x2940, 0x40b2, 0xbae8, 0x1d10, 0x2a49, 0x24f4, 0x4297, 0x44d2, 0x405b, 0xbfb4, 0x4036, 0x3cc2, 0xbac2, 0x4287, 0x408f, 0x04cf, 0x4402, 0xbf36, 0x18a3, 0x1153, 0x4222, 0xb058, 0x403b, 0x424e, 0x1f0a, 0x419f, 0x43cd, 0xba14, 0x403f, 0xbae3, 0x1a87, 0xb275, 0x4053, 0x433d, 0xb2b7, 0x20a2, 0x40ed, 0x425c, 0x405e, 0xb2f0, 0x169c, 0x43de, 0xb2b4, 0xb030, 0x401e, 0xbf75, 0x4329, 0x1f63, 0x40ed, 0xb2e5, 0xb21e, 0x2dae, 0x13ff, 0xb246, 0x19fb, 0x43a2, 0x4179, 0x4304, 0x25a9, 0x1d4e, 0x1804, 0xb2d8, 0x41ba, 0xb28c, 0x4264, 0x4237, 0x27ff, 0x400a, 0xb00e, 0xb0c7, 0x42af, 0x33d3, 0x0b19, 0x42f9, 0xbf29, 0x41be, 0xb20e, 0xba0a, 0x25c7, 0x4215, 0x1b76, 0x16d0, 0xb08c, 0xb299, 0x4305, 0xbf94, 0xa65b, 0x02f3, 0xb226, 0x40c5, 0x185a, 0x42a4, 0xbf92, 0x1a1f, 0xb29b, 0x40d5, 0xba22, 0xb27b, 0xb0ac, 0x2ee1, 0x424e, 0xb2f8, 0x4299, 0xb26d, 0x0c8c, 0x4350, 0x4191, 0x1a7a, 0x439f, 0x1f7b, 0xb0f6, 0x442d, 0x0997, 0xbf36, 0x418d, 0xba3f, 0xb096, 0x0faa, 0xb201, 0x4302, 0x1919, 0x1d75, 0xb2f2, 0x4288, 0xbf00, 0xb203, 0x413f, 0x40ea, 0xbfd0, 0x198b, 0x4117, 0x439c, 0x09e7, 0xb277, 0x0cce, 0x43a1, 0xba4e, 0x466d, 0x18cf, 0x1e3e, 0xba56, 0xad9d, 0xbf71, 0x428e, 0x0a44, 0x434f, 0x4335, 0x401c, 0x403a, 0xbac5, 0xb0eb, 0xb075, 0x1ee0, 0x300b, 0x419a, 0x4152, 0x1833, 0x412f, 0x42cd, 0x42b6, 0x0e30, 0xa3fc, 0x40d4, 0xb226, 0x409d, 0x0a68, 0x3bb8, 0x1b5d, 0x1a26, 0xb2bb, 0xb290, 0x4294, 0xbfb8, 0x1d4a, 0x431a, 0xb071, 0x41f8, 0x42d3, 0x23e3, 0xb2c7, 0xb217, 0x436d, 0xba56, 0xb2c6, 0x417d, 0x1b54, 0xba72, 0x40eb, 0x0b91, 0xbf2f, 0xb242, 0xa679, 0x41bd, 0xba15, 0x4394, 0x419e, 0x42ad, 0x2bbf, 0x405e, 0x4128, 0x3858, 0x46cb, 0xbf79, 0x40e6, 0x42e0, 0x411c, 0x23b9, 0x42c2, 0x42f9, 0x00d5, 0x435b, 0x08e8, 0x2279, 0xbf94, 0xad4a, 0x4014, 0x40b4, 0x21dc, 0x435a, 0x46b9, 0xb2c5, 0xa749, 0x43b4, 0x40d2, 0x4408, 0x43d5, 0xbf60, 0x43d3, 0x408f, 0x2e00, 0x1ec6, 0x4186, 0xa036, 0x4147, 0xb227, 0xbf9d, 0x1a4b, 0x418f, 0x44b2, 0x40d9, 0xbf37, 0xb2ac, 0x1173, 0xb0e2, 0x409d, 0xb23e, 0xb230, 0x42da, 0x4081, 0x413d, 0xafe1, 0x1d08, 0x408b, 0x412a, 0x334a, 0x4023, 0x4028, 0xb014, 0xba0d, 0x05a5, 0x118a, 0x4090, 0x40cc, 0xb2da, 0x2c35, 0x4057, 0x40ee, 0x408c, 0xb259, 0x3bb3, 0xbf3d, 0x4323, 0x4384, 0xb286, 0x43a6, 0x41e6, 0x2ac2, 0x434b, 0x2a9c, 0x40bc, 0x43c4, 0x1f48, 0x2920, 0xab0a, 0x439e, 0xb0c9, 0xbf7a, 0x4081, 0xba67, 0xbf70, 0x42bc, 0x2f3c, 0x41b1, 0xa46f, 0xbace, 0x3c3d, 0xb2f1, 0x43c7, 0x4049, 0x11d4, 0xbf45, 0x4042, 0x1115, 0x4113, 0xba1b, 0xa069, 0x4571, 0xbacd, 0x1aee, 0x4499, 0xb06a, 0x42ac, 0x4069, 0xa4cd, 0x402a, 0x416b, 0x429c, 0x4362, 0x1314, 0x38c5, 0x400a, 0x4122, 0x3473, 0x413a, 0xba66, 0x27a8, 0x42ca, 0x4369, 0xbfce, 0x4268, 0xa867, 0x4072, 0x04f5, 0xba27, 0x43b5, 0x3c3f, 0xbf25, 0xba45, 0x439c, 0x1cd7, 0xb203, 0x45a3, 0xba2b, 0x41bd, 0x4013, 0x41a0, 0x43cd, 0xb2c4, 0xba18, 0x4097, 0x1507, 0xba2e, 0xaa23, 0x417a, 0x42fc, 0x1df3, 0x40c1, 0xba68, 0x41b6, 0xa65b, 0x438f, 0x1cbe, 0x01aa, 0xbf5d, 0x42a5, 0x070b, 0x423f, 0x465f, 0x4350, 0xa044, 0xb287, 0xb289, 0x42ad, 0x40fc, 0xafbe, 0x0b8c, 0xb2aa, 0x40d3, 0x463c, 0x4314, 0x1d39, 0x40e1, 0x1d12, 0x0eb4, 0xbf23, 0x43b0, 0xb2ca, 0x1b06, 0x414d, 0x1813, 0xb2f0, 0x1cec, 0xa02b, 0x454e, 0x4111, 0x19e3, 0x41f2, 0x4228, 0xbada, 0xb20f, 0x4342, 0x418c, 0x435d, 0x2d58, 0xb2af, 0x429b, 0x4020, 0xb2ae, 0x198b, 0x4256, 0xbf7c, 0xbaf9, 0xa179, 0xb012, 0xb2ed, 0x466e, 0x42b8, 0xb003, 0xb0c2, 0x429c, 0xb2fe, 0x00cb, 0x4440, 0x428e, 0xb033, 0x467e, 0x419e, 0x4396, 0xb261, 0x29a3, 0x434c, 0x1f85, 0x2734, 0x4068, 0xbf52, 0x44cb, 0xbac6, 0x4374, 0xb013, 0x42b7, 0x3f89, 0x163d, 0x1db9, 0xbf19, 0x430c, 0x4326, 0x440e, 0xba28, 0xba31, 0x43e2, 0x41b9, 0xad58, 0x4108, 0x1ad5, 0x428b, 0x1393, 0x4290, 0xb2b9, 0x403d, 0xb23d, 0xb00e, 0xaf72, 0x420e, 0x438e, 0x1298, 0x40db, 0x41ce, 0x40b8, 0x1da4, 0xb085, 0x420b, 0xbf18, 0x025d, 0x1b8b, 0x1038, 0x4191, 0x1e3f, 0x3912, 0x4015, 0x1b40, 0xba0a, 0xb0a5, 0x2dfa, 0x405a, 0xb2dc, 0x419f, 0x020d, 0xbf03, 0x420b, 0x4185, 0xbaca, 0xb0a1, 0x32e6, 0xbae1, 0x40ad, 0x0c4c, 0x426a, 0x442d, 0x0cbc, 0x3f03, 0x4220, 0xb025, 0xb05d, 0x0a54, 0x4253, 0x40e4, 0x0628, 0xba74, 0x4130, 0xb201, 0x3d5a, 0x1f92, 0x42fb, 0x2807, 0x3fd0, 0x434e, 0xbfd5, 0x433e, 0x4171, 0xb0c9, 0x4389, 0x0633, 0xbadd, 0x4468, 0xae94, 0x1efb, 0xb0ba, 0x4066, 0x18b4, 0xbf9b, 0x0986, 0x4373, 0x3dfe, 0xb2cf, 0x4276, 0x37fc, 0x4154, 0xb03b, 0x34e0, 0x1e93, 0x4394, 0x4261, 0x43ce, 0xb0b7, 0xb234, 0xb242, 0x406a, 0x42e3, 0x2ae2, 0x1b93, 0xbf96, 0xb07a, 0x4317, 0x1e51, 0x42e5, 0x430d, 0x324b, 0x4354, 0x410d, 0x41b8, 0x1eb2, 0x4035, 0x06cb, 0x4353, 0x43ca, 0xa102, 0x203d, 0x414e, 0xba16, 0x1b5a, 0xb2d4, 0xb292, 0xbf2d, 0x411e, 0xba6a, 0xb29e, 0x430b, 0x418f, 0xb017, 0x3d40, 0x4271, 0x43ca, 0xb20d, 0x42d1, 0x1ae9, 0x419d, 0x4021, 0x466a, 0x2bb4, 0x1ef8, 0x24be, 0x4146, 0xbf77, 0x423e, 0x4075, 0x4145, 0xb23b, 0xbae8, 0x0db2, 0xb20b, 0xb2bf, 0x0cbf, 0x183f, 0x0f19, 0x21e3, 0x4231, 0x46f9, 0x400b, 0xb0bf, 0x405f, 0xb047, 0x3f77, 0x1c33, 0x3d13, 0x3476, 0xb218, 0xbf7c, 0x4060, 0x43b8, 0xbf80, 0xb0c4, 0x40e7, 0x421c, 0xbf64, 0xb0de, 0x436e, 0x41b5, 0xb0bc, 0x4065, 0x40f4, 0x2bb9, 0xaf92, 0x4624, 0x1e4c, 0x4318, 0x41e2, 0x0adf, 0xb2d4, 0x1e99, 0x1e3b, 0x46f0, 0xa8b3, 0xbf7e, 0x32ac, 0x2b7a, 0x41fb, 0x40fd, 0xba62, 0xbf21, 0x0d64, 0x42b0, 0x3a8c, 0x4270, 0x1e40, 0xbfd4, 0xb298, 0x4084, 0x43e2, 0xbaf3, 0xb256, 0x422d, 0x2965, 0xb29d, 0xb260, 0x41ec, 0x41fb, 0x071c, 0xbf4e, 0x403a, 0x41c6, 0xbad3, 0x41b6, 0x4651, 0xb2c7, 0x44e2, 0x43fd, 0x421f, 0x09c3, 0x4018, 0xbaf3, 0xbff0, 0x106b, 0x40bc, 0x46bc, 0x41ba, 0x41be, 0xb297, 0x4270, 0x4369, 0xbf6c, 0x46b4, 0xb21a, 0x0857, 0x37bc, 0x41c4, 0x1cc5, 0xaddc, 0xb014, 0xb2e3, 0xb0fb, 0x1f42, 0x06c9, 0x412d, 0xa6cd, 0xb2bc, 0x40a6, 0xb0e1, 0x4174, 0xb29f, 0x4200, 0xbf55, 0xba34, 0x4080, 0x434f, 0xb249, 0x43f7, 0x44e1, 0xa252, 0x409d, 0x42bc, 0x4195, 0x4274, 0xba0f, 0x41ce, 0x43e9, 0x45e3, 0xb21b, 0x2ffd, 0xa5c2, 0xba7b, 0x4068, 0x4062, 0x0ea1, 0xbf68, 0x0330, 0x4041, 0x2f6d, 0x0761, 0x4354, 0x3531, 0x40c5, 0xac1f, 0x0a0d, 0x4104, 0x4338, 0x428a, 0x4293, 0xba73, 0x0b8b, 0x0958, 0xbfa3, 0x00ff, 0x43b3, 0x1bca, 0x4146, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xca173107, 0x59131982, 0xd9386e54, 0x24fdf592, 0x3f36eb46, 0x7926dfe1, 0x64131655, 0x72501df8, 0xd923625a, 0xfe222377, 0xf0f7cf65, 0x2d44a91d, 0x61d44f3a, 0x2f3c5836, 0x00000000, 0x300001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0xffffffc0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0x00000000, 0x000016c0, 0x8f32eca2, 0xfe2234ce, 0x00000000, 0x2f3c6602, 0x00000000, 0x400001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x23f8, 0xbfae, 0x415e, 0xbf00, 0x00dd, 0x1949, 0xb2d0, 0x45e8, 0x4179, 0x1814, 0xb001, 0xb239, 0x2993, 0xbfd8, 0x42ec, 0x3367, 0xbf7c, 0xb2a5, 0x3158, 0x4273, 0xbf16, 0x4099, 0x2952, 0x1ff7, 0xbfa8, 0x4059, 0xac74, 0x413a, 0xb03e, 0x1e71, 0x2513, 0x4293, 0x1e93, 0x4321, 0x16a0, 0x410e, 0x1d63, 0xb048, 0x43eb, 0x417c, 0xbf7f, 0xab52, 0xaa4f, 0x4267, 0xb2bd, 0xb227, 0x40ce, 0x0c3d, 0x09b8, 0x4218, 0xa803, 0x1b80, 0x4037, 0x185b, 0x3a2a, 0xbfdc, 0x4345, 0x42e2, 0x2885, 0xb0f7, 0xb2ca, 0x46aa, 0x2e1d, 0x436c, 0xba12, 0x4227, 0x0c58, 0xb075, 0x08a1, 0xba6b, 0xada7, 0xb034, 0xa86d, 0x426e, 0x428f, 0x4218, 0x4389, 0x1a12, 0xaab7, 0xbfe0, 0xb299, 0xbf3e, 0x41c7, 0x4200, 0x40cd, 0x4073, 0xb00b, 0x1122, 0x4270, 0x1516, 0xb0fe, 0x306c, 0x412d, 0x40ce, 0x3c5c, 0x1fab, 0x43bb, 0x4446, 0x405e, 0x40c6, 0xb03b, 0xba37, 0x403e, 0x46fc, 0xbf15, 0xb262, 0x1b66, 0xb26f, 0x4461, 0x22a8, 0x4276, 0x4289, 0x42e4, 0xbaf7, 0x404b, 0x166d, 0x44d9, 0x43fe, 0xb0eb, 0x43d9, 0xaa3b, 0x0b0e, 0xbf0a, 0x332e, 0xba4f, 0x1f48, 0x446d, 0x2fcc, 0x4009, 0x431a, 0x4006, 0x1f9b, 0x42c6, 0xb01d, 0x17a0, 0x4369, 0xbfc0, 0x07be, 0x4026, 0xb00f, 0x4412, 0x30d4, 0x4089, 0x461c, 0x432e, 0xbf36, 0xba17, 0x40ab, 0x432a, 0xa280, 0x1f32, 0xb0e4, 0x23c0, 0x1a9e, 0x4080, 0xba1f, 0xba5e, 0x2887, 0xad8b, 0x282d, 0x1882, 0x41ab, 0xb2e0, 0xb2e7, 0x4373, 0x41c2, 0x41ed, 0x45eb, 0xb00f, 0xb07f, 0xba27, 0xbf63, 0x1bcc, 0x4098, 0xa2a7, 0x189c, 0x4338, 0x4308, 0xbace, 0x1ff4, 0x40fd, 0x4279, 0x0ae8, 0x3b6d, 0x4315, 0x4654, 0x00d0, 0x425e, 0x40ef, 0x4357, 0x192b, 0xbf94, 0x43ce, 0x37fa, 0x1c40, 0x2cbf, 0x26f3, 0x1fe8, 0xb227, 0xabe3, 0x401e, 0x310b, 0x40ce, 0x4223, 0xbf65, 0xba30, 0x4352, 0x4241, 0x0f84, 0x1941, 0x4098, 0x40f7, 0x409a, 0x4081, 0x400c, 0x41da, 0xb256, 0x45cd, 0x1f42, 0x42f2, 0xbaef, 0x0335, 0xa8e0, 0x429a, 0xba0e, 0x40a3, 0x3d57, 0x439d, 0x406f, 0xbae3, 0xbfc6, 0x2f1b, 0xb2f5, 0xa396, 0x41ff, 0x40e4, 0x4401, 0x43f9, 0x4169, 0x40cb, 0x1f8b, 0x4335, 0xb25a, 0x4407, 0xba60, 0x4286, 0xa2ee, 0x4027, 0xbf3f, 0x1c86, 0x435c, 0x40bc, 0x41cc, 0x1bda, 0x2ad1, 0x1c02, 0xb04d, 0x42cb, 0x4084, 0x1dd2, 0xb2fd, 0xb0ac, 0xb280, 0x1dad, 0xb06c, 0x43a1, 0x4310, 0x40e4, 0x435f, 0xb2be, 0x4201, 0xbf7b, 0x1e70, 0x0de8, 0x02bf, 0x407d, 0x45d2, 0x1d92, 0x43c8, 0xbfa4, 0x401d, 0x431e, 0x4030, 0x40a1, 0x1fb9, 0x2bdc, 0xbf44, 0x423c, 0x41f8, 0x0525, 0xbf03, 0xbae5, 0x4316, 0x2fa1, 0xb042, 0x438d, 0x3f53, 0x424a, 0xa7ee, 0x4470, 0x0dc7, 0x4408, 0x18e7, 0xb2f9, 0xb01f, 0xa1af, 0xbf28, 0xbfc0, 0xb26b, 0xa1cc, 0xbf6c, 0x0855, 0x3e49, 0x45e1, 0x41a7, 0xba1b, 0x2d90, 0x4216, 0xb215, 0xb224, 0x4136, 0xb0fb, 0x4676, 0x1127, 0x4151, 0xba11, 0xb287, 0xb2e4, 0x4074, 0x4313, 0x060e, 0x1dc1, 0xb213, 0x4000, 0x42ca, 0x4085, 0x264b, 0xbf84, 0xa3c4, 0xb045, 0x0f04, 0x425a, 0xba22, 0x414a, 0x4155, 0x40ae, 0x12ca, 0x3ed5, 0xb0e1, 0x4080, 0xba16, 0xb04b, 0xba70, 0xbfa0, 0x40d5, 0x402b, 0xbaf1, 0x46b8, 0x1f45, 0xbf60, 0x423d, 0xb2ee, 0x2b14, 0x1e82, 0xbfc9, 0x1e6e, 0x415b, 0xba48, 0x2cf4, 0x43ee, 0x4322, 0xba57, 0xba68, 0x434d, 0xba0d, 0x31f2, 0xbf86, 0xa72b, 0x43d1, 0xb22c, 0xb29a, 0x3a3a, 0x433b, 0x41ad, 0x401a, 0x1df5, 0x0084, 0xa1db, 0xbfe0, 0x4091, 0xb294, 0x43f7, 0xbfde, 0x1d6a, 0x40c1, 0x4052, 0x14ba, 0x4315, 0x1b64, 0x4370, 0x1ef0, 0x0bf2, 0xb09b, 0xbf2e, 0x4260, 0xba0d, 0x0a68, 0xbfd0, 0x4197, 0x1e0a, 0xb204, 0x4282, 0x1afb, 0x4369, 0x438d, 0x41b4, 0x40e6, 0xba10, 0xb2e6, 0x436f, 0x40f8, 0x4207, 0x1986, 0x4342, 0xa33e, 0xb2e6, 0x1fe0, 0x402c, 0xbf48, 0xb03e, 0x40df, 0x0251, 0x1e47, 0x42dd, 0xb2d3, 0x1c88, 0xbf42, 0x36f1, 0xbaf1, 0x15ed, 0x433c, 0xbac4, 0xb0aa, 0x4315, 0xb242, 0x1fcd, 0x40b2, 0x420b, 0xb0e1, 0xba7c, 0xa872, 0x2966, 0xba76, 0x4635, 0x438b, 0x40b4, 0xbf80, 0xba5a, 0xbf80, 0x4350, 0xbfcf, 0x3a60, 0x45f4, 0x21f6, 0x2fc8, 0x425f, 0x2511, 0x42cf, 0x1945, 0x2264, 0x40ab, 0xbf8e, 0xb27e, 0xba02, 0x415f, 0xbfd7, 0x1026, 0x40a9, 0x1a50, 0xaca5, 0x4569, 0x1a72, 0x1ca3, 0x407a, 0xbac3, 0x402a, 0x1d13, 0x1ba5, 0x43bb, 0xb2fa, 0x3dc5, 0xb25f, 0xb2cd, 0x1eda, 0xba67, 0x39d3, 0x16ad, 0x1c10, 0x0b53, 0xb225, 0xbf55, 0xb20a, 0x29be, 0xb2cc, 0x43f5, 0x0c83, 0xb21f, 0x439e, 0x45d1, 0xb2b1, 0x0286, 0x441f, 0x0eb2, 0xbae9, 0xba76, 0xb2c4, 0x18f1, 0x40f6, 0xb2c9, 0x052d, 0xbaf3, 0x427f, 0x0215, 0x43ba, 0x4561, 0x14e4, 0xba1e, 0x41b5, 0xaf51, 0x42a3, 0xbf52, 0x4037, 0x410b, 0xba44, 0xbf70, 0x2dee, 0x005e, 0x4181, 0x1d78, 0x0559, 0xbf48, 0xb0ef, 0x40d1, 0x1bc3, 0x4012, 0x4613, 0x0053, 0x4351, 0xb2bd, 0x4381, 0x1a6a, 0x45c6, 0xb200, 0x4443, 0x08ef, 0xbfaf, 0x425e, 0x42e9, 0x437d, 0x10d0, 0xb25b, 0x441c, 0xbad8, 0x4263, 0x2561, 0xb02c, 0xbaf6, 0x428d, 0xb2d5, 0x406d, 0x4015, 0xb0dd, 0x45ac, 0x43d0, 0x365f, 0xb0df, 0xb2c3, 0x1829, 0xb0b3, 0x42e6, 0x02a5, 0x46d2, 0xbf0c, 0x4132, 0x409e, 0x30b6, 0xb25a, 0x42ef, 0x42f1, 0x0405, 0x41b2, 0x41e6, 0xb254, 0x4107, 0x40c6, 0x41a8, 0x0bec, 0x4134, 0xa686, 0xa857, 0xbf04, 0xab3f, 0xbfb0, 0xbf16, 0x4240, 0x42f4, 0xb2fb, 0xb2db, 0xb0f2, 0x197c, 0x0476, 0x406d, 0xb265, 0x40a1, 0x4389, 0x433e, 0xba5f, 0x41e8, 0x0e2f, 0x42f7, 0x4088, 0x3609, 0x24cb, 0x06bb, 0x428c, 0x1544, 0xbf19, 0xba5b, 0xba74, 0x4317, 0x42cc, 0xb261, 0xab8d, 0xa8bf, 0x003f, 0xa63e, 0xba06, 0x4061, 0x1a1d, 0x1baa, 0xbfb5, 0x42e0, 0x4273, 0xba55, 0xa83f, 0x46e0, 0x4324, 0x4556, 0x40e0, 0x421d, 0x2fcd, 0x42b4, 0x3700, 0xbae7, 0x19a2, 0xba1c, 0xba30, 0xb269, 0x1717, 0x413f, 0x1d62, 0x412e, 0x1196, 0x1c42, 0xbf66, 0x4017, 0x4238, 0x4351, 0x405a, 0x28e5, 0x29e7, 0x4555, 0x4052, 0x4356, 0x42bb, 0x079e, 0x42cc, 0xb245, 0xba69, 0x41c6, 0x4158, 0x3c55, 0x1ea8, 0xb25d, 0xbfdb, 0xbae1, 0x2de6, 0x19fe, 0x1b24, 0x4076, 0x46f1, 0xb020, 0xbadf, 0x4373, 0x4332, 0xacca, 0xb072, 0x440d, 0x445c, 0x192d, 0x317a, 0xbf48, 0x2038, 0x08bd, 0xaf9f, 0x0031, 0x4336, 0x15e1, 0xbfb5, 0x4387, 0xbacb, 0x4379, 0xb215, 0xb254, 0xbf7c, 0x19bc, 0x04e4, 0x4574, 0x432f, 0xb279, 0xbf6b, 0xbfd0, 0x2ec6, 0x41a4, 0x2665, 0xb0d5, 0x40e9, 0x435a, 0x4064, 0x1f9b, 0xb2ca, 0x0bee, 0x3d64, 0x3530, 0xbada, 0xa670, 0x42d5, 0x4200, 0x42f5, 0x42b8, 0xbad3, 0xba31, 0xbf71, 0x1cb2, 0xb205, 0x41eb, 0xba50, 0x4126, 0xbfad, 0xb0d7, 0x0788, 0xba09, 0x4246, 0x464a, 0x07d1, 0x438e, 0x4336, 0x2594, 0x3309, 0x4373, 0x4324, 0xb097, 0x428b, 0xbf90, 0x40e8, 0xa8ec, 0xbf3a, 0x0a6a, 0x2d9b, 0x04d6, 0x407d, 0xb233, 0x10b7, 0x40e0, 0x42a7, 0x43fd, 0xa709, 0x19a8, 0x40c3, 0x1b39, 0x19ea, 0x4492, 0x4046, 0xbae9, 0xba67, 0xaee3, 0x435c, 0x43ef, 0x409e, 0x4397, 0x3a1d, 0xbfa0, 0x4158, 0x4135, 0x1f07, 0xbf25, 0xb24f, 0x428f, 0xb2c9, 0x411e, 0xbf00, 0xba6c, 0x26ae, 0xb00f, 0x35f8, 0x419c, 0x1ea4, 0x42d4, 0xb207, 0x1611, 0x4399, 0xbf5c, 0x4283, 0x07d5, 0x10a8, 0x4387, 0x42db, 0xb259, 0xbf04, 0x1f26, 0x413c, 0x40b0, 0x4389, 0x1526, 0x212f, 0xbf63, 0xb0d6, 0xbad7, 0x33b4, 0x4322, 0x4317, 0x4188, 0x43eb, 0x40ea, 0xbfe4, 0x0285, 0x22a7, 0x4291, 0xbaf8, 0xb0ef, 0x438a, 0x1dd5, 0x4344, 0x266e, 0x41ca, 0x4155, 0x430a, 0xb2b7, 0x3f6f, 0xba27, 0x4372, 0xba32, 0x291c, 0x129c, 0xbfb2, 0xac6e, 0x4046, 0x4248, 0x4338, 0x12c1, 0xb227, 0x4168, 0x0f8e, 0x4038, 0x3caa, 0x43b0, 0x1a4b, 0xbf33, 0x21dd, 0xbae6, 0x1bab, 0x43a6, 0xbada, 0x1c96, 0x40a7, 0x4065, 0xb264, 0x1b34, 0x103f, 0x10e0, 0x007e, 0x0742, 0x405b, 0xbada, 0x466f, 0x438a, 0x40f5, 0xb287, 0x43a5, 0xb237, 0x4350, 0x44e5, 0x4222, 0x4067, 0x405e, 0xbfc6, 0x411e, 0x4222, 0x289f, 0xba0c, 0x2535, 0xb29e, 0xba3c, 0xb2a9, 0x1d5c, 0x1afb, 0x4033, 0x1ea6, 0xa772, 0x2d80, 0xa777, 0x43e4, 0x40c9, 0x401f, 0x315d, 0xba55, 0x4595, 0xb2a3, 0x409a, 0xbfaf, 0x46db, 0x4328, 0x4373, 0xb23b, 0xb0a6, 0x4093, 0xa89e, 0xb248, 0x43c9, 0x1643, 0xb04f, 0xb21b, 0x4365, 0xb2d3, 0x4660, 0x42b9, 0xb273, 0x0942, 0x4335, 0x1def, 0xab0c, 0xbf0e, 0x43af, 0x317b, 0x407e, 0x435d, 0x361a, 0x454c, 0x4304, 0x3b0b, 0x4046, 0xbf76, 0x4368, 0xb0fc, 0x4277, 0xa54e, 0x15dd, 0x43ef, 0x42ba, 0x436a, 0x3be5, 0x410e, 0x2870, 0xbf7e, 0x1890, 0x18a3, 0x4203, 0x4384, 0x4371, 0x0e8e, 0x42a8, 0x288f, 0x1920, 0x447b, 0xba58, 0x41e2, 0xb296, 0x42a4, 0x4151, 0xae23, 0x4268, 0x42ea, 0xb2dc, 0x42b5, 0x1ac1, 0x43af, 0xba4d, 0xbf3e, 0xb238, 0x4208, 0x2d85, 0xb284, 0x183c, 0x40c6, 0xa6b9, 0x429f, 0x0428, 0xb080, 0x23ff, 0x4191, 0xb286, 0xab3d, 0xbf23, 0xb2ab, 0x4101, 0xba54, 0x42ae, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe99b8527, 0x3e866f78, 0xbb8f4079, 0xc9ef19b8, 0xaaf5b29d, 0x98cec417, 0x79e91cc3, 0x8e2e7557, 0xc691e551, 0x2095f998, 0xe3609aa0, 0xb521524b, 0xcc39afbb, 0xcf08047d, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0x161c0000, 0x0cd2dc16, 0xf32d3fff, 0xcf08105b, 0x000000c2, 0x0000161c, 0x00000000, 0x00000061, 0x000010d2, 0x00000000, 0x00001643, 0xb521524b, 0x000010d2, 0xcf080f67, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbf78, 0xb251, 0x1eb2, 0x15c1, 0x1b9e, 0xb26c, 0x46ba, 0x4392, 0x42d2, 0x42bd, 0x40e0, 0x1474, 0xba55, 0x4199, 0xb255, 0xbf64, 0x4214, 0x44fb, 0x437e, 0x426f, 0x4258, 0x4079, 0x409b, 0x4240, 0x42da, 0x02ff, 0x41f7, 0x1e1c, 0xbafd, 0x4108, 0x1976, 0x4304, 0x4363, 0x4376, 0x422f, 0x4448, 0x4164, 0x41f6, 0xaada, 0xbf0d, 0x43b8, 0xb0ab, 0x43c9, 0xb084, 0x0010, 0x419f, 0x4158, 0xb241, 0x4008, 0x44a8, 0x41dc, 0x42f1, 0x4083, 0x2913, 0x43e7, 0x4226, 0x1f85, 0xbf57, 0x40f3, 0xbac0, 0x43e2, 0x17b4, 0x401b, 0xa764, 0x19f8, 0xa5d3, 0xba04, 0x42c5, 0x410c, 0x0045, 0x40df, 0x45c4, 0x4370, 0xb0e6, 0x40f9, 0x1994, 0x42ea, 0xa907, 0xbf67, 0x4016, 0x1d1c, 0x0fd3, 0xb022, 0x1d1e, 0x4688, 0x44a4, 0x4357, 0x18dd, 0x3d71, 0x1ab6, 0x19b6, 0x1f8f, 0xaaf6, 0xb23a, 0x1b6a, 0x4137, 0x40a8, 0xbfe8, 0x4388, 0x422d, 0x434b, 0x0732, 0x4068, 0x42d5, 0x42f3, 0x41ad, 0xb20a, 0x4091, 0x411a, 0xba44, 0xa7d9, 0x1688, 0xb09c, 0x1d2f, 0x1cf1, 0xb2ca, 0x409c, 0x0873, 0x19b1, 0x409d, 0xb2e0, 0x4071, 0x425a, 0x04b0, 0xbf3a, 0x412f, 0x44e1, 0x3786, 0x412e, 0x419b, 0x4012, 0x4547, 0x19fb, 0xb0be, 0x4209, 0x4001, 0x43af, 0x429b, 0x20fd, 0x43d4, 0xbfa0, 0x40ee, 0x1a3d, 0xbfc0, 0x4418, 0xb2f9, 0x0a05, 0x2b88, 0xb23b, 0xbf5c, 0x183d, 0x4122, 0x463b, 0x3ed8, 0xbf70, 0x4381, 0xba5f, 0xba7d, 0x41ec, 0xba39, 0xbf70, 0x4596, 0x1660, 0x1e85, 0x4278, 0x43a4, 0xad91, 0x4046, 0x41fd, 0xba79, 0x408b, 0xbf4e, 0x0e75, 0xb22b, 0x1ee0, 0x4195, 0x1e6c, 0xb040, 0x43e2, 0x3745, 0xba65, 0x40e0, 0xa8ab, 0x4126, 0xbf41, 0xb20d, 0x4235, 0x0483, 0x43c5, 0x36d5, 0xaf62, 0xb0c7, 0x1e22, 0xbfa1, 0xb2e7, 0x17de, 0x3fa1, 0x2a2d, 0xb035, 0x4308, 0xa38f, 0x4115, 0xb001, 0xba31, 0x1d3f, 0x4093, 0xad99, 0x425f, 0x187a, 0x4206, 0xaaec, 0x41c2, 0x1b58, 0x4299, 0x419f, 0xbf59, 0x4252, 0xb275, 0x4157, 0x4314, 0x40c5, 0x4002, 0x4156, 0x4040, 0x1c34, 0xba34, 0x0b6f, 0x19d4, 0xba24, 0x40fd, 0x41cb, 0x4141, 0x3190, 0x19c1, 0xbad9, 0x1fdb, 0xbfab, 0x41ed, 0x43de, 0x4080, 0x3d58, 0x2532, 0x466a, 0xbfc0, 0xb049, 0x412a, 0xa8ca, 0xb2af, 0x420f, 0xbacd, 0x432a, 0xbfde, 0xb0e2, 0x43ea, 0x4100, 0x404d, 0x426e, 0xbf71, 0x124a, 0xb259, 0x407a, 0x43f7, 0x4215, 0x416f, 0x34fa, 0x4367, 0x40b1, 0xb239, 0x419f, 0xba28, 0xae76, 0x1a0e, 0xbf82, 0x4174, 0xb27d, 0xa2f6, 0x422b, 0x401c, 0x439a, 0xba51, 0x2d8f, 0xb063, 0x1d7c, 0x40f6, 0xb0f9, 0xb2aa, 0xbfaf, 0xaaaf, 0x40e9, 0x41a9, 0x16c4, 0xbad8, 0x1dda, 0x17ae, 0x26f9, 0xba47, 0xbad9, 0x41eb, 0x4086, 0x424f, 0x4234, 0xb290, 0x4236, 0xa8ce, 0x40fd, 0x18c3, 0x4291, 0xb2e9, 0x4240, 0xbf88, 0xbad6, 0x02a2, 0x439e, 0x1c1b, 0x1e78, 0xabba, 0x40c4, 0x42e9, 0xa4b2, 0x21f3, 0x1f64, 0x1c05, 0xbf6a, 0xb20b, 0x4034, 0x20f5, 0xbaf9, 0xac9b, 0xbfb0, 0xb02d, 0x43c9, 0x4020, 0x41f1, 0xb26e, 0x1c9d, 0xba7e, 0x4047, 0x41f4, 0x1bce, 0x412c, 0x4378, 0x3857, 0x43da, 0x419f, 0x4296, 0xba50, 0xbafd, 0x1f01, 0xbaf8, 0xa978, 0xbfb2, 0xb291, 0x42df, 0x410c, 0x40d3, 0x469b, 0x4388, 0x41eb, 0xbad7, 0x4367, 0x1aed, 0x411f, 0xba27, 0xb220, 0x417a, 0x1839, 0xb256, 0xb22e, 0xaeb8, 0xad5b, 0x0842, 0x2af8, 0x423d, 0xba02, 0xb256, 0xb093, 0xbfb5, 0x431d, 0xb01b, 0xb2cd, 0xba7c, 0x19f9, 0xbfa9, 0x1c2b, 0x40b2, 0xb024, 0xaf9c, 0xad91, 0x0191, 0x4152, 0x40b1, 0xbacb, 0xa5d4, 0x42e8, 0xb0b6, 0x080a, 0x41da, 0x4195, 0x40f4, 0x4329, 0xaa3c, 0x4602, 0x40f8, 0x1d9f, 0xbf86, 0x1d1d, 0xb041, 0x4294, 0xb215, 0x41a0, 0x4336, 0xb07c, 0xba73, 0x1537, 0xa583, 0x4332, 0xbfa3, 0xb03a, 0x2082, 0x405f, 0xb0e5, 0x4083, 0x3b48, 0x1fdd, 0x4399, 0x38f1, 0x41d9, 0xb09f, 0x1377, 0xb076, 0x195c, 0x4341, 0x1faa, 0x1a14, 0x1c98, 0x4177, 0xbf64, 0xbff0, 0x4341, 0x199c, 0xba2d, 0x4001, 0x4294, 0x271a, 0xbf49, 0xb2eb, 0x4625, 0xb2c7, 0x4282, 0x42c2, 0xbf90, 0x42cf, 0x4057, 0xbaef, 0x414a, 0xbf2b, 0x4196, 0x1071, 0x02c2, 0xa540, 0x424a, 0x032e, 0xb21b, 0x4331, 0x4049, 0x4673, 0xbfd2, 0x421f, 0x42ee, 0x417e, 0xa3ed, 0xb21e, 0xb0ba, 0xbac9, 0x4071, 0x3b78, 0x436b, 0xa792, 0xb0ea, 0xb271, 0xa5df, 0x1a45, 0x2020, 0x43ff, 0xb282, 0xb24f, 0xbfb6, 0x022c, 0xbae8, 0x3ac4, 0xb016, 0xba5a, 0x41e5, 0x407b, 0x0f5d, 0xb245, 0x1f55, 0x41ae, 0x43ba, 0x0399, 0x42df, 0x0d93, 0x4355, 0xba29, 0x4670, 0x4558, 0x047d, 0xbf86, 0x29a9, 0x19d7, 0x1f16, 0xb0fe, 0xbaf9, 0x21a5, 0x46fc, 0x2750, 0x1398, 0x2284, 0x0309, 0x461b, 0xb2cd, 0x29de, 0xbf0a, 0xba3f, 0x119d, 0xb24a, 0xa8e5, 0x4069, 0x148f, 0xb04d, 0xb264, 0xa8c6, 0x1dd3, 0x46b4, 0x18c7, 0x43bc, 0x40e3, 0xba4e, 0x405b, 0x4101, 0x1d87, 0x3d58, 0x43e6, 0x2199, 0xb21f, 0x1a00, 0x4301, 0x2a14, 0x02f4, 0xbfb7, 0x4441, 0xa4bf, 0x40bf, 0xb2d4, 0x4117, 0x11d9, 0x1f4a, 0x1923, 0x02f0, 0x4350, 0x1f6a, 0x4176, 0xb2f9, 0xb233, 0x31f5, 0x4012, 0xbfdd, 0x4297, 0x4575, 0x40ae, 0xba01, 0x42b1, 0xbacd, 0x40fa, 0x3d82, 0x4338, 0xba51, 0x411c, 0x4153, 0x2556, 0x4398, 0x1d63, 0x4305, 0x28c7, 0xbf0a, 0x4309, 0xb051, 0x42e7, 0x37b1, 0xb28c, 0x41f8, 0xb263, 0xbf60, 0x1f06, 0x421f, 0x4294, 0x401c, 0x43d3, 0xb2b3, 0x1a06, 0x428e, 0x42fe, 0xb246, 0xb27b, 0x43e7, 0x4135, 0x2f59, 0xbf6e, 0xac94, 0xbaf0, 0x12fd, 0x41da, 0x4337, 0xb294, 0xb06f, 0x4156, 0x42d8, 0x2309, 0x1de4, 0xb021, 0x0d01, 0x42fd, 0x1957, 0x44e1, 0x0a23, 0xb21d, 0xba2e, 0x2c2f, 0xa0c8, 0x40f8, 0x41f6, 0xbaed, 0x4224, 0xada8, 0x42ff, 0x4227, 0xbfc1, 0x2336, 0xbad7, 0xba01, 0xba5b, 0x1eec, 0xba44, 0x43d7, 0xbae3, 0xba1f, 0x417f, 0x3fbb, 0xb2aa, 0xb202, 0x07f6, 0xb249, 0x414f, 0x4144, 0xb25e, 0xba30, 0x413d, 0xb2c9, 0xb0a8, 0x029f, 0x19a0, 0xbf58, 0x4140, 0xb260, 0x0f52, 0xb23b, 0x42c4, 0x43db, 0xad0e, 0x43f0, 0x028d, 0xbf24, 0xbac3, 0x1c7e, 0x1094, 0xbff0, 0x41b1, 0x30a0, 0x01a0, 0x094c, 0x149b, 0xbfdf, 0x32f6, 0x41b3, 0x43d5, 0x35b7, 0x468c, 0x2936, 0x1877, 0x2db5, 0x436e, 0x18bc, 0x1c4b, 0xbf00, 0xb276, 0xbf00, 0xb262, 0xba52, 0x240e, 0x4281, 0x410b, 0xbfa2, 0x4173, 0x2aba, 0x43de, 0x2801, 0x432c, 0x40b4, 0x4119, 0xb2da, 0xaa5d, 0x43dd, 0x1c35, 0x4357, 0x4237, 0x191a, 0xba06, 0x3b92, 0x4018, 0xb299, 0x46c2, 0x1f3c, 0xba7a, 0x438e, 0x44b9, 0xba71, 0x401f, 0xbfd7, 0xbaf6, 0x41b4, 0x0e92, 0x4151, 0xb225, 0xb08b, 0x4035, 0x428c, 0x42b2, 0x41cb, 0xb2a3, 0xbae6, 0x41cd, 0xb2b9, 0xba59, 0x4022, 0xba11, 0x4246, 0x4625, 0x1b37, 0xad1c, 0x108d, 0x2de7, 0x1a8b, 0xbf9d, 0x1bc9, 0x43c4, 0x432a, 0x1a06, 0x4196, 0x3937, 0xb08f, 0xbad4, 0x18f6, 0x3f38, 0xbfc6, 0xb2d1, 0xb03a, 0x4246, 0x3ea1, 0x42bb, 0xb259, 0x43b0, 0xb2bb, 0xba47, 0xb28d, 0x1907, 0xbf6a, 0xb089, 0x4372, 0x4469, 0x182d, 0xb03d, 0xbf90, 0x4399, 0x4237, 0xb223, 0x36ba, 0x4173, 0x4233, 0x4616, 0x21de, 0x25e2, 0x4287, 0xb20a, 0x4612, 0x409c, 0xb24c, 0x40d6, 0x400f, 0xbad9, 0xbf01, 0x4324, 0x4374, 0xb0d1, 0xb21a, 0x1e51, 0xb2e8, 0x41ed, 0xbf60, 0x4664, 0x4043, 0x463a, 0xb21d, 0x430d, 0xbf19, 0x4203, 0x4339, 0x4549, 0xa026, 0x1ca7, 0xbaf4, 0xaaac, 0x4360, 0x2a0a, 0x125d, 0xb2de, 0x313b, 0x4025, 0xb221, 0xba16, 0x42e4, 0x4087, 0xba4e, 0x1f06, 0x31b6, 0x1af5, 0xbf3b, 0xb217, 0xb227, 0x3022, 0xba71, 0xbf11, 0x40db, 0x40a6, 0x4219, 0x4274, 0x4349, 0x4168, 0xbfb1, 0x4219, 0xb27f, 0x415d, 0x0685, 0x42f3, 0x4321, 0x400e, 0x4041, 0x3c1d, 0x41c8, 0xbacf, 0xa885, 0x425b, 0xa833, 0xb0ed, 0x4111, 0x408a, 0xbf60, 0x4048, 0xb2e6, 0x314a, 0xb21c, 0x2119, 0x4349, 0x1d8f, 0x43e4, 0x1ce5, 0xb2db, 0x4281, 0xbf3c, 0x1907, 0x431e, 0x41f2, 0x4309, 0x413f, 0x3c15, 0x1e57, 0x1875, 0x2a86, 0x1cde, 0x444d, 0xb26f, 0x40ad, 0x3408, 0x05f4, 0x42b4, 0x418f, 0x1bdb, 0x437c, 0xb29e, 0xa7c4, 0x44b8, 0x4254, 0x4198, 0x4568, 0xbf29, 0xba58, 0x42e5, 0x413d, 0xbae2, 0x42d2, 0x44a0, 0xb2b6, 0x431a, 0x3165, 0x1d28, 0x0b29, 0xb234, 0x409b, 0xb296, 0xbf5d, 0x13ee, 0x4310, 0x4015, 0x42b6, 0x1919, 0x21c3, 0xb249, 0x43dc, 0x459d, 0xbfc0, 0x4073, 0x4251, 0x428e, 0x42a6, 0xb212, 0xb20d, 0xbfc0, 0x40b8, 0xba7e, 0x42d8, 0xb234, 0x05c8, 0x4120, 0x1a7d, 0xb05f, 0x4608, 0x411a, 0xbf47, 0xba36, 0x19e1, 0x4019, 0x0eb2, 0x4090, 0x43f4, 0x42a7, 0xba3a, 0x0080, 0x42b9, 0xba72, 0xbf90, 0xb0ff, 0xb295, 0xa5d3, 0x4215, 0x36ac, 0x40be, 0x1a4a, 0xbad4, 0x09b5, 0xba41, 0x465c, 0xba6f, 0xb25f, 0xbfa2, 0x14b7, 0x09c9, 0x1ee3, 0x4397, 0x280b, 0xbf53, 0xb230, 0x42c9, 0x1896, 0x4454, 0xbafa, 0x0535, 0x42f0, 0x395d, 0x1d22, 0x4191, 0x4481, 0x1b2e, 0xba36, 0x4103, 0x1a29, 0x2b7c, 0x326a, 0x407a, 0x42a4, 0x0308, 0x4673, 0xbfd0, 0xb24d, 0x184f, 0x439d, 0xba62, 0x42f7, 0xbf93, 0x24a2, 0xb2d0, 0xb0f4, 0x13d7, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x7f786d81, 0xed0589af, 0xd3e5419b, 0x0129696d, 0xe25706db, 0x2c5ca927, 0x6d81e353, 0x64845d90, 0x7114f20e, 0x7890cd21, 0xe05b5922, 0xe345d064, 0xda1b31e1, 0xa9b62e07, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0x00000000, 0x00000000, 0xb6a9df2b, 0x00000000, 0x000000a2, 0x00000000, 0x21d44956, 0xffff6d53, 0x347f7fc7, 0x188dd287, 0xa9b62bdf, 0x00000000, 0xffffffff, 0xa9b62c9f, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x1e54, 0x40d8, 0x2f66, 0x276c, 0x416f, 0x1987, 0xb06a, 0x45ec, 0xba67, 0x408a, 0xba2f, 0xacd5, 0x29cf, 0x1872, 0x111e, 0x1b04, 0xbafd, 0x41a5, 0xbf97, 0x43df, 0xb06f, 0x1c82, 0x268b, 0xb2ba, 0xb08a, 0x407f, 0xbaea, 0x1d52, 0x06cb, 0x41e7, 0x4475, 0x43b8, 0xba6b, 0x1fb1, 0xbf3e, 0x4241, 0x3d38, 0x4120, 0x2a6b, 0xb217, 0xbfb8, 0x1f4e, 0xb218, 0xb2cc, 0x1ad6, 0x4128, 0x169b, 0xaa87, 0xaf67, 0xb2aa, 0xbfc0, 0xb0e8, 0x33a9, 0x43fb, 0x195b, 0x4091, 0x3613, 0x00c8, 0xbf09, 0xbff0, 0x0ab2, 0x4211, 0xbadc, 0x4100, 0xba4d, 0x0f34, 0xba3f, 0xb250, 0x1425, 0x1ad6, 0x1f20, 0xb0e6, 0xae48, 0x41f4, 0x4117, 0xb0d0, 0xba25, 0xb2f9, 0xba7d, 0x4063, 0x1e53, 0x40ef, 0x401c, 0x1b23, 0x059d, 0x4021, 0x4126, 0xbf0c, 0x3318, 0x4087, 0x4135, 0x430e, 0xbf69, 0x18c0, 0xb285, 0x4164, 0xbf90, 0x40e9, 0x43bf, 0xba12, 0x2a34, 0x4009, 0x4593, 0xbf06, 0x07c3, 0xb295, 0x369a, 0x41f4, 0x434a, 0xb01e, 0x42a5, 0x0343, 0x2ad7, 0x1be0, 0xbfce, 0x1bdd, 0x4484, 0x439c, 0x461b, 0x10f4, 0x2207, 0x0503, 0x40ab, 0xbf60, 0x4108, 0x4271, 0x1c07, 0x40d7, 0x1820, 0x01a5, 0x3519, 0xb283, 0xb0c1, 0xb28f, 0xb241, 0x238f, 0x43da, 0x2f61, 0xbfb2, 0xa2bd, 0xbae8, 0x410c, 0x46e2, 0xb012, 0x1e36, 0xb236, 0x45ba, 0xb23b, 0x2e93, 0x4369, 0x18a6, 0xb257, 0x4340, 0xba50, 0x4240, 0x4181, 0x1a86, 0xba64, 0x0cb1, 0x4065, 0xb21a, 0x1b54, 0xbf70, 0x462e, 0xb236, 0x01af, 0x41c3, 0xbf89, 0x4324, 0x4357, 0x41ae, 0x4654, 0x408c, 0x1c4d, 0x43ca, 0x405e, 0x19cc, 0x429b, 0xb026, 0x4337, 0x404b, 0x1c2f, 0xba75, 0x42f1, 0x1e97, 0xb27f, 0x425d, 0x049c, 0xb2e2, 0x1385, 0xb270, 0x1eb3, 0xa556, 0xbade, 0x4064, 0x22e3, 0xbfd1, 0x1cf0, 0x0f0c, 0x4266, 0xb0e2, 0x02c3, 0xb092, 0x1212, 0x3b16, 0x4349, 0x458d, 0x317f, 0x4263, 0x19ee, 0x3fde, 0x085c, 0x0f9e, 0x21c4, 0x41e8, 0xb27f, 0x1ae4, 0x425c, 0x2207, 0x43b9, 0xba3e, 0xb24a, 0xbfcc, 0x40b9, 0x40df, 0x4026, 0x415a, 0xb2db, 0x41e2, 0xbae2, 0x410a, 0x420e, 0xbfa5, 0x43d6, 0xb237, 0x40a9, 0x407f, 0x4075, 0xb23b, 0x1dbb, 0x4021, 0x16c5, 0x430d, 0xb258, 0x1fee, 0x44a5, 0xb03f, 0x4367, 0x43e4, 0x435a, 0x37aa, 0x1bd9, 0xbf08, 0x4143, 0xaea3, 0x2a14, 0x41ef, 0xb076, 0x1e69, 0x4644, 0x1971, 0x2fe4, 0x0aef, 0x3651, 0x43e0, 0x4171, 0x1c89, 0x417b, 0x4354, 0x43fa, 0x418d, 0x411b, 0x425a, 0xbf3a, 0x42b3, 0xa73a, 0x4396, 0xbfc0, 0xb258, 0x429c, 0x44c8, 0x4195, 0xba65, 0x46ed, 0xb0ec, 0x18f8, 0x43b9, 0xb246, 0x4306, 0x465f, 0xb2d4, 0xbf7e, 0x37f6, 0x4173, 0x4685, 0x0536, 0x3e40, 0x0691, 0x2372, 0xbf2e, 0xb213, 0xba2c, 0x1c23, 0x41e6, 0x30e0, 0x4062, 0x4030, 0xb229, 0x43d9, 0x2e16, 0x25b3, 0xb2ae, 0x439f, 0x18bb, 0x16b6, 0x4048, 0x4418, 0x42f7, 0xa615, 0xb008, 0xb2d6, 0x1958, 0x1b00, 0xb0d0, 0x4281, 0xb225, 0xab07, 0xbf82, 0x4276, 0x2565, 0x4156, 0x43eb, 0xb2fe, 0xba25, 0x4143, 0x435c, 0x41cf, 0x428c, 0xb2b4, 0x1772, 0x4398, 0x1b8d, 0xb016, 0x435d, 0xba5c, 0xbf63, 0xba66, 0x4100, 0xba5f, 0x43ac, 0xbfb1, 0x41c5, 0x3555, 0x3b44, 0x4305, 0x3e89, 0xba59, 0x1f34, 0x1828, 0x3d4b, 0xa344, 0xb2c2, 0x3ca8, 0x42ea, 0xbf88, 0x4250, 0xb2ac, 0x43fd, 0xb02a, 0x30bb, 0x0fba, 0x43b6, 0x4336, 0x432b, 0xb256, 0x432f, 0xb0bf, 0x4353, 0x4069, 0x095a, 0xa6cb, 0xbf47, 0x3074, 0x165b, 0x1fe4, 0x2ed8, 0xb272, 0x43d0, 0x43a8, 0x2770, 0xb05c, 0x4279, 0x4055, 0xbfd0, 0xbf84, 0x41aa, 0x43ec, 0x1daf, 0x4663, 0x40c5, 0x34e8, 0x447f, 0xa971, 0x145d, 0x00c3, 0x4281, 0x4392, 0xba2d, 0x3677, 0x42eb, 0x42d3, 0x28df, 0xb2c4, 0x4004, 0xa6ed, 0x40fb, 0x1947, 0xbf33, 0xbaee, 0x446b, 0x40b7, 0x402b, 0x460f, 0x1ef9, 0x4130, 0x28f6, 0xad23, 0x4042, 0x2583, 0x45b5, 0x41c8, 0x462e, 0x19f1, 0x4008, 0x4144, 0x421d, 0x4305, 0xba2b, 0x4103, 0xb2fd, 0xb239, 0x4365, 0xbf9a, 0xb219, 0x4461, 0x41a7, 0x43ac, 0xb2a4, 0x416a, 0xbfe0, 0xb09b, 0x4214, 0xae01, 0x2c45, 0x0c53, 0x4107, 0x43b4, 0x4005, 0xb22f, 0x1966, 0xbf60, 0x182c, 0x4011, 0x430e, 0x4291, 0x34de, 0xbfcc, 0xabcf, 0x4255, 0xba1e, 0xba3b, 0x4556, 0x0971, 0x1926, 0xb2ff, 0xaffe, 0x0c75, 0x401a, 0xbf69, 0x4026, 0x402a, 0x4406, 0x4213, 0xba16, 0x41da, 0x43f0, 0x41d3, 0x4312, 0x4228, 0x43fb, 0x44a8, 0x413c, 0x1e14, 0x1924, 0x435d, 0x409c, 0xb27c, 0x4113, 0x405c, 0x4180, 0xba02, 0x42c0, 0xba36, 0x4197, 0x4359, 0x319f, 0xbf82, 0x4092, 0x1fec, 0x1ee0, 0x43fa, 0x4170, 0x45b4, 0x1e51, 0x4109, 0x40e2, 0x2867, 0x30ff, 0x41f8, 0xbf69, 0x17f9, 0x0d33, 0x06dd, 0x1990, 0xa9a2, 0x3666, 0x1d0f, 0xa2cf, 0x4150, 0x4073, 0x217c, 0x1c58, 0x3267, 0x42d9, 0xb217, 0x439f, 0xbafd, 0x4054, 0x1a4c, 0x42e3, 0xbfc9, 0x2489, 0x4620, 0x00d9, 0x41c0, 0xbf90, 0xb23e, 0xb2a1, 0x27f9, 0x423a, 0x417f, 0x2922, 0xbac3, 0x1a8e, 0x1ff5, 0xb220, 0x421f, 0x4378, 0x433e, 0xa84c, 0xb081, 0x1a72, 0x4684, 0xb280, 0x1acb, 0x1d52, 0x43cb, 0x4125, 0x1b4d, 0xbf1b, 0x4190, 0x192c, 0x42fc, 0xaf1e, 0xbf3f, 0x42a6, 0x1ef6, 0x1a9b, 0xb076, 0x30ec, 0xb25b, 0x19f6, 0x29fb, 0x43f2, 0xb2b7, 0x40af, 0x438e, 0x1bbe, 0xb20b, 0x4166, 0x1a51, 0x4140, 0xb250, 0x23de, 0x4062, 0x39c9, 0xbf0d, 0x1ff7, 0xb04e, 0x4670, 0xb233, 0x40ca, 0x4280, 0x17e1, 0x4572, 0x435a, 0xb248, 0x198a, 0x427a, 0x21aa, 0x412d, 0x0d38, 0x1b5c, 0x1cda, 0x40ad, 0x081c, 0xb2ab, 0xbf98, 0x05b0, 0x157a, 0x42ad, 0x43dc, 0x09b2, 0xa299, 0x1def, 0x41dd, 0x2faa, 0x4089, 0x4161, 0x40eb, 0x400d, 0x42e7, 0xbf70, 0x408f, 0x439c, 0x11bc, 0x40c6, 0x1277, 0x2045, 0x426e, 0x420f, 0xbf70, 0xba1a, 0xbfc4, 0x088e, 0x4437, 0x19aa, 0xb095, 0xba24, 0x1d2b, 0x19d9, 0xb200, 0xb26c, 0xbac0, 0x42c3, 0xbf19, 0x3ab5, 0x185a, 0x1e03, 0x42bd, 0xb040, 0x456d, 0x0e83, 0x4244, 0x4001, 0xb257, 0xb255, 0x4016, 0x21f4, 0x134a, 0x1c09, 0xb2eb, 0xb2d5, 0x103c, 0x412f, 0xb2f8, 0x4645, 0x41ca, 0xbad7, 0x3c4c, 0x43f2, 0x4469, 0xbf7f, 0xb096, 0x404c, 0xb20e, 0x408f, 0xb293, 0x44f4, 0xb2ac, 0x4300, 0xb0d5, 0xb044, 0xb298, 0x401d, 0x40a5, 0x0257, 0xb2bd, 0x427d, 0x42d6, 0x432a, 0x41e0, 0xbf48, 0x3377, 0xb282, 0xabfc, 0xb0a9, 0xb2cf, 0x1ba6, 0x45c8, 0x4080, 0x1e90, 0xb0f0, 0x462e, 0xb229, 0x02cf, 0x41ba, 0x41b8, 0x4210, 0x3f35, 0x398c, 0xba39, 0x413b, 0xbf64, 0x21a7, 0x40b8, 0x0509, 0x4388, 0x20cc, 0xba62, 0x41b9, 0x1efd, 0x41c7, 0x413e, 0x40a3, 0x4136, 0x1b72, 0x4366, 0xba01, 0xb2e0, 0x428b, 0x438e, 0xa279, 0x43d2, 0x1b4e, 0x43ac, 0xbf99, 0x0ff8, 0x36ad, 0x46c3, 0x43e4, 0x42ae, 0xba6e, 0x368c, 0x12b7, 0x43cf, 0x405e, 0x1e52, 0xb28e, 0x0282, 0x280c, 0x4294, 0x2cf6, 0xbfd8, 0x418c, 0xa006, 0x4179, 0xb2b4, 0x415d, 0x0cba, 0x1502, 0x46da, 0x43cf, 0x42bd, 0x411a, 0x4037, 0x4265, 0x4268, 0xbfa9, 0xa149, 0x1de5, 0xb2d1, 0x416a, 0xb0af, 0x4390, 0x4302, 0xbf37, 0x36bf, 0x40ef, 0xaa8e, 0x1731, 0x2537, 0xaa7a, 0x439d, 0xbfdd, 0x416c, 0xb271, 0xb0a0, 0xb0cb, 0x462a, 0xa0ff, 0xaf7c, 0x4338, 0x41c6, 0x40e4, 0x430f, 0xbfae, 0x24ea, 0x41d3, 0x1baf, 0xbf0a, 0xb2b5, 0x415b, 0x4574, 0x42dd, 0x1886, 0xb298, 0x40e3, 0x23f6, 0x43b1, 0x4179, 0x421d, 0x00f3, 0x44fb, 0x43f2, 0xb2da, 0xbf43, 0x329d, 0x40df, 0x41f5, 0x43a4, 0x410f, 0xb070, 0x40c3, 0x0290, 0xba76, 0x40c0, 0x03b8, 0x449a, 0x1b97, 0x0f78, 0x419b, 0x422e, 0x179f, 0x4123, 0xa591, 0x1e2b, 0x1fda, 0x24bb, 0x412f, 0x4204, 0x4331, 0x40cc, 0x3aa9, 0xbfd7, 0x443b, 0x43ac, 0x130f, 0x4328, 0x2575, 0xba56, 0xb246, 0xa7fe, 0x46ac, 0x3801, 0x43dd, 0xba2b, 0x39e7, 0x0bd0, 0x42b3, 0x4038, 0x41f9, 0x4393, 0x4102, 0xb203, 0xb2f8, 0xa22e, 0xa526, 0xb08e, 0x2e59, 0x2b55, 0xb25f, 0x4663, 0xbf3e, 0x4005, 0x43c8, 0xb034, 0x3d71, 0x3d1d, 0xb258, 0x4322, 0x43a8, 0x4052, 0x2a11, 0x434d, 0xba44, 0xb283, 0x0014, 0x18b3, 0xbf85, 0x4236, 0x370f, 0xa4a4, 0x4081, 0xbfae, 0x3107, 0x4246, 0x4159, 0x40f4, 0xb262, 0x1bd6, 0xb275, 0x41ea, 0xbf8e, 0x40f6, 0x4004, 0xba73, 0xbf01, 0x42a3, 0x419b, 0x418a, 0x4309, 0x4300, 0x4053, 0xa24b, 0x43b2, 0x4105, 0x417a, 0x42ee, 0x3a99, 0x41b6, 0x1c97, 0x43e8, 0xbf95, 0x421b, 0xb22f, 0x468c, 0xba30, 0x171d, 0x42e5, 0x4283, 0xbf78, 0x1e0f, 0xb02e, 0x3bce, 0x46f2, 0xbaeb, 0x1a73, 0x1d12, 0xba2a, 0xa2cb, 0xb23a, 0x42a8, 0xa26b, 0x3c45, 0x0a4d, 0x1ce2, 0x1a89, 0xbfa3, 0x462f, 0x45f1, 0x0ccd, 0x19fc, 0x4432, 0xba68, 0x402c, 0x43bd, 0x4106, 0x2d24, 0x08ae, 0x41ee, 0x4395, 0x438a, 0x410d, 0x414e, 0x40c1, 0x41f5, 0xbae6, 0x2a67, 0x0307, 0x1b7c, 0x42fa, 0xba7a, 0x40e7, 0xbad0, 0xb218, 0x4551, 0xbfbc, 0xbfc0, 0x44e1, 0x19e1, 0xb2fc, 0x423b, 0xbf07, 0xb295, 0xb295, 0x4409, 0x42c5, 0xb285, 0x3d52, 0xbf66, 0xbf70, 0x4244, 0xba58, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x35dcd53f, 0x329b21b1, 0x5b679e3c, 0x95697b24, 0xded610ce, 0x5fb2bf66, 0xe3f3845c, 0xe4e7d67e, 0xb27d6955, 0x52a7f817, 0xf1c67637, 0xa8f2cd39, 0x50a9900e, 0xbf067896, 0x00000000, 0xb00001f0 }, FinalRegs = new uint[] { 0xe8fef8ea, 0x08800000, 0x20020000, 0xfee8eaf8, 0x00000000, 0x0000eaa6, 0x00000000, 0x02200000, 0x0525616d, 0x52a7f817, 0x00000000, 0x052577df, 0x01171508, 0x00011300, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x2155, 0x1e10, 0x3dda, 0xa4a1, 0x40e5, 0xba3b, 0x40e6, 0x1cbe, 0xbf21, 0x3982, 0x403a, 0x3b83, 0x40d0, 0x1dbd, 0x4633, 0xbf27, 0x40fa, 0x4217, 0xba03, 0xa0e8, 0x4051, 0x43d7, 0xb2ad, 0xb23a, 0x4456, 0x40ca, 0xb28a, 0x4598, 0x414f, 0x183b, 0xbf47, 0xb04c, 0x2d55, 0xb200, 0x410d, 0x423c, 0x427c, 0x43e2, 0xbf75, 0x438e, 0x420c, 0xb040, 0x431f, 0xaaec, 0x1ea1, 0x4219, 0x0e67, 0x4107, 0x258a, 0x4363, 0x4209, 0xbfa0, 0x4244, 0x1fed, 0x082e, 0xa5ef, 0xba10, 0xb215, 0xbf60, 0xbadd, 0x441f, 0x4360, 0x4416, 0x0ced, 0x4396, 0xb2a5, 0xbfbd, 0x020b, 0x10f7, 0x40bc, 0x43fa, 0xba04, 0xb203, 0x424b, 0x3ef2, 0xb21f, 0xb2db, 0x18b7, 0x41eb, 0x32b6, 0x4415, 0x405d, 0x19be, 0x124d, 0xb067, 0x4048, 0xbf7e, 0x4163, 0x4494, 0x2c27, 0x41a4, 0x4094, 0x417b, 0xbadb, 0xb221, 0xac56, 0x4045, 0xbf00, 0xbf9e, 0xb02c, 0x1467, 0xa9eb, 0xb28c, 0x42f0, 0x42f1, 0x2b99, 0x10b3, 0x4181, 0x4565, 0x16bf, 0xbaf0, 0x3ce0, 0x0fdb, 0x182c, 0x4008, 0xb253, 0x405c, 0x4291, 0x427d, 0x1b26, 0x4275, 0x424d, 0x400e, 0xbf97, 0x24cc, 0xb0ac, 0x00e5, 0x4646, 0x2dad, 0xbfd5, 0x416b, 0x43dc, 0x15af, 0x405f, 0x42ae, 0xaffc, 0x435c, 0x407e, 0x25a2, 0xb0a1, 0x26af, 0x4440, 0x2f9f, 0x0c00, 0x44e2, 0x4079, 0x0965, 0xb21f, 0x43b0, 0x081f, 0x441c, 0xbf5c, 0x4350, 0x1b32, 0x41d9, 0x46d8, 0xba76, 0x42cf, 0x426e, 0x438e, 0x0cf5, 0xb00b, 0x414f, 0xba7f, 0x2ea0, 0x4601, 0x43de, 0x2e67, 0x0090, 0xb095, 0x43b4, 0x1edf, 0x4695, 0xb264, 0x0acf, 0xbf11, 0x42f8, 0xa71c, 0x42c7, 0xb29e, 0xb223, 0x08a7, 0x4425, 0x40c5, 0x4342, 0x466f, 0x1e7a, 0x425e, 0xba7c, 0xbaef, 0x4195, 0x40f0, 0x4105, 0x182a, 0xb249, 0x01b6, 0x43db, 0x33cd, 0xb098, 0x22ef, 0x4670, 0x1c06, 0x4143, 0xb2ad, 0xbfdd, 0x44ea, 0xba3e, 0x43d7, 0xb251, 0xba39, 0xba0d, 0x40bf, 0x46a2, 0x20a1, 0x41ab, 0x3578, 0xafc6, 0x19e4, 0x4127, 0xb06d, 0xacfe, 0x1d9e, 0xbf31, 0x43da, 0x0408, 0x42e6, 0xb2ca, 0x400e, 0xa0f7, 0x42c4, 0xb24e, 0x401f, 0xb220, 0x4494, 0x4410, 0xbf15, 0x417b, 0x42eb, 0x151d, 0x4295, 0x4188, 0xbae2, 0x40d5, 0x40c3, 0x1d6c, 0xa78d, 0x4059, 0x46ad, 0x1903, 0x1b14, 0x407f, 0xb249, 0x43cf, 0x415f, 0x4423, 0x410e, 0xba10, 0x0aac, 0x40ae, 0xa148, 0x0bda, 0xbfb6, 0x4161, 0xb0c0, 0xb2b3, 0xacde, 0x43b8, 0x40c4, 0xb2b5, 0x4476, 0x41cf, 0x4245, 0xbf2a, 0x1c2a, 0x34fe, 0x089d, 0x1943, 0xa5a5, 0x4611, 0x4013, 0x305a, 0x1a16, 0x43f1, 0x4118, 0xb2a5, 0x44a3, 0x1aa6, 0x4055, 0x412d, 0xb2c1, 0x43cf, 0x0ae2, 0xbf28, 0x4299, 0x42ac, 0xb203, 0xa899, 0x4160, 0xbf56, 0xbafe, 0x40d0, 0xb2ea, 0xb2b1, 0x1f3a, 0x1cf9, 0x43fd, 0xbf76, 0xba48, 0xbaca, 0xb2d1, 0x4268, 0x43a9, 0x465a, 0x1f70, 0xb0a5, 0xb021, 0x4016, 0xae2f, 0x4012, 0x3ad1, 0x402f, 0x3c2d, 0x425e, 0x40a7, 0xba78, 0xbf2f, 0x226a, 0x44a0, 0x417a, 0x040e, 0x0659, 0xb288, 0x41b4, 0xba78, 0x0a09, 0x4165, 0x2bfb, 0xbf05, 0x4222, 0x1bfa, 0xa40a, 0x43ab, 0x41db, 0x43e3, 0x419f, 0x4694, 0x43d0, 0x35e8, 0x1f83, 0x4116, 0x4206, 0xac92, 0x4034, 0x220e, 0x413e, 0xad23, 0x4116, 0x4430, 0xbf60, 0x0e3f, 0x1c57, 0xac22, 0x3c5b, 0xb208, 0x098b, 0xbf0c, 0x42db, 0x3b41, 0x435e, 0x4302, 0xbf19, 0x3fc7, 0x4222, 0xbac3, 0x1528, 0x427e, 0x428a, 0xb2ce, 0xb09d, 0x41f4, 0x435c, 0xba54, 0xbf3f, 0x2dc4, 0x1526, 0x4335, 0xba05, 0xbf5f, 0xb252, 0x4431, 0x2474, 0x4260, 0x4278, 0x41eb, 0x43f7, 0x41bc, 0x4274, 0x2a23, 0x1ea8, 0xb024, 0x163b, 0x4365, 0x3c89, 0x424f, 0xba1c, 0x43d1, 0x42ac, 0xbac7, 0x405a, 0xb2d1, 0x1d69, 0xb2a2, 0x41bf, 0x412b, 0x42f5, 0x141d, 0xbf5b, 0x43f7, 0x0b65, 0x4124, 0xbacc, 0x414a, 0x39dc, 0x4290, 0x160f, 0xb2c6, 0x46b2, 0x43b8, 0xbf90, 0x417c, 0x41ea, 0x4280, 0x438c, 0x40b9, 0x1fc0, 0x0291, 0x46e2, 0x432a, 0x4206, 0x2149, 0x06e2, 0xb218, 0xba0d, 0x3fc3, 0xbf68, 0x46c8, 0x4166, 0x437d, 0x41a0, 0xb00f, 0x26f5, 0x42b6, 0x466c, 0xaf14, 0xba28, 0x0364, 0x0f78, 0xa3a4, 0xbf6e, 0x4572, 0xb2fe, 0xb046, 0x413c, 0xbaf3, 0x4370, 0x4005, 0x2268, 0x42af, 0x4278, 0x4089, 0x439f, 0xbfd6, 0xaed2, 0x4305, 0xb2e2, 0xb2f6, 0xbf18, 0x42a3, 0x1ee9, 0x41bc, 0x1d48, 0x4313, 0xa6e2, 0x407a, 0xb09e, 0x459c, 0xb24b, 0xb015, 0xaa03, 0x18b9, 0xacea, 0x1e86, 0x4169, 0xbfc8, 0x1f6a, 0x4396, 0x347f, 0x182d, 0x3184, 0xba54, 0x221f, 0x40d0, 0x337e, 0x4272, 0x40c0, 0xb0de, 0x430d, 0x4105, 0x315e, 0x11cc, 0xbf90, 0x228a, 0x4273, 0xbada, 0x006d, 0xbf2a, 0x4334, 0x18c8, 0xb2d9, 0x1cf8, 0xba7c, 0xbaef, 0x4461, 0x2973, 0x2d26, 0x4042, 0xaba4, 0x4186, 0xb05c, 0xbfd4, 0x4254, 0x1e78, 0x4643, 0xb29a, 0x427a, 0x4370, 0x1f71, 0x419a, 0xa4c1, 0x07c9, 0xbf80, 0x3f05, 0x405f, 0x3150, 0xbf37, 0x40ce, 0xba27, 0xb2a0, 0x2398, 0xbfca, 0x43e4, 0xaa17, 0xba74, 0xb0d6, 0xb08f, 0x42c9, 0x42cd, 0xb224, 0x1ccf, 0xafbc, 0x4060, 0xb0f5, 0x423d, 0x43c4, 0xb2fc, 0xbac2, 0x4017, 0xb265, 0x431a, 0xbfca, 0x43a8, 0x415f, 0x40ff, 0xb0d7, 0xbae4, 0x1b93, 0xb2e9, 0x1a43, 0x28ac, 0x427c, 0xb215, 0x4017, 0xbf3d, 0xb2d0, 0x42be, 0x1d8f, 0x447c, 0x40ec, 0xba4e, 0x1910, 0xb041, 0x1fdd, 0x41b7, 0x40c9, 0x42c8, 0xbf60, 0x404f, 0xb27d, 0x4661, 0x4399, 0x43eb, 0xb20a, 0x460b, 0x1c22, 0xb26f, 0xbae0, 0xbff0, 0x421f, 0x1f27, 0x4264, 0x4290, 0xbf93, 0xb2a4, 0xb2a7, 0xbaff, 0xad42, 0x4391, 0xba27, 0x40f4, 0xbfd6, 0x42f8, 0x429f, 0x03c8, 0x40c6, 0x32d1, 0x40c8, 0xaf82, 0x02cf, 0x046b, 0x4285, 0x4291, 0xba45, 0xbadd, 0x40f2, 0x33e6, 0xba09, 0xba1e, 0x4607, 0x45d9, 0x4453, 0xb00d, 0x4222, 0x40b0, 0xbf19, 0xb20c, 0x1ab7, 0x41dc, 0x439e, 0x4221, 0x1f89, 0x400c, 0xb288, 0x42f2, 0x4543, 0x3d97, 0x43e3, 0xba58, 0xaa3d, 0x42d0, 0x1870, 0x26b1, 0xbfab, 0x4112, 0x42d9, 0x467c, 0x4052, 0x4631, 0xb286, 0xbf5e, 0x43c5, 0x44d0, 0x2fb1, 0x43b3, 0xb2ea, 0x1d46, 0x4076, 0x415a, 0x3bbe, 0xbf03, 0x0812, 0x2d4f, 0x1f62, 0x4322, 0x0a58, 0x40e4, 0x1fcd, 0xba52, 0x15b6, 0x43b0, 0x43a0, 0x400a, 0x4303, 0x4297, 0x40e7, 0xb0cf, 0x43e8, 0x15c1, 0x4134, 0x4248, 0x4576, 0x0870, 0x01c4, 0xbf63, 0xbac5, 0x42cd, 0x437d, 0x43e7, 0xb261, 0x42e2, 0xbf18, 0xbac0, 0x4037, 0x40d0, 0x1c84, 0x4149, 0xbf80, 0xb2c6, 0xbfe0, 0x30f1, 0xa467, 0x2e0e, 0xbf6b, 0x46e2, 0x4216, 0x43df, 0x1ab8, 0xbf23, 0x46d3, 0xba71, 0xb208, 0x099f, 0xbf60, 0xba1f, 0x40a5, 0x42d4, 0x21cb, 0x1ffd, 0x40c9, 0x42dc, 0x15f1, 0x279d, 0x2437, 0x0cea, 0xbfdd, 0xbae4, 0x438b, 0x4378, 0x463c, 0x0a74, 0x4005, 0xba1e, 0x41d7, 0x4481, 0x4274, 0x45da, 0xbf46, 0xb0e9, 0x3f19, 0x1bb1, 0x4581, 0x42ce, 0xb2df, 0xba6f, 0xb22f, 0x343f, 0x40d8, 0xae4d, 0x4225, 0x404d, 0x3748, 0x4641, 0x1924, 0xba34, 0xbf27, 0x42ba, 0x1e38, 0x40c4, 0x1e29, 0xbfb8, 0xa804, 0xbaf2, 0xbaec, 0x411a, 0x42e3, 0x402b, 0x404a, 0x4654, 0x403c, 0x229e, 0x4343, 0x4278, 0x420e, 0x414e, 0x1b28, 0x1ec8, 0x4137, 0xbf74, 0x4132, 0x40db, 0xba2b, 0x424e, 0x400f, 0x4342, 0xa268, 0x45cc, 0x41a1, 0xb210, 0x1e13, 0xba32, 0x405f, 0x4070, 0x40a8, 0x4495, 0xaf59, 0x1a8a, 0x4335, 0x1aaf, 0xa0de, 0x4557, 0x429b, 0xbf2b, 0x1a1f, 0xb24a, 0x20ba, 0x4026, 0x4426, 0x415d, 0xba49, 0x1a0f, 0xa6ba, 0x408e, 0xba37, 0xb067, 0xa1be, 0x43f0, 0xb26f, 0x06e2, 0x4101, 0x424b, 0xb004, 0x17fe, 0x417d, 0x4053, 0x133e, 0xbfa1, 0xa51b, 0xaf80, 0x43e6, 0x4031, 0x0893, 0x42a2, 0x113a, 0xbf86, 0xb211, 0xb2e9, 0x1955, 0xb2e0, 0x4249, 0x1fee, 0x43bf, 0x1f72, 0xb29c, 0x41ce, 0x1c6c, 0x1ac1, 0x4216, 0x4309, 0xbf90, 0x4176, 0x4114, 0x0c13, 0xb22a, 0x10be, 0x4293, 0x1255, 0x40dd, 0x1f80, 0xba69, 0xb298, 0x430c, 0x3114, 0xbfc2, 0x4209, 0x4682, 0x4160, 0x461f, 0x4003, 0x4104, 0x27e8, 0xb27f, 0x13b2, 0x4034, 0x061d, 0xb093, 0x30b2, 0x4252, 0xa0b3, 0xa03e, 0x4489, 0x4458, 0xbad3, 0xbf2e, 0x4301, 0x401f, 0x40ed, 0xba6c, 0x41bf, 0xb017, 0xbf5b, 0x4549, 0x4316, 0xb2ac, 0x2a29, 0x19a6, 0x437d, 0xaba0, 0x3226, 0xa26e, 0x1bde, 0x4143, 0x40a4, 0x46ac, 0x420e, 0x43d6, 0x40d2, 0x40e5, 0x059e, 0x41eb, 0xbfb2, 0xaf76, 0x4194, 0x425c, 0x3ca9, 0xbafa, 0x1cd4, 0x40b9, 0xb29b, 0x439a, 0x3586, 0xb28f, 0x43a5, 0x438b, 0x45ad, 0xb200, 0xbf90, 0x387c, 0x1693, 0x40a5, 0x43c8, 0x065e, 0x37af, 0xbf55, 0x4319, 0x41ee, 0x1ee5, 0x1d3e, 0x43f1, 0x1d52, 0x462d, 0x0129, 0xbae8, 0x4613, 0x1d81, 0x2a54, 0xba46, 0x432c, 0x101c, 0xb29a, 0x42c2, 0xbfd0, 0x2520, 0x32e6, 0xba32, 0xba5a, 0x40ef, 0xbf70, 0xa438, 0xbf48, 0x42cf, 0x4220, 0x40f8, 0x0b4e, 0x434e, 0x1909, 0x417e, 0x42ee, 0xbf02, 0xbfd0, 0x157f, 0x408d, 0x42dc, 0xba68, 0xb219, 0xb075, 0xbfa5, 0x2f93, 0x1fb8, 0x40f1, 0xb240, 0xb2b3, 0x2097, 0x42f4, 0x188b, 0xb0e8, 0x4326, 0x4157, 0x2694, 0x404c, 0xbaf8, 0x431a, 0x4308, 0x2760, 0x359d, 0xb290, 0x1cc4, 0x4133, 0x1771, 0x408f, 0x4159, 0xba63, 0x4222, 0xbf7d, 0x44f8, 0x4088, 0xbad6, 0x0287, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x8f725d16, 0x8f2d1fe5, 0xd2b28a66, 0x47a47346, 0xc0b4401b, 0xf8e7fb12, 0x4ba63b76, 0xa9e6ad4c, 0xa1d2cdf2, 0x35a0c989, 0xc174909a, 0x846af94c, 0xe89071ad, 0x3db39c20, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0x0000414c, 0x00000000, 0x0000414c, 0x00004f41, 0x0000414f, 0x000000bd, 0x00004c41, 0x00000060, 0x08d61086, 0x35a0d48c, 0x00000000, 0x846afcc2, 0x00000000, 0x17fffbb8, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xbafe, 0xb286, 0x0988, 0xb25a, 0x4061, 0x1ed9, 0x4663, 0x430f, 0xb280, 0x2590, 0xb0a2, 0x43b0, 0x0a42, 0xa6b5, 0x4171, 0x3320, 0x4145, 0xb286, 0xba32, 0x1307, 0xbf7c, 0x435e, 0x1bc9, 0x41ff, 0x1e42, 0xb09a, 0x444e, 0x40b1, 0x4016, 0xbaca, 0x411c, 0x2fb8, 0x431a, 0xbf01, 0x4360, 0x41d8, 0x1e44, 0x1331, 0x3c8c, 0xb04e, 0x4296, 0x4548, 0xbf2e, 0x2139, 0x403d, 0x197a, 0xbf90, 0x1c14, 0x4472, 0x3fa6, 0x0f32, 0x21ac, 0x407e, 0xbaf8, 0xb255, 0xba53, 0x1b89, 0xbf65, 0x18ef, 0xba18, 0x405b, 0xb0bd, 0x0ebd, 0xb204, 0xba30, 0x413b, 0x4143, 0x4316, 0x41e9, 0x4367, 0x41c5, 0x1673, 0x103e, 0x09a5, 0x2ef0, 0xb263, 0x43e6, 0x42ec, 0x1c44, 0x082b, 0x45c5, 0x439b, 0xb2e9, 0xa969, 0xbf7b, 0xbfd0, 0x438b, 0x4132, 0xb0ec, 0x431b, 0x34ab, 0x0f49, 0xa401, 0x4124, 0xacc3, 0x4314, 0x1cf2, 0x3c2e, 0x309c, 0xb0be, 0xb0a3, 0x40e6, 0x4314, 0x41ab, 0x122b, 0xb2c2, 0x13c9, 0x423d, 0x423e, 0xbfce, 0x4175, 0xaa5d, 0x0876, 0x1c1c, 0xbad3, 0x4248, 0xb298, 0x0069, 0x16ba, 0x40c0, 0xbfc1, 0x43c4, 0x17a8, 0xa13f, 0x41a8, 0x40d6, 0x4680, 0x4319, 0xba2a, 0x44b8, 0x4470, 0xb0c8, 0xbf6d, 0x4303, 0x1c1a, 0x439b, 0xb271, 0xba0b, 0xb236, 0x41a2, 0x4071, 0x431e, 0x395c, 0xadd7, 0xba48, 0x4163, 0x40ee, 0x4195, 0x2da0, 0x19e4, 0x32b4, 0xb2ca, 0xb0b7, 0x45f5, 0xbfbd, 0x3c50, 0x23c9, 0x2092, 0x4424, 0x410a, 0x40d8, 0x43ee, 0xb2a8, 0x1d9f, 0x416e, 0x418c, 0x3fb8, 0x1557, 0xbac8, 0x4659, 0x4131, 0x0f9d, 0x434f, 0xb2a4, 0x41b5, 0xb2a8, 0xbf6c, 0x415e, 0xbacf, 0xb2f5, 0xb20a, 0xb059, 0xbaf2, 0x4003, 0x0bf5, 0x40b9, 0x4480, 0x0151, 0x0a9f, 0x4269, 0x439f, 0xb029, 0x4224, 0x04a3, 0x4365, 0x4071, 0x4630, 0x2d00, 0x4436, 0x1904, 0x3861, 0xbf71, 0x198a, 0xb2ad, 0x0641, 0x3aad, 0x425e, 0xb2e0, 0x00d3, 0xb030, 0xbf78, 0x4089, 0xba44, 0x1789, 0x0fb3, 0x45ba, 0xb2dc, 0xbf3a, 0x186d, 0x42c9, 0x432b, 0xb2ea, 0xb0cf, 0x42d5, 0xb22c, 0xb21e, 0x2355, 0xba01, 0xa56e, 0x41fd, 0xbad1, 0x2bcf, 0x1b37, 0xb2a4, 0x411f, 0x4018, 0x4342, 0x20f4, 0x43c4, 0x43be, 0xbfa1, 0x4139, 0x468b, 0x42c4, 0x1b6e, 0x21aa, 0x42a3, 0xb0cd, 0xb0d2, 0x431e, 0x431d, 0xbfa0, 0x428d, 0x35d6, 0xbf32, 0xae52, 0x431c, 0x1e62, 0xbaf6, 0x4099, 0xb015, 0x4122, 0x19f9, 0x01c1, 0xb2ef, 0xa849, 0x2614, 0xb24a, 0x02c0, 0x43ec, 0x0216, 0x42a0, 0x42c1, 0x43e2, 0x43d9, 0x1ae7, 0x1285, 0xb23e, 0x1d33, 0x187a, 0x3872, 0x4661, 0x3d9c, 0xbfaa, 0x4033, 0x4089, 0x41c3, 0x328b, 0x43d5, 0xb2ab, 0x06f1, 0xbaf9, 0x432e, 0x3160, 0x416a, 0x2c76, 0x4232, 0xbfe8, 0xbf70, 0xbf4f, 0xb20a, 0xb29a, 0x16b0, 0xb2f3, 0xb02f, 0x1ed3, 0x42e1, 0x44f8, 0xb279, 0xba78, 0xbff0, 0xb2ae, 0x433e, 0x388b, 0x3cac, 0xbf3f, 0xb2ab, 0x31d8, 0x34a0, 0xaa1a, 0xb22d, 0x3df9, 0xb28a, 0x43d6, 0x37ab, 0xb228, 0xbf1b, 0x41d9, 0x062a, 0xb256, 0x1c7f, 0xa48c, 0x441d, 0x403b, 0x20fc, 0x1841, 0x4260, 0xb280, 0xbfb0, 0x41ee, 0x4340, 0xb0dc, 0x415d, 0x41c7, 0x41c2, 0x4565, 0xb239, 0xba36, 0x435e, 0xb2c3, 0x0432, 0x434a, 0xbfd7, 0x4127, 0x26c1, 0x38e4, 0x4210, 0x400c, 0xb0ac, 0x46f9, 0xba4d, 0x4331, 0x2faf, 0x4088, 0x4014, 0x2e91, 0x42e5, 0xb0dc, 0xbfb3, 0x0e14, 0x42b4, 0xb251, 0x45a4, 0xbf13, 0x4460, 0xba07, 0x2702, 0x406e, 0xbfc0, 0x40d8, 0x3d8d, 0xb231, 0xb067, 0xb0cf, 0xba32, 0x4334, 0x29fc, 0x4051, 0x4068, 0x4617, 0x361f, 0x4013, 0x42c3, 0x1ff9, 0x437f, 0xba51, 0xb24f, 0x4150, 0xbf4f, 0xb2b8, 0x1a70, 0xaf84, 0x410e, 0xb207, 0x4041, 0x42f2, 0x4637, 0x00ae, 0x41b1, 0x411f, 0x4333, 0x41ae, 0x1d1b, 0xbf5b, 0x4313, 0x393e, 0xb277, 0xbfc0, 0x4103, 0x18c6, 0xae36, 0x429a, 0x42c7, 0x464b, 0x1569, 0x035a, 0x41c4, 0x3350, 0x209f, 0x43c8, 0x416e, 0x4305, 0x1daa, 0x43a6, 0x4325, 0x4248, 0x3079, 0xbf01, 0x4072, 0xafc0, 0xb299, 0x42a7, 0x422d, 0x40f4, 0xb2d7, 0xb2d8, 0xb006, 0x4318, 0xbf61, 0xb2c3, 0x2017, 0x4172, 0xa800, 0x43b6, 0xba42, 0x4367, 0x43a3, 0xb08f, 0x4267, 0x43c8, 0xb0f0, 0x0970, 0xbacc, 0x1b86, 0x4177, 0x439a, 0x41ce, 0x4053, 0x4307, 0xb29b, 0x3708, 0xb29f, 0x25a5, 0xb279, 0x4329, 0xb2df, 0xbf3c, 0x40c9, 0x435c, 0xbfd9, 0x4180, 0x01f8, 0x37cd, 0x4087, 0xbf80, 0x41fb, 0x43c3, 0xba0f, 0xb214, 0x1f01, 0x43e2, 0xbf28, 0xb2ce, 0x4181, 0x19db, 0x4124, 0xbff0, 0x170e, 0xb078, 0x41d6, 0x4325, 0x4033, 0xba21, 0x42b9, 0x15d9, 0x42c2, 0x0491, 0x42b1, 0x40c8, 0xba19, 0x4411, 0xbae6, 0xbac3, 0xbf61, 0x427b, 0x4341, 0xb2ba, 0xb233, 0xb2fa, 0x40c7, 0xb0b5, 0xb2b9, 0xb23a, 0x433a, 0x418e, 0xb240, 0xbf3d, 0x12df, 0x405d, 0x0bfe, 0x1c0d, 0xbff0, 0xb2a2, 0x42dc, 0x08fb, 0x42c4, 0x4129, 0x1eee, 0xba63, 0x41f4, 0xbfdd, 0x4239, 0xa32e, 0xbac6, 0x41dc, 0x1a05, 0x41dc, 0x3616, 0x4401, 0x40da, 0x4278, 0x15fd, 0x19be, 0xafdf, 0xada3, 0xbf22, 0x0b84, 0xbf90, 0x4558, 0x425f, 0x12b9, 0x42de, 0x43a1, 0x31dc, 0x1fb3, 0x1f66, 0x4313, 0x0dee, 0xa10b, 0x4272, 0x0ff7, 0x40cf, 0xbfb0, 0x4614, 0x4263, 0xbfd7, 0xa80c, 0x43d9, 0x4690, 0xba05, 0x40b9, 0x1a89, 0x3326, 0x43eb, 0x0377, 0x43f4, 0x4182, 0xb066, 0x25ac, 0x4076, 0x1848, 0x02ff, 0xbf2b, 0xb291, 0x4646, 0xa320, 0xb077, 0x41b0, 0x412b, 0x422b, 0x0c68, 0xbf62, 0x4139, 0x413d, 0x2f7a, 0x41a3, 0x417e, 0xb0c1, 0x3b6a, 0xb0d5, 0xb2a6, 0x43ef, 0x1a5e, 0x1de5, 0x4215, 0x211f, 0xbfd5, 0x1f96, 0x4694, 0x05d2, 0x2a85, 0x4460, 0x4180, 0x41bc, 0x30a7, 0x419a, 0x401d, 0x26d1, 0xb009, 0x4267, 0x40f1, 0x413e, 0xae3b, 0x4351, 0xa082, 0x3aec, 0xbf49, 0x42b4, 0x3cdf, 0xb031, 0xb225, 0xbf90, 0xb21e, 0xa23e, 0x3d65, 0x2014, 0xa8dc, 0xba0c, 0x4217, 0x17df, 0x4234, 0x18ea, 0x41f5, 0x0639, 0x45e4, 0x34d4, 0x4207, 0x0eb1, 0x421c, 0x408c, 0x405e, 0xa02e, 0xb273, 0x4109, 0x0240, 0x42a9, 0xbfcf, 0x0d28, 0x43ce, 0x2281, 0x430e, 0x2cde, 0xb08c, 0x4013, 0x165a, 0xb0db, 0xad8f, 0x3ed1, 0x111b, 0x461d, 0x08fb, 0x4074, 0x4076, 0x4030, 0x41a4, 0xb210, 0x144d, 0x24dc, 0x4063, 0x1890, 0x4313, 0x38c2, 0x416f, 0xbfc9, 0x270d, 0xb276, 0x4308, 0x1f80, 0x2858, 0x3b1a, 0x397f, 0x3b6e, 0x19f9, 0x43b3, 0x4249, 0xb2c7, 0x1f5a, 0xba0f, 0x42f8, 0xbfc3, 0xb09c, 0x1cc4, 0xa140, 0x4315, 0x4080, 0xb22d, 0x437d, 0x461f, 0xbada, 0x41af, 0xbf3c, 0x306b, 0x43ce, 0xb244, 0xba73, 0x4193, 0x400a, 0x1af3, 0x403f, 0xb26e, 0x1bee, 0x43a8, 0xba08, 0x400d, 0x4215, 0x3529, 0xba5b, 0x2623, 0xba16, 0x4025, 0x40d8, 0x43e9, 0x4225, 0x4112, 0xb2f4, 0x41db, 0x1c09, 0x1ece, 0xbf27, 0x1ac8, 0x4338, 0x4015, 0x4299, 0x4094, 0x4662, 0xba14, 0x43bd, 0x4082, 0x0e05, 0x40c2, 0x413e, 0x43bb, 0x42a6, 0x4146, 0x0cc0, 0x1a0b, 0x1c27, 0x4104, 0xbf93, 0x2799, 0x46ed, 0x1c30, 0xbfc0, 0x422b, 0xa622, 0x1866, 0x15a6, 0xbf80, 0x4334, 0x4060, 0x07a4, 0x14d3, 0xa26f, 0xbae9, 0xbf44, 0x4033, 0xba7e, 0xad85, 0x4045, 0x1bc4, 0x43b8, 0x43ed, 0x10c5, 0x4200, 0x4330, 0x44cd, 0x36e3, 0x4004, 0x4114, 0x438e, 0xba11, 0x4297, 0x19b2, 0x4222, 0xa7bb, 0xbfb4, 0xb0db, 0xba70, 0x430d, 0x4017, 0x1848, 0x1390, 0x4078, 0x4204, 0x42df, 0xba34, 0x414f, 0x4353, 0x4205, 0x01ea, 0xb249, 0xbf17, 0x4208, 0x42a3, 0x456e, 0xba29, 0xbf32, 0x430b, 0x19a9, 0xb09a, 0x41e6, 0xb0a6, 0x401f, 0xa293, 0x40bc, 0x1df0, 0x40df, 0x3ec9, 0x063a, 0x4255, 0xba51, 0xbf34, 0x405f, 0xb0d1, 0x3863, 0x0052, 0x1541, 0xa37a, 0x429d, 0x4285, 0x2a8b, 0xbf8b, 0x429e, 0x1dc2, 0x4193, 0xb249, 0xb0fe, 0x4135, 0x1864, 0xa8fd, 0xbfc0, 0x23fa, 0x4029, 0xb2b7, 0x4319, 0x408a, 0x4315, 0x4221, 0x1257, 0xb041, 0x0dcb, 0xbfb3, 0x4262, 0xb293, 0xbac4, 0xb20c, 0xa4ca, 0xbad8, 0x4215, 0x4188, 0x401a, 0x1a27, 0x41ce, 0xb282, 0x0e98, 0x4075, 0x40a9, 0xbafe, 0x3362, 0xb25e, 0x4165, 0x1cb7, 0x4025, 0x374e, 0xb2ac, 0x1b9c, 0xb28a, 0xbacb, 0xbf57, 0x2d7c, 0xb010, 0xa274, 0x3dab, 0x41a6, 0x44b9, 0x3456, 0xa547, 0x4304, 0x4056, 0x4359, 0x1e93, 0x416e, 0x4009, 0xba31, 0xbf6f, 0x410e, 0x40ac, 0x1854, 0xb0c6, 0xba4c, 0xa9db, 0xb2c3, 0x1cc0, 0x053b, 0xbf51, 0x04d7, 0x44f3, 0x4598, 0x4157, 0x4483, 0xb2f7, 0xbf5a, 0x40f9, 0x4075, 0xa00f, 0xbae4, 0xb0d9, 0x3a47, 0xbaec, 0x43b8, 0xb28f, 0x4308, 0xa8af, 0x4159, 0xb2c7, 0x4025, 0x2d96, 0x406f, 0xb28e, 0xb0e9, 0xbf44, 0xb2ba, 0x4236, 0x0e9d, 0x43cf, 0x420d, 0xb2f9, 0xb228, 0xb038, 0x42de, 0xb283, 0x43a7, 0x013a, 0xba3d, 0xb208, 0xa6da, 0x44fa, 0xb2cf, 0x3eb1, 0x1b4e, 0x404b, 0xbf60, 0xb2ae, 0x43a6, 0x44cb, 0x4297, 0xa637, 0x403f, 0xbfd8, 0xbfe0, 0x40ee, 0x41ed, 0x407c, 0x1927, 0x1c96, 0x4239, 0x19b4, 0xb25c, 0xbf3d, 0x20cf, 0xb025, 0xb2a8, 0x1849, 0x297d, 0x427e, 0x15b8, 0x42df, 0x4050, 0xb2fa, 0x4151, 0xbf3d, 0x267a, 0xb23f, 0x4098, 0x42cf, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xc83b3c92, 0xbd911bb4, 0xd5a5a9bb, 0xeab26e45, 0xe6aa6cea, 0xa200e8c6, 0x4e453677, 0x79cc53e1, 0x299e08fa, 0xe4a48c55, 0x96623e63, 0x103ae3c6, 0x15c81382, 0xac554abf, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0x00000000, 0x000001fd, 0x000000fe, 0x000000fd, 0xfffffffd, 0xff5e0000, 0x0000007a, 0x000043fe, 0xbfc51110, 0x00001384, 0x966255f3, 0x00004087, 0xfffffea7, 0xac554d4d, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xba07, 0x1e95, 0x30c3, 0x4194, 0x2be0, 0x1a8a, 0x404b, 0x4110, 0x4339, 0x444c, 0x46ec, 0xb2bb, 0xba43, 0x2218, 0x4228, 0x40af, 0xa05c, 0xb02b, 0xb297, 0xb2a6, 0x41fe, 0xbf38, 0x43f7, 0x43a4, 0x06aa, 0x41ce, 0x4586, 0x42ed, 0xbafd, 0xb020, 0x4274, 0xa264, 0x356c, 0x43f1, 0x3264, 0xac1d, 0xb252, 0x430b, 0xab27, 0xb2e9, 0xbf90, 0x1aea, 0xbfdb, 0x193b, 0x4338, 0x1bee, 0xb2c8, 0xbac2, 0x420f, 0xbf0d, 0x4206, 0xb266, 0x2a23, 0xbae6, 0x40ff, 0xbfb0, 0xb06e, 0x1a93, 0x1bfa, 0x3b77, 0x4167, 0x420b, 0xba05, 0x2a9d, 0xba3d, 0x431f, 0xba6f, 0x41e5, 0x406a, 0x307a, 0x4298, 0x41cc, 0xb202, 0xb0a0, 0xafbe, 0xbfde, 0x4625, 0x43f4, 0x1b22, 0x41c9, 0x0924, 0xbac1, 0x424e, 0x4024, 0xbf28, 0x4128, 0xb2f2, 0xa079, 0xb078, 0xb252, 0x2be8, 0x41f0, 0x4207, 0x035e, 0x42cc, 0x3a3d, 0x43d3, 0xb2ac, 0xb2e1, 0x40ec, 0xa1c3, 0xb0ae, 0xb226, 0x4084, 0xb0b4, 0xae1e, 0xb206, 0xbf2c, 0x3a9c, 0x42e1, 0x40a8, 0x0650, 0xbfe0, 0x1d2d, 0x1ddf, 0x40fe, 0xbaf2, 0x400a, 0x01fa, 0x1bc9, 0x45bc, 0x4111, 0x467b, 0x1255, 0xa90c, 0x422e, 0x1d2f, 0x00a2, 0x434f, 0x383d, 0x3bbd, 0x41b1, 0x42c7, 0xb2d2, 0x0a93, 0xbf73, 0x403d, 0xba68, 0x428b, 0xb087, 0x1943, 0x1ea1, 0x1cfe, 0x43b4, 0x1c92, 0xb215, 0x1ffa, 0xb20e, 0x40a8, 0x0631, 0xb2fe, 0x42fb, 0x4690, 0xb2e8, 0x411b, 0xa461, 0x415d, 0xb0dd, 0xbfdc, 0xbac6, 0x40b3, 0xb269, 0xb2c0, 0x10c9, 0xbac9, 0xba5d, 0x40c1, 0xbf6e, 0xa5fa, 0x1681, 0xb0da, 0x1f93, 0xb20c, 0x4624, 0xb067, 0x414c, 0x179d, 0xba2a, 0x41f6, 0x4320, 0xbf90, 0x430e, 0x45b1, 0x4008, 0x4273, 0x1619, 0xbf18, 0x093d, 0x42f5, 0x101c, 0x408e, 0xb2a8, 0x4068, 0xba64, 0x1b43, 0x445e, 0x4130, 0x424f, 0x1af2, 0xb2a9, 0xb2c7, 0x1aeb, 0x0e84, 0xb2fc, 0x42a0, 0x06a4, 0x408a, 0xbf8f, 0x40e0, 0xbf70, 0x4673, 0x3809, 0x432d, 0x4011, 0x41de, 0xb03e, 0x42e6, 0xbaf3, 0xb0ef, 0x40e5, 0x4616, 0x40e3, 0x445b, 0xbf60, 0x46d3, 0x41a9, 0x4012, 0x189d, 0xba3b, 0x42ea, 0x418b, 0xbfba, 0x4306, 0x4106, 0x4177, 0x1a98, 0x1fa5, 0x4220, 0x466e, 0xb272, 0x42ab, 0xae2c, 0x41d9, 0x4335, 0x400a, 0xbf80, 0x34cf, 0x060e, 0xb233, 0xba1c, 0x2797, 0xb0a7, 0x446d, 0xb265, 0xadc0, 0xaf88, 0xbf5b, 0x418b, 0x40b3, 0xa037, 0x40fc, 0x4186, 0x4294, 0x43d1, 0x415d, 0x43a0, 0x438f, 0xbf9e, 0x461d, 0x198d, 0x388a, 0xada6, 0x41f7, 0x4335, 0xb24f, 0x2507, 0x1d8e, 0xb29a, 0x1f43, 0x42d2, 0xbae6, 0x42de, 0xa28d, 0x1aeb, 0x43dd, 0x40f9, 0x4253, 0x22a2, 0xa9b0, 0xb232, 0xbacb, 0xba56, 0xa533, 0xba38, 0x4154, 0xba5c, 0xbf15, 0x35e5, 0xb2c0, 0x43ce, 0x0e9c, 0x46eb, 0xbfc0, 0x40eb, 0x44fc, 0x42f3, 0x4337, 0x439c, 0x1aa7, 0x435e, 0x4215, 0x0762, 0x4186, 0x403d, 0x41b2, 0xbf5a, 0x42dc, 0x4097, 0x40f6, 0xb284, 0xba73, 0x4648, 0x408f, 0x407a, 0x1cd9, 0x4446, 0x43aa, 0xbf12, 0x11a8, 0x4164, 0x11f9, 0xae18, 0xb0c6, 0xb0de, 0x43af, 0x405a, 0x4328, 0x4236, 0xace1, 0xa3a0, 0xa0ac, 0x1c62, 0x1dc2, 0x431e, 0xba78, 0x433c, 0x42f8, 0x467f, 0x102a, 0xba5f, 0x422f, 0x4168, 0x431e, 0xa297, 0xbf41, 0x1ac0, 0xb0c8, 0xb2b8, 0x4290, 0x1a1a, 0x2d4d, 0x409e, 0x1b2d, 0x430c, 0xbfe1, 0x41e9, 0xba38, 0x1dc3, 0x400f, 0xba5e, 0x4158, 0xb011, 0x43bb, 0x40f6, 0x1d74, 0xba3c, 0x1af3, 0x4607, 0x2a9a, 0x44c8, 0x40cd, 0x43cd, 0x4253, 0x0b40, 0x419c, 0x1afb, 0x405f, 0x1664, 0xb076, 0x3250, 0x3abc, 0x4282, 0x425d, 0xbf8a, 0xb0dc, 0x2556, 0x41d1, 0x4251, 0x4046, 0x14af, 0x1b2e, 0xb22f, 0x1cbc, 0xb21d, 0x401d, 0x18e1, 0x436a, 0xbfd3, 0x07d1, 0xb2df, 0x24c2, 0x416b, 0x1bd4, 0x1d69, 0x4172, 0x4049, 0x3a4d, 0xacb4, 0x19c2, 0x45a5, 0x1236, 0x2647, 0x3ed1, 0xb03f, 0x43f8, 0xac19, 0x41b9, 0xbf34, 0xba5f, 0xb23c, 0x238f, 0xba3e, 0x4630, 0x4376, 0xb263, 0xb261, 0x431e, 0x4069, 0x1a93, 0x407f, 0x4000, 0x40a3, 0x42c8, 0xbfd4, 0x4322, 0xb2e4, 0xb0ad, 0xb2e1, 0xb273, 0xb203, 0x4064, 0x43b4, 0x144b, 0x41a3, 0x4143, 0xacf2, 0xa4dc, 0xbfe2, 0x4101, 0x406d, 0x4075, 0xba34, 0xb0e0, 0x4010, 0xb247, 0x437a, 0x2989, 0x4375, 0x290c, 0x464a, 0x43b0, 0xb201, 0x4320, 0x4097, 0x2155, 0xaa34, 0x1967, 0xb211, 0x3a11, 0x1522, 0xba36, 0x1b4a, 0xbf6d, 0xb216, 0xb2e3, 0x1c04, 0xa4c9, 0x1b2f, 0xbfe0, 0x43bb, 0xb2af, 0xa800, 0xbf90, 0x4016, 0xba5e, 0xbf7c, 0x4193, 0x19a4, 0x1235, 0x0b1f, 0x429d, 0x2f62, 0x43d7, 0x4341, 0x2659, 0xba31, 0x094f, 0x44db, 0x4034, 0x418e, 0x1f48, 0x46a2, 0xba6a, 0xbf4d, 0xb23d, 0x404d, 0x4111, 0x0d4b, 0x40fd, 0x41d2, 0x403e, 0x30ee, 0xb26f, 0x1ebb, 0x4201, 0xa2a4, 0x402e, 0x40df, 0x2596, 0x4395, 0x2ab3, 0xb276, 0x4287, 0x14a6, 0x4313, 0xbf85, 0x1cc0, 0x46cc, 0x440b, 0x243b, 0x4123, 0x15af, 0x40ae, 0x4336, 0x41b3, 0xb207, 0x40b7, 0x40ea, 0x4288, 0xa9af, 0x40ae, 0x4173, 0x4293, 0x40a2, 0x2d32, 0x12f0, 0x4265, 0x339c, 0x2d1d, 0x4273, 0x4033, 0xb273, 0xa8dd, 0x4088, 0x4393, 0xbf85, 0x200b, 0xbfc0, 0x4370, 0x416a, 0xba73, 0xbafc, 0x40d6, 0xb20a, 0x09bc, 0x366a, 0x3509, 0xbf84, 0x2e0f, 0x419a, 0xb06a, 0x41cd, 0x4115, 0x433e, 0x1cdb, 0x42d0, 0x46f3, 0xb06c, 0x388e, 0x43b6, 0x41ed, 0x190c, 0x43ab, 0x1654, 0x4043, 0x427d, 0x0edd, 0x4022, 0xbaca, 0x0775, 0x18b0, 0xa255, 0xbfb9, 0x1cb8, 0x41c3, 0xb245, 0xa4a7, 0x07dd, 0x37c1, 0x41fb, 0x42d0, 0x349d, 0x42db, 0xbf9b, 0xba23, 0x40a2, 0x43f6, 0x43d2, 0xba6f, 0x368e, 0x4004, 0x1d3f, 0x43ee, 0x4127, 0xbf80, 0xbaec, 0x410a, 0x2bd6, 0x2a9f, 0x40cc, 0x40dc, 0xba0b, 0x43c4, 0x4554, 0x1afc, 0xa651, 0x1ebe, 0x41aa, 0xbf60, 0xb23f, 0xa420, 0xbf4b, 0x4412, 0x4168, 0xb200, 0x42bd, 0x4439, 0x188c, 0x0cce, 0x43a1, 0x1936, 0x36b8, 0x41a5, 0x1fd1, 0x193d, 0x44c9, 0x38d3, 0x40d8, 0xb22e, 0xbfac, 0x1893, 0x2638, 0x17ae, 0x404a, 0x44c8, 0xb257, 0x4097, 0x4328, 0x414e, 0x1e25, 0x424e, 0x41f0, 0x08f4, 0xbfd0, 0xb081, 0x4348, 0x4062, 0x43af, 0xb2fa, 0xbfdf, 0xad9a, 0x14a9, 0x1b10, 0x40c4, 0x430b, 0xba6c, 0xbaee, 0x4670, 0x40cf, 0x415c, 0xb29c, 0x4095, 0xa03c, 0x45f3, 0xbfa0, 0x1b48, 0xbf53, 0xad0c, 0x1f88, 0x3697, 0x42a2, 0x2c12, 0x2442, 0xbad7, 0xa0c2, 0xb277, 0xb0d9, 0x43e8, 0xa0c3, 0x4006, 0x4399, 0xbfca, 0x0cbc, 0xb261, 0x2cc7, 0x417e, 0x40d9, 0x43f3, 0xb27c, 0x09c8, 0xb0b9, 0x125f, 0x429f, 0xbfc3, 0x3d9c, 0x0f46, 0x0047, 0x4258, 0x4081, 0x1968, 0x45a1, 0x292c, 0x4002, 0x4463, 0x1a41, 0x414a, 0xba3f, 0x3ebb, 0x152e, 0x41f9, 0xb25f, 0x4005, 0x36c5, 0x22ec, 0xba69, 0x1ab9, 0x32de, 0xba32, 0x1867, 0xb296, 0xb2c2, 0x06f3, 0xb2e9, 0xbf8d, 0x1ea4, 0x406a, 0x46ad, 0xbacc, 0x4677, 0x4097, 0xbfa0, 0x2090, 0x0a7d, 0x400a, 0x42b6, 0xba75, 0xbaf3, 0x43e5, 0xa474, 0x419e, 0x4425, 0x3cd2, 0x46fc, 0x4043, 0xbfc5, 0x32fe, 0xaf8a, 0x1802, 0x1dfe, 0x432f, 0xb002, 0x42ef, 0xba54, 0x363d, 0x1fc4, 0x4619, 0x4206, 0x4247, 0xb0ef, 0xac69, 0xb276, 0x45b6, 0x4322, 0x3c86, 0x42fd, 0xbf5c, 0x1e9d, 0x0ef5, 0x4118, 0xb037, 0x315b, 0x42a2, 0xb083, 0x4245, 0xba51, 0x46f9, 0xbf70, 0x34d3, 0xb281, 0x43b7, 0xba6b, 0xb0d4, 0xba17, 0x431b, 0x4084, 0x425a, 0x0967, 0x40e1, 0x4571, 0x4429, 0xbf87, 0xb265, 0x4011, 0x4066, 0xba0f, 0xb0f6, 0x4334, 0x1a54, 0x0d0c, 0xb271, 0x4394, 0xbf48, 0x1fae, 0xac17, 0x1fea, 0xb2d6, 0x2631, 0x3ebb, 0x1d7f, 0x46c4, 0xba4f, 0xbf7b, 0x45aa, 0x4240, 0x3de4, 0xbafa, 0x41c2, 0x45b5, 0xba4c, 0x4129, 0x4202, 0x4489, 0x4220, 0x43f3, 0xbfd0, 0x421e, 0xbfa0, 0x0784, 0x4181, 0x42f5, 0x42ae, 0xbfb6, 0x40f3, 0xbaf7, 0x030d, 0x430c, 0xb024, 0x4162, 0x4055, 0x444b, 0x4278, 0xb2f9, 0xbfc5, 0x2d34, 0x41c2, 0x4260, 0xa8c5, 0x0235, 0xb0a5, 0x44b0, 0x2efc, 0x1bbd, 0x41d0, 0x062f, 0xbfba, 0xb25a, 0xb023, 0x46fa, 0xbac7, 0xbfd0, 0x0a43, 0xbf14, 0x3ff5, 0x4073, 0x4130, 0x41a7, 0x2bd7, 0xbf90, 0xb2e5, 0x4030, 0x41bb, 0x4262, 0x2062, 0x4021, 0xb266, 0x1a01, 0x4264, 0xb234, 0x40cb, 0x425d, 0x25e2, 0x414a, 0x3c20, 0xb0ea, 0xa1c7, 0x14e0, 0xbfb7, 0x4170, 0x42fc, 0x43d7, 0x4580, 0xb26d, 0x1a13, 0x44dc, 0xb244, 0x00e2, 0xb088, 0x422a, 0xb203, 0x1ac1, 0xba09, 0xa3ad, 0xbfd0, 0x1c62, 0xbf26, 0x02ce, 0x167e, 0x4108, 0x1b88, 0x4351, 0x4259, 0xb05d, 0x40e7, 0xbf00, 0x42aa, 0x1aac, 0xb201, 0xb036, 0xba1c, 0x429f, 0x033c, 0x462b, 0xa687, 0xbf60, 0x4216, 0xb2b0, 0xb24c, 0x4381, 0xbf75, 0x3c94, 0x4360, 0x143b, 0x40a5, 0xbaca, 0x32ca, 0x109f, 0x353e, 0x1e88, 0x4123, 0x1418, 0x4040, 0x40ae, 0x4332, 0x134c, 0x4211, 0x4271, 0x1ddd, 0x2885, 0x431f, 0xbfc5, 0x1dc1, 0x4296, 0xb2af, 0xb2b3, 0xb2d0, 0x2abd, 0x296b, 0x3323, 0x418f, 0x438a, 0xa4d6, 0x1ae8, 0x0ab5, 0x4173, 0xb095, 0x42db, 0xb226, 0xb03d, 0xbf2f, 0xb210, 0xb247, 0x437f, 0xb2f5, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x804206f6, 0x99583a3f, 0xad754391, 0x26941fad, 0x139e5e23, 0x688cb146, 0x1ba6feec, 0x7e394ee6, 0x68aeb641, 0x77239003, 0x176ac985, 0x4d825eb1, 0xa99614a6, 0x1716467f, 0x00000000, 0x900001f0 }, FinalRegs = new uint[] { 0xffffffe4, 0x00000000, 0x000021b0, 0x00000023, 0x00001b18, 0x00000018, 0x00001b18, 0x00000310, 0x4a3eb53f, 0x0000169f, 0x00000010, 0x00000000, 0x4a3eb5c9, 0x0603ff82, 0x00000000, 0x000001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x42b1, 0x434a, 0x1868, 0x1910, 0x4205, 0x3246, 0x2096, 0x439d, 0xba7d, 0xba78, 0x180e, 0xa168, 0xafc3, 0x40e6, 0xb2c6, 0xbf60, 0xbf80, 0x46ca, 0x2a71, 0xa766, 0xb269, 0x1f80, 0x14d9, 0x4050, 0xb209, 0xbf72, 0x0b06, 0x4024, 0x41fe, 0xa917, 0xba63, 0xb282, 0x41f2, 0x4171, 0x4288, 0x23f7, 0x032e, 0x4043, 0xb23d, 0xbf12, 0x430e, 0x1c8f, 0x1851, 0x4341, 0x0360, 0x4090, 0x2675, 0x4281, 0x411e, 0x4255, 0xba5b, 0xb283, 0x1af5, 0xb28d, 0xbf33, 0x41ed, 0xb07c, 0xae17, 0x1e11, 0x19ce, 0xb296, 0x33f9, 0x4392, 0xbaf3, 0x40bd, 0x433e, 0x40f9, 0xa383, 0x1b86, 0x1c9f, 0xb096, 0xbf00, 0x4369, 0x42ce, 0x1708, 0x37f1, 0xbf11, 0x431e, 0x28cb, 0x245a, 0x434f, 0x4298, 0x0366, 0x1b85, 0x22a8, 0x4214, 0x42fc, 0x43ca, 0x33ae, 0x42da, 0x1828, 0xabe1, 0x40e4, 0x39be, 0x1a59, 0xb01f, 0xbfb8, 0xb20e, 0x41cc, 0xb2ce, 0x4122, 0xac37, 0x435d, 0xbfc3, 0x4261, 0xb071, 0x24ed, 0xbadd, 0xba07, 0x19a6, 0x1e22, 0x4241, 0x4265, 0xb2f8, 0x401a, 0xb043, 0x4034, 0xb236, 0xb28b, 0x3189, 0xb085, 0xb239, 0x4409, 0x429d, 0x41c2, 0x30cc, 0xbfdf, 0xbaea, 0x40da, 0xb247, 0x1e98, 0xb20e, 0xbad2, 0x4136, 0x1f95, 0xaf3a, 0x2cae, 0x427a, 0x4015, 0x409f, 0x025c, 0xafcb, 0xb20f, 0x43a1, 0x1b77, 0x4222, 0x400b, 0x42c5, 0x1f2f, 0xbfc7, 0xb2ce, 0x4608, 0x4694, 0x42cb, 0x2963, 0x3619, 0x41dd, 0x43ac, 0xb204, 0x3c86, 0xbf05, 0xbac9, 0x1f7c, 0xba2e, 0x0be5, 0x38aa, 0x41aa, 0xa6f7, 0x432b, 0x4124, 0x43fa, 0x1d2d, 0x42d4, 0x1acf, 0x465e, 0x1aa0, 0x32b0, 0x410a, 0x4205, 0xb0b0, 0xbf9c, 0x409d, 0x3b35, 0x1b4f, 0x43eb, 0x189c, 0xbfb3, 0x43f9, 0x42a9, 0x19b1, 0x45cc, 0x1f2f, 0x2d27, 0x1f79, 0x41aa, 0xb20a, 0x420d, 0x45e0, 0xbf1e, 0xb008, 0x4254, 0x2b5d, 0x2382, 0xbf6a, 0x416f, 0xba16, 0x11bf, 0x40de, 0x1a5a, 0x182a, 0x4051, 0x40a8, 0xbf92, 0x41bc, 0xa479, 0x44ed, 0xa6bd, 0x449d, 0x35e7, 0xa2eb, 0x3f57, 0xb0a6, 0x191c, 0x401b, 0xb265, 0x4155, 0x4227, 0x42a1, 0x3f24, 0x1de3, 0x4312, 0x434f, 0x467f, 0xbfce, 0x4054, 0x42c8, 0x18a8, 0x4355, 0x42ad, 0x4139, 0x0b84, 0x2b9b, 0x3f55, 0x42ba, 0xbfc5, 0xa96c, 0x33cc, 0x41c6, 0xba77, 0x405f, 0x4009, 0x4106, 0x1ace, 0x0146, 0xbf1a, 0x1e1b, 0x41fb, 0x42b8, 0xbf82, 0x3dac, 0xaba4, 0xa622, 0x42d3, 0xb060, 0xba3f, 0xbaf0, 0x41b1, 0x4030, 0x410b, 0x1f0b, 0x03e5, 0x42a7, 0x2640, 0x416b, 0xb261, 0x1eaa, 0x4118, 0xba72, 0x4083, 0xbf31, 0x3606, 0xbac0, 0x25ff, 0xa90b, 0xa970, 0x4680, 0xb219, 0x4312, 0xb2b2, 0x4400, 0x1f32, 0xba03, 0x4371, 0x420f, 0x424b, 0xba02, 0x41f4, 0x46c1, 0x3158, 0xab7d, 0xba0b, 0xba2b, 0x42a4, 0x3e0b, 0x02c5, 0x4039, 0x4111, 0xb2f1, 0x0250, 0xbf5d, 0xbaf8, 0x464c, 0x43e7, 0x1baf, 0x41a3, 0x45c1, 0x4369, 0x4113, 0x415e, 0xbff0, 0x2a72, 0x1e9d, 0xba79, 0x083f, 0x41fe, 0x418c, 0x467e, 0x4472, 0x4484, 0xbf62, 0x4671, 0x4698, 0x4141, 0x41d0, 0x29ea, 0x1d92, 0xba36, 0xba01, 0x188a, 0x3f58, 0x43ec, 0x4396, 0xa1b9, 0x4266, 0x40ba, 0x4122, 0x43a4, 0xb269, 0x401f, 0x18f0, 0xb241, 0x4119, 0xb07a, 0xbf95, 0x08c9, 0x37d4, 0x21c0, 0x43e4, 0x4165, 0x468a, 0x40b6, 0xba3b, 0x0e8d, 0xba2f, 0xb287, 0x4223, 0x1bab, 0xb2e8, 0x41dd, 0x4065, 0xbf6c, 0xb23f, 0x2e6d, 0xb2fa, 0x4252, 0x0c56, 0xa14e, 0x4313, 0x42c3, 0x427c, 0x41ca, 0x430f, 0x4187, 0x11c1, 0x17df, 0x40ac, 0xb2c7, 0xb23e, 0xb0b3, 0xa0f6, 0x430f, 0xb25b, 0xbf81, 0xa50d, 0xb258, 0x217d, 0x40a7, 0x1bd7, 0xa080, 0x4248, 0x2643, 0xba66, 0xba2b, 0x4349, 0x1edb, 0x428e, 0x40a9, 0x2b5e, 0x439b, 0x417e, 0x402e, 0x12a6, 0x0e8d, 0xbfbb, 0x424f, 0xb06e, 0x4032, 0xa1c4, 0x4271, 0xb2e8, 0x4674, 0xb0cc, 0x427d, 0x42ac, 0x469b, 0x416b, 0xb201, 0xb2d2, 0x401a, 0x42b5, 0x4397, 0x4299, 0xb238, 0x4664, 0x40a4, 0xbafc, 0xbf7b, 0x1f88, 0x4334, 0x414b, 0x4365, 0x3112, 0x1aa7, 0x1ea7, 0x40f3, 0x1197, 0x45da, 0x411f, 0xbfdb, 0xb020, 0xba4b, 0x1e98, 0x42ea, 0x4388, 0x4344, 0x1956, 0x4225, 0x45c5, 0xb2b9, 0x435c, 0x1e1b, 0x40b7, 0x1bb0, 0x0400, 0x40d0, 0x43e1, 0x4071, 0x40ea, 0xb0b7, 0x122c, 0xb27d, 0x40d4, 0x4470, 0xbfc8, 0x4002, 0x408c, 0x0287, 0xb201, 0x4221, 0x408d, 0x42f2, 0x4009, 0xb2dd, 0x43ae, 0xba01, 0xbf17, 0xb2fb, 0xb271, 0x209a, 0x4201, 0xb203, 0x4202, 0x43e9, 0x2338, 0x40af, 0x2f11, 0x40c6, 0xbf70, 0x0018, 0xa318, 0xbae2, 0xb063, 0x1bbf, 0xba6f, 0xbf60, 0xb2fa, 0xb2c2, 0x40f1, 0xba51, 0xa87a, 0xbf1b, 0x43fe, 0x408f, 0x42b4, 0x09d9, 0xb2a1, 0xbae5, 0xbf32, 0x41fe, 0xa3c9, 0x42ec, 0x1bf6, 0x4151, 0xb2ac, 0xb26c, 0xa3e7, 0x1c0c, 0x4298, 0xb291, 0x41c3, 0x428b, 0xb278, 0xb062, 0x4054, 0xb232, 0x40c4, 0x1067, 0x43ea, 0x4261, 0x43c9, 0x1dca, 0xb044, 0xbfd4, 0xb2e7, 0xb221, 0xabdc, 0x42e2, 0x417e, 0x43fa, 0x42f2, 0xbaf1, 0xb237, 0x46b1, 0xbf70, 0x1b90, 0x1baf, 0x4029, 0xba6d, 0x4112, 0x4305, 0x2569, 0xb0c6, 0xbf2a, 0x430a, 0x434e, 0x411a, 0x4233, 0x41a0, 0x175a, 0x41c1, 0x4235, 0x404a, 0xb090, 0x40f4, 0xb25c, 0xb2b1, 0x41cb, 0xba1a, 0x4064, 0x1f35, 0x423d, 0x4124, 0x4264, 0x4344, 0xbf6b, 0xb0d3, 0x0de7, 0x40c9, 0xb204, 0xb257, 0x1d42, 0x4340, 0xba64, 0x2cc3, 0x23ec, 0xb223, 0x1ea1, 0x3050, 0x402c, 0x43e3, 0x2354, 0x4044, 0xbac8, 0xb262, 0x42c0, 0xb2c2, 0x40d2, 0x42e3, 0x45a2, 0x4176, 0x40c3, 0xbf45, 0xb069, 0x467a, 0xb00e, 0x4571, 0x412b, 0xbfb0, 0x42ab, 0x2c01, 0x461d, 0x33c4, 0x4124, 0x2d04, 0xbac0, 0xba2d, 0xb04a, 0x431c, 0xbf05, 0xb220, 0xb057, 0x2eb8, 0xba23, 0xbace, 0x38d4, 0xbfe0, 0x4076, 0xb2b3, 0x429a, 0xb041, 0x43c3, 0x3b35, 0x43f0, 0xb203, 0xa113, 0x2d45, 0xbff0, 0x1f05, 0xbf67, 0x422b, 0xba7c, 0x2794, 0x40c5, 0xbfba, 0x41a3, 0x43be, 0xbff0, 0xb27b, 0xb23c, 0x2e8b, 0x3d1e, 0x0282, 0x425e, 0xbad1, 0x4005, 0xbad3, 0xb0e5, 0x4227, 0xb010, 0x4381, 0xb2b3, 0xbf46, 0xb2ce, 0x1f53, 0x44ea, 0xa888, 0xae43, 0x424c, 0x1e4e, 0x18c7, 0x4424, 0xb0ff, 0xb274, 0x4294, 0x341d, 0x40bb, 0xa1b2, 0x4564, 0xba76, 0xbad6, 0x4177, 0xb000, 0xbae7, 0xbf8c, 0xb24e, 0x0516, 0x178a, 0xa2d3, 0x42bc, 0xbf32, 0x2fc5, 0x3c82, 0x188b, 0xb265, 0x4087, 0xb2c3, 0x293d, 0xba08, 0x438a, 0x0d71, 0x22c9, 0xb09a, 0xb2c6, 0xb259, 0x3f8c, 0x43bf, 0xbfd0, 0xbfc4, 0xb28a, 0x1f75, 0xb0dc, 0x418f, 0xae2d, 0x193f, 0xbadd, 0x31f3, 0x403c, 0x1537, 0xbaf9, 0xb299, 0x27eb, 0x4330, 0xb2df, 0xbf1d, 0xb253, 0xa5e3, 0x1b97, 0x40e3, 0x229a, 0xb0e9, 0x204d, 0x1e6b, 0x4217, 0xb056, 0x3e7d, 0x4347, 0x4151, 0x24fa, 0x231c, 0x4151, 0xb0bf, 0x4261, 0x4294, 0x1499, 0xa071, 0x4389, 0x40f0, 0xbf24, 0x43f0, 0xa34e, 0x3d28, 0x40fc, 0xba75, 0x42b5, 0xb235, 0x276f, 0xa13d, 0x4045, 0xae0d, 0x4339, 0xba7f, 0x46e0, 0x3011, 0xb222, 0x106e, 0x421e, 0x43fc, 0x306e, 0xbf6e, 0xb293, 0x410b, 0x1cf9, 0x4073, 0x38a8, 0xba4f, 0x0e8e, 0xba5a, 0x46e5, 0x0c13, 0x438c, 0x0dc5, 0xbf00, 0x42e2, 0x4098, 0x3453, 0x1904, 0xba61, 0x4475, 0xbf3c, 0x4280, 0x431e, 0x4411, 0x12e6, 0x3dce, 0x1d7d, 0x41f3, 0xb204, 0x1e51, 0x1e1c, 0x4082, 0x4220, 0x0544, 0x40eb, 0x40f7, 0x4161, 0x1ae9, 0x43e3, 0x4071, 0x2ecb, 0x401e, 0x44b5, 0xb2b8, 0xbf9a, 0x41ec, 0x324e, 0x19af, 0x44f2, 0xb2ce, 0xb248, 0x42dc, 0xba09, 0x3d53, 0x1a5c, 0xa24d, 0xa580, 0x08df, 0xba14, 0x4695, 0x4409, 0x4212, 0x0169, 0x41b5, 0xb2ae, 0x2638, 0x42b9, 0xbfcf, 0xb292, 0x441a, 0x19f3, 0x1831, 0xb200, 0xb2b0, 0xb2f8, 0xba7e, 0x43bf, 0x108a, 0x4113, 0xab7b, 0x42e2, 0x43bb, 0x406e, 0x44fa, 0x4225, 0xb2bb, 0x4416, 0x32c6, 0x41d4, 0x033a, 0x40f5, 0xba4d, 0xbfdb, 0xbf90, 0x3054, 0x1bc7, 0x2374, 0x40b9, 0xb2a7, 0x2123, 0x42c4, 0x2200, 0xb24b, 0xb096, 0xa563, 0x415a, 0x099a, 0x1bdd, 0x4279, 0x3b38, 0x421b, 0x3c50, 0x1efa, 0xb2ed, 0xb0ab, 0x1914, 0x34d6, 0x2af8, 0xba20, 0x056e, 0xbf52, 0x427b, 0x41dc, 0xbfc0, 0x0e3c, 0x4107, 0x439d, 0xb260, 0xb25d, 0x429c, 0x46b3, 0x179c, 0xbfc7, 0x43c5, 0xb27f, 0x197b, 0x402d, 0xb264, 0x4117, 0x2330, 0xbf70, 0x4073, 0xbf00, 0xbad5, 0xb069, 0x42ef, 0xaf7f, 0x4002, 0x436d, 0x4121, 0x1410, 0x1f1a, 0x042f, 0x423d, 0x3abd, 0xbf33, 0xb0cc, 0x425d, 0xb0ec, 0x41da, 0x4335, 0x4263, 0xb2b0, 0x047c, 0x0a77, 0x4326, 0xbac0, 0xabe5, 0x417c, 0x4141, 0xadbf, 0xbf62, 0xb051, 0x2cbe, 0x1cfa, 0x428d, 0x10c9, 0xa7b6, 0xba01, 0xbfd0, 0x4247, 0x43fe, 0x441a, 0x2a92, 0xab72, 0x406b, 0x2f61, 0xbfcc, 0x189e, 0x0ed2, 0x4337, 0x41fa, 0x4049, 0xb207, 0xa86f, 0x439d, 0xa2fa, 0xbf93, 0x1b7d, 0xb0b2, 0x2ea1, 0xaf72, 0xae7d, 0x436a, 0x415b, 0x412c, 0x1964, 0x1660, 0x3250, 0x40bb, 0x41b4, 0xa262, 0x430c, 0xbf22, 0xabab, 0x3c05, 0xb04a, 0x4322, 0x410c, 0xb23a, 0x26f1, 0x43db, 0xba7c, 0x42f8, 0x1fa5, 0x43f5, 0xad41, 0x4276, 0xbf85, 0x42e1, 0xb06d, 0x0bee, 0x43e4, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xe4628e3c, 0xb130dc40, 0x301f3068, 0x711e026e, 0x29a5a394, 0xa6eb85b1, 0xcd2f7a79, 0x2ec8b90f, 0xd90284cf, 0xe8b9b1aa, 0xa23589aa, 0x4701154b, 0x09e98069, 0x05583b86, 0x00000000, 0x000001f0 }, FinalRegs = new uint[] { 0xffffffff, 0x00000000, 0x00001870, 0xffffe6ab, 0x00007018, 0x000018d4, 0x00000000, 0x00001870, 0x09e98068, 0xffffffff, 0x05585795, 0x0c600000, 0x09e98068, 0x000017d0, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x190c, 0x41dd, 0xb2fe, 0x408e, 0x4222, 0x32ca, 0x40b9, 0x3388, 0xba3b, 0x455c, 0xbfd0, 0x0a17, 0x40b6, 0x1dc6, 0x316f, 0x1fe4, 0x1ddd, 0x4312, 0x1f2e, 0x41c8, 0x4142, 0x43e6, 0xbf64, 0x4407, 0x4230, 0x46a5, 0x43bc, 0x422b, 0x43b7, 0x4111, 0x4125, 0x42fc, 0xb0eb, 0xba23, 0x4375, 0xb002, 0xba10, 0x4272, 0xb282, 0xb0f4, 0x2703, 0x4192, 0xb2c8, 0xbf3a, 0x415a, 0x41dc, 0x2e6f, 0x4326, 0xa155, 0x1c7e, 0x43a5, 0x3588, 0xbf33, 0x434c, 0x3d8b, 0xa455, 0x4028, 0x42b9, 0x42e8, 0x4117, 0x12c6, 0xb2ea, 0x0555, 0x39c4, 0x4674, 0x4339, 0xb2fd, 0xb241, 0xba30, 0x42f0, 0x2e82, 0x418f, 0xbad0, 0x4253, 0x075f, 0x4412, 0x1dbd, 0xb229, 0x43c2, 0xbfae, 0xb085, 0xb2f3, 0xb0e9, 0x1b1f, 0x1f0e, 0x281f, 0x18e7, 0x30b9, 0xbadf, 0xba64, 0x42ec, 0x4208, 0x2e04, 0x2730, 0x430e, 0xb2aa, 0xb244, 0x4103, 0x1eb8, 0x4229, 0x1e5c, 0x40b5, 0x3def, 0xa5b2, 0x33b8, 0x4389, 0x42a1, 0xba14, 0xbf98, 0xa852, 0x4343, 0x40e5, 0xbaf5, 0x188e, 0x1ac6, 0x4073, 0x0c0c, 0xbfc2, 0x4028, 0x404d, 0x1860, 0x442f, 0xb0f6, 0xbf61, 0x3c74, 0xbf60, 0x41a0, 0x1c6a, 0xba14, 0x12cd, 0x40bb, 0xbf70, 0x4034, 0xbfc2, 0xbf00, 0x40d4, 0xb2f2, 0x433a, 0x001a, 0xac53, 0x0ab7, 0x428d, 0x443b, 0x4335, 0x27cc, 0x1e0a, 0x40e1, 0x17e2, 0xbad0, 0xb297, 0x405b, 0xb270, 0x429a, 0xbad7, 0x41de, 0x4210, 0x1acf, 0x42a3, 0x443b, 0x1d64, 0x460d, 0xbf06, 0xbad0, 0xb2b7, 0x4092, 0xba0f, 0x438c, 0x439f, 0x291d, 0x2fba, 0xb2b8, 0x435c, 0xbf9b, 0xac65, 0x4277, 0x4171, 0x2bd5, 0x0832, 0x46c5, 0xb275, 0xb292, 0x4012, 0x1396, 0x4014, 0xb046, 0xba1d, 0xb2a1, 0x067f, 0xb26b, 0x432a, 0x1630, 0x2119, 0xb202, 0x42cc, 0xbf54, 0xb004, 0x43df, 0xaad2, 0xa411, 0x1ea9, 0x40d3, 0x1fe2, 0xaf6c, 0xb068, 0x40ab, 0x2124, 0x39a3, 0x1a23, 0xba65, 0xb2eb, 0xbfa4, 0xb035, 0xba7c, 0x40b7, 0x4013, 0xb292, 0x444c, 0xb0d2, 0x401e, 0xba6a, 0xba5b, 0x4368, 0x46ec, 0x4493, 0x4016, 0x4601, 0x4049, 0x15b2, 0xb298, 0x18e4, 0xb22a, 0x1f07, 0x101f, 0x1908, 0xbfaf, 0x434e, 0x1125, 0x41d5, 0x1322, 0x41d0, 0xbfa2, 0x4349, 0x1fa5, 0x4289, 0x1ebd, 0x10be, 0x4328, 0x4084, 0x4076, 0xa326, 0x4262, 0xba59, 0xb017, 0x1eb2, 0x43e8, 0x43a9, 0xb014, 0x089a, 0xb22b, 0x1cf0, 0x4490, 0x1529, 0x4371, 0xbfa0, 0x43cc, 0x4127, 0xbfb7, 0xba00, 0xba0d, 0x41f4, 0xb0ea, 0x1e04, 0xbacd, 0x40bf, 0x46bc, 0x403b, 0x2642, 0x400b, 0x1d25, 0x4339, 0x4124, 0x43fb, 0x4203, 0x40f2, 0xb0f1, 0xac2d, 0xbfc0, 0x1f1c, 0x3447, 0x40b2, 0xbfa8, 0xbfb0, 0xbfb2, 0x4373, 0xabab, 0x428b, 0x400a, 0xaa61, 0xbff0, 0x46c0, 0x45e8, 0x1f40, 0xbae0, 0x2815, 0x4130, 0x39ef, 0xbace, 0x3978, 0x1c85, 0x4023, 0x1fbc, 0xbf01, 0x413b, 0x1f38, 0x4248, 0xa2f5, 0x1354, 0xa20b, 0x4287, 0x2088, 0x377f, 0x4029, 0x41a5, 0x0b8a, 0xb2ff, 0x419e, 0x003c, 0xb28e, 0xbf2a, 0xbad0, 0x3fea, 0x4138, 0x434b, 0x41c3, 0x4371, 0xb08c, 0xb240, 0x41d3, 0x42c1, 0x43ee, 0x42ae, 0x41e0, 0xb287, 0x1c7c, 0x41fc, 0x4130, 0x46e1, 0x4391, 0x1d6d, 0x401f, 0x189e, 0xbf24, 0x206b, 0xb27e, 0xb2e3, 0x432a, 0xba3f, 0x43b6, 0x1e9c, 0x41e6, 0x1943, 0x411a, 0x40d7, 0x4367, 0xa70f, 0x1088, 0x422f, 0x1d6e, 0x197a, 0x42ae, 0x3fb9, 0x01b5, 0xb226, 0x40c7, 0x3246, 0xbf58, 0xb07e, 0x4250, 0xba4e, 0xa4d2, 0xa9fa, 0x2365, 0xb28d, 0xa57c, 0xbaf7, 0xba5b, 0xb010, 0xba53, 0xbadf, 0x4122, 0xbf57, 0xb010, 0x4374, 0xbae0, 0xb2fb, 0x1fda, 0x41c7, 0x432a, 0xaa9f, 0x19da, 0x1f13, 0xb2e1, 0x1d38, 0x44a4, 0xbfb7, 0x438e, 0x054a, 0x425a, 0x3229, 0xbac4, 0x40a6, 0x404f, 0x4348, 0x41da, 0x40db, 0xafa8, 0xb0ba, 0x4004, 0x432c, 0xb058, 0x4324, 0x1daf, 0x4309, 0x42bd, 0x18cf, 0xbf6e, 0x42ee, 0x418a, 0xb270, 0xb287, 0xb209, 0x4271, 0xb0e0, 0x3c45, 0x1a47, 0xb284, 0xb224, 0x410f, 0x4230, 0xb239, 0x41d8, 0xb09f, 0x4125, 0xba3f, 0x18ea, 0x1a7b, 0x3bb4, 0xb018, 0xbf4a, 0x2a5f, 0x31f9, 0x411f, 0x4142, 0x423b, 0x4573, 0xbfb0, 0xba72, 0x1940, 0xb032, 0xbae0, 0x43e3, 0xba0c, 0x2537, 0x4081, 0xb211, 0x15fa, 0x41fa, 0xbfa0, 0xab62, 0xb0d6, 0xba2d, 0xbf51, 0x400b, 0x30b2, 0x4036, 0xaea0, 0xbf00, 0x42e8, 0x2b15, 0x1f0d, 0x381f, 0x4103, 0x4097, 0x41be, 0x235b, 0x4091, 0xbaf5, 0xb296, 0x455e, 0xb252, 0x4223, 0xba21, 0xbf90, 0x2bb5, 0x4153, 0xbf31, 0xa17a, 0x1938, 0x4210, 0x4274, 0x2b0c, 0x43b2, 0x411d, 0x1a71, 0x40e4, 0x2d1a, 0x4041, 0x4091, 0x40c5, 0x4082, 0xb256, 0x43a8, 0x4365, 0x426f, 0x42fc, 0xb058, 0xb098, 0x4077, 0xb074, 0x426f, 0x2362, 0x40fd, 0xbf22, 0xb2ba, 0xbf60, 0x438c, 0x4005, 0x4240, 0xbfd6, 0x4120, 0x42ab, 0xb0fa, 0x4674, 0x40ef, 0xbfce, 0x42fb, 0xb03e, 0x426b, 0x4116, 0x426b, 0xb29c, 0x419f, 0x4106, 0x437d, 0x43f6, 0x4625, 0x0a71, 0x424b, 0xb287, 0xa587, 0x40c2, 0xa389, 0x4043, 0xb23a, 0x0f83, 0x0e74, 0xbfa3, 0x42c8, 0x1837, 0xb04a, 0xb215, 0x1f63, 0x409b, 0x466f, 0x4020, 0x40cb, 0x1fc4, 0x424a, 0x4041, 0xab3f, 0x2513, 0xba39, 0x2761, 0x08ef, 0xb01c, 0x40ec, 0x4344, 0x432a, 0x423a, 0xbac6, 0x008b, 0x43cc, 0x1f37, 0x0f45, 0xbf11, 0x4088, 0x4359, 0x42cc, 0x1c92, 0xbacb, 0xb06c, 0xaf25, 0xb299, 0xbfde, 0x4184, 0x3ea1, 0xaab7, 0xbfc0, 0x2538, 0xbfd4, 0x4100, 0x2625, 0x419a, 0xb210, 0x231f, 0x40b5, 0xbfd9, 0x1dd1, 0x1c34, 0x41a3, 0x1c6a, 0xa756, 0xbf70, 0x430f, 0x42c1, 0x4077, 0x404f, 0x41fc, 0x4245, 0xbff0, 0x42cb, 0x3c1f, 0x1c6b, 0xab3a, 0x4083, 0x4131, 0x4234, 0x41a8, 0x41fe, 0x435e, 0x4086, 0xbf98, 0xb2c8, 0xbae1, 0x3ccd, 0x441d, 0xb29c, 0x4047, 0xbf92, 0x42ea, 0x4077, 0x1f00, 0x2d3c, 0x434f, 0x1d83, 0x2a97, 0x4238, 0x43d0, 0xb274, 0x4000, 0x438d, 0x407a, 0x199e, 0x1ec9, 0x4041, 0x4133, 0x0b38, 0x437e, 0xbadb, 0x0c6c, 0x43c9, 0x436d, 0xb2b7, 0x1b76, 0x1207, 0x403a, 0xbfa2, 0x0e8e, 0x1b82, 0x2840, 0x4018, 0xbfa5, 0x2ffa, 0x4493, 0x1cf6, 0xbfe0, 0x243e, 0x4113, 0xb28a, 0x4057, 0xb2d6, 0x43a2, 0xb2ac, 0x40d0, 0x4198, 0xae4e, 0x42f7, 0x3681, 0x4231, 0x4455, 0x35d7, 0x1e5f, 0x411e, 0xb2dc, 0x422d, 0x1edd, 0x4164, 0x2df7, 0xa616, 0x1734, 0xbf53, 0x404c, 0x4061, 0x44c8, 0x43d0, 0xbfdb, 0x415f, 0x1c73, 0xb2de, 0x40bf, 0x462e, 0x4218, 0x1dcc, 0xba45, 0xaef1, 0x425a, 0xa89f, 0xb25a, 0xa25f, 0x1fa5, 0x417a, 0xba13, 0x418e, 0x42a4, 0x35d6, 0x1b4b, 0xba13, 0xba40, 0x1b23, 0x0b14, 0xb20c, 0x2bba, 0xba45, 0x3b65, 0x256f, 0xbfd7, 0xba0d, 0xb2d5, 0x4267, 0x24e6, 0x2d6d, 0x11bc, 0x42b6, 0xbf15, 0xb217, 0xb2d8, 0x405f, 0x4143, 0xa6e6, 0xa92f, 0x42fb, 0x42bd, 0x438b, 0x458a, 0x43c3, 0x420c, 0x43bb, 0x4068, 0x43d4, 0x3fa0, 0x1968, 0x03e1, 0xb291, 0xbf90, 0x2968, 0x4029, 0x38bc, 0x431d, 0x43af, 0x1b23, 0x41a3, 0xbfd9, 0x43f3, 0x229b, 0x0925, 0xb295, 0x15ec, 0xb223, 0x19d3, 0xba1c, 0xb080, 0xb27a, 0xbf7e, 0xba7d, 0xb236, 0x4365, 0x4273, 0x1c90, 0x21b5, 0x42cf, 0x40d6, 0x18c8, 0x0777, 0x281d, 0xbfc0, 0x40ca, 0x1b92, 0x412d, 0xa3aa, 0x44c3, 0x4220, 0x4087, 0x4110, 0x43b0, 0x1aad, 0x3f60, 0x1515, 0xb2fd, 0x40ea, 0xbf0c, 0x19d3, 0x41ba, 0x4003, 0x4040, 0xb239, 0x4057, 0xbfd0, 0x439d, 0x41c6, 0x4382, 0x4122, 0x30b4, 0xbff0, 0x4285, 0x4112, 0x0eec, 0x41f2, 0x4165, 0xba79, 0xbfc0, 0xbac0, 0x43bc, 0x1fa0, 0xbf3b, 0x17fb, 0xba32, 0xba37, 0xb218, 0x4397, 0x40c6, 0xb2e3, 0xbfe4, 0xb23e, 0xb07d, 0x42cb, 0x424e, 0x4269, 0x41e7, 0x4323, 0x40f6, 0x3575, 0x4110, 0x41bc, 0xb2e8, 0x4228, 0xb242, 0x403a, 0x4262, 0xba27, 0x153a, 0xba00, 0xba70, 0x2dde, 0xb290, 0xbf91, 0x40bd, 0x427b, 0x0a83, 0x2605, 0x40ca, 0xa13e, 0x408a, 0x2078, 0xa34c, 0xbfc0, 0x4671, 0x1ae9, 0xbaed, 0x2113, 0xb27b, 0x4138, 0xb268, 0x0f57, 0xbfa5, 0xaeb6, 0xbac7, 0x4304, 0xb03a, 0x1524, 0x4439, 0xb2cf, 0xbfb0, 0xb252, 0xb083, 0x1b1f, 0x4297, 0xa384, 0x4077, 0x1b4d, 0x4386, 0x41da, 0x29a1, 0xbfc0, 0x1f1a, 0x3f17, 0x4007, 0x42f5, 0x4192, 0xbfe8, 0x2373, 0x40b8, 0x4160, 0x4621, 0xb02b, 0xae8c, 0x13f9, 0xba3d, 0x25d3, 0x3d46, 0x41df, 0x4481, 0x1289, 0xa8ac, 0x4257, 0x1157, 0x423d, 0xbff0, 0xb2f2, 0xab13, 0xbfde, 0x412c, 0xb09e, 0x40ba, 0xb0a4, 0x4356, 0x43fe, 0x4209, 0xbf3a, 0x40f3, 0x43df, 0x39a1, 0xb0c3, 0x0992, 0x46ca, 0x4269, 0x417e, 0xb200, 0x46bb, 0x41d6, 0xba1c, 0xbfe0, 0x42a5, 0x3e74, 0x4651, 0x4371, 0x435c, 0x437a, 0x23a1, 0xb217, 0x1bd7, 0x1920, 0x449b, 0xb24c, 0xbf26, 0x4551, 0x41d7, 0x43d9, 0xae4b, 0x4008, 0x403d, 0xba74, 0x43e3, 0x185b, 0xba5c, 0xbf16, 0xba06, 0xbff0, 0x4118, 0x2b3b, 0x435a, 0x409b, 0x4085, 0x434b, 0xa0a7, 0xbf02, 0x1b80, 0xb2a7, 0x1fb5, 0x2ab4, 0x4102, 0x40ec, 0x2d72, 0xba16, 0x4076, 0x23fd, 0xab93, 0xba7e, 0xbf5d, 0x41a0, 0x1d5c, 0x43cc, 0x3cb3, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x96070e06, 0x60f8a631, 0x6d122e21, 0xad567bfc, 0x291715df, 0x4658e055, 0x3efa71b8, 0x42812292, 0x84889393, 0xde7eb11f, 0x47c9ca7e, 0x0ba82b44, 0x8ffeeee8, 0x71d424e5, 0x00000000, 0x100001f0 }, FinalRegs = new uint[] { 0xb75339fa, 0xfffcaf4b, 0x00000000, 0x8488a09f, 0x000350b4, 0x48ace04c, 0x00002fab, 0x0000ab2f, 0x84889836, 0x00000741, 0x00000741, 0x000000a0, 0x00001640, 0x84889e53, 0x00000000, 0x200001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0x4361, 0xb2d9, 0x43f4, 0x2def, 0x1e99, 0x1afe, 0xbfc7, 0x43d0, 0x404f, 0xafd3, 0x4601, 0x4586, 0x4143, 0x42bd, 0x4118, 0x4128, 0x431b, 0xa978, 0x2d56, 0x4607, 0x4124, 0x401b, 0x40a2, 0x430f, 0xba2a, 0xae2f, 0xb035, 0x3079, 0xa7cc, 0x4374, 0x2f83, 0xb20d, 0xbf6c, 0xacbf, 0x40e3, 0xb23c, 0x02c5, 0x433b, 0x40fe, 0x284b, 0x4208, 0x4616, 0x444a, 0xbfc8, 0x3a02, 0x4427, 0x3cd7, 0xa66c, 0x3bf0, 0x4176, 0xb277, 0x0c72, 0x033a, 0x4374, 0x3b46, 0x4039, 0x4646, 0xbaef, 0x4694, 0x43d8, 0xbff0, 0x18d4, 0x191f, 0x1ba0, 0x34cd, 0x09c2, 0x41f8, 0x08e6, 0x22f3, 0xbf76, 0xb0c1, 0x4267, 0xba61, 0xb2cc, 0x412e, 0x41fc, 0x2b38, 0xba61, 0x4130, 0x3f3b, 0x19f1, 0xb2e9, 0x43d3, 0x429d, 0x3b51, 0x42a9, 0x3dcd, 0x06c6, 0x42e0, 0x404e, 0xbf2b, 0xa69c, 0xb26a, 0x4013, 0x2ddf, 0xba2c, 0xafc1, 0xb0c8, 0xb0e1, 0x0dbf, 0x42e7, 0x4102, 0xbf90, 0x4034, 0xb013, 0x1c3c, 0x41fb, 0x1961, 0xb26d, 0x45d2, 0xb07e, 0xbf87, 0x4085, 0x1cf2, 0xbfc0, 0x42ff, 0xa589, 0x43c3, 0xb24f, 0x418e, 0x4261, 0xab43, 0x1b92, 0xbf05, 0x4388, 0x43bb, 0x4038, 0x4393, 0x4187, 0xa081, 0xb0d2, 0x42e2, 0xbf00, 0x2535, 0x43fe, 0x42c1, 0xb24a, 0x408a, 0x1f7d, 0xbf92, 0x1bcb, 0x1b0c, 0x3141, 0xb262, 0xbf86, 0x08aa, 0xb2f5, 0x4262, 0xbfdf, 0x42ea, 0x460c, 0x424f, 0x1ada, 0x42f2, 0xa012, 0x081b, 0x4046, 0x42b5, 0xb008, 0xb231, 0xb0e8, 0x42c3, 0x4187, 0x464b, 0x4016, 0x1abe, 0x098f, 0x1e7f, 0xba68, 0x2a45, 0x1b1a, 0xbf96, 0x402f, 0x1350, 0x3bc2, 0x411b, 0x04a8, 0xb076, 0xb03b, 0x4550, 0xba39, 0xb22a, 0x42f4, 0x4334, 0x4249, 0xb268, 0x12a7, 0x414b, 0x45b1, 0x2e3a, 0xb021, 0x10fa, 0x4249, 0x40b0, 0xbf71, 0x422f, 0x1c42, 0xba7b, 0x41f7, 0xbacd, 0x3d7d, 0xba1d, 0x44d8, 0x45e3, 0x4021, 0x1933, 0xbac1, 0x386d, 0x2777, 0x2151, 0x425a, 0xb251, 0x4376, 0xb008, 0xb2fd, 0xbf42, 0x4191, 0x1724, 0x41da, 0x418a, 0x1a0b, 0x3c2d, 0x345d, 0x0f74, 0xa44e, 0x461c, 0x18d4, 0xa030, 0xb2e0, 0xba75, 0x3710, 0xbf81, 0xba2d, 0x2b8b, 0x1c31, 0x44e1, 0xbfbc, 0x40f3, 0x398e, 0xb260, 0x4567, 0x46e8, 0xb240, 0x1c35, 0xba1b, 0x46c1, 0x4109, 0x43b7, 0xb094, 0x1676, 0x4143, 0x43f1, 0xbf87, 0x136c, 0x4058, 0x1e68, 0x4234, 0x059a, 0x430d, 0x1631, 0x41fe, 0x43b6, 0xb2ed, 0xbff0, 0x4024, 0x406d, 0x4367, 0x418a, 0x4307, 0xbfaf, 0x4374, 0xbae2, 0x4097, 0x3185, 0xb0ec, 0xb051, 0x4052, 0xba60, 0x433d, 0xb210, 0x4347, 0x402e, 0x43d1, 0x412f, 0xb0f2, 0x2310, 0xbfac, 0x4191, 0xb2fd, 0x1db4, 0x4559, 0xb08f, 0xb2aa, 0x4182, 0x42ae, 0x4036, 0xaf83, 0xb234, 0x418d, 0xbf8c, 0x407b, 0xba34, 0x41cf, 0x4351, 0x41c5, 0xba77, 0x1cfd, 0xa0cd, 0xbac7, 0xbf43, 0xba21, 0x425d, 0xba4c, 0xba13, 0x413c, 0x4095, 0xa127, 0x4198, 0xb2f3, 0x1f4e, 0x40e9, 0x19cc, 0xba4c, 0xb2e0, 0x4241, 0xbf90, 0xb214, 0x4224, 0x40f4, 0x3c7c, 0x07b9, 0x43e1, 0x41c7, 0x4339, 0x4181, 0xba11, 0x0bcc, 0xbf29, 0x4051, 0x1dbf, 0xb25a, 0x07c0, 0xba76, 0x413e, 0x417f, 0x1d87, 0xa388, 0x1d2e, 0x40e6, 0x4165, 0x432e, 0x050e, 0xb0fd, 0x43f4, 0x1895, 0xbf32, 0xb090, 0x40ba, 0x1e9a, 0x1f13, 0x45d2, 0x4044, 0x1f04, 0xb2ea, 0x43fa, 0x4197, 0x43d6, 0x1de6, 0x4671, 0x402d, 0x4058, 0x1aca, 0x43c5, 0xadd4, 0x41e8, 0xba3b, 0xb273, 0xbf9c, 0x197a, 0xbaf6, 0xbfb0, 0xba0e, 0x45d1, 0x1f11, 0x4379, 0xaf6e, 0xbff0, 0xb019, 0xa676, 0xbf4e, 0x4264, 0x4351, 0xb051, 0xbae2, 0x415e, 0x4355, 0x4016, 0x437f, 0xbfca, 0x408e, 0x42ef, 0xb240, 0x40cc, 0x311b, 0x435a, 0xab55, 0x43de, 0xaeae, 0x20fa, 0xbacb, 0x4301, 0x43bd, 0x42b6, 0xbfb0, 0x403c, 0xaf7d, 0x1941, 0xbf05, 0xba02, 0x0c49, 0x42cc, 0x1e9d, 0x1efc, 0x0a69, 0x42a6, 0x42e6, 0x1ae2, 0xba55, 0xbfd0, 0x407b, 0xbaf9, 0xa02b, 0xba5d, 0xbf16, 0xb239, 0xbfa0, 0xba47, 0x418f, 0x41e9, 0xafc4, 0x23c8, 0xb04e, 0x42fb, 0xaf00, 0x402a, 0x4270, 0x4380, 0x4214, 0x4466, 0x2297, 0x441a, 0xbf6d, 0x4298, 0x1836, 0x1918, 0x3cff, 0x42ec, 0x4363, 0xbf60, 0x4311, 0xbfa3, 0x4004, 0x0c27, 0x41fe, 0x4346, 0xb0f2, 0x1544, 0x43b7, 0x412d, 0xab14, 0x1f41, 0xb2ab, 0x190f, 0x218f, 0xb2d9, 0x0e76, 0x40b8, 0xb2d0, 0x41da, 0xbf09, 0x1a5e, 0x4340, 0x4004, 0x0884, 0x437a, 0xbf90, 0x428a, 0x19e6, 0x4283, 0x439b, 0xbfb8, 0x43d1, 0xba5f, 0x4366, 0x06b4, 0x4390, 0xb20c, 0x2116, 0x10b5, 0x4266, 0x43d3, 0x17f9, 0x42d4, 0xbac7, 0x437c, 0x434e, 0x410d, 0xaef0, 0xad99, 0x231d, 0x41a0, 0xb2ac, 0x0716, 0x3728, 0x1cbc, 0xbac7, 0x42d9, 0xbf05, 0xb2fb, 0x438a, 0x08e7, 0x42f7, 0xa411, 0x35d7, 0x4631, 0x46f2, 0xa304, 0x4650, 0x4055, 0x416c, 0x4336, 0x0e29, 0x41cb, 0x40e5, 0xb2e9, 0x0e4d, 0x4172, 0x1d2c, 0x3f5c, 0x301e, 0x4245, 0xba5e, 0x0d11, 0xbf99, 0x4155, 0x02c2, 0xbfb0, 0x40ec, 0xb0a7, 0xb20a, 0xba21, 0xba5e, 0x41b8, 0x42cf, 0x41ab, 0xbfe2, 0xbae1, 0x3b4b, 0xb01c, 0x46cd, 0x43db, 0xb2c6, 0x46f0, 0x4141, 0x1c08, 0x41e4, 0xba11, 0x4127, 0xb220, 0xba75, 0x1846, 0xbf43, 0x1be2, 0x4073, 0x418f, 0x37e6, 0x2c57, 0xaf36, 0xb23b, 0x4095, 0x412e, 0x0e71, 0x42ba, 0x19ec, 0xb0ab, 0x19d9, 0xa24a, 0x362e, 0x408b, 0x1094, 0xb21f, 0x40c9, 0x42be, 0xb2c7, 0x409e, 0xbf15, 0xb0cf, 0xb00b, 0x415c, 0x4009, 0x4186, 0xb0c7, 0x4257, 0x1992, 0x40c9, 0xbf00, 0xb2d0, 0x4168, 0xbf9d, 0x43c4, 0xbaf8, 0xba68, 0xba26, 0x45bc, 0x4214, 0x1231, 0xbfd4, 0x4009, 0xba00, 0x3e72, 0xbf1e, 0xb29b, 0xbacb, 0xbaf9, 0x4352, 0x4105, 0x42e7, 0xaa0f, 0xb2b3, 0x43a6, 0x4699, 0x2170, 0x0c8e, 0xae4a, 0xbfe0, 0xb004, 0xb237, 0x1a88, 0xbf41, 0xb2ad, 0xbf00, 0x13a8, 0x42a5, 0x00cb, 0xa3f6, 0x1bd6, 0x44d1, 0xba06, 0x4278, 0xba65, 0xbfc0, 0x098f, 0x029d, 0x2a7b, 0xb2df, 0x07a7, 0x0105, 0xad3c, 0x43ac, 0x40bf, 0x428f, 0x339a, 0x40fb, 0x43b9, 0x446d, 0x42ac, 0x1083, 0xad05, 0xbf8c, 0x4264, 0x42e4, 0xbaf2, 0xbad8, 0xb21f, 0x4244, 0x25d1, 0xb282, 0x405c, 0x1a04, 0x4399, 0x414d, 0xb2fa, 0xba6b, 0xb216, 0xb250, 0x438b, 0x2b3c, 0x40b0, 0x425b, 0x412e, 0xb220, 0xb25f, 0xb2c2, 0x2ec8, 0xbf3a, 0xba70, 0x43bc, 0xb299, 0x4284, 0x369c, 0xbaff, 0x058f, 0xba2c, 0x1699, 0x42e3, 0x41ce, 0xabfc, 0xb2cc, 0x2a1e, 0xb201, 0x19d3, 0xb099, 0xbf34, 0x332f, 0x0535, 0x4422, 0x42b9, 0xbfdf, 0xb270, 0x400a, 0x40d7, 0x4024, 0x1f9e, 0x196d, 0x434c, 0x1dd6, 0x41df, 0xb219, 0x405d, 0x4022, 0x1e04, 0x41dc, 0x4296, 0x43c4, 0xba25, 0x145b, 0x42a6, 0x4154, 0xbf0a, 0x4061, 0x28b4, 0x1c9f, 0x4310, 0x274b, 0x4191, 0x4034, 0xbf7f, 0x439a, 0x42d8, 0xb0eb, 0xba0f, 0xba5a, 0x41a7, 0x43c1, 0x0538, 0x41ad, 0x42a1, 0x43ce, 0xbf04, 0xa79a, 0x1b98, 0xb018, 0xb0e0, 0x42f5, 0x4476, 0xaf71, 0x433b, 0xb212, 0xbf73, 0x0a7e, 0x2b1f, 0xbafb, 0xba4e, 0x0425, 0x420d, 0x43e2, 0x43e0, 0x4065, 0x4117, 0xba07, 0x2562, 0xa532, 0x43b3, 0xba29, 0x2779, 0x4353, 0x440b, 0x4140, 0xbf58, 0x27dc, 0x4286, 0xa57a, 0x42b2, 0x425d, 0xb2a9, 0xba52, 0x43de, 0x41ee, 0x0796, 0x1fa7, 0x2c5f, 0x2a61, 0x1c25, 0x425a, 0x40b7, 0x42f8, 0x4180, 0x40b4, 0xbfc5, 0x428e, 0x4205, 0xba23, 0x4003, 0x1f55, 0x3e3c, 0x416c, 0x42e0, 0xb234, 0x1d80, 0x432c, 0x417f, 0x4079, 0x3538, 0x1eff, 0x4475, 0xbf34, 0xb277, 0x43b6, 0x4355, 0x40d3, 0xb023, 0x17b5, 0x1d0a, 0x2b3a, 0x4455, 0x0615, 0xbf0b, 0x4045, 0x40ad, 0x2759, 0xb068, 0x40aa, 0x1f85, 0x436b, 0x4268, 0xb282, 0x40d6, 0xb0ed, 0xb01d, 0x422d, 0x4104, 0x4114, 0x1d54, 0xbf70, 0x1d50, 0xbf98, 0x432f, 0x403a, 0x417f, 0x4622, 0xb08c, 0x1bf4, 0x3219, 0x4079, 0x23f2, 0xba10, 0x29d4, 0x4224, 0x431b, 0x41ea, 0x4440, 0x41f5, 0x40c6, 0x1e5e, 0x01c4, 0x34a9, 0x1bde, 0x43b6, 0xb2e9, 0xbf24, 0x3fa4, 0xb2c1, 0x41d5, 0x4002, 0x4215, 0x185d, 0x1a17, 0xb27f, 0xb23a, 0x1397, 0x1e78, 0xb03b, 0x1cad, 0xba6a, 0x2a81, 0x0ee6, 0x4351, 0xbfbe, 0xb2fa, 0x225e, 0x429d, 0x410b, 0x4080, 0xba53, 0x4196, 0x40a2, 0x437b, 0x0a59, 0xb2aa, 0xbae9, 0x0534, 0x40a9, 0x465a, 0x448c, 0xb26a, 0xb213, 0x41c5, 0x424c, 0x18e1, 0x30fe, 0x4046, 0x40d8, 0xbf39, 0x1f57, 0x1865, 0x4059, 0x4376, 0x4564, 0x4438, 0x41e0, 0x12d8, 0xb06b, 0x4329, 0x42b6, 0x40f5, 0x41e4, 0xbac3, 0xb265, 0x4570, 0x42f8, 0x2fb0, 0x432d, 0x438e, 0x4222, 0x438f, 0xbf02, 0x43d6, 0x4179, 0x278d, 0x25df, 0xbafc, 0x251e, 0x1cfc, 0xba69, 0xbf31, 0x0ac3, 0xb0ad, 0x4205, 0x441e, 0x410c, 0xb273, 0x42be, 0x1e0d, 0xbf07, 0x468c, 0x4016, 0xa105, 0x437e, 0x416e, 0xbaf0, 0x418d, 0x3851, 0x2464, 0x43b8, 0x43e5, 0xbac0, 0x4146, 0x3c61, 0x2797, 0x4094, 0x41fb, 0x181a, 0x4437, 0xb273, 0x4365, 0x4321, 0x1fe4, 0x4199, 0x269e, 0x1c0a, 0xbf77, 0x40b0, 0x3eaf, 0x1f30, 0xa473, 0x26a3, 0x42f4, 0xb0a2, 0x4280, 0xb2d0, 0x2fb3, 0xb017, 0xb07d, 0x43f7, 0x439e, 0x43f1, 0xbfe0, 0x459e, 0x3cb3, 0xbfd6, 0x1244, 0x2ba2, 0x4391, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0xfaefb3b8, 0x78bd4750, 0x110d5992, 0x6de9a720, 0x42910210, 0xa47797d3, 0xb17bbffb, 0x69ee994d, 0xbfeace8e, 0x33a9e570, 0x6e9aadf6, 0xc053407f, 0x87f3e955, 0x34a56210, 0x00000000, 0x700001f0 }, FinalRegs = new uint[] { 0x000000a1, 0xffffe85e, 0x000017a1, 0x00000002, 0x000018e1, 0x00000000, 0x000000a1, 0xffffff5c, 0x00000000, 0x0000ffbc, 0x00000000, 0xc053407f, 0x00021000, 0x34a56118, 0x00000000, 0x800001d0 }, }, - new PrecomputedThumbTestCase() + new() { Instructions = new ushort[] { 0xba27, 0x42db, 0x40d3, 0x4362, 0x409f, 0x1812, 0xbac7, 0xa7df, 0x34cd, 0xa70f, 0x4280, 0x40b0, 0xb0ee, 0x01be, 0xb211, 0x4052, 0xb2a7, 0x4063, 0x406f, 0x4654, 0x434e, 0x42fb, 0xbf25, 0x4269, 0x1119, 0xb264, 0x4301, 0xba63, 0x1a18, 0xb2fb, 0x1887, 0x2f9b, 0xb233, 0x440e, 0x4206, 0xbad1, 0xb09d, 0x262b, 0xb2d3, 0xba06, 0xab5c, 0x2ee6, 0x4419, 0x339c, 0xb2d1, 0x4192, 0xbf72, 0xbae1, 0x410e, 0x41fe, 0x4193, 0xbf00, 0xba7d, 0xb2b6, 0x4318, 0x1eee, 0xb2f0, 0x413c, 0xba4b, 0x41e0, 0xb02d, 0x4287, 0x4467, 0xba35, 0x46d4, 0xa117, 0xbf5c, 0x40b4, 0xa074, 0x2102, 0x4230, 0x0167, 0x44e9, 0x0471, 0x4106, 0x42b1, 0x00af, 0x1a8c, 0x1ae2, 0xabbf, 0x42c5, 0x1b0b, 0x123f, 0x406d, 0xbf91, 0xb09b, 0xb276, 0xb07c, 0x4383, 0x18ab, 0x4156, 0xb2aa, 0xbf7b, 0x191b, 0xb228, 0xb263, 0x0dda, 0x4062, 0x46fa, 0x3720, 0x1f98, 0xb045, 0xb235, 0x1fa8, 0x4276, 0xb27a, 0xa3a8, 0x434b, 0x2044, 0x1f00, 0x169e, 0xbae6, 0xadf8, 0x4272, 0x4004, 0x42de, 0x2bee, 0xba2c, 0xbfb6, 0xb043, 0x415d, 0xb27f, 0x4213, 0x1ebb, 0x4077, 0xb284, 0xa9ab, 0x40a5, 0xb2a9, 0xb254, 0x4593, 0xa036, 0xb058, 0xb2ab, 0x3458, 0x1fa6, 0x3798, 0x4180, 0x41c0, 0x46cd, 0x1155, 0x42cd, 0xbf58, 0xba33, 0xa87e, 0xb012, 0x43eb, 0x2dc8, 0x42f8, 0x1e62, 0xb24f, 0x413d, 0x412d, 0x4335, 0x1915, 0x1ecf, 0x1358, 0x4419, 0x40ec, 0x438f, 0x2cb3, 0x360d, 0x2b61, 0x1cea, 0x0ed4, 0xb2e1, 0xb206, 0xb255, 0xbfa1, 0x1bc7, 0xb011, 0x4306, 0x3b69, 0xa9a9, 0xb20b, 0xad51, 0x420f, 0x1493, 0x4140, 0x405f, 0xb03f, 0x4107, 0x422c, 0x186c, 0xb285, 0x4350, 0x4115, 0x196d, 0xba29, 0xbaeb, 0x4340, 0x404c, 0x4195, 0xba1d, 0x4156, 0x4257, 0xbf45, 0x19bf, 0x4356, 0x3366, 0x40fa, 0x4022, 0x40e4, 0xa663, 0x284c, 0xbf4a, 0x415c, 0x0de3, 0x411d, 0x4268, 0x36e8, 0xbafe, 0xb0b5, 0x40b6, 0x406c, 0x4131, 0xb01b, 0x4281, 0x174c, 0xb06b, 0x421d, 0x412e, 0x4159, 0x4346, 0x01c4, 0x2f52, 0xbfd7, 0xb0f4, 0xb0b7, 0x4053, 0x1ce2, 0xbfdd, 0x40ae, 0xb286, 0x41d1, 0x4156, 0x1b8d, 0x41e1, 0x4281, 0x1844, 0xb297, 0x1c04, 0x405e, 0x1841, 0x412a, 0x414d, 0x45c8, 0xba0a, 0xb25d, 0xadc5, 0xa7eb, 0xbfad, 0x45e0, 0x41ce, 0x4230, 0x4232, 0x41e4, 0x42f1, 0x4566, 0x46d3, 0x1f16, 0x419c, 0x2db3, 0x4312, 0x30ff, 0x28e3, 0xbfd2, 0x4277, 0xa610, 0x41b8, 0xbad9, 0x42db, 0x1ff4, 0x088c, 0x41e5, 0x4104, 0xbf60, 0x0a11, 0x380e, 0xb24f, 0xb287, 0x42a9, 0xb006, 0xba45, 0x408d, 0x414f, 0x1d4e, 0xbf03, 0x3191, 0x41f3, 0xb260, 0x1f59, 0x4347, 0x42f8, 0x412d, 0x43f0, 0x1d4e, 0x42ba, 0xab0a, 0x1f8d, 0x432c, 0xaf72, 0x1bbd, 0x4130, 0x43ab, 0x41ba, 0x0066, 0xbf01, 0x1477, 0x410f, 0x40f3, 0x415c, 0x197b, 0x2fe1, 0x4542, 0xbf31, 0x4020, 0x420c, 0xa16c, 0xb231, 0xba34, 0x420e, 0x46f5, 0x0851, 0x4374, 0xbf22, 0xba20, 0x1df1, 0xb2fb, 0xb090, 0x431f, 0x44cc, 0x411a, 0xb2dd, 0x46b3, 0xba7b, 0x42b1, 0xb267, 0x41e5, 0x41c9, 0x414b, 0x40df, 0xa37b, 0xb2ef, 0x411d, 0xb29b, 0x1380, 0xbf2a, 0x41f5, 0xb280, 0xb2f2, 0x0ed2, 0x4329, 0xb248, 0x0969, 0x4631, 0x40f3, 0x43d0, 0xb093, 0xbf5f, 0xb204, 0x4463, 0x1d6d, 0x4382, 0x41a4, 0x40dc, 0x43f2, 0x460d, 0x42a1, 0x0a18, 0xb097, 0x23aa, 0x1acb, 0x1ff4, 0xb270, 0x0311, 0x0baa, 0x338c, 0xbf01, 0xb22a, 0x422c, 0xb2ae, 0x4369, 0x43d4, 0xba01, 0x41c8, 0xbafb, 0xb034, 0x0c66, 0xb221, 0x432c, 0x1b48, 0x413f, 0x4014, 0xa721, 0xbae1, 0xbae8, 0x192f, 0xba20, 0x416b, 0xb2cd, 0x408f, 0xbf61, 0x42a8, 0x3999, 0xbaff, 0x43a6, 0x416a, 0x4148, 0xb00e, 0x10c7, 0x1a90, 0xba63, 0xbf81, 0xb03e, 0xbaf7, 0x4046, 0x40a1, 0x439d, 0x4141, 0x2c5c, 0x29a6, 0x2b42, 0x0a7a, 0x3b4a, 0x42d6, 0x249b, 0x3240, 0x2ca8, 0xb2e1, 0xbfb8, 0x4059, 0x19ad, 0xb27c, 0xba73, 0x4270, 0x171d, 0x4183, 0xbac4, 0x43bf, 0x4418, 0x4169, 0x422e, 0x41e2, 0x43c2, 0xbf6d, 0x40de, 0xb283, 0x432a, 0x437e, 0xb2b8, 0x4277, 0x4338, 0x40d1, 0x42f7, 0xb2b2, 0xbaef, 0x16a7, 0x4124, 0x4341, 0xbf80, 0xb262, 0x021b, 0xb042, 0x18df, 0x1038, 0xb292, 0x1d14, 0xb0ee, 0x429e, 0x4019, 0xb293, 0x4216, 0xbf62, 0x07a6, 0xb281, 0x1f1b, 0x4660, 0x4568, 0x279d, 0x43e9, 0x45e1, 0xb27b, 0x40fe, 0xb242, 0x42d7, 0x2b9f, 0xb28d, 0xbfb1, 0xb2d4, 0x40ed, 0x43f7, 0xb05a, 0x34cd, 0xb2cc, 0x426d, 0x1c09, 0x414c, 0x4146, 0xb0ac, 0x434e, 0xb2f2, 0xba3c, 0xa981, 0x224b, 0xba0d, 0xb239, 0xbf8a, 0x3ae8, 0x40aa, 0x3459, 0xbf90, 0x4491, 0xb2e0, 0xba62, 0x425b, 0x41b8, 0x427e, 0xb24e, 0x4117, 0xbfc8, 0xa2f2, 0x1c4d, 0xaae2, 0x2776, 0xba4b, 0xbf03, 0x1f0e, 0x41ff, 0xa3a7, 0x21d9, 0x1520, 0xb2d1, 0x4344, 0x4669, 0x42b5, 0xba0a, 0x3274, 0xb23b, 0x1a2d, 0xbf60, 0x4424, 0x43e3, 0x4236, 0xbfa1, 0x4335, 0x41d8, 0xb2fa, 0xba3b, 0x3433, 0xb099, 0xbf26, 0x415a, 0x149b, 0x1e17, 0x2be1, 0x41c8, 0x26ac, 0x18c2, 0xbfe4, 0x27e9, 0x2413, 0x41c9, 0x29b1, 0xa042, 0xba56, 0xba44, 0x1c5f, 0xba32, 0xba60, 0xbac1, 0xa246, 0x1a6e, 0x41dc, 0xb2f1, 0x18f1, 0x1b38, 0x331a, 0x2a6f, 0x40e4, 0x0350, 0x1ff6, 0x43fb, 0xbf77, 0x2b4e, 0x1f7b, 0xa24e, 0xb05b, 0xbf51, 0x1b2f, 0xbaf4, 0x0756, 0x4238, 0x1c0d, 0x2351, 0xb03c, 0x2234, 0xb2c8, 0xb073, 0x1a1e, 0x4171, 0x3256, 0x0169, 0xa84a, 0x43f6, 0x2cb9, 0x429b, 0xba22, 0xbf9a, 0x43ba, 0x46cd, 0x30ba, 0x02fa, 0x42c5, 0x0ca2, 0x326a, 0x428b, 0x43be, 0x114e, 0xbf2c, 0x0816, 0x0903, 0x1a3d, 0xb291, 0x4025, 0xab9a, 0xa63e, 0x426f, 0x42f1, 0x1d96, 0x1e6c, 0x4117, 0x403d, 0x1ecb, 0x438c, 0xbfc4, 0x4110, 0xba01, 0x458c, 0x442e, 0xb07a, 0xb2fb, 0x43dc, 0xb2d4, 0x1bec, 0x407a, 0x19ff, 0x0fd7, 0x4321, 0x1e0c, 0x2ff7, 0xbf91, 0x4266, 0x1dd1, 0x42de, 0xbf90, 0xb208, 0x40a4, 0x1ff9, 0x41c7, 0xb276, 0x408b, 0xbfaf, 0xb2f4, 0x4056, 0xa933, 0x42c6, 0x4198, 0x42ee, 0xbad7, 0xb288, 0xb28f, 0x42b7, 0x43f8, 0xb27b, 0xaa4e, 0x4066, 0x30f5, 0x4242, 0x41e1, 0xba7e, 0xa066, 0x4038, 0x1c69, 0xba0c, 0x4372, 0xb08a, 0xbaef, 0x4314, 0x41c4, 0xbf9f, 0x4355, 0xb2be, 0xb096, 0x402d, 0xb206, 0xb2d6, 0x3674, 0x159d, 0x4093, 0x41dd, 0x427c, 0x40a7, 0xb0ca, 0xb214, 0xb25c, 0xbf1e, 0x4043, 0xa1bb, 0x41c7, 0x4655, 0x1805, 0x4313, 0xba4b, 0xb03c, 0xb2d9, 0xa67b, 0x291b, 0x0eb7, 0x425b, 0xb007, 0x4669, 0xb2aa, 0x41e8, 0x41b0, 0x4015, 0xa496, 0x3460, 0x43cd, 0x44f4, 0xbf01, 0x41cd, 0x4393, 0xad38, 0xba1a, 0xbf4b, 0x2960, 0x43a0, 0xba2d, 0x4559, 0xb0c7, 0x173a, 0x263a, 0xb23e, 0x43c1, 0x43aa, 0xa3b5, 0xba78, 0x0bf1, 0x4126, 0x067a, 0x42fc, 0x446f, 0x0b51, 0x2b2a, 0x439e, 0xb23c, 0xbf23, 0xb078, 0xbae6, 0x0398, 0xa8d3, 0x42ec, 0x431c, 0x41ed, 0x40d1, 0xba3d, 0x2317, 0xbac5, 0x42e1, 0x4268, 0x361b, 0x4235, 0x4151, 0x4596, 0x1ac6, 0x4215, 0xbfbc, 0xb0c0, 0x4296, 0x2525, 0x4287, 0xbf69, 0x24a2, 0x425c, 0xb258, 0xb01f, 0xbf3d, 0xb246, 0xb2cb, 0xbae2, 0xb284, 0xbac2, 0x4339, 0x4580, 0x3c52, 0x40f7, 0x04a0, 0xa9ab, 0x416b, 0xadba, 0x429d, 0x4270, 0x3def, 0x1a98, 0xbf65, 0x4286, 0x4056, 0x3ef0, 0xb287, 0x0bfb, 0x45e1, 0xb270, 0x0e86, 0x4006, 0x4593, 0xaf1d, 0x403a, 0x4113, 0x401e, 0x4209, 0x4018, 0x4130, 0x41ac, 0x425d, 0x42ba, 0xa052, 0x43de, 0x073f, 0x4018, 0xba5b, 0xb2e8, 0x1b83, 0x424a, 0x4000, 0xbf61, 0x1f92, 0x4086, 0x404a, 0x2004, 0x45e1, 0x4221, 0x4257, 0xbf9a, 0x1c3a, 0x20b9, 0x41b2, 0x429b, 0x4097, 0x40cb, 0xbfdb, 0x41de, 0x4107, 0xa45c, 0x426d, 0xafd3, 0x412f, 0x199b, 0x42ac, 0x4389, 0xbf41, 0xb20a, 0xa51d, 0x439f, 0x41ff, 0x4215, 0x3e5e, 0xba14, 0x18f3, 0x428a, 0x43fd, 0x438e, 0x4272, 0x1b74, 0x42ad, 0x417d, 0x1ff0, 0x1b9c, 0x41b7, 0x41b6, 0x405f, 0x17e5, 0xb2a3, 0x07dc, 0xbf22, 0xb08c, 0x409e, 0xb23e, 0xb2b4, 0x29f8, 0x44e8, 0x41ad, 0xba65, 0x4200, 0x41b5, 0xbf8c, 0x1ee4, 0x41ff, 0x40f0, 0xb2b3, 0x45dd, 0xba63, 0x1e16, 0xbfe2, 0x1ee9, 0xa4e7, 0xb07c, 0xae9f, 0xba20, 0x403a, 0x4260, 0x439a, 0x444f, 0x40bb, 0x430f, 0xbf57, 0x18d5, 0x43ba, 0x0350, 0x2a39, 0xaade, 0x0a7a, 0xb239, 0xb2c2, 0xb0a4, 0x0999, 0x4270, 0x4390, 0xbfb6, 0x4048, 0xbfa0, 0xb20d, 0x4026, 0xba4e, 0x430e, 0x4375, 0x4171, 0xb278, 0x4256, 0x40bd, 0x42e4, 0x1c1e, 0xb2de, 0xbf58, 0x059b, 0xb243, 0x197d, 0x4084, 0xa945, 0xb03f, 0x4430, 0x1c82, 0xa4a0, 0x43c7, 0x41e8, 0x41a5, 0xba57, 0x0ecc, 0x2e03, 0xba50, 0x42f7, 0xb017, 0x42b2, 0xbac1, 0xbfbc, 0x408a, 0x0c66, 0xa768, 0xb223, 0x427c, 0x448d, 0x445f, 0x437d, 0x405e, 0x42a3, 0x257d, 0x4485, 0x44ec, 0xba2b, 0x4177, 0x42ea, 0x42f5, 0x43cc, 0x4681, 0x4211, 0x416c, 0xb038, 0xba19, 0x4669, 0xbfbb, 0x08f8, 0x4338, 0xb2cf, 0x4653, 0xa218, 0xb2f6, 0x1894, 0xbaed, 0xb2c1, 0x19ae, 0x1138, 0x1b80, 0x4158, 0x41ea, 0xb083, 0xbfe2, 0xacb0, 0x1d95, 0xa07c, 0x4770, 0xe7fe }, StartRegs = new uint[] { 0x53e412bb, 0xf8075b4a, 0xc06d1392, 0xad332bbd, 0x27e72f7d, 0xac8f8bd5, 0x3a5b2232, 0xc5e2c8ec, 0xe032a7a7, 0x2b035634, 0x4a720b19, 0x0f87e928, 0x73db65d8, 0x6c7bc94f, 0x00000000, 0xf00001f0 }, @@ -881,4 +881,4 @@ namespace Ryujinx.Tests.Cpu }, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs b/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs index d374c08a5..1ef12de5f 100644 --- a/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs +++ b/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs @@ -10,22 +10,24 @@ namespace Ryujinx.Tests.Cpu { internal class EnvironmentTests { +#pragma warning disable IDE0052 // Remove unread private member private static Translator _translator; +#pragma warning restore IDE0052 - private void EnsureTranslator() + private static void EnsureTranslator() { // Create a translator, as one is needed to register the signal handler or emit methods. _translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] - private float GetDenormal() + private static float GetDenormal() { return BitConverter.Int32BitsToSingle(1); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] - private float GetZero() + private static float GetZero() { return BitConverter.Int32BitsToSingle(0); } @@ -53,7 +55,7 @@ namespace Ryujinx.Tests.Cpu if (test < 4f) { - throw new System.Exception("Sanity check."); + throw new Exception("Sanity check."); } isFz = GetDenormal() + GetZero() == 0f; @@ -62,7 +64,7 @@ namespace Ryujinx.Tests.Cpu { if (test >= 4f) { - throw new System.Exception("Always throws."); + throw new Exception("Always throws."); } } catch diff --git a/src/Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs b/src/Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs index cec5a6682..40ed26757 100644 --- a/src/Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs +++ b/src/Ryujinx.Tests/Cpu/PrecomputedMemoryThumbTestCase.cs @@ -6,5 +6,5 @@ namespace Ryujinx.Tests.Cpu public uint[] StartRegs; public uint[] FinalRegs; public (ulong Address, ushort Value)[] MemoryDelta; - }; -} \ No newline at end of file + } +} diff --git a/src/Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs b/src/Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs index 7e9112303..e699f8bbf 100644 --- a/src/Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs +++ b/src/Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs @@ -6,4 +6,4 @@ public uint[] StartRegs; public uint[] FinalRegs; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs b/src/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs index d16039ad3..b5bcc213f 100644 --- a/src/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs +++ b/src/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs @@ -21,8 +21,7 @@ namespace Ryujinx.Tests.HLE [Test] public void StripUnicodeControlCodes_Passthrough() { - string[] prompts = new string[] - { + string[] prompts = { "Please name him.", "Name her, too.", "Name your friend.", diff --git a/src/Ryujinx.Tests/Memory/MockMemoryManager.cs b/src/Ryujinx.Tests/Memory/MockMemoryManager.cs index eeecf419f..8a902db41 100644 --- a/src/Ryujinx.Tests/Memory/MockMemoryManager.cs +++ b/src/Ryujinx.Tests/Memory/MockMemoryManager.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Memory public MemoryManagerType Type => MemoryManagerType.HostMappedUnsafe; -#pragma warning disable CS0067 +#pragma warning disable CS0067 // The event is never used public event Action<ulong, ulong> UnmapEvent; #pragma warning restore CS0067 diff --git a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs index c60afc2de..296f74917 100644 --- a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs +++ b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Tests.Memory { private static Translator _translator; - private (MemoryBlock virt, MemoryBlock mirror, MemoryEhMeilleure exceptionHandler) GetVirtual(ulong asSize) + private static (MemoryBlock virt, MemoryBlock mirror, MemoryEhMeilleure exceptionHandler) GetVirtual(ulong asSize) { MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; @@ -33,7 +33,7 @@ namespace Ryujinx.Tests.Memory return (addressSpace, addressSpaceMirror, exceptionHandler); } - private int CountThreads(ref PartialUnmapState state) + private static int CountThreads(ref PartialUnmapState state) { int count = 0; @@ -50,7 +50,7 @@ namespace Ryujinx.Tests.Memory return count; } - private void EnsureTranslator() + private static void EnsureTranslator() { // Create a translator, as one is needed to register the signal handler or emit methods. _translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true); @@ -239,7 +239,7 @@ namespace Ryujinx.Tests.Memory var writeFunc = TestMethods.GenerateDebugNativeWriteLoop(); IntPtr writePtr = mainMemory.GetPointer(vaSize - 0x1000, 4); - Thread testThread = new Thread(() => + Thread testThread = new(() => { writeFunc(statePtr, writePtr); }); @@ -283,7 +283,7 @@ namespace Ryujinx.Tests.Memory [Test] // Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming. [Platform("Win")] - [SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")] + [SuppressMessage("Interoperability", "CA1416: Validate platform compatibility")] public void ThreadLocalMap() { PartialUnmapState.Reset(); @@ -465,4 +465,4 @@ namespace Ryujinx.Tests.Memory Assert.False(error); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Tests/TreeDictionaryTests.cs b/src/Ryujinx.Tests/TreeDictionaryTests.cs index 610c2f6e0..f1a8ee73d 100644 --- a/src/Ryujinx.Tests/TreeDictionaryTests.cs +++ b/src/Ryujinx.Tests/TreeDictionaryTests.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Tests.Collections [Test] public void EnsureAddIntegrity() { - TreeDictionary<int, int> dictionary = new TreeDictionary<int, int>(); + TreeDictionary<int, int> dictionary = new(); Assert.AreEqual(dictionary.Count, 0); @@ -35,7 +35,7 @@ namespace Ryujinx.Tests.Collections * 5 11 * */ - + Assert.AreEqual(list.Count, dictionary.Count); Assert.AreEqual(list[0].Key, 2); Assert.AreEqual(list[1].Key, 1); @@ -49,7 +49,7 @@ namespace Ryujinx.Tests.Collections [Test] public void EnsureRemoveIntegrity() { - TreeDictionary<int, int> dictionary = new TreeDictionary<int, int>(); + TreeDictionary<int, int> dictionary = new(); Assert.AreEqual(dictionary.Count, 0); @@ -165,7 +165,7 @@ namespace Ryujinx.Tests.Collections [Test] public void EnsureOverwriteIntegrity() { - TreeDictionary<int, int> dictionary = new TreeDictionary<int, int>(); + TreeDictionary<int, int> dictionary = new(); Assert.AreEqual(dictionary.Count, 0); From 79a1314ee45ea346efc0078d9338ae88e12d07a5 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 1 Jul 2023 04:18:52 +0200 Subject: [PATCH 688/737] [Ryujinx.Cpu] Address dotnet-format issues (#5365) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Silence dotnet format IDE0059 warnings * Address or silence dotnet format IDE1006 warnings * Address dotnet format CA1816 warnings * Address most dotnet format whitespace warnings * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Remove a few unused parameters * Adjust namespaces * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Fix and silence a few dotnet-format warnings again * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Address review feedback * Remove redundant unsafe modifiers * Fix build issues * Add GC.SuppressFinalize() call * Add trailing commas and fix naming rule violations * Remove unused members and assignments --- src/Ryujinx.Cpu/AddressSpace.cs | 18 +- src/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs | 4 +- .../AppleHv/DummyDiskCacheLoadState.cs | 4 +- src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs | 22 +- .../AppleHv/HvAddressSpaceRange.cs | 24 +- src/Ryujinx.Cpu/AppleHv/HvApi.cs | 465 +++++++++--------- src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs | 7 +- src/Ryujinx.Cpu/AppleHv/HvEngine.cs | 2 +- src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs | 20 +- .../AppleHv/HvExecutionContextShadow.cs | 4 +- .../AppleHv/HvExecutionContextVcpu.cs | 56 +-- src/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs | 2 +- .../AppleHv/HvMemoryBlockAllocation.cs | 2 +- .../AppleHv/HvMemoryBlockAllocator.cs | 7 +- src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs | 16 +- src/Ryujinx.Cpu/AppleHv/HvVcpu.cs | 6 +- src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs | 14 +- src/Ryujinx.Cpu/AppleHv/HvVm.cs | 6 +- .../AppleHv/IHvExecutionContext.cs | 2 +- src/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs | 4 +- src/Ryujinx.Cpu/Jit/JitEngine.cs | 2 +- src/Ryujinx.Cpu/Jit/JitExecutionContext.cs | 2 +- src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs | 8 +- src/Ryujinx.Cpu/Jit/MemoryManager.cs | 17 +- .../Jit/MemoryManagerHostMapped.cs | 8 +- src/Ryujinx.Cpu/LoadState.cs | 4 +- src/Ryujinx.Cpu/MemoryEhMeilleure.cs | 7 +- src/Ryujinx.Cpu/MemoryHelper.cs | 28 +- src/Ryujinx.Cpu/PrivateMemoryAllocation.cs | 6 +- src/Ryujinx.Cpu/PrivateMemoryAllocator.cs | 8 +- src/Ryujinx.Cpu/TickSource.cs | 2 +- 31 files changed, 383 insertions(+), 394 deletions(-) diff --git a/src/Ryujinx.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs index e051244d5..c467eb622 100644 --- a/src/Ryujinx.Cpu/AddressSpace.cs +++ b/src/Ryujinx.Cpu/AddressSpace.cs @@ -7,15 +7,13 @@ namespace Ryujinx.Cpu { public class AddressSpace : IDisposable { - private const ulong PageSize = 0x1000; - private const int DefaultBlockAlignment = 1 << 20; private enum MappingType : byte { None, Private, - Shared + Shared, } private class Mapping : IntrusiveRedBlackTreeNode<Mapping>, IComparable<Mapping> @@ -37,7 +35,7 @@ namespace Ryujinx.Cpu ulong leftSize = splitAddress - Address; ulong rightSize = EndAddress - splitAddress; - Mapping left = new Mapping(Address, leftSize, Type); + Mapping left = new(Address, leftSize, Type); Address = splitAddress; Size = rightSize; @@ -93,7 +91,7 @@ namespace Ryujinx.Cpu (var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize); - PrivateMapping left = new PrivateMapping(Address, leftSize, leftAllocation); + PrivateMapping left = new(Address, leftSize, leftAllocation); Address = splitAddress; Size = rightSize; @@ -181,7 +179,7 @@ namespace Ryujinx.Cpu { addressSpace = null; - MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; + const MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; ulong minAddressSpaceSize = Math.Min(asSize, 1UL << 36); @@ -391,8 +389,6 @@ namespace Ryujinx.Cpu ulong vaAligned = BitUtils.AlignDown(va, alignment); ulong endAddressAligned = BitUtils.AlignUp(endAddress, alignment); - ulong sizeAligned = endAddressAligned - vaAligned; - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default)); for (; map != null; map = map.Successor) @@ -436,8 +432,6 @@ namespace Ryujinx.Cpu return; } - ulong alignedSize = endAddressAligned - vaAligned; - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default)); for (; map != null; map = map.Successor) @@ -495,9 +489,11 @@ namespace Ryujinx.Cpu public void Dispose() { + GC.SuppressFinalize(this); + _privateMemoryAllocator?.Dispose(); Base.Dispose(); Mirror.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs b/src/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs index 18152f254..f7e277586 100644 --- a/src/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs +++ b/src/Ryujinx.Cpu/AppleHv/Arm/ExceptionClass.cs @@ -42,6 +42,6 @@ namespace Ryujinx.Cpu.AppleHv.Arm WatchpointSameEl = 0b110101, BkptAarch32 = 0b111000, VectorCatchAarch32 = 0b111010, - BrkAarch64 = 0b111100 + BrkAarch64 = 0b111100, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs b/src/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs index 6a692e740..8e775f094 100644 --- a/src/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs +++ b/src/Ryujinx.Cpu/AppleHv/DummyDiskCacheLoadState.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Cpu.AppleHv { public class DummyDiskCacheLoadState : IDiskCacheLoadState { -#pragma warning disable CS0067 +#pragma warning disable CS0067 // The event is never used /// <inheritdoc/> public event Action<LoadState, int, int> StateChanged; #pragma warning restore CS0067 @@ -14,4 +14,4 @@ namespace Ryujinx.Cpu.AppleHv { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs b/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs index 78f4c4640..4785a3f05 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs @@ -18,18 +18,16 @@ namespace Ryujinx.Cpu.AppleHv private const ulong AllocationGranule = 1UL << 14; private readonly ulong _asBase; - private readonly ulong _asSize; private readonly ulong _backingSize; private readonly HvAddressSpaceRange _userRange; private readonly HvAddressSpaceRange _kernelRange; - private MemoryBlock _kernelCodeBlock; + private readonly MemoryBlock _kernelCodeBlock; public HvAddressSpace(MemoryBlock backingMemory, ulong asSize) { (_asBase, var ipaAllocator) = HvVm.CreateAddressSpace(backingMemory); - _asSize = asSize; _backingSize = backingMemory.Size; _userRange = new HvAddressSpaceRange(ipaAllocator); @@ -58,20 +56,20 @@ namespace Ryujinx.Cpu.AppleHv _kernelCodeBlock.Write(KernelRegionEretOffset, 0xD69F03E0u); // ERET ulong kernelCodePa = ipaAllocator.Allocate(AllocationGranule); - HvApi.hv_vm_map((ulong)_kernelCodeBlock.Pointer, kernelCodePa, AllocationGranule, hv_memory_flags_t.HV_MEMORY_READ | hv_memory_flags_t.HV_MEMORY_EXEC).ThrowOnError(); + HvApi.hv_vm_map((ulong)_kernelCodeBlock.Pointer, kernelCodePa, AllocationGranule, HvMemoryFlags.Read | HvMemoryFlags.Exec).ThrowOnError(); _kernelRange.Map(KernelRegionCodeOffset, kernelCodePa, KernelRegionCodeSize, ApFlags.UserNoneKernelReadExecute); } public void InitializeMmu(ulong vcpu) { - HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_VBAR_EL1, KernelRegionBase + KernelRegionCodeOffset); + HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.VBAR_EL1, KernelRegionBase + KernelRegionCodeOffset); - HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TTBR0_EL1, _userRange.GetIpaBase()); - HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TTBR1_EL1, _kernelRange.GetIpaBase()); - HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_MAIR_EL1, 0xffUL); - HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TCR_EL1, 0x00000011B5193519UL); - HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_SCTLR_EL1, 0x0000000034D5D925UL); + HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.TTBR0_EL1, _userRange.GetIpaBase()); + HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.TTBR1_EL1, _kernelRange.GetIpaBase()); + HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.MAIR_EL1, 0xffUL); + HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.TCR_EL1, 0x00000011B5193519UL); + HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.SCTLR_EL1, 0x0000000034D5D925UL); } public bool GetAndClearUserTlbInvalidationPending() @@ -115,7 +113,7 @@ namespace Ryujinx.Cpu.AppleHv MemoryPermission.ReadAndWrite => ApFlags.UserReadWriteKernelReadWrite, MemoryPermission.ReadAndExecute => ApFlags.UserReadExecuteKernelRead, MemoryPermission.ReadWriteExecute => ApFlags.UserReadWriteExecuteKernelReadWrite, - _ => throw new ArgumentException($"Permission \"{permission}\" is invalid.") + _ => throw new ArgumentException($"Permission \"{permission}\" is invalid."), }; } @@ -126,4 +124,4 @@ namespace Ryujinx.Cpu.AppleHv HvVm.DestroyAddressSpace(_asBase, _backingSize); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs b/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs index ca30bb681..876334307 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Cpu.AppleHv ulong size = (ulong)count * sizeof(ulong); Allocation = blockAllocator.Allocate(size, PageSize); - AsSpan().Fill(0UL); + AsSpan().Clear(); if (hasNext) { @@ -42,7 +42,7 @@ namespace Ryujinx.Cpu.AppleHv } } - public unsafe Span<ulong> AsSpan() + public Span<ulong> AsSpan() { return MemoryMarshal.Cast<byte, ulong>(Allocation.Memory.GetSpan(Allocation.Offset, (int)Allocation.Size)); } @@ -52,12 +52,10 @@ namespace Ryujinx.Cpu.AppleHv private int _tlbInvalidationPending; - private readonly HvIpaAllocator _ipaAllocator; private readonly HvMemoryBlockAllocator _blockAllocator; public HvAddressSpaceRange(HvIpaAllocator ipaAllocator) { - _ipaAllocator = ipaAllocator; _blockAllocator = new HvMemoryBlockAllocator(ipaAllocator, (int)AllocationGranule); } @@ -129,7 +127,7 @@ namespace Ryujinx.Cpu.AppleHv ulong endVa = (va + size + PageMask) & ~((ulong)PageMask); va &= ~((ulong)PageMask); - (ulong blockSize, int blockShift) = GetBlockSizeAndShift(depth); + (ulong blockSize, _) = GetBlockSizeAndShift(depth); while (va < endVa) { @@ -138,7 +136,7 @@ namespace Ryujinx.Cpu.AppleHv int l = (int)(va >> (PageBits + (2 - depth) * LevelBits)) & LevelMask; - PtLevel nextTable = level.Next != null ? level.Next[l] : null; + PtLevel nextTable = level.Next?[l]; if (nextTable != null) { @@ -190,7 +188,7 @@ namespace Ryujinx.Cpu.AppleHv ulong endVa = (va + size + PageSize - 1) & ~((ulong)PageSize - 1); va &= ~((ulong)PageSize - 1); - (ulong blockSize, int blockShift) = GetBlockSizeAndShift(depth); + (ulong blockSize, _) = GetBlockSizeAndShift(depth); while (va < endVa) { @@ -204,7 +202,7 @@ namespace Ryujinx.Cpu.AppleHv // First check if the region is mapped. if ((pte & 3) != 0) { - PtLevel nextTable = level.Next != null ? level.Next[l] : null; + PtLevel nextTable = level.Next?[l]; if (nextTable != null) { @@ -240,10 +238,10 @@ namespace Ryujinx.Cpu.AppleHv pte &= ~3UL; pte |= (depth == 2 ? 3UL : 1UL); - PtLevel level = new PtLevel(_blockAllocator, LevelCount, depth < 2); + PtLevel level = new(_blockAllocator, LevelCount, depth < 2); Span<ulong> currentLevel = level.AsSpan(); - (ulong blockSize, int blockShift) = GetBlockSizeAndShift(depth); + (_, int blockShift) = GetBlockSizeAndShift(depth); // Fill in the blocks. for (int i = 0; i < LevelCount; i++) @@ -334,7 +332,7 @@ namespace Ryujinx.Cpu.AppleHv if ((currentTable[index] & 1) == 0) { - PtLevel nextLevel = new PtLevel(_blockAllocator, LevelCount, hasNext); + PtLevel nextLevel = new(_blockAllocator, LevelCount, hasNext); currentTable[index] = (nextLevel.Address & ~(ulong)PageMask) | 3UL; level.Next[index] = nextLevel; @@ -347,7 +345,7 @@ namespace Ryujinx.Cpu.AppleHv } } - private void WriteBlock(PtLevel level, int index, int depth, ulong pa, ulong attr) + private static void WriteBlock(PtLevel level, int index, int depth, ulong pa, ulong attr) { Span<ulong> currentTable = level.AsSpan(); @@ -367,4 +365,4 @@ namespace Ryujinx.Cpu.AppleHv _blockAllocator.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvApi.cs b/src/Ryujinx.Cpu/AppleHv/HvApi.cs index d7628bb52..4f7484981 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvApi.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvApi.cs @@ -1,245 +1,244 @@ -using ARMeilleure.State; using System; using System.Runtime.InteropServices; namespace Ryujinx.Cpu.AppleHv { - struct hv_vcpu_exit_exception_t + struct HvVcpuExitException { -#pragma warning disable CS0649 - public ulong syndrome; - public ulong virtual_address; - public ulong physical_address; +#pragma warning disable CS0649 // Field is never assigned to + public ulong Syndrome; + public ulong VirtualAddress; + public ulong PhysicalAddress; #pragma warning restore CS0649 } - struct hv_vcpu_exit_t + struct HvVcpuExit { -#pragma warning disable CS0649 - public uint reason; - public hv_vcpu_exit_exception_t exception; +#pragma warning disable CS0649 // Field is never assigned to + public uint Reason; + public HvVcpuExitException Exception; #pragma warning restore CS0649 } - enum hv_reg_t : uint + enum HvReg : uint { - HV_REG_X0, - HV_REG_X1, - HV_REG_X2, - HV_REG_X3, - HV_REG_X4, - HV_REG_X5, - HV_REG_X6, - HV_REG_X7, - HV_REG_X8, - HV_REG_X9, - HV_REG_X10, - HV_REG_X11, - HV_REG_X12, - HV_REG_X13, - HV_REG_X14, - HV_REG_X15, - HV_REG_X16, - HV_REG_X17, - HV_REG_X18, - HV_REG_X19, - HV_REG_X20, - HV_REG_X21, - HV_REG_X22, - HV_REG_X23, - HV_REG_X24, - HV_REG_X25, - HV_REG_X26, - HV_REG_X27, - HV_REG_X28, - HV_REG_X29, - HV_REG_FP = HV_REG_X29, - HV_REG_X30, - HV_REG_LR = HV_REG_X30, - HV_REG_PC, - HV_REG_FPCR, - HV_REG_FPSR, - HV_REG_CPSR, + X0, + X1, + X2, + X3, + X4, + X5, + X6, + X7, + X8, + X9, + X10, + X11, + X12, + X13, + X14, + X15, + X16, + X17, + X18, + X19, + X20, + X21, + X22, + X23, + X24, + X25, + X26, + X27, + X28, + X29, + FP = X29, + X30, + LR = X30, + PC, + FPCR, + FPSR, + CPSR, } - enum hv_simd_fp_reg_t : uint + enum HvSimdFPReg : uint { - HV_SIMD_FP_REG_Q0, - HV_SIMD_FP_REG_Q1, - HV_SIMD_FP_REG_Q2, - HV_SIMD_FP_REG_Q3, - HV_SIMD_FP_REG_Q4, - HV_SIMD_FP_REG_Q5, - HV_SIMD_FP_REG_Q6, - HV_SIMD_FP_REG_Q7, - HV_SIMD_FP_REG_Q8, - HV_SIMD_FP_REG_Q9, - HV_SIMD_FP_REG_Q10, - HV_SIMD_FP_REG_Q11, - HV_SIMD_FP_REG_Q12, - HV_SIMD_FP_REG_Q13, - HV_SIMD_FP_REG_Q14, - HV_SIMD_FP_REG_Q15, - HV_SIMD_FP_REG_Q16, - HV_SIMD_FP_REG_Q17, - HV_SIMD_FP_REG_Q18, - HV_SIMD_FP_REG_Q19, - HV_SIMD_FP_REG_Q20, - HV_SIMD_FP_REG_Q21, - HV_SIMD_FP_REG_Q22, - HV_SIMD_FP_REG_Q23, - HV_SIMD_FP_REG_Q24, - HV_SIMD_FP_REG_Q25, - HV_SIMD_FP_REG_Q26, - HV_SIMD_FP_REG_Q27, - HV_SIMD_FP_REG_Q28, - HV_SIMD_FP_REG_Q29, - HV_SIMD_FP_REG_Q30, - HV_SIMD_FP_REG_Q31, + Q0, + Q1, + Q2, + Q3, + Q4, + Q5, + Q6, + Q7, + Q8, + Q9, + Q10, + Q11, + Q12, + Q13, + Q14, + Q15, + Q16, + Q17, + Q18, + Q19, + Q20, + Q21, + Q22, + Q23, + Q24, + Q25, + Q26, + Q27, + Q28, + Q29, + Q30, + Q31, } - enum hv_sys_reg_t : ushort + enum HvSysReg : ushort { - HV_SYS_REG_DBGBVR0_EL1 = 0x8004, - HV_SYS_REG_DBGBCR0_EL1 = 0x8005, - HV_SYS_REG_DBGWVR0_EL1 = 0x8006, - HV_SYS_REG_DBGWCR0_EL1 = 0x8007, - HV_SYS_REG_DBGBVR1_EL1 = 0x800c, - HV_SYS_REG_DBGBCR1_EL1 = 0x800d, - HV_SYS_REG_DBGWVR1_EL1 = 0x800e, - HV_SYS_REG_DBGWCR1_EL1 = 0x800f, - HV_SYS_REG_MDCCINT_EL1 = 0x8010, - HV_SYS_REG_MDSCR_EL1 = 0x8012, - HV_SYS_REG_DBGBVR2_EL1 = 0x8014, - HV_SYS_REG_DBGBCR2_EL1 = 0x8015, - HV_SYS_REG_DBGWVR2_EL1 = 0x8016, - HV_SYS_REG_DBGWCR2_EL1 = 0x8017, - HV_SYS_REG_DBGBVR3_EL1 = 0x801c, - HV_SYS_REG_DBGBCR3_EL1 = 0x801d, - HV_SYS_REG_DBGWVR3_EL1 = 0x801e, - HV_SYS_REG_DBGWCR3_EL1 = 0x801f, - HV_SYS_REG_DBGBVR4_EL1 = 0x8024, - HV_SYS_REG_DBGBCR4_EL1 = 0x8025, - HV_SYS_REG_DBGWVR4_EL1 = 0x8026, - HV_SYS_REG_DBGWCR4_EL1 = 0x8027, - HV_SYS_REG_DBGBVR5_EL1 = 0x802c, - HV_SYS_REG_DBGBCR5_EL1 = 0x802d, - HV_SYS_REG_DBGWVR5_EL1 = 0x802e, - HV_SYS_REG_DBGWCR5_EL1 = 0x802f, - HV_SYS_REG_DBGBVR6_EL1 = 0x8034, - HV_SYS_REG_DBGBCR6_EL1 = 0x8035, - HV_SYS_REG_DBGWVR6_EL1 = 0x8036, - HV_SYS_REG_DBGWCR6_EL1 = 0x8037, - HV_SYS_REG_DBGBVR7_EL1 = 0x803c, - HV_SYS_REG_DBGBCR7_EL1 = 0x803d, - HV_SYS_REG_DBGWVR7_EL1 = 0x803e, - HV_SYS_REG_DBGWCR7_EL1 = 0x803f, - HV_SYS_REG_DBGBVR8_EL1 = 0x8044, - HV_SYS_REG_DBGBCR8_EL1 = 0x8045, - HV_SYS_REG_DBGWVR8_EL1 = 0x8046, - HV_SYS_REG_DBGWCR8_EL1 = 0x8047, - HV_SYS_REG_DBGBVR9_EL1 = 0x804c, - HV_SYS_REG_DBGBCR9_EL1 = 0x804d, - HV_SYS_REG_DBGWVR9_EL1 = 0x804e, - HV_SYS_REG_DBGWCR9_EL1 = 0x804f, - HV_SYS_REG_DBGBVR10_EL1 = 0x8054, - HV_SYS_REG_DBGBCR10_EL1 = 0x8055, - HV_SYS_REG_DBGWVR10_EL1 = 0x8056, - HV_SYS_REG_DBGWCR10_EL1 = 0x8057, - HV_SYS_REG_DBGBVR11_EL1 = 0x805c, - HV_SYS_REG_DBGBCR11_EL1 = 0x805d, - HV_SYS_REG_DBGWVR11_EL1 = 0x805e, - HV_SYS_REG_DBGWCR11_EL1 = 0x805f, - HV_SYS_REG_DBGBVR12_EL1 = 0x8064, - HV_SYS_REG_DBGBCR12_EL1 = 0x8065, - HV_SYS_REG_DBGWVR12_EL1 = 0x8066, - HV_SYS_REG_DBGWCR12_EL1 = 0x8067, - HV_SYS_REG_DBGBVR13_EL1 = 0x806c, - HV_SYS_REG_DBGBCR13_EL1 = 0x806d, - HV_SYS_REG_DBGWVR13_EL1 = 0x806e, - HV_SYS_REG_DBGWCR13_EL1 = 0x806f, - HV_SYS_REG_DBGBVR14_EL1 = 0x8074, - HV_SYS_REG_DBGBCR14_EL1 = 0x8075, - HV_SYS_REG_DBGWVR14_EL1 = 0x8076, - HV_SYS_REG_DBGWCR14_EL1 = 0x8077, - HV_SYS_REG_DBGBVR15_EL1 = 0x807c, - HV_SYS_REG_DBGBCR15_EL1 = 0x807d, - HV_SYS_REG_DBGWVR15_EL1 = 0x807e, - HV_SYS_REG_DBGWCR15_EL1 = 0x807f, - HV_SYS_REG_MIDR_EL1 = 0xc000, - HV_SYS_REG_MPIDR_EL1 = 0xc005, - HV_SYS_REG_ID_AA64PFR0_EL1 = 0xc020, - HV_SYS_REG_ID_AA64PFR1_EL1 = 0xc021, - HV_SYS_REG_ID_AA64DFR0_EL1 = 0xc028, - HV_SYS_REG_ID_AA64DFR1_EL1 = 0xc029, - HV_SYS_REG_ID_AA64ISAR0_EL1 = 0xc030, - HV_SYS_REG_ID_AA64ISAR1_EL1 = 0xc031, - HV_SYS_REG_ID_AA64MMFR0_EL1 = 0xc038, - HV_SYS_REG_ID_AA64MMFR1_EL1 = 0xc039, - HV_SYS_REG_ID_AA64MMFR2_EL1 = 0xc03a, - HV_SYS_REG_SCTLR_EL1 = 0xc080, - HV_SYS_REG_CPACR_EL1 = 0xc082, - HV_SYS_REG_TTBR0_EL1 = 0xc100, - HV_SYS_REG_TTBR1_EL1 = 0xc101, - HV_SYS_REG_TCR_EL1 = 0xc102, - HV_SYS_REG_APIAKEYLO_EL1 = 0xc108, - HV_SYS_REG_APIAKEYHI_EL1 = 0xc109, - HV_SYS_REG_APIBKEYLO_EL1 = 0xc10a, - HV_SYS_REG_APIBKEYHI_EL1 = 0xc10b, - HV_SYS_REG_APDAKEYLO_EL1 = 0xc110, - HV_SYS_REG_APDAKEYHI_EL1 = 0xc111, - HV_SYS_REG_APDBKEYLO_EL1 = 0xc112, - HV_SYS_REG_APDBKEYHI_EL1 = 0xc113, - HV_SYS_REG_APGAKEYLO_EL1 = 0xc118, - HV_SYS_REG_APGAKEYHI_EL1 = 0xc119, - HV_SYS_REG_SPSR_EL1 = 0xc200, - HV_SYS_REG_ELR_EL1 = 0xc201, - HV_SYS_REG_SP_EL0 = 0xc208, - HV_SYS_REG_AFSR0_EL1 = 0xc288, - HV_SYS_REG_AFSR1_EL1 = 0xc289, - HV_SYS_REG_ESR_EL1 = 0xc290, - HV_SYS_REG_FAR_EL1 = 0xc300, - HV_SYS_REG_PAR_EL1 = 0xc3a0, - HV_SYS_REG_MAIR_EL1 = 0xc510, - HV_SYS_REG_AMAIR_EL1 = 0xc518, - HV_SYS_REG_VBAR_EL1 = 0xc600, - HV_SYS_REG_CONTEXTIDR_EL1 = 0xc681, - HV_SYS_REG_TPIDR_EL1 = 0xc684, - HV_SYS_REG_CNTKCTL_EL1 = 0xc708, - HV_SYS_REG_CSSELR_EL1 = 0xd000, - HV_SYS_REG_TPIDR_EL0 = 0xde82, - HV_SYS_REG_TPIDRRO_EL0 = 0xde83, - HV_SYS_REG_CNTV_CTL_EL0 = 0xdf19, - HV_SYS_REG_CNTV_CVAL_EL0 = 0xdf1a, - HV_SYS_REG_SP_EL1 = 0xe208, + DBGBVR0_EL1 = 0x8004, + DBGBCR0_EL1 = 0x8005, + DBGWVR0_EL1 = 0x8006, + DBGWCR0_EL1 = 0x8007, + DBGBVR1_EL1 = 0x800c, + DBGBCR1_EL1 = 0x800d, + DBGWVR1_EL1 = 0x800e, + DBGWCR1_EL1 = 0x800f, + MDCCINT_EL1 = 0x8010, + MDSCR_EL1 = 0x8012, + DBGBVR2_EL1 = 0x8014, + DBGBCR2_EL1 = 0x8015, + DBGWVR2_EL1 = 0x8016, + DBGWCR2_EL1 = 0x8017, + DBGBVR3_EL1 = 0x801c, + DBGBCR3_EL1 = 0x801d, + DBGWVR3_EL1 = 0x801e, + DBGWCR3_EL1 = 0x801f, + DBGBVR4_EL1 = 0x8024, + DBGBCR4_EL1 = 0x8025, + DBGWVR4_EL1 = 0x8026, + DBGWCR4_EL1 = 0x8027, + DBGBVR5_EL1 = 0x802c, + DBGBCR5_EL1 = 0x802d, + DBGWVR5_EL1 = 0x802e, + DBGWCR5_EL1 = 0x802f, + DBGBVR6_EL1 = 0x8034, + DBGBCR6_EL1 = 0x8035, + DBGWVR6_EL1 = 0x8036, + DBGWCR6_EL1 = 0x8037, + DBGBVR7_EL1 = 0x803c, + DBGBCR7_EL1 = 0x803d, + DBGWVR7_EL1 = 0x803e, + DBGWCR7_EL1 = 0x803f, + DBGBVR8_EL1 = 0x8044, + DBGBCR8_EL1 = 0x8045, + DBGWVR8_EL1 = 0x8046, + DBGWCR8_EL1 = 0x8047, + DBGBVR9_EL1 = 0x804c, + DBGBCR9_EL1 = 0x804d, + DBGWVR9_EL1 = 0x804e, + DBGWCR9_EL1 = 0x804f, + DBGBVR10_EL1 = 0x8054, + DBGBCR10_EL1 = 0x8055, + DBGWVR10_EL1 = 0x8056, + DBGWCR10_EL1 = 0x8057, + DBGBVR11_EL1 = 0x805c, + DBGBCR11_EL1 = 0x805d, + DBGWVR11_EL1 = 0x805e, + DBGWCR11_EL1 = 0x805f, + DBGBVR12_EL1 = 0x8064, + DBGBCR12_EL1 = 0x8065, + DBGWVR12_EL1 = 0x8066, + DBGWCR12_EL1 = 0x8067, + DBGBVR13_EL1 = 0x806c, + DBGBCR13_EL1 = 0x806d, + DBGWVR13_EL1 = 0x806e, + DBGWCR13_EL1 = 0x806f, + DBGBVR14_EL1 = 0x8074, + DBGBCR14_EL1 = 0x8075, + DBGWVR14_EL1 = 0x8076, + DBGWCR14_EL1 = 0x8077, + DBGBVR15_EL1 = 0x807c, + DBGBCR15_EL1 = 0x807d, + DBGWVR15_EL1 = 0x807e, + DBGWCR15_EL1 = 0x807f, + MIDR_EL1 = 0xc000, + MPIDR_EL1 = 0xc005, + ID_AA64PFR0_EL1 = 0xc020, + ID_AA64PFR1_EL1 = 0xc021, + ID_AA64DFR0_EL1 = 0xc028, + ID_AA64DFR1_EL1 = 0xc029, + ID_AA64ISAR0_EL1 = 0xc030, + ID_AA64ISAR1_EL1 = 0xc031, + ID_AA64MMFR0_EL1 = 0xc038, + ID_AA64MMFR1_EL1 = 0xc039, + ID_AA64MMFR2_EL1 = 0xc03a, + SCTLR_EL1 = 0xc080, + CPACR_EL1 = 0xc082, + TTBR0_EL1 = 0xc100, + TTBR1_EL1 = 0xc101, + TCR_EL1 = 0xc102, + APIAKEYLO_EL1 = 0xc108, + APIAKEYHI_EL1 = 0xc109, + APIBKEYLO_EL1 = 0xc10a, + APIBKEYHI_EL1 = 0xc10b, + APDAKEYLO_EL1 = 0xc110, + APDAKEYHI_EL1 = 0xc111, + APDBKEYLO_EL1 = 0xc112, + APDBKEYHI_EL1 = 0xc113, + APGAKEYLO_EL1 = 0xc118, + APGAKEYHI_EL1 = 0xc119, + SPSR_EL1 = 0xc200, + ELR_EL1 = 0xc201, + SP_EL0 = 0xc208, + AFSR0_EL1 = 0xc288, + AFSR1_EL1 = 0xc289, + ESR_EL1 = 0xc290, + FAR_EL1 = 0xc300, + PAR_EL1 = 0xc3a0, + MAIR_EL1 = 0xc510, + AMAIR_EL1 = 0xc518, + VBAR_EL1 = 0xc600, + CONTEXTIDR_EL1 = 0xc681, + TPIDR_EL1 = 0xc684, + CNTKCTL_EL1 = 0xc708, + CSSELR_EL1 = 0xd000, + TPIDR_EL0 = 0xde82, + TPIDRRO_EL0 = 0xde83, + CNTV_CTL_EL0 = 0xdf19, + CNTV_CVAL_EL0 = 0xdf1a, + SP_EL1 = 0xe208, } - enum hv_memory_flags_t : ulong + enum HvMemoryFlags : ulong { - HV_MEMORY_READ = 1UL << 0, - HV_MEMORY_WRITE = 1UL << 1, - HV_MEMORY_EXEC = 1UL << 2 + Read = 1UL << 0, + Write = 1UL << 1, + Exec = 1UL << 2, } - enum hv_result_t : uint + enum HvResult : uint { - HV_SUCCESS = 0, - HV_ERROR = 0xfae94001, - HV_BUSY = 0xfae94002, - HV_BAD_ARGUMENT = 0xfae94003, - HV_NO_RESOURCES = 0xfae94005, - HV_NO_DEVICE = 0xfae94006, - HV_DENIED = 0xfae94007, - HV_UNSUPPORTED = 0xfae9400f + Success = 0, + Error = 0xfae94001, + Busy = 0xfae94002, + BadArgument = 0xfae94003, + NoResources = 0xfae94005, + NoDevice = 0xfae94006, + Denied = 0xfae94007, + Unsupported = 0xfae9400f, } - enum hv_interrupt_type_t : uint + enum HvInterruptType : uint { - HV_INTERRUPT_TYPE_IRQ, - HV_INTERRUPT_TYPE_FIQ + IRQ, + FIQ, } - struct hv_simd_fp_uchar16_t + struct HvSimdFPUchar16 { public ulong Low; public ulong High; @@ -247,9 +246,9 @@ namespace Ryujinx.Cpu.AppleHv static class HvResultExtensions { - public static void ThrowOnError(this hv_result_t result) + public static void ThrowOnError(this HvResult result) { - if (result != hv_result_t.HV_SUCCESS) + if (result != HvResult.Success) { throw new Exception($"Unexpected result \"{result}\"."); } @@ -261,60 +260,60 @@ namespace Ryujinx.Cpu.AppleHv public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor"; [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vm_get_max_vcpu_count(out uint max_vcpu_count); + public static partial HvResult hv_vm_get_max_vcpu_count(out uint max_vcpu_count); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vm_create(IntPtr config); + public static partial HvResult hv_vm_create(IntPtr config); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vm_destroy(); + public static partial HvResult hv_vm_destroy(); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vm_map(ulong addr, ulong ipa, ulong size, hv_memory_flags_t flags); + public static partial HvResult hv_vm_map(ulong addr, ulong ipa, ulong size, HvMemoryFlags flags); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vm_unmap(ulong ipa, ulong size); + public static partial HvResult hv_vm_unmap(ulong ipa, ulong size); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vm_protect(ulong ipa, ulong size, hv_memory_flags_t flags); + public static partial HvResult hv_vm_protect(ulong ipa, ulong size, HvMemoryFlags flags); [LibraryImport(LibraryName, SetLastError = true)] - public unsafe static partial hv_result_t hv_vcpu_create(out ulong vcpu, ref hv_vcpu_exit_t* exit, IntPtr config); + public unsafe static partial HvResult hv_vcpu_create(out ulong vcpu, ref HvVcpuExit* exit, IntPtr config); [LibraryImport(LibraryName, SetLastError = true)] - public unsafe static partial hv_result_t hv_vcpu_destroy(ulong vcpu); + public unsafe static partial HvResult hv_vcpu_destroy(ulong vcpu); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_run(ulong vcpu); + public static partial HvResult hv_vcpu_run(ulong vcpu); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpus_exit(ref ulong vcpus, uint vcpu_count); + public static partial HvResult hv_vcpus_exit(ref ulong vcpus, uint vcpu_count); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_set_vtimer_mask(ulong vcpu, [MarshalAs(UnmanagedType.Bool)] bool vtimer_is_masked); + public static partial HvResult hv_vcpu_set_vtimer_mask(ulong vcpu, [MarshalAs(UnmanagedType.Bool)] bool vtimer_is_masked); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_get_reg(ulong vcpu, hv_reg_t reg, out ulong value); + public static partial HvResult hv_vcpu_get_reg(ulong vcpu, HvReg reg, out ulong value); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_set_reg(ulong vcpu, hv_reg_t reg, ulong value); + public static partial HvResult hv_vcpu_set_reg(ulong vcpu, HvReg reg, ulong value); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_get_simd_fp_reg(ulong vcpu, hv_simd_fp_reg_t reg, out hv_simd_fp_uchar16_t value); + public static partial HvResult hv_vcpu_get_simd_fp_reg(ulong vcpu, HvSimdFPReg reg, out HvSimdFPUchar16 value); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_set_simd_fp_reg(ulong vcpu, hv_simd_fp_reg_t reg, hv_simd_fp_uchar16_t value); // DO NOT USE DIRECTLY! + public static partial HvResult hv_vcpu_set_simd_fp_reg(ulong vcpu, HvSimdFPReg reg, HvSimdFPUchar16 value); // DO NOT USE DIRECTLY! [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_get_sys_reg(ulong vcpu, hv_sys_reg_t reg, out ulong value); + public static partial HvResult hv_vcpu_get_sys_reg(ulong vcpu, HvSysReg reg, out ulong value); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_set_sys_reg(ulong vcpu, hv_sys_reg_t reg, ulong value); + public static partial HvResult hv_vcpu_set_sys_reg(ulong vcpu, HvSysReg reg, ulong value); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_get_pending_interrupt(ulong vcpu, hv_interrupt_type_t type, [MarshalAs(UnmanagedType.Bool)] out bool pending); + public static partial HvResult hv_vcpu_get_pending_interrupt(ulong vcpu, HvInterruptType type, [MarshalAs(UnmanagedType.Bool)] out bool pending); [LibraryImport(LibraryName, SetLastError = true)] - public static partial hv_result_t hv_vcpu_set_pending_interrupt(ulong vcpu, hv_interrupt_type_t type, [MarshalAs(UnmanagedType.Bool)] bool pending); + public static partial HvResult hv_vcpu_set_pending_interrupt(ulong vcpu, HvInterruptType type, [MarshalAs(UnmanagedType.Bool)] bool pending); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs b/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs index de782d541..a58f83591 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs @@ -1,5 +1,4 @@ using ARMeilleure.Memory; -using System; namespace Ryujinx.Cpu.AppleHv { @@ -14,10 +13,6 @@ namespace Ryujinx.Cpu.AppleHv _memoryManager = (HvMemoryManager)memory; } - private void UnmapHandler(ulong address, ulong size) - { - } - /// <inheritdoc/> public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks) { @@ -44,4 +39,4 @@ namespace Ryujinx.Cpu.AppleHv { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvEngine.cs b/src/Ryujinx.Cpu/AppleHv/HvEngine.cs index 7ad99cb9c..2967857f9 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvEngine.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvEngine.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Cpu.AppleHv return new HvCpuContext(_tickSource, memoryManager, for64Bit); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs index dc1f6f6da..2c9afdc4e 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs @@ -125,17 +125,17 @@ namespace Ryujinx.Cpu.AppleHv { HvVcpu vcpu = HvVcpuPool.Instance.Create(memoryManager.AddressSpace, _shadowContext, SwapContext); - HvApi.hv_vcpu_set_reg(vcpu.Handle, hv_reg_t.HV_REG_PC, address).ThrowOnError(); + HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError(); while (Running) { HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError(); - uint reason = vcpu.ExitInfo->reason; + uint reason = vcpu.ExitInfo->Reason; if (reason == 1) { - uint hvEsr = (uint)vcpu.ExitInfo->exception.syndrome; + uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome; ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26); if (hvEc != ExceptionClass.HvcAarch64) @@ -144,7 +144,7 @@ namespace Ryujinx.Cpu.AppleHv } address = SynchronousException(memoryManager, ref vcpu); - HvApi.hv_vcpu_set_reg(vcpu.Handle, hv_reg_t.HV_REG_PC, address).ThrowOnError(); + HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError(); } else if (reason == 0) { @@ -168,8 +168,8 @@ namespace Ryujinx.Cpu.AppleHv { ulong vcpuHandle = vcpu.Handle; - HvApi.hv_vcpu_get_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, out ulong elr).ThrowOnError(); - HvApi.hv_vcpu_get_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_ESR_EL1, out ulong esr).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError(); ExceptionClass ec = (ExceptionClass)((uint)esr >> 26); @@ -180,7 +180,7 @@ namespace Ryujinx.Cpu.AppleHv break; case ExceptionClass.TrappedMsrMrsSystem: InstructionTrap((uint)esr); - HvApi.hv_vcpu_set_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, elr + 4UL).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, elr + 4UL).ThrowOnError(); break; case ExceptionClass.SvcAarch64: ReturnToPool(vcpu); @@ -204,7 +204,7 @@ namespace Ryujinx.Cpu.AppleHv } } - private void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr) + private static void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr) { bool write = (esr & (1u << 6)) != 0; bool farValid = (esr & (1u << 10)) == 0; @@ -212,7 +212,7 @@ namespace Ryujinx.Cpu.AppleHv if (farValid) { - HvApi.hv_vcpu_get_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_FAR_EL1, out ulong far).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(vcpu, HvSysReg.FAR_EL1, out ulong far).ThrowOnError(); ulong size = 1UL << accessSizeLog2; @@ -281,4 +281,4 @@ namespace Ryujinx.Cpu.AppleHv { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs index c088ebdcf..78ffcbe4b 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs @@ -2,7 +2,7 @@ using ARMeilleure.State; namespace Ryujinx.Cpu.AppleHv { - unsafe class HvExecutionContextShadow : IHvExecutionContext + class HvExecutionContextShadow : IHvExecutionContext { public ulong Pc { get; set; } public ulong ElrEl1 { get; set; } @@ -56,4 +56,4 @@ namespace Ryujinx.Cpu.AppleHv return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs index 4f6ebefa7..d9ad637f8 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs @@ -8,10 +8,10 @@ namespace Ryujinx.Cpu.AppleHv { class HvExecutionContextVcpu : IHvExecutionContext { - private static MemoryBlock _setSimdFpRegFuncMem; - private delegate hv_result_t SetSimdFpReg(ulong vcpu, hv_simd_fp_reg_t reg, in V128 value, IntPtr funcPtr); - private static SetSimdFpReg _setSimdFpReg; - private static IntPtr _setSimdFpRegNativePtr; + private static readonly MemoryBlock _setSimdFpRegFuncMem; + private delegate HvResult SetSimdFpReg(ulong vcpu, HvSimdFPReg reg, in V128 value, IntPtr funcPtr); + private static readonly SetSimdFpReg _setSimdFpReg; + private static readonly IntPtr _setSimdFpRegNativePtr; static HvExecutionContextVcpu() { @@ -34,12 +34,12 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_PC, out ulong pc).ThrowOnError(); + HvApi.hv_vcpu_get_reg(_vcpu, HvReg.PC, out ulong pc).ThrowOnError(); return pc; } set { - HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_PC, value).ThrowOnError(); + HvApi.hv_vcpu_set_reg(_vcpu, HvReg.PC, value).ThrowOnError(); } } @@ -47,12 +47,12 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, out ulong elr).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError(); return elr; } set { - HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ELR_EL1, value).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.ELR_EL1, value).ThrowOnError(); } } @@ -60,12 +60,12 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ESR_EL1, out ulong esr).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError(); return esr; } set { - HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_ESR_EL1, value).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.ESR_EL1, value).ThrowOnError(); } } @@ -73,12 +73,12 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDR_EL0, out ulong tpidrEl0).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.TPIDR_EL0, out ulong tpidrEl0).ThrowOnError(); return (long)tpidrEl0; } set { - HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDR_EL0, (ulong)value).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.TPIDR_EL0, (ulong)value).ThrowOnError(); } } @@ -86,12 +86,12 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDRRO_EL0, out ulong tpidrroEl0).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.TPIDRRO_EL0, out ulong tpidrroEl0).ThrowOnError(); return (long)tpidrroEl0; } set { - HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_TPIDRRO_EL0, (ulong)value).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.TPIDRRO_EL0, (ulong)value).ThrowOnError(); } } @@ -99,12 +99,12 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_CPSR, out ulong cpsr).ThrowOnError(); + HvApi.hv_vcpu_get_reg(_vcpu, HvReg.CPSR, out ulong cpsr).ThrowOnError(); return (uint)cpsr; } set { - HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_CPSR, (ulong)value).ThrowOnError(); + HvApi.hv_vcpu_set_reg(_vcpu, HvReg.CPSR, (ulong)value).ThrowOnError(); } } @@ -112,12 +112,12 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_FPCR, out ulong fpcr).ThrowOnError(); + HvApi.hv_vcpu_get_reg(_vcpu, HvReg.FPCR, out ulong fpcr).ThrowOnError(); return (uint)fpcr; } set { - HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_FPCR, (ulong)value).ThrowOnError(); + HvApi.hv_vcpu_set_reg(_vcpu, HvReg.FPCR, (ulong)value).ThrowOnError(); } } @@ -125,16 +125,16 @@ namespace Ryujinx.Cpu.AppleHv { get { - HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_FPSR, out ulong fpsr).ThrowOnError(); + HvApi.hv_vcpu_get_reg(_vcpu, HvReg.FPSR, out ulong fpsr).ThrowOnError(); return (uint)fpsr; } set { - HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_FPSR, (ulong)value).ThrowOnError(); + HvApi.hv_vcpu_set_reg(_vcpu, HvReg.FPSR, (ulong)value).ThrowOnError(); } } - private ulong _vcpu; + private readonly ulong _vcpu; private int _interruptRequested; public HvExecutionContextVcpu(ulong vcpu) @@ -146,12 +146,12 @@ namespace Ryujinx.Cpu.AppleHv { if (index == 31) { - HvApi.hv_vcpu_get_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_SP_EL0, out ulong value).ThrowOnError(); + HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.SP_EL0, out ulong value).ThrowOnError(); return value; } else { - HvApi.hv_vcpu_get_reg(_vcpu, hv_reg_t.HV_REG_X0 + (uint)index, out ulong value).ThrowOnError(); + HvApi.hv_vcpu_get_reg(_vcpu, HvReg.X0 + (uint)index, out ulong value).ThrowOnError(); return value; } } @@ -160,23 +160,23 @@ namespace Ryujinx.Cpu.AppleHv { if (index == 31) { - HvApi.hv_vcpu_set_sys_reg(_vcpu, hv_sys_reg_t.HV_SYS_REG_SP_EL0, value).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.SP_EL0, value).ThrowOnError(); } else { - HvApi.hv_vcpu_set_reg(_vcpu, hv_reg_t.HV_REG_X0 + (uint)index, value).ThrowOnError(); + HvApi.hv_vcpu_set_reg(_vcpu, HvReg.X0 + (uint)index, value).ThrowOnError(); } } public V128 GetV(int index) { - HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, hv_simd_fp_reg_t.HV_SIMD_FP_REG_Q0 + (uint)index, out hv_simd_fp_uchar16_t value).ThrowOnError(); + HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, HvSimdFPReg.Q0 + (uint)index, out HvSimdFPUchar16 value).ThrowOnError(); return new V128(value.Low, value.High); } public void SetV(int index, V128 value) { - _setSimdFpReg(_vcpu, hv_simd_fp_reg_t.HV_SIMD_FP_REG_Q0 + (uint)index, value, _setSimdFpRegNativePtr).ThrowOnError(); + _setSimdFpReg(_vcpu, HvSimdFPReg.Q0 + (uint)index, value, _setSimdFpRegNativePtr).ThrowOnError(); } public void RequestInterrupt() @@ -193,4 +193,4 @@ namespace Ryujinx.Cpu.AppleHv return Interlocked.Exchange(ref _interruptRequested, 0) != 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs b/src/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs index 7eefe130b..35375ee60 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvIpaAllocator.cs @@ -31,4 +31,4 @@ namespace Ryujinx.Cpu.AppleHv _block.Free(offset, size); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs index 94289d1c3..3c3f087ab 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Cpu.AppleHv { - struct HvMemoryBlockAllocation : IDisposable + readonly struct HvMemoryBlockAllocation : IDisposable { private readonly HvMemoryBlockAllocator _owner; private readonly HvMemoryBlockAllocator.Block _block; diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs index 24c3a969c..ac184cb9a 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs @@ -1,12 +1,9 @@ using Ryujinx.Memory; -using System.Collections.Generic; namespace Ryujinx.Cpu.AppleHv { class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block> { - private const ulong InvalidOffset = ulong.MaxValue; - public class Block : PrivateMemoryAllocator.Block { private readonly HvIpaAllocator _ipaAllocator; @@ -21,7 +18,7 @@ namespace Ryujinx.Cpu.AppleHv Ipa = ipaAllocator.Allocate(size); } - HvApi.hv_vm_map((ulong)Memory.Pointer, Ipa, size, hv_memory_flags_t.HV_MEMORY_READ | hv_memory_flags_t.HV_MEMORY_WRITE).ThrowOnError(); + HvApi.hv_vm_map((ulong)Memory.Pointer, Ipa, size, HvMemoryFlags.Read | HvMemoryFlags.Write).ThrowOnError(); } public override void Destroy() @@ -44,7 +41,7 @@ namespace Ryujinx.Cpu.AppleHv _ipaAllocator = ipaAllocator; } - public unsafe HvMemoryBlockAllocation Allocate(ulong size, ulong alignment) + public HvMemoryBlockAllocation Allocate(ulong size, ulong alignment) { var allocation = Allocate(size, alignment, CreateBlock); diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs index 56a329e04..01c685d4b 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv MappedReplicated = 0x5555555555555555, WriteTrackedReplicated = 0xaaaaaaaaaaaaaaaa, - ReadWriteTrackedReplicated = ulong.MaxValue + ReadWriteTrackedReplicated = ulong.MaxValue, } private readonly InvalidAccessHandler _invalidAccessHandler; @@ -126,6 +126,7 @@ namespace Ryujinx.Cpu.AppleHv } } +#pragma warning disable IDE0051 // Remove unused private member /// <summary> /// Ensures the combination of virtual address and size is part of the addressable space and fully mapped. /// </summary> @@ -138,6 +139,7 @@ namespace Ryujinx.Cpu.AppleHv throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}"); } } +#pragma warning restore IDE0051 /// <inheritdoc/> public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) @@ -306,7 +308,7 @@ namespace Ryujinx.Cpu.AppleHv size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); + data[..size].CopyTo(_backingMemory.GetSpan(pa, size)); offset += size; } @@ -428,7 +430,7 @@ namespace Ryujinx.Cpu.AppleHv } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void GetPageBlockRange(ulong pageStart, ulong pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex) + private static void GetPageBlockRange(ulong pageStart, ulong pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex) { startMask = ulong.MaxValue << ((int)(pageStart & 31) << 1); endMask = ulong.MaxValue >> (64 - ((int)(pageEnd & 31) << 1)); @@ -606,7 +608,7 @@ namespace Ryujinx.Cpu.AppleHv size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); + _backingMemory.GetSpan(pa, size).CopyTo(data[..size]); offset += size; } @@ -723,7 +725,7 @@ namespace Ryujinx.Cpu.AppleHv /// <param name="startVa">The virtual address of the beginning of the first page</param> /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetPagesCount(ulong va, ulong size, out ulong startVa) + private static int GetPagesCount(ulong va, ulong size, out ulong startVa) { // WARNING: Always check if ulong does not overflow during the operations. startVa = va & ~(ulong)PageMask; @@ -814,7 +816,7 @@ namespace Ryujinx.Cpu.AppleHv { MemoryPermission.None => MemoryPermission.ReadAndWrite, MemoryPermission.Write => MemoryPermission.Read, - _ => MemoryPermission.None + _ => MemoryPermission.None, }; _addressSpace.ReprotectUser(va, size, protection); @@ -943,4 +945,4 @@ namespace Ryujinx.Cpu.AppleHv private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs index 484a9fe82..9c2cc0ff3 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs @@ -3,14 +3,14 @@ namespace Ryujinx.Cpu.AppleHv unsafe class HvVcpu { public readonly ulong Handle; - public readonly hv_vcpu_exit_t* ExitInfo; + public readonly HvVcpuExit* ExitInfo; public readonly IHvExecutionContext ShadowContext; public readonly IHvExecutionContext NativeContext; public readonly bool IsEphemeral; public HvVcpu( ulong handle, - hv_vcpu_exit_t* exitInfo, + HvVcpuExit* exitInfo, IHvExecutionContext shadowContext, IHvExecutionContext nativeContext, bool isEphemeral) @@ -22,4 +22,4 @@ namespace Ryujinx.Cpu.AppleHv IsEphemeral = isEphemeral; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs b/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs index cb1944fe7..fe01dce3c 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs @@ -17,10 +17,10 @@ namespace Ryujinx.Cpu.AppleHv private const int MaxActiveVcpus = 4; - public static readonly HvVcpuPool Instance = new HvVcpuPool(); + public static readonly HvVcpuPool Instance = new(); private int _totalVcpus; - private int _maxVcpus; + private readonly int _maxVcpus; public HvVcpuPool() { @@ -69,17 +69,17 @@ namespace Ryujinx.Cpu.AppleHv bool isEphemeral = newCount > _maxVcpus - MaxActiveVcpus; // Create VCPU. - hv_vcpu_exit_t* exitInfo = null; + HvVcpuExit* exitInfo = null; HvApi.hv_vcpu_create(out ulong vcpuHandle, ref exitInfo, IntPtr.Zero).ThrowOnError(); // Enable FP and SIMD instructions. - HvApi.hv_vcpu_set_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_CPACR_EL1, 0b11 << 20).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.CPACR_EL1, 0b11 << 20).ThrowOnError(); addressSpace.InitializeMmu(vcpuHandle); - HvExecutionContextVcpu nativeContext = new HvExecutionContextVcpu(vcpuHandle); + HvExecutionContextVcpu nativeContext = new(vcpuHandle); - HvVcpu vcpu = new HvVcpu(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral); + HvVcpu vcpu = new(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral); return vcpu; } @@ -100,4 +100,4 @@ namespace Ryujinx.Cpu.AppleHv Interlocked.Decrement(ref _totalVcpus); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvVm.cs b/src/Ryujinx.Cpu/AppleHv/HvVm.cs index d91abff9a..1f15022fd 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvVm.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvVm.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Cpu.AppleHv private static int _addressSpaces; private static HvIpaAllocator _ipaAllocator; - private static object _lock = new object(); + private static readonly object _lock = new(); public static (ulong, HvIpaAllocator) CreateAddressSpace(MemoryBlock block) { @@ -36,7 +36,7 @@ namespace Ryujinx.Cpu.AppleHv baseAddress = ipaAllocator.Allocate(block.Size, AsIpaAlignment); } - var rwx = hv_memory_flags_t.HV_MEMORY_READ | hv_memory_flags_t.HV_MEMORY_WRITE | hv_memory_flags_t.HV_MEMORY_EXEC; + var rwx = HvMemoryFlags.Read | HvMemoryFlags.Write | HvMemoryFlags.Exec; HvApi.hv_vm_map((ulong)block.Pointer, baseAddress, block.Size, rwx).ThrowOnError(); @@ -65,4 +65,4 @@ namespace Ryujinx.Cpu.AppleHv } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs index adf2dd997..7990ab72a 100644 --- a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs @@ -43,4 +43,4 @@ namespace Ryujinx.Cpu.AppleHv void RequestInterrupt(); bool GetAndClearInterruptRequested(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs b/src/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs index 7a4b670b3..085345999 100644 --- a/src/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs +++ b/src/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Cpu.Jit PtcLoadingState.Start => LoadState.Unloaded, PtcLoadingState.Loading => LoadState.Loading, PtcLoadingState.Loaded => LoadState.Loaded, - _ => throw new ArgumentException($"Invalid load state \"{newState}\".") + _ => throw new ArgumentException($"Invalid load state \"{newState}\"."), }; StateChanged?.Invoke(state, current, total); @@ -35,4 +35,4 @@ namespace Ryujinx.Cpu.Jit _loadState.Continue(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/Jit/JitEngine.cs b/src/Ryujinx.Cpu/Jit/JitEngine.cs index b158074f0..deebb8b9e 100644 --- a/src/Ryujinx.Cpu/Jit/JitEngine.cs +++ b/src/Ryujinx.Cpu/Jit/JitEngine.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Cpu.Jit return new JitCpuContext(_tickSource, memoryManager, for64Bit); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs index e1a527b1b..f15486e68 100644 --- a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs +++ b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs @@ -120,4 +120,4 @@ namespace Ryujinx.Cpu.Jit _impl.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs b/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs index 61e27eaf5..ed160680a 100644 --- a/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs +++ b/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs @@ -19,6 +19,10 @@ namespace Ryujinx.Cpu.Jit public void MapAsRx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndExecute); public void MapAsRwx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadWriteExecute); - public void Dispose() => _impl.Dispose(); + public void Dispose() + { + GC.SuppressFinalize(this); + _impl.Dispose(); + } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs index 6da11fb83..1c27e97fa 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -84,7 +84,6 @@ namespace Ryujinx.Cpu.Jit ulong remainingSize = size; ulong oVa = va; - ulong oPa = pa; while (remainingSize != 0) { _pageTable.Write((va / PageSize) * PteSize, PaToPte(pa)); @@ -246,7 +245,7 @@ namespace Ryujinx.Cpu.Jit size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); + data[..size].CopyTo(_backingMemory.GetSpan(pa, size)); offset += size; } @@ -298,7 +297,7 @@ namespace Ryujinx.Cpu.Jit } /// <inheritdoc/> - public unsafe WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false) + public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false) { if (size == 0) { @@ -345,7 +344,7 @@ namespace Ryujinx.Cpu.Jit /// <param name="startVa">The virtual address of the beginning of the first page</param> /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetPagesCount(ulong va, uint size, out ulong startVa) + private static int GetPagesCount(ulong va, uint size, out ulong startVa) { // WARNING: Always check if ulong does not overflow during the operations. startVa = va & ~(ulong)PageMask; @@ -482,7 +481,7 @@ namespace Ryujinx.Cpu.Jit size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); + _backingMemory.GetSpan(pa, size).CopyTo(data[..size]); offset += size; } @@ -576,6 +575,7 @@ namespace Ryujinx.Cpu.Jit } } +#pragma warning disable IDE0051 // Remove unused private member private ulong GetPhysicalAddress(ulong va) { // We return -1L if the virtual address is invalid or unmapped. @@ -586,6 +586,7 @@ namespace Ryujinx.Cpu.Jit return GetPhysicalAddressInternal(va); } +#pragma warning restore IDE0051 private ulong GetPhysicalAddressInternal(ulong va) { @@ -604,7 +605,7 @@ namespace Ryujinx.Cpu.Jit { MemoryPermission.None => 0L, MemoryPermission.Write => 2L << PointerTagBit, - _ => 3L << PointerTagBit + _ => 3L << PointerTagBit, }; int pages = GetPagesCount(va, (uint)size, out va); @@ -698,6 +699,8 @@ namespace Ryujinx.Cpu.Jit /// </summary> protected override void Destroy() => _pageTable.Dispose(); - private void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message); +#pragma warning disable IDE0051 // Remove unused private member + private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message); +#pragma warning restore IDE0051 } } diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 3686eb088..010a0bc25 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Cpu.Jit MappedReplicated = 0x5555555555555555, WriteTrackedReplicated = 0xaaaaaaaaaaaaaaaa, - ReadWriteTrackedReplicated = ulong.MaxValue + ReadWriteTrackedReplicated = ulong.MaxValue, } private readonly InvalidAccessHandler _invalidAccessHandler; @@ -404,7 +404,7 @@ namespace Ryujinx.Cpu.Jit } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void GetPageBlockRange(ulong pageStart, ulong pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex) + private static void GetPageBlockRange(ulong pageStart, ulong pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex) { startMask = ulong.MaxValue << ((int)(pageStart & 31) << 1); endMask = ulong.MaxValue >> (64 - ((int)(pageEnd & 31) << 1)); @@ -606,7 +606,7 @@ namespace Ryujinx.Cpu.Jit /// <param name="startVa">The virtual address of the beginning of the first page</param> /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetPagesCount(ulong va, ulong size, out ulong startVa) + private static int GetPagesCount(ulong va, ulong size, out ulong startVa) { // WARNING: Always check if ulong does not overflow during the operations. startVa = va & ~(ulong)PageMask; @@ -697,7 +697,7 @@ namespace Ryujinx.Cpu.Jit { MemoryPermission.None => MemoryPermission.ReadAndWrite, MemoryPermission.Write => MemoryPermission.Read, - _ => MemoryPermission.None + _ => MemoryPermission.None, }; _addressSpace.Base.Reprotect(va, size, protection, false); diff --git a/src/Ryujinx.Cpu/LoadState.cs b/src/Ryujinx.Cpu/LoadState.cs index 1f2e1ae8c..ea8f2abfa 100644 --- a/src/Ryujinx.Cpu/LoadState.cs +++ b/src/Ryujinx.Cpu/LoadState.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Cpu { Unloaded, Loading, - Loaded + Loaded, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/MemoryEhMeilleure.cs b/src/Ryujinx.Cpu/MemoryEhMeilleure.cs index 0b434ea74..9aa101aa9 100644 --- a/src/Ryujinx.Cpu/MemoryEhMeilleure.cs +++ b/src/Ryujinx.Cpu/MemoryEhMeilleure.cs @@ -10,7 +10,6 @@ namespace Ryujinx.Cpu { private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write); - private readonly MemoryTracking _tracking; private readonly TrackingEventDelegate _trackingEvent; private readonly ulong _baseAddress; @@ -18,12 +17,10 @@ namespace Ryujinx.Cpu public MemoryEhMeilleure(MemoryBlock addressSpace, MemoryBlock addressSpaceMirror, MemoryTracking tracking) { - _tracking = tracking; - _baseAddress = (ulong)addressSpace.Pointer; ulong endAddress = _baseAddress + addressSpace.Size; - _trackingEvent = new TrackingEventDelegate(tracking.VirtualMemoryEvent); + _trackingEvent = tracking.VirtualMemoryEvent; bool added = NativeSignalHandler.AddTrackedRegion((nuint)_baseAddress, (nuint)endAddress, Marshal.GetFunctionPointerForDelegate(_trackingEvent)); if (!added) @@ -51,6 +48,8 @@ namespace Ryujinx.Cpu public void Dispose() { + GC.SuppressFinalize(this); + NativeSignalHandler.RemoveTrackedRegion((nuint)_baseAddress); if (_mirrorAddress != 0) diff --git a/src/Ryujinx.Cpu/MemoryHelper.cs b/src/Ryujinx.Cpu/MemoryHelper.cs index 194a0c35d..ee14ee958 100644 --- a/src/Ryujinx.Cpu/MemoryHelper.cs +++ b/src/Ryujinx.Cpu/MemoryHelper.cs @@ -2,7 +2,6 @@ using Ryujinx.Common.Memory; using Ryujinx.Memory; using System; -using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -26,12 +25,12 @@ namespace Ryujinx.Cpu } } - public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : unmanaged + public static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : unmanaged { return MemoryMarshal.Cast<byte, T>(memory.GetSpan(position, Unsafe.SizeOf<T>()))[0]; } - public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : unmanaged + public static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : unmanaged { ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); @@ -42,22 +41,21 @@ namespace Ryujinx.Cpu public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1) { - using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream()) + using RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(); + + for (long offs = 0; offs < maxSize || maxSize == -1; offs++) { - for (long offs = 0; offs < maxSize || maxSize == -1; offs++) + byte value = memory.Read<byte>(position + (ulong)offs); + + if (value == 0) { - byte value = memory.Read<byte>(position + (ulong)offs); - - if (value == 0) - { - break; - } - - ms.WriteByte(value); + break; } - return Encoding.ASCII.GetString(ms.GetReadOnlySequence()); + ms.WriteByte(value); } + + return Encoding.ASCII.GetString(ms.GetReadOnlySequence()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/PrivateMemoryAllocation.cs b/src/Ryujinx.Cpu/PrivateMemoryAllocation.cs index 1327880e2..61b3827cc 100644 --- a/src/Ryujinx.Cpu/PrivateMemoryAllocation.cs +++ b/src/Ryujinx.Cpu/PrivateMemoryAllocation.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Cpu { - struct PrivateMemoryAllocation : IDisposable + readonly struct PrivateMemoryAllocation : IDisposable { private readonly PrivateMemoryAllocator _owner; private readonly PrivateMemoryAllocator.Block _block; @@ -27,8 +27,8 @@ namespace Ryujinx.Cpu public (PrivateMemoryAllocation, PrivateMemoryAllocation) Split(ulong splitOffset) { - PrivateMemoryAllocation left = new PrivateMemoryAllocation(_owner, _block, Offset, splitOffset); - PrivateMemoryAllocation right = new PrivateMemoryAllocation(_owner, _block, Offset + splitOffset, Size - splitOffset); + PrivateMemoryAllocation left = new(_owner, _block, Offset, splitOffset); + PrivateMemoryAllocation right = new(_owner, _block, Offset + splitOffset, Size - splitOffset); return (left, right); } diff --git a/src/Ryujinx.Cpu/PrivateMemoryAllocator.cs b/src/Ryujinx.Cpu/PrivateMemoryAllocator.cs index cbf1f1d9c..ce8e83419 100644 --- a/src/Ryujinx.Cpu/PrivateMemoryAllocator.cs +++ b/src/Ryujinx.Cpu/PrivateMemoryAllocator.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Cpu public MemoryBlock Memory { get; private set; } public ulong Size { get; } - private struct Range : IComparable<Range> + private readonly struct Range : IComparable<Range> { public ulong Offset { get; } public ulong Size { get; } @@ -40,7 +40,7 @@ namespace Ryujinx.Cpu Size = size; _freeRanges = new List<Range> { - new Range(0, size) + new Range(0, size), }; } @@ -164,7 +164,7 @@ namespace Ryujinx.Cpu { private const ulong InvalidOffset = ulong.MaxValue; - public struct Allocation + public readonly struct Allocation { public T Block { get; } public ulong Offset { get; } @@ -265,4 +265,4 @@ namespace Ryujinx.Cpu _blocks.Clear(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Cpu/TickSource.cs b/src/Ryujinx.Cpu/TickSource.cs index dc510bc26..eee83fc62 100644 --- a/src/Ryujinx.Cpu/TickSource.cs +++ b/src/Ryujinx.Cpu/TickSource.cs @@ -42,4 +42,4 @@ namespace Ryujinx.Cpu _tickCounter.Start(); } } -} \ No newline at end of file +} From 12c5f6ee89a2e93814d144a2c92acbf3f8a4788f Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Sat, 1 Jul 2023 03:29:37 -0300 Subject: [PATCH 689/737] Indexing at 0 should be used instead of the "Enumerable" extension method "First" (#5354) --- src/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs b/src/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs index f154e704c..6fb19fae3 100644 --- a/src/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs +++ b/src/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.LocaleGenerator context.RegisterSourceOutput(contents, (spc, content) => { - var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':').First().Trim().Replace("\"", "")); + var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':')[0].Trim().Replace("\"", "")); string enumSource = "namespace Ryujinx.Ava.Common.Locale;\n"; enumSource += "internal enum LocaleKeys\n{\n"; foreach (var line in lines) From 801b71a12883f8a104c699a92a9aa997e2a6d609 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 1 Jul 2023 12:31:42 +0200 Subject: [PATCH 690/737] [Ryujinx.Graphics.Vulkan] Address dotnet-format issues (#5378) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0059 warnings * Address dotnet format CA1816 warnings * Fix new dotnet-format issues after rebase * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Another rebase, another dotnet format run * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format analyzers after rebase * Run dotnet format style after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Run dotnet format after rebase * Address IDE0251 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Fix naming rule violations * Remove redundant code * Rename generics * Address review feedback * Remove SetOrigin --- .../AutoFlushCounter.cs | 18 +- .../BackgroundResources.cs | 14 +- src/Ryujinx.Graphics.Vulkan/BitMap.cs | 4 +- .../BufferAllocationType.cs | 2 +- src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 79 ++--- src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 16 +- src/Ryujinx.Graphics.Vulkan/BufferState.cs | 6 +- .../BufferUsageBitmap.cs | 12 +- src/Ryujinx.Graphics.Vulkan/CacheByRange.cs | 36 +- .../CommandBufferPool.cs | 23 +- .../DescriptorSetCollection.cs | 22 +- .../DescriptorSetManager.cs | 21 +- .../DescriptorSetUpdater.cs | 87 ++--- .../DisposableBuffer.cs | 5 +- .../Effects/FsrScalingFilter.cs | 18 +- .../Effects/FxaaPostProcessingEffect.cs | 11 +- .../Effects/IPostProcessingEffect.cs | 2 +- .../Effects/IScalingFilter.cs | 2 +- .../Effects/SmaaConstants.cs | 2 +- .../Effects/SmaaPostProcessingEffect.cs | 22 +- src/Ryujinx.Graphics.Vulkan/EnumConversion.cs | 308 +++++++++--------- src/Ryujinx.Graphics.Vulkan/FenceHolder.cs | 8 +- .../FormatCapabilities.cs | 39 +-- src/Ryujinx.Graphics.Vulkan/FormatTable.cs | 172 ++++------ .../FramebufferParams.cs | 11 +- .../HardwareCapabilities.cs | 2 +- src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs | 22 +- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 148 +++++---- .../HostMemoryAllocator.cs | 14 +- src/Ryujinx.Graphics.Vulkan/IdList.cs | 10 +- .../IndexBufferPattern.cs | 2 +- .../IndexBufferState.cs | 21 +- .../MemoryAllocation.cs | 2 +- .../MemoryAllocator.cs | 4 +- .../MemoryAllocatorBlockList.cs | 25 +- .../MoltenVK/MVKConfiguration.cs | 12 +- .../MoltenVK/MVKInitialization.cs | 2 +- .../MultiFenceHolder.cs | 10 +- .../PersistentFlushBuffer.cs | 12 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 103 +++--- .../PipelineConverter.cs | 16 +- .../PipelineDynamicState.cs | 6 +- src/Ryujinx.Graphics.Vulkan/PipelineFull.cs | 25 +- .../PipelineLayoutCache.cs | 6 +- .../PipelineLayoutFactory.cs | 12 +- src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 156 ++++----- src/Ryujinx.Graphics.Vulkan/PipelineUid.cs | 14 +- .../Queries/BufferedQuery.cs | 18 +- .../Queries/CounterQueue.cs | 18 +- .../Queries/CounterQueueEvent.cs | 6 +- .../ResourceBindingSegment.cs | 2 +- .../ResourceLayoutBuilder.cs | 4 +- src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs | 16 +- .../SemaphoreHolder.cs | 6 +- src/Ryujinx.Graphics.Vulkan/Shader.cs | 16 +- .../ShaderCollection.cs | 16 +- .../Shaders/ShaderBinaries.cs | 81 ++--- src/Ryujinx.Graphics.Vulkan/SpecInfo.cs | 16 +- src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs | 12 +- src/Ryujinx.Graphics.Vulkan/SyncManager.cs | 36 +- src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 12 +- src/Ryujinx.Graphics.Vulkan/TextureCopy.cs | 29 +- src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 39 +-- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 55 ++-- src/Ryujinx.Graphics.Vulkan/Vendor.cs | 6 +- .../VertexBufferState.cs | 28 +- .../VertexBufferUpdater.cs | 14 +- .../VulkanDebugMessenger.cs | 15 +- .../VulkanInitialization.cs | 112 ++++--- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 205 ++++++------ src/Ryujinx.Graphics.Vulkan/Window.cs | 34 +- src/Ryujinx.Graphics.Vulkan/WindowBase.cs | 4 +- 72 files changed, 1134 insertions(+), 1230 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs index 4e2a9d6bc..8ac412fe5 100644 --- a/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs +++ b/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs @@ -8,16 +8,16 @@ namespace Ryujinx.Graphics.Vulkan internal class AutoFlushCounter { // How often to flush on framebuffer change. - private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; // (1ms) + private readonly static long _framebufferFlushTimer = Stopwatch.Frequency / 1000; // (1ms) // How often to flush on draw when fast flush mode is enabled. - private readonly static long DrawFlushTimer = Stopwatch.Frequency / 666; // (1.5ms) + private readonly static long _drawFlushTimer = Stopwatch.Frequency / 666; // (1.5ms) // Average wait time that triggers fast flush mode to be entered. - private readonly static long FastFlushEnterThreshold = Stopwatch.Frequency / 666; // (1.5ms) + private readonly static long _fastFlushEnterThreshold = Stopwatch.Frequency / 666; // (1.5ms) // Average wait time that triggers fast flush mode to be exited. - private readonly static long FastFlushExitThreshold = Stopwatch.Frequency / 10000; // (0.1ms) + private readonly static long _fastFlushExitThreshold = Stopwatch.Frequency / 10000; // (0.1ms) // Number of frames to average waiting times over. private const int SyncWaitAverageCount = 20; @@ -34,11 +34,11 @@ namespace Ryujinx.Graphics.Vulkan private int _consecutiveQueries; private int _queryCount; - private int[] _queryCountHistory = new int[3]; + private readonly int[] _queryCountHistory = new int[3]; private int _queryCountHistoryIndex; private int _remainingQueries; - private long[] _syncWaitHistory = new long[SyncWaitAverageCount]; + private readonly long[] _syncWaitHistory = new long[SyncWaitAverageCount]; private int _syncWaitHistoryIndex; private bool _fastFlushMode; @@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan return false; } - long flushTimeout = DrawFlushTimer; + long flushTimeout = _drawFlushTimer; long now = Stopwatch.GetTimestamp(); @@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan return false; } - long flushTimeout = FramebufferFlushTimer; + long flushTimeout = _framebufferFlushTimer; long now = Stopwatch.GetTimestamp(); @@ -169,7 +169,7 @@ namespace Ryujinx.Graphics.Vulkan long averageWait = (long)_syncWaitHistory.Average(); - if (_fastFlushMode ? averageWait < FastFlushExitThreshold : averageWait > FastFlushEnterThreshold) + if (_fastFlushMode ? averageWait < _fastFlushExitThreshold : averageWait > _fastFlushEnterThreshold) { _fastFlushMode = !_fastFlushMode; Logger.Debug?.PrintMsg(LogClass.Gpu, $"Switched fast flush mode: ({_fastFlushMode})"); diff --git a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs index b93b7a250..e9478b438 100644 --- a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs +++ b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Vulkan { class BackgroundResource : IDisposable { - private VulkanRenderer _gd; + private readonly VulkanRenderer _gd; private Device _device; private CommandBufferPool _pool; @@ -38,10 +38,7 @@ namespace Ryujinx.Graphics.Vulkan public PersistentFlushBuffer GetFlushBuffer() { - if (_flushBuffer == null) - { - _flushBuffer = new PersistentFlushBuffer(_gd); - } + _flushBuffer ??= new PersistentFlushBuffer(_gd); return _flushBuffer; } @@ -55,10 +52,10 @@ namespace Ryujinx.Graphics.Vulkan class BackgroundResources : IDisposable { - private VulkanRenderer _gd; + private readonly VulkanRenderer _gd; private Device _device; - private Dictionary<Thread, BackgroundResource> _resources; + private readonly Dictionary<Thread, BackgroundResource> _resources; public BackgroundResources(VulkanRenderer gd, Device device) { @@ -89,8 +86,7 @@ namespace Ryujinx.Graphics.Vulkan lock (_resources) { - BackgroundResource resource; - if (!_resources.TryGetValue(thread, out resource)) + if (!_resources.TryGetValue(thread, out BackgroundResource resource)) { Cleanup(); diff --git a/src/Ryujinx.Graphics.Vulkan/BitMap.cs b/src/Ryujinx.Graphics.Vulkan/BitMap.cs index efa71fc78..5619fcdf7 100644 --- a/src/Ryujinx.Graphics.Vulkan/BitMap.cs +++ b/src/Ryujinx.Graphics.Vulkan/BitMap.cs @@ -131,7 +131,7 @@ public void Clear(int bit) { int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; + int wordBit = bit & IntMask; long wordMask = 1L << wordBit; @@ -154,4 +154,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs b/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs index 814890411..00b65714b 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs @@ -7,6 +7,6 @@ HostMappedNoCache, HostMapped, DeviceLocal, - DeviceLocalMapped + DeviceLocalMapped, } } diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 6e10fad00..54635631a 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan private MemoryAllocation _allocation; private Auto<DisposableBuffer> _buffer; private Auto<MemoryAllocation> _allocationAuto; - private bool _allocationImported; + private readonly bool _allocationImported; private ulong _bufferHandle; private CacheByRange<BufferHolder> _cachedConvertedBuffers; @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan private bool _lastAccessIsWrite; - private BufferAllocationType _baseType; + private readonly BufferAllocationType _baseType; private BufferAllocationType _currentType; private bool _swapQueued; @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Vulkan private int _flushTemp; private int _lastFlushWrite = -1; - private ReaderWriterLock _flushLock; + private readonly ReaderWriterLock _flushLock; private FenceHolder _flushFence; private int _flushWaiting; @@ -143,10 +143,7 @@ namespace Ryujinx.Graphics.Vulkan } else { - if (cbs == null) - { - cbs = _gd.CommandBufferPool.Rent(); - } + cbs ??= _gd.CommandBufferPool.Rent(); CommandBufferScoped cbsV = cbs.Value; @@ -184,17 +181,13 @@ namespace Ryujinx.Graphics.Vulkan return true; } - else - { - return false; - } - } - else - { - _swapQueued = false; - return true; + return false; } + + _swapQueued = false; + + return true; } private void ConsiderBackingSwap() @@ -251,13 +244,13 @@ namespace Ryujinx.Graphics.Vulkan public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size, Action invalidateView) { - var bufferViewCreateInfo = new BufferViewCreateInfo() + var bufferViewCreateInfo = new BufferViewCreateInfo { SType = StructureType.BufferViewCreateInfo, Buffer = new VkBuffer(_bufferHandle), Format = format, Offset = (uint)offset, - Range = (uint)size + Range = (uint)size, }; _gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError(); @@ -288,11 +281,11 @@ namespace Ryujinx.Graphics.Vulkan if (needsBarrier) { - MemoryBarrier memoryBarrier = new MemoryBarrier() + MemoryBarrier memoryBarrier = new() { SType = StructureType.MemoryBarrier, SrcAccessMask = DefaultAccessFlags, - DstAccessMask = DefaultAccessFlags + DstAccessMask = DefaultAccessFlags, }; _gd.Api.CmdPipelineBarrier( @@ -366,14 +359,14 @@ namespace Ryujinx.Graphics.Vulkan return Unsafe.As<ulong, BufferHandle>(ref handle); } - public unsafe IntPtr Map(int offset, int mappingSize) + public IntPtr Map(int offset, int mappingSize) { return _map; } private void ClearFlushFence() { - // Asusmes _flushLock is held as writer. + // Assumes _flushLock is held as writer. if (_flushFence != null) { @@ -421,7 +414,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public unsafe PinnedSpan<byte> GetData(int offset, int size) + public PinnedSpan<byte> GetData(int offset, int size) { _flushLock.AcquireReaderLock(Timeout.Infinite); @@ -447,26 +440,24 @@ namespace Ryujinx.Graphics.Vulkan return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount); } + + BackgroundResource resource = _gd.BackgroundResources.Get(); + + if (_gd.CommandBufferPool.OwnedByCurrentThread) + { + _gd.FlushAllCommands(); + + result = resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size); + } else { - BackgroundResource resource = _gd.BackgroundResources.Get(); - - if (_gd.CommandBufferPool.OwnedByCurrentThread) - { - _gd.FlushAllCommands(); - - result = resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size); - } - else - { - result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size); - } - - _flushLock.ReleaseReaderLock(); - - // Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses. - return PinnedSpan<byte>.UnsafeFromSpan(result); + result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size); } + + _flushLock.ReleaseReaderLock(); + + // Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses. + return PinnedSpan<byte>.UnsafeFromSpan(result); } public unsafe Span<byte> GetDataStorage(int offset, int size) @@ -503,7 +494,7 @@ namespace Ryujinx.Graphics.Vulkan { WaitForFences(offset, dataSize); - data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize)); + data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize)); SignalWrite(offset, dataSize); @@ -542,7 +533,7 @@ namespace Ryujinx.Graphics.Vulkan if (_map != IntPtr.Zero) { - data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize)); + data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize)); } else { @@ -657,7 +648,7 @@ namespace Ryujinx.Graphics.Vulkan int offset, int size) { - BufferMemoryBarrier memoryBarrier = new BufferMemoryBarrier() + BufferMemoryBarrier memoryBarrier = new() { SType = StructureType.BufferMemoryBarrier, SrcAccessMask = srcAccessMask, @@ -666,7 +657,7 @@ namespace Ryujinx.Graphics.Vulkan DstQueueFamilyIndex = Vk.QueueFamilyIgnored, Buffer = buffer, Offset = (ulong)offset, - Size = (ulong)size + Size = (ulong)size, }; gd.Api.CmdPipelineBarrier( diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index 1b55909d3..b916a1ef2 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -73,12 +73,12 @@ namespace Ryujinx.Graphics.Vulkan usage |= BufferUsageFlags.IndirectBufferBit; } - var bufferCreateInfo = new BufferCreateInfo() + var bufferCreateInfo = new BufferCreateInfo { SType = StructureType.BufferCreateInfo, Size = (ulong)size, Usage = usage, - SharingMode = SharingMode.Exclusive + SharingMode = SharingMode.Exclusive, }; gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); @@ -134,12 +134,12 @@ namespace Ryujinx.Graphics.Vulkan usage |= BufferUsageFlags.IndirectBufferBit; } - var bufferCreateInfo = new BufferCreateInfo() + var bufferCreateInfo = new BufferCreateInfo { SType = StructureType.BufferCreateInfo, Size = (ulong)Environment.SystemPageSize, Usage = usage, - SharingMode = SharingMode.Exclusive + SharingMode = SharingMode.Exclusive, }; gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); @@ -169,12 +169,12 @@ namespace Ryujinx.Graphics.Vulkan usage |= BufferUsageFlags.IndirectBufferBit; } - var bufferCreateInfo = new BufferCreateInfo() + var bufferCreateInfo = new BufferCreateInfo { SType = StructureType.BufferCreateInfo, Size = (ulong)size, Usage = usage, - SharingMode = SharingMode.Exclusive + SharingMode = SharingMode.Exclusive, }; gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); @@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Vulkan BufferAllocationType.HostMapped => DefaultBufferMemoryFlags, BufferAllocationType.DeviceLocal => DeviceLocalBufferMemoryFlags, BufferAllocationType.DeviceLocalMapped => DeviceLocalMappedBufferMemoryFlags, - _ => DefaultBufferMemoryFlags + _ => DefaultBufferMemoryFlags, }; // If an allocation with this memory type fails, fall back to the previous one. @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Vulkan return (buffer, allocation, type); } - public unsafe BufferHolder Create( + public BufferHolder Create( VulkanRenderer gd, int size, bool forConditionalRendering = false, diff --git a/src/Ryujinx.Graphics.Vulkan/BufferState.cs b/src/Ryujinx.Graphics.Vulkan/BufferState.cs index 6829f8333..ee4badd2f 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferState.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vulkan { struct BufferState : IDisposable { - public static BufferState Null => new BufferState(null, 0, 0); + public static BufferState Null => new(null, 0, 0); private readonly int _offset; private readonly int _size; @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan buffer?.IncrementReferenceCount(); } - public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding) + public readonly void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding) { if (_buffer != null) { @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public void Dispose() + public readonly void Dispose() { _buffer?.DecrementReferenceCount(); } diff --git a/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs b/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs index 920501d3a..a8ff7c286 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs @@ -2,13 +2,13 @@ { internal class BufferUsageBitmap { - private BitMap _bitmap; - private int _size; - private int _granularity; - private int _bits; + private readonly BitMap _bitmap; + private readonly int _size; + private readonly int _granularity; + private readonly int _bits; - private int _intsPerCb; - private int _bitsPerCb; + private readonly int _intsPerCb; + private readonly int _bitsPerCb; public BufferUsageBitmap(int size, int granularity) { diff --git a/src/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/src/Ryujinx.Graphics.Vulkan/CacheByRange.cs index a9d1b0ef7..16954d21b 100644 --- a/src/Ryujinx.Graphics.Vulkan/CacheByRange.cs +++ b/src/Ryujinx.Graphics.Vulkan/CacheByRange.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan _buffer = null; } - public bool KeyEqual(ICacheKey other) + public readonly bool KeyEqual(ICacheKey other) { return other is I8ToI16CacheKey; } @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Vulkan _buffer = buffer; } - public void Dispose() + public readonly void Dispose() { _gd.PipelineInternal.DirtyIndexBuffer(_buffer); } @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan _buffer = null; } - public bool KeyEqual(ICacheKey other) + public readonly bool KeyEqual(ICacheKey other) { return other is AlignedVertexBufferCacheKey entry && entry._stride == _stride && @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Vulkan _buffer = buffer; } - public void Dispose() + public readonly void Dispose() { _gd.PipelineInternal.DirtyVertexBuffer(_buffer); } @@ -73,8 +73,8 @@ namespace Ryujinx.Graphics.Vulkan struct TopologyConversionCacheKey : ICacheKey { - private IndexBufferPattern _pattern; - private int _indexSize; + private readonly IndexBufferPattern _pattern; + private readonly int _indexSize; // Used to notify the pipeline that bindings have invalidated on dispose. private readonly VulkanRenderer _gd; @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan _buffer = null; } - public bool KeyEqual(ICacheKey other) + public readonly bool KeyEqual(ICacheKey other) { return other is TopologyConversionCacheKey entry && entry._pattern == _pattern && @@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Vulkan _buffer = buffer; } - public void Dispose() + public readonly void Dispose() { _gd.PipelineInternal.DirtyIndexBuffer(_buffer); } @@ -147,9 +147,9 @@ namespace Ryujinx.Graphics.Vulkan } } - struct IndirectDataCacheKey : ICacheKey + readonly struct IndirectDataCacheKey : ICacheKey { - private IndexBufferPattern _pattern; + private readonly IndexBufferPattern _pattern; public IndirectDataCacheKey(IndexBufferPattern pattern) { @@ -168,12 +168,12 @@ namespace Ryujinx.Graphics.Vulkan struct DrawCountCacheKey : ICacheKey { - public bool KeyEqual(ICacheKey other) + public readonly bool KeyEqual(ICacheKey other) { return other is DrawCountCacheKey; } - public void Dispose() + public readonly void Dispose() { } } @@ -214,7 +214,7 @@ namespace Ryujinx.Graphics.Vulkan DependencyList = null; } - public void InvalidateDependencies() + public readonly void InvalidateDependencies() { if (DependencyList != null) { @@ -317,7 +317,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public void ClearRange(int offset, int size) + public readonly void ClearRange(int offset, int size) { if (_ranges != null && _ranges.Count > 0) { @@ -356,15 +356,11 @@ namespace Ryujinx.Graphics.Vulkan private List<Entry> GetEntries(int offset, int size) { - if (_ranges == null) - { - _ranges = new Dictionary<ulong, List<Entry>>(); - } + _ranges ??= new Dictionary<ulong, List<Entry>>(); ulong key = PackRange(offset, size); - List<Entry> value; - if (!_ranges.TryGetValue(key, out value)) + if (!_ranges.TryGetValue(key, out List<Entry> value)) { value = new List<Entry>(); _ranges.Add(key, value); diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs index 42b46eaec..17eeef68a 100644 --- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs @@ -2,7 +2,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using Thread = System.Threading.Thread; +using System.Threading; +using Semaphore = Silk.NET.Vulkan.Semaphore; namespace Ryujinx.Graphics.Vulkan { @@ -10,8 +11,8 @@ namespace Ryujinx.Graphics.Vulkan { public const int MaxCommandBuffers = 16; - private int _totalCommandBuffers; - private int _totalCommandBuffersMask; + private readonly int _totalCommandBuffers; + private readonly int _totalCommandBuffersMask; private readonly Vk _api; private readonly Device _device; @@ -36,12 +37,12 @@ namespace Ryujinx.Graphics.Vulkan public void Initialize(Vk api, Device device, CommandPool pool) { - var allocateInfo = new CommandBufferAllocateInfo() + var allocateInfo = new CommandBufferAllocateInfo { SType = StructureType.CommandBufferAllocateInfo, CommandBufferCount = 1, CommandPool = pool, - Level = CommandBufferLevel.Primary + Level = CommandBufferLevel.Primary, }; api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer); @@ -67,12 +68,12 @@ namespace Ryujinx.Graphics.Vulkan _queueLock = queueLock; _owner = Thread.CurrentThread; - var commandPoolCreateInfo = new CommandPoolCreateInfo() + var commandPoolCreateInfo = new CommandPoolCreateInfo { SType = StructureType.CommandPoolCreateInfo, QueueFamilyIndex = queueFamilyIndex, Flags = CommandPoolCreateFlags.TransientBit | - CommandPoolCreateFlags.ResetCommandBufferBit + CommandPoolCreateFlags.ResetCommandBufferBit, }; api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError(); @@ -243,9 +244,9 @@ namespace Ryujinx.Graphics.Vulkan _inUseCount++; - var commandBufferBeginInfo = new CommandBufferBeginInfo() + var commandBufferBeginInfo = new CommandBufferBeginInfo { - SType = StructureType.CommandBufferBeginInfo + SType = StructureType.CommandBufferBeginInfo, }; _api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError(); @@ -291,7 +292,7 @@ namespace Ryujinx.Graphics.Vulkan { fixed (PipelineStageFlags* pWaitDstStageMask = waitDstStageMask) { - SubmitInfo sInfo = new SubmitInfo() + SubmitInfo sInfo = new() { SType = StructureType.SubmitInfo, WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0, @@ -300,7 +301,7 @@ namespace Ryujinx.Graphics.Vulkan CommandBufferCount = 1, PCommandBuffers = &commandBuffer, SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0, - PSignalSemaphores = pSignalSemaphores + PSignalSemaphores = pSignalSemaphores, }; lock (_queueLock) diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs index a185d7871..846dd5c7d 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan { private DescriptorSetManager.DescriptorPoolHolder _holder; private readonly DescriptorSet[] _descriptorSets; - public int SetsCount => _descriptorSets.Length; + public readonly int SetsCount => _descriptorSets.Length; public DescriptorSetCollection(DescriptorSetManager.DescriptorPoolHolder holder, DescriptorSet[] descriptorSets) { @@ -20,10 +20,10 @@ namespace Ryujinx.Graphics.Vulkan { Span<DescriptorBufferInfo> infos = stackalloc DescriptorBufferInfo[count]; - infos.Fill(new DescriptorBufferInfo() + infos.Fill(new DescriptorBufferInfo { Buffer = dummyBuffer, - Range = Vk.WholeSize + Range = Vk.WholeSize, }); UpdateBuffers(setIndex, baseBinding, infos, type); @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)bindingIndex, DescriptorType = type, DescriptorCount = 1, - PBufferInfo = &bufferInfo + PBufferInfo = &bufferInfo, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)baseBinding, DescriptorType = type, DescriptorCount = (uint)bufferInfo.Length, - PBufferInfo = pBufferInfo + PBufferInfo = pBufferInfo, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); @@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)bindingIndex, DescriptorType = type, DescriptorCount = 1, - PImageInfo = &imageInfo + PImageInfo = &imageInfo, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); @@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)baseBinding, DescriptorType = type, DescriptorCount = (uint)imageInfo.Length, - PImageInfo = pImageInfo + PImageInfo = pImageInfo, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); @@ -141,7 +141,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)(baseBinding + i), DescriptorType = DescriptorType.CombinedImageSampler, DescriptorCount = (uint)count, - PImageInfo = pImageInfo + PImageInfo = pImageInfo, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); @@ -163,7 +163,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)bindingIndex, DescriptorType = type, DescriptorCount = 1, - PTexelBufferView = &texelBufferView + PTexelBufferView = &texelBufferView, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); @@ -197,7 +197,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)baseBinding + i, DescriptorType = type, DescriptorCount = count, - PTexelBufferView = pTexelBufferView + i + PTexelBufferView = pTexelBufferView + i, }; _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public DescriptorSet[] GetSets() + public readonly DescriptorSet[] GetSets() { return _descriptorSets; } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs index a88bb7b12..2f7b604c4 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs @@ -24,14 +24,14 @@ namespace Ryujinx.Graphics.Vulkan Api = api; Device = device; - var poolSizes = new DescriptorPoolSize[] + var poolSizes = new[] { new DescriptorPoolSize(DescriptorType.UniformBuffer, (1 + Constants.MaxUniformBufferBindings) * DescriptorPoolMultiplier), new DescriptorPoolSize(DescriptorType.StorageBuffer, Constants.MaxStorageBufferBindings * DescriptorPoolMultiplier), new DescriptorPoolSize(DescriptorType.CombinedImageSampler, Constants.MaxTextureBindings * DescriptorPoolMultiplier), new DescriptorPoolSize(DescriptorType.StorageImage, Constants.MaxImageBindings * DescriptorPoolMultiplier), new DescriptorPoolSize(DescriptorType.UniformTexelBuffer, Constants.MaxTextureBindings * DescriptorPoolMultiplier), - new DescriptorPoolSize(DescriptorType.StorageTexelBuffer, Constants.MaxImageBindings * DescriptorPoolMultiplier) + new DescriptorPoolSize(DescriptorType.StorageTexelBuffer, Constants.MaxImageBindings * DescriptorPoolMultiplier), }; uint maxSets = (uint)poolSizes.Length * DescriptorPoolMultiplier; @@ -40,19 +40,19 @@ namespace Ryujinx.Graphics.Vulkan fixed (DescriptorPoolSize* pPoolsSize = poolSizes) { - var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo() + var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo { SType = StructureType.DescriptorPoolCreateInfo, MaxSets = maxSets, PoolSizeCount = (uint)poolSizes.Length, - PPoolSizes = pPoolsSize + PPoolSizes = pPoolsSize, }; Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError(); } } - public unsafe DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts) + public DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts) { TryAllocateDescriptorSets(layouts, isTry: false, out var dsc); return dsc; @@ -73,12 +73,12 @@ namespace Ryujinx.Graphics.Vulkan { fixed (DescriptorSetLayout* pLayouts = layouts) { - var descriptorSetAllocateInfo = new DescriptorSetAllocateInfo() + var descriptorSetAllocateInfo = new DescriptorSetAllocateInfo { SType = StructureType.DescriptorSetAllocateInfo, DescriptorPool = _pool, DescriptorSetCount = (uint)layouts.Length, - PSetLayouts = pLayouts + PSetLayouts = pLayouts, }; var result = Api.AllocateDescriptorSets(Device, &descriptorSetAllocateInfo, pDescriptorSets); @@ -142,6 +142,7 @@ namespace Ryujinx.Graphics.Vulkan public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } } @@ -186,15 +187,13 @@ namespace Ryujinx.Graphics.Vulkan { if (disposing) { - unsafe - { - _currentPool?.Dispose(); - } + _currentPool?.Dispose(); } } public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index b09a0667e..087d90fb8 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -2,8 +2,11 @@ using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; -using System.Numerics; using System.Runtime.CompilerServices; +using Buffer = Silk.NET.Vulkan.Buffer; +using CompareOp = Ryujinx.Graphics.GAL.CompareOp; +using Format = Ryujinx.Graphics.GAL.Format; +using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; namespace Ryujinx.Graphics.Vulkan { @@ -14,25 +17,25 @@ namespace Ryujinx.Graphics.Vulkan private ShaderCollection _program; - private Auto<DisposableBuffer>[] _uniformBufferRefs; - private Auto<DisposableBuffer>[] _storageBufferRefs; - private Auto<DisposableImageView>[] _textureRefs; - private Auto<DisposableSampler>[] _samplerRefs; - private Auto<DisposableImageView>[] _imageRefs; - private TextureBuffer[] _bufferTextureRefs; - private TextureBuffer[] _bufferImageRefs; - private GAL.Format[] _bufferImageFormats; + private readonly Auto<DisposableBuffer>[] _uniformBufferRefs; + private readonly Auto<DisposableBuffer>[] _storageBufferRefs; + private readonly Auto<DisposableImageView>[] _textureRefs; + private readonly Auto<DisposableSampler>[] _samplerRefs; + private readonly Auto<DisposableImageView>[] _imageRefs; + private readonly TextureBuffer[] _bufferTextureRefs; + private readonly TextureBuffer[] _bufferImageRefs; + private readonly Format[] _bufferImageFormats; - private DescriptorBufferInfo[] _uniformBuffers; - private DescriptorBufferInfo[] _storageBuffers; - private DescriptorImageInfo[] _textures; - private DescriptorImageInfo[] _images; - private BufferView[] _bufferTextures; - private BufferView[] _bufferImages; + private readonly DescriptorBufferInfo[] _uniformBuffers; + private readonly DescriptorBufferInfo[] _storageBuffers; + private readonly DescriptorImageInfo[] _textures; + private readonly DescriptorImageInfo[] _images; + private readonly BufferView[] _bufferTextures; + private readonly BufferView[] _bufferImages; - private bool[] _uniformSet; - private bool[] _storageSet; - private Silk.NET.Vulkan.Buffer _cachedSupportBuffer; + private readonly bool[] _uniformSet; + private readonly bool[] _storageSet; + private Buffer _cachedSupportBuffer; [Flags] private enum DirtyFlags @@ -42,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan Storage = 1 << 1, Texture = 1 << 2, Image = 1 << 3, - All = Uniform | Storage | Texture | Image + All = Uniform | Storage | Texture | Image, } private DirtyFlags _dirty; @@ -66,7 +69,7 @@ namespace Ryujinx.Graphics.Vulkan _imageRefs = new Auto<DisposableImageView>[Constants.MaxImageBindings * 2]; _bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2]; _bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2]; - _bufferImageFormats = new GAL.Format[Constants.MaxImageBindings * 2]; + _bufferImageFormats = new Format[Constants.MaxImageBindings * 2]; _uniformBuffers = new DescriptorBufferInfo[Constants.MaxUniformBufferBindings]; _storageBuffers = new DescriptorBufferInfo[Constants.MaxStorageBufferBindings]; @@ -75,9 +78,9 @@ namespace Ryujinx.Graphics.Vulkan _bufferTextures = new BufferView[Constants.MaxTexturesPerStage]; _bufferImages = new BufferView[Constants.MaxImagesPerStage]; - var initialImageInfo = new DescriptorImageInfo() + var initialImageInfo = new DescriptorImageInfo { - ImageLayout = ImageLayout.General + ImageLayout = ImageLayout.General, }; _textures.AsSpan().Fill(initialImageInfo); @@ -106,7 +109,7 @@ namespace Ryujinx.Graphics.Vulkan 1, 1, 4, - GAL.Format.R8G8B8A8Unorm, + Format.R8G8B8A8Unorm, DepthStencilMode.Depth, Target.Texture2D, SwizzleComponent.Red, @@ -114,7 +117,7 @@ namespace Ryujinx.Graphics.Vulkan SwizzleComponent.Blue, SwizzleComponent.Alpha), 1f); - _dummySampler = (SamplerHolder)gd.CreateSampler(new GAL.SamplerCreateInfo( + _dummySampler = (SamplerHolder)gd.CreateSampler(new SamplerCreateInfo( MinFilter.Nearest, MagFilter.Nearest, false, @@ -122,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan AddressMode.Repeat, AddressMode.Repeat, CompareMode.None, - GAL.CompareOp.Always, + CompareOp.Always, new ColorF(0, 0, 0, 0), 0, 0, @@ -142,7 +145,7 @@ namespace Ryujinx.Graphics.Vulkan _dirty = DirtyFlags.All; } - public void SetImage(int binding, ITexture image, GAL.Format imageFormat) + public void SetImage(int binding, ITexture image, Format imageFormat) { if (image is TextureBuffer imageBuffer) { @@ -181,10 +184,10 @@ namespace Ryujinx.Graphics.Vulkan Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false, isSSBO: true); ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index]; - DescriptorBufferInfo info = new DescriptorBufferInfo() + DescriptorBufferInfo info = new() { Offset = (ulong)buffer.Offset, - Range = (ulong)buffer.Size + Range = (ulong)buffer.Size, }; ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index]; @@ -209,10 +212,10 @@ namespace Ryujinx.Graphics.Vulkan ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index]; - DescriptorBufferInfo info = new DescriptorBufferInfo() + DescriptorBufferInfo info = new() { Offset = 0, - Range = Vk.WholeSize + Range = Vk.WholeSize, }; ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index]; @@ -289,10 +292,10 @@ namespace Ryujinx.Graphics.Vulkan Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); ref Auto<DisposableBuffer> currentVkBuffer = ref _uniformBufferRefs[index]; - DescriptorBufferInfo info = new DescriptorBufferInfo() + DescriptorBufferInfo info = new() { Offset = (ulong)buffer.Offset, - Range = (ulong)buffer.Size + Range = (ulong)buffer.Size, }; ref DescriptorBufferInfo currentInfo = ref _uniformBuffers[index]; @@ -400,11 +403,11 @@ namespace Ryujinx.Graphics.Vulkan _uniformSet[0] = true; } - uniformBuffer[0] = new DescriptorBufferInfo() + uniformBuffer[0] = new DescriptorBufferInfo { Offset = 0, Range = (ulong)SupportBuffer.RequiredSize, - Buffer = _cachedSupportBuffer + Buffer = _cachedSupportBuffer, }; dsc.UpdateBuffers(0, 0, uniformBuffer, DescriptorType.UniformBuffer); @@ -474,7 +477,7 @@ namespace Ryujinx.Graphics.Vulkan } } - dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler); + dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler); } else { @@ -485,7 +488,7 @@ namespace Ryujinx.Graphics.Vulkan bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default; } - dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer); + dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer); } } else if (setIndex == PipelineBase.ImageSetIndex) @@ -499,7 +502,7 @@ namespace Ryujinx.Graphics.Vulkan images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default; } - dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage); + dsc.UpdateImages(0, binding, images[..count], DescriptorType.StorageImage); } else { @@ -510,7 +513,7 @@ namespace Ryujinx.Graphics.Vulkan bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default; } - dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer); + dsc.UpdateBufferImages(0, binding, bufferImages[..count], DescriptorType.StorageTexelBuffer); } } } @@ -540,7 +543,7 @@ namespace Ryujinx.Graphics.Vulkan DstBinding = (uint)baseBinding, DescriptorType = type, DescriptorCount = (uint)bufferInfo.Length, - PBufferInfo = pBufferInfo + PBufferInfo = pBufferInfo, }; _gd.PushDescriptorApi.CmdPushDescriptorSet(cbs.CommandBuffer, pbp, _program.PipelineLayout, 0, 1, &writeDescriptorSet); @@ -554,11 +557,11 @@ namespace Ryujinx.Graphics.Vulkan { Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1]; - uniformBuffer[0] = new DescriptorBufferInfo() + uniformBuffer[0] = new DescriptorBufferInfo { Offset = 0, Range = (ulong)SupportBuffer.RequiredSize, - Buffer = _gd.BufferManager.GetBuffer(cbs.CommandBuffer, _pipeline.SupportBufferUpdater.Handle, false).Get(cbs, 0, SupportBuffer.RequiredSize).Value + Buffer = _gd.BufferManager.GetBuffer(cbs.CommandBuffer, _pipeline.SupportBufferUpdater.Handle, false).Get(cbs, 0, SupportBuffer.RequiredSize).Value, }; _uniformSet[0] = true; @@ -620,7 +623,7 @@ namespace Ryujinx.Graphics.Vulkan Array.Clear(_storageSet); } - private void SwapBuffer(Auto<DisposableBuffer>[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + private static void SwapBuffer(Auto<DisposableBuffer>[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) { for (int i = 0; i < list.Length; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs b/src/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs index 0f474f970..ba52c2575 100644 --- a/src/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs @@ -1,5 +1,6 @@ using Silk.NET.Vulkan; using System; +using Buffer = Silk.NET.Vulkan.Buffer; namespace Ryujinx.Graphics.Vulkan { @@ -8,9 +9,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly Vk _api; private readonly Device _device; - public Silk.NET.Vulkan.Buffer Value { get; } + public Buffer Value { get; } - public DisposableBuffer(Vk api, Device device, Silk.NET.Vulkan.Buffer buffer) + public DisposableBuffer(Vk api, Device device, Buffer buffer) { _api = api; _device = device; diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index 7317b567a..89a43b12c 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -5,10 +5,12 @@ using Ryujinx.Graphics.Shader.Translation; using Silk.NET.Vulkan; using System; using Extent2D = Ryujinx.Graphics.GAL.Extents2D; +using Format = Silk.NET.Vulkan.Format; +using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; namespace Ryujinx.Graphics.Vulkan.Effects { - internal partial class FsrScalingFilter : IScalingFilter + internal class FsrScalingFilter : IScalingFilter { private readonly VulkanRenderer _renderer; private PipelineHelperShader _pipeline; @@ -66,16 +68,16 @@ namespace Ryujinx.Graphics.Vulkan.Effects .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); - _sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + _sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); _scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv) + new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv), }, scalingResourceLayout); _sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(sharpeningShader, ShaderStage.Compute, TargetLanguage.Spirv) + new ShaderSource(sharpeningShader, ShaderStage.Compute, TargetLanguage.Spirv), }, sharpeningResourceLayout); } @@ -83,7 +85,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects TextureView view, CommandBufferScoped cbs, Auto<DisposableImageView> destinationTexture, - Silk.NET.Vulkan.Format format, + Format format, int width, int height, Extent2D source, @@ -136,14 +138,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects destination.Y1, destination.Y2, scaleX, - scaleY + scaleY, }; int rangeSize = dimensionsBuffer.Length * sizeof(float); var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); _renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer); - ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)}; + ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f) }; var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float)); _renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer); @@ -172,4 +174,4 @@ namespace Ryujinx.Graphics.Vulkan.Effects _renderer.BufferManager.Delete(sharpeningBufferHandle); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index 3c3516bbf..2dd991802 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -4,16 +4,17 @@ using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; using Silk.NET.Vulkan; using System; +using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; namespace Ryujinx.Graphics.Vulkan.Effects { - internal partial class FxaaPostProcessingEffect : IPostProcessingEffect + internal class FxaaPostProcessingEffect : IPostProcessingEffect { private readonly VulkanRenderer _renderer; private ISampler _samplerLinear; private ShaderCollection _shaderProgram; - private PipelineHelperShader _pipeline; + private readonly PipelineHelperShader _pipeline; private TextureView _texture; public FxaaPostProcessingEffect(VulkanRenderer renderer, Device device) @@ -43,11 +44,11 @@ namespace Ryujinx.Graphics.Vulkan.Effects .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); - _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); _shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(shader, ShaderStage.Compute, TargetLanguage.Spirv) + new ShaderSource(shader, ShaderStage.Compute, TargetLanguage.Spirv), }, resourceLayout); } @@ -86,4 +87,4 @@ namespace Ryujinx.Graphics.Vulkan.Effects return _texture; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs index d36cf01d4..d13641c66 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Vulkan.Effects const int LocalGroupSize = 64; TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs index 54f809d71..50fc3710c 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Graphics.Vulkan.Effects Extent2D source, Extent2D destination); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs index a5f060f1b..0383ab237 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs @@ -12,4 +12,4 @@ namespace Ryujinx.Graphics.Vulkan.Effects public float Width; public float Height; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index f6de3ac2e..40ab245ee 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -4,10 +4,12 @@ using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; using Silk.NET.Vulkan; using System; +using Format = Ryujinx.Graphics.GAL.Format; +using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; namespace Ryujinx.Graphics.Vulkan.Effects { - internal partial class SmaaPostProcessingEffect : IPostProcessingEffect + internal class SmaaPostProcessingEffect : IPostProcessingEffect { public const int AreaWidth = 160; public const int AreaHeight = 560; @@ -63,7 +65,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects _searchTexture?.Dispose(); } - private unsafe void RecreateShaders(int width, int height) + private void RecreateShaders(int width, int height) { _recreatePipelines = false; @@ -94,9 +96,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); - _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); - _specConstants = new SmaaConstants() + _specConstants = new SmaaConstants { Width = width, Height = height, @@ -116,17 +118,17 @@ namespace Ryujinx.Graphics.Vulkan.Effects _edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv) + new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv), }, edgeResourceLayout, new[] { specInfo }); _blendProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv) + new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv), }, blendResourceLayout, new[] { specInfo }); _neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv) + new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv), }, neighbourResourceLayout, new[] { specInfo }); } @@ -148,7 +150,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects 1, 1, 1, - GAL.Format.R8G8Unorm, + Format.R8G8Unorm, DepthStencilMode.Depth, Target.Texture2D, SwizzleComponent.Red, @@ -164,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects 1, 1, 1, - GAL.Format.R8Unorm, + Format.R8Unorm, DepthStencilMode.Depth, Target.Texture2D, SwizzleComponent.Red, @@ -264,4 +266,4 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 55868ee35..9323fcf97 100644 --- a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -3,6 +3,14 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; +using BlendFactor = Silk.NET.Vulkan.BlendFactor; +using BlendOp = Silk.NET.Vulkan.BlendOp; +using CompareOp = Silk.NET.Vulkan.CompareOp; +using Format = Ryujinx.Graphics.GAL.Format; +using FrontFace = Silk.NET.Vulkan.FrontFace; +using IndexType = Silk.NET.Vulkan.IndexType; +using PrimitiveTopology = Silk.NET.Vulkan.PrimitiveTopology; +using StencilOp = Silk.NET.Vulkan.StencilOp; namespace Ryujinx.Graphics.Vulkan { @@ -18,7 +26,7 @@ namespace Ryujinx.Graphics.Vulkan ShaderStage.TessellationEvaluation => ShaderStageFlags.TessellationEvaluationBit, ShaderStage.Fragment => ShaderStageFlags.FragmentBit, ShaderStage.Compute => ShaderStageFlags.ComputeBit, - _ => LogInvalidAndReturn(stage, nameof(ShaderStage), (ShaderStageFlags)0) + _ => LogInvalidAndReturn(stage, nameof(ShaderStage), (ShaderStageFlags)0), }; } @@ -32,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan ShaderStage.TessellationEvaluation => PipelineStageFlags.TessellationEvaluationShaderBit, ShaderStage.Fragment => PipelineStageFlags.FragmentShaderBit, ShaderStage.Compute => PipelineStageFlags.ComputeShaderBit, - _ => LogInvalidAndReturn(stage, nameof(ShaderStage), (PipelineStageFlags)0) + _ => LogInvalidAndReturn(stage, nameof(ShaderStage), (PipelineStageFlags)0), }; } @@ -82,7 +90,7 @@ namespace Ryujinx.Graphics.Vulkan ResourceType.Image => DescriptorType.StorageImage, ResourceType.BufferTexture => DescriptorType.UniformTexelBuffer, ResourceType.BufferImage => DescriptorType.StorageTexelBuffer, - _ => throw new ArgumentException($"Invalid resource type \"{type}\".") + _ => throw new ArgumentException($"Invalid resource type \"{type}\"."), }; } @@ -98,128 +106,128 @@ namespace Ryujinx.Graphics.Vulkan AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder, AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat, AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge, - _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp. + _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge), // TODO: Should be clamp. }; } - public static Silk.NET.Vulkan.BlendFactor Convert(this GAL.BlendFactor factor) + public static BlendFactor Convert(this GAL.BlendFactor factor) { return factor switch { - GAL.BlendFactor.Zero or GAL.BlendFactor.ZeroGl => Silk.NET.Vulkan.BlendFactor.Zero, - GAL.BlendFactor.One or GAL.BlendFactor.OneGl => Silk.NET.Vulkan.BlendFactor.One, - GAL.BlendFactor.SrcColor or GAL.BlendFactor.SrcColorGl => Silk.NET.Vulkan.BlendFactor.SrcColor, - GAL.BlendFactor.OneMinusSrcColor or GAL.BlendFactor.OneMinusSrcColorGl => Silk.NET.Vulkan.BlendFactor.OneMinusSrcColor, - GAL.BlendFactor.SrcAlpha or GAL.BlendFactor.SrcAlphaGl => Silk.NET.Vulkan.BlendFactor.SrcAlpha, - GAL.BlendFactor.OneMinusSrcAlpha or GAL.BlendFactor.OneMinusSrcAlphaGl => Silk.NET.Vulkan.BlendFactor.OneMinusSrcAlpha, - GAL.BlendFactor.DstAlpha or GAL.BlendFactor.DstAlphaGl => Silk.NET.Vulkan.BlendFactor.DstAlpha, - GAL.BlendFactor.OneMinusDstAlpha or GAL.BlendFactor.OneMinusDstAlphaGl => Silk.NET.Vulkan.BlendFactor.OneMinusDstAlpha, - GAL.BlendFactor.DstColor or GAL.BlendFactor.DstColorGl => Silk.NET.Vulkan.BlendFactor.DstColor, - GAL.BlendFactor.OneMinusDstColor or GAL.BlendFactor.OneMinusDstColorGl => Silk.NET.Vulkan.BlendFactor.OneMinusDstColor, - GAL.BlendFactor.SrcAlphaSaturate or GAL.BlendFactor.SrcAlphaSaturateGl => Silk.NET.Vulkan.BlendFactor.SrcAlphaSaturate, - GAL.BlendFactor.Src1Color or GAL.BlendFactor.Src1ColorGl => Silk.NET.Vulkan.BlendFactor.Src1Color, - GAL.BlendFactor.OneMinusSrc1Color or GAL.BlendFactor.OneMinusSrc1ColorGl => Silk.NET.Vulkan.BlendFactor.OneMinusSrc1Color, - GAL.BlendFactor.Src1Alpha or GAL.BlendFactor.Src1AlphaGl => Silk.NET.Vulkan.BlendFactor.Src1Alpha, - GAL.BlendFactor.OneMinusSrc1Alpha or GAL.BlendFactor.OneMinusSrc1AlphaGl => Silk.NET.Vulkan.BlendFactor.OneMinusSrc1Alpha, - GAL.BlendFactor.ConstantColor => Silk.NET.Vulkan.BlendFactor.ConstantColor, - GAL.BlendFactor.OneMinusConstantColor => Silk.NET.Vulkan.BlendFactor.OneMinusConstantColor, - GAL.BlendFactor.ConstantAlpha => Silk.NET.Vulkan.BlendFactor.ConstantAlpha, - GAL.BlendFactor.OneMinusConstantAlpha => Silk.NET.Vulkan.BlendFactor.OneMinusConstantAlpha, - _ => LogInvalidAndReturn(factor, nameof(GAL.BlendFactor), Silk.NET.Vulkan.BlendFactor.Zero) + GAL.BlendFactor.Zero or GAL.BlendFactor.ZeroGl => BlendFactor.Zero, + GAL.BlendFactor.One or GAL.BlendFactor.OneGl => BlendFactor.One, + GAL.BlendFactor.SrcColor or GAL.BlendFactor.SrcColorGl => BlendFactor.SrcColor, + GAL.BlendFactor.OneMinusSrcColor or GAL.BlendFactor.OneMinusSrcColorGl => BlendFactor.OneMinusSrcColor, + GAL.BlendFactor.SrcAlpha or GAL.BlendFactor.SrcAlphaGl => BlendFactor.SrcAlpha, + GAL.BlendFactor.OneMinusSrcAlpha or GAL.BlendFactor.OneMinusSrcAlphaGl => BlendFactor.OneMinusSrcAlpha, + GAL.BlendFactor.DstAlpha or GAL.BlendFactor.DstAlphaGl => BlendFactor.DstAlpha, + GAL.BlendFactor.OneMinusDstAlpha or GAL.BlendFactor.OneMinusDstAlphaGl => BlendFactor.OneMinusDstAlpha, + GAL.BlendFactor.DstColor or GAL.BlendFactor.DstColorGl => BlendFactor.DstColor, + GAL.BlendFactor.OneMinusDstColor or GAL.BlendFactor.OneMinusDstColorGl => BlendFactor.OneMinusDstColor, + GAL.BlendFactor.SrcAlphaSaturate or GAL.BlendFactor.SrcAlphaSaturateGl => BlendFactor.SrcAlphaSaturate, + GAL.BlendFactor.Src1Color or GAL.BlendFactor.Src1ColorGl => BlendFactor.Src1Color, + GAL.BlendFactor.OneMinusSrc1Color or GAL.BlendFactor.OneMinusSrc1ColorGl => BlendFactor.OneMinusSrc1Color, + GAL.BlendFactor.Src1Alpha or GAL.BlendFactor.Src1AlphaGl => BlendFactor.Src1Alpha, + GAL.BlendFactor.OneMinusSrc1Alpha or GAL.BlendFactor.OneMinusSrc1AlphaGl => BlendFactor.OneMinusSrc1Alpha, + GAL.BlendFactor.ConstantColor => BlendFactor.ConstantColor, + GAL.BlendFactor.OneMinusConstantColor => BlendFactor.OneMinusConstantColor, + GAL.BlendFactor.ConstantAlpha => BlendFactor.ConstantAlpha, + GAL.BlendFactor.OneMinusConstantAlpha => BlendFactor.OneMinusConstantAlpha, + _ => LogInvalidAndReturn(factor, nameof(GAL.BlendFactor), BlendFactor.Zero), }; } - public static Silk.NET.Vulkan.BlendOp Convert(this GAL.AdvancedBlendOp op) + public static BlendOp Convert(this AdvancedBlendOp op) { return op switch { - GAL.AdvancedBlendOp.Zero => Silk.NET.Vulkan.BlendOp.ZeroExt, - GAL.AdvancedBlendOp.Src => Silk.NET.Vulkan.BlendOp.SrcExt, - GAL.AdvancedBlendOp.Dst => Silk.NET.Vulkan.BlendOp.DstExt, - GAL.AdvancedBlendOp.SrcOver => Silk.NET.Vulkan.BlendOp.SrcOverExt, - GAL.AdvancedBlendOp.DstOver => Silk.NET.Vulkan.BlendOp.DstOverExt, - GAL.AdvancedBlendOp.SrcIn => Silk.NET.Vulkan.BlendOp.SrcInExt, - GAL.AdvancedBlendOp.DstIn => Silk.NET.Vulkan.BlendOp.DstInExt, - GAL.AdvancedBlendOp.SrcOut => Silk.NET.Vulkan.BlendOp.SrcOutExt, - GAL.AdvancedBlendOp.DstOut => Silk.NET.Vulkan.BlendOp.DstOutExt, - GAL.AdvancedBlendOp.SrcAtop => Silk.NET.Vulkan.BlendOp.SrcAtopExt, - GAL.AdvancedBlendOp.DstAtop => Silk.NET.Vulkan.BlendOp.DstAtopExt, - GAL.AdvancedBlendOp.Xor => Silk.NET.Vulkan.BlendOp.XorExt, - GAL.AdvancedBlendOp.Plus => Silk.NET.Vulkan.BlendOp.PlusExt, - GAL.AdvancedBlendOp.PlusClamped => Silk.NET.Vulkan.BlendOp.PlusClampedExt, - GAL.AdvancedBlendOp.PlusClampedAlpha => Silk.NET.Vulkan.BlendOp.PlusClampedAlphaExt, - GAL.AdvancedBlendOp.PlusDarker => Silk.NET.Vulkan.BlendOp.PlusDarkerExt, - GAL.AdvancedBlendOp.Multiply => Silk.NET.Vulkan.BlendOp.MultiplyExt, - GAL.AdvancedBlendOp.Screen => Silk.NET.Vulkan.BlendOp.ScreenExt, - GAL.AdvancedBlendOp.Overlay => Silk.NET.Vulkan.BlendOp.OverlayExt, - GAL.AdvancedBlendOp.Darken => Silk.NET.Vulkan.BlendOp.DarkenExt, - GAL.AdvancedBlendOp.Lighten => Silk.NET.Vulkan.BlendOp.LightenExt, - GAL.AdvancedBlendOp.ColorDodge => Silk.NET.Vulkan.BlendOp.ColordodgeExt, - GAL.AdvancedBlendOp.ColorBurn => Silk.NET.Vulkan.BlendOp.ColorburnExt, - GAL.AdvancedBlendOp.HardLight => Silk.NET.Vulkan.BlendOp.HardlightExt, - GAL.AdvancedBlendOp.SoftLight => Silk.NET.Vulkan.BlendOp.SoftlightExt, - GAL.AdvancedBlendOp.Difference => Silk.NET.Vulkan.BlendOp.DifferenceExt, - GAL.AdvancedBlendOp.Minus => Silk.NET.Vulkan.BlendOp.MinusExt, - GAL.AdvancedBlendOp.MinusClamped => Silk.NET.Vulkan.BlendOp.MinusClampedExt, - GAL.AdvancedBlendOp.Exclusion => Silk.NET.Vulkan.BlendOp.ExclusionExt, - GAL.AdvancedBlendOp.Contrast => Silk.NET.Vulkan.BlendOp.ContrastExt, - GAL.AdvancedBlendOp.Invert => Silk.NET.Vulkan.BlendOp.InvertExt, - GAL.AdvancedBlendOp.InvertRGB => Silk.NET.Vulkan.BlendOp.InvertRgbExt, - GAL.AdvancedBlendOp.InvertOvg => Silk.NET.Vulkan.BlendOp.InvertOvgExt, - GAL.AdvancedBlendOp.LinearDodge => Silk.NET.Vulkan.BlendOp.LineardodgeExt, - GAL.AdvancedBlendOp.LinearBurn => Silk.NET.Vulkan.BlendOp.LinearburnExt, - GAL.AdvancedBlendOp.VividLight => Silk.NET.Vulkan.BlendOp.VividlightExt, - GAL.AdvancedBlendOp.LinearLight => Silk.NET.Vulkan.BlendOp.LinearlightExt, - GAL.AdvancedBlendOp.PinLight => Silk.NET.Vulkan.BlendOp.PinlightExt, - GAL.AdvancedBlendOp.HardMix => Silk.NET.Vulkan.BlendOp.HardmixExt, - GAL.AdvancedBlendOp.Red => Silk.NET.Vulkan.BlendOp.RedExt, - GAL.AdvancedBlendOp.Green => Silk.NET.Vulkan.BlendOp.GreenExt, - GAL.AdvancedBlendOp.Blue => Silk.NET.Vulkan.BlendOp.BlueExt, - GAL.AdvancedBlendOp.HslHue => Silk.NET.Vulkan.BlendOp.HslHueExt, - GAL.AdvancedBlendOp.HslSaturation => Silk.NET.Vulkan.BlendOp.HslSaturationExt, - GAL.AdvancedBlendOp.HslColor => Silk.NET.Vulkan.BlendOp.HslColorExt, - GAL.AdvancedBlendOp.HslLuminosity => Silk.NET.Vulkan.BlendOp.HslLuminosityExt, - _ => LogInvalidAndReturn(op, nameof(GAL.AdvancedBlendOp), Silk.NET.Vulkan.BlendOp.Add) + AdvancedBlendOp.Zero => BlendOp.ZeroExt, + AdvancedBlendOp.Src => BlendOp.SrcExt, + AdvancedBlendOp.Dst => BlendOp.DstExt, + AdvancedBlendOp.SrcOver => BlendOp.SrcOverExt, + AdvancedBlendOp.DstOver => BlendOp.DstOverExt, + AdvancedBlendOp.SrcIn => BlendOp.SrcInExt, + AdvancedBlendOp.DstIn => BlendOp.DstInExt, + AdvancedBlendOp.SrcOut => BlendOp.SrcOutExt, + AdvancedBlendOp.DstOut => BlendOp.DstOutExt, + AdvancedBlendOp.SrcAtop => BlendOp.SrcAtopExt, + AdvancedBlendOp.DstAtop => BlendOp.DstAtopExt, + AdvancedBlendOp.Xor => BlendOp.XorExt, + AdvancedBlendOp.Plus => BlendOp.PlusExt, + AdvancedBlendOp.PlusClamped => BlendOp.PlusClampedExt, + AdvancedBlendOp.PlusClampedAlpha => BlendOp.PlusClampedAlphaExt, + AdvancedBlendOp.PlusDarker => BlendOp.PlusDarkerExt, + AdvancedBlendOp.Multiply => BlendOp.MultiplyExt, + AdvancedBlendOp.Screen => BlendOp.ScreenExt, + AdvancedBlendOp.Overlay => BlendOp.OverlayExt, + AdvancedBlendOp.Darken => BlendOp.DarkenExt, + AdvancedBlendOp.Lighten => BlendOp.LightenExt, + AdvancedBlendOp.ColorDodge => BlendOp.ColordodgeExt, + AdvancedBlendOp.ColorBurn => BlendOp.ColorburnExt, + AdvancedBlendOp.HardLight => BlendOp.HardlightExt, + AdvancedBlendOp.SoftLight => BlendOp.SoftlightExt, + AdvancedBlendOp.Difference => BlendOp.DifferenceExt, + AdvancedBlendOp.Minus => BlendOp.MinusExt, + AdvancedBlendOp.MinusClamped => BlendOp.MinusClampedExt, + AdvancedBlendOp.Exclusion => BlendOp.ExclusionExt, + AdvancedBlendOp.Contrast => BlendOp.ContrastExt, + AdvancedBlendOp.Invert => BlendOp.InvertExt, + AdvancedBlendOp.InvertRGB => BlendOp.InvertRgbExt, + AdvancedBlendOp.InvertOvg => BlendOp.InvertOvgExt, + AdvancedBlendOp.LinearDodge => BlendOp.LineardodgeExt, + AdvancedBlendOp.LinearBurn => BlendOp.LinearburnExt, + AdvancedBlendOp.VividLight => BlendOp.VividlightExt, + AdvancedBlendOp.LinearLight => BlendOp.LinearlightExt, + AdvancedBlendOp.PinLight => BlendOp.PinlightExt, + AdvancedBlendOp.HardMix => BlendOp.HardmixExt, + AdvancedBlendOp.Red => BlendOp.RedExt, + AdvancedBlendOp.Green => BlendOp.GreenExt, + AdvancedBlendOp.Blue => BlendOp.BlueExt, + AdvancedBlendOp.HslHue => BlendOp.HslHueExt, + AdvancedBlendOp.HslSaturation => BlendOp.HslSaturationExt, + AdvancedBlendOp.HslColor => BlendOp.HslColorExt, + AdvancedBlendOp.HslLuminosity => BlendOp.HslLuminosityExt, + _ => LogInvalidAndReturn(op, nameof(AdvancedBlendOp), BlendOp.Add), }; } - public static Silk.NET.Vulkan.BlendOp Convert(this GAL.BlendOp op) + public static BlendOp Convert(this GAL.BlendOp op) { return op switch { - GAL.BlendOp.Add or GAL.BlendOp.AddGl => Silk.NET.Vulkan.BlendOp.Add, - GAL.BlendOp.Subtract or GAL.BlendOp.SubtractGl => Silk.NET.Vulkan.BlendOp.Subtract, - GAL.BlendOp.ReverseSubtract or GAL.BlendOp.ReverseSubtractGl => Silk.NET.Vulkan.BlendOp.ReverseSubtract, - GAL.BlendOp.Minimum or GAL.BlendOp.MinimumGl => Silk.NET.Vulkan.BlendOp.Min, - GAL.BlendOp.Maximum or GAL.BlendOp.MaximumGl => Silk.NET.Vulkan.BlendOp.Max, - _ => LogInvalidAndReturn(op, nameof(GAL.BlendOp), Silk.NET.Vulkan.BlendOp.Add) + GAL.BlendOp.Add or GAL.BlendOp.AddGl => BlendOp.Add, + GAL.BlendOp.Subtract or GAL.BlendOp.SubtractGl => BlendOp.Subtract, + GAL.BlendOp.ReverseSubtract or GAL.BlendOp.ReverseSubtractGl => BlendOp.ReverseSubtract, + GAL.BlendOp.Minimum or GAL.BlendOp.MinimumGl => BlendOp.Min, + GAL.BlendOp.Maximum or GAL.BlendOp.MaximumGl => BlendOp.Max, + _ => LogInvalidAndReturn(op, nameof(GAL.BlendOp), BlendOp.Add), }; } - public static Silk.NET.Vulkan.BlendOverlapEXT Convert(this GAL.AdvancedBlendOverlap overlap) + public static BlendOverlapEXT Convert(this AdvancedBlendOverlap overlap) { return overlap switch { - GAL.AdvancedBlendOverlap.Uncorrelated => Silk.NET.Vulkan.BlendOverlapEXT.UncorrelatedExt, - GAL.AdvancedBlendOverlap.Disjoint => Silk.NET.Vulkan.BlendOverlapEXT.DisjointExt, - GAL.AdvancedBlendOverlap.Conjoint => Silk.NET.Vulkan.BlendOverlapEXT.ConjointExt, - _ => LogInvalidAndReturn(overlap, nameof(GAL.AdvancedBlendOverlap), Silk.NET.Vulkan.BlendOverlapEXT.UncorrelatedExt) + AdvancedBlendOverlap.Uncorrelated => BlendOverlapEXT.UncorrelatedExt, + AdvancedBlendOverlap.Disjoint => BlendOverlapEXT.DisjointExt, + AdvancedBlendOverlap.Conjoint => BlendOverlapEXT.ConjointExt, + _ => LogInvalidAndReturn(overlap, nameof(AdvancedBlendOverlap), BlendOverlapEXT.UncorrelatedExt), }; } - public static Silk.NET.Vulkan.CompareOp Convert(this GAL.CompareOp op) + public static CompareOp Convert(this GAL.CompareOp op) { return op switch { - GAL.CompareOp.Never or GAL.CompareOp.NeverGl => Silk.NET.Vulkan.CompareOp.Never, - GAL.CompareOp.Less or GAL.CompareOp.LessGl => Silk.NET.Vulkan.CompareOp.Less, - GAL.CompareOp.Equal or GAL.CompareOp.EqualGl => Silk.NET.Vulkan.CompareOp.Equal, - GAL.CompareOp.LessOrEqual or GAL.CompareOp.LessOrEqualGl => Silk.NET.Vulkan.CompareOp.LessOrEqual, - GAL.CompareOp.Greater or GAL.CompareOp.GreaterGl => Silk.NET.Vulkan.CompareOp.Greater, - GAL.CompareOp.NotEqual or GAL.CompareOp.NotEqualGl => Silk.NET.Vulkan.CompareOp.NotEqual, - GAL.CompareOp.GreaterOrEqual or GAL.CompareOp.GreaterOrEqualGl => Silk.NET.Vulkan.CompareOp.GreaterOrEqual, - GAL.CompareOp.Always or GAL.CompareOp.AlwaysGl => Silk.NET.Vulkan.CompareOp.Always, - _ => LogInvalidAndReturn(op, nameof(GAL.CompareOp), Silk.NET.Vulkan.CompareOp.Never) + GAL.CompareOp.Never or GAL.CompareOp.NeverGl => CompareOp.Never, + GAL.CompareOp.Less or GAL.CompareOp.LessGl => CompareOp.Less, + GAL.CompareOp.Equal or GAL.CompareOp.EqualGl => CompareOp.Equal, + GAL.CompareOp.LessOrEqual or GAL.CompareOp.LessOrEqualGl => CompareOp.LessOrEqual, + GAL.CompareOp.Greater or GAL.CompareOp.GreaterGl => CompareOp.Greater, + GAL.CompareOp.NotEqual or GAL.CompareOp.NotEqualGl => CompareOp.NotEqual, + GAL.CompareOp.GreaterOrEqual or GAL.CompareOp.GreaterOrEqualGl => CompareOp.GreaterOrEqual, + GAL.CompareOp.Always or GAL.CompareOp.AlwaysGl => CompareOp.Always, + _ => LogInvalidAndReturn(op, nameof(GAL.CompareOp), CompareOp.Never), }; } @@ -230,29 +238,29 @@ namespace Ryujinx.Graphics.Vulkan Face.Back => CullModeFlags.BackBit, Face.Front => CullModeFlags.FrontBit, Face.FrontAndBack => CullModeFlags.FrontAndBack, - _ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit) + _ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit), }; } - public static Silk.NET.Vulkan.FrontFace Convert(this GAL.FrontFace frontFace) + public static FrontFace Convert(this GAL.FrontFace frontFace) { // Flipped to account for origin differences. return frontFace switch { - GAL.FrontFace.Clockwise => Silk.NET.Vulkan.FrontFace.CounterClockwise, - GAL.FrontFace.CounterClockwise => Silk.NET.Vulkan.FrontFace.Clockwise, - _ => LogInvalidAndReturn(frontFace, nameof(GAL.FrontFace), Silk.NET.Vulkan.FrontFace.Clockwise) + GAL.FrontFace.Clockwise => FrontFace.CounterClockwise, + GAL.FrontFace.CounterClockwise => FrontFace.Clockwise, + _ => LogInvalidAndReturn(frontFace, nameof(GAL.FrontFace), FrontFace.Clockwise), }; } - public static Silk.NET.Vulkan.IndexType Convert(this GAL.IndexType type) + public static IndexType Convert(this GAL.IndexType type) { return type switch { - GAL.IndexType.UByte => Silk.NET.Vulkan.IndexType.Uint8Ext, - GAL.IndexType.UShort => Silk.NET.Vulkan.IndexType.Uint16, - GAL.IndexType.UInt => Silk.NET.Vulkan.IndexType.Uint32, - _ => LogInvalidAndReturn(type, nameof(GAL.IndexType), Silk.NET.Vulkan.IndexType.Uint16) + GAL.IndexType.UByte => IndexType.Uint8Ext, + GAL.IndexType.UShort => IndexType.Uint16, + GAL.IndexType.UInt => IndexType.Uint32, + _ => LogInvalidAndReturn(type, nameof(GAL.IndexType), IndexType.Uint16), }; } @@ -262,7 +270,7 @@ namespace Ryujinx.Graphics.Vulkan { MagFilter.Nearest => Filter.Nearest, MagFilter.Linear => Filter.Linear, - _ => LogInvalidAndReturn(filter, nameof(MagFilter), Filter.Nearest) + _ => LogInvalidAndReturn(filter, nameof(MagFilter), Filter.Nearest), }; } @@ -276,45 +284,45 @@ namespace Ryujinx.Graphics.Vulkan MinFilter.LinearMipmapNearest => (Filter.Linear, SamplerMipmapMode.Nearest), MinFilter.NearestMipmapLinear => (Filter.Nearest, SamplerMipmapMode.Linear), MinFilter.LinearMipmapLinear => (Filter.Linear, SamplerMipmapMode.Linear), - _ => LogInvalidAndReturn(filter, nameof(MinFilter), (Filter.Nearest, SamplerMipmapMode.Nearest)) + _ => LogInvalidAndReturn(filter, nameof(MinFilter), (Filter.Nearest, SamplerMipmapMode.Nearest)), }; } - public static Silk.NET.Vulkan.PrimitiveTopology Convert(this GAL.PrimitiveTopology topology) + public static PrimitiveTopology Convert(this GAL.PrimitiveTopology topology) { return topology switch { - GAL.PrimitiveTopology.Points => Silk.NET.Vulkan.PrimitiveTopology.PointList, - GAL.PrimitiveTopology.Lines => Silk.NET.Vulkan.PrimitiveTopology.LineList, - GAL.PrimitiveTopology.LineStrip => Silk.NET.Vulkan.PrimitiveTopology.LineStrip, - GAL.PrimitiveTopology.Triangles => Silk.NET.Vulkan.PrimitiveTopology.TriangleList, - GAL.PrimitiveTopology.TriangleStrip => Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip, - GAL.PrimitiveTopology.TriangleFan => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, - GAL.PrimitiveTopology.LinesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.LineListWithAdjacency, - GAL.PrimitiveTopology.LineStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.LineStripWithAdjacency, - GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency, - GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency, - GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList, - GAL.PrimitiveTopology.Polygon => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, + GAL.PrimitiveTopology.Points => PrimitiveTopology.PointList, + GAL.PrimitiveTopology.Lines => PrimitiveTopology.LineList, + GAL.PrimitiveTopology.LineStrip => PrimitiveTopology.LineStrip, + GAL.PrimitiveTopology.Triangles => PrimitiveTopology.TriangleList, + GAL.PrimitiveTopology.TriangleStrip => PrimitiveTopology.TriangleStrip, + GAL.PrimitiveTopology.TriangleFan => PrimitiveTopology.TriangleFan, + GAL.PrimitiveTopology.LinesAdjacency => PrimitiveTopology.LineListWithAdjacency, + GAL.PrimitiveTopology.LineStripAdjacency => PrimitiveTopology.LineStripWithAdjacency, + GAL.PrimitiveTopology.TrianglesAdjacency => PrimitiveTopology.TriangleListWithAdjacency, + GAL.PrimitiveTopology.TriangleStripAdjacency => PrimitiveTopology.TriangleStripWithAdjacency, + GAL.PrimitiveTopology.Patches => PrimitiveTopology.PatchList, + GAL.PrimitiveTopology.Polygon => PrimitiveTopology.TriangleFan, GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."), GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."), - _ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList) + _ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), PrimitiveTopology.TriangleList), }; } - public static Silk.NET.Vulkan.StencilOp Convert(this GAL.StencilOp op) + public static StencilOp Convert(this GAL.StencilOp op) { return op switch { - GAL.StencilOp.Keep or GAL.StencilOp.KeepGl => Silk.NET.Vulkan.StencilOp.Keep, - GAL.StencilOp.Zero or GAL.StencilOp.ZeroGl => Silk.NET.Vulkan.StencilOp.Zero, - GAL.StencilOp.Replace or GAL.StencilOp.ReplaceGl => Silk.NET.Vulkan.StencilOp.Replace, - GAL.StencilOp.IncrementAndClamp or GAL.StencilOp.IncrementAndClampGl => Silk.NET.Vulkan.StencilOp.IncrementAndClamp, - GAL.StencilOp.DecrementAndClamp or GAL.StencilOp.DecrementAndClampGl => Silk.NET.Vulkan.StencilOp.DecrementAndClamp, - GAL.StencilOp.Invert or GAL.StencilOp.InvertGl => Silk.NET.Vulkan.StencilOp.Invert, - GAL.StencilOp.IncrementAndWrap or GAL.StencilOp.IncrementAndWrapGl => Silk.NET.Vulkan.StencilOp.IncrementAndWrap, - GAL.StencilOp.DecrementAndWrap or GAL.StencilOp.DecrementAndWrapGl => Silk.NET.Vulkan.StencilOp.DecrementAndWrap, - _ => LogInvalidAndReturn(op, nameof(GAL.StencilOp), Silk.NET.Vulkan.StencilOp.Keep) + GAL.StencilOp.Keep or GAL.StencilOp.KeepGl => StencilOp.Keep, + GAL.StencilOp.Zero or GAL.StencilOp.ZeroGl => StencilOp.Zero, + GAL.StencilOp.Replace or GAL.StencilOp.ReplaceGl => StencilOp.Replace, + GAL.StencilOp.IncrementAndClamp or GAL.StencilOp.IncrementAndClampGl => StencilOp.IncrementAndClamp, + GAL.StencilOp.DecrementAndClamp or GAL.StencilOp.DecrementAndClampGl => StencilOp.DecrementAndClamp, + GAL.StencilOp.Invert or GAL.StencilOp.InvertGl => StencilOp.Invert, + GAL.StencilOp.IncrementAndWrap or GAL.StencilOp.IncrementAndWrapGl => StencilOp.IncrementAndWrap, + GAL.StencilOp.DecrementAndWrap or GAL.StencilOp.DecrementAndWrapGl => StencilOp.DecrementAndWrap, + _ => LogInvalidAndReturn(op, nameof(GAL.StencilOp), StencilOp.Keep), }; } @@ -328,7 +336,7 @@ namespace Ryujinx.Graphics.Vulkan SwizzleComponent.Green => ComponentSwizzle.G, SwizzleComponent.Blue => ComponentSwizzle.B, SwizzleComponent.Alpha => ComponentSwizzle.A, - _ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), ComponentSwizzle.Zero) + _ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), ComponentSwizzle.Zero), }; } @@ -345,7 +353,7 @@ namespace Ryujinx.Graphics.Vulkan Target.Cubemap or Target.CubemapArray => ImageType.Type2D, Target.Texture3D => ImageType.Type3D, - _ => LogInvalidAndReturn(target, nameof(Target), ImageType.Type2D) + _ => LogInvalidAndReturn(target, nameof(Target), ImageType.Type2D), }; } @@ -360,33 +368,33 @@ namespace Ryujinx.Graphics.Vulkan Target.Texture2DArray => ImageViewType.Type2DArray, Target.Cubemap => ImageViewType.TypeCube, Target.CubemapArray => ImageViewType.TypeCubeArray, - _ => LogInvalidAndReturn(target, nameof(Target), ImageViewType.Type2D) + _ => LogInvalidAndReturn(target, nameof(Target), ImageViewType.Type2D), }; } - public static ImageAspectFlags ConvertAspectFlags(this GAL.Format format) + public static ImageAspectFlags ConvertAspectFlags(this Format format) { return format switch { - GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.DepthBit, - GAL.Format.S8Uint => ImageAspectFlags.StencilBit, - GAL.Format.D24UnormS8Uint or - GAL.Format.D32FloatS8Uint or - GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, - _ => ImageAspectFlags.ColorBit + Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit, + Format.S8Uint => ImageAspectFlags.StencilBit, + Format.D24UnormS8Uint or + Format.D32FloatS8Uint or + Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, + _ => ImageAspectFlags.ColorBit, }; } - public static ImageAspectFlags ConvertAspectFlags(this GAL.Format format, DepthStencilMode depthStencilMode) + public static ImageAspectFlags ConvertAspectFlags(this Format format, DepthStencilMode depthStencilMode) { return format switch { - GAL.Format.D16Unorm or GAL.Format.D32Float => ImageAspectFlags.DepthBit, - GAL.Format.S8Uint => ImageAspectFlags.StencilBit, - GAL.Format.D24UnormS8Uint or - GAL.Format.D32FloatS8Uint or - GAL.Format.S8UintD24Unorm => depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.StencilBit : ImageAspectFlags.DepthBit, - _ => ImageAspectFlags.ColorBit + Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit, + Format.S8Uint => ImageAspectFlags.StencilBit, + Format.D24UnormS8Uint or + Format.D32FloatS8Uint or + Format.S8UintD24Unorm => depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.StencilBit : ImageAspectFlags.DepthBit, + _ => ImageAspectFlags.ColorBit, }; } @@ -410,7 +418,7 @@ namespace Ryujinx.Graphics.Vulkan LogicalOp.OrInverted => LogicOp.OrInverted, LogicalOp.Nand => LogicOp.Nand, LogicalOp.Set => LogicOp.Set, - _ => LogInvalidAndReturn(op, nameof(LogicalOp), LogicOp.Copy) + _ => LogInvalidAndReturn(op, nameof(LogicalOp), LogicOp.Copy), }; } @@ -419,7 +427,7 @@ namespace Ryujinx.Graphics.Vulkan return access switch { BufferAccess.FlushPersistent => BufferAllocationType.HostMapped, - _ => BufferAllocationType.Auto + _ => BufferAllocationType.Auto, }; } diff --git a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs index 39d226983..d57d95ec1 100644 --- a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs @@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Vulkan _api = api; _device = device; - var fenceCreateInfo = new FenceCreateInfo() + var fenceCreateInfo = new FenceCreateInfo { - SType = StructureType.FenceCreateInfo + SType = StructureType.FenceCreateInfo, }; api.CreateFence(device, in fenceCreateInfo, null, out _fence).ThrowOnError(); @@ -70,7 +70,7 @@ namespace Ryujinx.Graphics.Vulkan { Span<Fence> fences = stackalloc Fence[] { - _fence + _fence, }; FenceHelper.WaitAllIndefinitely(_api, _device, fences); @@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan { Span<Fence> fences = stackalloc Fence[] { - _fence + _fence, }; return FenceHelper.AllSignaled(_api, _device, fences); diff --git a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index a2ebc518e..5f7deeb62 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -2,6 +2,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; +using Format = Ryujinx.Graphics.GAL.Format; using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan @@ -19,15 +20,15 @@ namespace Ryujinx.Graphics.Vulkan _api = api; _physicalDevice = physicalDevice; - int totalFormats = Enum.GetNames(typeof(GAL.Format)).Length; + int totalFormats = Enum.GetNames(typeof(Format)).Length; _bufferTable = new FormatFeatureFlags[totalFormats]; _optimalTable = new FormatFeatureFlags[totalFormats]; } - public bool BufferFormatsSupport(FormatFeatureFlags flags, params GAL.Format[] formats) + public bool BufferFormatsSupport(FormatFeatureFlags flags, params Format[] formats) { - foreach (GAL.Format format in formats) + foreach (Format format in formats) { if (!BufferFormatSupports(flags, format)) { @@ -38,9 +39,9 @@ namespace Ryujinx.Graphics.Vulkan return true; } - public bool OptimalFormatsSupport(FormatFeatureFlags flags, params GAL.Format[] formats) + public bool OptimalFormatsSupport(FormatFeatureFlags flags, params Format[] formats) { - foreach (GAL.Format format in formats) + foreach (Format format in formats) { if (!OptimalFormatSupports(flags, format)) { @@ -51,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan return true; } - public bool BufferFormatSupports(FormatFeatureFlags flags, GAL.Format format) + public bool BufferFormatSupports(FormatFeatureFlags flags, Format format) { var formatFeatureFlags = _bufferTable[(int)format]; @@ -72,7 +73,7 @@ namespace Ryujinx.Graphics.Vulkan return (fp.BufferFeatures & flags) == flags; } - public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format) + public bool OptimalFormatSupports(FormatFeatureFlags flags, Format format) { var formatFeatureFlags = _optimalTable[(int)format]; @@ -86,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan return (formatFeatureFlags & flags) == flags; } - public VkFormat ConvertToVkFormat(GAL.Format srcFormat) + public VkFormat ConvertToVkFormat(Format srcFormat) { var format = FormatTable.GetFormat(srcFormat); @@ -115,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan { format = VkFormat.D32SfloatS8Uint; } - else if (srcFormat == GAL.Format.R4G4B4A4Unorm) + else if (srcFormat == Format.R4G4B4A4Unorm) { format = VkFormat.R4G4B4A4UnormPack16; } @@ -128,7 +129,7 @@ namespace Ryujinx.Graphics.Vulkan return format; } - public VkFormat ConvertToVertexVkFormat(GAL.Format srcFormat) + public VkFormat ConvertToVertexVkFormat(Format srcFormat) { var format = FormatTable.GetFormat(srcFormat); @@ -138,13 +139,13 @@ namespace Ryujinx.Graphics.Vulkan // The format is not supported. Can we convert it to an alternative format? switch (srcFormat) { - case GAL.Format.R16G16B16Float: + case Format.R16G16B16Float: format = VkFormat.R16G16B16A16Sfloat; break; - case GAL.Format.R16G16B16Sint: + case Format.R16G16B16Sint: format = VkFormat.R16G16B16A16Sint; break; - case GAL.Format.R16G16B16Uint: + case Format.R16G16B16Uint: format = VkFormat.R16G16B16A16Uint; break; default: @@ -156,16 +157,16 @@ namespace Ryujinx.Graphics.Vulkan return format; } - public static bool IsD24S8(GAL.Format format) + public static bool IsD24S8(Format format) { - return format == GAL.Format.D24UnormS8Uint || format == GAL.Format.S8UintD24Unorm; + return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm; } - private static bool IsRGB16IntFloat(GAL.Format format) + private static bool IsRGB16IntFloat(Format format) { - return format == GAL.Format.R16G16B16Float || - format == GAL.Format.R16G16B16Sint || - format == GAL.Format.R16G16B16Uint; + return format == Format.R16G16B16Float || + format == Format.R16G16B16Sint || + format == Format.R16G16B16Uint; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs index 3d70f6f26..eacfa0dbf 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Vulkan { _table = new VkFormat[Enum.GetNames(typeof(Format)).Length]; +#pragma warning disable IDE0055 // Disable formatting Add(Format.R8Unorm, VkFormat.R8Unorm); Add(Format.R8Snorm, VkFormat.R8SNorm); Add(Format.R8Uint, VkFormat.R8Uint); @@ -157,6 +158,7 @@ namespace Ryujinx.Graphics.Vulkan Add(Format.A1B5G5R5Unorm, VkFormat.R5G5B5A1UnormPack16); Add(Format.B8G8R8A8Unorm, VkFormat.B8G8R8A8Unorm); Add(Format.B8G8R8A8Srgb, VkFormat.B8G8R8A8Srgb); +#pragma warning restore IDE0055 } private static void Add(Format format, VkFormat vkFormat) @@ -175,7 +177,7 @@ namespace Ryujinx.Graphics.Vulkan { Format.R8G8B8A8Srgb => Format.R8G8B8A8Unorm, Format.B8G8R8A8Srgb => Format.B8G8R8A8Unorm, - _ => format + _ => format, }; } @@ -280,121 +282,61 @@ namespace Ryujinx.Graphics.Vulkan public static VkFormat DropLastComponent(VkFormat format) { - switch (format) + return format switch { - case VkFormat.R8G8Unorm: - return VkFormat.R8Unorm; - case VkFormat.R8G8SNorm: - return VkFormat.R8SNorm; - case VkFormat.R8G8Uint: - return VkFormat.R8Uint; - case VkFormat.R8G8Sint: - return VkFormat.R8Sint; - case VkFormat.R8G8Uscaled: - return VkFormat.R8Uscaled; - case VkFormat.R8G8Sscaled: - return VkFormat.R8Sscaled; - - case VkFormat.R8G8B8Unorm: - return VkFormat.R8G8Unorm; - case VkFormat.R8G8B8SNorm: - return VkFormat.R8G8SNorm; - case VkFormat.R8G8B8Uint: - return VkFormat.R8G8Uint; - case VkFormat.R8G8B8Sint: - return VkFormat.R8G8Sint; - case VkFormat.R8G8B8Uscaled: - return VkFormat.R8G8Uscaled; - case VkFormat.R8G8B8Sscaled: - return VkFormat.R8G8Sscaled; - - case VkFormat.R8G8B8A8Unorm: - return VkFormat.R8G8B8Unorm; - case VkFormat.R8G8B8A8SNorm: - return VkFormat.R8G8B8SNorm; - case VkFormat.R8G8B8A8Uint: - return VkFormat.R8G8B8Uint; - case VkFormat.R8G8B8A8Sint: - return VkFormat.R8G8B8Sint; - case VkFormat.R8G8B8A8Srgb: - return VkFormat.R8G8B8Srgb; - case VkFormat.R8G8B8A8Uscaled: - return VkFormat.R8G8B8Uscaled; - case VkFormat.R8G8B8A8Sscaled: - return VkFormat.R8G8B8Sscaled; - case VkFormat.B8G8R8A8Unorm: - return VkFormat.B8G8R8Unorm; - case VkFormat.B8G8R8A8Srgb: - return VkFormat.B8G8R8Srgb; - - case VkFormat.R16G16Sfloat: - return VkFormat.R16Sfloat; - case VkFormat.R16G16Unorm: - return VkFormat.R16Unorm; - case VkFormat.R16G16SNorm: - return VkFormat.R16SNorm; - case VkFormat.R16G16Uint: - return VkFormat.R16Uint; - case VkFormat.R16G16Sint: - return VkFormat.R16Sint; - case VkFormat.R16G16Uscaled: - return VkFormat.R16Uscaled; - case VkFormat.R16G16Sscaled: - return VkFormat.R16Sscaled; - - case VkFormat.R16G16B16Sfloat: - return VkFormat.R16G16Sfloat; - case VkFormat.R16G16B16Unorm: - return VkFormat.R16G16Unorm; - case VkFormat.R16G16B16SNorm: - return VkFormat.R16G16SNorm; - case VkFormat.R16G16B16Uint: - return VkFormat.R16G16Uint; - case VkFormat.R16G16B16Sint: - return VkFormat.R16G16Sint; - case VkFormat.R16G16B16Uscaled: - return VkFormat.R16G16Uscaled; - case VkFormat.R16G16B16Sscaled: - return VkFormat.R16G16Sscaled; - - case VkFormat.R16G16B16A16Sfloat: - return VkFormat.R16G16B16Sfloat; - case VkFormat.R16G16B16A16Unorm: - return VkFormat.R16G16B16Unorm; - case VkFormat.R16G16B16A16SNorm: - return VkFormat.R16G16B16SNorm; - case VkFormat.R16G16B16A16Uint: - return VkFormat.R16G16B16Uint; - case VkFormat.R16G16B16A16Sint: - return VkFormat.R16G16B16Sint; - case VkFormat.R16G16B16A16Uscaled: - return VkFormat.R16G16B16Uscaled; - case VkFormat.R16G16B16A16Sscaled: - return VkFormat.R16G16B16Sscaled; - - case VkFormat.R32G32Sfloat: - return VkFormat.R32Sfloat; - case VkFormat.R32G32Uint: - return VkFormat.R32Uint; - case VkFormat.R32G32Sint: - return VkFormat.R32Sint; - - case VkFormat.R32G32B32Sfloat: - return VkFormat.R32G32Sfloat; - case VkFormat.R32G32B32Uint: - return VkFormat.R32G32Uint; - case VkFormat.R32G32B32Sint: - return VkFormat.R32G32Sint; - - case VkFormat.R32G32B32A32Sfloat: - return VkFormat.R32G32B32Sfloat; - case VkFormat.R32G32B32A32Uint: - return VkFormat.R32G32B32Uint; - case VkFormat.R32G32B32A32Sint: - return VkFormat.R32G32B32Sint; - } - - return format; + VkFormat.R8G8Unorm => VkFormat.R8Unorm, + VkFormat.R8G8SNorm => VkFormat.R8SNorm, + VkFormat.R8G8Uint => VkFormat.R8Uint, + VkFormat.R8G8Sint => VkFormat.R8Sint, + VkFormat.R8G8Uscaled => VkFormat.R8Uscaled, + VkFormat.R8G8Sscaled => VkFormat.R8Sscaled, + VkFormat.R8G8B8Unorm => VkFormat.R8G8Unorm, + VkFormat.R8G8B8SNorm => VkFormat.R8G8SNorm, + VkFormat.R8G8B8Uint => VkFormat.R8G8Uint, + VkFormat.R8G8B8Sint => VkFormat.R8G8Sint, + VkFormat.R8G8B8Uscaled => VkFormat.R8G8Uscaled, + VkFormat.R8G8B8Sscaled => VkFormat.R8G8Sscaled, + VkFormat.R8G8B8A8Unorm => VkFormat.R8G8B8Unorm, + VkFormat.R8G8B8A8SNorm => VkFormat.R8G8B8SNorm, + VkFormat.R8G8B8A8Uint => VkFormat.R8G8B8Uint, + VkFormat.R8G8B8A8Sint => VkFormat.R8G8B8Sint, + VkFormat.R8G8B8A8Srgb => VkFormat.R8G8B8Srgb, + VkFormat.R8G8B8A8Uscaled => VkFormat.R8G8B8Uscaled, + VkFormat.R8G8B8A8Sscaled => VkFormat.R8G8B8Sscaled, + VkFormat.B8G8R8A8Unorm => VkFormat.B8G8R8Unorm, + VkFormat.B8G8R8A8Srgb => VkFormat.B8G8R8Srgb, + VkFormat.R16G16Sfloat => VkFormat.R16Sfloat, + VkFormat.R16G16Unorm => VkFormat.R16Unorm, + VkFormat.R16G16SNorm => VkFormat.R16SNorm, + VkFormat.R16G16Uint => VkFormat.R16Uint, + VkFormat.R16G16Sint => VkFormat.R16Sint, + VkFormat.R16G16Uscaled => VkFormat.R16Uscaled, + VkFormat.R16G16Sscaled => VkFormat.R16Sscaled, + VkFormat.R16G16B16Sfloat => VkFormat.R16G16Sfloat, + VkFormat.R16G16B16Unorm => VkFormat.R16G16Unorm, + VkFormat.R16G16B16SNorm => VkFormat.R16G16SNorm, + VkFormat.R16G16B16Uint => VkFormat.R16G16Uint, + VkFormat.R16G16B16Sint => VkFormat.R16G16Sint, + VkFormat.R16G16B16Uscaled => VkFormat.R16G16Uscaled, + VkFormat.R16G16B16Sscaled => VkFormat.R16G16Sscaled, + VkFormat.R16G16B16A16Sfloat => VkFormat.R16G16B16Sfloat, + VkFormat.R16G16B16A16Unorm => VkFormat.R16G16B16Unorm, + VkFormat.R16G16B16A16SNorm => VkFormat.R16G16B16SNorm, + VkFormat.R16G16B16A16Uint => VkFormat.R16G16B16Uint, + VkFormat.R16G16B16A16Sint => VkFormat.R16G16B16Sint, + VkFormat.R16G16B16A16Uscaled => VkFormat.R16G16B16Uscaled, + VkFormat.R16G16B16A16Sscaled => VkFormat.R16G16B16Sscaled, + VkFormat.R32G32Sfloat => VkFormat.R32Sfloat, + VkFormat.R32G32Uint => VkFormat.R32Uint, + VkFormat.R32G32Sint => VkFormat.R32Sint, + VkFormat.R32G32B32Sfloat => VkFormat.R32G32Sfloat, + VkFormat.R32G32B32Uint => VkFormat.R32G32Uint, + VkFormat.R32G32B32Sint => VkFormat.R32G32Sint, + VkFormat.R32G32B32A32Sfloat => VkFormat.R32G32B32Sfloat, + VkFormat.R32G32B32A32Uint => VkFormat.R32G32B32Uint, + VkFormat.R32G32B32A32Sint => VkFormat.R32G32B32Sint, + _ => format, + }; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 0437a402c..7b40e96b2 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly Auto<DisposableImageView>[] _attachments; private readonly TextureView[] _colors; private readonly TextureView _depthStencil; - private uint _validColorAttachments; + private readonly uint _validColorAttachments; public uint Width { get; } public uint Height { get; } @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan public uint AttachmentIntegerFormatMask { get; } public int AttachmentsCount { get; } - public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1; + public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1; public bool HasDepthStencil { get; } public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); @@ -158,7 +158,8 @@ namespace Ryujinx.Graphics.Vulkan { return ComponentType.SignedInteger; } - else if (format.IsUint()) + + if (format.IsUint()) { return ComponentType.UnsignedInteger; } @@ -196,7 +197,7 @@ namespace Ryujinx.Graphics.Vulkan attachments[i] = _attachments[i].Get(cbs).Value; } - var framebufferCreateInfo = new FramebufferCreateInfo() + var framebufferCreateInfo = new FramebufferCreateInfo { SType = StructureType.FramebufferCreateInfo, RenderPass = renderPass.Get(cbs).Value, @@ -204,7 +205,7 @@ namespace Ryujinx.Graphics.Vulkan PAttachments = attachments, Width = Width, Height = Height, - Layers = Layers + Layers = Layers, }; api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 393bcf1a2..11d9c4fb4 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Vulkan NoTriangleFans = 1, NoPointMode = 1 << 1, No3DImageView = 1 << 2, - NoLodBias = 1 << 3 + NoLodBias = 1 << 3, } readonly struct HardwareCapabilities diff --git a/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs b/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs index e4ad39587..2efe81a60 100644 --- a/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs +++ b/src/Ryujinx.Graphics.Vulkan/HashTableSlim.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan bool Equals(ref T other); } - class HashTableSlim<K, V> where K : IRefEquatable<K> + class HashTableSlim<TKey, TValue> where TKey : IRefEquatable<TKey> { private const int TotalBuckets = 16; // Must be power of 2 private const int TotalBucketsMask = TotalBuckets - 1; @@ -16,13 +16,13 @@ namespace Ryujinx.Graphics.Vulkan private struct Entry { public int Hash; - public K Key; - public V Value; + public TKey Key; + public TValue Value; } private readonly Entry[][] _hashTable = new Entry[TotalBuckets][]; - public IEnumerable<K> Keys + public IEnumerable<TKey> Keys { get { @@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public IEnumerable<V> Values + public IEnumerable<TValue> Values { get { @@ -56,13 +56,13 @@ namespace Ryujinx.Graphics.Vulkan } } - public void Add(ref K key, V value) + public void Add(ref TKey key, TValue value) { - var entry = new Entry() + var entry = new Entry { Hash = key.GetHashCode(), Key = key, - Value = value + Value = value, }; int hashCode = key.GetHashCode(); @@ -79,14 +79,14 @@ namespace Ryujinx.Graphics.Vulkan } else { - _hashTable[bucketIndex] = new Entry[] + _hashTable[bucketIndex] = new[] { - entry + entry, }; } } - public bool TryGetValue(ref K key, out V value) + public bool TryGetValue(ref TKey key, out TValue value) { int hashCode = key.GetHashCode(); diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 43214be4d..648afcd6a 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -6,6 +6,12 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Numerics; +using CompareOp = Ryujinx.Graphics.GAL.CompareOp; +using Format = Ryujinx.Graphics.GAL.Format; +using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology; +using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; +using StencilOp = Ryujinx.Graphics.GAL.StencilOp; +using Viewport = Ryujinx.Graphics.GAL.Viewport; using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan @@ -14,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan { Float, SignedInteger, - UnsignedInteger + UnsignedInteger, } class HelperShader : IDisposable @@ -52,8 +58,8 @@ namespace Ryujinx.Graphics.Vulkan _pipeline = new PipelineHelperShader(gd, device); _pipeline.Initialize(); - _samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); - _samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); + _samplerLinear = gd.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + _samplerNearest = gd.CreateSampler(SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); var blitResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1) @@ -416,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + Span<Viewport> viewports = stackalloc Viewport[1]; var rect = new Rectangle<float>( MathF.Min(dstRegion.X1, dstRegion.X2), @@ -424,7 +430,7 @@ namespace Ryujinx.Graphics.Vulkan MathF.Abs(dstRegion.X2 - dstRegion.X1), MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - viewports[0] = new GAL.Viewport( + viewports[0] = new Viewport( rect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, @@ -440,7 +446,7 @@ namespace Ryujinx.Graphics.Vulkan if (dstIsDepthOrStencil) { _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); - _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } else if (src.Info.Target.IsMultisample()) { @@ -465,12 +471,12 @@ namespace Ryujinx.Graphics.Vulkan } _pipeline.SetViewports(viewports, false); - _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); if (dstIsDepthOrStencil) { - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always)); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); } _pipeline.Finish(gd, cbs); @@ -517,7 +523,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + Span<Viewport> viewports = stackalloc Viewport[1]; var rect = new Rectangle<float>( MathF.Min(dstRegion.X1, dstRegion.X2), @@ -525,7 +531,7 @@ namespace Ryujinx.Graphics.Vulkan MathF.Abs(dstRegion.X2 - dstRegion.X1), MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - viewports[0] = new GAL.Viewport( + viewports[0] = new Viewport( rect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, @@ -541,7 +547,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat); _pipeline.SetScissors(scissors); _pipeline.SetViewports(viewports, false); - _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); var aspectFlags = src.Info.Format.ConvertAspectFlags(); @@ -606,7 +612,7 @@ namespace Ryujinx.Graphics.Vulkan if (isDepth) { _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); - _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } else { @@ -618,7 +624,7 @@ namespace Ryujinx.Graphics.Vulkan if (isDepth) { - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always)); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); } else { @@ -630,17 +636,17 @@ namespace Ryujinx.Graphics.Vulkan { return new StencilTestDescriptor( enabled, - GAL.CompareOp.Always, - GAL.StencilOp.Replace, - GAL.StencilOp.Replace, - GAL.StencilOp.Replace, + CompareOp.Always, + StencilOp.Replace, + StencilOp.Replace, + StencilOp.Replace, 0, 0xff, 0xff, - GAL.CompareOp.Always, - GAL.StencilOp.Replace, - GAL.StencilOp.Replace, - GAL.StencilOp.Replace, + CompareOp.Always, + StencilOp.Replace, + StencilOp.Replace, + StencilOp.Replace, 0, 0xff, 0xff); @@ -667,13 +673,13 @@ namespace Ryujinx.Graphics.Vulkan var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize); - gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor); + gd.BufferManager.SetData(bufferHandle, 0, clearColor); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) }); - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + Span<Viewport> viewports = stackalloc Viewport[1]; - viewports[0] = new GAL.Viewport( + viewports[0] = new Viewport( new Rectangle<float>(0, 0, dstWidth, dstHeight), ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, @@ -703,10 +709,10 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetProgram(program); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); - _pipeline.SetRenderTargetColorMasks(new uint[] { componentMask }); + _pipeline.SetRenderTargetColorMasks(new[] { componentMask }); _pipeline.SetViewports(viewports, false); _pipeline.SetScissors(scissors); - _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); _pipeline.Finish(); @@ -748,7 +754,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + Span<Viewport> viewports = stackalloc Viewport[1]; var rect = new Rectangle<float>( MathF.Min(dstRegion.X1, dstRegion.X2), @@ -756,7 +762,7 @@ namespace Ryujinx.Graphics.Vulkan MathF.Abs(dstRegion.X2 - dstRegion.X1), MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - viewports[0] = new GAL.Viewport( + viewports[0] = new Viewport( rect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, @@ -769,13 +775,13 @@ namespace Ryujinx.Graphics.Vulkan pipeline.SetProgram(_programColorBlit); pipeline.SetViewports(viewports, false); - pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); pipeline.Draw(4, 1, 0, 0); gd.BufferManager.Delete(bufferHandle); } - public unsafe void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size) + public void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size) { ChangeStride(gd, cbs, src, dst, srcOffset, size, 1, 2); } @@ -1093,11 +1099,11 @@ namespace Ryujinx.Graphics.Vulkan { // We can't use compute for this case because compute can't modify depth textures. - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + Span<Viewport> viewports = stackalloc Viewport[1]; var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height); - viewports[0] = new GAL.Viewport( + viewports[0] = new Viewport( rect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, @@ -1112,7 +1118,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetScissors(scissors); _pipeline.SetViewports(viewports, false); - _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); for (int z = 0; z < depth; z++) { @@ -1120,7 +1126,7 @@ namespace Ryujinx.Graphics.Vulkan var dstView = Create2DLayerView(dst, dstLayer + z, 0); _pipeline.SetRenderTarget( - ((TextureView)dstView).GetImageViewForAttachment(), + dstView.GetImageViewForAttachment(), (uint)dst.Width, (uint)dst.Height, true, @@ -1225,11 +1231,11 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetCommandBuffer(cbs); - Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + Span<Viewport> viewports = stackalloc Viewport[1]; var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height); - viewports[0] = new GAL.Viewport( + viewports[0] = new Viewport( rect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, @@ -1245,7 +1251,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); _pipeline.SetScissors(scissors); _pipeline.SetViewports(viewports, false); - _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); @@ -1257,7 +1263,7 @@ namespace Ryujinx.Graphics.Vulkan var dstView = Create2DLayerView(dst, dstLayer + z, 0); _pipeline.SetRenderTarget( - ((TextureView)dstView).GetImageViewForAttachment(), + dstView.GetImageViewForAttachment(), (uint)dst.Width, (uint)dst.Height, (uint)samples, @@ -1291,7 +1297,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null); _pipeline.SetRenderTarget( - ((TextureView)dstView).GetView(format).GetImageViewForAttachment(), + dstView.GetView(format).GetImageViewForAttachment(), (uint)dst.Width, (uint)dst.Height, (uint)samples, @@ -1365,7 +1371,7 @@ namespace Ryujinx.Graphics.Vulkan if (isDepth) { _pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs); - _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } else { @@ -1377,7 +1383,7 @@ namespace Ryujinx.Graphics.Vulkan if (isDepth) { - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always)); + _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); } else { @@ -1420,7 +1426,7 @@ namespace Ryujinx.Graphics.Vulkan return (samplesInXLog2, samplesInYLog2); } - private static TextureView Create2DLayerView(TextureView from, int layer, int level, GAL.Format? format = null) + private static TextureView Create2DLayerView(TextureView from, int layer, int level, Format? format = null) { if (from.Info.Target == Target.Texture2D && level == 0 && (format == null || format.Value == from.Info.Format)) { @@ -1431,7 +1437,7 @@ namespace Ryujinx.Graphics.Vulkan { Target.Texture1DArray => Target.Texture1D, Target.Texture2DMultisampleArray => Target.Texture2DMultisample, - _ => Target.Texture2D + _ => Target.Texture2D, }; var info = new TextureCreateInfo( @@ -1454,55 +1460,55 @@ namespace Ryujinx.Graphics.Vulkan return from.CreateViewImpl(info, layer, level); } - private static GAL.Format GetFormat(int bytesPerPixel) + private static Format GetFormat(int bytesPerPixel) { return bytesPerPixel switch { - 1 => GAL.Format.R8Uint, - 2 => GAL.Format.R16Uint, - 4 => GAL.Format.R32Uint, - 8 => GAL.Format.R32G32Uint, - 16 => GAL.Format.R32G32B32A32Uint, - _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.") + 1 => Format.R8Uint, + 2 => Format.R16Uint, + 4 => Format.R32Uint, + 8 => Format.R32G32Uint, + 16 => Format.R32G32B32A32Uint, + _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}."), }; } - private static GAL.Format GetFormat(int componentSize, int componentsCount) + private static Format GetFormat(int componentSize, int componentsCount) { if (componentSize == 1) { return componentsCount switch { - 1 => GAL.Format.R8Uint, - 2 => GAL.Format.R8G8Uint, - 4 => GAL.Format.R8G8B8A8Uint, - _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + 1 => Format.R8Uint, + 2 => Format.R8G8Uint, + 4 => Format.R8G8B8A8Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}."), }; } - else if (componentSize == 2) + + if (componentSize == 2) { return componentsCount switch { - 1 => GAL.Format.R16Uint, - 2 => GAL.Format.R16G16Uint, - 4 => GAL.Format.R16G16B16A16Uint, - _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + 1 => Format.R16Uint, + 2 => Format.R16G16Uint, + 4 => Format.R16G16B16A16Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}."), }; } - else if (componentSize == 4) + + if (componentSize == 4) { return componentsCount switch { - 1 => GAL.Format.R32Uint, - 2 => GAL.Format.R32G32Uint, - 4 => GAL.Format.R32G32B32A32Uint, - _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + 1 => Format.R32Uint, + 2 => Format.R32G32Uint, + 4 => Format.R32G32B32A32Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}."), }; } - else - { - throw new ArgumentException($"Invalid component size {componentSize}."); - } + + throw new ArgumentException($"Invalid component size {componentSize}."); } public void ConvertIndexBufferIndirect( @@ -1524,7 +1530,7 @@ namespace Ryujinx.Graphics.Vulkan { // TODO: Support conversion with primitive restart enabled. - BufferRange drawCountBufferAligned = new BufferRange( + BufferRange drawCountBufferAligned = new( drawCountBuffer.Handle, drawCountBuffer.Offset & ~(UniformBufferAlignment - 1), UniformBufferAlignment); @@ -1562,7 +1568,7 @@ namespace Ryujinx.Graphics.Vulkan shaderParams[22] = indirectDataStride / 4; shaderParams[23] = srcIndirectBufferOffset / 4; - pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length)); + pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]); var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, out var patternBuffer); var patternBufferAuto = patternBuffer.GetBuffer(); diff --git a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs index e62b2dbba..726cafc51 100644 --- a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan { internal class HostMemoryAllocator { - private struct HostMemoryAllocation + private readonly struct HostMemoryAllocation { public readonly Auto<MemoryAllocation> Allocation; public readonly IntPtr Pointer; @@ -33,8 +33,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; private readonly object _lock = new(); - private List<HostMemoryAllocation> _allocations; - private IntervalTree<ulong, HostMemoryAllocation> _allocationTree; + private readonly List<HostMemoryAllocation> _allocations; + private readonly IntervalTree<ulong, HostMemoryAllocation> _allocationTree; public HostMemoryAllocator(MemoryAllocator allocator, Vk api, ExtExternalMemoryHost hostMemoryApi, Device device) { @@ -100,19 +100,19 @@ namespace Ryujinx.Graphics.Vulkan return false; } - ImportMemoryHostPointerInfoEXT importInfo = new ImportMemoryHostPointerInfoEXT() + ImportMemoryHostPointerInfoEXT importInfo = new() { SType = StructureType.ImportMemoryHostPointerInfoExt, HandleType = ExternalMemoryHandleTypeFlags.HostAllocationBitExt, - PHostPointer = (void*)pageAlignedPointer + PHostPointer = (void*)pageAlignedPointer, }; - var memoryAllocateInfo = new MemoryAllocateInfo() + var memoryAllocateInfo = new MemoryAllocateInfo { SType = StructureType.MemoryAllocateInfo, AllocationSize = pageAlignedSize, MemoryTypeIndex = (uint)memoryTypeIndex, - PNext = &importInfo + PNext = &importInfo, }; Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory); diff --git a/src/Ryujinx.Graphics.Vulkan/IdList.cs b/src/Ryujinx.Graphics.Vulkan/IdList.cs index 9fba9fe99..598d68584 100644 --- a/src/Ryujinx.Graphics.Vulkan/IdList.cs +++ b/src/Ryujinx.Graphics.Vulkan/IdList.cs @@ -85,11 +85,9 @@ namespace Ryujinx.Graphics.Vulkan value = _list[id]; return value != null; } - else - { - value = null; - return false; - } + + value = null; + return false; } catch (ArgumentOutOfRangeException) { @@ -120,4 +118,4 @@ namespace Ryujinx.Graphics.Vulkan } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs b/src/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs index 11f4ec333..9d34c8116 100644 --- a/src/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs +++ b/src/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Vulkan public int IndexStride { get; } public bool RepeatStart { get; } - private VulkanRenderer _gd; + private readonly VulkanRenderer _gd; private int _currentSize; private BufferHandle _repeatingBuffer; diff --git a/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs index 75b18456a..e81268e96 100644 --- a/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs @@ -1,19 +1,20 @@ -using Silk.NET.Vulkan; +using Ryujinx.Graphics.GAL; +using IndexType = Silk.NET.Vulkan.IndexType; namespace Ryujinx.Graphics.Vulkan { internal struct IndexBufferState { - public static IndexBufferState Null => new IndexBufferState(GAL.BufferHandle.Null, 0, 0); + public static IndexBufferState Null => new(BufferHandle.Null, 0, 0); private readonly int _offset; private readonly int _size; private readonly IndexType _type; - private readonly GAL.BufferHandle _handle; + private readonly BufferHandle _handle; private Auto<DisposableBuffer> _buffer; - public IndexBufferState(GAL.BufferHandle handle, int offset, int size, IndexType type) + public IndexBufferState(BufferHandle handle, int offset, int size, IndexType type) { _handle = handle; _offset = offset; @@ -22,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan _buffer = null; } - public IndexBufferState(GAL.BufferHandle handle, int offset, int size) + public IndexBufferState(BufferHandle handle, int offset, int size) { _handle = handle; _offset = offset; @@ -97,8 +98,8 @@ namespace Ryujinx.Graphics.Vulkan public Auto<DisposableBuffer> BindConvertedIndexBufferIndirect( VulkanRenderer gd, CommandBufferScoped cbs, - GAL.BufferRange indirectBuffer, - GAL.BufferRange drawCountBuffer, + BufferRange indirectBuffer, + BufferRange drawCountBuffer, IndexBufferPattern pattern, bool hasDrawCount, int maxDrawCount, @@ -110,7 +111,7 @@ namespace Ryujinx.Graphics.Vulkan (var indexBufferAuto, var indirectBufferAuto) = gd.BufferManager.GetBufferTopologyConversionIndirect( gd, cbs, - new GAL.BufferRange(_handle, _offset, _size), + new BufferRange(_handle, _offset, _size), indirectBuffer, drawCountBuffer, pattern, @@ -132,7 +133,7 @@ namespace Ryujinx.Graphics.Vulkan return indirectBufferAuto; } - private int GetIndexSize() + private readonly int GetIndexSize() { return _type switch { @@ -142,7 +143,7 @@ namespace Ryujinx.Graphics.Vulkan }; } - public bool BoundEquals(Auto<DisposableBuffer> buffer) + public readonly bool BoundEquals(Auto<DisposableBuffer> buffer) { return _buffer == buffer; } diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs index 3c98c417c..e2b6028ab 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly HostMemoryAllocator _hostMemory; public DeviceMemory Memory { get; } - public IntPtr HostPointer { get;} + public IntPtr HostPointer { get; } public ulong Offset { get; } public ulong Size { get; } diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 462d8f052..ab7006270 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Vulkan { class MemoryAllocator : IDisposable { - private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; + private const ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private readonly Vk _api; private readonly VulkanPhysicalDevice _physicalDevice; @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan _physicalDevice = physicalDevice; _device = device; _blockLists = new List<MemoryAllocatorBlockList>(); - _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)_physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount); + _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount); } public MemoryAllocation AllocateDeviceMemory( diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs index e564cb264..bd57e778b 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan Size = size; _freeRanges = new List<Range> { - new Range(0, size) + new Range(0, size), }; } @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan { var range = _freeRanges[i]; - ulong alignedOffset = BitUtils.AlignUp<ulong>(range.Offset, alignment); + ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment); ulong sizeDelta = alignedOffset - range.Offset; ulong usableSize = range.Size - sizeDelta; @@ -198,13 +198,13 @@ namespace Ryujinx.Graphics.Vulkan } } - ulong blockAlignedSize = BitUtils.AlignUp<ulong>(size, (ulong)_blockAlignment); + ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment); - var memoryAllocateInfo = new MemoryAllocateInfo() + var memoryAllocateInfo = new MemoryAllocateInfo { SType = StructureType.MemoryAllocateInfo, AllocationSize = blockAlignedSize, - MemoryTypeIndex = (uint)MemoryTypeIndex + MemoryTypeIndex = (uint)MemoryTypeIndex, }; _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError(); @@ -213,12 +213,9 @@ namespace Ryujinx.Graphics.Vulkan if (map) { - unsafe - { - void* pointer = null; - _api.MapMemory(_device, deviceMemory, 0, blockAlignedSize, 0, ref pointer).ThrowOnError(); - hostPointer = (IntPtr)pointer; - } + void* pointer = null; + _api.MapMemory(_device, deviceMemory, 0, blockAlignedSize, 0, ref pointer).ThrowOnError(); + hostPointer = (IntPtr)pointer; } var newBlock = new Block(deviceMemory, hostPointer, blockAlignedSize); @@ -238,10 +235,10 @@ namespace Ryujinx.Graphics.Vulkan return IntPtr.Zero; } - return (IntPtr)((nuint)(nint)block.HostPointer + offset); + return (IntPtr)((nuint)block.HostPointer + offset); } - public unsafe void Free(Block block, ulong offset, ulong size) + public void Free(Block block, ulong offset, ulong size) { block.Free(offset, size); @@ -271,7 +268,7 @@ namespace Ryujinx.Graphics.Vulkan _blocks.Insert(index, block); } - public unsafe void Dispose() + public void Dispose() { for (int i = 0; i < _blocks.Count; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs index 414dbc628..bdf606e82 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK Error = 1, Warning = 2, Info = 3, - Debug = 4 + Debug = 4, } enum MVKConfigTraceVulkanCalls @@ -17,14 +17,14 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK None = 0, Enter = 1, EnterExit = 2, - Duration = 3 + Duration = 3, } enum MVKConfigAutoGPUCaptureScope { None = 0, Device = 1, - Frame = 2 + Frame = 2, } [Flags] @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK All = 0x00000001, MoltenVK = 0x00000002, WSI = 0x00000004, - Portability = 0x00000008 + Portability = 0x00000008, } enum MVKVkSemaphoreSupportStyle @@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS_WHERE_SAFE = 1, MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS = 2, MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_CALLBACK = 3, - MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_MAX_ENUM = 0x7FFFFFFF + MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_MAX_ENUM = 0x7FFFFFFF, } readonly struct Bool32 @@ -60,7 +60,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK } public static implicit operator bool(Bool32 val) => val.Value == 1; - public static implicit operator Bool32(bool val) => new Bool32(val); + public static implicit operator Bool32(bool val) => new(val); } [StructLayout(LayoutKind.Sequential)] diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs index b5d88dde6..457240aa0 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -30,4 +30,4 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs index 13a4f4c14..d564f3e16 100644 --- a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs @@ -8,10 +8,10 @@ namespace Ryujinx.Graphics.Vulkan /// </summary> class MultiFenceHolder { - private static int BufferUsageTrackingGranularity = 4096; + private static readonly int _bufferUsageTrackingGranularity = 4096; private readonly FenceHolder[] _fences; - private BufferUsageBitmap _bufferUsageBitmap; + private readonly BufferUsageBitmap _bufferUsageBitmap; /// <summary> /// Creates a new instance of the multiple fence holder. @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan public MultiFenceHolder(int size) { _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); + _bufferUsageBitmap = new BufferUsageBitmap(size, _bufferUsageTrackingGranularity); } /// <summary> @@ -189,11 +189,11 @@ namespace Ryujinx.Graphics.Vulkan if (hasTimeout) { - signaled = FenceHelper.AllSignaled(api, device, fences.Slice(0, fenceCount), timeout); + signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout); } else { - FenceHelper.WaitAllIndefinitely(api, device, fences.Slice(0, fenceCount)); + FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]); } for (int i = 0; i < fenceCount; i++) diff --git a/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs index fc98b68f7..80a24682b 100644 --- a/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs @@ -1,10 +1,11 @@ -using System; +using Ryujinx.Graphics.GAL; +using System; namespace Ryujinx.Graphics.Vulkan { internal class PersistentFlushBuffer : IDisposable { - private VulkanRenderer _gd; + private readonly VulkanRenderer _gd; private BufferHolder _flushStorage; @@ -19,10 +20,7 @@ namespace Ryujinx.Graphics.Vulkan if (flushStorage == null || size > _flushStorage.Size) { - if (flushStorage != null) - { - flushStorage.Dispose(); - } + flushStorage?.Dispose(); flushStorage = _gd.BufferManager.Create(_gd, size); _flushStorage = flushStorage; @@ -59,7 +57,7 @@ namespace Ryujinx.Graphics.Vulkan public Span<byte> GetTextureData(CommandBufferPool cbp, TextureView view, int size) { - GAL.TextureCreateInfo info = view.Info; + TextureCreateInfo info = view.Info; var flushStorage = ResizeIfNeeded(size); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 1ee03536d..5ee926911 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1,5 +1,4 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; @@ -7,6 +6,13 @@ using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using CompareOp = Ryujinx.Graphics.GAL.CompareOp; +using Format = Ryujinx.Graphics.GAL.Format; +using FrontFace = Ryujinx.Graphics.GAL.FrontFace; +using IndexType = Ryujinx.Graphics.GAL.IndexType; +using PolygonMode = Ryujinx.Graphics.GAL.PolygonMode; +using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology; +using Viewport = Ryujinx.Graphics.GAL.Viewport; namespace Ryujinx.Graphics.Vulkan { @@ -28,7 +34,7 @@ namespace Ryujinx.Graphics.Vulkan protected PipelineDynamicState DynamicState; private PipelineState _newState; private bool _stateDirty; - private GAL.PrimitiveTopology _topology; + private PrimitiveTopology _topology; private ulong _currentPipelineHandle; @@ -44,7 +50,7 @@ namespace Ryujinx.Graphics.Vulkan private ShaderCollection _program; - private Vector4<float>[] _renderScale = new Vector4<float>[73]; + private readonly Vector4<float>[] _renderScale = new Vector4<float>[73]; private int _fragmentScaleCount; protected FramebufferParams FramebufferParams; @@ -78,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan private bool _tfEnabled; private bool _tfActive; - private PipelineColorBlendAttachmentState[] _storedBlend; + private readonly PipelineColorBlendAttachmentState[] _storedBlend; private ulong _drawCountSinceBarrier; public ulong DrawCount { get; private set; } @@ -91,9 +97,9 @@ namespace Ryujinx.Graphics.Vulkan AutoFlush = new AutoFlushCounter(gd); - var pipelineCacheCreateInfo = new PipelineCacheCreateInfo() + var pipelineCacheCreateInfo = new PipelineCacheCreateInfo { - SType = StructureType.PipelineCacheCreateInfo + SType = StructureType.PipelineCacheCreateInfo, }; gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); @@ -108,7 +114,7 @@ namespace Ryujinx.Graphics.Vulkan using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize); emptyVb.SetData(0, new byte[EmptyVbSize]); - _vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize, 0); + _vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize); _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff); @@ -146,11 +152,11 @@ namespace Ryujinx.Graphics.Vulkan } } - MemoryBarrier memoryBarrier = new MemoryBarrier() + MemoryBarrier memoryBarrier = new() { SType = StructureType.MemoryBarrier, SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, - DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, }; Gd.Api.CmdPipelineBarrier( @@ -168,11 +174,11 @@ namespace Ryujinx.Graphics.Vulkan public void ComputeBarrier() { - MemoryBarrier memoryBarrier = new MemoryBarrier() + MemoryBarrier memoryBarrier = new() { SType = StructureType.MemoryBarrier, SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, - DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, }; Gd.Api.CmdPipelineBarrier( @@ -188,7 +194,7 @@ namespace Ryujinx.Graphics.Vulkan ReadOnlySpan<ImageMemoryBarrier>.Empty); } - public void BeginTransformFeedback(GAL.PrimitiveTopology topology) + public void BeginTransformFeedback(PrimitiveTopology topology) { _tfEnabled = true; } @@ -281,11 +287,11 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void CommandBufferBarrier() { - MemoryBarrier memoryBarrier = new MemoryBarrier() + MemoryBarrier memoryBarrier = new() { SType = StructureType.MemoryBarrier, SrcAccessMask = BufferHolder.DefaultAccessFlags, - DstAccessMask = AccessFlags.IndirectCommandReadBit + DstAccessMask = AccessFlags.IndirectCommandReadBit, }; Gd.Api.CmdPipelineBarrier( @@ -374,10 +380,10 @@ namespace Ryujinx.Graphics.Vulkan IndexBufferPattern pattern = _topology switch { - GAL.PrimitiveTopology.Quads => QuadsToTrisPattern, - GAL.PrimitiveTopology.TriangleFan or - GAL.PrimitiveTopology.Polygon => TriFanToTrisPattern, - _ => throw new NotSupportedException($"Unsupported topology: {_topology}") + PrimitiveTopology.Quads => QuadsToTrisPattern, + PrimitiveTopology.TriangleFan or + PrimitiveTopology.Polygon => TriFanToTrisPattern, + _ => throw new NotSupportedException($"Unsupported topology: {_topology}"), }; BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount); @@ -406,10 +412,10 @@ namespace Ryujinx.Graphics.Vulkan { pattern = _topology switch { - GAL.PrimitiveTopology.Quads => QuadsToTrisPattern, - GAL.PrimitiveTopology.TriangleFan or - GAL.PrimitiveTopology.Polygon => TriFanToTrisPattern, - _ => throw new NotSupportedException($"Unsupported topology: {_topology}") + PrimitiveTopology.Quads => QuadsToTrisPattern, + PrimitiveTopology.TriangleFan or + PrimitiveTopology.Polygon => TriFanToTrisPattern, + _ => throw new NotSupportedException($"Unsupported topology: {_topology}"), }; } @@ -718,7 +724,7 @@ namespace Ryujinx.Graphics.Vulkan return CommandBuffer.Handle == cb.Handle; } - public void SetAlphaTest(bool enable, float reference, GAL.CompareOp op) + public void SetAlphaTest(bool enable, float reference, CompareOp op) { // This is currently handled using shader specialization, as Vulkan does not support alpha test. // In the future, we may want to use this to write the reference value into the support buffer, @@ -847,13 +853,13 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetFrontFace(GAL.FrontFace frontFace) + public void SetFrontFace(FrontFace frontFace) { _newState.FrontFace = frontFace.Convert(); SignalStateChange(); } - public void SetImage(int binding, ITexture image, GAL.Format imageFormat) + public void SetImage(int binding, ITexture image, Format imageFormat) { _descriptorSetUpdater.SetImage(binding, image, imageFormat); } @@ -863,7 +869,7 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSetUpdater.SetImage(binding, image); } - public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type) + public void SetIndexBuffer(BufferRange buffer, IndexType type) { if (buffer.Handle != BufferHandle.Null) { @@ -897,12 +903,7 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetOrigin(Origin origin) - { - // TODO. - } - - public unsafe void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel) + public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel) { _newState.PatchControlPoints = (uint)vertices; SignalStateChange(); @@ -910,15 +911,17 @@ namespace Ryujinx.Graphics.Vulkan // TODO: Default levels (likely needs emulation on shaders?) } +#pragma warning disable CA1822 // Mark member as static public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) { // TODO. } - public void SetPolygonMode(GAL.PolygonMode frontMode, GAL.PolygonMode backMode) + public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) { // TODO. } +#pragma warning restore CA1822 public void SetPrimitiveRestart(bool enable, int index) { @@ -927,7 +930,7 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetPrimitiveTopology(GAL.PrimitiveTopology topology) + public void SetPrimitiveTopology(PrimitiveTopology topology) { _topology = topology; @@ -950,7 +953,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.PipelineLayout = internalProgram.PipelineLayout; _newState.StagesCount = (uint)stages.Length; - stages.CopyTo(_newState.Stages.AsSpan().Slice(0, stages.Length)); + stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]); SignalStateChange(); @@ -1149,10 +1152,12 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers); } +#pragma warning disable CA1822 // Mark member as static public void SetUserClipDistance(int index, bool enableClip) { // TODO. } +#pragma warning restore CA1822 public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) { @@ -1298,7 +1303,7 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetViewports(ReadOnlySpan<GAL.Viewport> viewports, bool disableTransform) + public void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform) { int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; int count = Math.Min(maxViewports, viewports.Length); @@ -1332,7 +1337,7 @@ namespace Ryujinx.Graphics.Vulkan X = scale * 2f / viewports[0].Region.Width, Y = scale * 2f / viewports[0].Region.Height, Z = 1, - W = disableTransformF + W = disableTransformF, }); } @@ -1361,11 +1366,11 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void TextureBarrier() { - MemoryBarrier memoryBarrier = new MemoryBarrier() + MemoryBarrier memoryBarrier = new() { SType = StructureType.MemoryBarrier, SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, - DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit + DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, }; Gd.Api.CmdPipelineBarrier( @@ -1433,7 +1438,7 @@ namespace Ryujinx.Graphics.Vulkan // Just try to remove duplicate attachments. // Save a copy of the array to rebind when mask changes. - void maskOut() + void MaskOut() { if (!_framebufferUsingColorWriteMask) { @@ -1467,12 +1472,12 @@ namespace Ryujinx.Graphics.Vulkan if (vkBlend.ColorWriteMask == 0) { colors[i] = null; - maskOut(); + MaskOut(); } else if (vkBlend2.ColorWriteMask == 0) { colors[j] = null; - maskOut(); + MaskOut(); } } } @@ -1505,9 +1510,9 @@ namespace Ryujinx.Graphics.Vulkan AttachmentDescription[] attachmentDescs = null; - var subpass = new SubpassDescription() + var subpass = new SubpassDescription { - PipelineBindPoint = PipelineBindPoint.Graphics + PipelineBindPoint = PipelineBindPoint.Graphics, }; AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments]; @@ -1572,7 +1577,7 @@ namespace Ryujinx.Graphics.Vulkan fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { - var renderPassCreateInfo = new RenderPassCreateInfo() + var renderPassCreateInfo = new RenderPassCreateInfo { SType = StructureType.RenderPassCreateInfo, PAttachments = pAttachmentDescs, @@ -1580,7 +1585,7 @@ namespace Ryujinx.Graphics.Vulkan PSubpasses = &subpass, SubpassCount = 1, PDependencies = &subpassDependency, - DependencyCount = 1 + DependencyCount = 1, }; Gd.Api.CreateRenderPass(Device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); @@ -1688,14 +1693,14 @@ namespace Ryujinx.Graphics.Vulkan var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height)); var clearValue = new ClearValue(); - var renderPassBeginInfo = new RenderPassBeginInfo() + var renderPassBeginInfo = new RenderPassBeginInfo { SType = StructureType.RenderPassBeginInfo, RenderPass = _renderPass.Get(Cbs).Value, Framebuffer = _framebuffer.Get(Cbs).Value, RenderArea = renderArea, PClearValues = &clearValue, - ClearValueCount = 1 + ClearValueCount = 1, }; Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index a52b44622..7c1ddef8d 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -2,6 +2,8 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; +using Format = Silk.NET.Vulkan.Format; +using PolygonMode = Silk.NET.Vulkan.PolygonMode; namespace Ryujinx.Graphics.Vulkan { @@ -16,15 +18,15 @@ namespace Ryujinx.Graphics.Vulkan AttachmentDescription[] attachmentDescs = null; - var subpass = new SubpassDescription() + var subpass = new SubpassDescription { - PipelineBindPoint = PipelineBindPoint.Graphics + PipelineBindPoint = PipelineBindPoint.Graphics, }; AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments]; Span<int> attachmentIndices = stackalloc int[MaxAttachments]; - Span<Silk.NET.Vulkan.Format> attachmentFormats = stackalloc Silk.NET.Vulkan.Format[MaxAttachments]; + Span<Format> attachmentFormats = stackalloc Format[MaxAttachments]; int attachmentCount = 0; int colorCount = 0; @@ -106,7 +108,7 @@ namespace Ryujinx.Graphics.Vulkan fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { - var renderPassCreateInfo = new RenderPassCreateInfo() + var renderPassCreateInfo = new RenderPassCreateInfo { SType = StructureType.RenderPassCreateInfo, PAttachments = pAttachmentDescs, @@ -114,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan PSubpasses = &subpass, SubpassCount = 1, PDependencies = &subpassDependency, - DependencyCount = 1 + DependencyCount = 1, }; gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); @@ -151,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd) { - PipelineState pipeline = new PipelineState(); + PipelineState pipeline = new(); pipeline.Initialize(); // It is assumed that Dynamic State is enabled when this conversion is used. @@ -178,7 +180,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.MaxDepthBounds = 0f; // Not implemented. pipeline.PatchControlPoints = state.PatchControlPoints; - pipeline.PolygonMode = Silk.NET.Vulkan.PolygonMode.Fill; // Not implemented. + pipeline.PolygonMode = PolygonMode.Fill; // Not implemented. pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable; pipeline.RasterizerDiscardEnable = state.RasterizerDiscard; pipeline.SamplesCount = (uint)state.SamplesCount; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index 42ea022a4..ff88e4cf4 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Vulkan Scissor = 1 << 2, Stencil = 1 << 3, Viewport = 1 << 4, - All = Blend | DepthBias | Scissor | Stencil | Viewport + All = Blend | DepthBias | Scissor | Stencil | Viewport, } private DirtyFlags _dirty; @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Vulkan api.CmdSetBlendConstants(commandBuffer, _blendConstants.AsSpan()); } - private void RecordDepthBias(Vk api, CommandBuffer commandBuffer) + private readonly void RecordDepthBias(Vk api, CommandBuffer commandBuffer) { api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor); } @@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Vulkan api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan()); } - private void RecordStencilMasks(Vk api, CommandBuffer commandBuffer) + private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer) { api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask); api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 8026103e7..dfdac52fd 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan private ulong _byteWeight; - private List<BufferHolder> _backingSwaps; + private readonly List<BufferHolder> _backingSwaps; public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device) { @@ -116,15 +116,15 @@ namespace Ryujinx.Graphics.Vulkan if (Gd.Capabilities.SupportsConditionalRendering) { - var buffer = evt.GetBuffer().Get(Cbs, 0, sizeof(long)).Value; - var flags = isEqual ? ConditionalRenderingFlagsEXT.InvertedBitExt : 0; + // var buffer = evt.GetBuffer().Get(Cbs, 0, sizeof(long)).Value; + // var flags = isEqual ? ConditionalRenderingFlagsEXT.InvertedBitExt : 0; - var conditionalRenderingBeginInfo = new ConditionalRenderingBeginInfoEXT() - { - SType = StructureType.ConditionalRenderingBeginInfoExt, - Buffer = buffer, - Flags = flags - }; + // var conditionalRenderingBeginInfo = new ConditionalRenderingBeginInfoEXT + // { + // SType = StructureType.ConditionalRenderingBeginInfoExt, + // Buffer = buffer, + // Flags = flags, + // }; // Gd.ConditionalRenderingApi.CmdBeginConditionalRendering(CommandBuffer, conditionalRenderingBeginInfo); } @@ -156,10 +156,7 @@ namespace Ryujinx.Graphics.Vulkan public CommandBufferScoped GetPreloadCommandBuffer() { - if (PreloadCbs == null) - { - PreloadCbs = Gd.CommandBufferPool.Rent(); - } + PreloadCbs ??= Gd.CommandBufferPool.Rent(); return PreloadCbs.Value; } @@ -192,7 +189,7 @@ namespace Ryujinx.Graphics.Vulkan { CommandBufferScoped? cbs = null; - _backingSwaps.RemoveAll((holder) => holder.TryBackingSwap(ref cbs)); + _backingSwaps.RemoveAll(holder => holder.TryBackingSwap(ref cbs)); cbs?.Dispose(); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs index e7c435670..5d0cada96 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan public override int GetHashCode() { - HashCode hasher = new HashCode(); + HashCode hasher = new(); if (SetDescriptors != null) { @@ -83,10 +83,10 @@ namespace Ryujinx.Graphics.Vulkan { var key = new PlceKey(setDescriptors, usePushDescriptors); - return _plces.GetOrAdd(key, (newKey) => new PipelineLayoutCacheEntry(gd, device, setDescriptors, usePushDescriptors)); + return _plces.GetOrAdd(key, newKey => new PipelineLayoutCacheEntry(gd, device, setDescriptors, usePushDescriptors)); } - protected virtual unsafe void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index bcb2c1a50..ba93dfadb 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -45,23 +45,23 @@ namespace Ryujinx.Graphics.Vulkan stages = activeStages; } - layoutBindings[descIndex] = new DescriptorSetLayoutBinding() + layoutBindings[descIndex] = new DescriptorSetLayoutBinding { Binding = (uint)descriptor.Binding, DescriptorType = descriptor.Type.Convert(), DescriptorCount = (uint)descriptor.Count, - StageFlags = stages.Convert() + StageFlags = stages.Convert(), }; } fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings) { - var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() + var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo { SType = StructureType.DescriptorSetLayoutCreateInfo, PBindings = pLayoutBindings, BindingCount = (uint)layoutBindings.Length, - Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None + Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None, }; gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError(); @@ -72,11 +72,11 @@ namespace Ryujinx.Graphics.Vulkan fixed (DescriptorSetLayout* pLayouts = layouts) { - var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo() + var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo { SType = StructureType.PipelineLayoutCreateInfo, PSetLayouts = pLayouts, - SetLayoutCount = (uint)layouts.Length + SetLayoutCount = (uint)layouts.Length, }; gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError(); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index 7e803913f..cc9af5b6d 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -13,301 +13,301 @@ namespace Ryujinx.Graphics.Vulkan public float LineWidth { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 0) & 0xFFFFFFFF)); + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 0) & 0xFFFFFFFF)); set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); } public float DepthBiasClamp { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 32) & 0xFFFFFFFF)); + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 32) & 0xFFFFFFFF)); set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); } public float DepthBiasConstantFactor { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 0) & 0xFFFFFFFF)); + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 0) & 0xFFFFFFFF)); set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); } public float DepthBiasSlopeFactor { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 32) & 0xFFFFFFFF)); + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 32) & 0xFFFFFFFF)); set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); } public uint StencilFrontCompareMask { - get => (uint)((Internal.Id2 >> 0) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id2 >> 0) & 0xFFFFFFFF); set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF00000000) | ((ulong)value << 0); } public uint StencilFrontWriteMask { - get => (uint)((Internal.Id2 >> 32) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id2 >> 32) & 0xFFFFFFFF); set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF) | ((ulong)value << 32); } public uint StencilFrontReference { - get => (uint)((Internal.Id3 >> 0) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id3 >> 0) & 0xFFFFFFFF); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF00000000) | ((ulong)value << 0); } public uint StencilBackCompareMask { - get => (uint)((Internal.Id3 >> 32) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id3 >> 32) & 0xFFFFFFFF); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF) | ((ulong)value << 32); } public uint StencilBackWriteMask { - get => (uint)((Internal.Id4 >> 0) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id4 >> 0) & 0xFFFFFFFF); set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF00000000) | ((ulong)value << 0); } public uint StencilBackReference { - get => (uint)((Internal.Id4 >> 32) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id4 >> 32) & 0xFFFFFFFF); set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF) | ((ulong)value << 32); } public float MinDepthBounds { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 0) & 0xFFFFFFFF)); + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 0) & 0xFFFFFFFF)); set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); } public float MaxDepthBounds { - get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 32) & 0xFFFFFFFF)); + readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 32) & 0xFFFFFFFF)); set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); } public PolygonMode PolygonMode { - get => (PolygonMode)((Internal.Id6 >> 0) & 0x3FFFFFFF); + readonly get => (PolygonMode)((Internal.Id6 >> 0) & 0x3FFFFFFF); set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC0000000) | ((ulong)value << 0); } public uint StagesCount { - get => (byte)((Internal.Id6 >> 30) & 0xFF); + readonly get => (byte)((Internal.Id6 >> 30) & 0xFF); set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30); } public uint VertexAttributeDescriptionsCount { - get => (byte)((Internal.Id6 >> 38) & 0xFF); + readonly get => (byte)((Internal.Id6 >> 38) & 0xFF); set => Internal.Id6 = (Internal.Id6 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38); } public uint VertexBindingDescriptionsCount { - get => (byte)((Internal.Id6 >> 46) & 0xFF); + readonly get => (byte)((Internal.Id6 >> 46) & 0xFF); set => Internal.Id6 = (Internal.Id6 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46); } public uint ViewportsCount { - get => (byte)((Internal.Id6 >> 54) & 0xFF); + readonly get => (byte)((Internal.Id6 >> 54) & 0xFF); set => Internal.Id6 = (Internal.Id6 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54); } public uint ScissorsCount { - get => (byte)((Internal.Id7 >> 0) & 0xFF); + readonly get => (byte)((Internal.Id7 >> 0) & 0xFF); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); } public uint ColorBlendAttachmentStateCount { - get => (byte)((Internal.Id7 >> 8) & 0xFF); + readonly get => (byte)((Internal.Id7 >> 8) & 0xFF); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); } public PrimitiveTopology Topology { - get => (PrimitiveTopology)((Internal.Id7 >> 16) & 0xF); + readonly get => (PrimitiveTopology)((Internal.Id7 >> 16) & 0xF); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); } public LogicOp LogicOp { - get => (LogicOp)((Internal.Id7 >> 20) & 0xF); + readonly get => (LogicOp)((Internal.Id7 >> 20) & 0xF); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20); } public CompareOp DepthCompareOp { - get => (CompareOp)((Internal.Id7 >> 24) & 0x7); + readonly get => (CompareOp)((Internal.Id7 >> 24) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24); } public StencilOp StencilFrontFailOp { - get => (StencilOp)((Internal.Id7 >> 27) & 0x7); + readonly get => (StencilOp)((Internal.Id7 >> 27) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27); } public StencilOp StencilFrontPassOp { - get => (StencilOp)((Internal.Id7 >> 30) & 0x7); + readonly get => (StencilOp)((Internal.Id7 >> 30) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30); } public StencilOp StencilFrontDepthFailOp { - get => (StencilOp)((Internal.Id7 >> 33) & 0x7); + readonly get => (StencilOp)((Internal.Id7 >> 33) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33); } public CompareOp StencilFrontCompareOp { - get => (CompareOp)((Internal.Id7 >> 36) & 0x7); + readonly get => (CompareOp)((Internal.Id7 >> 36) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36); } public StencilOp StencilBackFailOp { - get => (StencilOp)((Internal.Id7 >> 39) & 0x7); + readonly get => (StencilOp)((Internal.Id7 >> 39) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39); } public StencilOp StencilBackPassOp { - get => (StencilOp)((Internal.Id7 >> 42) & 0x7); + readonly get => (StencilOp)((Internal.Id7 >> 42) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42); } public StencilOp StencilBackDepthFailOp { - get => (StencilOp)((Internal.Id7 >> 45) & 0x7); + readonly get => (StencilOp)((Internal.Id7 >> 45) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45); } public CompareOp StencilBackCompareOp { - get => (CompareOp)((Internal.Id7 >> 48) & 0x7); + readonly get => (CompareOp)((Internal.Id7 >> 48) & 0x7); set => Internal.Id7 = (Internal.Id7 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48); } public CullModeFlags CullMode { - get => (CullModeFlags)((Internal.Id7 >> 51) & 0x3); + readonly get => (CullModeFlags)((Internal.Id7 >> 51) & 0x3); set => Internal.Id7 = (Internal.Id7 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51); } public bool PrimitiveRestartEnable { - get => ((Internal.Id7 >> 53) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 53) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53); } public bool DepthClampEnable { - get => ((Internal.Id7 >> 54) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 54) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54); } public bool RasterizerDiscardEnable { - get => ((Internal.Id7 >> 55) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 55) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55); } public FrontFace FrontFace { - get => (FrontFace)((Internal.Id7 >> 56) & 0x1); + readonly get => (FrontFace)((Internal.Id7 >> 56) & 0x1); set => Internal.Id7 = (Internal.Id7 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56); } public bool DepthBiasEnable { - get => ((Internal.Id7 >> 57) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 57) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57); } public bool DepthTestEnable { - get => ((Internal.Id7 >> 58) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 58) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58); } public bool DepthWriteEnable { - get => ((Internal.Id7 >> 59) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 59) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59); } public bool DepthBoundsTestEnable { - get => ((Internal.Id7 >> 60) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 60) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60); } public bool StencilTestEnable { - get => ((Internal.Id7 >> 61) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 61) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61); } public bool LogicOpEnable { - get => ((Internal.Id7 >> 62) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 62) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62); } public bool HasDepthStencil { - get => ((Internal.Id7 >> 63) & 0x1) != 0UL; + readonly get => ((Internal.Id7 >> 63) & 0x1) != 0UL; set => Internal.Id7 = (Internal.Id7 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); } public uint PatchControlPoints { - get => (uint)((Internal.Id8 >> 0) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id8 >> 0) & 0xFFFFFFFF); set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF00000000) | ((ulong)value << 0); } public uint SamplesCount { - get => (uint)((Internal.Id8 >> 32) & 0xFFFFFFFF); + readonly get => (uint)((Internal.Id8 >> 32) & 0xFFFFFFFF); set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF) | ((ulong)value << 32); } public bool AlphaToCoverageEnable { - get => ((Internal.Id9 >> 0) & 0x1) != 0UL; + readonly get => ((Internal.Id9 >> 0) & 0x1) != 0UL; set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0); } public bool AlphaToOneEnable { - get => ((Internal.Id9 >> 1) & 0x1) != 0UL; + readonly get => ((Internal.Id9 >> 1) & 0x1) != 0UL; set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); } public bool AdvancedBlendSrcPreMultiplied { - get => ((Internal.Id9 >> 2) & 0x1) != 0UL; + readonly get => ((Internal.Id9 >> 2) & 0x1) != 0UL; set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2); } public bool AdvancedBlendDstPreMultiplied { - get => ((Internal.Id9 >> 3) & 0x1) != 0UL; + readonly get => ((Internal.Id9 >> 3) & 0x1) != 0UL; set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3); } public BlendOverlapEXT AdvancedBlendOverlap { - get => (BlendOverlapEXT)((Internal.Id9 >> 4) & 0x3); + readonly get => (BlendOverlapEXT)((Internal.Id9 >> 4) & 0x3); set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); } public bool DepthMode { - get => ((Internal.Id9 >> 6) & 0x1) != 0UL; + readonly get => ((Internal.Id9 >> 6) & 0x1) != 0UL; set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); } @@ -325,10 +325,10 @@ namespace Ryujinx.Graphics.Vulkan for (int index = 0; index < Constants.MaxShaderStages; index++) { - StageRequiredSubgroupSizes[index] = new PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT() + StageRequiredSubgroupSizes[index] = new PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT { SType = StructureType.PipelineShaderStageRequiredSubgroupSizeCreateInfoExt, - RequiredSubgroupSize = RequiredSubgroupSize + RequiredSubgroupSize = RequiredSubgroupSize, }; } @@ -357,12 +357,12 @@ namespace Ryujinx.Graphics.Vulkan UpdateStageRequiredSubgroupSizes(gd, 1); } - var pipelineCreateInfo = new ComputePipelineCreateInfo() + var pipelineCreateInfo = new ComputePipelineCreateInfo { SType = StructureType.ComputePipelineCreateInfo, Stage = Stages[0], BasePipelineIndex = -1, - Layout = PipelineLayout + Layout = PipelineLayout, }; Pipeline pipelineHandle = default; @@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Vulkan VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount, PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, VertexBindingDescriptionCount = VertexBindingDescriptionsCount, - PVertexBindingDescriptions = pVertexBindingDescriptions + PVertexBindingDescriptions = pVertexBindingDescriptions, }; bool primitiveRestartEnable = PrimitiveRestartEnable; @@ -453,20 +453,20 @@ namespace Ryujinx.Graphics.Vulkan primitiveRestartEnable &= topologySupportsRestart; - var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo() + var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo { SType = StructureType.PipelineInputAssemblyStateCreateInfo, PrimitiveRestartEnable = primitiveRestartEnable, - Topology = Topology + Topology = Topology, }; - var tessellationState = new PipelineTessellationStateCreateInfo() + var tessellationState = new PipelineTessellationStateCreateInfo { SType = StructureType.PipelineTessellationStateCreateInfo, - PatchControlPoints = PatchControlPoints + PatchControlPoints = PatchControlPoints, }; - var rasterizationState = new PipelineRasterizationStateCreateInfo() + var rasterizationState = new PipelineRasterizationStateCreateInfo { SType = StructureType.PipelineRasterizationStateCreateInfo, DepthClampEnable = DepthClampEnable, @@ -478,24 +478,24 @@ namespace Ryujinx.Graphics.Vulkan DepthBiasEnable = DepthBiasEnable, DepthBiasClamp = DepthBiasClamp, DepthBiasConstantFactor = DepthBiasConstantFactor, - DepthBiasSlopeFactor = DepthBiasSlopeFactor + DepthBiasSlopeFactor = DepthBiasSlopeFactor, }; - var viewportState = new PipelineViewportStateCreateInfo() + var viewportState = new PipelineViewportStateCreateInfo { SType = StructureType.PipelineViewportStateCreateInfo, ViewportCount = ViewportsCount, PViewports = pViewports, ScissorCount = ScissorsCount, - PScissors = pScissors + PScissors = pScissors, }; if (gd.Capabilities.SupportsDepthClipControl) { - var viewportDepthClipControlState = new PipelineViewportDepthClipControlCreateInfoEXT() + var viewportDepthClipControlState = new PipelineViewportDepthClipControlCreateInfoEXT { SType = StructureType.PipelineViewportDepthClipControlCreateInfoExt, - NegativeOneToOne = DepthMode + NegativeOneToOne = DepthMode, }; viewportState.PNext = &viewportDepthClipControlState; @@ -508,7 +508,7 @@ namespace Ryujinx.Graphics.Vulkan RasterizationSamples = TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, SamplesCount), MinSampleShading = 1, AlphaToCoverageEnable = AlphaToCoverageEnable, - AlphaToOneEnable = AlphaToOneEnable + AlphaToOneEnable = AlphaToOneEnable, }; var stencilFront = new StencilOpState( @@ -529,7 +529,7 @@ namespace Ryujinx.Graphics.Vulkan StencilBackWriteMask, StencilBackReference); - var depthStencilState = new PipelineDepthStencilStateCreateInfo() + var depthStencilState = new PipelineDepthStencilStateCreateInfo { SType = StructureType.PipelineDepthStencilStateCreateInfo, DepthTestEnable = DepthTestEnable, @@ -540,7 +540,7 @@ namespace Ryujinx.Graphics.Vulkan Front = stencilFront, Back = stencilBack, MinDepthBounds = MinDepthBounds, - MaxDepthBounds = MaxDepthBounds + MaxDepthBounds = MaxDepthBounds, }; uint blendEnables = 0; @@ -564,13 +564,13 @@ namespace Ryujinx.Graphics.Vulkan } } - var colorBlendState = new PipelineColorBlendStateCreateInfo() + var colorBlendState = new PipelineColorBlendStateCreateInfo { SType = StructureType.PipelineColorBlendStateCreateInfo, LogicOpEnable = LogicOpEnable, LogicOp = LogicOp, AttachmentCount = ColorBlendAttachmentStateCount, - PAttachments = pColorBlendAttachmentState + PAttachments = pColorBlendAttachmentState, }; PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState; @@ -579,12 +579,12 @@ namespace Ryujinx.Graphics.Vulkan !AdvancedBlendDstPreMultiplied || AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt) { - colorBlendAdvancedState = new PipelineColorBlendAdvancedStateCreateInfoEXT() + colorBlendAdvancedState = new PipelineColorBlendAdvancedStateCreateInfoEXT { SType = StructureType.PipelineColorBlendAdvancedStateCreateInfoExt, SrcPremultiplied = AdvancedBlendSrcPreMultiplied, DstPremultiplied = AdvancedBlendDstPreMultiplied, - BlendOverlap = AdvancedBlendOverlap + BlendOverlap = AdvancedBlendOverlap, }; colorBlendState.PNext = &colorBlendAdvancedState; @@ -609,11 +609,11 @@ namespace Ryujinx.Graphics.Vulkan dynamicStates[8] = DynamicState.VertexInputBindingStrideExt; } - var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo() + var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo { SType = StructureType.PipelineDynamicStateCreateInfo, DynamicStateCount = (uint)dynamicStatesCount, - PDynamicStates = dynamicStates + PDynamicStates = dynamicStates, }; if (gd.Capabilities.SupportsSubgroupSizeControl) @@ -621,7 +621,7 @@ namespace Ryujinx.Graphics.Vulkan UpdateStageRequiredSubgroupSizes(gd, (int)StagesCount); } - var pipelineCreateInfo = new GraphicsPipelineCreateInfo() + var pipelineCreateInfo = new GraphicsPipelineCreateInfo { SType = StructureType.GraphicsPipelineCreateInfo, StageCount = StagesCount, @@ -637,7 +637,7 @@ namespace Ryujinx.Graphics.Vulkan PDynamicState = &pipelineDynamicStateCreateInfo, Layout = PipelineLayout, RenderPass = renderPass, - BasePipelineIndex = -1 + BasePipelineIndex = -1, }; gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError(); @@ -659,7 +659,7 @@ namespace Ryujinx.Graphics.Vulkan return pipeline; } - private unsafe void UpdateStageRequiredSubgroupSizes(VulkanRenderer gd, int count) + private readonly unsafe void UpdateStageRequiredSubgroupSizes(VulkanRenderer gd, int count) { for (int index = 0; index < count; index++) { @@ -728,7 +728,7 @@ namespace Ryujinx.Graphics.Vulkan return -1; } - public void Dispose() + public readonly void Dispose() { Stages.Dispose(); StageRequiredSubgroupSizes.Dispose(); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs index f404be744..460c27d8b 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs @@ -22,12 +22,10 @@ namespace Ryujinx.Graphics.Vulkan public ulong Id8; public ulong Id9; - private uint VertexAttributeDescriptionsCount => (byte)((Id6 >> 38) & 0xFF); - private uint VertexBindingDescriptionsCount => (byte)((Id6 >> 46) & 0xFF); - private uint ViewportsCount => (byte)((Id6 >> 54) & 0xFF); - private uint ScissorsCount => (byte)(Id7 & 0xFF); - private uint ColorBlendAttachmentStateCount => (byte)((Id7 >> 8) & 0xFF); - private bool HasDepthStencil => ((Id7 >> 63) & 0x1) != 0UL; + private readonly uint VertexAttributeDescriptionsCount => (byte)((Id6 >> 38) & 0xFF); + private readonly uint VertexBindingDescriptionsCount => (byte)((Id6 >> 46) & 0xFF); + private readonly uint ColorBlendAttachmentStateCount => (byte)((Id7 >> 8) & 0xFF); + private readonly bool HasDepthStencil => ((Id7 >> 63) & 0x1) != 0UL; public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions; public Array33<VertexInputBindingDescription> VertexBindingDescriptions; @@ -37,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan public Array9<Format> AttachmentFormats; public uint AttachmentIntegerFormatMask; - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is PipelineUid other && Equals(other); } @@ -76,7 +74,7 @@ namespace Ryujinx.Graphics.Vulkan private static bool SequenceEqual<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> y, uint count) where T : unmanaged { - return MemoryMarshal.Cast<T, byte>(x.Slice(0, (int)count)).SequenceEqual(MemoryMarshal.Cast<T, byte>(y.Slice(0, (int)count))); + return MemoryMarshal.Cast<T, byte>(x[..(int)count]).SequenceEqual(MemoryMarshal.Cast<T, byte>(y[..(int)count])); } public override int GetHashCode() diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index 861155a30..8d4fdc196 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -23,10 +23,10 @@ namespace Ryujinx.Graphics.Vulkan.Queries private readonly BufferHolder _buffer; private readonly IntPtr _bufferMap; private readonly CounterType _type; - private bool _result32Bit; - private bool _isSupported; + private readonly bool _result32Bit; + private readonly bool _isSupported; - private long _defaultValue; + private readonly long _defaultValue; private int? _resetSequence; public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit) @@ -44,12 +44,12 @@ namespace Ryujinx.Graphics.Vulkan.Queries QueryPipelineStatisticFlags flags = type == CounterType.PrimitivesGenerated ? QueryPipelineStatisticFlags.GeometryShaderPrimitivesBit : 0; - var queryPoolCreateInfo = new QueryPoolCreateInfo() + var queryPoolCreateInfo = new QueryPoolCreateInfo { SType = StructureType.QueryPoolCreateInfo, QueryCount = 1, QueryType = GetQueryType(type), - PipelineStatistics = flags + PipelineStatistics = flags, }; gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError(); @@ -63,14 +63,14 @@ namespace Ryujinx.Graphics.Vulkan.Queries _buffer = buffer; } - private bool QueryTypeSupported(VulkanRenderer gd, CounterType type) + private static bool QueryTypeSupported(VulkanRenderer gd, CounterType type) { return type switch { CounterType.SamplesPassed => true, CounterType.PrimitivesGenerated => gd.Capabilities.SupportsPipelineStatisticsQuery, CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries, - _ => false + _ => false, }; } @@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries CounterType.SamplesPassed => QueryType.Occlusion, CounterType.PrimitivesGenerated => QueryType.PipelineStatistics, CounterType.TransformFeedbackPrimitivesWritten => QueryType.TransformFeedbackStreamExt, - _ => QueryType.Occlusion + _ => QueryType.Occlusion, }; } @@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries _resetSequence = null; } - public unsafe void End(bool withResult) + public void End(bool withResult) { if (_isSupported) { diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index 2fdddaeab..724588d57 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries public CounterType Type { get; } public bool Disposed { get; private set; } - private Queue<CounterQueueEvent> _events = new Queue<CounterQueueEvent>(); + private readonly Queue<CounterQueueEvent> _events = new(); private CounterQueueEvent _current; private ulong _accumulatedCounter; @@ -26,12 +26,12 @@ namespace Ryujinx.Graphics.Vulkan.Queries private readonly object _lock = new(); - private Queue<BufferedQuery> _queryPool; - private AutoResetEvent _queuedEvent = new AutoResetEvent(false); - private AutoResetEvent _wakeSignal = new AutoResetEvent(false); - private AutoResetEvent _eventConsumed = new AutoResetEvent(false); + private readonly Queue<BufferedQuery> _queryPool; + private readonly AutoResetEvent _queuedEvent = new(false); + private readonly AutoResetEvent _wakeSignal = new(false); + private readonly AutoResetEvent _eventConsumed = new(false); - private Thread _consumerThread; + private readonly Thread _consumerThread; public int ResetSequence { get; private set; } @@ -116,10 +116,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries BufferedQuery result = _queryPool.Dequeue(); return result; } - else - { - return new BufferedQuery(_gd, _device, _pipeline, Type, _gd.IsAmdWindows); - } + + return new BufferedQuery(_gd, _device, _pipeline, Type, _gd.IsAmdWindows); } } diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs index 9d0a674b4..77d9a3557 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs @@ -16,10 +16,10 @@ namespace Ryujinx.Graphics.Vulkan.Queries public ulong DrawIndex { get; } - private CounterQueue _queue; - private BufferedQuery _counter; + private readonly CounterQueue _queue; + private readonly BufferedQuery _counter; - private bool _hostAccessReserved = false; + private bool _hostAccessReserved; private int _refCount = 1; // Starts with a reference from the counter queue. private readonly object _lock = new(); diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs b/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs index feeba4744..9de46e614 100644 --- a/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs +++ b/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs @@ -19,4 +19,4 @@ namespace Ryujinx.Graphics.Vulkan Access = access; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs index eac0d6c20..0b87d8000 100644 --- a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs +++ b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Vulkan ResourceType.StorageBuffer => PipelineBase.StorageSetIndex, ResourceType.TextureAndSampler or ResourceType.BufferTexture => PipelineBase.TextureSetIndex, ResourceType.Image or ResourceType.BufferImage => PipelineBase.ImageSetIndex, - _ => throw new ArgumentException($"Invalid resource type \"{type}\".") + _ => throw new ArgumentException($"Invalid resource type \"{type}\"."), }; ResourceAccess access = IsReadOnlyType(type) ? ResourceAccess.Read : ResourceAccess.ReadWrite; @@ -64,4 +64,4 @@ namespace Ryujinx.Graphics.Vulkan return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs index a95e4dba2..6e9f408f3 100644 --- a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; +using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; namespace Ryujinx.Graphics.Vulkan { @@ -8,13 +9,13 @@ namespace Ryujinx.Graphics.Vulkan private readonly VulkanRenderer _gd; private readonly Auto<DisposableSampler> _sampler; - public unsafe SamplerHolder(VulkanRenderer gd, Device device, GAL.SamplerCreateInfo info) + public unsafe SamplerHolder(VulkanRenderer gd, Device device, SamplerCreateInfo info) { _gd = gd; gd.Samplers.Add(this); - (Filter minFilter, SamplerMipmapMode mipFilter) = EnumConversion.Convert(info.MinFilter); + (Filter minFilter, SamplerMipmapMode mipFilter) = info.MinFilter.Convert(); float minLod = info.MinLod; float maxLod = info.MaxLod; @@ -27,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan var borderColor = GetConstrainedBorderColor(info.BorderColor, out var cantConstrain); - var samplerCreateInfo = new Silk.NET.Vulkan.SamplerCreateInfo() + var samplerCreateInfo = new Silk.NET.Vulkan.SamplerCreateInfo { SType = StructureType.SamplerCreateInfo, MagFilter = info.MagFilter.Convert(), @@ -44,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan MinLod = minLod, MaxLod = maxLod, BorderColor = borderColor, - UnnormalizedCoordinates = false // TODO: Use unnormalized coordinates. + UnnormalizedCoordinates = false, // TODO: Use unnormalized coordinates. }; SamplerCustomBorderColorCreateInfoEXT customBorderColor; @@ -57,10 +58,10 @@ namespace Ryujinx.Graphics.Vulkan info.BorderColor.Blue, info.BorderColor.Alpha); - customBorderColor = new SamplerCustomBorderColorCreateInfoEXT() + customBorderColor = new SamplerCustomBorderColorCreateInfoEXT { SType = StructureType.SamplerCustomBorderColorCreateInfoExt, - CustomBorderColor = color + CustomBorderColor = color, }; samplerCreateInfo.PNext = &customBorderColor; @@ -86,7 +87,8 @@ namespace Ryujinx.Graphics.Vulkan cantConstrain = false; return BorderColor.FloatOpaqueBlack; } - else if (a == 0f) + + if (a == 0f) { cantConstrain = false; return BorderColor.FloatTransparentBlack; diff --git a/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs b/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs index aa1b0eafe..2dd17c51c 100644 --- a/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/SemaphoreHolder.cs @@ -11,16 +11,16 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; private VkSemaphore _semaphore; private int _referenceCount; - public bool _disposed; + private bool _disposed; public unsafe SemaphoreHolder(Vk api, Device device) { _api = api; _device = device; - var semaphoreCreateInfo = new SemaphoreCreateInfo() + var semaphoreCreateInfo = new SemaphoreCreateInfo { - SType = StructureType.SemaphoreCreateInfo + SType = StructureType.SemaphoreCreateInfo, }; api.CreateSemaphore(device, in semaphoreCreateInfo, null, out _semaphore).ThrowOnError(); diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs index d853bb04c..2229785d8 100644 --- a/src/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vulkan { // The shaderc.net dependency's Options constructor and dispose are not thread safe. // Take this lock when using them. - private static object _shaderOptionsLock = new object(); + private static readonly object _shaderOptionsLock = new(); private static readonly IntPtr _ptrMainEntryPointName = Marshal.StringToHGlobalAnsi("main"); @@ -57,11 +57,11 @@ namespace Ryujinx.Graphics.Vulkan fixed (byte* pCode = spirv) { - var shaderModuleCreateInfo = new ShaderModuleCreateInfo() + var shaderModuleCreateInfo = new ShaderModuleCreateInfo { SType = StructureType.ShaderModuleCreateInfo, CodeSize = (uint)spirv.Length, - PCode = (uint*)pCode + PCode = (uint*)pCode, }; api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError(); @@ -80,12 +80,12 @@ namespace Ryujinx.Graphics.Vulkan options = new Options(false) { SourceLanguage = SourceLanguage.Glsl, - TargetSpirVVersion = new SpirVVersion(1, 5) + TargetSpirVVersion = new SpirVVersion(1, 5), }; } options.SetTargetEnvironment(TargetEnvironment.Vulkan, EnvironmentVersion.Vulkan_1_2); - Compiler compiler = new Compiler(options); + Compiler compiler = new(options); var scr = compiler.Compile(glsl, "Ryu", GetShaderCShaderStage(stage)); lock (_shaderOptionsLock) @@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Vulkan byte[] code = new byte[(scr.CodeLength + 3) & ~3]; - spirvBytes.CopyTo(code.AsSpan().Slice(0, (int)scr.CodeLength)); + spirvBytes.CopyTo(code.AsSpan()[..(int)scr.CodeLength]); return code; } @@ -134,12 +134,12 @@ namespace Ryujinx.Graphics.Vulkan public unsafe PipelineShaderStageCreateInfo GetInfo() { - return new PipelineShaderStageCreateInfo() + return new PipelineShaderStageCreateInfo { SType = StructureType.PipelineShaderStageCreateInfo, Stage = _stage, Module = _module, - PName = (byte*)_ptrMainEntryPointName + PName = (byte*)_ptrMainEntryPointName, }; } diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index 2a3f86579..1e56d1e89 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -47,13 +47,13 @@ namespace Ryujinx.Graphics.Vulkan private HashTableSlim<PipelineUid, Auto<DisposablePipeline>> _graphicsPipelineCache; private HashTableSlim<SpecData, Auto<DisposablePipeline>> _computePipelineCache; - private VulkanRenderer _gd; + private readonly VulkanRenderer _gd; private Device _device; private bool _initialized; private ProgramPipelineState _state; private DisposableRenderPass _dummyRenderPass; - private Task _compileTask; + private readonly Task _compileTask; private bool _firstBackgroundUse; public ShaderCollection( @@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Vulkan ShaderStageFlags.GeometryBit => 2, ShaderStageFlags.TessellationControlBit => 3, ShaderStageFlags.TessellationEvaluationBit => 4, - _ => 0 + _ => 0, }; if (shader.StageFlags == ShaderStageFlags.ComputeBit) @@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.Vulkan for (int setIndex = 0; setIndex < sets.Count; setIndex++) { - List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>(); + List<ResourceBindingSegment> currentSegments = new(); ResourceDescriptor currentDescriptor = default; int currentCount = 0; @@ -197,7 +197,7 @@ namespace Ryujinx.Graphics.Vulkan for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) { - List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>(); + List<ResourceBindingSegment> currentSegments = new(); ResourceUsage currentUsage = default; int currentCount = 0; @@ -319,7 +319,7 @@ namespace Ryujinx.Graphics.Vulkan return _infos; } - protected unsafe DisposableRenderPass CreateDummyRenderPass() + protected DisposableRenderPass CreateDummyRenderPass() { if (_dummyRenderPass.Value.Handle != 0) { @@ -331,7 +331,7 @@ namespace Ryujinx.Graphics.Vulkan public void CreateBackgroundComputePipeline() { - PipelineState pipeline = new PipelineState(); + PipelineState pipeline = new(); pipeline.Initialize(); pipeline.Stages[0] = _shaders[0].GetInfo(); @@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Vulkan return _plce.GetNewDescriptorSetCollection(gd, commandBufferIndex, setIndex, out isNew); } - protected virtual unsafe void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index 69d1fa3ce..f6ca2432f 100644 --- a/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -2,8 +2,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders { static class ShaderBinaries { - public static readonly byte[] ChangeBufferStrideShaderSource = new byte[] - { + public static readonly byte[] ChangeBufferStrideShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x60, 0x11, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, @@ -242,11 +241,10 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x06, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x57, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x59, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00 + 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorBlitClearAlphaFragmentShaderSource = new byte[] - { + public static readonly byte[] ColorBlitClearAlphaFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -290,8 +288,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x09, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorBlitFragmentShaderSource = new byte[] - { + public static readonly byte[] ColorBlitFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -329,8 +326,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorBlitMsFragmentShaderSource = new byte[] - { + public static readonly byte[] ColorBlitMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, @@ -384,8 +380,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorBlitVertexShaderSource = new byte[] - { + public static readonly byte[] ColorBlitVertexShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -486,8 +481,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x3C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorClearFFragmentShaderSource = new byte[] - { + public static readonly byte[] ColorClearFFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -514,8 +508,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x0C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorClearSIFragmentShaderSource = new byte[] - { + public static readonly byte[] ColorClearSIFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -545,8 +538,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorClearUIFragmentShaderSource = new byte[] - { + public static readonly byte[] ColorClearUIFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -576,8 +568,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorClearVertexShaderSource = new byte[] - { + public static readonly byte[] ColorClearVertexShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -669,8 +660,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x35, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorCopyShorteningComputeShaderSource = new byte[] - { + public static readonly byte[] ColorCopyShorteningComputeShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, @@ -801,8 +791,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorCopyToNonMsComputeShaderSource = new byte[] - { + public static readonly byte[] ColorCopyToNonMsComputeShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, @@ -933,8 +922,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x84, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorCopyWideningComputeShaderSource = new byte[] - { + public static readonly byte[] ColorCopyWideningComputeShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, @@ -1060,8 +1048,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xF8, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorDrawToMsVertexShaderSource = new byte[] - { + public static readonly byte[] ColorDrawToMsVertexShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -1133,8 +1120,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x2D, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ColorDrawToMsFragmentShaderSource = new byte[] - { + public static readonly byte[] ColorDrawToMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, @@ -1236,8 +1222,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ConvertD32S8ToD24S8ShaderSource = new byte[] - { + public static readonly byte[] ConvertD32S8ToD24S8ShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -1441,10 +1426,9 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x3E, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x41, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x43, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 - }; +, }; - public static readonly byte[] ConvertIndexBufferShaderSource = new byte[] - { + public static readonly byte[] ConvertIndexBufferShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x61, 0x11, 0x00, 0x00, 0x0A, 0x00, 0x07, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F, @@ -1638,8 +1622,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] ConvertIndirectDataShaderSource = new byte[] - { + public static readonly byte[] ConvertIndirectDataShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -1981,8 +1964,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xF8, 0x00, 0x02, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] DepthBlitFragmentShaderSource = new byte[] - { + public static readonly byte[] DepthBlitFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -2023,8 +2005,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] DepthBlitMsFragmentShaderSource = new byte[] - { + public static readonly byte[] DepthBlitMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, @@ -2081,8 +2062,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] DepthDrawToMsFragmentShaderSource = new byte[] - { + public static readonly byte[] DepthDrawToMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, @@ -2185,8 +2165,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x5D, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] DepthDrawToNonMsFragmentShaderSource = new byte[] - { + public static readonly byte[] DepthDrawToNonMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, @@ -2288,8 +2267,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] StencilBlitFragmentShaderSource = new byte[] - { + public static readonly byte[] StencilBlitFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, @@ -2336,8 +2314,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] StencilBlitMsFragmentShaderSource = new byte[] - { + public static readonly byte[] StencilBlitMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, @@ -2399,8 +2376,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] StencilDrawToMsFragmentShaderSource = new byte[] - { + public static readonly byte[] StencilDrawToMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, @@ -2509,8 +2485,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x38, 0x00, 0x01, 0x00, }; - public static readonly byte[] StencilDrawToNonMsFragmentShaderSource = new byte[] - { + public static readonly byte[] StencilDrawToNonMsFragmentShaderSource = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, @@ -2617,4 +2592,4 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x5F, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/SpecInfo.cs b/src/Ryujinx.Graphics.Vulkan/SpecInfo.cs index 4d226f615..ecb2abfc6 100644 --- a/src/Ryujinx.Graphics.Vulkan/SpecInfo.cs +++ b/src/Ryujinx.Graphics.Vulkan/SpecInfo.cs @@ -1,7 +1,5 @@ using Silk.NET.Vulkan; using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Vulkan { @@ -13,7 +11,7 @@ namespace Ryujinx.Graphics.Vulkan Int64, Float16, Float32, - Float64 + Float64, } sealed class SpecDescription @@ -36,10 +34,10 @@ namespace Ryujinx.Graphics.Vulkan structSize += typeSize; } - Info = new SpecializationInfo() + Info = new SpecializationInfo { DataSize = structSize, - MapEntryCount = (uint)count + MapEntryCount = (uint)count, }; } @@ -54,10 +52,10 @@ namespace Ryujinx.Graphics.Vulkan structSize = Math.Max(structSize, map[i].Offset + (uint)map[i].Size); } - Info = new SpecializationInfo() + Info = new SpecializationInfo { DataSize = structSize, - MapEntryCount = (uint)map.Length + MapEntryCount = (uint)map.Length, }; } @@ -66,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan SpecConstType.Int16 or SpecConstType.Float16 => 2, SpecConstType.Bool32 or SpecConstType.Int32 or SpecConstType.Float32 => 4, SpecConstType.Int64 or SpecConstType.Float64 => 8, - _ => throw new ArgumentOutOfRangeException(nameof(type)) + _ => throw new ArgumentOutOfRangeException(nameof(type)), }; private SpecDescription() @@ -99,4 +97,4 @@ namespace Ryujinx.Graphics.Vulkan public override bool Equals(object obj) => obj is SpecData other && Equals(other); public bool Equals(ref SpecData other) => _data.AsSpan().SequenceEqual(other._data); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs b/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs index 4e3c1deea..00fa6477d 100644 --- a/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Vulkan _freeSize = BufferSize; } - public unsafe void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data) + public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data) { bool isRender = cbs != null; CommandBufferScoped scoped = cbs ?? cbp.Rent(); @@ -72,10 +72,10 @@ namespace Ryujinx.Graphics.Vulkan int chunkSize = Math.Min(_freeSize, data.Length); - PushDataImpl(scoped, dst, dstOffset, data.Slice(0, chunkSize)); + PushDataImpl(scoped, dst, dstOffset, data[..chunkSize]); dstOffset += chunkSize; - data = data.Slice(chunkSize); + data = data[chunkSize..]; } if (!isRender) @@ -93,8 +93,8 @@ namespace Ryujinx.Graphics.Vulkan int capacity = BufferSize - offset; if (capacity < data.Length) { - _buffer.SetDataUnchecked(offset, data.Slice(0, capacity)); - _buffer.SetDataUnchecked(0, data.Slice(capacity)); + _buffer.SetDataUnchecked(offset, data[..capacity]); + _buffer.SetDataUnchecked(0, data[capacity..]); BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity); BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity); @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Vulkan _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length)); } - public unsafe bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data) + public bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data) { if (data.Length > BufferSize) { diff --git a/src/Ryujinx.Graphics.Vulkan/SyncManager.cs b/src/Ryujinx.Graphics.Vulkan/SyncManager.cs index b3f6e8e5a..35e9ab971 100644 --- a/src/Ryujinx.Graphics.Vulkan/SyncManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/SyncManager.cs @@ -21,13 +21,13 @@ namespace Ryujinx.Graphics.Vulkan } } - private ulong _firstHandle = 0; + private ulong _firstHandle; private readonly VulkanRenderer _gd; private readonly Device _device; - private List<SyncHandle> _handles; - private ulong FlushId; - private long WaitTicks; + private readonly List<SyncHandle> _handles; + private ulong _flushId; + private long _waitTicks; public SyncManager(VulkanRenderer gd, Device device) { @@ -38,13 +38,13 @@ namespace Ryujinx.Graphics.Vulkan public void RegisterFlush() { - FlushId++; + _flushId++; } public void Create(ulong id, bool strict) { - ulong flushId = FlushId; - MultiFenceHolder waitable = new MultiFenceHolder(); + ulong flushId = _flushId; + MultiFenceHolder waitable = new(); if (strict || _gd.InterruptAction == null) { _gd.FlushAllCommands(); @@ -58,11 +58,11 @@ namespace Ryujinx.Graphics.Vulkan _gd.CommandBufferPool.AddInUseWaitable(waitable); } - SyncHandle handle = new SyncHandle + SyncHandle handle = new() { ID = id, Waitable = waitable, - FlushId = flushId + FlushId = flushId, }; lock (_handles) @@ -132,11 +132,11 @@ namespace Ryujinx.Graphics.Vulkan long beforeTicks = Stopwatch.GetTimestamp(); - if (result.NeedsFlush(FlushId)) + if (result.NeedsFlush(_flushId)) { _gd.InterruptAction(() => { - if (result.NeedsFlush(FlushId)) + if (result.NeedsFlush(_flushId)) { _gd.FlushAllCommands(); } @@ -158,7 +158,7 @@ namespace Ryujinx.Graphics.Vulkan } else { - WaitTicks += Stopwatch.GetTimestamp() - beforeTicks; + _waitTicks += Stopwatch.GetTimestamp() - beforeTicks; result.Signalled = true; } } @@ -177,7 +177,10 @@ namespace Ryujinx.Graphics.Vulkan first = _handles.FirstOrDefault(); } - if (first == null || first.NeedsFlush(FlushId)) break; + if (first == null || first.NeedsFlush(_flushId)) + { + break; + } bool signaled = first.Waitable.WaitForFences(_gd.Api, _device, 0); if (signaled) @@ -192,7 +195,8 @@ namespace Ryujinx.Graphics.Vulkan first.Waitable = null; } } - } else + } + else { // This sync handle and any following have not been reached yet. break; @@ -202,8 +206,8 @@ namespace Ryujinx.Graphics.Vulkan public long GetAndResetWaitTicks() { - long result = WaitTicks; - WaitTicks = 0; + long result = _waitTicks; + _waitTicks = 0; return result; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index 66951153d..e7d24a007 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; using System.Collections.Generic; +using Format = Ryujinx.Graphics.GAL.Format; using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan @@ -15,7 +16,7 @@ namespace Ryujinx.Graphics.Vulkan private int _offset; private int _size; private Auto<DisposableBufferView> _bufferView; - private Dictionary<GAL.Format, Auto<DisposableBufferView>> _selfManagedViews; + private Dictionary<Format, Auto<DisposableBufferView>> _selfManagedViews; private int _bufferCount; @@ -131,15 +132,12 @@ namespace Ryujinx.Graphics.Vulkan public BufferView GetBufferView(CommandBufferScoped cbs) { - if (_bufferView == null) - { - _bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size, ReleaseImpl); - } + _bufferView ??= _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size, ReleaseImpl); return _bufferView?.Get(cbs, _offset, _size).Value ?? default; } - public BufferView GetBufferView(CommandBufferScoped cbs, GAL.Format format) + public BufferView GetBufferView(CommandBufferScoped cbs, Format format) { var vkFormat = FormatTable.GetFormat(format); if (vkFormat == VkFormat) @@ -156,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan if (bufferView != null) { - (_selfManagedViews ??= new Dictionary<GAL.Format, Auto<DisposableBufferView>>()).Add(format, bufferView); + (_selfManagedViews ??= new Dictionary<Format, Auto<DisposableBufferView>>()).Add(format, bufferView); } return bufferView?.Get(cbs, _offset, _size).Value ?? default; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs index c7ce2d99f..717935fb4 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs @@ -80,12 +80,12 @@ namespace Ryujinx.Graphics.Vulkan (srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level); (dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level); - var region = new ImageBlit() + var region = new ImageBlit { SrcSubresource = srcSl, SrcOffsets = srcOffsets, DstSubresource = dstSl, - DstOffsets = dstOffsets + DstOffsets = dstOffsets, }; api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter); @@ -219,21 +219,18 @@ namespace Ryujinx.Graphics.Vulkan int dstZ; int dstLayer; - int dstDepth; int dstLayers; if (dstInfo.Target == Target.Texture3D) { dstZ = dstDepthOrLayer; dstLayer = 0; - dstDepth = depthOrLayers; dstLayers = 1; } else { dstZ = 0; dstLayer = dstDepthOrLayer; - dstDepth = 1; dstLayers = depthOrLayers; } @@ -366,20 +363,20 @@ namespace Ryujinx.Graphics.Vulkan var dsAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 0, ImageLayout.General); var dsResolveAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 1, ImageLayout.General); - var subpassDsResolve = new SubpassDescriptionDepthStencilResolve() + var subpassDsResolve = new SubpassDescriptionDepthStencilResolve { SType = StructureType.SubpassDescriptionDepthStencilResolve, PDepthStencilResolveAttachment = &dsResolveAttachmentReference, DepthResolveMode = ResolveModeFlags.SampleZeroBit, - StencilResolveMode = ResolveModeFlags.SampleZeroBit + StencilResolveMode = ResolveModeFlags.SampleZeroBit, }; - var subpass = new SubpassDescription2() + var subpass = new SubpassDescription2 { SType = StructureType.SubpassDescription2, PipelineBindPoint = PipelineBindPoint.Graphics, PDepthStencilAttachment = &dsAttachmentReference, - PNext = &subpassDsResolve + PNext = &subpassDsResolve, }; AttachmentDescription2[] attachmentDescs = new AttachmentDescription2[2]; @@ -414,7 +411,7 @@ namespace Ryujinx.Graphics.Vulkan fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs) { - var renderPassCreateInfo = new RenderPassCreateInfo2() + var renderPassCreateInfo = new RenderPassCreateInfo2 { SType = StructureType.RenderPassCreateInfo2, PAttachments = pAttachmentDescs, @@ -422,7 +419,7 @@ namespace Ryujinx.Graphics.Vulkan PSubpasses = &subpass, SubpassCount = 1, PDependencies = &subpassDependency, - DependencyCount = 1 + DependencyCount = 1, }; gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); @@ -437,7 +434,7 @@ namespace Ryujinx.Graphics.Vulkan attachments[0] = srcView.Get(cbs).Value; attachments[1] = dstView.Get(cbs).Value; - var framebufferCreateInfo = new FramebufferCreateInfo() + var framebufferCreateInfo = new FramebufferCreateInfo { SType = StructureType.FramebufferCreateInfo, RenderPass = rp.Get(cbs).Value, @@ -445,23 +442,23 @@ namespace Ryujinx.Graphics.Vulkan PAttachments = attachments, Width = (uint)src.Width, Height = (uint)src.Height, - Layers = (uint)src.Layers + Layers = (uint)src.Layers, }; gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); - using var fb = new Auto<DisposableFramebuffer>(new DisposableFramebuffer(gd.Api, device, framebuffer), null, new[] { srcView, dstView }); + using var fb = new Auto<DisposableFramebuffer>(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView); var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height)); var clearValue = new ClearValue(); - var renderPassBeginInfo = new RenderPassBeginInfo() + var renderPassBeginInfo = new RenderPassBeginInfo { SType = StructureType.RenderPassBeginInfo, RenderPass = rp.Get(cbs).Value, Framebuffer = fb.Get(cbs).Value, RenderArea = renderArea, PClearValues = &clearValue, - ClearValueCount = 1 + ClearValueCount = 1, }; // The resolve operation happens at the end of the subpass, so let's just do a begin/end diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 4edb6f2fe..2a51c132a 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -4,6 +4,7 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Numerics; +using Format = Ryujinx.Graphics.GAL.Format; using VkBuffer = Silk.NET.Vulkan.Buffer; using VkFormat = Silk.NET.Vulkan.Format; @@ -42,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly Auto<MemoryAllocation> _allocationAuto; private Auto<MemoryAllocation> _foreignAllocationAuto; - private Dictionary<GAL.Format, TextureStorage> _aliasedStorages; + private Dictionary<Format, TextureStorage> _aliasedStorages; private AccessFlags _lastModificationAccess; private PipelineStageFlags _lastModificationStage; @@ -50,7 +51,7 @@ namespace Ryujinx.Graphics.Vulkan private PipelineStageFlags _lastReadStage; private int _viewsCount; - private ulong _size; + private readonly ulong _size; public VkFormat VkFormat { get; } public float ScaleFactor { get; } @@ -98,7 +99,7 @@ namespace Ryujinx.Graphics.Vulkan flags |= ImageCreateFlags.Create2DArrayCompatibleBit; } - var imageCreateInfo = new ImageCreateInfo() + var imageCreateInfo = new ImageCreateInfo { SType = StructureType.ImageCreateInfo, ImageType = type, @@ -111,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan Usage = usage, SharingMode = SharingMode.Exclusive, InitialLayout = ImageLayout.Undefined, - Flags = flags + Flags = flags, }; gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError(); @@ -150,27 +151,27 @@ namespace Ryujinx.Graphics.Vulkan } } - public TextureStorage CreateAliasedColorForDepthStorageUnsafe(GAL.Format format) + public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format) { var colorFormat = format switch { - GAL.Format.S8Uint => GAL.Format.R8Unorm, - GAL.Format.D16Unorm => GAL.Format.R16Unorm, - GAL.Format.S8UintD24Unorm => GAL.Format.R8G8B8A8Unorm, - GAL.Format.D32Float => GAL.Format.R32Float, - GAL.Format.D24UnormS8Uint => GAL.Format.R8G8B8A8Unorm, - GAL.Format.D32FloatS8Uint => GAL.Format.R32G32Float, - _ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format.") + Format.S8Uint => Format.R8Unorm, + Format.D16Unorm => Format.R16Unorm, + Format.S8UintD24Unorm => Format.R8G8B8A8Unorm, + Format.D32Float => Format.R32Float, + Format.D24UnormS8Uint => Format.R8G8B8A8Unorm, + Format.D32FloatS8Uint => Format.R32G32Float, + _ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format."), }; return CreateAliasedStorageUnsafe(colorFormat); } - public TextureStorage CreateAliasedStorageUnsafe(GAL.Format format) + public TextureStorage CreateAliasedStorageUnsafe(Format format) { if (_aliasedStorages == null || !_aliasedStorages.TryGetValue(format, out var storage)) { - _aliasedStorages ??= new Dictionary<GAL.Format, TextureStorage>(); + _aliasedStorages ??= new Dictionary<Format, TextureStorage>(); var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel); @@ -182,14 +183,14 @@ namespace Ryujinx.Graphics.Vulkan return storage; } - public static TextureCreateInfo NewCreateInfoWith(ref TextureCreateInfo info, GAL.Format format, int bytesPerPixel) + public static TextureCreateInfo NewCreateInfoWith(ref TextureCreateInfo info, Format format, int bytesPerPixel) { return NewCreateInfoWith(ref info, format, bytesPerPixel, info.Width, info.Height); } public static TextureCreateInfo NewCreateInfoWith( ref TextureCreateInfo info, - GAL.Format format, + Format format, int bytesPerPixel, int width, int height) @@ -262,7 +263,7 @@ namespace Ryujinx.Graphics.Vulkan var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, (uint)_info.Levels, 0, (uint)_info.GetLayers()); - var barrier = new ImageMemoryBarrier() + var barrier = new ImageMemoryBarrier { SType = StructureType.ImageMemoryBarrier, SrcAccessMask = 0, @@ -272,7 +273,7 @@ namespace Ryujinx.Graphics.Vulkan SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, DstQueueFamilyIndex = Vk.QueueFamilyIgnored, Image = _imageAuto.Get(cbs).Value, - SubresourceRange = subresourceRange + SubresourceRange = subresourceRange, }; _gd.Api.CmdPipelineBarrier( @@ -293,7 +294,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsage(GAL.Format format, Target target, bool supportsMsStorage) + public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage) { var usage = DefaultUsageFlags; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index eb094b3e6..6151d5a9e 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; using System.Collections.Generic; +using Format = Ryujinx.Graphics.GAL.Format; using VkBuffer = Silk.NET.Vulkan.Buffer; using VkFormat = Silk.NET.Vulkan.Format; @@ -18,9 +19,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly Auto<DisposableImageView> _imageViewDraw; private readonly Auto<DisposableImageView> _imageViewIdentity; private readonly Auto<DisposableImageView> _imageView2dArray; - private Dictionary<GAL.Format, TextureView> _selfManagedViews; + private Dictionary<Format, TextureView> _selfManagedViews; - private TextureCreateInfo _info; + private readonly TextureCreateInfo _info; public TextureCreateInfo Info => _info; @@ -68,16 +69,13 @@ namespace Ryujinx.Graphics.Vulkan var swizzleB = info.SwizzleB.Convert(); var swizzleA = info.SwizzleA.Convert(); - if (info.Format == GAL.Format.R5G5B5A1Unorm || - info.Format == GAL.Format.R5G5B5X1Unorm || - info.Format == GAL.Format.R5G6B5Unorm) + if (info.Format == Format.R5G5B5A1Unorm || + info.Format == Format.R5G5B5X1Unorm || + info.Format == Format.R5G6B5Unorm) { - var temp = swizzleR; - - swizzleR = swizzleB; - swizzleB = temp; + (swizzleB, swizzleR) = (swizzleR, swizzleB); } - else if (VkFormat == VkFormat.R4G4B4A4UnormPack16 || info.Format == GAL.Format.A1B5G5R5Unorm) + else if (VkFormat == VkFormat.R4G4B4A4UnormPack16 || info.Format == Format.A1B5G5R5Unorm) { var tempB = swizzleB; var tempA = swizzleA; @@ -98,13 +96,13 @@ namespace Ryujinx.Graphics.Vulkan unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags) { - var usage = new ImageViewUsageCreateInfo() + var usage = new ImageViewUsageCreateInfo { SType = StructureType.ImageViewUsageCreateInfo, - Usage = usageFlags + Usage = usageFlags, }; - var imageCreateInfo = new ImageViewCreateInfo() + var imageCreateInfo = new ImageViewCreateInfo { SType = StructureType.ImageViewCreateInfo, Image = storage.GetImageForViewCreation(), @@ -112,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan Format = format, Components = cm, SubresourceRange = sr, - PNext = &usage + PNext = &usage, }; gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError(); @@ -354,8 +352,9 @@ namespace Ryujinx.Graphics.Vulkan return; } - else if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitSrcBit, srcFormat) && - _gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitDstBit, dstFormat)) + + if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitSrcBit, srcFormat) && + _gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitDstBit, dstFormat)) { TextureCopy.Blit( _gd.Api, @@ -444,7 +443,7 @@ namespace Ryujinx.Graphics.Vulkan int layers, int levels) { - ImageMemoryBarrier memoryBarrier = new ImageMemoryBarrier() + ImageMemoryBarrier memoryBarrier = new() { SType = StructureType.ImageMemoryBarrier, SrcAccessMask = srcAccessMask, @@ -454,7 +453,7 @@ namespace Ryujinx.Graphics.Vulkan Image = image, OldLayout = ImageLayout.General, NewLayout = ImageLayout.General, - SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers) + SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers), }; api.CmdPipelineBarrier( @@ -470,7 +469,7 @@ namespace Ryujinx.Graphics.Vulkan memoryBarrier); } - public TextureView GetView(GAL.Format format) + public TextureView GetView(Format format) { if (format == Info.Format) { @@ -499,7 +498,7 @@ namespace Ryujinx.Graphics.Vulkan Info.SwizzleB, Info.SwizzleA), 0, 0); - (_selfManagedViews ??= new Dictionary<GAL.Format, TextureView>()).Add(format, view); + (_selfManagedViews ??= new Dictionary<Format, TextureView>()).Add(format, view); return view; } @@ -543,10 +542,8 @@ namespace Ryujinx.Graphics.Vulkan return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer())); } - else - { - return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer())); - } + + return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer())); } public PinnedSpan<byte> GetData(int layer, int level) @@ -559,10 +556,8 @@ namespace Ryujinx.Graphics.Vulkan return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level)); } - else - { - return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); - } + + return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); } public void CopyTo(BufferRange range, int layer, int level, int stride) @@ -686,11 +681,11 @@ namespace Ryujinx.Graphics.Vulkan return length; } - private GAL.Format GetCompatibleGalFormat(GAL.Format format) + private Format GetCompatibleGalFormat(Format format) { if (NeedsD24S8Conversion()) { - return GAL.Format.D32FloatS8Uint; + return Format.D32FloatS8Uint; } return format; diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs index 5e0290c0a..2d2f17b25 100644 --- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan Broadcom, Qualcomm, Apple, - Unknown + Unknown, } static partial class VendorUtils @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Vulkan 0x14E4 => Vendor.Broadcom, 0x8086 => Vendor.Intel, 0x5143 => Vendor.Qualcomm, - _ => Vendor.Unknown + _ => Vendor.Unknown, }; } @@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Vulkan 0x10004 => "Codeplay Software Ltd.", 0x10005 => "Mesa", 0x10006 => "PoCL", - _ => $"0x{id:X}" + _ => $"0x{id:X}", }; } } diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index 2118bfaeb..cbbd829ab 100644 --- a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -1,10 +1,10 @@ -using BufferHandle = Ryujinx.Graphics.GAL.BufferHandle; +using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Vulkan { internal struct VertexBufferState { - public static VertexBufferState Null => new VertexBufferState(null, 0, 0, 0); + public static VertexBufferState Null => new(null, 0, 0, 0); private readonly int _offset; private readonly int _size; @@ -74,17 +74,15 @@ namespace Ryujinx.Graphics.Vulkan return; } - else + + autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size); + + // The original stride must be reapplied in case it was rewritten. + state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; + + if (_offset >= size) { - autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size); - - // The original stride must be reapplied in case it was rewritten. - state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; - - if (_offset >= size) - { - autoBuffer = null; - } + autoBuffer = null; } } @@ -96,12 +94,12 @@ namespace Ryujinx.Graphics.Vulkan } } - public bool BoundEquals(Auto<DisposableBuffer> buffer) + public readonly bool BoundEquals(Auto<DisposableBuffer> buffer) { return _buffer == buffer; } - public bool Matches(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0) + public readonly bool Matches(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0) { return _buffer == buffer && DescriptorIndex == descriptorIndex && _offset == offset && _size == size && _stride == stride; } @@ -117,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public void Dispose() + public readonly void Dispose() { // Only dispose if this buffer is not refetched on each bind. diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs index bceaeb8c9..8d6b0a055 100644 --- a/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs @@ -1,21 +1,19 @@ -using Silk.NET.Vulkan; -using System; - +using System; using VkBuffer = Silk.NET.Vulkan.Buffer; namespace Ryujinx.Graphics.Vulkan { internal class VertexBufferUpdater : IDisposable { - private VulkanRenderer _gd; + private readonly VulkanRenderer _gd; private uint _baseBinding; private uint _count; - private NativeArray<VkBuffer> _buffers; - private NativeArray<ulong> _offsets; - private NativeArray<ulong> _sizes; - private NativeArray<ulong> _strides; + private readonly NativeArray<VkBuffer> _buffers; + private readonly NativeArray<ulong> _offsets; + private readonly NativeArray<ulong> _sizes; + private readonly NativeArray<ulong> _strides; public VertexBufferUpdater(VulkanRenderer gd) { diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs b/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs index 7e39a251e..82e30f5be 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs @@ -10,8 +10,7 @@ namespace Ryujinx.Graphics.Vulkan { class VulkanDebugMessenger : IDisposable { - private static string[] _excludedMessages = new string[] - { + private static readonly string[] _excludedMessages = { // NOTE: Done on purpose right now. "UNASSIGNED-CoreValidation-Shader-OutputNotConsumed", // TODO: Figure out if fixable @@ -19,7 +18,7 @@ namespace Ryujinx.Graphics.Vulkan // TODO: Might be worth looking into making this happy to possibly optimize copies. "UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout", // TODO: Fix this, it's causing too much noise right now. - "VUID-VkSubpassDependency-srcSubpass-00867" + "VUID-VkSubpassDependency-srcSubpass-00867", }; private readonly Vk _api; @@ -48,7 +47,7 @@ namespace Ryujinx.Graphics.Vulkan private Result TryInitialize(out DebugUtilsMessengerEXT? debugUtilsMessengerHandle) { debugUtilsMessengerHandle = null; - + if (_debugUtils != null && _logLevel != GraphicsDebugLevel.None) { var messageType = _logLevel switch @@ -59,7 +58,7 @@ namespace Ryujinx.Graphics.Vulkan GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt, - _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".") + _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\"."), }; var messageSeverity = _logLevel switch @@ -71,14 +70,14 @@ namespace Ryujinx.Graphics.Vulkan DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt, - _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".") + _ => throw new ArgumentException($"Invalid log level \"{_logLevel}\"."), }; - var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT() + var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT { SType = StructureType.DebugUtilsMessengerCreateInfoExt, MessageType = messageType, - MessageSeverity = messageSeverity + MessageSeverity = messageSeverity, }; unsafe diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 51a3b129a..b0a3ba37b 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -14,14 +14,13 @@ namespace Ryujinx.Graphics.Vulkan public unsafe static class VulkanInitialization { private const uint InvalidIndex = uint.MaxValue; - private static uint MinimalVulkanVersion = Vk.Version11.Value; - private static uint MinimalInstanceVulkanVersion = Vk.Version12.Value; - private static uint MaximumVulkanVersion = Vk.Version12.Value; + private static readonly uint _minimalVulkanVersion = Vk.Version11.Value; + private static readonly uint _minimalInstanceVulkanVersion = Vk.Version12.Value; + private static readonly uint _maximumVulkanVersion = Vk.Version12.Value; private const string AppName = "Ryujinx.Graphics.Vulkan"; private const int QueuesCount = 2; - private static readonly string[] _desirableExtensions = new string[] - { + private static readonly string[] _desirableExtensions = { ExtConditionalRendering.ExtensionName, ExtExtendedDynamicState.ExtensionName, ExtTransformFeedback.ExtensionName, @@ -42,12 +41,11 @@ namespace Ryujinx.Graphics.Vulkan "VK_NV_geometry_shader_passthrough", "VK_NV_viewport_array2", "VK_EXT_depth_clip_control", - "VK_KHR_portability_subset" // As per spec, we should enable this if present. + "VK_KHR_portability_subset", // As per spec, we should enable this if present. }; - private static readonly string[] _requiredExtensions = new string[] - { - KhrSwapchain.ExtensionName + private static readonly string[] _requiredExtensions = { + KhrSwapchain.ExtensionName, }; internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions) @@ -89,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan ApplicationVersion = 1, PEngineName = (byte*)appName, EngineVersion = 1, - ApiVersion = MaximumVulkanVersion + ApiVersion = _maximumVulkanVersion, }; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; @@ -112,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan PpEnabledExtensionNames = (byte**)ppEnabledExtensions, PpEnabledLayerNames = (byte**)ppEnabledLayers, EnabledExtensionCount = (uint)enabledExtensions.Length, - EnabledLayerCount = (uint)enabledLayers.Count + EnabledLayerCount = (uint)enabledLayers.Count, }; Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var instance); @@ -169,7 +167,7 @@ namespace Ryujinx.Graphics.Vulkan ApplicationVersion = 1, PEngineName = (byte*)appName, EngineVersion = 1, - ApiVersion = MaximumVulkanVersion + ApiVersion = _maximumVulkanVersion, }; var instanceCreateInfo = new InstanceCreateInfo @@ -179,7 +177,7 @@ namespace Ryujinx.Graphics.Vulkan PpEnabledExtensionNames = null, PpEnabledLayerNames = null, EnabledExtensionCount = 0, - EnabledLayerCount = 0 + EnabledLayerCount = 0, }; Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var rawInstance); @@ -192,18 +190,18 @@ namespace Ryujinx.Graphics.Vulkan // We currently assume that the instance is compatible with Vulkan 1.2 // TODO: Remove this once we relax our initialization codepaths. - if (instance.InstanceVersion < MinimalInstanceVulkanVersion) + if (instance.InstanceVersion < _minimalInstanceVulkanVersion) { return Array.Empty<DeviceInfo>(); } instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError(); - List<DeviceInfo> deviceInfos = new List<DeviceInfo>(); + List<DeviceInfo> deviceInfos = new(); foreach (VulkanPhysicalDevice physicalDevice in physicalDevices) { - if (physicalDevice.PhysicalDeviceProperties.ApiVersion < MinimalVulkanVersion) + if (physicalDevice.PhysicalDeviceProperties.ApiVersion < _minimalVulkanVersion) { continue; } @@ -278,33 +276,33 @@ namespace Ryujinx.Graphics.Vulkan queuePriorities[i] = 1f; } - var queueCreateInfo = new DeviceQueueCreateInfo() + var queueCreateInfo = new DeviceQueueCreateInfo { SType = StructureType.DeviceQueueCreateInfo, QueueFamilyIndex = queueFamilyIndex, QueueCount = queueCount, - PQueuePriorities = queuePriorities + PQueuePriorities = queuePriorities, }; bool useRobustBufferAccess = VendorUtils.FromId(physicalDevice.PhysicalDeviceProperties.VendorID) == Vendor.Nvidia; - PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() + PhysicalDeviceFeatures2 features2 = new() { - SType = StructureType.PhysicalDeviceFeatures2 + SType = StructureType.PhysicalDeviceFeatures2, }; - PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new PhysicalDeviceVulkan11Features() + PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new() { SType = StructureType.PhysicalDeviceVulkan11Features, - PNext = features2.PNext + PNext = features2.PNext, }; features2.PNext = &supportedFeaturesVk11; - PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() + PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new() { SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, - PNext = features2.PNext + PNext = features2.PNext, }; if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color")) @@ -312,10 +310,10 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesCustomBorderColor; } - PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() + PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new() { SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, - PNext = features2.PNext + PNext = features2.PNext, }; if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) @@ -323,10 +321,10 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart; } - PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new() { SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, - PNext = features2.PNext + PNext = features2.PNext, }; if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName)) @@ -334,9 +332,9 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesTransformFeedback; } - PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new() { - SType = StructureType.PhysicalDeviceRobustness2FeaturesExt + SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, }; if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) @@ -346,10 +344,10 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesRobustness2; } - PhysicalDeviceDepthClipControlFeaturesEXT supportedFeaturesDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + PhysicalDeviceDepthClipControlFeaturesEXT supportedFeaturesDepthClipControl = new() { SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, - PNext = features2.PNext + PNext = features2.PNext, }; if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control")) @@ -361,7 +359,7 @@ namespace Ryujinx.Graphics.Vulkan var supportedFeatures = features2.Features; - var features = new PhysicalDeviceFeatures() + var features = new PhysicalDeviceFeatures { DepthBiasClamp = supportedFeatures.DepthBiasClamp, DepthClamp = supportedFeatures.DepthClamp, @@ -383,7 +381,7 @@ namespace Ryujinx.Graphics.Vulkan // ShaderStorageImageWriteWithoutFormat = true, TessellationShader = supportedFeatures.TessellationShader, VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics, - RobustBufferAccess = useRobustBufferAccess + RobustBufferAccess = useRobustBufferAccess, }; void* pExtendedFeatures = null; @@ -392,11 +390,11 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName)) { - featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() + featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT { SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, PNext = pExtendedFeatures, - TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback + TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback, }; pExtendedFeatures = &featuresTransformFeedback; @@ -406,12 +404,12 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) { - featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() + featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT { SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, PNext = pExtendedFeatures, PrimitiveTopologyListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, - PrimitiveTopologyPatchListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart + PrimitiveTopologyPatchListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, }; pExtendedFeatures = &featuresPrimitiveTopologyListRestart; @@ -421,41 +419,41 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) { - featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT { SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, PNext = pExtendedFeatures, - NullDescriptor = supportedFeaturesRobustness2.NullDescriptor + NullDescriptor = supportedFeaturesRobustness2.NullDescriptor, }; pExtendedFeatures = &featuresRobustness2; } - var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT() + var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT { SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt, PNext = pExtendedFeatures, - ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName) + ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), }; pExtendedFeatures = &featuresExtendedDynamicState; - var featuresVk11 = new PhysicalDeviceVulkan11Features() + var featuresVk11 = new PhysicalDeviceVulkan11Features { SType = StructureType.PhysicalDeviceVulkan11Features, PNext = pExtendedFeatures, - ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters + ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters, }; pExtendedFeatures = &featuresVk11; - var featuresVk12 = new PhysicalDeviceVulkan12Features() + var featuresVk12 = new PhysicalDeviceVulkan12Features { SType = StructureType.PhysicalDeviceVulkan12Features, PNext = pExtendedFeatures, DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"), DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName), - UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout") + UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout"), }; pExtendedFeatures = &featuresVk12; @@ -464,11 +462,11 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8")) { - featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT() + featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT { SType = StructureType.PhysicalDeviceIndexTypeUint8FeaturesExt, PNext = pExtendedFeatures, - IndexTypeUint8 = true + IndexTypeUint8 = true, }; pExtendedFeatures = &featuresIndexU8; @@ -478,11 +476,11 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock")) { - featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT() + featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT { SType = StructureType.PhysicalDeviceFragmentShaderInterlockFeaturesExt, PNext = pExtendedFeatures, - FragmentShaderPixelInterlock = true + FragmentShaderPixelInterlock = true, }; pExtendedFeatures = &featuresFragmentShaderInterlock; @@ -492,11 +490,11 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control")) { - featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT() + featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT { SType = StructureType.PhysicalDeviceSubgroupSizeControlFeaturesExt, PNext = pExtendedFeatures, - SubgroupSizeControl = true + SubgroupSizeControl = true, }; pExtendedFeatures = &featuresSubgroupSizeControl; @@ -508,7 +506,7 @@ namespace Ryujinx.Graphics.Vulkan supportedFeaturesCustomBorderColor.CustomBorderColors && supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) { - featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() + featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT { SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, PNext = pExtendedFeatures, @@ -524,11 +522,11 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control") && supportedFeaturesDepthClipControl.DepthClipControl) { - featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT { SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, PNext = pExtendedFeatures, - DepthClipControl = true + DepthClipControl = true, }; pExtendedFeatures = &featuresDepthClipControl; @@ -543,7 +541,7 @@ namespace Ryujinx.Graphics.Vulkan ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]); } - var deviceCreateInfo = new DeviceCreateInfo() + var deviceCreateInfo = new DeviceCreateInfo { SType = StructureType.DeviceCreateInfo, PNext = pExtendedFeatures, @@ -551,7 +549,7 @@ namespace Ryujinx.Graphics.Vulkan PQueueCreateInfos = &queueCreateInfo, PpEnabledExtensionNames = (byte**)ppEnabledExtensions, EnabledExtensionCount = (uint)enabledExtensions.Length, - PEnabledFeatures = &features + PEnabledFeatures = &features, }; api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError(); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 8c1787d69..8f0c73061 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -11,6 +11,9 @@ using Silk.NET.Vulkan.Extensions.KHR; using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using Format = Ryujinx.Graphics.GAL.Format; +using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology; +using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; namespace Ryujinx.Graphics.Vulkan { @@ -143,14 +146,14 @@ namespace Ryujinx.Graphics.Vulkan BackgroundQueueLock = new object(); } - PhysicalDeviceProperties2 properties2 = new PhysicalDeviceProperties2() + PhysicalDeviceProperties2 properties2 = new() { - SType = StructureType.PhysicalDeviceProperties2 + SType = StructureType.PhysicalDeviceProperties2, }; - PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new PhysicalDeviceBlendOperationAdvancedPropertiesEXT() + PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new() { - SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt + SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt, }; bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced"); @@ -161,9 +164,9 @@ namespace Ryujinx.Graphics.Vulkan properties2.PNext = &propertiesBlendOperationAdvanced; } - PhysicalDeviceSubgroupSizeControlPropertiesEXT propertiesSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlPropertiesEXT() + PhysicalDeviceSubgroupSizeControlPropertiesEXT propertiesSubgroupSizeControl = new() { - SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt + SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt, }; bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"); @@ -175,9 +178,9 @@ namespace Ryujinx.Graphics.Vulkan bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName); - PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT() + PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new() { - SType = StructureType.PhysicalDeviceTransformFeedbackPropertiesExt + SType = StructureType.PhysicalDeviceTransformFeedbackPropertiesExt, }; if (supportsTransformFeedback) @@ -186,44 +189,44 @@ namespace Ryujinx.Graphics.Vulkan properties2.PNext = &propertiesTransformFeedback; } - PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new PhysicalDevicePortabilitySubsetPropertiesKHR() + PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new() { - SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr + SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr, }; - PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() + PhysicalDeviceFeatures2 features2 = new() { - SType = StructureType.PhysicalDeviceFeatures2 + SType = StructureType.PhysicalDeviceFeatures2, }; - PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() + PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart = new() { - SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt + SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, }; - PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new() { - SType = StructureType.PhysicalDeviceRobustness2FeaturesExt + SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, }; - PhysicalDeviceShaderFloat16Int8FeaturesKHR featuresShaderInt8 = new PhysicalDeviceShaderFloat16Int8FeaturesKHR() + PhysicalDeviceShaderFloat16Int8FeaturesKHR featuresShaderInt8 = new() { - SType = StructureType.PhysicalDeviceShaderFloat16Int8Features + SType = StructureType.PhysicalDeviceShaderFloat16Int8Features, }; - PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() + PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor = new() { - SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt + SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt, }; - PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT() + PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl = new() { - SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt + SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, }; - PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR() + PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new() { - SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr + SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr, }; if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart")) @@ -359,7 +362,7 @@ namespace Ryujinx.Graphics.Vulkan _counters = new Counters(this, _device, _pipeline); } - private unsafe void SetupContext(GraphicsDebugLevel logLevel) + private void SetupContext(GraphicsDebugLevel logLevel) { _instance = VulkanInitialization.CreateInstance(Api, logLevel, _getRequiredExtensions()); _debugMessenger = new VulkanDebugMessenger(Api, _instance.Instance, logLevel); @@ -415,10 +418,8 @@ namespace Ryujinx.Graphics.Vulkan { return new ShaderCollection(this, _device, sources, info.ResourceLayout, info.State ?? default, info.FromCache); } - else - { - return new ShaderCollection(this, _device, sources, info.ResourceLayout); - } + + return new ShaderCollection(this, _device, sources, info.ResourceLayout); } internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, ResourceLayout resourceLayout, SpecDescription[] specDescription = null) @@ -426,7 +427,7 @@ namespace Ryujinx.Graphics.Vulkan return new ShaderCollection(this, _device, sources, resourceLayout, specDescription, isMinimal: true); } - public ISampler CreateSampler(GAL.SamplerCreateInfo info) + public ISampler CreateSampler(SamplerCreateInfo info) { return new SamplerHolder(this, _device, info); } @@ -483,83 +484,83 @@ namespace Ryujinx.Graphics.Vulkan FormatFeatureFlags.TransferDstBit; bool supportsBc123CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, - GAL.Format.Bc1RgbaSrgb, - GAL.Format.Bc1RgbaUnorm, - GAL.Format.Bc2Srgb, - GAL.Format.Bc2Unorm, - GAL.Format.Bc3Srgb, - GAL.Format.Bc3Unorm); + Format.Bc1RgbaSrgb, + Format.Bc1RgbaUnorm, + Format.Bc2Srgb, + Format.Bc2Unorm, + Format.Bc3Srgb, + Format.Bc3Unorm); bool supportsBc45CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, - GAL.Format.Bc4Snorm, - GAL.Format.Bc4Unorm, - GAL.Format.Bc5Snorm, - GAL.Format.Bc5Unorm); + Format.Bc4Snorm, + Format.Bc4Unorm, + Format.Bc5Snorm, + Format.Bc5Unorm); bool supportsBc67CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, - GAL.Format.Bc6HSfloat, - GAL.Format.Bc6HUfloat, - GAL.Format.Bc7Srgb, - GAL.Format.Bc7Unorm); + Format.Bc6HSfloat, + Format.Bc6HUfloat, + Format.Bc7Srgb, + Format.Bc7Unorm); bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, - GAL.Format.Etc2RgbaSrgb, - GAL.Format.Etc2RgbaUnorm, - GAL.Format.Etc2RgbPtaSrgb, - GAL.Format.Etc2RgbPtaUnorm, - GAL.Format.Etc2RgbSrgb, - GAL.Format.Etc2RgbUnorm); + Format.Etc2RgbaSrgb, + Format.Etc2RgbaUnorm, + Format.Etc2RgbPtaSrgb, + Format.Etc2RgbPtaUnorm, + Format.Etc2RgbSrgb, + Format.Etc2RgbUnorm); bool supports5BitComponentFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, - GAL.Format.R5G6B5Unorm, - GAL.Format.R5G5B5A1Unorm, - GAL.Format.R5G5B5X1Unorm, - GAL.Format.B5G6R5Unorm, - GAL.Format.B5G5R5A1Unorm, - GAL.Format.A1B5G5R5Unorm); + Format.R5G6B5Unorm, + Format.R5G5B5A1Unorm, + Format.R5G5B5X1Unorm, + Format.B5G6R5Unorm, + Format.B5G5R5A1Unorm, + Format.A1B5G5R5Unorm); bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, - GAL.Format.R4G4B4A4Unorm); + Format.R4G4B4A4Unorm); bool supportsAstcFormats = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, - GAL.Format.Astc4x4Unorm, - GAL.Format.Astc5x4Unorm, - GAL.Format.Astc5x5Unorm, - GAL.Format.Astc6x5Unorm, - GAL.Format.Astc6x6Unorm, - GAL.Format.Astc8x5Unorm, - GAL.Format.Astc8x6Unorm, - GAL.Format.Astc8x8Unorm, - GAL.Format.Astc10x5Unorm, - GAL.Format.Astc10x6Unorm, - GAL.Format.Astc10x8Unorm, - GAL.Format.Astc10x10Unorm, - GAL.Format.Astc12x10Unorm, - GAL.Format.Astc12x12Unorm, - GAL.Format.Astc4x4Srgb, - GAL.Format.Astc5x4Srgb, - GAL.Format.Astc5x5Srgb, - GAL.Format.Astc6x5Srgb, - GAL.Format.Astc6x6Srgb, - GAL.Format.Astc8x5Srgb, - GAL.Format.Astc8x6Srgb, - GAL.Format.Astc8x8Srgb, - GAL.Format.Astc10x5Srgb, - GAL.Format.Astc10x6Srgb, - GAL.Format.Astc10x8Srgb, - GAL.Format.Astc10x10Srgb, - GAL.Format.Astc12x10Srgb, - GAL.Format.Astc12x12Srgb); + Format.Astc4x4Unorm, + Format.Astc5x4Unorm, + Format.Astc5x5Unorm, + Format.Astc6x5Unorm, + Format.Astc6x6Unorm, + Format.Astc8x5Unorm, + Format.Astc8x6Unorm, + Format.Astc8x8Unorm, + Format.Astc10x5Unorm, + Format.Astc10x6Unorm, + Format.Astc10x8Unorm, + Format.Astc10x10Unorm, + Format.Astc12x10Unorm, + Format.Astc12x12Unorm, + Format.Astc4x4Srgb, + Format.Astc5x4Srgb, + Format.Astc5x5Srgb, + Format.Astc6x5Srgb, + Format.Astc6x6Srgb, + Format.Astc8x5Srgb, + Format.Astc8x6Srgb, + Format.Astc8x8Srgb, + Format.Astc10x5Srgb, + Format.Astc10x6Srgb, + Format.Astc10x8Srgb, + Format.Astc10x10Srgb, + Format.Astc12x10Srgb, + Format.Astc12x12Srgb); - PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features() + PhysicalDeviceVulkan12Features featuresVk12 = new() { - SType = StructureType.PhysicalDeviceVulkan12Features + SType = StructureType.PhysicalDeviceVulkan12Features, }; - PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() + PhysicalDeviceFeatures2 features2 = new() { SType = StructureType.PhysicalDeviceFeatures2, - PNext = &featuresVk12 + PNext = &featuresVk12, }; Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2); @@ -665,10 +666,8 @@ namespace Ryujinx.Graphics.Vulkan { return $"{(driverVersionRaw >> 22) & 0x3FF}.{(driverVersionRaw >> 14) & 0xFF}.{(driverVersionRaw >> 6) & 0xFF}.{driverVersionRaw & 0x3F}"; } - else - { - return ParseStandardVulkanVersion(driverVersionRaw); - } + + return ParseStandardVulkanVersion(driverVersionRaw); } private unsafe void PrintGpuInformation() @@ -696,24 +695,24 @@ namespace Ryujinx.Graphics.Vulkan Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); } - internal GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology) + internal PrimitiveTopology TopologyRemap(PrimitiveTopology topology) { return topology switch { - GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles, - GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip, - GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans) ? GAL.PrimitiveTopology.Triangles : topology, - _ => topology + PrimitiveTopology.Quads => PrimitiveTopology.Triangles, + PrimitiveTopology.QuadStrip => PrimitiveTopology.TriangleStrip, + PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans) ? PrimitiveTopology.Triangles : topology, + _ => topology, }; } - internal bool TopologyUnsupported(GAL.PrimitiveTopology topology) + internal bool TopologyUnsupported(PrimitiveTopology topology) { return topology switch { - GAL.PrimitiveTopology.Quads => true, - GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans), - _ => false + PrimitiveTopology.Quads => true, + PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans), + _ => false, }; } @@ -873,4 +872,4 @@ namespace Ryujinx.Graphics.Vulkan HostMemoryAllocator.TryImport(BufferManager.HostImportedBufferMemoryRequirements, BufferManager.DefaultBufferMemoryFlags, address, size); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 075d1b303..0a41e98df 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -48,9 +48,9 @@ namespace Ryujinx.Graphics.Vulkan CreateSwapchain(); - var semaphoreCreateInfo = new SemaphoreCreateInfo() + var semaphoreCreateInfo = new SemaphoreCreateInfo { - SType = StructureType.SemaphoreCreateInfo + SType = StructureType.SemaphoreCreateInfo, }; gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _imageAvailableSemaphore).ThrowOnError(); @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan var oldSwapchain = _swapchain; - var swapchainCreateInfo = new SwapchainCreateInfoKHR() + var swapchainCreateInfo = new SwapchainCreateInfoKHR { SType = StructureType.SwapchainCreateInfoKhr, Surface = _surface, @@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Vulkan PreTransform = capabilities.CurrentTransform, CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha), PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), - Clipped = true + Clipped = true, }; _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError(); @@ -164,14 +164,14 @@ namespace Ryujinx.Graphics.Vulkan var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, 1, 0, 1); - var imageCreateInfo = new ImageViewCreateInfo() + var imageCreateInfo = new ImageViewCreateInfo { SType = StructureType.ImageViewCreateInfo, Image = swapchainImage, ViewType = ImageViewType.Type2D, Format = format, Components = componentMapping, - SubresourceRange = subresourceRange + SubresourceRange = subresourceRange, }; _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError(); @@ -234,13 +234,11 @@ namespace Ryujinx.Graphics.Vulkan { return capabilities.CurrentExtent; } - else - { - uint width = Math.Max(capabilities.MinImageExtent.Width, Math.Min(capabilities.MaxImageExtent.Width, SurfaceWidth)); - uint height = Math.Max(capabilities.MinImageExtent.Height, Math.Min(capabilities.MaxImageExtent.Height, SurfaceHeight)); - return new Extent2D(width, height); - } + uint width = Math.Max(capabilities.MinImageExtent.Width, Math.Min(capabilities.MaxImageExtent.Width, SurfaceWidth)); + uint height = Math.Max(capabilities.MinImageExtent.Height, Math.Min(capabilities.MaxImageExtent.Height, SurfaceHeight)); + + return new Extent2D(width, height); } public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) @@ -350,10 +348,10 @@ namespace Ryujinx.Graphics.Vulkan 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 dstWidth = (int)(_width * ratioX); int dstHeight = (int)(_height * ratioY); - int dstPaddingX = (_width - dstWidth) / 2; + int dstPaddingX = (_width - dstWidth) / 2; int dstPaddingY = (_height - dstHeight) / 2; int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; @@ -413,7 +411,7 @@ namespace Ryujinx.Graphics.Vulkan Result result; - var presentInfo = new PresentInfoKHR() + var presentInfo = new PresentInfoKHR { SType = StructureType.PresentInfoKhr, WaitSemaphoreCount = 1, @@ -421,7 +419,7 @@ namespace Ryujinx.Graphics.Vulkan SwapchainCount = 1, PSwapchains = &swapchain, PImageIndices = &nextImage, - PResults = &result + PResults = &result, }; lock (_gd.QueueLock) @@ -529,7 +527,7 @@ namespace Ryujinx.Graphics.Vulkan { var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, 0, 1); - var barrier = new ImageMemoryBarrier() + var barrier = new ImageMemoryBarrier { SType = StructureType.ImageMemoryBarrier, SrcAccessMask = srcAccess, @@ -539,7 +537,7 @@ namespace Ryujinx.Graphics.Vulkan SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, DstQueueFamilyIndex = Vk.QueueFamilyIgnored, Image = image, - SubresourceRange = subresourceRange + SubresourceRange = subresourceRange, }; _gd.Api.CmdPipelineBarrier( diff --git a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs index 0a365e8fb..146cf6607 100644 --- a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.Vulkan { - internal abstract class WindowBase: IWindow + internal abstract class WindowBase : IWindow { public bool ScreenCaptureRequested { get; set; } @@ -15,4 +15,4 @@ namespace Ryujinx.Graphics.Vulkan public abstract void SetScalingFilter(ScalingFilter scalerType); public abstract void SetScalingFilterLevel(float scale); } -} \ No newline at end of file +} From 02b5c7ea89bb6aae1c214b78fb1047872382dc43 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 1 Jul 2023 12:42:10 +0200 Subject: [PATCH 691/737] [Ryujinx.Horizon] Address dotnet-format issues (#5381) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Address dotnet format CA1822 warnings * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Revert formatting changes for while and for-loops * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Remove a few unused parameters * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Address IDE0251 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Add trailing commas and fix formatting issues * Convert if-else chain to switch block * Address review feedback --- src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs | 7 +- src/Ryujinx.Horizon/Bcat/BcatMain.cs | 9 +- src/Ryujinx.Horizon/Bcat/BcatServerManager.cs | 10 +- .../Bcat/Ipc/ServiceCreator/BcatService.cs | 7 +- .../DeliveryCacheDirectoryService.cs | 2 +- .../DeliveryCacheFileService.cs | 2 +- .../DeliveryCacheProgressService.cs | 2 +- .../Types/DeliveryCacheProgressImpl.cs | 4 +- .../Bcat/Types/BcatPortIndex.cs | 2 +- .../Bcat/Types/BcatServicePermissionLevel.cs | 10 +- src/Ryujinx.Horizon/HeapAllocator.cs | 16 +-- src/Ryujinx.Horizon/HorizonOptions.cs | 8 +- src/Ryujinx.Horizon/HorizonStatic.cs | 26 ++--- .../LogManager/Ipc/LmLogger.cs | 110 +++++++----------- .../LogManager/Ipc/LogService.cs | 2 +- src/Ryujinx.Horizon/LogManager/LmIpcServer.cs | 10 +- src/Ryujinx.Horizon/LogManager/LmMain.cs | 2 +- .../LogManager/Types/LogPacket.cs | 24 ++-- src/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs | 17 +-- src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs | 12 +- src/Ryujinx.Horizon/Prepo/PrepoMain.cs | 2 +- src/Ryujinx.Horizon/Prepo/PrepoResult.cs | 4 +- .../Prepo/PrepoServerManager.cs | 4 +- .../Prepo/Types/PrepoPortIndex.cs | 4 +- .../Types/PrepoServicePermissionLevel.cs | 10 +- src/Ryujinx.Horizon/Sdk/Account/Uid.cs | 8 +- .../Sdk/Bcat/IServiceCreator.cs | 3 +- src/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs | 4 +- src/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs | 2 +- src/Ryujinx.Horizon/Sdk/Lm/ILogService.cs | 2 +- src/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs | 4 +- src/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs | 2 +- src/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs | 2 +- src/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs | 2 +- .../Sdk/OsTypes/EventClearMode.cs | 2 +- .../Sdk/OsTypes/Impl/MultiWaitImpl.cs | 17 +-- .../Sdk/OsTypes/InitializationState.cs | 4 +- .../Sdk/OsTypes/MultiWaitHolderOfEvent.cs | 2 +- .../Sdk/OsTypes/MultiWaitHolderOfHandle.cs | 2 +- src/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs | 2 +- src/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs | 2 +- .../Sdk/OsTypes/SystemEventType.cs | 4 +- src/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs | 2 +- .../Sdk/Prepo/IPrepoService.cs | 7 +- src/Ryujinx.Horizon/Sdk/ServiceUtil.cs | 10 +- .../Sdk/Sf/Cmif/CmifDomainInHeader.cs | 10 +- .../Sdk/Sf/Cmif/CmifDomainOutHeader.cs | 2 +- .../Sdk/Sf/Cmif/CmifDomainRequestType.cs | 4 +- .../Sdk/Sf/Cmif/CmifMessage.cs | 64 +++++----- .../Sdk/Sf/Cmif/CmifOutHeader.cs | 8 +- .../Sdk/Sf/Cmif/CmifRequest.cs | 8 +- .../Sdk/Sf/Cmif/CmifRequestFormat.cs | 28 ++--- .../Sdk/Sf/Cmif/CmifResponse.cs | 4 +- .../Sdk/Sf/Cmif/CommandType.cs | 14 +-- .../Cmif/DomainServiceObjectDispatchTable.cs | 2 +- .../Sf/Cmif/DomainServiceObjectProcessor.cs | 1 - .../Sdk/Sf/Cmif/HandlesToClose.cs | 39 +++++-- .../Sdk/Sf/Cmif/PointerAndSize.cs | 4 +- .../Sdk/Sf/Cmif/ScopedInlineContextChange.cs | 2 +- .../Sdk/Sf/Cmif/ServerDomainManager.cs | 2 +- .../Sf/Cmif/ServerMessageRuntimeMetadata.cs | 34 +++--- .../Sdk/Sf/Cmif/ServiceDispatchContext.cs | 18 +-- .../Sdk/Sf/Cmif/ServiceDispatchMeta.cs | 2 +- .../Sdk/Sf/Cmif/ServiceDispatchTable.cs | 4 +- .../Sdk/Sf/Cmif/ServiceDispatchTableBase.cs | 8 +- src/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs | 40 +++---- .../Sdk/Sf/CommandArgAttributes.cs | 8 +- src/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs | 20 ++-- .../Sdk/Sf/CommandSerialization.cs | 2 +- src/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs | 14 +-- src/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs | 18 +-- .../Sdk/Sf/Hipc/HipcBufferDescriptor.cs | 10 +- .../Sdk/Sf/Hipc/HipcBufferFlags.cs | 14 +-- .../Sdk/Sf/Hipc/HipcBufferMode.cs | 6 +- .../Sdk/Sf/Hipc/HipcManager.cs | 2 +- .../Sdk/Sf/Hipc/HipcMessage.cs | 92 +++++++-------- .../Sdk/Sf/Hipc/HipcMessageData.cs | 8 +- .../Sdk/Sf/Hipc/HipcMetadata.cs | 18 +-- .../Sdk/Sf/Hipc/HipcReceiveListEntry.cs | 10 +- src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs | 2 + .../Sdk/Sf/Hipc/HipcStaticDescriptor.cs | 10 +- .../Sdk/Sf/Hipc/ManagerOptions.cs | 16 +-- .../Sdk/Sf/Hipc/ReceiveResult.cs | 2 +- src/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs | 20 ++-- .../Sdk/Sf/Hipc/ServerManager.cs | 22 ++-- .../Sdk/Sf/Hipc/ServerManagerBase.cs | 22 ++-- .../Sdk/Sf/Hipc/ServerSession.cs | 16 +-- .../Sdk/Sf/Hipc/ServerSessionManager.cs | 99 +++++++--------- .../Sdk/Sf/Hipc/SpecialHeader.cs | 6 +- .../Sdk/Sf/HipcCommandProcessor.cs | 34 +++--- .../Sdk/Sf/RawDataOffsetCalculator.cs | 14 +-- src/Ryujinx.Horizon/Sdk/Sf/SfResult.cs | 4 +- src/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs | 2 +- src/Ryujinx.Horizon/Sdk/Sm/IUserService.cs | 2 +- src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs | 6 +- src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs | 2 +- src/Ryujinx.Horizon/ServiceEntry.cs | 12 +- src/Ryujinx.Horizon/ServiceTable.cs | 2 +- src/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs | 10 +- src/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs | 12 +- src/Ryujinx.Horizon/Sm/Ipc/UserService.cs | 6 +- src/Ryujinx.Horizon/Sm/SmMain.cs | 10 +- src/Ryujinx.Horizon/Sm/SmResult.cs | 4 +- src/Ryujinx.Horizon/Sm/SmServerManager.cs | 6 +- src/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs | 4 +- 105 files changed, 617 insertions(+), 637 deletions(-) diff --git a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs index 4a5378af2..f39929ddc 100644 --- a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs +++ b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs @@ -1,5 +1,4 @@ -using Ryujinx.Horizon.Bcat.Ipc; -using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Bcat.Types; using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; @@ -8,7 +7,7 @@ namespace Ryujinx.Horizon.Bcat internal class BcatIpcServer { private const int BcatMaxSessionsCount = 8; - private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4; + private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4; private const int PointerBufferSize = 0x400; private const int MaxDomains = 64; @@ -29,10 +28,12 @@ namespace Ryujinx.Horizon.Bcat _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount); +#pragma warning disable IDE0055 // Disable formatting _serverManager.RegisterServer((int)BcatPortIndex.Admin, ServiceName.Encode("bcat:a"), BcatMaxSessionsCount); _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount); _serverManager.RegisterServer((int)BcatPortIndex.User, ServiceName.Encode("bcat:u"), BcatMaxSessionsCount); _serverManager.RegisterServer((int)BcatPortIndex.System, ServiceName.Encode("bcat:s"), BcatMaxSessionsCount); +#pragma warning restore IDE0055 } public void ServiceRequests() diff --git a/src/Ryujinx.Horizon/Bcat/BcatMain.cs b/src/Ryujinx.Horizon/Bcat/BcatMain.cs index d4166fb18..f347e2166 100644 --- a/src/Ryujinx.Horizon/Bcat/BcatMain.cs +++ b/src/Ryujinx.Horizon/Bcat/BcatMain.cs @@ -1,11 +1,4 @@ -using Ryujinx.Horizon.LogManager; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Ryujinx.Horizon.Bcat +namespace Ryujinx.Horizon.Bcat { internal class BcatMain : IService { diff --git a/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs index f02eafd9d..0431bd9a9 100644 --- a/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs +++ b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs @@ -17,12 +17,12 @@ namespace Ryujinx.Horizon.Bcat { return (BcatPortIndex)portIndex switch { - BcatPortIndex.Admin => AcceptImpl(server, new ServiceCreator("bcat:a", BcatServicePermissionLevel.Admin)), + BcatPortIndex.Admin => AcceptImpl(server, new ServiceCreator("bcat:a", BcatServicePermissionLevel.Admin)), BcatPortIndex.Manager => AcceptImpl(server, new ServiceCreator("bcat:m", BcatServicePermissionLevel.Manager)), - BcatPortIndex.User => AcceptImpl(server, new ServiceCreator("bcat:u", BcatServicePermissionLevel.User)), - BcatPortIndex.System => AcceptImpl(server, new ServiceCreator("bcat:s", BcatServicePermissionLevel.System)), - _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + BcatPortIndex.User => AcceptImpl(server, new ServiceCreator("bcat:u", BcatServicePermissionLevel.User)), + BcatPortIndex.System => AcceptImpl(server, new ServiceCreator("bcat:s", BcatServicePermissionLevel.System)), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs index e82f597e9..9ea2dc11c 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs @@ -7,12 +7,7 @@ namespace Ryujinx.Horizon.Bcat.Ipc { partial class BcatService : IBcatService { - private readonly BcatServicePermissionLevel _permissionLevel; - - public BcatService(BcatServicePermissionLevel permissionLevel) - { - _permissionLevel = permissionLevel; - } + public BcatService(BcatServicePermissionLevel permissionLevel) { } [CmifCommand(10100)] public Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService) diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs index dd13eefb4..c8b38c28f 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Bcat.Ipc } [CmifCommand(1)] - public Result Read(out int entriesRead, [Buffer(Sdk.Sf.Hipc.HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeliveryCacheDirectoryEntry> entriesBuffer) + public Result Read(out int entriesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeliveryCacheDirectoryEntry> entriesBuffer) { return _libHacService.Get.Read(out entriesRead, entriesBuffer).ToHorizonResult(); } diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs index d23f5f41f..a26c3258f 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Horizon.Bcat.Ipc [CmifCommand(1)] public Result Read(long offset, out long bytesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> data) { - return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult(); + return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult(); } [CmifCommand(2)] diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs index 91aa26867..578c18f43 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs @@ -39,7 +39,7 @@ namespace Ryujinx.Horizon.Bcat.Ipc deliveryCacheProgressImpl = new DeliveryCacheProgressImpl { State = DeliveryCacheProgressImpl.Status.Done, - Result = 0 + Result = 0, }; Logger.Stub?.PrintStub(LogClass.ServiceBcat); diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs index 10e0b54fe..9e5274a61 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs @@ -8,11 +8,11 @@ namespace Ryujinx.Horizon.Bcat.Ipc.Types public enum Status { // TODO: determine other values - Done = 9 + Done = 9, } public Status State; - public uint Result; + public uint Result; // TODO: reverse the rest of the structure } } diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs index e448dfdcd..958517892 100644 --- a/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs @@ -5,6 +5,6 @@ Admin, Manager, User, - System + System, } } diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs index 54d7461a7..6bf4b4cc0 100644 --- a/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs @@ -2,9 +2,9 @@ { enum BcatServicePermissionLevel { - Admin = -1, - User = 1, - System = 2, - Manager = 6 + Admin = -1, + User = 1, + System = 2, + Manager = 6, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/HeapAllocator.cs b/src/Ryujinx.Horizon/HeapAllocator.cs index 40ff14d09..fc125387e 100644 --- a/src/Ryujinx.Horizon/HeapAllocator.cs +++ b/src/Ryujinx.Horizon/HeapAllocator.cs @@ -9,15 +9,15 @@ namespace Ryujinx.Horizon { private const ulong InvalidAddress = ulong.MaxValue; - private struct Range : IComparable<Range> + private readonly struct Range : IComparable<Range> { public ulong Offset { get; } - public ulong Size { get; } + public ulong Size { get; } public Range(ulong offset, ulong size) { Offset = offset; - Size = size; + Size = size; } public int CompareTo(Range other) @@ -31,7 +31,7 @@ namespace Ryujinx.Horizon public HeapAllocator() { - _freeRanges = new List<Range>(); + _freeRanges = new List<Range>(); _currentHeapSize = 0; } @@ -70,8 +70,8 @@ namespace Ryujinx.Horizon var range = _freeRanges[i]; ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment); - ulong sizeDelta = alignedOffset - range.Offset; - ulong usableSize = range.Size - sizeDelta; + ulong sizeDelta = alignedOffset - range.Offset; + ulong usableSize = range.Size - sizeDelta; if (sizeDelta < range.Size && usableSize >= size) { @@ -82,7 +82,7 @@ namespace Ryujinx.Horizon InsertFreeRange(range.Offset, sizeDelta); } - ulong endOffset = range.Offset + range.Size; + ulong endOffset = range.Offset + range.Size; ulong remainingSize = endOffset - (alignedOffset + size); if (remainingSize != 0) { @@ -140,4 +140,4 @@ namespace Ryujinx.Horizon _freeRanges.Insert(index, range); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/HorizonOptions.cs b/src/Ryujinx.Horizon/HorizonOptions.cs index 1ec56bfa7..75cc29b72 100644 --- a/src/Ryujinx.Horizon/HorizonOptions.cs +++ b/src/Ryujinx.Horizon/HorizonOptions.cs @@ -2,18 +2,18 @@ using LibHac; namespace Ryujinx.Horizon { - public struct HorizonOptions + public readonly struct HorizonOptions { - public bool IgnoreMissingServices { get; } + public bool IgnoreMissingServices { get; } public bool ThrowOnInvalidCommandIds { get; } public HorizonClient BcatClient { get; } public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient) { - IgnoreMissingServices = ignoreMissingServices; + IgnoreMissingServices = ignoreMissingServices; ThrowOnInvalidCommandIds = true; - BcatClient = bcatClient; + BcatClient = bcatClient; } } } diff --git a/src/Ryujinx.Horizon/HorizonStatic.cs b/src/Ryujinx.Horizon/HorizonStatic.cs index e372df699..1e483cd44 100644 --- a/src/Ryujinx.Horizon/HorizonStatic.cs +++ b/src/Ryujinx.Horizon/HorizonStatic.cs @@ -21,24 +21,24 @@ namespace Ryujinx.Horizon [ThreadStatic] private static int _threadHandle; - public static HorizonOptions Options => _options; - public static ISyscallApi Syscall => _syscall; - public static IVirtualMemoryManager AddressSpace => _addressSpace; - public static IThreadContext ThreadContext => _threadContext; - public static int CurrentThreadHandle => _threadHandle; + public static HorizonOptions Options => _options; + public static ISyscallApi Syscall => _syscall; + public static IVirtualMemoryManager AddressSpace => _addressSpace; + public static IThreadContext ThreadContext => _threadContext; + public static int CurrentThreadHandle => _threadHandle; public static void Register( - HorizonOptions options, - ISyscallApi syscallApi, + HorizonOptions options, + ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, - IThreadContext threadContext, - int threadHandle) + IThreadContext threadContext, + int threadHandle) { - _options = options; - _syscall = syscallApi; - _addressSpace = addressSpace; + _options = options; + _syscall = syscallApi; + _addressSpace = addressSpace; _threadContext = threadContext; - _threadHandle = threadHandle; + _threadHandle = threadHandle; } } } diff --git a/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs index 88dddef5e..b6460a4bc 100644 --- a/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs +++ b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -95,74 +95,52 @@ namespace Ryujinx.Horizon.LogManager.Ipc LogDataChunkKey key = (LogDataChunkKey)type; - if (key == LogDataChunkKey.Start) + switch (key) { - reader.Skip(size); - - continue; - } - else if (key == LogDataChunkKey.Stop) - { - break; - } - else if (key == LogDataChunkKey.Line) - { - if (!reader.TryRead<int>(out _logPacket.Line)) - { + case LogDataChunkKey.Start: + reader.Skip(size); + continue; + case LogDataChunkKey.Stop: + break; + case LogDataChunkKey.Line when !reader.TryRead(out _logPacket.Line): + case LogDataChunkKey.DropCount when !reader.TryRead(out _logPacket.DropCount): + case LogDataChunkKey.Time when !reader.TryRead(out _logPacket.Time): return true; - } - } - else if (key == LogDataChunkKey.DropCount) - { - if (!reader.TryRead<long>(out _logPacket.DropCount)) - { - return true; - } - } - else if (key == LogDataChunkKey.Time) - { - if (!reader.TryRead<long>(out _logPacket.Time)) - { - return true; - } - } - else if (key == LogDataChunkKey.Message) - { - string text = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); - - if (isHeadPacket && isTailPacket) - { - _logPacket.Message = text; - } - else - { - _logPacket.Message += text; - - if (_logPacket.Message.Length >= MessageLengthLimit) + case LogDataChunkKey.Message: { - isTailPacket = true; + string text = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + + if (isHeadPacket && isTailPacket) + { + _logPacket.Message = text; + } + else + { + _logPacket.Message += text; + + if (_logPacket.Message.Length >= MessageLengthLimit) + { + isTailPacket = true; + } + } + + break; } - } - } - else if (key == LogDataChunkKey.Filename) - { - _logPacket.Filename = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); - } - else if (key == LogDataChunkKey.Function) - { - _logPacket.Function = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); - } - else if (key == LogDataChunkKey.Module) - { - _logPacket.Module = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); - } - else if (key == LogDataChunkKey.Thread) - { - _logPacket.Thread = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); - } - else if (key == LogDataChunkKey.ProgramName) - { - _logPacket.ProgramName = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + case LogDataChunkKey.Filename: + _logPacket.Filename = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + break; + case LogDataChunkKey.Function: + _logPacket.Function = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + break; + case LogDataChunkKey.Module: + _logPacket.Module = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + break; + case LogDataChunkKey.Thread: + _logPacket.Thread = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + break; + case LogDataChunkKey.ProgramName: + _logPacket.ProgramName = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + break; } } @@ -177,7 +155,7 @@ namespace Ryujinx.Horizon.LogManager.Ipc do { - if (!reader.TryRead<byte>(out encoded)) + if (!reader.TryRead(out encoded)) { return false; } @@ -190,4 +168,4 @@ namespace Ryujinx.Horizon.LogManager.Ipc return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs index 6899739e3..9ac9c27e6 100644 --- a/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs +++ b/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Horizon.LogManager.Ipc return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs index 71b844a2b..d1a405b87 100644 --- a/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs +++ b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs @@ -9,13 +9,13 @@ namespace Ryujinx.Horizon.LogManager private const int LogMaxSessionsCount = 42; private const int PointerBufferSize = 0x400; - private const int MaxDomains = 31; - private const int MaxDomainObjects = 61; - private const int MaxPortsCount = 1; + private const int MaxDomains = 31; + private const int MaxDomainObjects = 61; + private const int MaxPortsCount = 1; private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); - private SmApi _sm; + private SmApi _sm; private ServerManager _serverManager; public void Initialize() @@ -40,4 +40,4 @@ namespace Ryujinx.Horizon.LogManager _serverManager.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/LogManager/LmMain.cs b/src/Ryujinx.Horizon/LogManager/LmMain.cs index bbe96d4c9..c229de597 100644 --- a/src/Ryujinx.Horizon/LogManager/LmMain.cs +++ b/src/Ryujinx.Horizon/LogManager/LmMain.cs @@ -14,4 +14,4 @@ ipcServer.Shutdown(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs b/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs index dbff5e3e2..57a389be9 100644 --- a/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs +++ b/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs @@ -5,15 +5,15 @@ namespace Ryujinx.Horizon.LogManager.Types { struct LogPacket { - public string Message; - public int Line; - public string Filename; - public string Function; - public string Module; - public string Thread; - public long DropCount; - public long Time; - public string ProgramName; + public string Message; + public int Line; + public string Filename; + public string Function; + public string Module; + public string Thread; + public long DropCount; + public long Time; + public string ProgramName; public LogSeverity Severity; public override string ToString() @@ -35,12 +35,12 @@ namespace Ryujinx.Horizon.LogManager.Types { builder.AppendLine($" ProgramName: {ProgramName}"); } - + if (!string.IsNullOrEmpty(Module)) { builder.AppendLine($" Module: {Module}"); } - + if (!string.IsNullOrEmpty(Thread)) { builder.AppendLine($" Thread: {Thread}"); @@ -69,4 +69,4 @@ namespace Ryujinx.Horizon.LogManager.Types return builder.ToString(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs b/src/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs index e157fa562..f424b17e2 100644 --- a/src/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs +++ b/src/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs @@ -10,6 +10,7 @@ using Ryujinx.Horizon.Sdk.Sf; using Ryujinx.Horizon.Sdk.Sf.Hipc; using System; using System.Text; +using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId; namespace Ryujinx.Horizon.Prepo.Ipc { @@ -18,14 +19,14 @@ namespace Ryujinx.Horizon.Prepo.Ipc enum PlayReportKind { Normal, - System + System, } private readonly PrepoServicePermissionLevel _permissionLevel; private ulong _systemSessionId; - private bool _immediateTransmissionEnabled = false; - private bool _userAgreementCheckEnabled = true; + private bool _immediateTransmissionEnabled; + private bool _userAgreementCheckEnabled = true; public PrepoService(PrepoServicePermissionLevel permissionLevel) { @@ -107,7 +108,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc } [CmifCommand(20100)] - public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer) + public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer) { if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) { @@ -118,7 +119,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc } [CmifCommand(20101)] - public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer) + public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer) { if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) { @@ -164,7 +165,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc return PrepoResult.PermissionDenied; } - private static Result ProcessPlayReport(PlayReportKind playReportKind, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid, Uid userId, bool withUserId = false, Sdk.Ncm.ApplicationId applicationId = default) + private static Result ProcessPlayReport(PlayReportKind playReportKind, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid, Uid userId, bool withUserId = false, ApplicationId applicationId = default) { if (withUserId) { @@ -191,7 +192,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc return PrepoResult.InvalidBufferSize; } - StringBuilder builder = new(); + StringBuilder builder = new(); MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray()); builder.AppendLine(); @@ -222,4 +223,4 @@ namespace Ryujinx.Horizon.Prepo.Ipc return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs index b80399eaa..9c185520d 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs @@ -6,13 +6,13 @@ namespace Ryujinx.Horizon.Prepo { class PrepoIpcServer { - private const int PrepoMaxSessionsCount = 12; + private const int PrepoMaxSessionsCount = 12; private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6; private const int PointerBufferSize = 0x80; - private const int MaxDomains = 64; - private const int MaxDomainObjects = 16; - private const int MaxPortsCount = 6; + private const int MaxDomains = 64; + private const int MaxDomainObjects = 16; + private const int MaxPortsCount = 6; private static readonly ManagerOptions _prepoManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); @@ -28,12 +28,14 @@ namespace Ryujinx.Horizon.Prepo _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _prepoManagerOptions, PrepoTotalMaxSessionsCount); +#pragma warning disable IDE0055 // Disable formatting _serverManager.RegisterServer((int)PrepoPortIndex.Admin, ServiceName.Encode("prepo:a"), PrepoMaxSessionsCount); // 1.0.0-5.1.0 _serverManager.RegisterServer((int)PrepoPortIndex.Admin2, ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+ _serverManager.RegisterServer((int)PrepoPortIndex.Manager, ServiceName.Encode("prepo:m"), PrepoMaxSessionsCount); _serverManager.RegisterServer((int)PrepoPortIndex.User, ServiceName.Encode("prepo:u"), PrepoMaxSessionsCount); _serverManager.RegisterServer((int)PrepoPortIndex.System, ServiceName.Encode("prepo:s"), PrepoMaxSessionsCount); _serverManager.RegisterServer((int)PrepoPortIndex.Debug, ServiceName.Encode("prepo:d"), PrepoMaxSessionsCount); // 1.0.0 +#pragma warning restore IDE0055 } public void ServiceRequests() @@ -46,4 +48,4 @@ namespace Ryujinx.Horizon.Prepo _serverManager.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Prepo/PrepoMain.cs b/src/Ryujinx.Horizon/Prepo/PrepoMain.cs index 5ff0f53d9..c311d619f 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoMain.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoMain.cs @@ -14,4 +14,4 @@ ipcServer.Shutdown(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Prepo/PrepoResult.cs b/src/Ryujinx.Horizon/Prepo/PrepoResult.cs index 12255e3d8..4c6bc7a45 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoResult.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoResult.cs @@ -6,10 +6,12 @@ namespace Ryujinx.Horizon.Prepo { private const int ModuleId = 129; +#pragma warning disable IDE0055 // Disable formatting public static Result InvalidArgument => new(ModuleId, 1); public static Result InvalidState => new(ModuleId, 5); public static Result InvalidBufferSize => new(ModuleId, 9); public static Result PermissionDenied => new(ModuleId, 90); public static Result InvalidPid => new(ModuleId, 101); +#pragma warning restore IDE0055 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Prepo/PrepoServerManager.cs b/src/Ryujinx.Horizon/Prepo/PrepoServerManager.cs index 55e4ff7db..a79360953 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoServerManager.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoServerManager.cs @@ -17,6 +17,7 @@ namespace Ryujinx.Horizon.Prepo { return (PrepoPortIndex)portIndex switch { +#pragma warning disable IDE0055 // Disable formatting PrepoPortIndex.Admin => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)), PrepoPortIndex.Admin2 => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)), PrepoPortIndex.Manager => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Manager)), @@ -24,7 +25,8 @@ namespace Ryujinx.Horizon.Prepo PrepoPortIndex.System => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.System)), PrepoPortIndex.Debug => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Debug)), _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), +#pragma warning restore IDE0055 }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs b/src/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs index f4d6b8773..31c5b02d0 100644 --- a/src/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs +++ b/src/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs @@ -7,6 +7,6 @@ Manager, User, System, - Debug + Debug, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs b/src/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs index 8214f4b9a..759c5018d 100644 --- a/src/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs +++ b/src/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs @@ -2,10 +2,10 @@ { enum PrepoServicePermissionLevel { - Admin = -1, - User = 1, - System = 2, + Admin = -1, + User = 1, + System = 2, Manager = 6, - Debug = unchecked((int)0x80000006) + Debug = unchecked((int)0x80000006), } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Account/Uid.cs b/src/Ryujinx.Horizon/Sdk/Account/Uid.cs index 5aad0463f..0175d393c 100644 --- a/src/Ryujinx.Horizon/Sdk/Account/Uid.cs +++ b/src/Ryujinx.Horizon/Sdk/Account/Uid.cs @@ -17,14 +17,14 @@ namespace Ryujinx.Horizon.Sdk.Account public Uid(long low, long high) { - Low = low; + Low = low; High = high; } public Uid(byte[] bytes) { High = BitConverter.ToInt64(bytes, 0); - Low = BitConverter.ToInt64(bytes, 8); + Low = BitConverter.ToInt64(bytes, 8); } public Uid(string hex) @@ -34,7 +34,7 @@ namespace Ryujinx.Horizon.Sdk.Account throw new ArgumentException("Invalid Hex value!", nameof(hex)); } - Low = Convert.ToInt64(hex[16..], 16); + Low = Convert.ToInt64(hex[16..], 16); High = Convert.ToInt64(hex[..16], 16); } @@ -59,4 +59,4 @@ namespace Ryujinx.Horizon.Sdk.Account return new UInt128((ulong)High, (ulong)Low); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs b/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs index edc525900..04f25259f 100644 --- a/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs +++ b/src/Ryujinx.Horizon/Sdk/Bcat/IServiceCreator.cs @@ -1,4 +1,5 @@ using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Ncm; using Ryujinx.Horizon.Sdk.Sf; namespace Ryujinx.Horizon.Sdk.Bcat @@ -7,6 +8,6 @@ namespace Ryujinx.Horizon.Sdk.Bcat { Result CreateBcatService(out IBcatService service, ulong pid); Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, ulong pid); - Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, Ncm.ApplicationId applicationId); + Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, ApplicationId applicationId); } } diff --git a/src/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs b/src/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs index 72acf7896..ecac00bb3 100644 --- a/src/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs +++ b/src/Ryujinx.Horizon/Sdk/Diag/LogSeverity.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Horizon.Sdk.Diag Info = 1, Warn = 2, Error = 3, - Fatal = 4 + Fatal = 4, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs b/src/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs index bb5770cbc..42165aab3 100644 --- a/src/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs +++ b/src/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Horizon.Sdk.Lm Result Log(Span<byte> message); Result SetDestination(LogDestination destination); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Lm/ILogService.cs b/src/Ryujinx.Horizon/Sdk/Lm/ILogService.cs index ad6c8455a..b66b91267 100644 --- a/src/Ryujinx.Horizon/Sdk/Lm/ILogService.cs +++ b/src/Ryujinx.Horizon/Sdk/Lm/ILogService.cs @@ -8,4 +8,4 @@ namespace Ryujinx.Horizon.Sdk.Lm { Result OpenLogger(out LmLogger logger, ulong pid); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs b/src/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs index 90756ece2..6905db0ce 100644 --- a/src/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs +++ b/src/Ryujinx.Horizon/Sdk/Lm/LogDataChunkKey.cs @@ -14,6 +14,6 @@ namespace Ryujinx.Horizon.Sdk.Lm Time = 9, ProgramName = 10, - Count + Count, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs b/src/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs index 8b08548d6..d9078629a 100644 --- a/src/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs +++ b/src/Ryujinx.Horizon/Sdk/Lm/LogDestination.cs @@ -9,6 +9,6 @@ namespace Ryujinx.Horizon.Sdk.Lm Uart = 1 << 1, UartIfSleep = 1 << 2, - All = 0xffff + All = 0xffff, } } diff --git a/src/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs b/src/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs index 75d9f40b9..e7d5d664f 100644 --- a/src/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs +++ b/src/Ryujinx.Horizon/Sdk/Lm/LogPacketFlags.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Horizon.Sdk.Lm { IsHead = 1 << 0, IsTail = 1 << 1, - IsLittleEndian = 1 << 2 + IsLittleEndian = 1 << 2, } } diff --git a/src/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs b/src/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs index 37b4cbfbc..652589e1a 100644 --- a/src/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs +++ b/src/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs @@ -49,4 +49,4 @@ return $"0x{Id:x}"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs index b500e6b3c..b82518d79 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs @@ -3,6 +3,6 @@ enum EventClearMode { ManualClear, - AutoClear + AutoClear, } } diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index 04bc8d1db..ad57152cb 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -7,9 +7,9 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl { class MultiWaitImpl { - private const int WaitTimedOut = -1; + private const int WaitTimedOut = -1; private const int WaitCancelled = -2; - private const int WaitInvalid = -3; + private const int WaitInvalid = -3; private readonly List<MultiWaitHolderBase> _multiWaits; @@ -63,10 +63,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl } } - if (result == null) - { - result = WaitAnyHandleImpl(infinite, timeout); - } + result ??= WaitAnyHandleImpl(infinite, timeout); UnlinkHoldersFromObjectsList(); _waitingThreadHandle = 0; @@ -98,7 +95,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl } else { - index = WaitSynchronization(objectHandles.Slice(0, count), minTimeout); + index = WaitSynchronization(objectHandles[..count], minTimeout); DebugUtil.Assert(index != WaitInvalid); } @@ -200,10 +197,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl { return WaitCancelled; } - else - { - result.AbortOnFailure(); - } + + result.AbortOnFailure(); return index; } diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs index 45ffd2587..3d5bb8108 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes enum InitializationState : byte { NotInitialized, - Initialized + Initialized, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs index 37ac22f0b..f5597847a 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes { class MultiWaitHolderOfEvent : MultiWaitHolder { - private Event _event; + private readonly Event _event; private LinkedListNode<MultiWaitHolderBase> _node; public override TriBool Signaled diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs index 6fc5c75b9..e5839a480 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs @@ -2,7 +2,7 @@ { class MultiWaitHolderOfHandle : MultiWaitHolder { - private int _handle; + private readonly int _handle; public override int Handle => _handle; diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs index cc7e84836..eac5e7c43 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes InitiallySignaled = signaled, ClearMode = clearMode, State = InitializationState.Initialized, - Lock = new object() + Lock = new object(), }; } diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs index 86dcd1fad..302922f12 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Horizon.Sdk.OsTypes { private const int ModuleId = 3; - public static Result OutOfResource => new Result(ModuleId, 9); + public static Result OutOfResource => new(ModuleId, 9); } } diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs index 338493d23..dee0fa43a 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs @@ -6,12 +6,12 @@ { NotInitialized, InitializedAsEvent, - InitializedAsInterProcess + InitializedAsInterProcess, } public InterProcessEventType InterProcessEvent; public InitializationState State; - public bool NotInitialized => State == InitializationState.NotInitialized; + public readonly bool NotInitialized => State == InitializationState.NotInitialized; } } diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs index 7debd9e27..868d10258 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs @@ -4,6 +4,6 @@ { False, True, - Undefined + Undefined, } } diff --git a/src/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs b/src/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs index 042cb4007..3f2628205 100644 --- a/src/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs +++ b/src/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs @@ -2,6 +2,7 @@ using Ryujinx.Horizon.Sdk.Account; using Ryujinx.Horizon.Sdk.Sf; using System; +using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId; namespace Ryujinx.Horizon.Sdk.Prepo { @@ -12,9 +13,9 @@ namespace Ryujinx.Horizon.Sdk.Prepo Result RequestImmediateTransmission(); Result GetTransmissionStatus(out int status); Result GetSystemSessionId(out ulong systemSessionId); - Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer); - Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer); + Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer); + Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer); Result IsUserAgreementCheckEnabled(out bool enabled); Result SetUserAgreementCheckEnabled(bool enabled); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs b/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs index fe6fcce15..ccd6c93a6 100644 --- a/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs +++ b/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs @@ -10,15 +10,15 @@ namespace Ryujinx.Horizon.Sdk public static Result SendRequest(out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan<byte> data) { ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress; - int tlsSize = Api.TlsMessageBufferSize; + int tlsSize = Api.TlsMessageBufferSize; using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize)) { - CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat() + CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat { - DataSize = data.Length, + DataSize = data.Length, RequestId = requestId, - SendPid = sendPid + SendPid = sendPid, }); data.CopyTo(request.Data); @@ -36,4 +36,4 @@ namespace Ryujinx.Horizon.Sdk return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs index beaff613f..882115013 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs @@ -3,10 +3,10 @@ struct CmifDomainInHeader { public CmifDomainRequestType Type; - public byte ObjectsCount; - public ushort DataSize; - public int ObjectId; - public uint Padding; - public uint Token; + public byte ObjectsCount; + public ushort DataSize; + public int ObjectId; + public uint Padding; + public uint Token; } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs index 2086d24c1..89766a421 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainOutHeader.cs @@ -2,7 +2,7 @@ { struct CmifDomainOutHeader { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint ObjectsCount; public uint Padding; public uint Padding2; diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs index 1a02e0825..4e52ff936 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs @@ -2,8 +2,8 @@ { enum CmifDomainRequestType : byte { - Invalid = 0, + Invalid = 0, SendMessage = 1, - Close = 2 + Close = 2, } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs index 0d23d33bb..f0b6f0c36 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { static class CmifMessage { - public const uint CmifInHeaderMagic = 0x49434653; // SFCI + public const uint CmifInHeaderMagic = 0x49434653; // SFCI public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO public static CmifRequest CreateRequest(Span<byte> output, CmifRequestFormat format) @@ -21,10 +21,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif } totalSize += Unsafe.SizeOf<CmifInHeader>() + format.DataSize; - totalSize = (totalSize + 1) & ~1; + totalSize = (totalSize + 1) & ~1; int outPointerSizeTableOffset = totalSize; - int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount; + int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount; totalSize += sizeof(ushort) * outPointerSizeTableSize; @@ -32,19 +32,19 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif CmifRequest request = new() { - Hipc = HipcMessage.WriteMessage(output, new HipcMetadata() + Hipc = HipcMessage.WriteMessage(output, new HipcMetadata { - Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request, - SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount, - SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount, - ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount, + Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request, + SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount, + SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount, + ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount, ExchangeBuffersCount = format.InOutBuffersCount, - DataWordsCount = rawDataSizeInWords, - ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount, - SendPid = format.SendPid, - CopyHandlesCount = format.HandlesCount, - MoveHandlesCount = 0 - }) + DataWordsCount = rawDataSizeInWords, + ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount, + SendPid = format.SendPid, + CopyHandlesCount = format.HandlesCount, + MoveHandlesCount = 0, + }), }; Span<uint> data = request.Hipc.DataWords; @@ -55,14 +55,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif int payloadSize = Unsafe.SizeOf<CmifInHeader>() + format.DataSize; - domainHeader = new CmifDomainInHeader() + domainHeader = new CmifDomainInHeader { - Type = CmifDomainRequestType.SendMessage, + Type = CmifDomainRequestType.SendMessage, ObjectsCount = (byte)format.ObjectsCount, - DataSize = (ushort)payloadSize, - ObjectId = format.ObjectId, - Padding = 0, - Token = format.Context + DataSize = (ushort)payloadSize, + ObjectId = format.ObjectId, + Padding = 0, + Token = format.Context, }; data = data[(Unsafe.SizeOf<CmifDomainInHeader>() / sizeof(uint))..]; @@ -72,12 +72,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif ref CmifInHeader header = ref MemoryMarshal.Cast<uint, CmifInHeader>(data)[0]; - header = new CmifInHeader() + header = new CmifInHeader { - Magic = CmifInHeaderMagic, - Version = format.Context != 0 ? 1u : 0u, + Magic = CmifInHeaderMagic, + Version = format.Context != 0 ? 1u : 0u, CommandId = format.RequestId, - Token = format.ObjectId != 0 ? 0u : format.Context + Token = format.ObjectId != 0 ? 0u : format.Context, }; request.Data = MemoryMarshal.Cast<uint, byte>(data)[Unsafe.SizeOf<CmifInHeader>()..]; @@ -86,7 +86,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif Span<byte> outPointerTable = MemoryMarshal.Cast<uint, byte>(request.Hipc.DataWords)[(outPointerSizeTableOffset - paddingSizeBefore)..]; - request.OutPointerSizes = MemoryMarshal.Cast<byte, ushort>(outPointerTable); + request.OutPointerSizes = MemoryMarshal.Cast<byte, ushort>(outPointerTable); request.ServerPointerSize = format.ServerPointerSize; return request; @@ -96,12 +96,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { HipcMessage responseMessage = new(input); - Span<byte> data = MemoryMarshal.Cast<uint, byte>(responseMessage.Data.DataWords); + Span<byte> data = MemoryMarshal.Cast<uint, byte>(responseMessage.Data.DataWords); Span<uint> objects = Span<uint>.Empty; if (isDomain) { - data = data[Unsafe.SizeOf<CmifDomainOutHeader>()..]; + data = data[Unsafe.SizeOf<CmifDomainOutHeader>()..]; objects = MemoryMarshal.Cast<byte, uint>(data[(Unsafe.SizeOf<CmifOutHeader>() + size)..]); } @@ -121,15 +121,15 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return header.Result; } - response = new CmifResponse() + response = new CmifResponse { - Data = data[Unsafe.SizeOf<CmifOutHeader>()..], - Objects = objects, + Data = data[Unsafe.SizeOf<CmifOutHeader>()..], + Objects = objects, CopyHandles = responseMessage.Data.CopyHandles, - MoveHandles = responseMessage.Data.MoveHandles + MoveHandles = responseMessage.Data.MoveHandles, }; return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs index 00b9d2bda..ae32e78d6 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs @@ -4,11 +4,11 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { struct CmifOutHeader { -#pragma warning disable CS0649 - public uint Magic; - public uint Version; +#pragma warning disable CS0649 // Field is never assigned to + public uint Magic; + public uint Version; public Result Result; - public uint Token; + public uint Token; #pragma warning restore CS0649 } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs index e44a84ec1..80772ad33 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs @@ -6,9 +6,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif ref struct CmifRequest { public HipcMessageData Hipc; - public Span<byte> Data; - public Span<ushort> OutPointerSizes; - public Span<uint> Objects; - public int ServerPointerSize; + public Span<byte> Data; + public Span<ushort> OutPointerSizes; + public Span<uint> Objects; + public int ServerPointerSize; } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs index 592f11f42..c32646e30 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs @@ -2,22 +2,22 @@ { struct CmifRequestFormat { -#pragma warning disable CS0649 - public int ObjectId; +#pragma warning disable CS0649 // Field is never assigned to + public int ObjectId; public uint RequestId; public uint Context; - public int DataSize; - public int ServerPointerSize; - public int InAutoBuffersCount; - public int OutAutoBuffersCount; - public int InBuffersCount; - public int OutBuffersCount; - public int InOutBuffersCount; - public int InPointersCount; - public int OutPointersCount; - public int OutFixedPointersCount; - public int ObjectsCount; - public int HandlesCount; + public int DataSize; + public int ServerPointerSize; + public int InAutoBuffersCount; + public int OutAutoBuffersCount; + public int InBuffersCount; + public int OutBuffersCount; + public int InOutBuffersCount; + public int InPointersCount; + public int OutPointersCount; + public int OutFixedPointersCount; + public int ObjectsCount; + public int HandlesCount; public bool SendPid; #pragma warning restore CS0649 } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs index 2ff31eb67..d1d8dc9c5 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { public ReadOnlySpan<byte> Data; public ReadOnlySpan<uint> Objects; - public ReadOnlySpan<int> CopyHandles; - public ReadOnlySpan<int> MoveHandles; + public ReadOnlySpan<int> CopyHandles; + public ReadOnlySpan<int> MoveHandles; } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs index 82c0648b6..4f6c50fc9 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs @@ -2,13 +2,13 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { enum CommandType { - Invalid = 0, - LegacyRequest = 1, - Close = 2, - LegacyControl = 3, - Request = 4, - Control = 5, + Invalid = 0, + LegacyRequest = 1, + Close = 2, + LegacyControl = 3, + Request = 4, + Control = 5, RequestWithContext = 6, - ControlWithContext = 7 + ControlWithContext = 7, } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs index b0b4498d9..ccfacd908 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return ProcessMessageImpl(ref context, ((DomainServiceObject)context.ServiceObject).GetServerDomain(), inRawData); } - private Result ProcessMessageImpl(ref ServiceDispatchContext context, ServerDomainBase domain, ReadOnlySpan<byte> inRawData) + private static Result ProcessMessageImpl(ref ServiceDispatchContext context, ServerDomainBase domain, ReadOnlySpan<byte> inRawData) { if (inRawData.Length < Unsafe.SizeOf<CmifDomainInHeader>()) { diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs index 796b8a789..20ac5f101 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs @@ -19,7 +19,6 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif private int InObjectsCount => _inObjectIds.Length; private int OutObjectsCount => _implMetadata.OutObjectsCount; - private int ImplOutHeadersSize => _implMetadata.OutHeadersSize; private int ImplOutDataTotalSize => _implMetadata.OutDataSize + _implMetadata.OutHeadersSize; public DomainServiceObjectProcessor(ServerDomainBase domain, int[] inObjectIds) diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs index 0f3b259af..3c37e8b2e 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/HandlesToClose.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public int this[int index] { - get + readonly get { return index switch { @@ -29,22 +29,39 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif 5 => _handle5, 6 => _handle6, 7 => _handle7, - _ => throw new IndexOutOfRangeException() + _ => throw new IndexOutOfRangeException(), }; } set { switch (index) { - case 0: _handle0 = value; break; - case 1: _handle1 = value; break; - case 2: _handle2 = value; break; - case 3: _handle3 = value; break; - case 4: _handle4 = value; break; - case 5: _handle5 = value; break; - case 6: _handle6 = value; break; - case 7: _handle7 = value; break; - default: throw new IndexOutOfRangeException(); + case 0: + _handle0 = value; + break; + case 1: + _handle1 = value; + break; + case 2: + _handle2 = value; + break; + case 3: + _handle3 = value; + break; + case 4: + _handle4 = value; + break; + case 5: + _handle5 = value; + break; + case 6: + _handle6 = value; + break; + case 7: + _handle7 = value; + break; + default: + throw new IndexOutOfRangeException(); } } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs index ad0e18244..23780c7c9 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { - struct PointerAndSize + readonly struct PointerAndSize { public static PointerAndSize Empty => new(0UL, 0UL); @@ -11,7 +11,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public PointerAndSize(ulong address, ulong size) { Address = address; - Size = size; + Size = size; } } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs index eabe544f4..0126d1f60 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ScopedInlineContextChange.cs @@ -2,7 +2,7 @@ using System; namespace Ryujinx.Horizon.Sdk.Sf.Cmif { - struct ScopedInlineContextChange : IDisposable + readonly struct ScopedInlineContextChange : IDisposable { private readonly int _previousContext; diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs index f789b6c0c..f0222991d 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs @@ -211,7 +211,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif private readonly EntryManager _entryManager; private readonly object _entryOwnerLock; private readonly HashSet<Domain> _domains; - private int _maxDomains; + private readonly int _maxDomains; public ServerDomainManager(int entryCount, int maxDomains) { diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs index 6a92e8d55..206676020 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs @@ -1,30 +1,30 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { - struct ServerMessageRuntimeMetadata + readonly struct ServerMessageRuntimeMetadata { - public ushort InDataSize { get; } - public ushort OutDataSize { get; } - public byte InHeadersSize { get; } - public byte OutHeadersSize { get; } - public byte InObjectsCount { get; } - public byte OutObjectsCount { get; } + public ushort InDataSize { get; } + public ushort OutDataSize { get; } + public byte InHeadersSize { get; } + public byte OutHeadersSize { get; } + public byte InObjectsCount { get; } + public byte OutObjectsCount { get; } public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10; public ServerMessageRuntimeMetadata( ushort inDataSize, ushort outDataSize, - byte inHeadersSize, - byte outHeadersSize, - byte inObjectsCount, - byte outObjectsCount) + byte inHeadersSize, + byte outHeadersSize, + byte inObjectsCount, + byte outObjectsCount) { - InDataSize = inDataSize; - OutDataSize = outDataSize; - InHeadersSize = inHeadersSize; - OutHeadersSize = outHeadersSize; - InObjectsCount = inObjectsCount; + InDataSize = inDataSize; + OutDataSize = outDataSize; + InHeadersSize = inHeadersSize; + OutHeadersSize = outHeadersSize; + InObjectsCount = inObjectsCount; OutObjectsCount = outObjectsCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs index 31be810d3..3339a1a60 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs @@ -5,14 +5,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { ref struct ServiceDispatchContext { - public IServiceObject ServiceObject; - public ServerSessionManager Manager; - public ServerSession Session; + public IServiceObject ServiceObject; + public ServerSessionManager Manager; + public ServerSession Session; public ServerMessageProcessor Processor; - public HandlesToClose HandlesToClose; - public PointerAndSize PointerBuffer; - public ReadOnlySpan<byte> InMessageBuffer; - public Span<byte> OutMessageBuffer; - public HipcMessage Request; + public HandlesToClose HandlesToClose; + public PointerAndSize PointerBuffer; + public ReadOnlySpan<byte> InMessageBuffer; + public Span<byte> OutMessageBuffer; + public HipcMessage Request; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs index 7fbd8eb84..286e94148 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchMeta.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { - struct ServiceDispatchMeta + readonly struct ServiceDispatchMeta { public ServiceDispatchTableBase DispatchTable { get; } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs index 21b342dff..145c17839 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public ServiceDispatchTable(string objectName, IReadOnlyDictionary<int, CommandHandler> entries) { _objectName = objectName; - _entries = entries; + _entries = entries; } public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData) @@ -30,4 +30,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs index 816000675..a127bfcd5 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public abstract Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData); - protected Result ProcessMessageImpl(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData, IReadOnlyDictionary<int, CommandHandler> entries, string objectName) + protected static Result ProcessMessageImpl(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData, IReadOnlyDictionary<int, CommandHandler> entries, string objectName) { if (inRawData.Length < Unsafe.SizeOf<CmifInHeader>()) { @@ -44,7 +44,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif // If ignore missing services is enabled, just pretend that everything is fine. PrepareForStubReply(ref context, out Span<byte> outRawData); CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData); - outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success }; + outHeader[0] = new CmifOutHeader { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success }; Logger.Warning?.Print(LogClass.Service, $"Missing service {objectName} (command ID: {commandId}) ignored"); @@ -80,7 +80,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return commandResult; } - outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = commandResult }; + outHeader[0] = new CmifOutHeader { Magic = CmifMessage.CmifOutHeaderMagic, Result = commandResult }; return Result.Success; } @@ -91,4 +91,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs index 47aedde96..f6b54403f 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs @@ -15,42 +15,42 @@ namespace Ryujinx.Horizon.Sdk.Sf OutCopyHandle, OutMoveHandle, OutObject, - ProcessId + ProcessId, } - struct CommandArg + readonly struct CommandArg { - public CommandArgType Type { get; } - public HipcBufferFlags BufferFlags { get; } - public ushort BufferFixedSize { get; } - public int ArgSize { get; } - public int ArgAlignment { get; } + public CommandArgType Type { get; } + public HipcBufferFlags BufferFlags { get; } + public ushort BufferFixedSize { get; } + public int ArgSize { get; } + public int ArgAlignment { get; } public CommandArg(CommandArgType type) { - Type = type; - BufferFlags = default; + Type = type; + BufferFlags = default; BufferFixedSize = 0; - ArgSize = 0; - ArgAlignment = 0; + ArgSize = 0; + ArgAlignment = 0; } public CommandArg(CommandArgType type, int argSize, int argAlignment) { - Type = type; - BufferFlags = default; + Type = type; + BufferFlags = default; BufferFixedSize = 0; - ArgSize = argSize; - ArgAlignment = argAlignment; + ArgSize = argSize; + ArgAlignment = argAlignment; } public CommandArg(HipcBufferFlags flags, ushort fixedSize = 0) { - Type = CommandArgType.Buffer; - BufferFlags = flags; + Type = CommandArgType.Buffer; + BufferFlags = flags; BufferFixedSize = fixedSize; - ArgSize = 0; - ArgAlignment = 0; + ArgSize = 0; + ArgAlignment = 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs index 294c7d58d..5b7c302f2 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Horizon.Sdk.Sf [AttributeUsage(AttributeTargets.Parameter)] class BufferAttribute : Attribute { - public HipcBufferFlags Flags { get; } - public ushort FixedSize { get; } + public HipcBufferFlags Flags { get; } + public ushort FixedSize { get; } public BufferAttribute(HipcBufferFlags flags) { @@ -16,7 +16,7 @@ namespace Ryujinx.Horizon.Sdk.Sf public BufferAttribute(HipcBufferFlags flags, ushort fixedSize) { - Flags = flags | HipcBufferFlags.FixedSize; + Flags = flags | HipcBufferFlags.FixedSize; FixedSize = fixedSize; } } @@ -35,4 +35,4 @@ namespace Ryujinx.Horizon.Sdk.Sf class MoveHandleAttribute : Attribute { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs index 081ce3be5..fb88eaaa0 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs @@ -9,20 +9,20 @@ namespace Ryujinx.Horizon.Sdk.Sf class CommandHandler { public delegate Result MethodInvoke( - ref ServiceDispatchContext context, - HipcCommandProcessor processor, + ref ServiceDispatchContext context, + HipcCommandProcessor processor, ServerMessageRuntimeMetadata runtimeMetadata, - ReadOnlySpan<byte> inRawData, - ref Span<CmifOutHeader> outHeader); + ReadOnlySpan<byte> inRawData, + ref Span<CmifOutHeader> outHeader); - private readonly MethodInvoke _invoke; + private readonly MethodInvoke _invoke; private readonly HipcCommandProcessor _processor; public string MethodName => _invoke.Method.Name; public CommandHandler(MethodInvoke invoke, params CommandArg[] args) { - _invoke = invoke; + _invoke = invoke; _processor = new HipcCommandProcessor(args); } @@ -37,16 +37,16 @@ namespace Ryujinx.Horizon.Sdk.Sf context.Processor.SetImplementationProcessor(_processor); } - var runtimeMetadata = context.Processor.GetRuntimeMetadata(); - Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); + var runtimeMetadata = context.Processor.GetRuntimeMetadata(); + Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); return result.IsFailure ? result : _invoke(ref context, _processor, runtimeMetadata, inRawData, ref outHeader); } public static void GetCmifOutHeaderPointer(ref Span<CmifOutHeader> outHeader, ref Span<byte> outRawData) { - outHeader = MemoryMarshal.Cast<byte, CmifOutHeader>(outRawData)[..1]; + outHeader = MemoryMarshal.Cast<byte, CmifOutHeader>(outRawData)[..1]; outRawData = outRawData[Unsafe.SizeOf<CmifOutHeader>()..]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs b/src/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs index 4205d3c16..a14892a8d 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs @@ -66,4 +66,4 @@ namespace Ryujinx.Horizon.Sdk.Sf response.MoveHandles[index] = value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs index 33c42825f..530f81bd1 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs @@ -41,10 +41,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { return HorizonStatic.Syscall.ReplyAndReceive(out _, handles, 0, -1L); } - else - { - throw new NotImplementedException(); - } + + throw new NotImplementedException(); } public static Result Reply(int sessionHandle, ReadOnlySpan<byte> messageBuffer) @@ -64,10 +62,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { return HorizonStatic.Syscall.ReplyAndReceive(out _, ReadOnlySpan<int>.Empty, sessionHandle, 0); } - else - { - throw new NotImplementedException(); - } + + throw new NotImplementedException(); } public static Result CreateSession(out int serverHandle, out int clientHandle) @@ -82,4 +78,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return result; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs index cdb50b578..04abf6937 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs @@ -10,55 +10,55 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public CommandType Type { - get => (CommandType)_word0.Extract(0, 16); + readonly get => (CommandType)_word0.Extract(0, 16); set => _word0 = _word0.Insert(0, 16, (uint)value); } public int SendStaticsCount { - get => (int)_word0.Extract(16, 4); + readonly get => (int)_word0.Extract(16, 4); set => _word0 = _word0.Insert(16, 4, (uint)value); } public int SendBuffersCount { - get => (int)_word0.Extract(20, 4); + readonly get => (int)_word0.Extract(20, 4); set => _word0 = _word0.Insert(20, 4, (uint)value); } public int ReceiveBuffersCount { - get => (int)_word0.Extract(24, 4); + readonly get => (int)_word0.Extract(24, 4); set => _word0 = _word0.Insert(24, 4, (uint)value); } public int ExchangeBuffersCount { - get => (int)_word0.Extract(28, 4); + readonly get => (int)_word0.Extract(28, 4); set => _word0 = _word0.Insert(28, 4, (uint)value); } public int DataWordsCount { - get => (int)_word1.Extract(0, 10); + readonly get => (int)_word1.Extract(0, 10); set => _word1 = _word1.Insert(0, 10, (uint)value); } public int ReceiveStaticMode { - get => (int)_word1.Extract(10, 4); + readonly get => (int)_word1.Extract(10, 4); set => _word1 = _word1.Insert(10, 4, (uint)value); } public int ReceiveListOffset { - get => (int)_word1.Extract(20, 11); + readonly get => (int)_word1.Extract(20, 11); set => _word1 = _word1.Insert(20, 11, (uint)value); } public bool HasSpecialHeader { - get => _word1.Extract(31); + readonly get => _word1.Extract(31); set => _word1 = _word1.Insert(31, value); } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs index 7778d5bca..bef772e67 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs @@ -1,11 +1,11 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { - struct HipcBufferDescriptor + readonly struct HipcBufferDescriptor { -#pragma warning disable CS0649 - private uint _sizeLow; - private uint _addressLow; - private uint _word2; +#pragma warning disable CS0649 // Field is never assigned to + private readonly uint _sizeLow; + private readonly uint _addressLow; + private readonly uint _word2; #pragma warning restore CS0649 public ulong Address => _addressLow | (((ulong)_word2 << 4) & 0xf00000000UL) | (((ulong)_word2 << 34) & 0x7000000000UL); diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs index 269ab4fe5..b1523d61d 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs @@ -5,13 +5,13 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc [Flags] enum HipcBufferFlags : byte { - In = 1 << 0, - Out = 1 << 1, - MapAlias = 1 << 2, - Pointer = 1 << 3, - FixedSize = 1 << 4, - AutoSelect = 1 << 5, + In = 1 << 0, + Out = 1 << 1, + MapAlias = 1 << 2, + Pointer = 1 << 3, + FixedSize = 1 << 4, + AutoSelect = 1 << 5, MapTransferAllowsNonSecure = 1 << 6, - MapTransferAllowsNonDevice = 1 << 7 + MapTransferAllowsNonDevice = 1 << 7, } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs index b1e67253c..bc2d5336c 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs @@ -2,9 +2,9 @@ { enum HipcBufferMode { - Normal = 0, + Normal = 0, NonSecure = 1, - Invalid = 2, - NonDevice = 3 + Invalid = 2, + NonDevice = 3, } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs index 7541e2941..f72d8e81d 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs @@ -112,4 +112,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs index 6500d6cf7..82cf6e75f 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs @@ -10,9 +10,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { public const int AutoReceiveStatic = byte.MaxValue; - public HipcMetadata Meta; + public HipcMetadata Meta; public HipcMessageData Data; - public ulong Pid; + public ulong Pid; public HipcMessage(Span<byte> data) { @@ -22,8 +22,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc data = data[Unsafe.SizeOf<Header>()..]; - int receiveStaticsCount = 0; - ulong pid = 0; + int receiveStaticsCount = 0; + ulong pid = 0; if (header.ReceiveStaticMode != 0) { @@ -42,75 +42,75 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (header.HasSpecialHeader) { specialHeader = MemoryMarshal.Cast<byte, SpecialHeader>(data)[0]; - data = data[Unsafe.SizeOf<SpecialHeader>()..]; + data = data[Unsafe.SizeOf<SpecialHeader>()..]; if (specialHeader.SendPid) { - pid = MemoryMarshal.Cast<byte, ulong>(data)[0]; + pid = MemoryMarshal.Cast<byte, ulong>(data)[0]; data = data[sizeof(ulong)..]; } } - Meta = new HipcMetadata() + Meta = new HipcMetadata { - Type = (int)header.Type, - SendStaticsCount = header.SendStaticsCount, - SendBuffersCount = header.SendBuffersCount, - ReceiveBuffersCount = header.ReceiveBuffersCount, + Type = (int)header.Type, + SendStaticsCount = header.SendStaticsCount, + SendBuffersCount = header.SendBuffersCount, + ReceiveBuffersCount = header.ReceiveBuffersCount, ExchangeBuffersCount = header.ExchangeBuffersCount, - DataWordsCount = header.DataWordsCount, - ReceiveStaticsCount = receiveStaticsCount, - SendPid = specialHeader.SendPid, - CopyHandlesCount = specialHeader.CopyHandlesCount, - MoveHandlesCount = specialHeader.MoveHandlesCount + DataWordsCount = header.DataWordsCount, + ReceiveStaticsCount = receiveStaticsCount, + SendPid = specialHeader.SendPid, + CopyHandlesCount = specialHeader.CopyHandlesCount, + MoveHandlesCount = specialHeader.MoveHandlesCount, }; Data = CreateMessageData(Meta, data, initialLength); - Pid = pid; + Pid = pid; } public static HipcMessageData WriteResponse( Span<byte> destination, - int sendStaticCount, - int dataWordsCount, - int copyHandlesCount, - int moveHandlesCount) + int sendStaticCount, + int dataWordsCount, + int copyHandlesCount, + int moveHandlesCount) { - return WriteMessage(destination, new HipcMetadata() + return WriteMessage(destination, new HipcMetadata { SendStaticsCount = sendStaticCount, - DataWordsCount = dataWordsCount, + DataWordsCount = dataWordsCount, CopyHandlesCount = copyHandlesCount, - MoveHandlesCount = moveHandlesCount + MoveHandlesCount = moveHandlesCount, }); } public static HipcMessageData WriteMessage(Span<byte> destination, HipcMetadata meta) { - int initialLength = destination.Length; + int initialLength = destination.Length; bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0; - MemoryMarshal.Cast<byte, Header>(destination)[0] = new Header() + MemoryMarshal.Cast<byte, Header>(destination)[0] = new Header { - Type = (CommandType)meta.Type, - SendStaticsCount = meta.SendStaticsCount, - SendBuffersCount = meta.SendBuffersCount, - ReceiveBuffersCount = meta.ReceiveBuffersCount, + Type = (CommandType)meta.Type, + SendStaticsCount = meta.SendStaticsCount, + SendBuffersCount = meta.SendBuffersCount, + ReceiveBuffersCount = meta.ReceiveBuffersCount, ExchangeBuffersCount = meta.ExchangeBuffersCount, - DataWordsCount = meta.DataWordsCount, - ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0, - HasSpecialHeader = hasSpecialHeader + DataWordsCount = meta.DataWordsCount, + ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0, + HasSpecialHeader = hasSpecialHeader, }; destination = destination[Unsafe.SizeOf<Header>()..]; if (hasSpecialHeader) { - MemoryMarshal.Cast<byte, SpecialHeader>(destination)[0] = new SpecialHeader() + MemoryMarshal.Cast<byte, SpecialHeader>(destination)[0] = new SpecialHeader { - SendPid = meta.SendPid, + SendPid = meta.SendPid, CopyHandlesCount = meta.CopyHandlesCount, - MoveHandlesCount = meta.MoveHandlesCount + MoveHandlesCount = meta.MoveHandlesCount, }; destination = destination[Unsafe.SizeOf<SpecialHeader>()..]; @@ -184,9 +184,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (meta.DataWordsCount != 0) { - int dataOffset = initialLength - data.Length; + int dataOffset = initialLength - data.Length; int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10); - int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); + int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); dataWords = MemoryMarshal.Cast<byte, uint>(data)[padding..meta.DataWordsCount]; @@ -202,16 +202,16 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc receiveList = MemoryMarshal.Cast<byte, HipcReceiveListEntry>(data)[..receiveListSize]; } - return new HipcMessageData() + return new HipcMessageData { - SendStatics = sendStatics, - SendBuffers = sendBuffers, - ReceiveBuffers = receiveBuffers, + SendStatics = sendStatics, + SendBuffers = sendBuffers, + ReceiveBuffers = receiveBuffers, ExchangeBuffers = exchangeBuffers, - DataWords = dataWords, - ReceiveList = receiveList, - CopyHandles = copyHandles, - MoveHandles = moveHandles + DataWords = dataWords, + ReceiveList = receiveList, + CopyHandles = copyHandles, + MoveHandles = moveHandles, }; } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs index 154b8f079..c83c422ca 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs @@ -8,9 +8,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public Span<HipcBufferDescriptor> SendBuffers; public Span<HipcBufferDescriptor> ReceiveBuffers; public Span<HipcBufferDescriptor> ExchangeBuffers; - public Span<uint> DataWords; + public Span<uint> DataWords; public Span<HipcReceiveListEntry> ReceiveList; - public Span<int> CopyHandles; - public Span<int> MoveHandles; + public Span<int> CopyHandles; + public Span<int> MoveHandles; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs index 10abc4006..fe13137a6 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs @@ -2,15 +2,15 @@ { struct HipcMetadata { - public int Type; - public int SendStaticsCount; - public int SendBuffersCount; - public int ReceiveBuffersCount; - public int ExchangeBuffersCount; - public int DataWordsCount; - public int ReceiveStaticsCount; + public int Type; + public int SendStaticsCount; + public int SendBuffersCount; + public int ReceiveBuffersCount; + public int ExchangeBuffersCount; + public int DataWordsCount; + public int ReceiveStaticsCount; public bool SendPid; - public int CopyHandlesCount; - public int MoveHandlesCount; + public int CopyHandlesCount; + public int MoveHandlesCount; } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs index 56cf16fb0..955428b84 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs @@ -1,14 +1,16 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { - struct HipcReceiveListEntry + readonly struct HipcReceiveListEntry { - private uint _addressLow; - private uint _word1; +#pragma warning disable IDE0052 // Remove unread private member + private readonly uint _addressLow; + private readonly uint _word1; +#pragma warning restore IDE0052 public HipcReceiveListEntry(ulong address, ulong size) { _addressLow = (uint)address; - _word1 = (ushort)(address >> 32) | (uint)(size << 16); + _word1 = (ushort)(address >> 32) | (uint)(size << 16); } } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs index 3b483be81..faf5dc410 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { public const int ModuleId = 11; +#pragma warning disable IDE0055 // Disable formatting public static Result OutOfSessionMemory => new(ModuleId, 102); public static Result OutOfSessions => new(ModuleId, 131); public static Result PointerBufferTooSmall => new(ModuleId, 141); @@ -15,5 +16,6 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public static Result InvalidCmifRequest => new(ModuleId, 420); public static Result TargetNotDomain => new(ModuleId, 491); public static Result DomainObjectNotFound => new(ModuleId, 492); + #pragma warning restore IDE0055 } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs index 103820a6b..43e7afb9d 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs @@ -1,12 +1,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { - struct HipcStaticDescriptor + readonly struct HipcStaticDescriptor { private readonly ulong _data; - public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32); - public ushort Size => (ushort)(_data >> 16); - public int ReceiveIndex => (int)(_data & 0xf); + public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32); + public ushort Size => (ushort)(_data >> 16); + public int ReceiveIndex => (int)(_data & 0xf); public HipcStaticDescriptor(ulong address, ushort size, int receiveIndex) { @@ -19,4 +19,4 @@ _data = data; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs index b99d63c5e..e747490e6 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs @@ -1,20 +1,20 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { - struct ManagerOptions + readonly struct ManagerOptions { public static ManagerOptions Default => new(0, 0, 0, false); - public int PointerBufferSize { get; } - public int MaxDomains { get; } - public int MaxDomainObjects { get; } + public int PointerBufferSize { get; } + public int MaxDomains { get; } + public int MaxDomainObjects { get; } public bool CanDeferInvokeRequest { get; } public ManagerOptions(int pointerBufferSize, int maxDomains, int maxDomainObjects, bool canDeferInvokeRequest) { - PointerBufferSize = pointerBufferSize; - MaxDomains = maxDomains; - MaxDomainObjects = maxDomainObjects; + PointerBufferSize = pointerBufferSize; + MaxDomains = maxDomains; + MaxDomainObjects = maxDomainObjects; CanDeferInvokeRequest = canDeferInvokeRequest; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs index 7c380a011..efe99f3f9 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs @@ -4,6 +4,6 @@ { Success, Closed, - NeedsRetry + NeedsRetry, } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs index bbbab8985..923f2d52d 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs @@ -6,22 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { class Server : MultiWaitHolderOfHandle { - public int PortIndex { get; } - public int PortHandle { get; } - public ServiceName Name { get; } - public bool Managed { get; } + public int PortIndex { get; } + public int PortHandle { get; } + public ServiceName Name { get; } + public bool Managed { get; } public ServiceObjectHolder StaticObject { get; } public Server( - int portIndex, - int portHandle, - ServiceName name, - bool managed, + int portIndex, + int portHandle, + ServiceName name, + bool managed, ServiceObjectHolder staticHoder) : base(portHandle) { PortHandle = portHandle; - Name = name; - Managed = managed; + Name = name; + Managed = managed; if (staticHoder != null) { diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs index 2ca9ceea2..9ac2a337e 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs @@ -14,8 +14,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private readonly bool _canDeferInvokeRequest; private readonly int _maxSessions; - private ulong _pointerBuffersBaseAddress; - private ulong _savedMessagesBaseAddress; + private readonly ulong _pointerBuffersBaseAddress; + private readonly ulong _savedMessagesBaseAddress; private readonly object _resourceLock; private readonly ulong[] _sessionAllocationBitmap; @@ -35,7 +35,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (options.CanDeferInvokeRequest) { - _savedMessagesBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)Api.TlsMessageBufferSize); + _savedMessagesBaseAddress = allocator.Allocate((ulong)maxSessions * Api.TlsMessageBufferSize); } } @@ -45,7 +45,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc _servers = new HashSet<Server>(); } - private PointerAndSize GetObjectBySessionIndex(ServerSession session, ulong baseAddress, ulong size) + private static PointerAndSize GetObjectBySessionIndex(ServerSession session, ulong baseAddress, ulong size) { return new PointerAndSize(baseAddress + (ulong)session.SessionIndex * size, size); } @@ -61,7 +61,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return null; } - for (int i = 0; i <_sessionAllocationBitmap.Length; i++) + for (int i = 0; i < _sessionAllocationBitmap.Length; i++) { ref ulong mask = ref _sessionAllocationBitmap[i]; @@ -145,10 +145,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { return GetObjectBySessionIndex(session, _pointerBuffersBaseAddress, (ulong)_pointerBufferSize); } - else - { - return PointerAndSize.Empty; - } + + return PointerAndSize.Empty; } protected override PointerAndSize GetSessionSavedMessageBuffer(ServerSession session) @@ -157,10 +155,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { return GetObjectBySessionIndex(session, _savedMessagesBaseAddress, Api.TlsMessageBufferSize); } - else - { - return PointerAndSize.Empty; - } + + return PointerAndSize.Empty; } protected virtual void Dispose(bool disposing) diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs index c36cdda26..764078408 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { private readonly SmApi _sm; - private bool _canDeferInvokeRequest; + private readonly bool _canDeferInvokeRequest; private readonly MultiWait _multiWait; private readonly MultiWait _waitList; @@ -26,8 +26,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private enum UserDataTag { - Server = 1, - Session = 2 + Server = 1, + Session = 2, } public ServerManagerBase(SmApi sm, ManagerOptions options) : base(options.MaxDomainObjects, options.MaxDomains) @@ -36,13 +36,13 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc _canDeferInvokeRequest = options.CanDeferInvokeRequest; _multiWait = new MultiWait(); - _waitList = new MultiWait(); + _waitList = new MultiWait(); _multiWaitSelectionLock = new object(); - _waitListLock = new object(); + _waitListLock = new object(); _requestStopEvent = new Event(EventClearMode.ManualClear); - _notifyEvent = new Event(EventClearMode.ManualClear); + _notifyEvent = new Event(EventClearMode.ManualClear); _requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent); _multiWait.LinkMultiWaitHolder(_requestStopEventHolder); @@ -113,7 +113,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public void ServiceRequests() { - while (WaitAndProcessRequestsImpl()); + while (WaitAndProcessRequestsImpl()) + { + } } public void WaitAndProcessRequests() @@ -183,7 +185,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected override void RegisterSessionToWaitList(ServerSession session) { session.HasReceived = false; - session.UserData = UserDataTag.Session; + session.UserData = UserDataTag.Session; RegisterToWaitList(session); } @@ -209,9 +211,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { return (UserDataTag)holder.UserData switch { - UserDataTag.Server => ProcessForServer(holder), + UserDataTag.Server => ProcessForServer(holder), UserDataTag.Session => ProcessForSession(holder), - _ => throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()) + _ => throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()), }; } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs index a17300823..eb98fefd0 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs @@ -6,18 +6,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc class ServerSession : MultiWaitHolderOfHandle { public ServiceObjectHolder ServiceObjectHolder { get; set; } - public PointerAndSize PointerBuffer { get; set; } - public PointerAndSize SavedMessage { get; set; } - public int SessionIndex { get; } - public int SessionHandle { get; } - public bool IsClosed { get; set; } - public bool HasReceived { get; set; } + public PointerAndSize PointerBuffer { get; set; } + public PointerAndSize SavedMessage { get; set; } + public int SessionIndex { get; } + public int SessionHandle { get; } + public bool IsClosed { get; set; } + public bool HasReceived { get; set; } public ServerSession(int index, int handle, ServiceObjectHolder obj) : base(handle) { ServiceObjectHolder = obj; - SessionIndex = index; - SessionHandle = handle; + SessionIndex = index; + SessionHandle = handle; } } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs index 6d3950813..bd5a48444 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs @@ -75,7 +75,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } session.PointerBuffer = GetSessionPointerBuffer(session); - session.SavedMessage = GetSessionSavedMessageBuffer(session); + session.SavedMessage = GetSessionSavedMessageBuffer(session); RegisterSessionToWaitList(session); @@ -110,10 +110,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } protected virtual Server AllocateServer( - int portIndex, - int portHandle, - ServiceName name, - bool managed, + int portIndex, + int portHandle, + ServiceName name, + bool managed, ServiceObjectHolder staticHoder) { throw new NotSupportedException(); @@ -161,29 +161,25 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return Result.Success; } - else + + Result result = ProcessRequestImpl(session, message, message); + + if (result.IsSuccess) { - Result result = ProcessRequestImpl(session, message, message); + RegisterSessionToWaitList(session); - if (result.IsSuccess) - { - RegisterSessionToWaitList(session); - - return Result.Success; - } - else if (SfResult.RequestContextChanged(result)) - { - return result; - } - else - { - Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}"); - - CloseSessionImpl(session); - - return Result.Success; - } + return Result.Success; } + else if (SfResult.RequestContextChanged(result)) + { + return result; + } + + Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}"); + + CloseSessionImpl(session); + + return Result.Success; } private Result ProcessRequestImpl(ServerSession session, Span<byte> inMessage, Span<byte> outMessage) @@ -192,18 +188,13 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc using var _ = new ScopedInlineContextChange(GetInlineContext(commandType, inMessage)); - switch (commandType) + return commandType switch { - case CommandType.Request: - case CommandType.RequestWithContext: - return DispatchRequest(session.ServiceObjectHolder, session, inMessage, outMessage); - case CommandType.Control: - case CommandType.ControlWithContext: - return DispatchManagerRequest(session, inMessage, outMessage); - default: - return HipcResult.UnknownCommandType; + CommandType.Request or CommandType.RequestWithContext => DispatchRequest(session.ServiceObjectHolder, session, inMessage, outMessage), + CommandType.Control or CommandType.ControlWithContext => DispatchManagerRequest(session, inMessage, outMessage), + _ => HipcResult.UnknownCommandType, + }; } - } private static int GetInlineContext(CommandType commandType, ReadOnlySpan<byte> inMessage) { @@ -221,12 +212,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return 0; } - protected Result ReceiveRequest(ServerSession session, Span<byte> message) + protected static Result ReceiveRequest(ServerSession session, Span<byte> message) { return ReceiveRequestImpl(session, message); } - private Result ReceiveRequestImpl(ServerSession session, Span<byte> message) + private static Result ReceiveRequestImpl(ServerSession session, Span<byte> message) { PointerAndSize pointerBuffer = session.PointerBuffer; @@ -234,19 +225,19 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { if (pointerBuffer.Address != 0) { - HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata() + HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata { - Type = (int)CommandType.Invalid, - ReceiveStaticsCount = HipcMessage.AutoReceiveStatic + Type = (int)CommandType.Invalid, + ReceiveStaticsCount = HipcMessage.AutoReceiveStatic, }); messageData.ReceiveList[0] = new HipcReceiveListEntry(pointerBuffer.Address, pointerBuffer.Size); } else { - MemoryMarshal.Cast<byte, Header>(message)[0] = new Header() + MemoryMarshal.Cast<byte, Header>(message)[0] = new Header { - Type = CommandType.Invalid + Type = CommandType.Invalid, }; } @@ -276,9 +267,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected virtual Result DispatchRequest( ServiceObjectHolder objectHolder, - ServerSession session, - Span<byte> inMessage, - Span<byte> outMessage) + ServerSession session, + Span<byte> inMessage, + Span<byte> outMessage) { HipcMessage request; @@ -291,16 +282,16 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return HipcResult.InvalidRequestSize; } - var dispatchCtx = new ServiceDispatchContext() + var dispatchCtx = new ServiceDispatchContext { - ServiceObject = objectHolder.ServiceObject, - Manager = this, - Session = session, - HandlesToClose = new HandlesToClose(), - PointerBuffer = session.PointerBuffer, - InMessageBuffer = inMessage, + ServiceObject = objectHolder.ServiceObject, + Manager = this, + Session = session, + HandlesToClose = new HandlesToClose(), + PointerBuffer = session.PointerBuffer, + InMessageBuffer = inMessage, OutMessageBuffer = outMessage, - Request = request + Request = request, }; ReadOnlySpan<byte> inRawData = MemoryMarshal.Cast<uint, byte>(dispatchCtx.Request.Data.DataWords); @@ -337,4 +328,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return this; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs index 8b747626d..b6304b7b6 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs @@ -8,19 +8,19 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public bool SendPid { - get => _word.Extract(0); + readonly get => _word.Extract(0); set => _word = _word.Insert(0, value); } public int CopyHandlesCount { - get => (int)_word.Extract(1, 4); + readonly get => (int)_word.Extract(1, 4); set => _word = _word.Insert(1, 4, (uint)value); } public int MoveHandlesCount { - get => (int)_word.Extract(5, 4); + readonly get => (int)_word.Extract(5, 4); set => _word = _word.Insert(5, 4, (uint)value); } } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/src/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs index a0578d48f..08b1d89b9 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs @@ -134,9 +134,9 @@ namespace Ryujinx.Horizon.Sdk.Sf ulong pointerBufferTail = context.PointerBuffer.Address; ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size; - int sendMapAliasIndex = 0; - int recvMapAliasIndex = 0; - int sendPointerIndex = 0; + int sendMapAliasIndex = 0; + int recvMapAliasIndex = 0; + int sendPointerIndex = 0; int unfixedRecvPointerIndex = 0; for (int i = 0; i < _args.Length; i++) @@ -186,8 +186,8 @@ namespace Ryujinx.Horizon.Sdk.Sf if (flags.HasFlag(HipcBufferFlags.In)) { var descriptor = context.Request.Data.SendStatics[sendPointerIndex++]; - ulong address = descriptor.Address; - ulong size = descriptor.Size; + ulong address = descriptor.Address; + ulong size = descriptor.Size; _bufferRanges[i] = new PointerAndSize(address, size); @@ -206,14 +206,14 @@ namespace Ryujinx.Horizon.Sdk.Sf } else { - var data = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWords); + var data = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWords); var recvPointerSizes = MemoryMarshal.Cast<byte, ushort>(data[runtimeMetadata.UnfixedOutPointerSizeOffset..]); size = recvPointerSizes[unfixedRecvPointerIndex++]; } pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL); - _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); + _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); } } } @@ -305,13 +305,13 @@ namespace Ryujinx.Horizon.Sdk.Sf { ref var meta = ref context.Request.Meta; bool requestValid = true; - requestValid &= meta.SendPid == _hasInProcessIdHolder; - requestValid &= meta.SendStaticsCount == _inPointerBuffersCount; - requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount; - requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount; + requestValid &= meta.SendPid == _hasInProcessIdHolder; + requestValid &= meta.SendStaticsCount == _inPointerBuffersCount; + requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount; + requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount; requestValid &= meta.ExchangeBuffersCount == 0; - requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount; - requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount; + requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount; + requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount; int rawSizeInBytes = meta.DataWordsCount * sizeof(uint); int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint)); @@ -345,7 +345,7 @@ namespace Ryujinx.Horizon.Sdk.Sf continue; } - int index = inObjectIndex++; + int index = inObjectIndex++; var inObject = inObjects[index]; objects[index] = inObject?.ServiceObject; @@ -386,7 +386,9 @@ namespace Ryujinx.Horizon.Sdk.Sf outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords); } +#pragma warning disable CA1822 // Mark member as static public void SetOutObjects(ref ServiceDispatchContext context, HipcMessageData response, Span<IServiceObject> objects) +#pragma warning restore CA1822 { if (objects.Length == 0) { @@ -411,7 +413,7 @@ namespace Ryujinx.Horizon.Sdk.Sf } } - private void SetOutObjectImpl(int index, HipcMessageData response, ServerSessionManager manager, ServiceObjectHolder obj) + private static void SetOutObjectImpl(int index, HipcMessageData response, ServerSessionManager manager, ServiceObjectHolder obj) { if (obj == null) { @@ -425,4 +427,4 @@ namespace Ryujinx.Horizon.Sdk.Sf response.MoveHandles[index] = clientHandle; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs b/src/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs index 10e4f9094..0172c1151 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs @@ -12,15 +12,15 @@ namespace Ryujinx.Horizon.Sdk.Sf { int argsCount = args.Length; - int[] sizes = new int[argsCount]; + int[] sizes = new int[argsCount]; int[] aligns = new int[argsCount]; - int[] map = new int[argsCount]; + int[] map = new int[argsCount]; for (int i = 0; i < argsCount; i++) { - sizes[i] = args[i].ArgSize; + sizes[i] = args[i].ArgSize; aligns[i] = args[i].ArgAlignment; - map[i] = i; + map[i] = i; } for (int i = 1; i < argsCount; i++) @@ -35,9 +35,9 @@ namespace Ryujinx.Horizon.Sdk.Sf foreach (int i in map) { - offset = BitUtils.AlignUp(offset, aligns[i]); + offset = BitUtils.AlignUp(offset, aligns[i]); offsets[i] = offset; - offset += sizes[i]; + offset += sizes[i]; } offsets[argsCount] = offset; @@ -46,4 +46,4 @@ namespace Ryujinx.Horizon.Sdk.Sf return offsets; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sf/SfResult.cs b/src/Ryujinx.Horizon/Sdk/Sf/SfResult.cs index 72502d17e..029e17af0 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/SfResult.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/SfResult.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Horizon.Sdk.Sf { public const int ModuleId = 10; +#pragma warning disable IDE0055 // Disable formatting public static Result NotSupported => new(ModuleId, 1); public static Result InvalidHeaderSize => new(ModuleId, 202); public static Result InvalidInHeader => new(ModuleId, 211); @@ -23,5 +24,6 @@ namespace Ryujinx.Horizon.Sdk.Sf public static bool RequestContextChanged(Result result) => result.InRange(800, 899); public static bool Invalidated(Result result) => result.InRange(801, 809); public static bool RequestDeferred(Result result) => result.InRange(811, 819); +#pragma warning restore IDE0055 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs b/src/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs index 644285834..2343c7d6f 100644 --- a/src/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs +++ b/src/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs @@ -5,4 +5,4 @@ namespace Ryujinx.Horizon.Sdk.Sm interface IManagerService : IServiceObject { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sm/IUserService.cs b/src/Ryujinx.Horizon/Sdk/Sm/IUserService.cs index ad9bc9d7b..8605cdd1b 100644 --- a/src/Ryujinx.Horizon/Sdk/Sm/IUserService.cs +++ b/src/Ryujinx.Horizon/Sdk/Sm/IUserService.cs @@ -10,4 +10,4 @@ namespace Ryujinx.Horizon.Sdk.Sm Result RegisterService(out int handle, ServiceName name, int maxSessions, bool isLight); Result UnregisterService(ServiceName name); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs b/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs index 9b7fae3f5..f90d39c22 100644 --- a/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs +++ b/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs @@ -4,13 +4,13 @@ using System.Runtime.InteropServices; namespace Ryujinx.Horizon.Sdk.Sm { [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ServiceName + readonly struct ServiceName { - public static ServiceName Invalid { get; } = new ServiceName(0); + public static ServiceName Invalid { get; } = new(0); public bool IsValid => Packed != 0; - public int Length => sizeof(ulong); + public const int Length = sizeof(ulong); public ulong Packed { get; } diff --git a/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs b/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs index 533e68d9c..3e5635bf1 100644 --- a/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs +++ b/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs @@ -110,4 +110,4 @@ namespace Ryujinx.Horizon.Sdk.Sm return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/ServiceEntry.cs b/src/Ryujinx.Horizon/ServiceEntry.cs index 06152d9ff..edf76fcd8 100644 --- a/src/Ryujinx.Horizon/ServiceEntry.cs +++ b/src/Ryujinx.Horizon/ServiceEntry.cs @@ -4,17 +4,17 @@ using System; namespace Ryujinx.Horizon { - public struct ServiceEntry + public readonly struct ServiceEntry { private readonly Action<ServiceTable> _entrypoint; - private readonly ServiceTable _serviceTable; - private readonly HorizonOptions _options; + private readonly ServiceTable _serviceTable; + private readonly HorizonOptions _options; internal ServiceEntry(Action<ServiceTable> entrypoint, ServiceTable serviceTable, HorizonOptions options) { - _entrypoint = entrypoint; + _entrypoint = entrypoint; _serviceTable = serviceTable; - _options = options; + _options = options; } public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext) @@ -24,4 +24,4 @@ namespace Ryujinx.Horizon _entrypoint(_serviceTable); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs index d97457d92..d47f91bf9 100644 --- a/src/Ryujinx.Horizon/ServiceTable.cs +++ b/src/Ryujinx.Horizon/ServiceTable.cs @@ -57,4 +57,4 @@ namespace Ryujinx.Horizon Dispose(true); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs b/src/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs index 50c18a2c9..fed420aa8 100644 --- a/src/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs +++ b/src/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs @@ -5,16 +5,16 @@ namespace Ryujinx.Horizon.Sm.Impl struct ServiceInfo { public ServiceName Name; - public ulong OwnerProcessId; - public int PortHandle; + public ulong OwnerProcessId; + public int PortHandle; public void Free() { HorizonStatic.Syscall.CloseHandle(PortHandle); - Name = ServiceName.Invalid; + Name = ServiceName.Invalid; OwnerProcessId = 0L; - PortHandle = 0; + PortHandle = 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/src/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs index d1f94267c..929474aa7 100644 --- a/src/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs +++ b/src/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Horizon.Sm.Impl return result == KernelResult.SessionCountExceeded ? SmResult.OutOfSessions : result; } - private Result GetServiceImpl(out int handle, ref ServiceInfo serviceInfo) + private static Result GetServiceImpl(out int handle, ref ServiceInfo serviceInfo) { return HorizonStatic.Syscall.ConnectToPort(out handle, serviceInfo.PortHandle); } @@ -96,8 +96,8 @@ namespace Ryujinx.Horizon.Sm.Impl return result; } - freeService.PortHandle = clientPort; - freeService.Name = name; + freeService.PortHandle = clientPort; + freeService.Name = name; freeService.OwnerProcessId = processId; return Result.Success; @@ -140,7 +140,7 @@ namespace Ryujinx.Horizon.Sm.Impl int nameLength = 1; - for (; nameLength < name.Length; nameLength++) + for (; nameLength < ServiceName.Length; nameLength++) { if (name[nameLength] == 0) { @@ -148,7 +148,7 @@ namespace Ryujinx.Horizon.Sm.Impl } } - while (nameLength < name.Length) + while (nameLength < ServiceName.Length) { if (name[nameLength++] != 0) { @@ -182,4 +182,4 @@ namespace Ryujinx.Horizon.Sm.Impl return -1; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sm/Ipc/UserService.cs b/src/Ryujinx.Horizon/Sm/Ipc/UserService.cs index d093913a9..868a15e6f 100644 --- a/src/Ryujinx.Horizon/Sm/Ipc/UserService.cs +++ b/src/Ryujinx.Horizon/Sm/Ipc/UserService.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Horizon.Sm.Ipc private readonly ServiceManager _serviceManager; private ulong _clientProcessId; - private bool _initialized; + private bool _initialized; public UserService(ServiceManager serviceManager) { @@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sm.Ipc public Result Initialize([ClientProcessId] ulong clientProcessId) { _clientProcessId = clientProcessId; - _initialized = true; + _initialized = true; return Result.Success; } @@ -63,4 +63,4 @@ namespace Ryujinx.Horizon.Sm.Ipc return _serviceManager.UnregisterService(_clientProcessId, name); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sm/SmMain.cs b/src/Ryujinx.Horizon/Sm/SmMain.cs index f0b4d3300..7303847ab 100644 --- a/src/Ryujinx.Horizon/Sm/SmMain.cs +++ b/src/Ryujinx.Horizon/Sm/SmMain.cs @@ -1,6 +1,4 @@ -using Ryujinx.Horizon.Prepo; -using Ryujinx.Horizon.Prepo.Types; -using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sm.Impl; using Ryujinx.Horizon.Sm.Types; @@ -9,8 +7,8 @@ namespace Ryujinx.Horizon.Sm { public class SmMain { - private const int SmMaxSessionsCount = 64; - private const int SmmMaxSessionsCount = 1; + private const int SmMaxSessionsCount = 64; + private const int SmmMaxSessionsCount = 1; private const int SmTotalMaxSessionsCount = SmMaxSessionsCount + SmmMaxSessionsCount; private const int MaxPortsCount = 2; @@ -31,4 +29,4 @@ namespace Ryujinx.Horizon.Sm _serverManager.ServiceRequests(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sm/SmResult.cs b/src/Ryujinx.Horizon/Sm/SmResult.cs index 2d503a4f8..75f47ca93 100644 --- a/src/Ryujinx.Horizon/Sm/SmResult.cs +++ b/src/Ryujinx.Horizon/Sm/SmResult.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Horizon.Sm { private const int ModuleId = 21; +#pragma warning disable IDE0055 // Disable formatting public static Result OutOfProcess => new(ModuleId, 1); public static Result InvalidClient => new(ModuleId, 2); public static Result OutOfSessions => new(ModuleId, 3); @@ -15,5 +16,6 @@ namespace Ryujinx.Horizon.Sm public static Result NotRegistered => new(ModuleId, 7); public static Result NotAllowed => new(ModuleId, 8); public static Result TooLargeAccessControl => new(ModuleId, 9); +#pragma warning restore IDE0055 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sm/SmServerManager.cs b/src/Ryujinx.Horizon/Sm/SmServerManager.cs index dc8dc5b67..c04123455 100644 --- a/src/Ryujinx.Horizon/Sm/SmServerManager.cs +++ b/src/Ryujinx.Horizon/Sm/SmServerManager.cs @@ -21,10 +21,10 @@ namespace Ryujinx.Horizon.Sm { return (SmPortIndex)portIndex switch { - SmPortIndex.User => AcceptImpl(server, new UserService(_serviceManager)), + SmPortIndex.User => AcceptImpl(server, new UserService(_serviceManager)), SmPortIndex.Manager => AcceptImpl(server, new ManagerService()), - _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs b/src/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs index 5325558b8..a29778566 100644 --- a/src/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs +++ b/src/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs @@ -3,6 +3,6 @@ enum SmPortIndex { User, - Manager + Manager, } -} \ No newline at end of file +} From 0684b00b3c4d000cf627b9c08a49d7469ae50d04 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 2 Jul 2023 00:25:07 +0200 Subject: [PATCH 692/737] [Ryujinx] Address dotnet-format issues (#5395) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Address dotnet format CA1816 warnings * Address or silence dotnet format CA2208 warnings * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address dotnet format CA1822 warnings * Make dotnet format succeed in style mode * Address dotnet format CA2208 warnings properly * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Another rebase, another dotnet format run * Run dotnet format whitespace after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Fix build issues * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Second dotnet format pass * Update src/Ryujinx/Modules/Updater/Updater.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Add trailing commas and improve formatting * Fix formatting and naming issues * Rename nvStutterWorkaround to nvidiaStutterWorkaround * Use using declarations and extend resource lifetimes * Fix GTK issues * Add formatting for generated files * Add trailing commas --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- src/Ryujinx/Input/GTK3/GTK3Keyboard.cs | 5 +- src/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs | 7 +- src/Ryujinx/Input/GTK3/GTK3Mouse.cs | 3 +- src/Ryujinx/Input/GTK3/GTK3MouseDriver.cs | 26 +- src/Ryujinx/Modules/Updater/UpdateDialog.cs | 28 +- src/Ryujinx/Modules/Updater/Updater.cs | 196 ++--- src/Ryujinx/Program.cs | 35 +- src/Ryujinx/Ui/Applet/ErrorAppletDialog.cs | 4 +- .../Ui/Applet/GtkDynamicTextInputHandler.cs | 16 +- src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs | 52 +- src/Ryujinx/Ui/Applet/GtkHostUiTheme.cs | 12 +- src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs | 18 +- src/Ryujinx/Ui/Helper/MetalHelper.cs | 31 +- src/Ryujinx/Ui/Helper/ThemeHelper.cs | 6 +- src/Ryujinx/Ui/MainWindow.cs | 537 ++++++------ .../Ui/{GLRenderer.cs => OpenGLRenderer.cs} | 11 +- src/Ryujinx/Ui/OpenToolkitBindingsContext.cs | 2 +- src/Ryujinx/Ui/RendererWidgetBase.cs | 57 +- src/Ryujinx/Ui/SPBOpenGLContext.cs | 4 +- src/Ryujinx/Ui/StatusUpdatedEventArgs.cs | 20 +- .../Ui/{VKRenderer.cs => VulkanRenderer.cs} | 4 +- .../Widgets/GameTableContextMenu.Designer.cs | 38 +- .../Ui/Widgets/GameTableContextMenu.cs | 295 ++++--- src/Ryujinx/Ui/Widgets/GtkDialog.cs | 20 +- src/Ryujinx/Ui/Widgets/GtkInputDialog.cs | 16 +- src/Ryujinx/Ui/Widgets/ProfileDialog.cs | 4 +- src/Ryujinx/Ui/Widgets/UserErrorDialog.cs | 48 +- .../Ui/Windows/AboutWindow.Designer.cs | 216 +++-- src/Ryujinx/Ui/Windows/AboutWindow.cs | 4 +- .../Ui/Windows/AmiiboWindow.Designer.cs | 78 +- src/Ryujinx/Ui/Windows/AmiiboWindow.cs | 60 +- src/Ryujinx/Ui/Windows/AvatarWindow.cs | 249 +++--- src/Ryujinx/Ui/Windows/CheatWindow.cs | 17 +- src/Ryujinx/Ui/Windows/ControllerWindow.cs | 788 +++++++++--------- src/Ryujinx/Ui/Windows/DlcWindow.cs | 121 +-- src/Ryujinx/Ui/Windows/SettingsWindow.cs | 388 +++++---- src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs | 109 ++- .../UserProfilesManagerWindow.Designer.cs | 135 ++- .../Ui/Windows/UserProfilesManagerWindow.cs | 61 +- 39 files changed, 1891 insertions(+), 1830 deletions(-) rename src/Ryujinx/Ui/{GLRenderer.cs => OpenGLRenderer.cs} (91%) rename src/Ryujinx/Ui/{VKRenderer.cs => VulkanRenderer.cs} (93%) diff --git a/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs b/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs index ca3ff32f4..16bef39b4 100644 --- a/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs +++ b/src/Ryujinx/Input/GTK3/GTK3Keyboard.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Input.GTK3 private readonly GTK3KeyboardDriver _driver; private StandardKeyboardInputConfig _configuration; - private List<ButtonMappingEntry> _buttonsUserMapping; + private readonly List<ButtonMappingEntry> _buttonsUserMapping; public GTK3Keyboard(GTK3KeyboardDriver driver, string id, string name) { @@ -48,6 +48,7 @@ namespace Ryujinx.Input.GTK3 public void Dispose() { // No operations + GC.SuppressFinalize(this); } public KeyboardStateSnapshot GetKeyboardStateSnapshot() @@ -87,7 +88,7 @@ namespace Ryujinx.Input.GTK3 stickX -= 1; } - OpenTK.Mathematics.Vector2 stick = new OpenTK.Mathematics.Vector2(stickX, stickY); + OpenTK.Mathematics.Vector2 stick = new(stickX, stickY); stick.NormalizeFast(); diff --git a/src/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs b/src/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs index 10c092fe7..ae249c273 100644 --- a/src/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs +++ b/src/Ryujinx/Input/GTK3/GTK3KeyboardDriver.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Input.GTK3 public class GTK3KeyboardDriver : IGamepadDriver { private readonly Widget _widget; - private HashSet<GtkKey> _pressedKeys; + private readonly HashSet<GtkKey> _pressedKeys; public GTK3KeyboardDriver(Widget widget) { @@ -28,13 +28,13 @@ namespace Ryujinx.Input.GTK3 public event Action<string> OnGamepadConnected { - add { } + add { } remove { } } public event Action<string> OnGamepadDisconnected { - add { } + add { } remove { } } @@ -49,6 +49,7 @@ namespace Ryujinx.Input.GTK3 public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } diff --git a/src/Ryujinx/Input/GTK3/GTK3Mouse.cs b/src/Ryujinx/Input/GTK3/GTK3Mouse.cs index 836c2bf44..0ab817ecb 100644 --- a/src/Ryujinx/Input/GTK3/GTK3Mouse.cs +++ b/src/Ryujinx/Input/GTK3/GTK3Mouse.cs @@ -83,7 +83,8 @@ namespace Ryujinx.Input.GTK3 public void Dispose() { + GC.SuppressFinalize(this); _driver = null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Input/GTK3/GTK3MouseDriver.cs b/src/Ryujinx/Input/GTK3/GTK3MouseDriver.cs index df37e4f4a..5962bcb25 100644 --- a/src/Ryujinx/Input/GTK3/GTK3MouseDriver.cs +++ b/src/Ryujinx/Input/GTK3/GTK3MouseDriver.cs @@ -12,18 +12,18 @@ namespace Ryujinx.Input.GTK3 private bool _isDisposed; public bool[] PressedButtons { get; } - + public Vector2 CurrentPosition { get; private set; } - public Vector2 Scroll{ get; private set; } + public Vector2 Scroll { get; private set; } public GTK3MouseDriver(Widget parent) { _widget = parent; - _widget.MotionNotifyEvent += Parent_MotionNotifyEvent; - _widget.ButtonPressEvent += Parent_ButtonPressEvent; + _widget.MotionNotifyEvent += Parent_MotionNotifyEvent; + _widget.ButtonPressEvent += Parent_ButtonPressEvent; _widget.ButtonReleaseEvent += Parent_ButtonReleaseEvent; - _widget.ScrollEvent += Parent_ScrollEvent; + _widget.ScrollEvent += Parent_ScrollEvent; PressedButtons = new bool[(int)MouseButton.Count]; } @@ -58,7 +58,7 @@ namespace Ryujinx.Input.GTK3 public bool IsButtonPressed(MouseButton button) { - return PressedButtons[(int) button]; + return PressedButtons[(int)button]; } public Size GetClientSize() @@ -67,21 +67,21 @@ namespace Ryujinx.Input.GTK3 } public string DriverName => "GTK3"; - + public event Action<string> OnGamepadConnected { - add { } + add { } remove { } } public event Action<string> OnGamepadDisconnected { - add { } + add { } remove { } } - public ReadOnlySpan<string> GamepadsIds => new[] {"0"}; - + public ReadOnlySpan<string> GamepadsIds => new[] { "0" }; + public IGamepad GetGamepad(string id) { return new GTK3Mouse(this); @@ -94,6 +94,8 @@ namespace Ryujinx.Input.GTK3 return; } + GC.SuppressFinalize(this); + _isDisposed = true; _widget.MotionNotifyEvent -= Parent_MotionNotifyEvent; @@ -103,4 +105,4 @@ namespace Ryujinx.Input.GTK3 _widget = null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Modules/Updater/UpdateDialog.cs b/src/Ryujinx/Modules/Updater/UpdateDialog.cs index e0a257fd6..695634374 100644 --- a/src/Ryujinx/Modules/Updater/UpdateDialog.cs +++ b/src/Ryujinx/Modules/Updater/UpdateDialog.cs @@ -12,17 +12,17 @@ namespace Ryujinx.Modules { public class UpdateDialog : Gtk.Window { -#pragma warning disable CS0649, IDE0044 - [Builder.Object] public Label MainText; - [Builder.Object] public Label SecondaryText; +#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier + [Builder.Object] public Label MainText; + [Builder.Object] public Label SecondaryText; [Builder.Object] public LevelBar ProgressBar; - [Builder.Object] public Button YesButton; - [Builder.Object] public Button NoButton; + [Builder.Object] public Button YesButton; + [Builder.Object] public Button NoButton; #pragma warning restore CS0649, IDE0044 private readonly MainWindow _mainWindow; - private readonly string _buildUrl; - private bool _restartQuery; + private readonly string _buildUrl; + private bool _restartQuery; public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx.Modules.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { } @@ -31,16 +31,16 @@ namespace Ryujinx.Modules builder.Autoconnect(this); _mainWindow = mainWindow; - _buildUrl = buildUrl; + _buildUrl = buildUrl; Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); - MainText.Text = "Do you want to update Ryujinx to the latest version?"; + MainText.Text = "Do you want to update Ryujinx to the latest version?"; SecondaryText.Text = $"{Program.Version} -> {newVersion}"; ProgressBar.Hide(); YesButton.Clicked += YesButton_Clicked; - NoButton.Clicked += NoButton_Clicked; + NoButton.Clicked += NoButton_Clicked; } private void YesButton_Clicked(object sender, EventArgs args) @@ -52,7 +52,7 @@ namespace Ryujinx.Modules ProcessStartInfo processStart = new(ryuName) { UseShellExecute = true, - WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory() + WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory(), }; foreach (string argument in CommandLineState.Arguments) @@ -74,7 +74,7 @@ namespace Ryujinx.Modules ProgressBar.Show(); SecondaryText.Text = ""; - _restartQuery = true; + _restartQuery = true; Updater.UpdateRyujinx(this, _buildUrl); } @@ -85,10 +85,10 @@ namespace Ryujinx.Modules Updater.Running = false; _mainWindow.Window.Functions = WMFunction.All; - _mainWindow.ExitMenuItem.Sensitive = true; + _mainWindow.ExitMenuItem.Sensitive = true; _mainWindow.UpdateMenuItem.Sensitive = true; Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Modules/Updater/Updater.cs b/src/Ryujinx/Modules/Updater/Updater.cs index 344edf9e5..f8ce4c0b9 100644 --- a/src/Ryujinx/Modules/Updater/Updater.cs +++ b/src/Ryujinx/Modules/Updater/Updater.cs @@ -24,28 +24,28 @@ namespace Ryujinx.Modules { public static class Updater { - private const string GitHubApiURL = "https://api.github.com"; + private const string GitHubApiUrl = "https://api.github.com"; private const int ConnectionCount = 4; internal static bool Running; - private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; - private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); - private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); + private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; + private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); + private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish"); private static string _buildVer; private static string _platformExt; private static string _buildUrl; - private static long _buildSize; + private static long _buildSize; - private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. - private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; + private static readonly string[] _windowsDependencyDirs = { "bin", "etc", "lib", "share" }; private static HttpClient ConstructHttpClient() { - HttpClient result = new HttpClient(); + HttpClient result = new(); // Required by GitHub to interact with APIs. result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); @@ -55,7 +55,10 @@ namespace Ryujinx.Modules public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) { - if (Running) return; + if (Running) + { + return; + } Running = true; mainWindow.UpdateMenuItem.Sensitive = false; @@ -65,17 +68,17 @@ namespace Ryujinx.Modules // Detect current platform if (OperatingSystem.IsMacOS()) { - _platformExt = "osx_x64.zip"; + _platformExt = "osx_x64.zip"; artifactIndex = 1; } else if (OperatingSystem.IsWindows()) { - _platformExt = "win_x64.zip"; + _platformExt = "win_x64.zip"; artifactIndex = 2; } else if (OperatingSystem.IsLinux()) { - _platformExt = "linux_x64.tar.gz"; + _platformExt = "linux_x64.tar.gz"; artifactIndex = 0; } @@ -105,11 +108,11 @@ namespace Ryujinx.Modules try { using HttpClient jsonClient = ConstructHttpClient(); - string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; // Fetch latest build information - string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); + string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl); + var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse); _buildVer = fetched.Name; foreach (var asset in fetched.Assets) @@ -176,45 +179,43 @@ namespace Ryujinx.Modules } // Fetch build size information to learn chunk sizes. - using (HttpClient buildSizeClient = ConstructHttpClient()) + using HttpClient buildSizeClient = ConstructHttpClient(); + try { - try - { - buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); + buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); - HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); + HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); - _buildSize = message.Content.Headers.ContentRange.Length.Value; - } - catch (Exception ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); + _buildSize = message.Content.Headers.ContentRange.Length.Value; + } + catch (Exception ex) + { + Logger.Warning?.Print(LogClass.Application, ex.Message); + Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); - _buildSize = -1; - } + _buildSize = -1; } // Show a message asking the user if they want to update - UpdateDialog updateDialog = new UpdateDialog(mainWindow, newVersion, _buildUrl); + UpdateDialog updateDialog = new(mainWindow, newVersion, _buildUrl); updateDialog.Show(); } public static void UpdateRyujinx(UpdateDialog updateDialog, string downloadUrl) { // Empty update dir, although it shouldn't ever have anything inside it - if (Directory.Exists(UpdateDir)) + if (Directory.Exists(_updateDir)) { - Directory.Delete(UpdateDir, true); + Directory.Delete(_updateDir, true); } - Directory.CreateDirectory(UpdateDir); + Directory.CreateDirectory(_updateDir); - string updateFile = Path.Combine(UpdateDir, "update.bin"); + string updateFile = Path.Combine(_updateDir, "update.bin"); // Download the update .zip - updateDialog.MainText.Text = "Downloading Update..."; - updateDialog.ProgressBar.Value = 0; + updateDialog.MainText.Text = "Downloading Update..."; + updateDialog.ProgressBar.Value = 0; updateDialog.ProgressBar.MaxValue = 100; if (_buildSize >= 0) @@ -237,8 +238,8 @@ namespace Ryujinx.Modules int totalProgressPercentage = 0; int[] progressPercentage = new int[ConnectionCount]; - List<byte[]> list = new List<byte[]>(ConnectionCount); - List<WebClient> webClients = new List<WebClient>(ConnectionCount); + List<byte[]> list = new(ConnectionCount); + List<WebClient> webClients = new(ConnectionCount); for (int i = 0; i < ConnectionCount; i++) { @@ -249,7 +250,7 @@ namespace Ryujinx.Modules { #pragma warning disable SYSLIB0014 // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. - using WebClient client = new WebClient(); + using WebClient client = new(); #pragma warning restore SYSLIB0014 webClients.Add(client); @@ -337,35 +338,32 @@ namespace Ryujinx.Modules private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) { - using HttpClient client = new HttpClient(); + using HttpClient client = new(); // We do not want to timeout while downloading client.Timeout = TimeSpan.FromDays(1); - using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) - using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) + using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result; + using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result; + using Stream updateFileStream = File.Open(updateFile, FileMode.Create); + + long totalBytes = response.Content.Headers.ContentLength.Value; + long byteWritten = 0; + + byte[] buffer = new byte[32 * 1024]; + + while (true) { - using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) + int readSize = remoteFileStream.Read(buffer); + + if (readSize == 0) { - long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; - - byte[] buffer = new byte[32 * 1024]; - - while (true) - { - int readSize = remoteFileStream.Read(buffer); - - if (readSize == 0) - { - break; - } - - byteWritten += readSize; - - updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; - updateFileStream.Write(buffer, 0, readSize); - } + break; } + + byteWritten += readSize; + + updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; + updateFileStream.Write(buffer, 0, readSize); } InstallUpdate(updateDialog, updateFile); @@ -373,9 +371,9 @@ namespace Ryujinx.Modules private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) { - Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)) + Thread worker = new(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)) { - Name = "Updater.SingleThreadWorker" + Name = "Updater.SingleThreadWorker", }; worker.Start(); } @@ -383,14 +381,14 @@ namespace Ryujinx.Modules private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) { // Extract Update - updateDialog.MainText.Text = "Extracting Update..."; + updateDialog.MainText.Text = "Extracting Update..."; updateDialog.ProgressBar.Value = 0; if (OperatingSystem.IsLinux()) { - using Stream inStream = File.OpenRead(updateFile); - using Stream gzipStream = new GZipInputStream(inStream); - using TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII); + using Stream inStream = File.OpenRead(updateFile); + using Stream gzipStream = new GZipInputStream(inStream); + using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); updateDialog.ProgressBar.MaxValue = inStream.Length; await Task.Run(() => @@ -401,16 +399,17 @@ namespace Ryujinx.Modules { while ((tarEntry = tarStream.GetNextEntry()) != null) { - if (tarEntry.IsDirectory) continue; + if (tarEntry.IsDirectory) + { + continue; + } - string outPath = Path.Combine(UpdateDir, tarEntry.Name); + string outPath = Path.Combine(_updateDir, tarEntry.Name); Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - using (FileStream outStream = File.OpenWrite(outPath)) - { - tarStream.CopyEntryContents(outStream); - } + using FileStream outStream = File.OpenWrite(outPath); + tarStream.CopyEntryContents(outStream); File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); @@ -429,25 +428,26 @@ namespace Ryujinx.Modules } else { - using Stream inStream = File.OpenRead(updateFile); - using ZipFile zipFile = new ZipFile(inStream); + using Stream inStream = File.OpenRead(updateFile); + using ZipFile zipFile = new(inStream); updateDialog.ProgressBar.MaxValue = zipFile.Count; await Task.Run(() => { foreach (ZipEntry zipEntry in zipFile) { - if (zipEntry.IsDirectory) continue; + if (zipEntry.IsDirectory) + { + continue; + } - string outPath = Path.Combine(UpdateDir, zipEntry.Name); + string outPath = Path.Combine(_updateDir, zipEntry.Name); Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - using (Stream zipStream = zipFile.GetInputStream(zipEntry)) - using (FileStream outStream = File.OpenWrite(outPath)) - { - zipStream.CopyTo(outStream); - } + using Stream zipStream = zipFile.GetInputStream(zipEntry); + using FileStream outStream = File.OpenWrite(outPath); + zipStream.CopyTo(outStream); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); @@ -464,8 +464,8 @@ namespace Ryujinx.Modules List<string> allFiles = EnumerateFilesToDelete().ToList(); - updateDialog.MainText.Text = "Renaming Old Files..."; - updateDialog.ProgressBar.Value = 0; + updateDialog.MainText.Text = "Renaming Old Files..."; + updateDialog.ProgressBar.Value = 0; updateDialog.ProgressBar.MaxValue = allFiles.Count; // Replace old files @@ -490,19 +490,19 @@ namespace Ryujinx.Modules Application.Invoke(delegate { - updateDialog.MainText.Text = "Adding New Files..."; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.MaxValue = Directory.GetFiles(UpdatePublishDir, "*", SearchOption.AllDirectories).Length; + updateDialog.MainText.Text = "Adding New Files..."; + updateDialog.ProgressBar.Value = 0; + updateDialog.ProgressBar.MaxValue = Directory.GetFiles(_updatePublishDir, "*", SearchOption.AllDirectories).Length; }); - MoveAllFilesOver(UpdatePublishDir, HomeDir, updateDialog); + MoveAllFilesOver(_updatePublishDir, _homeDir, updateDialog); }); - Directory.Delete(UpdateDir, true); + Directory.Delete(_updateDir, true); - updateDialog.MainText.Text = "Update Complete!"; + updateDialog.MainText.Text = "Update Complete!"; updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; - updateDialog.Modal = true; + updateDialog.Modal = true; updateDialog.ProgressBar.Hide(); updateDialog.YesButton.Show(); @@ -563,15 +563,15 @@ namespace Ryujinx.Modules // NOTE: This method should always reflect the latest build layout. private static IEnumerable<string> EnumerateFilesToDelete() { - var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. + var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir. // Determine and exclude user files only when the updater is running, not when cleaning old files if (Running) { // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. - var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename)); + var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename)); // Remove user files from the paths in files. files = files.Except(userFiles); @@ -579,9 +579,9 @@ namespace Ryujinx.Modules if (OperatingSystem.IsWindows()) { - foreach (string dir in WindowsDependencyDirs) + foreach (string dir in _windowsDependencyDirs) { - string dirPath = Path.Combine(HomeDir, dir); + string dirPath = Path.Combine(_homeDir, dir); if (Directory.Exists(dirPath)) { files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories)); @@ -628,4 +628,4 @@ namespace Ryujinx.Modules } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 96024548f..50151d733 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -43,10 +43,7 @@ namespace Ryujinx [LibraryImport("libc", SetLastError = true)] private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite); - [LibraryImport("libc")] - private static partial IntPtr getenv([MarshalAs(UnmanagedType.LPStr)] string name); - - private const uint MB_ICONWARNING = 0x30; + private const uint MbIconWarning = 0x30; static Program() { @@ -78,16 +75,16 @@ namespace Ryujinx if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) { - MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING); + MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning); } // Parse arguments CommandLineState.ParseArguments(args); // Hook unhandled exception and process exit events. - GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); + GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); + AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); // Make process DPI aware for proper window sizing on high-res screens. ForceDpiAware.Windows(); @@ -102,7 +99,11 @@ namespace Ryujinx // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads). if (OperatingSystem.IsLinux()) { - XInitThreads(); + if (XInitThreads() == 0) + { + throw new NotSupportedException("Failed to initialize multi-threading support."); + } + Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); setenv("GDK_BACKEND", "x11", 1); } @@ -121,7 +122,7 @@ namespace Ryujinx resourcesDataDir = baseDirectory; } - void SetEnvironmentVariableNoCaching(string key, string value) + static void SetEnvironmentVariableNoCaching(string key, string value) { int res = setenv(key, value, 1); Debug.Assert(res != -1); @@ -163,11 +164,11 @@ namespace Ryujinx // Sets ImageSharp Jpeg Encoder Quality. SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder() { - Quality = 100 + Quality = 100, }); - string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); - string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json"); + string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); + string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json"); // Now load the configuration as the other subsystems are now registered ConfigurationPath = File.Exists(localConfigurationPath) @@ -232,7 +233,7 @@ namespace Ryujinx "never" => HideCursorMode.Never, "onidle" => HideCursorMode.OnIdle, "always" => HideCursorMode.Always, - _ => ConfigurationState.Instance.HideCursor.Value + _ => ConfigurationState.Instance.HideCursor.Value, }; } @@ -261,7 +262,7 @@ namespace Ryujinx } // Show the main window UI. - MainWindow mainWindow = new MainWindow(); + MainWindow mainWindow = new(); mainWindow.Show(); if (OperatingSystem.IsLinux()) @@ -278,7 +279,7 @@ namespace Ryujinx { { 0, "Yes, until the next restart" }, { 1, "Yes, permanently" }, - { 2, "No" } + { 2, "No" }, }; ResponseType response = GtkDialog.CreateCustomDialog( @@ -347,7 +348,7 @@ namespace Ryujinx var buttonTexts = new Dictionary<int, string>() { { 0, "Yes (Vulkan)" }, - { 1, "No (OpenGL)" } + { 1, "No (OpenGL)" }, }; ResponseType response = GtkDialog.CreateCustomDialog( @@ -416,4 +417,4 @@ namespace Ryujinx Logger.Shutdown(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Applet/ErrorAppletDialog.cs b/src/Ryujinx/Ui/Applet/ErrorAppletDialog.cs index d4cc7cccb..cd3530f34 100644 --- a/src/Ryujinx/Ui/Applet/ErrorAppletDialog.cs +++ b/src/Ryujinx/Ui/Applet/ErrorAppletDialog.cs @@ -24,8 +24,8 @@ namespace Ryujinx.Ui.Applet { AddButton("OK", 0); } - + ShowAll(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs b/src/Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs index 79df3cc77..3a3da4650 100644 --- a/src/Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs +++ b/src/Ryujinx/Ui/Applet/GtkDynamicTextInputHandler.cs @@ -11,15 +11,15 @@ namespace Ryujinx.Ui.Applet /// </summary> internal class GtkDynamicTextInputHandler : IDynamicTextInputHandler { - private readonly Window _parent; - private readonly OffscreenWindow _inputToTextWindow = new OffscreenWindow(); - private readonly RawInputToTextEntry _inputToTextEntry = new RawInputToTextEntry(); + private readonly Window _parent; + private readonly OffscreenWindow _inputToTextWindow = new(); + private readonly RawInputToTextEntry _inputToTextEntry = new(); private bool _canProcessInput; public event DynamicTextChangedHandler TextChangedEvent; - public event KeyPressedHandler KeyPressedEvent; - public event KeyReleasedHandler KeyReleasedEvent; + public event KeyPressedHandler KeyPressedEvent; + public event KeyReleasedHandler KeyReleasedEvent; public bool TextProcessingEnabled { @@ -37,7 +37,7 @@ namespace Ryujinx.Ui.Applet public GtkDynamicTextInputHandler(Window parent) { _parent = parent; - _parent.KeyPressEvent += HandleKeyPressEvent; + _parent.KeyPressEvent += HandleKeyPressEvent; _parent.KeyReleaseEvent += HandleKeyReleaseEvent; _inputToTextWindow.Add(_inputToTextEntry); @@ -101,8 +101,8 @@ namespace Ryujinx.Ui.Applet public void Dispose() { - _parent.KeyPressEvent -= HandleKeyPressEvent; + _parent.KeyPressEvent -= HandleKeyPressEvent; _parent.KeyReleaseEvent -= HandleKeyReleaseEvent; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs b/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs index b7577b85d..241e5e6cf 100644 --- a/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs +++ b/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs @@ -5,7 +5,6 @@ using Ryujinx.HLE.Ui; using Ryujinx.Ui.Widgets; using System; using System.Threading; -using Action = System.Action; namespace Ryujinx.Ui.Applet { @@ -37,7 +36,7 @@ namespace Ryujinx.Ui.Applet public bool DisplayMessageDialog(string title, string message) { - ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); + ManualResetEvent dialogCloseEvent = new(false); bool okPressed = false; @@ -49,9 +48,9 @@ namespace Ryujinx.Ui.Applet { msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) { - Title = title, - Text = message, - UseMarkup = true + Title = title, + Text = message, + UseMarkup = true, }; msgDialog.SetDefaultSize(400, 0); @@ -84,10 +83,10 @@ namespace Ryujinx.Ui.Applet public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText) { - ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); + ManualResetEvent dialogCloseEvent = new(false); - bool okPressed = false; - bool error = false; + bool okPressed = false; + bool error = false; string inputText = args.InitialText ?? ""; Application.Invoke(delegate @@ -96,14 +95,14 @@ namespace Ryujinx.Ui.Applet { var swkbdDialog = new SwkbdAppletDialog(_parent) { - Title = "Software Keyboard", - Text = args.HeaderText, - SecondaryText = args.SubtitleText + Title = "Software Keyboard", + Text = args.HeaderText, + SecondaryText = args.SubtitleText, }; - swkbdDialog.InputEntry.Text = inputText; + swkbdDialog.InputEntry.Text = inputText; swkbdDialog.InputEntry.PlaceholderText = args.GuideText; - swkbdDialog.OkButton.Label = args.SubmitText; + swkbdDialog.OkButton.Label = args.SubmitText; swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); swkbdDialog.SetInputValidation(args.KeyboardMode); @@ -143,7 +142,7 @@ namespace Ryujinx.Ui.Applet public bool DisplayErrorAppletDialog(string title, string message, string[] buttons) { - ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); + ManualResetEvent dialogCloseEvent = new(false); bool showDetails = false; @@ -151,12 +150,12 @@ namespace Ryujinx.Ui.Applet { try { - ErrorAppletDialog msgDialog = new ErrorAppletDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Error, buttons) + ErrorAppletDialog msgDialog = new(_parent, DialogFlags.DestroyWithParent, MessageType.Error, buttons) { - Title = title, - Text = message, - UseMarkup = true, - WindowPosition = WindowPosition.CenterAlways + Title = title, + Text = message, + UseMarkup = true, + WindowPosition = WindowPosition.CenterAlways, }; msgDialog.SetDefaultSize(400, 0); @@ -193,22 +192,9 @@ namespace Ryujinx.Ui.Applet return showDetails; } - private void SynchronousGtkInvoke(Action action) - { - var waitHandle = new ManualResetEventSlim(); - - Application.Invoke(delegate - { - action(); - waitHandle.Set(); - }); - - waitHandle.Wait(); - } - public IDynamicTextInputHandler CreateDynamicTextInputHandler() { return new GtkDynamicTextInputHandler(_parent); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Applet/GtkHostUiTheme.cs b/src/Ryujinx/Ui/Applet/GtkHostUiTheme.cs index f25da47c4..df8103325 100644 --- a/src/Ryujinx/Ui/Applet/GtkHostUiTheme.cs +++ b/src/Ryujinx/Ui/Applet/GtkHostUiTheme.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Ui.Applet { internal class GtkHostUiTheme : IHostUiTheme { - private const int RenderSurfaceWidth = 32; + private const int RenderSurfaceWidth = 32; private const int RenderSurfaceHeight = 32; public string FontFamily { get; private set; } @@ -19,7 +19,7 @@ namespace Ryujinx.Ui.Applet public GtkHostUiTheme(Window parent) { - Entry entry = new Entry(); + Entry entry = new(); entry.SetStateFlags(StateFlags.Selected, true); // Get the font and some colors directly from GTK. @@ -30,10 +30,10 @@ namespace Ryujinx.Ui.Applet var defaultForegroundColor = entry.StyleContext.GetColor(StateFlags.Normal); var selectedForegroundColor = entry.StyleContext.GetColor(StateFlags.Selected); - DefaultForegroundColor = new ThemeColor((float) defaultForegroundColor.Alpha, (float) defaultForegroundColor.Red, (float) defaultForegroundColor.Green, (float) defaultForegroundColor.Blue); + DefaultForegroundColor = new ThemeColor((float)defaultForegroundColor.Alpha, (float)defaultForegroundColor.Red, (float)defaultForegroundColor.Green, (float)defaultForegroundColor.Blue); SelectionForegroundColor = new ThemeColor((float)selectedForegroundColor.Alpha, (float)selectedForegroundColor.Red, (float)selectedForegroundColor.Green, (float)selectedForegroundColor.Blue); - ListBoxRow row = new ListBoxRow(); + ListBoxRow row = new(); row.SetStateFlags(StateFlags.Selected, true); // Request the main thread to render some UI elements to an image to get an approximation for the color. @@ -67,7 +67,7 @@ namespace Ryujinx.Ui.Applet SelectionBackgroundColor = DefaultBorderColor; } - private ThemeColor ToThemeColor(byte[] data) + private static ThemeColor ToThemeColor(byte[] data) { Debug.Assert(data.Length == 4 * RenderSurfaceWidth * RenderSurfaceHeight); @@ -87,4 +87,4 @@ namespace Ryujinx.Ui.Applet return new ThemeColor(a, r, g, b); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs index 13d30f6c0..1ed08250f 100644 --- a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs +++ b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs @@ -9,7 +9,9 @@ namespace Ryujinx.Ui.Applet { private int _inputMin; private int _inputMax; +#pragma warning disable IDE0052 // Remove unread private member private KeyboardMode _mode; +#pragma warning restore IDE0052 private string _validationInfoText = ""; @@ -18,8 +20,8 @@ namespace Ryujinx.Ui.Applet private readonly Label _validationInfo; - public Entry InputEntry { get; } - public Button OkButton { get; } + public Entry InputEntry { get; } + public Button OkButton { get; } public Button CancelButton { get; } public SwkbdAppletDialog(Window parent) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, null) @@ -28,22 +30,22 @@ namespace Ryujinx.Ui.Applet _validationInfo = new Label() { - Visible = false + Visible = false, }; InputEntry = new Entry() { - Visible = true + Visible = true, }; InputEntry.Activated += OnInputActivated; - InputEntry.Changed += OnInputChanged; + InputEntry.Changed += OnInputChanged; - OkButton = (Button)AddButton("OK", ResponseType.Ok); + OkButton = (Button)AddButton("OK", ResponseType.Ok); CancelButton = (Button)AddButton("Cancel", ResponseType.Cancel); ((Box)MessageArea).PackEnd(_validationInfo, true, true, 0); - ((Box)MessageArea).PackEnd(InputEntry, true, true, 4); + ((Box)MessageArea).PackEnd(InputEntry, true, true, 4); } private void ApplyValidationInfo() @@ -122,4 +124,4 @@ namespace Ryujinx.Ui.Applet OkButton.Sensitive = _checkLength(InputEntry.Text.Length) && _checkInput(InputEntry.Text); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Helper/MetalHelper.cs b/src/Ryujinx/Ui/Helper/MetalHelper.cs index c2d4893e8..a7af2aed2 100644 --- a/src/Ryujinx/Ui/Helper/MetalHelper.cs +++ b/src/Ryujinx/Ui/Helper/MetalHelper.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Ui.Helper { private const string LibObjCImport = "/usr/lib/libobjc.A.dylib"; - private struct Selector + private readonly struct Selector { public readonly IntPtr NativePtr; @@ -29,7 +29,7 @@ namespace Ryujinx.Ui.Helper NativePtr = sel_registerName(data); } - public static implicit operator Selector(string value) => new Selector(value); + public static implicit operator Selector(string value) => new(value); } private static unsafe IntPtr GetClass(string value) @@ -45,27 +45,27 @@ namespace Ryujinx.Ui.Helper return objc_getClass(data); } - private struct NSPoint + private struct NsPoint { public double X; public double Y; - public NSPoint(double x, double y) + public NsPoint(double x, double y) { X = x; Y = y; } } - private struct NSRect + private struct NsRect { - public NSPoint Pos; - public NSPoint Size; + public NsPoint Pos; + public NsPoint Size; - public NSRect(double x, double y, double width, double height) + public NsRect(double x, double y, double width, double height) { - Pos = new NSPoint(x, y); - Size = new NSPoint(width, height); + Pos = new NsPoint(x, y); + Size = new NsPoint(width, height); } } @@ -81,7 +81,7 @@ namespace Ryujinx.Ui.Helper // Create a child NSView to render into. IntPtr nsViewClass = GetClass("NSView"); IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc"); - objc_msgSend(child, "init", new NSRect()); + objc_msgSend(child, "init", new NsRect()); // Add it as a child. objc_msgSend(nsView, "addSubview:", child); @@ -92,11 +92,12 @@ namespace Ryujinx.Ui.Helper objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor); // Set the frame position/location. - updateBounds = (Window window) => { + updateBounds = (Window window) => + { window.GetPosition(out int x, out int y); int width = window.Width; int height = window.Height; - objc_msgSend(child, "setFrame:", new NSRect(x, y, width, height)); + objc_msgSend(child, "setFrame:", new NsRect(x, y, width, height)); }; updateBounds(window); @@ -120,7 +121,7 @@ namespace Ryujinx.Ui.Helper private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); + private static partial void objc_msgSend(IntPtr receiver, Selector selector, NsRect point); [LibraryImport(LibObjCImport)] private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); @@ -131,4 +132,4 @@ namespace Ryujinx.Ui.Helper [LibraryImport("libgdk-3.0.dylib")] private static partial IntPtr gdk_quartz_window_get_nsview(IntPtr gdkWindow); } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Helper/ThemeHelper.cs b/src/Ryujinx/Ui/Helper/ThemeHelper.cs index 448afcc96..5289ebc5f 100644 --- a/src/Ryujinx/Ui/Helper/ThemeHelper.cs +++ b/src/Ryujinx/Ui/Helper/ThemeHelper.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Ui.Helper if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css")) { - CssProvider cssProvider = new CssProvider(); + CssProvider cssProvider = new(); cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath); @@ -26,10 +26,10 @@ namespace Ryujinx.Ui.Helper { Logger.Warning?.Print(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\"."); - ConfigurationState.Instance.Ui.CustomThemePath.Value = ""; + ConfigurationState.Instance.Ui.CustomThemePath.Value = ""; ConfigurationState.Instance.Ui.EnableCustomTheme.Value = false; ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index 628bcff4f..7625e9e6f 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -18,8 +18,6 @@ using Ryujinx.Common.SystemInterop; using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.OpenGL; -using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; @@ -51,9 +49,9 @@ namespace Ryujinx.Ui { public class MainWindow : Window { - private readonly VirtualFileSystem _virtualFileSystem; - private readonly ContentManager _contentManager; - private readonly AccountManager _accountManager; + private readonly VirtualFileSystem _virtualFileSystem; + private readonly ContentManager _contentManager; + private readonly AccountManager _accountManager; private readonly LibHacHorizonManager _libHacHorizonManager; private UserChannelPersistence _userChannelPersistence; @@ -63,9 +61,9 @@ namespace Ryujinx.Ui private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; private readonly ApplicationLibrary _applicationLibrary; - private readonly GtkHostUiHandler _uiHandler; - private readonly AutoResetEvent _deviceExitStatus; - private readonly ListStore _tableStore; + private readonly GtkHostUiHandler _uiHandler; + private readonly AutoResetEvent _deviceExitStatus; + private readonly ListStore _tableStore; private bool _updatingGameTable; private bool _gameLoaded; @@ -74,76 +72,76 @@ namespace Ryujinx.Ui private string _currentEmulatedGamePath = null; private string _lastScannedAmiiboId = ""; - private bool _lastScannedAmiiboShowAll = false; + private bool _lastScannedAmiiboShowAll = false; public RendererWidgetBase RendererWidget; public InputManager InputManager; public bool IsFocused; -#pragma warning disable CS0169, CS0649, IDE0044 +#pragma warning disable CS0169, CS0649, IDE0044, IDE0051 // Field is never assigned to, Add readonly modifier, Remove unused private member [GUI] public MenuItem ExitMenuItem; [GUI] public MenuItem UpdateMenuItem; - [GUI] MenuBar _menuBar; - [GUI] Box _footerBox; - [GUI] Box _statusBar; - [GUI] MenuItem _optionMenu; - [GUI] MenuItem _manageUserProfiles; - [GUI] MenuItem _fileMenu; - [GUI] MenuItem _loadApplicationFile; - [GUI] MenuItem _loadApplicationFolder; - [GUI] MenuItem _appletMenu; - [GUI] MenuItem _actionMenu; - [GUI] MenuItem _pauseEmulation; - [GUI] MenuItem _resumeEmulation; - [GUI] MenuItem _stopEmulation; - [GUI] MenuItem _simulateWakeUpMessage; - [GUI] MenuItem _scanAmiibo; - [GUI] MenuItem _takeScreenshot; - [GUI] MenuItem _hideUi; - [GUI] MenuItem _fullScreen; - [GUI] CheckMenuItem _startFullScreen; - [GUI] CheckMenuItem _showConsole; - [GUI] CheckMenuItem _favToggle; - [GUI] MenuItem _firmwareInstallDirectory; - [GUI] MenuItem _firmwareInstallFile; - [GUI] MenuItem _fileTypesSubMenu; - [GUI] Label _fifoStatus; - [GUI] CheckMenuItem _iconToggle; - [GUI] CheckMenuItem _developerToggle; - [GUI] CheckMenuItem _appToggle; - [GUI] CheckMenuItem _timePlayedToggle; - [GUI] CheckMenuItem _versionToggle; - [GUI] CheckMenuItem _lastPlayedToggle; - [GUI] CheckMenuItem _fileExtToggle; - [GUI] CheckMenuItem _pathToggle; - [GUI] CheckMenuItem _fileSizeToggle; - [GUI] CheckMenuItem _nspShown; - [GUI] CheckMenuItem _pfs0Shown; - [GUI] CheckMenuItem _xciShown; - [GUI] CheckMenuItem _ncaShown; - [GUI] CheckMenuItem _nroShown; - [GUI] CheckMenuItem _nsoShown; - [GUI] Label _gpuBackend; - [GUI] Label _dockedMode; - [GUI] Label _aspectRatio; - [GUI] Label _gameStatus; - [GUI] TreeView _gameTable; - [GUI] TreeSelection _gameTableSelection; - [GUI] ScrolledWindow _gameTableWindow; - [GUI] Label _gpuName; - [GUI] Label _progressLabel; - [GUI] Label _firmwareVersionLabel; + [GUI] MenuBar _menuBar; + [GUI] Box _footerBox; + [GUI] Box _statusBar; + [GUI] MenuItem _optionMenu; + [GUI] MenuItem _manageUserProfiles; + [GUI] MenuItem _fileMenu; + [GUI] MenuItem _loadApplicationFile; + [GUI] MenuItem _loadApplicationFolder; + [GUI] MenuItem _appletMenu; + [GUI] MenuItem _actionMenu; + [GUI] MenuItem _pauseEmulation; + [GUI] MenuItem _resumeEmulation; + [GUI] MenuItem _stopEmulation; + [GUI] MenuItem _simulateWakeUpMessage; + [GUI] MenuItem _scanAmiibo; + [GUI] MenuItem _takeScreenshot; + [GUI] MenuItem _hideUi; + [GUI] MenuItem _fullScreen; + [GUI] CheckMenuItem _startFullScreen; + [GUI] CheckMenuItem _showConsole; + [GUI] CheckMenuItem _favToggle; + [GUI] MenuItem _firmwareInstallDirectory; + [GUI] MenuItem _firmwareInstallFile; + [GUI] MenuItem _fileTypesSubMenu; + [GUI] Label _fifoStatus; + [GUI] CheckMenuItem _iconToggle; + [GUI] CheckMenuItem _developerToggle; + [GUI] CheckMenuItem _appToggle; + [GUI] CheckMenuItem _timePlayedToggle; + [GUI] CheckMenuItem _versionToggle; + [GUI] CheckMenuItem _lastPlayedToggle; + [GUI] CheckMenuItem _fileExtToggle; + [GUI] CheckMenuItem _pathToggle; + [GUI] CheckMenuItem _fileSizeToggle; + [GUI] CheckMenuItem _nspShown; + [GUI] CheckMenuItem _pfs0Shown; + [GUI] CheckMenuItem _xciShown; + [GUI] CheckMenuItem _ncaShown; + [GUI] CheckMenuItem _nroShown; + [GUI] CheckMenuItem _nsoShown; + [GUI] Label _gpuBackend; + [GUI] Label _dockedMode; + [GUI] Label _aspectRatio; + [GUI] Label _gameStatus; + [GUI] TreeView _gameTable; + [GUI] TreeSelection _gameTableSelection; + [GUI] ScrolledWindow _gameTableWindow; + [GUI] Label _gpuName; + [GUI] Label _progressLabel; + [GUI] Label _firmwareVersionLabel; [GUI] Gtk.ProgressBar _progressBar; - [GUI] Box _viewBox; - [GUI] Label _vSyncStatus; - [GUI] Label _volumeStatus; - [GUI] Box _listStatusBox; - [GUI] Label _loadingStatusLabel; + [GUI] Box _viewBox; + [GUI] Label _vSyncStatus; + [GUI] Label _volumeStatus; + [GUI] Box _listStatusBox; + [GUI] Label _loadingStatusLabel; [GUI] Gtk.ProgressBar _loadingStatusBar; -#pragma warning restore CS0649, IDE0044, CS0169 +#pragma warning restore CS0649, IDE0044, CS0169, IDE0051 public MainWindow() : this(new Builder("Ryujinx.Ui.MainWindow.glade")) { } @@ -156,14 +154,14 @@ namespace Ryujinx.Ui SetWindowSizePosition(); - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); + Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Title = $"Ryujinx {Program.Version}"; // Hide emulation context status bar. _statusBar.Hide(); // Instantiate HLE objects. - _virtualFileSystem = VirtualFileSystem.CreateInstance(); + _virtualFileSystem = VirtualFileSystem.CreateInstance(); _libHacHorizonManager = new LibHacHorizonManager(); _libHacHorizonManager.InitializeFsServer(_virtualFileSystem); @@ -178,36 +176,36 @@ namespace Ryujinx.Ui // Consider removing this at some point in the future when we don't need to worry about old saves. VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient); - _contentManager = new ContentManager(_virtualFileSystem); - _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile); + _contentManager = new ContentManager(_virtualFileSystem); + _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile); _userChannelPersistence = new UserChannelPersistence(); // Instantiate GUI objects. _applicationLibrary = new ApplicationLibrary(_virtualFileSystem); - _uiHandler = new GtkHostUiHandler(this); - _deviceExitStatus = new AutoResetEvent(false); + _uiHandler = new GtkHostUiHandler(this); + _deviceExitStatus = new AutoResetEvent(false); WindowStateEvent += WindowStateEvent_Changed; - DeleteEvent += Window_Close; - FocusInEvent += MainWindow_FocusInEvent; - FocusOutEvent += MainWindow_FocusOutEvent; + DeleteEvent += Window_Close; + FocusInEvent += MainWindow_FocusInEvent; + FocusOutEvent += MainWindow_FocusOutEvent; - _applicationLibrary.ApplicationAdded += Application_Added; + _applicationLibrary.ApplicationAdded += Application_Added; _applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; - _fileMenu.StateChanged += FileMenu_StateChanged; + _fileMenu.StateChanged += FileMenu_StateChanged; _actionMenu.StateChanged += ActionMenu_StateChanged; _optionMenu.StateChanged += OptionMenu_StateChanged; _gameTable.ButtonReleaseEvent += Row_Clicked; - _fullScreen.Activated += FullScreen_Toggled; + _fullScreen.Activated += FullScreen_Toggled; RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar; ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; if (ConfigurationState.Instance.Ui.StartFullscreen) { @@ -221,43 +219,73 @@ namespace Ryujinx.Ui _pauseEmulation.Sensitive = false; _resumeEmulation.Sensitive = false; - _nspShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value; + _nspShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value; _pfs0Shown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value; - _xciShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value; - _ncaShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value; - _nroShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value; - _nsoShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value; + _xciShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value; + _ncaShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value; + _nroShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value; + _nsoShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value; - _nspShown.Toggled += NSP_Shown_Toggled; + _nspShown.Toggled += NSP_Shown_Toggled; _pfs0Shown.Toggled += PFS0_Shown_Toggled; - _xciShown.Toggled += XCI_Shown_Toggled; - _ncaShown.Toggled += NCA_Shown_Toggled; - _nroShown.Toggled += NRO_Shown_Toggled; - _nsoShown.Toggled += NSO_Shown_Toggled; + _xciShown.Toggled += XCI_Shown_Toggled; + _ncaShown.Toggled += NCA_Shown_Toggled; + _nroShown.Toggled += NRO_Shown_Toggled; + _nsoShown.Toggled += NSO_Shown_Toggled; _fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported; - if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _developerToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeToggle.Active = true; - if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathToggle.Active = true; + if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) + { + _favToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) + { + _iconToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) + { + _appToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) + { + _developerToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) + { + _versionToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) + { + _timePlayedToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) + { + _lastPlayedToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) + { + _fileExtToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) + { + _fileSizeToggle.Active = true; + } + if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) + { + _pathToggle.Active = true; + } - _favToggle.Toggled += Fav_Toggled; - _iconToggle.Toggled += Icon_Toggled; - _appToggle.Toggled += App_Toggled; - _developerToggle.Toggled += Developer_Toggled; - _versionToggle.Toggled += Version_Toggled; + _favToggle.Toggled += Fav_Toggled; + _iconToggle.Toggled += Icon_Toggled; + _appToggle.Toggled += App_Toggled; + _developerToggle.Toggled += Developer_Toggled; + _versionToggle.Toggled += Version_Toggled; _timePlayedToggle.Toggled += TimePlayed_Toggled; _lastPlayedToggle.Toggled += LastPlayed_Toggled; - _fileExtToggle.Toggled += FileExt_Toggled; - _fileSizeToggle.Toggled += FileSize_Toggled; - _pathToggle.Toggled += Path_Toggled; + _fileExtToggle.Toggled += FileExt_Toggled; + _fileSizeToggle.Toggled += FileSize_Toggled; + _pathToggle.Toggled += Path_Toggled; _gameTable.Model = _tableStore = new ListStore( typeof(bool), @@ -276,7 +304,7 @@ namespace Ryujinx.Ui _tableStore.SetSortFunc(6, SortHelper.LastPlayedSort); _tableStore.SetSortFunc(8, SortHelper.FileSizeSort); - int columnId = ConfigurationState.Instance.Ui.ColumnSort.SortColumnId; + int columnId = ConfigurationState.Instance.Ui.ColumnSort.SortColumnId; bool ascending = ConfigurationState.Instance.Ui.ColumnSort.SortAscending; _tableStore.SetSortColumnId(columnId, ascending ? SortType.Ascending : SortType.Descending); @@ -321,10 +349,7 @@ namespace Ryujinx.Ui private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e) { - if (_emulationContext != null) - { - _emulationContext.System.ChangeDockedModeState(e.NewValue); - } + _emulationContext?.System.ChangeDockedModeState(e.NewValue); } private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e) @@ -354,19 +379,49 @@ namespace Ryujinx.Ui _gameTable.RemoveColumn(column); } - CellRendererToggle favToggle = new CellRendererToggle(); + CellRendererToggle favToggle = new(); favToggle.Toggled += FavToggle_Toggled; - if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _gameTable.AppendColumn("Fav", favToggle, "active", 0); - if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1); - if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2); - if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3); - if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4); - if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5); - if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6); - if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7); - if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8); - if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9); + if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) + { + _gameTable.AppendColumn("Fav", favToggle, "active", 0); + } + if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) + { + _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1); + } + if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) + { + _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2); + } + if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) + { + _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3); + } + if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) + { + _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4); + } + if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) + { + _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5); + } + if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) + { + _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6); + } + if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) + { + _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7); + } + if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) + { + _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8); + } + if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) + { + _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9); + } foreach (TreeViewColumn column in _gameTable.Columns) { @@ -426,11 +481,11 @@ namespace Ryujinx.Ui if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) { string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - renderer = new VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); + renderer = new Graphics.Vulkan.VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); } else { - renderer = new OpenGLRenderer(); + renderer = new Graphics.OpenGL.OpenGLRenderer(); } BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; @@ -570,38 +625,38 @@ namespace Ryujinx.Ui IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; - HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(_virtualFileSystem, - _libHacHorizonManager, - _contentManager, - _accountManager, - _userChannelPersistence, - renderer, - deviceDriver, - memoryConfiguration, - _uiHandler, - (SystemLanguage)ConfigurationState.Instance.System.Language.Value, - (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, - ConfigurationState.Instance.System.EnableDockedMode, - ConfigurationState.Instance.System.EnablePtc, - ConfigurationState.Instance.System.EnableInternetAccess, - fsIntegrityCheckLevel, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - 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); + HLE.HLEConfiguration configuration = new(_virtualFileSystem, + _libHacHorizonManager, + _contentManager, + _accountManager, + _userChannelPersistence, + renderer, + deviceDriver, + memoryConfiguration, + _uiHandler, + (SystemLanguage)ConfigurationState.Instance.System.Language.Value, + (RegionCode)ConfigurationState.Instance.System.Region.Value, + ConfigurationState.Instance.Graphics.EnableVsync, + ConfigurationState.Instance.System.EnableDockedMode, + ConfigurationState.Instance.System.EnablePtc, + ConfigurationState.Instance.System.EnableInternetAccess, + fsIntegrityCheckLevel, + ConfigurationState.Instance.System.FsGlobalAccessLogMode, + 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); _emulationContext = new HLE.Switch(configuration); } private SurfaceKHR CreateVulkanSurface(Instance instance, Vk vk) { - return new SurfaceKHR((ulong)((VKRenderer)RendererWidget).CreateWindowSurface(instance.Handle)); + return new SurfaceKHR((ulong)((VulkanRenderer)RendererWidget).CreateWindowSurface(instance.Handle)); } private void SetupProgressUiHandlers() @@ -655,14 +710,16 @@ namespace Ryujinx.Ui _tableStore.Clear(); - Thread applicationLibraryThread = new Thread(() => + Thread applicationLibraryThread = new(() => { _applicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language); _updatingGameTable = false; - }); - applicationLibraryThread.Name = "GUI.ApplicationLibraryThread"; - applicationLibraryThread.IsBackground = true; + }) + { + Name = "GUI.ApplicationLibraryThread", + IsBackground = true, + }; applicationLibraryThread.Start(); } @@ -671,11 +728,11 @@ namespace Ryujinx.Ui { if (ConfigurationState.Instance.Logger.EnableTrace.Value) { - MessageDialog debugWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) + MessageDialog debugWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) { - Title = "Ryujinx - Warning", - Text = "You have trace logging enabled, which is designed to be used by developers only.", - SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?" + Title = "Ryujinx - Warning", + Text = "You have trace logging enabled, which is designed to be used by developers only.", + SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?", }; if (debugWarningDialog.Run() == (int)ResponseType.Yes) @@ -689,11 +746,11 @@ namespace Ryujinx.Ui if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) { - MessageDialog shadersDumpWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) + MessageDialog shadersDumpWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) { - Title = "Ryujinx - Warning", - Text = "You have shader dumping enabled, which is designed to be used by developers only.", - SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?" + Title = "Ryujinx - Warning", + Text = "You have shader dumping enabled, which is designed to be used by developers only.", + SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?", }; if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes) @@ -857,18 +914,18 @@ namespace Ryujinx.Ui Thread windowThread = new(CreateGameWindow) { - Name = "GUI.WindowThread" + Name = "GUI.WindowThread", }; windowThread.Start(); - _gameLoaded = true; + _gameLoaded = true; _actionMenu.Sensitive = true; UpdateMenuItem.Sensitive = false; _lastScannedAmiiboId = ""; - _firmwareInstallFile.Sensitive = false; + _firmwareInstallFile.Sensitive = false; _firmwareInstallDirectory.Sensitive = false; DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText, @@ -885,11 +942,11 @@ namespace Ryujinx.Ui { if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) { - return new VKRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); + return new VulkanRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); } else { - return new GlRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); + return new OpenGLRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); } } @@ -1030,20 +1087,20 @@ namespace Ryujinx.Ui } } - public void UpdateGraphicsConfig() + public static void UpdateGraphicsConfig() { - int resScale = ConfigurationState.Instance.Graphics.ResScale; + int resScale = ConfigurationState.Instance.Graphics.ResScale; float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom; - Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale; - Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; - Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; - Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; + Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale; + Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; + Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; + Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; - Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; + Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; } - public void SaveConfig() + public static void SaveConfig() { ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } @@ -1105,7 +1162,7 @@ namespace Ryujinx.Ui Application.Invoke(delegate { _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded"; - float barValue = 0; + float barValue = 0; if (args.NumAppsFound != 0) { @@ -1126,12 +1183,12 @@ namespace Ryujinx.Ui { Application.Invoke(delegate { - _gameStatus.Text = args.GameStatus; - _fifoStatus.Text = args.FifoStatus; - _gpuName.Text = args.GpuName; - _dockedMode.Text = args.DockedMode; - _aspectRatio.Text = args.AspectRatio; - _gpuBackend.Text = args.GpuBackend; + _gameStatus.Text = args.GameStatus; + _fifoStatus.Text = args.FifoStatus; + _gpuName.Text = args.GpuName; + _dockedMode.Text = args.DockedMode; + _aspectRatio.Text = args.AspectRatio; + _gpuBackend.Text = args.GpuBackend; _volumeStatus.Text = GetVolumeLabelText(args.Volume); if (args.VSyncEnabled) @@ -1151,8 +1208,8 @@ namespace Ryujinx.Ui { _tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path)); - string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); - bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0); + string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); + bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0); _tableStore.SetValue(treeIter, 0, newToggleValue); @@ -1166,7 +1223,7 @@ namespace Ryujinx.Ui { TreeViewColumn column = (TreeViewColumn)sender; - ConfigurationState.Instance.Ui.ColumnSort.SortColumnId.Value = column.SortColumnId; + ConfigurationState.Instance.Ui.ColumnSort.SortColumnId.Value = column.SortColumnId; ConfigurationState.Instance.Ui.ColumnSort.SortAscending.Value = column.SortOrder == SortType.Ascending; SaveConfig(); @@ -1193,7 +1250,7 @@ namespace Ryujinx.Ui ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; } - private string GetVolumeLabelText(float volume) + private static string GetVolumeLabelText(float volume) { string icon = volume == 0 ? "🔇" : "🔊"; @@ -1237,8 +1294,8 @@ namespace Ryujinx.Ui } string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString(); - string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0]; - string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); + string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0]; + string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10); @@ -1247,43 +1304,41 @@ namespace Ryujinx.Ui private void Load_Application_File(object sender, EventArgs args) { - using (FileChooserNative fileChooser = new FileChooserNative("Choose the file to open", this, FileChooserAction.Open, "Open", "Cancel")) + using FileChooserNative fileChooser = new("Choose the file to open", this, FileChooserAction.Open, "Open", "Cancel"); + + FileFilter filter = new() { - FileFilter filter = new FileFilter() - { - Name = "Switch Executables" - }; - filter.AddPattern("*.xci"); - filter.AddPattern("*.nsp"); - filter.AddPattern("*.pfs0"); - filter.AddPattern("*.nca"); - filter.AddPattern("*.nro"); - filter.AddPattern("*.nso"); + Name = "Switch Executables", + }; + filter.AddPattern("*.xci"); + filter.AddPattern("*.nsp"); + filter.AddPattern("*.pfs0"); + filter.AddPattern("*.nca"); + filter.AddPattern("*.nro"); + filter.AddPattern("*.nso"); - fileChooser.AddFilter(filter); + fileChooser.AddFilter(filter); - if (fileChooser.Run() == (int)ResponseType.Accept) - { - RunApplication(fileChooser.Filename); - } + if (fileChooser.Run() == (int)ResponseType.Accept) + { + RunApplication(fileChooser.Filename); } } private void Load_Application_Folder(object sender, EventArgs args) { - using (FileChooserNative fileChooser = new FileChooserNative("Choose the folder to open", this, FileChooserAction.SelectFolder, "Open", "Cancel")) + using FileChooserNative fileChooser = new("Choose the folder to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); + + if (fileChooser.Run() == (int)ResponseType.Accept) { - if (fileChooser.Run() == (int)ResponseType.Accept) - { - RunApplication(fileChooser.Filename); - } + RunApplication(fileChooser.Filename); } } private void FileMenu_StateChanged(object o, StateChangedArgs args) { - _appletMenu.Sensitive = _emulationContext == null && _contentManager.GetCurrentFirmwareVersion() != null && _contentManager.GetCurrentFirmwareVersion().Major > 3; - _loadApplicationFile.Sensitive = _emulationContext == null; + _appletMenu.Sensitive = _emulationContext == null && _contentManager.GetCurrentFirmwareVersion() != null && _contentManager.GetCurrentFirmwareVersion().Major > 3; + _loadApplicationFile.Sensitive = _emulationContext == null; _loadApplicationFolder.Sensitive = _emulationContext == null; } @@ -1332,7 +1387,7 @@ namespace Ryujinx.Ui private void SetWindowSizePosition() { - DefaultWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth; + DefaultWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth; DefaultHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight; Move(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); @@ -1399,11 +1454,11 @@ namespace Ryujinx.Ui private void Installer_File_Pressed(object o, EventArgs args) { - FileChooserNative fileChooser = new FileChooserNative("Choose the firmware file to open", this, FileChooserAction.Open, "Open", "Cancel"); + FileChooserNative fileChooser = new("Choose the firmware file to open", this, FileChooserAction.Open, "Open", "Cancel"); - FileFilter filter = new FileFilter + FileFilter filter = new() { - Name = "Switch Firmware Files" + Name = "Switch Firmware Files", }; filter.AddPattern("*.zip"); filter.AddPattern("*.xci"); @@ -1415,7 +1470,7 @@ namespace Ryujinx.Ui private void Installer_Directory_Pressed(object o, EventArgs args) { - FileChooserNative directoryChooser = new FileChooserNative("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); + FileChooserNative directoryChooser = new("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); HandleInstallerDialog(directoryChooser); } @@ -1460,7 +1515,7 @@ namespace Ryujinx.Ui { Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}"); - Thread thread = new Thread(() => + Thread thread = new(() => { Application.Invoke(delegate { @@ -1483,7 +1538,7 @@ namespace Ryujinx.Ui // Purge Applet Cache. - DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); + DirectoryInfo miiEditorCacheFolder = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); if (miiEditorCacheFolder.Exists) { @@ -1504,9 +1559,10 @@ namespace Ryujinx.Ui { RefreshFirmwareLabel(); } - }); - - thread.Name = "GUI.FirmwareInstallerThread"; + }) + { + Name = "GUI.FirmwareInstallerThread", + }; thread.Start(); } } @@ -1571,7 +1627,7 @@ namespace Ryujinx.Ui else { // otherwise, clear state. - _userChannelPersistence = new UserChannelPersistence(); + _userChannelPersistence = new UserChannelPersistence(); _currentEmulatedGamePath = null; _actionMenu.Sensitive = false; _firmwareInstallFile.Sensitive = true; @@ -1616,7 +1672,7 @@ namespace Ryujinx.Ui private void Settings_Pressed(object sender, EventArgs args) { - SettingsWindow settingsWindow = new SettingsWindow(this, _virtualFileSystem, _contentManager); + SettingsWindow settingsWindow = new(this, _virtualFileSystem, _contentManager); settingsWindow.SetSizeRequest((int)(settingsWindow.DefaultWidth * Program.WindowScaleFactor), (int)(settingsWindow.DefaultHeight * Program.WindowScaleFactor)); settingsWindow.Show(); @@ -1648,7 +1704,7 @@ namespace Ryujinx.Ui private void ManageUserProfiles_Pressed(object sender, EventArgs args) { - UserProfilesManagerWindow userProfilesManagerWindow = new UserProfilesManagerWindow(_accountManager, _contentManager, _virtualFileSystem); + UserProfilesManagerWindow userProfilesManagerWindow = new(_accountManager, _contentManager, _virtualFileSystem); userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor)); userProfilesManagerWindow.Show(); @@ -1656,15 +1712,12 @@ namespace Ryujinx.Ui private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args) { - if (_emulationContext != null) - { - _emulationContext.System.SimulateWakeUpMessage(); - } + _emulationContext?.System.SimulateWakeUpMessage(); } private void ActionMenu_StateChanged(object o, StateChangedArgs args) { - _scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _); + _scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _); _takeScreenshot.Sensitive = _emulationContext != null; } @@ -1672,12 +1725,12 @@ namespace Ryujinx.Ui { if (_emulationContext.System.SearchingForAmiibo(out int deviceId)) { - AmiiboWindow amiiboWindow = new AmiiboWindow + AmiiboWindow amiiboWindow = new() { LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll, - LastScannedAmiiboId = _lastScannedAmiiboId, - DeviceId = deviceId, - TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.ToUpper() + LastScannedAmiiboId = _lastScannedAmiiboId, + DeviceId = deviceId, + TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.ToUpper(), }; amiiboWindow.DeleteEvent += AmiiboWindow_DeleteEvent; @@ -1702,7 +1755,7 @@ namespace Ryujinx.Ui { if (((AmiiboWindow)sender).AmiiboId != "" && ((AmiiboWindow)sender).Response == ResponseType.Ok) { - _lastScannedAmiiboId = ((AmiiboWindow)sender).AmiiboId; + _lastScannedAmiiboId = ((AmiiboWindow)sender).AmiiboId; _lastScannedAmiiboShowAll = ((AmiiboWindow)sender).LastScannedAmiiboShowAll; _emulationContext.System.ScanAmiibo(((AmiiboWindow)sender).DeviceId, ((AmiiboWindow)sender).AmiiboId, ((AmiiboWindow)sender).UseRandomUuid); @@ -1722,7 +1775,7 @@ namespace Ryujinx.Ui private void About_Pressed(object sender, EventArgs args) { - AboutWindow aboutWindow = new AboutWindow(); + AboutWindow aboutWindow = new(); aboutWindow.SetSizeRequest((int)(aboutWindow.DefaultWidth * Program.WindowScaleFactor), (int)(aboutWindow.DefaultHeight * Program.WindowScaleFactor)); aboutWindow.Show(); @@ -1824,7 +1877,7 @@ namespace Ryujinx.Ui UpdateGameTable(); } - private void XCI_Shown_Toggled (object sender, EventArgs args) + private void XCI_Shown_Toggled(object sender, EventArgs args) { ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = _xciShown.Active; @@ -1832,7 +1885,7 @@ namespace Ryujinx.Ui UpdateGameTable(); } - private void NCA_Shown_Toggled (object sender, EventArgs args) + private void NCA_Shown_Toggled(object sender, EventArgs args) { ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = _ncaShown.Active; @@ -1840,7 +1893,7 @@ namespace Ryujinx.Ui UpdateGameTable(); } - private void NRO_Shown_Toggled (object sender, EventArgs args) + private void NRO_Shown_Toggled(object sender, EventArgs args) { ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = _nroShown.Active; @@ -1848,7 +1901,7 @@ namespace Ryujinx.Ui UpdateGameTable(); } - private void NSO_Shown_Toggled (object sender, EventArgs args) + private void NSO_Shown_Toggled(object sender, EventArgs args) { ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = _nsoShown.Active; diff --git a/src/Ryujinx/Ui/GLRenderer.cs b/src/Ryujinx/Ui/OpenGLRenderer.cs similarity index 91% rename from src/Ryujinx/Ui/GLRenderer.cs rename to src/Ryujinx/Ui/OpenGLRenderer.cs index c56996910..2ca791fe8 100644 --- a/src/Ryujinx/Ui/GLRenderer.cs +++ b/src/Ryujinx/Ui/OpenGLRenderer.cs @@ -1,7 +1,6 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.Graphics.OpenGL; using Ryujinx.Input.HLE; using SPB.Graphics; using SPB.Graphics.Exceptions; @@ -15,16 +14,16 @@ using System.Runtime.InteropServices; namespace Ryujinx.Ui { - public partial class GlRenderer : RendererWidgetBase + public partial class OpenGLRenderer : RendererWidgetBase { - private GraphicsDebugLevel _glLogLevel; + private readonly GraphicsDebugLevel _glLogLevel; private bool _initializedOpenGL; private OpenGLContextBase _openGLContext; private SwappableNativeWindowBase _nativeWindow; - public GlRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) + public OpenGLRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { _glLogLevel = glLogLevel; } @@ -93,7 +92,7 @@ namespace Ryujinx.Ui public override void InitializeRenderer() { // First take exclusivity on the OpenGL context. - ((OpenGLRenderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext)); + ((Graphics.OpenGL.OpenGLRenderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext)); _openGLContext.MakeCurrent(_nativeWindow); @@ -140,4 +139,4 @@ namespace Ryujinx.Ui _openGLContext?.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/OpenToolkitBindingsContext.cs b/src/Ryujinx/Ui/OpenToolkitBindingsContext.cs index ec4111fa3..b35673ebb 100644 --- a/src/Ryujinx/Ui/OpenToolkitBindingsContext.cs +++ b/src/Ryujinx/Ui/OpenToolkitBindingsContext.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Ui { public class OpenToolkitBindingsContext : OpenTK.IBindingsContext { - private IBindingsContext _bindingContext; + private readonly IBindingsContext _bindingContext; public OpenToolkitBindingsContext(IBindingsContext bindingsContext) { diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index 87ff7f6c0..0ee344434 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -21,14 +21,13 @@ using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; +using Image = SixLabors.ImageSharp.Image; +using Key = Ryujinx.Input.Key; +using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter; +using Switch = Ryujinx.HLE.Switch; namespace Ryujinx.Ui { - using Image = SixLabors.ImageSharp.Image; - using Key = Input.Key; - using ScalingFilter = Graphics.GAL.ScalingFilter; - using Switch = HLE.Switch; - public abstract class RendererWidgetBase : DrawingArea { private const int SwitchPanelWidth = 1280; @@ -71,12 +70,12 @@ namespace Ryujinx.Ui // Hide Cursor const int CursorHideIdleTime = 5; // seconds - private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor); + private static readonly Cursor _invisibleCursor = new(Display.Default, CursorType.BlankCursor); private long _lastCursorMoveTime; private HideCursorMode _hideCursorMode; - private InputManager _inputManager; - private IKeyboard _keyboardInterface; - private GraphicsDebugLevel _glLogLevel; + private readonly InputManager _inputManager; + private readonly IKeyboard _keyboardInterface; + private readonly GraphicsDebugLevel _glLogLevel; private string _gpuBackendName; private string _gpuVendorName; private bool _isMouseInClient; @@ -165,7 +164,7 @@ namespace Ryujinx.Ui Window.Cursor = _invisibleCursor; break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(state)); } }); } @@ -379,12 +378,12 @@ namespace Ryujinx.Ui { lock (this) { - var currentTime = DateTime.Now; - string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; - string directory = AppDataManager.Mode switch + var currentTime = DateTime.Now; + string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; + string directory = AppDataManager.Mode switch { AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") + _ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), }; string path = System.IO.Path.Combine(directory, filename); @@ -415,7 +414,7 @@ namespace Ryujinx.Ui image.SaveAsPng(path, new PngEncoder() { - ColorType = PngColorType.Rgb + ColorType = PngColorType.Rgb, }); image.Dispose(); @@ -524,30 +523,30 @@ namespace Ryujinx.Ui { parent.Present(); - var activeProcess = Device.Processes.ActiveApplication; + var activeProcess = Device.Processes.ActiveApplication; - string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; + string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}"; - string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; - string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; + string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; + string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; parent.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); - Thread renderLoopThread = new Thread(Render) + Thread renderLoopThread = new(Render) { - Name = "GUI.RenderLoop" + Name = "GUI.RenderLoop", }; renderLoopThread.Start(); - Thread nvStutterWorkaround = null; + Thread nvidiaStutterWorkaround = null; if (Renderer is Graphics.OpenGL.OpenGLRenderer) { - nvStutterWorkaround = new Thread(NVStutterWorkaround) + nvidiaStutterWorkaround = new Thread(NvidiaStutterWorkaround) { - Name = "GUI.NVStutterWorkaround" + Name = "GUI.NvidiaStutterWorkaround", }; - nvStutterWorkaround.Start(); + nvidiaStutterWorkaround.Start(); } MainLoop(); @@ -556,7 +555,7 @@ namespace Ryujinx.Ui // We only need to wait for all commands submitted during the main gpu loop to be processed. _gpuDoneEvent.WaitOne(); _gpuDoneEvent.Dispose(); - nvStutterWorkaround?.Join(); + nvidiaStutterWorkaround?.Join(); Exit(); } @@ -584,7 +583,7 @@ namespace Ryujinx.Ui } } - private void NVStutterWorkaround() + private void NvidiaStutterWorkaround() { while (_isActive) { @@ -752,7 +751,7 @@ namespace Ryujinx.Ui ResScaleUp = 1 << 5, ResScaleDown = 1 << 6, VolumeUp = 1 << 7, - VolumeDown = 1 << 8 + VolumeDown = 1 << 8, } private KeyboardHotkeyState GetHotkeyState() @@ -807,4 +806,4 @@ namespace Ryujinx.Ui return state; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/SPBOpenGLContext.cs b/src/Ryujinx/Ui/SPBOpenGLContext.cs index 97644269b..b6195a9c6 100644 --- a/src/Ryujinx/Ui/SPBOpenGLContext.cs +++ b/src/Ryujinx/Ui/SPBOpenGLContext.cs @@ -9,8 +9,8 @@ namespace Ryujinx.Ui { class SPBOpenGLContext : IOpenGLContext { - private OpenGLContextBase _context; - private NativeWindowBase _window; + private readonly OpenGLContextBase _context; + private readonly NativeWindowBase _window; private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window) { diff --git a/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs b/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs index 046597b07..949390caa 100644 --- a/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs @@ -4,8 +4,8 @@ namespace Ryujinx.Ui { public class StatusUpdatedEventArgs : EventArgs { - public bool VSyncEnabled; - public float Volume; + public bool VSyncEnabled; + public float Volume; public string DockedMode; public string AspectRatio; public string GameStatus; @@ -16,13 +16,13 @@ namespace Ryujinx.Ui public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) { VSyncEnabled = vSyncEnabled; - Volume = volume; - GpuBackend = gpuBackend; - DockedMode = dockedMode; - AspectRatio = aspectRatio; - GameStatus = gameStatus; - FifoStatus = fifoStatus; - GpuName = gpuName; + Volume = volume; + GpuBackend = gpuBackend; + DockedMode = dockedMode; + AspectRatio = aspectRatio; + GameStatus = gameStatus; + FifoStatus = fifoStatus; + GpuName = gpuName; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/VKRenderer.cs b/src/Ryujinx/Ui/VulkanRenderer.cs similarity index 93% rename from src/Ryujinx/Ui/VKRenderer.cs rename to src/Ryujinx/Ui/VulkanRenderer.cs index d2106c58f..ecb3fa243 100644 --- a/src/Ryujinx/Ui/VKRenderer.cs +++ b/src/Ryujinx/Ui/VulkanRenderer.cs @@ -12,12 +12,12 @@ using System.Runtime.InteropServices; namespace Ryujinx.Ui { - public partial class VKRenderer : RendererWidgetBase + public partial class VulkanRenderer : RendererWidgetBase { public NativeWindowBase NativeWindow { get; private set; } private UpdateBoundsCallbackDelegate _updateBoundsCallback; - public VKRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { } + public VulkanRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { } private NativeWindowBase RetrieveNativeWindow() { diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs index 5a0563d93..0f7b4f22b 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Ui.Widgets private MenuItem _manageCheatMenuItem; private MenuItem _openTitleModDirMenuItem; private MenuItem _openTitleSdModDirMenuItem; - private Menu _extractSubMenu; + private Menu _extractSubMenu; private MenuItem _extractMenuItem; private MenuItem _extractRomFsMenuItem; private MenuItem _extractExeFsMenuItem; private MenuItem _extractLogoMenuItem; - private Menu _manageSubMenu; + private Menu _manageSubMenu; private MenuItem _manageCacheMenuItem; private MenuItem _purgePtcCacheMenuItem; private MenuItem _purgeShaderCacheMenuItem; @@ -31,7 +31,7 @@ namespace Ryujinx.Ui.Widgets // _openSaveUserDirMenuItem = new MenuItem("Open User Save Directory") { - TooltipText = "Open the directory which contains Application's User Saves." + TooltipText = "Open the directory which contains Application's User Saves.", }; _openSaveUserDirMenuItem.Activated += OpenSaveUserDir_Clicked; @@ -40,7 +40,7 @@ namespace Ryujinx.Ui.Widgets // _openSaveDeviceDirMenuItem = new MenuItem("Open Device Save Directory") { - TooltipText = "Open the directory which contains Application's Device Saves." + TooltipText = "Open the directory which contains Application's Device Saves.", }; _openSaveDeviceDirMenuItem.Activated += OpenSaveDeviceDir_Clicked; @@ -49,7 +49,7 @@ namespace Ryujinx.Ui.Widgets // _openSaveBcatDirMenuItem = new MenuItem("Open BCAT Save Directory") { - TooltipText = "Open the directory which contains Application's BCAT Saves." + TooltipText = "Open the directory which contains Application's BCAT Saves.", }; _openSaveBcatDirMenuItem.Activated += OpenSaveBcatDir_Clicked; @@ -58,7 +58,7 @@ namespace Ryujinx.Ui.Widgets // _manageTitleUpdatesMenuItem = new MenuItem("Manage Title Updates") { - TooltipText = "Open the Title Update management window" + TooltipText = "Open the Title Update management window", }; _manageTitleUpdatesMenuItem.Activated += ManageTitleUpdates_Clicked; @@ -67,7 +67,7 @@ namespace Ryujinx.Ui.Widgets // _manageDlcMenuItem = new MenuItem("Manage DLC") { - TooltipText = "Open the DLC management window" + TooltipText = "Open the DLC management window", }; _manageDlcMenuItem.Activated += ManageDlc_Clicked; @@ -76,7 +76,7 @@ namespace Ryujinx.Ui.Widgets // _manageCheatMenuItem = new MenuItem("Manage Cheats") { - TooltipText = "Open the Cheat management window" + TooltipText = "Open the Cheat management window", }; _manageCheatMenuItem.Activated += ManageCheats_Clicked; @@ -85,7 +85,7 @@ namespace Ryujinx.Ui.Widgets // _openTitleModDirMenuItem = new MenuItem("Open Mods Directory") { - TooltipText = "Open the directory which contains Application's Mods." + TooltipText = "Open the directory which contains Application's Mods.", }; _openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked; @@ -94,7 +94,7 @@ namespace Ryujinx.Ui.Widgets // _openTitleSdModDirMenuItem = new MenuItem("Open Atmosphere Mods Directory") { - TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods." + TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods.", }; _openTitleSdModDirMenuItem.Activated += OpenTitleSdModDir_Clicked; @@ -116,7 +116,7 @@ namespace Ryujinx.Ui.Widgets // _extractRomFsMenuItem = new MenuItem("RomFS") { - TooltipText = "Extract the RomFS section from Application's current config (including updates)." + TooltipText = "Extract the RomFS section from Application's current config (including updates).", }; _extractRomFsMenuItem.Activated += ExtractRomFs_Clicked; @@ -125,7 +125,7 @@ namespace Ryujinx.Ui.Widgets // _extractExeFsMenuItem = new MenuItem("ExeFS") { - TooltipText = "Extract the ExeFS section from Application's current config (including updates)." + TooltipText = "Extract the ExeFS section from Application's current config (including updates).", }; _extractExeFsMenuItem.Activated += ExtractExeFs_Clicked; @@ -134,7 +134,7 @@ namespace Ryujinx.Ui.Widgets // _extractLogoMenuItem = new MenuItem("Logo") { - TooltipText = "Extract the Logo section from Application's current config (including updates)." + TooltipText = "Extract the Logo section from Application's current config (including updates).", }; _extractLogoMenuItem.Activated += ExtractLogo_Clicked; @@ -148,7 +148,7 @@ namespace Ryujinx.Ui.Widgets // _manageCacheMenuItem = new MenuItem("Cache Management") { - Submenu = _manageSubMenu + Submenu = _manageSubMenu, }; // @@ -156,7 +156,7 @@ namespace Ryujinx.Ui.Widgets // _purgePtcCacheMenuItem = new MenuItem("Queue PPTC Rebuild") { - TooltipText = "Trigger PPTC to rebuild at boot time on the next game launch." + TooltipText = "Trigger PPTC to rebuild at boot time on the next game launch.", }; _purgePtcCacheMenuItem.Activated += PurgePtcCache_Clicked; @@ -165,7 +165,7 @@ namespace Ryujinx.Ui.Widgets // _purgeShaderCacheMenuItem = new MenuItem("Purge Shader Cache") { - TooltipText = "Delete the Application's shader cache." + TooltipText = "Delete the Application's shader cache.", }; _purgeShaderCacheMenuItem.Activated += PurgeShaderCache_Clicked; @@ -174,7 +174,7 @@ namespace Ryujinx.Ui.Widgets // _openPtcDirMenuItem = new MenuItem("Open PPTC Directory") { - TooltipText = "Open the directory which contains the Application's PPTC cache." + TooltipText = "Open the directory which contains the Application's PPTC cache.", }; _openPtcDirMenuItem.Activated += OpenPtcDir_Clicked; @@ -183,7 +183,7 @@ namespace Ryujinx.Ui.Widgets // _openShaderCacheDirMenuItem = new MenuItem("Open Shader Cache Directory") { - TooltipText = "Open the directory which contains the Application's shader cache." + TooltipText = "Open the directory which contains the Application's shader cache.", }; _openShaderCacheDirMenuItem.Activated += OpenShaderCacheDir_Clicked; @@ -217,4 +217,4 @@ namespace Ryujinx.Ui.Widgets ShowAll(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 6279891e6..8170b9317 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -31,19 +31,19 @@ namespace Ryujinx.Ui.Widgets { public partial class GameTableContextMenu : Menu { - private readonly MainWindow _parent; - private readonly VirtualFileSystem _virtualFileSystem; - private readonly AccountManager _accountManager; - private readonly HorizonClient _horizonClient; + private readonly MainWindow _parent; + private readonly VirtualFileSystem _virtualFileSystem; + private readonly AccountManager _accountManager; + private readonly HorizonClient _horizonClient; private readonly BlitStruct<ApplicationControlProperty> _controlData; private readonly string _titleFilePath; private readonly string _titleName; private readonly string _titleIdText; - private readonly ulong _titleId; + private readonly ulong _titleId; private MessageDialog _dialog; - private bool _cancel; + private bool _cancel; public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData) { @@ -52,12 +52,12 @@ namespace Ryujinx.Ui.Widgets InitializeComponent(); _virtualFileSystem = virtualFileSystem; - _accountManager = accountManager; - _horizonClient = horizonClient; - _titleFilePath = titleFilePath; - _titleName = titleName; - _titleIdText = titleId; - _controlData = controlData; + _accountManager = accountManager; + _horizonClient = horizonClient; + _titleFilePath = titleFilePath; + _titleName = titleName; + _titleIdText = titleId; + _controlData = controlData; if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId)) { @@ -66,16 +66,16 @@ namespace Ryujinx.Ui.Widgets return; } - _openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0; - _openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0; - _openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0; + _openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0; + _openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0; + _openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0; string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower(); - bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci"; + bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci"; _extractRomFsMenuItem.Sensitive = hasNca; _extractExeFsMenuItem.Sensitive = hasNca; - _extractLogoMenuItem.Sensitive = hasNca; + _extractLogoMenuItem.Sensitive = hasNca; PopupAtPointer(null); } @@ -99,13 +99,13 @@ namespace Ryujinx.Ui.Widgets control = ref new BlitStruct<ApplicationControlProperty>(1).Value; // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; + control.UserAccountSaveDataSize = 0x4000; control.UserAccountSaveDataJournalSize = 0x4000; Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } - Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); + Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); @@ -148,7 +148,7 @@ namespace Ryujinx.Ui.Widgets } string committedPath = System.IO.Path.Combine(saveRootPath, "0"); - string workingPath = System.IO.Path.Combine(saveRootPath, "1"); + string workingPath = System.IO.Path.Combine(saveRootPath, "1"); // If the committed directory exists, that path will be loaded the next time the savedata is mounted if (Directory.Exists(committedPath)) @@ -170,25 +170,25 @@ namespace Ryujinx.Ui.Widgets private void ExtractSection(NcaSectionType ncaSectionType, int programIndex = 0) { - FileChooserNative fileChooser = new FileChooserNative("Choose the folder to extract into", _parent, FileChooserAction.SelectFolder, "Extract", "Cancel"); + FileChooserNative fileChooser = new("Choose the folder to extract into", _parent, FileChooserAction.SelectFolder, "Extract", "Cancel"); - ResponseType response = (ResponseType)fileChooser.Run(); - string destination = fileChooser.Filename; + ResponseType response = (ResponseType)fileChooser.Run(); + string destination = fileChooser.Filename; fileChooser.Dispose(); if (response == ResponseType.Accept) { - Thread extractorThread = new Thread(() => + Thread extractorThread = new(() => { Gtk.Application.Invoke(delegate { _dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Cancel, null) { - Title = "Ryujinx - NCA Section Extractor", - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"), - SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...", - WindowPosition = WindowPosition.Center + Title = "Ryujinx - NCA Section Extractor", + Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"), + SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...", + WindowPosition = WindowPosition.Center, }; int dialogResponse = _dialog.Run(); @@ -199,139 +199,140 @@ namespace Ryujinx.Ui.Widgets } }); - using (FileStream file = new FileStream(_titleFilePath, FileMode.Open, FileAccess.Read)) + using FileStream file = new(_titleFilePath, FileMode.Open, FileAccess.Read); + + Nca mainNca = null; + Nca patchNca = null; + + if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") || + (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") || + (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci")) { - Nca mainNca = null; - Nca patchNca = null; + PartitionFileSystem pfs; - if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") || - (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") || - (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci")) + if (System.IO.Path.GetExtension(_titleFilePath) == ".xci") { - PartitionFileSystem pfs; + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - if (System.IO.Path.GetExtension(_titleFilePath) == ".xci") + pfs = xci.OpenPartition(XciPartitionType.Secure); + } + else + { + pfs = new PartitionFileSystem(file.AsStorage()); + } + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef<IFile>(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Release().AsStorage()); + + if (nca.Header.ContentType == NcaContentType.Program) { - Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - pfs = xci.OpenPartition(XciPartitionType.Secure); - } - else - { - pfs = new PartitionFileSystem(file.AsStorage()); - } - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Release().AsStorage()); - - if (nca.Header.ContentType == NcaContentType.Program) + if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } + patchNca = nca; + } + else + { + mainNca = nca; } } } - else if (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nca") - { - mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); - } + } + else if (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nca") + { + mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); + } - if (mainNca == null) - { - Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA is not present in the selected file."); + if (mainNca == null) + { + Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA is not present in the selected file."); - Gtk.Application.Invoke(delegate + Gtk.Application.Invoke(delegate { GtkDialog.CreateErrorDialog("Extraction failure. The main NCA is not present in the selected file."); }); - return; - } + return; + } - (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); - if (updatePatchNca != null) - { - patchNca = updatePatchNca; - } + if (updatePatchNca != null) + { + patchNca = updatePatchNca; + } - int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); + int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); - bool sectionExistsInPatch = false; - if (patchNca != null) - { - sectionExistsInPatch = patchNca.CanOpenSection(index); - } + bool sectionExistsInPatch = false; - IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) + if (patchNca != null) + { + sectionExistsInPatch = patchNca.CanOpenSection(index); + } + + IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); - FileSystemClient fsClient = _horizonClient.Fs; + FileSystemClient fsClient = _horizonClient.Fs; - string source = DateTime.Now.ToFileTime().ToString()[10..]; - string output = DateTime.Now.ToFileTime().ToString()[10..]; + string source = DateTime.Now.ToFileTime().ToString()[10..]; + string output = DateTime.Now.ToFileTime().ToString()[10..]; - using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem); - using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination)); + using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem); + using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination)); - fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); - fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); + fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); + fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); - (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); + (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); - if (!canceled) + if (!canceled) + { + if (resultCode.Value.IsFailure()) { - if (resultCode.Value.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); + Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); - Gtk.Application.Invoke(delegate + Gtk.Application.Invoke(delegate { _dialog?.Dispose(); GtkDialog.CreateErrorDialog("Extraction failed. Read the log file for further information."); }); - } - else if (resultCode.Value.IsSuccess()) - { - Gtk.Application.Invoke(delegate + } + else if (resultCode.Value.IsSuccess()) + { + Gtk.Application.Invoke(delegate { _dialog?.Dispose(); - MessageDialog dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) + MessageDialog dialog = new(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) { - Title = "Ryujinx - NCA Section Extractor", - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"), - SecondaryText = "Extraction completed successfully.", - WindowPosition = WindowPosition.Center + Title = "Ryujinx - NCA Section Extractor", + Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"), + SecondaryText = "Extraction completed successfully.", + WindowPosition = WindowPosition.Center, }; dialog.Run(); dialog.Dispose(); }); - } } - - fsClient.Unmount(source.ToU8Span()); - fsClient.Unmount(output.ToU8Span()); } - }); - extractorThread.Name = "GUI.NcaSectionExtractorThread"; - extractorThread.IsBackground = true; + fsClient.Unmount(source.ToU8Span()); + fsClient.Unmount(output.ToU8Span()); + }) + { + Name = "GUI.NcaSectionExtractorThread", + IsBackground = true, + }; extractorThread.Start(); } } @@ -339,7 +340,10 @@ namespace Ryujinx.Ui.Widgets private (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath) { Result rc = fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath.ToU8Span(), OpenDirectoryMode.All); - if (rc.IsFailure()) return (rc, false); + if (rc.IsFailure()) + { + return (rc, false); + } using (sourceHandle) { @@ -369,7 +373,10 @@ namespace Ryujinx.Ui.Widgets fs.CreateOrOverwriteFile(subDstPath, entry.Size); rc = CopyFile(fs, subSrcPath, subDstPath); - if (rc.IsFailure()) return (rc, false); + if (rc.IsFailure()) + { + return (rc, false); + } } } } @@ -377,22 +384,31 @@ namespace Ryujinx.Ui.Widgets return (Result.Success, false); } - public Result CopyFile(FileSystemClient fs, string sourcePath, string destPath) + public static Result CopyFile(FileSystemClient fs, string sourcePath, string destPath) { Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath.ToU8Span(), OpenMode.Read); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } using (sourceHandle) { rc = fs.OpenFile(out FileHandle destHandle, destPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } using (destHandle) { const int MaxBufferSize = 1024 * 1024; rc = fs.GetFileSize(out long fileSize, sourceHandle); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } int bufferSize = (int)Math.Min(MaxBufferSize, fileSize); @@ -405,10 +421,16 @@ namespace Ryujinx.Ui.Widgets Span<byte> buf = buffer.AsSpan(0, toRead); rc = fs.ReadFile(out long _, sourceHandle, offset, buf); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } rc = fs.WriteFile(destHandle, offset, buf, WriteOption.None); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } } } finally @@ -417,7 +439,10 @@ namespace Ryujinx.Ui.Widgets } rc = fs.FlushFile(destHandle); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } } } @@ -466,7 +491,7 @@ namespace Ryujinx.Ui.Widgets private void OpenTitleModDir_Clicked(object sender, EventArgs args) { - string modsBasePath = ModLoader.GetModsBasePath(); + string modsBasePath = ModLoader.GetModsBasePath(); string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); @@ -474,8 +499,8 @@ namespace Ryujinx.Ui.Widgets private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) { - string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); + string sdModsBasePath = ModLoader.GetSdModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } @@ -497,9 +522,9 @@ namespace Ryujinx.Ui.Widgets private void OpenPtcDir_Clicked(object sender, EventArgs args) { - string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu"); + string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu"); - string mainPath = System.IO.Path.Combine(ptcDir, "0"); + string mainPath = System.IO.Path.Combine(ptcDir, "0"); string backupPath = System.IO.Path.Combine(ptcDir, "1"); if (!Directory.Exists(ptcDir)) @@ -526,12 +551,12 @@ namespace Ryujinx.Ui.Widgets private void PurgePtcCache_Clicked(object sender, EventArgs args) { - DirectoryInfo mainDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0")); - DirectoryInfo backupDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1")); + DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0")); + DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1")); MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?"); - List<FileInfo> cacheFiles = new List<FileInfo>(); + List<FileInfo> cacheFiles = new(); if (mainDir.Exists) { @@ -551,7 +576,7 @@ namespace Ryujinx.Ui.Widgets { file.Delete(); } - catch(Exception e) + catch (Exception e) { GtkDialog.CreateErrorDialog($"Error purging PPTC cache {file.Name}: {e}"); } @@ -563,12 +588,12 @@ namespace Ryujinx.Ui.Widgets private void PurgeShaderCache_Clicked(object sender, EventArgs args) { - DirectoryInfo shaderCacheDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader")); + DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader")); using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?"); - List<DirectoryInfo> oldCacheDirectories = new List<DirectoryInfo>(); - List<FileInfo> newCacheFiles = new List<FileInfo>(); + List<DirectoryInfo> oldCacheDirectories = new(); + List<FileInfo> newCacheFiles = new(); if (shaderCacheDir.Exists) { diff --git a/src/Ryujinx/Ui/Widgets/GtkDialog.cs b/src/Ryujinx/Ui/Widgets/GtkDialog.cs index d2cab2196..51e777fa1 100644 --- a/src/Ryujinx/Ui/Widgets/GtkDialog.cs +++ b/src/Ryujinx/Ui/Widgets/GtkDialog.cs @@ -10,14 +10,14 @@ namespace Ryujinx.Ui.Widgets { private static bool _isChoiceDialogOpen; - private GtkDialog(string title, string mainText, string secondaryText, MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok) + private GtkDialog(string title, string mainText, string secondaryText, MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok) : base(null, DialogFlags.Modal, messageType, buttonsType, null) { - Title = title; - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); - Text = mainText; - SecondaryText = secondaryText; - WindowPosition = WindowPosition.Center; + Title = title; + Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); + Text = mainText; + SecondaryText = secondaryText; + WindowPosition = WindowPosition.Center; SecondaryUseMarkup = true; Response += GtkDialog_Response; @@ -80,7 +80,7 @@ namespace Ryujinx.Ui.Widgets internal static ResponseType CreateCustomDialog(string title, string mainText, string secondaryText, Dictionary<int, string> buttons, MessageType messageType = MessageType.Other) { - GtkDialog gtkDialog = new GtkDialog(title, mainText, secondaryText, messageType, ButtonsType.None); + GtkDialog gtkDialog = new(title, mainText, secondaryText, messageType, ButtonsType.None); foreach (var button in buttons) { @@ -92,9 +92,9 @@ namespace Ryujinx.Ui.Widgets internal static string CreateInputDialog(Window parent, string title, string mainText, uint inputMax) { - GtkInputDialog gtkDialog = new GtkInputDialog(parent, title, mainText, inputMax); - ResponseType response = (ResponseType)gtkDialog.Run(); - string responseText = gtkDialog.InputEntry.Text.TrimEnd(); + GtkInputDialog gtkDialog = new(parent, title, mainText, inputMax); + ResponseType response = (ResponseType)gtkDialog.Run(); + string responseText = gtkDialog.InputEntry.Text.TrimEnd(); gtkDialog.Dispose(); diff --git a/src/Ryujinx/Ui/Widgets/GtkInputDialog.cs b/src/Ryujinx/Ui/Widgets/GtkInputDialog.cs index 21b349373..e4fb5aa17 100644 --- a/src/Ryujinx/Ui/Widgets/GtkInputDialog.cs +++ b/src/Ryujinx/Ui/Widgets/GtkInputDialog.cs @@ -12,26 +12,26 @@ namespace Ryujinx.Ui.Widgets Title = title; - Label mainTextLabel = new Label + Label mainTextLabel = new() { - Text = mainText + Text = mainText, }; InputEntry = new Entry { - MaxLength = (int)inputMax + MaxLength = (int)inputMax, }; - Label inputMaxTextLabel = new Label + Label inputMaxTextLabel = new() { - Text = $"(Max length: {inputMax})" + Text = $"(Max length: {inputMax})", }; - ((Box)MessageArea).PackStart(mainTextLabel, true, true, 0); - ((Box)MessageArea).PackStart(InputEntry, true, true, 5); + ((Box)MessageArea).PackStart(mainTextLabel, true, true, 0); + ((Box)MessageArea).PackStart(InputEntry, true, true, 5); ((Box)MessageArea).PackStart(inputMaxTextLabel, true, true, 0); ShowAll(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Widgets/ProfileDialog.cs b/src/Ryujinx/Ui/Widgets/ProfileDialog.cs index 242e8bd7d..0731b37a1 100644 --- a/src/Ryujinx/Ui/Widgets/ProfileDialog.cs +++ b/src/Ryujinx/Ui/Widgets/ProfileDialog.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Ui.Widgets { public string FileName { get; private set; } -#pragma warning disable CS0649, IDE0044 +#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier [GUI] Entry _profileEntry; [GUI] Label _errorMessage; #pragma warning restore CS0649, IDE0044 @@ -54,4 +54,4 @@ namespace Ryujinx.Ui.Widgets Respond(ResponseType.Cancel); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Widgets/UserErrorDialog.cs b/src/Ryujinx/Ui/Widgets/UserErrorDialog.cs index fc3938d42..b2dea8355 100644 --- a/src/Ryujinx/Ui/Widgets/UserErrorDialog.cs +++ b/src/Ryujinx/Ui/Widgets/UserErrorDialog.cs @@ -6,9 +6,9 @@ namespace Ryujinx.Ui.Widgets { internal class UserErrorDialog : MessageDialog { - private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"; - private const int OkResponseId = 0; - private const int SetupGuideResponseId = 1; + private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"; + private const int OkResponseId = 0; + private const int SetupGuideResponseId = 1; private readonly UserError _userError; @@ -16,7 +16,7 @@ namespace Ryujinx.Ui.Widgets { _userError = error; - WindowPosition = WindowPosition.Center; + WindowPosition = WindowPosition.Center; SecondaryUseMarkup = true; Response += UserErrorDialog_Response; @@ -36,8 +36,8 @@ namespace Ryujinx.Ui.Widgets SecondaryUseMarkup = true; - Title = $"Ryujinx error ({errorCode})"; - Text = $"{errorCode}: {GetErrorTitle(error)}"; + Title = $"Ryujinx error ({errorCode})"; + Text = $"{errorCode}: {GetErrorTitle(error)}"; SecondaryText = GetErrorDescription(error); if (isInSetupGuide) @@ -46,34 +46,34 @@ namespace Ryujinx.Ui.Widgets } } - private string GetErrorCode(UserError error) + private static string GetErrorCode(UserError error) { return $"RYU-{(uint)error:X4}"; } - private string GetErrorTitle(UserError error) + private static string GetErrorTitle(UserError error) { return error switch { - UserError.NoKeys => "Keys not found", - UserError.NoFirmware => "Firmware not found", + UserError.NoKeys => "Keys not found", + UserError.NoFirmware => "Firmware not found", UserError.FirmwareParsingFailed => "Firmware parsing error", - UserError.ApplicationNotFound => "Application not found", - UserError.Unknown => "Unknown error", - _ => "Undefined error", + UserError.ApplicationNotFound => "Application not found", + UserError.Unknown => "Unknown error", + _ => "Undefined error", }; } - private string GetErrorDescription(UserError error) + private static string GetErrorDescription(UserError error) { return error switch { - UserError.NoKeys => "Ryujinx was unable to find your 'prod.keys' file", - UserError.NoFirmware => "Ryujinx was unable to find any firmwares installed", + UserError.NoKeys => "Ryujinx was unable to find your 'prod.keys' file", + UserError.NoFirmware => "Ryujinx was unable to find any firmwares installed", UserError.FirmwareParsingFailed => "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.", - UserError.ApplicationNotFound => "Ryujinx couldn't find a valid application at the given path.", - UserError.Unknown => "An unknown error occured!", - _ => "An undefined error occured! This shouldn't happen, please contact a dev!", + UserError.ApplicationNotFound => "Ryujinx couldn't find a valid application at the given path.", + UserError.Unknown => "An unknown error occured!", + _ => "An undefined error occured! This shouldn't happen, please contact a dev!", }; } @@ -82,9 +82,9 @@ namespace Ryujinx.Ui.Widgets return error switch { UserError.NoKeys or - UserError.NoFirmware or + UserError.NoFirmware or UserError.FirmwareParsingFailed => true, - _ => false, + _ => false, }; } @@ -97,9 +97,9 @@ namespace Ryujinx.Ui.Widgets return error switch { - UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys", + UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys", UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware", - _ => SetupGuideUrl, + _ => SetupGuideUrl, }; } @@ -120,4 +120,4 @@ namespace Ryujinx.Ui.Widgets new UserErrorDialog(error).Run(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs b/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs index 3edc002d7..345026334 100644 --- a/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs +++ b/src/Ryujinx/Ui/Windows/AboutWindow.Designer.cs @@ -7,65 +7,63 @@ namespace Ryujinx.Ui.Windows { public partial class AboutWindow : Window { - private Box _mainBox; - private Box _leftBox; - private Box _logoBox; - private Image _ryujinxLogo; - private Box _logoTextBox; - private Label _ryujinxLabel; - private Label _ryujinxPhoneticLabel; - private EventBox _ryujinxLink; - private Label _ryujinxLinkLabel; - private Label _versionLabel; - private Label _disclaimerLabel; - private EventBox _amiiboApiLink; - private Label _amiiboApiLinkLabel; - private Box _socialBox; - private EventBox _patreonEventBox; - private Box _patreonBox; - private Image _patreonLogo; - private Label _patreonLabel; - private EventBox _githubEventBox; - private Box _githubBox; - private Image _githubLogo; - private Label _githubLabel; - private Box _discordBox; - private EventBox _discordEventBox; - private Image _discordLogo; - private Label _discordLabel; - private EventBox _twitterEventBox; - private Box _twitterBox; - private Image _twitterLogo; - private Label _twitterLabel; - private Separator _separator; - private Box _rightBox; - private Label _aboutLabel; - private Label _aboutDescriptionLabel; - private Label _createdByLabel; - private TextView _createdByText; - private EventBox _contributorsEventBox; - private Label _contributorsLinkLabel; - private Label _patreonNamesLabel; + private Box _mainBox; + private Box _leftBox; + private Box _logoBox; + private Image _ryujinxLogo; + private Box _logoTextBox; + private Label _ryujinxLabel; + private Label _ryujinxPhoneticLabel; + private EventBox _ryujinxLink; + private Label _ryujinxLinkLabel; + private Label _versionLabel; + private Label _disclaimerLabel; + private EventBox _amiiboApiLink; + private Label _amiiboApiLinkLabel; + private Box _socialBox; + private EventBox _patreonEventBox; + private Box _patreonBox; + private Image _patreonLogo; + private Label _patreonLabel; + private EventBox _githubEventBox; + private Box _githubBox; + private Image _githubLogo; + private Label _githubLabel; + private Box _discordBox; + private EventBox _discordEventBox; + private Image _discordLogo; + private Label _discordLabel; + private EventBox _twitterEventBox; + private Box _twitterBox; + private Image _twitterLogo; + private Label _twitterLabel; + private Separator _separator; + private Box _rightBox; + private Label _aboutLabel; + private Label _aboutDescriptionLabel; + private Label _createdByLabel; + private TextView _createdByText; + private EventBox _contributorsEventBox; + private Label _contributorsLinkLabel; + private Label _patreonNamesLabel; private ScrolledWindow _patreonNamesScrolled; - private TextView _patreonNamesText; - private EventBox _changelogEventBox; - private Label _changelogLinkLabel; + private TextView _patreonNamesText; + private EventBox _changelogEventBox; + private Label _changelogLinkLabel; private void InitializeComponent() { -#pragma warning disable CS0612 - // // AboutWindow // - CanFocus = false; - Resizable = false; - Modal = true; + CanFocus = false; + Resizable = false; + Modal = true; WindowPosition = WindowPosition.Center; - DefaultWidth = 800; - DefaultHeight = 450; - TypeHint = Gdk.WindowTypeHint.Dialog; + DefaultWidth = 800; + DefaultHeight = 450; + TypeHint = Gdk.WindowTypeHint.Dialog; // // _mainBox @@ -77,9 +75,9 @@ namespace Ryujinx.Ui.Windows // _leftBox = new Box(Orientation.Vertical, 0) { - Margin = 15, - MarginLeft = 30, - MarginRight = 0 + Margin = 15, + MarginStart = 30, + MarginEnd = 0, }; // @@ -92,8 +90,8 @@ namespace Ryujinx.Ui.Windows // _ryujinxLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png", 100, 100)) { - Margin = 10, - MarginLeft = 15 + Margin = 10, + MarginStart = 15, }; // @@ -106,9 +104,9 @@ namespace Ryujinx.Ui.Windows // _ryujinxLabel = new Label("Ryujinx") { - MarginTop = 15, - Justify = Justification.Center, - Attributes = new AttrList() + MarginTop = 15, + Justify = Justification.Center, + Attributes = new AttrList(), }; _ryujinxLabel.Attributes.Insert(new Pango.AttrScale(2.7f)); @@ -117,7 +115,7 @@ namespace Ryujinx.Ui.Windows // _ryujinxPhoneticLabel = new Label("(REE-YOU-JINX)") { - Justify = Justification.Center + Justify = Justification.Center, }; // @@ -135,8 +133,8 @@ namespace Ryujinx.Ui.Windows _ryujinxLinkLabel = new Label("www.ryujinx.org") { TooltipText = "Click to open the Ryujinx website in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList() + Justify = Justification.Center, + Attributes = new AttrList(), }; _ryujinxLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); @@ -145,9 +143,9 @@ namespace Ryujinx.Ui.Windows // _versionLabel = new Label(Program.Version) { - Expand = true, + Expand = true, Justify = Justification.Center, - Margin = 5 + Margin = 5, }; // @@ -163,7 +161,7 @@ namespace Ryujinx.Ui.Windows { TooltipText = "Click to open the changelog for this version in your default browser.", Justify = Justification.Center, - Attributes = new AttrList() + Attributes = new AttrList(), }; _changelogLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); @@ -172,10 +170,10 @@ namespace Ryujinx.Ui.Windows // _disclaimerLabel = new Label("Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.") { - Expand = true, - Justify = Justification.Center, - Margin = 5, - Attributes = new AttrList() + Expand = true, + Justify = Justification.Center, + Margin = 5, + Attributes = new AttrList(), }; _disclaimerLabel.Attributes.Insert(new Pango.AttrScale(0.8f)); @@ -184,7 +182,7 @@ namespace Ryujinx.Ui.Windows // _amiiboApiLink = new EventBox() { - Margin = 5 + Margin = 5, }; _amiiboApiLink.ButtonPressEvent += AmiiboApiButton_Pressed; @@ -194,8 +192,8 @@ namespace Ryujinx.Ui.Windows _amiiboApiLinkLabel = new Label("AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.") { TooltipText = "Click to open the AmiiboAPI website in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList() + Justify = Justification.Center, + Attributes = new AttrList(), }; _amiiboApiLinkLabel.Attributes.Insert(new Pango.AttrScale(0.9f)); @@ -204,8 +202,8 @@ namespace Ryujinx.Ui.Windows // _socialBox = new Box(Orientation.Horizontal, 0) { - Margin = 25, - MarginBottom = 10 + Margin = 25, + MarginBottom = 10, }; // @@ -213,7 +211,7 @@ namespace Ryujinx.Ui.Windows // _patreonEventBox = new EventBox() { - TooltipText = "Click to open the Ryujinx Patreon page in your default browser." + TooltipText = "Click to open the Ryujinx Patreon page in your default browser.", }; _patreonEventBox.ButtonPressEvent += PatreonButton_Pressed; @@ -227,7 +225,7 @@ namespace Ryujinx.Ui.Windows // _patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png", 30, 30)) { - Margin = 10 + Margin = 10, }; // @@ -235,7 +233,7 @@ namespace Ryujinx.Ui.Windows // _patreonLabel = new Label("Patreon") { - Justify = Justification.Center + Justify = Justification.Center, }; // @@ -243,7 +241,7 @@ namespace Ryujinx.Ui.Windows // _githubEventBox = new EventBox() { - TooltipText = "Click to open the Ryujinx GitHub page in your default browser." + TooltipText = "Click to open the Ryujinx GitHub page in your default browser.", }; _githubEventBox.ButtonPressEvent += GitHubButton_Pressed; @@ -257,7 +255,7 @@ namespace Ryujinx.Ui.Windows // _githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png", 30, 30)) { - Margin = 10 + Margin = 10, }; // @@ -265,7 +263,7 @@ namespace Ryujinx.Ui.Windows // _githubLabel = new Label("GitHub") { - Justify = Justification.Center + Justify = Justification.Center, }; // @@ -278,7 +276,7 @@ namespace Ryujinx.Ui.Windows // _discordEventBox = new EventBox() { - TooltipText = "Click to open an invite to the Ryujinx Discord server in your default browser." + TooltipText = "Click to open an invite to the Ryujinx Discord server in your default browser.", }; _discordEventBox.ButtonPressEvent += DiscordButton_Pressed; @@ -287,7 +285,7 @@ namespace Ryujinx.Ui.Windows // _discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png", 30, 30)) { - Margin = 10 + Margin = 10, }; // @@ -295,7 +293,7 @@ namespace Ryujinx.Ui.Windows // _discordLabel = new Label("Discord") { - Justify = Justification.Center + Justify = Justification.Center, }; // @@ -303,7 +301,7 @@ namespace Ryujinx.Ui.Windows // _twitterEventBox = new EventBox() { - TooltipText = "Click to open the Ryujinx Twitter page in your default browser." + TooltipText = "Click to open the Ryujinx Twitter page in your default browser.", }; _twitterEventBox.ButtonPressEvent += TwitterButton_Pressed; @@ -317,7 +315,7 @@ namespace Ryujinx.Ui.Windows // _twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png", 30, 30)) { - Margin = 10 + Margin = 10, }; // @@ -325,7 +323,7 @@ namespace Ryujinx.Ui.Windows // _twitterLabel = new Label("Twitter") { - Justify = Justification.Center + Justify = Justification.Center, }; // @@ -333,7 +331,7 @@ namespace Ryujinx.Ui.Windows // _separator = new Separator(Orientation.Vertical) { - Margin = 15 + Margin = 15, }; // @@ -341,8 +339,8 @@ namespace Ryujinx.Ui.Windows // _rightBox = new Box(Orientation.Vertical, 0) { - Margin = 15, - MarginTop = 40 + Margin = 15, + MarginTop = 40, }; // @@ -350,8 +348,8 @@ namespace Ryujinx.Ui.Windows // _aboutLabel = new Label("About :") { - Halign = Align.Start, - Attributes = new AttrList() + Halign = Align.Start, + Attributes = new AttrList(), }; _aboutLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _aboutLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); @@ -365,7 +363,7 @@ namespace Ryujinx.Ui.Windows "Developers interested in contributing can find out more on our GitHub or Discord.") { Margin = 15, - Halign = Align.Start + Halign = Align.Start, }; // @@ -373,8 +371,8 @@ namespace Ryujinx.Ui.Windows // _createdByLabel = new Label("Maintained by :") { - Halign = Align.Start, - Attributes = new AttrList() + Halign = Align.Start, + Attributes = new AttrList(), }; _createdByLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _createdByLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); @@ -384,11 +382,11 @@ namespace Ryujinx.Ui.Windows // _createdByText = new TextView() { - WrapMode = Gtk.WrapMode.Word, - Editable = false, + WrapMode = Gtk.WrapMode.Word, + Editable = false, CursorVisible = false, - Margin = 15, - MarginRight = 30 + Margin = 15, + MarginEnd = 30, }; _createdByText.Buffer.Text = "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD« and more..."; @@ -404,9 +402,9 @@ namespace Ryujinx.Ui.Windows _contributorsLinkLabel = new Label("See All Contributors...") { TooltipText = "Click to open the Contributors page in your default browser.", - MarginRight = 30, - Halign = Align.End, - Attributes = new AttrList() + MarginEnd = 30, + Halign = Align.End, + Attributes = new AttrList(), }; _contributorsLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); @@ -415,8 +413,8 @@ namespace Ryujinx.Ui.Windows // _patreonNamesLabel = new Label("Supported on Patreon by :") { - Halign = Align.Start, - Attributes = new AttrList() + Halign = Align.Start, + Attributes = new AttrList(), }; _patreonNamesLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _patreonNamesLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); @@ -426,10 +424,10 @@ namespace Ryujinx.Ui.Windows // _patreonNamesScrolled = new ScrolledWindow() { - Margin = 15, - MarginRight = 30, - Expand = true, - ShadowType = ShadowType.In + Margin = 15, + MarginEnd = 30, + Expand = true, + ShadowType = ShadowType.In, }; _patreonNamesScrolled.SetPolicy(PolicyType.Never, PolicyType.Automatic); @@ -438,13 +436,11 @@ namespace Ryujinx.Ui.Windows // _patreonNamesText = new TextView() { - WrapMode = Gtk.WrapMode.Word + WrapMode = Gtk.WrapMode.Word, }; _patreonNamesText.Buffer.Text = "Loading..."; _patreonNamesText.SetProperty("editable", new GLib.Value(false)); -#pragma warning restore CS0612 - ShowComponent(); } @@ -512,4 +508,4 @@ namespace Ryujinx.Ui.Windows ShowAll(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/AboutWindow.cs b/src/Ryujinx/Ui/Windows/AboutWindow.cs index 15bfa500d..9e11905db 100644 --- a/src/Ryujinx/Ui/Windows/AboutWindow.cs +++ b/src/Ryujinx/Ui/Windows/AboutWindow.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Ui.Windows _patreonNamesText.Buffer.Text = "Connection Error."; } - HttpClient httpClient = new HttpClient(); + HttpClient httpClient = new(); try { @@ -82,4 +82,4 @@ namespace Ryujinx.Ui.Windows OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs b/src/Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs index 3480c6e8a..cb27c34cb 100644 --- a/src/Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs +++ b/src/Ryujinx/Ui/Windows/AmiiboWindow.Designer.cs @@ -4,37 +4,35 @@ namespace Ryujinx.Ui.Windows { public partial class AmiiboWindow : Window { - private Box _mainBox; - private ButtonBox _buttonBox; - private Button _scanButton; - private Button _cancelButton; - private CheckButton _randomUuidCheckBox; - private Box _amiiboBox; - private Box _amiiboHeadBox; - private Box _amiiboSeriesBox; - private Label _amiiboSeriesLabel; + private Box _mainBox; + private ButtonBox _buttonBox; + private Button _scanButton; + private Button _cancelButton; + private CheckButton _randomUuidCheckBox; + private Box _amiiboBox; + private Box _amiiboHeadBox; + private Box _amiiboSeriesBox; + private Label _amiiboSeriesLabel; private ComboBoxText _amiiboSeriesComboBox; - private Box _amiiboCharsBox; - private Label _amiiboCharsLabel; + private Box _amiiboCharsBox; + private Label _amiiboCharsLabel; private ComboBoxText _amiiboCharsComboBox; - private CheckButton _showAllCheckBox; - private Image _amiiboImage; - private Label _gameUsageLabel; + private CheckButton _showAllCheckBox; + private Image _amiiboImage; + private Label _gameUsageLabel; private void InitializeComponent() { -#pragma warning disable CS0612 - // // AmiiboWindow // - CanFocus = false; - Resizable = false; - Modal = true; + CanFocus = false; + Resizable = false; + Modal = true; WindowPosition = WindowPosition.Center; - DefaultWidth = 600; - DefaultHeight = 470; - TypeHint = Gdk.WindowTypeHint.Dialog; + DefaultWidth = 600; + DefaultHeight = 470; + TypeHint = Gdk.WindowTypeHint.Dialog; // // _mainBox @@ -46,8 +44,8 @@ namespace Ryujinx.Ui.Windows // _buttonBox = new ButtonBox(Orientation.Horizontal) { - Margin = 20, - LayoutStyle = ButtonBoxStyle.End + Margin = 20, + LayoutStyle = ButtonBoxStyle.End, }; // @@ -55,10 +53,10 @@ namespace Ryujinx.Ui.Windows // _scanButton = new Button() { - Label = "Scan It!", - CanFocus = true, + Label = "Scan It!", + CanFocus = true, ReceivesDefault = true, - MarginLeft = 10 + MarginStart = 10, }; _scanButton.Clicked += ScanButton_Pressed; @@ -67,8 +65,8 @@ namespace Ryujinx.Ui.Windows // _randomUuidCheckBox = new CheckButton() { - Label = "Hack: Use Random Tag Uuid", - TooltipText = "This allows multiple scans of a single Amiibo.\n(used in The Legend of Zelda: Breath of the Wild)" + Label = "Hack: Use Random Tag Uuid", + TooltipText = "This allows multiple scans of a single Amiibo.\n(used in The Legend of Zelda: Breath of the Wild)", }; // @@ -76,10 +74,10 @@ namespace Ryujinx.Ui.Windows // _cancelButton = new Button() { - Label = "Cancel", - CanFocus = true, + Label = "Cancel", + CanFocus = true, ReceivesDefault = true, - MarginLeft = 10 + MarginStart = 10, }; _cancelButton.Clicked += CancelButton_Pressed; @@ -94,7 +92,7 @@ namespace Ryujinx.Ui.Windows _amiiboHeadBox = new Box(Orientation.Horizontal, 0) { Margin = 20, - Hexpand = true + Hexpand = true, }; // @@ -102,7 +100,7 @@ namespace Ryujinx.Ui.Windows // _amiiboSeriesBox = new Box(Orientation.Horizontal, 0) { - Hexpand = true + Hexpand = true, }; // @@ -120,7 +118,7 @@ namespace Ryujinx.Ui.Windows // _amiiboCharsBox = new Box(Orientation.Horizontal, 0) { - Hexpand = true + Hexpand = true, }; // @@ -138,7 +136,7 @@ namespace Ryujinx.Ui.Windows // _showAllCheckBox = new CheckButton() { - Label = "Show All Amiibo" + Label = "Show All Amiibo", }; // @@ -147,7 +145,7 @@ namespace Ryujinx.Ui.Windows _amiiboImage = new Image() { HeightRequest = 350, - WidthRequest = 350 + WidthRequest = 350, }; // @@ -155,11 +153,9 @@ namespace Ryujinx.Ui.Windows // _gameUsageLabel = new Label("") { - MarginTop = 20 + MarginTop = 20, }; -#pragma warning restore CS0612 - ShowComponent(); } @@ -191,4 +187,4 @@ namespace Ryujinx.Ui.Windows ShowAll(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs index 5bf69d5af..4bbaaaaa4 100644 --- a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -14,21 +14,19 @@ using System.Net.Http; using System.Reflection; using System.Text; using System.Threading.Tasks; -using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi; -using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ui.Windows { public partial class AmiiboWindow : Window { - private const string DEFAULT_JSON = "{ \"amiibo\": [] }"; + private const string DefaultJson = "{ \"amiibo\": [] }"; public string AmiiboId { get; private set; } - public int DeviceId { get; set; } - public string TitleId { get; set; } - public string LastScannedAmiiboId { get; set; } - public bool LastScannedAmiiboShowAll { get; set; } + public int DeviceId { get; set; } + public string TitleId { get; set; } + public string LastScannedAmiiboId { get; set; } + public bool LastScannedAmiiboShowAll { get; set; } public ResponseType Response { get; private set; } @@ -41,13 +39,13 @@ namespace Ryujinx.Ui.Windows } private readonly HttpClient _httpClient; - private readonly string _amiiboJsonPath; + private readonly string _amiiboJsonPath; private readonly byte[] _amiiboLogoBytes; private List<AmiiboApi> _amiiboList; - private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo") { @@ -57,18 +55,18 @@ namespace Ryujinx.Ui.Windows _httpClient = new HttpClient() { - Timeout = TimeSpan.FromSeconds(30) + Timeout = TimeSpan.FromSeconds(30), }; Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); _amiiboJsonPath = System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); - _amiiboList = new List<AmiiboApi>(); + _amiiboList = new List<AmiiboApi>(); - _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png"); + _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png"); _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes); - _scanButton.Sensitive = false; + _scanButton.Sensitive = false; _randomUuidCheckBox.Sensitive = false; _ = LoadContentAsync(); @@ -76,13 +74,13 @@ namespace Ryujinx.Ui.Windows private async Task LoadContentAsync() { - string amiiboJsonString = DEFAULT_JSON; + string amiiboJsonString = DefaultJson; if (File.Exists(_amiiboJsonPath)) { amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -103,7 +101,7 @@ namespace Ryujinx.Ui.Windows } } - _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; + _amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); if (LastScannedAmiiboShowAll) @@ -118,7 +116,7 @@ namespace Ryujinx.Ui.Windows private void ParseAmiiboData() { - List<string> comboxItemList = new List<string>(); + List<string> comboxItemList = new(); for (int i = 0; i < _amiiboList.Count; i++) { @@ -149,7 +147,7 @@ namespace Ryujinx.Ui.Windows } _amiiboSeriesComboBox.Changed += SeriesComboBox_Changed; - _amiiboCharsComboBox.Changed += CharacterComboBox_Changed; + _amiiboCharsComboBox.Changed += CharacterComboBox_Changed; if (LastScannedAmiiboId != "") { @@ -219,7 +217,7 @@ namespace Ryujinx.Ui.Windows Close(); } - return DEFAULT_JSON; + return DefaultJson; } private async Task UpdateAmiiboPreview(string imageUrl) @@ -228,14 +226,14 @@ namespace Ryujinx.Ui.Windows if (response.IsSuccessStatusCode) { - byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync(); - Gdk.Pixbuf amiiboPreview = new Gdk.Pixbuf(amiiboPreviewBytes); + byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync(); + Gdk.Pixbuf amiiboPreview = new(amiiboPreviewBytes); - float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width, + float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width, (float)_amiiboImage.AllocatedHeight / amiiboPreview.Height); int resizeHeight = (int)(amiiboPreview.Height * ratio); - int resizeWidth = (int)(amiiboPreview.Width * ratio); + int resizeWidth = (int)(amiiboPreview.Width * ratio); _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear); } @@ -245,7 +243,7 @@ namespace Ryujinx.Ui.Windows } } - private void ShowInfoDialog() + private static void ShowInfoDialog() { GtkDialog.CreateInfoDialog($"Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online."); } @@ -261,7 +259,7 @@ namespace Ryujinx.Ui.Windows List<AmiiboApi> amiiboSortedList = _amiiboList.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeriesComboBox.ActiveId).OrderBy(amiibo => amiibo.Name).ToList(); - List<string> comboxItemList = new List<string>(); + List<string> comboxItemList = new(); for (int i = 0; i < amiiboSortedList.Count; i++) { @@ -295,7 +293,7 @@ namespace Ryujinx.Ui.Windows _amiiboCharsComboBox.Active = 0; - _scanButton.Sensitive = true; + _scanButton.Sensitive = true; _randomUuidCheckBox.Sensitive = true; } @@ -346,12 +344,12 @@ namespace Ryujinx.Ui.Windows _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes); _amiiboSeriesComboBox.Changed -= SeriesComboBox_Changed; - _amiiboCharsComboBox.Changed -= CharacterComboBox_Changed; + _amiiboCharsComboBox.Changed -= CharacterComboBox_Changed; _amiiboSeriesComboBox.RemoveAll(); _amiiboCharsComboBox.RemoveAll(); - _scanButton.Sensitive = false; + _scanButton.Sensitive = false; _randomUuidCheckBox.Sensitive = false; new Task(() => ParseAmiiboData()).Start(); @@ -368,8 +366,8 @@ namespace Ryujinx.Ui.Windows private void CancelButton_Pressed(object sender, EventArgs args) { - AmiiboId = ""; - LastScannedAmiiboId = ""; + AmiiboId = ""; + LastScannedAmiiboId = ""; LastScannedAmiiboShowAll = false; Response = ResponseType.Cancel; @@ -384,4 +382,4 @@ namespace Ryujinx.Ui.Windows base.Dispose(disposing); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/AvatarWindow.cs b/src/Ryujinx/Ui/Windows/AvatarWindow.cs index 0cda890f5..3940f17d6 100644 --- a/src/Ryujinx/Ui/Windows/AvatarWindow.cs +++ b/src/Ryujinx/Ui/Windows/AvatarWindow.cs @@ -18,7 +18,6 @@ using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Reflection; - using Image = SixLabors.ImageSharp.Image; namespace Ryujinx.Ui.Windows @@ -26,23 +25,23 @@ namespace Ryujinx.Ui.Windows public class AvatarWindow : Window { public byte[] SelectedProfileImage; - public bool NewUser; + public bool NewUser; - private static Dictionary<string, byte[]> _avatarDict = new Dictionary<string, byte[]>(); + private static readonly Dictionary<string, byte[]> _avatarDict = new(); - private ListStore _listStore; - private IconView _iconView; - private Button _setBackgroungColorButton; - private Gdk.RGBA _backgroundColor; + private readonly ListStore _listStore; + private readonly IconView _iconView; + private readonly Button _setBackgroungColorButton; + private Gdk.RGBA _backgroundColor; public AvatarWindow() : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar") { Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); - CanFocus = false; + CanFocus = false; Resizable = false; - Modal = true; - TypeHint = Gdk.WindowTypeHint.Dialog; + Modal = true; + TypeHint = Gdk.WindowTypeHint.Dialog; SetDefaultSize(740, 400); SetPosition(WindowPosition.Center); @@ -50,54 +49,56 @@ namespace Ryujinx.Ui.Windows Box vbox = new(Orientation.Vertical, 0); Add(vbox); - ScrolledWindow scrolledWindow = new ScrolledWindow + ScrolledWindow scrolledWindow = new() { - ShadowType = ShadowType.EtchedIn + ShadowType = ShadowType.EtchedIn, }; scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic); Box hbox = new(Orientation.Horizontal, 0); - Button chooseButton = new Button() + Button chooseButton = new() { - Label = "Choose", - CanFocus = true, - ReceivesDefault = true + Label = "Choose", + CanFocus = true, + ReceivesDefault = true, }; chooseButton.Clicked += ChooseButton_Pressed; _setBackgroungColorButton = new Button() { - Label = "Set Background Color", - CanFocus = true + Label = "Set Background Color", + CanFocus = true, }; _setBackgroungColorButton.Clicked += SetBackgroungColorButton_Pressed; - _backgroundColor.Red = 1; + _backgroundColor.Red = 1; _backgroundColor.Green = 1; - _backgroundColor.Blue = 1; + _backgroundColor.Blue = 1; _backgroundColor.Alpha = 1; - Button closeButton = new Button() + Button closeButton = new() { - Label = "Close", - CanFocus = true + Label = "Close", + CanFocus = true, }; closeButton.Clicked += CloseButton_Pressed; - vbox.PackStart(scrolledWindow, true, true, 0); - hbox.PackStart(chooseButton, true, true, 0); - hbox.PackStart(_setBackgroungColorButton, true, true, 0); - hbox.PackStart(closeButton, true, true, 0); - vbox.PackStart(hbox, false, false, 0); + vbox.PackStart(scrolledWindow, true, true, 0); + hbox.PackStart(chooseButton, true, true, 0); + hbox.PackStart(_setBackgroungColorButton, true, true, 0); + hbox.PackStart(closeButton, true, true, 0); + vbox.PackStart(hbox, false, false, 0); _listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf)); _listStore.SetSortColumnId(0, SortType.Ascending); - _iconView = new IconView(_listStore); - _iconView.ItemWidth = 64; - _iconView.ItemPadding = 10; - _iconView.PixbufColumn = 1; + _iconView = new IconView(_listStore) + { + ItemWidth = 64, + ItemPadding = 10, + PixbufColumn = 1, + }; _iconView.SelectionChanged += IconView_SelectionChanged; @@ -118,39 +119,36 @@ namespace Ryujinx.Ui.Windows } string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data); - string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); + string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(avatarPath)) { - using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open)) + using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open); + + Nca nca = new(virtualFileSystem.KeySet, ncaFileStream); + IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + foreach (var item in romfs.EnumerateEntries()) { - Nca nca = new Nca(virtualFileSystem.KeySet, ncaFileStream); - IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. - foreach (var item in romfs.EnumerateEntries()) + if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) { - // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. + using var file = new UniqueRef<IFile>(); - if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) - { - using var file = new UniqueRef<IFile>(); + romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); - romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using MemoryStream streamPng = MemoryStreamManager.Shared.GetStream(); + file.Get.AsStream().CopyTo(stream); - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (MemoryStream streamPng = MemoryStreamManager.Shared.GetStream()) - { - file.Get.AsStream().CopyTo(stream); + stream.Position = 0; - stream.Position = 0; + Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); - Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); + avatarImage.SaveAsPng(streamPng); - avatarImage.SaveAsPng(streamPng); - - _avatarDict.Add(item.FullPath, streamPng.ToArray()); - } - } + _avatarDict.Add(item.FullPath, streamPng.ToArray()); } } } @@ -165,23 +163,24 @@ namespace Ryujinx.Ui.Windows _listStore.AppendValues(avatar.Key, new Gdk.Pixbuf(ProcessImage(avatar.Value), 96, 96)); } - _iconView.SelectPath(new TreePath(new int[] { 0 })); + _iconView.SelectPath(new TreePath(new[] { 0 })); } private byte[] ProcessImage(byte[] data) { - using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream()) - { - Image avatarImage = Image.Load(data, new PngDecoder()); + using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); - avatarImage.Mutate(x => x.BackgroundColor(new Rgba32((byte)(_backgroundColor.Red * 255), - (byte)(_backgroundColor.Green * 255), - (byte)(_backgroundColor.Blue * 255), - (byte)(_backgroundColor.Alpha * 255)))); - avatarImage.SaveAsJpeg(streamJpg); + Image avatarImage = Image.Load(data, new PngDecoder()); - return streamJpg.ToArray(); - } + avatarImage.Mutate(x => x.BackgroundColor(new Rgba32( + (byte)(_backgroundColor.Red * 255), + (byte)(_backgroundColor.Green * 255), + (byte)(_backgroundColor.Blue * 255), + (byte)(_backgroundColor.Alpha * 255) + ))); + avatarImage.SaveAsJpeg(streamJpg); + + return streamJpg.ToArray(); } private void CloseButton_Pressed(object sender, EventArgs e) @@ -203,20 +202,19 @@ namespace Ryujinx.Ui.Windows private void SetBackgroungColorButton_Pressed(object sender, EventArgs e) { - using (ColorChooserDialog colorChooserDialog = new ColorChooserDialog("Set Background Color", this)) + using ColorChooserDialog colorChooserDialog = new("Set Background Color", this); + + colorChooserDialog.UseAlpha = false; + colorChooserDialog.Rgba = _backgroundColor; + + if (colorChooserDialog.Run() == (int)ResponseType.Ok) { - colorChooserDialog.UseAlpha = false; - colorChooserDialog.Rgba = _backgroundColor; - - if (colorChooserDialog.Run() == (int)ResponseType.Ok) - { - _backgroundColor = colorChooserDialog.Rgba; + _backgroundColor = colorChooserDialog.Rgba; - ProcessAvatars(); - } - - colorChooserDialog.Hide(); + ProcessAvatars(); } + + colorChooserDialog.Hide(); } private void ChooseButton_Pressed(object sender, EventArgs e) @@ -226,69 +224,68 @@ namespace Ryujinx.Ui.Windows private static byte[] DecompressYaz0(Stream stream) { - using (BinaryReader reader = new BinaryReader(stream)) + using BinaryReader reader = new(stream); + + reader.ReadInt32(); // Magic + + uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); + + reader.ReadInt64(); // Padding + + byte[] input = new byte[stream.Length - stream.Position]; + stream.Read(input, 0, input.Length); + + long inputOffset = 0; + + byte[] output = new byte[decodedLength]; + long outputOffset = 0; + + ushort mask = 0; + byte header = 0; + + while (outputOffset < decodedLength) { - reader.ReadInt32(); // Magic - - uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); - - reader.ReadInt64(); // Padding - - byte[] input = new byte[stream.Length - stream.Position]; - stream.Read(input, 0, input.Length); - - long inputOffset = 0; - - byte[] output = new byte[decodedLength]; - long outputOffset = 0; - - ushort mask = 0; - byte header = 0; - - while (outputOffset < decodedLength) + if ((mask >>= 1) == 0) { - if ((mask >>= 1) == 0) + header = input[inputOffset++]; + mask = 0x80; + } + + if ((header & mask) > 0) + { + if (outputOffset == output.Length) { - header = input[inputOffset++]; - mask = 0x80; + break; } - if ((header & mask) > 0) - { - if (outputOffset == output.Length) - { - break; - } + output[outputOffset++] = input[inputOffset++]; + } + else + { + byte byte1 = input[inputOffset++]; + byte byte2 = input[inputOffset++]; - output[outputOffset++] = input[inputOffset++]; + int dist = ((byte1 & 0xF) << 8) | byte2; + int position = (int)outputOffset - (dist + 1); + + int length = byte1 >> 4; + if (length == 0) + { + length = input[inputOffset++] + 0x12; } else { - byte byte1 = input[inputOffset++]; - byte byte2 = input[inputOffset++]; + length += 2; + } - int dist = ((byte1 & 0xF) << 8) | byte2; - int position = (int)outputOffset - (dist + 1); - - int length = byte1 >> 4; - if (length == 0) - { - length = input[inputOffset++] + 0x12; - } - else - { - length += 2; - } - - while (length-- > 0) - { - output[outputOffset++] = output[position++]; - } + while (length-- > 0) + { + output[outputOffset++] = output[position++]; } } - - return output; } + + return output; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs index 32df2c0c2..1eca732b2 100644 --- a/src/Ryujinx/Ui/Windows/CheatWindow.cs +++ b/src/Ryujinx/Ui/Windows/CheatWindow.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; - using GUI = Gtk.Builder.ObjectAttribute; namespace Ryujinx.Ui.Windows @@ -16,11 +15,11 @@ namespace Ryujinx.Ui.Windows private readonly string _enabledCheatsPath; private readonly bool _noCheatsFound; -#pragma warning disable CS0649, IDE0044 - [GUI] Label _baseTitleInfoLabel; +#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier + [GUI] Label _baseTitleInfoLabel; [GUI] TextView _buildIdTextView; [GUI] TreeView _cheatTreeView; - [GUI] Button _saveButton; + [GUI] Button _saveButton; #pragma warning restore CS0649, IDE0044 public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { } @@ -31,14 +30,14 @@ namespace Ryujinx.Ui.Windows _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}"; - string modsBasePath = ModLoader.GetModsBasePath(); + string modsBasePath = ModLoader.GetModsBasePath(); string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt"); _cheatTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string), typeof(string)); - CellRendererToggle enableToggle = new CellRendererToggle(); + CellRendererToggle enableToggle = new(); enableToggle.Toggled += (sender, args) => { _cheatTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); @@ -62,7 +61,7 @@ namespace Ryujinx.Ui.Windows var buildIdColumn = _cheatTreeView.AppendColumn("Build Id", new CellRendererText(), "text", 3); buildIdColumn.Visible = false; - string[] enabled = { }; + string[] enabled = Array.Empty<string>(); if (File.Exists(_enabledCheatsPath)) { @@ -90,7 +89,7 @@ namespace Ryujinx.Ui.Windows parentIter = ((TreeStore)_cheatTreeView.Model).AppendValues(false, buildId, parentPath, ""); } - string cleanName = cheat.Name.Substring(1, cheat.Name.Length - 8); + string cleanName = cheat.Name[1..^7]; ((TreeStore)_cheatTreeView.Model).AppendValues(parentIter, enabled.Contains($"{buildId}-{cheat.Name}"), cleanName, "", buildId); cheatAdded++; @@ -116,7 +115,7 @@ namespace Ryujinx.Ui.Windows return; } - List<string> enabledCheats = new List<string>(); + List<string> enabledCheats = new(); if (_cheatTreeView.Model.GetIterFirst(out TreeIter parentIter)) { diff --git a/src/Ryujinx/Ui/Windows/ControllerWindow.cs b/src/Ryujinx/Ui/Windows/ControllerWindow.cs index 9b4befd85..19cc6f204 100644 --- a/src/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/src/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -31,47 +31,47 @@ namespace Ryujinx.Ui.Windows private bool _isWaitingForInput; -#pragma warning disable CS0649, IDE0044 - [GUI] Adjustment _controllerStrongRumble; - [GUI] Adjustment _controllerWeakRumble; - [GUI] Adjustment _controllerDeadzoneLeft; - [GUI] Adjustment _controllerDeadzoneRight; - [GUI] Adjustment _controllerRangeLeft; - [GUI] Adjustment _controllerRangeRight; - [GUI] Adjustment _controllerTriggerThreshold; - [GUI] Adjustment _slotNumber; - [GUI] Adjustment _altSlotNumber; - [GUI] Adjustment _sensitivity; - [GUI] Adjustment _gyroDeadzone; - [GUI] CheckButton _enableMotion; - [GUI] CheckButton _enableCemuHook; - [GUI] CheckButton _mirrorInput; - [GUI] Entry _dsuServerHost; - [GUI] Entry _dsuServerPort; +#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier + [GUI] Adjustment _controllerStrongRumble; + [GUI] Adjustment _controllerWeakRumble; + [GUI] Adjustment _controllerDeadzoneLeft; + [GUI] Adjustment _controllerDeadzoneRight; + [GUI] Adjustment _controllerRangeLeft; + [GUI] Adjustment _controllerRangeRight; + [GUI] Adjustment _controllerTriggerThreshold; + [GUI] Adjustment _slotNumber; + [GUI] Adjustment _altSlotNumber; + [GUI] Adjustment _sensitivity; + [GUI] Adjustment _gyroDeadzone; + [GUI] CheckButton _enableMotion; + [GUI] CheckButton _enableCemuHook; + [GUI] CheckButton _mirrorInput; + [GUI] Entry _dsuServerHost; + [GUI] Entry _dsuServerPort; [GUI] ComboBoxText _inputDevice; [GUI] ComboBoxText _profile; - [GUI] Box _settingsBox; - [GUI] Box _motionAltBox; - [GUI] Box _motionBox; - [GUI] Box _dsuServerHostBox; - [GUI] Box _dsuServerPortBox; - [GUI] Box _motionControllerSlot; - [GUI] Grid _leftStickKeyboard; - [GUI] Grid _leftStickController; - [GUI] Box _deadZoneLeftBox; - [GUI] Box _rangeLeftBox; - [GUI] Grid _rightStickKeyboard; - [GUI] Grid _rightStickController; - [GUI] Box _deadZoneRightBox; - [GUI] Box _rangeRightBox; - [GUI] Grid _leftSideTriggerBox; - [GUI] Grid _rightSideTriggerBox; - [GUI] Box _triggerThresholdBox; + [GUI] Box _settingsBox; + [GUI] Box _motionAltBox; + [GUI] Box _motionBox; + [GUI] Box _dsuServerHostBox; + [GUI] Box _dsuServerPortBox; + [GUI] Box _motionControllerSlot; + [GUI] Grid _leftStickKeyboard; + [GUI] Grid _leftStickController; + [GUI] Box _deadZoneLeftBox; + [GUI] Box _rangeLeftBox; + [GUI] Grid _rightStickKeyboard; + [GUI] Grid _rightStickController; + [GUI] Box _deadZoneRightBox; + [GUI] Box _rangeRightBox; + [GUI] Grid _leftSideTriggerBox; + [GUI] Grid _rightSideTriggerBox; + [GUI] Box _triggerThresholdBox; [GUI] ComboBoxText _controllerType; [GUI] ToggleButton _lStick; - [GUI] CheckButton _invertLStickX; - [GUI] CheckButton _invertLStickY; - [GUI] CheckButton _rotateL90CW; + [GUI] CheckButton _invertLStickX; + [GUI] CheckButton _invertLStickY; + [GUI] CheckButton _rotateL90CW; [GUI] ToggleButton _lStickUp; [GUI] ToggleButton _lStickDown; [GUI] ToggleButton _lStickLeft; @@ -85,9 +85,9 @@ namespace Ryujinx.Ui.Windows [GUI] ToggleButton _l; [GUI] ToggleButton _zL; [GUI] ToggleButton _rStick; - [GUI] CheckButton _invertRStickX; - [GUI] CheckButton _invertRStickY; - [GUI] CheckButton _rotateR90CW; + [GUI] CheckButton _invertRStickX; + [GUI] CheckButton _invertRStickY; + [GUI] CheckButton _rotateR90Cw; [GUI] ToggleButton _rStickUp; [GUI] ToggleButton _rStickDown; [GUI] ToggleButton _rStickLeft; @@ -104,18 +104,18 @@ namespace Ryujinx.Ui.Windows [GUI] ToggleButton _lSr; [GUI] ToggleButton _rSl; [GUI] ToggleButton _rSr; - [GUI] Image _controllerImage; - [GUI] CheckButton _enableRumble; - [GUI] Box _rumbleBox; + [GUI] Image _controllerImage; + [GUI] CheckButton _enableRumble; + [GUI] Box _rumbleBox; #pragma warning restore CS0649, IDE0044 - private MainWindow _mainWindow; - private IGamepadDriver _gtk3KeyboardDriver; + private readonly MainWindow _mainWindow; + private readonly IGamepadDriver _gtk3KeyboardDriver; private IGamepad _selectedGamepad; private bool _mousePressed; private bool _middleMousePressed; - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { } @@ -152,36 +152,36 @@ namespace Ryujinx.Ui.Windows _controllerType.Active = 0; // Set initial value to first in list. // Bind Events. - _lStick.Clicked += ButtonForStick_Pressed; - _lStickUp.Clicked += Button_Pressed; - _lStickDown.Clicked += Button_Pressed; - _lStickLeft.Clicked += Button_Pressed; - _lStickRight.Clicked += Button_Pressed; - _lStickButton.Clicked += Button_Pressed; - _dpadUp.Clicked += Button_Pressed; - _dpadDown.Clicked += Button_Pressed; - _dpadLeft.Clicked += Button_Pressed; - _dpadRight.Clicked += Button_Pressed; - _minus.Clicked += Button_Pressed; - _l.Clicked += Button_Pressed; - _zL.Clicked += Button_Pressed; - _lSl.Clicked += Button_Pressed; - _lSr.Clicked += Button_Pressed; - _rStick.Clicked += ButtonForStick_Pressed; - _rStickUp.Clicked += Button_Pressed; - _rStickDown.Clicked += Button_Pressed; - _rStickLeft.Clicked += Button_Pressed; - _rStickRight.Clicked += Button_Pressed; - _rStickButton.Clicked += Button_Pressed; - _a.Clicked += Button_Pressed; - _b.Clicked += Button_Pressed; - _x.Clicked += Button_Pressed; - _y.Clicked += Button_Pressed; - _plus.Clicked += Button_Pressed; - _r.Clicked += Button_Pressed; - _zR.Clicked += Button_Pressed; - _rSl.Clicked += Button_Pressed; - _rSr.Clicked += Button_Pressed; + _lStick.Clicked += ButtonForStick_Pressed; + _lStickUp.Clicked += Button_Pressed; + _lStickDown.Clicked += Button_Pressed; + _lStickLeft.Clicked += Button_Pressed; + _lStickRight.Clicked += Button_Pressed; + _lStickButton.Clicked += Button_Pressed; + _dpadUp.Clicked += Button_Pressed; + _dpadDown.Clicked += Button_Pressed; + _dpadLeft.Clicked += Button_Pressed; + _dpadRight.Clicked += Button_Pressed; + _minus.Clicked += Button_Pressed; + _l.Clicked += Button_Pressed; + _zL.Clicked += Button_Pressed; + _lSl.Clicked += Button_Pressed; + _lSr.Clicked += Button_Pressed; + _rStick.Clicked += ButtonForStick_Pressed; + _rStickUp.Clicked += Button_Pressed; + _rStickDown.Clicked += Button_Pressed; + _rStickLeft.Clicked += Button_Pressed; + _rStickRight.Clicked += Button_Pressed; + _rStickButton.Clicked += Button_Pressed; + _a.Clicked += Button_Pressed; + _b.Clicked += Button_Pressed; + _x.Clicked += Button_Pressed; + _y.Clicked += Button_Pressed; + _plus.Clicked += Button_Pressed; + _r.Clicked += Button_Pressed; + _zR.Clicked += Button_Pressed; + _rSl.Clicked += Button_Pressed; + _rSr.Clicked += Button_Pressed; _enableCemuHook.Clicked += CemuHookCheckButtonPressed; // Setup current values. @@ -197,10 +197,7 @@ namespace Ryujinx.Ui.Windows mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - if (_mainWindow.RendererWidget != null) - { - _mainWindow.RendererWidget.NpadManager.BlockInputUpdates(); - } + _mainWindow.RendererWidget?.NpadManager.BlockInputUpdates(); } private void CemuHookCheckButtonPressed(object sender, EventArgs e) @@ -229,10 +226,7 @@ namespace Ryujinx.Ui.Windows _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; - if (_mainWindow.RendererWidget != null) - { - _mainWindow.RendererWidget.NpadManager.UnblockInputUpdates(); - } + _mainWindow.RendererWidget?.NpadManager.UnblockInputUpdates(); _selectedGamepad?.Dispose(); @@ -384,62 +378,62 @@ namespace Ryujinx.Ui.Windows _controllerImage.Pixbuf = _controllerType.ActiveId switch { "ProController" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_ProCon.svg", 400, 400), - "JoyconLeft" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConLeft.svg", 400, 500), - "JoyconRight" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConRight.svg", 400, 500), - _ => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConPair.svg", 400, 500), + "JoyconLeft" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConLeft.svg", 400, 500), + "JoyconRight" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConRight.svg", 400, 500), + _ => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConPair.svg", 400, 500), }; } } private void ClearValues() { - _lStick.Label = "Unbound"; - _lStickUp.Label = "Unbound"; - _lStickDown.Label = "Unbound"; - _lStickLeft.Label = "Unbound"; - _lStickRight.Label = "Unbound"; - _lStickButton.Label = "Unbound"; - _dpadUp.Label = "Unbound"; - _dpadDown.Label = "Unbound"; - _dpadLeft.Label = "Unbound"; - _dpadRight.Label = "Unbound"; - _minus.Label = "Unbound"; - _l.Label = "Unbound"; - _zL.Label = "Unbound"; - _lSl.Label = "Unbound"; - _lSr.Label = "Unbound"; - _rStick.Label = "Unbound"; - _rStickUp.Label = "Unbound"; - _rStickDown.Label = "Unbound"; - _rStickLeft.Label = "Unbound"; - _rStickRight.Label = "Unbound"; - _rStickButton.Label = "Unbound"; - _a.Label = "Unbound"; - _b.Label = "Unbound"; - _x.Label = "Unbound"; - _y.Label = "Unbound"; - _plus.Label = "Unbound"; - _r.Label = "Unbound"; - _zR.Label = "Unbound"; - _rSl.Label = "Unbound"; - _rSr.Label = "Unbound"; - _controllerStrongRumble.Value = 1; - _controllerWeakRumble.Value = 1; - _controllerDeadzoneLeft.Value = 0; - _controllerDeadzoneRight.Value = 0; - _controllerRangeLeft.Value = 1; - _controllerRangeRight.Value = 1; + _lStick.Label = "Unbound"; + _lStickUp.Label = "Unbound"; + _lStickDown.Label = "Unbound"; + _lStickLeft.Label = "Unbound"; + _lStickRight.Label = "Unbound"; + _lStickButton.Label = "Unbound"; + _dpadUp.Label = "Unbound"; + _dpadDown.Label = "Unbound"; + _dpadLeft.Label = "Unbound"; + _dpadRight.Label = "Unbound"; + _minus.Label = "Unbound"; + _l.Label = "Unbound"; + _zL.Label = "Unbound"; + _lSl.Label = "Unbound"; + _lSr.Label = "Unbound"; + _rStick.Label = "Unbound"; + _rStickUp.Label = "Unbound"; + _rStickDown.Label = "Unbound"; + _rStickLeft.Label = "Unbound"; + _rStickRight.Label = "Unbound"; + _rStickButton.Label = "Unbound"; + _a.Label = "Unbound"; + _b.Label = "Unbound"; + _x.Label = "Unbound"; + _y.Label = "Unbound"; + _plus.Label = "Unbound"; + _r.Label = "Unbound"; + _zR.Label = "Unbound"; + _rSl.Label = "Unbound"; + _rSr.Label = "Unbound"; + _controllerStrongRumble.Value = 1; + _controllerWeakRumble.Value = 1; + _controllerDeadzoneLeft.Value = 0; + _controllerDeadzoneRight.Value = 0; + _controllerRangeLeft.Value = 1; + _controllerRangeRight.Value = 1; _controllerTriggerThreshold.Value = 0; - _mirrorInput.Active = false; - _enableMotion.Active = false; - _enableCemuHook.Active = false; - _slotNumber.Value = 0; - _altSlotNumber.Value = 0; - _sensitivity.Value = 100; - _gyroDeadzone.Value = 1; - _dsuServerHost.Buffer.Text = ""; - _dsuServerPort.Buffer.Text = ""; - _enableRumble.Active = false; + _mirrorInput.Active = false; + _enableMotion.Active = false; + _enableCemuHook.Active = false; + _slotNumber.Value = 0; + _altSlotNumber.Value = 0; + _sensitivity.Value = 100; + _gyroDeadzone.Value = 1; + _dsuServerHost.Buffer.Text = ""; + _dsuServerPort.Buffer.Text = ""; + _enableRumble.Active = false; } private void SetValues(InputConfig config) @@ -454,34 +448,34 @@ namespace Ryujinx.Ui.Windows : ControllerType.ProController.ToString()); } - _lStickUp.Label = keyboardConfig.LeftJoyconStick.StickUp.ToString(); - _lStickDown.Label = keyboardConfig.LeftJoyconStick.StickDown.ToString(); - _lStickLeft.Label = keyboardConfig.LeftJoyconStick.StickLeft.ToString(); - _lStickRight.Label = keyboardConfig.LeftJoyconStick.StickRight.ToString(); - _lStickButton.Label = keyboardConfig.LeftJoyconStick.StickButton.ToString(); - _dpadUp.Label = keyboardConfig.LeftJoycon.DpadUp.ToString(); - _dpadDown.Label = keyboardConfig.LeftJoycon.DpadDown.ToString(); - _dpadLeft.Label = keyboardConfig.LeftJoycon.DpadLeft.ToString(); - _dpadRight.Label = keyboardConfig.LeftJoycon.DpadRight.ToString(); - _minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString(); - _l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString(); - _zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString(); - _lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString(); - _lSr.Label = keyboardConfig.LeftJoycon.ButtonSr.ToString(); - _rStickUp.Label = keyboardConfig.RightJoyconStick.StickUp.ToString(); - _rStickDown.Label = keyboardConfig.RightJoyconStick.StickDown.ToString(); - _rStickLeft.Label = keyboardConfig.RightJoyconStick.StickLeft.ToString(); - _rStickRight.Label = keyboardConfig.RightJoyconStick.StickRight.ToString(); - _rStickButton.Label = keyboardConfig.RightJoyconStick.StickButton.ToString(); - _a.Label = keyboardConfig.RightJoycon.ButtonA.ToString(); - _b.Label = keyboardConfig.RightJoycon.ButtonB.ToString(); - _x.Label = keyboardConfig.RightJoycon.ButtonX.ToString(); - _y.Label = keyboardConfig.RightJoycon.ButtonY.ToString(); - _plus.Label = keyboardConfig.RightJoycon.ButtonPlus.ToString(); - _r.Label = keyboardConfig.RightJoycon.ButtonR.ToString(); - _zR.Label = keyboardConfig.RightJoycon.ButtonZr.ToString(); - _rSl.Label = keyboardConfig.RightJoycon.ButtonSl.ToString(); - _rSr.Label = keyboardConfig.RightJoycon.ButtonSr.ToString(); + _lStickUp.Label = keyboardConfig.LeftJoyconStick.StickUp.ToString(); + _lStickDown.Label = keyboardConfig.LeftJoyconStick.StickDown.ToString(); + _lStickLeft.Label = keyboardConfig.LeftJoyconStick.StickLeft.ToString(); + _lStickRight.Label = keyboardConfig.LeftJoyconStick.StickRight.ToString(); + _lStickButton.Label = keyboardConfig.LeftJoyconStick.StickButton.ToString(); + _dpadUp.Label = keyboardConfig.LeftJoycon.DpadUp.ToString(); + _dpadDown.Label = keyboardConfig.LeftJoycon.DpadDown.ToString(); + _dpadLeft.Label = keyboardConfig.LeftJoycon.DpadLeft.ToString(); + _dpadRight.Label = keyboardConfig.LeftJoycon.DpadRight.ToString(); + _minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString(); + _l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString(); + _zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString(); + _lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString(); + _lSr.Label = keyboardConfig.LeftJoycon.ButtonSr.ToString(); + _rStickUp.Label = keyboardConfig.RightJoyconStick.StickUp.ToString(); + _rStickDown.Label = keyboardConfig.RightJoyconStick.StickDown.ToString(); + _rStickLeft.Label = keyboardConfig.RightJoyconStick.StickLeft.ToString(); + _rStickRight.Label = keyboardConfig.RightJoyconStick.StickRight.ToString(); + _rStickButton.Label = keyboardConfig.RightJoyconStick.StickButton.ToString(); + _a.Label = keyboardConfig.RightJoycon.ButtonA.ToString(); + _b.Label = keyboardConfig.RightJoycon.ButtonB.ToString(); + _x.Label = keyboardConfig.RightJoycon.ButtonX.ToString(); + _y.Label = keyboardConfig.RightJoycon.ButtonY.ToString(); + _plus.Label = keyboardConfig.RightJoycon.ButtonPlus.ToString(); + _r.Label = keyboardConfig.RightJoycon.ButtonR.ToString(); + _zR.Label = keyboardConfig.RightJoycon.ButtonZr.ToString(); + _rSl.Label = keyboardConfig.RightJoycon.ButtonSl.ToString(); + _rSr.Label = keyboardConfig.RightJoycon.ButtonSr.ToString(); break; case StandardControllerInputConfig controllerConfig: @@ -492,63 +486,63 @@ namespace Ryujinx.Ui.Windows : ControllerType.ProController.ToString()); } - _lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString(); - _invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX; - _invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY; - _rotateL90CW.Active = controllerConfig.LeftJoyconStick.Rotate90CW; - _lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString(); - _dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString(); - _dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString(); - _dpadLeft.Label = controllerConfig.LeftJoycon.DpadLeft.ToString(); - _dpadRight.Label = controllerConfig.LeftJoycon.DpadRight.ToString(); - _minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString(); - _l.Label = controllerConfig.LeftJoycon.ButtonL.ToString(); - _zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString(); - _lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString(); - _lSr.Label = controllerConfig.LeftJoycon.ButtonSr.ToString(); - _rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString(); - _invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX; - _invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY; - _rotateR90CW.Active = controllerConfig.RightJoyconStick.Rotate90CW; - _rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString(); - _a.Label = controllerConfig.RightJoycon.ButtonA.ToString(); - _b.Label = controllerConfig.RightJoycon.ButtonB.ToString(); - _x.Label = controllerConfig.RightJoycon.ButtonX.ToString(); - _y.Label = controllerConfig.RightJoycon.ButtonY.ToString(); - _plus.Label = controllerConfig.RightJoycon.ButtonPlus.ToString(); - _r.Label = controllerConfig.RightJoycon.ButtonR.ToString(); - _zR.Label = controllerConfig.RightJoycon.ButtonZr.ToString(); - _rSl.Label = controllerConfig.RightJoycon.ButtonSl.ToString(); - _rSr.Label = controllerConfig.RightJoycon.ButtonSr.ToString(); - _controllerStrongRumble.Value = controllerConfig.Rumble.StrongRumble; - _controllerWeakRumble.Value = controllerConfig.Rumble.WeakRumble; - _enableRumble.Active = controllerConfig.Rumble.EnableRumble; - _controllerDeadzoneLeft.Value = controllerConfig.DeadzoneLeft; - _controllerDeadzoneRight.Value = controllerConfig.DeadzoneRight; - _controllerRangeLeft.Value = controllerConfig.RangeLeft; - _controllerRangeRight.Value = controllerConfig.RangeRight; + _lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString(); + _invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX; + _invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY; + _rotateL90CW.Active = controllerConfig.LeftJoyconStick.Rotate90CW; + _lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString(); + _dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString(); + _dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString(); + _dpadLeft.Label = controllerConfig.LeftJoycon.DpadLeft.ToString(); + _dpadRight.Label = controllerConfig.LeftJoycon.DpadRight.ToString(); + _minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString(); + _l.Label = controllerConfig.LeftJoycon.ButtonL.ToString(); + _zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString(); + _lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString(); + _lSr.Label = controllerConfig.LeftJoycon.ButtonSr.ToString(); + _rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString(); + _invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX; + _invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY; + _rotateR90Cw.Active = controllerConfig.RightJoyconStick.Rotate90CW; + _rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString(); + _a.Label = controllerConfig.RightJoycon.ButtonA.ToString(); + _b.Label = controllerConfig.RightJoycon.ButtonB.ToString(); + _x.Label = controllerConfig.RightJoycon.ButtonX.ToString(); + _y.Label = controllerConfig.RightJoycon.ButtonY.ToString(); + _plus.Label = controllerConfig.RightJoycon.ButtonPlus.ToString(); + _r.Label = controllerConfig.RightJoycon.ButtonR.ToString(); + _zR.Label = controllerConfig.RightJoycon.ButtonZr.ToString(); + _rSl.Label = controllerConfig.RightJoycon.ButtonSl.ToString(); + _rSr.Label = controllerConfig.RightJoycon.ButtonSr.ToString(); + _controllerStrongRumble.Value = controllerConfig.Rumble.StrongRumble; + _controllerWeakRumble.Value = controllerConfig.Rumble.WeakRumble; + _enableRumble.Active = controllerConfig.Rumble.EnableRumble; + _controllerDeadzoneLeft.Value = controllerConfig.DeadzoneLeft; + _controllerDeadzoneRight.Value = controllerConfig.DeadzoneRight; + _controllerRangeLeft.Value = controllerConfig.RangeLeft; + _controllerRangeRight.Value = controllerConfig.RangeRight; _controllerTriggerThreshold.Value = controllerConfig.TriggerThreshold; - _sensitivity.Value = controllerConfig.Motion.Sensitivity; - _gyroDeadzone.Value = controllerConfig.Motion.GyroDeadzone; - _enableMotion.Active = controllerConfig.Motion.EnableMotion; - _enableCemuHook.Active = controllerConfig.Motion.MotionBackend == MotionInputBackendType.CemuHook; + _sensitivity.Value = controllerConfig.Motion.Sensitivity; + _gyroDeadzone.Value = controllerConfig.Motion.GyroDeadzone; + _enableMotion.Active = controllerConfig.Motion.EnableMotion; + _enableCemuHook.Active = controllerConfig.Motion.MotionBackend == MotionInputBackendType.CemuHook; // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. if (_controllerRangeLeft.Value <= 0.0 && _controllerRangeRight.Value <= 0.0) { - _controllerRangeLeft.Value = 1.0; + _controllerRangeLeft.Value = 1.0; _controllerRangeRight.Value = 1.0; - + Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); } if (controllerConfig.Motion is CemuHookMotionConfigController cemuHookMotionConfig) { - _slotNumber.Value = cemuHookMotionConfig.Slot; - _altSlotNumber.Value = cemuHookMotionConfig.AltSlot; - _mirrorInput.Active = cemuHookMotionConfig.MirrorInput; - _dsuServerHost.Buffer.Text = cemuHookMotionConfig.DsuServerHost; - _dsuServerPort.Buffer.Text = cemuHookMotionConfig.DsuServerPort.ToString(); + _slotNumber.Value = cemuHookMotionConfig.Slot; + _altSlotNumber.Value = cemuHookMotionConfig.AltSlot; + _mirrorInput.Active = cemuHookMotionConfig.MirrorInput; + _dsuServerHost.Buffer.Text = cemuHookMotionConfig.DsuServerHost; + _dsuServerPort.Buffer.Text = cemuHookMotionConfig.DsuServerPort.ToString(); } break; @@ -559,6 +553,7 @@ namespace Ryujinx.Ui.Windows { if (_inputDevice.ActiveId.StartsWith("keyboard")) { +#pragma warning disable CA1806, IDE0055 // Disable formatting Enum.TryParse(_lStickUp.Label, out Key lStickUp); Enum.TryParse(_lStickDown.Label, out Key lStickDown); Enum.TryParse(_lStickLeft.Label, out Key lStickLeft); @@ -588,60 +583,62 @@ namespace Ryujinx.Ui.Windows Enum.TryParse(_zR.Label, out Key rButtonZr); Enum.TryParse(_rSl.Label, out Key rButtonSl); Enum.TryParse(_rSr.Label, out Key rButtonSr); +#pragma warning restore CA1806, IDE0055 return new StandardKeyboardInputConfig { - Backend = InputBackendType.WindowKeyboard, - Version = InputConfig.CurrentVersion, - Id = _inputDevice.ActiveId.Split("/")[1], - ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId), - PlayerIndex = _playerIndex, - LeftJoycon = new LeftJoyconCommonConfig<Key> + Backend = InputBackendType.WindowKeyboard, + Version = InputConfig.CurrentVersion, + Id = _inputDevice.ActiveId.Split("/")[1], + ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId), + PlayerIndex = _playerIndex, + LeftJoycon = new LeftJoyconCommonConfig<Key> { - ButtonMinus = lButtonMinus, - ButtonL = lButtonL, - ButtonZl = lButtonZl, - ButtonSl = lButtonSl, - ButtonSr = lButtonSr, - DpadUp = lDPadUp, - DpadDown = lDPadDown, - DpadLeft = lDPadLeft, - DpadRight = lDPadRight + ButtonMinus = lButtonMinus, + ButtonL = lButtonL, + ButtonZl = lButtonZl, + ButtonSl = lButtonSl, + ButtonSr = lButtonSr, + DpadUp = lDPadUp, + DpadDown = lDPadDown, + DpadLeft = lDPadLeft, + DpadRight = lDPadRight, }, LeftJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = lStickUp, - StickDown = lStickDown, - StickLeft = lStickLeft, - StickRight = lStickRight, - StickButton = lStickButton, + StickUp = lStickUp, + StickDown = lStickDown, + StickLeft = lStickLeft, + StickRight = lStickRight, + StickButton = lStickButton, }, - RightJoycon = new RightJoyconCommonConfig<Key> + RightJoycon = new RightJoyconCommonConfig<Key> { - ButtonA = rButtonA, - ButtonB = rButtonB, - ButtonX = rButtonX, - ButtonY = rButtonY, - ButtonPlus = rButtonPlus, - ButtonR = rButtonR, - ButtonZr = rButtonZr, - ButtonSl = rButtonSl, - ButtonSr = rButtonSr + ButtonA = rButtonA, + ButtonB = rButtonB, + ButtonX = rButtonX, + ButtonY = rButtonY, + ButtonPlus = rButtonPlus, + ButtonR = rButtonR, + ButtonZr = rButtonZr, + ButtonSl = rButtonSl, + ButtonSr = rButtonSr, }, RightJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = rStickUp, - StickDown = rStickDown, - StickLeft = rStickLeft, - StickRight = rStickRight, - StickButton = rStickButton, + StickUp = rStickUp, + StickDown = rStickDown, + StickLeft = rStickLeft, + StickRight = rStickRight, + StickButton = rStickButton, }, }; } - + if (_inputDevice.ActiveId.StartsWith("controller")) { - Enum.TryParse(_lStick.Label, out ConfigStickInputId lStick); +#pragma warning disable CA1806, IDE0055 // Disable formatting + Enum.TryParse(_lStick.Label, out ConfigStickInputId lStick); Enum.TryParse(_lStickButton.Label, out ConfigGamepadInputId lStickButton); Enum.TryParse(_minus.Label, out ConfigGamepadInputId lButtonMinus); Enum.TryParse(_l.Label, out ConfigGamepadInputId lButtonL); @@ -653,7 +650,7 @@ namespace Ryujinx.Ui.Windows Enum.TryParse(_dpadLeft.Label, out ConfigGamepadInputId lDPadLeft); Enum.TryParse(_dpadRight.Label, out ConfigGamepadInputId lDPadRight); - Enum.TryParse(_rStick.Label, out ConfigStickInputId rStick); + Enum.TryParse(_rStick.Label, out ConfigStickInputId rStick); Enum.TryParse(_rStickButton.Label, out ConfigGamepadInputId rStickButton); Enum.TryParse(_a.Label, out ConfigGamepadInputId rButtonA); Enum.TryParse(_b.Label, out ConfigGamepadInputId rButtonB); @@ -666,94 +663,95 @@ namespace Ryujinx.Ui.Windows Enum.TryParse(_rSr.Label, out ConfigGamepadInputId rButtonSr); int.TryParse(_dsuServerPort.Buffer.Text, out int port); +#pragma warning restore CA1806, IDE0055 MotionConfigController motionConfig; if (_enableCemuHook.Active) { - motionConfig = new CemuHookMotionConfigController + motionConfig = new CemuHookMotionConfigController { MotionBackend = MotionInputBackendType.CemuHook, - EnableMotion = _enableMotion.Active, - Sensitivity = (int)_sensitivity.Value, - GyroDeadzone = _gyroDeadzone.Value, - MirrorInput = _mirrorInput.Active, - Slot = (int)_slotNumber.Value, - AltSlot = (int)_altSlotNumber.Value, + EnableMotion = _enableMotion.Active, + Sensitivity = (int)_sensitivity.Value, + GyroDeadzone = _gyroDeadzone.Value, + MirrorInput = _mirrorInput.Active, + Slot = (int)_slotNumber.Value, + AltSlot = (int)_altSlotNumber.Value, DsuServerHost = _dsuServerHost.Buffer.Text, - DsuServerPort = port + DsuServerPort = port, }; } else { - motionConfig = new StandardMotionConfigController + motionConfig = new StandardMotionConfigController { MotionBackend = MotionInputBackendType.GamepadDriver, - EnableMotion = _enableMotion.Active, - Sensitivity = (int)_sensitivity.Value, - GyroDeadzone = _gyroDeadzone.Value, + EnableMotion = _enableMotion.Active, + Sensitivity = (int)_sensitivity.Value, + GyroDeadzone = _gyroDeadzone.Value, }; } return new StandardControllerInputConfig { - Backend = InputBackendType.GamepadSDL2, - Version = InputConfig.CurrentVersion, - Id = _inputDevice.ActiveId.Split("/")[1].Split(" ")[0], - ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId), - PlayerIndex = _playerIndex, - DeadzoneLeft = (float)_controllerDeadzoneLeft.Value, - DeadzoneRight = (float)_controllerDeadzoneRight.Value, - RangeLeft = (float)_controllerRangeLeft.Value, - RangeRight = (float)_controllerRangeRight.Value, + Backend = InputBackendType.GamepadSDL2, + Version = InputConfig.CurrentVersion, + Id = _inputDevice.ActiveId.Split("/")[1].Split(" ")[0], + ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId), + PlayerIndex = _playerIndex, + DeadzoneLeft = (float)_controllerDeadzoneLeft.Value, + DeadzoneRight = (float)_controllerDeadzoneRight.Value, + RangeLeft = (float)_controllerRangeLeft.Value, + RangeRight = (float)_controllerRangeRight.Value, TriggerThreshold = (float)_controllerTriggerThreshold.Value, - LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId> + LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId> { - ButtonMinus = lButtonMinus, - ButtonL = lButtonL, - ButtonZl = lButtonZl, - ButtonSl = lButtonSl, - ButtonSr = lButtonSr, - DpadUp = lDPadUp, - DpadDown = lDPadDown, - DpadLeft = lDPadLeft, - DpadRight = lDPadRight + ButtonMinus = lButtonMinus, + ButtonL = lButtonL, + ButtonZl = lButtonZl, + ButtonSl = lButtonSl, + ButtonSr = lButtonSr, + DpadUp = lDPadUp, + DpadDown = lDPadDown, + DpadLeft = lDPadLeft, + DpadRight = lDPadRight, }, LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { InvertStickX = _invertLStickX.Active, - Joystick = lStick, + Joystick = lStick, InvertStickY = _invertLStickY.Active, - StickButton = lStickButton, - Rotate90CW = _rotateL90CW.Active, + StickButton = lStickButton, + Rotate90CW = _rotateL90CW.Active, }, - RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> + RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = rButtonA, - ButtonB = rButtonB, - ButtonX = rButtonX, - ButtonY = rButtonY, - ButtonPlus = rButtonPlus, - ButtonR = rButtonR, - ButtonZr = rButtonZr, - ButtonSl = rButtonSl, - ButtonSr = rButtonSr + ButtonA = rButtonA, + ButtonB = rButtonB, + ButtonX = rButtonX, + ButtonY = rButtonY, + ButtonPlus = rButtonPlus, + ButtonR = rButtonR, + ButtonZr = rButtonZr, + ButtonSl = rButtonSl, + ButtonSr = rButtonSr, }, RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { InvertStickX = _invertRStickX.Active, - Joystick = rStick, + Joystick = rStick, InvertStickY = _invertRStickY.Active, - StickButton = rStickButton, - Rotate90CW = _rotateR90CW.Active, + StickButton = rStickButton, + Rotate90CW = _rotateR90Cw.Active, }, - Motion = motionConfig, - Rumble = new RumbleConfigController + Motion = motionConfig, + Rumble = new RumbleConfigController { StrongRumble = (float)_controllerStrongRumble.Value, - WeakRumble = (float)_controllerWeakRumble.Value, - EnableRumble = _enableRumble.Active - } + WeakRumble = (float)_controllerWeakRumble.Value, + EnableRumble = _enableRumble.Active, + }, }; } @@ -856,7 +854,7 @@ namespace Ryujinx.Ui.Windows { throw new Exception("Controller not supported"); } - + return assigner; } @@ -880,7 +878,7 @@ namespace Ryujinx.Ui.Windows // Open GTK3 keyboard for cancel operations IKeyboard keyboard = (IKeyboard)_gtk3KeyboardDriver.GetGamepad("0"); - Thread inputThread = new Thread(() => + Thread inputThread = new(() => { assigner.Initialize(); @@ -916,10 +914,11 @@ namespace Ryujinx.Ui.Windows button.Active = false; _isWaitingForInput = false; }); - }); - - inputThread.Name = "GUI.InputThread"; - inputThread.IsBackground = true; + }) + { + Name = "GUI.InputThread", + IsBackground = true, + }; inputThread.Start(); } @@ -950,7 +949,7 @@ namespace Ryujinx.Ui.Windows Directory.CreateDirectory(basePath); } - if (_inputDevice.ActiveId == null|| _inputDevice.ActiveId.Equals("disabled")) + if (_inputDevice.ActiveId == null || _inputDevice.ActiveId.Equals("disabled")) { _profile.Append("default", "None"); } @@ -971,10 +970,13 @@ namespace Ryujinx.Ui.Windows { ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) return; + if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) + { + return; + } InputConfig config = null; - int pos = _profile.Active; + int pos = _profile.Active; if (_profile.ActiveId == "default") { @@ -982,53 +984,53 @@ namespace Ryujinx.Ui.Windows { config = new StandardKeyboardInputConfig { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = null, - ControllerType = ControllerType.ProController, - LeftJoycon = new LeftJoyconCommonConfig<Key> + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.WindowKeyboard, + Id = null, + ControllerType = ControllerType.ProController, + LeftJoycon = new LeftJoyconCommonConfig<Key> { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound + DpadUp = Key.Up, + DpadDown = Key.Down, + DpadLeft = Key.Left, + DpadRight = Key.Right, + ButtonMinus = Key.Minus, + ButtonL = Key.E, + ButtonZl = Key.Q, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, }, - LeftJoyconStick = new JoyconConfigKeyboardStick<Key> + LeftJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, + StickUp = Key.W, + StickDown = Key.S, + StickLeft = Key.A, + StickRight = Key.D, + StickButton = Key.F, }, - RightJoycon = new RightJoyconCommonConfig<Key> + RightJoycon = new RightJoyconCommonConfig<Key> { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound + ButtonA = Key.Z, + ButtonB = Key.X, + ButtonX = Key.C, + ButtonY = Key.V, + ButtonPlus = Key.Plus, + ButtonR = Key.U, + ButtonZr = Key.O, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - } + StickUp = Key.I, + StickDown = Key.K, + StickLeft = Key.J, + StickRight = Key.L, + StickButton = Key.H, + }, }; } else if (_inputDevice.ActiveId.StartsWith("controller")) @@ -1037,72 +1039,72 @@ namespace Ryujinx.Ui.Windows config = new StandardControllerInputConfig { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.GamepadSDL2, - Id = null, - ControllerType = ControllerType.JoyconPair, - DeadzoneLeft = 0.1f, - DeadzoneRight = 0.1f, - RangeLeft = 1.0f, - RangeRight = 1.0f, + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.GamepadSDL2, + Id = null, + ControllerType = ControllerType.JoyconPair, + DeadzoneLeft = 0.1f, + DeadzoneRight = 0.1f, + RangeLeft = 1.0f, + RangeRight = 1.0f, TriggerThreshold = 0.5f, LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId> { - DpadUp = ConfigGamepadInputId.DpadUp, - DpadDown = ConfigGamepadInputId.DpadDown, - DpadLeft = ConfigGamepadInputId.DpadLeft, - DpadRight = ConfigGamepadInputId.DpadRight, - ButtonMinus = ConfigGamepadInputId.Minus, - ButtonL = ConfigGamepadInputId.LeftShoulder, - ButtonZl = ConfigGamepadInputId.LeftTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, + DpadUp = ConfigGamepadInputId.DpadUp, + DpadDown = ConfigGamepadInputId.DpadDown, + DpadLeft = ConfigGamepadInputId.DpadLeft, + DpadRight = ConfigGamepadInputId.DpadRight, + ButtonMinus = ConfigGamepadInputId.Minus, + ButtonL = ConfigGamepadInputId.LeftShoulder, + ButtonZl = ConfigGamepadInputId.LeftTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, }, LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { - Joystick = ConfigStickInputId.Left, - StickButton = ConfigGamepadInputId.LeftStick, + Joystick = ConfigStickInputId.Left, + StickButton = ConfigGamepadInputId.LeftStick, InvertStickX = false, InvertStickY = false, - Rotate90CW = false, + Rotate90CW = false, }, RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, - ButtonPlus = ConfigGamepadInputId.Plus, - ButtonR = ConfigGamepadInputId.RightShoulder, - ButtonZr = ConfigGamepadInputId.RightTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonPlus = ConfigGamepadInputId.Plus, + ButtonR = ConfigGamepadInputId.RightShoulder, + ButtonZr = ConfigGamepadInputId.RightTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, }, RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { - Joystick = ConfigStickInputId.Right, - StickButton = ConfigGamepadInputId.RightStick, + Joystick = ConfigStickInputId.Right, + StickButton = ConfigGamepadInputId.RightStick, InvertStickX = false, InvertStickY = false, - Rotate90CW = false, + Rotate90CW = false, }, Motion = new StandardMotionConfigController { MotionBackend = MotionInputBackendType.GamepadDriver, EnableMotion = true, - Sensitivity = 100, + Sensitivity = 100, GyroDeadzone = 1, }, Rumble = new RumbleConfigController { StrongRumble = 1f, - WeakRumble = 1f, - EnableRumble = false - } + WeakRumble = 1f, + EnableRumble = false, + }, }; } } @@ -1122,7 +1124,7 @@ namespace Ryujinx.Ui.Windows try { - config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); + config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); } catch (JsonException) { } } @@ -1134,17 +1136,23 @@ namespace Ryujinx.Ui.Windows { ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - if (_inputDevice.ActiveId == "disabled") return; + if (_inputDevice.ActiveId == "disabled") + { + return; + } - InputConfig inputConfig = GetValues(); - ProfileDialog profileDialog = new ProfileDialog(); + InputConfig inputConfig = GetValues(); + ProfileDialog profileDialog = new(); - if (inputConfig == null) return; + if (inputConfig == null) + { + return; + } if (profileDialog.Run() == (int)ResponseType.Ok) { string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName); - string jsonString = JsonHelper.Serialize(inputConfig, SerializerContext.InputConfig); + string jsonString = JsonHelper.Serialize(inputConfig, _serializerContext.InputConfig); File.WriteAllText(path, jsonString); } @@ -1156,9 +1164,12 @@ namespace Ryujinx.Ui.Windows private void ProfileRemove_Activated(object sender, EventArgs args) { - ((ToggleButton) sender).SetStateFlags(StateFlags.Normal, true); + ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) return; + if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) + { + return; + } MessageDialog confirmDialog = GtkDialog.CreateConfirmationDialog("Deleting Profile", "This action is irreversible, are you sure you want to continue?"); @@ -1200,10 +1211,7 @@ namespace Ryujinx.Ui.Windows } } - if (_mainWindow.RendererWidget != null) - { - _mainWindow.RendererWidget.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); - } + _mainWindow.RendererWidget?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); // Atomically replace and signal input change. // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event. diff --git a/src/Ryujinx/Ui/Windows/DlcWindow.cs b/src/Ryujinx/Ui/Windows/DlcWindow.cs index b22f15932..74aef00f4 100644 --- a/src/Ryujinx/Ui/Windows/DlcWindow.cs +++ b/src/Ryujinx/Ui/Windows/DlcWindow.cs @@ -19,16 +19,16 @@ namespace Ryujinx.Ui.Windows { public class DlcWindow : Window { - private readonly VirtualFileSystem _virtualFileSystem; - private readonly string _titleId; - private readonly string _dlcJsonPath; + private readonly VirtualFileSystem _virtualFileSystem; + private readonly string _titleId; + private readonly string _dlcJsonPath; private readonly List<DownloadableContentContainer> _dlcContainerList; - private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); -#pragma warning disable CS0649, IDE0044 - [GUI] Label _baseTitleInfoLabel; - [GUI] TreeView _dlcTreeView; +#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier + [GUI] Label _baseTitleInfoLabel; + [GUI] TreeView _dlcTreeView; [GUI] TreeSelection _dlcTreeSelection; #pragma warning restore CS0649, IDE0044 @@ -38,23 +38,23 @@ namespace Ryujinx.Ui.Windows { builder.Autoconnect(this); - _titleId = titleId; - _virtualFileSystem = virtualFileSystem; - _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json"); + _titleId = titleId; + _virtualFileSystem = virtualFileSystem; + _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json"); _baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]"; try { - _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, SerializerContext.ListDownloadableContentContainer); + _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, _serializerContext.ListDownloadableContentContainer); } catch { _dlcContainerList = new List<DownloadableContentContainer>(); } - + _dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string)); - CellRendererToggle enableToggle = new CellRendererToggle(); + CellRendererToggle enableToggle = new(); enableToggle.Toggled += (sender, args) => { _dlcTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); @@ -71,9 +71,9 @@ namespace Ryujinx.Ui.Windows } }; - _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); - _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1); - _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); + _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); + _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1); + _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); foreach (DownloadableContentContainer dlcContainer in _dlcContainerList) { @@ -85,8 +85,11 @@ namespace Ryujinx.Ui.Windows // "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca. bool areAllContentPacksEnabled = dlcContainer.DownloadableContentNcaList.TrueForAll((nca) => nca.Enabled); TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.ContainerPath); + using FileStream containerFile = File.OpenRead(dlcContainer.ContainerPath); - PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); + + PartitionFileSystem pfs = new(containerFile.AsStorage()); + _virtualFileSystem.ImportTickets(pfs); foreach (DownloadableContentNca dlcNca in dlcContainer.DownloadableContentNcaList) @@ -126,14 +129,14 @@ namespace Ryujinx.Ui.Windows private void AddButton_Clicked(object sender, EventArgs args) { - FileChooserNative fileChooser = new FileChooserNative("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") + FileChooserNative fileChooser = new("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") { - SelectMultiple = true + SelectMultiple = true, }; - FileFilter filter = new FileFilter() + FileFilter filter = new() { - Name = "Switch Game DLCs" + Name = "Switch Game DLCs", }; filter.AddPattern("*.nsp"); @@ -148,44 +151,46 @@ namespace Ryujinx.Ui.Windows return; } - using (FileStream containerFile = File.OpenRead(containerPath)) + using FileStream containerFile = File.OpenRead(containerPath); + + PartitionFileSystem pfs = new(containerFile.AsStorage()); + bool containsDlc = false; + + _virtualFileSystem.ImportTickets(pfs); + + TreeIter? parentIter = null; + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { - PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); - bool containsDlc = false; + using var ncaFile = new UniqueRef<IFile>(); - _virtualFileSystem.ImportTickets(pfs); + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - TreeIter? parentIter = null; + Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), containerPath); - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + if (nca == null) { - using var ncaFile = new UniqueRef<IFile>(); + continue; + } - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), containerPath); - - if (nca == null) continue; - - if (nca.Header.ContentType == NcaContentType.PublicData) + if (nca.Header.ContentType == NcaContentType.PublicData) + { + if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId) { - if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId) - { - break; - } - - parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath); - - ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); - containsDlc = true; + break; } - } - if (!containsDlc) - { - GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); + parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath); + + ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); + containsDlc = true; } } + + if (!containsDlc) + { + GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); + } } } @@ -206,17 +211,17 @@ namespace Ryujinx.Ui.Windows } } } - + private void RemoveAllButton_Clicked(object sender, EventArgs args) { - List<TreeIter> toRemove = new List<TreeIter>(); + List<TreeIter> toRemove = new(); if (_dlcTreeView.Model.GetIterFirst(out TreeIter iter)) { do { toRemove.Add(iter); - } + } while (_dlcTreeView.Model.IterNext(ref iter)); } @@ -237,19 +242,19 @@ namespace Ryujinx.Ui.Windows { if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, parentIter)) { - DownloadableContentContainer dlcContainer = new DownloadableContentContainer + DownloadableContentContainer dlcContainer = new() { - ContainerPath = (string)_dlcTreeView.Model.GetValue(parentIter, 2), - DownloadableContentNcaList = new List<DownloadableContentNca>() + ContainerPath = (string)_dlcTreeView.Model.GetValue(parentIter, 2), + DownloadableContentNcaList = new List<DownloadableContentNca>(), }; do { dlcContainer.DownloadableContentNcaList.Add(new DownloadableContentNca { - Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0), - TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16), - FullPath = (string)_dlcTreeView.Model.GetValue(childIter, 2) + Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0), + TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16), + FullPath = (string)_dlcTreeView.Model.GetValue(childIter, 2), }); } while (_dlcTreeView.Model.IterNext(ref childIter)); @@ -260,7 +265,7 @@ namespace Ryujinx.Ui.Windows while (_dlcTreeView.Model.IterNext(ref parentIter)); } - JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, SerializerContext.ListDownloadableContentContainer); + JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, _serializerContext.ListDownloadableContentContainer); Dispose(); } diff --git a/src/Ryujinx/Ui/Windows/SettingsWindow.cs b/src/Ryujinx/Ui/Windows/SettingsWindow.cs index fbfbf527c..b9f1a90a3 100644 --- a/src/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/src/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -6,14 +6,12 @@ using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.GraphicsDriver; -using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Helper; using Ryujinx.Ui.Widgets; -using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Globalization; @@ -27,95 +25,95 @@ namespace Ryujinx.Ui.Windows { public class SettingsWindow : Window { - private readonly MainWindow _parent; - private readonly ListStore _gameDirsBoxStore; - private readonly ListStore _audioBackendStore; + private readonly MainWindow _parent; + private readonly ListStore _gameDirsBoxStore; + private readonly ListStore _audioBackendStore; private readonly TimeZoneContentManager _timeZoneContentManager; - private readonly HashSet<string> _validTzRegions; + private readonly HashSet<string> _validTzRegions; - private long _systemTimeOffset; + private long _systemTimeOffset; private float _previousVolumeLevel; private bool _directoryChanged = false; -#pragma warning disable CS0649, IDE0044 - [GUI] CheckButton _traceLogToggle; - [GUI] CheckButton _errorLogToggle; - [GUI] CheckButton _warningLogToggle; - [GUI] CheckButton _infoLogToggle; - [GUI] CheckButton _stubLogToggle; - [GUI] CheckButton _debugLogToggle; - [GUI] CheckButton _fileLogToggle; - [GUI] CheckButton _guestLogToggle; - [GUI] CheckButton _fsAccessLogToggle; - [GUI] Adjustment _fsLogSpinAdjustment; - [GUI] ComboBoxText _graphicsDebugLevel; - [GUI] CheckButton _dockedModeToggle; - [GUI] CheckButton _discordToggle; - [GUI] CheckButton _checkUpdatesToggle; - [GUI] CheckButton _showConfirmExitToggle; - [GUI] RadioButton _hideCursorNever; - [GUI] RadioButton _hideCursorOnIdle; - [GUI] RadioButton _hideCursorAlways; - [GUI] CheckButton _vSyncToggle; - [GUI] CheckButton _shaderCacheToggle; - [GUI] CheckButton _textureRecompressionToggle; - [GUI] CheckButton _macroHLEToggle; - [GUI] CheckButton _ptcToggle; - [GUI] CheckButton _internetToggle; - [GUI] CheckButton _fsicToggle; - [GUI] RadioButton _mmSoftware; - [GUI] RadioButton _mmHost; - [GUI] RadioButton _mmHostUnsafe; - [GUI] CheckButton _expandRamToggle; - [GUI] CheckButton _ignoreToggle; - [GUI] CheckButton _directKeyboardAccess; - [GUI] CheckButton _directMouseAccess; - [GUI] ComboBoxText _systemLanguageSelect; - [GUI] ComboBoxText _systemRegionSelect; - [GUI] Entry _systemTimeZoneEntry; +#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier + [GUI] CheckButton _traceLogToggle; + [GUI] CheckButton _errorLogToggle; + [GUI] CheckButton _warningLogToggle; + [GUI] CheckButton _infoLogToggle; + [GUI] CheckButton _stubLogToggle; + [GUI] CheckButton _debugLogToggle; + [GUI] CheckButton _fileLogToggle; + [GUI] CheckButton _guestLogToggle; + [GUI] CheckButton _fsAccessLogToggle; + [GUI] Adjustment _fsLogSpinAdjustment; + [GUI] ComboBoxText _graphicsDebugLevel; + [GUI] CheckButton _dockedModeToggle; + [GUI] CheckButton _discordToggle; + [GUI] CheckButton _checkUpdatesToggle; + [GUI] CheckButton _showConfirmExitToggle; + [GUI] RadioButton _hideCursorNever; + [GUI] RadioButton _hideCursorOnIdle; + [GUI] RadioButton _hideCursorAlways; + [GUI] CheckButton _vSyncToggle; + [GUI] CheckButton _shaderCacheToggle; + [GUI] CheckButton _textureRecompressionToggle; + [GUI] CheckButton _macroHLEToggle; + [GUI] CheckButton _ptcToggle; + [GUI] CheckButton _internetToggle; + [GUI] CheckButton _fsicToggle; + [GUI] RadioButton _mmSoftware; + [GUI] RadioButton _mmHost; + [GUI] RadioButton _mmHostUnsafe; + [GUI] CheckButton _expandRamToggle; + [GUI] CheckButton _ignoreToggle; + [GUI] CheckButton _directKeyboardAccess; + [GUI] CheckButton _directMouseAccess; + [GUI] ComboBoxText _systemLanguageSelect; + [GUI] ComboBoxText _systemRegionSelect; + [GUI] Entry _systemTimeZoneEntry; [GUI] EntryCompletion _systemTimeZoneCompletion; - [GUI] Box _audioBackendBox; - [GUI] ComboBox _audioBackendSelect; - [GUI] Label _audioVolumeLabel; - [GUI] Scale _audioVolumeSlider; - [GUI] SpinButton _systemTimeYearSpin; - [GUI] SpinButton _systemTimeMonthSpin; - [GUI] SpinButton _systemTimeDaySpin; - [GUI] SpinButton _systemTimeHourSpin; - [GUI] SpinButton _systemTimeMinuteSpin; - [GUI] Adjustment _systemTimeYearSpinAdjustment; - [GUI] Adjustment _systemTimeMonthSpinAdjustment; - [GUI] Adjustment _systemTimeDaySpinAdjustment; - [GUI] Adjustment _systemTimeHourSpinAdjustment; - [GUI] Adjustment _systemTimeMinuteSpinAdjustment; - [GUI] ComboBoxText _multiLanSelect; - [GUI] CheckButton _custThemeToggle; - [GUI] Entry _custThemePath; - [GUI] ToggleButton _browseThemePath; - [GUI] Label _custThemePathLabel; - [GUI] TreeView _gameDirsBox; - [GUI] Entry _addGameDirBox; - [GUI] ComboBoxText _galThreading; - [GUI] Entry _graphicsShadersDumpPath; - [GUI] ComboBoxText _anisotropy; - [GUI] ComboBoxText _aspectRatio; - [GUI] ComboBoxText _antiAliasing; - [GUI] ComboBoxText _scalingFilter; - [GUI] ComboBoxText _graphicsBackend; - [GUI] ComboBoxText _preferredGpu; - [GUI] ComboBoxText _resScaleCombo; - [GUI] Entry _resScaleText; - [GUI] Adjustment _scalingFilterLevel; - [GUI] Scale _scalingFilterSlider; - [GUI] ToggleButton _configureController1; - [GUI] ToggleButton _configureController2; - [GUI] ToggleButton _configureController3; - [GUI] ToggleButton _configureController4; - [GUI] ToggleButton _configureController5; - [GUI] ToggleButton _configureController6; - [GUI] ToggleButton _configureController7; - [GUI] ToggleButton _configureController8; - [GUI] ToggleButton _configureControllerH; + [GUI] Box _audioBackendBox; + [GUI] ComboBox _audioBackendSelect; + [GUI] Label _audioVolumeLabel; + [GUI] Scale _audioVolumeSlider; + [GUI] SpinButton _systemTimeYearSpin; + [GUI] SpinButton _systemTimeMonthSpin; + [GUI] SpinButton _systemTimeDaySpin; + [GUI] SpinButton _systemTimeHourSpin; + [GUI] SpinButton _systemTimeMinuteSpin; + [GUI] Adjustment _systemTimeYearSpinAdjustment; + [GUI] Adjustment _systemTimeMonthSpinAdjustment; + [GUI] Adjustment _systemTimeDaySpinAdjustment; + [GUI] Adjustment _systemTimeHourSpinAdjustment; + [GUI] Adjustment _systemTimeMinuteSpinAdjustment; + [GUI] ComboBoxText _multiLanSelect; + [GUI] CheckButton _custThemeToggle; + [GUI] Entry _custThemePath; + [GUI] ToggleButton _browseThemePath; + [GUI] Label _custThemePathLabel; + [GUI] TreeView _gameDirsBox; + [GUI] Entry _addGameDirBox; + [GUI] ComboBoxText _galThreading; + [GUI] Entry _graphicsShadersDumpPath; + [GUI] ComboBoxText _anisotropy; + [GUI] ComboBoxText _aspectRatio; + [GUI] ComboBoxText _antiAliasing; + [GUI] ComboBoxText _scalingFilter; + [GUI] ComboBoxText _graphicsBackend; + [GUI] ComboBoxText _preferredGpu; + [GUI] ComboBoxText _resScaleCombo; + [GUI] Entry _resScaleText; + [GUI] Adjustment _scalingFilterLevel; + [GUI] Scale _scalingFilterSlider; + [GUI] ToggleButton _configureController1; + [GUI] ToggleButton _configureController2; + [GUI] ToggleButton _configureController3; + [GUI] ToggleButton _configureController4; + [GUI] ToggleButton _configureController5; + [GUI] ToggleButton _configureController6; + [GUI] ToggleButton _configureController7; + [GUI] ToggleButton _configureController8; + [GUI] ToggleButton _configureControllerH; #pragma warning restore CS0649, IDE0044 @@ -316,11 +314,11 @@ namespace Ryujinx.Ui.Windows } // Custom EntryCompletion Columns. If added to glade, need to override more signals - ListStore tzList = new ListStore(typeof(string), typeof(string), typeof(string)); + ListStore tzList = new(typeof(string), typeof(string), typeof(string)); _systemTimeZoneCompletion.Model = tzList; - CellRendererText offsetCol = new CellRendererText(); - CellRendererText abbrevCol = new CellRendererText(); + CellRendererText offsetCol = new(); + CellRendererText abbrevCol = new(); _systemTimeZoneCompletion.PackStart(offsetCol, false); _systemTimeZoneCompletion.AddAttribute(offsetCol, "text", 0); @@ -364,17 +362,17 @@ namespace Ryujinx.Ui.Windows PopulateNetworkInterfaces(); _multiLanSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); - _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath; - _resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString(); - _scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value; - _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; - _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; + _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath; + _resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString(); + _scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value; + _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; + _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath; - _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode; - _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset; + _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode; + _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset; _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0); - _gameDirsBoxStore = new ListStore(typeof(string)); + _gameDirsBoxStore = new ListStore(typeof(string)); _gameDirsBox.Model = _gameDirsBoxStore; foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value) @@ -384,9 +382,9 @@ namespace Ryujinx.Ui.Windows if (_custThemeToggle.Active == false) { - _custThemePath.Sensitive = false; + _custThemePath.Sensitive = false; _custThemePathLabel.Sensitive = false; - _browseThemePath.Sensitive = false; + _browseThemePath.Sensitive = false; } // Setup system time spinners @@ -394,10 +392,10 @@ namespace Ryujinx.Ui.Windows _audioBackendStore = new ListStore(typeof(string), typeof(AudioBackend)); - TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl); + TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl); TreeIter soundIoIter = _audioBackendStore.AppendValues("SoundIO", AudioBackend.SoundIo); - TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2); - TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy); + TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2); + TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy); _audioBackendSelect = ComboBox.NewWithModelAndEntry(_audioBackendStore); _audioBackendSelect.EntryTextColumn = 0; @@ -418,35 +416,35 @@ namespace Ryujinx.Ui.Windows _audioBackendSelect.SetActiveIter(dummyIter); break; default: - throw new ArgumentOutOfRangeException(); + throw new InvalidOperationException($"{nameof(ConfigurationState.Instance.System.AudioBackend)} contains an invalid value: {ConfigurationState.Instance.System.AudioBackend.Value}"); } _audioBackendBox.Add(_audioBackendSelect); _audioBackendSelect.Show(); - _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume; - _audioVolumeLabel = new Label("Volume: "); - _audioVolumeSlider = new Scale(Orientation.Horizontal, 0, 100, 1); - _audioVolumeLabel.MarginStart = 10; - _audioVolumeSlider.ValuePos = PositionType.Right; + _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume; + _audioVolumeLabel = new Label("Volume: "); + _audioVolumeSlider = new Scale(Orientation.Horizontal, 0, 100, 1); + _audioVolumeLabel.MarginStart = 10; + _audioVolumeSlider.ValuePos = PositionType.Right; _audioVolumeSlider.WidthRequest = 200; - _audioVolumeSlider.Value = _previousVolumeLevel * 100; + _audioVolumeSlider.Value = _previousVolumeLevel * 100; _audioVolumeSlider.ValueChanged += VolumeSlider_OnChange; _audioBackendBox.Add(_audioVolumeLabel); _audioBackendBox.Add(_audioVolumeSlider); _audioVolumeLabel.Show(); _audioVolumeSlider.Show(); - bool openAlIsSupported = false; + bool openAlIsSupported = false; bool soundIoIsSupported = false; - bool sdl2IsSupported = false; + bool sdl2IsSupported = false; Task.Run(() => { - openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported; + openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported; soundIoIsSupported = !OperatingSystem.IsMacOS() && SoundIoHardwareDeviceDriver.IsSupported; - sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported; + sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported; }); // This function runs whenever the dropdown is opened @@ -454,18 +452,18 @@ namespace Ryujinx.Ui.Windows { cell.Sensitive = ((AudioBackend)_audioBackendStore.GetValue(iter, 1)) switch { - AudioBackend.OpenAl => openAlIsSupported, + AudioBackend.OpenAl => openAlIsSupported, AudioBackend.SoundIo => soundIoIsSupported, - AudioBackend.SDL2 => sdl2IsSupported, - AudioBackend.Dummy => true, - _ => throw new ArgumentOutOfRangeException() + AudioBackend.SDL2 => sdl2IsSupported, + AudioBackend.Dummy => true, + _ => throw new InvalidOperationException($"{nameof(_audioBackendStore)} contains an invalid value for iteration {iter}: {_audioBackendStore.GetValue(iter, 1)}"), }; }); if (OperatingSystem.IsMacOS()) { var store = (_graphicsBackend.Model as ListStore); - store.GetIter(out TreeIter openglIter, new TreePath(new int[] {1})); + store.GetIter(out TreeIter openglIter, new TreePath(new[] { 1 })); store.Remove(ref openglIter); _graphicsBackend.Model = store; @@ -478,15 +476,15 @@ namespace Ryujinx.Ui.Windows if (Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan) { - var devices = VulkanRenderer.GetPhysicalDevices(); + var devices = Graphics.Vulkan.VulkanRenderer.GetPhysicalDevices(); string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value; string preferredGpuId = preferredGpuIdFromConfig; bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig); foreach (var device in devices) { - string dGPU = device.IsDiscrete ? " (dGPU)" : ""; - _preferredGpu.Append(device.Id, $"{device.Name}{dGPU}"); + string dGpu = device.IsDiscrete ? " (dGPU)" : ""; + _preferredGpu.Append(device.Id, $"{device.Name}{dGpu}"); // If there's no GPU selected yet, we just pick the first GPU. // If there's a discrete GPU available, we always prefer that over the previous selection, @@ -521,33 +519,33 @@ namespace Ryujinx.Ui.Windows private void UpdateSystemTimeSpinners() { //Bind system time events - _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged; _systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged; //Apply actual system time + SystemTimeOffset to system time spin buttons DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset); - _systemTimeYearSpinAdjustment.Value = systemTime.Year; - _systemTimeMonthSpinAdjustment.Value = systemTime.Month; - _systemTimeDaySpinAdjustment.Value = systemTime.Day; - _systemTimeHourSpinAdjustment.Value = systemTime.Hour; + _systemTimeYearSpinAdjustment.Value = systemTime.Year; + _systemTimeMonthSpinAdjustment.Value = systemTime.Month; + _systemTimeDaySpinAdjustment.Value = systemTime.Day; + _systemTimeHourSpinAdjustment.Value = systemTime.Hour; _systemTimeMinuteSpinAdjustment.Value = systemTime.Minute; //Format spin buttons text to include leading zeros - _systemTimeYearSpin.Text = systemTime.Year.ToString("0000"); - _systemTimeMonthSpin.Text = systemTime.Month.ToString("00"); - _systemTimeDaySpin.Text = systemTime.Day.ToString("00"); - _systemTimeHourSpin.Text = systemTime.Hour.ToString("00"); + _systemTimeYearSpin.Text = systemTime.Year.ToString("0000"); + _systemTimeMonthSpin.Text = systemTime.Month.ToString("00"); + _systemTimeDaySpin.Text = systemTime.Day.ToString("00"); + _systemTimeHourSpin.Text = systemTime.Hour.ToString("00"); _systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00"); //Bind system time events - _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged; _systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged; } @@ -555,7 +553,7 @@ namespace Ryujinx.Ui.Windows { if (_directoryChanged) { - List<string> gameDirs = new List<string>(); + List<string> gameDirs = new(); _gameDirsBoxStore.GetIterFirst(out TreeIter treeIter); @@ -611,52 +609,52 @@ namespace Ryujinx.Ui.Windows DriverUtilities.ToggleOGLThreading(backendThreading == BackendThreading.Off); } - ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active; - ConfigurationState.Instance.Logger.EnableTrace.Value = _traceLogToggle.Active; - ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active; - ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active; - ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active; - ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active; - ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active; - ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active; - ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active; - ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value = Enum.Parse<GraphicsDebugLevel>(_graphicsDebugLevel.ActiveId); - ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active; - ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; - ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; - ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active; - ConfigurationState.Instance.HideCursor.Value = hideCursor; - ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; - ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; + ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active; + ConfigurationState.Instance.Logger.EnableTrace.Value = _traceLogToggle.Active; + ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active; + ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active; + ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active; + ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active; + ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active; + ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active; + ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active; + ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value = Enum.Parse<GraphicsDebugLevel>(_graphicsDebugLevel.ActiveId); + ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active; + ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; + ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; + ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active; + ConfigurationState.Instance.HideCursor.Value = hideCursor; + ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; + ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active; - ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active; - ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; - ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active; - ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; - ConfigurationState.Instance.System.MemoryManagerMode.Value = memoryMode; - ConfigurationState.Instance.System.ExpandRam.Value = _expandRamToggle.Active; - ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; - ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active; - ConfigurationState.Instance.Hid.EnableMouse.Value = _directMouseAccess.Active; - ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active; - ConfigurationState.Instance.System.Language.Value = Enum.Parse<Language>(_systemLanguageSelect.ActiveId); - ConfigurationState.Instance.System.Region.Value = Enum.Parse<Common.Configuration.System.Region>(_systemRegionSelect.ActiveId); - ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset; - ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text; - ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text; - ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value; - ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId, CultureInfo.InvariantCulture); - ConfigurationState.Instance.Graphics.AspectRatio.Value = Enum.Parse<AspectRatio>(_aspectRatio.ActiveId); - ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading; - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId); - ConfigurationState.Instance.Graphics.PreferredGpu.Value = _preferredGpu.ActiveId; - ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId); - ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom; - ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f; - ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId); - ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId); - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; + ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active; + ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; + ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active; + ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; + ConfigurationState.Instance.System.MemoryManagerMode.Value = memoryMode; + ConfigurationState.Instance.System.ExpandRam.Value = _expandRamToggle.Active; + ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; + ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active; + ConfigurationState.Instance.Hid.EnableMouse.Value = _directMouseAccess.Active; + ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active; + ConfigurationState.Instance.System.Language.Value = Enum.Parse<Language>(_systemLanguageSelect.ActiveId); + ConfigurationState.Instance.System.Region.Value = Enum.Parse<Common.Configuration.System.Region>(_systemRegionSelect.ActiveId); + ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset; + ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text; + ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text; + ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value; + ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId, CultureInfo.InvariantCulture); + ConfigurationState.Instance.Graphics.AspectRatio.Value = Enum.Parse<AspectRatio>(_aspectRatio.ActiveId); + ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading; + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId); + ConfigurationState.Instance.Graphics.PreferredGpu.Value = _preferredGpu.ActiveId; + ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId); + ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom; + ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f; + ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId); + ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId); + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value; @@ -666,7 +664,7 @@ namespace Ryujinx.Ui.Windows } ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - _parent.UpdateGraphicsConfig(); + MainWindow.UpdateGraphicsConfig(); ThemeHelper.ApplyTheme(); } @@ -692,10 +690,10 @@ namespace Ryujinx.Ui.Windows private void SystemTimeSpin_ValueChanged(object sender, EventArgs e) { - int year = _systemTimeYearSpin.ValueAsInt; - int month = _systemTimeMonthSpin.ValueAsInt; - int day = _systemTimeDaySpin.ValueAsInt; - int hour = _systemTimeHourSpin.ValueAsInt; + int year = _systemTimeYearSpin.ValueAsInt; + int month = _systemTimeMonthSpin.ValueAsInt; + int day = _systemTimeDaySpin.ValueAsInt; + int hour = _systemTimeHourSpin.ValueAsInt; int minute = _systemTimeMinuteSpin.ValueAsInt; if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime)) @@ -725,9 +723,9 @@ namespace Ryujinx.Ui.Windows } else { - FileChooserNative fileChooser = new FileChooserNative("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Add", "Cancel") + FileChooserNative fileChooser = new("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Add", "Cancel") { - SelectMultiple = true + SelectMultiple = true, }; if (fileChooser.Run() == (int)ResponseType.Accept) @@ -779,18 +777,18 @@ namespace Ryujinx.Ui.Windows private void CustThemeToggle_Activated(object sender, EventArgs args) { - _custThemePath.Sensitive = _custThemeToggle.Active; + _custThemePath.Sensitive = _custThemeToggle.Active; _custThemePathLabel.Sensitive = _custThemeToggle.Active; - _browseThemePath.Sensitive = _custThemeToggle.Active; + _browseThemePath.Sensitive = _custThemeToggle.Active; } private void BrowseThemeDir_Pressed(object sender, EventArgs args) { - using (FileChooserNative fileChooser = new FileChooserNative("Choose the theme to load", this, FileChooserAction.Open, "Select", "Cancel")) + using (FileChooserNative fileChooser = new("Choose the theme to load", this, FileChooserAction.Open, "Select", "Cancel")) { - FileFilter filter = new FileFilter() + FileFilter filter = new() { - Name = "Theme Files" + Name = "Theme Files", }; filter.AddPattern("*.css"); @@ -809,7 +807,7 @@ namespace Ryujinx.Ui.Windows { ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - ControllerWindow controllerWindow = new ControllerWindow(_parent, playerIndex); + ControllerWindow controllerWindow = new(_parent, playerIndex); controllerWindow.SetSizeRequest((int)(controllerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(controllerWindow.DefaultHeight * Program.WindowScaleFactor)); controllerWindow.Show(); diff --git a/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs index c40adc116..044f7e95a 100644 --- a/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs +++ b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs @@ -9,33 +9,32 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; +using GUI = Gtk.Builder.ObjectAttribute; using SpanHelpers = LibHac.Common.SpanHelpers; namespace Ryujinx.Ui.Windows { public class TitleUpdateWindow : Window { - private readonly MainWindow _parent; + private readonly MainWindow _parent; private readonly VirtualFileSystem _virtualFileSystem; - private readonly string _titleId; - private readonly string _updateJsonPath; + private readonly string _titleId; + private readonly string _updateJsonPath; private TitleUpdateMetadata _titleUpdateWindowData; private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary; - private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); -#pragma warning disable CS0649, IDE0044 - [GUI] Label _baseTitleInfoLabel; - [GUI] Box _availableUpdatesBox; +#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier + [GUI] Label _baseTitleInfoLabel; + [GUI] Box _availableUpdatesBox; [GUI] RadioButton _noUpdateRadioButton; #pragma warning restore CS0649, IDE0044 @@ -47,26 +46,26 @@ namespace Ryujinx.Ui.Windows builder.Autoconnect(this); - _titleId = titleId; - _virtualFileSystem = virtualFileSystem; - _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json"); + _titleId = titleId; + _virtualFileSystem = virtualFileSystem; + _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json"); _radioButtonToPathDictionary = new Dictionary<RadioButton, string>(); try { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, SerializerContext.TitleUpdateMetadata); + _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, _serializerContext.TitleUpdateMetadata); } catch { _titleUpdateWindowData = new TitleUpdateMetadata { Selected = "", - Paths = new List<string>() + Paths = new List<string>(), }; } _baseTitleInfoLabel.Text = $"Updates Available for {titleName} [{titleId.ToUpper()}]"; - + foreach (string path in _titleUpdateWindowData.Paths) { AddUpdate(path); @@ -89,42 +88,41 @@ namespace Ryujinx.Ui.Windows { if (File.Exists(path)) { - using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read)) + using FileStream file = new(path, FileMode.Open, FileAccess.Read); + + PartitionFileSystem nsp = new(file.AsStorage()); + + try { - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); + (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); - try + if (controlNca != null && patchNca != null) { - (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); + ApplicationControlProperty controlData = new(); - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new ApplicationControlProperty(); + using var nacpFile = new UniqueRef<IFile>(); - using var nacpFile = new UniqueRef<IFile>(); + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + RadioButton radioButton = new($"Version {controlData.DisplayVersionString.ToString()} - {path}"); + radioButton.JoinGroup(_noUpdateRadioButton); - RadioButton radioButton = new RadioButton($"Version {controlData.DisplayVersionString.ToString()} - {path}"); - radioButton.JoinGroup(_noUpdateRadioButton); + _availableUpdatesBox.Add(radioButton); + _radioButtonToPathDictionary.Add(radioButton, path); - _availableUpdatesBox.Add(radioButton); - _radioButtonToPathDictionary.Add(radioButton, path); - - radioButton.Show(); - radioButton.Active = true; - } - else - { - GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!"); - } + radioButton.Show(); + radioButton.Active = true; } - catch (Exception exception) + else { - GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); + GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!"); } } + catch (Exception exception) + { + GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); + } } } @@ -143,24 +141,23 @@ namespace Ryujinx.Ui.Windows private void AddButton_Clicked(object sender, EventArgs args) { - using (FileChooserNative fileChooser = new FileChooserNative("Select update files", this, FileChooserAction.Open, "Add", "Cancel")) + using FileChooserNative fileChooser = new("Select update files", this, FileChooserAction.Open, "Add", "Cancel"); + + fileChooser.SelectMultiple = true; + + FileFilter filter = new() { - fileChooser.SelectMultiple = true; + Name = "Switch Game Updates", + }; + filter.AddPattern("*.nsp"); - FileFilter filter = new FileFilter() + fileChooser.AddFilter(filter); + + if (fileChooser.Run() == (int)ResponseType.Accept) + { + foreach (string path in fileChooser.Filenames) { - Name = "Switch Game Updates" - }; - filter.AddPattern("*.nsp"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - foreach (string path in fileChooser.Filenames) - { - AddUpdate(path); - } + AddUpdate(path); } } } @@ -193,7 +190,7 @@ namespace Ryujinx.Ui.Windows } } - JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); + JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, _serializerContext.TitleUpdateMetadata); _parent.UpdateGameTable(); @@ -205,4 +202,4 @@ namespace Ryujinx.Ui.Windows Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs b/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs index 7c9ae8baa..804bd3fb0 100644 --- a/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs +++ b/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs @@ -1,45 +1,43 @@ using Gtk; using Pango; +using System; namespace Ryujinx.Ui.Windows { public partial class UserProfilesManagerWindow : Window { - private Box _mainBox; - private Label _selectedLabel; - private Box _selectedUserBox; - private Image _selectedUserImage; - private VBox _selectedUserInfoBox; - private Entry _selectedUserNameEntry; - private Label _selectedUserIdLabel; - private VBox _selectedUserButtonsBox; - private Button _saveProfileNameButton; - private Button _changeProfileImageButton; - private Box _usersTreeViewBox; - private Label _availableUsersLabel; + private Box _mainBox; + private Label _selectedLabel; + private Box _selectedUserBox; + private Image _selectedUserImage; + private Box _selectedUserInfoBox; + private Entry _selectedUserNameEntry; + private Label _selectedUserIdLabel; + private Box _selectedUserButtonsBox; + private Button _saveProfileNameButton; + private Button _changeProfileImageButton; + private Box _usersTreeViewBox; + private Label _availableUsersLabel; private ScrolledWindow _usersTreeViewWindow; - private ListStore _tableStore; - private TreeView _usersTreeView; - private Box _bottomBox; - private Button _addButton; - private Button _deleteButton; - private Button _closeButton; + private ListStore _tableStore; + private TreeView _usersTreeView; + private Box _bottomBox; + private Button _addButton; + private Button _deleteButton; + private Button _closeButton; private void InitializeComponent() { - -#pragma warning disable CS0612 - // // UserProfilesManagerWindow // - CanFocus = false; - Resizable = false; - Modal = true; + CanFocus = false; + Resizable = false; + Modal = true; WindowPosition = WindowPosition.Center; - DefaultWidth = 620; - DefaultHeight = 548; - TypeHint = Gdk.WindowTypeHint.Dialog; + DefaultWidth = 620; + DefaultHeight = 548; + TypeHint = Gdk.WindowTypeHint.Dialog; // // _mainBox @@ -51,9 +49,9 @@ namespace Ryujinx.Ui.Windows // _selectedLabel = new Label("Selected User Profile:") { - Margin = 15, + Margin = 15, Attributes = new AttrList(), - Halign = Align.Start + Halign = Align.Start, }; _selectedLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); @@ -67,7 +65,7 @@ namespace Ryujinx.Ui.Windows // _selectedUserBox = new Box(Orientation.Horizontal, 0) { - MarginLeft = 30 + MarginStart = 30, }; // @@ -78,15 +76,18 @@ namespace Ryujinx.Ui.Windows // // _selectedUserInfoBox // - _selectedUserInfoBox = new VBox(true, 0); + _selectedUserInfoBox = new Box(Orientation.Vertical, 0) + { + Homogeneous = true, + }; // // _selectedUserNameEntry // _selectedUserNameEntry = new Entry("") { - MarginLeft = 15, - MaxLength = (int)MaxProfileNameLength + MarginStart = 15, + MaxLength = (int)MaxProfileNameLength, }; _selectedUserNameEntry.KeyReleaseEvent += SelectedUserNameEntry_KeyReleaseEvent; @@ -95,16 +96,16 @@ namespace Ryujinx.Ui.Windows // _selectedUserIdLabel = new Label("") { - MarginTop = 15, - MarginLeft = 15 + MarginTop = 15, + MarginStart = 15, }; // // _selectedUserButtonsBox // - _selectedUserButtonsBox = new VBox() + _selectedUserButtonsBox = new Box(Orientation.Vertical, 0) { - MarginRight = 30 + MarginEnd = 30, }; // @@ -112,10 +113,10 @@ namespace Ryujinx.Ui.Windows // _saveProfileNameButton = new Button() { - Label = "Save Profile Name", - CanFocus = true, + Label = "Save Profile Name", + CanFocus = true, ReceivesDefault = true, - Sensitive = false + Sensitive = false, }; _saveProfileNameButton.Clicked += EditProfileNameButton_Pressed; @@ -124,10 +125,10 @@ namespace Ryujinx.Ui.Windows // _changeProfileImageButton = new Button() { - Label = "Change Profile Image", - CanFocus = true, + Label = "Change Profile Image", + CanFocus = true, ReceivesDefault = true, - MarginTop = 10 + MarginTop = 10, }; _changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed; @@ -136,9 +137,9 @@ namespace Ryujinx.Ui.Windows // _availableUsersLabel = new Label("Available User Profiles:") { - Margin = 15, + Margin = 15, Attributes = new AttrList(), - Halign = Align.Start + Halign = Align.Start, }; _availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); @@ -147,12 +148,12 @@ namespace Ryujinx.Ui.Windows // _usersTreeViewWindow = new ScrolledWindow() { - ShadowType = ShadowType.In, - CanFocus = true, - Expand = true, - MarginLeft = 30, - MarginRight = 30, - MarginBottom = 15 + ShadowType = ShadowType.In, + CanFocus = true, + Expand = true, + MarginStart = 30, + MarginEnd = 30, + MarginBottom = 15, }; // @@ -175,9 +176,9 @@ namespace Ryujinx.Ui.Windows // _bottomBox = new Box(Orientation.Horizontal, 0) { - MarginLeft = 30, - MarginRight = 30, - MarginBottom = 15 + MarginStart = 30, + MarginEnd = 30, + MarginBottom = 15, }; // @@ -185,10 +186,10 @@ namespace Ryujinx.Ui.Windows // _addButton = new Button() { - Label = "Add New Profile", - CanFocus = true, + Label = "Add New Profile", + CanFocus = true, ReceivesDefault = true, - HeightRequest = 35 + HeightRequest = 35, }; _addButton.Clicked += AddButton_Pressed; @@ -197,11 +198,11 @@ namespace Ryujinx.Ui.Windows // _deleteButton = new Button() { - Label = "Delete Selected Profile", - CanFocus = true, + Label = "Delete Selected Profile", + CanFocus = true, ReceivesDefault = true, - HeightRequest = 35, - MarginLeft = 10 + HeightRequest = 35, + MarginStart = 10, }; _deleteButton.Clicked += DeleteButton_Pressed; @@ -210,16 +211,14 @@ namespace Ryujinx.Ui.Windows // _closeButton = new Button() { - Label = "Close", - CanFocus = true, + Label = "Close", + CanFocus = true, ReceivesDefault = true, - HeightRequest = 35, - WidthRequest = 80 + HeightRequest = 35, + WidthRequest = 80, }; _closeButton.Clicked += CloseButton_Pressed; -#pragma warning restore CS0612 - ShowComponent(); } @@ -253,4 +252,4 @@ namespace Ryujinx.Ui.Windows ShowAll(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs index a08b5dd17..c2ca010c7 100644 --- a/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs +++ b/src/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs @@ -13,7 +13,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using Image = SixLabors.ImageSharp.Image; -using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId; namespace Ryujinx.Ui.Windows { @@ -29,7 +28,7 @@ namespace Ryujinx.Ui.Windows private Gdk.RGBA _selectedColor; - private ManualResetEvent _avatarsPreloadingEvent = new ManualResetEvent(false); + private readonly ManualResetEvent _avatarsPreloadingEvent = new(false); public UserProfilesManagerWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles") { @@ -37,24 +36,24 @@ namespace Ryujinx.Ui.Windows InitializeComponent(); - _selectedColor.Red = 0.212; + _selectedColor.Red = 0.212; _selectedColor.Green = 0.843; - _selectedColor.Blue = 0.718; + _selectedColor.Blue = 0.718; _selectedColor.Alpha = 1; _accountManager = accountManager; _contentManager = contentManager; - CellRendererToggle userSelectedToggle = new CellRendererToggle(); + CellRendererToggle userSelectedToggle = new(); userSelectedToggle.Toggled += UserSelectedToggle_Toggled; // NOTE: Uncomment following line when multiple selection of user profiles is supported. //_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0); _usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1); - _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3); + _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3); _tableStore.SetSortColumnId(0, SortType.Descending); - + RefreshList(); if (_contentManager.GetCurrentFirmwareVersion() != null) @@ -77,8 +76,8 @@ namespace Ryujinx.Ui.Windows if (userProfile.AccountState == AccountState.Open) { - _selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96); - _selectedUserIdLabel.Text = userProfile.UserId.ToString(); + _selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96); + _selectedUserIdLabel.Text = userProfile.UserId.ToString(); _selectedUserNameEntry.Text = userProfile.Name; _deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId; @@ -111,7 +110,7 @@ namespace Ryujinx.Ui.Windows Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1); string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0]; - string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1]; + string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1]; // Unselect the first user. _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); @@ -121,9 +120,9 @@ namespace Ryujinx.Ui.Windows // Set new informations. _tableStore.SetValue(selectedIter, 0, true); - _selectedUserImage.Pixbuf = userPicture; - _selectedUserNameEntry.Text = userName; - _selectedUserIdLabel.Text = userId; + _selectedUserImage.Pixbuf = userPicture; + _selectedUserNameEntry.Text = userName; + _selectedUserIdLabel.Text = userId; _saveProfileNameButton.Sensitive = false; // Open the selected one. @@ -178,29 +177,27 @@ namespace Ryujinx.Ui.Windows private void ProcessProfileImage(byte[] buffer) { - using (Image image = Image.Load(buffer)) - { - image.Mutate(x => x.Resize(256, 256)); + using Image image = Image.Load(buffer); - using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream()) - { - image.SaveAsJpeg(streamJpg); + image.Mutate(x => x.Resize(256, 256)); - _bufferImageProfile = streamJpg.ToArray(); - } - } + using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); + + image.SaveAsJpeg(streamJpg); + + _bufferImageProfile = streamJpg.ToArray(); } private void ProfileImageFileChooser() { - FileChooserNative fileChooser = new FileChooserNative("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel") + FileChooserNative fileChooser = new("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel") { - SelectMultiple = false + SelectMultiple = false, }; - FileFilter filter = new FileFilter() + FileFilter filter = new() { - Name = "Custom Profile Images" + Name = "Custom Profile Images", }; filter.AddPattern("*.jpg"); filter.AddPattern("*.jpeg"); @@ -225,15 +222,15 @@ namespace Ryujinx.Ui.Windows } else { - Dictionary<int, string> buttons = new Dictionary<int, string>() + Dictionary<int, string> buttons = new() { { 0, "Import Image File" }, - { 1, "Select Firmware Avatar" } + { 1, "Select Firmware Avatar" }, }; ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection", "Choose a Profile Image", - "You may import a custom profile image, or select an avatar from the system firmware.", + "You may import a custom profile image, or select an avatar from the system firmware.", buttons, MessageType.Question); if (responseDialog == 0) @@ -242,9 +239,9 @@ namespace Ryujinx.Ui.Windows } else if (responseDialog == (ResponseType)1) { - AvatarWindow avatarWindow = new AvatarWindow() + AvatarWindow avatarWindow = new() { - NewUser = newUser + NewUser = newUser, }; avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent; @@ -328,4 +325,4 @@ namespace Ryujinx.Ui.Windows Close(); } } -} \ No newline at end of file +} From 515fc32b21f59298ec8ca45f5d3c36e9d3041084 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 2 Jul 2023 01:27:18 +0200 Subject: [PATCH 693/737] [Ryujinx.Audio] Address dotnet-format issues (#5362) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA2208 warnings * Address or silence dotnet format CA2211 warnings * Address review comments * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Run dotnet format whitespace after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Remove a few unused parameters * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Fix and silence a few dotnet-format warnings again * Address IDE0251 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * Fix naming rule violations, remove redundant code and fix build issues * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Add trailing commas * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Address review feedback --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- src/Ryujinx.Audio/AudioManager.cs | 11 +- .../Backends/Common/BackendHelper.cs | 2 +- .../Backends/Common/DynamicRingBuffer.cs | 2 +- .../Common/HardwareDeviceSessionOutputBase.cs | 7 +- .../CompatLayerHardwareDeviceDriver.cs | 14 +- .../CompatLayerHardwareDeviceSession.cs | 12 +- .../Backends/CompatLayer/Downmixing.cs | 14 +- .../Dummy/DummyHardwareDeviceDriver.cs | 15 +- .../Dummy/DummyHardwareDeviceSessionInput.cs | 8 +- .../Dummy/DummyHardwareDeviceSessionOutput.cs | 4 +- src/Ryujinx.Audio/Common/AudioBuffer.cs | 2 +- .../Common/AudioDeviceSession.cs | 20 +- src/Ryujinx.Audio/Common/AudioDeviceState.cs | 4 +- .../Common/AudioInputConfiguration.cs | 4 +- .../Common/AudioOutputConfiguration.cs | 2 +- src/Ryujinx.Audio/Common/AudioUserBuffer.cs | 2 +- src/Ryujinx.Audio/Common/SampleFormat.cs | 4 +- src/Ryujinx.Audio/Constants.cs | 2 +- src/Ryujinx.Audio/Input/AudioInputManager.cs | 14 +- src/Ryujinx.Audio/Input/AudioInputSystem.cs | 24 +- .../Integration/HardwareDeviceImpl.cs | 13 +- .../Integration/IHardwareDevice.cs | 2 +- .../Integration/IHardwareDeviceDriver.cs | 4 +- .../Integration/IHardwareDeviceSession.cs | 2 +- .../Integration/IWritableEvent.cs | 2 +- .../Output/AudioOutputManager.cs | 15 +- src/Ryujinx.Audio/Output/AudioOutputSystem.cs | 18 +- .../Common/AuxiliaryBufferAddresses.cs | 2 +- .../Renderer/Common/BehaviourParameter.cs | 6 +- .../Renderer/Common/EdgeMatrix.cs | 2 +- .../Renderer/Common/EffectType.cs | 2 +- .../Renderer/Common/MemoryPoolUserState.cs | 4 +- .../Renderer/Common/NodeIdHelper.cs | 2 +- .../Renderer/Common/NodeIdType.cs | 4 +- .../Renderer/Common/NodeStates.cs | 25 +- .../Renderer/Common/PerformanceDetailType.cs | 4 +- .../Renderer/Common/PerformanceEntryType.cs | 4 +- .../Renderer/Common/PlayState.cs | 4 +- .../Renderer/Common/ReverbEarlyMode.cs | 4 +- .../Renderer/Common/ReverbLateMode.cs | 4 +- src/Ryujinx.Audio/Renderer/Common/SinkType.cs | 4 +- .../Renderer/Common/UpdateDataHeader.cs | 7 +- .../Renderer/Common/VoiceUpdateState.cs | 2 +- .../Renderer/Common/WaveBuffer.cs | 5 +- .../Renderer/Common/WorkBufferAllocator.cs | 8 +- .../Renderer/Device/VirtualDevice.cs | 12 +- .../Renderer/Device/VirtualDeviceSession.cs | 2 +- .../Device/VirtualDeviceSessionRegistry.cs | 6 +- src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs | 10 +- .../Renderer/Dsp/AudioProcessor.cs | 21 +- .../Renderer/Dsp/BiquadFilterHelper.cs | 2 +- .../Command/AdpcmDataSourceCommandVersion1.cs | 8 +- .../Dsp/Command/AuxiliaryBufferCommand.cs | 4 +- .../Dsp/Command/BiquadFilterCommand.cs | 2 +- .../Dsp/Command/CaptureBufferCommand.cs | 2 +- .../Dsp/Command/ClearMixBufferCommand.cs | 2 +- .../Renderer/Dsp/Command/CommandList.cs | 5 +- .../Renderer/Dsp/Command/CommandType.cs | 4 +- .../Renderer/Dsp/Command/CompressorCommand.cs | 5 +- .../Dsp/Command/CopyMixBufferCommand.cs | 2 +- .../Dsp/Command/DataSourceVersion2Command.cs | 26 +- .../Renderer/Dsp/Command/DelayCommand.cs | 26 +- .../Dsp/Command/DepopForMixBuffersCommand.cs | 16 +- .../Dsp/Command/DepopPrepareCommand.cs | 2 +- .../Command/DownMixSurroundToStereoCommand.cs | 2 +- .../Dsp/Command/GroupedBiquadFilterCommand.cs | 12 +- .../Renderer/Dsp/Command/ICommand.cs | 2 +- .../Dsp/Command/LimiterCommandVersion1.cs | 9 +- .../Dsp/Command/LimiterCommandVersion2.cs | 9 +- .../Renderer/Dsp/Command/MixCommand.cs | 2 +- .../Renderer/Dsp/Command/MixRampCommand.cs | 2 +- .../Dsp/Command/MixRampGroupedCommand.cs | 4 +- .../PcmFloatDataSourceCommandVersion1.cs | 8 +- .../PcmInt16DataSourceCommandVersion1.cs | 8 +- .../Dsp/Command/PerformanceCommand.cs | 4 +- .../Renderer/Dsp/Command/Reverb3dCommand.cs | 34 +- .../Renderer/Dsp/Command/ReverbCommand.cs | 73 +-- .../Renderer/Dsp/Command/UpsampleCommand.cs | 2 +- .../Renderer/Dsp/Command/VolumeCommand.cs | 2 +- .../Renderer/Dsp/Command/VolumeRampCommand.cs | 2 +- .../Renderer/Dsp/DataSourceHelper.cs | 12 +- .../Renderer/Dsp/Effect/DecayDelay.cs | 2 +- .../Renderer/Dsp/Effect/DelayLine.cs | 6 +- .../Renderer/Dsp/Effect/DelayLineReverb3d.cs | 6 +- .../Dsp/Effect/ExponentialMovingAverage.cs | 6 +- .../Renderer/Dsp/Effect/IDelayLine.cs | 2 +- .../Renderer/Dsp/FixedPointHelper.cs | 2 +- .../Renderer/Dsp/FloatingPointHelper.cs | 6 +- src/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs | 5 +- .../Renderer/Dsp/ResamplerHelper.cs | 45 +- .../Renderer/Dsp/State/AdpcmLoopContext.cs | 2 +- .../Dsp/State/AuxiliaryBufferHeader.cs | 2 +- .../Renderer/Dsp/State/BiquadFilterState.cs | 2 +- .../Renderer/Dsp/State/DelayState.cs | 2 +- .../Renderer/Dsp/State/LimiterState.cs | 8 +- .../Renderer/Dsp/State/Reverb3dState.cs | 24 +- .../Renderer/Dsp/State/ReverbState.cs | 32 +- .../Renderer/Dsp/UpsamplerHelper.cs | 40 +- .../Parameter/AudioRendererConfiguration.cs | 4 +- .../Parameter/BehaviourErrorInfoOutStatus.cs | 2 +- .../Parameter/BiquadFilterParameter.cs | 4 +- .../Effect/AuxiliaryBufferParameter.cs | 2 +- .../Effect/BiquadFilterEffectParameter.cs | 2 +- .../Parameter/Effect/BufferMixerParameter.cs | 2 +- .../Parameter/Effect/CompressorParameter.cs | 4 +- .../Parameter/Effect/DelayParameter.cs | 6 +- .../Parameter/Effect/LimiterParameter.cs | 8 +- .../Parameter/Effect/LimiterStatistics.cs | 4 +- .../Parameter/Effect/Reverb3dParameter.cs | 8 +- .../Parameter/Effect/ReverbParameter.cs | 6 +- .../Parameter/EffectInParameterVersion1.cs | 20 +- .../Parameter/EffectInParameterVersion2.cs | 20 +- .../Parameter/EffectOutStatusVersion1.cs | 4 +- .../Parameter/EffectOutStatusVersion2.cs | 4 +- .../Renderer/Parameter/EffectResultState.cs | 2 +- .../Renderer/Parameter/EffectState.cs | 4 +- .../Renderer/Parameter/IEffectInParameter.cs | 2 +- .../Renderer/Parameter/IEffectOutStatus.cs | 2 +- .../Parameter/MemoryPoolInParameter.cs | 2 +- .../Renderer/Parameter/MemoryPoolOutStatus.cs | 2 +- .../MixInParameterDirtyOnlyUpdate.cs | 2 +- .../Renderer/Parameter/MixParameter.cs | 8 +- .../Performance/PerformanceInParameter.cs | 2 +- .../Performance/PerformanceOutStatus.cs | 2 +- .../Parameter/RendererInfoOutStatus.cs | 4 +- .../Parameter/Sink/CircularBufferParameter.cs | 7 +- .../Parameter/Sink/DeviceParameter.cs | 6 +- .../Renderer/Parameter/SinkInParameter.cs | 4 +- .../Renderer/Parameter/SinkOutStatus.cs | 4 +- .../SplitterDestinationInParameter.cs | 4 +- .../Renderer/Parameter/SplitterInParameter.cs | 4 +- .../Parameter/SplitterInParameterHeader.cs | 4 +- .../VoiceChannelResourceInParameter.cs | 2 +- .../Renderer/Parameter/VoiceInParameter.cs | 40 +- .../Renderer/Parameter/VoiceOutStatus.cs | 2 +- .../Renderer/Server/AudioRenderSystem.cs | 70 ++- .../Renderer/Server/AudioRendererManager.cs | 12 +- .../Renderer/Server/BehaviourContext.cs | 15 +- .../Renderer/Server/CommandBuffer.cs | 60 +-- .../Renderer/Server/CommandGenerator.cs | 44 +- .../CommandProcessingTimeEstimatorVersion1.cs | 6 +- .../CommandProcessingTimeEstimatorVersion2.cs | 256 ++++----- .../CommandProcessingTimeEstimatorVersion3.cs | 496 +++++++----------- .../CommandProcessingTimeEstimatorVersion4.cs | 10 +- .../CommandProcessingTimeEstimatorVersion5.cs | 354 +++++-------- .../Server/Effect/AuxiliaryBufferEffect.cs | 4 +- .../Renderer/Server/Effect/BaseEffect.cs | 36 +- .../Server/Effect/BiquadFilterEffect.cs | 2 +- .../Renderer/Server/Effect/BufferMixEffect.cs | 2 +- .../Server/Effect/CaptureBufferEffect.cs | 2 +- .../Renderer/Server/Effect/DelayEffect.cs | 2 +- .../Renderer/Server/Effect/EffectContext.cs | 2 +- .../Renderer/Server/Effect/LimiterEffect.cs | 2 +- .../Renderer/Server/Effect/Reverb3dEffect.cs | 2 +- .../Renderer/Server/Effect/ReverbEffect.cs | 2 +- .../Renderer/Server/Effect/UsageState.cs | 4 +- .../Server/ICommandProcessingTimeEstimator.cs | 2 +- .../Renderer/Server/MemoryPool/AddressInfo.cs | 12 +- .../Server/MemoryPool/MemoryPoolState.cs | 12 +- .../Renderer/Server/MemoryPool/PoolMapper.cs | 21 +- .../Renderer/Server/Mix/MixContext.cs | 12 +- .../Renderer/Server/Mix/MixState.cs | 11 +- .../Performance/IPerformanceDetailEntry.cs | 2 +- .../Server/Performance/IPerformanceEntry.cs | 2 +- .../Server/Performance/IPerformanceHeader.cs | 2 +- .../Performance/PerformanceDetailVersion1.cs | 10 +- .../Performance/PerformanceDetailVersion2.cs | 10 +- .../Performance/PerformanceEntryAddresses.cs | 2 +- .../Performance/PerformanceEntryVersion1.cs | 10 +- .../Performance/PerformanceEntryVersion2.cs | 10 +- .../PerformanceFrameHeaderVersion1.cs | 14 +- .../PerformanceFrameHeaderVersion2.cs | 8 +- .../Server/Performance/PerformanceManager.cs | 24 +- .../Performance/PerformanceManagerGeneric.cs | 44 +- .../Renderer/Server/RendererSystemContext.cs | 2 +- .../Renderer/Server/Sink/BaseSink.cs | 2 +- .../Server/Sink/CircularBufferSink.cs | 2 +- .../Renderer/Server/Sink/DeviceSink.cs | 2 +- .../Renderer/Server/Sink/SinkContext.cs | 2 +- .../Server/Splitter/SplitterContext.cs | 22 +- .../Server/Splitter/SplitterDestination.cs | 10 +- .../Renderer/Server/Splitter/SplitterState.cs | 10 +- .../Renderer/Server/StateUpdater.cs | 129 ++--- .../Types/AudioRendererExecutionMode.cs | 4 +- .../Types/AudioRendererRenderingDevice.cs | 4 +- .../Renderer/Server/Types/PlayState.cs | 4 +- .../Server/Upsampler/UpsamplerBufferState.cs | 2 +- .../Server/Upsampler/UpsamplerManager.cs | 14 +- .../Server/Upsampler/UpsamplerState.cs | 6 +- .../Server/Voice/VoiceChannelResource.cs | 2 +- .../Renderer/Server/Voice/VoiceContext.cs | 4 +- .../Renderer/Server/Voice/VoiceState.cs | 54 +- .../Renderer/Server/Voice/WaveBuffer.cs | 11 +- .../Utils/AudioProcessorMemoryManager.cs | 2 +- src/Ryujinx.Audio/Renderer/Utils/BitArray.cs | 6 +- .../Renderer/Utils/FileHardwareDevice.cs | 8 +- src/Ryujinx.Audio/Renderer/Utils/Mailbox.cs | 7 +- .../Renderer/Utils/Math/Matrix2x2.cs | 2 +- .../Renderer/Utils/Math/Matrix6x6.cs | 2 +- .../Renderer/Utils/Math/MatrixHelper.cs | 4 +- .../Renderer/Utils/Math/Vector6.cs | 2 +- .../Renderer/Utils/SpanIOHelper.cs | 38 +- .../Renderer/Utils/SpanMemoryManager.cs | 4 +- .../Renderer/Utils/SplitterHardwareDevice.cs | 7 +- src/Ryujinx.Audio/ResultCode.cs | 2 +- .../HOS/Services/Audio/AudioInManager.cs | 1 - .../HOS/Services/Audio/AudioOutManager.cs | 1 - 207 files changed, 1354 insertions(+), 1670 deletions(-) diff --git a/src/Ryujinx.Audio/AudioManager.cs b/src/Ryujinx.Audio/AudioManager.cs index 9f2a05b09..370d3d098 100644 --- a/src/Ryujinx.Audio/AudioManager.cs +++ b/src/Ryujinx.Audio/AudioManager.cs @@ -16,17 +16,17 @@ namespace Ryujinx.Audio /// <summary> /// Events signaled when the driver played audio buffers. /// </summary> - private ManualResetEvent[] _updateRequiredEvents; + private readonly ManualResetEvent[] _updateRequiredEvents; /// <summary> /// Action to execute when the driver played audio buffers. /// </summary> - private Action[] _actions; + private readonly Action[] _actions; /// <summary> /// The worker thread in charge of handling sessions update. /// </summary> - private Thread _workerThread; + private readonly Thread _workerThread; private bool _isRunning; @@ -44,7 +44,7 @@ namespace Ryujinx.Audio _workerThread = new Thread(Update) { - Name = "AudioManager.Worker" + Name = "AudioManager.Worker", }; } @@ -115,6 +115,7 @@ namespace Ryujinx.Audio public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -129,4 +130,4 @@ namespace Ryujinx.Audio } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs b/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs index 30db340fa..124d8364f 100644 --- a/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs +++ b/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs @@ -23,4 +23,4 @@ namespace Ryujinx.Audio.Backends.Common return bufferSize / GetSampleSize(format) / channelCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs index d17303cd3..05dd2162a 100644 --- a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs +++ b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs @@ -163,4 +163,4 @@ namespace Ryujinx.Audio.Backends.Common } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs b/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs index 6fb3bee02..5599c0827 100644 --- a/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs +++ b/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs @@ -66,14 +66,11 @@ namespace Ryujinx.Audio.Backends.Common return false; } - if (buffer.Data == null) - { - buffer.Data = samples; - } + buffer.Data ??= samples; return true; } public virtual void UnregisterBuffer(AudioBuffer buffer) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs index 22919f1e1..3f3806c3e 100644 --- a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs @@ -6,14 +6,13 @@ using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Threading; - using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; namespace Ryujinx.Audio.Backends.CompatLayer { public class CompatLayerHardwareDeviceDriver : IHardwareDeviceDriver { - private IHardwareDeviceDriver _realDriver; + private readonly IHardwareDeviceDriver _realDriver; public static bool IsSupported => true; @@ -24,6 +23,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer public void Dispose() { + GC.SuppressFinalize(this); _realDriver.Dispose(); } @@ -49,7 +49,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer 6 => SelectHardwareChannelCount(2), 2 => SelectHardwareChannelCount(1), 1 => throw new ArgumentException("No valid channel configuration found!"), - _ => throw new ArgumentException($"Invalid targetChannelCount {targetChannelCount}") + _ => throw new ArgumentException($"Invalid targetChannelCount {targetChannelCount}"), }; } @@ -110,7 +110,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer { Logger.Warning?.Print(LogClass.Audio, "The selected audio backend doesn't support audio input, fallback to dummy..."); - return new DummyHardwareDeviceSessionInput(this, memoryManager, sampleFormat, sampleRate, channelCount); + return new DummyHardwareDeviceSessionInput(this, memoryManager); } throw new NotImplementedException(); @@ -138,12 +138,12 @@ namespace Ryujinx.Audio.Backends.CompatLayer if (direction == Direction.Input) { - Logger.Warning?.Print(LogClass.Audio, $"The selected audio backend doesn't support the requested audio input configuration, fallback to dummy..."); + Logger.Warning?.Print(LogClass.Audio, "The selected audio backend doesn't support the requested audio input configuration, fallback to dummy..."); // TODO: We currently don't support audio input upsampling/downsampling, implement this. realSession.Dispose(); - return new DummyHardwareDeviceSessionInput(this, memoryManager, sampleFormat, sampleRate, channelCount); + return new DummyHardwareDeviceSessionInput(this, memoryManager); } // It must be a HardwareDeviceSessionOutputBase. @@ -183,4 +183,4 @@ namespace Ryujinx.Audio.Backends.CompatLayer return direction == Direction.Input || direction == Direction.Output; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs index f22a7a690..a9acabec9 100644 --- a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs @@ -8,9 +8,9 @@ namespace Ryujinx.Audio.Backends.CompatLayer { class CompatLayerHardwareDeviceSession : HardwareDeviceSessionOutputBase { - private HardwareDeviceSessionOutputBase _realSession; - private SampleFormat _userSampleFormat; - private uint _userChannelCount; + private readonly HardwareDeviceSessionOutputBase _realSession; + private readonly SampleFormat _userSampleFormat; + private readonly uint _userChannelCount; public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, SampleFormat userSampleFormat, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount) { @@ -116,11 +116,11 @@ namespace Ryujinx.Audio.Backends.CompatLayer samples = MemoryMarshal.Cast<short, byte>(samplesPCM16).ToArray(); } - AudioBuffer fakeBuffer = new AudioBuffer + AudioBuffer fakeBuffer = new() { BufferTag = buffer.BufferTag, DataPointer = buffer.DataPointer, - DataSize = (ulong)samples.Length + DataSize = (ulong)samples.Length, }; bool result = _realSession.RegisterBuffer(fakeBuffer, samples); @@ -159,4 +159,4 @@ namespace Ryujinx.Audio.Backends.CompatLayer return _realSession.WasBufferFullyConsumed(buffer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs b/src/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs index 6959c1588..ffd427a5e 100644 --- a/src/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs +++ b/src/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs @@ -31,18 +31,18 @@ namespace Ryujinx.Audio.Backends.CompatLayer private const int Minus6dBInQ15 = (int)(0.501f * RawQ15One); private const int Minus12dBInQ15 = (int)(0.251f * RawQ15One); - private static readonly int[] DefaultSurroundToStereoCoefficients = new int[4] + private static readonly int[] _defaultSurroundToStereoCoefficients = new int[4] { RawQ15One, Minus3dBInQ15, Minus12dBInQ15, - Minus3dBInQ15 + Minus3dBInQ15, }; - private static readonly int[] DefaultStereoToMonoCoefficients = new int[2] + private static readonly int[] _defaultStereoToMonoCoefficients = new int[2] { Minus6dBInQ15, - Minus6dBInQ15 + Minus6dBInQ15, }; private const int SurroundChannelCount = 6; @@ -114,12 +114,12 @@ namespace Ryujinx.Audio.Backends.CompatLayer public static short[] DownMixStereoToMono(ReadOnlySpan<short> data) { - return DownMixStereoToMono(DefaultStereoToMonoCoefficients, data); + return DownMixStereoToMono(_defaultStereoToMonoCoefficients, data); } public static short[] DownMixSurroundToStereo(ReadOnlySpan<short> data) { - return DownMixSurroundToStereo(DefaultSurroundToStereoCoefficients, data); + return DownMixSurroundToStereo(_defaultSurroundToStereoCoefficients, data); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs index 641640f0e..bac21c448 100644 --- a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs @@ -1,16 +1,16 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using Ryujinx.Memory; +using System; using System.Threading; - using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; namespace Ryujinx.Audio.Backends.Dummy { public class DummyHardwareDeviceDriver : IHardwareDeviceDriver { - private ManualResetEvent _updateRequiredEvent; - private ManualResetEvent _pauseEvent; + private readonly ManualResetEvent _updateRequiredEvent; + private readonly ManualResetEvent _pauseEvent; public static bool IsSupported => true; @@ -36,10 +36,8 @@ namespace Ryujinx.Audio.Backends.Dummy { return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); } - else - { - return new DummyHardwareDeviceSessionInput(this, memoryManager, sampleFormat, sampleRate, channelCount); - } + + return new DummyHardwareDeviceSessionInput(this, memoryManager); } public ManualResetEvent GetUpdateRequiredEvent() @@ -54,6 +52,7 @@ namespace Ryujinx.Audio.Backends.Dummy public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -86,4 +85,4 @@ namespace Ryujinx.Audio.Backends.Dummy return channelCount == 1 || channelCount == 2 || channelCount == 6; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs index 845713a19..f51a63393 100644 --- a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs +++ b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs @@ -8,10 +8,10 @@ namespace Ryujinx.Audio.Backends.Dummy class DummyHardwareDeviceSessionInput : IHardwareDeviceSession { private float _volume; - private IHardwareDeviceDriver _manager; - private IVirtualMemoryManager _memoryManager; + private readonly IHardwareDeviceDriver _manager; + private readonly IVirtualMemoryManager _memoryManager; - public DummyHardwareDeviceSessionInput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) + public DummyHardwareDeviceSessionInput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager) { _volume = 1.0f; _manager = manager; @@ -64,4 +64,4 @@ namespace Ryujinx.Audio.Backends.Dummy return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs index 8e2c949ec..1c248faaa 100644 --- a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs +++ b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Audio.Backends.Dummy internal class DummyHardwareDeviceSessionOutput : HardwareDeviceSessionOutputBase { private float _volume; - private IHardwareDeviceDriver _manager; + private readonly IHardwareDeviceDriver _manager; private ulong _playedSampleCount; @@ -59,4 +59,4 @@ namespace Ryujinx.Audio.Backends.Dummy return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Common/AudioBuffer.cs b/src/Ryujinx.Audio/Common/AudioBuffer.cs index b79401b77..87a7d5f32 100644 --- a/src/Ryujinx.Audio/Common/AudioBuffer.cs +++ b/src/Ryujinx.Audio/Common/AudioBuffer.cs @@ -34,4 +34,4 @@ namespace Ryujinx.Audio.Common /// </summary> public byte[] Data; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Common/AudioDeviceSession.cs b/src/Ryujinx.Audio/Common/AudioDeviceSession.cs index 0191f7ccd..a0e04c80d 100644 --- a/src/Ryujinx.Audio/Common/AudioDeviceSession.cs +++ b/src/Ryujinx.Audio/Common/AudioDeviceSession.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Common /// <summary> /// Array of all buffers currently used or released. /// </summary> - private AudioBuffer[] _buffers; + private readonly AudioBuffer[] _buffers; /// <summary> /// The server index inside <see cref="_buffers"/> (appended but not queued to device driver). @@ -58,17 +58,17 @@ namespace Ryujinx.Audio.Common /// <summary> /// The released buffer event. /// </summary> - private IWritableEvent _bufferEvent; + private readonly IWritableEvent _bufferEvent; /// <summary> /// The session on the device driver. /// </summary> - private IHardwareDeviceSession _hardwareDeviceSession; + private readonly IHardwareDeviceSession _hardwareDeviceSession; /// <summary> /// Max number of buffers that can be registered to the device driver at a time. /// </summary> - private uint _bufferRegisteredLimit; + private readonly uint _bufferRegisteredLimit; /// <summary> /// Create a new <see cref="AudioDeviceSession"/>. @@ -311,9 +311,9 @@ namespace Ryujinx.Audio.Common return false; } - public bool AppendUacBuffer(AudioBuffer buffer, uint handle) + public static bool AppendUacBuffer(AudioBuffer buffer, uint handle) { - // NOTE: On hardware, there is another RegisterBuffer method taking an handle. + // NOTE: On hardware, there is another RegisterBuffer method taking a handle. // This variant of the call always return false (stubbed?) as a result this logic will never succeed. return false; @@ -425,10 +425,8 @@ namespace Ryujinx.Audio.Common { return 0; } - else - { - return _hardwareDeviceSession.GetPlayedSampleCount(); - } + + return _hardwareDeviceSession.GetPlayedSampleCount(); } /// <summary> @@ -515,4 +513,4 @@ namespace Ryujinx.Audio.Common } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Common/AudioDeviceState.cs b/src/Ryujinx.Audio/Common/AudioDeviceState.cs index b3f968da2..8705e802e 100644 --- a/src/Ryujinx.Audio/Common/AudioDeviceState.cs +++ b/src/Ryujinx.Audio/Common/AudioDeviceState.cs @@ -13,6 +13,6 @@ namespace Ryujinx.Audio.Common /// <summary> /// The audio device is stopped. /// </summary> - Stopped + Stopped, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Common/AudioInputConfiguration.cs b/src/Ryujinx.Audio/Common/AudioInputConfiguration.cs index d3cfdd47b..078c3a394 100644 --- a/src/Ryujinx.Audio/Common/AudioInputConfiguration.cs +++ b/src/Ryujinx.Audio/Common/AudioInputConfiguration.cs @@ -24,6 +24,6 @@ namespace Ryujinx.Audio.Common /// <summary> /// Reserved/unused. /// </summary> - private ushort _reserved; + private readonly ushort _reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Common/AudioOutputConfiguration.cs b/src/Ryujinx.Audio/Common/AudioOutputConfiguration.cs index e17e17576..594f12250 100644 --- a/src/Ryujinx.Audio/Common/AudioOutputConfiguration.cs +++ b/src/Ryujinx.Audio/Common/AudioOutputConfiguration.cs @@ -34,4 +34,4 @@ namespace Ryujinx.Audio.Common /// </summary> public AudioDeviceState AudioOutState; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Common/AudioUserBuffer.cs b/src/Ryujinx.Audio/Common/AudioUserBuffer.cs index 50ab67fa3..bb71165ff 100644 --- a/src/Ryujinx.Audio/Common/AudioUserBuffer.cs +++ b/src/Ryujinx.Audio/Common/AudioUserBuffer.cs @@ -33,4 +33,4 @@ namespace Ryujinx.Audio.Common /// </summary> public ulong DataOffset; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Common/SampleFormat.cs b/src/Ryujinx.Audio/Common/SampleFormat.cs index 901410a24..39e525e87 100644 --- a/src/Ryujinx.Audio/Common/SampleFormat.cs +++ b/src/Ryujinx.Audio/Common/SampleFormat.cs @@ -38,6 +38,6 @@ namespace Ryujinx.Audio.Common /// <summary> /// ADPCM sample format. (Also known as GC-ADPCM) /// </summary> - Adpcm = 6 + Adpcm = 6, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Constants.cs b/src/Ryujinx.Audio/Constants.cs index cde87744f..eb5b39013 100644 --- a/src/Ryujinx.Audio/Constants.cs +++ b/src/Ryujinx.Audio/Constants.cs @@ -172,4 +172,4 @@ namespace Ryujinx.Audio 0.707f, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Input/AudioInputManager.cs b/src/Ryujinx.Audio/Input/AudioInputManager.cs index 63cbe031f..4d1796c96 100644 --- a/src/Ryujinx.Audio/Input/AudioInputManager.cs +++ b/src/Ryujinx.Audio/Input/AudioInputManager.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Input /// <summary> /// The session ids allocation table. /// </summary> - private int[] _sessionIds; + private readonly int[] _sessionIds; /// <summary> /// The device driver. @@ -39,7 +39,7 @@ namespace Ryujinx.Audio.Input /// <summary> /// The <see cref="AudioInputSystem"/> session instances. /// </summary> - private AudioInputSystem[] _sessions; + private readonly AudioInputSystem[] _sessions; /// <summary> /// The count of active sessions. @@ -166,6 +166,7 @@ namespace Ryujinx.Audio.Input /// </summary> /// <param name="filtered">If true, filter disconnected devices</param> /// <returns>The list of all audio inputs name</returns> +#pragma warning disable CA1822 // Mark member as static public string[] ListAudioIns(bool filtered) { if (filtered) @@ -173,8 +174,9 @@ namespace Ryujinx.Audio.Input // TODO: Detect if the driver supports audio input } - return new string[] { Constants.DefaultDeviceInputName }; + return new[] { Constants.DefaultDeviceInputName }; } +#pragma warning restore CA1822 /// <summary> /// Open a new <see cref="AudioInputSystem"/>. @@ -205,7 +207,7 @@ namespace Ryujinx.Audio.Input IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Input, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount); - AudioInputSystem audioIn = new AudioInputSystem(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]); + AudioInputSystem audioIn = new(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]); ResultCode result = audioIn.Initialize(inputDeviceName, sampleFormat, ref parameter, sessionId); @@ -238,6 +240,8 @@ namespace Ryujinx.Audio.Input public void Dispose() { + GC.SuppressFinalize(this); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); @@ -263,4 +267,4 @@ namespace Ryujinx.Audio.Input } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Input/AudioInputSystem.cs b/src/Ryujinx.Audio/Input/AudioInputSystem.cs index 33364e28a..34623b34f 100644 --- a/src/Ryujinx.Audio/Input/AudioInputSystem.cs +++ b/src/Ryujinx.Audio/Input/AudioInputSystem.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Input /// <summary> /// The session the <see cref="AudioInputSystem"/>. /// </summary> - private AudioDeviceSession _session; + private readonly AudioDeviceSession _session; /// <summary> /// The target device name of the <see cref="AudioInputSystem"/>. @@ -43,7 +43,7 @@ namespace Ryujinx.Audio.Input /// <summary> /// The <see cref="AudioInputManager"/> owning this. /// </summary> - private AudioInputManager _manager; + private readonly AudioInputManager _manager; /// <summary> /// The lock of the parent. @@ -90,11 +90,13 @@ namespace Ryujinx.Audio.Input { return ResultCode.DeviceNotFound; } - else if (configuration.SampleRate != 0 && configuration.SampleRate != Constants.TargetSampleRate) + + if (configuration.SampleRate != 0 && configuration.SampleRate != Constants.TargetSampleRate) { return ResultCode.UnsupportedSampleRate; } - else if (configuration.ChannelCount != 0 && configuration.ChannelCount != 1 && configuration.ChannelCount != 2 && configuration.ChannelCount != 6) + + if (configuration.ChannelCount != 0 && configuration.ChannelCount != 1 && configuration.ChannelCount != 2 && configuration.ChannelCount != 6) { return ResultCode.UnsupportedChannelConfiguration; } @@ -185,11 +187,11 @@ namespace Ryujinx.Audio.Input { lock (_parentLock) { - AudioBuffer buffer = new AudioBuffer + AudioBuffer buffer = new() { BufferTag = bufferTag, DataPointer = userBuffer.Data, - DataSize = userBuffer.DataSize + DataSize = userBuffer.DataSize, }; if (_session.AppendBuffer(buffer)) @@ -213,14 +215,14 @@ namespace Ryujinx.Audio.Input { lock (_parentLock) { - AudioBuffer buffer = new AudioBuffer + AudioBuffer buffer = new() { BufferTag = bufferTag, DataPointer = userBuffer.Data, - DataSize = userBuffer.DataSize + DataSize = userBuffer.DataSize, }; - if (_session.AppendUacBuffer(buffer, handle)) + if (AudioDeviceSession.AppendUacBuffer(buffer, handle)) { return ResultCode.Success; } @@ -373,6 +375,8 @@ namespace Ryujinx.Audio.Input public void Dispose() { + GC.SuppressFinalize(this); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); @@ -389,4 +393,4 @@ namespace Ryujinx.Audio.Input } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs b/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs index 552f1ab24..576954b96 100644 --- a/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs +++ b/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs @@ -6,12 +6,12 @@ namespace Ryujinx.Audio.Integration { public class HardwareDeviceImpl : IHardwareDevice { - private IHardwareDeviceSession _session; - private uint _channelCount; - private uint _sampleRate; + private readonly IHardwareDeviceSession _session; + private readonly uint _channelCount; + private readonly uint _sampleRate; private uint _currentBufferTag; - private byte[] _buffer; + private readonly byte[] _buffer; public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate, float volume) { @@ -36,7 +36,7 @@ namespace Ryujinx.Audio.Integration DataSize = (ulong)_buffer.Length, }); - _currentBufferTag = _currentBufferTag % 4; + _currentBufferTag %= 4; } public void SetVolume(float volume) @@ -61,6 +61,7 @@ namespace Ryujinx.Audio.Integration public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -72,4 +73,4 @@ namespace Ryujinx.Audio.Integration } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Integration/IHardwareDevice.cs b/src/Ryujinx.Audio/Integration/IHardwareDevice.cs index 300de8c5d..f9ade9dbc 100644 --- a/src/Ryujinx.Audio/Integration/IHardwareDevice.cs +++ b/src/Ryujinx.Audio/Integration/IHardwareDevice.cs @@ -52,4 +52,4 @@ namespace Ryujinx.Audio.Integration return channelCount != Constants.ChannelCountMax; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs index 4ed179519..9c812fb9a 100644 --- a/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Integration public enum Direction { Input, - Output + Output, } IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume = 1f); @@ -33,4 +33,4 @@ namespace Ryujinx.Audio.Integration return this; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs b/src/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs index 400daec00..f29c109cb 100644 --- a/src/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs @@ -25,4 +25,4 @@ namespace Ryujinx.Audio.Integration void PrepareToClose(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Integration/IWritableEvent.cs b/src/Ryujinx.Audio/Integration/IWritableEvent.cs index 9a12e3d28..a3b3bc0bc 100644 --- a/src/Ryujinx.Audio/Integration/IWritableEvent.cs +++ b/src/Ryujinx.Audio/Integration/IWritableEvent.cs @@ -15,4 +15,4 @@ namespace Ryujinx.Audio.Integration /// </summary> void Clear(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Output/AudioOutputManager.cs b/src/Ryujinx.Audio/Output/AudioOutputManager.cs index bc2fc6f43..5232357bb 100644 --- a/src/Ryujinx.Audio/Output/AudioOutputManager.cs +++ b/src/Ryujinx.Audio/Output/AudioOutputManager.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Output /// <summary> /// The session ids allocation table. /// </summary> - private int[] _sessionIds; + private readonly int[] _sessionIds; /// <summary> /// The device driver. @@ -39,7 +39,7 @@ namespace Ryujinx.Audio.Output /// <summary> /// The <see cref="AudioOutputSystem"/> session instances. /// </summary> - private AudioOutputSystem[] _sessions; + private readonly AudioOutputSystem[] _sessions; /// <summary> /// The count of active sessions. @@ -165,10 +165,12 @@ namespace Ryujinx.Audio.Output /// Get the list of all audio outputs name. /// </summary> /// <returns>The list of all audio outputs name</returns> +#pragma warning disable CA1822 // Mark member as static public string[] ListAudioOuts() { - return new string[] { Constants.DefaultDeviceOutputName }; + return new[] { Constants.DefaultDeviceOutputName }; } +#pragma warning restore CA1822 /// <summary> /// Open a new <see cref="AudioOutputSystem"/>. @@ -182,6 +184,7 @@ namespace Ryujinx.Audio.Output /// <param name="parameter">The user configuration</param> /// <param name="appletResourceUserId">The applet resource user id of the application</param> /// <param name="processHandle">The process handle of the application</param> + /// <param name="volume">The volume level to request</param> /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns> public ResultCode OpenAudioOut(out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, @@ -200,7 +203,7 @@ namespace Ryujinx.Audio.Output IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount, volume); - AudioOutputSystem audioOut = new AudioOutputSystem(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]); + AudioOutputSystem audioOut = new(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]); ResultCode result = audioOut.Initialize(inputDeviceName, sampleFormat, ref parameter, sessionId); @@ -268,6 +271,8 @@ namespace Ryujinx.Audio.Output public void Dispose() { + GC.SuppressFinalize(this); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); @@ -293,4 +298,4 @@ namespace Ryujinx.Audio.Output } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Output/AudioOutputSystem.cs b/src/Ryujinx.Audio/Output/AudioOutputSystem.cs index 8378f33f8..f9b9bdcf1 100644 --- a/src/Ryujinx.Audio/Output/AudioOutputSystem.cs +++ b/src/Ryujinx.Audio/Output/AudioOutputSystem.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Output /// <summary> /// The session the <see cref="AudioOutputSystem"/>. /// </summary> - private AudioDeviceSession _session; + private readonly AudioDeviceSession _session; /// <summary> /// The target device name of the <see cref="AudioOutputSystem"/>. @@ -43,7 +43,7 @@ namespace Ryujinx.Audio.Output /// <summary> /// The <see cref="AudioOutputManager"/> owning this. /// </summary> - private AudioOutputManager _manager; + private readonly AudioOutputManager _manager; /// <summary> /// THe lock of the parent. @@ -90,11 +90,13 @@ namespace Ryujinx.Audio.Output { return ResultCode.DeviceNotFound; } - else if (configuration.SampleRate != 0 && configuration.SampleRate != Constants.TargetSampleRate) + + if (configuration.SampleRate != 0 && configuration.SampleRate != Constants.TargetSampleRate) { return ResultCode.UnsupportedSampleRate; } - else if (configuration.ChannelCount != 0 && configuration.ChannelCount != 1 && configuration.ChannelCount != 2 && configuration.ChannelCount != 6) + + if (configuration.ChannelCount != 0 && configuration.ChannelCount != 1 && configuration.ChannelCount != 2 && configuration.ChannelCount != 6) { return ResultCode.UnsupportedChannelConfiguration; } @@ -185,11 +187,11 @@ namespace Ryujinx.Audio.Output { lock (_parentLock) { - AudioBuffer buffer = new AudioBuffer + AudioBuffer buffer = new() { BufferTag = bufferTag, DataPointer = userBuffer.Data, - DataSize = userBuffer.DataSize + DataSize = userBuffer.DataSize, }; if (_session.AppendBuffer(buffer)) @@ -346,6 +348,8 @@ namespace Ryujinx.Audio.Output public void Dispose() { + GC.SuppressFinalize(this); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); @@ -362,4 +366,4 @@ namespace Ryujinx.Audio.Output } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs b/src/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs index 966474052..b7b97d5d8 100644 --- a/src/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs +++ b/src/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs @@ -10,4 +10,4 @@ namespace Ryujinx.Audio.Renderer.Common public ulong ReturnBufferInfo; public ulong ReturnBufferInfoBase; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs b/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs index 270f84d5b..b0963c935 100644 --- a/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// Reserved/padding. /// </summary> - private uint _padding; + private readonly uint _padding; /// <summary> /// The flags given controlling behaviour of the audio renderer @@ -38,7 +38,7 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// Reserved/padding. /// </summary> - private uint _padding; + private readonly uint _padding; /// <summary> /// Extra information given with the <see cref="ResultCode"/> @@ -47,4 +47,4 @@ namespace Ryujinx.Audio.Renderer.Common public ulong ExtraErrorInfo; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs b/src/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs index 24a9350fc..3beb62399 100644 --- a/src/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs +++ b/src/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs @@ -147,4 +147,4 @@ namespace Ryujinx.Audio.Renderer.Common return _nodeCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/EffectType.cs b/src/Ryujinx.Audio/Renderer/Common/EffectType.cs index 7128db4ce..7c8713b12 100644 --- a/src/Ryujinx.Audio/Renderer/Common/EffectType.cs +++ b/src/Ryujinx.Audio/Renderer/Common/EffectType.cs @@ -55,4 +55,4 @@ namespace Ryujinx.Audio.Renderer.Common /// </summary> Compressor, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs b/src/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs index 590731c3b..6d835879c 100644 --- a/src/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs +++ b/src/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs @@ -38,6 +38,6 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// The memory pool is released. (client side only) /// </summary> - Released = 6 + Released = 6, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs b/src/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs index a999e3ad1..76fba54b6 100644 --- a/src/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs @@ -25,4 +25,4 @@ namespace Ryujinx.Audio.Renderer.Common return (nodeId >> 16) & 0xFFF; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/NodeIdType.cs b/src/Ryujinx.Audio/Renderer/Common/NodeIdType.cs index 69b58f6bc..b226da14f 100644 --- a/src/Ryujinx.Audio/Renderer/Common/NodeIdType.cs +++ b/src/Ryujinx.Audio/Renderer/Common/NodeIdType.cs @@ -28,6 +28,6 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// Performance monitoring related node id (performance commands) /// </summary> - Performance = 15 + Performance = 15, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/NodeStates.cs b/src/Ryujinx.Audio/Renderer/Common/NodeStates.cs index 45748d606..75290a741 100644 --- a/src/Ryujinx.Audio/Renderer/Common/NodeStates.cs +++ b/src/Ryujinx.Audio/Renderer/Common/NodeStates.cs @@ -53,17 +53,17 @@ namespace Ryujinx.Audio.Renderer.Common } private int _nodeCount; - private EdgeMatrix _discovered; - private EdgeMatrix _finished; + private readonly EdgeMatrix _discovered; + private readonly EdgeMatrix _finished; private Memory<int> _resultArray; - private Stack _stack; + private readonly Stack _stack; private int _tsortResultIndex; private enum NodeState : byte { Unknown, Discovered, - Finished + Finished, } public NodeStates() @@ -88,16 +88,16 @@ namespace Ryujinx.Audio.Renderer.Common int edgeMatrixWorkBufferSize = EdgeMatrix.GetWorkBufferSize(nodeCount); - _discovered.Initialize(nodeStatesWorkBuffer.Slice(0, edgeMatrixWorkBufferSize), nodeCount); + _discovered.Initialize(nodeStatesWorkBuffer[..edgeMatrixWorkBufferSize], nodeCount); _finished.Initialize(nodeStatesWorkBuffer.Slice(edgeMatrixWorkBufferSize, edgeMatrixWorkBufferSize), nodeCount); - nodeStatesWorkBuffer = nodeStatesWorkBuffer.Slice(edgeMatrixWorkBufferSize * 2); + nodeStatesWorkBuffer = nodeStatesWorkBuffer[(edgeMatrixWorkBufferSize * 2)..]; - _resultArray = SpanMemoryManager<int>.Cast(nodeStatesWorkBuffer.Slice(0, sizeof(int) * nodeCount)); + _resultArray = SpanMemoryManager<int>.Cast(nodeStatesWorkBuffer[..(sizeof(int) * nodeCount)]); - nodeStatesWorkBuffer = nodeStatesWorkBuffer.Slice(sizeof(int) * nodeCount); + nodeStatesWorkBuffer = nodeStatesWorkBuffer[(sizeof(int) * nodeCount)..]; - Memory<int> stackWorkBuffer = SpanMemoryManager<int>.Cast(nodeStatesWorkBuffer.Slice(0, Stack.CalcBufferSize(nodeCount * nodeCount))); + Memory<int> stackWorkBuffer = SpanMemoryManager<int>.Cast(nodeStatesWorkBuffer[..Stack.CalcBufferSize(nodeCount * nodeCount)]); _stack.Reset(stackWorkBuffer, nodeCount * nodeCount); } @@ -120,7 +120,8 @@ namespace Ryujinx.Audio.Renderer.Common return NodeState.Discovered; } - else if (_finished.Test(index)) + + if (_finished.Test(index)) { Debug.Assert(!_discovered.Test(index)); @@ -158,7 +159,7 @@ namespace Ryujinx.Audio.Renderer.Common public ReadOnlySpan<int> GetTsortResult() { - return _resultArray.Span.Slice(0, _tsortResultIndex); + return _resultArray.Span[.._tsortResultIndex]; } public bool Sort(EdgeMatrix edgeMatrix) @@ -226,4 +227,4 @@ namespace Ryujinx.Audio.Renderer.Common return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs b/src/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs index 805d55183..bde32a709 100644 --- a/src/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs +++ b/src/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs @@ -15,6 +15,6 @@ namespace Ryujinx.Audio.Renderer.Common PcmFloat, Limiter, CaptureBuffer, - Compressor + Compressor, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs b/src/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs index bde72aaed..e32095e62 100644 --- a/src/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs +++ b/src/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Audio.Renderer.Common Voice, SubMix, FinalMix, - Sink + Sink, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/PlayState.cs b/src/Ryujinx.Audio/Renderer/Common/PlayState.cs index 4a6929e03..a83d16afb 100644 --- a/src/Ryujinx.Audio/Renderer/Common/PlayState.cs +++ b/src/Ryujinx.Audio/Renderer/Common/PlayState.cs @@ -18,6 +18,6 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// The user request the voice to be paused. /// </summary> - Pause + Pause, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs b/src/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs index aa7685621..c7443cc49 100644 --- a/src/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs +++ b/src/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs @@ -28,6 +28,6 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// No early reflection. /// </summary> - Disabled + Disabled, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs b/src/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs index 8aa88165a..78f91cf08 100644 --- a/src/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs +++ b/src/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs @@ -33,6 +33,6 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// Max delay. (used for delay line limits) /// </summary> - Limit = NoDelay + Limit = NoDelay, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/SinkType.cs b/src/Ryujinx.Audio/Renderer/Common/SinkType.cs index 2e17201e7..5a08df4e1 100644 --- a/src/Ryujinx.Audio/Renderer/Common/SinkType.cs +++ b/src/Ryujinx.Audio/Renderer/Common/SinkType.cs @@ -18,6 +18,6 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// The sink is a circular buffer. /// </summary> - CircularBuffer + CircularBuffer, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs b/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs index 70dbfa947..7efe3b02b 100644 --- a/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs +++ b/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Memory; using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Common @@ -19,7 +20,9 @@ namespace Ryujinx.Audio.Renderer.Common public uint Unknown24; public uint RenderInfoSize; - private unsafe fixed int _reserved[4]; +#pragma warning disable IDE0051, CS0169 // Remove unused field + private Array4<int> _reserved; +#pragma warning restore IDE0051, CS0169 public uint TotalSize; @@ -30,4 +33,4 @@ namespace Ryujinx.Audio.Renderer.Common TotalSize = (uint)Unsafe.SizeOf<UpdateDataHeader>(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs b/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs index f52c2f4c4..608381af1 100644 --- a/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs +++ b/src/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs @@ -101,4 +101,4 @@ namespace Ryujinx.Audio.Renderer.Common } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs b/src/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs index 0d00e8384..5109d3fa0 100644 --- a/src/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs @@ -1,5 +1,4 @@ using System.Runtime.InteropServices; - using DspAddr = System.UInt64; namespace Ryujinx.Audio.Renderer.Common @@ -77,6 +76,6 @@ namespace Ryujinx.Audio.Renderer.Common /// <summary> /// Padding/Reserved. /// </summary> - private ushort _padding; + private readonly ushort _padding; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs b/src/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs index f35dbec7f..54673f2f6 100644 --- a/src/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs +++ b/src/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common if (size != 0) { - ulong alignedOffset = BitUtils.AlignUp<ulong>(Offset, (ulong)align); + ulong alignedOffset = BitUtils.AlignUp(Offset, (ulong)align); if (alignedOffset + size <= (ulong)BackingMemory.Length) { @@ -32,7 +32,7 @@ namespace Ryujinx.Audio.Renderer.Common Offset = alignedOffset + size; // Clear the memory to be sure that is does not contain any garbage. - result.Span.Fill(0); + result.Span.Clear(); return result; } @@ -55,7 +55,7 @@ namespace Ryujinx.Audio.Renderer.Common public static ulong GetTargetSize<T>(ulong currentSize, ulong count, int align) where T : unmanaged { - return BitUtils.AlignUp<ulong>(currentSize, (ulong)align) + (ulong)Unsafe.SizeOf<T>() * count; + return BitUtils.AlignUp(currentSize, (ulong)align) + (ulong)Unsafe.SizeOf<T>() * count; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs index 90692b004..91956fda6 100644 --- a/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs +++ b/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs @@ -12,11 +12,11 @@ namespace Ryujinx.Audio.Renderer.Device /// </summary> public static readonly VirtualDevice[] Devices = new VirtualDevice[5] { - new VirtualDevice("AudioStereoJackOutput", 2, true), - new VirtualDevice("AudioBuiltInSpeakerOutput", 2, false), - new VirtualDevice("AudioTvOutput", 6, false), - new VirtualDevice("AudioUsbDeviceOutput", 2, true), - new VirtualDevice("AudioExternalOutput", 6, true), + new("AudioStereoJackOutput", 2, true), + new("AudioBuiltInSpeakerOutput", 2, false), + new("AudioTvOutput", 6, false), + new("AudioUsbDeviceOutput", 2, true), + new("AudioExternalOutput", 6, true), }; /// <summary> @@ -86,4 +86,4 @@ namespace Ryujinx.Audio.Renderer.Device return Name; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs index db35d26d2..09fa71eda 100644 --- a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs +++ b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs @@ -24,4 +24,4 @@ namespace Ryujinx.Audio.Renderer.Device Device = virtualDevice; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs index 696af90fa..4ad70619e 100644 --- a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs +++ b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs @@ -11,13 +11,15 @@ namespace Ryujinx.Audio.Renderer.Device /// <summary> /// The session registry, used to store the sessions of a given AppletResourceId. /// </summary> - private Dictionary<ulong, VirtualDeviceSession[]> _sessionsRegistry = new Dictionary<ulong, VirtualDeviceSession[]>(); + private readonly Dictionary<ulong, VirtualDeviceSession[]> _sessionsRegistry = new(); /// <summary> /// The default <see cref="VirtualDevice"/>. /// </summary> /// <remarks>This is used when the USB device is the default one on older revision.</remarks> +#pragma warning disable CA1822 // Mark member as static public VirtualDevice DefaultDevice => VirtualDevice.Devices[0]; +#pragma warning restore CA1822 /// <summary> /// The current active <see cref="VirtualDevice"/>. @@ -76,4 +78,4 @@ namespace Ryujinx.Audio.Renderer.Device return virtualDeviceSession; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs index 2680dcb1e..5cb4509ff 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs @@ -12,7 +12,9 @@ namespace Ryujinx.Audio.Renderer.Dsp private const int SamplesPerFrame = 14; private const int NibblesPerFrame = SamplesPerFrame + 2; private const int BytesPerFrame = 8; +#pragma warning disable IDE0051 // Remove unused private member private const int BitsPerFrame = BytesPerFrame * 8; +#pragma warning restore IDE0051 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint GetAdpcmDataSize(int sampleCount) @@ -64,10 +66,14 @@ namespace Ryujinx.Audio.Renderer.Dsp private static short Saturate(int value) { if (value > short.MaxValue) + { value = short.MaxValue; + } if (value < short.MinValue) + { value = short.MinValue; + } return (short)value; } @@ -109,7 +115,7 @@ namespace Ryujinx.Audio.Renderer.Dsp ReadOnlySpan<byte> targetInput; - targetInput = input.Slice(nibbles / 2); + targetInput = input[(nibbles / 2)..]; while (remaining > 0) { @@ -213,4 +219,4 @@ namespace Ryujinx.Audio.Renderer.Dsp return decodedCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs index 899c2ef97..9c885b2cf 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Dsp Start, Stop, RenderStart, - RenderEnd + RenderEnd, } private class RendererSession @@ -36,7 +36,7 @@ namespace Ryujinx.Audio.Renderer.Dsp private long _lastTime; private long _playbackEnds; - private ManualResetEvent _event; + private readonly ManualResetEvent _event; private ManualResetEvent _pauseEvent; @@ -45,6 +45,7 @@ namespace Ryujinx.Audio.Renderer.Dsp _event = new ManualResetEvent(false); } +#pragma warning disable IDE0051 // Remove unused private member private static uint GetHardwareChannelCount(IHardwareDeviceDriver deviceDriver) { // Get the real device driver (In case the compat layer is on top of it). @@ -54,12 +55,11 @@ namespace Ryujinx.Audio.Renderer.Dsp { return 6; } - else - { - // NOTE: We default to stereo as this will get downmixed to mono by the compat layer if it's not compatible. - return 2; - } + + // NOTE: We default to stereo as this will get downmixed to mono by the compat layer if it's not compatible. + return 2; } +#pragma warning restore IDE0051 public void Start(IHardwareDeviceDriver deviceDriver, float volume) { @@ -110,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Dsp { CommandList = commands, RenderingLimit = renderingLimit, - AppletResourceId = appletResourceId + AppletResourceId = appletResourceId, }; } @@ -171,7 +171,7 @@ namespace Ryujinx.Audio.Renderer.Dsp { _workerThread = new Thread(Work) { - Name = "AudioProcessor.Worker" + Name = "AudioProcessor.Worker", }; _workerThread.Start(); @@ -260,6 +260,7 @@ namespace Ryujinx.Audio.Renderer.Dsp public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -271,4 +272,4 @@ namespace Ryujinx.Audio.Renderer.Dsp } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs index 98460ff1a..1a51a1fbd 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs @@ -80,4 +80,4 @@ namespace Ryujinx.Audio.Renderer.Dsp } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs index 1fe6069f7..51a12b4e7 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs @@ -1,7 +1,9 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Server.Voice; using System; using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command { @@ -29,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public DecodingBehaviour DecodingBehaviour { get; } - public AdpcmDataSourceCommandVersion1(ref Server.Voice.VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, int nodeId) + public AdpcmDataSourceCommandVersion1(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, int nodeId) { Enabled = true; NodeId = nodeId; @@ -57,7 +59,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex); - DataSourceHelper.WaveBufferInformation info = new DataSourceHelper.WaveBufferInformation + DataSourceHelper.WaveBufferInformation info = new() { SourceSampleRate = SampleRate, SampleFormat = SampleFormat.Adpcm, @@ -72,4 +74,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command DataSourceHelper.ProcessWaveBuffers(context.MemoryManager, outputBuffer, ref info, WaveBuffers, ref State.Span[0], context.SampleRate, (int)context.SampleCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs index 5c3c0324b..7ed32800f 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs @@ -155,7 +155,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (readResult != context.SampleCount) { - outputBuffer.Slice((int)readResult, (int)context.SampleCount - (int)readResult).Fill(0); + outputBuffer[(int)readResult..(int)context.SampleCount].Clear(); } } else @@ -170,4 +170,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs index b994c1cb9..f56dd70e3 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs @@ -48,4 +48,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command BiquadFilterHelper.ProcessBiquadFilter(ref _parameter, ref state, outputBuffer, inputBuffer, context.SampleCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs index da1cb2546..01bff1e7d 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CaptureBufferCommand.cs @@ -133,4 +133,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs index 9e653e804..f0f85b0b2 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs @@ -21,4 +21,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command context.ClearBuffers(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs index 2cbed9c2b..19a9576f7 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command return (IntPtr)((float*)_buffersMemoryHandle.Pointer + index * _sampleCount); } - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(index), index, null); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -149,7 +149,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public void Dispose() { + GC.SuppressFinalize(this); _buffersMemoryHandle.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs index 9ce181b17..098a04a04 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs @@ -32,6 +32,6 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command LimiterVersion2, GroupedBiquadFilter, CaptureBuffer, - Compressor + Compressor, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs index 34231e614..01291852e 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs @@ -1,6 +1,7 @@ using Ryujinx.Audio.Renderer.Dsp.Effect; using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter.Effect; +using Ryujinx.Audio.Renderer.Server.Effect; using System; using System.Diagnostics; @@ -51,11 +52,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (_parameter.Status == Server.Effect.UsageState.Invalid) + if (_parameter.Status == UsageState.Invalid) { state = new CompressorState(ref _parameter); } - else if (_parameter.Status == Server.Effect.UsageState.New) + else if (_parameter.Status == UsageState.New) { state.UpdateParameter(ref _parameter); } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs index 7237fddf6..3f6aa8390 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs @@ -27,4 +27,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command context.CopyBuffer(OutputBufferIndex, InputBufferIndex); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs index c1503b6a0..e82d403bf 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs @@ -1,7 +1,9 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Server.Voice; using System; using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command { @@ -37,7 +39,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public SampleRateConversionQuality SrcQuality { get; } - public DataSourceVersion2Command(ref Server.Voice.VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public DataSourceVersion2Command(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { Enabled = true; NodeId = nodeId; @@ -72,24 +74,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command private static CommandType GetCommandTypeBySampleFormat(SampleFormat sampleFormat) { - switch (sampleFormat) + return sampleFormat switch { - case SampleFormat.Adpcm: - return CommandType.AdpcmDataSourceVersion2; - case SampleFormat.PcmInt16: - return CommandType.PcmInt16DataSourceVersion2; - case SampleFormat.PcmFloat: - return CommandType.PcmFloatDataSourceVersion2; - default: - throw new NotImplementedException($"{sampleFormat}"); - } + SampleFormat.Adpcm => CommandType.AdpcmDataSourceVersion2, + SampleFormat.PcmInt16 => CommandType.PcmInt16DataSourceVersion2, + SampleFormat.PcmFloat => CommandType.PcmFloatDataSourceVersion2, + _ => throw new NotImplementedException($"{sampleFormat}"), + }; } public void Process(CommandList context) { Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex); - DataSourceHelper.WaveBufferInformation info = new DataSourceHelper.WaveBufferInformation + DataSourceHelper.WaveBufferInformation info = new() { SourceSampleRate = SampleRate, SampleFormat = SampleFormat, @@ -99,10 +97,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ExtraParameterSize = ExtraParameterSize, ChannelIndex = (int)ChannelIndex, ChannelCount = (int)ChannelCount, - SrcQuality = SrcQuality + SrcQuality = SrcQuality, }; DataSourceHelper.ProcessWaveBuffers(context.MemoryManager, outputBuffer, ref info, WaveBuffers, ref State.Span[0], context.SampleRate, (int)context.SampleCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index e7e179389..003806cf7 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -87,18 +87,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision); - Matrix2x2 delayFeedback = new Matrix2x2(delayFeedbackBaseGain, delayFeedbackCrossGain, + Matrix2x2 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain); for (int i = 0; i < sampleCount; i++) { - Vector2 channelInput = new Vector2 + Vector2 channelInput = new() { X = *((float*)inputBuffers[0] + i) * 64, Y = *((float*)inputBuffers[1] + i) * 64, }; - Vector2 delayLineValues = new Vector2() + Vector2 delayLineValues = new() { X = state.DelayLines[0].Read(), Y = state.DelayLines[1].Read(), @@ -124,7 +124,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision); - Matrix4x4 delayFeedback = new Matrix4x4(delayFeedbackBaseGain, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, + Matrix4x4 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain); @@ -132,20 +132,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command for (int i = 0; i < sampleCount; i++) { - Vector4 channelInput = new Vector4 + Vector4 channelInput = new() { X = *((float*)inputBuffers[0] + i) * 64, Y = *((float*)inputBuffers[1] + i) * 64, Z = *((float*)inputBuffers[2] + i) * 64, - W = *((float*)inputBuffers[3] + i) * 64 + W = *((float*)inputBuffers[3] + i) * 64, }; - Vector4 delayLineValues = new Vector4() + Vector4 delayLineValues = new() { X = state.DelayLines[0].Read(), Y = state.DelayLines[1].Read(), Z = state.DelayLines[2].Read(), - W = state.DelayLines[3].Read() + W = state.DelayLines[3].Read(), }; Vector4 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; @@ -171,7 +171,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision); float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision); - Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f, + Matrix6x6 delayFeedback = new(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f, @@ -180,24 +180,24 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command for (int i = 0; i < sampleCount; i++) { - Vector6 channelInput = new Vector6 + Vector6 channelInput = new() { X = *((float*)inputBuffers[0] + i) * 64, Y = *((float*)inputBuffers[1] + i) * 64, Z = *((float*)inputBuffers[2] + i) * 64, W = *((float*)inputBuffers[3] + i) * 64, V = *((float*)inputBuffers[4] + i) * 64, - U = *((float*)inputBuffers[5] + i) * 64 + U = *((float*)inputBuffers[5] + i) * 64, }; - Vector6 delayLineValues = new Vector6 + Vector6 delayLineValues = new() { X = state.DelayLines[0].Read(), Y = state.DelayLines[1].Read(), Z = state.DelayLines[2].Read(), W = state.DelayLines[3].Read(), V = state.DelayLines[4].Read(), - U = state.DelayLines[5].Read() + U = state.DelayLines[5].Read(), }; Vector6 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs index 1dba56e6c..ff38f38ca 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs @@ -55,17 +55,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command return -depopValue; } - else + + for (int i = 0; i < sampleCount; i++) { - for (int i = 0; i < sampleCount; i++) - { - depopValue = FloatingPointHelper.MultiplyRoundDown(Decay, depopValue); + depopValue = FloatingPointHelper.MultiplyRoundDown(Decay, depopValue); - buffer[i] += depopValue; - } - - return depopValue; + buffer[i] += depopValue; } + + return depopValue; } public void Process(CommandList context) @@ -89,4 +87,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs index d02f7c121..c64bbdc57 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs @@ -54,4 +54,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs index 79cefcc53..8997b0dbd 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs @@ -65,4 +65,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command context.ClearBuffer(OutputBufferIndices[5]); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs index b190cc10d..7af851bdc 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/GroupedBiquadFilterCommand.cs @@ -14,11 +14,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public uint EstimatedProcessingTime { get; set; } - private BiquadFilterParameter[] _parameters; - private Memory<BiquadFilterState> _biquadFilterStates; - private int _inputBufferIndex; - private int _outputBufferIndex; - private bool[] _isInitialized; + private readonly BiquadFilterParameter[] _parameters; + private readonly Memory<BiquadFilterState> _biquadFilterStates; + private readonly int _inputBufferIndex; + private readonly int _outputBufferIndex; + private readonly bool[] _isInitialized; public GroupedBiquadFilterCommand(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId) { @@ -59,4 +59,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs index d281e6e9f..34a62c58d 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs index a464ad704..3ba0b5884 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter.Effect; +using Ryujinx.Audio.Renderer.Server.Effect; using System; using System.Diagnostics; @@ -50,13 +51,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (Parameter.Status == Server.Effect.UsageState.Invalid) + if (Parameter.Status == UsageState.Invalid) { state = new LimiterState(ref _parameter, WorkBuffer); } - else if (Parameter.Status == Server.Effect.UsageState.New) + else if (Parameter.Status == UsageState.New) { - state.UpdateParameter(ref _parameter); + LimiterState.UpdateParameter(ref _parameter); } } @@ -141,4 +142,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs index 950de97b8..682098670 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs @@ -1,6 +1,7 @@ using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter.Effect; +using Ryujinx.Audio.Renderer.Server.Effect; using System; using System.Diagnostics; using System.Runtime.InteropServices; @@ -54,13 +55,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (Parameter.Status == Server.Effect.UsageState.Invalid) + if (Parameter.Status == UsageState.Invalid) { state = new LimiterState(ref _parameter, WorkBuffer); } - else if (Parameter.Status == Server.Effect.UsageState.New) + else if (Parameter.Status == UsageState.New) { - state.UpdateParameter(ref _parameter); + LimiterState.UpdateParameter(ref _parameter); } } @@ -160,4 +161,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs index 2616bda57..c701f80eb 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs @@ -134,4 +134,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ProcessMix(outputBuffer, inputBuffer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs index 76a1aba25..f77a233e1 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs @@ -65,4 +65,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command State.Span[0].LastSamples[LastSampleIndex] = ProcessMixRamp(outputBuffer, inputBuffer, (int)context.SampleCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs index e348e3588..3c7dd63b2 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private float ProcessMixRampGrouped(Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, float volume0, float volume1, int sampleCount) + private static float ProcessMixRampGrouped(Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, float volume0, float volume1, int sampleCount) { float ramp = (volume1 - volume0) / sampleCount; float volume = volume0; @@ -88,4 +88,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs index 7cec7d2ab..585edc058 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs @@ -1,7 +1,9 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Server.Voice; using System; using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command { @@ -28,7 +30,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public Memory<VoiceUpdateState> State { get; } public DecodingBehaviour DecodingBehaviour { get; } - public PcmFloatDataSourceCommandVersion1(ref Server.Voice.VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public PcmFloatDataSourceCommandVersion1(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { Enabled = true; NodeId = nodeId; @@ -56,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex); - DataSourceHelper.WaveBufferInformation info = new DataSourceHelper.WaveBufferInformation + DataSourceHelper.WaveBufferInformation info = new() { SourceSampleRate = SampleRate, SampleFormat = SampleFormat.PcmFloat, @@ -71,4 +73,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command DataSourceHelper.ProcessWaveBuffers(context.MemoryManager, outputBuffer, ref info, WaveBuffers, ref State.Span[0], context.SampleRate, (int)context.SampleCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs index dfe9814fe..6f01219f3 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs @@ -1,7 +1,9 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Server.Voice; using System; using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using WaveBuffer = Ryujinx.Audio.Renderer.Common.WaveBuffer; namespace Ryujinx.Audio.Renderer.Dsp.Command { @@ -28,7 +30,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public Memory<VoiceUpdateState> State { get; } public DecodingBehaviour DecodingBehaviour { get; } - public PcmInt16DataSourceCommandVersion1(ref Server.Voice.VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) + public PcmInt16DataSourceCommandVersion1(ref VoiceState serverState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { Enabled = true; NodeId = nodeId; @@ -56,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { Span<float> outputBuffer = context.GetBuffer(OutputBufferIndex); - DataSourceHelper.WaveBufferInformation info = new DataSourceHelper.WaveBufferInformation + DataSourceHelper.WaveBufferInformation info = new() { SourceSampleRate = SampleRate, SampleFormat = SampleFormat.PcmInt16, @@ -71,4 +73,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command DataSourceHelper.ProcessWaveBuffers(context.MemoryManager, outputBuffer, ref info, WaveBuffers, ref State.Span[0], context.SampleRate, (int)context.SampleCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs index d3e3f8056..d3d2ee306 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { Invalid, Start, - End + End, } public bool Enabled { get; set; } @@ -44,4 +44,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs index d1177e60f..8cdd4843b 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs @@ -9,21 +9,21 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { public class Reverb3dCommand : ICommand { - private static readonly int[] OutputEarlyIndicesTableMono = new int[20] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableMono = new int[20] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; - private static readonly int[] TargetOutputFeedbackIndicesTableMono = new int[1] { 0 }; + private static readonly int[] _outputEarlyIndicesTableMono = new int[20] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableMono = new int[20] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + private static readonly int[] _targetOutputFeedbackIndicesTableMono = new int[1] { 0 }; - private static readonly int[] OutputEarlyIndicesTableStereo = new int[20] { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableStereo = new int[20] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; - private static readonly int[] TargetOutputFeedbackIndicesTableStereo = new int[2] { 0, 1 }; + private static readonly int[] _outputEarlyIndicesTableStereo = new int[20] { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableStereo = new int[20] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + private static readonly int[] _targetOutputFeedbackIndicesTableStereo = new int[2] { 0, 1 }; - private static readonly int[] OutputEarlyIndicesTableQuadraphonic = new int[20] { 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableQuadraphonic = new int[20] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; - private static readonly int[] TargetOutputFeedbackIndicesTableQuadraphonic = new int[4] { 0, 1, 2, 3 }; + private static readonly int[] _outputEarlyIndicesTableQuadraphonic = new int[20] { 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableQuadraphonic = new int[20] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + private static readonly int[] _targetOutputFeedbackIndicesTableQuadraphonic = new int[4] { 0, 1, 2, 3 }; - private static readonly int[] OutputEarlyIndicesTableSurround = new int[40] { 4, 5, 0, 5, 0, 5, 1, 5, 1, 5, 1, 5, 1, 5, 2, 5, 2, 5, 2, 5, 1, 5, 1, 5, 1, 5, 0, 5, 0, 5, 0, 5, 0, 5, 3, 5, 3, 5, 3, 5 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableSurround = new int[40] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19 }; - private static readonly int[] TargetOutputFeedbackIndicesTableSurround = new int[6] { 0, 1, 2, 3, -1, 3 }; + private static readonly int[] _outputEarlyIndicesTableSurround = new int[40] { 4, 5, 0, 5, 0, 5, 1, 5, 1, 5, 1, 5, 1, 5, 2, 5, 2, 5, 2, 5, 1, 5, 1, 5, 1, 5, 0, 5, 0, 5, 0, 5, 0, 5, 3, 5, 3, 5, 3, 5 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableSurround = new int[40] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19 }; + private static readonly int[] _targetOutputFeedbackIndicesTableSurround = new int[6] { 0, 1, 2, 3, -1, 3 }; public bool Enabled { get; set; } @@ -73,25 +73,25 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessReverb3dMono(ref Reverb3dState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { - ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, OutputEarlyIndicesTableMono, TargetEarlyDelayLineIndicesTableMono, TargetOutputFeedbackIndicesTableMono); + ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableMono, _targetEarlyDelayLineIndicesTableMono, _targetOutputFeedbackIndicesTableMono); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessReverb3dStereo(ref Reverb3dState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { - ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, OutputEarlyIndicesTableStereo, TargetEarlyDelayLineIndicesTableStereo, TargetOutputFeedbackIndicesTableStereo); + ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableStereo, _targetEarlyDelayLineIndicesTableStereo, _targetOutputFeedbackIndicesTableStereo); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessReverb3dQuadraphonic(ref Reverb3dState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { - ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, OutputEarlyIndicesTableQuadraphonic, TargetEarlyDelayLineIndicesTableQuadraphonic, TargetOutputFeedbackIndicesTableQuadraphonic); + ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableQuadraphonic, _targetEarlyDelayLineIndicesTableQuadraphonic, _targetOutputFeedbackIndicesTableQuadraphonic); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessReverb3dSurround(ref Reverb3dState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount) { - ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, OutputEarlyIndicesTableSurround, TargetEarlyDelayLineIndicesTableSurround, TargetOutputFeedbackIndicesTableSurround); + ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableSurround, _targetEarlyDelayLineIndicesTableSurround, _targetOutputFeedbackIndicesTableSurround); } private unsafe void ProcessReverb3dGeneric(ref Reverb3dState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount, ReadOnlySpan<int> outputEarlyIndicesTable, ReadOnlySpan<int> targetEarlyDelayLineIndicesTable, ReadOnlySpan<int> targetOutputFeedbackIndicesTable) @@ -109,7 +109,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command for (int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) { - outputValues.Fill(0); + outputValues.Clear(); float tapOut = state.PreDelayLine.TapUnsafe(state.ReflectionDelayTime, DelayLineSampleIndexOffset); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs index cd08b838a..f494b3028 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter.Effect; +using Ryujinx.Audio.Renderer.Server.Effect; using System; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -8,25 +9,25 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { public class ReverbCommand : ICommand { - private static readonly int[] OutputEarlyIndicesTableMono = new int[10] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableMono = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - private static readonly int[] OutputIndicesTableMono = new int[4] { 0, 0, 0, 0 }; - private static readonly int[] TargetOutputFeedbackIndicesTableMono = new int[4] { 0, 1, 2, 3 }; + private static readonly int[] _outputEarlyIndicesTableMono = new int[10] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableMono = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + private static readonly int[] _outputIndicesTableMono = new int[4] { 0, 0, 0, 0 }; + private static readonly int[] _targetOutputFeedbackIndicesTableMono = new int[4] { 0, 1, 2, 3 }; - private static readonly int[] OutputEarlyIndicesTableStereo = new int[10] { 0, 0, 1, 1, 0, 1, 0, 0, 1, 1 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableStereo = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - private static readonly int[] OutputIndicesTableStereo = new int[4] { 0, 0, 1, 1 }; - private static readonly int[] TargetOutputFeedbackIndicesTableStereo = new int[4] { 2, 0, 3, 1 }; + private static readonly int[] _outputEarlyIndicesTableStereo = new int[10] { 0, 0, 1, 1, 0, 1, 0, 0, 1, 1 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableStereo = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + private static readonly int[] _outputIndicesTableStereo = new int[4] { 0, 0, 1, 1 }; + private static readonly int[] _targetOutputFeedbackIndicesTableStereo = new int[4] { 2, 0, 3, 1 }; - private static readonly int[] OutputEarlyIndicesTableQuadraphonic = new int[10] { 0, 0, 1, 1, 0, 1, 2, 2, 3, 3 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableQuadraphonic = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - private static readonly int[] OutputIndicesTableQuadraphonic = new int[4] { 0, 1, 2, 3 }; - private static readonly int[] TargetOutputFeedbackIndicesTableQuadraphonic = new int[4] { 0, 1, 2, 3 }; + private static readonly int[] _outputEarlyIndicesTableQuadraphonic = new int[10] { 0, 0, 1, 1, 0, 1, 2, 2, 3, 3 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableQuadraphonic = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + private static readonly int[] _outputIndicesTableQuadraphonic = new int[4] { 0, 1, 2, 3 }; + private static readonly int[] _targetOutputFeedbackIndicesTableQuadraphonic = new int[4] { 0, 1, 2, 3 }; - private static readonly int[] OutputEarlyIndicesTableSurround = new int[20] { 0, 5, 0, 5, 1, 5, 1, 5, 4, 5, 4, 5, 2, 5, 2, 5, 3, 5, 3, 5 }; - private static readonly int[] TargetEarlyDelayLineIndicesTableSurround = new int[20] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9 }; - private static readonly int[] OutputIndicesTableSurround = new int[Constants.ChannelCountMax] { 0, 1, 2, 3, 4, 5 }; - private static readonly int[] TargetOutputFeedbackIndicesTableSurround = new int[Constants.ChannelCountMax] { 0, 1, 2, 3, -1, 3 }; + private static readonly int[] _outputEarlyIndicesTableSurround = new int[20] { 0, 5, 0, 5, 1, 5, 1, 5, 4, 5, 4, 5, 2, 5, 2, 5, 3, 5, 3, 5 }; + private static readonly int[] _targetEarlyDelayLineIndicesTableSurround = new int[20] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9 }; + private static readonly int[] _outputIndicesTableSurround = new int[Constants.ChannelCountMax] { 0, 1, 2, 3, 4, 5 }; + private static readonly int[] _targetOutputFeedbackIndicesTableSurround = new int[Constants.ChannelCountMax] { 0, 1, 2, 3, -1, 3 }; public bool Enabled { get; set; } @@ -82,10 +83,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command outputBuffers, inputBuffers, sampleCount, - OutputEarlyIndicesTableMono, - TargetEarlyDelayLineIndicesTableMono, - TargetOutputFeedbackIndicesTableMono, - OutputIndicesTableMono); + _outputEarlyIndicesTableMono, + _targetEarlyDelayLineIndicesTableMono, + _targetOutputFeedbackIndicesTableMono, + _outputIndicesTableMono); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -95,10 +96,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command outputBuffers, inputBuffers, sampleCount, - OutputEarlyIndicesTableStereo, - TargetEarlyDelayLineIndicesTableStereo, - TargetOutputFeedbackIndicesTableStereo, - OutputIndicesTableStereo); + _outputEarlyIndicesTableStereo, + _targetEarlyDelayLineIndicesTableStereo, + _targetOutputFeedbackIndicesTableStereo, + _outputIndicesTableStereo); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -108,10 +109,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command outputBuffers, inputBuffers, sampleCount, - OutputEarlyIndicesTableQuadraphonic, - TargetEarlyDelayLineIndicesTableQuadraphonic, - TargetOutputFeedbackIndicesTableQuadraphonic, - OutputIndicesTableQuadraphonic); + _outputEarlyIndicesTableQuadraphonic, + _targetEarlyDelayLineIndicesTableQuadraphonic, + _targetOutputFeedbackIndicesTableQuadraphonic, + _outputIndicesTableQuadraphonic); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -121,10 +122,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command outputBuffers, inputBuffers, sampleCount, - OutputEarlyIndicesTableSurround, - TargetEarlyDelayLineIndicesTableSurround, - TargetOutputFeedbackIndicesTableSurround, - OutputIndicesTableSurround); + _outputEarlyIndicesTableSurround, + _targetEarlyDelayLineIndicesTableSurround, + _targetOutputFeedbackIndicesTableSurround, + _outputIndicesTableSurround); } private unsafe void ProcessReverbGeneric(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount, ReadOnlySpan<int> outputEarlyIndicesTable, ReadOnlySpan<int> targetEarlyDelayLineIndicesTable, ReadOnlySpan<int> targetOutputFeedbackIndicesTable, ReadOnlySpan<int> outputIndicesTable) @@ -143,7 +144,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command for (int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) { - outputValues.Fill(0); + outputValues.Clear(); for (int i = 0; i < targetEarlyDelayLineIndicesTable.Length; i++) { @@ -263,11 +264,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (Parameter.Status == Server.Effect.UsageState.Invalid) + if (Parameter.Status == UsageState.Invalid) { state = new ReverbState(ref _parameter, WorkBuffer, IsLongSizePreDelaySupported); } - else if (Parameter.Status == Server.Effect.UsageState.New) + else if (Parameter.Status == UsageState.New) { state.UpdateParameter(ref _parameter); } @@ -276,4 +277,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ProcessReverb(context, ref state); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs index 0870d59ce..8882500cd 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs @@ -67,4 +67,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs index 0628f6d81..5deeb07f1 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs @@ -134,4 +134,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ProcessVolume(outputBuffer, inputBuffer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs index 5c0c88451..bffbcbc63 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs @@ -53,4 +53,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ProcessVolumeRamp(outputBuffer, inputBuffer, (int)context.SampleCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs index 12e0f13ff..98657bd13 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs @@ -76,7 +76,7 @@ namespace Ryujinx.Audio.Renderer.Dsp if (!info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion)) { - voiceState.Pitch.AsSpan().Slice(0, pitchMaxLength).CopyTo(tempBuffer); + voiceState.Pitch.AsSpan()[..pitchMaxLength].CopyTo(tempBuffer); tempBufferIndex += pitchMaxLength; } @@ -107,7 +107,7 @@ namespace Ryujinx.Audio.Renderer.Dsp voiceState.LoopContext = memoryManager.Read<AdpcmLoopContext>(waveBuffer.Context); } - Span<short> tempSpan = tempBuffer.Slice(tempBufferIndex + y); + Span<short> tempSpan = tempBuffer[(tempBufferIndex + y)..]; int decodedSampleCount = -1; @@ -168,7 +168,7 @@ namespace Ryujinx.Audio.Renderer.Dsp decodedSampleCount = PcmHelper.Decode(tempSpan, waveBufferPcmFloat, targetSampleStartOffset, targetSampleEndOffset, info.ChannelIndex, info.ChannelCount); break; default: - Logger.Error?.Print(LogClass.AudioRenderer, $"Unsupported sample format " + info.SampleFormat); + Logger.Error?.Print(LogClass.AudioRenderer, "Unsupported sample format " + info.SampleFormat); break; } @@ -220,7 +220,7 @@ namespace Ryujinx.Audio.Renderer.Dsp } } - Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputBuffer.Slice(i)); + Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputBuffer[i..]); if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion)) { @@ -231,9 +231,9 @@ namespace Ryujinx.Audio.Renderer.Dsp } else { - Span<short> tempSpan = tempBuffer.Slice(tempBufferIndex + y); + Span<short> tempSpan = tempBuffer[(tempBufferIndex + y)..]; - tempSpan.Slice(0, sampleCountToDecode - y).Fill(0); + tempSpan[..(sampleCountToDecode - y)].Clear(); ToFloat(outputBuffer, outputSpanInt, sampleCountToProcess); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs index 37e066bf0..7253fdc92 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs @@ -49,4 +49,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Effect return _delayLine.Tap(sampleIndex); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs index 56890ebe8..8a3590a20 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs @@ -4,8 +4,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Effect { public class DelayLine : IDelayLine { - private float[] _workBuffer; - private uint _sampleRate; + private readonly float[] _workBuffer; + private readonly uint _sampleRate; private uint _currentSampleIndex; private uint _lastSampleIndex; @@ -75,4 +75,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Effect return TapUnsafe(sampleIndex, -1); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs index a2ac9d265..ed8e7cfe0 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs @@ -4,8 +4,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Effect { public class DelayLine3d : IDelayLine { - private float[] _workBuffer; - private uint _sampleRate; + private readonly float[] _workBuffer; + private readonly uint _sampleRate; private uint _currentSampleIndex; private uint _lastSampleIndex; @@ -73,4 +73,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Effect return TapUnsafe(sampleIndex, -1); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs index 78e46bf96..253400a5a 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Effect/ExponentialMovingAverage.cs @@ -1,6 +1,4 @@ -using System.Runtime.CompilerServices; - -namespace Ryujinx.Audio.Renderer.Dsp.Effect +namespace Ryujinx.Audio.Renderer.Dsp.Effect { public struct ExponentialMovingAverage { @@ -11,7 +9,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Effect _mean = mean; } - public float Read() + public readonly float Read() { return _mean; } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs b/src/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs index fd902525e..b408e294d 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs @@ -34,4 +34,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.Effect return (uint)MathF.Round(sampleRate * delayTime); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs index 280e47c0c..d519de333 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs @@ -36,4 +36,4 @@ namespace Ryujinx.Audio.Renderer.Dsp return ToInt(value + half, qBits); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs index 6645e20a3..b231dbb6a 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Reflection.Metadata; using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Dsp @@ -39,7 +38,8 @@ namespace Ryujinx.Audio.Renderer.Dsp { return 1.0f; } - else if (x <= -5.3f) + + if (x <= -5.3f) { return 0.0f; } @@ -112,4 +112,4 @@ namespace Ryujinx.Audio.Renderer.Dsp return MathF.Sin(DegreesToRadians(value)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs index 0233a8d71..d209c515b 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Numerics; using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Dsp @@ -62,7 +61,7 @@ namespace Ryujinx.Audio.Renderer.Dsp { for (int i = 0; i < input.Length; i++) { - output[i] = ((int)input[i]) << 16; + output[i] = input[i] << 16; } } @@ -127,4 +126,4 @@ namespace Ryujinx.Audio.Renderer.Dsp return (short)value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs index 7873c4d27..e44e9f41e 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -11,8 +10,7 @@ namespace Ryujinx.Audio.Renderer.Dsp public static class ResamplerHelper { #region "Default Quality Lookup Tables" - private static short[] _normalCurveLut0 = new short[] - { + private static readonly short[] _normalCurveLut0 = { 6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239, 19412, 7093, 22, 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377, 7472, 41, 5773, 19361, 7600, 48, 5659, 19342, 7728, 55, 5546, 19321, 7857, 62, 5434, 19298, 7987, 69, 5323, 19273, 8118, 77, @@ -44,11 +42,10 @@ namespace Ryujinx.Audio.Renderer.Dsp 109, 8646, 19148, 4890, 101, 8513, 19183, 4997, 92, 8381, 19215, 5104, 84, 8249, 19245, 5213, 77, 8118, 19273, 5323, 69, 7987, 19298, 5434, 62, 7857, 19321, 5546, 55, 7728, 19342, 5659, 48, 7600, 19361, 5773, 41, 7472, 19377, 5888, 34, 7345, 19391, 6004, 28, 7219, 19403, 6121, - 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424, 6479, 3, 6722, 19426, 6600 + 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424, 6479, 3, 6722, 19426, 6600, }; - private static short[] _normalCurveLut1 = new short[] - { + private static readonly short[] _normalCurveLut1 = { -68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450, 32586, 512, -36, -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454, 1000, -69, -891, 32393, 1174, -80, -990, 32323, 1352, -92, -1084, 32244, 1536, -103, -1173, 32157, 1724, -115, -1258, 32061, 1919, -128, @@ -80,11 +77,10 @@ namespace Ryujinx.Audio.Renderer.Dsp -180, 2747, 31593, -1554, -167, 2532, 31723, -1486, -153, 2322, 31844, -1414, -140, 2118, 31956, -1338, -128, 1919, 32061, -1258, -115, 1724, 32157, -1173, -103, 1536, 32244, -1084, -92, 1352, 32323, -990, -80, 1174, 32393, -891, -69, 1000, 32454, -788, -58, 832, 32507, -680, -47, 669, 32551, -568, - -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630, -200, -5, 69, 32639, -68 + -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630, -200, -5, 69, 32639, -68, }; - private static short[] _normalCurveLut2 = new short[] - { + private static readonly short[] _normalCurveLut2 = { 3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811, 26253, 3751, -42, 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169, 4199, -54, 2338, 26130, 4354, -58, 2227, 26085, 4512, -63, 2120, 26035, 4673, -67, 2015, 25980, 4837, -72, 1912, 25919, 5004, -76, @@ -116,13 +112,12 @@ namespace Ryujinx.Audio.Renderer.Dsp -98, 5701, 25621, 1531, -92, 5522, 25704, 1622, -87, 5347, 25780, 1716, -81, 5174, 25852, 1813, -76, 5004, 25919, 1912, -72, 4837, 25980, 2015, -67, 4673, 26035, 2120, -63, 4512, 26085, 2227, -58, 4354, 26130, 2338, -54, 4199, 26169, 2451, -50, 4046, 26202, 2568, -46, 3897, 26230, 2688, - -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281, 3064, -32, 3329, 26287, 3195 + -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281, 3064, -32, 3329, 26287, 3195, }; #endregion #region "High Quality Lookup Tables" - private static short[] _highCurveLut0 = new short[] - { + private static readonly short[] _highCurveLut0 = { -582, -23, 8740, 16386, 8833, 8, -590, 0, -573, -54, 8647, 16385, 8925, 40, -598, -1, -565, -84, 8555, 16383, 9018, 72, -606, -1, -557, -113, 8462, 16379, 9110, 105, -614, -2, -549, -142, 8370, 16375, 9203, 139, -622, -2, -541, -170, 8277, 16369, 9295, 173, -630, -3, @@ -189,8 +184,7 @@ namespace Ryujinx.Audio.Renderer.Dsp -1, -598, 40, 8925, 16385, 8647, -54, -573, 0, -590, 8, 8833, 16386, 8740, -23, -582, }; - private static short[] _highCurveLut1 = new short[] - { + private static readonly short[] _highCurveLut1 = { -12, 47, -134, 32767, 81, -16, 2, 0, -26, 108, -345, 32760, 301, -79, 17, -1, -40, 168, -552, 32745, 526, -144, 32, -2, -53, 226, -753, 32723, 755, -210, 47, -3, -66, 284, -950, 32694, 989, -277, 63, -5, -78, 340, -1143, 32658, 1226, -346, 79, -6, @@ -257,8 +251,7 @@ namespace Ryujinx.Audio.Renderer.Dsp -1, 17, -79, 301, 32760, -345, 108, -26, 0, 2, -16, 81, 32767, -134, 47, -12, }; - private static short[] _highCurveLut2 = new short[] - { + private static readonly short[] _highCurveLut2 = { 418, -2538, 6118, 24615, 6298, -2563, 417, 0, 420, -2512, 5939, 24611, 6479, -2588, 415, 1, 421, -2485, 5761, 24605, 6662, -2612, 412, 2, 422, -2458, 5585, 24595, 6846, -2635, 409, 3, 423, -2430, 5410, 24582, 7030, -2658, 406, 4, 423, -2402, 5236, 24565, 7216, -2680, 403, 5, @@ -326,13 +319,13 @@ namespace Ryujinx.Audio.Renderer.Dsp }; #endregion - private static float[] _normalCurveLut0F; - private static float[] _normalCurveLut1F; - private static float[] _normalCurveLut2F; + private static readonly float[] _normalCurveLut0F; + private static readonly float[] _normalCurveLut1F; + private static readonly float[] _normalCurveLut2F; - private static float[] _highCurveLut0F; - private static float[] _highCurveLut1F; - private static float[] _highCurveLut2F; + private static readonly float[] _highCurveLut0F; + private static readonly float[] _highCurveLut1F; + private static readonly float[] _highCurveLut2F; static ResamplerHelper() { @@ -373,7 +366,8 @@ namespace Ryujinx.Audio.Renderer.Dsp { return _normalCurveLut1F; } - else if (ratio > 1.333313f) + + if (ratio > 1.333313f) { return _normalCurveLut0F; } @@ -514,7 +508,8 @@ namespace Ryujinx.Audio.Renderer.Dsp { return _highCurveLut1F; } - else if (ratio > 1.333313f) + + if (ratio > 1.333313f) { return _highCurveLut0F; } @@ -601,4 +596,4 @@ namespace Ryujinx.Audio.Renderer.Dsp } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs index 821a135ef..f9ef201f2 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.State public short History0; public short History1; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs index 4e8d11e4c..97bbc80cc 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs @@ -71,4 +71,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.State public AuxiliaryBufferInfo CpuBufferInfo; public AuxiliaryBufferInfo DspBufferInfo; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs index 4220e6d51..f9a32b3f9 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs @@ -10,4 +10,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.State public float State2; public float State3; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs index 2a1e7f834..c56fa078a 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs @@ -64,4 +64,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.State } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs index 0560757c9..80d1cb62e 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/LimiterState.cs @@ -20,12 +20,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.State DetectorAverage.AsSpan().Fill(new ExponentialMovingAverage(0.0f)); CompressionGainAverage.AsSpan().Fill(new ExponentialMovingAverage(1.0f)); - DelayedSampleBufferPosition.AsSpan().Fill(0); - DelayedSampleBuffer.AsSpan().Fill(0.0f); + DelayedSampleBufferPosition.AsSpan().Clear(); + DelayedSampleBuffer.AsSpan().Clear(); UpdateParameter(ref parameter); } - public void UpdateParameter(ref LimiterParameter parameter) { } + public static void UpdateParameter(ref LimiterParameter parameter) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs index c06466031..5056b750e 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs @@ -6,11 +6,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.State { public class Reverb3dState { - private readonly float[] FdnDelayMinTimes = new float[4] { 5.0f, 6.0f, 13.0f, 14.0f }; - private readonly float[] FdnDelayMaxTimes = new float[4] { 45.704f, 82.782f, 149.94f, 271.58f }; - private readonly float[] DecayDelayMaxTimes1 = new float[4] { 17.0f, 13.0f, 9.0f, 7.0f }; - private readonly float[] DecayDelayMaxTimes2 = new float[4] { 19.0f, 11.0f, 10.0f, 6.0f }; - private readonly float[] EarlyDelayTimes = new float[20] { 0.017136f, 0.059154f, 0.16173f, 0.39019f, 0.42526f, 0.45541f, 0.68974f, 0.74591f, 0.83384f, 0.8595f, 0.0f, 0.075024f, 0.16879f, 0.2999f, 0.33744f, 0.3719f, 0.59901f, 0.71674f, 0.81786f, 0.85166f }; + private readonly float[] _fdnDelayMinTimes = new float[4] { 5.0f, 6.0f, 13.0f, 14.0f }; + private readonly float[] _fdnDelayMaxTimes = new float[4] { 45.704f, 82.782f, 149.94f, 271.58f }; + private readonly float[] _decayDelayMaxTimes1 = new float[4] { 17.0f, 13.0f, 9.0f, 7.0f }; + private readonly float[] _decayDelayMaxTimes2 = new float[4] { 19.0f, 11.0f, 10.0f, 6.0f }; + private readonly float[] _earlyDelayTimes = new float[20] { 0.017136f, 0.059154f, 0.16173f, 0.39019f, 0.42526f, 0.45541f, 0.68974f, 0.74591f, 0.83384f, 0.8595f, 0.0f, 0.075024f, 0.16879f, 0.2999f, 0.33744f, 0.3719f, 0.59901f, 0.71674f, 0.81786f, 0.85166f }; public readonly float[] EarlyGain = new float[20] { 0.67096f, 0.61027f, 1.0f, 0.35680f, 0.68361f, 0.65978f, 0.51939f, 0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.38270f, 0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f }; public IDelayLine[] FdnDelayLines { get; } @@ -46,9 +46,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.State for (int i = 0; i < 4; i++) { - FdnDelayLines[i] = new DelayLine3d(sampleRate, FdnDelayMaxTimes[i]); - DecayDelays1[i] = new DecayDelay(new DelayLine3d(sampleRate, DecayDelayMaxTimes1[i])); - DecayDelays2[i] = new DecayDelay(new DelayLine3d(sampleRate, DecayDelayMaxTimes2[i])); + FdnDelayLines[i] = new DelayLine3d(sampleRate, _fdnDelayMaxTimes[i]); + DecayDelays1[i] = new DecayDelay(new DelayLine3d(sampleRate, _decayDelayMaxTimes1[i])); + DecayDelays2[i] = new DecayDelay(new DelayLine3d(sampleRate, _decayDelayMaxTimes2[i])); } PreDelayLine = new DelayLine3d(sampleRate, 400); @@ -63,7 +63,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State EarlyDelayTime = new uint[20]; DryGain = parameter.DryGain; - PreviousFeedbackOutputDecayed.AsSpan().Fill(0); + PreviousFeedbackOutputDecayed.AsSpan().Clear(); PreviousPreDelayValue = 0; EarlyReflectionsGain = FloatingPointHelper.Pow10(Math.Min(parameter.RoomGain + parameter.ReflectionsGain, 5000.0f) / 2000.0f); @@ -91,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State for (int i = 0; i < FdnDelayLines.Length; i++) { - FdnDelayLines[i].SetDelay(FdnDelayMinTimes[i] + (parameter.Density / 100 * (FdnDelayMaxTimes[i] - FdnDelayMinTimes[i]))); + FdnDelayLines[i].SetDelay(_fdnDelayMinTimes[i] + (parameter.Density / 100 * (_fdnDelayMaxTimes[i] - _fdnDelayMinTimes[i]))); uint tempSampleCount = FdnDelayLines[i].CurrentSampleCount + DecayDelays1[i].CurrentSampleCount + DecayDelays2[i].CurrentSampleCount; @@ -111,9 +111,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.State for (int i = 0; i < EarlyDelayTime.Length; i++) { - uint sampleCount = Math.Min(IDelayLine.GetSampleCount(sampleRate, (parameter.ReflectionDelay * 1000.0f) + (EarlyDelayTimes[i] * 1000.0f * ((parameter.ReverbDelayTime * 0.9998f) + 0.02f))), PreDelayLine.SampleCountMax); + uint sampleCount = Math.Min(IDelayLine.GetSampleCount(sampleRate, (parameter.ReflectionDelay * 1000.0f) + (_earlyDelayTimes[i] * 1000.0f * ((parameter.ReverbDelayTime * 0.9998f) + 0.02f))), PreDelayLine.SampleCountMax); EarlyDelayTime[i] = sampleCount; } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs b/src/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs index 1ffabe05c..2f574f475 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State { public class ReverbState { - private static readonly float[] FdnDelayTimes = new float[20] + private static readonly float[] _fdnDelayTimes = new float[20] { // Room 53.953247f, 79.192566f, 116.238770f, 130.615295f, @@ -21,7 +21,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State 53.953247f, 79.192566f, 116.238770f, 170.615295f, }; - private static readonly float[] DecayDelayTimes = new float[20] + private static readonly float[] _decayDelayTimes = new float[20] { // Room 7f, 9f, 13f, 17f, @@ -35,7 +35,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State 7f, 9f, 13f, 17f, }; - private static readonly float[] EarlyDelayTimes = new float[50] + private static readonly float[] _earlyDelayTimes = new float[50] { // Room 0.0f, 3.5f, 2.8f, 3.9f, 2.7f, 13.4f, 7.9f, 8.4f, 9.9f, 12.0f, @@ -46,10 +46,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.State // Cathedral 33.1f, 43.3f, 22.8f, 37.9f, 14.9f, 35.3f, 17.9f, 34.2f, 0.0f, 43.3f, // Disabled - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, }; - private static readonly float[] EarlyGainBase = new float[50] + private static readonly float[] _earlyGainBase = new float[50] { // Room 0.70f, 0.68f, 0.70f, 0.68f, 0.70f, 0.68f, 0.70f, 0.68f, 0.68f, 0.68f, @@ -60,10 +60,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.State // Cathedral 0.93f, 0.92f, 0.87f, 0.86f, 0.94f, 0.81f, 0.80f, 0.77f, 0.76f, 0.65f, // Disabled - 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f + 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }; - private static readonly float[] PreDelayTimes = new float[5] + private static readonly float[] _preDelayTimes = new float[5] { // Room 12.5f, @@ -74,7 +74,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State // Cathedral 50.0f, // Disabled - 0.0f + 0.0f, }; public DelayLine[] FdnDelayLines { get; } @@ -93,14 +93,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.State private const int FixedPointPrecision = 14; - private ReadOnlySpan<float> GetFdnDelayTimesByLateMode(ReverbLateMode lateMode) + private static ReadOnlySpan<float> GetFdnDelayTimesByLateMode(ReverbLateMode lateMode) { - return FdnDelayTimes.AsSpan((int)lateMode * 4, 4); + return _fdnDelayTimes.AsSpan((int)lateMode * 4, 4); } - private ReadOnlySpan<float> GetDecayDelayTimesByLateMode(ReverbLateMode lateMode) + private static ReadOnlySpan<float> GetDecayDelayTimesByLateMode(ReverbLateMode lateMode) { - return DecayDelayTimes.AsSpan((int)lateMode * 4, 4); + return _decayDelayTimes.AsSpan((int)lateMode * 4, 4); } public ReverbState(ref ReverbParameter parameter, ulong workBuffer, bool isLongSizePreDelaySupported) @@ -148,8 +148,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.State for (int i = 0; i < 10; i++) { - EarlyDelayTime[i] = Math.Min(IDelayLine.GetSampleCount(sampleRate, EarlyDelayTimes[i] + preDelayTimeInMilliseconds), PreDelayLine.SampleCountMax) + 1; - EarlyGain[i] = EarlyGainBase[i] * earlyGain; + EarlyDelayTime[i] = Math.Min(IDelayLine.GetSampleCount(sampleRate, _earlyDelayTimes[i] + preDelayTimeInMilliseconds), PreDelayLine.SampleCountMax) + 1; + EarlyGain[i] = _earlyGainBase[i] * earlyGain; } if (parameter.ChannelCount == 2) @@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State EarlyGain[5] = EarlyGain[5] * 0.5f; } - PreDelayLineDelayTime = Math.Min(IDelayLine.GetSampleCount(sampleRate, PreDelayTimes[(int)parameter.EarlyMode] + preDelayTimeInMilliseconds), PreDelayLine.SampleCountMax); + PreDelayLineDelayTime = Math.Min(IDelayLine.GetSampleCount(sampleRate, _preDelayTimes[(int)parameter.EarlyMode] + preDelayTimeInMilliseconds), PreDelayLine.SampleCountMax); ReadOnlySpan<float> fdnDelayTimes = GetFdnDelayTimesByLateMode(parameter.LateMode); ReadOnlySpan<float> decayDelayTimes = GetDecayDelayTimesByLateMode(parameter.LateMode); @@ -201,4 +201,4 @@ namespace Ryujinx.Audio.Renderer.Dsp.State } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs index 54a63ace0..5732cdb21 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs @@ -13,11 +13,11 @@ namespace Ryujinx.Audio.Renderer.Dsp private const int FilterBankLength = 20; // Bank0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; private const int Bank0CenterIndex = 9; - private static readonly Array20<float> Bank1 = PrecomputeFilterBank(1.0f / 6.0f); - private static readonly Array20<float> Bank2 = PrecomputeFilterBank(2.0f / 6.0f); - private static readonly Array20<float> Bank3 = PrecomputeFilterBank(3.0f / 6.0f); - private static readonly Array20<float> Bank4 = PrecomputeFilterBank(4.0f / 6.0f); - private static readonly Array20<float> Bank5 = PrecomputeFilterBank(5.0f / 6.0f); + private static readonly Array20<float> _bank1 = PrecomputeFilterBank(1.0f / 6.0f); + private static readonly Array20<float> _bank2 = PrecomputeFilterBank(2.0f / 6.0f); + private static readonly Array20<float> _bank3 = PrecomputeFilterBank(3.0f / 6.0f); + private static readonly Array20<float> _bank4 = PrecomputeFilterBank(4.0f / 6.0f); + private static readonly Array20<float> _bank5 = PrecomputeFilterBank(5.0f / 6.0f); private static Array20<float> PrecomputeFilterBank(float offset) { @@ -39,7 +39,7 @@ namespace Ryujinx.Audio.Renderer.Dsp return A0 + A1 * MathF.Cos(2 * MathF.PI * x) + A2 * MathF.Cos(4 * MathF.PI * x); } - Array20<float> result = new Array20<float>(); + Array20<float> result = new(); for (int i = 0; i < FilterBankLength; i++) { @@ -58,10 +58,10 @@ namespace Ryujinx.Audio.Renderer.Dsp { state.Scale = inputSampleCount switch { - 40 => 6.0f, - 80 => 3.0f, + 40 => 6.0f, + 80 => 3.0f, 160 => 1.5f, - _ => throw new ArgumentOutOfRangeException() + _ => throw new ArgumentOutOfRangeException(nameof(inputSampleCount), inputSampleCount, null), }; state.Initialized = true; } @@ -105,7 +105,7 @@ namespace Ryujinx.Audio.Renderer.Dsp [MethodImpl(MethodImplOptions.AggressiveInlining)] void NextInput(ref UpsamplerBufferState state, float input) { - state.History.AsSpan().Slice(1).CopyTo(state.History.AsSpan()); + state.History.AsSpan()[1..].CopyTo(state.History.AsSpan()); state.History[HistoryLength - 1] = input; } @@ -123,19 +123,19 @@ namespace Ryujinx.Audio.Renderer.Dsp outputBuffer[i] = state.History[Bank0CenterIndex]; break; case 1: - outputBuffer[i] = DoFilterBank(ref state, Bank1); + outputBuffer[i] = DoFilterBank(ref state, _bank1); break; case 2: - outputBuffer[i] = DoFilterBank(ref state, Bank2); + outputBuffer[i] = DoFilterBank(ref state, _bank2); break; case 3: - outputBuffer[i] = DoFilterBank(ref state, Bank3); + outputBuffer[i] = DoFilterBank(ref state, _bank3); break; case 4: - outputBuffer[i] = DoFilterBank(ref state, Bank4); + outputBuffer[i] = DoFilterBank(ref state, _bank4); break; case 5: - outputBuffer[i] = DoFilterBank(ref state, Bank5); + outputBuffer[i] = DoFilterBank(ref state, _bank5); break; } @@ -152,10 +152,10 @@ namespace Ryujinx.Audio.Renderer.Dsp outputBuffer[i] = state.History[Bank0CenterIndex]; break; case 1: - outputBuffer[i] = DoFilterBank(ref state, Bank2); + outputBuffer[i] = DoFilterBank(ref state, _bank2); break; case 2: - outputBuffer[i] = DoFilterBank(ref state, Bank4); + outputBuffer[i] = DoFilterBank(ref state, _bank4); break; } @@ -173,11 +173,11 @@ namespace Ryujinx.Audio.Renderer.Dsp outputBuffer[i] = state.History[Bank0CenterIndex]; break; case 1: - outputBuffer[i] = DoFilterBank(ref state, Bank4); + outputBuffer[i] = DoFilterBank(ref state, _bank4); break; case 2: NextInput(ref state, inputBuffer[inputBufferIndex++]); - outputBuffer[i] = DoFilterBank(ref state, Bank2); + outputBuffer[i] = DoFilterBank(ref state, _bank2); break; } @@ -185,7 +185,7 @@ namespace Ryujinx.Audio.Renderer.Dsp } break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(state), state.Scale, null); } } } diff --git a/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs b/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs index 359cd4c02..491a05c86 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs @@ -60,7 +60,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/unused /// </summary> - private byte _reserved; + private readonly byte _reserved; /// <summary> /// The target rendering device. @@ -96,4 +96,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <seealso cref="Server.BehaviourContext"/> public int Revision; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs index aba7dcd61..5a0565dc6 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs @@ -27,4 +27,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> private unsafe fixed uint _reserved[3]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs index ef86015fe..f1492b0b1 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private byte _reserved; + private readonly byte _reserved; /// <summary> /// Biquad filter numerator (b0, b1, b2). @@ -31,4 +31,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <remarks>a0 = 1</remarks> public Array2<short> Denominator; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs index 36f286775..65f265a32 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs @@ -81,4 +81,4 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// <remarks>This is unused.</remarks> public uint MixBufferSampleCount; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs index 73e0e9bbc..b12a941a5 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs @@ -41,4 +41,4 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// </summary> public UsageState Status; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs index b03559eba..49b70e501 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs @@ -29,4 +29,4 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// </summary> public uint MixesCount; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs index 0be376088..1a936b0df 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCount"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns> - public bool IsChannelCountValid() + public readonly bool IsChannelCountValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCount); } @@ -107,7 +107,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCountMax"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns> - public bool IsChannelCountMaxValid() + public readonly bool IsChannelCountMaxValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax); } diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs index 72332c170..99c97d9d9 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs @@ -84,7 +84,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCount"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns> - public bool IsChannelCountValid() + public readonly bool IsChannelCountValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCount); } @@ -93,9 +93,9 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCountMax"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns> - public bool IsChannelCountMaxValid() + public readonly bool IsChannelCountMaxValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs index 0bce94a27..23ccb8c88 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterParameter.cs @@ -115,13 +115,13 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// <summary> /// Reserved/padding. /// </summary> - private byte _reserved; + private readonly byte _reserved; /// <summary> /// Check if the <see cref="ChannelCount"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns> - public bool IsChannelCountValid() + public readonly bool IsChannelCountValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCount); } @@ -130,9 +130,9 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCountMax"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns> - public bool IsChannelCountMaxValid() + public readonly bool IsChannelCountMaxValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs index f353f18d1..97e2f39fc 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/LimiterStatistics.cs @@ -24,8 +24,8 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// </summary> public void Reset() { - InputMax.AsSpan().Fill(0.0f); + InputMax.AsSpan().Clear(); CompressionGainMin.AsSpan().Fill(1.0f); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs index c78ce5951..d2cd78707 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// <summary> /// Reserved/unused. /// </summary> - private uint _reserved; + private readonly uint _reserved; /// <summary> /// The target sample rate. @@ -110,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCount"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns> - public bool IsChannelCountValid() + public readonly bool IsChannelCountValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCount); } @@ -119,9 +119,9 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCountMax"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns> - public bool IsChannelCountMaxValid() + public readonly bool IsChannelCountMaxValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs index baf049fbd..51ab156d2 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs @@ -102,7 +102,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCount"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns> - public bool IsChannelCountValid() + public readonly bool IsChannelCountValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCount); } @@ -111,9 +111,9 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect /// Check if the <see cref="ChannelCountMax"/> is valid. /// </summary> /// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns> - public bool IsChannelCountMaxValid() + public readonly bool IsChannelCountMaxValid() { return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs index e5419f70a..46686e3b4 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion1.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private byte _reserved1; + private readonly byte _reserved1; /// <summary> /// The target mix id of the effect. @@ -58,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private uint _reserved2; + private readonly uint _reserved2; /// <summary> /// Specific data storage. @@ -70,19 +70,19 @@ namespace Ryujinx.Audio.Renderer.Parameter public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart); - EffectType IEffectInParameter.Type => Type; + readonly EffectType IEffectInParameter.Type => Type; - bool IEffectInParameter.IsNew => IsNew; + readonly bool IEffectInParameter.IsNew => IsNew; - bool IEffectInParameter.IsEnabled => IsEnabled; + readonly bool IEffectInParameter.IsEnabled => IsEnabled; - int IEffectInParameter.MixId => MixId; + readonly int IEffectInParameter.MixId => MixId; - ulong IEffectInParameter.BufferBase => BufferBase; + readonly ulong IEffectInParameter.BufferBase => BufferBase; - ulong IEffectInParameter.BufferSize => BufferSize; + readonly ulong IEffectInParameter.BufferSize => BufferSize; - uint IEffectInParameter.ProcessingOrder => ProcessingOrder; + readonly uint IEffectInParameter.ProcessingOrder => ProcessingOrder; /// <summary> /// Check if the given channel count is valid. @@ -94,4 +94,4 @@ namespace Ryujinx.Audio.Renderer.Parameter return channelCount == 1 || channelCount == 2 || channelCount == 4 || channelCount == 6; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs index 250012d16..3854c7148 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/EffectInParameterVersion2.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private byte _reserved1; + private readonly byte _reserved1; /// <summary> /// The target mix id of the effect. @@ -58,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private uint _reserved2; + private readonly uint _reserved2; /// <summary> /// Specific data storage. @@ -70,19 +70,19 @@ namespace Ryujinx.Audio.Renderer.Parameter public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart); - EffectType IEffectInParameter.Type => Type; + readonly EffectType IEffectInParameter.Type => Type; - bool IEffectInParameter.IsNew => IsNew; + readonly bool IEffectInParameter.IsNew => IsNew; - bool IEffectInParameter.IsEnabled => IsEnabled; + readonly bool IEffectInParameter.IsEnabled => IsEnabled; - int IEffectInParameter.MixId => MixId; + readonly int IEffectInParameter.MixId => MixId; - ulong IEffectInParameter.BufferBase => BufferBase; + readonly ulong IEffectInParameter.BufferBase => BufferBase; - ulong IEffectInParameter.BufferSize => BufferSize; + readonly ulong IEffectInParameter.BufferSize => BufferSize; - uint IEffectInParameter.ProcessingOrder => ProcessingOrder; + readonly uint IEffectInParameter.ProcessingOrder => ProcessingOrder; /// <summary> /// Check if the given channel count is valid. @@ -94,4 +94,4 @@ namespace Ryujinx.Audio.Renderer.Parameter return channelCount == 1 || channelCount == 2 || channelCount == 4 || channelCount == 6; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs index 5e6a33ace..3c3e95538 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion1.cs @@ -18,6 +18,6 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> private unsafe fixed byte _reserved[15]; - EffectState IEffectOutStatus.State { get => State; set => State = value; } + EffectState IEffectOutStatus.State { readonly get => State; set => State = value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs index f2c9768b3..ee058d3ae 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/EffectOutStatusVersion2.cs @@ -23,6 +23,6 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> public EffectResultState ResultState; - EffectState IEffectOutStatus.State { get => State; set => State = value; } + EffectState IEffectOutStatus.State { readonly get => State; set => State = value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs index bd96c22bf..b3a4bae12 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/EffectResultState.cs @@ -23,4 +23,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/EffectState.cs b/src/Ryujinx.Audio/Renderer/Parameter/EffectState.cs index 911ba6d84..c4d06f122 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/EffectState.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/EffectState.cs @@ -13,6 +13,6 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// The effect is disabled. /// </summary> - Disabled = 4 + Disabled = 4, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs index bdd1ca45e..703c3e6db 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/IEffectInParameter.cs @@ -50,4 +50,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> Span<byte> SpecificData { get; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs index a5addbcb7..74d132209 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/IEffectOutStatus.cs @@ -10,4 +10,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> EffectState State { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs index 242e3843c..602508589 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs @@ -30,4 +30,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> private unsafe fixed uint _reserved[3]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs index 29a6e261f..a78937d01 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs @@ -19,4 +19,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> private unsafe fixed uint _reserved[3]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs b/src/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs index c0954cda0..733b5ad76 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs @@ -24,4 +24,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> private unsafe fixed byte _reserved[24]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs index 5b9a969a0..2eec04a21 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private ushort _reserved1; + private readonly ushort _reserved1; /// <summary> /// The id of the mix. @@ -61,7 +61,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private ulong _reserved2; + private readonly ulong _reserved2; /// <summary> /// Mix buffer volumes storage. @@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private uint _reserved3; + private readonly uint _reserved3; [StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax * Constants.MixBufferCountMax, Pack = 1)] private struct MixVolumeArray { } @@ -92,4 +92,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <remarks>Used when no splitter id is specified.</remarks> public Span<float> MixBufferVolume => SpanHelpers.AsSpan<MixVolumeArray, float>(ref _mixBufferVolumeArray); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs index 0f9a3aa3e..806f7fa89 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs @@ -18,4 +18,4 @@ namespace Ryujinx.Audio.Renderer.Parameter.Performance /// </summary> private unsafe fixed uint _reserved[3]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs index 64bbe080a..839d6eb6b 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs @@ -18,4 +18,4 @@ namespace Ryujinx.Audio.Renderer.Parameter.Performance /// </summary> private unsafe fixed uint _reserved[3]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs index a42ea833f..c97ce2965 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs @@ -16,6 +16,6 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/Unused. /// </summary> - private ulong _reserved; + private readonly ulong _reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs index 7c02d65ff..0d4b276ef 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs @@ -2,7 +2,6 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; using Ryujinx.Common.Memory; using System.Runtime.InteropServices; - using CpuAddress = System.UInt64; namespace Ryujinx.Audio.Renderer.Parameter.Sink @@ -41,7 +40,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Sink /// <summary> /// The target <see cref="SampleFormat"/>. /// </summary> - /// <remarks>Only <see cref="SampleFormat.PcmInt16"/> is supported.</remarks> + /// <remarks>Only <see cref="Audio.Common.SampleFormat.PcmInt16"/> is supported.</remarks> public SampleFormat SampleFormat; /// <summary> @@ -57,6 +56,6 @@ namespace Ryujinx.Audio.Renderer.Parameter.Sink /// <summary> /// Reserved/padding. /// </summary> - private ushort _reserved2; + private readonly ushort _reserved2; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs index abeadaccf..652d02a63 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Sink /// <summary> /// Reserved/padding. /// </summary> - private byte _padding; + private readonly byte _padding; /// <summary> /// The total count of channels to output to the device. @@ -34,7 +34,7 @@ namespace Ryujinx.Audio.Renderer.Parameter.Sink /// <summary> /// Reserved/padding. /// </summary> - private byte _reserved; + private readonly byte _reserved; /// <summary> /// Set to true if the user controls Surround to Stereo downmixing coefficients. @@ -55,4 +55,4 @@ namespace Ryujinx.Audio.Renderer.Parameter.Sink /// </summary> public Span<byte> DeviceName => SpanHelpers.AsSpan<DeviceNameStruct, byte>(ref _deviceName); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs index 1ee4eb532..3c1ac09c0 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private ushort _reserved1; + private readonly ushort _reserved1; /// <summary> /// The node id of the sink. @@ -50,4 +50,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> public Span<byte> SpecificData => SpanHelpers.AsSpan<SpecificDataStruct, byte>(ref _specificDataStart); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs index 426b861ca..dd0f867b5 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs @@ -16,11 +16,11 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/padding. /// </summary> - private uint _padding; + private readonly uint _padding; /// <summary> /// Reserved/padding. /// </summary> private unsafe fixed ulong _reserved[3]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs index 96c43092b..b74b67be0 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs @@ -59,9 +59,9 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Check if the magic is valid. /// </summary> /// <returns>Returns true if the magic is valid.</returns> - public bool IsMagicValid() + public readonly bool IsMagicValid() { return Magic == ValidMagic; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs index 0220497de..2567b15a8 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs @@ -38,9 +38,9 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Check if the magic is valid. /// </summary> /// <returns>Returns true if the magic is valid.</returns> - public bool IsMagicValid() + public readonly bool IsMagicValid() { return Magic == ValidMagic; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs index dbae17a9a..10fa866e7 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs @@ -37,9 +37,9 @@ namespace Ryujinx.Audio.Renderer.Parameter /// Check if the magic is valid. /// </summary> /// <returns>Returns true if the magic is valid.</returns> - public bool IsMagicValid() + public readonly bool IsMagicValid() { return Magic == ValidMagic; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs index 6a863237d..6cff1a251 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs @@ -25,4 +25,4 @@ namespace Ryujinx.Audio.Renderer.Parameter [MarshalAs(UnmanagedType.I1)] public bool IsUsed; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs index c4b4ba312..86f92442b 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs @@ -94,7 +94,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/unused. /// </summary> - private uint _reserved1; + private readonly uint _reserved1; /// <summary> /// User state address required by the data source. @@ -143,7 +143,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/unused. /// </summary> - private ushort _reserved2; + private readonly ushort _reserved2; /// <summary> /// Change the behaviour of the voice. @@ -222,7 +222,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Reserved/unused. /// </summary> - private byte _reserved; + private readonly byte _reserved; /// <summary> /// If set to anything other than 0, specifies how many times to loop the wavebuffer. @@ -260,7 +260,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <typeparam name="T">The PCM sample type</typeparam> /// <returns>Returns true if the sample offset are in range of the size.</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool IsSampleOffsetInRangeForPcm<T>() where T : unmanaged + private readonly bool IsSampleOffsetInRangeForPcm<T>() where T : unmanaged { uint dataTypeSize = (uint)Unsafe.SizeOf<T>(); @@ -273,27 +273,15 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> /// <param name="format">The target <see cref="SampleFormat"/></param> /// <returns>Returns true if the sample offset are in range of the size.</returns> - public bool IsSampleOffsetValid(SampleFormat format) + public readonly bool IsSampleOffsetValid(SampleFormat format) { - bool result; - - switch (format) + return format switch { - case SampleFormat.PcmInt16: - result = IsSampleOffsetInRangeForPcm<ushort>(); - break; - case SampleFormat.PcmFloat: - result = IsSampleOffsetInRangeForPcm<float>(); - break; - case SampleFormat.Adpcm: - result = AdpcmHelper.GetAdpcmDataSize((int)StartSampleOffset) <= Size && - AdpcmHelper.GetAdpcmDataSize((int)EndSampleOffset) <= Size; - break; - default: - throw new NotImplementedException($"{format} not implemented!"); - } - - return result; + SampleFormat.PcmInt16 => IsSampleOffsetInRangeForPcm<ushort>(), + SampleFormat.PcmFloat => IsSampleOffsetInRangeForPcm<float>(), + SampleFormat.Adpcm => AdpcmHelper.GetAdpcmDataSize((int)StartSampleOffset) <= Size && AdpcmHelper.GetAdpcmDataSize((int)EndSampleOffset) <= Size, + _ => throw new NotImplementedException($"{format} not implemented!"), + }; } } @@ -316,7 +304,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Skip pitch and Sample Rate Conversion (SRC). /// </summary> - SkipPitchAndSampleRateConversion = 2 + SkipPitchAndSampleRateConversion = 2, } /// <summary> @@ -338,7 +326,7 @@ namespace Ryujinx.Audio.Renderer.Parameter /// <summary> /// Resample interpolating 1 samples per output sample. /// </summary> - Low + Low, } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs index be9d35849..a7c749835 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs @@ -32,4 +32,4 @@ namespace Ryujinx.Audio.Renderer.Parameter /// </summary> private unsafe fixed byte _reserved[3]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 8485fb4c5..7bb8ae5ba 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -19,7 +19,6 @@ using System; using System.Buffers; using System.Diagnostics; using System.Threading; - using CpuAddress = System.UInt64; namespace Ryujinx.Audio.Renderer.Server @@ -30,19 +29,21 @@ namespace Ryujinx.Audio.Renderer.Server private AudioRendererRenderingDevice _renderingDevice; private AudioRendererExecutionMode _executionMode; - private IWritableEvent _systemEvent; + private readonly IWritableEvent _systemEvent; private MemoryPoolState _dspMemoryPoolState; - private VoiceContext _voiceContext; - private MixContext _mixContext; - private SinkContext _sinkContext; - private SplitterContext _splitterContext; - private EffectContext _effectContext; + private readonly VoiceContext _voiceContext; + private readonly MixContext _mixContext; + private readonly SinkContext _sinkContext; + private readonly SplitterContext _splitterContext; + private readonly EffectContext _effectContext; private PerformanceManager _performanceManager; private UpsamplerManager _upsamplerManager; private bool _isActive; private BehaviourContext _behaviourContext; +#pragma warning disable IDE0052 // Remove unread private member private ulong _totalElapsedTicksUpdating; private ulong _totalElapsedTicks; +#pragma warning restore IDE0052 private int _sessionId; private Memory<MemoryPoolState> _memoryPools; @@ -75,7 +76,7 @@ namespace Ryujinx.Audio.Renderer.Server private ulong _elapsedFrameCount; private ulong _renderingStartTick; - private AudioRendererManager _manager; + private readonly AudioRendererManager _manager; private int _disposeState; @@ -143,12 +144,12 @@ namespace Ryujinx.Audio.Renderer.Server WorkBufferAllocator workBufferAllocator; - workBufferMemory.Span.Fill(0); + workBufferMemory.Span.Clear(); _workBufferMemoryPin = workBufferMemory.Pin(); workBufferAllocator = new WorkBufferAllocator(workBufferMemory); - PoolMapper poolMapper = new PoolMapper(processHandle, false); + PoolMapper poolMapper = new(processHandle, false); poolMapper.InitializeSystemPool(ref _dspMemoryPoolState, workBuffer, workBufferSize); _mixBuffer = workBufferAllocator.Allocate<float>(_sampleCount * (_voiceChannelCountMax + _mixBufferCount), 0x10); @@ -244,9 +245,9 @@ namespace Ryujinx.Audio.Renderer.Server foreach (ref MixState mix in mixes.Span) { - mix = new MixState(effectProcessingOrderArray.Slice(0, (int)parameter.EffectCount), ref _behaviourContext); + mix = new MixState(effectProcessingOrderArray[..(int)parameter.EffectCount], ref _behaviourContext); - effectProcessingOrderArray = effectProcessingOrderArray.Slice((int)parameter.EffectCount); + effectProcessingOrderArray = effectProcessingOrderArray[(int)parameter.EffectCount..]; } } @@ -341,26 +342,15 @@ namespace Ryujinx.Audio.Renderer.Server _elapsedFrameCount = 0; _voiceDropParameter = 1.0f; - switch (_behaviourContext.GetCommandProcessingTimeEstimatorVersion()) + _commandProcessingTimeEstimator = _behaviourContext.GetCommandProcessingTimeEstimatorVersion() switch { - case 1: - _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion1(_sampleCount, _mixBufferCount); - break; - case 2: - _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion2(_sampleCount, _mixBufferCount); - break; - case 3: - _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion3(_sampleCount, _mixBufferCount); - break; - case 4: - _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount); - break; - case 5: - _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion5(_sampleCount, _mixBufferCount); - break; - default: - throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}."); - } + 1 => new CommandProcessingTimeEstimatorVersion1(_sampleCount, _mixBufferCount), + 2 => new CommandProcessingTimeEstimatorVersion2(_sampleCount, _mixBufferCount), + 3 => new CommandProcessingTimeEstimatorVersion3(_sampleCount, _mixBufferCount), + 4 => new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount), + 5 => new CommandProcessingTimeEstimatorVersion5(_sampleCount, _mixBufferCount), + _ => throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}."), + }; return ResultCode.Success; } @@ -402,9 +392,9 @@ namespace Ryujinx.Audio.Renderer.Server { ulong updateStartTicks = GetSystemTicks(); - output.Span.Fill(0); + output.Span.Clear(); - StateUpdater stateUpdater = new StateUpdater(input, output, _processHandle, _behaviourContext); + StateUpdater stateUpdater = new(input, output, _processHandle, _behaviourContext); ResultCode result; @@ -609,9 +599,9 @@ namespace Ryujinx.Audio.Renderer.Server _renderingStartTick = 0; } - CommandBuffer commandBuffer = new CommandBuffer(commandList, _commandProcessingTimeEstimator); + CommandBuffer commandBuffer = new(commandList, _commandProcessingTimeEstimator); - CommandGenerator commandGenerator = new CommandGenerator(commandBuffer, GetContext(), _voiceContext, _mixContext, _effectContext, _sinkContext, _splitterContext, _performanceManager); + CommandGenerator commandGenerator = new(commandBuffer, GetContext(), _voiceContext, _mixContext, _effectContext, _sinkContext, _splitterContext, _performanceManager); _voiceContext.Sort(); commandGenerator.GenerateVoices(); @@ -731,7 +721,7 @@ namespace Ryujinx.Audio.Renderer.Server DepopBuffer = _depopBuffer, MixBufferCount = GetMixBufferCount(), SessionId = _sessionId, - UpsamplerManager = _upsamplerManager + UpsamplerManager = _upsamplerManager, }; } @@ -742,7 +732,7 @@ namespace Ryujinx.Audio.Renderer.Server public static ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter) { - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(parameter.Revision); @@ -813,6 +803,8 @@ namespace Ryujinx.Audio.Renderer.Server public void Dispose() { + GC.SuppressFinalize(this); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); @@ -828,7 +820,7 @@ namespace Ryujinx.Audio.Renderer.Server Stop(); } - PoolMapper mapper = new PoolMapper(_processHandle, false); + PoolMapper mapper = new(_processHandle, false); mapper.Unmap(ref _dspMemoryPoolState); PoolMapper.ClearUsageState(_memoryPools); @@ -876,4 +868,4 @@ namespace Ryujinx.Audio.Renderer.Server return ResultCode.UnsupportedOperation; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs index e41d5cc50..0dbbd26c8 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <summary> /// The session ids allocation table. /// </summary> - private int[] _sessionIds; + private readonly int[] _sessionIds; /// <summary> /// The events linked to each session. @@ -39,7 +39,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <summary> /// The <see cref="AudioRenderSystem"/> sessions instances. /// </summary> - private AudioRenderSystem[] _sessions; + private readonly AudioRenderSystem[] _sessions; /// <summary> /// The count of active sessions. @@ -186,7 +186,7 @@ namespace Ryujinx.Audio.Renderer.Server _workerThread = new Thread(SendCommands) { - Name = "AudioRendererManager.Worker" + Name = "AudioRendererManager.Worker", }; _workerThread.Start(); @@ -317,7 +317,7 @@ namespace Ryujinx.Audio.Renderer.Server { int sessionId = AcquireSessionId(); - AudioRenderSystem audioRenderer = new AudioRenderSystem(this, _sessionsSystemEvent[sessionId]); + AudioRenderSystem audioRenderer = new(this, _sessionsSystemEvent[sessionId]); // TODO: Eventually, we should try to use the guest supplied work buffer instead of allocating // our own. However, it was causing problems on some applications that would unmap the memory @@ -367,6 +367,8 @@ namespace Ryujinx.Audio.Renderer.Server public void Dispose() { + GC.SuppressFinalize(this); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); @@ -402,4 +404,4 @@ namespace Ryujinx.Audio.Renderer.Server } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs index 821947a98..3297b5d9f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <summary> /// Error storage. /// </summary> - private ErrorInfo[] _errorInfos; + private readonly ErrorInfo[] _errorInfos; /// <summary> /// Current position in the <see cref="_errorInfos"/> array. @@ -254,7 +254,8 @@ namespace Ryujinx.Audio.Renderer.Server { return 0.80f; } - else if (CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision4)) + + if (CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision4)) { return 0.75f; } @@ -299,10 +300,8 @@ namespace Ryujinx.Audio.Renderer.Server { return 2; } - else - { - return 1; - } + + return 1; } /// <summary> @@ -436,7 +435,7 @@ namespace Ryujinx.Audio.Renderer.Server errorInfos[i] = new ErrorInfo { ErrorCode = 0, - ExtraErrorInfo = 0 + ExtraErrorInfo = 0, }; } } @@ -450,4 +449,4 @@ namespace Ryujinx.Audio.Renderer.Server _errorIndex = 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index 905cb2054..f4174a913 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <summary> /// The command processing time estimator in use. /// </summary> - private ICommandProcessingTimeEstimator _commandProcessingTimeEstimator; + private readonly ICommandProcessingTimeEstimator _commandProcessingTimeEstimator; /// <summary> /// The estimated total processing time. @@ -61,7 +61,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateClearMixBuffer(int nodeId) { - ClearMixBufferCommand command = new ClearMixBufferCommand(nodeId); + ClearMixBufferCommand command = new(nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -79,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="wasPlaying">Set to true if the voice was playing previously.</param> public void GenerateDepopPrepare(Memory<VoiceUpdateState> state, Memory<float> depopBuffer, uint bufferCount, uint bufferOffset, int nodeId, bool wasPlaying) { - DepopPrepareCommand command = new DepopPrepareCommand(state, depopBuffer, bufferCount, bufferOffset, nodeId, wasPlaying); + DepopPrepareCommand command = new(state, depopBuffer, bufferCount, bufferOffset, nodeId, wasPlaying); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -94,7 +94,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GeneratePerformance(ref PerformanceEntryAddresses performanceEntryAddresses, PerformanceCommand.Type type, int nodeId) { - PerformanceCommand command = new PerformanceCommand(ref performanceEntryAddresses, type, nodeId); + PerformanceCommand command = new(ref performanceEntryAddresses, type, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -110,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateVolumeRamp(float previousVolume, float volume, uint bufferIndex, int nodeId) { - VolumeRampCommand command = new VolumeRampCommand(previousVolume, volume, bufferIndex, nodeId); + VolumeRampCommand command = new(previousVolume, volume, bufferIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -127,7 +127,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateDataSourceVersion2(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { - DataSourceVersion2Command command = new DataSourceVersion2Command(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); + DataSourceVersion2Command command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -144,7 +144,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GeneratePcmInt16DataSourceVersion1(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { - PcmInt16DataSourceCommandVersion1 command = new PcmInt16DataSourceCommandVersion1(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); + PcmInt16DataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -161,7 +161,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GeneratePcmFloatDataSourceVersion1(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, ushort channelIndex, int nodeId) { - PcmFloatDataSourceCommandVersion1 command = new PcmFloatDataSourceCommandVersion1(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); + PcmFloatDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, channelIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -177,7 +177,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateAdpcmDataSourceVersion1(ref VoiceState voiceState, Memory<VoiceUpdateState> state, ushort outputBufferIndex, int nodeId) { - AdpcmDataSourceCommandVersion1 command = new AdpcmDataSourceCommandVersion1(ref voiceState, state, outputBufferIndex, nodeId); + AdpcmDataSourceCommandVersion1 command = new(ref voiceState, state, outputBufferIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -196,7 +196,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateBiquadFilter(int baseIndex, ref BiquadFilterParameter filter, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId) { - BiquadFilterCommand command = new BiquadFilterCommand(baseIndex, ref filter, biquadFilterStateMemory, inputBufferOffset, outputBufferOffset, needInitialization, nodeId); + BiquadFilterCommand command = new(baseIndex, ref filter, biquadFilterStateMemory, inputBufferOffset, outputBufferOffset, needInitialization, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -215,7 +215,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateGroupedBiquadFilter(int baseIndex, ReadOnlySpan<BiquadFilterParameter> filters, Memory<BiquadFilterState> biquadFilterStatesMemory, int inputBufferOffset, int outputBufferOffset, ReadOnlySpan<bool> isInitialized, int nodeId) { - GroupedBiquadFilterCommand command = new GroupedBiquadFilterCommand(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId); + GroupedBiquadFilterCommand command = new(baseIndex, filters, biquadFilterStatesMemory, inputBufferOffset, outputBufferOffset, isInitialized, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateMixRampGrouped(uint mixBufferCount, uint inputBufferIndex, uint outputBufferIndex, Span<float> previousVolume, Span<float> volume, Memory<VoiceUpdateState> state, int nodeId) { - MixRampGroupedCommand command = new MixRampGroupedCommand(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId); + MixRampGroupedCommand command = new(mixBufferCount, inputBufferIndex, outputBufferIndex, previousVolume, volume, state, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -253,7 +253,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateMixRamp(float previousVolume, float volume, uint inputBufferIndex, uint outputBufferIndex, int lastSampleIndex, Memory<VoiceUpdateState> state, int nodeId) { - MixRampCommand command = new MixRampCommand(previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, nodeId); + MixRampCommand command = new(previousVolume, volume, inputBufferIndex, outputBufferIndex, lastSampleIndex, state, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -270,7 +270,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="sampleRate">The target sample rate in use.</param> public void GenerateDepopForMixBuffersCommand(Memory<float> depopBuffer, uint bufferOffset, uint bufferCount, int nodeId, uint sampleRate) { - DepopForMixBuffersCommand command = new DepopForMixBuffersCommand(depopBuffer, bufferOffset, bufferCount, nodeId, sampleRate); + DepopForMixBuffersCommand command = new(depopBuffer, bufferOffset, bufferCount, nodeId, sampleRate); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -285,7 +285,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateCopyMixBuffer(uint inputBufferIndex, uint outputBufferIndex, int nodeId) { - CopyMixBufferCommand command = new CopyMixBufferCommand(inputBufferIndex, outputBufferIndex, nodeId); + CopyMixBufferCommand command = new(inputBufferIndex, outputBufferIndex, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -301,7 +301,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="volume">The mix volume.</param> public void GenerateMix(uint inputBufferIndex, uint outputBufferIndex, int nodeId, float volume) { - MixCommand command = new MixCommand(inputBufferIndex, outputBufferIndex, nodeId, volume); + MixCommand command = new(inputBufferIndex, outputBufferIndex, nodeId, volume); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -323,7 +323,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (parameter.IsChannelCountValid()) { - ReverbCommand command = new ReverbCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, isLongSizePreDelaySupported, newEffectChannelMappingSupported); + ReverbCommand command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, isLongSizePreDelaySupported, newEffectChannelMappingSupported); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -345,7 +345,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (parameter.IsChannelCountValid()) { - Reverb3dCommand command = new Reverb3dCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported); + Reverb3dCommand command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -368,7 +368,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (parameter.IsChannelCountValid()) { - DelayCommand command = new DelayCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported); + DelayCommand command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -389,7 +389,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (parameter.IsChannelCountValid()) { - LimiterCommandVersion1 command = new LimiterCommandVersion1(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId); + LimiterCommandVersion1 command = new(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -411,7 +411,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (parameter.IsChannelCountValid()) { - LimiterCommandVersion2 command = new LimiterCommandVersion2(bufferOffset, parameter, state, effectResultState, isEnabled, workBuffer, nodeId); + LimiterCommandVersion2 command = new(bufferOffset, parameter, state, effectResultState, isEnabled, workBuffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -437,7 +437,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (state.SendBufferInfoBase != 0 && state.ReturnBufferInfoBase != 0) { - AuxiliaryBufferCommand command = new AuxiliaryBufferCommand(bufferOffset, inputBufferOffset, outputBufferOffset, ref state, isEnabled, countMax, outputBuffer, inputBuffer, updateCount, writeOffset, nodeId); + AuxiliaryBufferCommand command = new(bufferOffset, inputBufferOffset, outputBufferOffset, ref state, isEnabled, countMax, outputBuffer, inputBuffer, updateCount, writeOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -461,7 +461,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (sendBufferInfo != 0) { - CaptureBufferCommand command = new CaptureBufferCommand(bufferOffset, inputBufferOffset, sendBufferInfo, isEnabled, countMax, outputBuffer, updateCount, writeOffset, nodeId); + CaptureBufferCommand command = new(bufferOffset, inputBufferOffset, sendBufferInfo, isEnabled, countMax, outputBuffer, updateCount, writeOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -473,7 +473,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (parameter.IsChannelCountValid()) { - CompressorCommand command = new CompressorCommand(bufferOffset, parameter, state, isEnabled, nodeId); + CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -489,7 +489,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateVolume(float volume, uint bufferOffset, int nodeId) { - VolumeCommand command = new VolumeCommand(volume, bufferOffset, nodeId); + VolumeCommand command = new(volume, bufferOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -504,7 +504,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateCircularBuffer(uint bufferOffset, CircularBufferSink sink, int nodeId) { - CircularBufferSinkCommand command = new CircularBufferSinkCommand(bufferOffset, ref sink.Parameter, ref sink.CircularBufferAddressInfo, sink.CurrentWriteOffset, nodeId); + CircularBufferSinkCommand command = new(bufferOffset, ref sink.Parameter, ref sink.CircularBufferAddressInfo, sink.CurrentWriteOffset, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -521,7 +521,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateDownMixSurroundToStereo(uint bufferOffset, Span<byte> inputBufferOffset, Span<byte> outputBufferOffset, float[] downMixParameter, int nodeId) { - DownMixSurroundToStereoCommand command = new DownMixSurroundToStereoCommand(bufferOffset, inputBufferOffset, outputBufferOffset, downMixParameter, nodeId); + DownMixSurroundToStereoCommand command = new(bufferOffset, inputBufferOffset, outputBufferOffset, downMixParameter, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -541,7 +541,7 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateUpsample(uint bufferOffset, UpsamplerState upsampler, uint inputCount, Span<byte> inputBufferOffset, uint bufferCountPerSample, uint sampleCount, uint sampleRate, int nodeId) { - UpsampleCommand command = new UpsampleCommand(bufferOffset, upsampler, inputCount, inputBufferOffset, bufferCountPerSample, sampleCount, sampleRate, nodeId); + UpsampleCommand command = new(bufferOffset, upsampler, inputCount, inputBufferOffset, bufferCountPerSample, sampleCount, sampleRate, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); @@ -558,11 +558,11 @@ namespace Ryujinx.Audio.Renderer.Server /// <param name="nodeId">The node id associated to this command.</param> public void GenerateDeviceSink(uint bufferOffset, DeviceSink sink, int sessionId, Memory<float> buffer, int nodeId) { - DeviceSinkCommand command = new DeviceSinkCommand(bufferOffset, sink, sessionId, buffer, nodeId); + DeviceSinkCommand command = new(bufferOffset, sink, sessionId, buffer, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); AddCommand(command); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs index afc1e39b7..ae8f699f3 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs @@ -17,14 +17,14 @@ namespace Ryujinx.Audio.Renderer.Server { public class CommandGenerator { - private CommandBuffer _commandBuffer; - private RendererSystemContext _rendererContext; - private VoiceContext _voiceContext; - private MixContext _mixContext; - private EffectContext _effectContext; - private SinkContext _sinkContext; - private SplitterContext _splitterContext; - private PerformanceManager _performanceManager; + private readonly CommandBuffer _commandBuffer; + private readonly RendererSystemContext _rendererContext; + private readonly VoiceContext _voiceContext; + private readonly MixContext _mixContext; + private readonly EffectContext _effectContext; + private readonly SinkContext _sinkContext; + private readonly SplitterContext _splitterContext; + private readonly PerformanceManager _performanceManager; public CommandGenerator(CommandBuffer commandBuffer, RendererSystemContext rendererContext, VoiceContext voiceContext, MixContext mixContext, EffectContext effectContext, SinkContext sinkContext, SplitterContext splitterContext, PerformanceManager performanceManager) { @@ -138,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Server if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable) { - Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount); + Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)]; Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory); _commandBuffer.GenerateGroupedBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId); @@ -151,7 +151,7 @@ namespace Ryujinx.Audio.Renderer.Server if (filter.Enable) { - Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount); + Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount)]; Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory); @@ -224,7 +224,7 @@ namespace Ryujinx.Audio.Renderer.Server bool performanceInitialized = false; - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); if (_performanceManager != null && _performanceManager.IsTargetNodeId(nodeId) && _performanceManager.GetNextEntry(out performanceEntry, dataSourceDetailType, PerformanceEntryType.Voice, nodeId)) { @@ -371,7 +371,7 @@ namespace Ryujinx.Audio.Renderer.Server { int nodeId = sortedState.NodeId; - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); bool performanceInitialized = false; @@ -502,9 +502,11 @@ namespace Ryujinx.Audio.Renderer.Server bool needInitialization = effect.Parameter.Status == UsageState.Invalid || (effect.Parameter.Status == UsageState.New && !_rendererContext.BehaviourContext.IsBiquadFilterEffectStateClearBugFixed()); - BiquadFilterParameter parameter = new BiquadFilterParameter(); + BiquadFilterParameter parameter = new() + { + Enable = true, + }; - parameter.Enable = true; effect.Parameter.Denominator.AsSpan().CopyTo(parameter.Denominator.AsSpan()); effect.Parameter.Numerator.AsSpan().CopyTo(parameter.Numerator.AsSpan()); @@ -623,7 +625,7 @@ namespace Ryujinx.Audio.Renderer.Server bool isFinalMix = mix.MixId == Constants.FinalMixId; - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); bool performanceInitialized = false; @@ -789,7 +791,7 @@ namespace Ryujinx.Audio.Renderer.Server GenerateEffects(ref subMix); - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); int nodeId = subMix.NodeId; @@ -820,7 +822,7 @@ namespace Ryujinx.Audio.Renderer.Server { int nodeId = sortedState.NodeId; - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); bool performanceInitialized = false; @@ -853,7 +855,7 @@ namespace Ryujinx.Audio.Renderer.Server GenerateEffects(ref finalMix); - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); int nodeId = finalMix.NodeId; @@ -901,7 +903,7 @@ namespace Ryujinx.Audio.Renderer.Server { int nodeId = _mixContext.GetFinalState().NodeId; - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); bool performanceInitialized = false; @@ -977,7 +979,7 @@ namespace Ryujinx.Audio.Renderer.Server { bool performanceInitialized = false; - PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses(); + PerformanceEntryAddresses performanceEntry = new(); if (_performanceManager != null && _performanceManager.GetNextEntry(out performanceEntry, PerformanceEntryType.Sink, sink.NodeId)) { @@ -1025,4 +1027,4 @@ namespace Ryujinx.Audio.Renderer.Server } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs index 63dc9ca96..d95e9aa71 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs @@ -8,8 +8,8 @@ namespace Ryujinx.Audio.Renderer.Server /// </summary> public class CommandProcessingTimeEstimatorVersion1 : ICommandProcessingTimeEstimator { - private uint _sampleCount; - private uint _bufferCount; + private readonly uint _sampleCount; + private readonly uint _bufferCount; public CommandProcessingTimeEstimatorVersion1(uint sampleCount, uint bufferCount) { @@ -185,4 +185,4 @@ namespace Ryujinx.Audio.Renderer.Server return 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs index d4f28a07d..929aaf383 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs @@ -9,8 +9,8 @@ namespace Ryujinx.Audio.Renderer.Server /// </summary> public class CommandProcessingTimeEstimatorVersion2 : ICommandProcessingTimeEstimator { - private uint _sampleCount; - private uint _bufferCount; + private readonly uint _sampleCount; + private readonly uint _bufferCount; public CommandProcessingTimeEstimatorVersion2(uint sampleCount, uint bufferCount) { @@ -189,71 +189,47 @@ namespace Ryujinx.Audio.Renderer.Server { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)41636.0f; - case 2: - return (uint)97861.0f; - case 4: - return (uint)192520.0f; - case 6: - return (uint)301760.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)41636.0f, + 2 => (uint)97861.0f, + 4 => (uint)192520.0f, + 6 => (uint)301760.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)578.53f; - case 2: - return (uint)663.06f; - case 4: - return (uint)703.98f; - case 6: - return (uint)760.03f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)578.53f, + 2 => (uint)663.06f, + 4 => (uint)703.98f, + 6 => (uint)760.03f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)8770.3f; - case 2: - return (uint)25741.0f; - case 4: - return (uint)47551.0f; - case 6: - return (uint)81629.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)8770.3f, + 2 => (uint)25741.0f, + 4 => (uint)47551.0f, + 6 => (uint)81629.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)521.28f; - case 2: - return (uint)585.4f; - case 4: - return (uint)629.88f; - case 6: - return (uint)713.57f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)521.28f, + 2 => (uint)585.4f, + 4 => (uint)629.88f, + 6 => (uint)713.57f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public uint Estimate(ReverbCommand command) @@ -264,71 +240,47 @@ namespace Ryujinx.Audio.Renderer.Server { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)97192.0f; - case 2: - return (uint)103280.0f; - case 4: - return (uint)109580.0f; - case 6: - return (uint)115070.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)97192.0f, + 2 => (uint)103280.0f, + 4 => (uint)109580.0f, + 6 => (uint)115070.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)492.01f; - case 2: - return (uint)554.46f; - case 4: - return (uint)595.86f; - case 6: - return (uint)656.62f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)492.01f, + 2 => (uint)554.46f, + 4 => (uint)595.86f, + 6 => (uint)656.62f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)136460.0f; - case 2: - return (uint)145750.0f; - case 4: - return (uint)154800.0f; - case 6: - return (uint)161970.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)136460.0f, + 2 => (uint)145750.0f, + 4 => (uint)154800.0f, + 6 => (uint)161970.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)495.79f; - case 2: - return (uint)527.16f; - case 4: - return (uint)598.75f; - case 6: - return (uint)666.03f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)495.79f, + 2 => (uint)527.16f, + 4 => (uint)598.75f, + 6 => (uint)666.03f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public uint Estimate(Reverb3dCommand command) @@ -339,70 +291,46 @@ namespace Ryujinx.Audio.Renderer.Server { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)138840.0f; - case 2: - return (uint)135430.0f; - case 4: - return (uint)199180.0f; - case 6: - return (uint)247350.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)138840.0f, + 2 => (uint)135430.0f, + 4 => (uint)199180.0f, + 6 => (uint)247350.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)718.7f; - case 2: - return (uint)751.3f; - case 4: - return (uint)797.46f; - case 6: - return (uint)867.43f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)718.7f, + 2 => (uint)751.3f, + 4 => (uint)797.46f, + 6 => (uint)867.43f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)199950.0f; - case 2: - return (uint)195200.0f; - case 4: - return (uint)290580.0f; - case 6: - return (uint)363490.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)199950.0f, + 2 => (uint)195200.0f, + 4 => (uint)290580.0f, + 6 => (uint)363490.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)534.24f; - case 2: - return (uint)570.87f; - case 4: - return (uint)660.93f; - case 6: - return (uint)694.6f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)534.24f, + 2 => (uint)570.87f, + 4 => (uint)660.93f, + 6 => (uint)694.6f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public uint Estimate(AuxiliaryBufferCommand command) diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs index b79ca1369..8ae4bc059 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs @@ -12,20 +12,20 @@ namespace Ryujinx.Audio.Renderer.Server /// </summary> public class CommandProcessingTimeEstimatorVersion3 : ICommandProcessingTimeEstimator { - protected uint _sampleCount; - protected uint _bufferCount; + protected uint SampleCount; + protected uint BufferCount; public CommandProcessingTimeEstimatorVersion3(uint sampleCount, uint bufferCount) { - _sampleCount = sampleCount; - _bufferCount = bufferCount; + SampleCount = sampleCount; + BufferCount = bufferCount; } public uint Estimate(PerformanceCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)498.17f; } @@ -35,24 +35,24 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(ClearMixBufferCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); float costPerBuffer = 440.68f; float baseCost = 0; - if (_sampleCount == 160) + if (SampleCount == 160) { costPerBuffer = 266.65f; } - return (uint)(baseCost + costPerBuffer * _bufferCount); + return (uint)(baseCost + costPerBuffer * BufferCount); } public uint Estimate(BiquadFilterCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)4173.2f; } @@ -64,9 +64,9 @@ namespace Ryujinx.Audio.Renderer.Server { float costPerSample = 6.4434f; - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { costPerSample = 6.708f; } @@ -81,14 +81,14 @@ namespace Ryujinx.Audio.Renderer.Server } } - return (uint)(_sampleCount * costPerSample * volumeCount); + return (uint)(SampleCount * costPerSample * volumeCount); } public uint Estimate(MixRampCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)1968.7f; } @@ -103,9 +103,9 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(VolumeRampCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)1425.3f; } @@ -115,41 +115,41 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(PcmInt16DataSourceCommandVersion1 command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); float costPerSample = 710.143f; float baseCost = 7853.286f; - if (_sampleCount == 160) + if (SampleCount == 160) { costPerSample = 427.52f; baseCost = 6329.442f; } - return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / _sampleCount) * (command.Pitch * 0.000030518f)))); + return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / SampleCount) * (command.Pitch * 0.000030518f)))); } public uint Estimate(AdpcmDataSourceCommandVersion1 command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); float costPerSample = 3564.1f; float baseCost = 9736.702f; - if (_sampleCount == 160) + if (SampleCount == 160) { costPerSample = 2125.6f; baseCost = 7913.808f; } - return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / _sampleCount) * (command.Pitch * 0.000030518f)))); + return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / SampleCount) * (command.Pitch * 0.000030518f)))); } public uint Estimate(DepopForMixBuffersCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)739.64f; } @@ -159,9 +159,9 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(CopyMixBufferCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)842.59f; } @@ -171,9 +171,9 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(MixCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)1402.8f; } @@ -183,231 +183,159 @@ namespace Ryujinx.Audio.Renderer.Server public virtual uint Estimate(DelayCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)8929.04f; - case 2: - return (uint)25500.75f; - case 4: - return (uint)47759.62f; - case 6: - return (uint)82203.07f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)8929.04f, + 2 => (uint)25500.75f, + 4 => (uint)47759.62f, + 6 => (uint)82203.07f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)1295.20f; - case 2: - return (uint)1213.60f; - case 4: - return (uint)942.03f; - case 6: - return (uint)1001.55f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)1295.20f, + 2 => (uint)1213.60f, + 4 => (uint)942.03f, + 6 => (uint)1001.55f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)11941.05f; - case 2: - return (uint)37197.37f; - case 4: - return (uint)69749.84f; - case 6: - return (uint)120042.40f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)11941.05f, + 2 => (uint)37197.37f, + 4 => (uint)69749.84f, + 6 => (uint)120042.40f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)997.67f; - case 2: - return (uint)977.63f; - case 4: - return (uint)792.30f; - case 6: - return (uint)875.43f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)997.67f, + 2 => (uint)977.63f, + 4 => (uint)792.30f, + 6 => (uint)875.43f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public virtual uint Estimate(ReverbCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)81475.05f; - case 2: - return (uint)84975.0f; - case 4: - return (uint)91625.15f; - case 6: - return (uint)95332.27f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)81475.05f, + 2 => (uint)84975.0f, + 4 => (uint)91625.15f, + 6 => (uint)95332.27f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)536.30f; - case 2: - return (uint)588.70f; - case 4: - return (uint)643.70f; - case 6: - return (uint)706.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)536.30f, + 2 => (uint)588.70f, + 4 => (uint)643.70f, + 6 => (uint)706.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)120174.47f; - case 2: - return (uint)25262.22f; - case 4: - return (uint)135751.23f; - case 6: - return (uint)141129.23f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)120174.47f, + 2 => (uint)25262.22f, + 4 => (uint)135751.23f, + 6 => (uint)141129.23f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)617.64f; - case 2: - return (uint)659.54f; - case 4: - return (uint)711.43f; - case 6: - return (uint)778.07f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)617.64f, + 2 => (uint)659.54f, + 4 => (uint)711.43f, + 6 => (uint)778.07f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public virtual uint Estimate(Reverb3dCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)116754.0f; - case 2: - return (uint)125912.05f; - case 4: - return (uint)146336.03f; - case 6: - return (uint)165812.66f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)116754.0f, + 2 => (uint)125912.05f, + 4 => (uint)146336.03f, + 6 => (uint)165812.66f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)734.0f; - case 2: - return (uint)766.62f; - case 4: - return (uint)797.46f; - case 6: - return (uint)867.43f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)734.0f, + 2 => (uint)766.62f, + 4 => (uint)797.46f, + 6 => (uint)867.43f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)170292.34f; - case 2: - return (uint)183875.63f; - case 4: - return (uint)214696.19f; - case 6: - return (uint)243846.77f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)170292.34f, + 2 => (uint)183875.63f, + 4 => (uint)214696.19f, + 6 => (uint)243846.77f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)508.47f; - case 2: - return (uint)582.45f; - case 4: - return (uint)626.42f; - case 6: - return (uint)682.47f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)508.47f, + 2 => (uint)582.45f, + 4 => (uint)626.42f, + 6 => (uint)682.47f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public uint Estimate(AuxiliaryBufferCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { @@ -427,9 +355,9 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(VolumeCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)1311.1f; } @@ -439,12 +367,12 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(CircularBufferSinkCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); float costPerBuffer = 770.26f; float baseCost = 0f; - if (_sampleCount == 160) + if (SampleCount == 160) { costPerBuffer = 531.07f; } @@ -454,9 +382,9 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(DownMixSurroundToStereoCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)9949.7f; } @@ -466,9 +394,9 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(UpsampleCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)312990.0f; } @@ -478,12 +406,12 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(DeviceSinkCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); Debug.Assert(command.InputCount == 2 || command.InputCount == 6); if (command.InputCount == 2) { - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)8980.0f; } @@ -491,7 +419,7 @@ namespace Ryujinx.Audio.Renderer.Server return (uint)9221.9f; } - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)9177.9f; } @@ -501,27 +429,27 @@ namespace Ryujinx.Audio.Renderer.Server public uint Estimate(PcmFloatDataSourceCommandVersion1 command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); float costPerSample = 3490.9f; float baseCost = 10090.9f; - if (_sampleCount == 160) + if (SampleCount == 160) { costPerSample = 2310.4f; baseCost = 7845.25f; } - return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / _sampleCount) * (command.Pitch * 0.000030518f)))); + return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / SampleCount) * (command.Pitch * 0.000030518f)))); } public uint Estimate(DataSourceVersion2Command command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - (float baseCost, float costPerSample) = GetCostByFormat(_sampleCount, command.SampleFormat, command.SrcQuality); + (float baseCost, float costPerSample) = GetCostByFormat(SampleCount, command.SampleFormat, command.SrcQuality); - return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / _sampleCount) * (command.Pitch * 0.000030518f) - 1.0f))); + return (uint)(baseCost + (costPerSample * (((command.SampleRate / 200.0f) / SampleCount) * (command.Pitch * 0.000030518f) - 1.0f))); } private static (float, float) GetCostByFormat(uint sampleCount, SampleFormat format, SampleRateConversionQuality quality) @@ -618,124 +546,90 @@ namespace Ryujinx.Audio.Renderer.Server private uint EstimateLimiterCommandCommon(LimiterParameter parameter, bool enabled) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (enabled) { - switch (parameter.ChannelCount) + return parameter.ChannelCount switch { - case 1: - return (uint)21392.0f; - case 2: - return (uint)26829.0f; - case 4: - return (uint)32405.0f; - case 6: - return (uint)52219.0f; - default: - throw new NotImplementedException($"{parameter.ChannelCount}"); - } + 1 => (uint)21392.0f, + 2 => (uint)26829.0f, + 4 => (uint)32405.0f, + 6 => (uint)52219.0f, + _ => throw new NotImplementedException($"{parameter.ChannelCount}"), + }; } - else + + return parameter.ChannelCount switch { - switch (parameter.ChannelCount) - { - case 1: - return (uint)897.0f; - case 2: - return (uint)931.55f; - case 4: - return (uint)975.39f; - case 6: - return (uint)1016.8f; - default: - throw new NotImplementedException($"{parameter.ChannelCount}"); - } - } + 1 => (uint)897.0f, + 2 => (uint)931.55f, + 4 => (uint)975.39f, + 6 => (uint)1016.8f, + _ => throw new NotImplementedException($"{parameter.ChannelCount}"), + }; } if (enabled) { - switch (parameter.ChannelCount) + return parameter.ChannelCount switch { - case 1: - return (uint)30556.0f; - case 2: - return (uint)39011.0f; - case 4: - return (uint)48270.0f; - case 6: - return (uint)76712.0f; - default: - throw new NotImplementedException($"{parameter.ChannelCount}"); - } + 1 => (uint)30556.0f, + 2 => (uint)39011.0f, + 4 => (uint)48270.0f, + 6 => (uint)76712.0f, + _ => throw new NotImplementedException($"{parameter.ChannelCount}"), + }; } - else + + return parameter.ChannelCount switch { - switch (parameter.ChannelCount) - { - case 1: - return (uint)874.43f; - case 2: - return (uint)921.55f; - case 4: - return (uint)945.26f; - case 6: - return (uint)992.26f; - default: - throw new NotImplementedException($"{parameter.ChannelCount}"); - } - } + 1 => (uint)874.43f, + 2 => (uint)921.55f, + 4 => (uint)945.26f, + 6 => (uint)992.26f, + _ => throw new NotImplementedException($"{parameter.ChannelCount}"), + }; } public uint Estimate(LimiterCommandVersion1 command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled); } public uint Estimate(LimiterCommandVersion2 command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); if (!command.Parameter.StatisticsEnabled || !command.IsEffectEnabled) { return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled); } - if (_sampleCount == 160) + if (SampleCount == 160) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)23309.0f; - case 2: - return (uint)29954.0f; - case 4: - return (uint)35807.0f; - case 6: - return (uint)58340.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)23309.0f, + 2 => (uint)29954.0f, + 4 => (uint)35807.0f, + 6 => (uint)58340.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return (uint)33526.0f; - case 2: - return (uint)43549.0f; - case 4: - return (uint)52190.0f; - case 6: - return (uint)85527.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => (uint)33526.0f, + 2 => (uint)43549.0f, + 4 => (uint)52190.0f, + 6 => (uint)85527.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public virtual uint Estimate(GroupedBiquadFilterCommand command) @@ -753,4 +647,4 @@ namespace Ryujinx.Audio.Renderer.Server return 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs index c60d8ebcb..25bc67cd9 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion4.cs @@ -12,9 +12,9 @@ namespace Ryujinx.Audio.Renderer.Server public override uint Estimate(GroupedBiquadFilterCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { return (uint)7424.5f; } @@ -24,9 +24,9 @@ namespace Ryujinx.Audio.Renderer.Server public override uint Estimate(CaptureBufferCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { @@ -44,4 +44,4 @@ namespace Ryujinx.Audio.Renderer.Server return (uint)435.2f; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs index 2ed7e6a5b..7135c1c4f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs @@ -13,298 +13,202 @@ namespace Ryujinx.Audio.Renderer.Server public override uint Estimate(DelayCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 8929; - case 2: - return 25501; - case 4: - return 47760; - case 6: - return 82203; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 8929, + 2 => 25501, + 4 => 47760, + 6 => 82203, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)1295.20f; - case 2: - return (uint)1213.60f; - case 4: - return (uint)942.03f; - case 6: - return (uint)1001.6f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)1295.20f, + 2 => (uint)1213.60f, + 4 => (uint)942.03f, + 6 => (uint)1001.6f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 11941; - case 2: - return 37197; - case 4: - return 69750; - case 6: - return 12004; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 11941, + 2 => 37197, + 4 => 69750, + 6 => 12004, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)997.67f; - case 2: - return (uint)977.63f; - case 4: - return (uint)792.31f; - case 6: - return (uint)875.43f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)997.67f, + 2 => (uint)977.63f, + 4 => (uint)792.31f, + 6 => (uint)875.43f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public override uint Estimate(ReverbCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 81475; - case 2: - return 84975; - case 4: - return 91625; - case 6: - return 95332; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 81475, + 2 => 84975, + 4 => 91625, + 6 => 95332, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)536.30f; - case 2: - return (uint)588.80f; - case 4: - return (uint)643.70f; - case 6: - return (uint)706.0f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)536.30f, + 2 => (uint)588.80f, + 4 => (uint)643.70f, + 6 => (uint)706.0f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 120170; - case 2: - return 125260; - case 4: - return 135750; - case 6: - return 141130; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 120170, + 2 => 125260, + 4 => 135750, + 6 => 141130, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)617.64f; - case 2: - return (uint)659.54f; - case 4: - return (uint)711.44f; - case 6: - return (uint)778.07f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)617.64f, + 2 => (uint)659.54f, + 4 => (uint)711.44f, + 6 => (uint)778.07f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public override uint Estimate(Reverb3dCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 116750; - case 2: - return 125910; - case 4: - return 146340; - case 6: - return 165810; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 116750, + 2 => 125910, + 4 => 146340, + 6 => 165810, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return 735; - case 2: - return (uint)766.62f; - case 4: - return (uint)834.07f; - case 6: - return (uint)875.44f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => 735, + 2 => (uint)766.62f, + 4 => (uint)834.07f, + 6 => (uint)875.44f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 170290; - case 2: - return 183880; - case 4: - return 214700; - case 6: - return 243850; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 170290, + 2 => 183880, + 4 => 214700, + 6 => 243850, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)508.47f; - case 2: - return (uint)582.45f; - case 4: - return (uint)626.42f; - case 6: - return (uint)682.47f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)508.47f, + 2 => (uint)582.45f, + 4 => (uint)626.42f, + 6 => (uint)682.47f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } public override uint Estimate(CompressorCommand command) { - Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + Debug.Assert(SampleCount == 160 || SampleCount == 240); - if (_sampleCount == 160) + if (SampleCount == 160) { if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 34431; - case 2: - return 44253; - case 4: - return 63827; - case 6: - return 83361; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 34431, + 2 => 44253, + 4 => 63827, + 6 => 83361, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)630.12f; - case 2: - return (uint)638.27f; - case 4: - return (uint)705.86f; - case 6: - return (uint)782.02f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)630.12f, + 2 => (uint)638.27f, + 4 => (uint)705.86f, + 6 => (uint)782.02f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } if (command.Enabled) { - switch (command.Parameter.ChannelCount) + return command.Parameter.ChannelCount switch { - case 1: - return 51095; - case 2: - return 65693; - case 4: - return 95383; - case 6: - return 124510; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } + 1 => 51095, + 2 => 65693, + 4 => 95383, + 6 => 124510, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } - else + + return command.Parameter.ChannelCount switch { - switch (command.Parameter.ChannelCount) - { - case 1: - return (uint)840.14f; - case 2: - return (uint)826.1f; - case 4: - return (uint)901.88f; - case 6: - return (uint)965.29f; - default: - throw new NotImplementedException($"{command.Parameter.ChannelCount}"); - } - } + 1 => (uint)840.14f, + 2 => (uint)826.1f, + 4 => (uint)901.88f, + 6 => (uint)965.29f, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs index 164065271..57ca266f4 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs @@ -58,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect { ulong bufferSize = (ulong)Unsafe.SizeOf<int>() * Parameter.BufferStorageSize + (ulong)Unsafe.SizeOf<AuxiliaryBufferHeader>(); - bool sendBufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[0], Parameter.SendBufferInfoAddress, bufferSize); + bool sendBufferUnmapped = !mapper.TryAttachBuffer(out _, ref WorkBuffers[0], Parameter.SendBufferInfoAddress, bufferSize); bool returnBufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[1], Parameter.ReturnBufferInfoAddress, bufferSize); BufferUnmapped = sendBufferUnmapped && returnBufferUnmapped; @@ -82,4 +82,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect UpdateUsageStateForCommandGeneration(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs index 825b3bf76..a9716db2a 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs @@ -244,29 +244,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// <returns>The <see cref="PerformanceDetailType"/> associated to the <see cref="Type"/> of this effect.</returns> public PerformanceDetailType GetPerformanceDetailType() { - switch (Type) + return Type switch { - case EffectType.BiquadFilter: - return PerformanceDetailType.BiquadFilter; - case EffectType.AuxiliaryBuffer: - return PerformanceDetailType.Aux; - case EffectType.Delay: - return PerformanceDetailType.Delay; - case EffectType.Reverb: - return PerformanceDetailType.Reverb; - case EffectType.Reverb3d: - return PerformanceDetailType.Reverb3d; - case EffectType.BufferMix: - return PerformanceDetailType.Mix; - case EffectType.Limiter: - return PerformanceDetailType.Limiter; - case EffectType.CaptureBuffer: - return PerformanceDetailType.CaptureBuffer; - case EffectType.Compressor: - return PerformanceDetailType.Compressor; - default: - throw new NotImplementedException($"{Type}"); - } + EffectType.BiquadFilter => PerformanceDetailType.BiquadFilter, + EffectType.AuxiliaryBuffer => PerformanceDetailType.Aux, + EffectType.Delay => PerformanceDetailType.Delay, + EffectType.Reverb => PerformanceDetailType.Reverb, + EffectType.Reverb3d => PerformanceDetailType.Reverb3d, + EffectType.BufferMix => PerformanceDetailType.Mix, + EffectType.Limiter => PerformanceDetailType.Limiter, + EffectType.CaptureBuffer => PerformanceDetailType.CaptureBuffer, + EffectType.Compressor => PerformanceDetailType.Compressor, + _ => throw new NotImplementedException($"{Type}"), + }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs index de91046dc..b987f7c85 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs @@ -64,4 +64,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect Parameter.Status = UsageState.Enabled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs index 82c0a055a..d6cb9cfa3 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs @@ -46,4 +46,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect UpdateUsageStateForCommandGeneration(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs index c445798d4..5be4b4ed5 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs @@ -79,4 +79,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect UpdateUsageStateForCommandGeneration(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs index 3f5d70bcf..43cabb7db 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs @@ -90,4 +90,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect Parameter.Status = UsageState.Enabled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs index bfb6528b4..619f31100 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs @@ -120,4 +120,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs index 6e17ef3d1..3e2f7326d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs @@ -92,4 +92,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect destState = srcState; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs index 473fddb84..f9d7f4943 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs @@ -89,4 +89,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect Parameter.ParameterStatus = UsageState.Enabled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs index e1543fd17..6fdf8fc23 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs @@ -92,4 +92,4 @@ namespace Ryujinx.Audio.Renderer.Server.Effect Parameter.Status = UsageState.Enabled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs index 8648aa2ca..da7172244 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs @@ -23,6 +23,6 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// <summary> /// The effect is disabled. /// </summary> - Disabled + Disabled, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs b/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs index 4872ddb3a..27b22363a 100644 --- a/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs @@ -37,4 +37,4 @@ namespace Ryujinx.Audio.Renderer.Server uint Estimate(CaptureBufferCommand command); uint Estimate(CompressorCommand command); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs index 5fd6b2b92..a7ec4cf51 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs @@ -27,9 +27,9 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// </summary> public DspAddress ForceMappedDspAddress; - private unsafe ref MemoryPoolState MemoryPoolState => ref *_memoryPools; + private readonly unsafe ref MemoryPoolState MemoryPoolState => ref *_memoryPools; - public unsafe bool HasMemoryPoolState => (IntPtr)_memoryPools != IntPtr.Zero; + public readonly unsafe bool HasMemoryPoolState => (IntPtr)_memoryPools != IntPtr.Zero; /// <summary> /// Create an new empty <see cref="AddressInfo"/>. @@ -55,7 +55,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool CpuAddress = cpuAddress, _memoryPools = MemoryPoolState.Null, Size = size, - ForceMappedDspAddress = 0 + ForceMappedDspAddress = 0, }; } } @@ -105,7 +105,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// Check if the <see cref="MemoryPoolState"/> is mapped. /// </summary> /// <returns>Returns true if the <see cref="MemoryPoolState"/> is mapped.</returns> - public bool HasMappedMemoryPool() + public readonly bool HasMappedMemoryPool() { return HasMemoryPoolState && MemoryPoolState.IsMapped(); } @@ -115,7 +115,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// </summary> /// <param name="markUsed">If true, mark the <see cref="MemoryPoolState"/> as used.</param> /// <returns>Returns the DSP address associated to the <see cref="AddressInfo"/>.</returns> - public DspAddress GetReference(bool markUsed) + public readonly DspAddress GetReference(bool markUsed) { if (!HasMappedMemoryPool()) { @@ -130,4 +130,4 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool return MemoryPoolState.Translate(CpuAddress, Size); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs index 69466bab5..91bd5dbf5 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// <summary> /// <see cref="MemoryPoolState"/> located on the DSP side for system use. /// </summary> - Dsp + Dsp, } /// <summary> @@ -69,7 +69,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool CpuAddress = 0, DspAddress = 0, Size = 0, - Location = location + Location = location, }; } @@ -90,7 +90,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param> /// <param name="size">The size.</param> /// <returns>True if the <see cref="CpuAddress"/> is contained inside the <see cref="MemoryPoolState"/>.</returns> - public bool Contains(CpuAddress targetCpuAddress, ulong size) + public readonly bool Contains(CpuAddress targetCpuAddress, ulong size) { if (CpuAddress <= targetCpuAddress && size + targetCpuAddress <= Size + CpuAddress) { @@ -106,7 +106,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param> /// <param name="size">The size.</param> /// <returns>the target DSP address.</returns> - public DspAddress Translate(CpuAddress targetCpuAddress, ulong size) + public readonly DspAddress Translate(CpuAddress targetCpuAddress, ulong size) { if (Contains(targetCpuAddress, size) && IsMapped()) { @@ -122,9 +122,9 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// Is the <see cref="MemoryPoolState"/> mapped on the DSP? /// </summary> /// <returns>Returns true if the <see cref="MemoryPoolState"/> is mapped on the DSP.</returns> - public bool IsMapped() + public readonly bool IsMapped() { return DspAddress != 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs index 4a29ead3e..391b80f8d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs @@ -40,23 +40,23 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool /// <summary> /// <see cref="Dsp.AudioProcessor"/> unmapping failed. /// </summary> - UnmapError = 3 + UnmapError = 3, } /// <summary> /// The handle of the process owning the CPU memory manipulated. /// </summary> - private uint _processHandle; + private readonly uint _processHandle; /// <summary> /// The <see cref="Memory{MemoryPoolState}"/> that will be manipulated. /// </summary> - private Memory<MemoryPoolState> _memoryPools; + private readonly Memory<MemoryPoolState> _memoryPools; /// <summary> /// If set to true, this will try to force map memory pool even if their state are considered invalid. /// </summary> - private bool _isForceMapEnabled; + private readonly bool _isForceMapEnabled; /// <summary> /// Create a new <see cref="PoolMapper"/> used for system mapping. @@ -125,7 +125,8 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool { return CurrentProcessPseudoHandle; } - else if (memoryPool.Location == MemoryPoolState.LocationType.Dsp) + + if (memoryPool.Location == MemoryPoolState.LocationType.Dsp) { return _processHandle; } @@ -234,13 +235,11 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool return true; } - else - { - errorInfo.ErrorCode = ResultCode.InvalidAddressInfo; - errorInfo.ExtraErrorInfo = addressInfo.CpuAddress; - return _isForceMapEnabled; - } + errorInfo.ErrorCode = ResultCode.InvalidAddressInfo; + errorInfo.ExtraErrorInfo = addressInfo.CpuAddress; + + return _isForceMapEnabled; } /// <summary> diff --git a/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs index cda6f737c..8991ceaf9 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs @@ -206,7 +206,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix { UpdateDistancesFromFinalMix(); - int[] sortedMixesTemp = _sortedMixes.Slice(0, (int)GetCount()).ToArray(); + int[] sortedMixesTemp = _sortedMixes[..(int)GetCount()].ToArray(); Array.Sort(sortedMixesTemp, (a, b) => { @@ -248,12 +248,10 @@ namespace Ryujinx.Audio.Renderer.Server.Mix return isValid; } - else - { - UpdateMixBufferOffset(); - return true; - } + UpdateMixBufferOffset(); + + return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs index 146e67811..88ae44831 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs @@ -7,7 +7,6 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - using static Ryujinx.Audio.Constants; namespace Ryujinx.Audio.Renderer.Server.Mix @@ -66,7 +65,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// <summary> /// The effect processing order storage. /// </summary> - private IntPtr _effectProcessingOrderArrayPointer; + private readonly IntPtr _effectProcessingOrderArrayPointer; /// <summary> /// The max element count that can be found in the effect processing order storage. @@ -120,7 +119,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// <summary> /// The array used to order effects associated to this mix. /// </summary> - public Span<int> EffectProcessingOrderArray + public readonly Span<int> EffectProcessingOrderArray { get { @@ -175,7 +174,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// <summary> /// Clear the <see cref="EffectProcessingOrderArray"/> to its default state. /// </summary> - public void ClearEffectProcessingOrder() + public readonly void ClearEffectProcessingOrder() { EffectProcessingOrderArray.Fill(-1); } @@ -184,7 +183,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// Return true if the mix has any destinations. /// </summary> /// <returns>True if the mix has any destinations.</returns> - public bool HasAnyDestination() + public readonly bool HasAnyDestination() { return DestinationMixId != UnusedMixId || DestinationSplitterId != UnusedSplitterId; } @@ -310,4 +309,4 @@ namespace Ryujinx.Audio.Renderer.Server.Mix return isDirty; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs index dbe59cb0d..ffabf4676 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs @@ -49,4 +49,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// <param name="detailType">The type to use.</param> void SetDetailType(PerformanceDetailType detailType); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs index 9888a4cc1..a0178187b 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs @@ -43,4 +43,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// <param name="type">The type to use.</param> void SetEntryType(PerformanceEntryType type); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs index 21876b4b4..deacd8ccc 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs @@ -77,4 +77,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// <param name="entryDetailCount">The total count of detailed entries in this frame.</param> void SetEntryDetailCount(int entryDetailCount); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs index 22704c0d1..a4024607c 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs @@ -34,22 +34,22 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// </summary> public PerformanceEntryType EntryType; - public int GetProcessingTime() + public readonly int GetProcessingTime() { return ProcessingTime; } - public int GetProcessingTimeOffset() + public readonly int GetProcessingTimeOffset() { return 8; } - public int GetStartTime() + public readonly int GetStartTime() { return StartTime; } - public int GetStartTimeOffset() + public readonly int GetStartTimeOffset() { return 4; } @@ -69,4 +69,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance NodeId = nodeId; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs index 05ecda9b6..f10e2937e 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs @@ -34,22 +34,22 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// </summary> public PerformanceEntryType EntryType; - public int GetProcessingTime() + public readonly int GetProcessingTime() { return ProcessingTime; } - public int GetProcessingTimeOffset() + public readonly int GetProcessingTimeOffset() { return 8; } - public int GetStartTime() + public readonly int GetStartTime() { return StartTime; } - public int GetStartTimeOffset() + public readonly int GetStartTimeOffset() { return 4; } @@ -69,4 +69,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance NodeId = nodeId; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs index 1b8d8668a..d24b96a27 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs @@ -53,4 +53,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance BaseMemory.Span[(int)ProcessingTimeOffset / 4] = (int)(endTimeNano / 1000) - BaseMemory.Span[(int)StartTimeOffset / 4]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs index fa2d32164..2c407670f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs @@ -29,22 +29,22 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// </summary> public PerformanceEntryType EntryType; - public int GetProcessingTime() + public readonly int GetProcessingTime() { return ProcessingTime; } - public int GetProcessingTimeOffset() + public readonly int GetProcessingTimeOffset() { return 8; } - public int GetStartTime() + public readonly int GetStartTime() { return StartTime; } - public int GetStartTimeOffset() + public readonly int GetStartTimeOffset() { return 4; } @@ -59,4 +59,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance NodeId = nodeId; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs index 49d4b3ce0..eb96a3141 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs @@ -29,22 +29,22 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// </summary> public PerformanceEntryType EntryType; - public int GetProcessingTime() + public readonly int GetProcessingTime() { return ProcessingTime; } - public int GetProcessingTimeOffset() + public readonly int GetProcessingTimeOffset() { return 8; } - public int GetStartTime() + public readonly int GetStartTime() { return StartTime; } - public int GetStartTimeOffset() + public readonly int GetStartTimeOffset() { return 4; } @@ -59,4 +59,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance NodeId = nodeId; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs index 5fe6bff06..5aeb703c5 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs @@ -38,22 +38,22 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// </summary> public uint VoiceDropCount; - public int GetEntryCount() + public readonly int GetEntryCount() { return EntryCount; } - public int GetEntryCountOffset() + public readonly int GetEntryCountOffset() { return 4; } - public int GetEntryDetailCount() + public readonly int GetEntryDetailCount() { return EntryDetailCount; } - public void SetDspRunningBehind(bool isRunningBehind) + public readonly void SetDspRunningBehind(bool isRunningBehind) { // NOTE: Not present in version 1 } @@ -68,7 +68,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance EntryDetailCount = entryDetailCount; } - public void SetIndex(uint index) + public readonly void SetIndex(uint index) { // NOTE: Not present in version 1 } @@ -83,7 +83,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance NextOffset = nextOffset; } - public void SetStartRenderingTicks(ulong startTicks) + public readonly void SetStartRenderingTicks(ulong startTicks) { // NOTE: not present in version 1 } @@ -98,4 +98,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance VoiceDropCount = voiceCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs index a18229686..d6e0ffc8b 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs @@ -54,17 +54,17 @@ namespace Ryujinx.Audio.Renderer.Server.Performance [MarshalAs(UnmanagedType.I1)] public bool IsDspRunningBehind; - public int GetEntryCount() + public readonly int GetEntryCount() { return EntryCount; } - public int GetEntryCountOffset() + public readonly int GetEntryCountOffset() { return 4; } - public int GetEntryDetailCount() + public readonly int GetEntryDetailCount() { return EntryDetailCount; } @@ -114,4 +114,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance VoiceDropCount = voiceCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs index f996441c0..0a035916c 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs @@ -22,11 +22,12 @@ namespace Ryujinx.Audio.Renderer.Server.Performance PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); } - else if (version == 1) + + if (version == 1) { return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, - PerformanceEntryVersion1, - PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + PerformanceEntryVersion1, + PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); } throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); @@ -90,17 +91,12 @@ namespace Ryujinx.Audio.Renderer.Server.Performance { uint version = behaviourContext.GetPerformanceMetricsDataFormat(); - switch (version) + return version switch { - case 1: - return new PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>(performanceBuffer, - ref parameter); - case 2: - return new PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>(performanceBuffer, - ref parameter); - default: - throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); - } + 1 => new PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>(performanceBuffer, ref parameter), + 2 => new PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>(performanceBuffer, ref parameter), + _ => throw new NotImplementedException($"Unknown Performance metrics data format version {version}"), + }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs index 18e77391d..5a70a1bcf 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs @@ -25,20 +25,20 @@ namespace Ryujinx.Audio.Renderer.Server.Performance /// </summary> private const int MaxFrameDetailCount = 100; - private Memory<byte> _buffer; - private Memory<byte> _historyBuffer; + private readonly Memory<byte> _buffer; + private readonly Memory<byte> _historyBuffer; - private Memory<byte> CurrentBuffer => _buffer.Slice(0, _frameSize); - private Memory<byte> CurrentBufferData => CurrentBuffer.Slice(Unsafe.SizeOf<THeader>()); + private Memory<byte> CurrentBuffer => _buffer[.._frameSize]; + private Memory<byte> CurrentBufferData => CurrentBuffer[Unsafe.SizeOf<THeader>()..]; private ref THeader CurrentHeader => ref MemoryMarshal.Cast<byte, THeader>(CurrentBuffer.Span)[0]; - private Span<TEntry> Entries => MemoryMarshal.Cast<byte, TEntry>(CurrentBufferData.Span.Slice(0, GetEntriesSize())); + private Span<TEntry> Entries => MemoryMarshal.Cast<byte, TEntry>(CurrentBufferData.Span[..GetEntriesSize()]); private Span<TEntryDetail> EntriesDetail => MemoryMarshal.Cast<byte, TEntryDetail>(CurrentBufferData.Span.Slice(GetEntriesSize(), GetEntriesDetailSize())); - private int _frameSize; - private int _availableFrameCount; - private int _entryCountPerFrame; + private readonly int _frameSize; + private readonly int _availableFrameCount; + private readonly int _entryCountPerFrame; private int _detailTarget; private int _entryIndex; private int _entryDetailIndex; @@ -56,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance _historyFrameIndex = 0; - _historyBuffer = _buffer.Slice(_frameSize); + _historyBuffer = _buffer[_frameSize..]; SetupNewHeader(); } @@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance Span<TEntry> inputEntries = GetEntriesFromBuffer(_historyBuffer.Span, _indexHistoryRead); Span<TEntryDetail> inputEntriesDetail = GetEntriesDetailFromBuffer(_historyBuffer.Span, _indexHistoryRead); - Span<byte> targetSpan = performanceOutput.Slice(nextOffset); + Span<byte> targetSpan = performanceOutput[nextOffset..]; // NOTE: We check for the space for two headers for the final blank header. int requiredSpace = Unsafe.SizeOf<THeader>() + Unsafe.SizeOf<TEntry>() * inputHeader.GetEntryCount() @@ -146,7 +146,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance nextOffset += Unsafe.SizeOf<THeader>(); - Span<TEntry> outputEntries = MemoryMarshal.Cast<byte, TEntry>(performanceOutput.Slice(nextOffset)); + Span<TEntry> outputEntries = MemoryMarshal.Cast<byte, TEntry>(performanceOutput[nextOffset..]); int totalProcessingTime = 0; @@ -168,7 +168,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance } } - Span<TEntryDetail> outputEntriesDetail = MemoryMarshal.Cast<byte, TEntryDetail>(performanceOutput.Slice(nextOffset)); + Span<TEntryDetail> outputEntriesDetail = MemoryMarshal.Cast<byte, TEntryDetail>(performanceOutput[nextOffset..]); int effectiveEntryDetailCount = 0; @@ -198,7 +198,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance if (nextOffset < performanceOutput.Length && (performanceOutput.Length - nextOffset) >= Unsafe.SizeOf<THeader>()) { - ref THeader outputHeader = ref MemoryMarshal.Cast<byte, THeader>(performanceOutput.Slice(nextOffset))[0]; + ref THeader outputHeader = ref MemoryMarshal.Cast<byte, THeader>(performanceOutput[nextOffset..])[0]; outputHeader = default; } @@ -208,9 +208,11 @@ namespace Ryujinx.Audio.Renderer.Server.Performance public override bool GetNextEntry(out PerformanceEntryAddresses performanceEntry, PerformanceEntryType entryType, int nodeId) { - performanceEntry = new PerformanceEntryAddresses(); - performanceEntry.BaseMemory = SpanMemoryManager<int>.Cast(CurrentBuffer); - performanceEntry.EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(); + performanceEntry = new PerformanceEntryAddresses + { + BaseMemory = SpanMemoryManager<int>.Cast(CurrentBuffer), + EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(), + }; uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + Unsafe.SizeOf<TEntry>() * _entryIndex); @@ -237,9 +239,11 @@ namespace Ryujinx.Audio.Renderer.Server.Performance return false; } - performanceEntry = new PerformanceEntryAddresses(); - performanceEntry.BaseMemory = SpanMemoryManager<int>.Cast(CurrentBuffer); - performanceEntry.EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(); + performanceEntry = new PerformanceEntryAddresses + { + BaseMemory = SpanMemoryManager<int>.Cast(CurrentBuffer), + EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(), + }; uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex); @@ -301,4 +305,4 @@ namespace Ryujinx.Audio.Renderer.Server.Performance } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs b/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs index 164567806..090850018 100644 --- a/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs @@ -45,4 +45,4 @@ namespace Ryujinx.Audio.Renderer.Server /// </remarks> public Memory<float> DepopBuffer; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs index f7b639975..d36c5e260 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs @@ -99,4 +99,4 @@ namespace Ryujinx.Audio.Renderer.Server.Sink errorInfo = new ErrorInfo(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs index 722d8c4b4..097757988 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs @@ -106,4 +106,4 @@ namespace Ryujinx.Audio.Renderer.Server.Sink base.CleanUp(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs index de345d3ad..e03fe11d4 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs @@ -72,4 +72,4 @@ namespace Ryujinx.Audio.Renderer.Server.Sink outStatus = new SinkOutStatus(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs index b57d39908..951984d8c 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs @@ -53,4 +53,4 @@ namespace Ryujinx.Audio.Renderer.Server.Sink return ref _sinks[id]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs index 91877cdda..e408692ab 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs @@ -101,10 +101,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter return size; } - else - { - return size; - } + + return size; } /// <summary> @@ -164,10 +162,10 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { ref SplitterState splitter = ref GetState(parameter.Id); - splitter.Update(this, ref parameter, input.Slice(Unsafe.SizeOf<SplitterInParameter>())); + splitter.Update(this, ref parameter, input[Unsafe.SizeOf<SplitterInParameter>()..]); } - input = input.Slice(0x1C + (int)parameter.DestinationCount * 4); + input = input[(0x1C + parameter.DestinationCount * 4)..]; } } } @@ -194,7 +192,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter destination.Update(parameter); } - input = input.Slice(Unsafe.SizeOf<SplitterDestinationInParameter>()); + input = input[Unsafe.SizeOf<SplitterDestinationInParameter>()..]; } } } @@ -229,12 +227,10 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter return true; } - else - { - consumedSize = 0; - return false; - } + consumedSize = 0; + + return false; } /// <summary> @@ -300,4 +296,4 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs index c074e4a72..1faf7921f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// <summary> /// Get the <see cref="Span{SplitterDestination}"/> of the next element or <see cref="Span{SplitterDestination}.Empty"/> if not present. /// </summary> - public Span<SplitterDestination> Next + public readonly Span<SplitterDestination> Next { get { @@ -138,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Return true if the <see cref="SplitterDestination"/> is used and has a destination. /// </summary> /// <returns>True if the <see cref="SplitterDestination"/> is used and has a destination.</returns> - public bool IsConfigured() + public readonly bool IsConfigured() { return IsUsed && DestinationId != Constants.UnusedMixId; } @@ -160,8 +160,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// </summary> public void ClearVolumes() { - MixBufferVolume.Fill(0); - PreviousMixBufferVolume.Fill(0); + MixBufferVolume.Clear(); + PreviousMixBufferVolume.Clear(); } /// <summary> @@ -190,4 +190,4 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs index 15a0c6ba4..e08ee9ea7 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// <summary> /// Span to the first element of the linked list of <see cref="SplitterDestination"/>. /// </summary> - public Span<SplitterDestination> Destinations + public readonly Span<SplitterDestination> Destinations { get { @@ -63,7 +63,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter Id = id; } - public Span<SplitterDestination> GetData(int index) + public readonly Span<SplitterDestination> GetData(int index) { int i = 0; @@ -95,7 +95,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Utility function to apply a given <see cref="SpanAction{T, TArg}"/> to all <see cref="Destinations"/>. /// </summary> /// <param name="action">The action to execute on each elements.</param> - private void ForEachDestination(SpanAction<SplitterDestination, int> action) + private readonly void ForEachDestination(SpanAction<SplitterDestination, int> action) { Span<SplitterDestination> temp = Destinations; @@ -183,7 +183,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// <summary> /// Update the internal state of this instance. /// </summary> - public void UpdateInternalState() + public readonly void UpdateInternalState() { ForEachDestination((destination, _) => destination[0].UpdateInternalState()); } @@ -218,4 +218,4 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs index 5cf539c6d..22eebc7cc 100644 --- a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs +++ b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs @@ -22,15 +22,15 @@ namespace Ryujinx.Audio.Renderer.Server public class StateUpdater { private readonly ReadOnlyMemory<byte> _inputOrigin; - private ReadOnlyMemory<byte> _outputOrigin; + private readonly ReadOnlyMemory<byte> _outputOrigin; private ReadOnlyMemory<byte> _input; private Memory<byte> _output; - private uint _processHandle; + private readonly uint _processHandle; private BehaviourContext _behaviourContext; private UpdateDataHeader _inputHeader; - private Memory<UpdateDataHeader> _outputHeader; + private readonly Memory<UpdateDataHeader> _outputHeader; private ref UpdateDataHeader OutputHeader => ref _outputHeader.Span[0]; @@ -45,9 +45,9 @@ namespace Ryujinx.Audio.Renderer.Server _inputHeader = SpanIOHelper.Read<UpdateDataHeader>(ref _input); - _outputHeader = SpanMemoryManager<UpdateDataHeader>.Cast(_output.Slice(0, Unsafe.SizeOf<UpdateDataHeader>())); + _outputHeader = SpanMemoryManager<UpdateDataHeader>.Cast(_output[..Unsafe.SizeOf<UpdateDataHeader>()]); OutputHeader.Initialize(_behaviourContext.UserRevision); - _output = _output.Slice(Unsafe.SizeOf<UpdateDataHeader>()); + _output = _output[Unsafe.SizeOf<UpdateDataHeader>()..]; } public ResultCode UpdateBehaviourContext() @@ -72,7 +72,7 @@ namespace Ryujinx.Audio.Renderer.Server public ResultCode UpdateMemoryPools(Span<MemoryPoolState> memoryPools) { - PoolMapper mapper = new PoolMapper(_processHandle, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + PoolMapper mapper = new(_processHandle, _behaviourContext.IsMemoryPoolForceMappingEnabled()); if (memoryPools.Length * Unsafe.SizeOf<MemoryPoolInParameter>() != _inputHeader.MemoryPoolsSize) { @@ -136,11 +136,11 @@ namespace Ryujinx.Audio.Renderer.Server int initialOutputSize = _output.Length; - ReadOnlySpan<VoiceInParameter> parameters = MemoryMarshal.Cast<byte, VoiceInParameter>(_input.Slice(0, (int)_inputHeader.VoicesSize).Span); + ReadOnlySpan<VoiceInParameter> parameters = MemoryMarshal.Cast<byte, VoiceInParameter>(_input[..(int)_inputHeader.VoicesSize].Span); - _input = _input.Slice((int)_inputHeader.VoicesSize); + _input = _input[(int)_inputHeader.VoicesSize..]; - PoolMapper mapper = new PoolMapper(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); // First make everything not in use. for (int i = 0; i < context.GetCount(); i++) @@ -151,7 +151,7 @@ namespace Ryujinx.Audio.Renderer.Server } Memory<VoiceUpdateState>[] voiceUpdateStatesArray = ArrayPool<Memory<VoiceUpdateState>>.Shared.Rent(Constants.VoiceChannelCountMax); - + Span<Memory<VoiceUpdateState>> voiceUpdateStates = voiceUpdateStatesArray.AsSpan(0, Constants.VoiceChannelCountMax); // Start processing @@ -218,42 +218,20 @@ namespace Ryujinx.Audio.Renderer.Server { effect.ForceUnmapBuffers(mapper); - switch (parameter.Type) + effect = parameter.Type switch { - case EffectType.Invalid: - effect = new BaseEffect(); - break; - case EffectType.BufferMix: - effect = new BufferMixEffect(); - break; - case EffectType.AuxiliaryBuffer: - effect = new AuxiliaryBufferEffect(); - break; - case EffectType.Delay: - effect = new DelayEffect(); - break; - case EffectType.Reverb: - effect = new ReverbEffect(); - break; - case EffectType.Reverb3d: - effect = new Reverb3dEffect(); - break; - case EffectType.BiquadFilter: - effect = new BiquadFilterEffect(); - break; - case EffectType.Limiter: - effect = new LimiterEffect(); - break; - case EffectType.CaptureBuffer: - effect = new CaptureBufferEffect(); - break; - case EffectType.Compressor: - effect = new CompressorEffect(); - break; - - default: - throw new NotImplementedException($"EffectType {parameter.Type} not implemented!"); - } + EffectType.Invalid => new BaseEffect(), + EffectType.BufferMix => new BufferMixEffect(), + EffectType.AuxiliaryBuffer => new AuxiliaryBufferEffect(), + EffectType.Delay => new DelayEffect(), + EffectType.Reverb => new ReverbEffect(), + EffectType.Reverb3d => new Reverb3dEffect(), + EffectType.BiquadFilter => new BiquadFilterEffect(), + EffectType.Limiter => new LimiterEffect(), + EffectType.CaptureBuffer => new CaptureBufferEffect(), + EffectType.Compressor => new CompressorEffect(), + _ => throw new NotImplementedException($"EffectType {parameter.Type} not implemented!"), + }; } public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools) @@ -262,10 +240,8 @@ namespace Ryujinx.Audio.Renderer.Server { return UpdateEffectsVersion2(context, isAudioRendererActive, memoryPools); } - else - { - return UpdateEffectsVersion1(context, isAudioRendererActive, memoryPools); - } + + return UpdateEffectsVersion1(context, isAudioRendererActive, memoryPools); } public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools) @@ -277,11 +253,11 @@ namespace Ryujinx.Audio.Renderer.Server int initialOutputSize = _output.Length; - ReadOnlySpan<EffectInParameterVersion2> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion2>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span); + ReadOnlySpan<EffectInParameterVersion2> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion2>(_input[..(int)_inputHeader.EffectsSize].Span); - _input = _input.Slice((int)_inputHeader.EffectsSize); + _input = _input[(int)_inputHeader.EffectsSize..]; - PoolMapper mapper = new PoolMapper(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); for (int i = 0; i < context.GetCount(); i++) { @@ -333,11 +309,11 @@ namespace Ryujinx.Audio.Renderer.Server int initialOutputSize = _output.Length; - ReadOnlySpan<EffectInParameterVersion1> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion1>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span); + ReadOnlySpan<EffectInParameterVersion1> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion1>(_input[..(int)_inputHeader.EffectsSize].Span); - _input = _input.Slice((int)_inputHeader.EffectsSize); + _input = _input[(int)_inputHeader.EffectsSize..]; - PoolMapper mapper = new PoolMapper(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); for (int i = 0; i < context.GetCount(); i++) { @@ -376,17 +352,15 @@ namespace Ryujinx.Audio.Renderer.Server { if (context.Update(_input.Span, out int consumedSize)) { - _input = _input.Slice(consumedSize); + _input = _input[consumedSize..]; return ResultCode.Success; } - else - { - return ResultCode.InvalidUpdateInfo; - } + + return ResultCode.InvalidUpdateInfo; } - private bool CheckMixParametersValidity(MixContext mixContext, uint mixBufferCount, uint inputMixCount, ReadOnlySpan<MixParameter> parameters) + private static bool CheckMixParametersValidity(MixContext mixContext, uint mixBufferCount, uint inputMixCount, ReadOnlySpan<MixParameter> parameters) { uint maxMixStateCount = mixContext.GetCount(); uint totalRequiredMixBufferCount = 0; @@ -439,12 +413,12 @@ namespace Ryujinx.Audio.Renderer.Server if (_behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()) { - _input = _input.Slice(Unsafe.SizeOf<MixInParameterDirtyOnlyUpdate>()); + _input = _input[Unsafe.SizeOf<MixInParameterDirtyOnlyUpdate>()..]; } - ReadOnlySpan<MixParameter> parameters = MemoryMarshal.Cast<byte, MixParameter>(_input.Span.Slice(0, (int)inputMixSize)); + ReadOnlySpan<MixParameter> parameters = MemoryMarshal.Cast<byte, MixParameter>(_input.Span[..(int)inputMixSize]); - _input = _input.Slice((int)inputMixSize); + _input = _input[(int)inputMixSize..]; if (CheckMixParametersValidity(mixContext, mixBufferCount, mixCount, parameters)) { @@ -506,25 +480,18 @@ namespace Ryujinx.Audio.Renderer.Server { sink.CleanUp(); - switch (parameter.Type) + sink = parameter.Type switch { - case SinkType.Invalid: - sink = new BaseSink(); - break; - case SinkType.CircularBuffer: - sink = new CircularBufferSink(); - break; - case SinkType.Device: - sink = new DeviceSink(); - break; - default: - throw new NotImplementedException($"SinkType {parameter.Type} not implemented!"); - } + SinkType.Invalid => new BaseSink(), + SinkType.CircularBuffer => new CircularBufferSink(), + SinkType.Device => new DeviceSink(), + _ => throw new NotImplementedException($"SinkType {parameter.Type} not implemented!"), + }; } public ResultCode UpdateSinks(SinkContext context, Memory<MemoryPoolState> memoryPools) { - PoolMapper mapper = new PoolMapper(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); if (context.GetCount() * Unsafe.SizeOf<SinkInParameter>() != _inputHeader.SinksSize) { @@ -533,9 +500,9 @@ namespace Ryujinx.Audio.Renderer.Server int initialOutputSize = _output.Length; - ReadOnlySpan<SinkInParameter> parameters = MemoryMarshal.Cast<byte, SinkInParameter>(_input.Slice(0, (int)_inputHeader.SinksSize).Span); + ReadOnlySpan<SinkInParameter> parameters = MemoryMarshal.Cast<byte, SinkInParameter>(_input[..(int)_inputHeader.SinksSize].Span); - _input = _input.Slice((int)_inputHeader.SinksSize); + _input = _input[(int)_inputHeader.SinksSize..]; for (int i = 0; i < context.GetCount(); i++) { @@ -640,4 +607,4 @@ namespace Ryujinx.Audio.Renderer.Server return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs b/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs index 5d82ce0b6..0db61c5e6 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs @@ -14,6 +14,6 @@ namespace Ryujinx.Audio.Renderer.Server.Types /// Audio renderer operation needs to be done manually via ExecuteAudioRenderer. /// </summary> /// <remarks>This is not supported on the DSP and is as such stubbed.</remarks> - Manual + Manual, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs b/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs index 5ad27b0b1..fd9e231cf 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs @@ -19,6 +19,6 @@ namespace Ryujinx.Audio.Renderer.Server.Types /// <remarks> /// Only supports <see cref="AudioRendererExecutionMode.Manual"/>. /// </remarks> - Cpu + Cpu, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs b/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs index 25cc34a8f..46aae05ab 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs @@ -34,6 +34,6 @@ namespace Ryujinx.Audio.Renderer.Server.Types /// <remarks> /// The user can resume to the <see cref="Started"/> state. /// </remarks> - Paused + Paused, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs index a45fa8e5b..a3c442a45 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerBufferState.cs @@ -11,4 +11,4 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler public bool Initialized; public int Phase; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs index 0fee00001..dbc2c9b3f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs @@ -11,22 +11,22 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler /// <summary> /// Work buffer for upsampler. /// </summary> - private Memory<float> _upSamplerWorkBuffer; + private readonly Memory<float> _upSamplerWorkBuffer; /// <summary> /// Global lock of the object. /// </summary> - private readonly object Lock = new(); + private readonly object _lock = new(); /// <summary> /// The upsamplers instances. /// </summary> - private UpsamplerState[] _upsamplers; + private readonly UpsamplerState[] _upsamplers; /// <summary> /// The count of upsamplers. /// </summary> - private uint _count; + private readonly uint _count; /// <summary> /// Create a new <see cref="UpsamplerManager"/>. @@ -49,7 +49,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler { int workBufferOffset = 0; - lock (Lock) + lock (_lock) { for (int i = 0; i < _count; i++) { @@ -73,7 +73,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler /// <param name="index">The index of the <see cref="UpsamplerState"/> to free.</param> public void Free(int index) { - lock (Lock) + lock (_lock) { Debug.Assert(_upsamplers[index] != null); @@ -81,4 +81,4 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs index e508f35b4..39a58c91a 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs @@ -20,12 +20,12 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler /// <summary> /// The index of the <see cref="UpsamplerState"/>. (used to free it) /// </summary> - private int _index; + private readonly int _index; /// <summary> /// The <see cref="UpsamplerManager"/>. /// </summary> - private UpsamplerManager _manager; + private readonly UpsamplerManager _manager; /// <summary> /// The source sample count. @@ -65,4 +65,4 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler _manager.Free(_index); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs index 939d92944..e3d5f797d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs @@ -37,4 +37,4 @@ namespace Ryujinx.Audio.Renderer.Server.Voice Mix.AsSpan().CopyTo(PreviousMix.AsSpan()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs index 1c57b71be..7ce7143ed 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs @@ -126,7 +126,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice _sortedVoices.Span[i] = i; } - int[] sortedVoicesTemp = _sortedVoices.Slice(0, (int)GetCount()).ToArray(); + int[] sortedVoicesTemp = _sortedVoices[..(int)GetCount()].ToArray(); Array.Sort(sortedVoicesTemp, (a, b) => { @@ -146,4 +146,4 @@ namespace Ryujinx.Audio.Renderer.Server.Voice sortedVoicesTemp.AsSpan().CopyTo(_sortedVoices.Span); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs index 0bf53c544..225f7d31b 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Server.MemoryPool; using Ryujinx.Common.Memory; @@ -9,6 +10,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using static Ryujinx.Audio.Renderer.Common.BehaviourParameter; using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; +using PlayState = Ryujinx.Audio.Renderer.Server.Types.PlayState; namespace Ryujinx.Audio.Renderer.Server.Voice { @@ -65,12 +67,12 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// <summary> /// The current voice <see cref="Types.PlayState"/>. /// </summary> - public Types.PlayState PlayState; + public PlayState PlayState; /// <summary> /// The previous voice <see cref="Types.PlayState"/>. /// </summary> - public Types.PlayState PreviousPlayState; + public PlayState PreviousPlayState; /// <summary> /// The priority of the voice. @@ -192,7 +194,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice DataSourceStateUnmapped = false; BufferInfoUnmapped = false; FlushWaveBufferCount = 0; - PlayState = Types.PlayState.Stopped; + PlayState = PlayState.Stopped; Priority = Constants.VoiceLowestPriority; Id = 0; NodeId = 0; @@ -202,7 +204,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice Pitch = 0.0f; Volume = 0.0f; PreviousVolume = 0.0f; - BiquadFilters.AsSpan().Fill(new BiquadFilterParameter()); + BiquadFilters.AsSpan().Clear(); WaveBuffersCount = 0; WaveBuffersIndex = 0; MixId = Constants.UnusedMixId; @@ -233,7 +235,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// Check if the voice needs to be skipped. /// </summary> /// <returns>Returns true if the voice needs to be skipped.</returns> - public bool ShouldSkip() + public readonly bool ShouldSkip() { return !InUse || WaveBuffersCount == 0 || DataSourceStateUnmapped || BufferInfoUnmapped || VoiceDropFlag; } @@ -242,7 +244,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// Return true if the mix has any destinations. /// </summary> /// <returns>True if the mix has any destinations.</returns> - public bool HasAnyDestination() + public readonly bool HasAnyDestination() { return MixId != Constants.UnusedMixId || SplitterId != Constants.UnusedSplitterId; } @@ -252,7 +254,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// </summary> /// <param name="parameter">The user parameter.</param> /// <returns>Return true, if the server voice information needs to be updated.</returns> - private bool ShouldUpdateParameters(ref VoiceInParameter parameter) + private readonly bool ShouldUpdateParameters(ref VoiceInParameter parameter) { if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress) { @@ -338,31 +340,31 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// Update the internal play state from user play state. /// </summary> /// <param name="userPlayState">The target user play state.</param> - public void UpdatePlayState(PlayState userPlayState) + public void UpdatePlayState(Common.PlayState userPlayState) { - Types.PlayState oldServerPlayState = PlayState; + PlayState oldServerPlayState = PlayState; PreviousPlayState = oldServerPlayState; - Types.PlayState newServerPlayState; + PlayState newServerPlayState; switch (userPlayState) { case Common.PlayState.Start: - newServerPlayState = Types.PlayState.Started; + newServerPlayState = PlayState.Started; break; case Common.PlayState.Stop: - if (oldServerPlayState == Types.PlayState.Stopped) + if (oldServerPlayState == PlayState.Stopped) { return; } - newServerPlayState = Types.PlayState.Stopping; + newServerPlayState = PlayState.Stopping; break; case Common.PlayState.Pause: - newServerPlayState = Types.PlayState.Paused; + newServerPlayState = PlayState.Paused; break; default: @@ -434,7 +436,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice for (int i = 0; i < parameter.ChannelCount; i++) { - voiceUpdateStates[i].Span[0].IsWaveBufferValid.Fill(false); + voiceUpdateStates[i].Span[0].IsWaveBufferValid.Clear(); } } @@ -530,7 +532,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice Memory<VoiceUpdateState> dspSharedState = context.GetUpdateStateForDsp(channelResourceId); - MemoryMarshal.Cast<VoiceUpdateState, byte>(dspSharedState.Span).Fill(0); + MemoryMarshal.Cast<VoiceUpdateState, byte>(dspSharedState.Span).Clear(); voiceChannelResource.UpdateState(); } @@ -579,7 +581,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice switch (PlayState) { - case Types.PlayState.Started: + case PlayState.Started: for (int i = 0; i < WaveBuffers.Length; i++) { ref WaveBuffer wavebuffer = ref WaveBuffers[i]; @@ -611,7 +613,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice return false; - case Types.PlayState.Stopping: + case PlayState.Stopping: for (int i = 0; i < WaveBuffers.Length; i++) { ref WaveBuffer wavebuffer = ref WaveBuffers[i]; @@ -638,18 +640,18 @@ namespace Ryujinx.Audio.Renderer.Server.Voice voiceUpdateState.Offset = 0; voiceUpdateState.PlayedSampleCount = 0; - voiceUpdateState.Pitch.AsSpan().Fill(0); + voiceUpdateState.Pitch.AsSpan().Clear(); voiceUpdateState.Fraction = 0; - voiceUpdateState.LoopContext = new Dsp.State.AdpcmLoopContext(); + voiceUpdateState.LoopContext = new AdpcmLoopContext(); } - PlayState = Types.PlayState.Stopped; - WasPlaying = PreviousPlayState == Types.PlayState.Started; + PlayState = PlayState.Stopped; + WasPlaying = PreviousPlayState == PlayState.Started; return WasPlaying; - case Types.PlayState.Stopped: - case Types.PlayState.Paused: + case PlayState.Stopped: + case PlayState.Paused: foreach (ref WaveBuffer wavebuffer in WaveBuffers.AsSpan()) { wavebuffer.BufferAddressInfo.GetReference(true); @@ -664,7 +666,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice } } - WasPlaying = PreviousPlayState == Types.PlayState.Started; + WasPlaying = PreviousPlayState == PlayState.Started; return WasPlaying; default: @@ -696,4 +698,4 @@ namespace Ryujinx.Audio.Renderer.Server.Voice return UpdateParametersForCommandGeneration(voiceUpdateStates); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs index 4bf7dd280..a9946ba44 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs @@ -71,10 +71,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// <returns>A new <see cref="Common.WaveBuffer"/> for use by the <see cref="Dsp.AudioProcessor"/>.</returns> public Common.WaveBuffer ToCommon(int version) { - Common.WaveBuffer waveBuffer = new Common.WaveBuffer(); - - waveBuffer.Buffer = BufferAddressInfo.GetReference(true); - waveBuffer.BufferSize = (uint)BufferAddressInfo.Size; + Common.WaveBuffer waveBuffer = new() + { + Buffer = BufferAddressInfo.GetReference(true), + BufferSize = (uint)BufferAddressInfo.Size, + }; if (ContextAddressInfo.CpuAddress != 0) { @@ -101,4 +102,4 @@ namespace Ryujinx.Audio.Renderer.Server.Voice return waveBuffer; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs b/src/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs index 973f0672a..8ebf20340 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs @@ -54,4 +54,4 @@ namespace Ryujinx.Audio.Renderer.Utils { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/BitArray.cs b/src/Ryujinx.Audio/Renderer/Utils/BitArray.cs index 8b1054771..8fd0baeae 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/BitArray.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/BitArray.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Utils /// <summary> /// The backing storage of the <see cref="BitArray"/>. /// </summary> - private Memory<byte> _storage; + private readonly Memory<byte> _storage; /// <summary> /// Create a new <see cref="BitArray"/> from <see cref="Memory{T}"/>. @@ -97,7 +97,7 @@ namespace Ryujinx.Audio.Renderer.Utils [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { - _storage.Span.Fill(0); + _storage.Span.Clear(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs b/src/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs index d49313ea1..bc2313ccf 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs @@ -2,7 +2,6 @@ using Ryujinx.Audio.Integration; using System; using System.IO; using System.Runtime.InteropServices; -using System.Text; namespace Ryujinx.Audio.Renderer.Utils { @@ -12,8 +11,8 @@ namespace Ryujinx.Audio.Renderer.Utils public class FileHardwareDevice : IHardwareDevice { private FileStream _stream; - private uint _channelCount; - private uint _sampleRate; + private readonly uint _channelCount; + private readonly uint _sampleRate; private const int HeaderSize = 44; @@ -82,6 +81,7 @@ namespace Ryujinx.Audio.Renderer.Utils public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -96,4 +96,4 @@ namespace Ryujinx.Audio.Renderer.Utils } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/Mailbox.cs b/src/Ryujinx.Audio/Renderer/Utils/Mailbox.cs index 35c71ae36..26907af91 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/Mailbox.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/Mailbox.cs @@ -9,8 +9,8 @@ namespace Ryujinx.Audio.Renderer.Utils /// <typeparam name="T">The target unmanaged type used</typeparam> public class Mailbox<T> : IDisposable where T : unmanaged { - private BlockingCollection<T> _messageQueue; - private BlockingCollection<T> _responseQueue; + private readonly BlockingCollection<T> _messageQueue; + private readonly BlockingCollection<T> _responseQueue; public Mailbox() { @@ -40,6 +40,7 @@ namespace Ryujinx.Audio.Renderer.Utils public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -52,4 +53,4 @@ namespace Ryujinx.Audio.Renderer.Utils } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs index 5b513aff3..7a6edab19 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs @@ -68,4 +68,4 @@ namespace Ryujinx.Audio.Renderer.Utils.Math return m; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs index 415a81fdf..ff0123026 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs @@ -94,4 +94,4 @@ namespace Ryujinx.Audio.Renderer.Utils.Math M66 = m66; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs index 209a81c46..7b4b7ad1d 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Audio.Renderer.Dsp X = value2.M11 * value1.X + value2.M12 * value1.Y + value2.M13 * value1.Z + value2.M14 * value1.W, Y = value2.M21 * value1.X + value2.M22 * value1.Y + value2.M23 * value1.Z + value2.M24 * value1.W, Z = value2.M31 * value1.X + value2.M32 * value1.Y + value2.M33 * value1.Z + value2.M34 * value1.W, - W = value2.M41 * value1.X + value2.M42 * value1.Y + value2.M43 * value1.Z + value2.M44 * value1.W + W = value2.M41 * value1.X + value2.M42 * value1.Y + value2.M43 * value1.Z + value2.M44 * value1.W, }; } @@ -42,4 +42,4 @@ namespace Ryujinx.Audio.Renderer.Dsp }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs b/src/Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs index 81bcb6982..303c5e9d0 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs @@ -53,4 +53,4 @@ namespace Ryujinx.Audio.Renderer.Utils.Math return left * new Vector6(right); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs b/src/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs index 103fb6a04..4771ae4dd 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs @@ -22,12 +22,12 @@ namespace Ryujinx.Audio.Renderer.Utils if (size > backingMemory.Length) { - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null); } - MemoryMarshal.Write<T>(backingMemory.Span.Slice(0, size), ref data); + MemoryMarshal.Write(backingMemory.Span[..size], ref data); - backingMemory = backingMemory.Slice(size); + backingMemory = backingMemory[size..]; } /// <summary> @@ -42,12 +42,12 @@ namespace Ryujinx.Audio.Renderer.Utils if (size > backingMemory.Length) { - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null); } - MemoryMarshal.Write<T>(backingMemory.Slice(0, size), ref data); + MemoryMarshal.Write(backingMemory[..size], ref data); - backingMemory = backingMemory.Slice(size); + backingMemory = backingMemory[size..]; } /// <summary> @@ -62,12 +62,12 @@ namespace Ryujinx.Audio.Renderer.Utils if (size > backingMemory.Length) { - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null); } - Span<T> result = MemoryMarshal.Cast<byte, T>(backingMemory.Span.Slice(0, size)); + Span<T> result = MemoryMarshal.Cast<byte, T>(backingMemory.Span[..size]); - backingMemory = backingMemory.Slice(size); + backingMemory = backingMemory[size..]; return result; } @@ -84,12 +84,12 @@ namespace Ryujinx.Audio.Renderer.Utils if (size > backingMemory.Length) { - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null); } - Span<T> result = MemoryMarshal.Cast<byte, T>(backingMemory.Slice(0, size)); + Span<T> result = MemoryMarshal.Cast<byte, T>(backingMemory[..size]); - backingMemory = backingMemory.Slice(size); + backingMemory = backingMemory[size..]; return result; } @@ -106,12 +106,12 @@ namespace Ryujinx.Audio.Renderer.Utils if (size > backingMemory.Length) { - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null); } - T result = MemoryMarshal.Read<T>(backingMemory.Span.Slice(0, size)); + T result = MemoryMarshal.Read<T>(backingMemory.Span[..size]); - backingMemory = backingMemory.Slice(size); + backingMemory = backingMemory[size..]; return result; } @@ -128,12 +128,12 @@ namespace Ryujinx.Audio.Renderer.Utils if (size > backingMemory.Length) { - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null); } - T result = MemoryMarshal.Read<T>(backingMemory.Slice(0, size)); + T result = MemoryMarshal.Read<T>(backingMemory[..size]); - backingMemory = backingMemory.Slice(size); + backingMemory = backingMemory[size..]; return result; } @@ -168,4 +168,4 @@ namespace Ryujinx.Audio.Renderer.Utils return ref GetMemory(memory, id, count).Span[0]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs b/src/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs index 2c48da6aa..b6bafbe0f 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Audio.Renderer.Utils } } - public override Span<T> GetSpan() => new Span<T>(_pointer, _length); + public override Span<T> GetSpan() => new(_pointer, _length); public override MemoryHandle Pin(int elementIndex = 0) { @@ -40,4 +40,4 @@ namespace Ryujinx.Audio.Renderer.Utils return new SpanMemoryManager<T>(MemoryMarshal.Cast<TFrom, T>(memory.Span)).Memory; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs b/src/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs index 183960789..32d50e12f 100644 --- a/src/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs +++ b/src/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs @@ -5,8 +5,8 @@ namespace Ryujinx.Audio.Renderer.Utils { public class SplitterHardwareDevice : IHardwareDevice { - private IHardwareDevice _baseDevice; - private IHardwareDevice _secondaryDevice; + private readonly IHardwareDevice _baseDevice; + private readonly IHardwareDevice _secondaryDevice; public SplitterHardwareDevice(IHardwareDevice baseDevice, IHardwareDevice secondaryDevice) { @@ -43,6 +43,7 @@ namespace Ryujinx.Audio.Renderer.Utils public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -55,4 +56,4 @@ namespace Ryujinx.Audio.Renderer.Utils } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio/ResultCode.cs b/src/Ryujinx.Audio/ResultCode.cs index 1d05ac65e..eab27c16d 100644 --- a/src/Ryujinx.Audio/ResultCode.cs +++ b/src/Ryujinx.Audio/ResultCode.cs @@ -19,4 +19,4 @@ namespace Ryujinx.Audio UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId, InvalidExecutionContextOperation = (514 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs index 2d342206b..36b0ed282 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs @@ -1,7 +1,6 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Input; using Ryujinx.HLE.HOS.Services.Audio.AudioIn; - using AudioInManagerImpl = Ryujinx.Audio.Input.AudioInputManager; namespace Ryujinx.HLE.HOS.Services.Audio diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs index 7b2891963..e95de0575 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs @@ -1,7 +1,6 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Output; using Ryujinx.HLE.HOS.Services.Audio.AudioOut; - using AudioOutManagerImpl = Ryujinx.Audio.Output.AudioOutputManager; namespace Ryujinx.HLE.HOS.Services.Audio From 2457cfc9118a6ebb6008945c919edfd8b46af5e7 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 2 Jul 2023 02:26:32 +0200 Subject: [PATCH 694/737] Fix naming issue in ControllerWindow (#5424) --- src/Ryujinx/Ui/Windows/ControllerWindow.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx/Ui/Windows/ControllerWindow.cs b/src/Ryujinx/Ui/Windows/ControllerWindow.cs index 19cc6f204..ebf22ab60 100644 --- a/src/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/src/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -87,7 +87,7 @@ namespace Ryujinx.Ui.Windows [GUI] ToggleButton _rStick; [GUI] CheckButton _invertRStickX; [GUI] CheckButton _invertRStickY; - [GUI] CheckButton _rotateR90Cw; + [GUI] CheckButton _rotateR90CW; [GUI] ToggleButton _rStickUp; [GUI] ToggleButton _rStickDown; [GUI] ToggleButton _rStickLeft; @@ -503,7 +503,7 @@ namespace Ryujinx.Ui.Windows _rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString(); _invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX; _invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY; - _rotateR90Cw.Active = controllerConfig.RightJoyconStick.Rotate90CW; + _rotateR90CW.Active = controllerConfig.RightJoyconStick.Rotate90CW; _rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString(); _a.Label = controllerConfig.RightJoycon.ButtonA.ToString(); _b.Label = controllerConfig.RightJoycon.ButtonB.ToString(); @@ -743,7 +743,7 @@ namespace Ryujinx.Ui.Windows Joystick = rStick, InvertStickY = _invertRStickY.Active, StickButton = rStickButton, - Rotate90CW = _rotateR90Cw.Active, + Rotate90CW = _rotateR90CW.Active, }, Motion = motionConfig, Rumble = new RumbleConfigController From 3b46bb73f781a011705ecbc8a1d3207dfb145829 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 2 Jul 2023 02:47:54 +0200 Subject: [PATCH 695/737] [Ryujinx.Graphics.Gpu] Address dotnet-format issues (#5367) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA1069 warnings * Address or silence dotnet format CA2211 warnings * Address remaining dotnet format analyzer warnings * Address review comments * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Another rebase, another dotnet format run * Run dotnet format style after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Remove a few unused parameters * Replace MmeShadowScratch with Array256<uint> * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Fix and silence a few dotnet-format warnings again * Run dotnet format after rebase * Address IDE0251 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First pass of dotnet format * Add unsafe dotnet format changes * Fix typos * Add trailing commas * Disable formatting for FormatTable * Address review feedback --- src/Ryujinx.Graphics.Gpu/ClassId.cs | 4 +- src/Ryujinx.Graphics.Gpu/Constants.cs | 2 +- .../Engine/Compute/ComputeClass.cs | 10 +- .../Engine/Compute/ComputeClassState.cs | 204 ++++++------- .../Engine/Compute/ComputeQmd.cs | 270 +++++++++--------- .../Engine/ConditionalRenderEnabled.cs | 2 +- .../Engine/Dma/DmaClass.cs | 70 +++-- .../Engine/Dma/DmaClassState.cs | 84 +++--- .../Engine/Dma/DmaTexture.cs | 4 +- .../Engine/GPFifo/CompressedMethod.cs | 24 +- .../Engine/GPFifo/GPEntry.cs | 22 +- .../Engine/GPFifo/GPFifoClass.cs | 3 +- .../Engine/GPFifo/GPFifoClassState.cs | 92 +++--- .../Engine/GPFifo/GPFifoDevice.cs | 12 +- .../Engine/GPFifo/GPFifoProcessor.cs | 2 +- .../InlineToMemory/InlineToMemoryClass.cs | 8 +- .../InlineToMemoryClassState.cs | 48 ++-- .../Engine/MME/AluOperation.cs | 2 +- .../Engine/MME/AluRegOperation.cs | 2 +- .../Engine/MME/AssignmentOperation.cs | 2 +- src/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs | 6 +- .../Engine/MME/MacroHLE.cs | 3 +- .../Engine/MME/MacroHLEFunctionName.cs | 2 +- .../Engine/MME/MacroHLETable.cs | 14 +- .../Engine/MME/MacroInterpreter.cs | 21 +- .../Engine/MME/MacroJit.cs | 4 +- .../Engine/MME/MacroJitCompiler.cs | 2 +- .../Engine/MME/MacroJitContext.cs | 2 +- .../Engine/MmeShadowScratch.cs | 2 +- .../Engine/SetMmeShadowRamControlMode.cs | 2 +- .../Engine/ShaderTexture.cs | 4 +- .../Threed/Blender/AdvancedBlendFunctions.cs | 6 +- .../Threed/Blender/AdvancedBlendManager.cs | 6 +- .../Blender/AdvancedBlendPreGenTable.cs | 4 +- .../Threed/Blender/AdvancedBlendUcode.cs | 10 +- .../Engine/Threed/Blender/UcodeAssembler.cs | 52 ++-- .../Engine/Threed/ConstantBufferUpdater.cs | 2 +- .../Engine/Threed/DrawManager.cs | 10 +- .../Engine/Threed/DrawState.cs | 2 +- .../Engine/Threed/IbStreamer.cs | 22 +- .../Engine/Threed/IndirectDrawType.cs | 7 +- .../Engine/Threed/RenderTargetUpdateFlags.cs | 2 +- .../Engine/Threed/SemaphoreUpdater.cs | 19 +- .../Threed/SpecializationStateUpdater.cs | 2 +- .../Engine/Threed/StateUpdater.cs | 30 +- .../Engine/Threed/ThreedClass.cs | 2 +- .../Engine/Threed/ThreedClassState.cs | 170 +++++------ .../Engine/Twod/TwodClass.cs | 12 +- .../Engine/Twod/TwodClassState.cs | 206 ++++++------- .../Engine/Twod/TwodTexture.cs | 2 +- .../Engine/Types/Boolean32.cs | 6 +- .../Engine/Types/ColorFormat.cs | 104 +++---- .../Engine/Types/GpuVa.cs | 4 +- .../Engine/Types/MemoryLayout.cs | 12 +- .../Engine/Types/PrimitiveType.cs | 14 +- .../Engine/Types/SamplerIndex.cs | 6 +- .../Engine/Types/SbDescriptor.cs | 4 +- .../Engine/Types/ZetaFormat.cs | 14 +- src/Ryujinx.Graphics.Gpu/GpuChannel.cs | 1 + src/Ryujinx.Graphics.Gpu/GpuContext.cs | 6 +- src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs | 4 +- .../Image/AutoDeleteCache.cs | 4 +- src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs | 20 +- src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs | 15 +- src/Ryujinx.Graphics.Gpu/Image/Pool.cs | 4 +- src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs | 2 +- .../Image/ReductionFilter.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/Sampler.cs | 10 +- .../Image/SamplerDescriptor.cs | 52 ++-- .../Image/SamplerMinFilter.cs | 2 +- .../Image/SamplerMipFilter.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs | 2 +- .../Image/SamplerPoolCache.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 36 +-- .../Image/TextureBindingInfo.cs | 12 +- .../Image/TextureBindingsManager.cs | 12 +- .../Image/TextureCache.cs | 18 +- .../Image/TextureCompatibility.cs | 105 ++----- .../Image/TextureComponent.cs | 31 +- .../Image/TextureDescriptor.cs | 61 ++-- .../Image/TextureDescriptorType.cs | 4 +- .../Image/TextureGroup.cs | 16 +- .../Image/TextureGroupHandle.cs | 10 +- src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs | 78 ++--- .../Image/TextureManager.cs | 14 +- .../Image/TextureMatchQuality.cs | 2 +- .../Image/TextureMsaaMode.cs | 10 +- src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 16 +- .../Image/TexturePoolCache.cs | 2 +- .../Image/TextureScaleMode.cs | 2 +- .../Image/TextureSearchFlags.cs | 12 +- .../Image/TextureTarget.cs | 53 ++-- .../Image/TextureViewCompatibility.cs | 2 +- src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 19 +- .../Memory/BufferBounds.cs | 2 +- .../Memory/BufferCache.cs | 12 +- .../Memory/BufferManager.cs | 6 +- .../Memory/BufferModifiedRangeList.cs | 9 +- .../Memory/CounterCache.cs | 7 +- .../Memory/IndexBuffer.cs | 2 +- .../Memory/MemoryManager.cs | 16 +- .../Memory/PhysicalMemory.cs | 4 +- src/Ryujinx.Graphics.Gpu/Memory/PteKind.cs | 4 +- .../Memory/ResourceKind.cs | 2 +- .../Memory/VertexBuffer.cs | 6 +- .../Shader/CachedShaderStage.cs | 2 +- .../Shader/ComputeShaderCacheHashTable.cs | 5 +- .../DiskCache/BackgroundDiskCacheWriter.cs | 2 +- .../Shader/DiskCache/BinarySerializer.cs | 20 +- .../Shader/DiskCache/CompressionAlgorithm.cs | 4 +- .../Shader/DiskCache/DiskCacheCommon.cs | 2 +- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 8 +- .../Shader/DiskCache/DiskCacheGuestStorage.cs | 22 +- .../Shader/DiskCache/DiskCacheHostStorage.cs | 84 +++--- .../DiskCache/DiskCacheLoadException.cs | 2 +- .../Shader/DiskCache/DiskCacheLoadResult.cs | 6 +- .../Shader/DiskCache/GuestCodeAndCbData.cs | 2 +- .../DiskCache/ParallelDiskCacheLoader.cs | 39 ++- .../DiskCache/ShaderBinarySerializer.cs | 9 +- .../Shader/GpuAccessor.cs | 2 +- .../Shader/GpuAccessorBase.cs | 9 +- .../Shader/GpuAccessorState.cs | 2 +- .../Shader/GpuChannelComputeState.cs | 2 +- .../Shader/GpuChannelGraphicsState.cs | 2 +- .../Shader/GpuChannelPoolState.cs | 9 +- .../Shader/HashTable/HashState.cs | 6 +- .../Shader/HashTable/PartitionHashTable.cs | 20 +- .../Shader/HashTable/PartitionedHashTable.cs | 6 +- .../Shader/HashTable/SmartDataAccessor.cs | 4 +- .../Shader/ResourceCounts.cs | 2 +- .../Shader/ShaderAddresses.cs | 20 +- .../Shader/ShaderCache.cs | 34 ++- .../Shader/ShaderCacheHashTable.cs | 20 +- .../Shader/ShaderCacheState.cs | 4 +- .../Shader/ShaderCodeAccessor.cs | 2 +- .../Shader/ShaderDumpPaths.cs | 2 +- .../Shader/ShaderDumper.cs | 10 +- .../Shader/ShaderInfoBuilder.cs | 12 +- .../Shader/ShaderSpecializationList.cs | 4 +- .../Shader/ShaderSpecializationState.cs | 16 +- .../Shader/TransformFeedbackDescriptor.cs | 2 +- .../Synchronization/HostSyncFlags.cs | 2 +- .../Synchronization/SynchronizationManager.cs | 36 ++- .../Synchronization/Syncpoint.cs | 11 +- src/Ryujinx.Graphics.Gpu/Window.cs | 54 ++-- 145 files changed, 1445 insertions(+), 1427 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/ClassId.cs b/src/Ryujinx.Graphics.Gpu/ClassId.cs index 4e475a24d..7c1d1e46b 100644 --- a/src/Ryujinx.Graphics.Gpu/ClassId.cs +++ b/src/Ryujinx.Graphics.Gpu/ClassId.cs @@ -10,6 +10,6 @@ namespace Ryujinx.Graphics.Gpu Compute = 0xb1c0, InlineToMemory = 0xa140, Dma = 0xb0b5, - GPFifo = 0xb06f + GPFifo = 0xb06f, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Constants.cs b/src/Ryujinx.Graphics.Gpu/Constants.cs index ff90e61ba..c553d988e 100644 --- a/src/Ryujinx.Graphics.Gpu/Constants.cs +++ b/src/Ryujinx.Graphics.Gpu/Constants.cs @@ -90,4 +90,4 @@ namespace Ryujinx.Graphics.Gpu /// </summary> public const ulong MaxUnknownStorageSize = 0x100000; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index d8103ac71..67743de37 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -1,9 +1,7 @@ using Ryujinx.Graphics.Device; -using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Engine.Types; -using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Shader; using System; @@ -39,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { { nameof(ComputeClassState.LaunchDma), new RwCallback(LaunchDma, null) }, { nameof(ComputeClassState.LoadInlineData), new RwCallback(LoadInlineData, null) }, - { nameof(ComputeClassState.SendSignalingPcasB), new RwCallback(SendSignalingPcasB, null) } + { nameof(ComputeClassState.SendSignalingPcasB), new RwCallback(SendSignalingPcasB, null) }, }); _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false); @@ -128,12 +126,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute ulong samplerPoolGpuVa = ((ulong)_state.State.SetTexSamplerPoolAOffsetUpper << 32) | _state.State.SetTexSamplerPoolB; ulong texturePoolGpuVa = ((ulong)_state.State.SetTexHeaderPoolAOffsetUpper << 32) | _state.State.SetTexHeaderPoolB; - GpuChannelPoolState poolState = new GpuChannelPoolState( + GpuChannelPoolState poolState = new( texturePoolGpuVa, _state.State.SetTexHeaderPoolCMaximumIndex, _state.State.SetBindlessTextureConstantBufferSlotSelect); - GpuChannelComputeState computeState = new GpuChannelComputeState( + GpuChannelComputeState computeState = new( qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2, @@ -189,8 +187,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); - - info = cs.Shaders[0].Info; } _channel.BufferManager.SetComputeBufferBindings(cs.Bindings); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs index 73dd31b23..0b192ef6b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClassState.cs @@ -98,24 +98,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute /// </summary> unsafe struct ComputeClassState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint SetObject; - public int SetObjectClassId => (int)(SetObject & 0xFFFF); - public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); + public readonly int SetObjectClassId => (int)(SetObject & 0xFFFF); + public readonly int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); + public readonly int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); public uint SetNotifyB; public uint Notify; - public NotifyType NotifyType => (NotifyType)(Notify); + public readonly NotifyType NotifyType => (NotifyType)(Notify); public uint WaitForIdle; public fixed uint Reserved114[7]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); + public readonly int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); + public readonly int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public uint PmTriggerWfi; @@ -126,34 +126,34 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint LineLengthIn; public uint LineCount; public uint OffsetOutUpper; - public int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); + public readonly int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); public uint OffsetOut; public uint PitchOut; public uint SetDstBlockSize; - public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); - public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); - public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); + public readonly SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); + public readonly SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); + public readonly SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); public uint SetDstWidth; public uint SetDstHeight; public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOriginBytesX; - public int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); + public readonly int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); public uint SetDstOriginSamplesY; - public int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); + public readonly int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); public uint LaunchDma; - public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); - public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); - public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); - public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); - public bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0; - public LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7); - public LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3); - public bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0; + public readonly LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); + public readonly LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); + public readonly LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); + public readonly LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); + public readonly bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0; + public readonly LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7); + public readonly LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3); + public readonly bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0; public uint LoadInlineData; public fixed uint Reserved1B8[9]; public uint SetI2mSemaphoreA; - public int SetI2mSemaphoreAOffsetUpper => (int)(SetI2mSemaphoreA & 0xFF); + public readonly int SetI2mSemaphoreAOffsetUpper => (int)(SetI2mSemaphoreA & 0xFF); public uint SetI2mSemaphoreB; public uint SetI2mSemaphoreC; public fixed uint Reserved1E8[2]; @@ -162,20 +162,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetI2mSpareNoop02; public uint SetI2mSpareNoop03; public uint SetValidSpanOverflowAreaA; - public int SetValidSpanOverflowAreaAAddressUpper => (int)(SetValidSpanOverflowAreaA & 0xFF); + public readonly int SetValidSpanOverflowAreaAAddressUpper => (int)(SetValidSpanOverflowAreaA & 0xFF); public uint SetValidSpanOverflowAreaB; public uint SetValidSpanOverflowAreaC; public uint SetCoalesceWaitingPeriodUnit; public uint PerfmonTransfer; public uint SetShaderSharedMemoryWindow; public uint SetSelectMaxwellTextureHeaders; - public bool SetSelectMaxwellTextureHeadersV => (SetSelectMaxwellTextureHeaders & 0x1) != 0; + public readonly bool SetSelectMaxwellTextureHeadersV => (SetSelectMaxwellTextureHeaders & 0x1) != 0; public uint InvalidateShaderCaches; - public bool InvalidateShaderCachesInstruction => (InvalidateShaderCaches & 0x1) != 0; - public bool InvalidateShaderCachesData => (InvalidateShaderCaches & 0x10) != 0; - public bool InvalidateShaderCachesConstant => (InvalidateShaderCaches & 0x1000) != 0; - public bool InvalidateShaderCachesLocks => (InvalidateShaderCaches & 0x2) != 0; - public bool InvalidateShaderCachesFlushData => (InvalidateShaderCaches & 0x4) != 0; + public readonly bool InvalidateShaderCachesInstruction => (InvalidateShaderCaches & 0x1) != 0; + public readonly bool InvalidateShaderCachesData => (InvalidateShaderCaches & 0x10) != 0; + public readonly bool InvalidateShaderCachesConstant => (InvalidateShaderCaches & 0x1000) != 0; + public readonly bool InvalidateShaderCachesLocks => (InvalidateShaderCaches & 0x2) != 0; + public readonly bool InvalidateShaderCachesFlushData => (InvalidateShaderCaches & 0x4) != 0; public uint SetReservedSwMethod00; public uint SetReservedSwMethod01; public uint SetReservedSwMethod02; @@ -185,13 +185,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetReservedSwMethod06; public uint SetReservedSwMethod07; public uint SetCwdControl; - public SetCwdControlSmSelection SetCwdControlSmSelection => (SetCwdControlSmSelection)(SetCwdControl & 0x1); + public readonly SetCwdControlSmSelection SetCwdControlSmSelection => (SetCwdControlSmSelection)(SetCwdControl & 0x1); public uint InvalidateTextureHeaderCacheNoWfi; - public InvalidateCacheLines InvalidateTextureHeaderCacheNoWfiLines => (InvalidateCacheLines)(InvalidateTextureHeaderCacheNoWfi & 0x1); - public int InvalidateTextureHeaderCacheNoWfiTag => (int)((InvalidateTextureHeaderCacheNoWfi >> 4) & 0x3FFFFF); + public readonly InvalidateCacheLines InvalidateTextureHeaderCacheNoWfiLines => (InvalidateCacheLines)(InvalidateTextureHeaderCacheNoWfi & 0x1); + public readonly int InvalidateTextureHeaderCacheNoWfiTag => (int)((InvalidateTextureHeaderCacheNoWfi >> 4) & 0x3FFFFF); public uint SetCwdRefCounter; - public int SetCwdRefCounterSelect => (int)(SetCwdRefCounter & 0x3F); - public int SetCwdRefCounterValue => (int)((SetCwdRefCounter >> 8) & 0xFFFF); + public readonly int SetCwdRefCounterSelect => (int)(SetCwdRefCounter & 0x3F); + public readonly int SetCwdRefCounterValue => (int)((SetCwdRefCounter >> 8) & 0xFFFF); public uint SetReservedSwMethod08; public uint SetReservedSwMethod09; public uint SetReservedSwMethod10; @@ -201,59 +201,59 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetReservedSwMethod14; public uint SetReservedSwMethod15; public uint SetGwcScgType; - public SetGwcScgTypeScgType SetGwcScgTypeScgType => (SetGwcScgTypeScgType)(SetGwcScgType & 0x1); + public readonly SetGwcScgTypeScgType SetGwcScgTypeScgType => (SetGwcScgTypeScgType)(SetGwcScgType & 0x1); public uint SetScgControl; - public int SetScgControlCompute1MaxSmCount => (int)(SetScgControl & 0x1FF); + public readonly int SetScgControlCompute1MaxSmCount => (int)(SetScgControl & 0x1FF); public uint InvalidateConstantBufferCacheA; - public int InvalidateConstantBufferCacheAAddressUpper => (int)(InvalidateConstantBufferCacheA & 0xFF); + public readonly int InvalidateConstantBufferCacheAAddressUpper => (int)(InvalidateConstantBufferCacheA & 0xFF); public uint InvalidateConstantBufferCacheB; public uint InvalidateConstantBufferCacheC; - public int InvalidateConstantBufferCacheCByteCount => (int)(InvalidateConstantBufferCacheC & 0x1FFFF); - public bool InvalidateConstantBufferCacheCThruL2 => (InvalidateConstantBufferCacheC & 0x80000000) != 0; + public readonly int InvalidateConstantBufferCacheCByteCount => (int)(InvalidateConstantBufferCacheC & 0x1FFFF); + public readonly bool InvalidateConstantBufferCacheCThruL2 => (InvalidateConstantBufferCacheC & 0x80000000) != 0; public uint SetComputeClassVersion; - public int SetComputeClassVersionCurrent => (int)(SetComputeClassVersion & 0xFFFF); - public int SetComputeClassVersionOldestSupported => (int)((SetComputeClassVersion >> 16) & 0xFFFF); + public readonly int SetComputeClassVersionCurrent => (int)(SetComputeClassVersion & 0xFFFF); + public readonly int SetComputeClassVersionOldestSupported => (int)((SetComputeClassVersion >> 16) & 0xFFFF); public uint CheckComputeClassVersion; - public int CheckComputeClassVersionCurrent => (int)(CheckComputeClassVersion & 0xFFFF); - public int CheckComputeClassVersionOldestSupported => (int)((CheckComputeClassVersion >> 16) & 0xFFFF); + public readonly int CheckComputeClassVersionCurrent => (int)(CheckComputeClassVersion & 0xFFFF); + public readonly int CheckComputeClassVersionOldestSupported => (int)((CheckComputeClassVersion >> 16) & 0xFFFF); public uint SetQmdVersion; - public int SetQmdVersionCurrent => (int)(SetQmdVersion & 0xFFFF); - public int SetQmdVersionOldestSupported => (int)((SetQmdVersion >> 16) & 0xFFFF); + public readonly int SetQmdVersionCurrent => (int)(SetQmdVersion & 0xFFFF); + public readonly int SetQmdVersionOldestSupported => (int)((SetQmdVersion >> 16) & 0xFFFF); public uint SetWfiConfig; - public bool SetWfiConfigEnableScgTypeWfi => (SetWfiConfig & 0x1) != 0; + public readonly bool SetWfiConfigEnableScgTypeWfi => (SetWfiConfig & 0x1) != 0; public uint CheckQmdVersion; - public int CheckQmdVersionCurrent => (int)(CheckQmdVersion & 0xFFFF); - public int CheckQmdVersionOldestSupported => (int)((CheckQmdVersion >> 16) & 0xFFFF); + public readonly int CheckQmdVersionCurrent => (int)(CheckQmdVersion & 0xFFFF); + public readonly int CheckQmdVersionOldestSupported => (int)((CheckQmdVersion >> 16) & 0xFFFF); public uint WaitForIdleScgType; public uint InvalidateSkedCaches; - public bool InvalidateSkedCachesV => (InvalidateSkedCaches & 0x1) != 0; + public readonly bool InvalidateSkedCachesV => (InvalidateSkedCaches & 0x1) != 0; public uint SetScgRenderEnableControl; - public bool SetScgRenderEnableControlCompute1UsesRenderEnable => (SetScgRenderEnableControl & 0x1) != 0; + public readonly bool SetScgRenderEnableControlCompute1UsesRenderEnable => (SetScgRenderEnableControl & 0x1) != 0; public fixed uint Reserved2A0[4]; public uint SetCwdSlotCount; - public int SetCwdSlotCountV => (int)(SetCwdSlotCount & 0xFF); + public readonly int SetCwdSlotCountV => (int)(SetCwdSlotCount & 0xFF); public uint SendPcasA; public uint SendPcasB; - public int SendPcasBFrom => (int)(SendPcasB & 0xFFFFFF); - public int SendPcasBDelta => (int)((SendPcasB >> 24) & 0xFF); + public readonly int SendPcasBFrom => (int)(SendPcasB & 0xFFFFFF); + public readonly int SendPcasBDelta => (int)((SendPcasB >> 24) & 0xFF); public uint SendSignalingPcasB; - public bool SendSignalingPcasBInvalidate => (SendSignalingPcasB & 0x1) != 0; - public bool SendSignalingPcasBSchedule => (SendSignalingPcasB & 0x2) != 0; + public readonly bool SendSignalingPcasBInvalidate => (SendSignalingPcasB & 0x1) != 0; + public readonly bool SendSignalingPcasBSchedule => (SendSignalingPcasB & 0x2) != 0; public fixed uint Reserved2C0[9]; public uint SetShaderLocalMemoryNonThrottledA; - public int SetShaderLocalMemoryNonThrottledASizeUpper => (int)(SetShaderLocalMemoryNonThrottledA & 0xFF); + public readonly int SetShaderLocalMemoryNonThrottledASizeUpper => (int)(SetShaderLocalMemoryNonThrottledA & 0xFF); public uint SetShaderLocalMemoryNonThrottledB; public uint SetShaderLocalMemoryNonThrottledC; - public int SetShaderLocalMemoryNonThrottledCMaxSmCount => (int)(SetShaderLocalMemoryNonThrottledC & 0x1FF); + public readonly int SetShaderLocalMemoryNonThrottledCMaxSmCount => (int)(SetShaderLocalMemoryNonThrottledC & 0x1FF); public uint SetShaderLocalMemoryThrottledA; - public int SetShaderLocalMemoryThrottledASizeUpper => (int)(SetShaderLocalMemoryThrottledA & 0xFF); + public readonly int SetShaderLocalMemoryThrottledASizeUpper => (int)(SetShaderLocalMemoryThrottledA & 0xFF); public uint SetShaderLocalMemoryThrottledB; public uint SetShaderLocalMemoryThrottledC; - public int SetShaderLocalMemoryThrottledCMaxSmCount => (int)(SetShaderLocalMemoryThrottledC & 0x1FF); + public readonly int SetShaderLocalMemoryThrottledCMaxSmCount => (int)(SetShaderLocalMemoryThrottledC & 0x1FF); public fixed uint Reserved2FC[5]; public uint SetSpaVersion; - public int SetSpaVersionMinor => (int)(SetSpaVersion & 0xFF); - public int SetSpaVersionMajor => (int)((SetSpaVersion >> 8) & 0xFF); + public readonly int SetSpaVersionMinor => (int)(SetSpaVersion & 0xFF); + public readonly int SetSpaVersionMajor => (int)((SetSpaVersion >> 8) & 0xFF); public fixed uint Reserved314[123]; public uint SetFalcon00; public uint SetFalcon01; @@ -291,14 +291,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetShaderLocalMemoryWindow; public fixed uint Reserved780[4]; public uint SetShaderLocalMemoryA; - public int SetShaderLocalMemoryAAddressUpper => (int)(SetShaderLocalMemoryA & 0xFF); + public readonly int SetShaderLocalMemoryAAddressUpper => (int)(SetShaderLocalMemoryA & 0xFF); public uint SetShaderLocalMemoryB; public fixed uint Reserved798[383]; public uint SetShaderCacheControl; - public bool SetShaderCacheControlIcachePrefetchEnable => (SetShaderCacheControl & 0x1) != 0; + public readonly bool SetShaderCacheControlIcachePrefetchEnable => (SetShaderCacheControl & 0x1) != 0; public fixed uint ReservedD98[19]; public uint SetSmTimeoutInterval; - public int SetSmTimeoutIntervalCounterBit => (int)(SetSmTimeoutInterval & 0x3F); + public readonly int SetSmTimeoutIntervalCounterBit => (int)(SetSmTimeoutInterval & 0x3F); public fixed uint ReservedDE8[87]; public uint SetSpareNoop12; public uint SetSpareNoop13; @@ -319,62 +319,62 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetSpareNoop11; public fixed uint Reserved1070[103]; public uint InvalidateSamplerCacheAll; - public bool InvalidateSamplerCacheAllV => (InvalidateSamplerCacheAll & 0x1) != 0; + public readonly bool InvalidateSamplerCacheAllV => (InvalidateSamplerCacheAll & 0x1) != 0; public uint InvalidateTextureHeaderCacheAll; - public bool InvalidateTextureHeaderCacheAllV => (InvalidateTextureHeaderCacheAll & 0x1) != 0; + public readonly bool InvalidateTextureHeaderCacheAllV => (InvalidateTextureHeaderCacheAll & 0x1) != 0; public fixed uint Reserved1214[29]; public uint InvalidateTextureDataCacheNoWfi; - public InvalidateCacheLines InvalidateTextureDataCacheNoWfiLines => (InvalidateCacheLines)(InvalidateTextureDataCacheNoWfi & 0x1); - public int InvalidateTextureDataCacheNoWfiTag => (int)((InvalidateTextureDataCacheNoWfi >> 4) & 0x3FFFFF); + public readonly InvalidateCacheLines InvalidateTextureDataCacheNoWfiLines => (InvalidateCacheLines)(InvalidateTextureDataCacheNoWfi & 0x1); + public readonly int InvalidateTextureDataCacheNoWfiTag => (int)((InvalidateTextureDataCacheNoWfi >> 4) & 0x3FFFFF); public fixed uint Reserved128C[7]; public uint ActivatePerfSettingsForComputeContext; - public bool ActivatePerfSettingsForComputeContextAll => (ActivatePerfSettingsForComputeContext & 0x1) != 0; + public readonly bool ActivatePerfSettingsForComputeContextAll => (ActivatePerfSettingsForComputeContext & 0x1) != 0; public fixed uint Reserved12AC[33]; public uint InvalidateSamplerCache; - public InvalidateCacheLines InvalidateSamplerCacheLines => (InvalidateCacheLines)(InvalidateSamplerCache & 0x1); - public int InvalidateSamplerCacheTag => (int)((InvalidateSamplerCache >> 4) & 0x3FFFFF); + public readonly InvalidateCacheLines InvalidateSamplerCacheLines => (InvalidateCacheLines)(InvalidateSamplerCache & 0x1); + public readonly int InvalidateSamplerCacheTag => (int)((InvalidateSamplerCache >> 4) & 0x3FFFFF); public uint InvalidateTextureHeaderCache; - public InvalidateCacheLines InvalidateTextureHeaderCacheLines => (InvalidateCacheLines)(InvalidateTextureHeaderCache & 0x1); - public int InvalidateTextureHeaderCacheTag => (int)((InvalidateTextureHeaderCache >> 4) & 0x3FFFFF); + public readonly InvalidateCacheLines InvalidateTextureHeaderCacheLines => (InvalidateCacheLines)(InvalidateTextureHeaderCache & 0x1); + public readonly int InvalidateTextureHeaderCacheTag => (int)((InvalidateTextureHeaderCache >> 4) & 0x3FFFFF); public uint InvalidateTextureDataCache; - public InvalidateCacheLines InvalidateTextureDataCacheLines => (InvalidateCacheLines)(InvalidateTextureDataCache & 0x1); - public int InvalidateTextureDataCacheTag => (int)((InvalidateTextureDataCache >> 4) & 0x3FFFFF); + public readonly InvalidateCacheLines InvalidateTextureDataCacheLines => (InvalidateCacheLines)(InvalidateTextureDataCache & 0x1); + public readonly int InvalidateTextureDataCacheTag => (int)((InvalidateTextureDataCache >> 4) & 0x3FFFFF); public fixed uint Reserved133C[58]; public uint InvalidateSamplerCacheNoWfi; - public InvalidateCacheLines InvalidateSamplerCacheNoWfiLines => (InvalidateCacheLines)(InvalidateSamplerCacheNoWfi & 0x1); - public int InvalidateSamplerCacheNoWfiTag => (int)((InvalidateSamplerCacheNoWfi >> 4) & 0x3FFFFF); + public readonly InvalidateCacheLines InvalidateSamplerCacheNoWfiLines => (InvalidateCacheLines)(InvalidateSamplerCacheNoWfi & 0x1); + public readonly int InvalidateSamplerCacheNoWfiTag => (int)((InvalidateSamplerCacheNoWfi >> 4) & 0x3FFFFF); public fixed uint Reserved1428[64]; public uint SetShaderExceptions; - public bool SetShaderExceptionsEnable => (SetShaderExceptions & 0x1) != 0; + public readonly bool SetShaderExceptionsEnable => (SetShaderExceptions & 0x1) != 0; public fixed uint Reserved152C[9]; public uint SetRenderEnableA; - public int SetRenderEnableAOffsetUpper => (int)(SetRenderEnableA & 0xFF); + public readonly int SetRenderEnableAOffsetUpper => (int)(SetRenderEnableA & 0xFF); public uint SetRenderEnableB; public uint SetRenderEnableC; - public int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); + public readonly int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); public uint SetTexSamplerPoolA; - public int SetTexSamplerPoolAOffsetUpper => (int)(SetTexSamplerPoolA & 0xFF); + public readonly int SetTexSamplerPoolAOffsetUpper => (int)(SetTexSamplerPoolA & 0xFF); public uint SetTexSamplerPoolB; public uint SetTexSamplerPoolC; - public int SetTexSamplerPoolCMaximumIndex => (int)(SetTexSamplerPoolC & 0xFFFFF); + public readonly int SetTexSamplerPoolCMaximumIndex => (int)(SetTexSamplerPoolC & 0xFFFFF); public fixed uint Reserved1568[3]; public uint SetTexHeaderPoolA; - public int SetTexHeaderPoolAOffsetUpper => (int)(SetTexHeaderPoolA & 0xFF); + public readonly int SetTexHeaderPoolAOffsetUpper => (int)(SetTexHeaderPoolA & 0xFF); public uint SetTexHeaderPoolB; public uint SetTexHeaderPoolC; - public int SetTexHeaderPoolCMaximumIndex => (int)(SetTexHeaderPoolC & 0x3FFFFF); + public readonly int SetTexHeaderPoolCMaximumIndex => (int)(SetTexHeaderPoolC & 0x3FFFFF); public fixed uint Reserved1580[34]; public uint SetProgramRegionA; - public int SetProgramRegionAAddressUpper => (int)(SetProgramRegionA & 0xFF); + public readonly int SetProgramRegionAAddressUpper => (int)(SetProgramRegionA & 0xFF); public uint SetProgramRegionB; public fixed uint Reserved1610[34]; public uint InvalidateShaderCachesNoWfi; - public bool InvalidateShaderCachesNoWfiInstruction => (InvalidateShaderCachesNoWfi & 0x1) != 0; - public bool InvalidateShaderCachesNoWfiGlobalData => (InvalidateShaderCachesNoWfi & 0x10) != 0; - public bool InvalidateShaderCachesNoWfiConstant => (InvalidateShaderCachesNoWfi & 0x1000) != 0; + public readonly bool InvalidateShaderCachesNoWfiInstruction => (InvalidateShaderCachesNoWfi & 0x1) != 0; + public readonly bool InvalidateShaderCachesNoWfiGlobalData => (InvalidateShaderCachesNoWfi & 0x10) != 0; + public readonly bool InvalidateShaderCachesNoWfiConstant => (InvalidateShaderCachesNoWfi & 0x1000) != 0; public fixed uint Reserved169C[170]; public uint SetRenderEnableOverride; - public SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)(SetRenderEnableOverride & 0x3); + public readonly SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)(SetRenderEnableOverride & 0x3); public fixed uint Reserved1948[57]; public uint PipeNop; public uint SetSpare00; @@ -383,20 +383,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public uint SetSpare03; public fixed uint Reserved1A40[48]; public uint SetReportSemaphoreA; - public int SetReportSemaphoreAOffsetUpper => (int)(SetReportSemaphoreA & 0xFF); + public readonly int SetReportSemaphoreAOffsetUpper => (int)(SetReportSemaphoreA & 0xFF); public uint SetReportSemaphoreB; public uint SetReportSemaphoreC; public uint SetReportSemaphoreD; - public SetReportSemaphoreDOperation SetReportSemaphoreDOperation => (SetReportSemaphoreDOperation)(SetReportSemaphoreD & 0x3); - public bool SetReportSemaphoreDAwakenEnable => (SetReportSemaphoreD & 0x100000) != 0; - public SetReportSemaphoreDStructureSize SetReportSemaphoreDStructureSize => (SetReportSemaphoreDStructureSize)((SetReportSemaphoreD >> 28) & 0x1); - public bool SetReportSemaphoreDFlushDisable => (SetReportSemaphoreD & 0x4) != 0; - public bool SetReportSemaphoreDReductionEnable => (SetReportSemaphoreD & 0x8) != 0; - public SetReportSemaphoreDReductionOp SetReportSemaphoreDReductionOp => (SetReportSemaphoreDReductionOp)((SetReportSemaphoreD >> 9) & 0x7); - public SetReportSemaphoreDReductionFormat SetReportSemaphoreDReductionFormat => (SetReportSemaphoreDReductionFormat)((SetReportSemaphoreD >> 17) & 0x3); + public readonly SetReportSemaphoreDOperation SetReportSemaphoreDOperation => (SetReportSemaphoreDOperation)(SetReportSemaphoreD & 0x3); + public readonly bool SetReportSemaphoreDAwakenEnable => (SetReportSemaphoreD & 0x100000) != 0; + public readonly SetReportSemaphoreDStructureSize SetReportSemaphoreDStructureSize => (SetReportSemaphoreDStructureSize)((SetReportSemaphoreD >> 28) & 0x1); + public readonly bool SetReportSemaphoreDFlushDisable => (SetReportSemaphoreD & 0x4) != 0; + public readonly bool SetReportSemaphoreDReductionEnable => (SetReportSemaphoreD & 0x8) != 0; + public readonly SetReportSemaphoreDReductionOp SetReportSemaphoreDReductionOp => (SetReportSemaphoreDReductionOp)((SetReportSemaphoreD >> 9) & 0x7); + public readonly SetReportSemaphoreDReductionFormat SetReportSemaphoreDReductionFormat => (SetReportSemaphoreDReductionFormat)((SetReportSemaphoreD >> 17) & 0x3); public fixed uint Reserved1B10[702]; public uint SetBindlessTexture; - public int SetBindlessTextureConstantBufferSlotSelect => (int)(SetBindlessTexture & 0x7); + public readonly int SetBindlessTextureConstantBufferSlotSelect => (int)(SetBindlessTexture & 0x7); public uint SetTrapHandler; public fixed uint Reserved2610[843]; public Array8<uint> SetShaderPerformanceCounterValueUpper; @@ -423,13 +423,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute public bool SetShaderPerformanceCounterControlBWindowed(int i) => (SetShaderPerformanceCounterControlB[i] & 0x8) != 0; public int SetShaderPerformanceCounterControlBFunc(int i) => (int)((SetShaderPerformanceCounterControlB[i] >> 4) & 0xFFFF); public uint SetShaderPerformanceCounterTrapControl; - public int SetShaderPerformanceCounterTrapControlMask => (int)(SetShaderPerformanceCounterTrapControl & 0xFF); + public readonly int SetShaderPerformanceCounterTrapControlMask => (int)(SetShaderPerformanceCounterTrapControl & 0xFF); public uint StartShaderPerformanceCounter; - public int StartShaderPerformanceCounterCounterMask => (int)(StartShaderPerformanceCounter & 0xFF); + public readonly int StartShaderPerformanceCounterCounterMask => (int)(StartShaderPerformanceCounter & 0xFF); public uint StopShaderPerformanceCounter; - public int StopShaderPerformanceCounterCounterMask => (int)(StopShaderPerformanceCounter & 0xFF); + public readonly int StopShaderPerformanceCounterCounterMask => (int)(StopShaderPerformanceCounter & 0xFF); public fixed uint Reserved33E8[6]; - public MmeShadowScratch SetMmeShadowScratch; + public Array256<uint> SetMmeShadowScratch; #pragma warning restore CS0649 } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs index 1b20e41c2..4e750ff5d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum DependentQmdType { Queue, - Grid + Grid, } /// <summary> @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum ReleaseMembarType { FeNone, - FeSysmembar + FeSysmembar, } /// <summary> @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { L1None, L1Sysmembar, - L1Membar + L1Membar, } /// <summary> @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum Fp32NanBehavior { Legacy, - Fp64Compatible + Fp64Compatible, } /// <summary> @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum Fp32F2iNanBehavior { PassZero, - PassIndefinite + PassIndefinite, } /// <summary> @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum ApiVisibleCallLimit { _32, - NoCheck + NoCheck, } /// <summary> @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum SharedMemoryBankMapping { FourBytesPerBank, - EightBytesPerBank + EightBytesPerBank, } /// <summary> @@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum Fp32NarrowInstruction { KeepDenorms, - FlushDenorms + FlushDenorms, } /// <summary> @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { DirectlyAddressableMemorySize16kb, DirectlyAddressableMemorySize32kb, - DirectlyAddressableMemorySize48kb + DirectlyAddressableMemorySize48kb, } /// <summary> @@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute RedDec, RedAnd, RedOr, - RedXor + RedXor, } /// <summary> @@ -108,7 +108,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum ReductionFormat { Unsigned32, - Signed32 + Signed32, } /// <summary> @@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute enum StructureSize { FourWords, - OneWord + OneWord, } /// <summary> @@ -127,129 +127,129 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { private fixed int _words[64]; - public int OuterPut => BitRange(30, 0); - public bool OuterOverflow => Bit(31); - public int OuterGet => BitRange(62, 32); - public bool OuterStickyOverflow => Bit(63); - public int InnerGet => BitRange(94, 64); - public bool InnerOverflow => Bit(95); - public int InnerPut => BitRange(126, 96); - public bool InnerStickyOverflow => Bit(127); - public int QmdReservedAA => BitRange(159, 128); - public int DependentQmdPointer => BitRange(191, 160); - public int QmdGroupId => BitRange(197, 192); - public bool SmGlobalCachingEnable => Bit(198); - public bool RunCtaInOneSmPartition => Bit(199); - public bool IsQueue => Bit(200); - public bool AddToHeadOfQmdGroupLinkedList => Bit(201); - public bool SemaphoreReleaseEnable0 => Bit(202); - public bool SemaphoreReleaseEnable1 => Bit(203); - public bool RequireSchedulingPcas => Bit(204); - public bool DependentQmdScheduleEnable => Bit(205); - public DependentQmdType DependentQmdType => (DependentQmdType)BitRange(206, 206); - public bool DependentQmdFieldCopy => Bit(207); - public int QmdReservedB => BitRange(223, 208); - public int CircularQueueSize => BitRange(248, 224); - public bool QmdReservedC => Bit(249); - public bool InvalidateTextureHeaderCache => Bit(250); - public bool InvalidateTextureSamplerCache => Bit(251); - public bool InvalidateTextureDataCache => Bit(252); - public bool InvalidateShaderDataCache => Bit(253); - public bool InvalidateInstructionCache => Bit(254); - public bool InvalidateShaderConstantCache => Bit(255); - public int ProgramOffset => BitRange(287, 256); - public int CircularQueueAddrLower => BitRange(319, 288); - public int CircularQueueAddrUpper => BitRange(327, 320); - public int QmdReservedD => BitRange(335, 328); - public int CircularQueueEntrySize => BitRange(351, 336); - public int CwdReferenceCountId => BitRange(357, 352); - public int CwdReferenceCountDeltaMinusOne => BitRange(365, 358); - public ReleaseMembarType ReleaseMembarType => (ReleaseMembarType)BitRange(366, 366); - public bool CwdReferenceCountIncrEnable => Bit(367); - public CwdMembarType CwdMembarType => (CwdMembarType)BitRange(369, 368); - public bool SequentiallyRunCtas => Bit(370); - public bool CwdReferenceCountDecrEnable => Bit(371); - public bool Throttled => Bit(372); - public Fp32NanBehavior Fp32NanBehavior => (Fp32NanBehavior)BitRange(376, 376); - public Fp32F2iNanBehavior Fp32F2iNanBehavior => (Fp32F2iNanBehavior)BitRange(377, 377); - public ApiVisibleCallLimit ApiVisibleCallLimit => (ApiVisibleCallLimit)BitRange(378, 378); - public SharedMemoryBankMapping SharedMemoryBankMapping => (SharedMemoryBankMapping)BitRange(379, 379); - public SamplerIndex SamplerIndex => (SamplerIndex)BitRange(382, 382); - public Fp32NarrowInstruction Fp32NarrowInstruction => (Fp32NarrowInstruction)BitRange(383, 383); - public int CtaRasterWidth => BitRange(415, 384); - public int CtaRasterHeight => BitRange(431, 416); - public int CtaRasterDepth => BitRange(447, 432); - public int CtaRasterWidthResume => BitRange(479, 448); - public int CtaRasterHeightResume => BitRange(495, 480); - public int CtaRasterDepthResume => BitRange(511, 496); - public int QueueEntriesPerCtaMinusOne => BitRange(518, 512); - public int CoalesceWaitingPeriod => BitRange(529, 522); - public int SharedMemorySize => BitRange(561, 544); - public int QmdReservedG => BitRange(575, 562); - public int QmdVersion => BitRange(579, 576); - public int QmdMajorVersion => BitRange(583, 580); - public int QmdReservedH => BitRange(591, 584); - public int CtaThreadDimension0 => BitRange(607, 592); - public int CtaThreadDimension1 => BitRange(623, 608); - public int CtaThreadDimension2 => BitRange(639, 624); - public bool ConstantBufferValid(int i) => Bit(640 + i * 1); - public int QmdReservedI => BitRange(668, 648); - public L1Configuration L1Configuration => (L1Configuration)BitRange(671, 669); - public int SmDisableMaskLower => BitRange(703, 672); - public int SmDisableMaskUpper => BitRange(735, 704); - public int Release0AddressLower => BitRange(767, 736); - public int Release0AddressUpper => BitRange(775, 768); - public int QmdReservedJ => BitRange(783, 776); - public ReductionOp Release0ReductionOp => (ReductionOp)BitRange(790, 788); - public bool QmdReservedK => Bit(791); - public ReductionFormat Release0ReductionFormat => (ReductionFormat)BitRange(793, 792); - public bool Release0ReductionEnable => Bit(794); - public StructureSize Release0StructureSize => (StructureSize)BitRange(799, 799); - public int Release0Payload => BitRange(831, 800); - public int Release1AddressLower => BitRange(863, 832); - public int Release1AddressUpper => BitRange(871, 864); - public int QmdReservedL => BitRange(879, 872); - public ReductionOp Release1ReductionOp => (ReductionOp)BitRange(886, 884); - public bool QmdReservedM => Bit(887); - public ReductionFormat Release1ReductionFormat => (ReductionFormat)BitRange(889, 888); - public bool Release1ReductionEnable => Bit(890); - public StructureSize Release1StructureSize => (StructureSize)BitRange(895, 895); - public int Release1Payload => BitRange(927, 896); - public int ConstantBufferAddrLower(int i) => BitRange(959 + i * 64, 928 + i * 64); - public int ConstantBufferAddrUpper(int i) => BitRange(967 + i * 64, 960 + i * 64); - public int ConstantBufferReservedAddr(int i) => BitRange(973 + i * 64, 968 + i * 64); - public bool ConstantBufferInvalidate(int i) => Bit(974 + i * 64); - public int ConstantBufferSize(int i) => BitRange(991 + i * 64, 975 + i * 64); - public int ShaderLocalMemoryLowSize => BitRange(1463, 1440); - public int QmdReservedN => BitRange(1466, 1464); - public int BarrierCount => BitRange(1471, 1467); - public int ShaderLocalMemoryHighSize => BitRange(1495, 1472); - public int RegisterCount => BitRange(1503, 1496); - public int ShaderLocalMemoryCrsSize => BitRange(1527, 1504); - public int SassVersion => BitRange(1535, 1528); - public int HwOnlyInnerGet => BitRange(1566, 1536); - public bool HwOnlyRequireSchedulingPcas => Bit(1567); - public int HwOnlyInnerPut => BitRange(1598, 1568); - public bool HwOnlyScgType => Bit(1599); - public int HwOnlySpanListHeadIndex => BitRange(1629, 1600); - public bool QmdReservedQ => Bit(1630); - public bool HwOnlySpanListHeadIndexValid => Bit(1631); - public int HwOnlySkedNextQmdPointer => BitRange(1663, 1632); - public int QmdSpareE => BitRange(1695, 1664); - public int QmdSpareF => BitRange(1727, 1696); - public int QmdSpareG => BitRange(1759, 1728); - public int QmdSpareH => BitRange(1791, 1760); - public int QmdSpareI => BitRange(1823, 1792); - public int QmdSpareJ => BitRange(1855, 1824); - public int QmdSpareK => BitRange(1887, 1856); - public int QmdSpareL => BitRange(1919, 1888); - public int QmdSpareM => BitRange(1951, 1920); - public int QmdSpareN => BitRange(1983, 1952); - public int DebugIdUpper => BitRange(2015, 1984); - public int DebugIdLower => BitRange(2047, 2016); + public readonly int OuterPut => BitRange(30, 0); + public readonly bool OuterOverflow => Bit(31); + public readonly int OuterGet => BitRange(62, 32); + public readonly bool OuterStickyOverflow => Bit(63); + public readonly int InnerGet => BitRange(94, 64); + public readonly bool InnerOverflow => Bit(95); + public readonly int InnerPut => BitRange(126, 96); + public readonly bool InnerStickyOverflow => Bit(127); + public readonly int QmdReservedAA => BitRange(159, 128); + public readonly int DependentQmdPointer => BitRange(191, 160); + public readonly int QmdGroupId => BitRange(197, 192); + public readonly bool SmGlobalCachingEnable => Bit(198); + public readonly bool RunCtaInOneSmPartition => Bit(199); + public readonly bool IsQueue => Bit(200); + public readonly bool AddToHeadOfQmdGroupLinkedList => Bit(201); + public readonly bool SemaphoreReleaseEnable0 => Bit(202); + public readonly bool SemaphoreReleaseEnable1 => Bit(203); + public readonly bool RequireSchedulingPcas => Bit(204); + public readonly bool DependentQmdScheduleEnable => Bit(205); + public readonly DependentQmdType DependentQmdType => (DependentQmdType)BitRange(206, 206); + public readonly bool DependentQmdFieldCopy => Bit(207); + public readonly int QmdReservedB => BitRange(223, 208); + public readonly int CircularQueueSize => BitRange(248, 224); + public readonly bool QmdReservedC => Bit(249); + public readonly bool InvalidateTextureHeaderCache => Bit(250); + public readonly bool InvalidateTextureSamplerCache => Bit(251); + public readonly bool InvalidateTextureDataCache => Bit(252); + public readonly bool InvalidateShaderDataCache => Bit(253); + public readonly bool InvalidateInstructionCache => Bit(254); + public readonly bool InvalidateShaderConstantCache => Bit(255); + public readonly int ProgramOffset => BitRange(287, 256); + public readonly int CircularQueueAddrLower => BitRange(319, 288); + public readonly int CircularQueueAddrUpper => BitRange(327, 320); + public readonly int QmdReservedD => BitRange(335, 328); + public readonly int CircularQueueEntrySize => BitRange(351, 336); + public readonly int CwdReferenceCountId => BitRange(357, 352); + public readonly int CwdReferenceCountDeltaMinusOne => BitRange(365, 358); + public readonly ReleaseMembarType ReleaseMembarType => (ReleaseMembarType)BitRange(366, 366); + public readonly bool CwdReferenceCountIncrEnable => Bit(367); + public readonly CwdMembarType CwdMembarType => (CwdMembarType)BitRange(369, 368); + public readonly bool SequentiallyRunCtas => Bit(370); + public readonly bool CwdReferenceCountDecrEnable => Bit(371); + public readonly bool Throttled => Bit(372); + public readonly Fp32NanBehavior Fp32NanBehavior => (Fp32NanBehavior)BitRange(376, 376); + public readonly Fp32F2iNanBehavior Fp32F2iNanBehavior => (Fp32F2iNanBehavior)BitRange(377, 377); + public readonly ApiVisibleCallLimit ApiVisibleCallLimit => (ApiVisibleCallLimit)BitRange(378, 378); + public readonly SharedMemoryBankMapping SharedMemoryBankMapping => (SharedMemoryBankMapping)BitRange(379, 379); + public readonly SamplerIndex SamplerIndex => (SamplerIndex)BitRange(382, 382); + public readonly Fp32NarrowInstruction Fp32NarrowInstruction => (Fp32NarrowInstruction)BitRange(383, 383); + public readonly int CtaRasterWidth => BitRange(415, 384); + public readonly int CtaRasterHeight => BitRange(431, 416); + public readonly int CtaRasterDepth => BitRange(447, 432); + public readonly int CtaRasterWidthResume => BitRange(479, 448); + public readonly int CtaRasterHeightResume => BitRange(495, 480); + public readonly int CtaRasterDepthResume => BitRange(511, 496); + public readonly int QueueEntriesPerCtaMinusOne => BitRange(518, 512); + public readonly int CoalesceWaitingPeriod => BitRange(529, 522); + public readonly int SharedMemorySize => BitRange(561, 544); + public readonly int QmdReservedG => BitRange(575, 562); + public readonly int QmdVersion => BitRange(579, 576); + public readonly int QmdMajorVersion => BitRange(583, 580); + public readonly int QmdReservedH => BitRange(591, 584); + public readonly int CtaThreadDimension0 => BitRange(607, 592); + public readonly int CtaThreadDimension1 => BitRange(623, 608); + public readonly int CtaThreadDimension2 => BitRange(639, 624); + public readonly bool ConstantBufferValid(int i) => Bit(640 + i * 1); + public readonly int QmdReservedI => BitRange(668, 648); + public readonly L1Configuration L1Configuration => (L1Configuration)BitRange(671, 669); + public readonly int SmDisableMaskLower => BitRange(703, 672); + public readonly int SmDisableMaskUpper => BitRange(735, 704); + public readonly int Release0AddressLower => BitRange(767, 736); + public readonly int Release0AddressUpper => BitRange(775, 768); + public readonly int QmdReservedJ => BitRange(783, 776); + public readonly ReductionOp Release0ReductionOp => (ReductionOp)BitRange(790, 788); + public readonly bool QmdReservedK => Bit(791); + public readonly ReductionFormat Release0ReductionFormat => (ReductionFormat)BitRange(793, 792); + public readonly bool Release0ReductionEnable => Bit(794); + public readonly StructureSize Release0StructureSize => (StructureSize)BitRange(799, 799); + public readonly int Release0Payload => BitRange(831, 800); + public readonly int Release1AddressLower => BitRange(863, 832); + public readonly int Release1AddressUpper => BitRange(871, 864); + public readonly int QmdReservedL => BitRange(879, 872); + public readonly ReductionOp Release1ReductionOp => (ReductionOp)BitRange(886, 884); + public readonly bool QmdReservedM => Bit(887); + public readonly ReductionFormat Release1ReductionFormat => (ReductionFormat)BitRange(889, 888); + public readonly bool Release1ReductionEnable => Bit(890); + public readonly StructureSize Release1StructureSize => (StructureSize)BitRange(895, 895); + public readonly int Release1Payload => BitRange(927, 896); + public readonly int ConstantBufferAddrLower(int i) => BitRange(959 + i * 64, 928 + i * 64); + public readonly int ConstantBufferAddrUpper(int i) => BitRange(967 + i * 64, 960 + i * 64); + public readonly int ConstantBufferReservedAddr(int i) => BitRange(973 + i * 64, 968 + i * 64); + public readonly bool ConstantBufferInvalidate(int i) => Bit(974 + i * 64); + public readonly int ConstantBufferSize(int i) => BitRange(991 + i * 64, 975 + i * 64); + public readonly int ShaderLocalMemoryLowSize => BitRange(1463, 1440); + public readonly int QmdReservedN => BitRange(1466, 1464); + public readonly int BarrierCount => BitRange(1471, 1467); + public readonly int ShaderLocalMemoryHighSize => BitRange(1495, 1472); + public readonly int RegisterCount => BitRange(1503, 1496); + public readonly int ShaderLocalMemoryCrsSize => BitRange(1527, 1504); + public readonly int SassVersion => BitRange(1535, 1528); + public readonly int HwOnlyInnerGet => BitRange(1566, 1536); + public readonly bool HwOnlyRequireSchedulingPcas => Bit(1567); + public readonly int HwOnlyInnerPut => BitRange(1598, 1568); + public readonly bool HwOnlyScgType => Bit(1599); + public readonly int HwOnlySpanListHeadIndex => BitRange(1629, 1600); + public readonly bool QmdReservedQ => Bit(1630); + public readonly bool HwOnlySpanListHeadIndexValid => Bit(1631); + public readonly int HwOnlySkedNextQmdPointer => BitRange(1663, 1632); + public readonly int QmdSpareE => BitRange(1695, 1664); + public readonly int QmdSpareF => BitRange(1727, 1696); + public readonly int QmdSpareG => BitRange(1759, 1728); + public readonly int QmdSpareH => BitRange(1791, 1760); + public readonly int QmdSpareI => BitRange(1823, 1792); + public readonly int QmdSpareJ => BitRange(1855, 1824); + public readonly int QmdSpareK => BitRange(1887, 1856); + public readonly int QmdSpareL => BitRange(1919, 1888); + public readonly int QmdSpareM => BitRange(1951, 1920); + public readonly int QmdSpareN => BitRange(1983, 1952); + public readonly int DebugIdUpper => BitRange(2015, 1984); + public readonly int DebugIdLower => BitRange(2047, 2016); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool Bit(int bit) + private readonly bool Bit(int bit) { if ((uint)bit >= 64 * 32) { @@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int BitRange(int upper, int lower) + private readonly int BitRange(int upper, int lower) { if ((uint)lower >= 64 * 32) { @@ -272,4 +272,4 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute return (_words[lower >> 5] >> (lower & 31)) & mask; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs b/src/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs index 5581b5cc1..52d7f69f2 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs @@ -7,6 +7,6 @@ { False, True, - Host + Host, } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index fd93cd8ba..e6557780b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -30,13 +30,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma SrcLinear = 1 << 7, DstLinear = 1 << 8, MultiLineEnable = 1 << 9, - RemapEnable = 1 << 10 + RemapEnable = 1 << 10, } /// <summary> /// Texture parameters for copy. /// </summary> - private struct TextureParams + private readonly struct TextureParams { /// <summary> /// Copy region X coordinate. @@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma _3dEngine = threedEngine; _state = new DeviceState<DmaClassState>(new Dictionary<string, RwCallback> { - { nameof(DmaClassState.LaunchDma), new RwCallback(LaunchDma, null) } + { nameof(DmaClassState.LaunchDma), new RwCallback(LaunchDma, null) }, }); } @@ -345,8 +345,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma // all be rewritten to use pooled arrays, but that gets complicated with packed data and strides Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray(); - TextureParams srcParams = new TextureParams(srcRegionX, srcRegionY, srcBaseOffset, srcBpp, srcLinear, srcCalculator); - TextureParams dstParams = new TextureParams(dstRegionX, dstRegionY, dstBaseOffset, dstBpp, dstLinear, dstCalculator); + TextureParams srcParams = new(srcRegionX, srcRegionY, srcBaseOffset, srcBpp, srcLinear, srcCalculator); + TextureParams dstParams = new(dstRegionX, dstRegionY, dstBaseOffset, dstBpp, dstLinear, dstCalculator); // If remapping is enabled, we always copy the components directly, in order. // If it's enabled, but the mapping is just XYZW, we also copy them in order. @@ -363,13 +363,26 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma switch (srcBpp) { - case 1: Copy<byte>(dstSpan, srcSpan, dstParams, srcParams); break; - case 2: Copy<ushort>(dstSpan, srcSpan, dstParams, srcParams); break; - case 4: Copy<uint>(dstSpan, srcSpan, dstParams, srcParams); break; - case 8: Copy<ulong>(dstSpan, srcSpan, dstParams, srcParams); break; - case 12: Copy<Bpp12Pixel>(dstSpan, srcSpan, dstParams, srcParams); break; - case 16: Copy<Vector128<byte>>(dstSpan, srcSpan, dstParams, srcParams); break; - default: throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format."); + case 1: + Copy<byte>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 2: + Copy<ushort>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 4: + Copy<uint>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 8: + Copy<ulong>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 12: + Copy<Bpp12Pixel>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 16: + Copy<Vector128<byte>>(dstSpan, srcSpan, dstParams, srcParams); + break; + default: + throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format."); } } else @@ -378,11 +391,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma switch (componentSize) { - case 1: CopyShuffle<byte>(dstSpan, srcSpan, dstParams, srcParams); break; - case 2: CopyShuffle<ushort>(dstSpan, srcSpan, dstParams, srcParams); break; - case 3: CopyShuffle<UInt24>(dstSpan, srcSpan, dstParams, srcParams); break; - case 4: CopyShuffle<uint>(dstSpan, srcSpan, dstParams, srcParams); break; - default: throw new NotSupportedException($"Unable to copy ${componentSize} component size."); + case 1: + CopyShuffle<byte>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 2: + CopyShuffle<ushort>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 3: + CopyShuffle<UInt24>(dstSpan, srcSpan, dstParams, srcParams); + break; + case 4: + CopyShuffle<uint>(dstSpan, srcSpan, dstParams, srcParams); + break; + default: + throw new NotSupportedException($"Unable to copy ${componentSize} component size."); } } @@ -526,28 +548,28 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma 0 => _state.State.SetRemapComponentsDstX, 1 => _state.State.SetRemapComponentsDstY, 2 => _state.State.SetRemapComponentsDstZ, - _ => _state.State.SetRemapComponentsDstW + _ => _state.State.SetRemapComponentsDstW, }; switch (componentsDst) { case SetRemapComponentsDst.SrcX: - Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan, dst, src); + Copy<T>(dstSpan[(Unsafe.SizeOf<T>() * i)..], srcSpan, dst, src); break; case SetRemapComponentsDst.SrcY: - Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>()), dst, src); + Copy<T>(dstSpan[(Unsafe.SizeOf<T>() * i)..], srcSpan[Unsafe.SizeOf<T>()..], dst, src); break; case SetRemapComponentsDst.SrcZ: - Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>() * 2), dst, src); + Copy<T>(dstSpan[(Unsafe.SizeOf<T>() * i)..], srcSpan[(Unsafe.SizeOf<T>() * 2)..], dst, src); break; case SetRemapComponentsDst.SrcW: - Copy<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), srcSpan.Slice(Unsafe.SizeOf<T>() * 3), dst, src); + Copy<T>(dstSpan[(Unsafe.SizeOf<T>() * i)..], srcSpan[(Unsafe.SizeOf<T>() * 3)..], dst, src); break; case SetRemapComponentsDst.ConstA: - Fill<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstA)); + Fill<T>(dstSpan[(Unsafe.SizeOf<T>() * i)..], dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstA)); break; case SetRemapComponentsDst.ConstB: - Fill<T>(dstSpan.Slice(Unsafe.SizeOf<T>() * i), dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstB)); + Fill<T>(dstSpan[(Unsafe.SizeOf<T>() * i)..], dst, Unsafe.As<uint, T>(ref _state.State.SetRemapConstB)); break; } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs index 6f3b91f2c..d85887368 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClassState.cs @@ -179,49 +179,49 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma /// </summary> unsafe struct DmaClassState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public fixed uint Reserved00[64]; public uint Nop; public fixed uint Reserved104[15]; public uint PmTrigger; public fixed uint Reserved144[63]; public uint SetSemaphoreA; - public int SetSemaphoreAUpper => (int)(SetSemaphoreA & 0xFF); + public readonly int SetSemaphoreAUpper => (int)(SetSemaphoreA & 0xFF); public uint SetSemaphoreB; public uint SetSemaphorePayload; public fixed uint Reserved24C[2]; public uint SetRenderEnableA; - public int SetRenderEnableAUpper => (int)(SetRenderEnableA & 0xFF); + public readonly int SetRenderEnableAUpper => (int)(SetRenderEnableA & 0xFF); public uint SetRenderEnableB; public uint SetRenderEnableC; - public int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); + public readonly int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); public uint SetSrcPhysMode; - public SetPhysModeTarget SetSrcPhysModeTarget => (SetPhysModeTarget)(SetSrcPhysMode & 0x3); + public readonly SetPhysModeTarget SetSrcPhysModeTarget => (SetPhysModeTarget)(SetSrcPhysMode & 0x3); public uint SetDstPhysMode; - public SetPhysModeTarget SetDstPhysModeTarget => (SetPhysModeTarget)(SetDstPhysMode & 0x3); + public readonly SetPhysModeTarget SetDstPhysModeTarget => (SetPhysModeTarget)(SetDstPhysMode & 0x3); public fixed uint Reserved268[38]; public uint LaunchDma; - public LaunchDmaDataTransferType LaunchDmaDataTransferType => (LaunchDmaDataTransferType)(LaunchDma & 0x3); - public bool LaunchDmaFlushEnable => (LaunchDma & 0x4) != 0; - public LaunchDmaSemaphoreType LaunchDmaSemaphoreType => (LaunchDmaSemaphoreType)((LaunchDma >> 3) & 0x3); - public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 5) & 0x3); - public LaunchDmaMemoryLayout LaunchDmaSrcMemoryLayout => (LaunchDmaMemoryLayout)((LaunchDma >> 7) & 0x1); - public LaunchDmaMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaMemoryLayout)((LaunchDma >> 8) & 0x1); - public bool LaunchDmaMultiLineEnable => (LaunchDma & 0x200) != 0; - public bool LaunchDmaRemapEnable => (LaunchDma & 0x400) != 0; - public bool LaunchDmaForceRmwdisable => (LaunchDma & 0x800) != 0; - public LaunchDmaType LaunchDmaSrcType => (LaunchDmaType)((LaunchDma >> 12) & 0x1); - public LaunchDmaType LaunchDmaDstType => (LaunchDmaType)((LaunchDma >> 13) & 0x1); - public LaunchDmaSemaphoreReduction LaunchDmaSemaphoreReduction => (LaunchDmaSemaphoreReduction)((LaunchDma >> 14) & 0xF); - public LaunchDmaSemaphoreReductionSign LaunchDmaSemaphoreReductionSign => (LaunchDmaSemaphoreReductionSign)((LaunchDma >> 18) & 0x1); - public bool LaunchDmaSemaphoreReductionEnable => (LaunchDma & 0x80000) != 0; - public LaunchDmaBypassL2 LaunchDmaBypassL2 => (LaunchDmaBypassL2)((LaunchDma >> 20) & 0x1); + public readonly LaunchDmaDataTransferType LaunchDmaDataTransferType => (LaunchDmaDataTransferType)(LaunchDma & 0x3); + public readonly bool LaunchDmaFlushEnable => (LaunchDma & 0x4) != 0; + public readonly LaunchDmaSemaphoreType LaunchDmaSemaphoreType => (LaunchDmaSemaphoreType)((LaunchDma >> 3) & 0x3); + public readonly LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 5) & 0x3); + public readonly LaunchDmaMemoryLayout LaunchDmaSrcMemoryLayout => (LaunchDmaMemoryLayout)((LaunchDma >> 7) & 0x1); + public readonly LaunchDmaMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaMemoryLayout)((LaunchDma >> 8) & 0x1); + public readonly bool LaunchDmaMultiLineEnable => (LaunchDma & 0x200) != 0; + public readonly bool LaunchDmaRemapEnable => (LaunchDma & 0x400) != 0; + public readonly bool LaunchDmaForceRmwdisable => (LaunchDma & 0x800) != 0; + public readonly LaunchDmaType LaunchDmaSrcType => (LaunchDmaType)((LaunchDma >> 12) & 0x1); + public readonly LaunchDmaType LaunchDmaDstType => (LaunchDmaType)((LaunchDma >> 13) & 0x1); + public readonly LaunchDmaSemaphoreReduction LaunchDmaSemaphoreReduction => (LaunchDmaSemaphoreReduction)((LaunchDma >> 14) & 0xF); + public readonly LaunchDmaSemaphoreReductionSign LaunchDmaSemaphoreReductionSign => (LaunchDmaSemaphoreReductionSign)((LaunchDma >> 18) & 0x1); + public readonly bool LaunchDmaSemaphoreReductionEnable => (LaunchDma & 0x80000) != 0; + public readonly LaunchDmaBypassL2 LaunchDmaBypassL2 => (LaunchDmaBypassL2)((LaunchDma >> 20) & 0x1); public fixed uint Reserved304[63]; public uint OffsetInUpper; - public int OffsetInUpperUpper => (int)(OffsetInUpper & 0xFF); + public readonly int OffsetInUpperUpper => (int)(OffsetInUpper & 0xFF); public uint OffsetInLower; public uint OffsetOutUpper; - public int OffsetOutUpperUpper => (int)(OffsetOutUpper & 0xFF); + public readonly int OffsetOutUpperUpper => (int)(OffsetOutUpper & 0xFF); public uint OffsetOutLower; public uint PitchIn; public uint PitchOut; @@ -231,38 +231,38 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public uint SetRemapConstA; public uint SetRemapConstB; public uint SetRemapComponents; - public SetRemapComponentsDst SetRemapComponentsDstX => (SetRemapComponentsDst)(SetRemapComponents & 0x7); - public SetRemapComponentsDst SetRemapComponentsDstY => (SetRemapComponentsDst)((SetRemapComponents >> 4) & 0x7); - public SetRemapComponentsDst SetRemapComponentsDstZ => (SetRemapComponentsDst)((SetRemapComponents >> 8) & 0x7); - public SetRemapComponentsDst SetRemapComponentsDstW => (SetRemapComponentsDst)((SetRemapComponents >> 12) & 0x7); - public SetRemapComponentsComponentSize SetRemapComponentsComponentSize => (SetRemapComponentsComponentSize)((SetRemapComponents >> 16) & 0x3); - public SetRemapComponentsNumComponents SetRemapComponentsNumSrcComponents => (SetRemapComponentsNumComponents)((SetRemapComponents >> 20) & 0x3); - public SetRemapComponentsNumComponents SetRemapComponentsNumDstComponents => (SetRemapComponentsNumComponents)((SetRemapComponents >> 24) & 0x3); + public readonly SetRemapComponentsDst SetRemapComponentsDstX => (SetRemapComponentsDst)(SetRemapComponents & 0x7); + public readonly SetRemapComponentsDst SetRemapComponentsDstY => (SetRemapComponentsDst)((SetRemapComponents >> 4) & 0x7); + public readonly SetRemapComponentsDst SetRemapComponentsDstZ => (SetRemapComponentsDst)((SetRemapComponents >> 8) & 0x7); + public readonly SetRemapComponentsDst SetRemapComponentsDstW => (SetRemapComponentsDst)((SetRemapComponents >> 12) & 0x7); + public readonly SetRemapComponentsComponentSize SetRemapComponentsComponentSize => (SetRemapComponentsComponentSize)((SetRemapComponents >> 16) & 0x3); + public readonly SetRemapComponentsNumComponents SetRemapComponentsNumSrcComponents => (SetRemapComponentsNumComponents)((SetRemapComponents >> 20) & 0x3); + public readonly SetRemapComponentsNumComponents SetRemapComponentsNumDstComponents => (SetRemapComponentsNumComponents)((SetRemapComponents >> 24) & 0x3); public uint SetDstBlockSize; - public SetBlockSizeWidth SetDstBlockSizeWidth => (SetBlockSizeWidth)(SetDstBlockSize & 0xF); - public SetBlockSizeHeight SetDstBlockSizeHeight => (SetBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); - public SetBlockSizeDepth SetDstBlockSizeDepth => (SetBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); - public SetBlockSizeGobHeight SetDstBlockSizeGobHeight => (SetBlockSizeGobHeight)((SetDstBlockSize >> 12) & 0xF); + public readonly SetBlockSizeWidth SetDstBlockSizeWidth => (SetBlockSizeWidth)(SetDstBlockSize & 0xF); + public readonly SetBlockSizeHeight SetDstBlockSizeHeight => (SetBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); + public readonly SetBlockSizeDepth SetDstBlockSizeDepth => (SetBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); + public readonly SetBlockSizeGobHeight SetDstBlockSizeGobHeight => (SetBlockSizeGobHeight)((SetDstBlockSize >> 12) & 0xF); public uint SetDstWidth; public uint SetDstHeight; public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOrigin; - public int SetDstOriginX => (int)(SetDstOrigin & 0xFFFF); - public int SetDstOriginY => (int)((SetDstOrigin >> 16) & 0xFFFF); + public readonly int SetDstOriginX => (int)(SetDstOrigin & 0xFFFF); + public readonly int SetDstOriginY => (int)((SetDstOrigin >> 16) & 0xFFFF); public uint Reserved724; public uint SetSrcBlockSize; - public SetBlockSizeWidth SetSrcBlockSizeWidth => (SetBlockSizeWidth)(SetSrcBlockSize & 0xF); - public SetBlockSizeHeight SetSrcBlockSizeHeight => (SetBlockSizeHeight)((SetSrcBlockSize >> 4) & 0xF); - public SetBlockSizeDepth SetSrcBlockSizeDepth => (SetBlockSizeDepth)((SetSrcBlockSize >> 8) & 0xF); - public SetBlockSizeGobHeight SetSrcBlockSizeGobHeight => (SetBlockSizeGobHeight)((SetSrcBlockSize >> 12) & 0xF); + public readonly SetBlockSizeWidth SetSrcBlockSizeWidth => (SetBlockSizeWidth)(SetSrcBlockSize & 0xF); + public readonly SetBlockSizeHeight SetSrcBlockSizeHeight => (SetBlockSizeHeight)((SetSrcBlockSize >> 4) & 0xF); + public readonly SetBlockSizeDepth SetSrcBlockSizeDepth => (SetBlockSizeDepth)((SetSrcBlockSize >> 8) & 0xF); + public readonly SetBlockSizeGobHeight SetSrcBlockSizeGobHeight => (SetBlockSizeGobHeight)((SetSrcBlockSize >> 12) & 0xF); public uint SetSrcWidth; public uint SetSrcHeight; public uint SetSrcDepth; public uint SetSrcLayer; public uint SetSrcOrigin; - public int SetSrcOriginX => (int)(SetSrcOrigin & 0xFFFF); - public int SetSrcOriginY => (int)((SetSrcOrigin >> 16) & 0xFFFF); + public readonly int SetSrcOriginX => (int)(SetSrcOrigin & 0xFFFF); + public readonly int SetSrcOriginY => (int)((SetSrcOrigin >> 16) & 0xFFFF); public fixed uint Reserved740[629]; public uint PmTriggerEnd; public fixed uint Reserved1118[2490]; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs index 6873ff40f..8193c4a2c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma /// </summary> struct DmaTexture { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public MemoryLayout MemoryLayout; public int Width; public int Height; @@ -17,4 +17,4 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma public ushort RegionY; #pragma warning restore CS0649 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs index d082ee9dd..afa4db5ec 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo Grp0SetSubDevMask = 1, Grp0StoreSubDevMask = 2, Grp0UseSubDevMask = 3, - Grp2NonIncMethod = 0 + Grp2NonIncMethod = Grp0IncMethod, } enum SecOp @@ -20,22 +20,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo ImmdDataMethod = 4, OneInc = 5, Reserved6 = 6, - EndPbSegment = 7 + EndPbSegment = 7, } struct CompressedMethod { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Method; #pragma warning restore CS0649 - public int MethodAddressOld => (int)((Method >> 2) & 0x7FF); - public int MethodAddress => (int)(Method & 0xFFF); - public int SubdeviceMask => (int)((Method >> 4) & 0xFFF); - public int MethodSubchannel => (int)((Method >> 13) & 0x7); - public TertOp TertOp => (TertOp)((Method >> 16) & 0x3); - public int MethodCountOld => (int)((Method >> 18) & 0x7FF); - public int MethodCount => (int)((Method >> 16) & 0x1FFF); - public int ImmdData => (int)((Method >> 16) & 0x1FFF); - public SecOp SecOp => (SecOp)((Method >> 29) & 0x7); + public readonly int MethodAddressOld => (int)((Method >> 2) & 0x7FF); + public readonly int MethodAddress => (int)(Method & 0xFFF); + public readonly int SubdeviceMask => (int)((Method >> 4) & 0xFFF); + public readonly int MethodSubchannel => (int)((Method >> 13) & 0x7); + public readonly TertOp TertOp => (TertOp)((Method >> 16) & 0x3); + public readonly int MethodCountOld => (int)((Method >> 18) & 0x7FF); + public readonly int MethodCount => (int)((Method >> 16) & 0x1FFF); + public readonly int ImmdData => (int)((Method >> 16) & 0x1FFF); + public readonly SecOp SecOp => (SecOp)((Method >> 29) & 0x7); } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs index 31ba3217d..81e28acf4 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs @@ -36,20 +36,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo struct GPEntry { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Entry0; #pragma warning restore CS0649 - public Entry0Fetch Entry0Fetch => (Entry0Fetch)(Entry0 & 0x1); - public int Entry0Get => (int)((Entry0 >> 2) & 0x3FFFFFFF); - public int Entry0Operand => (int)(Entry0); -#pragma warning disable CS0649 + public readonly Entry0Fetch Entry0Fetch => (Entry0Fetch)(Entry0 & 0x1); + public readonly int Entry0Get => (int)((Entry0 >> 2) & 0x3FFFFFFF); + public readonly int Entry0Operand => (int)(Entry0); +#pragma warning disable CS0649 // Field is never assigned to public uint Entry1; #pragma warning restore CS0649 - public int Entry1GetHi => (int)(Entry1 & 0xFF); - public Entry1Priv Entry1Priv => (Entry1Priv)((Entry1 >> 8) & 0x1); - public Entry1Level Entry1Level => (Entry1Level)((Entry1 >> 9) & 0x1); - public int Entry1Length => (int)((Entry1 >> 10) & 0x1FFFFF); - public Entry1Sync Entry1Sync => (Entry1Sync)((Entry1 >> 31) & 0x1); - public Entry1Opcode Entry1Opcode => (Entry1Opcode)(Entry1 & 0xFF); + public readonly int Entry1GetHi => (int)(Entry1 & 0xFF); + public readonly Entry1Priv Entry1Priv => (Entry1Priv)((Entry1 >> 8) & 0x1); + public readonly Entry1Level Entry1Level => (Entry1Level)((Entry1 >> 9) & 0x1); + public readonly int Entry1Length => (int)((Entry1 >> 10) & 0x1FFFFF); + public readonly Entry1Sync Entry1Sync => (Entry1Sync)((Entry1 >> 31) & 0x1); + public readonly Entry1Opcode Entry1Opcode => (Entry1Opcode)(Entry1 & 0xFF); } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs index 7a11c649c..4bdbd1a03 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs @@ -16,7 +16,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo private readonly GPFifoProcessor _parent; private readonly DeviceState<GPFifoClassState> _state; - private int _previousSubChannel; private bool _createSyncPending; private const int MacrosCount = 0x80; @@ -45,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo { nameof(GPFifoClassState.SetReference), new RwCallback(SetReference, null) }, { nameof(GPFifoClassState.LoadMmeInstructionRam), new RwCallback(LoadMmeInstructionRam, null) }, { nameof(GPFifoClassState.LoadMmeStartAddressRam), new RwCallback(LoadMmeStartAddressRam, null) }, - { nameof(GPFifoClassState.SetMmeShadowRamControl), new RwCallback(SetMmeShadowRamControl, null) } + { nameof(GPFifoClassState.SetMmeShadowRamControl), new RwCallback(SetMmeShadowRamControl, null) }, }); _macros = new Macro[MacrosCount]; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs index ebfe15664..dd6ae1b40 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo Release = 2, AcqGeq = 4, AcqAnd = 8, - Reduction = 16 + Reduction = 16, } /// <summary> @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum SemaphoredAcquireSwitch { Disabled = 0, - Enabled = 1 + Enabled = 1, } /// <summary> @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum SemaphoredReleaseWfi { En = 0, - Dis = 1 + Dis = 1, } /// <summary> @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum SemaphoredReleaseSize { SixteenBytes = 0, - FourBytes = 1 + FourBytes = 1, } /// <summary> @@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo Or = 4, Add = 5, Inc = 6, - Dec = 7 + Dec = 7, } /// <summary> @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum SemaphoredFormat { Signed = 0, - Unsigned = 1 + Unsigned = 1, } /// <summary> @@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum MemOpCTlbInvalidatePdb { One = 0, - All = 1 + All = 1, } /// <summary> @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum MemOpCTlbInvalidateGpc { Enable = 0, - Disable = 1 + Disable = 1, } /// <summary> @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo { VidMem = 0, SysMemCoherent = 2, - SysMemNoncoherent = 3 + SysMemNoncoherent = 3, } /// <summary> @@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo L2PeermemInvalidate = 13, L2SysmemInvalidate = 14, L2CleanComptags = 15, - L2FlushDirty = 16 + L2FlushDirty = 16, } /// <summary> @@ -114,7 +114,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum SyncpointbOperation { Wait = 0, - Incr = 1 + Incr = 1, } /// <summary> @@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum SyncpointbWaitSwitch { Dis = 0, - En = 1 + En = 1, } /// <summary> @@ -132,7 +132,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo enum WfiScope { CurrentScgType = 0, - All = 1 + All = 1, } /// <summary> @@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo Nop = 0, PbdmaTimeslice = 1, RunlistTimeslice = 2, - Tsg = 3 + Tsg = 3, } /// <summary> @@ -151,44 +151,44 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo /// </summary> struct GPFifoClassState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint SetObject; - public int SetObjectNvclass => (int)(SetObject & 0xFFFF); - public int SetObjectEngine => (int)((SetObject >> 16) & 0x1F); + public readonly int SetObjectNvclass => (int)(SetObject & 0xFFFF); + public readonly int SetObjectEngine => (int)((SetObject >> 16) & 0x1F); public uint Illegal; - public int IllegalHandle => (int)(Illegal); + public readonly int IllegalHandle => (int)(Illegal); public uint Nop; - public int NopHandle => (int)(Nop); + public readonly int NopHandle => (int)(Nop); public uint Reserved0C; public uint Semaphorea; - public int SemaphoreaOffsetUpper => (int)(Semaphorea & 0xFF); + public readonly int SemaphoreaOffsetUpper => (int)(Semaphorea & 0xFF); public uint Semaphoreb; - public int SemaphorebOffsetLower => (int)((Semaphoreb >> 2) & 0x3FFFFFFF); + public readonly int SemaphorebOffsetLower => (int)((Semaphoreb >> 2) & 0x3FFFFFFF); public uint Semaphorec; - public int SemaphorecPayload => (int)(Semaphorec); + public readonly int SemaphorecPayload => (int)(Semaphorec); public uint Semaphored; - public SemaphoredOperation SemaphoredOperation => (SemaphoredOperation)(Semaphored & 0x1F); - public SemaphoredAcquireSwitch SemaphoredAcquireSwitch => (SemaphoredAcquireSwitch)((Semaphored >> 12) & 0x1); - public SemaphoredReleaseWfi SemaphoredReleaseWfi => (SemaphoredReleaseWfi)((Semaphored >> 20) & 0x1); - public SemaphoredReleaseSize SemaphoredReleaseSize => (SemaphoredReleaseSize)((Semaphored >> 24) & 0x1); - public SemaphoredReduction SemaphoredReduction => (SemaphoredReduction)((Semaphored >> 27) & 0xF); - public SemaphoredFormat SemaphoredFormat => (SemaphoredFormat)((Semaphored >> 31) & 0x1); + public readonly SemaphoredOperation SemaphoredOperation => (SemaphoredOperation)(Semaphored & 0x1F); + public readonly SemaphoredAcquireSwitch SemaphoredAcquireSwitch => (SemaphoredAcquireSwitch)((Semaphored >> 12) & 0x1); + public readonly SemaphoredReleaseWfi SemaphoredReleaseWfi => (SemaphoredReleaseWfi)((Semaphored >> 20) & 0x1); + public readonly SemaphoredReleaseSize SemaphoredReleaseSize => (SemaphoredReleaseSize)((Semaphored >> 24) & 0x1); + public readonly SemaphoredReduction SemaphoredReduction => (SemaphoredReduction)((Semaphored >> 27) & 0xF); + public readonly SemaphoredFormat SemaphoredFormat => (SemaphoredFormat)((Semaphored >> 31) & 0x1); public uint NonStallInterrupt; - public int NonStallInterruptHandle => (int)(NonStallInterrupt); + public readonly int NonStallInterruptHandle => (int)(NonStallInterrupt); public uint FbFlush; - public int FbFlushHandle => (int)(FbFlush); + public readonly int FbFlushHandle => (int)(FbFlush); public uint Reserved28; public uint Reserved2C; public uint MemOpC; - public int MemOpCOperandLow => (int)((MemOpC >> 2) & 0x3FFFFFFF); - public MemOpCTlbInvalidatePdb MemOpCTlbInvalidatePdb => (MemOpCTlbInvalidatePdb)(MemOpC & 0x1); - public MemOpCTlbInvalidateGpc MemOpCTlbInvalidateGpc => (MemOpCTlbInvalidateGpc)((MemOpC >> 1) & 0x1); - public MemOpCTlbInvalidateTarget MemOpCTlbInvalidateTarget => (MemOpCTlbInvalidateTarget)((MemOpC >> 10) & 0x3); - public int MemOpCTlbInvalidateAddrLo => (int)((MemOpC >> 12) & 0xFFFFF); + public readonly int MemOpCOperandLow => (int)((MemOpC >> 2) & 0x3FFFFFFF); + public readonly MemOpCTlbInvalidatePdb MemOpCTlbInvalidatePdb => (MemOpCTlbInvalidatePdb)(MemOpC & 0x1); + public readonly MemOpCTlbInvalidateGpc MemOpCTlbInvalidateGpc => (MemOpCTlbInvalidateGpc)((MemOpC >> 1) & 0x1); + public readonly MemOpCTlbInvalidateTarget MemOpCTlbInvalidateTarget => (MemOpCTlbInvalidateTarget)((MemOpC >> 10) & 0x3); + public readonly int MemOpCTlbInvalidateAddrLo => (int)((MemOpC >> 12) & 0xFFFFF); public uint MemOpD; - public int MemOpDOperandHigh => (int)(MemOpD & 0xFF); - public MemOpDOperation MemOpDOperation => (MemOpDOperation)((MemOpD >> 27) & 0x1F); - public int MemOpDTlbInvalidateAddrHi => (int)(MemOpD & 0xFF); + public readonly int MemOpDOperandHigh => (int)(MemOpD & 0xFF); + public readonly MemOpDOperation MemOpDOperation => (MemOpDOperation)((MemOpD >> 27) & 0x1F); + public readonly int MemOpDTlbInvalidateAddrHi => (int)(MemOpD & 0xFF); public uint Reserved38; public uint Reserved3C; public uint Reserved40; @@ -196,7 +196,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo public uint Reserved48; public uint Reserved4C; public uint SetReference; - public int SetReferenceCount => (int)(SetReference); + public readonly int SetReferenceCount => (int)(SetReference); public uint Reserved54; public uint Reserved58; public uint Reserved5C; @@ -205,17 +205,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo public uint Reserved68; public uint Reserved6C; public uint Syncpointa; - public int SyncpointaPayload => (int)(Syncpointa); + public readonly int SyncpointaPayload => (int)(Syncpointa); public uint Syncpointb; - public SyncpointbOperation SyncpointbOperation => (SyncpointbOperation)(Syncpointb & 0x1); - public SyncpointbWaitSwitch SyncpointbWaitSwitch => (SyncpointbWaitSwitch)((Syncpointb >> 4) & 0x1); - public int SyncpointbSyncptIndex => (int)((Syncpointb >> 8) & 0xFFF); + public readonly SyncpointbOperation SyncpointbOperation => (SyncpointbOperation)(Syncpointb & 0x1); + public readonly SyncpointbWaitSwitch SyncpointbWaitSwitch => (SyncpointbWaitSwitch)((Syncpointb >> 4) & 0x1); + public readonly int SyncpointbSyncptIndex => (int)((Syncpointb >> 8) & 0xFFF); public uint Wfi; - public WfiScope WfiScope => (WfiScope)(Wfi & 0x1); + public readonly WfiScope WfiScope => (WfiScope)(Wfi & 0x1); public uint CrcCheck; - public int CrcCheckValue => (int)(CrcCheck); + public readonly int CrcCheckValue => (int)(CrcCheck); public uint Yield; - public YieldOp YieldOp => (YieldOp)(Yield & 0x3); + public readonly YieldOp YieldOp => (YieldOp)(Yield & 0x3); // TODO: Eventually move this to per-engine state. public Array31<uint> Reserved84; public uint NoOperation; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs index 09bcdec11..bb5bc21cb 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo private enum CommandBufferType { Prefetch, - NoPrefetch + NoPrefetch, } /// <summary> @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo /// <param name="memoryManager">The memory manager used to fetch the data</param> /// <param name="flush">If true, flushes potential GPU written data before reading the command buffer</param> /// <returns>The fetched data</returns> - private ReadOnlySpan<int> GetWords(MemoryManager memoryManager, bool flush) + private readonly ReadOnlySpan<int> GetWords(MemoryManager memoryManager, bool flush) { return MemoryMarshal.Cast<byte, int>(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, flush)); } @@ -77,7 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo /// <param name="memoryManager">The memory manager used to fetch the data</param> /// <param name="flush">If true, flushes potential GPU written data before reading the command buffer</param> /// <returns>The command buffer words</returns> - public ReadOnlySpan<int> Fetch(MemoryManager memoryManager, bool flush) + public readonly ReadOnlySpan<int> Fetch(MemoryManager memoryManager, bool flush) { return Words ?? GetWords(memoryManager, flush); } @@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo private readonly ConcurrentQueue<CommandBuffer> _commandBufferQueue; - private CommandBuffer _currentCommandBuffer; private GPFifoProcessor _prevChannelProcessor; private readonly bool _ibEnable; @@ -129,7 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo Type = CommandBufferType.Prefetch, Words = commandBuffer, EntryAddress = ulong.MaxValue, - EntryCount = (uint)commandBuffer.Length + EntryCount = (uint)commandBuffer.Length, }); } @@ -156,7 +155,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo Type = type, Words = null, EntryAddress = startAddress, - EntryCount = (uint)entry.Entry1Length + EntryCount = (uint)entry.Entry1Length, }; } @@ -217,7 +216,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo flushCommandBuffer = false; } - _currentCommandBuffer = entry; ReadOnlySpan<int> words = entry.Fetch(entry.Processor.MemoryManager, flushCommandBuffer); // If we are changing the current channel, diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs index 3fb3feeea..6ba1bc22e 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs @@ -243,7 +243,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo { 0 => _3dClass, 3 => _2dClass, - _ => null + _ => null, }; if (state != null) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs index e1d7e9407..e417b9a0a 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory _state = new DeviceState<InlineToMemoryClassState>(new Dictionary<string, RwCallback> { { nameof(InlineToMemoryClassState.LaunchDma), new RwCallback(LaunchDma, null) }, - { nameof(InlineToMemoryClassState.LoadInlineData), new RwCallback(LoadInlineData, null) } + { nameof(InlineToMemoryClassState.LoadInlineData), new RwCallback(LoadInlineData, null) }, }); } } @@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory if (!_finished) { int copySize = Math.Min(data.Length, _buffer.Length - _offset); - data.Slice(0, copySize).CopyTo(new Span<int>(_buffer).Slice(_offset, copySize)); + data[..copySize].CopyTo(new Span<int>(_buffer).Slice(_offset, copySize)); _offset += copySize; @@ -169,11 +169,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory { var memoryManager = _channel.MemoryManager; - var data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size); + var data = MemoryMarshal.Cast<int, byte>(_buffer)[.._size]; if (_isLinear && _lineCount == 1) { - memoryManager.WriteTrackedResource(_dstGpuVa, data.Slice(0, _lineLengthIn)); + memoryManager.WriteTrackedResource(_dstGpuVa, data[.._lineLengthIn]); _context.AdvanceSequence(); } else diff --git a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs index f6d9602a6..ec5042fda 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClassState.cs @@ -1,5 +1,7 @@ // This file was auto-generated from NVIDIA official Maxwell definitions. +using Ryujinx.Common.Memory; + namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory { /// <summary> @@ -111,24 +113,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory /// </summary> unsafe struct InlineToMemoryClassState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint SetObject; - public int SetObjectClassId => (int)(SetObject & 0xFFFF); - public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); + public readonly int SetObjectClassId => (int)(SetObject & 0xFFFF); + public readonly int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); + public readonly int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); public uint SetNotifyB; public uint Notify; - public NotifyType NotifyType => (NotifyType)(Notify); + public readonly NotifyType NotifyType => (NotifyType)(Notify); public uint WaitForIdle; public fixed uint Reserved114[7]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); + public readonly int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); + public readonly int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public uint PmTriggerWfi; @@ -139,34 +141,34 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory public uint LineLengthIn; public uint LineCount; public uint OffsetOutUpper; - public int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); + public readonly int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); public uint OffsetOut; public uint PitchOut; public uint SetDstBlockSize; - public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); - public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); - public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); + public readonly SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); + public readonly SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); + public readonly SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); public uint SetDstWidth; public uint SetDstHeight; public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOriginBytesX; - public int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); + public readonly int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); public uint SetDstOriginSamplesY; - public int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); + public readonly int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); public uint LaunchDma; - public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); - public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); - public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); - public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); - public bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0; - public LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7); - public LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3); - public bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0; + public readonly LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); + public readonly LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); + public readonly LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); + public readonly LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); + public readonly bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0; + public readonly LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7); + public readonly LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3); + public readonly bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0; public uint LoadInlineData; public fixed uint Reserved1B8[9]; public uint SetI2mSemaphoreA; - public int SetI2mSemaphoreAOffsetUpper => (int)(SetI2mSemaphoreA & 0xFF); + public readonly int SetI2mSemaphoreAOffsetUpper => (int)(SetI2mSemaphoreA & 0xFF); public uint SetI2mSemaphoreB; public uint SetI2mSemaphoreC; public fixed uint Reserved1E8[2]; @@ -175,7 +177,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory public uint SetI2mSpareNoop02; public uint SetI2mSpareNoop03; public fixed uint Reserved200[3200]; - public MmeShadowScratch SetMmeShadowScratch; + public Array256<uint> SetMmeShadowScratch; #pragma warning restore CS0649 } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs index eeef9c67e..43faa788b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/AluOperation.cs @@ -10,6 +10,6 @@ BitfieldReplace = 2, BitfieldExtractLslImm = 3, BitfieldExtractLslReg = 4, - ReadImmediate = 5 + ReadImmediate = 5, } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs index f3e05d38e..c878bd41f 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/AluRegOperation.cs @@ -13,6 +13,6 @@ BitwiseOr = 9, BitwiseAnd = 10, BitwiseAndNot = 11, - BitwiseNotAnd = 12 + BitwiseNotAnd = 12, } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs index dc3360266..592aa5a68 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/AssignmentOperation.cs @@ -12,6 +12,6 @@ MoveAndSend = 4, FetchAndSetMaddr = 5, MoveAndSetMaddrThenFetchAndSend = 6, - MoveAndSetMaddrThenSendHigh = 7 + MoveAndSetMaddrThenSendHigh = 7, } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs index 12a3ac028..65c6aab4a 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME if (_executionEngine == null) { - if (GraphicsConfig.EnableMacroHLE && MacroHLETable.TryGetMacroHLEFunction(code.Slice(Position), context.Capabilities, out _hleFunction)) + if (GraphicsConfig.EnableMacroHLE && MacroHLETable.TryGetMacroHLEFunction(code[Position..], context.Capabilities, out _hleFunction)) { _executionEngine = new MacroHLE(processor, _hleFunction); } @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME if (_executionPending) { _executionPending = false; - _executionEngine?.Execute(code.Slice(Position), state, _argument); + _executionEngine?.Execute(code[Position..], state, _argument); } } @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// </summary> /// <param name="gpuVa">GPU virtual address where the command word is located</param> /// <param name="argument">Argument to be pushed</param> - public void PushArgument(ulong gpuVa, int argument) + public readonly void PushArgument(ulong gpuVa, int argument) { _executionEngine?.Fifo.Enqueue(new FifoWord(gpuVa, argument)); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs index 8630bbc42..a4c4dd106 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs @@ -16,7 +16,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME private const int ColorStructSize = 0x40; private const int ZetaLayerCountOffset = 0x1230; - private const int IndirectDataEntrySize = 0x10; private const int IndirectIndexedDataEntrySize = 0x14; private readonly GPFifoProcessor _processor; @@ -262,10 +261,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME for (int i = 0; i < maxDrawCount; i++) { var count = FetchParam(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment var instanceCount = FetchParam(); var firstIndex = FetchParam(); var firstVertex = FetchParam(); var firstInstance = FetchParam(); +#pragma warning restore IDE0059 if (i == 0) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs index 751867fc7..9e71761b4 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs @@ -11,6 +11,6 @@ DrawArraysInstanced, DrawElementsInstanced, DrawElementsIndirect, - MultiDrawElementsIndirectCount + MultiDrawElementsIndirectCount, } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs index 719e170fd..5630756cb 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs @@ -46,12 +46,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME private static readonly TableEntry[] _table = new TableEntry[] { - new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28), - new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24), - new TableEntry(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48), - new TableEntry(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c), - new TableEntry(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c), - new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C) + new(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28), + new(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24), + new(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48), + new(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c), + new(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c), + new(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C), }; /// <summary> @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME { ref var entry = ref _table[i]; - var hash = XXHash128.ComputeHash(mc.Slice(0, entry.Length)); + var hash = XXHash128.ComputeHash(mc[..entry.Length]); if (hash == entry.Hash) { if (IsMacroHLESupported(caps, entry.Name)) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs index df6ee040e..dd60688d6 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// </summary> public Queue<FifoWord> Fifo { get; } - private int[] _gprs; + private readonly int[] _gprs; private int _methAddr; private int _methIncr; @@ -291,11 +291,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME return (int)result; - case AluRegOperation.BitwiseExclusiveOr: return a ^ b; - case AluRegOperation.BitwiseOr: return a | b; - case AluRegOperation.BitwiseAnd: return a & b; - case AluRegOperation.BitwiseAndNot: return a & ~b; - case AluRegOperation.BitwiseNotAnd: return ~(a & b); + case AluRegOperation.BitwiseExclusiveOr: + return a ^ b; + case AluRegOperation.BitwiseOr: + return a | b; + case AluRegOperation.BitwiseAnd: + return a & b; + case AluRegOperation.BitwiseAndNot: + return a & ~b; + case AluRegOperation.BitwiseNotAnd: + return ~(a & b); } throw new InvalidOperationException($"Invalid operation \"{aluOp}\" on instruction 0x{_opCode:X8}."); @@ -380,7 +385,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <param name="state">Current GPU state</param> /// <param name="reg">Register offset to read</param> /// <returns>GPU register value</returns> - private int Read(IDeviceState state, int reg) + private static int Read(IDeviceState state, int reg) { return state.Read(reg * 4); } @@ -397,4 +402,4 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME _methAddr += _methIncr; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs index 4077f74ec..a23bfef12 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// </summary> class MacroJit : IMacroEE { - private readonly MacroJitContext _context = new MacroJitContext(); + private readonly MacroJitContext _context = new(); /// <summary> /// Arguments FIFO. @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME { if (_execute == null) { - MacroJitCompiler compiler = new MacroJitCompiler(); + MacroJitCompiler compiler = new(); _execute = compiler.Compile(code); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs index f8d839fa0..d9c26c587 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <returns>Delegate of the host compiled code</returns> public MacroExecute Compile(ReadOnlySpan<int> code) { - Dictionary<int, Label> labels = new Dictionary<int, Label>(); + Dictionary<int, Label> labels = new(); int lastTarget = 0; int i; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs index 52c2a11b2..5a71e3f47 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <summary> /// Arguments FIFO. /// </summary> - public Queue<FifoWord> Fifo { get; } = new Queue<FifoWord>(); + public Queue<FifoWord> Fifo { get; } = new(); /// <summary> /// Fetches a arguments from the arguments FIFO. diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs b/src/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs index 44cd82130..0aa6b82af 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Engine [StructLayout(LayoutKind.Sequential, Size = 1024)] struct MmeShadowScratch { -#pragma warning disable CS0169 +#pragma warning disable CS0169 // The private field is never used private uint _e0; #pragma warning restore CS0169 public ref uint this[int index] => ref AsSpan()[index]; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs b/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs index 060d35cad..ebb0ff33e 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs @@ -10,4 +10,4 @@ namespace Ryujinx.Graphics.Gpu.Engine MethodPassthrough = 2, MethodReplay = 3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs index e1e3085b9..40d9a97df 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/ShaderTexture.cs @@ -65,6 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { return format switch { +#pragma warning disable IDE0055 // Disable formatting TextureFormat.R8Unorm => Format.R8Unorm, TextureFormat.R8Snorm => Format.R8Snorm, TextureFormat.R8Uint => Format.R8Uint, @@ -104,7 +105,8 @@ namespace Ryujinx.Graphics.Gpu.Engine TextureFormat.R10G10B10A2Unorm => Format.R10G10B10A2Unorm, TextureFormat.R10G10B10A2Uint => Format.R10G10B10A2Uint, TextureFormat.R11G11B10Float => Format.R11G11B10Float, - _ => 0 + _ => 0, +#pragma warning restore IDE0055 }; } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs index a40b9cc47..0aca39075 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs @@ -209,14 +209,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender new AdvancedBlendUcode(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, false, GenConjointHslHue), new AdvancedBlendUcode(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, false, GenConjointHslSaturation), new AdvancedBlendUcode(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, false, GenConjointHslColor), - new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, false, GenConjointHslLuminosity) + new AdvancedBlendUcode(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, false, GenConjointHslLuminosity), }; public static string GenTable() { // This can be used to generate the table on AdvancedBlendPreGenTable. - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); sb.AppendLine($"private static Dictionary<Hash128, AdvancedBlendEntry> _entries = new()"); sb.AppendLine("{"); @@ -4223,4 +4223,4 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender return new FixedFunctionAlpha(BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs index 8072c6af2..b336382d4 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs @@ -54,12 +54,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender /// <returns>True if the function was found, false otherwise</returns> public bool TryGetAdvancedBlend(out AdvancedBlendDescriptor descriptor) { - Span<uint> currentCode = new Span<uint>(_code); + Span<uint> currentCode = new(_code); byte codeLength = (byte)_state.State.BlendUcodeSize; if (currentCode.Length > codeLength) { - currentCode = currentCode.Slice(0, codeLength); + currentCode = currentCode[..codeLength]; } Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast<uint, byte>(currentCode)); @@ -112,4 +112,4 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs index d35d8abf4..b6cd9def9 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendPreGenTable.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender /// <summary> /// Advanced blend function entry. /// </summary> - struct AdvancedBlendEntry + readonly struct AdvancedBlendEntry { /// <summary> /// Advanced blend operation. @@ -270,4 +270,4 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender { new Hash128(0x8652300E32D93050, 0x9460E7B449132371), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) }, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs index f06b4bf74..d3e3abbc1 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs @@ -5,12 +5,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender /// <summary> /// Fixed function alpha state used for a advanced blend function. /// </summary> - struct FixedFunctionAlpha + readonly struct FixedFunctionAlpha { /// <summary> /// Fixed function alpha state with alpha blending disabled. /// </summary> - public static FixedFunctionAlpha Disabled => new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, default, default, default); + public static FixedFunctionAlpha Disabled => new(BlendUcodeEnable.EnableRGBA, default, default, default); /// <summary> /// Individual enable bits for the RGB and alpha components. @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender /// <summary> /// Advanced blend microcode state. /// </summary> - struct AdvancedBlendUcode + readonly struct AdvancedBlendUcode { /// <summary> /// Advanced blend operation. @@ -117,10 +117,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender Overlap = overlap; SrcPreMultiplied = srcPreMultiplied; - UcodeAssembler asm = new UcodeAssembler(); + UcodeAssembler asm = new(); Alpha = genFunc(ref asm); Code = asm.GetCode(); Constants = asm.GetConstants(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs index f854787e0..8e0209062 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender Max = 3, Rcp = 4, Add = 5, - Sub = 6 + Sub = 6, } /// <summary> @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender LT = 4, LE = 5, GT = 6, - GE = 7 + GE = 7, } /// <summary> @@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender Temp1 = 0xa, Temp2 = 0xb, PBR = 0xc, - ConstantRGB = 0xd + ConstantRGB = 0xd, } /// <summary> @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender Temp0 = 4, Temp1 = 5, Temp2 = 6, - PBR = 7 + PBR = 7, } /// <summary> @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender Temp0 = 0, Temp1 = 1, Temp2 = 2, - PBR = 3 + PBR = 3, } /// <summary> @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender RRR = 2, GGG = 3, BBB = 4, - RToA = 5 + RToA = 5, } /// <summary> @@ -99,13 +99,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender RGB = 0, R = 1, G = 2, - B = 3 + B = 3, } /// <summary> /// Floating-point RGB color values. /// </summary> - struct RgbFloat + readonly struct RgbFloat { /// <summary> /// Red component value. @@ -139,24 +139,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender /// <summary> /// Blend microcode destination operand, including swizzle, write mask and condition code update flag. /// </summary> - struct Dest + readonly struct Dest { - public static Dest Temp0 => new Dest(OpDst.Temp0, Swizzle.RGB, WriteMask.RGB, false); - public static Dest Temp1 => new Dest(OpDst.Temp1, Swizzle.RGB, WriteMask.RGB, false); - public static Dest Temp2 => new Dest(OpDst.Temp2, Swizzle.RGB, WriteMask.RGB, false); - public static Dest PBR => new Dest(OpDst.PBR, Swizzle.RGB, WriteMask.RGB, false); + public static Dest Temp0 => new(OpDst.Temp0, Swizzle.RGB, WriteMask.RGB, false); + public static Dest Temp1 => new(OpDst.Temp1, Swizzle.RGB, WriteMask.RGB, false); + public static Dest Temp2 => new(OpDst.Temp2, Swizzle.RGB, WriteMask.RGB, false); + public static Dest PBR => new(OpDst.PBR, Swizzle.RGB, WriteMask.RGB, false); - public Dest GBR => new Dest(Dst, Swizzle.GBR, WriteMask, WriteCC); - public Dest RRR => new Dest(Dst, Swizzle.RRR, WriteMask, WriteCC); - public Dest GGG => new Dest(Dst, Swizzle.GGG, WriteMask, WriteCC); - public Dest BBB => new Dest(Dst, Swizzle.BBB, WriteMask, WriteCC); - public Dest RToA => new Dest(Dst, Swizzle.RToA, WriteMask, WriteCC); + public Dest GBR => new(Dst, Swizzle.GBR, WriteMask, WriteCC); + public Dest RRR => new(Dst, Swizzle.RRR, WriteMask, WriteCC); + public Dest GGG => new(Dst, Swizzle.GGG, WriteMask, WriteCC); + public Dest BBB => new(Dst, Swizzle.BBB, WriteMask, WriteCC); + public Dest RToA => new(Dst, Swizzle.RToA, WriteMask, WriteCC); - public Dest R => new Dest(Dst, Swizzle, WriteMask.R, WriteCC); - public Dest G => new Dest(Dst, Swizzle, WriteMask.G, WriteCC); - public Dest B => new Dest(Dst, Swizzle, WriteMask.B, WriteCC); + public Dest R => new(Dst, Swizzle, WriteMask.R, WriteCC); + public Dest G => new(Dst, Swizzle, WriteMask.G, WriteCC); + public Dest B => new(Dst, Swizzle, WriteMask.B, WriteCC); - public Dest CC => new Dest(Dst, Swizzle, WriteMask, true); + public Dest CC => new(Dst, Swizzle, WriteMask, true); public OpDst Dst { get; } public Swizzle Swizzle { get; } @@ -182,7 +182,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender /// <summary> /// Blend microcode operaiton. /// </summary> - struct UcodeOp + readonly struct UcodeOp { public readonly uint Word; @@ -292,14 +292,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender _constantIndex = index; } - public uint[] GetCode() + public readonly uint[] GetCode() { return _code?.ToArray(); } - public RgbFloat[] GetConstants() + public readonly RgbFloat[] GetConstants() { return _constants; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs index 5c9366160..e30092cee 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private ulong _ubFollowUpAddress = 0; private ulong _ubByteCount = 0; private int _ubIndex = 0; - private int[] _ubData = new int[UniformDataCacheSize]; + private readonly int[] _ubData = new int[UniformDataCacheSize]; /// <summary> /// Creates a new instance of the constant buffer updater. diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 9c4921c8b..d7ee24b19 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -186,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { int firstVertex = (int)_state.State.FirstVertex; - BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); + BufferRange br = new(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); @@ -200,7 +200,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } else { +#pragma warning disable IDE0059 // Remove unnecessary value assignment var drawState = _state.State.VertexBufferDrawState; +#pragma warning restore IDE0059 _context.Renderer.Pipeline.Draw(drawVertexCount, 1, drawFirstVertex, firstInstance); } @@ -679,7 +681,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (indexedInline) { int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer); - BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); + BufferRange br = new(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); } @@ -809,7 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[] { - new Rectangle<int>(scissorX, scissorY, scissorW, scissorH) + new Rectangle<int>(scissorX, scissorY, scissorW, scissorH), }; _context.Renderer.Pipeline.SetScissors(scissors); @@ -821,7 +823,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { var clearColor = _state.State.ClearColors; - ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha); + ColorF color = new(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha); _context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, layerCount, componentMask, color); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs index 42ec2442e..12099aef9 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs @@ -60,6 +60,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// <summary> /// Index buffer data streamer for inline index buffer updates, such as those used in legacy OpenGL. /// </summary> - public IbStreamer IbStreamer = new IbStreamer(); + public IbStreamer IbStreamer = new(); } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs index 80d8c00b9..022e12f57 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs @@ -1,6 +1,5 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; -using System; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Engine.Threed @@ -17,33 +16,35 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private int _inlineIndexBufferSize; private int _inlineIndexCount; private uint[] _buffer; - private int _bufferOffset; +#pragma warning disable IDE0051 // Remove unused private member + private readonly int _bufferOffset; +#pragma warning restore IDE0051 /// <summary> /// Indicates if any index buffer data has been pushed. /// </summary> - public bool HasInlineIndexData => _inlineIndexCount != 0; + public readonly bool HasInlineIndexData => _inlineIndexCount != 0; /// <summary> /// Total numbers of indices that have been pushed. /// </summary> - public int InlineIndexCount => _inlineIndexCount; + public readonly int InlineIndexCount => _inlineIndexCount; /// <summary> /// Gets the handle for the host buffer currently holding the inline index buffer data. /// </summary> /// <returns>Host buffer handle</returns> - public BufferHandle GetInlineIndexBuffer() + public readonly BufferHandle GetInlineIndexBuffer() { return _inlineIndexBuffer; } /// <summary> /// Gets the number of elements on the current inline index buffer, - /// while also reseting it to zero for the next draw. + /// while also resetting it to zero for the next draw. /// </summary> /// <param name="renderer">Host renderer</param> - /// <returns>Inline index bufffer count</returns> + /// <returns>Inline index buffer count</returns> public int GetAndResetInlineIndexCount(IRenderer renderer) { UpdateRemaining(renderer); @@ -114,10 +115,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// <param name="value">Index value to be written</param> private void PushData(IRenderer renderer, int offset, uint value) { - if (_buffer == null) - { - _buffer = new uint[BufferCapacity]; - } + _buffer ??= new uint[BufferCapacity]; // We upload data in chunks. // If we are at the start of a chunk, then the buffer might be full, @@ -155,7 +153,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int baseOffset = (offset - count) * sizeof(uint); int length = count * sizeof(uint); BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, length); - renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer).Slice(0, length)); + renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer)[..length]); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs index d78aa4982..331b1976b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs @@ -1,8 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + namespace Ryujinx.Graphics.Gpu.Engine.Threed { /// <summary> /// Indirect draw type, which can be indexed or non-indexed, with or without a draw count. /// </summary> + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum IndirectDrawType { /// <summary> @@ -33,6 +36,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// <summary> /// Draw count flag. /// </summary> - Count = 1 << 1 + Count = 1 << 1, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs index cf2e818ce..e575923d9 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs @@ -36,6 +36,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// <summary> /// Default update flags for draw. /// </summary> - UpdateAll = UseControl | UpdateDepthStencil + UpdateAll = UseControl | UpdateDepthStencil, } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs index 63a2c841c..0a813ee55 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs @@ -1,5 +1,4 @@ using Ryujinx.Graphics.GAL; -using System; namespace Ryujinx.Graphics.Gpu.Engine.Threed { @@ -15,7 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { Release = 0, Acquire = 1, - Counter = 2 + Counter = 2, } /// <summary> @@ -37,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ClipperInputPrimitives = 0x1c, ClipperOutputPrimitives = 0x1d, FragmentShaderInvocations = 0x1e, - PrimitivesGenerated = 0x1f + PrimitivesGenerated = 0x1f, } /// <summary> @@ -64,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed TransformFeedbackOffset = 0x1a, TessControlShaderInvocations = 0x1b, TessEvaluationShaderInvocations = 0x1d, - TessEvaluationShaderPrimitives = 0x1f + TessEvaluationShaderPrimitives = 0x1f, } private readonly GpuContext _context; @@ -117,8 +116,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed switch (op) { - case SemaphoreOperation.Release: ReleaseSemaphore(); break; - case SemaphoreOperation.Counter: ReportCounter(type); break; + case SemaphoreOperation.Release: + ReleaseSemaphore(); + break; + case SemaphoreOperation.Counter: + ReportCounter(type); + break; } } @@ -156,10 +159,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed void resultHandler(object evt, ulong result) { - CounterData counterData = new CounterData + CounterData counterData = new() { Counter = result, - Timestamp = ticks + Timestamp = ticks, }; if (counter?.Invalid != true) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs index a8af54970..cbf1573cd 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -227,7 +227,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { VertexAttribType.Sint => AttributeType.Sint, VertexAttribType.Uint => AttributeType.Uint, - _ => AttributeType.Float + _ => AttributeType.Float, }; if (attributeTypes[location] != value) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 92e980ced..34439657e 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private readonly ShaderProgramInfo[] _currentProgramInfo; private ShaderSpecializationState _shaderSpecState; - private SpecializationStateUpdater _currentSpecState; + private readonly SpecializationStateUpdater _currentSpecState; private ProgramPipelineState _pipeline; @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private uint _vbEnableMask; private bool _prevDrawIndexed; - private bool _prevDrawIndirect; + private readonly bool _prevDrawIndirect; private IndexType _prevIndexType; private uint _prevFirstVertex; private bool _prevTfEnable; @@ -448,7 +448,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int samplesInY = msaaMode.SamplesInY(); var scissor = _state.State.ScreenScissorState; - Size sizeHint = new Size((scissor.X + scissor.Width) * samplesInX, (scissor.Y + scissor.Height) * samplesInY, 1); + Size sizeHint = new((scissor.X + scissor.Width) * samplesInX, (scissor.Y + scissor.Height) * samplesInY, 1); int clipRegionWidth = int.MaxValue; int clipRegionHeight = int.MaxValue; @@ -669,7 +669,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> private void UpdateDepthTestState() { - DepthTestDescriptor descriptor = new DepthTestDescriptor( + DepthTestDescriptor descriptor = new( _state.State.DepthTestEnable, _state.State.DepthWriteEnable, _state.State.DepthTestFunc); @@ -739,7 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed height *= scale; } - Rectangle<float> region = new Rectangle<float>(x, y, width, height); + Rectangle<float> region = new(x, y, width, height); ViewportSwizzle swizzleX = transform.UnpackSwizzleX(); ViewportSwizzle swizzleY = transform.UnpackSwizzleY(); @@ -751,9 +751,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (transform.ScaleZ < 0) { - float temp = depthNear; - depthNear = depthFar; - depthFar = temp; + (depthFar, depthNear) = (depthNear, depthFar); } viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar); @@ -845,7 +843,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed backMask = test.FrontMask; } - StencilTestDescriptor descriptor = new StencilTestDescriptor( + StencilTestDescriptor descriptor = new( test.Enable, test.FrontFunc, test.FrontSFail, @@ -939,7 +937,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { VertexAttribType.Sint => Format.R32G32B32A32Sint, VertexAttribType.Uint => Format.R32G32B32A32Uint, - _ => Format.R32G32B32A32Float + _ => Format.R32G32B32A32Float, }; } @@ -1017,8 +1015,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed switch (indexBuffer.Type) { - case IndexType.UShort: size *= 2; break; - case IndexType.UInt: size *= 4; break; + case IndexType.UShort: + size *= 2; + break; + case IndexType.UInt: + size *= 4; + break; } _channel.BufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type); @@ -1338,7 +1340,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _vtgWritesRtLayer = false; - ShaderAddresses addresses = new ShaderAddresses(); + ShaderAddresses addresses = new(); Span<ulong> addressesSpan = addresses.AsSpan(); ulong baseAddress = _state.State.ShaderBaseAddress.Pack(); @@ -1453,7 +1455,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed // ScaleZ = (Far - Near) / 2 // DepthNear/Far are sorted such as that Near is always less than Far. depthMode = extents.DepthNear != transform.TranslateZ && - extents.DepthFar != transform.TranslateZ + extents.DepthFar != transform.TranslateZ ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne; } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 1386071cf..1f6628909 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { nameof(ThreedClassState.UniformBufferBindTessControl), new RwCallback(ConstantBufferBindTessControl, null) }, { nameof(ThreedClassState.UniformBufferBindTessEvaluation), new RwCallback(ConstantBufferBindTessEvaluation, null) }, { nameof(ThreedClassState.UniformBufferBindGeometry), new RwCallback(ConstantBufferBindGeometry, null) }, - { nameof(ThreedClassState.UniformBufferBindFragment), new RwCallback(ConstantBufferBindFragment, null) } + { nameof(ThreedClassState.UniformBufferBindFragment), new RwCallback(ConstantBufferBindFragment, null) }, }); _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index beda2dbfe..f2997678c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed TessellationControl, TessellationEvaluation, Geometry, - Fragment + Fragment, } /// <summary> @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct TessMode { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Packed; #pragma warning restore CS0649 @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the tessellation abstract patch type. /// </summary> /// <returns>Abtract patch type</returns> - public TessPatchType UnpackPatchType() + public readonly TessPatchType UnpackPatchType() { return (TessPatchType)(Packed & 3); } @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the spacing between tessellated vertices of the patch. /// </summary> /// <returns>Spacing between tessellated vertices</returns> - public TessSpacing UnpackSpacing() + public readonly TessSpacing UnpackSpacing() { return (TessSpacing)((Packed >> 4) & 3); } @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the primitive winding order. /// </summary> /// <returns>True if clockwise, false if counter-clockwise</returns> - public bool UnpackCw() + public readonly bool UnpackCw() { return (Packed & (1 << 8)) != 0; } @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct TfBufferState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 Enable; public GpuVa Address; public int Size; @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct TfState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public int BufferIndex; public int VaryingsCount; public int Stride; @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct RtColorState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public GpuVa Address; public int WidthOrStride; public int Height; @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct ViewportTransform { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public float ScaleX; public float ScaleY; public float ScaleZ; @@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks viewport swizzle of the position X component. /// </summary> /// <returns>Swizzle enum value</returns> - public ViewportSwizzle UnpackSwizzleX() + public readonly ViewportSwizzle UnpackSwizzleX() { return (ViewportSwizzle)(Swizzle & 7); } @@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks viewport swizzle of the position Y component. /// </summary> /// <returns>Swizzle enum value</returns> - public ViewportSwizzle UnpackSwizzleY() + public readonly ViewportSwizzle UnpackSwizzleY() { return (ViewportSwizzle)((Swizzle >> 4) & 7); } @@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks viewport swizzle of the position Z component. /// </summary> /// <returns>Swizzle enum value</returns> - public ViewportSwizzle UnpackSwizzleZ() + public readonly ViewportSwizzle UnpackSwizzleZ() { return (ViewportSwizzle)((Swizzle >> 8) & 7); } @@ -158,7 +158,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks viewport swizzle of the position W component. /// </summary> /// <returns>Swizzle enum value</returns> - public ViewportSwizzle UnpackSwizzleW() + public readonly ViewportSwizzle UnpackSwizzleW() { return (ViewportSwizzle)((Swizzle >> 12) & 7); } @@ -169,7 +169,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct ViewportExtents { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public ushort X; public ushort Width; public ushort Y; @@ -184,7 +184,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct VertexBufferDrawState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public int First; public int Count; #pragma warning restore CS0649 @@ -195,7 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct ClearColors { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public float Red; public float Green; public float Blue; @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct DepthBiasState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 PointEnable; public Boolean32 LineEnable; public Boolean32 FillEnable; @@ -223,7 +223,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Disabled = 0, EnableRGB = 1, EnableAlpha = 2, - EnableRGBA = 3 + EnableRGBA = 3, } /// <summary> @@ -231,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct ScissorState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 Enable; public ushort X1; public ushort X2; @@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct StencilBackMasks { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public int FuncRef; public int Mask; public int FuncMask; @@ -258,7 +258,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct RtDepthStencilState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public GpuVa Address; public ZetaFormat Format; public MemoryLayout MemoryLayout; @@ -271,7 +271,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct ScreenScissorState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public ushort X; public ushort Width; public ushort Y; @@ -297,7 +297,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Size16 = 0x1b, Size8 = 0x1d, Rgb10A2 = 0x30, - Rg11B10 = 0x31 + Rg11B10 = 0x31, } /// <summary> @@ -311,7 +311,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Uint = 4, Uscaled = 5, Sscaled = 6, - Float = 7 + Float = 7, } /// <summary> @@ -319,7 +319,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct VertexAttribState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Attribute; #pragma warning restore CS0649 @@ -327,7 +327,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the index of the vertex buffer this attribute belongs to. /// </summary> /// <returns>Vertex buffer index</returns> - public int UnpackBufferIndex() + public readonly int UnpackBufferIndex() { return (int)(Attribute & 0x1f); } @@ -336,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the attribute constant flag. /// </summary> /// <returns>True if the attribute is constant, false otherwise</returns> - public bool UnpackIsConstant() + public readonly bool UnpackIsConstant() { return (Attribute & 0x40) != 0; } @@ -345,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the offset, in bytes, of the attribute on the vertex buffer. /// </summary> /// <returns>Attribute offset in bytes</returns> - public int UnpackOffset() + public readonly int UnpackOffset() { return (int)((Attribute >> 7) & 0x3fff); } @@ -354,7 +354,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the Maxwell attribute format integer. /// </summary> /// <returns>Attribute format integer</returns> - public uint UnpackFormat() + public readonly uint UnpackFormat() { return Attribute & 0x3fe00000; } @@ -363,7 +363,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the Maxwell attribute size. /// </summary> /// <returns>Attribute size</returns> - public VertexAttribSize UnpackSize() + public readonly VertexAttribSize UnpackSize() { return (VertexAttribSize)((Attribute >> 21) & 0x3f); } @@ -372,7 +372,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the Maxwell attribute component type. /// </summary> /// <returns>Attribute component type</returns> - public VertexAttribType UnpackType() + public readonly VertexAttribType UnpackType() { return (VertexAttribType)((Attribute >> 27) & 7); } @@ -383,7 +383,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct RtControl { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Packed; #pragma warning restore CS0649 @@ -391,7 +391,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the number of active draw buffers. /// </summary> /// <returns>Number of active draw buffers</returns> - public int UnpackCount() + public readonly int UnpackCount() { return (int)(Packed & 0xf); } @@ -401,7 +401,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> /// <param name="index">Index of the draw buffer</param> /// <returns>Attachment index</returns> - public int UnpackPermutationIndex(int index) + public readonly int UnpackPermutationIndex(int index) { return (int)((Packed >> (4 + index * 3)) & 7); } @@ -412,7 +412,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct Size3D { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public int Width; public int Height; public int Depth; @@ -424,7 +424,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct StencilTestState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 Enable; public StencilOp FrontSFail; public StencilOp FrontDpFail; @@ -443,7 +443,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed enum YControl { NegateY = 1 << 0, - TriangleRastFlip = 1 << 4 + TriangleRastFlip = 1 << 4, } /// <summary> @@ -451,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct RgbHalf { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint R; public uint G; public uint B; @@ -462,7 +462,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the red color component as a 16-bit float value. /// </summary> /// <returns>The component value</returns> - public Half UnpackR() + public readonly Half UnpackR() { ushort value = (ushort)R; return Unsafe.As<ushort, Half>(ref value); @@ -472,7 +472,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the green color component as a 16-bit float value. /// </summary> /// <returns>The component value</returns> - public Half UnpackG() + public readonly Half UnpackG() { ushort value = (ushort)G; return Unsafe.As<ushort, Half>(ref value); @@ -482,7 +482,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks the blue color component as a 16-bit float value. /// </summary> /// <returns>The component value</returns> - public Half UnpackB() + public readonly Half UnpackB() { ushort value = (ushort)B; return Unsafe.As<ushort, Half>(ref value); @@ -498,7 +498,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Always, ResultNonZero, Equal, - NotEqual + NotEqual, } /// <summary> @@ -506,7 +506,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct PoolState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public GpuVa Address; public int MaximumId; #pragma warning restore CS0649 @@ -517,7 +517,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct StencilBackTestState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 TwoSided; public StencilOp BackSFail; public StencilOp BackDpFail; @@ -531,7 +531,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct PrimitiveRestartState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 Enable; public int Index; #pragma warning restore CS0649 @@ -543,7 +543,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct IndexBufferState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public GpuVa Address; public GpuVa EndAddress; public IndexType Type; @@ -556,7 +556,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct FaceState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 CullEnable; public FrontFace FrontFace; public Face CullFace; @@ -570,7 +570,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed enum ViewVolumeClipControl { ForceDepthRangeZeroToOne = 1 << 0, - DepthClampDisabled = 1 << 11 + DepthClampDisabled = 1 << 11, } /// <summary> @@ -578,7 +578,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct LogicalOpState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 Enable; public LogicalOp LogicalOp; #pragma warning restore CS0649 @@ -590,7 +590,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct RtColorMask { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Packed; #pragma warning restore CS0649 @@ -598,7 +598,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks red channel enable. /// </summary> /// <returns>True to write the new red channel color, false to keep the old value</returns> - public bool UnpackRed() + public readonly bool UnpackRed() { return (Packed & 0x1) != 0; } @@ -607,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks green channel enable. /// </summary> /// <returns>True to write the new green channel color, false to keep the old value</returns> - public bool UnpackGreen() + public readonly bool UnpackGreen() { return (Packed & 0x10) != 0; } @@ -616,7 +616,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks blue channel enable. /// </summary> /// <returns>True to write the new blue channel color, false to keep the old value</returns> - public bool UnpackBlue() + public readonly bool UnpackBlue() { return (Packed & 0x100) != 0; } @@ -625,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Unpacks alpha channel enable. /// </summary> /// <returns>True to write the new alpha channel color, false to keep the old value</returns> - public bool UnpackAlpha() + public readonly bool UnpackAlpha() { return (Packed & 0x1000) != 0; } @@ -636,7 +636,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct VertexBufferState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Control; public GpuVa Address; public int Divisor; @@ -646,7 +646,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Vertex buffer stride, defined as the number of bytes occupied by each vertex in memory. /// </summary> /// <returns>Vertex buffer stride</returns> - public int UnpackStride() + public readonly int UnpackStride() { return (int)(Control & 0xfff); } @@ -655,7 +655,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Vertex buffer enable. /// </summary> /// <returns>True if the vertex buffer is enabled, false otherwise</returns> - public bool UnpackEnable() + public readonly bool UnpackEnable() { return (Control & (1 << 12)) != 0; } @@ -666,7 +666,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct BlendStateCommon { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 SeparateAlpha; public BlendOp ColorOp; public BlendFactor ColorSrcFactor; @@ -683,7 +683,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct BlendState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public Boolean32 SeparateAlpha; public BlendOp ColorOp; public BlendFactor ColorSrcFactor; @@ -700,7 +700,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct ShaderState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Control; public uint Offset; public uint Unknown0x8; @@ -724,7 +724,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Must be ignored for vertex shaders, those are always enabled. /// </summary> /// <returns>True if the stage is enabled, false otherwise</returns> - public bool UnpackEnable() + public readonly bool UnpackEnable() { return (Control & 1) != 0; } @@ -735,7 +735,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> struct UniformBufferState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public int Size; public GpuVa Address; public int Offset; @@ -744,30 +744,30 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed unsafe struct ThreedClassState : IShadowState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint SetObject; - public int SetObjectClassId => (int)(SetObject & 0xFFFF); - public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); + public readonly int SetObjectClassId => (int)(SetObject & 0xFFFF); + public readonly int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); + public readonly int SetNotifyAAddressUpper => (int)(SetNotifyA & 0xFF); public uint SetNotifyB; public uint Notify; - public NotifyType NotifyType => (NotifyType)(Notify); + public readonly NotifyType NotifyType => (NotifyType)(Notify); public uint WaitForIdle; public uint LoadMmeInstructionRamPointer; public uint LoadMmeInstructionRam; public uint LoadMmeStartAddressRamPointer; public uint LoadMmeStartAddressRam; public uint SetMmeShadowRamControl; - public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)(SetMmeShadowRamControl & 0x3); + public readonly SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)(SetMmeShadowRamControl & 0x3); public fixed uint Reserved128[2]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); + public readonly int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); + public readonly int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public uint PmTriggerWfi; @@ -778,30 +778,30 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public uint LineLengthIn; public uint LineCount; public uint OffsetOutUpper; - public int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); + public readonly int OffsetOutUpperValue => (int)(OffsetOutUpper & 0xFF); public uint OffsetOut; public uint PitchOut; public uint SetDstBlockSize; - public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); - public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); - public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); + public readonly SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)(SetDstBlockSize & 0xF); + public readonly SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF); + public readonly SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF); public uint SetDstWidth; public uint SetDstHeight; public uint SetDstDepth; public uint SetDstLayer; public uint SetDstOriginBytesX; - public int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); + public readonly int SetDstOriginBytesXV => (int)(SetDstOriginBytesX & 0xFFFFF); public uint SetDstOriginSamplesY; - public int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); + public readonly int SetDstOriginSamplesYV => (int)(SetDstOriginSamplesY & 0xFFFF); public uint LaunchDma; - public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); - public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); - public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); - public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); - public bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0; - public LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7); - public LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3); - public bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0; + public readonly LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)(LaunchDma & 0x1); + public readonly LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3); + public readonly LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3); + public readonly LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1); + public readonly bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0; + public readonly LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7); + public readonly LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3); + public readonly bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0; public uint LoadInlineData; public fixed uint Reserved1B8[22]; public Boolean32 EarlyZForce; @@ -1042,7 +1042,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public fixed uint Reserved260C[125]; public Array4<Array32<uint>> TfVaryingLocations; public fixed uint Reserved2A00[640]; - public MmeShadowScratch SetMmeShadowScratch; + public Array256<uint> SetMmeShadowScratch; #pragma warning restore CS0649 } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs index 2ac84ab50..b33fb7f73 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod _channel = channel; _state = new DeviceState<TwodClassState>(new Dictionary<string, RwCallback> { - { nameof(TwodClassState.PixelsFromMemorySrcY0Int), new RwCallback(PixelsFromMemorySrcY0Int, null) } + { nameof(TwodClassState.PixelsFromMemorySrcY0Int), new RwCallback(PixelsFromMemorySrcY0Int, null) }, }); } @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod /// <param name="lhsFormat">Format of the first texture</param> /// <param name="rhsFormat">Format of the second texture</param> /// <returns>True if the data is compatible, false otherwise</returns> - private bool IsDataCompatible(TwodTexture lhs, TwodTexture rhs, FormatInfo lhsFormat, FormatInfo rhsFormat) + private static bool IsDataCompatible(TwodTexture lhs, TwodTexture rhs, FormatInfo lhsFormat, FormatInfo rhsFormat) { if (lhsFormat.BytesPerPixel != rhsFormat.BytesPerPixel || lhs.Height != rhs.Height || @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod /// <param name="x2">Region end x</param> /// <param name="y2">Region end y</param> /// <returns>True if the region covers the full texture, false otherwise</returns> - private bool IsCopyRegionComplete(TwodTexture texture, FormatInfo formatInfo, int x1, int y1, int x2, int y2) + private static bool IsCopyRegionComplete(TwodTexture texture, FormatInfo formatInfo, int x1, int y1, int x2, int y2) { if (x1 != 0 || y1 != 0 || y2 != texture.Height) { @@ -172,7 +172,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod for (int y = 0; y < height; y++) { - srcSpan.Slice(offset, lineSize).CopyTo(dstSpan.Slice(offset)); + srcSpan.Slice(offset, lineSize).CopyTo(dstSpan[offset..]); offset += stride; } @@ -364,13 +364,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod float scale = srcTexture.ScaleFactor; float dstScale = dstTexture.ScaleFactor; - Extents2D srcRegion = new Extents2D( + Extents2D srcRegion = new( (int)Math.Ceiling(scale * (srcX1 / srcTexture.Info.SamplesInX)), (int)Math.Ceiling(scale * (srcY1 / srcTexture.Info.SamplesInY)), (int)Math.Ceiling(scale * (srcX2 / srcTexture.Info.SamplesInX)), (int)Math.Ceiling(scale * (srcY2 / srcTexture.Info.SamplesInY))); - Extents2D dstRegion = new Extents2D( + Extents2D dstRegion = new( (int)Math.Ceiling(dstScale * (dstX1 / dstTexture.Info.SamplesInX)), (int)Math.Ceiling(dstScale * (dstY1 / dstTexture.Info.SamplesInY)), (int)Math.Ceiling(dstScale * (dstX2 / dstTexture.Info.SamplesInX)), diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs index 55e5019fc..edea73273 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs @@ -486,7 +486,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod /// </summary> struct RenderSolidPrimPoint { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint SetX; public uint Y; #pragma warning restore CS0649 @@ -497,30 +497,30 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod /// </summary> unsafe struct TwodClassState : IShadowState { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint SetObject; - public int SetObjectClassId => (int)(SetObject & 0xFFFF); - public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); + public readonly int SetObjectClassId => (int)(SetObject & 0xFFFF); + public readonly int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F); public fixed uint Reserved04[63]; public uint NoOperation; public uint SetNotifyA; - public int SetNotifyAAddressUpper => (int)(SetNotifyA & 0x1FFFFFF); + public readonly int SetNotifyAAddressUpper => (int)(SetNotifyA & 0x1FFFFFF); public uint SetNotifyB; public uint Notify; - public NotifyType NotifyType => (NotifyType)(Notify); + public readonly NotifyType NotifyType => (NotifyType)(Notify); public uint WaitForIdle; public uint LoadMmeInstructionRamPointer; public uint LoadMmeInstructionRam; public uint LoadMmeStartAddressRamPointer; public uint LoadMmeStartAddressRam; public uint SetMmeShadowRamControl; - public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)(SetMmeShadowRamControl & 0x3); + public readonly SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)(SetMmeShadowRamControl & 0x3); public fixed uint Reserved128[2]; public uint SetGlobalRenderEnableA; - public int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); + public readonly int SetGlobalRenderEnableAOffsetUpper => (int)(SetGlobalRenderEnableA & 0xFF); public uint SetGlobalRenderEnableB; public uint SetGlobalRenderEnableC; - public int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); + public readonly int SetGlobalRenderEnableCMode => (int)(SetGlobalRenderEnableC & 0x7); public uint SendGoIdle; public uint PmTrigger; public fixed uint Reserved144[3]; @@ -528,54 +528,54 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetInstrumentationMethodData; public fixed uint Reserved158[37]; public uint SetMmeSwitchState; - public bool SetMmeSwitchStateValid => (SetMmeSwitchState & 0x1) != 0; - public int SetMmeSwitchStateSaveMacro => (int)((SetMmeSwitchState >> 4) & 0xFF); - public int SetMmeSwitchStateRestoreMacro => (int)((SetMmeSwitchState >> 12) & 0xFF); + public readonly bool SetMmeSwitchStateValid => (SetMmeSwitchState & 0x1) != 0; + public readonly int SetMmeSwitchStateSaveMacro => (int)((SetMmeSwitchState >> 4) & 0xFF); + public readonly int SetMmeSwitchStateRestoreMacro => (int)((SetMmeSwitchState >> 12) & 0xFF); public fixed uint Reserved1F0[4]; public uint SetDstFormat; - public SetDstFormatV SetDstFormatV => (SetDstFormatV)(SetDstFormat & 0xFF); + public readonly SetDstFormatV SetDstFormatV => (SetDstFormatV)(SetDstFormat & 0xFF); public uint SetDstMemoryLayout; - public SetDstMemoryLayoutV SetDstMemoryLayoutV => (SetDstMemoryLayoutV)(SetDstMemoryLayout & 0x1); + public readonly SetDstMemoryLayoutV SetDstMemoryLayoutV => (SetDstMemoryLayoutV)(SetDstMemoryLayout & 0x1); public uint SetDstBlockSize; - public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0x7); - public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0x7); + public readonly SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0x7); + public readonly SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0x7); public uint SetDstDepth; public uint SetDstLayer; public uint SetDstPitch; public uint SetDstWidth; public uint SetDstHeight; public uint SetDstOffsetUpper; - public int SetDstOffsetUpperV => (int)(SetDstOffsetUpper & 0xFF); + public readonly int SetDstOffsetUpperV => (int)(SetDstOffsetUpper & 0xFF); public uint SetDstOffsetLower; public uint FlushAndInvalidateRopMiniCache; - public bool FlushAndInvalidateRopMiniCacheV => (FlushAndInvalidateRopMiniCache & 0x1) != 0; + public readonly bool FlushAndInvalidateRopMiniCacheV => (FlushAndInvalidateRopMiniCache & 0x1) != 0; public uint SetSpareNoop06; public uint SetSrcFormat; - public SetSrcFormatV SetSrcFormatV => (SetSrcFormatV)(SetSrcFormat & 0xFF); + public readonly SetSrcFormatV SetSrcFormatV => (SetSrcFormatV)(SetSrcFormat & 0xFF); public uint SetSrcMemoryLayout; - public SetSrcMemoryLayoutV SetSrcMemoryLayoutV => (SetSrcMemoryLayoutV)(SetSrcMemoryLayout & 0x1); + public readonly SetSrcMemoryLayoutV SetSrcMemoryLayoutV => (SetSrcMemoryLayoutV)(SetSrcMemoryLayout & 0x1); public uint SetSrcBlockSize; - public SetSrcBlockSizeHeight SetSrcBlockSizeHeight => (SetSrcBlockSizeHeight)((SetSrcBlockSize >> 4) & 0x7); - public SetSrcBlockSizeDepth SetSrcBlockSizeDepth => (SetSrcBlockSizeDepth)((SetSrcBlockSize >> 8) & 0x7); + public readonly SetSrcBlockSizeHeight SetSrcBlockSizeHeight => (SetSrcBlockSizeHeight)((SetSrcBlockSize >> 4) & 0x7); + public readonly SetSrcBlockSizeDepth SetSrcBlockSizeDepth => (SetSrcBlockSizeDepth)((SetSrcBlockSize >> 8) & 0x7); public uint SetSrcDepth; public uint TwodInvalidateTextureDataCache; - public TwodInvalidateTextureDataCacheV TwodInvalidateTextureDataCacheV => (TwodInvalidateTextureDataCacheV)(TwodInvalidateTextureDataCache & 0x3); + public readonly TwodInvalidateTextureDataCacheV TwodInvalidateTextureDataCacheV => (TwodInvalidateTextureDataCacheV)(TwodInvalidateTextureDataCache & 0x3); public uint SetSrcPitch; public uint SetSrcWidth; public uint SetSrcHeight; public uint SetSrcOffsetUpper; - public int SetSrcOffsetUpperV => (int)(SetSrcOffsetUpper & 0xFF); + public readonly int SetSrcOffsetUpperV => (int)(SetSrcOffsetUpper & 0xFF); public uint SetSrcOffsetLower; public uint SetPixelsFromMemorySectorPromotion; - public SetPixelsFromMemorySectorPromotionV SetPixelsFromMemorySectorPromotionV => (SetPixelsFromMemorySectorPromotionV)(SetPixelsFromMemorySectorPromotion & 0x3); + public readonly SetPixelsFromMemorySectorPromotionV SetPixelsFromMemorySectorPromotionV => (SetPixelsFromMemorySectorPromotionV)(SetPixelsFromMemorySectorPromotion & 0x3); public uint SetSpareNoop12; public uint SetNumProcessingClusters; - public SetNumProcessingClustersV SetNumProcessingClustersV => (SetNumProcessingClustersV)(SetNumProcessingClusters & 0x1); + public readonly SetNumProcessingClustersV SetNumProcessingClustersV => (SetNumProcessingClustersV)(SetNumProcessingClusters & 0x1); public uint SetRenderEnableA; - public int SetRenderEnableAOffsetUpper => (int)(SetRenderEnableA & 0xFF); + public readonly int SetRenderEnableAOffsetUpper => (int)(SetRenderEnableA & 0xFF); public uint SetRenderEnableB; public uint SetRenderEnableC; - public int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); + public readonly int SetRenderEnableCMode => (int)(SetRenderEnableC & 0x7); public uint SetSpareNoop08; public uint SetSpareNoop01; public uint SetSpareNoop11; @@ -585,29 +585,29 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetClipWidth; public uint SetClipHeight; public uint SetClipEnable; - public bool SetClipEnableV => (SetClipEnable & 0x1) != 0; + public readonly bool SetClipEnableV => (SetClipEnable & 0x1) != 0; public uint SetColorKeyFormat; - public SetColorKeyFormatV SetColorKeyFormatV => (SetColorKeyFormatV)(SetColorKeyFormat & 0x7); + public readonly SetColorKeyFormatV SetColorKeyFormatV => (SetColorKeyFormatV)(SetColorKeyFormat & 0x7); public uint SetColorKey; public uint SetColorKeyEnable; - public bool SetColorKeyEnableV => (SetColorKeyEnable & 0x1) != 0; + public readonly bool SetColorKeyEnableV => (SetColorKeyEnable & 0x1) != 0; public uint SetRop; - public int SetRopV => (int)(SetRop & 0xFF); + public readonly int SetRopV => (int)(SetRop & 0xFF); public uint SetBeta1; public uint SetBeta4; - public int SetBeta4B => (int)(SetBeta4 & 0xFF); - public int SetBeta4G => (int)((SetBeta4 >> 8) & 0xFF); - public int SetBeta4R => (int)((SetBeta4 >> 16) & 0xFF); - public int SetBeta4A => (int)((SetBeta4 >> 24) & 0xFF); + public readonly int SetBeta4B => (int)(SetBeta4 & 0xFF); + public readonly int SetBeta4G => (int)((SetBeta4 >> 8) & 0xFF); + public readonly int SetBeta4R => (int)((SetBeta4 >> 16) & 0xFF); + public readonly int SetBeta4A => (int)((SetBeta4 >> 24) & 0xFF); public uint SetOperation; - public SetOperationV SetOperationV => (SetOperationV)(SetOperation & 0x7); + public readonly SetOperationV SetOperationV => (SetOperationV)(SetOperation & 0x7); public uint SetPatternOffset; - public int SetPatternOffsetX => (int)(SetPatternOffset & 0x3F); - public int SetPatternOffsetY => (int)((SetPatternOffset >> 8) & 0x3F); + public readonly int SetPatternOffsetX => (int)(SetPatternOffset & 0x3F); + public readonly int SetPatternOffsetY => (int)((SetPatternOffset >> 8) & 0x3F); public uint SetPatternSelect; - public SetPatternSelectV SetPatternSelectV => (SetPatternSelectV)(SetPatternSelect & 0x3); + public readonly SetPatternSelectV SetPatternSelectV => (SetPatternSelectV)(SetPatternSelect & 0x3); public uint SetDstColorRenderToZetaSurface; - public bool SetDstColorRenderToZetaSurfaceV => (SetDstColorRenderToZetaSurface & 0x1) != 0; + public readonly bool SetDstColorRenderToZetaSurfaceV => (SetDstColorRenderToZetaSurface & 0x1) != 0; public uint SetSpareNoop04; public uint SetSpareNoop15; public uint SetSpareNoop13; @@ -615,18 +615,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetSpareNoop14; public uint SetSpareNoop02; public uint SetCompression; - public bool SetCompressionEnable => (SetCompression & 0x1) != 0; + public readonly bool SetCompressionEnable => (SetCompression & 0x1) != 0; public uint SetSpareNoop09; public uint SetRenderEnableOverride; - public SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)(SetRenderEnableOverride & 0x3); + public readonly SetRenderEnableOverrideMode SetRenderEnableOverrideMode => (SetRenderEnableOverrideMode)(SetRenderEnableOverride & 0x3); public uint SetPixelsFromMemoryDirection; - public SetPixelsFromMemoryDirectionHorizontal SetPixelsFromMemoryDirectionHorizontal => (SetPixelsFromMemoryDirectionHorizontal)(SetPixelsFromMemoryDirection & 0x3); - public SetPixelsFromMemoryDirectionVertical SetPixelsFromMemoryDirectionVertical => (SetPixelsFromMemoryDirectionVertical)((SetPixelsFromMemoryDirection >> 4) & 0x3); + public readonly SetPixelsFromMemoryDirectionHorizontal SetPixelsFromMemoryDirectionHorizontal => (SetPixelsFromMemoryDirectionHorizontal)(SetPixelsFromMemoryDirection & 0x3); + public readonly SetPixelsFromMemoryDirectionVertical SetPixelsFromMemoryDirectionVertical => (SetPixelsFromMemoryDirectionVertical)((SetPixelsFromMemoryDirection >> 4) & 0x3); public uint SetSpareNoop10; public uint SetMonochromePatternColorFormat; - public SetMonochromePatternColorFormatV SetMonochromePatternColorFormatV => (SetMonochromePatternColorFormatV)(SetMonochromePatternColorFormat & 0x7); + public readonly SetMonochromePatternColorFormatV SetMonochromePatternColorFormatV => (SetMonochromePatternColorFormatV)(SetMonochromePatternColorFormat & 0x7); public uint SetMonochromePatternFormat; - public SetMonochromePatternFormatV SetMonochromePatternFormatV => (SetMonochromePatternFormatV)(SetMonochromePatternFormat & 0x1); + public readonly SetMonochromePatternFormatV SetMonochromePatternFormatV => (SetMonochromePatternFormatV)(SetMonochromePatternFormat & 0x1); public uint SetMonochromePatternColor0; public uint SetMonochromePatternColor1; public uint SetMonochromePattern0; @@ -662,52 +662,52 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetRenderSolidPrimColor2; public uint SetRenderSolidPrimColor3; public uint SetMmeMemAddressA; - public int SetMmeMemAddressAUpper => (int)(SetMmeMemAddressA & 0x1FFFFFF); + public readonly int SetMmeMemAddressAUpper => (int)(SetMmeMemAddressA & 0x1FFFFFF); public uint SetMmeMemAddressB; public uint SetMmeDataRamAddress; public uint MmeDmaRead; public uint MmeDmaReadFifoed; public uint MmeDmaWrite; public uint MmeDmaReduction; - public MmeDmaReductionReductionOp MmeDmaReductionReductionOp => (MmeDmaReductionReductionOp)(MmeDmaReduction & 0x7); - public MmeDmaReductionReductionFormat MmeDmaReductionReductionFormat => (MmeDmaReductionReductionFormat)((MmeDmaReduction >> 4) & 0x3); - public MmeDmaReductionReductionSize MmeDmaReductionReductionSize => (MmeDmaReductionReductionSize)((MmeDmaReduction >> 8) & 0x1); + public readonly MmeDmaReductionReductionOp MmeDmaReductionReductionOp => (MmeDmaReductionReductionOp)(MmeDmaReduction & 0x7); + public readonly MmeDmaReductionReductionFormat MmeDmaReductionReductionFormat => (MmeDmaReductionReductionFormat)((MmeDmaReduction >> 4) & 0x3); + public readonly MmeDmaReductionReductionSize MmeDmaReductionReductionSize => (MmeDmaReductionReductionSize)((MmeDmaReduction >> 8) & 0x1); public uint MmeDmaSysmembar; - public bool MmeDmaSysmembarV => (MmeDmaSysmembar & 0x1) != 0; + public readonly bool MmeDmaSysmembarV => (MmeDmaSysmembar & 0x1) != 0; public uint MmeDmaSync; public uint SetMmeDataFifoConfig; - public SetMmeDataFifoConfigFifoSize SetMmeDataFifoConfigFifoSize => (SetMmeDataFifoConfigFifoSize)(SetMmeDataFifoConfig & 0x7); + public readonly SetMmeDataFifoConfigFifoSize SetMmeDataFifoConfigFifoSize => (SetMmeDataFifoConfigFifoSize)(SetMmeDataFifoConfig & 0x7); public fixed uint Reserved578[2]; public uint RenderSolidPrimMode; - public RenderSolidPrimModeV RenderSolidPrimModeV => (RenderSolidPrimModeV)(RenderSolidPrimMode & 0x7); + public readonly RenderSolidPrimModeV RenderSolidPrimModeV => (RenderSolidPrimModeV)(RenderSolidPrimMode & 0x7); public uint SetRenderSolidPrimColorFormat; - public SetRenderSolidPrimColorFormatV SetRenderSolidPrimColorFormatV => (SetRenderSolidPrimColorFormatV)(SetRenderSolidPrimColorFormat & 0xFF); + public readonly SetRenderSolidPrimColorFormatV SetRenderSolidPrimColorFormatV => (SetRenderSolidPrimColorFormatV)(SetRenderSolidPrimColorFormat & 0xFF); public uint SetRenderSolidPrimColor; public uint SetRenderSolidLineTieBreakBits; - public bool SetRenderSolidLineTieBreakBitsXmajXincYinc => (SetRenderSolidLineTieBreakBits & 0x1) != 0; - public bool SetRenderSolidLineTieBreakBitsXmajXdecYinc => (SetRenderSolidLineTieBreakBits & 0x10) != 0; - public bool SetRenderSolidLineTieBreakBitsYmajXincYinc => (SetRenderSolidLineTieBreakBits & 0x100) != 0; - public bool SetRenderSolidLineTieBreakBitsYmajXdecYinc => (SetRenderSolidLineTieBreakBits & 0x1000) != 0; + public readonly bool SetRenderSolidLineTieBreakBitsXmajXincYinc => (SetRenderSolidLineTieBreakBits & 0x1) != 0; + public readonly bool SetRenderSolidLineTieBreakBitsXmajXdecYinc => (SetRenderSolidLineTieBreakBits & 0x10) != 0; + public readonly bool SetRenderSolidLineTieBreakBitsYmajXincYinc => (SetRenderSolidLineTieBreakBits & 0x100) != 0; + public readonly bool SetRenderSolidLineTieBreakBitsYmajXdecYinc => (SetRenderSolidLineTieBreakBits & 0x1000) != 0; public fixed uint Reserved590[20]; public uint RenderSolidPrimPointXY; - public int RenderSolidPrimPointXYX => (int)(RenderSolidPrimPointXY & 0xFFFF); - public int RenderSolidPrimPointXYY => (int)((RenderSolidPrimPointXY >> 16) & 0xFFFF); + public readonly int RenderSolidPrimPointXYX => (int)(RenderSolidPrimPointXY & 0xFFFF); + public readonly int RenderSolidPrimPointXYY => (int)((RenderSolidPrimPointXY >> 16) & 0xFFFF); public fixed uint Reserved5E4[7]; public Array64<RenderSolidPrimPoint> RenderSolidPrimPoint; public uint SetPixelsFromCpuDataType; - public SetPixelsFromCpuDataTypeV SetPixelsFromCpuDataTypeV => (SetPixelsFromCpuDataTypeV)(SetPixelsFromCpuDataType & 0x1); + public readonly SetPixelsFromCpuDataTypeV SetPixelsFromCpuDataTypeV => (SetPixelsFromCpuDataTypeV)(SetPixelsFromCpuDataType & 0x1); public uint SetPixelsFromCpuColorFormat; - public SetPixelsFromCpuColorFormatV SetPixelsFromCpuColorFormatV => (SetPixelsFromCpuColorFormatV)(SetPixelsFromCpuColorFormat & 0xFF); + public readonly SetPixelsFromCpuColorFormatV SetPixelsFromCpuColorFormatV => (SetPixelsFromCpuColorFormatV)(SetPixelsFromCpuColorFormat & 0xFF); public uint SetPixelsFromCpuIndexFormat; - public SetPixelsFromCpuIndexFormatV SetPixelsFromCpuIndexFormatV => (SetPixelsFromCpuIndexFormatV)(SetPixelsFromCpuIndexFormat & 0x3); + public readonly SetPixelsFromCpuIndexFormatV SetPixelsFromCpuIndexFormatV => (SetPixelsFromCpuIndexFormatV)(SetPixelsFromCpuIndexFormat & 0x3); public uint SetPixelsFromCpuMonoFormat; - public SetPixelsFromCpuMonoFormatV SetPixelsFromCpuMonoFormatV => (SetPixelsFromCpuMonoFormatV)(SetPixelsFromCpuMonoFormat & 0x1); + public readonly SetPixelsFromCpuMonoFormatV SetPixelsFromCpuMonoFormatV => (SetPixelsFromCpuMonoFormatV)(SetPixelsFromCpuMonoFormat & 0x1); public uint SetPixelsFromCpuWrap; - public SetPixelsFromCpuWrapV SetPixelsFromCpuWrapV => (SetPixelsFromCpuWrapV)(SetPixelsFromCpuWrap & 0x3); + public readonly SetPixelsFromCpuWrapV SetPixelsFromCpuWrapV => (SetPixelsFromCpuWrapV)(SetPixelsFromCpuWrap & 0x3); public uint SetPixelsFromCpuColor0; public uint SetPixelsFromCpuColor1; public uint SetPixelsFromCpuMonoOpacity; - public SetPixelsFromCpuMonoOpacityV SetPixelsFromCpuMonoOpacityV => (SetPixelsFromCpuMonoOpacityV)(SetPixelsFromCpuMonoOpacity & 0x1); + public readonly SetPixelsFromCpuMonoOpacityV SetPixelsFromCpuMonoOpacityV => (SetPixelsFromCpuMonoOpacityV)(SetPixelsFromCpuMonoOpacity & 0x1); public fixed uint Reserved820[6]; public uint SetPixelsFromCpuSrcWidth; public uint SetPixelsFromCpuSrcHeight; @@ -722,45 +722,45 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint PixelsFromCpuData; public fixed uint Reserved864[3]; public uint SetBigEndianControl; - public bool SetBigEndianControlX32Swap1 => (SetBigEndianControl & 0x1) != 0; - public bool SetBigEndianControlX32Swap4 => (SetBigEndianControl & 0x2) != 0; - public bool SetBigEndianControlX32Swap8 => (SetBigEndianControl & 0x4) != 0; - public bool SetBigEndianControlX32Swap16 => (SetBigEndianControl & 0x8) != 0; - public bool SetBigEndianControlX16Swap1 => (SetBigEndianControl & 0x10) != 0; - public bool SetBigEndianControlX16Swap4 => (SetBigEndianControl & 0x20) != 0; - public bool SetBigEndianControlX16Swap8 => (SetBigEndianControl & 0x40) != 0; - public bool SetBigEndianControlX16Swap16 => (SetBigEndianControl & 0x80) != 0; - public bool SetBigEndianControlX8Swap1 => (SetBigEndianControl & 0x100) != 0; - public bool SetBigEndianControlX8Swap4 => (SetBigEndianControl & 0x200) != 0; - public bool SetBigEndianControlX8Swap8 => (SetBigEndianControl & 0x400) != 0; - public bool SetBigEndianControlX8Swap16 => (SetBigEndianControl & 0x800) != 0; - public bool SetBigEndianControlI1X8Cga6Swap1 => (SetBigEndianControl & 0x1000) != 0; - public bool SetBigEndianControlI1X8Cga6Swap4 => (SetBigEndianControl & 0x2000) != 0; - public bool SetBigEndianControlI1X8Cga6Swap8 => (SetBigEndianControl & 0x4000) != 0; - public bool SetBigEndianControlI1X8Cga6Swap16 => (SetBigEndianControl & 0x8000) != 0; - public bool SetBigEndianControlI1X8LeSwap1 => (SetBigEndianControl & 0x10000) != 0; - public bool SetBigEndianControlI1X8LeSwap4 => (SetBigEndianControl & 0x20000) != 0; - public bool SetBigEndianControlI1X8LeSwap8 => (SetBigEndianControl & 0x40000) != 0; - public bool SetBigEndianControlI1X8LeSwap16 => (SetBigEndianControl & 0x80000) != 0; - public bool SetBigEndianControlI4Swap1 => (SetBigEndianControl & 0x100000) != 0; - public bool SetBigEndianControlI4Swap4 => (SetBigEndianControl & 0x200000) != 0; - public bool SetBigEndianControlI4Swap8 => (SetBigEndianControl & 0x400000) != 0; - public bool SetBigEndianControlI4Swap16 => (SetBigEndianControl & 0x800000) != 0; - public bool SetBigEndianControlI8Swap1 => (SetBigEndianControl & 0x1000000) != 0; - public bool SetBigEndianControlI8Swap4 => (SetBigEndianControl & 0x2000000) != 0; - public bool SetBigEndianControlI8Swap8 => (SetBigEndianControl & 0x4000000) != 0; - public bool SetBigEndianControlI8Swap16 => (SetBigEndianControl & 0x8000000) != 0; - public bool SetBigEndianControlOverride => (SetBigEndianControl & 0x10000000) != 0; + public readonly bool SetBigEndianControlX32Swap1 => (SetBigEndianControl & 0x1) != 0; + public readonly bool SetBigEndianControlX32Swap4 => (SetBigEndianControl & 0x2) != 0; + public readonly bool SetBigEndianControlX32Swap8 => (SetBigEndianControl & 0x4) != 0; + public readonly bool SetBigEndianControlX32Swap16 => (SetBigEndianControl & 0x8) != 0; + public readonly bool SetBigEndianControlX16Swap1 => (SetBigEndianControl & 0x10) != 0; + public readonly bool SetBigEndianControlX16Swap4 => (SetBigEndianControl & 0x20) != 0; + public readonly bool SetBigEndianControlX16Swap8 => (SetBigEndianControl & 0x40) != 0; + public readonly bool SetBigEndianControlX16Swap16 => (SetBigEndianControl & 0x80) != 0; + public readonly bool SetBigEndianControlX8Swap1 => (SetBigEndianControl & 0x100) != 0; + public readonly bool SetBigEndianControlX8Swap4 => (SetBigEndianControl & 0x200) != 0; + public readonly bool SetBigEndianControlX8Swap8 => (SetBigEndianControl & 0x400) != 0; + public readonly bool SetBigEndianControlX8Swap16 => (SetBigEndianControl & 0x800) != 0; + public readonly bool SetBigEndianControlI1X8Cga6Swap1 => (SetBigEndianControl & 0x1000) != 0; + public readonly bool SetBigEndianControlI1X8Cga6Swap4 => (SetBigEndianControl & 0x2000) != 0; + public readonly bool SetBigEndianControlI1X8Cga6Swap8 => (SetBigEndianControl & 0x4000) != 0; + public readonly bool SetBigEndianControlI1X8Cga6Swap16 => (SetBigEndianControl & 0x8000) != 0; + public readonly bool SetBigEndianControlI1X8LeSwap1 => (SetBigEndianControl & 0x10000) != 0; + public readonly bool SetBigEndianControlI1X8LeSwap4 => (SetBigEndianControl & 0x20000) != 0; + public readonly bool SetBigEndianControlI1X8LeSwap8 => (SetBigEndianControl & 0x40000) != 0; + public readonly bool SetBigEndianControlI1X8LeSwap16 => (SetBigEndianControl & 0x80000) != 0; + public readonly bool SetBigEndianControlI4Swap1 => (SetBigEndianControl & 0x100000) != 0; + public readonly bool SetBigEndianControlI4Swap4 => (SetBigEndianControl & 0x200000) != 0; + public readonly bool SetBigEndianControlI4Swap8 => (SetBigEndianControl & 0x400000) != 0; + public readonly bool SetBigEndianControlI4Swap16 => (SetBigEndianControl & 0x800000) != 0; + public readonly bool SetBigEndianControlI8Swap1 => (SetBigEndianControl & 0x1000000) != 0; + public readonly bool SetBigEndianControlI8Swap4 => (SetBigEndianControl & 0x2000000) != 0; + public readonly bool SetBigEndianControlI8Swap8 => (SetBigEndianControl & 0x4000000) != 0; + public readonly bool SetBigEndianControlI8Swap16 => (SetBigEndianControl & 0x8000000) != 0; + public readonly bool SetBigEndianControlOverride => (SetBigEndianControl & 0x10000000) != 0; public fixed uint Reserved874[3]; public uint SetPixelsFromMemoryBlockShape; - public SetPixelsFromMemoryBlockShapeV SetPixelsFromMemoryBlockShapeV => (SetPixelsFromMemoryBlockShapeV)(SetPixelsFromMemoryBlockShape & 0x7); + public readonly SetPixelsFromMemoryBlockShapeV SetPixelsFromMemoryBlockShapeV => (SetPixelsFromMemoryBlockShapeV)(SetPixelsFromMemoryBlockShape & 0x7); public uint SetPixelsFromMemoryCorralSize; - public int SetPixelsFromMemoryCorralSizeV => (int)(SetPixelsFromMemoryCorralSize & 0x3FF); + public readonly int SetPixelsFromMemoryCorralSizeV => (int)(SetPixelsFromMemoryCorralSize & 0x3FF); public uint SetPixelsFromMemorySafeOverlap; - public bool SetPixelsFromMemorySafeOverlapV => (SetPixelsFromMemorySafeOverlap & 0x1) != 0; + public readonly bool SetPixelsFromMemorySafeOverlapV => (SetPixelsFromMemorySafeOverlap & 0x1) != 0; public uint SetPixelsFromMemorySampleMode; - public SetPixelsFromMemorySampleModeOrigin SetPixelsFromMemorySampleModeOrigin => (SetPixelsFromMemorySampleModeOrigin)(SetPixelsFromMemorySampleMode & 0x1); - public SetPixelsFromMemorySampleModeFilter SetPixelsFromMemorySampleModeFilter => (SetPixelsFromMemorySampleModeFilter)((SetPixelsFromMemorySampleMode >> 4) & 0x1); + public readonly SetPixelsFromMemorySampleModeOrigin SetPixelsFromMemorySampleModeOrigin => (SetPixelsFromMemorySampleModeOrigin)(SetPixelsFromMemorySampleMode & 0x1); + public readonly SetPixelsFromMemorySampleModeFilter SetPixelsFromMemorySampleModeFilter => (SetPixelsFromMemorySampleModeFilter)((SetPixelsFromMemorySampleMode >> 4) & 0x1); public fixed uint Reserved890[8]; public uint SetPixelsFromMemoryDstX0; public uint SetPixelsFromMemoryDstY0; @@ -808,9 +808,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod public uint SetFalcon31; public fixed uint Reserved960[291]; public uint MmeDmaWriteMethodBarrier; - public bool MmeDmaWriteMethodBarrierV => (MmeDmaWriteMethodBarrier & 0x1) != 0; + public readonly bool MmeDmaWriteMethodBarrierV => (MmeDmaWriteMethodBarrier & 0x1) != 0; public fixed uint ReservedDF0[2436]; - public MmeShadowScratch SetMmeShadowScratch; + public Array256<uint> SetMmeShadowScratch; #pragma warning restore CS0649 } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs index c28da0948..dd6b69000 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod /// </summary> struct TwodTexture { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public ColorFormat Format; public Boolean32 LinearLayout; public MemoryLayout MemoryLayout; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs index c982347a1..911ad53b4 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs @@ -3,10 +3,10 @@ /// <summary> /// Boolean value, stored as a 32-bits integer in memory. /// </summary> - struct Boolean32 + readonly struct Boolean32 { -#pragma warning disable CS0649 - private uint _value; +#pragma warning disable CS0649 // Field is never assigned to + private readonly uint _value; #pragma warning restore CS0649 public static implicit operator bool(Boolean32 value) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs index 889b5c8b0..c798384f0 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs @@ -9,58 +9,58 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types enum ColorFormat { R32G32B32A32Float = 0xc0, - R32G32B32A32Sint = 0xc1, - R32G32B32A32Uint = 0xc2, + R32G32B32A32Sint = 0xc1, + R32G32B32A32Uint = 0xc2, R32G32B32X32Float = 0xc3, - R32G32B32X32Sint = 0xc4, - R32G32B32X32Uint = 0xc5, + R32G32B32X32Sint = 0xc4, + R32G32B32X32Uint = 0xc5, R16G16B16X16Unorm = 0xc6, R16G16B16X16Snorm = 0xc7, - R16G16B16X16Sint = 0xc8, - R16G16B16X16Uint = 0xc9, + R16G16B16X16Sint = 0xc8, + R16G16B16X16Uint = 0xc9, R16G16B16A16Float = 0xca, - R32G32Float = 0xcb, - R32G32Sint = 0xcc, - R32G32Uint = 0xcd, + R32G32Float = 0xcb, + R32G32Sint = 0xcc, + R32G32Uint = 0xcd, R16G16B16X16Float = 0xce, - B8G8R8A8Unorm = 0xcf, - B8G8R8A8Srgb = 0xd0, - R10G10B10A2Unorm = 0xd1, - R10G10B10A2Uint = 0xd2, - R8G8B8A8Unorm = 0xd5, - R8G8B8A8Srgb = 0xd6, - R8G8B8X8Snorm = 0xd7, - R8G8B8X8Sint = 0xd8, - R8G8B8X8Uint = 0xd9, - R16G16Unorm = 0xda, - R16G16Snorm = 0xdb, - R16G16Sint = 0xdc, - R16G16Uint = 0xdd, - R16G16Float = 0xde, - R11G11B10Float = 0xe0, - R32Sint = 0xe3, - R32Uint = 0xe4, - R32Float = 0xe5, - B8G8R8X8Unorm = 0xe6, - B8G8R8X8Srgb = 0xe7, - B5G6R5Unorm = 0xe8, - B5G5R5A1Unorm = 0xe9, - R8G8Unorm = 0xea, - R8G8Snorm = 0xeb, - R8G8Sint = 0xec, - R8G8Uint = 0xed, - R16Unorm = 0xee, - R16Snorm = 0xef, - R16Sint = 0xf0, - R16Uint = 0xf1, - R16Float = 0xf2, - R8Unorm = 0xf3, - R8Snorm = 0xf4, - R8Sint = 0xf5, - R8Uint = 0xf6, - B5G5R5X1Unorm = 0xf8, - R8G8B8X8Unorm = 0xf9, - R8G8B8X8Srgb = 0xfa + B8G8R8A8Unorm = 0xcf, + B8G8R8A8Srgb = 0xd0, + R10G10B10A2Unorm = 0xd1, + R10G10B10A2Uint = 0xd2, + R8G8B8A8Unorm = 0xd5, + R8G8B8A8Srgb = 0xd6, + R8G8B8X8Snorm = 0xd7, + R8G8B8X8Sint = 0xd8, + R8G8B8X8Uint = 0xd9, + R16G16Unorm = 0xda, + R16G16Snorm = 0xdb, + R16G16Sint = 0xdc, + R16G16Uint = 0xdd, + R16G16Float = 0xde, + R11G11B10Float = 0xe0, + R32Sint = 0xe3, + R32Uint = 0xe4, + R32Float = 0xe5, + B8G8R8X8Unorm = 0xe6, + B8G8R8X8Srgb = 0xe7, + B5G6R5Unorm = 0xe8, + B5G5R5A1Unorm = 0xe9, + R8G8Unorm = 0xea, + R8G8Snorm = 0xeb, + R8G8Sint = 0xec, + R8G8Uint = 0xed, + R16Unorm = 0xee, + R16Snorm = 0xef, + R16Sint = 0xf0, + R16Uint = 0xf1, + R16Float = 0xf2, + R8Unorm = 0xf3, + R8Snorm = 0xf4, + R8Sint = 0xf5, + R8Uint = 0xf6, + B5G5R5X1Unorm = 0xf8, + R8G8B8X8Unorm = 0xf9, + R8G8B8X8Srgb = 0xfa, } static class ColorFormatConverter @@ -74,6 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types { return format switch { +#pragma warning disable IDE0055 // Disable formatting ColorFormat.R32G32B32A32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4), ColorFormat.R32G32B32A32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4), ColorFormat.R32G32B32A32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4), @@ -127,7 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types ColorFormat.B5G5R5X1Unorm => new FormatInfo(Format.B5G5R5A1Unorm, 1, 1, 2, 4), ColorFormat.R8G8B8X8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4), ColorFormat.R8G8B8X8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4), - _ => FormatInfo.Default + _ => FormatInfo.Default, +#pragma warning restore IDE0055 }; } @@ -157,9 +159,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types case ColorFormat.R8G8B8X8Unorm: case ColorFormat.R8G8B8X8Srgb: return true; + default: + return false; } - - return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs index 839faac9b..b3b0c41ad 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs @@ -5,7 +5,7 @@ /// </summary> struct GpuVa { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint High; public uint Low; #pragma warning restore CS0649 @@ -14,7 +14,7 @@ /// Packs the split address into a 64-bits address value. /// </summary> /// <returns>The 64-bits address value</returns> - public ulong Pack() + public readonly ulong Pack() { return Low | ((ulong)High << 32); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs index 6da96bd44..5a4253734 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs @@ -5,31 +5,31 @@ /// </summary> struct MemoryLayout { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Packed; #pragma warning restore CS0649 - public int UnpackGobBlocksInX() + public readonly int UnpackGobBlocksInX() { return 1 << (int)(Packed & 0xf); } - public int UnpackGobBlocksInY() + public readonly int UnpackGobBlocksInY() { return 1 << (int)((Packed >> 4) & 0xf); } - public int UnpackGobBlocksInZ() + public readonly int UnpackGobBlocksInZ() { return 1 << (int)((Packed >> 8) & 0xf); } - public bool UnpackIsLinear() + public readonly bool UnpackIsLinear() { return (Packed & 0x1000) != 0; } - public bool UnpackIsTarget3D() + public readonly bool UnpackIsTarget3D() { return (Packed & 0x10000) != 0; } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs index dae631248..5abbc9235 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types LineStripAdjacency, TrianglesAdjacency, TriangleStripAdjacency, - Patches + Patches, } /// <summary> @@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types LineStripAdjacency = 11, TrianglesAdjacency = 12, TriangleStripAdjacency = 13, - Patches = 14 + Patches = 14, } static class PrimitiveTypeConverter @@ -53,6 +53,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types { return type switch { +#pragma warning disable IDE0055 // Disable formatting PrimitiveType.Points => PrimitiveTopology.Points, PrimitiveType.Lines => PrimitiveTopology.Lines, PrimitiveType.LineLoop => PrimitiveTopology.LineLoop, @@ -68,7 +69,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types PrimitiveType.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency, PrimitiveType.TriangleStripAdjacency => PrimitiveTopology.TriangleStripAdjacency, PrimitiveType.Patches => PrimitiveTopology.Patches, - _ => PrimitiveTopology.Triangles + _ => PrimitiveTopology.Triangles, +#pragma warning restore IDE0055 }; } @@ -81,6 +83,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types { return type switch { +#pragma warning disable IDE0055 // Disable formatting PrimitiveTypeOverride.Points => PrimitiveTopology.Points, PrimitiveTypeOverride.Lines => PrimitiveTopology.Lines, PrimitiveTypeOverride.LineStrip => PrimitiveTopology.LineStrip, @@ -92,8 +95,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types PrimitiveTypeOverride.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency, PrimitiveTypeOverride.TriangleStripAdjacency => PrimitiveTopology.TriangleStripAdjacency, PrimitiveTypeOverride.Patches => PrimitiveTopology.Patches, - _ => PrimitiveTopology.Triangles + _ => PrimitiveTopology.Triangles, +#pragma warning restore IDE0055 }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs index 839a4d0af..22fe4a92f 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types /// </summary> enum SamplerIndex { - Independently = 0, - ViaHeaderIndex = 1 + Independently = 0, + ViaHeaderIndex = 1, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs index c457dbf91..e92263df0 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs @@ -5,14 +5,14 @@ /// </summary> struct SbDescriptor { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint AddressLow; public uint AddressHigh; public int Size; public int Padding; #pragma warning restore CS0649 - public ulong PackAddress() + public readonly ulong PackAddress() { return AddressLow | ((ulong)AddressHigh << 32); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs index 1de1621fc..0fa073d7b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs @@ -8,13 +8,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types /// </summary> enum ZetaFormat { - D32Float = 0xa, - D16Unorm = 0x13, + D32Float = 0xa, + D16Unorm = 0x13, D24UnormS8Uint = 0x14, - D24Unorm = 0x15, + D24Unorm = 0x15, S8UintD24Unorm = 0x16, - S8Uint = 0x17, - D32FloatS8Uint = 0x19 + S8Uint = 0x17, + D32FloatS8Uint = 0x19, } static class ZetaFormatConverter @@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types { return format switch { +#pragma warning disable IDE0055 // Disable formatting ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1), ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1), ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2), @@ -35,7 +36,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2), ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1), ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2), - _ => FormatInfo.Default + _ => FormatInfo.Default, +#pragma warning restore IDE0055 }; } } diff --git a/src/Ryujinx.Graphics.Gpu/GpuChannel.cs b/src/Ryujinx.Graphics.Gpu/GpuChannel.cs index 43fa8484b..8fe643815 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuChannel.cs @@ -125,6 +125,7 @@ namespace Ryujinx.Graphics.Gpu /// </summary> public void Dispose() { + GC.SuppressFinalize(this); _context.DeferredActions.Enqueue(Destroy); } diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index 233227b4d..a5fe8f4c1 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu private bool _pendingSync; private long _modifiedSequence; - private ulong _firstTimestamp; + private readonly ulong _firstTimestamp; /// <summary> /// Creates a new instance of the GPU emulation context. @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu /// <returns>The current GPU timestamp</returns> public ulong GetTimestamp() { - // Guest timestamp will start at 0, instead of host value. + // Guest timestamp will start at 0, instead of host value. ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds) - _firstTimestamp; if (GraphicsConfig.FastGpuTime) @@ -406,4 +406,4 @@ namespace Ryujinx.Graphics.Gpu Renderer.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs b/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs index d2f98c7f2..4dfb93381 100644 --- a/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs +++ b/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs @@ -1,5 +1,6 @@ namespace Ryujinx.Graphics.Gpu { +#pragma warning disable CA2211 // Non-constant fields should not be visible /// <summary> /// General GPU and graphics configuration. /// </summary> @@ -67,4 +68,5 @@ namespace Ryujinx.Graphics.Gpu /// </summary> public static bool EnableTextureRecompression = false; } -} \ No newline at end of file +#pragma warning restore CA2211 +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index 2465efb0f..05782605b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Gpu.Image private HashSet<ShortTextureCacheEntry> _shortCacheBuilder; private HashSet<ShortTextureCacheEntry> _shortCache; - private Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup; + private readonly Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup; /// <summary> /// Creates a new instance of the automatic deletion cache. @@ -295,4 +295,4 @@ namespace Ryujinx.Graphics.Gpu.Image return _textures.GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs index 9ee649d2d..8a9f37bb0 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// A default, generic RGBA8 texture format. /// </summary> - public static FormatInfo Default { get; } = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4); + public static FormatInfo Default { get; } = new(Format.R8G8B8A8Unorm, 1, 1, 4, 4); /// <summary> /// The format of the texture data. @@ -57,16 +57,16 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param> public FormatInfo( Format format, - int blockWidth, - int blockHeight, - int bytesPerPixel, - int components) + int blockWidth, + int blockHeight, + int bytesPerPixel, + int components) { - Format = format; - BlockWidth = blockWidth; - BlockHeight = blockHeight; + Format = format; + BlockWidth = blockWidth; + BlockHeight = blockHeight; BytesPerPixel = bytesPerPixel; - Components = components; + Components = components; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs index 729016104..ea5e9d002 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Ryujinx.Graphics.Gpu.Image { @@ -8,6 +9,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> static class FormatTable { +#pragma warning disable IDE0055 // Disable formatting + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] private enum TextureFormat : uint { // Formats @@ -244,6 +247,7 @@ namespace Ryujinx.Graphics.Gpu.Image A5B5G5R1Unorm = A5B5G5R1 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24913 } + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] private enum VertexAttributeFormat : uint { // Width @@ -357,7 +361,7 @@ namespace Ryujinx.Graphics.Gpu.Image A2B10G10R10Sscaled = (A2B10G10R10 << 21) | (Sscaled << 27), // 0x36000000 } - private static readonly Dictionary<TextureFormat, FormatInfo> _textureFormats = new Dictionary<TextureFormat, FormatInfo>() + private static readonly Dictionary<TextureFormat, FormatInfo> _textureFormats = new() { { TextureFormat.R8Unorm, new FormatInfo(Format.R8Unorm, 1, 1, 1, 1) }, { TextureFormat.R8Snorm, new FormatInfo(Format.R8Snorm, 1, 1, 1, 1) }, @@ -464,10 +468,10 @@ namespace Ryujinx.Graphics.Gpu.Image { TextureFormat.Astc2D10x10UnormSrgb, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16, 4) }, { TextureFormat.Astc2D12x10UnormSrgb, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16, 4) }, { TextureFormat.Astc2D12x12UnormSrgb, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16, 4) }, - { TextureFormat.A5B5G5R1Unorm, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2, 4) } + { TextureFormat.A5B5G5R1Unorm, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2, 4) }, }; - private static readonly Dictionary<VertexAttributeFormat, Format> _attribFormats = new Dictionary<VertexAttributeFormat, Format>() + private static readonly Dictionary<VertexAttributeFormat, Format> _attribFormats = new() { { VertexAttributeFormat.R8Unorm, Format.R8Unorm }, { VertexAttributeFormat.R8Snorm, Format.R8Snorm }, @@ -547,8 +551,9 @@ namespace Ryujinx.Graphics.Gpu.Image { VertexAttributeFormat.A2B10G10R10Snorm, Format.R10G10B10A2Snorm }, { VertexAttributeFormat.A2B10G10R10Sint, Format.R10G10B10A2Sint }, { VertexAttributeFormat.A2B10G10R10Uscaled, Format.R10G10B10A2Uscaled }, - { VertexAttributeFormat.A2B10G10R10Sscaled, Format.R10G10B10A2Sscaled } + { VertexAttributeFormat.A2B10G10R10Sscaled, Format.R10G10B10A2Sscaled }, }; +#pragma warning restore IDE0055 /// <summary> /// Try getting the texture format from an encoded format integer from the Maxwell texture descriptor. @@ -575,4 +580,4 @@ namespace Ryujinx.Graphics.Gpu.Image return _attribFormats.TryGetValue((VertexAttributeFormat)encoded, out format); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/Pool.cs b/src/Ryujinx.Graphics.Gpu/Image/Pool.cs index 63be151f3..0c3a219de 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Gpu.Image DescriptorCache = new T2[count]; Address = address; - Size = size; + Size = size; _memoryTracking = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Pool); _memoryTracking.RegisterPreciseAction(address, size, PreciseAction); @@ -219,4 +219,4 @@ namespace Ryujinx.Graphics.Gpu.Image _memoryTracking.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs index e1493f388..d9881f897 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs @@ -126,4 +126,4 @@ namespace Ryujinx.Graphics.Gpu.Image _pools.Clear(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs b/src/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs index 1f7d9b070..01553e50c 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs @@ -10,6 +10,6 @@ namespace Ryujinx.Graphics.Gpu.Image { Average, Minimum, - Maximum + Maximum, } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs index b70ac9eb9..d6a3d975b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Sampler.cs @@ -40,16 +40,16 @@ namespace Ryujinx.Graphics.Gpu.Image AddressMode addressP = descriptor.UnpackAddressP(); CompareMode compareMode = descriptor.UnpackCompareMode(); - CompareOp compareOp = descriptor.UnpackCompareOp(); + CompareOp compareOp = descriptor.UnpackCompareOp(); - ColorF color = new ColorF( + ColorF color = new( descriptor.BorderColorR, descriptor.BorderColorG, descriptor.BorderColorB, descriptor.BorderColorA); - float minLod = descriptor.UnpackMinLod(); - float maxLod = descriptor.UnpackMaxLod(); + float minLod = descriptor.UnpackMinLod(); + float maxLod = descriptor.UnpackMaxLod(); float mipLodBias = descriptor.UnpackMipLodBias(); float maxRequestedAnisotropy = descriptor.UnpackMaxAnisotropy(); @@ -112,4 +112,4 @@ namespace Ryujinx.Graphics.Gpu.Image _anisoSampler?.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs index 64a146fb3..e04c31dfa 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs @@ -43,17 +43,17 @@ namespace Ryujinx.Graphics.Gpu.Image 0.45833334f, 0.46153846f, 0.4642857f, - 0.46666667f + 0.46666667f, }; private static readonly float[] _maxAnisotropyLut = new float[] { - 1, 2, 4, 6, 8, 10, 12, 16 + 1, 2, 4, 6, 8, 10, 12, 16, }; private const float Frac8ToF32 = 1.0f / 256.0f; -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Word0; public uint Word1; public uint Word2; @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the texture wrap mode along the X axis. /// </summary> /// <returns>The texture wrap mode enum</returns> - public AddressMode UnpackAddressU() + public readonly AddressMode UnpackAddressU() { return (AddressMode)(Word0 & 7); } @@ -77,7 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the texture wrap mode along the Y axis. /// </summary> /// <returns>The texture wrap mode enum</returns> - public AddressMode UnpackAddressV() + public readonly AddressMode UnpackAddressV() { return (AddressMode)((Word0 >> 3) & 7); } @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the texture wrap mode along the Z axis. /// </summary> /// <returns>The texture wrap mode enum</returns> - public AddressMode UnpackAddressP() + public readonly AddressMode UnpackAddressP() { return (AddressMode)((Word0 >> 6) & 7); } @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// This is only relevant for shaders with shadow samplers. /// </summary> /// <returns>The depth comparison mode enum</returns> - public CompareMode UnpackCompareMode() + public readonly CompareMode UnpackCompareMode() { return (CompareMode)((Word0 >> 9) & 1); } @@ -108,7 +108,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// This is only relevant for shaders with shadow samplers. /// </summary> /// <returns>The depth comparison operation enum</returns> - public CompareOp UnpackCompareOp() + public readonly CompareOp UnpackCompareOp() { return (CompareOp)(((Word0 >> 10) & 7) + 1); } @@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering. /// </summary> /// <returns>The maximum anisotropy</returns> - public float UnpackMaxAnisotropy() + public readonly float UnpackMaxAnisotropy() { return _maxAnisotropyLut[(Word0 >> 20) & 7]; } @@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// that is larger than the texture size. /// </summary> /// <returns>The magnification filter</returns> - public MagFilter UnpackMagFilter() + public readonly MagFilter UnpackMagFilter() { return (MagFilter)(Word1 & 3); } @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// that is smaller than the texture size. /// </summary> /// <returns>The minification filter</returns> - public MinFilter UnpackMinFilter() + public readonly MinFilter UnpackMinFilter() { SamplerMinFilter minFilter = (SamplerMinFilter)((Word1 >> 4) & 3); SamplerMipFilter mipFilter = (SamplerMipFilter)((Word1 >> 6) & 3); @@ -161,24 +161,30 @@ namespace Ryujinx.Graphics.Gpu.Image case SamplerMipFilter.None: switch (minFilter) { - case SamplerMinFilter.Nearest: return MinFilter.Nearest; - case SamplerMinFilter.Linear: return MinFilter.Linear; + case SamplerMinFilter.Nearest: + return MinFilter.Nearest; + case SamplerMinFilter.Linear: + return MinFilter.Linear; } break; case SamplerMipFilter.Nearest: switch (minFilter) { - case SamplerMinFilter.Nearest: return MinFilter.NearestMipmapNearest; - case SamplerMinFilter.Linear: return MinFilter.LinearMipmapNearest; + case SamplerMinFilter.Nearest: + return MinFilter.NearestMipmapNearest; + case SamplerMinFilter.Linear: + return MinFilter.LinearMipmapNearest; } break; case SamplerMipFilter.Linear: switch (minFilter) { - case SamplerMinFilter.Nearest: return MinFilter.NearestMipmapLinear; - case SamplerMinFilter.Linear: return MinFilter.LinearMipmapLinear; + case SamplerMinFilter.Nearest: + return MinFilter.NearestMipmapLinear; + case SamplerMinFilter.Linear: + return MinFilter.LinearMipmapLinear; } break; } @@ -190,7 +196,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the seamless cubemap flag. /// </summary> /// <returns>The seamless cubemap flag</returns> - public bool UnpackSeamlessCubemap() + public readonly bool UnpackSeamlessCubemap() { return (Word1 & (1 << 9)) != 0; } @@ -200,7 +206,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// This describes how the final value will be computed from neighbouring pixels. /// </summary> /// <returns>The reduction filter</returns> - public ReductionFilter UnpackReductionFilter() + public readonly ReductionFilter UnpackReductionFilter() { return (ReductionFilter)((Word1 >> 10) & 3); } @@ -211,7 +217,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// which mipmap level to use from a given texture. /// </summary> /// <returns>The level-of-detail bias value</returns> - public float UnpackMipLodBias() + public readonly float UnpackMipLodBias() { int fixedValue = (int)(Word1 >> 12) & 0x1fff; @@ -224,7 +230,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the level-of-detail snap value. /// </summary> /// <returns>The level-of-detail snap value</returns> - public float UnpackLodSnap() + public readonly float UnpackLodSnap() { return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f]; } @@ -233,7 +239,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the minimum level-of-detail value. /// </summary> /// <returns>The minimum level-of-detail value</returns> - public float UnpackMinLod() + public readonly float UnpackMinLod() { return (Word2 & 0xfff) * Frac8ToF32; } @@ -242,7 +248,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the maximum level-of-detail value. /// </summary> /// <returns>The maximum level-of-detail value</returns> - public float UnpackMaxLod() + public readonly float UnpackMaxLod() { return ((Word2 >> 12) & 0xfff) * Frac8ToF32; } diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs index 17beb1293..d3009219a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Graphics.Gpu.Image enum SamplerMinFilter { Nearest = 1, - Linear + Linear, } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs index 319d41960..b965f0c36 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Graphics.Gpu.Image { None = 1, Nearest, - Linear + Linear, } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs index eb7222f9c..3efcad760 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs @@ -159,4 +159,4 @@ namespace Ryujinx.Graphics.Gpu.Image item?.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs index 3b3350fb5..881c37af4 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs @@ -27,4 +27,4 @@ namespace Ryujinx.Graphics.Gpu.Image return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index a7af1aad7..c0d45cbd1 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Image private bool _modifiedStale = true; private ITexture _arrayViewTexture; - private Target _arrayViewTarget; + private Target _arrayViewTarget; private ITexture _flushHostTexture; private ITexture _setHostTexture; @@ -334,7 +334,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>The child texture</returns> public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, MultiRange range, int firstLayer, int firstLevel) { - Texture texture = new Texture( + Texture texture = new( _context, _physicalMemory, info, @@ -523,7 +523,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (ScaleFactor != scale) { - Logger.Debug?.Print(LogClass.Gpu, $"Rescaling {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()} to ({ScaleFactor} to {scale}). "); + Logger.Debug?.Print(LogClass.Gpu, $"Rescaling {Info.Width}x{Info.Height} {Info.FormatInfo.Format} to ({ScaleFactor} to {scale}). "); ScaleFactor = scale; @@ -537,7 +537,7 @@ namespace Ryujinx.Graphics.Gpu.Image foreach (var view in _views) { - Logger.Debug?.Print(LogClass.Gpu, $" Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()}."); + Logger.Debug?.Print(LogClass.Gpu, $" Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format}."); view.ScaleFactor = scale; TextureCreateInfo viewCreateInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, scale); @@ -1254,7 +1254,7 @@ namespace Ryujinx.Graphics.Gpu.Image { FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities); - TextureCreateInfo createInfo = new TextureCreateInfo( + TextureCreateInfo createInfo = new( Info.Width, Info.Height, target == Target.CubemapArray ? 6 : 1, @@ -1274,7 +1274,7 @@ namespace Ryujinx.Graphics.Gpu.Image ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0); _arrayViewTexture = viewTexture; - _arrayViewTarget = target; + _arrayViewTarget = target; return viewTexture; } @@ -1317,29 +1317,21 @@ namespace Ryujinx.Graphics.Gpu.Image { case Target.Texture1D: case Target.Texture1DArray: - return target == Target.Texture1D || - target == Target.Texture1DArray; - + return target == Target.Texture1D || target == Target.Texture1DArray; case Target.Texture2D: case Target.Texture2DArray: - return target == Target.Texture2D || - target == Target.Texture2DArray; - + return target == Target.Texture2D || target == Target.Texture2DArray; case Target.Cubemap: case Target.CubemapArray: - return target == Target.Cubemap || - target == Target.CubemapArray; - + return target == Target.Cubemap || target == Target.CubemapArray; case Target.Texture2DMultisample: case Target.Texture2DMultisampleArray: - return target == Target.Texture2DMultisample || - target == Target.Texture2DMultisampleArray; - + return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray; case Target.Texture3D: return target == Target.Texture3D; + default: + return false; } - - return false; } /// <summary> @@ -1398,7 +1390,7 @@ namespace Ryujinx.Graphics.Gpu.Image Height = info.Height; CanForceAnisotropy = CanTextureForceAnisotropy(); - _depth = info.GetDepth(); + _depth = info.GetDepth(); _layers = info.GetLayers(); } @@ -1714,4 +1706,4 @@ namespace Ryujinx.Graphics.Gpu.Image } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs index febe508be..606842d6d 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs @@ -50,12 +50,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param> public TextureBindingInfo(Target target, Format format, int binding, int cbufSlot, int handle, TextureUsageFlags flags) { - Target = target; - Format = format; - Binding = binding; + Target = target; + Format = format; + Binding = binding; CbufSlot = cbufSlot; - Handle = handle; - Flags = flags; + Handle = handle; + Flags = flags; } /// <summary> @@ -70,4 +70,4 @@ namespace Ryujinx.Graphics.Gpu.Image { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index b08fb3eb1..e5df17760 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -93,10 +93,10 @@ namespace Ryujinx.Graphics.Gpu.Image int stages = isCompute ? 1 : Constants.ShaderStages; _textureBindings = new TextureBindingInfo[stages][]; - _imageBindings = new TextureBindingInfo[stages][]; + _imageBindings = new TextureBindingInfo[stages][]; _textureState = new TextureState[InitialTextureStateSize]; - _imageState = new TextureState[InitialImageStateSize]; + _imageState = new TextureState[InitialImageStateSize]; for (int stage = 0; stage < stages; stage++) { @@ -418,6 +418,7 @@ namespace Ryujinx.Graphics.Gpu.Image } } +#pragma warning disable IDE0051 // Remove unused private member /// <summary> /// Counts the total number of texture bindings used by all shader stages. /// </summary> @@ -426,16 +427,17 @@ namespace Ryujinx.Graphics.Gpu.Image { int count = 0; - for (int i = 0; i < _textureBindings.Length; i++) + foreach (TextureBindingInfo[] textureInfo in _textureBindings) { - if (_textureBindings[i] != null) + if (textureInfo != null) { - count += _textureBindings[i].Length; + count += textureInfo.Length; } } return count; } +#pragma warning restore IDE0051 /// <summary> /// Ensures that the texture bindings are visible to the host GPU. diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index bccd3fd72..3f215a4ac 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Image } private const int OverlapsBufferInitialCapacity = 10; - private const int OverlapsBufferMaxCapacity = 10000; + private const int OverlapsBufferMaxCapacity = 10000; private readonly GpuContext _context; private readonly PhysicalMemory _physicalMemory; @@ -224,7 +224,7 @@ namespace Ryujinx.Graphics.Gpu.Image for (int i = 0; i < overlapCount; i++) { var other = _textureOverlaps[i]; - + if (texture != other && (texture.IsViewCompatible(other.Info, other.Range, true, other.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible || other.IsViewCompatible(texture.Info, texture.Range, true, texture.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible)) @@ -278,7 +278,7 @@ namespace Ryujinx.Graphics.Gpu.Image width = copyTexture.Width; } - TextureInfo info = new TextureInfo( + TextureInfo info = new( copyTexture.Address.Pack() + offset, GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout), copyTexture.Height, @@ -371,16 +371,16 @@ namespace Ryujinx.Graphics.Gpu.Image // so the width we get here is the aligned width. if (isLinear) { - width = colorState.WidthOrStride / formatInfo.BytesPerPixel; + width = colorState.WidthOrStride / formatInfo.BytesPerPixel; stride = colorState.WidthOrStride; } else { - width = colorState.WidthOrStride; + width = colorState.WidthOrStride; stride = 0; } - TextureInfo info = new TextureInfo( + TextureInfo info = new( colorState.Address.Pack(), GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear), colorState.Height, @@ -449,7 +449,7 @@ namespace Ryujinx.Graphics.Gpu.Image FormatInfo formatInfo = dsState.Format.Convert(); - TextureInfo info = new TextureInfo( + TextureInfo info = new( dsState.Address.Pack(), GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false), size.Height, @@ -1136,14 +1136,14 @@ namespace Ryujinx.Graphics.Gpu.Image } } - int width = info.Width / info.SamplesInX; + int width = info.Width / info.SamplesInX; int height = info.Height / info.SamplesInY; int depth = info.GetDepth() * info.GetLayers(); if (scale != 1f) { - width = (int)MathF.Ceiling(width * scale); + width = (int)MathF.Ceiling(width * scale); height = (int)MathF.Ceiling(height * scale); } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 9a8d048ed..eafa50b2e 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Image Astc10x8, Astc10x10, Astc12x10, - Astc12x12 + Astc12x12, } /// <summary> @@ -629,7 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Image { TextureMatchQuality.Perfect => TextureViewCompatibility.Full, TextureMatchQuality.FormatAlias => TextureViewCompatibility.FormatAlias, - _ => TextureViewCompatibility.Incompatible + _ => TextureViewCompatibility.Incompatible, }; } @@ -783,80 +783,33 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>Format class</returns> private static FormatClass GetFormatClass(Format format) { - switch (format) + return format switch { - case Format.Bc1RgbaSrgb: - case Format.Bc1RgbaUnorm: - return FormatClass.Bc1Rgba; - case Format.Bc2Srgb: - case Format.Bc2Unorm: - return FormatClass.Bc2; - case Format.Bc3Srgb: - case Format.Bc3Unorm: - return FormatClass.Bc3; - case Format.Bc4Snorm: - case Format.Bc4Unorm: - return FormatClass.Bc4; - case Format.Bc5Snorm: - case Format.Bc5Unorm: - return FormatClass.Bc5; - case Format.Bc6HSfloat: - case Format.Bc6HUfloat: - return FormatClass.Bc6; - case Format.Bc7Srgb: - case Format.Bc7Unorm: - return FormatClass.Bc7; - case Format.Etc2RgbSrgb: - case Format.Etc2RgbUnorm: - return FormatClass.Etc2Rgb; - case Format.Etc2RgbaSrgb: - case Format.Etc2RgbaUnorm: - return FormatClass.Etc2Rgba; - case Format.Astc4x4Srgb: - case Format.Astc4x4Unorm: - return FormatClass.Astc4x4; - case Format.Astc5x4Srgb: - case Format.Astc5x4Unorm: - return FormatClass.Astc5x4; - case Format.Astc5x5Srgb: - case Format.Astc5x5Unorm: - return FormatClass.Astc5x5; - case Format.Astc6x5Srgb: - case Format.Astc6x5Unorm: - return FormatClass.Astc6x5; - case Format.Astc6x6Srgb: - case Format.Astc6x6Unorm: - return FormatClass.Astc6x6; - case Format.Astc8x5Srgb: - case Format.Astc8x5Unorm: - return FormatClass.Astc8x5; - case Format.Astc8x6Srgb: - case Format.Astc8x6Unorm: - return FormatClass.Astc8x6; - case Format.Astc8x8Srgb: - case Format.Astc8x8Unorm: - return FormatClass.Astc8x8; - case Format.Astc10x5Srgb: - case Format.Astc10x5Unorm: - return FormatClass.Astc10x5; - case Format.Astc10x6Srgb: - case Format.Astc10x6Unorm: - return FormatClass.Astc10x6; - case Format.Astc10x8Srgb: - case Format.Astc10x8Unorm: - return FormatClass.Astc10x8; - case Format.Astc10x10Srgb: - case Format.Astc10x10Unorm: - return FormatClass.Astc10x10; - case Format.Astc12x10Srgb: - case Format.Astc12x10Unorm: - return FormatClass.Astc12x10; - case Format.Astc12x12Srgb: - case Format.Astc12x12Unorm: - return FormatClass.Astc12x12; - } - - return FormatClass.Unclassified; + Format.Bc1RgbaSrgb or Format.Bc1RgbaUnorm => FormatClass.Bc1Rgba, + Format.Bc2Srgb or Format.Bc2Unorm => FormatClass.Bc2, + Format.Bc3Srgb or Format.Bc3Unorm => FormatClass.Bc3, + Format.Bc4Snorm or Format.Bc4Unorm => FormatClass.Bc4, + Format.Bc5Snorm or Format.Bc5Unorm => FormatClass.Bc5, + Format.Bc6HSfloat or Format.Bc6HUfloat => FormatClass.Bc6, + Format.Bc7Srgb or Format.Bc7Unorm => FormatClass.Bc7, + Format.Etc2RgbSrgb or Format.Etc2RgbUnorm => FormatClass.Etc2Rgb, + Format.Etc2RgbaSrgb or Format.Etc2RgbaUnorm => FormatClass.Etc2Rgba, + Format.Astc4x4Srgb or Format.Astc4x4Unorm => FormatClass.Astc4x4, + Format.Astc5x4Srgb or Format.Astc5x4Unorm => FormatClass.Astc5x4, + Format.Astc5x5Srgb or Format.Astc5x5Unorm => FormatClass.Astc5x5, + Format.Astc6x5Srgb or Format.Astc6x5Unorm => FormatClass.Astc6x5, + Format.Astc6x6Srgb or Format.Astc6x6Unorm => FormatClass.Astc6x6, + Format.Astc8x5Srgb or Format.Astc8x5Unorm => FormatClass.Astc8x5, + Format.Astc8x6Srgb or Format.Astc8x6Unorm => FormatClass.Astc8x6, + Format.Astc8x8Srgb or Format.Astc8x8Unorm => FormatClass.Astc8x8, + Format.Astc10x5Srgb or Format.Astc10x5Unorm => FormatClass.Astc10x5, + Format.Astc10x6Srgb or Format.Astc10x6Unorm => FormatClass.Astc10x6, + Format.Astc10x8Srgb or Format.Astc10x8Unorm => FormatClass.Astc10x8, + Format.Astc10x10Srgb or Format.Astc10x10Unorm => FormatClass.Astc10x10, + Format.Astc12x10Srgb or Format.Astc12x10Unorm => FormatClass.Astc12x10, + Format.Astc12x12Srgb or Format.Astc12x12Unorm => FormatClass.Astc12x12, + _ => FormatClass.Unclassified, + }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs index 359069bcf..172d11a83 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs @@ -7,13 +7,13 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> enum TextureComponent { - Zero = 0, - Red = 2, + Zero = 0, + Red = 2, Green = 3, - Blue = 4, + Blue = 4, Alpha = 5, OneSI = 6, - OneF = 7 + OneF = 7, } static class TextureComponentConverter @@ -25,19 +25,16 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>Converted enum</returns> public static SwizzleComponent Convert(this TextureComponent component) { - switch (component) + return component switch { - case TextureComponent.Zero: return SwizzleComponent.Zero; - case TextureComponent.Red: return SwizzleComponent.Red; - case TextureComponent.Green: return SwizzleComponent.Green; - case TextureComponent.Blue: return SwizzleComponent.Blue; - case TextureComponent.Alpha: return SwizzleComponent.Alpha; - case TextureComponent.OneSI: - case TextureComponent.OneF: - return SwizzleComponent.One; - } - - return SwizzleComponent.Zero; + TextureComponent.Zero => SwizzleComponent.Zero, + TextureComponent.Red => SwizzleComponent.Red, + TextureComponent.Green => SwizzleComponent.Green, + TextureComponent.Blue => SwizzleComponent.Blue, + TextureComponent.Alpha => SwizzleComponent.Alpha, + TextureComponent.OneSI or TextureComponent.OneF => SwizzleComponent.One, + _ => SwizzleComponent.Zero, + }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs index 3e35f8d2c..c82a555ee 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> struct TextureDescriptor : ITextureDescriptor, IEquatable<TextureDescriptor> { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Word0; public uint Word1; public uint Word2; @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks Maxwell texture format integer. /// </summary> /// <returns>The texture format integer</returns> - public uint UnpackFormat() + public readonly uint UnpackFormat() { return Word0 & 0x8007ffff; } @@ -33,43 +33,43 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the swizzle component for the texture red color channel. /// </summary> /// <returns>The swizzle component</returns> - public TextureComponent UnpackSwizzleR() + public readonly TextureComponent UnpackSwizzleR() { - return(TextureComponent)((Word0 >> 19) & 7); + return (TextureComponent)((Word0 >> 19) & 7); } /// <summary> /// Unpacks the swizzle component for the texture green color channel. /// </summary> /// <returns>The swizzle component</returns> - public TextureComponent UnpackSwizzleG() + public readonly TextureComponent UnpackSwizzleG() { - return(TextureComponent)((Word0 >> 22) & 7); + return (TextureComponent)((Word0 >> 22) & 7); } /// <summary> /// Unpacks the swizzle component for the texture blue color channel. /// </summary> /// <returns>The swizzle component</returns> - public TextureComponent UnpackSwizzleB() + public readonly TextureComponent UnpackSwizzleB() { - return(TextureComponent)((Word0 >> 25) & 7); + return (TextureComponent)((Word0 >> 25) & 7); } /// <summary> /// Unpacks the swizzle component for the texture alpha color channel. /// </summary> /// <returns>The swizzle component</returns> - public TextureComponent UnpackSwizzleA() + public readonly TextureComponent UnpackSwizzleA() { - return(TextureComponent)((Word0 >> 28) & 7); + return (TextureComponent)((Word0 >> 28) & 7); } /// <summary> /// Unpacks the 40-bits texture GPU virtual address. /// </summary> /// <returns>The GPU virtual address</returns> - public ulong UnpackAddress() + public readonly ulong UnpackAddress() { return Word1 | ((ulong)(Word2 & 0xffff) << 32); } @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// This defines the texture layout, among other things. /// </summary> /// <returns>The texture descriptor type</returns> - public TextureDescriptorType UnpackTextureDescriptorType() + public readonly TextureDescriptorType UnpackTextureDescriptorType() { return (TextureDescriptorType)((Word2 >> 21) & 7); } @@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Always 32-bytes aligned. /// </summary> /// <returns>The linear texture stride</returns> - public int UnpackStride() + public readonly int UnpackStride() { return (int)(Word3 & 0xffff) << 5; } @@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Must be always 1, ignored by the GPU. /// </summary> /// <returns>THe GOB block X size</returns> - public int UnpackGobBlocksInX() + public readonly int UnpackGobBlocksInX() { return 1 << (int)(Word3 & 7); } @@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Must be always a power of 2, with a maximum value of 32. /// </summary> /// <returns>THe GOB block Y size</returns> - public int UnpackGobBlocksInY() + public readonly int UnpackGobBlocksInY() { return 1 << (int)((Word3 >> 3) & 7); } @@ -120,7 +120,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Must be 1 for any texture target other than 3D textures. /// </summary> /// <returns>The GOB block Z size</returns> - public int UnpackGobBlocksInZ() + public readonly int UnpackGobBlocksInZ() { return 1 << (int)((Word3 >> 6) & 7); } @@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// This is only used for sparse textures, should be 1 otherwise. /// </summary> /// <returns>The number of GOB blocks per tile</returns> - public int UnpackGobBlocksInTileX() + public readonly int UnpackGobBlocksInTileX() { return 1 << (int)((Word3 >> 10) & 7); } @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the number of mipmap levels of the texture. /// </summary> /// <returns>The number of mipmap levels</returns> - public int UnpackLevels() + public readonly int UnpackLevels() { return (int)(Word3 >> 28) + 1; } @@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpack the base level texture width size. /// </summary> /// <returns>The texture width</returns> - public int UnpackWidth() + public readonly int UnpackWidth() { return (int)(Word4 & 0xffff) + 1; } @@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpack the width of a buffer texture. /// </summary> /// <returns>The texture width</returns> - public int UnpackBufferTextureWidth() + public readonly int UnpackBufferTextureWidth() { return (int)((Word4 & 0xffff) | (Word3 << 16)) + 1; } @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the texture sRGB format flag. /// </summary> /// <returns>True if the texture is sRGB, false otherwise</returns> - public bool UnpackSrgb() + public readonly bool UnpackSrgb() { return (Word4 & (1 << 22)) != 0; } @@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the texture target. /// </summary> /// <returns>The texture target</returns> - public TextureTarget UnpackTextureTarget() + public readonly TextureTarget UnpackTextureTarget() { return (TextureTarget)((Word4 >> 23) & 0xf); } @@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Should be ignored for 1D or buffer textures. /// </summary> /// <returns>The texture height or layers count</returns> - public int UnpackHeight() + public readonly int UnpackHeight() { return (int)(Word5 & 0xffff) + 1; } @@ -195,7 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// The meaning of this value depends on the texture target. /// </summary> /// <returns>The texture depth, layer or faces count</returns> - public int UnpackDepth() + public readonly int UnpackDepth() { return (int)((Word5 >> 16) & 0x3fff) + 1; } @@ -207,7 +207,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// It must be set to false (by the guest driver) for rectangle textures. /// </summary> /// <returns>The texture coordinates normalized flag</returns> - public bool UnpackTextureCoordNormalized() + public readonly bool UnpackTextureCoordNormalized() { return (Word5 & (1 << 31)) != 0; } @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Unpacks the base mipmap level of the texture. /// </summary> /// <returns>The base mipmap level of the texture</returns> - public int UnpackBaseLevel() + public readonly int UnpackBaseLevel() { return (int)(Word7 & 0xf); } @@ -226,7 +226,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Usually equal to Levels minus 1. /// </summary> /// <returns>The maximum mipmap level (inclusive) of the texture</returns> - public int UnpackMaxLevelInclusive() + public readonly int UnpackMaxLevelInclusive() { return (int)((Word7 >> 4) & 0xf); } @@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Must be ignored for non-multisample textures. /// </summary> /// <returns>The multisample counts enum</returns> - public TextureMsaaMode UnpackTextureMsaaMode() + public readonly TextureMsaaMode UnpackTextureMsaaMode() { return (TextureMsaaMode)((Word7 >> 8) & 0xf); } @@ -269,5 +269,10 @@ namespace Ryujinx.Graphics.Gpu.Image { return Unsafe.As<TextureDescriptor, Vector256<byte>>(ref this).GetHashCode(); } + + public override bool Equals(object obj) + { + return obj is TextureDescriptor descriptor && Equals(descriptor); + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs index 8e7d40bbe..ad0715c58 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs @@ -11,6 +11,6 @@ namespace Ryujinx.Graphics.Gpu.Image LinearColorKey, Linear, BlockLinear, - BlockLinearColorKey + BlockLinearColorKey, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 2fa1e79e5..1b947cd3b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -78,11 +78,11 @@ namespace Ryujinx.Graphics.Gpu.Image private int[] _allOffsets; private int[] _sliceSizes; - private bool _is3D; + private readonly bool _is3D; private bool _hasMipViews; private bool _hasLayerViews; - private int _layers; - private int _levels; + private readonly int _layers; + private readonly int _levels; private MultiRange TextureRange => Storage.Range; @@ -96,9 +96,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// Other texture groups that have incompatible overlaps with this one. /// </summary> - private List<TextureIncompatibleOverlap> _incompatibleOverlaps; + private readonly List<TextureIncompatibleOverlap> _incompatibleOverlaps; private bool _incompatibleOverlapsDirty = true; - private bool _flushIncompatibleOverlaps; + private readonly bool _flushIncompatibleOverlaps; private BufferHandle _flushBuffer; private bool _flushBufferImported; @@ -423,7 +423,7 @@ namespace Ryujinx.Graphics.Gpu.Image int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level); int offset = _allOffsets[offsetIndex]; - ReadOnlySpan<byte> data = dataSpan.Slice(offset - spanBase); + ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..]; SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); @@ -1500,13 +1500,13 @@ namespace Ryujinx.Graphics.Gpu.Image { for (int i = 0; i < _allOffsets.Length; i++) { - (int layer, int level) = GetLayerLevelForView(i); + (_, int level) = GetLayerLevelForView(i); MultiRange handleRange = Storage.Range.Slice((ulong)_allOffsets[i], 1); ulong handleBase = handleRange.GetSubRange(0).Address; for (int j = 0; j < other._handles.Length; j++) { - (int otherLayer, int otherLevel) = other.GetLayerLevelForView(j); + (_, int otherLevel) = other.GetLayerLevelForView(j); MultiRange otherHandleRange = other.Storage.Range.Slice((ulong)other._allOffsets[j], 1); ulong otherHandleBase = otherHandleRange.GetSubRange(0).Address; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index da8dd849d..ef7198e88 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -22,10 +22,10 @@ namespace Ryujinx.Graphics.Gpu.Image private const int FlushBalanceMax = 60; private const int FlushBalanceMin = -10; - private TextureGroup _group; + private readonly TextureGroup _group; private int _bindCount; - private int _firstLevel; - private int _firstLayer; + private readonly int _firstLevel; + private readonly int _firstLayer; // Sync state for texture flush. @@ -463,8 +463,8 @@ namespace Ryujinx.Graphics.Gpu.Image _group.HasCopyDependencies = true; other._group.HasCopyDependencies = true; - TextureDependency dependency = new TextureDependency(this); - TextureDependency otherDependency = new TextureDependency(other); + TextureDependency dependency = new(this); + TextureDependency otherDependency = new(other); dependency.Other = otherDependency; otherDependency.Other = dependency; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs index 1994d2263..94d2e0bfc 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs @@ -134,45 +134,45 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="swizzleB">Swizzle for the blue color channel</param> /// <param name="swizzleA">Swizzle for the alpha color channel</param> public TextureInfo( - ulong gpuAddress, - int width, - int height, - int depthOrLayers, - int levels, - int samplesInX, - int samplesInY, - int stride, - bool isLinear, - int gobBlocksInY, - int gobBlocksInZ, - int gobBlocksInTileX, - Target target, - FormatInfo formatInfo, + ulong gpuAddress, + int width, + int height, + int depthOrLayers, + int levels, + int samplesInX, + int samplesInY, + int stride, + bool isLinear, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX, + Target target, + FormatInfo formatInfo, DepthStencilMode depthStencilMode = DepthStencilMode.Depth, - SwizzleComponent swizzleR = SwizzleComponent.Red, - SwizzleComponent swizzleG = SwizzleComponent.Green, - SwizzleComponent swizzleB = SwizzleComponent.Blue, - SwizzleComponent swizzleA = SwizzleComponent.Alpha) + SwizzleComponent swizzleR = SwizzleComponent.Red, + SwizzleComponent swizzleG = SwizzleComponent.Green, + SwizzleComponent swizzleB = SwizzleComponent.Blue, + SwizzleComponent swizzleA = SwizzleComponent.Alpha) { - GpuAddress = gpuAddress; - Width = width; - Height = height; - DepthOrLayers = depthOrLayers; - Levels = levels; - SamplesInX = samplesInX; - SamplesInY = samplesInY; - Stride = stride; - IsLinear = isLinear; - GobBlocksInY = gobBlocksInY; - GobBlocksInZ = gobBlocksInZ; + GpuAddress = gpuAddress; + Width = width; + Height = height; + DepthOrLayers = depthOrLayers; + Levels = levels; + SamplesInX = samplesInX; + SamplesInY = samplesInY; + Stride = stride; + IsLinear = isLinear; + GobBlocksInY = gobBlocksInY; + GobBlocksInZ = gobBlocksInZ; GobBlocksInTileX = gobBlocksInTileX; - Target = target; - FormatInfo = formatInfo; + Target = target; + FormatInfo = formatInfo; DepthStencilMode = depthStencilMode; - SwizzleR = swizzleR; - SwizzleG = swizzleG; - SwizzleB = swizzleB; - SwizzleA = swizzleA; + SwizzleR = swizzleR; + SwizzleG = swizzleG; + SwizzleB = swizzleB; + SwizzleA = swizzleA; } /// <summary> @@ -318,17 +318,17 @@ namespace Ryujinx.Graphics.Gpu.Image // - If the parent format is not compressed, and the view is, the view // size is calculated as described on the first point, but the width and height // of the view must be also multiplied by the block width and height. - int width = Math.Max(1, parent.Info.Width >> firstLevel); + int width = Math.Max(1, parent.Info.Width >> firstLevel); int height = Math.Max(1, parent.Info.Height >> firstLevel); if (parent.Info.FormatInfo.IsCompressed && !FormatInfo.IsCompressed) { - width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth); + width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth); height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight); } else if (!parent.Info.FormatInfo.IsCompressed && FormatInfo.IsCompressed) { - width *= FormatInfo.BlockWidth; + width *= FormatInfo.BlockWidth; height *= FormatInfo.BlockHeight; } @@ -408,4 +408,4 @@ namespace Ryujinx.Graphics.Gpu.Image SwizzleA); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 266f62856..63b9b47c3 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -41,8 +41,8 @@ namespace Ryujinx.Graphics.Gpu.Image _context = context; _channel = channel; - TexturePoolCache texturePoolCache = new TexturePoolCache(context); - SamplerPoolCache samplerPoolCache = new SamplerPoolCache(context); + TexturePoolCache texturePoolCache = new(context); + SamplerPoolCache samplerPoolCache = new(context); float[] scales = new float[64]; new Span<float>(scales).Fill(1f); @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> /// <param name="texture">The texture to check</param> /// <returns>True if the scale needs updating, false if the scale is up to date</returns> - private bool ScaleNeedsUpdated(Texture texture) + private static bool ScaleNeedsUpdated(Texture texture) { return texture != null && !(texture.ScaleMode == TextureScaleMode.Blacklisted || texture.ScaleMode == TextureScaleMode.Undesired) && texture.ScaleFactor != GraphicsConfig.ResScale; } @@ -234,7 +234,11 @@ namespace Ryujinx.Graphics.Gpu.Image void ConsiderTarget(Texture target) { - if (target == null) return; + if (target == null) + { + return; + } + float scale = target.ScaleFactor; switch (target.ScaleMode) @@ -445,7 +449,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// </remarks> public void UpdateRenderTargetDepthStencil() { - new Span<ITexture>(_rtHostColors).Fill(null); + new Span<ITexture>(_rtHostColors).Clear(); _rtHostDs = _rtDepthStencil?.HostTexture; _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs); diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs index 1351bf242..67835e954 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs @@ -4,6 +4,6 @@ { NoMatch, FormatAlias, - Perfect + Perfect, } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs index 0461888f1..43b83ae18 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Image Ms2x2 = 2, Ms4x2 = 4, Ms2x1 = 5, - Ms4x4 = 6 + Ms4x4 = 6, } static class TextureMsaaModeConverter @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureMsaaMode.Ms2x2 => 4, TextureMsaaMode.Ms4x2 => 8, TextureMsaaMode.Ms4x4 => 16, - _ => 1 + _ => 1, }; } @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureMsaaMode.Ms2x2 => 2, TextureMsaaMode.Ms4x2 => 4, TextureMsaaMode.Ms4x4 => 4, - _ => 1 + _ => 1, }; } @@ -61,8 +61,8 @@ namespace Ryujinx.Graphics.Gpu.Image TextureMsaaMode.Ms2x2 => 2, TextureMsaaMode.Ms4x2 => 2, TextureMsaaMode.Ms4x4 => 4, - _ => 1 + _ => 1, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index bade9bbb3..0fdb6cd64 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// A request to dereference a texture from a pool. /// </summary> - private struct DereferenceRequest + private readonly struct DereferenceRequest { /// <summary> /// Whether the dereference is due to a mapping change or not. @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Image } private readonly GpuChannel _channel; - private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new ConcurrentQueue<DereferenceRequest>(); + private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new(); private TextureDescriptor _defaultDescriptor; /// <summary> @@ -379,10 +379,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="descriptor">The texture descriptor</param> /// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param> /// <returns>The texture information</returns> - private TextureInfo GetInfo(in TextureDescriptor descriptor, out int layerSize) + private static TextureInfo GetInfo(in TextureDescriptor descriptor, out int layerSize) { int depthOrLayers = descriptor.UnpackDepth(); - int levels = descriptor.UnpackLevels(); + int levels = descriptor.UnpackLevels(); TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode(); @@ -424,7 +424,7 @@ namespace Ryujinx.Graphics.Gpu.Image } uint format = descriptor.UnpackFormat(); - bool srgb = descriptor.UnpackSrgb(); + bool srgb = descriptor.UnpackSrgb(); ulong gpuVa = descriptor.UnpackAddress(); @@ -451,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Image // Linear textures don't support mipmaps, so we don't handle this case here. if ((minLod != 0 || maxLod + 1 != levels) && target != Target.TextureBuffer && !isLinear) { - int depth = TextureInfo.GetDepth(target, depthOrLayers); + int depth = TextureInfo.GetDepth(target, depthOrLayers); int layers = TextureInfo.GetLayers(target, depthOrLayers); SizeInfo sizeInfo = SizeCalculator.GetBlockLinearTextureSize( @@ -476,7 +476,7 @@ namespace Ryujinx.Graphics.Gpu.Image // address if there is a overlapping texture on the cache that can contain the new texture. gpuVa += (ulong)sizeInfo.GetMipOffset(minLod); - width = Math.Max(1, width >> minLod); + width = Math.Max(1, width >> minLod); height = Math.Max(1, height >> minLod); if (target == Target.Texture3D) @@ -608,4 +608,4 @@ namespace Ryujinx.Graphics.Gpu.Image base.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs index 0017f4cc5..5da2c439c 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs @@ -27,4 +27,4 @@ namespace Ryujinx.Graphics.Gpu.Image return new TexturePool(context, channel, address, maximumId); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs index b937f5778..5d8e14db3 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs @@ -11,6 +11,6 @@ Eligible = 0, Scaled = 1, Blacklisted = 2, - Undesired = 3 + Undesired = 3, } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs index d7b99a173..fb2a97b0b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs @@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.Gpu.Image [Flags] enum TextureSearchFlags { - None = 0, - ForSampler = 1 << 1, - ForCopy = 1 << 2, - DepthAlias = 1 << 3, + None = 0, + ForSampler = 1 << 1, + ForCopy = 1 << 2, + DepthAlias = 1 << 3, WithUpscale = 1 << 4, - NoCreate = 1 << 5 + NoCreate = 1 << 5, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs index 5e0a0721f..b46b42046 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Image Texture2DArray, TextureBuffer, Texture2DRect, - CubemapArray + CubemapArray, } static class TextureTargetConverter @@ -33,23 +33,34 @@ namespace Ryujinx.Graphics.Gpu.Image { switch (target) { - case TextureTarget.Texture2D: return Target.Texture2DMultisample; - case TextureTarget.Texture2DArray: return Target.Texture2DMultisampleArray; + case TextureTarget.Texture2D: + return Target.Texture2DMultisample; + case TextureTarget.Texture2DArray: + return Target.Texture2DMultisampleArray; } } else { switch (target) { - case TextureTarget.Texture1D: return Target.Texture1D; - case TextureTarget.Texture2D: return Target.Texture2D; - case TextureTarget.Texture2DRect: return Target.Texture2D; - case TextureTarget.Texture3D: return Target.Texture3D; - case TextureTarget.Texture1DArray: return Target.Texture1DArray; - case TextureTarget.Texture2DArray: return Target.Texture2DArray; - case TextureTarget.Cubemap: return Target.Cubemap; - case TextureTarget.CubemapArray: return Target.CubemapArray; - case TextureTarget.TextureBuffer: return Target.TextureBuffer; + case TextureTarget.Texture1D: + return Target.Texture1D; + case TextureTarget.Texture2D: + return Target.Texture2D; + case TextureTarget.Texture2DRect: + return Target.Texture2D; + case TextureTarget.Texture3D: + return Target.Texture3D; + case TextureTarget.Texture1DArray: + return Target.Texture1DArray; + case TextureTarget.Texture2DArray: + return Target.Texture2DArray; + case TextureTarget.Cubemap: + return Target.Cubemap; + case TextureTarget.CubemapArray: + return Target.CubemapArray; + case TextureTarget.TextureBuffer: + return Target.TextureBuffer; } } @@ -65,17 +76,17 @@ namespace Ryujinx.Graphics.Gpu.Image { return target switch { - TextureTarget.Texture1D => SamplerType.Texture1D, - TextureTarget.Texture2D => SamplerType.Texture2D, - TextureTarget.Texture3D => SamplerType.Texture3D, - TextureTarget.Cubemap => SamplerType.TextureCube, + TextureTarget.Texture1D => SamplerType.Texture1D, + TextureTarget.Texture2D => SamplerType.Texture2D, + TextureTarget.Texture3D => SamplerType.Texture3D, + TextureTarget.Cubemap => SamplerType.TextureCube, TextureTarget.Texture1DArray => SamplerType.Texture1D | SamplerType.Array, TextureTarget.Texture2DArray => SamplerType.Texture2D | SamplerType.Array, - TextureTarget.TextureBuffer => SamplerType.TextureBuffer, - TextureTarget.Texture2DRect => SamplerType.Texture2D, - TextureTarget.CubemapArray => SamplerType.TextureCube | SamplerType.Array, - _ => SamplerType.Texture2D + TextureTarget.TextureBuffer => SamplerType.TextureBuffer, + TextureTarget.Texture2DRect => SamplerType.Texture2D, + TextureTarget.CubemapArray => SamplerType.TextureCube | SamplerType.Array, + _ => SamplerType.Texture2D, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs index dfa688c45..3f3bfdfef 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs @@ -10,6 +10,6 @@ LayoutIncompatible, CopyOnly, FormatAlias, - Full + Full, } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index dc5037c56..e27c14a16 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private int _sequenceNumber; - private bool _useGranular; + private readonly bool _useGranular; private bool _syncActionRegistered; private int _referenceCount = 1; @@ -80,10 +80,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="baseBuffers">Buffers which this buffer contains, and will inherit tracking handles from</param> public Buffer(GpuContext context, PhysicalMemory physicalMemory, ulong address, ulong size, IEnumerable<Buffer> baseBuffers = null) { - _context = context; + _context = context; _physicalMemory = physicalMemory; - Address = address; - Size = size; + Address = address; + Size = size; Handle = context.Renderer.CreateBuffer((int)size, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null); @@ -252,10 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </summary> private void EnsureRangeList() { - if (_modifiedRanges == null) - { - _modifiedRanges = new BufferModifiedRangeList(_context, this, Flush); - } + _modifiedRanges ??= new BufferModifiedRangeList(_context, this, Flush); } /// <summary> @@ -326,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _syncActionRegistered = true; } - Action<ulong, ulong> registerRangeAction = (ulong address, ulong size) => + void registerRangeAction(ulong address, ulong size) { if (_useGranular) { @@ -336,7 +333,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { _memoryTracking.RegisterAction(_externalFlushDelegate); } - }; + } EnsureRangeList(); @@ -643,4 +640,4 @@ namespace Ryujinx.Graphics.Gpu.Memory DecrementReferenceCount(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs index d513b7adf..a9ea04cef 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs @@ -35,4 +35,4 @@ namespace Ryujinx.Graphics.Gpu.Memory Flags = flags; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index a5a9b75e9..99c571ba5 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Gpu.Memory class BufferCache : IDisposable { private const int OverlapsBufferInitialCapacity = 10; - private const int OverlapsBufferMaxCapacity = 10000; + private const int OverlapsBufferMaxCapacity = 10000; private const ulong BufferAlignmentSize = 0x1000; private const ulong BufferAlignmentMask = BufferAlignmentSize - 1; @@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { Buffer buffer = _bufferOverlaps[index]; - address = Math.Min(address, buffer.Address); + address = Math.Min(address, buffer.Address); endAddress = Math.Max(endAddress, buffer.EndAddress); lock (_buffers) @@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong newSize = endAddress - address; - Buffer newBuffer = new Buffer(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount)); + Buffer newBuffer = new(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount)); lock (_buffers) { @@ -285,7 +285,7 @@ namespace Ryujinx.Graphics.Gpu.Memory else { // No overlap, just create a new buffer. - Buffer buffer = new Buffer(_context, _physicalMemory, address, size); + Buffer buffer = new(_context, _physicalMemory, address, size); lock (_buffers) { @@ -446,7 +446,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </summary> /// <param name="dictionary">Dictionary to prune</param> /// <param name="toDelete">List used to track entries to delete</param> - private void Prune(Dictionary<ulong, BufferCacheEntry> dictionary, ref List<ulong> toDelete) + private static void Prune(Dictionary<ulong, BufferCacheEntry> dictionary, ref List<ulong> toDelete) { foreach (var entry in dictionary) { @@ -504,4 +504,4 @@ namespace Ryujinx.Graphics.Gpu.Memory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 07429cfe8..4cd3710b9 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly BuffersPerStage[] _gpUniformBuffers; private BufferHandle _tfInfoBuffer; - private int[] _tfInfoData; + private readonly int[] _tfInfoData; private bool _gpStorageBuffersDirty; private bool _gpUniformBuffersDirty; @@ -777,11 +777,11 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (isStorage) { - _context.Renderer.Pipeline.SetStorageBuffers(ranges.Slice(0, count)); + _context.Renderer.Pipeline.SetStorageBuffers(ranges[..count]); } else { - _context.Renderer.Pipeline.SetUniformBuffers(ranges.Slice(0, count)); + _context.Renderer.Pipeline.SetUniformBuffers(ranges[..count]); } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index 03504b11f..160a9aff7 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -1,5 +1,4 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Common.Pools; +using Ryujinx.Common.Pools; using Ryujinx.Memory.Range; using System; using System.Collections.Generic; @@ -71,9 +70,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { private const int BackingInitialSize = 8; - private GpuContext _context; - private Buffer _parent; - private Action<ulong, ulong> _flushAction; + private readonly GpuContext _context; + private readonly Buffer _parent; + private readonly Action<ulong, ulong> _flushAction; private List<BufferMigration> _sources; private BufferMigration _migrationTarget; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs index e763a899c..6dcc52cb6 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs @@ -34,11 +34,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Adds a new counter to the counter cache, or updates a existing one. /// </summary> /// <param name="gpuVa">GPU virtual address where the counter will be written in memory</param> + /// <param name="evt">The new counter</param> public void AddOrUpdate(ulong gpuVa, ICounterEvent evt) { int index = BinarySearch(gpuVa); - CounterEntry entry = new CounterEntry(gpuVa, evt); + CounterEntry entry = new(gpuVa, evt); if (index < 0) { @@ -127,7 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _items[index].Event?.Flush(); return true; - } + } else { return false; @@ -168,7 +169,7 @@ namespace Ryujinx.Graphics.Gpu.Memory int middle = left + (range >> 1); - CounterEntry item = _items[middle]; + CounterEntry item = _items[middle]; if (item.Address == address) { diff --git a/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs index 7765e8994..c72fa50e5 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs @@ -12,4 +12,4 @@ namespace Ryujinx.Graphics.Gpu.Memory public IndexType Type; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index c7a138c98..6af12de11 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -14,15 +14,15 @@ namespace Ryujinx.Graphics.Gpu.Memory { private const int PtLvl0Bits = 14; private const int PtLvl1Bits = 14; - public const int PtPageBits = 12; + public const int PtPageBits = 12; private const ulong PtLvl0Size = 1UL << PtLvl0Bits; private const ulong PtLvl1Size = 1UL << PtLvl1Bits; - public const ulong PageSize = 1UL << PtPageBits; + public const ulong PageSize = 1UL << PtPageBits; private const ulong PtLvl0Mask = PtLvl0Size - 1; private const ulong PtLvl1Mask = PtLvl1Size - 1; - public const ulong PageMask = PageSize - 1; + public const ulong PageMask = PageSize - 1; private const int PtLvl0Bit = PtPageBits + PtLvl1Bits; private const int PtLvl1Bit = PtPageBits; @@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Memory size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); - Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(0, size)); + Physical.GetSpan(pa, size, tracked).CopyTo(data[..size]); offset += size; } @@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Gpu.Memory size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); - writeCallback(pa, data.Slice(0, size)); + writeCallback(pa, data[..size]); offset += size; } @@ -345,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (pa != PteUnmapped && Physical.IsMapped(pa)) { - Physical.Write(pa, data.Slice(0, size)); + Physical.Write(pa, data[..size]); } offset += size; @@ -370,7 +370,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// These must run after the mapping completes. /// </summary> /// <param name="e">Event with remap actions</param> - private void RunRemapActions(UnmapEventArgs e) + private static void RunRemapActions(UnmapEventArgs e) { if (e.RemapActions != null) { @@ -759,4 +759,4 @@ namespace Ryujinx.Graphics.Gpu.Memory return pte & 0xffffffffffffffUL; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index cb95b04a8..d0b4478e1 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Gpu.Memory class PhysicalMemory : IDisposable { private readonly GpuContext _context; - private IVirtualMemoryManagerTracked _cpuMemory; + private readonly IVirtualMemoryManagerTracked _cpuMemory; private int _referenceCount; /// <summary> @@ -438,4 +438,4 @@ namespace Ryujinx.Graphics.Gpu.Memory DecrementReferenceCount(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PteKind.cs b/src/Ryujinx.Graphics.Gpu/Memory/PteKind.cs index 4ceb6bcf4..1585328f0 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PteKind.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PteKind.cs @@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.Memory X8C24 = 0xfc, PitchNoSwizzle = 0xfd, SmSkedMessage = 0xca, - SmHostMessage = 0xcb + SmHostMessage = 0xcb, } static class PteKindExtensions @@ -265,4 +265,4 @@ namespace Ryujinx.Graphics.Gpu.Memory return kind == PteKind.Pitch || kind == PteKind.PitchNoSwizzle; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs b/src/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs index 55d697b81..5d2ada566 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs @@ -8,6 +8,6 @@ namespace Ryujinx.Graphics.Gpu.Memory None, Buffer, Texture, - Pool + Pool, } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs index 8f0891251..ac334881d 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { public ulong Address; public ulong Size; - public int Stride; - public int Divisor; + public int Stride; + public int Divisor; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs index 22b08dd5a..2381991dd 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderStage.cs @@ -35,4 +35,4 @@ namespace Ryujinx.Graphics.Gpu.Shader Cb1Data = cb1Data; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs index a67182112..0119a6a33 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ComputeShaderCacheHashTable.cs @@ -50,8 +50,9 @@ namespace Ryujinx.Graphics.Gpu.Shader out byte[] cachedGuestCode) { program = null; - ShaderCodeAccessor codeAccessor = new ShaderCodeAccessor(channel.MemoryManager, gpuVa); + ShaderCodeAccessor codeAccessor = new(channel.MemoryManager, gpuVa); bool hasSpecList = _cache.TryFindItem(codeAccessor, out var specList, out cachedGuestCode); + return hasSpecList && specList.TryFindForCompute(channel, poolState, computeState, out program); } @@ -67,4 +68,4 @@ namespace Ryujinx.Graphics.Gpu.Shader } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs index 568fe9683..e0f17ba9c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <summary> /// Operation to add a shader to the cache. /// </summary> - AddShader + AddShader, } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs index 50e37033e..b08c44d67 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs @@ -29,12 +29,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// </summary> /// <typeparam name="T">Type of the data</typeparam> /// <param name="data">Data read</param> - public void Read<T>(ref T data) where T : unmanaged + public readonly void Read<T>(ref T data) where T : unmanaged { Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1)); for (int offset = 0; offset < buffer.Length;) { - offset += _activeStream.Read(buffer.Slice(offset)); + offset += _activeStream.Read(buffer[offset..]); } } @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <typeparam name="T">Type of the data</typeparam> /// <param name="data">Data read</param> /// <returns>True if the read was successful, false otherwise</returns> - public bool TryRead<T>(ref T data) where T : unmanaged + public readonly bool TryRead<T>(ref T data) where T : unmanaged { // Length is unknown on compressed streams. if (_activeStream == _stream) @@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <typeparam name="T">Type of the data</typeparam> /// <param name="data">Data read</param> /// <param name="magic">Expected magic value, for validation</param> - public void ReadWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged + public readonly void ReadWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged { uint actualMagic = 0; int size = 0; @@ -84,10 +84,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedInvalidLength); } - Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1)).Slice(0, size); + Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1))[..size]; for (int offset = 0; offset < buffer.Length;) { - offset += _activeStream.Read(buffer.Slice(offset)); + offset += _activeStream.Read(buffer[offset..]); } } @@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// </summary> /// <typeparam name="T">Type of the data</typeparam> /// <param name="data">Data to be written</param> - public void Write<T>(ref T data) where T : unmanaged + public readonly void Write<T>(ref T data) where T : unmanaged { Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1)); _activeStream.Write(buffer); @@ -108,7 +108,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <typeparam name="T">Type of the data</typeparam> /// <param name="data">Data to write</param> /// <param name="magic">Magic value to write</param> - public void WriteWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged + public readonly void WriteWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged { int size = Unsafe.SizeOf<T>(); Write(ref magic); @@ -183,7 +183,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache stream = new DeflateStream(stream, CompressionMode.Decompress, true); for (int offset = 0; offset < data.Length;) { - offset += stream.Read(data.Slice(offset)); + offset += stream.Read(data[offset..]); } stream.Dispose(); break; @@ -213,4 +213,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs index a46e1ef76..96ddbb513 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs @@ -13,6 +13,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <summary> /// Deflate compression (RFC 1951). /// </summary> - Deflate + Deflate, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs index c8a9f7ff2..c4ce0b870 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs @@ -54,4 +54,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return CompressionAlgorithm.Deflate; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 537cead0e..7f01aca63 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private readonly ShaderSpecializationState _newSpecState; private readonly int _stageIndex; private readonly bool _isVulkan; - private readonly ResourceCounts _resourceCounts; /// <summary> /// Creates a new instance of the cached GPU state accessor for shader translation. @@ -45,7 +44,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache _newSpecState = newSpecState; _stageIndex = stageIndex; _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; - _resourceCounts = counts; } /// <inheritdoc/> @@ -56,7 +54,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache throw new DiskCacheLoadException(DiskCacheLoadResult.InvalidCb1DataLength); } - return MemoryMarshal.Cast<byte, uint>(_cb1Data.Span.Slice(offset))[0]; + return MemoryMarshal.Cast<byte, uint>(_cb1Data.Span[offset..])[0]; } /// <inheritdoc/> @@ -68,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <inheritdoc/> public ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize) { - return MemoryMarshal.Cast<byte, ulong>(_data.Span.Slice((int)address)); + return MemoryMarshal.Cast<byte, ulong>(_data.Span[(int)address..]); } /// <inheritdoc/> @@ -94,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater, CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual, CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual, - _ => AlphaTestOp.Always + _ => AlphaTestOp.Always, }; } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs index 01034b495..59d2cfb3f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs @@ -205,10 +205,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache if (guestCode == null || cb1Data == null) { - BinarySerializer tocReader = new BinarySerializer(tocFileStream); + BinarySerializer tocReader = new(tocFileStream); tocFileStream.Seek(Unsafe.SizeOf<TocHeader>() + index * Unsafe.SizeOf<TocEntry>(), SeekOrigin.Begin); - TocEntry entry = new TocEntry(); + TocEntry entry = new(); tocReader.Read(ref entry); guestCode = new byte[entry.CodeSize]; @@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, TocFileName, writable: true); using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, DataFileName, writable: true); - TocHeader header = new TocHeader(); + TocHeader header = new(); LoadOrCreateToc(tocFileStream, ref header); @@ -299,7 +299,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <param name="header">Set to the TOC file header</param> private void LoadOrCreateToc(Stream tocFileStream, ref TocHeader header) { - BinarySerializer reader = new BinarySerializer(tocFileStream); + BinarySerializer reader = new(tocFileStream); if (!reader.TryRead(ref header) || header.Magic != TocMagic || header.Version != VersionPacked) { @@ -322,9 +322,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// </summary> /// <param name="tocFileStream">Guest TOC file stream</param> /// <param name="header">Set to the TOC header</param> - private void CreateToc(Stream tocFileStream, ref TocHeader header) + private static void CreateToc(Stream tocFileStream, ref TocHeader header) { - BinarySerializer writer = new BinarySerializer(tocFileStream); + BinarySerializer writer = new(tocFileStream); header.Magic = TocMagic; header.Version = VersionPacked; @@ -352,7 +352,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { _toc = new Dictionary<uint, List<TocMemoryEntry>>(); - TocEntry entry = new TocEntry(); + TocEntry entry = new(); int index = 0; while (tocFileStream.Position < tocFileStream.Length) @@ -386,7 +386,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache ReadOnlySpan<byte> cb1Data, uint hash) { - BinarySerializer tocWriter = new BinarySerializer(tocFileStream); + BinarySerializer tocWriter = new(tocFileStream); dataFileStream.Seek(0, SeekOrigin.End); uint dataOffset = checked((uint)dataFileStream.Position); @@ -399,12 +399,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache tocFileStream.Seek(0, SeekOrigin.Begin); tocWriter.Write(ref header); - TocEntry entry = new TocEntry() + TocEntry entry = new() { Offset = dataOffset, CodeSize = codeSize, Cb1DataSize = cb1DataSize, - Hash = hash + Hash = hash, }; tocFileStream.Seek(0, SeekOrigin.End); @@ -456,4 +456,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return (uint)XXHash128.ComputeHash(data).Low; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 267112865..95a0a6bdd 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache int indexOfSpace = fileName.IndexOf(' '); if (indexOfSpace >= 0) { - fileName = fileName.Substring(0, indexOfSpace); + fileName = fileName[..indexOfSpace]; } return string.Concat(fileName.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries)); @@ -287,10 +287,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache using var guestTocFileStream = _guestStorage.OpenTocFileStream(); using var guestDataFileStream = _guestStorage.OpenDataFileStream(); - BinarySerializer tocReader = new BinarySerializer(tocFileStream); - BinarySerializer dataReader = new BinarySerializer(dataFileStream); + BinarySerializer tocReader = new(tocFileStream); + BinarySerializer dataReader = new(dataFileStream); - TocHeader header = new TocHeader(); + TocHeader header = new(); if (!tocReader.TryRead(ref header) || header.Magic != TocsMagic) { @@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache int programIndex = 0; - DataEntry entry = new DataEntry(); + DataEntry entry = new(); while (tocFileStream.Position < tocFileStream.Length && loader.Active) { @@ -337,7 +337,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache GuestCodeAndCbData?[] guestShaders = new GuestCodeAndCbData?[isCompute ? 1 : Constants.ShaderStages + 1]; - DataEntryPerStage stageEntry = new DataEntryPerStage(); + DataEntryPerStage stageEntry = new(); while (stagesBitMask != 0) { @@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo); } - CachedShaderProgram program = new CachedShaderProgram(hostProgram, specState, shaders); + CachedShaderProgram program = new(hostProgram, specState, shaders); loader.QueueHostProgram(program, hostCode, programIndex, isCompute); } @@ -448,9 +448,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: false); dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: false); - BinarySerializer tempTocReader = new BinarySerializer(tocFileStream); + BinarySerializer tempTocReader = new(tocFileStream); - TocHeader header = new TocHeader(); + TocHeader header = new(); tempTocReader.Read(ref header); @@ -473,9 +473,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache tocFileStream.Seek(offset, SeekOrigin.Begin); - BinarySerializer tocReader = new BinarySerializer(tocFileStream); + BinarySerializer tocReader = new(tocFileStream); - OffsetAndSize offsetAndSize = new OffsetAndSize(); + OffsetAndSize offsetAndSize = new(); tocReader.Read(ref offsetAndSize); if (offsetAndSize.Offset >= (ulong)dataFileStream.Length) @@ -490,7 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache BinarySerializer.ReadCompressed(dataFileStream, hostCode); CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length]; - BinarySerializer dataReader = new BinarySerializer(dataFileStream); + BinarySerializer dataReader = new(dataFileStream); dataFileStream.Seek((long)(offsetAndSize.Offset + offsetAndSize.CompressedSize), SeekOrigin.Begin); @@ -559,27 +559,28 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache if (tocFileStream.Length == 0) { - TocHeader header = new TocHeader(); + TocHeader header = new(); CreateToc(tocFileStream, ref header, TocsMagic, CodeGenVersion, timestamp); } tocFileStream.Seek(0, SeekOrigin.End); dataFileStream.Seek(0, SeekOrigin.End); - BinarySerializer tocWriter = new BinarySerializer(tocFileStream); - BinarySerializer dataWriter = new BinarySerializer(dataFileStream); + BinarySerializer tocWriter = new(tocFileStream); + BinarySerializer dataWriter = new(dataFileStream); ulong dataOffset = (ulong)dataFileStream.Position; tocWriter.Write(ref dataOffset); - DataEntry entry = new DataEntry(); - - entry.StagesBitMask = stagesBitMask; + DataEntry entry = new() + { + StagesBitMask = stagesBitMask, + }; dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm()); dataWriter.Write(ref entry); - DataEntryPerStage stageEntry = new DataEntryPerStage(); + DataEntryPerStage stageEntry = new(); for (int index = 0; index < program.Shaders.Length; index++) { @@ -665,19 +666,21 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache if (tocFileStream.Length == 0) { - TocHeader header = new TocHeader(); + TocHeader header = new(); CreateToc(tocFileStream, ref header, TochMagic, 0, timestamp); } tocFileStream.Seek(0, SeekOrigin.End); dataFileStream.Seek(0, SeekOrigin.End); - BinarySerializer tocWriter = new BinarySerializer(tocFileStream); - BinarySerializer dataWriter = new BinarySerializer(dataFileStream); + BinarySerializer tocWriter = new(tocFileStream); + BinarySerializer dataWriter = new(dataFileStream); - OffsetAndSize offsetAndSize = new OffsetAndSize(); - offsetAndSize.Offset = (ulong)dataFileStream.Position; - offsetAndSize.UncompressedSize = (uint)hostCode.Length; + OffsetAndSize offsetAndSize = new() + { + Offset = (ulong)dataFileStream.Position, + UncompressedSize = (uint)hostCode.Length, + }; long dataStartPosition = dataFileStream.Position; @@ -714,9 +717,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <param name="magic">Magic value to be written</param> /// <param name="codegenVersion">Shader codegen version, only valid for the host file</param> /// <param name="timestamp">File creation timestamp</param> - private void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion, ulong timestamp) + private static void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion, ulong timestamp) { - BinarySerializer writer = new BinarySerializer(tocFileStream); + BinarySerializer writer = new(tocFileStream); header.Magic = magic; header.FormatVersion = FileFormatVersionPacked; @@ -741,7 +744,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <returns>Shader program info</returns> private static ShaderProgramInfo ReadShaderProgramInfo(ref BinarySerializer dataReader) { - DataShaderInfo dataInfo = new DataShaderInfo(); + DataShaderInfo dataInfo = new(); dataReader.ReadWithMagicAndSize(ref dataInfo, ShdiMagic); @@ -797,18 +800,19 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return; } - DataShaderInfo dataInfo = new DataShaderInfo(); - - dataInfo.CBuffersCount = (ushort)info.CBuffers.Count; - dataInfo.SBuffersCount = (ushort)info.SBuffers.Count; - dataInfo.TexturesCount = (ushort)info.Textures.Count; - dataInfo.ImagesCount = (ushort)info.Images.Count; - dataInfo.Stage = info.Stage; - dataInfo.UsesInstanceId = info.UsesInstanceId; - dataInfo.UsesDrawParameters = info.UsesDrawParameters; - dataInfo.UsesRtLayer = info.UsesRtLayer; - dataInfo.ClipDistancesWritten = info.ClipDistancesWritten; - dataInfo.FragmentOutputMap = info.FragmentOutputMap; + DataShaderInfo dataInfo = new() + { + CBuffersCount = (ushort)info.CBuffers.Count, + SBuffersCount = (ushort)info.SBuffers.Count, + TexturesCount = (ushort)info.Textures.Count, + ImagesCount = (ushort)info.Images.Count, + Stage = info.Stage, + UsesInstanceId = info.UsesInstanceId, + UsesDrawParameters = info.UsesDrawParameters, + UsesRtLayer = info.UsesRtLayer, + ClipDistancesWritten = info.ClipDistancesWritten, + FragmentOutputMap = info.FragmentOutputMap, + }; dataWriter.WriteWithMagicAndSize(ref dataInfo, ShdiMagic); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs index d6e23302c..9320638ca 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs @@ -45,4 +45,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache Result = result; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs index b3ffa4a73..ba23f70ee 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <summary> /// File might be valid, but is incompatible with the current emulator version. /// </summary> - IncompatibleVersion + IncompatibleVersion, } static class DiskCacheLoadResultExtensions @@ -65,8 +65,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache DiskCacheLoadResult.FileCorruptedInvalidMagic => "Magic check failed, the cache file is corrupted.", DiskCacheLoadResult.FileCorruptedInvalidLength => "Length check failed, the cache file is corrupted.", DiskCacheLoadResult.IncompatibleVersion => "The version of the disk cache is not compatible with this version of the emulator.", - _ => "Unknown error." + _ => "Unknown error.", }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs index 959d6e184..f412c62e2 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs @@ -26,4 +26,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache Cb1Data = cb1Data; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 8df89824b..8c2108bfa 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private readonly BlockingCollection<AsyncProgramTranslation> _asyncTranslationQueue; private readonly SortedList<int, (CachedShaderProgram, byte[])> _programList; - private int _backendParallelCompileThreads; + private readonly int _backendParallelCompileThreads; private int _compiledCount; private int _totalCount; @@ -201,22 +201,21 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <param name="graphicsCache">Graphics shader cache</param> /// <param name="computeCache">Compute shader cache</param> /// <param name="hostStorage">Disk cache host storage</param> - /// <param name="cancellationToken">Cancellation token</param> /// <param name="stateChangeCallback">Function to be called when there is a state change, reporting state, compiled and total shaders count</param> - public ParallelDiskCacheLoader( - GpuContext context, + /// <param name="cancellationToken">Cancellation token</param> + public ParallelDiskCacheLoader(GpuContext context, ShaderCacheHashTable graphicsCache, ComputeShaderCacheHashTable computeCache, DiskCacheHostStorage hostStorage, - CancellationToken cancellationToken, - Action<ShaderCacheState, int, int> stateChangeCallback) + Action<ShaderCacheState, int, int> stateChangeCallback, + CancellationToken cancellationToken) { _context = context; _graphicsCache = graphicsCache; _computeCache = computeCache; _hostStorage = hostStorage; - _cancellationToken = cancellationToken; _stateChangeCallback = stateChangeCallback; + _cancellationToken = cancellationToken; _validationQueue = new Queue<ProgramEntry>(); _compilationQueue = new ConcurrentQueue<ProgramCompilation>(); _asyncTranslationQueue = new BlockingCollection<AsyncProgramTranslation>(ThreadCount); @@ -235,7 +234,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { workThreads[index] = new Thread(ProcessAsyncQueue) { - Name = $"GPU.AsyncTranslationThread.{index}" + Name = $"GPU.AsyncTranslationThread.{index}", }; } @@ -367,7 +366,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { try { - AsyncProgramTranslation asyncTranslation = new AsyncProgramTranslation(guestShaders, specState, programIndex, isCompute); + AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute); _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); } catch (OperationCanceledException) @@ -491,7 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null); + ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null); for (int index = 0; index < compilation.TranslatedStages.Length; index++) { @@ -502,7 +501,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache ShaderInfo shaderInfo = shaderInfoBuilder.Build(compilation.SpecializationState.PipelineState, fromCache: true); IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo); - CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders); + CachedShaderProgram program = new(hostProgram, compilation.SpecializationState, compilation.Shaders); // Vulkan's binary code is the SPIR-V used for compilation, so it is ready immediately. Other APIs get this after compilation. byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(shaderSources) : null; @@ -589,12 +588,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// <param name="programIndex">Program index</param> private void RecompileGraphicsFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex) { - ShaderSpecializationState newSpecState = new ShaderSpecializationState( + ShaderSpecializationState newSpecState = new( ref specState.GraphicsState, specState.PipelineState, specState.TransformFeedbackDescriptors); - ResourceCounts counts = new ResourceCounts(); + ResourceCounts counts = new(); TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1]; TranslatorContext nextStage = null; @@ -610,7 +609,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache byte[] guestCode = shader.Code; byte[] cb1Data = shader.Cb1Data; - DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex); + DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex); TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0); if (nextStage != null) @@ -623,7 +622,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache byte[] guestCodeA = guestShaders[0].Value.Code; byte[] cb1DataA = guestShaders[0].Value.Cb1Data; - DiskCacheGpuAccessor gpuAccessorA = new DiskCacheGpuAccessor(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0); + DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0); translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0); } @@ -638,7 +637,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length]; - List<ShaderProgram> translatedStages = new List<ShaderProgram>(); + List<ShaderProgram> translatedStages = new(); TranslatorContext previousStage = null; @@ -699,9 +698,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private void RecompileComputeFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex) { GuestCodeAndCbData shader = guestShaders[0].Value; - ResourceCounts counts = new ResourceCounts(); - ShaderSpecializationState newSpecState = new ShaderSpecializationState(ref specState.ComputeState); - DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0); + ResourceCounts counts = new(); + ShaderSpecializationState newSpecState = new(ref specState.ComputeState); + DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0); TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0); @@ -721,4 +720,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache _stateChangeCallback(ShaderCacheState.Loading, ++_compiledCount, _totalCount); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs index 2dc5c9719..a18b5780e 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs @@ -3,7 +3,6 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; -using System; using System.Collections.Generic; using System.IO; @@ -29,10 +28,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code) { - using MemoryStream input = new MemoryStream(code); - using BinaryReader reader = new BinaryReader(input); + using MemoryStream input = new(code); + using BinaryReader reader = new(input); - List<ShaderSource> output = new List<ShaderSource>(); + List<ShaderSource> output = new(); int count = reader.ReadInt32(); @@ -48,4 +47,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return output.ToArray(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 5e18e61f2..ca9c883e3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Gpu.Shader CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater, CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual, CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual, - _ => AlphaTestOp.Always + _ => AlphaTestOp.Always, }; } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 2dd7c631c..07b7ddc28 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; -using System; namespace Ryujinx.Graphics.Gpu.Shader { @@ -125,7 +124,7 @@ namespace Ryujinx.Graphics.Gpu.Shader 3 => 2, // Geometry 1 => 3, // Tessellation control 2 => 4, // Tessellation evaluation - _ => 0 // Vertex/Compute + _ => 0, // Vertex/Compute }; } @@ -188,6 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Shader return formatInfo.Format switch { +#pragma warning disable IDE0055 // Disable formatting Format.R8Unorm => TextureFormat.R8Unorm, Format.R8Snorm => TextureFormat.R8Snorm, Format.R8Uint => TextureFormat.R8Uint, @@ -228,7 +228,8 @@ namespace Ryujinx.Graphics.Gpu.Shader Format.R10G10B10A2Unorm => TextureFormat.R10G10B10A2Unorm, Format.R10G10B10A2Uint => TextureFormat.R10G10B10A2Uint, Format.R11G11B10Float => TextureFormat.R11G11B10Float, - _ => TextureFormat.Unknown + _ => TextureFormat.Unknown, +#pragma warning restore IDE0055 }; } @@ -256,7 +257,7 @@ namespace Ryujinx.Graphics.Gpu.Shader PrimitiveTopology.Patches => tessellationMode.UnpackPatchType() == TessPatchType.Isolines ? InputTopology.Lines : InputTopology.Triangles, - _ => InputTopology.Points + _ => InputTopology.Points, }; } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs index 0e8e979c8..cfc4a2ccc 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs @@ -58,4 +58,4 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceCounts = new ResourceCounts(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs index b65dd75e3..d8cdbc348 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs @@ -62,4 +62,4 @@ namespace Ryujinx.Graphics.Gpu.Shader HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 5247a096f..544e689ab 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -155,4 +155,4 @@ namespace Ryujinx.Graphics.Gpu.Shader DualSourceBlendEnable = dualSourceBlendEnable; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs index 1e34c5ded..ddb45152e 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs @@ -2,6 +2,7 @@ using System; namespace Ryujinx.Graphics.Gpu.Shader { +#pragma warning disable CS0659 // Class overrides Object.Equals(object o) but does not override Object.GetHashCode() /// <summary> /// State used by the <see cref="GpuAccessor"/>. /// </summary> @@ -46,5 +47,11 @@ namespace Ryujinx.Graphics.Gpu.Shader TexturePoolMaximumId == other.TexturePoolMaximumId && TextureBufferIndex == other.TextureBufferIndex; } + + public override bool Equals(object obj) + { + return obj is GpuChannelPoolState state && Equals(state); + } } -} \ No newline at end of file +#pragma warning restore CS0659 +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs index 584eefdc6..836b8663a 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// <returns>Hash of the given data</returns> public static uint CalcHash(ReadOnlySpan<byte> data) { - HashState state = new HashState(); + HashState state = new(); state.Initialize(); state.Continue(data); @@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable { ulong h = _hash; - ReadOnlySpan<ulong> dataAsUlong = MemoryMarshal.Cast<byte, ulong>(data.Slice(_start)); + ReadOnlySpan<ulong> dataAsUlong = MemoryMarshal.Cast<byte, ulong>(data[_start..]); for (int i = 0; i < dataAsUlong.Length; i++) { @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// </remarks> /// <param name="data">Data to be hashed</param> /// <returns>Hash of all the data hashed with this <see cref="HashState"/></returns> - public uint Finalize(ReadOnlySpan<byte> data) + public readonly uint Finalize(ReadOnlySpan<byte> data) { ulong h = _hash; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs index d7cb3d99e..c8c8dfcb3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionHashTable.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// Partial entries have no items associated with them. They just indicates that the data might be present on /// the table, and one must keep looking for the full entry on other tables of larger data size. /// </remarks> - public bool IsPartial => OwnSize != 0; + public readonly bool IsPartial => OwnSize != 0; /// <summary> /// Creates a new partial hash table entry. @@ -82,11 +82,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// Gets the data for this entry, either full or partial. /// </summary> /// <returns>Data sub-region</returns> - public ReadOnlySpan<byte> GetData() + public readonly ReadOnlySpan<byte> GetData() { if (OwnSize != 0) { - return new ReadOnlySpan<byte>(Data).Slice(0, OwnSize); + return new ReadOnlySpan<byte>(Data)[..OwnSize]; } return Data; @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable return existingItem; } - Entry entry = new Entry(dataHash, data, item); + Entry entry = new(dataHash, data, item); AddToBucket(dataHash, ref entry); @@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable return false; } - Entry entry = new Entry(dataHash, data, item); + Entry entry = new(dataHash, data, item); AddToBucket(dataHash, ref entry); @@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// <returns>True if added, false otherwise</returns> public bool AddPartial(byte[] ownerData, int ownSize) { - ReadOnlySpan<byte> data = new ReadOnlySpan<byte>(ownerData).Slice(0, ownSize); + ReadOnlySpan<byte> data = new ReadOnlySpan<byte>(ownerData)[..ownSize]; return AddPartial(ownerData, HashState.CalcHash(data), ownSize); } @@ -189,14 +189,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// <returns>True if added, false otherwise</returns> public bool AddPartial(byte[] ownerData, uint dataHash, int ownSize) { - ReadOnlySpan<byte> data = new ReadOnlySpan<byte>(ownerData).Slice(0, ownSize); + ReadOnlySpan<byte> data = new ReadOnlySpan<byte>(ownerData)[..ownSize]; if (TryFindItem(dataHash, data, out _)) { return false; } - Entry entry = new Entry(dataHash, ownerData, ownSize); + Entry entry = new(dataHash, ownerData, ownSize); AddToBucket(dataHash, ref entry); @@ -226,7 +226,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// </summary> /// <param name="bucket">Bucket to add the entry into</param> /// <param name="entry">Entry to be added</param> - private void AddToBucket(ref Bucket bucket, ref Entry entry) + private static void AddToBucket(ref Bucket bucket, ref Entry entry) { if (bucket.InlineEntry.Data == null) { @@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// <summary> /// A full entry was found, the search was concluded and the item can be retrieved. /// </summary> - FoundFull + FoundFull, } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs index e9a4f6549..341d3114f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs @@ -149,12 +149,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable } } - HashState hashState = new HashState(); + HashState hashState = new(); hashState.Initialize(); for (int i = 0; i < index; i++) { - ReadOnlySpan<byte> dataSlice = new ReadOnlySpan<byte>(data).Slice(0, _sizeTable[i].Size); + ReadOnlySpan<byte> dataSlice = new ReadOnlySpan<byte>(data)[.._sizeTable[i].Size]; hashState.Continue(dataSlice); _sizeTable[i].AddPartial(data, hashState.Finalize(dataSlice)); } @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// <returns>True if the item was found on the table, false otherwise</returns> public bool TryFindItem(IDataAccessor dataAccessor, out T item, out byte[] data) { - SmartDataAccessor sda = new SmartDataAccessor(dataAccessor); + SmartDataAccessor sda = new(dataAccessor); item = default; data = null; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs index 0632add6c..17853e90a 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/SmartDataAccessor.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable } else if (_data.Length > length) { - return _data.Slice(0, length); + return _data[..length]; } return _data; @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable /// </summary> /// <param name="data">Data to be hashed</param> /// <returns>Hash of the data</returns> - private uint CalcHashCached(ReadOnlySpan<byte> data) + private readonly uint CalcHashCached(ReadOnlySpan<byte> data) { HashState state = default; bool found = false; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs b/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs index f495229ff..126e3249c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ResourceCounts.cs @@ -25,4 +25,4 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> public int ImagesCount; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs index 651dfd263..32d92223f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> struct ShaderAddresses : IEquatable<ShaderAddresses> { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public ulong VertexA; public ulong VertexB; public ulong TessControl; @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> /// <param name="other">Shader addresses structure to compare with</param> /// <returns>True if they are equal, false otherwise</returns> - public override bool Equals(object other) + public readonly override bool Equals(object other) { return other is ShaderAddresses addresses && Equals(addresses); } @@ -33,21 +33,21 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> /// <param name="other">Shader addresses structure to compare with</param> /// <returns>True if they are equal, false otherwise</returns> - public bool Equals(ShaderAddresses other) + public readonly bool Equals(ShaderAddresses other) { - return VertexA == other.VertexA && - VertexB == other.VertexB && - TessControl == other.TessControl && + return VertexA == other.VertexA && + VertexB == other.VertexB && + TessControl == other.TessControl && TessEvaluation == other.TessEvaluation && - Geometry == other.Geometry && - Fragment == other.Fragment; + Geometry == other.Geometry && + Fragment == other.Fragment; } /// <summary> /// Computes hash code from the addresses. /// </summary> /// <returns>Hash code</returns> - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(VertexA, VertexB, TessControl, TessEvaluation, Geometry, Fragment); } @@ -61,4 +61,4 @@ namespace Ryujinx.Graphics.Gpu.Shader return MemoryMarshal.CreateSpan(ref VertexA, Unsafe.SizeOf<ShaderAddresses>() / sizeof(ulong)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 913f6e9ec..97d7a7206 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -11,7 +11,6 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; namespace Ryujinx.Graphics.Gpu.Shader @@ -73,7 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } } - private Queue<ProgramToSave> _programsToSaveQueue; + private readonly Queue<ProgramToSave> _programsToSaveQueue; private readonly ComputeShaderCacheHashTable _computeShaderCache; private readonly ShaderCacheHashTable _graphicsShaderCache; @@ -157,13 +156,12 @@ namespace Ryujinx.Graphics.Gpu.Shader { if (_diskCacheHostStorage.CacheEnabled) { - ParallelDiskCacheLoader loader = new ParallelDiskCacheLoader( + ParallelDiskCacheLoader loader = new( _context, _graphicsShaderCache, _computeShaderCache, _diskCacheHostStorage, - cancellationToken, - ShaderCacheStateUpdate); + ShaderCacheStateUpdate, cancellationToken); loader.LoadShaders(); @@ -214,9 +212,9 @@ namespace Ryujinx.Graphics.Gpu.Shader return cpShader; } - ShaderSpecializationState specState = new ShaderSpecializationState(ref computeState); - GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, computeState, default, specState); - GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState); + ShaderSpecializationState specState = new(ref computeState); + GpuAccessorState gpuAccessorState = new(poolState, computeState, default, specState); + GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState); TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa); TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode); @@ -241,7 +239,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="pipeline">Shader pipeline state to be updated</param> /// <param name="graphicsState">Current graphics state</param> /// <param name="channel">Current GPU channel</param> - private void UpdatePipelineInfo( + private static void UpdatePipelineInfo( ref ThreedClassState state, ref ProgramPipelineState pipeline, GpuChannelGraphicsState graphicsState, @@ -318,8 +316,8 @@ namespace Ryujinx.Graphics.Gpu.Shader UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel); - ShaderSpecializationState specState = new ShaderSpecializationState(ref graphicsState, ref pipeline, transformFeedbackDescriptors); - GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, default, graphicsState, specState, transformFeedbackDescriptors); + ShaderSpecializationState specState = new(ref graphicsState, ref pipeline, transformFeedbackDescriptors); + GpuAccessorState gpuAccessorState = new(poolState, default, graphicsState, specState, transformFeedbackDescriptors); ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan(); @@ -334,7 +332,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (gpuVa != 0) { - GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, stageIndex); + GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex); TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa); if (nextStage != null) @@ -358,11 +356,11 @@ namespace Ryujinx.Graphics.Gpu.Shader } CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; - List<ShaderSource> shaderSources = new List<ShaderSource>(); + List<ShaderSource> shaderSources = new(); TranslatorContext previousStage = null; - ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context, transformFeedbackDescriptors != null); + ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null); for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) { @@ -486,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (_diskCacheHostStorage.CacheEnabled) { byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(sources) : null; - ProgramToSave programToSave = new ProgramToSave(program, hostProgram, binaryCode); + ProgramToSave programToSave = new(program, hostProgram, binaryCode); _programsToSaveQueue.Enqueue(programToSave); } @@ -670,8 +668,8 @@ namespace Ryujinx.Graphics.Gpu.Shader pathsB.Prepend(program); pathsA.Prepend(program); - CachedShaderStage vertexAStage = new CachedShaderStage(null, codeA, cb1DataA); - CachedShaderStage vertexBStage = new CachedShaderStage(program.Info, codeB, cb1DataB); + CachedShaderStage vertexAStage = new(null, codeA, cb1DataA); + CachedShaderStage vertexBStage = new(program.Info, codeB, cb1DataB); return new TranslatedShaderVertexPair(vertexAStage, vertexBStage, program); } @@ -716,7 +714,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ShaderStage.TessellationEvaluation => 2, ShaderStage.Geometry => 3, ShaderStage.Fragment => 4, - _ => 0 + _ => 0, }; } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs index e35c06b13..e65a1dec9 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> /// <param name="stageIndex">Index of the shader stage</param> /// <returns>Guest code, or null if not present</returns> - public byte[] GetByIndex(int stageIndex) + public readonly byte[] GetByIndex(int stageIndex) { return stageIndex switch { @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader 2 => TessEvaluationCode, 3 => GeometryCode, 4 => FragmentCode, - _ => VertexBCode + _ => VertexBCode, }; } } @@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="id">ID of the guest code, if found</param> /// <param name="data">Cached guest code, if found</param> /// <returns>True if found, false otherwise</returns> - public bool TryFind(IDataAccessor dataAccessor, out int id, out byte[] data) + public readonly bool TryFind(IDataAccessor dataAccessor, out int id, out byte[] data) { return _cache.TryFindItem(dataAccessor, out id, out data); } @@ -103,12 +103,12 @@ namespace Ryujinx.Graphics.Gpu.Shader public int GeometryId; public int FragmentId; - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is IdTable other && Equals(other); } - public bool Equals(IdTable other) + public readonly bool Equals(IdTable other) { return other.VertexAId == VertexAId && other.VertexBId == VertexBId && @@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Gpu.Shader other.FragmentId == FragmentId; } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(VertexAId, VertexBId, TessControlId, TessEvaluationId, GeometryId, FragmentId); } @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="program">Program to be added</param> public void Add(CachedShaderProgram program) { - IdTable idTable = new IdTable(); + IdTable idTable = new(); foreach (var shader in program.Shaders) { @@ -222,7 +222,7 @@ namespace Ryujinx.Graphics.Gpu.Shader out CachedGraphicsGuestCode guestCode) { var memoryManager = channel.MemoryManager; - IdTable idTable = new IdTable(); + IdTable idTable = new(); guestCode = new CachedGraphicsGuestCode(); program = null; @@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Shader return true; } - ShaderCodeAccessor codeAccessor = new ShaderCodeAccessor(memoryManager, baseAddress); + ShaderCodeAccessor codeAccessor = new(memoryManager, baseAddress); return idCache.TryFind(codeAccessor, out id, out data); } @@ -279,4 +279,4 @@ namespace Ryujinx.Graphics.Gpu.Shader } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs index b94a6c054..075e3a613 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCacheState.cs @@ -10,6 +10,6 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <summary>Shader cache is written to disk</summary> Packaging, /// <summary>Shader cache finished loading</summary> - Loaded + Loaded, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs index e896493c5..240a4ce9f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs @@ -29,4 +29,4 @@ namespace Ryujinx.Graphics.Gpu.Shader return _memoryManager.GetSpanMapped(_baseAddress + (ulong)offset, length); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs index 6ca7daef2..d0765963d 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs @@ -46,4 +46,4 @@ namespace Ryujinx.Graphics.Gpu.Shader } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs index 93eeb8d72..80d599e9b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs @@ -46,13 +46,13 @@ namespace Ryujinx.Graphics.Gpu.Shader CurrentDumpIndex++; - using MemoryStream stream = new MemoryStream(code); - BinaryReader codeReader = new BinaryReader(stream); + using MemoryStream stream = new(code); + BinaryReader codeReader = new(stream); using FileStream fullFile = File.Create(fullPath); using FileStream codeFile = File.Create(codePath); - BinaryWriter fullWriter = new BinaryWriter(fullFile); - BinaryWriter codeWriter = new BinaryWriter(codeFile); + BinaryWriter fullWriter = new(fullFile); + BinaryWriter codeWriter = new(codeFile); int headerSize = compute ? 0 : 0x50; @@ -126,4 +126,4 @@ namespace Ryujinx.Graphics.Gpu.Shader return dir; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 83d92edc3..7356410ca 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ShaderStage.TessellationEvaluation => 2, ShaderStage.Geometry => 3, ShaderStage.Fragment => 4, - _ => 0 + _ => 0, }); ResourceStages stages = info.Stage switch @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation, ShaderStage.Geometry => ResourceStages.Geometry, ShaderStage.Fragment => ResourceStages.Fragment, - _ => ResourceStages.None + _ => ResourceStages.None, }; int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage; @@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Shader usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly()); } - ResourceLayout resourceLayout = new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly()); + ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly()); if (pipeline.HasValue) { @@ -262,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ProgramPipelineState? pipeline, bool tfEnabled) { - ShaderInfoBuilder builder = new ShaderInfoBuilder(context, tfEnabled); + ShaderInfoBuilder builder = new(context, tfEnabled); foreach (CachedShaderStage program in programs) { @@ -284,11 +284,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <returns>Shader information</returns> public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false) { - ShaderInfoBuilder builder = new ShaderInfoBuilder(context, tfEnabled: false); + ShaderInfoBuilder builder = new(context, tfEnabled: false); builder.AddStageInfo(info); return builder.Build(null, fromCache); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs index 7d61332e5..e57e1df1a 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> class ShaderSpecializationList : IEnumerable<CachedShaderProgram> { - private readonly List<CachedShaderProgram> _entries = new List<CachedShaderProgram>(); + private readonly List<CachedShaderProgram> _entries = new(); /// <summary> /// Adds a program to the list. @@ -81,4 +81,4 @@ namespace Ryujinx.Graphics.Gpu.Shader return GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 9b0c8b9b9..775bfb2af 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader EarlyZForce = 1 << 0, PrimitiveTopology = 1 << 1, TessellationMode = 1 << 2, - TransformFeedback = 1 << 3 + TransformFeedback = 1 << 3, } private QueriedStateFlags _queriedState; @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { TextureFormat = 1 << 0, SamplerType = 1 << 1, - CoordNormalized = 1 << 2 + CoordNormalized = 1 << 2, } /// <summary> @@ -440,7 +440,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <returns>Texture specialization state</returns> private Box<TextureSpecializationState> GetOrCreateTextureSpecState(int stageIndex, int handle, int cbufSlot) { - TextureKey key = new TextureKey(stageIndex, handle, cbufSlot); + TextureKey key = new(stageIndex, handle, cbufSlot); if (!_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state)) { @@ -459,7 +459,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <returns>Texture specialization state</returns> private Box<TextureSpecializationState> GetTextureSpecState(int stageIndex, int handle, int cbufSlot) { - TextureKey key = new TextureKey(stageIndex, handle, cbufSlot); + TextureKey key = new(stageIndex, handle, cbufSlot); if (_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state)) { @@ -694,7 +694,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="descriptor">Texture descriptor</param> /// <returns>True if the state matches, false otherwise</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool MatchesTexture(Box<TextureSpecializationState> specializationState, in Image.TextureDescriptor descriptor) + private static bool MatchesTexture(Box<TextureSpecializationState> specializationState, in Image.TextureDescriptor descriptor) { if (specializationState != null) { @@ -756,7 +756,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <returns>Shader specialization state</returns> public static ShaderSpecializationState Read(ref BinarySerializer dataReader) { - ShaderSpecializationState specState = new ShaderSpecializationState(); + ShaderSpecializationState specState = new(); dataReader.Read(ref specState._queriedState); dataReader.Read(ref specState._compute); @@ -812,7 +812,7 @@ namespace Ryujinx.Graphics.Gpu.Shader for (int index = 0; index < count; index++) { TextureKey textureKey = default; - Box<TextureSpecializationState> textureState = new Box<TextureSpecializationState>(); + Box<TextureSpecializationState> textureState = new(); dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); dataReader.ReadWithMagicAndSize(ref textureState.Value, TexsMagic); @@ -886,4 +886,4 @@ namespace Ryujinx.Graphics.Gpu.Shader } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs b/src/Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs index 5baf2a1a6..1f245881d 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/TransformFeedbackDescriptor.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <returns>Span of varying locations</returns> public ReadOnlySpan<byte> AsSpan() { - return MemoryMarshal.Cast<uint, byte>(VaryingLocations.AsSpan()).Slice(0, Math.Min(128, VaryingCount)); + return MemoryMarshal.Cast<uint, byte>(VaryingLocations.AsSpan())[..Math.Min(128, VaryingCount)]; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs index 426669914..1b4df37e2 100644 --- a/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs +++ b/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs @@ -25,6 +25,6 @@ namespace Ryujinx.Graphics.Gpu.Synchronization /// </summary> Force = 1 << 2, - StrictSyncpoint = Strict | Syncpoint + StrictSyncpoint = Strict | Syncpoint, } } diff --git a/src/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs index 968de930d..ccec763e3 100644 --- a/src/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Synchronization/SynchronizationManager.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization /// <summary> /// Array containing all hardware syncpoints. /// </summary> - private Syncpoint[] _syncpoints; + private readonly Syncpoint[] _syncpoints; public SynchronizationManager() { @@ -118,26 +118,24 @@ namespace Ryujinx.Graphics.Gpu.Synchronization timeout = TimeSpan.FromSeconds(1); } - using (ManualResetEvent waitEvent = new ManualResetEvent(false)) + using ManualResetEvent waitEvent = new(false); + var info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set()); + + if (info == null) { - var info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set()); - - if (info == null) - { - return false; - } - - bool signaled = waitEvent.WaitOne(timeout); - - if (!signaled && info != null) - { - Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution..."); - - _syncpoints[id].UnregisterCallback(info); - } - - return !signaled; + return false; } + + bool signaled = waitEvent.WaitOne(timeout); + + if (!signaled && info != null) + { + Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution..."); + + _syncpoints[id].UnregisterCallback(info); + } + + return !signaled; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs index 39fb83c05..16f4d971b 100644 --- a/src/Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs +++ b/src/Ryujinx.Graphics.Gpu/Synchronization/Syncpoint.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization public Syncpoint(uint id) { - Id = id; + Id = id; _waiters = new List<SyncpointWaiterHandle>(); } @@ -46,10 +46,10 @@ namespace Ryujinx.Graphics.Gpu.Synchronization } else { - SyncpointWaiterHandle waiterInformation = new SyncpointWaiterHandle + SyncpointWaiterHandle waiterInformation = new() { Threshold = threshold, - Callback = callback + Callback = callback, }; _waiters.Add(waiterInformation); @@ -92,10 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization } else { - if (expiredList == null) - { - expiredList = new List<SyncpointWaiterHandle>(); - } + expiredList ??= new List<SyncpointWaiterHandle>(); expiredList.Add(item); } diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs index 06d0fddf4..5da472b3e 100644 --- a/src/Ryujinx.Graphics.Gpu/Window.cs +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -68,21 +68,21 @@ namespace Ryujinx.Graphics.Gpu /// <param name="releaseCallback">Texture release callback</param> /// <param name="userObj">User defined object passed to the release callback, can be used to identify the texture</param> public PresentationTexture( - TextureCache cache, - TextureInfo info, - MultiRange range, - ImageCrop crop, + TextureCache cache, + TextureInfo info, + MultiRange range, + ImageCrop crop, Action<GpuContext, object> acquireCallback, - Action<object> releaseCallback, - object userObj) + Action<object> releaseCallback, + object userObj) { - Cache = cache; - Info = info; - Range = range; - Crop = crop; + Cache = cache; + Info = info; + Range = range; + Crop = crop; AcquireCallback = acquireCallback; ReleaseCallback = releaseCallback; - UserObj = userObj; + UserObj = userObj; } } @@ -125,28 +125,28 @@ namespace Ryujinx.Graphics.Gpu /// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception> /// <returns>True if the frame was added to the queue, false otherwise</returns> public bool EnqueueFrameThreadSafe( - ulong pid, - ulong address, - int width, - int height, - int stride, - bool isLinear, - int gobBlocksInY, - Format format, - int bytesPerPixel, - ImageCrop crop, + ulong pid, + ulong address, + int width, + int height, + int stride, + bool isLinear, + int gobBlocksInY, + Format format, + int bytesPerPixel, + ImageCrop crop, Action<GpuContext, object> acquireCallback, - Action<object> releaseCallback, - object userObj) + Action<object> releaseCallback, + object userObj) { if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory)) { return false; } - FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4); + FormatInfo formatInfo = new(format, 1, 1, bytesPerPixel, 4); - TextureInfo info = new TextureInfo( + TextureInfo info = new( 0UL, width, height, @@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Gpu 1, 1).TotalSize; - MultiRange range = new MultiRange(address, (ulong)size); + MultiRange range = new(address, (ulong)size); _frameQueue.Enqueue(new PresentationTexture( physicalMemory.TextureCache, @@ -260,4 +260,4 @@ namespace Ryujinx.Graphics.Gpu return false; } } -} \ No newline at end of file +} From 1c7a90ef359d9974e5bd257c4d8e9bf526a6966c Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 3 Jul 2023 14:29:27 -0300 Subject: [PATCH 696/737] Stop identifying shader textures with handle and cbuf, use binding instead (#5266) * Stop identifying shader textures with handle and cbuf, use binding instead * Remove now unused code * Consider image operations as having accurate type information too I don't know why that was not the case before * Fix missing unscale on InsertCoordNormalization, stop calling SetUsageFlagsForTextureQuery when not needed * Shader cache version bump * Change get texture methods to return descriptors created from ResourceManager state This is required to ensure that reserved textures and images will not be bound as a guest texture/image * Fix BindlessElimination.SetHandle inserting coords at the wrong place --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../BufferDescriptor.cs | 15 +- .../CodeGen/Glsl/Declarations.cs | 79 ++--- .../CodeGen/Glsl/DefaultNames.cs | 3 - .../Glsl/Instructions/InstGenMemory.cs | 36 ++- .../CodeGen/Glsl/OperandManager.cs | 58 +--- .../CodeGen/Spirv/CodeGenContext.cs | 6 +- .../CodeGen/Spirv/Declarations.cs | 77 ++--- .../CodeGen/Spirv/Instructions.cs | 20 +- .../CodeGen/Spirv/TextureMeta.cs | 4 - .../Instructions/InstEmitSurface.cs | 66 ++-- .../Instructions/InstEmitTexture.cs | 132 ++++---- .../TextureOperation.cs | 30 +- .../StructuredIr/AstTextureOperation.cs | 9 +- .../StructuredIr/ShaderProperties.cs | 20 +- .../StructuredIr/StructuredProgram.cs | 10 +- .../StructuredIr/TextureDefinition.cs | 27 ++ .../TextureDescriptor.cs | 13 +- .../Translation/EmitterContext.cs | 30 -- .../Translation/EmitterContextInsts.cs | 81 +++++ .../Optimizations/BindlessElimination.cs | 14 +- .../Optimizations/BindlessToIndexed.cs | 15 +- .../Translation/ResourceManager.cs | 286 +++++++++++++++++- .../Translation/Rewriter.cs | 54 ++-- .../Translation/ShaderConfig.cs | 228 +------------- 25 files changed, 656 insertions(+), 659 deletions(-) delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 95a0a6bdd..672b3b8d1 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5311; + private const uint CodeGenVersion = 5266; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs index d1da95393..d8cad1df8 100644 --- a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader public readonly byte Slot; public readonly byte SbCbSlot; public readonly ushort SbCbOffset; - public BufferUsageFlags Flags; + public readonly BufferUsageFlags Flags; public BufferDescriptor(int binding, int slot) { @@ -16,25 +16,16 @@ namespace Ryujinx.Graphics.Shader Slot = (byte)slot; SbCbSlot = 0; SbCbOffset = 0; - Flags = BufferUsageFlags.None; } - public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset) + public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags) { Binding = binding; Slot = (byte)slot; SbCbSlot = (byte)sbCbSlot; SbCbOffset = (ushort)sbCbOffset; - - Flags = BufferUsageFlags.None; - } - - public BufferDescriptor SetFlag(BufferUsageFlags flag) - { - Flags |= flag; - - return this; + Flags = flags; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 94b850e7b..2370b49f0 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -75,22 +75,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); DeclareMemories(context, context.Config.Properties.LocalMemories.Values, isShared: false); DeclareMemories(context, context.Config.Properties.SharedMemories.Values, isShared: true); - - var textureDescriptors = context.Config.GetTextureDescriptors(); - if (textureDescriptors.Length != 0) - { - DeclareSamplers(context, textureDescriptors); - - context.AppendLine(); - } - - var imageDescriptors = context.Config.GetImageDescriptors(); - if (imageDescriptors.Length != 0) - { - DeclareImages(context, imageDescriptors); - - context.AppendLine(); - } + DeclareSamplers(context, context.Config.Properties.Textures.Values); + DeclareImages(context, context.Config.Properties.Images.Values); if (context.Config.Stage != ShaderStage.Compute) { @@ -369,80 +355,71 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) + private static void DeclareSamplers(CodeGenContext context, IEnumerable<TextureDefinition> definitions) { int arraySize = 0; - foreach (var descriptor in descriptors) + + foreach (var definition in definitions) { - if (descriptor.Type.HasFlag(SamplerType.Indexed)) + string indexExpr = string.Empty; + + if (definition.Type.HasFlag(SamplerType.Indexed)) { if (arraySize == 0) { - arraySize = ShaderConfig.SamplerArraySize; + arraySize = ResourceManager.SamplerArraySize; } else if (--arraySize != 0) { continue; } + + indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]"; } - string indexExpr = NumberFormatter.FormatInt(arraySize); - - string samplerName = OperandManager.GetSamplerName( - context.Config.Stage, - descriptor.CbufSlot, - descriptor.HandleIndex, - descriptor.Type.HasFlag(SamplerType.Indexed), - indexExpr); - - string samplerTypeName = descriptor.Type.ToGlslSamplerType(); + string samplerTypeName = definition.Type.ToGlslSamplerType(); string layout = string.Empty; if (context.Config.Options.TargetApi == TargetApi.Vulkan) { - layout = ", set = 2"; + layout = $", set = {definition.Set}"; } - context.AppendLine($"layout (binding = {descriptor.Binding}{layout}) uniform {samplerTypeName} {samplerName};"); + context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {samplerTypeName} {definition.Name}{indexExpr};"); } } - private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors) + private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> definitions) { int arraySize = 0; - foreach (var descriptor in descriptors) + + foreach (var definition in definitions) { - if (descriptor.Type.HasFlag(SamplerType.Indexed)) + string indexExpr = string.Empty; + + if (definition.Type.HasFlag(SamplerType.Indexed)) { if (arraySize == 0) { - arraySize = ShaderConfig.SamplerArraySize; + arraySize = ResourceManager.SamplerArraySize; } else if (--arraySize != 0) { continue; } + + indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]"; } - string indexExpr = NumberFormatter.FormatInt(arraySize); + string imageTypeName = definition.Type.ToGlslImageType(definition.Format.GetComponentType()); - string imageName = OperandManager.GetImageName( - context.Config.Stage, - descriptor.CbufSlot, - descriptor.HandleIndex, - descriptor.Format, - descriptor.Type.HasFlag(SamplerType.Indexed), - indexExpr); - - string imageTypeName = descriptor.Type.ToGlslImageType(descriptor.Format.GetComponentType()); - - if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) + if (definition.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) { imageTypeName = "coherent " + imageTypeName; } - string layout = descriptor.Format.ToGlslFormat(); + string layout = definition.Format.ToGlslFormat(); if (!string.IsNullOrEmpty(layout)) { @@ -451,10 +428,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Options.TargetApi == TargetApi.Vulkan) { - layout = $", set = 3{layout}"; + layout = $", set = {definition.Set}{layout}"; } - context.AppendLine($"layout (binding = {descriptor.Binding}{layout}) uniform {imageTypeName} {imageName};"); + context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {imageTypeName} {definition.Name}{indexExpr};"); } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 842228edf..54bf9aeb2 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -4,9 +4,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public const string LocalNamePrefix = "temp"; - public const string SamplerNamePrefix = "tex"; - public const string ImageNamePrefix = "img"; - public const string PerPatchAttributePrefix = "patch_attr_"; public const string IAttributePrefix = "in_attr"; public const string OAttributePrefix = "out_attr"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index e0faed298..7e6d8bb5c 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = Src(AggregateType.S32); } - string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); + string imageName = GetImageName(context.Config, texOp, indexExpr); texCallBuilder.Append('('); texCallBuilder.Append(imageName); @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); } - string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); + string samplerName = GetSamplerName(context.Config, texOp, indexExpr); int coordsIndex = isBindless || isIndexed ? 1 : 0; @@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = Src(AggregateType.S32); } - string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); + string samplerName = GetSamplerName(context.Config, texOp, indexExpr); texCall += "(" + samplerName; @@ -538,7 +538,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); } - string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); + string samplerName = GetSamplerName(context.Config, texOp, indexExpr); if (texOp.Index == 3) { @@ -546,8 +546,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else { - TextureDescriptor descriptor = context.Config.FindTextureDescriptor(texOp); - bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer; + context.Config.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); + bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; string texCall; if (hasLod) @@ -715,6 +715,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return varName; } + private static string GetSamplerName(ShaderConfig config, AstTextureOperation texOp, string indexExpr) + { + string name = config.Properties.Textures[texOp.Binding].Name; + + if (texOp.Type.HasFlag(SamplerType.Indexed)) + { + name = $"{name}[{indexExpr}]"; + } + + return name; + } + + private static string GetImageName(ShaderConfig config, AstTextureOperation texOp, string indexExpr) + { + string name = config.Properties.Images[texOp.Binding].Name; + + if (texOp.Type.HasFlag(SamplerType.Indexed)) + { + name = $"{name}[{indexExpr}]"; + } + + return name; + } + private static string GetMask(int index) { return $".{"rgba".AsSpan(index, 1)}"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 0ca3b55fc..17ffad9a5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -11,9 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { class OperandManager { - private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; - - private readonly Dictionary<AstOperand, string> _locals; + private Dictionary<AstOperand, string> _locals; public OperandManager() { @@ -41,60 +39,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl }; } - public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr) - { - return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr); - } - - public static string GetSamplerName(ShaderStage stage, int cbufSlot, int handle, bool indexed, string indexExpr) - { - string suffix = cbufSlot < 0 ? $"_tcb_{handle:X}" : $"_cb{cbufSlot}_{handle:X}"; - - if (indexed) - { - suffix += $"a[{indexExpr}]"; - } - - return GetShaderStagePrefix(stage) + "_" + DefaultNames.SamplerNamePrefix + suffix; - } - - public static string GetImageName(ShaderStage stage, AstTextureOperation texOp, string indexExpr) - { - return GetImageName(stage, texOp.CbufSlot, texOp.Handle, texOp.Format, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr); - } - - public static string GetImageName( - ShaderStage stage, - int cbufSlot, - int handle, - TextureFormat format, - bool indexed, - string indexExpr) - { - string suffix = cbufSlot < 0 - ? $"_tcb_{handle:X}_{format.ToGlslFormat()}" - : $"_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}"; - - if (indexed) - { - suffix += $"a[{indexExpr}]"; - } - - return GetShaderStagePrefix(stage) + "_" + DefaultNames.ImageNamePrefix + suffix; - } - - public static string GetShaderStagePrefix(ShaderStage stage) - { - int index = (int)stage; - - if ((uint)index >= _stagePrefixes.Length) - { - return "invalid"; - } - - return _stagePrefixes[index]; - } - public static string GetArgumentName(int argIndex) { return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 9956e90a3..d4f490458 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -28,9 +28,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>(); public Dictionary<int, Instruction> LocalMemories { get; } = new Dictionary<int, Instruction>(); public Dictionary<int, Instruction> SharedMemories { get; } = new Dictionary<int, Instruction>(); - public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>(); - public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>(); - public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>(); + public Dictionary<int, SamplerType> SamplersTypes { get; } = new Dictionary<int, SamplerType>(); + public Dictionary<int, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<int, (Instruction, Instruction, Instruction)>(); + public Dictionary<int, (Instruction, Instruction)> Images { get; } = new Dictionary<int, (Instruction, Instruction)>(); public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>(); public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>(); public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index da1e385a7..2c849cd49 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -72,8 +72,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); DeclareMemories(context, context.Config.Properties.LocalMemories, context.LocalMemories, StorageClass.Private); DeclareMemories(context, context.Config.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup); - DeclareSamplers(context, context.Config.GetTextureDescriptors()); - DeclareImages(context, context.Config.GetImageDescriptors()); + DeclareSamplers(context, context.Config.Properties.Textures.Values); + DeclareImages(context, context.Config.Properties.Images.Values); DeclareInputsAndOutputs(context, info); } @@ -110,6 +110,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv foreach (BufferDefinition buffer in buffers) { + int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? buffer.Set : 0; int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4; int alignmentMask = alignment - 1; int offset = 0; @@ -163,7 +164,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var variable = context.Variable(pointerType, StorageClass.Uniform); context.Name(variable, buffer.Name); - context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set); + context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)setIndex); context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding); context.AddGlobalVariable(variable); @@ -178,92 +179,72 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) + private static void DeclareSamplers(CodeGenContext context, IEnumerable<TextureDefinition> samplers) { - foreach (var descriptor in descriptors) + foreach (var sampler in samplers) { - var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format); + int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? sampler.Set : 0; - if (context.Samplers.ContainsKey(meta)) - { - continue; - } - - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 2 : 0; - - var dim = (descriptor.Type & SamplerType.Mask) switch + var dim = (sampler.Type & SamplerType.Mask) switch { SamplerType.Texture1D => Dim.Dim1D, SamplerType.Texture2D => Dim.Dim2D, SamplerType.Texture3D => Dim.Dim3D, SamplerType.TextureCube => Dim.Cube, SamplerType.TextureBuffer => Dim.Buffer, - _ => throw new InvalidOperationException($"Invalid sampler type \"{descriptor.Type & SamplerType.Mask}\"."), + _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\".") }; var imageType = context.TypeImage( context.TypeFP32(), dim, - descriptor.Type.HasFlag(SamplerType.Shadow), - descriptor.Type.HasFlag(SamplerType.Array), - descriptor.Type.HasFlag(SamplerType.Multisample), + sampler.Type.HasFlag(SamplerType.Shadow), + sampler.Type.HasFlag(SamplerType.Array), + sampler.Type.HasFlag(SamplerType.Multisample), 1, ImageFormat.Unknown); - var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}"; - var sampledImageType = context.TypeSampledImage(imageType); var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType); var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant); - context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable)); - context.SamplersTypes.Add(meta, descriptor.Type); + context.Samplers.Add(sampler.Binding, (imageType, sampledImageType, sampledImageVariable)); + context.SamplersTypes.Add(sampler.Binding, sampler.Type); - context.Name(sampledImageVariable, $"{GetStagePrefix(context.Config.Stage)}_tex{nameSuffix}"); + context.Name(sampledImageVariable, sampler.Name); context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); - context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding); + context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)sampler.Binding); context.AddGlobalVariable(sampledImageVariable); } } - private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors) + private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> images) { - foreach (var descriptor in descriptors) + foreach (var image in images) { - var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format); + int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? image.Set : 0; - if (context.Images.ContainsKey(meta)) - { - continue; - } - - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 3 : 0; - - var dim = GetDim(descriptor.Type); + var dim = GetDim(image.Type); var imageType = context.TypeImage( - context.GetType(meta.Format.GetComponentType()), + context.GetType(image.Format.GetComponentType()), dim, - descriptor.Type.HasFlag(SamplerType.Shadow), - descriptor.Type.HasFlag(SamplerType.Array), - descriptor.Type.HasFlag(SamplerType.Multisample), + image.Type.HasFlag(SamplerType.Shadow), + image.Type.HasFlag(SamplerType.Array), + image.Type.HasFlag(SamplerType.Multisample), AccessQualifier.ReadWrite, - GetImageFormat(meta.Format)); - - var nameSuffix = meta.CbufSlot < 0 ? - $"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" : - $"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}"; + GetImageFormat(image.Format)); var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType); var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant); - context.Images.Add(meta, (imageType, imageVariable)); + context.Images.Add(image.Binding, (imageType, imageVariable)); - context.Name(imageVariable, $"{GetStagePrefix(context.Config.Stage)}_img{nameSuffix}"); + context.Name(imageVariable, image.Name); context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); - context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding); + context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)image.Binding); - if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) + if (image.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) { context.Decorate(imageVariable, Decoration.Coherent); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index a53b40b24..9489397bc 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -657,7 +657,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SpvInstruction value = Src(componentType); - (SpvInstruction imageType, SpvInstruction imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; + (var imageType, var imageVariable) = context.Images[texOp.Binding]; context.Load(imageType, imageVariable); @@ -742,7 +742,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv pCoords = Src(AggregateType.S32); } - var (imageType, imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; + (var imageType, var imageVariable) = context.Images[texOp.Binding]; var image = context.Load(imageType, imageVariable); var imageComponentType = context.GetType(componentType); @@ -829,7 +829,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType), ComponentsCount), cElems); - var (imageType, imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; + (var imageType, var imageVariable) = context.Images[texOp.Binding]; var image = context.Load(imageType, imageVariable); @@ -908,9 +908,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv pCoords = Src(AggregateType.FP32); } - var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - - var (_, sampledImageType, sampledImageVariable) = context.Samplers[meta]; + (_, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding]; var image = context.Load(sampledImageType, sampledImageVariable); @@ -1511,9 +1509,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32(); - var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - - var (imageType, sampledImageType, sampledImageVariable) = context.Samplers[meta]; + (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding]; var image = context.Load(sampledImageType, sampledImageVariable); @@ -1592,9 +1588,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.GetS32(texOp.GetSource(0)); } - var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - - (SpvInstruction imageType, SpvInstruction sampledImageType, SpvInstruction sampledImageVariable) = context.Samplers[meta]; + (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding]; var image = context.Load(sampledImageType, sampledImageVariable); image = context.Image(imageType, image); @@ -1605,7 +1599,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else { - var type = context.SamplersTypes[meta]; + var type = context.SamplersTypes[texOp.Binding]; bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer; int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs deleted file mode 100644 index 56ea9a2a6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Spirv -{ - readonly record struct TextureMeta(int CbufSlot, int Handle, TextureFormat Format); -} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index 78fc313d8..0b929307e 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(srcB++, RegisterType.Gpr)); } - Operand destOperand = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null; + Operand d = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null; List<Operand> sourcesList = new(); @@ -277,17 +277,17 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - TextureOperation operation = context.CreateTextureOperation( + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, flags, - imm, - 0, - new[] { destOperand }, - sources); + TextureOperation.DefaultCbufSlot, + imm); - context.Add(operation); + Operand res = context.ImageAtomic(type, format, flags, binding, sources); + + context.Copy(d, res); } private static void EmitSuld( @@ -383,21 +383,17 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - TextureOperation operation = context.CreateTextureOperation( + TextureFormat format = isBindless ? TextureFormat.Unknown : context.Config.GetTextureFormat(handle); + + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageLoad, type, + format, flags, - handle, - (int)componentMask, - dests, - sources); + TextureOperation.DefaultCbufSlot, + handle); - if (!isBindless) - { - operation.Format = context.Config.GetTextureFormat(handle); - } - - context.Add(operation); + context.ImageLoad(type, format, flags, binding, (int)componentMask, dests, sources); } else { @@ -430,17 +426,17 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - TextureOperation operation = context.CreateTextureOperation( + TextureFormat format = GetTextureFormat(size); + + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageLoad, type, - GetTextureFormat(size), + format, flags, - handle, - compMask, - dests, - sources); + TextureOperation.DefaultCbufSlot, + handle); - context.Add(operation); + context.ImageLoad(type, format, flags, binding, compMask, dests, sources); switch (size) { @@ -552,17 +548,15 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - TextureOperation operation = context.CreateTextureOperation( + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, flags, - imm, - 0, - null, - sources); + TextureOperation.DefaultCbufSlot, + imm); - context.Add(operation); + context.ImageAtomic(type, format, flags, binding, sources); } private static void EmitSust( @@ -681,17 +675,15 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Coherent; } - TextureOperation operation = context.CreateTextureOperation( + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageStore, type, format, flags, - handle, - 0, - null, - sources); + TextureOperation.DefaultCbufSlot, + handle); - context.Add(operation); + context.ImageStore(type, format, flags, binding, sources); } private static int GetComponentSizeInBytesLog2(SuatomSize size) diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 3701325e2..7d3d22d8a 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -324,16 +324,7 @@ namespace Ryujinx.Graphics.Shader.Instructions int handle = !isBindless ? imm : 0; - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, handle, componentMask, dests, sources); } private static void EmitTexs( @@ -657,16 +648,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, handle, componentMask, dests, sources); if (isF16) { @@ -812,18 +794,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - int handle = imm; - - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, imm, componentMask, dests, sources); } private static void EmitTmml( @@ -913,15 +884,21 @@ namespace Ryujinx.Graphics.Shader.Instructions return Register(dest++, RegisterType.Gpr); } - int handle = imm; + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + Instruction.Lod, + type, + TextureFormat.Unknown, + flags, + TextureOperation.DefaultCbufSlot, + imm); for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) { if ((compMask & 1) != 0) { - Operand destOperand = GetDest(); + Operand d = GetDest(); - if (destOperand == null) + if (d == null) { break; } @@ -930,28 +907,18 @@ namespace Ryujinx.Graphics.Shader.Instructions if (compIndex >= 2) { context.Add(new CommentNode("Unsupported component z or w found")); - context.Copy(destOperand, Const(0)); + context.Copy(d, Const(0)); } else { - Operand tempDest = Local(); + // The instruction component order is the inverse of GLSL's. + Operand res = context.Lod(type, flags, binding, compIndex ^ 1, sources); - TextureOperation operation = context.CreateTextureOperation( - Instruction.Lod, - type, - flags, - handle, - compIndex ^ 1, // The instruction component order is the inverse of GLSL's. - new[] { tempDest }, - sources); + res = context.FPMultiply(res, ConstF(256.0f)); - context.Add(operation); + Operand fixedPointValue = context.FP32ConvertToS32(res); - tempDest = context.FPMultiply(tempDest, ConstF(256.0f)); - - Operand fixedPointValue = context.FP32ConvertToS32(tempDest); - - context.Copy(destOperand, fixedPointValue); + context.Copy(d, fixedPointValue); } } } @@ -1081,18 +1048,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - int handle = imm; - - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, imm, componentMask, dests, sources); } private static void EmitTxq( @@ -1111,10 +1067,6 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); - // TODO: Validate and use query. - Instruction inst = Instruction.TextureSize; - TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None; - Operand Ra() { if (srcA > RegisterConsts.RegisterZeroIndex) @@ -1157,31 +1109,55 @@ namespace Ryujinx.Graphics.Shader.Instructions type = context.Config.GpuAccessor.QuerySamplerType(imm); } + TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None; + + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + Instruction.TextureSize, + type, + TextureFormat.Unknown, + flags, + TextureOperation.DefaultCbufSlot, + imm); + for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) { if ((compMask & 1) != 0) { - Operand destOperand = GetDest(); + Operand d = GetDest(); - if (destOperand == null) + if (d == null) { break; } - TextureOperation operation = context.CreateTextureOperation( - inst, - type, - flags, - imm, - compIndex, - new[] { destOperand }, - sources); + // TODO: Validate and use query parameter. + Operand res = context.TextureSize(type, flags, binding, compIndex, sources); - context.Add(operation); + context.Copy(d, res); } } } + private static void EmitTextureSample( + EmitterContext context, + SamplerType type, + TextureFlags flags, + int handle, + int componentMask, + Operand[] dests, + Operand[] sources) + { + int binding = flags.HasFlag(TextureFlags.Bindless) ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + Instruction.TextureSample, + type, + TextureFormat.Unknown, + flags, + TextureOperation.DefaultCbufSlot, + handle); + + context.TextureSample(type, flags, binding, componentMask, dests, sources); + } + private static SamplerType ConvertSamplerType(TexDim dimensions) { return dimensions switch diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index b467fe533..fa5550a64 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -8,16 +8,14 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public TextureFormat Format { get; set; } public TextureFlags Flags { get; private set; } - public int CbufSlot { get; private set; } - public int Handle { get; private set; } + public int Binding { get; private set; } public TextureOperation( Instruction inst, SamplerType type, TextureFormat format, TextureFlags flags, - int cbufSlot, - int handle, + int binding, int compIndex, Operand[] dests, Operand[] sources) : base(inst, compIndex, dests, sources) @@ -25,30 +23,17 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Type = type; Format = format; Flags = flags; - CbufSlot = cbufSlot; - Handle = handle; + Binding = binding; } - public TextureOperation( - Instruction inst, - SamplerType type, - TextureFormat format, - TextureFlags flags, - int handle, - int compIndex, - Operand[] dests, - Operand[] sources) : this(inst, type, format, flags, DefaultCbufSlot, handle, compIndex, dests, sources) - { - } - - public void TurnIntoIndexed(int handle) + public void TurnIntoIndexed(int binding) { Type |= SamplerType.Indexed; Flags &= ~TextureFlags.Bindless; - Handle = handle; + Binding = binding; } - public void SetHandle(int handle, int cbufSlot = DefaultCbufSlot) + public void SetBinding(int binding) { if ((Flags & TextureFlags.Bindless) != 0) { @@ -57,8 +42,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation RemoveSource(0); } - CbufSlot = cbufSlot; - Handle = handle; + Binding = binding; } public void SetLodLevelFlag() diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 4ff2035a8..3970df1e9 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -8,24 +8,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public TextureFormat Format { get; } public TextureFlags Flags { get; } - public int CbufSlot { get; } - public int Handle { get; } + public int Binding { get; } public AstTextureOperation( Instruction inst, SamplerType type, TextureFormat format, TextureFlags flags, - int cbufSlot, - int handle, + int binding, int index, params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) { Type = type; Format = format; Flags = flags; - CbufSlot = cbufSlot; - Handle = handle; + Binding = binding; } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index 1da5cb657..048a260ab 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -6,11 +6,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { private readonly Dictionary<int, BufferDefinition> _constantBuffers; private readonly Dictionary<int, BufferDefinition> _storageBuffers; + private readonly Dictionary<int, TextureDefinition> _textures; + private readonly Dictionary<int, TextureDefinition> _images; private readonly Dictionary<int, MemoryDefinition> _localMemories; private readonly Dictionary<int, MemoryDefinition> _sharedMemories; public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers; public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers; + public IReadOnlyDictionary<int, TextureDefinition> Textures => _textures; + public IReadOnlyDictionary<int, TextureDefinition> Images => _images; public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories; public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories; @@ -18,20 +22,32 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { _constantBuffers = new Dictionary<int, BufferDefinition>(); _storageBuffers = new Dictionary<int, BufferDefinition>(); + _textures = new Dictionary<int, TextureDefinition>(); + _images = new Dictionary<int, TextureDefinition>(); _localMemories = new Dictionary<int, MemoryDefinition>(); _sharedMemories = new Dictionary<int, MemoryDefinition>(); } - public void AddConstantBuffer(int binding, BufferDefinition definition) + public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition) { _constantBuffers[binding] = definition; } - public void AddStorageBuffer(int binding, BufferDefinition definition) + public void AddOrUpdateStorageBuffer(int binding, BufferDefinition definition) { _storageBuffers[binding] = definition; } + public void AddOrUpdateTexture(int binding, TextureDefinition descriptor) + { + _textures[binding] = descriptor; + } + + public void AddOrUpdateImage(int binding, TextureDefinition descriptor) + { + _images[binding] = descriptor; + } + public int AddLocalMemory(MemoryDefinition definition) { int id = _localMemories.Count; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index a4e6444b0..87acedf62 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -125,15 +125,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AstTextureOperation GetAstTextureOperation(TextureOperation texOp) { - return new AstTextureOperation( - inst, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.CbufSlot, - texOp.Handle, - texOp.Index, - sources); + return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.Index, sources); } int componentsCount = BitOperations.PopCount((uint)operation.Index); diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs new file mode 100644 index 000000000..ba31dae6c --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.Graphics.Shader +{ + readonly struct TextureDefinition + { + public int Set { get; } + public int Binding { get; } + public string Name { get; } + public SamplerType Type { get; } + public TextureFormat Format { get; } + public TextureUsageFlags Flags { get; } + + public TextureDefinition(int set, int binding, string name, SamplerType type, TextureFormat format, TextureUsageFlags flags) + { + Set = set; + Binding = binding; + Name = name; + Type = type; + Format = format; + Flags = flags; + } + + public TextureDefinition SetFlag(TextureUsageFlags flag) + { + return new TextureDefinition(Set, Binding, Name, Type, Format, Flags | flag); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs index 626faa695..58b8e5a51 100644 --- a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs @@ -12,23 +12,16 @@ namespace Ryujinx.Graphics.Shader public readonly int CbufSlot; public readonly int HandleIndex; - public TextureUsageFlags Flags; + public readonly TextureUsageFlags Flags; - public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int cbufSlot, int handleIndex) + public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int cbufSlot, int handleIndex, TextureUsageFlags flags) { Binding = binding; Type = type; Format = format; CbufSlot = cbufSlot; HandleIndex = handleIndex; - Flags = TextureUsageFlags.None; - } - - public TextureDescriptor SetFlag(TextureUsageFlags flag) - { - Flags |= flag; - - return this; + Flags = flags; } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 9eedc3f91..08d4b9156 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -115,36 +115,6 @@ namespace Ryujinx.Graphics.Shader.Translation _operations.Add(operation); } - public TextureOperation CreateTextureOperation( - Instruction inst, - SamplerType type, - TextureFlags flags, - int handle, - int compIndex, - Operand[] dests, - params Operand[] sources) - { - return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dests, sources); - } - - public TextureOperation CreateTextureOperation( - Instruction inst, - SamplerType type, - TextureFormat format, - TextureFlags flags, - int handle, - int compIndex, - Operand[] dests, - params Operand[] sources) - { - if (!flags.HasFlag(TextureFlags.Bindless)) - { - Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle); - } - - return new TextureOperation(inst, type, format, flags, handle, compIndex, dests, sources); - } - public void FlagAttributeRead(int attribute) { if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId) diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index c2f1b790f..c92d05838 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -604,6 +604,45 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.Subtract, Local(), a, b); } + public static Operand ImageAtomic( + this EmitterContext context, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int binding, + Operand[] sources) + { + Operand dest = Local(); + + context.Add(new TextureOperation(Instruction.ImageAtomic, type, format, flags, binding, 0, new[] { dest }, sources)); + + return dest; + } + + public static void ImageLoad( + this EmitterContext context, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int binding, + int compMask, + Operand[] dests, + Operand[] sources) + { + context.Add(new TextureOperation(Instruction.ImageLoad, type, format, flags, binding, compMask, dests, sources)); + } + + public static void ImageStore( + this EmitterContext context, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int binding, + Operand[] sources) + { + context.Add(new TextureOperation(Instruction.ImageStore, type, format, flags, binding, 0, null, sources)); + } + public static Operand IsNan(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32) { return context.Add(fpType | Instruction.IsNan, Local(), a); @@ -666,6 +705,21 @@ namespace Ryujinx.Graphics.Shader.Translation : context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex); } + public static Operand Lod( + this EmitterContext context, + SamplerType type, + TextureFlags flags, + int binding, + int compIndex, + Operand[] sources) + { + Operand dest = Local(); + + context.Add(new TextureOperation(Instruction.Lod, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); + + return dest; + } + public static Operand MemoryBarrier(this EmitterContext context) { return context.Add(Instruction.MemoryBarrier); @@ -797,6 +851,33 @@ namespace Ryujinx.Graphics.Shader.Translation : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value); } + public static void TextureSample( + this EmitterContext context, + SamplerType type, + TextureFlags flags, + int binding, + int compMask, + Operand[] dests, + Operand[] sources) + { + context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources)); + } + + public static Operand TextureSize( + this EmitterContext context, + SamplerType type, + TextureFlags flags, + int binding, + int compIndex, + Operand[] sources) + { + Operand dest = Local(); + + context.Add(new TextureOperation(Instruction.TextureSize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); + + return dest; + } + public static Operand UnpackDouble2x32High(this EmitterContext context, Operand a) { return UnpackDouble2x32(context, a, 1); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index bb25c1602..bf087affb 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -222,8 +222,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot, bool rewriteSamplerType, bool isImage) { - texOp.SetHandle(cbufOffset, cbufSlot); - if (rewriteSamplerType) { SamplerType newType = config.GpuAccessor.QuerySamplerType(cbufOffset, cbufSlot); @@ -234,7 +232,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } else if (texOp.Type == SamplerType.TextureBuffer && newType == SamplerType.Texture1D) { - int coordsCount = 1; + int coordsCount = 2; if (InstEmit.Sample1DAs2D) { @@ -255,7 +253,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, cbufSlot, cbufOffset); + int binding = config.ResourceManager.GetTextureOrImageBinding( + texOp.Inst, + texOp.Type, + texOp.Format, + texOp.Flags & ~TextureFlags.Bindless, + cbufSlot, + cbufOffset); + + texOp.SetBinding(binding); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs index f966a4fc5..4b1bf76e5 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class BindlessToIndexed { + private const int NvnTextureBufferIndex = 2; + public static void RunPass(BasicBlock block, ShaderConfig config) { // We can turn a bindless texture access into a indexed access, @@ -43,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (ldcSrc0.Type != OperandType.Constant || !config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) || - src0CbufSlot != 2) + src0CbufSlot != NvnTextureBufferIndex) { continue; } @@ -102,8 +104,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle) { - texOp.TurnIntoIndexed(handle); - config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, handle); + int binding = config.ResourceManager.GetTextureOrImageBinding( + texOp.Inst, + texOp.Type | SamplerType.Indexed, + texOp.Format, + texOp.Flags & ~TextureFlags.Bindless, + NvnTextureBufferIndex, + handle); + + texOp.TurnIntoIndexed(binding); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index f3a5ba6c4..5991cd25d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System; using System.Collections.Generic; @@ -13,9 +14,13 @@ 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" }; + // TODO: Non-hardcoded array size. + public const int SamplerArraySize = 4; + + private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private readonly IGpuAccessor _gpuAccessor; + private readonly ShaderStage _stage; private readonly string _stagePrefix; private readonly int[] _cbSlotToBindingMap; @@ -27,6 +32,19 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly HashSet<int> _usedConstantBufferBindings; + private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format); + + private struct TextureMeta + { + public int Binding; + public bool AccurateType; + public SamplerType Type; + public TextureUsageFlags UsageFlags; + } + + private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures; + private readonly Dictionary<TextureInfo, TextureMeta> _usedImages; + public int LocalMemoryId { get; private set; } public int SharedMemoryId { get; private set; } @@ -36,6 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation { _gpuAccessor = gpuAccessor; Properties = properties; + _stage = stage; _stagePrefix = GetShaderStagePrefix(stage); _cbSlotToBindingMap = new int[18]; @@ -48,7 +67,10 @@ namespace Ryujinx.Graphics.Shader.Translation _usedConstantBufferBindings = new HashSet<int>(); - properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); + _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); + _usedImages = new Dictionary<TextureInfo, TextureMeta>(); + + properties.AddOrUpdateConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); LocalMemoryId = -1; SharedMemoryId = -1; @@ -166,6 +188,198 @@ namespace Ryujinx.Graphics.Shader.Translation return false; } + public int GetTextureOrImageBinding( + Instruction inst, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int cbufSlot, + int handle) + { + inst &= Instruction.Mask; + bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic; + bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic; + bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize; + bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize; + bool coherent = flags.HasFlag(TextureFlags.Coherent); + + if (!isImage) + { + format = TextureFormat.Unknown; + } + + int binding = GetTextureOrImageBinding(cbufSlot, handle, type, format, isImage, intCoords, isWrite, accurateType, coherent); + + _gpuAccessor.RegisterTexture(handle, cbufSlot); + + return binding; + } + + private int GetTextureOrImageBinding( + int cbufSlot, + int handle, + SamplerType type, + TextureFormat format, + bool isImage, + bool intCoords, + bool write, + bool accurateType, + bool coherent) + { + var dimensions = type.GetDimensions(); + var isIndexed = type.HasFlag(SamplerType.Indexed); + var dict = isImage ? _usedImages : _usedTextures; + + var usageFlags = TextureUsageFlags.None; + + if (intCoords) + { + usageFlags |= TextureUsageFlags.NeedsScaleValue; + + var canScale = _stage.SupportsRenderScale() && !isIndexed && !write && dimensions == 2; + + if (!canScale) + { + // Resolution scaling cannot be applied to this texture right now. + // Flag so that we know to blacklist scaling on related textures when binding them. + usageFlags |= TextureUsageFlags.ResScaleUnsupported; + } + } + + if (write) + { + usageFlags |= TextureUsageFlags.ImageStore; + } + + if (coherent) + { + usageFlags |= TextureUsageFlags.ImageCoherent; + } + + int arraySize = isIndexed ? SamplerArraySize : 1; + int firstBinding = -1; + + for (int layer = 0; layer < arraySize; layer++) + { + var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format); + var meta = new TextureMeta() + { + AccurateType = accurateType, + Type = type, + UsageFlags = usageFlags + }; + + int binding; + + if (dict.TryGetValue(info, out var existingMeta)) + { + dict[info] = MergeTextureMeta(meta, existingMeta); + binding = existingMeta.Binding; + } + else + { + bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer; + + binding = isImage + ? _gpuAccessor.QueryBindingImage(dict.Count, isBuffer) + : _gpuAccessor.QueryBindingTexture(dict.Count, isBuffer); + + meta.Binding = binding; + + dict.Add(info, meta); + } + + string nameSuffix; + + if (isImage) + { + nameSuffix = cbufSlot < 0 + ? $"i_tcb_{handle:X}_{format.ToGlslFormat()}" + : $"i_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}"; + } + else + { + nameSuffix = cbufSlot < 0 ? $"t_tcb_{handle:X}" : $"t_cb{cbufSlot}_{handle:X}"; + } + + var definition = new TextureDefinition( + isImage ? 3 : 2, + binding, + $"{_stagePrefix}_{nameSuffix}", + meta.Type, + info.Format, + meta.UsageFlags); + + if (isImage) + { + Properties.AddOrUpdateImage(binding, definition); + } + else + { + Properties.AddOrUpdateTexture(binding, definition); + } + + if (layer == 0) + { + firstBinding = binding; + } + } + + return firstBinding; + } + + private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta) + { + meta.Binding = existingMeta.Binding; + meta.UsageFlags |= existingMeta.UsageFlags; + + // If the texture we have has inaccurate type information, then + // we prefer the most accurate one. + if (existingMeta.AccurateType) + { + meta.AccurateType = true; + meta.Type = existingMeta.Type; + } + + return meta; + } + + public void SetUsageFlagsForTextureQuery(int binding, SamplerType type) + { + TextureInfo selectedInfo = default; + TextureMeta selectedMeta = default; + bool found = false; + + foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) + { + if (meta.Binding == binding) + { + selectedInfo = info; + selectedMeta = meta; + found = true; + break; + } + } + + if (found) + { + selectedMeta.UsageFlags |= TextureUsageFlags.NeedsScaleValue; + + var dimensions = type.GetDimensions(); + var isIndexed = type.HasFlag(SamplerType.Indexed); + var canScale = _stage.SupportsRenderScale() && !isIndexed && dimensions == 2; + + if (!canScale) + { + // Resolution scaling cannot be applied to this texture right now. + // Flag so that we know to blacklist scaling on related textures when binding them. + selectedMeta.UsageFlags |= TextureUsageFlags.ResScaleUnsupported; + } + + _usedTextures[selectedInfo] = selectedMeta; + } + } + public void SetUsedConstantBufferBinding(int binding) { _usedConstantBufferBindings.Add(binding); @@ -208,10 +422,8 @@ namespace Ryujinx.Graphics.Shader.Translation if (binding >= 0) { (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); - descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset) - { - Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None, - }; + BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None; + descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset, flags); } } @@ -223,6 +435,64 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors; } + public TextureDescriptor[] GetTextureDescriptors() + { + return GetDescriptors(_usedTextures, _usedTextures.Count); + } + + public TextureDescriptor[] GetImageDescriptors() + { + return GetDescriptors(_usedImages, _usedImages.Count); + } + + private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary<TextureInfo, TextureMeta> usedResources, int count) + { + TextureDescriptor[] descriptors = new TextureDescriptor[count]; + + int descriptorIndex = 0; + + foreach ((TextureInfo info, TextureMeta meta) in usedResources) + { + descriptors[descriptorIndex++] = new TextureDescriptor( + meta.Binding, + meta.Type, + info.Format, + info.CbufSlot, + info.Handle, + meta.UsageFlags); + } + + return descriptors; + } + + public (int, int) GetCbufSlotAndHandleForTexture(int binding) + { + foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) + { + if (meta.Binding == binding) + { + return (info.CbufSlot, info.Handle); + } + } + + throw new ArgumentException($"Binding {binding} is invalid."); + } + + private static int FindDescriptorIndex(TextureDescriptor[] array, int binding) + { + return Array.FindIndex(array, x => x.Binding == binding); + } + + public int FindTextureDescriptorIndex(int binding) + { + return FindDescriptorIndex(GetTextureDescriptors(), binding); + } + + public int FindImageDescriptorIndex(int binding) + { + return FindDescriptorIndex(GetImageDescriptors(), binding); + } + private void AddNewConstantBuffer(int binding, string name) { StructureType type = new(new[] @@ -230,7 +500,7 @@ namespace Ryujinx.Graphics.Shader.Translation new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), }); - Properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); + Properties.AddOrUpdateConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); } private void AddNewStorageBuffer(int binding, string name) @@ -240,7 +510,7 @@ namespace Ryujinx.Graphics.Shader.Translation new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), }); - Properties.AddStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type)); + Properties.AddOrUpdateStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type)); } public static string GetShaderStagePrefix(ShaderStage stage) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 42e3ecee3..0fa75203b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (texOp.Inst == Instruction.TextureSample) { - node = InsertCoordNormalization(node, config); + node = InsertCoordNormalization(hfm, node, config); node = InsertCoordGatherBias(node, config); node = InsertConstOffsets(node, config); @@ -285,8 +285,8 @@ namespace Ryujinx.Graphics.Shader.Translation { int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); int samplerIndex = isImage - ? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp) - : config.FindTextureDescriptorIndex(texOp); + ? config.ResourceManager.GetTextureDescriptors().Length + config.ResourceManager.FindImageDescriptorIndex(texOp.Binding) + : config.ResourceManager.FindTextureDescriptorIndex(texOp.Binding); for (int index = 0; index < coordsCount; index++) { @@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Shader.Translation TypeSupportsScale(texOp.Type)) { int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale); - int samplerIndex = config.FindTextureDescriptorIndex(texOp, ignoreType: true); + int samplerIndex = config.ResourceManager.FindTextureDescriptorIndex(texOp.Binding); for (int index = texOp.DestsCount - 1; index >= 0; index--) { @@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Shader.Translation return (type & SamplerType.Mask) == SamplerType.Texture2D; } - private static LinkedListNode<INode> InsertCoordNormalization(LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertCoordNormalization(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) { // Emulate non-normalized coordinates by normalizing the coordinates on the shader. // Without normalization, the coordinates are expected to the in the [0, W or H] range, @@ -378,9 +378,17 @@ namespace Ryujinx.Graphics.Shader.Translation TextureOperation texOp = (TextureOperation)node.Value; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + + if (isBindless) + { + return node; + } + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); + (int cbufSlot, int handle) = config.ResourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding); + + bool isCoordNormalized = config.GpuAccessor.QueryTextureCoordNormalized(handle, cbufSlot); if (isCoordNormalized || intCoords) { @@ -411,18 +419,17 @@ namespace Ryujinx.Graphics.Shader.Translation texSizeSources = new Operand[] { Const(0) }; } - node.List.AddBefore(node, new TextureOperation( + LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation( Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, index, new[] { coordSize }, texSizeSources)); - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); + config.ResourceManager.SetUsageFlagsForTextureQuery(texOp.Binding, texOp.Type); Operand source = texOp.GetSource(coordsIndex + index); @@ -431,6 +438,8 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize))); texOp.SetSource(coordsIndex + index, coordNormalized); + + InsertTextureSizeUnscale(hfm, textureSizeNode, config); } return node; @@ -491,14 +500,11 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, index, new[] { coordSize }, texSizeSources)); - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - node.List.AddBefore(node, new Operation( Instruction.FP32 | Instruction.Multiply, scaledSize, @@ -686,8 +692,6 @@ namespace Ryujinx.Graphics.Shader.Translation for (int index = 0; index < coordsCount; index++) { - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - Operand offset = Local(); Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; @@ -712,8 +716,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, 1, new[] { dests[destIndex++] }, newSources); @@ -744,8 +747,6 @@ namespace Ryujinx.Graphics.Shader.Translation for (int index = 0; index < coordsCount; index++) { - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - Operand offset = Local(); Operand intOffset = offsets[index]; @@ -771,8 +772,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, componentIndex, dests, sources); @@ -806,8 +806,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, 0, new[] { lod }, lodSources)); @@ -832,8 +831,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, index, new[] { texSizes[index] }, texSizeSources)); @@ -853,7 +851,9 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle, texOp.CbufSlot); + (int cbufSlot, int handle) = config.ResourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding); + + TextureFormat format = config.GpuAccessor.QueryTextureFormat(handle, cbufSlot); int maxPositive = format switch { diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index e93a709c2..5741d0288 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -2,16 +2,12 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System; using System.Collections.Generic; -using System.Linq; using System.Numerics; namespace Ryujinx.Graphics.Shader.Translation { class ShaderConfig { - // TODO: Non-hardcoded array size. - public const int SamplerArraySize = 4; - private const int ThreadsPerWarp = 32; public ShaderStage Stage { get; } @@ -110,20 +106,6 @@ namespace Ryujinx.Graphics.Shader.Translation public UInt128 NextInputAttributesComponents { get; private set; } public UInt128 ThisInputAttributesComponents { get; private set; } - private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format); - - private struct TextureMeta - { - public bool AccurateType; - public SamplerType Type; - public TextureUsageFlags UsageFlags; - } - - private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures; - private readonly Dictionary<TextureInfo, TextureMeta> _usedImages; - private TextureDescriptor[] _cachedTextureDescriptors; - private TextureDescriptor[] _cachedImageDescriptors; - public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize) { Stage = stage; @@ -141,9 +123,6 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>(); - _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); - _usedImages = new Dictionary<TextureInfo, TextureMeta>(); - ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) @@ -156,7 +135,7 @@ namespace Ryujinx.Graphics.Shader.Translation BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); - Properties.AddStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); + Properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); StructureType tfeDataStruct = new(new StructureField[] { @@ -167,7 +146,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int binding = Constants.TfeBufferBaseBinding + i; BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); - Properties.AddStorageBuffer(binding, tfeDataBuffer); + Properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer); } } } @@ -443,22 +422,6 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributes |= other.UsedInputAttributes; UsedOutputAttributes |= other.UsedOutputAttributes; - - foreach (var kv in other._usedTextures) - { - if (!_usedTextures.TryAdd(kv.Key, kv.Value)) - { - _usedTextures[kv.Key] = MergeTextureMeta(kv.Value, _usedTextures[kv.Key]); - } - } - - foreach (var kv in other._usedImages) - { - if (!_usedImages.TryAdd(kv.Key, kv.Value)) - { - _usedImages[kv.Key] = MergeTextureMeta(kv.Value, _usedImages[kv.Key]); - } - } } public void SetLayerOutputAttribute(int attr) @@ -642,196 +605,13 @@ namespace Ryujinx.Graphics.Shader.Translation UsedFeatures |= flags; } - public void SetUsedTexture( - Instruction inst, - SamplerType type, - TextureFormat format, - TextureFlags flags, - int cbufSlot, - int handle) - { - inst &= Instruction.Mask; - bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic; - bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic; - bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize; - bool coherent = flags.HasFlag(TextureFlags.Coherent); - - if (isImage) - { - SetUsedTextureOrImage(_usedImages, cbufSlot, handle, type, format, true, isWrite, false, coherent); - } - else - { - bool intCoords = flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize; - SetUsedTextureOrImage(_usedTextures, cbufSlot, handle, type, TextureFormat.Unknown, intCoords, false, accurateType, coherent); - } - - GpuAccessor.RegisterTexture(handle, cbufSlot); - } - - private void SetUsedTextureOrImage( - Dictionary<TextureInfo, TextureMeta> dict, - int cbufSlot, - int handle, - SamplerType type, - TextureFormat format, - bool intCoords, - bool write, - bool accurateType, - bool coherent) - { - var dimensions = type.GetDimensions(); - var isIndexed = type.HasFlag(SamplerType.Indexed); - - var usageFlags = TextureUsageFlags.None; - - if (intCoords) - { - usageFlags |= TextureUsageFlags.NeedsScaleValue; - - var canScale = Stage.SupportsRenderScale() && !isIndexed && !write && dimensions == 2; - - if (!canScale) - { - // Resolution scaling cannot be applied to this texture right now. - // Flag so that we know to blacklist scaling on related textures when binding them. - usageFlags |= TextureUsageFlags.ResScaleUnsupported; - } - } - - if (write) - { - usageFlags |= TextureUsageFlags.ImageStore; - } - - if (coherent) - { - usageFlags |= TextureUsageFlags.ImageCoherent; - } - - int arraySize = isIndexed ? SamplerArraySize : 1; - - for (int layer = 0; layer < arraySize; layer++) - { - var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format); - var meta = new TextureMeta() - { - AccurateType = accurateType, - Type = type, - UsageFlags = usageFlags, - }; - - if (dict.TryGetValue(info, out var existingMeta)) - { - dict[info] = MergeTextureMeta(meta, existingMeta); - } - else - { - dict.Add(info, meta); - } - } - } - - private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta) - { - meta.UsageFlags |= existingMeta.UsageFlags; - - // If the texture we have has inaccurate type information, then - // we prefer the most accurate one. - if (existingMeta.AccurateType) - { - meta.AccurateType = true; - meta.Type = existingMeta.Type; - } - - return meta; - } - - public TextureDescriptor[] GetTextureDescriptors() - { - return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture); - } - - public TextureDescriptor[] GetImageDescriptors() - { - return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, GpuAccessor.QueryBindingImage); - } - - private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary<TextureInfo, TextureMeta> dict, Func<int, bool, int> getBindingCallback) - { - var descriptors = new TextureDescriptor[dict.Count]; - - int i = 0; - foreach (var kv in dict.OrderBy(x => x.Key.Indexed).ThenBy(x => x.Key.Handle)) - { - var info = kv.Key; - var meta = kv.Value; - - bool isBuffer = (meta.Type & SamplerType.Mask) == SamplerType.TextureBuffer; - int binding = getBindingCallback(i, isBuffer); - - descriptors[i] = new TextureDescriptor(binding, meta.Type, info.Format, info.CbufSlot, info.Handle); - descriptors[i].SetFlag(meta.UsageFlags); - i++; - } - - return descriptors; - } - - public TextureDescriptor FindTextureDescriptor(AstTextureOperation texOp) - { - TextureDescriptor[] descriptors = GetTextureDescriptors(); - - for (int i = 0; i < descriptors.Length; i++) - { - var descriptor = descriptors[i]; - - if (descriptor.CbufSlot == texOp.CbufSlot && - descriptor.HandleIndex == texOp.Handle && - descriptor.Format == texOp.Format) - { - return descriptor; - } - } - - return default; - } - - private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false) - { - for (int i = 0; i < array.Length; i++) - { - var descriptor = array[i]; - - if ((descriptor.Type == texOp.Type || ignoreType) && - descriptor.CbufSlot == texOp.CbufSlot && - descriptor.HandleIndex == texOp.Handle && - descriptor.Format == texOp.Format) - { - return i; - } - } - - return -1; - } - - public int FindTextureDescriptorIndex(TextureOperation texOp, bool ignoreType = false) - { - return FindDescriptorIndex(GetTextureDescriptors(), texOp, ignoreType); - } - - public int FindImageDescriptorIndex(TextureOperation texOp) - { - return FindDescriptorIndex(GetImageDescriptors(), texOp); - } - public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None) { return new ShaderProgramInfo( ResourceManager.GetConstantBufferDescriptors(), ResourceManager.GetStorageBufferDescriptors(), - GetTextureDescriptors(), - GetImageDescriptors(), + ResourceManager.GetTextureDescriptors(), + ResourceManager.GetImageDescriptors(), identification, GpLayerInputAttribute, Stage, From c19c8bbadea027e4f31a0739fac0f2a27fbe6dbf Mon Sep 17 00:00:00 2001 From: Theun de Bruijn <theun@theundebruijn.com> Date: Thu, 6 Jul 2023 20:10:15 +1000 Subject: [PATCH 697/737] Headless: Add support for fullscreen option (#5339) * Headless: Added support for fullscreen option * Headless: cleanup of fullscreen support * Headless: fullscreen support : implemented proposed changes * Headless: fullscreen support: cleanup * Headless: fullscreen support: fix for OpenGL scaling * Headless: fullscreen support: cleanup * Headless: fullscreen support: cleanup * Headless: fullscreen support: add. macOS fullscreen fix * Headless: fullscreen support: cleanup * Headless: fullscreen support: cleanup * Headless: fullscreen support: cleanup --- .../OpenGL/OpenGLWindow.cs | 18 ++++++++++++++++-- src/Ryujinx.Headless.SDL2/Options.cs | 3 +++ src/Ryujinx.Headless.SDL2/Program.cs | 5 +++++ src/Ryujinx.Headless.SDL2/WindowBase.cs | 19 ++++++++++++++----- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs index 199f723e9..355dcf6c7 100644 --- a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs @@ -151,8 +151,22 @@ namespace Ryujinx.Headless.SDL2.OpenGL GL.Clear(ClearBufferMask.ColorBufferBit); SwapBuffers(); - Renderer?.Window.SetSize(DefaultWidth, DefaultHeight); - MouseDriver.SetClientSize(DefaultWidth, DefaultHeight); + if (IsFullscreen) + { + // NOTE: grabbing the main display's dimensions directly as OpenGL doesn't scale along like the VulkanWindow. + // we might have to amend this if people run this on a non-primary display set to a different resolution. + SDL_Rect displayBounds; + SDL_GetDisplayBounds(0, out displayBounds); + + Renderer?.Window.SetSize(displayBounds.w, displayBounds.h); + MouseDriver.SetClientSize(displayBounds.w, displayBounds.h); + } + + else + { + Renderer?.Window.SetSize(DefaultWidth, DefaultHeight); + MouseDriver.SetClientSize(DefaultWidth, DefaultHeight); + } } protected override void InitializeRenderer() { } diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index 86a6c63c4..e44cedec9 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -14,6 +14,9 @@ namespace Ryujinx.Headless.SDL2 [Option("profile", Required = false, HelpText = "Set the user profile to launch the game with.")] public string UserProfile { get; set; } + [Option("fullscreen", Required = false, HelpText = "Launch the game in fullscreen mode.")] + public bool IsFullscreen { get; set; } + // Input [Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")] diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 39eae14a8..98cc5abf4 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -64,6 +64,9 @@ namespace Ryujinx.Headless.SDL2 { Version = ReleaseInformation.GetVersion(); + // Make process DPI aware for proper window sizing on high-res screens. + ForceDpiAware.Windows(); + Console.Title = $"Ryujinx Console {Version} (Headless SDL2)"; if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) @@ -592,6 +595,8 @@ namespace Ryujinx.Headless.SDL2 _window = window; + _window.IsFullscreen = options.IsFullscreen; + _emulationContext = InitializeEmulationContext(window, renderer, options); SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index 2fcd00f86..c542a4e61 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -55,6 +55,7 @@ namespace Ryujinx.Headless.SDL2 public IHostUiTheme HostUiTheme { get; } public int Width { get; private set; } public int Height { get; private set; } + public bool IsFullscreen { get; set; } protected SDL2MouseDriver MouseDriver; private readonly InputManager _inputManager; @@ -158,7 +159,9 @@ namespace Ryujinx.Headless.SDL2 string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})"; string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; - WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DefaultWidth, DefaultHeight, DefaultFlags | GetWindowFlags()); + SDL_WindowFlags fullscreenFlag = IsFullscreen ? SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP : 0; + + WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DefaultWidth, DefaultHeight, DefaultFlags | fullscreenFlag | GetWindowFlags()); if (WindowHandle == IntPtr.Zero) { @@ -185,10 +188,16 @@ namespace Ryujinx.Headless.SDL2 switch (evnt.window.windowEvent) { case SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED: - Width = evnt.window.data1; - Height = evnt.window.data2; - Renderer?.Window.SetSize(Width, Height); - MouseDriver.SetClientSize(Width, Height); + // Unlike on Windows, this event fires on macOS when triggering fullscreen mode. + // And promptly crashes the process because `Renderer?.window.SetSize` is undefined. + // As we don't need this to fire in either case we can test for isFullscreen. + if (!IsFullscreen) + { + Width = evnt.window.data1; + Height = evnt.window.data2; + Renderer?.Window.SetSize(Width, Height); + MouseDriver.SetClientSize(Width, Height); + } break; case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE: From 2b5abac809dacb351ec69e322732d45ea01a4d65 Mon Sep 17 00:00:00 2001 From: SuperSamus <40663462+SuperSamus@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:11:26 +0200 Subject: [PATCH 698/737] sdl: set `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` to 0 (#5433) Nintendo controllers notoriously have the A/B and X/Y buttons swapped, compared to the standard. In order to combat this, when setting the default controller layout, Ryujinx checks whether the controller name contains "Nintendo", and swaps the mapping accordingly. However, the reason the mapping is inverted in the first place is because SDL has `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` set to 1 by default. By setting it to 0, the mapping will be based on the buttons' position instead. So, by doing it (and removing the `isNintendoStyle` variable), we get the following advantages: - The mapping will be the same on all controllers, removing the need to adjust custom mappings depending on what controller is used - Users who already set `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` to 0 globally for other games/applications (like me) won't have a wrong default mapping - Checking whether the controller name contains "Nintendo" is ugly Disadvantages: - Breaks the controller configuration for existing users who are using a Nintendo controller --- .../UI/ViewModels/ControllerInputViewModel.cs | 10 ++++------ src/Ryujinx.Headless.SDL2/Program.cs | 10 ++++------ src/Ryujinx.SDL2.Common/SDL2Driver.cs | 1 + src/Ryujinx/Ui/Windows/ControllerWindow.cs | 10 ++++------ 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs index fda58504e..8b5dbcef0 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs @@ -597,8 +597,6 @@ namespace Ryujinx.Ava.UI.ViewModels } else if (activeDevice.Type == DeviceType.Controller) { - bool isNintendoStyle = Devices.ToList().Find(x => x.Id == activeDevice.Id).Name.Contains("Nintendo"); - string id = activeDevice.Id.Split(" ")[0]; config = new StandardControllerInputConfig @@ -633,10 +631,10 @@ namespace Ryujinx.Ava.UI.ViewModels }, RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonA = ConfigGamepadInputId.B, + ButtonB = ConfigGamepadInputId.A, + ButtonX = ConfigGamepadInputId.Y, + ButtonY = ConfigGamepadInputId.X, ButtonPlus = ConfigGamepadInputId.Plus, ButtonR = ConfigGamepadInputId.RightShoulder, ButtonZr = ConfigGamepadInputId.RightTrigger, diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 98cc5abf4..ffcac9ed5 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -195,8 +195,6 @@ namespace Ryujinx.Headless.SDL2 } else { - bool isNintendoStyle = gamepadName.Contains("Nintendo"); - config = new StandardControllerInputConfig { Version = InputConfig.CurrentVersion, @@ -232,10 +230,10 @@ namespace Ryujinx.Headless.SDL2 RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonA = ConfigGamepadInputId.B, + ButtonB = ConfigGamepadInputId.A, + ButtonX = ConfigGamepadInputId.Y, + ButtonY = ConfigGamepadInputId.X, ButtonPlus = ConfigGamepadInputId.Plus, ButtonR = ConfigGamepadInputId.RightShoulder, ButtonZr = ConfigGamepadInputId.RightTrigger, diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 2642b26fa..3411fa041 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -60,6 +60,7 @@ namespace Ryujinx.SDL2.Common SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); diff --git a/src/Ryujinx/Ui/Windows/ControllerWindow.cs b/src/Ryujinx/Ui/Windows/ControllerWindow.cs index ebf22ab60..2993e1f23 100644 --- a/src/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/src/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -1035,8 +1035,6 @@ namespace Ryujinx.Ui.Windows } else if (_inputDevice.ActiveId.StartsWith("controller")) { - bool isNintendoStyle = _inputDevice.ActiveText.Contains("Nintendo"); - config = new StandardControllerInputConfig { Version = InputConfig.CurrentVersion, @@ -1072,10 +1070,10 @@ namespace Ryujinx.Ui.Windows RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonA = ConfigGamepadInputId.B, + ButtonB = ConfigGamepadInputId.A, + ButtonX = ConfigGamepadInputId.Y, + ButtonY = ConfigGamepadInputId.X, ButtonPlus = ConfigGamepadInputId.Plus, ButtonR = ConfigGamepadInputId.RightShoulder, ButtonZr = ConfigGamepadInputId.RightTrigger, From 8a363b5df2387bd254a3dd48bfd3c9884ff74dab Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Thu, 6 Jul 2023 18:08:14 +0200 Subject: [PATCH 699/737] Revert "sdl: set `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` to 0 (#5433)" (#5439) This reverts commit 2b5abac809dacb351ec69e322732d45ea01a4d65. --- .../UI/ViewModels/ControllerInputViewModel.cs | 10 ++++++---- src/Ryujinx.Headless.SDL2/Program.cs | 10 ++++++---- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 1 - src/Ryujinx/Ui/Windows/ControllerWindow.cs | 10 ++++++---- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs index 8b5dbcef0..fda58504e 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs @@ -597,6 +597,8 @@ namespace Ryujinx.Ava.UI.ViewModels } else if (activeDevice.Type == DeviceType.Controller) { + bool isNintendoStyle = Devices.ToList().Find(x => x.Id == activeDevice.Id).Name.Contains("Nintendo"); + string id = activeDevice.Id.Split(" ")[0]; config = new StandardControllerInputConfig @@ -631,10 +633,10 @@ namespace Ryujinx.Ava.UI.ViewModels }, RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = ConfigGamepadInputId.B, - ButtonB = ConfigGamepadInputId.A, - ButtonX = ConfigGamepadInputId.Y, - ButtonY = ConfigGamepadInputId.X, + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, ButtonPlus = ConfigGamepadInputId.Plus, ButtonR = ConfigGamepadInputId.RightShoulder, ButtonZr = ConfigGamepadInputId.RightTrigger, diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index ffcac9ed5..98cc5abf4 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -195,6 +195,8 @@ namespace Ryujinx.Headless.SDL2 } else { + bool isNintendoStyle = gamepadName.Contains("Nintendo"); + config = new StandardControllerInputConfig { Version = InputConfig.CurrentVersion, @@ -230,10 +232,10 @@ namespace Ryujinx.Headless.SDL2 RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = ConfigGamepadInputId.B, - ButtonB = ConfigGamepadInputId.A, - ButtonX = ConfigGamepadInputId.Y, - ButtonY = ConfigGamepadInputId.X, + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, ButtonPlus = ConfigGamepadInputId.Plus, ButtonR = ConfigGamepadInputId.RightShoulder, ButtonZr = ConfigGamepadInputId.RightTrigger, diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 3411fa041..2642b26fa 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -60,7 +60,6 @@ namespace Ryujinx.SDL2.Common SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); - SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); diff --git a/src/Ryujinx/Ui/Windows/ControllerWindow.cs b/src/Ryujinx/Ui/Windows/ControllerWindow.cs index 2993e1f23..ebf22ab60 100644 --- a/src/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/src/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -1035,6 +1035,8 @@ namespace Ryujinx.Ui.Windows } else if (_inputDevice.ActiveId.StartsWith("controller")) { + bool isNintendoStyle = _inputDevice.ActiveText.Contains("Nintendo"); + config = new StandardControllerInputConfig { Version = InputConfig.CurrentVersion, @@ -1070,10 +1072,10 @@ namespace Ryujinx.Ui.Windows RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { - ButtonA = ConfigGamepadInputId.B, - ButtonB = ConfigGamepadInputId.A, - ButtonX = ConfigGamepadInputId.Y, - ButtonY = ConfigGamepadInputId.X, + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, ButtonPlus = ConfigGamepadInputId.Plus, ButtonR = ConfigGamepadInputId.RightShoulder, ButtonZr = ConfigGamepadInputId.RightTrigger, From 6c515e18228b93c4d856129ba55a692e830cbdaa Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 7 Jul 2023 23:03:27 +0200 Subject: [PATCH 700/737] [Ryujinx.Ava] Address dotnet-format issues (#5361) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Silence dotnet format IDE0059 warnings * Address or silence dotnet format IDE1006 warnings * Address dotnet format CA1816 warnings * Address dotnet format CA1822 warnings * Address or silence dotnet format CA1069 warnings * Make dotnet format succeed in style mode * Address dotnet format CA1401 warnings * Address remaining dotnet format analyzer warnings * Address review comments * dotnet-format fixes after rebase * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Another rebase, another dotnet format run * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Add comments to disabled warnings * Remove a few unused parameters * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Fix and silence a few dotnet-format warnings again * Address IDE0260 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * dotnet format pass with new editorconfig * Fix naming style issues * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Revert one suggestion * Second dotnet format pass and fix build issues * Final pass of dotnet format * Add trailing commas * Fix formatting issues * Keep unnecessary assignment in IconColorPicker.cs * Use using declarations and extend resource lifetimes * Fix rebase issues * Adjust comment spacing * Fix typo * Fix naming issues * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Revert unintentional change * Remove unused file * Remove static keyword from ViewModels Binding of static members doesn't work and is silently ignored. --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> --- src/Ryujinx.Ava/App.axaml.cs | 5 +- src/Ryujinx.Ava/AppHost.cs | 233 +++++------ src/Ryujinx.Ava/Common/ApplicationHelper.cs | 25 +- src/Ryujinx.Ava/Common/ApplicationSort.cs | 4 +- src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs | 4 +- .../Common/Locale/LocaleExtension.cs | 6 +- .../Common/Locale/LocaleManager.cs | 20 +- src/Ryujinx.Ava/Input/AvaloniaKeyboard.cs | 28 +- .../Input/AvaloniaKeyboardDriver.cs | 20 +- .../Input/AvaloniaKeyboardMappingHelper.cs | 4 +- src/Ryujinx.Ava/Input/AvaloniaMouse.cs | 10 +- src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs | 44 +-- src/Ryujinx.Ava/Modules/Updater/Updater.cs | 200 +++++----- src/Ryujinx.Ava/Program.cs | 32 +- src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs | 13 +- .../Applet/AvaloniaDynamicTextInputHandler.cs | 8 +- .../UI/Applet/AvaloniaHostUiTheme.cs | 10 +- .../UI/Applet/ErrorAppletWindow.axaml.cs | 6 +- .../UI/Applet/SwkbdAppletDialog.axaml.cs | 20 +- .../Controls/ApplicationContextMenu.axaml.cs | 5 +- .../UI/Controls/ApplicationGridView.axaml.cs | 2 +- .../UI/Controls/ApplicationListView.axaml.cs | 2 +- .../UI/Controls/NavigationDialogHost.axaml.cs | 21 +- .../UI/Controls/UpdateWaitWindow.axaml.cs | 2 +- .../UI/Helpers/ApplicationOpenedEventArgs.cs | 2 +- .../UI/Helpers/BitmapArrayValueConverter.cs | 2 +- .../UI/Helpers/ButtonKeyAssigner.cs | 2 +- .../UI/Helpers/ContentDialogHelper.cs | 61 ++- .../UI/Helpers/GlyphValueConverter.cs | 17 +- src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs | 2 +- .../UI/Helpers/KeyValueConverter.cs | 2 +- src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs | 28 +- src/Ryujinx.Ava/UI/Helpers/MiniCommand.cs | 4 +- .../UI/Helpers/NotificationHelper.cs | 8 +- .../UI/Helpers/NullableDateTimeConverter.cs | 2 +- .../UI/Helpers/OffscreenTextBox.cs | 10 +- src/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs | 13 +- src/Ryujinx.Ava/UI/Helpers/UserResult.cs | 2 +- .../UI/Helpers/Win32NativeInterop.cs | 54 +-- src/Ryujinx.Ava/UI/Models/CheatModel.cs | 8 +- src/Ryujinx.Ava/UI/Models/CheatsList.cs | 6 +- src/Ryujinx.Ava/UI/Models/DeviceType.cs | 4 +- .../UI/Models/DownloadableContentModel.cs | 12 +- .../Models/Generic/LastPlayedSortComparer.cs | 2 +- .../UI/Models/InputConfiguration.cs | 298 +++++++------- .../UI/Models/ProfileImageModel.cs | 2 +- src/Ryujinx.Ava/UI/Models/SaveModel.cs | 13 +- .../UI/Models/StatusUpdatedEventArgs.cs | 2 +- src/Ryujinx.Ava/UI/Models/TempProfile.cs | 4 +- src/Ryujinx.Ava/UI/Models/TimeZone.cs | 2 +- src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs | 2 +- src/Ryujinx.Ava/UI/Models/UserProfile.cs | 5 +- src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 78 ++-- .../UI/Renderer/EmbeddedWindowOpenGL.cs | 2 +- .../UI/Renderer/EmbeddedWindowVulkan.cs | 4 +- .../UI/Renderer/OpenTKBindingsContext.cs | 2 +- .../UI/Renderer/RendererHost.axaml.cs | 8 +- .../UI/Renderer/SPBOpenGLContext.cs | 6 +- .../UI/ViewModels/AboutWindowViewModel.cs | 5 +- .../UI/ViewModels/AmiiboWindowViewModel.cs | 38 +- .../UI/ViewModels/AvatarProfileViewModel.cs | 363 ------------------ src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs | 2 +- .../UI/ViewModels/ControllerInputViewModel.cs | 111 +++--- .../DownloadableContentManagerViewModel.cs | 49 ++- .../UI/ViewModels/MainWindowViewModel.cs | 102 ++--- .../UI/ViewModels/MotionInputViewModel.cs | 2 +- .../UI/ViewModels/RumbleInputViewModel.cs | 2 +- .../UI/ViewModels/SettingsViewModel.cs | 7 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 61 ++- .../UserFirmwareAvatarSelectorViewModel.cs | 166 ++++---- .../UserProfileImageSelectorViewModel.cs | 4 +- .../UI/ViewModels/UserProfileViewModel.cs | 9 +- .../UI/ViewModels/UserSaveManagerViewModel.cs | 25 +- .../Views/Input/ControllerInputView.axaml.cs | 4 +- .../UI/Views/Input/MotionInputView.axaml.cs | 8 +- .../UI/Views/Input/RumbleInputView.axaml.cs | 8 +- .../UI/Views/Main/MainMenuBarView.axaml.cs | 22 +- .../UI/Views/Main/MainStatusBarView.axaml.cs | 2 +- .../UI/Views/Main/MainViewControls.axaml.cs | 8 +- .../Views/Settings/SettingsAudioView.axaml.cs | 2 +- .../Views/Settings/SettingsCPUView.axaml.cs | 2 +- .../Settings/SettingsGraphicsView.axaml.cs | 2 +- .../Settings/SettingsHotkeysView.axaml.cs | 12 +- .../Views/Settings/SettingsInputView.axaml.cs | 2 +- .../Settings/SettingsLoggingView.axaml.cs | 2 +- .../Settings/SettingsNetworkView.axaml.cs | 2 +- .../Settings/SettingsSystemView.axaml.cs | 4 +- .../UI/Views/Settings/SettingsUIView.axaml | 2 +- .../UI/Views/Settings/SettingsUIView.axaml.cs | 19 +- .../UI/Views/User/UserEditorView.axaml.cs | 14 +- .../UserFirmwareAvatarSelectorView.axaml.cs | 5 +- .../UserProfileImageSelectorView.axaml.cs | 20 +- .../UI/Views/User/UserRecovererView.axaml.cs | 2 +- .../Views/User/UserSaveManagerView.axaml.cs | 19 +- .../UI/Views/User/UserSelectorView.axaml.cs | 7 +- .../UI/Windows/AboutWindow.axaml.cs | 11 +- .../UI/Windows/AmiiboWindow.axaml.cs | 9 +- .../UI/Windows/CheatWindow.axaml.cs | 20 +- .../ContentDialogOverlayWindow.axaml.cs | 7 +- .../DownloadableContentManagerWindow.axaml | 4 +- .../DownloadableContentManagerWindow.axaml.cs | 14 +- src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs | 2 + .../UI/Windows/MainWindow.axaml.cs | 39 +- .../UI/Windows/SettingsWindow.axaml | 4 +- .../UI/Windows/SettingsWindow.axaml.cs | 6 +- src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs | 5 +- .../UI/Windows/TitleUpdateWindow.axaml.cs | 14 +- .../FileSystem/VirtualFileSystem.cs | 1 - 108 files changed, 1140 insertions(+), 1503 deletions(-) delete mode 100644 src/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs diff --git a/src/Ryujinx.Ava/App.axaml.cs b/src/Ryujinx.Ava/App.axaml.cs index e36cbfdd6..4ecc424a6 100644 --- a/src/Ryujinx.Ava/App.axaml.cs +++ b/src/Ryujinx.Ava/App.axaml.cs @@ -5,7 +5,6 @@ using Avalonia.Styling; using Avalonia.Threading; using FluentAvalonia.Styling; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; @@ -67,7 +66,7 @@ namespace Ryujinx.Ava if (result == UserResult.Yes) { - var path = Process.GetCurrentProcess().MainModule.FileName; + var path = Environment.ProcessPath; var proc = Process.Start(path, CommandLineState.Arguments); desktop.Shutdown(); Environment.Exit(0); @@ -151,4 +150,4 @@ namespace Ryujinx.Ava } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index c2b1064be..7c1ce542c 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -1,4 +1,5 @@ using ARMeilleure.Translation; +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; @@ -26,6 +27,7 @@ using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; +using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; @@ -41,7 +43,6 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SPB.Graphics.Exceptions; using SPB.Graphics.Vulkan; using System; using System.Collections.Generic; @@ -50,10 +51,12 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; +using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; using Image = SixLabors.ImageSharp.Image; using InputManager = Ryujinx.Input.HLE.InputManager; using Key = Ryujinx.Input.Key; using MouseButton = Ryujinx.Input.MouseButton; +using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; using Size = Avalonia.Size; using Switch = Ryujinx.HLE.Switch; @@ -61,31 +64,31 @@ namespace Ryujinx.Ava { internal class AppHost { - private const int CursorHideIdleTime = 5; // Hide Cursor seconds. + private const int CursorHideIdleTime = 5; // Hide Cursor seconds. private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. - private const int TargetFps = 60; - private const float VolumeDelta = 0.05f; + private const int TargetFps = 60; + private const float VolumeDelta = 0.05f; - private static readonly Cursor InvisibleCursor = new(StandardCursorType.None); - private readonly IntPtr InvisibleCursorWin; - private readonly IntPtr DefaultCursorWin; + private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); + private readonly IntPtr _invisibleCursorWin; + private readonly IntPtr _defaultCursorWin; - private readonly long _ticksPerFrame; + private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; - private long _ticks; + private long _ticks; - private readonly AccountManager _accountManager; + private readonly AccountManager _accountManager; private readonly UserChannelPersistence _userChannelPersistence; - private readonly InputManager _inputManager; + private readonly InputManager _inputManager; private readonly MainWindowViewModel _viewModel; - private readonly IKeyboard _keyboardInterface; - private readonly TopLevel _topLevel; - public RendererHost _rendererHost; + private readonly IKeyboard _keyboardInterface; + private readonly TopLevel _topLevel; + public RendererHost RendererHost; private readonly GraphicsDebugLevel _glLogLevel; - private float _newVolume; - private KeyboardHotkeyState _prevHotkeyState; + private float _newVolume; + private KeyboardHotkeyState _prevHotkeyState; private long _lastCursorMoveTime; private bool _isCursorInRenderer = true; @@ -94,14 +97,14 @@ namespace Ryujinx.Ava private bool _isActive; private bool _renderingStarted; - private ManualResetEvent _gpuDoneEvent; + private readonly ManualResetEvent _gpuDoneEvent; - private IRenderer _renderer; - private readonly Thread _renderingThread; + private IRenderer _renderer; + private readonly Thread _renderingThread; private readonly CancellationTokenSource _gpuCancellationTokenSource; private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - private bool _dialogShown; + private bool _dialogShown; private readonly bool _isFirmwareTitle; private readonly object _lockObject = new(); @@ -109,55 +112,55 @@ namespace Ryujinx.Ava public event EventHandler AppExit; public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent; - public VirtualFileSystem VirtualFileSystem { get; } - public ContentManager ContentManager { get; } - public NpadManager NpadManager { get; } + public VirtualFileSystem VirtualFileSystem { get; } + public ContentManager ContentManager { get; } + public NpadManager NpadManager { get; } public TouchScreenManager TouchScreenManager { get; } - public Switch Device { get; set; } + public Switch Device { get; set; } - public int Width { get; private set; } - public int Height { get; private set; } - public string ApplicationPath { get; private set; } - public bool ScreenshotRequested { get; set; } + public int Width { get; private set; } + public int Height { get; private set; } + public string ApplicationPath { get; private set; } + public bool ScreenshotRequested { get; set; } public AppHost( - RendererHost renderer, - InputManager inputManager, - string applicationPath, - VirtualFileSystem virtualFileSystem, - ContentManager contentManager, - AccountManager accountManager, + RendererHost renderer, + InputManager inputManager, + string applicationPath, + VirtualFileSystem virtualFileSystem, + ContentManager contentManager, + AccountManager accountManager, UserChannelPersistence userChannelPersistence, - MainWindowViewModel viewmodel, - TopLevel topLevel) + MainWindowViewModel viewmodel, + TopLevel topLevel) { - _viewModel = viewmodel; - _inputManager = inputManager; - _accountManager = accountManager; + _viewModel = viewmodel; + _inputManager = inputManager; + _accountManager = accountManager; _userChannelPersistence = userChannelPersistence; - _renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; - _topLevel = topLevel; + _renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; + _topLevel = topLevel; _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - NpadManager = _inputManager.CreateNpadManager(); + NpadManager = _inputManager.CreateNpadManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager(); - ApplicationPath = applicationPath; - VirtualFileSystem = virtualFileSystem; - ContentManager = contentManager; + ApplicationPath = applicationPath; + VirtualFileSystem = virtualFileSystem; + ContentManager = contentManager; - _rendererHost = renderer; + RendererHost = renderer; - _chrono = new Stopwatch(); + _chrono = new Stopwatch(); _ticksPerFrame = Stopwatch.Frequency / TargetFps; if (ApplicationPath.StartsWith("@SystemContent")) { - ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); + ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); _isFirmwareTitle = true; } @@ -170,21 +173,21 @@ namespace Ryujinx.Ava if (OperatingSystem.IsWindows()) { - InvisibleCursorWin = CreateEmptyCursor(); - DefaultCursorWin = CreateArrowCursor(); + _invisibleCursorWin = CreateEmptyCursor(); + _defaultCursorWin = CreateArrowCursor(); } ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; - ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; _gpuCancellationTokenSource = new CancellationTokenSource(); _gpuDoneEvent = new ManualResetEvent(false); @@ -196,10 +199,10 @@ namespace Ryujinx.Ava { _lastCursorMoveTime = Stopwatch.GetTimestamp(); - if (_rendererHost.EmbeddedWindow.TransformedBounds != null) + if (RendererHost.EmbeddedWindow.TransformedBounds != null) { - var point = e.GetCurrentPoint(window).Position; - var bounds = _rendererHost.EmbeddedWindow.TransformedBounds.Value.Clip; + var point = e.GetCurrentPoint(window).Position; + var bounds = RendererHost.EmbeddedWindow.TransformedBounds.Value.Clip; _isCursorInRenderer = point.X >= bounds.X && point.X <= bounds.Width + bounds.X && @@ -220,7 +223,7 @@ namespace Ryujinx.Ava _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); } - private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e) + private void UpdateScalingFilter(object sender, ReactiveEventArgs<ScalingFilter> e) { _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); @@ -234,7 +237,7 @@ namespace Ryujinx.Ava if (OperatingSystem.IsWindows()) { - SetCursor(DefaultCursorWin); + SetCursor(_defaultCursorWin); } }); } @@ -243,11 +246,11 @@ namespace Ryujinx.Ava { Dispatcher.UIThread.Post(() => { - _viewModel.Cursor = InvisibleCursor; + _viewModel.Cursor = _invisibleCursor; if (OperatingSystem.IsWindows()) { - SetCursor(InvisibleCursorWin); + SetCursor(_invisibleCursorWin); } }); } @@ -271,12 +274,12 @@ namespace Ryujinx.Ava lock (_lockObject) { DateTime currentTime = DateTime.Now; - string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; + string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; string directory = AppDataManager.Mode switch { AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") + _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), }; string path = Path.Combine(directory, filename); @@ -305,9 +308,9 @@ namespace Ryujinx.Ava image.Mutate(x => x.Flip(FlipMode.Vertical)); } - image.SaveAsPng(path, new PngEncoder() + image.SaveAsPng(path, new PngEncoder { - ColorType = PngColorType.Rgb + ColorType = PngColorType.Rgb, }); image.Dispose(); @@ -336,21 +339,21 @@ namespace Ryujinx.Ava _viewModel.IsGameRunning = true; - var activeProcess = Device.Processes.ActiveApplication; + var activeProcess = Device.Processes.ActiveApplication; - string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; + string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}"; - string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; - string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; + string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; + string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; Dispatcher.UIThread.InvokeAsync(() => { _viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); - _viewModel.SetUIProgressHandlers(Device); + _viewModel.SetUiProgressHandlers(Device); - _rendererHost.SizeChanged += Window_SizeChanged; + RendererHost.SizeChanged += Window_SizeChanged; _isActive = true; @@ -379,7 +382,7 @@ namespace Ryujinx.Ava } } - private void UpdateAntiAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e) + private void UpdateAntiAliasing(object sender, ReactiveEventArgs<AntiAliasing> e) { _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); } @@ -419,7 +422,7 @@ namespace Ryujinx.Ava } _isStopped = true; - _isActive = false; + _isActive = false; } public void DisposeContext() @@ -448,16 +451,16 @@ namespace Ryujinx.Ava { if (Device.Processes != null) { - _viewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); + MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); } ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; - ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; - ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; + ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; + ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; + ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved; _topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved; @@ -477,7 +480,7 @@ namespace Ryujinx.Ava _windowsMultimediaTimerResolution = null; } - if (_rendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) + if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) { // Try to bind the OpenGL context before calling the shutdown event. openGlWindow.MakeCurrent(false, false); @@ -508,7 +511,7 @@ namespace Ryujinx.Ava SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) { @@ -526,7 +529,7 @@ namespace Ryujinx.Ava if (result != UserResult.Yes) { - await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); + await UserErrorDialog.ShowUserErrorDialog(userError); Device.Dispose(); return false; @@ -535,7 +538,7 @@ namespace Ryujinx.Ava if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) { - await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); + await UserErrorDialog.ShowUserErrorDialog(userError); Device.Dispose(); return false; @@ -558,7 +561,7 @@ namespace Ryujinx.Ava } else { - await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); + await UserErrorDialog.ShowUserErrorDialog(userError); Device.Dispose(); return false; @@ -727,7 +730,7 @@ namespace Ryujinx.Ava { renderer = new VulkanRenderer( Vk.GetApi(), - (_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, + (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, VulkanHelper.GetRequiredInstanceExtensions, ConfigurationState.Instance.Graphics.PreferredGpu.Value); } @@ -738,18 +741,18 @@ namespace Ryujinx.Ava BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - var isGALthreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - if (isGALthreaded) + var isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); + if (isGALThreaded) { renderer = new ThreadedRenderer(renderer); } - Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALthreaded}"); + Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); // Initialize Configuration. - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB; + var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB; - HLE.HLEConfiguration configuration = new(VirtualFileSystem, + HLEConfiguration configuration = new(VirtualFileSystem, _viewModel.LibHacHorizonManager, ContentManager, _accountManager, @@ -780,12 +783,12 @@ namespace Ryujinx.Ava private static IHardwareDeviceDriver InitializeAudio() { - var availableBackends = new List<AudioBackend>() + var availableBackends = new List<AudioBackend> { AudioBackend.SDL2, AudioBackend.SoundIo, AudioBackend.OpenAl, - AudioBackend.Dummy + AudioBackend.Dummy, }; AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value; @@ -806,12 +809,10 @@ namespace Ryujinx.Ava { return new T(); } - else - { - Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); - return null; - } + Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); + + return null; } IHardwareDeviceDriver deviceDriver = null; @@ -819,14 +820,14 @@ namespace Ryujinx.Ava for (int i = 0; i < availableBackends.Count; i++) { AudioBackend currentBackend = availableBackends[i]; - AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy; + AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy; deviceDriver = currentBackend switch { - AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend), + AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend), AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend), - AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend), - _ => new DummyHardwareDeviceDriver() + AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend), + _ => new DummyHardwareDeviceDriver(), }; if (deviceDriver != null) @@ -843,7 +844,7 @@ namespace Ryujinx.Ava private void Window_SizeChanged(object sender, Size e) { - Width = (int)e.Width; + Width = (int)e.Width; Height = (int)e.Height; SetRendererWindowSize(e); @@ -879,7 +880,7 @@ namespace Ryujinx.Ava _renderer.ScreenCaptured += Renderer_ScreenCaptured; - (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); + (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); Device.Gpu.Renderer.Initialize(_glLogLevel); @@ -887,8 +888,8 @@ namespace Ryujinx.Ava _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - Width = (int)_rendererHost.Bounds.Width; - Height = (int)_rendererHost.Bounds.Height; + Width = (int)RendererHost.Bounds.Width; + Height = (int)RendererHost.Bounds.Height; _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling)); @@ -923,7 +924,7 @@ namespace Ryujinx.Ava _viewModel.SwitchToRenderer(false); } - Device.PresentFrame(() => (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); + Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); } if (_ticks >= _ticksPerFrame) @@ -941,7 +942,7 @@ namespace Ryujinx.Ava _gpuDoneEvent.Set(); }); - (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); + (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); } public void UpdateStatus() @@ -1175,4 +1176,4 @@ namespace Ryujinx.Ava return state; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Common/ApplicationHelper.cs b/src/Ryujinx.Ava/Common/ApplicationHelper.cs index 882c06949..b8f2366cd 100644 --- a/src/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/src/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -18,7 +18,6 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common.Helper; @@ -27,6 +26,7 @@ using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; +using ApplicationId = LibHac.Ncm.ApplicationId; using Path = System.IO.Path; namespace Ryujinx.Ava.Common @@ -57,7 +57,7 @@ namespace Ryujinx.Ava.Common Logger.Info?.Print(LogClass.Application, $"Creating save directory for Title: {titleName} [{titleId:x16}]"); - if (Utilities.IsZeros(controlHolder.ByteSpan)) + if (controlHolder.ByteSpan.IsZeros()) { // If the current application doesn't have a loaded control property, create a dummy one // and set the savedata sizes so a user savedata will be created. @@ -72,7 +72,7 @@ namespace Ryujinx.Ava.Common Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); - result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); + result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new ApplicationId(titleId), in control, in user); if (result.IsFailure()) { Dispatcher.UIThread.InvokeAsync(async () => @@ -147,11 +147,11 @@ namespace Ryujinx.Ava.Common { OpenFolderDialog folderDialog = new() { - Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] + Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle], }; - string destination = await folderDialog.ShowAsync(_owner); - var cancellationToken = new CancellationTokenSource(); + string destination = await folderDialog.ShowAsync(_owner); + var cancellationToken = new CancellationTokenSource(); UpdateWaitWindow waitingDialog = new( LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle], @@ -166,7 +166,7 @@ namespace Ryujinx.Ava.Common using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); - Nca mainNca = null; + Nca mainNca = null; Nca patchNca = null; string extension = Path.GetExtension(titleFilePath).ToLower(); @@ -293,10 +293,11 @@ namespace Ryujinx.Ava.Common await ContentDialogHelper.CreateErrorDialog(ex.Message); }); } - }); - - extractorThread.Name = "GUI.NcaSectionExtractorThread"; - extractorThread.IsBackground = true; + }) + { + Name = "GUI.NcaSectionExtractorThread", + IsBackground = true, + }; extractorThread.Start(); } } @@ -413,4 +414,4 @@ namespace Ryujinx.Ava.Common return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Common/ApplicationSort.cs b/src/Ryujinx.Ava/Common/ApplicationSort.cs index 6ff06a1e2..4b80e3d29 100644 --- a/src/Ryujinx.Ava/Common/ApplicationSort.cs +++ b/src/Ryujinx.Ava/Common/ApplicationSort.cs @@ -10,6 +10,6 @@ namespace Ryujinx.Ava.Common FileType, FileSize, Path, - Favorite + Favorite, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs b/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs index e85bdf341..1d7f0b9ce 100644 --- a/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs +++ b/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs @@ -11,6 +11,6 @@ ResScaleUp, ResScaleDown, VolumeUp, - VolumeDown + VolumeDown, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Common/Locale/LocaleExtension.cs b/src/Ryujinx.Ava/Common/Locale/LocaleExtension.cs index b82c405de..cd1c1510d 100644 --- a/src/Ryujinx.Ava/Common/Locale/LocaleExtension.cs +++ b/src/Ryujinx.Ava/Common/Locale/LocaleExtension.cs @@ -20,11 +20,11 @@ namespace Ryujinx.Ava.Common.Locale ReflectionBindingExtension binding = new($"[{keyToUse}]") { - Mode = BindingMode.OneWay, - Source = LocaleManager.Instance + Mode = BindingMode.OneWay, + Source = LocaleManager.Instance, }; return binding.ProvideValue(serviceProvider); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/src/Ryujinx.Ava/Common/Locale/LocaleManager.cs index 464ab780d..583619fe5 100644 --- a/src/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/src/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -13,17 +13,17 @@ namespace Ryujinx.Ava.Common.Locale { private const string DefaultLanguageCode = "en_US"; - private Dictionary<LocaleKeys, string> _localeStrings; - private Dictionary<LocaleKeys, string> _localeDefaultStrings; + private readonly Dictionary<LocaleKeys, string> _localeStrings; + private Dictionary<LocaleKeys, string> _localeDefaultStrings; private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues; - public static LocaleManager Instance { get; } = new LocaleManager(); + public static LocaleManager Instance { get; } = new(); public LocaleManager() { - _localeStrings = new Dictionary<LocaleKeys, string>(); + _localeStrings = new Dictionary<LocaleKeys, string>(); _localeDefaultStrings = new Dictionary<LocaleKeys, string>(); - _dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>(); + _dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>(); Load(); } @@ -126,11 +126,11 @@ namespace Ryujinx.Ava.Common.Locale } } - private Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode) + private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode) { - var localeStrings = new Dictionary<LocaleKeys, string>(); - string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); - var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); + var localeStrings = new Dictionary<LocaleKeys, string>(); + string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); + var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); foreach (var item in strings) { @@ -143,4 +143,4 @@ namespace Ryujinx.Ava.Common.Locale return localeStrings; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Input/AvaloniaKeyboard.cs b/src/Ryujinx.Ava/Input/AvaloniaKeyboard.cs index d40ebbd2b..d07c81340 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaKeyboard.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaKeyboard.cs @@ -12,25 +12,25 @@ namespace Ryujinx.Ava.Input internal class AvaloniaKeyboard : IKeyboard { private readonly List<ButtonMappingEntry> _buttonsUserMapping; - private readonly AvaloniaKeyboardDriver _driver; - private StandardKeyboardInputConfig _configuration; + private readonly AvaloniaKeyboardDriver _driver; + private StandardKeyboardInputConfig _configuration; private readonly object _userMappingLock = new(); - public string Id { get; } + public string Id { get; } public string Name { get; } - public bool IsConnected => true; - public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; + public bool IsConnected => true; + public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; private class ButtonMappingEntry { - public readonly Key From; + public readonly Key From; public readonly GamepadButtonInputId To; public ButtonMappingEntry(GamepadButtonInputId to, Key from) { - To = to; + To = to; From = from; } } @@ -40,8 +40,8 @@ namespace Ryujinx.Ava.Input _buttonsUserMapping = new List<ButtonMappingEntry>(); _driver = driver; - Id = id; - Name = name; + Id = id; + Name = name; } public KeyboardStateSnapshot GetKeyboardStateSnapshot() @@ -52,7 +52,7 @@ namespace Ryujinx.Ava.Input public GamepadStateSnapshot GetMappedStateSnapshot() { KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot(); - GamepadStateSnapshot result = default; + GamepadStateSnapshot result = default; lock (_userMappingLock) { @@ -75,10 +75,10 @@ namespace Ryujinx.Ava.Input } } - (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick); + (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick); (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick); - result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY)); + result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY)); result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY)); } @@ -120,6 +120,7 @@ namespace Ryujinx.Ava.Input _buttonsUserMapping.Clear(); +#pragma warning disable IDE0055 // Disable formatting // Left JoyCon _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp)); @@ -143,6 +144,7 @@ namespace Ryujinx.Ava.Input _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); +#pragma warning restore IDE0055 } } @@ -198,4 +200,4 @@ namespace Ryujinx.Ava.Input public void Dispose() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs index edcdb52fd..f12d0214b 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs @@ -13,23 +13,23 @@ namespace Ryujinx.Ava.Input internal class AvaloniaKeyboardDriver : IGamepadDriver { private static readonly string[] _keyboardIdentifers = new string[1] { "0" }; - private readonly Control _control; + private readonly Control _control; private readonly HashSet<AvaKey> _pressedKeys; public event EventHandler<KeyEventArgs> KeyPressed; public event EventHandler<KeyEventArgs> KeyRelease; - public event EventHandler<string> TextInput; + public event EventHandler<string> TextInput; - public string DriverName => "AvaloniaKeyboardDriver"; + public string DriverName => "AvaloniaKeyboardDriver"; public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers; public AvaloniaKeyboardDriver(Control control) { - _control = control; + _control = control; _pressedKeys = new HashSet<AvaKey>(); - _control.KeyDown += OnKeyPress; - _control.KeyUp += OnKeyRelease; + _control.KeyDown += OnKeyPress; + _control.KeyUp += OnKeyRelease; _control.TextInput += Control_TextInput; _control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble); } @@ -47,13 +47,13 @@ namespace Ryujinx.Ava.Input public event Action<string> OnGamepadConnected { - add { } + add { } remove { } } public event Action<string> OnGamepadDisconnected { - add { } + add { } remove { } } @@ -71,7 +71,7 @@ namespace Ryujinx.Ava.Input { if (disposing) { - _control.KeyUp -= OnKeyPress; + _control.KeyUp -= OnKeyPress; _control.KeyDown -= OnKeyRelease; } } @@ -112,4 +112,4 @@ namespace Ryujinx.Ava.Input Dispose(true); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs b/src/Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs index 0cc04d0a9..d30a7566a 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaKeyboardMappingHelper.cs @@ -143,7 +143,7 @@ namespace Ryujinx.Ava.Input AvaKey.OemBackslash, // NOTE: invalid - AvaKey.None + AvaKey.None, }; private static readonly Dictionary<AvaKey, Key> _avaKeyMapping; @@ -182,4 +182,4 @@ namespace Ryujinx.Ava.Input return _avaKeyMapping.GetValueOrDefault(key, Key.Unknown); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Input/AvaloniaMouse.cs b/src/Ryujinx.Ava/Input/AvaloniaMouse.cs index 3a9c91c0d..1aa2d586a 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaMouse.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaMouse.cs @@ -10,12 +10,12 @@ namespace Ryujinx.Ava.Input { private AvaloniaMouseDriver _driver; - public string Id => "0"; + public string Id => "0"; public string Name => "AvaloniaMouse"; - public bool IsConnected => true; - public GamepadFeaturesFlag Features => throw new NotImplementedException(); - public bool[] Buttons => _driver.PressedButtons; + public bool IsConnected => true; + public GamepadFeaturesFlag Features => throw new NotImplementedException(); + public bool[] Buttons => _driver.PressedButtons; public AvaloniaMouse(AvaloniaMouseDriver driver) { @@ -84,4 +84,4 @@ namespace Ryujinx.Ava.Input _driver = null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs index b7e5a4d95..e71bbf64a 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs @@ -11,16 +11,16 @@ namespace Ryujinx.Ava.Input { internal class AvaloniaMouseDriver : IGamepadDriver { - private Control _widget; - private bool _isDisposed; - private Size _size; + private Control _widget; + private bool _isDisposed; + private Size _size; private readonly TopLevel _window; - public bool[] PressedButtons { get; } + public bool[] PressedButtons { get; } public Vector2 CurrentPosition { get; private set; } - public Vector2 Scroll { get; private set; } + public Vector2 Scroll { get; private set; } - public string DriverName => "AvaloniaMouseDriver"; + public string DriverName => "AvaloniaMouseDriver"; public ReadOnlySpan<string> GamepadsIds => new[] { "0" }; public AvaloniaMouseDriver(TopLevel window, Control parent) @@ -28,14 +28,14 @@ namespace Ryujinx.Ava.Input _widget = parent; _window = window; - _widget.PointerMoved += Parent_PointerMovedEvent; - _widget.PointerPressed += Parent_PointerPressedEvent; - _widget.PointerReleased += Parent_PointerReleasedEvent; + _widget.PointerMoved += Parent_PointerMovedEvent; + _widget.PointerPressed += Parent_PointerPressedEvent; + _widget.PointerReleased += Parent_PointerReleasedEvent; _widget.PointerWheelChanged += Parent_PointerWheelChanged; - - _window.PointerMoved += Parent_PointerMovedEvent; - _window.PointerPressed += Parent_PointerPressedEvent; - _window.PointerReleased += Parent_PointerReleasedEvent; + + _window.PointerMoved += Parent_PointerMovedEvent; + _window.PointerPressed += Parent_PointerPressedEvent; + _window.PointerReleased += Parent_PointerReleasedEvent; _window.PointerWheelChanged += Parent_PointerWheelChanged; PressedButtons = new bool[(int)MouseButton.Count]; @@ -47,13 +47,13 @@ namespace Ryujinx.Ava.Input public event Action<string> OnGamepadConnected { - add { } + add { } remove { } } public event Action<string> OnGamepadDisconnected { - add { } + add { } remove { } } @@ -143,17 +143,17 @@ namespace Ryujinx.Ava.Input _isDisposed = true; - _widget.PointerMoved -= Parent_PointerMovedEvent; - _widget.PointerPressed -= Parent_PointerPressedEvent; - _widget.PointerReleased -= Parent_PointerReleasedEvent; + _widget.PointerMoved -= Parent_PointerMovedEvent; + _widget.PointerPressed -= Parent_PointerPressedEvent; + _widget.PointerReleased -= Parent_PointerReleasedEvent; _widget.PointerWheelChanged -= Parent_PointerWheelChanged; - _window.PointerMoved -= Parent_PointerMovedEvent; - _window.PointerPressed -= Parent_PointerPressedEvent; - _window.PointerReleased -= Parent_PointerReleasedEvent; + _window.PointerMoved -= Parent_PointerMovedEvent; + _window.PointerPressed -= Parent_PointerPressedEvent; + _window.PointerReleased -= Parent_PointerReleasedEvent; _window.PointerWheelChanged -= Parent_PointerWheelChanged; _widget = null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Modules/Updater/Updater.cs b/src/Ryujinx.Ava/Modules/Updater/Updater.cs index 71d978c67..8216333a7 100644 --- a/src/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/src/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -31,22 +31,22 @@ namespace Ryujinx.Modules { internal static class Updater { - private const string GitHubApiURL = "https://api.github.com"; - private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private const string GitHubApiUrl = "https://api.github.com"; + private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; - private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); - private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); - private static readonly int ConnectionCount = 4; + private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; + private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); + private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish"); + private static readonly int _connectionCount = 4; private static string _buildVer; private static string _platformExt; private static string _buildUrl; - private static long _buildSize; - private static bool _updateSuccessful; - private static bool _running; + private static long _buildSize; + private static bool _updateSuccessful; + private static bool _running; - private static readonly string[] WindowsDependencyDirs = Array.Empty<string>(); + private static readonly string[] _windowsDependencyDirs = Array.Empty<string>(); public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate) { @@ -99,9 +99,9 @@ namespace Ryujinx.Modules { using HttpClient jsonClient = ConstructHttpClient(); - string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; - string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); + string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl); + var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse); _buildVer = fetched.Name; foreach (var asset in fetched.Assets) @@ -195,23 +195,21 @@ namespace Ryujinx.Modules } // Fetch build size information to learn chunk sizes. - using (HttpClient buildSizeClient = ConstructHttpClient()) + using HttpClient buildSizeClient = ConstructHttpClient(); + try { - try - { - buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); + buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); - HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); + HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); - _buildSize = message.Content.Headers.ContentRange.Length.Value; - } - catch (Exception ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); + _buildSize = message.Content.Headers.ContentRange.Length.Value; + } + catch (Exception ex) + { + Logger.Warning?.Print(LogClass.Application, ex.Message); + Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); - _buildSize = -1; - } + _buildSize = -1; } Dispatcher.UIThread.Post(async () => @@ -248,23 +246,22 @@ namespace Ryujinx.Modules _updateSuccessful = false; // Empty update dir, although it shouldn't ever have anything inside it - if (Directory.Exists(UpdateDir)) + if (Directory.Exists(_updateDir)) { - Directory.Delete(UpdateDir, true); + Directory.Delete(_updateDir, true); } - Directory.CreateDirectory(UpdateDir); + Directory.CreateDirectory(_updateDir); - string updateFile = Path.Combine(UpdateDir, "update.bin"); + string updateFile = Path.Combine(_updateDir, "update.bin"); TaskDialog taskDialog = new() { - Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater], - SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], - IconSource = new SymbolIconSource { Symbol = Symbol.Download }, - Buttons = { }, + Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], + IconSource = new SymbolIconSource { Symbol = Symbol.Download }, ShowProgressBar = true, - XamlRoot = parent + XamlRoot = parent, }; taskDialog.Opened += (s, e) => @@ -301,7 +298,7 @@ namespace Ryujinx.Modules if (OperatingSystem.IsMacOS()) { string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); - string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app"); + string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app"); string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); string currentPid = Environment.ProcessId.ToString(); @@ -328,7 +325,7 @@ namespace Ryujinx.Modules ProcessStartInfo processStart = new(ryuName) { UseShellExecute = true, - WorkingDirectory = executableDirectory + WorkingDirectory = executableDirectory, }; foreach (string argument in CommandLineState.Arguments) @@ -347,22 +344,22 @@ namespace Ryujinx.Modules private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile) { // Multi-Threaded Updater - long chunkSize = _buildSize / ConnectionCount; - long remainderChunk = _buildSize % ConnectionCount; + long chunkSize = _buildSize / _connectionCount; + long remainderChunk = _buildSize % _connectionCount; - int completedRequests = 0; - int totalProgressPercentage = 0; - int[] progressPercentage = new int[ConnectionCount]; + int completedRequests = 0; + int totalProgressPercentage = 0; + int[] progressPercentage = new int[_connectionCount]; - List<byte[]> list = new(ConnectionCount); - List<WebClient> webClients = new(ConnectionCount); + List<byte[]> list = new(_connectionCount); + List<WebClient> webClients = new(_connectionCount); - for (int i = 0; i < ConnectionCount; i++) + for (int i = 0; i < _connectionCount; i++) { list.Add(Array.Empty<byte>()); } - for (int i = 0; i < ConnectionCount; i++) + for (int i = 0; i < _connectionCount; i++) { #pragma warning disable SYSLIB0014 // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. @@ -371,7 +368,7 @@ namespace Ryujinx.Modules webClients.Add(client); - if (i == ConnectionCount - 1) + if (i == _connectionCount - 1) { client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); } @@ -388,7 +385,7 @@ namespace Ryujinx.Modules Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal); + taskDialog.SetProgressBarState(totalProgressPercentage / _connectionCount, TaskDialogProgressState.Normal); }; client.DownloadDataCompleted += (_, args) => @@ -407,10 +404,10 @@ namespace Ryujinx.Modules list[index] = args.Result; Interlocked.Increment(ref completedRequests); - if (Equals(completedRequests, ConnectionCount)) + if (Equals(completedRequests, _connectionCount)) { byte[] mergedFileBytes = new byte[_buildSize]; - for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) + for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < _connectionCount; connectionIndex++) { Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); destinationOffset += list[connectionIndex].Length; @@ -421,10 +418,9 @@ namespace Ryujinx.Modules // On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution. if (OperatingSystem.IsMacOS()) { - using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile })) - { - xattrProcess.WaitForExit(); - } + using Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile }); + + xattrProcess.WaitForExit(); } try @@ -437,8 +433,6 @@ namespace Ryujinx.Modules Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); - - return; } } }; @@ -470,31 +464,29 @@ namespace Ryujinx.Modules // We do not want to timeout while downloading client.Timeout = TimeSpan.FromDays(1); - using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) - using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) + using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result; + using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result; + using Stream updateFileStream = File.Open(updateFile, FileMode.Create); + + long totalBytes = response.Content.Headers.ContentLength.Value; + long byteWritten = 0; + + byte[] buffer = new byte[32 * 1024]; + + while (true) { - using Stream updateFileStream = File.Open(updateFile, FileMode.Create); + int readSize = remoteFileStream.Read(buffer); - long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; - - byte[] buffer = new byte[32 * 1024]; - - while (true) + if (readSize == 0) { - int readSize = remoteFileStream.Read(buffer); - - if (readSize == 0) - { - break; - } - - byteWritten += readSize; - - taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal); - - updateFileStream.Write(buffer, 0, readSize); + break; } + + byteWritten += readSize; + + taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal); + + updateFileStream.Write(buffer, 0, readSize); } InstallUpdate(taskDialog, updateFile); @@ -510,7 +502,7 @@ namespace Ryujinx.Modules { Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile)) { - Name = "Updater.SingleThreadWorker" + Name = "Updater.SingleThreadWorker", }; worker.Start(); @@ -520,9 +512,9 @@ namespace Ryujinx.Modules [SupportedOSPlatform("macos")] private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath) { - using Stream inStream = File.OpenRead(archivePath); + using Stream inStream = File.OpenRead(archivePath); using GZipInputStream gzipStream = new(inStream); - using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); + using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); TarEntry tarEntry; @@ -537,10 +529,8 @@ namespace Ryujinx.Modules Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - using (FileStream outStream = File.OpenWrite(outPath)) - { - tarStream.CopyEntryContents(outStream); - } + using FileStream outStream = File.OpenWrite(outPath); + tarStream.CopyEntryContents(outStream); File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); @@ -559,24 +549,26 @@ namespace Ryujinx.Modules private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath) { - using Stream inStream = File.OpenRead(archivePath); - using ZipFile zipFile = new(inStream); + using Stream inStream = File.OpenRead(archivePath); + using ZipFile zipFile = new(inStream); double count = 0; foreach (ZipEntry zipEntry in zipFile) { count++; - if (zipEntry.IsDirectory) continue; + if (zipEntry.IsDirectory) + { + continue; + } string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name); Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - using (Stream zipStream = zipFile.GetInputStream(zipEntry)) - using (FileStream outStream = File.OpenWrite(outPath)) - { - zipStream.CopyTo(outStream); - } + using Stream zipStream = zipFile.GetInputStream(zipEntry); + using FileStream outStream = File.OpenWrite(outPath); + + zipStream.CopyTo(outStream); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); @@ -597,11 +589,11 @@ namespace Ryujinx.Modules { if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { - ExtractTarGzipFile(taskDialog, updateFile, UpdateDir); + ExtractTarGzipFile(taskDialog, updateFile, _updateDir); } else if (OperatingSystem.IsWindows()) { - ExtractZipFile(taskDialog, updateFile, UpdateDir); + ExtractZipFile(taskDialog, updateFile, _updateDir); } else { @@ -648,10 +640,10 @@ namespace Ryujinx.Modules taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); }); - MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog); + MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog); }); - Directory.Delete(UpdateDir, true); + Directory.Delete(_updateDir, true); } _updateSuccessful = true; @@ -738,15 +730,15 @@ namespace Ryujinx.Modules // NOTE: This method should always reflect the latest build layout. private static IEnumerable<string> EnumerateFilesToDelete() { - var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. + var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir. // Determine and exclude user files only when the updater is running, not when cleaning old files if (_running && !OperatingSystem.IsMacOS()) { // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. - var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename)); + var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); + var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename)); // Remove user files from the paths in files. files = files.Except(userFiles); @@ -754,9 +746,9 @@ namespace Ryujinx.Modules if (OperatingSystem.IsWindows()) { - foreach (string dir in WindowsDependencyDirs) + foreach (string dir in _windowsDependencyDirs) { - string dirPath = Path.Combine(HomeDir, dir); + string dirPath = Path.Combine(_homeDir, dir); if (Directory.Exists(dirPath)) { files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories)); @@ -798,10 +790,10 @@ namespace Ryujinx.Modules public static void CleanupUpdate() { - foreach (string file in Directory.GetFiles(HomeDir, "*.ryuold", SearchOption.AllDirectories)) + foreach (string file in Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories)) { File.Delete(file); } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/Program.cs b/src/Ryujinx.Ava/Program.cs index 0629e6062..5241f91f8 100644 --- a/src/Ryujinx.Ava/Program.cs +++ b/src/Ryujinx.Ava/Program.cs @@ -22,16 +22,16 @@ namespace Ryujinx.Ava { internal partial class Program { - public static double WindowScaleFactor { get; set; } + public static double WindowScaleFactor { get; set; } public static double DesktopScaleFactor { get; set; } = 1.0; - public static string Version { get; private set; } - public static string ConfigurationPath { get; private set; } - public static bool PreviewerDetached { get; private set; } + public static string Version { get; private set; } + public static string ConfigurationPath { get; private set; } + public static bool PreviewerDetached { get; private set; } [LibraryImport("user32.dll", SetLastError = true)] public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); - private const uint MB_ICONWARNING = 0x30; + private const uint MbIconwarning = 0x30; public static void Main(string[] args) { @@ -39,7 +39,7 @@ namespace Ryujinx.Ava if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) { - _ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING); + _ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning); } PreviewerDetached = true; @@ -58,15 +58,15 @@ namespace Ryujinx.Ava .With(new X11PlatformOptions { EnableMultiTouch = true, - EnableIme = true, - UseEGL = false, - UseGpu = true + EnableIme = true, + UseEGL = false, + UseGpu = true, }) .With(new Win32PlatformOptions { - EnableMultitouch = true, - UseWgl = false, - AllowEglInitialization = false, + EnableMultitouch = true, + UseWgl = false, + AllowEglInitialization = false, CompositionBackdropCornerRadius = 8.0f, }) .UseSkia(); @@ -84,7 +84,7 @@ namespace Ryujinx.Ava // Hook unhandled exception and process exit events. AppDomain.CurrentDomain.UnhandledException += (sender, e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit(); + AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit(); // Setup base data directory. AppDataManager.Initialize(CommandLineState.BaseDirPathArg); @@ -130,7 +130,7 @@ namespace Ryujinx.Ava public static void ReloadConfig() { - string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); + string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json"); // Now load the configuration as the other subsystems are now registered @@ -192,7 +192,7 @@ namespace Ryujinx.Ava "never" => HideCursorMode.Never, "onidle" => HideCursorMode.OnIdle, "always" => HideCursorMode.Always, - _ => ConfigurationState.Instance.HideCursor.Value + _ => ConfigurationState.Instance.HideCursor.Value, }; } } @@ -238,4 +238,4 @@ namespace Ryujinx.Ava Logger.Shutdown(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs index c87308cfb..d35f44bf5 100644 --- a/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs +++ b/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs @@ -64,7 +64,7 @@ namespace Ryujinx.Ava.UI.Applet LocaleManager.Instance[LocaleKeys.SettingsButtonClose], (int)Symbol.Important, deferEvent, - async (window) => + async window => { if (opened) { @@ -112,7 +112,7 @@ namespace Ryujinx.Ava.UI.Applet { try { - var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args); + var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args); if (response.Result == UserResult.Ok) { @@ -142,10 +142,7 @@ namespace Ryujinx.Ava.UI.Applet public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value) { device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); - if (_parent.ViewModel.AppHost != null) - { - _parent.ViewModel.AppHost.Stop(); - } + _parent.ViewModel.AppHost?.Stop(); } public bool DisplayErrorAppletDialog(string title, string message, string[] buttons) @@ -162,7 +159,7 @@ namespace Ryujinx.Ava.UI.Applet { Title = title, WindowStartupLocation = WindowStartupLocation.CenterScreen, - Width = 400 + Width = 400, }; object response = await msgDialog.Run(); @@ -194,4 +191,4 @@ namespace Ryujinx.Ava.UI.Applet return new AvaloniaDynamicTextInputHandler(_parent); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/src/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs index 2dd65e362..2fa4d54f8 100644 --- a/src/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs +++ b/src/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs @@ -3,13 +3,11 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Threading; using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.HLE.Ui; using System; using System.Threading; - using HidKey = Ryujinx.Common.Configuration.Hid.Key; namespace Ryujinx.Ava.UI.Applet @@ -17,7 +15,7 @@ namespace Ryujinx.Ava.UI.Applet class AvaloniaDynamicTextInputHandler : IDynamicTextInputHandler { private MainWindow _parent; - private OffscreenTextBox _hiddenTextBox; + private readonly OffscreenTextBox _hiddenTextBox; private bool _canProcessInput; private IDisposable _textChangedSubscription; private IDisposable _selectionStartChangedSubscription; @@ -76,7 +74,7 @@ namespace Ryujinx.Ava.UI.Applet return; } - e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent(); + e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent(); Dispatcher.UIThread.InvokeAsync(() => { @@ -96,7 +94,7 @@ namespace Ryujinx.Ava.UI.Applet return; } - e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent(); + e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent(); Dispatcher.UIThread.InvokeAsync(() => { diff --git a/src/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs b/src/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs index 77c7a2d2c..3cf273347 100644 --- a/src/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs +++ b/src/Ryujinx.Ava/UI/Applet/AvaloniaHostUiTheme.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Ava.UI.Applet { public AvaloniaHostUiTheme(MainWindow parent) { - FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000, 0) ? "Segoe UI Variable" : parent.FontFamily.Name; + FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name; DefaultBackgroundColor = BrushToThemeColor(parent.Background); DefaultForegroundColor = BrushToThemeColor(parent.Foreground); DefaultBorderColor = BrushToThemeColor(parent.BorderBrush); @@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Applet public ThemeColor SelectionBackgroundColor { get; } public ThemeColor SelectionForegroundColor { get; } - private ThemeColor BrushToThemeColor(IBrush brush) + private static ThemeColor BrushToThemeColor(IBrush brush) { if (brush is SolidColorBrush solidColor) { @@ -34,10 +34,8 @@ namespace Ryujinx.Ava.UI.Applet (float)solidColor.Color.G / 255, (float)solidColor.Color.B / 255); } - else - { - return new ThemeColor(); - } + + return new ThemeColor(); } } } diff --git a/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs index 4134797ba..b77cc4020 100644 --- a/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs @@ -1,10 +1,12 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Windows; using System.Threading.Tasks; +#if DEBUG +using Avalonia; +#endif namespace Ryujinx.Ava.UI.Applet { @@ -77,4 +79,4 @@ namespace Ryujinx.Ava.UI.Applet return _buttonResponse; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index 81258b448..210fc9a2b 100644 --- a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -1,13 +1,10 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; -using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Windows; using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using System; @@ -22,7 +19,7 @@ namespace Ryujinx.Ava.UI.Controls private Predicate<string> _checkInput = _ => true; private int _inputMax; private int _inputMin; - private string _placeholder; + private readonly string _placeholder; private ContentDialog _host; @@ -57,13 +54,13 @@ namespace Ryujinx.Ava.UI.Controls public string MainText { get; set; } = ""; public string SecondaryText { get; set; } = ""; - public static async Task<(UserResult Result, string Input)> ShowInputDialog(StyleableWindow window, string title, SoftwareKeyboardUiArgs args) + public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUiArgs args) { - ContentDialog contentDialog = new ContentDialog(); + ContentDialog contentDialog = new(); UserResult result = UserResult.Cancel; - SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText); + SwkbdAppletDialog content = new(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText); string input = string.Empty; @@ -78,15 +75,16 @@ namespace Ryujinx.Ava.UI.Controls contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel]; contentDialog.Content = content; - TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) => + void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs) { if (eventArgs.Result == ContentDialogResult.Primary) { result = UserResult.Ok; input = content.Input.Text; } - }; - contentDialog.Closed += handler; + } + + contentDialog.Closed += Handler; await ContentDialogHelper.ShowAsync(contentDialog); @@ -182,4 +180,4 @@ namespace Ryujinx.Ava.UI.Controls } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index b01c7c5e3..77b4f5207 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -18,7 +18,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using Path = System.IO.Path; -using UserId = LibHac.Fs.UserId; namespace Ryujinx.Ava.UI.Controls { @@ -53,7 +52,7 @@ namespace Ryujinx.Ava.UI.Controls public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args) { - if ((sender as MenuItem)?.DataContext is MainWindowViewModel viewModel) + if (sender is MenuItem { DataContext: MainWindowViewModel viewModel }) { OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low)); } @@ -334,4 +333,4 @@ namespace Ryujinx.Ava.UI.Controls } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs index c30c75b36..efdc9ab02 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Ava.UI.Controls public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened { - add { AddHandler(ApplicationOpenedEvent, value); } + add { AddHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); } } diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs index 1a07c4251..1646fc1b1 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Ava.UI.Controls public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened { - add { AddHandler(ApplicationOpenedEvent, value); } + add { AddHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); } } diff --git a/src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs b/src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs index 1b857fae4..a32c052bf 100644 --- a/src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs @@ -2,7 +2,6 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Styling; using Avalonia.Threading; -using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using LibHac; using LibHac.Common; @@ -10,7 +9,6 @@ using LibHac.Fs; using LibHac.Fs.Shim; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Views.User; using Ryujinx.HLE.FileSystem; @@ -19,6 +17,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.Controls @@ -56,7 +55,7 @@ namespace Ryujinx.Ava.UI.Controls InitializeComponent(); } - public void GoBack(object parameter = null) + public void GoBack() { if (ContentFrame.BackStack.Count > 0) { @@ -75,14 +74,14 @@ namespace Ryujinx.Ava.UI.Controls VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient) { var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient); - ContentDialog contentDialog = new ContentDialog + ContentDialog contentDialog = new() { Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle], PrimaryButtonText = "", SecondaryButtonText = "", CloseButtonText = "", Content = content, - Padding = new Thickness(0) + Padding = new Thickness(0), }; contentDialog.Closed += (sender, args) => @@ -125,7 +124,7 @@ namespace Ryujinx.Ava.UI.Controls Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10]; - HashSet<HLE.HOS.Services.Account.Acc.UserId> lostAccounts = new(); + HashSet<UserId> lostAccounts = new(); while (true) { @@ -139,15 +138,15 @@ namespace Ryujinx.Ava.UI.Controls for (int i = 0; i < readCount; i++) { var save = saveDataInfo[i]; - var id = new HLE.HOS.Services.Account.Acc.UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High); - if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault( x=> x.UserId == id) == null) + var id = new UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High); + if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault(x => x.UserId == id) == null) { lostAccounts.Add(id); } } } - foreach(var account in lostAccounts) + foreach (var account in lostAccounts) { ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this)); } @@ -166,7 +165,7 @@ namespace Ryujinx.Ava.UI.Controls if (profile == null) { - async void Action() + static async void Action() { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]); } @@ -215,4 +214,4 @@ namespace Ryujinx.Ava.UI.Controls Navigate(typeof(UserSaveManagerView), (this, AccountManager, HorizonClient, VirtualFileSystem)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs b/src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs index 80a437e33..7ad1ee332 100644 --- a/src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/UpdateWaitWindow.axaml.cs @@ -28,4 +28,4 @@ namespace Ryujinx.Ava.UI.Controls InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs b/src/Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs index ebf5c16ec..cd63a99b0 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ApplicationOpenedEventArgs.cs @@ -13,4 +13,4 @@ namespace Ryujinx.Ava.UI.Helpers RoutedEvent = routedEvent; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs index 133f8dbc9..42bd8d5a8 100644 --- a/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/BitmapArrayValueConverter.cs @@ -33,4 +33,4 @@ namespace Ryujinx.Ava.UI.Helpers throw new NotSupportedException(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs b/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs index 6730b5711..7e8ba7342 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers IsAssigned = isAssigned; } } - + public ToggleButton ToggledButton { get; set; } private bool _isWaitingForInput; diff --git a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index 045d508c6..7dab57b88 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.Threading; using FluentAvalonia.Core; @@ -32,18 +33,17 @@ namespace Ryujinx.Ava.UI.Helpers ContentDialog contentDialog = new() { - Title = title, - PrimaryButtonText = primaryButton, + Title = title, + PrimaryButtonText = primaryButton, SecondaryButtonText = secondaryButton, - CloseButtonText = closeButton, - Content = content + CloseButtonText = closeButton, + Content = content, + PrimaryButtonCommand = MiniCommand.Create(() => + { + result = primaryButtonResult; + }), }; - contentDialog.PrimaryButtonCommand = MiniCommand.Create(() => - { - result = primaryButtonResult; - }); - contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => { result = UserResult.No; @@ -96,7 +96,6 @@ namespace Ryujinx.Ava.UI.Helpers Func<Window, Task> doWhileDeferred = null) { bool startedDeferring = false; - UserResult result = UserResult.None; return await ShowTextDialog( title, @@ -123,8 +122,6 @@ namespace Ryujinx.Ava.UI.Helpers var deferral = args.GetDeferral(); - result = primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok; - sender.PrimaryButtonClick -= DeferClose; _ = Task.Run(() => @@ -150,18 +147,18 @@ namespace Ryujinx.Ava.UI.Helpers { Grid content = new() { - RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() }, - ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() }, + RowDefinitions = new RowDefinitions { new(), new() }, + ColumnDefinitions = new ColumnDefinitions { new(GridLength.Auto), new() }, - MinHeight = 80 + MinHeight = 80, }; SymbolIcon icon = new() { - Symbol = (Symbol)symbol, - Margin = new Thickness(10), - FontSize = 40, - VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center + Symbol = (Symbol)symbol, + Margin = new Thickness(10), + FontSize = 40, + VerticalAlignment = VerticalAlignment.Center, }; Grid.SetColumn(icon, 0); @@ -170,18 +167,18 @@ namespace Ryujinx.Ava.UI.Helpers TextBlock primaryLabel = new() { - Text = primaryText, - Margin = new Thickness(5), + Text = primaryText, + Margin = new Thickness(5), TextWrapping = TextWrapping.Wrap, - MaxWidth = 450 + MaxWidth = 450, }; TextBlock secondaryLabel = new() { - Text = secondaryText, - Margin = new Thickness(5), + Text = secondaryText, + Margin = new Thickness(5), TextWrapping = TextWrapping.Wrap, - MaxWidth = 450 + MaxWidth = 450, }; Grid.SetColumn(primaryLabel, 1); @@ -318,14 +315,14 @@ namespace Ryujinx.Ava.UI.Helpers Window parent = GetMainWindow(); - if (parent != null && parent.IsActive && (parent as MainWindow).ViewModel.IsGameRunning) + if (parent is { IsActive: true } and MainWindow window && window.ViewModel.IsGameRunning) { contentDialogOverlayWindow = new() { - Height = parent.Bounds.Height, - Width = parent.Bounds.Width, - Position = parent.PointToScreen(new Point()), - ShowInTaskbar = false + Height = parent.Bounds.Height, + Width = parent.Bounds.Width, + Position = parent.PointToScreen(new Point()), + ShowInTaskbar = false, }; parent.PositionChanged += OverlayOnPositionChanged; @@ -389,7 +386,7 @@ namespace Ryujinx.Ava.UI.Helpers private static Window GetMainWindow() { - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al) + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al) { foreach (Window item in al.Windows) { @@ -403,4 +400,4 @@ namespace Ryujinx.Ava.UI.Helpers return null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs index 3d6c9c018..4e4b971eb 100644 --- a/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs @@ -1,5 +1,6 @@ using Avalonia.Data; using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml.MarkupExtensions; using FluentAvalonia.UI.Controls; using System; using System.Collections.Generic; @@ -8,13 +9,13 @@ namespace Ryujinx.Ava.UI.Helpers { public class GlyphValueConverter : MarkupExtension { - private string _key; + private readonly string _key; - private static Dictionary<Glyph, string> _glyphs = new Dictionary<Glyph, string> + private static readonly Dictionary<Glyph, string> _glyphs = new() { - { Glyph.List, char.ConvertFromUtf32((int)Symbol.List).ToString() }, - { Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll).ToString() }, - { Glyph.Chip, char.ConvertFromUtf32(59748).ToString() } + { Glyph.List, char.ConvertFromUtf32((int)Symbol.List) }, + { Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll) }, + { Glyph.Chip, char.ConvertFromUtf32(59748) }, }; public GlyphValueConverter(string key) @@ -37,13 +38,13 @@ namespace Ryujinx.Ava.UI.Helpers public override object ProvideValue(IServiceProvider serviceProvider) { - Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension binding = new($"[{_key}]") + ReflectionBindingExtension binding = new($"[{_key}]") { Mode = BindingMode.OneWay, - Source = this + Source = this, }; return binding.ProvideValue(serviceProvider); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs b/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs index f1fad1576..4ae903b49 100644 --- a/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs +++ b/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs @@ -49,4 +49,4 @@ namespace Ryujinx.Ava.UI.Helpers } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs index 8d5c2815a..028ed6bf4 100644 --- a/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs @@ -43,4 +43,4 @@ namespace Ryujinx.Ava.UI.Helpers return key; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs b/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs index 7a29cc198..dc8e3f945 100644 --- a/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs @@ -1,15 +1,17 @@ +using Avalonia.Logging; using Avalonia.Utilities; +using Ryujinx.Common.Logging; using System; using System.Text; namespace Ryujinx.Ava.UI.Helpers { - using AvaLogger = Avalonia.Logging.Logger; - using AvaLogLevel = Avalonia.Logging.LogEventLevel; - using RyuLogClass = Ryujinx.Common.Logging.LogClass; - using RyuLogger = Ryujinx.Common.Logging.Logger; + using AvaLogger = Avalonia.Logging.Logger; + using AvaLogLevel = LogEventLevel; + using RyuLogClass = LogClass; + using RyuLogger = Ryujinx.Common.Logging.Logger; - internal class LoggerAdapter : Avalonia.Logging.ILogSink + internal class LoggerAdapter : ILogSink { public static void Register() { @@ -20,13 +22,13 @@ namespace Ryujinx.Ava.UI.Helpers { return level switch { - AvaLogLevel.Verbose => RyuLogger.Debug, - AvaLogLevel.Debug => RyuLogger.Debug, + AvaLogLevel.Verbose => RyuLogger.Debug, + AvaLogLevel.Debug => RyuLogger.Debug, AvaLogLevel.Information => RyuLogger.Debug, - AvaLogLevel.Warning => RyuLogger.Debug, - AvaLogLevel.Error => RyuLogger.Error, - AvaLogLevel.Fatal => RyuLogger.Error, - _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) + AvaLogLevel.Warning => RyuLogger.Debug, + AvaLogLevel.Error => RyuLogger.Error, + AvaLogLevel.Fatal => RyuLogger.Error, + _ => throw new ArgumentOutOfRangeException(nameof(level), level, null), }; } @@ -45,7 +47,7 @@ namespace Ryujinx.Ava.UI.Helpers GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0 })); } - public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) { GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0, propertyValue1 })); } @@ -112,4 +114,4 @@ namespace Ryujinx.Ava.UI.Helpers return result.ToString(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/MiniCommand.cs b/src/Ryujinx.Ava/UI/Helpers/MiniCommand.cs index 305182c92..864f53b8a 100644 --- a/src/Ryujinx.Ava/UI/Helpers/MiniCommand.cs +++ b/src/Ryujinx.Ava/UI/Helpers/MiniCommand.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Ava.UI.Helpers { private readonly Action<T> _callback; private bool _busy; - private Func<T, Task> _asyncCallback; + private readonly Func<T, Task> _asyncCallback; public MiniCommand(Action<T> callback) { @@ -68,4 +68,4 @@ namespace Ryujinx.Ava.UI.Helpers public abstract void Execute(object parameter); public abstract event EventHandler CanExecuteChanged; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs index f207c5fb0..a11fb5267 100644 --- a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Ava.UI.Helpers { public static class NotificationHelper { - private const int MaxNotifications = 4; + private const int MaxNotifications = 4; private const int NotificationDelayInMs = 5000; private static WindowNotificationManager _notificationManager; - private static readonly BlockingCollection<Notification> _notifications = new(); + private static readonly BlockingCollection<Notification> _notifications = new(); public static void SetNotificationManager(Window host) { @@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Helpers { Position = NotificationPosition.BottomRight, MaxItems = MaxNotifications, - Margin = new Thickness(0, 0, 15, 40) + Margin = new Thickness(0, 0, 15, 40), }; var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>( @@ -67,4 +67,4 @@ namespace Ryujinx.Ava.UI.Helpers Show(LocaleManager.Instance[LocaleKeys.DialogErrorTitle], $"{LocaleManager.Instance[LocaleKeys.DialogErrorMessage]}\n\n{message}", NotificationType.Error); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs b/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs index 1d862de01..e91937612 100644 --- a/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs @@ -35,4 +35,4 @@ namespace Ryujinx.Ava.UI.Helpers return _instance; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs b/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs index 785e785cd..670d3b361 100644 --- a/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs +++ b/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs @@ -6,12 +6,12 @@ namespace Ryujinx.Ava.UI.Helpers { public class OffscreenTextBox : TextBox { - public RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent() + public static RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent() { return KeyDownEvent; } - public RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent() + public static RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent() { return KeyUpEvent; } @@ -28,13 +28,13 @@ namespace Ryujinx.Ava.UI.Helpers public void SendText(string text) { - OnTextInput(new TextInputEventArgs() + OnTextInput(new TextInputEventArgs { Text = text, Device = KeyboardDevice.Instance, Source = this, - RoutedEvent = TextInputEvent + RoutedEvent = TextInputEvent, }); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs b/src/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs index 4ed629ff1..afd2958c6 100644 --- a/src/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs +++ b/src/Ryujinx.Ava/UI/Helpers/UserErrorDialog.cs @@ -1,5 +1,4 @@ using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Windows; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Helper; using System.Threading.Tasks; @@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailed], UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFound], UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknown], - _ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined] + _ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined], }; } @@ -37,7 +36,7 @@ namespace Ryujinx.Ava.UI.Helpers UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailedDescription], UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFoundDescription], UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknownDescription], - _ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription] + _ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription], }; } @@ -48,7 +47,7 @@ namespace Ryujinx.Ava.UI.Helpers UserError.NoKeys or UserError.NoFirmware or UserError.FirmwareParsingFailed => true, - _ => false + _ => false, }; } @@ -63,11 +62,11 @@ namespace Ryujinx.Ava.UI.Helpers { UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys", UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware", - _ => SetupGuideUrl + _ => SetupGuideUrl, }; } - public static async Task ShowUserErrorDialog(UserError error, StyleableWindow owner) + public static async Task ShowUserErrorDialog(UserError error) { string errorCode = GetErrorCode(error); @@ -88,4 +87,4 @@ namespace Ryujinx.Ava.UI.Helpers } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/UserResult.cs b/src/Ryujinx.Ava/UI/Helpers/UserResult.cs index 57802804f..2fcd35ae4 100644 --- a/src/Ryujinx.Ava/UI/Helpers/UserResult.cs +++ b/src/Ryujinx.Ava/UI/Helpers/UserResult.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Ava.UI.Helpers Cancel, None, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs b/src/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs index 03d3a49f3..ca55d0399 100644 --- a/src/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs +++ b/src/Ryujinx.Ava/UI/Helpers/Win32NativeInterop.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -10,46 +11,47 @@ namespace Ryujinx.Ava.UI.Helpers [Flags] public enum ClassStyles : uint { - CS_CLASSDC = 0x40, - CS_OWNDC = 0x20, + CsClassdc = 0x40, + CsOwndc = 0x20, } [Flags] public enum WindowStyles : uint { - WS_CHILD = 0x40000000 + WsChild = 0x40000000, } public enum Cursors : uint { - IDC_ARROW = 32512 + IdcArrow = 32512, } + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] public enum WindowsMessages : uint { - MOUSEMOVE = 0x0200, - LBUTTONDOWN = 0x0201, - LBUTTONUP = 0x0202, - LBUTTONDBLCLK = 0x0203, - RBUTTONDOWN = 0x0204, - RBUTTONUP = 0x0205, - RBUTTONDBLCLK = 0x0206, - MBUTTONDOWN = 0x0207, - MBUTTONUP = 0x0208, - MBUTTONDBLCLK = 0x0209, - MOUSEWHEEL = 0x020A, - XBUTTONDOWN = 0x020B, - XBUTTONUP = 0x020C, - XBUTTONDBLCLK = 0x020D, - MOUSEHWHEEL = 0x020E, - MOUSELAST = 0x020E + Mousemove = 0x0200, + Lbuttondown = 0x0201, + Lbuttonup = 0x0202, + Lbuttondblclk = 0x0203, + Rbuttondown = 0x0204, + Rbuttonup = 0x0205, + Rbuttondblclk = 0x0206, + Mbuttondown = 0x0207, + Mbuttonup = 0x0208, + Mbuttondblclk = 0x0209, + Mousewheel = 0x020A, + Xbuttondown = 0x020B, + Xbuttonup = 0x020C, + Xbuttondblclk = 0x020D, + Mousehwheel = 0x020E, + Mouselast = 0x020E, } [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] - public struct WNDCLASSEX + public struct WndClassEx { public int cbSize; public ClassStyles style; @@ -64,9 +66,9 @@ namespace Ryujinx.Ava.UI.Helpers public IntPtr lpszClassName; public IntPtr hIconSm; - public WNDCLASSEX() + public WndClassEx() { - cbSize = Marshal.SizeOf<WNDCLASSEX>(); + cbSize = Marshal.SizeOf<WndClassEx>(); } } @@ -77,17 +79,17 @@ namespace Ryujinx.Ava.UI.Helpers public static IntPtr CreateArrowCursor() { - return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW); + return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IdcArrow); } [LibraryImport("user32.dll")] public static partial IntPtr SetCursor(IntPtr handle); [LibraryImport("user32.dll")] - public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvANDPlane, byte[] pvXORPlane); + public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvAndPlane, byte[] pvXorPlane); [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")] - public static partial ushort RegisterClassEx(ref WNDCLASSEX param); + public static partial ushort RegisterClassEx(ref WndClassEx param); [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "UnregisterClassW")] public static partial short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance); diff --git a/src/Ryujinx.Ava/UI/Models/CheatModel.cs b/src/Ryujinx.Ava/UI/Models/CheatModel.cs index a7507add9..4434c09f4 100644 --- a/src/Ryujinx.Ava/UI/Models/CheatModel.cs +++ b/src/Ryujinx.Ava/UI/Models/CheatModel.cs @@ -11,8 +11,8 @@ namespace Ryujinx.Ava.UI.Models public CheatModel(string name, string buildId, bool isEnabled) { - Name = name; - BuildId = buildId; + Name = name; + BuildId = buildId; IsEnabled = isEnabled; } @@ -35,6 +35,6 @@ namespace Ryujinx.Ava.UI.Models public string Name { get; } - public string CleanName => Name.Substring(1, Name.Length - 8); + public string CleanName => Name[1..^7]; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/CheatsList.cs b/src/Ryujinx.Ava/UI/Models/CheatsList.cs index e674f4eb7..1cb0480c7 100644 --- a/src/Ryujinx.Ava/UI/Models/CheatsList.cs +++ b/src/Ryujinx.Ava/UI/Models/CheatsList.cs @@ -10,13 +10,13 @@ namespace Ryujinx.Ava.UI.Models public CheatsList(string buildId, string path) { BuildId = buildId; - Path = path; + Path = path; CollectionChanged += CheatsList_CollectionChanged; } public string BuildId { get; } - public string Path { get; } + public string Path { get; } public bool IsEnabled { @@ -48,4 +48,4 @@ namespace Ryujinx.Ava.UI.Models OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled))); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/DeviceType.cs b/src/Ryujinx.Ava/UI/Models/DeviceType.cs index fa2e620cc..bb4fc3b30 100644 --- a/src/Ryujinx.Ava/UI/Models/DeviceType.cs +++ b/src/Ryujinx.Ava/UI/Models/DeviceType.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Ava.UI.Models { None, Keyboard, - Controller + Controller, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs b/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs index b2ad0d31d..fedb3527b 100644 --- a/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs +++ b/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs @@ -18,18 +18,18 @@ namespace Ryujinx.Ava.UI.Models } } - public string TitleId { get; } + public string TitleId { get; } public string ContainerPath { get; } - public string FullPath { get; } + public string FullPath { get; } public string FileName => Path.GetFileName(ContainerPath); public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled) { - TitleId = titleId; + TitleId = titleId; ContainerPath = containerPath; - FullPath = fullPath; - Enabled = enabled; + FullPath = fullPath; + Enabled = enabled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs b/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs index 3627ada9a..8a4346556 100644 --- a/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs +++ b/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs @@ -29,4 +29,4 @@ namespace Ryujinx.Ava.UI.Models.Generic return (IsAscending ? 1 : -1) * DateTime.Compare(bValue.Value, aValue.Value); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs index 2acd716ba..f1352c6d8 100644 --- a/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs +++ b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs @@ -7,16 +7,16 @@ using System; namespace Ryujinx.Ava.UI.Models { - internal class InputConfiguration<Key, Stick> : BaseModel + internal class InputConfiguration<TKey, TStick> : BaseModel { private float _deadzoneRight; private float _triggerThreshold; private float _deadzoneLeft; private double _gyroDeadzone; private int _sensitivity; - private bool enableMotion; - private float weakRumble; - private float strongRumble; + private bool _enableMotion; + private float _weakRumble; + private float _strongRumble; private float _rangeLeft; private float _rangeRight; @@ -37,17 +37,17 @@ namespace Ryujinx.Ava.UI.Models /// </summary> public PlayerIndex PlayerIndex { get; set; } - public Stick LeftJoystick { get; set; } + public TStick LeftJoystick { get; set; } public bool LeftInvertStickX { get; set; } public bool LeftInvertStickY { get; set; } public bool RightRotate90 { get; set; } - public Key LeftControllerStickButton { get; set; } + public TKey LeftControllerStickButton { get; set; } - public Stick RightJoystick { get; set; } + public TStick RightJoystick { get; set; } public bool RightInvertStickX { get; set; } public bool RightInvertStickY { get; set; } public bool LeftRotate90 { get; set; } - public Key RightControllerStickButton { get; set; } + public TKey RightControllerStickButton { get; set; } public float DeadzoneLeft { @@ -106,38 +106,37 @@ namespace Ryujinx.Ava.UI.Models public MotionInputBackendType MotionBackend { get; set; } - public Key ButtonMinus { get; set; } - public Key ButtonL { get; set; } - public Key ButtonZl { get; set; } - public Key LeftButtonSl { get; set; } - public Key LeftButtonSr { get; set; } - public Key DpadUp { get; set; } - public Key DpadDown { get; set; } - public Key DpadLeft { get; set; } - public Key DpadRight { get; set; } + public TKey ButtonMinus { get; set; } + public TKey ButtonL { get; set; } + public TKey ButtonZl { get; set; } + public TKey LeftButtonSl { get; set; } + public TKey LeftButtonSr { get; set; } + public TKey DpadUp { get; set; } + public TKey DpadDown { get; set; } + public TKey DpadLeft { get; set; } + public TKey DpadRight { get; set; } - public Key ButtonPlus { get; set; } - public Key ButtonR { get; set; } - public Key ButtonZr { get; set; } - public Key RightButtonSl { get; set; } - public Key RightButtonSr { get; set; } - public Key ButtonX { get; set; } - public Key ButtonB { get; set; } - public Key ButtonY { get; set; } - public Key ButtonA { get; set; } + public TKey ButtonPlus { get; set; } + public TKey ButtonR { get; set; } + public TKey ButtonZr { get; set; } + public TKey RightButtonSl { get; set; } + public TKey RightButtonSr { get; set; } + public TKey ButtonX { get; set; } + public TKey ButtonB { get; set; } + public TKey ButtonY { get; set; } + public TKey ButtonA { get; set; } + public TKey LeftStickUp { get; set; } + public TKey LeftStickDown { get; set; } + public TKey LeftStickLeft { get; set; } + public TKey LeftStickRight { get; set; } + public TKey LeftKeyboardStickButton { get; set; } - public Key LeftStickUp { get; set; } - public Key LeftStickDown { get; set; } - public Key LeftStickLeft { get; set; } - public Key LeftStickRight { get; set; } - public Key LeftKeyboardStickButton { get; set; } - - public Key RightStickUp { get; set; } - public Key RightStickDown { get; set; } - public Key RightStickLeft { get; set; } - public Key RightStickRight { get; set; } - public Key RightKeyboardStickButton { get; set; } + public TKey RightStickUp { get; set; } + public TKey RightStickDown { get; set; } + public TKey RightStickLeft { get; set; } + public TKey RightStickRight { get; set; } + public TKey RightKeyboardStickButton { get; set; } public int Sensitivity { @@ -163,9 +162,9 @@ namespace Ryujinx.Ava.UI.Models public bool EnableMotion { - get => enableMotion; set + get => _enableMotion; set { - enableMotion = value; + _enableMotion = value; OnPropertyChanged(); } @@ -181,18 +180,18 @@ namespace Ryujinx.Ava.UI.Models public bool EnableRumble { get; set; } public float WeakRumble { - get => weakRumble; set + get => _weakRumble; set { - weakRumble = value; + _weakRumble = value; OnPropertyChanged(); } } public float StrongRumble { - get => strongRumble; set + get => _strongRumble; set { - strongRumble = value; + _strongRumble = value; OnPropertyChanged(); } @@ -209,71 +208,71 @@ namespace Ryujinx.Ava.UI.Models if (config is StandardKeyboardInputConfig keyboardConfig) { - LeftStickUp = (Key)(object)keyboardConfig.LeftJoyconStick.StickUp; - LeftStickDown = (Key)(object)keyboardConfig.LeftJoyconStick.StickDown; - LeftStickLeft = (Key)(object)keyboardConfig.LeftJoyconStick.StickLeft; - LeftStickRight = (Key)(object)keyboardConfig.LeftJoyconStick.StickRight; - LeftKeyboardStickButton = (Key)(object)keyboardConfig.LeftJoyconStick.StickButton; + LeftStickUp = (TKey)(object)keyboardConfig.LeftJoyconStick.StickUp; + LeftStickDown = (TKey)(object)keyboardConfig.LeftJoyconStick.StickDown; + LeftStickLeft = (TKey)(object)keyboardConfig.LeftJoyconStick.StickLeft; + LeftStickRight = (TKey)(object)keyboardConfig.LeftJoyconStick.StickRight; + LeftKeyboardStickButton = (TKey)(object)keyboardConfig.LeftJoyconStick.StickButton; - RightStickUp = (Key)(object)keyboardConfig.RightJoyconStick.StickUp; - RightStickDown = (Key)(object)keyboardConfig.RightJoyconStick.StickDown; - RightStickLeft = (Key)(object)keyboardConfig.RightJoyconStick.StickLeft; - RightStickRight = (Key)(object)keyboardConfig.RightJoyconStick.StickRight; - RightKeyboardStickButton = (Key)(object)keyboardConfig.RightJoyconStick.StickButton; + RightStickUp = (TKey)(object)keyboardConfig.RightJoyconStick.StickUp; + RightStickDown = (TKey)(object)keyboardConfig.RightJoyconStick.StickDown; + RightStickLeft = (TKey)(object)keyboardConfig.RightJoyconStick.StickLeft; + RightStickRight = (TKey)(object)keyboardConfig.RightJoyconStick.StickRight; + RightKeyboardStickButton = (TKey)(object)keyboardConfig.RightJoyconStick.StickButton; - ButtonA = (Key)(object)keyboardConfig.RightJoycon.ButtonA; - ButtonB = (Key)(object)keyboardConfig.RightJoycon.ButtonB; - ButtonX = (Key)(object)keyboardConfig.RightJoycon.ButtonX; - ButtonY = (Key)(object)keyboardConfig.RightJoycon.ButtonY; - ButtonR = (Key)(object)keyboardConfig.RightJoycon.ButtonR; - RightButtonSl = (Key)(object)keyboardConfig.RightJoycon.ButtonSl; - RightButtonSr = (Key)(object)keyboardConfig.RightJoycon.ButtonSr; - ButtonZr = (Key)(object)keyboardConfig.RightJoycon.ButtonZr; - ButtonPlus = (Key)(object)keyboardConfig.RightJoycon.ButtonPlus; + ButtonA = (TKey)(object)keyboardConfig.RightJoycon.ButtonA; + ButtonB = (TKey)(object)keyboardConfig.RightJoycon.ButtonB; + ButtonX = (TKey)(object)keyboardConfig.RightJoycon.ButtonX; + ButtonY = (TKey)(object)keyboardConfig.RightJoycon.ButtonY; + ButtonR = (TKey)(object)keyboardConfig.RightJoycon.ButtonR; + RightButtonSl = (TKey)(object)keyboardConfig.RightJoycon.ButtonSl; + RightButtonSr = (TKey)(object)keyboardConfig.RightJoycon.ButtonSr; + ButtonZr = (TKey)(object)keyboardConfig.RightJoycon.ButtonZr; + ButtonPlus = (TKey)(object)keyboardConfig.RightJoycon.ButtonPlus; - DpadUp = (Key)(object)keyboardConfig.LeftJoycon.DpadUp; - DpadDown = (Key)(object)keyboardConfig.LeftJoycon.DpadDown; - DpadLeft = (Key)(object)keyboardConfig.LeftJoycon.DpadLeft; - DpadRight = (Key)(object)keyboardConfig.LeftJoycon.DpadRight; - ButtonMinus = (Key)(object)keyboardConfig.LeftJoycon.ButtonMinus; - LeftButtonSl = (Key)(object)keyboardConfig.LeftJoycon.ButtonSl; - LeftButtonSr = (Key)(object)keyboardConfig.LeftJoycon.ButtonSr; - ButtonZl = (Key)(object)keyboardConfig.LeftJoycon.ButtonZl; - ButtonL = (Key)(object)keyboardConfig.LeftJoycon.ButtonL; + DpadUp = (TKey)(object)keyboardConfig.LeftJoycon.DpadUp; + DpadDown = (TKey)(object)keyboardConfig.LeftJoycon.DpadDown; + DpadLeft = (TKey)(object)keyboardConfig.LeftJoycon.DpadLeft; + DpadRight = (TKey)(object)keyboardConfig.LeftJoycon.DpadRight; + ButtonMinus = (TKey)(object)keyboardConfig.LeftJoycon.ButtonMinus; + LeftButtonSl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSl; + LeftButtonSr = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSr; + ButtonZl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonZl; + ButtonL = (TKey)(object)keyboardConfig.LeftJoycon.ButtonL; } else if (config is StandardControllerInputConfig controllerConfig) { - LeftJoystick = (Stick)(object)controllerConfig.LeftJoyconStick.Joystick; + LeftJoystick = (TStick)(object)controllerConfig.LeftJoyconStick.Joystick; LeftInvertStickX = controllerConfig.LeftJoyconStick.InvertStickX; LeftInvertStickY = controllerConfig.LeftJoyconStick.InvertStickY; LeftRotate90 = controllerConfig.LeftJoyconStick.Rotate90CW; - LeftControllerStickButton = (Key)(object)controllerConfig.LeftJoyconStick.StickButton; + LeftControllerStickButton = (TKey)(object)controllerConfig.LeftJoyconStick.StickButton; - RightJoystick = (Stick)(object)controllerConfig.RightJoyconStick.Joystick; + RightJoystick = (TStick)(object)controllerConfig.RightJoyconStick.Joystick; RightInvertStickX = controllerConfig.RightJoyconStick.InvertStickX; RightInvertStickY = controllerConfig.RightJoyconStick.InvertStickY; RightRotate90 = controllerConfig.RightJoyconStick.Rotate90CW; - RightControllerStickButton = (Key)(object)controllerConfig.RightJoyconStick.StickButton; + RightControllerStickButton = (TKey)(object)controllerConfig.RightJoyconStick.StickButton; - ButtonA = (Key)(object)controllerConfig.RightJoycon.ButtonA; - ButtonB = (Key)(object)controllerConfig.RightJoycon.ButtonB; - ButtonX = (Key)(object)controllerConfig.RightJoycon.ButtonX; - ButtonY = (Key)(object)controllerConfig.RightJoycon.ButtonY; - ButtonR = (Key)(object)controllerConfig.RightJoycon.ButtonR; - RightButtonSl = (Key)(object)controllerConfig.RightJoycon.ButtonSl; - RightButtonSr = (Key)(object)controllerConfig.RightJoycon.ButtonSr; - ButtonZr = (Key)(object)controllerConfig.RightJoycon.ButtonZr; - ButtonPlus = (Key)(object)controllerConfig.RightJoycon.ButtonPlus; + ButtonA = (TKey)(object)controllerConfig.RightJoycon.ButtonA; + ButtonB = (TKey)(object)controllerConfig.RightJoycon.ButtonB; + ButtonX = (TKey)(object)controllerConfig.RightJoycon.ButtonX; + ButtonY = (TKey)(object)controllerConfig.RightJoycon.ButtonY; + ButtonR = (TKey)(object)controllerConfig.RightJoycon.ButtonR; + RightButtonSl = (TKey)(object)controllerConfig.RightJoycon.ButtonSl; + RightButtonSr = (TKey)(object)controllerConfig.RightJoycon.ButtonSr; + ButtonZr = (TKey)(object)controllerConfig.RightJoycon.ButtonZr; + ButtonPlus = (TKey)(object)controllerConfig.RightJoycon.ButtonPlus; - DpadUp = (Key)(object)controllerConfig.LeftJoycon.DpadUp; - DpadDown = (Key)(object)controllerConfig.LeftJoycon.DpadDown; - DpadLeft = (Key)(object)controllerConfig.LeftJoycon.DpadLeft; - DpadRight = (Key)(object)controllerConfig.LeftJoycon.DpadRight; - ButtonMinus = (Key)(object)controllerConfig.LeftJoycon.ButtonMinus; - LeftButtonSl = (Key)(object)controllerConfig.LeftJoycon.ButtonSl; - LeftButtonSr = (Key)(object)controllerConfig.LeftJoycon.ButtonSr; - ButtonZl = (Key)(object)controllerConfig.LeftJoycon.ButtonZl; - ButtonL = (Key)(object)controllerConfig.LeftJoycon.ButtonL; + DpadUp = (TKey)(object)controllerConfig.LeftJoycon.DpadUp; + DpadDown = (TKey)(object)controllerConfig.LeftJoycon.DpadDown; + DpadLeft = (TKey)(object)controllerConfig.LeftJoycon.DpadLeft; + DpadRight = (TKey)(object)controllerConfig.LeftJoycon.DpadRight; + ButtonMinus = (TKey)(object)controllerConfig.LeftJoycon.ButtonMinus; + LeftButtonSl = (TKey)(object)controllerConfig.LeftJoycon.ButtonSl; + LeftButtonSr = (TKey)(object)controllerConfig.LeftJoycon.ButtonSr; + ButtonZl = (TKey)(object)controllerConfig.LeftJoycon.ButtonZl; + ButtonL = (TKey)(object)controllerConfig.LeftJoycon.ButtonL; DeadzoneLeft = controllerConfig.DeadzoneLeft; DeadzoneRight = controllerConfig.DeadzoneRight; @@ -317,65 +316,66 @@ namespace Ryujinx.Ava.UI.Models { if (Backend == InputBackendType.WindowKeyboard) { - return new StandardKeyboardInputConfig() + return new StandardKeyboardInputConfig { Id = Id, Backend = Backend, PlayerIndex = PlayerIndex, ControllerType = ControllerType, - LeftJoycon = new LeftJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>() + LeftJoycon = new LeftJoyconCommonConfig<Key> { - DpadUp = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadUp, - DpadDown = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadDown, - DpadLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadLeft, - DpadRight = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadRight, - ButtonL = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonL, - ButtonZl = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZl, - ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSl, - ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSr, - ButtonMinus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonMinus + DpadUp = (Key)(object)DpadUp, + DpadDown = (Key)(object)DpadDown, + DpadLeft = (Key)(object)DpadLeft, + DpadRight = (Key)(object)DpadRight, + ButtonL = (Key)(object)ButtonL, + ButtonZl = (Key)(object)ButtonZl, + ButtonSl = (Key)(object)LeftButtonSl, + ButtonSr = (Key)(object)LeftButtonSr, + ButtonMinus = (Key)(object)ButtonMinus, }, - RightJoycon = new RightJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>() + RightJoycon = new RightJoyconCommonConfig<Key> { - ButtonA = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonA, - ButtonB = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonB, - ButtonX = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonX, - ButtonY = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonY, - ButtonPlus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonPlus, - ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSl, - ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSr, - ButtonR = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonR, - ButtonZr = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZr + ButtonA = (Key)(object)ButtonA, + ButtonB = (Key)(object)ButtonB, + ButtonX = (Key)(object)ButtonX, + ButtonY = (Key)(object)ButtonY, + ButtonPlus = (Key)(object)ButtonPlus, + ButtonSl = (Key)(object)RightButtonSl, + ButtonSr = (Key)(object)RightButtonSr, + ButtonR = (Key)(object)ButtonR, + ButtonZr = (Key)(object)ButtonZr, }, - LeftJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>() + LeftJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickUp, - StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickDown, - StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickRight, - StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickLeft, - StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftKeyboardStickButton + StickUp = (Key)(object)LeftStickUp, + StickDown = (Key)(object)LeftStickDown, + StickRight = (Key)(object)LeftStickRight, + StickLeft = (Key)(object)LeftStickLeft, + StickButton = (Key)(object)LeftKeyboardStickButton, }, - RightJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>() + RightJoyconStick = new JoyconConfigKeyboardStick<Key> { - StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickUp, - StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickDown, - StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickLeft, - StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickRight, - StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)RightKeyboardStickButton + StickUp = (Key)(object)RightStickUp, + StickDown = (Key)(object)RightStickDown, + StickLeft = (Key)(object)RightStickLeft, + StickRight = (Key)(object)RightStickRight, + StickButton = (Key)(object)RightKeyboardStickButton, }, - Version = InputConfig.CurrentVersion + Version = InputConfig.CurrentVersion, }; } - else if (Backend == InputBackendType.GamepadSDL2) + + if (Backend == InputBackendType.GamepadSDL2) { - var config = new StandardControllerInputConfig() + var config = new StandardControllerInputConfig { Id = Id, Backend = Backend, PlayerIndex = PlayerIndex, ControllerType = ControllerType, - LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>() + LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId> { DpadUp = (GamepadInputId)(object)DpadUp, DpadDown = (GamepadInputId)(object)DpadDown, @@ -387,7 +387,7 @@ namespace Ryujinx.Ava.UI.Models ButtonSr = (GamepadInputId)(object)LeftButtonSr, ButtonMinus = (GamepadInputId)(object)ButtonMinus, }, - RightJoycon = new RightJoyconCommonConfig<GamepadInputId>() + RightJoycon = new RightJoyconCommonConfig<GamepadInputId> { ButtonA = (GamepadInputId)(object)ButtonA, ButtonB = (GamepadInputId)(object)ButtonB, @@ -399,7 +399,7 @@ namespace Ryujinx.Ava.UI.Models ButtonR = (GamepadInputId)(object)ButtonR, ButtonZr = (GamepadInputId)(object)ButtonZr, }, - LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>() + LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId> { Joystick = (StickInputId)(object)LeftJoystick, InvertStickX = LeftInvertStickX, @@ -407,7 +407,7 @@ namespace Ryujinx.Ava.UI.Models Rotate90CW = LeftRotate90, StickButton = (GamepadInputId)(object)LeftControllerStickButton, }, - RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>() + RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId> { Joystick = (StickInputId)(object)RightJoystick, InvertStickX = RightInvertStickX, @@ -415,11 +415,11 @@ namespace Ryujinx.Ava.UI.Models Rotate90CW = RightRotate90, StickButton = (GamepadInputId)(object)RightControllerStickButton, }, - Rumble = new RumbleConfigController() + Rumble = new RumbleConfigController { EnableRumble = EnableRumble, WeakRumble = WeakRumble, - StrongRumble = StrongRumble + StrongRumble = StrongRumble, }, Version = InputConfig.CurrentVersion, DeadzoneLeft = DeadzoneLeft, @@ -428,19 +428,19 @@ namespace Ryujinx.Ava.UI.Models RangeRight = RangeRight, TriggerThreshold = TriggerThreshold, Motion = EnableCemuHookMotion - ? new CemuHookMotionConfigController() - { - DsuServerHost = DsuServerHost, - DsuServerPort = DsuServerPort, - Slot = Slot, - AltSlot = AltSlot, - MirrorInput = MirrorInput, - MotionBackend = MotionInputBackendType.CemuHook - } - : new StandardMotionConfigController() - { - MotionBackend = MotionInputBackendType.GamepadDriver - } + ? new CemuHookMotionConfigController + { + DsuServerHost = DsuServerHost, + DsuServerPort = DsuServerPort, + Slot = Slot, + AltSlot = AltSlot, + MirrorInput = MirrorInput, + MotionBackend = MotionInputBackendType.CemuHook, + } + : new StandardMotionConfigController + { + MotionBackend = MotionInputBackendType.GamepadDriver, + }, }; config.Motion.Sensitivity = Sensitivity; @@ -453,4 +453,4 @@ namespace Ryujinx.Ava.UI.Models return null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/ProfileImageModel.cs b/src/Ryujinx.Ava/UI/Models/ProfileImageModel.cs index 8aa194005..99365dfc7 100644 --- a/src/Ryujinx.Ava/UI/Models/ProfileImageModel.cs +++ b/src/Ryujinx.Ava/UI/Models/ProfileImageModel.cs @@ -29,4 +29,4 @@ namespace Ryujinx.Ava.UI.Models } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/SaveModel.cs b/src/Ryujinx.Ava/UI/Models/SaveModel.cs index cc9b67681..2f220162f 100644 --- a/src/Ryujinx.Ava/UI/Models/SaveModel.cs +++ b/src/Ryujinx.Ava/UI/Models/SaveModel.cs @@ -2,12 +2,12 @@ using LibHac.Fs; using LibHac.Ncm; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; -using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.App.Common; using System; using System.IO; using System.Linq; using System.Threading.Tasks; +using Path = System.IO.Path; namespace Ryujinx.Ava.UI.Models { @@ -58,7 +58,7 @@ namespace Ryujinx.Ava.UI.Models return "0 KiB"; } - public SaveModel(SaveDataInfo info, VirtualFileSystem virtualFileSystem) + public SaveModel(SaveDataInfo info) { SaveId = info.SaveDataId; TitleId = info.ProgramId; @@ -81,10 +81,11 @@ namespace Ryujinx.Ava.UI.Models Task.Run(() => { - var saveRoot = System.IO.Path.Combine(virtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}"); + var saveRoot = Path.Combine(MainWindow.MainWindowViewModel.VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}"); - long total_size = GetDirectorySize(saveRoot); - long GetDirectorySize(string path) + long totalSize = GetDirectorySize(saveRoot); + + static long GetDirectorySize(string path) { long size = 0; if (Directory.Exists(path)) @@ -105,7 +106,7 @@ namespace Ryujinx.Ava.UI.Models return size; } - Size = total_size; + Size = totalSize; }); } diff --git a/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs b/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs index ea2b90385..cc29442d0 100644 --- a/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs @@ -25,4 +25,4 @@ namespace Ryujinx.Ava.UI.Models GpuName = gpuName; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/TempProfile.cs b/src/Ryujinx.Ava/UI/Models/TempProfile.cs index 05e16632d..659092e6d 100644 --- a/src/Ryujinx.Ava/UI/Models/TempProfile.cs +++ b/src/Ryujinx.Ava/UI/Models/TempProfile.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Models private string _name = String.Empty; private UserId _userId; - public uint MaxProfileNameLength => 0x20; + public static uint MaxProfileNameLength => 0x20; public byte[] Image { @@ -58,4 +58,4 @@ namespace Ryujinx.Ava.UI.Models } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/TimeZone.cs b/src/Ryujinx.Ava/UI/Models/TimeZone.cs index cb6cc2fdc..950fbce43 100644 --- a/src/Ryujinx.Ava/UI/Models/TimeZone.cs +++ b/src/Ryujinx.Ava/UI/Models/TimeZone.cs @@ -13,4 +13,4 @@ namespace Ryujinx.Ava.UI.Models public string Location { get; set; } public string Abbreviation { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs b/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs index 80476a433..3b44e8ee6 100644 --- a/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs +++ b/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs @@ -16,4 +16,4 @@ namespace Ryujinx.Ava.UI.Models Path = path; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Models/UserProfile.cs b/src/Ryujinx.Ava/UI/Models/UserProfile.cs index e7cd53007..e1698b4df 100644 --- a/src/Ryujinx.Ava/UI/Models/UserProfile.cs +++ b/src/Ryujinx.Ava/UI/Models/UserProfile.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Media; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.ViewModels; @@ -87,7 +88,7 @@ namespace Ryujinx.Ava.UI.Models private void UpdateBackground() { - Avalonia.Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color); + Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color); if (color is not null) { @@ -100,4 +101,4 @@ namespace Ryujinx.Ava.UI.Models _owner.Navigate(typeof(UserEditorView), (_owner, userProfile, true)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs index c5dd9332c..e324b484d 100644 --- a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs +++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -21,20 +21,20 @@ namespace Ryujinx.Ava.UI.Renderer public class EmbeddedWindow : NativeControlHost { private WindowProc _wndProcDelegate; - private string _className; + private string _className; protected GLXWindow X11Window { get; set; } protected IntPtr WindowHandle { get; set; } - protected IntPtr X11Display { get; set; } - protected IntPtr NsView { get; set; } - protected IntPtr MetalLayer { get; set; } + protected IntPtr X11Display { get; set; } + protected IntPtr NsView { get; set; } + protected IntPtr MetalLayer { get; set; } public delegate void UpdateBoundsCallbackDelegate(Rect rect); private UpdateBoundsCallbackDelegate _updateBoundsCallback; public event EventHandler<IntPtr> WindowCreated; - public event EventHandler<Size> SizeChanged; + public event EventHandler<Size> SizeChanged; public EmbeddedWindow() { @@ -50,9 +50,9 @@ namespace Ryujinx.Ava.UI.Renderer protected virtual void OnWindowDestroying() { WindowHandle = IntPtr.Zero; - X11Display = IntPtr.Zero; - NsView = IntPtr.Zero; - MetalLayer = IntPtr.Zero; + X11Display = IntPtr.Zero; + NsView = IntPtr.Zero; + MetalLayer = IntPtr.Zero; } private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e) @@ -77,11 +77,13 @@ namespace Ryujinx.Ava.UI.Renderer { return CreateLinux(control); } - else if (OperatingSystem.IsWindows()) + + if (OperatingSystem.IsWindows()) { return CreateWin32(control); } - else if (OperatingSystem.IsMacOS()) + + if (OperatingSystem.IsMacOS()) { return CreateMacOS(); } @@ -127,7 +129,7 @@ namespace Ryujinx.Ava.UI.Renderer } WindowHandle = X11Window.WindowHandle.RawHandle; - X11Display = X11Window.DisplayHandle.RawHandle; + X11Display = X11Window.DisplayHandle.RawHandle; return new PlatformHandle(WindowHandle, "X11"); } @@ -141,23 +143,23 @@ namespace Ryujinx.Ava.UI.Renderer { if (VisualRoot != null) { - if (msg == WindowsMessages.LBUTTONDOWN || - msg == WindowsMessages.RBUTTONDOWN || - msg == WindowsMessages.LBUTTONUP || - msg == WindowsMessages.RBUTTONUP || - msg == WindowsMessages.MOUSEMOVE) + if (msg == WindowsMessages.Lbuttondown || + msg == WindowsMessages.Rbuttondown || + msg == WindowsMessages.Lbuttonup || + msg == WindowsMessages.Rbuttonup || + msg == WindowsMessages.Mousemove) { - Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value; - Pointer pointer = new(0, PointerType.Mouse, true); + Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value; + Pointer pointer = new(0, PointerType.Mouse, true); switch (msg) { - case WindowsMessages.LBUTTONDOWN: - case WindowsMessages.RBUTTONDOWN: + case WindowsMessages.Lbuttondown: + case WindowsMessages.Rbuttondown: { - bool isLeft = msg == WindowsMessages.LBUTTONDOWN; - RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; - PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed); + bool isLeft = msg == WindowsMessages.Lbuttondown; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed); var evnt = new PointerPressedEventArgs( this, @@ -172,12 +174,12 @@ namespace Ryujinx.Ava.UI.Renderer break; } - case WindowsMessages.LBUTTONUP: - case WindowsMessages.RBUTTONUP: + case WindowsMessages.Lbuttonup: + case WindowsMessages.Rbuttonup: { - bool isLeft = msg == WindowsMessages.LBUTTONUP; - RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; - PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased); + bool isLeft = msg == WindowsMessages.Lbuttonup; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased); var evnt = new PointerReleasedEventArgs( this, @@ -193,7 +195,7 @@ namespace Ryujinx.Ava.UI.Renderer break; } - case WindowsMessages.MOUSEMOVE: + case WindowsMessages.Mousemove: { var evnt = new PointerEventArgs( PointerMovedEvent, @@ -216,19 +218,19 @@ namespace Ryujinx.Ava.UI.Renderer return DefWindowProc(hWnd, msg, wParam, lParam); }; - WNDCLASSEX wndClassEx = new() + WndClassEx wndClassEx = new() { - cbSize = Marshal.SizeOf<WNDCLASSEX>(), - hInstance = GetModuleHandle(null), - lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), - style = ClassStyles.CS_OWNDC, + cbSize = Marshal.SizeOf<WndClassEx>(), + hInstance = GetModuleHandle(null), + lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), + style = ClassStyles.CsOwndc, lpszClassName = Marshal.StringToHGlobalUni(_className), - hCursor = CreateArrowCursor() + hCursor = CreateArrowCursor(), }; RegisterClassEx(ref wndClassEx); - WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WsChild, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); Marshal.FreeHGlobal(wndClassEx.lpszClassName); @@ -280,9 +282,11 @@ namespace Ryujinx.Ava.UI.Renderer } [SupportedOSPlatform("macos")] +#pragma warning disable CA1822 // Mark member as static void DestroyMacOS() { // TODO } +#pragma warning restore CA1822 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs index d427ab88c..769a1c91a 100644 --- a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs +++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs @@ -91,4 +91,4 @@ namespace Ryujinx.Ava.UI.Renderer MakeCurrent(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs index 0b3eb9e30..fafbec207 100644 --- a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs +++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs @@ -34,9 +34,9 @@ namespace Ryujinx.Ava.UI.Renderer return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, nativeWindowBase)); } - public SurfaceKHR CreateSurface(Instance instance, Vk api) + public SurfaceKHR CreateSurface(Instance instance, Vk _) { return CreateSurface(instance); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs b/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs index a2ec02b25..85e8585fc 100644 --- a/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs +++ b/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs @@ -17,4 +17,4 @@ namespace Ryujinx.Ava.UI.Renderer return _getProcAddress(procName); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs index ee10282db..b74265a31 100644 --- a/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs +++ b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Renderer public readonly EmbeddedWindow EmbeddedWindow; public event EventHandler<EventArgs> WindowCreated; - public event Action<object, Size> SizeChanged; + public event Action<object, Size> SizeChanged; public RendererHost() { @@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Renderer private void Initialize() { EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated; - EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged; + EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged; Content = EmbeddedWindow; } @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Renderer if (EmbeddedWindow != null) { EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated; - EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged; + EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged; } GC.SuppressFinalize(this); @@ -65,4 +65,4 @@ namespace Ryujinx.Ava.UI.Renderer WindowCreated?.Invoke(this, EventArgs.Empty); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs b/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs index e090f14c7..a2c7aa279 100644 --- a/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs +++ b/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs @@ -10,12 +10,12 @@ namespace Ryujinx.Ava.UI.Renderer class SPBOpenGLContext : IOpenGLContext { private readonly OpenGLContextBase _context; - private readonly NativeWindowBase _window; + private readonly NativeWindowBase _window; private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window) { _context = context; - _window = window; + _window = window; } public void Dispose() @@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Renderer public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext) { OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext); - NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); + NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); context.Initialize(window); context.MakeCurrent(window); diff --git a/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs index 479411cba..fddcd71a7 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Media.Imaging; +using Avalonia.Platform; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; using Ryujinx.Common.Utilities; @@ -87,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels { Version = Program.Version; - var assets = AvaloniaLocator.Current.GetService<Avalonia.Platform.IAssetLoader>(); + var assets = AvaloniaLocator.Current.GetService<IAssetLoader>(); if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light") { @@ -130,4 +131,4 @@ namespace Ryujinx.Ava.UI.ViewModels } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index ecd425ef2..3138dd146 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -18,7 +18,6 @@ using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; namespace Ryujinx.Ava.UI.ViewModels { @@ -44,7 +43,7 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _useRandomUuid; private string _usage; - private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) { @@ -52,7 +51,7 @@ namespace Ryujinx.Ava.UI.ViewModels _httpClient = new HttpClient { - Timeout = TimeSpan.FromSeconds(30) + Timeout = TimeSpan.FromSeconds(30), }; LastScannedAmiiboId = lastScannedAmiiboId; @@ -185,6 +184,7 @@ namespace Ryujinx.Ava.UI.ViewModels public void Dispose() { + GC.SuppressFinalize(this); _httpClient.Dispose(); } @@ -196,7 +196,7 @@ namespace Ryujinx.Ava.UI.ViewModels { amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); - if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) + if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated)) { amiiboJsonString = await DownloadAmiiboJson(); } @@ -215,7 +215,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; + _amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo; _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); ParseAmiiboData(); @@ -426,18 +426,17 @@ namespace Ryujinx.Ava.UI.ViewModels if (response.IsSuccessStatusCode) { byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync(); - using (MemoryStream memoryStream = new(amiiboPreviewBytes)) - { - Bitmap bitmap = new(memoryStream); + using MemoryStream memoryStream = new(amiiboPreviewBytes); - double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width, + Bitmap bitmap = new(memoryStream); + + double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width, AmiiboImageSize / bitmap.Size.Height); - int resizeHeight = (int)(bitmap.Size.Height * ratio); - int resizeWidth = (int)(bitmap.Size.Width * ratio); + int resizeHeight = (int)(bitmap.Size.Height * ratio); + int resizeWidth = (int)(bitmap.Size.Width * ratio); - AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight)); - } + AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight)); } else { @@ -447,15 +446,14 @@ namespace Ryujinx.Ava.UI.ViewModels private void ResetAmiiboPreview() { - using (MemoryStream memoryStream = new(_amiiboLogoBytes)) - { - Bitmap bitmap = new(memoryStream); + using MemoryStream memoryStream = new(_amiiboLogoBytes); - AmiiboImage = bitmap; - } + Bitmap bitmap = new(memoryStream); + + AmiiboImage = bitmap; } - private async void ShowInfoDialog() + private static async void ShowInfoDialog() { await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle], LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage], @@ -464,4 +462,4 @@ namespace Ryujinx.Ava.UI.ViewModels LocaleManager.Instance[LocaleKeys.RyujinxInfo]); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs deleted file mode 100644 index b2b310149..000000000 --- a/src/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs +++ /dev/null @@ -1,363 +0,0 @@ -using Avalonia.Media; -using DynamicData; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Ncm; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Ava.UI.Models; -using Ryujinx.HLE.FileSystem; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Color = Avalonia.Media.Color; - -namespace Ryujinx.Ava.UI.ViewModels -{ - internal class AvatarProfileViewModel : BaseModel, IDisposable - { - private const int MaxImageTasks = 4; - - private static readonly Dictionary<string, byte[]> _avatarStore = new(); - private static bool _isPreloading; - private static Action _loadCompleteAction; - - private ObservableCollection<ProfileImageModel> _images; - private Color _backgroundColor = Colors.White; - - private int _selectedIndex; - private int _imagesLoaded; - private bool _isActive; - private byte[] _selectedImage; - private bool _isIndeterminate = true; - - public bool IsActive - { - get => _isActive; - set => _isActive = value; - } - - public AvatarProfileViewModel() - { - _images = new ObservableCollection<ProfileImageModel>(); - } - - public AvatarProfileViewModel(Action loadCompleteAction) - { - _images = new ObservableCollection<ProfileImageModel>(); - - if (_isPreloading) - { - _loadCompleteAction = loadCompleteAction; - } - else - { - ReloadImages(); - } - } - - public Color BackgroundColor - { - get => _backgroundColor; - set - { - _backgroundColor = value; - - IsActive = false; - - ReloadImages(); - } - } - - public ObservableCollection<ProfileImageModel> Images - { - get => _images; - set - { - _images = value; - OnPropertyChanged(); - } - } - - public bool IsIndeterminate - { - get => _isIndeterminate; - set - { - _isIndeterminate = value; - - OnPropertyChanged(); - } - } - - public int ImageCount => _avatarStore.Count; - - public int ImagesLoaded - { - get => _imagesLoaded; - set - { - _imagesLoaded = value; - OnPropertyChanged(); - } - } - - public int SelectedIndex - { - get => _selectedIndex; - set - { - _selectedIndex = value; - - if (_selectedIndex == -1) - { - SelectedImage = null; - } - else - { - SelectedImage = _images[_selectedIndex].Data; - } - - OnPropertyChanged(); - } - } - - public byte[] SelectedImage - { - get => _selectedImage; - private set => _selectedImage = value; - } - - public void ReloadImages() - { - if (_isPreloading) - { - IsIndeterminate = false; - return; - } - Task.Run(() => - { - IsActive = true; - - Images.Clear(); - int selectedIndex = _selectedIndex; - int index = 0; - - ImagesLoaded = 0; - IsIndeterminate = false; - - var keys = _avatarStore.Keys.ToList(); - - var newImages = new List<ProfileImageModel>(); - var tasks = new List<Task>(); - - for (int i = 0; i < MaxImageTasks; i++) - { - var start = i; - tasks.Add(Task.Run(() => ImageTask(start))); - } - - Task.WaitAll(tasks.ToArray()); - - Images.AddRange(newImages); - - void ImageTask(int start) - { - for (int i = start; i < keys.Count; i += MaxImageTasks) - { - if (!IsActive) - { - return; - } - - var key = keys[i]; - var image = _avatarStore[keys[i]]; - - var data = ProcessImage(image); - newImages.Add(new ProfileImageModel(key, data)); - if (index++ == selectedIndex) - { - SelectedImage = data; - } - - Interlocked.Increment(ref _imagesLoaded); - OnPropertyChanged(nameof(ImagesLoaded)); - } - } - }); - } - - private byte[] ProcessImage(byte[] data) - { - using (MemoryStream streamJpg = new()) - { - Image avatarImage = Image.Load(data, new PngDecoder()); - - avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(BackgroundColor.R, - BackgroundColor.G, - BackgroundColor.B, - BackgroundColor.A))); - avatarImage.SaveAsJpeg(streamJpg); - - return streamJpg.ToArray(); - } - } - - public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem) - { - try - { - if (_avatarStore.Count > 0) - { - return; - } - - _isPreloading = true; - - string contentPath = - contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, - NcaContentType.Data); - string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); - - if (!string.IsNullOrWhiteSpace(avatarPath)) - { - using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open)) - { - Nca nca = new(virtualFileSystem.KeySet, ncaFileStream); - IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - foreach (DirectoryEntryEx item in romfs.EnumerateEntries()) - { - // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. - if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && - item.FullPath.Contains("szs")) - { - using var file = new UniqueRef<IFile>(); - - romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read) - .ThrowIfFailure(); - - using (MemoryStream stream = new()) - using (MemoryStream streamPng = new()) - { - file.Get.AsStream().CopyTo(stream); - - stream.Position = 0; - - Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); - - avatarImage.SaveAsPng(streamPng); - - _avatarStore.Add(item.FullPath, streamPng.ToArray()); - } - } - } - } - } - } - finally - { - _isPreloading = false; - _loadCompleteAction?.Invoke(); - } - } - - private static byte[] DecompressYaz0(Stream stream) - { - using (BinaryReader reader = new(stream)) - { - reader.ReadInt32(); // Magic - - uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); - - reader.ReadInt64(); // Padding - - byte[] input = new byte[stream.Length - stream.Position]; - stream.Read(input, 0, input.Length); - - uint inputOffset = 0; - - byte[] output = new byte[decodedLength]; - uint outputOffset = 0; - - ushort mask = 0; - byte header = 0; - - while (outputOffset < decodedLength) - { - if ((mask >>= 1) == 0) - { - header = input[inputOffset++]; - mask = 0x80; - } - - if ((header & mask) != 0) - { - if (outputOffset == output.Length) - { - break; - } - - output[outputOffset++] = input[inputOffset++]; - } - else - { - byte byte1 = input[inputOffset++]; - byte byte2 = input[inputOffset++]; - - uint dist = (uint)((byte1 & 0xF) << 8) | byte2; - uint position = outputOffset - (dist + 1); - - uint length = (uint)byte1 >> 4; - if (length == 0) - { - length = (uint)input[inputOffset++] + 0x12; - } - else - { - length += 2; - } - - uint gap = outputOffset - position; - uint nonOverlappingLength = length; - - if (nonOverlappingLength > gap) - { - nonOverlappingLength = gap; - } - - Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength); - outputOffset += nonOverlappingLength; - position += nonOverlappingLength; - length -= nonOverlappingLength; - - while (length-- > 0) - { - output[outputOffset++] = output[position++]; - } - } - } - - return output; - } - } - - public void Dispose() - { - _loadCompleteAction = null; - IsActive = false; - } - } -} \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs b/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs index 5a3717fd3..0ff72dbc4 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs @@ -12,4 +12,4 @@ namespace Ryujinx.Ava.UI.ViewModels PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs index fda58504e..c0c625321 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; @@ -44,15 +45,14 @@ namespace Ryujinx.Ava.UI.ViewModels private PlayerIndex _playerId; private int _controller; - private int _controllerNumber = 0; + private int _controllerNumber; private string _controllerImage; private int _device; private object _configuration; private string _profileName; private bool _isLoaded; - private readonly UserControl _owner; - private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public IGamepadDriver AvaloniaKeyboardDriver { get; } public IGamepad SelectedGamepad { get; private set; } @@ -176,11 +176,11 @@ namespace Ryujinx.Ava.UI.ViewModels { get { - SvgImage image = new SvgImage(); + SvgImage image = new(); if (!string.IsNullOrWhiteSpace(_controllerImage)) { - SvgSource source = new SvgSource(); + SvgSource source = new(); source.Load(EmbeddedResources.GetStream(_controllerImage)); @@ -234,22 +234,18 @@ namespace Ryujinx.Ava.UI.ViewModels public ControllerInputViewModel(UserControl owner) : this() { - _owner = owner; - if (Program.PreviewerDetached) { _mainWindow = - (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current + (MainWindow)((IClassicDesktopStyleApplicationLifetime)Application.Current .ApplicationLifetime).MainWindow; AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner); _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - if (_mainWindow.ViewModel.AppHost != null) - { - _mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates(); - } + + _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates(); _isLoaded = false; @@ -351,7 +347,8 @@ namespace Ryujinx.Ava.UI.ViewModels { return; } - else if (type == DeviceType.Keyboard) + + if (type == DeviceType.Keyboard) { if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver) { @@ -448,7 +445,7 @@ namespace Ryujinx.Ava.UI.ViewModels const string Hyphen = "-"; const int Offset = 1; - return str.Substring(str.IndexOf(Hyphen) + Offset); + return str[(str.IndexOf(Hyphen) + Offset)..]; } public void LoadDevices() @@ -562,7 +559,7 @@ namespace Ryujinx.Ava.UI.ViewModels ButtonL = Key.E, ButtonZl = Key.Q, ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound + ButtonSr = Key.Unbound, }, LeftJoyconStick = new JoyconConfigKeyboardStick<Key> @@ -571,7 +568,7 @@ namespace Ryujinx.Ava.UI.ViewModels StickDown = Key.S, StickLeft = Key.A, StickRight = Key.D, - StickButton = Key.F + StickButton = Key.F, }, RightJoycon = new RightJoyconCommonConfig<Key> { @@ -583,7 +580,7 @@ namespace Ryujinx.Ava.UI.ViewModels ButtonR = Key.U, ButtonZr = Key.O, ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound + ButtonSr = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick<Key> { @@ -591,8 +588,8 @@ namespace Ryujinx.Ava.UI.ViewModels StickDown = Key.K, StickLeft = Key.J, StickRight = Key.L, - StickButton = Key.H - } + StickButton = Key.H, + }, }; } else if (activeDevice.Type == DeviceType.Controller) @@ -622,14 +619,14 @@ namespace Ryujinx.Ava.UI.ViewModels ButtonL = ConfigGamepadInputId.LeftShoulder, ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound + ButtonSr = ConfigGamepadInputId.Unbound, }, LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { Joystick = ConfigStickInputId.Left, StickButton = ConfigGamepadInputId.LeftStick, InvertStickX = false, - InvertStickY = false + InvertStickY = false, }, RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> { @@ -641,28 +638,28 @@ namespace Ryujinx.Ava.UI.ViewModels ButtonR = ConfigGamepadInputId.RightShoulder, ButtonZr = ConfigGamepadInputId.RightTrigger, ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound + ButtonSr = ConfigGamepadInputId.Unbound, }, RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> { Joystick = ConfigStickInputId.Right, StickButton = ConfigGamepadInputId.RightStick, InvertStickX = false, - InvertStickY = false + InvertStickY = false, }, Motion = new StandardMotionConfigController { MotionBackend = MotionInputBackendType.GamepadDriver, EnableMotion = true, Sensitivity = 100, - GyroDeadzone = 1 + GyroDeadzone = 1, }, Rumble = new RumbleConfigController { StrongRumble = 1f, WeakRumble = 1f, - EnableRumble = false - } + EnableRumble = false, + }, }; } else @@ -709,7 +706,7 @@ namespace Ryujinx.Ava.UI.ViewModels try { - config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); + config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); } catch (JsonException) { } catch (InvalidOperationException) @@ -754,37 +751,35 @@ namespace Ryujinx.Ava.UI.ViewModels return; } + + bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; + + if (validFileName) + { + string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json"); + + InputConfig config = null; + + if (IsKeyboard) + { + config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig(); + } + else if (IsController) + { + config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig(); + } + + config.ControllerType = Controllers[_controller].Type; + + string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig); + + await File.WriteAllTextAsync(path, jsonString); + + LoadProfiles(); + } else { - bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; - - if (validFileName) - { - string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json"); - - InputConfig config = null; - - if (IsKeyboard) - { - config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig(); - } - else if (IsController) - { - config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig(); - } - - config.ControllerType = Controllers[_controller].Type; - - string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig); - - await File.WriteAllTextAsync(path, jsonString); - - LoadProfiles(); - } - else - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]); - } + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]); } } @@ -887,6 +882,8 @@ namespace Ryujinx.Ava.UI.ViewModels public void Dispose() { + GC.SuppressFinalize(this); + _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; @@ -897,4 +894,4 @@ namespace Ryujinx.Ava.UI.ViewModels AvaloniaKeyboardDriver.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs index 1d7da9a40..f2f569472 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Application = Avalonia.Application; using Path = System.IO.Path; namespace Ryujinx.Ava.UI.ViewModels @@ -29,18 +30,17 @@ namespace Ryujinx.Ava.UI.ViewModels public class DownloadableContentManagerViewModel : BaseModel { private readonly List<DownloadableContentContainer> _downloadableContentContainerList; - private readonly string _downloadableContentJsonPath; + private readonly string _downloadableContentJsonPath; - private VirtualFileSystem _virtualFileSystem; + private readonly VirtualFileSystem _virtualFileSystem; private AvaloniaList<DownloadableContentModel> _downloadableContents = new(); private AvaloniaList<DownloadableContentModel> _views = new(); private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new(); private string _search; - private ulong _titleId; - private string _titleName; + private readonly ulong _titleId; - private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AvaloniaList<DownloadableContentModel> DownloadableContents { @@ -90,18 +90,17 @@ namespace Ryujinx.Ava.UI.ViewModels get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); } - public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId) { _virtualFileSystem = virtualFileSystem; - _titleId = titleId; - _titleName = titleName; + _titleId = titleId; _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); try { - _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer); + _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, _serializerContext.ListDownloadableContentContainer); } catch { @@ -132,7 +131,7 @@ namespace Ryujinx.Ava.UI.ViewModels Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); if (nca != null) - { + { var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath, @@ -196,19 +195,19 @@ namespace Ryujinx.Ava.UI.ViewModels public async void Add() { - OpenFileDialog dialog = new OpenFileDialog() + OpenFileDialog dialog = new() { - Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], - AllowMultiple = true + Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], + AllowMultiple = true, }; dialog.Filters.Add(new FileDialogFilter { - Name = "NSP", - Extensions = { "nsp" } + Name = "NSP", + Extensions = { "nsp" }, }); - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { string[] files = await dialog.ShowAsync(desktop.MainWindow); @@ -231,8 +230,8 @@ namespace Ryujinx.Ava.UI.ViewModels using FileStream containerFile = File.OpenRead(path); - PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); - bool containsDownloadableContent = false; + PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); + bool containsDownloadableContent = false; _virtualFileSystem.ImportTickets(partitionFileSystem); @@ -313,16 +312,16 @@ namespace Ryujinx.Ava.UI.ViewModels container = new DownloadableContentContainer { - ContainerPath = downloadableContent.ContainerPath, - DownloadableContentNcaList = new List<DownloadableContentNca>() + ContainerPath = downloadableContent.ContainerPath, + DownloadableContentNcaList = new List<DownloadableContentNca>(), }; } container.DownloadableContentNcaList.Add(new DownloadableContentNca { - Enabled = downloadableContent.Enabled, - TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), - FullPath = downloadableContent.FullPath + Enabled = downloadableContent.Enabled, + TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), + FullPath = downloadableContent.FullPath, }); } @@ -331,8 +330,8 @@ namespace Ryujinx.Ava.UI.ViewModels _downloadableContentContainerList.Add(container); } - JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer); + JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 0976e2fae..e8d68f769 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; @@ -12,6 +13,7 @@ using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Models.Generic; using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; @@ -23,6 +25,7 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.Ui; +using Ryujinx.Modules; using Ryujinx.Ui.App.Common; using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; @@ -34,7 +37,10 @@ using System.Collections.ObjectModel; using System.IO; using System.Threading; using System.Threading.Tasks; -using Path = System.IO.Path; +using Image = SixLabors.ImageSharp.Image; +using InputManager = Ryujinx.Input.HLE.InputManager; +using Key = Ryujinx.Input.Key; +using MissingKeyException = LibHac.Common.Keys.MissingKeyException; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; namespace Ryujinx.Ava.UI.ViewModels @@ -89,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels private Cursor _cursor; private string _title; private string _currentEmulatedGamePath; - private AutoResetEvent _rendererWaitEvent; + private readonly AutoResetEvent _rendererWaitEvent; private WindowState _windowState; private double _windowWidth; private double _windowHeight; @@ -128,7 +134,7 @@ namespace Ryujinx.Ava.UI.ViewModels ApplicationLibrary applicationLibrary, VirtualFileSystem virtualFileSystem, AccountManager accountManager, - Ryujinx.Input.HLE.InputManager inputManager, + InputManager inputManager, UserChannelPersistence userChannelPersistence, LibHacHorizonManager libHacHorizonManager, IHostUiHandler uiHandler, @@ -152,7 +158,7 @@ namespace Ryujinx.Ava.UI.ViewModels TopLevel = topLevel; } -#region Properties + #region Properties public string SearchText { @@ -177,7 +183,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool CanUpdate { - get => _canUpdate && EnableNonGameRunningControls && Modules.Updater.CanUpdate(false); + get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(false); set { _canUpdate = value; @@ -343,11 +349,11 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool OpenUserSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; + public bool OpenUserSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; - public bool OpenDeviceSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; + public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; - public bool OpenBcatSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; + public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public string LoadHeading { @@ -888,7 +894,7 @@ namespace Ryujinx.Ava.UI.ViewModels public ApplicationLibrary ApplicationLibrary { get; private set; } public VirtualFileSystem VirtualFileSystem { get; private set; } public AccountManager AccountManager { get; private set; } - public Ryujinx.Input.HLE.InputManager InputManager { get; private set; } + public InputManager InputManager { get; private set; } public UserChannelPersistence UserChannelPersistence { get; private set; } public Action<bool> ShowLoading { get; private set; } public Action<bool> SwitchToGameControl { get; private set; } @@ -911,15 +917,16 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; -#endregion + #endregion -#region PrivateMethods + #region PrivateMethods private IComparer<ApplicationData> GetComparer() { return SortMode switch { - ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending), +#pragma warning disable IDE0055 // Disable formatting + ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending), ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSizeBytes) : SortExpressionComparer<ApplicationData>.Descending(app => app.FileSizeBytes), ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TimePlayedNum) @@ -935,6 +942,7 @@ namespace Ryujinx.Ava.UI.ViewModels ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path) : SortExpressionComparer<ApplicationData>.Descending(app => app.Path), _ => null, +#pragma warning restore IDE0055 }; } @@ -1023,7 +1031,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Purge Applet Cache. - DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); + DirectoryInfo miiEditorCacheFolder = new(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); if (miiEditorCacheFolder.Exists) { @@ -1044,18 +1052,21 @@ namespace Ryujinx.Ava.UI.ViewModels { RefreshFirmwareStatus(); } - }) { Name = "GUI.FirmwareInstallerThread" }; + }) + { + Name = "GUI.FirmwareInstallerThread", + }; thread.Start(); } } - catch (LibHac.Common.Keys.MissingKeyException ex) + catch (MissingKeyException ex) { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { Logger.Error?.Print(LogClass.Application, ex.ToString()); - async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, (desktop.MainWindow as MainWindow)); + static async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys); Dispatcher.UIThread.Post(Action); } @@ -1120,7 +1131,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void PrepareLoadScreen() { using MemoryStream stream = new(SelectedIcon); - using var gameIconBmp = SixLabors.ImageSharp.Image.Load<Bgra32>(stream); + using var gameIconBmp = Image.Load<Bgra32>(stream); var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>(); @@ -1175,7 +1186,7 @@ namespace Ryujinx.Ava.UI.ViewModels { Dispatcher.UIThread.InvokeAsync(() => { - Avalonia.Application.Current.Styles.TryGetResource(args.VSyncEnabled + Application.Current.Styles.TryGetResource(args.VSyncEnabled ? "VsyncEnabled" : "VsyncDisabled", out object color); @@ -1204,11 +1215,11 @@ namespace Ryujinx.Ava.UI.ViewModels _rendererWaitEvent.Set(); } -#endregion + #endregion -#region PublicMethods + #region PublicMethods - public void SetUIProgressHandlers(Switch emulationContext) + public void SetUiProgressHandlers(Switch emulationContext) { if (emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) { @@ -1222,17 +1233,17 @@ namespace Ryujinx.Ava.UI.ViewModels public void LoadConfigurableHotKeys() { - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey)) { ShowUiKey = new KeyGesture(showUiKey); } - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) { ScreenshotKey = new KeyGesture(screenshotKey); } - if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) + if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) { PauseKey = new KeyGesture(pauseKey); } @@ -1260,12 +1271,12 @@ namespace Ryujinx.Ava.UI.ViewModels public async void InstallFirmwareFromFile() { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { OpenFileDialog dialog = new() { AllowMultiple = false }; dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); + dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); string[] file = await dialog.ShowAsync(desktop.MainWindow); @@ -1278,7 +1289,7 @@ namespace Ryujinx.Ava.UI.ViewModels public async void InstallFirmwareFromFolder() { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { OpenFolderDialog dialog = new(); @@ -1327,7 +1338,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void ChangeLanguage(object languageCode) + public static void ChangeLanguage(object languageCode) { LocaleManager.Instance.LoadLanguage((string)languageCode); @@ -1342,6 +1353,7 @@ namespace Ryujinx.Ava.UI.ViewModels { _ = fileType switch { +#pragma warning disable IDE0055 // Disable formatting "NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP, "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0, "XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI, @@ -1349,6 +1361,7 @@ namespace Ryujinx.Ava.UI.ViewModels "NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO, "NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO, _ => throw new ArgumentOutOfRangeException(fileType), +#pragma warning restore IDE0055 }; ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); @@ -1371,9 +1384,9 @@ namespace Ryujinx.Ava.UI.ViewModels { Applications.Clear(); - StatusBarVisible = true; + StatusBarVisible = true; StatusBarProgressMaximum = 0; - StatusBarProgressValue = 0; + StatusBarProgressValue = 0; LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); }); @@ -1383,11 +1396,11 @@ namespace Ryujinx.Ava.UI.ViewModels public async void OpenFile() { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { OpenFileDialog dialog = new() { - Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle] + Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle], }; dialog.Filters.Add(new FileDialogFilter @@ -1400,16 +1413,18 @@ namespace Ryujinx.Ava.UI.ViewModels "xci", "nca", "nro", - "nso" - } + "nso", + }, }); +#pragma warning disable IDE0055 // Disable formatting dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } }); dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); +#pragma warning restore IDE0055 string[] files = await dialog.ShowAsync(desktop.MainWindow); @@ -1422,11 +1437,11 @@ namespace Ryujinx.Ava.UI.ViewModels public async void OpenFolder() { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { OpenFolderDialog dialog = new() { - Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle] + Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle], }; string folder = await dialog.ShowAsync(desktop.MainWindow); @@ -1458,10 +1473,7 @@ namespace Ryujinx.Ava.UI.ViewModels Logger.RestartTime(); - if (SelectedIcon == null) - { - SelectedIcon = ApplicationLibrary.GetApplicationIcon(path); - } + SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path); PrepareLoadScreen(); @@ -1495,7 +1507,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (string.IsNullOrWhiteSpace(titleName)) { LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name); - TitleName = AppHost.Device.Processes.ActiveApplication.Name; + TitleName = AppHost.Device.Processes.ActiveApplication.Name; } SwitchToRenderer(startFullscreen); @@ -1521,7 +1533,7 @@ namespace Ryujinx.Ava.UI.ViewModels }); } - public void UpdateGameMetadata(string titleId) + public static void UpdateGameMetadata(string titleId) { ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { @@ -1675,6 +1687,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } -#endregion + #endregion } } diff --git a/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs index d57ccc6bb..0b12a51f6 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs @@ -90,4 +90,4 @@ namespace Ryujinx.Ava.UI.ViewModels } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs index 2d53968f7..49de19937 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs @@ -24,4 +24,4 @@ namespace Ryujinx.Ava.UI.ViewModels } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 0a2ffae3b..a9eb9c618 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -18,7 +18,6 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration.System; -using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -248,7 +247,7 @@ namespace Ryujinx.Ava.UI.ViewModels public AvaloniaList<string> NetworkInterfaceList { - get => new AvaloniaList<string>(_networkInterfaces.Keys); + get => new(_networkInterfaces.Keys); } public KeyboardHotkeys KeyboardHotkeys @@ -561,7 +560,7 @@ namespace Ryujinx.Ava.UI.ViewModels _directoryChanged = false; } - public void RevertIfNotSaved() + private static void RevertIfNotSaved() { Program.ReloadConfig(); } @@ -583,4 +582,4 @@ namespace Ryujinx.Ava.UI.ViewModels CloseWindow?.Invoke(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index 1f4e3c624..740d888bb 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; @@ -16,7 +17,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; using Ryujinx.Ui.App.Common; using System; using System.Collections.Generic; @@ -29,17 +29,16 @@ namespace Ryujinx.Ava.UI.ViewModels { public class TitleUpdateViewModel : BaseModel { - public TitleUpdateMetadata _titleUpdateWindowData; - public readonly string _titleUpdateJsonPath; - private VirtualFileSystem _virtualFileSystem { get; } - private ulong _titleId { get; } - private string _titleName { get; } + public TitleUpdateMetadata TitleUpdateWindowData; + public readonly string TitleUpdateJsonPath; + private VirtualFileSystem VirtualFileSystem { get; } + private ulong TitleId { get; } private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); private AvaloniaList<object> _views = new(); private object _selectedUpdate; - private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public AvaloniaList<TitleUpdateModel> TitleUpdates { @@ -71,27 +70,26 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId) { - _virtualFileSystem = virtualFileSystem; + VirtualFileSystem = virtualFileSystem; - _titleId = titleId; - _titleName = titleName; + TitleId = titleId; - _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); + TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); try { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata); + TitleUpdateWindowData = JsonHelper.DeserializeFromFile(TitleUpdateJsonPath, _serializerContext.TitleUpdateMetadata); } catch { - Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}"); - _titleUpdateWindowData = new TitleUpdateMetadata + TitleUpdateWindowData = new TitleUpdateMetadata { Selected = "", - Paths = new List<string>() + Paths = new List<string>(), }; Save(); @@ -102,12 +100,12 @@ namespace Ryujinx.Ava.UI.ViewModels private void LoadUpdates() { - foreach (string path in _titleUpdateWindowData.Paths) + foreach (string path in TitleUpdateWindowData.Paths) { AddUpdate(path); } - TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); + TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == TitleUpdateWindowData.Selected, null); SelectedUpdate = selected; @@ -126,7 +124,8 @@ namespace Ryujinx.Ava.UI.ViewModels { return -1; } - else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) + + if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) { return 1; } @@ -163,7 +162,7 @@ namespace Ryujinx.Ava.UI.ViewModels try { - (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, new PartitionFileSystem(file.AsStorage()), TitleId.ToString("x16"), 0); if (controlNca != null && patchNca != null) { @@ -205,17 +204,17 @@ namespace Ryujinx.Ava.UI.ViewModels { OpenFileDialog dialog = new() { - Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], - AllowMultiple = true + Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], + AllowMultiple = true, }; dialog.Filters.Add(new FileDialogFilter { - Name = "NSP", - Extensions = { "nsp" } + Name = "NSP", + Extensions = { "nsp" }, }); - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { string[] files = await dialog.ShowAsync(desktop.MainWindow); @@ -233,20 +232,20 @@ namespace Ryujinx.Ava.UI.ViewModels public void Save() { - _titleUpdateWindowData.Paths.Clear(); - _titleUpdateWindowData.Selected = ""; + TitleUpdateWindowData.Paths.Clear(); + TitleUpdateWindowData.Selected = ""; foreach (TitleUpdateModel update in TitleUpdates) { - _titleUpdateWindowData.Paths.Add(update.Path); + TitleUpdateWindowData.Paths.Add(update.Path); if (update == SelectedUpdate) { - _titleUpdateWindowData.Selected = update.Path; + TitleUpdateWindowData.Selected = update.Path; } } - JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); + JsonHelper.SerializeToFile(TitleUpdateJsonPath, TitleUpdateWindowData, _serializerContext.TitleUpdateMetadata); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index 558cad5a7..cb8da9ede 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -28,7 +28,6 @@ namespace Ryujinx.Ava.UI.ViewModels private Color _backgroundColor = Colors.White; private int _selectedIndex; - private byte[] _selectedImage; public UserFirmwareAvatarSelectorViewModel() { @@ -78,11 +77,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public byte[] SelectedImage - { - get => _selectedImage; - private set => _selectedImage = value; - } + public byte[] SelectedImage { get; private set; } private void LoadImagesFromStore() { @@ -114,34 +109,32 @@ namespace Ryujinx.Ava.UI.ViewModels if (!string.IsNullOrWhiteSpace(avatarPath)) { - using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open)) + using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open); + + Nca nca = new(virtualFileSystem.KeySet, ncaFileStream); + IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + foreach (DirectoryEntryEx item in romfs.EnumerateEntries()) { - Nca nca = new(virtualFileSystem.KeySet, ncaFileStream); - IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - foreach (DirectoryEntryEx item in romfs.EnumerateEntries()) + // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. + if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) { - // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. - if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) - { - using var file = new UniqueRef<IFile>(); + using var file = new UniqueRef<IFile>(); - romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); + romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); - using (MemoryStream stream = new()) - using (MemoryStream streamPng = new()) - { - file.Get.AsStream().CopyTo(stream); + using MemoryStream stream = new(); + using MemoryStream streamPng = new(); - stream.Position = 0; + file.Get.AsStream().CopyTo(stream); - Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); + stream.Position = 0; - avatarImage.SaveAsPng(streamPng); + Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); - _avatarStore.Add(item.FullPath, streamPng.ToArray()); - } - } + avatarImage.SaveAsPng(streamPng); + + _avatarStore.Add(item.FullPath, streamPng.ToArray()); } } } @@ -149,82 +142,81 @@ namespace Ryujinx.Ava.UI.ViewModels private static byte[] DecompressYaz0(Stream stream) { - using (BinaryReader reader = new(stream)) + using BinaryReader reader = new(stream); + + reader.ReadInt32(); // Magic + + uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); + + reader.ReadInt64(); // Padding + + byte[] input = new byte[stream.Length - stream.Position]; + stream.Read(input, 0, input.Length); + + uint inputOffset = 0; + + byte[] output = new byte[decodedLength]; + uint outputOffset = 0; + + ushort mask = 0; + byte header = 0; + + while (outputOffset < decodedLength) { - reader.ReadInt32(); // Magic - - uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); - - reader.ReadInt64(); // Padding - - byte[] input = new byte[stream.Length - stream.Position]; - stream.Read(input, 0, input.Length); - - uint inputOffset = 0; - - byte[] output = new byte[decodedLength]; - uint outputOffset = 0; - - ushort mask = 0; - byte header = 0; - - while (outputOffset < decodedLength) + if ((mask >>= 1) == 0) { - if ((mask >>= 1) == 0) + header = input[inputOffset++]; + mask = 0x80; + } + + if ((header & mask) != 0) + { + if (outputOffset == output.Length) { - header = input[inputOffset++]; - mask = 0x80; + break; } - if ((header & mask) != 0) - { - if (outputOffset == output.Length) - { - break; - } + output[outputOffset++] = input[inputOffset++]; + } + else + { + byte byte1 = input[inputOffset++]; + byte byte2 = input[inputOffset++]; - output[outputOffset++] = input[inputOffset++]; + uint dist = (uint)((byte1 & 0xF) << 8) | byte2; + uint position = outputOffset - (dist + 1); + + uint length = (uint)byte1 >> 4; + if (length == 0) + { + length = (uint)input[inputOffset++] + 0x12; } else { - byte byte1 = input[inputOffset++]; - byte byte2 = input[inputOffset++]; + length += 2; + } - uint dist = (uint)((byte1 & 0xF) << 8) | byte2; - uint position = outputOffset - (dist + 1); + uint gap = outputOffset - position; + uint nonOverlappingLength = length; - uint length = (uint)byte1 >> 4; - if (length == 0) - { - length = (uint)input[inputOffset++] + 0x12; - } - else - { - length += 2; - } + if (nonOverlappingLength > gap) + { + nonOverlappingLength = gap; + } - uint gap = outputOffset - position; - uint nonOverlappingLength = length; + Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength); + outputOffset += nonOverlappingLength; + position += nonOverlappingLength; + length -= nonOverlappingLength; - if (nonOverlappingLength > gap) - { - nonOverlappingLength = gap; - } - - Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength); - outputOffset += nonOverlappingLength; - position += nonOverlappingLength; - length -= nonOverlappingLength; - - while (length-- > 0) - { - output[outputOffset++] = output[position++]; - } + while (length-- > 0) + { + output[outputOffset++] = output[position++]; } } - - return output; } + + return output; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs index 7261631c1..8e7d41a55 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool FirmwareFound { get => _firmwareFound; - + set { _firmwareFound = value; @@ -15,4 +15,4 @@ namespace Ryujinx.Ava.UI.ViewModels } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs index 8f997efc1..70274847f 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs @@ -1,7 +1,7 @@ using Microsoft.IdentityModel.Tokens; +using Ryujinx.Ava.UI.Models; using System; using System.Collections.ObjectModel; -using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.ViewModels { @@ -20,6 +20,9 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsEmpty { get; set; } - public void Dispose() { } + public void Dispose() + { + GC.SuppressFinalize(this); + } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs index 097634a80..0a13f24a8 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Ava.UI.ViewModels private string _search; private ObservableCollection<SaveModel> _saves = new(); private ObservableCollection<SaveModel> _views = new(); - private AccountManager _accountManager; + private readonly AccountManager _accountManager; public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); @@ -102,19 +102,16 @@ namespace Ryujinx.Ava.UI.ViewModels private IComparer<SaveModel> GetComparer() { - switch (SortIndex) + return SortIndex switch { - case 0: - return OrderIndex == 0 - ? SortExpressionComparer<SaveModel>.Ascending(save => save.Title) - : SortExpressionComparer<SaveModel>.Descending(save => save.Title); - case 1: - return OrderIndex == 0 - ? SortExpressionComparer<SaveModel>.Ascending(save => save.Size) - : SortExpressionComparer<SaveModel>.Descending(save => save.Size); - default: - return null; - } + 0 => OrderIndex == 0 + ? SortExpressionComparer<SaveModel>.Ascending(save => save.Title) + : SortExpressionComparer<SaveModel>.Descending(save => save.Title), + 1 => OrderIndex == 0 + ? SortExpressionComparer<SaveModel>.Ascending(save => save.Size) + : SortExpressionComparer<SaveModel>.Descending(save => save.Size), + _ => null, + }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs index 8fe7b9412..19009f5f2 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Input foreach (ILogical visual in SettingButtons.GetLogicalDescendants()) { - if (visual is ToggleButton button && !(visual is CheckBox)) + if (visual is ToggleButton button && visual is not CheckBox) { button.Checked += Button_Checked; button.Unchecked += Button_Unchecked; @@ -177,4 +177,4 @@ namespace Ryujinx.Ava.UI.Views.Input ViewModel.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs index 88bbcd932..1b340752b 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Views.Input { public partial class MotionInputView : UserControl { - private MotionInputViewModel _viewModel; + private readonly MotionInputViewModel _viewModel; public MotionInputView() { @@ -30,7 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Input MirrorInput = config.MirrorInput, Sensitivity = config.Sensitivity, GyroDeadzone = config.GyroDeadzone, - EnableCemuHookMotion = config.EnableCemuHookMotion + EnableCemuHookMotion = config.EnableCemuHookMotion, }; InitializeComponent(); @@ -47,7 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Input PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave], SecondaryButtonText = "", CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], - Content = content + Content = content, }; contentDialog.PrimaryButtonClick += (sender, args) => { @@ -65,4 +65,4 @@ namespace Ryujinx.Ava.UI.Views.Input await contentDialog.ShowAsync(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs index dfe05b08b..9307f872c 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Views.Input { public partial class RumbleInputView : UserControl { - private RumbleInputViewModel _viewModel; + private readonly RumbleInputViewModel _viewModel; public RumbleInputView() { @@ -24,11 +24,11 @@ namespace Ryujinx.Ava.UI.Views.Input _viewModel = new RumbleInputViewModel { StrongRumble = config.StrongRumble, - WeakRumble = config.WeakRumble + WeakRumble = config.WeakRumble, }; InitializeComponent(); - + DataContext = _viewModel; } @@ -55,4 +55,4 @@ namespace Ryujinx.Ava.UI.Views.Input await contentDialog.ShowAsync(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index f94b80d45..ae52f0719 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -38,26 +38,26 @@ namespace Ryujinx.Ava.UI.Views.Main { List<CheckBox> checkBoxes = new(); - foreach (var item in Enum.GetValues(typeof (FileTypes))) + foreach (var item in Enum.GetValues(typeof(FileTypes))) { - string fileName = Enum.GetName(typeof (FileTypes), item); - checkBoxes.Add(new CheckBox() + string fileName = Enum.GetName(typeof(FileTypes), item); + checkBoxes.Add(new CheckBox { Content = $".{fileName}", IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes), - Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)) + Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)), }); } return checkBoxes.ToArray(); } - private MenuItem[] GenerateLanguageMenuItems() + private static MenuItem[] GenerateLanguageMenuItems() { List<MenuItem> menuItems = new(); string localePath = "Ryujinx.Ava/Assets/Locales"; - string localeExt = ".json"; + string localeExt = ".json"; string[] localesPath = EmbeddedResources.GetAllAvailableResources(localePath, localeExt); @@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Views.Main { string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last(); string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}"); - var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); + var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); if (!strings.TryGetValue("Language", out string languageName)) { @@ -76,11 +76,11 @@ namespace Ryujinx.Ava.UI.Views.Main MenuItem menuItem = new() { - Header = languageName, + Header = languageName, Command = MiniCommand.Create(() => { - ViewModel.ChangeLanguage(languageCode); - }) + MainWindowViewModel.ChangeLanguage(languageCode); + }), }; menuItems.Add(menuItem); @@ -236,4 +236,4 @@ namespace Ryujinx.Ava.UI.Views.Main Window.Close(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs index 473de0ee2..0640869c1 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs @@ -49,4 +49,4 @@ namespace Ryujinx.Ava.UI.Views.Main ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs index fd7578939..9163222f4 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Ava.UI.Views.Main public partial class MainViewControls : UserControl { public MainWindowViewModel ViewModel; - + public MainViewControls() { InitializeComponent(); @@ -37,7 +37,7 @@ namespace Ryujinx.Ava.UI.Views.Main ViewModel.Sort(Enum.Parse<ApplicationSort>(button.Tag.ToString())); } } - + public void Order_Checked(object sender, RoutedEventArgs args) { if (sender is RadioButton button) @@ -45,10 +45,10 @@ namespace Ryujinx.Ava.UI.Views.Main ViewModel.Sort(button.Tag.ToString() != "Descending"); } } - + private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) { ViewModel.SearchText = SearchBox.Text; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs index 026c7fdf8..f9f93ba3c 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Ava.UI.Views.Settings InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs index 5c5b60793..47a640e08 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Ava.UI.Views.Settings InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs index 8fe08552e..56b966523 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Ava.UI.Views.Settings InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs index 702f73e87..4bbd0133a 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Ava.UI.Views.Settings public partial class SettingsHotkeysView : UserControl { private ButtonKeyAssigner _currentAssigner; - private IGamepadDriver AvaloniaKeyboardDriver; - + private readonly IGamepadDriver _avaloniaKeyboardDriver; + public SettingsHotkeysView() { InitializeComponent(); - AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this); + _avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this); } private void MouseClick(object sender, PointerPressedEventArgs e) @@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Views.Settings PointerPressed -= MouseClick; } - + private void Button_Checked(object sender, RoutedEventArgs e) { if (sender is ToggleButton button) @@ -46,7 +46,7 @@ namespace Ryujinx.Ava.UI.Views.Settings PointerPressed += MouseClick; - var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]); + var keyboard = (IKeyboard)_avaloniaKeyboardDriver.GetGamepad(_avaloniaKeyboardDriver.GamepadsIds[0]); IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard); _currentAssigner.GetInputAndAssign(assigner); @@ -78,4 +78,4 @@ namespace Ryujinx.Ava.UI.Views.Settings _currentAssigner = null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs index 0c2105ec4..8a0cb8ab9 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs @@ -14,4 +14,4 @@ namespace Ryujinx.Ava.UI.Views.Settings ControllerSettings.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs index 2ec476ac6..f18f73390 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Ava.UI.Views.Settings InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs index d7407b9d5..bf728f410 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Ava.UI.Views.Settings InitializeComponent(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs index d2ea59deb..4acf2f44c 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Ava.UI.Views.Settings public SettingsSystemView() { InitializeComponent(); - + FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim()); MultiBinding tzMultiBinding = new() { Converter = converter }; @@ -49,4 +49,4 @@ namespace Ryujinx.Ava.UI.Views.Settings } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml index acc5e2b70..c92d56728 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml @@ -1,5 +1,5 @@ <UserControl - x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUIView" + x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUiView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs index 8e6da7c54..132435e04 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls; +using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Interactivity; using Ryujinx.Ava.Common.Locale; @@ -9,11 +10,11 @@ using System.Linq; namespace Ryujinx.Ava.UI.Views.Settings { - public partial class SettingsUIView : UserControl + public partial class SettingsUiView : UserControl { public SettingsViewModel ViewModel; - public SettingsUIView() + public SettingsUiView() { InitializeComponent(); } @@ -29,7 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Settings } else { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow); @@ -60,15 +61,15 @@ namespace Ryujinx.Ava.UI.Views.Settings public async void BrowseTheme(object sender, RoutedEventArgs e) { - var dialog = new OpenFileDialog() + var dialog = new OpenFileDialog { Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle], - AllowMultiple = false + AllowMultiple = false, }; - dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] }); + dialog.Filters.Add(new FileDialogFilter { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] }); - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { var file = await dialog.ShowAsync(desktop.MainWindow); @@ -79,4 +80,4 @@ namespace Ryujinx.Ava.UI.Views.Settings } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs index 81938d23b..588fa471e 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Views.User private bool _isNewUser; public TempProfile TempProfile { get; set; } - public uint MaxProfileNameLength => 0x20; + public static uint MaxProfileNameLength => 0x20; public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId; public UserEditorView() @@ -39,17 +39,17 @@ namespace Ryujinx.Ava.UI.Views.User switch (arg.NavigationMode) { case NavigationMode.New: - var args = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter; - _isNewUser = args.isNewUser; - _profile = args.profile; + var (parent, profile, isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter; + _isNewUser = isNewUser; + _profile = profile; TempProfile = new TempProfile(_profile); - _parent = args.parent; + _parent = parent; break; } ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " + - $"{ (_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}"; + $"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}"; DataContext = TempProfile; @@ -162,4 +162,4 @@ namespace Ryujinx.Ava.UI.Views.User } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs index 7c9191ab2..b6376866d 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs @@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using System.IO; +using Image = SixLabors.ImageSharp.Image; namespace Ryujinx.Ava.UI.Views.User { @@ -70,7 +71,7 @@ namespace Ryujinx.Ava.UI.Views.User if (ViewModel.SelectedImage != null) { MemoryStream streamJpg = new(); - SixLabors.ImageSharp.Image avatarImage = SixLabors.ImageSharp.Image.Load(ViewModel.SelectedImage, new PngDecoder()); + Image avatarImage = Image.Load(ViewModel.SelectedImage, new PngDecoder()); avatarImage.Mutate(x => x.BackgroundColor(new Rgba32( ViewModel.BackgroundColor.R, @@ -85,4 +86,4 @@ namespace Ryujinx.Ava.UI.Views.User } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs index 18f76f805..26b77dcdc 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs @@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Views.User dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats], - Extensions = { "jpg", "jpeg", "png", "bmp" } + Extensions = { "jpg", "jpeg", "png", "bmp" }, }); dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } }); dialog.Filters.Add(new FileDialogFilter { Name = "PNG", Extensions = { "png" } }); @@ -108,17 +108,15 @@ namespace Ryujinx.Ava.UI.Views.User private static byte[] ProcessProfileImage(byte[] buffer) { - using (Image image = Image.Load(buffer)) - { - image.Mutate(x => x.Resize(256, 256)); + using Image image = Image.Load(buffer); - using (MemoryStream streamJpg = new()) - { - image.SaveAsJpeg(streamJpg); + image.Mutate(x => x.Resize(256, 256)); - return streamJpg.ToArray(); - } - } + using MemoryStream streamJpg = new(); + + image.SaveAsJpeg(streamJpg); + + return streamJpg.ToArray(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs index 0c53e53d7..31934349d 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs @@ -48,4 +48,4 @@ namespace Ryujinx.Ava.UI.Views.User _parent?.RecoverLostAccounts(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs index 08fef27d0..00a229fae 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs @@ -18,6 +18,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc; using System; using System.Collections.ObjectModel; using System.Threading.Tasks; +using Button = Avalonia.Controls.Button; using UserId = LibHac.Fs.UserId; namespace Ryujinx.Ava.UI.Views.User @@ -47,12 +48,12 @@ namespace Ryujinx.Ava.UI.Views.User switch (arg.NavigationMode) { case NavigationMode.New: - var args = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter; - _accountManager = args.accountManager; - _horizonClient = args.client; - _virtualFileSystem = args.virtualFileSystem; + var (parent, accountManager, client, virtualFileSystem) = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter; + _accountManager = accountManager; + _horizonClient = client; + _virtualFileSystem = virtualFileSystem; - _parent = args.parent; + _parent = parent; break; } @@ -94,7 +95,7 @@ namespace Ryujinx.Ava.UI.Views.User var save = saveDataInfo[i]; if (save.ProgramId.Value != 0) { - var saveModel = new SaveModel(save, _virtualFileSystem); + var saveModel = new SaveModel(save); saves.Add(saveModel); } } @@ -114,7 +115,7 @@ namespace Ryujinx.Ava.UI.Views.User private void OpenLocation(object sender, RoutedEventArgs e) { - if (sender is Avalonia.Controls.Button button) + if (sender is Button button) { if (button.DataContext is SaveModel saveModel) { @@ -125,7 +126,7 @@ namespace Ryujinx.Ava.UI.Views.User private async void Delete(object sender, RoutedEventArgs e) { - if (sender is Avalonia.Controls.Button button) + if (sender is Button button) { if (button.DataContext is SaveModel saveModel) { @@ -144,4 +145,4 @@ namespace Ryujinx.Ava.UI.Views.User } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs index aa89fea9e..fa3383aa9 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs @@ -5,8 +5,9 @@ using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; +using Button = Avalonia.Controls.Button; namespace Ryujinx.Ava.UI.Views.User { @@ -101,7 +102,7 @@ namespace Ryujinx.Ava.UI.Views.User private void EditUser(object sender, RoutedEventArgs e) { - if (sender is Avalonia.Controls.Button button) + if (sender is Button button) { if (button.DataContext is UserProfile userProfile) { @@ -125,4 +126,4 @@ namespace Ryujinx.Ava.UI.Views.User ((ContentDialog)_parent.Parent).Hide(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs index 36a28605f..ca1c40350 100644 --- a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs @@ -1,6 +1,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Layout; using Avalonia.Styling; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; @@ -25,17 +26,17 @@ namespace Ryujinx.Ava.UI.Windows { ContentDialog contentDialog = new() { - PrimaryButtonText = "", + PrimaryButtonText = "", SecondaryButtonText = "", - CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], - Content = new AboutWindow() + CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], + Content = new AboutWindow(), }; 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)); + closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Right)); contentDialog.Styles.Add(closeButton); contentDialog.Styles.Add(closeButtonParent); @@ -59,4 +60,4 @@ namespace Ryujinx.Ava.UI.Windows } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs index 206d0a7ea..57106638a 100644 --- a/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs @@ -9,9 +9,10 @@ namespace Ryujinx.Ava.UI.Windows { public AmiiboWindow(bool showAll, string lastScannedAmiiboId, string titleId) { - ViewModel = new AmiiboWindowViewModel(this, lastScannedAmiiboId, titleId); - - ViewModel.ShowAllAmiibo = showAll; + ViewModel = new AmiiboWindowViewModel(this, lastScannedAmiiboId, titleId) + { + ShowAllAmiibo = showAll, + }; DataContext = ViewModel; @@ -56,4 +57,4 @@ namespace Ryujinx.Ava.UI.Windows Close(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index f5bba7d2d..df6b123bd 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -1,11 +1,12 @@ -using Avalonia; -using Avalonia.Collections; +using Avalonia.Collections; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Models; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.Ui.App.Common; +using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; @@ -36,16 +37,16 @@ namespace Ryujinx.Ava.UI.Windows Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper()); BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath); - + InitializeComponent(); string modsBasePath = ModLoader.GetModsBasePath(); string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId); - ulong titleIdValue = ulong.Parse(titleId, System.Globalization.NumberStyles.HexNumber); + ulong titleIdValue = ulong.Parse(titleId, NumberStyles.HexNumber); _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); - string[] enabled = { }; + string[] enabled = Array.Empty<string>(); if (File.Exists(_enabledCheatsPath)) { @@ -60,7 +61,6 @@ namespace Ryujinx.Ava.UI.Windows string currentCheatFile = string.Empty; string buildId = string.Empty; - string parentPath = string.Empty; CheatsList currentGroup = null; @@ -69,7 +69,7 @@ namespace Ryujinx.Ava.UI.Windows if (cheat.Path.FullName != currentCheatFile) { currentCheatFile = cheat.Path.FullName; - parentPath = currentCheatFile.Replace(titleModsPath, ""); + string parentPath = currentCheatFile.Replace(titleModsPath, ""); buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper(); currentGroup = new CheatsList(buildId, parentPath); @@ -89,7 +89,7 @@ namespace Ryujinx.Ava.UI.Windows } DataContext = this; - + Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle]; } @@ -100,7 +100,7 @@ namespace Ryujinx.Ava.UI.Windows return; } - List<string> enabledCheats = new List<string>(); + List<string> enabledCheats = new(); foreach (var cheats in LoadedCheats) { @@ -120,4 +120,4 @@ namespace Ryujinx.Ava.UI.Windows Close(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs index 3f77124d9..a33ee518b 100644 --- a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs @@ -1,7 +1,8 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Avalonia.Media; +#if DEBUG +using Avalonia; +#endif namespace Ryujinx.Ava.UI.Windows { @@ -22,4 +23,4 @@ namespace Ryujinx.Ava.UI.Windows CanResize = false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml index fe446fb3e..f4ba4f9c9 100644 --- a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml @@ -89,8 +89,8 @@ <Grid Grid.Column="0"> <Grid.ColumnDefinitions> - <ColumnDefinition Width="*"></ColumnDefinition> - <ColumnDefinition Width="Auto"></ColumnDefinition> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" diff --git a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs index 6dc99fb54..dfe8807be 100644 --- a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs @@ -24,9 +24,9 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); } - public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId) { - DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId, titleName); + DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId); InitializeComponent(); } @@ -35,11 +35,11 @@ namespace Ryujinx.Ava.UI.Windows { ContentDialog contentDialog = new() { - PrimaryButtonText = "", + PrimaryButtonText = "", SecondaryButtonText = "", - CloseButtonText = "", - Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId, titleName), - Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")) + CloseButtonText = "", + Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId), + Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")), }; Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); @@ -112,4 +112,4 @@ namespace Ryujinx.Ava.UI.Windows } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs b/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs index a4c6287f3..4c75a5ff9 100644 --- a/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs +++ b/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs @@ -68,8 +68,10 @@ namespace Ryujinx.Ava.UI.Windows int w8 = w << 8; int h8 = image.Height << 8; +#pragma warning disable IDE0059 // Unnecessary assignment int xStep = w8 / ColorsPerLine; int yStep = h8 / ColorsPerLine; +#pragma warning restore IDE0059 int i = 0; int maxHitCount = 0; diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index 66988c4b3..d79bdc56a 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -155,7 +155,7 @@ namespace Ryujinx.Ava.UI.Windows Dispatcher.UIThread.Post(() => { - ViewModel.StatusBarProgressValue = e.NumAppsLoaded; + ViewModel.StatusBarProgressValue = e.NumAppsLoaded; ViewModel.StatusBarProgressMaximum = e.NumAppsFound; if (e.NumAppsFound == 0) @@ -323,7 +323,7 @@ namespace Ryujinx.Ava.UI.Windows ShowKeyErrorOnLoad = false; Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, this)); + UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys)); } if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount) @@ -373,24 +373,26 @@ namespace Ryujinx.Ava.UI.Windows private void SetWindowSizePosition() { - PixelPoint SavedPoint = new PixelPoint(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, + PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor; ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor; ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal; - - if (CheckScreenBounds(SavedPoint)) - { - Position = SavedPoint; - } - else WindowStartupLocation = WindowStartupLocation.CenterScreen; + if (CheckScreenBounds(savedPoint)) + { + Position = savedPoint; + } + else + { + WindowStartupLocation = WindowStartupLocation.CenterScreen; + } } private bool CheckScreenBounds(PixelPoint configPoint) - { + { for (int i = 0; i < Screens.ScreenCount; i++) { if (Screens.All[i].Bounds.Contains(configPoint)) @@ -399,7 +401,7 @@ namespace Ryujinx.Ava.UI.Windows } } - Logger.Warning?.Print(LogClass.Application, $"Failed to find valid start-up coordinates. Defaulting to primary monitor center."); + Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center."); return false; } @@ -425,10 +427,7 @@ namespace Ryujinx.Ava.UI.Windows private void SetMainContent(Control content = null) { - if (content == null) - { - content = GameLibrary; - } + content ??= GameLibrary; if (MainContent.Content != content) { @@ -438,21 +437,25 @@ namespace Ryujinx.Ava.UI.Windows public static void UpdateGraphicsConfig() { +#pragma warning disable IDE0055 // Disable formatting GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale; GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; +#pragma warning restore IDE0055 } public void LoadHotKeys() { +#pragma warning disable IDE0055 // Disable formatting HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11)); HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta)); HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); +#pragma warning restore IDE0055 } private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e) @@ -536,8 +539,8 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.Applications.Clear(); StatusBarView.LoadProgressBar.IsVisible = true; - ViewModel.StatusBarProgressMaximum = 0; - ViewModel.StatusBarProgressValue = 0; + ViewModel.StatusBarProgressMaximum = 0; + ViewModel.StatusBarProgressValue = 0; LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); }); @@ -559,4 +562,4 @@ namespace Ryujinx.Ava.UI.Windows _isLoading = false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml index a44cbfe7d..4b248db79 100644 --- a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml @@ -34,7 +34,7 @@ IsVisible="False" KeyboardNavigation.IsTabStop="False"/> <Grid Name="Pages" IsVisible="False" Grid.Row="2"> - <settings:SettingsUIView Name="UiPage" /> + <settings:SettingsUiView Name="UiPage" /> <settings:SettingsInputView Name="InputPage" /> <settings:SettingsHotkeysView Name="HotkeysPage" /> <settings:SettingsSystemView Name="SystemPage" /> @@ -125,4 +125,4 @@ Command="{ReflectionBinding ApplyButton}" /> </ReversibleStackPanel> </Grid> -</window:StyleableWindow> \ No newline at end of file +</window:StyleableWindow> diff --git a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs index bdf7e94d8..518c2d328 100644 --- a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Ava.UI.Windows { Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.Settings]}"; - ViewModel = new SettingsViewModel(virtualFileSystem, contentManager); + ViewModel = new SettingsViewModel(virtualFileSystem, contentManager); DataContext = ViewModel; ViewModel.CloseWindow += Close; @@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Windows public SettingsWindow() { - ViewModel = new SettingsViewModel(); + ViewModel = new SettingsViewModel(); DataContext = ViewModel; InitializeComponent(); @@ -100,4 +100,4 @@ namespace Ryujinx.Ava.UI.Windows base.OnClosing(e); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs b/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs index a157f1547..ca23a5ba3 100644 --- a/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs +++ b/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs @@ -2,6 +2,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Media.Imaging; using Avalonia.Platform; +using Ryujinx.Ui.Common.Configuration; using System; using System.IO; using System.Reflection; @@ -17,7 +18,7 @@ namespace Ryujinx.Ava.UI.Windows WindowStartupLocation = WindowStartupLocation.CenterOwner; TransparencyLevelHint = WindowTransparencyLevel.None; - using Stream stream = Assembly.GetAssembly(typeof(Ryujinx.Ui.Common.Configuration.ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); + using Stream stream = Assembly.GetAssembly(typeof(ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Icon = new WindowIcon(stream); stream.Position = 0; @@ -36,4 +37,4 @@ namespace Ryujinx.Ava.UI.Windows ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome | ExtendClientAreaChromeHints.OSXThickTitleBar; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 153ce95d2..f4fcad319 100644 --- a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -24,9 +24,9 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); } - public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId) { - DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId, titleName); + DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId); InitializeComponent(); } @@ -35,11 +35,11 @@ namespace Ryujinx.Ava.UI.Windows { ContentDialog contentDialog = new() { - PrimaryButtonText = "", + PrimaryButtonText = "", SecondaryButtonText = "", - CloseButtonText = "", - Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName), - Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")) + CloseButtonText = "", + Content = new TitleUpdateWindow(virtualFileSystem, titleId), + Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")), }; Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); @@ -93,4 +93,4 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.SortUpdates(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index e21c28145..83604b420 100644 --- a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -21,7 +21,6 @@ using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using Path = System.IO.Path; -using RightsId = LibHac.Fs.RightsId; namespace Ryujinx.HLE.FileSystem { From 7805d27e675c9c8c620bdd327810b12c644708e9 Mon Sep 17 00:00:00 2001 From: gleng <906346+gleng@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:00:19 -0700 Subject: [PATCH 701/737] MacOS: Fix rendering on AMD GPUs (#5446) * MacOS: Fix rendering on AMD GPUs * Only disable MultiViewPort on MoltenVK for AMD GPUs --- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 8f0c73061..d0bd1b2cc 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -315,7 +315,7 @@ namespace Ryujinx.Graphics.Vulkan features2.Features.ShaderStorageImageMultisample, _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), - features2.Features.MultiViewport, + features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue featuresRobustness2.NullDescriptor || IsMoltenVk, _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName), featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, From fa32ef92755a51a2567a1bcbb35fb34886b5f979 Mon Sep 17 00:00:00 2001 From: gleng <906346+gleng@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:10:23 -0700 Subject: [PATCH 702/737] MacOS: Allow barriers inside a render pass for non-Apple GPUs and don't treat as TBDR (#5440) * MoltenVK: Allow barriers inside a render pass on non-Apple GPUs * Don't treat all non-Apple GPUs using MoltenVK as TBDR --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 ++-- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 5ee926911..7f175d93c 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -144,9 +144,9 @@ namespace Ryujinx.Graphics.Vulkan { _drawCountSinceBarrier = DrawCount; - // Barriers apparently have no effect inside a render pass on MoltenVK. + // Barriers are not supported inside a render pass on Apple GPUs. // As a workaround, end the render pass. - if (Gd.IsMoltenVk) + if (Gd.Vendor == Vendor.Apple) { EndRenderPass(); } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index d0bd1b2cc..b9b1ba911 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -680,7 +680,8 @@ namespace Ryujinx.Graphics.Vulkan IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows(); IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows(); - IsTBDR = IsMoltenVk || + IsTBDR = + Vendor == Vendor.Apple || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.Broadcom || From 9c6071a645e72b56e42cf687f9c1a182be2673ac Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Tue, 11 Jul 2023 14:07:41 -0300 Subject: [PATCH 703/737] Move support buffer update out of the backends (#5411) * Move support buffer update out of the backends * Fix render scale init and remove redundant state from SupportBufferUpdater * Stop passing texture scale to the backends * XML docs for SupportBufferUpdater --- src/Ryujinx.Graphics.GAL/IPipeline.cs | 5 +- src/Ryujinx.Graphics.GAL/IRenderer.cs | 5 +- src/Ryujinx.Graphics.GAL/ITexture.cs | 1 - .../Multithreading/CommandHelper.cs | 2 - .../Multithreading/CommandType.cs | 2 - .../Commands/Renderer/CreateTextureCommand.cs | 6 +- .../Commands/Renderer/ReportCounterCommand.cs | 6 +- .../Commands/SetRenderTargetScaleCommand.cs | 18 -- .../Commands/SetViewportsCommand.cs | 6 +- .../Commands/UpdateRenderScaleCommand.cs | 25 -- .../Resources/ThreadedCounterEvent.cs | 4 +- .../Resources/ThreadedTexture.cs | 7 +- .../Multithreading/ThreadedPipeline.cs | 16 +- .../Multithreading/ThreadedRenderer.cs | 16 +- .../SupportBufferUpdater.cs | 102 -------- .../Engine/Threed/SemaphoreUpdater.cs | 8 +- .../Engine/Threed/StateUpdater.cs | 17 +- src/Ryujinx.Graphics.Gpu/GpuContext.cs | 9 + src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 6 +- .../Image/TextureBindingsManager.cs | 2 +- .../Memory/BufferManager.cs | 4 + .../Memory/SupportBufferUpdater.cs | 232 ++++++++++++++++++ .../Shader/ShaderInfoBuilder.cs | 1 + src/Ryujinx.Graphics.Gpu/Window.cs | 11 +- .../Effects/FsrScalingFilter.cs | 2 +- .../Effects/FxaaPostProcessingEffect.cs | 2 +- .../Effects/SmaaPostProcessingEffect.cs | 10 +- .../Image/IntermmediatePool.cs | 2 +- .../Image/TextureBase.cs | 4 +- .../Image/TextureCopy.cs | 2 +- .../Image/TextureStorage.cs | 4 +- .../Image/TextureView.cs | 2 +- src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 9 +- src/Ryujinx.Graphics.OpenGL/Pipeline.cs | 88 +------ .../Queries/CounterQueue.cs | 4 +- .../Queries/Counters.cs | 4 +- src/Ryujinx.Graphics.OpenGL/ResourcePool.cs | 12 +- src/Ryujinx.Graphics.OpenGL/Window.cs | 15 +- .../DescriptorSetUpdater.cs | 39 +-- .../Effects/FsrScalingFilter.cs | 2 +- .../Effects/FxaaPostProcessingEffect.cs | 2 +- .../Effects/SmaaPostProcessingEffect.cs | 10 +- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 12 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 74 +----- .../Queries/CounterQueue.cs | 4 +- .../Queries/Counters.cs | 4 +- src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 5 +- src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 5 +- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 1 - src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 18 +- src/Ryujinx.Graphics.Vulkan/Window.cs | 13 +- 51 files changed, 364 insertions(+), 496 deletions(-) delete mode 100644 src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs delete mode 100644 src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs delete mode 100644 src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs create mode 100644 src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs index 0a362081c..f5978cefa 100644 --- a/src/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs @@ -79,7 +79,6 @@ namespace Ryujinx.Graphics.GAL void SetRasterizerDiscard(bool discard); - void SetRenderTargetScale(float scale); void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask); void SetRenderTargets(ITexture[] colors, ITexture depthStencil); @@ -99,7 +98,7 @@ namespace Ryujinx.Graphics.GAL void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs); void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers); - void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform); + void SetViewports(ReadOnlySpan<Viewport> viewports); void TextureBarrier(); void TextureBarrierTiled(); @@ -107,7 +106,5 @@ namespace Ryujinx.Graphics.GAL bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual); bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual); void EndHostConditionalRendering(); - - void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount); } } diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs index b668d56ec..1dabbdaef 100644 --- a/src/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -17,7 +17,6 @@ namespace Ryujinx.Graphics.GAL void BackgroundContextAction(Action action, bool alwaysBackground = false); BufferHandle CreateBuffer(int size, BufferHandle storageHint); - BufferHandle CreateBuffer(int size) { return CreateBuffer(size, BufferHandle.Null); @@ -28,7 +27,7 @@ namespace Ryujinx.Graphics.GAL IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info); ISampler CreateSampler(SamplerCreateInfo info); - ITexture CreateTexture(TextureCreateInfo info, float scale); + ITexture CreateTexture(TextureCreateInfo info); bool PrepareHostMapping(nint address, ulong size); void CreateSync(ulong id, bool strict); @@ -49,7 +48,7 @@ namespace Ryujinx.Graphics.GAL void PreFrame(); - ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved); + ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved); void ResetCounter(CounterType type); diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs index d39b59499..5a4623a66 100644 --- a/src/Ryujinx.Graphics.GAL/ITexture.cs +++ b/src/Ryujinx.Graphics.GAL/ITexture.cs @@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.GAL { int Width { get; } int Height { get; } - float ScaleFactor { get; } void CopyTo(ITexture destination, int firstLayer, int firstLevel); void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs index 12c5245a5..6cd6f1599 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -125,7 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register<SetProgramCommand>(CommandType.SetProgram); Register<SetRasterizerDiscardCommand>(CommandType.SetRasterizerDiscard); Register<SetRenderTargetColorMasksCommand>(CommandType.SetRenderTargetColorMasks); - Register<SetRenderTargetScaleCommand>(CommandType.SetRenderTargetScale); Register<SetRenderTargetsCommand>(CommandType.SetRenderTargets); Register<SetScissorsCommand>(CommandType.SetScissor); Register<SetStencilTestCommand>(CommandType.SetStencilTest); @@ -138,7 +137,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading Register<TextureBarrierTiledCommand>(CommandType.TextureBarrierTiled); Register<TryHostConditionalRenderingCommand>(CommandType.TryHostConditionalRendering); Register<TryHostConditionalRenderingFlushCommand>(CommandType.TryHostConditionalRenderingFlush); - Register<UpdateRenderScaleCommand>(CommandType.UpdateRenderScale); return maxCommandSize; } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs index 1e73fba62..c24a934aa 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -87,7 +87,6 @@ SetProgram, SetRasterizerDiscard, SetRenderTargetColorMasks, - SetRenderTargetScale, SetRenderTargets, SetScissor, SetStencilTest, @@ -100,6 +99,5 @@ TextureBarrierTiled, TryHostConditionalRendering, TryHostConditionalRenderingFlush, - UpdateRenderScale, } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs index f52db30ae..6ec0daae8 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs @@ -8,18 +8,16 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer public readonly CommandType CommandType => CommandType.CreateTexture; private TableRef<ThreadedTexture> _texture; private TextureCreateInfo _info; - private float _scale; - public void Set(TableRef<ThreadedTexture> texture, TextureCreateInfo info, float scale) + public void Set(TableRef<ThreadedTexture> texture, TextureCreateInfo info) { _texture = texture; _info = info; - _scale = scale; } public static void Run(ref CreateTextureCommand command, ThreadedRenderer threaded, IRenderer renderer) { - command._texture.Get(threaded).Base = renderer.CreateTexture(command._info, command._scale); + command._texture.Get(threaded).Base = renderer.CreateTexture(command._info); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs index 2a373f5b7..659cf4753 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs @@ -10,13 +10,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer private TableRef<ThreadedCounterEvent> _event; private CounterType _type; private TableRef<EventHandler<ulong>> _resultHandler; + private float _divisor; private bool _hostReserved; - public void Set(TableRef<ThreadedCounterEvent> evt, CounterType type, TableRef<EventHandler<ulong>> resultHandler, bool hostReserved) + public void Set(TableRef<ThreadedCounterEvent> evt, CounterType type, TableRef<EventHandler<ulong>> resultHandler, float divisor, bool hostReserved) { _event = evt; _type = type; _resultHandler = resultHandler; + _divisor = divisor; _hostReserved = hostReserved; } @@ -24,7 +26,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer { ThreadedCounterEvent evt = command._event.Get(threaded); - evt.Create(renderer, command._type, command._resultHandler.Get(threaded), command._hostReserved); + evt.Create(renderer, command._type, command._resultHandler.Get(threaded), command._divisor, command._hostReserved); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs deleted file mode 100644 index 7207fd9da..000000000 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics.GAL.Multithreading.Commands -{ - struct SetRenderTargetScaleCommand : IGALCommand, IGALCommand<SetRenderTargetScaleCommand> - { - public readonly CommandType CommandType => CommandType.SetRenderTargetScale; - private float _scale; - - public void Set(float scale) - { - _scale = scale; - } - - public static void Run(ref SetRenderTargetScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) - { - renderer.Pipeline.SetRenderTargetScale(command._scale); - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs index a0ad026a0..4ad264780 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs @@ -7,18 +7,16 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { public readonly CommandType CommandType => CommandType.SetViewports; private SpanRef<Viewport> _viewports; - private bool _disableTransform; - public void Set(SpanRef<Viewport> viewports, bool disableTransform) + public void Set(SpanRef<Viewport> viewports) { _viewports = viewports; - _disableTransform = disableTransform; } public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer) { ReadOnlySpan<Viewport> viewports = command._viewports.Get(threaded); - renderer.Pipeline.SetViewports(viewports, command._disableTransform); + renderer.Pipeline.SetViewports(viewports); command._viewports.Dispose(threaded); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs deleted file mode 100644 index c3c240455..000000000 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Ryujinx.Graphics.GAL.Multithreading.Model; - -namespace Ryujinx.Graphics.GAL.Multithreading.Commands -{ - struct UpdateRenderScaleCommand : IGALCommand, IGALCommand<UpdateRenderScaleCommand> - { - public readonly CommandType CommandType => CommandType.UpdateRenderScale; - private SpanRef<float> _scales; - private int _totalCount; - private int _fragmentCount; - - public void Set(SpanRef<float> scales, int totalCount, int fragmentCount) - { - _scales = scales; - _totalCount = totalCount; - _fragmentCount = fragmentCount; - } - - public static void Run(ref UpdateRenderScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) - { - renderer.Pipeline.UpdateRenderScale(command._scales.Get(threaded), command._totalCount, command._fragmentCount); - command._scales.Dispose(threaded); - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs index e4e197ebf..2992b4a8c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs @@ -70,10 +70,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } } - public void Create(IRenderer renderer, CounterType type, System.EventHandler<ulong> eventHandler, bool hostReserved) + public void Create(IRenderer renderer, CounterType type, System.EventHandler<ulong> eventHandler, float divisor, bool hostReserved) { ThreadedHelpers.SpinUntilExchange(ref _createLock, 1, 0); - Base = renderer.ReportCounter(type, eventHandler, hostReserved || _reserved); + Base = renderer.ReportCounter(type, eventHandler, divisor, hostReserved || _reserved); Volatile.Write(ref _createLock, 0); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index b82e286ad..68f258539 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -17,13 +17,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources public int Height => _info.Height; - public float ScaleFactor { get; } - - public ThreadedTexture(ThreadedRenderer renderer, TextureCreateInfo info, float scale) + public ThreadedTexture(ThreadedRenderer renderer, TextureCreateInfo info) { _renderer = renderer; _info = info; - ScaleFactor = scale; } private TableRef<T> Ref<T>(T reference) @@ -64,7 +61,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - ThreadedTexture newTex = new(_renderer, info, ScaleFactor); + ThreadedTexture newTex = new(_renderer, info); _renderer.New<TextureCreateViewCommand>().Set(Ref(this), Ref(newTex), info, firstLayer, firstLevel); _renderer.QueueCommand(); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index de6ba18d5..69c67d642 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -261,12 +261,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetRenderTargetScale(float scale) - { - _renderer.New<SetRenderTargetScaleCommand>().Set(scale); - _renderer.QueueCommand(); - } - public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors) { _renderer.New<SetScissorsCommand>().Set(_renderer.CopySpan(scissors)); @@ -321,9 +315,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform) + public void SetViewports(ReadOnlySpan<Viewport> viewports) { - _renderer.New<SetViewportsCommand>().Set(_renderer.CopySpan(viewports), disableTransform); + _renderer.New<SetViewportsCommand>().Set(_renderer.CopySpan(viewports)); _renderer.QueueCommand(); } @@ -368,11 +362,5 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); return false; } - - public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount) - { - _renderer.New<UpdateRenderScaleCommand>().Set(_renderer.CopySpan(scales[..totalCount]), totalCount, fragmentCount); - _renderer.QueueCommand(); - } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index dc7aab36c..e59a3928b 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -320,21 +320,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading QueueCommand(); } - public ITexture CreateTexture(TextureCreateInfo info, float scale) + public ITexture CreateTexture(TextureCreateInfo info) { if (IsGpuThread()) { - var texture = new ThreadedTexture(this, info, scale); - New<CreateTextureCommand>().Set(Ref(texture), info, scale); + var texture = new ThreadedTexture(this, info); + New<CreateTextureCommand>().Set(Ref(texture), info); QueueCommand(); return texture; } else { - var texture = new ThreadedTexture(this, info, scale) + var texture = new ThreadedTexture(this, info) { - Base = _baseRenderer.CreateTexture(info, scale), + Base = _baseRenderer.CreateTexture(info), }; return texture; @@ -410,10 +410,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading QueueCommand(); } - public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) + public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved) { - ThreadedCounterEvent evt = new(this, type, _lastSampleCounterClear); - New<ReportCounterCommand>().Set(Ref(evt), type, Ref(resultHandler), hostReserved); + ThreadedCounterEvent evt = new ThreadedCounterEvent(this, type, _lastSampleCounterClear); + New<ReportCounterCommand>().Set(Ref(evt), type, Ref(resultHandler), divisor, hostReserved); QueueCommand(); if (type == CounterType.SamplesPassed) diff --git a/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs deleted file mode 100644 index 84936e547..000000000 --- a/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs +++ /dev/null @@ -1,102 +0,0 @@ -using Ryujinx.Graphics.Shader; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.GAL -{ - public class SupportBufferUpdater : IDisposable - { - public SupportBuffer Data; - public BufferHandle Handle; - - private readonly IRenderer _renderer; - private int _startOffset = -1; - private int _endOffset = -1; - - public SupportBufferUpdater(IRenderer renderer) - { - _renderer = renderer; - Handle = renderer.CreateBuffer(SupportBuffer.RequiredSize); - renderer.Pipeline.ClearBuffer(Handle, 0, SupportBuffer.RequiredSize, 0); - } - - private void MarkDirty(int startOffset, int byteSize) - { - int endOffset = startOffset + byteSize; - - if (_startOffset == -1) - { - _startOffset = startOffset; - _endOffset = endOffset; - } - else - { - if (startOffset < _startOffset) - { - _startOffset = startOffset; - } - - if (endOffset > _endOffset) - { - _endOffset = endOffset; - } - } - } - - public void UpdateFragmentRenderScaleCount(int count) - { - if (Data.FragmentRenderScaleCount.X != count) - { - Data.FragmentRenderScaleCount.X = count; - - MarkDirty(SupportBuffer.FragmentRenderScaleCountOffset, sizeof(int)); - } - } - - private void UpdateGenericField<T>(int baseOffset, ReadOnlySpan<T> data, Span<T> target, int offset, int count) where T : unmanaged - { - data[..count].CopyTo(target[offset..]); - - int elemSize = Unsafe.SizeOf<T>(); - - MarkDirty(baseOffset + offset * elemSize, count * elemSize); - } - - public void UpdateRenderScale(ReadOnlySpan<Vector4<float>> data, int offset, int count) - { - UpdateGenericField(SupportBuffer.GraphicsRenderScaleOffset, data, Data.RenderScale.AsSpan(), offset, count); - } - - public void UpdateFragmentIsBgra(ReadOnlySpan<Vector4<int>> data, int offset, int count) - { - UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.AsSpan(), offset, count); - } - - public void UpdateViewportInverse(Vector4<float> data) - { - Data.ViewportInverse = data; - - MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize); - } - - public void Commit() - { - if (_startOffset != -1) - { - ReadOnlySpan<byte> data = MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref Data, 1)); - - _renderer.SetBufferData(Handle, _startOffset, data[_startOffset.._endOffset]); - - _startOffset = -1; - _endOffset = -1; - } - } - - public void Dispose() - { - GC.SuppressFinalize(this); - _renderer.DeleteBuffer(Handle); - } - } -} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs index 0a813ee55..247752caf 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs @@ -177,13 +177,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed resultHandler(null, (ulong)_state.State.SemaphorePayload); break; case ReportCounterType.SamplesPassed: - counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false); + float scale = _channel.TextureManager.RenderTargetScale; + float divisor = scale * scale; + counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, divisor, false); break; case ReportCounterType.PrimitivesGenerated: - counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler, false); + counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler, 1f, false); break; case ReportCounterType.TransformFeedbackPrimitivesWritten: - counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler, false); + counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler, 1f, false); break; } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 34439657e..b4f56245e 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -495,6 +495,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { clipRegionHeight = color.Height / samplesInY; } + + if (!_context.Capabilities.SupportsBgraFormat) + { + _context.SupportBufferUpdater.SetRenderTargetIsBgra(index, color.Format.IsBgr()); + } } } @@ -539,7 +544,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (oldScale != _channel.TextureManager.RenderTargetScale) { - _context.Renderer.Pipeline.SetRenderTargetScale(_channel.TextureManager.RenderTargetScale); + _context.SupportBufferUpdater.SetRenderTargetScale(_channel.TextureManager.RenderTargetScale); UpdateViewportTransform(); UpdateScissorState(); @@ -758,9 +763,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } _context.Renderer.Pipeline.SetDepthMode(GetDepthMode()); - _context.Renderer.Pipeline.SetViewports(viewports, disableTransform); + _context.Renderer.Pipeline.SetViewports(viewports); - _currentSpecState.SetViewportTransformDisable(_state.State.ViewportTransformEnable == 0); + _context.SupportBufferUpdater.SetViewportTransformDisable( + viewports[0].Region.Width, + viewports[0].Region.Height, + _channel.TextureManager.RenderTargetScale, + disableTransform); + + _currentSpecState.SetViewportTransformDisable(disableTransform); _currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne); } diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index a5fe8f4c1..a50852b05 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -85,6 +85,11 @@ namespace Ryujinx.Graphics.Gpu /// </summary> internal ConcurrentDictionary<ulong, PhysicalMemory> PhysicalMemoryRegistry { get; } + /// <summary> + /// Support buffer updater. + /// </summary> + internal SupportBufferUpdater SupportBufferUpdater { get; } + /// <summary> /// Host hardware capabilities. /// </summary> @@ -125,6 +130,8 @@ namespace Ryujinx.Graphics.Gpu PhysicalMemoryRegistry = new ConcurrentDictionary<ulong, PhysicalMemory>(); + SupportBufferUpdater = new SupportBufferUpdater(renderer); + _firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); } @@ -399,6 +406,8 @@ namespace Ryujinx.Graphics.Gpu physicalMemory.Dispose(); } + SupportBufferUpdater.Dispose(); + PhysicalMemoryRegistry.Clear(); RunDeferredActions(); diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index c0d45cbd1..0fce4debb 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -278,7 +278,7 @@ namespace Ryujinx.Graphics.Gpu.Image Debug.Assert(!isView); TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor); - HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor); + HostTexture = _context.Renderer.CreateTexture(createInfo); SynchronizeMemory(); // Load the data. if (ScaleMode == TextureScaleMode.Scaled) @@ -302,7 +302,7 @@ namespace Ryujinx.Graphics.Gpu.Image } TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor); - HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor); + HostTexture = _context.Renderer.CreateTexture(createInfo); } } } @@ -490,7 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (storage == null) { TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, scale); - storage = _context.Renderer.CreateTexture(createInfo, scale); + storage = _context.Renderer.CreateTexture(createInfo); } if (copy) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index e5df17760..d1454fc9a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -302,7 +302,7 @@ namespace Ryujinx.Graphics.Gpu.Image _lastFragmentTotal = fragmentTotal; - _context.Renderer.Pipeline.UpdateRenderScale(_scales, total, fragmentTotal); + _context.SupportBufferUpdater.UpdateRenderScale(_scales, total, fragmentTotal); _scaleChanged = false; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 4cd3710b9..10224a6d4 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -478,6 +478,8 @@ namespace Ryujinx.Graphics.Gpu.Memory // Force rebind after doing compute work. Rebind(); + + _context.SupportBufferUpdater.Commit(); } /// <summary> @@ -686,6 +688,8 @@ namespace Ryujinx.Graphics.Gpu.Memory CommitBufferTextureBindings(); _rebind = false; + + _context.SupportBufferUpdater.Commit(); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs new file mode 100644 index 000000000..3ea37c55a --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs @@ -0,0 +1,232 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + /// <summary> + /// Support buffer data updater. + /// </summary> + class SupportBufferUpdater : IDisposable + { + private SupportBuffer _data; + private BufferHandle _handle; + + private readonly IRenderer _renderer; + private int _startOffset = -1; + private int _endOffset = -1; + + /// <summary> + /// Creates a new instance of the support buffer updater. + /// </summary> + /// <param name="renderer">Renderer that the support buffer will be used with</param> + public SupportBufferUpdater(IRenderer renderer) + { + _renderer = renderer; + + var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f }; + _data.RenderScale.AsSpan().Fill(defaultScale); + DirtyRenderScale(0, SupportBuffer.RenderScaleMaxCount); + } + + /// <summary> + /// Mark a region of the support buffer as modified and needing to be sent to the GPU. + /// </summary> + /// <param name="startOffset">Start offset of the region in bytes</param> + /// <param name="byteSize">Size of the region in bytes</param> + private void MarkDirty(int startOffset, int byteSize) + { + int endOffset = startOffset + byteSize; + + if (_startOffset == -1) + { + _startOffset = startOffset; + _endOffset = endOffset; + } + else + { + if (startOffset < _startOffset) + { + _startOffset = startOffset; + } + + if (endOffset > _endOffset) + { + _endOffset = endOffset; + } + } + } + + /// <summary> + /// Marks the fragment render scale count as being modified. + /// </summary> + private void DirtyFragmentRenderScaleCount() + { + MarkDirty(SupportBuffer.FragmentRenderScaleCountOffset, sizeof(int)); + } + + /// <summary> + /// Marks data of a given type as being modified. + /// </summary> + /// <typeparam name="T">Type of the data</typeparam> + /// <param name="baseOffset">Base offset of the data in bytes</param> + /// <param name="offset">Index of the data, in elements</param> + /// <param name="count">Number of elements</param> + private void DirtyGenericField<T>(int baseOffset, int offset, int count) where T : unmanaged + { + int elemSize = Unsafe.SizeOf<T>(); + + MarkDirty(baseOffset + offset * elemSize, count * elemSize); + } + + /// <summary> + /// Marks render scales as being modified. + /// </summary> + /// <param name="offset">Index of the first scale that was modified</param> + /// <param name="count">Number of modified scales</param> + private void DirtyRenderScale(int offset, int count) + { + DirtyGenericField<Vector4<float>>(SupportBuffer.GraphicsRenderScaleOffset, offset, count); + } + + /// <summary> + /// Marks render target BGRA format state as modified. + /// </summary> + /// <param name="offset">Index of the first render target that had its BGRA format modified</param> + /// <param name="count">Number of render targets</param> + private void DirtyFragmentIsBgra(int offset, int count) + { + DirtyGenericField<Vector4<int>>(SupportBuffer.FragmentIsBgraOffset, offset, count); + } + + /// <summary> + /// Updates the inverse viewport vector. + /// </summary> + /// <param name="data">Inverse viewport vector</param> + private void UpdateViewportInverse(Vector4<float> data) + { + _data.ViewportInverse = data; + + MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize); + } + + /// <summary> + /// Sets the scale of all output render targets (they should all have the same scale). + /// </summary> + /// <param name="scale">Scale value</param> + public void SetRenderTargetScale(float scale) + { + _data.RenderScale[0].X = scale; + DirtyRenderScale(0, 1); // Just the first element. + } + + /// <summary> + /// Updates the render scales for shader input textures or images. + /// </summary> + /// <param name="scales">Scale values</param> + /// <param name="totalCount">Total number of scales across all stages</param> + /// <param name="fragmentCount">Total number of scales on the fragment shader stage</param> + public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount) + { + bool changed = false; + + for (int index = 0; index < totalCount; index++) + { + if (_data.RenderScale[1 + index].X != scales[index]) + { + _data.RenderScale[1 + index].X = scales[index]; + changed = true; + } + } + + // Only update fragment count if there are scales after it for the vertex stage. + if (fragmentCount != totalCount && fragmentCount != _data.FragmentRenderScaleCount.X) + { + _data.FragmentRenderScaleCount.X = fragmentCount; + DirtyFragmentRenderScaleCount(); + } + + if (changed) + { + DirtyRenderScale(0, 1 + totalCount); + } + } + + /// <summary> + /// Sets whether the format of a given render target is a BGRA format. + /// </summary> + /// <param name="index">Render target index</param> + /// <param name="isBgra">True if the format is BGRA< false otherwise</param> + public void SetRenderTargetIsBgra(int index, bool isBgra) + { + bool isBgraChanged = (_data.FragmentIsBgra[index].X != 0) != isBgra; + + if (isBgraChanged) + { + _data.FragmentIsBgra[index].X = isBgra ? 1 : 0; + DirtyFragmentIsBgra(index, 1); + } + } + + /// <summary> + /// Sets whether a viewport has transform disabled. + /// </summary> + /// <param name="viewportWidth">Value used as viewport width</param> + /// <param name="viewportHeight">Value used as viewport height</param> + /// <param name="scale">Render target scale</param> + /// <param name="disableTransform">True if transform is disabled, false otherwise</param> + public void SetViewportTransformDisable(float viewportWidth, float viewportHeight, float scale, bool disableTransform) + { + float disableTransformF = disableTransform ? 1.0f : 0.0f; + if (_data.ViewportInverse.W != disableTransformF || disableTransform) + { + UpdateViewportInverse(new Vector4<float> + { + X = scale * 2f / viewportWidth, + Y = scale * 2f / viewportHeight, + Z = 1, + W = disableTransformF + }); + } + } + + /// <summary> + /// Submits all pending buffer updates to the GPU. + /// </summary> + public void Commit() + { + if (_startOffset != -1) + { + if (_handle == BufferHandle.Null) + { + _handle = _renderer.CreateBuffer(SupportBuffer.RequiredSize); + _renderer.Pipeline.ClearBuffer(_handle, 0, SupportBuffer.RequiredSize, 0); + + var range = new BufferRange(_handle, 0, SupportBuffer.RequiredSize); + _renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, range) }); + } + + ReadOnlySpan<byte> data = MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref _data, 1)); + + _renderer.SetBufferData(_handle, _startOffset, data.Slice(_startOffset, _endOffset - _startOffset)); + + _startOffset = -1; + _endOffset = -1; + } + } + + /// <summary> + /// Destroys the support buffer. + /// </summary> + public void Dispose() + { + if (_handle != BufferHandle.Null) + { + _renderer.DeleteBuffer(_handle); + _handle = BufferHandle.Null; + } + } + } +} diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 7356410ca..af1e1ee3f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -58,6 +58,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); + AddUsage(SupportBufferStages, ResourceType.UniformBuffer, ResourceAccess.Read, UniformSetIndex, 0, 1); _reservedConstantBuffers = 1; // For the support buffer. diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs index 5da472b3e..89addc8a8 100644 --- a/src/Ryujinx.Graphics.Gpu/Window.cs +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -208,7 +208,16 @@ namespace Ryujinx.Graphics.Gpu texture.SynchronizeMemory(); - ImageCrop crop = pt.Crop; + ImageCrop crop = new ImageCrop( + (int)(pt.Crop.Left * texture.ScaleFactor), + (int)MathF.Ceiling(pt.Crop.Right * texture.ScaleFactor), + (int)(pt.Crop.Top * texture.ScaleFactor), + (int)MathF.Ceiling(pt.Crop.Bottom * texture.ScaleFactor), + pt.Crop.FlipX, + pt.Crop.FlipY, + pt.Crop.IsStretched, + pt.Crop.AspectRatioX, + pt.Crop.AspectRatioY); if (texture.Info.Width > pt.Info.Width || texture.Info.Height > pt.Info.Height) { diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs index 5daaf8c45..1a130bebb 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs @@ -114,7 +114,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects originalInfo.SwizzleB, originalInfo.SwizzleA); - _intermediaryTexture = new TextureStorage(_renderer, info, view.ScaleFactor); + _intermediaryTexture = new TextureStorage(_renderer, info); _intermediaryTexture.CreateDefaultView(); } diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs index c8f170846..93654ac70 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects if (_textureStorage == null || _textureStorage.Info.Width != view.Width || _textureStorage.Info.Height != view.Height) { _textureStorage?.Dispose(); - _textureStorage = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _textureStorage = new TextureStorage(_renderer, view.Info); _textureStorage.CreateDefaultView(); } diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs index eede852f7..b3f6cb1e5 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs @@ -147,8 +147,8 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa SwizzleComponent.Blue, SwizzleComponent.Alpha); - _areaTexture = new TextureStorage(_renderer, areaInfo, 1); - _searchTexture = new TextureStorage(_renderer, searchInfo, 1); + _areaTexture = new TextureStorage(_renderer, areaInfo); + _searchTexture = new TextureStorage(_renderer, searchInfo); var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin"); var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin"); @@ -165,11 +165,11 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa if (_outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height) { _outputTexture?.Dispose(); - _outputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _outputTexture = new TextureStorage(_renderer, view.Info); _outputTexture.CreateDefaultView(); - _edgeOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _edgeOutputTexture = new TextureStorage(_renderer, view.Info); _edgeOutputTexture.CreateDefaultView(); - _blendOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor); + _blendOutputTexture = new TextureStorage(_renderer, view.Info); _blendOutputTexture.CreateDefaultView(); DeleteShaders(); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs b/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs index 1a08f973a..64ee73fbe 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs @@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.OpenGL.Image SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, - SwizzleComponent.Alpha), 1f); + SwizzleComponent.Alpha)); } public void Dispose() diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs index 2ab9dffbc..070a36b5e 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs @@ -11,15 +11,13 @@ namespace Ryujinx.Graphics.OpenGL.Image public int Width => Info.Width; public int Height => Info.Height; - public float ScaleFactor { get; } public Target Target => Info.Target; public Format Format => Info.Format; - public TextureBase(TextureCreateInfo info, float scaleFactor = 1f) + public TextureBase(TextureCreateInfo info) { Info = info; - ScaleFactor = scaleFactor; Handle = GL.GenTexture(); } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index bb1911e8b..e33940cb1 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -349,7 +349,7 @@ namespace Ryujinx.Graphics.OpenGL.Image public TextureView BgraSwap(TextureView from) { - TextureView to = (TextureView)_renderer.CreateTexture(from.Info, from.ScaleFactor); + TextureView to = (TextureView)_renderer.CreateTexture(from.Info); EnsurePbo(from); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs index d714caf38..79c6cb685 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs @@ -8,7 +8,6 @@ namespace Ryujinx.Graphics.OpenGL.Image { public ITextureInfo Storage => this; public int Handle { get; private set; } - public float ScaleFactor { get; private set; } public TextureCreateInfo Info { get; } @@ -18,13 +17,12 @@ namespace Ryujinx.Graphics.OpenGL.Image internal ITexture DefaultView { get; private set; } - public TextureStorage(OpenGLRenderer renderer, TextureCreateInfo info, float scaleFactor) + public TextureStorage(OpenGLRenderer renderer, TextureCreateInfo info) { _renderer = renderer; Info = info; Handle = GL.GenTexture(); - ScaleFactor = scaleFactor; CreateImmutableStorage(); } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 21d8e449c..f4b1e0dad 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureStorage parent, TextureCreateInfo info, int firstLayer, - int firstLevel) : base(info, parent.ScaleFactor) + int firstLevel) : base(info) { _renderer = renderer; _parent = parent; diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index b4c567a81..1ad927ea6 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.OpenGL return new Sampler(info); } - public ITexture CreateTexture(TextureCreateInfo info, float scaleFactor) + public ITexture CreateTexture(TextureCreateInfo info) { if (info.Target == Target.TextureBuffer) { @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.OpenGL } else { - return ResourcePool.GetTextureOrNull(info, scaleFactor) ?? new TextureStorage(this, info, scaleFactor).CreateDefaultView(); + return ResourcePool.GetTextureOrNull(info) ?? new TextureStorage(this, info).CreateDefaultView(); } } @@ -194,9 +194,9 @@ namespace Ryujinx.Graphics.OpenGL ResourcePool.Tick(); } - public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) + public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved) { - return _counters.QueueReport(type, resultHandler, _pipeline.DrawCount, hostReserved); + return _counters.QueueReport(type, resultHandler, divisor, _pipeline.DrawCount, hostReserved); } public void Initialize(GraphicsDebugLevel glLogLevel) @@ -210,7 +210,6 @@ namespace Ryujinx.Graphics.OpenGL GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8)); } - _pipeline.Initialize(this); _counters.Initialize(_pipeline); // This is required to disable [0, 1] clamping for SNorm outputs on compatibility profiles. diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index df618d5bc..85f5b1139 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -44,9 +44,7 @@ namespace Ryujinx.Graphics.OpenGL private CounterQueueEvent _activeConditionalRender; - private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount]; - private readonly Vector4<float>[] _renderScale = new Vector4<float>[73]; - private int _fragmentScaleCount; + private Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount]; private readonly (TextureBase, Format)[] _images; private TextureBase _unit0Texture; @@ -66,7 +64,6 @@ namespace Ryujinx.Graphics.OpenGL private bool _tfEnabled; private TransformFeedbackPrimitiveType _tfTopology; - private SupportBufferUpdater _supportBuffer; private readonly BufferHandle[] _tfbs; private readonly BufferRange[] _tfbTargets; @@ -84,22 +81,10 @@ namespace Ryujinx.Graphics.OpenGL _images = new (TextureBase, Format)[SavedImages]; - var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f }; - new Span<Vector4<float>>(_renderScale).Fill(defaultScale); - _tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers]; _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers]; } - public void Initialize(OpenGLRenderer renderer) - { - _supportBuffer = new SupportBufferUpdater(renderer); - GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As<BufferHandle, int>(ref _supportBuffer.Handle)); - - _supportBuffer.UpdateFragmentIsBgra(_fpIsBgra, 0, SupportBuffer.FragmentIsBgraCount); - _supportBuffer.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount); - } - public void Barrier() { GL.MemoryBarrier(MemoryBarrierFlags.AllBarrierBits); @@ -684,8 +669,6 @@ namespace Ryujinx.Graphics.OpenGL { if (texture is TextureView view && sampler is Sampler samp) { - _supportBuffer.Commit(); - if (HwCapabilities.SupportsDrawTexture) { GL.NV.DrawTexture( @@ -777,16 +760,6 @@ namespace Ryujinx.Graphics.OpenGL _tfEnabled = false; } - public double GetCounterDivisor(CounterType type) - { - if (type == CounterType.SamplesPassed) - { - return _renderScale[0].X * _renderScale[0].X; - } - - return 1; - } - public void SetAlphaTest(bool enable, float reference, CompareOp op) { if (!enable) @@ -1172,12 +1145,6 @@ namespace Ryujinx.Graphics.OpenGL _rasterizerDiscard = discard; } - public void SetRenderTargetScale(float scale) - { - _renderScale[0].X = scale; - _supportBuffer.UpdateRenderScale(_renderScale, 0, 1); // Just the first element. - } - public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks) { _componentMasks = 0; @@ -1194,8 +1161,6 @@ namespace Ryujinx.Graphics.OpenGL { EnsureFramebuffer(); - bool isBgraChanged = false; - for (int index = 0; index < colors.Length; index++) { TextureView color = (TextureView)colors[index]; @@ -1209,18 +1174,12 @@ namespace Ryujinx.Graphics.OpenGL if (_fpIsBgra[index].X != isBgra) { _fpIsBgra[index].X = isBgra; - isBgraChanged = true; RestoreComponentMask(index); } } } - if (isBgraChanged) - { - _supportBuffer.UpdateFragmentIsBgra(_fpIsBgra, 0, SupportBuffer.FragmentIsBgraCount); - } - TextureView depthStencilView = (TextureView)depthStencil; _framebuffer.AttachDepthStencil(depthStencilView); @@ -1411,7 +1370,7 @@ namespace Ryujinx.Graphics.OpenGL _vertexArray.SetVertexBuffers(vertexBuffers); } - public void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform) + public void SetViewports(ReadOnlySpan<Viewport> viewports) { Array.Resize(ref _viewportArray, viewports.Length * 4); Array.Resize(ref _depthRangeArray, viewports.Length * 2); @@ -1450,19 +1409,6 @@ namespace Ryujinx.Graphics.OpenGL GL.ViewportArray(0, viewports.Length, viewportArray); GL.DepthRangeArray(0, viewports.Length, depthRangeArray); - - float disableTransformF = disableTransform ? 1.0f : 0.0f; - if (_supportBuffer.Data.ViewportInverse.W != disableTransformF || disableTransform) - { - float scale = _renderScale[0].X; - _supportBuffer.UpdateViewportInverse(new Vector4<float> - { - X = scale * 2f / viewports[0].Region.Width, - Y = scale * 2f / viewports[0].Region.Height, - Z = 1, - W = disableTransformF - }); - } } public void TextureBarrier() @@ -1552,36 +1498,9 @@ namespace Ryujinx.Graphics.OpenGL return (_boundDrawFramebuffer, _boundReadFramebuffer); } - public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount) - { - bool changed = false; - - for (int index = 0; index < totalCount; index++) - { - if (_renderScale[1 + index].X != scales[index]) - { - _renderScale[1 + index].X = scales[index]; - changed = true; - } - } - - // Only update fragment count if there are scales after it for the vertex stage. - if (fragmentCount != totalCount && fragmentCount != _fragmentScaleCount) - { - _fragmentScaleCount = fragmentCount; - _supportBuffer.UpdateFragmentRenderScaleCount(_fragmentScaleCount); - } - - if (changed) - { - _supportBuffer.UpdateRenderScale(_renderScale, 0, 1 + totalCount); - } - } - private void PrepareForDispatch() { _unit0Texture?.Bind(0); - _supportBuffer.Commit(); } private void PreDraw(int vertexCount) @@ -1601,7 +1520,6 @@ namespace Ryujinx.Graphics.OpenGL DrawCount++; _unit0Texture?.Bind(0); - _supportBuffer.Commit(); } private void PostDraw() @@ -1752,8 +1670,6 @@ namespace Ryujinx.Graphics.OpenGL public void Dispose() { - _supportBuffer?.Dispose(); - for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++) { if (_tfbs[i] != BufferHandle.Null) diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index ff7e9a3ee..1724616ea 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } - public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, ulong lastDrawIndex, bool hostReserved) + public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, bool hostReserved) { CounterQueueEvent result; ulong draws = lastDrawIndex - _current.DrawIndex; @@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries _current.ReserveForHostAccess(); } - _current.Complete(draws > 0, _pipeline.GetCounterDivisor(Type)); + _current.Complete(draws > 0, divisor); _events.Enqueue(_current); _current.OnResult += resultHandler; diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index 91554ebad..88e8bf917 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -23,9 +23,9 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } - public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, ulong lastDrawIndex, bool hostReserved) + public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, bool hostReserved) { - return _counterQueues[(int)type].QueueReport(resultHandler, lastDrawIndex, hostReserved); + return _counterQueues[(int)type].QueueReport(resultHandler, divisor, lastDrawIndex, hostReserved); } public void QueueReset(CounterType type) diff --git a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs index bba5c5cb7..43410c99b 100644 --- a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs +++ b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs @@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.OpenGL { public TextureCreateInfo Info; public TextureView View; - public float ScaleFactor; public int RemainingFrames; } @@ -42,7 +41,6 @@ namespace Ryujinx.Graphics.OpenGL { Info = view.Info, View = view, - ScaleFactor = view.ScaleFactor, RemainingFrames = DisposedLiveFrames }); } @@ -52,9 +50,8 @@ namespace Ryujinx.Graphics.OpenGL /// Attempt to obtain a texture from the resource cache with the desired parameters. /// </summary> /// <param name="info">The creation info for the desired texture</param> - /// <param name="scaleFactor">The scale factor for the desired texture</param> /// <returns>A TextureView with the description specified, or null if one was not found.</returns> - public TextureView GetTextureOrNull(TextureCreateInfo info, float scaleFactor) + public TextureView GetTextureOrNull(TextureCreateInfo info) { lock (_lock) { @@ -65,11 +62,8 @@ namespace Ryujinx.Graphics.OpenGL foreach (DisposedTexture texture in list) { - if (scaleFactor == texture.ScaleFactor) - { - list.Remove(texture); - return texture.View; - } + list.Remove(texture); + return texture.View; } return null; diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index a928772fd..a8e6031b6 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -111,12 +111,11 @@ namespace Ryujinx.Graphics.OpenGL GL.Clear(ClearBufferMask.ColorBufferBit); int srcX0, srcX1, srcY0, srcY1; - float scale = viewConverted.ScaleFactor; if (crop.Left == 0 && crop.Right == 0) { srcX0 = 0; - srcX1 = (int)(viewConverted.Width / scale); + srcX1 = viewConverted.Width; } else { @@ -127,7 +126,7 @@ namespace Ryujinx.Graphics.OpenGL if (crop.Top == 0 && crop.Bottom == 0) { srcY0 = 0; - srcY1 = (int)(viewConverted.Height / scale); + srcY1 = viewConverted.Height; } else { @@ -135,14 +134,6 @@ namespace Ryujinx.Graphics.OpenGL srcY1 = crop.Bottom; } - if (scale != 1f) - { - srcX0 = (int)(srcX0 * scale); - srcY0 = (int)(srcY0 * scale); - srcX1 = (int)Math.Ceiling(srcX1 * scale); - srcY1 = (int)Math.Ceiling(srcY1 * scale); - } - 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)); @@ -408,7 +399,7 @@ namespace Ryujinx.Graphics.OpenGL SwizzleComponent.Alpha); _isBgra = forceBgra; - _upscaledTexture = _renderer.CreateTexture(info, 1) as TextureView; + _upscaledTexture = _renderer.CreateTexture(info) as TextureView; } public void SetScalingFilterLevel(float level) diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 087d90fb8..894710911 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -35,7 +35,6 @@ namespace Ryujinx.Graphics.Vulkan private readonly bool[] _uniformSet; private readonly bool[] _storageSet; - private Buffer _cachedSupportBuffer; [Flags] private enum DirtyFlags @@ -115,7 +114,7 @@ namespace Ryujinx.Graphics.Vulkan SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, - SwizzleComponent.Alpha), 1f); + SwizzleComponent.Alpha)); _dummySampler = (SamplerHolder)gd.CreateSampler(new SamplerCreateInfo( MinFilter.Nearest, @@ -392,26 +391,6 @@ namespace Ryujinx.Graphics.Vulkan { Initialize(cbs, setIndex, dsc); } - - if (setIndex == PipelineBase.UniformSetIndex) - { - Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1]; - - if (!_uniformSet[0]) - { - _cachedSupportBuffer = _gd.BufferManager.GetBuffer(cbs.CommandBuffer, _pipeline.SupportBufferUpdater.Handle, false).Get(cbs, 0, SupportBuffer.RequiredSize).Value; - _uniformSet[0] = true; - } - - uniformBuffer[0] = new DescriptorBufferInfo - { - Offset = 0, - Range = (ulong)SupportBuffer.RequiredSize, - Buffer = _cachedSupportBuffer, - }; - - dsc.UpdateBuffers(0, 0, uniformBuffer, DescriptorType.UniformBuffer); - } } foreach (ResourceBindingSegment segment in bindingSegments) @@ -553,22 +532,6 @@ namespace Ryujinx.Graphics.Vulkan [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp) { - if (!_uniformSet[0]) - { - Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1]; - - uniformBuffer[0] = new DescriptorBufferInfo - { - Offset = 0, - Range = (ulong)SupportBuffer.RequiredSize, - Buffer = _gd.BufferManager.GetBuffer(cbs.CommandBuffer, _pipeline.SupportBufferUpdater.Handle, false).Get(cbs, 0, SupportBuffer.RequiredSize).Value, - }; - - _uniformSet[0] = true; - - UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer); - } - var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex]; var dummyBuffer = _dummyBuffer?.GetBuffer(); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index 89a43b12c..23acdcf8f 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects originalInfo.SwizzleB, originalInfo.SwizzleA); _intermediaryTexture?.Dispose(); - _intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + _intermediaryTexture = _renderer.CreateTexture(info) as TextureView; } _pipeline.SetCommandBuffer(cbs); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index 2dd991802..7729934f2 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height) { _texture?.Dispose(); - _texture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; + _texture = _renderer.CreateTexture(view.Info) as TextureView; } _pipeline.SetCommandBuffer(cbs); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index 40ab245ee..c521f2273 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -177,8 +177,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin"); var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin"); - _areaTexture = _renderer.CreateTexture(areaInfo, 1) as TextureView; - _searchTexture = _renderer.CreateTexture(searchInfo, 1) as TextureView; + _areaTexture = _renderer.CreateTexture(areaInfo) as TextureView; + _searchTexture = _renderer.CreateTexture(searchInfo) as TextureView; _areaTexture.SetData(areaTexture); _searchTexture.SetData(searchTexture); @@ -193,9 +193,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects _edgeOutputTexture?.Dispose(); _blendOutputTexture?.Dispose(); - _outputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; - _edgeOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; - _blendOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView; + _outputTexture = _renderer.CreateTexture(view.Info) as TextureView; + _edgeOutputTexture = _renderer.CreateTexture(view.Info) as TextureView; + _blendOutputTexture = _renderer.CreateTexture(view.Info) as TextureView; } _pipeline.SetCommandBuffer(cbs); diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 648afcd6a..b19a4f704 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -470,7 +470,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); } - _pipeline.SetViewports(viewports, false); + _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); @@ -546,7 +546,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat); _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); + _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); var aspectFlags = src.Info.Format.ConvertAspectFlags(); @@ -710,7 +710,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetProgram(program); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); _pipeline.SetRenderTargetColorMasks(new[] { componentMask }); - _pipeline.SetViewports(viewports, false); + _pipeline.SetViewports(viewports); _pipeline.SetScissors(scissors); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); @@ -774,7 +774,7 @@ namespace Ryujinx.Graphics.Vulkan Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; pipeline.SetProgram(_programColorBlit); - pipeline.SetViewports(viewports, false); + pipeline.SetViewports(viewports); pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); pipeline.Draw(4, 1, 0, 0); @@ -1117,7 +1117,7 @@ namespace Ryujinx.Graphics.Vulkan scissors[0] = new Rectangle<int>(0, 0, dst.Width, dst.Height); _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); + _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); for (int z = 0; z < depth; z++) @@ -1250,7 +1250,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports, false); + _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 7f175d93c..b76e482b5 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -50,9 +50,6 @@ namespace Ryujinx.Graphics.Vulkan private ShaderCollection _program; - private readonly Vector4<float>[] _renderScale = new Vector4<float>[73]; - private int _fragmentScaleCount; - protected FramebufferParams FramebufferParams; private Auto<DisposableFramebuffer> _framebuffer; private Auto<DisposableRenderPass> _renderPass; @@ -74,7 +71,6 @@ namespace Ryujinx.Graphics.Vulkan private readonly VertexBufferUpdater _vertexBufferUpdater; - public SupportBufferUpdater SupportBufferUpdater; public IndexBufferPattern QuadsToTrisPattern; public IndexBufferPattern TriFanToTrisPattern; @@ -119,9 +115,6 @@ namespace Ryujinx.Graphics.Vulkan ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff); - var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f }; - new Span<Vector4<float>>(_renderScale).Fill(defaultScale); - _storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets]; _newState.Initialize(); @@ -131,9 +124,6 @@ namespace Ryujinx.Graphics.Vulkan { _descriptorSetUpdater.Initialize(); - SupportBufferUpdater = new SupportBufferUpdater(Gd); - SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount); - QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false); TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true); } @@ -666,8 +656,6 @@ namespace Ryujinx.Graphics.Vulkan { if (texture is TextureView srcTexture) { - SupportBufferUpdater.Commit(); - var oldCullMode = _newState.CullMode; var oldStencilTestEnable = _newState.StencilTestEnable; var oldDepthTestEnable = _newState.DepthTestEnable; @@ -709,16 +697,6 @@ namespace Ryujinx.Graphics.Vulkan _tfEnabled = false; } - public double GetCounterDivisor(CounterType type) - { - if (type == CounterType.SamplesPassed) - { - return _renderScale[0].X * _renderScale[0].X; - } - - return 1; - } - public bool IsCommandBufferActive(CommandBuffer cb) { return CommandBuffer.Handle == cb.Handle; @@ -1050,12 +1028,6 @@ namespace Ryujinx.Graphics.Vulkan SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR); } - public void SetRenderTargetScale(float scale) - { - _renderScale[0].X = scale; - SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, 1); // Just the first element. - } - public void SetScissors(ReadOnlySpan<Rectangle<int>> regions) { int maxScissors = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; @@ -1303,7 +1275,7 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform) + public void SetViewports(ReadOnlySpan<Viewport> viewports) { int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; int count = Math.Min(maxViewports, viewports.Length); @@ -1328,19 +1300,6 @@ namespace Ryujinx.Graphics.Vulkan Clamp(viewport.DepthFar))); } - float disableTransformF = disableTransform ? 1.0f : 0.0f; - if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform) - { - float scale = _renderScale[0].X; - SupportBufferUpdater.UpdateViewportInverse(new Vector4<float> - { - X = scale * 2f / viewports[0].Region.Width, - Y = scale * 2f / viewports[0].Region.Height, - Z = 1, - W = disableTransformF, - }); - } - _newState.ViewportsCount = (uint)count; SignalStateChange(); } @@ -1391,32 +1350,6 @@ namespace Ryujinx.Graphics.Vulkan TextureBarrier(); } - public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount) - { - bool changed = false; - - for (int index = 0; index < totalCount; index++) - { - if (_renderScale[1 + index].X != scales[index]) - { - _renderScale[1 + index].X = scales[index]; - changed = true; - } - } - - // Only update fragment count if there are scales after it for the vertex stage. - if (fragmentCount != totalCount && fragmentCount != _fragmentScaleCount) - { - _fragmentScaleCount = fragmentCount; - SupportBufferUpdater.UpdateFragmentRenderScaleCount(_fragmentScaleCount); - } - - if (changed) - { - SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, 1 + totalCount); - } - } - protected void SignalCommandBufferChange() { _needsIndexBufferRebind = true; @@ -1614,9 +1547,6 @@ namespace Ryujinx.Graphics.Vulkan DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); - // Commit changes to the support buffer before drawing. - SupportBufferUpdater.Commit(); - if (_needsIndexBufferRebind && _indexBufferPattern == null) { _indexBuffer.BindIndexBuffer(Gd, Cbs); @@ -1777,8 +1707,6 @@ namespace Ryujinx.Graphics.Vulkan { Gd.Api.DestroyPipelineCache(Device, PipelineCache, null); } - - SupportBufferUpdater.Dispose(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index 724588d57..9c450cb78 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries } } - public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, ulong lastDrawIndex, bool hostReserved) + public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, bool hostReserved) { CounterQueueEvent result; ulong draws = lastDrawIndex - _current.DrawIndex; @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries _current.ReserveForHostAccess(); } - _current.Complete(draws > 0 && Type != CounterType.TransformFeedbackPrimitivesWritten, _pipeline.GetCounterDivisor(Type)); + _current.Complete(draws > 0 && Type != CounterType.TransformFeedbackPrimitivesWritten, divisor); _events.Enqueue(_current); _current.OnResult += resultHandler; diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs b/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs index d9d65062f..518ede5f3 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs @@ -37,9 +37,9 @@ namespace Ryujinx.Graphics.Vulkan.Queries _counterQueues[(int)CounterType.SamplesPassed].ResetFutureCounters(cmd, count); } - public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) + public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved) { - return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved); + return _counterQueues[(int)type].QueueReport(resultHandler, divisor, _pipeline.DrawCount, hostReserved); } public void QueueReset(CounterType type) diff --git a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index e7d24a007..ddcf51f69 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -25,15 +25,12 @@ namespace Ryujinx.Graphics.Vulkan public VkFormat VkFormat { get; } - public float ScaleFactor { get; } - - public TextureBuffer(VulkanRenderer gd, TextureCreateInfo info, float scale) + public TextureBuffer(VulkanRenderer gd, TextureCreateInfo info) { _gd = gd; Width = info.Width; Height = info.Height; VkFormat = FormatTable.GetFormat(info.Format); - ScaleFactor = scale; gd.Textures.Add(this); } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 2a51c132a..5ecd99073 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -54,19 +54,16 @@ namespace Ryujinx.Graphics.Vulkan private readonly ulong _size; public VkFormat VkFormat { get; } - public float ScaleFactor { get; } public unsafe TextureStorage( VulkanRenderer gd, Device device, TextureCreateInfo info, - float scaleFactor, Auto<MemoryAllocation> foreignAllocation = null) { _gd = gd; _device = device; _info = info; - ScaleFactor = scaleFactor; var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); var levels = (uint)info.Levels; @@ -175,7 +172,7 @@ namespace Ryujinx.Graphics.Vulkan var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel); - storage = new TextureStorage(_gd, _device, info, ScaleFactor, _allocationAuto); + storage = new TextureStorage(_gd, _device, info, _allocationAuto); _aliasedStorages.Add(format, storage); } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 6151d5a9e..bb14ea611 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -32,7 +32,6 @@ namespace Ryujinx.Graphics.Vulkan public int Layers => Info.GetDepthOrLayers(); public int FirstLayer { get; } public int FirstLevel { get; } - public float ScaleFactor => Storage.ScaleFactor; public VkFormat VkFormat { get; } public bool Valid { get; private set; } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index b9b1ba911..11c3bfe4e 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -432,26 +432,26 @@ namespace Ryujinx.Graphics.Vulkan return new SamplerHolder(this, _device, info); } - public ITexture CreateTexture(TextureCreateInfo info, float scale) + public ITexture CreateTexture(TextureCreateInfo info) { if (info.Target == Target.TextureBuffer) { - return new TextureBuffer(this, info, scale); + return new TextureBuffer(this, info); } - return CreateTextureView(info, scale); + return CreateTextureView(info); } - internal TextureView CreateTextureView(TextureCreateInfo info, float scale) + internal TextureView CreateTextureView(TextureCreateInfo info) { // This should be disposed when all views are destroyed. - var storage = CreateTextureStorage(info, scale); + var storage = CreateTextureStorage(info); return storage.CreateView(info, 0, 0); } - internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale) + internal TextureStorage CreateTextureStorage(TextureCreateInfo info) { - return new TextureStorage(this, _device, info, scale); + return new TextureStorage(this, _device, info); } public void DeleteBuffer(BufferHandle buffer) @@ -753,9 +753,9 @@ namespace Ryujinx.Graphics.Vulkan SyncManager.Cleanup(); } - public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) + public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved) { - return _counters.QueueReport(type, resultHandler, hostReserved); + return _counters.QueueReport(type, resultHandler, divisor, hostReserved); } public void ResetCounter(CounterType type) diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 0a41e98df..6027962cf 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -294,12 +294,11 @@ namespace Ryujinx.Graphics.Vulkan } int srcX0, srcX1, srcY0, srcY1; - float scale = view.ScaleFactor; if (crop.Left == 0 && crop.Right == 0) { srcX0 = 0; - srcX1 = (int)(view.Width / scale); + srcX1 = view.Width; } else { @@ -310,7 +309,7 @@ namespace Ryujinx.Graphics.Vulkan if (crop.Top == 0 && crop.Bottom == 0) { srcY0 = 0; - srcY1 = (int)(view.Height / scale); + srcY1 = view.Height; } else { @@ -318,14 +317,6 @@ namespace Ryujinx.Graphics.Vulkan srcY1 = crop.Bottom; } - if (scale != 1f) - { - srcX0 = (int)(srcX0 * scale); - srcY0 = (int)(srcY0 * scale); - srcX1 = (int)Math.Ceiling(srcX1 * scale); - srcY1 = (int)Math.Ceiling(srcY1 * scale); - } - if (ScreenCaptureRequested) { if (_effect != null) From ac2444f908bee5b5c1a13fe64e997315cea4b23c Mon Sep 17 00:00:00 2001 From: ealekseychik <74912034+ealekseychik@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:41:18 +0300 Subject: [PATCH 704/737] Move ShaderBinaries into individual .spv files (#5280) * Move ShaderBinaries into individual spv files * Rename binaries directory, remove variables and add helper method instead * Update .csproj file * Move ShaderBinaries into individual spv files * Rename binaries directory, remove variables and add helper method instead * Split shader binaries into folders, use string.Join to create filepath * Move files back to general binaries folder * Remove ShaderSource suffix from file names --------- Co-authored-by: Egor Alekseychik <e.alekseychik@syberry.com> Co-authored-by: Gabriel A <gab.dark.100@gmail.com> --- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 85 +- .../Ryujinx.Graphics.Vulkan.csproj | 25 + .../Shaders/ShaderBinaries.cs | 2595 ----------------- .../SpirvBinaries/ChangeBufferStride.spv | Bin 0 -> 3812 bytes .../ColorBlitClearAlphaFragment.spv | Bin 0 -> 656 bytes .../SpirvBinaries/ColorBlitFragment.spv | Bin 0 -> 552 bytes .../SpirvBinaries/ColorBlitMsFragment.spv | Bin 0 -> 808 bytes .../Shaders/SpirvBinaries/ColorBlitVertex.spv | Bin 0 -> 1564 bytes .../SpirvBinaries/ColorClearFFragment.spv | Bin 0 -> 380 bytes .../SpirvBinaries/ColorClearSIFragment.spv | Bin 0 -> 428 bytes .../SpirvBinaries/ColorClearUIFragment.spv | Bin 0 -> 428 bytes .../SpirvBinaries/ColorClearVertex.spv | Bin 0 -> 1424 bytes .../ColorCopyShorteningCompute.spv | Bin 0 -> 2040 bytes .../SpirvBinaries/ColorCopyToNonMsCompute.spv | Bin 0 -> 2044 bytes .../ColorCopyWideningCompute.spv | Bin 0 -> 1968 bytes .../SpirvBinaries/ColorDrawToMsFragment.spv | Bin 0 -> 1572 bytes .../SpirvBinaries/ColorDrawToMsVertex.spv | Bin 0 -> 1104 bytes .../SpirvBinaries/ConvertD32S8ToD24S8.spv | Bin 0 -> 3236 bytes .../SpirvBinaries/ConvertIndexBuffer.spv | Bin 0 -> 3044 bytes .../SpirvBinaries/ConvertIndirectData.spv | Bin 0 -> 5424 bytes .../SpirvBinaries/DepthBlitFragment.spv | Bin 0 -> 600 bytes .../SpirvBinaries/DepthBlitMsFragment.spv | Bin 0 -> 856 bytes .../SpirvBinaries/DepthDrawToMsFragment.spv | Bin 0 -> 1596 bytes .../DepthDrawToNonMsFragment.spv | Bin 0 -> 1576 bytes .../SpirvBinaries/StencilBlitFragment.spv | Bin 0 -> 704 bytes .../SpirvBinaries/StencilBlitMsFragment.spv | Bin 0 -> 944 bytes .../SpirvBinaries/StencilDrawToMsFragment.spv | Bin 0 -> 1684 bytes .../StencilDrawToNonMsFragment.spv | Bin 0 -> 1664 bytes 28 files changed, 71 insertions(+), 2634 deletions(-) delete mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ChangeBufferStride.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitClearAlphaFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitMsFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitVertex.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearFFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearSIFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearUIFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearVertex.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyShorteningCompute.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyToNonMsCompute.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyWideningCompute.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorDrawToMsFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorDrawToMsVertex.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertD32S8ToD24S8.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertIndexBuffer.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertIndirectData.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthBlitFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthBlitMsFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthDrawToMsFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthDrawToNonMsFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilBlitFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilBlitMsFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilDrawToMsFragment.spv create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilDrawToNonMsFragment.spv diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index b19a4f704..684ed068a 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -1,10 +1,11 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; -using Ryujinx.Graphics.Vulkan.Shaders; using Silk.NET.Vulkan; using System; using System.Collections.Generic; +using System.IO; using System.Numerics; using CompareOp = Ryujinx.Graphics.GAL.CompareOp; using Format = Ryujinx.Graphics.GAL.Format; @@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan class HelperShader : IDisposable { private const int UniformBufferAlignment = 256; + private const string ShaderBinariesPath = "Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries"; private readonly PipelineHelperShader _pipeline; private readonly ISampler _samplerLinear; @@ -67,40 +69,40 @@ namespace Ryujinx.Graphics.Vulkan _programColorBlit = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, blitResourceLayout); _programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, blitResourceLayout); _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitClearAlphaFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, blitResourceLayout); var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build(); _programColorClearF = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorClearFFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorClearResourceLayout); _programColorClearSI = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorClearSIFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorClearResourceLayout); _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorClearUIFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorClearResourceLayout); var strideChangeResourceLayout = new ResourceLayoutBuilder() @@ -110,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan _programStrideChange = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ChangeBufferStride.spv"), ShaderStage.Compute, TargetLanguage.Spirv), }, strideChangeResourceLayout); var colorCopyResourceLayout = new ResourceLayoutBuilder() @@ -120,17 +122,17 @@ namespace Ryujinx.Graphics.Vulkan _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorCopyShorteningCompute.spv"), ShaderStage.Compute, TargetLanguage.Spirv), }, colorCopyResourceLayout); _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorCopyToNonMsCompute.spv"), ShaderStage.Compute, TargetLanguage.Spirv), }, colorCopyResourceLayout); _programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorCopyWideningCompute.spv"), ShaderStage.Compute, TargetLanguage.Spirv), }, colorCopyResourceLayout); var colorDrawToMsResourceLayout = new ResourceLayoutBuilder() @@ -139,8 +141,8 @@ namespace Ryujinx.Graphics.Vulkan _programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorDrawToMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorDrawToMsResourceLayout); var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() @@ -150,7 +152,7 @@ namespace Ryujinx.Graphics.Vulkan _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ConvertD32S8ToD24S8.spv"), ShaderStage.Compute, TargetLanguage.Spirv), }, convertD32S8ToD24S8ResourceLayout); var convertIndexBufferResourceLayout = new ResourceLayoutBuilder() @@ -160,7 +162,7 @@ namespace Ryujinx.Graphics.Vulkan _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ConvertIndexBuffer.spv"), ShaderStage.Compute, TargetLanguage.Spirv), }, convertIndexBufferResourceLayout); var convertIndirectDataResourceLayout = new ResourceLayoutBuilder() @@ -171,61 +173,66 @@ namespace Ryujinx.Graphics.Vulkan _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, ShaderStage.Compute, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ConvertIndirectData.spv"), ShaderStage.Compute, TargetLanguage.Spirv), }, convertIndirectDataResourceLayout); _programDepthBlit = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("DepthBlitFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, blitResourceLayout); _programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("DepthBlitMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, blitResourceLayout); _programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("DepthDrawToMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorDrawToMsResourceLayout); _programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("DepthDrawToNonMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorDrawToMsResourceLayout); if (gd.Capabilities.SupportsShaderStencilExport) { _programStencilBlit = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("StencilBlitFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, blitResourceLayout); _programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("StencilBlitMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, blitResourceLayout); _programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("StencilDrawToMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorDrawToMsResourceLayout); _programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] { - new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv), - new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("StencilDrawToNonMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorDrawToMsResourceLayout); } } + private static byte[] ReadSpirv(string fileName) + { + return EmbeddedResources.Read(string.Join('/', ShaderBinariesPath, fileName)); + } + public void Blit( VulkanRenderer gd, TextureView src, diff --git a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index 20216e51e..343951186 100644 --- a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -21,6 +21,31 @@ <EmbeddedResource Include="Effects\Shaders\SmaaBlend.spv" /> <EmbeddedResource Include="Effects\Shaders\SmaaEdge.spv" /> <EmbeddedResource Include="Effects\Shaders\SmaaNeighbour.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ChangeBufferStride.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorBlitClearAlphaFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorBlitFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorBlitMsFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorBlitVertex.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorClearFFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorClearSIFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorClearUIFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorClearVertex.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorCopyShorteningCompute.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorCopyToNonMsCompute.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorCopyWideningCompute.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorDrawToMsFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ColorDrawToMsVertex.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ConvertD32S8ToD24S8.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ConvertIndexBuffer.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\ConvertIndirectData.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\DepthBlitFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\DepthBlitMsFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\DepthDrawToMsFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\DepthDrawToNonMsFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\StencilBlitFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\StencilBlitMsFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\StencilDrawToMsFragment.spv" /> + <EmbeddedResource Include="Shaders\SpirvBinaries\StencilDrawToNonMsFragment.spv" /> </ItemGroup> <ItemGroup> diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs deleted file mode 100644 index f6ca2432f..000000000 --- a/src/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ /dev/null @@ -1,2595 +0,0 @@ -namespace Ryujinx.Graphics.Vulkan.Shaders -{ - static class ShaderBinaries - { - public static readonly byte[] ChangeBufferStrideShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x8E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x60, 0x11, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, - 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, - 0x72, 0x5F, 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00, - 0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, - 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, - 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x07, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61, - 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61, - 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x74, 0x72, 0x69, 0x64, 0x65, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x52, 0x65, - 0x6D, 0x61, 0x69, 0x6E, 0x64, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x69, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x29, 0x00, 0x00, 0x00, 0x61, 0x6C, 0x6C, 0x49, - 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x43, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x4C, 0x6F, 0x63, 0x61, 0x6C, - 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x65, 0x78, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x79, 0x43, 0x6F, 0x75, 0x6E, - 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x42, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x43, 0x6F, 0x70, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x73, 0x72, 0x63, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x7B, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6F, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8D, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x03, 0x00, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00, - 0x6C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x6F, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x75, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x06, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x2D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x57, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x57, 0x00, 0x00, 0x00, - 0xF6, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x59, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x58, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, - 0x6D, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, - 0x4C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x75, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x6D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, - 0x7E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x80, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x71, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x7F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x5A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x57, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x59, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorBlitClearAlphaFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, - 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, - 0x64, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3F, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorBlitFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, - 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, - 0x64, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorBlitMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, - 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorBlitVertexShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6E, 0x64, - 0x65, 0x78, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, - 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, - 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x5F, 0x69, 0x6E, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x69, 0x6E, 0x74, - 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, - 0x6E, 0x63, 0x65, 0x00, 0x06, 0x00, 0x07, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C, 0x6C, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x06, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x2B, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorClearFFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, - 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61, - 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorClearSIFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, - 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61, - 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorClearUIFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, - 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61, - 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorClearVertexShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6E, 0x64, - 0x65, 0x78, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61, - 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, - 0x5F, 0x69, 0x6E, 0x00, 0x06, 0x00, 0x09, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x63, 0x6C, 0x65, 0x61, 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x5F, 0x69, 0x6E, 0x5F, - 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, - 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, - 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, - 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C, 0x6C, 0x44, - 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x06, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x2B, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0xC7, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x29, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x29, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x35, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorCopyShorteningComputeShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, - 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x73, 0x72, 0x63, 0x00, 0x05, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, - 0x6F, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x64, 0x73, 0x74, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x76, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x67, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x77, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x56, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x61, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x77, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x77, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorCopyToNonMsComputeShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, - 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x00, 0x05, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, - 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, - 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4D, - 0x53, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x03, 0x00, 0x79, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x7A, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x84, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x35, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0xC7, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x5E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0xC7, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0xC3, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x3F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x7F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0x75, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x84, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x84, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorCopyWideningComputeShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, - 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x64, 0x73, 0x74, 0x00, 0x05, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, - 0x6F, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x73, 0x72, 0x63, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, - 0x3F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x71, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, - 0x3F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x6D, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x66, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x6D, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorDrawToMsVertexShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, - 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, - 0x6F, 0x69, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, - 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0x06, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C, 0x6C, 0x44, 0x69, 0x73, 0x74, 0x61, - 0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x06, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0xC7, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x2D, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ColorDrawToMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, - 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, - 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, - 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x34, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, - 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x73, 0x72, 0x63, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x5A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0xC7, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x3F, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x35, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x53, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x50, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ConvertD32S8ToD24S8ShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, - 0x58, 0x54, 0x5F, 0x73, 0x63, 0x61, 0x6C, 0x61, 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F, - 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, - 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, - 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, - 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x69, 0x6E, - 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, - 0x6F, 0x6E, 0x73, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61, 0x72, 0x67, 0x75, 0x6D, - 0x65, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x61, 0x6C, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x43, 0x6F, 0x70, - 0x69, 0x65, 0x73, 0x00, 0x05, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, - 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x4C, - 0x6F, 0x63, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x65, 0x78, 0x74, 0x72, - 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x29, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x79, - 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x70, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4F, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x64, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x05, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x64, - 0x61, 0x74, 0x61, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x63, 0x61, 0x6C, 0x65, 0x64, - 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x76, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x25, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x4A, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, - 0x2B, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x4B, - 0x1D, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x6B, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0xA9, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x2D, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x41, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x41, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x5A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, - 0x6D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, - 0xC7, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x6C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x71, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x44, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x41, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x43, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00 -, }; - - public static readonly byte[] ConvertIndexBufferShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x61, 0x11, 0x00, 0x00, 0x0A, 0x00, 0x07, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F, - 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, - 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x63, 0x61, 0x6C, 0x61, - 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F, 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 0x00, 0x00, - 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, - 0x72, 0x5F, 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, - 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, - 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, 0x06, 0x00, 0x09, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, - 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, - 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x5F, 0x6F, - 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, - 0x06, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, - 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, - 0x62, 0x61, 0x73, 0x65, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, - 0x78, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x5F, 0x6F, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x74, 0x6F, 0x74, 0x61, 0x6C, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x06, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, - 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x69, 0x6E, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x87, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x0B, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x59, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, - 0x5A, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x5A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x5B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x61, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x6C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0xF6, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x8F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x59, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x6C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x52, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x73, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x8F, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, - 0xB1, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x7F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x90, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x75, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x88, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] ConvertIndirectDataShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3D, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, - 0x58, 0x54, 0x5F, 0x73, 0x63, 0x61, 0x6C, 0x61, 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F, - 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5F, 0x70, 0x61, 0x74, - 0x74, 0x65, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, - 0x06, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, - 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, - 0x65, 0x73, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, - 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, - 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, - 0x7A, 0x65, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x5F, 0x69, 0x6E, 0x64, - 0x65, 0x78, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x73, 0x72, 0x63, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x74, 0x6F, 0x74, 0x61, 0x6C, 0x5F, 0x70, 0x72, - 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x5F, 0x78, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x79, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x5F, 0x7A, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x68, 0x61, 0x73, 0x5F, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x78, 0x5F, - 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, - 0x6E, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x07, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, - 0x6E, 0x74, 0x5F, 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, - 0x6E, 0x74, 0x5F, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x69, 0x6E, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0xB6, 0x00, 0x00, 0x00, - 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x08, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x39, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xB5, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x03, 0x01, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x9D, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0xB5, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0xB6, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x02, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x06, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x37, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x47, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x59, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x37, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, - 0x37, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, - 0x39, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x8D, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x9A, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x9B, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x07, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, - 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, - 0x09, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0D, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x0C, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA1, 0x00, 0x00, 0x00, - 0x0D, 0x01, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xA2, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, - 0x4C, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, - 0xB0, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, - 0x36, 0x01, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, - 0xA4, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x18, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, - 0xAE, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0xB2, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xBC, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, - 0x1C, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, - 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x1D, 0x01, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x16, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x17, 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x18, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0xAF, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, - 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x00, 0x00, 0x00, - 0xC5, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, - 0xAF, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xCA, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x18, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0xCA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, - 0xCB, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, - 0xCC, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x30, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x31, 0x01, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x00, 0x00, 0x00, - 0x2B, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, - 0xAF, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xD5, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x18, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0xD5, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, - 0xD6, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xD8, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xDB, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xDF, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xE0, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x11, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA4, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xE3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x40, 0x00, 0x00, 0x00, - 0xE9, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00, - 0x33, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, - 0xF6, 0x00, 0x04, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0xEB, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xEE, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xEF, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, - 0xF1, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xF6, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF9, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, - 0xF1, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xFC, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x3B, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] DepthBlitFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x44, - 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x78, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] DepthBlitMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, - 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x53, - 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] DepthDrawToMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, - 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, - 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, - 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6F, 0x6F, 0x72, 0x64, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x51, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, - 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x51, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x09, 0x00, 0x52, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0xC5, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x53, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x5B, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x31, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] DepthDrawToNonMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, - 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, - 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, - 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, - 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4D, 0x53, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x6D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x3F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] StencilBlitFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, - 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, - 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x13, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, - 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, - 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x53, - 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x53, 0x74, 0x65, 0x6E, 0x63, - 0x69, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, - 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] StencilBlitMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, - 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, - 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xA3, 0x13, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, - 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, - 0x72, 0x61, 0x67, 0x53, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x53, - 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] StencilDrawToMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, - 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, - 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, - 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x13, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, - 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, - 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, - 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, - 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6F, 0x6F, 0x72, 0x64, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x51, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, - 0x72, 0x61, 0x67, 0x53, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0xC3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x4C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x5A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x51, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00, - }; - - public static readonly byte[] StencilDrawToNonMsFragmentShaderSource = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, - 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, - 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x13, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, - 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, - 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, - 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F, - 0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, - 0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, - 0x72, 0x61, 0x67, 0x53, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4D, - 0x53, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x96, 0x13, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x09, 0x00, 0x60, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, - 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - }; - } -} diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ChangeBufferStride.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ChangeBufferStride.spv new file mode 100644 index 0000000000000000000000000000000000000000..49e7d23f9a9c463775404cee485a872fd0d636bb GIT binary patch literal 3812 zcmZ9NX>(Ln5QZ-#ldvxW;syk8Ll8v)Q3S(gKu4nqBCf+Qa}!2~nJ{w)37{aNsHp$L zkNyRJl%-Z#<@4O!rf%h?y87+6m(!=unUkTVJ4chsl4M1)I{7VG_|_*&lO;g)h4o3Y zG8xJ3iRyH9Z$E49-GAUkBiAHDd4jQPljTVTJ&atgw>mmjAqKXQ9<q))`YD-=BjzbX z{4GWQi7}ikOD3wdQ|Hgt`j_g>v{yTHv6a>OS+`f8P4%zPKQTEuQ9V^_%+2ANZ>P0( ztCQB6tzO#5T37S9yiO6<>NMJeW;vijrE;$odRs;h{qCUGNT;)2t4WoFJ&m#=e9qMB zz1hL#w3GGqjj(5ZevAFJW<9IJSJ<0#@N<m~=!`09R$il$Y=*x$n3+j?)2-{QZ0zH3 z3;j&a%=FU?Gv~L-=lx8j&YCH~SCXCZt<KeMqn@?8oqits6kMY_*Gl_S>6HQVX<B16 z*#%#3x6eEXKamIF8&00j>on7A=n7Nu3}@T5Duqi4XHG)r@dwc9wXC<W-%4^6n>FW8 zbO#-qwzuQxJ{!+iXTIR7a=pfb%^5cZ*X(B{ZYVhi*UEE-k{bGp91rKZJK%C)kMJk& zCs&N0ibMJRlXq`(=vF6BA)}Jp`rz^u<GrhX;u~n|p2mu^cFuki_~pF26V&#ccOZUY zZ(lHT+c~a8JXihhX$0Zg=k=ria(%g|@9)U^#+$Eg{qT3e<$CVbuUt<q;@xMtp0@R( z{;y#BeH7V1mU-LR^AK9Ri5^^U3^8X6dlaq6Z^m9;V1H{{i+RZzYi;W|!^4H`4CQuX zTVpli=UQvfV;X$=wb$m>LcMihbIl9?MzFnw{RlYfoBssjeqy~R!Q+U%??oG<-+o^} zTT9-4wLMcg{rd`R&PG;w4IOn~FMRIf64<xKZwuAE2XfBG^VYWBmuTl9=X%<nxtw{P zcjS4l+U8wB8zX0)wr4A+e^6j^V=ljd$B{iu&;5D!a?VdV$o+S2o}-^L`5UH!b`8(m zJ*mU?tt;EUab?@zLD}|QE89C4*}i3E|E@*0?^fCGyHvJ)m&*3>MRs+OJ+;X89V+Mh z7L{$^3hiUe(7WjWIcM%2UW*)IPTtLj5Pe6PqBGNuoK4`!aqf}xFj${C&Q&{d{C!4_ z_d9YP1M4%#*=tA67I5VF8;G2(V14FzKHA|M1G{$E+rjpKhg;yjb|7Pjy!oD|oY=GU zw-vchgY7BoXTa9FSJZkIY>d45p1oYeKL?fzdpFoMB7YCq7<u!(D{{xEzYqNq;%_(3 z#oz491%I<Rr~L(IzIRJI);kE63;Pvtxt~MW_H)13&#Pc#<o|&4Z8(frN8j&E&>0>< z<b5x^H?HkHmXEdG0K1>C$HB(jF4j5*Hf9_#-+M0?@h8CL{FB)3<!+IG3T%vg<h%(k z?{5OTyuZ`f#>hv`8L)L%@vCreZy`$%&(wJR(evA2*EPnT{cXxct#`oQ$FSc88x!^< zco`CVcn@rhe5~<4*gE=Lql%V`8q?r%&9m6v_o(>+*cf^9eIMk+=g_N>e|Qr=MEf?X z|Kcuvg#H-u)Bg!tzdF|b6l{&KKLdMiaaTSE8zcWOHMP$p*0z?|K75m+j|+uQZ66oW z4aCnrnrL&?v2F^sM%Xi8=Nf&?f{l^45A8Z)ZEJ}m_cGWS#r!*9Iq!DNPcGv7V9zAt zGq7C5>yz^h8=$WuJ;ePQXPryvuMm9^cMaS|jB|b0mNU*dI1_tUM=$eW=McSI2g^k- z`sB>NfHvP5So;Rrwe;OAd}`0&8}u#2PycPSx$0Q^Td+04z60Kd#2I`CHb&kv(Eb{+ zwzb5Odly_jgYU8BVjueCt`xKT0qmO?v%3e5*_r45<cza__u`uBSo24){m1Nn0?Wmk p`sB*<v3G5MC)QI(y`RDUHlp5ruw2yBCue^A_WWJ^$7^#4`5%KlQNsWL literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitClearAlphaFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitClearAlphaFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..84d317c899714ebaa8f04f471020eaa2df52345e GIT binary patch literal 656 zcmY+AJ4*vm5QWFAd6;M(1`=aH5-o*bp(2P{rb(e#Sc)VTg1QhRSo+)iRW^d(x5<JI z%+Ac2bI(0@lfpqcVlApsiGD1j6$OxpT9mRnp4?7GZ;RRJ_~b}NJ(7$lrxE2?r<yMh zPp>XE$QIdzC(Z?Q391oa94dnT<vV+3nm(uRbN5MXF)toJ=!=;a{rxme=QHr}ioZv1 zILqHBy(6E8^oFy3a()|@ykX{Qd8R{O0vnKB_<DBM=YVg8>Z`@iSF@hQdbqDr<@3CM zk<WdYH8%3p+g!24)N}R?hW9t9>giK{ld6`ud}rSzcE~KZLq>dFo$}8-r*}h--ylWm z1*mt{N$+!3bDLD}4wi3hlXApwz|`-O;zo|`(2MCK$9gMF57(%vw@Z3nzI(l1;>>^O J?gxc4@)y+P9U1@t literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..9bd6a0b2c5856822c418a71287d46bdf7bba2fdc GIT binary patch literal 552 zcmY+AO-lk{6oqe2qo(C&6cL2hURq=Xky|(JTC|MN7C|*&2K~N%RU4t_8D~H*+`0Fh z_uQ{ZDp$3Ly=X)|j$>opsK7)tqngL*>~S{wS}jI5<7*YINOGZ?cGO~@sDHe?efn^K zckvE7c?OUrbdC6>D23hDi>;Yw@7ed#_au(MtJfd$H19?JJkPS_0)ATPFUU2q`b*py zB_5J%V)rTaeN^g(`Bs~n6Y?tD06#_V&SLi*@U0WQTK>Mk?kso4_6?$X&fBpPpQCK@ zqRxJYg-XtTVsFr#-zM76J=J%J_L5gG_9nSo{*gL&)!gY1CM7<=I-`Z#Uu=&aZv7J~ SZ|dV}<R^6SHn}wu?(l!U3>lOF literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..ea73a84b00d81ba3af0db7ed6d648c0dfa9b17cd GIT binary patch literal 808 zcmYk3?@9tu5XC3AHEYX1m73X~HNQ(lMG!?F0tx))11#c71*<D&&<pfpJyl<V&TqYD z7iMS9xie>G?s~cQhS{10R<O_(r(!uVX85-A7xIDSGrrsF_d0L$xN~`RPDRl?Ty|HI z&bnf3Jb0bJZ^|mNBUxFw+^MM|PaVv@)XW$AzXnX*Fiq0;8TX#;2+v0!iv3JaaWqWR zSu7q7{pTd=5607E^z>+5r*R^l14iRaM&BiFDdvEgbCvI9nnibqejwYG?MVTRR<^}L zuckOJE|k@!#g1V13iU0>^HT7)4znX%L)&i2ldtFaJH$)eR3`n&LvPz<!J!9cKMws% z^5`)q`DJ;u;K>KGFI-i)DjzybeJ$fx^-Xt29`#_}g!*f}KYiGh!%p8~eEB=E%*Z=3 zAD9_<NBYOY?6fZ<=S7&_b;qSH|G;sdQl-8jLj!)|Y7QMw59U0UG2^la|CSz+{sRBu WR0c=ggN*!pP0xSf?nm3*$bJDTxh5$9 literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitVertex.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorBlitVertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..b2c8511560025fd4283aef8b56bb6c2b5b9adf63 GIT binary patch literal 1564 zcmYk6TTc{05QUpvXHoEm2x!E07QAEDD~KW>!30fsFd^}EvtcJ@l6A)HjtTLJ#Q4Yb z!N25-iRbI?+Dwz8t4^IhRb4&MY^+W@H|1vBS-0!LnsW^y&ds`(-wzIt4!h%O(0#G~ z1ja?z^h7w9bdGgK_NAW}_%pJrvPZI}ej3_;iJXX4=OxmI`+3;=l#QzF<U=vYPT(c} zypY~;S&jzcz+Kcn!Fr)qOZCB7^;~bzulm9g)xYMmLF1u()%%!@LP=6B{eZK`mE*k1 z%R=b1$GNA+^P)P+zi03)$K90MALd`*<>RVf9A}dpQzG|mIGp6@hq<s0G!5=QE2BO! zb8E|3@72BCZ8bpRG(ARL_}pz}k_&N1F+FqM?nMojVdTN;JT$@R+j#h3yc@OAgEz0= z>>Xin@E62tX-{S7ri#x+AABpu+?o7Z<e!i6qLk+3r?MsGu4pGl8?AZm)B;C~n0bN0 zU(gPpUfV*M9T;bqw4(#YSz=}fW_?_j&w_tLJ2~{Y7jKfvSn!E&`6%#H`Sbzb^j74X zKKy&KDcQ2lKhO>rtgWdrSL>e~Mjvb1(Irpc#NqEM)IsC1Olz0A#7}+b-dg-B#J(^3 zcrCNO;J@+tQ-7ZPwv3t^`hMUsPZro;A&K^Fp}Z;I2JiVtDDj1e@ut5ccDaV_iN$P} zWz3(Ma|S(nxhW&Jr?aW{j*K^E);sEe7|v<bzAc|QQ^U0H$Tw~BrhQjFIn%x`A8p<M zZDKfO?4>K8-hQYSz46ZvjCucz*t3W;bLywspUZe7?q+?1!8Pr5`Sfhs8}h;G+AkG@ o*R?klo3`~02G{Cu)i}I0cLU=)FwRbm!+XO4<6HQruXI!PAGT0rzW@LL literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearFFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearFFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..556384e686d3e85203648a4e646498dd11489b65 GIT binary patch literal 380 zcmYjM%?g5G5S%o%v|oa{h0$F)R0L61FW!292<s47g4GN5R9%8*zBKw^-JRLloz=Fc z9#Dgi2HN<(J}fdo0N3O=*(K5KevIaenG_w^Mkq}e9_q}B%i(-gqsa&uJ?g~MfR)Xy zz>`A`*?+yTX3{*%ABFm^nNPFRq1aa}5SO|!wR*zxsqlaf@|YMmn0m<CB^NSA)aw>u z`G@>AnDsZ}*Cp01;?(z<dGfC>EKhMuUMfHB($ma~jKjPktDP-|^jmhdSNP5872^ZR CVHNfO literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearSIFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearSIFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..0b5acf01bf80eabdfdb5cfd98ddffcbec9f76d46 GIT binary patch literal 428 zcmYk1%W48a6htdd9LKj3Om;Goy|_>bgk<Yxc78w*%|b8=K6d#!zsikZopuInsJ^$V z>pnUvy;r0s)s(2C`{}A=MXD<fahA=q;o-O%jwd4=Ek(hIr>%;f9l_sXz2%{5G>wjZ z=pNuKa#!i>qO#RTKb)H7ZnHb>d5=T=a`U^`{}ie;%YA)&IdJ9_{KXTPF$mKX@}51h zwbWqWH*@wuV~5mt;>eHCKXcALXs^C&j;TRy-4R~YHk{KJomx0Mp-uf0_QA`c-^EJV a@hk7ua*qGsocV17?b~j4fM4D1!?*#t2^TN` literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearUIFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearUIFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..0f5bba47ae42a179e5659e50d95cacd9448c8255 GIT binary patch literal 428 zcmYk1%W48q5JW3Z9LKj3Om;Goy|_>bgk<Yxc78w*%|b8=K6d#!zsikZo$CzP(6_tl zR==XsdqsLuO^G_XTURABQeAO~vuvIX569JTJQ<;ADGEX~ZB_K_i2p6tTRy7xroCew zyazao+*LZesBHF852j|h+w4wz{^L-;-25)~KLsnza$VnA49uJYzjy*O24R{)-m@k) zm)fy6WA=e#htzN4$VKp<Ic6WYSHCsK)J|>P5nR+ZocpA}QwwG%xT!B;AG8?#E@sM( ZUwN;VbM*hl%x~M_zO7~l@YUTu>^Hbc7cT$+ literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearVertex.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorClearVertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..568f413560ba0c495dbcc7bd53bb2c9debfa69f4 GIT binary patch literal 1424 zcmZ9L+e;fk6voHIto71XYt>$COpLcy8+%a^N-t7D3qA;fuVG0RSy;1@tRQ_7r2km^ z;J@UH;P;!I2}@5nnK|G2<~wK3B;`_L#F!y7W*(UxliQRj2{C5e1a>_*Iyq|gvrcns z`vZ(8rfi9Dp6VPnEI$w9F8eY0b9r4})~BTPugDd#ioA;S;hrU})2Nq4SHHTQ=n7t7 z&$p8(?6ulyl3w;iv-phGBHmKTz>FGj?7v*A6J{ZJMSaY%k)gk!RdtV}US1kJa5#H% zX+O^56#Ws)=e`z?yV*(nJA&tP+)=roNqn&%_p`9ujs`iVMeZ_5208j+KG=b#!W_os z?_A9Ms&ed59sBz=a!6;&7Naibx!d9(m&fSSGv~Q4Mh(nk<iUzOG{NZG^Voy&e$++} z-n711>%!o$pAjq2T9cz&Q#|VS;9p$Koyo5|{)CHXr8K2|O+KgGGp)qbMr&FtwZPFL zW?o=beKRX^aA&pVJ>^_tW(DT!zR(_n|57VC&U24>i|b1UpZJY@NItKaH=;MNs&36( ze81#ybZc7ak32mS*X3a7qOl~GefRN79{=!l*Yeovz15zxob!D9=X5cBew6!r!uu>Y z=Gy9$-;$%Tqu&QS=8J*d38`rPDwH?l?Rl?Tp~R0J#+&_f*kl3Q6UHoG$(c7ZMvK|J zmV<v&CEkrVRgNATa<p%Bo_N_|%;t~7RvpfK_S6lt;D5tBsl%Mm27~LhH?*fmul-(o uu%dQTF?dmXQ?b|PozVt^>+63iaF{zBFy6#-whJ6)4+o5Qxz{ghSN;IYi(t?I literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyShorteningCompute.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyShorteningCompute.spv new file mode 100644 index 0000000000000000000000000000000000000000..d5e50b954083becb4ced18bdb0b7678dd20c045c GIT binary patch literal 2040 zcmZ9M+fGzL5Je9&45+9;5D+ywh_{I1C5m`UhQtIxBBIe3FARbr3XB0nO?>ckeDfcS zzhu0{#I@#h#S@$Cbk**vUDb7ZD3^xXvr0?Wk!{P~XZh;RN?8l0`}wqwv3F!`O@3nf z-t@@E=HkeOi)YQ)ot531XPw#htb*MNR_B-3<adHTphg{Zq2qZss3usi%wLK93r>ZI zqDF=O)LB}Yn^<W)onM(;+iEP#Z!S01CU3x3$hF4C`T}+(8^vDtcrNduEpgB}&u)%A zxwPx-iyNES1eLAT0S*9twGRXP3x5#Xb+e#7lvBi%n>|Flb<B5}uOIpPb$rjuApC#l zd2a*hY>VCA^n2*Y8iQn-tKExU8+#SF`tjS@<viElk$iKi`0BL)`K!ry26F0kvR9LD zeiyc!dINm(9(MJOFRt+S)7>5pjI&;E3X-#)zB6_Wr+RyHm%LK8Kk?S^o4AbAKLM<B zkQ#pXyw}$361M%xSwr8s%h|)_WE&q^Q;7?I7T+0p=LhW8^Idx9C&`aH;x?E1E^Ex? zW;50v^y`U>8gKBc;1uf4e+$S7?>5T&Qq#IF`}qOm@9aC?TS?Y$Y-jThc<z3kEH!rl zzvm1u!MCtGaq=$jV65MxAM}Ame7~3POm2yae)oNe^ZU%gIg>*mv_6m*??v7i{UKnk z>oc6+^a!W|&pweScNEBbx8v-tU!2Jw19IL^JL;drw!Zg>`oq}rQU5fy^^J-8BiP1y z_7ly}cK^nt?B@)yALnVjb?xmOyUX5;@t>iby=klGx6zj$=gz*lD_{&bKWFZ2t^)b5 zX`a`xot=E#v4$OYyhB_&{x~qU20o_T8;SGnMBD_nd<{h0Eo^hAK*Zg~mal<`yNhj{ zzlDgqk1byV&f9nK5csa*-8{nf-OSOczn#ZG&bU(b-~Ig^&4W4!tp#lFHpB1C+-Jai z@&EEazWL@DmsRt*s{0(&LFm4~b{3)g5<7HTu!~UFd~=K|>b}CQgV0^Yc6OoLzz*FK tb`k2DZ;o+A-PgEv5W4Hw&N_5Au|v0vU4**kn`4~1#W(0%{KE~$z+db4eChxI literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyToNonMsCompute.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyToNonMsCompute.spv new file mode 100644 index 0000000000000000000000000000000000000000..23ebd33d50843fcd34f8189a72f7655e376e72d0 GIT binary patch literal 2044 zcmZXUc}tX06o+3m&9qEur-f0AB2C&X+id}piV7i$qG=k#glQ(4X%8w}K1=`g9a`uU zMH@lS^Uggu&>0TroZngRx%Zup_Ti;j)|T~T-P!xBNds9sNR}!0COLuZ&AM`Y>io?4 zv3g^6Z2$OfGy1X)MebWkOegvh_+DwgD!v@<2NxTF3yJN~!LJ?p38RyU*1S$;GIy?0 zoT}7rmnzfMN40XPF<+}rp8@YAcMWp3-azlpXRVj+-LKp!mTM2Ije4<Co7-E=hv-Uv z!n6Nlnzi=pgq-6!vi_iaVFo#c?Hs-Erexc92=?UqwFhBoD$TV4{Vdhop)}kSxnIn4 zn`>WdNntP6YR;lR;+?J4{|4LH?6-<2;|7_*+0@t2qYGJprFGa`dXDMW?n$;e1#IiJ z!Qw}f?GD7Ow-Q-Mw)y?&V%FP`En|*medAk6zhsZqO*N0Thqil^R#Ry`as=GjJ)0Ev zHgs#-dk@myzMU<c+TDuTV;|C<V)oE>-(uD>Zyc74KY-LH9=#4FEbK{a_Z0c((4EC| zKS4ULc94458_9f%ZJhm{r64iq)AoKP&-MywJ<k^X7tjmvC?4;}`xlel%K_e;XD~-{ z)}J7Lmp`!H3POL--RnD8zw#Ja)?5L5-xql)zLma&iA$cxSnqi)?7iK^_8qKC*kw98 zyWdRYy&%TjZcex{+6CA==_8nTKL{IFfIpBYHUx`%j;%=PH$L%USj@AiNB`~U_HTou z{|<EV=)V)){>DWA5p?79{i0^nR(@wI&NB)-kGnJ8zRos=l$_0&U9gz5sax-Dvc*rb zDc{~f*!St1^lsd-`<a0CMciTZh&zNHaYxYgEu_63MK@PGa*v}&?l$zuJ%O$-;!d{2 zO`=EKsXS(}$7yVH#lNJ!7tsAK>>Jpngc<ia#a%)7j-4}RzKS0C-%{K)Fk|(_y<SIO z0k_`k4Q%nKe-mBI8KeF!^q3{;m%xnG7xR|U#XZB<G{-Euv*?Q+Rdmnbo&&3++t<BZ zBG>Pt0UN8&UHa`jg#C7Wm&QDX`(XV~bG-R~FpK>J`(?t4yuVlIuM=kNN!a<y#9G%{ P`di<cZ|*M}n}GiTRxFJ? literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyWideningCompute.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorCopyWideningCompute.spv new file mode 100644 index 0000000000000000000000000000000000000000..a1439ca655375837d9723e2c22d8c721b729091c GIT binary patch literal 1968 zcmZvcYfn=_5QeuvK|#3$5Q&!@K%)^PMgj3cgnB6$5{i8BmNcbRApxqC_{ksSH~)k2 zFBxw!@p*dA#$)0%Z)WD**_qkdJ?-q6>Iua{=nKcfVMt{tbc6z?`g*1p(S4!2&6n4= z)@NHsjoG>RYwqX|ovQZQKsX+X*j-@1wtFCd0*r!rPy&PGaeW9R8LZdIuLJ!Vr$|IT zN0I)_^JcGF-fM2x_SO#GH|w>d-R8mC1Nb6)t<h*5VHd*<>_eBUX%F2Q2c7b0_t=x7 zZEvl^dMH!bT7BRw&^Pxa@TAe7z*fpx?Q!hYT%D=*YPxPep3S}1G?x<nzqz{FeYhUu zOW67KCceE{Z<r|KCg{Q5v`4AYjZT0vhTnshb6vkT^WBr+o38-m?_|C+kTc%^I>~(Z z4`R!iZ=5?HMw{RGytuAdV<a`(vDVOcuF43RPNS#c&EwgW=wHM(_jyj~EYp6v!W_0W z<jkq>eC6D0+yYSS;U?NOdHcSH{$Jnm9A~JvOpI~vUqLI&z<T;6pv-Xh&gBuXrf^oP zXlwYMV^15{)`)v5*mCZPF;B7WHO4%{mdnS~@V&zrvyCkmW31~(n1C6&@P3;>PB_;E z`nHb!DdzhQ<9GZG@2w>A3)_2n2V7S#vCEt%f$wF5m*!jR&p3I-vl{C=IR(7;9en3C zmT{X@v|qouxbFio-qpE`H%31J?o&R&`F19NaS8ayKDkLC?>R1@m7lE1PXRg4q8;mB z!nS?^#QK-9<zxLT*w!~D)}O{U&b1%pjJefcnTq|)0Q+%v##`6kX3>hh8FLlL*_*cc zya#>x70&EiUIe~(@7wu0n_EEsOE%Bj*v?cwp7AbrJY$78?`#Pe>zcWi(f2_KI0x_Q zAuz6i?>A8f##H%#_MNX~oN>KL+Lzx$tos<-7$xS|#Lnk<f-jfPQE7AO``E%ZMu|C| zW9M_cz?aMC@cW5<yu>y}i8<=n`5X;=xqOao{FvhvwlPY4+pn>G+i_ky*nSHYChUCn cfiv(7_F#@W^LL-Iu7~8``}W>{^U@Z<A2+yl4gdfE literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorDrawToMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorDrawToMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..a56ed9681378f9c2e59748f5150a8ae893ca5c9f GIT binary patch literal 1572 zcmZvcZA+9<5Qa~#yOox2o0VCzCPWZzX=;LGSf&YLmR8W)A{r8`ZtUtG=%@8Uf2l7) z*Y)ff@Iyz2nYrhlmov{<OFdI(Q#zCS(z*00b<c3>0ZB<ch3-$4!e86i-l%tu_v^EB z_hk;GlB$RelHZH19PWNO60ak($SY(Bt}&Wnm8hK3cbb$zPv?}W(?_hcd-%2axv|$e zIXdn%nyrJGMp3kq<52#?v0f=%FvuzM=L`qU#&%I|eFdaa%mlXD-aUBQYPI*f+}>i& zy;iez((cxIL!i^%OV7!2CXf5@tkFlzd#rH-d)`Dm;ZJtmg6kXuIi}r@jAY*(=?`Y# zeN7-9YdMFtqnCcvu7ZkB7F+FdM#-sw)eu&kuHc_Xi@EEGJiAy8#NO8MK8xA+K4N$9 z&AW(rtnEE&IvnHr&hD|^5ZYYxB3{i{_*e0rE&Oq8Uz5Ihw-DpjyPIus=hJtG#xIjK zjjkcya|W&NJ<WZ9mM3nmzWWw4K9_BC%-=$LpU8iS?OfvK>-&Ag#@OmMTE6v8pYX?F z?hue?zt@>0W<7nsr^o*9(B9Mi&GYWm&f>A>ACNM78J2q<M*LQOAHTsP*yfKQ#)5YV zJ9t&>;EiG%`&Rri+-EmeJaWgdBiHX1x!16b1@HPPp5HTgH?WOG58t-A;y-iW+t_|{ z`$pb9Y`^m|oH_J7GK<7Lr?DSq{3zdhA!AW@5ns%GME@o1==VGKe+*_nW0AXzE$&W! zW!@8PYZ{BWR<NBbcH>;n5b>C6HDghG9be2n{K&O8u${pj1aA{t+?~Y?FR;xw=B{?o vZxP?o7PWjs?-6H}yAQ|r_W_aD;J>i%`D4c9`p$eqOT04Qx3Ryx>ILK<wr5^y literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorDrawToMsVertex.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ColorDrawToMsVertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..51fea565037df2aca23eb81a429f00c302670c0c GIT binary patch literal 1104 zcmYk4NlODk5QW>s#C?fNRE#?AOEh>;5yTCJD0mPAuOd3gK%7W2f_M_dKc)x&k{7}E z&2*dTP^qq0Z(hHynQUgh!<aVHZ8}ZGB&%RDLX7D#`8ZcEt}ZIg=)SVGy$Pe=WMd+n z0i(yY+7*wtLCC&aF|Nq!mr?!{F(*cvlan^QhuY1x--!H|^YGq(ftNSM1i$nfNlT^B z1ZS^jF6+%83hJTIj+l>^7!SheDtPtbxgH+WJE;Xvr$IBi4e$K_9yg))T&w-}a`KIT zR#mR?*`rufoXH{nSYRxNB{O51EsV3_agO7$2V)*pb<l%1tbg*XFgWZ-#mXyt3Uocy zU3L$?XKOstFI#@Et&37BXzwY;)EiQ!=4`Zwl|2QEXQ@XNXq8ltD#HhxmJ_@>4SEEg z#+30)A5YZDH_-AlA&kX2XqMv29J+cssgIlwFR5ewS?$s1ImchIeaBx_&CFcC9QWU1 zeeSicKx0Sm5FRsNf&B={DentquDl;+`Yn{Y&tlB=%VL8mtZwf#X}Ry>;B`s?#$7)w zHe+$_dn|SCHm6`-%*az5$Q2k|r@f#(XFBag?ZML8ORB-s+RLh)Ht!K_Fu3k~zHP^0 Yj&Q)3vEyu{ILs0b7<2yB7qhGQ1Mdn%V*mgE literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertD32S8ToD24S8.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertD32S8ToD24S8.spv new file mode 100644 index 0000000000000000000000000000000000000000..fb543e4433695e239d28199f178a1431ff52e5eb GIT binary patch literal 3236 zcmZ9OYjYG;5Qa~(mn0Z&a`Q&qfQksj8#hH@V-lB`)o6kSufwoA31cR^VP`gyDy!s! zmLK>fe)KO`TK*$Tt+L|#%uc6{8LH`i-|o}h=bYXHrQy>ft}^8Ix&7{2H~0>_VK*f6 z30KW^qq*FiO0)LV#Y?Xdaln=GKw=KMF;|f;OE$u|gJZws8Ofq#O>#(i@E?}cC9FeA z`ZFy3t3cU}x<)gYyK_58TVWFRf_q7~wH_qlcDJA5--o}kxY%gU1+C4^AkDUuC`jT? z6tv@B)XL(mXg5z07k64ozwHN9l*#Y4qV7l4L)_Www!$p#c2Y4_RdcS@-HfAjDSFUX z{<af8qBC(W&3dtthP}1^M%2ktQCd;Web}4v!zh{Q_B-mkTC8Ee?KE4?!d|wxx|&Av zRkVN3ijUk+N{2}@zuU@8-hg~%cU-=>(~chIv7`C<t|dWJO?wOGXXVc0YtqrftQY3E z;?7EIKifrHimA8@;!+w?o$Vs-3S6&cE#`68#Hn|WE9LhX=WEJt0e`fa-7oM5(s3s! z&ZXkOeB#<+7Aj{cpA9(nwj!NoQKuCr!d3UBa4%vEqV}wpSJ&$_40&SC8_WlnCzefa zW{&Tc5ckD*dZ2bW?gNflXlM;_%wj_fcXCR?XLxb|bIfcL|7_uJN>?T1qNeTFYLXw$ z^7FeOKRu8iOn$=}wg2U_efs&}EFb&z^MT1{{w>+``?TbUSolfxRFcL%rXJ|OCZUI# z@Q5@5f3EP4$*xN#gvp0*u&(UJWs?j3n*3nS5zf{hkj+Q_gVOj|PyG`)ufO!25PmAx zyb*(VdZ#yd@`JetIP$Ze``{!0bJF<WkE+s(($>eB!e{s`VR|{PYV*>yT*~f!Y0d!d zq<q{n`6gs@pI~ZzD9wK1=nc%B!r@;k?7<$(?o(m%g6V<$UrYE52RV9l({L!fv-AA| zeJ~%^2Xg?X&Z;yu;phXrR&ci0bz$B+FgbXzcRZ|T$<;mFmpvpoubblRnI&T2k;|HQ z&W_pV<G%h9Q<0_z_DMaoVcu3B^X~bWx6a44J^1V%d|?m1vIp~i`T6P|=8hLU%-yW) z!MsPTzoFf+modq-cE}7Lk}&7Y_I2%-dFJ0ac;eYB*nCH1!x=s*o18x>-{7~vZ<LuG ztmkZwNvOw6pOi57%sM#+b1>k)E9yTl--LwvoF&-mP0EHdd`h<84{r?pJTCfqK{he) zzlh^^^pb=;_<mN`^!>5~{)~jPpmt3f-fF!foBbF*C%YzjRMa{zn;3Z3bI))VKPB5= z|Ee&1`Jq_Pe@=*jw>7WJ_V@ROu)n`Ig^7W;H5X-*cU-s5-Y!XoB<ziN{MPej+0-S5 zo|#2B%XL*Yob~v&Y&hN=;+a)Ao2PeVGf&jA9M@&T8@?f%oYuq5+(*7aPUans{B>#W z<8S>Yr=|IAqW{#a%t+5l`0&q3<40Sq_hi>S+>p(^*eu+VO$_`$$_ai~LT++lTdt2} zvtR4`W7%-*)y@KLTBp7wy)5A!rGMhc!@S>?;A7tBr9UY+a`0OQTg^MN{hFT%!}&Gg ztmfykttN3+^9$MdsIejq#z+0HB-9<(WP$HWm>1?4A8&gg`B8I<|DNQoB$R9vduqvs zCqM5E*!FN=Hg{_07R!dSJ>Y}0UcZq|j^9*|^GYOe>k^CEkj*~+C}O&@;X0Dv3vN?3 yYw_9nJdnL6;ZEp*e&L8^hL|Vr9c}&hWY;Cue<~Z!`o{<7&*+TszdFUMlK%jz)Cy_< literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertIndexBuffer.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertIndexBuffer.spv new file mode 100644 index 0000000000000000000000000000000000000000..ac98276a4ab750d248fdd579dff4192f7d970d8c GIT binary patch literal 3044 zcmZ{md2du@6o)Teh8-k;ib5?GjR+}1P#YGt0v4na1Y>Z^<<8Q2!*r&dxozB{#MJE{ z)K5bE3VtW|nD{(*?$N2ymz<n)erG@Ld#8+!Ol?k*P06-oOY(g(ID3*2kR*|3dlGD$ zlbhGyOs`(Pk<QffBJCI5UbfiMdwWtVedp$H&QJG?=Je@jpAp}cjLJ&eZhXd($DpMw z?}+b!9sn1k1oFVwx}*FXf&YjyMqhLD>7`q5r2R(L&U$IR-EG`S+u4WR)ncRl{jAyQ z{j0-qG7Q%pC!e(!+v!}pThH28I&0lVR^;8zm5boo_rvo}v-Lq*UtL%r<#JXOtzOx8 zjh?l8^7``MrdqPK#O=Std-+mc<ZG>Tt<@{?MysC?xvlhZw*U4?%b9BI+Zt|Azd<kk z{3AAAtNL@Mjkb-k?pu33>$e68{)w$^j4gV3v%JfgjR%qRdyTZau+VR@<EXg>yyzBL zJ6(?Hn97}vCJ!la-1j?h21zxVQ{5xh%!*9enD-Po#?;|?rwk!tywtUUl_B~!1vqo~ zdxzVhgO$Dy?p*HWGVyUoQ|O++ydSa2A<X*_J2Hg1d$Fg6FmsC?AHv+Tn2$bc*oWBZ zA<UhM%?x4gQ|ufx$9(o|FV5#aABQ}lA0DWs{x-68oX5JcWAk~;CpKEjW96>Rr@#5! zz4@YEhCH0Fj_w)GCl>j<i{X6g=8ODaA$vaupuJSF&S9dBSKdz#)|-TkQQunWFy<fQ zYe`DXm`QZ==_`J=(tQVF$Ex_9$R7Q7!L^w;?0b>j`$6pSX2d4Q?*0R_xBnw^KZt!W z$DkK=?@Y{E#y$*LOMmyEO?)4c?>7A0v3Tf5k%#k4qkF=B2HAHP`UPZn6#7+U->N%( z3vLgdLwNaa#3qn^AL{n^F5H@8?pED5B4%6$kGMLrx^WG-K4QkH`xeBaAAekY|N6Hp zo4LZig1o8Hy{j)E{~l;p{}R%EoGp3xzAb&krRUg&^_<z`E`9}ZN57zbEy(!;+4u1c zq+LFXm+xdZ<Xh5yzN%{v;-8><Kk;|Um;>mKK;F6cY>YmSLgM%F@s1Bc#);d@V{p&! z_-mVh#Oz1;H@N+(i$|>^$kv*IqSh10;_KA1_LGpc#3Sx$WaExP5qAt(JmUO4Gfv$4 zC*anX|HRFi`yS*ww5N6G6!b$i<MYVcjsKasPQtZ&oKw5MkK*Q4j{eRf+us=|`a6d# zez%(IJhHQi&(e*%e-Sdyo~#|QFClA-*xAySB;q@&Szbo=J-Gwp^@)BiBAfRD6ge*; zi^slRK{khY)Oi)zJ=#m;nnyO5-(%!@4Ou*LT|+ij+}&S?yCZq*{##`GbZ<TAbtra! z3t79pxceJ$?H+fpU0vL~%F**}Wa}FrJue{Ja|?=|7m>vyE=Lw~4}sl579ZrQmEZXi zdK0pD?dJ2l??BqT%Qn0V`CFskZRiEQ6EXcFzjK>=1sdLybE*4(4d;``-o8UNMxW22 z0*bwTjI7<gySG)ic8`10t}bp~<>=*8<ZF=eEBHFoXV6Y4X1a?UGwJV4V){iMdm7}} jID<8v=@i`B@`zbSz6Zq~PE<Z8;l{*ohx|`o`3&?I#H|^V literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertIndirectData.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/ConvertIndirectData.spv new file mode 100644 index 0000000000000000000000000000000000000000..b924d0846ccfb29f1963082defae304b0217f4cb GIT binary patch literal 5424 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 literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthBlitFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthBlitFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..16de860230ee7f94f929bf183453a5348aa66b72 GIT binary patch literal 600 zcmY+AO-n*i5QZnOS1rpjL5U!=_R^vvh+0K%+(e6(A}JPBhPmkX^{d(hJ<quXHiu#6 zea|}|=VYa$YQ$R9qZXYgtQjSkfUBf97~T#0@5@R5<n&lYBeEn^vmMna6ZMzz)2kOX zd?$I9cm{xre3Lxml!%`+$Wg4;=FGHy_B^^-jAz%=x8)-cT^`mhryu_~=fI5~=JUmb zd_Xtr>v;PYTX%>%(|d6b*TnWXNGPI(N?k8)S=6-2D{$T9WnSzXB5@bgi=8RA7weqj z&i`4pKfI4}g9~+C>fKe7v!B=<wGOyJbiRA3-zM5iUcJ~i$f@5Ys`p>d_&4j!HFx}i q$%&V+&fLPCC$?u3x8FS~d$n*i^0#pI_HX62E4uoAxo@^O$A19B%^pSo literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthBlitMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthBlitMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..1b8dba6259e70dcee0df19e6c09d152552f5357e GIT binary patch literal 856 zcmY+B-AV#c6oq$=W0s|vshO33_YzSNMA3yLfj8j;h@@!XALd0b(2Mm{-2|=g%o(UN zEcRaO?B6*-qP1<dWf@Cb%c7IFgcLI@8un5)V=1S1d;MPfeKTrbU0;xqwE*Mpa;lS* z59Y(S1$tUI5US1w?oLgmMgE@rKwm=kON~QC{LK&5ml~7V;AuUabjGXAR8(=Hk-Hgx z{L|<qZScBWu17wO-pcO}=d0QHxno^*pk_uW32<sP1?Gr(Lp}#i-E+s_f!inR@2F3$ zL!qDw)M&V^qIxCyDQR_wq_IDL4ed^~;mm_pu^oNU9q$ky{Fql2xhqawUHSCppivLb zKAf^RbJB~sh|kGV3!Qj4`=J%Zk7VnflV6mN7U!S%J2dj)`~&id=ReVpcf8R%k%!M= z$8Ra2?7dWG-WQk`&P;iMc}C*wSP+PriZcsuhPL$aWv_jzD*49(HP9D6r{Z;bF!!mz o%yEx1`Scj*HJHC9ppo||5dWh3?9~*w=R9a5-VW_Yf8bX51q+!f=l}o! literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthDrawToMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthDrawToMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..9574918ae58e05bda715aecee9239b61d2baf1f9 GIT binary patch literal 1596 zcmZvc>q?YS6oz+>GaW3CX_+NsLIjbOsR@!{nI=d%nL)=vR4h<O9J_%otpnXse}bOp z`}PWdv}Li@`>x|&-ySO+<EK+PmAcZI^f8sEFLl7Aq#j3irCNcPSJzi3+Q-`yQ`2{i z>`oO`;p-v46I<Ee{CprkflMI{q?cHIH2Y_t$FA_#f&NCrDiJ61tJFM)uf4f{xVO{X zYJE94Za4Q@yOYhLK`r|U`A>}XD#;$6Du2$iyVqPV%B?KI)Qb7wmX0=epR`&>+huI6 zm^t%{JBP=gO1_ddFxp33=^5G1=5a@!W%|i^pJi@h@9T&s@K_0pSm)`^KJ6}~KV$bM z?#b94iia|G4r@m*anv4wl^-j%TgD7BPYte)u<CRPd=@R|&PVg?a&;JcS;PCxW$b<A zZh_6afOxF!J?a9EezCKAtk;V+*Szo#WG?V!u(Jgo!S;oT&AWl<x8Ch+%R8Ug9qJz< z>mIs}c+W|+*n66LA8nkxxnlP%r++%zWsT0X26hgy@#042hS}oF>@)x52|PmfIu7Hk z|0)N`Sx@Zu^EmGtwD)pfGrTjkvwNK5H%t}1K$Ls!L;N;=4|o2MX!H9KeGzvNJK_eg zBW@5|-`C<7QNA&A<s)|(J97O_k$VMOU&LKK5$E@dxNF$@qQ@w<x$-}9-<#NeWBW$l z9c;hr0x{F*8Dt8Hd-_ctWd0!Edp2`XcMdG)oudCdcJ%v|`#*xSpT5Xlz?S!S_cO;m z#<r%um}?Q+xneiY^%Rkhxt1~)wO7D$?%{i`y^8G&?jYixW6Qg<nBfJs`TE?IZ|OC% zfvi!>H}n>9R%5q`@%_C+jBE0rcoO|SbH@74d_xWLo&N)(-!sq4YoO)+@JpFR{sQ_p BVKM*! literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthDrawToNonMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthDrawToNonMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..3341b648dce69e38363e6560a7530447c4fc0343 GIT binary patch literal 1576 zcmZXSTTfI`3`RE$H$}N<01?H5c#R41N{j?WQ6Y#C!-FsCFb-tE07Dpj^U0s)CH^H} zOkC@nQ!<I2blQIX+V0)6E1lE5DRrd&)R#V|LWWZ(NCNB0?b7P{>P+isbLReo+cE}H zB`3-mOueZKtM1iy_D!5dtLP9saRwawr|>KMI<enr&<&%#wwsyOH+HKp4{MFZX7g~9 z5MEyz#kOjD2fJI<dh^TvQLDP!Y}~EpLwm}+-2XY&SJF9?h%-E2Ipg`(!O?bsccnT` z>#+WM9lONIJWD@1h%Qs<8I<Y5z^UYz_mS_6%g4DQo@$GoYd`zQcb@#HHxB+;Q1PjJ zV+Ch~njWwWRmO<jubA9XxF@k0YR*~4?l|M${db_^b7kxu#pDlQ<=e|yXRsL>`4N0E z`%RX27qj;mwm3u4+t_`G-70&H<15CV`FtJ~^%MAV?H~Ec5{uZqdzOf=;aAGox;to` zbq!z4o}TX`R<8H+?#6x(vGH^wZD7q=AEF=e1k4)o25P@gSVhc!#&rQ?mbc<u_oO)E z4@ft59yH#RyMMzMbnio`?@U#ky9d|rJB*rt%GTV|1=QWTPj_ff?|Kn67raaO!E=|v z%ka&8<3!d?q1I+-)Ly}l+G+f#y^3!xc-Px_evjbYz&95&ZsJ=j{<EBW58rQ=wPCX* zChsjg-^nA?9VlPRyUyW@|0sLS<BNHhnE3=hX8bOB3t)21MgM2`_U}NW|02G4^nZab z<~^eS5`LUF`Y(gYH5d0>!57caU*$Po;V+=(V#Ygs@8Gux>^;77-Q60szUvRDTyyTb thTTN#x$R0D*hcRA#(VSkwq0V@E}+i${=WGg)V%VT*ILDj{beH$(SKqNU?~6q literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilBlitFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilBlitFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..5df61b94ce3532a39ed0652600e0b021b00ca3f9 GIT binary patch literal 704 zcmZ{g&q@Me6vc1q__zF%l|>L^?WILU5CsM0#zkpVyFqj^!8FEM_#*WJy-82iCg}WT zG~9HD%l+>8&OP6mn@k*J%vP*mS=+WaDwYspMt!fE<t=BO+XwIZ>E4^Zbo+kbO+$a& z8;(4G_BIJZaSN7?W}2-|t1%7xjicj3)QXm*M0X|2SW0K|wL2VxXO$IYSv+u=`8$8= z?WDehurC=($w#y0UAC|OwPB7}%leL9;Eln^y9v63PVBqwKTAch_>QfrhW>1R7h`x5 z=Jh6%ps#aNs^}DyAM!=d+`bZji@vTiL662>)R|Sqf>)3JD4y9ACoRlXvTs*C|Ifz2 z&*nYvg6-)4bMrg+%dzNp#mwsh9(wpCz|f<|H?bvdQPow}bXQWP$0xI8Rdm6brRSf( z;A;($PxgJHJu=6c!0)#ZNA+AN@2@F&n;y+|C3=ryp|zoe1AZrr9n_TI#D!GZ1=x>V HoGO0-|8+Xg literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilBlitMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilBlitMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..4d130a59b091e6345535557d4c60d03a8fb5426d GIT binary patch literal 944 zcmZ{h-AY1H5QQgy`q9$LwEt4OkBEvOii%1CFG?f2JBXf4@F&!ZUZ6*)7wAoTs&0bT zcibnsX&)A|XU*(2Ywr_{Y)zPrS<)tL%SLO?B4W&laKv2~-!f(io3iT7y?6O==MA1} z4ZrUVhJLr+YJ2|6bFV*CZ_>sCjZ(Q<-X07a+k5*vqAg`nLQqR<W=wm~skOTBlfs;^ zD$J^vnhgH#KQuq8FCy+!CS&qaDtl*@^M9>aTwQ->#5ILE&9-;duQjV9?-l=1Dx%dN zw`s+p|Ke&R4qxKDdau`SxITPJr&{YgxBcr2D`^tiN#R{y$<Znbcn<S|_AGLAH-d~j z8r(hQYUraiFJz>EMj?2CkzQ8k$HnCZ{#jl*@5L*_AGsV)aLanHDOaOET<pVqRh<dt z^r6Rm794uy+=-P_FJAM)qH1YHa(r@jRuNrz&XRLqIQp}S_ze43TrKoY&KrdNyc0G4 z=Yjqc=h(Bj-<O!U;;~r#&IouV#{=)k{DyMww;<5-q#Q3f$MszQqT^nr%K0S$4fw7* wv+Q_g;B8I7b2wvNduDk0Gx*;W;5c_G(0?m^&K3pgtP|ac{|5K17dRAt0P~kd_5c6? literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilDrawToMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilDrawToMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..bd2516dcb35770ef791aa2fe01be2ef1195db21a GIT binary patch literal 1684 zcmZ{k+fGwa5QdlZAW-F0Q4u9Y5@KSkiUmoCm?BU~8Y~ou=bfglm87(!yCz=y4*CMV ziHG<~-kA7*d#@qA(A7-V%s>Ae)~vOg7+N^*+&PzV!|tt%*Q6T~;vD;e-#=%a8+9YD zS~&`~o*x9^`&y&f3&MW0Q$KD8&5x(uUSHgdOGmM#a<!Zf`;B~I`5sxvT!Jm~jjL8t zI&xAw?tted1<9UdLR@@m>`ecv`U(Apgnd(UN%c}H2gUu3|F+^%Vhz?vsqU!a!`jJd zyBXBGA3FUoXm?vnLDWRr`$hQ&$0=XJUBIc8(vO~7?VuW^+u0P7j?N%#yH{&H>2`aK zm^&Pu%j4B}X8X-|>OS@-+@X}PS9ed9g&tYVo>fv^FwS1miRj^`gk|=8Y(`xA8uLE% zjAY8&%ouyz+sqsLi{?u%`XIN}!nWL5q2Tk;O~jmO#iWJhB)S%NMfQkxFlImJdk4!2 zp%ytf&ziS657=$l#9frI$jv!&7+UzT>77Ns3GKuZXMP>!EFb)<5_&WLtaM(Z)m!v_ z^4;+|`REUu84`b4QTMgyB%E_eJ2vMe_JMYI;KX7xZ!rAJUXODmT}3v1SS@Vs18hc9 z-1k1>2bbBiVpipVNB#p(0;4`Q_s62&7uu=CyjJwi*y)``&EG;&+KXZ_*GUO?!+kLG zM`9B{CBbLBOVW*(m2SLg>G(cHUk3AwW5F$UM!LmvPZoPkIzHoFAK-C+#=9XMpVgR? zPAvEjU-y>uMG18+?yhw1wJ6TA_7zD%V&~+J9(ugv&%Np~%ey8UjCol7b?H{`m#_a= z81?X3Y*9KmGx_Ox8`8;%&-&VwPG2?~`r49!TVLBAv)ntf!I;B$pSvub9+-jgcBO+e zGwb1*bmH+bE8fzP<WN#kF5b{f3BAH?h{OAPC4m>{Uz>0Jwa4J{&Uiz6zLz)B@w3+T O^7gcY{n3}QD)|eP_j}O* literal 0 HcmV?d00001 diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilDrawToNonMsFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/StencilDrawToNonMsFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..e41cee388261f1a4badca0242703d5b82c51b4de GIT binary patch literal 1664 zcmZ{j+fGwa5QbL|v<S*U0*EN?T8}Xy9*L2l1%f47W2HvDP<LxvOHHAqt%(<2`40L5 zzKMtUO5T|Ge|xVXO^k~SGxN_shc#<AmCR?IOSo}2>fX8dOuM8I=g^;LoEvjTT&4Uh z+<Nge?C;hZ&0g3aH23T6PT2f#(CrPx9e0^1r&y|#mivRo^37XUsW#zKs3<n+vMwzf z*{ikp!H-Eo$&`5D8s^acs@^I6C53&{KxxfUEQRYk8~+`{jfgc|e?)W9qtyxT_iC-p zZnxKvE8eJ^lkV5{4m!=S-hID6=!czd>v|Xsmho}8|8mGrxf29+sKp(8k4n6oo#tCj z8pqSFE~DS8KdML<H6b&NOC}}Ts=zFqe5}MtMHqL7Pp`9wvDnWI`ShpW7wrr_{oq?Z zzVT;-g6E>`#++H@WP}A0&b)ly0u1h)xMR|Rgc!Z>*>#}*N2L=I@Kx_~M=<yk((tK8 zujA5z#NxBE!Kk<7?+!-odFgnDxHdlf0K4LAEy(8JGxN#Fi}M#{!==8(mpo=ZduJB& zFUU@LpS)`lo3$((jGD~%S{g1GcgN>Fz!vn)R;7uNZwK4_MKNpgs}kzHk>-F=55Eqe zv!ZW>zU+xZkKaN@q}PPnH^uHB>jl~Sl!WgL;m~(aT;6wDLVR6YV^60f?3R79LuxYn zX$djoosn%kc4@poHnC5dh`gMH+(2Tv=VV)MUbf|)mrcxg7l(Mfhw(1SCT2Y@%O)56 zhwpnsHg6UTVJjYk_e4CtlRFZ2!1?I!x+)v|yRWq-8;rYH&wH}1$1l$-2!l(^>Tk%V zenMjPH)Vrc{VmyG+{5Y@W!t<~e_I$_Vs_65vcUt%Pd~>)*#!wP>+xJRci=4ydnuc~ z?5?a_zUx;KxWw3ZO}ZheM>_4Q(yhqm8_(*u?A)%$$Sp|d&;5D-wuCt6uD;e#8tjiY Ha$E8jPTG5v literal 0 HcmV?d00001 From e61c09bc85eda86d207d557d8bde929fe69d5287 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 12 Jul 2023 18:31:08 +0200 Subject: [PATCH 705/737] infra: Fix PR triage once and for all (#5442) Switch to a custom made python script that query GitHub API to grab latest state of the PR after label assign. --- .github/assign/audio.yml | 8 ---- .github/assign/cpu.yml | 11 ----- .github/assign/global.yml | 4 -- .github/assign/gpu.yml | 10 ----- .github/assign/gui.yml | 11 ----- .github/assign/horizon.yml | 11 ----- .github/assign/infra.yml | 9 ---- .github/reviewers.yml | 32 +++++++++++++ .github/update_reviewers.py | 79 +++++++++++++++++++++++++++++++++ .github/workflows/pr_triage.yml | 48 ++++++-------------- 10 files changed, 125 insertions(+), 98 deletions(-) delete mode 100644 .github/assign/audio.yml delete mode 100644 .github/assign/cpu.yml delete mode 100644 .github/assign/global.yml delete mode 100644 .github/assign/gpu.yml delete mode 100644 .github/assign/gui.yml delete mode 100644 .github/assign/horizon.yml delete mode 100644 .github/assign/infra.yml create mode 100644 .github/reviewers.yml create mode 100644 .github/update_reviewers.py diff --git a/.github/assign/audio.yml b/.github/assign/audio.yml deleted file mode 100644 index 337007d39..000000000 --- a/.github/assign/audio.yml +++ /dev/null @@ -1,8 +0,0 @@ -addReviewers: true - -reviewers: - - marysaka - -filterLabels: - include: - - audio \ No newline at end of file diff --git a/.github/assign/cpu.yml b/.github/assign/cpu.yml deleted file mode 100644 index da824bdc3..000000000 --- a/.github/assign/cpu.yml +++ /dev/null @@ -1,11 +0,0 @@ -addReviewers: true - -reviewers: - - gdkchan - - riperiperi - - marysaka - - LDj3SNuD - -filterLabels: - include: - - cpu \ No newline at end of file diff --git a/.github/assign/global.yml b/.github/assign/global.yml deleted file mode 100644 index 53a9af429..000000000 --- a/.github/assign/global.yml +++ /dev/null @@ -1,4 +0,0 @@ -addReviewers: true - -reviewers: - - Ryujinx/developers \ No newline at end of file diff --git a/.github/assign/gpu.yml b/.github/assign/gpu.yml deleted file mode 100644 index b96d9d87d..000000000 --- a/.github/assign/gpu.yml +++ /dev/null @@ -1,10 +0,0 @@ -addReviewers: true - -reviewers: - - gdkchan - - riperiperi - - marysaka - -filterLabels: - include: - - gpu \ No newline at end of file diff --git a/.github/assign/gui.yml b/.github/assign/gui.yml deleted file mode 100644 index 9731ea5bd..000000000 --- a/.github/assign/gui.yml +++ /dev/null @@ -1,11 +0,0 @@ -addReviewers: true - -reviewers: - - Ack77 - - emmauss - - TSRBerry - - marysaka - -filterLabels: - include: - - gui \ No newline at end of file diff --git a/.github/assign/horizon.yml b/.github/assign/horizon.yml deleted file mode 100644 index 966382b29..000000000 --- a/.github/assign/horizon.yml +++ /dev/null @@ -1,11 +0,0 @@ -addReviewers: true - -reviewers: - - gdkchan - - Ack77 - - marysaka - - TSRBerry - -filterLabels: - include: - - horizon \ No newline at end of file diff --git a/.github/assign/infra.yml b/.github/assign/infra.yml deleted file mode 100644 index d319fef19..000000000 --- a/.github/assign/infra.yml +++ /dev/null @@ -1,9 +0,0 @@ -addReviewers: true - -reviewers: - - marysaka - - TSRBerry - -filterLabels: - include: - - infra \ No newline at end of file diff --git a/.github/reviewers.yml b/.github/reviewers.yml new file mode 100644 index 000000000..83d313e3f --- /dev/null +++ b/.github/reviewers.yml @@ -0,0 +1,32 @@ +audio: + - marysaka + +cpu: + - gdkchan + - riperiperi + - marysaka + - LDj3SNuD + +gpu: + - gdkchan + - riperiperi + - marysaka + +gui: + - Ack77 + - emmauss + - TSRBerry + - marysaka + +horizon: + - gdkchan + - Ack77 + - marysaka + - TSRBerry + +infra: + - marysaka + - TSRBerry + +default: + - "@Ryujinx/developers" diff --git a/.github/update_reviewers.py b/.github/update_reviewers.py new file mode 100644 index 000000000..14c0cc285 --- /dev/null +++ b/.github/update_reviewers.py @@ -0,0 +1,79 @@ +from pathlib import Path +from typing import List, Set +from github import Github +from github.Repository import Repository +from github.GithubException import GithubException + +import sys +import yaml + + +def add_reviewers( + reviewers: Set[str], team_reviewers: Set[str], new_entries: List[str] +): + for reviewer in new_entries: + if reviewer.startswith("@"): + team_reviewers.add(reviewer[1:]) + else: + reviewers.add(reviewer) + + +def update_reviewers(config, repo: Repository, pr_id: int) -> int: + pull_request = repo.get_pull(pr_id) + + if not pull_request: + sys.stderr.writable(f"Unknown PR #{pr_id}\n") + return 1 + + pull_request_author = pull_request.user.login + reviewers = set() + team_reviewers = set() + + for label in pull_request.labels: + if label.name in config: + add_reviewers(reviewers, team_reviewers, config[label.name]) + + if "default" in config: + add_reviewers(reviewers, team_reviewers, config["default"]) + + if pull_request_author in reviewers: + reviewers.remove(pull_request_author) + + try: + reviewers = list(reviewers) + team_reviewers = list(team_reviewers) + print( + f"Attempting to assign reviewers ({reviewers}) and team_reviewers ({team_reviewers})" + ) + pull_request.create_review_request(reviewers, team_reviewers) + return 0 + except GithubException as e: + sys.stderr.write(f"Cannot assign review request for PR #{pr_id}: {e}\n") + return 1 + + +if __name__ == "__main__": + if len(sys.argv) != 5: + sys.stderr.write("usage: <token> <repo_path> <pr_id> <config_path>\n") + sys.exit(1) + + token = sys.argv[1] + repo_path = sys.argv[2] + pr_id = int(sys.argv[3]) + config_path = Path(sys.argv[4]) + + g = Github(token) + repo = g.get_repo(repo_path) + + if not repo: + sys.stderr.write("Repository not found!\n") + sys.exit(1) + + if not config_path.exists(): + sys.stderr.write(f'Config "{config_path}" not found!\n') + sys.exit(1) + + with open(config_path, "r") as f: + config = yaml.safe_load(f) + + sys.exit(update_reviewers(config, repo, pr_id)) diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml index e32dd27ad..86e5084f2 100644 --- a/.github/workflows/pr_triage.yml +++ b/.github/workflows/pr_triage.yml @@ -12,43 +12,23 @@ jobs: runs-on: ubuntu-latest steps: + # Grab sources to get update_reviewers.py and reviewers.yml + - name: Fetch sources + uses: actions/checkout@v3 + with: + # Ensure we pin the source origin as pull_request_target run under forks. + fetch-depth: 0 + repository: Ryujinx/Ryujinx + ref: master + - name: Update labels based on changes uses: actions/labeler@v4 with: sync-labels: true dot: true - - name: Auto Assign [Audio] - uses: kentaro-m/auto-assign-action@v1.2.5 - with: - configuration-path: '.github/assign/audio.yml' - - - name: Auto Assign [CPU] - uses: kentaro-m/auto-assign-action@v1.2.5 - with: - configuration-path: '.github/assign/cpu.yml' - - - name: Auto Assign [GPU] - uses: kentaro-m/auto-assign-action@v1.2.5 - with: - configuration-path: '.github/assign/gpu.yml' - - - name: Auto Assign [GUI] - uses: kentaro-m/auto-assign-action@v1.2.5 - with: - configuration-path: '.github/assign/gui.yml' - - - name: Auto Assign [Horizon] - uses: kentaro-m/auto-assign-action@v1.2.5 - with: - configuration-path: '.github/assign/horizon.yml' - - - name: Auto Assign [Infra] - uses: kentaro-m/auto-assign-action@v1.2.5 - with: - configuration-path: '.github/assign/infra.yml' - - - name: Auto Assign [Global] - uses: kentaro-m/auto-assign-action@v1.2.5 - with: - configuration-path: '.github/assign/global.yml' \ No newline at end of file + - name: Assign reviewers + run: | + pip3 install PyGithub + python3 .github/update_reviewers.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml + shell: bash From e5261228d77240da87a8327147d6b5a889bc29ef Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Wed, 12 Jul 2023 19:22:09 +0200 Subject: [PATCH 706/737] infra: Fix team name in reviewer.yml --- .github/reviewers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/reviewers.yml b/.github/reviewers.yml index 83d313e3f..f809a0875 100644 --- a/.github/reviewers.yml +++ b/.github/reviewers.yml @@ -29,4 +29,4 @@ infra: - TSRBerry default: - - "@Ryujinx/developers" + - "@developers" From c5d9e67cb24667b659452e02650dd862b5cf1100 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Fri, 14 Jul 2023 04:08:52 -0300 Subject: [PATCH 707/737] Fix some Vulkan validation errors (#5452) * Fix some validation errors and silence the annoying pipeline barrier error * Remove bogus decref/incref on index buffer state * Make unsafe blit opt-in rather than opt-out * Remove Vulkan debugger messages blacklist * Adjust GetImageUsage to not set the storage bit for multisample textures if not supported --- .../FramebufferParams.cs | 12 ++++++++- .../IndexBufferState.cs | 3 --- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 27 ++++++++++++------- src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 6 ++--- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 11 ++++++-- .../VulkanConfiguration.cs | 2 +- .../VulkanDebugMessenger.cs | 19 ------------- .../VulkanInitialization.cs | 4 +-- 8 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 7b40e96b2..749d5929c 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -168,6 +168,16 @@ namespace Ryujinx.Graphics.Vulkan return ComponentType.Float; } + public ImageAspectFlags GetDepthStencilAspectFlags() + { + if (_depthStencil == null) + { + return ImageAspectFlags.None; + } + + return _depthStencil.Info.Format.ConvertAspectFlags(); + } + public bool IsValidColorAttachment(int bindIndex) { return (uint)bindIndex < Constants.MaxRenderTargets && (_validColorAttachments & (1u << bindIndex)) != 0; @@ -226,7 +236,7 @@ namespace Ryujinx.Graphics.Vulkan _depthStencil?.Storage.SetModification( AccessFlags.DepthStencilAttachmentWriteBit, - PipelineStageFlags.ColorAttachmentOutputBit); + PipelineStageFlags.LateFragmentTestsBit); } public void InsertClearBarrier(CommandBufferScoped cbs, int index) diff --git a/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs index e81268e96..b839619e9 100644 --- a/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs @@ -152,9 +152,6 @@ namespace Ryujinx.Graphics.Vulkan { if (_buffer == from) { - _buffer.DecrementReferenceCount(); - to.IncrementReferenceCount(); - _buffer = to; } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index b76e482b5..b0bc1d0d0 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -245,13 +245,28 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - // TODO: Use stencilMask (fully) + // TODO: Use stencilMask (fully). if (FramebufferParams == null || !FramebufferParams.HasDepthStencil) { return; } + var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue)); + var flags = depthMask ? ImageAspectFlags.DepthBit : 0; + + if (stencilMask != 0) + { + flags |= ImageAspectFlags.StencilBit; + } + + flags &= FramebufferParams.GetDepthStencilAspectFlags(); + + if (flags == ImageAspectFlags.None) + { + return; + } + if (_renderPass == null) { CreateRenderPass(); @@ -259,14 +274,6 @@ namespace Ryujinx.Graphics.Vulkan BeginRenderPass(); - var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue)); - var flags = depthMask ? ImageAspectFlags.DepthBit : 0; - - if (stencilMask != 0) - { - flags |= ImageAspectFlags.StencilBit; - } - var attachment = new ClearAttachment(flags, 0, clearValue); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); @@ -935,7 +942,7 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); - if (_program.IsCompute) + if (internalProgram.IsCompute) { EndRenderPass(); } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 5ecd99073..090f69dca 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); + var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample, forceStorage: true); var flags = ImageCreateFlags.CreateMutableFormatBit; @@ -291,7 +291,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage) + public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage, bool forceStorage = false) { var usage = DefaultUsageFlags; @@ -304,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } - if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample())) + if (((forceStorage && !format.IsDepthOrStencil()) || format.IsImageCompatible()) && (supportsMsStorage || !target.IsMultisample())) { usage |= ImageUsageFlags.StorageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index bb14ea611..9fc50f67a 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -116,7 +116,14 @@ namespace Ryujinx.Graphics.Vulkan return new Auto<DisposableImageView>(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage()); } - _imageView = CreateImageView(componentMapping, subresourceRange, type, ImageUsageFlags.SampledBit); + ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit; + + if (info.Format.IsImageCompatible()) + { + shaderUsage |= ImageUsageFlags.StorageBit; + } + + _imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage); // Framebuffer attachments and storage images requires a identity component mapping. var identityComponentMapping = new ComponentMapping( @@ -378,7 +385,7 @@ namespace Ryujinx.Graphics.Vulkan bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - if (VulkanConfiguration.UseSlowSafeBlitOnAmd && (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk)) + if (!VulkanConfiguration.UseUnsafeBlit || (_gd.Vendor != Vendor.Nvidia && _gd.Vendor != Vendor.Intel)) { _gd.HelperShader.Blit( _gd, diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs b/src/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs index 752d4f7cc..5080ebf68 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs @@ -3,7 +3,7 @@ static class VulkanConfiguration { public const bool UseFastBufferUpdates = true; - public const bool UseSlowSafeBlitOnAmd = true; + public const bool UseUnsafeBlit = true; public const bool UsePushDescriptors = false; public const bool ForceD24S8Unsupported = false; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs b/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs index 82e30f5be..496a90fbe 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs @@ -10,17 +10,6 @@ namespace Ryujinx.Graphics.Vulkan { class VulkanDebugMessenger : IDisposable { - private static readonly string[] _excludedMessages = { - // NOTE: Done on purpose right now. - "UNASSIGNED-CoreValidation-Shader-OutputNotConsumed", - // TODO: Figure out if fixable - "VUID-vkCmdDrawIndexed-None-04584", - // TODO: Might be worth looking into making this happy to possibly optimize copies. - "UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout", - // TODO: Fix this, it's causing too much noise right now. - "VUID-VkSubpassDependency-srcSubpass-00867", - }; - private readonly Vk _api; private readonly Instance _instance; private readonly GraphicsDebugLevel _logLevel; @@ -108,14 +97,6 @@ namespace Ryujinx.Graphics.Vulkan { var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage); - foreach (string excludedMessagePart in _excludedMessages) - { - if (msg.Contains(excludedMessagePart)) - { - return 0; - } - } - if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) { Logger.Error?.Print(LogClass.Gpu, msg); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index b0a3ba37b..02cfe91d9 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -377,8 +377,8 @@ namespace Ryujinx.Graphics.Vulkan ShaderFloat64 = supportedFeatures.ShaderFloat64, ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, - // ShaderStorageImageReadWithoutFormat = true, - // ShaderStorageImageWriteWithoutFormat = true, + ShaderStorageImageReadWithoutFormat = supportedFeatures.ShaderStorageImageReadWithoutFormat, + ShaderStorageImageWriteWithoutFormat = supportedFeatures.ShaderStorageImageWriteWithoutFormat, TessellationShader = supportedFeatures.TessellationShader, VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics, RobustBufferAccess = useRobustBufferAccess, From fec8291c17fa106c28f58b56419e90d49a41a1ea Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Fri, 14 Jul 2023 11:32:14 +0200 Subject: [PATCH 708/737] infra: do not assign developers team for now Hopefully fix PR triage for real... --- .github/reviewers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/reviewers.yml b/.github/reviewers.yml index f809a0875..c0dc59dd5 100644 --- a/.github/reviewers.yml +++ b/.github/reviewers.yml @@ -29,4 +29,4 @@ infra: - TSRBerry default: - - "@developers" + - marysaka From 326749498bed4360e5a4b11fc67d5ec7cb9a3076 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 16 Jul 2023 19:31:14 +0200 Subject: [PATCH 709/737] [Ryujinx.HLE] Address dotnet-format issues (#5380) * dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Address or silence dotnet format IDE1006 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA2208 warnings * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address dotnet format CA2211 warnings * Address dotnet format CA1822 warnings * Address or silence dotnet format CA1069 warnings * Make dotnet format succeed in style mode * Address or silence dotnet format CA2211 warnings * Address review comments * Address dotnet format CA2208 warnings properly * Make ProcessResult readonly * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add previously silenced warnings back I have no clue how these disappeared * Revert formatting changes for while and for-loops * Format if-blocks correctly * Run dotnet format style after rebase * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format analyzers after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Fix a few disabled warnings * Fix naming rule violation, Convert shader properties to auto-property and convert values to const * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Fix and silence a few dotnet-format warnings again * Run dotnet format after rebase * Use using declaration instead of block syntax * Address IDE0251 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Fix naming rule violations * Fix typo * Add trailing commas, use targeted new and use array initializer * Fix build issues * Fix remaining build issues * Remove SuppressMessage for CA1069 where possible * Address dotnet format issues * Address formatting issues Co-authored-by: Ac_K <acoustik666@gmail.com> * Add GetHashCode implementation for RenderingSurfaceInfo * Explicitly silence CA1822 for every affected method in Syscall * Address formatting issues in Demangler.cs * Address review feedback Co-authored-by: Ac_K <acoustik666@gmail.com> * Revert marking service methods as static * Next dotnet format pass * Address review feedback --------- Co-authored-by: Ac_K <acoustik666@gmail.com> --- src/Ryujinx.Ava/Common/ApplicationHelper.cs | 2 +- src/Ryujinx.Ava/UI/Models/SaveModel.cs | 3 +- .../UserFirmwareAvatarSelectorViewModel.cs | 2 +- src/Ryujinx.HLE/AssemblyInfo.cs | 2 +- .../GuestBrokeExecutionException.cs | 2 +- .../Exceptions/InternalServiceException.cs | 4 +- .../InvalidFirmwarePackageException.cs | 2 +- .../Exceptions/InvalidNpdmException.cs | 2 +- .../InvalidStructLayoutException.cs | 6 +- .../InvalidSystemResourceException.cs | 2 +- .../ServiceNotImplementedException.cs | 16 +- .../Exceptions/TamperCompilationException.cs | 2 +- .../Exceptions/TamperExecutionException.cs | 2 +- .../UndefinedInstructionException.cs | 2 +- src/Ryujinx.HLE/FileSystem/ContentManager.cs | 407 ++++++++---------- src/Ryujinx.HLE/FileSystem/ContentPath.cs | 57 ++- .../FileSystem/EncryptedFileSystemCreator.cs | 2 +- src/Ryujinx.HLE/FileSystem/LocationEntry.cs | 10 +- src/Ryujinx.HLE/FileSystem/SystemVersion.cs | 44 +- .../FileSystem/VirtualFileSystem.cs | 125 ++++-- src/Ryujinx.HLE/HLEConfiguration.cs | 98 ++--- src/Ryujinx.HLE/HOS/Applets/AppletManager.cs | 4 +- .../HOS/Applets/Browser/BootDisplayKind.cs | 2 +- .../HOS/Applets/Browser/BrowserApplet.cs | 55 ++- .../HOS/Applets/Browser/BrowserArgument.cs | 16 +- .../HOS/Applets/Browser/BrowserOutput.cs | 12 +- .../HOS/Applets/Browser/BrowserOutputType.cs | 16 +- .../HOS/Applets/Browser/DocumentKind.cs | 2 +- .../HOS/Applets/Browser/LeftStickMode.cs | 2 +- .../HOS/Applets/Browser/ShimKind.cs | 2 +- .../HOS/Applets/Browser/WebArgHeader.cs | 4 +- .../HOS/Applets/Browser/WebArgTLV.cs | 2 +- .../HOS/Applets/Browser/WebArgTLVType.cs | 110 ++--- .../Applets/Browser/WebCommonReturnValue.cs | 4 +- .../HOS/Applets/Browser/WebExitReason.cs | 2 +- .../HOS/Applets/CommonArguments.cs | 10 +- .../Applets/Controller/ControllerApplet.cs | 42 +- .../Controller/ControllerAppletUiArgs.cs | 2 +- .../Controller/ControllerSupportArgHeader.cs | 4 +- .../Controller/ControllerSupportArgPrivate.cs | 4 +- .../Controller/ControllerSupportArgV7.cs | 4 +- .../Controller/ControllerSupportArgVPre7.cs | 4 +- .../Controller/ControllerSupportMode.cs | 4 +- .../Controller/ControllerSupportResultInfo.cs | 4 +- .../HOS/Applets/Error/ApplicationErrorArg.cs | 6 +- .../HOS/Applets/Error/ErrorApplet.cs | 63 +-- .../HOS/Applets/Error/ErrorCommonArg.cs | 2 +- .../HOS/Applets/Error/ErrorCommonHeader.cs | 16 +- .../HOS/Applets/Error/ErrorType.cs | 4 +- .../PlayerSelect/PlayerSelectApplet.cs | 19 +- .../PlayerSelect/PlayerSelectResult.cs | 2 +- .../CJKCharacterValidation.cs | 2 +- .../SoftwareKeyboard/InitialCursorPosition.cs | 2 +- .../SoftwareKeyboard/InlineKeyboardRequest.cs | 2 +- .../InlineKeyboardResponse.cs | 2 +- .../SoftwareKeyboard/InlineKeyboardState.cs | 2 +- .../SoftwareKeyboard/InlineResponses.cs | 219 +++++----- .../Applets/SoftwareKeyboard/InputFormMode.cs | 2 +- .../SoftwareKeyboard/InvalidButtonFlags.cs | 6 +- .../SoftwareKeyboard/InvalidCharFlags.cs | 2 +- .../SoftwareKeyboard/KeyboardCalcFlags.cs | 26 +- .../KeyboardMiniaturizationMode.cs | 6 +- .../Applets/SoftwareKeyboard/KeyboardMode.cs | 12 +- .../SoftwareKeyboard/KeyboardResult.cs | 2 +- .../Applets/SoftwareKeyboard/PasswordMode.cs | 2 +- .../SoftwareKeyboardAppear.cs | 63 +-- .../SoftwareKeyboardAppearEx.cs | 4 +- .../SoftwareKeyboardApplet.cs | 389 +++++++++-------- .../SoftwareKeyboard/SoftwareKeyboardCalc.cs | 79 ++-- .../SoftwareKeyboardConfig.cs | 6 +- .../SoftwareKeyboardRenderer.cs | 32 +- .../SoftwareKeyboardRendererBase.cs | 210 +++++---- .../SoftwareKeyboard/SoftwareKeyboardState.cs | 4 +- .../SoftwareKeyboardUiArgs.cs | 2 +- .../SoftwareKeyboardUiState.cs | 18 +- .../Applets/SoftwareKeyboard/TimedAction.cs | 4 +- .../HOS/ArmProcessContextFactory.cs | 2 +- .../Ast/ArraySubscriptingExpression.cs | 10 +- .../Diagnostics/Demangler/Ast/ArrayType.cs | 16 +- .../HOS/Diagnostics/Demangler/Ast/BaseNode.cs | 8 +- .../Demangler/Ast/BinaryExpression.cs | 12 +- .../Demangler/Ast/BracedExpression.cs | 10 +- .../Demangler/Ast/BracedRangeExpression.cs | 10 +- .../Demangler/Ast/CallExpression.cs | 4 +- .../Demangler/Ast/CastExpression.cs | 10 +- .../Demangler/Ast/ConditionalExpression.cs | 12 +- .../Demangler/Ast/ConversionExpression.cs | 8 +- .../Demangler/Ast/ConversionOperatorType.cs | 2 +- .../Demangler/Ast/CtorDtorNameType.cs | 4 +- .../Demangler/Ast/CtorVtableSpecialName.cs | 8 +- .../Demangler/Ast/DeleteExpression.cs | 8 +- .../HOS/Diagnostics/Demangler/Ast/DtorName.cs | 2 +- .../Demangler/Ast/DynamicExceptionSpec.cs | 2 +- .../Demangler/Ast/ElaboratedType.cs | 4 +- .../Demangler/Ast/EnclosedExpression.cs | 12 +- .../Demangler/Ast/EncodedFunction.cs | 55 +-- .../Demangler/Ast/FoldExpression.cs | 16 +- .../Demangler/Ast/ForwardTemplateReference.cs | 4 +- .../Demangler/Ast/FunctionParameter.cs | 4 +- .../Diagnostics/Demangler/Ast/FunctionType.cs | 20 +- .../Demangler/Ast/InitListExpression.cs | 13 +- .../Demangler/Ast/IntegerCastExpression.cs | 4 +- .../Demangler/Ast/IntegerLiteral.cs | 8 +- .../Demangler/Ast/LiteralOperator.cs | 2 +- .../Diagnostics/Demangler/Ast/LocalName.cs | 8 +- .../Demangler/Ast/MemberExpression.cs | 12 +- .../HOS/Diagnostics/Demangler/Ast/NameType.cs | 4 +- .../Ast/NameTypeWithTemplateArguments.cs | 10 +- .../Diagnostics/Demangler/Ast/NestedName.cs | 4 +- .../Demangler/Ast/NewExpression.cs | 20 +- .../Diagnostics/Demangler/Ast/NodeArray.cs | 2 +- .../Demangler/Ast/PackedTemplateParameter.cs | 2 +- .../Ast/PackedTemplateParameterExpansion.cs | 10 +- .../Diagnostics/Demangler/Ast/ParentNode.cs | 2 +- .../Diagnostics/Demangler/Ast/PointerType.cs | 4 +- .../Demangler/Ast/PostfixExpression.cs | 4 +- .../Demangler/Ast/PostfixQualifiedType.cs | 4 +- .../Demangler/Ast/PrefixExpression.cs | 4 +- .../Demangler/Ast/QualifiedName.cs | 8 +- .../Diagnostics/Demangler/Ast/Qualifier.cs | 21 +- .../Demangler/Ast/ReferenceType.cs | 8 +- .../Diagnostics/Demangler/Ast/SpecialName.cs | 4 +- .../Demangler/Ast/SpecialSubstitution.cs | 29 +- .../Demangler/Ast/TemplateArguments.cs | 6 +- .../Demangler/Ast/ThrowExpression.cs | 4 +- .../HOS/Diagnostics/Demangler/Demangler.cs | 131 +++--- src/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs | 6 +- src/Ryujinx.HLE/HOS/Horizon.cs | 70 ++- src/Ryujinx.HLE/HOS/IdDictionary.cs | 10 +- src/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs | 12 +- src/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs | 2 +- src/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs | 2 +- src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 156 ++++--- src/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs | 12 +- src/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs | 18 +- .../HOS/Ipc/IpcRecvListBuffDesc.cs | 6 +- .../HOS/Ipc/ServiceProcessRequest.cs | 2 +- .../Kernel/Common/IKFutureSchedulerObject.cs | 2 +- .../HOS/Kernel/Common/KAutoObject.cs | 2 +- .../HOS/Kernel/Common/KResourceLimit.cs | 8 +- .../Kernel/Common/KSynchronizationObject.cs | 2 +- .../HOS/Kernel/Common/KSystemControl.cs | 8 +- .../HOS/Kernel/Common/KTimeManager.cs | 8 +- .../HOS/Kernel/Common/KernelInit.cs | 26 +- .../HOS/Kernel/Common/KernelTransfer.cs | 4 +- .../HOS/Kernel/Common/LimitableResource.cs | 12 +- .../HOS/Kernel/Common/MemoryArrange.cs | 4 +- .../Common/{MemroySize.cs => MemorySize.cs} | 4 +- .../HOS/Kernel/Common/MersenneTwister.cs | 16 +- .../HOS/Kernel/Ipc/ChannelState.cs | 4 +- .../HOS/Kernel/Ipc/KBufferDescriptor.cs | 14 +- .../HOS/Kernel/Ipc/KBufferDescriptorTable.cs | 40 +- src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs | 10 +- .../HOS/Kernel/Ipc/KClientSession.cs | 12 +- .../HOS/Kernel/Ipc/KLightClientSession.cs | 4 +- .../HOS/Kernel/Ipc/KLightServerSession.cs | 4 +- .../HOS/Kernel/Ipc/KLightSession.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs | 8 +- src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs | 6 +- .../HOS/Kernel/Ipc/KServerSession.cs | 147 +++---- src/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs | 4 +- .../HOS/Kernel/Ipc/KSessionRequest.cs | 12 +- src/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 16 +- .../HOS/Kernel/Memory/AddressSpaceType.cs | 8 +- .../HOS/Kernel/Memory/DramMemoryMap.cs | 4 +- .../HOS/Kernel/Memory/KCodeMemory.cs | 2 +- .../HOS/Kernel/Memory/KMemoryBlock.cs | 2 +- .../Kernel/Memory/KMemoryBlockSlabManager.cs | 4 +- .../HOS/Kernel/Memory/KMemoryInfo.cs | 38 +- .../HOS/Kernel/Memory/KMemoryRegionManager.cs | 2 +- .../HOS/Kernel/Memory/KPageBitmap.cs | 4 +- .../HOS/Kernel/Memory/KPageHeap.cs | 26 +- .../HOS/Kernel/Memory/KPageList.cs | 10 +- .../HOS/Kernel/Memory/KPageNode.cs | 4 +- .../HOS/Kernel/Memory/KPageTable.cs | 10 +- .../HOS/Kernel/Memory/KPageTableBase.cs | 90 ++-- .../HOS/Kernel/Memory/KScopedPageList.cs | 2 +- .../HOS/Kernel/Memory/KSharedMemory.cs | 2 +- .../HOS/Kernel/Memory/KSlabHeap.cs | 4 +- .../HOS/Kernel/Memory/KTransferMemory.cs | 2 +- .../HOS/Kernel/Memory/MemoryAttribute.cs | 12 +- .../HOS/Kernel/Memory/MemoryPermission.cs | 16 +- .../HOS/Kernel/Memory/MemoryRegion.cs | 8 +- .../HOS/Kernel/Memory/MemoryState.cs | 78 ++-- .../Kernel/Process/CapabilityExtensions.cs | 2 +- .../HOS/Kernel/Process/CapabilityType.cs | 22 +- .../HOS/Kernel/Process/HleProcessDebugger.cs | 46 +- .../HOS/Kernel/Process/KContextIdManager.cs | 10 +- .../HOS/Kernel/Process/KHandleEntry.cs | 6 +- .../HOS/Kernel/Process/KHandleTable.cs | 27 +- .../HOS/Kernel/Process/KProcess.cs | 41 +- .../Kernel/Process/KProcessCapabilities.cs | 260 +++++------ .../HOS/Kernel/Process/KTlsPageInfo.cs | 2 +- .../HOS/Kernel/Process/KTlsPageManager.cs | 8 +- .../Kernel/Process/ProcessCreationFlags.cs | 4 +- .../HOS/Kernel/Process/ProcessCreationInfo.cs | 2 +- .../Kernel/Process/ProcessExecutionContext.cs | 5 +- .../HOS/Kernel/Process/ProcessState.cs | 16 +- .../HOS/Kernel/Process/ProcessTamperInfo.cs | 12 +- .../SupervisorCall/CodeMemoryOperation.cs | 4 +- .../HOS/Kernel/SupervisorCall/InfoType.cs | 2 +- .../HOS/Kernel/SupervisorCall/MemoryInfo.cs | 8 +- .../HOS/Kernel/SupervisorCall/Syscall.cs | 223 ++++++++-- .../Kernel/SupervisorCall/SyscallHandler.cs | 2 +- .../Kernel/SupervisorCall/ThreadContext.cs | 6 +- .../HOS/Kernel/Threading/ArbitrationType.cs | 6 +- .../HOS/Kernel/Threading/KAddressArbiter.cs | 42 +- .../Kernel/Threading/KConditionVariable.cs | 2 +- .../HOS/Kernel/Threading/KCriticalSection.cs | 2 +- .../HOS/Kernel/Threading/KEvent.cs | 4 +- .../HOS/Kernel/Threading/KPriorityQueue.cs | 4 +- .../HOS/Kernel/Threading/KReadableEvent.cs | 7 +- .../HOS/Kernel/Threading/KScheduler.cs | 24 +- .../HOS/Kernel/Threading/KSynchronization.cs | 10 +- .../HOS/Kernel/Threading/KThread.cs | 8 +- .../HOS/Kernel/Threading/KWritableEvent.cs | 2 +- .../HOS/Kernel/Threading/SignalType.cs | 4 +- .../HOS/Kernel/Threading/ThreadSchedState.cs | 22 +- .../HOS/Kernel/Threading/ThreadType.cs | 4 +- src/Ryujinx.HLE/HOS/LibHacHorizonManager.cs | 27 +- src/Ryujinx.HLE/HOS/ModLoader.cs | 38 +- src/Ryujinx.HLE/HOS/ResultCode.cs | 6 +- src/Ryujinx.HLE/HOS/ServiceCtx.cs | 46 +- .../Services/Account/Acc/AccountManager.cs | 8 +- .../Account/Acc/AccountSaveDataManager.cs | 32 +- .../AccountService/IManagerForApplication.cs | 4 +- .../IManagerForSystemService.cs | 4 +- .../Account/Acc/AccountService/IProfile.cs | 4 +- .../Acc/AccountService/IProfileEditor.cs | 4 +- .../Acc/AccountService/ManagerServer.cs | 28 +- .../Acc/AccountService/ProfileServer.cs | 16 +- .../Account/Acc/ApplicationServiceServer.cs | 10 +- .../Acc/AsyncContext/AsyncExecution.cs | 14 +- .../Acc/IAccountServiceForAdministrator.cs | 4 +- .../Acc/IAccountServiceForApplication.cs | 4 +- .../Acc/IAccountServiceForSystemService.cs | 4 +- .../HOS/Services/Account/Acc/IAsyncContext.cs | 8 +- .../IAsyncNetworkServiceLicenseKindContext.cs | 2 +- .../Account/Acc/IBaasAccessTokenAccessor.cs | 2 +- .../Acc/ProfilesJsonSerializerContext.cs | 2 +- .../Account/Acc/Types/AccountServiceFlag.cs | 10 +- .../Account/Acc/Types/AccountState.cs | 4 +- .../Acc/Types/NetworkServiceLicenseKind.cs | 2 +- .../Account/Acc/Types/ProfilesJson.cs | 2 +- .../HOS/Services/Account/Acc/Types/UserId.cs | 10 +- .../Services/Account/Acc/Types/UserProfile.cs | 10 +- .../Account/Acc/Types/UserProfileJson.cs | 2 +- .../HOS/Services/Account/Dauth/IService.cs | 2 +- .../HOS/Services/Account/ResultCode.cs | 26 +- .../ILibraryAppletProxy.cs | 2 +- .../ISystemAppletProxy.cs | 2 +- .../ILibraryAppletAccessor.cs | 24 +- .../LibraryAppletProxy/AppletStandalone.cs | 6 +- .../ILibraryAppletSelfAccessor.cs | 18 +- .../IProcessWindingController.cs | 6 +- .../IAppletCommonFunctions.cs | 2 +- .../SystemAppletProxy/IApplicationCreator.cs | 2 +- .../SystemAppletProxy/IAudioController.cs | 12 +- .../SystemAppletProxy/ICommonStateGetter.cs | 22 +- .../SystemAppletProxy/IDebugFunctions.cs | 2 +- .../SystemAppletProxy/IDisplayController.cs | 10 +- .../IGlobalStateController.cs | 2 +- .../SystemAppletProxy/IHomeMenuFunctions.cs | 6 +- .../ILibraryAppletCreator.cs | 16 +- .../SystemAppletProxy/ISelfController.cs | 24 +- .../SystemAppletProxy/IWindowController.cs | 2 +- .../Types/AlbumReportOption.cs | 4 +- .../SystemAppletProxy/Types/AppletMessage.cs | 60 +-- .../SystemAppletProxy/Types/FocusState.cs | 6 +- .../SystemAppletProxy/Types/OperationMode.cs | 4 +- .../Types/WirelessPriorityMode.cs | 4 +- .../HOS/Services/Am/AppletAE/AppletFifo.cs | 2 +- .../HOS/Services/Am/AppletAE/AppletSession.cs | 6 +- .../IAllSystemAppletProxiesService.cs | 2 +- .../HOS/Services/Am/AppletAE/IStorage.cs | 8 +- .../Services/Am/AppletAE/IStorageAccessor.cs | 4 +- .../Am/AppletAE/Storage/StorageHelper.cs | 16 +- .../Services/Am/AppletAE/Types/AppletId.cs | 40 +- .../Am/AppletAE/Types/AppletIdentityInfo.cs | 6 +- .../Types/AppletProcessLaunchReason.cs | 6 +- .../Am/AppletAE/Types/LibraryAppletInfo.cs | 4 +- .../Am/AppletAE/Types/LibraryAppletMode.cs | 4 +- .../ApplicationProxy/IApplicationFunctions.cs | 72 ++-- .../Types/LaunchParameterKind.cs | 2 +- .../Types/ProgramSpecifyKind.cs | 2 +- .../IApplicationProxy.cs | 2 +- .../Am/AppletOE/IApplicationProxyService.cs | 2 +- .../Services/Am/Idle/IPolicyManagerSystem.cs | 2 +- .../Services/Am/Omm/IOperationModeManager.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs | 36 +- .../Services/Am/Spsm/IPowerStateInterface.cs | 2 +- .../HOS/Services/Am/Tcap/IManager.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Apm/IManager.cs | 2 +- .../HOS/Services/Apm/IManagerPrivileged.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Apm/ISession.cs | 4 +- .../HOS/Services/Apm/ISystemManager.cs | 2 +- .../HOS/Services/Apm/ManagerServer.cs | 2 +- .../HOS/Services/Apm/PerformanceState.cs | 10 +- .../HOS/Services/Apm/ResultCode.cs | 6 +- .../HOS/Services/Apm/SessionServer.cs | 4 +- .../HOS/Services/Apm/SystemManagerServer.cs | 2 +- .../HOS/Services/Apm/Types/CpuBoostMode.cs | 8 +- .../Apm/Types/PerformanceConfiguration.cs | 22 +- .../HOS/Services/Apm/Types/PerformanceMode.cs | 4 +- .../Services/Arp/ApplicationLaunchProperty.cs | 26 +- src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs | 2 +- .../HOS/Services/Arp/LibHacIReader.cs | 7 +- .../HOS/Services/Audio/AudioIn/AudioIn.cs | 12 +- .../Services/Audio/AudioIn/AudioInServer.cs | 22 +- .../HOS/Services/Audio/AudioInManager.cs | 2 +- .../Services/Audio/AudioInManagerServer.cs | 10 +- .../HOS/Services/Audio/AudioOut/AudioOut.cs | 10 +- .../Services/Audio/AudioOut/AudioOutServer.cs | 22 +- .../HOS/Services/Audio/AudioOutManager.cs | 2 +- .../Services/Audio/AudioOutManagerServer.cs | 6 +- .../Audio/AudioRenderer/AudioDevice.cs | 16 +- .../Audio/AudioRenderer/AudioDeviceServer.cs | 2 +- .../Audio/AudioRenderer/AudioRenderer.cs | 6 +- .../AudioRenderer/AudioRendererServer.cs | 42 +- .../Audio/AudioRenderer/IAudioDevice.cs | 2 +- .../Services/Audio/AudioRendererManager.cs | 4 +- .../Audio/AudioRendererManagerServer.cs | 2 +- .../HardwareOpusDecoderManager/Decoder.cs | 2 +- .../DecoderCommon.cs | 2 +- .../HardwareOpusDecoderManager/IDecoder.cs | 2 +- .../IHardwareOpusDecoder.cs | 8 +- .../MultiSampleDecoder.cs | 2 +- .../HOS/Services/Audio/IAudioController.cs | 2 +- .../HOS/Services/Audio/IAudioInManager.cs | 2 +- .../Audio/IAudioInManagerForApplet.cs | 2 +- .../Audio/IAudioInManagerForDebugger.cs | 2 +- .../Audio/IAudioOutManagerForApplet.cs | 2 +- .../Audio/IAudioOutManagerForDebugger.cs | 2 +- .../Audio/IAudioRendererManagerForApplet.cs | 2 +- .../Audio/IAudioRendererManagerForDebugger.cs | 2 +- .../HOS/Services/Audio/IAudioSnoopManager.cs | 2 +- .../Audio/IFinalOutputRecorderManager.cs | 2 +- .../IFinalOutputRecorderManagerForApplet.cs | 2 +- .../IFinalOutputRecorderManagerForDebugger.cs | 2 +- .../Audio/IHardwareOpusDecoderManager.cs | 10 +- .../HOS/Services/Audio/ResultCode.cs | 20 +- .../Services/Audio/Types/OpusPacketHeader.cs | 2 +- .../HOS/Services/Bgtc/IStateControlService.cs | 2 +- .../HOS/Services/Bgtc/ITaskService.cs | 2 +- .../BluetoothDriver/BluetoothEventManager.cs | 14 +- .../Services/Bluetooth/IBluetoothDriver.cs | 6 +- .../HOS/Services/Bluetooth/IBluetoothUser.cs | 2 +- .../BluetoothManager/BtmUser/IBtmUserCore.cs | 10 +- .../HOS/Services/BluetoothManager/IBtm.cs | 2 +- .../Services/BluetoothManager/IBtmDebug.cs | 2 +- .../Services/BluetoothManager/IBtmSystem.cs | 2 +- .../HOS/Services/BluetoothManager/IBtmUser.cs | 2 +- .../Services/BluetoothManager/ResultCode.cs | 6 +- .../HOS/Services/Caps/CaptureManager.cs | 40 +- .../Services/Caps/IAlbumAccessorService.cs | 2 +- .../Services/Caps/IAlbumApplicationService.cs | 12 +- .../HOS/Services/Caps/IAlbumControlService.cs | 2 +- .../Caps/IScreenShotApplicationService.cs | 37 +- .../Caps/IScreenShotControlService.cs | 2 +- .../HOS/Services/Caps/IScreenshotService.cs | 2 +- .../HOS/Services/Caps/ResultCode.cs | 18 +- .../Services/Caps/Types/AlbumFileDateTime.cs | 14 +- .../Caps/Types/AlbumImageOrientation.cs | 4 +- .../HOS/Services/Caps/Types/AlbumStorage.cs | 4 +- .../Caps/Types/ApplicationAlbumEntry.cs | 14 +- .../HOS/Services/Caps/Types/ContentType.cs | 4 +- .../Caps/Types/ScreenShotAttribute.cs | 10 +- .../HOS/Services/Cec/ICecManager.cs | 2 +- .../HOS/Services/CommandCmifAttribute.cs | 2 +- src/Ryujinx.HLE/HOS/Services/DummyService.cs | 2 +- .../HOS/Services/Ectx/IReaderForSystem.cs | 2 +- .../Services/Ectx/IWriterForApplication.cs | 2 +- .../HOS/Services/Ectx/IWriterForSystem.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs | 2 +- .../HOS/Services/Es/IETicketService.cs | 2 +- .../HOS/Services/Eupld/IControl.cs | 2 +- .../HOS/Services/Eupld/IRequest.cs | 2 +- .../HOS/Services/Fatal/IPrivateService.cs | 2 +- .../HOS/Services/Fatal/IService.cs | 16 +- .../HOS/Services/Fatal/Types/CpuContext32.cs | 2 +- .../HOS/Services/Fatal/Types/CpuContext64.cs | 4 +- .../HOS/Services/Fatal/Types/FatalPolicy.cs | 4 +- .../HOS/Services/Friend/IServiceCreator.cs | 4 +- .../HOS/Services/Friend/ResultCode.cs | 8 +- .../FriendService/Types/Friend.cs | 8 +- .../FriendService/Types/FriendFilter.cs | 2 +- .../FriendService/Types/PresenceStatus.cs | 4 +- .../Types/PresenceStatusFilter.cs | 4 +- .../FriendService/Types/UserPresence.cs | 8 +- .../IDaemonSuspendSessionService.cs | 8 +- .../Friend/ServiceCreator/IFriendService.cs | 54 +-- .../ServiceCreator/INotificationService.cs | 24 +- .../NotificationEventHandler.cs | 27 +- .../Types/NotificationEventType.cs | 6 +- .../Types/NotificationInfo.cs | 2 +- .../Types/FriendServicePermissionLevel.cs | 16 +- .../Types/PlayHistoryRegistrationKey.cs | 8 +- .../FileSystemProxy/FileSystemProxyHelper.cs | 10 +- .../Services/Fs/FileSystemProxy/IDirectory.cs | 10 +- .../HOS/Services/Fs/FileSystemProxy/IFile.cs | 16 +- .../Fs/FileSystemProxy/IFileSystem.cs | 6 +- .../Services/Fs/FileSystemProxy/IStorage.cs | 13 +- .../HOS/Services/Fs/IFileSystemProxy.cs | 238 +++++++--- .../Services/Fs/IFileSystemProxyForLoader.cs | 2 +- .../HOS/Services/Fs/IProgramRegistry.cs | 2 +- .../HOS/Services/Fs/ISaveDataInfoReader.cs | 10 +- src/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs | 12 +- .../HOS/Services/Fs/Types/FileSystemType.cs | 14 +- .../HOS/Services/Grc/IGrcService.cs | 2 +- .../HOS/Services/Grc/IRemoteVideoTransfer.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs | 20 +- .../HOS/Services/Hid/HidDevices/BaseDevice.cs | 2 +- .../Services/Hid/HidDevices/DebugPadDevice.cs | 4 +- .../Services/Hid/HidDevices/KeyboardDevice.cs | 4 +- .../Services/Hid/HidDevices/MouseDevice.cs | 6 +- .../Services/Hid/HidDevices/NpadDevices.cs | 75 ++-- .../Services/Hid/HidDevices/TouchDevice.cs | 8 +- .../Hid/HidDevices/Types/ControllerConfig.cs | 4 +- .../Hid/HidDevices/Types/GamepadInput.cs | 6 +- .../Hid/HidDevices/Types/JoystickPosition.cs | 2 +- .../Hid/HidDevices/Types/KeyboardInput.cs | 2 +- .../Hid/HidDevices/Types/SixAxisInput.cs | 10 +- .../Hid/HidDevices/Types/TouchPoint.cs | 2 +- .../HOS/Services/Hid/HidServer/HidUtils.cs | 10 +- .../HidServer/IActiveVibrationDeviceList.cs | 4 +- .../Services/Hid/HidServer/IAppletResource.cs | 6 +- .../Types/Npad/NpadHandheldActivationMode.cs | 4 +- .../HidServer/Types/Npad/NpadJoyDeviceType.cs | 4 +- .../Types/SixAxis/AccelerometerParameters.cs | 2 +- .../Types/SixAxis/GyroscopeZeroDriftMode.cs | 4 +- .../Types/SixAxis/SensorFusionParameters.cs | 2 +- .../Types/Vibration/VibrationDeviceHandle.cs | 2 +- .../Vibration/VibrationDevicePosition.cs | 4 +- .../Types/Vibration/VibrationDeviceType.cs | 4 +- .../Types/Vibration/VibrationDeviceValue.cs | 4 +- .../Types/Vibration/VibrationValue.cs | 16 +- .../HOS/Services/Hid/IHidDebugServer.cs | 2 +- .../HOS/Services/Hid/IHidServer.cs | 243 ++++++----- .../HOS/Services/Hid/IHidSystemServer.cs | 4 +- .../HOS/Services/Hid/IHidbusServer.cs | 10 +- .../HOS/Services/Hid/ISystemServer.cs | 2 +- .../HOS/Services/Hid/Irs/IIrSensorServer.cs | 46 +- .../Services/Hid/Irs/IIrSensorSystemServer.cs | 2 +- .../HOS/Services/Hid/Irs/ResultCode.cs | 8 +- .../Irs/Types/ImageTransferProcessorState.cs | 6 +- .../Services/Hid/Irs/Types/IrCameraHandle.cs | 6 +- .../Types/PackedClusteringProcessorConfig.cs | 24 +- .../PackedImageTransferProcessorConfig.cs | 20 +- .../Irs/Types/PackedMomentProcessorConfig.cs | 20 +- .../Types/PackedTeraPluginProcessorConfig.cs | 2 +- .../HOS/Services/Hid/ResultCode.cs | 10 +- .../Services/Hid/Types/AppletFooterUiType.cs | 4 +- .../HOS/Services/Hid/Types/HidVector.cs | 2 +- .../HOS/Services/Hid/Types/Npad/BusHandle.cs | 2 +- .../HOS/Services/Hid/Types/Npad/BusType.cs | 4 +- .../Services/Hid/Types/Npad/ControllerKeys.cs | 64 +-- .../Services/Hid/Types/Npad/ControllerType.cs | 18 +- .../HOS/Services/Hid/Types/Npad/NpadColor.cs | 64 +-- .../HOS/Services/Hid/Types/Npad/NpadIdType.cs | 20 +- .../Services/Hid/Types/Npad/NpadStyleIndex.cs | 12 +- .../Services/Hid/Types/Npad/PlayerIndex.cs | 22 +- .../HOS/Services/Hid/Types/NpadJoyHoldType.cs | 2 +- .../SharedMemory/Common/AtomicStorage.cs | 2 +- .../SharedMemory/Common/ISampledDataStruct.cs | 8 +- .../Hid/Types/SharedMemory/Common/RingLifo.cs | 16 +- .../DebugPad/DebugPadAttribute.cs | 4 +- .../SharedMemory/DebugPad/DebugPadButton.cs | 4 +- .../SharedMemory/Keyboard/KeyboardKey.cs | 2 +- .../SharedMemory/Keyboard/KeyboardKeyShift.cs | 4 +- .../SharedMemory/Keyboard/KeyboardModifier.cs | 2 +- .../SharedMemory/Mouse/MouseAttribute.cs | 4 +- .../Types/SharedMemory/Mouse/MouseButton.cs | 4 +- .../Hid/Types/SharedMemory/Npad/DeviceType.cs | 2 +- .../Types/SharedMemory/Npad/NpadAttribute.cs | 4 +- .../SharedMemory/Npad/NpadBatteryLevel.cs | 2 +- .../Hid/Types/SharedMemory/Npad/NpadButton.cs | 4 +- .../SharedMemory/Npad/NpadColorAttribute.cs | 2 +- .../SharedMemory/Npad/NpadCommonState.cs | 2 +- .../Npad/NpadFullKeyColorState.cs | 2 +- .../SharedMemory/Npad/NpadGcTriggerState.cs | 4 +- .../SharedMemory/Npad/NpadInternalState.cs | 12 +- .../Npad/NpadJoyAssignmentMode.cs | 4 +- .../SharedMemory/Npad/NpadJoyColorState.cs | 2 +- .../Types/SharedMemory/Npad/NpadLuciaType.cs | 4 +- .../Hid/Types/SharedMemory/Npad/NpadState.cs | 2 +- .../Types/SharedMemory/Npad/NpadStyleTag.cs | 2 +- .../Npad/NpadSystemButtonProperties.cs | 4 +- .../SharedMemory/Npad/NpadSystemProperties.cs | 2 +- .../Npad/SixAxisSensorAttribute.cs | 4 +- .../SharedMemory/Npad/SixAxisSensorState.cs | 4 +- .../Hid/Types/SharedMemory/SharedMemory.cs | 2 +- .../TouchScreen/TouchAttribute.cs | 2 +- .../TouchScreen/TouchScreenState.cs | 2 +- .../SharedMemory/TouchScreen/TouchState.cs | 8 +- .../HOS/Services/Ins/IReceiverManager.cs | 2 +- .../HOS/Services/Ins/ISenderManager.cs | 2 +- src/Ryujinx.HLE/HOS/Services/IpcService.cs | 10 +- .../HOS/Services/Lbl/ILblController.cs | 2 +- .../HOS/Services/Lbl/LblControllerServer.cs | 2 +- .../Services/Ldn/IMonitorServiceCreator.cs | 2 +- .../HOS/Services/Ldn/ISystemServiceCreator.cs | 2 +- .../HOS/Services/Ldn/IUserServiceCreator.cs | 2 +- .../HOS/Services/Ldn/Lp2p/IServiceCreator.cs | 2 +- .../HOS/Services/Ldn/NetworkInterface.cs | 8 +- .../HOS/Services/Ldn/ResultCode.cs | 12 +- .../HOS/Services/Ldn/Types/NetworkState.cs | 4 +- .../IUserLocalCommunicationService.cs | 8 +- .../Services/Loader/IDebugMonitorInterface.cs | 2 +- .../Loader/IProcessManagerInterface.cs | 2 +- .../HOS/Services/Loader/IShellInterface.cs | 2 +- .../HOS/Services/Loader/ResultCode.cs | 69 +-- src/Ryujinx.HLE/HOS/Services/Mig/IService.cs | 2 +- .../HOS/Services/Mii/DatabaseImpl.cs | 7 +- .../Services/Mii/DatabaseSessionMetadata.cs | 6 +- src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs | 2 + .../HOS/Services/Mii/IImageDatabaseService.cs | 4 +- .../HOS/Services/Mii/IStaticService.cs | 8 +- .../HOS/Services/Mii/MiiDatabaseManager.cs | 71 +-- .../HOS/Services/Mii/ResultCode.cs | 40 +- .../Mii/StaticService/DatabaseServiceImpl.cs | 6 +- .../Mii/StaticService/IDatabaseService.cs | 30 +- src/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs | 2 +- .../HOS/Services/Mii/Types/BeardType.cs | 4 +- .../HOS/Services/Mii/Types/CharInfo.cs | 373 +++++++++++----- .../HOS/Services/Mii/Types/CharInfoElement.cs | 2 +- .../HOS/Services/Mii/Types/CommonColor.cs | 2 +- .../HOS/Services/Mii/Types/CoreData.cs | 216 +++++----- .../HOS/Services/Mii/Types/CreateId.cs | 12 +- .../HOS/Services/Mii/Types/DefaultMii.cs | 6 +- .../HOS/Services/Mii/Types/EyeType.cs | 4 +- .../HOS/Services/Mii/Types/EyebrowType.cs | 4 +- .../HOS/Services/Mii/Types/FacelineColor.cs | 4 +- .../HOS/Services/Mii/Types/FacelineMake.cs | 4 +- .../HOS/Services/Mii/Types/FacelineType.cs | 4 +- .../HOS/Services/Mii/Types/FacelineWrinkle.cs | 4 +- .../HOS/Services/Mii/Types/FontRegion.cs | 2 +- .../HOS/Services/Mii/Types/Gender.cs | 4 +- .../HOS/Services/Mii/Types/GlassType.cs | 4 +- .../HOS/Services/Mii/Types/HairFlip.cs | 4 +- .../HOS/Services/Mii/Types/HairType.cs | 4 +- .../HOS/Services/Mii/Types/MoleType.cs | 4 +- .../HOS/Services/Mii/Types/MouthType.cs | 4 +- .../HOS/Services/Mii/Types/MustacheType.cs | 4 +- .../HOS/Services/Mii/Types/Nickname.cs | 6 +- .../Mii/Types/NintendoFigurineDatabase.cs | 26 +- .../HOS/Services/Mii/Types/NoseType.cs | 4 +- .../HOS/Services/Mii/Types/Race.cs | 2 +- .../Services/Mii/Types/RandomMiiConstants.cs | 50 ++- .../HOS/Services/Mii/Types/Source.cs | 2 +- .../HOS/Services/Mii/Types/SourceFlag.cs | 4 +- .../Services/Mii/Types/SpecialMiiKeyCode.cs | 2 +- .../HOS/Services/Mii/Types/StoreData.cs | 36 +- .../Services/Mii/Types/StoreDataElement.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs | 30 +- .../Mm/Types/MultiMediaOperationType.cs | 6 +- .../Services/Mm/Types/MultiMediaSession.cs | 10 +- .../Services/Mnpp/IServiceForApplication.cs | 4 +- .../HOS/Services/Mnpp/ResultCode.cs | 8 +- .../HOS/Services/Ncm/IContentManager.cs | 2 +- .../Ncm/Lr/ILocationResolverManager.cs | 2 +- .../ILocationResolver.cs | 17 +- .../HOS/Services/Ncm/Lr/ResultCode.cs | 20 +- .../HOS/Services/News/IServiceCreator.cs | 2 +- .../HOS/Services/Nfc/IAmManager.cs | 2 +- .../HOS/Services/Nfc/ISystemManager.cs | 2 +- .../HOS/Services/Nfc/IUserManager.cs | 2 +- .../HOS/Services/Nfc/Mifare/IUserManager.cs | 2 +- .../HOS/Services/Nfc/NfcManager/INfc.cs | 6 +- .../NfcManager/Types/NfcPermissionLevel.cs | 4 +- .../Services/Nfc/NfcManager/Types/State.cs | 4 +- .../Nfc/Nfp/AmiiboJsonSerializerContext.cs | 2 +- .../HOS/Services/Nfc/Nfp/IDebugManager.cs | 2 +- .../HOS/Services/Nfc/Nfp/ISystemManager.cs | 2 +- .../HOS/Services/Nfc/Nfp/IUserManager.cs | 2 +- .../HOS/Services/Nfc/Nfp/NfpManager/INfp.cs | 81 ++-- .../Nfp/NfpManager/Types/AmiiboConstants.cs | 4 +- .../Nfc/Nfp/NfpManager/Types/CommonInfo.cs | 14 +- .../Nfc/Nfp/NfpManager/Types/DeviceType.cs | 2 +- .../Nfc/Nfp/NfpManager/Types/ModelInfo.cs | 12 +- .../Nfc/Nfp/NfpManager/Types/MountTarget.cs | 4 +- .../Nfc/Nfp/NfpManager/Types/NfpDevice.cs | 6 +- .../Nfp/NfpManager/Types/NfpDeviceState.cs | 14 +- .../NfpManager/Types/NfpPermissionLevel.cs | 4 +- .../Nfc/Nfp/NfpManager/Types/RegisterInfo.cs | 12 +- .../Nfc/Nfp/NfpManager/Types/State.cs | 4 +- .../Nfc/Nfp/NfpManager/Types/TagInfo.cs | 8 +- .../Nfp/NfpManager/Types/VirtualAmiiboFile.cs | 14 +- .../HOS/Services/Nfc/Nfp/ResultCode.cs | 18 +- .../HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 57 ++- src/Ryujinx.HLE/HOS/Services/Ngct/IService.cs | 2 +- .../Ngct/IServiceWithManagementApi.cs | 2 +- .../HOS/Services/Ngct/NgctServer.cs | 12 +- .../HOS/Services/Nifm/IStaticService.cs | 2 +- .../HOS/Services/Nifm/ResultCode.cs | 10 +- .../GeneralService/GeneralServiceManager.cs | 4 +- .../Types/GeneralServiceDetail.cs | 4 +- .../Nifm/StaticService/IGeneralService.cs | 30 +- .../Services/Nifm/StaticService/IRequest.cs | 14 +- .../Nifm/StaticService/Types/DnsSetting.cs | 8 +- .../Types/InternetConnectionState.cs | 2 +- .../Types/InternetConnectionStatus.cs | 4 +- .../Types/InternetConnectionType.cs | 4 +- .../StaticService/Types/IpAddressSetting.cs | 10 +- .../Nifm/StaticService/Types/IpSettingData.cs | 8 +- .../StaticService/Types/NetworkProfileData.cs | 12 +- .../Nifm/StaticService/Types/ProxySetting.cs | 18 +- .../Types/WirelessSettingData.cs | 8 +- .../Services/Nim/INetworkInstallManager.cs | 2 +- .../Services/Nim/IShopServiceAccessServer.cs | 2 +- .../Nim/IShopServiceAccessServerInterface.cs | 2 +- .../Nim/IShopServiceAccessSystemInterface.cs | 2 +- .../HOS/Services/Nim/IShopServiceAccessor.cs | 2 +- .../HOS/Services/Nim/IShopServiceAsync.cs | 2 +- .../HOS/Services/Nim/IShopServiceManager.cs | 2 +- .../HOS/Services/Nim/Ntc/IStaticService.cs | 2 +- .../IEnsureNetworkClockAvailabilityService.cs | 6 +- .../HOS/Services/Nim/ResultCode.cs | 6 +- .../INotificationServicesForApplication.cs | 2 +- .../INotificationServicesForSystem.cs | 2 +- .../HOS/Services/Npns/INpnsSystem.cs | 2 +- .../HOS/Services/Npns/INpnsUser.cs | 2 +- .../Services/Ns/Aoc/IAddOnContentManager.cs | 28 +- .../Ns/Aoc/IContentsServiceManager.cs | 2 +- .../Services/Ns/Aoc/IPurchaseEventManager.cs | 8 +- .../HOS/Services/Ns/Aoc/ResultCode.cs | 6 +- .../Ns/IApplicationManagerInterface.cs | 7 +- .../HOS/Services/Ns/IDevelopInterface.cs | 2 +- ...ReadOnlyApplicationControlDataInterface.cs | 2 + .../Services/Ns/IServiceGetterInterface.cs | 2 +- .../HOS/Services/Ns/ISystemUpdateInterface.cs | 2 +- .../Ns/IVulnerabilityManagerInterface.cs | 2 +- .../HOS/Services/Nv/Host1xContext.cs | 4 +- .../HOS/Services/Nv/INvDrvDebugFSServices.cs | 2 +- .../HOS/Services/Nv/INvDrvServices.cs | 120 +++--- .../HOS/Services/Nv/INvGemControl.cs | 2 +- .../HOS/Services/Nv/INvGemCoreDump.cs | 2 +- .../Services/Nv/NvDrvServices/NvDeviceFile.cs | 4 +- .../NvHostAsGpu/NvHostAsGpuDeviceFile.cs | 7 +- .../NvHostAsGpu/Types/AddressSpaceContext.cs | 18 +- .../NvHostAsGpu/Types/AddressSpaceFlags.cs | 2 +- .../NvHostAsGpu/Types/AllocSpaceArguments.cs | 8 +- .../NvHostAsGpu/Types/FreeSpaceArguments.cs | 4 +- .../Types/InitializeExArguments.cs | 8 +- .../NvHostAsGpu/Types/MapBufferExArguments.cs | 12 +- .../NvHostAsGpu/Types/RemapArguments.cs | 8 +- .../NvHostAsGpu/Types/UnmapBufferArguments.cs | 2 +- .../NvHostChannel/ChannelInitialization.cs | 2 +- .../NvHostChannel/NvHostChannelDeviceFile.cs | 52 +-- .../NvHostChannel/NvHostGpuDeviceFile.cs | 37 +- .../Types/AllocGpfifoExArguments.cs | 12 +- .../Types/AllocObjCtxArguments.cs | 4 +- .../Types/MapCommandBufferArguments.cs | 8 +- .../NvHostChannel/Types/NvChannel.cs | 4 +- .../NvHostChannel/Types/NvChannelPriority.cs | 6 +- .../Types/SetErrorNotifierArguments.cs | 4 +- .../Types/SubmitGpfifoArguments.cs | 6 +- .../NvHostChannel/Types/SubmitGpfifoFlags.cs | 8 +- .../NvHostChannel/Types/ZcullBindArguments.cs | 4 +- .../NvHostCtrl/NvHostCtrlDeviceFile.cs | 6 +- .../NvHostCtrl/Types/EventWaitArguments.cs | 4 +- .../Types/GetConfigurationArguments.cs | 12 +- .../NvHostCtrl/Types/NvHostEvent.cs | 20 +- .../NvHostCtrl/Types/NvHostEventState.cs | 12 +- .../NvHostCtrl/Types/NvHostSyncPt.cs | 24 +- .../NvHostCtrl/Types/SyncptWaitArguments.cs | 2 +- .../NvHostCtrl/Types/SyncptWaitExArguments.cs | 2 +- .../NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs | 16 +- .../Types/GetCharacteristicsArguments.cs | 64 +-- .../Types/GetTpcMasksArguments.cs | 8 +- .../Types/ZbcSetTableArguments.cs | 1 - .../NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs | 2 +- .../Nv/NvDrvServices/NvInternalResult.cs | 50 +-- .../Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs | 35 +- .../NvDrvServices/NvMap/Types/NvMapAlloc.cs | 12 +- .../NvDrvServices/NvMap/Types/NvMapCreate.cs | 2 +- .../Nv/NvDrvServices/NvMap/Types/NvMapFree.cs | 10 +- .../NvDrvServices/NvMap/Types/NvMapFromId.cs | 2 +- .../NvDrvServices/NvMap/Types/NvMapGetId.cs | 2 +- .../NvDrvServices/NvMap/Types/NvMapHandle.cs | 16 +- .../NvMap/Types/NvMapHandleParam.cs | 12 +- .../NvMap/Types/NvMapIdDictionary.cs | 2 +- .../NvDrvServices/NvMap/Types/NvMapParam.cs | 6 +- src/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs | 38 +- .../HOS/Services/Nv/NvMemoryAllocator.cs | 6 +- .../HOS/Services/Nv/Types/NvFence.cs | 4 +- .../Types/NvIoctlNotImplementedException.cs | 10 +- .../NvQueryEventNotImplementedException.cs | 10 +- .../HOS/Services/Nv/Types/NvResult.cs | 46 +- .../HOS/Services/Nv/Types/NvStatus.cs | 2 +- .../Olsc/IOlscServiceForApplication.cs | 8 +- .../Olsc/IOlscServiceForSystemService.cs | 2 +- .../HOS/Services/Olsc/ResultCode.cs | 6 +- .../HOS/Services/Ovln/IReceiverService.cs | 2 +- .../HOS/Services/Ovln/ISenderService.cs | 2 +- .../HOS/Services/Pcie/ILogManager.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs | 2 +- .../Pctl/IParentalControlServiceFactory.cs | 6 +- .../IParentalControlService.cs | 41 +- .../HOS/Services/Pctl/ResultCode.cs | 10 +- .../Pcv/Bpc/IBoardPowerControlManager.cs | 2 +- .../Clkrst/ClkrstManager/IClkrstSession.cs | 23 +- .../Pcv/Clkrst/IArbitrationManager.cs | 2 +- .../HOS/Services/Pcv/Clkrst/IClkrstManager.cs | 4 +- .../HOS/Services/Pcv/IPcvService.cs | 2 +- .../HOS/Services/Pcv/ResultCode.cs | 6 +- .../Services/Pcv/Rgltr/IRegulatorManager.cs | 2 +- .../HOS/Services/Pcv/Rtc/IRtcManager.cs | 2 +- .../HOS/Services/Pcv/Types/DeviceCode.cs | 176 ++++---- .../HOS/Services/Pm/IBootModeInterface.cs | 2 +- .../HOS/Services/Pm/IDebugMonitorInterface.cs | 2 +- .../HOS/Services/Pm/IInformationInterface.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs | 14 +- .../HOS/Services/Psc/IPmControl.cs | 2 +- .../HOS/Services/Psc/IPmService.cs | 2 +- .../HOS/Services/Psc/IPmUnknown.cs | 2 +- .../HOS/Services/Ptm/Fan/IManager.cs | 2 +- .../HOS/Services/Ptm/Fgm/IDebugger.cs | 2 +- .../HOS/Services/Ptm/Fgm/ISession.cs | 2 +- .../HOS/Services/Ptm/Pcm/IManager.cs | 2 +- .../HOS/Services/Ptm/Psm/IPsmServer.cs | 2 +- .../HOS/Services/Ptm/Psm/IPsmSession.cs | 8 +- .../HOS/Services/Ptm/Psm/Types/ChargerType.cs | 4 +- .../HOS/Services/Ptm/Tc/IManager.cs | 2 +- .../HOS/Services/Ptm/Ts/IMeasurementServer.cs | 2 +- .../HOS/Services/Ptm/Ts/Types/Location.cs | 2 +- .../HOS/Services/Ro/IRoInterface.cs | 52 ++- src/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs | 30 +- .../HOS/Services/Ro/Types/NroInfo.cs | 38 +- .../HOS/Services/Ro/Types/NrrInfo.cs | 12 +- .../HOS/Services/Sdb/Avm/IAvmService.cs | 2 +- .../HOS/Services/Sdb/Pdm/INotifyService.cs | 2 +- .../HOS/Services/Sdb/Pdm/IQueryService.cs | 2 +- .../QueryPlayStatisticsManager.cs | 14 +- .../Types/ApplicationPlayStatistics.cs | 6 +- .../Types/PlayLogQueryCapability.cs | 4 +- .../HOS/Services/Sdb/Pdm/ResultCode.cs | 8 +- .../HOS/Services/Sdb/Pl/ISharedFontManager.cs | 18 +- .../HOS/Services/Sdb/Pl/SharedFontManager.cs | 51 ++- .../Services/Sdb/Pl/Types/SharedFontType.cs | 14 +- src/Ryujinx.HLE/HOS/Services/ServerBase.cs | 38 +- .../HOS/Services/ServiceAttributes.cs | 4 +- .../Settings/IFactorySettingsServer.cs | 2 +- .../Settings/IFirmwareDebugSettingsServer.cs | 2 +- .../HOS/Services/Settings/ISettingsServer.cs | 2 +- .../Settings/ISystemSettingsServer.cs | 123 +++--- .../HOS/Services/Settings/KeyCodeMaps.cs | 30 +- .../HOS/Services/Settings/NxSettings.cs | 6 +- .../HOS/Services/Settings/ResultCode.cs | 232 +++++----- .../Services/Settings/Types/PlatformRegion.cs | 4 +- .../HOS/Services/Sm/IManagerInterface.cs | 2 +- .../HOS/Services/Sm/IUserInterface.cs | 11 +- src/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs | 10 +- src/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs | 2 +- .../HOS/Services/Sockets/Bsd/BsdContext.cs | 6 +- .../HOS/Services/Sockets/Bsd/IClient.cs | 224 +++++----- .../Services/Sockets/Bsd/IFileDescriptor.cs | 2 +- .../Sockets/Bsd/Impl/EventFileDescriptor.cs | 2 +- .../Impl/EventFileDescriptorPollManager.cs | 9 +- .../Sockets/Bsd/Impl/ManagedSocket.cs | 6 +- .../Bsd/Impl/ManagedSocketPollManager.cs | 13 +- .../HOS/Services/Sockets/Bsd/Impl/WSAError.cs | 146 +++---- .../Sockets/Bsd/Impl/WinSockHelper.cs | 16 +- .../Services/Sockets/Bsd/ServerInterface.cs | 2 +- .../Sockets/Bsd/Types/BsdAddressFamily.cs | 4 +- .../Services/Sockets/Bsd/Types/BsdIoctl.cs | 4 +- .../Services/Sockets/Bsd/Types/BsdSockAddr.cs | 6 +- .../Bsd/Types/BsdSocketCreationFlags.cs | 2 +- .../Sockets/Bsd/Types/BsdSocketFlags.cs | 4 +- .../Sockets/Bsd/Types/BsdSocketOption.cs | 7 +- .../Bsd/Types/BsdSocketShutdownFlags.cs | 2 +- .../Sockets/Bsd/Types/EventFdFlags.cs | 2 +- .../Sockets/Bsd/Types/IPollManager.cs | 2 +- .../Services/Sockets/Bsd/Types/LinuxError.cs | 264 ++++++------ .../Services/Sockets/Bsd/Types/PollEvent.cs | 2 +- .../Sockets/Bsd/Types/PollEventData.cs | 4 +- .../Sockets/Bsd/Types/PollEventTypeMask.cs | 2 +- .../Services/Sockets/Ethc/IEthInterface.cs | 2 +- .../Sockets/Ethc/IEthInterfaceGroup.cs | 2 +- .../HOS/Services/Sockets/Nsd/IManager.cs | 14 +- .../Sockets/Nsd/Manager/FqdnResolver.cs | 14 +- .../HOS/Services/Sockets/Nsd/ResultCode.cs | 20 +- .../Types/ApplicationServerEnvironmentType.cs | 4 +- .../Services/Sockets/Nsd/Types/NsdSettings.cs | 6 +- .../Services/Sockets/Sfdnsres/IResolver.cs | 220 +++++----- .../Sockets/Sfdnsres/Proxy/DnsBlacklist.cs | 8 +- .../Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs | 8 +- .../Sockets/Sfdnsres/Types/AddrInfo4.cs | 14 +- .../Types/AddrInfoSerializedHeader.cs | 38 +- .../Sockets/Sfdnsres/Types/GaiError.cs | 4 +- .../Sockets/Sfdnsres/Types/NetDBError.cs | 4 +- .../Sfdnsres/Types/SfdnsresContants.cs | 4 +- .../HOS/Services/Spl/IGeneralInterface.cs | 14 +- .../HOS/Services/Spl/IRandomInterface.cs | 6 +- .../HOS/Services/Spl/ResultCode.cs | 6 +- .../HOS/Services/Spl/Types/ConfigItem.cs | 34 +- .../HOS/Services/Spl/Types/DramId.cs | 4 +- .../HOS/Services/Spl/Types/HardwareState.cs | 4 +- .../HOS/Services/Spl/Types/HardwareType.cs | 2 +- .../HOS/Services/Spl/Types/SmcResult.cs | 23 +- .../HOS/Services/Srepo/ISrepoService.cs | 2 +- .../Services/Ssl/BuiltInCertificateManager.cs | 19 +- .../HOS/Services/Ssl/ISslService.cs | 12 +- .../HOS/Services/Ssl/ResultCode.cs | 20 +- .../Services/Ssl/SslService/ISslConnection.cs | 47 +- .../Ssl/SslService/ISslConnectionBase.cs | 2 +- .../Services/Ssl/SslService/ISslContext.cs | 12 +- .../SslService/SslManagedSocketConnection.cs | 32 +- .../HOS/Services/Ssl/Types/CaCertificateId.cs | 2 +- .../Services/Ssl/Types/CertificateFormat.cs | 4 +- .../HOS/Services/Ssl/Types/IoMode.cs | 6 +- .../HOS/Services/Ssl/Types/OptionType.cs | 4 +- .../Services/Ssl/Types/SessionCacheMode.cs | 4 +- .../HOS/Services/Ssl/Types/SslVersion.cs | 6 +- .../Services/Ssl/Types/TrustedCertStatus.cs | 2 +- .../HOS/Services/Ssl/Types/VerifyOption.cs | 14 +- .../SurfaceFlinger/BufferItemConsumer.cs | 2 +- .../Services/SurfaceFlinger/BufferQueue.cs | 2 +- .../SurfaceFlinger/BufferQueueConsumer.cs | 24 +- .../SurfaceFlinger/BufferQueueCore.cs | 94 ++-- .../SurfaceFlinger/BufferQueueProducer.cs | 108 ++--- .../HOS/Services/SurfaceFlinger/BufferSlot.cs | 28 +- .../SurfaceFlinger/BufferSlotArray.cs | 6 +- .../Services/SurfaceFlinger/ConsumerBase.cs | 14 +- .../SurfaceFlinger/HOSBinderDriverServer.cs | 4 +- .../HOS/Services/SurfaceFlinger/IBinder.cs | 4 +- .../SurfaceFlinger/IGraphicBufferProducer.cs | 58 +-- .../SurfaceFlinger/IHOSBinderDriver.cs | 37 +- .../HOS/Services/SurfaceFlinger/LayerState.cs | 4 +- .../SurfaceFlinger/NativeWindowApi.cs | 10 +- .../SurfaceFlinger/NativeWindowAttribute.cs | 12 +- .../SurfaceFlinger/NativeWindowScalingMode.cs | 8 +- .../SurfaceFlinger/NativeWindowTransform.cs | 16 +- .../HOS/Services/SurfaceFlinger/Parcel.cs | 42 +- .../HOS/Services/SurfaceFlinger/Status.cs | 23 +- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 100 +++-- .../SurfaceFlinger/Types/AndroidFence.cs | 14 +- .../Types/AndroidStrongPointer.cs | 6 +- .../SurfaceFlinger/Types/BufferInfo.cs | 4 +- .../SurfaceFlinger/Types/BufferItem.cs | 73 ++-- .../SurfaceFlinger/Types/BufferState.cs | 8 +- .../Types/Color/ColorBytePerPixel.cs | 24 +- .../Types/Color/ColorComponent.cs | 4 +- .../Types/Color/ColorDataType.cs | 6 +- .../SurfaceFlinger/Types/Color/ColorFormat.cs | 9 +- .../SurfaceFlinger/Types/Color/ColorShift.cs | 8 +- .../SurfaceFlinger/Types/Color/ColorSpace.cs | 40 +- .../Types/Color/ColorSwizzle.cs | 6 +- .../SurfaceFlinger/Types/GraphicBuffer.cs | 26 +- .../Types/GraphicBufferHeader.cs | 12 +- .../SurfaceFlinger/Types/NvGraphicBuffer.cs | 2 +- .../Types/NvGraphicBufferSurface.cs | 2 +- .../Types/NvGraphicBufferSurfaceArray.cs | 25 +- .../HOS/Services/SurfaceFlinger/Types/Rect.cs | 30 +- .../Clock/LocalSystemClockContextWriter.cs | 4 +- .../Clock/NetworkSystemClockContextWriter.cs | 4 +- .../Clock/StandardLocalSystemClockCore.cs | 2 +- .../Clock/StandardNetworkSystemClockCore.cs | 2 +- .../Time/Clock/StandardSteadyClockCore.cs | 14 +- .../Time/Clock/StandardUserSystemClockCore.cs | 18 +- .../Services/Time/Clock/SteadyClockCore.cs | 12 +- .../Clock/SystemClockContextUpdateCallback.cs | 14 +- .../Services/Time/Clock/SystemClockCore.cs | 23 +- .../Time/Clock/TickBasedSteadyClockCore.cs | 8 +- .../Time/Clock/Types/ClockSnapshot.cs | 20 +- .../Time/Clock/Types/SteadyClockTimePoint.cs | 10 +- .../Time/Clock/Types/SystemClockContext.cs | 4 +- .../Services/Time/Clock/Types/TimeSpanType.cs | 10 +- .../HOS/Services/Time/IAlarmService.cs | 2 +- .../Time/IPowerStateRequestHandler.cs | 2 +- .../Services/Time/IStaticServiceForGlue.cs | 8 +- .../HOS/Services/Time/IStaticServiceForPsc.cs | 43 +- .../HOS/Services/Time/ITimeServiceManager.cs | 35 +- .../HOS/Services/Time/ResultCode.cs | 33 +- .../Time/StaticService/ISteadyClock.cs | 12 +- .../Time/StaticService/ISystemClock.cs | 18 +- .../StaticService/ITimeZoneServiceForGlue.cs | 27 +- .../StaticService/ITimeZoneServiceForPsc.cs | 39 +- .../HOS/Services/Time/TimeManager.cs | 43 +- .../HOS/Services/Time/TimeSharedMemory.cs | 28 +- .../HOS/Services/Time/TimeZone/TimeZone.cs | 251 +++++------ .../Time/TimeZone/TimeZoneContentManager.cs | 64 ++- .../Services/Time/TimeZone/TimeZoneManager.cs | 42 +- .../TimeZone/Types/CalendarAdditionalInfo.cs | 2 +- .../Time/TimeZone/Types/CalendarInfo.cs | 4 +- .../Time/TimeZone/Types/CalendarTime.cs | 2 +- .../Time/TimeZone/Types/TimeTypeInfo.cs | 2 +- .../Time/TimeZone/Types/TimeZoneRule.cs | 2 +- .../Time/TimeZone/Types/TzifHeader.cs | 2 +- .../Services/Time/Types/SteadyClockContext.cs | 2 +- .../Services/Time/Types/TimePermissions.cs | 20 +- .../HOS/Services/Usb/IClientRootSession.cs | 2 +- .../HOS/Services/Usb/IDsService.cs | 2 +- .../HOS/Services/Usb/IPdCradleManager.cs | 2 +- .../HOS/Services/Usb/IPdManager.cs | 2 +- .../HOS/Services/Usb/IPmService.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs | 2 +- .../Services/Vi/IApplicationRootService.cs | 2 +- .../HOS/Services/Vi/IManagerRootService.cs | 2 +- .../HOS/Services/Vi/ISystemRootService.cs | 2 +- src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs | 14 +- .../AndroidSurfaceComposerClient.cs | 4 +- .../IManagerDisplayService.cs | 12 +- .../ISystemDisplayService.cs | 6 +- .../Types/DestinationScalingMode.cs | 4 +- .../Types/DisplayInfo.cs | 12 +- .../Types/SourceScalingMode.cs | 4 +- .../RootService/IApplicationDisplayService.cs | 75 ++-- .../HOS/Services/Vi/Types/ViServiceType.cs | 4 +- .../HOS/Services/Wlan/IInfraManager.cs | 2 +- .../HOS/Services/Wlan/ILocalGetActionFrame.cs | 2 +- .../HOS/Services/Wlan/ILocalGetFrame.cs | 2 +- .../HOS/Services/Wlan/ILocalManager.cs | 2 +- .../HOS/Services/Wlan/ISocketGetFrame.cs | 2 +- .../HOS/Services/Wlan/ISocketManager.cs | 2 +- .../HOS/Services/Wlan/IUnknown1.cs | 2 +- .../HOS/SystemState/AppletStateMgr.cs | 4 +- src/Ryujinx.HLE/HOS/SystemState/ColorSet.cs | 2 +- .../HOS/SystemState/KeyboardLayout.cs | 2 +- src/Ryujinx.HLE/HOS/SystemState/RegionCode.cs | 2 +- .../HOS/SystemState/SystemLanguage.cs | 4 +- .../HOS/SystemState/SystemStateMgr.cs | 19 +- .../HOS/SystemState/TitleLanguage.cs | 2 +- .../HOS/Tamper/AtmosphereCompiler.cs | 37 +- .../HOS/Tamper/AtmosphereProgram.cs | 4 +- .../HOS/Tamper/CodeEmitters/Arithmetic.cs | 50 ++- .../CodeEmitters/EndConditionalBlock.cs | 6 +- .../Tamper/CodeEmitters/LegacyArithmetic.cs | 24 +- .../CodeEmitters/LoadRegisterWithConstant.cs | 2 +- .../Tamper/CodeEmitters/MemoryConditional.cs | 2 +- .../HOS/Tamper/CodeEmitters/ResumeProcess.cs | 1 - .../HOS/Tamper/CodeEmitters/StartEndLoop.cs | 2 +- .../CodeEmitters/StoreConstantToAddress.cs | 2 +- .../CodeEmitters/StoreConstantToMemory.cs | 2 +- src/Ryujinx.HLE/HOS/Tamper/CodeType.cs | 2 +- src/Ryujinx.HLE/HOS/Tamper/Comparison.cs | 2 +- .../HOS/Tamper/CompilationContext.cs | 18 +- .../HOS/Tamper/Conditions/CondEQ.cs | 4 +- .../HOS/Tamper/Conditions/CondGE.cs | 4 +- .../HOS/Tamper/Conditions/CondGT.cs | 4 +- .../HOS/Tamper/Conditions/CondLE.cs | 4 +- .../HOS/Tamper/Conditions/CondLT.cs | 4 +- .../HOS/Tamper/Conditions/CondNE.cs | 4 +- .../HOS/Tamper/Conditions/InputMask.cs | 4 +- .../HOS/Tamper/InstructionHelper.cs | 37 +- src/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs | 41 +- .../HOS/Tamper/Operations/Block.cs | 2 +- .../HOS/Tamper/Operations/ForBlock.cs | 6 +- .../HOS/Tamper/Operations/IfBlock.cs | 6 +- .../HOS/Tamper/Operations/OpAdd.cs | 6 +- .../HOS/Tamper/Operations/OpAnd.cs | 6 +- .../HOS/Tamper/Operations/OpLog.cs | 4 +- .../HOS/Tamper/Operations/OpLsh.cs | 6 +- .../HOS/Tamper/Operations/OpMov.cs | 4 +- .../HOS/Tamper/Operations/OpMul.cs | 6 +- .../HOS/Tamper/Operations/OpNot.cs | 4 +- src/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs | 6 +- .../HOS/Tamper/Operations/OpProcCtrl.cs | 4 +- .../HOS/Tamper/Operations/OpRsh.cs | 6 +- .../HOS/Tamper/Operations/OpSub.cs | 6 +- .../HOS/Tamper/Operations/OpXor.cs | 6 +- src/Ryujinx.HLE/HOS/Tamper/Pointer.cs | 4 +- src/Ryujinx.HLE/HOS/Tamper/Register.cs | 2 +- .../HOS/Tamper/TamperedKProcess.cs | 4 +- src/Ryujinx.HLE/HOS/Tamper/Value.cs | 8 +- src/Ryujinx.HLE/HOS/TamperMachine.cs | 16 +- src/Ryujinx.HLE/HOS/UserChannelPersistence.cs | 2 +- src/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs | 4 +- src/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs | 133 +++--- src/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs | 36 +- src/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs | 14 +- src/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs | 14 +- .../Loaders/Elf/ElfSymbolBinding.cs | 6 +- src/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs | 14 +- .../Loaders/Elf/ElfSymbolVisibility.cs | 10 +- .../Loaders/Executables/IExecutable.cs | 10 +- .../Loaders/Executables/KipExecutable.cs | 52 +-- .../Loaders/Executables/NroExecutable.cs | 18 +- .../Loaders/Executables/NsoExecutable.cs | 30 +- src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs | 27 +- .../Loaders/Mods/IPSwitchPatcher.cs | 50 ++- src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs | 6 +- src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs | 20 +- src/Ryujinx.HLE/Loaders/Npdm/ACID.cs | 24 +- .../Loaders/Npdm/FsAccessControl.cs | 22 +- .../Loaders/Npdm/FsAccessHeader.cs | 11 +- .../Loaders/Npdm/KernelAccessControl.cs | 2 +- src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs | 28 +- .../Loaders/Npdm/ServiceAccessControl.cs | 6 +- .../Extensions/FileSystemExtensions.cs | 4 +- .../Extensions/LocalFileSystemExtensions.cs | 8 +- .../Extensions/MetaLoaderExtensions.cs | 2 +- .../Processes/Extensions/NcaExtensions.cs | 4 +- .../PartitionFileSystemExtensions.cs | 14 +- .../Loaders/Processes/ProcessConst.cs | 8 +- .../Loaders/Processes/ProcessLoader.cs | 23 +- .../Loaders/Processes/ProcessLoaderHelper.cs | 46 +- .../Loaders/Processes/ProcessResult.cs | 43 +- src/Ryujinx.HLE/MemoryConfiguration.cs | 16 +- src/Ryujinx.HLE/PerformanceStatistics.cs | 62 +-- src/Ryujinx.HLE/Switch.cs | 29 +- .../Ui/DynamicTextChangedHandler.cs | 2 +- .../Ui/IDynamicTextInputHandler.cs | 2 +- src/Ryujinx.HLE/Ui/IHostUiHandler.cs | 2 +- src/Ryujinx.HLE/Ui/Input/NpadReader.cs | 12 +- src/Ryujinx.HLE/Ui/KeyPressedHandler.cs | 2 +- src/Ryujinx.HLE/Ui/KeyReleasedHandler.cs | 2 +- src/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs | 18 +- src/Ryujinx.HLE/Utilities/StringUtils.cs | 49 +-- src/Ryujinx/Ui/MainWindow.cs | 2 +- .../Ui/Widgets/GameTableContextMenu.cs | 2 +- src/Ryujinx/Ui/Windows/AvatarWindow.cs | 2 +- 1015 files changed, 8173 insertions(+), 7615 deletions(-) rename src/Ryujinx.HLE/HOS/Kernel/Common/{MemroySize.cs => MemorySize.cs} (82%) diff --git a/src/Ryujinx.Ava/Common/ApplicationHelper.cs b/src/Ryujinx.Ava/Common/ApplicationHelper.cs index b8f2366cd..d28d5aac1 100644 --- a/src/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/src/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -114,7 +114,7 @@ namespace Ryujinx.Ava.Common public static void OpenSaveDir(ulong saveDataId) { - string saveRootPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}"); + string saveRootPath = Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}"); if (!Directory.Exists(saveRootPath)) { diff --git a/src/Ryujinx.Ava/UI/Models/SaveModel.cs b/src/Ryujinx.Ava/UI/Models/SaveModel.cs index 2f220162f..f15befbb3 100644 --- a/src/Ryujinx.Ava/UI/Models/SaveModel.cs +++ b/src/Ryujinx.Ava/UI/Models/SaveModel.cs @@ -2,6 +2,7 @@ using LibHac.Fs; using LibHac.Ncm; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; +using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.App.Common; using System; using System.IO; @@ -81,7 +82,7 @@ namespace Ryujinx.Ava.UI.Models Task.Run(() => { - var saveRoot = Path.Combine(MainWindow.MainWindowViewModel.VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}"); + var saveRoot = Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}"); long totalSize = GetDirectorySize(saveRoot); diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index cb8da9ede..89b591229 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.ViewModels } string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data); - string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); + string avatarPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(avatarPath)) { diff --git a/src/Ryujinx.HLE/AssemblyInfo.cs b/src/Ryujinx.HLE/AssemblyInfo.cs index 9d7bad6be..82519d542 100644 --- a/src/Ryujinx.HLE/AssemblyInfo.cs +++ b/src/Ryujinx.HLE/AssemblyInfo.cs @@ -1,3 +1,3 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Ryujinx.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("Ryujinx.Tests")] diff --git a/src/Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs b/src/Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs index fe41b02a6..4e06ab772 100644 --- a/src/Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs +++ b/src/Ryujinx.HLE/Exceptions/GuestBrokeExecutionException.cs @@ -8,4 +8,4 @@ namespace Ryujinx.HLE.Exceptions public GuestBrokeExecutionException() : base(ExMsg) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/InternalServiceException.cs b/src/Ryujinx.HLE/Exceptions/InternalServiceException.cs index 85de63a6a..e0ee5eb91 100644 --- a/src/Ryujinx.HLE/Exceptions/InternalServiceException.cs +++ b/src/Ryujinx.HLE/Exceptions/InternalServiceException.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.Exceptions { - class InternalServiceException: Exception + class InternalServiceException : Exception { public InternalServiceException(string message) : base(message) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs b/src/Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs index d08de581b..bddd827a0 100644 --- a/src/Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs +++ b/src/Ryujinx.HLE/Exceptions/InvalidFirmwarePackageException.cs @@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions { public InvalidFirmwarePackageException(string message) : base(message) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/InvalidNpdmException.cs b/src/Ryujinx.HLE/Exceptions/InvalidNpdmException.cs index 98675e42c..c4036ea0d 100644 --- a/src/Ryujinx.HLE/Exceptions/InvalidNpdmException.cs +++ b/src/Ryujinx.HLE/Exceptions/InvalidNpdmException.cs @@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions { public InvalidNpdmException(string message) : base(message) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs b/src/Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs index 2f03d13ab..6852dcefe 100644 --- a/src/Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs +++ b/src/Ryujinx.HLE/Exceptions/InvalidStructLayoutException.cs @@ -3,13 +3,13 @@ using System.Runtime.CompilerServices; namespace Ryujinx.HLE.Exceptions { - public class InvalidStructLayoutException<T> : Exception + public class InvalidStructLayoutException<T> : Exception { static readonly Type _structType = typeof(T); public InvalidStructLayoutException(string message) : base(message) { } - + public InvalidStructLayoutException(int expectedSize) : base($"Type {_structType.Name} has the wrong size. Expected: {expectedSize} bytes, got: {Unsafe.SizeOf<T>()} bytes") { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs b/src/Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs index 3c63e0641..d9afff111 100644 --- a/src/Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs +++ b/src/Ryujinx.HLE/Exceptions/InvalidSystemResourceException.cs @@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions { public InvalidSystemResourceException(string message) : base(message) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs b/src/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs index 40b19cc8e..e43c838a2 100644 --- a/src/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs +++ b/src/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs @@ -47,7 +47,7 @@ namespace Ryujinx.HLE.Exceptions private string BuildMessage() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); // Print the IPC command details (service name, command ID, and handler) (Type callingType, MethodBase callingMethod) = WalkStackTrace(new StackTrace(this)); @@ -58,9 +58,9 @@ namespace Ryujinx.HLE.Exceptions var ipcCommands = Request.Type > IpcMessageType.TipcCloseSession ? Service.TipcCommands : Service.CmifCommands; // Find the handler for the method called - var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod); + var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod); var ipcCommandId = ipcHandler.Key; - var ipcMethod = ipcHandler.Value; + var ipcMethod = ipcHandler.Value; if (ipcMethod != null) { @@ -73,9 +73,9 @@ namespace Ryujinx.HLE.Exceptions sb.AppendLine(Context.Thread.GetGuestStackTrace()); // Print buffer information - if (Request.PtrBuff.Count > 0 || - Request.SendBuff.Count > 0 || - Request.ReceiveBuff.Count > 0 || + if (Request.PtrBuff.Count > 0 || + Request.SendBuff.Count > 0 || + Request.ReceiveBuff.Count > 0 || Request.ExchangeBuff.Count > 0 || Request.RecvListBuff.Count > 0) { @@ -149,7 +149,7 @@ namespace Ryujinx.HLE.Exceptions // Find the IIpcService method that threw this exception while ((frame = trace.GetFrame(i++)) != null) { - var method = frame.GetMethod(); + var method = frame.GetMethod(); var declType = method.DeclaringType; if (typeof(IpcService).IsAssignableFrom(declType)) @@ -161,4 +161,4 @@ namespace Ryujinx.HLE.Exceptions return (null, null); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/TamperCompilationException.cs b/src/Ryujinx.HLE/Exceptions/TamperCompilationException.cs index 02d871638..370df5d3b 100644 --- a/src/Ryujinx.HLE/Exceptions/TamperCompilationException.cs +++ b/src/Ryujinx.HLE/Exceptions/TamperCompilationException.cs @@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions { public TamperCompilationException(string message) : base(message) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/TamperExecutionException.cs b/src/Ryujinx.HLE/Exceptions/TamperExecutionException.cs index d62effe3a..1f132607d 100644 --- a/src/Ryujinx.HLE/Exceptions/TamperExecutionException.cs +++ b/src/Ryujinx.HLE/Exceptions/TamperExecutionException.cs @@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions { public TamperExecutionException(string message) : base(message) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs b/src/Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs index dfbd6c272..3886e5e76 100644 --- a/src/Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs +++ b/src/Ryujinx.HLE/Exceptions/UndefinedInstructionException.cs @@ -10,4 +10,4 @@ namespace Ryujinx.HLE.Exceptions public UndefinedInstructionException(ulong address, int opCode) : base(string.Format(ExMsg, address, opCode)) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs index e00310a07..13f987638 100644 --- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -26,17 +26,17 @@ namespace Ryujinx.HLE.FileSystem public class ContentManager { private const ulong SystemVersionTitleId = 0x0100000000000809; - private const ulong SystemUpdateTitleId = 0x0100000000000816; + private const ulong SystemUpdateTitleId = 0x0100000000000816; private Dictionary<StorageId, LinkedList<LocationEntry>> _locationEntries; - private Dictionary<string, ulong> _sharedFontTitleDictionary; - private Dictionary<ulong, string> _systemTitlesNameDictionary; - private Dictionary<string, string> _sharedFontFilenameDictionary; + private readonly Dictionary<string, ulong> _sharedFontTitleDictionary; + private readonly Dictionary<ulong, string> _systemTitlesNameDictionary; + private readonly Dictionary<string, string> _sharedFontFilenameDictionary; private SortedDictionary<(ulong titleId, NcaContentType type), string> _contentDictionary; - private struct AocItem + private readonly struct AocItem { public readonly string ContainerPath; public readonly string NcaPath; @@ -48,16 +48,16 @@ namespace Ryujinx.HLE.FileSystem } } - private SortedList<ulong, AocItem> _aocData { get; } + private SortedList<ulong, AocItem> AocData { get; } - private VirtualFileSystem _virtualFileSystem; + private readonly VirtualFileSystem _virtualFileSystem; private readonly object _lock = new(); public ContentManager(VirtualFileSystem virtualFileSystem) { _contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>(); - _locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>(); + _locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>(); _sharedFontTitleDictionary = new Dictionary<string, ulong> { @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.FileSystem { "FontExtendedChineseSimplified", 0x0100000000000814 }, { "FontKorean", 0x0100000000000812 }, { "FontChineseTraditional", 0x0100000000000813 }, - { "FontNintendoExtended", 0x0100000000000810 } + { "FontNintendoExtended", 0x0100000000000810 }, }; _systemTitlesNameDictionary = new Dictionary<ulong, string>() @@ -86,12 +86,12 @@ namespace Ryujinx.HLE.FileSystem { "FontExtendedChineseSimplified", "nintendo_udsg-r_ext_zh-cn_003.bfttf" }, { "FontKorean", "nintendo_udsg-r_ko_003.bfttf" }, { "FontChineseTraditional", "nintendo_udjxh-db_zh-tw_003.bfttf" }, - { "FontNintendoExtended", "nintendo_ext_003.bfttf" } + { "FontNintendoExtended", "nintendo_ext_003.bfttf" }, }; _virtualFileSystem = virtualFileSystem; - _aocData = new SortedList<ulong, AocItem>(); + AocData = new SortedList<ulong, AocItem>(); } public void LoadEntries(Switch device = null) @@ -99,18 +99,18 @@ namespace Ryujinx.HLE.FileSystem lock (_lock) { _contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>(); - _locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>(); + _locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>(); foreach (StorageId storageId in Enum.GetValues<StorageId>()) { - string contentDirectory = null; - string contentPathString = null; + string contentDirectory = null; + string contentPathString = null; string registeredDirectory = null; try { - contentPathString = ContentPath.GetContentPath(storageId); - contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString); + contentPathString = ContentPath.GetContentPath(storageId); + contentDirectory = ContentPath.GetRealPath(contentPathString); registeredDirectory = Path.Combine(contentDirectory, "registered"); } catch (NotSupportedException) @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.FileSystem Directory.CreateDirectory(registeredDirectory); - LinkedList<LocationEntry> locationList = new LinkedList<LocationEntry>(); + LinkedList<LocationEntry> locationList = new(); void AddEntry(LocationEntry entry) { @@ -133,24 +133,19 @@ namespace Ryujinx.HLE.FileSystem { string ncaName = new DirectoryInfo(directoryPath).Name.Replace(".nca", string.Empty); - using (FileStream ncaFile = File.OpenRead(Directory.GetFiles(directoryPath)[0])) - { - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); + using FileStream ncaFile = File.OpenRead(Directory.GetFiles(directoryPath)[0]); + Nca nca = new(_virtualFileSystem.KeySet, ncaFile.AsStorage()); - string switchPath = contentPathString + ":/" + ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar); + string switchPath = contentPathString + ":/" + ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar); - // Change path format to switch's - switchPath = switchPath.Replace('\\', '/'); + // Change path format to switch's + switchPath = switchPath.Replace('\\', '/'); - LocationEntry entry = new LocationEntry(switchPath, - 0, - nca.Header.TitleId, - nca.Header.ContentType); + LocationEntry entry = new(switchPath, 0, nca.Header.TitleId, nca.Header.ContentType); - AddEntry(entry); + AddEntry(entry); - _contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName); - } + _contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName); } } @@ -160,24 +155,19 @@ namespace Ryujinx.HLE.FileSystem { string ncaName = Path.GetFileNameWithoutExtension(filePath); - using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); + using FileStream ncaFile = new(filePath, FileMode.Open, FileAccess.Read); + Nca nca = new(_virtualFileSystem.KeySet, ncaFile.AsStorage()); - string switchPath = contentPathString + ":/" + filePath.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar); + string switchPath = contentPathString + ":/" + filePath.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar); - // Change path format to switch's - switchPath = switchPath.Replace('\\', '/'); + // Change path format to switch's + switchPath = switchPath.Replace('\\', '/'); - LocationEntry entry = new LocationEntry(switchPath, - 0, - nca.Header.TitleId, - nca.Header.ContentType); + LocationEntry entry = new(switchPath, 0, nca.Header.TitleId, nca.Header.ContentType); - AddEntry(entry); + AddEntry(entry); - _contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName); - } + _contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName); } } @@ -186,10 +176,7 @@ namespace Ryujinx.HLE.FileSystem _locationEntries.Remove(storageId); } - if (!_locationEntries.ContainsKey(storageId)) - { - _locationEntries.Add(storageId, locationList); - } + _locationEntries.TryAdd(storageId, locationList); } if (device != null) @@ -239,7 +226,7 @@ namespace Ryujinx.HLE.FileSystem public void AddAocItem(ulong titleId, string containerPath, string ncaPath, bool mergedToContainer = false) { // TODO: Check Aoc version. - if (!_aocData.TryAdd(titleId, new AocItem(containerPath, ncaPath))) + if (!AocData.TryAdd(titleId, new AocItem(containerPath, ncaPath))) { Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16}"); } @@ -249,7 +236,7 @@ namespace Ryujinx.HLE.FileSystem if (!mergedToContainer) { - using FileStream fileStream = File.OpenRead(containerPath); + using FileStream fileStream = File.OpenRead(containerPath); using PartitionFileSystem partitionFileSystem = new(fileStream.AsStorage()); _virtualFileSystem.ImportTickets(partitionFileSystem); @@ -257,17 +244,17 @@ namespace Ryujinx.HLE.FileSystem } } - public void ClearAocData() => _aocData.Clear(); + public void ClearAocData() => AocData.Clear(); - public int GetAocCount() => _aocData.Count; + public int GetAocCount() => AocData.Count; - public IList<ulong> GetAocTitleIds() => _aocData.Select(e => e.Key).ToList(); + public IList<ulong> GetAocTitleIds() => AocData.Select(e => e.Key).ToList(); public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage, IntegrityCheckLevel integrityCheckLevel) { aocStorage = null; - if (_aocData.TryGetValue(aocTitleId, out AocItem aoc)) + if (AocData.TryGetValue(aocTitleId, out AocItem aoc)) { var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read); using var ncaFile = new UniqueRef<IFile>(); @@ -307,7 +294,7 @@ namespace Ryujinx.HLE.FileSystem { lock (_lock) { - LinkedList<LocationEntry> locationList = _locationEntries[storageId]; + LinkedList<LocationEntry> locationList = _locationEntries[storageId]; LinkedListNode<LocationEntry> locationEntry = locationList.First; while (locationEntry != null) @@ -331,7 +318,7 @@ namespace Ryujinx.HLE.FileSystem if (_contentDictionary.ContainsValue(ncaId)) { var content = _contentDictionary.FirstOrDefault(x => x.Value == ncaId); - ulong titleId = content.Key.Item1; + ulong titleId = content.Key.titleId; NcaContentType contentType = content.Key.type; StorageId storage = GetInstalledStorage(titleId, contentType, storageId); @@ -403,19 +390,17 @@ namespace Ryujinx.HLE.FileSystem return false; } - string installedPath = _virtualFileSystem.SwitchPathToSystemPath(locationEntry.ContentPath); + string installedPath = VirtualFileSystem.SwitchPathToSystemPath(locationEntry.ContentPath); if (!string.IsNullOrWhiteSpace(installedPath)) { if (File.Exists(installedPath)) { - using (FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read)) - { - Nca nca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); - bool contentCheck = nca.Header.ContentType == contentType; + using FileStream file = new(installedPath, FileMode.Open, FileAccess.Read); + Nca nca = new(_virtualFileSystem.KeySet, file.AsStorage()); + bool contentCheck = nca.Header.ContentType == contentType; - return contentCheck; - } + return contentCheck; } } @@ -426,9 +411,9 @@ namespace Ryujinx.HLE.FileSystem { LinkedList<LocationEntry> locationList = null; - if (_locationEntries.ContainsKey(storageId)) + if (_locationEntries.TryGetValue(storageId, out LinkedList<LocationEntry> locationEntry)) { - locationList = _locationEntries[storageId]; + locationList = locationEntry; } if (locationList != null) @@ -446,9 +431,9 @@ namespace Ryujinx.HLE.FileSystem { LinkedList<LocationEntry> locationList = null; - if (_locationEntries.ContainsKey(storageId)) + if (_locationEntries.TryGetValue(storageId, out LinkedList<LocationEntry> locationEntry)) { - locationList = _locationEntries[storageId]; + locationList = locationEntry; } if (locationList != null) @@ -487,10 +472,10 @@ namespace Ryujinx.HLE.FileSystem public void InstallFirmware(string firmwareSource) { - string contentPathString = ContentPath.GetContentPath(StorageId.BuiltInSystem); - string contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString); + string contentPathString = ContentPath.GetContentPath(StorageId.BuiltInSystem); + string contentDirectory = ContentPath.GetRealPath(contentPathString); string registeredDirectory = Path.Combine(contentDirectory, "registered"); - string temporaryDirectory = Path.Combine(contentDirectory, "temp"); + string temporaryDirectory = Path.Combine(contentDirectory, "temp"); if (Directory.Exists(temporaryDirectory)) { @@ -510,28 +495,27 @@ namespace Ryujinx.HLE.FileSystem throw new FileNotFoundException("Firmware file does not exist."); } - FileInfo info = new FileInfo(firmwareSource); + FileInfo info = new(firmwareSource); - using (FileStream file = File.OpenRead(firmwareSource)) + using FileStream file = File.OpenRead(firmwareSource); + + switch (info.Extension) { - switch (info.Extension) - { - case ".zip": - using (ZipArchive archive = ZipFile.OpenRead(firmwareSource)) - { - InstallFromZip(archive, temporaryDirectory); - } - break; - case ".xci": - Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); - InstallFromCart(xci, temporaryDirectory); - break; - default: - throw new InvalidFirmwarePackageException("Input file is not a valid firmware package"); - } - - FinishInstallation(temporaryDirectory, registeredDirectory); + case ".zip": + using (ZipArchive archive = ZipFile.OpenRead(firmwareSource)) + { + InstallFromZip(archive, temporaryDirectory); + } + break; + case ".xci": + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); + InstallFromCart(xci, temporaryDirectory); + break; + default: + throw new InvalidFirmwarePackageException("Input file is not a valid firmware package"); } + + FinishInstallation(temporaryDirectory, registeredDirectory); } private void FinishInstallation(string temporaryDirectory, string registeredDirectory) @@ -555,7 +539,7 @@ namespace Ryujinx.HLE.FileSystem { foreach (var entry in filesystem.EnumerateEntries("/", "*.nca")) { - Nca nca = new Nca(_virtualFileSystem.KeySet, OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage()); + Nca nca = new(_virtualFileSystem.KeySet, OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage()); SaveNca(nca, entry.Name.Remove(entry.Name.IndexOf('.')), temporaryDirectory); } @@ -575,52 +559,47 @@ namespace Ryujinx.HLE.FileSystem } } - private void InstallFromZip(ZipArchive archive, string temporaryDirectory) + private static void InstallFromZip(ZipArchive archive, string temporaryDirectory) { - using (archive) + foreach (var entry in archive.Entries) { - foreach (var entry in archive.Entries) + if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00")) { - if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00")) + // Clean up the name and get the NcaId + + string[] pathComponents = entry.FullName.Replace(".cnmt", "").Split('/'); + + string ncaId = pathComponents[^1]; + + // If this is a fragmented nca, we need to get the previous element.GetZip + if (ncaId.Equals("00")) { - // Clean up the name and get the NcaId + ncaId = pathComponents[^2]; + } - string[] pathComponents = entry.FullName.Replace(".cnmt", "").Split('/'); + if (ncaId.Contains(".nca")) + { + string newPath = Path.Combine(temporaryDirectory, ncaId); - string ncaId = pathComponents[pathComponents.Length - 1]; + Directory.CreateDirectory(newPath); - // If this is a fragmented nca, we need to get the previous element.GetZip - if (ncaId.Equals("00")) - { - ncaId = pathComponents[pathComponents.Length - 2]; - } - - if (ncaId.Contains(".nca")) - { - string newPath = Path.Combine(temporaryDirectory, ncaId); - - Directory.CreateDirectory(newPath); - - entry.ExtractToFile(Path.Combine(newPath, "00")); - } + entry.ExtractToFile(Path.Combine(newPath, "00")); } } } } - public void SaveNca(Nca nca, string ncaId, string temporaryDirectory) + public static void SaveNca(Nca nca, string ncaId, string temporaryDirectory) { string newPath = Path.Combine(temporaryDirectory, ncaId + ".nca"); Directory.CreateDirectory(newPath); - using (FileStream file = File.Create(Path.Combine(newPath, "00"))) - { - nca.BaseStorage.AsStream().CopyTo(file); - } + using FileStream file = File.Create(Path.Combine(newPath, "00")); + nca.BaseStorage.AsStream().CopyTo(file); } - private IFile OpenPossibleFragmentedFile(IFileSystem filesystem, string path, OpenMode mode) + private static IFile OpenPossibleFragmentedFile(IFileSystem filesystem, string path, OpenMode mode) { using var file = new UniqueRef<IFile>(); @@ -636,14 +615,12 @@ namespace Ryujinx.HLE.FileSystem return file.Release(); } - private Stream GetZipStream(ZipArchiveEntry entry) + private static Stream GetZipStream(ZipArchiveEntry entry) { MemoryStream dest = MemoryStreamManager.Shared.GetStream(); - using (Stream src = entry.Open()) - { - src.CopyTo(dest); - } + using Stream src = entry.Open(); + src.CopyTo(dest); return dest; } @@ -659,7 +636,7 @@ namespace Ryujinx.HLE.FileSystem throw new MissingKeyException("HeaderKey is empty. Cannot decrypt NCA headers."); } - Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new Dictionary<ulong, List<(NcaContentType, string)>>(); + Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new(); if (Directory.Exists(firmwarePackage)) { @@ -671,33 +648,32 @@ namespace Ryujinx.HLE.FileSystem throw new FileNotFoundException("Firmware file does not exist."); } - FileInfo info = new FileInfo(firmwarePackage); + FileInfo info = new(firmwarePackage); - using (FileStream file = File.OpenRead(firmwarePackage)) + using FileStream file = File.OpenRead(firmwarePackage); + + switch (info.Extension) { - switch (info.Extension) - { - case ".zip": - using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage)) - { - return VerifyAndGetVersionZip(archive); - } - case ".xci": - Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); + case ".zip": + using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage)) + { + return VerifyAndGetVersionZip(archive); + } + case ".xci": + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - if (xci.HasPartition(XciPartitionType.Update)) - { - XciPartition partition = xci.OpenPartition(XciPartitionType.Update); + if (xci.HasPartition(XciPartitionType.Update)) + { + XciPartition partition = xci.OpenPartition(XciPartitionType.Update); - return VerifyAndGetVersion(partition); - } - else - { - throw new InvalidFirmwarePackageException("Update not found in xci file."); - } - default: - break; - } + return VerifyAndGetVersion(partition); + } + else + { + throw new InvalidFirmwarePackageException("Update not found in xci file."); + } + default: + break; } SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory) @@ -713,21 +689,19 @@ namespace Ryujinx.HLE.FileSystem { if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00")) { - using (Stream ncaStream = GetZipStream(entry)) + using Stream ncaStream = GetZipStream(entry); + IStorage storage = ncaStream.AsStorage(); + + Nca nca = new(_virtualFileSystem.KeySet, storage); + + if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem)) { - IStorage storage = ncaStream.AsStorage(); - - Nca nca = new Nca(_virtualFileSystem.KeySet, storage); - - if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem)) - { - updateNcasItem.Add((nca.Header.ContentType, entry.FullName)); - } - else - { - updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>()); - updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName)); - } + updateNcasItem.Add((nca.Header.ContentType, entry.FullName)); + } + else + { + updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>()); + updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName)); } } } @@ -742,7 +716,7 @@ namespace Ryujinx.HLE.FileSystem using (Stream ncaStream = GetZipStream(fileEntry)) { - Nca metaNca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage()); + Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage()); IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); @@ -772,18 +746,16 @@ namespace Ryujinx.HLE.FileSystem { string versionEntry = updateNcasItem.Find(x => x.type != NcaContentType.Meta).path; - using (Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry))) + using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry)); + Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage()); + + var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + using var systemVersionFile = new UniqueRef<IFile>(); + + if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage()); - - var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - using var systemVersionFile = new UniqueRef<IFile>(); - - if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) - { - systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); - } + systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); } } @@ -804,43 +776,39 @@ namespace Ryujinx.HLE.FileSystem continue; } - ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath); + ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath); ZipArchiveEntry contentZipEntry = archive.GetEntry(contentPath); - using (Stream metaNcaStream = GetZipStream(metaZipEntry)) + using Stream metaNcaStream = GetZipStream(metaZipEntry); + using Stream contentNcaStream = GetZipStream(contentZipEntry); + Nca metaNca = new(_virtualFileSystem.KeySet, metaNcaStream.AsStorage()); + + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; + + using var metaFile = new UniqueRef<IFile>(); + + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) { - using (Stream contentNcaStream = GetZipStream(contentZipEntry)) + var meta = new Cnmt(metaFile.Get.AsStream()); + + IStorage contentStorage = contentNcaStream.AsStorage(); + if (contentStorage.GetSize(out long size).IsSuccess()) { - Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaNcaStream.AsStorage()); + byte[] contentData = new byte[size]; - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + Span<byte> content = new(contentData); - string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; + contentStorage.Read(0, content); - using var metaFile = new UniqueRef<IFile>(); + Span<byte> hash = new(new byte[32]); - if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash); + + if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash)) { - var meta = new Cnmt(metaFile.Get.AsStream()); - - IStorage contentStorage = contentNcaStream.AsStorage(); - if (contentStorage.GetSize(out long size).IsSuccess()) - { - byte[] contentData = new byte[size]; - - Span<byte> content = new Span<byte>(contentData); - - contentStorage.Read(0, content); - - Span<byte> hash = new Span<byte>(new byte[32]); - - LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash); - - if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash)) - { - updateNcas.Remove(metaEntry.TitleId); - } - } + updateNcas.Remove(metaEntry.TitleId); } } } @@ -853,9 +821,9 @@ namespace Ryujinx.HLE.FileSystem foreach (var entry in updateNcas) { - foreach (var nca in entry.Value) + foreach (var (type, path) in entry.Value) { - extraNcas += nca.path + Environment.NewLine; + extraNcas += path + Environment.NewLine; } } @@ -880,7 +848,7 @@ namespace Ryujinx.HLE.FileSystem { IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage(); - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStorage); + Nca nca = new(_virtualFileSystem.KeySet, ncaStorage); if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta) { @@ -936,8 +904,8 @@ namespace Ryujinx.HLE.FileSystem { if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry)) { - var metaNcaEntry = ncaEntry.Find(x => x.type == NcaContentType.Meta); - string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path; + string metaNcaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; + string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path; // Nintendo in 9.0.0, removed PPC and only kept the meta nca of it. // This is a perfect valid case, so we should just ignore the missing content nca and continue. @@ -948,10 +916,10 @@ namespace Ryujinx.HLE.FileSystem continue; } - IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaEntry.path, OpenMode.Read).AsStorage(); + IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaPath, OpenMode.Read).AsStorage(); IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, OpenMode.Read).AsStorage(); - Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaStorage); + Nca metaNca = new(_virtualFileSystem.KeySet, metaStorage); IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); @@ -967,11 +935,11 @@ namespace Ryujinx.HLE.FileSystem { byte[] contentData = new byte[size]; - Span<byte> content = new Span<byte>(contentData); + Span<byte> content = new(contentData); contentStorage.Read(0, content); - Span<byte> hash = new Span<byte>(new byte[32]); + Span<byte> hash = new(new byte[32]); LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash); @@ -1017,24 +985,21 @@ namespace Ryujinx.HLE.FileSystem { if (entry.ContentType == NcaContentType.Data) { - var path = _virtualFileSystem.SwitchPathToSystemPath(entry.ContentPath); + var path = VirtualFileSystem.SwitchPathToSystemPath(entry.ContentPath); - using (FileStream fileStream = File.OpenRead(path)) + using FileStream fileStream = File.OpenRead(path); + Nca nca = new(_virtualFileSystem.KeySet, fileStream.AsStorage()); + + if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) { - Nca nca = new Nca(_virtualFileSystem.KeySet, fileStream.AsStorage()); + var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) + using var systemVersionFile = new UniqueRef<IFile>(); + + if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { - var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - using var systemVersionFile = new UniqueRef<IFile>(); - - if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) - { - return new SystemVersion(systemVersionFile.Get.AsStream()); - } + return new SystemVersion(systemVersionFile.Get.AsStream()); } - } } } diff --git a/src/Ryujinx.HLE/FileSystem/ContentPath.cs b/src/Ryujinx.HLE/FileSystem/ContentPath.cs index c8663081a..6d2f3f6ab 100644 --- a/src/Ryujinx.HLE/FileSystem/ContentPath.cs +++ b/src/Ryujinx.HLE/FileSystem/ContentPath.cs @@ -2,7 +2,6 @@ using LibHac.Ncm; using Ryujinx.Common.Configuration; using System; - using static Ryujinx.HLE.FileSystem.VirtualFileSystem; using Path = System.IO.Path; @@ -10,33 +9,33 @@ namespace Ryujinx.HLE.FileSystem { internal static class ContentPath { - public const string SystemContent = "@SystemContent"; - public const string UserContent = "@UserContent"; - public const string SdCardContent = "@SdCardContent"; - public const string SdCard = "@Sdcard"; - public const string CalibFile = "@CalibFile"; - public const string Safe = "@Safe"; - public const string User = "@User"; - public const string System = "@System"; - public const string Host = "@Host"; - public const string GamecardApp = "@GcApp"; + public const string SystemContent = "@SystemContent"; + public const string UserContent = "@UserContent"; + public const string SdCardContent = "@SdCardContent"; + public const string SdCard = "@Sdcard"; + public const string CalibFile = "@CalibFile"; + public const string Safe = "@Safe"; + public const string User = "@User"; + public const string System = "@System"; + public const string Host = "@Host"; + public const string GamecardApp = "@GcApp"; public const string GamecardContents = "@GcS00000001"; - public const string GamecardUpdate = "@upp"; + public const string GamecardUpdate = "@upp"; public const string RegisteredUpdate = "@RegUpdate"; public const string Nintendo = "Nintendo"; public const string Contents = "Contents"; - public static string GetRealPath(VirtualFileSystem fileSystem, string switchContentPath) + public static string GetRealPath(string switchContentPath) { return switchContentPath switch { SystemContent => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath, Contents), - UserContent => Path.Combine(AppDataManager.BaseDirPath, UserNandPath, Contents), - SdCardContent => Path.Combine(fileSystem.GetSdCardPath(), Nintendo, Contents), - System => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath), - User => Path.Combine(AppDataManager.BaseDirPath, UserNandPath), - _ => throw new NotSupportedException($"Content Path \"`{switchContentPath}`\" is not supported.") + UserContent => Path.Combine(AppDataManager.BaseDirPath, UserNandPath, Contents), + SdCardContent => Path.Combine(GetSdCardPath(), Nintendo, Contents), + System => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath), + User => Path.Combine(AppDataManager.BaseDirPath, UserNandPath), + _ => throw new NotSupportedException($"Content Path \"`{switchContentPath}`\" is not supported."), }; } @@ -45,9 +44,9 @@ namespace Ryujinx.HLE.FileSystem return contentStorageId switch { ContentStorageId.System => SystemContent, - ContentStorageId.User => UserContent, + ContentStorageId.User => UserContent, ContentStorageId.SdCard => SdCardContent, - _ => throw new NotSupportedException($"Content Storage Id \"`{contentStorageId}`\" is not supported.") + _ => throw new NotSupportedException($"Content Storage Id \"`{contentStorageId}`\" is not supported."), }; } @@ -56,9 +55,9 @@ namespace Ryujinx.HLE.FileSystem return storageId switch { StorageId.BuiltInSystem => SystemContent, - StorageId.BuiltInUser => UserContent, - StorageId.SdCard => SdCardContent, - _ => throw new NotSupportedException($"Storage Id \"`{storageId}`\" is not supported.") + StorageId.BuiltInUser => UserContent, + StorageId.SdCard => SdCardContent, + _ => throw new NotSupportedException($"Storage Id \"`{storageId}`\" is not supported."), }; } @@ -67,16 +66,16 @@ namespace Ryujinx.HLE.FileSystem return contentPathString.Split(':')[0] switch { SystemContent or - System => StorageId.BuiltInSystem, + System => StorageId.BuiltInSystem, UserContent or - User => StorageId.BuiltInUser, - SdCardContent => StorageId.SdCard, - Host => StorageId.Host, + User => StorageId.BuiltInUser, + SdCardContent => StorageId.SdCard, + Host => StorageId.Host, GamecardApp or GamecardContents or GamecardUpdate => StorageId.GameCard, - _ => StorageId.None + _ => StorageId.None, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs b/src/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs index f32dc2d77..d50b80ffb 100644 --- a/src/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs +++ b/src/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs @@ -23,4 +23,4 @@ namespace Ryujinx.HLE.FileSystem return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/FileSystem/LocationEntry.cs b/src/Ryujinx.HLE/FileSystem/LocationEntry.cs index a60c28967..dd4068aab 100644 --- a/src/Ryujinx.HLE/FileSystem/LocationEntry.cs +++ b/src/Ryujinx.HLE/FileSystem/LocationEntry.cs @@ -4,16 +4,16 @@ namespace Ryujinx.HLE.FileSystem { public struct LocationEntry { - public string ContentPath { get; private set; } - public int Flag { get; private set; } - public ulong TitleId { get; private set; } + public string ContentPath { get; private set; } + public int Flag { get; private set; } + public ulong TitleId { get; private set; } public NcaContentType ContentType { get; private set; } public LocationEntry(string contentPath, int flag, ulong titleId, NcaContentType contentType) { ContentPath = contentPath; - Flag = flag; - TitleId = titleId; + Flag = flag; + TitleId = titleId; ContentType = contentType; } diff --git a/src/Ryujinx.HLE/FileSystem/SystemVersion.cs b/src/Ryujinx.HLE/FileSystem/SystemVersion.cs index a7926d5d3..bd64d7f2e 100644 --- a/src/Ryujinx.HLE/FileSystem/SystemVersion.cs +++ b/src/Ryujinx.HLE/FileSystem/SystemVersion.cs @@ -5,36 +5,34 @@ namespace Ryujinx.HLE.FileSystem { public class SystemVersion { - public byte Major { get; } - public byte Minor { get; } - public byte Micro { get; } - public byte RevisionMajor { get; } - public byte RevisionMinor { get; } + public byte Major { get; } + public byte Minor { get; } + public byte Micro { get; } + public byte RevisionMajor { get; } + public byte RevisionMinor { get; } public string PlatformString { get; } - public string Hex { get; } - public string VersionString { get; } - public string VersionTitle { get; } + public string Hex { get; } + public string VersionString { get; } + public string VersionTitle { get; } public SystemVersion(Stream systemVersionFile) { - using (BinaryReader reader = new BinaryReader(systemVersionFile)) - { - Major = reader.ReadByte(); - Minor = reader.ReadByte(); - Micro = reader.ReadByte(); + using BinaryReader reader = new(systemVersionFile); + Major = reader.ReadByte(); + Minor = reader.ReadByte(); + Micro = reader.ReadByte(); - reader.ReadByte(); // Padding + reader.ReadByte(); // Padding - RevisionMajor = reader.ReadByte(); - RevisionMinor = reader.ReadByte(); + RevisionMajor = reader.ReadByte(); + RevisionMinor = reader.ReadByte(); - reader.ReadBytes(2); // Padding + reader.ReadBytes(2); // Padding - PlatformString = StringUtils.ReadInlinedAsciiString(reader, 0x20); - Hex = StringUtils.ReadInlinedAsciiString(reader, 0x40); - VersionString = StringUtils.ReadInlinedAsciiString(reader, 0x18); - VersionTitle = StringUtils.ReadInlinedAsciiString(reader, 0x80); - } + PlatformString = StringUtils.ReadInlinedAsciiString(reader, 0x20); + Hex = StringUtils.ReadInlinedAsciiString(reader, 0x40); + VersionString = StringUtils.ReadInlinedAsciiString(reader, 0x18); + VersionTitle = StringUtils.ReadInlinedAsciiString(reader, 0x80); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 83604b420..807020c60 100644 --- a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -26,14 +26,14 @@ namespace Ryujinx.HLE.FileSystem { public class VirtualFileSystem : IDisposable { - public static string SafeNandPath = Path.Combine(AppDataManager.DefaultNandDir, "safe"); - public static string SystemNandPath = Path.Combine(AppDataManager.DefaultNandDir, "system"); - public static string UserNandPath = Path.Combine(AppDataManager.DefaultNandDir, "user"); + public static readonly string SafeNandPath = Path.Combine(AppDataManager.DefaultNandDir, "safe"); + public static readonly string SystemNandPath = Path.Combine(AppDataManager.DefaultNandDir, "system"); + public static readonly string UserNandPath = Path.Combine(AppDataManager.DefaultNandDir, "user"); - public KeySet KeySet { get; private set; } - public EmulatedGameCard GameCard { get; private set; } - public EmulatedSdCard SdCard { get; private set; } - public ModLoader ModLoader { get; private set; } + public KeySet KeySet { get; private set; } + public EmulatedGameCard GameCard { get; private set; } + public EmulatedSdCard SdCard { get; private set; } + public ModLoader ModLoader { get; private set; } private readonly ConcurrentDictionary<ulong, Stream> _romFsByPid; @@ -85,15 +85,15 @@ namespace Ryujinx.HLE.FileSystem return _romFsByPid[pid]; } - public string GetFullPath(string basePath, string fileName) + public static string GetFullPath(string basePath, string fileName) { if (fileName.StartsWith("//")) { - fileName = fileName.Substring(2); + fileName = fileName[2..]; } else if (fileName.StartsWith('/')) { - fileName = fileName.Substring(1); + fileName = fileName[1..]; } else { @@ -110,10 +110,10 @@ namespace Ryujinx.HLE.FileSystem return fullPath; } - internal string GetSdCardPath() => MakeFullPath(AppDataManager.DefaultSdcardDir); - public string GetNandPath() => MakeFullPath(AppDataManager.DefaultNandDir); + internal static string GetSdCardPath() => MakeFullPath(AppDataManager.DefaultSdcardDir); + public static string GetNandPath() => MakeFullPath(AppDataManager.DefaultNandDir); - public string SwitchPathToSystemPath(string switchPath) + public static string SwitchPathToSystemPath(string switchPath) { string[] parts = switchPath.Split(":"); @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.FileSystem return GetFullPath(MakeFullPath(parts[0]), parts[1]); } - public string SystemPathToSwitchPath(string systemPath) + public static string SystemPathToSwitchPath(string systemPath) { string baseSystemPath = AppDataManager.BaseDirPath + Path.DirectorySeparatorChar; @@ -148,7 +148,7 @@ namespace Ryujinx.HLE.FileSystem return null; } - private string MakeFullPath(string path, bool isDirectory = true) + private static string MakeFullPath(string path, bool isDirectory = true) { // Handles Common Switch Content Paths switch (path) @@ -185,7 +185,7 @@ namespace Ryujinx.HLE.FileSystem public void InitializeFsServer(LibHac.Horizon horizon, out HorizonClient fsServerClient) { - LocalFileSystem serverBaseFs = new LocalFileSystem(AppDataManager.BaseDirPath); + LocalFileSystem serverBaseFs = new(AppDataManager.BaseDirPath); fsServerClient = horizon.CreatePrivilegedHorizonClient(); var fsServer = new FileSystemServer(fsServerClient); @@ -207,7 +207,7 @@ namespace Ryujinx.HLE.FileSystem DeviceOperator = fsServerObjects.DeviceOperator, ExternalKeySet = KeySet.ExternalKeySet, FsCreators = fsServerObjects.FsCreators, - RandomGenerator = randomGenerator + RandomGenerator = randomGenerator, }; FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig); @@ -282,16 +282,28 @@ namespace Ryujinx.HLE.FileSystem public static Result FixExtraData(HorizonClient hos) { Result rc = GetSystemSaveList(hos, out List<ulong> systemSaveIds); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } rc = FixUnindexedSystemSaves(hos, systemSaveIds); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } rc = FixExtraDataInSpaceId(hos, SaveDataSpaceId.System); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } rc = FixExtraDataInSpaceId(hos, SaveDataSpaceId.User); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } return Result.Success; } @@ -303,15 +315,23 @@ namespace Ryujinx.HLE.FileSystem using var iterator = new UniqueRef<SaveDataIterator>(); Result rc = hos.Fs.OpenSaveDataIterator(ref iterator.Ref, spaceId); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } while (true) { rc = iterator.Get.ReadSaveDataInfo(out long count, info); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } if (count == 0) + { return Result.Success; + } for (int i = 0; i < count; i++) { @@ -351,7 +371,9 @@ namespace Ryujinx.HLE.FileSystem private static Result CreateSaveDataDirectory(HorizonClient hos, in SaveDataInfo info) { if (info.SpaceId != SaveDataSpaceId.User && info.SpaceId != SaveDataSpaceId.System) + { return Result.Success; + } const string MountName = "SaveDir"; var mountNameU8 = MountName.ToU8Span(); @@ -360,11 +382,15 @@ namespace Ryujinx.HLE.FileSystem { SaveDataSpaceId.System => BisPartitionId.System, SaveDataSpaceId.User => BisPartitionId.User, - _ => throw new ArgumentOutOfRangeException() + _ => throw new ArgumentOutOfRangeException(nameof(info), info.SpaceId, null), }; Result rc = hos.Fs.MountBis(mountNameU8, partitionId); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } + try { var path = $"{MountName}:/save/{info.SaveDataId:x16}".ToU8Span(); @@ -391,28 +417,38 @@ namespace Ryujinx.HLE.FileSystem var mountName = "system".ToU8Span(); DirectoryHandle handle = default; - List<ulong> localList = new List<ulong>(); + List<ulong> localList = new(); try { Result rc = hos.Fs.MountBis(mountName, BisPartitionId.System); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } rc = hos.Fs.OpenDirectory(out handle, "system:/save".ToU8Span(), OpenDirectoryMode.All); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } - DirectoryEntry entry = new DirectoryEntry(); + DirectoryEntry entry = new(); while (true) { rc = hos.Fs.ReadDirectory(out long readCount, SpanHelpers.AsSpan(ref entry), handle); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) + { + return rc; + } if (readCount == 0) + { break; + } - if (Utf8Parser.TryParse(entry.Name, out ulong saveDataId, out int bytesRead, 'x') && - bytesRead == 16 && (long)saveDataId < 0) + if (Utf8Parser.TryParse(entry.Name, out ulong saveDataId, out int bytesRead, 'x') && bytesRead == 16 && (long)saveDataId < 0) { localList.Add(saveDataId); } @@ -440,7 +476,7 @@ namespace Ryujinx.HLE.FileSystem // Only save data IDs added to SystemExtraDataFixInfo will be fixed. private static Result FixUnindexedSystemSaves(HorizonClient hos, List<ulong> existingSaveIds) { - foreach (var fixInfo in SystemExtraDataFixInfo) + foreach (var fixInfo in _systemExtraDataFixInfo) { if (!existingSaveIds.Contains(fixInfo.StaticSaveDataId)) { @@ -472,7 +508,9 @@ namespace Ryujinx.HLE.FileSystem if (!rc.IsSuccess()) { if (!ResultFs.TargetNotFound.Includes(rc)) + { return rc; + } // We'll reach this point only if the save data directory exists but it's not in the save data indexer. // Creating the save will add it to the indexer while leaving its existing contents intact. @@ -492,7 +530,7 @@ namespace Ryujinx.HLE.FileSystem OwnerId = info.OwnerId, Flags = info.Flags, DataSize = info.DataSize, - JournalSize = info.JournalSize + JournalSize = info.JournalSize, }; // Make a mask for writing the entire extra data @@ -507,9 +545,11 @@ namespace Ryujinx.HLE.FileSystem { wasFixNeeded = true; - Result rc = hos.Fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, info.SpaceId, - info.SaveDataId); - if (rc.IsFailure()) return rc; + Result rc = hos.Fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, info.SpaceId, info.SaveDataId); + if (rc.IsFailure()) + { + return rc; + } // The extra data should have program ID or static save data ID set if it's valid. // We only try to fix the extra data if the info from the save data indexer has a program ID or static save data ID. @@ -543,7 +583,7 @@ namespace Ryujinx.HLE.FileSystem else { // Try to match the system save with one of the known saves - foreach (ExtraDataFixInfo fixInfo in SystemExtraDataFixInfo) + foreach (ExtraDataFixInfo fixInfo in _systemExtraDataFixInfo) { if (extraData.Attribute.StaticSaveDataId == fixInfo.StaticSaveDataId) { @@ -573,7 +613,7 @@ namespace Ryujinx.HLE.FileSystem public long JournalSize; } - private static readonly ExtraDataFixInfo[] SystemExtraDataFixInfo = + private static readonly ExtraDataFixInfo[] _systemExtraDataFixInfo = { new ExtraDataFixInfo() { @@ -581,7 +621,7 @@ namespace Ryujinx.HLE.FileSystem OwnerId = 0x010000000000001F, Flags = SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData, DataSize = 0x10000, - JournalSize = 0x10000 + JournalSize = 0x10000, }, new ExtraDataFixInfo() { @@ -589,12 +629,13 @@ namespace Ryujinx.HLE.FileSystem OwnerId = 0x0100000000001009, Flags = SaveDataFlags.None, DataSize = 0xC000, - JournalSize = 0xC000 - } + JournalSize = 0xC000, + }, }; public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index df8dea6d6..22477a8c0 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -163,57 +163,57 @@ namespace Ryujinx.HLE /// </summary> public Action RefreshInputConfig { internal get; set; } - public HLEConfiguration(VirtualFileSystem virtualFileSystem, - LibHacHorizonManager libHacHorizonManager, - ContentManager contentManager, - AccountManager accountManager, + public HLEConfiguration(VirtualFileSystem virtualFileSystem, + LibHacHorizonManager libHacHorizonManager, + ContentManager contentManager, + AccountManager accountManager, UserChannelPersistence userChannelPersistence, - IRenderer gpuRenderer, - IHardwareDeviceDriver audioDeviceDriver, - MemoryConfiguration memoryConfiguration, - IHostUiHandler hostUiHandler, - SystemLanguage systemLanguage, - RegionCode region, - bool enableVsync, - bool enableDockedMode, - bool enablePtc, - bool enableInternetAccess, - IntegrityCheckLevel fsIntegrityCheckLevel, - int fsGlobalAccessLogMode, - long systemTimeOffset, - string timeZone, - MemoryManagerMode memoryManagerMode, - bool ignoreMissingServices, - AspectRatio aspectRatio, - float audioVolume, - bool useHypervisor, - string multiplayerLanInterfaceId) + IRenderer gpuRenderer, + IHardwareDeviceDriver audioDeviceDriver, + MemoryConfiguration memoryConfiguration, + IHostUiHandler hostUiHandler, + SystemLanguage systemLanguage, + RegionCode region, + bool enableVsync, + bool enableDockedMode, + bool enablePtc, + bool enableInternetAccess, + IntegrityCheckLevel fsIntegrityCheckLevel, + int fsGlobalAccessLogMode, + long systemTimeOffset, + string timeZone, + MemoryManagerMode memoryManagerMode, + bool ignoreMissingServices, + AspectRatio aspectRatio, + float audioVolume, + bool useHypervisor, + string multiplayerLanInterfaceId) { - VirtualFileSystem = virtualFileSystem; - LibHacHorizonManager = libHacHorizonManager; - AccountManager = accountManager; - ContentManager = contentManager; - UserChannelPersistence = userChannelPersistence; - GpuRenderer = gpuRenderer; - AudioDeviceDriver = audioDeviceDriver; - MemoryConfiguration = memoryConfiguration; - HostUiHandler = hostUiHandler; - SystemLanguage = systemLanguage; - Region = region; - EnableVsync = enableVsync; - EnableDockedMode = enableDockedMode; - EnablePtc = enablePtc; - EnableInternetAccess = enableInternetAccess; - FsIntegrityCheckLevel = fsIntegrityCheckLevel; - FsGlobalAccessLogMode = fsGlobalAccessLogMode; - SystemTimeOffset = systemTimeOffset; - TimeZone = timeZone; - MemoryManagerMode = memoryManagerMode; - IgnoreMissingServices = ignoreMissingServices; - AspectRatio = aspectRatio; - AudioVolume = audioVolume; - UseHypervisor = useHypervisor; + VirtualFileSystem = virtualFileSystem; + LibHacHorizonManager = libHacHorizonManager; + AccountManager = accountManager; + ContentManager = contentManager; + UserChannelPersistence = userChannelPersistence; + GpuRenderer = gpuRenderer; + AudioDeviceDriver = audioDeviceDriver; + MemoryConfiguration = memoryConfiguration; + HostUiHandler = hostUiHandler; + SystemLanguage = systemLanguage; + Region = region; + EnableVsync = enableVsync; + EnableDockedMode = enableDockedMode; + EnablePtc = enablePtc; + EnableInternetAccess = enableInternetAccess; + FsIntegrityCheckLevel = fsIntegrityCheckLevel; + FsGlobalAccessLogMode = fsGlobalAccessLogMode; + SystemTimeOffset = systemTimeOffset; + TimeZone = timeZone; + MemoryManagerMode = memoryManagerMode; + IgnoreMissingServices = ignoreMissingServices; + AspectRatio = aspectRatio; + AudioVolume = audioVolume; + UseHypervisor = useHypervisor; MultiplayerLanInterfaceId = multiplayerLanInterfaceId; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs index a686a8328..93d49cd24 100644 --- a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs +++ b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Applets { static class AppletManager { - private static Dictionary<AppletId, Type> _appletMapping; + private static readonly Dictionary<AppletId, Type> _appletMapping; static AppletManager() { @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Applets { AppletId.SoftwareKeyboard, typeof(SoftwareKeyboardApplet) }, { AppletId.LibAppletWeb, typeof(BrowserApplet) }, { AppletId.LibAppletShop, typeof(BrowserApplet) }, - { AppletId.LibAppletOff, typeof(BrowserApplet) } + { AppletId.LibAppletOff, typeof(BrowserApplet) }, }; } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs index fe6e60400..11c1cb912 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs @@ -6,6 +6,6 @@ Offline, Black, Share, - Lobby + Lobby, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs index 952afcd5f..f50e6448f 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs @@ -13,13 +13,12 @@ namespace Ryujinx.HLE.HOS.Applets.Browser public event EventHandler AppletStateChanged; private AppletSession _normalSession; - private AppletSession _interactiveSession; private CommonArguments _commonArguments; private List<BrowserArgument> _arguments; private ShimKind _shimKind; - public BrowserApplet(Horizon system) {} + public BrowserApplet(Horizon system) { } public ResultCode GetResult() { @@ -29,7 +28,6 @@ namespace Ryujinx.HLE.HOS.Applets.Browser public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) { _normalSession = normalSession; - _interactiveSession = interactiveSession; _commonArguments = IApplet.ReadStruct<CommonArguments>(_normalSession.Pop()); @@ -48,17 +46,18 @@ namespace Ryujinx.HLE.HOS.Applets.Browser if ((_commonArguments.AppletVersion >= 0x80000 && _shimKind == ShimKind.Web) || (_commonArguments.AppletVersion >= 0x30000 && _shimKind == ShimKind.Share)) { - List<BrowserOutput> result = new List<BrowserOutput>(); - - result.Add(new BrowserOutput(BrowserOutputType.ExitReason, (uint)WebExitReason.ExitButton)); + List<BrowserOutput> result = new() + { + new BrowserOutput(BrowserOutputType.ExitReason, (uint)WebExitReason.ExitButton), + }; _normalSession.Push(BuildResponseNew(result)); } else { - WebCommonReturnValue result = new WebCommonReturnValue() + WebCommonReturnValue result = new() { - ExitReason = WebExitReason.ExitButton, + ExitReason = WebExitReason.ExitButton, }; _normalSession.Push(BuildResponseOld(result)); @@ -69,36 +68,32 @@ namespace Ryujinx.HLE.HOS.Applets.Browser return ResultCode.Success; } - private byte[] BuildResponseOld(WebCommonReturnValue result) + private static byte[] BuildResponseOld(WebCommonReturnValue result) { - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.WriteStruct(result); + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); + writer.WriteStruct(result); - return stream.ToArray(); - } + return stream.ToArray(); } private byte[] BuildResponseNew(List<BrowserOutput> outputArguments) { - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); + writer.WriteStruct(new WebArgHeader { - writer.WriteStruct(new WebArgHeader - { - Count = (ushort)outputArguments.Count, - ShimKind = _shimKind - }); + Count = (ushort)outputArguments.Count, + ShimKind = _shimKind, + }); - foreach (BrowserOutput output in outputArguments) - { - output.Write(writer); - } - - writer.Write(new byte[0x2000 - writer.BaseStream.Position]); - - return stream.ToArray(); + foreach (BrowserOutput output in outputArguments) + { + output.Write(writer); } + + writer.Write(new byte[0x2000 - writer.BaseStream.Position]); + + return stream.ToArray(); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs index 17fd40898..708781bf3 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs @@ -8,16 +8,16 @@ namespace Ryujinx.HLE.HOS.Applets.Browser { class BrowserArgument { - public WebArgTLVType Type { get; } - public byte[] Value { get; } + public WebArgTLVType Type { get; } + public byte[] Value { get; } public BrowserArgument(WebArgTLVType type, byte[] value) { - Type = type; + Type = type; Value = value; } - private static readonly Dictionary<WebArgTLVType, Type> _typeRegistry = new Dictionary<WebArgTLVType, Type> + private static readonly Dictionary<WebArgTLVType, Type> _typeRegistry = new() { { WebArgTLVType.InitialURL, typeof(string) }, { WebArgTLVType.CallbackUrl, typeof(string) }, @@ -64,11 +64,11 @@ namespace Ryujinx.HLE.HOS.Applets.Browser public static (ShimKind, List<BrowserArgument>) ParseArguments(ReadOnlySpan<byte> data) { - List<BrowserArgument> browserArguments = new List<BrowserArgument>(); + List<BrowserArgument> browserArguments = new(); - WebArgHeader header = IApplet.ReadStruct<WebArgHeader>(data.Slice(0, 8)); + WebArgHeader header = IApplet.ReadStruct<WebArgHeader>(data[..8]); - ReadOnlySpan<byte> rawTLVs = data.Slice(8); + ReadOnlySpan<byte> rawTLVs = data[8..]; for (int i = 0; i < header.Count; i++) { @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser browserArguments.Add(new BrowserArgument((WebArgTLVType)tlv.Type, tlvData.ToArray())); - rawTLVs = rawTLVs.Slice(Unsafe.SizeOf<WebArgTLV>() + tlv.Size); + rawTLVs = rawTLVs[(Unsafe.SizeOf<WebArgTLV>() + tlv.Size)..]; } return (header.ShimKind, browserArguments); diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs index 0b3682627..443886fde 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs @@ -11,25 +11,25 @@ namespace Ryujinx.HLE.HOS.Applets.Browser public BrowserOutput(BrowserOutputType type, byte[] value) { - Type = type; + Type = type; Value = value; } public BrowserOutput(BrowserOutputType type, uint value) { - Type = type; - Value = BitConverter.GetBytes(value); + Type = type; + Value = BitConverter.GetBytes(value); } public BrowserOutput(BrowserOutputType type, ulong value) { - Type = type; + Type = type; Value = BitConverter.GetBytes(value); } public BrowserOutput(BrowserOutputType type, bool value) { - Type = type; + Type = type; Value = BitConverter.GetBytes(value); } @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser writer.WriteStruct(new WebArgTLV { Type = (ushort)Type, - Size = (ushort)Value.Length + Size = (ushort)Value.Length, }); writer.Write(Value); diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs index 209ae8ae1..7f85bc81c 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs @@ -2,13 +2,13 @@ { enum BrowserOutputType : ushort { - ExitReason = 0x1, - LastUrl = 0x2, - LastUrlSize = 0x3, - SharePostResult = 0x4, - PostServiceName = 0x5, - PostServiceNameSize = 0x6, - PostId = 0x7, - MediaPlayerAutoClosedByCompletion = 0x8 + ExitReason = 0x1, + LastUrl = 0x2, + LastUrlSize = 0x3, + SharePostResult = 0x4, + PostServiceName = 0x5, + PostServiceNameSize = 0x6, + PostId = 0x7, + MediaPlayerAutoClosedByCompletion = 0x8, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs index 385bcdd00..9d355cd75 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs @@ -4,6 +4,6 @@ { OfflineHtmlPage = 1, ApplicationLegalInformation, - SystemDataPage + SystemDataPage, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs index 917549d2c..50ad7b8bb 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs @@ -3,6 +3,6 @@ enum LeftStickMode { Pointer = 0, - Cursor + Cursor, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs index ca2ef32fe..f097d1f4a 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs @@ -8,6 +8,6 @@ Share, Web, Wifi, - Lobby + Lobby, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs index c5e19f6ca..c3cc711e4 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs @@ -2,8 +2,8 @@ { public struct WebArgHeader { - public ushort Count; - public ushort Padding; + public ushort Count; + public ushort Padding; public ShimKind ShimKind; } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs index f6c1e5ae0..1b5510a5d 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs @@ -4,6 +4,6 @@ { public ushort Type; public ushort Size; - public uint Padding; + public uint Padding; } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs index bd3032072..575138330 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs @@ -2,61 +2,61 @@ { enum WebArgTLVType : ushort { - InitialURL = 0x1, - CallbackUrl = 0x3, - CallbackableUrl = 0x4, - ApplicationId = 0x5, - DocumentPath = 0x6, - DocumentKind = 0x7, - SystemDataId = 0x8, - ShareStartPage = 0x9, - Whitelist = 0xA, - NewsFlag = 0xB, - UserID = 0xE, - AlbumEntry0 = 0xF, - ScreenShotEnabled = 0x10, - EcClientCertEnabled = 0x11, - PlayReportEnabled = 0x13, - UnknownFlag0x14 = 0x14, - UnknownFlag0x15 = 0x15, - BootDisplayKind = 0x17, - BackgroundKind = 0x18, - FooterEnabled = 0x19, - PointerEnabled = 0x1A, - LeftStickMode = 0x1B, - KeyRepeatFrame1 = 0x1C, - KeyRepeatFrame2 = 0x1D, - BootAsMediaPlayerInverted = 0x1E, - DisplayUrlKind = 0x1F, - BootAsMediaPlayer = 0x21, - ShopJumpEnabled = 0x22, - MediaAutoPlayEnabled = 0x23, - LobbyParameter = 0x24, - ApplicationAlbumEntry = 0x26, - JsExtensionEnabled = 0x27, - AdditionalCommentText = 0x28, - TouchEnabledOnContents = 0x29, - UserAgentAdditionalString = 0x2A, - AdditionalMediaData0 = 0x2B, - MediaPlayerAutoCloseEnabled = 0x2C, - PageCacheEnabled = 0x2D, - WebAudioEnabled = 0x2E, - FooterFixedKind = 0x32, - PageFadeEnabled = 0x33, + InitialURL = 0x1, + CallbackUrl = 0x3, + CallbackableUrl = 0x4, + ApplicationId = 0x5, + DocumentPath = 0x6, + DocumentKind = 0x7, + SystemDataId = 0x8, + ShareStartPage = 0x9, + Whitelist = 0xA, + NewsFlag = 0xB, + UserID = 0xE, + AlbumEntry0 = 0xF, + ScreenShotEnabled = 0x10, + EcClientCertEnabled = 0x11, + PlayReportEnabled = 0x13, + UnknownFlag0x14 = 0x14, + UnknownFlag0x15 = 0x15, + BootDisplayKind = 0x17, + BackgroundKind = 0x18, + FooterEnabled = 0x19, + PointerEnabled = 0x1A, + LeftStickMode = 0x1B, + KeyRepeatFrame1 = 0x1C, + KeyRepeatFrame2 = 0x1D, + BootAsMediaPlayerInverted = 0x1E, + DisplayUrlKind = 0x1F, + BootAsMediaPlayer = 0x21, + ShopJumpEnabled = 0x22, + MediaAutoPlayEnabled = 0x23, + LobbyParameter = 0x24, + ApplicationAlbumEntry = 0x26, + JsExtensionEnabled = 0x27, + AdditionalCommentText = 0x28, + TouchEnabledOnContents = 0x29, + UserAgentAdditionalString = 0x2A, + AdditionalMediaData0 = 0x2B, + MediaPlayerAutoCloseEnabled = 0x2C, + PageCacheEnabled = 0x2D, + WebAudioEnabled = 0x2E, + FooterFixedKind = 0x32, + PageFadeEnabled = 0x33, MediaCreatorApplicationRatingAge = 0x34, - BootLoadingIconEnabled = 0x35, - PageScrollIndicatorEnabled = 0x36, - MediaPlayerSpeedControlEnabled = 0x37, - AlbumEntry1 = 0x38, - AlbumEntry2 = 0x39, - AlbumEntry3 = 0x3A, - AdditionalMediaData1 = 0x3B, - AdditionalMediaData2 = 0x3C, - AdditionalMediaData3 = 0x3D, - BootFooterButton = 0x3E, - OverrideWebAudioVolume = 0x3F, - OverrideMediaAudioVolume = 0x40, - BootMode = 0x41, - MediaPlayerUiEnabled = 0x43 + BootLoadingIconEnabled = 0x35, + PageScrollIndicatorEnabled = 0x36, + MediaPlayerSpeedControlEnabled = 0x37, + AlbumEntry1 = 0x38, + AlbumEntry2 = 0x39, + AlbumEntry3 = 0x3A, + AdditionalMediaData1 = 0x3B, + AdditionalMediaData2 = 0x3C, + AdditionalMediaData3 = 0x3D, + BootFooterButton = 0x3E, + OverrideWebAudioVolume = 0x3F, + OverrideMediaAudioVolume = 0x40, + BootMode = 0x41, + MediaPlayerUiEnabled = 0x43, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs index 9f7eae700..a1bdf0c76 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Applets.Browser public struct WebCommonReturnValue { public WebExitReason ExitReason; - public uint Padding; + public uint Padding; public ByteArray4096 LastUrl; - public ulong LastUrlSize; + public ulong LastUrlSize; } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs index 4e44d34ab..1959fc0b4 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs @@ -6,6 +6,6 @@ BackButton, Requested, LastUrl, - ErrorDialog = 7 + ErrorDialog = 7, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/CommonArguments.cs b/src/Ryujinx.HLE/HOS/Applets/CommonArguments.cs index 5da34db1f..857798987 100644 --- a/src/Ryujinx.HLE/HOS/Applets/CommonArguments.cs +++ b/src/Ryujinx.HLE/HOS/Applets/CommonArguments.cs @@ -5,12 +5,12 @@ namespace Ryujinx.HLE.HOS.Applets [StructLayout(LayoutKind.Sequential, Pack = 8)] struct CommonArguments { - public uint Version; - public uint StructureSize; - public uint AppletVersion; - public uint ThemeColor; + public uint Version; + public uint StructureSize; + public uint AppletVersion; + public uint ThemeColor; [MarshalAs(UnmanagedType.I1)] - public bool PlayStartupSound; + public bool PlayStartupSound; public ulong SystemTicks; } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs index 5d5a26c23..867202178 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Applets { internal class ControllerApplet : IApplet { - private Horizon _system; + private readonly Horizon _system; private AppletSession _normalSession; @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Applets } else { - Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version Unknown"); + Logger.Stub?.PrintStub(LogClass.ServiceHid, "ControllerSupportArg Version Unknown"); argHeader = IApplet.ReadStruct<ControllerSupportArgHeader>(controllerSupportArg); // Read just the header } @@ -82,17 +82,17 @@ namespace Ryujinx.HLE.HOS.Applets playerMin = playerMax = 1; } - int configuredCount = 0; - PlayerIndex primaryIndex = PlayerIndex.Unknown; + int configuredCount; + PlayerIndex primaryIndex; while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex)) { - ControllerAppletUiArgs uiArgs = new ControllerAppletUiArgs + ControllerAppletUiArgs uiArgs = new() { PlayerCountMin = playerMin, PlayerCountMax = playerMax, SupportedStyles = (ControllerType)privateArg.NpadStyleSet, SupportedPlayers = _system.Device.Hid.Npads.GetSupportedPlayers(), - IsDocked = _system.State.DockedMode + IsDocked = _system.State.DockedMode, }; if (!_system.Device.UiHandler.DisplayMessageDialog(uiArgs)) @@ -101,10 +101,10 @@ namespace Ryujinx.HLE.HOS.Applets } } - ControllerSupportResultInfo result = new ControllerSupportResultInfo + ControllerSupportResultInfo result = new() { PlayerCount = (sbyte)configuredCount, - SelectedId = (uint)GetNpadIdTypeFromIndex(primaryIndex) + SelectedId = (uint)GetNpadIdTypeFromIndex(primaryIndex), }; Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ReturnResult {result.PlayerCount} {result.SelectedId}"); @@ -122,26 +122,24 @@ namespace Ryujinx.HLE.HOS.Applets return ResultCode.Success; } - private byte[] BuildResponse(ControllerSupportResultInfo result) + private static byte[] BuildResponse(ControllerSupportResultInfo result) { - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>()))); + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>()))); + + return stream.ToArray(); } - private byte[] BuildResponse() + private static byte[] BuildResponse() { - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.Write((ulong)ResultCode.Success); + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + writer.Write((ulong)ResultCode.Success); + + return stream.ToArray(); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs index cc15a406a..bf440515b 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs @@ -11,4 +11,4 @@ namespace Ryujinx.HLE.HOS.Applets public IEnumerable<PlayerIndex> SupportedPlayers; public bool IsDocked; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs index 141994a8a..b066da8e8 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Applets { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ControllerSupportArgHeader { @@ -15,4 +15,4 @@ namespace Ryujinx.HLE.HOS.Applets public byte EnableIdentificationColor; } #pragma warning restore CS0649 -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs index d4c8177eb..d5a18d468 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgPrivate.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Applets { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to struct ControllerSupportArgPrivate { public uint PrivateSize; @@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Applets public uint NpadJoyHoldType; } #pragma warning restore CS0649 -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs index 98c413beb..0969493fa 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Applets { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to // (8.0.0+ version) [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ControllerSupportArgV7 @@ -23,4 +23,4 @@ namespace Ryujinx.HLE.HOS.Applets } } #pragma warning restore CS0649 -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs index 87417e16f..886782553 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Applets { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to // (1.0.0+ version) [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ControllerSupportArgVPre7 @@ -23,4 +23,4 @@ namespace Ryujinx.HLE.HOS.Applets } } #pragma warning restore CS0649 -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs index 9496c1dd6..5e181e197 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportMode.cs @@ -4,6 +4,6 @@ namespace Ryujinx.HLE.HOS.Applets { ShowControllerSupport = 0, ShowControllerStrapGuide = 1, - ShowControllerFirmwareUpdate = 2 + ShowControllerFirmwareUpdate = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs index 689a54dec..96cfd5903 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Applets { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ControllerSupportResultInfo { @@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Applets public uint Result; } #pragma warning restore CS0649 -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs index f40d54112..4263c84b8 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Error/ApplicationErrorArg.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Applets.Error [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ApplicationErrorArg { - public uint ErrorNumber; - public ulong LanguageCode; + public uint ErrorNumber; + public ulong LanguageCode; public ByteArray2048 MessageText; public ByteArray2048 DetailsText; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs index c5c6e8e95..fa415b396 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs @@ -22,11 +22,11 @@ namespace Ryujinx.HLE.HOS.Applets.Error { private const long ErrorMessageBinaryTitleId = 0x0100000000000801; - private Horizon _horizon; - private AppletSession _normalSession; - private CommonArguments _commonArguments; + private readonly Horizon _horizon; + private AppletSession _normalSession; + private CommonArguments _commonArguments; private ErrorCommonHeader _errorCommonHeader; - private byte[] _errorStorage; + private byte[] _errorStorage; public event EventHandler AppletStateChanged; @@ -40,14 +40,14 @@ namespace Ryujinx.HLE.HOS.Applets.Error public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) { - _normalSession = normalSession; + _normalSession = normalSession; _commonArguments = IApplet.ReadStruct<CommonArguments>(_normalSession.Pop()); Logger.Info?.PrintMsg(LogClass.ServiceAm, $"ErrorApplet version: 0x{_commonArguments.AppletVersion:x8}"); - _errorStorage = _normalSession.Pop(); + _errorStorage = _normalSession.Pop(); _errorCommonHeader = IApplet.ReadStruct<ErrorCommonHeader>(_errorStorage); - _errorStorage = _errorStorage.Skip(Marshal.SizeOf<ErrorCommonHeader>()).ToArray(); + _errorStorage = _errorStorage.Skip(Marshal.SizeOf<ErrorCommonHeader>()).ToArray(); switch (_errorCommonHeader.Type) { @@ -63,7 +63,8 @@ namespace Ryujinx.HLE.HOS.Applets.Error break; } - default: throw new NotImplementedException($"ErrorApplet type {_errorCommonHeader.Type} is not implemented."); + default: + throw new NotImplementedException($"ErrorApplet type {_errorCommonHeader.Type} is not implemented."); } AppletStateChanged?.Invoke(this, null); @@ -71,15 +72,16 @@ namespace Ryujinx.HLE.HOS.Applets.Error return ResultCode.Success; } - private (uint module, uint description) HexToResultCode(uint resultCode) + private static (uint module, uint description) HexToResultCode(uint resultCode) { return ((resultCode & 0x1FF) + 2000, (resultCode >> 9) & 0x3FFF); } - private string SystemLanguageToLanguageKey(SystemLanguage systemLanguage) + private static string SystemLanguageToLanguageKey(SystemLanguage systemLanguage) { return systemLanguage switch { +#pragma warning disable IDE0055 // Disable formatting SystemLanguage.Japanese => "ja", SystemLanguage.AmericanEnglish => "en-US", SystemLanguage.French => "fr", @@ -98,7 +100,8 @@ namespace Ryujinx.HLE.HOS.Applets.Error SystemLanguage.SimplifiedChinese => "zh-Hans", SystemLanguage.TraditionalChinese => "zh-Hant", SystemLanguage.BrazilianPortuguese => "pt-BR", - _ => "en-US" + _ => "en-US", +#pragma warning restore IDE0055 }; } @@ -111,26 +114,24 @@ namespace Ryujinx.HLE.HOS.Applets.Error { string binaryTitleContentPath = _horizon.ContentManager.GetInstalledContentPath(ErrorMessageBinaryTitleId, StorageId.BuiltInSystem, NcaContentType.Data); - using (LibHac.Fs.IStorage ncaFileStream = new LocalStorage(_horizon.Device.FileSystem.SwitchPathToSystemPath(binaryTitleContentPath), FileAccess.Read, FileMode.Open)) + using LibHac.Fs.IStorage ncaFileStream = new LocalStorage(FileSystem.VirtualFileSystem.SwitchPathToSystemPath(binaryTitleContentPath), FileAccess.Read, FileMode.Open); + Nca nca = new(_horizon.Device.FileSystem.KeySet, ncaFileStream); + IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _horizon.FsIntegrityCheckLevel); + string languageCode = SystemLanguageToLanguageKey(_horizon.State.DesiredSystemLanguage); + string filePath = $"/{module}/{description:0000}/{languageCode}_{key}"; + + if (romfs.FileExists(filePath)) { - Nca nca = new Nca(_horizon.Device.FileSystem.KeySet, ncaFileStream); - IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _horizon.FsIntegrityCheckLevel); - string languageCode = SystemLanguageToLanguageKey(_horizon.State.DesiredSystemLanguage); - string filePath = $"/{module}/{description:0000}/{languageCode}_{key}"; + using var binaryFile = new UniqueRef<IFile>(); - if (romfs.FileExists(filePath)) - { - using var binaryFile = new UniqueRef<IFile>(); + romfs.OpenFile(ref binaryFile.Ref, filePath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + StreamReader reader = new(binaryFile.Get.AsStream(), Encoding.Unicode); - romfs.OpenFile(ref binaryFile.Ref, filePath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - StreamReader reader = new StreamReader(binaryFile.Get.AsStream(), Encoding.Unicode); - - return CleanText(reader.ReadToEnd()); - } - else - { - return ""; - } + return CleanText(reader.ReadToEnd()); + } + else + { + return ""; } } @@ -145,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error { ErrorCommonArg errorCommonArg = IApplet.ReadStruct<ErrorCommonArg>(_errorStorage); - uint module = errorCommonArg.Module; + uint module = errorCommonArg.Module; uint description = errorCommonArg.Description; if (_errorCommonHeader.MessageFlag == 0) @@ -188,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error string messageText = Encoding.ASCII.GetString(messageTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray()); string detailsText = Encoding.ASCII.GetString(detailsTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray()); - List<string> buttons = new List<string>(); + List<string> buttons = new(); // TODO: Handle the LanguageCode to return the translated "OK" and "Details". @@ -213,4 +214,4 @@ namespace Ryujinx.HLE.HOS.Applets.Error return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs index 530a2ad8b..a042739cb 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonArg.cs @@ -9,4 +9,4 @@ namespace Ryujinx.HLE.HOS.Applets.Error public uint Description; public uint ResultCode; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs index b93cdd4f1..ca8e9220f 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorCommonHeader.cs @@ -6,12 +6,12 @@ namespace Ryujinx.HLE.HOS.Applets.Error struct ErrorCommonHeader { public ErrorType Type; - public byte JumpFlag; - public byte ReservedFlag1; - public byte ReservedFlag2; - public byte ReservedFlag3; - public byte ContextFlag; - public byte MessageFlag; - public byte ContextFlag2; + public byte JumpFlag; + public byte ReservedFlag1; + public byte ReservedFlag2; + public byte ReservedFlag3; + public byte ContextFlag; + public byte MessageFlag; + public byte ContextFlag2; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs index f06af1d3c..c5f7e4eb0 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorType.cs @@ -8,6 +8,6 @@ ErrorEulaArg, ErrorPctlArg, ErrorRecordArg, - SystemUpdateEulaArg = 8 + SystemUpdateEulaArg = 8, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs index a8119a470..8db23d30b 100644 --- a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs @@ -8,10 +8,12 @@ namespace Ryujinx.HLE.HOS.Applets { internal class PlayerSelectApplet : IApplet { - private Horizon _system; + private readonly Horizon _system; private AppletSession _normalSession; +#pragma warning disable IDE0052 // Remove unread private member private AppletSession _interactiveSession; +#pragma warning restore IDE0052 public event EventHandler AppletStateChanged; @@ -22,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Applets public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) { - _normalSession = normalSession; + _normalSession = normalSession; _interactiveSession = interactiveSession; // TODO(jduncanator): Parse PlayerSelectConfig from input data @@ -44,15 +46,14 @@ namespace Ryujinx.HLE.HOS.Applets { UserProfile currentUser = _system.AccountManager.LastOpenedUser; - using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.Write((ulong)PlayerSelectResult.Success); + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); - currentUser.UserId.Write(writer); + writer.Write((ulong)PlayerSelectResult.Success); - return stream.ToArray(); - } + currentUser.UserId.Write(writer); + + return stream.ToArray(); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs index 682e094ed..d07aadf4b 100644 --- a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs +++ b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs @@ -3,6 +3,6 @@ enum PlayerSelectResult : ulong { Success = 0, - Failure = 2 + Failure = 2, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs index 36e6ff512..722fe60af 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/CJKCharacterValidation.cs @@ -14,4 +14,4 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard [GeneratedRegex("\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")] private static partial Regex CJKRegex(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs index 727b6d27b..be94ee482 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs @@ -13,6 +13,6 @@ /// <summary> /// Position the cursor at the end of the text /// </summary> - End + End, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs index b17debfc7..cc2938e04 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardRequest.cs @@ -43,6 +43,6 @@ /// <summary> /// [8.0.0+] Request the keyboard applet to use the MovedCursorV2 response when notifying changes in cursor position. /// </summary> - UseMovedCursorV2 = 0xE + UseMovedCursorV2 = 0xE, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs index b21db507c..2179752e1 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardResponse.cs @@ -88,6 +88,6 @@ /// <summary> /// Same as MovedCursorUtf8, but with additional fields. /// </summary> - MovedCursorUtf8V2 = 0x10 + MovedCursorUtf8V2 = 0x10, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs index 47e1a7746..da802fbc1 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineKeyboardState.cs @@ -28,6 +28,6 @@ /// <summary> /// software keyboard is transitioning to a hidden state because the user pressed either OK or Cancel. /// </summary> - Disappearing = 0x4 + Disappearing = 0x4, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs index c3e45d469..e8e57a48d 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InlineResponses.cs @@ -60,55 +60,51 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { uint resSize = 2 * sizeof(uint) + 0x1; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.FinishedInitialize, writer); - writer.Write((byte)1); // Data (ignored by the program) + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.FinishedInitialize, writer); + writer.Write((byte)1); // Data (ignored by the program) + + return stream.ToArray(); } public static byte[] Default(InlineKeyboardState state) { uint resSize = 2 * sizeof(uint); - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.Default, writer); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.Default, writer); + + return stream.ToArray(); } public static byte[] ChangedString(string text, uint cursor, InlineKeyboardState state) { uint resSize = 6 * sizeof(uint) + MaxStrLenUTF16; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.ChangedString, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.ChangedString, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true); + + return stream.ToArray(); } public static byte[] MovedCursor(string text, uint cursor, InlineKeyboardState state) { uint resSize = 4 * sizeof(uint) + MaxStrLenUTF16; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.MovedCursor, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.MovedCursor, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false); + + return stream.ToArray(); } public static byte[] MovedTab(string text, uint cursor, InlineKeyboardState state) @@ -117,182 +113,169 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard uint resSize = 4 * sizeof(uint) + MaxStrLenUTF16; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.MovedTab, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.MovedTab, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false); + + return stream.ToArray(); } public static byte[] DecidedEnter(string text, InlineKeyboardState state) { uint resSize = 3 * sizeof(uint) + MaxStrLenUTF16; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.DecidedEnter, writer); - WriteString(text, writer, MaxStrLenUTF16, Encoding.Unicode); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.DecidedEnter, writer); + WriteString(text, writer, MaxStrLenUTF16, Encoding.Unicode); + + return stream.ToArray(); } public static byte[] DecidedCancel(InlineKeyboardState state) { uint resSize = 2 * sizeof(uint); - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.DecidedCancel, writer); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.DecidedCancel, writer); + + return stream.ToArray(); } public static byte[] ChangedStringUtf8(string text, uint cursor, InlineKeyboardState state) { uint resSize = 6 * sizeof(uint) + MaxStrLenUTF8; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true); + + return stream.ToArray(); } public static byte[] MovedCursorUtf8(string text, uint cursor, InlineKeyboardState state) { uint resSize = 4 * sizeof(uint) + MaxStrLenUTF8; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false); + + return stream.ToArray(); } public static byte[] DecidedEnterUtf8(string text, InlineKeyboardState state) { uint resSize = 3 * sizeof(uint) + MaxStrLenUTF8; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.DecidedEnterUtf8, writer); - WriteString(text, writer, MaxStrLenUTF8, Encoding.UTF8); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.DecidedEnterUtf8, writer); + WriteString(text, writer, MaxStrLenUTF8, Encoding.UTF8); + + return stream.ToArray(); } public static byte[] UnsetCustomizeDic(InlineKeyboardState state) { uint resSize = 2 * sizeof(uint); - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.UnsetCustomizeDic, writer); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.UnsetCustomizeDic, writer); + + return stream.ToArray(); } public static byte[] ReleasedUserWordInfo(InlineKeyboardState state) { uint resSize = 2 * sizeof(uint); - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.ReleasedUserWordInfo, writer); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.ReleasedUserWordInfo, writer); + + return stream.ToArray(); } public static byte[] UnsetCustomizedDictionaries(InlineKeyboardState state) { uint resSize = 2 * sizeof(uint); - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.UnsetCustomizedDictionaries, writer); + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.UnsetCustomizedDictionaries, writer); + + return stream.ToArray(); } public static byte[] ChangedStringV2(string text, uint cursor, InlineKeyboardState state) { uint resSize = 6 * sizeof(uint) + MaxStrLenUTF16 + 0x1; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.ChangedStringV2, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true); - writer.Write((byte)0); // Flag == 0 + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.ChangedStringV2, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true); + writer.Write((byte)0); // Flag == 0 + + return stream.ToArray(); } public static byte[] MovedCursorV2(string text, uint cursor, InlineKeyboardState state) { uint resSize = 4 * sizeof(uint) + MaxStrLenUTF16 + 0x1; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.MovedCursorV2, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false); - writer.Write((byte)0); // Flag == 0 + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.MovedCursorV2, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false); + writer.Write((byte)0); // Flag == 0 + + return stream.ToArray(); } public static byte[] ChangedStringUtf8V2(string text, uint cursor, InlineKeyboardState state) { uint resSize = 6 * sizeof(uint) + MaxStrLenUTF8 + 0x1; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8V2, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true); - writer.Write((byte)0); // Flag == 0 + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8V2, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true); + writer.Write((byte)0); // Flag == 0 + + return stream.ToArray(); } public static byte[] MovedCursorUtf8V2(string text, uint cursor, InlineKeyboardState state) { uint resSize = 4 * sizeof(uint) + MaxStrLenUTF8 + 0x1; - using (MemoryStream stream = new MemoryStream(new byte[resSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8V2, writer); - WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false); - writer.Write((byte)0); // Flag == 0 + using MemoryStream stream = new(new byte[resSize]); + using BinaryWriter writer = new(stream); - return stream.ToArray(); - } + BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8V2, writer); + WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false); + writer.Write((byte)0); // Flag == 0 + + return stream.ToArray(); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs index c3ce2c125..9b7ef9c61 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs @@ -13,6 +13,6 @@ /// <summary> /// Displays the text entry area as a multi-line field. /// </summary> - MultiLine + MultiLine, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs index 1166e81dd..4928c1943 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidButtonFlags.cs @@ -8,10 +8,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard [Flags] enum InvalidButtonFlags : uint { - None = 0, + None = 0, AnalogStickL = 1 << 1, AnalogStickR = 1 << 2, - ZL = 1 << 3, - ZR = 1 << 4, + ZL = 1 << 3, + ZR = 1 << 4, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs index f3fd8ac85..f2dcc5823 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs @@ -51,6 +51,6 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard /// <summary> /// Prohibits characters outside of those allowed in Mii Nicknames. /// </summary> - Username = 1 << 8 + Username = 1 << 8, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs index 0b0f138b6..e1402b1b2 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardCalcFlags.cs @@ -8,19 +8,19 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard [Flags] enum KeyboardCalcFlags : ulong { - Initialize = 0x1, - SetVolume = 0x2, - Appear = 0x4, - SetInputText = 0x8, - SetCursorPos = 0x10, - SetUtf8Mode = 0x20, + Initialize = 0x1, + SetVolume = 0x2, + Appear = 0x4, + SetInputText = 0x8, + SetCursorPos = 0x10, + SetUtf8Mode = 0x20, SetKeyboardBackground = 0x100, - SetKeyboardOptions1 = 0x200, - SetKeyboardOptions2 = 0x800, - EnableSeGroup = 0x2000, - DisableSeGroup = 0x4000, - SetBackspaceEnabled = 0x8000, - AppearTrigger = 0x10000, - MustShow = Appear | SetInputText | AppearTrigger + SetKeyboardOptions1 = 0x200, + SetKeyboardOptions2 = 0x800, + EnableSeGroup = 0x2000, + DisableSeGroup = 0x4000, + SetBackspaceEnabled = 0x8000, + AppearTrigger = 0x10000, + MustShow = Appear | SetInputText | AppearTrigger, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs index 5184118cd..b5ebabebb 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMiniaturizationMode.cs @@ -5,8 +5,8 @@ /// </summary> enum KeyboardMiniaturizationMode : byte { - None = 0, - Auto = 1, - Forced = 2 + None = 0, + Auto = 1, + Forced = 2, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs index e28622111..917e1f9e1 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs @@ -24,16 +24,16 @@ /// Synonymous with default. /// </summary> FullLatin = 3, - + /// <summary> /// All UTF-16 characters except CJK characters allowed. /// </summary> Alphabet = 4, - - SimplifiedChinese = 5, + + SimplifiedChinese = 5, TraditionalChinese = 6, - Korean = 7, - LanguageSet2 = 8, - LanguageSet2Latin = 9, + Korean = 7, + LanguageSet2 = 8, + LanguageSet2Latin = 9, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs index 4f570d3f3..dc39feb87 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardResult.cs @@ -9,4 +9,4 @@ Accept = 1, Cancel = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs index fc9e1ff8e..b5ea7faa2 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs @@ -13,6 +13,6 @@ /// <summary> /// Hide input characters. /// </summary> - Enabled + Enabled, } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs index e1ee0507d..fb6aad649 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppear.cs @@ -77,41 +77,42 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard [MarshalAs(UnmanagedType.I1)] public bool UseSaveData; - public uint Reserved3; + public uint Reserved3; public ushort Reserved4; - public byte Reserved5; - public ulong Reserved6; - public ulong Reserved7; + public byte Reserved5; + public ulong Reserved6; + public ulong Reserved7; public SoftwareKeyboardAppearEx ToExtended() { - SoftwareKeyboardAppearEx appear = new SoftwareKeyboardAppearEx(); - - appear.KeyboardMode = KeyboardMode; - appear.OkText = OkText; - appear.LeftOptionalSymbolKey = LeftOptionalSymbolKey; - appear.RightOptionalSymbolKey = RightOptionalSymbolKey; - appear.PredictionEnabled = PredictionEnabled; - appear.CancelButtonDisabled = CancelButtonDisabled; - appear.InvalidChars = InvalidChars; - appear.TextMaxLength = TextMaxLength; - appear.TextMinLength = TextMinLength; - appear.UseNewLine = UseNewLine; - appear.MiniaturizationMode = MiniaturizationMode; - appear.Reserved1 = Reserved1; - appear.Reserved2 = Reserved2; - appear.InvalidButtons = InvalidButtons; - appear.UseSaveData = UseSaveData; - appear.Reserved3 = Reserved3; - appear.Reserved4 = Reserved4; - appear.Reserved5 = Reserved5; - appear.Uid0 = Reserved6; - appear.Uid1 = Reserved7; - appear.SamplingNumber = 0; - appear.Reserved6 = 0; - appear.Reserved7 = 0; - appear.Reserved8 = 0; - appear.Reserved9 = 0; + SoftwareKeyboardAppearEx appear = new() + { + KeyboardMode = KeyboardMode, + OkText = OkText, + LeftOptionalSymbolKey = LeftOptionalSymbolKey, + RightOptionalSymbolKey = RightOptionalSymbolKey, + PredictionEnabled = PredictionEnabled, + CancelButtonDisabled = CancelButtonDisabled, + InvalidChars = InvalidChars, + TextMaxLength = TextMaxLength, + TextMinLength = TextMinLength, + UseNewLine = UseNewLine, + MiniaturizationMode = MiniaturizationMode, + Reserved1 = Reserved1, + Reserved2 = Reserved2, + InvalidButtons = InvalidButtons, + UseSaveData = UseSaveData, + Reserved3 = Reserved3, + Reserved4 = Reserved4, + Reserved5 = Reserved5, + Uid0 = Reserved6, + Uid1 = Reserved7, + SamplingNumber = 0, + Reserved6 = 0, + Reserved7 = 0, + Reserved8 = 0, + Reserved9 = 0, + }; return appear; } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs index d1756b07a..29cd0a73c 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardAppearEx.cs @@ -77,9 +77,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard [MarshalAs(UnmanagedType.I1)] public bool UseSaveData; - public uint Reserved3; + public uint Reserved3; public ushort Reserved4; - public byte Reserved5; + public byte Reserved5; /// <summary> /// The id of the user associated with the appear request. diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 4337ec44b..e0f6e3f0c 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -21,16 +21,16 @@ namespace Ryujinx.HLE.HOS.Applets { private const string DefaultInputText = "Ryujinx"; - private const int StandardBufferSize = 0x7D8; + private const int StandardBufferSize = 0x7D8; private const int InteractiveBufferSize = 0x7D4; - private const int MaxUserWords = 0x1388; - private const int MaxUiTextSize = 100; + private const int MaxUserWords = 0x1388; + private const int MaxUiTextSize = 100; private const Key CycleInputModesKey = Key.F6; private readonly Switch _device; - private SoftwareKeyboardState _foregroundState = SoftwareKeyboardState.Uninitialized; + private SoftwareKeyboardState _foregroundState = SoftwareKeyboardState.Uninitialized; private volatile InlineKeyboardState _backgroundState = InlineKeyboardState.Uninitialized; private bool _isBackground = false; @@ -42,23 +42,25 @@ namespace Ryujinx.HLE.HOS.Applets private SoftwareKeyboardConfig _keyboardForegroundConfig; // Configuration for background (inline) mode. - private SoftwareKeyboardInitialize _keyboardBackgroundInitialize; +#pragma warning disable IDE0052 // Remove unread private member + private SoftwareKeyboardInitialize _keyboardBackgroundInitialize; private SoftwareKeyboardCustomizeDic _keyboardBackgroundDic; - private SoftwareKeyboardDictSet _keyboardBackgroundDictSet; - private SoftwareKeyboardUserWord[] _keyboardBackgroundUserWords; + private SoftwareKeyboardDictSet _keyboardBackgroundDictSet; +#pragma warning restore IDE0052 + private SoftwareKeyboardUserWord[] _keyboardBackgroundUserWords; private byte[] _transferMemory; - private string _textValue = ""; - private int _cursorBegin = 0; - private Encoding _encoding = Encoding.Unicode; - private KeyboardResult _lastResult = KeyboardResult.NotSet; + private string _textValue = ""; + private int _cursorBegin = 0; + private Encoding _encoding = Encoding.Unicode; + private KeyboardResult _lastResult = KeyboardResult.NotSet; private IDynamicTextInputHandler _dynamicTextInputHandler = null; - private SoftwareKeyboardRenderer _keyboardRenderer = null; - private NpadReader _npads = null; - private bool _canAcceptController = false; - private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard; + private SoftwareKeyboardRenderer _keyboardRenderer = null; + private NpadReader _npads = null; + private bool _canAcceptController = false; + private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard; private readonly object _lock = new(); @@ -73,12 +75,12 @@ namespace Ryujinx.HLE.HOS.Applets { lock (_lock) { - _normalSession = normalSession; + _normalSession = normalSession; _interactiveSession = interactiveSession; _interactiveSession.DataAvailable += OnInteractiveData; - var launchParams = _normalSession.Pop(); + var launchParams = _normalSession.Pop(); var keyboardConfig = _normalSession.Pop(); _isBackground = keyboardConfig.Length == Unsafe.SizeOf<SoftwareKeyboardInitialize>(); @@ -88,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Applets // Initialize the keyboard applet in background mode. _keyboardBackgroundInitialize = MemoryMarshal.Read<SoftwareKeyboardInitialize>(keyboardConfig); - _backgroundState = InlineKeyboardState.Uninitialized; + _backgroundState = InlineKeyboardState.Uninitialized; if (_device.UiHandler == null) { @@ -99,11 +101,11 @@ namespace Ryujinx.HLE.HOS.Applets // Create a text handler that converts keyboard strokes to strings. _dynamicTextInputHandler = _device.UiHandler.CreateDynamicTextInputHandler(); _dynamicTextInputHandler.TextChangedEvent += HandleTextChangedEvent; - _dynamicTextInputHandler.KeyPressedEvent += HandleKeyPressedEvent; + _dynamicTextInputHandler.KeyPressedEvent += HandleKeyPressedEvent; _npads = new NpadReader(_device); _npads.NpadButtonDownEvent += HandleNpadButtonDownEvent; - _npads.NpadButtonUpEvent += HandleNpadButtonUpEvent; + _npads.NpadButtonUpEvent += HandleNpadButtonUpEvent; _keyboardRenderer = new SoftwareKeyboardRenderer(_device.UiHandler.HostUiTheme); } @@ -217,7 +219,7 @@ namespace Ryujinx.HLE.HOS.Applets _keyboardForegroundConfig.SubmitText : "OK"), StringLengthMin = _keyboardForegroundConfig.StringLengthMin, StringLengthMax = _keyboardForegroundConfig.StringLengthMax, - InitialText = initialText + InitialText = initialText, }; _lastResult = _device.UiHandler.DisplayInputDialog(args, out _textValue) ? KeyboardResult.Accept : KeyboardResult.Cancel; @@ -237,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Applets // we truncate it. if (_textValue.Length > _keyboardForegroundConfig.StringLengthMax) { - _textValue = _textValue.Substring(0, _keyboardForegroundConfig.StringLengthMax); + _textValue = _textValue[.._keyboardForegroundConfig.StringLengthMax]; } // Does the application want to validate the text itself? @@ -319,178 +321,177 @@ namespace Ryujinx.HLE.HOS.Applets // request from the game, this is because the inline keyboard is expected to // keep running in the background sending data by itself. - using (MemoryStream stream = new MemoryStream(data)) - using (BinaryReader reader = new BinaryReader(stream)) + using MemoryStream stream = new(data); + using BinaryReader reader = new(stream); + + var request = (InlineKeyboardRequest)reader.ReadUInt32(); + + long remaining; + + Logger.Debug?.Print(LogClass.ServiceAm, $"Keyboard received command {request} in state {_backgroundState}"); + + switch (request) { - var request = (InlineKeyboardRequest)reader.ReadUInt32(); - - long remaining; - - Logger.Debug?.Print(LogClass.ServiceAm, $"Keyboard received command {request} in state {_backgroundState}"); - - switch (request) - { - case InlineKeyboardRequest.UseChangedStringV2: - Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseChangedStringV2"); - break; - case InlineKeyboardRequest.UseMovedCursorV2: - Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseMovedCursorV2"); - break; - case InlineKeyboardRequest.SetUserWordInfo: - // Read the user word info data. + case InlineKeyboardRequest.UseChangedStringV2: + Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseChangedStringV2"); + break; + case InlineKeyboardRequest.UseMovedCursorV2: + Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseMovedCursorV2"); + break; + case InlineKeyboardRequest.SetUserWordInfo: + // Read the user word info data. + remaining = stream.Length - stream.Position; + if (remaining < sizeof(int)) + { + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info of {remaining} bytes"); + } + else + { + int wordsCount = reader.ReadInt32(); + int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>(); remaining = stream.Length - stream.Position; - if (remaining < sizeof(int)) + + if (wordsCount > MaxUserWords) { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info of {remaining} bytes"); + Logger.Warning?.Print(LogClass.ServiceAm, $"Received {wordsCount} User Words but the maximum is {MaxUserWords}"); + } + else if (wordsCount * wordSize != remaining) + { + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info data of {remaining} bytes for {wordsCount} words"); } else { - int wordsCount = reader.ReadInt32(); - int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>(); - remaining = stream.Length - stream.Position; + _keyboardBackgroundUserWords = new SoftwareKeyboardUserWord[wordsCount]; - if (wordsCount > MaxUserWords) + for (int word = 0; word < wordsCount; word++) { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received {wordsCount} User Words but the maximum is {MaxUserWords}"); - } - else if (wordsCount * wordSize != remaining) - { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info data of {remaining} bytes for {wordsCount} words"); - } - else - { - _keyboardBackgroundUserWords = new SoftwareKeyboardUserWord[wordsCount]; - - for (int word = 0; word < wordsCount; word++) - { - _keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>(); - } + _keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>(); } } - _interactiveSession.Push(InlineResponses.ReleasedUserWordInfo(_backgroundState)); - break; - case InlineKeyboardRequest.SetCustomizeDic: - // Read the custom dic data. - remaining = stream.Length - stream.Position; - if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>()) - { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes"); - } - else - { - _keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>(); - } - break; - case InlineKeyboardRequest.SetCustomizedDictionaries: - // Read the custom dictionaries data. - remaining = stream.Length - stream.Position; - if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>()) - { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes"); - } - else - { - _keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>(); - } - break; - case InlineKeyboardRequest.Calc: - // The Calc request is used to communicate configuration changes and commands to the keyboard. - // Fields in the Calc struct and operations are masked by the Flags field. + } + _interactiveSession.Push(InlineResponses.ReleasedUserWordInfo(_backgroundState)); + break; + case InlineKeyboardRequest.SetCustomizeDic: + // Read the custom dic data. + remaining = stream.Length - stream.Position; + if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>()) + { + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes"); + } + else + { + _keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>(); + } + break; + case InlineKeyboardRequest.SetCustomizedDictionaries: + // Read the custom dictionaries data. + remaining = stream.Length - stream.Position; + if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>()) + { + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes"); + } + else + { + _keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>(); + } + break; + case InlineKeyboardRequest.Calc: + // The Calc request is used to communicate configuration changes and commands to the keyboard. + // Fields in the Calc struct and operations are masked by the Flags field. - // Read the Calc data. - SoftwareKeyboardCalcEx newCalc; - remaining = stream.Length - stream.Position; - if (remaining == Marshal.SizeOf<SoftwareKeyboardCalc>()) - { - var keyboardCalcData = reader.ReadBytes((int)remaining); - var keyboardCalc = ReadStruct<SoftwareKeyboardCalc>(keyboardCalcData); + // Read the Calc data. + SoftwareKeyboardCalcEx newCalc; + remaining = stream.Length - stream.Position; + if (remaining == Marshal.SizeOf<SoftwareKeyboardCalc>()) + { + var keyboardCalcData = reader.ReadBytes((int)remaining); + var keyboardCalc = ReadStruct<SoftwareKeyboardCalc>(keyboardCalcData); - newCalc = keyboardCalc.ToExtended(); - } - else if (remaining == Marshal.SizeOf<SoftwareKeyboardCalcEx>() || remaining == SoftwareKeyboardCalcEx.AlternativeSize) - { - var keyboardCalcData = reader.ReadBytes((int)remaining); + newCalc = keyboardCalc.ToExtended(); + } + else if (remaining == Marshal.SizeOf<SoftwareKeyboardCalcEx>() || remaining == SoftwareKeyboardCalcEx.AlternativeSize) + { + var keyboardCalcData = reader.ReadBytes((int)remaining); - newCalc = ReadStruct<SoftwareKeyboardCalcEx>(keyboardCalcData); - } - else - { - Logger.Error?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Calc of {remaining} bytes"); + newCalc = ReadStruct<SoftwareKeyboardCalcEx>(keyboardCalcData); + } + else + { + Logger.Error?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Calc of {remaining} bytes"); - newCalc = new SoftwareKeyboardCalcEx(); - } + newCalc = new SoftwareKeyboardCalcEx(); + } - // Process each individual operation specified in the flags. + // Process each individual operation specified in the flags. - bool updateText = false; + bool updateText = false; - if ((newCalc.Flags & KeyboardCalcFlags.Initialize) != 0) - { - _interactiveSession.Push(InlineResponses.FinishedInitialize(_backgroundState)); + if ((newCalc.Flags & KeyboardCalcFlags.Initialize) != 0) + { + _interactiveSession.Push(InlineResponses.FinishedInitialize(_backgroundState)); - _backgroundState = InlineKeyboardState.Initialized; - } + _backgroundState = InlineKeyboardState.Initialized; + } - if ((newCalc.Flags & KeyboardCalcFlags.SetCursorPos) != 0) - { - _cursorBegin = newCalc.CursorPos; - updateText = true; + if ((newCalc.Flags & KeyboardCalcFlags.SetCursorPos) != 0) + { + _cursorBegin = newCalc.CursorPos; + updateText = true; - Logger.Debug?.Print(LogClass.ServiceAm, $"Cursor position set to {_cursorBegin}"); - } + Logger.Debug?.Print(LogClass.ServiceAm, $"Cursor position set to {_cursorBegin}"); + } - if ((newCalc.Flags & KeyboardCalcFlags.SetInputText) != 0) - { - _textValue = newCalc.InputText; - updateText = true; + if ((newCalc.Flags & KeyboardCalcFlags.SetInputText) != 0) + { + _textValue = newCalc.InputText; + updateText = true; - Logger.Debug?.Print(LogClass.ServiceAm, $"Input text set to {_textValue}"); - } + Logger.Debug?.Print(LogClass.ServiceAm, $"Input text set to {_textValue}"); + } - if ((newCalc.Flags & KeyboardCalcFlags.SetUtf8Mode) != 0) - { - _encoding = newCalc.UseUtf8 ? Encoding.UTF8 : Encoding.Default; + if ((newCalc.Flags & KeyboardCalcFlags.SetUtf8Mode) != 0) + { + _encoding = newCalc.UseUtf8 ? Encoding.UTF8 : Encoding.Default; - Logger.Debug?.Print(LogClass.ServiceAm, $"Encoding set to {_encoding}"); - } + Logger.Debug?.Print(LogClass.ServiceAm, $"Encoding set to {_encoding}"); + } - if (updateText) - { - _dynamicTextInputHandler.SetText(_textValue, _cursorBegin); - _keyboardRenderer.UpdateTextState(_textValue, _cursorBegin, _cursorBegin, null, null); - } + if (updateText) + { + _dynamicTextInputHandler.SetText(_textValue, _cursorBegin); + _keyboardRenderer.UpdateTextState(_textValue, _cursorBegin, _cursorBegin, null, null); + } - if ((newCalc.Flags & KeyboardCalcFlags.MustShow) != 0) - { - ActivateFrontend(); + if ((newCalc.Flags & KeyboardCalcFlags.MustShow) != 0) + { + ActivateFrontend(); - _backgroundState = InlineKeyboardState.Shown; + _backgroundState = InlineKeyboardState.Shown; - PushChangedString(_textValue, (uint)_cursorBegin, _backgroundState); - } + PushChangedString(_textValue, (uint)_cursorBegin, _backgroundState); + } - // Send the response to the Calc - _interactiveSession.Push(InlineResponses.Default(_backgroundState)); - break; - case InlineKeyboardRequest.Finalize: - // Destroy the frontend. - DestroyFrontend(); - // The calling application wants to close the keyboard applet and will wait for a state change. - _backgroundState = InlineKeyboardState.Uninitialized; - AppletStateChanged?.Invoke(this, null); - break; - default: - // We shouldn't be able to get here through standard swkbd execution. - Logger.Warning?.Print(LogClass.ServiceAm, $"Invalid Software Keyboard request {request} during state {_backgroundState}"); - _interactiveSession.Push(InlineResponses.Default(_backgroundState)); - break; - } + // Send the response to the Calc + _interactiveSession.Push(InlineResponses.Default(_backgroundState)); + break; + case InlineKeyboardRequest.Finalize: + // Destroy the frontend. + DestroyFrontend(); + // The calling application wants to close the keyboard applet and will wait for a state change. + _backgroundState = InlineKeyboardState.Uninitialized; + AppletStateChanged?.Invoke(this, null); + break; + default: + // We shouldn't be able to get here through standard swkbd execution. + Logger.Warning?.Print(LogClass.ServiceAm, $"Invalid Software Keyboard request {request} during state {_backgroundState}"); + _interactiveSession.Push(InlineResponses.Default(_backgroundState)); + break; } } private void ActivateFrontend() { - Logger.Debug?.Print(LogClass.ServiceAm, $"Activating software keyboard frontend"); + Logger.Debug?.Print(LogClass.ServiceAm, "Activating software keyboard frontend"); _inputMode = KeyboardInputMode.ControllerAndKeyboard; @@ -509,9 +510,9 @@ namespace Ryujinx.HLE.HOS.Applets private void DeactivateFrontend() { - Logger.Debug?.Print(LogClass.ServiceAm, $"Deactivating software keyboard frontend"); + Logger.Debug?.Print(LogClass.ServiceAm, "Deactivating software keyboard frontend"); - _inputMode = KeyboardInputMode.ControllerAndKeyboard; + _inputMode = KeyboardInputMode.ControllerAndKeyboard; _canAcceptController = false; _dynamicTextInputHandler.TextProcessingEnabled = false; @@ -520,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Applets private void DestroyFrontend() { - Logger.Debug?.Print(LogClass.ServiceAm, $"Destroying software keyboard frontend"); + Logger.Debug?.Print(LogClass.ServiceAm, "Destroying software keyboard frontend"); _keyboardRenderer?.Dispose(); _keyboardRenderer = null; @@ -528,7 +529,7 @@ namespace Ryujinx.HLE.HOS.Applets if (_dynamicTextInputHandler != null) { _dynamicTextInputHandler.TextChangedEvent -= HandleTextChangedEvent; - _dynamicTextInputHandler.KeyPressedEvent -= HandleKeyPressedEvent; + _dynamicTextInputHandler.KeyPressedEvent -= HandleKeyPressedEvent; _dynamicTextInputHandler.Dispose(); _dynamicTextInputHandler = null; } @@ -536,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Applets if (_npads != null) { _npads.NpadButtonDownEvent -= HandleNpadButtonDownEvent; - _npads.NpadButtonUpEvent -= HandleNpadButtonUpEvent; + _npads.NpadButtonUpEvent -= HandleNpadButtonUpEvent; _npads = null; } } @@ -551,7 +552,7 @@ namespace Ryujinx.HLE.HOS.Applets { AdvanceInputMode(); - bool typingEnabled = InputModeTypingEnabled(); + bool typingEnabled = InputModeTypingEnabled(); bool controllerEnabled = InputModeControllerEnabled(); _dynamicTextInputHandler.TextProcessingEnabled = typingEnabled; @@ -575,14 +576,14 @@ namespace Ryujinx.HLE.HOS.Applets if (text.Length > MaxUiTextSize) { // Limit the text size and change it back. - text = text.Substring(0, MaxUiTextSize); + text = text[..MaxUiTextSize]; cursorBegin = Math.Min(cursorBegin, MaxUiTextSize); - cursorEnd = Math.Min(cursorEnd, MaxUiTextSize); + cursorEnd = Math.Min(cursorEnd, MaxUiTextSize); _dynamicTextInputHandler.SetText(text, cursorBegin, cursorEnd); } - _textValue = text; + _textValue = text; _cursorBegin = cursorBegin; _keyboardRenderer.UpdateTextState(text, cursorBegin, cursorEnd, overwriteMode, null); @@ -646,7 +647,7 @@ namespace Ryujinx.HLE.HOS.Applets private void PushUpdatedState(string text, int cursorBegin, KeyboardResult result) { _lastResult = result; - _textValue = text; + _textValue = text; bool cancel = result == KeyboardResult.Cancel; bool accept = result == KeyboardResult.Accept; @@ -734,33 +735,31 @@ namespace Ryujinx.HLE.HOS.Applets { int bufferSize = interactive ? InteractiveBufferSize : StandardBufferSize; - using (MemoryStream stream = new MemoryStream(new byte[bufferSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) + using MemoryStream stream = new(new byte[bufferSize]); + using BinaryWriter writer = new(stream); + byte[] output = _encoding.GetBytes(_textValue); + + if (!interactive) { - byte[] output = _encoding.GetBytes(_textValue); + // Result Code. + writer.Write(_lastResult == KeyboardResult.Accept ? 0U : 1U); + } + else + { + // In interactive mode, we write the length of the text as a long, rather than + // a result code. This field is inclusive of the 64-bit size. + writer.Write((long)output.Length + 8); + } - if (!interactive) - { - // Result Code. - writer.Write(_lastResult == KeyboardResult.Accept ? 0U : 1U); - } - else - { - // In interactive mode, we write the length of the text as a long, rather than - // a result code. This field is inclusive of the 64-bit size. - writer.Write((long)output.Length + 8); - } + writer.Write(output); - writer.Write(output); - - if (!interactive) - { - _normalSession.Push(stream.ToArray()); - } - else - { - _interactiveSession.Push(stream.ToArray()); - } + if (!interactive) + { + _normalSession.Push(stream.ToArray()); + } + else + { + _interactiveSession.Push(stream.ToArray()); } } @@ -787,7 +786,7 @@ namespace Ryujinx.HLE.HOS.Applets return string.Empty; } - StringBuilder sb = new StringBuilder(capacity: input.Length); + StringBuilder sb = new(capacity: input.Length); foreach (char c in input) { if (!char.IsControl(c)) diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs index 90df6fa35..2941e15d1 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCalc.cs @@ -174,45 +174,46 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard public SoftwareKeyboardCalcEx ToExtended() { - SoftwareKeyboardCalcEx calc = new SoftwareKeyboardCalcEx(); - - calc.Unknown = Unknown; - calc.Size = Size; - calc.Unknown1 = Unknown1; - calc.Unknown2 = Unknown2; - calc.Flags = Flags; - calc.Initialize = Initialize; - calc.Volume = Volume; - calc.CursorPos = CursorPos; - calc.Appear = Appear.ToExtended(); - calc.InputText = InputText; - calc.UseUtf8 = UseUtf8; - calc.Unknown3 = Unknown3; - calc.BackspaceEnabled = BackspaceEnabled; - calc.Unknown4 = Unknown4; - calc.Unknown5 = Unknown5; - calc.KeytopAsFloating = KeytopAsFloating; - calc.FooterScalable = FooterScalable; - calc.AlphaEnabledInInputMode = AlphaEnabledInInputMode; - calc.InputModeFadeType = InputModeFadeType; - calc.TouchDisabled = TouchDisabled; - calc.HardwareKeyboardDisabled = HardwareKeyboardDisabled; - calc.Unknown6 = Unknown6; - calc.Unknown7 = Unknown7; - calc.KeytopScale0 = KeytopScale0; - calc.KeytopScale1 = KeytopScale1; - calc.KeytopTranslate0 = KeytopTranslate0; - calc.KeytopTranslate1 = KeytopTranslate1; - calc.KeytopBgAlpha = KeytopBgAlpha; - calc.FooterBgAlpha = FooterBgAlpha; - calc.BalloonScale = BalloonScale; - calc.Unknown8 = Unknown8; - calc.Unknown9 = Unknown9; - calc.Unknown10 = Unknown10; - calc.Unknown11 = Unknown11; - calc.SeGroup = SeGroup; - calc.TriggerFlag = TriggerFlag; - calc.Trigger = Trigger; + SoftwareKeyboardCalcEx calc = new() + { + Unknown = Unknown, + Size = Size, + Unknown1 = Unknown1, + Unknown2 = Unknown2, + Flags = Flags, + Initialize = Initialize, + Volume = Volume, + CursorPos = CursorPos, + Appear = Appear.ToExtended(), + InputText = InputText, + UseUtf8 = UseUtf8, + Unknown3 = Unknown3, + BackspaceEnabled = BackspaceEnabled, + Unknown4 = Unknown4, + Unknown5 = Unknown5, + KeytopAsFloating = KeytopAsFloating, + FooterScalable = FooterScalable, + AlphaEnabledInInputMode = AlphaEnabledInInputMode, + InputModeFadeType = InputModeFadeType, + TouchDisabled = TouchDisabled, + HardwareKeyboardDisabled = HardwareKeyboardDisabled, + Unknown6 = Unknown6, + Unknown7 = Unknown7, + KeytopScale0 = KeytopScale0, + KeytopScale1 = KeytopScale1, + KeytopTranslate0 = KeytopTranslate0, + KeytopTranslate1 = KeytopTranslate1, + KeytopBgAlpha = KeytopBgAlpha, + FooterBgAlpha = FooterBgAlpha, + BalloonScale = BalloonScale, + Unknown8 = Unknown8, + Unknown9 = Unknown9, + Unknown10 = Unknown10, + Unknown11 = Unknown11, + SeGroup = SeGroup, + TriggerFlag = TriggerFlag, + Trigger = Trigger, + }; return calc; } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs index fd462382b..58c389e1a 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs @@ -8,10 +8,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct SoftwareKeyboardConfig { - private const int SubmitTextLength = 8; - private const int HeaderTextLength = 64; + private const int SubmitTextLength = 8; + private const int HeaderTextLength = 64; private const int SubtitleTextLength = 128; - private const int GuideTextLength = 256; + private const int GuideTextLength = 256; /// <summary> /// Type of keyboard. diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs index fb4cec82a..2331e36ab 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs @@ -10,16 +10,16 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard /// </summary> internal class SoftwareKeyboardRenderer : IDisposable { - private const int TextBoxBlinkSleepMilliseconds = 100; + private const int TextBoxBlinkSleepMilliseconds = 100; private const int RendererWaitTimeoutMilliseconds = 100; - private readonly object _stateLock = new(); + private readonly object _stateLock = new(); - private SoftwareKeyboardUiState _state = new SoftwareKeyboardUiState(); - private SoftwareKeyboardRendererBase _renderer; + private readonly SoftwareKeyboardUiState _state = new(); + private readonly SoftwareKeyboardRendererBase _renderer; - private TimedAction _textBoxBlinkTimedAction = new TimedAction(); - private TimedAction _renderAction = new TimedAction(); + private readonly TimedAction _textBoxBlinkTimedAction = new(); + private readonly TimedAction _renderAction = new(); public SoftwareKeyboardRenderer(IHostUiTheme uiTheme) { @@ -47,10 +47,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard private static void StartRenderer(TimedAction timedAction, SoftwareKeyboardRendererBase renderer, SoftwareKeyboardUiState state, object stateLock) { - SoftwareKeyboardUiState internalState = new SoftwareKeyboardUiState(); + SoftwareKeyboardUiState internalState = new(); bool canCreateSurface = false; - bool needsUpdate = true; + bool needsUpdate = true; timedAction.Reset(() => { @@ -61,6 +61,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard return; } +#pragma warning disable IDE0055 // Disable formatting needsUpdate = UpdateStateField(ref state.InputText, ref internalState.InputText); needsUpdate |= UpdateStateField(ref state.CursorBegin, ref internalState.CursorBegin); needsUpdate |= UpdateStateField(ref state.CursorEnd, ref internalState.CursorEnd); @@ -70,6 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard needsUpdate |= UpdateStateField(ref state.TypingEnabled, ref internalState.TypingEnabled); needsUpdate |= UpdateStateField(ref state.ControllerEnabled, ref internalState.ControllerEnabled); needsUpdate |= UpdateStateField(ref state.TextBoxBlinkCounter, ref internalState.TextBoxBlinkCounter); +#pragma warning restore IDE0055 canCreateSurface = state.SurfaceInfo != null && internalState.SurfaceInfo == null; @@ -104,16 +106,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard return false; } -#pragma warning disable CS8632 - public void UpdateTextState(string? inputText, int? cursorBegin, int? cursorEnd, bool? overwriteMode, bool? typingEnabled) -#pragma warning restore CS8632 + public void UpdateTextState(string inputText, int? cursorBegin, int? cursorEnd, bool? overwriteMode, bool? typingEnabled) { lock (_stateLock) { // Update the parameters that were provided. - _state.InputText = inputText != null ? inputText : _state.InputText; - _state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin); - _state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd); + _state.InputText = inputText ?? _state.InputText; + _state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin); + _state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd); _state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode); _state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled); @@ -130,8 +130,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard lock (_stateLock) { // Update the parameters that were provided. - _state.AcceptPressed = acceptPressed.GetValueOrDefault(_state.AcceptPressed); - _state.CancelPressed = cancelPressed.GetValueOrDefault(_state.CancelPressed); + _state.AcceptPressed = acceptPressed.GetValueOrDefault(_state.AcceptPressed); + _state.CancelPressed = cancelPressed.GetValueOrDefault(_state.CancelPressed); _state.ControllerEnabled = controllerEnabled.GetValueOrDefault(_state.ControllerEnabled); // Tell the render thread there is something new to render. diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index 595223ed3..770bf6e5a 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -21,47 +21,47 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { public const int TextBoxBlinkThreshold = 8; - const string MessageText = "Please use the keyboard to input text"; - const string AcceptText = "Accept"; - const string CancelText = "Cancel"; + const string MessageText = "Please use the keyboard to input text"; + const string AcceptText = "Accept"; + const string CancelText = "Cancel"; const string ControllerToggleText = "Toggle input"; private readonly object _bufferLock = new(); private RenderingSurfaceInfo _surfaceInfo = null; - private Image<Argb32> _surface = null; - private byte[] _bufferData = null; + private Image<Argb32> _surface = null; + private byte[] _bufferData = null; - private Image _ryujinxLogo = null; - private Image _padAcceptIcon = null; - private Image _padCancelIcon = null; - private Image _keyModeIcon = null; + private readonly Image _ryujinxLogo = null; + private readonly Image _padAcceptIcon = null; + private readonly Image _padCancelIcon = null; + private readonly Image _keyModeIcon = null; - private float _textBoxOutlineWidth; - private float _padPressedPenWidth; + private readonly float _textBoxOutlineWidth; + private readonly float _padPressedPenWidth; - private Color _textNormalColor; - private Color _textSelectedColor; - private Color _textOverCursorColor; + private readonly Color _textNormalColor; + private readonly Color _textSelectedColor; + private readonly Color _textOverCursorColor; - private IBrush _panelBrush; - private IBrush _disabledBrush; - private IBrush _cursorBrush; - private IBrush _selectionBoxBrush; + private readonly IBrush _panelBrush; + private readonly IBrush _disabledBrush; + private readonly IBrush _cursorBrush; + private readonly IBrush _selectionBoxBrush; - private Pen _textBoxOutlinePen; - private Pen _cursorPen; - private Pen _selectionBoxPen; - private Pen _padPressedPen; + private readonly Pen _textBoxOutlinePen; + private readonly Pen _cursorPen; + private readonly Pen _selectionBoxPen; + private readonly Pen _padPressedPen; - private int _inputTextFontSize; + private readonly int _inputTextFontSize; private Font _messageFont; private Font _inputTextFont; private Font _labelsTextFont; private RectangleF _panelRectangle; - private Point _logoPosition; - private float _messagePositionY; + private Point _logoPosition; + private float _messagePositionY; public SoftwareKeyboardRendererBase(IHostUiTheme uiTheme) { @@ -72,35 +72,35 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard string padAcceptIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnA.png"; string padCancelIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnB.png"; - string keyModeIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_KeyF6.png"; + string keyModeIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_KeyF6.png"; - _padAcceptIcon = LoadResource(Assembly.GetExecutingAssembly(), padAcceptIconPath , 0, 0); - _padCancelIcon = LoadResource(Assembly.GetExecutingAssembly(), padCancelIconPath , 0, 0); - _keyModeIcon = LoadResource(Assembly.GetExecutingAssembly(), keyModeIconPath , 0, 0); + _padAcceptIcon = LoadResource(Assembly.GetExecutingAssembly(), padAcceptIconPath, 0, 0); + _padCancelIcon = LoadResource(Assembly.GetExecutingAssembly(), padCancelIconPath, 0, 0); + _keyModeIcon = LoadResource(Assembly.GetExecutingAssembly(), keyModeIconPath, 0, 0); - Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255); - Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150); - Color borderColor = ToColor(uiTheme.DefaultBorderColor); + Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255); + Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150); + Color borderColor = ToColor(uiTheme.DefaultBorderColor); Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor); - _textNormalColor = ToColor(uiTheme.DefaultForegroundColor); - _textSelectedColor = ToColor(uiTheme.SelectionForegroundColor); + _textNormalColor = ToColor(uiTheme.DefaultForegroundColor); + _textSelectedColor = ToColor(uiTheme.SelectionForegroundColor); _textOverCursorColor = ToColor(uiTheme.DefaultForegroundColor, null, true); float cursorWidth = 2; _textBoxOutlineWidth = 2; - _padPressedPenWidth = 2; + _padPressedPenWidth = 2; - _panelBrush = new SolidBrush(panelColor); - _disabledBrush = new SolidBrush(panelTransparentColor); - _cursorBrush = new SolidBrush(_textNormalColor); + _panelBrush = new SolidBrush(panelColor); + _disabledBrush = new SolidBrush(panelTransparentColor); + _cursorBrush = new SolidBrush(_textNormalColor); _selectionBoxBrush = new SolidBrush(selectionBackgroundColor); _textBoxOutlinePen = new Pen(borderColor, _textBoxOutlineWidth); - _cursorPen = new Pen(_textNormalColor, cursorWidth); - _selectionBoxPen = new Pen(selectionBackgroundColor, cursorWidth); - _padPressedPen = new Pen(borderColor, _padPressedPenWidth); + _cursorPen = new Pen(_textNormalColor, cursorWidth); + _selectionBoxPen = new Pen(selectionBackgroundColor, cursorWidth); + _padPressedPen = new Pen(borderColor, _padPressedPenWidth); _inputTextFontSize = 20; @@ -111,22 +111,21 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { // Try a list of fonts in case any of them is not available in the system. - string[] availableFonts = new string[] - { + string[] availableFonts = { uiThemeFontFamily, "Liberation Sans", "FreeSans", "DejaVu Sans", - "Lucida Grande" + "Lucida Grande", }; foreach (string fontFamily in availableFonts) { try { - _messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular); - _inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular); - _labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular); + _messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular); + _inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular); + _labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular); return; } @@ -138,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!"); } - private Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false) + private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false) { var a = (byte)(color.A * 255); var r = (byte)(color.R * 255); @@ -155,14 +154,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a)); } - private Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight) + private static Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight) { Stream resourceStream = assembly.GetManifestResourceStream(resourcePath); return LoadResource(resourceStream, newWidth, newHeight); } - private Image LoadResource(Stream resourceStream, int newWidth, int newHeight) + private static Image LoadResource(Stream resourceStream, int newWidth, int newHeight) { Debug.Assert(resourceStream != null); @@ -176,7 +175,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard return image; } - private void SetGraphicsOptions(IImageProcessingContext context) + private static void SetGraphicsOptions(IImageProcessingContext context) { context.GetGraphicsOptions().Antialias = true; context.GetShapeGraphicsOptions().GraphicsOptions.Antialias = true; @@ -198,9 +197,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard context.DrawImage(_ryujinxLogo, _logoPosition, 1); float halfWidth = _panelRectangle.Width / 2; - float buttonsY = _panelRectangle.Y + 185; + float buttonsY = _panelRectangle.Y + 185; - PointF disableButtonPosition = new PointF(halfWidth + 180, buttonsY); + PointF disableButtonPosition = new(halfWidth + 180, buttonsY); DrawControllerToggle(context, disableButtonPosition); }); @@ -215,11 +214,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard _surface.Mutate(context => { - var messageRectangle = MeasureString(MessageText, _messageFont); - float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X; - float messagePositionY = _messagePositionY - messageRectangle.Y; - var messagePosition = new PointF(messagePositionX, messagePositionY); - var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height); + var messageRectangle = MeasureString(MessageText, _messageFont); + float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X; + float messagePositionY = _messagePositionY - messageRectangle.Y; + var messagePosition = new PointF(messagePositionX, messagePositionY); + var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height); SetGraphicsOptions(context); @@ -238,11 +237,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard DrawTextBox(context, state); float halfWidth = _panelRectangle.Width / 2; - float buttonsY = _panelRectangle.Y + 185; + float buttonsY = _panelRectangle.Y + 185; - PointF acceptButtonPosition = new PointF(halfWidth - 180, buttonsY); - PointF cancelButtonPosition = new PointF(halfWidth , buttonsY); - PointF disableButtonPosition = new PointF(halfWidth + 180, buttonsY); + PointF acceptButtonPosition = new(halfWidth - 180, buttonsY); + PointF cancelButtonPosition = new(halfWidth, buttonsY); + PointF disableButtonPosition = new(halfWidth + 180, buttonsY); DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled); DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled); @@ -262,7 +261,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard // Use the whole area of the image to draw, even the alignment, otherwise it may shear the final // image if the pitch is different. - uint totalWidth = _surfaceInfo.Pitch / 4; + uint totalWidth = _surfaceInfo.Pitch / 4; uint totalHeight = _surfaceInfo.Size / _surfaceInfo.Pitch; Debug.Assert(_surfaceInfo.Width <= totalWidth); @@ -277,10 +276,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard private void ComputeConstants() { - int totalWidth = (int)_surfaceInfo.Width; + int totalWidth = (int)_surfaceInfo.Width; int totalHeight = (int)_surfaceInfo.Height; - int panelHeight = 240; + int panelHeight = 240; int panelPositionY = totalHeight - panelHeight; _panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight); @@ -294,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard } private static RectangleF MeasureString(string text, Font font) { - RendererOptions options = new RendererOptions(font); + RendererOptions options = new(font); if (text == "") { @@ -310,7 +309,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard private static RectangleF MeasureString(ReadOnlySpan<char> text, Font font) { - RendererOptions options = new RendererOptions(font); + RendererOptions options = new(font); if (text == "") { @@ -327,14 +326,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { var inputTextRectangle = MeasureString(state.InputText, _inputTextFont); - float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8)); + float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8)); float boxHeight = 32; - float boxY = _panelRectangle.Y + 110; - float boxX = (int)((_panelRectangle.Width - boxWidth) / 2); + float boxY = _panelRectangle.Y + 110; + float boxX = (int)((_panelRectangle.Width - boxWidth) / 2); - RectangleF boxRectangle = new RectangleF(boxX, boxY, boxWidth, boxHeight); + RectangleF boxRectangle = new(boxX, boxY, boxWidth, boxHeight); - RectangleF boundRectangle = new RectangleF(_panelRectangle.X, boxY - _textBoxOutlineWidth, + RectangleF boundRectangle = new(_panelRectangle.X, boxY - _textBoxOutlineWidth, _panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth); context.Fill(_panelBrush, boundRectangle); @@ -350,11 +349,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard // Draw the cursor on top of the text and redraw the text with a different color if necessary. - Color cursorTextColor; + Color cursorTextColor; IBrush cursorBrush; - Pen cursorPen; + Pen cursorPen; - float cursorPositionYTop = inputTextY + 1; + float cursorPositionYTop = inputTextY + 1; float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1; float cursorPositionXLeft; float cursorPositionXRight; @@ -366,34 +365,34 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard Debug.Assert(state.InputText.Length > 0); cursorTextColor = _textSelectedColor; - cursorBrush = _selectionBoxBrush; - cursorPen = _selectionBoxPen; + cursorBrush = _selectionBoxBrush; + cursorPen = _selectionBoxPen; ReadOnlySpan<char> textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin); - ReadOnlySpan<char> textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd); + ReadOnlySpan<char> textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd); var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont); - var selectionEndRectangle = MeasureString(textUntilEnd , _inputTextFont); + var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont); - cursorVisible = true; - cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X; - cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X; + cursorVisible = true; + cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X; + cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X; } else { cursorTextColor = _textOverCursorColor; - cursorBrush = _cursorBrush; - cursorPen = _cursorPen; + cursorBrush = _cursorBrush; + cursorPen = _cursorPen; if (state.TextBoxBlinkCounter < TextBoxBlinkThreshold) { // Show the blinking cursor. - int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); - ReadOnlySpan<char> textUntilCursor = state.InputText.AsSpan(0, cursorBegin); - var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); + int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); + ReadOnlySpan<char> textUntilCursor = state.InputText.AsSpan(0, cursorBegin); + var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); - cursorVisible = true; + cursorVisible = true; cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; if (state.OverwriteMode) @@ -402,8 +401,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard if (state.CursorBegin < state.InputText.Length) { - textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1); - cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); + textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1); + cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; } else @@ -419,20 +418,19 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard } else { - cursorPositionXLeft = inputTextX; + cursorPositionXLeft = inputTextX; cursorPositionXRight = inputTextX; } } if (state.TypingEnabled && cursorVisible) { - float cursorWidth = cursorPositionXRight - cursorPositionXLeft; + float cursorWidth = cursorPositionXRight - cursorPositionXLeft; float cursorHeight = cursorPositionYBottom - cursorPositionYTop; if (cursorWidth == 0) { - PointF[] points = new PointF[] - { + PointF[] points = { new PointF(cursorPositionXLeft, cursorPositionYTop), new PointF(cursorPositionXLeft, cursorPositionYBottom), }; @@ -443,10 +441,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight); - context.Draw(cursorPen , cursorRectangle); + context.Draw(cursorPen, cursorRectangle); context.Fill(cursorBrush, cursorRectangle); - Image<Argb32> textOverCursor = new Image<Argb32>((int)cursorRectangle.Width, (int)cursorRectangle.Height); + Image<Argb32> textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height); textOverCursor.Mutate(context => { var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y); @@ -470,9 +468,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { // Use relative positions so we can center the the entire drawing later. - float iconX = 0; - float iconY = 0; - float iconWidth = icon.Width; + float iconX = 0; + float iconY = 0; + float iconWidth = icon.Width; float iconHeight = icon.Height; var labelRectangle = MeasureString(label, _labelsTextFont); @@ -480,18 +478,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard float labelPositionX = iconWidth + 8 - labelRectangle.X; float labelPositionY = 3; - float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X; + float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X; float fullHeight = iconHeight; // Convert all relative positions into absolute. - float originX = (int)(point.X - fullWidth / 2); + float originX = (int)(point.X - fullWidth / 2); float originY = (int)(point.Y - fullHeight / 2); iconX += originX; iconY += originY; - var iconPosition = new Point((int)iconX, (int)iconY); + var iconPosition = new Point((int)iconX, (int)iconY); var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY); var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth, @@ -526,7 +524,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard // Use relative positions so we can center the the entire drawing later. - float keyWidth = _keyModeIcon.Width; + float keyWidth = _keyModeIcon.Width; float keyHeight = _keyModeIcon.Height; float labelPositionX = keyWidth + 8 - labelRectangle.X; @@ -535,18 +533,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard float keyX = 0; float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2); - float fullWidth = labelPositionX + labelRectangle.Width; + float fullWidth = labelPositionX + labelRectangle.Width; float fullHeight = Math.Max(labelPositionY + labelRectangle.Height, keyHeight); // Convert all relative positions into absolute. - float originX = (int)(point.X - fullWidth / 2); + float originX = (int)(point.X - fullWidth / 2); float originY = (int)(point.Y - fullHeight / 2); keyX += originX; keyY += originY; - var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY); + var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY); var overlayPosition = new Point((int)keyX, (int)keyY); context.DrawImage(_keyModeIcon, overlayPosition, 1); diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs index 0f66fc9ba..92a943f27 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs @@ -23,6 +23,6 @@ /// <summary> /// swkbd has completed. /// </summary> - Complete + Complete, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs index d67a44094..52fa7ed85 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs @@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Applets public int StringLengthMin; public int StringLengthMax; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs index e6131e62d..aed53d40d 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs @@ -7,15 +7,15 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard /// </summary> internal class SoftwareKeyboardUiState { - public string InputText = ""; - public int CursorBegin = 0; - public int CursorEnd = 0; - public bool AcceptPressed = false; - public bool CancelPressed = false; - public bool OverwriteMode = false; - public bool TypingEnabled = true; - public bool ControllerEnabled = true; - public int TextBoxBlinkCounter = 0; + public string InputText = ""; + public int CursorBegin = 0; + public int CursorEnd = 0; + public bool AcceptPressed = false; + public bool CancelPressed = false; + public bool OverwriteMode = false; + public bool TypingEnabled = true; + public bool ControllerEnabled = true; + public int TextBoxBlinkCounter = 0; public RenderingSurfaceInfo SurfaceInfo = null; } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs index ed876ffd2..f44dc5da0 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs @@ -19,8 +19,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard public SleepSubstepData(int sleepMilliseconds) { - SleepMilliseconds = Math.Min(sleepMilliseconds, MaxThreadSleep); - SleepCount = sleepMilliseconds / SleepMilliseconds; + SleepMilliseconds = Math.Min(sleepMilliseconds, MaxThreadSleep); + SleepCount = sleepMilliseconds / SleepMilliseconds; SleepRemainderMilliseconds = sleepMilliseconds - SleepCount * SleepMilliseconds; } } diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 140b10a26..7ff055a69 100644 --- a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -96,7 +96,7 @@ namespace Ryujinx.HLE.HOS break; default: - throw new ArgumentOutOfRangeException(); + throw new InvalidOperationException($"{nameof(mode)} contains an invalid value: {mode}"); } } diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs index 5145ff7b0..8248b420e 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs @@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class ArraySubscriptingExpression : BaseNode { - private BaseNode _leftNode; - private BaseNode _subscript; + private readonly BaseNode _leftNode; + private readonly BaseNode _subscript; public ArraySubscriptingExpression(BaseNode leftNode, BaseNode subscript) : base(NodeType.ArraySubscriptingExpression) { - _leftNode = leftNode; + _leftNode = leftNode; _subscript = subscript; } @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _leftNode.Print(writer); writer.Write(")["); _subscript.Print(writer); - writer.Write("]"); + writer.Write("]"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs index 4b1041ab7..59fd915a7 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs @@ -4,19 +4,19 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class ArrayType : BaseNode { - private BaseNode _base; - private BaseNode _dimensionExpression; - private string _dimensionString; + private readonly BaseNode _base; + private readonly BaseNode _dimensionExpression; + private readonly string _dimensionString; public ArrayType(BaseNode Base, BaseNode dimensionExpression = null) : base(NodeType.ArrayType) { - _base = Base; + _base = Base; _dimensionExpression = dimensionExpression; } public ArrayType(BaseNode Base, string dimensionString) : base(NodeType.ArrayType) { - _base = Base; + _base = Base; _dimensionString = dimensionString; } @@ -46,9 +46,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { writer.Write(_dimensionString); } - else if (_dimensionExpression != null) + else { - _dimensionExpression.Print(writer); + _dimensionExpression?.Print(writer); } writer.Write("]"); @@ -56,4 +56,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _base.PrintRight(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs index ca4b98f88..fc60fb6e9 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast ConversionOperatorType, LocalName, CtorVtableSpecialName, - ArrayType + ArrayType, } public abstract class BaseNode @@ -99,15 +99,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast return null; } - public virtual void PrintRight(TextWriter writer) {} + public virtual void PrintRight(TextWriter writer) { } public override string ToString() { - StringWriter writer = new StringWriter(); + StringWriter writer = new(); Print(writer); return writer.ToString(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs index 0c492df39..81ea80dbc 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs @@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class BinaryExpression : BaseNode { - private BaseNode _leftPart; - private string _name; - private BaseNode _rightPart; + private readonly BaseNode _leftPart; + private readonly string _name; + private readonly BaseNode _rightPart; public BinaryExpression(BaseNode leftPart, string name, BaseNode rightPart) : base(NodeType.BinaryExpression) { - _leftPart = leftPart; - _name = name; + _leftPart = leftPart; + _name = name; _rightPart = rightPart; } @@ -38,4 +38,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs index 6b9782f5c..53fbb2040 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs @@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class BracedExpression : BaseNode { - private BaseNode _element; - private BaseNode _expression; - private bool _isArrayExpression; + private readonly BaseNode _element; + private readonly BaseNode _expression; + private readonly bool _isArrayExpression; public BracedExpression(BaseNode element, BaseNode expression, bool isArrayExpression) : base(NodeType.BracedExpression) { - _element = element; - _expression = expression; + _element = element; + _expression = expression; _isArrayExpression = isArrayExpression; } diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs index 802422d9a..38d8ccd4d 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs @@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class BracedRangeExpression : BaseNode { - private BaseNode _firstNode; - private BaseNode _lastNode; - private BaseNode _expression; + private readonly BaseNode _firstNode; + private readonly BaseNode _lastNode; + private readonly BaseNode _expression; public BracedRangeExpression(BaseNode firstNode, BaseNode lastNode, BaseNode expression) : base(NodeType.BracedRangeExpression) { - _firstNode = firstNode; - _lastNode = lastNode; + _firstNode = firstNode; + _lastNode = lastNode; _expression = expression; } diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs index 8e3fc3e69..0ee2e9d77 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class CallExpression : NodeArray { - private BaseNode _callee; + private readonly BaseNode _callee; public CallExpression(BaseNode callee, List<BaseNode> nodes) : base(nodes, NodeType.CallExpression) { @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(")"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs index 1149a788c..b35d06dd4 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs @@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class CastExpression : BaseNode { - private string _kind; - private BaseNode _to; - private BaseNode _from; + private readonly string _kind; + private readonly BaseNode _to; + private readonly BaseNode _from; public CastExpression(string kind, BaseNode to, BaseNode from) : base(NodeType.CastExpression) { _kind = kind; - _to = to; + _to = to; _from = from; } @@ -25,4 +25,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(")"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs index c0dd67179..b441a377d 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs @@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class ConditionalExpression : BaseNode { - private BaseNode _thenNode; - private BaseNode _elseNode; - private BaseNode _conditionNode; + private readonly BaseNode _thenNode; + private readonly BaseNode _elseNode; + private readonly BaseNode _conditionNode; public ConditionalExpression(BaseNode conditionNode, BaseNode thenNode, BaseNode elseNode) : base(NodeType.ConditionalExpression) { - _thenNode = thenNode; + _thenNode = thenNode; _conditionNode = conditionNode; - _elseNode = elseNode; + _elseNode = elseNode; } public override void PrintLeft(TextWriter writer) @@ -26,4 +26,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(")"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs index dd1f7a008..d30912f46 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs @@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class ConversionExpression : BaseNode { - private BaseNode _typeNode; - private BaseNode _expressions; + private readonly BaseNode _typeNode; + private readonly BaseNode _expressions; public ConversionExpression(BaseNode typeNode, BaseNode expressions) : base(NodeType.ConversionExpression) { - _typeNode = typeNode; + _typeNode = typeNode; _expressions = expressions; } @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _expressions.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs index 8a5cde860..814b4a2fe 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs @@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast Child.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs index 5f4581235..068662a66 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class CtorDtorNameType : ParentNode { - private bool _isDestructor; + private readonly bool _isDestructor; public CtorDtorNameType(BaseNode name, bool isDestructor) : base(NodeType.CtorDtorNameType, name) { @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(Child.GetName()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs index 3bb5b1631..924969565 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs @@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class CtorVtableSpecialName : BaseNode { - private BaseNode _firstType; - private BaseNode _secondType; + private readonly BaseNode _firstType; + private readonly BaseNode _secondType; public CtorVtableSpecialName(BaseNode firstType, BaseNode secondType) : base(NodeType.CtorVtableSpecialName) { - _firstType = firstType; + _firstType = firstType; _secondType = secondType; } @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _secondType.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs index 14715d25e..20d0bd8a2 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs @@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class DeleteExpression : ParentNode { - private bool _isGlobal; - private bool _isArrayExpression; + private readonly bool _isGlobal; + private readonly bool _isArrayExpression; public DeleteExpression(BaseNode child, bool isGlobal, bool isArrayExpression) : base(NodeType.DeleteExpression, child) { - _isGlobal = isGlobal; + _isGlobal = isGlobal; _isArrayExpression = isArrayExpression; } @@ -30,4 +30,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast Child.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs index 5cc4e6cfb..b584dba93 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs @@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast Child.PrintLeft(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs index faa91443a..98619b20b 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs @@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(")"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs index 086cd3dc7..2877557d1 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class ElaboratedType : ParentNode { - private string _elaborated; + private readonly string _elaborated; public ElaboratedType(string elaborated, BaseNode type) : base(NodeType.ElaboratedType, type) { @@ -18,4 +18,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast Child.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs index b45481ddc..178db5129 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs @@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class EnclosedExpression : BaseNode { - private string _prefix; - private BaseNode _expression; - private string _postfix; + private readonly string _prefix; + private readonly BaseNode _expression; + private readonly string _postfix; public EnclosedExpression(string prefix, BaseNode expression, string postfix) : base(NodeType.EnclosedExpression) { - _prefix = prefix; + _prefix = prefix; _expression = expression; - _postfix = postfix; + _postfix = postfix; } public override void PrintLeft(TextWriter writer) @@ -22,4 +22,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(_postfix); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs index c7b6dab1a..de5598044 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs @@ -4,21 +4,21 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class EncodedFunction : BaseNode { - private BaseNode _name; - private BaseNode _params; - private BaseNode _cv; - private BaseNode _ref; - private BaseNode _attrs; - private BaseNode _ret; + private readonly BaseNode _name; + private readonly BaseNode _params; + private readonly BaseNode _cv; + private readonly BaseNode _ref; + private readonly BaseNode _attrs; + private readonly BaseNode _ret; public EncodedFunction(BaseNode name, BaseNode Params, BaseNode cv, BaseNode Ref, BaseNode attrs, BaseNode ret) : base(NodeType.NameType) { - _name = name; + _name = name; _params = Params; - _cv = cv; - _ref = Ref; - _attrs = attrs; - _ret = ret; + _cv = cv; + _ref = Ref; + _attrs = attrs; + _ret = ret; } public override void PrintLeft(TextWriter writer) @@ -45,33 +45,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast public override void PrintRight(TextWriter writer) { writer.Write("("); - - if (_params != null) - { - _params.Print(writer); - } - + _params?.Print(writer); writer.Write(")"); - - if (_ret != null) - { - _ret.PrintRight(writer); - } - - if (_cv != null) - { - _cv.Print(writer); - } - - if (_ref != null) - { - _ref.Print(writer); - } - - if (_attrs != null) - { - _attrs.Print(writer); - } + _ret?.PrintRight(writer); + _cv?.Print(writer); + _ref?.Print(writer); + _attrs?.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs index 04f7053e3..ed73fa09a 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs @@ -4,17 +4,17 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class FoldExpression : BaseNode { - private bool _isLeftFold; - private string _operatorName; - private BaseNode _expression; - private BaseNode _initializer; + private readonly bool _isLeftFold; + private readonly string _operatorName; + private readonly BaseNode _expression; + private readonly BaseNode _initializer; public FoldExpression(bool isLeftFold, string operatorName, BaseNode expression, BaseNode initializer) : base(NodeType.FunctionParameter) { - _isLeftFold = isLeftFold; + _isLeftFold = isLeftFold; _operatorName = operatorName; - _expression = expression; - _initializer = initializer; + _expression = expression; + _initializer = initializer; } public override void PrintLeft(TextWriter writer) @@ -45,4 +45,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(")"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs index 1bbf6ef9a..30c838ca0 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs @@ -6,7 +6,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { // TODO: Compute inside the Demangler public BaseNode Reference; - private int _index; +#pragma warning disable IDE0052 // Remove unread private member + private readonly int _index; +#pragma warning restore IDE0052 public ForwardTemplateReference(int index) : base(NodeType.ForwardTemplateReference) { diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs index 5654a048f..9c2d0955f 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class FunctionParameter : BaseNode { - private string _number; + private readonly string _number; public FunctionParameter(string number) : base(NodeType.FunctionParameter) { @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs index 4ad0c9f5f..431c21119 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs @@ -4,19 +4,19 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class FunctionType : BaseNode { - private BaseNode _returnType; - private BaseNode _params; - private BaseNode _cvQualifier; - private SimpleReferenceType _referenceQualifier; - private BaseNode _exceptionSpec; + private readonly BaseNode _returnType; + private readonly BaseNode _params; + private readonly BaseNode _cvQualifier; + private readonly SimpleReferenceType _referenceQualifier; + private readonly BaseNode _exceptionSpec; public FunctionType(BaseNode returnType, BaseNode Params, BaseNode cvQualifier, SimpleReferenceType referenceQualifier, BaseNode exceptionSpec) : base(NodeType.FunctionType) { - _returnType = returnType; - _params = Params; - _cvQualifier = cvQualifier; + _returnType = returnType; + _params = Params; + _cvQualifier = cvQualifier; _referenceQualifier = referenceQualifier; - _exceptionSpec = exceptionSpec; + _exceptionSpec = exceptionSpec; } public override void PrintLeft(TextWriter writer) @@ -58,4 +58,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs index 7155dd601..908319c31 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs @@ -5,25 +5,22 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class InitListExpression : BaseNode { - private BaseNode _typeNode; - private List<BaseNode> _nodes; + private readonly BaseNode _typeNode; + private readonly List<BaseNode> _nodes; public InitListExpression(BaseNode typeNode, List<BaseNode> nodes) : base(NodeType.InitListExpression) { _typeNode = typeNode; - _nodes = nodes; + _nodes = nodes; } public override void PrintLeft(TextWriter writer) { - if (_typeNode != null) - { - _typeNode.Print(writer); - } + _typeNode?.Print(writer); writer.Write("{"); writer.Write(string.Join<BaseNode>(", ", _nodes.ToArray())); writer.Write("}"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs index ef07414de..eed36b218 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class IntegerCastExpression : ParentNode { - private string _number; + private readonly string _number; public IntegerCastExpression(BaseNode type, string number) : base(NodeType.IntegerCastExpression, type) { @@ -19,4 +19,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(_number); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs index 33752d00c..762ef7aea 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs @@ -5,13 +5,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class IntegerLiteral : BaseNode { - private string _literalName; - private string _literalValue; + private readonly string _literalName; + private readonly string _literalValue; public IntegerLiteral(string literalName, string literalValue) : base(NodeType.IntegerLiteral) { _literalValue = literalValue; - _literalName = literalName; + _literalName = literalName; } public override void PrintLeft(TextWriter writer) @@ -39,4 +39,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs index f7e86c9e2..f2c934afa 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs @@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write("\""); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs index 15d46b387..661043b44 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs @@ -4,13 +4,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class LocalName : BaseNode { - private BaseNode _encoding; - private BaseNode _entity; + private readonly BaseNode _encoding; + private readonly BaseNode _entity; public LocalName(BaseNode encoding, BaseNode entity) : base(NodeType.LocalName) { _encoding = encoding; - _entity = entity; + _entity = entity; } public override void PrintLeft(TextWriter writer) @@ -20,4 +20,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _entity.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs index 9b91f6f5d..6617ec208 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs @@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class MemberExpression : BaseNode { - private BaseNode _leftNode; - private string _kind; - private BaseNode _rightNode; + private readonly BaseNode _leftNode; + private readonly string _kind; + private readonly BaseNode _rightNode; public MemberExpression(BaseNode leftNode, string kind, BaseNode rightNode) : base(NodeType.MemberExpression) { - _leftNode = leftNode; - _kind = kind; + _leftNode = leftNode; + _kind = kind; _rightNode = rightNode; } @@ -22,4 +22,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _rightNode.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs index f9f4cb20e..5ea21a2b7 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class NameType : BaseNode { - private string _nameValue; + private readonly string _nameValue; public NameType(string nameValue, NodeType type) : base(type) { @@ -26,4 +26,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(_nameValue); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs index ee725f36b..9512926a9 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs @@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class NameTypeWithTemplateArguments : BaseNode { - private BaseNode _prev; - private BaseNode _templateArgument; + private readonly BaseNode _prev; + private readonly BaseNode _templateArgument; public NameTypeWithTemplateArguments(BaseNode prev, BaseNode templateArgument) : base(NodeType.NameTypeWithTemplateArguments) { - _prev = prev; + _prev = prev; _templateArgument = templateArgument; } @@ -17,11 +17,11 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { return _prev.GetName(); } - + public override void PrintLeft(TextWriter writer) { _prev.Print(writer); _templateArgument.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs index 640c200cb..cadea66c6 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class NestedName : ParentNode { - private BaseNode _name; + private readonly BaseNode _name; public NestedName(BaseNode name, BaseNode type) : base(NodeType.NestedName, type) { @@ -23,4 +23,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _name.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs index ba4690af4..4c321afd3 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs @@ -4,20 +4,20 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class NewExpression : BaseNode { - private NodeArray _expressions; - private BaseNode _typeNode; - private NodeArray _initializers; + private readonly NodeArray _expressions; + private readonly BaseNode _typeNode; + private readonly NodeArray _initializers; - private bool _isGlobal; - private bool _isArrayExpression; + private readonly bool _isGlobal; + private readonly bool _isArrayExpression; public NewExpression(NodeArray expressions, BaseNode typeNode, NodeArray initializers, bool isGlobal, bool isArrayExpression) : base(NodeType.NewExpression) { - _expressions = expressions; - _typeNode = typeNode; - _initializers = initializers; + _expressions = expressions; + _typeNode = typeNode; + _initializers = initializers; - _isGlobal = isGlobal; + _isGlobal = isGlobal; _isArrayExpression = isArrayExpression; } @@ -52,4 +52,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs index 1482dfc37..395ad1a05 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs @@ -27,4 +27,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(string.Join<BaseNode>(", ", Nodes.ToArray())); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs index 4c8200958..23104fff0 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs @@ -36,4 +36,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs index c3645044a..45e4084a4 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs @@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class PackedTemplateParameterExpansion : ParentNode { - public PackedTemplateParameterExpansion(BaseNode child) : base(NodeType.PackedTemplateParameterExpansion, child) {} + public PackedTemplateParameterExpansion(BaseNode child) : base(NodeType.PackedTemplateParameterExpansion, child) { } public override void PrintLeft(TextWriter writer) { - if (Child is PackedTemplateParameter) + if (Child is PackedTemplateParameter parameter) { - if (((PackedTemplateParameter)Child).Nodes.Count != 0) + if (parameter.Nodes.Count != 0) { - Child.Print(writer); + parameter.Print(writer); } } else @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs index 786abced8..848727db2 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs @@ -14,4 +14,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast return Child.GetName(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs index b1a3ec422..f331e8331 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class PointerType : BaseNode { - private BaseNode _child; + private readonly BaseNode _child; public PointerType(BaseNode child) : base(NodeType.PointerType) { @@ -42,4 +42,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _child.PrintRight(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs index ccaea3ba7..dfbe11ae3 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class PostfixExpression : ParentNode { - private string _operator; + private readonly string _operator; public PostfixExpression(BaseNode type, string Operator) : base(NodeType.PostfixExpression, type) { @@ -19,4 +19,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(_operator); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs index 5024a8f99..ec1dd142f 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class PostfixQualifiedType : ParentNode { - private string _postfixQualifier; + private readonly string _postfixQualifier; public PostfixQualifiedType(string postfixQualifier, BaseNode type) : base(NodeType.PostfixQualifiedType, type) { @@ -17,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(_postfixQualifier); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs index 9c3d4552a..309c575d9 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class PrefixExpression : ParentNode { - private string _prefix; + private readonly string _prefix; public PrefixExpression(string prefix, BaseNode child) : base(NodeType.PrefixExpression, child) { @@ -19,4 +19,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast writer.Write(")"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs index 2e18f564e..ae43f0418 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs @@ -4,13 +4,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class QualifiedName : BaseNode { - private BaseNode _qualifier; - private BaseNode _name; + private readonly BaseNode _qualifier; + private readonly BaseNode _name; public QualifiedName(BaseNode qualifier, BaseNode name) : base(NodeType.QualifiedName) { _qualifier = qualifier; - _name = name; + _name = name; } public override void PrintLeft(TextWriter writer) @@ -20,4 +20,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _name.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs index cb6dd6bf6..e926c3443 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs @@ -7,14 +7,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast None, Const, Volatile, - Restricted = 4 + Restricted = 4, } public enum Reference { None, RValue, - LValue + LValue, } public class CvType : ParentNode @@ -46,10 +46,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast public override void PrintLeft(TextWriter writer) { - if (Child != null) - { - Child.PrintLeft(writer); - } + Child?.PrintLeft(writer); PrintQualifier(writer); } @@ -61,10 +58,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast public override void PrintRight(TextWriter writer) { - if (Child != null) - { - Child.PrintRight(writer); - } + Child?.PrintRight(writer); } } @@ -111,10 +105,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast public override void PrintRight(TextWriter writer) { - if (Child != null) - { - Child.PrintRight(writer); - } + Child?.PrintRight(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs index a3214171f..22a601c91 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs @@ -4,13 +4,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class ReferenceType : BaseNode { - private string _reference; - private BaseNode _child; + private readonly string _reference; + private readonly BaseNode _child; public ReferenceType(string reference, BaseNode child) : base(NodeType.ReferenceType) { _reference = reference; - _child = child; + _child = child; } public override bool HasRightPart() @@ -44,4 +44,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _child.PrintRight(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs index 1447458b3..8a968d63d 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class SpecialName : ParentNode { - private string _specialValue; + private readonly string _specialValue; public SpecialName(string specialValue, BaseNode type) : base(NodeType.SpecialName, type) { @@ -17,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast Child.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs index 8d45e180b..793d94989 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast IOStream } - private SpecialType _specialSubstitutionKey; + private readonly SpecialType _specialSubstitutionKey; public SpecialSubstitution(SpecialType specialSubstitutionKey) : base(NodeType.SpecialSubstitution) { @@ -54,23 +54,16 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast private string GetExtendedName() { - switch (_specialSubstitutionKey) + return _specialSubstitutionKey switch { - case SpecialType.Allocator: - return "std::allocator"; - case SpecialType.BasicString: - return "std::basic_string"; - case SpecialType.String: - return "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"; - case SpecialType.IStream: - return "std::basic_istream<char, std::char_traits<char> >"; - case SpecialType.OStream: - return "std::basic_ostream<char, std::char_traits<char> >"; - case SpecialType.IOStream: - return "std::basic_iostream<char, std::char_traits<char> >"; - } - - return null; + SpecialType.Allocator => "std::allocator", + SpecialType.BasicString => "std::basic_string", + SpecialType.String => "std::basic_string<char, std::char_traits<char>, std::allocator<char> >", + SpecialType.IStream => "std::basic_istream<char, std::char_traits<char> >", + SpecialType.OStream => "std::basic_ostream<char, std::char_traits<char> >", + SpecialType.IOStream => "std::basic_iostream<char, std::char_traits<char> >", + _ => null, + }; } public override void PrintLeft(TextWriter writer) @@ -86,4 +79,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs index cc14d9645..d9f25ac66 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs @@ -9,13 +9,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast public override void PrintLeft(TextWriter writer) { - string Params = string.Join<BaseNode>(", ", Nodes.ToArray()); + string paramsString = string.Join<BaseNode>(", ", Nodes.ToArray()); writer.Write("<"); - writer.Write(Params); + writer.Write(paramsString); - if (Params.EndsWith('>')) + if (paramsString.EndsWith('>')) { writer.Write(" "); } diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs index 2972a31c2..4edff138b 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class ThrowExpression : BaseNode { - private BaseNode _expression; + private readonly BaseNode _expression; public ThrowExpression(BaseNode expression) : base(NodeType.ThrowExpression) { @@ -17,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast _expression.Print(writer); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs index 1bfd7ac07..ba2d67fca 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -8,25 +8,25 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler { class Demangler { - private static readonly string Base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; - private List<BaseNode> _substitutionList = new List<BaseNode>(); - private List<BaseNode> _templateParamList = new List<BaseNode>(); + private static readonly string _base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; + private readonly List<BaseNode> _substitutionList = new(); + private List<BaseNode> _templateParamList = new(); - private List<ForwardTemplateReference> _forwardTemplateReferenceList = new List<ForwardTemplateReference>(); + private readonly List<ForwardTemplateReference> _forwardTemplateReferenceList = new(); public string Mangled { get; private set; } private int _position; - private int _length; + private readonly int _length; private bool _canForwardTemplateReference; private bool _canParseTemplateArgs; public Demangler(string mangled) { - Mangled = mangled; - _position = 0; - _length = mangled.Length; + Mangled = mangled; + _position = 0; + _length = mangled.Length; _canParseTemplateArgs = true; } @@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler for (int i = 0; i < reversedEncoded.Length; i++) { - int value = Base36.IndexOf(reversedEncoded[i]); + int value = _base36.IndexOf(reversedEncoded[i]); if (value == -1) { return -1; @@ -101,8 +101,8 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private int ParseSeqId() { - ReadOnlySpan<char> part = Mangled.AsSpan(_position); - int seqIdLen = 0; + ReadOnlySpan<char> part = Mangled.AsSpan(_position); + int seqIdLen = 0; for (; seqIdLen < part.Length; seqIdLen++) { @@ -274,7 +274,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler } else if (ConsumeIf("Dw")) { - List<BaseNode> types = new List<BaseNode>(); + List<BaseNode> types = new(); while (!ConsumeIf("E")) { @@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler } Reference referenceQualifier = Reference.None; - List<BaseNode> Params = new List<BaseNode>(); + List<BaseNode> paramsList = new(); while (true) { @@ -339,10 +339,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; } - Params.Add(type); + paramsList.Add(type); } - return new FunctionType(returnType, new NodeArray(Params), new CvType(cvQualifiers, null), new SimpleReferenceType(referenceQualifier, null), exceptionSpec); + return new FunctionType(returnType, new NodeArray(paramsList), new CvType(cvQualifiers, null), new SimpleReferenceType(referenceQualifier, null), exceptionSpec); } // <array-type> ::= A <positive dimension number> _ <element type> @@ -416,12 +416,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private BaseNode ParseType(NameParserContext context = null) { // Temporary context - if (context == null) - { - context = new NameParserContext(); - } + context ??= new NameParserContext(); - BaseNode result = null; + BaseNode result; switch (Peek()) { case 'r': @@ -545,8 +542,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler case 'h': _position += 2; // FIXME: GNU c++flit returns this but that is not what is supposed to be returned. - return new NameType("half"); - // return new NameType("decimal16"); + return new NameType("half"); // return new NameType("decimal16"); case 'i': _position += 2; return new NameType("char32_t"); @@ -559,8 +555,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler case 'n': _position += 2; // FIXME: GNU c++flit returns this but that is not what is supposed to be returned. - return new NameType("decltype(nullptr)"); - // return new NameType("std::nullptr_t"); + return new NameType("decltype(nullptr)"); // return new NameType("std::nullptr_t"); case 't': case 'T': _position += 2; @@ -882,7 +877,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return new SimpleReferenceType(result, null); } - private BaseNode CreateNameNode(BaseNode prev, BaseNode name, NameParserContext context) + private static BaseNode CreateNameNode(BaseNode prev, BaseNode name, NameParserContext context) { BaseNode result = name; if (prev != null) @@ -900,8 +895,8 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private int ParsePositiveNumber() { - ReadOnlySpan<char> part = Mangled.AsSpan(_position); - int numberLength = 0; + ReadOnlySpan<char> part = Mangled.AsSpan(_position); + int numberLength = 0; for (; numberLength < part.Length; numberLength++) { @@ -933,8 +928,8 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; } - ReadOnlySpan<char> part = Mangled.AsSpan(_position); - int numberLength = 0; + ReadOnlySpan<char> part = Mangled.AsSpan(_position); + int numberLength = 0; for (; numberLength < part.Length; numberLength++) { @@ -1057,15 +1052,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler case 'v': _position += 2; - bool canParseTemplateArgsBackup = _canParseTemplateArgs; + bool canParseTemplateArgsBackup = _canParseTemplateArgs; bool canForwardTemplateReferenceBackup = _canForwardTemplateReference; - _canParseTemplateArgs = false; + _canParseTemplateArgs = false; _canForwardTemplateReference = canForwardTemplateReferenceBackup || context != null; BaseNode type = ParseType(); - _canParseTemplateArgs = canParseTemplateArgsBackup; + _canParseTemplateArgs = canParseTemplateArgsBackup; _canForwardTemplateReference = canForwardTemplateReferenceBackup; if (type == null) @@ -1324,17 +1319,17 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler // ::= C3 # complete object allocating constructor // ::= D0 # deleting destructor // ::= D1 # complete object destructor - // ::= D2 # base object destructor + // ::= D2 # base object destructor private BaseNode ParseCtorDtorName(NameParserContext context, BaseNode prev) { - if (prev.Type == NodeType.SpecialSubstitution && prev is SpecialSubstitution) + if (prev.Type == NodeType.SpecialSubstitution && prev is SpecialSubstitution substitution) { - ((SpecialSubstitution)prev).SetExtended(); + substitution.SetExtended(); } if (ConsumeIf("C")) { - bool isInherited = ConsumeIf("I"); + bool isInherited = ConsumeIf("I"); char ctorDtorType = Peek(); if (ctorDtorType != '1' && ctorDtorType != '2' && ctorDtorType != '3') @@ -1434,9 +1429,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; } - char foldKind = Peek(); + char foldKind = Peek(); bool hasInitializer = foldKind == 'L' || foldKind == 'R'; - bool isLeftFold = foldKind == 'l' || foldKind == 'L'; + bool isLeftFold = foldKind == 'l' || foldKind == 'L'; if (!isLeftFold && !(foldKind == 'r' || foldKind == 'R')) { @@ -1445,7 +1440,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler _position++; - string operatorName = null; + string operatorName; switch (PeekString(0, 2)) { @@ -1567,9 +1562,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler if (isLeftFold && initializer != null) { - BaseNode temp = expression; - expression = initializer; - initializer = temp; + (initializer, expression) = (expression, initializer); } return new FoldExpression(isLeftFold, operatorName, new PackedTemplateParameterExpansion(expression), initializer); @@ -1586,16 +1579,16 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler } bool canParseTemplateArgsBackup = _canParseTemplateArgs; - _canParseTemplateArgs = false; - BaseNode type = ParseType(); - _canParseTemplateArgs = canParseTemplateArgsBackup; + _canParseTemplateArgs = false; + BaseNode type = ParseType(); + _canParseTemplateArgs = canParseTemplateArgsBackup; if (type == null) { return null; } - List<BaseNode> expressions = new List<BaseNode>(); + List<BaseNode> expressions = new(); if (ConsumeIf("_")) { while (!ConsumeIf("E")) @@ -1730,15 +1723,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private BaseNode ParseNewExpression() { bool isGlobal = ConsumeIf("gs"); - bool isArray = Peek(1) == 'a'; + bool isArray = Peek(1) == 'a'; if (!ConsumeIf("nw") || !ConsumeIf("na")) { return null; } - List<BaseNode> expressions = new List<BaseNode>(); - List<BaseNode> initializers = new List<BaseNode>(); + List<BaseNode> expressions = new(); + List<BaseNode> initializers = new(); while (!ConsumeIf("_")) { @@ -1824,7 +1817,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler private BaseNode ParseExpression() { bool isGlobal = ConsumeIf("gs"); - BaseNode expression = null; + BaseNode expression; if (Count() < 2) { return null; @@ -1906,7 +1899,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; } - List<BaseNode> names = new List<BaseNode>(); + List<BaseNode> names = new(); while (!ConsumeIf("E")) { expression = ParseExpression(); @@ -1929,8 +1922,8 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler } return null; case 'd': - BaseNode leftNode = null; - BaseNode rightNode = null; + BaseNode leftNode; + BaseNode rightNode; switch (Peek(1)) { case 'a': @@ -2055,7 +2048,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler case 'l': _position += 2; - List<BaseNode> bracedExpressions = new List<BaseNode>(); + List<BaseNode> bracedExpressions = new(); while (!ConsumeIf("E")) { expression = ParseBracedExpression(); @@ -2310,7 +2303,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return new EnclosedExpression("sizeof (", expression, ")"); case 'Z': _position += 2; - BaseNode sizeofParamNode = null; + BaseNode sizeofParamNode; switch (Peek()) { case 'T': @@ -2334,7 +2327,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; case 'P': _position += 2; - List<BaseNode> arguments = new List<BaseNode>(); + List<BaseNode> arguments = new(); while (!ConsumeIf("E")) { BaseNode argument = ParseTemplateArgument(); @@ -2375,7 +2368,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; } - List<BaseNode> bracedExpressions = new List<BaseNode>(); + List<BaseNode> bracedExpressions = new(); while (!ConsumeIf("E")) { expression = ParseBracedExpression(); @@ -2582,7 +2575,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler if (_canForwardTemplateReference) { - ForwardTemplateReference forwardTemplateReference = new ForwardTemplateReference(index); + ForwardTemplateReference forwardTemplateReference = new(index); _forwardTemplateReferenceList.Add(forwardTemplateReference); return forwardTemplateReference; } @@ -2607,12 +2600,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler _templateParamList.Clear(); } - List<BaseNode> args = new List<BaseNode>(); + List<BaseNode> args = new(); while (!ConsumeIf("E")) { if (hasContext) { - List<BaseNode> templateParamListTemp = new List<BaseNode>(_templateParamList); + List<BaseNode> templateParamListTemp = new(_templateParamList); BaseNode templateArgument = ParseTemplateArgument(); _templateParamList = templateParamListTemp; if (templateArgument == null) @@ -2666,7 +2659,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler // J <template-arg>* E case 'J': _position++; - List<BaseNode> templateArguments = new List<BaseNode>(); + List<BaseNode> templateArguments = new(); while (!ConsumeIf("E")) { BaseNode templateArgument = ParseTemplateArgument(); @@ -2976,7 +2969,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler } BaseNode result = null; - CvType cv = new CvType(ParseCvQualifiers(), null); + CvType cv = new(ParseCvQualifiers(), null); if (context != null) { context.Cv = cv; @@ -3269,7 +3262,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler // ::= <special-name> private BaseNode ParseEncoding() { - NameParserContext context = new NameParserContext(); + NameParserContext context = new(); if (Peek() == 'T' || (Peek() == 'G' && Peek(1) == 'V')) { return ParseSpecialName(context); @@ -3305,7 +3298,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return new EncodedFunction(name, null, context.Cv, context.Ref, null, returnType); } - List<BaseNode> Params = new List<BaseNode>(); + List<BaseNode> paramsList = new(); // backup because that can be destroyed by parseType CvType cv = context.Cv; @@ -3319,10 +3312,10 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return null; } - Params.Add(param); + paramsList.Add(param); } - return new EncodedFunction(name, new NodeArray(Params), cv, Ref, null, returnType); + return new EncodedFunction(name, new NodeArray(paramsList), cv, Ref, null, returnType); } // <mangled-name> ::= _Z <encoding> @@ -3351,12 +3344,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler public static string Parse(string originalMangled) { - Demangler instance = new Demangler(originalMangled); - BaseNode resNode = instance.Parse(); + Demangler instance = new(originalMangled); + BaseNode resNode = instance.Parse(); if (resNode != null) { - StringWriter writer = new StringWriter(); + StringWriter writer = new(); resNode.Print(writer); return writer.ToString(); } diff --git a/src/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs b/src/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs index 59bc881f0..2cba640b8 100644 --- a/src/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs +++ b/src/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs @@ -5,12 +5,12 @@ namespace Ryujinx.HLE.HOS { class HomebrewRomFsStream : Stream { - private Stream _baseStream; - private long _positionOffset; + private readonly Stream _baseStream; + private readonly long _positionOffset; public HomebrewRomFsStream(Stream baseStream, long positionOffset) { - _baseStream = baseStream; + _baseStream = baseStream; _positionOffset = positionOffset; _baseStream.Position = _positionOffset; diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 2f163fa22..f65d357e7 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -10,7 +10,6 @@ using Ryujinx.Audio.Integration; using Ryujinx.Audio.Output; using Ryujinx.Audio.Renderer.Device; using Ryujinx.Audio.Renderer.Server; -using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Kernel; @@ -42,7 +41,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using TimeSpanType = Ryujinx.HLE.HOS.Services.Time.Clock.TimeSpanType; namespace Ryujinx.HLE.HOS { @@ -50,10 +48,10 @@ namespace Ryujinx.HLE.HOS public class Horizon : IDisposable { - internal const int HidSize = 0x40000; - internal const int FontSize = 0x1100000; - internal const int IirsSize = 0x8000; - internal const int TimeSize = 0x1000; + internal const int HidSize = 0x40000; + internal const int FontSize = 0x1100000; + internal const int IirsSize = 0x8000; + internal const int TimeSize = 0x1000; internal const int AppletCaptureBufferSize = 0x384000; internal KernelContext KernelContext { get; } @@ -91,16 +89,16 @@ namespace Ryujinx.HLE.HOS internal ServerBase ViServerM { get; private set; } internal ServerBase ViServerS { get; private set; } - internal KSharedMemory HidSharedMem { get; private set; } + internal KSharedMemory HidSharedMem { get; private set; } internal KSharedMemory FontSharedMem { get; private set; } internal KSharedMemory IirsSharedMem { get; private set; } internal KTransferMemory AppletCaptureBufferTransfer { get; private set; } internal SharedFontManager SharedFontManager { get; private set; } - internal AccountManager AccountManager { get; private set; } - internal ContentManager ContentManager { get; private set; } - internal CaptureManager CaptureManager { get; private set; } + internal AccountManager AccountManager { get; private set; } + internal ContentManager ContentManager { get; private set; } + internal CaptureManager CaptureManager { get; private set; } internal KEvent VsyncEvent { get; private set; } @@ -149,17 +147,17 @@ namespace Ryujinx.HLE.HOS // region used that is used is Application, so we can use the other ones for anything. KMemoryRegionManager region = KernelContext.MemoryManager.MemoryRegions[(int)MemoryRegion.NvServices]; - ulong hidPa = region.Address; - ulong fontPa = region.Address + HidSize; - ulong iirsPa = region.Address + HidSize + FontSize; - ulong timePa = region.Address + HidSize + FontSize + IirsSize; + ulong hidPa = region.Address; + ulong fontPa = region.Address + HidSize; + ulong iirsPa = region.Address + HidSize + FontSize; + ulong timePa = region.Address + HidSize + FontSize + IirsSize; ulong appletCaptureBufferPa = region.Address + HidSize + FontSize + IirsSize + TimeSize; - KPageList hidPageList = new KPageList(); - KPageList fontPageList = new KPageList(); - KPageList iirsPageList = new KPageList(); - KPageList timePageList = new KPageList(); - KPageList appletCaptureBufferPageList = new KPageList(); + KPageList hidPageList = new(); + KPageList fontPageList = new(); + KPageList iirsPageList = new(); + KPageList timePageList = new(); + KPageList appletCaptureBufferPageList = new(); hidPageList.AddRange(hidPa, HidSize / KPageTableBase.PageSize); fontPageList.AddRange(fontPa, FontSize / KPageTableBase.PageSize); @@ -175,11 +173,11 @@ namespace Ryujinx.HLE.HOS HidStorage = hidStorage; - HidSharedMem = new KSharedMemory(KernelContext, hidStorage, 0, 0, KMemoryPermission.Read); + HidSharedMem = new KSharedMemory(KernelContext, hidStorage, 0, 0, KMemoryPermission.Read); FontSharedMem = new KSharedMemory(KernelContext, fontStorage, 0, 0, KMemoryPermission.Read); IirsSharedMem = new KSharedMemory(KernelContext, iirsStorage, 0, 0, KMemoryPermission.Read); - KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timeStorage, 0, 0, KMemoryPermission.Read); + KSharedMemory timeSharedMemory = new(KernelContext, timeStorage, 0, 0, KMemoryPermission.Read); TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timeStorage, TimeSize); @@ -194,16 +192,16 @@ namespace Ryujinx.HLE.HOS DisplayResolutionChangeEvent = new KEvent(KernelContext); SharedFontManager = new SharedFontManager(device, fontStorage); - AccountManager = device.Configuration.AccountManager; - ContentManager = device.Configuration.ContentManager; - CaptureManager = new CaptureManager(device); + AccountManager = device.Configuration.AccountManager; + ContentManager = device.Configuration.ContentManager; + CaptureManager = new CaptureManager(device); LibHacHorizonManager = device.Configuration.LibHacHorizonManager; // We hardcode a clock source id to avoid it changing between each start. // TODO: use set:sys (and get external clock source id from settings) // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. - UInt128 clockSourceId = new UInt128(0x36a0328702ce8bc1, 0x1608eaba02333284); + UInt128 clockSourceId = new(0x36a0328702ce8bc1, 0x1608eaba02333284); IRtcManager.GetExternalRtcValue(out ulong rtcValue); // We assume the rtc is system time. @@ -212,7 +210,7 @@ namespace Ryujinx.HLE.HOS // Configure and setup internal offset TimeSpanType internalOffset = TimeSpanType.FromSeconds(device.Configuration.SystemTimeOffset); - TimeSpanType systemTimeOffset = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds); + TimeSpanType systemTimeOffset = new(systemTime.NanoSeconds + internalOffset.NanoSeconds); if (systemTime.IsDaylightSavingTime() && !systemTimeOffset.IsDaylightSavingTime()) { @@ -232,7 +230,7 @@ namespace Ryujinx.HLE.HOS if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes)) { - TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000); + TimeSpanType standardNetworkClockSufficientAccuracy = new((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000); // The network system clock needs a valid system clock, as such we setup this system clock using the local system clock. TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy); @@ -267,7 +265,7 @@ namespace Ryujinx.HLE.HOS for (int i = 0; i < audioOutputRegisterBufferEvents.Length; i++) { - KEvent registerBufferEvent = new KEvent(KernelContext); + KEvent registerBufferEvent = new(KernelContext); audioOutputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent); } @@ -279,7 +277,7 @@ namespace Ryujinx.HLE.HOS for (int i = 0; i < audioInputRegisterBufferEvents.Length; i++) { - KEvent registerBufferEvent = new KEvent(KernelContext); + KEvent registerBufferEvent = new(KernelContext); audioInputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent); } @@ -290,7 +288,7 @@ namespace Ryujinx.HLE.HOS for (int i = 0; i < systemEvents.Length; i++) { - KEvent systemEvent = new KEvent(KernelContext); + KEvent systemEvent = new(KernelContext); systemEvents[i] = new AudioKernelEvent(systemEvent); } @@ -338,16 +336,15 @@ namespace Ryujinx.HLE.HOS ProcessCreationFlags.Is64Bit | ProcessCreationFlags.PoolPartitionSystem; - ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); + ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); - uint[] defaultCapabilities = new uint[] - { + uint[] defaultCapabilities = { 0x030363F7, 0x1FFFFFCF, 0x207FFFEF, 0x47E0060F, 0x0048BFFF, - 0x01007FFF + 0x01007FFF, }; // TODO: @@ -445,6 +442,7 @@ namespace Ryujinx.HLE.HOS public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } @@ -464,8 +462,8 @@ namespace Ryujinx.HLE.HOS AudioRendererManager.StopSendingCommands(); } - KProcess terminationProcess = new KProcess(KernelContext); - KThread terminationThread = new KThread(KernelContext); + KProcess terminationProcess = new(KernelContext); + KThread terminationThread = new(KernelContext); terminationThread.Initialize(0, 0, 0, 3, 0, terminationProcess, ThreadType.Kernel, () => { diff --git a/src/Ryujinx.HLE/HOS/IdDictionary.cs b/src/Ryujinx.HLE/HOS/IdDictionary.cs index 5ae720ea3..56ffcd0c3 100644 --- a/src/Ryujinx.HLE/HOS/IdDictionary.cs +++ b/src/Ryujinx.HLE/HOS/IdDictionary.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS { class IdDictionary { - private ConcurrentDictionary<int, object> _objs; + private readonly ConcurrentDictionary<int, object> _objs; public ICollection<object> Values => _objs.Values; @@ -45,12 +45,12 @@ namespace Ryujinx.HLE.HOS public T GetData<T>(int id) { - if (_objs.TryGetValue(id, out object data) && data is T) + if (_objs.TryGetValue(id, out object dataObject) && dataObject is T data) { - return (T)data; + return data; } - return default(T); + return default; } public object Delete(int id) @@ -72,4 +72,4 @@ namespace Ryujinx.HLE.HOS return values; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs index b61d56973..aa9174354 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Ipc struct IpcBuffDesc { public ulong Position { get; private set; } - public ulong Size { get; private set; } - public byte Flags { get; private set; } + public ulong Size { get; private set; } + public byte Flags { get; private set; } public IpcBuffDesc(BinaryReader reader) { @@ -14,14 +14,14 @@ namespace Ryujinx.HLE.HOS.Ipc ulong word1 = reader.ReadUInt32(); ulong word2 = reader.ReadUInt32(); - Position = word1; - Position |= (word2 << 4) & 0x0f00000000; + Position = word1; + Position |= (word2 << 4) & 0x0f00000000; Position |= (word2 << 34) & 0x7000000000; - Size = word0; + Size = word0; Size |= (word2 << 8) & 0xf00000000; Flags = (byte)(word2 & 3); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs index c7ef7e9cf..887fe28e6 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs @@ -90,4 +90,4 @@ namespace Ryujinx.HLE.HOS.Ipc return ms; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs index 72770b90e..05d609071 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcMagic.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Ipc public const long Sfci = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'I' << 24; public const long Sfco = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'O' << 24; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 21630c42e..feba09fe3 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -2,7 +2,6 @@ using Microsoft.IO; using Ryujinx.Common; using Ryujinx.Common.Memory; using System; -using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -15,10 +14,10 @@ namespace Ryujinx.HLE.HOS.Ipc public IpcHandleDesc HandleDesc { get; set; } - public List<IpcPtrBuffDesc> PtrBuff { get; private set; } - public List<IpcBuffDesc> SendBuff { get; private set; } - public List<IpcBuffDesc> ReceiveBuff { get; private set; } - public List<IpcBuffDesc> ExchangeBuff { get; private set; } + public List<IpcPtrBuffDesc> PtrBuff { get; private set; } + public List<IpcBuffDesc> SendBuff { get; private set; } + public List<IpcBuffDesc> ReceiveBuff { get; private set; } + public List<IpcBuffDesc> ExchangeBuff { get; private set; } public List<IpcRecvListBuffDesc> RecvListBuff { get; private set; } public List<int> ObjectIds { get; private set; } @@ -27,9 +26,9 @@ namespace Ryujinx.HLE.HOS.Ipc public IpcMessage() { - PtrBuff = new List<IpcPtrBuffDesc>(0); - SendBuff = new List<IpcBuffDesc>(0); - ReceiveBuff = new List<IpcBuffDesc>(0); + PtrBuff = new List<IpcPtrBuffDesc>(0); + SendBuff = new List<IpcBuffDesc>(0); + ReceiveBuff = new List<IpcBuffDesc>(0); ExchangeBuff = new List<IpcBuffDesc>(0); RecvListBuff = new List<IpcRecvListBuffDesc>(0); @@ -38,93 +37,92 @@ namespace Ryujinx.HLE.HOS.Ipc public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) { - using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data)) + using RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data); + + BinaryReader reader = new(ms); + + int word0 = reader.ReadInt32(); + int word1 = reader.ReadInt32(); + + Type = (IpcMessageType)(word0 & 0xffff); + + int ptrBuffCount = (word0 >> 16) & 0xf; + int sendBuffCount = (word0 >> 20) & 0xf; + int recvBuffCount = (word0 >> 24) & 0xf; + int xchgBuffCount = (word0 >> 28) & 0xf; + + int rawDataSize = (word1 >> 0) & 0x3ff; + int recvListFlags = (word1 >> 10) & 0xf; + bool hndDescEnable = ((word1 >> 31) & 0x1) != 0; + + if (hndDescEnable) { - BinaryReader reader = new BinaryReader(ms); + HandleDesc = new IpcHandleDesc(reader); + } - int word0 = reader.ReadInt32(); - int word1 = reader.ReadInt32(); + PtrBuff = new List<IpcPtrBuffDesc>(ptrBuffCount); - Type = (IpcMessageType)(word0 & 0xffff); + for (int index = 0; index < ptrBuffCount; index++) + { + PtrBuff.Add(new IpcPtrBuffDesc(reader)); + } - int ptrBuffCount = (word0 >> 16) & 0xf; - int sendBuffCount = (word0 >> 20) & 0xf; - int recvBuffCount = (word0 >> 24) & 0xf; - int xchgBuffCount = (word0 >> 28) & 0xf; + static List<IpcBuffDesc> ReadBuff(BinaryReader reader, int count) + { + List<IpcBuffDesc> buff = new(count); - int rawDataSize = (word1 >> 0) & 0x3ff; - int recvListFlags = (word1 >> 10) & 0xf; - bool hndDescEnable = ((word1 >> 31) & 0x1) != 0; - - if (hndDescEnable) + for (int index = 0; index < count; index++) { - HandleDesc = new IpcHandleDesc(reader); + buff.Add(new IpcBuffDesc(reader)); } - PtrBuff = new List<IpcPtrBuffDesc>(ptrBuffCount); + return buff; + } - for (int index = 0; index < ptrBuffCount; index++) - { - PtrBuff.Add(new IpcPtrBuffDesc(reader)); - } + SendBuff = ReadBuff(reader, sendBuffCount); + ReceiveBuff = ReadBuff(reader, recvBuffCount); + ExchangeBuff = ReadBuff(reader, xchgBuffCount); - static List<IpcBuffDesc> ReadBuff(BinaryReader reader, int count) - { - List<IpcBuffDesc> buff = new List<IpcBuffDesc>(count); - - for (int index = 0; index < count; index++) - { - buff.Add(new IpcBuffDesc(reader)); - } - - return buff; - } + rawDataSize *= 4; - SendBuff = ReadBuff(reader, sendBuffCount); - ReceiveBuff = ReadBuff(reader, recvBuffCount); - ExchangeBuff = ReadBuff(reader, xchgBuffCount); - - rawDataSize *= 4; - - long recvListPos = reader.BaseStream.Position + rawDataSize; + long recvListPos = reader.BaseStream.Position + rawDataSize; // Only CMIF has the padding requirements. if (Type < IpcMessageType.TipcCloseSession) { long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr); - if (rawDataSize != 0) - { - rawDataSize -= (int)pad0; - } - - reader.BaseStream.Seek(pad0, SeekOrigin.Current); - } - - int recvListCount = recvListFlags - 2; - - if (recvListCount == 0) + if (rawDataSize != 0) { - recvListCount = 1; - } - else if (recvListCount < 0) - { - recvListCount = 0; + rawDataSize -= (int)pad0; } - RawData = reader.ReadBytes(rawDataSize); - - reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin); - - RecvListBuff = new List<IpcRecvListBuffDesc>(recvListCount); - - for (int index = 0; index < recvListCount; index++) - { - RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64())); - } - - ObjectIds = new List<int>(0); + reader.BaseStream.Seek(pad0, SeekOrigin.Current); } + + int recvListCount = recvListFlags - 2; + + if (recvListCount == 0) + { + recvListCount = 1; + } + else if (recvListCount < 0) + { + recvListCount = 0; + } + + RawData = reader.ReadBytes(rawDataSize); + + reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin); + + RecvListBuff = new List<IpcRecvListBuffDesc>(recvListCount); + + for (int index = 0; index < recvListCount; index++) + { + RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64())); + } + + ObjectIds = new List<int>(0); } public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr) @@ -134,10 +132,10 @@ namespace Ryujinx.HLE.HOS.Ipc int word0; int word1; - word0 = (int)Type; - word0 |= (PtrBuff.Count & 0xf) << 16; - word0 |= (SendBuff.Count & 0xf) << 20; - word0 |= (ReceiveBuff.Count & 0xf) << 24; + word0 = (int)Type; + word0 |= (PtrBuff.Count & 0xf) << 16; + word0 |= (SendBuff.Count & 0xf) << 20; + word0 |= (ReceiveBuff.Count & 0xf) << 24; word0 |= (ExchangeBuff.Count & 0xf) << 28; using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream(); @@ -238,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Ipc return ms; } - private long GetPadSize16(long position) + private static long GetPadSize16(long position) { if ((position & 0xf) != 0) { diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs index 1c8622485..1391569cb 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcMessageType.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Ipc { enum IpcMessageType { - CmifResponse = 0, - CmifCloseSession = 2, - CmifRequest = 4, - CmifControl = 5, + CmifResponse = 0, + CmifCloseSession = 2, + CmifRequest = 4, + CmifControl = 5, CmifRequestWithContext = 6, CmifControlWithContext = 7, - TipcCloseSession = 0xF + TipcCloseSession = 0xF, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs index 05798fe18..7ea2af0a8 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Ipc struct IpcPtrBuffDesc { public ulong Position { get; private set; } - public uint Index { get; private set; } - public ulong Size { get; private set; } + public uint Index { get; private set; } + public ulong Size { get; private set; } public IpcPtrBuffDesc(ulong position, uint index, ulong size) { @@ -20,26 +20,26 @@ namespace Ryujinx.HLE.HOS.Ipc ulong word0 = reader.ReadUInt32(); ulong word1 = reader.ReadUInt32(); - Position = word1; + Position = word1; Position |= (word0 << 20) & 0x0f00000000; Position |= (word0 << 30) & 0x7000000000; - Index = ((uint)word0 >> 0) & 0x03f; + Index = ((uint)word0 >> 0) & 0x03f; Index |= ((uint)word0 >> 3) & 0x1c0; Size = (ushort)(word0 >> 16); } - public IpcPtrBuffDesc WithSize(ulong size) + public readonly IpcPtrBuffDesc WithSize(ulong size) { return new IpcPtrBuffDesc(Position, Index, size); } - public uint GetWord0() + public readonly uint GetWord0() { uint word0; - word0 = (uint)((Position & 0x0f00000000) >> 20); + word0 = (uint)((Position & 0x0f00000000) >> 20); word0 |= (uint)((Position & 0x7000000000) >> 30); word0 |= (Index & 0x03f) << 0; @@ -50,9 +50,9 @@ namespace Ryujinx.HLE.HOS.Ipc return word0; } - public uint GetWord1() + public readonly uint GetWord1() { return (uint)Position; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs index bcc9d8f89..f74f81c74 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs @@ -1,11 +1,9 @@ -using System.IO; - namespace Ryujinx.HLE.HOS.Ipc { struct IpcRecvListBuffDesc { public ulong Position { get; private set; } - public ulong Size { get; private set; } + public ulong Size { get; private set; } public IpcRecvListBuffDesc(ulong position, ulong size) { @@ -20,4 +18,4 @@ namespace Ryujinx.HLE.HOS.Ipc Size = (ushort)(packedValue >> 48); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs b/src/Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs index b3aaa2191..556b3df45 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/ServiceProcessRequest.cs @@ -1,4 +1,4 @@ namespace Ryujinx.HLE.HOS.Ipc { delegate long ServiceProcessRequest(ServiceCtx context); -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs index 473683ff7..1550c9bf2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs @@ -4,4 +4,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { void TimeUp(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs index 424bf788b..e5dd3d171 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs @@ -70,4 +70,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs index b1a602f18..3f16f8c24 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs @@ -22,10 +22,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Common public KResourceLimit(KernelContext context) : base(context) { - _current = new long[(int)LimitableResource.Count]; - _limit = new long[(int)LimitableResource.Count]; + _current = new long[(int)LimitableResource.Count]; + _limit = new long[(int)LimitableResource.Count]; _current2 = new long[(int)LimitableResource.Count]; - _peak = new long[(int)LimitableResource.Count]; + _peak = new long[(int)LimitableResource.Count]; _lock = new object(); @@ -185,4 +185,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Common return (int)resource; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs index ddc0069d0..7e725e747 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs @@ -32,4 +32,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Common return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs index 8a727c30c..10f0b6f78 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB, MemoryArrange.MemoryArrange6GiB or MemoryArrange.MemoryArrange8GiB => 4916 * MiB, - _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".") + _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), }; } @@ -44,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange.MemoryArrange6GiB => 562 * MiB, MemoryArrange.MemoryArrange6GiBAppletDev or MemoryArrange.MemoryArrange8GiB => 2193 * MiB, - _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".") + _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), }; } @@ -71,8 +71,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemorySize.MemorySize4GiB => 4 * GiB, MemorySize.MemorySize6GiB => 6 * GiB, MemorySize.MemorySize8GiB => 8 * GiB, - _ => throw new ArgumentException($"Invalid memory size \"{size}\".") + _ => throw new ArgumentException($"Invalid memory size \"{size}\"."), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs index c0cd9ce99..499bc2c61 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs @@ -36,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common _waitingObjects = new List<WaitingObject>(); _keepRunning = true; - Thread work = new Thread(WaitAndCheckScheduledObjects) + Thread work = new(WaitAndCheckScheduledObjects) { - Name = "HLE.TimeManager" + Name = "HLE.TimeManager", }; work.Start(); @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common private void WaitAndCheckScheduledObjects() { - SpinWait spinWait = new SpinWait(); + SpinWait spinWait = new(); WaitingObject next; using (_waitEvent = new AutoResetEvent(false)) @@ -215,4 +215,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Common _waitEvent?.Set(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs index efa2a480d..8021d8da0 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs @@ -9,20 +9,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Common private readonly struct MemoryRegion { public ulong Address { get; } - public ulong Size { get; } + public ulong Size { get; } public ulong EndAddress => Address + Size; public MemoryRegion(ulong address, ulong size) { Address = address; - Size = size; + Size = size; } } public static void InitializeResourceLimit(KResourceLimit resourceLimit, MemorySize size) { - void EnsureSuccess(Result result) + static void EnsureSuccess(Result result) { if (result != Result.Success) { @@ -32,11 +32,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Common ulong ramSize = KSystemControl.GetDramSize(size); - EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Memory, (long)ramSize)); - EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Thread, 800)); - EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Event, 700)); + EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Memory, (long)ramSize)); + EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Thread, 800)); + EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Event, 700)); EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 200)); - EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Session, 900)); + EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Session, 900)); if (!resourceLimit.Reserve(LimitableResource.Memory, 0) || !resourceLimit.Reserve(LimitableResource.Memory, 0x60000)) @@ -47,9 +47,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common public static KMemoryRegionManager[] GetMemoryRegions(MemorySize size, MemoryArrange arrange) { - ulong poolEnd = KSystemControl.GetDramEndAddress(size); + ulong poolEnd = KSystemControl.GetDramEndAddress(size); ulong applicationPoolSize = KSystemControl.GetApplicationPoolSize(arrange); - ulong appletPoolSize = KSystemControl.GetAppletPoolSize(arrange); + ulong appletPoolSize = KSystemControl.GetAppletPoolSize(arrange); MemoryRegion servicePool; MemoryRegion nvServicesPool; @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common ulong nvServicesPoolEnd = applicationPool.Address - appletPoolSize; nvServicesPool = new MemoryRegion(nvServicesPoolEnd - nvServicesPoolSize, nvServicesPoolSize); - appletPool = new MemoryRegion(nvServicesPoolEnd, appletPoolSize); + appletPool = new MemoryRegion(nvServicesPoolEnd, appletPoolSize); // Note: There is an extra region used by the kernel, however // since we are doing HLE we are not going to use that memory, so give all @@ -72,12 +72,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common servicePool = new MemoryRegion(DramMemoryMap.SlabHeapEnd, servicePoolSize); - return new KMemoryRegionManager[] + return new[] { GetMemoryRegion(applicationPool), GetMemoryRegion(appletPool), GetMemoryRegion(servicePool), - GetMemoryRegion(nvServicesPool) + GetMemoryRegion(nvServicesPool), }; } @@ -86,4 +86,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Common return new KMemoryRegionManager(region.Address, region.Size, region.EndAddress); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs index cbc276c50..082d25ff0 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs @@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common return false; } - public static bool KernelToUser<T>(ulong address, T value) where T: unmanaged + public static bool KernelToUser<T>(ulong address, T value) where T : unmanaged { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -70,4 +70,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Common return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs index 2e6a3e455..8655e61c1 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { enum LimitableResource : byte { - Memory = 0, - Thread = 1, - Event = 2, + Memory = 0, + Thread = 1, + Event = 2, TransferMemory = 3, - Session = 4, + Session = 4, - Count = 5 + Count = 5, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs index d2bcfd62e..2c88d8b35 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs @@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange4GiBSystemDev, MemoryArrange6GiB, MemoryArrange6GiBAppletDev, - MemoryArrange8GiB + MemoryArrange8GiB, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemorySize.cs similarity index 82% rename from src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs rename to src/Ryujinx.HLE/HOS/Kernel/Common/MemorySize.cs index 159385b63..7cc34a722 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemorySize.cs @@ -4,6 +4,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { MemorySize4GiB = 0, MemorySize6GiB = 1, - MemorySize8GiB = 2 + MemorySize8GiB = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs index 4c99f4251..9f078b10a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common class MersenneTwister { private int _index; - private uint[] _mt; + private readonly uint[] _mt; public MersenneTwister(uint seed) { @@ -46,12 +46,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common // If Range is already power of 2, subtract one to use log2(Range) directly. int rangeLog2 = nextRangeLog2 - (BitOperations.IsPow2(range) ? 1 : 0); - int parts = rangeLog2 > 32 ? 2 : 1; + int parts = rangeLog2 > 32 ? 2 : 1; int bitsPerPart = rangeLog2 / parts; int fullParts = parts - (rangeLog2 - parts * bitsPerPart); - uint mask = 0xffffffffu >> (32 - bitsPerPart); + uint mask = 0xffffffffu >> (32 - bitsPerPart); uint maskPlus1 = 0xffffffffu >> (31 - bitsPerPart); long randomNumber; @@ -66,9 +66,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common } private long GenRandomNumber( - int parts, - int fullParts, - int bitsPerPart, + int parts, + int fullParts, + int bitsPerPart, uint mask, uint maskPlus1) { @@ -79,13 +79,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common for (; part < fullParts; part++) { randomNumber <<= bitsPerPart; - randomNumber |= GenRandomNumber() & mask; + randomNumber |= GenRandomNumber() & mask; } for (; part < parts; part++) { randomNumber <<= bitsPerPart + 1; - randomNumber |= GenRandomNumber() & maskPlus1; + randomNumber |= GenRandomNumber() & maskPlus1; } return randomNumber; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs index 4827384eb..3a943f94b 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs @@ -5,6 +5,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc NotInitialized, Open, ClientDisconnected, - ServerDisconnected + ServerDisconnected, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs index e28244d4a..5fa9cbe7a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs @@ -4,17 +4,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { class KBufferDescriptor { - public ulong ClientAddress { get; } - public ulong ServerAddress { get; } - public ulong Size { get; } - public MemoryState State { get; } + public ulong ClientAddress { get; } + public ulong ServerAddress { get; } + public ulong Size { get; } + public MemoryState State { get; } public KBufferDescriptor(ulong src, ulong dst, ulong size, MemoryState state) { ClientAddress = src; ServerAddress = dst; - Size = size; - State = state; + Size = size; + State = state; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs index 593d2c9d5..373899b7b 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs @@ -9,14 +9,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { private const int MaxInternalBuffersCount = 8; - private List<KBufferDescriptor> _sendBufferDescriptors; - private List<KBufferDescriptor> _receiveBufferDescriptors; - private List<KBufferDescriptor> _exchangeBufferDescriptors; + private readonly List<KBufferDescriptor> _sendBufferDescriptors; + private readonly List<KBufferDescriptor> _receiveBufferDescriptors; + private readonly List<KBufferDescriptor> _exchangeBufferDescriptors; public KBufferDescriptorTable() { - _sendBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount); - _receiveBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount); + _sendBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount); + _receiveBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount); _exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount); } @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return Add(_exchangeBufferDescriptors, src, dst, size, state); } - private Result Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state) + private static Result Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state) { if (list.Count < MaxInternalBuffersCount) { @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return CopyToClient(memoryManager, _exchangeBufferDescriptors); } - private Result CopyToClient(KPageTableBase memoryManager, List<KBufferDescriptor> list) + private static Result CopyToClient(KPageTableBase memoryManager, List<KBufferDescriptor> list) { foreach (KBufferDescriptor desc in list) { @@ -67,11 +67,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc switch (desc.State) { - case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break; - case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break; - case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break; - - default: return KernelResult.InvalidCombination; + case MemoryState.IpcBuffer0: + stateMask = MemoryState.IpcSendAllowedType0; + break; + case MemoryState.IpcBuffer1: + stateMask = MemoryState.IpcSendAllowedType1; + break; + case MemoryState.IpcBuffer3: + stateMask = MemoryState.IpcSendAllowedType3; + break; + default: + return KernelResult.InvalidCombination; } MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached; @@ -82,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } ulong clientAddrTruncated = BitUtils.AlignDown<ulong>(desc.ClientAddress, KPageTableBase.PageSize); - ulong clientAddrRounded = BitUtils.AlignUp<ulong>(desc.ClientAddress, KPageTableBase.PageSize); + ulong clientAddrRounded = BitUtils.AlignUp<ulong>(desc.ClientAddress, KPageTableBase.PageSize); // Check if address is not aligned, in this case we need to perform 2 copies. if (clientAddrTruncated != clientAddrRounded) @@ -114,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc ulong serverEndAddr = desc.ServerAddress + desc.Size; ulong clientEndAddrTruncated = BitUtils.AlignDown<ulong>(clientEndAddr, (ulong)KPageTableBase.PageSize); - ulong clientEndAddrRounded = BitUtils.AlignUp<ulong>(clientEndAddr, KPageTableBase.PageSize); + ulong clientEndAddrRounded = BitUtils.AlignUp<ulong>(clientEndAddr, KPageTableBase.PageSize); ulong serverEndAddrTruncated = BitUtils.AlignDown<ulong>(serverEndAddr, (ulong)KPageTableBase.PageSize); if (clientEndAddrTruncated < clientEndAddrRounded && @@ -159,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return UnmapServer(memoryManager, _exchangeBufferDescriptors); } - private Result UnmapServer(KPageTableBase memoryManager, List<KBufferDescriptor> list) + private static Result UnmapServer(KPageTableBase memoryManager, List<KBufferDescriptor> list) { foreach (KBufferDescriptor descriptor in list) { @@ -196,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return RestoreClient(memoryManager, _exchangeBufferDescriptors); } - private Result RestoreClient(KPageTableBase memoryManager, List<KBufferDescriptor> list) + private static Result RestoreClient(KPageTableBase memoryManager, List<KBufferDescriptor> list) { foreach (KBufferDescriptor descriptor in list) { @@ -214,4 +220,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs index eb7c5a418..6355fb6ee 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public KClientPort(KernelContext context, KPort parent, int maxSessions) : base(context) { _maxSessions = maxSessions; - _parent = parent; + _parent = parent; } public Result Connect(out KClientSession clientSession) @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return KernelResult.SessionCountExceeded; } - KSession session = new KSession(KernelContext, this); + KSession session = new(KernelContext, this); Result result = _parent.EnqueueIncomingSession(session.ServerSession); @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return KernelResult.SessionCountExceeded; } - KLightSession session = new KLightSession(KernelContext); + KLightSession session = new(KernelContext); Result result = _parent.EnqueueIncomingLightSession(session.ServerSession); @@ -133,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { KAutoObject foundObj = FindNamedObject(context, name); - if (!(foundObj is KClientPort)) + if (foundObj is not KClientPort) { return KernelResult.NotFound; } @@ -141,4 +141,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return KAutoObject.RemoveName(context, name); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs index a24bcc311..385f09020 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { public KProcess CreatorProcess { get; } - private KSession _parent; + private readonly KSession _parent; public ChannelState State { get; set; } @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public KClientSession(KernelContext context, KSession parent, KClientPort parentPort) : base(context) { - _parent = parent; + _parent = parent; ParentPort = parentPort; parentPort?.IncrementReferenceCount(); @@ -32,11 +32,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { KThread currentThread = KernelStatic.GetCurrentThread(); - KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize); + KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize); KernelContext.CriticalSection.Enter(); - currentThread.SignaledObj = null; + currentThread.SignaledObj = null; currentThread.ObjSyncResult = Result.Success; Result result = _parent.ServerSession.EnqueueRequest(request); @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { KThread currentThread = KernelStatic.GetCurrentThread(); - KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent); + KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent); KernelContext.CriticalSection.Enter(); @@ -81,4 +81,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc _parent.DecrementReferenceCount(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs index 27a9732be..1ff37282f 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs @@ -4,11 +4,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { class KLightClientSession : KAutoObject { +#pragma warning disable IDE0052 // Remove unread private member private readonly KLightSession _parent; +#pragma warning restore IDE0052 public KLightClientSession(KernelContext context, KLightSession parent) : base(context) { _parent = parent; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs index 0edbba6cb..c355409ec 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs @@ -4,11 +4,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { class KLightServerSession : KAutoObject { +#pragma warning disable IDE0052 // Remove unread private member private readonly KLightSession _parent; +#pragma warning restore IDE0052 public KLightServerSession(KernelContext context, KLightSession parent) : base(context) { _parent = parent; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs index 3abb1ab0d..16158a707 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs @@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc ClientSession = new KLightClientSession(context, this); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs index 93f0f34cd..84ebcbc3e 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs @@ -8,9 +8,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public KServerPort ServerPort { get; } public KClientPort ClientPort { get; } - private string _name; +#pragma warning disable IDE0052 // Remove unread private member + private readonly string _name; +#pragma warning restore IDE0052 - private ChannelState _state; + private readonly ChannelState _state; public bool IsLight { get; private set; } @@ -69,4 +71,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return result; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs index 21a3919cf..08efa8d94 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { class KServerPort : KSynchronizationObject { - private readonly LinkedList<KServerSession> _incomingConnections; + private readonly LinkedList<KServerSession> _incomingConnections; private readonly LinkedList<KLightServerSession> _lightIncomingConnections; private readonly KPort _parent; @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { _parent = parent; - _incomingConnections = new LinkedList<KServerSession>(); + _incomingConnections = new LinkedList<KServerSession>(); _lightIncomingConnections = new LinkedList<KLightServerSession>(); } @@ -84,4 +84,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs index 86469c03a..7e41a3f3a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs @@ -10,19 +10,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { class KServerSession : KSynchronizationObject { - private static readonly MemoryState[] IpcMemoryStates = new MemoryState[] - { + private static readonly MemoryState[] _ipcMemoryStates = { MemoryState.IpcBuffer3, MemoryState.IpcBuffer0, MemoryState.IpcBuffer1, - (MemoryState)0xfffce5d4 //This is invalid, shouldn't be accessed. + (MemoryState)0xfffce5d4, //This is invalid, shouldn't be accessed. }; private readonly struct Message { - public ulong Address { get; } - public ulong Size { get; } - public bool IsCustom { get; } + public ulong Address { get; } + public ulong Size { get; } + public bool IsCustom { get; } public Message(KThread thread, ulong customCmdBuffAddress, ulong customCmdBuffSize) { @@ -31,19 +30,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc if (IsCustom) { Address = customCmdBuffAddress; - Size = customCmdBuffSize; + Size = customCmdBuffSize; } else { Address = thread.TlsAddress; - Size = 0x100; + Size = 0x100; } } public Message(KSessionRequest request) : this( request.ClientThread, request.CustomCmdBuffAddr, - request.CustomCmdBuffSize) { } + request.CustomCmdBuffSize) + { } } private readonly struct MessageHeader @@ -52,18 +52,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public uint Word1 { get; } public uint Word2 { get; } - public uint PointerBuffersCount { get; } - public uint SendBuffersCount { get; } - public uint ReceiveBuffersCount { get; } + public uint PointerBuffersCount { get; } + public uint SendBuffersCount { get; } + public uint ReceiveBuffersCount { get; } public uint ExchangeBuffersCount { get; } public uint RawDataSizeInWords { get; } public uint ReceiveListType { get; } - public uint MessageSizeInWords { get; } + public uint MessageSizeInWords { get; } public uint ReceiveListOffsetInWords { get; } - public uint ReceiveListOffset { get; } + public uint ReceiveListOffset { get; } public bool HasHandles { get; } @@ -101,14 +101,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc MoveHandlesCount = 0; } - PointerBuffersCount = (word0 >> 16) & 0xf; - SendBuffersCount = (word0 >> 20) & 0xf; - ReceiveBuffersCount = (word0 >> 24) & 0xf; - ExchangeBuffersCount = word0 >> 28; + PointerBuffersCount = (word0 >> 16) & 0xf; + SendBuffersCount = (word0 >> 20) & 0xf; + ReceiveBuffersCount = (word0 >> 24) & 0xf; + ExchangeBuffersCount = word0 >> 28; - uint pointerDescSizeInWords = PointerBuffersCount * 2; - uint sendDescSizeInWords = SendBuffersCount * 3; - uint receiveDescSizeInWords = ReceiveBuffersCount * 3; + uint pointerDescSizeInWords = PointerBuffersCount * 2; + uint sendDescSizeInWords = SendBuffersCount * 3; + uint receiveDescSizeInWords = ReceiveBuffersCount * 3; uint exchangeDescSizeInWords = ExchangeBuffersCount * 3; RawDataSizeInWords = word1 & 0x3ff; @@ -119,12 +119,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc uint paddingSizeInWords = HasHandles ? 3u : 2u; - MessageSizeInWords = pointerDescSizeInWords + - sendDescSizeInWords + - receiveDescSizeInWords + + MessageSizeInWords = pointerDescSizeInWords + + sendDescSizeInWords + + receiveDescSizeInWords + exchangeDescSizeInWords + - RawDataSizeInWords + - paddingSizeInWords + + RawDataSizeInWords + + paddingSizeInWords + handleDescSizeInWords; if (ReceiveListOffsetInWords == 0) @@ -140,25 +140,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { public uint ReceiveIndex { get; } - public uint BufferSize { get; } + public uint BufferSize { get; } public ulong BufferAddress { get; set; } public PointerBufferDesc(ulong dword) { ReceiveIndex = (uint)dword & 0xf; - BufferSize = (uint)dword >> 16; + BufferSize = (uint)dword >> 16; - BufferAddress = (dword >> 2) & 0x70; + BufferAddress = (dword >> 2) & 0x70; BufferAddress |= (dword >> 12) & 0xf; BufferAddress = (BufferAddress << 32) | (dword >> 32); } - public ulong Pack() + public readonly ulong Pack() { ulong dword = (ReceiveIndex & 0xf) | ((BufferSize & 0xffff) << 16); - dword |= BufferAddress << 32; + dword |= BufferAddress << 32; dword |= (BufferAddress >> 20) & 0xf000; dword |= (BufferAddress >> 30) & 0xffc0; @@ -166,9 +166,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } - private KSession _parent; + private readonly KSession _parent; - private LinkedList<KSessionRequest> _requests; + private readonly LinkedList<KSessionRequest> _requests; private KSessionRequest _activeRequest; @@ -208,7 +208,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public Result Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) { - KThread serverThread = KernelStatic.GetCurrentThread(); + KThread serverThread = KernelStatic.GetCurrentThread(); KProcess serverProcess = serverThread.Owner; KernelContext.CriticalSection.Enter(); @@ -234,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return KernelResult.PortRemoteClosed; } - KThread clientThread = request.ClientThread; + KThread clientThread = request.ClientThread; KProcess clientProcess = clientThread.Owner; KernelContext.CriticalSection.Leave(); @@ -243,8 +243,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc request.ServerProcess = serverProcess; - Message clientMsg = new Message(request); - Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize); + Message clientMsg = new(request); + Message serverMsg = new(serverThread, customCmdBuffAddr, customCmdBuffSize); MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg); MessageHeader serverHeader = GetServerMessageHeader(serverMsg); @@ -399,7 +399,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { ulong pointerDesc = clientProcess.CpuMemory.Read<ulong>(clientMsg.Address + offset * 4); - PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc); + PointerBufferDesc descriptor = new(pointerDesc); if (descriptor.BufferSize != 0) { @@ -409,7 +409,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc serverHeader.ReceiveListType, clientHeader.MessageSizeInWords, receiveList, - ref recvListDstOffset, + ref recvListDstOffset, out ulong recvListBufferAddress); if (clientResult != Result.Success) @@ -450,7 +450,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc // Copy send, receive and exchange buffers. uint totalBuffersCount = - clientHeader.SendBuffersCount + + clientHeader.SendBuffersCount + clientHeader.ReceiveBuffersCount + clientHeader.ExchangeBuffersCount; @@ -462,11 +462,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc uint descWord1 = clientProcess.CpuMemory.Read<uint>(clientDescAddress + 4); uint descWord2 = clientProcess.CpuMemory.Read<uint>(clientDescAddress + 8); - bool isSendDesc = index < clientHeader.SendBuffersCount; + bool isSendDesc = index < clientHeader.SendBuffersCount; bool isExchangeDesc = index >= clientHeader.SendBuffersCount + clientHeader.ReceiveBuffersCount; bool notReceiveDesc = isSendDesc || isExchangeDesc; - bool isReceiveDesc = !notReceiveDesc; + bool isReceiveDesc = !notReceiveDesc; KMemoryPermission permission = index >= clientHeader.SendBuffersCount ? KMemoryPermission.ReadAndWrite @@ -482,12 +482,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { ulong bufferAddress; - bufferAddress = descWord2 >> 28; + bufferAddress = descWord2 >> 28; bufferAddress |= ((descWord2 >> 2) & 7) << 4; bufferAddress = (bufferAddress << 32) | descWord1; - MemoryState state = IpcMemoryStates[(descWord2 + 1) & 3]; + MemoryState state = _ipcMemoryStates[(descWord2 + 1) & 3]; clientResult = serverProcess.MemoryManager.MapBufferFromClientProcess( bufferSize, @@ -533,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc descWord2 |= sizeHigh4 << 24; descWord2 |= (uint)(dstAddress >> 34) & 0x3ffffffc; - descWord2 |= (uint)(dstAddress >> 4) & 0xf0000000; + descWord2 |= (uint)(dstAddress >> 4) & 0xf0000000; ulong serverDescAddress = serverMsg.Address + offset * 4; @@ -586,7 +586,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public Result Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) { - KThread serverThread = KernelStatic.GetCurrentThread(); + KThread serverThread = KernelStatic.GetCurrentThread(); KProcess serverProcess = serverThread.Owner; KernelContext.CriticalSection.Enter(); @@ -609,11 +609,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc KernelContext.CriticalSection.Leave(); - KThread clientThread = request.ClientThread; + KThread clientThread = request.ClientThread; KProcess clientProcess = clientThread.Owner; - Message clientMsg = new Message(request); - Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize); + Message clientMsg = new(request); + Message serverMsg = new(serverThread, customCmdBuffAddr, customCmdBuffSize); MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg); MessageHeader serverHeader = GetServerMessageHeader(serverMsg); @@ -664,8 +664,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return KernelResult.CmdBufferTooSmall; } - if (serverHeader.SendBuffersCount != 0 || - serverHeader.ReceiveBuffersCount != 0 || + if (serverHeader.SendBuffersCount != 0 || + serverHeader.ReceiveBuffersCount != 0 || serverHeader.ExchangeBuffersCount != 0) { CleanUpForError(); @@ -761,7 +761,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { ulong pointerDesc = serverProcess.CpuMemory.Read<ulong>(serverMsg.Address + offset * 4); - PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc); + PointerBufferDesc descriptor = new(pointerDesc); ulong recvListBufferAddress = 0; @@ -817,7 +817,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc // Set send, receive and exchange buffer descriptors to zero. uint totalBuffersCount = - serverHeader.SendBuffersCount + + serverHeader.SendBuffersCount + serverHeader.ReceiveBuffersCount + serverHeader.ExchangeBuffersCount; @@ -868,7 +868,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return serverResult; } - private MessageHeader GetClientMessageHeader(KProcess clientProcess, Message clientMsg) + private static MessageHeader GetClientMessageHeader(KProcess clientProcess, Message clientMsg) { uint word0 = clientProcess.CpuMemory.Read<uint>(clientMsg.Address + 0); uint word1 = clientProcess.CpuMemory.Read<uint>(clientMsg.Address + 4); @@ -877,7 +877,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return new MessageHeader(word0, word1, word2); } - private MessageHeader GetServerMessageHeader(Message serverMsg) + private static MessageHeader GetServerMessageHeader(Message serverMsg) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -888,7 +888,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return new MessageHeader(word0, word1, word2); } - private Result GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle) + private static Result GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle) { dstHandle = 0; @@ -919,7 +919,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } - private Result GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle) + private static Result GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle) { dstHandle = 0; @@ -939,7 +939,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } - private ulong[] GetReceiveList(KProcess ownerProcess, Message message, uint recvListType, uint recvListOffset) + private static ulong[] GetReceiveList(KProcess ownerProcess, Message message, uint recvListType, uint recvListOffset) { int recvListSize = 0; @@ -964,16 +964,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return receiveList; } - private Result GetReceiveListAddress( + private static Result GetReceiveListAddress( PointerBufferDesc descriptor, - Message message, - uint recvListType, - uint messageSizeInWords, - ulong[] receiveList, - ref uint dstOffset, - out ulong address) + Message message, + uint recvListType, + uint messageSizeInWords, + ulong[] receiveList, + ref uint dstOffset, + out ulong address) { - ulong recvListBufferAddress = address = 0; + ulong recvListBufferAddress; + address = 0; if (recvListType == 0) { @@ -987,7 +988,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc if (recvListType == 1) { recvListBaseAddr = message.Address + messageSizeInWords * 4; - recvListEndAddr = message.Address + message.Size; + recvListEndAddr = message.Address + message.Size; } else /* if (recvListType == 2) */ { @@ -1012,7 +1013,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc dstOffset = (uint)endAddress - (uint)recvListBaseAddr; if (recvListBufferAddress + descriptor.BufferSize <= recvListBufferAddress || - recvListBufferAddress + descriptor.BufferSize > recvListEndAddr) + recvListBufferAddress + descriptor.BufferSize > recvListEndAddr) { return KernelResult.OutOfResource; } @@ -1041,7 +1042,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return Result.Success; } - private void CloseAllHandles(Message message, MessageHeader header, KProcess process) + private static void CloseAllHandles(Message message, MessageHeader header, KProcess process) { if (header.HasHandles) { @@ -1202,7 +1203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } - private void SendResultToAsyncRequestClient(KSessionRequest request, Result result) + private static void SendResultToAsyncRequestClient(KSessionRequest request, Result result) { KProcess clientProcess = request.ClientThread.Owner; @@ -1232,15 +1233,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc KernelContext.CriticalSection.Leave(); } - private void WakeAndSetResult(KThread thread, Result result, KSynchronizationObject signaledObj = null) + private static void WakeAndSetResult(KThread thread, Result result, KSynchronizationObject signaledObj = null) { if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) { - thread.SignaledObj = signaledObj; + thread.SignaledObj = signaledObj; thread.ObjSyncResult = result; thread.Reschedule(ThreadSchedState.Running); } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs index 13cf4b51b..6659d4141 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public KServerSession ServerSession { get; } public KClientSession ClientSession { get; } - private bool _hasBeenInitialized; + private readonly bool _hasBeenInitialized; public KSession(KernelContext context, KClientPort parentPort = null) : base(context) { @@ -51,4 +51,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs index 31ddfc9c4..bc3eef71e 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs @@ -17,17 +17,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public ulong CustomCmdBuffSize { get; } public KSessionRequest( - KThread clientThread, - ulong customCmdBuffAddr, - ulong customCmdBuffSize, + KThread clientThread, + ulong customCmdBuffAddr, + ulong customCmdBuffSize, KWritableEvent asyncEvent = null) { - ClientThread = clientThread; + ClientThread = clientThread; CustomCmdBuffAddr = customCmdBuffAddr; CustomCmdBuffSize = customCmdBuffSize; - AsyncEvent = asyncEvent; + AsyncEvent = asyncEvent; BufferDescriptorTable = new KBufferDescriptorTable(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs index 28db750c7..3dbaec187 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs @@ -17,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Kernel public const ulong CounterFrequency = 19200000; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs index c66f4b578..2234a8c42 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -10,10 +10,10 @@ namespace Ryujinx.HLE.HOS.Kernel static class KernelStatic { [ThreadStatic] - private static KernelContext Context; + private static KernelContext _context; [ThreadStatic] - private static KThread CurrentThread; + private static KThread _currentThread; public static Result StartInitialProcess( KernelContext context, @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel int mainThreadPriority, ThreadStart customThreadStart) { - KProcess process = new KProcess(context); + KProcess process = new(context); Result result = process.Initialize( creationInfo, @@ -46,13 +46,13 @@ namespace Ryujinx.HLE.HOS.Kernel internal static void SetKernelContext(KernelContext context, KThread thread) { - Context = context; - CurrentThread = thread; + _context = context; + _currentThread = thread; } internal static KThread GetCurrentThread() { - return CurrentThread; + return _currentThread; } internal static KProcess GetCurrentProcess() @@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Kernel internal static KProcess GetProcessByPid(ulong pid) { - if (Context.Processes.TryGetValue(pid, out KProcess process)) + if (_context.Processes.TryGetValue(pid, out KProcess process)) { return process; } @@ -70,4 +70,4 @@ namespace Ryujinx.HLE.HOS.Kernel return null; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs index 8395c5777..8dfa4303f 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { enum AddressSpaceType { - Addr32Bits = 0, - Addr36Bits = 1, + Addr32Bits = 0, + Addr36Bits = 1, Addr32BitsNoMap = 2, - Addr39Bits = 3 + Addr39Bits = 3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs index 4941d5b78..e7af29636 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs @@ -8,11 +8,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public const ulong SlabHeapBase = KernelReserveBase + 0x85000; public const ulong SlapHeapSize = 0xa21000; - public const ulong SlabHeapEnd = SlabHeapBase + SlapHeapSize; + public const ulong SlabHeapEnd = SlabHeapBase + SlapHeapSize; public static bool IsHeapPhysicalAddress(ulong address) { return address >= SlabHeapEnd; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs index 11474e493..c725501b0 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs @@ -166,4 +166,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Owner.DecrementReferenceCount(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs index e082105b8..d2c4aadf3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs @@ -153,4 +153,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs index 8732b507a..b4f566991 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { class KMemoryBlockSlabManager { - private ulong _capacityElements; + private readonly ulong _capacityElements; public int Count { get; set; } @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return (ulong)(Count + count) <= _capacityElements; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs index af070ac23..4db484d04 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs @@ -3,34 +3,34 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory class KMemoryInfo { public ulong Address { get; } - public ulong Size { get; } + public ulong Size { get; } - public MemoryState State { get; } - public KMemoryPermission Permission { get; } - public MemoryAttribute Attribute { get; } + public MemoryState State { get; } + public KMemoryPermission Permission { get; } + public MemoryAttribute Attribute { get; } public KMemoryPermission SourcePermission { get; } - public int IpcRefCount { get; } + public int IpcRefCount { get; } public int DeviceRefCount { get; } public KMemoryInfo( - ulong address, - ulong size, - MemoryState state, + ulong address, + ulong size, + MemoryState state, KMemoryPermission permission, - MemoryAttribute attribute, + MemoryAttribute attribute, KMemoryPermission sourcePermission, - int ipcRefCount, - int deviceRefCount) + int ipcRefCount, + int deviceRefCount) { - Address = address; - Size = size; - State = state; - Permission = permission; - Attribute = attribute; + Address = address; + Size = size; + State = state; + Permission = permission; + Attribute = attribute; SourcePermission = sourcePermission; - IpcRefCount = ipcRefCount; - DeviceRefCount = deviceRefCount; + IpcRefCount = ipcRefCount; + DeviceRefCount = deviceRefCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs index 4596b15d5..2eff616c4 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs @@ -239,4 +239,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return (EndAddr - address) / KPageTableBase.PageSize; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs index fa090b02c..e0f9df484 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs @@ -224,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { while (depth >= 0) { - int ind = (int)(offset / UInt64BitSize); + int ind = (int)(offset / UInt64BitSize); int which = (int)(offset % UInt64BitSize); ulong mask = 1UL << which; @@ -247,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { while (depth >= 0) { - int ind = (int)(offset / UInt64BitSize); + int ind = (int)(offset / UInt64BitSize); int which = (int)(offset % UInt64BitSize); ulong mask = 1UL << which; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs index c3586ed78..635dcced8 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { private class Block { - private KPageBitmap _bitmap = new KPageBitmap(); + private readonly KPageBitmap _bitmap = new(); private ulong _heapAddress; private ulong _endOffset; @@ -29,8 +29,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ? 1UL << nextBlockShift : 1UL << blockShift; - address = BitUtils.AlignDown(address, align); - endAddress = BitUtils.AlignUp (endAddress, align); + address = BitUtils.AlignDown(address, align); + endAddress = BitUtils.AlignUp(endAddress, align); _heapAddress = address; _endOffset = (endAddress - address) / (1UL << blockShift); @@ -84,11 +84,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - private static readonly int[] _memoryBlockPageShifts = new int[] { 12, 16, 21, 22, 25, 29, 30 }; + private static readonly int[] _memoryBlockPageShifts = { 12, 16, 21, 22, 25, 29, 30 }; +#pragma warning disable IDE0052 // Remove unread private member private readonly ulong _heapAddress; private readonly ulong _heapSize; private ulong _usedSize; +#pragma warning restore IDE0052 private readonly int _blocksCount; private readonly Block[] _blocks; @@ -175,19 +177,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory int bigIndex = _blocksCount - 1; - ulong start = address; - ulong end = address + pagesCount * KPageTableBase.PageSize; + ulong start = address; + ulong end = address + pagesCount * KPageTableBase.PageSize; ulong beforeStart = start; - ulong beforeEnd = start; - ulong afterStart = end; - ulong afterEnd = end; + ulong beforeEnd = start; + ulong afterStart = end; + ulong afterEnd = end; while (bigIndex >= 0) { ulong blockSize = _blocks[bigIndex].Size; - ulong bigStart = BitUtils.AlignUp (start, blockSize); - ulong bigEnd = BitUtils.AlignDown(end, blockSize); + ulong bigStart = BitUtils.AlignUp(start, blockSize); + ulong bigEnd = BitUtils.AlignDown(end, blockSize); if (bigStart < bigEnd) { @@ -196,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory FreeBlock(block, bigIndex); } - beforeEnd = bigStart; + beforeEnd = bigStart; afterStart = bigEnd; break; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs index 3149faa97..60514824a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (lastNode.Address + lastNode.PagesCount * KPageTableBase.PageSize == address) { - address = lastNode.Address; + address = lastNode.Address; pagesCount += lastNode.PagesCount; Nodes.RemoveLast(); @@ -50,18 +50,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public bool IsEqual(KPageList other) { - LinkedListNode<KPageNode> thisNode = Nodes.First; + LinkedListNode<KPageNode> thisNode = Nodes.First; LinkedListNode<KPageNode> otherNode = other.Nodes.First; while (thisNode != null && otherNode != null) { - if (thisNode.Value.Address != otherNode.Value.Address || + if (thisNode.Value.Address != otherNode.Value.Address || thisNode.Value.PagesCount != otherNode.Value.PagesCount) { return false; } - thisNode = thisNode.Next; + thisNode = thisNode.Next; otherNode = otherNode.Next; } @@ -94,4 +94,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs index ada41687e..395c8c835 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs @@ -7,8 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public KPageNode(ulong address, ulong pagesCount) { - Address = address; + Address = address; PagesCount = pagesCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs index 119034c16..dcfc8f4ff 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// <inheritdoc/> protected override Result MapMemory(ulong src, ulong dst, ulong pagesCount, KMemoryPermission oldSrcPermission, KMemoryPermission newDstPermission) { - KPageList pageList = new KPageList(); + KPageList pageList = new(); GetPhysicalRegions(src, pagesCount * PageSize, pageList); Result result = Reprotect(src, pagesCount, KMemoryPermission.None); @@ -69,8 +69,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong size = pagesCount * PageSize; - KPageList srcPageList = new KPageList(); - KPageList dstPageList = new KPageList(); + KPageList srcPageList = new(); + KPageList dstPageList = new(); GetPhysicalRegions(src, size, srcPageList); GetPhysicalRegions(dst, size, dstPageList); @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// <inheritdoc/> protected override Result Unmap(ulong address, ulong pagesCount) { - KPageList pagesToClose = new KPageList(); + KPageList pagesToClose = new(); var regions = _cpuMemory.GetPhysicalRegions(address, pagesCount * PageSize); @@ -226,4 +226,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _cpuMemory.Write(va, data); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index 6746a0a7a..2b00f802a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -12,14 +12,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { abstract class KPageTableBase { - private static readonly int[] MappingUnitSizes = new int[] - { + private static readonly int[] _mappingUnitSizes = { 0x1000, 0x10000, 0x200000, 0x400000, 0x2000000, - 0x40000000 + 0x40000000, }; private const ulong RegionAlignment = 0x200000; @@ -58,7 +57,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public ulong AslrRegionStart { get; private set; } public ulong AslrRegionEnd { get; private set; } +#pragma warning disable IDE0052 // Remove unread private member private ulong _heapCapacity; +#pragma warning restore IDE0052 public ulong PhysicalMemoryUsage { get; private set; } @@ -67,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private MemoryRegion _memRegion; private bool _allocateFromBack; - private bool _isKernel; + private readonly bool _isKernel; private bool _aslrEnabled; @@ -77,10 +78,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private MersenneTwister _randomNumberGenerator; - private MemoryFillValue _heapFillValue; - private MemoryFillValue _ipcFillValue; + private readonly MemoryFillValue _heapFillValue; + private readonly MemoryFillValue _ipcFillValue; - private ulong _reservedAddressSpaceSize; + private readonly ulong _reservedAddressSpaceSize; public KPageTableBase(KernelContext context, ulong reservedAddressSpaceSize) { @@ -96,7 +97,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _reservedAddressSpaceSize = reservedAddressSpaceSize; } - private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 }; + private static readonly int[] _addrSpaceSizes = { 32, 36, 32, 39 }; public Result InitializeForProcess( AddressSpaceType addrSpaceType, @@ -109,13 +110,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { if ((uint)addrSpaceType > (uint)AddressSpaceType.Addr39Bits) { - throw new ArgumentException(nameof(addrSpaceType)); + throw new ArgumentException($"AddressSpaceType bigger than {(uint)AddressSpaceType.Addr39Bits}: {(uint)addrSpaceType}", nameof(addrSpaceType)); } _contextId = Context.ContextIdManager.GetId(); ulong addrSpaceBase = 0; - ulong addrSpaceSize = 1UL << AddrSpaceSizes[(int)addrSpaceType]; + ulong addrSpaceSize = 1UL << _addrSpaceSizes[(int)addrSpaceType]; Result result = CreateUserAddressSpace( addrSpaceType, @@ -157,10 +158,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong endAddr = address + size; - Region aliasRegion = new Region(); - Region heapRegion = new Region(); - Region stackRegion = new Region(); - Region tlsIoRegion = new Region(); + Region aliasRegion = new(); + Region heapRegion = new(); + Region stackRegion = new(); + Region tlsIoRegion = new(); ulong codeRegionSize; ulong stackAndTlsIoStart; @@ -238,8 +239,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory stackAndTlsIoEnd = 0; } break; - - default: throw new ArgumentException(nameof(addrSpaceType)); + default: + throw new ArgumentException($"AddressSpaceType bigger than {(uint)AddressSpaceType.Addr39Bits}: {(uint)addrSpaceType}", nameof(addrSpaceType)); } CodeRegionEnd = CodeRegionStart + codeRegionSize; @@ -374,10 +375,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private long GetRandomValue(long min, long max) { - if (_randomNumberGenerator == null) - { - _randomNumberGenerator = new MersenneTwister(0); - } + _randomNumberGenerator ??= new MersenneTwister(0); return _randomNumberGenerator.GenRandomNumber(min, max); } @@ -442,7 +440,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory lock (_blockManager) { - KPageList currentPageList = new KPageList(); + KPageList currentPageList = new(); GetPhysicalRegions(address, size, currentPageList); @@ -486,13 +484,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public Result MapNormalMemory(long address, long size, KMemoryPermission permission) + public static Result MapNormalMemory(long address, long size, KMemoryPermission permission) { // TODO. return Result.Success; } - public Result MapIoMemory(long address, long size, KMemoryPermission permission) + public static Result MapIoMemory(long address, long size, KMemoryPermission permission) { // TODO. return Result.Success; @@ -1174,8 +1172,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidMemState; } - KPageList srcPageList = new KPageList(); - KPageList dstPageList = new KPageList(); + KPageList srcPageList = new(); + KPageList dstPageList = new(); srcPageTable.GetPhysicalRegions(src, size, srcPageList); GetPhysicalRegions(dst, size, dstPageList); @@ -1681,11 +1679,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory switch (state) { - case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break; - case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break; - case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break; - - default: return KernelResult.InvalidCombination; + case MemoryState.IpcBuffer0: + stateMask = MemoryState.IpcSendAllowedType0; + break; + case MemoryState.IpcBuffer1: + stateMask = MemoryState.IpcSendAllowedType1; + break; + case MemoryState.IpcBuffer3: + stateMask = MemoryState.IpcSendAllowedType3; + break; + default: + return KernelResult.InvalidCombination; } KMemoryPermission permissionMask = permission == KMemoryPermission.ReadAndWrite @@ -1822,9 +1826,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong va = 0; - for (int unit = MappingUnitSizes.Length - 1; unit >= 0 && va == 0; unit--) + for (int unit = _mappingUnitSizes.Length - 1; unit >= 0 && va == 0; unit--) { - int alignment = MappingUnitSizes[unit]; + int alignment = _mappingUnitSizes[unit]; va = AllocateVa(AliasRegionStart, regionPagesCount, neededPagesCount, alignment); } @@ -1937,7 +1941,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (srcPageTable.Supports4KBPages) { - KPageList pageList = new KPageList(); + KPageList pageList = new(); srcPageTable.GetPhysicalRegions(addressRounded, alignedSize, pageList); result = MapPages(currentVa, pageList, permission, MemoryMapFlags.None); @@ -2075,11 +2079,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory switch (state) { - case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break; - case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break; - case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break; - - default: return KernelResult.InvalidCombination; + case MemoryState.IpcBuffer0: + stateMask = MemoryState.IpcSendAllowedType0; + break; + case MemoryState.IpcBuffer1: + stateMask = MemoryState.IpcSendAllowedType1; + break; + case MemoryState.IpcBuffer3: + stateMask = MemoryState.IpcSendAllowedType3; + break; + default: + return KernelResult.InvalidCombination; } MemoryAttribute attributeMask = @@ -2392,7 +2402,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (pageList != null) { - KPageList currentPageList = new KPageList(); + KPageList currentPageList = new(); GetPhysicalRegions(address, pagesCount * PageSize, currentPageList); @@ -3050,4 +3060,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// <exception cref="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> protected abstract void Write(ulong va, ReadOnlySpan<byte> data); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs index a0c19f9c1..498e6f8c8 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _pageList = null; } - public void Dispose() + public readonly void Dispose() { _pageList?.DecrementPagesReferenceCount(_manager); } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs index 5ec3cd724..e302ee443 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs @@ -72,4 +72,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return memoryManager.UnmapPages(address, _pageList, MemoryState.SharedMemory); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs index 9051e84cc..cd8c2e470 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { class KSlabHeap { - private LinkedList<ulong> _items; + private readonly LinkedList<ulong> _items; public KSlabHeap(ulong pa, ulong itemSize, ulong size) { @@ -47,4 +47,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs index b24495980..9f64532e7 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs @@ -127,4 +127,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs index 42407ffe2..36b1ec8c3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs @@ -3,20 +3,20 @@ using System; namespace Ryujinx.HLE.HOS.Kernel.Memory { [Flags] - enum MemoryAttribute : byte + enum MemoryAttribute : byte { None = 0, Mask = 0xff, - Borrowed = 1 << 0, - IpcMapped = 1 << 1, + Borrowed = 1 << 0, + IpcMapped = 1 << 1, DeviceMapped = 1 << 2, - Uncached = 1 << 3, + Uncached = 1 << 3, IpcAndDeviceMapped = IpcMapped | DeviceMapped, BorrowedAndIpcMapped = Borrowed | IpcMapped, - DeviceMappedAndUncached = DeviceMapped | Uncached + DeviceMappedAndUncached = DeviceMapped | Uncached, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs index 563b817d8..068cdbb88 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs @@ -5,16 +5,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory [Flags] enum KMemoryPermission : uint { - None = 0, + None = 0, UserMask = Read | Write | Execute, - Mask = uint.MaxValue, + Mask = uint.MaxValue, - Read = 1 << 0, - Write = 1 << 1, - Execute = 1 << 2, + Read = 1 << 0, + Write = 1 << 1, + Execute = 1 << 2, DontCare = 1 << 28, - ReadAndWrite = Read | Write, - ReadAndExecute = Read | Execute + ReadAndWrite = Read | Write, + ReadAndExecute = Read | Execute, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs index ad719bded..18784bf91 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs @@ -3,8 +3,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory enum MemoryRegion { Application = 0, - Applet = 1, - Service = 2, - NvServices = 3 + Applet = 1, + Service = 2, + NvServices = 3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs index d3b61780c..273b58e5e 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs @@ -5,46 +5,46 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory [Flags] enum MemoryState : uint { - Unmapped = 0x00000000, - Io = 0x00002001, - Normal = 0x00042002, - CodeStatic = 0x00DC7E03, - CodeMutable = 0x03FEBD04, - Heap = 0x037EBD05, - SharedMemory = 0x00402006, - ModCodeStatic = 0x00DD7E08, - ModCodeMutable = 0x03FFBD09, - IpcBuffer0 = 0x005C3C0A, - Stack = 0x005C3C0B, - ThreadLocal = 0x0040200C, + Unmapped = 0x00000000, + Io = 0x00002001, + Normal = 0x00042002, + CodeStatic = 0x00DC7E03, + CodeMutable = 0x03FEBD04, + Heap = 0x037EBD05, + SharedMemory = 0x00402006, + ModCodeStatic = 0x00DD7E08, + ModCodeMutable = 0x03FFBD09, + IpcBuffer0 = 0x005C3C0A, + Stack = 0x005C3C0B, + ThreadLocal = 0x0040200C, TransferMemoryIsolated = 0x015C3C0D, - TransferMemory = 0x005C380E, - ProcessMemory = 0x0040380F, - Reserved = 0x00000010, - IpcBuffer1 = 0x005C3811, - IpcBuffer3 = 0x004C2812, - KernelStack = 0x00002013, - CodeReadOnly = 0x00402214, - CodeWritable = 0x00402015, - UserMask = 0xff, - Mask = 0xffffffff, + TransferMemory = 0x005C380E, + ProcessMemory = 0x0040380F, + Reserved = 0x00000010, + IpcBuffer1 = 0x005C3811, + IpcBuffer3 = 0x004C2812, + KernelStack = 0x00002013, + CodeReadOnly = 0x00402214, + CodeWritable = 0x00402015, + UserMask = 0xff, + Mask = 0xffffffff, - PermissionChangeAllowed = 1 << 8, + PermissionChangeAllowed = 1 << 8, ForceReadWritableByDebugSyscalls = 1 << 9, - IpcSendAllowedType0 = 1 << 10, - IpcSendAllowedType3 = 1 << 11, - IpcSendAllowedType1 = 1 << 12, - ProcessPermissionChangeAllowed = 1 << 14, - MapAllowed = 1 << 15, - UnmapProcessCodeMemoryAllowed = 1 << 16, - TransferMemoryAllowed = 1 << 17, - QueryPhysicalAddressAllowed = 1 << 18, - MapDeviceAllowed = 1 << 19, - MapDeviceAlignedAllowed = 1 << 20, - IpcBufferAllowed = 1 << 21, - IsPoolAllocated = 1 << 22, - MapProcessAllowed = 1 << 23, - AttributeChangeAllowed = 1 << 24, - CodeMemoryAllowed = 1 << 25 + IpcSendAllowedType0 = 1 << 10, + IpcSendAllowedType3 = 1 << 11, + IpcSendAllowedType1 = 1 << 12, + ProcessPermissionChangeAllowed = 1 << 14, + MapAllowed = 1 << 15, + UnmapProcessCodeMemoryAllowed = 1 << 16, + TransferMemoryAllowed = 1 << 17, + QueryPhysicalAddressAllowed = 1 << 18, + MapDeviceAllowed = 1 << 19, + MapDeviceAlignedAllowed = 1 << 20, + IpcBufferAllowed = 1 << 21, + IsPoolAllocated = 1 << 22, + MapProcessAllowed = 1 << 23, + AttributeChangeAllowed = 1 << 24, + CodeMemoryAllowed = 1 << 25, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs index 66d56fe31..dd133ee15 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs @@ -19,4 +19,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return (uint)BitOperations.TrailingZeroCount(type.GetFlag()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs index 51d92316d..9812eea0c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs @@ -2,18 +2,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { enum CapabilityType : uint { - CorePriority = (1u << 3) - 1, - SyscallMask = (1u << 4) - 1, - MapRange = (1u << 6) - 1, - MapIoPage = (1u << 7) - 1, - MapRegion = (1u << 10) - 1, + CorePriority = (1u << 3) - 1, + SyscallMask = (1u << 4) - 1, + MapRange = (1u << 6) - 1, + MapIoPage = (1u << 7) - 1, + MapRegion = (1u << 10) - 1, InterruptPair = (1u << 11) - 1, - ProgramType = (1u << 13) - 1, + ProgramType = (1u << 13) - 1, KernelVersion = (1u << 14) - 1, - HandleTable = (1u << 15) - 1, - DebugFlags = (1u << 16) - 1, + HandleTable = (1u << 15) - 1, + DebugFlags = (1u << 16) - 1, - Invalid = 0u, - Padding = ~0u + Invalid = 0u, + Padding = ~0u, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs index 8fee5c0d1..cbfef588c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24; - private KProcess _owner; + private readonly KProcess _owner; private class Image { @@ -27,12 +27,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public Image(ulong baseAddress, ulong size, ElfSymbol[] symbols) { BaseAddress = baseAddress; - Size = size; - Symbols = symbols; + Size = size; + Symbols = symbols; } } - private List<Image> _images; + private readonly List<Image> _images; private int _loaded; @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process var context = thread.Context; - StringBuilder trace = new StringBuilder(); + StringBuilder trace = new(); trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}"); @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process var context = thread.Context; - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); string GetReg(int x) { @@ -145,11 +145,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return sb.ToString(); } - private bool TryGetSubName(Image image, ulong address, out ElfSymbol symbol) + private static bool TryGetSubName(Image image, ulong address, out ElfSymbol symbol) { address -= image.BaseAddress; - int left = 0; + int left = 0; int right = image.Symbols.Length - 1; while (left <= right) @@ -190,9 +190,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public ulong Offset; public ulong SubOffset; - public string ImageDisplay => $"{ImageName}:0x{Offset:x4}"; - public string SubDisplay => SubOffset == 0 ? SubName : $"{SubName}:0x{SubOffset:x4}"; - public string SpDisplay => SubOffset == 0 ? "SP" : $"SP:-0x{SubOffset:x4}"; + public readonly string ImageDisplay => $"{ImageName}:0x{Offset:x4}"; + public readonly string SubDisplay => SubOffset == 0 ? SubName : $"{SubName}:0x{SubOffset:x4}"; + public readonly string SpDisplay => SubOffset == 0 ? "SP" : $"SP:-0x{SubOffset:x4}"; } private bool AnalyzePointer(out PointerInfo info, ulong address, KThread thread) @@ -324,7 +324,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private void ScanMemoryForTextSegments() { ulong oldAddress = 0; - ulong address = 0; + ulong address = 0; while (address >= oldAddress) { @@ -355,7 +355,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return; } - Dictionary<ElfDynamicTag, ulong> dynamic = new Dictionary<ElfDynamicTag, ulong>(); + Dictionary<ElfDynamicTag, ulong> dynamic = new(); int mod0Magic = memory.Read<int>(mod0Offset + 0x0); @@ -364,12 +364,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return; } - ulong dynamicOffset = memory.Read<uint>(mod0Offset + 0x4) + mod0Offset; - ulong bssStartOffset = memory.Read<uint>(mod0Offset + 0x8) + mod0Offset; - ulong bssEndOffset = memory.Read<uint>(mod0Offset + 0xc) + mod0Offset; + ulong dynamicOffset = memory.Read<uint>(mod0Offset + 0x4) + mod0Offset; + ulong bssStartOffset = memory.Read<uint>(mod0Offset + 0x8) + mod0Offset; + ulong bssEndOffset = memory.Read<uint>(mod0Offset + 0xc) + mod0Offset; ulong ehHdrStartOffset = memory.Read<uint>(mod0Offset + 0x10) + mod0Offset; - ulong ehHdrEndOffset = memory.Read<uint>(mod0Offset + 0x14) + mod0Offset; - ulong modObjOffset = memory.Read<uint>(mod0Offset + 0x18) + mod0Offset; + ulong ehHdrEndOffset = memory.Read<uint>(mod0Offset + 0x14) + mod0Offset; + ulong modObjOffset = memory.Read<uint>(mod0Offset + 0x18) + mod0Offset; bool isAArch32 = memory.Read<ulong>(dynamicOffset) > 0xFFFFFFFF || memory.Read<ulong>(dynamicOffset + 0x10) > 0xFFFFFFFF; @@ -381,14 +381,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if (isAArch32) { tagVal = memory.Read<uint>(dynamicOffset + 0); - value = memory.Read<uint>(dynamicOffset + 4); + value = memory.Read<uint>(dynamicOffset + 4); dynamicOffset += 0x8; } else { tagVal = memory.Read<ulong>(dynamicOffset + 0); - value = memory.Read<ulong>(dynamicOffset + 8); + value = memory.Read<ulong>(dynamicOffset + 8); dynamicOffset += 0x10; } @@ -413,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ulong strTblAddr = textOffset + strTab; ulong symTblAddr = textOffset + symTab; - List<ElfSymbol> symbols = new List<ElfSymbol>(); + List<ElfSymbol> symbols = new(); while (symTblAddr < strTblAddr) { @@ -430,7 +430,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - private ElfSymbol GetSymbol64(IVirtualMemoryManager memory, ulong address, ulong strTblAddr) + private static ElfSymbol GetSymbol64(IVirtualMemoryManager memory, ulong address, ulong strTblAddr) { ElfSymbol64 sym = memory.Read<ElfSymbol64>(address); @@ -446,7 +446,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size); } - private ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr) + private static ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr) { ElfSymbol32 sym = memory.Read<ElfSymbol32>(address); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs index 104fe578a..32a751325 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { private const int IdMasksCount = 8; - private int[] _idMasks; + private readonly int[] _idMasks; private int _nextFreeBitHint; @@ -67,17 +67,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private bool TestBit(int bit) { - return (_idMasks[_nextFreeBitHint / 32] & (1 << (_nextFreeBitHint & 31))) != 0; + return (_idMasks[bit / 32] & (1 << (bit & 31))) != 0; } private void SetBit(int bit) { - _idMasks[_nextFreeBitHint / 32] |= (1 << (_nextFreeBitHint & 31)); + _idMasks[bit / 32] |= (1 << (bit & 31)); } private void ClearBit(int bit) { - _idMasks[_nextFreeBitHint / 32] &= ~(1 << (_nextFreeBitHint & 31)); + _idMasks[bit / 32] &= ~(1 << (bit & 31)); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs index b5ca9b5e2..c80423b7a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs @@ -8,12 +8,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public int Index { get; private set; } - public ushort HandleId { get; set; } - public KAutoObject Obj { get; set; } + public ushort HandleId { get; set; } + public KAutoObject Obj { get; set; } public KHandleEntry(int index) { Index = index; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs index 6dd7e5b78..21ea6bb28 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs @@ -7,11 +7,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { class KHandleTable { - public const int SelfThreadHandle = (0x1ffff << 15) | 0; + public const int SelfThreadHandle = (0x1ffff << 15) | 0; public const int SelfProcessHandle = (0x1ffff << 15) | 1; - private readonly KernelContext _context; - private KHandleEntry[] _table; private KHandleEntry _tableHead; @@ -23,11 +21,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private ushort _idCounter; - public KHandleTable(KernelContext context) - { - _context = context; - } - public Result Initialize(uint size) { if (size > 1024) @@ -81,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _nextFreeEntry = entry.Next; - entry.Obj = obj; + entry.Obj = obj; entry.HandleId = _idCounter; _activeSlotsCount++; @@ -143,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { KHandleEntry entry = _table[index]; - entry.Obj = null; + entry.Obj = null; entry.Next = _nextFreeEntry; _nextFreeEntry = entry; @@ -154,14 +147,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public void SetReservedHandleObj(int handle, KAutoObject obj) { - int index = (handle >> 0) & 0x7fff; + int index = (handle >> 0) & 0x7fff; int handleId = (handle >> 15); lock (_table) { KHandleEntry entry = _table[index]; - entry.Obj = obj; + entry.Obj = obj; entry.HandleId = (ushort)handleId; obj.IncrementReferenceCount(); @@ -177,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return false; } - int index = (handle >> 0) & 0x7fff; + int index = (handle >> 0) & 0x7fff; int handleId = (handle >> 15); KAutoObject obj = null; @@ -192,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if ((obj = entry.Obj) != null && entry.HandleId == handleId) { - entry.Obj = null; + entry.Obj = null; entry.Next = _nextFreeEntry; _nextFreeEntry = entry; @@ -214,7 +207,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public T GetObject<T>(int handle) where T : KAutoObject { - int index = (handle >> 0) & 0x7fff; + int index = (handle >> 0) & 0x7fff; int handleId = (handle >> 15); lock (_table) @@ -273,7 +266,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } entry.Obj.DecrementReferenceCount(); - entry.Obj = null; + entry.Obj = null; entry.Next = _nextFreeEntry; _nextFreeEntry = entry; @@ -282,4 +275,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index c284243ae..6008548be 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -27,8 +27,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public KPageTableBase MemoryManager { get; private set; } - private SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages; - private SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages; + private readonly SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages; + private readonly SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages; public int DefaultCpuCore { get; set; } @@ -66,19 +66,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public bool IsApplication { get; private set; } public ulong Pid { get; private set; } - private long _creationTimestamp; private ulong _entrypoint; private ThreadStart _customThreadStart; private ulong _imageSize; private ulong _mainThreadStackSize; private ulong _memoryUsageCapacity; - private int _version; public KHandleTable HandleTable { get; private set; } public ulong UserExceptionContextAddress { get; private set; } - private LinkedList<KThread> _threads; + private readonly LinkedList<KThread> _threads; public bool IsPaused { get; private set; } @@ -107,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process PinnedThreads = new KThread[KScheduler.CpuCoresCount]; // TODO: Remove once we no longer need to initialize it externally. - HandleTable = new KHandleTable(context); + HandleTable = new KHandleTable(); _threads = new LinkedList<KThread>(); @@ -347,10 +345,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process State = ProcessState.Created; - _creationTimestamp = PerformanceCounter.ElapsedMilliseconds; - Flags = creationInfo.Flags; - _version = creationInfo.Version; TitleId = creationInfo.TitleId; _entrypoint = creationInfo.CodeAddress; _imageSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize; @@ -370,8 +365,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process MemoryManager.AliasRegionEnd - MemoryManager.AliasRegionStart; break; - - default: throw new InvalidOperationException($"Invalid MMU flags value 0x{Flags:x2}."); + default: + throw new InvalidOperationException($"Invalid MMU flags value 0x{Flags:x2}."); } GenerateRandomEntropy(); @@ -476,9 +471,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process Result result = Result.Success; - KTlsPageInfo pageInfo; - if (_fullTlsPages.TryGetValue(tlsPageAddr, out pageInfo)) + if (_fullTlsPages.TryGetValue(tlsPageAddr, out KTlsPageInfo pageInfo)) { // TLS page was full, free slot and move to free pages tree. _fullTlsPages.Remove(tlsPageAddr); @@ -525,10 +519,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } +#pragma warning disable CA1822 // Mark member as static private void GenerateRandomEntropy() { // TODO. } +#pragma warning restore CA1822 public Result Start(int mainThreadPriority, ulong stackSize) { @@ -549,7 +545,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if (_mainThreadStackSize != 0) { - throw new InvalidOperationException("Trying to start a process with a invalid state!"); + throw new InvalidOperationException("Trying to start a process with an invalid state!"); } ulong stackSizeRounded = BitUtils.AlignUp<ulong>(stackSize, KPageTableBase.PageSize); @@ -648,7 +644,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - HandleTable = new KHandleTable(KernelContext); + HandleTable = new KHandleTable(); result = HandleTable.Initialize(Capabilities.HandleTableSize); @@ -1018,22 +1014,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - private void SignalExitToDebugTerminated() + private static void SignalExitToDebugTerminated() { // TODO: Debug events. } - private void SignalExitToDebugExited() + private static void SignalExitToDebugExited() { // TODO: Debug events. } private void SignalExit() { - if (ResourceLimit != null) - { - ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage()); - } + ResourceLimit?.Release(LimitableResource.Memory, GetMemoryUsage()); KernelContext.CriticalSection.Enter(); @@ -1075,7 +1068,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ProcessCreationFlags.AddressSpace64BitDeprecated => 36, ProcessCreationFlags.AddressSpace32BitWithoutAlias => 32, ProcessCreationFlags.AddressSpace64Bit => 39, - _ => 39 + _ => 39, }; bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit); @@ -1184,10 +1177,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - public bool IsExceptionUserThread(KThread thread) + public static bool IsExceptionUserThread(KThread thread) { // TODO return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index c99e31123..314aadf36 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -11,13 +11,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public byte[] SvcAccessMask { get; } public byte[] IrqAccessMask { get; } - public ulong AllowedCpuCoresMask { get; private set; } + public ulong AllowedCpuCoresMask { get; private set; } public ulong AllowedThreadPriosMask { get; private set; } - public uint DebuggingFlags { get; private set; } - public uint HandleTableSize { get; private set; } + public uint DebuggingFlags { get; private set; } + public uint HandleTableSize { get; private set; } public uint KernelReleaseVersion { get; private set; } - public uint ApplicationType { get; private set; } + public uint ApplicationType { get; private set; } public KProcessCapabilities() { @@ -28,10 +28,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager) { - AllowedCpuCoresMask = 0xf; + AllowedCpuCoresMask = 0xf; AllowedThreadPriosMask = ulong.MaxValue; - DebuggingFlags &= ~3u; - KernelReleaseVersion = KProcess.KernelVersionPacked; + DebuggingFlags &= ~3u; + KernelReleaseVersion = KProcess.KernelVersionPacked; return Parse(capabilities, memoryManager); } @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } long address = ((long)prevCap << 5) & 0xffffff000; - long size = ((long)cap << 5) & 0xfffff000; + long size = ((long)cap << 5) & 0xfffff000; if (((ulong)(address + size - 1) >> 36) != 0) { @@ -101,11 +101,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if ((cap >> 31) != 0) { - result = memoryManager.MapNormalMemory(address, size, perm); + result = KPageTableBase.MapNormalMemory(address, size, perm); } else { - result = memoryManager.MapIoMemory(address, size, perm); + result = KPageTableBase.MapIoMemory(address, size, perm); } if (result != Result.Success) @@ -144,168 +144,168 @@ namespace Ryujinx.HLE.HOS.Kernel.Process switch (code) { case CapabilityType.CorePriority: - { - if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) { - return KernelResult.InvalidCapability; + if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) + { + return KernelResult.InvalidCapability; + } + + uint lowestCpuCore = (cap >> 16) & 0xff; + uint highestCpuCore = (cap >> 24) & 0xff; + + if (lowestCpuCore > highestCpuCore) + { + return KernelResult.InvalidCombination; + } + + uint highestThreadPrio = (cap >> 4) & 0x3f; + uint lowestThreadPrio = (cap >> 10) & 0x3f; + + if (lowestThreadPrio > highestThreadPrio) + { + return KernelResult.InvalidCombination; + } + + if (highestCpuCore >= KScheduler.CpuCoresCount) + { + return KernelResult.InvalidCpuCore; + } + + AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore); + AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio); + + break; } - uint lowestCpuCore = (cap >> 16) & 0xff; - uint highestCpuCore = (cap >> 24) & 0xff; - - if (lowestCpuCore > highestCpuCore) - { - return KernelResult.InvalidCombination; - } - - uint highestThreadPrio = (cap >> 4) & 0x3f; - uint lowestThreadPrio = (cap >> 10) & 0x3f; - - if (lowestThreadPrio > highestThreadPrio) - { - return KernelResult.InvalidCombination; - } - - if (highestCpuCore >= KScheduler.CpuCoresCount) - { - return KernelResult.InvalidCpuCore; - } - - AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore); - AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio); - - break; - } - case CapabilityType.SyscallMask: - { - int slot = ((int)cap >> 29) & 7; - - int svcSlotMask = 1 << slot; - - if ((mask1 & svcSlotMask) != 0) { - return KernelResult.InvalidCombination; - } + int slot = ((int)cap >> 29) & 7; - mask1 |= svcSlotMask; + int svcSlotMask = 1 << slot; - uint svcMask = (cap >> 5) & 0xffffff; - - int baseSvc = slot * 24; - - for (int index = 0; index < 24; index++) - { - if (((svcMask >> index) & 1) == 0) + if ((mask1 & svcSlotMask) != 0) { - continue; + return KernelResult.InvalidCombination; } - int svcId = baseSvc + index; + mask1 |= svcSlotMask; - if (svcId >= KernelConstants.SupervisorCallCount) + uint svcMask = (cap >> 5) & 0xffffff; + + int baseSvc = slot * 24; + + for (int index = 0; index < 24; index++) { - return KernelResult.MaximumExceeded; + if (((svcMask >> index) & 1) == 0) + { + continue; + } + + int svcId = baseSvc + index; + + if (svcId >= KernelConstants.SupervisorCallCount) + { + return KernelResult.MaximumExceeded; + } + + SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7)); } - SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7)); + break; } - break; - } - case CapabilityType.MapIoPage: - { - long address = ((long)cap << 4) & 0xffffff000; + { + long address = ((long)cap << 4) & 0xffffff000; - memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite); + KPageTableBase.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite); - break; - } + break; + } case CapabilityType.MapRegion: - { - // TODO: Implement capabilities for MapRegion + { + // TODO: Implement capabilities for MapRegion - break; - } + break; + } case CapabilityType.InterruptPair: - { - // TODO: GIC distributor check. - int irq0 = ((int)cap >> 12) & 0x3ff; - int irq1 = ((int)cap >> 22) & 0x3ff; - - if (irq0 != 0x3ff) { - IrqAccessMask[irq0 / 8] |= (byte)(1 << (irq0 & 7)); - } + // TODO: GIC distributor check. + int irq0 = ((int)cap >> 12) & 0x3ff; + int irq1 = ((int)cap >> 22) & 0x3ff; - if (irq1 != 0x3ff) - { - IrqAccessMask[irq1 / 8] |= (byte)(1 << (irq1 & 7)); - } + if (irq0 != 0x3ff) + { + IrqAccessMask[irq0 / 8] |= (byte)(1 << (irq0 & 7)); + } - break; - } + if (irq1 != 0x3ff) + { + IrqAccessMask[irq1 / 8] |= (byte)(1 << (irq1 & 7)); + } + + break; + } case CapabilityType.ProgramType: - { - uint applicationType = (cap >> 14); - - if (applicationType > 7) { - return KernelResult.ReservedValue; + uint applicationType = (cap >> 14); + + if (applicationType > 7) + { + return KernelResult.ReservedValue; + } + + ApplicationType = applicationType; + + break; } - ApplicationType = applicationType; - - break; - } - case CapabilityType.KernelVersion: - { - // Note: This check is bugged on kernel too, we are just replicating the bug here. - if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000) { - return KernelResult.ReservedValue; + // Note: This check is bugged on kernel too, we are just replicating the bug here. + if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000) + { + return KernelResult.ReservedValue; + } + + KernelReleaseVersion = cap; + + break; } - KernelReleaseVersion = cap; - - break; - } - case CapabilityType.HandleTable: - { - uint handleTableSize = cap >> 26; - - if (handleTableSize > 0x3ff) { - return KernelResult.ReservedValue; + uint handleTableSize = cap >> 26; + + if (handleTableSize > 0x3ff) + { + return KernelResult.ReservedValue; + } + + HandleTableSize = handleTableSize; + + break; } - HandleTableSize = handleTableSize; - - break; - } - case CapabilityType.DebugFlags: - { - uint debuggingFlags = cap >> 19; - - if (debuggingFlags > 3) { - return KernelResult.ReservedValue; + uint debuggingFlags = cap >> 19; + + if (debuggingFlags > 3) + { + return KernelResult.ReservedValue; + } + + DebuggingFlags &= ~3u; + DebuggingFlags |= debuggingFlags; + + break; } - - DebuggingFlags &= ~3u; - DebuggingFlags |= debuggingFlags; - - break; - } - - default: return KernelResult.InvalidCapability; + default: + return KernelResult.InvalidCapability; } return Result.Success; @@ -325,4 +325,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return mask << (int)min; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs index f55e3c10e..7fcd87b6c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs @@ -74,4 +74,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _isSlotFree[(address - PageVirtualAddress) / TlsEntrySize] = true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs index 0fde495ca..279fa13e8 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs @@ -7,14 +7,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { private const int TlsEntrySize = 0x200; - private long _pagePosition; + private readonly long _pagePosition; private int _usedSlots; - private bool[] _slots; + private readonly bool[] _slots; public bool IsEmpty => _usedSlots == 0; - public bool IsFull => _usedSlots == _slots.Length; + public bool IsFull => _usedSlots == _slots.Length; public KTlsPageManager(long pagePosition) { @@ -58,4 +58,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _usedSlots--; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs index a79978ac4..c68190d69 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Ryujinx.HLE.HOS.Kernel.Process { [Flags] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum ProcessCreationFlags { Is64Bit = 1 << 0, @@ -36,6 +38,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process IsApplication | DeprecatedUseSecureMemory | PoolPartitionMask | - OptimizeMemoryAllocation + OptimizeMemoryAllocation, } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs index c05bb574e..b5e5c29bb 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs @@ -34,4 +34,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process SystemResourcePagesCount = systemResourcePagesCount; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs index 77fcdf33b..b8118fbb4 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs @@ -7,9 +7,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { public ulong Pc => 0UL; - public ulong CntfrqEl0 { get; set; } - public ulong CntpctEl0 => 0UL; - public long TpidrEl0 { get; set; } public long TpidrroEl0 { get; set; } @@ -43,4 +40,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs index 5ef3077e3..e6c7e33e7 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs @@ -2,13 +2,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { enum ProcessState : byte { - Created = 0, + Created = 0, CreatedAttached = 1, - Started = 2, - Crashed = 3, - Attached = 4, - Exiting = 5, - Exited = 6, - DebugSuspended = 7 + Started = 2, + Crashed = 3, + Attached = 4, + Exiting = 5, + Exited = 6, + DebugSuspended = 7, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs index 4cf67172d..8eafd96c2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs @@ -13,12 +13,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public ProcessTamperInfo(KProcess process, IEnumerable<string> buildIds, IEnumerable<ulong> codeAddresses, ulong heapAddress, ulong aliasAddress, ulong aslrAddress) { - Process = process; - BuildIds = buildIds; + Process = process; + BuildIds = buildIds; CodeAddresses = codeAddresses; - HeapAddress = heapAddress; - AliasAddress = aliasAddress; - AslrAddress = aslrAddress; + HeapAddress = heapAddress; + AliasAddress = aliasAddress; + AslrAddress = aslrAddress; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs index 511ee99af..09e159a83 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs @@ -5,6 +5,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall Map, MapToOwner, Unmap, - UnmapFromOwner + UnmapFromOwner, }; -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs index 3cf7ba748..2ca0d03a4 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InfoType.cs @@ -29,6 +29,6 @@ IsApplication, FreeThreadCount, ThreadTickCount, - MesosphereCurrentProcess = 65001 + MesosphereCurrentProcess = 65001, } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs index a71cce1fe..c900781e9 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/MemoryInfo.cs @@ -11,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public KMemoryPermission Permission; public int IpcRefCount; public int DeviceRefCount; -#pragma warning disable CS0414 - private int _padding; -#pragma warning restore CS0414 +#pragma warning disable CS0414, IDE0052 // Remove unread private member + private readonly int _padding; +#pragma warning restore CS0414, IDE0052 public MemoryInfo( ulong address, @@ -34,4 +34,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall _padding = 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 3163c3487..82632f44c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KHandleTable handleTable = KernelStatic.GetCurrentProcess().HandleTable; - KProcess process = new KProcess(_context); + KProcess process = new(_context); using var _ = new OnScopeExit(process.DecrementReferenceCount); @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ProcessCreationFlags.PoolPartitionApplet => MemoryRegion.Applet, ProcessCreationFlags.PoolPartitionSystem => MemoryRegion.Service, ProcessCreationFlags.PoolPartitionSystemNonSecure => MemoryRegion.NvServices, - _ => MemoryRegion.NvServices + _ => MemoryRegion.NvServices, }; Result result = process.Initialize( @@ -138,6 +138,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return handleTable.GenerateHandle(process, out handle); } +#pragma warning disable CA1822 // Mark member as static public Result StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize) { KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle); @@ -170,14 +171,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x5f)] +#pragma warning disable CA1822 // Mark member as static public Result FlushProcessDataCache(int processHandle, ulong address, ulong size) { // FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0. // As we don't support (and don't actually need) to flush the cache, this is stubbed. return Result.Success; } +#pragma warning restore CA1822 // IPC @@ -251,6 +255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x22)] +#pragma warning disable CA1822 // Mark member as static public Result SendSyncRequestWithUserBuffer( [PointerSized] ulong messagePtr, [PointerSized] ulong messageSize, @@ -300,6 +305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } +#pragma warning restore CA1822 [Svc(0x23)] public Result SendAsyncRequestWithUserBuffer( @@ -351,7 +357,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } else { - KEvent doneEvent = new KEvent(_context); + KEvent doneEvent = new(_context); result = currentProcess.HandleTable.GenerateHandle(doneEvent.ReadableEvent, out doneEventHandle); @@ -408,7 +414,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall if (isLight) { - KLightSession session = new KLightSession(_context); + KLightSession session = new(_context); result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle); @@ -429,7 +435,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } else { - KSession session = new KSession(_context); + KSession session = new(_context); result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle); @@ -745,7 +751,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.MaximumExceeded; } - KPort port = new KPort(_context, maxSessions, isLight, name); + KPort port = new(_context, maxSessions, isLight, name); KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -798,7 +804,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KAutoObject.RemoveName(_context, name); } - KPort port = new KPort(_context, maxSessions, false, null); + KPort port = new(_context, maxSessions, false, null); KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -889,6 +895,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(2)] +#pragma warning disable CA1822 // Mark member as static public Result SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) { if (!PageAligned(address)) @@ -920,8 +927,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission); } +#pragma warning restore CA1822 [Svc(3)] +#pragma warning disable CA1822 // Mark member as static public Result SetMemoryAttribute( [PointerSized] ulong address, [PointerSized] ulong size, @@ -961,8 +970,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } +#pragma warning restore CA1822 [Svc(4)] +#pragma warning disable CA1822 // Mark member as static public Result MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) { if (!PageAligned(src | dst)) @@ -998,8 +1009,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return process.MemoryManager.Map(dst, src, size); } +#pragma warning restore CA1822 [Svc(5)] +#pragma warning disable CA1822 // Mark member as static public Result UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size) { if (!PageAligned(src | dst)) @@ -1035,6 +1048,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return process.MemoryManager.Unmap(dst, src, size); } +#pragma warning restore CA1822 [Svc(6)] public Result QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address) @@ -1051,6 +1065,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } +#pragma warning disable CA1822 // Mark member as static public Result QueryMemory(out MemoryInfo info, out ulong pageInfo, ulong address) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -1070,8 +1085,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x13)] +#pragma warning disable CA1822 // Mark member as static public Result MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) { if (!PageAligned(address)) @@ -1117,8 +1134,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall currentProcess, permission); } +#pragma warning restore CA1822 [Svc(0x14)] +#pragma warning disable CA1822 // Mark member as static public Result UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) @@ -1158,6 +1177,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall size, currentProcess); } +#pragma warning restore CA1822 [Svc(0x15)] public Result CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) @@ -1205,7 +1225,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - KTransferMemory transferMemory = new KTransferMemory(_context); + KTransferMemory transferMemory = new(_context); Result result = transferMemory.Initialize(address, size, permission); @@ -1224,6 +1244,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x51)] +#pragma warning disable CA1822 // Mark member as static public Result MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission) { if (!PageAligned(address)) @@ -1269,8 +1290,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall currentProcess, permission); } +#pragma warning restore CA1822 [Svc(0x52)] +#pragma warning disable CA1822 // Mark member as static public Result UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) @@ -1310,8 +1333,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall size, currentProcess); } +#pragma warning restore CA1822 [Svc(0x2c)] +#pragma warning disable CA1822 // Mark member as static public Result MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) @@ -1346,8 +1371,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return process.MemoryManager.MapPhysicalMemory(address, size); } +#pragma warning restore CA1822 [Svc(0x2d)] +#pragma warning disable CA1822 // Mark member as static public Result UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size) { if (!PageAligned(address)) @@ -1382,6 +1409,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return process.MemoryManager.UnmapPhysicalMemory(address, size); } +#pragma warning restore CA1822 [Svc(0x4b)] public Result CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size) @@ -1403,7 +1431,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - KCodeMemory codeMemory = new KCodeMemory(_context); + KCodeMemory codeMemory = new(_context); using var _ = new OnScopeExit(codeMemory.DecrementReferenceCount); @@ -1425,6 +1453,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x4c)] +#pragma warning disable CA1822 // Mark member as static public Result ControlCodeMemory( int handle, CodeMemoryOperation op, @@ -1498,11 +1527,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return codeMemory.UnmapFromOwner(address, size); - default: return KernelResult.InvalidEnumValue; + default: + return KernelResult.InvalidEnumValue; } } +#pragma warning restore CA1822 [Svc(0x73)] +#pragma warning disable CA1822 // Mark member as static public Result SetProcessMemoryPermission( int handle, [PointerSized] ulong src, @@ -1543,8 +1575,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission); } +#pragma warning restore CA1822 [Svc(0x74)] +#pragma warning disable CA1822 // Mark member as static public Result MapProcessMemory( [PointerSized] ulong dst, int handle, @@ -1580,7 +1614,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemRange; } - KPageList pageList = new KPageList(); + KPageList pageList = new(); Result result = srcProcess.MemoryManager.GetPagesIfStateEquals( src, @@ -1600,8 +1634,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite); } +#pragma warning restore CA1822 [Svc(0x75)] +#pragma warning disable CA1822 // Mark member as static public Result UnmapProcessMemory( [PointerSized] ulong dst, int handle, @@ -1646,8 +1682,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x77)] +#pragma warning disable CA1822 // Mark member as static public Result MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) { if (!PageAligned(dst) || !PageAligned(src)) @@ -1684,8 +1722,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size); } +#pragma warning restore CA1822 [Svc(0x78)] +#pragma warning disable CA1822 // Mark member as static public Result UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) { if (!PageAligned(dst) || !PageAligned(src)) @@ -1722,6 +1762,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size); } +#pragma warning restore CA1822 private static bool PageAligned(ulong address) { @@ -1731,6 +1772,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall // System [Svc(0x7b)] +#pragma warning disable CA1822 // Mark member as static public Result TerminateProcess(int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -1759,12 +1801,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } +#pragma warning restore CA1822 [Svc(7)] +#pragma warning disable CA1822 // Mark member as static public void ExitProcess() { KernelStatic.GetCurrentProcess().TerminateCurrentProcess(); } +#pragma warning restore CA1822 [Svc(0x11)] public Result SignalEvent(int handle) @@ -1857,6 +1902,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x26)] +#pragma warning disable CA1822 // Mark member as static public void Break(ulong reason) { KThread currentThread = KernelStatic.GetCurrentThread(); @@ -1882,8 +1928,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall Logger.Debug?.Print(LogClass.KernelSvc, "Debugger triggered."); } } +#pragma warning restore CA1822 [Svc(0x27)] +#pragma warning disable CA1822 // Mark member as static public void OutputDebugString([PointerSized] ulong strPtr, [PointerSized] ulong size) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -1892,6 +1940,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall Logger.Warning?.Print(LogClass.KernelSvc, str); } +#pragma warning restore CA1822 [Svc(0x29)] public Result GetInfo(out ulong value, InfoType id, int handle, long subId) @@ -1937,33 +1986,56 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall switch (id) { - case InfoType.CoreMask: value = process.Capabilities.AllowedCpuCoresMask; break; - case InfoType.PriorityMask: value = process.Capabilities.AllowedThreadPriosMask; break; + case InfoType.CoreMask: + value = process.Capabilities.AllowedCpuCoresMask; + break; + case InfoType.PriorityMask: + value = process.Capabilities.AllowedThreadPriosMask; + break; - case InfoType.AliasRegionAddress: value = process.MemoryManager.AliasRegionStart; break; + case InfoType.AliasRegionAddress: + value = process.MemoryManager.AliasRegionStart; + break; case InfoType.AliasRegionSize: value = (process.MemoryManager.AliasRegionEnd - - process.MemoryManager.AliasRegionStart); break; + process.MemoryManager.AliasRegionStart); + break; - case InfoType.HeapRegionAddress: value = process.MemoryManager.HeapRegionStart; break; + case InfoType.HeapRegionAddress: + value = process.MemoryManager.HeapRegionStart; + break; case InfoType.HeapRegionSize: value = (process.MemoryManager.HeapRegionEnd - - process.MemoryManager.HeapRegionStart); break; + process.MemoryManager.HeapRegionStart); + break; - case InfoType.TotalMemorySize: value = process.GetMemoryCapacity(); break; + case InfoType.TotalMemorySize: + value = process.GetMemoryCapacity(); + break; - case InfoType.UsedMemorySize: value = process.GetMemoryUsage(); break; + case InfoType.UsedMemorySize: + value = process.GetMemoryUsage(); + break; - case InfoType.AslrRegionAddress: value = process.MemoryManager.GetAddrSpaceBaseAddr(); break; + case InfoType.AslrRegionAddress: + value = process.MemoryManager.GetAddrSpaceBaseAddr(); + break; - case InfoType.AslrRegionSize: value = process.MemoryManager.GetAddrSpaceSize(); break; + case InfoType.AslrRegionSize: + value = process.MemoryManager.GetAddrSpaceSize(); + break; - case InfoType.StackRegionAddress: value = process.MemoryManager.StackRegionStart; break; + case InfoType.StackRegionAddress: + value = process.MemoryManager.StackRegionStart; + break; case InfoType.StackRegionSize: value = (process.MemoryManager.StackRegionEnd - - process.MemoryManager.StackRegionStart); break; + process.MemoryManager.StackRegionStart); + break; - case InfoType.SystemResourceSizeTotal: value = process.PersonalMmHeapPagesCount * KPageTableBase.PageSize; break; + case InfoType.SystemResourceSizeTotal: + value = process.PersonalMmHeapPagesCount * KPageTableBase.PageSize; + break; case InfoType.SystemResourceSizeUsed: if (process.PersonalMmHeapPagesCount != 0) @@ -1973,15 +2045,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall break; - case InfoType.ProgramId: value = process.TitleId; break; + case InfoType.ProgramId: + value = process.TitleId; + break; - case InfoType.UserExceptionContextAddress: value = process.UserExceptionContextAddress; break; + case InfoType.UserExceptionContextAddress: + value = process.UserExceptionContextAddress; + break; - case InfoType.TotalNonSystemMemorySize: value = process.GetMemoryCapacityWithoutPersonalMmHeap(); break; + case InfoType.TotalNonSystemMemorySize: + value = process.GetMemoryCapacityWithoutPersonalMmHeap(); + break; - case InfoType.UsedNonSystemMemorySize: value = process.GetMemoryUsageWithoutPersonalMmHeap(); break; + case InfoType.UsedNonSystemMemorySize: + value = process.GetMemoryUsageWithoutPersonalMmHeap(); + break; - case InfoType.IsApplication: value = process.IsApplication ? 1UL : 0UL; break; + case InfoType.IsApplication: + value = process.IsApplication ? 1UL : 0UL; + break; case InfoType.FreeThreadCount: if (process.ResourceLimit != null) @@ -2160,7 +2242,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall break; } - default: return KernelResult.InvalidEnumValue; + default: + return KernelResult.InvalidEnumValue; } return Result.Success; @@ -2169,7 +2252,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall [Svc(0x45)] public Result CreateEvent(out int wEventHandle, out int rEventHandle) { - KEvent Event = new KEvent(_context); + KEvent Event = new(_context); KProcess process = KernelStatic.GetCurrentProcess(); @@ -2269,7 +2352,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall switch (id) { // Memory region capacity. - case 0: value = (long)region.Size; break; + case 0: + value = (long)region.Size; + break; // Memory region free space. case 1: @@ -2291,8 +2376,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall switch (subId) { - case 0: value = _context.PrivilegedProcessLowestId; break; - case 1: value = _context.PrivilegedProcessHighestId; break; + case 0: + value = _context.PrivilegedProcessLowestId; + break; + case 1: + value = _context.PrivilegedProcessHighestId; + break; } } @@ -2300,6 +2389,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x30)] +#pragma warning disable CA1822 // Mark member as static public Result GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource) { limitValue = 0; @@ -2320,8 +2410,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x31)] +#pragma warning disable CA1822 // Mark member as static public Result GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource) { limitValue = 0; @@ -2342,8 +2434,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x37)] +#pragma warning disable CA1822 // Mark member as static public Result GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource) { peak = 0; @@ -2364,11 +2458,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x7d)] public Result CreateResourceLimit(out int handle) { - KResourceLimit limit = new KResourceLimit(_context); + KResourceLimit limit = new(_context); KProcess process = KernelStatic.GetCurrentProcess(); @@ -2376,6 +2471,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x7e)] +#pragma warning disable CA1822 // Mark member as static public Result SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue) { if (resource >= LimitableResource.Count) @@ -2392,6 +2488,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return resourceLimit.SetLimitValue(resource, limitValue); } +#pragma warning restore CA1822 // Thread @@ -2443,7 +2540,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.ResLimitExceeded; } - KThread thread = new KThread(_context); + KThread thread = new(_context); Result result = currentProcess.InitializeThread( thread, @@ -2471,6 +2568,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(9)] +#pragma warning disable CA1822 // Mark member as static public Result StartThread(int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2497,14 +2595,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidHandle; } } +#pragma warning restore CA1822 [Svc(0xa)] +#pragma warning disable CA1822 // Mark member as static public void ExitThread() { KThread currentThread = KernelStatic.GetCurrentThread(); currentThread.Exit(); } +#pragma warning restore CA1822 [Svc(0xb)] public void SleepThread(long timeout) @@ -2513,9 +2614,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { switch (timeout) { - case 0: KScheduler.Yield(_context); break; - case -1: KScheduler.YieldWithLoadBalancing(_context); break; - case -2: KScheduler.YieldToAnyThread(_context); break; + case 0: + KScheduler.Yield(_context); + break; + case -1: + KScheduler.YieldWithLoadBalancing(_context); + break; + case -2: + KScheduler.YieldToAnyThread(_context); + break; } } else @@ -2525,6 +2632,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0xc)] +#pragma warning disable CA1822 // Mark member as static public Result GetThreadPriority(out int priority, int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2544,8 +2652,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidHandle; } } +#pragma warning restore CA1822 [Svc(0xd)] +#pragma warning disable CA1822 // Mark member as static public Result SetThreadPriority(int handle, int priority) { // TODO: NPDM check. @@ -2563,8 +2673,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0xe)] +#pragma warning disable CA1822 // Mark member as static public Result GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2586,8 +2698,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidHandle; } } +#pragma warning restore CA1822 [Svc(0xf)] +#pragma warning disable CA1822 // Mark member as static public Result SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -2635,14 +2749,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return thread.SetCoreAndAffinityMask(preferredCore, affinityMask); } +#pragma warning restore CA1822 [Svc(0x10)] +#pragma warning disable CA1822 // Mark member as static public int GetCurrentProcessorNumber() { return KernelStatic.GetCurrentThread().CurrentCore; } +#pragma warning restore CA1822 [Svc(0x25)] +#pragma warning disable CA1822 // Mark member as static public Result GetThreadId(out ulong threadUid, int handle) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2662,8 +2780,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidHandle; } } +#pragma warning restore CA1822 [Svc(0x32)] +#pragma warning disable CA1822 // Mark member as static public Result SetThreadActivity(int handle, bool pause) { KProcess process = KernelStatic.GetCurrentProcess(); @@ -2687,8 +2807,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return thread.SetActivity(pause); } +#pragma warning restore CA1822 [Svc(0x33)] +#pragma warning disable CA1822 // Mark member as static public Result GetThreadContext3([PointerSized] ulong address, int handle) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -2722,6 +2844,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } +#pragma warning restore CA1822 // Thread synchronization @@ -2758,7 +2881,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } - Span<int> handles = new Span<int>(currentThread.WaitSyncHandles).Slice(0, handlesCount); + Span<int> handles = new Span<int>(currentThread.WaitSyncHandles)[..handlesCount]; if (!KernelTransfer.UserToKernelArray(handlesPtr, handles)) { @@ -2782,7 +2905,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KThread currentThread = KernelStatic.GetCurrentThread(); - var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects).Slice(0, handles.Length); + var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects)[..handles.Length]; if (handles.Length != 0) { @@ -2854,6 +2977,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } [Svc(0x1a)] +#pragma warning disable CA1822 // Mark member as static public Result ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle) { if (IsPointingInsideKernel(mutexAddress)) @@ -2870,8 +2994,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle); } +#pragma warning restore CA1822 [Svc(0x1b)] +#pragma warning disable CA1822 // Mark member as static public Result ArbitrateUnlock([PointerSized] ulong mutexAddress) { if (IsPointingInsideKernel(mutexAddress)) @@ -2888,8 +3014,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress); } +#pragma warning restore CA1822 [Svc(0x1c)] +#pragma warning disable CA1822 // Mark member as static public Result WaitProcessWideKeyAtomic( [PointerSized] ulong mutexAddress, [PointerSized] ulong condVarAddress, @@ -2919,8 +3047,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall handle, timeout); } +#pragma warning restore CA1822 [Svc(0x1d)] +#pragma warning disable CA1822 // Mark member as static public Result SignalProcessWideKey([PointerSized] ulong address, int count) { KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -2929,8 +3059,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return Result.Success; } +#pragma warning restore CA1822 [Svc(0x34)] +#pragma warning disable CA1822 // Mark member as static public Result WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout) { if (IsPointingInsideKernel(address)) @@ -2961,8 +3093,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall _ => KernelResult.InvalidEnumValue, }; } +#pragma warning restore CA1822 [Svc(0x35)] +#pragma warning disable CA1822 // Mark member as static public Result SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count) { if (IsPointingInsideKernel(address)) @@ -2985,17 +3119,20 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall => currentProcess.AddressArbiter.SignalAndIncrementIfEqual(address, value, count), SignalType.SignalAndModifyIfEqual => currentProcess.AddressArbiter.SignalAndModifyIfEqual(address, value, count), - _ => KernelResult.InvalidEnumValue + _ => KernelResult.InvalidEnumValue, }; } +#pragma warning restore CA1822 [Svc(0x36)] +#pragma warning disable CA1822 // Mark member as static public Result SynchronizePreemptionState() { KernelStatic.GetCurrentThread().SynchronizePreemptionState(); return Result.Success; } +#pragma warning restore CA1822 private static bool IsPointingInsideKernel(ulong address) { @@ -3007,4 +3144,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return (address & 3) != 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs index 710bac946..72f906422 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs @@ -41,4 +41,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall currentThread.HandlePostSyscall(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs index b524406af..fd1b41e32 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ThreadContext.cs @@ -11,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public ulong Sp; public ulong Pc; public uint Pstate; -#pragma warning disable CS0169 - private uint _padding; -#pragma warning restore CS0169 +#pragma warning disable CS0169, IDE0051 // Remove unused private member + private readonly uint _padding; +#pragma warning restore CS0169, IDE0051 public Array32<V128> FpuRegisters; public uint Fpcr; public uint Fpsr; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs index 89c1bf1fa..ddcf02029 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { enum ArbitrationType { - WaitIfLessThan = 0, + WaitIfLessThan = 0, DecrementAndWaitIfLessThan = 1, - WaitIfEqual = 2 + WaitIfEqual = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index 57fbabd51..f6b9a112c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.ThreadTerminating; } - currentThread.SignaledObj = null; + currentThread.SignaledObj = null; currentThread.ObjSyncResult = Result.Success; KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.InvalidHandle; } - currentThread.MutexAddress = mutexAddress; + currentThread.MutexAddress = mutexAddress; currentThread.ThreadHandleForUserMutex = requesterHandle; mutexOwner.AddMutexWaiter(currentThread); @@ -76,10 +76,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Leave(); _context.CriticalSection.Enter(); - if (currentThread.MutexOwner != null) - { - currentThread.MutexOwner.RemoveMutexWaiter(currentThread); - } + currentThread.MutexOwner?.RemoveMutexWaiter(currentThread); _context.CriticalSection.Leave(); @@ -103,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (result != Result.Success && newOwnerThread != null) { - newOwnerThread.SignaledObj = null; + newOwnerThread.SignaledObj = null; newOwnerThread.ObjSyncResult = result; } @@ -118,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KThread currentThread = KernelStatic.GetCurrentThread(); - currentThread.SignaledObj = null; + currentThread.SignaledObj = null; currentThread.ObjSyncResult = KernelResult.TimedOut; if (currentThread.TerminationRequested) @@ -139,9 +136,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.InvalidMemState; } - currentThread.MutexAddress = mutexAddress; + currentThread.MutexAddress = mutexAddress; currentThread.ThreadHandleForUserMutex = threadHandle; - currentThread.CondVarAddress = condVarAddress; + currentThread.CondVarAddress = condVarAddress; _condVarThreads.Add(currentThread); @@ -164,10 +161,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Enter(); - if (currentThread.MutexOwner != null) - { - currentThread.MutexOwner.RemoveMutexWaiter(currentThread); - } + currentThread.MutexOwner?.RemoveMutexWaiter(currentThread); _condVarThreads.Remove(currentThread); @@ -176,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return currentThread.ObjSyncResult; } - private (int, KThread) MutexUnlock(KThread currentThread, ulong mutexAddress) + private static (int, KThread) MutexUnlock(KThread currentThread, ulong mutexAddress) { KThread newOwnerThread = currentThread.RelinquishMutex(mutexAddress, out int count); @@ -191,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading mutexValue |= HasListenersMask; } - newOwnerThread.SignaledObj = null; + newOwnerThread.SignaledObj = null; newOwnerThread.ObjSyncResult = Result.Success; newOwnerThread.ReleaseAndResume(); @@ -223,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (!currentProcess.CpuMemory.IsMapped(address)) { // Invalid address. - requester.SignaledObj = null; + requester.SignaledObj = null; requester.ObjSyncResult = KernelResult.InvalidMemState; return; @@ -253,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (mutexValue == 0) { // We now own the mutex. - requester.SignaledObj = null; + requester.SignaledObj = null; requester.ObjSyncResult = Result.Success; requester.ReleaseAndResume(); @@ -273,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading else { // Invalid mutex owner. - requester.SignaledObj = null; + requester.SignaledObj = null; requester.ObjSyncResult = KernelResult.InvalidHandle; requester.ReleaseAndResume(); @@ -293,7 +287,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.ThreadTerminating; } - currentThread.SignaledObj = null; + currentThread.SignaledObj = null; currentThread.ObjSyncResult = KernelResult.TimedOut; if (!KernelTransfer.UserToKernel(out int currentValue, address)) @@ -312,7 +306,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.TimedOut; } - currentThread.MutexAddress = address; + currentThread.MutexAddress = address; currentThread.WaitingInArbitration = true; _arbiterThreads.Add(currentThread); @@ -363,7 +357,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.ThreadTerminating; } - currentThread.SignaledObj = null; + currentThread.SignaledObj = null; currentThread.ObjSyncResult = KernelResult.TimedOut; KProcess currentProcess = KernelStatic.GetCurrentProcess(); @@ -389,7 +383,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return KernelResult.TimedOut; } - currentThread.MutexAddress = address; + currentThread.MutexAddress = address; currentThread.WaitingInArbitration = true; _arbiterThreads.Add(currentThread); @@ -551,7 +545,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { static void RemoveArbiterThread(KThread thread) { - thread.SignaledObj = null; + thread.SignaledObj = null; thread.ObjSyncResult = Result.Success; thread.ReleaseAndResume(); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs index 891e632f9..c6aa984c2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs @@ -67,4 +67,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading context.CriticalSection.Leave(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs index 1d61f2f06..3d6744882 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs @@ -61,4 +61,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs index da3e217bf..65169d035 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs @@ -7,8 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public KEvent(KernelContext context) { - ReadableEvent = new KReadableEvent(context, this); + ReadableEvent = new KReadableEvent(context); WritableEvent = new KWritableEvent(context, this); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs index 14fba7045..1608db095 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs @@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _prioMask &= ~(1L << _prio); } - public KThread Current => _node?.Value; + public readonly KThread Current => _node?.Value; public bool MoveNext() { @@ -283,4 +283,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return _scheduledThreadsPerPrioPerCore[prio][core]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs index d9e7befa6..6ed61f5a3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs @@ -5,13 +5,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { class KReadableEvent : KSynchronizationObject { - private readonly KEvent _parent; - private bool _signaled; - public KReadableEvent(KernelContext context, KEvent parent) : base(context) + public KReadableEvent(KernelContext context) : base(context) { - _parent = parent; } public override void Signal() @@ -62,4 +59,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return _signaled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index b9de7d9c7..905c61d66 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -9,11 +9,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading partial class KScheduler : IDisposable { public const int PrioritiesCount = 64; - public const int CpuCoresCount = 4; + public const int CpuCoresCount = 4; private const int RoundRobinTimeQuantumMs = 10; - private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 }; + private static readonly int[] _preemptionPriorities = { 59, 59, 59, 63 }; private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount]; @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private KThread CreateIdleThread(KernelContext context, int cpuCore) { - KThread idleThread = new KThread(context); + KThread idleThread = new(context); idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop); @@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KThread candidate = thread.Owner.PinnedThreads[core]; - if (candidate.KernelWaitersCount == 0 && !thread.Owner.IsExceptionUserThread(candidate)) + if (candidate.KernelWaitersCount == 0 && !KProcess.IsExceptionUserThread(candidate)) { if (candidate.SchedFlags == ThreadSchedState.Running) { @@ -378,10 +378,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading currentThread.AddCpuTime(ticksDelta); - if (currentProcess != null) - { - currentProcess.AddCpuTime(ticksDelta); - } + currentProcess?.AddCpuTime(ticksDelta); LastContextSwitchTime = currentTicks; @@ -411,7 +408,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading for (int core = 0; core < CpuCoresCount; core++) { - RotateScheduledQueue(context, core, PreemptionPriorities[core]); + RotateScheduledQueue(context, core, _preemptionPriorities[core]); } context.CriticalSection.Leave(); @@ -431,7 +428,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread); } - static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate< KThread> predicate) + static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate<KThread> predicate) { foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core)) { @@ -651,11 +648,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // Ensure that the idle thread is not blocked and can exit. lock (_idleInterruptEventLock) { - if (_idleInterruptEvent != null) - { - _idleInterruptEvent.Set(); - } + _idleInterruptEvent?.Set(); } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs index 9c196810c..b1af06b0d 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { class KSynchronization { - private KernelContext _context; + private readonly KernelContext _context; public KSynchronization(KernelContext context) { @@ -68,8 +68,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading syncNodes[index] = syncObjs[index].AddWaitingThread(currentThread); } - currentThread.WaitingSync = true; - currentThread.SignaledObj = null; + currentThread.WaitingSync = true; + currentThread.SignaledObj = null; currentThread.ObjSyncResult = result; currentThread.Reschedule(ThreadSchedState.Paused); @@ -126,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) { - thread.SignaledObj = syncObj; + thread.SignaledObj = syncObj; thread.ObjSyncResult = Result.Success; thread.Reschedule(ThreadSchedState.Running); @@ -139,4 +139,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _context.CriticalSection.Leave(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 78bd577e1..12383fb8a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -68,10 +68,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public LinkedListNode<KThread> ProcessListNode { get; set; } - private LinkedList<KThread> _mutexWaiters; + private readonly LinkedList<KThread> _mutexWaiters; private LinkedListNode<KThread> _mutexWaiterNode; - private LinkedList<KThread> _pinnedWaiters; + private readonly LinkedList<KThread> _pinnedWaiters; public KThread MutexOwner { get; private set; } @@ -659,7 +659,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading const int MaxRegistersAArch32 = 15; const int MaxFpuRegistersAArch32 = 16; - ThreadContext context = new ThreadContext(); + ThreadContext context = new(); if (Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit)) { @@ -1433,4 +1433,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Owner.CpuMemory.Write<ushort>(_tlsAddress + TlsUserInterruptFlagOffset, 0); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs index b46122be7..fd65361ac 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs @@ -22,4 +22,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return _parent.ReadableEvent.Clear(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs index e72b719b4..9f257d983 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { enum SignalType { - Signal = 0, + Signal = 0, SignalAndIncrementIfEqual = 1, - SignalAndModifyIfEqual = 2 + SignalAndModifyIfEqual = 2, } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs index 1d09e021e..eca412d03 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs @@ -5,19 +5,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading [Flags] enum ThreadSchedState : ushort { - LowMask = 0xf, - HighMask = 0xfff0, + LowMask = 0xf, + HighMask = 0xfff0, ForcePauseMask = 0x1f0, - ProcessPauseFlag = 1 << 4, - ThreadPauseFlag = 1 << 5, + ProcessPauseFlag = 1 << 4, + ThreadPauseFlag = 1 << 5, ProcessDebugPauseFlag = 1 << 6, - BacktracePauseFlag = 1 << 7, - KernelInitPauseFlag = 1 << 8, + BacktracePauseFlag = 1 << 7, + KernelInitPauseFlag = 1 << 8, - None = 0, - Paused = 1, - Running = 2, - TerminationPending = 3 + None = 0, + Paused = 1, + Running = 2, + TerminationPending = 3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs index 0b44b57ff..83093570b 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs @@ -5,6 +5,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Dummy, Kernel, Kernel2, - User + User, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/LibHacHorizonManager.cs b/src/Ryujinx.HLE/HOS/LibHacHorizonManager.cs index 8fde5dd6a..01d4b2450 100644 --- a/src/Ryujinx.HLE/HOS/LibHacHorizonManager.cs +++ b/src/Ryujinx.HLE/HOS/LibHacHorizonManager.cs @@ -1,15 +1,12 @@ using LibHac; using LibHac.Bcat; using LibHac.Common; -using LibHac.Fs.Fsa; -using LibHac.Fs.Shim; using LibHac.FsSrv.Impl; using LibHac.Loader; using LibHac.Ncm; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Arp; using System; -using StorageId = LibHac.Ncm.StorageId; namespace Ryujinx.HLE.HOS { @@ -17,15 +14,15 @@ namespace Ryujinx.HLE.HOS { private LibHac.Horizon Server { get; set; } - public HorizonClient RyujinxClient { get; private set; } + public HorizonClient RyujinxClient { get; private set; } public HorizonClient ApplicationClient { get; private set; } - public HorizonClient AccountClient { get; private set; } - public HorizonClient AmClient { get; private set; } - public HorizonClient BcatClient { get; private set; } - public HorizonClient FsClient { get; private set; } - public HorizonClient NsClient { get; private set; } - public HorizonClient PmClient { get; private set; } - public HorizonClient SdbClient { get; private set; } + public HorizonClient AccountClient { get; private set; } + public HorizonClient AmClient { get; private set; } + public HorizonClient BcatClient { get; private set; } + public HorizonClient FsClient { get; private set; } + public HorizonClient NsClient { get; private set; } + public HorizonClient PmClient { get; private set; } + public HorizonClient SdbClient { get; private set; } private SharedRef<LibHacIReader> _arpIReader; internal LibHacIReader ArpIReader => _arpIReader.Get; @@ -64,11 +61,13 @@ namespace Ryujinx.HLE.HOS public void InitializeSystemClients() { +#pragma warning disable IDE0055 // Disable formatting PmClient = Server.CreatePrivilegedHorizonClient(); AccountClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Account, StorageId.BuiltInSystem), AccountFsPermissions); AmClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Am, StorageId.BuiltInSystem), AmFsPermissions); NsClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Ns, StorageId.BuiltInSystem), NsFsPermissions); SdbClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Sdb, StorageId.BuiltInSystem), SdbFacData, SdbFacDescriptor); +#pragma warning restore IDE0055 } public void InitializeApplicationClient(ProgramId programId, in Npdm npdm) @@ -106,7 +105,7 @@ namespace Ryujinx.HLE.HOS 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01 + 0x00, 0x00, 0x00, 0x01, }; private static ReadOnlySpan<byte> SdbFacDescriptor => new byte[] @@ -114,7 +113,7 @@ namespace Ryujinx.HLE.HOS 0x01, 0x00, 0x02, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + 0x01, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index 243510967..6706006c3 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -110,16 +110,16 @@ namespace Ryujinx.HLE.HOS private readonly Dictionary<ulong, ModCache> _appMods; // key is TitleId private PatchCache _patches; - private static readonly EnumerationOptions DirEnumOptions; + private static readonly EnumerationOptions _dirEnumOptions; static ModLoader() { - DirEnumOptions = new EnumerationOptions + _dirEnumOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, MatchType = MatchType.Simple, RecurseSubdirectories = false, - ReturnSpecialDirectories = false + ReturnSpecialDirectories = false, }; } @@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); - public static string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); + public static string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); public static string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); private static string EnsureBaseDirStructure(string modsBasePath) @@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS } private static DirectoryInfo FindTitleDir(DirectoryInfo contentsDir, string titleId) - => contentsDir.EnumerateDirectories(titleId, DirEnumOptions).FirstOrDefault(); + => contentsDir.EnumerateDirectories(titleId, _dirEnumOptions).FirstOrDefault(); private static void AddModsFromDirectory(ModCache mods, DirectoryInfo dir, string titleId) { @@ -218,15 +218,18 @@ namespace Ryujinx.HLE.HOS if (StrEquals(AmsNsoPatchDir, patchDir.Name)) { - patches = cache.NsoPatches; type = "NSO"; + patches = cache.NsoPatches; + type = "NSO"; } else if (StrEquals(AmsNroPatchDir, patchDir.Name)) { - patches = cache.NroPatches; type = "NRO"; + patches = cache.NroPatches; + type = "NRO"; } else if (StrEquals(AmsKipPatchDir, patchDir.Name)) { - patches = cache.KipPatches; type = "KIP"; + patches = cache.KipPatches; + type = "KIP"; } else { @@ -352,7 +355,7 @@ namespace Ryujinx.HLE.HOS } // Start a new cheat section. - cheatName = line.Substring(1, line.Length - 2); + cheatName = line[1..^1]; instructions = new List<string>(); } else if (line.Length > 0) @@ -528,7 +531,7 @@ namespace Ryujinx.HLE.HOS Logger.Warning?.Print(LogClass.ModLoader, "Multiple ExeFS partition replacements detected"); } - Logger.Info?.Print(LogClass.ModLoader, $"Using replacement ExeFS partition"); + Logger.Info?.Print(LogClass.ModLoader, "Using replacement ExeFS partition"); exefs = new PartitionFileSystem(mods.ExefsContainers[0].Path.OpenRead().AsStorage()); @@ -549,7 +552,7 @@ namespace Ryujinx.HLE.HOS ModLoadResult modLoadResult = new() { Stubs = new BitVector32(), - Replaces = new BitVector32() + Replaces = new BitVector32(), }; if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) @@ -559,7 +562,7 @@ namespace Ryujinx.HLE.HOS if (nsos.Length != ProcessConst.ExeFsPrefixes.Length) { - throw new ArgumentOutOfRangeException("NSO Count is incorrect"); + throw new ArgumentOutOfRangeException(nameof(nsos), nsos.Length, "NSO Count is incorrect"); } var exeMods = mods.ExefsDirs; @@ -622,7 +625,10 @@ namespace Ryujinx.HLE.HOS { var nroPatches = _patches.NroPatches; - if (nroPatches.Count == 0) return; + if (nroPatches.Count == 0) + { + return; + } // NRO patches aren't offset relative to header unlike NSO // according to Atmosphere's ro patcher module @@ -682,7 +688,7 @@ namespace Ryujinx.HLE.HOS EnableCheats(titleId, tamperMachine); } - internal void EnableCheats(ulong titleId, TamperMachine tamperMachine) + internal static void EnableCheats(ulong titleId, TamperMachine tamperMachine) { var contentDirectory = FindTitleDir(new DirectoryInfo(Path.Combine(GetModsBasePath(), AmsContentsDir)), $"{titleId:x16}"); string enabledCheatsPath = Path.Combine(contentDirectory.FullName, CheatDir, "enabled.txt"); @@ -708,7 +714,7 @@ namespace Ryujinx.HLE.HOS { NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()).TrimEnd('0'), NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'), - _ => string.Empty + _ => string.Empty, }).ToList(); int GetIndex(string buildId) => buildIds.FindIndex(id => id == buildId); // O(n) but list is small @@ -767,4 +773,4 @@ namespace Ryujinx.HLE.HOS return count > 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/ResultCode.cs b/src/Ryujinx.HLE/HOS/ResultCode.cs index 4004357b5..3fec365cd 100644 --- a/src/Ryujinx.HLE/HOS/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/ResultCode.cs @@ -2,11 +2,11 @@ { public enum ResultCode { - OsModuleId = 3, + OsModuleId = 3, ErrorCodeShift = 9, Success = 0, - NotAllocated = (1023 << ErrorCodeShift) | OsModuleId + NotAllocated = (1023 << ErrorCodeShift) | OsModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/ServiceCtx.cs b/src/Ryujinx.HLE/HOS/ServiceCtx.cs index 659b212ac..ba27e12ea 100644 --- a/src/Ryujinx.HLE/HOS/ServiceCtx.cs +++ b/src/Ryujinx.HLE/HOS/ServiceCtx.cs @@ -8,33 +8,33 @@ namespace Ryujinx.HLE.HOS { class ServiceCtx { - public Switch Device { get; } - public KProcess Process { get; } - public IVirtualMemoryManager Memory { get; } - public KThread Thread { get; } - public IpcMessage Request { get; } - public IpcMessage Response { get; } - public BinaryReader RequestData { get; } - public BinaryWriter ResponseData { get; } + public Switch Device { get; } + public KProcess Process { get; } + public IVirtualMemoryManager Memory { get; } + public KThread Thread { get; } + public IpcMessage Request { get; } + public IpcMessage Response { get; } + public BinaryReader RequestData { get; } + public BinaryWriter ResponseData { get; } public ServiceCtx( - Switch device, - KProcess process, + Switch device, + KProcess process, IVirtualMemoryManager memory, - KThread thread, - IpcMessage request, - IpcMessage response, - BinaryReader requestData, - BinaryWriter responseData) + KThread thread, + IpcMessage request, + IpcMessage response, + BinaryReader requestData, + BinaryWriter responseData) { - Device = device; - Process = process; - Memory = memory; - Thread = thread; - Request = request; - Response = response; - RequestData = requestData; + Device = device; + Process = process; + Memory = memory; + Thread = thread; + Request = request; + Response = response; + RequestData = requestData; ResponseData = responseData; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs index f5364329d..a6dde3c53 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { public class AccountManager { - public static readonly UserId DefaultUserId = new UserId("00000000000000010000000000000000"); + public static readonly UserId DefaultUserId = new("00000000000000010000000000000000"); private readonly AccountSaveDataManager _accountSaveDataManager; @@ -51,7 +51,9 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { commandLineUserProfileOverride = _profiles.Values.FirstOrDefault(x => x.Name == initialProfileName)?.UserId ?? default; if (commandLineUserProfileOverride.IsNull) + { Logger.Warning?.Print(LogClass.Application, $"The command line specified profile named '{initialProfileName}' was not found"); + } } OpenUser(commandLineUserProfileOverride.IsNull ? _accountSaveDataManager.LastOpened : commandLineUserProfileOverride); } @@ -64,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc userId = new UserId(Guid.NewGuid().ToString().Replace("-", "")); } - UserProfile profile = new UserProfile(userId, name, image); + UserProfile profile = new(userId, name, image); _profiles.AddOrUpdate(userId.ToString(), profile, (key, old) => profile); @@ -238,4 +240,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _profiles.First().Value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs index 535779d2e..c2ae0119c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); - private static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly ProfilesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public UserId LastOpened { get; set; } @@ -23,22 +23,22 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc if (File.Exists(_profilesJsonPath)) { - try + try { - ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson); + ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, _serializerContext.ProfilesJson); foreach (var profile in profilesJson.Profiles) { - UserProfile addedProfile = new UserProfile(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp); + UserProfile addedProfile = new(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp); profiles.AddOrUpdate(profile.UserId, addedProfile, (key, old) => addedProfile); } LastOpened = new UserId(profilesJson.LastOpened); } - catch (Exception e) + catch (Exception ex) { - Logger.Error?.Print(LogClass.Application, $"Failed to parse {_profilesJsonPath}: {e.Message} Loading default profile!"); + Logger.Error?.Print(LogClass.Application, $"Failed to parse {_profilesJsonPath}: {ex.Message} Loading default profile!"); LastOpened = AccountManager.DefaultUserId; } @@ -51,26 +51,26 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public void Save(ConcurrentDictionary<string, UserProfile> profiles) { - ProfilesJson profilesJson = new ProfilesJson() + ProfilesJson profilesJson = new() { - Profiles = new List<UserProfileJson>(), - LastOpened = LastOpened.ToString() + Profiles = new List<UserProfileJson>(), + LastOpened = LastOpened.ToString(), }; foreach (var profile in profiles) { profilesJson.Profiles.Add(new UserProfileJson() { - UserId = profile.Value.UserId.ToString(), - Name = profile.Value.Name, - AccountState = profile.Value.AccountState, - OnlinePlayState = profile.Value.OnlinePlayState, + UserId = profile.Value.UserId.ToString(), + Name = profile.Value.Name, + AccountState = profile.Value.AccountState, + OnlinePlayState = profile.Value.OnlinePlayState, LastModifiedTimestamp = profile.Value.LastModifiedTimestamp, - Image = profile.Value.Image, + Image = profile.Value.Image, }); } - JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson); + JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, _serializerContext.ProfilesJson); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs index 9c058cb59..3cb46d204 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { class IManagerForApplication : IpcService { - private ManagerServer _managerServer; + private readonly ManagerServer _managerServer; public IManagerForApplication(UserId userId) { @@ -72,4 +72,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return resultCode; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs index ecd516876..8510837b2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { class IManagerForSystemService : IpcService { - private ManagerServer _managerServer; + private readonly ManagerServer _managerServer; public IManagerForSystemService(UserId userId) { @@ -44,4 +44,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return _managerServer.LoadIdTokenCache(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs index 14911dfb1..a0021917f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { class IProfile : IpcService { - private ProfileServer _profileServer; + private readonly ProfileServer _profileServer; public IProfile(UserProfile profile) { @@ -37,4 +37,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return _profileServer.LoadImage(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs index 64b6070f2..5d5d0dd69 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { class IProfileEditor : IpcService { - private ProfileServer _profileServer; + private readonly ProfileServer _profileServer; public IProfileEditor(UserProfile profile) { @@ -51,4 +51,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return _profileServer.StoreWithImage(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs index 972403118..c43186de1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs @@ -16,7 +16,9 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService // TODO: Determine where and how NetworkServiceAccountId is set. private const long NetworkServiceAccountId = 0xcafe; - private UserId _userId; +#pragma warning disable IDE0052 // Remove unread private member + private readonly UserId _userId; +#pragma warning restore IDE0052 public ManagerServer(UserId userId) { @@ -29,15 +31,15 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService RSAParameters parameters = provider.ExportParameters(true); - RsaSecurityKey secKey = new RsaSecurityKey(parameters); + RsaSecurityKey secKey = new(parameters); - SigningCredentials credentials = new SigningCredentials(secKey, "RS256"); + SigningCredentials credentials = new(secKey, "RS256"); credentials.Key.KeyId = parameters.ToString(); var header = new JwtHeader(credentials) { - { "jku", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com/1.0.0/certificates" } + { "jku", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com/1.0.0/certificates" }, }; byte[] rawUserId = new byte[0x10]; @@ -60,10 +62,10 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { "typ", "id_token" }, { "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, { "jti", Guid.NewGuid().ToString() }, - { "exp", (DateTimeOffset.UtcNow + TimeSpan.FromHours(3)).ToUnixTimeSeconds() } + { "exp", (DateTimeOffset.UtcNow + TimeSpan.FromHours(3)).ToUnixTimeSeconds() }, }; - JwtSecurityToken securityToken = new JwtSecurityToken(header, payload); + JwtSecurityToken securityToken = new(header, payload); return new JwtSecurityTokenHandler().WriteToken(securityToken); } @@ -94,8 +96,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context, out IAsyncContext asyncContext) { - KEvent asyncEvent = new KEvent(context.Device.System.KernelContext); - AsyncExecution asyncExecution = new AsyncExecution(asyncEvent); + KEvent asyncEvent = new(context.Device.System.KernelContext); + AsyncExecution asyncExecution = new(asyncEvent); asyncExecution.Initialize(1000, EnsureIdTokenCacheAsyncImpl); @@ -123,7 +125,9 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode LoadIdTokenCache(ServiceCtx context) { ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferSize = context.Request.ReceiveBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong bufferSize = context.Request.ReceiveBuff[0].Size; +#pragma warning restore IDE0059 // NOTE: This opens the file at "su/cache/USERID_IN_UUID_STRING.dat" (where USERID_IN_UUID_STRING is formatted as "%08x-%04x-%04x-%02x%02x-%08x%04x") // in the "account:/" savedata and writes some data in the buffer. @@ -169,8 +173,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode LoadNetworkServiceLicenseKindAsync(ServiceCtx context, out IAsyncNetworkServiceLicenseKindContext asyncContext) { - KEvent asyncEvent = new KEvent(context.Device.System.KernelContext); - AsyncExecution asyncExecution = new AsyncExecution(asyncEvent); + KEvent asyncEvent = new(context.Device.System.KernelContext); + AsyncExecution asyncExecution = new(asyncEvent); Logger.Stub?.PrintStub(LogClass.ServiceAcc); @@ -184,4 +188,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs index 8e29f94bc..08400baf2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { class ProfileServer { - private UserProfile _profile; + private readonly UserProfile _profile; public ProfileServer(UserProfile profile) { @@ -23,8 +23,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService MemoryHelper.FillWithZeros(context.Memory, bufferPosition, 0x80); // TODO: Determine the struct. - context.Memory.Write(bufferPosition, 0); // Unknown - context.Memory.Write(bufferPosition + 4, 1); // Icon ID. 0 = Mii, the rest are character icon IDs. + context.Memory.Write(bufferPosition, 0); // Unknown + context.Memory.Write(bufferPosition + 4, 1); // Icon ID. 0 = Mii, the rest are character icon IDs. context.Memory.Write(bufferPosition + 8, (byte)1); // Profile icon background color ID // 0x07 bytes - Unknown // 0x10 bytes - Some ID related to the Mii? All zeros when a character icon is used. @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode LoadImage(ServiceCtx context) { ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferLen = context.Request.ReceiveBuff[0].Size; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; if ((ulong)_profile.Image.Length > bufferLen) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode Store(ServiceCtx context) { ulong userDataPosition = context.Request.PtrBuff[0].Position; - ulong userDataSize = context.Request.PtrBuff[0].Size; + ulong userDataSize = context.Request.PtrBuff[0].Size; byte[] userData = new byte[userDataSize]; @@ -91,14 +91,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode StoreWithImage(ServiceCtx context) { ulong userDataPosition = context.Request.PtrBuff[0].Position; - ulong userDataSize = context.Request.PtrBuff[0].Size; + ulong userDataSize = context.Request.PtrBuff[0].Size; byte[] userData = new byte[userDataSize]; context.Memory.Read(userDataPosition, userData); ulong profileImagePosition = context.Request.SendBuff[0].Position; - ulong profileImageSize = context.Request.SendBuff[0].Size; + ulong profileImageSize = context.Request.SendBuff[0].Size; byte[] profileImageData = new byte[profileImageSize]; @@ -111,4 +111,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs index d9f9864a0..b30a81e94 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc } ulong outputPosition = context.Request.RecvListBuff[0].Position; - ulong outputSize = context.Request.RecvListBuff[0].Size; + ulong outputSize = context.Request.RecvListBuff[0].Size; MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); @@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc break; } - context.Memory.Write(outputPosition + offset, userProfile.UserId.High); + context.Memory.Write(outputPosition + offset, userProfile.UserId.High); context.Memory.Write(outputPosition + offset + 8, userProfile.UserId.Low); offset += 0x10; @@ -148,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public ResultCode CheckNetworkServiceAvailabilityAsync(ServiceCtx context, out IAsyncContext asyncContext) { - KEvent asyncEvent = new(context.Device.System.KernelContext); + KEvent asyncEvent = new(context.Device.System.KernelContext); AsyncExecution asyncExecution = new(asyncEvent); asyncExecution.Initialize(1000, CheckNetworkServiceAvailabilityAsyncImpl); @@ -183,7 +183,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc } ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; + ulong inputSize = context.Request.SendBuff[0].Size; if (inputSize != 0x24000) { @@ -251,4 +251,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs index 2ea92b117..c5f3d91ec 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Logging; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Threading; using System; using System.Threading; @@ -9,18 +9,18 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext class AsyncExecution { private readonly CancellationTokenSource _tokenSource; - private readonly CancellationToken _token; + private readonly CancellationToken _token; - public KEvent SystemEvent { get; } - public bool IsInitialized { get; private set; } - public bool IsRunning { get; private set; } + public KEvent SystemEvent { get; } + public bool IsInitialized { get; private set; } + public bool IsRunning { get; private set; } public AsyncExecution(KEvent asyncEvent) { SystemEvent = asyncEvent; _tokenSource = new CancellationTokenSource(); - _token = _tokenSource.Token; + _token = _tokenSource.Token; } public void Initialize(int timeout, Func<CancellationToken, Task> taskAsync) @@ -53,4 +53,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext _tokenSource.Cancel(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs index 6a457f042..121b79373 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc [Service("acc:su", AccountServiceFlag.Administrator)] // Max Sessions: 8 class IAccountServiceForAdministrator : IpcService { - private ApplicationServiceServer _applicationServiceServer; + private readonly ApplicationServiceServer _applicationServiceServer; public IAccountServiceForAdministrator(ServiceCtx context, AccountServiceFlag serviceFlag) { @@ -126,4 +126,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 8ec83e5c1..98af10694 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc [Service("acc:u0", AccountServiceFlag.Application)] // Max Sessions: 4 class IAccountServiceForApplication : IpcService { - private ApplicationServiceServer _applicationServiceServer; + private readonly ApplicationServiceServer _applicationServiceServer; public IAccountServiceForApplication(ServiceCtx context, AccountServiceFlag serviceFlag) { @@ -197,4 +197,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs index 3b5f3b03e..a586d21cc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc [Service("acc:u1", AccountServiceFlag.SystemService)] // Max Sessions: 16 class IAccountServiceForSystemService : IpcService { - private ApplicationServiceServer _applicationServiceServer; + private readonly ApplicationServiceServer _applicationServiceServer; public IAccountServiceForSystemService(ServiceCtx context, AccountServiceFlag serviceFlag) { @@ -104,4 +104,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _applicationServiceServer.ListQualifiedUsers(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs index c9af0727f..2fa354b19 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext; using Ryujinx.Horizon.Common; using System; @@ -18,12 +18,12 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // GetSystemEvent() -> handle<copy> public ResultCode GetSystemEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(AsyncExecution.SystemEvent.ReadableEvent, out int _systemEventHandle) != Result.Success) + if (context.Process.HandleTable.GenerateHandle(AsyncExecution.SystemEvent.ReadableEvent, out int systemEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_systemEventHandle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(systemEventHandle); return ResultCode.Success; } @@ -76,4 +76,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs index 1fa5cf2a1..a7b0c0638 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncNetworkServiceLicenseKindContext.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { class IAsyncNetworkServiceLicenseKindContext : IAsyncContext { - private NetworkServiceLicenseKind? _serviceLicenseKind; + private readonly NetworkServiceLicenseKind? _serviceLicenseKind; public IAsyncNetworkServiceLicenseKindContext(AsyncExecution asyncExecution, NetworkServiceLicenseKind? serviceLicenseKind) : base(asyncExecution) { diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs index 223be2f5e..f86e30746 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs @@ -5,4 +5,4 @@ { public IBaasAccessTokenAccessor(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs index 6b54898e5..d6446e735 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/ProfilesJsonSerializerContext.cs @@ -8,4 +8,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc internal partial class ProfilesJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs index a991f9776..5dbf9a673 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs @@ -2,9 +2,9 @@ { enum AccountServiceFlag { - Administrator = 100, - SystemService = 101, - Application = 102, - BaasAccessTokenAccessor = 200 + Administrator = 100, + SystemService = 101, + Application = 102, + BaasAccessTokenAccessor = 200, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs index 1699abfbd..0e35b4812 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs @@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public enum AccountState { Closed, - Open + Open, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs index a33e26707..a766edeff 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/NetworkServiceLicenseKind.cs @@ -3,6 +3,6 @@ enum NetworkServiceLicenseKind : uint { NoSubscription, - Subscribed + Subscribed, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs index 09f9d1421..4e22f434e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs @@ -7,4 +7,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types public List<UserProfileJson> Profiles { get; set; } public string LastOpened { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs index e5577a94b..ed7bf4e56 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs @@ -15,18 +15,18 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public bool IsNull => (Low | High) == 0; - public static UserId Null => new UserId(0, 0); + public static UserId Null => new(0, 0); public UserId(long low, long high) { - Low = low; + Low = low; High = high; } public UserId(byte[] bytes) { High = BitConverter.ToInt64(bytes, 0); - Low = BitConverter.ToInt64(bytes, 8); + Low = BitConverter.ToInt64(bytes, 8); } public UserId(string hex) @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc throw new ArgumentException("Invalid Hex value!", nameof(hex)); } - Low = long.Parse(hex.AsSpan(16), NumberStyles.HexNumber); + Low = long.Parse(hex.AsSpan(16), NumberStyles.HexNumber); High = long.Parse(hex.AsSpan(0, 16), NumberStyles.HexNumber); } @@ -61,4 +61,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return new UInt128((ulong)High, (ulong)Low); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs index 210b369c3..4482de2dd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs @@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc } } - public AccountState _onlinePlayState; + private AccountState _onlinePlayState; public AccountState OnlinePlayState { @@ -63,10 +63,10 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public UserProfile(UserId userId, string name, byte[] image, long lastModifiedTimestamp = 0) { UserId = userId; - Name = name; - Image = image; + Name = name; + Image = image; - AccountState = AccountState.Closed; + AccountState = AccountState.Closed; OnlinePlayState = AccountState.Closed; if (lastModifiedTimestamp != 0) @@ -84,4 +84,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc LastModifiedTimestamp = (long)(DateTime.Now - DateTime.UnixEpoch).TotalSeconds; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs index 06ff4833f..e51aa8d1b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfileJson.cs @@ -9,4 +9,4 @@ public long LastModifiedTimestamp { get; set; } public byte[] Image { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs index 723013492..698c01a4a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs @@ -5,4 +5,4 @@ { public IService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs index 34114ec9f..6bd3cce85 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs @@ -2,23 +2,23 @@ namespace Ryujinx.HLE.HOS.Services.Account { enum ResultCode { - ModuleId = 124, + ModuleId = 124, ErrorCodeShift = 9, Success = 0, - NullArgument = (20 << ErrorCodeShift) | ModuleId, - InvalidArgument = (22 << ErrorCodeShift) | ModuleId, - NullInputBuffer = (30 << ErrorCodeShift) | ModuleId, - InvalidBufferSize = (31 << ErrorCodeShift) | ModuleId, - InvalidBuffer = (32 << ErrorCodeShift) | ModuleId, - AsyncExecutionNotInitialized = (40 << ErrorCodeShift) | ModuleId, - Unknown41 = (41 << ErrorCodeShift) | ModuleId, - InternetRequestDenied = (59 << ErrorCodeShift) | ModuleId, - UserNotFound = (100 << ErrorCodeShift) | ModuleId, - NullObject = (302 << ErrorCodeShift) | ModuleId, - Unknown341 = (341 << ErrorCodeShift) | ModuleId, + NullArgument = (20 << ErrorCodeShift) | ModuleId, + InvalidArgument = (22 << ErrorCodeShift) | ModuleId, + NullInputBuffer = (30 << ErrorCodeShift) | ModuleId, + InvalidBufferSize = (31 << ErrorCodeShift) | ModuleId, + InvalidBuffer = (32 << ErrorCodeShift) | ModuleId, + AsyncExecutionNotInitialized = (40 << ErrorCodeShift) | ModuleId, + Unknown41 = (41 << ErrorCodeShift) | ModuleId, + InternetRequestDenied = (59 << ErrorCodeShift) | ModuleId, + UserNotFound = (100 << ErrorCodeShift) | ModuleId, + NullObject = (302 << ErrorCodeShift) | ModuleId, + Unknown341 = (341 << ErrorCodeShift) | ModuleId, MissingNetworkServiceLicenseKind = (400 << ErrorCodeShift) | ModuleId, - InvalidIdTokenCacheBufferSize = (451 << ErrorCodeShift) | ModuleId + InvalidIdTokenCacheBufferSize = (451 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs index bf86aaaa9..6821c7111 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs @@ -102,4 +102,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs index 93dff041c..dd015fd80 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs @@ -110,4 +110,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 0057eba34..d35cfdbe2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -10,16 +10,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { class ILibraryAppletAccessor : DisposableIpcService { - private KernelContext _kernelContext; + private readonly KernelContext _kernelContext; - private IApplet _applet; + private readonly IApplet _applet; - private AppletSession _normalSession; - private AppletSession _interactiveSession; + private readonly AppletSession _normalSession; + private readonly AppletSession _interactiveSession; - private KEvent _stateChangedEvent; - private KEvent _normalOutDataEvent; - private KEvent _interactiveOutDataEvent; + private readonly KEvent _stateChangedEvent; + private readonly KEvent _normalOutDataEvent; + private readonly KEvent _interactiveOutDataEvent; private int _stateChangedEventHandle; private int _normalOutDataEventHandle; @@ -31,17 +31,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { _kernelContext = system.KernelContext; - _stateChangedEvent = new KEvent(system.KernelContext); - _normalOutDataEvent = new KEvent(system.KernelContext); + _stateChangedEvent = new KEvent(system.KernelContext); + _normalOutDataEvent = new KEvent(system.KernelContext); _interactiveOutDataEvent = new KEvent(system.KernelContext); _applet = AppletManager.Create(appletId, system); - _normalSession = new AppletSession(); + _normalSession = new AppletSession(); _interactiveSession = new AppletSession(); - _applet.AppletStateChanged += OnAppletStateChanged; - _normalSession.DataAvailable += OnNormalOutData; + _applet.AppletStateChanged += OnAppletStateChanged; + _normalSession.DataAvailable += OnNormalOutData; _interactiveSession.DataAvailable += OnInteractiveOutData; Logger.Info?.Print(LogClass.ServiceAm, $"Applet '{appletId}' created."); diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs index 69967c568..b523f02f1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs @@ -4,13 +4,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { class AppletStandalone { - public AppletId AppletId; + public AppletId AppletId; public LibraryAppletMode LibraryAppletMode; - public Queue<byte[]> InputData; + public Queue<byte[]> InputData; public AppletStandalone() { InputData = new Queue<byte[]>(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs index 176bd6322..85bdd985c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { class ILibraryAppletSelfAccessor : IpcService { - private AppletStandalone _appletStandalone = new AppletStandalone(); + private readonly AppletStandalone _appletStandalone = new(); public ILibraryAppletSelfAccessor(ServiceCtx context) { @@ -14,8 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // Create MiiEdit data. _appletStandalone = new AppletStandalone() { - AppletId = AppletId.MiiEdit, - LibraryAppletMode = LibraryAppletMode.AllForeground + AppletId = AppletId.MiiEdit, + LibraryAppletMode = LibraryAppletMode.AllForeground, }; byte[] miiEditInputData = new byte[0x100]; @@ -49,10 +49,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo public ResultCode GetLibraryAppletInfo(ServiceCtx context) { - LibraryAppletInfo libraryAppletInfo = new LibraryAppletInfo() + LibraryAppletInfo libraryAppletInfo = new() { - AppletId = _appletStandalone.AppletId, - LibraryAppletMode = _appletStandalone.LibraryAppletMode + AppletId = _appletStandalone.AppletId, + LibraryAppletMode = _appletStandalone.LibraryAppletMode, }; context.ResponseData.WriteStruct(libraryAppletInfo); @@ -64,10 +64,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // GetCallerAppletIdentityInfo() -> nn::am::service::AppletIdentityInfo public ResultCode GetCallerAppletIdentityInfo(ServiceCtx context) { - AppletIdentifyInfo appletIdentifyInfo = new AppletIdentifyInfo() + AppletIdentifyInfo appletIdentifyInfo = new() { AppletId = AppletId.QLaunch, - TitleId = 0x0100000000001000 + TitleId = 0x0100000000001000, }; context.ResponseData.WriteStruct(appletIdentifyInfo); @@ -75,4 +75,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs index 6acd18cd6..e1857fb3e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs @@ -11,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib public ResultCode GetLaunchReason(ServiceCtx context) { // NOTE: Flag is set by using an internal field. - AppletProcessLaunchReason appletProcessLaunchReason = new AppletProcessLaunchReason() + AppletProcessLaunchReason appletProcessLaunchReason = new() { - Flag = 0 + Flag = 0, }; context.ResponseData.WriteStruct(appletProcessLaunchReason); @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs index c42202b82..fbcc33204 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs @@ -4,4 +4,4 @@ { public IAppletCommonFunctions() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs index 79e5b0507..502324ea1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationCreator.cs @@ -4,4 +4,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { public IApplicationCreator() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs index 48dd42e41..05a4b0a63 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAudioController.cs @@ -10,8 +10,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetExpectedMasterVolume(f32, f32) public ResultCode SetExpectedMasterVolume(ServiceCtx context) { - float appletVolume = context.RequestData.ReadSingle(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + float appletVolume = context.RequestData.ReadSingle(); float libraryAppletVolume = context.RequestData.ReadSingle(); +#pragma warning restore IDE0059 Logger.Stub?.PrintStub(LogClass.ServiceAm); @@ -44,8 +46,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // ChangeMainAppletMasterVolume(f32, u64) public ResultCode ChangeMainAppletMasterVolume(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment float unknown0 = context.RequestData.ReadSingle(); - long unknown1 = context.RequestData.ReadInt64(); + long unknown1 = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 Logger.Stub?.PrintStub(LogClass.ServiceAm); @@ -56,11 +60,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetTransparentVolumeRate(f32) public ResultCode SetTransparentVolumeRate(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment float unknown0 = context.RequestData.ReadSingle(); +#pragma warning restore IDE0059 Logger.Stub?.PrintStub(LogClass.ServiceAm); return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index 5e7d0baea..0d2ec8bc4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -13,28 +13,28 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { private readonly ServiceCtx _context; - private Apm.ManagerServer _apmManagerServer; - private Apm.SystemManagerServer _apmSystemManagerServer; - private Lbl.LblControllerServer _lblControllerServer; + private readonly Apm.ManagerServer _apmManagerServer; + private readonly Apm.SystemManagerServer _apmSystemManagerServer; + private readonly Lbl.LblControllerServer _lblControllerServer; private bool _vrModeEnabled; -#pragma warning disable CS0414 +#pragma warning disable CS0414, IDE0052 // Remove unread private member private bool _lcdBacklighOffEnabled; private bool _requestExitToLibraryAppletAtExecuteNextProgramEnabled; -#pragma warning restore CS0414 - private int _messageEventHandle; - private int _displayResolutionChangedEventHandle; +#pragma warning restore CS0414, IDE0052 + private int _messageEventHandle; + private int _displayResolutionChangedEventHandle; - private KEvent _acquiredSleepLockEvent; + private readonly KEvent _acquiredSleepLockEvent; private int _acquiredSleepLockEventHandle; public ICommonStateGetter(ServiceCtx context) { _context = context; - _apmManagerServer = new Apm.ManagerServer(context); + _apmManagerServer = new Apm.ManagerServer(context); _apmSystemManagerServer = new Apm.SystemManagerServer(context); - _lblControllerServer = new Lbl.LblControllerServer(context); + _lblControllerServer = new Lbl.LblControllerServer(context); _acquiredSleepLockEvent = new KEvent(context.Device.System.KernelContext); } @@ -331,4 +331,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs index 51a112fd0..61cef13b9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDebugFunctions.cs @@ -4,4 +4,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { public IDebugFunctions() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs index 92c97d861..6bd35a779 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs @@ -8,9 +8,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { class IDisplayController : IpcService { - private KTransferMemory _transferMem; - private bool _lastApplicationCaptureBufferAcquired; - private bool _callerAppletCaptureBufferAcquired; + private readonly KTransferMemory _transferMem; + private bool _lastApplicationCaptureBufferAcquired; + private bool _callerAppletCaptureBufferAcquired; public IDisplayController(ServiceCtx context) { @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys public ResultCode TakeScreenShotOfOwnLayer(ServiceCtx context) { bool unknown1 = context.RequestData.ReadBoolean(); - int unknown2 = context.RequestData.ReadInt32(); + int unknown2 = context.RequestData.ReadInt32(); Logger.Stub?.PrintStub(LogClass.ServiceAm, new { unknown1, unknown2 }); @@ -103,4 +103,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs index 24eeefb98..9e46d1cd7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs @@ -4,4 +4,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { public IGlobalStateController() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs index c7c073ff1..78f47e0e9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs @@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { class IHomeMenuFunctions : IpcService { - private KEvent _channelEvent; - private int _channelEventHandle; + private readonly KEvent _channelEvent; + private int _channelEventHandle; public IHomeMenuFunctions(Horizon system) { @@ -45,4 +45,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs index fb870c242..23ba99b04 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs @@ -11,8 +11,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // CreateLibraryApplet(u32, u32) -> object<nn::am::service::ILibraryAppletAccessor> public ResultCode CreateLibraryApplet(ServiceCtx context) { - AppletId appletId = (AppletId)context.RequestData.ReadInt32(); - int libraryAppletMode = context.RequestData.ReadInt32(); + AppletId appletId = (AppletId)context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + int libraryAppletMode = context.RequestData.ReadInt32(); +#pragma warning restore IDE0059 MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System)); @@ -42,8 +44,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys public ResultCode CreateTransferMemoryStorage(ServiceCtx context) { bool isReadOnly = (context.RequestData.ReadInt64() & 1) == 0; - long size = context.RequestData.ReadInt64(); - int handle = context.Request.HandleDesc.ToCopy[0]; + long size = context.RequestData.ReadInt64(); + int handle = context.Request.HandleDesc.ToCopy[0]; KTransferMemory transferMem = context.Process.HandleTable.GetObject<KTransferMemory>(handle); @@ -67,8 +69,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // CreateHandleStorage(u64, handle<copy>) -> object<nn::am::service::IStorage> public ResultCode CreateHandleStorage(ServiceCtx context) { - long size = context.RequestData.ReadInt64(); - int handle = context.Request.HandleDesc.ToCopy[0]; + long size = context.RequestData.ReadInt64(); + int handle = context.Request.HandleDesc.ToCopy[0]; KTransferMemory transferMem = context.Process.HandleTable.GetObject<KTransferMemory>(handle); @@ -88,4 +90,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 8f93117e1..85898f138 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -11,30 +11,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { private readonly ulong _pid; - private KEvent _libraryAppletLaunchableEvent; - private int _libraryAppletLaunchableEventHandle; + private readonly KEvent _libraryAppletLaunchableEvent; + private int _libraryAppletLaunchableEventHandle; private KEvent _accumulatedSuspendedTickChangedEvent; - private int _accumulatedSuspendedTickChangedEventHandle; + private int _accumulatedSuspendedTickChangedEventHandle; private readonly object _fatalSectionLock = new(); private int _fatalSectionCount; // TODO: Set this when the game goes in suspension (go back to home menu ect), we currently don't support that so we can keep it set to 0. - private ulong _accumulatedSuspendedTickValue = 0; + private readonly ulong _accumulatedSuspendedTickValue = 0; // TODO: Determine where those fields are used. - private bool _screenShotPermission = false; - private bool _operationModeChangedNotification = false; +#pragma warning disable IDE0052 // Remove unread private member + private bool _screenShotPermission = false; + private bool _operationModeChangedNotification = false; private bool _performanceModeChangedNotification = false; - private bool _restartMessageEnabled = false; - private bool _outOfFocusSuspendingEnabled = false; - private bool _handlesRequestToDisplay = false; - private bool _autoSleepDisabled = false; + private bool _restartMessageEnabled = false; + private bool _outOfFocusSuspendingEnabled = false; + private bool _handlesRequestToDisplay = false; +#pragma warning restore IDE0052 + private bool _autoSleepDisabled = false; +#pragma warning disable IDE0052 // Remove unread private member private bool _albumImageTakenNotificationEnabled = false; private bool _recordVolumeMuted = false; private uint _screenShotImageOrientation = 0; +#pragma warning restore IDE0052 private uint _idleTimeDetectionExtension = 0; public ISelfController(ServiceCtx context, ulong pid) diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs index 730df5d05..46dc4916d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs @@ -33,4 +33,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs index 84fc5c836..e4b434956 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AlbumReportOption.cs @@ -5,6 +5,6 @@ OverlayNotDisplayed, OverlayDisplayed, Unknown2, - Unknown3 + Unknown3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs index 2920c3298..3f4600fa5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/AppletMessage.cs @@ -2,35 +2,35 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { enum AppletMessage { - None = 0, - ChangeIntoForeground = 1, - ChangeIntoBackground = 2, - Exit = 4, - ApplicationExited = 6, - FocusStateChanged = 15, - Resume = 16, - DetectShortPressingHomeButton = 20, - DetectLongPressingHomeButton = 21, - DetectShortPressingPowerButton = 22, - DetectMiddlePressingPowerButton = 23, - DetectLongPressingPowerButton = 24, - RequestToPrepareSleep = 25, - FinishedSleepSequence = 26, - SleepRequiredByHighTemperature = 27, - SleepRequiredByLowBattery = 28, - AutoPowerDown = 29, - OperationModeChanged = 30, - PerformanceModeChanged = 31, - DetectReceivingCecSystemStandby = 32, - SdCardRemoved = 33, - LaunchApplicationRequested = 50, - RequestToDisplay = 51, - ShowApplicationLogo = 55, - HideApplicationLogo = 56, - ForceHideApplicationLogo = 57, - FloatingApplicationDetected = 60, + None = 0, + ChangeIntoForeground = 1, + ChangeIntoBackground = 2, + Exit = 4, + ApplicationExited = 6, + FocusStateChanged = 15, + Resume = 16, + DetectShortPressingHomeButton = 20, + DetectLongPressingHomeButton = 21, + DetectShortPressingPowerButton = 22, + DetectMiddlePressingPowerButton = 23, + DetectLongPressingPowerButton = 24, + RequestToPrepareSleep = 25, + FinishedSleepSequence = 26, + SleepRequiredByHighTemperature = 27, + SleepRequiredByLowBattery = 28, + AutoPowerDown = 29, + OperationModeChanged = 30, + PerformanceModeChanged = 31, + DetectReceivingCecSystemStandby = 32, + SdCardRemoved = 33, + LaunchApplicationRequested = 50, + RequestToDisplay = 51, + ShowApplicationLogo = 55, + HideApplicationLogo = 56, + ForceHideApplicationLogo = 57, + FloatingApplicationDetected = 60, DetectShortPressingCaptureButton = 90, - AlbumScreenShotTaken = 92, - AlbumRecordingSaved = 93 + AlbumScreenShotTaken = 92, + AlbumRecordingSaved = 93, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs index dfd7d7f2c..afb7d6b42 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/FocusState.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { enum FocusState { - InFocus = 1, - OutOfFocus = 2 + InFocus = 1, + OutOfFocus = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs index a82ed476e..861259783 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/OperationMode.cs @@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys enum OperationMode { Handheld = 0, - Docked = 1 + Docked = 1, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs index e8ba9b615..3ea923f55 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/WirelessPriorityMode.cs @@ -4,6 +4,6 @@ { Default, OptimizedForWlan, - Unknown2 + Unknown2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs index fb16c86e7..e7482b789 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE { internal class AppletFifo<T> : IAppletFifo<T> { - private ConcurrentQueue<T> _dataQueue; + private readonly ConcurrentQueue<T> _dataQueue; public event EventHandler DataAvailable; diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs index 6c9197b3e..63eb2ca54 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletSession.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE { internal class AppletSession { - private IAppletFifo<byte[]> _inputData; - private IAppletFifo<byte[]> _outputData; + private readonly IAppletFifo<byte[]> _inputData; + private readonly IAppletFifo<byte[]> _outputData; public event EventHandler DataAvailable; @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE IAppletFifo<byte[]> inputData, IAppletFifo<byte[]> outputData) { - _inputData = inputData; + _inputData = inputData; _outputData = outputData; _inputData.DataAvailable += OnDataAvailable; diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index 728a10180..0a032562a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -26,4 +26,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs index 190f1a514..311084aa1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorage.cs @@ -2,13 +2,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE { class IStorage : IpcService { - public bool IsReadOnly { get; private set; } - public byte[] Data { get; private set; } + public bool IsReadOnly { get; private set; } + public byte[] Data { get; private set; } public IStorage(byte[] data, bool isReadOnly = false) { IsReadOnly = isReadOnly; - Data = data; + Data = data; } [CommandCmif(0)] @@ -20,4 +20,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs index 4c7e264d0..54c7b69e5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE { class IStorageAccessor : IpcService { - private IStorage _storage; + private readonly IStorage _storage; public IStorageAccessor(IStorage storage) { @@ -83,4 +83,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs index 49e342f27..ef5951d70 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs @@ -11,18 +11,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage public static byte[] MakeLaunchParams(UserProfile userProfile) { // Size needs to be at least 0x88 bytes otherwise application errors. - using (MemoryStream ms = MemoryStreamManager.Shared.GetStream()) - { - BinaryWriter writer = new BinaryWriter(ms); + using MemoryStream ms = MemoryStreamManager.Shared.GetStream(); + BinaryWriter writer = new(ms); - ms.SetLength(0x88); + ms.SetLength(0x88); - writer.Write(LaunchParamsMagic); - writer.Write(1); // IsAccountSelected? Only lower 8 bits actually used. - userProfile.UserId.Write(writer); + writer.Write(LaunchParamsMagic); + writer.Write(1); // IsAccountSelected? Only lower 8 bits actually used. + userProfile.UserId.Write(writer); - return ms.ToArray(); - } + return ms.ToArray(); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs index 917f68658..503de4d2e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs @@ -2,26 +2,26 @@ { enum AppletId { - Application = 0x01, - OverlayDisplay = 0x02, - QLaunch = 0x03, - Starter = 0x04, - Auth = 0x0A, - Cabinet = 0x0B, - Controller = 0x0C, - DataErase = 0x0D, - Error = 0x0E, - NetConnect = 0x0F, - PlayerSelect = 0x10, - SoftwareKeyboard = 0x11, - MiiEdit = 0x12, - LibAppletWeb = 0x13, - LibAppletShop = 0x14, - PhotoViewer = 0x15, - Settings = 0x16, - LibAppletOff = 0x17, + Application = 0x01, + OverlayDisplay = 0x02, + QLaunch = 0x03, + Starter = 0x04, + Auth = 0x0A, + Cabinet = 0x0B, + Controller = 0x0C, + DataErase = 0x0D, + Error = 0x0E, + NetConnect = 0x0F, + PlayerSelect = 0x10, + SoftwareKeyboard = 0x11, + MiiEdit = 0x12, + LibAppletWeb = 0x13, + LibAppletShop = 0x14, + PhotoViewer = 0x15, + Settings = 0x16, + LibAppletOff = 0x17, LibAppletWhitelisted = 0x18, - LibAppletAuth = 0x19, - MyPage = 0x1A + LibAppletAuth = 0x19, + MyPage = 0x1A, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs index 17a485ab0..84fd1b4ca 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE struct AppletIdentifyInfo { public AppletId AppletId; - public uint Padding; - public ulong TitleId; + public uint Padding; + public ulong TitleId; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs index 6c5283370..b6c32da5d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE [StructLayout(LayoutKind.Sequential, Size = 0x4)] struct AppletProcessLaunchReason { - public byte Flag; + public byte Flag; public ushort Unknown1; - public byte Unknown2; + public byte Unknown2; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs index fc1c11e48..1205a7dcf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE [StructLayout(LayoutKind.Sequential, Size = 0x8)] struct LibraryAppletInfo { - public AppletId AppletId; + public AppletId AppletId; public LibraryAppletMode LibraryAppletMode; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs index 6b9a2284d..044a3168c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs @@ -9,6 +9,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE PartialForeground, NoUi, PartialForegroundWithIndirectDisplay, - AllForegroundInitiallyHidden + AllForegroundInitiallyHidden, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index 5ae8f459f..271d00605 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -1,5 +1,4 @@ using LibHac.Account; -using LibHac.Common; using LibHac.Fs; using LibHac.Ncm; using LibHac.Ns; @@ -18,20 +17,20 @@ using Ryujinx.Horizon.Common; using System; using System.Numerics; using System.Threading; -using AccountUid = Ryujinx.HLE.HOS.Services.Account.Acc.UserId; +using AccountUid = Ryujinx.HLE.HOS.Services.Account.Acc.UserId; using ApplicationId = LibHac.Ncm.ApplicationId; namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy { class IApplicationFunctions : IpcService { - private long _defaultSaveDataSize = 200000000; + private long _defaultSaveDataSize = 200000000; private long _defaultJournalSaveDataSize = 200000000; - private KEvent _gpuErrorDetectedSystemEvent; - private KEvent _friendInvitationStorageChannelEvent; - private KEvent _notificationStorageChannelEvent; - private KEvent _healthWarningDisappearedSystemEvent; + private readonly KEvent _gpuErrorDetectedSystemEvent; + private readonly KEvent _friendInvitationStorageChannelEvent; + private readonly KEvent _notificationStorageChannelEvent; + private readonly KEvent _healthWarningDisappearedSystemEvent; private int _gpuErrorDetectedSystemEventHandle; private int _friendInvitationStorageChannelEventHandle; @@ -42,14 +41,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati private int _jitLoaded; - private LibHac.HorizonClient _horizon; + private readonly LibHac.HorizonClient _horizon; public IApplicationFunctions(Horizon system) { // TODO: Find where they are signaled. - _gpuErrorDetectedSystemEvent = new KEvent(system.KernelContext); + _gpuErrorDetectedSystemEvent = new KEvent(system.KernelContext); _friendInvitationStorageChannelEvent = new KEvent(system.KernelContext); - _notificationStorageChannelEvent = new KEvent(system.KernelContext); + _notificationStorageChannelEvent = new KEvent(system.KernelContext); _healthWarningDisappearedSystemEvent = new KEvent(system.KernelContext); _horizon = system.LibHacHorizonManager.AmClient; @@ -115,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati Uid userId = context.RequestData.ReadStruct<AccountUid>().ToLibHacUid(); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new ApplicationId(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); + ApplicationId applicationId = new(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; @@ -137,8 +136,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // TODO: When above calls are implemented, switch to using ns:am long desiredLanguageCode = context.Device.System.State.DesiredLanguageCode; - int supportedLanguages = (int)context.Device.Processes.ActiveApplication.ApplicationControlProperties.SupportedLanguageFlag; - int firstSupported = BitOperations.TrailingZeroCount(supportedLanguages); + int supportedLanguages = (int)context.Device.Processes.ActiveApplication.ApplicationControlProperties.SupportedLanguageFlag; + int firstSupported = BitOperations.TrailingZeroCount(supportedLanguages); if (firstSupported > (int)TitleLanguage.BrazilianPortuguese) { @@ -168,7 +167,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // SetTerminateResult(u32) public ResultCode SetTerminateResult(ServiceCtx context) { - LibHac.Result result = new LibHac.Result(context.RequestData.ReadUInt32()); + LibHac.Result result = new(context.RequestData.ReadUInt32()); Logger.Info?.Print(LogClass.ServiceAm, $"Result = 0x{result.Value:x8} ({result.ToStringWithName()})."); @@ -190,14 +189,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati public ResultCode ExtendSaveData(ServiceCtx context) { SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64(); - Uid userId = context.RequestData.ReadStruct<Uid>(); - long saveDataSize = context.RequestData.ReadInt64(); - long journalSize = context.RequestData.ReadInt64(); + Uid userId = context.RequestData.ReadStruct<Uid>(); + long saveDataSize = context.RequestData.ReadInt64(); + long journalSize = context.RequestData.ReadInt64(); // NOTE: Service calls nn::fs::ExtendApplicationSaveData. // Since LibHac currently doesn't support this method, we can stub it for now. - _defaultSaveDataSize = saveDataSize; + _defaultSaveDataSize = saveDataSize; _defaultJournalSaveDataSize = journalSize; context.ResponseData.Write((uint)ResultCode.Success); @@ -212,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati public ResultCode GetSaveDataSize(ServiceCtx context) { SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64(); - Uid userId = context.RequestData.ReadStruct<Uid>(); + Uid userId = context.RequestData.ReadStruct<Uid>(); // NOTE: Service calls nn::fs::FindSaveDataWithFilter with SaveDataType = 1 hardcoded. // Then it calls nn::fs::GetSaveDataAvailableSize and nn::fs::GetSaveDataJournalSize to get the sizes. @@ -235,14 +234,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati long journalSize = context.RequestData.ReadInt64(); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new ApplicationId(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); + ApplicationId applicationId = new(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; LibHac.Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize, out CacheStorageTargetMedia storageTarget, applicationId, in nacp, index, saveSize, journalSize); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write((ulong)storageTarget); context.ResponseData.Write(requiredSize); @@ -391,10 +393,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // InitializeApplicationCopyrightFrameBuffer(s32 width, s32 height, handle<copy, transfer_memory> transfer_memory, u64 transfer_memory_size) public ResultCode InitializeApplicationCopyrightFrameBuffer(ServiceCtx context) { - int width = context.RequestData.ReadInt32(); - int height = context.RequestData.ReadInt32(); - ulong transferMemorySize = context.RequestData.ReadUInt64(); - int transferMemoryHandle = context.Request.HandleDesc.ToCopy[0]; + int width = context.RequestData.ReadInt32(); + int height = context.RequestData.ReadInt32(); + ulong transferMemorySize = context.RequestData.ReadUInt64(); + int transferMemoryHandle = context.Request.HandleDesc.ToCopy[0]; ulong transferMemoryAddress = context.Process.HandleTable.GetObject<KTransferMemory>(transferMemoryHandle).Address; ResultCode resultCode = ResultCode.InvalidParameters; @@ -437,13 +439,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // SetApplicationCopyrightImage(buffer<bytes, 0x45> frame_buffer, s32 x, s32 y, s32 width, s32 height, s32 window_origin_mode) public ResultCode SetApplicationCopyrightImage(ServiceCtx context) { - ulong frameBufferPos = context.Request.SendBuff[0].Position; - ulong frameBufferSize = context.Request.SendBuff[0].Size; - int x = context.RequestData.ReadInt32(); - int y = context.RequestData.ReadInt32(); - int width = context.RequestData.ReadInt32(); - int height = context.RequestData.ReadInt32(); - uint windowOriginMode = context.RequestData.ReadUInt32(); + ulong frameBufferPos = context.Request.SendBuff[0].Position; + ulong frameBufferSize = context.Request.SendBuff[0].Size; + int x = context.RequestData.ReadInt32(); + int y = context.RequestData.ReadInt32(); + int width = context.RequestData.ReadInt32(); + int height = context.RequestData.ReadInt32(); + uint windowOriginMode = context.RequestData.ReadUInt32(); ResultCode resultCode = ResultCode.InvalidParameters; @@ -653,11 +655,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati if (Interlocked.Exchange(ref _jitLoaded, 1) == 0) { string jitPath = context.Device.System.ContentManager.GetInstalledContentPath(0x010000000000003B, StorageId.BuiltInSystem, NcaContentType.Program); - string filePath = context.Device.FileSystem.SwitchPathToSystemPath(jitPath); + string filePath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(jitPath); if (string.IsNullOrWhiteSpace(filePath)) { - throw new InvalidSystemResourceException($"JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)"); + throw new InvalidSystemResourceException("JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)"); } context.Device.LoadNca(filePath); @@ -672,4 +674,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs index 40432074d..7d82dcf5f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/LaunchParameterKind.cs @@ -4,6 +4,6 @@ { UserChannel = 1, PreselectedUser, - Unknown + Unknown, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs index efc284a5c..29ecf4217 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/Types/ProgramSpecifyKind.cs @@ -4,6 +4,6 @@ { ExecuteProgram, SubApplicationProgram, - RestartProgram + RestartProgram, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs index 50e3be274..b24e1bf4f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs @@ -84,4 +84,4 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs index 3a4c71e4b..9814976f7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Am return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs b/src/Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs index 8c72319c3..824d4c22f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/Idle/IPolicyManagerSystem.cs @@ -5,4 +5,4 @@ { public IPolicyManagerSystem(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs b/src/Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs index 2856e6d7b..44c4dafce 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/Omm/IOperationModeManager.cs @@ -5,4 +5,4 @@ { public IOperationModeManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs index 5cafff678..9142f65e0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs @@ -2,29 +2,29 @@ namespace Ryujinx.HLE.HOS.Services.Am { enum ResultCode { - ModuleId = 128, + ModuleId = 128, ErrorCodeShift = 9, Success = 0, - NotAvailable = (2 << ErrorCodeShift) | ModuleId, - NoMessages = (3 << ErrorCodeShift) | ModuleId, - AppletLaunchFailed = (35 << ErrorCodeShift) | ModuleId, - TitleIdNotFound = (37 << ErrorCodeShift) | ModuleId, - ObjectInvalid = (500 << ErrorCodeShift) | ModuleId, - IStorageInUse = (502 << ErrorCodeShift) | ModuleId, - OutOfBounds = (503 << ErrorCodeShift) | ModuleId, - BufferNotAcquired = (504 << ErrorCodeShift) | ModuleId, - BufferAlreadyAcquired = (505 << ErrorCodeShift) | ModuleId, - InvalidParameters = (506 << ErrorCodeShift) | ModuleId, - OpenedAsWrongType = (511 << ErrorCodeShift) | ModuleId, + NotAvailable = (2 << ErrorCodeShift) | ModuleId, + NoMessages = (3 << ErrorCodeShift) | ModuleId, + AppletLaunchFailed = (35 << ErrorCodeShift) | ModuleId, + TitleIdNotFound = (37 << ErrorCodeShift) | ModuleId, + ObjectInvalid = (500 << ErrorCodeShift) | ModuleId, + IStorageInUse = (502 << ErrorCodeShift) | ModuleId, + OutOfBounds = (503 << ErrorCodeShift) | ModuleId, + BufferNotAcquired = (504 << ErrorCodeShift) | ModuleId, + BufferAlreadyAcquired = (505 << ErrorCodeShift) | ModuleId, + InvalidParameters = (506 << ErrorCodeShift) | ModuleId, + OpenedAsWrongType = (511 << ErrorCodeShift) | ModuleId, UnbalancedFatalSection = (512 << ErrorCodeShift) | ModuleId, - NullObject = (518 << ErrorCodeShift) | ModuleId, + NullObject = (518 << ErrorCodeShift) | ModuleId, MemoryAllocationFailed = (600 << ErrorCodeShift) | ModuleId, - StackPoolExhausted = (712 << ErrorCodeShift) | ModuleId, - DebugModeNotEnabled = (974 << ErrorCodeShift) | ModuleId, - DevFunctionNotEnabled = (980 << ErrorCodeShift) | ModuleId, - NotImplemented = (998 << ErrorCodeShift) | ModuleId, - Stubbed = (999 << ErrorCodeShift) | ModuleId + StackPoolExhausted = (712 << ErrorCodeShift) | ModuleId, + DebugModeNotEnabled = (974 << ErrorCodeShift) | ModuleId, + DevFunctionNotEnabled = (980 << ErrorCodeShift) | ModuleId, + NotImplemented = (998 << ErrorCodeShift) | ModuleId, + Stubbed = (999 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs b/src/Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs index a393f76be..40a1300c1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/Spsm/IPowerStateInterface.cs @@ -5,4 +5,4 @@ { public IPowerStateInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs index b31ccf8a3..edb9618c6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/Tcap/IManager.cs @@ -5,4 +5,4 @@ { public IManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Apm/IManager.cs index 72e39a77f..83215befa 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/IManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/IManager.cs @@ -40,4 +40,4 @@ namespace Ryujinx.HLE.HOS.Services.Apm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs b/src/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs index 9620c30a1..bb0049d1b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/IManagerPrivileged.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Apm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/ISession.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ISession.cs index f828cd175..6ee696056 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/ISession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/ISession.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm // SetPerformanceConfiguration(nn::apm::PerformanceMode, nn::apm::PerformanceConfiguration) public ResultCode SetPerformanceConfiguration(ServiceCtx context) { - PerformanceMode performanceMode = (PerformanceMode)context.RequestData.ReadInt32(); + PerformanceMode performanceMode = (PerformanceMode)context.RequestData.ReadInt32(); PerformanceConfiguration performanceConfiguration = (PerformanceConfiguration)context.RequestData.ReadInt32(); return SetPerformanceConfiguration(performanceMode, performanceConfiguration); @@ -42,4 +42,4 @@ namespace Ryujinx.HLE.HOS.Services.Apm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs index 9d2c7b0b4..375423cf2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/ISystemManager.cs @@ -39,4 +39,4 @@ namespace Ryujinx.HLE.HOS.Services.Apm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs index af0519344..9a3a0462f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/ManagerServer.cs @@ -28,4 +28,4 @@ return _context.Device.System.PerformanceState.CpuOverclockEnabled; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs b/src/Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs index d03bf6c72..0fb6c28a6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/PerformanceState.cs @@ -7,19 +7,19 @@ public bool CpuOverclockEnabled = false; public PerformanceMode PerformanceMode = PerformanceMode.Default; - public CpuBoostMode CpuBoostMode = CpuBoostMode.Disabled; + public CpuBoostMode CpuBoostMode = CpuBoostMode.Disabled; public PerformanceConfiguration DefaultPerformanceConfiguration = PerformanceConfiguration.PerformanceConfiguration7; - public PerformanceConfiguration BoostPerformanceConfiguration = PerformanceConfiguration.PerformanceConfiguration8; + public PerformanceConfiguration BoostPerformanceConfiguration = PerformanceConfiguration.PerformanceConfiguration8; public PerformanceConfiguration GetCurrentPerformanceConfiguration(PerformanceMode performanceMode) { return performanceMode switch { PerformanceMode.Default => DefaultPerformanceConfiguration, - PerformanceMode.Boost => BoostPerformanceConfiguration, - _ => PerformanceConfiguration.PerformanceConfiguration7 + PerformanceMode.Boost => BoostPerformanceConfiguration, + _ => PerformanceConfiguration.PerformanceConfiguration7, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs index c4499b014..3cbfbffb4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/ResultCode.cs @@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Services.Apm { enum ResultCode { - ModuleId = 148, + ModuleId = 148, ErrorCodeShift = 9, Success = 0, - InvalidParameters = (1 << ErrorCodeShift) | ModuleId + InvalidParameters = (1 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs b/src/Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs index 3ef713cf3..260992be7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/SessionServer.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm { private readonly ServiceCtx _context; - public SessionServer(ServiceCtx context) : base(context) + public SessionServer(ServiceCtx context) : base(context) { _context = context; } @@ -55,4 +55,4 @@ namespace Ryujinx.HLE.HOS.Services.Apm // NOTE: This call seems to overclock the system, since we emulate it, it's fine to do nothing instead. } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs index a62642360..3fe5b3834 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/SystemManagerServer.cs @@ -25,4 +25,4 @@ return _context.Device.System.PerformanceState.GetCurrentPerformanceConfiguration(_context.Device.System.PerformanceState.PerformanceMode); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs b/src/Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs index 587142c83..c84d32baf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/Types/CpuBoostMode.cs @@ -2,8 +2,8 @@ { enum CpuBoostMode { - Disabled = 0, - BoostCPU = 1, // Uses PerformanceConfiguration13 and PerformanceConfiguration14, or PerformanceConfiguration15 and PerformanceConfiguration16 - ConservePower = 2 // Uses PerformanceConfiguration15 and PerformanceConfiguration16. + Disabled = 0, + BoostCPU = 1, // Uses PerformanceConfiguration13 and PerformanceConfiguration14, or PerformanceConfiguration15 and PerformanceConfiguration16 + ConservePower = 2, // Uses PerformanceConfiguration15 and PerformanceConfiguration16. } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs b/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs index e8c5752e4..6dd193f9b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceConfiguration.cs @@ -2,21 +2,21 @@ { enum PerformanceConfiguration : uint // Clocks are all in MHz. { // CPU | GPU | RAM | NOTE - PerformanceConfiguration1 = 0x00010000, // 1020 | 384 | 1600 | Only available while docked. - PerformanceConfiguration2 = 0x00010001, // 1020 | 768 | 1600 | Only available while docked. - PerformanceConfiguration3 = 0x00010002, // 1224 | 691.2 | 1600 | Only available for SDEV units. - PerformanceConfiguration4 = 0x00020000, // 1020 | 230.4 | 1600 | Only available for SDEV units. - PerformanceConfiguration5 = 0x00020001, // 1020 | 307.2 | 1600 | - PerformanceConfiguration6 = 0x00020002, // 1224 | 230.4 | 1600 | - PerformanceConfiguration7 = 0x00020003, // 1020 | 307 | 1331.2 | - PerformanceConfiguration8 = 0x00020004, // 1020 | 384 | 1331.2 | - PerformanceConfiguration9 = 0x00020005, // 1020 | 307.2 | 1065.6 | + PerformanceConfiguration1 = 0x00010000, // 1020 | 384 | 1600 | Only available while docked. + PerformanceConfiguration2 = 0x00010001, // 1020 | 768 | 1600 | Only available while docked. + PerformanceConfiguration3 = 0x00010002, // 1224 | 691.2 | 1600 | Only available for SDEV units. + PerformanceConfiguration4 = 0x00020000, // 1020 | 230.4 | 1600 | Only available for SDEV units. + PerformanceConfiguration5 = 0x00020001, // 1020 | 307.2 | 1600 | + PerformanceConfiguration6 = 0x00020002, // 1224 | 230.4 | 1600 | + PerformanceConfiguration7 = 0x00020003, // 1020 | 307 | 1331.2 | + PerformanceConfiguration8 = 0x00020004, // 1020 | 384 | 1331.2 | + PerformanceConfiguration9 = 0x00020005, // 1020 | 307.2 | 1065.6 | PerformanceConfiguration10 = 0x00020006, // 1020 | 384 | 1065.6 | PerformanceConfiguration11 = 0x92220007, // 1020 | 460.8 | 1600 | PerformanceConfiguration12 = 0x92220008, // 1020 | 460.8 | 1331.2 | PerformanceConfiguration13 = 0x92220009, // 1785 | 768 | 1600 | 7.0.0+ PerformanceConfiguration14 = 0x9222000A, // 1785 | 768 | 1331.2 | 7.0.0+ PerformanceConfiguration15 = 0x9222000B, // 1020 | 768 | 1600 | 7.0.0+ - PerformanceConfiguration16 = 0x9222000C // 1020 | 768 | 1331.2 | 7.0.0+ + PerformanceConfiguration16 = 0x9222000C, // 1020 | 768 | 1331.2 | 7.0.0+ } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs b/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs index 6d6f9643e..0a7719657 100644 --- a/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Apm/Types/PerformanceMode.cs @@ -3,6 +3,6 @@ enum PerformanceMode : uint { Default = 0, - Boost = 1 + Boost = 1, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs b/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs index 3e4eca0ac..9c2b9d19a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs +++ b/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs @@ -5,10 +5,10 @@ namespace Ryujinx.HLE.HOS.Services.Arp class ApplicationLaunchProperty { public ulong TitleId; - public int Version; - public byte BaseGameStorageId; - public byte UpdateGameStorageId; -#pragma warning disable CS0649 + public int Version; + public byte BaseGameStorageId; + public byte UpdateGameStorageId; +#pragma warning disable CS0649 // Field is never assigned to public short Padding; #pragma warning restore CS0649 @@ -18,10 +18,10 @@ namespace Ryujinx.HLE.HOS.Services.Arp { return new ApplicationLaunchProperty { - TitleId = 0x00, - Version = 0x00, - BaseGameStorageId = (byte)StorageId.BuiltInSystem, - UpdateGameStorageId = (byte)StorageId.None + TitleId = 0x00, + Version = 0x00, + BaseGameStorageId = (byte)StorageId.BuiltInSystem, + UpdateGameStorageId = (byte)StorageId.None, }; } } @@ -33,11 +33,11 @@ namespace Ryujinx.HLE.HOS.Services.Arp return new ApplicationLaunchProperty { - TitleId = context.Device.Processes.ActiveApplication.ProgramId, - Version = 0x00, - BaseGameStorageId = (byte)StorageId.BuiltInSystem, - UpdateGameStorageId = (byte)StorageId.None + TitleId = context.Device.Processes.ActiveApplication.ProgramId, + Version = 0x00, + BaseGameStorageId = (byte)StorageId.BuiltInSystem, + UpdateGameStorageId = (byte)StorageId.None, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs b/src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs index 35a2de0c1..90cba861a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs @@ -5,4 +5,4 @@ { public IReader(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs b/src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs index 8d13f0fb8..7c3992c73 100644 --- a/src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs @@ -5,4 +5,4 @@ { public IWriter(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs b/src/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs index d7686871a..fdddb79e5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs @@ -3,7 +3,6 @@ using LibHac.Common; using LibHac.Ncm; using LibHac.Ns; using System; - using ApplicationId = LibHac.ApplicationId; namespace Ryujinx.HLE.HOS.Services.Arp @@ -17,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp launchProperty = new LibHac.Arp.ApplicationLaunchProperty { StorageId = StorageId.BuiltInUser, - ApplicationId = ApplicationId + ApplicationId = ApplicationId, }; return Result.Success; @@ -30,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp launchProperty = new LibHac.Arp.ApplicationLaunchProperty { StorageId = StorageId.BuiltInUser, - ApplicationId = applicationId + ApplicationId = applicationId, }; return Result.Success; @@ -73,4 +72,4 @@ namespace Ryujinx.HLE.HOS.Services.Arp return Result.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs index ee85ded98..ee8e46436 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs @@ -10,9 +10,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn { class AudioIn : IAudioIn { - private AudioInputSystem _system; - private uint _processHandle; - private KernelContext _kernelContext; + private readonly AudioInputSystem _system; + private readonly uint _processHandle; + private readonly KernelContext _kernelContext; public AudioIn(AudioInputSystem system, KernelContext kernelContext, uint processHandle) { @@ -80,9 +80,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn { IWritableEvent outEvent = _system.RegisterBufferEvent(); - if (outEvent is AudioKernelEvent) + if (outEvent is AudioKernelEvent kernelEvent) { - return ((AudioKernelEvent)outEvent).Event; + return kernelEvent.Event; } else { @@ -105,4 +105,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return (ResultCode)_system.Stop(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs index a80b94025..81b76e7a8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn { class AudioInServer : DisposableIpcService { - private IAudioIn _impl; + private readonly IAudioIn _impl; public AudioInServer(IAudioIn impl) { @@ -77,14 +77,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn ulong position = context.Request.ReceiveBuff[0].Position; ulong size = context.Request.ReceiveBuff[0].Size; - using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size)) - { - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); + using WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size); + ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); - context.ResponseData.Write(releasedCount); + context.ResponseData.Write(releasedCount); - return result; - } + return result; } [CommandCmif(6)] @@ -131,14 +129,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn { (ulong position, ulong size) = context.Request.GetBufferType0x22(); - using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size)) - { - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); + using WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size); + ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); - context.ResponseData.Write(releasedCount); + context.ResponseData.Write(releasedCount); - return result; - } + return result; } [CommandCmif(10)] // 3.0.0+ diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs index 36b0ed282..ba7462d36 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio { class AudioInManager : IAudioInManager { - private AudioInManagerImpl _impl; + private readonly AudioInManagerImpl _impl; public AudioInManager(AudioInManagerImpl impl) { diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs index 755caee52..ac1863abe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio { private const int AudioInNameSize = 0x100; - private IAudioInManager _impl; + private readonly IAudioInManager _impl; public AudioInManagerServer(ServiceCtx context) : this(context, new AudioInManager(context.Device.System.AudioInputManager)) { } @@ -69,7 +69,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio ulong deviceNameInputSize = context.Request.SendBuff[0].Size; ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; +#pragma warning restore IDE0059 uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; @@ -136,7 +138,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio ulong appletResourceUserId = context.RequestData.ReadUInt64(); (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22(); +#pragma warning restore IDE0059 uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; @@ -200,7 +204,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio public ResultCode OpenAudioInProtocolSpecified(ServiceCtx context) { // NOTE: We always assume that only the default device will be plugged (we never report any USB Audio Class type devices). +#pragma warning disable IDE0059 // Remove unnecessary value assignment bool protocolSpecifiedRelated = context.RequestData.ReadUInt64() == 1; +#pragma warning restore IDE0059 AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); @@ -209,7 +215,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio ulong deviceNameInputSize = context.Request.SendBuff[0].Size; ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; +#pragma warning disable IDE0051, IDE0059 // Remove unused private member ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; +#pragma warning restore IDE0051, IDE0059 uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs index f25884523..8624ab9bc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs @@ -10,9 +10,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut { class AudioOut : IAudioOut { - private AudioOutputSystem _system; - private uint _processHandle; - private KernelContext _kernelContext; + private readonly AudioOutputSystem _system; + private readonly uint _processHandle; + private readonly KernelContext _kernelContext; public AudioOut(AudioOutputSystem system, KernelContext kernelContext, uint processHandle) { @@ -80,9 +80,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut { IWritableEvent outEvent = _system.RegisterBufferEvent(); - if (outEvent is AudioKernelEvent) + if (outEvent is AudioKernelEvent kernelEvent) { - return ((AudioKernelEvent)outEvent).Event; + return kernelEvent.Event; } else { diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs index 329e17941..3b6834f7c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut { class AudioOutServer : DisposableIpcService { - private IAudioOut _impl; + private readonly IAudioOut _impl; public AudioOutServer(IAudioOut impl) { @@ -77,14 +77,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut ulong position = context.Request.ReceiveBuff[0].Position; ulong size = context.Request.ReceiveBuff[0].Size; - using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size)) - { - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); + using WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size); + ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); - context.ResponseData.Write(releasedCount); + context.ResponseData.Write(releasedCount); - return result; - } + return result; } [CommandCmif(6)] @@ -117,14 +115,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut { (ulong position, ulong size) = context.Request.GetBufferType0x22(); - using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size)) - { - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); + using WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size); + ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); - context.ResponseData.Write(releasedCount); + context.ResponseData.Write(releasedCount); - return result; - } + return result; } [CommandCmif(9)] // 4.0.0+ diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs index e95de0575..fbbb3e1df 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio { class AudioOutManager : IAudioOutManager { - private AudioOutManagerImpl _impl; + private readonly AudioOutManagerImpl _impl; public AudioOutManager(AudioOutManagerImpl impl) { diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs index 7c5d8c4ec..ca4d61630 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio { private const int AudioOutNameSize = 0x100; - private IAudioOutManager _impl; + private readonly IAudioOutManager _impl; public AudioOutManagerServer(ServiceCtx context) : this(context, new AudioOutManager(context.Device.System.AudioOutputManager)) { } @@ -69,7 +69,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio ulong deviceNameInputSize = context.Request.SendBuff[0].Size; ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; +#pragma warning restore IDE0059 uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; @@ -136,7 +138,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio ulong appletResourceUserId = context.RequestData.ReadUInt64(); (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22(); +#pragma warning restore IDE0059 uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs index 724a1e9ec..9a08f1ba9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs @@ -7,13 +7,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { class AudioDevice : IAudioDevice { - private VirtualDeviceSession[] _sessions; - private ulong _appletResourceId; - private int _revision; - private bool _isUsbDeviceSupported; + private readonly VirtualDeviceSession[] _sessions; +#pragma warning disable IDE0052 // Remove unread private member + private readonly ulong _appletResourceId; + private readonly int _revision; +#pragma warning restore IDE0052 + private readonly bool _isUsbDeviceSupported; - private VirtualDeviceSessionRegistry _registry; - private KEvent _systemEvent; + private readonly VirtualDeviceSessionRegistry _registry; + private readonly KEvent _systemEvent; public AudioDevice(VirtualDeviceSessionRegistry registry, KernelContext context, ulong appletResourceId, int revision) { @@ -21,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer _appletResourceId = appletResourceId; _revision = revision; - BehaviourContext behaviourContext = new BehaviourContext(); + BehaviourContext behaviourContext = new(); behaviourContext.SetUserRevision(revision); _isUsbDeviceSupported = behaviourContext.IsAudioUsbDeviceOutputSupported(); diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs index e7a751216..9694406d6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { private const int AudioDeviceNameSize = 0x100; - private IAudioDevice _impl; + private readonly IAudioDevice _impl; public AudioDeviceServer(IAudioDevice impl) { diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs index 5b682bf84..8b8e55fb2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { class AudioRenderer : IAudioRenderer { - private AudioRenderSystem _impl; + private readonly AudioRenderSystem _impl; public AudioRenderer(AudioRenderSystem impl) { @@ -55,9 +55,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer if (resultCode == ResultCode.Success) { - if (outEvent is AudioKernelEvent) + if (outEvent is AudioKernelEvent kernelEvent) { - systemEvent = ((AudioKernelEvent)outEvent).Event; + systemEvent = kernelEvent.Event; } else { diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index 5d8e086d0..af8d20ba7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { class AudioRendererServer : DisposableIpcService { - private IAudioRenderer _impl; + private readonly IAudioRenderer _impl; public AudioRendererServer(IAudioRenderer impl) { @@ -69,29 +69,27 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); - using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.RentCleared(outputSize)) - using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.RentCleared(performanceOutputSize)) + using IMemoryOwner<byte> outputOwner = ByteMemoryPool.RentCleared(outputSize); + using IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.RentCleared(performanceOutputSize); + Memory<byte> output = outputOwner.Memory; + Memory<byte> performanceOutput = performanceOutputOwner.Memory; + + using MemoryHandle outputHandle = output.Pin(); + using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); + + ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); + + if (result == ResultCode.Success) { - Memory<byte> output = outputOwner.Memory; - Memory<byte> performanceOutput = performanceOutputOwner.Memory; - - using MemoryHandle outputHandle = output.Pin(); - using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); - - ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); - - if (result == ResultCode.Success) - { - context.Memory.Write(outputPosition, output.Span); - context.Memory.Write(performanceOutputPosition, performanceOutput.Span); - } - else - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}"); - } - - return result; + context.Memory.Write(outputPosition, output.Span); + context.Memory.Write(performanceOutputPosition, performanceOutput.Span); } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}"); + } + + return result; } [CommandCmif(5)] diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs index 1918a977f..4a1b5ffb1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { string[] ListAudioDeviceName(); ResultCode SetAudioDeviceOutputVolume(string name, float volume); - ResultCode GetAudioDeviceOutputVolume(string name, out float volume); + ResultCode GetAudioDeviceOutputVolume(string name, out float volume); string GetActiveAudioDeviceName(); KEvent QueryAudioDeviceSystemEvent(); uint GetActiveChannelCount(); diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs index 40e71a43b..b40c32a12 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs @@ -10,8 +10,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio { class AudioRendererManager : IAudioRendererManager { - private AudioRendererManagerImpl _impl; - private VirtualDeviceSessionRegistry _registry; + private readonly AudioRendererManagerImpl _impl; + private readonly VirtualDeviceSessionRegistry _registry; public AudioRendererManager(AudioRendererManagerImpl impl, VirtualDeviceSessionRegistry registry) { diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs index 80b54e8c7..58a1daf10 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio { private const int InitialRevision = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('1' << 24); - private IAudioRendererManager _impl; + private readonly IAudioRendererManager _impl; public AudioRendererManagerServer(ServiceCtx context) : this(context, new AudioRendererManager(context.Device.System.AudioRendererManager, context.Device.System.AudioDeviceSessionRegistry)) { } diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs index b77fc4b05..c5dd2f00d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs @@ -24,4 +24,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager _decoder.ResetState(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs index 944541cce..9ff511a50 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs @@ -89,4 +89,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs index 9047c2668..cc83be5e3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs @@ -8,4 +8,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize); void ResetState(); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs index e94b31ca1..3d5d2839f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs @@ -87,10 +87,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager private ResultCode DecodeInterleavedInternal(ServiceCtx context, OpusDecoderFlags flags, bool reset, bool withPerf) { - ulong inPosition = context.Request.SendBuff[0].Position; - ulong inSize = context.Request.SendBuff[0].Size; + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; ulong outputPosition = context.Request.ReceiveBuff[0].Position; - ulong outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputSize = context.Request.ReceiveBuff[0].Size; ReadOnlySpan<byte> input = context.Memory.GetSpan(inPosition, (int)inSize); @@ -113,4 +113,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager return result; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs index 23721d3bf..910bb23ee 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs @@ -25,4 +25,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager _decoder.ResetState(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs index 1bd2e31de..223675808 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs @@ -5,4 +5,4 @@ { public IAudioController(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs index 9bbe5b0e4..709320c82 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs @@ -9,4 +9,4 @@ namespace Ryujinx.HLE.HOS.Services.Audio public ResultCode OpenAudioIn(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs index 37d9a8fe8..1735768c0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs @@ -5,4 +5,4 @@ { public IAudioInManagerForApplet(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs index 1a497efb6..ebdcfd8c2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs @@ -5,4 +5,4 @@ { public IAudioInManagerForDebugger(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs index 4b41b0cfc..1c3502d6c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs @@ -5,4 +5,4 @@ { public IAudioOutManagerForApplet(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs index 41cde9726..1da017cbe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs @@ -5,4 +5,4 @@ { public IAudioOutManagerForDebugger(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs index ca5768cca..28aa6d8c9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs @@ -5,4 +5,4 @@ { public IAudioRendererManagerForApplet(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs index a970ae45c..50754f8a8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs @@ -5,4 +5,4 @@ { public IAudioRendererManagerForDebugger(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs index 59e3ad09b..73bc629fe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs @@ -5,4 +5,4 @@ { public IAudioSnoopManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs index 014350081..56647d04f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs @@ -5,4 +5,4 @@ { public IFinalOutputRecorderManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs index d8fd270d5..6dfd5f968 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs @@ -5,4 +5,4 @@ { public IFinalOutputRecorderManagerForApplet(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs index a8ec51ee5..653ab598c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs @@ -5,4 +5,4 @@ { public IFinalOutputRecorderManagerForDebugger(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs index 8df8a38c9..b69a23921 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio // Initialize(bytes<8, 4>, u32, handle<copy>) -> object<nn::codec::detail::IHardwareOpusDecoder> public ResultCode Initialize(ServiceCtx context) { - int sampleRate = context.RequestData.ReadInt32(); + int sampleRate = context.RequestData.ReadInt32(); int channelsCount = context.RequestData.ReadInt32(); MakeObject(context, new IHardwareOpusDecoder(sampleRate, channelsCount, OpusDecoderFlags.None)); @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio // GetWorkBufferSize(bytes<8, 4>) -> u32 public ResultCode GetWorkBufferSize(ServiceCtx context) { - int sampleRate = context.RequestData.ReadInt32(); + int sampleRate = context.RequestData.ReadInt32(); int channelsCount = context.RequestData.ReadInt32(); int opusDecoderSize = GetOpusDecoderSize(channelsCount); @@ -196,10 +196,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio private static int GetCeltDecoderSize(int channelsCount) { const int DecodeBufferSize = 0x2030; - const int Overlap = 120; - const int EBandsCount = 21; + const int Overlap = 120; + const int EBandsCount = 21; return (DecodeBufferSize + Overlap * 4) * channelsCount + EBandsCount * 16 + 0x50; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs index fd2091c2a..c1d49109c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs @@ -2,20 +2,20 @@ namespace Ryujinx.HLE.HOS.Services.Audio { enum ResultCode { - ModuleId = 153, + ModuleId = 153, ErrorCodeShift = 9, Success = 0, - DeviceNotFound = (1 << ErrorCodeShift) | ModuleId, - UnsupportedRevision = (2 << ErrorCodeShift) | ModuleId, + DeviceNotFound = (1 << ErrorCodeShift) | ModuleId, + UnsupportedRevision = (2 << ErrorCodeShift) | ModuleId, UnsupportedSampleRate = (3 << ErrorCodeShift) | ModuleId, - BufferSizeTooSmall = (4 << ErrorCodeShift) | ModuleId, - OpusInvalidInput = (6 << ErrorCodeShift) | ModuleId, - TooManyBuffersInUse = (8 << ErrorCodeShift) | ModuleId, - InvalidChannelCount = (10 << ErrorCodeShift) | ModuleId, - InvalidOperation = (513 << ErrorCodeShift) | ModuleId, - InvalidHandle = (1536 << ErrorCodeShift) | ModuleId, - OutputAlreadyStarted = (1540 << ErrorCodeShift) | ModuleId + BufferSizeTooSmall = (4 << ErrorCodeShift) | ModuleId, + OpusInvalidInput = (6 << ErrorCodeShift) | ModuleId, + TooManyBuffersInUse = (8 << ErrorCodeShift) | ModuleId, + InvalidChannelCount = (10 << ErrorCodeShift) | ModuleId, + InvalidOperation = (513 << ErrorCodeShift) | ModuleId, + InvalidHandle = (1536 << ErrorCodeShift) | ModuleId, + OutputAlreadyStarted = (1540 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs index 5ae0eb1e4..2c5478276 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.Types { OpusPacketHeader header = MemoryMarshal.Cast<byte, OpusPacketHeader>(data)[0]; - header.length = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.length) : header.length; + header.length = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.length) : header.length; header.finalRange = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.finalRange) : header.finalRange; return header; diff --git a/src/Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs b/src/Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs index 4926d4d82..97d0cf946 100644 --- a/src/Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Bgtc/IStateControlService.cs @@ -5,4 +5,4 @@ { public IStateControlService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs b/src/Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs index a032c3809..1b4b45f61 100644 --- a/src/Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Bgtc/ITaskService.cs @@ -5,4 +5,4 @@ { public ITaskService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs b/src/Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs index 81f4a7d29..5b8624001 100644 --- a/src/Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Bluetooth/BluetoothDriver/BluetoothEventManager.cs @@ -5,21 +5,21 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth.BluetoothDriver static class BluetoothEventManager { public static KEvent InitializeBleDebugEvent; - public static int InitializeBleDebugEventHandle; + public static int InitializeBleDebugEventHandle; public static KEvent UnknownBleDebugEvent; - public static int UnknownBleDebugEventHandle; + public static int UnknownBleDebugEventHandle; public static KEvent RegisterBleDebugEvent; - public static int RegisterBleDebugEventHandle; + public static int RegisterBleDebugEventHandle; public static KEvent InitializeBleEvent; - public static int InitializeBleEventHandle; + public static int InitializeBleEventHandle; public static KEvent UnknownBleEvent; - public static int UnknownBleEventHandle; + public static int UnknownBleEventHandle; public static KEvent RegisterBleEvent; - public static int RegisterBleEventHandle; + public static int RegisterBleEventHandle; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs b/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs index feff5a73f..ace2c86a4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothDriver.cs @@ -10,9 +10,9 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth [Service("btdrv")] class IBluetoothDriver : IpcService { -#pragma warning disable CS0414 +#pragma warning disable CS0414, IDE0052 // Remove unread private member private string _unknownLowEnergy; -#pragma warning restore CS0414 +#pragma warning restore CS0414, IDE0052 public IBluetoothDriver(ServiceCtx context) { } @@ -100,4 +100,4 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs b/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs index 1a5e25a48..04782d08e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs +++ b/src/Ryujinx.HLE/HOS/Services/Bluetooth/IBluetoothUser.cs @@ -27,4 +27,4 @@ namespace Ryujinx.HLE.HOS.Services.Bluetooth return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs index 3c9938ad7..da45dc776 100644 --- a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmUser/IBtmUserCore.cs @@ -8,16 +8,16 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser class IBtmUserCore : IpcService { public KEvent _bleScanEvent; - public int _bleScanEventHandle; + public int _bleScanEventHandle; public KEvent _bleConnectionEvent; - public int _bleConnectionEventHandle; + public int _bleConnectionEventHandle; public KEvent _bleServiceDiscoveryEvent; - public int _bleServiceDiscoveryEventHandle; + public int _bleServiceDiscoveryEventHandle; public KEvent _bleMtuConfigEvent; - public int _bleMtuConfigEventHandle; + public int _bleMtuConfigEventHandle; public IBtmUserCore() { } @@ -125,4 +125,4 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs index 48a273a09..13d14151a 100644 --- a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtm.cs @@ -5,4 +5,4 @@ { public IBtm(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs index 259698af5..43a6ccc6c 100644 --- a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmDebug.cs @@ -5,4 +5,4 @@ { public IBtmDebug(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs index c4210b782..67d851b49 100644 --- a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs @@ -5,4 +5,4 @@ { public IBtmSystem(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs index b00f0a2f3..225a71e33 100644 --- a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmUser.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.BluetoothManager return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs index 0ad2c4855..01e62d2af 100644 --- a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/ResultCode.cs @@ -2,9 +2,9 @@ { enum ResultCode { - ModuleId = 143, + ModuleId = 143, ErrorCodeShift = 9, - Success = 0 + Success = 0, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs index 6320fe284..349dd34f9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs @@ -11,19 +11,21 @@ namespace Ryujinx.HLE.HOS.Services.Caps { class CaptureManager { - private string _sdCardPath; + private readonly string _sdCardPath; private uint _shimLibraryVersion; public CaptureManager(Switch device) { - _sdCardPath = device.FileSystem.GetSdCardPath(); + _sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath(); } public ResultCode SetShimLibraryVersion(ServiceCtx context) { - ulong shimLibraryVersion = context.RequestData.ReadUInt64(); + ulong shimLibraryVersion = context.RequestData.ReadUInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong appletResourceUserId = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 // TODO: Service checks if the pid is present in an internal list and returns ResultCode.BlacklistedPid if it is. // The list contents needs to be determined. @@ -82,28 +84,28 @@ namespace Ryujinx.HLE.HOS.Services.Caps applicationAlbumEntry = new ApplicationAlbumEntry() { - Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(), - TitleId = titleId, + Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(), + TitleId = titleId, AlbumFileDateTime = new AlbumFileDateTime() { - Year = (ushort)currentDateTime.Year, - Month = (byte)currentDateTime.Month, - Day = (byte)currentDateTime.Day, - Hour = (byte)currentDateTime.Hour, - Minute = (byte)currentDateTime.Minute, - Second = (byte)currentDateTime.Second, - UniqueId = 0 + Year = (ushort)currentDateTime.Year, + Month = (byte)currentDateTime.Month, + Day = (byte)currentDateTime.Day, + Hour = (byte)currentDateTime.Hour, + Minute = (byte)currentDateTime.Minute, + Second = (byte)currentDateTime.Second, + UniqueId = 0, }, - AlbumStorage = AlbumStorage.Sd, - ContentType = ContentType.Screenshot, - Padding = new Array5<byte>(), - Unknown0x1f = 1 + AlbumStorage = AlbumStorage.Sd, + ContentType = ContentType.Screenshot, + Padding = new Array5<byte>(), + Unknown0x1f = 1, }; // NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead. - string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId))).Remove(0x20); + string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId))).Remove(0x20); string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00")); - string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash); + string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash); // TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions. Directory.CreateDirectory(folderPath); @@ -131,4 +133,4 @@ namespace Ryujinx.HLE.HOS.Services.Caps return Path.Combine(folderPath, fileName); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs index 4071b9cc0..de62b08d7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumAccessorService.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Caps { public IAlbumAccessorService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs index af99232eb..5fbba310f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumApplicationService.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps [CommandCmif(102)] // GetAlbumFileList0AafeAruidDeprecated(pid, u16 content_type, u64 start_time, u64 end_time, nn::applet::AppletResourceUserId) -> (u64 count, buffer<ApplicationAlbumFileEntry, 0x6>) - public ResultCode GetAlbumFileList0AafeAruidDeprecated(ServiceCtx context) + public ResultCode GetAlbumFileList0AafeAruidDeprecated(ServiceCtx context) { // NOTE: ApplicationAlbumFileEntry size is 0x30. return GetAlbumFileList(context); @@ -35,18 +35,18 @@ namespace Ryujinx.HLE.HOS.Services.Caps private ResultCode GetAlbumFileList(ServiceCtx context) { ResultCode resultCode = ResultCode.Success; - ulong count = 0; + ulong count = 0; ContentType contentType = (ContentType)context.RequestData.ReadUInt16(); - ulong startTime = context.RequestData.ReadUInt64(); - ulong endTime = context.RequestData.ReadUInt64(); + ulong startTime = context.RequestData.ReadUInt64(); + ulong endTime = context.RequestData.ReadUInt64(); context.RequestData.ReadUInt16(); // Alignment. ulong appletResourceUserId = context.RequestData.ReadUInt64(); ulong applicationAlbumFileEntryPosition = context.Request.ReceiveBuff[0].Position; - ulong applicationAlbumFileEntrySize = context.Request.ReceiveBuff[0].Size; + ulong applicationAlbumFileEntrySize = context.Request.ReceiveBuff[0].Size; MemoryHelper.FillWithZeros(context.Memory, applicationAlbumFileEntryPosition, (int)applicationAlbumFileEntrySize); @@ -66,4 +66,4 @@ namespace Ryujinx.HLE.HOS.Services.Caps return resultCode; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs index b16a41224..4376c4d14 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/IAlbumControlService.cs @@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Services.Caps return context.Device.System.CaptureManager.SetShimLibraryVersion(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs index a3501286c..fb62b710d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs @@ -20,14 +20,18 @@ namespace Ryujinx.HLE.HOS.Services.Caps public ResultCode SaveScreenShotEx0(ServiceCtx context) { // TODO: Use the ScreenShotAttribute. +#pragma warning disable IDE0059 // Remove unnecessary value assignment ScreenShotAttribute screenShotAttribute = context.RequestData.ReadStruct<ScreenShotAttribute>(); - uint unknown = context.RequestData.ReadUInt32(); + uint unknown = context.RequestData.ReadUInt32(); +#pragma warning restore IDE0059 ulong appletResourceUserId = context.RequestData.ReadUInt64(); - ulong pidPlaceholder = context.RequestData.ReadUInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong pidPlaceholder = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 ulong screenshotDataPosition = context.Request.SendBuff[0].Position; - ulong screenshotDataSize = context.Request.SendBuff[0].Size; + ulong screenshotDataSize = context.Request.SendBuff[0].Size; byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); @@ -43,20 +47,22 @@ namespace Ryujinx.HLE.HOS.Services.Caps public ResultCode SaveScreenShotEx1(ServiceCtx context) { // TODO: Use the ScreenShotAttribute. - ScreenShotAttribute screenShotAttribute = context.RequestData.ReadStruct<ScreenShotAttribute>(); + _ = context.RequestData.ReadStruct<ScreenShotAttribute>(); - uint unknown = context.RequestData.ReadUInt32(); + _ = context.RequestData.ReadUInt32(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); - ulong pidPlaceholder = context.RequestData.ReadUInt64(); + + _ = context.RequestData.ReadUInt64(); ulong applicationDataPosition = context.Request.SendBuff[0].Position; - ulong applicationDataSize = context.Request.SendBuff[0].Size; + ulong applicationDataSize = context.Request.SendBuff[0].Size; ulong screenshotDataPosition = context.Request.SendBuff[1].Position; - ulong screenshotDataSize = context.Request.SendBuff[1].Size; + ulong screenshotDataSize = context.Request.SendBuff[1].Size; + // TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now). - byte[] applicationData = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray(); + _ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray(); byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); @@ -72,19 +78,20 @@ namespace Ryujinx.HLE.HOS.Services.Caps public ResultCode SaveScreenShotEx2(ServiceCtx context) { // TODO: Use the ScreenShotAttribute. - ScreenShotAttribute screenShotAttribute = context.RequestData.ReadStruct<ScreenShotAttribute>(); + _ = context.RequestData.ReadStruct<ScreenShotAttribute>(); - uint unknown = context.RequestData.ReadUInt32(); + _ = context.RequestData.ReadUInt32(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); ulong userIdListPosition = context.Request.SendBuff[0].Position; - ulong userIdListSize = context.Request.SendBuff[0].Size; + ulong userIdListSize = context.Request.SendBuff[0].Size; ulong screenshotDataPosition = context.Request.SendBuff[1].Position; - ulong screenshotDataSize = context.Request.SendBuff[1].Size; + ulong screenshotDataSize = context.Request.SendBuff[1].Size; + // TODO: Parse the UserIdList. - byte[] userIdList = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray(); + _ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray(); byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); @@ -95,4 +102,4 @@ namespace Ryujinx.HLE.HOS.Services.Caps return resultCode; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs index 337fa9eec..c3e2036bb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotControlService.cs @@ -5,4 +5,4 @@ { public IScreenShotControlService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs index 03703e05d..ab921e085 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenshotService.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Caps { public IScreenshotService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs index 2615eeda6..51a6fd8ac 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/ResultCode.cs @@ -2,17 +2,17 @@ { enum ResultCode { - ModuleId = 206, + ModuleId = 206, ErrorCodeShift = 9, Success = 0, - InvalidArgument = (2 << ErrorCodeShift) | ModuleId, - ShimLibraryVersionAlreadySet = (7 << ErrorCodeShift) | ModuleId, - OutOfRange = (8 << ErrorCodeShift) | ModuleId, - InvalidContentType = (14 << ErrorCodeShift) | ModuleId, - NullOutputBuffer = (141 << ErrorCodeShift) | ModuleId, - NullInputBuffer = (142 << ErrorCodeShift) | ModuleId, - BlacklistedPid = (822 << ErrorCodeShift) | ModuleId + InvalidArgument = (2 << ErrorCodeShift) | ModuleId, + ShimLibraryVersionAlreadySet = (7 << ErrorCodeShift) | ModuleId, + OutOfRange = (8 << ErrorCodeShift) | ModuleId, + InvalidContentType = (14 << ErrorCodeShift) | ModuleId, + NullOutputBuffer = (141 << ErrorCodeShift) | ModuleId, + NullInputBuffer = (142 << ErrorCodeShift) | ModuleId, + BlacklistedPid = (822 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs index b9bc799c0..09c9b1760 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumFileDateTime.cs @@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Services.Caps.Types struct AlbumFileDateTime { public ushort Year; - public byte Month; - public byte Day; - public byte Hour; - public byte Minute; - public byte Second; - public byte UniqueId; + public byte Month; + public byte Day; + public byte Hour; + public byte Minute; + public byte Second; + public byte UniqueId; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs index 479675d67..2a1fbcac6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumImageOrientation.cs @@ -5,6 +5,6 @@ Degrees0, Degrees90, Degrees180, - Degrees270 + Degrees270, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs index cfe6c1e04..2fe99d455 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/Types/AlbumStorage.cs @@ -3,6 +3,6 @@ enum AlbumStorage : byte { Nand, - Sd + Sd, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs index 699bb4180..701fa63d9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ApplicationAlbumEntry.cs @@ -6,12 +6,12 @@ namespace Ryujinx.HLE.HOS.Services.Caps.Types [StructLayout(LayoutKind.Sequential, Size = 0x20)] struct ApplicationAlbumEntry { - public ulong Size; - public ulong TitleId; + public ulong Size; + public ulong TitleId; public AlbumFileDateTime AlbumFileDateTime; - public AlbumStorage AlbumStorage; - public ContentType ContentType; - public Array5<byte> Padding; - public byte Unknown0x1f; // Always 1 + public AlbumStorage AlbumStorage; + public ContentType ContentType; + public Array5<byte> Padding; + public byte Unknown0x1f; // Always 1 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs index 5f8bb5376..56023d2ea 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ContentType.cs @@ -5,6 +5,6 @@ Screenshot, Movie, ExtraMovie, - Unknown + Unknown, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs index 5528379ad..e7d8d0afd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/Types/ScreenShotAttribute.cs @@ -6,10 +6,10 @@ namespace Ryujinx.HLE.HOS.Services.Caps.Types [StructLayout(LayoutKind.Sequential, Size = 0x40)] struct ScreenShotAttribute { - public uint Unknown0x00; // Always 0 + public uint Unknown0x00; // Always 0 public AlbumImageOrientation AlbumImageOrientation; - public uint Unknown0x08; // Always 0 - public uint Unknown0x0C; // Always 1 - public Array30<byte> Unknown0x10; // Always 0 + public uint Unknown0x08; // Always 0 + public uint Unknown0x0C; // Always 1 + public Array30<byte> Unknown0x10; // Always 0 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs b/src/Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs index 71c267868..044f5b1eb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Cec/ICecManager.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Cec { public ICecManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs b/src/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs index 3b3279ab6..5b4f30da9 100644 --- a/src/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/CommandCmifAttribute.cs @@ -9,4 +9,4 @@ namespace Ryujinx.HLE.HOS.Services public CommandCmifAttribute(int id) => Id = id; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/DummyService.cs b/src/Ryujinx.HLE/HOS/Services/DummyService.cs index d69441a3a..838a9018e 100644 --- a/src/Ryujinx.HLE/HOS/Services/DummyService.cs +++ b/src/Ryujinx.HLE/HOS/Services/DummyService.cs @@ -9,4 +9,4 @@ ServiceName = serviceName; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs b/src/Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs index 52fe87024..09f02fc4b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ectx/IReaderForSystem.cs @@ -5,4 +5,4 @@ { public IReaderForSystem(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs index 9401a6d71..a61fca553 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForApplication.cs @@ -5,4 +5,4 @@ { public IWriterForApplication(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs b/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs index 621ec777d..30c22d1fc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ectx/IWriterForSystem.cs @@ -5,4 +5,4 @@ { public IWriterForSystem(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs b/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs index 9a689172b..eadd3ea8b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs @@ -5,4 +5,4 @@ { public IContext(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs b/src/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs index 6397afae8..b43cb30a2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Erpt/ISession.cs @@ -5,4 +5,4 @@ { public ISession(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs b/src/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs index 34be7bdd8..ced213f02 100644 --- a/src/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs @@ -5,4 +5,4 @@ { public IETicketService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Eupld/IControl.cs b/src/Ryujinx.HLE/HOS/Services/Eupld/IControl.cs index dd8705e66..4b069c60a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Eupld/IControl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Eupld/IControl.cs @@ -5,4 +5,4 @@ { public IControl(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs index 850978789..e14e4df4d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs +++ b/src/Ryujinx.HLE/HOS/Services/Eupld/IRequest.cs @@ -5,4 +5,4 @@ { public IRequest(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs index eb2c95534..4cb3a2c18 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fatal/IPrivateService.cs @@ -5,4 +5,4 @@ { public IPrivateService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index aaa5c873a..a62d633e2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal public ResultCode ThrowFatal(ServiceCtx context) { ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); - ulong pid = context.Request.HandleDesc.PId; + ulong pid = context.Request.HandleDesc.PId; return ThrowFatalWithCpuContextImpl(context, resultCode, pid, FatalPolicy.ErrorReportAndErrorScreen, null); } @@ -25,9 +25,9 @@ namespace Ryujinx.HLE.HOS.Services.Fatal // ThrowFatalWithPolicy(u64 result_code, u32 fatal_policy, u64 pid) public ResultCode ThrowFatalWithPolicy(ServiceCtx context) { - ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); FatalPolicy fatalPolicy = (FatalPolicy)context.RequestData.ReadUInt32(); - ulong pid = context.Request.HandleDesc.PId; + ulong pid = context.Request.HandleDesc.PId; return ThrowFatalWithCpuContextImpl(context, resultCode, pid, fatalPolicy, null); } @@ -36,12 +36,12 @@ namespace Ryujinx.HLE.HOS.Services.Fatal // ThrowFatalWithCpuContext(u64 result_code, u32 fatal_policy, u64 pid, buffer<bytes, 0x15> cpu_context) public ResultCode ThrowFatalWithCpuContext(ServiceCtx context) { - ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); FatalPolicy fatalPolicy = (FatalPolicy)context.RequestData.ReadUInt32(); - ulong pid = context.Request.HandleDesc.PId; + ulong pid = context.Request.HandleDesc.PId; ulong cpuContextPosition = context.Request.SendBuff[0].Position; - ulong cpuContextSize = context.Request.SendBuff[0].Size; + ulong cpuContextSize = context.Request.SendBuff[0].Size; ReadOnlySpan<byte> cpuContextData = context.Memory.GetSpan(cpuContextPosition, (int)cpuContextSize); @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal private ResultCode ThrowFatalWithCpuContextImpl(ServiceCtx context, ResultCode resultCode, ulong pid, FatalPolicy fatalPolicy, ReadOnlySpan<byte> cpuContext) { - StringBuilder errorReport = new StringBuilder(); + StringBuilder errorReport = new(); errorReport.AppendLine(); errorReport.AppendLine("ErrorReport log:"); @@ -144,4 +144,4 @@ namespace Ryujinx.HLE.HOS.Services.Fatal return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs index 5c0b116bc..5eb1406a1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs @@ -22,4 +22,4 @@ namespace Ryujinx.HLE.HOS.Services.Fatal.Types public uint StartAddress; public uint RegisterSetFlags; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs index 24829a78a..58fd3c4ac 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs @@ -19,6 +19,6 @@ namespace Ryujinx.HLE.HOS.Services.Fatal.Types public Array32<ulong> StackTrace; public ulong StartAddress; public ulong RegisterSetFlags; - public uint StackTraceSize; + public uint StackTraceSize; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs index fe55cf12b..f536d17d3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs @@ -4,6 +4,6 @@ { ErrorReportAndErrorScreen, ErrorReport, - ErrorScreen + ErrorScreen, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs index d5258a824..3f15f3fc6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend [Service("friend:v", FriendServicePermissionLevel.Viewer)] class IServiceCreator : IpcService { - private FriendServicePermissionLevel _permissionLevel; + private readonly FriendServicePermissionLevel _permissionLevel; public IServiceCreator(ServiceCtx context, FriendServicePermissionLevel permissionLevel) { @@ -52,4 +52,4 @@ namespace Ryujinx.HLE.HOS.Services.Friend return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs index 3e66e8733..82462b4ed 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs @@ -2,13 +2,13 @@ { enum ResultCode { - ModuleId = 121, + ModuleId = 121, ErrorCodeShift = 9, Success = 0, - InvalidArgument = (2 << ErrorCodeShift) | ModuleId, - InternetRequestDenied = (6 << ErrorCodeShift) | ModuleId, - NotificationQueueEmpty = (15 << ErrorCodeShift) | ModuleId + InvalidArgument = (2 << ErrorCodeShift) | ModuleId, + InternetRequestDenied = (6 << ErrorCodeShift) | ModuleId, + NotificationQueueEmpty = (15 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs index 87f54bf3e..e727cafbb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs @@ -6,8 +6,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService [StructLayout(LayoutKind.Sequential, Pack = 0x8, Size = 0x200, CharSet = CharSet.Ansi)] struct Friend { - public UserId UserId; - public long NetworkUserId; + public UserId UserId; + public long NetworkUserId; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x21)] public string Nickname; @@ -21,9 +21,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService public bool IsNew; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x6)] - char[] Unknown; + readonly char[] Unknown; [MarshalAs(UnmanagedType.I1)] public bool IsValid; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs index 261bf7bf0..6f10a7579 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService public long PresenceGroupId; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs index df2e65257..0271e61a4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs @@ -4,6 +4,6 @@ { Offline, Online, - OnlinePlay + OnlinePlay, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs index 24da7fd33..5949b8f64 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs @@ -5,6 +5,6 @@ None, Online, OnlinePlay, - OnlineOrOnlinePlay + OnlineOrOnlinePlay, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs index d36b3f310..9769e1609 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs @@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService [StructLayout(LayoutKind.Sequential, Pack = 0x8)] struct UserPresence { - public UserId UserId; - public long LastTimeOnlineTimestamp; + public UserId UserId; + public long LastTimeOnlineTimestamp; public PresenceStatus Status; [MarshalAs(UnmanagedType.I1)] @@ -26,9 +26,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService public const int Size = 0xC0; } - public override string ToString() + public readonly override string ToString() { return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status} }}"; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs index 42b34312c..3b1601abb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs @@ -2,11 +2,13 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator { class IDaemonSuspendSessionService : IpcService { - private FriendServicePermissionLevel PermissionLevel; +#pragma warning disable IDE0052 // Remove unread private member + private readonly FriendServicePermissionLevel _permissionLevel; +#pragma warning restore IDE0052 public IDaemonSuspendSessionService(FriendServicePermissionLevel permissionLevel) { - PermissionLevel = permissionLevel; + _permissionLevel = permissionLevel; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 2858aa468..f084fad11 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -15,8 +15,10 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator { class IFriendService : IpcService { - private FriendServicePermissionLevel _permissionLevel; - private KEvent _completionEvent; +#pragma warning disable IDE0052 // Remove unread private member + private readonly FriendServicePermissionLevel _permissionLevel; +#pragma warning restore IDE0052 + private KEvent _completionEvent; public IFriendService(FriendServicePermissionLevel permissionLevel) { @@ -27,10 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // GetCompletionEvent() -> handle<copy> public ResultCode GetCompletionEvent(ServiceCtx context) { - if (_completionEvent == null) - { - _completionEvent = new KEvent(context.Device.System.KernelContext); - } + _completionEvent ??= new KEvent(context.Device.System.KernelContext); if (context.Process.HandleTable.GenerateHandle(_completionEvent.ReadableEvent, out int completionEventHandle) != Result.Success) { @@ -62,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // Padding context.RequestData.ReadInt32(); - UserId userId = context.RequestData.ReadStruct<UserId>(); + UserId userId = context.RequestData.ReadStruct<UserId>(); FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>(); // Pid placeholder @@ -101,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // Padding context.RequestData.ReadInt32(); - UserId userId = context.RequestData.ReadStruct<UserId>(); + UserId userId = context.RequestData.ReadStruct<UserId>(); FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>(); // Pid placeholder @@ -115,7 +114,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty. context.ResponseData.Write(0); - Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { + Logger.Stub?.PrintStub(LogClass.ServiceFriend, new + { UserId = userId.ToString(), offset, filter.PresenceStatus, @@ -233,7 +233,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator context.RequestData.ReadInt64(); ulong position = context.Request.PtrBuff[0].Position; - ulong size = context.Request.PtrBuff[0].Size; + ulong size = context.Request.PtrBuff[0].Size; ReadOnlySpan<UserPresence> userPresenceInputArray = MemoryMarshal.Cast<byte, UserPresence>(context.Memory.GetSpan(position, (int)size)); @@ -251,8 +251,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // nn::friends::GetPlayHistoryRegistrationKey(b8 unknown, nn::account::Uid) -> buffer<nn::friends::PlayHistoryRegistrationKey, 0x1a> public ResultCode GetPlayHistoryRegistrationKey(ServiceCtx context) { - bool unknownBool = context.RequestData.ReadBoolean(); - UserId userId = context.RequestData.ReadStruct<UserId>(); + bool unknownBool = context.RequestData.ReadBoolean(); + UserId userId = context.RequestData.ReadStruct<UserId>(); context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40UL); @@ -274,18 +274,18 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // Then it checks if an Uuid is already stored for the UserId, if not it generates a random Uuid. // And store it in the savedata 8000000000000080 in the friends:/uid.bin file. - Array16<byte> randomGuid = new Array16<byte>(); + Array16<byte> randomGuid = new(); Guid.NewGuid().ToByteArray().AsSpan().CopyTo(randomGuid.AsSpan()); - PlayHistoryRegistrationKey playHistoryRegistrationKey = new PlayHistoryRegistrationKey + PlayHistoryRegistrationKey playHistoryRegistrationKey = new() { - Type = 0x101, - KeyIndex = (byte)(randomBytes[0] & 7), - UserIdBool = 0, // TODO: Find it. + Type = 0x101, + KeyIndex = (byte)(randomBytes[0] & 7), + UserIdBool = 0, // TODO: Find it. UnknownBool = (byte)(unknownBool ? 1 : 0), // TODO: Find it. - Reserved = new Array11<byte>(), - Uuid = randomGuid + Reserved = new Array11<byte>(), + Uuid = randomGuid, }; ReadOnlySpan<byte> playHistoryRegistrationKeyBuffer = SpanHelpers.AsByteSpan(ref playHistoryRegistrationKey); @@ -303,7 +303,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator */ - context.Memory.Write(bufferPosition, playHistoryRegistrationKeyBuffer); + context.Memory.Write(bufferPosition, playHistoryRegistrationKeyBuffer); context.Memory.Write(bufferPosition + 0x20, new byte[0x20]); // HmacHash return ResultCode.Success; @@ -317,16 +317,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // Pid placeholder context.RequestData.ReadInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; ulong playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position; - ulong PlayHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size; + ulong playHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size; ulong inAppScreenName1Position = context.Request.PtrBuff[1].Position; - ulong inAppScreenName1Size = context.Request.PtrBuff[1].Size; +#pragma warning restore IDE0059 + ulong inAppScreenName1Size = context.Request.PtrBuff[1].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong inAppScreenName2Position = context.Request.PtrBuff[2].Position; - ulong inAppScreenName2Size = context.Request.PtrBuff[2].Size; +#pragma warning restore IDE0059 + ulong inAppScreenName2Size = context.Request.PtrBuff[2].Size; if (userId.IsNull || inAppScreenName1Size > 0x48 || inAppScreenName2Size > 0x48) { @@ -334,7 +338,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator } // TODO: Call nn::arp::GetApplicationControlProperty here when implemented. +#pragma warning disable IDE0059 // Remove unnecessary value assignment ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties; +#pragma warning restore IDE0059 /* @@ -349,4 +355,4 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs index 505580221..8fc7a4609 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs @@ -11,24 +11,24 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator { class INotificationService : DisposableIpcService { - private readonly UserId _userId; + private readonly UserId _userId; private readonly FriendServicePermissionLevel _permissionLevel; private readonly object _lock = new(); - private KEvent _notificationEvent; - private int _notificationEventHandle = 0; + private readonly KEvent _notificationEvent; + private int _notificationEventHandle = 0; - private LinkedList<NotificationInfo> _notifications; + private readonly LinkedList<NotificationInfo> _notifications; private bool _hasNewFriendRequest; private bool _hasFriendListUpdate; public INotificationService(ServiceCtx context, UserId userId, FriendServicePermissionLevel permissionLevel) { - _userId = userId; - _permissionLevel = permissionLevel; - _notifications = new LinkedList<NotificationInfo>(); + _userId = userId; + _permissionLevel = permissionLevel; + _notifications = new LinkedList<NotificationInfo>(); _notificationEvent = new KEvent(context.Device.System.KernelContext); _hasNewFriendRequest = false; @@ -106,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator { if (!_hasFriendListUpdate) { - NotificationInfo friendListNotification = new NotificationInfo(); + NotificationInfo friendListNotification = new(); if (_notifications.Count != 0) { @@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator if (_hasNewFriendRequest) { - NotificationInfo newFriendRequestNotification = new NotificationInfo(); + NotificationInfo newFriendRequestNotification = new(); if (_notifications.Count != 0) { @@ -153,9 +153,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator SignalFriendListUpdate(targetId); } - NotificationInfo newFriendRequestNotification = new NotificationInfo + NotificationInfo newFriendRequestNotification = new() { - Type = NotificationEventType.NewFriendRequest + Type = NotificationEventType.NewFriendRequest, }; _notifications.AddLast(newFriendRequestNotification); @@ -175,4 +175,4 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs index 383ad006e..f0970d166 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs @@ -4,23 +4,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService { public sealed class NotificationEventHandler { - private static NotificationEventHandler instance; - private static object instanceLock = new object(); + private static NotificationEventHandler _instance; + private static readonly object _instanceLock = new(); - private INotificationService[] _registry; + private readonly INotificationService[] _registry; public static NotificationEventHandler Instance { get { - lock (instanceLock) + lock (_instanceLock) { - if (instance == null) - { - instance = new NotificationEventHandler(); - } + _instance ??= new NotificationEventHandler(); - return instance; + return _instance; } } } @@ -61,10 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService { for (int i = 0; i < _registry.Length; i++) { - if (_registry[i] != null) - { - _registry[i].SignalFriendListUpdate(targetId); - } + _registry[i]?.SignalFriendListUpdate(targetId); } } @@ -73,11 +67,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService { for (int i = 0; i < _registry.Length; i++) { - if (_registry[i] != null) - { - _registry[i].SignalNewFriendRequest(targetId); - } + _registry[i]?.SignalNewFriendRequest(targetId); } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs index 5136ae8a6..d7f3f55d2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs @@ -2,8 +2,8 @@ { enum NotificationEventType : uint { - Invalid = 0x0, + Invalid = 0x0, FriendListUpdate = 0x1, - NewFriendRequest = 0x65 + NewFriendRequest = 0x65, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs index e710bf064..1073c47d3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs @@ -10,4 +10,4 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService private Array4<byte> _padding; public long NetworkUserIdPlaceholder; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs index 12a3d42fe..bf7d1bd32 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs @@ -5,15 +5,15 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator [Flags] enum FriendServicePermissionLevel { - UserMask = 1, - ViewerMask = 2, + UserMask = 1, + ViewerMask = 2, ManagerMask = 4, - SystemMask = 8, + SystemMask = 8, Administrator = -1, - User = UserMask, - Viewer = UserMask | ViewerMask, - Manager = UserMask | ViewerMask | ManagerMask, - System = UserMask | SystemMask + User = UserMask, + Viewer = UserMask | ViewerMask, + Manager = UserMask | ViewerMask | ManagerMask, + System = UserMask | SystemMask, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs index 32d962c11..aaeef0593 100644 --- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs +++ b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs @@ -6,10 +6,10 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator [StructLayout(LayoutKind.Sequential, Size = 0x20)] struct PlayHistoryRegistrationKey { - public ushort Type; - public byte KeyIndex; - public byte UserIdBool; - public byte UnknownBool; + public ushort Type; + public byte KeyIndex; + public byte UserIdBool; + public byte UnknownBool; public Array11<byte> Reserved; public Array16<byte> Uuid; } diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs index ba924db83..599025e3b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy try { - LocalStorage storage = new LocalStorage(pfsPath, FileAccess.Read, FileMode.Open); + LocalStorage storage = new(pfsPath, FileAccess.Read, FileMode.Open); using SharedRef<LibHac.Fs.Fsa.IFileSystem> nsp = new(new PartitionFileSystem(storage)); ImportTitleKeysFromNsp(nsp.Get, context.Device.System.KeySet); @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy try { - Nca nca = new Nca(context.Device.System.KeySet, ncaStorage); + Nca nca = new(context.Device.System.KeySet, ncaStorage); if (!nca.SectionExists(NcaSectionType.Data)) { @@ -83,14 +83,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy if (archivePath.Extension == ".nsp" && File.Exists(archivePath.FullName)) { - FileStream pfsFile = new FileStream( + FileStream pfsFile = new( archivePath.FullName.TrimEnd(Path.DirectorySeparatorChar), FileMode.Open, FileAccess.Read); try { - PartitionFileSystem nsp = new PartitionFileSystem(pfsFile.AsStorage()); + PartitionFileSystem nsp = new(pfsFile.AsStorage()); ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet); @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy if (result.IsSuccess()) { - Ticket ticket = new Ticket(ticketFile.Get.AsStream()); + Ticket ticket = new(ticketFile.Get.AsStream()); var titleKey = ticket.GetTitleKey(keySet); if (titleKey != null) diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs index b97594498..70d3a6bd8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs @@ -20,14 +20,12 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy ulong bufferAddress = context.Request.ReceiveBuff[0].Position; ulong bufferLen = context.Request.ReceiveBuff[0].Size; - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _baseDirectory.Get.Read(out long entriesRead, new OutBuffer(region.Memory.Span)); + using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); + Result result = _baseDirectory.Get.Read(out long entriesRead, new OutBuffer(region.Memory.Span)); - context.ResponseData.Write(entriesRead); + context.ResponseData.Write(entriesRead); - return (ResultCode)result.Value; - } + return (ResultCode)result.Value; } [CommandCmif(1)] diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs index 4bc58ae5f..dcc34a754 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs @@ -26,16 +26,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy context.RequestData.BaseStream.Position += 4; long offset = context.RequestData.ReadInt64(); - long size = context.RequestData.ReadInt64(); + long size = context.RequestData.ReadInt64(); - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _baseFile.Get.Read(out long bytesRead, offset, new OutBuffer(region.Memory.Span), size, readOption); + using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); + Result result = _baseFile.Get.Read(out long bytesRead, offset, new OutBuffer(region.Memory.Span), size, readOption); - context.ResponseData.Write(bytesRead); + context.ResponseData.Write(bytesRead); - return (ResultCode)result.Value; - } + return (ResultCode)result.Value; } [CommandCmif(1)] @@ -48,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy context.RequestData.BaseStream.Position += 4; long offset = context.RequestData.ReadInt64(); - long size = context.RequestData.ReadInt64(); + long size = context.RequestData.ReadInt64(); byte[] data = new byte[context.Request.SendBuff[0].Size]; @@ -92,4 +90,4 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs index 9effa79dc..4c5c56240 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFileSystem.cs @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy if (result.IsSuccess()) { - IFile fileInterface = new IFile(ref file.Ref); + IFile fileInterface = new(ref file.Ref); MakeObject(context, fileInterface); } @@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy if (result.IsSuccess()) { - IDirectory dirInterface = new IDirectory(ref dir.Ref); + IDirectory dirInterface = new(ref dir.Ref); MakeObject(context, dirInterface); } @@ -210,4 +210,4 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs index 54c7b800b..4299a6c74 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs @@ -1,7 +1,6 @@ using LibHac; using LibHac.Common; using LibHac.Sf; -using Ryujinx.HLE.HOS.Ipc; namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy { @@ -19,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy public ResultCode Read(ServiceCtx context) { ulong offset = context.RequestData.ReadUInt64(); - ulong size = context.RequestData.ReadUInt64(); + ulong size = context.RequestData.ReadUInt64(); if (context.Request.ReceiveBuff.Count > 0) { @@ -32,12 +31,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy size = bufferLen; } - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size); + using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); + Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size); - return (ResultCode)result.Value; - } + return (ResultCode)result.Value; } return ResultCode.Success; @@ -62,4 +59,4 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index e961e9b18..644e1a17a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -18,7 +18,6 @@ using static Ryujinx.HLE.Utilities.StringUtils; using GameCardHandle = System.UInt32; using IFileSystem = LibHac.FsSrv.Sf.IFileSystem; using IStorage = LibHac.FsSrv.Sf.IStorage; -using RightsId = LibHac.Fs.RightsId; namespace Ryujinx.HLE.HOS.Services.Fs { @@ -48,14 +47,16 @@ namespace Ryujinx.HLE.HOS.Services.Fs // -> object<nn::fssrv::sf::IFileSystem> contentFs public ResultCode OpenFileSystemWithId(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment FileSystemType fileSystemType = (FileSystemType)context.RequestData.ReadInt32(); ulong titleId = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 string switchPath = ReadUtf8String(context); - string fullPath = context.Device.FileSystem.SwitchPathToSystemPath(switchPath); + string fullPath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(switchPath); if (!File.Exists(fullPath)) { - if (fullPath.Contains(".")) + if (fullPath.Contains('.')) { ResultCode result = FileSystemProxyHelper.OpenFileSystemFromInternalFile(context, fullPath, out FileSystemProxy.IFileSystem fileSystem); @@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs return ResultCode.PathDoesNotExist; } - FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read); + FileStream fileStream = new(fullPath, FileMode.Open, FileAccess.Read); string extension = System.IO.Path.GetExtension(fullPath); if (extension == ".nca") @@ -109,7 +110,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenBisFileSystem(ref fileSystem.Ref, in path, bisPartitionId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -124,7 +128,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var storage = new SharedRef<IStorage>(); Result result = _baseFileSystemProxy.Get.OpenBisStorage(ref storage.Ref, bisPartitionId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IStorage(ref storage.Ref)); @@ -145,7 +152,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenSdCardFileSystem(ref fileSystem.Ref); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -221,7 +231,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs public ResultCode IsExFatSupported(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.IsExFatSupported(out bool isSupported); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(isSupported); @@ -247,7 +260,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var storage = new SharedRef<IStorage>(); Result result = _baseFileSystemProxy.Get.OpenGameCardStorage(ref storage.Ref, handle, partitionId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IStorage(ref storage.Ref)); @@ -263,7 +279,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenGameCardFileSystem(ref fileSystem.Ref, handle, partitionId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -298,7 +317,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ushort index = context.RequestData.ReadUInt16(); Result result = _baseFileSystemProxy.Get.GetCacheStorageSize(out long dataSize, out long journalSize, index); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(dataSize); context.ResponseData.Write(journalSize); @@ -338,7 +360,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataFileSystem(ref fileSystem.Ref, spaceId, in attribute); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -354,7 +379,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataFileSystemBySystemSaveDataId(ref fileSystem.Ref, spaceId, in attribute); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -370,7 +398,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenReadOnlySaveDataFileSystem(ref fileSystem.Ref, spaceId, in attribute); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -388,7 +419,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs context.Memory.Read(context.Request.ReceiveBuff[0].Position, extraDataBuffer); Result result = _baseFileSystemProxy.Get.ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(new OutBuffer(extraDataBuffer), spaceId, saveDataId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.Memory.Write(context.Request.ReceiveBuff[0].Position, extraDataBuffer); @@ -405,7 +439,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs context.Memory.Read(context.Request.ReceiveBuff[0].Position, extraDataBuffer); Result result = _baseFileSystemProxy.Get.ReadSaveDataFileSystemExtraData(new OutBuffer(extraDataBuffer), saveDataId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.Memory.Write(context.Request.ReceiveBuff[0].Position, extraDataBuffer); @@ -432,7 +469,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReader(ref infoReader.Ref); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); @@ -447,7 +487,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderBySaveDataSpaceId(ref infoReader.Ref, spaceId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); @@ -461,7 +504,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderOnlyCacheStorage(ref infoReader.Ref); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); @@ -477,7 +523,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataInternalStorageFileSystem(ref fileSystem.Ref, spaceId, saveDataId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -518,14 +567,15 @@ namespace Ryujinx.HLE.HOS.Services.Fs ulong bufferAddress = context.Request.ReceiveBuff[0].Position; ulong bufferLen = context.Request.ReceiveBuff[0].Size; - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) + using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); + Result result = _baseFileSystemProxy.Get.FindSaveDataWithFilter(out long count, new OutBuffer(region.Memory.Span), spaceId, in filter); + if (result.IsFailure()) { - Result result = _baseFileSystemProxy.Get.FindSaveDataWithFilter(out long count, new OutBuffer(region.Memory.Span), spaceId, in filter); - if (result.IsFailure()) return (ResultCode)result.Value; - - context.ResponseData.Write(count); + return (ResultCode)result.Value; } + context.ResponseData.Write(count); + return ResultCode.Success; } @@ -537,7 +587,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var infoReader = new SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataInfoReaderWithFilter(ref infoReader.Ref, spaceId, in filter); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new ISaveDataInfoReader(ref infoReader.Ref)); @@ -554,7 +607,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs context.Memory.Read(context.Request.ReceiveBuff[0].Position, outputBuffer); Result result = _baseFileSystemProxy.Get.ReadSaveDataFileSystemExtraDataBySaveDataAttribute(new OutBuffer(outputBuffer), spaceId, in attribute); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.Memory.Write(context.Request.ReceiveBuff[0].Position, outputBuffer); @@ -589,7 +645,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs context.Memory.Read(context.Request.ReceiveBuff[0].Position, outputBuffer); Result result = _baseFileSystemProxy.Get.ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(new OutBuffer(outputBuffer), spaceId, in attribute, new InBuffer(maskBuffer)); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.Memory.Write(context.Request.ReceiveBuff[0].Position, outputBuffer); @@ -605,7 +664,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var file = new SharedRef<LibHac.FsSrv.Sf.IFile>(); Result result = _baseFileSystemProxy.Get.OpenSaveDataMetaFile(ref file.Ref, spaceId, in attribute, metaType); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new IFile(ref file.Ref)); @@ -623,7 +685,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs context.Memory.Read(context.Request.ReceiveBuff[0].Position, outputBuffer); Result result = _baseFileSystemProxy.Get.ListAccessibleSaveDataOwnerId(out int readCount, new OutBuffer(outputBuffer), programId, startIndex, bufferCount); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(readCount); @@ -637,7 +702,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenImageDirectoryFileSystem(ref fileSystem.Ref, directoryId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -651,7 +719,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenBaseFileSystem(ref fileSystem.Ref, fileSystemId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -665,7 +736,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenContentStorageFileSystem(ref fileSystem.Ref, contentStorageId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -679,7 +753,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenCloudBackupWorkStorageFileSystem(ref fileSystem.Ref, storageId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -693,7 +770,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenCustomStorageFileSystem(ref fileSystem.Ref, customStorageId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -718,7 +798,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs public ResultCode OpenDataStorageByDataId(ServiceCtx context) { StorageId storageId = (StorageId)context.RequestData.ReadByte(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment byte[] padding = context.RequestData.ReadBytes(7); +#pragma warning restore IDE0059 ulong titleId = context.RequestData.ReadUInt64(); // We do a mitm here to find if the request is for an AOC. @@ -750,7 +832,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs if (installedStorage != StorageId.None) { string contentPath = context.Device.System.ContentManager.GetInstalledContentPath(titleId, storageId, contentType); - string installPath = context.Device.FileSystem.SwitchPathToSystemPath(contentPath); + string installPath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(installPath)) { @@ -761,7 +843,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs try { LibHac.Fs.IStorage ncaStorage = new LocalStorage(ncaPath, FileAccess.Read, FileMode.Open); - Nca nca = new Nca(context.Device.System.KeySet, ncaStorage); + Nca nca = new(context.Device.System.KeySet, ncaStorage); LibHac.Fs.IStorage romfsStorage = nca.OpenStorage(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel); using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(romfsStorage); using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref)); @@ -829,7 +911,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var deviceOperator = new SharedRef<LibHac.FsSrv.Sf.IDeviceOperator>(); Result result = _baseFileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new IDeviceOperator(ref deviceOperator.Ref)); @@ -843,7 +928,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs long journalSize = context.RequestData.ReadInt64(); Result result = _baseFileSystemProxy.Get.QuerySaveDataTotalSize(out long totalSize, dataSize, journalSize); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(totalSize); @@ -907,7 +995,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ProgramId programId = context.RequestData.ReadStruct<ProgramId>(); Result result = _baseFileSystemProxy.Get.GetRightsId(out RightsId rightsId, programId, storageId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.WriteStruct(rightsId); @@ -935,7 +1026,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ref readonly var path = ref FileSystemProxyHelper.GetFspPath(context); Result result = _baseFileSystemProxy.Get.GetRightsIdByPath(out RightsId rightsId, in path); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.WriteStruct(rightsId); @@ -948,7 +1042,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ref readonly var path = ref FileSystemProxyHelper.GetFspPath(context); Result result = _baseFileSystemProxy.Get.GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in path); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(keyGeneration); context.ResponseData.BaseStream.Seek(7, SeekOrigin.Current); @@ -973,7 +1070,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataSpaceId spaceId = context.RequestData.ReadStruct<SaveDataSpaceId>(); Result result = _baseFileSystemProxy.Get.GetFreeSpaceSizeForSaveData(out long freeSpaceSize, spaceId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(freeSpaceSize); @@ -1008,7 +1108,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ulong saveDataId = context.RequestData.ReadUInt64(); Result result = _baseFileSystemProxy.Get.QuerySaveDataInternalStorageTotalSize(out long size, spaceId, saveDataId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(size); @@ -1022,7 +1125,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ulong saveDataId = context.RequestData.ReadUInt64(); Result result = _baseFileSystemProxy.Get.GetSaveDataCommitId(out long commitId, spaceId, saveDataId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(commitId); @@ -1059,7 +1165,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs public ResultCode IsSdCardAccessible(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.IsSdCardAccessible(out bool isAccessible); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(isAccessible); @@ -1072,7 +1181,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs ulong processId = context.RequestData.ReadUInt64(); Result result = _baseFileSystemProxy.Get.IsAccessFailureDetected(out bool isDetected, processId); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(isDetected); @@ -1099,7 +1211,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs public ResultCode GetAndClearErrorInfo(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.GetAndClearErrorInfo(out FileSystemProxyErrorInfo errorInfo); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.WriteStruct(errorInfo); @@ -1195,7 +1310,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var fileSystem = new SharedRef<IFileSystem>(); Result result = _baseFileSystemProxy.Get.OpenRegisteredUpdatePartition(ref fileSystem.Ref); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new FileSystemProxy.IFileSystem(ref fileSystem.Ref)); @@ -1206,7 +1324,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs public ResultCode GetAndClearMemoryReportInfo(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.WriteStruct(reportInfo); @@ -1217,7 +1338,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs public ResultCode GetProgramIndexForAccessLog(ServiceCtx context) { Result result = _baseFileSystemProxy.Get.GetProgramIndexForAccessLog(out int programIndex, out int programCount); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(programIndex); context.ResponseData.Write(programCount); @@ -1231,7 +1355,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs FsStackUsageThreadType threadType = context.RequestData.ReadStruct<FsStackUsageThreadType>(); Result result = _baseFileSystemProxy.Get.GetFsStackUsage(out uint usage, threadType); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } context.ResponseData.Write(usage); @@ -1290,7 +1417,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs using var commitManager = new SharedRef<LibHac.FsSrv.Sf.IMultiCommitManager>(); Result result = _baseFileSystemProxy.Get.OpenMultiCommitManager(ref commitManager.Ref); - if (result.IsFailure()) return (ResultCode)result.Value; + if (result.IsFailure()) + { + return (ResultCode)result.Value; + } MakeObject(context, new IMultiCommitManager(ref commitManager.Ref)); @@ -1305,4 +1435,4 @@ namespace Ryujinx.HLE.HOS.Services.Fs } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs index a40821b93..c3e228758 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxyForLoader.cs @@ -5,4 +5,4 @@ { public IFileSystemProxyForLoader(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs index e11eadf53..4554a053a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/IProgramRegistry.cs @@ -5,4 +5,4 @@ { public IProgramRegistry(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs b/src/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs index 0611375b1..022d7b819 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs @@ -20,14 +20,12 @@ namespace Ryujinx.HLE.HOS.Services.Fs ulong bufferAddress = context.Request.ReceiveBuff[0].Position; ulong bufferLen = context.Request.ReceiveBuff[0].Size; - using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true)) - { - Result result = _baseReader.Get.Read(out long readCount, new OutBuffer(region.Memory.Span)); + using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); + Result result = _baseReader.Get.Read(out long readCount, new OutBuffer(region.Memory.Span)); - context.ResponseData.Write(readCount); + context.ResponseData.Write(readCount); - return (ResultCode)result.Value; - } + return (ResultCode)result.Value; } protected override void Dispose(bool isDisposing) diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs index 8f87142b1..e04bd7be2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/ResultCode.cs @@ -2,15 +2,15 @@ namespace Ryujinx.HLE.HOS.Services.Fs { enum ResultCode { - ModuleId = 2, + ModuleId = 2, ErrorCodeShift = 9, Success = 0, - PathDoesNotExist = (1 << ErrorCodeShift) | ModuleId, - PathAlreadyExists = (2 << ErrorCodeShift) | ModuleId, - PathAlreadyInUse = (7 << ErrorCodeShift) | ModuleId, + PathDoesNotExist = (1 << ErrorCodeShift) | ModuleId, + PathAlreadyExists = (2 << ErrorCodeShift) | ModuleId, + PathAlreadyInUse = (7 << ErrorCodeShift) | ModuleId, PartitionNotFound = (1001 << ErrorCodeShift) | ModuleId, - InvalidInput = (6001 << ErrorCodeShift) | ModuleId + InvalidInput = (6001 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs b/src/Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs index f12c1661d..369df9b68 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/Types/FileSystemType.cs @@ -2,11 +2,11 @@ { enum FileSystemType { - Logo = 2, - ContentControl = 3, - ContentManual = 4, - ContentMeta = 5, - ContentData = 6, - ApplicationPackage = 7 + Logo = 2, + ContentControl = 3, + ContentManual = 4, + ContentMeta = 5, + ContentData = 6, + ApplicationPackage = 7, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs b/src/Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs index 90646b406..131ba541c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Grc/IGrcService.cs @@ -5,4 +5,4 @@ { public IGrcService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs b/src/Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs index edb1d64e1..1d200ac3c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Grc/IRemoteVideoTransfer.cs @@ -5,4 +5,4 @@ { public IRemoteVideoTransfer(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs index e4755f787..dbcbe1870 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs @@ -26,10 +26,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid internal const int SharedMemEntryCount = 17; public DebugPadDevice DebugPad; - public TouchDevice Touchscreen; - public MouseDevice Mouse; + public TouchDevice Touchscreen; + public MouseDevice Mouse; public KeyboardDevice Keyboard; - public NpadDevices Npads; + public NpadDevices Npads; private static void CheckTypeSizeOrThrow<T>(int expectedSize) { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid internal Hid(in Switch device, SharedMemoryStorage storage) { - _device = device; + _device = device; _storage = storage; SharedMemory = SharedMemory.Create(); @@ -61,11 +61,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid private void InitDevices() { - DebugPad = new DebugPadDevice(_device, true); + DebugPad = new DebugPadDevice(_device, true); Touchscreen = new TouchDevice(_device, true); - Mouse = new MouseDevice(_device, false); - Keyboard = new KeyboardDevice(_device, false); - Npads = new NpadDevices(_device, true); + Mouse = new MouseDevice(_device, false); + Keyboard = new KeyboardDevice(_device, false); + Npads = new NpadDevices(_device, true); } public void RefreshInputConfig(List<InputConfig> inputConfig) @@ -86,6 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid const int StickButtonThreshold = short.MaxValue / 2; ControllerKeys result = 0; +#pragma warning disable IDE0055 // Disable formatting result |= (leftStick.Dx < -StickButtonThreshold) ? ControllerKeys.LStickLeft : result; result |= (leftStick.Dx > StickButtonThreshold) ? ControllerKeys.LStickRight : result; result |= (leftStick.Dy < -StickButtonThreshold) ? ControllerKeys.LStickDown : result; @@ -95,11 +96,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid result |= (rightStick.Dx > StickButtonThreshold) ? ControllerKeys.RStickRight : result; result |= (rightStick.Dy < -StickButtonThreshold) ? ControllerKeys.RStickDown : result; result |= (rightStick.Dy > StickButtonThreshold) ? ControllerKeys.RStickUp : result; +#pragma warning restore IDE0055 return result; } - internal static ulong GetTimestampTicks() + internal ulong GetTimestampTicks() { return (ulong)PerformanceCounter.ElapsedMilliseconds * 19200; } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs index 0e3cd18a0..a6c21fdaf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs @@ -11,4 +11,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid Active = active; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs index e3b95390b..6b1d7af5d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid ref DebugPadState previousEntry = ref lifo.GetCurrentEntryRef(); - DebugPadState newState = new DebugPadState(); + DebugPadState newState = new(); if (Active) { @@ -25,4 +25,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid lifo.Write(ref newState); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs index 8908b74dc..0e3630f26 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid ref KeyboardState previousEntry = ref lifo.GetCurrentEntryRef(); - KeyboardState newState = new KeyboardState + KeyboardState newState = new() { SamplingNumber = previousEntry.SamplingNumber + 1, }; @@ -32,4 +32,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid lifo.Write(ref newState); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs index 66d1b0c49..b2dd3feaf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs @@ -12,8 +12,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid ref RingLifo<MouseState> lifo = ref _device.Hid.SharedMemory.Mouse; ref MouseState previousEntry = ref lifo.GetCurrentEntryRef(); - - MouseState newState = new MouseState() + + MouseState newState = new() { SamplingNumber = previousEntry.SamplingNumber + 1, }; @@ -33,4 +33,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid lifo.Write(ref newState); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index edcc47d83..240933ada 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -19,22 +19,22 @@ namespace Ryujinx.HLE.HOS.Services.Hid public const int MaxControllers = 9; // Players 1-8 and Handheld private ControllerType[] _configuredTypes; - private KEvent[] _styleSetUpdateEvents; - private bool[] _supportedPlayers; - private static VibrationValue _neutralVibrationValue = new VibrationValue + private readonly KEvent[] _styleSetUpdateEvents; + private readonly bool[] _supportedPlayers; + private VibrationValue _neutralVibrationValue = new() { AmplitudeLow = 0f, FrequencyLow = 160f, AmplitudeHigh = 0f, - FrequencyHigh = 320f + FrequencyHigh = 320f, }; internal NpadJoyHoldType JoyHold { get; set; } internal bool SixAxisActive = false; // TODO: link to hidserver when implemented internal ControllerType SupportedStyleSets { get; set; } - public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>>(); - public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (VibrationValue, VibrationValue)>(); + public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new(); + public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new(); public NpadDevices(Switch device, bool active = true) : base(device, active) { @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid if (player > PlayerIndex.Handheld) { - throw new ArgumentOutOfRangeException("Player must be Player1-8 or Handheld"); + throw new InvalidOperationException("Player must be Player1-8 or Handheld"); } if (controllerType == ControllerType.Handheld) @@ -249,6 +249,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid switch (type) { +#pragma warning disable IDE0055 // Disable formatting case ControllerType.ProController: controller.StyleSet = NpadStyleTag.FullKey; controller.DeviceType = DeviceType.FullKey; @@ -296,6 +297,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid controller.DeviceType = DeviceType.Palma; controller.AppletFooterUiType = AppletFooterUiType.None; break; +#pragma warning restore IDE0055 } _styleSetUpdateEvents[(int)player].ReadableEvent.Signal(); @@ -329,7 +331,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { if (!Unsafe.AreSame(ref currentlyUsed, ref possiblyUnused)) { - NpadCommonState newState = new NpadCommonState(); + NpadCommonState newState = new(); WriteNewInputEntry(ref possiblyUnused, ref newState); } @@ -348,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { if (!Unsafe.AreSame(ref currentlyUsed, ref possiblyUnused)) { - SixAxisSensorState newState = new SixAxisSensorState(); + SixAxisSensorState newState = new(); WriteNewSixInputEntry(ref possiblyUnused, ref newState); } @@ -379,23 +381,22 @@ namespace Ryujinx.HLE.HOS.Services.Hid ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad); - NpadCommonState newState = new NpadCommonState + NpadCommonState newState = new() { - Buttons = (NpadButton)state.Buttons, + Buttons = (NpadButton)state.Buttons, AnalogStickL = new AnalogStickState { - X = state.LStick.Dx, - Y = state.LStick.Dy, + X = state.LStick.Dx, + Y = state.LStick.Dy, }, AnalogStickR = new AnalogStickState { - X = state.RStick.Dx, - Y = state.RStick.Dy, - } + X = state.RStick.Dx, + Y = state.RStick.Dy, + }, + Attributes = NpadAttribute.IsConnected, }; - newState.Attributes = NpadAttribute.IsConnected; - switch (currentNpad.StyleSet) { case NpadStyleTag.Handheld: @@ -434,7 +435,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState; - NpadCommonState newState = new NpadCommonState(); + NpadCommonState newState = new(); WriteNewInputEntry(ref currentNpad.FullKey, ref newState); WriteNewInputEntry(ref currentNpad.Handheld, ref newState); @@ -514,33 +515,33 @@ namespace Ryujinx.HLE.HOS.Services.Hid return false; } - HidVector accel = new HidVector() + HidVector accel = new() { X = state.Accelerometer.X, Y = state.Accelerometer.Y, - Z = state.Accelerometer.Z + Z = state.Accelerometer.Z, }; - HidVector gyro = new HidVector() + HidVector gyro = new() { X = state.Gyroscope.X, Y = state.Gyroscope.Y, - Z = state.Gyroscope.Z + Z = state.Gyroscope.Z, }; - HidVector rotation = new HidVector() + HidVector rotation = new() { X = state.Rotation.X, Y = state.Rotation.Y, - Z = state.Rotation.Z + Z = state.Rotation.Z, }; - SixAxisSensorState newState = new SixAxisSensorState + SixAxisSensorState newState = new() { - Acceleration = accel, + Acceleration = accel, AngularVelocity = gyro, - Angle = rotation, - Attributes = SixAxisSensorAttribute.IsConnected + Angle = rotation, + Attributes = SixAxisSensorAttribute.IsConnected, }; state.Orientation.AsSpan().CopyTo(newState.Direction.AsSpan()); @@ -562,9 +563,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid if (!needUpdateRight && !isRightPair) { - SixAxisSensorState emptyState = new SixAxisSensorState(); - - emptyState.Attributes = SixAxisSensorAttribute.IsConnected; + SixAxisSensorState emptyState = new() + { + Attributes = SixAxisSensorAttribute.IsConnected, + }; WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState); } @@ -576,9 +578,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid { ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState; - SixAxisSensorState newState = new SixAxisSensorState(); - - newState.Attributes = SixAxisSensorAttribute.IsConnected; + SixAxisSensorState newState = new() + { + Attributes = SixAxisSensorAttribute.IsConnected, + }; WriteNewSixInputEntry(ref currentNpad.FullKeySixAxisSensor, ref newState); WriteNewSixInputEntry(ref currentNpad.HandheldSixAxisSensor, ref newState); @@ -632,4 +635,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid return rumbleQueue; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs index bb58ee517..35ac1a16f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs @@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid ref TouchScreenState previousEntry = ref lifo.GetCurrentEntryRef(); - TouchScreenState newState = new TouchScreenState + TouchScreenState newState = new() { - SamplingNumber = previousEntry.SamplingNumber + 1 + SamplingNumber = previousEntry.SamplingNumber + 1, }; if (Active) @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid FingerId = (uint)i, DiameterX = pi.DiameterX, DiameterY = pi.DiameterY, - RotationAngle = pi.Angle + RotationAngle = pi.Angle, }; } } @@ -45,4 +45,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid lifo.Write(ref newState); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs index 477e1a840..cba5c7b1d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { public struct ControllerConfig { - public PlayerIndex Player; + public PlayerIndex Player; public ControllerType Type; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs index 633671df8..452901a08 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid { public struct GamepadInput { - public PlayerIndex PlayerId; - public ControllerKeys Buttons; + public PlayerIndex PlayerId; + public ControllerKeys Buttons; public JoystickPosition LStick; public JoystickPosition RStick; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs index 6df477d6c..47be8d413 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/JoystickPosition.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid public int Dx; public int Dy; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs index be6857fb3..26fe980f5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid public int Modifier; public ulong[] Keys; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs index 4dda82c72..5c5c5d8c1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/SixAxisInput.cs @@ -5,9 +5,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid public struct SixAxisInput { public PlayerIndex PlayerId; - public Vector3 Accelerometer; - public Vector3 Gyroscope; - public Vector3 Rotation; - public float[] Orientation; + public Vector3 Accelerometer; + public Vector3 Gyroscope; + public Vector3 Rotation; + public float[] Orientation; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs index 457d2b0d0..ab0c85262 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/TouchPoint.cs @@ -11,4 +11,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid public uint DiameterY; public uint Angle; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs index b98f60658..d76704b3e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs @@ -7,6 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer public static PlayerIndex GetIndexFromNpadIdType(NpadIdType npadIdType) => npadIdType switch { +#pragma warning disable IDE0055 // Disable formatting NpadIdType.Player1 => PlayerIndex.Player1, NpadIdType.Player2 => PlayerIndex.Player2, NpadIdType.Player3 => PlayerIndex.Player3, @@ -17,12 +18,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer NpadIdType.Player8 => PlayerIndex.Player8, NpadIdType.Handheld => PlayerIndex.Handheld, NpadIdType.Unknown => PlayerIndex.Unknown, - _ => throw new ArgumentOutOfRangeException(nameof(npadIdType)) + _ => throw new ArgumentOutOfRangeException(nameof(npadIdType)), +#pragma warning restore IDE0055 }; public static NpadIdType GetNpadIdTypeFromIndex(PlayerIndex index) => index switch { +#pragma warning disable IDE0055 // Disable formatting PlayerIndex.Player1 => NpadIdType.Player1, PlayerIndex.Player2 => NpadIdType.Player2, PlayerIndex.Player3 => NpadIdType.Player3, @@ -33,7 +36,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer PlayerIndex.Player8 => NpadIdType.Player8, PlayerIndex.Handheld => NpadIdType.Handheld, PlayerIndex.Unknown => NpadIdType.Unknown, - _ => throw new ArgumentOutOfRangeException(nameof(index)) + _ => throw new ArgumentOutOfRangeException(nameof(index)), +#pragma warning restore IDE0055 }; public static bool IsValidNpadIdType(NpadIdType npadIdType) @@ -43,4 +47,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer npadIdType == NpadIdType.Unknown; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs index 56f63e52e..93f19c915 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IActiveVibrationDeviceList.cs @@ -8,9 +8,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer // ActivateVibrationDevice(nn::hid::VibrationDeviceHandle) public ResultCode ActivateVibrationDevice(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment int vibrationDeviceHandle = context.RequestData.ReadInt32(); +#pragma warning restore IDE0059 return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs index f0aaf5e36..56eb345af 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs @@ -7,8 +7,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer { class IAppletResource : IpcService { - private KSharedMemory _hidSharedMem; - private int _hidSharedMemHandle; + private readonly KSharedMemory _hidSharedMem; + private int _hidSharedMemHandle; public IAppletResource(KSharedMemory hidSharedMem) { @@ -32,4 +32,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs index 0cf4a0472..02e3d803f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadHandheldActivationMode.cs @@ -4,6 +4,6 @@ { Dual, Single, - None + None, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs index 05587bfd2..f52e18f28 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Npad/NpadJoyDeviceType.cs @@ -3,6 +3,6 @@ public enum NpadJoyDeviceType { Left, - Right + Right, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs index 4fd0a1b5b..8e4d80e24 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/AccelerometerParameters.cs @@ -5,4 +5,4 @@ public float X; public float Y; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs index db7467fa7..659afa0df 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/GyroscopeZeroDriftMode.cs @@ -4,6 +4,6 @@ { Loose, Standard, - Tight + Tight, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs index 2683ffee4..37cef783a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/SixAxis/SensorFusionParameters.cs @@ -5,4 +5,4 @@ public float RevisePower; public float ReviseRange; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs index fe50e6718..d59afe513 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceHandle.cs @@ -7,4 +7,4 @@ public byte Position; public byte Reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs index 117451f16..19d52f9fd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDevicePosition.cs @@ -4,6 +4,6 @@ { None, Left, - Right + Right, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs index 4e5557c94..4849c3426 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceType.cs @@ -4,6 +4,6 @@ { None, LinearResonantActuator, - GcErm + GcErm, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs index 91a23eb7e..8ac06cb79 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationDeviceValue.cs @@ -2,7 +2,7 @@ { public struct VibrationDeviceValue { - public VibrationDeviceType DeviceType; + public VibrationDeviceType DeviceType; public VibrationDevicePosition Position; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs index 38ac9ccac..c6143a013 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidServer/Types/Vibration/VibrationValue.cs @@ -9,16 +9,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid public float AmplitudeHigh; public float FrequencyHigh; - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is VibrationValue value && AmplitudeLow == value.AmplitudeLow && AmplitudeHigh == value.AmplitudeHigh; } - public override int GetHashCode() + public readonly override int GetHashCode() { return HashCode.Combine(AmplitudeLow, AmplitudeHigh); } + + public static bool operator ==(VibrationValue left, VibrationValue right) + { + return left.Equals(right); + } + + public static bool operator !=(VibrationValue left, VibrationValue right) + { + return !(left == right); + } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs index adaaa0123..0d1867db5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs @@ -5,4 +5,4 @@ { public IHidDebugServer(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index d508aba42..1d1b145cc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -15,8 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid [Service("hid")] class IHidServer : IpcService { - private KEvent _xpadIdEvent; - private KEvent _palmaOperationCompleteEvent; + private readonly KEvent _xpadIdEvent; + private readonly KEvent _palmaOperationCompleteEvent; private int _xpadIdEventHandle; @@ -24,33 +24,33 @@ namespace Ryujinx.HLE.HOS.Services.Hid private bool _unintendedHomeButtonInputProtectionEnabled; private bool _vibrationPermitted; private bool _usbFullKeyControllerEnabled; - private bool _isFirmwareUpdateAvailableForSixAxisSensor; + private readonly bool _isFirmwareUpdateAvailableForSixAxisSensor; private bool _isSixAxisSensorUnalteredPassthroughEnabled; private NpadHandheldActivationMode _npadHandheldActivationMode; - private GyroscopeZeroDriftMode _gyroscopeZeroDriftMode; + private GyroscopeZeroDriftMode _gyroscopeZeroDriftMode; - private long _npadCommunicationMode; - private uint _accelerometerPlayMode; -#pragma warning disable CS0649 - private long _vibrationGcErmCommand; + private long _npadCommunicationMode; + private uint _accelerometerPlayMode; +#pragma warning disable CS0649 // Field is never assigned to + private readonly long _vibrationGcErmCommand; #pragma warning restore CS0649 private float _sevenSixAxisSensorFusionStrength; - private SensorFusionParameters _sensorFusionParams; + private SensorFusionParameters _sensorFusionParams; private AccelerometerParameters _accelerometerParams; public IHidServer(ServiceCtx context) : base(context.Device.System.HidServer) { - _xpadIdEvent = new KEvent(context.Device.System.KernelContext); + _xpadIdEvent = new KEvent(context.Device.System.KernelContext); _palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext); _npadHandheldActivationMode = NpadHandheldActivationMode.Dual; - _gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard; + _gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard; _isFirmwareUpdateAvailableForSixAxisSensor = false; - _sensorFusionParams = new SensorFusionParameters(); + _sensorFusionParams = new SensorFusionParameters(); _accelerometerParams = new AccelerometerParameters(); // TODO: signal event at right place @@ -63,7 +63,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // CreateAppletResource(nn::applet::AppletResourceUserId) -> object<nn::hid::IAppletResource> public ResultCode CreateAppletResource(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 MakeObject(context, new IAppletResource(context.Device.System.HidSharedMem)); @@ -138,8 +140,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid // Initialize entries to avoid issues with some games. - KeyboardInput emptyInput = new KeyboardInput(); - emptyInput.Keys = new ulong[4]; + KeyboardInput emptyInput = new() + { + Keys = new ulong[4], + }; for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) { @@ -199,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // ActivateXpad(nn::hid::BasicXpadId, nn::applet::AppletResourceUserId) public ResultCode ActivateXpad(ServiceCtx context) { - int basicXpadId = context.RequestData.ReadInt32(); + int basicXpadId = context.RequestData.ReadInt32(); long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, basicXpadId }); @@ -395,7 +399,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid _sensorFusionParams = new SensorFusionParameters { RevisePower = context.RequestData.ReadInt32(), - ReviseRange = context.RequestData.ReadInt32() + ReviseRange = context.RequestData.ReadInt32(), }; long appletResourceUserId = context.RequestData.ReadInt64(); @@ -447,7 +451,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid _accelerometerParams = new AccelerometerParameters { X = context.RequestData.ReadInt32(), - Y = context.RequestData.ReadInt32() + Y = context.RequestData.ReadInt32(), }; long appletResourceUserId = context.RequestData.ReadInt64(); @@ -671,7 +675,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ResultCode ActivateGesture(ServiceCtx context) { long appletResourceUserId = context.RequestData.ReadInt64(); - int unknown0 = context.RequestData.ReadInt32(); + int unknown0 = context.RequestData.ReadInt32(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, unknown0 }); @@ -698,8 +702,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid // GetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag public ResultCode GetSupportedNpadStyleSet(ServiceCtx context) { - ulong pid = context.Request.HandleDesc.PId; - long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 + long appletResourceUserId = context.RequestData.ReadInt64(); context.ResponseData.Write((int)context.Device.Hid.Npads.SupportedStyleSets); @@ -712,9 +718,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SetSupportedNpadIdType(nn::applet::AppletResourceUserId, array<NpadIdType, 9>) public ResultCode SetSupportedNpadIdType(ServiceCtx context) { - long appletResourceUserId = context.RequestData.ReadInt64(); - ulong arrayPosition = context.Request.PtrBuff[0].Position; - ulong arraySize = context.Request.PtrBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 + ulong arrayPosition = context.Request.PtrBuff[0].Position; + ulong arraySize = context.Request.PtrBuff[0].Size; ReadOnlySpan<NpadIdType> supportedPlayerIds = MemoryMarshal.Cast<byte, NpadIdType>(context.Memory.GetSpan(arrayPosition, (int)arraySize)); @@ -756,9 +764,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // AcquireNpadStyleSetUpdateEventHandle(nn::applet::AppletResourceUserId, uint, ulong) -> nn::sf::NativeHandle public ResultCode AcquireNpadStyleSetUpdateEventHandle(ServiceCtx context) { - PlayerIndex npadId = HidUtils.GetIndexFromNpadIdType((NpadIdType)context.RequestData.ReadInt32()); - long appletResourceUserId = context.RequestData.ReadInt64(); - long npadStyleSet = context.RequestData.ReadInt64(); + PlayerIndex npadId = HidUtils.GetIndexFromNpadIdType((NpadIdType)context.RequestData.ReadInt32()); + long appletResourceUserId = context.RequestData.ReadInt64(); + long npadStyleSet = context.RequestData.ReadInt64(); KEvent evnt = context.Device.Hid.Npads.GetStyleSetUpdateEvent(npadId); if (context.Process.HandleTable.GenerateHandle(evnt.ReadableEvent, out int handle) != Result.Success) @@ -780,8 +788,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // DisconnectNpad(nn::applet::AppletResourceUserId, uint NpadIdType) public ResultCode DisconnectNpad(ServiceCtx context) { - NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); + NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, npadIdType }); @@ -796,17 +804,17 @@ namespace Ryujinx.HLE.HOS.Services.Hid ulong ledPattern = npadId switch { - NpadIdType.Player1 => 0b0001, - NpadIdType.Player2 => 0b0011, - NpadIdType.Player3 => 0b0111, - NpadIdType.Player4 => 0b1111, - NpadIdType.Player5 => 0b1001, - NpadIdType.Player6 => 0b0101, - NpadIdType.Player7 => 0b1101, - NpadIdType.Player8 => 0b0110, - NpadIdType.Unknown => 0b0000, + NpadIdType.Player1 => 0b0001, + NpadIdType.Player2 => 0b0011, + NpadIdType.Player3 => 0b0111, + NpadIdType.Player4 => 0b1111, + NpadIdType.Player5 => 0b1001, + NpadIdType.Player6 => 0b0101, + NpadIdType.Player7 => 0b1101, + NpadIdType.Player8 => 0b0110, + NpadIdType.Unknown => 0b0000, NpadIdType.Handheld => 0b0000, - _ => throw new ArgumentOutOfRangeException(nameof(npadId)) + _ => throw new InvalidOperationException($"{nameof(npadId)} contains an invalid value: {npadId}"), }; context.ResponseData.Write(ledPattern); @@ -831,13 +839,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid // Initialize entries to avoid issues with some games. - List<GamepadInput> emptyGamepadInputs = new List<GamepadInput>(); - List<SixAxisInput> emptySixAxisInputs = new List<SixAxisInput>(); + List<GamepadInput> emptyGamepadInputs = new(); + List<SixAxisInput> emptySixAxisInputs = new(); for (int player = 0; player < NpadDevices.MaxControllers; player++) { - GamepadInput gamepadInput = new GamepadInput(); - SixAxisInput sixaxisInput = new SixAxisInput(); + GamepadInput gamepadInput = new(); + SixAxisInput sixaxisInput = new(); gamepadInput.PlayerId = (PlayerIndex)player; sixaxisInput.PlayerId = (PlayerIndex)player; @@ -863,13 +871,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SetNpadJoyHoldType(nn::applet::AppletResourceUserId, ulong NpadJoyHoldType) public ResultCode SetNpadJoyHoldType(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 NpadJoyHoldType npadJoyHoldType = (NpadJoyHoldType)context.RequestData.ReadUInt64(); if (npadJoyHoldType > NpadJoyHoldType.Horizontal) { - throw new ArgumentOutOfRangeException(nameof(npadJoyHoldType)); + throw new InvalidOperationException($"{nameof(npadJoyHoldType)} contains an invalid value: {npadJoyHoldType}"); } foreach (PlayerIndex playerIndex in context.Device.Hid.Npads.GetSupportedPlayers()) @@ -889,7 +899,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // GetNpadJoyHoldType(nn::applet::AppletResourceUserId) -> ulong NpadJoyHoldType public ResultCode GetNpadJoyHoldType(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 foreach (PlayerIndex playerIndex in context.Device.Hid.Npads.GetSupportedPlayers()) { @@ -910,7 +922,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid { NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32(); context.RequestData.BaseStream.Position += 4; // Padding +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 if (HidUtils.IsValidNpadIdType(npadIdType)) { @@ -943,7 +957,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid { NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32(); context.RequestData.BaseStream.Position += 4; // Padding +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 if (HidUtils.IsValidNpadIdType(npadIdType)) { @@ -957,9 +973,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // MergeSingleJoyAsDualJoy(uint npadIdType0, uint npadIdType1, nn::applet::AppletResourceUserId) public ResultCode MergeSingleJoyAsDualJoy(ServiceCtx context) { - NpadIdType npadIdType0 = (NpadIdType)context.RequestData.ReadUInt32(); - NpadIdType npadIdType1 = (NpadIdType)context.RequestData.ReadUInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); + NpadIdType npadIdType0 = (NpadIdType)context.RequestData.ReadUInt32(); + NpadIdType npadIdType1 = (NpadIdType)context.RequestData.ReadUInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); if (HidUtils.IsValidNpadIdType(npadIdType0) && HidUtils.IsValidNpadIdType(npadIdType1)) { @@ -995,7 +1011,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SetNpadHandheldActivationMode(nn::applet::AppletResourceUserId, long HidNpadHandheldActivationMode) public ResultCode SetNpadHandheldActivationMode(ServiceCtx context) { - long appletResourceUserId = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); _npadHandheldActivationMode = (NpadHandheldActivationMode)context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _npadHandheldActivationMode }); @@ -1020,8 +1036,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SwapNpadAssignment(uint OldNpadAssignment, uint NewNpadAssignment, nn::applet::AppletResourceUserId) public ResultCode SwapNpadAssignment(ServiceCtx context) { - int oldNpadAssignment = context.RequestData.ReadInt32(); - int newNpadAssignment = context.RequestData.ReadInt32(); + int oldNpadAssignment = context.RequestData.ReadInt32(); + int newNpadAssignment = context.RequestData.ReadInt32(); long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, oldNpadAssignment, newNpadAssignment }); @@ -1033,7 +1049,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // IsUnintendedHomeButtonInputProtectionEnabled(uint Unknown0, nn::applet::AppletResourceUserId) -> bool IsEnabled public ResultCode IsUnintendedHomeButtonInputProtectionEnabled(ServiceCtx context) { - uint unknown0 = context.RequestData.ReadUInt32(); + uint unknown0 = context.RequestData.ReadUInt32(); long appletResourceUserId = context.RequestData.ReadInt64(); context.ResponseData.Write(_unintendedHomeButtonInputProtectionEnabled); @@ -1048,8 +1064,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ResultCode EnableUnintendedHomeButtonInputProtection(ServiceCtx context) { _unintendedHomeButtonInputProtectionEnabled = context.RequestData.ReadBoolean(); - uint unknown0 = context.RequestData.ReadUInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); + uint unknown0 = context.RequestData.ReadUInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, unknown0, _unintendedHomeButtonInputProtectionEnabled }); @@ -1060,7 +1076,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SetNpadJoyAssignmentModeSingleWithDestination(uint npadIdType, uint npadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool npadIdTypeIsSet, uint npadIdTypeSet public ResultCode SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx context) { - NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); + NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); NpadJoyDeviceType npadJoyDeviceType = (NpadJoyDeviceType)context.RequestData.ReadInt32(); context.RequestData.BaseStream.Position += 4; // Padding long appletResourceUserId = context.RequestData.ReadInt64(); @@ -1081,7 +1097,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid private void SetNpadJoyAssignmentModeSingleWithDestinationImpl(ServiceCtx context, NpadIdType npadIdType, long appletResourceUserId, NpadJoyDeviceType npadJoyDeviceType, out NpadIdType npadIdTypeSet, out bool npadIdTypeIsSet) { - npadIdTypeSet = default; + npadIdTypeSet = default; npadIdTypeIsSet = false; context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Single; @@ -1096,8 +1112,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ResultCode GetVibrationDeviceInfo(ServiceCtx context) { VibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<VibrationDeviceHandle>(); - NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType; - NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId; + NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType; + NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId; if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey) { @@ -1136,14 +1152,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid } else { - throw new ArgumentOutOfRangeException(nameof(deviceHandle.Position)); + throw new InvalidOperationException($"{nameof(deviceHandle.Position)} contains an invalid value: {deviceHandle.Position}"); } } - VibrationDeviceValue deviceInfo = new VibrationDeviceValue + VibrationDeviceValue deviceInfo = new() { DeviceType = vibrationDeviceType, - Position = vibrationDevicePosition + Position = vibrationDevicePosition, }; context.ResponseData.WriteStruct(deviceInfo); @@ -1158,27 +1174,30 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId) public ResultCode SendVibrationValue(ServiceCtx context) { - VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle + VibrationDeviceHandle deviceHandle = new() { DeviceType = context.RequestData.ReadByte(), - PlayerId = context.RequestData.ReadByte(), - Position = context.RequestData.ReadByte(), - Reserved = context.RequestData.ReadByte() + PlayerId = context.RequestData.ReadByte(), + Position = context.RequestData.ReadByte(), + Reserved = context.RequestData.ReadByte(), }; - VibrationValue vibrationValue = new VibrationValue + VibrationValue vibrationValue = new() { - AmplitudeLow = context.RequestData.ReadSingle(), - FrequencyLow = context.RequestData.ReadSingle(), + AmplitudeLow = context.RequestData.ReadSingle(), + FrequencyLow = context.RequestData.ReadSingle(), AmplitudeHigh = context.RequestData.ReadSingle(), - FrequencyHigh = context.RequestData.ReadSingle() + FrequencyHigh = context.RequestData.ReadSingle(), }; +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 - Dictionary<byte, VibrationValue> dualVibrationValues = new Dictionary<byte, VibrationValue>(); - - dualVibrationValues[deviceHandle.Position] = vibrationValue; + Dictionary<byte, VibrationValue> dualVibrationValues = new() + { + [deviceHandle.Position] = vibrationValue, + }; context.Device.Hid.Npads.UpdateRumbleQueue((PlayerIndex)deviceHandle.PlayerId, dualVibrationValues); @@ -1189,15 +1208,17 @@ namespace Ryujinx.HLE.HOS.Services.Hid // GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue public ResultCode GetActualVibrationValue(ServiceCtx context) { - VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle + VibrationDeviceHandle deviceHandle = new() { DeviceType = context.RequestData.ReadByte(), - PlayerId = context.RequestData.ReadByte(), - Position = context.RequestData.ReadByte(), - Reserved = context.RequestData.ReadByte() + PlayerId = context.RequestData.ReadByte(), + Position = context.RequestData.ReadByte(), + Reserved = context.RequestData.ReadByte(), }; +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 VibrationValue vibrationValue = context.Device.Hid.Npads.GetLastVibrationValue((PlayerIndex)deviceHandle.PlayerId, deviceHandle.Position); @@ -1242,7 +1263,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SendVibrationValues(nn::applet::AppletResourceUserId, buffer<array<nn::hid::VibrationDeviceHandle>, type: 9>, buffer<array<nn::hid::VibrationValue>, type: 9>) public ResultCode SendVibrationValues(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 byte[] vibrationDeviceHandleBuffer = new byte[context.Request.PtrBuff[0].Size]; @@ -1252,12 +1275,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer); - Span<VibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, VibrationDeviceHandle>(vibrationDeviceHandleBuffer); - Span<VibrationValue> vibrationValues = MemoryMarshal.Cast<byte, VibrationValue>(vibrationValueBuffer); + Span<VibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, VibrationDeviceHandle>(vibrationDeviceHandleBuffer); + Span<VibrationValue> vibrationValues = MemoryMarshal.Cast<byte, VibrationValue>(vibrationValueBuffer); if (!deviceHandles.IsEmpty && vibrationValues.Length == deviceHandles.Length) { - Dictionary<byte, VibrationValue> dualVibrationValues = new Dictionary<byte, VibrationValue>(); + Dictionary<byte, VibrationValue> dualVibrationValues = new(); PlayerIndex currentIndex = (PlayerIndex)deviceHandles[0].PlayerId; for (int deviceCounter = 0; deviceCounter < deviceHandles.Length; deviceCounter++) @@ -1285,9 +1308,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SendVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::hid::VibrationGcErmCommand, nn::applet::AppletResourceUserId) public ResultCode SendVibrationGcErmCommand(ServiceCtx context) { - int vibrationDeviceHandle = context.RequestData.ReadInt32(); + int vibrationDeviceHandle = context.RequestData.ReadInt32(); long vibrationGcErmCommand = context.RequestData.ReadInt64(); - long appletResourceUserId = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, vibrationDeviceHandle, vibrationGcErmCommand }); @@ -1298,8 +1321,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // GetActualVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationGcErmCommand public ResultCode GetActualVibrationGcErmCommand(ServiceCtx context) { - int vibrationDeviceHandle = context.RequestData.ReadInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); + int vibrationDeviceHandle = context.RequestData.ReadInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); context.ResponseData.Write(_vibrationGcErmCommand); @@ -1332,8 +1355,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid // IsVibrationDeviceMounted(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) public ResultCode IsVibrationDeviceMounted(ServiceCtx context) { - int vibrationDeviceHandle = context.RequestData.ReadInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + int vibrationDeviceHandle = context.RequestData.ReadInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 // NOTE: Service use vibrationDeviceHandle to get the PlayerIndex. // And return false if (npadIdType >= (NpadIdType)8 && npadIdType != NpadIdType.Handheld && npadIdType != NpadIdType.Unknown) @@ -1358,8 +1383,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // StartConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode StartConsoleSixAxisSensor(ServiceCtx context) { - int consoleSixAxisSensorHandle = context.RequestData.ReadInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); + int consoleSixAxisSensorHandle = context.RequestData.ReadInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, consoleSixAxisSensorHandle }); @@ -1370,8 +1395,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // StopConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) public ResultCode StopConsoleSixAxisSensor(ServiceCtx context) { - int consoleSixAxisSensorHandle = context.RequestData.ReadInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); + int consoleSixAxisSensorHandle = context.RequestData.ReadInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, consoleSixAxisSensorHandle }); @@ -1416,8 +1441,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ResultCode InitializeSevenSixAxisSensor(ServiceCtx context) { long appletResourceUserId = context.RequestData.ReadInt64(); - long counter0 = context.RequestData.ReadInt64(); - long counter1 = context.RequestData.ReadInt64(); + long counter0 = context.RequestData.ReadInt64(); + long counter1 = context.RequestData.ReadInt64(); // TODO: Determine if array<nn::sf::NativeHandle> is a buffer or not... @@ -1441,8 +1466,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SetSevenSixAxisSensorFusionStrength(float Strength, nn::applet::AppletResourceUserId) public ResultCode SetSevenSixAxisSensorFusionStrength(ServiceCtx context) { - _sevenSixAxisSensorFusionStrength = context.RequestData.ReadSingle(); - long appletResourceUserId = context.RequestData.ReadInt64(); + _sevenSixAxisSensorFusionStrength = context.RequestData.ReadSingle(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _sevenSixAxisSensorFusionStrength }); @@ -1566,14 +1591,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid // GetPalmaConnectionHandle(uint Unknown0, nn::applet::AppletResourceUserId) -> nn::hid::PalmaConnectionHandle public ResultCode GetPalmaConnectionHandle(ServiceCtx context) { - int unknown0 = context.RequestData.ReadInt32(); + int unknown0 = context.RequestData.ReadInt32(); long appletResourceUserId = context.RequestData.ReadInt64(); int palmaConnectionHandle = 0; context.ResponseData.Write(palmaConnectionHandle); - Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId , unknown0, palmaConnectionHandle }); + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, unknown0, palmaConnectionHandle }); return ResultCode.Success; } @@ -1628,8 +1653,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // PlayPalmaActivity(nn::hid::PalmaConnectionHandle, ulong Unknown0) public ResultCode PlayPalmaActivity(ServiceCtx context) { - int palmaConnectionHandle = context.RequestData.ReadInt32(); - long unknown0 = context.RequestData.ReadInt64(); + int palmaConnectionHandle = context.RequestData.ReadInt32(); + long unknown0 = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { palmaConnectionHandle, unknown0 }); @@ -1642,8 +1667,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SetPalmaFrModeType(nn::hid::PalmaConnectionHandle, ulong FrModeType) public ResultCode SetPalmaFrModeType(ServiceCtx context) { - int palmaConnectionHandle = context.RequestData.ReadInt32(); - long frModeType = context.RequestData.ReadInt64(); + int palmaConnectionHandle = context.RequestData.ReadInt32(); + long frModeType = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { palmaConnectionHandle, frModeType }); @@ -1667,8 +1692,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // EnablePalmaStep(nn::hid::PalmaConnectionHandle, bool Enable) public ResultCode EnablePalmaStep(ServiceCtx context) { - int palmaConnectionHandle = context.RequestData.ReadInt32(); - bool enabledPalmaStep = context.RequestData.ReadBoolean(); + int palmaConnectionHandle = context.RequestData.ReadInt32(); + bool enabledPalmaStep = context.RequestData.ReadBoolean(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { palmaConnectionHandle, enabledPalmaStep }); @@ -1694,9 +1719,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // ReadPalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1) public ResultCode ReadPalmaApplicationSection(ServiceCtx context) { - int palmaConnectionHandle = context.RequestData.ReadInt32(); - long unknown0 = context.RequestData.ReadInt64(); - long unknown1 = context.RequestData.ReadInt64(); + int palmaConnectionHandle = context.RequestData.ReadInt32(); + long unknown0 = context.RequestData.ReadInt64(); + long unknown1 = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { palmaConnectionHandle, unknown0, unknown1 }); @@ -1707,9 +1732,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // WritePalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1, nn::hid::PalmaApplicationSectionAccessBuffer) public ResultCode WritePalmaApplicationSection(ServiceCtx context) { - int palmaConnectionHandle = context.RequestData.ReadInt32(); - long unknown0 = context.RequestData.ReadInt64(); - long unknown1 = context.RequestData.ReadInt64(); + int palmaConnectionHandle = context.RequestData.ReadInt32(); + long unknown0 = context.RequestData.ReadInt64(); + long unknown1 = context.RequestData.ReadInt64(); // nn::hid::PalmaApplicationSectionAccessBuffer cast is unknown Logger.Stub?.PrintStub(LogClass.ServiceHid, new { palmaConnectionHandle, unknown0, unknown1 }); @@ -1746,7 +1771,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ResultCode SetIsPalmaAllConnectable(ServiceCtx context) { long appletResourceUserId = context.RequestData.ReadInt64(); - long unknownBool = context.RequestData.ReadInt64(); + long unknownBool = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, unknownBool }); @@ -1766,8 +1791,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid // SetNpadCommunicationMode(long CommunicationMode, nn::applet::AppletResourceUserId) public ResultCode SetNpadCommunicationMode(ServiceCtx context) { - _npadCommunicationMode = context.RequestData.ReadInt64(); - long appletResourceUserId = context.RequestData.ReadInt64(); + _npadCommunicationMode = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _npadCommunicationMode }); @@ -1790,7 +1815,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ResultCode SetTouchScreenConfiguration(ServiceCtx context) { long touchScreenConfigurationForNx = context.RequestData.ReadInt64(); - long appletResourceUserId = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, touchScreenConfigurationForNx }); diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs index 4a5d0e9b5..685f6841d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid private ResultCode GetAppletFooterUiTypeImpl(ServiceCtx context, out AppletFooterUiType appletFooterUiType) { - NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32(); + NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32(); PlayerIndex playerIndex = HidUtils.GetIndexFromNpadIdType(npadIdType); appletFooterUiType = context.Device.Hid.SharedMemory.Npads[(int)playerIndex].InternalState.AppletFooterUiType; @@ -73,4 +73,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs index eec5292f5..7c624dfc3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs @@ -10,12 +10,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid [CommandCmif(1)] // GetBusHandle(nn::hid::NpadIdType, nn::hidbus::BusType, nn::applet::AppletResourceUserId) -> (bool HasHandle, nn::hidbus::BusHandle) +#pragma warning disable CA1822 // Mark member as static public ResultCode GetBusHandle(ServiceCtx context) +#pragma warning restore CA1822 { - NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); + NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); context.RequestData.BaseStream.Position += 4; // Padding - BusType busType = (BusType)context.RequestData.ReadInt64(); - long appletResourceUserId = context.RequestData.ReadInt64(); + BusType busType = (BusType)context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); context.ResponseData.Write(false); context.ResponseData.BaseStream.Position += 7; // Padding @@ -26,4 +28,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs index 713533442..daff6fda9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs @@ -5,4 +5,4 @@ { public ISystemServer(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs index 130fcf687..a13e77e72 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs @@ -65,8 +65,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // StopImageProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId) public ResultCode StopImageProcessor(ServiceCtx context) { - IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); + IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); CheckCameraHandle(irCameraHandle); @@ -79,9 +79,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // RunMomentProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedMomentProcessorConfig) public ResultCode RunMomentProcessor(ServiceCtx context) { - IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - var packedMomentProcessorConfig = context.RequestData.ReadStruct<PackedMomentProcessorConfig>(); + IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); + var packedMomentProcessorConfig = context.RequestData.ReadStruct<PackedMomentProcessorConfig>(); CheckCameraHandle(irCameraHandle); @@ -94,9 +94,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // RunClusteringProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedClusteringProcessorConfig) public ResultCode RunClusteringProcessor(ServiceCtx context) { - IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - var packedClusteringProcessorConfig = context.RequestData.ReadStruct<PackedClusteringProcessorConfig>(); + IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); + var packedClusteringProcessorConfig = context.RequestData.ReadStruct<PackedClusteringProcessorConfig>(); CheckCameraHandle(irCameraHandle); @@ -109,9 +109,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // RunImageTransferProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedImageTransferProcessorConfig, u64 TransferMemorySize, TransferMemoryHandle) public ResultCode RunImageTransferProcessor(ServiceCtx context) { - IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - var packedImageTransferProcessorConfig = context.RequestData.ReadStruct<PackedImageTransferProcessorConfig>(); + IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); + var packedImageTransferProcessorConfig = context.RequestData.ReadStruct<PackedImageTransferProcessorConfig>(); CheckCameraHandle(irCameraHandle); @@ -126,8 +126,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // GetImageTransferProcessorState(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId) public ResultCode GetImageTransferProcessorState(ServiceCtx context) { - IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); + IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); // ulong imageTransferBufferAddress = context.Request.ReceiveBuff[0].Position; ulong imageTransferBufferSize = context.Request.ReceiveBuff[0].Size; @@ -144,8 +144,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // TODO: Uses the buffer to copy the JoyCon IR data (by using a JoyCon driver) and update the following struct. context.ResponseData.WriteStruct(new ImageTransferProcessorState() { - SamplingNumber = 0, - AmbientNoiseLevel = 0 + SamplingNumber = 0, + AmbientNoiseLevel = 0, }); return ResultCode.Success; @@ -155,9 +155,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // RunTeraPluginProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedTeraPluginProcessorConfig) public ResultCode RunTeraPluginProcessor(ServiceCtx context) { - IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - var packedTeraPluginProcessorConfig = context.RequestData.ReadStruct<PackedTeraPluginProcessorConfig>(); + IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); + var packedTeraPluginProcessorConfig = context.RequestData.ReadStruct<PackedTeraPluginProcessorConfig>(); CheckCameraHandle(irCameraHandle); @@ -172,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs { NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32(); - if (npadIdType > NpadIdType.Player8 && + if (npadIdType > NpadIdType.Player8 && npadIdType != NpadIdType.Unknown && npadIdType != NpadIdType.Handheld) { @@ -193,10 +193,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // CheckFirmwareVersion(nn::irsensor::IrCameraHandle, nn::irsensor::PackedMcuVersion, nn::applet::AppletResourceUserId, pid) public ResultCode CheckFirmwareVersion(ServiceCtx context) { - int irCameraHandle = context.RequestData.ReadInt32(); + int irCameraHandle = context.RequestData.ReadInt32(); short packedMcuVersionMajor = context.RequestData.ReadInt16(); short packedMcuVersionMinor = context.RequestData.ReadInt16(); - long appletResourceUserId = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle, packedMcuVersionMajor, packedMcuVersionMinor }); @@ -207,7 +207,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs // StopImageProcessorAsync(nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, pid) public ResultCode StopImageProcessorAsync(ServiceCtx context) { - int irCameraHandle = context.RequestData.ReadInt32(); + int irCameraHandle = context.RequestData.ReadInt32(); long appletResourceUserId = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle }); @@ -220,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs public ResultCode ActivateIrsensorWithFunctionLevel(ServiceCtx context) { long appletResourceUserId = context.RequestData.ReadInt64(); - long packedFunctionLevel = context.RequestData.ReadInt64(); + long packedFunctionLevel = context.RequestData.ReadInt64(); Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, packedFunctionLevel }); diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs index 99fcd5415..ad78d4f1e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorSystemServer.cs @@ -5,4 +5,4 @@ { public IIrSensorSystemServer(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs index 3afc03c27..322f9349b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs @@ -2,14 +2,14 @@ { public enum ResultCode { - ModuleId = 205, + ModuleId = 205, ErrorCodeShift = 9, Success = 0, InvalidCameraHandle = (204 << ErrorCodeShift) | ModuleId, - InvalidBufferSize = (207 << ErrorCodeShift) | ModuleId, + InvalidBufferSize = (207 << ErrorCodeShift) | ModuleId, HandlePointerIsNull = (212 << ErrorCodeShift) | ModuleId, - NpadIdOutOfRange = (709 << ErrorCodeShift) | ModuleId + NpadIdOutOfRange = (709 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs index 647aef649..7f28667ce 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/ImageTransferProcessorState.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types struct ImageTransferProcessorState { public ulong SamplingNumber; - public uint AmbientNoiseLevel; - public uint Reserved; + public uint AmbientNoiseLevel; + public uint Reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs index 8ed7201ed..282f92796 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types [StructLayout(LayoutKind.Sequential, Size = 0x4)] struct IrCameraHandle { - public byte PlayerNumber; - public byte DeviceType; + public byte PlayerNumber; + public byte DeviceType; public ushort Reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs index 735f78227..a65a1ba61 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedClusteringProcessorConfig.cs @@ -5,21 +5,21 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types [StructLayout(LayoutKind.Sequential, Size = 0x28)] struct PackedClusteringProcessorConfig { - public long ExposureTime; - public byte LightTarget; - public byte Gain; - public byte IsNegativeImageUsed; - public byte Reserved1; - public uint Reserved2; + public long ExposureTime; + public byte LightTarget; + public byte Gain; + public byte IsNegativeImageUsed; + public byte Reserved1; + public uint Reserved2; public ushort WindowOfInterestX; public ushort WindowOfInterestY; public ushort WindowOfInterestWidth; public ushort WindowOfInterestHeight; - public uint RequiredMcuVersion; - public uint ObjectPixelCountMin; - public uint ObjectPixelCountMax; - public byte ObjectIntensityMin; - public byte IsExternalLightFilterEnabled; + public uint RequiredMcuVersion; + public uint ObjectPixelCountMin; + public uint ObjectPixelCountMax; + public byte ObjectIntensityMin; + public byte IsExternalLightFilterEnabled; public ushort Reserved3; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs index 094413e00..c97de27a8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedImageTransferProcessorConfig.cs @@ -5,15 +5,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types [StructLayout(LayoutKind.Sequential, Size = 0x18)] struct PackedImageTransferProcessorConfig { - public long ExposureTime; - public byte LightTarget; - public byte Gain; - public byte IsNegativeImageUsed; - public byte Reserved1; - public uint Reserved2; - public uint RequiredMcuVersion; - public byte Format; - public byte Reserved3; + public long ExposureTime; + public byte LightTarget; + public byte Gain; + public byte IsNegativeImageUsed; + public byte Reserved1; + public uint Reserved2; + public uint RequiredMcuVersion; + public byte Format; + public byte Reserved3; public ushort Reserved4; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs index a1b70b405..bbee28156 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedMomentProcessorConfig.cs @@ -5,19 +5,19 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types [StructLayout(LayoutKind.Sequential, Size = 0x20)] struct PackedMomentProcessorConfig { - public long ExposureTime; - public byte LightTarget; - public byte Gain; - public byte IsNegativeImageUsed; - public byte Reserved1; - public uint Reserved2; + public long ExposureTime; + public byte LightTarget; + public byte Gain; + public byte IsNegativeImageUsed; + public byte Reserved1; + public uint Reserved2; public ushort WindowOfInterestX; public ushort WindowOfInterestY; public ushort WindowOfInterestWidth; public ushort WindowOfInterestHeight; - public uint RequiredMcuVersion; - public byte Preprocess; - public byte PreprocessIntensityThreshold; + public uint RequiredMcuVersion; + public byte Preprocess; + public byte PreprocessIntensityThreshold; public ushort Reserved3; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs index 808b0b727..4fada04af 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/PackedTeraPluginProcessorConfig.cs @@ -11,4 +11,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types public byte Unknown2; public byte Unknown3; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs index 9c87ac1dc..993ff7076 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs @@ -2,14 +2,14 @@ { enum ResultCode { - ModuleId = 202, + ModuleId = 202, ErrorCodeShift = 9, Success = 0, InvalidNpadDeviceType = (122 << ErrorCodeShift) | ModuleId, - InvalidNpadIdType = (123 << ErrorCodeShift) | ModuleId, - InvalidDeviceIndex = (124 << ErrorCodeShift) | ModuleId, - InvalidBufferSize = (131 << ErrorCodeShift) | ModuleId + InvalidNpadIdType = (123 << ErrorCodeShift) | ModuleId, + InvalidDeviceIndex = (124 << ErrorCodeShift) | ModuleId, + InvalidBufferSize = (131 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs index c4ff8d7ef..1eccd856e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs @@ -25,6 +25,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types LarkNesLeft, LarkNesRight, Lucia, - Verification + Verification, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs index 18d9fd9c9..8d667796a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs @@ -6,4 +6,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types public float Y; public float Z; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs index 936ee68c4..033eb3069 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid [StructLayout(LayoutKind.Sequential)] struct BusHandle { - public int AbstractedPadId; + public int AbstractedPadId; public byte InternalIndex; public byte PlayerNumber; public byte BusTypeId; diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs index 41852365b..25b36ca45 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs @@ -2,8 +2,8 @@ { public enum BusType : long { - LeftJoyRail = 0, + LeftJoyRail = 0, RightJoyRail = 1, - InternalBus = 2 + InternalBus = 2, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs index c91636b2c..b43381e6d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs @@ -5,41 +5,41 @@ namespace Ryujinx.HLE.HOS.Services.Hid [Flags] public enum ControllerKeys : long { - A = 1 << 0, - B = 1 << 1, - X = 1 << 2, - Y = 1 << 3, - LStick = 1 << 4, - RStick = 1 << 5, - L = 1 << 6, - R = 1 << 7, - Zl = 1 << 8, - Zr = 1 << 9, - Plus = 1 << 10, - Minus = 1 << 11, - DpadLeft = 1 << 12, - DpadUp = 1 << 13, - DpadRight = 1 << 14, - DpadDown = 1 << 15, - LStickLeft = 1 << 16, - LStickUp = 1 << 17, + A = 1 << 0, + B = 1 << 1, + X = 1 << 2, + Y = 1 << 3, + LStick = 1 << 4, + RStick = 1 << 5, + L = 1 << 6, + R = 1 << 7, + Zl = 1 << 8, + Zr = 1 << 9, + Plus = 1 << 10, + Minus = 1 << 11, + DpadLeft = 1 << 12, + DpadUp = 1 << 13, + DpadRight = 1 << 14, + DpadDown = 1 << 15, + LStickLeft = 1 << 16, + LStickUp = 1 << 17, LStickRight = 1 << 18, - LStickDown = 1 << 19, - RStickLeft = 1 << 20, - RStickUp = 1 << 21, + LStickDown = 1 << 19, + RStickLeft = 1 << 20, + RStickUp = 1 << 21, RStickRight = 1 << 22, - RStickDown = 1 << 23, - SlLeft = 1 << 24, - SrLeft = 1 << 25, - SlRight = 1 << 26, - SrRight = 1 << 27, + RStickDown = 1 << 23, + SlLeft = 1 << 24, + SrLeft = 1 << 25, + SlRight = 1 << 26, + SrRight = 1 << 27, // Generic Catch-all - Up = DpadUp | LStickUp | RStickUp, - Down = DpadDown | LStickDown | RStickDown, - Left = DpadLeft | LStickLeft | RStickLeft, + Up = DpadUp | LStickUp | RStickUp, + Down = DpadDown | LStickDown | RStickDown, + Left = DpadLeft | LStickLeft | RStickLeft, Right = DpadRight | LStickRight | RStickRight, - Sl = SlLeft | SlRight, - Sr = SrLeft | SrRight + Sl = SlLeft | SlRight, + Sr = SrLeft | SrRight, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs index d830f46ad..1f5da3173 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs @@ -6,14 +6,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid public enum ControllerType { None, - ProController = 1 << 0, - Handheld = 1 << 1, - JoyconPair = 1 << 2, - JoyconLeft = 1 << 3, - JoyconRight = 1 << 4, - Invalid = 1 << 5, - Pokeball = 1 << 6, + ProController = 1 << 0, + Handheld = 1 << 1, + JoyconPair = 1 << 2, + JoyconLeft = 1 << 3, + JoyconRight = 1 << 4, + Invalid = 1 << 5, + Pokeball = 1 << 6, SystemExternal = 1 << 29, - System = 1 << 30 + System = 1 << 30, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs index 3c311e218..010cffbd2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs @@ -1,37 +1,37 @@ namespace Ryujinx.HLE.HOS.Services.Hid { - public enum NpadColor : uint + public enum NpadColor : uint { - BodyGray = 0x828282, - BodyNeonRed = 0xFF3C28, - BodyNeonBlue = 0x0AB9E6, - BodyNeonYellow = 0xE6FF00, - BodyNeonGreen = 0x1EDC00, - BodyNeonPink = 0xFF3278, - BodyRed = 0xE10F00, - BodyBlue = 0x4655F5, - BodyNeonPurple = 0xB400E6, - BodyNeonOrange = 0xFAA005, - BodyPokemonLetsGoPikachu = 0xFFDC00, - BodyPokemonLetsGoEevee = 0xC88C32, - BodyNintendoLaboCreatorsContestEdition = 0xD7AA73, - BodyAnimalCrossingSpecialEditionLeftJoyCon = 0x82FF96, - BodyAnimalCrossingSpecialEditionRightJoyCon = 0x96F5F5, + BodyGray = 0x828282, + BodyNeonRed = 0xFF3C28, + BodyNeonBlue = 0x0AB9E6, + BodyNeonYellow = 0xE6FF00, + BodyNeonGreen = 0x1EDC00, + BodyNeonPink = 0xFF3278, + BodyRed = 0xE10F00, + BodyBlue = 0x4655F5, + BodyNeonPurple = 0xB400E6, + BodyNeonOrange = 0xFAA005, + BodyPokemonLetsGoPikachu = 0xFFDC00, + BodyPokemonLetsGoEevee = 0xC88C32, + BodyNintendoLaboCreatorsContestEdition = 0xD7AA73, + BodyAnimalCrossingSpecialEditionLeftJoyCon = 0x82FF96, + BodyAnimalCrossingSpecialEditionRightJoyCon = 0x96F5F5, - ButtonGray = 0x0F0F0F, - ButtonNeonRed = 0x1E0A0A, - ButtonNeonBlue = 0x001E1E, - ButtonNeonYellow = 0x142800, - ButtonNeonGreen = 0x002800, - ButtonNeonPink = 0x28001E, - ButtonRed = 0x280A0A, - ButtonBlue = 0x00000A, - ButtonNeonPurple = 0x140014, - ButtonNeonOrange = 0x0F0A00, - ButtonPokemonLetsGoPikachu = 0x322800, - ButtonPokemonLetsGoEevee = 0x281900, - ButtonNintendoLaboCreatorsContestEdition = 0x1E1914, - ButtonAnimalCrossingSpecialEditionLeftJoyCon = 0x0A1E0A, - ButtonAnimalCrossingSpecialEditionRightJoyCon = 0x0A1E28 + ButtonGray = 0x0F0F0F, + ButtonNeonRed = 0x1E0A0A, + ButtonNeonBlue = 0x001E1E, + ButtonNeonYellow = 0x142800, + ButtonNeonGreen = 0x002800, + ButtonNeonPink = 0x28001E, + ButtonRed = 0x280A0A, + ButtonBlue = 0x00000A, + ButtonNeonPurple = 0x140014, + ButtonNeonOrange = 0x0F0A00, + ButtonPokemonLetsGoPikachu = 0x322800, + ButtonPokemonLetsGoEevee = 0x281900, + ButtonNintendoLaboCreatorsContestEdition = 0x1E1914, + ButtonAnimalCrossingSpecialEditionLeftJoyCon = 0x0A1E0A, + ButtonAnimalCrossingSpecialEditionRightJoyCon = 0x0A1E28, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs index 4b488cd2c..5a9247d92 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs @@ -2,15 +2,15 @@ { public enum NpadIdType { - Player1 = 0, - Player2 = 1, - Player3 = 2, - Player4 = 3, - Player5 = 4, - Player6 = 5, - Player7 = 6, - Player8 = 7, - Unknown = 16, - Handheld = 32 + Player1 = 0, + Player2 = 1, + Player3 = 2, + Player4 = 3, + Player5 = 4, + Player6 = 5, + Player7 = 6, + Player8 = 7, + Unknown = 16, + Handheld = 32, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs index ddf5d97f5..04550b6fe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadStyleIndex.cs @@ -2,12 +2,12 @@ { public enum NpadStyleIndex : byte { - FullKey = 3, - Handheld = 4, - JoyDual = 5, - JoyLeft = 6, - JoyRight = 7, + FullKey = 3, + Handheld = 4, + JoyDual = 5, + JoyLeft = 6, + JoyRight = 7, SystemExt = 32, - System = 33 + System = 33, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs index 972d69b45..d68b6d938 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs @@ -2,16 +2,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid { public enum PlayerIndex { - Player1 = 0, - Player2 = 1, - Player3 = 2, - Player4 = 3, - Player5 = 4, - Player6 = 5, - Player7 = 6, - Player8 = 7, + Player1 = 0, + Player2 = 1, + Player3 = 2, + Player4 = 3, + Player5 = 4, + Player6 = 5, + Player7 = 6, + Player8 = 7, Handheld = 8, - Unknown = 9, - Auto = 10 // Shouldn't be used directly + Unknown = 9, + Auto = 10, // Shouldn't be used directly } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs index d3b51a24c..19297de4c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs @@ -3,6 +3,6 @@ enum NpadJoyHoldType { Vertical, - Horizontal + Horizontal, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs index da53e4211..b6bc288e6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common { - struct AtomicStorage<T> where T: unmanaged, ISampledDataStruct + struct AtomicStorage<T> where T : unmanaged, ISampledDataStruct { public ulong SamplingNumber; public T Object; diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs index a382c0c28..ae1997d4e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledDataStruct.cs @@ -11,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common /// - use <c>StructLayoutAttribute</c> (and related attributes) to explicity control how the struct is laid out in memory. /// - ensure that the method <c>ISampledDataStruct.GetSamplingNumberFieldOffset()</c> correctly returns the offset, in bytes, /// to the ulong "Sampling Number" field within the struct. Most types have it as the first field, so the default offset is 0. - /// + /// /// Example: - /// + /// /// <c> /// [StructLayout(LayoutKind.Sequential, Pack = 8)] /// struct DebugPadState : ISampledDataStruct @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common if (fieldOffset > 0) { - byteSpan = byteSpan.Slice(fieldOffset); + byteSpan = byteSpan[fieldOffset..]; } ulong value = BinaryPrimitives.ReadUInt64LittleEndian(byteSpan); @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common return sampledDataStruct switch { Npad.SixAxisSensorState _ => sizeof(ulong), - _ => 0 + _ => 0, }; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs index ae654d6f8..26ea1cff1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs @@ -5,16 +5,16 @@ using System.Threading; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common { - struct RingLifo<T> where T: unmanaged, ISampledDataStruct + struct RingLifo<T> where T : unmanaged, ISampledDataStruct { private const ulong MaxEntries = 17; -#pragma warning disable CS0169 - private ulong _unused; -#pragma warning restore CS0169 -#pragma warning disable CS0414 +#pragma warning disable IDE0051, CS0169 // Remove unused private member + private readonly ulong _unused; +#pragma warning restore IDE0051, CS0169 +#pragma warning disable CS0414, IDE0052 // Remove unread private member private ulong _bufferCount; -#pragma warning restore CS0414 +#pragma warning restore CS0414, IDE0052 private ulong _index; private ulong _count; private Array17<AtomicStorage<T>> _storage; @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong GetNextIndexForWrite(ulong index) + private readonly ulong GetNextIndexForWrite(ulong index) { return (index + 1) % MaxEntries; } @@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common { return new RingLifo<T> { - _bufferCount = MaxEntries + _bufferCount = MaxEntries, }; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs index ec5bd3c88..abc6990b8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs @@ -6,6 +6,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad enum DebugPadAttribute : uint { None = 0, - Connected = 1 << 0 + Connected = 1 << 0, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs index e8f283170..dc713a038 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs @@ -19,6 +19,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad Left = 1 << 10, Up = 1 << 11, Right = 1 << 12, - Down = 1 << 13 + Down = 1 << 13, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs index 22df7c791..336037ab1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs @@ -26,4 +26,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs index 01c2bb304..46461ad84 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs @@ -133,6 +133,6 @@ RightControl = 228, RightShift = 229, RightAlt = 230, - RightGui = 231 + RightGui = 231, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs index 839a4e829..ad94619ed 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs @@ -15,6 +15,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard ScrollLock = 1 << 9, NumLock = 1 << 10, Katakana = 1 << 11, - Hiragana = 1 << 12 + Hiragana = 1 << 12, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs index 5ffba0d7b..ce3dd9462 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs @@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse { None = 0, Transferable = 1 << 0, - IsConnected = 1 << 1 + IsConnected = 1 << 1, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs index 7e35140c6..3e0259938 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs @@ -10,6 +10,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse Right = 1 << 1, Middle = 1 << 2, Forward = 1 << 3, - Back = 1 << 4 + Back = 1 << 4, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs index 95b1cb518..259c712b8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs @@ -24,6 +24,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad HandheldLarkNesRight = 1 << 14, Lucia = 1 << 15, - System = 1 << 31 + System = 1 << 31, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs index 0960b7bf3..6ad2531f4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs @@ -11,6 +11,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad IsLeftConnected = 1 << 2, IsLeftWired = 1 << 3, IsRightConnected = 1 << 4, - IsRightWired = 1 << 5 + IsRightWired = 1 << 5, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs index e10e55cd8..a84e0259d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs @@ -6,6 +6,6 @@ Percent25, Percent50, Percent75, - Percent100 + Percent100, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs index 5b3e13a72..442d4089b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs @@ -39,6 +39,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad // FIXME: Probably a button on Lark. Unknown29 = 1 << 29, - HandheldLeftB = 1 << 30 + HandheldLeftB = 1 << 30, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs index 1e547cc8f..1a7846dbc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs @@ -4,6 +4,6 @@ { Ok, ReadError, - NoController + NoController, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs index 64f75ce9a..e1d70a8a2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs @@ -11,6 +11,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad public AnalogStickState AnalogStickL; public AnalogStickState AnalogStickR; public NpadAttribute Attributes; - private uint _reserved; + private readonly uint _reserved; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs index 990eafb26..92d4a2ae0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs @@ -6,4 +6,4 @@ public uint FullKeyBody; public uint FullKeyButtons; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs index bddd6212d..39453984f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs @@ -6,10 +6,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad [StructLayout(LayoutKind.Sequential, Pack = 1)] struct NpadGcTriggerState : ISampledDataStruct { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public ulong SamplingNumber; public uint TriggerL; public uint TriggerR; #pragma warning restore CS0649 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs index b009f95e5..60d16fd83 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs @@ -23,7 +23,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad public RingLifo<SixAxisSensorState> JoyLeftSixAxisSensor; public RingLifo<SixAxisSensorState> JoyRightSixAxisSensor; public DeviceType DeviceType; - private uint _reserved1; +#pragma warning disable IDE0051 // Remove unused private member + private readonly uint _reserved1; +#pragma warning restore IDE0051 public NpadSystemProperties SystemProperties; public NpadSystemButtonProperties SystemButtonProperties; public NpadBatteryLevel BatteryLevelJoyDual; @@ -31,7 +33,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad public NpadBatteryLevel BatteryLevelJoyRight; public uint AppletFooterUiAttributes; public AppletFooterUiType AppletFooterUiType; - private Reserved2Struct _reserved2; +#pragma warning disable IDE0051 // Remove unused private member + private readonly Reserved2Struct _reserved2; +#pragma warning restore IDE0051 public RingLifo<NpadGcTriggerState> GcTrigger; public NpadLarkType LarkTypeLeftAndMain; public NpadLarkType LarkTypeRight; @@ -39,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad public uint Unknown43EC; [StructLayout(LayoutKind.Sequential, Size = 123, Pack = 1)] - private struct Reserved2Struct {} + private struct Reserved2Struct { } public static NpadInternalState Create() { @@ -62,4 +66,4 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs index 871c4c5a4..c50abe16e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs @@ -3,6 +3,6 @@ enum NpadJoyAssignmentMode : uint { Dual, - Single + Single, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs index 3986dd5e0..6fec613b6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs @@ -8,4 +8,4 @@ public uint RightBody; public uint RightButtons; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs index 951484857..e12e84e4f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs @@ -5,6 +5,6 @@ Invalid, J, E, - U + U, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs index ed9e7c0df..643234fc2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad { return new NpadState { - InternalState = NpadInternalState.Create() + InternalState = NpadInternalState.Create(), }; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs index f31978e24..d9ecdcd1a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs @@ -71,6 +71,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad /// <summary> /// Generic controller. /// </summary> - System = 1 << 30 + System = 1 << 30, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs index 686032714..56a4a80f7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs @@ -6,6 +6,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad enum NpadSystemButtonProperties : uint { None = 0, - IsUnintendedHomeButtonInputProtectionEnabled = 1 << 0 + IsUnintendedHomeButtonInputProtectionEnabled = 1 << 0, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs index 13444555e..817c6c143 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs @@ -19,6 +19,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad IsSlSrButtonOriented = 1 << 12, IsPlusAvailable = 1 << 13, IsMinusAvailable = 1 << 14, - IsDirectionalButtonsAvailable = 1 << 15 + IsDirectionalButtonsAvailable = 1 << 15, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs index 7ed46d981..0190f09d7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs @@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad { None = 0, IsConnected = 1 << 0, - IsInterpolated = 1 << 1 + IsInterpolated = 1 << 1, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs index 18be32763..25f65a0ef 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs @@ -14,6 +14,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad public HidVector Angle; public Array9<float> Direction; public SixAxisSensorAttribute Attributes; - private uint _reserved; + private readonly uint _reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs index 48acfc3f3..640076b34 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs @@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory public static SharedMemory Create() { - SharedMemory result = new SharedMemory + SharedMemory result = new() { DebugPad = RingLifo<DebugPadState>.Create(), TouchScreen = RingLifo<TouchScreenState>.Create(), diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs index d2c5726a5..7c43e6cd4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs @@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen { None = 0, Start = 1 << 0, - End = 1 << 1 + End = 1 << 1, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs index cdd4cc45e..5ce51ee3c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen { public ulong SamplingNumber; public int TouchesCount; - private int _reserved; + private readonly int _reserved; public Array16<TouchState> Touches; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs index ba621a2b0..908f9ed5e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs @@ -3,7 +3,7 @@ struct TouchState { public ulong DeltaTime; -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public TouchAttribute Attribute; #pragma warning restore CS0649 public uint FingerId; @@ -12,8 +12,8 @@ public uint DiameterX; public uint DiameterY; public uint RotationAngle; -#pragma warning disable CS0169 - private uint _reserved; -#pragma warning restore CS0169 +#pragma warning disable CS0169, IDE0051 // Remove unused private member + private readonly uint _reserved; +#pragma warning restore CS0169, IDE0051 } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs b/src/Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs index 34d4bdfd7..8ee00d0e9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ins/IReceiverManager.cs @@ -5,4 +5,4 @@ { public IReceiverManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs b/src/Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs index 38a95ee7b..239c4cc83 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ins/ISenderManager.cs @@ -5,4 +5,4 @@ { public ISenderManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 048a68a9e..eb9f6b2fd 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services public ServerBase Server { get; private set; } private IpcService _parent; - private IdDictionary _domainObjects; + private readonly IdDictionary _domainObjects; private int _selfId; private bool _isDomain; @@ -106,7 +106,9 @@ namespace Ryujinx.HLE.HOS.Services } } +#pragma warning disable IDE0059 // Remove unnecessary value assignment long sfciMagic = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 int commandId = (int)context.RequestData.ReadInt64(); bool serviceExists = service.CmifCommands.TryGetValue(commandId, out MethodInfo processRequest); @@ -127,9 +129,8 @@ namespace Ryujinx.HLE.HOS.Services { string serviceName; - DummyService dummyService = service as DummyService; - serviceName = (dummyService == null) ? service.GetType().FullName : dummyService.ServiceName; + serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName; Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored"); } @@ -181,9 +182,8 @@ namespace Ryujinx.HLE.HOS.Services { string serviceName; - DummyService dummyService = this as DummyService; - serviceName = (dummyService == null) ? GetType().FullName : dummyService.ServiceName; + serviceName = (this is not DummyService dummyService) ? GetType().FullName : dummyService.ServiceName; Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored"); } diff --git a/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs b/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs index 65074d5ff..75d787432 100644 --- a/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs @@ -89,4 +89,4 @@ return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs b/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs index b68be1f2f..899e882e5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs @@ -3,7 +3,7 @@ [Service("lbl")] class LblControllerServer : ILblController { - private bool _vrModeEnabled; + private bool _vrModeEnabled; private float _currentBrightnessSettingForVrMode; public LblControllerServer(ServiceCtx context) : base(context) { } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs index 09dfa78fa..a7e99241c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs @@ -5,4 +5,4 @@ { public IMonitorServiceCreator(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs index b4dac449e..9acfef5c9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/ISystemServiceCreator.cs @@ -5,4 +5,4 @@ { public ISystemServiceCreator(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs index 4f3094ae5..317b1dbe8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Ldn return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs index 9c9ee3be2..f2fd482dd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs @@ -6,4 +6,4 @@ { public IServiceCreator(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs index 274b6132c..26696cbeb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs @@ -7,15 +7,15 @@ namespace Ryujinx.HLE.HOS.Services.Ldn { internal class NetworkInterface { - public ResultCode NifmState { get; set; } - public KEvent StateChangeEvent { get; private set; } + public ResultCode NifmState { get; set; } + public KEvent StateChangeEvent { get; private set; } private NetworkState _state; public NetworkInterface(Horizon system) { // TODO(Ac_K): Determine where the internal state is set. - NifmState = ResultCode.Success; + NifmState = ResultCode.Success; StateChangeEvent = new KEvent(system.KernelContext); _state = NetworkState.None; @@ -56,4 +56,4 @@ namespace Ryujinx.HLE.HOS.Services.Ldn return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs index 87674f7c3..a9f2cbc33 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs @@ -2,15 +2,15 @@ namespace Ryujinx.HLE.HOS.Services.Ldn { enum ResultCode { - ModuleId = 203, + ModuleId = 203, ErrorCodeShift = 9, Success = 0, - DeviceDisabled = (22 << ErrorCodeShift) | ModuleId, - InvalidState = (32 << ErrorCodeShift) | ModuleId, - Unknown1 = (48 << ErrorCodeShift) | ModuleId, + DeviceDisabled = (22 << ErrorCodeShift) | ModuleId, + InvalidState = (32 << ErrorCodeShift) | ModuleId, + Unknown1 = (48 << ErrorCodeShift) | ModuleId, InvalidArgument = (96 << ErrorCodeShift) | ModuleId, - InvalidObject = (97 << ErrorCodeShift) | ModuleId, + InvalidObject = (97 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs index 6ac204833..61fd831df 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs @@ -8,6 +8,6 @@ AccessPointCreated, Station, StationConnected, - Error + Error, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index f425ddf78..d390a3e68 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator // TODO(Ac_K): Determine what the hardcoded unknown value is. private const int UnknownValue = 90; - private NetworkInterface _networkInterface; + private readonly NetworkInterface _networkInterface; private int _stateChangeEventHandle = 0; @@ -79,10 +79,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public ResultCode Initialize(ServiceCtx context) { // TODO(Ac_K): Determine what addresses are. - IPAddress unknownAddress1 = new IPAddress(context.RequestData.ReadUInt32()); - IPAddress unknownAddress2 = new IPAddress(context.RequestData.ReadUInt32()); + IPAddress unknownAddress1 = new(context.RequestData.ReadUInt32()); + IPAddress unknownAddress2 = new(context.RequestData.ReadUInt32()); return _networkInterface.Initialize(UnknownValue, version: 1, unknownAddress1, unknownAddress2); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs b/src/Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs index 82b24a35c..78c405b4e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Loader/IDebugMonitorInterface.cs @@ -5,4 +5,4 @@ { public IDebugMonitorInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs index 2ecde2ade..693d68dac 100644 --- a/src/Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Loader/IProcessManagerInterface.cs @@ -5,4 +5,4 @@ { public IProcessManagerInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs b/src/Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs index 362f82f03..d2dc57bdb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Loader/IShellInterface.cs @@ -5,4 +5,4 @@ { public IShellInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs index 170dfa015..ec1166480 100644 --- a/src/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs @@ -1,43 +1,46 @@ -namespace Ryujinx.HLE.HOS.Services.Loader +using System.Diagnostics.CodeAnalysis; + +namespace Ryujinx.HLE.HOS.Services.Loader { + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum ResultCode { - ModuleId = 9, + ModuleId = 9, ErrorCodeShift = 9, Success = 0, - ArgsTooLong = (1 << ErrorCodeShift) | ModuleId, - MaximumProcessesLoaded = (2 << ErrorCodeShift) | ModuleId, - NPDMTooBig = (3 << ErrorCodeShift) | ModuleId, - InvalidNPDM = (4 << ErrorCodeShift) | ModuleId, - InvalidNSO = (5 << ErrorCodeShift) | ModuleId, - InvalidPath = (6 << ErrorCodeShift) | ModuleId, - AlreadyRegistered = (7 << ErrorCodeShift) | ModuleId, - TitleNotFound = (8 << ErrorCodeShift) | ModuleId, - ACI0TitleIdNotMatchingRangeInACID = (9 << ErrorCodeShift) | ModuleId, - InvalidVersionInNPDM = (10 << ErrorCodeShift) | ModuleId, - InsufficientAddressSpace = (51 << ErrorCodeShift) | ModuleId, - InsufficientNRO = (52 << ErrorCodeShift) | ModuleId, - InvalidNRR = (53 << ErrorCodeShift) | ModuleId, - InvalidSignature = (54 << ErrorCodeShift) | ModuleId, - InsufficientNRORegistrations = (55 << ErrorCodeShift) | ModuleId, - InsufficientNRRRegistrations = (56 << ErrorCodeShift) | ModuleId, - NROAlreadyLoaded = (57 << ErrorCodeShift) | ModuleId, - UnalignedNRRAddress = (81 << ErrorCodeShift) | ModuleId, - BadNRRSize = (82 << ErrorCodeShift) | ModuleId, - NRRNotLoaded = (84 << ErrorCodeShift) | ModuleId, - BadNRRAddress = (85 << ErrorCodeShift) | ModuleId, - BadInitialization = (87 << ErrorCodeShift) | ModuleId, - UnknownACI0Descriptor = (100 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingKernelFlagsDescriptor = (103 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingSyscallMaskDescriptor = (104 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingMapIoOrNormalRangeDescriptor = (106 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingMapNormalPageDescriptor = (107 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingInterruptPairDescriptor = (111 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingApplicationTypeDescriptor = (113 << ErrorCodeShift) | ModuleId, + ArgsTooLong = (1 << ErrorCodeShift) | ModuleId, + MaximumProcessesLoaded = (2 << ErrorCodeShift) | ModuleId, + NPDMTooBig = (3 << ErrorCodeShift) | ModuleId, + InvalidNPDM = (4 << ErrorCodeShift) | ModuleId, + InvalidNSO = (5 << ErrorCodeShift) | ModuleId, + InvalidPath = (6 << ErrorCodeShift) | ModuleId, + AlreadyRegistered = (7 << ErrorCodeShift) | ModuleId, + TitleNotFound = (8 << ErrorCodeShift) | ModuleId, + ACI0TitleIdNotMatchingRangeInACID = (9 << ErrorCodeShift) | ModuleId, + InvalidVersionInNPDM = (10 << ErrorCodeShift) | ModuleId, + InsufficientAddressSpace = (51 << ErrorCodeShift) | ModuleId, + InsufficientNRO = (52 << ErrorCodeShift) | ModuleId, + InvalidNRR = (53 << ErrorCodeShift) | ModuleId, + InvalidSignature = (54 << ErrorCodeShift) | ModuleId, + InsufficientNRORegistrations = (55 << ErrorCodeShift) | ModuleId, + InsufficientNRRRegistrations = (56 << ErrorCodeShift) | ModuleId, + NROAlreadyLoaded = (57 << ErrorCodeShift) | ModuleId, + UnalignedNRRAddress = (81 << ErrorCodeShift) | ModuleId, + BadNRRSize = (82 << ErrorCodeShift) | ModuleId, + NRRNotLoaded = (84 << ErrorCodeShift) | ModuleId, + BadNRRAddress = (85 << ErrorCodeShift) | ModuleId, + BadInitialization = (87 << ErrorCodeShift) | ModuleId, + UnknownACI0Descriptor = (100 << ErrorCodeShift) | ModuleId, + ACI0NotMatchingKernelFlagsDescriptor = (103 << ErrorCodeShift) | ModuleId, + ACI0NotMatchingSyscallMaskDescriptor = (104 << ErrorCodeShift) | ModuleId, + ACI0NotMatchingMapIoOrNormalRangeDescriptor = (106 << ErrorCodeShift) | ModuleId, + ACI0NotMatchingMapNormalPageDescriptor = (107 << ErrorCodeShift) | ModuleId, + ACI0NotMatchingInterruptPairDescriptor = (111 << ErrorCodeShift) | ModuleId, + ACI0NotMatchingApplicationTypeDescriptor = (113 << ErrorCodeShift) | ModuleId, ACI0NotMatchingKernelReleaseVersionDescriptor = (114 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingHandleTableSizeDescriptor = (115 << ErrorCodeShift) | ModuleId, - ACI0NotMatchingDebugFlagsDescriptor = (116 << ErrorCodeShift) | ModuleId + ACI0NotMatchingHandleTableSizeDescriptor = (115 << ErrorCodeShift) | ModuleId, + ACI0NotMatchingDebugFlagsDescriptor = (116 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mig/IService.cs b/src/Ryujinx.HLE/HOS/Services/Mig/IService.cs index 2f6eb99ee..81f858f8f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mig/IService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mig/IService.cs @@ -5,4 +5,4 @@ { public IService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs b/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs index 6d65de95d..d9bcecee1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs @@ -13,17 +13,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii { get { - if (_instance == null) - { - _instance = new DatabaseImpl(); - } + _instance ??= new DatabaseImpl(); return _instance; } } private UtilityImpl _utilityImpl; - private MiiDatabaseManager _miiDatabase; + private readonly MiiDatabaseManager _miiDatabase; private bool _isBroken; public DatabaseImpl() diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs b/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs index 6982b0edd..56486c434 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii { class DatabaseSessionMetadata { - public uint InterfaceVersion; + public uint InterfaceVersion; public ulong UpdateCounter; public SpecialMiiKeyCode MiiKeyCode { get; private set; } @@ -12,8 +12,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii public DatabaseSessionMetadata(ulong updateCounter, SpecialMiiKeyCode miiKeyCode) { InterfaceVersion = 0; - UpdateCounter = updateCounter; - MiiKeyCode = miiKeyCode; + UpdateCounter = updateCounter; + MiiKeyCode = miiKeyCode; } public bool IsInterfaceVersionSupported(uint interfaceVersion) diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs index b8dbce155..71b9ab43f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Helper.cs @@ -39,10 +39,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii return UInt128Utils.FromHex("5279754d69694e780000000000000000"); // RyuMiiNx } +#pragma warning disable IDE0055 // Disable formatting public static ReadOnlySpan<byte> Ver3FacelineColorTable => new byte[] { 0, 1, 2, 3, 4, 5 }; public static ReadOnlySpan<byte> Ver3HairColorTable => new byte[] { 8, 1, 2, 3, 4, 5, 6, 7 }; public static ReadOnlySpan<byte> Ver3EyeColorTable => new byte[] { 8, 9, 10, 11, 12, 13 }; public static ReadOnlySpan<byte> Ver3MouthColorTable => new byte[] { 19, 20, 21, 22, 23 }; public static ReadOnlySpan<byte> Ver3GlassColorTable => new byte[] { 8, 14, 15, 16, 17, 18, 0 }; +#pragma warning restore IDE0055 } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs b/src/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs index 7d65c73fa..0a763da1c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii bool useHardcodedData = context.RequestData.ReadBoolean(); _imageCount = 0; - _isDirty = false; + _isDirty = false; context.ResponseData.Write(_isDirty); @@ -38,4 +38,4 @@ namespace Ryujinx.HLE.HOS.Services.Mii return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs b/src/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs index a7fc71c96..acf358eea 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs @@ -8,13 +8,13 @@ namespace Ryujinx.HLE.HOS.Services.Mii [Service("mii:u", false)] class IStaticService : IpcService { - private DatabaseImpl _databaseImpl; + private readonly DatabaseImpl _databaseImpl; - private bool _isSystem; + private readonly bool _isSystem; public IStaticService(ServiceCtx context, bool isSystem) { - _isSystem = isSystem; + _isSystem = isSystem; _databaseImpl = DatabaseImpl.Instance; } @@ -29,4 +29,4 @@ namespace Ryujinx.HLE.HOS.Services.Mii return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs b/src/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs index 682283b04..cb43ed2ad 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs @@ -11,17 +11,17 @@ namespace Ryujinx.HLE.HOS.Services.Mii { class MiiDatabaseManager { - private static bool IsTestModeEnabled = false; - private static uint MountCounter = 0; + private readonly bool _isTestModeEnabled = false; + private uint _mountCounter = 0; - private const ulong DatabaseTestSaveDataId = 0x8000000000000031; - private const ulong DatabaseSaveDataId = 0x8000000000000030; + private const ulong DatabaseTestSaveDataId = 0x8000000000000031; + private const ulong DatabaseSaveDataId = 0x8000000000000030; - private static U8String DatabasePath = new U8String("mii:/MiiDatabase.dat"); - private static U8String MountName = new U8String("mii"); + private readonly U8String _databasePath = new("mii:/MiiDatabase.dat"); + private readonly U8String _mountName = new("mii"); private NintendoFigurineDatabase _database; - private bool _isDirty; + private bool _isDirty; private HorizonClient _horizonClient; @@ -29,8 +29,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii public MiiDatabaseManager() { - _database = new NintendoFigurineDatabase(); - _isDirty = false; + _database = new NintendoFigurineDatabase(); + _isDirty = false; UpdateCounter = 0; } @@ -106,50 +106,63 @@ namespace Ryujinx.HLE.HOS.Services.Mii private Result MountSave() { - if (MountCounter != 0) + if (_mountCounter != 0) { - MountCounter++; + _mountCounter++; return Result.Success; } - ulong saveDataId = IsTestModeEnabled ? DatabaseTestSaveDataId : DatabaseSaveDataId; + ulong saveDataId = _isTestModeEnabled ? DatabaseTestSaveDataId : DatabaseSaveDataId; - Result result = _horizonClient.Fs.MountSystemSaveData(MountName, SaveDataSpaceId.System, saveDataId); + Result result = _horizonClient.Fs.MountSystemSaveData(_mountName, SaveDataSpaceId.System, saveDataId); if (result.IsFailure()) { if (!ResultFs.TargetNotFound.Includes(result)) + { return result; + } - if (IsTestModeEnabled) + if (_isTestModeEnabled) +#pragma warning disable CS0162 { result = _horizonClient.Fs.CreateSystemSaveData(saveDataId, 0x10000, 0x10000, SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData); - if (result.IsFailure()) return result; + if (result.IsFailure()) + { + return result; + } } +#pragma warning restore CS0162 else { result = _horizonClient.Fs.CreateSystemSaveData(saveDataId, SystemProgramId.Ns.Value, 0x10000, 0x10000, SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData); - if (result.IsFailure()) return result; + if (result.IsFailure()) + { + return result; + } } - result = _horizonClient.Fs.MountSystemSaveData(MountName, SaveDataSpaceId.System, saveDataId); - if (result.IsFailure()) return result; + result = _horizonClient.Fs.MountSystemSaveData(_mountName, SaveDataSpaceId.System, saveDataId); + if (result.IsFailure()) + { + return result; + } } if (result == Result.Success) { - MountCounter++; + _mountCounter++; } return result; } public ResultCode DeleteFile() { - ResultCode result = (ResultCode)_horizonClient.Fs.DeleteFile(DatabasePath).Value; + ResultCode result = (ResultCode)_horizonClient.Fs.DeleteFile(_databasePath).Value; - _horizonClient.Fs.Commit(MountName); + _horizonClient.Fs.Commit(_mountName); return result; } @@ -158,7 +171,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii { isBroken = false; - if (MountCounter == 0) + if (_mountCounter == 0) { return ResultCode.InvalidArgument; } @@ -167,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii ResetDatabase(); - Result result = _horizonClient.Fs.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Read); + Result result = _horizonClient.Fs.OpenFile(out FileHandle handle, _databasePath, OpenMode.Read); if (result.IsSuccess()) { @@ -213,11 +226,11 @@ namespace Ryujinx.HLE.HOS.Services.Mii private Result ForceSaveDatabase() { - Result result = _horizonClient.Fs.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>()); + Result result = _horizonClient.Fs.CreateFile(_databasePath, Unsafe.SizeOf<NintendoFigurineDatabase>()); if (result.IsSuccess() || ResultFs.PathAlreadyExists.Includes(result)) { - result = _horizonClient.Fs.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Write); + result = _horizonClient.Fs.OpenFile(out FileHandle handle, _databasePath, OpenMode.Write); if (result.IsSuccess()) { @@ -230,15 +243,15 @@ namespace Ryujinx.HLE.HOS.Services.Mii { _horizonClient.Fs.CloseFile(handle); - result = _horizonClient.Fs.DeleteFile(DatabasePath); + result = _horizonClient.Fs.DeleteFile(_databasePath); if (result.IsSuccess()) { - result = _horizonClient.Fs.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>()); + result = _horizonClient.Fs.CreateFile(_databasePath, Unsafe.SizeOf<NintendoFigurineDatabase>()); if (result.IsSuccess()) { - result = _horizonClient.Fs.OpenFile(out handle, DatabasePath, OpenMode.Write); + result = _horizonClient.Fs.OpenFile(out handle, _databasePath, OpenMode.Write); } } @@ -259,7 +272,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii { _isDirty = false; - result = _horizonClient.Fs.Commit(MountName); + result = _horizonClient.Fs.Commit(_mountName); } return result; diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs index 4a4c0c239..8611d5af1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs @@ -2,29 +2,29 @@ { public enum ResultCode { - ModuleId = 126, + ModuleId = 126, ErrorCodeShift = 9, Success = 0, - InvalidArgument = (1 << ErrorCodeShift) | ModuleId, - BufferTooSmall = (2 << ErrorCodeShift) | ModuleId, - NotUpdated = (3 << ErrorCodeShift) | ModuleId, - NotFound = (4 << ErrorCodeShift) | ModuleId, - DatabaseFull = (5 << ErrorCodeShift) | ModuleId, - InvalidDatabaseSignatureValue = (67 << ErrorCodeShift) | ModuleId, - InvalidDatabaseEntryCount = (69 << ErrorCodeShift) | ModuleId, - InvalidCharInfo = (100 << ErrorCodeShift) | ModuleId, - InvalidCrc = (101 << ErrorCodeShift) | ModuleId, - InvalidDeviceCrc = (102 << ErrorCodeShift) | ModuleId, - InvalidDatabaseMagic = (103 << ErrorCodeShift) | ModuleId, - InvalidDatabaseVersion = (104 << ErrorCodeShift) | ModuleId, - InvalidDatabaseSize = (105 << ErrorCodeShift) | ModuleId, - InvalidCreateId = (106 << ErrorCodeShift) | ModuleId, - InvalidCoreData = (108 << ErrorCodeShift) | ModuleId, - InvalidStoreData = (109 << ErrorCodeShift) | ModuleId, - InvalidOperationOnSpecialMii = (202 << ErrorCodeShift) | ModuleId, - PermissionDenied = (203 << ErrorCodeShift) | ModuleId, - TestModeNotEnabled = (204 << ErrorCodeShift) | ModuleId + InvalidArgument = (1 << ErrorCodeShift) | ModuleId, + BufferTooSmall = (2 << ErrorCodeShift) | ModuleId, + NotUpdated = (3 << ErrorCodeShift) | ModuleId, + NotFound = (4 << ErrorCodeShift) | ModuleId, + DatabaseFull = (5 << ErrorCodeShift) | ModuleId, + InvalidDatabaseSignatureValue = (67 << ErrorCodeShift) | ModuleId, + InvalidDatabaseEntryCount = (69 << ErrorCodeShift) | ModuleId, + InvalidCharInfo = (100 << ErrorCodeShift) | ModuleId, + InvalidCrc = (101 << ErrorCodeShift) | ModuleId, + InvalidDeviceCrc = (102 << ErrorCodeShift) | ModuleId, + InvalidDatabaseMagic = (103 << ErrorCodeShift) | ModuleId, + InvalidDatabaseVersion = (104 << ErrorCodeShift) | ModuleId, + InvalidDatabaseSize = (105 << ErrorCodeShift) | ModuleId, + InvalidCreateId = (106 << ErrorCodeShift) | ModuleId, + InvalidCoreData = (108 << ErrorCodeShift) | ModuleId, + InvalidStoreData = (109 << ErrorCodeShift) | ModuleId, + InvalidOperationOnSpecialMii = (202 << ErrorCodeShift) | ModuleId, + PermissionDenied = (203 << ErrorCodeShift) | ModuleId, + TestModeNotEnabled = (204 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs b/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs index 4b5ed0d0c..0d286e1cc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService { class DatabaseServiceImpl : IDatabaseService { - private DatabaseImpl _database; - private DatabaseSessionMetadata _metadata; - private bool _isSystem; + private readonly DatabaseImpl _database; + private readonly DatabaseSessionMetadata _metadata; + private readonly bool _isSystem; public DatabaseServiceImpl(DatabaseImpl database, bool isSystem, SpecialMiiKeyCode miiKeyCode) { diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs b/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs index e95364be8..051921f50 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService ResultCode result = Get(flag, out int count, elementsSpan); - elementsSpan = elementsSpan.Slice(0, count); + elementsSpan = elementsSpan[..count]; context.ResponseData.Write(count); @@ -72,7 +72,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService ResultCode result = Get1(flag, out int count, elementsSpan); - elementsSpan = elementsSpan.Slice(0, count); + elementsSpan = elementsSpan[..count]; context.ResponseData.Write(count); @@ -85,8 +85,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService // UpdateLatest(nn::mii::CharInfo old_char_info, SourceFlag flag) -> nn::mii::CharInfo public ResultCode UpdateLatest(ServiceCtx context) { - CharInfo oldCharInfo = context.RequestData.ReadStruct<CharInfo>(); - SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32(); + CharInfo oldCharInfo = context.RequestData.ReadStruct<CharInfo>(); + SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32(); ResultCode result = UpdateLatest(oldCharInfo, flag, out CharInfo newCharInfo); @@ -99,9 +99,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService // BuildRandom(Age age, Gender gender, Race race) -> nn::mii::CharInfo public ResultCode BuildRandom(ServiceCtx context) { - Age age = (Age)context.RequestData.ReadInt32(); + Age age = (Age)context.RequestData.ReadInt32(); Gender gender = (Gender)context.RequestData.ReadInt32(); - Race race = (Race)context.RequestData.ReadInt32(); + Race race = (Race)context.RequestData.ReadInt32(); ResultCode result = BuildRandom(age, gender, race, out CharInfo charInfo); @@ -135,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService ResultCode result = Get2(flag, out int count, elementsSpan); - elementsSpan = elementsSpan.Slice(0, count); + elementsSpan = elementsSpan[..count]; context.ResponseData.Write(count); @@ -156,7 +156,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService ResultCode result = Get3(flag, out int count, elementsSpan); - elementsSpan = elementsSpan.Slice(0, count); + elementsSpan = elementsSpan[..count]; context.ResponseData.Write(count); @@ -169,8 +169,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService // UpdateLatest1(nn::mii::StoreData old_store_data, SourceFlag flag) -> nn::mii::StoreData public ResultCode UpdateLatest1(ServiceCtx context) { - StoreData oldStoreData = context.RequestData.ReadStruct<StoreData>(); - SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32(); + StoreData oldStoreData = context.RequestData.ReadStruct<StoreData>(); + SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32(); ResultCode result = UpdateLatest1(oldStoreData, flag, out StoreData newStoreData); @@ -183,8 +183,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService // FindIndex(nn::mii::CreateId create_id, bool is_special) -> s32 public ResultCode FindIndex(ServiceCtx context) { - CreateId createId = context.RequestData.ReadStruct<CreateId>(); - bool isSpecial = context.RequestData.ReadBoolean(); + CreateId createId = context.RequestData.ReadStruct<CreateId>(); + bool isSpecial = context.RequestData.ReadBoolean(); ResultCode result = FindIndex(createId, isSpecial, out int index); @@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService public ResultCode Move(ServiceCtx context) { CreateId createId = context.RequestData.ReadStruct<CreateId>(); - int newIndex = context.RequestData.ReadInt32(); + int newIndex = context.RequestData.ReadInt32(); return Move(createId, newIndex); } @@ -358,12 +358,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService return new Span<byte>(rawData); } - private Span<T> CreateSpanFromBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) where T: unmanaged + private Span<T> CreateSpanFromBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) where T : unmanaged { return MemoryMarshal.Cast<byte, T>(CreateByteSpanFromBuffer(context, ipcBuff, isOutput)); } - private void WriteSpanToBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, Span<T> span) where T: unmanaged + private void WriteSpanToBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, Span<T> span) where T : unmanaged { Span<byte> rawData = MemoryMarshal.Cast<T, byte>(span); diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs index 7beb6ec06..a443ad09a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs @@ -5,6 +5,6 @@ Young, Normal, Old, - All + All, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs index a028b9be4..85fda6713 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs @@ -9,7 +9,7 @@ LionsMane, Full, - Min = 0, - Max = 5 + Min = None, + Max = Full, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs index 256ec9e06..14b616870 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs @@ -59,11 +59,11 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public byte MoleY; public byte Reserved; - byte IStoredData<CharInfo>.Type => Type; + readonly byte IStoredData<CharInfo>.Type => Type; - CreateId IStoredData<CharInfo>.CreateId => CreateId; + readonly CreateId IStoredData<CharInfo>.CreateId => CreateId; - public ResultCode InvalidData => ResultCode.InvalidCharInfo; + public readonly ResultCode InvalidData => ResultCode.InvalidCharInfo; public bool IsValid() { @@ -72,118 +72,271 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public uint Verify() { - if (!CreateId.IsValid) return 50; - if (!Nickname.IsValid()) return 51; - if ((byte)FontRegion > 3) return 23; - if (FavoriteColor > 11) return 22; - if (Gender > Gender.Max) return 24; - if ((sbyte)Height < 0) return 32; - if ((sbyte)Build < 0) return 3; - if (Type > 1) return 53; - if (RegionMove > 3) return 49; - if (FacelineType > FacelineType.Max) return 21; - if (FacelineColor > FacelineColor.Max) return 18; - if (FacelineWrinkle > FacelineWrinkle.Max) return 20; - if (FacelineMake > FacelineMake.Max) return 19; - if (HairType > HairType.Max) return 31; - if (HairColor > CommonColor.Max) return 29; - if (HairFlip > HairFlip.Max) return 30; - if (EyeType > EyeType.Max) return 8; - if (EyeColor > CommonColor.Max) return 5; - if (EyeScale > 7) return 7; - if (EyeAspect > 6) return 4; - if (EyeRotate > 7) return 6; - if (EyeX > 12) return 9; - if (EyeY > 18) return 10; - if (EyebrowType > EyebrowType.Max) return 15; - if (EyebrowColor > CommonColor.Max) return 12; - if (EyebrowScale > 8) return 14; - if (EyebrowAspect > 6) return 11; - if (EyebrowRotate > 11) return 13; - if (EyebrowX > 12) return 16; - if (EyebrowY - 3 > 15) return 17; - if (NoseType > NoseType.Max) return 47; - if (NoseScale > 8) return 46; - if (NoseY> 18) return 48; - if (MouthType > MouthType.Max) return 40; - if (MouthColor > CommonColor.Max) return 38; - if (MouthScale > 8) return 39; - if (MouthAspect > 6) return 37; - if (MouthY > 18) return 41; - if (BeardColor > CommonColor.Max) return 1; - if (BeardType > BeardType.Max) return 2; - if (MustacheType > MustacheType.Max) return 43; - if (MustacheScale > 8) return 42; - if (MustacheY > 16) return 44; - if (GlassType > GlassType.Max) return 27; - if (GlassColor > CommonColor.Max) return 25; - if (GlassScale > 7) return 26; - if (GlassY > 20) return 28; - if (MoleType > MoleType.Max) return 34; - if (MoleScale > 8) return 33; - if (MoleX > 16) return 35; - if (MoleY >= 31) return 36; + if (!CreateId.IsValid) + { + return 50; + } + if (!Nickname.IsValid()) + { + return 51; + } + if ((byte)FontRegion > 3) + { + return 23; + } + if (FavoriteColor > 11) + { + return 22; + } + if (Gender > Gender.Max) + { + return 24; + } + if ((sbyte)Height < 0) + { + return 32; + } + if ((sbyte)Build < 0) + { + return 3; + } + if (Type > 1) + { + return 53; + } + if (RegionMove > 3) + { + return 49; + } + if (FacelineType > FacelineType.Max) + { + return 21; + } + if (FacelineColor > FacelineColor.Max) + { + return 18; + } + if (FacelineWrinkle > FacelineWrinkle.Max) + { + return 20; + } + if (FacelineMake > FacelineMake.Max) + { + return 19; + } + if (HairType > HairType.Max) + { + return 31; + } + if (HairColor > CommonColor.Max) + { + return 29; + } + if (HairFlip > HairFlip.Max) + { + return 30; + } + if (EyeType > EyeType.Max) + { + return 8; + } + if (EyeColor > CommonColor.Max) + { + return 5; + } + if (EyeScale > 7) + { + return 7; + } + if (EyeAspect > 6) + { + return 4; + } + if (EyeRotate > 7) + { + return 6; + } + if (EyeX > 12) + { + return 9; + } + if (EyeY > 18) + { + return 10; + } + if (EyebrowType > EyebrowType.Max) + { + return 15; + } + if (EyebrowColor > CommonColor.Max) + { + return 12; + } + if (EyebrowScale > 8) + { + return 14; + } + if (EyebrowAspect > 6) + { + return 11; + } + if (EyebrowRotate > 11) + { + return 13; + } + if (EyebrowX > 12) + { + return 16; + } + if (EyebrowY - 3 > 15) + { + return 17; + } + if (NoseType > NoseType.Max) + { + return 47; + } + if (NoseScale > 8) + { + return 46; + } + if (NoseY > 18) + { + return 48; + } + if (MouthType > MouthType.Max) + { + return 40; + } + if (MouthColor > CommonColor.Max) + { + return 38; + } + if (MouthScale > 8) + { + return 39; + } + if (MouthAspect > 6) + { + return 37; + } + if (MouthY > 18) + { + return 41; + } + if (BeardColor > CommonColor.Max) + { + return 1; + } + if (BeardType > BeardType.Max) + { + return 2; + } + if (MustacheType > MustacheType.Max) + { + return 43; + } + if (MustacheScale > 8) + { + return 42; + } + if (MustacheY > 16) + { + return 44; + } + if (GlassType > GlassType.Max) + { + return 27; + } + if (GlassColor > CommonColor.Max) + { + return 25; + } + if (GlassScale > 7) + { + return 26; + } + if (GlassY > 20) + { + return 28; + } + if (MoleType > MoleType.Max) + { + return 34; + } + if (MoleScale > 8) + { + return 33; + } + if (MoleX > 16) + { + return 35; + } + if (MoleY >= 31) + { + return 36; + } return 0; } public void SetFromStoreData(StoreData storeData) { - Nickname = storeData.CoreData.Nickname; - CreateId = storeData.CreateId; - FontRegion = storeData.CoreData.FontRegion; - FavoriteColor = storeData.CoreData.FavoriteColor; - Gender = storeData.CoreData.Gender; - Height = storeData.CoreData.Height; - Build = storeData.CoreData.Build; - Type = storeData.CoreData.Type; - RegionMove = storeData.CoreData.RegionMove; - FacelineType = storeData.CoreData.FacelineType; - FacelineColor = storeData.CoreData.FacelineColor; + Nickname = storeData.CoreData.Nickname; + CreateId = storeData.CreateId; + FontRegion = storeData.CoreData.FontRegion; + FavoriteColor = storeData.CoreData.FavoriteColor; + Gender = storeData.CoreData.Gender; + Height = storeData.CoreData.Height; + Build = storeData.CoreData.Build; + Type = storeData.CoreData.Type; + RegionMove = storeData.CoreData.RegionMove; + FacelineType = storeData.CoreData.FacelineType; + FacelineColor = storeData.CoreData.FacelineColor; FacelineWrinkle = storeData.CoreData.FacelineWrinkle; - FacelineMake = storeData.CoreData.FacelineMake; - HairType = storeData.CoreData.HairType; - HairColor = storeData.CoreData.HairColor; - HairFlip = storeData.CoreData.HairFlip; - EyeType = storeData.CoreData.EyeType; - EyeColor = storeData.CoreData.EyeColor; - EyeScale = storeData.CoreData.EyeScale; - EyeAspect = storeData.CoreData.EyeAspect; - EyeRotate = storeData.CoreData.EyeRotate; - EyeX = storeData.CoreData.EyeX; - EyeY = storeData.CoreData.EyeY; - EyebrowType = storeData.CoreData.EyebrowType; - EyebrowColor = storeData.CoreData.EyebrowColor; - EyebrowScale = storeData.CoreData.EyebrowScale; - EyebrowAspect = storeData.CoreData.EyebrowAspect; - EyebrowRotate = storeData.CoreData.EyebrowRotate; - EyebrowX = storeData.CoreData.EyebrowX; - EyebrowY = storeData.CoreData.EyebrowY; - NoseType = storeData.CoreData.NoseType; - NoseScale = storeData.CoreData.NoseScale; - NoseY = storeData.CoreData.NoseY; - MouthType = storeData.CoreData.MouthType; - MouthColor = storeData.CoreData.MouthColor; - MouthScale = storeData.CoreData.MouthScale; - MouthAspect = storeData.CoreData.MouthAspect; - MouthY = storeData.CoreData.MouthY; - BeardColor = storeData.CoreData.BeardColor; - BeardType = storeData.CoreData.BeardType; - MustacheType = storeData.CoreData.MustacheType; - MustacheScale = storeData.CoreData.MustacheScale; - MustacheY = storeData.CoreData.MustacheY; - GlassType = storeData.CoreData.GlassType; - GlassColor = storeData.CoreData.GlassColor; - GlassScale = storeData.CoreData.GlassScale; - GlassY = storeData.CoreData.GlassY; - MoleType = storeData.CoreData.MoleType; - MoleScale = storeData.CoreData.MoleScale; - MoleX = storeData.CoreData.MoleX; - MoleY = storeData.CoreData.MoleY; - Reserved = 0; + FacelineMake = storeData.CoreData.FacelineMake; + HairType = storeData.CoreData.HairType; + HairColor = storeData.CoreData.HairColor; + HairFlip = storeData.CoreData.HairFlip; + EyeType = storeData.CoreData.EyeType; + EyeColor = storeData.CoreData.EyeColor; + EyeScale = storeData.CoreData.EyeScale; + EyeAspect = storeData.CoreData.EyeAspect; + EyeRotate = storeData.CoreData.EyeRotate; + EyeX = storeData.CoreData.EyeX; + EyeY = storeData.CoreData.EyeY; + EyebrowType = storeData.CoreData.EyebrowType; + EyebrowColor = storeData.CoreData.EyebrowColor; + EyebrowScale = storeData.CoreData.EyebrowScale; + EyebrowAspect = storeData.CoreData.EyebrowAspect; + EyebrowRotate = storeData.CoreData.EyebrowRotate; + EyebrowX = storeData.CoreData.EyebrowX; + EyebrowY = storeData.CoreData.EyebrowY; + NoseType = storeData.CoreData.NoseType; + NoseScale = storeData.CoreData.NoseScale; + NoseY = storeData.CoreData.NoseY; + MouthType = storeData.CoreData.MouthType; + MouthColor = storeData.CoreData.MouthColor; + MouthScale = storeData.CoreData.MouthScale; + MouthAspect = storeData.CoreData.MouthAspect; + MouthY = storeData.CoreData.MouthY; + BeardColor = storeData.CoreData.BeardColor; + BeardType = storeData.CoreData.BeardType; + MustacheType = storeData.CoreData.MustacheType; + MustacheScale = storeData.CoreData.MustacheScale; + MustacheY = storeData.CoreData.MustacheY; + GlassType = storeData.CoreData.GlassType; + GlassColor = storeData.CoreData.GlassColor; + GlassScale = storeData.CoreData.GlassScale; + GlassY = storeData.CoreData.GlassY; + MoleType = storeData.CoreData.MoleType; + MoleScale = storeData.CoreData.MoleScale; + MoleX = storeData.CoreData.MoleX; + MoleY = storeData.CoreData.MoleY; + Reserved = 0; } - public void SetSource(Source source) + public readonly void SetSource(Source source) { // Only implemented for Element variants. } @@ -198,12 +351,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types return !x.Equals(y); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is CharInfo charInfo && Equals(charInfo); } - public bool Equals(CharInfo cmpObj) + public readonly bool Equals(CharInfo cmpObj) { if (!cmpObj.IsValid()) { @@ -267,9 +420,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types return result; } - public override int GetHashCode() + public readonly override int GetHashCode() { - HashCode hashCode = new HashCode(); + HashCode hashCode = new(); hashCode.Add(Nickname); hashCode.Add(CreateId); diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs index f1f850fd2..fead4861a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types struct CharInfoElement : IElement { public CharInfo CharInfo; - public Source Source; + public Source Source; public void SetFromStoreData(StoreData storeData) { diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs index 8b613850a..3f61232ce 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs @@ -4,6 +4,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types enum CommonColor : byte { Min = 0, - Max = 99 + Max = 99, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs index abf18e36e..00e49ecb9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public void SetDefault() { - Storage.Fill(0); + Storage.Clear(); Nickname = Nickname.Default; } @@ -374,18 +374,18 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public Span<byte> GetNicknameStorage() { - return Storage.Slice(0x1c); + return Storage[0x1c..]; } public Nickname Nickname { get => Nickname.FromBytes(GetNicknameStorage()); - set => value.Raw.Slice(0, 20).CopyTo(GetNicknameStorage()); + set => value.Raw[..20].CopyTo(GetNicknameStorage()); } public static CoreData BuildRandom(UtilityImpl utilImpl, Age age, Gender gender, Race race) { - CoreData coreData = new CoreData(); + CoreData coreData = new(); coreData.SetDefault(); @@ -439,29 +439,29 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types int indexFor4 = 3 * (int)age + 9 * (int)gender + (int)race; - var facelineTypeInfo = RandomMiiFacelineArray[indexFor4]; - var facelineColorInfo = RandomMiiFacelineColorArray[3 * (int)gender + (int)race]; + var facelineTypeInfo = RandomMiiFacelineArray[indexFor4]; + var facelineColorInfo = RandomMiiFacelineColorArray[3 * (int)gender + (int)race]; var facelineWrinkleInfo = RandomMiiFacelineWrinkleArray[indexFor4]; - var facelineMakeInfo = RandomMiiFacelineMakeArray[indexFor4]; - var hairTypeInfo = RandomMiiHairTypeArray[indexFor4]; - var hairColorInfo = RandomMiiHairColorArray[3 * (int)race + (int)age]; - var eyeTypeInfo = RandomMiiEyeTypeArray[indexFor4]; - var eyeColorInfo = RandomMiiEyeColorArray[(int)race]; - var eyebrowTypeInfo = RandomMiiEyebrowTypeArray[indexFor4]; - var noseTypeInfo = RandomMiiNoseTypeArray[indexFor4]; - var mouthTypeInfo = RandomMiiMouthTypeArray[indexFor4]; - var glassTypeInfo = RandomMiiGlassTypeArray[(int)age]; + var facelineMakeInfo = RandomMiiFacelineMakeArray[indexFor4]; + var hairTypeInfo = RandomMiiHairTypeArray[indexFor4]; + var hairColorInfo = RandomMiiHairColorArray[3 * (int)race + (int)age]; + var eyeTypeInfo = RandomMiiEyeTypeArray[indexFor4]; + var eyeColorInfo = RandomMiiEyeColorArray[(int)race]; + var eyebrowTypeInfo = RandomMiiEyebrowTypeArray[indexFor4]; + var noseTypeInfo = RandomMiiNoseTypeArray[indexFor4]; + var mouthTypeInfo = RandomMiiMouthTypeArray[indexFor4]; + var glassTypeInfo = RandomMiiGlassTypeArray[(int)age]; // Faceline - coreData.FacelineType = (FacelineType)facelineTypeInfo.Values[utilImpl.GetRandom(facelineTypeInfo.ValuesCount)]; - coreData.FacelineColor = (FacelineColor)Helper.Ver3FacelineColorTable[facelineColorInfo.Values[utilImpl.GetRandom(facelineColorInfo.ValuesCount)]]; + coreData.FacelineType = (FacelineType)facelineTypeInfo.Values[utilImpl.GetRandom(facelineTypeInfo.ValuesCount)]; + coreData.FacelineColor = (FacelineColor)Helper.Ver3FacelineColorTable[facelineColorInfo.Values[utilImpl.GetRandom(facelineColorInfo.ValuesCount)]]; coreData.FacelineWrinkle = (FacelineWrinkle)facelineWrinkleInfo.Values[utilImpl.GetRandom(facelineWrinkleInfo.ValuesCount)]; - coreData.FacelineMake = (FacelineMake)facelineMakeInfo.Values[utilImpl.GetRandom(facelineMakeInfo.ValuesCount)]; + coreData.FacelineMake = (FacelineMake)facelineMakeInfo.Values[utilImpl.GetRandom(facelineMakeInfo.ValuesCount)]; // Hair - coreData.HairType = (HairType)hairTypeInfo.Values[utilImpl.GetRandom(hairTypeInfo.ValuesCount)]; + coreData.HairType = (HairType)hairTypeInfo.Values[utilImpl.GetRandom(hairTypeInfo.ValuesCount)]; coreData.HairColor = (CommonColor)Helper.Ver3HairColorTable[hairColorInfo.Values[utilImpl.GetRandom(hairColorInfo.ValuesCount)]]; - coreData.HairFlip = (HairFlip)utilImpl.GetRandom((int)HairFlip.Max + 1); + coreData.HairFlip = (HairFlip)utilImpl.GetRandom((int)HairFlip.Max + 1); // Eye coreData.EyeType = (EyeType)eyeTypeInfo.Values[utilImpl.GetRandom(eyeTypeInfo.ValuesCount)]; @@ -470,56 +470,56 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types int eyeRotateKey2 = gender != Gender.Male ? 3 : 4; byte eyeRotateOffset = (byte)(32 - EyeRotateTable[eyeRotateKey1] + eyeRotateKey2); - byte eyeRotate = (byte)(32 - EyeRotateTable[(int)coreData.EyeType]); + byte eyeRotate = (byte)(32 - EyeRotateTable[(int)coreData.EyeType]); - coreData.EyeColor = (CommonColor)Helper.Ver3EyeColorTable[eyeColorInfo.Values[utilImpl.GetRandom(eyeColorInfo.ValuesCount)]]; - coreData.EyeScale = 4; + coreData.EyeColor = (CommonColor)Helper.Ver3EyeColorTable[eyeColorInfo.Values[utilImpl.GetRandom(eyeColorInfo.ValuesCount)]]; + coreData.EyeScale = 4; coreData.EyeAspect = 3; coreData.EyeRotate = (byte)(eyeRotateOffset - eyeRotate); - coreData.EyeX = 2; - coreData.EyeY = (byte)(axisY + 12); + coreData.EyeX = 2; + coreData.EyeY = (byte)(axisY + 12); // Eyebrow coreData.EyebrowType = (EyebrowType)eyebrowTypeInfo.Values[utilImpl.GetRandom(eyebrowTypeInfo.ValuesCount)]; int eyebrowRotateKey = race == Race.Asian ? 6 : 0; - int eyebrowY = race == Race.Asian ? 9 : 10; + int eyebrowY = race == Race.Asian ? 9 : 10; byte eyebrowRotateOffset = (byte)(32 - EyebrowRotateTable[eyebrowRotateKey] + 6); - byte eyebrowRotate = (byte)(32 - EyebrowRotateTable[(int)coreData.EyebrowType]); + byte eyebrowRotate = (byte)(32 - EyebrowRotateTable[(int)coreData.EyebrowType]); - coreData.EyebrowColor = coreData.HairColor; - coreData.EyebrowScale = 4; + coreData.EyebrowColor = coreData.HairColor; + coreData.EyebrowScale = 4; coreData.EyebrowAspect = 3; coreData.EyebrowRotate = (byte)(eyebrowRotateOffset - eyebrowRotate); - coreData.EyebrowX = 2; - coreData.EyebrowY = (byte)(axisY + eyebrowY); + coreData.EyebrowX = 2; + coreData.EyebrowY = (byte)(axisY + eyebrowY); // Nose int noseScale = gender == Gender.Female ? 3 : 4; - coreData.NoseType = (NoseType)noseTypeInfo.Values[utilImpl.GetRandom(noseTypeInfo.ValuesCount)]; + coreData.NoseType = (NoseType)noseTypeInfo.Values[utilImpl.GetRandom(noseTypeInfo.ValuesCount)]; coreData.NoseScale = (byte)noseScale; - coreData.NoseY = (byte)(axisY + 9); + coreData.NoseY = (byte)(axisY + 9); // Mouth int mouthColor = gender == Gender.Female ? utilImpl.GetRandom(0, 4) : 0; - coreData.MouthType = (MouthType)mouthTypeInfo.Values[utilImpl.GetRandom(mouthTypeInfo.ValuesCount)]; - coreData.MouthColor = (CommonColor)Helper.Ver3MouthColorTable[mouthColor]; - coreData.MouthScale = 4; + coreData.MouthType = (MouthType)mouthTypeInfo.Values[utilImpl.GetRandom(mouthTypeInfo.ValuesCount)]; + coreData.MouthColor = (CommonColor)Helper.Ver3MouthColorTable[mouthColor]; + coreData.MouthScale = 4; coreData.MouthAspect = 3; - coreData.MouthY = (byte)(axisY + 13); + coreData.MouthY = (byte)(axisY + 13); // Beard & Mustache - coreData.BeardColor = coreData.HairColor; + coreData.BeardColor = coreData.HairColor; coreData.MustacheScale = 4; if (gender == Gender.Male && age != Age.Young && utilImpl.GetRandom(10) < 2) { BeardAndMustacheFlag mustacheAndBeardFlag = (BeardAndMustacheFlag)utilImpl.GetRandom(3); - BeardType beardType = BeardType.None; + BeardType beardType = BeardType.None; MustacheType mustacheType = MustacheType.None; if ((mustacheAndBeardFlag & BeardAndMustacheFlag.Beard) == BeardAndMustacheFlag.Beard) @@ -533,14 +533,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types } coreData.MustacheType = mustacheType; - coreData.BeardType = beardType; - coreData.MustacheY = 10; + coreData.BeardType = beardType; + coreData.MustacheY = 10; } else { coreData.MustacheType = MustacheType.None; - coreData.BeardType = BeardType.None; - coreData.MustacheY = (byte)(axisY + 10); + coreData.BeardType = BeardType.None; + coreData.MustacheY = (byte)(axisY + 10); } // Glass @@ -557,84 +557,84 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types } } - coreData.GlassType = glassType; + coreData.GlassType = glassType; coreData.GlassColor = (CommonColor)Helper.Ver3GlassColorTable[0]; coreData.GlassScale = 4; - coreData.GlassY = (byte)(axisY + 10); + coreData.GlassY = (byte)(axisY + 10); // Mole - coreData.MoleType = 0; + coreData.MoleType = 0; coreData.MoleScale = 4; - coreData.MoleX = 2; - coreData.MoleY = 20; + coreData.MoleX = 2; + coreData.MoleY = 20; // Body sizing coreData.Height = 64; - coreData.Build = 64; + coreData.Build = 64; // Misc - coreData.Nickname = Nickname.Default; - coreData.Gender = gender; + coreData.Nickname = Nickname.Default; + coreData.Gender = gender; coreData.FavoriteColor = (byte)utilImpl.GetRandom(0, 11); - coreData.RegionMove = 0; - coreData.FontRegion = 0; - coreData.Type = 0; + coreData.RegionMove = 0; + coreData.FontRegion = 0; + coreData.Type = 0; return coreData; } public void SetFromCharInfo(CharInfo charInfo) { - Nickname = charInfo.Nickname; - FontRegion = charInfo.FontRegion; - FavoriteColor = charInfo.FavoriteColor; - Gender = charInfo.Gender; - Height = charInfo.Height; - Build = charInfo.Build; - Type = charInfo.Type; - RegionMove = charInfo.RegionMove; - FacelineType = charInfo.FacelineType; - FacelineColor = charInfo.FacelineColor; + Nickname = charInfo.Nickname; + FontRegion = charInfo.FontRegion; + FavoriteColor = charInfo.FavoriteColor; + Gender = charInfo.Gender; + Height = charInfo.Height; + Build = charInfo.Build; + Type = charInfo.Type; + RegionMove = charInfo.RegionMove; + FacelineType = charInfo.FacelineType; + FacelineColor = charInfo.FacelineColor; FacelineWrinkle = charInfo.FacelineWrinkle; - FacelineMake = charInfo.FacelineMake; - HairType = charInfo.HairType; - HairColor = charInfo.HairColor; - HairFlip = charInfo.HairFlip; - EyeType = charInfo.EyeType; - EyeColor = charInfo.EyeColor; - EyeScale = charInfo.EyeScale; - EyeAspect = charInfo.EyeAspect; - EyeRotate = charInfo.EyeRotate; - EyeX = charInfo.EyeX; - EyeY = charInfo.EyeY; - EyebrowType = charInfo.EyebrowType; - EyebrowColor = charInfo.EyebrowColor; - EyebrowScale = charInfo.EyebrowScale; - EyebrowAspect = charInfo.EyebrowAspect; - EyebrowRotate = charInfo.EyebrowRotate; - EyebrowX = charInfo.EyebrowX; - EyebrowY = charInfo.EyebrowY; - NoseType = charInfo.NoseType; - NoseScale = charInfo.NoseScale; - NoseY = charInfo.NoseY; - MouthType = charInfo.MouthType; - MouthColor = charInfo.MouthColor; - MouthScale = charInfo.MouthScale; - MouthAspect = charInfo.MouthAspect; - MouthY = charInfo.MouthY; - BeardColor = charInfo.BeardColor; - BeardType = charInfo.BeardType; - MustacheType = charInfo.MustacheType; - MustacheScale = charInfo.MustacheScale; - MustacheY = charInfo.MustacheY; - GlassType = charInfo.GlassType; - GlassColor = charInfo.GlassColor; - GlassScale = charInfo.GlassScale; - GlassY = charInfo.GlassY; - MoleType = charInfo.MoleType; - MoleScale = charInfo.MoleScale; - MoleX = charInfo.MoleX; - MoleY = charInfo.MoleY; + FacelineMake = charInfo.FacelineMake; + HairType = charInfo.HairType; + HairColor = charInfo.HairColor; + HairFlip = charInfo.HairFlip; + EyeType = charInfo.EyeType; + EyeColor = charInfo.EyeColor; + EyeScale = charInfo.EyeScale; + EyeAspect = charInfo.EyeAspect; + EyeRotate = charInfo.EyeRotate; + EyeX = charInfo.EyeX; + EyeY = charInfo.EyeY; + EyebrowType = charInfo.EyebrowType; + EyebrowColor = charInfo.EyebrowColor; + EyebrowScale = charInfo.EyebrowScale; + EyebrowAspect = charInfo.EyebrowAspect; + EyebrowRotate = charInfo.EyebrowRotate; + EyebrowX = charInfo.EyebrowX; + EyebrowY = charInfo.EyebrowY; + NoseType = charInfo.NoseType; + NoseScale = charInfo.NoseScale; + NoseY = charInfo.NoseY; + MouthType = charInfo.MouthType; + MouthColor = charInfo.MouthColor; + MouthScale = charInfo.MouthScale; + MouthAspect = charInfo.MouthAspect; + MouthY = charInfo.MouthY; + BeardColor = charInfo.BeardColor; + BeardType = charInfo.BeardType; + MustacheType = charInfo.MustacheType; + MustacheScale = charInfo.MustacheScale; + MustacheY = charInfo.MustacheY; + GlassType = charInfo.GlassType; + GlassColor = charInfo.GlassColor; + GlassScale = charInfo.GlassScale; + GlassY = charInfo.GlassY; + MoleType = charInfo.MoleType; + MoleScale = charInfo.MoleScale; + MoleX = charInfo.MoleX; + MoleY = charInfo.MoleY; } public static bool operator ==(CoreData x, CoreData y) @@ -717,7 +717,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public override int GetHashCode() { - HashCode hashCode = new HashCode(); + HashCode hashCode = new(); hashCode.Add(Nickname); hashCode.Add(FontRegion); @@ -773,7 +773,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types return hashCode.ToHashCode(); } - private static ReadOnlySpan<ElementInfo> ElementInfos => MemoryMarshal.Cast<byte, ElementInfo>(ElementInfoArray); + private readonly ReadOnlySpan<ElementInfo> ElementInfos => MemoryMarshal.Cast<byte, ElementInfo>(ElementInfoArray); private enum ElementInfoIndex { @@ -825,11 +825,11 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types NoseScale, MouthScale, MustacheScale, - MoleScale + MoleScale, } #region "Element Info Array" - private static ReadOnlySpan<byte> ElementInfoArray => new byte[] + private readonly ReadOnlySpan<byte> ElementInfoArray => new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -904,7 +904,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 + 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, }; #endregion } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs index 283d82fc8..f61e83d73 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs @@ -4,12 +4,12 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Mii.Types { [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] - struct CreateId : IEquatable<CreateId> + readonly struct CreateId : IEquatable<CreateId> { public readonly UInt128 Raw; - public bool IsNull => Raw == UInt128.Zero; - public bool IsValid => !IsNull && ((Raw >> 64) & 0xC0) == 0x80; + public readonly bool IsNull => Raw == UInt128.Zero; + public readonly bool IsValid => !IsNull && ((Raw >> 64) & 0xC0) == 0x80; public CreateId(UInt128 raw) { @@ -26,18 +26,18 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types return !x.Equals(y); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is CreateId createId && Equals(createId); } - public bool Equals(CreateId cmpObj) + public readonly bool Equals(CreateId cmpObj) { // Nintendo additionally check that the CreatorId is valid before doing the actual comparison. return IsValid && Raw == cmpObj.Raw; } - public override int GetHashCode() + public readonly override int GetHashCode() { return Raw.GetHashCode(); } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs index 285a9242d..e3c6a42e6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public Nickname Nickname { get => Nickname.FromBytes(NicknameStorage); - set => value.Raw.Slice(0, 20).CopyTo(NicknameStorage); + set => value.Raw[..20].CopyTo(NicknameStorage); } public static ReadOnlySpan<DefaultMii> Table => MemoryMarshal.Cast<byte, DefaultMii>(TableRawArray); @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types // The first 2 Mii in the default table are used as base for Male/Female in editor but not exposed via IPC. public static int TableLength => _fromIndex.Length; - private static readonly int[] _fromIndex = new int[] { 2, 3, 4, 5, 6, 7 }; + private static readonly int[] _fromIndex = { 2, 3, 4, 5, 6, 7 }; public static DefaultMii GetDefaultMii(uint index) { @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, - 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; #endregion } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs index 2e4502ed7..70b6e31c5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs @@ -63,7 +63,7 @@ RoundTired, WhiteLarge, - Min = 0, - Max = 59 + Min = Normal, + Max = WhiteLarge, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs index af870e10c..3790128a8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs @@ -27,7 +27,7 @@ Dotted, None, - Min = 0, - Max = 23 + Min = FlatAngledLarge, + Max = None, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs index 551f053d1..4571168f1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs @@ -13,7 +13,7 @@ Almond, Espresso, - Min = 0, - Max = 9 + Min = Beige, + Max = Espresso, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs index af6d72764..acd94ca2e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs @@ -15,7 +15,7 @@ LionsManeBeard, StubbleBeard, - Min = 0, - Max = 11 + Min = None, + Max = StubbleBeard, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs index fe27636f4..29d14a126 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs @@ -15,7 +15,7 @@ FlatRounded, AngularSmall, - Min = 0, - Max = 11 + Min = Sharp, + Max = AngularSmall, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs index afb75dd8a..1a144748b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs @@ -15,7 +15,7 @@ CrowsFeet, FoldsCrowsFrown, - Min = 0, - Max = 11 + Min = None, + Max = FoldsCrowsFrown, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs index d1d86f16d..c981a5ede 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs @@ -5,6 +5,6 @@ Standard, China, Korea, - Taiwan + Taiwan, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs index 75f9a745e..c120c7581 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs @@ -6,7 +6,7 @@ Female, All, - Min = 0, - Max = 1 + Min = Male, + Max = Female, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs index ccfed0f60..e2b6e6f5b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs @@ -23,7 +23,7 @@ OpaqueRectangle, OpaqueAviator, - Min = 0, - Max = 19 + Min = None, + Max = OpaqueAviator, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs index 2f7f1d734..dee42e0d1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs @@ -5,7 +5,7 @@ Left, Right, - Min = 0, - Max = 1 + Min = Left, + Max = Right, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs index a8a611dac..a3e183961 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs @@ -135,7 +135,7 @@ TwoLongSidedPonyTails, LongFrontTwoBackPonyTails, - Min = 0, - Max = 131 + Min = NormalLong, + Max = LongFrontTwoBackPonyTails, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs index 12cb6dc3f..7ed274d2a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs @@ -5,7 +5,7 @@ None, OneDot, - Min = 0, - Max = 1 + Min = None, + Max = OneDot, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs index 2a8e7a00b..69236bd42 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs @@ -39,7 +39,7 @@ SmileDownLine, Kiss, - Min = 0, - Max = 35 + Min = Neutral, + Max = Kiss, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs index a15382dd5..7b0255f20 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs @@ -9,7 +9,7 @@ Normal, Toothbrush, - Min = 0, - Max = 5 + Min = None, + Max = Toothbrush, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs index 677d81b00..dc21d8e56 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public bool IsValid() { // Create a new unicode encoding instance with error checking enabled - UnicodeEncoding unicodeEncoding = new UnicodeEncoding(false, false, true); + UnicodeEncoding unicodeEncoding = new(false, false, true); try { @@ -77,10 +77,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types { if (data.Length > SizeConst) { - data = data.Slice(0, SizeConst); + data = data[..SizeConst]; } - Nickname result = new Nickname(); + Nickname result = new(); data.CopyTo(result.Raw); diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs index 14eda2ed8..1e36788e7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs @@ -25,19 +25,19 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types // Set to true to allow fixing database with invalid storedata device crc instead of deleting them. private const bool AcceptInvalidDeviceCrc = true; - public int Length => _figurineCount; + public readonly int Length => _figurineCount; [StructLayout(LayoutKind.Sequential, Size = FigurineArraySize)] private struct FigurineStorageStruct { } private Span<StoreData> Figurines => SpanHelpers.AsSpan<FigurineStorageStruct, StoreData>(ref _figurineStorage); - + public StoreData Get(int index) { return Figurines[index]; } - public bool IsFull() + public readonly bool IsFull() { return Length >= MaxMii; } @@ -74,14 +74,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types if (newIndex < oldIndex) { - targetLength = oldIndex - newIndex; - sourceIndex = newIndex; + targetLength = oldIndex - newIndex; + sourceIndex = newIndex; destinationIndex = newIndex + 1; } else { - targetLength = newIndex - oldIndex; - sourceIndex = oldIndex + 1; + targetLength = newIndex - oldIndex; + sourceIndex = oldIndex + 1; destinationIndex = oldIndex; } @@ -113,8 +113,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types // If this isn't the only element in the list, move the data in it. if (index < newCount) { - int targetLength = newCount - index; - int sourceIndex = index + 1; + int targetLength = newCount - index; + int sourceIndex = index + 1; int destinationIndex = index; Figurines.Slice(sourceIndex, targetLength).CopyTo(Figurines.Slice(destinationIndex, targetLength)); @@ -204,12 +204,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public void Format() { - _magic = DatabaseMagic; - _version = CurrentVersion; + _magic = DatabaseMagic; + _version = CurrentVersion; _figurineCount = 0; // Fill with empty data - Figurines.Fill(new StoreData()); + Figurines.Clear(); UpdateCrc(); } @@ -248,7 +248,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types private ReadOnlySpan<byte> AsSpanWithoutCrc() { - return AsReadOnlySpan().Slice(0, Unsafe.SizeOf<NintendoFigurineDatabase>() - 2); + return AsReadOnlySpan()[..(Unsafe.SizeOf<NintendoFigurineDatabase>() - 2)]; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs index e898a02ed..3144b132a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs @@ -21,7 +21,7 @@ Droopy, ArrowLarge, - Min = 0, - Max = 17 + Min = Normal, + Max = ArrowLarge, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs index 8cf36c277..5f304a4e8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs @@ -5,6 +5,6 @@ Black, White, Asian, - All + All, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs index 6def469db..17333093e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs @@ -6,25 +6,23 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types { static class RandomMiiConstants { - public static int[] EyeRotateTable = new int[] - { + public static int[] EyeRotateTable = { 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04 + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, }; - public static int[] EyebrowRotateTable = new int[] - { + public static int[] EyebrowRotateTable = { 0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06, - 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05 + 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05, }; [Flags] public enum BeardAndMustacheFlag { Beard = 1, - Mustache + Mustache, } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = ValuesArraySize)] @@ -32,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types { private const int ValuesArraySize = 0xbc; - private int _firstValueByte; + private readonly int _firstValueByte; public ReadOnlySpan<int> Values => SpanHelpers.AsSpan<RandomMiiValues, int>(ref this); } @@ -44,34 +42,34 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public int Age; public int Race; public int ValuesCount; - - private RandomMiiValues _values; - public ReadOnlySpan<int> Values => _values.Values.Slice(0, ValuesCount); + private readonly RandomMiiValues _values; + + public readonly ReadOnlySpan<int> Values => _values.Values[..ValuesCount]; } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0xC8)] public struct RandomMiiData3 { - private int _argument1; - private int _argument2; + private readonly int _argument1; + private readonly int _argument2; public int ValuesCount; - - private RandomMiiValues _values; - public ReadOnlySpan<int> Values => _values.Values.Slice(0, ValuesCount); + private readonly RandomMiiValues _values; + + public readonly ReadOnlySpan<int> Values => _values.Values[..ValuesCount]; } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0xC4)] public struct RandomMiiData2 { - private int _argument; + private readonly int _argument; public int ValuesCount; - private RandomMiiValues _values; + private readonly RandomMiiValues _values; - public ReadOnlySpan<int> Values => _values.Values.Slice(0, ValuesCount); + public readonly ReadOnlySpan<int> Values => _values.Values[..ValuesCount]; } public static ReadOnlySpan<RandomMiiData4> RandomMiiFacelineArray => MemoryMarshal.Cast<byte, RandomMiiData4>(RandomMiiFacelineRawArray); @@ -332,7 +330,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + , }; private static ReadOnlySpan<byte> RandomMiiFacelineColorRawArray => new byte[] { @@ -411,7 +409,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + , }; private static ReadOnlySpan<byte> RandomMiiFacelineWrinkleRawArray => new byte[] { @@ -645,7 +643,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + , }; private static ReadOnlySpan<byte> RandomMiiFacelineMakeRawArray => new byte[] { @@ -879,7 +877,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + , }; private static ReadOnlySpan<byte> RandomMiiHairTypeRawArray => new byte[] { @@ -1113,7 +1111,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + , }; private static ReadOnlySpan<byte> RandomMiiHairColorRawArray => new byte[] { @@ -1230,7 +1228,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + , }; private static ReadOnlySpan<byte> RandomMiiEyeTypeRawArray => new byte[] { @@ -1464,7 +1462,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + ,}; private static ReadOnlySpan<byte> RandomMiiEyeColorRawArray => new byte[] { diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs index 2ae02ea03..f4d2b8cb9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs @@ -3,6 +3,6 @@ enum Source { Database, - Default + Default, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs index c9682bdbb..c04eb0437 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types enum SourceFlag { Database = 1 << Source.Database, - Default = 1 << Source.Default, - All = Database | Default + Default = 1 << Source.Default, + All = Database | Default, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs index 7fe13238d..be2433ddf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public uint RawValue; - public bool IsEnabledSpecialMii() + public readonly bool IsEnabledSpecialMii() { return RawValue == SpecialMiiMagic; } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs index 8411693f4..994f6b7ce 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs @@ -9,16 +9,16 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types { public const int Size = 0x44; - public CoreData CoreData; + public CoreData CoreData; private CreateId _createId; - public ushort DataCrc; - public ushort DeviceCrc; + public ushort DataCrc; + public ushort DeviceCrc; public byte Type => CoreData.Type; - public CreateId CreateId => _createId; + public readonly CreateId CreateId => _createId; - public ResultCode InvalidData => ResultCode.InvalidStoreData; + public readonly ResultCode InvalidData => ResultCode.InvalidStoreData; private void UpdateDataCrc() { @@ -81,22 +81,23 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types private ReadOnlySpan<byte> AsSpanWithoutDeviceCrc() { - return AsSpan().Slice(0, Size - 2); + return AsSpan()[..(Size - 2)]; } public static StoreData BuildDefault(UtilityImpl utilImpl, uint index) { - StoreData result = new StoreData + StoreData result = new() { - _createId = utilImpl.MakeCreateId() + _createId = utilImpl.MakeCreateId(), }; - CoreData coreData = new CoreData(); + CoreData coreData = new(); DefaultMii template = DefaultMii.GetDefaultMii(index); coreData.SetDefault(); +#pragma warning disable IDE0055 // Disable formatting coreData.Nickname = template.Nickname; coreData.FontRegion = (FontRegion)template.FontRegion; coreData.FavoriteColor = (byte)template.FavoriteColor; @@ -147,6 +148,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types coreData.MoleScale = (byte)template.MoleScale; coreData.MoleX = (byte)template.MoleX; coreData.MoleY = (byte)template.MoleY; +#pragma warning restore IDE0055 result.CoreData = coreData; @@ -162,10 +164,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types public static StoreData BuildFromCoreData(UtilityImpl utilImpl, CoreData coreData) { - StoreData result = new StoreData + StoreData result = new() { - CoreData = coreData, - _createId = utilImpl.MakeCreateId() + CoreData = coreData, + _createId = utilImpl.MakeCreateId(), }; result.UpdateCrc(); @@ -178,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types this = storeData; } - public void SetSource(Source source) + public readonly void SetSource(Source source) { // Only implemented for Element variants. } @@ -193,12 +195,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types return !x.Equals(y); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is StoreData storeData && Equals(storeData); } - public bool Equals(StoreData cmpObj) + public readonly bool Equals(StoreData cmpObj) { if (!cmpObj.IsValid()) { @@ -215,9 +217,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types return result; } - public override int GetHashCode() + public readonly override int GetHashCode() { - HashCode hashCode = new HashCode(); + HashCode hashCode = new(); hashCode.Add(CreateId); hashCode.Add(CoreData); diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs index 8d3e96bea..2008c1ffa 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types struct StoreDataElement : IElement { public StoreData StoreData; - public Source Source; + public Source Source; public void SetFromStoreData(StoreData storeData) { diff --git a/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index fac425553..31d325a94 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -7,10 +7,10 @@ namespace Ryujinx.HLE.HOS.Services.Mm [Service("mm:u")] class IRequest : IpcService { - private static object _sessionListLock = new object(); - private static List<MultiMediaSession> _sessionList = new List<MultiMediaSession>(); + private readonly object _sessionListLock = new(); + private readonly List<MultiMediaSession> _sessionList = new(); - private static uint _uniqueId = 1; + private uint _uniqueId = 1; public IRequest(ServiceCtx context) { } @@ -18,9 +18,9 @@ namespace Ryujinx.HLE.HOS.Services.Mm // InitializeOld(u32, u32, u32) public ResultCode InitializeOld(ServiceCtx context) { - MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32(); - int fgmId = context.RequestData.ReadInt32(); - bool isAutoClearEvent = context.RequestData.ReadInt32() != 0; + MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32(); + int fgmId = context.RequestData.ReadInt32(); + bool isAutoClearEvent = context.RequestData.ReadInt32() != 0; Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, fgmId, isAutoClearEvent }); @@ -50,8 +50,8 @@ namespace Ryujinx.HLE.HOS.Services.Mm public ResultCode SetAndWaitOld(ServiceCtx context) { MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32(); - uint frequenceHz = context.RequestData.ReadUInt32(); - int timeout = context.RequestData.ReadInt32(); + uint frequenceHz = context.RequestData.ReadUInt32(); + int timeout = context.RequestData.ReadInt32(); Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, frequenceHz, timeout }); @@ -87,9 +87,9 @@ namespace Ryujinx.HLE.HOS.Services.Mm // Initialize(u32, u32, u32) -> u32 public ResultCode Initialize(ServiceCtx context) { - MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32(); - int fgmId = context.RequestData.ReadInt32(); - bool isAutoClearEvent = context.RequestData.ReadInt32() != 0; + MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32(); + int fgmId = context.RequestData.ReadInt32(); + bool isAutoClearEvent = context.RequestData.ReadInt32() != 0; Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, fgmId, isAutoClearEvent }); @@ -120,9 +120,9 @@ namespace Ryujinx.HLE.HOS.Services.Mm // SetAndWait(u32, u32, u32) public ResultCode SetAndWait(ServiceCtx context) { - uint id = context.RequestData.ReadUInt32(); + uint id = context.RequestData.ReadUInt32(); uint frequenceHz = context.RequestData.ReadUInt32(); - int timeout = context.RequestData.ReadInt32(); + int timeout = context.RequestData.ReadInt32(); Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id, frequenceHz, timeout }); @@ -185,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm lock (_sessionListLock) { // Nintendo ignore the fgm id as the other interfaces were deprecated. - MultiMediaSession session = new MultiMediaSession(_uniqueId++, type, isAutoClearEvent); + MultiMediaSession session = new(_uniqueId++, type, isAutoClearEvent); _sessionList.Add(session); @@ -193,4 +193,4 @@ namespace Ryujinx.HLE.HOS.Services.Mm } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs index 2742af6c4..095dbfc31 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs @@ -2,9 +2,9 @@ { enum MultiMediaOperationType : uint { - Ram = 2, + Ram = 2, NvEnc = 5, NvDec = 6, - NvJpg = 7 + NvJpg = 7, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs index a6723ecab..32b52ca54 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs @@ -5,15 +5,15 @@ public MultiMediaOperationType Type { get; } public bool IsAutoClearEvent { get; } - public uint Id { get; } - public uint CurrentValue { get; private set; } + public uint Id { get; } + public uint CurrentValue { get; private set; } public MultiMediaSession(uint id, MultiMediaOperationType type, bool isAutoClearEvent) { - Type = type; - Id = id; + Type = type; + Id = id; IsAutoClearEvent = isAutoClearEvent; - CurrentValue = 0; + CurrentValue = 0; } public void SetAndWait(uint value, int timeout) diff --git a/src/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs index c2a4345c6..7c20579be 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Mnpp public ResultCode SendRawTelemetryData(ServiceCtx context) { ulong titleIdInputPosition = context.Request.SendBuff[0].Position; - ulong titleIdInputSize = context.Request.SendBuff[0].Size; + ulong titleIdInputSize = context.Request.SendBuff[0].Size; UserId userId = context.RequestData.ReadStruct<UserId>(); @@ -60,4 +60,4 @@ namespace Ryujinx.HLE.HOS.Services.Mnpp return ResultCode.InvalidBufferSize; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs index dfc39a735..af3acf0ab 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Services.Mnpp { enum ResultCode { - ModuleId = 239, + ModuleId = 239, ErrorCodeShift = 9, Success = 0, - InvalidArgument = (100 << ErrorCodeShift) | ModuleId, - InvalidBufferSize = (101 << ErrorCodeShift) | ModuleId + InvalidArgument = (100 << ErrorCodeShift) | ModuleId, + InvalidBufferSize = (101 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs index 7f05d9bed..cf0d41494 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs @@ -5,4 +5,4 @@ { public IContentManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs index 318ad30e6..f59216786 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ILocationResolverManager.cs @@ -19,4 +19,4 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs index 55b49bcec..f5a0c665c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs @@ -2,14 +2,13 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.HLE.FileSystem; using System.Text; - using static Ryujinx.HLE.Utilities.StringUtils; namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager { class ILocationResolver : IpcService { - private StorageId _storageId; + private readonly StorageId _storageId; public ILocationResolver(StorageId storageId) { @@ -215,8 +214,8 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager private void RedirectPath(ServiceCtx context, ulong titleId, int flag, NcaContentType contentType) { - string contentPath = ReadUtf8String(context); - LocationEntry newLocation = new LocationEntry(contentPath, flag, titleId, contentType); + string contentPath = ReadUtf8String(context); + LocationEntry newLocation = new(contentPath, flag, titleId, contentType); context.Device.System.ContentManager.RedirectLocation(newLocation, _storageId); } @@ -224,12 +223,14 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager private bool ResolvePath(ServiceCtx context, ulong titleId, NcaContentType contentType) { ContentManager contentManager = context.Device.System.ContentManager; - string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Program); + string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Program); if (!string.IsNullOrWhiteSpace(contentPath)) { ulong position = context.Request.RecvListBuff[0].Position; - ulong size = context.Request.RecvListBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong size = context.Request.RecvListBuff[0].Size; +#pragma warning restore IDE0059 byte[] contentPathBuffer = Encoding.UTF8.GetBytes(contentPath); @@ -246,7 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager private void DeleteContentPath(ServiceCtx context, ulong titleId, NcaContentType contentType) { ContentManager contentManager = context.Device.System.ContentManager; - string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Manual); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Manual); +#pragma warning restore IDE0059 contentManager.ClearEntry(titleId, NcaContentType.Manual, _storageId); } diff --git a/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs index d21fe634e..3a027fd04 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ncm/Lr/ResultCode.cs @@ -2,19 +2,19 @@ { enum ResultCode { - ModuleId = 8, + ModuleId = 8, ErrorCodeShift = 9, Success = 0, - ProgramLocationEntryNotFound = (2 << ErrorCodeShift) | ModuleId, - InvalidContextForControlLocation = (3 << ErrorCodeShift) | ModuleId, - StorageNotFound = (4 << ErrorCodeShift) | ModuleId, - AccessDenied = (5 << ErrorCodeShift) | ModuleId, - OfflineManualHTMLLocationEntryNotFound = (6 << ErrorCodeShift) | ModuleId, - TitleIsNotRegistered = (7 << ErrorCodeShift) | ModuleId, - ControlLocationEntryForHostNotFound = (8 << ErrorCodeShift) | ModuleId, - LegalInfoHTMLLocationEntryNotFound = (9 << ErrorCodeShift) | ModuleId, - ProgramLocationForDebugEntryNotFound = (10 << ErrorCodeShift) | ModuleId + ProgramLocationEntryNotFound = (2 << ErrorCodeShift) | ModuleId, + InvalidContextForControlLocation = (3 << ErrorCodeShift) | ModuleId, + StorageNotFound = (4 << ErrorCodeShift) | ModuleId, + AccessDenied = (5 << ErrorCodeShift) | ModuleId, + OfflineManualHTMLLocationEntryNotFound = (6 << ErrorCodeShift) | ModuleId, + TitleIsNotRegistered = (7 << ErrorCodeShift) | ModuleId, + ControlLocationEntryForHostNotFound = (8 << ErrorCodeShift) | ModuleId, + LegalInfoHTMLLocationEntryNotFound = (9 << ErrorCodeShift) | ModuleId, + ProgramLocationForDebugEntryNotFound = (10 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs index 7ea89b20d..5d33e1a2b 100644 --- a/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs @@ -9,4 +9,4 @@ { public IServiceCreator(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs index 33932568d..142c4da48 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/IAmManager.cs @@ -5,4 +5,4 @@ { public IAmManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs index ef90b6ad8..50453117d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Nfc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs index 97959a62f..69c3bd191 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Nfc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs index cc3cd3aa9..2c5aaa2e9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs @@ -5,4 +5,4 @@ { public IUserManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs index b091aabfc..dac0c4c33 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager { class INfc : IpcService { - private NfcPermissionLevel _permissionLevel; + private readonly NfcPermissionLevel _permissionLevel; private State _state; public INfc(NfcPermissionLevel permissionLevel) @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager [CommandCmif(2)] [CommandCmif(402)] // 4.0.0+ // GetState() -> u32 - public ResultCode GetState(ServiceCtx context) + public ResultCode GetState(ServiceCtx context) { context.ResponseData.Write((int)_state); @@ -60,4 +60,4 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs index 39babc737..37a78dfd3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs @@ -3,6 +3,6 @@ enum NfcPermissionLevel { User, - System + System, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs index 85f999507..cfc86dfa6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/State.cs @@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager enum State { NonInitialized, - Initialized + Initialized, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs index e75f62004..d48671126 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/AmiiboJsonSerializerContext.cs @@ -7,4 +7,4 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp internal partial class AmiiboJsonSerializerContext : JsonSerializerContext { } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs index fc4544739..8196f42d1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs index 3fcf7a87a..d5933a4ce 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs index 93da84194..fa80e9b86 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs index e25a2972a..9dc75cd2c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs @@ -18,8 +18,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { class INfp : IpcService { - private ulong _appletResourceUserId; - private ulong _mcuVersionData; +#pragma warning disable IDE0052 // Remove unread private member + private ulong _appletResourceUserId; + private ulong _mcuVersionData; +#pragma warning restore IDE0052 private byte[] _mcuData; private State _state = State.NonInitialized; @@ -28,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp private CancellationTokenSource _cancelTokenSource; - private NfpPermissionLevel _permissionLevel; + private readonly NfpPermissionLevel _permissionLevel; public INfp(NfpPermissionLevel permissionLevel) { @@ -40,10 +42,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp public ResultCode Initialize(ServiceCtx context) { _appletResourceUserId = context.RequestData.ReadUInt64(); - _mcuVersionData = context.RequestData.ReadUInt64(); + _mcuVersionData = context.RequestData.ReadUInt64(); ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; + ulong inputSize = context.Request.SendBuff[0].Size; _mcuData = new byte[inputSize]; @@ -53,11 +55,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp // TODO: Handle this in a controller class directly. // Every functions which use the Handle call nn::hid::system::GetXcdHandleForNpadWithNfc(). - NfpDevice devicePlayer1 = new NfpDevice + NfpDevice devicePlayer1 = new() { NpadIdType = NpadIdType.Player1, - Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1), - State = NfpDeviceState.Initialized + Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1), + State = NfpDeviceState.Initialized, }; context.Device.System.NfpDevices.Add(devicePlayer1); @@ -75,10 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { if (_state == State.Initialized) { - if (_cancelTokenSource != null) - { - _cancelTokenSource.Cancel(); - } + _cancelTokenSource?.Cancel(); // NOTE: All events are destroyed here. context.Device.System.NfpDevices.Clear(); @@ -99,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp } ulong outputPosition = context.Request.RecvListBuff[0].Position; - ulong outputSize = context.Request.RecvListBuff[0].Size; + ulong outputSize = context.Request.RecvListBuff[0].Size; if (context.Device.System.NfpDevices.Count == 0) { @@ -187,10 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - if (_cancelTokenSource != null) - { - _cancelTokenSource.Cancel(); - } + _cancelTokenSource?.Cancel(); uint deviceHandle = (uint)context.RequestData.ReadUInt64(); @@ -218,9 +214,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return resultCode; } - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - DeviceType deviceType = (DeviceType)context.RequestData.ReadUInt32(); - MountTarget mountTarget = (MountTarget)context.RequestData.ReadUInt32(); + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + DeviceType deviceType = (DeviceType)context.RequestData.ReadUInt32(); + MountTarget mountTarget = (MountTarget)context.RequestData.ReadUInt32(); if (deviceType != 0) { @@ -382,7 +378,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp } ulong outputPosition = context.Request.ReceiveBuff[0].Position; - ulong outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputSize = context.Request.ReceiveBuff[0].Size; MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); @@ -450,7 +446,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp } ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; + ulong inputSize = context.Request.SendBuff[0].Size; byte[] applicationArea = new byte[inputSize]; @@ -489,7 +485,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp // Flush(bytes<8, 4>) public ResultCode Flush(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment uint deviceHandle = (uint)context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 if (context.Device.System.NfpDevices.Count == 0) { @@ -529,7 +527,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp uint applicationAreaId = context.RequestData.ReadUInt32(); ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; + ulong inputSize = context.Request.SendBuff[0].Size; byte[] applicationArea = new byte[inputSize]; @@ -612,23 +610,23 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted || context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) { - byte[] Uuid = VirtualAmiibo.GenerateUuid(context.Device.System.NfpDevices[i].AmiiboId, context.Device.System.NfpDevices[i].UseRandomUuid); + byte[] uuid = VirtualAmiibo.GenerateUuid(context.Device.System.NfpDevices[i].AmiiboId, context.Device.System.NfpDevices[i].UseRandomUuid); - if (Uuid.Length > AmiiboConstants.UuidMaxLength) + if (uuid.Length > AmiiboConstants.UuidMaxLength) { - throw new ArgumentOutOfRangeException(); + throw new InvalidOperationException($"{nameof(uuid)} is too long: {uuid.Length}"); } - TagInfo tagInfo = new TagInfo + TagInfo tagInfo = new() { - UuidLength = (byte)Uuid.Length, - Reserved1 = new Array21<byte>(), - Protocol = uint.MaxValue, // All Protocol - TagType = uint.MaxValue, // All Type - Reserved2 = new Array6<byte>() + UuidLength = (byte)uuid.Length, + Reserved1 = new Array21<byte>(), + Protocol = uint.MaxValue, // All Protocol + TagType = uint.MaxValue, // All Type + Reserved2 = new Array6<byte>(), }; - Uuid.CopyTo(tagInfo.Uuid.AsSpan()); + uuid.CopyTo(tagInfo.Uuid.AsSpan()); context.Memory.Write(outputPosition, tagInfo); @@ -811,17 +809,16 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) { - ModelInfo modelInfo = new ModelInfo + ModelInfo modelInfo = new() { - Reserved = new Array57<byte>() + Reserved = new Array57<byte>(), + CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(0, 4), NumberStyles.HexNumber)), + CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(4, 2), NumberStyles.HexNumber), + Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(12, 2), NumberStyles.HexNumber), + ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(8, 4), NumberStyles.HexNumber), + Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber), }; - modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(0, 4), NumberStyles.HexNumber)); - modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(4, 2), NumberStyles.HexNumber); - modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(12, 2), NumberStyles.HexNumber); - modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(8, 4), NumberStyles.HexNumber); - modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber); - context.Memory.Write(outputPosition, modelInfo); resultCode = ResultCode.Success; @@ -912,7 +909,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { if (context.Device.System.NfpDevices[i].State > NfpDeviceState.Finalized) { - throw new ArgumentOutOfRangeException(); + throw new InvalidOperationException($"{nameof(context.Device.System.NfpDevices)} contains an invalid state for device {i}: {context.Device.System.NfpDevices[i].State}"); } context.ResponseData.Write((uint)context.Device.System.NfpDevices[i].State); diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs index b06492e6e..a5d420cb1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs @@ -2,7 +2,7 @@ { static class AmiiboConstants { - public const int UuidMaxLength = 10; + public const int UuidMaxLength = 10; public const int ApplicationAreaSize = 0xD8; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs index a7976de9e..ba953125e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs @@ -6,12 +6,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager [StructLayout(LayoutKind.Sequential, Size = 0x40)] struct CommonInfo { - public ushort LastWriteYear; - public byte LastWriteMonth; - public byte LastWriteDay; - public ushort WriteCounter; - public ushort Version; - public uint ApplicationAreaSize; + public ushort LastWriteYear; + public byte LastWriteMonth; + public byte LastWriteDay; + public ushort WriteCounter; + public ushort Version; + public uint ApplicationAreaSize; public Array52<byte> Reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs index 096522a0a..44c8a79a4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs @@ -4,4 +4,4 @@ { Amiibo } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs index c66636ae0..48aba269d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs @@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager [StructLayout(LayoutKind.Sequential, Size = 0x40)] struct ModelInfo { - public ushort CharacterId; - public byte CharacterVariant; - public byte Series; - public ushort ModelNumber; - public byte Type; + public ushort CharacterId; + public byte CharacterVariant; + public byte Series; + public ushort ModelNumber; + public byte Type; public Array57<byte> Reserved; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs index 4a1457738..d71d7eea8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs @@ -4,6 +4,6 @@ { Rom = 1, Ram = 2, - All = 3 + All = 3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs index f56d33a95..82602837a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs @@ -8,16 +8,16 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager public KEvent ActivateEvent; public KEvent DeactivateEvent; - public void SignalActivate() => ActivateEvent.ReadableEvent.Signal(); + public void SignalActivate() => ActivateEvent.ReadableEvent.Signal(); public void SignalDeactivate() => DeactivateEvent.ReadableEvent.Signal(); public NfpDeviceState State = NfpDeviceState.Unavailable; public PlayerIndex Handle; - public NpadIdType NpadIdType; + public NpadIdType NpadIdType; public string AmiiboId; public bool UseRandomUuid; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs index 51e1d0608..d0e0c8baa 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs @@ -2,12 +2,12 @@ { enum NfpDeviceState { - Initialized = 0, + Initialized = 0, SearchingForTag = 1, - TagFound = 2, - TagRemoved = 3, - TagMounted = 4, - Unavailable = 5, - Finalized = 6 + TagFound = 2, + TagRemoved = 3, + TagMounted = 4, + Unavailable = 5, + Finalized = 6, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs index 8b84dcfee..972f0af34 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs @@ -4,6 +4,6 @@ { Debug, User, - System + System, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs index 6b30eb8ed..28d8fae20 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs @@ -7,13 +7,13 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager [StructLayout(LayoutKind.Sequential, Size = 0x100)] struct RegisterInfo { - public CharInfo MiiCharInfo; - public ushort FirstWriteYear; - public byte FirstWriteMonth; - public byte FirstWriteDay; + public CharInfo MiiCharInfo; + public ushort FirstWriteYear; + public byte FirstWriteMonth; + public byte FirstWriteDay; public Array41<byte> Nickname; - public byte FontRegion; + public byte FontRegion; public Array64<byte> Reserved1; public Array58<byte> Reserved2; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs index b38cf9e21..ca7faf042 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs @@ -3,6 +3,6 @@ enum State { NonInitialized = 0, - Initialized = 1 + Initialized = 1, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs index d2076b2a6..903743f13 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs @@ -7,10 +7,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager struct TagInfo { public Array10<byte> Uuid; - public byte UuidLength; + public byte UuidLength; public Array21<byte> Reserved1; - public uint Protocol; - public uint TagType; - public Array6<byte> Reserved2; + public uint Protocol; + public uint TagType; + public Array6<byte> Reserved2; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs index be1877e50..51e04dcac 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs @@ -5,18 +5,18 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager { struct VirtualAmiiboFile { - public uint FileVersion { get; set; } - public byte[] TagUuid { get; set; } - public string AmiiboId { get; set; } + public uint FileVersion { get; set; } + public byte[] TagUuid { get; set; } + public string AmiiboId { get; set; } public DateTime FirstWriteDate { get; set; } - public DateTime LastWriteDate { get; set; } - public ushort WriteCounter { get; set; } + public DateTime LastWriteDate { get; set; } + public ushort WriteCounter { get; set; } public List<VirtualAmiiboApplicationArea> ApplicationAreas { get; set; } } struct VirtualAmiiboApplicationArea { - public uint ApplicationAreaId { get; set; } - public byte[] ApplicationArea { get; set; } + public uint ApplicationAreaId { get; set; } + public byte[] ApplicationArea { get; set; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs index e0ccbc6d6..233877073 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ResultCode.cs @@ -2,17 +2,17 @@ { public enum ResultCode { - ModuleId = 115, + ModuleId = 115, ErrorCodeShift = 9, Success = 0, - DeviceNotFound = (64 << ErrorCodeShift) | ModuleId, - WrongArgument = (65 << ErrorCodeShift) | ModuleId, - WrongDeviceState = (73 << ErrorCodeShift) | ModuleId, - NfcDisabled = (80 << ErrorCodeShift) | ModuleId, - TagNotFound = (97 << ErrorCodeShift) | ModuleId, - ApplicationAreaIsNull = (128 << ErrorCodeShift) | ModuleId, - ApplicationAreaAlreadyCreated = (168 << ErrorCodeShift) | ModuleId + DeviceNotFound = (64 << ErrorCodeShift) | ModuleId, + WrongArgument = (65 << ErrorCodeShift) | ModuleId, + WrongDeviceState = (73 << ErrorCodeShift) | ModuleId, + NfcDisabled = (80 << ErrorCodeShift) | ModuleId, + TagNotFound = (97 << ErrorCodeShift) | ModuleId, + ApplicationAreaIsNull = (128 << ErrorCodeShift) | ModuleId, + ApplicationAreaAlreadyCreated = (168 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 9e1db7fcd..3d1426530 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -8,7 +8,6 @@ using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using System; using System.Collections.Generic; using System.IO; -using System.Linq; namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { @@ -16,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { private static uint _openedApplicationAreaId; - private static readonly AmiiboJsonSerializerContext SerializerContext = AmiiboJsonSerializerContext.Default; + private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default; public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid) { @@ -43,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp Random.Shared.NextBytes(uuid); - uuid[3] = (byte)(0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2]); + uuid[3] = (byte)(0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2]); uuid[8] = (byte)(uuid[3] ^ uuid[4] ^ uuid[5] ^ uuid[6]); return uuid; @@ -55,13 +54,13 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return new CommonInfo() { - LastWriteYear = (ushort)amiiboFile.LastWriteDate.Year, - LastWriteMonth = (byte)amiiboFile.LastWriteDate.Month, - LastWriteDay = (byte)amiiboFile.LastWriteDate.Day, - WriteCounter = amiiboFile.WriteCounter, - Version = 1, + LastWriteYear = (ushort)amiiboFile.LastWriteDate.Year, + LastWriteMonth = (byte)amiiboFile.LastWriteDate.Month, + LastWriteDay = (byte)amiiboFile.LastWriteDate.Day, + WriteCounter = amiiboFile.WriteCounter, + Version = 1, ApplicationAreaSize = AmiiboConstants.ApplicationAreaSize, - Reserved = new Array52<byte>() + Reserved = new Array52<byte>(), }; } @@ -69,22 +68,22 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId); - UtilityImpl utilityImpl = new UtilityImpl(tickSource); - CharInfo charInfo = new CharInfo(); + UtilityImpl utilityImpl = new(tickSource); + CharInfo charInfo = new(); charInfo.SetFromStoreData(StoreData.BuildDefault(utilityImpl, 0)); charInfo.Nickname = Nickname.FromString(nickname); - RegisterInfo registerInfo = new RegisterInfo() + RegisterInfo registerInfo = new() { - MiiCharInfo = charInfo, - FirstWriteYear = (ushort)amiiboFile.FirstWriteDate.Year, + MiiCharInfo = charInfo, + FirstWriteYear = (ushort)amiiboFile.FirstWriteDate.Year, FirstWriteMonth = (byte)amiiboFile.FirstWriteDate.Month, - FirstWriteDay = (byte)amiiboFile.FirstWriteDate.Day, - FontRegion = 0, - Reserved1 = new Array64<byte>(), - Reserved2 = new Array58<byte>() + FirstWriteDay = (byte)amiiboFile.FirstWriteDate.Day, + FontRegion = 0, + Reserved1 = new Array64<byte>(), + Reserved2 = new Array58<byte>(), }; "Ryujinx"u8.CopyTo(registerInfo.Nickname.AsSpan()); @@ -132,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp virtualAmiiboFile.ApplicationAreas.Add(new VirtualAmiiboApplicationArea() { ApplicationAreaId = applicationAreaId, - ApplicationArea = applicationAreaData + ApplicationArea = applicationAreaData, }); SaveAmiiboFile(virtualAmiiboFile); @@ -153,7 +152,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp virtualAmiiboFile.ApplicationAreas[i] = new VirtualAmiiboApplicationArea() { ApplicationAreaId = _openedApplicationAreaId, - ApplicationArea = applicationAreaData + ApplicationArea = applicationAreaData, }; break; @@ -174,19 +173,19 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp if (File.Exists(filePath)) { - virtualAmiiboFile = JsonHelper.DeserializeFromFile(filePath, SerializerContext.VirtualAmiiboFile); + virtualAmiiboFile = JsonHelper.DeserializeFromFile(filePath, _serializerContext.VirtualAmiiboFile); } else { virtualAmiiboFile = new VirtualAmiiboFile() { - FileVersion = 0, - TagUuid = Array.Empty<byte>(), - AmiiboId = amiiboId, - FirstWriteDate = DateTime.Now, - LastWriteDate = DateTime.Now, - WriteCounter = 0, - ApplicationAreas = new List<VirtualAmiiboApplicationArea>() + FileVersion = 0, + TagUuid = Array.Empty<byte>(), + AmiiboId = amiiboId, + FirstWriteDate = DateTime.Now, + LastWriteDate = DateTime.Now, + WriteCounter = 0, + ApplicationAreas = new List<VirtualAmiiboApplicationArea>(), }; SaveAmiiboFile(virtualAmiiboFile); @@ -198,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile) { string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"); - JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, SerializerContext.VirtualAmiiboFile); + JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, _serializerContext.VirtualAmiiboFile); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ngct/IService.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/IService.cs index eacf35f32..802be7514 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ngct/IService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ngct/IService.cs @@ -19,4 +19,4 @@ return NgctServer.Filter(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs index 5ad056bab..7ef998359 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ngct/IServiceWithManagementApi.cs @@ -19,4 +19,4 @@ return NgctServer.Filter(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs index 8d99721eb..ae00842d7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs @@ -12,10 +12,10 @@ namespace Ryujinx.HLE.HOS.Services.Ngct // If both conditions are true, it does this following code. Since we currently stub it, it's fine to don't check settings service values. ulong bufferPosition = context.Request.PtrBuff[0].Position; - ulong bufferSize = context.Request.PtrBuff[0].Size; + ulong bufferSize = context.Request.PtrBuff[0].Size; - bool isMatch = false; - string text = ""; + bool isMatch = false; + string text = ""; if (bufferSize != 0) { @@ -53,11 +53,11 @@ namespace Ryujinx.HLE.HOS.Services.Ngct // If both conditions are true, it does this following code. Since we currently stub it, it's fine to don't check settings service values. ulong bufferPosition = context.Request.PtrBuff[0].Position; - ulong bufferSize = context.Request.PtrBuff[0].Size; + ulong bufferSize = context.Request.PtrBuff[0].Size; ulong bufferFilteredPosition = context.Request.RecvListBuff[0].Position; - string text = ""; + string text = ""; string textFiltered = ""; if (bufferSize != 0) @@ -89,4 +89,4 @@ namespace Ryujinx.HLE.HOS.Services.Ngct return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs index d6a4a29fb..96e71cd07 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs @@ -27,4 +27,4 @@ namespace Ryujinx.HLE.HOS.Services.Nifm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs index 73cadb114..91a3d0af8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/ResultCode.cs @@ -2,14 +2,14 @@ namespace Ryujinx.HLE.HOS.Services.Nifm { enum ResultCode { - ModuleId = 110, + ModuleId = 110, ErrorCodeShift = 9, Success = 0, - Unknown112 = (112 << ErrorCodeShift) | ModuleId, // IRequest::GetResult - Unknown180 = (180 << ErrorCodeShift) | ModuleId, // IRequest::GetAppletInfo + Unknown112 = (112 << ErrorCodeShift) | ModuleId, // IRequest::GetResult + Unknown180 = (180 << ErrorCodeShift) | ModuleId, // IRequest::GetAppletInfo NoInternetConnection = (300 << ErrorCodeShift) | ModuleId, - ObjectIsNull = (350 << ErrorCodeShift) | ModuleId + ObjectIsNull = (350 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs index bbb218bbe..5f26f2114 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/GeneralServiceManager.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService { static class GeneralServiceManager { - private static List<GeneralServiceDetail> _generalServices = new List<GeneralServiceDetail>(); + private static readonly List<GeneralServiceDetail> _generalServices = new(); public static int Count { @@ -27,4 +27,4 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService return _generalServices.First(item => item.ClientId == clientId); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs index 3cf55345c..fab158515 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/GeneralService/Types/GeneralServiceDetail.cs @@ -2,7 +2,7 @@ { class GeneralServiceDetail { - public int ClientId; + public int ClientId; public bool IsAnyInternetRequestAccepted; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index e9712e927..581a2906b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { class IGeneralService : DisposableIpcService { - private GeneralServiceDetail _generalServiceDetail; + private readonly GeneralServiceDetail _generalServiceDetail; private IPInterfaceProperties _targetPropertiesCache = null; private UnicastIPAddressInformation _targetAddressInfoCache = null; @@ -21,11 +21,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { _generalServiceDetail = new GeneralServiceDetail { - ClientId = GeneralServiceManager.Count, - IsAnyInternetRequestAccepted = true // NOTE: Why not accept any internet request? + ClientId = GeneralServiceManager.Count, + IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request? }; - NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(LocalInterfaceCacheHandler); + NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler; GeneralServiceManager.Add(_generalServiceDetail); } @@ -76,13 +76,13 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<NetworkProfileData>()); - NetworkProfileData networkProfile = new NetworkProfileData + NetworkProfileData networkProfile = new() { - Uuid = UInt128Utils.CreateRandom() + Uuid = UInt128Utils.CreateRandom(), }; networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress); - networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties); + networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties); "RyujinxNetwork"u8.CopyTo(networkProfile.Name.AsSpan()); @@ -137,11 +137,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.NoInternetConnection; } - InternetConnectionStatus internetConnectionStatus = new InternetConnectionStatus + InternetConnectionStatus internetConnectionStatus = new() { - Type = InternetConnectionType.WiFi, + Type = InternetConnectionType.WiFi, WifiStrength = 3, - State = InternetConnectionState.Connected, + State = InternetConnectionState.Connected, }; context.ResponseData.WriteStruct(internetConnectionStatus); @@ -154,7 +154,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService public ResultCode IsAnyInternetRequestAccepted(ServiceCtx context) { ulong position = context.Request.PtrBuff[0].Position; - ulong size = context.Request.PtrBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong size = context.Request.PtrBuff[0].Size; +#pragma warning restore IDE0059 int clientId = context.Memory.Read<int>(position); @@ -184,9 +186,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService private void LocalInterfaceCacheHandler(object sender, EventArgs e) { - Logger.Info?.Print(LogClass.ServiceNifm, $"NetworkAddress changed, invalidating cached data."); + Logger.Info?.Print(LogClass.ServiceNifm, "NetworkAddress changed, invalidating cached data."); - _targetPropertiesCache = null; + _targetPropertiesCache = null; _targetAddressInfoCache = null; } @@ -200,4 +202,4 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs index 87aad30be..577d03822 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs @@ -12,16 +12,18 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { Error = 1, OnHold = 2, - Available = 3 + Available = 3, } - private KEvent _event0; - private KEvent _event1; + private readonly KEvent _event0; + private readonly KEvent _event1; private int _event0Handle; private int _event1Handle; - private uint _version; +#pragma warning disable IDE0052 // Remove unread private member + private readonly uint _version; +#pragma warning restore IDE0052 public IRequest(Horizon system, uint version) { @@ -116,7 +118,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // GetAppletInfo(u32) -> (u32, u32, u32, buffer<bytes, 6>) public ResultCode GetAppletInfo(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment uint themeColor = context.RequestData.ReadUInt32(); +#pragma warning restore IDE0059 Logger.Stub?.PrintStub(LogClass.ServiceNifm); @@ -139,4 +143,4 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs index 374558eab..4a9d782db 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types struct DnsSetting { [MarshalAs(UnmanagedType.U1)] - public bool IsDynamicDnsEnabled; + public bool IsDynamicDnsEnabled; public IpV4Address PrimaryDns; public IpV4Address SecondaryDns; @@ -18,14 +18,14 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types if (interfaceProperties.DnsAddresses.Count == 0) { - PrimaryDns = new IpV4Address(); + PrimaryDns = new IpV4Address(); SecondaryDns = new IpV4Address(); } else { - PrimaryDns = new IpV4Address(interfaceProperties.DnsAddresses[0]); + PrimaryDns = new IpV4Address(interfaceProperties.DnsAddresses[0]); SecondaryDns = new IpV4Address(interfaceProperties.DnsAddresses[interfaceProperties.DnsAddresses.Count > 1 ? 1 : 0]); } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs index dfb8f76ca..8c9efa61e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionState.cs @@ -6,6 +6,6 @@ ConnectingType1 = 1, ConnectingType2 = 2, ConnectingType3 = 3, - Connected = 4, + Connected = 4, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs index ff944eca2..1bf41fc6b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionStatus.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types [StructLayout(LayoutKind.Sequential)] struct InternetConnectionStatus { - public InternetConnectionType Type; - public byte WifiStrength; + public InternetConnectionType Type; + public byte WifiStrength; public InternetConnectionState State; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs index af2bcfa18..ab03382d4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/InternetConnectionType.cs @@ -2,8 +2,8 @@ { enum InternetConnectionType : byte { - Invalid = 0, - WiFi = 1, + Invalid = 0, + WiFi = 1, Ethernet = 2, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs index 59c1f6a7f..5ea9d849c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs @@ -8,17 +8,17 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types struct IpAddressSetting { [MarshalAs(UnmanagedType.U1)] - public bool IsDhcpEnabled; + public bool IsDhcpEnabled; public IpV4Address Address; public IpV4Address IPv4Mask; public IpV4Address GatewayAddress; public IpAddressSetting(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastIPAddressInformation) { - IsDhcpEnabled = OperatingSystem.IsMacOS() || interfaceProperties.DhcpServerAddresses.Count != 0; - Address = new IpV4Address(unicastIPAddressInformation.Address); - IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask); + IsDhcpEnabled = OperatingSystem.IsMacOS() || interfaceProperties.DhcpServerAddresses.Count != 0; + Address = new IpV4Address(unicastIPAddressInformation.Address); + IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask); GatewayAddress = (interfaceProperties.GatewayAddresses.Count == 0) ? new IpV4Address() : new IpV4Address(interfaceProperties.GatewayAddresses[0].Address); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs index 8ffe824c8..328dc7da6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs @@ -6,8 +6,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types struct IpSettingData { public IpAddressSetting IpAddressSetting; - public DnsSetting DnsSetting; - public ProxySetting ProxySetting; - public short Mtu; + public DnsSetting DnsSetting; + public ProxySetting ProxySetting; + public short Mtu; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs index e270c10ae..12a1c30fa 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs @@ -7,11 +7,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x17C)] struct NetworkProfileData { - public IpSettingData IpSettingData; - public UInt128 Uuid; - public Array64<byte> Name; - public Array4<byte> Unknown; + public IpSettingData IpSettingData; + public UInt128 Uuid; + public Array64<byte> Name; + public Array4<byte> Unknown; public WirelessSettingData WirelessSettingData; - public byte Padding; + public byte Padding; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs index 6e534fe1c..909138e80 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs @@ -9,19 +9,19 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types public struct ProxySetting { [MarshalAs(UnmanagedType.I1)] - public bool Enabled; - private byte _padding; - public short Port; - private NameStruct _name; + public bool Enabled; + private readonly byte _padding; + public short Port; + private NameStruct _name; [MarshalAs(UnmanagedType.I1)] - public bool AutoAuthEnabled; - public Array32<byte> User; - public Array32<byte> Pass; - private byte _padding2; + public bool AutoAuthEnabled; + public Array32<byte> User; + public Array32<byte> Pass; + private readonly byte _padding2; [StructLayout(LayoutKind.Sequential, Size = 0x64)] private struct NameStruct { } public Span<byte> Name => SpanHelpers.AsSpan<NameStruct, byte>(ref _name); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs index 8aa122c7d..53855a4ef 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs @@ -6,10 +6,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x65)] struct WirelessSettingData { - public byte SsidLength; + public byte SsidLength; public Array32<byte> Ssid; - public Array3<byte> Unknown; + public Array3<byte> Unknown; public Array64<byte> Passphrase1; - public byte Passphrase2; + public byte Passphrase2; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs b/src/Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs index ad79ca0de..7ed1ffa62 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/INetworkInstallManager.cs @@ -5,4 +5,4 @@ { public INetworkInstallManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs index ab17871f8..29a47baf6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs @@ -18,4 +18,4 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs index 950004fa4..847a432e9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs @@ -41,4 +41,4 @@ namespace Ryujinx.HLE.HOS.Services.Nim return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs index bf201b988..a57677aed 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessSystemInterface.cs @@ -5,4 +5,4 @@ { public IShopServiceAccessSystemInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs index 3c0136faf..4d3002cc1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs @@ -39,4 +39,4 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServ return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs index 81d892c5d..150d48d4d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs @@ -4,4 +4,4 @@ { public IShopServiceAsync() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs index 2420615a6..b5a2f6b55 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceManager.cs @@ -5,4 +5,4 @@ { public IShopServiceManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs b/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs index 4a63615bc..2b078be7d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs b/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs index 82d0b5a80..9fee7b1ac 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs @@ -8,13 +8,13 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService { class IEnsureNetworkClockAvailabilityService : IpcService { - private KEvent _finishNotificationEvent; + private readonly KEvent _finishNotificationEvent; private ResultCode _taskResultCode; public IEnsureNetworkClockAvailabilityService(ServiceCtx context) { _finishNotificationEvent = new KEvent(context.Device.System.KernelContext); - _taskResultCode = ResultCode.Success; + _taskResultCode = ResultCode.Success; // NOTE: The service starts a thread that polls Nintendo NTP server and syncs the time with it. // Additionnally it gets and uses some settings too: @@ -74,4 +74,4 @@ namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs index 166e39a3c..3fcb90245 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nim/ResultCode.cs @@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Services.Nim { enum ResultCode { - ModuleId = 137, + ModuleId = 137, ErrorCodeShift = 9, Success = 0, - NullArgument = (90 << ErrorCodeShift) | ModuleId + NullArgument = (90 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs index c4a35b291..0d77dd6e6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForApplication.cs @@ -5,4 +5,4 @@ { public INotificationServicesForApplication(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs index 0939dff62..8dc1dac3b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs @@ -5,4 +5,4 @@ { public INotificationServicesForSystem(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs index fd8ccfb58..53c5b5392 100644 --- a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs @@ -5,4 +5,4 @@ { public INpnsSystem(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs index 68e769385..cfa25255a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs +++ b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs @@ -5,4 +5,4 @@ { public INpnsUser(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs index b4b5bb1f5..083a83214 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs @@ -11,11 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc class IAddOnContentManager : IpcService { private readonly KEvent _addOnContentListChangedEvent; - private int _addOnContentListChangedEventHandle; + private int _addOnContentListChangedEventHandle; private ulong _addOnContentBaseId; - private List<ulong> _mountedAocTitleIds = new List<ulong>(); + private readonly List<ulong> _mountedAocTitleIds = new(); public IAddOnContentManager(ServiceCtx context) { @@ -44,7 +44,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // CountAddOnContent(pid) -> u32 public ResultCode CountAddOnContent(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. @@ -55,7 +57,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // ListAddOnContent(u32 start_index, u32 buffer_size, pid) -> (u32 count, buffer<u32>) public ResultCode ListAddOnContent(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. @@ -75,7 +79,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // GetAddOnContentBaseId(pid) -> u64 public ResultCode GetAddOnContentBaseId(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. @@ -95,7 +101,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // PrepareAddOnContent(u32 index, pid) public ResultCode PrepareAddOnContent(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. @@ -123,7 +131,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // GetAddOnContentListChangedEventWithProcessId(pid) -> handle<copy> public ResultCode GetAddOnContentListChangedEventWithProcessId(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. @@ -142,7 +152,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NotifyMountAddOnContent(pid, u64 title_id) public ResultCode NotifyMountAddOnContent(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. @@ -160,7 +172,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NotifyUnmountAddOnContent(pid, u64 title_id) public ResultCode NotifyUnmountAddOnContent(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. @@ -175,7 +189,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // CheckAddOnContentMountStatus(pid) public ResultCode CheckAddOnContentMountStatus(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pid = context.Request.HandleDesc.PId; +#pragma warning restore IDE0059 // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. // Then it does some internal checks and returns InvalidBufferSize if they fail. @@ -239,10 +255,10 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call sys:set GetQuestFlag and store it internally. // If QuestFlag is true, counts some extra titles. - uint startIndex = context.RequestData.ReadUInt32(); - uint indexNumber = context.RequestData.ReadUInt32(); + uint startIndex = context.RequestData.ReadUInt32(); + uint indexNumber = context.RequestData.ReadUInt32(); ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; // TODO: This should use _addOnContentBaseId; uint aocTotalCount = (uint)context.Device.System.ContentManager.GetAocCount(); @@ -343,4 +359,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ((ulong)errorCode & 0x1FF | ((((ulong)errorCode >> 9) & 0x1FFF) << 32)) + 2000; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs index cb8903d42..40b0b2a8f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs @@ -4,4 +4,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc { public IContentsServiceManager() { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs index 1673fafc4..c92a10d66 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs @@ -19,9 +19,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // SetDefaultDeliveryTarget(pid, buffer<bytes, 5> unknown) public ResultCode SetDefaultDeliveryTarget(ServiceCtx context) { - ulong inBufferPosition = context.Request.SendBuff[0].Position; - ulong inBufferSize = context.Request.SendBuff[0].Size; - byte[] buffer = new byte[inBufferSize]; + ulong inBufferPosition = context.Request.SendBuff[0].Position; + ulong inBufferSize = context.Request.SendBuff[0].Size; + byte[] buffer = new byte[inBufferSize]; context.Memory.Read(inBufferPosition, buffer); @@ -65,4 +65,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs index 7602ecb36..b795a7563 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc { enum ResultCode { - ModuleId = 166, + ModuleId = 166, ErrorCodeShift = 9, Success = 0, InvalidBufferSize = (200 << ErrorCodeShift) | ModuleId, - InvalidPid = (300 << ErrorCodeShift) | ModuleId + InvalidPid = (300 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index 06e911f8d..a8a2a8e0c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -1,6 +1,5 @@ using LibHac.Ns; using Ryujinx.Common.Utilities; -using System; namespace Ryujinx.HLE.HOS.Services.Ns { @@ -13,8 +12,10 @@ namespace Ryujinx.HLE.HOS.Services.Ns // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>) public ResultCode GetApplicationControlData(ServiceCtx context) { - byte source = (byte)context.RequestData.ReadInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + byte source = (byte)context.RequestData.ReadInt64(); ulong titleId = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 ulong position = context.Request.ReceiveBuff[0].Position; @@ -25,4 +26,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs index c74ebd691..8f0f6cbe9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns { public IDevelopInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs index aa37a1e7f..8327d6195 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs @@ -11,8 +11,10 @@ namespace Ryujinx.HLE.HOS.Services.Ns // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>) public ResultCode GetApplicationControlData(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment byte source = (byte)context.RequestData.ReadInt64(); ulong titleId = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 ulong position = context.Request.ReceiveBuff[0].Position; diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs index 886bffdd3..e45c6750c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs @@ -27,4 +27,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs index 84ed3d0f0..1108778c3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns { public ISystemUpdateInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs index 0b6409920..c1ec50bc8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs @@ -5,4 +5,4 @@ namespace Ryujinx.HLE.HOS.Services.Ns { public IVulnerabilityManagerInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs index bb609fa4a..371edbecd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv { public MemoryManager Smmu { get; } public NvMemoryAllocator MemoryAllocator { get; } - public Host1xDevice Host1x { get;} + public Host1xDevice Host1x { get; } public Host1xContext(GpuContext gpu, ulong pid) { @@ -29,4 +29,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv Host1x.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs index dffe8783d..22d0aacc5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvDebugFSServices.cs @@ -5,4 +5,4 @@ { public INvDrvDebugFSServices(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 1d075d43f..a0df66d00 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -25,13 +25,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv [Service("nvdrv:t")] class INvDrvServices : IpcService { - private static readonly List<string> _deviceFileDebugRegistry = new List<string>() + private static readonly List<string> _deviceFileDebugRegistry = new() { "/dev/nvhost-dbg-gpu", - "/dev/nvhost-prof-gpu" + "/dev/nvhost-prof-gpu", }; - private static readonly Dictionary<string, Type> _deviceFileRegistry = new Dictionary<string, Type>() + private static readonly Dictionary<string, Type> _deviceFileRegistry = new() { { "/dev/nvmap", typeof(NvMapDeviceFile) }, { "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) }, @@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv { "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) }, }; - public static IdDictionary DeviceFileIdRegistry = new IdDictionary(); + public static IdDictionary DeviceFileIdRegistry = new(); private IVirtualMemoryManager _clientMemory; private ulong _owner; @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv private bool _transferMemInitialized = false; // TODO: This should call set:sys::GetDebugModeFlag - private bool _debugModeEnabled = false; + private readonly bool _debugModeEnabled = false; public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer) { @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass)) { - ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx), typeof(IVirtualMemoryManager), typeof(ulong) }); + ConstructorInfo constructor = deviceFileClass.GetConstructor(new[] { typeof(ServiceCtx), typeof(IVirtualMemoryManager), typeof(ulong) }); NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke(new object[] { context, _clientMemory, _owner }); @@ -91,13 +91,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv private NvResult GetIoctlArgument(ServiceCtx context, NvIoctl ioctlCommand, out Span<byte> arguments) { - (ulong inputDataPosition, ulong inputDataSize) = context.Request.GetBufferType0x21(0); + (ulong inputDataPosition, ulong inputDataSize) = context.Request.GetBufferType0x21(0); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong outputDataPosition, ulong outputDataSize) = context.Request.GetBufferType0x22(0); +#pragma warning restore IDE0059 NvIoctl.Direction ioctlDirection = ioctlCommand.DirectionValue; - uint ioctlSize = ioctlCommand.Size; + uint ioctlSize = ioctlCommand.Size; - bool isRead = (ioctlDirection & NvIoctl.Direction.Read) != 0; + bool isRead = (ioctlDirection & NvIoctl.Direction.Read) != 0; bool isWrite = (ioctlDirection & NvIoctl.Direction.Write) != 0; if ((isWrite && ioctlSize > outputDataSize) || (isRead && ioctlSize > inputDataSize)) @@ -186,53 +188,29 @@ namespace Ryujinx.HLE.HOS.Services.Nv return NvResult.Success; } - private static NvResult ConvertInternalErrorCode(NvInternalResult errorCode) + private NvResult ConvertInternalErrorCode(NvInternalResult errorCode) { - switch (errorCode) + return errorCode switch { - case NvInternalResult.Success: - return NvResult.Success; - case NvInternalResult.Unknown0x72: - return NvResult.AlreadyAllocated; - case NvInternalResult.TimedOut: - case NvInternalResult.TryAgain: - case NvInternalResult.Interrupted: - return NvResult.Timeout; - case NvInternalResult.InvalidAddress: - return NvResult.InvalidAddress; - case NvInternalResult.NotSupported: - case NvInternalResult.Unknown0x18: - return NvResult.NotSupported; - case NvInternalResult.InvalidState: - return NvResult.InvalidState; - case NvInternalResult.ReadOnlyAttribute: - return NvResult.ReadOnlyAttribute; - case NvInternalResult.NoSpaceLeft: - case NvInternalResult.FileTooBig: - return NvResult.InvalidSize; - case NvInternalResult.FileTableOverflow: - case NvInternalResult.BadFileNumber: - return NvResult.FileOperationFailed; - case NvInternalResult.InvalidInput: - return NvResult.InvalidValue; - case NvInternalResult.NotADirectory: - return NvResult.DirectoryOperationFailed; - case NvInternalResult.Busy: - return NvResult.Busy; - case NvInternalResult.BadAddress: - return NvResult.InvalidAddress; - case NvInternalResult.AccessDenied: - case NvInternalResult.OperationNotPermitted: - return NvResult.AccessDenied; - case NvInternalResult.OutOfMemory: - return NvResult.InsufficientMemory; - case NvInternalResult.DeviceNotFound: - return NvResult.ModuleNotPresent; - case NvInternalResult.IoError: - return NvResult.ResourceError; - default: - return NvResult.IoctlFailed; - } + NvInternalResult.Success => NvResult.Success, + NvInternalResult.Unknown0x72 => NvResult.AlreadyAllocated, + NvInternalResult.TimedOut or NvInternalResult.TryAgain or NvInternalResult.Interrupted => NvResult.Timeout, + NvInternalResult.InvalidAddress => NvResult.InvalidAddress, + NvInternalResult.NotSupported or NvInternalResult.Unknown0x18 => NvResult.NotSupported, + NvInternalResult.InvalidState => NvResult.InvalidState, + NvInternalResult.ReadOnlyAttribute => NvResult.ReadOnlyAttribute, + NvInternalResult.NoSpaceLeft or NvInternalResult.FileTooBig => NvResult.InvalidSize, + NvInternalResult.FileTableOverflow or NvInternalResult.BadFileNumber => NvResult.FileOperationFailed, + NvInternalResult.InvalidInput => NvResult.InvalidValue, + NvInternalResult.NotADirectory => NvResult.DirectoryOperationFailed, + NvInternalResult.Busy => NvResult.Busy, + NvInternalResult.BadAddress => NvResult.InvalidAddress, + NvInternalResult.AccessDenied or NvInternalResult.OperationNotPermitted => NvResult.AccessDenied, + NvInternalResult.OutOfMemory => NvResult.InsufficientMemory, + NvInternalResult.DeviceNotFound => NvResult.ModuleNotPresent, + NvInternalResult.IoError => NvResult.ResourceError, + _ => NvResult.IoctlFailed, + }; } [CommandCmif(0)] @@ -240,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv public ResultCode Open(ServiceCtx context) { NvResult errorCode = EnsureInitialized(); - int fd = -1; + int fd = -1; if (errorCode == NvResult.Success) { @@ -266,7 +244,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - int fd = context.RequestData.ReadInt32(); + int fd = context.RequestData.ReadInt32(); NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>(); errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments); @@ -328,8 +306,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv // Initialize(u32 transfer_memory_size, handle<copy, process> current_process, handle<copy, transfer_memory> transfer_memory) -> u32 error_code public ResultCode Initialize(ServiceCtx context) { - long transferMemSize = context.RequestData.ReadInt64(); - int transferMemHandle = context.Request.HandleDesc.ToCopy[1]; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + long transferMemSize = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 + int transferMemHandle = context.Request.HandleDesc.ToCopy[1]; // TODO: When transfer memory will be implemented, this could be removed. _transferMemInitialized = true; @@ -357,7 +337,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - int fd = context.RequestData.ReadInt32(); + int fd = context.RequestData.ReadInt32(); uint eventId = context.RequestData.ReadUInt32(); errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); @@ -393,9 +373,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - int fd = context.RequestData.ReadInt32(); - uint argument = context.RequestData.ReadUInt32(); - int sharedMemoryHandle = context.Request.HandleDesc.ToCopy[0]; + int fd = context.RequestData.ReadInt32(); + uint argument = context.RequestData.ReadUInt32(); + int sharedMemoryHandle = context.Request.HandleDesc.ToCopy[0]; errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); @@ -418,12 +398,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (_transferMemInitialized) { // TODO: Populate values when more RE will be done. - NvStatus nvStatus = new NvStatus + NvStatus nvStatus = new() { MemoryValue1 = 0, // GetMemStats(transfer_memory + 0x60, 3) MemoryValue2 = 0, // GetMemStats(transfer_memory + 0x60, 5) MemoryValue3 = 0, // transfer_memory + 0x78 - MemoryValue4 = 0 // transfer_memory + 0x80 + MemoryValue4 = 0, // transfer_memory + 0x80 }; context.ResponseData.WriteStruct(nvStatus); @@ -450,7 +430,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv // SetClientPID(u64, pid) -> u32 error_code public ResultCode SetClientPid(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment long pid = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 context.ResponseData.Write(0); @@ -481,7 +463,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - int fd = context.RequestData.ReadInt32(); + int fd = context.RequestData.ReadInt32(); NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>(); (ulong inlineInBufferPosition, ulong inlineInBufferSize) = context.Request.GetBufferType0x21(1); @@ -492,7 +474,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv context.Memory.Read(inlineInBufferPosition, temp); - Span<byte> inlineInBuffer = new Span<byte>(temp); + Span<byte> inlineInBuffer = new(temp); if (errorCode == NvResult.Success) { @@ -530,7 +512,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - int fd = context.RequestData.ReadInt32(); + int fd = context.RequestData.ReadInt32(); NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>(); (ulong inlineOutBufferPosition, ulong inlineOutBufferSize) = context.Request.GetBufferType0x22(1); @@ -541,7 +523,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv context.Memory.Read(inlineOutBufferPosition, temp); - Span<byte> inlineOutBuffer = new Span<byte>(temp); + Span<byte> inlineOutBuffer = new(temp); if (errorCode == NvResult.Success) { @@ -595,4 +577,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv DeviceFileIdRegistry.Clear(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs index 7bf99ed1a..2134c026e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/INvGemControl.cs @@ -5,4 +5,4 @@ { public INvGemControl(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs index ff3774da1..ecbbe2226 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/INvGemCoreDump.cs @@ -5,4 +5,4 @@ { public INvGemCoreDump(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs index 9568fc84f..3eaf8a91b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs @@ -10,14 +10,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices abstract class NvDeviceFile { public readonly ServiceCtx Context; - public readonly ulong Owner; + public readonly ulong Owner; public string Path; public NvDeviceFile(ServiceCtx context, ulong owner) { Context = context; - Owner = owner; + Owner = owner; } public virtual NvInternalResult QueryEvent(out int eventHandle, uint eventId) diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index 0e0fe7f2d..db460429d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu private const uint SmallPageSize = 0x1000; private const uint BigPageSize = 0x10000; - private static readonly uint[] _pageSizes = new uint[] { SmallPageSize, BigPageSize }; + private static readonly uint[] _pageSizes = { SmallPageSize, BigPageSize }; private const ulong SmallRegionLimit = 0x400000000UL; // 16 GiB private const ulong DefaultUserSize = 1UL << 37; @@ -32,10 +32,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu } } - private static readonly VmRegion[] _vmRegions = new VmRegion[] - { + private static readonly VmRegion[] _vmRegions = { new VmRegion((ulong)BigPageSize << 16, SmallRegionLimit), - new VmRegion(SmallRegionLimit, DefaultUserSize) + new VmRegion(SmallRegionLimit, DefaultUserSize), }; private readonly AddressSpaceContext _asContext; diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs index ab9d798e1..36aa10f34 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs @@ -8,24 +8,24 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types private class Range { public ulong Start { get; } - public ulong End { get; } + public ulong End { get; } public Range(ulong address, ulong size) { Start = address; - End = size + Start; + End = size + Start; } } private class MappedMemory : Range { public ulong PhysicalAddress { get; } - public bool VaAllocated { get; } + public bool VaAllocated { get; } public MappedMemory(ulong address, ulong size, ulong physicalAddress, bool vaAllocated) : base(address, size) { PhysicalAddress = physicalAddress; - VaAllocated = vaAllocated; + VaAllocated = vaAllocated; } } @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types { Gmm = gmm; - _maps = new SortedList<ulong, Range>(); + _maps = new SortedList<ulong, Range>(); _reservations = new SortedList<ulong, Range>(); } @@ -123,9 +123,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types return _reservations.Remove(gpuVa); } - private static Range BinarySearch(SortedList<ulong, Range> list, ulong address) + private Range BinarySearch(SortedList<ulong, Range> list, ulong address) { - int left = 0; + int left = 0; int right = list.Count - 1; while (left <= right) @@ -154,11 +154,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types return null; } - private static Range BinarySearchLt(SortedList<ulong, Range> list, ulong address) + private Range BinarySearchLt(SortedList<ulong, Range> list, ulong address) { Range ltRg = null; - int left = 0; + int left = 0; int right = list.Count - 1; while (left <= right) diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs index 611cf78bf..0627d56f5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types [Flags] enum AddressSpaceFlags : uint { - FixedOffset = 1, + FixedOffset = 1, RemapSubRange = 0x100, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs index d6dbbc261..c9d7724e8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs @@ -5,10 +5,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types [StructLayout(LayoutKind.Sequential)] struct AllocSpaceArguments { - public uint Pages; - public uint PageSize; + public uint Pages; + public uint PageSize; public AddressSpaceFlags Flags; - public uint Padding; - public ulong Offset; + public uint Padding; + public ulong Offset; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs index b25d295af..56e9028e5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types struct FreeSpaceArguments { public ulong Offset; - public uint Pages; - public uint PageSize; + public uint Pages; + public uint PageSize; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs index 882bda591..34b80bd31 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs @@ -5,10 +5,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types [StructLayout(LayoutKind.Sequential)] struct InitializeExArguments { - public uint Flags; - public int AsFd; - public uint BigPageSize; - public uint Reserved; + public uint Flags; + public int AsFd; + public uint BigPageSize; + public uint Reserved; public ulong Unknown0; public ulong Unknown1; public ulong Unknown2; diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs index 278793a02..fdeff9f8b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs @@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types struct MapBufferExArguments { public AddressSpaceFlags Flags; - public int Kind; - public int NvMapHandle; - public int PageSize; - public ulong BufferOffset; - public ulong MappingSize; - public ulong Offset; + public int Kind; + public int NvMapHandle; + public int PageSize; + public ulong BufferOffset; + public ulong MappingSize; + public ulong Offset; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs index bc149d424..1fb224ff0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs @@ -7,9 +7,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types { public ushort Flags; public ushort Kind; - public int NvMapHandle; - public uint MapOffset; - public uint GpuOffset; - public uint Pages; + public int NvMapHandle; + public uint MapOffset; + public uint GpuOffset; + public uint Pages; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs index 8fc4646ec..46a9659d5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs @@ -2,7 +2,7 @@ { struct UnmapBufferArguments { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public ulong Offset; #pragma warning restore CS0649 } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs index 87a06bd39..59578775d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs @@ -1358,4 +1358,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel channel.Write(ClassId.Twod, 0x24C, 0x100); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs index 9f16a280f..bd2c5e4d0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs @@ -19,9 +19,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private const uint MaxModuleSyncpoint = 16; +#pragma warning disable IDE0052 // Remove unread private member private uint _timeout; private uint _submitTimeout; private uint _timeslice; +#pragma warning restore IDE0052 private readonly Switch _device; @@ -34,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel public enum ResourcePolicy { Device, - Channel + Channel, } protected static uint[] DeviceSyncpoints = new uint[MaxModuleSyncpoint]; @@ -47,14 +49,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel public NvHostChannelDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { - _device = context.Device; - _memory = memory; - _timeout = 3000; + _device = context.Device; + _memory = memory; + _timeout = 3000; _submitTimeout = 0; - _timeslice = 0; + _timeslice = 0; _host1xContext = GetHost1XContext(context.Device.Gpu, owner); - _contextId = _host1xContext.Host1x.CreateContext(); - Channel = _device.Gpu.CreateChannel(); + _contextId = _host1xContext.Host1x.CreateContext(); + Channel = _device.Gpu.CreateChannel(); ChannelInitialization.InitializeState(Channel); @@ -143,12 +145,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private NvInternalResult Submit(Span<byte> arguments) { - SubmitArguments submitHeader = GetSpanAndSkip<SubmitArguments>(ref arguments, 1)[0]; - Span<CommandBuffer> commandBuffers = GetSpanAndSkip<CommandBuffer>(ref arguments, submitHeader.CmdBufsCount); - Span<Reloc> relocs = GetSpanAndSkip<Reloc>(ref arguments, submitHeader.RelocsCount); - Span<uint> relocShifts = GetSpanAndSkip<uint>(ref arguments, submitHeader.RelocsCount); - Span<SyncptIncr> syncptIncrs = GetSpanAndSkip<SyncptIncr>(ref arguments, submitHeader.SyncptIncrsCount); - Span<uint> fenceThresholds = GetSpanAndSkip<uint>(ref arguments, submitHeader.FencesCount); + SubmitArguments submitHeader = GetSpanAndSkip<SubmitArguments>(ref arguments, 1)[0]; + Span<CommandBuffer> commandBuffers = GetSpanAndSkip<CommandBuffer>(ref arguments, submitHeader.CmdBufsCount); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + Span<Reloc> relocs = GetSpanAndSkip<Reloc>(ref arguments, submitHeader.RelocsCount); + Span<uint> relocShifts = GetSpanAndSkip<uint>(ref arguments, submitHeader.RelocsCount); +#pragma warning restore IDE0059 + Span<SyncptIncr> syncptIncrs = GetSpanAndSkip<SyncptIncr>(ref arguments, submitHeader.SyncptIncrsCount); + Span<uint> fenceThresholds = GetSpanAndSkip<uint>(ref arguments, submitHeader.FencesCount); lock (_device) { @@ -176,9 +180,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private Span<T> GetSpanAndSkip<T>(ref Span<byte> arguments, int count) where T : unmanaged { - Span<T> output = MemoryMarshal.Cast<byte, T>(arguments).Slice(0, count); + Span<T> output = MemoryMarshal.Cast<byte, T>(arguments)[..count]; - arguments = arguments.Slice(Unsafe.SizeOf<T>() * count); + arguments = arguments[(Unsafe.SizeOf<T>() * count)..]; return output; } @@ -227,9 +231,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private NvInternalResult MapCommandBuffer(Span<byte> arguments) { - int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>(); - MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0]; - Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); + int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>(); + MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0]; + Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments[headerSize..])[..commandBufferHeader.NumEntries]; foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) { @@ -269,9 +273,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private NvInternalResult UnmapCommandBuffer(Span<byte> arguments) { - int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>(); - MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0]; - Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); + int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>(); + MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0]; + Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments[headerSize..])[..commandBufferHeader.NumEntries]; foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) { @@ -320,9 +324,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private NvInternalResult SubmitGpfifo(Span<byte> arguments) { - int headerSize = Unsafe.SizeOf<SubmitGpfifoArguments>(); + int headerSize = Unsafe.SizeOf<SubmitGpfifoArguments>(); SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast<byte, SubmitGpfifoArguments>(arguments)[0]; - Span<ulong> gpfifoEntries = MemoryMarshal.Cast<byte, ulong>(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries); + Span<ulong> gpfifoEntries = MemoryMarshal.Cast<byte, ulong>(arguments[headerSize..])[..gpfifoSubmissionHeader.NumEntries]; return SubmitGpfifo(ref gpfifoSubmissionHeader, gpfifoEntries); } @@ -473,7 +477,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel return ChannelSyncpoints[index]; } - public static uint GetSyncpointDevice(NvHostSyncpt syncpointManager, uint index, bool isClientManaged) + public uint GetSyncpointDevice(NvHostSyncpt syncpointManager, uint index, bool isClientManaged) { if (DeviceSyncpoints[index] != 0) { diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs index f33cc4601..5a1d1a68d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs @@ -8,9 +8,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { internal class NvHostGpuDeviceFile : NvHostChannelDeviceFile { - private KEvent _smExceptionBptIntReportEvent; - private KEvent _smExceptionBptPauseReportEvent; - private KEvent _errorNotifierEvent; +#pragma warning disable IDE0052 // Remove unread private member + private readonly KEvent _smExceptionBptIntReportEvent; + private readonly KEvent _smExceptionBptPauseReportEvent; + private readonly KEvent _errorNotifierEvent; +#pragma warning restore IDE0052 private int _smExceptionBptIntReportEventHandle; private int _smExceptionBptPauseReportEventHandle; @@ -18,14 +20,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel public NvHostGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, memory, owner) { - _smExceptionBptIntReportEvent = CreateEvent(context, out _smExceptionBptIntReportEventHandle); + _smExceptionBptIntReportEvent = CreateEvent(context, out _smExceptionBptIntReportEventHandle); _smExceptionBptPauseReportEvent = CreateEvent(context, out _smExceptionBptPauseReportEventHandle); - _errorNotifierEvent = CreateEvent(context, out _errorNotifierEventHandle); + _errorNotifierEvent = CreateEvent(context, out _errorNotifierEventHandle); } - private static KEvent CreateEvent(ServiceCtx context, out int handle) + private KEvent CreateEvent(ServiceCtx context, out int handle) { - KEvent evnt = new KEvent(context.Device.System.KernelContext); + KEvent evnt = new(context.Device.System.KernelContext); if (context.Process.HandleTable.GenerateHandle(evnt.ReadableEvent, out handle) != Result.Success) { @@ -55,22 +57,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel public override NvInternalResult QueryEvent(out int eventHandle, uint eventId) { // TODO: accurately represent and implement those events. - switch (eventId) + eventHandle = eventId switch { - case 0x1: - eventHandle = _smExceptionBptIntReportEventHandle; - break; - case 0x2: - eventHandle = _smExceptionBptPauseReportEventHandle; - break; - case 0x3: - eventHandle = _errorNotifierEventHandle; - break; - default: - eventHandle = 0; - break; - } - + 0x1 => _smExceptionBptIntReportEventHandle, + 0x2 => _smExceptionBptPauseReportEventHandle, + 0x3 => _errorNotifierEventHandle, + _ => 0, + }; return eventHandle != 0 ? NvInternalResult.Success : NvInternalResult.InvalidInput; } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs index 8e5a15235..37577a5f3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs @@ -6,12 +6,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types [StructLayout(LayoutKind.Sequential)] struct AllocGpfifoExArguments { - public uint NumEntries; - public uint NumJobs; - public uint Flags; + public uint NumEntries; + public uint NumJobs; + public uint Flags; public NvFence Fence; - public uint Reserved1; - public uint Reserved2; - public uint Reserved3; + public uint Reserved1; + public uint Reserved2; + public uint Reserved3; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs index fae91622c..1ffbb68fc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types [StructLayout(LayoutKind.Sequential)] struct AllocObjCtxArguments { - public uint ClassNumber; - public uint Flags; + public uint ClassNumber; + public uint Flags; public ulong ObjectId; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs index 6a7e3da80..0777e6461 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs @@ -12,10 +12,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types [StructLayout(LayoutKind.Sequential, Pack = 1)] struct MapCommandBufferArguments { - public int NumEntries; - public int DataAddress; // Ignored by the driver. - public bool AttachHostChDas; - public byte Padding1; + public int NumEntries; + public int DataAddress; // Ignored by the driver. + public bool AttachHostChDas; + public byte Padding1; public short Padding2; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs index 8e2c6ca32..177f483f0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannel.cs @@ -2,10 +2,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { class NvChannel { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public int Timeout; public int SubmitTimeout; public int Timeslice; #pragma warning restore CS0649 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs index 4112a9fcc..c267e2df9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs @@ -2,8 +2,8 @@ { enum NvChannelPriority : uint { - Low = 50, + Low = 50, Medium = 100, - High = 150 + High = 150, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs index 1aba53ca6..7e5a5e961 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types { public ulong Offset; public ulong Size; - public uint Mem; - public uint Reserved; + public uint Mem; + public uint Reserved; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs index a10abd4b5..1bb08da1b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types [StructLayout(LayoutKind.Sequential)] struct SubmitGpfifoArguments { - public long Address; - public int NumEntries; + public long Address; + public int NumEntries; public SubmitGpfifoFlags Flags; - public NvFence Fence; + public NvFence Fence; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs index d81fd3862..1f0609804 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoFlags.cs @@ -6,10 +6,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types enum SubmitGpfifoFlags : uint { None, - FenceWait = 1 << 0, - FenceIncrement = 1 << 1, - HwFormat = 1 << 2, - SuppressWfi = 1 << 4, + FenceWait = 1 << 0, + FenceIncrement = 1 << 1, + HwFormat = 1 << 2, + SuppressWfi = 1 << 4, IncrementWithValue = 1 << 8, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs index 19a997f43..71b124667 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types struct ZcullBindArguments { public ulong GpuVirtualAddress; - public uint Mode; - public uint Reserved; + public uint Mode; + public uint Reserved; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs index f130c4558..6142611e5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs @@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl { public const int EventsCount = 64; - private bool _isProductionMode; - private Switch _device; - private NvHostEvent[] _events; + private readonly bool _isProductionMode; + private readonly Switch _device; + private readonly NvHostEvent[] _events; public NvHostCtrlDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs index 16f970e86..84f678bab 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types struct EventWaitArguments { public NvFence Fence; - public int Timeout; - public uint Value; + public int Timeout; + public uint Value; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs index 3ee318a37..a1400f8ef 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs @@ -11,14 +11,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types public static GetConfigurationArguments FromSpan(Span<byte> span) { - string domain = Encoding.ASCII.GetString(span.Slice(0, 0x41)); + string domain = Encoding.ASCII.GetString(span[..0x41]); string parameter = Encoding.ASCII.GetString(span.Slice(0x41, 0x41)); - GetConfigurationArguments result = new GetConfigurationArguments + GetConfigurationArguments result = new() { - Domain = domain.Substring(0, domain.IndexOf('\0')), - Parameter = parameter.Substring(0, parameter.IndexOf('\0')), - Configuration = span.Slice(0x82, 0x101).ToArray() + Domain = domain[..domain.IndexOf('\0')], + Parameter = parameter[..parameter.IndexOf('\0')], + Configuration = span.Slice(0x82, 0x101).ToArray(), }; return result; @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types public void CopyTo(Span<byte> span) { - Encoding.ASCII.GetBytes(Domain + '\0').CopyTo(span.Slice(0, 0x41)); + Encoding.ASCII.GetBytes(Domain + '\0').CopyTo(span[..0x41]); Encoding.ASCII.GetBytes(Parameter + '\0').CopyTo(span.Slice(0x41, 0x41)); Configuration.CopyTo(span.Slice(0x82, 0x101)); } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs index 383fb3fbe..8f851f37a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs @@ -12,17 +12,19 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl { class NvHostEvent { - public NvFence Fence; + public NvFence Fence; public NvHostEventState State; - public KEvent Event; - public int EventHandle; + public KEvent Event; + public int EventHandle; - private uint _eventId; - private NvHostSyncpt _syncpointManager; + private readonly uint _eventId; +#pragma warning disable IDE0052 // Remove unread private member + private readonly NvHostSyncpt _syncpointManager; +#pragma warning restore IDE0052 private SyncpointWaiterHandle _waiterInformation; private NvFence _previousFailingFence; - private uint _failingCount; + private uint _failingCount; public readonly object Lock = new(); @@ -54,9 +56,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl private void ResetFailingState() { - _previousFailingFence.Id = NvFence.InvalidSyncPointId; + _previousFailingFence.Id = NvFence.InvalidSyncPointId; _previousFailingFence.Value = 0; - _failingCount = 0; + _failingCount = 0; } private void Signal() @@ -182,4 +184,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs index c7b4bc9fe..57c99d61f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEventState.cs @@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl { enum NvHostEventState { - Available = 0, - Waiting = 1, + Available = 0, + Waiting = 1, Cancelling = 2, - Signaling = 3, - Signaled = 4, - Cancelled = 5 + Signaling = 3, + Signaled = 4, + Cancelled = 5, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs index 1b842aa17..9c6d025eb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs @@ -10,22 +10,22 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl public const int VBlank0SyncpointId = 26; public const int VBlank1SyncpointId = 27; - private int[] _counterMin; - private int[] _counterMax; - private bool[] _clientManaged; - private bool[] _assigned; + private readonly int[] _counterMin; + private readonly int[] _counterMax; + private readonly bool[] _clientManaged; + private readonly bool[] _assigned; - private Switch _device; + private readonly Switch _device; private readonly object _syncpointAllocatorLock = new(); public NvHostSyncpt(Switch device) { - _device = device; - _counterMin = new int[SynchronizationManager.MaxHardwareSyncpoints]; - _counterMax = new int[SynchronizationManager.MaxHardwareSyncpoints]; + _device = device; + _counterMin = new int[SynchronizationManager.MaxHardwareSyncpoints]; + _counterMax = new int[SynchronizationManager.MaxHardwareSyncpoints]; _clientManaged = new bool[SynchronizationManager.MaxHardwareSyncpoints]; - _assigned = new bool[SynchronizationManager.MaxHardwareSyncpoints]; + _assigned = new bool[SynchronizationManager.MaxHardwareSyncpoints]; // Reserve VBLANK syncpoints ReserveSyncpointLocked(VBlank0SyncpointId, true); @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl throw new ArgumentOutOfRangeException(nameof(id)); } - _assigned[id] = true; + _assigned[id] = true; _clientManaged[id] = isClientManaged; } @@ -76,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl throw new ArgumentOutOfRangeException(nameof(id)); } - _assigned[id] = false; + _assigned[id] = false; _clientManaged[id] = false; SetSyncpointMinEqualSyncpointMax(id); @@ -196,4 +196,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs index cda97f18a..b0efb7eda 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs @@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types struct SyncptWaitArguments { public NvFence Fence; - public int Timeout; + public int Timeout; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs index f2279c3de..7471274ba 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs @@ -6,6 +6,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types struct SyncptWaitExArguments { public SyncptWaitArguments Input; - public uint Value; + public uint Value; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs index d6a8e29fb..23cf1f002 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs @@ -10,15 +10,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu { class NvHostCtrlGpuDeviceFile : NvDeviceFile { - private static Stopwatch _pTimer = new Stopwatch(); - private static double _ticksToNs = (1.0 / Stopwatch.Frequency) * 1_000_000_000; + private static readonly Stopwatch _pTimer = new(); + private static readonly double _ticksToNs = (1.0 / Stopwatch.Frequency) * 1_000_000_000; - private KEvent _errorEvent; - private KEvent _unknownEvent; + private readonly KEvent _errorEvent; + private readonly KEvent _unknownEvent; public NvHostCtrlGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { - _errorEvent = new KEvent(context.Device.System.KernelContext); + _errorEvent = new KEvent(context.Device.System.KernelContext); _unknownEvent = new KEvent(context.Device.System.KernelContext); } @@ -125,6 +125,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu private NvInternalResult ZcullGetInfo(ref ZcullGetInfoArguments arguments) { +#pragma warning disable IDE0055 // Disable formatting arguments.WidthAlignPixels = 0x20; arguments.HeightAlignPixels = 0x20; arguments.PixelSquaresByAliquots = 0x400; @@ -135,6 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu arguments.SubregionWidthAlignPixels = 0x20; arguments.SubregionHeightAlignPixels = 0x40; arguments.SubregionCount = 0x10; +#pragma warning restore IDE0055 return NvInternalResult.Success; } @@ -155,6 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu { arguments.Header.BufferSize = 0xa0; +#pragma warning disable IDE0055 // Disable formatting characteristics.Arch = 0x120; characteristics.Impl = 0xb; characteristics.Rev = 0xa1; @@ -190,6 +193,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu characteristics.RopL2EnMask1 = 0x0; characteristics.ChipName = 0x6230326d67; characteristics.GrCompbitStoreBaseHw = 0x0; +#pragma warning restore IDE0055 arguments.Characteristics = characteristics; @@ -205,7 +209,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu { if (arguments.MaskBufferSize != 0) { - tpcMask = 3; + tpcMask = 3; arguments.TpcMask = tpcMask; } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs index d66481781..64bfbe88d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs @@ -5,46 +5,46 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types [StructLayout(LayoutKind.Sequential)] struct GpuCharacteristics { - public int Arch; - public int Impl; - public int Rev; - public int NumGpc; + public int Arch; + public int Impl; + public int Rev; + public int NumGpc; public long L2CacheSize; public long OnBoardVideoMemorySize; - public int NumTpcPerGpc; - public int BusType; - public int BigPageSize; - public int CompressionPageSize; - public int PdeCoverageBitCount; - public int AvailableBigPageSizes; - public int GpcMask; - public int SmArchSmVersion; - public int SmArchSpaVersion; - public int SmArchWarpCount; - public int GpuVaBitCount; - public int Reserved; + public int NumTpcPerGpc; + public int BusType; + public int BigPageSize; + public int CompressionPageSize; + public int PdeCoverageBitCount; + public int AvailableBigPageSizes; + public int GpcMask; + public int SmArchSmVersion; + public int SmArchSpaVersion; + public int SmArchWarpCount; + public int GpuVaBitCount; + public int Reserved; public long Flags; - public int TwodClass; - public int ThreedClass; - public int ComputeClass; - public int GpfifoClass; - public int InlineToMemoryClass; - public int DmaCopyClass; - public int MaxFbpsCount; - public int FbpEnMask; - public int MaxLtcPerFbp; - public int MaxLtsPerLtc; - public int MaxTexPerTpc; - public int MaxGpcCount; - public int RopL2EnMask0; - public int RopL2EnMask1; + public int TwodClass; + public int ThreedClass; + public int ComputeClass; + public int GpfifoClass; + public int InlineToMemoryClass; + public int DmaCopyClass; + public int MaxFbpsCount; + public int FbpEnMask; + public int MaxLtcPerFbp; + public int MaxLtsPerLtc; + public int MaxTexPerTpc; + public int MaxGpcCount; + public int RopL2EnMask0; + public int RopL2EnMask1; public long ChipName; public long GrCompbitStoreBaseHw; } struct CharacteristicsHeader { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public long BufferSize; public long BufferAddress; #pragma warning restore CS0649 @@ -54,6 +54,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types struct GetCharacteristicsArguments { public CharacteristicsHeader Header; - public GpuCharacteristics Characteristics; + public GpuCharacteristics Characteristics; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs index 16ef2d6e1..dafde6e55 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs @@ -6,10 +6,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types [StructLayout(LayoutKind.Sequential)] struct GetTpcMasksArguments { - public int MaskBufferSize; - public int Reserved; + public int MaskBufferSize; + public int Reserved; public long MaskBufferAddress; - public int TpcMask; - public int Padding; + public int TpcMask; + public int Padding; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs index a4651f844..93c264332 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Memory; -using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs index fe302b98e..667915787 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostDbgGpu/NvHostDbgGpuDeviceFile.cs @@ -1,5 +1,5 @@ using Ryujinx.Memory; -using System; + namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostDbgGpu { class NvHostDbgGpuDeviceFile : NvDeviceFile diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs index 9a3aa7aa1..c7746a556 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs @@ -2,31 +2,31 @@ { enum NvInternalResult { - Success = 0, + Success = 0, OperationNotPermitted = -1, - NoEntry = -2, - Interrupted = -4, - IoError = -5, - DeviceNotFound = -6, - BadFileNumber = -9, - TryAgain = -11, - OutOfMemory = -12, - AccessDenied = -13, - BadAddress = -14, - Busy = -16, - NotADirectory = -20, - InvalidInput = -22, - FileTableOverflow = -23, - Unknown0x18 = -24, - NotSupported = -25, - FileTooBig = -27, - NoSpaceLeft = -28, - ReadOnlyAttribute = -30, - NotImplemented = -38, - InvalidState = -40, - Restart = -85, - InvalidAddress = -99, - TimedOut = -110, - Unknown0x72 = -114, + NoEntry = -2, + Interrupted = -4, + IoError = -5, + DeviceNotFound = -6, + BadFileNumber = -9, + TryAgain = -11, + OutOfMemory = -12, + AccessDenied = -13, + BadAddress = -14, + Busy = -16, + NotADirectory = -20, + InvalidInput = -22, + FileTableOverflow = -23, + Unknown0x18 = -24, + NotSupported = -25, + FileTooBig = -27, + NoSpaceLeft = -28, + ReadOnlyAttribute = -30, + NotImplemented = -38, + InvalidState = -40, + Restart = -85, + InvalidAddress = -99, + TimedOut = -110, + Unknown0x72 = -114, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs index a52b36a22..06df5f93c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { private const int FlagNotFreedYet = 1; - private static NvMapIdDictionary _maps = new NvMapIdDictionary(); + private static readonly NvMapIdDictionary _maps = new(); public NvMapDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { @@ -125,8 +125,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { map.Allocated = true; - map.Align = arguments.Align; - map.Kind = (byte)arguments.Kind; + map.Align = arguments.Align; + map.Kind = (byte)arguments.Kind; int size = BitUtils.AlignUp(map.Size, (int)MemoryManager.PageSize); @@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap if (result == NvInternalResult.Success) { - map.Size = size; + map.Size = size; map.Address = address; } } @@ -164,12 +164,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap if (DecrementMapRefCount(Owner, arguments.Handle)) { arguments.Address = map.Address; - arguments.Flags = 0; + arguments.Flags = 0; } else { arguments.Address = 0; - arguments.Flags = FlagNotFreedYet; + arguments.Flags = FlagNotFreedYet; } arguments.Size = map.Size; @@ -190,15 +190,26 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap switch (arguments.Param) { - case NvMapHandleParam.Size: arguments.Result = map.Size; break; - case NvMapHandleParam.Align: arguments.Result = map.Align; break; - case NvMapHandleParam.Heap: arguments.Result = 0x40000000; break; - case NvMapHandleParam.Kind: arguments.Result = map.Kind; break; - case NvMapHandleParam.Compr: arguments.Result = 0; break; + case NvMapHandleParam.Size: + arguments.Result = map.Size; + break; + case NvMapHandleParam.Align: + arguments.Result = map.Align; + break; + case NvMapHandleParam.Heap: + arguments.Result = 0x40000000; + break; + case NvMapHandleParam.Kind: + arguments.Result = map.Kind; + break; + case NvMapHandleParam.Compr: + arguments.Result = 0; + break; // Note: Base is not supported and returns an error. // Any other value also returns an error. - default: return NvInternalResult.InvalidInput; + default: + return NvInternalResult.InvalidInput; } return NvInternalResult.Success; diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs index 2ec75fc96..dc4f5d608 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs @@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap [StructLayout(LayoutKind.Sequential)] struct NvMapAlloc { - public int Handle; - public int HeapMask; - public int Flags; - public int Align; - public long Kind; + public int Handle; + public int HeapMask; + public int Flags; + public int Align; + public long Kind; public ulong Address; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs index b47e46294..5380c45c7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs @@ -8,4 +8,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap public int Size; public int Handle; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs index 34bcbc645..b0b3fa2d6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs @@ -5,10 +5,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap [StructLayout(LayoutKind.Sequential)] struct NvMapFree { - public int Handle; - public int Padding; + public int Handle; + public int Padding; public ulong Address; - public int Size; - public int Flags; + public int Size; + public int Flags; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs index 2e559534d..9ec81f9f3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs @@ -8,4 +8,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap public int Id; public int Handle; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs index fe574eea5..8306ae4ca 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs @@ -8,4 +8,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap public int Id; public int Handle; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs index c97cee492..301179747 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs @@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { class NvMapHandle { -#pragma warning disable CS0649 - public int Handle; - public int Id; +#pragma warning disable CS0649 // Field is never assigned to + public int Handle; + public int Id; #pragma warning restore CS0649 - public int Size; - public int Align; - public int Kind; + public int Size; + public int Align; + public int Kind; public ulong Address; - public bool Allocated; + public bool Allocated; public ulong DmaMapAddress; private long _dupes; @@ -37,4 +37,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap return Interlocked.Decrement(ref _dupes); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs index 9eb7efff9..21393e7a4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs @@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { enum NvMapHandleParam { - Size = 1, + Size = 1, Align = 2, - Base = 3, - Heap = 4, - Kind = 5, - Compr = 6 + Base = 3, + Heap = 4, + Kind = 5, + Compr = 6, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs index c4733e948..1b4d8dd42 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapIdDictionary.cs @@ -58,4 +58,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap return values; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs index de5bab770..16fd78043 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap [StructLayout(LayoutKind.Sequential)] struct NvMapParam { - public int Handle; + public int Handle; public NvMapHandleParam Param; - public int Result; + public int Result; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs index 058586949..c9218e677 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs @@ -7,39 +7,39 @@ namespace Ryujinx.HLE.HOS.Services.Nv struct NvIoctl { public const int NvHostCustomMagic = 0x00; - public const int NvMapCustomMagic = 0x01; - public const int NvGpuAsMagic = 0x41; - public const int NvGpuMagic = 0x47; - public const int NvHostMagic = 0x48; + public const int NvMapCustomMagic = 0x01; + public const int NvGpuAsMagic = 0x41; + public const int NvGpuMagic = 0x47; + public const int NvHostMagic = 0x48; - private const int NumberBits = 8; - private const int TypeBits = 8; - private const int SizeBits = 14; + private const int NumberBits = 8; + private const int TypeBits = 8; + private const int SizeBits = 14; private const int DirectionBits = 2; - private const int NumberShift = 0; - private const int TypeShift = NumberShift + NumberBits; - private const int SizeShift = TypeShift + TypeBits; + private const int NumberShift = 0; + private const int TypeShift = NumberShift + NumberBits; + private const int SizeShift = TypeShift + TypeBits; private const int DirectionShift = SizeShift + SizeBits; - private const int NumberMask = (1 << NumberBits) - 1; - private const int TypeMask = (1 << TypeBits) - 1; - private const int SizeMask = (1 << SizeBits) - 1; + private const int NumberMask = (1 << NumberBits) - 1; + private const int TypeMask = (1 << TypeBits) - 1; + private const int SizeMask = (1 << SizeBits) - 1; private const int DirectionMask = (1 << DirectionBits) - 1; [Flags] public enum Direction : uint { - None = 0, - Read = 1, + None = 0, + Read = 1, Write = 2, } public uint RawValue; - public uint Number => (RawValue >> NumberShift) & NumberMask; - public uint Type => (RawValue >> TypeShift) & TypeMask; - public uint Size => (RawValue >> SizeShift) & SizeMask; - public Direction DirectionValue => (Direction)((RawValue >> DirectionShift) & DirectionMask); + public readonly uint Number => (RawValue >> NumberShift) & NumberMask; + public readonly uint Type => (RawValue >> TypeShift) & TypeMask; + public readonly uint Size => (RawValue >> SizeShift) & SizeMask; + public readonly Direction DirectionValue => (Direction)((RawValue >> DirectionShift) & DirectionMask); } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs index 341b5e576..66c953a2d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs @@ -20,10 +20,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv // Key --> Start Address of Region // Value --> End Address of Region - private readonly TreeDictionary<ulong, ulong> _tree = new TreeDictionary<ulong, ulong>(); + private readonly TreeDictionary<ulong, ulong> _tree = new(); - private readonly Dictionary<ulong, LinkedListNode<ulong>> _dictionary = new Dictionary<ulong, LinkedListNode<ulong>>(); - private readonly LinkedList<ulong> _list = new LinkedList<ulong>(); + private readonly Dictionary<ulong, LinkedListNode<ulong>> _dictionary = new(); + private readonly LinkedList<ulong> _list = new(); public NvMemoryAllocator() { diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs index 664610a43..5af613cd6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types public uint Id; public uint Value; - public bool IsValid() + public readonly bool IsValid() { return Id != InvalidSyncPointId; } @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types Value = gpuContext.Synchronization.IncrementSyncpoint(Id); } - public bool Wait(GpuContext gpuContext, TimeSpan timeout) + public readonly bool Wait(GpuContext gpuContext, TimeSpan timeout) { if (IsValid()) { diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs index 9404c18ce..7af7e5337 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types { class NvIoctlNotImplementedException : Exception { - public ServiceCtx Context { get; } + public ServiceCtx Context { get; } public NvDeviceFile DeviceFile { get; } - public NvIoctl Command { get; } + public NvIoctl Command { get; } public NvIoctlNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, NvIoctl command) : this(context, deviceFile, command, "The ioctl is not implemented.") @@ -17,9 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types public NvIoctlNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, NvIoctl command, string message) : base(message) { - Context = context; + Context = context; DeviceFile = deviceFile; - Command = command; + Command = command; } public override string Message @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types private string BuildMessage() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); sb.AppendLine($"Device File: {DeviceFile.GetType().Name}"); sb.AppendLine(); diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs index b7a72eba0..844bce135 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types { class NvQueryEventNotImplementedException : Exception { - public ServiceCtx Context { get; } + public ServiceCtx Context { get; } public NvDeviceFile DeviceFile { get; } - public uint EventId { get; } + public uint EventId { get; } public NvQueryEventNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, uint eventId) : this(context, deviceFile, eventId, "This query event is not implemented.") @@ -17,9 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types public NvQueryEventNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, uint eventId, string message) : base(message) { - Context = context; + Context = context; DeviceFile = deviceFile; - EventId = eventId; + EventId = eventId; } public override string Message @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types private string BuildMessage() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); sb.AppendLine($"Device File: {DeviceFile.GetType().Name}"); sb.AppendLine(); diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs index 1c9cae8ca..6f7e09a65 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs @@ -2,29 +2,29 @@ namespace Ryujinx.HLE.HOS.Services.Nv { enum NvResult : uint { - Success = 0, - NotImplemented = 1, - NotSupported = 2, - NotInitialized = 3, - InvalidParameter = 4, - Timeout = 5, - InsufficientMemory = 6, - ReadOnlyAttribute = 7, - InvalidState = 8, - InvalidAddress = 9, - InvalidSize = 10, - InvalidValue = 11, - AlreadyAllocated = 13, - Busy = 14, - ResourceError = 15, - CountMismatch = 16, - SharedMemoryTooSmall = 0x1000, - FileOperationFailed = 0x30003, + Success = 0, + NotImplemented = 1, + NotSupported = 2, + NotInitialized = 3, + InvalidParameter = 4, + Timeout = 5, + InsufficientMemory = 6, + ReadOnlyAttribute = 7, + InvalidState = 8, + InvalidAddress = 9, + InvalidSize = 10, + InvalidValue = 11, + AlreadyAllocated = 13, + Busy = 14, + ResourceError = 15, + CountMismatch = 16, + SharedMemoryTooSmall = 0x1000, + FileOperationFailed = 0x30003, DirectoryOperationFailed = 0x30004, NotAvailableInProduction = 0x30006, - IoctlFailed = 0x3000F, - AccessDenied = 0x30010, - FileNotFound = 0x30013, - ModuleNotPresent = 0xA000E, + IoctlFailed = 0x3000F, + AccessDenied = 0x30010, + FileNotFound = 0x30013, + ModuleNotPresent = 0xA000E, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs index d5c35265a..ad4f70354 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/Types/NvStatus.cs @@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.Types public long Padding1; public long Padding2; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs index 89fe0c3a2..073e9b1b9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForApplication.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Olsc [Service("olsc:u")] // 10.0.0+ class IOlscServiceForApplication : IpcService { - private bool _initialized; + private bool _initialized; private Dictionary<UserId, bool> _saveDataBackupSettingDatabase; public IOlscServiceForApplication(ServiceCtx context) { } @@ -65,8 +65,8 @@ namespace Ryujinx.HLE.HOS.Services.Olsc // SetSaveDataBackupSettingEnabled(nn::account::Uid, bool) public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context) { - bool saveDataBackupSettingEnabled = context.RequestData.ReadUInt64() != 0; - UserId userId = context.RequestData.ReadStruct<UserId>(); + bool saveDataBackupSettingEnabled = context.RequestData.ReadUInt64() != 0; + UserId userId = context.RequestData.ReadStruct<UserId>(); if (!_initialized) { @@ -87,4 +87,4 @@ namespace Ryujinx.HLE.HOS.Services.Olsc return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs index 52f74da92..27ac6b9cf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs @@ -5,4 +5,4 @@ { public IOlscServiceForSystemService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs index 141d1ae94..78392f6eb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/ResultCode.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Services.Olsc { enum ResultCode { - ModuleId = 179, + ModuleId = 179, ErrorCodeShift = 9, Success = 0, - NullArgument = (100 << ErrorCodeShift) | ModuleId, - NotInitialized = (101 << ErrorCodeShift) | ModuleId + NullArgument = (100 << ErrorCodeShift) | ModuleId, + NotInitialized = (101 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs b/src/Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs index 67b82e424..99e929a70 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ovln/IReceiverService.cs @@ -5,4 +5,4 @@ { public IReceiverService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs b/src/Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs index 70c860e1c..e445c16cd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ovln/ISenderService.cs @@ -5,4 +5,4 @@ { public ISenderService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs index 9c6387e15..78f927aa4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcie/ILogManager.cs @@ -5,4 +5,4 @@ { public ILogManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs index f189dc8c8..0450a1ca4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcie/IManager.cs @@ -5,4 +5,4 @@ { public IManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs index 990aef092..707f6423c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlServiceFactory.cs @@ -2,13 +2,13 @@ using Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory; namespace Ryujinx.HLE.HOS.Services.Pctl { - [Service("pctl", 0x303)] + [Service("pctl", 0x303)] [Service("pctl:a", 0x83BE)] [Service("pctl:r", 0x8040)] [Service("pctl:s", 0x838E)] class IParentalControlServiceFactory : IpcService { - private int _permissionFlag; + private readonly int _permissionFlag; public IParentalControlServiceFactory(ServiceCtx context, int permissionFlag) { @@ -37,4 +37,4 @@ namespace Ryujinx.HLE.HOS.Services.Pctl return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs index 594ee4e0e..cf8c1f78d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs @@ -1,31 +1,30 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Arp; using System; - using static LibHac.Ns.ApplicationControlProperty; namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory { class IParentalControlService : IpcService { - private ulong _pid; - private int _permissionFlag; - private ulong _titleId; + private readonly ulong _pid; + private readonly int _permissionFlag; + private ulong _titleId; private ParentalControlFlagValue _parentalControlFlag; - private int[] _ratingAge; +#pragma warning disable IDE0052, CS0414 // Remove unread private member + private int[] _ratingAge; -#pragma warning disable CS0414 // TODO: Find where they are set. - private bool _restrictionEnabled = false; - private bool _featuresRestriction = false; - private bool _freeCommunicationEnabled = false; - private bool _stereoVisionRestrictionConfigurable = true; - private bool _stereoVisionRestriction = false; -#pragma warning restore CS0414 + private readonly bool _restrictionEnabled = false; + private readonly bool _featuresRestriction = false; + private bool _freeCommunicationEnabled = false; + private readonly bool _stereoVisionRestrictionConfigurable = true; + private bool _stereoVisionRestriction = false; +#pragma warning restore IDE0052, CS0414 public IParentalControlService(ServiceCtx context, ulong pid, bool withInitialize, int permissionFlag) { - _pid = pid; + _pid = pid; _permissionFlag = permissionFlag; if (withInitialize) @@ -56,19 +55,19 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory _titleId = titleId; // TODO: Call nn::arp::GetApplicationControlProperty here when implemented, if it return ResultCode.Success we assign fields. - _ratingAge = Array.ConvertAll(context.Device.Processes.ActiveApplication.ApplicationControlProperties.RatingAge.ItemsRo.ToArray(), Convert.ToInt32); + _ratingAge = Array.ConvertAll(context.Device.Processes.ActiveApplication.ApplicationControlProperties.RatingAge.ItemsRo.ToArray(), Convert.ToInt32); _parentalControlFlag = context.Device.Processes.ActiveApplication.ApplicationControlProperties.ParentalControlFlag; } } if (_titleId != 0) { - // TODO: Service store some private fields in another static object. + // TODO: Service store some private fields in another object. if ((_permissionFlag & 0x8040) == 0) { - // TODO: Service store TitleId and FreeCommunicationEnabled in another static object. - // When it's done it signal an event in this static object. + // TODO: Service store TitleId and FreeCommunicationEnabled in another object. + // When it's done it signal an event in this object. Logger.Stub?.PrintStub(LogClass.ServicePctl); } } @@ -160,7 +159,9 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory } else { +#pragma warning disable CS0162 // Unreachable code return ResultCode.StereoVisionRestrictionConfigurableDisabled; +#pragma warning restore CS0162 } } @@ -173,7 +174,9 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.PermissionDenied; } +#pragma warning disable // Remove unnecessary value assignment bool stereoVisionRestriction = false; +#pragma warning restore IDE0059 if (_stereoVisionRestrictionConfigurable) { @@ -202,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory { _stereoVisionRestriction = stereoVisionRestriction; - // TODO: It signals an internal event of service. We have to determine where this event is used. + // TODO: It signals an internal event of service. We have to determine where this event is used. } } @@ -256,4 +259,4 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs index fcf06ee97..86ff88142 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pctl/ResultCode.cs @@ -2,15 +2,15 @@ { enum ResultCode { - ModuleId = 142, + ModuleId = 142, ErrorCodeShift = 9, Success = 0, - FreeCommunicationDisabled = (101 << ErrorCodeShift) | ModuleId, - StereoVisionDenied = (104 << ErrorCodeShift) | ModuleId, - InvalidPid = (131 << ErrorCodeShift) | ModuleId, - PermissionDenied = (133 << ErrorCodeShift) | ModuleId, + FreeCommunicationDisabled = (101 << ErrorCodeShift) | ModuleId, + StereoVisionDenied = (104 << ErrorCodeShift) | ModuleId, + InvalidPid = (131 << ErrorCodeShift) | ModuleId, + PermissionDenied = (133 << ErrorCodeShift) | ModuleId, StereoVisionRestrictionConfigurableDisabled = (181 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs index 7d0222d55..e078de39a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/Bpc/IBoardPowerControlManager.cs @@ -5,4 +5,4 @@ { public IBoardPowerControlManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs index b81e7fee0..a3ca56bf2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/ClkrstManager/IClkrstSession.cs @@ -6,32 +6,33 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst.ClkrstManager { class IClkrstSession : IpcService { - private DeviceCode _deviceCode; - private uint _unknown; - private uint _clockRate; + private readonly DeviceCode _deviceCode; +#pragma warning disable IDE0052 // Remove unread private member + private readonly uint _unknown; +#pragma warning restore IDE0052 + private uint _clockRate; - private DeviceCode[] allowedDeviceCodeTable = new DeviceCode[] - { + private readonly DeviceCode[] _allowedDeviceCodeTable = { DeviceCode.Cpu, DeviceCode.Gpu, DeviceCode.Disp1, DeviceCode.Disp2, DeviceCode.Tsec, DeviceCode.Mselect, DeviceCode.Sor1, DeviceCode.Host1x, DeviceCode.Vic, DeviceCode.Nvenc, DeviceCode.Nvjpg, DeviceCode.Nvdec, DeviceCode.Ape, DeviceCode.AudioDsp, DeviceCode.Emc, DeviceCode.Dsi, DeviceCode.SysBus, DeviceCode.XusbSs, DeviceCode.XusbHost, DeviceCode.XusbDevice, - DeviceCode.Gpuaux, DeviceCode.Pcie, DeviceCode.Apbdma, DeviceCode.Sdmmc1, - DeviceCode.Sdmmc2, DeviceCode.Sdmmc4 + DeviceCode.Gpuaux, DeviceCode.Pcie, DeviceCode.Apbdma, DeviceCode.Sdmmc1, + DeviceCode.Sdmmc2, DeviceCode.Sdmmc4, }; public IClkrstSession(DeviceCode deviceCode, uint unknown) { _deviceCode = deviceCode; - _unknown = unknown; + _unknown = unknown; } [CommandCmif(7)] // SetClockRate(u32 hz) public ResultCode SetClockRate(ServiceCtx context) { - if (!allowedDeviceCodeTable.Contains(_deviceCode)) + if (!_allowedDeviceCodeTable.Contains(_deviceCode)) { return ResultCode.InvalidArgument; } @@ -47,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst.ClkrstManager // GetClockRate() -> u32 hz public ResultCode GetClockRate(ServiceCtx context) { - if (!allowedDeviceCodeTable.Contains(_deviceCode)) + if (!_allowedDeviceCodeTable.Contains(_deviceCode)) { return ResultCode.InvalidArgument; } @@ -59,4 +60,4 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst.ClkrstManager return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs index 6f1e5d25d..492ffa57a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IArbitrationManager.cs @@ -5,4 +5,4 @@ { public IArbitrationManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs index 4ba2f0945..2e18dba74 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/Clkrst/IClkrstManager.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst public ResultCode OpenSession(ServiceCtx context) { DeviceCode deviceCode = (DeviceCode)context.RequestData.ReadUInt32(); - uint unknown = context.RequestData.ReadUInt32(); + uint unknown = context.RequestData.ReadUInt32(); // TODO: Service checks the deviceCode and the unk value. @@ -54,4 +54,4 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs index 0e74dc3e5..5fd8493c9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs @@ -5,4 +5,4 @@ { public IPcvService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs index 2041e423e..b1694b34d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/ResultCode.cs @@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Services.Pcv { enum ResultCode { - ModuleId = 30, + ModuleId = 30, ErrorCodeShift = 9, Success = 0, - InvalidArgument = (5 << ErrorCodeShift) | ModuleId + InvalidArgument = (5 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs index f7834777e..9a7d8a098 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/Rgltr/IRegulatorManager.cs @@ -5,4 +5,4 @@ { public IRegulatorManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs index 2b4a1239c..6e255a1dd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/Rtc/IRtcManager.cs @@ -5,4 +5,4 @@ { public IRtcManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs b/src/Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs index 5380d82fb..79f13dbdf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pcv/Types/DeviceCode.cs @@ -2,93 +2,93 @@ { enum DeviceCode { - Cpu = 0x40000001, - Gpu = 0x40000002, - I2s1 = 0x40000003, - I2s2 = 0x40000004, - I2s3 = 0x40000005, - Pwm = 0x40000006, - I2c1 = 0x02000001, - I2c2 = 0x02000002, - I2c3 = 0x02000003, - I2c4 = 0x02000004, - I2c5 = 0x02000005, - I2c6 = 0x02000006, - Spi1 = 0x07000000, - Spi2 = 0x07000001, - Spi3 = 0x07000002, - Spi4 = 0x07000003, - Disp1 = 0x40000011, - Disp2 = 0x40000012, - Isp = 0x40000013, - Vi = 0x40000014, - Sdmmc1 = 0x40000015, - Sdmmc2 = 0x40000016, - Sdmmc3 = 0x40000017, - Sdmmc4 = 0x40000018, - Owr = 0x40000019, - Csite = 0x4000001A, - Tsec = 0x4000001B, - Mselect = 0x4000001C, - Hda2codec2x = 0x4000001D, - Actmon = 0x4000001E, - I2cSlow = 0x4000001F, - Sor1 = 0x40000020, - Sata = 0x40000021, - Hda = 0x40000022, + Cpu = 0x40000001, + Gpu = 0x40000002, + I2s1 = 0x40000003, + I2s2 = 0x40000004, + I2s3 = 0x40000005, + Pwm = 0x40000006, + I2c1 = 0x02000001, + I2c2 = 0x02000002, + I2c3 = 0x02000003, + I2c4 = 0x02000004, + I2c5 = 0x02000005, + I2c6 = 0x02000006, + Spi1 = 0x07000000, + Spi2 = 0x07000001, + Spi3 = 0x07000002, + Spi4 = 0x07000003, + Disp1 = 0x40000011, + Disp2 = 0x40000012, + Isp = 0x40000013, + Vi = 0x40000014, + Sdmmc1 = 0x40000015, + Sdmmc2 = 0x40000016, + Sdmmc3 = 0x40000017, + Sdmmc4 = 0x40000018, + Owr = 0x40000019, + Csite = 0x4000001A, + Tsec = 0x4000001B, + Mselect = 0x4000001C, + Hda2codec2x = 0x4000001D, + Actmon = 0x4000001E, + I2cSlow = 0x4000001F, + Sor1 = 0x40000020, + Sata = 0x40000021, + Hda = 0x40000022, XusbCoreHostSrc = 0x40000023, - XusbFalconSrc = 0x40000024, - XusbFsSrc = 0x40000025, - XusbCoreDevSrc = 0x40000026, - XusbSsSrc = 0x40000027, - UartA = 0x03000001, - UartB = 0x35000405, - UartC = 0x3500040F, - UartD = 0x37000001, - Host1x = 0x4000002C, - Entropy = 0x4000002D, - SocTherm = 0x4000002E, - Vic = 0x4000002F, - Nvenc = 0x40000030, - Nvjpg = 0x40000031, - Nvdec = 0x40000032, - Qspi = 0x40000033, - ViI2c = 0x40000034, - Tsecb = 0x40000035, - Ape = 0x40000036, - AudioDsp = 0x40000037, - AudioUart = 0x40000038, - Emc = 0x40000039, - Plle = 0x4000003A, - PlleHwSeq = 0x4000003B, - Dsi = 0x4000003C, - Maud = 0x4000003D, - Dpaux1 = 0x4000003E, - MipiCal = 0x4000003F, - UartFstMipiCal = 0x40000040, - Osc = 0x40000041, - SysBus = 0x40000042, - SorSafe = 0x40000043, - XusbSs = 0x40000044, - XusbHost = 0x40000045, - XusbDevice = 0x40000046, - Extperiph1 = 0x40000047, - Ahub = 0x40000048, - Hda2hdmicodec = 0x40000049, - Gpuaux = 0x4000004A, - UsbD = 0x4000004B, - Usb2 = 0x4000004C, - Pcie = 0x4000004D, - Afi = 0x4000004E, - PciExClk = 0x4000004F, - PExUsbPhy = 0x40000050, - XUsbPadCtl = 0x40000051, - Apbdma = 0x40000052, - Usb2TrkClk = 0x40000053, - XUsbIoPll = 0x40000054, - XUsbIoPllHwSeq = 0x40000055, - Cec = 0x40000056, - Extperiph2 = 0x40000057, - OscClk = 0x40000080 + XusbFalconSrc = 0x40000024, + XusbFsSrc = 0x40000025, + XusbCoreDevSrc = 0x40000026, + XusbSsSrc = 0x40000027, + UartA = 0x03000001, + UartB = 0x35000405, + UartC = 0x3500040F, + UartD = 0x37000001, + Host1x = 0x4000002C, + Entropy = 0x4000002D, + SocTherm = 0x4000002E, + Vic = 0x4000002F, + Nvenc = 0x40000030, + Nvjpg = 0x40000031, + Nvdec = 0x40000032, + Qspi = 0x40000033, + ViI2c = 0x40000034, + Tsecb = 0x40000035, + Ape = 0x40000036, + AudioDsp = 0x40000037, + AudioUart = 0x40000038, + Emc = 0x40000039, + Plle = 0x4000003A, + PlleHwSeq = 0x4000003B, + Dsi = 0x4000003C, + Maud = 0x4000003D, + Dpaux1 = 0x4000003E, + MipiCal = 0x4000003F, + UartFstMipiCal = 0x40000040, + Osc = 0x40000041, + SysBus = 0x40000042, + SorSafe = 0x40000043, + XusbSs = 0x40000044, + XusbHost = 0x40000045, + XusbDevice = 0x40000046, + Extperiph1 = 0x40000047, + Ahub = 0x40000048, + Hda2hdmicodec = 0x40000049, + Gpuaux = 0x4000004A, + UsbD = 0x4000004B, + Usb2 = 0x4000004C, + Pcie = 0x4000004D, + Afi = 0x4000004E, + PciExClk = 0x4000004F, + PExUsbPhy = 0x40000050, + XUsbPadCtl = 0x40000051, + Apbdma = 0x40000052, + Usb2TrkClk = 0x40000053, + XUsbIoPll = 0x40000054, + XUsbIoPllHwSeq = 0x40000055, + Cec = 0x40000056, + Extperiph2 = 0x40000057, + OscClk = 0x40000080, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs b/src/Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs index 45771db6b..473f74135 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pm/IBootModeInterface.cs @@ -5,4 +5,4 @@ { public IBootModeInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs b/src/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs index cce2967a7..82190b047 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs @@ -46,4 +46,4 @@ namespace Ryujinx.HLE.HOS.Services.Pm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs b/src/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs index b3b5595fd..500d121ea 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs @@ -24,4 +24,4 @@ namespace Ryujinx.HLE.HOS.Services.Pm return ResultCode.ProcessNotFound; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs index 92b5925e4..9894cd204 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pm/ResultCode.cs @@ -2,16 +2,16 @@ namespace Ryujinx.HLE.HOS.Services.Pm { enum ResultCode { - ModuleId = 15, + ModuleId = 15, ErrorCodeShift = 9, Success = 0, - ProcessNotFound = (1 << ErrorCodeShift) | ModuleId, - AlreadyStarted = (2 << ErrorCodeShift) | ModuleId, - NotTerminated = (3 << ErrorCodeShift) | ModuleId, - DebugHookInUse = (4 << ErrorCodeShift) | ModuleId, + ProcessNotFound = (1 << ErrorCodeShift) | ModuleId, + AlreadyStarted = (2 << ErrorCodeShift) | ModuleId, + NotTerminated = (3 << ErrorCodeShift) | ModuleId, + DebugHookInUse = (4 << ErrorCodeShift) | ModuleId, ApplicationRunning = (5 << ErrorCodeShift) | ModuleId, - InvalidSize = (6 << ErrorCodeShift) | ModuleId, + InvalidSize = (6 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs b/src/Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs index 3810c2826..6682a8481 100644 --- a/src/Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Psc/IPmControl.cs @@ -5,4 +5,4 @@ { public IPmControl(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Psc/IPmService.cs b/src/Ryujinx.HLE/HOS/Services/Psc/IPmService.cs index c8dfb32e0..1be338660 100644 --- a/src/Ryujinx.HLE/HOS/Services/Psc/IPmService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Psc/IPmService.cs @@ -5,4 +5,4 @@ { public IPmService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs b/src/Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs index ef48fa41e..95aff9ece 100644 --- a/src/Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs +++ b/src/Ryujinx.HLE/HOS/Services/Psc/IPmUnknown.cs @@ -5,4 +5,4 @@ { public IPmUnknown(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs index e2fe22356..8011f9193 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Fan/IManager.cs @@ -5,4 +5,4 @@ { public IManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs index a93f52830..8cb671bc3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/IDebugger.cs @@ -5,4 +5,4 @@ { public IDebugger(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs index 0e3f965b3..1488c2886 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Fgm/ISession.cs @@ -7,4 +7,4 @@ { public ISession(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs index 0bec45fa3..53f3bc391 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Pcm/IManager.cs @@ -5,4 +5,4 @@ { public IManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs index 4e3d3e8e2..0c475150b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmServer.cs @@ -42,4 +42,4 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs index 5d11f227a..a603b7e41 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/IPsmSession.cs @@ -7,12 +7,12 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm { class IPsmSession : IpcService { - private KEvent _stateChangeEvent; - private int _stateChangeEventHandle; + private readonly KEvent _stateChangeEvent; + private int _stateChangeEventHandle; public IPsmSession(Horizon system) { - _stateChangeEvent = new KEvent(system.KernelContext); + _stateChangeEvent = new KEvent(system.KernelContext); _stateChangeEventHandle = -1; } @@ -85,4 +85,4 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Psm return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs index 3e239711d..a0b0c3dd4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Psm/Types/ChargerType.cs @@ -4,6 +4,6 @@ { None, ChargerOrDock, - UsbC + UsbC, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs index 1daa4f5e8..0317e532b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Tc/IManager.cs @@ -5,4 +5,4 @@ { public IManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs index 6ddc0aef2..07a067688 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs @@ -36,4 +36,4 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Ts return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs index e72491d51..c04db09ce 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs @@ -3,6 +3,6 @@ enum Location : byte { Internal, - External + External, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index 966adcffe..3f31fe9f8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -18,16 +18,16 @@ namespace Ryujinx.HLE.HOS.Services.Ro [Service("ro:1")] // 7.0.0+ class IRoInterface : DisposableIpcService { - private const int MaxNrr = 0x40; - private const int MaxNro = 0x40; - private const int MaxMapRetries = 0x200; + private const int MaxNrr = 0x40; + private const int MaxNro = 0x40; + private const int MaxMapRetries = 0x200; private const int GuardPagesSize = 0x4000; private const uint NrrMagic = 0x3052524E; private const uint NroMagic = 0x304F524E; - private List<NrrInfo> _nrrInfos; - private List<NroInfo> _nroInfos; + private readonly List<NrrInfo> _nrrInfos; + private readonly List<NroInfo> _nroInfos; private KProcess _owner; private IVirtualMemoryManager _ownerMm; @@ -36,8 +36,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro { _nrrInfos = new List<NrrInfo>(MaxNrr); _nroInfos = new List<NroInfo>(MaxNro); - _owner = null; - _ownerMm = null; + _owner = null; + _ownerMm = null; } private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize) @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.InvalidSize; } - List<byte[]> hashes = new List<byte[]>(); + List<byte[]> hashes = new(); for (int i = 0; i < header.HashesCount; i++) { @@ -130,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.InvalidAddress; } - uint magic = _owner.CpuMemory.Read<uint>(nroAddress + 0x10); + uint magic = _owner.CpuMemory.Read<uint>(nroAddress + 0x10); uint nroFileSize = _owner.CpuMemory.Read<uint>(nroAddress + 0x18); if (magic != NroMagic || nroSize != nroFileSize) @@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro _owner.CpuMemory.Read(nroAddress, nroData); - MemoryStream stream = new MemoryStream(nroData); + MemoryStream stream = new(nroData); byte[] nroHash = SHA256.HashData(stream); @@ -158,19 +158,19 @@ namespace Ryujinx.HLE.HOS.Services.Ro stream.Position = 0; - NroExecutable nro = new NroExecutable(stream.AsStorage(), nroAddress, bssAddress); + NroExecutable nro = new(stream.AsStorage(), nroAddress, bssAddress); // Check if everything is page align. if ((nro.Text.Length & 0xFFF) != 0 || (nro.Ro.Length & 0xFFF) != 0 || - (nro.Data.Length & 0xFFF) != 0 || (nro.BssSize & 0xFFF) != 0) + (nro.Data.Length & 0xFFF) != 0 || (nro.BssSize & 0xFFF) != 0) { return ResultCode.InvalidNro; } // Check if everything is contiguous. - if (nro.RoOffset != nro.TextOffset + nro.Text.Length || - nro.DataOffset != nro.RoOffset + nro.Ro.Length || - nroFileSize != nro.DataOffset + nro.Data.Length) + if (nro.RoOffset != nro.TextOffset + nro.Text.Length || + nro.DataOffset != nro.RoOffset + nro.Ro.Length || + nroFileSize != nro.DataOffset + nro.Data.Length) { return ResultCode.InvalidNro; } @@ -316,7 +316,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro private Result SetNroMemoryPermissions(KProcess process, IExecutable relocatableObject, ulong baseAddress) { ulong textStart = baseAddress + relocatableObject.TextOffset; - ulong roStart = baseAddress + relocatableObject.RoOffset; + ulong roStart = baseAddress + relocatableObject.RoOffset; ulong dataStart = baseAddress + relocatableObject.DataOffset; ulong bssStart = dataStart + (ulong)relocatableObject.Data.Length; @@ -324,7 +324,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro ulong bssEnd = BitUtils.AlignUp<ulong>(bssStart + relocatableObject.BssSize, KPageTableBase.PageSize); process.CpuMemory.Write(textStart, relocatableObject.Text); - process.CpuMemory.Write(roStart, relocatableObject.Ro); + process.CpuMemory.Write(roStart, relocatableObject.Ro); process.CpuMemory.Write(dataStart, relocatableObject.Data); MemoryHelper.FillWithZeros(process.CpuMemory, bssStart, (int)(bssEnd - bssStart)); @@ -381,9 +381,9 @@ namespace Ryujinx.HLE.HOS.Services.Ro private ResultCode UnmapNroFromInfo(NroInfo info) { ulong textSize = (ulong)info.Executable.Text.Length; - ulong roSize = (ulong)info.Executable.Ro.Length; + ulong roSize = (ulong)info.Executable.Ro.Length; ulong dataSize = (ulong)info.Executable.Data.Length; - ulong bssSize = (ulong)info.Executable.BssSize; + ulong bssSize = (ulong)info.Executable.BssSize; Result result = Result.Success; @@ -434,17 +434,16 @@ namespace Ryujinx.HLE.HOS.Services.Ro context.RequestData.ReadUInt64(); ulong nroHeapAddress = context.RequestData.ReadUInt64(); - ulong nroSize = context.RequestData.ReadUInt64(); + ulong nroSize = context.RequestData.ReadUInt64(); ulong bssHeapAddress = context.RequestData.ReadUInt64(); - ulong bssSize = context.RequestData.ReadUInt64(); + ulong bssSize = context.RequestData.ReadUInt64(); ulong nroMappedAddress = 0; if (result == ResultCode.Success) { - NroInfo info; - result = ParseNro(out info, context, nroHeapAddress, nroSize, bssHeapAddress, bssSize); + result = ParseNro(out NroInfo info, context, nroHeapAddress, nroSize, bssHeapAddress, bssSize); if (result == ResultCode.Success) { @@ -503,12 +502,11 @@ namespace Ryujinx.HLE.HOS.Services.Ro context.RequestData.ReadUInt64(); ulong nrrAddress = context.RequestData.ReadUInt64(); - ulong nrrSize = context.RequestData.ReadUInt64(); + ulong nrrSize = context.RequestData.ReadUInt64(); if (result == ResultCode.Success) { - NrrInfo info; - result = ParseNrr(out info, context, nrrAddress, nrrSize); + result = ParseNrr(out NrrInfo info, context, nrrAddress, nrrSize); if (result == ResultCode.Success) { @@ -599,4 +597,4 @@ namespace Ryujinx.HLE.HOS.Services.Ro } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs index 92bb55029..1e31e5760 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs @@ -2,26 +2,26 @@ { enum ResultCode { - ModuleId = 22, + ModuleId = 22, ErrorCodeShift = 9, Success = 0, InsufficientAddressSpace = (2 << ErrorCodeShift) | ModuleId, - AlreadyLoaded = (3 << ErrorCodeShift) | ModuleId, - InvalidNro = (4 << ErrorCodeShift) | ModuleId, - InvalidNrr = (6 << ErrorCodeShift) | ModuleId, - TooManyNro = (7 << ErrorCodeShift) | ModuleId, - TooManyNrr = (8 << ErrorCodeShift) | ModuleId, - NotAuthorized = (9 << ErrorCodeShift) | ModuleId, + AlreadyLoaded = (3 << ErrorCodeShift) | ModuleId, + InvalidNro = (4 << ErrorCodeShift) | ModuleId, + InvalidNrr = (6 << ErrorCodeShift) | ModuleId, + TooManyNro = (7 << ErrorCodeShift) | ModuleId, + TooManyNrr = (8 << ErrorCodeShift) | ModuleId, + NotAuthorized = (9 << ErrorCodeShift) | ModuleId, - InvalidNrrType = (10 << ErrorCodeShift) | ModuleId, + InvalidNrrType = (10 << ErrorCodeShift) | ModuleId, - InvalidAddress = (1025 << ErrorCodeShift) | ModuleId, - InvalidSize = (1026 << ErrorCodeShift) | ModuleId, - NotLoaded = (1028 << ErrorCodeShift) | ModuleId, - NotRegistered = (1029 << ErrorCodeShift) | ModuleId, - InvalidSession = (1030 << ErrorCodeShift) | ModuleId, - InvalidProcess = (1031 << ErrorCodeShift) | ModuleId, + InvalidAddress = (1025 << ErrorCodeShift) | ModuleId, + InvalidSize = (1026 << ErrorCodeShift) | ModuleId, + NotLoaded = (1028 << ErrorCodeShift) | ModuleId, + NotRegistered = (1029 << ErrorCodeShift) | ModuleId, + InvalidSession = (1030 << ErrorCodeShift) | ModuleId, + InvalidProcess = (1031 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs index 45daf1bda..fd4947c6f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs @@ -6,30 +6,30 @@ namespace Ryujinx.HLE.HOS.Services.Ro { public NroExecutable Executable { get; private set; } - public byte[] Hash { get; private set; } - public ulong NroAddress { get; private set; } - public ulong NroSize { get; private set; } - public ulong BssAddress { get; private set; } - public ulong BssSize { get; private set; } - public ulong TotalSize { get; private set; } - public ulong NroMappedAddress { get; set; } + public byte[] Hash { get; private set; } + public ulong NroAddress { get; private set; } + public ulong NroSize { get; private set; } + public ulong BssAddress { get; private set; } + public ulong BssSize { get; private set; } + public ulong TotalSize { get; private set; } + public ulong NroMappedAddress { get; set; } public NroInfo( - NroExecutable executable, - byte[] hash, - ulong nroAddress, - ulong nroSize, - ulong bssAddress, - ulong bssSize, - ulong totalSize) + NroExecutable executable, + byte[] hash, + ulong nroAddress, + ulong nroSize, + ulong bssAddress, + ulong bssSize, + ulong totalSize) { Executable = executable; - Hash = hash; + Hash = hash; NroAddress = nroAddress; - NroSize = nroSize; + NroSize = nroSize; BssAddress = bssAddress; - BssSize = bssSize; - TotalSize = totalSize; + BssSize = bssSize; + TotalSize = totalSize; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs index 45c34f1c5..b322c06ed 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs @@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Services.Ro { class NrrInfo { - public NrrHeader Header { get; private set; } - public List<byte[]> Hashes { get; private set; } - public ulong NrrAddress { get; private set; } + public NrrHeader Header { get; private set; } + public List<byte[]> Hashes { get; private set; } + public ulong NrrAddress { get; private set; } public NrrInfo(ulong nrrAddress, NrrHeader header, List<byte[]> hashes) { NrrAddress = nrrAddress; - Header = header; - Hashes = hashes; + Header = header; + Hashes = hashes; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs index d65c8bbae..ec312108b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Avm/IAvmService.cs @@ -5,4 +5,4 @@ { public IAvmService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs index 5247a238d..c2e042901 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/INotifyService.cs @@ -5,4 +5,4 @@ { public INotifyService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs index 1f66ff9de..c46050b85 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs @@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm return QueryPlayStatisticsManager.GetPlayStatistics(context, true); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs index 52a07d466..7017cc50c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs @@ -9,17 +9,17 @@ using System.Runtime.CompilerServices; namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService { - static class QueryPlayStatisticsManager + class QueryPlayStatisticsManager { - private static Dictionary<UserId, ApplicationPlayStatistics> applicationPlayStatistics = new Dictionary<UserId, ApplicationPlayStatistics>(); + private static readonly Dictionary<UserId, ApplicationPlayStatistics> _applicationPlayStatistics = new(); internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false) { ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; + ulong inputSize = context.Request.SendBuff[0].Size; ulong outputPosition = context.Request.ReceiveBuff[0].Position; - ulong outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputSize = context.Request.ReceiveBuff[0].Size; UserId userId = byUserId ? context.RequestData.ReadStruct<UserId>() : new UserId(); @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryCapability; - List<ulong> titleIds = new List<ulong>(); + List<ulong> titleIds = new(); for (ulong i = 0; i < inputSize / sizeof(ulong); i++) { @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); // Return ResultCode.ServiceUnavailable if data is locked by another process. - var filteredApplicationPlayStatistics = applicationPlayStatistics.AsEnumerable(); + var filteredApplicationPlayStatistics = _applicationPlayStatistics.AsEnumerable(); if (queryCapability == PlayLogQueryCapability.None) { @@ -81,4 +81,4 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs index c28d757e8..b58fd2191 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types struct ApplicationPlayStatistics { public ulong TitleId; - public long TotalPlayTime; // In nanoseconds. - public long TotalLaunchCount; + public long TotalPlayTime; // In nanoseconds. + public long TotalLaunchCount; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs index 9e4b85dea..c1e77ed3b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs @@ -4,6 +4,6 @@ { None, WhiteList, - All + All, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs index c337051b4..dd20220f2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs @@ -2,14 +2,14 @@ { enum ResultCode { - ModuleId = 178, + ModuleId = 178, ErrorCodeShift = 9, Success = 0, - InvalidUserID = (100 << ErrorCodeShift) | ModuleId, - UserNotFound = (101 << ErrorCodeShift) | ModuleId, + InvalidUserID = (100 << ErrorCodeShift) | ModuleId, + UserNotFound = (101 << ErrorCodeShift) | ModuleId, ServiceUnavailable = (150 << ErrorCodeShift) | ModuleId, - FileStorageFailure = (200 << ErrorCodeShift) | ModuleId + FileStorageFailure = (200 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs index 9e2f7a4e5..45c4ce7e1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs @@ -17,7 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl // RequestLoad(u32) public ResultCode RequestLoad(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment SharedFontType fontType = (SharedFontType)context.RequestData.ReadInt32(); +#pragma warning restore IDE0059 // We don't need to do anything here because we do lazy initialization // on SharedFontManager (the font is loaded when necessary). @@ -28,7 +30,9 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl // GetLoadState(u32) -> u32 public ResultCode GetLoadState(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment SharedFontType fontType = (SharedFontType)context.RequestData.ReadInt32(); +#pragma warning restore IDE0059 // 1 (true) indicates that the font is already loaded. // All fonts are already loaded. @@ -82,8 +86,10 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl // GetSharedFontInOrderOfPriority(bytes<8, 1>) -> (u8, u32, buffer<unknown, 6>, buffer<unknown, 6>, buffer<unknown, 6>) public ResultCode GetSharedFontInOrderOfPriority(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment long languageCode = context.RequestData.ReadInt64(); - int loadedCount = 0; +#pragma warning restore IDE0059 + int loadedCount = 0; for (SharedFontType type = 0; type < SharedFontType.Count; type++) { @@ -115,15 +121,15 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl private bool AddFontToOrderOfPriorityList(ServiceCtx context, SharedFontType fontType, uint offset) { ulong typesPosition = context.Request.ReceiveBuff[0].Position; - ulong typesSize = context.Request.ReceiveBuff[0].Size; + ulong typesSize = context.Request.ReceiveBuff[0].Size; ulong offsetsPosition = context.Request.ReceiveBuff[1].Position; - ulong offsetsSize = context.Request.ReceiveBuff[1].Size; + ulong offsetsSize = context.Request.ReceiveBuff[1].Size; ulong fontSizeBufferPosition = context.Request.ReceiveBuff[2].Position; - ulong fontSizeBufferSize = context.Request.ReceiveBuff[2].Size; + ulong fontSizeBufferSize = context.Request.ReceiveBuff[2].Size; - if (offset + 4 > (uint)typesSize || + if (offset + 4 > (uint)typesSize || offset + 4 > (uint)offsetsSize || offset + 4 > (uint)fontSizeBufferSize) { @@ -137,4 +143,4 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return true; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs index c0556c316..641795890 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs @@ -19,10 +19,10 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl { class SharedFontManager { - private static readonly uint FontKey = 0x06186249; - private static readonly uint BFTTFMagic = 0x18029a7f; + private const uint FontKey = 0x06186249; + private const uint BFTTFMagic = 0x18029a7f; - private readonly Switch _device; + private readonly Switch _device; private readonly SharedMemoryStorage _storage; private struct FontInfo @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl public FontInfo(int offset, int size) { Offset = offset; - Size = size; + Size = size; } } @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl public SharedFontManager(Switch device, SharedMemoryStorage storage) { - _device = device; + _device = device; _storage = storage; } @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename)) { string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.BuiltInSystem, NcaContentType.Data); - string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath); + string fontPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(fontPath)) { @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl using (IStorage ncaFileStream = new LocalStorage(fontPath, FileAccess.Read, FileMode.Open)) { - Nca nca = new Nca(_device.System.KeySet, ncaFileStream); + Nca nca = new(_device.System.KeySet, ncaFileStream); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); using var fontFile = new UniqueRef<IFile>(); @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl data = DecryptFont(fontFile.Get.AsStream()); } - FontInfo info = new FontInfo((int)fontOffset, data.Length); + FontInfo info = new((int)fontOffset, data.Length); WriteMagicAndSize(fontOffset, data.Length); @@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") }, { SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") }, { SharedFontType.Korean, CreateFont("FontKorean") }, - { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") } + { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }, }; if (fontOffset > Horizon.FontSize) @@ -156,28 +156,27 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return _fontData[fontType].Offset + 8; } - private static byte[] DecryptFont(Stream bfttfStream) + private byte[] DecryptFont(Stream bfttfStream) { static uint KXor(uint data) => data ^ FontKey; - using (BinaryReader reader = new BinaryReader(bfttfStream)) - using (MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream()) - using (BinaryWriter output = new BinaryWriter(ttfStream)) + using BinaryReader reader = new(bfttfStream); + using MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter output = new(ttfStream); + + if (KXor(reader.ReadUInt32()) != BFTTFMagic) { - if (KXor(reader.ReadUInt32()) != BFTTFMagic) - { - throw new InvalidDataException("Error: Input file is not in BFTTF format!"); - } - - bfttfStream.Position += 4; - - for (int i = 0; i < (bfttfStream.Length - 8) / 4; i++) - { - output.Write(KXor(reader.ReadUInt32())); - } - - return ttfStream.ToArray(); + throw new InvalidDataException("Error: Input file is not in BFTTF format!"); } + + bfttfStream.Position += 4; + + for (int i = 0; i < (bfttfStream.Length - 8) / 4; i++) + { + output.Write(KXor(reader.ReadUInt32())); + } + + return ttfStream.ToArray(); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs index 90ee4f037..273fcafbd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/Types/SharedFontType.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl.Types { public enum SharedFontType { - JapanUsEurope = 0, - SimplifiedChinese = 1, + JapanUsEurope = 0, + SimplifiedChinese = 1, SimplifiedChineseEx = 2, - TraditionalChinese = 3, - Korean = 4, - NintendoEx = 5, - Count + TraditionalChinese = 3, + Korean = 4, + NintendoEx = 5, + Count, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index 4cd55a2e9..f107f5026 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -23,26 +23,25 @@ namespace Ryujinx.HLE.HOS.Services // not large enough. private const int PointerBufferSize = 0x8000; - private readonly static uint[] DefaultCapabilities = new uint[] - { + private readonly static uint[] _defaultCapabilities = { 0x030363F7, 0x1FFFFFCF, 0x207FFFEF, 0x47E0060F, 0x0048BFFF, - 0x01007FFF + 0x01007FFF, }; // The amount of time Dispose() will wait to Join() the thread executing the ServerLoop() - private static readonly TimeSpan ThreadJoinTimeout = TimeSpan.FromSeconds(3); + private static readonly TimeSpan _threadJoinTimeout = TimeSpan.FromSeconds(3); private readonly KernelContext _context; private KProcess _selfProcess; private KThread _selfThread; - private readonly ReaderWriterLockSlim _handleLock = new ReaderWriterLockSlim(); - private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>(); - private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>(); + private readonly ReaderWriterLockSlim _handleLock = new(); + private readonly Dictionary<int, IpcService> _sessions = new(); + private readonly Dictionary<int, Func<IpcService>> _ports = new(); private readonly MemoryStream _requestDataStream; private readonly BinaryReader _requestDataReader; @@ -76,9 +75,9 @@ namespace Ryujinx.HLE.HOS.Services ProcessCreationFlags.Is64Bit | ProcessCreationFlags.PoolPartitionSystem; - ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); + ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); - KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main); + KernelStatic.StartInitialProcess(context, creationInfo, _defaultCapabilities, 44, Main); } private void AddPort(int serverPortHandle, Func<IpcService> objectFactory) @@ -281,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Services { IpcMessage request = ReadRequest(); - IpcMessage response = new IpcMessage(); + IpcMessage response = new(); ulong tempAddr = recvListAddr; int sizesOffset = request.RawData.Length - ((request.RecvListBuff.Count * 2 + 3) & ~3); @@ -323,7 +322,7 @@ namespace Ryujinx.HLE.HOS.Services _responseDataStream.SetLength(0); - ServiceCtx context = new ServiceCtx( + ServiceCtx context = new( _context.Device, _selfProcess, _selfProcess.CpuMemory, @@ -340,7 +339,9 @@ namespace Ryujinx.HLE.HOS.Services else if (request.Type == IpcMessageType.CmifControl || request.Type == IpcMessageType.CmifControlWithContext) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment uint magic = (uint)_requestDataReader.ReadUInt64(); +#pragma warning restore IDE0059 uint cmdId = (uint)_requestDataReader.ReadUInt64(); switch (cmdId) @@ -382,7 +383,8 @@ namespace Ryujinx.HLE.HOS.Services break; } - default: throw new NotImplementedException(cmdId.ToString()); + default: + throw new NotImplementedException(cmdId.ToString()); } } else if (request.Type == IpcMessageType.CmifCloseSession || request.Type == IpcMessageType.TipcCloseSession) @@ -404,7 +406,7 @@ namespace Ryujinx.HLE.HOS.Services _responseDataStream.SetLength(0); - ServiceCtx context = new ServiceCtx( + ServiceCtx context = new( _context.Device, _selfProcess, _selfProcess.CpuMemory, @@ -437,15 +439,15 @@ namespace Ryujinx.HLE.HOS.Services private IpcMessage ReadRequest() { - const int messageSize = 0x100; + const int MessageSize = 0x100; - using IMemoryOwner<byte> reqDataOwner = ByteMemoryPool.Rent(messageSize); + using IMemoryOwner<byte> reqDataOwner = ByteMemoryPool.Rent(MessageSize); Span<byte> reqDataSpan = reqDataOwner.Memory.Span; _selfProcess.CpuMemory.Read(_selfThread.TlsAddress, reqDataSpan); - IpcMessage request = new IpcMessage(reqDataSpan, (long)_selfThread.TlsAddress); + IpcMessage request = new(reqDataSpan, (long)_selfThread.TlsAddress); return request; } @@ -480,9 +482,9 @@ namespace Ryujinx.HLE.HOS.Services { if (disposing && _selfThread != null) { - if (_selfThread.HostThread.ManagedThreadId != Environment.CurrentManagedThreadId && _selfThread.HostThread.Join(ThreadJoinTimeout) == false) + if (_selfThread.HostThread.ManagedThreadId != Environment.CurrentManagedThreadId && _selfThread.HostThread.Join(_threadJoinTimeout) == false) { - Logger.Warning?.Print(LogClass.Service, $"The ServerBase thread didn't terminate within {ThreadJoinTimeout:g}, waiting longer."); + Logger.Warning?.Print(LogClass.Service, $"The ServerBase thread didn't terminate within {_threadJoinTimeout:g}, waiting longer."); _selfThread.HostThread.Join(Timeout.Infinite); } diff --git a/src/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs b/src/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs index 1b896a277..c625af487 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs @@ -10,8 +10,8 @@ namespace Ryujinx.HLE.HOS.Services public ServiceAttribute(string name, object parameter = null) { - Name = name; + Name = name; Parameter = parameter; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs index 4dd344f8e..174a1c981 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/IFactorySettingsServer.cs @@ -5,4 +5,4 @@ { public IFactorySettingsServer(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs index 3b7e1af2d..7368cf441 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/IFirmwareDebugSettingsServer.cs @@ -5,4 +5,4 @@ { public IFirmwareDebugSettingsServer(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs index 17e9ec683..abb9b6d8e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings public ResultCode GetDeviceNickName(ServiceCtx context) { ulong deviceNickNameBufferPosition = context.Request.ReceiveBuff[0].Position; - ulong deviceNickNameBufferSize = context.Request.ReceiveBuff[0].Size; + ulong deviceNickNameBufferSize = context.Request.ReceiveBuff[0].Size; if (deviceNickNameBufferPosition == 0) { diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index 07c9f6b3b..65748be33 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings // GetFirmwareVersion2() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100> public ResultCode GetFirmwareVersion2(ServiceCtx context) { - ulong replyPos = context.Request.RecvListBuff[0].Position; + ulong replyPos = context.Request.RecvListBuff[0].Position; context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x100L); @@ -46,43 +46,42 @@ namespace Ryujinx.HLE.HOS.Services.Settings const byte MajorFwVersion = 0x03; const byte MinorFwVersion = 0x00; const byte MicroFwVersion = 0x00; - const byte Unknown = 0x00; //Build? + const byte Unknown = 0x00; //Build? const int RevisionNumber = 0x0A; - const string Platform = "NX"; + const string Platform = "NX"; const string UnknownHex = "7fbde2b0bba4d14107bf836e4643043d9f6c8e47"; - const string Version = "3.0.0"; - const string Build = "NintendoSDK Firmware for NX 3.0.0-10.0"; + const string Version = "3.0.0"; + const string Build = "NintendoSDK Firmware for NX 3.0.0-10.0"; // http://switchbrew.org/index.php?title=System_Version_Title - using (MemoryStream ms = new MemoryStream(0x100)) - { - BinaryWriter writer = new BinaryWriter(ms); + using MemoryStream ms = new(0x100); - writer.Write(MajorFwVersion); - writer.Write(MinorFwVersion); - writer.Write(MicroFwVersion); - writer.Write(Unknown); + BinaryWriter writer = new(ms); - writer.Write(RevisionNumber); + writer.Write(MajorFwVersion); + writer.Write(MinorFwVersion); + writer.Write(MicroFwVersion); + writer.Write(Unknown); - writer.Write(Encoding.ASCII.GetBytes(Platform)); + writer.Write(RevisionNumber); - ms.Seek(0x28, SeekOrigin.Begin); + writer.Write(Encoding.ASCII.GetBytes(Platform)); - writer.Write(Encoding.ASCII.GetBytes(UnknownHex)); + ms.Seek(0x28, SeekOrigin.Begin); - ms.Seek(0x68, SeekOrigin.Begin); + writer.Write(Encoding.ASCII.GetBytes(UnknownHex)); - writer.Write(Encoding.ASCII.GetBytes(Version)); + ms.Seek(0x68, SeekOrigin.Begin); - ms.Seek(0x80, SeekOrigin.Begin); + writer.Write(Encoding.ASCII.GetBytes(Version)); - writer.Write(Encoding.ASCII.GetBytes(Build)); + ms.Seek(0x80, SeekOrigin.Begin); - context.Memory.Write(replyPos, ms.ToArray()); - } + writer.Write(Encoding.ASCII.GetBytes(Build)); + + context.Memory.Write(replyPos, ms.ToArray()); return ResultCode.Success; } @@ -111,10 +110,10 @@ namespace Ryujinx.HLE.HOS.Services.Settings // GetSettingsItemValueSize(buffer<nn::settings::SettingsName, 0x19>, buffer<nn::settings::SettingsItemKey, 0x19>) -> u64 public ResultCode GetSettingsItemValueSize(ServiceCtx context) { - ulong classPos = context.Request.PtrBuff[0].Position; + ulong classPos = context.Request.PtrBuff[0].Position; ulong classSize = context.Request.PtrBuff[0].Size; - ulong namePos = context.Request.PtrBuff[1].Position; + ulong namePos = context.Request.PtrBuff[1].Position; ulong nameSize = context.Request.PtrBuff[1].Size; byte[] classBuffer = new byte[classSize]; @@ -160,13 +159,13 @@ namespace Ryujinx.HLE.HOS.Services.Settings // GetSettingsItemValue(buffer<nn::settings::SettingsName, 0x19, 0x48>, buffer<nn::settings::SettingsItemKey, 0x19, 0x48>) -> (u64, buffer<unknown, 6, 0>) public ResultCode GetSettingsItemValue(ServiceCtx context) { - ulong classPos = context.Request.PtrBuff[0].Position; + ulong classPos = context.Request.PtrBuff[0].Position; ulong classSize = context.Request.PtrBuff[0].Size; - ulong namePos = context.Request.PtrBuff[1].Position; + ulong namePos = context.Request.PtrBuff[1].Position; ulong nameSize = context.Request.PtrBuff[1].Size; - ulong replyPos = context.Request.ReceiveBuff[0].Position; + ulong replyPos = context.Request.ReceiveBuff[0].Position; ulong replySize = context.Request.ReceiveBuff[0].Size; byte[] classBuffer = new byte[classSize]; @@ -250,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings public ResultCode GetDeviceNickName(ServiceCtx context) { ulong deviceNickNameBufferPosition = context.Request.ReceiveBuff[0].Position; - ulong deviceNickNameBufferSize = context.Request.ReceiveBuff[0].Size; + ulong deviceNickNameBufferSize = context.Request.ReceiveBuff[0].Size; if (deviceNickNameBufferPosition == 0) { @@ -272,7 +271,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings public ResultCode SetDeviceNickName(ServiceCtx context) { ulong deviceNickNameBufferPosition = context.Request.SendBuff[0].Position; - ulong deviceNickNameBufferSize = context.Request.SendBuff[0].Size; + ulong deviceNickNameBufferSize = context.Request.SendBuff[0].Size; byte[] deviceNickNameBuffer = new byte[deviceNickNameBufferSize]; @@ -306,43 +305,41 @@ namespace Ryujinx.HLE.HOS.Services.Settings return null; } - string firmwareTitlePath = device.FileSystem.SwitchPathToSystemPath(contentPath); + string firmwareTitlePath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(contentPath); - using(IStorage firmwareStorage = new LocalStorage(firmwareTitlePath, FileAccess.Read)) + using IStorage firmwareStorage = new LocalStorage(firmwareTitlePath, FileAccess.Read); + Nca firmwareContent = new(device.System.KeySet, firmwareStorage); + + if (!firmwareContent.CanOpenSection(NcaSectionType.Data)) { - Nca firmwareContent = new Nca(device.System.KeySet, firmwareStorage); - - if (!firmwareContent.CanOpenSection(NcaSectionType.Data)) - { - return null; - } - - IFileSystem firmwareRomFs = firmwareContent.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); - - using var firmwareFile = new UniqueRef<IFile>(); - - Result result = firmwareRomFs.OpenFile(ref firmwareFile.Ref, "/file".ToU8Span(), OpenMode.Read); - if (result.IsFailure()) - { - return null; - } - - result = firmwareFile.Get.GetSize(out long fileSize); - if (result.IsFailure()) - { - return null; - } - - byte[] data = new byte[fileSize]; - - result = firmwareFile.Get.Read(out _, 0, data); - if (result.IsFailure()) - { - return null; - } - - return data; + return null; } + + IFileSystem firmwareRomFs = firmwareContent.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); + + using var firmwareFile = new UniqueRef<IFile>(); + + Result result = firmwareRomFs.OpenFile(ref firmwareFile.Ref, "/file".ToU8Span(), OpenMode.Read); + if (result.IsFailure()) + { + return null; + } + + result = firmwareFile.Get.GetSize(out long fileSize); + if (result.IsFailure()) + { + return null; + } + + byte[] data = new byte[fileSize]; + + result = firmwareFile.Get.Read(out _, 0, data); + if (result.IsFailure()) + { + return null; + } + + return data; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs b/src/Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs index 67d1ac929..981fc18ea 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/KeyCodeMaps.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Settings { - class KeyCodeMaps + static class KeyCodeMaps { public static byte[] Default = { @@ -345,7 +345,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] EnglishUsInternational = @@ -691,7 +691,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] EnglishUk = @@ -1037,7 +1037,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] French = @@ -1383,7 +1383,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] FrenchCa = @@ -1729,7 +1729,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] Spanish = @@ -2075,7 +2075,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] SpanishLatin = @@ -2421,7 +2421,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] German = @@ -2767,7 +2767,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] Italian = @@ -3113,7 +3113,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] Portuguese = @@ -3459,7 +3459,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] Russian = @@ -3805,7 +3805,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] Korean = @@ -4151,7 +4151,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] ChineseSimplified = @@ -4497,7 +4497,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; public static byte[] ChineseTraditional = @@ -4843,7 +4843,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, }; }; } diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs index e5f218a64..b2d4d55cc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings static class NxSettings { // Generated automatically from a Switch 3.0 config file (Tid: 0100000000000818). - public static Dictionary<string, object> Settings = new Dictionary<string, object> + public static Dictionary<string, object> Settings = new() { { "account!na_required_for_network_service", true }, { "account.daemon!background_awaking_periodicity", 10800 }, @@ -1706,7 +1706,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings { "time!standard_network_clock_sufficient_accuracy_minutes", 43200 }, { "time!standard_user_clock_initial_year", 2019 }, { "usb!usb30_force_enabled", false }, - { "wlan_debug!skip_wlan_boot", false } + { "wlan_debug!skip_wlan_boot", false }, }; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs index 8b0fde6c7..9d4678091 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/ResultCode.cs @@ -2,125 +2,125 @@ { enum ResultCode { - ModuleId = 105, + ModuleId = 105, ErrorCodeShift = 9, Success = 0, - NullSettingsName = (201 << ErrorCodeShift) | ModuleId, - NullSettingsKey = (202 << ErrorCodeShift) | ModuleId, - NullSettingsValue = (203 << ErrorCodeShift) | ModuleId, - NullSettingsValueBuffer = (205 << ErrorCodeShift) | ModuleId, - NullSettingValueSizeBuffer = (208 << ErrorCodeShift) | ModuleId, - NullDebugModeFlagBuffer = (209 << ErrorCodeShift) | ModuleId, - SettingGroupNameHasZeroLength = (221 << ErrorCodeShift) | ModuleId, - EmptySettingsItemKey = (222 << ErrorCodeShift) | ModuleId, - SettingGroupNameIsTooLong = (241 << ErrorCodeShift) | ModuleId, - SettingNameIsTooLong = (242 << ErrorCodeShift) | ModuleId, - SettingGroupNameEndsWithDotOrContainsInvalidCharacters = (261 << ErrorCodeShift) | ModuleId, - SettingNameEndsWithDotOrContainsInvalidCharacters = (262 << ErrorCodeShift) | ModuleId, - NullLanguageCodeBuffer = (621 << ErrorCodeShift) | ModuleId, - LanguageOutOfRange = (625 << ErrorCodeShift) | ModuleId, - NullNetworkSettingsBuffer = (631 << ErrorCodeShift) | ModuleId, - NullNetworkSettingsOutputCountBuffer = (632 << ErrorCodeShift) | ModuleId, - NullBacklightSettingsBuffer = (641 << ErrorCodeShift) | ModuleId, - NullBluetoothDeviceSettingBuffer = (651 << ErrorCodeShift) | ModuleId, - NullBluetoothDeviceSettingOutputCountBuffer = (652 << ErrorCodeShift) | ModuleId, - NullBluetoothEnableFlagBuffer = (653 << ErrorCodeShift) | ModuleId, - NullBluetoothAFHEnableFlagBuffer = (654 << ErrorCodeShift) | ModuleId, - NullBluetoothBoostEnableFlagBuffer = (655 << ErrorCodeShift) | ModuleId, - NullBLEPairingSettingsBuffer = (656 << ErrorCodeShift) | ModuleId, - NullBLEPairingSettingsEntryCountBuffer = (657 << ErrorCodeShift) | ModuleId, - NullExternalSteadyClockSourceIDBuffer = (661 << ErrorCodeShift) | ModuleId, - NullUserSystemClockContextBuffer = (662 << ErrorCodeShift) | ModuleId, - NullNetworkSystemClockContextBuffer = (663 << ErrorCodeShift) | ModuleId, - NullUserSystemClockAutomaticCorrectionEnabledFlagBuffer = (664 << ErrorCodeShift) | ModuleId, - NullShutdownRTCValueBuffer = (665 << ErrorCodeShift) | ModuleId, - NullExternalSteadyClockInternalOffsetBuffer = (666 << ErrorCodeShift) | ModuleId, - NullAccountSettingsBuffer = (671 << ErrorCodeShift) | ModuleId, - NullAudioVolumeBuffer = (681 << ErrorCodeShift) | ModuleId, - NullForceMuteOnHeadphoneRemovedBuffer = (683 << ErrorCodeShift) | ModuleId, - NullHeadphoneVolumeWarningCountBuffer = (684 << ErrorCodeShift) | ModuleId, - InvalidAudioOutputMode = (687 << ErrorCodeShift) | ModuleId, - NullHeadphoneVolumeUpdateFlagBuffer = (688 << ErrorCodeShift) | ModuleId, - NullConsoleInformationUploadFlagBuffer = (691 << ErrorCodeShift) | ModuleId, - NullAutomaticApplicationDownloadFlagBuffer = (701 << ErrorCodeShift) | ModuleId, - NullNotificationSettingsBuffer = (702 << ErrorCodeShift) | ModuleId, - NullAccountNotificationSettingsEntryCountBuffer = (703 << ErrorCodeShift) | ModuleId, - NullAccountNotificationSettingsBuffer = (704 << ErrorCodeShift) | ModuleId, - NullVibrationMasterVolumeBuffer = (711 << ErrorCodeShift) | ModuleId, - NullNXControllerSettingsBuffer = (712 << ErrorCodeShift) | ModuleId, - NullNXControllerSettingsEntryCountBuffer = (713 << ErrorCodeShift) | ModuleId, - NullUSBFullKeyEnableFlagBuffer = (714 << ErrorCodeShift) | ModuleId, - NullTVSettingsBuffer = (721 << ErrorCodeShift) | ModuleId, - NullEDIDBuffer = (722 << ErrorCodeShift) | ModuleId, - NullDataDeletionSettingsBuffer = (731 << ErrorCodeShift) | ModuleId, - NullInitialSystemAppletProgramIDBuffer = (741 << ErrorCodeShift) | ModuleId, - NullOverlayDispProgramIDBuffer = (742 << ErrorCodeShift) | ModuleId, - NullIsInRepairProcessBuffer = (743 << ErrorCodeShift) | ModuleId, - NullRequiresRunRepairTimeReviserBuffer = (744 << ErrorCodeShift) | ModuleId, - NullDeviceTimezoneLocationNameBuffer = (751 << ErrorCodeShift) | ModuleId, - NullPrimaryAlbumStorageBuffer = (761 << ErrorCodeShift) | ModuleId, - NullUSB30EnableFlagBuffer = (771 << ErrorCodeShift) | ModuleId, - NullUSBTypeCPowerSourceCircuitVersionBuffer = (772 << ErrorCodeShift) | ModuleId, - NullBatteryLotBuffer = (781 << ErrorCodeShift) | ModuleId, - NullSerialNumberBuffer = (791 << ErrorCodeShift) | ModuleId, - NullLockScreenFlagBuffer = (801 << ErrorCodeShift) | ModuleId, - NullColorSetIDBuffer = (803 << ErrorCodeShift) | ModuleId, - NullQuestFlagBuffer = (804 << ErrorCodeShift) | ModuleId, - NullWirelessCertificationFileSizeBuffer = (805 << ErrorCodeShift) | ModuleId, - NullWirelessCertificationFileBuffer = (806 << ErrorCodeShift) | ModuleId, - NullInitialLaunchSettingsBuffer = (807 << ErrorCodeShift) | ModuleId, - NullDeviceNicknameBuffer = (808 << ErrorCodeShift) | ModuleId, - NullBatteryPercentageFlagBuffer = (809 << ErrorCodeShift) | ModuleId, - NullAppletLaunchFlagsBuffer = (810 << ErrorCodeShift) | ModuleId, - NullWirelessLANEnableFlagBuffer = (1012 << ErrorCodeShift) | ModuleId, - NullProductModelBuffer = (1021 << ErrorCodeShift) | ModuleId, - NullNFCEnableFlagBuffer = (1031 << ErrorCodeShift) | ModuleId, - NullECIDeviceCertificateBuffer = (1041 << ErrorCodeShift) | ModuleId, - NullETicketDeviceCertificateBuffer = (1042 << ErrorCodeShift) | ModuleId, - NullSleepSettingsBuffer = (1051 << ErrorCodeShift) | ModuleId, - NullEULAVersionBuffer = (1061 << ErrorCodeShift) | ModuleId, - NullEULAVersionEntryCountBuffer = (1062 << ErrorCodeShift) | ModuleId, - NullLDNChannelBuffer = (1071 << ErrorCodeShift) | ModuleId, - NullSSLKeyBuffer = (1081 << ErrorCodeShift) | ModuleId, - NullSSLCertificateBuffer = (1082 << ErrorCodeShift) | ModuleId, - NullTelemetryFlagsBuffer = (1091 << ErrorCodeShift) | ModuleId, - NullGamecardKeyBuffer = (1101 << ErrorCodeShift) | ModuleId, - NullGamecardCertificateBuffer = (1102 << ErrorCodeShift) | ModuleId, - NullPTMBatteryLotBuffer = (1111 << ErrorCodeShift) | ModuleId, - NullPTMFuelGaugeParameterBuffer = (1112 << ErrorCodeShift) | ModuleId, - NullECIDeviceKeyBuffer = (1121 << ErrorCodeShift) | ModuleId, - NullETicketDeviceKeyBuffer = (1122 << ErrorCodeShift) | ModuleId, - NullSpeakerParameterBuffer = (1131 << ErrorCodeShift) | ModuleId, - NullFirmwareVersionBuffer = (1141 << ErrorCodeShift) | ModuleId, - NullFirmwareVersionDigestBuffer = (1142 << ErrorCodeShift) | ModuleId, - NullRebootlessSystemUpdateVersionBuffer = (1143 << ErrorCodeShift) | ModuleId, - NullMiiAuthorIDBuffer = (1151 << ErrorCodeShift) | ModuleId, - NullFatalFlagsBuffer = (1161 << ErrorCodeShift) | ModuleId, - NullAutoUpdateEnableFlagBuffer = (1171 << ErrorCodeShift) | ModuleId, - NullExternalRTCResetFlagBuffer = (1181 << ErrorCodeShift) | ModuleId, - NullPushNotificationActivityModeBuffer = (1191 << ErrorCodeShift) | ModuleId, - NullServiceDiscoveryControlSettingBuffer = (1201 << ErrorCodeShift) | ModuleId, - NullErrorReportSharePermissionBuffer = (1211 << ErrorCodeShift) | ModuleId, - NullLCDVendorIDBuffer = (1221 << ErrorCodeShift) | ModuleId, - NullConsoleSixAxisSensorAccelerationBiasBuffer = (1231 << ErrorCodeShift) | ModuleId, - NullConsoleSixAxisSensorAngularVelocityBiasBuffer = (1232 << ErrorCodeShift) | ModuleId, - NullConsoleSixAxisSensorAccelerationGainBuffer = (1233 << ErrorCodeShift) | ModuleId, - NullConsoleSixAxisSensorAngularVelocityGainBuffer = (1234 << ErrorCodeShift) | ModuleId, - NullConsoleSixAxisSensorAngularVelocityTimeBiasBuffer = (1235 << ErrorCodeShift) | ModuleId, - NullConsoleSixAxisSensorAngularAccelerationBuffer = (1236 << ErrorCodeShift) | ModuleId, - NullKeyboardLayoutBuffer = (1241 << ErrorCodeShift) | ModuleId, - InvalidKeyboardLayout = (1245 << ErrorCodeShift) | ModuleId, - NullWebInspectorFlagBuffer = (1251 << ErrorCodeShift) | ModuleId, - NullAllowedSSLHostsBuffer = (1252 << ErrorCodeShift) | ModuleId, - NullAllowedSSLHostsEntryCountBuffer = (1253 << ErrorCodeShift) | ModuleId, - NullHostFSMountPointBuffer = (1254 << ErrorCodeShift) | ModuleId, - NullAmiiboKeyBuffer = (1271 << ErrorCodeShift) | ModuleId, - NullAmiiboECQVCertificateBuffer = (1272 << ErrorCodeShift) | ModuleId, - NullAmiiboECDSACertificateBuffer = (1273 << ErrorCodeShift) | ModuleId, - NullAmiiboECQVBLSKeyBuffer = (1274 << ErrorCodeShift) | ModuleId, - NullAmiiboECQVBLSCertificateBuffer = (1275 << ErrorCodeShift) | ModuleId, - NullAmiiboECQVBLSRootCertificateBuffer = (1276 << ErrorCodeShift) | ModuleId + NullSettingsName = (201 << ErrorCodeShift) | ModuleId, + NullSettingsKey = (202 << ErrorCodeShift) | ModuleId, + NullSettingsValue = (203 << ErrorCodeShift) | ModuleId, + NullSettingsValueBuffer = (205 << ErrorCodeShift) | ModuleId, + NullSettingValueSizeBuffer = (208 << ErrorCodeShift) | ModuleId, + NullDebugModeFlagBuffer = (209 << ErrorCodeShift) | ModuleId, + SettingGroupNameHasZeroLength = (221 << ErrorCodeShift) | ModuleId, + EmptySettingsItemKey = (222 << ErrorCodeShift) | ModuleId, + SettingGroupNameIsTooLong = (241 << ErrorCodeShift) | ModuleId, + SettingNameIsTooLong = (242 << ErrorCodeShift) | ModuleId, + SettingGroupNameEndsWithDotOrContainsInvalidCharacters = (261 << ErrorCodeShift) | ModuleId, + SettingNameEndsWithDotOrContainsInvalidCharacters = (262 << ErrorCodeShift) | ModuleId, + NullLanguageCodeBuffer = (621 << ErrorCodeShift) | ModuleId, + LanguageOutOfRange = (625 << ErrorCodeShift) | ModuleId, + NullNetworkSettingsBuffer = (631 << ErrorCodeShift) | ModuleId, + NullNetworkSettingsOutputCountBuffer = (632 << ErrorCodeShift) | ModuleId, + NullBacklightSettingsBuffer = (641 << ErrorCodeShift) | ModuleId, + NullBluetoothDeviceSettingBuffer = (651 << ErrorCodeShift) | ModuleId, + NullBluetoothDeviceSettingOutputCountBuffer = (652 << ErrorCodeShift) | ModuleId, + NullBluetoothEnableFlagBuffer = (653 << ErrorCodeShift) | ModuleId, + NullBluetoothAFHEnableFlagBuffer = (654 << ErrorCodeShift) | ModuleId, + NullBluetoothBoostEnableFlagBuffer = (655 << ErrorCodeShift) | ModuleId, + NullBLEPairingSettingsBuffer = (656 << ErrorCodeShift) | ModuleId, + NullBLEPairingSettingsEntryCountBuffer = (657 << ErrorCodeShift) | ModuleId, + NullExternalSteadyClockSourceIDBuffer = (661 << ErrorCodeShift) | ModuleId, + NullUserSystemClockContextBuffer = (662 << ErrorCodeShift) | ModuleId, + NullNetworkSystemClockContextBuffer = (663 << ErrorCodeShift) | ModuleId, + NullUserSystemClockAutomaticCorrectionEnabledFlagBuffer = (664 << ErrorCodeShift) | ModuleId, + NullShutdownRTCValueBuffer = (665 << ErrorCodeShift) | ModuleId, + NullExternalSteadyClockInternalOffsetBuffer = (666 << ErrorCodeShift) | ModuleId, + NullAccountSettingsBuffer = (671 << ErrorCodeShift) | ModuleId, + NullAudioVolumeBuffer = (681 << ErrorCodeShift) | ModuleId, + NullForceMuteOnHeadphoneRemovedBuffer = (683 << ErrorCodeShift) | ModuleId, + NullHeadphoneVolumeWarningCountBuffer = (684 << ErrorCodeShift) | ModuleId, + InvalidAudioOutputMode = (687 << ErrorCodeShift) | ModuleId, + NullHeadphoneVolumeUpdateFlagBuffer = (688 << ErrorCodeShift) | ModuleId, + NullConsoleInformationUploadFlagBuffer = (691 << ErrorCodeShift) | ModuleId, + NullAutomaticApplicationDownloadFlagBuffer = (701 << ErrorCodeShift) | ModuleId, + NullNotificationSettingsBuffer = (702 << ErrorCodeShift) | ModuleId, + NullAccountNotificationSettingsEntryCountBuffer = (703 << ErrorCodeShift) | ModuleId, + NullAccountNotificationSettingsBuffer = (704 << ErrorCodeShift) | ModuleId, + NullVibrationMasterVolumeBuffer = (711 << ErrorCodeShift) | ModuleId, + NullNXControllerSettingsBuffer = (712 << ErrorCodeShift) | ModuleId, + NullNXControllerSettingsEntryCountBuffer = (713 << ErrorCodeShift) | ModuleId, + NullUSBFullKeyEnableFlagBuffer = (714 << ErrorCodeShift) | ModuleId, + NullTVSettingsBuffer = (721 << ErrorCodeShift) | ModuleId, + NullEDIDBuffer = (722 << ErrorCodeShift) | ModuleId, + NullDataDeletionSettingsBuffer = (731 << ErrorCodeShift) | ModuleId, + NullInitialSystemAppletProgramIDBuffer = (741 << ErrorCodeShift) | ModuleId, + NullOverlayDispProgramIDBuffer = (742 << ErrorCodeShift) | ModuleId, + NullIsInRepairProcessBuffer = (743 << ErrorCodeShift) | ModuleId, + NullRequiresRunRepairTimeReviserBuffer = (744 << ErrorCodeShift) | ModuleId, + NullDeviceTimezoneLocationNameBuffer = (751 << ErrorCodeShift) | ModuleId, + NullPrimaryAlbumStorageBuffer = (761 << ErrorCodeShift) | ModuleId, + NullUSB30EnableFlagBuffer = (771 << ErrorCodeShift) | ModuleId, + NullUSBTypeCPowerSourceCircuitVersionBuffer = (772 << ErrorCodeShift) | ModuleId, + NullBatteryLotBuffer = (781 << ErrorCodeShift) | ModuleId, + NullSerialNumberBuffer = (791 << ErrorCodeShift) | ModuleId, + NullLockScreenFlagBuffer = (801 << ErrorCodeShift) | ModuleId, + NullColorSetIDBuffer = (803 << ErrorCodeShift) | ModuleId, + NullQuestFlagBuffer = (804 << ErrorCodeShift) | ModuleId, + NullWirelessCertificationFileSizeBuffer = (805 << ErrorCodeShift) | ModuleId, + NullWirelessCertificationFileBuffer = (806 << ErrorCodeShift) | ModuleId, + NullInitialLaunchSettingsBuffer = (807 << ErrorCodeShift) | ModuleId, + NullDeviceNicknameBuffer = (808 << ErrorCodeShift) | ModuleId, + NullBatteryPercentageFlagBuffer = (809 << ErrorCodeShift) | ModuleId, + NullAppletLaunchFlagsBuffer = (810 << ErrorCodeShift) | ModuleId, + NullWirelessLANEnableFlagBuffer = (1012 << ErrorCodeShift) | ModuleId, + NullProductModelBuffer = (1021 << ErrorCodeShift) | ModuleId, + NullNFCEnableFlagBuffer = (1031 << ErrorCodeShift) | ModuleId, + NullECIDeviceCertificateBuffer = (1041 << ErrorCodeShift) | ModuleId, + NullETicketDeviceCertificateBuffer = (1042 << ErrorCodeShift) | ModuleId, + NullSleepSettingsBuffer = (1051 << ErrorCodeShift) | ModuleId, + NullEULAVersionBuffer = (1061 << ErrorCodeShift) | ModuleId, + NullEULAVersionEntryCountBuffer = (1062 << ErrorCodeShift) | ModuleId, + NullLDNChannelBuffer = (1071 << ErrorCodeShift) | ModuleId, + NullSSLKeyBuffer = (1081 << ErrorCodeShift) | ModuleId, + NullSSLCertificateBuffer = (1082 << ErrorCodeShift) | ModuleId, + NullTelemetryFlagsBuffer = (1091 << ErrorCodeShift) | ModuleId, + NullGamecardKeyBuffer = (1101 << ErrorCodeShift) | ModuleId, + NullGamecardCertificateBuffer = (1102 << ErrorCodeShift) | ModuleId, + NullPTMBatteryLotBuffer = (1111 << ErrorCodeShift) | ModuleId, + NullPTMFuelGaugeParameterBuffer = (1112 << ErrorCodeShift) | ModuleId, + NullECIDeviceKeyBuffer = (1121 << ErrorCodeShift) | ModuleId, + NullETicketDeviceKeyBuffer = (1122 << ErrorCodeShift) | ModuleId, + NullSpeakerParameterBuffer = (1131 << ErrorCodeShift) | ModuleId, + NullFirmwareVersionBuffer = (1141 << ErrorCodeShift) | ModuleId, + NullFirmwareVersionDigestBuffer = (1142 << ErrorCodeShift) | ModuleId, + NullRebootlessSystemUpdateVersionBuffer = (1143 << ErrorCodeShift) | ModuleId, + NullMiiAuthorIDBuffer = (1151 << ErrorCodeShift) | ModuleId, + NullFatalFlagsBuffer = (1161 << ErrorCodeShift) | ModuleId, + NullAutoUpdateEnableFlagBuffer = (1171 << ErrorCodeShift) | ModuleId, + NullExternalRTCResetFlagBuffer = (1181 << ErrorCodeShift) | ModuleId, + NullPushNotificationActivityModeBuffer = (1191 << ErrorCodeShift) | ModuleId, + NullServiceDiscoveryControlSettingBuffer = (1201 << ErrorCodeShift) | ModuleId, + NullErrorReportSharePermissionBuffer = (1211 << ErrorCodeShift) | ModuleId, + NullLCDVendorIDBuffer = (1221 << ErrorCodeShift) | ModuleId, + NullConsoleSixAxisSensorAccelerationBiasBuffer = (1231 << ErrorCodeShift) | ModuleId, + NullConsoleSixAxisSensorAngularVelocityBiasBuffer = (1232 << ErrorCodeShift) | ModuleId, + NullConsoleSixAxisSensorAccelerationGainBuffer = (1233 << ErrorCodeShift) | ModuleId, + NullConsoleSixAxisSensorAngularVelocityGainBuffer = (1234 << ErrorCodeShift) | ModuleId, + NullConsoleSixAxisSensorAngularVelocityTimeBiasBuffer = (1235 << ErrorCodeShift) | ModuleId, + NullConsoleSixAxisSensorAngularAccelerationBuffer = (1236 << ErrorCodeShift) | ModuleId, + NullKeyboardLayoutBuffer = (1241 << ErrorCodeShift) | ModuleId, + InvalidKeyboardLayout = (1245 << ErrorCodeShift) | ModuleId, + NullWebInspectorFlagBuffer = (1251 << ErrorCodeShift) | ModuleId, + NullAllowedSSLHostsBuffer = (1252 << ErrorCodeShift) | ModuleId, + NullAllowedSSLHostsEntryCountBuffer = (1253 << ErrorCodeShift) | ModuleId, + NullHostFSMountPointBuffer = (1254 << ErrorCodeShift) | ModuleId, + NullAmiiboKeyBuffer = (1271 << ErrorCodeShift) | ModuleId, + NullAmiiboECQVCertificateBuffer = (1272 << ErrorCodeShift) | ModuleId, + NullAmiiboECDSACertificateBuffer = (1273 << ErrorCodeShift) | ModuleId, + NullAmiiboECQVBLSKeyBuffer = (1274 << ErrorCodeShift) | ModuleId, + NullAmiiboECQVBLSCertificateBuffer = (1275 << ErrorCodeShift) | ModuleId, + NullAmiiboECQVBLSRootCertificateBuffer = (1276 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs index b8ef8e8ea..3953c050c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs @@ -3,6 +3,6 @@ enum PlatformRegion { Global = 1, - China = 2 + China = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs index f867f23a4..412a33246 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/IManagerInterface.cs @@ -5,4 +5,4 @@ { public IManagerInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 005ec32d8..2d8502208 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm { class IUserInterface : IpcService { - private static Dictionary<string, Type> _services; + private static readonly Dictionary<string, Type> _services; private readonly SmRegistry _registry; private readonly ServerBase _commonServer; @@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm return ResultCode.InvalidName; } - KSession session = new KSession(context.Device.System.KernelContext); + KSession session = new(context.Device.System.KernelContext); if (_registry.TryGetService(name, out KPort port)) { @@ -182,7 +182,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm Logger.Debug?.Print(LogClass.ServiceSm, $"Register \"{name}\"."); - KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, null); + KPort port = new(context.Device.System.KernelContext, maxSessions, isLight, null); if (!_registry.TryRegister(name, port)) { @@ -215,9 +215,10 @@ namespace Ryujinx.HLE.HOS.Services.Sm context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin); +#pragma warning disable IDE0059 // Remove unnecessary value assignment bool isLight = (context.RequestData.ReadInt32() & 1) != 0; - int maxSessions = context.RequestData.ReadInt32(); +#pragma warning restore IDE0059 if (string.IsNullOrEmpty(name)) { @@ -258,4 +259,4 @@ namespace Ryujinx.HLE.HOS.Services.Sm base.DestroyAtExit(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs index f72bf0109..6db33d2aa 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/ResultCode.cs @@ -2,14 +2,14 @@ namespace Ryujinx.HLE.HOS.Services.Sm { enum ResultCode { - ModuleId = 21, + ModuleId = 21, ErrorCodeShift = 9, Success = 0, - NotInitialized = (2 << ErrorCodeShift) | ModuleId, + NotInitialized = (2 << ErrorCodeShift) | ModuleId, AlreadyRegistered = (4 << ErrorCodeShift) | ModuleId, - InvalidName = (6 << ErrorCodeShift) | ModuleId, - NotRegistered = (7 << ErrorCodeShift) | ModuleId + InvalidName = (6 << ErrorCodeShift) | ModuleId, + NotRegistered = (7 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs b/src/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs index e62e0eb53..3919eaae9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/SmRegistry.cs @@ -46,4 +46,4 @@ namespace Ryujinx.HLE.HOS.Services.Sm _serviceRegistrationEvent.WaitOne(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs index b0ac6e680..254ad6675 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs @@ -8,11 +8,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { class BsdContext { - private static ConcurrentDictionary<ulong, BsdContext> _registry = new ConcurrentDictionary<ulong, BsdContext>(); + private static readonly ConcurrentDictionary<ulong, BsdContext> _registry = new(); private readonly object _lock = new(); - private List<IFileDescriptor> _fds; + private readonly List<IFileDescriptor> _fds; private BsdContext() { @@ -181,4 +181,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return processContext; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs index b63864c90..d16e7536e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Numerics; using System.Runtime.CompilerServices; using System.Text; @@ -17,14 +16,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd [Service("bsd:u", false)] class IClient : IpcService { - private static readonly List<IPollManager> _pollManagers = new List<IPollManager> + private static readonly List<IPollManager> _pollManagers = new() { EventFileDescriptorPollManager.Instance, - ManagedSocketPollManager.Instance + ManagedSocketPollManager.Instance, }; private BsdContext _context; - private bool _isPrivileged; + private readonly bool _isPrivileged; public IClient(ServiceCtx context, bool isPrivileged) : base(context.Device.System.BsdServer) { @@ -46,19 +45,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd private static AddressFamily ConvertBsdAddressFamily(BsdAddressFamily family) { - switch (family) + return family switch { - case BsdAddressFamily.Unspecified: - return AddressFamily.Unspecified; - case BsdAddressFamily.InterNetwork: - return AddressFamily.InterNetwork; - case BsdAddressFamily.InterNetworkV6: - return AddressFamily.InterNetworkV6; - case BsdAddressFamily.Unknown: - return AddressFamily.Unknown; - default: - throw new NotImplementedException(family.ToString()); - } + BsdAddressFamily.Unspecified => AddressFamily.Unspecified, + BsdAddressFamily.InterNetwork => AddressFamily.InterNetwork, + BsdAddressFamily.InterNetworkV6 => AddressFamily.InterNetworkV6, + BsdAddressFamily.Unknown => AddressFamily.Unknown, + _ => throw new NotImplementedException(family.ToString()), + }; } private LinuxError SetResultErrno(IFileDescriptor socket, int result) @@ -68,9 +62,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd private ResultCode SocketInternal(ServiceCtx context, bool exempt) { - BsdAddressFamily domain = (BsdAddressFamily)context.RequestData.ReadInt32(); - BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32(); - ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32(); + BsdAddressFamily domain = (BsdAddressFamily)context.RequestData.ReadInt32(); + BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32(); + ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32(); BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift); type &= BsdSocketType.TypeMask; @@ -101,8 +95,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } } - ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol); - newBsdSocket.Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking); + ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol) + { + Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking), + }; LinuxError errno = LinuxError.SUCCESS; @@ -210,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd public ResultCode Select(ServiceCtx context) { int fdsCount = context.RequestData.ReadInt32(); - int timeout = context.RequestData.ReadInt32(); + int timeout = context.RequestData.ReadInt32(); (ulong readFdsInBufferPosition, ulong readFdsInBufferSize) = context.Request.GetBufferType0x21(0); (ulong writeFdsInBufferPosition, ulong writeFdsInBufferSize) = context.Request.GetBufferType0x21(1); @@ -220,7 +216,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd (ulong writeFdsOutBufferPosition, ulong writeFdsOutBufferSize) = context.Request.GetBufferType0x22(1); (ulong errorFdsOutBufferPosition, ulong errorFdsOutBufferSize) = context.Request.GetBufferType0x22(2); - List<IFileDescriptor> readFds = _context.RetrieveFileDescriptorsFromMask(context.Memory.GetSpan(readFdsInBufferPosition, (int)readFdsInBufferSize)); + List<IFileDescriptor> readFds = _context.RetrieveFileDescriptorsFromMask(context.Memory.GetSpan(readFdsInBufferPosition, (int)readFdsInBufferSize)); List<IFileDescriptor> writeFds = _context.RetrieveFileDescriptorsFromMask(context.Memory.GetSpan(writeFdsInBufferPosition, (int)writeFdsInBufferSize)); List<IFileDescriptor> errorFds = _context.RetrieveFileDescriptorsFromMask(context.Memory.GetSpan(errorFdsInBufferPosition, (int)errorFdsInBufferSize)); @@ -312,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } } - using var readFdsOut = context.Memory.GetWritableRegion(readFdsOutBufferPosition, (int)readFdsOutBufferSize); + using var readFdsOut = context.Memory.GetWritableRegion(readFdsOutBufferPosition, (int)readFdsOutBufferSize); using var writeFdsOut = context.Memory.GetWritableRegion(writeFdsOutBufferPosition, (int)writeFdsOutBufferSize); using var errorFdsOut = context.Memory.GetWritableRegion(errorFdsOutBufferPosition, (int)errorFdsOutBufferSize); @@ -330,10 +326,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd public ResultCode Poll(ServiceCtx context) { int fdsCount = context.RequestData.ReadInt32(); - int timeout = context.RequestData.ReadInt32(); + int timeout = context.RequestData.ReadInt32(); (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); +#pragma warning restore IDE0059 if (timeout < -1 || fdsCount < 0 || (ulong)(fdsCount * 8) > inputBufferSize) { @@ -356,7 +354,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd events[i] = new PollEvent(pollEventData, fileDescriptor); } - List<PollEvent> discoveredEvents = new List<PollEvent>(); + List<PollEvent> discoveredEvents = new(); List<PollEvent>[] eventsByPollManager = new List<PollEvent>[_pollManagers.Count]; for (int i = 0; i < eventsByPollManager.Length; i++) @@ -389,7 +387,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd if (fdsCount != 0) { - bool IsUnexpectedLinuxError(LinuxError error) + static bool IsUnexpectedLinuxError(LinuxError error) { return error != LinuxError.SUCCESS && error != LinuxError.ETIMEDOUT; } @@ -478,16 +476,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // Recv(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, array<i8, 0x22> message) public ResultCode Recv(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32(); (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22(); WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); + int result = -1; if (socket != null) { @@ -508,17 +506,17 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // RecvFrom(u32 sock, u32 flags) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<i8, 0x22, 0> message, buffer<nn::socket::sockaddr_in, 0x22, 0x10>) public ResultCode RecvFrom(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32(); - (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22(0); + (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22(0); (ulong sockAddrOutPosition, ulong sockAddrOutSize) = context.Request.GetBufferType0x22(1); WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); + int result = -1; if (socket != null) { @@ -530,7 +528,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd receiveRegion.Dispose(); - if (sockAddrOutSize != 0 && sockAddrOutSize >= (ulong) Unsafe.SizeOf<BsdSockAddr>()) + if (sockAddrOutSize != 0 && sockAddrOutSize >= (ulong)Unsafe.SizeOf<BsdSockAddr>()) { context.Memory.Write(sockAddrOutPosition, BsdSockAddr.FromIPEndPoint(endPoint)); } @@ -548,16 +546,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // Send(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno) public ResultCode Send(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32(); (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21(); ReadOnlySpan<byte> sendBuffer = context.Memory.GetSpan(sendPosition, (int)sendSize); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); + int result = -1; if (socket != null) { @@ -576,17 +574,19 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // SendTo(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<nn::socket::sockaddr_in, 0x21, 0x10>) -> (i32 ret, u32 bsd_errno) public ResultCode SendTo(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32(); - (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21(0); + (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21(0); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(1); +#pragma warning restore IDE0059 ReadOnlySpan<byte> sendBuffer = context.Memory.GetSpan(sendPosition, (int)sendSize); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); + int result = -1; if (socket != null) { @@ -609,10 +609,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22(); +#pragma warning restore IDE0059 - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -652,10 +654,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); +#pragma warning restore IDE0059 - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -673,10 +677,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); +#pragma warning restore IDE0059 - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -694,10 +700,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22(); +#pragma warning restore IDE0059 - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { errno = LinuxError.ENOTCONN; @@ -721,10 +729,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22(); +#pragma warning restore IDE0059 - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -742,15 +752,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // GetSockOpt(u32 socket, u32 level, u32 option_name) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>) public ResultCode GetSockOpt(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); - SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32(); - BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); + SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32(); + BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32(); (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22(); WritableRegion optionValue = context.Memory.GetWritableRegion(bufferPosition, (int)bufferSize); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -770,10 +780,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd public ResultCode Listen(ServiceCtx context) { int socketFd = context.RequestData.ReadInt32(); - int backlog = context.RequestData.ReadInt32(); + int backlog = context.RequestData.ReadInt32(); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -787,12 +797,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // Ioctl(u32 fd, u32 request, u32 bufcount, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>) public ResultCode Ioctl(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); - BsdIoctl cmd = (BsdIoctl)context.RequestData.ReadInt32(); - int bufferCount = context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); + BsdIoctl cmd = (BsdIoctl)context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + int bufferCount = context.RequestData.ReadInt32(); +#pragma warning restore IDE0059 - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -801,7 +813,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd case BsdIoctl.AtMark: errno = LinuxError.SUCCESS; +#pragma warning disable IDE0059 // Remove unnecessary value assignment (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22(); +#pragma warning restore IDE0059 // FIXME: OOB not implemented. context.Memory.Write(bufferPosition, 0); @@ -823,12 +837,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd public ResultCode Fcntl(ServiceCtx context) { int socketFd = context.RequestData.ReadInt32(); - int cmd = context.RequestData.ReadInt32(); - int arg = context.RequestData.ReadInt32(); + int cmd = context.RequestData.ReadInt32(); + int arg = context.RequestData.ReadInt32(); - int result = 0; - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + int result = 0; + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -856,16 +870,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // SetSockOpt(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0> option_value) -> (i32 ret, u32 bsd_errno) public ResultCode SetSockOpt(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); - SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32(); - BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); + SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32(); + BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32(); (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21(); ReadOnlySpan<byte> optionValue = context.Memory.GetSpan(bufferPos, (int)bufferSize); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -880,10 +894,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd public ResultCode Shutdown(ServiceCtx context) { int socketFd = context.RequestData.ReadInt32(); - int how = context.RequestData.ReadInt32(); + int how = context.RequestData.ReadInt32(); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); if (socket != null) { @@ -924,9 +938,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd ReadOnlySpan<byte> sendBuffer = context.Memory.GetSpan(sendPosition, (int)sendSize); - LinuxError errno = LinuxError.EBADF; - IFileDescriptor file = _context.RetrieveFileDescriptor(fd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + IFileDescriptor file = _context.RetrieveFileDescriptor(fd); + int result = -1; if (file != null) { @@ -951,9 +965,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength); - LinuxError errno = LinuxError.EBADF; - IFileDescriptor file = _context.RetrieveFileDescriptor(fd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + IFileDescriptor file = _context.RetrieveFileDescriptor(fd); + int result = -1; if (file != null) { @@ -990,8 +1004,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // DuplicateSocket(u32 fd, u64 reserved) -> (i32 ret, u32 bsd_errno) public ResultCode DuplicateSocket(ServiceCtx context) { - int fd = context.RequestData.ReadInt32(); + int fd = context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong reserved = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 LinuxError errno = LinuxError.ENOENT; int newSockFd = -1; @@ -1016,20 +1032,22 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // RecvMMsg(u32 fd, u32 vlen, u32 flags, u32 reserved, nn::socket::TimeVal timeout) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message); public ResultCode RecvMMsg(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); - int vlen = context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); + int vlen = context.RequestData.ReadInt32(); BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32(); - uint reserved = context.RequestData.ReadUInt32(); - TimeVal timeout = context.RequestData.ReadStruct<TimeVal>(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + uint reserved = context.RequestData.ReadUInt32(); +#pragma warning restore IDE0059 + TimeVal timeout = context.RequestData.ReadStruct<TimeVal>(); ulong receivePosition = context.Request.ReceiveBuff[0].Position; ulong receiveLength = context.Request.ReceiveBuff[0].Size; WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); + int result = -1; if (socket != null) { @@ -1059,8 +1077,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // SendMMsg(u32 fd, u32 vlen, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message); public ResultCode SendMMsg(ServiceCtx context) { - int socketFd = context.RequestData.ReadInt32(); - int vlen = context.RequestData.ReadInt32(); + int socketFd = context.RequestData.ReadInt32(); + int vlen = context.RequestData.ReadInt32(); BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32(); ulong receivePosition = context.Request.ReceiveBuff[0].Position; @@ -1068,9 +1086,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength); - LinuxError errno = LinuxError.EBADF; - ISocket socket = _context.RetrieveSocket(socketFd); - int result = -1; + LinuxError errno = LinuxError.EBADF; + ISocket socket = _context.RetrieveSocket(socketFd); + int result = -1; if (socket != null) { @@ -1104,7 +1122,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd context.RequestData.BaseStream.Position += 4; // Padding ulong initialValue = context.RequestData.ReadUInt64(); - EventFileDescriptor newEventFile = new EventFileDescriptor(initialValue, flags); + EventFileDescriptor newEventFile = new(initialValue, flags); LinuxError errno = LinuxError.SUCCESS; @@ -1118,4 +1136,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, newSockFd, errno); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs index 9d4f81ce2..dbeb90222 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IFileDescriptor.cs @@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd LinuxError Write(out int writeSize, ReadOnlySpan<byte> buffer); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs index d7b53158a..937d2fd70 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs @@ -150,4 +150,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs index e0ab68c62..9039d49a7 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptorPollManager.cs @@ -13,10 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { get { - if (_instance == null) - { - _instance = new EventFileDescriptorPollManager(); - } + _instance ??= new EventFileDescriptorPollManager(); return _instance; } @@ -31,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { updatedCount = 0; - List<ManualResetEvent> waiters = new List<ManualResetEvent>(); + List<ManualResetEvent> waiters = new(); for (int i = 0; i < events.Count; i++) { @@ -119,4 +116,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl return LinuxError.EOPNOTSUPP; } } -} \ No newline at end of file +} 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 75efc49a1..dfc2a6723 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs @@ -462,7 +462,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl if (!CanSupportMMsgHdr(message)) { - Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr"); + Logger.Warning?.Print(LogClass.ServiceBsd, "Unsupported BsdMMsgHdr"); return LinuxError.EOPNOTSUPP; } @@ -500,7 +500,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl if (!CanSupportMMsgHdr(message)) { - Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr"); + Logger.Warning?.Print(LogClass.ServiceBsd, "Unsupported BsdMMsgHdr"); return LinuxError.EOPNOTSUPP; } @@ -527,4 +527,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs index 1b305dfb7..10d9882c1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs @@ -13,10 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { get { - if (_instance == null) - { - _instance = new ManagedSocketPollManager(); - } + _instance ??= new ManagedSocketPollManager(); return _instance; } @@ -29,9 +26,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl public LinuxError Poll(List<PollEvent> events, int timeoutMilliseconds, out int updatedCount) { - List<Socket> readEvents = new List<Socket>(); - List<Socket> writeEvents = new List<Socket>(); - List<Socket> errorEvents = new List<Socket>(); + List<Socket> readEvents = new(); + List<Socket> writeEvents = new(); + List<Socket> errorEvents = new(); updatedCount = 0; @@ -174,4 +171,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl return LinuxError.SUCCESS; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs index 0f24a57f5..5f3495df5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs @@ -9,77 +9,77 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl * All Windows Sockets error constants are biased by WSABASEERR from * the "normal" */ - WSABASEERR = 10000, + WSABASEERR = 10000, /* * Windows Sockets definitions of regular Microsoft C error constants */ - WSAEINTR = (WSABASEERR + 4), - WSAEBADF = (WSABASEERR + 9), - WSAEACCES = (WSABASEERR + 13), - WSAEFAULT = (WSABASEERR + 14), - WSAEINVAL = (WSABASEERR + 22), - WSAEMFILE = (WSABASEERR + 24), + WSAEINTR = (WSABASEERR + 4), + WSAEBADF = (WSABASEERR + 9), + WSAEACCES = (WSABASEERR + 13), + WSAEFAULT = (WSABASEERR + 14), + WSAEINVAL = (WSABASEERR + 22), + WSAEMFILE = (WSABASEERR + 24), /* * Windows Sockets definitions of regular Berkeley error constants */ - WSAEWOULDBLOCK = (WSABASEERR + 35), - WSAEINPROGRESS = (WSABASEERR + 36), - WSAEALREADY = (WSABASEERR + 37), - WSAENOTSOCK = (WSABASEERR + 38), - WSAEDESTADDRREQ = (WSABASEERR + 39), - WSAEMSGSIZE = (WSABASEERR + 40), - WSAEPROTOTYPE = (WSABASEERR + 41), - WSAENOPROTOOPT = (WSABASEERR + 42), - WSAEPROTONOSUPPORT = (WSABASEERR + 43), - WSAESOCKTNOSUPPORT = (WSABASEERR + 44), - WSAEOPNOTSUPP = (WSABASEERR + 45), - WSAEPFNOSUPPORT = (WSABASEERR + 46), - WSAEAFNOSUPPORT = (WSABASEERR + 47), - WSAEADDRINUSE = (WSABASEERR + 48), - WSAEADDRNOTAVAIL = (WSABASEERR + 49), - WSAENETDOWN = (WSABASEERR + 50), - WSAENETUNREACH = (WSABASEERR + 51), - WSAENETRESET = (WSABASEERR + 52), - WSAECONNABORTED = (WSABASEERR + 53), - WSAECONNRESET = (WSABASEERR + 54), - WSAENOBUFS = (WSABASEERR + 55), - WSAEISCONN = (WSABASEERR + 56), - WSAENOTCONN = (WSABASEERR + 57), - WSAESHUTDOWN = (WSABASEERR + 58), - WSAETOOMANYREFS = (WSABASEERR + 59), - WSAETIMEDOUT = (WSABASEERR + 60), - WSAECONNREFUSED = (WSABASEERR + 61), - WSAELOOP = (WSABASEERR + 62), - WSAENAMETOOLONG = (WSABASEERR + 63), - WSAEHOSTDOWN = (WSABASEERR + 64), - WSAEHOSTUNREACH = (WSABASEERR + 65), - WSAENOTEMPTY = (WSABASEERR + 66), - WSAEPROCLIM = (WSABASEERR + 67), - WSAEUSERS = (WSABASEERR + 68), - WSAEDQUOT = (WSABASEERR + 69), - WSAESTALE = (WSABASEERR + 70), - WSAEREMOTE = (WSABASEERR + 71), + WSAEWOULDBLOCK = (WSABASEERR + 35), + WSAEINPROGRESS = (WSABASEERR + 36), + WSAEALREADY = (WSABASEERR + 37), + WSAENOTSOCK = (WSABASEERR + 38), + WSAEDESTADDRREQ = (WSABASEERR + 39), + WSAEMSGSIZE = (WSABASEERR + 40), + WSAEPROTOTYPE = (WSABASEERR + 41), + WSAENOPROTOOPT = (WSABASEERR + 42), + WSAEPROTONOSUPPORT = (WSABASEERR + 43), + WSAESOCKTNOSUPPORT = (WSABASEERR + 44), + WSAEOPNOTSUPP = (WSABASEERR + 45), + WSAEPFNOSUPPORT = (WSABASEERR + 46), + WSAEAFNOSUPPORT = (WSABASEERR + 47), + WSAEADDRINUSE = (WSABASEERR + 48), + WSAEADDRNOTAVAIL = (WSABASEERR + 49), + WSAENETDOWN = (WSABASEERR + 50), + WSAENETUNREACH = (WSABASEERR + 51), + WSAENETRESET = (WSABASEERR + 52), + WSAECONNABORTED = (WSABASEERR + 53), + WSAECONNRESET = (WSABASEERR + 54), + WSAENOBUFS = (WSABASEERR + 55), + WSAEISCONN = (WSABASEERR + 56), + WSAENOTCONN = (WSABASEERR + 57), + WSAESHUTDOWN = (WSABASEERR + 58), + WSAETOOMANYREFS = (WSABASEERR + 59), + WSAETIMEDOUT = (WSABASEERR + 60), + WSAECONNREFUSED = (WSABASEERR + 61), + WSAELOOP = (WSABASEERR + 62), + WSAENAMETOOLONG = (WSABASEERR + 63), + WSAEHOSTDOWN = (WSABASEERR + 64), + WSAEHOSTUNREACH = (WSABASEERR + 65), + WSAENOTEMPTY = (WSABASEERR + 66), + WSAEPROCLIM = (WSABASEERR + 67), + WSAEUSERS = (WSABASEERR + 68), + WSAEDQUOT = (WSABASEERR + 69), + WSAESTALE = (WSABASEERR + 70), + WSAEREMOTE = (WSABASEERR + 71), /* * Extended Windows Sockets error constant definitions */ - WSASYSNOTREADY = (WSABASEERR + 91), - WSAVERNOTSUPPORTED = (WSABASEERR + 92), - WSANOTINITIALISED = (WSABASEERR + 93), - WSAEDISCON = (WSABASEERR + 101), - WSAENOMORE = (WSABASEERR + 102), - WSAECANCELLED = (WSABASEERR + 103), - WSAEINVALIDPROCTABLE = (WSABASEERR + 104), - WSAEINVALIDPROVIDER = (WSABASEERR + 105), - WSAEPROVIDERFAILEDINIT = (WSABASEERR + 106), - WSASYSCALLFAILURE = (WSABASEERR + 107), - WSASERVICE_NOT_FOUND = (WSABASEERR + 108), - WSATYPE_NOT_FOUND = (WSABASEERR + 109), - WSA_E_NO_MORE = (WSABASEERR + 110), - WSA_E_CANCELLED = (WSABASEERR + 111), - WSAEREFUSED = (WSABASEERR + 112), + WSASYSNOTREADY = (WSABASEERR + 91), + WSAVERNOTSUPPORTED = (WSABASEERR + 92), + WSANOTINITIALISED = (WSABASEERR + 93), + WSAEDISCON = (WSABASEERR + 101), + WSAENOMORE = (WSABASEERR + 102), + WSAECANCELLED = (WSABASEERR + 103), + WSAEINVALIDPROCTABLE = (WSABASEERR + 104), + WSAEINVALIDPROVIDER = (WSABASEERR + 105), + WSAEPROVIDERFAILEDINIT = (WSABASEERR + 106), + WSASYSCALLFAILURE = (WSABASEERR + 107), + WSASERVICE_NOT_FOUND = (WSABASEERR + 108), + WSATYPE_NOT_FOUND = (WSABASEERR + 109), + WSA_E_NO_MORE = (WSABASEERR + 110), + WSA_E_CANCELLED = (WSABASEERR + 111), + WSAEREFUSED = (WSABASEERR + 112), /* * Error return codes from gethostbyname() and gethostbyaddr() @@ -93,42 +93,42 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl */ /* Authoritative Answer: Host not found */ - WSAHOST_NOT_FOUND = (WSABASEERR + 1001), + WSAHOST_NOT_FOUND = (WSABASEERR + 1001), /* Non-Authoritative: Host not found, or SERVERFAIL */ - WSATRY_AGAIN = (WSABASEERR + 1002), + WSATRY_AGAIN = (WSABASEERR + 1002), /* Non-recoverable errors, FORMERR, REFUSED, NOTIMP */ - WSANO_RECOVERY = (WSABASEERR + 1003), + WSANO_RECOVERY = (WSABASEERR + 1003), /* Valid name, no data record of requested type */ - WSANO_DATA = (WSABASEERR + 1004), + WSANO_DATA = (WSABASEERR + 1004), /* * Define QOS related error return codes * */ - WSA_QOS_RECEIVERS = (WSABASEERR + 1005), + WSA_QOS_RECEIVERS = (WSABASEERR + 1005), /* at least one Reserve has arrived */ - WSA_QOS_SENDERS = (WSABASEERR + 1006), + WSA_QOS_SENDERS = (WSABASEERR + 1006), /* at least one Path has arrived */ - WSA_QOS_NO_SENDERS = (WSABASEERR + 1007), + WSA_QOS_NO_SENDERS = (WSABASEERR + 1007), /* there are no senders */ - WSA_QOS_NO_RECEIVERS = (WSABASEERR + 1008), + WSA_QOS_NO_RECEIVERS = (WSABASEERR + 1008), /* there are no receivers */ - WSA_QOS_REQUEST_CONFIRMED = (WSABASEERR + 1009), + WSA_QOS_REQUEST_CONFIRMED = (WSABASEERR + 1009), /* Reserve has been confirmed */ - WSA_QOS_ADMISSION_FAILURE = (WSABASEERR + 1010), + WSA_QOS_ADMISSION_FAILURE = (WSABASEERR + 1010), /* error due to lack of resources */ - WSA_QOS_POLICY_FAILURE = (WSABASEERR + 1011), + WSA_QOS_POLICY_FAILURE = (WSABASEERR + 1011), /* rejected for administrative reasons - bad credentials */ - WSA_QOS_BAD_STYLE = (WSABASEERR + 1012), + WSA_QOS_BAD_STYLE = (WSABASEERR + 1012), /* unknown or conflicting style */ - WSA_QOS_BAD_OBJECT = (WSABASEERR + 1013), + WSA_QOS_BAD_OBJECT = (WSABASEERR + 1013), /* problem with some part of the filterspec or providerspecific * buffer in general */ WSA_QOS_TRAFFIC_CTRL_ERROR = (WSABASEERR + 1014), /* problem with some part of the flowspec */ - WSA_QOS_GENERIC_ERROR = (WSABASEERR + 1015) + WSA_QOS_GENERIC_ERROR = (WSABASEERR + 1015), } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs index 5668d30b0..9df180235 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs @@ -1,5 +1,5 @@ using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types; -using System; +using System; using System.Collections.Generic; using System.Net.Sockets; @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl // WSAEFAULT { WsaError.WSAEFAULT, LinuxError.EFAULT }, // NOERROR - { 0, 0 } + { 0, 0 }, }; private static readonly Dictionary<int, LinuxError> _errorMapMacOs = new() @@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { 59, LinuxError.ETOOMANYREFS }, { 92, LinuxError.EILSEQ }, { 89, LinuxError.ECANCELED }, - { 84, LinuxError.EOVERFLOW } + { 84, LinuxError.EOVERFLOW }, }; private static readonly Dictionary<BsdSocketOption, SocketOptionName> _soSocketOptionMap = new() @@ -157,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { BsdSocketOption.SoSndTimeo, SocketOptionName.SendTimeout }, { BsdSocketOption.SoRcvTimeo, SocketOptionName.ReceiveTimeout }, { BsdSocketOption.SoError, SocketOptionName.Error }, - { BsdSocketOption.SoType, SocketOptionName.Type } + { BsdSocketOption.SoType, SocketOptionName.Type }, }; private static readonly Dictionary<BsdSocketOption, SocketOptionName> _ipSocketOptionMap = new() @@ -172,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { BsdSocketOption.IpDropMembership, SocketOptionName.DropMembership }, { BsdSocketOption.IpDontFrag, SocketOptionName.DontFragment }, { BsdSocketOption.IpAddSourceMembership, SocketOptionName.AddSourceMembership }, - { BsdSocketOption.IpDropSourceMembership, SocketOptionName.DropSourceMembership } + { BsdSocketOption.IpDropSourceMembership, SocketOptionName.DropSourceMembership }, }; private static readonly Dictionary<BsdSocketOption, SocketOptionName> _tcpSocketOptionMap = new() @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { BsdSocketOption.TcpNoDelay, SocketOptionName.NoDelay }, { BsdSocketOption.TcpKeepIdle, SocketOptionName.TcpKeepAliveTime }, { BsdSocketOption.TcpKeepIntvl, SocketOptionName.TcpKeepAliveInterval }, - { BsdSocketOption.TcpKeepCnt, SocketOptionName.TcpKeepAliveRetryCount } + { BsdSocketOption.TcpKeepCnt, SocketOptionName.TcpKeepAliveRetryCount }, }; public static LinuxError ConvertError(WsaError errorCode) @@ -210,7 +210,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl SocketOptionLevel.Socket => _soSocketOptionMap, SocketOptionLevel.IP => _ipSocketOptionMap, SocketOptionLevel.Tcp => _tcpSocketOptionMap, - _ => null + _ => null, }; if (table == null) @@ -222,4 +222,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl return table.TryGetValue(option, out name); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs index 798fc0157..9afdf2503 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs @@ -5,4 +5,4 @@ { public ServerInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs index 37461bb25..747946386 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdAddressFamily.cs @@ -6,6 +6,6 @@ InterNetwork = 2, InterNetworkV6 = 28, - Unknown = uint.MaxValue + Unknown = uint.MaxValue, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs index 1dfa5a5f9..9c330e35d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs @@ -2,6 +2,6 @@ { enum BsdIoctl { - AtMark = 0x40047307 + AtMark = 0x40047307, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs index 67c11e54d..af3a44e88 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types public IPEndPoint ToIPEndPoint() { - IPAddress address = new IPAddress(Address.AsSpan()); + IPAddress address = new(Address.AsSpan()); int port = (ushort)IPAddress.NetworkToHostOrder((short)Port); return new IPEndPoint(address, port); @@ -24,11 +24,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types public static BsdSockAddr FromIPEndPoint(IPEndPoint endpoint) { - BsdSockAddr result = new BsdSockAddr + BsdSockAddr result = new() { Length = 0, Family = (byte)endpoint.AddressFamily, - Port = (ushort)IPAddress.HostToNetworkOrder((short)endpoint.Port) + Port = (ushort)IPAddress.HostToNetworkOrder((short)endpoint.Port), }; endpoint.Address.GetAddressBytes().AsSpan().CopyTo(result.Address.AsSpan()); diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs index be5991ff6..ac79deb31 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketCreationFlags.cs @@ -9,6 +9,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types CloseOnExecution = 1, NonBlocking = 2, - FlagsShift = 28 + FlagsShift = 28, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs index 4408c89af..639981291 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketFlags.cs @@ -17,6 +17,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types Compat = 0x8000, SoCallbck = 0x10000, NoSignal = 0x20000, - CMsgCloExec = 0x40000 + CMsgCloExec = 0x40000, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs index 4d0d1dcf3..5bc3e81f4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types { + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum BsdSocketOption { SoDebug = 0x1, @@ -114,6 +117,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types TcpKeepInit = 128, TcpKeepIdle = 256, TcpKeepIntvl = 512, - TcpKeepCnt = 1024 + TcpKeepCnt = 1024, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs index 13230ac39..883e3c31f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketShutdownFlags.cs @@ -4,6 +4,6 @@ { Receive, Send, - ReceiveAndSend + ReceiveAndSend, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs index e01d82267..996facd35 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/EventFdFlags.cs @@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types { None = 0, Semaphore = 1 << 0, - NonBlocking = 1 << 2 + NonBlocking = 1 << 2, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs index d36638784..66b1bcf1b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/IPollManager.cs @@ -10,4 +10,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types LinuxError Select(List<PollEvent> events, int timeout, out int updatedCount); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs index 96602830b..aaeee44d1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/LinuxError.cs @@ -5,44 +5,44 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types [SuppressMessage("ReSharper", "InconsistentNaming")] enum LinuxError { - SUCCESS = 0, - EPERM = 1 /* Operation not permitted */, - ENOENT = 2 /* No such file or directory */, - ESRCH = 3 /* No such process */, - EINTR = 4 /* Interrupted system call */, - EIO = 5 /* I/O error */, - ENXIO = 6 /* No such device or address */, - E2BIG = 7 /* Argument list too long */, - ENOEXEC = 8 /* Exec format error */, - EBADF = 9 /* Bad file number */, - ECHILD = 10 /* No child processes */, - EAGAIN = 11 /* Try again */, - ENOMEM = 12 /* Out of memory */, - EACCES = 13 /* Permission denied */, - EFAULT = 14 /* Bad address */, - ENOTBLK = 15 /* Block device required */, - EBUSY = 16 /* Device or resource busy */, - EEXIST = 17 /* File exists */, - EXDEV = 18 /* Cross-device link */, - ENODEV = 19 /* No such device */, - ENOTDIR = 20 /* Not a directory */, - EISDIR = 21 /* Is a directory */, - EINVAL = 22 /* Invalid argument */, - ENFILE = 23 /* File table overflow */, - EMFILE = 24 /* Too many open files */, - ENOTTY = 25 /* Not a typewriter */, - ETXTBSY = 26 /* Text file busy */, - EFBIG = 27 /* File too large */, - ENOSPC = 28 /* No space left on device */, - ESPIPE = 29 /* Illegal seek */, - EROFS = 30 /* Read-only file system */, - EMLINK = 31 /* Too many links */, - EPIPE = 32 /* Broken pipe */, - EDOM = 33 /* Math argument out of domain of func */, - ERANGE = 34 /* Math result not representable */, - EDEADLK = 35 /* Resource deadlock would occur */, - ENAMETOOLONG = 36 /* File name too long */, - ENOLCK = 37 /* No record locks available */, + SUCCESS = 0, + EPERM = 1 /* Operation not permitted */, + ENOENT = 2 /* No such file or directory */, + ESRCH = 3 /* No such process */, + EINTR = 4 /* Interrupted system call */, + EIO = 5 /* I/O error */, + ENXIO = 6 /* No such device or address */, + E2BIG = 7 /* Argument list too long */, + ENOEXEC = 8 /* Exec format error */, + EBADF = 9 /* Bad file number */, + ECHILD = 10 /* No child processes */, + EAGAIN = 11 /* Try again */, + ENOMEM = 12 /* Out of memory */, + EACCES = 13 /* Permission denied */, + EFAULT = 14 /* Bad address */, + ENOTBLK = 15 /* Block device required */, + EBUSY = 16 /* Device or resource busy */, + EEXIST = 17 /* File exists */, + EXDEV = 18 /* Cross-device link */, + ENODEV = 19 /* No such device */, + ENOTDIR = 20 /* Not a directory */, + EISDIR = 21 /* Is a directory */, + EINVAL = 22 /* Invalid argument */, + ENFILE = 23 /* File table overflow */, + EMFILE = 24 /* Too many open files */, + ENOTTY = 25 /* Not a typewriter */, + ETXTBSY = 26 /* Text file busy */, + EFBIG = 27 /* File too large */, + ENOSPC = 28 /* No space left on device */, + ESPIPE = 29 /* Illegal seek */, + EROFS = 30 /* Read-only file system */, + EMLINK = 31 /* Too many links */, + EPIPE = 32 /* Broken pipe */, + EDOM = 33 /* Math argument out of domain of func */, + ERANGE = 34 /* Math result not representable */, + EDEADLK = 35 /* Resource deadlock would occur */, + ENAMETOOLONG = 36 /* File name too long */, + ENOLCK = 37 /* No record locks available */, /* * This error code is special: arch syscall entry code will return @@ -51,105 +51,105 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ - ENOSYS = 38 /* Invalid system call number */, - ENOTEMPTY = 39 /* Directory not empty */, - ELOOP = 40 /* Too many symbolic links encountered */, - EWOULDBLOCK = EAGAIN /* Operation would block */, - ENOMSG = 42 /* No message of desired type */, - EIDRM = 43 /* Identifier removed */, - ECHRNG = 44 /* Channel number out of range */, - EL2NSYNC = 45 /* Level 2 not synchronized */, - EL3HLT = 46 /* Level 3 halted */, - EL3RST = 47 /* Level 3 reset */, - ELNRNG = 48 /* Link number out of range */, - EUNATCH = 49 /* Protocol driver not attached */, - ENOCSI = 50 /* No CSI structure available */, - EL2HLT = 51 /* Level 2 halted */, - EBADE = 52 /* Invalid exchange */, - EBADR = 53 /* Invalid request descriptor */, - EXFULL = 54 /* Exchange full */, - ENOANO = 55 /* No anode */, - EBADRQC = 56 /* Invalid request code */, - EBADSLT = 57 /* Invalid slot */, - EDEADLOCK = EDEADLK, - EBFONT = 59 /* Bad font file format */, - ENOSTR = 60 /* Device not a stream */, - ENODATA = 61 /* No data available */, - ETIME = 62 /* Timer expired */, - ENOSR = 63 /* Out of streams resources */, - ENONET = 64 /* Machine is not on the network */, - ENOPKG = 65 /* Package not installed */, - EREMOTE = 66 /* Object is remote */, - ENOLINK = 67 /* Link has been severed */, - EADV = 68 /* Advertise error */, - ESRMNT = 69 /* Srmount error */, - ECOMM = 70 /* Communication error on send */, - EPROTO = 71 /* Protocol error */, - EMULTIHOP = 72 /* Multihop attempted */, - EDOTDOT = 73 /* RFS specific error */, - EBADMSG = 74 /* Not a data message */, - EOVERFLOW = 75 /* Value too large for defined data type */, - ENOTUNIQ = 76 /* Name not unique on network */, - EBADFD = 77 /* File descriptor in bad state */, - EREMCHG = 78 /* Remote address changed */, - ELIBACC = 79 /* Can not access a needed shared library */, - ELIBBAD = 80 /* Accessing a corrupted shared library */, - ELIBSCN = 81 /* .lib section in a.out corrupted */, - ELIBMAX = 82 /* Attempting to link in too many shared libraries */, - ELIBEXEC = 83 /* Cannot exec a shared library directly */, - EILSEQ = 84 /* Illegal byte sequence */, - ERESTART = 85 /* Interrupted system call should be restarted */, - ESTRPIPE = 86 /* Streams pipe error */, - EUSERS = 87 /* Too many users */, - ENOTSOCK = 88 /* Socket operation on non-socket */, - EDESTADDRREQ = 89 /* Destination address required */, - EMSGSIZE = 90 /* Message too long */, - EPROTOTYPE = 91 /* Protocol wrong type for socket */, - ENOPROTOOPT = 92 /* Protocol not available */, + ENOSYS = 38 /* Invalid system call number */, + ENOTEMPTY = 39 /* Directory not empty */, + ELOOP = 40 /* Too many symbolic links encountered */, + EWOULDBLOCK = EAGAIN /* Operation would block */, + ENOMSG = 42 /* No message of desired type */, + EIDRM = 43 /* Identifier removed */, + ECHRNG = 44 /* Channel number out of range */, + EL2NSYNC = 45 /* Level 2 not synchronized */, + EL3HLT = 46 /* Level 3 halted */, + EL3RST = 47 /* Level 3 reset */, + ELNRNG = 48 /* Link number out of range */, + EUNATCH = 49 /* Protocol driver not attached */, + ENOCSI = 50 /* No CSI structure available */, + EL2HLT = 51 /* Level 2 halted */, + EBADE = 52 /* Invalid exchange */, + EBADR = 53 /* Invalid request descriptor */, + EXFULL = 54 /* Exchange full */, + ENOANO = 55 /* No anode */, + EBADRQC = 56 /* Invalid request code */, + EBADSLT = 57 /* Invalid slot */, + EDEADLOCK = EDEADLK, + EBFONT = 59 /* Bad font file format */, + ENOSTR = 60 /* Device not a stream */, + ENODATA = 61 /* No data available */, + ETIME = 62 /* Timer expired */, + ENOSR = 63 /* Out of streams resources */, + ENONET = 64 /* Machine is not on the network */, + ENOPKG = 65 /* Package not installed */, + EREMOTE = 66 /* Object is remote */, + ENOLINK = 67 /* Link has been severed */, + EADV = 68 /* Advertise error */, + ESRMNT = 69 /* Srmount error */, + ECOMM = 70 /* Communication error on send */, + EPROTO = 71 /* Protocol error */, + EMULTIHOP = 72 /* Multihop attempted */, + EDOTDOT = 73 /* RFS specific error */, + EBADMSG = 74 /* Not a data message */, + EOVERFLOW = 75 /* Value too large for defined data type */, + ENOTUNIQ = 76 /* Name not unique on network */, + EBADFD = 77 /* File descriptor in bad state */, + EREMCHG = 78 /* Remote address changed */, + ELIBACC = 79 /* Can not access a needed shared library */, + ELIBBAD = 80 /* Accessing a corrupted shared library */, + ELIBSCN = 81 /* .lib section in a.out corrupted */, + ELIBMAX = 82 /* Attempting to link in too many shared libraries */, + ELIBEXEC = 83 /* Cannot exec a shared library directly */, + EILSEQ = 84 /* Illegal byte sequence */, + ERESTART = 85 /* Interrupted system call should be restarted */, + ESTRPIPE = 86 /* Streams pipe error */, + EUSERS = 87 /* Too many users */, + ENOTSOCK = 88 /* Socket operation on non-socket */, + EDESTADDRREQ = 89 /* Destination address required */, + EMSGSIZE = 90 /* Message too long */, + EPROTOTYPE = 91 /* Protocol wrong type for socket */, + ENOPROTOOPT = 92 /* Protocol not available */, EPROTONOSUPPORT = 93 /* Protocol not supported */, ESOCKTNOSUPPORT = 94 /* Socket type not supported */, - EOPNOTSUPP = 95 /* Operation not supported on transport endpoint */, - EPFNOSUPPORT = 96 /* Protocol family not supported */, - EAFNOSUPPORT = 97 /* Address family not supported by protocol */, - EADDRINUSE = 98 /* Address already in use */, - EADDRNOTAVAIL = 99 /* Cannot assign requested address */, - ENETDOWN = 100 /* Network is down */, - ENETUNREACH = 101 /* Network is unreachable */, - ENETRESET = 102 /* Network dropped connection because of reset */, - ECONNABORTED = 103 /* Software caused connection abort */, - ECONNRESET = 104 /* Connection reset by peer */, - ENOBUFS = 105 /* No buffer space available */, - EISCONN = 106 /* Transport endpoint is already connected */, - ENOTCONN = 107 /* Transport endpoint is not connected */, - ESHUTDOWN = 108 /* Cannot send after transport endpoint shutdown */, - ETOOMANYREFS = 109 /* Too many references: cannot splice */, - ETIMEDOUT = 110 /* Connection timed out */, - ECONNREFUSED = 111 /* Connection refused */, - EHOSTDOWN = 112 /* Host is down */, - EHOSTUNREACH = 113 /* No route to host */, - EALREADY = 114 /* Operation already in progress */, - EINPROGRESS = 115 /* Operation now in progress */, - ESTALE = 116 /* Stale file handle */, - EUCLEAN = 117 /* Structure needs cleaning */, - ENOTNAM = 118 /* Not a XENIX named type file */, - ENAVAIL = 119 /* No XENIX semaphores available */, - EISNAM = 120 /* Is a named type file */, - EREMOTEIO = 121 /* Remote I/O error */, - EDQUOT = 122 /* Quota exceeded */, - ENOMEDIUM = 123 /* No medium found */, - EMEDIUMTYPE = 124 /* Wrong medium type */, - ECANCELED = 125 /* Operation Canceled */, - ENOKEY = 126 /* Required key not available */, - EKEYEXPIRED = 127 /* Key has expired */, - EKEYREVOKED = 128 /* Key has been revoked */, - EKEYREJECTED = 129 /* Key was rejected by service */, + EOPNOTSUPP = 95 /* Operation not supported on transport endpoint */, + EPFNOSUPPORT = 96 /* Protocol family not supported */, + EAFNOSUPPORT = 97 /* Address family not supported by protocol */, + EADDRINUSE = 98 /* Address already in use */, + EADDRNOTAVAIL = 99 /* Cannot assign requested address */, + ENETDOWN = 100 /* Network is down */, + ENETUNREACH = 101 /* Network is unreachable */, + ENETRESET = 102 /* Network dropped connection because of reset */, + ECONNABORTED = 103 /* Software caused connection abort */, + ECONNRESET = 104 /* Connection reset by peer */, + ENOBUFS = 105 /* No buffer space available */, + EISCONN = 106 /* Transport endpoint is already connected */, + ENOTCONN = 107 /* Transport endpoint is not connected */, + ESHUTDOWN = 108 /* Cannot send after transport endpoint shutdown */, + ETOOMANYREFS = 109 /* Too many references: cannot splice */, + ETIMEDOUT = 110 /* Connection timed out */, + ECONNREFUSED = 111 /* Connection refused */, + EHOSTDOWN = 112 /* Host is down */, + EHOSTUNREACH = 113 /* No route to host */, + EALREADY = 114 /* Operation already in progress */, + EINPROGRESS = 115 /* Operation now in progress */, + ESTALE = 116 /* Stale file handle */, + EUCLEAN = 117 /* Structure needs cleaning */, + ENOTNAM = 118 /* Not a XENIX named type file */, + ENAVAIL = 119 /* No XENIX semaphores available */, + EISNAM = 120 /* Is a named type file */, + EREMOTEIO = 121 /* Remote I/O error */, + EDQUOT = 122 /* Quota exceeded */, + ENOMEDIUM = 123 /* No medium found */, + EMEDIUMTYPE = 124 /* Wrong medium type */, + ECANCELED = 125 /* Operation Canceled */, + ENOKEY = 126 /* Required key not available */, + EKEYEXPIRED = 127 /* Key has expired */, + EKEYREVOKED = 128 /* Key has been revoked */, + EKEYREJECTED = 129 /* Key was rejected by service */, /* for robust mutexes */ - EOWNERDEAD = 130 /* Owner died */, + EOWNERDEAD = 130 /* Owner died */, ENOTRECOVERABLE = 131 /* State not recoverable */, - ERFKILL = 132 /* Operation not possible due to RF-kill */, + ERFKILL = 132 /* Operation not possible due to RF-kill */, - EHWPOISON = 133 /* Memory page has hardware error */ + EHWPOISON = 133, /* Memory page has hardware error */ } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs index 8b77a6c2f..27a96bd81 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs @@ -11,4 +11,4 @@ FileDescriptor = fileDescriptor; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs index 546b738ee..16d01055c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventData.cs @@ -2,10 +2,10 @@ { struct PollEventData { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public int SocketFd; public PollEventTypeMask InputEvents; #pragma warning restore CS0649 public PollEventTypeMask OutputEvents; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs index f434fa032..d4c96c81f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEventTypeMask.cs @@ -10,6 +10,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types Output = 4, Error = 8, Disconnected = 0x10, - Invalid = 0x20 + Invalid = 0x20, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs index f58776977..1c216ea58 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterface.cs @@ -5,4 +5,4 @@ { public IEthInterface(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs index 9832e4483..3147a8928 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Ethc/IEthInterfaceGroup.cs @@ -5,4 +5,4 @@ { public IEthInterfaceGroup(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs index 0b7adff4f..c991db3fb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs @@ -14,9 +14,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd class IManager : IpcService { public static readonly NsdSettings NsdSettings; +#pragma warning disable IDE0052 // Remove unread private member private readonly FqdnResolver _fqdnResolver; +#pragma warning restore IDE0052 - private bool _isInitialized = false; + private readonly bool _isInitialized = false; public IManager(ServiceCtx context) { @@ -43,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd { Initialized = true, TestMode = (bool)testMode, - Environment = (string)environmentIdentifier + Environment = (string)environmentIdentifier, }; } @@ -158,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd public ResultCode Resolve(ServiceCtx context) { ulong outputPosition = context.Request.ReceiveBuff[0].Position; - ulong outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputSize = context.Request.ReceiveBuff[0].Size; ResultCode result = _fqdnResolver.ResolveEx(context, out _, out string resolvedAddress); @@ -181,7 +183,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd public ResultCode ResolveEx(ServiceCtx context) { ulong outputPosition = context.Request.ReceiveBuff[0].Position; - ulong outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputSize = context.Request.ReceiveBuff[0].Size; ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress); @@ -377,7 +379,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd "sd" => (byte)ApplicationServerEnvironmentType.Sd, "sp" => (byte)ApplicationServerEnvironmentType.Sp, "dp" => (byte)ApplicationServerEnvironmentType.Dp, - _ => (byte)ApplicationServerEnvironmentType.None + _ => (byte)ApplicationServerEnvironmentType.None, }; context.ResponseData.Write(environmentType); @@ -399,4 +401,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd throw new ServiceNotImplementedException(this, context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs index 4096e431f..a8fbcf092 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager { class FqdnResolver { - private const string _dummyAddress = "unknown.dummy.nintendo.net"; + private const string DummyAddress = "unknown.dummy.nintendo.net"; public ResultCode GetEnvironmentIdentifier(out string identifier) { @@ -24,8 +24,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager public static ResultCode Resolve(string address, out string resolvedAddress) { - if (address == "api.sect.srv.nintendo.net" || - address == "ctest.cdn.nintendo.net" || + if (address == "api.sect.srv.nintendo.net" || + address == "ctest.cdn.nintendo.net" || address == "ctest.cdn.n.nintendoswitch.cn" || address == "unknown.dummy.nintendo.net") { @@ -50,6 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager resolvedAddress = address switch { +#pragma warning disable IDE0055 // Disable formatting "e97b8a9d672e4ce4845ec6947cd66ef6-sb-api.accounts.nintendo.com" => "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com", // dp1 environment "api.accounts.nintendo.com" => "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com", // dp1 environment "e97b8a9d672e4ce4845ec6947cd66ef6-sb.accounts.nintendo.com" => "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com", // lp1 environment @@ -60,6 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager this + 0x2BE8 => this + 0x2BE8 + 0x300 */ _ => address, +#pragma warning restore IDE0055 }; } @@ -69,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager public ResultCode ResolveEx(ServiceCtx context, out ResultCode resultCode, out string resolvedAddress) { ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; + ulong inputSize = context.Request.SendBuff[0].Size; byte[] addressBuffer = new byte[inputSize]; @@ -81,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager if (resultCode != ResultCode.Success) { - resolvedAddress = _dummyAddress; + resolvedAddress = DummyAddress; } if (IManager.NsdSettings.TestMode) @@ -94,4 +96,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs index 993fbe8af..5a62922fc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs @@ -2,18 +2,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd { enum ResultCode { - ModuleId = 141, + ModuleId = 141, ErrorCodeShift = 9, Success = 0, - InvalidSettingsValue = ( 1 << ErrorCodeShift) | ModuleId, - InvalidObject1 = ( 3 << ErrorCodeShift) | ModuleId, - InvalidObject2 = ( 4 << ErrorCodeShift) | ModuleId, - NullOutputObject = ( 5 << ErrorCodeShift) | ModuleId, - SettingsNotLoaded = ( 6 << ErrorCodeShift) | ModuleId, - InvalidArgument = ( 8 << ErrorCodeShift) | ModuleId, - SettingsNotInitialized = ( 10 << ErrorCodeShift) | ModuleId, - ServiceNotInitialized = (400 << ErrorCodeShift) | ModuleId, + InvalidSettingsValue = (1 << ErrorCodeShift) | ModuleId, + InvalidObject1 = (3 << ErrorCodeShift) | ModuleId, + InvalidObject2 = (4 << ErrorCodeShift) | ModuleId, + NullOutputObject = (5 << ErrorCodeShift) | ModuleId, + SettingsNotLoaded = (6 << ErrorCodeShift) | ModuleId, + InvalidArgument = (8 << ErrorCodeShift) | ModuleId, + SettingsNotInitialized = (10 << ErrorCodeShift) | ModuleId, + ServiceNotInitialized = (400 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs index 150bdab4f..1acb69fce 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs @@ -6,6 +6,6 @@ Lp, Sd, Sp, - Dp + Dp, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs index 0a72fa874..08fb15e0a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs @@ -2,8 +2,8 @@ { class NsdSettings { - public bool Initialized; - public bool TestMode; + public bool Initialized; + public bool TestMode; public string Environment; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs index 64c3acbb5..93960d13e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs @@ -28,8 +28,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode SetDnsAddressesPrivateRequest(ServiceCtx context) { uint cancelHandleRequest = context.RequestData.ReadUInt32(); - ulong bufferPosition = context.Request.SendBuff[0].Position; - ulong bufferSize = context.Request.SendBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong bufferPosition = context.Request.SendBuff[0].Position; + ulong bufferSize = context.Request.SendBuff[0].Size; +#pragma warning restore IDE0059 // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness. Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest }); @@ -42,8 +44,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode GetDnsAddressPrivateRequest(ServiceCtx context) { uint cancelHandleRequest = context.RequestData.ReadUInt32(); - ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferSize = context.Request.ReceiveBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; +#pragma warning restore IDE0059 // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness. Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest }); @@ -56,10 +60,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode GetHostByNameRequest(ServiceCtx context) { ulong inputBufferPosition = context.Request.SendBuff[0].Position; - ulong inputBufferSize = context.Request.SendBuff[0].Size; + ulong inputBufferSize = context.Request.SendBuff[0].Size; ulong outputBufferPosition = context.Request.ReceiveBuff[0].Position; - ulong outputBufferSize = context.Request.ReceiveBuff[0].Size; + ulong outputBufferSize = context.Request.ReceiveBuff[0].Size; return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, false, 0, 0); } @@ -69,10 +73,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode GetHostByAddrRequest(ServiceCtx context) { ulong inputBufferPosition = context.Request.SendBuff[0].Position; - ulong inputBufferSize = context.Request.SendBuff[0].Size; + ulong inputBufferSize = context.Request.SendBuff[0].Size; ulong outputBufferPosition = context.Request.ReceiveBuff[0].Position; - ulong outputBufferSize = context.Request.ReceiveBuff[0].Size; + ulong outputBufferSize = context.Request.ReceiveBuff[0].Size; return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, false, 0, 0); } @@ -82,20 +86,20 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode GetHostStringErrorRequest(ServiceCtx context) { ResultCode resultCode = ResultCode.NotAllocated; - NetDbError errorCode = (NetDbError)context.RequestData.ReadInt32(); + NetDbError errorCode = (NetDbError)context.RequestData.ReadInt32(); string errorString = errorCode switch { - NetDbError.Success => "Resolver Error 0 (no error)", + NetDbError.Success => "Resolver Error 0 (no error)", NetDbError.HostNotFound => "Unknown host", - NetDbError.TryAgain => "Host name lookup failure", - NetDbError.NoRecovery => "Unknown server error", - NetDbError.NoData => "No address associated with name", - _ => (errorCode <= NetDbError.Internal) ? "Resolver internal error" : "Unknown resolver error" + NetDbError.TryAgain => "Host name lookup failure", + NetDbError.NoRecovery => "Unknown server error", + NetDbError.NoData => "No address associated with name", + _ => (errorCode <= NetDbError.Internal) ? "Resolver internal error" : "Unknown resolver error", }; ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; if ((ulong)(errorString.Length + 1) <= bufferSize) { @@ -112,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode GetGaiStringErrorRequest(ServiceCtx context) { ResultCode resultCode = ResultCode.NotAllocated; - GaiError errorCode = (GaiError)context.RequestData.ReadInt32(); + GaiError errorCode = (GaiError)context.RequestData.ReadInt32(); if (errorCode > GaiError.Max) { @@ -122,25 +126,25 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres string errorString = errorCode switch { GaiError.AddressFamily => "Address family for hostname not supported", - GaiError.Again => "Temporary failure in name resolution", - GaiError.BadFlags => "Invalid value for ai_flags", - GaiError.Fail => "Non-recoverable failure in name resolution", - GaiError.Family => "ai_family not supported", - GaiError.Memory => "Memory allocation failure", - GaiError.NoData => "No address associated with hostname", - GaiError.NoName => "hostname nor servname provided, or not known", - GaiError.Service => "servname not supported for ai_socktype", - GaiError.SocketType => "ai_socktype not supported", - GaiError.System => "System error returned in errno", - GaiError.BadHints => "Invalid value for hints", - GaiError.Protocol => "Resolved protocol is unknown", - GaiError.Overflow => "Argument buffer overflow", - GaiError.Max => "Unknown error", - _ => "Success" + GaiError.Again => "Temporary failure in name resolution", + GaiError.BadFlags => "Invalid value for ai_flags", + GaiError.Fail => "Non-recoverable failure in name resolution", + GaiError.Family => "ai_family not supported", + GaiError.Memory => "Memory allocation failure", + GaiError.NoData => "No address associated with hostname", + GaiError.NoName => "hostname nor servname provided, or not known", + GaiError.Service => "servname not supported for ai_socktype", + GaiError.SocketType => "ai_socktype not supported", + GaiError.System => "System error returned in errno", + GaiError.BadHints => "Invalid value for hints", + GaiError.Protocol => "Resolved protocol is unknown", + GaiError.Overflow => "Argument buffer overflow", + GaiError.Max => "Unknown error", + _ => "Success", }; ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; if ((ulong)(errorString.Length + 1) <= bufferSize) { @@ -157,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode GetAddrInfoRequest(ServiceCtx context) { ulong responseBufferPosition = context.Request.ReceiveBuff[0].Position; - ulong responseBufferSize = context.Request.ReceiveBuff[0].Size; + ulong responseBufferSize = context.Request.ReceiveBuff[0].Size; return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, false, 0, 0); } @@ -166,8 +170,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetCancelHandleRequest(u64, pid) -> u32 public ResultCode GetCancelHandleRequest(ServiceCtx context) { - ulong pidPlaceHolder = context.RequestData.ReadUInt64(); - uint cancelHandleRequest = 0; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong pidPlaceHolder = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 + uint cancelHandleRequest = 0; context.ResponseData.Write(cancelHandleRequest); @@ -180,8 +186,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // CancelRequest(u32, u64, pid) public ResultCode CancelRequest(ServiceCtx context) { - uint cancelHandleRequest = context.RequestData.ReadUInt32(); - ulong pidPlaceHolder = context.RequestData.ReadUInt64(); + uint cancelHandleRequest = context.RequestData.ReadUInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong pidPlaceHolder = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest }); @@ -192,8 +200,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetHostByNameRequestWithOptions(u8, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>) public ResultCode GetHostByNameRequestWithOptions(ServiceCtx context) { - (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21(); - (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); + (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21(); + (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21(); return GetHostByNameRequestImpl( @@ -211,8 +219,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetHostByAddrRequestWithOptions(u32, u32, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>) public ResultCode GetHostByAddrRequestWithOptions(ServiceCtx context) { - (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21(); - (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); + (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21(); + (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21(); return GetHostByAddrRequestImpl( @@ -230,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetAddrInfoRequestWithOptions(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints, buffer<unknown, 21, 0>) -> (i32 ret, u32 bsd_errno, u32 unknown, u32 packed_addrinfo_size, buffer<packed_addrinfo, 22, 0> response) public ResultCode GetAddrInfoRequestWithOptions(ServiceCtx context) { - (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); + (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21(); return GetAddrInfoRequestImpl(context, outputBufferPosition, outputBufferSize, true, optionsBufferPosition, optionsBufferSize); @@ -241,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode ResolverSetOptionRequest(ServiceCtx context) { ulong bufferPosition = context.Request.SendBuff[0].Position; - ulong bufferSize = context.Request.SendBuff[0].Size; + ulong bufferSize = context.Request.SendBuff[0].Size; ulong unknown = context.RequestData.ReadUInt64(); @@ -254,7 +262,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown }); NetDbError netDbErrorCode = NetDbError.Success; - GaiError errno = GaiError.Success; + GaiError errno = GaiError.Success; context.ResponseData.Write((int)errno); context.ResponseData.Write((int)netDbErrorCode); @@ -294,9 +302,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres } // TODO: Use params. - bool enableNsdResolve = (context.RequestData.ReadInt32() & 1) != 0; - int timeOut = context.RequestData.ReadInt32(); - ulong pidPlaceholder = context.RequestData.ReadUInt64(); + bool enableNsdResolve = (context.RequestData.ReadInt32() & 1) != 0; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + int timeOut = context.RequestData.ReadInt32(); + ulong pidPlaceholder = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 if (withOptions) { @@ -306,8 +316,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres IPHostEntry hostEntry = null; NetDbError netDbErrorCode = NetDbError.Success; - GaiError errno = GaiError.Overflow; - int serializedSize = 0; + GaiError errno = GaiError.Overflow; + int serializedSize = 0; if (host.Length <= byte.MaxValue) { @@ -326,7 +336,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres Logger.Info?.Print(LogClass.ServiceSfdnsres, $"DNS Blocked: {host}"); netDbErrorCode = NetDbError.HostNotFound; - errno = GaiError.NoData; + errno = GaiError.NoData; } else { @@ -339,7 +349,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres catch (SocketException exception) { netDbErrorCode = ConvertSocketErrorCodeToNetDbError(exception.ErrorCode); - errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno); + errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno); } } } @@ -354,12 +364,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres if (!addresses.Any()) { - errno = GaiError.NoData; + errno = GaiError.NoData; netDbErrorCode = NetDbError.NoAddress; } else { - errno = GaiError.Success; + errno = GaiError.Success; serializedSize = SerializeHostEntries(context, outputBufferPosition, outputBufferSize, hostEntry, addresses); } } @@ -381,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres { if (!context.Device.Configuration.EnableInternetAccess) { - Logger.Info?.Print(LogClass.ServiceSfdnsres, $"Guest network access disabled, DNS Blocked."); + Logger.Info?.Print(LogClass.ServiceSfdnsres, "Guest network access disabled, DNS Blocked."); WriteResponse(context, withOptions, 0, GaiError.NoData, NetDbError.HostNotFound); @@ -393,10 +403,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres context.Memory.Read(inputBufferPosition, rawIp); // TODO: Use params. - uint socketLength = context.RequestData.ReadUInt32(); - uint type = context.RequestData.ReadUInt32(); - int timeOut = context.RequestData.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + uint socketLength = context.RequestData.ReadUInt32(); + uint type = context.RequestData.ReadUInt32(); + int timeOut = context.RequestData.ReadInt32(); ulong pidPlaceholder = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 if (withOptions) { @@ -406,21 +418,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres IPHostEntry hostEntry = null; NetDbError netDbErrorCode = NetDbError.Success; - GaiError errno = GaiError.AddressFamily; - int serializedSize = 0; + GaiError errno = GaiError.AddressFamily; + int serializedSize = 0; if (rawIp.Length == 4) { try { - IPAddress address = new IPAddress(rawIp); + IPAddress address = new(rawIp); hostEntry = Dns.GetHostEntry(address); } catch (SocketException exception) { netDbErrorCode = ConvertSocketErrorCodeToNetDbError(exception.ErrorCode); - errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno); + errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno); } } else @@ -430,7 +442,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres if (hostEntry != null) { - errno = GaiError.Success; + errno = GaiError.Success; serializedSize = SerializeHostEntries(context, outputBufferPosition, outputBufferSize, hostEntry, GetIpv4Addresses(hostEntry)); } @@ -442,7 +454,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres private static int SerializeHostEntries(ServiceCtx context, ulong outputBufferPosition, ulong outputBufferSize, IPHostEntry hostEntry, IEnumerable<IPAddress> addresses = null) { ulong originalBufferPosition = outputBufferPosition; - ulong bufferPosition = originalBufferPosition; + ulong bufferPosition = originalBufferPosition; string hostName = hostEntry.HostName + '\0'; @@ -494,9 +506,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres ulong optionsBufferSize) { bool enableNsdResolve = (context.RequestData.ReadInt32() & 1) != 0; - uint cancelHandle = context.RequestData.ReadUInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + uint cancelHandle = context.RequestData.ReadUInt32(); +#pragma warning restore IDE0059 - string host = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[0].Position, (long)context.Request.SendBuff[0].Size); + string host = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[0].Position, (long)context.Request.SendBuff[0].Size); string service = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[1].Position, (long)context.Request.SendBuff[1].Size); if (!context.Device.Configuration.EnableInternetAccess) @@ -509,21 +523,27 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres } // NOTE: We ignore hints for now. +#pragma warning disable IDE0059 // Remove unnecessary value assignment List<AddrInfoSerialized> hints = DeserializeAddrInfos(context.Memory, context.Request.SendBuff[2].Position, context.Request.SendBuff[2].Size); +#pragma warning restore IDE0059 if (withOptions) { // TODO: Find unknown, Parse and use options. +#pragma warning disable IDE0059 // Remove unnecessary value assignment uint unknown = context.RequestData.ReadUInt32(); +#pragma warning restore IDE0059 } +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong pidPlaceHolder = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 IPHostEntry hostEntry = null; NetDbError netDbErrorCode = NetDbError.Success; - GaiError errno = GaiError.AddressFamily; - int serializedSize = 0; + GaiError errno = GaiError.AddressFamily; + int serializedSize = 0; if (host.Length <= byte.MaxValue) { @@ -542,7 +562,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres Logger.Info?.Print(LogClass.ServiceSfdnsres, $"DNS Blocked: {host}"); netDbErrorCode = NetDbError.HostNotFound; - errno = GaiError.NoData; + errno = GaiError.NoData; } else { @@ -555,7 +575,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres catch (SocketException exception) { netDbErrorCode = ConvertSocketErrorCodeToNetDbError(exception.ErrorCode); - errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno); + errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno); } } } @@ -566,10 +586,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres if (hostEntry != null) { - int.TryParse(service, out int port); - - errno = GaiError.Success; - serializedSize = SerializeAddrInfos(context, responseBufferPosition, responseBufferSize, hostEntry, port); + if (int.TryParse(service, out int port)) + { + errno = GaiError.Success; + serializedSize = SerializeAddrInfos(context, responseBufferPosition, responseBufferSize, hostEntry, port); + } + else + { + errno = GaiError.Service; + } } WriteResponse(context, withOptions, serializedSize, errno, netDbErrorCode); @@ -601,37 +626,38 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres private static int SerializeAddrInfos(ServiceCtx context, ulong responseBufferPosition, ulong responseBufferSize, IPHostEntry hostEntry, int port) { ulong originalBufferPosition = responseBufferPosition; - ulong bufferPosition = originalBufferPosition; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong bufferPosition = originalBufferPosition; byte[] hostName = Encoding.ASCII.GetBytes(hostEntry.HostName + '\0'); +#pragma warning restore IDE0059 - using (WritableRegion region = context.Memory.GetWritableRegion(responseBufferPosition, (int)responseBufferSize)) + using WritableRegion region = context.Memory.GetWritableRegion(responseBufferPosition, (int)responseBufferSize); + + Span<byte> data = region.Memory.Span; + + for (int i = 0; i < hostEntry.AddressList.Length; i++) { - Span<byte> data = region.Memory.Span; + IPAddress ip = hostEntry.AddressList[i]; - for (int i = 0; i < hostEntry.AddressList.Length; i++) + if (ip.AddressFamily != AddressFamily.InterNetwork) { - IPAddress ip = hostEntry.AddressList[i]; - - if (ip.AddressFamily != AddressFamily.InterNetwork) - { - continue; - } - - // NOTE: 0 = Any - AddrInfoSerializedHeader header = new(ip, 0); - AddrInfo4 addr = new(ip, (short)port); - AddrInfoSerialized info = new(header, addr, null, hostEntry.HostName); - - data = info.Write(data); + continue; } - uint sentinel = 0; - MemoryMarshal.Write(data, ref sentinel); - data = data[sizeof(uint)..]; + // NOTE: 0 = Any + AddrInfoSerializedHeader header = new(ip, 0); + AddrInfo4 addr = new(ip, (short)port); + AddrInfoSerialized info = new(header, addr, null, hostEntry.HostName); - return region.Memory.Span.Length - data.Length; + data = info.Write(data); } + + uint sentinel = 0; + MemoryMarshal.Write(data, ref sentinel); + data = data[sizeof(uint)..]; + + return region.Memory.Span.Length - data.Length; } private static void WriteResponse( @@ -669,7 +695,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres 11002 => NetDbError.TryAgain, 11003 => NetDbError.NoRecovery, 11004 => NetDbError.NoData, - _ => NetDbError.Internal + _ => NetDbError.Internal, }; } @@ -679,8 +705,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres { 11001 => GaiError.NoData, 10060 => GaiError.Again, - _ => errno + _ => errno, }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs index 776a6f7cc..a2ccd190e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs @@ -19,18 +19,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy [GeneratedRegex(@"^accounts\.nintendo\.com$", RegexOpts)] private static partial Regex BlockedHost6(); - private static readonly Regex[] BlockedHosts = { + private static readonly Regex[] _blockedHosts = { BlockedHost1(), BlockedHost2(), BlockedHost3(), BlockedHost4(), BlockedHost5(), - BlockedHost6() + BlockedHost6(), }; public static bool IsHostBlocked(string host) { - foreach (Regex regex in BlockedHosts) + foreach (Regex regex in _blockedHosts) { if (regex.IsMatch(host)) { @@ -41,4 +41,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs index 0e18c5705..d17a999dc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsMitmResolver.cs @@ -19,8 +19,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy public void ReloadEntries(ServiceCtx context) { - string sdPath = context.Device.Configuration.VirtualFileSystem.GetSdCardPath(); - string filePath = context.Device.Configuration.VirtualFileSystem.GetFullPath(sdPath, HostsFilePath); + string sdPath = FileSystem.VirtualFileSystem.GetSdCardPath(); + string filePath = FileSystem.VirtualFileSystem.GetFullPath(sdPath, HostsFilePath); _mitmHostEntries.Clear(); @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy { AddressList = new[] { hostEntry.Value }, HostName = hostEntry.Key, - Aliases = Array.Empty<string>() + Aliases = Array.Empty<string>(), }; } } @@ -103,4 +103,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy return Dns.GetHostEntry(host); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs index 0a20e0572..68250022e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs @@ -10,17 +10,17 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] struct AddrInfo4 { - public byte Length; - public byte Family; - public short Port; + public byte Length; + public byte Family; + public short Port; public Array4<byte> Address; public Array8<byte> Padding; public AddrInfo4(IPAddress address, short port) { - Length = (byte)Unsafe.SizeOf<Array4<byte>>(); - Family = (byte)AddressFamily.InterNetwork; - Port = IPAddress.HostToNetworkOrder(port); + Length = (byte)Unsafe.SizeOf<Array4<byte>>(); + Family = (byte)AddressFamily.InterNetwork; + Port = IPAddress.HostToNetworkOrder(port); Address = new Array4<byte>(); address.TryWriteBytes(Address.AsSpan(), out _); @@ -48,4 +48,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs index 8e304dfa4..9c50b1b86 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs @@ -10,19 +10,19 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types struct AddrInfoSerializedHeader { public uint Magic; - public int Flags; - public int Family; - public int SocketType; - public int Protocol; + public int Flags; + public int Family; + public int SocketType; + public int Protocol; public uint AddressLength; public AddrInfoSerializedHeader(IPAddress address, SocketType socketType) { - Magic = SfdnsresContants.AddrInfoMagic; - Flags = 0; - Family = (int)address.AddressFamily; + Magic = SfdnsresContants.AddrInfoMagic; + Flags = 0; + Family = (int)address.AddressFamily; SocketType = (int)socketType; - Protocol = 0; + Protocol = 0; if (address.AddressFamily == AddressFamily.InterNetwork) { @@ -36,22 +36,22 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types public void ToNetworkOrder() { - Magic = (uint)IPAddress.HostToNetworkOrder((int)Magic); - Flags = IPAddress.HostToNetworkOrder(Flags); - Family = IPAddress.HostToNetworkOrder(Family); - SocketType = IPAddress.HostToNetworkOrder(SocketType); - Protocol = IPAddress.HostToNetworkOrder(Protocol); + Magic = (uint)IPAddress.HostToNetworkOrder((int)Magic); + Flags = IPAddress.HostToNetworkOrder(Flags); + Family = IPAddress.HostToNetworkOrder(Family); + SocketType = IPAddress.HostToNetworkOrder(SocketType); + Protocol = IPAddress.HostToNetworkOrder(Protocol); AddressLength = (uint)IPAddress.HostToNetworkOrder((int)AddressLength); } public void ToHostOrder() { - Magic = (uint)IPAddress.NetworkToHostOrder((int)Magic); - Flags = IPAddress.NetworkToHostOrder(Flags); - Family = IPAddress.NetworkToHostOrder(Family); - SocketType = IPAddress.NetworkToHostOrder(SocketType); - Protocol = IPAddress.NetworkToHostOrder(Protocol); + Magic = (uint)IPAddress.NetworkToHostOrder((int)Magic); + Flags = IPAddress.NetworkToHostOrder(Flags); + Family = IPAddress.NetworkToHostOrder(Family); + SocketType = IPAddress.NetworkToHostOrder(SocketType); + Protocol = IPAddress.NetworkToHostOrder(Protocol); AddressLength = (uint)IPAddress.NetworkToHostOrder((int)AddressLength); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs index f9f28b44c..46682b3c8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/GaiError.cs @@ -17,6 +17,6 @@ BadHints, Protocol, Overflow, - Max + Max, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs index 3c04c049a..0ded59603 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/NetDBError.cs @@ -8,6 +8,6 @@ TryAgain, NoRecovery, NoData, - NoAddress = NoData + NoAddress = NoData, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs index d194a3c66..7c51413cf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs @@ -1,7 +1,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types { - static class SfdnsresContants + class SfdnsresContants { public const uint AddrInfoMagic = 0xBEEFCAFE; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs b/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs index aa350b734..951ed5301 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs @@ -58,8 +58,10 @@ namespace Ryujinx.HLE.HOS.Services.Spl { configValue = default; - SystemVersion version = context.Device.System.ContentManager.GetCurrentFirmwareVersion(); - MemorySize memorySize = context.Device.Configuration.MemoryConfiguration.ToKernelMemorySize(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + SystemVersion version = context.Device.System.ContentManager.GetCurrentFirmwareVersion(); +#pragma warning restore IDE0059 + MemorySize memorySize = context.Device.Configuration.MemoryConfiguration.ToKernelMemorySize(); switch (configItem) { @@ -80,9 +82,9 @@ namespace Ryujinx.HLE.HOS.Services.Spl configValue = (ulong)DramId.IcosaSamsung4GiB; } break; - case ConfigItem.SecurityEngineInterruptNumber: + case ConfigItem.SecurityEngineInterruptNumber: return SmcResult.NotImplemented; - case ConfigItem.FuseVersion: + case ConfigItem.FuseVersion: return SmcResult.NotImplemented; case ConfigItem.HardwareType: configValue = (ulong)HardwareType.Icosa; @@ -93,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl case ConfigItem.IsRecoveryBoot: configValue = 0; break; - case ConfigItem.DeviceId: + case ConfigItem.DeviceId: return SmcResult.NotImplemented; case ConfigItem.BootReason: // This was removed in firmware 4.0.0. @@ -123,4 +125,4 @@ namespace Ryujinx.HLE.HOS.Services.Spl return SmcResult.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs index db2241631..922fd34a6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs @@ -5,9 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl [Service("csrng")] class IRandomInterface : DisposableIpcService { - private RandomNumberGenerator _rng; - - private readonly object _lock = new(); + private readonly RandomNumberGenerator _rng; public IRandomInterface(ServiceCtx context) { @@ -35,4 +33,4 @@ namespace Ryujinx.HLE.HOS.Services.Spl } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs index 4f61998a6..fe77c753a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/ResultCode.cs @@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Services.Spl { enum ResultCode { - ModuleId = 26, + ModuleId = 26, ErrorCodeShift = 9, Success = 0, - InvalidArguments = (101 << ErrorCodeShift) | ModuleId + InvalidArguments = (101 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs index f08bbeaa3..6822ad134 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/Types/ConfigItem.cs @@ -3,22 +3,22 @@ enum ConfigItem { // Standard config items. - DisableProgramVerification = 1, - DramId = 2, + DisableProgramVerification = 1, + DramId = 2, SecurityEngineInterruptNumber = 3, - FuseVersion = 4, - HardwareType = 5, - HardwareState = 6, - IsRecoveryBoot = 7, - DeviceId = 8, - BootReason = 9, - MemoryMode = 10, - IsDevelopmentFunctionEnabled = 11, - KernelConfiguration = 12, - IsChargerHiZModeEnabled = 13, - QuestState = 14, - RegulatorType = 15, - DeviceUniqueKeyGeneration = 16, - Package2Hash = 17 + FuseVersion = 4, + HardwareType = 5, + HardwareState = 6, + IsRecoveryBoot = 7, + DeviceId = 8, + BootReason = 9, + MemoryMode = 10, + IsDevelopmentFunctionEnabled = 11, + KernelConfiguration = 12, + IsChargerHiZModeEnabled = 13, + QuestState = 14, + RegulatorType = 15, + DeviceUniqueKeyGeneration = 16, + Package2Hash = 17, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs index 422c8d69f..05a465571 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/Types/DramId.cs @@ -30,6 +30,6 @@ IowaMicron1y4GiB, HoagMicron1y4GiB, AulaMicron1y4GiB, - AulaSamsung1y8GiBX + AulaSamsung1y8GiBX, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs index 414d0f118..8ab0ba9b0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareState.cs @@ -3,6 +3,6 @@ enum HardwareState { Development, - Production + Production, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs index 491eb943a..82d733308 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/Types/HardwareType.cs @@ -9,4 +9,4 @@ Calcio, Aula } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs b/src/Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs index d5f424a63..ade0378c5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs +++ b/src/Ryujinx.HLE/HOS/Services/Spl/Types/SmcResult.cs @@ -2,19 +2,18 @@ { enum SmcResult { - Success = 0, - NotImplemented = 1, - InvalidArgument = 2, - Busy = 3, - NoAsyncOperation = 4, + Success = 0, + NotImplemented = 1, + InvalidArgument = 2, + Busy = 3, + NoAsyncOperation = 4, InvalidAsyncOperation = 5, - NotPermitted = 6, - NotInitialized = 7, + NotPermitted = 6, + NotInitialized = 7, - PsciSuccess = 0, - PsciNotSupported = -1, + PsciNotSupported = -1, PsciInvalidParameters = -2, - PsciDenied = -3, - PsciAlreadyOn = -4 + PsciDenied = -3, + PsciAlreadyOn = -4, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs b/src/Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs index 167dea677..f5467983a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Srepo/ISrepoService.cs @@ -6,4 +6,4 @@ { public ISrepoService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs index dae0698c3..e31092fda 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { private const long CertStoreTitleId = 0x0100000000000800; - private readonly string CertStoreTitleMissingErrorMessage = "CertStore system title not found! SSL CA retrieving will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; + private const string CertStoreTitleMissingErrorMessage = "CertStore system title not found! SSL CA retrieving will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; private static BuiltInCertificateManager _instance; @@ -31,10 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { get { - if (_instance == null) - { - _instance = new BuiltInCertificateManager(); - } + _instance ??= new BuiltInCertificateManager(); return _instance; } @@ -52,12 +49,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { private const uint ValidMagic = 0x546C7373; -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public uint Magic; public uint EntriesCount; #pragma warning restore CS0649 - public bool IsValid() + public readonly bool IsValid() { return Magic == ValidMagic; } @@ -65,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl private struct CertStoreFileEntry { -#pragma warning disable CS0649 +#pragma warning disable CS0649 // Field is never assigned to public CaCertificateId Id; public TrustedCertStatus Status; public uint DataSize; @@ -109,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { Id = entry.Id, Status = entry.Status, - Data = data + Data = data, }; } @@ -125,9 +122,9 @@ namespace Ryujinx.HLE.HOS.Services.Ssl if (HasCertStoreTitle()) { - using LocalStorage ncaFile = new LocalStorage(_virtualFileSystem.SwitchPathToSystemPath(GetCertStoreTitleContentPath()), FileAccess.Read, FileMode.Open); + using LocalStorage ncaFile = new(VirtualFileSystem.SwitchPathToSystemPath(GetCertStoreTitleContentPath()), FileAccess.Read, FileMode.Open); - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile); + Nca nca = new(_virtualFileSystem.KeySet, ncaFile); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _fsIntegrityCheckLevel); diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs index 7741ef7ed..5e4a0c53b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs @@ -19,8 +19,10 @@ namespace Ryujinx.HLE.HOS.Services.Ssl // CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object<nn::ssl::sf::ISslContext> public ResultCode CreateContext(ServiceCtx context) { - SslVersion sslVersion = (SslVersion)context.RequestData.ReadUInt32(); - ulong pidPlaceholder = context.RequestData.ReadUInt64(); + SslVersion sslVersion = (SslVersion)context.RequestData.ReadUInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong pidPlaceholder = context.RequestData.ReadUInt64(); +#pragma warning restore IDE0059 MakeObject(context, new ISslContext(context.Request.HandleDesc.PId, sslVersion)); @@ -71,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl Id = entries[i].Id, Status = entries[i].Status, CertificateDataSize = (ulong)entries[i].Data.Length, - CertificateDataOffset = (ulong)(rawData.Length - certificatesData.Length) + CertificateDataOffset = (ulong)(rawData.Length - certificatesData.Length), }; certificatesData = certificatesData[entries[i].Data.Length..]; @@ -84,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl Id = CaCertificateId.All, Status = TrustedCertStatus.Invalid, CertificateDataSize = 0, - CertificateDataOffset = 0 + CertificateDataOffset = 0, }; } } @@ -122,4 +124,4 @@ namespace Ryujinx.HLE.HOS.Services.Ssl return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs index 862c79cdc..6aae11feb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/ResultCode.cs @@ -2,19 +2,19 @@ { public enum ResultCode { - OsModuleId = 123, + OsModuleId = 123, ErrorCodeShift = 9, - Success = 0, - NoSocket = (103 << ErrorCodeShift) | OsModuleId, - InvalidSocket = (106 << ErrorCodeShift) | OsModuleId, + Success = 0, + NoSocket = (103 << ErrorCodeShift) | OsModuleId, + InvalidSocket = (106 << ErrorCodeShift) | OsModuleId, InvalidCertBufSize = (112 << ErrorCodeShift) | OsModuleId, - InvalidOption = (126 << ErrorCodeShift) | OsModuleId, + InvalidOption = (126 << ErrorCodeShift) | OsModuleId, CertBufferTooSmall = (202 << ErrorCodeShift) | OsModuleId, - AlreadyInUse = (203 << ErrorCodeShift) | OsModuleId, - WouldBlock = (204 << ErrorCodeShift) | OsModuleId, - Timeout = (205 << ErrorCodeShift) | OsModuleId, - ConnectionReset = (209 << ErrorCodeShift) | OsModuleId, - ConnectionAbort = (210 << ErrorCodeShift) | OsModuleId + AlreadyInUse = (203 << ErrorCodeShift) | OsModuleId, + WouldBlock = (204 << ErrorCodeShift) | OsModuleId, + Timeout = (205 << ErrorCodeShift) | OsModuleId, + ConnectionReset = (209 << ErrorCodeShift) | OsModuleId, + ConnectionAbort = (210 << ErrorCodeShift) | OsModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs index b9087f409..b5c608d3d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService private bool _skipDefaultVerify; private bool _enableAlpn; - private SslVersion _sslVersion; + private readonly SslVersion _sslVersion; private IoMode _ioMode; private VerifyOption _verifyOption; private SessionCacheMode _sessionCacheMode; @@ -206,13 +206,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService { if (_getServerCertChain) { - using (WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size)) - { - result = _connection.GetServerCertificate(_hostName, region.Memory.Span, out uint bufferSize, out uint certificateCount); + using WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size); - context.ResponseData.Write(bufferSize); - context.ResponseData.Write(certificateCount); - } + result = _connection.GetServerCertificate(_hostName, region.Memory.Span, out uint bufferSize, out uint certificateCount); + + context.ResponseData.Write(bufferSize); + context.ResponseData.Write(certificateCount); } else { @@ -235,15 +234,13 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService ResultCode result; - using (WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size)) - { - // TODO: Better error management. - result = _connection.Read(out int readCount, region.Memory); + using WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size); + // TODO: Better error management. + result = _connection.Read(out int readCount, region.Memory); - if (result == ResultCode.Success) - { - context.ResponseData.Write(readCount); - } + if (result == ResultCode.Success) + { + context.ResponseData.Write(readCount); } return result; @@ -297,15 +294,15 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService ResultCode result; - using (WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size)) - { - // TODO: Better error management. - result = _connection.Peek(out int peekCount, region.Memory); + using WritableRegion region = context.Memory.GetWritableRegion(context.Request.ReceiveBuff[0].Position, (int)context.Request.ReceiveBuff[0].Size); - if (result == ResultCode.Success) - { - context.ResponseData.Write(peekCount); - } + + // TODO: Better error management. + result = _connection.Peek(out int peekCount, region.Memory); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(peekCount); } return result; @@ -381,7 +378,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService // SetOption(b8 value, nn::ssl::sf::OptionType option) public ResultCode SetOption(ServiceCtx context) { - bool value = context.RequestData.ReadUInt32() != 0; + bool value = context.RequestData.ReadUInt32() != 0; OptionType option = (OptionType)context.RequestData.ReadUInt32(); Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { option, value }); @@ -516,4 +513,4 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService _connection?.Dispose(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs index 18e03e49d..500e2b896 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnectionBase.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.HLE.HOS.Services.Ssl.SslService { - interface ISslConnectionBase: IDisposable + interface ISslConnectionBase : IDisposable { int SocketFd { get; } diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs index b38ff9214..7b371d299 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs @@ -47,8 +47,10 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService { CertificateFormat certificateFormat = (CertificateFormat)context.RequestData.ReadUInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong certificateDataPosition = context.Request.SendBuff[0].Position; - ulong certificateDataSize = context.Request.SendBuff[0].Size; + ulong certificateDataSize = context.Request.SendBuff[0].Size; +#pragma warning restore IDE0059 context.ResponseData.Write(_serverCertificateId++); @@ -61,11 +63,13 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService // ImportClientPki(buffer<bytes, 5> certificate, buffer<bytes, 5> ascii_password) -> u64 certificateId public ResultCode ImportClientPki(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment ulong certificateDataPosition = context.Request.SendBuff[0].Position; - ulong certificateDataSize = context.Request.SendBuff[0].Size; + ulong certificateDataSize = context.Request.SendBuff[0].Size; +#pragma warning restore IDE0059 ulong asciiPasswordDataPosition = context.Request.SendBuff[1].Position; - ulong asciiPasswordDataSize = context.Request.SendBuff[1].Size; + ulong asciiPasswordDataSize = context.Request.SendBuff[1].Size; byte[] asciiPasswordData = new byte[asciiPasswordDataSize]; @@ -80,4 +84,4 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs index 47d3eddbe..dab099aab 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs @@ -15,8 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService public ISocket Socket { get; } - private BsdContext _bsdContext; - private SslVersion _sslVersion; + private readonly BsdContext _bsdContext; + private readonly SslVersion _sslVersion; private SslStream _stream; private bool _isBlockingSocket; private int _previousReadTimeout; @@ -67,25 +67,19 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService EndSslOperation(); } -// NOTE: We silence warnings about TLS 1.0 and 1.1 as games will likely use it. + // NOTE: We silence warnings about TLS 1.0 and 1.1 as games will likely use it. #pragma warning disable SYSLIB0039 - private static SslProtocols TranslateSslVersion(SslVersion version) + private SslProtocols TranslateSslVersion(SslVersion version) { - switch (version & SslVersion.VersionMask) + return (version & SslVersion.VersionMask) switch { - case SslVersion.Auto: - return SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; - case SslVersion.TlsV10: - return SslProtocols.Tls; - case SslVersion.TlsV11: - return SslProtocols.Tls11; - case SslVersion.TlsV12: - return SslProtocols.Tls12; - case SslVersion.TlsV13: - return SslProtocols.Tls13; - default: - throw new NotImplementedException(version.ToString()); - } + SslVersion.Auto => SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, + SslVersion.TlsV10 => SslProtocols.Tls, + SslVersion.TlsV11 => SslProtocols.Tls11, + SslVersion.TlsV12 => SslProtocols.Tls12, + SslVersion.TlsV13 => SslProtocols.Tls13, + _ => throw new NotImplementedException(version.ToString()), + }; } #pragma warning restore SYSLIB0039 @@ -114,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService return 0; } - private static bool TryTranslateWinSockError(bool isBlocking, WsaError error, out ResultCode resultCode) + private bool TryTranslateWinSockError(bool isBlocking, WsaError error, out ResultCode resultCode) { switch (error) { diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs index 5c84579a7..a351ce545 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CaCertificateId.cs @@ -63,6 +63,6 @@ GTSRootR4, SecurityCommunicationRootCA, - All = uint.MaxValue + All = uint.MaxValue, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs index 1d80f739e..35842e85b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs @@ -3,6 +3,6 @@ enum CertificateFormat : uint { Pem = 1, - Der = 2 + Der = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs index 1cd06d6de..c8b3889c5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs @@ -2,7 +2,7 @@ { enum IoMode : uint { - Blocking = 1, - NonBlocking = 2 + Blocking = 1, + NonBlocking = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs index 3673200a4..85a813dca 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs @@ -5,6 +5,6 @@ DoNotCloseSocket, GetServerCertChain, // 3.0.0+ SkipDefaultVerify, // 5.0.0+ - EnableAlpn // 9.0.0+ + EnableAlpn, // 9.0.0+ } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs index cec7b745e..b9c739376 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs @@ -4,6 +4,6 @@ { None, SessionId, - SessionTicket + SessionTicket, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs index 7110fd852..7026596e5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs @@ -5,12 +5,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.Types [Flags] enum SslVersion : uint { - Auto = 1 << 0, + Auto = 1 << 0, TlsV10 = 1 << 3, TlsV11 = 1 << 4, TlsV12 = 1 << 5, TlsV13 = 1 << 6, // 11.0.0+ - VersionMask = 0xFFFFFF + VersionMask = 0xFFFFFF, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs index 7fd5efd62..e9d348c12 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/TrustedCertStatus.cs @@ -7,6 +7,6 @@ EnabledNotTrusted, Revoked, - Invalid = uint.MaxValue + Invalid = uint.MaxValue, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs index d25bb6c34..1c13e80f3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs @@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.Types [Flags] enum VerifyOption : uint { - PeerCa = 1 << 0, - HostName = 1 << 1, - DateCheck = 1 << 2, - EvCertPartial = 1 << 3, - EvPolicyOid = 1 << 4, // 6.0.0+ - EvCertFingerprint = 1 << 5 // 6.0.0+ + PeerCa = 1 << 0, + HostName = 1 << 1, + DateCheck = 1 << 2, + EvCertPartial = 1 << 3, + EvPolicyOid = 1 << 4, // 6.0.0+ + EvCertFingerprint = 1 << 5, // 6.0.0+ } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs index 3b33bf8bc..265962106 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItemConsumer.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { class BufferItemConsumer : ConsumerBase { - private GpuContext _gpuContext; + private readonly GpuContext _gpuContext; public BufferItemConsumer(Switch device, BufferQueueConsumer consumer, diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs index bc0901ab2..347eb8fba 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs @@ -4,7 +4,7 @@ { public static BufferQueueCore CreateBufferQueue(Switch device, ulong pid, out BufferQueueProducer producer, out BufferQueueConsumer consumer) { - BufferQueueCore core = new BufferQueueCore(device, pid); + BufferQueueCore core = new(device, pid); producer = new BufferQueueProducer(core, device.System.TickSource); consumer = new BufferQueueConsumer(core); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs index c9bb0a655..4cd45d691 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs @@ -54,10 +54,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (Core.StillTracking(ref bufferItem)) { - Core.Slots[bufferItem.Slot].AcquireCalled = true; + Core.Slots[bufferItem.Slot].AcquireCalled = true; Core.Slots[bufferItem.Slot].NeedsCleanupOnRelease = true; - Core.Slots[bufferItem.Slot].BufferState = BufferState.Acquired; - Core.Slots[bufferItem.Slot].Fence = AndroidFence.NoFence; + Core.Slots[bufferItem.Slot].BufferState = BufferState.Acquired; + Core.Slots[bufferItem.Slot].Fence = AndroidFence.NoFence; ulong targetFrameNumber = Core.Slots[bufferItem.Slot].FrameNumber; @@ -159,12 +159,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Core.Slots[slot].GraphicBuffer.Set(graphicBuffer); - Core.Slots[slot].BufferState = BufferState.Acquired; - Core.Slots[slot].AttachedByConsumer = true; + Core.Slots[slot].BufferState = BufferState.Acquired; + Core.Slots[slot].AttachedByConsumer = true; Core.Slots[slot].NeedsCleanupOnRelease = false; - Core.Slots[slot].Fence = AndroidFence.NoFence; - Core.Slots[slot].FrameNumber = 0; - Core.Slots[slot].AcquireCalled = false; + Core.Slots[slot].Fence = AndroidFence.NoFence; + Core.Slots[slot].FrameNumber = 0; + Core.Slots[slot].AcquireCalled = false; } return Status.Success; @@ -197,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (Core.Slots[slot].BufferState == BufferState.Acquired) { Core.Slots[slot].BufferState = BufferState.Free; - Core.Slots[slot].Fence = fence; + Core.Slots[slot].Fence = fence; listener = Core.ProducerListener; } @@ -237,7 +237,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return Status.NoInit; } - Core.ConsumerListener = consumerListener; + Core.ConsumerListener = consumerListener; Core.ConsumerControlledByApp = controlledByApp; } @@ -253,7 +253,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return Status.BadValue; } - Core.IsAbandoned = true; + Core.IsAbandoned = true; Core.ConsumerListener = null; Core.Queue.Clear(); @@ -304,7 +304,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger lock (Core.Lock) { - Core.DefaultWidth = (int)width; + Core.DefaultWidth = (int)width; Core.DefaultHeight = (int)height; } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs index 8cf55912e..f1d2f84b5 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs @@ -9,35 +9,35 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { class BufferQueueCore { - public BufferSlotArray Slots; - public int OverrideMaxBufferCount; - public bool UseAsyncBuffer; - public bool DequeueBufferCannotBlock; - public PixelFormat DefaultBufferFormat; - public int DefaultWidth; - public int DefaultHeight; - public int DefaultMaxBufferCount; - public int MaxAcquiredBufferCount; - public bool BufferHasBeenQueued; - public ulong FrameCounter; + public BufferSlotArray Slots; + public int OverrideMaxBufferCount; + public bool UseAsyncBuffer; + public bool DequeueBufferCannotBlock; + public PixelFormat DefaultBufferFormat; + public int DefaultWidth; + public int DefaultHeight; + public int DefaultMaxBufferCount; + public int MaxAcquiredBufferCount; + public bool BufferHasBeenQueued; + public ulong FrameCounter; public NativeWindowTransform TransformHint; - public bool IsAbandoned; - public NativeWindowApi ConnectedApi; - public bool IsAllocating; - public IProducerListener ProducerListener; - public IConsumerListener ConsumerListener; - public bool ConsumerControlledByApp; - public uint ConsumerUsageBits; - public List<BufferItem> Queue; - public BufferInfo[] BufferHistory; - public uint BufferHistoryPosition; - public bool EnableExternalEvent; - public int MaxBufferCountCached; + public bool IsAbandoned; + public NativeWindowApi ConnectedApi; + public bool IsAllocating; + public IProducerListener ProducerListener; + public IConsumerListener ConsumerListener; + public bool ConsumerControlledByApp; + public uint ConsumerUsageBits; + public List<BufferItem> Queue; + public BufferInfo[] BufferHistory; + public uint BufferHistoryPosition; + public bool EnableExternalEvent; + public int MaxBufferCountCached; public readonly object Lock = new(); - private KEvent _waitBufferFreeEvent; - private KEvent _frameAvailableEvent; + private readonly KEvent _waitBufferFreeEvent; + private readonly KEvent _frameAvailableEvent; public ulong Owner { get; } @@ -49,36 +49,36 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public BufferQueueCore(Switch device, ulong pid) { - Slots = new BufferSlotArray(); - IsAbandoned = false; - OverrideMaxBufferCount = 0; + Slots = new BufferSlotArray(); + IsAbandoned = false; + OverrideMaxBufferCount = 0; DequeueBufferCannotBlock = false; - UseAsyncBuffer = false; - DefaultWidth = 1; - DefaultHeight = 1; - DefaultMaxBufferCount = 2; - MaxAcquiredBufferCount = 1; - FrameCounter = 0; - TransformHint = 0; - DefaultBufferFormat = PixelFormat.Rgba8888; - IsAllocating = false; - ProducerListener = null; - ConsumerListener = null; - ConsumerUsageBits = 0; + UseAsyncBuffer = false; + DefaultWidth = 1; + DefaultHeight = 1; + DefaultMaxBufferCount = 2; + MaxAcquiredBufferCount = 1; + FrameCounter = 0; + TransformHint = 0; + DefaultBufferFormat = PixelFormat.Rgba8888; + IsAllocating = false; + ProducerListener = null; + ConsumerListener = null; + ConsumerUsageBits = 0; Queue = new List<BufferItem>(); // TODO: CreateGraphicBufferAlloc? - _waitBufferFreeEvent = new KEvent(device.System.KernelContext); + _waitBufferFreeEvent = new KEvent(device.System.KernelContext); _frameAvailableEvent = new KEvent(device.System.KernelContext); Owner = pid; Active = true; - BufferHistory = new BufferInfo[BufferHistoryArraySize]; - EnableExternalEvent = true; + BufferHistory = new BufferInfo[BufferHistoryArraySize]; + EnableExternalEvent = true; MaxBufferCountCached = 0; } @@ -220,9 +220,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Slots[slot].NeedsCleanupOnRelease = true; } - Slots[slot].BufferState = BufferState.Free; - Slots[slot].FrameNumber = uint.MaxValue; - Slots[slot].AcquireCalled = false; + Slots[slot].BufferState = BufferState.Free; + Slots[slot].FrameNumber = uint.MaxValue; + Slots[slot].AcquireCalled = false; Slots[slot].Fence.FenceCount = 0; } @@ -259,7 +259,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return; } - bool needBufferReleaseSignal = false; + bool needBufferReleaseSignal = false; bool needFrameAvailableSignal = false; if (maxBufferCount > 1) diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs index fa840f2ad..887d1861f 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs @@ -15,7 +15,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private readonly ITickSource _tickSource; +#pragma warning disable IDE0052 // Remove unread private member private uint _stickyTransform; +#pragma warning restore IDE0052 private uint _nextCallbackTicket; private uint _currentCallbackTicket; @@ -28,9 +30,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Core = core; _tickSource = tickSource; - _stickyTransform = 0; - _callbackTicket = 0; - _nextCallbackTicket = 0; + _stickyTransform = 0; + _callbackTicket = 0; + _nextCallbackTicket = 0; _currentCallbackTicket = 0; } @@ -134,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { if ((width == 0 && height != 0) || (height == 0 && width != 0)) { - slot = BufferSlotArray.InvalidBufferSlot; + slot = BufferSlotArray.InvalidBufferSlot; fence = AndroidFence.NoFence; return Status.BadValue; @@ -157,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (status != Status.Success) { - slot = BufferSlotArray.InvalidBufferSlot; + slot = BufferSlotArray.InvalidBufferSlot; fence = AndroidFence.NoFence; return status; @@ -176,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (width == 0 || height == 0) { - width = (uint)Core.DefaultWidth; + width = (uint)Core.DefaultWidth; height = (uint)Core.DefaultHeight; } @@ -190,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { if (!Core.Slots[slot].IsPreallocated) { - slot = BufferSlotArray.InvalidBufferSlot; + slot = BufferSlotArray.InvalidBufferSlot; fence = AndroidFence.NoFence; return Status.NoMemory; @@ -202,7 +204,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger $"available: Width = {graphicBuffer.Width} Height = {graphicBuffer.Height} Format = {graphicBuffer.Format} Usage = {graphicBuffer.Usage:x} " + $"requested: Width = {width} Height = {height} Format = {format} Usage = {usage:x}"); - slot = BufferSlotArray.InvalidBufferSlot; + slot = BufferSlotArray.InvalidBufferSlot; fence = AndroidFence.NoFence; return Status.NoInit; @@ -215,8 +217,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger fence = Core.Slots[slot].Fence; - Core.Slots[slot].Fence = AndroidFence.NoFence; - Core.Slots[slot].QueueTime = TimeSpanType.Zero; + Core.Slots[slot].Fence = AndroidFence.NoFence; + Core.Slots[slot].QueueTime = TimeSpanType.Zero; Core.Slots[slot].PresentationTime = TimeSpanType.Zero; Core.CheckSystemEventsLocked(Core.GetMaxBufferCountLocked(async)); @@ -267,7 +269,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (Core.IsAbandoned) { graphicBuffer = default; - fence = AndroidFence.NoFence; + fence = AndroidFence.NoFence; return Status.NoInit; } @@ -288,13 +290,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (nextBufferSlot == BufferSlotArray.InvalidBufferSlot) { graphicBuffer = default; - fence = AndroidFence.NoFence; + fence = AndroidFence.NoFence; return Status.NoMemory; } graphicBuffer = Core.Slots[nextBufferSlot].GraphicBuffer; - fence = Core.Slots[nextBufferSlot].Fence; + fence = Core.Slots[nextBufferSlot].Fence; Core.FreeBufferLocked(nextBufferSlot); @@ -326,8 +328,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Core.Slots[slot].GraphicBuffer.Set(graphicBuffer); - Core.Slots[slot].BufferState = BufferState.Dequeued; - Core.Slots[slot].Fence = AndroidFence.NoFence; + Core.Slots[slot].BufferState = BufferState.Dequeued; + Core.Slots[slot].Fence = AndroidFence.NoFence; Core.Slots[slot].RequestBufferCalled = true; return returnFlags; @@ -350,10 +352,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return Status.BadValue; } - BufferItem item = new BufferItem(); + BufferItem item = new(); IConsumerListener frameAvailableListener = null; - IConsumerListener frameReplaceListener = null; + IConsumerListener frameReplaceListener = null; lock (Core.Lock) { @@ -388,25 +390,25 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return Status.BadValue; } - Core.Slots[slot].Fence = input.Fence; + Core.Slots[slot].Fence = input.Fence; Core.Slots[slot].BufferState = BufferState.Queued; Core.FrameCounter++; - Core.Slots[slot].FrameNumber = Core.FrameCounter; - Core.Slots[slot].QueueTime = TimeSpanType.FromTimeSpan(_tickSource.ElapsedTime); + Core.Slots[slot].FrameNumber = Core.FrameCounter; + Core.Slots[slot].QueueTime = TimeSpanType.FromTimeSpan(_tickSource.ElapsedTime); Core.Slots[slot].PresentationTime = TimeSpanType.Zero; - item.AcquireCalled = Core.Slots[slot].AcquireCalled; - item.Crop = input.Crop; - item.Transform = input.Transform; + item.AcquireCalled = Core.Slots[slot].AcquireCalled; + item.Crop = input.Crop; + item.Transform = input.Transform; item.TransformToDisplayInverse = (input.Transform & NativeWindowTransform.InverseDisplay) == NativeWindowTransform.InverseDisplay; - item.ScalingMode = input.ScalingMode; - item.Timestamp = input.Timestamp; - item.IsAutoTimestamp = input.IsAutoTimestamp != 0; - item.SwapInterval = input.SwapInterval; - item.FrameNumber = Core.FrameCounter; - item.Slot = slot; - item.Fence = input.Fence; - item.IsDroppable = Core.DequeueBufferCannotBlock || input.Async != 0; + item.ScalingMode = input.ScalingMode; + item.Timestamp = input.Timestamp; + item.IsAutoTimestamp = input.IsAutoTimestamp != 0; + item.SwapInterval = input.SwapInterval; + item.FrameNumber = Core.FrameCounter; + item.Slot = slot; + item.Fence = input.Fence; + item.IsDroppable = Core.DequeueBufferCannotBlock || input.Async != 0; item.GraphicBuffer.Set(Core.Slots[slot].GraphicBuffer); item.GraphicBuffer.Object.IncrementNvMapHandleRefCount(Core.Owner); @@ -416,8 +418,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Core.BufferHistory[Core.BufferHistoryPosition] = new BufferInfo { FrameNumber = Core.FrameCounter, - QueueTime = Core.Slots[slot].QueueTime, - State = BufferState.Queued + QueueTime = Core.Slots[slot].QueueTime, + State = BufferState.Queued, }; _stickyTransform = input.StickyTransform; @@ -460,10 +462,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger output = new QueueBufferOutput { - Width = (uint)Core.DefaultWidth, - Height = (uint)Core.DefaultHeight, - TransformHint = Core.TransformHint, - NumPendingBuffers = (uint)Core.Queue.Count + Width = (uint)Core.DefaultWidth, + Height = (uint)Core.DefaultHeight, + TransformHint = Core.TransformHint, + NumPendingBuffers = (uint)Core.Queue.Count, }; if ((input.StickyTransform & 8) != 0) @@ -506,7 +508,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Core.Slots[slot].BufferState = BufferState.Free; Core.Slots[slot].FrameNumber = 0; - Core.Slots[slot].Fence = fence; + Core.Slots[slot].Fence = fence; Core.SignalDequeueEvent(); Core.SignalWaitBufferFreeEvent(); } @@ -568,7 +570,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return Status.BadValue; } - Core.BufferHasBeenQueued = false; + Core.BufferHasBeenQueued = false; Core.DequeueBufferCannotBlock = Core.ConsumerControlledByApp && producerControlledByApp; switch (api) @@ -578,11 +580,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger case NativeWindowApi.Media: case NativeWindowApi.Camera: Core.ProducerListener = listener; - Core.ConnectedApi = api; + Core.ConnectedApi = api; - output.Width = (uint)Core.DefaultWidth; - output.Height = (uint)Core.DefaultHeight; - output.TransformHint = Core.TransformHint; + output.Width = (uint)Core.DefaultWidth; + output.Height = (uint)Core.DefaultHeight; + output.TransformHint = Core.TransformHint; output.NumPendingBuffers = (uint)Core.Queue.Count; if (NxSettings.Settings.TryGetValue("nv!nvn_no_vsync_capability", out object noVSyncCapability) && (bool)noVSyncCapability) @@ -627,7 +629,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger producerListener = Core.ProducerListener; Core.ProducerListener = null; - Core.ConnectedApi = NativeWindowApi.NoApi; + Core.ConnectedApi = NativeWindowApi.NoApi; Core.SignalWaitBufferFreeEvent(); Core.SignalFrameAvailableEvent(); @@ -667,13 +669,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger lock (Core.Lock) { - Core.Slots[slot].BufferState = BufferState.Free; - Core.Slots[slot].Fence = AndroidFence.NoFence; - Core.Slots[slot].RequestBufferCalled = false; - Core.Slots[slot].AcquireCalled = false; + Core.Slots[slot].BufferState = BufferState.Free; + Core.Slots[slot].Fence = AndroidFence.NoFence; + Core.Slots[slot].RequestBufferCalled = false; + Core.Slots[slot].AcquireCalled = false; Core.Slots[slot].NeedsCleanupOnRelease = false; - Core.Slots[slot].IsPreallocated = !graphicBuffer.IsNull; - Core.Slots[slot].FrameNumber = 0; + Core.Slots[slot].IsPreallocated = !graphicBuffer.IsNull; + Core.Slots[slot].FrameNumber = 0; Core.Slots[slot].GraphicBuffer.Set(graphicBuffer); @@ -689,8 +691,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { // NOTE: Nintendo set the default width, height and format from the GraphicBuffer.. // This is entirely wrong and should only be controlled by the consumer... - Core.DefaultWidth = graphicBuffer.Object.Width; - Core.DefaultHeight = graphicBuffer.Object.Height; + Core.DefaultWidth = graphicBuffer.Object.Width; + Core.DefaultHeight = graphicBuffer.Object.Height; Core.DefaultBufferFormat = graphicBuffer.Object.Format; } else @@ -729,7 +731,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { bool tryAgain = true; - freeSlot = BufferSlotArray.InvalidBufferSlot; + freeSlot = BufferSlotArray.InvalidBufferSlot; returnStatus = Status.Success; while (tryAgain) diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs index fb84934a2..4632053df 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs @@ -6,24 +6,24 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger class BufferSlot { public AndroidStrongPointer<GraphicBuffer> GraphicBuffer; - public BufferState BufferState; - public bool RequestBufferCalled; - public ulong FrameNumber; - public AndroidFence Fence; - public bool AcquireCalled; - public bool NeedsCleanupOnRelease; - public bool AttachedByConsumer; - public TimeSpanType QueueTime; - public TimeSpanType PresentationTime; - public bool IsPreallocated; + public BufferState BufferState; + public bool RequestBufferCalled; + public ulong FrameNumber; + public AndroidFence Fence; + public bool AcquireCalled; + public bool NeedsCleanupOnRelease; + public bool AttachedByConsumer; + public TimeSpanType QueueTime; + public TimeSpanType PresentationTime; + public bool IsPreallocated; public BufferSlot() { - GraphicBuffer = new AndroidStrongPointer<GraphicBuffer>(); - BufferState = BufferState.Free; - QueueTime = TimeSpanType.Zero; + GraphicBuffer = new AndroidStrongPointer<GraphicBuffer>(); + BufferState = BufferState.Free; + QueueTime = TimeSpanType.Zero; PresentationTime = TimeSpanType.Zero; - IsPreallocated = false; + IsPreallocated = false; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs index d2404c587..5a6604f14 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlotArray.cs @@ -3,11 +3,11 @@ class BufferSlotArray { // TODO: move to BufferQueue - public const int NumBufferSlots = 0x40; + public const int NumBufferSlots = 0x40; public const int MaxAcquiredBuffers = NumBufferSlots - 2; - public const int InvalidBufferSlot = -1; + public const int InvalidBufferSlot = -1; - private BufferSlot[] _raw = new BufferSlot[NumBufferSlots]; + private readonly BufferSlot[] _raw = new BufferSlot[NumBufferSlots]; public BufferSlotArray() { diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs index 754fa7d73..d63019972 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs @@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public class Slot { public AndroidStrongPointer<GraphicBuffer> GraphicBuffer; - public AndroidFence Fence; - public ulong FrameNumber; + public AndroidFence Fence; + public ulong FrameNumber; public Slot() { @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger protected readonly object Lock = new(); - private IConsumerListener _listener; + private readonly IConsumerListener _listener; public ConsumerBase(BufferQueueConsumer consumer, bool controlledByApp, IConsumerListener listener) { @@ -35,8 +35,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } IsAbandoned = false; - Consumer = consumer; - _listener = listener; + Consumer = consumer; + _listener = listener; Status connectStatus = consumer.Connect(this, controlledByApp); @@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { Slots[slotIndex].GraphicBuffer.Reset(); - Slots[slotIndex].Fence = AndroidFence.NoFence; + Slots[slotIndex].Fence = AndroidFence.NoFence; Slots[slotIndex].FrameNumber = 0; } @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } Slots[bufferItem.Slot].FrameNumber = bufferItem.FrameNumber; - Slots[bufferItem.Slot].Fence = bufferItem.Fence; + Slots[bufferItem.Slot].Fence = bufferItem.Fence; return Status.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs index d6c98be1a..f89eaeeb6 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs @@ -7,11 +7,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { class HOSBinderDriverServer : IHOSBinderDriver { - private static Dictionary<int, IBinder> _registeredBinderObjects = new Dictionary<int, IBinder>(); + private static readonly Dictionary<int, IBinder> _registeredBinderObjects = new(); private static int _lastBinderId = 0; - private static object _lock = new object(); + private static readonly object _lock = new(); public static int RegisterBinderObject(IBinder binder) { diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs index 9003201ba..9690cd4a1 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IBinder.cs @@ -13,10 +13,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger ResultCode OnTransact(uint code, uint flags, ReadOnlySpan<byte> inputParcel, Span<byte> outputParcel) { - Parcel inputParcelReader = new Parcel(inputParcel.ToArray()); + Parcel inputParcelReader = new(inputParcel.ToArray()); // TODO: support objects? - Parcel outputParcelWriter = new Parcel((uint)(outputParcel.Length - Unsafe.SizeOf<ParcelHeader>()), 0); + Parcel outputParcelWriter = new((uint)(outputParcel.Length - Unsafe.SizeOf<ParcelHeader>()), 0); string inputInterfaceToken = inputParcelReader.ReadInterfaceToken(); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs index f0b393a02..a70f7fe39 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs @@ -29,33 +29,33 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger SetPreallocatedBuffer, Reserved15, GetBufferInfo, - GetBufferHistory + GetBufferHistory, } [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x54)] public struct QueueBufferInput : IFlattenable { - public long Timestamp; - public int IsAutoTimestamp; - public Rect Crop; + public long Timestamp; + public int IsAutoTimestamp; + public Rect Crop; public NativeWindowScalingMode ScalingMode; - public NativeWindowTransform Transform; - public uint StickyTransform; - public int Async; - public int SwapInterval; - public AndroidFence Fence; + public NativeWindowTransform Transform; + public uint StickyTransform; + public int Async; + public int SwapInterval; + public AndroidFence Fence; public void Flatten(Parcel parcel) { parcel.WriteUnmanagedType(ref this); } - public uint GetFdCount() + public readonly uint GetFdCount() { return 0; } - public uint GetFlattenedSize() + public readonly uint GetFlattenedSize() { return (uint)Unsafe.SizeOf<QueueBufferInput>(); } @@ -68,11 +68,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public struct QueueBufferOutput { - public uint Width; - public uint Height; + public uint Width; + public uint Height; public NativeWindowTransform TransformHint; - public uint NumPendingBuffers; - public ulong FrameNumber; + public uint NumPendingBuffers; + public ulong FrameNumber; public void WriteToParcel(Parcel parcel) { @@ -108,15 +108,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void OnTransact(uint code, uint flags, Parcel inputParcel, Parcel outputParcel) { - Status status = Status.Success; - int slot; - AndroidFence fence; - QueueBufferInput queueInput; + Status status = Status.Success; + int slot; + AndroidFence fence; + QueueBufferInput queueInput; QueueBufferOutput queueOutput; - NativeWindowApi api; + NativeWindowApi api; AndroidStrongPointer<GraphicBuffer> graphicBuffer; - AndroidStrongPointer<AndroidFence> strongFence; + AndroidStrongPointer<AndroidFence> strongFence; switch ((TransactionCode)code) { @@ -139,13 +139,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger break; case TransactionCode.DequeueBuffer: - bool async = inputParcel.ReadBoolean(); - uint width = inputParcel.ReadUInt32(); - uint height = inputParcel.ReadUInt32(); + bool async = inputParcel.ReadBoolean(); + uint width = inputParcel.ReadUInt32(); + uint height = inputParcel.ReadUInt32(); PixelFormat format = inputParcel.ReadUnmanagedType<PixelFormat>(); - uint usage = inputParcel.ReadUInt32(); + uint usage = inputParcel.ReadUInt32(); - status = DequeueBuffer(out int dequeueSlot, out fence, async, width, height, format, usage); + status = DequeueBuffer(out int dequeueSlot, out fence, async, width, height, format, usage); strongFence = new AndroidStrongPointer<AndroidFence>(fence); outputParcel.WriteInt32(dequeueSlot); @@ -163,7 +163,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger break; case TransactionCode.DetachNextBuffer: - status = DetachNextBuffer(out graphicBuffer, out fence); + status = DetachNextBuffer(out graphicBuffer, out fence); strongFence = new AndroidStrongPointer<AndroidFence>(fence); outputParcel.WriteStrongPointer(ref graphicBuffer); @@ -183,7 +183,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger break; case TransactionCode.QueueBuffer: - slot = inputParcel.ReadInt32(); + slot = inputParcel.ReadInt32(); queueInput = inputParcel.ReadFlattenable<QueueBufferInput>(); status = QueueBuffer(slot, ref queueInput, out queueOutput); @@ -194,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger break; case TransactionCode.CancelBuffer: - slot = inputParcel.ReadInt32(); + slot = inputParcel.ReadInt32(); fence = inputParcel.ReadFlattenable<AndroidFence>(); CancelBuffer(slot, ref fence); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index 2f9f42914..3d44e23dc 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -17,18 +17,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { int binderId = context.RequestData.ReadInt32(); - uint code = context.RequestData.ReadUInt32(); + uint code = context.RequestData.ReadUInt32(); uint flags = context.RequestData.ReadUInt32(); - ulong dataPos = context.Request.SendBuff[0].Position; + ulong dataPos = context.Request.SendBuff[0].Position; ulong dataSize = context.Request.SendBuff[0].Size; - ulong replyPos = context.Request.ReceiveBuff[0].Position; + ulong replyPos = context.Request.ReceiveBuff[0].Position; ulong replySize = context.Request.ReceiveBuff[0].Size; ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); - Span<byte> outputParcel = new Span<byte>(new byte[replySize]); + Span<byte> outputParcel = new(new byte[replySize]); ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel); @@ -45,8 +45,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public ResultCode AdjustRefcount(ServiceCtx context) { int binderId = context.RequestData.ReadInt32(); - int addVal = context.RequestData.ReadInt32(); - int type = context.RequestData.ReadInt32(); + int addVal = context.RequestData.ReadInt32(); + int type = context.RequestData.ReadInt32(); return AdjustRefcount(binderId, addVal, type); } @@ -77,27 +77,26 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { int binderId = context.RequestData.ReadInt32(); - uint code = context.RequestData.ReadUInt32(); + uint code = context.RequestData.ReadUInt32(); uint flags = context.RequestData.ReadUInt32(); - (ulong dataPos, ulong dataSize) = context.Request.GetBufferType0x21(); + (ulong dataPos, ulong dataSize) = context.Request.GetBufferType0x21(); (ulong replyPos, ulong replySize) = context.Request.GetBufferType0x22(); ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); - using (IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.RentCleared(replySize)) + using IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.RentCleared(replySize); + + Span<byte> outputParcel = outputParcelOwner.Memory.Span; + + ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel); + + if (result == ResultCode.Success) { - Span<byte> outputParcel = outputParcelOwner.Memory.Span; - - ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel); - - if (result == ResultCode.Success) - { - context.Memory.Write(replyPos, outputParcel); - } - - return result; + context.Memory.Write(replyPos, outputParcel); } + + return result; } protected abstract ResultCode AdjustRefcount(int binderId, int addVal, int type); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs index 5f014e13a..af32be3d0 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/LayerState.cs @@ -5,6 +5,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger NotInitialized, ManagedClosed, ManagedOpened, - Stray + Stray, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs index 794c48b82..b565f3f60 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowApi.cs @@ -2,10 +2,10 @@ { enum NativeWindowApi { - NoApi = 0, - NVN = 1, - CPU = 2, - Media = 3, - Camera = 4 + NoApi = 0, + NVN = 1, + CPU = 2, + Media = 3, + Camera = 4, } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs index c40b4fa10..075f81369 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowAttribute.cs @@ -2,12 +2,12 @@ { enum NativeWindowAttribute : uint { - Width = 0, - Height = 1, - Format = 2, - MinUnqueuedBuffers = 3, + Width = 0, + Height = 1, + Format = 2, + MinUnqueuedBuffers = 3, ConsumerRunningBehind = 9, - ConsumerUsageBits = 10, - MaxBufferCountAsync = 12 + ConsumerUsageBits = 10, + MaxBufferCountAsync = 12, } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs index 4194c9159..9b07e43bd 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowScalingMode.cs @@ -2,10 +2,10 @@ { enum NativeWindowScalingMode : uint { - Freeze = 0, + Freeze = 0, ScaleToWindow = 1, - ScaleCrop = 2, - Unknown = 3, - NoScaleCrop = 4, + ScaleCrop = 2, + Unknown = 3, + NoScaleCrop = 4, } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs index 66482b123..1a0c7d13b 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs @@ -5,14 +5,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger [Flags] enum NativeWindowTransform : uint { - None = 0, - FlipX = 1, - FlipY = 2, - Rotate90 = 4, - Rotate180 = FlipX | FlipY, - Rotate270 = Rotate90 | Rotate180, - InverseDisplay = 8, + None = 0, + FlipX = 1, + FlipY = 2, + Rotate90 = 4, + Rotate180 = FlipX | FlipY, + Rotate270 = Rotate90 | Rotate180, + InverseDisplay = 8, NoVSyncCapability = 0x10, - ReturnFrameNumber = 0x20 + ReturnFrameNumber = 0x20, } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs index 19b221571..431c2cadc 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { private readonly byte[] _rawData; - private Span<byte> Raw => new Span<byte>(_rawData); + private Span<byte> Raw => new(_rawData); private ref ParcelHeader Header => ref MemoryMarshal.Cast<byte, ParcelHeader>(_rawData)[0]; @@ -26,10 +26,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public Parcel(byte[] rawData) { - _rawData = rawData; + _rawData = rawData; _payloadPosition = 0; - _objectPosition = 0; + _objectPosition = 0; } public Parcel(uint payloadSize, uint objectsSize) @@ -38,16 +38,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger _rawData = new byte[BitUtils.AlignUp<uint>(headerSize + payloadSize + objectsSize, 4)]; - Header.PayloadSize = payloadSize; - Header.ObjectsSize = objectsSize; + Header.PayloadSize = payloadSize; + Header.ObjectsSize = objectsSize; Header.PayloadOffset = headerSize; - Header.ObjectOffset = Header.PayloadOffset + Header.ObjectsSize; + Header.ObjectOffset = Header.PayloadOffset + Header.ObjectsSize; } public string ReadInterfaceToken() { // Ignore the policy flags +#pragma warning disable IDE0059 // Remove unnecessary value assignment int strictPolicy = ReadInt32(); +#pragma warning restore IDE0059 return ReadString16(); } @@ -64,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger ReadOnlySpan<byte> data = ReadInPlace((size + 1) * 2); // Return the unicode string without the last character (null terminator) - return Encoding.Unicode.GetString(data.Slice(0, size * 2)); + return Encoding.Unicode.GetString(data[..(size * 2)]); } public int ReadInt32() => ReadUnmanagedType<int>(); @@ -77,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { long flattenableSize = ReadInt64(); - T result = new T(); + T result = new(); Debug.Assert(flattenableSize == result.GetFlattenedSize()); @@ -86,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return result; } - public T ReadUnmanagedType<T>() where T: unmanaged + public T ReadUnmanagedType<T>() where T : unmanaged { ReadOnlySpan<byte> data = ReadInPlace(Unsafe.SizeOf<T>()); @@ -105,8 +107,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger [StructLayout(LayoutKind.Sequential, Size = 0x28)] private struct FlatBinderObject { - public int Type; - public int Flags; + public int Type; + public int Flags; public long BinderId; public long Cookie; @@ -115,12 +117,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public Span<byte> ServiceName => MemoryMarshal.CreateSpan(ref _serviceNameStart, 0x8); } - public void WriteObject<T>(T obj, string serviceName) where T: IBinder + public void WriteObject<T>(T obj, string serviceName) where T : IBinder { - FlatBinderObject flatBinderObject = new FlatBinderObject + FlatBinderObject flatBinderObject = new() { - Type = 2, - Flags = 0, + Type = 2, + Flags = 0, BinderId = HOSBinderDriverServer.GetBinderId(obj), }; @@ -149,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } } - public void WriteStrongPointer<T>(ref AndroidStrongPointer<T> value) where T: unmanaged, IFlattenable + public void WriteStrongPointer<T>(ref AndroidStrongPointer<T> value) where T : unmanaged, IFlattenable { WriteBoolean(!value.IsNull); @@ -205,17 +207,17 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { uint headerSize = (uint)Unsafe.SizeOf<ParcelHeader>(); - Header.PayloadSize = (uint)_payloadPosition; - Header.ObjectsSize = (uint)_objectPosition; + Header.PayloadSize = (uint)_payloadPosition; + Header.ObjectsSize = (uint)_objectPosition; Header.PayloadOffset = headerSize; - Header.ObjectOffset = Header.PayloadOffset + Header.PayloadSize; + Header.ObjectOffset = Header.PayloadOffset + Header.PayloadSize; } public ReadOnlySpan<byte> Finish() { UpdateHeader(); - return Raw.Slice(0, (int)(Header.PayloadSize + Header.ObjectsSize + Unsafe.SizeOf<ParcelHeader>())); + return Raw[..(int)(Header.PayloadSize + Header.ObjectsSize + Unsafe.SizeOf<ParcelHeader>())]; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs index 925811fa2..d328aee9b 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Status.cs @@ -1,22 +1,25 @@ -namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger +using System.Diagnostics.CodeAnalysis; + +namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum Status { - Success = 0, - WouldBlock = -11, - NoMemory = -12, - Busy = -16, - NoInit = -19, - BadValue = -22, + Success = 0, + WouldBlock = -11, + NoMemory = -12, + Busy = -16, + NoInit = -19, + BadValue = -22, InvalidOperation = -37, // Producer flags BufferNeedsReallocation = 1, - ReleaseAllBuffers = 2, + ReleaseAllBuffers = 2, // Consumer errors - StaleBufferSlot = 1, + StaleBufferSlot = 1, NoBufferAvailaible = 2, - PresentLater = 3, + PresentLater = 3, } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 0c1cea517..d3d9dc030 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -11,49 +11,47 @@ using System.Threading; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { - using ResultCode = Ryujinx.HLE.HOS.Services.Vi.ResultCode; - class SurfaceFlinger : IConsumerListener, IDisposable { private const int TargetFps = 60; - private Switch _device; + private readonly Switch _device; - private Dictionary<long, Layer> _layers; + private readonly Dictionary<long, Layer> _layers; private bool _isRunning; - private Thread _composerThread; + private readonly Thread _composerThread; - private Stopwatch _chrono; + private readonly Stopwatch _chrono; - private ManualResetEvent _event = new ManualResetEvent(false); - private AutoResetEvent _nextFrameEvent = new AutoResetEvent(true); + private readonly ManualResetEvent _event = new(false); + private readonly AutoResetEvent _nextFrameEvent = new(true); private long _ticks; private long _ticksPerFrame; - private long _spinTicks; - private long _1msTicks; + private readonly long _spinTicks; + private readonly long _1msTicks; private int _swapInterval; private int _swapIntervalDelay; - private readonly object Lock = new(); + private readonly object _lock = new(); public long RenderLayerId { get; private set; } private class Layer { - public int ProducerBinderId; + public int ProducerBinderId; public IGraphicBufferProducer Producer; - public BufferItemConsumer Consumer; - public BufferQueueCore Core; - public ulong Owner; - public LayerState State; + public BufferItemConsumer Consumer; + public BufferQueueCore Core; + public ulong Owner; + public LayerState State; } private class TextureCallbackInformation { - public Layer Layer; + public Layer Layer; public BufferItem Item; } @@ -65,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger _composerThread = new Thread(HandleComposition) { - Name = "SurfaceFlinger.Composer" + Name = "SurfaceFlinger.Composer", }; _chrono = new Stopwatch(); @@ -100,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { layerId = 1; - lock (Lock) + lock (_lock) { foreach (KeyValuePair<long, Layer> pair in _layers) { @@ -118,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private void CreateLayerFromId(ulong pid, long layerId, LayerState initialState) { - lock (Lock) + lock (_lock) { Logger.Info?.Print(LogClass.SurfaceFlinger, $"Creating layer {layerId}"); @@ -132,16 +130,16 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger _layers.Add(layerId, new Layer { ProducerBinderId = HOSBinderDriverServer.RegisterBinderObject(producer), - Producer = producer, - Consumer = new BufferItemConsumer(_device, consumer, 0, -1, false, this), - Core = core, - Owner = pid, - State = initialState + Producer = producer, + Consumer = new BufferItemConsumer(_device, consumer, 0, -1, false, this), + Core = core, + Owner = pid, + State = initialState, }); } } - public ResultCode OpenLayer(ulong pid, long layerId, out IBinder producer) + public Vi.ResultCode OpenLayer(ulong pid, long layerId, out IBinder producer) { Layer layer = GetLayerByIdLocked(layerId); @@ -149,18 +147,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { producer = null; - return ResultCode.InvalidArguments; + return Vi.ResultCode.InvalidArguments; } layer.State = LayerState.ManagedOpened; producer = layer.Producer; - return ResultCode.Success; + return Vi.ResultCode.Success; } - public ResultCode CloseLayer(long layerId) + public Vi.ResultCode CloseLayer(long layerId) { - lock (Lock) + lock (_lock) { Layer layer = GetLayerByIdLocked(layerId); @@ -168,18 +166,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to close layer {layerId}"); - return ResultCode.InvalidValue; + return Vi.ResultCode.InvalidValue; } CloseLayer(layerId, layer); - return ResultCode.Success; + return Vi.ResultCode.Success; } } - public ResultCode DestroyManagedLayer(long layerId) + public Vi.ResultCode DestroyManagedLayer(long layerId) { - lock (Lock) + lock (_lock) { Layer layer = GetLayerByIdLocked(layerId); @@ -187,14 +185,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy managed layer {layerId} (not found)"); - return ResultCode.InvalidValue; + return Vi.ResultCode.InvalidValue; } if (layer.State != LayerState.ManagedClosed && layer.State != LayerState.ManagedOpened) { Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy managed layer {layerId} (permission denied)"); - return ResultCode.PermissionDenied; + return Vi.ResultCode.PermissionDenied; } HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId); @@ -204,13 +202,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger CloseLayer(layerId, layer); } - return ResultCode.Success; + return Vi.ResultCode.Success; } } - public ResultCode DestroyStrayLayer(long layerId) + public Vi.ResultCode DestroyStrayLayer(long layerId) { - lock (Lock) + lock (_lock) { Layer layer = GetLayerByIdLocked(layerId); @@ -218,14 +216,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy stray layer {layerId} (not found)"); - return ResultCode.InvalidValue; + return Vi.ResultCode.InvalidValue; } if (layer.State != LayerState.Stray) { Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy stray layer {layerId} (permission denied)"); - return ResultCode.PermissionDenied; + return Vi.ResultCode.PermissionDenied; } HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId); @@ -235,7 +233,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger CloseLayer(layerId, layer); } - return ResultCode.Success; + return Vi.ResultCode.Success; } } @@ -263,7 +261,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void SetRenderLayer(long layerId) { - lock (Lock) + lock (_lock) { RenderLayerId = layerId; } @@ -284,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public IGraphicBufferProducer GetProducerByLayerId(long layerId) { - lock (Lock) + lock (_lock) { Layer layer = GetLayerByIdLocked(layerId); @@ -365,7 +363,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void Compose() { - lock (Lock) + lock (_lock) { // TODO: support multilayers (& multidisplay ?) if (RenderLayerId == 0) @@ -403,7 +401,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private void PostFrameBuffer(Layer layer, BufferItem item) { - int frameBufferWidth = item.GraphicBuffer.Object.Width; + int frameBufferWidth = item.GraphicBuffer.Object.Width; int frameBufferHeight = item.GraphicBuffer.Object.Height; int nvMapHandle = item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle; @@ -434,9 +432,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY); AspectRatio aspectRatio = _device.Configuration.AspectRatio; - bool isStretched = aspectRatio == AspectRatio.Stretched; + bool isStretched = aspectRatio == AspectRatio.Stretched; - ImageCrop crop = new ImageCrop( + ImageCrop crop = new( cropRect.Left, cropRect.Right, cropRect.Top, @@ -447,10 +445,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger aspectRatio.ToFloatX(), aspectRatio.ToFloatY()); - TextureCallbackInformation textureCallbackInformation = new TextureCallbackInformation + TextureCallbackInformation textureCallbackInformation = new() { Layer = layer, - Item = item + Item = item, }; if (_device.Gpu.Window.EnqueueFrameThreadSafe( @@ -543,6 +541,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger _device.Statistics.RecordGameFrameTime(); } - public void OnBuffersReleased() {} + public void OnBuffersReleased() { } } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs index 5b72e257f..448074cd7 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs @@ -16,17 +16,17 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private byte _fenceStorageStart; - private Span<byte> _storage => MemoryMarshal.CreateSpan(ref _fenceStorageStart, Unsafe.SizeOf<NvFence>() * 4); + private Span<byte> Storage => MemoryMarshal.CreateSpan(ref _fenceStorageStart, Unsafe.SizeOf<NvFence>() * 4); - public Span<NvFence> NvFences => MemoryMarshal.Cast<byte, NvFence>(_storage); + public Span<NvFence> NvFences => MemoryMarshal.Cast<byte, NvFence>(Storage); public static AndroidFence NoFence { get { - AndroidFence fence = new AndroidFence + AndroidFence fence = new() { - FenceCount = 0 + FenceCount = 0, }; fence.NvFences[0].Id = NvFence.InvalidSyncPointId; @@ -81,12 +81,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } } - public uint GetFlattenedSize() + public readonly uint GetFlattenedSize() { return (uint)Unsafe.SizeOf<AndroidFence>(); } - public uint GetFdCount() + public readonly uint GetFdCount() { return 0; } @@ -101,4 +101,4 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger this = parcel.ReadUnmanagedType<AndroidFence>(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs index c356671bc..963a1fe42 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidStrongPointer.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types { - class AndroidStrongPointer<T> where T: unmanaged, IFlattenable + class AndroidStrongPointer<T> where T : unmanaged, IFlattenable { public T Object; @@ -20,13 +20,13 @@ public void Set(AndroidStrongPointer<T> other) { - Object = other.Object; + Object = other.Object; _hasObject = other._hasObject; } public void Set(T obj) { - Object = obj; + Object = obj; _hasObject = true; } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs index 12c41b0d4..faade87f8 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types [StructLayout(LayoutKind.Sequential, Size = 0x1C, Pack = 1)] struct BufferInfo { - public ulong FrameNumber; + public ulong FrameNumber; public TimeSpanType QueueTime; public TimeSpanType PresentationTime; - public BufferState State; + public BufferState State; } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs index 19fc79008..cbb5ce760 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs @@ -6,33 +6,33 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger class BufferItem : ICloneable { public AndroidStrongPointer<GraphicBuffer> GraphicBuffer; - public AndroidFence Fence; - public Rect Crop; - public NativeWindowTransform Transform; - public NativeWindowScalingMode ScalingMode; - public long Timestamp; - public bool IsAutoTimestamp; - public int SwapInterval; - public ulong FrameNumber; - public int Slot; - public bool IsDroppable; - public bool AcquireCalled; - public bool TransformToDisplayInverse; + public AndroidFence Fence; + public Rect Crop; + public NativeWindowTransform Transform; + public NativeWindowScalingMode ScalingMode; + public long Timestamp; + public bool IsAutoTimestamp; + public int SwapInterval; + public ulong FrameNumber; + public int Slot; + public bool IsDroppable; + public bool AcquireCalled; + public bool TransformToDisplayInverse; public BufferItem() { - GraphicBuffer = new AndroidStrongPointer<GraphicBuffer>(); - Transform = NativeWindowTransform.None; - ScalingMode = NativeWindowScalingMode.Freeze; - Timestamp = 0; - IsAutoTimestamp = false; - FrameNumber = 0; - Slot = BufferSlotArray.InvalidBufferSlot; - IsDroppable = false; - AcquireCalled = false; + GraphicBuffer = new AndroidStrongPointer<GraphicBuffer>(); + Transform = NativeWindowTransform.None; + ScalingMode = NativeWindowScalingMode.Freeze; + Timestamp = 0; + IsAutoTimestamp = false; + FrameNumber = 0; + Slot = BufferSlotArray.InvalidBufferSlot; + IsDroppable = false; + AcquireCalled = false; TransformToDisplayInverse = false; - SwapInterval = 1; - Fence = AndroidFence.NoFence; + SwapInterval = 1; + Fence = AndroidFence.NoFence; Crop = new Rect(); Crop.MakeInvalid(); @@ -40,19 +40,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public object Clone() { - BufferItem item = new BufferItem(); - - item.Transform = Transform; - item.ScalingMode = ScalingMode; - item.IsAutoTimestamp = IsAutoTimestamp; - item.FrameNumber = FrameNumber; - item.Slot = Slot; - item.IsDroppable = IsDroppable; - item.AcquireCalled = AcquireCalled; - item.TransformToDisplayInverse = TransformToDisplayInverse; - item.SwapInterval = SwapInterval; - item.Fence = Fence; - item.Crop = Crop; + BufferItem item = new() + { + Transform = Transform, + ScalingMode = ScalingMode, + IsAutoTimestamp = IsAutoTimestamp, + FrameNumber = FrameNumber, + Slot = Slot, + IsDroppable = IsDroppable, + AcquireCalled = AcquireCalled, + TransformToDisplayInverse = TransformToDisplayInverse, + SwapInterval = SwapInterval, + Fence = Fence, + Crop = Crop, + }; item.GraphicBuffer.Set(GraphicBuffer); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs index 1787f5a62..d735a294e 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferState.cs @@ -2,9 +2,9 @@ { internal enum BufferState { - Free = 0, + Free = 0, Dequeued = 1, - Queued = 2, - Acquired = 3 + Queued = 2, + Acquired = 3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs index b47d35b47..ec119d687 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorBytePerPixel.cs @@ -2,16 +2,16 @@ { enum ColorBytePerPixel { - Bpp1 = 1, - Bpp2 = 2, - Bpp4 = 4, - Bpp8 = 8, - Bpp16 = 16, - Bpp24 = 24, - Bpp32 = 32, - Bpp48 = 48, - Bpp64 = 64, - Bpp96 = 96, - Bpp128 = 128 + Bpp1 = 1, + Bpp2 = 2, + Bpp4 = 4, + Bpp8 = 8, + Bpp16 = 16, + Bpp24 = 24, + Bpp32 = 32, + Bpp48 = 48, + Bpp64 = 64, + Bpp96 = 96, + Bpp128 = 128, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs index e9669f129..e3da29319 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorComponent.cs @@ -2,6 +2,7 @@ { enum ColorComponent : uint { +#pragma warning disable IDE0055 // Disable formatting X1 = (0x01 << ColorShift.Component) | ColorBytePerPixel.Bpp1, X2 = (0x02 << ColorShift.Component) | ColorBytePerPixel.Bpp2, X4 = (0x03 << ColorShift.Component) | ColorBytePerPixel.Bpp4, @@ -38,5 +39,6 @@ Y12X12 = (0x24 << ColorShift.Component) | ColorBytePerPixel.Bpp32, X20Y20Z20 = (0x26 << ColorShift.Component) | ColorBytePerPixel.Bpp64, X16Y16Z16W16 = (0x27 << ColorShift.Component) | ColorBytePerPixel.Bpp64, +#pragma warning restore IDE0055 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs index cfa3b018c..2daa73b80 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorDataType.cs @@ -3,7 +3,7 @@ enum ColorDataType { Integer = 0x0 << ColorShift.DataType, - Float = 0x1 << ColorShift.DataType, - Stencil = 0x2 << ColorShift.DataType + Float = 0x1 << ColorShift.DataType, + Stencil = 0x2 << ColorShift.DataType, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs index 227d648ac..e13dbd86a 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorFormat.cs @@ -1,7 +1,11 @@ -namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger +using System.Diagnostics.CodeAnalysis; + +namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum ColorFormat : ulong { +#pragma warning disable IDE0055 // Disable formatting NonColor8 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, NonColor16 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, NonColor24 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X24 | ColorDataType.Integer, @@ -231,5 +235,6 @@ X4Bayer12GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, X6Bayer10GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, XYZ = ColorSpace.XYZ | ColorSwizzle.XYZ1 | ColorComponent.X20Y20Z20 | ColorDataType.Float, +#pragma warning restore IDE0055 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs index 3ad216a8e..e19ed3eff 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorShift.cs @@ -2,9 +2,9 @@ { class ColorShift { - public const int Swizzle = 16; - public const int DataType = 14; - public const int Space = 32; + public const int Swizzle = 16; + public const int DataType = 14; + public const int Space = 32; public const int Component = 8; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs index 9003a00ba..5021e356e 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSpace.cs @@ -2,32 +2,32 @@ { enum ColorSpace : ulong { - NonColor = 0x0L << ColorShift.Space, - LinearRGBA = 0x1L << ColorShift.Space, - SRGB = 0x2L << ColorShift.Space, + NonColor = 0x0L << ColorShift.Space, + LinearRGBA = 0x1L << ColorShift.Space, + SRGB = 0x2L << ColorShift.Space, - RGB709 = 0x3L << ColorShift.Space, - LinearRGB709 = 0x4L << ColorShift.Space, + RGB709 = 0x3L << ColorShift.Space, + LinearRGB709 = 0x4L << ColorShift.Space, - LinearScRGB = 0x5L << ColorShift.Space, + LinearScRGB = 0x5L << ColorShift.Space, - RGB2020 = 0x6L << ColorShift.Space, + RGB2020 = 0x6L << ColorShift.Space, LinearRGB2020 = 0x7L << ColorShift.Space, - RGB2020_PQ = 0x8L << ColorShift.Space, + RGB2020_PQ = 0x8L << ColorShift.Space, - ColorIndex = 0x9L << ColorShift.Space, + ColorIndex = 0x9L << ColorShift.Space, - YCbCr601 = 0xAL << ColorShift.Space, - YCbCr601_RR = 0xBL << ColorShift.Space, - YCbCr601_ER = 0xCL << ColorShift.Space, - YCbCr709 = 0xDL << ColorShift.Space, - YCbCr709_ER = 0xEL << ColorShift.Space, + YCbCr601 = 0xAL << ColorShift.Space, + YCbCr601_RR = 0xBL << ColorShift.Space, + YCbCr601_ER = 0xCL << ColorShift.Space, + YCbCr709 = 0xDL << ColorShift.Space, + YCbCr709_ER = 0xEL << ColorShift.Space, - BayerRGGB = 0x10L << ColorShift.Space, - BayerBGGR = 0x11L << ColorShift.Space, - BayerGRBG = 0x12L << ColorShift.Space, - BayerGBRG = 0x13L << ColorShift.Space, + BayerRGGB = 0x10L << ColorShift.Space, + BayerBGGR = 0x11L << ColorShift.Space, + BayerGRBG = 0x12L << ColorShift.Space, + BayerGBRG = 0x13L << ColorShift.Space, - XYZ = 0x14L << ColorShift.Space, + XYZ = 0x14L << ColorShift.Space, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs index 4c1370c71..0473d56b3 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Color/ColorSwizzle.cs @@ -2,6 +2,7 @@ { enum ColorSwizzle { +#pragma warning disable IDE0055 // Disable formatting XYZW = 0x688 << ColorShift.Swizzle, ZYXW = 0x60a << ColorShift.Swizzle, WZYX = 0x053 << ColorShift.Swizzle, @@ -26,6 +27,7 @@ _000X = 0x124 << ColorShift.Swizzle, _0XY0 = 0x844 << ColorShift.Swizzle, XXXY = 0x200 << ColorShift.Swizzle, - YYYX = 0x049 << ColorShift.Swizzle + YYYX = 0x049 << ColorShift.Swizzle, +#pragma warning restore IDE0055 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs index d1143225e..f7bd3b241 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs @@ -9,14 +9,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger struct GraphicBuffer : IFlattenable { public GraphicBufferHeader Header; - public NvGraphicBuffer Buffer; + public NvGraphicBuffer Buffer; - public int Width => Header.Width; - public int Height => Header.Height; - public PixelFormat Format => Header.Format; - public int Usage => Header.Usage; + public readonly int Width => Header.Width; + public readonly int Height => Header.Height; + public readonly PixelFormat Format => Header.Format; + public readonly int Usage => Header.Usage; - public Rect ToRect() + public readonly Rect ToRect() { return new Rect(Width, Height); } @@ -41,34 +41,34 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Buffer = parcel.ReadUnmanagedType<NvGraphicBuffer>(); } - public void IncrementNvMapHandleRefCount(ulong pid) + public readonly void IncrementNvMapHandleRefCount(ulong pid) { NvMapDeviceFile.IncrementMapRefCount(pid, Buffer.NvMapId); - for (int i = 0; i < Buffer.Surfaces.Length; i++) + for (int i = 0; i < NvGraphicBufferSurfaceArray.Length; i++) { NvMapDeviceFile.IncrementMapRefCount(pid, Buffer.Surfaces[i].NvMapHandle); } } - public void DecrementNvMapHandleRefCount(ulong pid) + public readonly void DecrementNvMapHandleRefCount(ulong pid) { NvMapDeviceFile.DecrementMapRefCount(pid, Buffer.NvMapId); - for (int i = 0; i < Buffer.Surfaces.Length; i++) + for (int i = 0; i < NvGraphicBufferSurfaceArray.Length; i++) { NvMapDeviceFile.DecrementMapRefCount(pid, Buffer.Surfaces[i].NvMapHandle); } } - public uint GetFlattenedSize() + public readonly uint GetFlattenedSize() { return (uint)Unsafe.SizeOf<GraphicBuffer>(); } - public uint GetFdCount() + public readonly uint GetFdCount() { return 0; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs index 77495922c..c6413ec13 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBufferHeader.cs @@ -5,12 +5,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger [StructLayout(LayoutKind.Sequential, Size = 0x28, Pack = 1)] struct GraphicBufferHeader { - public int Magic; - public int Width; - public int Height; - public int Stride; + public int Magic; + public int Width; + public int Height; + public int Stride; public PixelFormat Format; - public int Usage; + public int Usage; public int Pid; public int RefCount; @@ -18,4 +18,4 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public int FdsCount; public int IntsCount; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs index 6bb47dccf..cbae4a902 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs @@ -38,4 +38,4 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger [FieldOffset(0x34)] public NvGraphicBufferSurfaceArray Surfaces; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs index e084bc73e..e0570c70a 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurface.cs @@ -41,4 +41,4 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger [FieldOffset(0x38)] public long Size; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs index 51ac98f82..8bc30c1f1 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBufferSurfaceArray.cs @@ -15,27 +15,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger [FieldOffset(0xb0)] private NvGraphicBufferSurface Surface2; - public NvGraphicBufferSurface this[int index] + public readonly NvGraphicBufferSurface this[int index] { get { - if (index == 0) + return index switch { - return Surface0; - } - else if (index == 1) - { - return Surface1; - } - else if (index == 2) - { - return Surface2; - } - - throw new IndexOutOfRangeException(); + 0 => Surface0, + 1 => Surface1, + 2 => Surface2, + _ => throw new IndexOutOfRangeException(), + }; } } - public int Length => 3; + public static int Length => 3; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs index a5dec9694..a5f7efa51 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Rect.cs @@ -11,18 +11,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public int Right; public int Bottom; - public int Width => Right - Left; - public int Height => Bottom - Top; + public readonly int Width => Right - Left; + public readonly int Height => Bottom - Top; public Rect(int width, int height) { - Left = 0; - Top = 0; - Right = width; + Left = 0; + Top = 0; + Right = width; Bottom = height; } - public bool IsEmpty() + public readonly bool IsEmpty() { return Width <= 0 || Height <= 0; } @@ -31,10 +31,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { result = new Rect { - Left = Math.Max(Left, other.Left), - Top = Math.Max(Top, other.Top), - Right = Math.Min(Right, other.Right), - Bottom = Math.Min(Bottom, other.Bottom) + Left = Math.Max(Left, other.Left), + Top = Math.Max(Top, other.Top), + Right = Math.Min(Right, other.Right), + Bottom = Math.Min(Bottom, other.Bottom), }; return !result.IsEmpty(); @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void MakeInvalid() { - Right = -1; + Right = -1; Bottom = -1; } @@ -56,16 +56,16 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return !x.Equals(y); } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { return obj is Rect rect && Equals(rect); } - public bool Equals(Rect cmpObj) + public readonly bool Equals(Rect cmpObj) { return Left == cmpObj.Left && Top == cmpObj.Top && Right == cmpObj.Right && Bottom == cmpObj.Bottom; } - public override int GetHashCode() => HashCode.Combine(Left, Top, Right, Bottom); + public readonly override int GetHashCode() => HashCode.Combine(Left, Top, Right, Bottom); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs index fb7ebdc5b..49da1245f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs @@ -2,7 +2,7 @@ { class LocalSystemClockContextWriter : SystemClockContextUpdateCallback { - private TimeSharedMemory _sharedMemory; + private readonly TimeSharedMemory _sharedMemory; public LocalSystemClockContextWriter(TimeSharedMemory sharedMemory) { @@ -11,7 +11,7 @@ protected override ResultCode Update() { - _sharedMemory.UpdateLocalSystemClockContext(_context); + _sharedMemory.UpdateLocalSystemClockContext(Context); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs index 36468ec14..ef315ea4d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs @@ -2,7 +2,7 @@ { class NetworkSystemClockContextWriter : SystemClockContextUpdateCallback { - private TimeSharedMemory _sharedMemory; + private readonly TimeSharedMemory _sharedMemory; public NetworkSystemClockContextWriter(TimeSharedMemory sharedMemory) { @@ -11,7 +11,7 @@ protected override ResultCode Update() { - _sharedMemory.UpdateNetworkSystemClockContext(_context); + _sharedMemory.UpdateNetworkSystemClockContext(Context); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs index 20c334e86..458ec4057 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs @@ -2,6 +2,6 @@ { class StandardLocalSystemClockCore : SystemClockCore { - public StandardLocalSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore) {} + public StandardLocalSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore) { } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs index aec034850..200ab6306 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public bool IsStandardNetworkSystemClockAccuracySufficient(ITickSource tickSource) { - SteadyClockCore steadyClockCore = GetSteadyClockCore(); + SteadyClockCore steadyClockCore = GetSteadyClockCore(); SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(tickSource); bool isStandardNetworkClockSufficientAccuracy = false; diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs index 8392c4b50..3ae699ae0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs @@ -11,18 +11,18 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public StandardSteadyClockCore() { - _setupValue = TimeSpanType.Zero; - _testOffset = TimeSpanType.Zero; - _internalOffset = TimeSpanType.Zero; + _setupValue = TimeSpanType.Zero; + _testOffset = TimeSpanType.Zero; + _internalOffset = TimeSpanType.Zero; _cachedRawTimePoint = TimeSpanType.Zero; } public override SteadyClockTimePoint GetTimePoint(ITickSource tickSource) { - SteadyClockTimePoint result = new SteadyClockTimePoint + SteadyClockTimePoint result = new() { - TimePoint = GetCurrentRawTimePoint(tickSource).ToSeconds(), - ClockSourceId = GetClockSourceId() + TimePoint = GetCurrentRawTimePoint(tickSource).ToSeconds(), + ClockSourceId = GetClockSourceId(), }; return result; @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); - TimeSpanType rawTimePoint = new TimeSpanType(_setupValue.NanoSeconds + ticksTimeSpan.NanoSeconds); + TimeSpanType rawTimePoint = new(_setupValue.NanoSeconds + ticksTimeSpan.NanoSeconds); if (rawTimePoint.NanoSeconds < _cachedRawTimePoint.NanoSeconds) { diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs index fa485437b..e50ee7da6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs @@ -6,19 +6,19 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { class StandardUserSystemClockCore : SystemClockCore { - private StandardLocalSystemClockCore _localSystemClockCore; - private StandardNetworkSystemClockCore _networkSystemClockCore; - private bool _autoCorrectionEnabled; - private SteadyClockTimePoint _autoCorrectionTime; - private KEvent _autoCorrectionEvent; + private readonly StandardLocalSystemClockCore _localSystemClockCore; + private readonly StandardNetworkSystemClockCore _networkSystemClockCore; + private bool _autoCorrectionEnabled; + private SteadyClockTimePoint _autoCorrectionTime; + private KEvent _autoCorrectionEvent; public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore) : base(localSystemClockCore.GetSteadyClockCore()) { - _localSystemClockCore = localSystemClockCore; + _localSystemClockCore = localSystemClockCore; _networkSystemClockCore = networkSystemClockCore; - _autoCorrectionEnabled = false; - _autoCorrectionTime = SteadyClockTimePoint.GetRandom(); - _autoCorrectionEvent = null; + _autoCorrectionEnabled = false; + _autoCorrectionTime = SteadyClockTimePoint.GetRandom(); + _autoCorrectionEvent = null; } protected override ResultCode Flush(SystemClockContext context) diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs index 18da4ed3e..2d12a2a06 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs @@ -7,14 +7,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock abstract class SteadyClockCore { private UInt128 _clockSourceId; - private bool _isRtcResetDetected; - private bool _isInitialized; + private bool _isRtcResetDetected; + private bool _isInitialized; public SteadyClockCore() { - _clockSourceId = UInt128Utils.CreateRandom(); + _clockSourceId = UInt128Utils.CreateRandom(); _isRtcResetDetected = false; - _isInitialized = false; + _isInitialized = false; } public UInt128 GetClockSourceId() @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return new TimeSpanType(0); } - public virtual void SetTestOffset(TimeSpanType testOffset) {} + public virtual void SetTestOffset(TimeSpanType testOffset) { } public ResultCode GetRtcValue(out ulong rtcValue) { @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return new TimeSpanType(0); } - public virtual void SetInternalOffset(TimeSpanType internalOffset) {} + public virtual void SetInternalOffset(TimeSpanType internalOffset) { } public virtual SteadyClockTimePoint GetTimePoint(ITickSource tickSource) { diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs index 6229f5edd..3f191820f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs @@ -6,22 +6,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { abstract class SystemClockContextUpdateCallback { - private List<KWritableEvent> _operationEventList; - protected SystemClockContext _context; - private bool _hasContext; + private readonly List<KWritableEvent> _operationEventList; + protected SystemClockContext Context; + private bool _hasContext; public SystemClockContextUpdateCallback() { _operationEventList = new List<KWritableEvent>(); - _context = new SystemClockContext(); - _hasContext = false; + Context = new SystemClockContext(); + _hasContext = false; } private bool NeedUpdate(SystemClockContext context) { if (_hasContext) { - return _context.Offset != context.Offset || _context.SteadyTimePoint.ClockSourceId != context.SteadyTimePoint.ClockSourceId; + return Context.Offset != context.Offset || Context.SteadyTimePoint.ClockSourceId != context.SteadyTimePoint.ClockSourceId; } return true; @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock if (NeedUpdate(context)) { - _context = context; + Context = context; _hasContext = true; result = Update(); diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs index f4bbaa604..f8a995b71 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs @@ -5,19 +5,19 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { abstract class SystemClockCore { - private SteadyClockCore _steadyClockCore; - private SystemClockContext _context; - private bool _isInitialized; + private readonly SteadyClockCore _steadyClockCore; + private SystemClockContext _context; + private bool _isInitialized; private SystemClockContextUpdateCallback _systemClockContextUpdateCallback; public SystemClockCore(SteadyClockCore steadyClockCore) { _steadyClockCore = steadyClockCore; - _context = new SystemClockContext(); - _isInitialized = false; + _context = new SystemClockContext(); + _isInitialized = false; _context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId(); - _systemClockContextUpdateCallback = null; + _systemClockContextUpdateCallback = null; } public virtual SteadyClockCore GetSteadyClockCore() @@ -52,10 +52,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { SteadyClockTimePoint currentTimePoint = _steadyClockCore.GetCurrentTimePoint(tickSource); - SystemClockContext clockContext = new SystemClockContext() + SystemClockContext clockContext = new() { - Offset = posixTime - currentTimePoint.TimePoint, - SteadyTimePoint = currentTimePoint + Offset = posixTime - currentTimePoint.TimePoint, + SteadyTimePoint = currentTimePoint, }; ResultCode result = SetClockContext(clockContext); @@ -99,10 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public void RegisterOperationEvent(KWritableEvent writableEvent) { - if (_systemClockContextUpdateCallback != null) - { - _systemClockContextUpdateCallback.RegisterOperationEvent(writableEvent); - } + _systemClockContextUpdateCallback?.RegisterOperationEvent(writableEvent); } public ResultCode SetSystemClockContext(SystemClockContext context) diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs index fe74da7e9..99e981ecd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs @@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { class TickBasedSteadyClockCore : SteadyClockCore { - public TickBasedSteadyClockCore() {} + public TickBasedSteadyClockCore() { } public override SteadyClockTimePoint GetTimePoint(ITickSource tickSource) { - SteadyClockTimePoint result = new SteadyClockTimePoint + SteadyClockTimePoint result = new() { - TimePoint = 0, - ClockSourceId = GetClockSourceId() + TimePoint = 0, + ClockSourceId = GetClockSourceId(), }; TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs index 07c1b405f..d0a74a935 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs @@ -7,23 +7,23 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock [StructLayout(LayoutKind.Sequential, Size = 0xD0)] struct ClockSnapshot { - public SystemClockContext UserContext; - public SystemClockContext NetworkContext; - public long UserTime; - public long NetworkTime; - public CalendarTime UserCalendarTime; - public CalendarTime NetworkCalendarTime; + public SystemClockContext UserContext; + public SystemClockContext NetworkContext; + public long UserTime; + public long NetworkTime; + public CalendarTime UserCalendarTime; + public CalendarTime NetworkCalendarTime; public CalendarAdditionalInfo UserCalendarAdditionalTime; public CalendarAdditionalInfo NetworkCalendarAdditionalTime; - public SteadyClockTimePoint SteadyClockTimePoint; + public SteadyClockTimePoint SteadyClockTimePoint; private LocationNameStorageHolder _locationName; public Span<byte> LocationName => MemoryMarshal.Cast<LocationNameStorageHolder, byte>(MemoryMarshal.CreateSpan(ref _locationName, LocationNameStorageHolder.Size)); [MarshalAs(UnmanagedType.I1)] - public bool IsAutomaticCorrectionEnabled; - public byte Type; + public bool IsAutomaticCorrectionEnabled; + public byte Type; public ushort Unknown; @@ -47,4 +47,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return ResultCode.TimeMismatch; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs index 729e11b6b..2bc99ff81 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs @@ -7,10 +7,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SteadyClockTimePoint { - public long TimePoint; + public long TimePoint; public UInt128 ClockSourceId; - public ResultCode GetSpanBetween(SteadyClockTimePoint other, out long outSpan) + public readonly ResultCode GetSpanBetween(SteadyClockTimePoint other, out long outSpan) { outSpan = 0; @@ -35,9 +35,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { return new SteadyClockTimePoint { - TimePoint = 0, - ClockSourceId = UInt128Utils.CreateRandom() + TimePoint = 0, + ClockSourceId = UInt128Utils.CreateRandom(), }; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs index 6b589c28a..fc47e775f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SystemClockContext.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SystemClockContext { - public long Offset; + public long Offset; public SteadyClockTimePoint SteadyTimePoint; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs index 0070193f2..a7ef81150 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { private const long NanoSecondsPerSecond = 1000000000; - public static readonly TimeSpanType Zero = new TimeSpanType(0); + public static readonly TimeSpanType Zero = new(0); public long NanoSeconds; @@ -17,17 +17,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock NanoSeconds = nanoSeconds; } - public long ToSeconds() + public readonly long ToSeconds() { return NanoSeconds / NanoSecondsPerSecond; } - public TimeSpanType AddSeconds(long seconds) + public readonly TimeSpanType AddSeconds(long seconds) { return new TimeSpanType(NanoSeconds + (seconds * NanoSecondsPerSecond)); } - public bool IsDaylightSavingTime() + public readonly bool IsDaylightSavingTime() { return DateTime.UnixEpoch.AddSeconds(ToSeconds()).ToLocalTime().IsDaylightSavingTime(); } @@ -47,4 +47,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return FromSeconds((long)ticks / (long)frequency); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs b/src/Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs index 092fa8ce5..46b58408a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/IAlarmService.cs @@ -5,4 +5,4 @@ { public IAlarmService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs b/src/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs index 8ec55c159..64b7e69c6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs @@ -5,4 +5,4 @@ { public IPowerStateRequestHandler(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs b/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs index 31548b80c..32aed90cb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs @@ -12,13 +12,13 @@ namespace Ryujinx.HLE.HOS.Services.Time [Service("time:u", TimePermissions.User)] class IStaticServiceForGlue : IpcService { - private IStaticServiceForPsc _inner; - private TimePermissions _permissions; + private readonly IStaticServiceForPsc _inner; + private readonly TimePermissions _permissions; public IStaticServiceForGlue(ServiceCtx context, TimePermissions permissions) : base(context.Device.System.TimeServer) { _permissions = permissions; - _inner = new IStaticServiceForPsc(context, permissions); + _inner = new IStaticServiceForPsc(context, permissions); _inner.TrySetServer(Server); _inner.SetParent(this); } @@ -83,7 +83,9 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.PermissionDenied; } +#pragma warning disable IDE0059 // Remove unnecessary value assignment TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>(); +#pragma warning restore IDE0059 // TODO: set:sys SetExternalSteadyClockInternalOffset(internalOffset.ToSeconds()) diff --git a/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs b/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs index 145d4e3b9..a6b33e4ae 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs @@ -18,12 +18,12 @@ namespace Ryujinx.HLE.HOS.Services.Time [Service("time:su", TimePermissions.SystemUpdate)] class IStaticServiceForPsc : IpcService { - private TimeManager _timeManager; - private TimePermissions _permissions; + private readonly TimeManager _timeManager; + private readonly TimePermissions _permissions; private int _timeSharedMemoryNativeHandle = 0; - public IStaticServiceForPsc(ServiceCtx context, TimePermissions permissions) : this(TimeManager.Instance, permissions) {} + public IStaticServiceForPsc(ServiceCtx context, TimePermissions permissions) : this(TimeManager.Instance, permissions) { } public IStaticServiceForPsc(TimeManager manager, TimePermissions permissions) { @@ -149,8 +149,8 @@ namespace Ryujinx.HLE.HOS.Services.Time // SetStandardUserSystemClockAutomaticCorrectionEnabled(b8) public ResultCode SetStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) { - SteadyClockCore steadyClock = _timeManager.StandardSteadyClock; - StandardUserSystemClockCore userClock = _timeManager.StandardUserSystemClock; + SteadyClockCore steadyClock = _timeManager.StandardSteadyClock; + StandardUserSystemClockCore userClock = _timeManager.StandardUserSystemClock; if (!userClock.IsInitialized() || !steadyClock.IsInitialized()) { @@ -229,7 +229,7 @@ namespace Ryujinx.HLE.HOS.Services.Time ITickSource tickSource = context.Device.System.TickSource; - SystemClockContext otherContext = context.RequestData.ReadStruct<SystemClockContext>(); + SystemClockContext otherContext = context.RequestData.ReadStruct<SystemClockContext>(); SteadyClockTimePoint currentTimePoint = steadyClock.GetCurrentTimePoint(tickSource); ResultCode result = ResultCode.TimeMismatch; @@ -237,7 +237,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (currentTimePoint.ClockSourceId == otherContext.SteadyTimePoint.ClockSourceId) { TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); - long baseTimePoint = otherContext.Offset + currentTimePoint.TimePoint - ticksTimeSpan.ToSeconds(); + long baseTimePoint = otherContext.Offset + currentTimePoint.TimePoint - ticksTimeSpan.ToSeconds(); context.ResponseData.Write(baseTimePoint); @@ -287,7 +287,7 @@ namespace Ryujinx.HLE.HOS.Services.Time context.RequestData.BaseStream.Position += 7; - SystemClockContext userContext = context.RequestData.ReadStruct<SystemClockContext>(); + SystemClockContext userContext = context.RequestData.ReadStruct<SystemClockContext>(); SystemClockContext networkContext = context.RequestData.ReadStruct<SystemClockContext>(); ITickSource tickSource = context.Device.System.TickSource; @@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { ClockSnapshot clockSnapshotA = ReadClockSnapshotFromBuffer(context, context.Request.PtrBuff[0]); ClockSnapshot clockSnapshotB = ReadClockSnapshotFromBuffer(context, context.Request.PtrBuff[1]); - TimeSpanType difference = TimeSpanType.FromSeconds(clockSnapshotB.UserContext.Offset - clockSnapshotA.UserContext.Offset); + TimeSpanType difference = TimeSpanType.FromSeconds(clockSnapshotB.UserContext.Offset - clockSnapshotA.UserContext.Offset); if (clockSnapshotB.UserContext.SteadyTimePoint.ClockSourceId != clockSnapshotA.UserContext.SteadyTimePoint.ClockSourceId || (clockSnapshotB.IsAutomaticCorrectionEnabled && clockSnapshotA.IsAutomaticCorrectionEnabled)) { @@ -337,7 +337,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (clockSnapshotA.NetworkTime != 0 && clockSnapshotB.NetworkTime != 0) { - result = TimeSpanType.FromSeconds(clockSnapshotB.NetworkTime - clockSnapshotA.NetworkTime); + result = TimeSpanType.FromSeconds(clockSnapshotB.NetworkTime - clockSnapshotA.NetworkTime); resultCode = ResultCode.Success; } else @@ -359,13 +359,13 @@ namespace Ryujinx.HLE.HOS.Services.Time { clockSnapshot = new ClockSnapshot(); - SteadyClockCore steadyClockCore = _timeManager.StandardSteadyClock; + SteadyClockCore steadyClockCore = _timeManager.StandardSteadyClock; SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(tickSource); clockSnapshot.IsAutomaticCorrectionEnabled = _timeManager.StandardUserSystemClock.IsAutomaticCorrectionEnabled(); - clockSnapshot.UserContext = userContext; - clockSnapshot.NetworkContext = networkContext; - clockSnapshot.SteadyClockTimePoint = currentTimePoint; + clockSnapshot.UserContext = userContext; + clockSnapshot.NetworkContext = networkContext; + clockSnapshot.SteadyClockTimePoint = currentTimePoint; ResultCode result = _timeManager.TimeZone.Manager.GetDeviceLocationName(out string deviceLocationName); @@ -386,7 +386,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (result == ResultCode.Success) { - clockSnapshot.UserCalendarTime = userCalendarInfo.Time; + clockSnapshot.UserCalendarTime = userCalendarInfo.Time; clockSnapshot.UserCalendarAdditionalTime = userCalendarInfo.AdditionalInfo; if (ClockSnapshot.GetCurrentTime(out clockSnapshot.NetworkTime, currentTimePoint, clockSnapshot.NetworkContext) != ResultCode.Success) @@ -398,9 +398,9 @@ namespace Ryujinx.HLE.HOS.Services.Time if (result == ResultCode.Success) { - clockSnapshot.NetworkCalendarTime = networkCalendarInfo.Time; + clockSnapshot.NetworkCalendarTime = networkCalendarInfo.Time; clockSnapshot.NetworkCalendarAdditionalTime = networkCalendarInfo.AdditionalInfo; - clockSnapshot.Type = type; + clockSnapshot.Type = type; // Probably a version field? clockSnapshot.Unknown = 0; @@ -419,10 +419,9 @@ namespace Ryujinx.HLE.HOS.Services.Time context.Memory.Read(ipcDesc.Position, temp); - using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(temp))) - { - return bufferReader.ReadStruct<ClockSnapshot>(); - } + using BinaryReader bufferReader = new(new MemoryStream(temp)); + + return bufferReader.ReadStruct<ClockSnapshot>(); } private void WriteClockSnapshotFromBuffer(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, ClockSnapshot clockSnapshot) @@ -430,4 +429,4 @@ namespace Ryujinx.HLE.HOS.Services.Time MemoryHelper.Write(context.Memory, ipcDesc.Position, clockSnapshot); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs index 6c9c15f1b..b690b708f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs @@ -13,12 +13,12 @@ namespace Ryujinx.HLE.HOS.Services.Time [Service("time:m")] // 9.0.0+ class ITimeServiceManager : IpcService { - private TimeManager _timeManager; - private int _automaticCorrectionEvent; + private readonly TimeManager _timeManager; + private int _automaticCorrectionEvent; public ITimeServiceManager(ServiceCtx context) { - _timeManager = TimeManager.Instance; + _timeManager = TimeManager.Instance; _automaticCorrectionEvent = 0; } @@ -62,11 +62,11 @@ namespace Ryujinx.HLE.HOS.Services.Time // SetupStandardSteadyClock(nn::util::Uuid clock_source_id, nn::TimeSpanType setup_value, nn::TimeSpanType internal_offset, nn::TimeSpanType test_offset, bool is_rtc_reset_detected) public ResultCode SetupStandardSteadyClock(ServiceCtx context) { - UInt128 clockSourceId = context.RequestData.ReadStruct<UInt128>(); - TimeSpanType setupValue = context.RequestData.ReadStruct<TimeSpanType>(); - TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>(); - TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>(); - bool isRtcResetDetected = context.RequestData.ReadBoolean(); + UInt128 clockSourceId = context.RequestData.ReadStruct<UInt128>(); + TimeSpanType setupValue = context.RequestData.ReadStruct<TimeSpanType>(); + TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>(); + TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>(); + bool isRtcResetDetected = context.RequestData.ReadBoolean(); ITickSource tickSource = context.Device.System.TickSource; @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public ResultCode SetupStandardLocalSystemClock(ServiceCtx context) { SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>(); - long posixTime = context.RequestData.ReadInt64(); + long posixTime = context.RequestData.ReadInt64(); ITickSource tickSource = context.Device.System.TickSource; @@ -93,8 +93,8 @@ namespace Ryujinx.HLE.HOS.Services.Time // SetupStandardNetworkSystemClock(nn::time::SystemClockContext context, nn::TimeSpanType sufficient_accuracy) public ResultCode SetupStandardNetworkSystemClock(ServiceCtx context) { - SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>(); - TimeSpanType sufficientAccuracy = context.RequestData.ReadStruct<TimeSpanType>(); + SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>(); + TimeSpanType sufficientAccuracy = context.RequestData.ReadStruct<TimeSpanType>(); _timeManager.SetupStandardNetworkSystemClock(clockContext, sufficientAccuracy); @@ -122,10 +122,10 @@ namespace Ryujinx.HLE.HOS.Services.Time // SetupTimeZoneManager(nn::time::LocationName location_name, nn::time::SteadyClockTimePoint timezone_update_timepoint, u32 total_location_name_count, nn::time::TimeZoneRuleVersion timezone_rule_version, buffer<nn::time::TimeZoneBinary, 0x21> timezone_binary) public ResultCode SetupTimeZoneManager(ServiceCtx context) { - string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); + string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); SteadyClockTimePoint timeZoneUpdateTimePoint = context.RequestData.ReadStruct<SteadyClockTimePoint>(); - uint totalLocationNameCount = context.RequestData.ReadUInt32(); - UInt128 timeZoneRuleVersion = context.RequestData.ReadStruct<UInt128>(); + uint totalLocationNameCount = context.RequestData.ReadUInt32(); + UInt128 timeZoneRuleVersion = context.RequestData.ReadStruct<UInt128>(); (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); @@ -133,10 +133,9 @@ namespace Ryujinx.HLE.HOS.Services.Time context.Memory.Read(bufferPosition, temp); - using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) - { - _timeManager.SetupTimeZoneManager(locationName, timeZoneUpdateTimePoint, totalLocationNameCount, timeZoneRuleVersion, timeZoneBinaryStream); - } + using MemoryStream timeZoneBinaryStream = new(temp); + + _timeManager.SetupTimeZoneManager(locationName, timeZoneUpdateTimePoint, totalLocationNameCount, timeZoneRuleVersion, timeZoneBinaryStream); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs index 3b042ec03..66ae67517 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs @@ -1,24 +1,27 @@ -namespace Ryujinx.HLE.HOS.Services.Time +using System.Diagnostics.CodeAnalysis; + +namespace Ryujinx.HLE.HOS.Services.Time { + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] public enum ResultCode { - ModuleId = 116, + ModuleId = 116, ErrorCodeShift = 9, Success = 0, - TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId, - PermissionDenied = (1 << ErrorCodeShift) | ModuleId, - TimeMismatch = (102 << ErrorCodeShift) | ModuleId, - UninitializedClock = (103 << ErrorCodeShift) | ModuleId, - TimeNotFound = (200 << ErrorCodeShift) | ModuleId, - Overflow = (201 << ErrorCodeShift) | ModuleId, - LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId, - OutOfRange = (902 << ErrorCodeShift) | ModuleId, - TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId, - TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId, - NotImplemented = (990 << ErrorCodeShift) | ModuleId, - NetworkTimeNotAvailable = (1000 << ErrorCodeShift) | ModuleId, - NetworkTimeTaskCanceled = (1003 << ErrorCodeShift) | ModuleId, + TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId, + PermissionDenied = (1 << ErrorCodeShift) | ModuleId, + TimeMismatch = (102 << ErrorCodeShift) | ModuleId, + UninitializedClock = (103 << ErrorCodeShift) | ModuleId, + TimeNotFound = (200 << ErrorCodeShift) | ModuleId, + Overflow = (201 << ErrorCodeShift) | ModuleId, + LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId, + OutOfRange = (902 << ErrorCodeShift) | ModuleId, + TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId, + TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId, + NotImplemented = (990 << ErrorCodeShift) | ModuleId, + NetworkTimeNotAvailable = (1000 << ErrorCodeShift) | ModuleId, + NetworkTimeTaskCanceled = (1003 << ErrorCodeShift) | ModuleId, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs index 97d7884ea..8ddb646b9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs @@ -6,14 +6,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService { class ISteadyClock : IpcService { - private SteadyClockCore _steadyClock; - private bool _writePermission; - private bool _bypassUninitializedClock; + private readonly SteadyClockCore _steadyClock; + private readonly bool _writePermission; + private readonly bool _bypassUninitializedClock; public ISteadyClock(SteadyClockCore steadyClock, bool writePermission, bool bypassUninitializedClock) { - _steadyClock = steadyClock; - _writePermission = writePermission; + _steadyClock = steadyClock; + _writePermission = writePermission; _bypassUninitializedClock = bypassUninitializedClock; } @@ -152,4 +152,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs index 3cd0a4a69..ada5f057a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs @@ -10,16 +10,16 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService { class ISystemClock : IpcService { - private SystemClockCore _clockCore; - private bool _writePermission; - private bool _bypassUninitializedClock; - private int _operationEventReadableHandle; + private readonly SystemClockCore _clockCore; + private readonly bool _writePermission; + private readonly bool _bypassUninitializedClock; + private int _operationEventReadableHandle; public ISystemClock(SystemClockCore clockCore, bool writePermission, bool bypassUninitializedClock) { - _clockCore = clockCore; - _writePermission = writePermission; - _bypassUninitializedClock = bypassUninitializedClock; + _clockCore = clockCore; + _writePermission = writePermission; + _bypassUninitializedClock = bypassUninitializedClock; _operationEventReadableHandle = 0; } @@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService { if (_operationEventReadableHandle == 0) { - KEvent kEvent = new KEvent(context.Device.System.KernelContext); + KEvent kEvent = new(context.Device.System.KernelContext); _clockCore.RegisterOperationEvent(kEvent.WritableEvent); @@ -128,4 +128,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs index 96a7e604f..81944c835 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs @@ -12,15 +12,15 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService { class ITimeZoneServiceForGlue : IpcService { - private TimeZoneContentManager _timeZoneContentManager; - private ITimeZoneServiceForPsc _inner; - private bool _writePermission; + private readonly TimeZoneContentManager _timeZoneContentManager; + private readonly ITimeZoneServiceForPsc _inner; + private readonly bool _writePermission; public ITimeZoneServiceForGlue(TimeZoneContentManager timeZoneContentManager, bool writePermission) { _timeZoneContentManager = timeZoneContentManager; - _writePermission = writePermission; - _inner = new ITimeZoneServiceForPsc(timeZoneContentManager.Manager, writePermission); + _writePermission = writePermission; + _inner = new ITimeZoneServiceForPsc(timeZoneContentManager.Manager, writePermission); } [CommandCmif(0)] @@ -55,9 +55,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) public ResultCode LoadLocationNameList(ServiceCtx context) { - uint index = context.RequestData.ReadUInt32(); + uint index = context.RequestData.ReadUInt32(); ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; ResultCode errorCode = _timeZoneContentManager.LoadLocationNameList(index, out string[] locationNameArray, (uint)bufferSize / 0x24); @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService public ResultCode LoadTimeZoneRule(ServiceCtx context) { ulong bufferPosition = context.Request.ReceiveBuff[0].Position; - ulong bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; if (bufferSize != 0x4000) { @@ -103,12 +103,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); - using (WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf<TimeZoneRule>())) - { - ref TimeZoneRule rules = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0]; + using WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf<TimeZoneRule>()); - return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName); - } + ref TimeZoneRule rules = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0]; + + return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName); } [CommandCmif(100)] @@ -139,4 +138,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return _inner.ToPosixTimeWithMyRule(context); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs index 3c9ac71f3..b3a87b8a6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs @@ -14,8 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService { class ITimeZoneServiceForPsc : IpcService { - private TimeZoneManager _timeZoneManager; - private bool _writePermission; + private readonly TimeZoneManager _timeZoneManager; + private readonly bool _writePermission; public ITimeZoneServiceForPsc(TimeZoneManager timeZoneManager, bool writePermission) { @@ -134,10 +134,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService context.Memory.Read(bufferPosition, temp); - using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) - { - result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream); - } + using MemoryStream timeZoneBinaryStream = new(temp); + result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream); return result; } @@ -149,7 +147,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); ulong timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position; - ulong timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size; + ulong timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size; if (timeZoneRuleBufferSize != 0x4000) { @@ -165,15 +163,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService context.Memory.Read(bufferPosition, temp); - using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) - { - using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>())) - { - ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0]; + using MemoryStream timeZoneBinaryStream = new(temp); + using WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>()); - result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream); - } - } + ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0]; + + result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream); return result; } @@ -189,9 +184,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) public ResultCode ToCalendarTime(ServiceCtx context) { - long posixTime = context.RequestData.ReadInt64(); + long posixTime = context.RequestData.ReadInt64(); ulong bufferPosition = context.Request.SendBuff[0].Position; - ulong bufferSize = context.Request.SendBuff[0].Size; + ulong bufferSize = context.Request.SendBuff[0].Size; if (bufferSize != 0x4000) { @@ -234,7 +229,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService public ResultCode ToPosixTime(ServiceCtx context) { ulong inBufferPosition = context.Request.SendBuff[0].Position; - ulong inBufferSize = context.Request.SendBuff[0].Size; + ulong inBufferSize = context.Request.SendBuff[0].Size; CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>(); @@ -253,7 +248,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService if (resultCode == ResultCode.Success) { ulong outBufferPosition = context.Request.RecvListBuff[0].Position; - ulong outBufferSize = context.Request.RecvListBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong outBufferSize = context.Request.RecvListBuff[0].Size; +#pragma warning restore IDE0059 context.Memory.Write(outBufferPosition, posixTime); context.ResponseData.Write(1); @@ -273,7 +270,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService if (resultCode == ResultCode.Success) { ulong outBufferPosition = context.Request.RecvListBuff[0].Position; - ulong outBufferSize = context.Request.RecvListBuff[0].Size; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + ulong outBufferSize = context.Request.RecvListBuff[0].Size; +#pragma warning restore IDE0059 context.Memory.Write(outBufferPosition, posixTime); diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs index e3b65f2ad..53c052e45 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs @@ -16,41 +16,38 @@ namespace Ryujinx.HLE.HOS.Services.Time { get { - if (_instance == null) - { - _instance = new TimeManager(); - } + _instance ??= new TimeManager(); return _instance; } } - public StandardSteadyClockCore StandardSteadyClock { get; } - public TickBasedSteadyClockCore TickBasedSteadyClock { get; } - public StandardLocalSystemClockCore StandardLocalSystemClock { get; } - public StandardNetworkSystemClockCore StandardNetworkSystemClock { get; } - public StandardUserSystemClockCore StandardUserSystemClock { get; } - public TimeZoneContentManager TimeZone { get; } - public EphemeralNetworkSystemClockCore EphemeralNetworkSystemClock { get; } - public TimeSharedMemory SharedMemory { get; } - public LocalSystemClockContextWriter LocalClockContextWriter { get; } - public NetworkSystemClockContextWriter NetworkClockContextWriter { get; } + public StandardSteadyClockCore StandardSteadyClock { get; } + public TickBasedSteadyClockCore TickBasedSteadyClock { get; } + public StandardLocalSystemClockCore StandardLocalSystemClock { get; } + public StandardNetworkSystemClockCore StandardNetworkSystemClock { get; } + public StandardUserSystemClockCore StandardUserSystemClock { get; } + public TimeZoneContentManager TimeZone { get; } + public EphemeralNetworkSystemClockCore EphemeralNetworkSystemClock { get; } + public TimeSharedMemory SharedMemory { get; } + public LocalSystemClockContextWriter LocalClockContextWriter { get; } + public NetworkSystemClockContextWriter NetworkClockContextWriter { get; } public EphemeralNetworkSystemClockContextWriter EphemeralClockContextWriter { get; } // TODO: 9.0.0+ power states and alarms public TimeManager() { - StandardSteadyClock = new StandardSteadyClockCore(); - TickBasedSteadyClock = new TickBasedSteadyClockCore(); - StandardLocalSystemClock = new StandardLocalSystemClockCore(StandardSteadyClock); - StandardNetworkSystemClock = new StandardNetworkSystemClockCore(StandardSteadyClock); - StandardUserSystemClock = new StandardUserSystemClockCore(StandardLocalSystemClock, StandardNetworkSystemClock); - TimeZone = new TimeZoneContentManager(); + StandardSteadyClock = new StandardSteadyClockCore(); + TickBasedSteadyClock = new TickBasedSteadyClockCore(); + StandardLocalSystemClock = new StandardLocalSystemClockCore(StandardSteadyClock); + StandardNetworkSystemClock = new StandardNetworkSystemClockCore(StandardSteadyClock); + StandardUserSystemClock = new StandardUserSystemClockCore(StandardLocalSystemClock, StandardNetworkSystemClock); + TimeZone = new TimeZoneContentManager(); EphemeralNetworkSystemClock = new EphemeralNetworkSystemClockCore(TickBasedSteadyClock); - SharedMemory = new TimeSharedMemory(); - LocalClockContextWriter = new LocalSystemClockContextWriter(SharedMemory); - NetworkClockContextWriter = new NetworkSystemClockContextWriter(SharedMemory); + SharedMemory = new TimeSharedMemory(); + LocalClockContextWriter = new LocalSystemClockContextWriter(SharedMemory); + NetworkClockContextWriter = new NetworkSystemClockContextWriter(SharedMemory); EphemeralClockContextWriter = new EphemeralNetworkSystemClockContextWriter(); } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs index 6b1e16875..75479a173 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs @@ -11,10 +11,12 @@ namespace Ryujinx.HLE.HOS.Services.Time { class TimeSharedMemory { - private Switch _device; - private KSharedMemory _sharedMemory; + private Switch _device; + private KSharedMemory _sharedMemory; private SharedMemoryStorage _timeSharedMemoryStorage; - private int _timeSharedMemorySize; +#pragma warning disable IDE0052 // Remove unread private member + private int _timeSharedMemorySize; +#pragma warning restore IDE0052 private const uint SteadyClockContextOffset = 0x00; private const uint LocalSystemClockContextOffset = 0x38; @@ -24,10 +26,10 @@ namespace Ryujinx.HLE.HOS.Services.Time public void Initialize(Switch device, KSharedMemory sharedMemory, SharedMemoryStorage timeSharedMemoryStorage, int timeSharedMemorySize) { - _device = device; - _sharedMemory = sharedMemory; + _device = device; + _sharedMemory = sharedMemory; _timeSharedMemoryStorage = timeSharedMemoryStorage; - _timeSharedMemorySize = timeSharedMemorySize; + _timeSharedMemorySize = timeSharedMemorySize; // Clean the shared memory timeSharedMemoryStorage.ZeroFill(); @@ -60,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); - ContinuousAdjustmentTimePoint adjustmentTimePoint = new ContinuousAdjustmentTimePoint + ContinuousAdjustmentTimePoint adjustmentTimePoint = new() { ClockOffset = (ulong)ticksTimeSpan.NanoSeconds, Multiplier = 1, @@ -71,17 +73,17 @@ namespace Ryujinx.HLE.HOS.Services.Time SteadyTimePoint = new SteadyClockTimePoint { ClockSourceId = clockSourceId, - TimePoint = 0 - } - } + TimePoint = 0, + }, + }, }; WriteObjectToSharedMemory(ContinuousAdjustmentTimePointOffset, 4, adjustmentTimePoint); - SteadyClockContext context = new SteadyClockContext + SteadyClockContext context = new() { InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds), - ClockSourceId = clockSourceId + ClockSourceId = clockSourceId, }; WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context); @@ -99,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Time private T ReadObjectFromSharedMemory<T>(ulong offset, ulong padding) where T : unmanaged { - T result; + T result; uint index; uint possiblyNewIndex; diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs index f7477e97d..58ae1cd8c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs @@ -8,36 +8,34 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; - using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule; namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { public class TimeZone { - private const int TimeTypeSize = 8; - private const int EpochYear = 1970; - private const int YearBase = 1900; - private const int EpochWeekDay = 4; + private const int TimeTypeSize = 8; + private const int EpochYear = 1970; + private const int YearBase = 1900; + private const int EpochWeekDay = 4; private const int SecondsPerMinute = 60; - private const int MinutesPerHour = 60; - private const int HoursPerDays = 24; - private const int DaysPerWekk = 7; - private const int DaysPerNYear = 365; - private const int DaysPerLYear = 366; - private const int MonthsPerYear = 12; - private const int SecondsPerHour = SecondsPerMinute * MinutesPerHour; - private const int SecondsPerDay = SecondsPerHour * HoursPerDays; + private const int MinutesPerHour = 60; + private const int HoursPerDays = 24; + private const int DaysPerWeek = 7; + private const int DaysPerNYear = 365; + private const int DaysPerLYear = 366; + private const int MonthsPerYear = 12; + private const int SecondsPerHour = SecondsPerMinute * MinutesPerHour; + private const int SecondsPerDay = SecondsPerHour * HoursPerDays; - private const int YearsPerRepeat = 400; + private const int YearsPerRepeat = 400; private const long AverageSecondsPerYear = 31556952; - private const long SecondsPerRepeat = YearsPerRepeat * AverageSecondsPerYear; + private const long SecondsPerRepeat = YearsPerRepeat * AverageSecondsPerYear; - private static readonly int[] YearLengths = { DaysPerNYear, DaysPerLYear }; - private static readonly int[][] MonthsLengths = new int[][] - { - new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + private static readonly int[] _yearLengths = { DaysPerNYear, DaysPerLYear }; + private static readonly int[][] _monthsLengths = { + new[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + new[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, }; private static ReadOnlySpan<byte> TimeZoneDefaultRule => ",M4.1.0,M10.5.0"u8; @@ -46,14 +44,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone private struct CalendarTimeInternal { // NOTE: On the IPC side this is supposed to be a 16 bits value but internally this need to be a 64 bits value for ToPosixTime. - public long Year; + public long Year; public sbyte Month; public sbyte Day; public sbyte Hour; public sbyte Minute; public sbyte Second; - public int CompareTo(CalendarTimeInternal other) + public readonly int CompareTo(CalendarTimeInternal other) { if (Year != other.Year) { @@ -98,16 +96,16 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { JulianDay, DayOfYear, - MonthNthDayOfWeek + MonthNthDayOfWeek, } private struct Rule { public RuleType Type; - public int Day; - public int Week; - public int Month; - public int TransitionTime; + public int Day; + public int Week; + public int Month; + public int TransitionTime; } private static int Detzcode32(ReadOnlySpan<byte> bytes) @@ -145,10 +143,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone TimeTypeInfo a = outRules.Ttis[aIndex]; TimeTypeInfo b = outRules.Ttis[bIndex]; - return a.GmtOffset == b.GmtOffset && - a.IsDaySavingTime == b.IsDaySavingTime && + return a.GmtOffset == b.GmtOffset && + a.IsDaySavingTime == b.IsDaySavingTime && a.IsStandardTimeDaylight == b.IsStandardTimeDaylight && - a.IsGMT == b.IsGMT && + a.IsGMT == b.IsGMT && StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0; } @@ -224,7 +222,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone seconds = 0; - bool isValid = GetNum(name, ref namePosition, out int num, 0, HoursPerDays * DaysPerWekk - 1); + bool isValid = GetNum(name, ref namePosition, out int num, 0, HoursPerDays * DaysPerWeek - 1); if (!isValid) { return false; @@ -310,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { rule = new Rule(); - bool isValid = false; + bool isValid; if (name[namePosition] == 'J') { @@ -347,7 +345,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return false; } - isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1); + isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWeek - 1); } else if (char.IsDigit((char)name[namePosition])) { @@ -391,7 +389,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { outRules = new TimeZoneRule(); - int stdLen; + int stdLen; ReadOnlySpan<byte> stdName = name; int namePosition = 0; @@ -408,7 +406,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { namePosition++; - stdName = name.Slice(namePosition); + stdName = name[namePosition..]; int stdNamePosition = namePosition; @@ -442,10 +440,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } int charCount = stdLen + 1; - int destLen = 0; + int destLen = 0; int dstOffset = 0; - ReadOnlySpan<byte> destName = name.Slice(namePosition); + ReadOnlySpan<byte> destName = name[namePosition..]; if (TzCharsArraySize < charCount) { @@ -456,7 +454,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { if (name[namePosition] == '<') { - destName = name.Slice(++namePosition); + destName = name[++namePosition..]; int destNamePosition = namePosition; namePosition = GetQZName(name.ToArray(), namePosition, '>'); @@ -471,9 +469,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } else { - destName = name.Slice(namePosition); + destName = name[namePosition..]; namePosition = GetTZName(name, namePosition); - destLen = namePosition; + destLen = namePosition; } if (destLen == 0) @@ -511,8 +509,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { namePosition++; - bool IsRuleValid = GetRule(name, ref namePosition, out Rule start); - if (!IsRuleValid) + bool isRuleValid = GetRule(name, ref namePosition, out Rule start); + if (!isRuleValid) { return false; } @@ -522,8 +520,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return false; } - IsRuleValid = GetRule(name, ref namePosition, out Rule end); - if (!IsRuleValid) + isRuleValid = GetRule(name, ref namePosition, out Rule end); + if (!isRuleValid) { return false; } @@ -537,28 +535,28 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone outRules.Ttis[0] = new TimeTypeInfo { - GmtOffset = -dstOffset, - IsDaySavingTime = true, - AbbreviationListIndex = stdLen + 1 + GmtOffset = -dstOffset, + IsDaySavingTime = true, + AbbreviationListIndex = stdLen + 1, }; outRules.Ttis[1] = new TimeTypeInfo { - GmtOffset = -stdOffset, - IsDaySavingTime = false, - AbbreviationListIndex = 0 + GmtOffset = -stdOffset, + IsDaySavingTime = false, + AbbreviationListIndex = 0, }; outRules.DefaultType = 0; - int timeCount = 0; - long janFirst = 0; - int janOffset = 0; - int yearBegining = EpochYear; + int timeCount = 0; + long janFirst = 0; + int janOffset = 0; + int yearBegining = EpochYear; do { - int yearSeconds = YearLengths[IsLeap(yearBegining - 1)] * SecondsPerDay; + int yearSeconds = _yearLengths[IsLeap(yearBegining - 1)] * SecondsPerDay; yearBegining--; if (IncrementOverflow64(ref janFirst, -yearSeconds)) { @@ -573,17 +571,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone for (year = yearBegining; year < yearLimit; year++) { int startTime = TransitionTime(year, start, stdOffset); - int endTime = TransitionTime(year, end, dstOffset); + int endTime = TransitionTime(year, end, dstOffset); - int yearSeconds = YearLengths[IsLeap(year)] * SecondsPerDay; + int yearSeconds = _yearLengths[IsLeap(year)] * SecondsPerDay; bool isReversed = endTime < startTime; if (isReversed) { - int swap = startTime; - - startTime = endTime; - endTime = swap; + (endTime, startTime) = (startTime, endTime); } if (isReversed || (startTime < endTime && (endTime - startTime < (yearSeconds + (stdOffset - dstOffset))))) @@ -632,7 +627,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } else if (YearsPerRepeat < year - yearBegining) { - outRules.GoBack = true; + outRules.GoBack = true; outRules.GoAhead = true; } } @@ -653,18 +648,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } } - long theirDstOffset = 0; + long theirDstOffset; for (int i = 0; i < outRules.TimeCount; i++) { int j = outRules.Types[i]; if (outRules.Ttis[j].IsDaySavingTime) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment theirDstOffset = -outRules.Ttis[j].GmtOffset; +#pragma warning restore IDE0059 } } bool isDaySavingTime = false; - long theirOffset = theirStdOffset; +#pragma warning disable IDE0059 // Remove unnecessary value assignment + long theirOffset = theirStdOffset; +#pragma warning restore IDE0059 for (int i = 0; i < outRules.TimeCount; i++) { int j = outRules.Types[i]; @@ -684,7 +683,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone theirOffset = -outRules.Ttis[j].GmtOffset; if (outRules.Ttis[j].IsDaySavingTime) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment theirDstOffset = theirOffset; +#pragma warning restore IDE0059 } else { @@ -694,33 +695,33 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone outRules.Ttis[0] = new TimeTypeInfo { - GmtOffset = -stdOffset, - IsDaySavingTime = false, - AbbreviationListIndex = 0 + GmtOffset = -stdOffset, + IsDaySavingTime = false, + AbbreviationListIndex = 0, }; outRules.Ttis[1] = new TimeTypeInfo { - GmtOffset = -dstOffset, - IsDaySavingTime = true, - AbbreviationListIndex = stdLen + 1 + GmtOffset = -dstOffset, + IsDaySavingTime = true, + AbbreviationListIndex = stdLen + 1, }; - outRules.TypeCount = 2; + outRules.TypeCount = 2; outRules.DefaultType = 0; } } else { // default is perpetual standard time - outRules.TypeCount = 1; - outRules.TimeCount = 0; + outRules.TypeCount = 1; + outRules.TimeCount = 0; outRules.DefaultType = 0; - outRules.Ttis[0] = new TimeTypeInfo + outRules.Ttis[0] = new TimeTypeInfo { - GmtOffset = -stdOffset, - IsDaySavingTime = false, - AbbreviationListIndex = 0 + GmtOffset = -stdOffset, + IsDaySavingTime = false, + AbbreviationListIndex = 0, }; } @@ -770,7 +771,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone case RuleType.MonthNthDayOfWeek: // Here we use Zeller's Congruence to get the day of week of the first month. - int m1 = (rule.Month + 9) % 12 + 1; + int m1 = (rule.Month + 9) % 12 + 1; int yy0 = (rule.Month <= 2) ? (year - 1) : year; int yy1 = yy0 / 100; int yy2 = yy0 % 100; @@ -779,7 +780,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (dayOfWeek < 0) { - dayOfWeek += DaysPerWekk; + dayOfWeek += DaysPerWeek; } // Get the zero origin @@ -787,23 +788,23 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (d < 0) { - d += DaysPerWekk; + d += DaysPerWeek; } for (int i = 1; i < rule.Week; i++) { - if (d + DaysPerWekk >= MonthsLengths[leapYear][rule.Month - 1]) + if (d + DaysPerWeek >= _monthsLengths[leapYear][rule.Month - 1]) { break; } - d += DaysPerWekk; + d += DaysPerWeek; } value = d * SecondsPerDay; for (int i = 0; i < rule.Month - 1; i++) { - value += MonthsLengths[leapYear][i] * SecondsPerDay; + value += _monthsLengths[leapYear][i] * SecondsPerDay; } break; @@ -887,7 +888,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { outRules = new TimeZoneRule(); - BinaryReader reader = new BinaryReader(inputData); + BinaryReader reader = new(inputData); long streamLength = reader.BaseStream.Length; @@ -902,10 +903,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone int ttisGMTCount = Detzcode32(header.TtisGMTCount); int ttisSTDCount = Detzcode32(header.TtisSTDCount); - int leapCount = Detzcode32(header.LeapCount); - int timeCount = Detzcode32(header.TimeCount); - int typeCount = Detzcode32(header.TypeCount); - int charCount = Detzcode32(header.CharCount); + int leapCount = Detzcode32(header.LeapCount); + int timeCount = Detzcode32(header.TimeCount); + int typeCount = Detzcode32(header.TypeCount); + int charCount = Detzcode32(header.CharCount); if (!(0 <= leapCount && leapCount < TzMaxLeaps @@ -1053,7 +1054,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } long position = (workBuffer.Length - p.Length); - long nRead = streamLength - position; + long nRead = streamLength - position; if (nRead < 0) { @@ -1077,7 +1078,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone byte[] name = new byte[TzNameMax]; Array.Copy(tempName, 1, name, 0, nRead - 1); - Box<TimeZoneRule> tempRulesBox = new Box<TimeZoneRule>(); + Box<TimeZoneRule> tempRulesBox = new(); ref TimeZoneRule tempRules = ref tempRulesBox.Data; if (ParsePosixName(name, ref tempRulesBox.Data, false)) @@ -1247,17 +1248,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone private static ResultCode CreateCalendarTime(long time, int gmtOffset, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo) { - long year = EpochYear; - long timeDays = time / SecondsPerDay; + long year = EpochYear; + long timeDays = time / SecondsPerDay; long remainingSeconds = time % SecondsPerDay; - calendarTime = new CalendarTimeInternal(); + calendarTime = new CalendarTimeInternal(); calendarAdditionalInfo = new CalendarAdditionalInfo(); - while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)]) + while (timeDays < 0 || timeDays >= _yearLengths[IsLeap((int)year)]) { long timeDelta = timeDays / DaysPerLYear; - long delta = timeDelta; + long delta = timeDelta; if (delta == 0) { @@ -1298,12 +1299,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return ResultCode.OutOfRange; } - dayOfYear += YearLengths[IsLeap((int)year)]; + dayOfYear += _yearLengths[IsLeap((int)year)]; } - while (dayOfYear >= YearLengths[IsLeap((int)year)]) + while (dayOfYear >= _yearLengths[IsLeap((int)year)]) { - dayOfYear -= YearLengths[IsLeap((int)year)]; + dayOfYear -= _yearLengths[IsLeap((int)year)]; if (IncrementOverflow64(ref year, 1)) { @@ -1311,13 +1312,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } } - calendarTime.Year = year; + calendarTime.Year = year; calendarAdditionalInfo.DayOfYear = (uint)dayOfYear; - long dayOfWeek = (EpochWeekDay + ((year - EpochYear) % DaysPerWekk) * (DaysPerNYear % DaysPerWekk) + GetLeapDays(year - 1) - GetLeapDays(EpochYear - 1) + dayOfYear) % DaysPerWekk; + long dayOfWeek = (EpochWeekDay + ((year - EpochYear) % DaysPerWeek) * (DaysPerNYear % DaysPerWeek) + GetLeapDays(year - 1) - GetLeapDays(EpochYear - 1) + dayOfYear) % DaysPerWeek; if (dayOfWeek < 0) { - dayOfWeek += DaysPerWekk; + dayOfWeek += DaysPerWeek; } calendarAdditionalInfo.DayOfWeek = (uint)dayOfWeek; @@ -1328,7 +1329,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone calendarTime.Minute = (sbyte)(remainingSeconds / SecondsPerMinute); calendarTime.Second = (sbyte)(remainingSeconds % SecondsPerMinute); - int[] ip = MonthsLengths[IsLeap((int)year)]; + int[] ip = _monthsLengths[IsLeap((int)year)]; for (calendarTime.Month = 0; dayOfYear >= ip[calendarTime.Month]; ++calendarTime.Month) { @@ -1338,14 +1339,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone calendarTime.Day = (sbyte)(dayOfYear + 1); calendarAdditionalInfo.IsDaySavingTime = false; - calendarAdditionalInfo.GmtOffset = gmtOffset; + calendarAdditionalInfo.GmtOffset = gmtOffset; return 0; } private static ResultCode ToCalendarTimeInternal(in TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo) { - calendarTime = new CalendarTimeInternal(); + calendarTime = new CalendarTimeInternal(); calendarAdditionalInfo = new CalendarAdditionalInfo(); ResultCode result; @@ -1368,7 +1369,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone seconds -= 1; - years = (seconds / SecondsPerRepeat + 1) * YearsPerRepeat; + years = (seconds / SecondsPerRepeat + 1) * YearsPerRepeat; seconds = years * AverageSecondsPerYear; if (time < rules.Ats[0]) @@ -1411,7 +1412,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } else { - int low = 1; + int low = 1; int high = rules.TimeCount; while (low < high) @@ -1451,7 +1452,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { posixTime = 0; - int hour = calendarTime.Hour; + int hour = calendarTime.Hour; int minute = calendarTime.Minute; if (NormalizeOverflow32(ref hour, ref minute, MinutesPerHour)) @@ -1467,10 +1468,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return ResultCode.Overflow; } - calendarTime.Day = (sbyte)day; + calendarTime.Day = (sbyte)day; calendarTime.Hour = (sbyte)hour; - long year = calendarTime.Year; + long year = calendarTime.Year; long month = calendarTime.Month; if (NormalizeOverflow64(ref year, ref month, MonthsPerYear)) @@ -1499,7 +1500,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone li++; } - day += YearLengths[IsLeap((int)li)]; + day += _yearLengths[IsLeap((int)li)]; } while (day > DaysPerLYear) @@ -1511,7 +1512,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone li++; } - day -= YearLengths[IsLeap((int)li)]; + day -= _yearLengths[IsLeap((int)li)]; if (IncrementOverflow64(ref year, 1)) { @@ -1521,7 +1522,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone while (true) { - int i = MonthsLengths[IsLeap((int)year)][calendarTime.Month]; + int i = _monthsLengths[IsLeap((int)year)][calendarTime.Month]; if (day <= i) { @@ -1573,7 +1574,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone calendarTime.Second = 0; } - long low = long.MinValue; + long low = long.MinValue; long high = long.MaxValue; while (true) @@ -1670,15 +1671,15 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { Time = new CalendarTime() { - Year = (short)calendarTime.Year, + Year = (short)calendarTime.Year, // NOTE: Nintendo's month range is 1-12, internal range is 0-11. Month = (sbyte)(calendarTime.Month + 1), - Day = calendarTime.Day, - Hour = calendarTime.Hour, + Day = calendarTime.Day, + Hour = calendarTime.Hour, Minute = calendarTime.Minute, - Second = calendarTime.Second + Second = calendarTime.Second, }, - AdditionalInfo = calendarAdditionalInfo + AdditionalInfo = calendarAdditionalInfo, }; return result; @@ -1686,18 +1687,18 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone internal static ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime) { - CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal() + CalendarTimeInternal calendarTimeInternal = new() { - Year = calendarTime.Year, + Year = calendarTime.Year, // NOTE: Nintendo's month range is 1-12, internal range is 0-11. - Month = (sbyte)(calendarTime.Month - 1), - Day = calendarTime.Day, - Hour = calendarTime.Hour, + Month = (sbyte)(calendarTime.Month - 1), + Day = calendarTime.Day, + Hour = calendarTime.Hour, Minute = calendarTime.Minute, - Second = calendarTime.Second + Second = calendarTime.Second, }; return ToPosixTimeInternal(in rules, calendarTimeInternal, out posixTime); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs index 9367024e4..67cb286ae 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs @@ -11,7 +11,6 @@ using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Time.Clock; -using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; using System.IO; @@ -24,11 +23,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { private const long TimeZoneBinaryTitleId = 0x010000000000080E; - private readonly string TimeZoneSystemTitleMissingErrorMessage = "TimeZoneBinary system title not found! TimeZone conversions will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; + private const string TimeZoneSystemTitleMissingErrorMessage = "TimeZoneBinary system title not found! TimeZone conversions will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; - private VirtualFileSystem _virtualFileSystem; + private VirtualFileSystem _virtualFileSystem; private IntegrityCheckLevel _fsIntegrityCheckLevel; - private ContentManager _contentManager; + private ContentManager _contentManager; public string[] LocationNameCache { get; private set; } @@ -41,8 +40,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public void InitializeInstance(VirtualFileSystem virtualFileSystem, ContentManager contentManager, IntegrityCheckLevel fsIntegrityCheckLevel) { - _virtualFileSystem = virtualFileSystem; - _contentManager = contentManager; + _virtualFileSystem = virtualFileSystem; + _contentManager = contentManager; _fsIntegrityCheckLevel = fsIntegrityCheckLevel; InitializeLocationNameCache(); @@ -90,31 +89,30 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { if (HasTimeZoneBinaryTitle()) { - using (IStorage ncaFileStream = new LocalStorage(_virtualFileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open)) + using IStorage ncaFileStream = new LocalStorage(VirtualFileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open); + + Nca nca = new(_virtualFileSystem.KeySet, ncaFileStream); + IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _fsIntegrityCheckLevel); + + using var binaryListFile = new UniqueRef<IFile>(); + + romfs.OpenFile(ref binaryListFile.Ref, "/binaryList.txt".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + StreamReader reader = new(binaryListFile.Get.AsStream()); + + List<string> locationNameList = new(); + + string locationName; + while ((locationName = reader.ReadLine()) != null) { - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFileStream); - IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _fsIntegrityCheckLevel); - - using var binaryListFile = new UniqueRef<IFile>(); - - romfs.OpenFile(ref binaryListFile.Ref, "/binaryList.txt".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - StreamReader reader = new StreamReader(binaryListFile.Get.AsStream()); - - List<string> locationNameList = new List<string>(); - - string locationName; - while ((locationName = reader.ReadLine()) != null) - { - locationNameList.Add(locationName); - } - - LocationNameCache = locationNameList.ToArray(); + locationNameList.Add(locationName); } + + LocationNameCache = locationNameList.ToArray(); } else { - LocationNameCache = new string[] { "UTC" }; + LocationNameCache = new[] { "UTC" }; Logger.Error?.Print(LogClass.ServiceTime, TimeZoneSystemTitleMissingErrorMessage); } @@ -129,9 +127,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return new[] { (0, "UTC", "UTC") }; } - List<(int Offset, string Location, string Abbr)> outList = new List<(int Offset, string Location, string Abbr)>(); + List<(int Offset, string Location, string Abbr)> outList = new(); var now = DateTimeOffset.Now.ToUnixTimeSeconds(); - using (IStorage ncaStorage = new LocalStorage(_virtualFileSystem.SwitchPathToSystemPath(tzBinaryContentPath), FileAccess.Read, FileMode.Open)) + using (IStorage ncaStorage = new LocalStorage(VirtualFileSystem.SwitchPathToSystemPath(tzBinaryContentPath), FileAccess.Read, FileMode.Open)) using (IFileSystem romfs = new Nca(_virtualFileSystem.KeySet, ncaStorage).OpenFileSystem(NcaSectionType.Data, _fsIntegrityCheckLevel)) { foreach (string locName in LocationNameCache) @@ -149,7 +147,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone continue; } - TimeZoneRuleBox tzRuleBox = new TimeZoneRuleBox(); + TimeZoneRuleBox tzRuleBox = new(); ref TimeZoneRule tzRule = ref tzRuleBox.Data; TimeZone.ParseTimeZoneBinary(ref tzRule, tzif.Get.AsStream()); @@ -219,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public ResultCode LoadLocationNameList(uint index, out string[] outLocationNameArray, uint maxLength) { - List<string> locationNameList = new List<string>(); + List<string> locationNameList = new(); for (int i = 0; i < LocationNameCache.Length && i < maxLength; i++) { @@ -259,16 +257,16 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone internal ResultCode GetTimeZoneBinary(string locationName, out Stream timeZoneBinaryStream, out LocalStorage ncaFile) { timeZoneBinaryStream = null; - ncaFile = null; + ncaFile = null; if (!HasTimeZoneBinaryTitle() || !IsLocationNameValid(locationName)) { return ResultCode.TimeZoneNotFound; } - ncaFile = new LocalStorage(_virtualFileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open); + ncaFile = new LocalStorage(VirtualFileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open); - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile); + Nca nca = new(_virtualFileSystem.KeySet, ncaFile); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _fsIntegrityCheckLevel); using var timeZoneBinaryFile = new UniqueRef<IFile>(); diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs index 8b85d697b..219b1d9fb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs @@ -7,20 +7,20 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { class TimeZoneManager { - private bool _isInitialized; - private Box<TimeZoneRule> _myRules; - private string _deviceLocationName; - private UInt128 _timeZoneRuleVersion; - private uint _totalLocationNameCount; + private bool _isInitialized; + private Box<TimeZoneRule> _myRules; + private string _deviceLocationName; + private UInt128 _timeZoneRuleVersion; + private uint _totalLocationNameCount; private SteadyClockTimePoint _timeZoneUpdateTimePoint; - private readonly object _lock = new(); + private readonly object _lock = new(); public TimeZoneManager() { - _isInitialized = false; - _deviceLocationName = "UTC"; + _isInitialized = false; + _deviceLocationName = "UTC"; _timeZoneRuleVersion = new UInt128(); - _myRules = new Box<TimeZoneRule>(); + _myRules = new Box<TimeZoneRule>(); _timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom(); } @@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (_isInitialized) { deviceLocationName = _deviceLocationName; - result = ResultCode.Success; + result = ResultCode.Success; } } @@ -69,15 +69,15 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone lock (_lock) { - Box<TimeZoneRule> rules = new Box<TimeZoneRule>(); + Box<TimeZoneRule> rules = new(); bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref rules.Data, timeZoneBinaryStream); if (timeZoneConversionSuccess) { _deviceLocationName = locationName; - _myRules = rules; - result = ResultCode.Success; + _myRules = rules; + result = ResultCode.Success; } } @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (_isInitialized) { totalLocationNameCount = _totalLocationNameCount; - result = ResultCode.Success; + result = ResultCode.Success; } } @@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (_isInitialized || bypassUninitialized) { _timeZoneUpdateTimePoint = timeZoneUpdatedTimePoint; - result = ResultCode.Success; + result = ResultCode.Success; } } @@ -135,12 +135,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (_isInitialized) { timeZoneUpdatedTimePoint = _timeZoneUpdateTimePoint; - result = ResultCode.Success; + result = ResultCode.Success; } else { timeZoneUpdatedTimePoint = SteadyClockTimePoint.GetRandom(); - result = ResultCode.UninitializedClock; + result = ResultCode.UninitializedClock; } } @@ -181,12 +181,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (_isInitialized) { timeZoneRuleVersion = _timeZoneRuleVersion; - result = ResultCode.Success; + result = ResultCode.Success; } else { timeZoneRuleVersion = new UInt128(); - result = ResultCode.UninitializedClock; + result = ResultCode.UninitializedClock; } } @@ -206,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone else { calendar = new CalendarInfo(); - result = ResultCode.UninitializedClock; + result = ResultCode.UninitializedClock; } } @@ -238,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone else { posixTime = 0; - result = ResultCode.UninitializedClock; + result = ResultCode.UninitializedClock; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs index a84a27859..8e7dc6d52 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs @@ -18,4 +18,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public int GmtOffset; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs index 68e6245b3..7fc6f7a09 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarInfo.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone [StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x20, CharSet = CharSet.Ansi)] struct CalendarInfo { - public CalendarTime Time; + public CalendarTime Time; public CalendarAdditionalInfo AdditionalInfo; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs index d594223d4..fa082bde5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarTime.cs @@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public sbyte Minute; public sbyte Second; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs index b8b3d9171..47620f563 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs @@ -25,4 +25,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public ushort Padding2; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs index 67237f3d2..b8dd2382a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs @@ -53,4 +53,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public int DefaultType; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs index 022c34a93..1d05ee246 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TzifHeader.cs @@ -16,4 +16,4 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public int TypeCount; public int CharCount; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs b/src/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs index 38d37055c..2b80604ac 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Types [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SteadyClockContext { - public ulong InternalOffset; + public ulong InternalOffset; public UInt128 ClockSourceId; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs b/src/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs index 3fcd3a144..34749c425 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs @@ -5,18 +5,18 @@ namespace Ryujinx.HLE.HOS.Services.Time [Flags] enum TimePermissions { - LocalSystemClockWritableMask = 0x1, - UserSystemClockWritableMask = 0x2, + LocalSystemClockWritableMask = 0x1, + UserSystemClockWritableMask = 0x2, NetworkSystemClockWritableMask = 0x4, - TimeZoneWritableMask = 0x8, - SteadyClockWritableMask = 0x10, - BypassUninitialized = 0x20, + TimeZoneWritableMask = 0x8, + SteadyClockWritableMask = 0x10, + BypassUninitialized = 0x20, - User = 0, - Admin = LocalSystemClockWritableMask | UserSystemClockWritableMask | TimeZoneWritableMask, - System = NetworkSystemClockWritableMask, + User = 0, + Admin = LocalSystemClockWritableMask | UserSystemClockWritableMask | TimeZoneWritableMask, + System = NetworkSystemClockWritableMask, SystemUpdate = BypassUninitialized, - Repair = SteadyClockWritableMask, - Manufacture = LocalSystemClockWritableMask | UserSystemClockWritableMask | NetworkSystemClockWritableMask | TimeZoneWritableMask | SteadyClockWritableMask + Repair = SteadyClockWritableMask, + Manufacture = LocalSystemClockWritableMask | UserSystemClockWritableMask | NetworkSystemClockWritableMask | TimeZoneWritableMask | SteadyClockWritableMask, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs index 56b12af08..b41b8a48c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs +++ b/src/Ryujinx.HLE/HOS/Services/Usb/IClientRootSession.cs @@ -6,4 +6,4 @@ { public IClientRootSession(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Usb/IDsService.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IDsService.cs index 4dbb6fc1d..ee6c8f070 100644 --- a/src/Ryujinx.HLE/HOS/Services/Usb/IDsService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Usb/IDsService.cs @@ -5,4 +5,4 @@ { public IDsService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs index cecdbc313..18cbce79a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Usb/IPdCradleManager.cs @@ -5,4 +5,4 @@ { public IPdCradleManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs index 1fb574d29..011debafd 100644 --- a/src/Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Usb/IPdManager.cs @@ -5,4 +5,4 @@ { public IPdManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Usb/IPmService.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IPmService.cs index 38beee079..ed6bba694 100644 --- a/src/Ryujinx.HLE/HOS/Services/Usb/IPmService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Usb/IPmService.cs @@ -5,4 +5,4 @@ { public IPmService(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs index 0981e4ff4..65bf1c9fa 100644 --- a/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs +++ b/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown1.cs @@ -5,4 +5,4 @@ { public IUnknown1(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs b/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs index 563696bb8..e0bf0bf4a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs +++ b/src/Ryujinx.HLE/HOS/Services/Usb/IUnknown2.cs @@ -5,4 +5,4 @@ { public IUnknown2(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs index 526cecf84..9cc9d421f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs @@ -24,4 +24,4 @@ namespace Ryujinx.HLE.HOS.Services.Vi return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs index d564dabe7..dd4b25a08 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs @@ -25,4 +25,4 @@ namespace Ryujinx.HLE.HOS.Services.Vi return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs index 0dfd84f59..b2415f2e4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs @@ -25,4 +25,4 @@ namespace Ryujinx.HLE.HOS.Services.Vi return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs index c64339c91..3bed7e57d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs @@ -2,16 +2,16 @@ namespace Ryujinx.HLE.HOS.Services.Vi { enum ResultCode { - ModuleId = 114, + ModuleId = 114, ErrorCodeShift = 9, Success = 0, - InvalidArguments = (1 << ErrorCodeShift) | ModuleId, - InvalidLayerSize = (4 << ErrorCodeShift) | ModuleId, - PermissionDenied = (5 << ErrorCodeShift) | ModuleId, + InvalidArguments = (1 << ErrorCodeShift) | ModuleId, + InvalidLayerSize = (4 << ErrorCodeShift) | ModuleId, + PermissionDenied = (5 << ErrorCodeShift) | ModuleId, InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId, - InvalidValue = (7 << ErrorCodeShift) | ModuleId, - AlreadyOpened = (9 << ErrorCodeShift) | ModuleId + InvalidValue = (7 << ErrorCodeShift) | ModuleId, + AlreadyOpened = (9 << ErrorCodeShift) | ModuleId, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs index 1fa99e65a..782bb7d7e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService { - static class AndroidSurfaceComposerClient + class AndroidSurfaceComposerClient { // NOTE: This is android::SurfaceComposerClient::getDisplayInfo. public static (ulong, ulong) GetDisplayInfo(ServiceCtx context, ulong displayId = 0) @@ -16,4 +16,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs index 6093381cc..3a08cdd78 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs @@ -4,7 +4,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService { class IManagerDisplayService : IpcService { - private IApplicationDisplayService _applicationDisplayService; +#pragma warning disable IDE0052 // Remove unread private member + private readonly IApplicationDisplayService _applicationDisplayService; +#pragma warning restore IDE0052 public IManagerDisplayService(IApplicationDisplayService applicationDisplayService) { @@ -29,8 +31,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService // CreateManagedLayer(u32, u64, nn::applet::AppletResourceUserId) -> u64 public ResultCode CreateManagedLayer(ServiceCtx context) { - long layerFlags = context.RequestData.ReadInt64(); - long displayId = context.RequestData.ReadInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + long layerFlags = context.RequestData.ReadInt64(); + long displayId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 long appletResourceUserId = context.RequestData.ReadInt64(); ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId); @@ -77,4 +81,4 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs index a24aa0798..12ac2cf4a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs @@ -4,7 +4,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService { class ISystemDisplayService : IpcService { - private IApplicationDisplayService _applicationDisplayService; +#pragma warning disable IDE0052 // Remove unread private member + private readonly IApplicationDisplayService _applicationDisplayService; +#pragma warning restore IDE0052 public ISystemDisplayService(IApplicationDisplayService applicationDisplayService) { @@ -56,4 +58,4 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs index cf459cb2e..c712bb1d3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs @@ -6,6 +6,6 @@ ScaleToWindow, ScaleAndCrop, None, - PreserveAspectRatio + PreserveAspectRatio, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs index d46206d4a..eae8a8001 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs @@ -7,10 +7,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Type struct DisplayInfo { public Array64<byte> Name; - public bool LayerLimitEnabled; - public Array7<byte> Padding; - public ulong LayerLimitMax; - public ulong Width; - public ulong Height; + public bool LayerLimitEnabled; + public Array7<byte> Padding; + public ulong LayerLimitMax; + public ulong Width; + public ulong Height; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs index ac8c3e027..536c0d46f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs @@ -6,6 +6,6 @@ Freeze, ScaleToWindow, ScaleAndCrop, - PreserveAspectRatio + PreserveAspectRatio, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 89eed5b57..3fbd7d20d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -3,7 +3,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Services.SurfaceFlinger; using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService; using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types; @@ -27,27 +26,27 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService public int RetrievedEventsCount; } - private readonly List<DisplayInfo> _displayInfo; + private readonly List<DisplayInfo> _displayInfo; private readonly Dictionary<ulong, DisplayState> _openDisplays; private int _vsyncEventHandle; public IApplicationDisplayService(ViServiceType serviceType) { - _serviceType = serviceType; - _displayInfo = new List<DisplayInfo>(); + _serviceType = serviceType; + _displayInfo = new List<DisplayInfo>(); _openDisplays = new Dictionary<ulong, DisplayState>(); void AddDisplayInfo(string name, bool layerLimitEnabled, ulong layerLimitMax, ulong width, ulong height) { - DisplayInfo displayInfo = new DisplayInfo() + DisplayInfo displayInfo = new() { - Name = new Array64<byte>(), + Name = new Array64<byte>(), LayerLimitEnabled = layerLimitEnabled, - Padding = new Array7<byte>(), - LayerLimitMax = layerLimitMax, - Width = width, - Height = height + Padding = new Array7<byte>(), + LayerLimitMax = layerLimitMax, + Width = width, + Height = height, }; Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(displayInfo.Name.AsSpan()); @@ -55,11 +54,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService _displayInfo.Add(displayInfo); } - AddDisplayInfo("Default", true, 1, 1920, 1080); - AddDisplayInfo("External", true, 1, 1920, 1080); - AddDisplayInfo("Edid", true, 1, 0, 0); - AddDisplayInfo("Internal", true, 1, 1920, 1080); - AddDisplayInfo("Null", false, 0, 1920, 1080); + AddDisplayInfo("Default", true, 1, 1920, 1080); + AddDisplayInfo("External", true, 1, 1920, 1080); + AddDisplayInfo("Edid", true, 1, 0, 0); + AddDisplayInfo("Internal", true, 1, 1920, 1080); + AddDisplayInfo("Null", false, 0, 1920, 1080); } [CommandCmif(100)] @@ -232,10 +231,14 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService public ResultCode OpenLayer(ServiceCtx context) { // TODO: support multi display. +#pragma warning disable IDE0059 // Remove unnecessary value assignment byte[] displayName = context.RequestData.ReadBytes(0x40); +#pragma warning restore IDE0059 - long layerId = context.RequestData.ReadInt64(); - long userId = context.RequestData.ReadInt64(); + long layerId = context.RequestData.ReadInt64(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + long userId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 ulong parcelPtr = context.Request.ReceiveBuff[0].Position; ResultCode result = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId, out IBinder producer); @@ -247,7 +250,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); - Parcel parcel = new Parcel(0x28, 0x4); + Parcel parcel = new(0x28, 0x4); parcel.WriteObject(producer, "dispdrv\0"); @@ -273,8 +276,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // CreateStrayLayer(u32, u64) -> (u64, u64, buffer<bytes, 6>) public ResultCode CreateStrayLayer(ServiceCtx context) { +#pragma warning disable IDE0059 // Remove unnecessary value assignment long layerFlags = context.RequestData.ReadInt64(); - long displayId = context.RequestData.ReadInt64(); + long displayId = context.RequestData.ReadInt64(); +#pragma warning restore IDE0059 ulong parcelPtr = context.Request.ReceiveBuff[0].Position; @@ -283,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); - Parcel parcel = new Parcel(0x28, 0x4); + Parcel parcel = new(0x28, 0x4); parcel.WriteObject(producer, "dispdrv\0"); @@ -327,10 +332,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService DestinationScalingMode? convertedScalingMode = scalingMode switch { - SourceScalingMode.None => DestinationScalingMode.None, - SourceScalingMode.Freeze => DestinationScalingMode.Freeze, - SourceScalingMode.ScaleAndCrop => DestinationScalingMode.ScaleAndCrop, - SourceScalingMode.ScaleToWindow => DestinationScalingMode.ScaleToWindow, + SourceScalingMode.None => DestinationScalingMode.None, + SourceScalingMode.Freeze => DestinationScalingMode.Freeze, + SourceScalingMode.ScaleAndCrop => DestinationScalingMode.ScaleAndCrop, + SourceScalingMode.ScaleToWindow => DestinationScalingMode.ScaleToWindow, SourceScalingMode.PreserveAspectRatio => DestinationScalingMode.PreserveAspectRatio, _ => null, }; @@ -354,13 +359,13 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService private ulong GetA8B8G8R8LayerSize(int width, int height, out int pitch, out int alignment) { - const int DefaultAlignment = 0x1000; - const ulong DefaultSize = 0x20000; + const int DefaultAlignment = 0x1000; + const ulong DefaultSize = 0x20000; alignment = DefaultAlignment; - pitch = BitUtils.AlignUp(BitUtils.DivRoundUp(width * 32, 8), 64); + pitch = BitUtils.AlignUp(BitUtils.DivRoundUp(width * 32, 8), 64); - int memorySize = pitch * BitUtils.AlignUp(height, 64); + int memorySize = pitch * BitUtils.AlignUp(height, 64); ulong requiredMemorySize = (ulong)BitUtils.AlignUp(memorySize, alignment); return (requiredMemorySize + DefaultSize - 1) / DefaultSize * DefaultSize; @@ -373,18 +378,18 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // The size of the layer buffer should be an aligned multiple of width * height // because it was created using GetIndirectLayerImageRequiredMemoryInfo as a guide. - long layerWidth = context.RequestData.ReadInt64(); - long layerHeight = context.RequestData.ReadInt64(); - long layerHandle = context.RequestData.ReadInt64(); + long layerWidth = context.RequestData.ReadInt64(); + long layerHeight = context.RequestData.ReadInt64(); + long layerHandle = context.RequestData.ReadInt64(); ulong layerBuffPosition = context.Request.ReceiveBuff[0].Position; - ulong layerBuffSize = context.Request.ReceiveBuff[0].Size; + ulong layerBuffSize = context.Request.ReceiveBuff[0].Size; // Get the pitch of the layer that is necessary to render correctly. ulong size = GetA8B8G8R8LayerSize((int)layerWidth, (int)layerHeight, out int pitch, out _); Debug.Assert(layerBuffSize == size); - RenderingSurfaceInfo surfaceInfo = new RenderingSurfaceInfo(ColorFormat.A8B8G8R8, (uint)layerWidth, (uint)layerHeight, (uint)pitch, (uint)layerBuffSize); + RenderingSurfaceInfo surfaceInfo = new(ColorFormat.A8B8G8R8, (uint)layerWidth, (uint)layerHeight, (uint)pitch, (uint)layerBuffSize); // Get the applet associated with the handle. object appletObject = context.Device.System.AppletState.IndirectLayerHandles.GetData((int)layerHandle); @@ -425,7 +430,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService } */ - int width = (int)context.RequestData.ReadUInt64(); + int width = (int)context.RequestData.ReadUInt64(); int height = (int)context.RequestData.ReadUInt64(); if (height < 0 || width < 0) @@ -445,7 +450,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // NOTE: The official service setup a A8B8G8R8 texture with a linear layout and then query its size. // As we don't need this texture on the emulator, we can just simplify this logic and directly // do a linear layout size calculation. (stride * height * bytePerPixel) - ulong size = GetA8B8G8R8LayerSize(width, height, out int pitch, out int alignment); + ulong size = GetA8B8G8R8LayerSize(width, height, out _, out int alignment); context.ResponseData.Write(size); context.ResponseData.Write(alignment); diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs b/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs index ba6f8e5f8..41c8bbfaf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs @@ -4,6 +4,6 @@ { Application, Manager, - System + System, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs index 0416868a8..a84625780 100644 --- a/src/Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Wlan/IInfraManager.cs @@ -5,4 +5,4 @@ { public IInfraManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs index 6c2e20a45..060e5854d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs +++ b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetActionFrame.cs @@ -5,4 +5,4 @@ { public ILocalGetActionFrame(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs index a224a192d..d726b7d49 100644 --- a/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs +++ b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalGetFrame.cs @@ -5,4 +5,4 @@ { public ILocalGetFrame(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs index 4cc2c4b2e..3db07ac73 100644 --- a/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Wlan/ILocalManager.cs @@ -5,4 +5,4 @@ { public ILocalManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs index ab5b21933..5c9329234 100644 --- a/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs +++ b/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketGetFrame.cs @@ -5,4 +5,4 @@ { public ISocketGetFrame(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs index afa1bede2..1b40b80e4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Wlan/ISocketManager.cs @@ -5,4 +5,4 @@ { public ISocketManager(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs b/src/Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs index dfae18e5d..731f8c0a9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs +++ b/src/Ryujinx.HLE/HOS/Services/Wlan/IUnknown1.cs @@ -5,4 +5,4 @@ { public IUnknown1(ServiceCtx context) { } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs index 5704ef4b0..a2837e207 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.SystemState public AppletStateMgr(Horizon system) { - Messages = new ConcurrentQueue<AppletMessage>(); + Messages = new ConcurrentQueue<AppletMessage>(); MessageEvent = new KEvent(system.KernelContext); AppletResourceUserIds = new IdDictionary(); @@ -39,4 +39,4 @@ namespace Ryujinx.HLE.HOS.SystemState MessageEvent.ReadableEvent.Signal(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/SystemState/ColorSet.cs b/src/Ryujinx.HLE/HOS/SystemState/ColorSet.cs index 4d7a7e2f0..b19242065 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/ColorSet.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/ColorSet.cs @@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.SystemState public enum ColorSet { BasicWhite = 0, - BasicBlack = 1 + BasicBlack = 1, } } diff --git a/src/Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs b/src/Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs index ba35ea6bc..e3cb68a23 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/KeyboardLayout.cs @@ -20,6 +20,6 @@ namespace Ryujinx.HLE.HOS.SystemState ChineseTraditional, Min = Default, - Max = ChineseTraditional + Max = ChineseTraditional, } } diff --git a/src/Ryujinx.HLE/HOS/SystemState/RegionCode.cs b/src/Ryujinx.HLE/HOS/SystemState/RegionCode.cs index dd6ed8fac..111227237 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/RegionCode.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/RegionCode.cs @@ -12,6 +12,6 @@ namespace Ryujinx.HLE.HOS.SystemState Taiwan, Min = Japan, - Max = Taiwan + Max = Taiwan, } } diff --git a/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs index 3f755105b..f5b7fc0f1 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs @@ -19,6 +19,6 @@ namespace Ryujinx.HLE.HOS.SystemState LatinAmericanSpanish, SimplifiedChinese, TraditionalChinese, - BrazilianPortuguese + BrazilianPortuguese, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs index 6627700f5..c650fe036 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs @@ -4,8 +4,7 @@ namespace Ryujinx.HLE.HOS.SystemState { public class SystemStateMgr { - internal static string[] LanguageCodes = new string[] - { + internal static string[] LanguageCodes = { "ja", "en-US", "fr", @@ -23,7 +22,7 @@ namespace Ryujinx.HLE.HOS.SystemState "es-419", "zh-Hans", "zh-Hant", - "pt-BR" + "pt-BR", }; internal long DesiredKeyboardLayout { get; private set; } @@ -46,21 +45,21 @@ namespace Ryujinx.HLE.HOS.SystemState { // TODO: Let user specify fields. DesiredKeyboardLayout = (long)KeyboardLayout.Default; - DeviceNickName = "Ryujinx's Switch"; + DeviceNickName = "Ryujinx's Switch"; } public void SetLanguage(SystemLanguage language) { DesiredSystemLanguage = language; - DesiredLanguageCode = GetLanguageCode((int)DesiredSystemLanguage); + DesiredLanguageCode = GetLanguageCode((int)DesiredSystemLanguage); DesiredTitleLanguage = language switch { SystemLanguage.Taiwanese or SystemLanguage.TraditionalChinese => TitleLanguage.TraditionalChinese, SystemLanguage.Chinese or - SystemLanguage.SimplifiedChinese => TitleLanguage.SimplifiedChinese, - _ => Enum.Parse<TitleLanguage>(Enum.GetName<SystemLanguage>(language)), + SystemLanguage.SimplifiedChinese => TitleLanguage.SimplifiedChinese, + _ => Enum.Parse<TitleLanguage>(Enum.GetName<SystemLanguage>(language)), }; } @@ -76,8 +75,8 @@ namespace Ryujinx.HLE.HOS.SystemState throw new ArgumentOutOfRangeException(nameof(index)); } - long code = 0; - int shift = 0; + long code = 0; + int shift = 0; foreach (char chr in LanguageCodes[index]) { @@ -87,4 +86,4 @@ namespace Ryujinx.HLE.HOS.SystemState return code; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs index c612259b3..5a0b9f8c2 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs @@ -17,6 +17,6 @@ Korean, TraditionalChinese, SimplifiedChinese, - BrazilianPortuguese + BrazilianPortuguese, } } diff --git a/src/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs b/src/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs index 7d7af2085..e25ba7a55 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs @@ -9,29 +9,28 @@ namespace Ryujinx.HLE.HOS.Tamper { class AtmosphereCompiler { - private ulong _exeAddress; - private ulong _heapAddress; - private ulong _aliasAddress; - private ulong _aslrAddress; - private ITamperedProcess _process; + private readonly ulong _exeAddress; + private readonly ulong _heapAddress; + private readonly ulong _aliasAddress; + private readonly ulong _aslrAddress; + private readonly ITamperedProcess _process; public AtmosphereCompiler(ulong exeAddress, ulong heapAddress, ulong aliasAddress, ulong aslrAddress, ITamperedProcess process) { - _exeAddress = exeAddress; - _heapAddress = heapAddress; + _exeAddress = exeAddress; + _heapAddress = heapAddress; _aliasAddress = aliasAddress; - _aslrAddress = aslrAddress; - _process = process; + _aslrAddress = aslrAddress; + _process = process; } public ITamperProgram Compile(string name, IEnumerable<string> rawInstructions) { - string[] addresses = new string[] - { + string[] addresses = { $" Executable address: 0x{_exeAddress:X16}", $" Heap address : 0x{_heapAddress:X16}", $" Alias address : 0x{_aliasAddress:X16}", - $" Aslr address : 0x{_aslrAddress:X16}" + $" Aslr address : 0x{_aslrAddress:X16}", }; Logger.Debug?.Print(LogClass.TamperMachine, $"Compiling Atmosphere cheat {name}...\n{string.Join('\n', addresses)}"); @@ -40,14 +39,14 @@ namespace Ryujinx.HLE.HOS.Tamper { return CompileImpl(name, rawInstructions); } - catch(TamperCompilationException exception) + catch (TamperCompilationException ex) { // Just print the message without the stack trace. - Logger.Error?.Print(LogClass.TamperMachine, exception.Message); + Logger.Error?.Print(LogClass.TamperMachine, ex.Message); } - catch (Exception exception) + catch (Exception ex) { - Logger.Error?.Print(LogClass.TamperMachine, exception.ToString()); + Logger.Error?.Print(LogClass.TamperMachine, ex.ToString()); } Logger.Error?.Print(LogClass.TamperMachine, "There was a problem while compiling the Atmosphere cheat"); @@ -57,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Tamper private ITamperProgram CompileImpl(string name, IEnumerable<string> rawInstructions) { - CompilationContext context = new CompilationContext(_exeAddress, _heapAddress, _aliasAddress, _aslrAddress, _process); + CompilationContext context = new(_exeAddress, _heapAddress, _aliasAddress, _aslrAddress, _process); context.BlockStack.Push(new OperationBlock(null)); // Parse the instructions. @@ -132,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Tamper // Initialize only the registers used. - Value<ulong> zero = new Value<ulong>(0UL); + Value<ulong> zero = new(0UL); int position = 0; foreach (Register register in context.Registers.Values) @@ -143,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Tamper if (context.BlockStack.Count != 1) { - throw new TamperCompilationException($"Reached end of compilation with unmatched conditional(s) or loop(s)"); + throw new TamperCompilationException("Reached end of compilation with unmatched conditional(s) or loop(s)"); } return new AtmosphereProgram(name, _process, context.PressedKeys, new Block(context.CurrentOperations)); diff --git a/src/Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs b/src/Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs index a2aa73a4f..8171d2177 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/AtmosphereProgram.cs @@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Tamper { class AtmosphereProgram : ITamperProgram { - private Parameter<long> _pressedKeys; - private IOperation _entryPoint; + private readonly Parameter<long> _pressedKeys; + private readonly IOperation _entryPoint; public string Name { get; } public bool TampersCodeMemory { get; set; } = false; diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs index b7d46d3a2..c51630d64 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/Arithmetic.cs @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters private const byte Lsh = 3; // lhs << rhs private const byte Rsh = 4; // lhs >> rhs private const byte And = 5; // lhs & rhs - private const byte Or = 6; // lhs | rhs + private const byte Or = 6; // lhs | rhs private const byte Not = 7; // ~lhs (discards right-hand operand) private const byte Xor = 8; // lhs ^ rhs private const byte Mov = 9; // lhs (discards right-hand operand) @@ -73,9 +73,11 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters void Emit(Type operationType, IOperand rhs = null) { - List<IOperand> operandList = new List<IOperand>(); - operandList.Add(destinationRegister); - operandList.Add(leftHandSideRegister); + List<IOperand> operandList = new() + { + destinationRegister, + leftHandSideRegister, + }; if (rhs != null) { @@ -87,16 +89,36 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters switch (operation) { - case Add: Emit(typeof(OpAdd<>), rightHandSideOperand); break; - case Sub: Emit(typeof(OpSub<>), rightHandSideOperand); break; - case Mul: Emit(typeof(OpMul<>), rightHandSideOperand); break; - case Lsh: Emit(typeof(OpLsh<>), rightHandSideOperand); break; - case Rsh: Emit(typeof(OpRsh<>), rightHandSideOperand); break; - case And: Emit(typeof(OpAnd<>), rightHandSideOperand); break; - case Or: Emit(typeof(OpOr<> ), rightHandSideOperand); break; - case Not: Emit(typeof(OpNot<>) ); break; - case Xor: Emit(typeof(OpXor<>), rightHandSideOperand); break; - case Mov: Emit(typeof(OpMov<>) ); break; + case Add: + Emit(typeof(OpAdd<>), rightHandSideOperand); + break; + case Sub: + Emit(typeof(OpSub<>), rightHandSideOperand); + break; + case Mul: + Emit(typeof(OpMul<>), rightHandSideOperand); + break; + case Lsh: + Emit(typeof(OpLsh<>), rightHandSideOperand); + break; + case Rsh: + Emit(typeof(OpRsh<>), rightHandSideOperand); + break; + case And: + Emit(typeof(OpAnd<>), rightHandSideOperand); + break; + case Or: + Emit(typeof(OpOr<>), rightHandSideOperand); + break; + case Not: + Emit(typeof(OpNot<>)); + break; + case Xor: + Emit(typeof(OpXor<>), rightHandSideOperand); + break; + case Mov: + Emit(typeof(OpMov<>)); + break; default: throw new TamperCompilationException($"Invalid arithmetic operation {operation} in Atmosphere cheat"); } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs index a25dddde5..63625a556 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/EndConditionalBlock.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters { const int TerminationTypeIndex = 1; - private const byte End = 0; // True end of the conditional. + private const byte End = 0; // True end of the conditional. private const byte Else = 1; // End of the 'then' block and beginning of 'else' block. public static void Emit(byte[] instruction, CompilationContext context) @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters // If the else operations are already set, then the upper block must not be another end. if (operationsElse != null && codeType == CodeType.EndConditionalBlock) { - throw new TamperCompilationException($"Expected an upper 'if' conditional instead of 'end conditional'"); + throw new TamperCompilationException("Expected an upper 'if' conditional instead of 'end conditional'"); } ICondition condition; @@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters // Create a conditional block with the current operations and nest it in the upper // block of the stack. - IfBlock block = new IfBlock(condition, operations, operationsElse); + IfBlock block = new(condition, operations, operationsElse); context.CurrentOperations.Add(block); } } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs index 479c80ec0..1c389cd7c 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LegacyArithmetic.cs @@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters /// </summary> class LegacyArithmetic { - const int OperationWidthIndex = 1; + const int OperationWidthIndex = 1; const int DestinationRegisterIndex = 3; const int OperationTypeIndex = 4; const int ValueImmediateIndex = 8; @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters Register register = context.GetRegister(instruction[DestinationRegisterIndex]); byte operation = instruction[OperationTypeIndex]; ulong immediate = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, ValueImmediateSize); - Value<ulong> rightHandSideValue = new Value<ulong>(immediate); + Value<ulong> rightHandSideValue = new(immediate); void Emit(Type operationType) { @@ -44,11 +44,21 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters switch (operation) { - case Add: Emit(typeof(OpAdd<>)); break; - case Sub: Emit(typeof(OpSub<>)); break; - case Mul: Emit(typeof(OpMul<>)); break; - case Lsh: Emit(typeof(OpLsh<>)); break; - case Rsh: Emit(typeof(OpRsh<>)); break; + case Add: + Emit(typeof(OpAdd<>)); + break; + case Sub: + Emit(typeof(OpSub<>)); + break; + case Mul: + Emit(typeof(OpMul<>)); + break; + case Lsh: + Emit(typeof(OpLsh<>)); + break; + case Rsh: + Emit(typeof(OpRsh<>)); + break; default: throw new TamperCompilationException($"Invalid arithmetic operation {operation} in Atmosphere cheat"); } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs index e4a86d7b6..16500cf9e 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/LoadRegisterWithConstant.cs @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters Register destinationRegister = context.GetRegister(instruction[RegisterIndex]); ulong immediate = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, ValueImmediateSize); - Value<ulong> sourceValue = new Value<ulong>(immediate); + Value<ulong> sourceValue = new(immediate); context.CurrentOperations.Add(new OpMov<ulong>(destinationRegister, sourceValue)); } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs index 2048a67b3..272cf3d05 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/MemoryConditional.cs @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters int valueSize = operationWidth <= 4 ? ValueImmediateSize4 : ValueImmediateSize8; ulong value = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, valueSize); - Value<ulong> compareToValue = new Value<ulong>(value); + Value<ulong> compareToValue = new(value); return InstructionHelper.CreateCondition(comparison, operationWidth, sourceMemory, compareToValue); } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs index 02f76e224..7af327d3e 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/ResumeProcess.cs @@ -8,7 +8,6 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters class ResumeProcess { // FF1????? - public static void Emit(byte[] instruction, CompilationContext context) { context.CurrentOperations.Add(new OpProcCtrl(context.Process, false)); diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs index 1e399b598..51fc8f35c 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StartEndLoop.cs @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters // Create a loop block with the current operations and nest it in the upper // block of the stack. - ForBlock block = new ForBlock(immediate, iterationRegister, context.CurrentOperations); + ForBlock block = new(immediate, iterationRegister, context.CurrentOperations); context.BlockStack.Pop(); context.CurrentOperations.Add(block); } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs index 933646bd0..a2a62015a 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToAddress.cs @@ -33,7 +33,7 @@ int valueImmediateSize = operationWidth <= 4 ? ValueImmediateSize8 : ValueImmediateSize16; ulong valueImmediate = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, valueImmediateSize); - Value<ulong> storeValue = new Value<ulong>(valueImmediate); + Value<ulong> storeValue = new(valueImmediate); InstructionHelper.EmitMov(operationWidth, context, dstMem, storeValue); } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs index 5f0369693..d53b7a26f 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeEmitters/StoreConstantToMemory.cs @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters byte incrementAddressRegister = instruction[IncrementAddressRegisterIndex]; byte useOffsetRegister = instruction[UseOffsetRegisterIndex]; ulong immediate = InstructionHelper.GetImmediate(instruction, ValueImmediateIndex, ValueImmediateSize); - Value<ulong> storeValue = new Value<ulong>(immediate); + Value<ulong> storeValue = new(immediate); Pointer destinationMemory; diff --git a/src/Ryujinx.HLE/HOS/Tamper/CodeType.cs b/src/Ryujinx.HLE/HOS/Tamper/CodeType.cs index 7c4f22860..d65f8a2f6 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CodeType.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CodeType.cs @@ -105,6 +105,6 @@ /// <summary> /// Code type 0xFFF writes a debug log. /// </summary> - DebugLog = 0xFFF + DebugLog = 0xFFF, } } diff --git a/src/Ryujinx.HLE/HOS/Tamper/Comparison.cs b/src/Ryujinx.HLE/HOS/Tamper/Comparison.cs index cd162b1ce..dc0f02649 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Comparison.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Comparison.cs @@ -10,6 +10,6 @@ Less = 3, LessOrEqual = 4, Equal = 5, - NotEqual = 6 + NotEqual = 6, } } diff --git a/src/Ryujinx.HLE/HOS/Tamper/CompilationContext.cs b/src/Ryujinx.HLE/HOS/Tamper/CompilationContext.cs index 45a47f445..922e568a3 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/CompilationContext.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/CompilationContext.cs @@ -21,16 +21,16 @@ namespace Ryujinx.HLE.HOS.Tamper public CompilationContext(ulong exeAddress, ulong heapAddress, ulong aliasAddress, ulong aslrAddress, ITamperedProcess process) { - Process = process; - PressedKeys = new Parameter<long>(0); - BlockStack = new Stack<OperationBlock>(); - Registers = new Dictionary<byte, Register>(); - SavedRegisters = new Dictionary<byte, Register>(); + Process = process; + PressedKeys = new Parameter<long>(0); + BlockStack = new Stack<OperationBlock>(); + Registers = new Dictionary<byte, Register>(); + SavedRegisters = new Dictionary<byte, Register>(); StaticRegisters = new Dictionary<byte, Register>(); - ExeAddress = exeAddress; - HeapAddress = heapAddress; - AliasAddress = aliasAddress; - AslrAddress = aslrAddress; + ExeAddress = exeAddress; + HeapAddress = heapAddress; + AliasAddress = aliasAddress; + AslrAddress = aslrAddress; } public Register GetRegister(byte index) diff --git a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs index ad5bd2232..529ed25b6 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondEQ.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions { class CondEQ<T> : ICondition where T : unmanaged { - private IOperand _lhs; - private IOperand _rhs; + private readonly IOperand _lhs; + private readonly IOperand _rhs; public CondEQ(IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs index d9ad6d81d..94877c2a6 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGE.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions { class CondGE<T> : ICondition where T : unmanaged { - private IOperand _lhs; - private IOperand _rhs; + private readonly IOperand _lhs; + private readonly IOperand _rhs; public CondGE(IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs index 262457da0..350688164 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondGT.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions { class CondGT<T> : ICondition where T : unmanaged { - private IOperand _lhs; - private IOperand _rhs; + private readonly IOperand _lhs; + private readonly IOperand _rhs; public CondGT(IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs index fd488bc1a..dd9cf70cc 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLE.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions { class CondLE<T> : ICondition where T : unmanaged { - private IOperand _lhs; - private IOperand _rhs; + private readonly IOperand _lhs; + private readonly IOperand _rhs; public CondLE(IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs index 744eb5dc7..0c85f5e47 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondLT.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions { class CondLT<T> : ICondition where T : unmanaged { - private IOperand _lhs; - private IOperand _rhs; + private readonly IOperand _lhs; + private readonly IOperand _rhs; public CondLT(IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs index 2709ad925..b649eccee 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Conditions/CondNE.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions { class CondNE<T> : ICondition where T : unmanaged { - private IOperand _lhs; - private IOperand _rhs; + private readonly IOperand _lhs; + private readonly IOperand _rhs; public CondNE(IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs b/src/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs index 8d75a0e18..6c72eb5c8 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Conditions/InputMask.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions { class InputMask : ICondition { - private long _mask; - private Parameter<long> _input; + private readonly long _mask; + private readonly Parameter<long> _input; public InputMask(long mask, Parameter<long> input) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs b/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs index a31c055fa..76ffefb66 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/InstructionHelper.cs @@ -32,33 +32,28 @@ namespace Ryujinx.HLE.HOS.Tamper return (ICondition)InstructionHelper.Create(conditionType, width, lhs, rhs); } - switch (comparison) + return comparison switch { - case Comparison.Greater : return Create(typeof(CondGT<>)); - case Comparison.GreaterOrEqual: return Create(typeof(CondGE<>)); - case Comparison.Less : return Create(typeof(CondLT<>)); - case Comparison.LessOrEqual : return Create(typeof(CondLE<>)); - case Comparison.Equal : return Create(typeof(CondEQ<>)); - case Comparison.NotEqual : return Create(typeof(CondNE<>)); - default: - throw new TamperCompilationException($"Invalid comparison {comparison} in Atmosphere cheat"); - } + Comparison.Greater => Create(typeof(CondGT<>)), + Comparison.GreaterOrEqual => Create(typeof(CondGE<>)), + Comparison.Less => Create(typeof(CondLT<>)), + Comparison.LessOrEqual => Create(typeof(CondLE<>)), + Comparison.Equal => Create(typeof(CondEQ<>)), + Comparison.NotEqual => Create(typeof(CondNE<>)), + _ => throw new TamperCompilationException($"Invalid comparison {comparison} in Atmosphere cheat"), + }; } public static Object Create(Type instruction, byte width, params Object[] operands) { - Type realType; - - switch (width) + Type realType = width switch { - case 1: realType = instruction.MakeGenericType(typeof(byte)); break; - case 2: realType = instruction.MakeGenericType(typeof(ushort)); break; - case 4: realType = instruction.MakeGenericType(typeof(uint)); break; - case 8: realType = instruction.MakeGenericType(typeof(ulong)); break; - default: - throw new TamperCompilationException($"Invalid instruction width {width} in Atmosphere cheat"); - } - + 1 => instruction.MakeGenericType(typeof(byte)), + 2 => instruction.MakeGenericType(typeof(ushort)), + 4 => instruction.MakeGenericType(typeof(uint)), + 8 => instruction.MakeGenericType(typeof(ulong)), + _ => throw new TamperCompilationException($"Invalid instruction width {width} in Atmosphere cheat"), + }; return Activator.CreateInstance(realType, operands); } diff --git a/src/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs b/src/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs index 1260ed9ac..52e12bfe5 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/MemoryHelper.cs @@ -7,23 +7,18 @@ namespace Ryujinx.HLE.HOS.Tamper { public static ulong GetAddressShift(MemoryRegion source, CompilationContext context) { - switch (source) + return source switch { - case MemoryRegion.NSO: - // Memory address is relative to the code start. - return context.ExeAddress; - case MemoryRegion.Heap: - // Memory address is relative to the heap. - return context.HeapAddress; - case MemoryRegion.Alias: - // Memory address is relative to the alias region. - return context.AliasAddress; - case MemoryRegion.Asrl: - // Memory address is relative to the asrl region, which matches the code region. - return context.AslrAddress; - default: - throw new TamperCompilationException($"Invalid memory source {source} in Atmosphere cheat"); - } + // Memory address is relative to the code start. + MemoryRegion.NSO => context.ExeAddress, + // Memory address is relative to the heap. + MemoryRegion.Heap => context.HeapAddress, + // Memory address is relative to the alias region. + MemoryRegion.Alias => context.AliasAddress, + // Memory address is relative to the asrl region, which matches the code region. + MemoryRegion.Asrl => context.AslrAddress, + _ => throw new TamperCompilationException($"Invalid memory source {source} in Atmosphere cheat"), + }; } private static void EmitAdd(Value<ulong> finalValue, IOperand firstOperand, IOperand secondOperand, CompilationContext context) @@ -33,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Tamper public static Pointer EmitPointer(ulong addressImmediate, CompilationContext context) { - Value<ulong> addressImmediateValue = new Value<ulong>(addressImmediate); + Value<ulong> addressImmediateValue = new(addressImmediate); return new Pointer(addressImmediateValue, context.Process); } @@ -45,8 +40,8 @@ namespace Ryujinx.HLE.HOS.Tamper public static Pointer EmitPointer(Register addressRegister, ulong offsetImmediate, CompilationContext context) { - Value<ulong> offsetImmediateValue = new Value<ulong>(offsetImmediate); - Value<ulong> finalAddressValue = new Value<ulong>(0); + Value<ulong> offsetImmediateValue = new(offsetImmediate); + Value<ulong> finalAddressValue = new(0); EmitAdd(finalAddressValue, addressRegister, offsetImmediateValue, context); return new Pointer(finalAddressValue, context.Process); @@ -54,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Tamper public static Pointer EmitPointer(Register addressRegister, Register offsetRegister, CompilationContext context) { - Value<ulong> finalAddressValue = new Value<ulong>(0); + Value<ulong> finalAddressValue = new(0); EmitAdd(finalAddressValue, addressRegister, offsetRegister, context); return new Pointer(finalAddressValue, context.Process); @@ -62,10 +57,10 @@ namespace Ryujinx.HLE.HOS.Tamper public static Pointer EmitPointer(Register addressRegister, Register offsetRegister, ulong offsetImmediate, CompilationContext context) { - Value<ulong> offsetImmediateValue = new Value<ulong>(offsetImmediate); - Value<ulong> finalOffsetValue = new Value<ulong>(0); + Value<ulong> offsetImmediateValue = new(offsetImmediate); + Value<ulong> finalOffsetValue = new(0); EmitAdd(finalOffsetValue, offsetRegister, offsetImmediateValue, context); - Value<ulong> finalAddressValue = new Value<ulong>(0); + Value<ulong> finalAddressValue = new(0); EmitAdd(finalAddressValue, addressRegister, finalOffsetValue, context); return new Pointer(finalAddressValue, context.Process); diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/Block.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/Block.cs index d81daa903..6d4b16373 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/Block.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/Block.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class Block : IOperation { - private IEnumerable<IOperation> _operations; + private readonly IEnumerable<IOperation> _operations; public Block(IEnumerable<IOperation> operations) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs index ef95fa2bf..1e8cafa84 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/ForBlock.cs @@ -4,9 +4,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class ForBlock : IOperation { - private ulong _count; - private Register _register; - private IEnumerable<IOperation> _operations; + private readonly ulong _count; + private readonly Register _register; + private readonly IEnumerable<IOperation> _operations; public ForBlock(ulong count, Register register, IEnumerable<IOperation> operations) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs index b7c5684ef..a6b31f252 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/IfBlock.cs @@ -5,9 +5,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class IfBlock : IOperation { - private ICondition _condition; - private IEnumerable<IOperation> _operationsThen; - private IEnumerable<IOperation> _operationsElse; + private readonly ICondition _condition; + private readonly IEnumerable<IOperation> _operationsThen; + private readonly IEnumerable<IOperation> _operationsElse; public IfBlock(ICondition condition, IEnumerable<IOperation> operationsThen, IEnumerable<IOperation> operationsElse) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs index 214518d7d..855245e34 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAdd.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpAdd<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpAdd(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs index 366a82b07..7d1fa10b9 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpAnd.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpAnd<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpAnd(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs index 49f8b41e1..4017e5f75 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLog.cs @@ -4,8 +4,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpLog<T> : IOperation where T : unmanaged { - int _logId; - IOperand _source; + readonly int _logId; + readonly IOperand _source; public OpLog(int logId, IOperand source) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs index 34e7c81a1..6c846425f 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpLsh.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpLsh<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpLsh(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs index 5fad38f91..af82f18e0 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMov.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpMov<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _source; + readonly IOperand _destination; + readonly IOperand _source; public OpMov(IOperand destination, IOperand source) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs index 5aa0e34ef..a1b080f00 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpMul.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpMul<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpMul(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs index 8a97c3fe8..034e22008 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpNot.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpNot<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _source; + readonly IOperand _destination; + readonly IOperand _source; public OpNot(IOperand destination, IOperand source) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs index d074de1c8..0afdc3f4b 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpOr.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpOr<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpOr(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs index 1b89f4507..5de225a19 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpProcCtrl.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpProcCtrl : IOperation { - private ITamperedProcess _process; - private bool _pause; + private readonly ITamperedProcess _process; + private readonly bool _pause; public OpProcCtrl(ITamperedProcess process, bool pause) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs index b08dd957c..e7e0f870e 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpRsh.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpRsh<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpRsh(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs index b9c67d040..d860d66fd 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpSub.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpSub<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpSub(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs index 3bbb76a1b..07ba6b335 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Operations/OpXor.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS.Tamper.Operations { class OpXor<T> : IOperation where T : unmanaged { - IOperand _destination; - IOperand _lhs; - IOperand _rhs; + readonly IOperand _destination; + readonly IOperand _lhs; + readonly IOperand _rhs; public OpXor(IOperand destination, IOperand lhs, IOperand rhs) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Pointer.cs b/src/Ryujinx.HLE/HOS/Tamper/Pointer.cs index 22acf4d56..c961e1a7d 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Pointer.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Pointer.cs @@ -6,8 +6,8 @@ namespace Ryujinx.HLE.HOS.Tamper { class Pointer : IOperand { - private IOperand _position; - private ITamperedProcess _process; + private readonly IOperand _position; + private readonly ITamperedProcess _process; public Pointer(IOperand position, ITamperedProcess process) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/Register.cs b/src/Ryujinx.HLE/HOS/Tamper/Register.cs index 01af20dea..cce13ee69 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Register.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Register.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Tamper class Register : IOperand { private ulong _register = 0; - private string _alias; + private readonly string _alias; public Register(string alias) { diff --git a/src/Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs b/src/Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs index be51264a2..c8768e88a 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/TamperedKProcess.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Tamper { class TamperedKProcess : ITamperedProcess { - private KProcess _process; + private readonly KProcess _process; public ProcessState State => _process.State; @@ -65,4 +65,4 @@ namespace Ryujinx.HLE.HOS.Tamper Logger.Warning?.Print(LogClass.TamperMachine, "Process resuming is not supported!"); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/HOS/Tamper/Value.cs b/src/Ryujinx.HLE/HOS/Tamper/Value.cs index 865f8e046..436fc13d3 100644 --- a/src/Ryujinx.HLE/HOS/Tamper/Value.cs +++ b/src/Ryujinx.HLE/HOS/Tamper/Value.cs @@ -2,11 +2,11 @@ using Ryujinx.HLE.HOS.Tamper.Operations; namespace Ryujinx.HLE.HOS.Tamper { - class Value<P> : IOperand where P : unmanaged + class Value<TP> : IOperand where TP : unmanaged { - private P _value; + private TP _value; - public Value(P value) + public Value(TP value) { _value = value; } @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Tamper public void Set<T>(T value) where T : unmanaged { - _value = (P)(dynamic)value; + _value = (TP)(dynamic)value; } } } diff --git a/src/Ryujinx.HLE/HOS/TamperMachine.cs b/src/Ryujinx.HLE/HOS/TamperMachine.cs index 596fc3ce8..f234e540e 100644 --- a/src/Ryujinx.HLE/HOS/TamperMachine.cs +++ b/src/Ryujinx.HLE/HOS/TamperMachine.cs @@ -17,16 +17,18 @@ namespace Ryujinx.HLE.HOS private const int TamperMachineSleepMs = 1000 / 12; private Thread _tamperThread = null; - private ConcurrentQueue<ITamperProgram> _programs = new ConcurrentQueue<ITamperProgram>(); + private readonly ConcurrentQueue<ITamperProgram> _programs = new(); private long _pressedKeys = 0; - private Dictionary<string, ITamperProgram> _programDictionary = new Dictionary<string, ITamperProgram>(); + private readonly Dictionary<string, ITamperProgram> _programDictionary = new(); private void Activate() { if (_tamperThread == null || !_tamperThread.IsAlive) { - _tamperThread = new Thread(this.TamperRunner); - _tamperThread.Name = "HLE.TamperMachine"; + _tamperThread = new Thread(this.TamperRunner) + { + Name = "HLE.TamperMachine", + }; _tamperThread.Start(); } } @@ -39,7 +41,7 @@ namespace Ryujinx.HLE.HOS } ITamperedProcess tamperedProcess = new TamperedKProcess(info.Process); - AtmosphereCompiler compiler = new AtmosphereCompiler(exeAddress, info.HeapAddress, info.AliasAddress, info.AslrAddress, tamperedProcess); + AtmosphereCompiler compiler = new(exeAddress, info.HeapAddress, info.AliasAddress, info.AslrAddress, tamperedProcess); ITamperProgram program = compiler.Compile(name, rawInstructions); if (program != null) @@ -53,7 +55,7 @@ namespace Ryujinx.HLE.HOS Activate(); } - private bool CanInstallOnPid(ulong pid) + private static bool CanInstallOnPid(ulong pid) { // Do not allow tampering of kernel processes. if (pid < KernelConstants.InitialProcessId) @@ -82,7 +84,7 @@ namespace Ryujinx.HLE.HOS } } - private bool IsProcessValid(ITamperedProcess process) + private static bool IsProcessValid(ITamperedProcess process) { return process.State != ProcessState.Crashed && process.State != ProcessState.Exiting && process.State != ProcessState.Exited; } diff --git a/src/Ryujinx.HLE/HOS/UserChannelPersistence.cs b/src/Ryujinx.HLE/HOS/UserChannelPersistence.cs index 4b041ce88..7c7448ba7 100644 --- a/src/Ryujinx.HLE/HOS/UserChannelPersistence.cs +++ b/src/Ryujinx.HLE/HOS/UserChannelPersistence.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS { public class UserChannelPersistence { - private Stack<byte[]> _userChannelStorages; + private readonly Stack<byte[]> _userChannelStorages; public int PreviousIndex { get; private set; } public int Index { get; private set; } public ProgramSpecifyKind Kind { get; private set; } diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs index f489e85af..4cc1a9fea 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs @@ -8,8 +8,8 @@ namespace Ryujinx.HLE.Loaders.Elf public ElfDynamic(ElfDynamicTag tag, long value) { - Tag = tag; + Tag = tag; Value = value; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs index eb37d612d..6505e17dd 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs @@ -3,73 +3,74 @@ using System.Diagnostics.CodeAnalysis; namespace Ryujinx.HLE.Loaders.Elf { [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum ElfDynamicTag { - DT_NULL = 0, - DT_NEEDED = 1, - DT_PLTRELSZ = 2, - DT_PLTGOT = 3, - DT_HASH = 4, - DT_STRTAB = 5, - DT_SYMTAB = 6, - DT_RELA = 7, - DT_RELASZ = 8, - DT_RELAENT = 9, - DT_STRSZ = 10, - DT_SYMENT = 11, - DT_INIT = 12, - DT_FINI = 13, - DT_SONAME = 14, - DT_RPATH = 15, - DT_SYMBOLIC = 16, - DT_REL = 17, - DT_RELSZ = 18, - DT_RELENT = 19, - DT_PLTREL = 20, - DT_DEBUG = 21, - DT_TEXTREL = 22, - DT_JMPREL = 23, - DT_BIND_NOW = 24, - DT_INIT_ARRAY = 25, - DT_FINI_ARRAY = 26, - DT_INIT_ARRAYSZ = 27, - DT_FINI_ARRAYSZ = 28, - DT_RUNPATH = 29, - DT_FLAGS = 30, - DT_ENCODING = 32, - DT_PREINIT_ARRAY = 32, + DT_NULL = 0, + DT_NEEDED = 1, + DT_PLTRELSZ = 2, + DT_PLTGOT = 3, + DT_HASH = 4, + DT_STRTAB = 5, + DT_SYMTAB = 6, + DT_RELA = 7, + DT_RELASZ = 8, + DT_RELAENT = 9, + DT_STRSZ = 10, + DT_SYMENT = 11, + DT_INIT = 12, + DT_FINI = 13, + DT_SONAME = 14, + DT_RPATH = 15, + DT_SYMBOLIC = 16, + DT_REL = 17, + DT_RELSZ = 18, + DT_RELENT = 19, + DT_PLTREL = 20, + DT_DEBUG = 21, + DT_TEXTREL = 22, + DT_JMPREL = 23, + DT_BIND_NOW = 24, + DT_INIT_ARRAY = 25, + DT_FINI_ARRAY = 26, + DT_INIT_ARRAYSZ = 27, + DT_FINI_ARRAYSZ = 28, + DT_RUNPATH = 29, + DT_FLAGS = 30, + DT_ENCODING = 32, + DT_PREINIT_ARRAY = 32, DT_PREINIT_ARRAYSZ = 33, - DT_GNU_PRELINKED = 0x6ffffdf5, - DT_GNU_CONFLICTSZ = 0x6ffffdf6, - DT_GNU_LIBLISTSZ = 0x6ffffdf7, - DT_CHECKSUM = 0x6ffffdf8, - DT_PLTPADSZ = 0x6ffffdf9, - DT_MOVEENT = 0x6ffffdfa, - DT_MOVESZ = 0x6ffffdfb, - DT_FEATURE_1 = 0x6ffffdfc, - DT_POSFLAG_1 = 0x6ffffdfd, - DT_SYMINSZ = 0x6ffffdfe, - DT_SYMINENT = 0x6ffffdff, - DT_GNU_HASH = 0x6ffffef5, - DT_TLSDESC_PLT = 0x6ffffef6, - DT_TLSDESC_GOT = 0x6ffffef7, - DT_GNU_CONFLICT = 0x6ffffef8, - DT_GNU_LIBLIST = 0x6ffffef9, - DT_CONFIG = 0x6ffffefa, - DT_DEPAUDIT = 0x6ffffefb, - DT_AUDIT = 0x6ffffefc, - DT_PLTPAD = 0x6ffffefd, - DT_MOVETAB = 0x6ffffefe, - DT_SYMINFO = 0x6ffffeff, - DT_VERSYM = 0x6ffffff0, - DT_RELACOUNT = 0x6ffffff9, - DT_RELCOUNT = 0x6ffffffa, - DT_FLAGS_1 = 0x6ffffffb, - DT_VERDEF = 0x6ffffffc, - DT_VERDEFNUM = 0x6ffffffd, - DT_VERNEED = 0x6ffffffe, - DT_VERNEEDNUM = 0x6fffffff, - DT_AUXILIARY = 0x7ffffffd, - DT_FILTER = 0x7fffffff + DT_GNU_PRELINKED = 0x6ffffdf5, + DT_GNU_CONFLICTSZ = 0x6ffffdf6, + DT_GNU_LIBLISTSZ = 0x6ffffdf7, + DT_CHECKSUM = 0x6ffffdf8, + DT_PLTPADSZ = 0x6ffffdf9, + DT_MOVEENT = 0x6ffffdfa, + DT_MOVESZ = 0x6ffffdfb, + DT_FEATURE_1 = 0x6ffffdfc, + DT_POSFLAG_1 = 0x6ffffdfd, + DT_SYMINSZ = 0x6ffffdfe, + DT_SYMINENT = 0x6ffffdff, + DT_GNU_HASH = 0x6ffffef5, + DT_TLSDESC_PLT = 0x6ffffef6, + DT_TLSDESC_GOT = 0x6ffffef7, + DT_GNU_CONFLICT = 0x6ffffef8, + DT_GNU_LIBLIST = 0x6ffffef9, + DT_CONFIG = 0x6ffffefa, + DT_DEPAUDIT = 0x6ffffefb, + DT_AUDIT = 0x6ffffefc, + DT_PLTPAD = 0x6ffffefd, + DT_MOVETAB = 0x6ffffefe, + DT_SYMINFO = 0x6ffffeff, + DT_VERSYM = 0x6ffffff0, + DT_RELACOUNT = 0x6ffffff9, + DT_RELCOUNT = 0x6ffffffa, + DT_FLAGS_1 = 0x6ffffffb, + DT_VERDEF = 0x6ffffffc, + DT_VERDEFNUM = 0x6ffffffd, + DT_VERNEED = 0x6ffffffe, + DT_VERNEEDNUM = 0x6fffffff, + DT_AUXILIARY = 0x7ffffffd, + DT_FILTER = 0x7fffffff, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs index 1cfc0bdcc..1ed61b2cb 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs @@ -4,32 +4,32 @@ namespace Ryujinx.HLE.Loaders.Elf { public string Name { get; private set; } - public ElfSymbolType Type { get; private set; } - public ElfSymbolBinding Binding { get; private set; } + public ElfSymbolType Type { get; private set; } + public ElfSymbolBinding Binding { get; private set; } public ElfSymbolVisibility Visibility { get; private set; } - public bool IsFuncOrObject => Type == ElfSymbolType.SttFunc || Type == ElfSymbolType.SttObject; - public bool IsGlobalOrWeak => Binding == ElfSymbolBinding.StbGlobal || Binding == ElfSymbolBinding.StbWeak; + public readonly bool IsFuncOrObject => Type == ElfSymbolType.SttFunc || Type == ElfSymbolType.SttObject; + public readonly bool IsGlobalOrWeak => Binding == ElfSymbolBinding.StbGlobal || Binding == ElfSymbolBinding.StbWeak; - public int ShIdx { get; private set; } + public int ShIdx { get; private set; } public ulong Value { get; private set; } - public ulong Size { get; private set; } + public ulong Size { get; private set; } public ElfSymbol( string name, - int info, - int other, - int shIdx, - ulong value, - ulong size) + int info, + int other, + int shIdx, + ulong value, + ulong size) { - Name = name; - Type = (ElfSymbolType)(info & 0xf); - Binding = (ElfSymbolBinding)(info >> 4); + Name = name; + Type = (ElfSymbolType)(info & 0xf); + Binding = (ElfSymbolBinding)(info >> 4); Visibility = (ElfSymbolVisibility)other; - ShIdx = shIdx; - Value = value; - Size = size; + ShIdx = shIdx; + Value = value; + Size = size; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs index 2f84796b8..945b0c8b2 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol32.cs @@ -2,13 +2,13 @@ { struct ElfSymbol32 { -#pragma warning disable CS0649 - public uint NameOffset; - public uint ValueAddress; - public uint Size; - public byte Info; - public byte Other; +#pragma warning disable CS0649 // Field is never assigned to + public uint NameOffset; + public uint ValueAddress; + public uint Size; + public byte Info; + public byte Other; public ushort SectionIndex; #pragma warning restore CS0649 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs index 665e65b0c..3a73ca4ed 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbol64.cs @@ -2,13 +2,13 @@ { struct ElfSymbol64 { -#pragma warning disable CS0649 - public uint NameOffset; - public byte Info; - public byte Other; +#pragma warning disable CS0649 // Field is never assigned to + public uint NameOffset; + public byte Info; + public byte Other; public ushort SectionIndex; - public ulong ValueAddress; - public ulong Size; + public ulong ValueAddress; + public ulong Size; #pragma warning restore CS0649 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs index 92274fded..cf504ae2d 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs @@ -2,8 +2,8 @@ namespace Ryujinx.HLE.Loaders.Elf { enum ElfSymbolBinding { - StbLocal = 0, + StbLocal = 0, StbGlobal = 1, - StbWeak = 2 + StbWeak = 2, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs index 4110d4c3e..9dc414360 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.Loaders.Elf { enum ElfSymbolType { - SttNoType = 0, - SttObject = 1, - SttFunc = 2, + SttNoType = 0, + SttObject = 1, + SttFunc = 2, SttSection = 3, - SttFile = 4, - SttCommon = 5, - SttTls = 6 + SttFile = 4, + SttCommon = 5, + SttTls = 6, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs index f026fca89..6305ab1ca 100644 --- a/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs +++ b/src/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs @@ -2,9 +2,9 @@ namespace Ryujinx.HLE.Loaders.Elf { enum ElfSymbolVisibility { - StvDefault = 0, - StvInternal = 1, - StvHidden = 2, - StvProtected = 3 + StvDefault = 0, + StvInternal = 1, + StvHidden = 2, + StvProtected = 3, } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/IExecutable.cs index 27479efe1..06f6d9694 100644 --- a/src/Ryujinx.HLE/Loaders/Executables/IExecutable.cs +++ b/src/Ryujinx.HLE/Loaders/Executables/IExecutable.cs @@ -6,13 +6,13 @@ namespace Ryujinx.HLE.Loaders.Executables { byte[] Program { get; } Span<byte> Text { get; } - Span<byte> Ro { get; } + Span<byte> Ro { get; } Span<byte> Data { get; } uint TextOffset { get; } - uint RoOffset { get; } + uint RoOffset { get; } uint DataOffset { get; } - uint BssOffset { get; } - uint BssSize { get; } + uint BssOffset { get; } + uint BssSize { get; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs index ad2b681cd..83380ff45 100644 --- a/src/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs +++ b/src/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs @@ -9,53 +9,53 @@ namespace Ryujinx.HLE.Loaders.Executables { public byte[] Program { get; } public Span<byte> Text => Program.AsSpan((int)TextOffset, (int)TextSize); - public Span<byte> Ro => Program.AsSpan((int)RoOffset, (int)RoSize); + public Span<byte> Ro => Program.AsSpan((int)RoOffset, (int)RoSize); public Span<byte> Data => Program.AsSpan((int)DataOffset, (int)DataSize); public uint TextOffset { get; } - public uint RoOffset { get; } + public uint RoOffset { get; } public uint DataOffset { get; } - public uint BssOffset { get; } + public uint BssOffset { get; } public uint TextSize { get; } - public uint RoSize { get; } + public uint RoSize { get; } public uint DataSize { get; } - public uint BssSize { get; } + public uint BssSize { get; } - public uint[] Capabilities { get; } - public bool UsesSecureMemory { get; } + public uint[] Capabilities { get; } + public bool UsesSecureMemory { get; } public bool Is64BitAddressSpace { get; } - public bool Is64Bit { get; } - public ulong ProgramId { get; } - public byte Priority { get; } - public int StackSize { get; } - public byte IdealCoreId { get; } - public int Version { get; } - public string Name { get; } + public bool Is64Bit { get; } + public ulong ProgramId { get; } + public byte Priority { get; } + public int StackSize { get; } + public byte IdealCoreId { get; } + public int Version { get; } + public string Name { get; } public KipExecutable(in SharedRef<IStorage> inStorage) { - KipReader reader = new KipReader(); + KipReader reader = new(); reader.Initialize(in inStorage).ThrowIfFailure(); TextOffset = (uint)reader.Segments[0].MemoryOffset; - RoOffset = (uint)reader.Segments[1].MemoryOffset; + RoOffset = (uint)reader.Segments[1].MemoryOffset; DataOffset = (uint)reader.Segments[2].MemoryOffset; - BssOffset = (uint)reader.Segments[3].MemoryOffset; - BssSize = (uint)reader.Segments[3].Size; + BssOffset = (uint)reader.Segments[3].MemoryOffset; + BssSize = (uint)reader.Segments[3].Size; StackSize = reader.StackSize; - UsesSecureMemory = reader.UsesSecureMemory; + UsesSecureMemory = reader.UsesSecureMemory; Is64BitAddressSpace = reader.Is64BitAddressSpace; - Is64Bit = reader.Is64Bit; + Is64Bit = reader.Is64Bit; - ProgramId = reader.ProgramId; - Priority = reader.Priority; + ProgramId = reader.ProgramId; + Priority = reader.Priority; IdealCoreId = reader.IdealCoreId; - Version = reader.Version; - Name = reader.Name.ToString(); + Version = reader.Version; + Name = reader.Name.ToString(); Capabilities = new uint[32]; @@ -68,7 +68,7 @@ namespace Ryujinx.HLE.Loaders.Executables Program = new byte[DataOffset + uncompressedSize]; TextSize = DecompressSection(reader, KipReader.SegmentType.Text, TextOffset, Program); - RoSize = DecompressSection(reader, KipReader.SegmentType.Ro, RoOffset, Program); + RoSize = DecompressSection(reader, KipReader.SegmentType.Ro, RoOffset, Program); DataSize = DecompressSection(reader, KipReader.SegmentType.Data, DataOffset, Program); } @@ -83,4 +83,4 @@ namespace Ryujinx.HLE.Loaders.Executables return (uint)uncompressedSize; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Executables/NroExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/NroExecutable.cs index 621ab43b3..6b5a8c831 100644 --- a/src/Ryujinx.HLE/Loaders/Executables/NroExecutable.cs +++ b/src/Ryujinx.HLE/Loaders/Executables/NroExecutable.cs @@ -8,31 +8,31 @@ namespace Ryujinx.HLE.Loaders.Executables { public byte[] Program { get; } public Span<byte> Text => Program.AsSpan((int)TextOffset, (int)Header.NroSegments[0].Size); - public Span<byte> Ro => Program.AsSpan((int)RoOffset, (int)Header.NroSegments[1].Size); + public Span<byte> Ro => Program.AsSpan((int)RoOffset, (int)Header.NroSegments[1].Size); public Span<byte> Data => Program.AsSpan((int)DataOffset, (int)Header.NroSegments[2].Size); public uint TextOffset => Header.NroSegments[0].FileOffset; - public uint RoOffset => Header.NroSegments[1].FileOffset; + public uint RoOffset => Header.NroSegments[1].FileOffset; public uint DataOffset => Header.NroSegments[2].FileOffset; - public uint BssOffset => DataOffset + (uint)Data.Length; - public uint BssSize => Header.BssSize; + public uint BssOffset => DataOffset + (uint)Data.Length; + public uint BssSize => Header.BssSize; public uint Mod0Offset => (uint)Start.Mod0Offset; - public uint FileSize => Header.Size; + public uint FileSize => Header.Size; public ulong SourceAddress { get; private set; } - public ulong BssAddress { get; private set; } + public ulong BssAddress { get; private set; } public NroExecutable(IStorage inStorage, ulong sourceAddress = 0, ulong bssAddress = 0) : base(inStorage) { Program = new byte[FileSize]; SourceAddress = sourceAddress; - BssAddress = bssAddress; + BssAddress = bssAddress; OpenNroSegment(NroSegmentType.Text, false).Read(0, Text); - OpenNroSegment(NroSegmentType.Ro , false).Read(0, Ro); + OpenNroSegment(NroSegmentType.Ro, false).Read(0, Ro); OpenNroSegment(NroSegmentType.Data, false).Read(0, Data); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs index d695449be..83ad5d7e8 100644 --- a/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs +++ b/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs @@ -13,20 +13,20 @@ namespace Ryujinx.HLE.Loaders.Executables { public byte[] Program { get; } public Span<byte> Text => Program.AsSpan((int)TextOffset, (int)TextSize); - public Span<byte> Ro => Program.AsSpan((int)RoOffset, (int)RoSize); + public Span<byte> Ro => Program.AsSpan((int)RoOffset, (int)RoSize); public Span<byte> Data => Program.AsSpan((int)DataOffset, (int)DataSize); public uint TextOffset { get; } - public uint RoOffset { get; } + public uint RoOffset { get; } public uint DataOffset { get; } public uint BssOffset => DataOffset + (uint)Data.Length; public uint TextSize { get; } - public uint RoSize { get; } + public uint RoSize { get; } public uint DataSize { get; } - public uint BssSize { get; } + public uint BssSize { get; } - public string Name; + public string Name; public Array32<byte> BuildId; [GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] @@ -38,24 +38,24 @@ namespace Ryujinx.HLE.Loaders.Executables public NsoExecutable(IStorage inStorage, string name = null) { - NsoReader reader = new NsoReader(); + NsoReader reader = new(); reader.Initialize(inStorage.AsFile(OpenMode.Read)).ThrowIfFailure(); TextOffset = reader.Header.Segments[0].MemoryOffset; - RoOffset = reader.Header.Segments[1].MemoryOffset; + RoOffset = reader.Header.Segments[1].MemoryOffset; DataOffset = reader.Header.Segments[2].MemoryOffset; - BssSize = reader.Header.BssSize; + BssSize = reader.Header.BssSize; reader.GetSegmentSize(NsoReader.SegmentType.Data, out uint uncompressedSize).ThrowIfFailure(); Program = new byte[DataOffset + uncompressedSize]; TextSize = DecompressSection(reader, NsoReader.SegmentType.Text, TextOffset); - RoSize = DecompressSection(reader, NsoReader.SegmentType.Ro, RoOffset); + RoSize = DecompressSection(reader, NsoReader.SegmentType.Ro, RoOffset); DataSize = DecompressSection(reader, NsoReader.SegmentType.Data, DataOffset); - Name = name; + Name = name; BuildId = reader.Header.ModuleId; PrintRoSectionInfo(); @@ -74,12 +74,12 @@ namespace Ryujinx.HLE.Loaders.Executables private void PrintRoSectionInfo() { - string rawTextBuffer = Encoding.ASCII.GetString(Ro); - StringBuilder stringBuilder = new StringBuilder(); + string rawTextBuffer = Encoding.ASCII.GetString(Ro); + StringBuilder stringBuilder = new(); string modulePath = null; - if (BitConverter.ToInt32(Ro.Slice(0, 4)) == 0) + if (BitConverter.ToInt32(Ro[..4]) == 0) { int length = BitConverter.ToInt32(Ro.Slice(4, 4)); if (length > 0) @@ -108,7 +108,7 @@ namespace Ryujinx.HLE.Loaders.Executables MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer); if (sdkMwMatches.Count != 0) { - string libHeader = " SDK Libraries: "; + string libHeader = " SDK Libraries: "; string libContent = string.Join($"\n{new string(' ', libHeader.Length)}", sdkMwMatches); stringBuilder.AppendLine($"{libHeader}{libContent}"); @@ -120,4 +120,4 @@ namespace Ryujinx.HLE.Loaders.Executables } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs b/src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs index 510fec059..cf316b565 100644 --- a/src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs +++ b/src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs @@ -1,13 +1,12 @@ using Ryujinx.Common.Logging; using System; using System.IO; -using System.Text; namespace Ryujinx.HLE.Loaders.Mods { class IpsPatcher { - MemPatch _patches; + readonly MemPatch _patches; public IpsPatcher(BinaryReader reader) { @@ -20,15 +19,15 @@ namespace Ryujinx.HLE.Loaders.Mods private static MemPatch ParseIps(BinaryReader reader) { - ReadOnlySpan<byte> IpsHeaderMagic = "PATCH"u8; - ReadOnlySpan<byte> IpsTailMagic = "EOF"u8; - ReadOnlySpan<byte> Ips32HeaderMagic = "IPS32"u8; - ReadOnlySpan<byte> Ips32TailMagic = "EEOF"u8; + ReadOnlySpan<byte> ipsHeaderMagic = "PATCH"u8; + ReadOnlySpan<byte> ipsTailMagic = "EOF"u8; + ReadOnlySpan<byte> ips32HeaderMagic = "IPS32"u8; + ReadOnlySpan<byte> ips32TailMagic = "EEOF"u8; - MemPatch patches = new MemPatch(); - var header = reader.ReadBytes(IpsHeaderMagic.Length).AsSpan(); + MemPatch patches = new(); + var header = reader.ReadBytes(ipsHeaderMagic.Length).AsSpan(); - if (header.Length != IpsHeaderMagic.Length) + if (header.Length != ipsHeaderMagic.Length) { return null; } @@ -36,15 +35,15 @@ namespace Ryujinx.HLE.Loaders.Mods bool is32; ReadOnlySpan<byte> tailSpan; - if (header.SequenceEqual(IpsHeaderMagic)) + if (header.SequenceEqual(ipsHeaderMagic)) { is32 = false; - tailSpan = IpsTailMagic; + tailSpan = ipsTailMagic; } - else if (header.SequenceEqual(Ips32HeaderMagic)) + else if (header.SequenceEqual(ips32HeaderMagic)) { is32 = true; - tailSpan = Ips32TailMagic; + tailSpan = ips32TailMagic; } else { @@ -114,4 +113,4 @@ namespace Ryujinx.HLE.Loaders.Mods patches.AddFrom(_patches); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs b/src/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs index 416fc1b49..693e03888 100644 --- a/src/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs +++ b/src/Ryujinx.HLE/Loaders/Mods/IPSwitchPatcher.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.Loaders.Mods Normal, String, EscapeChar, - Comment + Comment, } private readonly StreamReader _reader; @@ -31,13 +31,13 @@ namespace Ryujinx.HLE.Loaders.Mods } _reader = reader; - BuildId = header.Substring(BidHeader.Length).TrimEnd().TrimEnd('0'); + BuildId = header[BidHeader.Length..].TrimEnd().TrimEnd('0'); } // Uncomments line and unescapes C style strings within private static string PreprocessLine(string line) { - StringBuilder str = new StringBuilder(); + StringBuilder str = new(); Token state = Token.Normal; for (int i = 0; i < line.Length; ++i) @@ -56,29 +56,32 @@ namespace Ryujinx.HLE.Loaders.Mods case Token.String: state = c switch { - '"' => Token.Normal, + '"' => Token.Normal, '\\' => Token.EscapeChar, - _ => Token.String + _ => Token.String, }; break; case Token.EscapeChar: state = Token.String; c = c switch { - 'a' => '\a', - 'b' => '\b', - 'f' => '\f', - 'n' => '\n', - 'r' => '\r', - 't' => '\t', - 'v' => '\v', + 'a' => '\a', + 'b' => '\b', + 'f' => '\f', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + 'v' => '\v', '\\' => '\\', - _ => '?' + _ => '?', }; break; } - if (state == Token.Comment) break; + if (state == Token.Comment) + { + break; + } if (state < Token.EscapeChar) { @@ -112,14 +115,17 @@ namespace Ryujinx.HLE.Loaders.Mods // Big Endian static byte[] Hex2ByteArrayBE(string hexstr) { - if ((hexstr.Length & 1) == 1) return null; + if ((hexstr.Length & 1) == 1) + { + return null; + } byte[] bytes = new byte[hexstr.Length >> 1]; for (int i = 0; i < hexstr.Length; i += 2) { int high = ParseHexByte((byte)hexstr[i]); - int low = ParseHexByte((byte)hexstr[i + 1]); + int low = ParseHexByte((byte)hexstr[i + 1]); bytes[i >> 1] = (byte)((high << 4) | low); } @@ -147,11 +153,11 @@ namespace Ryujinx.HLE.Loaders.Mods return null; } - MemPatch patches = new MemPatch(); + MemPatch patches = new(); - bool enabled = false; + bool enabled = false; bool printValues = false; - int offset_shift = 0; + int offsetShift = 0; string line; int lineNum = 0; @@ -205,7 +211,7 @@ namespace Ryujinx.HLE.Loaders.Mods if (tokens[1] == "offset_shift") { - if (tokens.Length != 3 || !ParseInt(tokens[2], out offset_shift)) + if (tokens.Length != 3 || !ParseInt(tokens[2], out offsetShift)) { ParseWarn(); @@ -244,7 +250,7 @@ namespace Ryujinx.HLE.Loaders.Mods continue; } - offset += offset_shift; + offset += offsetShift; if (printValues) { @@ -272,4 +278,4 @@ namespace Ryujinx.HLE.Loaders.Mods patches.AddFrom(Parse()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs b/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs index f9db7c699..0a1f12b18 100644 --- a/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs +++ b/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.Loaders.Mods { public class MemPatch { - readonly Dictionary<uint, byte[]> _patches = new Dictionary<uint, byte[]>(); + readonly Dictionary<uint, byte[]> _patches = new(); /// <summary> /// Adds a patch to specified offset. Overwrites if already present. @@ -69,7 +69,7 @@ namespace Ryujinx.HLE.Loaders.Mods foreach (var (offset, patch) in _patches.OrderBy(item => item.Key)) { int patchOffset = (int)offset; - int patchSize = patch.Length; + int patchSize = patch.Length; if (patchOffset < protectedOffset || patchOffset > memory.Length) { @@ -93,4 +93,4 @@ namespace Ryujinx.HLE.Loaders.Mods return count; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs index 209e79d1e..9a5b6b0aa 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs @@ -9,17 +9,17 @@ namespace Ryujinx.HLE.Loaders.Npdm public ulong TitleId { get; set; } - public int FsVersion { get; private set; } + public int FsVersion { get; private set; } public ulong FsPermissionsBitmask { get; private set; } public ServiceAccessControl ServiceAccessControl { get; private set; } - public KernelAccessControl KernelAccessControl { get; private set; } + public KernelAccessControl KernelAccessControl { get; private set; } public Aci0(Stream stream, int offset) { stream.Seek(offset, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); if (reader.ReadInt32() != Aci0Magic) { @@ -33,16 +33,16 @@ namespace Ryujinx.HLE.Loaders.Npdm // Reserved. stream.Seek(8, SeekOrigin.Current); - int fsAccessHeaderOffset = reader.ReadInt32(); - int fsAccessHeaderSize = reader.ReadInt32(); + int fsAccessHeaderOffset = reader.ReadInt32(); + int fsAccessHeaderSize = reader.ReadInt32(); int serviceAccessControlOffset = reader.ReadInt32(); - int serviceAccessControlSize = reader.ReadInt32(); - int kernelAccessControlOffset = reader.ReadInt32(); - int kernelAccessControlSize = reader.ReadInt32(); + int serviceAccessControlSize = reader.ReadInt32(); + int kernelAccessControlOffset = reader.ReadInt32(); + int kernelAccessControlSize = reader.ReadInt32(); - FsAccessHeader fsAccessHeader = new FsAccessHeader(stream, offset + fsAccessHeaderOffset, fsAccessHeaderSize); + FsAccessHeader fsAccessHeader = new(stream, offset + fsAccessHeaderOffset, fsAccessHeaderSize); - FsVersion = fsAccessHeader.Version; + FsVersion = fsAccessHeader.Version; FsPermissionsBitmask = fsAccessHeader.PermissionsBitmask; ServiceAccessControl = new ServiceAccessControl(stream, offset + serviceAccessControlOffset, serviceAccessControlSize); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs index 365495c60..ab30b40ca 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs @@ -8,25 +8,25 @@ namespace Ryujinx.HLE.Loaders.Npdm private const int AcidMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24; public byte[] Rsa2048Signature { get; private set; } - public byte[] Rsa2048Modulus { get; private set; } - public int Unknown1 { get; private set; } - public int Flags { get; private set; } + public byte[] Rsa2048Modulus { get; private set; } + public int Unknown1 { get; private set; } + public int Flags { get; private set; } public long TitleIdRangeMin { get; private set; } public long TitleIdRangeMax { get; private set; } - public FsAccessControl FsAccessControl { get; private set; } + public FsAccessControl FsAccessControl { get; private set; } public ServiceAccessControl ServiceAccessControl { get; private set; } - public KernelAccessControl KernelAccessControl { get; private set; } + public KernelAccessControl KernelAccessControl { get; private set; } public Acid(Stream stream, int offset) { stream.Seek(offset, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); Rsa2048Signature = reader.ReadBytes(0x100); - Rsa2048Modulus = reader.ReadBytes(0x100); + Rsa2048Modulus = reader.ReadBytes(0x100); if (reader.ReadInt32() != AcidMagic) { @@ -44,12 +44,12 @@ namespace Ryujinx.HLE.Loaders.Npdm TitleIdRangeMin = reader.ReadInt64(); TitleIdRangeMax = reader.ReadInt64(); - int fsAccessControlOffset = reader.ReadInt32(); - int fsAccessControlSize = reader.ReadInt32(); + int fsAccessControlOffset = reader.ReadInt32(); + int fsAccessControlSize = reader.ReadInt32(); int serviceAccessControlOffset = reader.ReadInt32(); - int serviceAccessControlSize = reader.ReadInt32(); - int kernelAccessControlOffset = reader.ReadInt32(); - int kernelAccessControlSize = reader.ReadInt32(); + int serviceAccessControlSize = reader.ReadInt32(); + int kernelAccessControlOffset = reader.ReadInt32(); + int kernelAccessControlSize = reader.ReadInt32(); FsAccessControl = new FsAccessControl(stream, offset + fsAccessControlOffset, fsAccessControlSize); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs index d0f349eaf..e533f513c 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs @@ -4,25 +4,25 @@ namespace Ryujinx.HLE.Loaders.Npdm { public class FsAccessControl { - public int Version { get; private set; } + public int Version { get; private set; } public ulong PermissionsBitmask { get; private set; } - public int Unknown1 { get; private set; } - public int Unknown2 { get; private set; } - public int Unknown3 { get; private set; } - public int Unknown4 { get; private set; } + public int Unknown1 { get; private set; } + public int Unknown2 { get; private set; } + public int Unknown3 { get; private set; } + public int Unknown4 { get; private set; } public FsAccessControl(Stream stream, int offset, int size) { stream.Seek(offset, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); - Version = reader.ReadInt32(); + Version = reader.ReadInt32(); PermissionsBitmask = reader.ReadUInt64(); - Unknown1 = reader.ReadInt32(); - Unknown2 = reader.ReadInt32(); - Unknown3 = reader.ReadInt32(); - Unknown4 = reader.ReadInt32(); + Unknown1 = reader.ReadInt32(); + Unknown2 = reader.ReadInt32(); + Unknown3 = reader.ReadInt32(); + Unknown4 = reader.ReadInt32(); } } } diff --git a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs index 564b8dc39..7336464f5 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs @@ -6,16 +6,16 @@ namespace Ryujinx.HLE.Loaders.Npdm { class FsAccessHeader { - public int Version { get; private set; } + public int Version { get; private set; } public ulong PermissionsBitmask { get; private set; } public FsAccessHeader(Stream stream, int offset, int size) { stream.Seek(offset, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); - Version = reader.ReadInt32(); + Version = reader.ReadInt32(); PermissionsBitmask = reader.ReadUInt64(); int dataSize = reader.ReadInt32(); @@ -24,8 +24,9 @@ namespace Ryujinx.HLE.Loaders.Npdm { throw new InvalidNpdmException("FsAccessHeader is corrupted!"); } - - int contentOwnerIdSize = reader.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + int contentOwnerIdSize = reader.ReadInt32(); +#pragma warning restore IDE0059 int dataAndContentOwnerIdSize = reader.ReadInt32(); if (dataAndContentOwnerIdSize != 0x1c) diff --git a/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs index 39803642c..bc8bdbaaa 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.Loaders.Npdm Capabilities = new int[size / 4]; - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); for (int index = 0; index < Capabilities.Length; index++) { diff --git a/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs index 29a2b0fc0..622d7ee03 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs @@ -11,22 +11,22 @@ namespace Ryujinx.HLE.Loaders.Npdm { private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24; - public byte ProcessFlags { get; private set; } - public bool Is64Bit { get; private set; } - public byte MainThreadPriority { get; private set; } - public byte DefaultCpuId { get; private set; } - public int PersonalMmHeapSize { get; private set; } - public int Version { get; private set; } - public int MainThreadStackSize { get; private set; } - public string TitleName { get; set; } - public byte[] ProductCode { get; private set; } + public byte ProcessFlags { get; private set; } + public bool Is64Bit { get; private set; } + public byte MainThreadPriority { get; private set; } + public byte DefaultCpuId { get; private set; } + public int PersonalMmHeapSize { get; private set; } + public int Version { get; private set; } + public int MainThreadStackSize { get; private set; } + public string TitleName { get; set; } + public byte[] ProductCode { get; private set; } public Aci0 Aci0 { get; private set; } public Acid Acid { get; private set; } public Npdm(Stream stream) { - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); if (reader.ReadInt32() != MetaMagic) { @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.Loaders.Npdm reader.ReadByte(); MainThreadPriority = reader.ReadByte(); - DefaultCpuId = reader.ReadByte(); + DefaultCpuId = reader.ReadByte(); reader.ReadInt32(); @@ -61,9 +61,11 @@ namespace Ryujinx.HLE.Loaders.Npdm stream.Seek(0x30, SeekOrigin.Current); int aci0Offset = reader.ReadInt32(); - int aci0Size = reader.ReadInt32(); +#pragma warning disable IDE0059 // Remove unnecessary value assignment + int aci0Size = reader.ReadInt32(); int acidOffset = reader.ReadInt32(); - int acidSize = reader.ReadInt32(); + int acidSize = reader.ReadInt32(); +#pragma warning restore IDE0059 Aci0 = new Aci0(stream, aci0Offset); Acid = new Acid(stream, acidOffset); diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs index 54012b8a9..53c864346 100644 --- a/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs +++ b/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs @@ -13,11 +13,11 @@ namespace Ryujinx.HLE.Loaders.Npdm { stream.Seek(offset, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); + BinaryReader reader = new(stream); int bytesRead = 0; - Dictionary<string, bool> services = new Dictionary<string, bool>(); + Dictionary<string, bool> services = new(); while (bytesRead != size) { @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.Loaders.Npdm break; } - int length = (controlByte & 0x07) + 1; + int length = (controlByte & 0x07) + 1; bool registerAllowed = (controlByte & 0x80) != 0; services[Encoding.ASCII.GetString(reader.ReadBytes(length))] = registerAllowed; diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index b619a7134..040d1143d 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions bool enablePtc = device.System.EnablePtc && !modLoadResult.Modified; if (!enablePtc) { - Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PTC disabled."); + Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled."); } // We allow it for nx-hbloader because it can be used to launch homebrew. @@ -131,4 +131,4 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return processResult; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs index fb85329d2..2b7e84734 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -1,10 +1,10 @@ using LibHac.Common; using LibHac.FsSystem; using LibHac.Loader; +using LibHac.Ncm; using LibHac.Ns; using Ryujinx.HLE.HOS; using Ryujinx.HLE.Loaders.Processes.Extensions; -using ApplicationId = LibHac.Ncm.ApplicationId; namespace Ryujinx.HLE.Loaders.Processes { @@ -13,8 +13,8 @@ namespace Ryujinx.HLE.Loaders.Processes public static ProcessResult Load(this LocalFileSystem exeFs, Switch device, string romFsPath = "") { MetaLoader metaLoader = exeFs.GetNpdm(); - var nacpData = new BlitStruct<ApplicationControlProperty>(1); - ulong programId = metaLoader.GetProgramId(); + var nacpData = new BlitStruct<ApplicationControlProperty>(1); + ulong programId = metaLoader.GetProgramId(); device.Configuration.VirtualFileSystem.ModLoader.CollectMods( new[] { programId }, @@ -37,4 +37,4 @@ namespace Ryujinx.HLE.Loaders.Processes return processResult; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs index c639ee524..88f437591 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/MetaLoaderExtensions.cs @@ -58,4 +58,4 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions metaLoader.Load(npdmBuffer).ThrowIfFailure(); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index e11b81d7f..4568b44da 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca) { // Extract RomFs and ExeFs from NCA. - IStorage romFs = nca.GetRomFs(device, patchNca); + IStorage romFs = nca.GetRomFs(device, patchNca); IFileSystem exeFs = nca.GetExeFs(device, patchNca); if (exeFs == null) @@ -173,4 +173,4 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return nacpData; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs index e93802ae8..6de99131e 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs @@ -17,16 +17,16 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions { public static class PartitionFileSystemExtensions { - private static readonly DownloadableContentJsonSerializerContext ContentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); internal static (bool, ProcessResult) TryLoad(this PartitionFileSystem partitionFileSystem, Switch device, string path, out string errorMessage) { errorMessage = null; // Load required NCAs. - Nca mainNca = null; - Nca patchNca = null; + Nca mainNca = null; + Nca patchNca = null; Nca controlNca = null; try @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); if (File.Exists(titleUpdateMetadataPath)) { - string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; + string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected; if (File.Exists(updatePath)) { PartitionFileSystem updatePartitionFileSystem = new(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()); @@ -142,7 +142,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); if (File.Exists(addOnContentMetadataPath)) { - List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, ContentSerializerContext.ListDownloadableContentContainer); + List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer); foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) { @@ -177,4 +177,4 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return new Nca(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage()); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs index 42ae2e89b..5df7be29f 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs @@ -17,7 +17,7 @@ "subsdk7", "subsdk8", "subsdk9", - "sdk" + "sdk", }; public static readonly string MainNpdmPath = "/main.npdm"; @@ -27,7 +27,7 @@ public const bool AslrEnabled = true; public const int NsoArgsHeaderSize = 8; - public const int NsoArgsDataSize = 0x9000; - public const int NsoArgsTotalSize = NsoArgsHeaderSize + NsoArgsDataSize; + public const int NsoArgsDataSize = 0x9000; + public const int NsoArgsTotalSize = NsoArgsHeaderSize + NsoArgsDataSize; } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index f391f9656..51cbb6f99 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -12,7 +12,6 @@ using Ryujinx.HLE.Loaders.Processes.Extensions; using System; using System.Collections.Concurrent; using System.IO; -using System.Linq; using Path = System.IO.Path; namespace Ryujinx.HLE.Loaders.Processes @@ -29,14 +28,14 @@ namespace Ryujinx.HLE.Loaders.Processes public ProcessLoader(Switch device) { - _device = device; + _device = device; _processesByPid = new ConcurrentDictionary<ulong, ProcessResult>(); } public bool LoadXci(string path) { FileStream stream = new(path, FileMode.Open, FileAccess.Read); - Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage()); + Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage()); if (!xci.HasPartition(XciPartitionType.Secure)) { @@ -69,7 +68,7 @@ namespace Ryujinx.HLE.Loaders.Processes public bool LoadNsp(string path) { - FileStream file = new(path, FileMode.Open, FileAccess.Read); + FileStream file = new(path, FileMode.Open, FileAccess.Read); PartitionFileSystem partitionFileSystem = new(file.AsStorage()); (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage); @@ -101,7 +100,7 @@ namespace Ryujinx.HLE.Loaders.Processes public bool LoadNca(string path) { FileStream file = new(path, FileMode.Open, FileAccess.Read); - Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); + Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); ProcessResult processResult = nca.Load(_device, null, null); @@ -141,20 +140,20 @@ namespace Ryujinx.HLE.Loaders.Processes public bool LoadNxo(string path) { - var nacpData = new BlitStruct<ApplicationControlProperty>(1); - IFileSystem dummyExeFs = null; - Stream romfsStream = null; + var nacpData = new BlitStruct<ApplicationControlProperty>(1); + IFileSystem dummyExeFs = null; + Stream romfsStream = null; string programName = ""; - ulong programId = 0000000000000000; + ulong programId = 0000000000000000; // Load executable. IExecutable executable; if (Path.GetExtension(path).ToLower() == ".nro") { - FileStream input = new(path, FileMode.Open); - NroExecutable nro = new(input.AsStorage()); + FileStream input = new(path, FileMode.Open); + NroExecutable nro = new(input.AsStorage()); executable = nro; @@ -242,4 +241,4 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index d1c60f167..d14a013af 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Loaders.Processes public static LibHac.Result RegisterProgramMapInfo(Switch device, PartitionFileSystem partitionFileSystem) { ulong applicationId = 0; - int programCount = 0; + int programCount = 0; Span<bool> hasIndex = stackalloc bool[0x10]; @@ -44,7 +44,7 @@ namespace Ryujinx.HLE.Loaders.Processes continue; } - ulong currentProgramId = nca.Header.TitleId; + ulong currentProgramId = nca.Header.TitleId; ulong currentMainProgramId = currentProgramId & ~0xFFFul; if (applicationId == 0 && currentMainProgramId != 0) @@ -82,9 +82,9 @@ namespace Ryujinx.HLE.Loaders.Processes for (int i = 0; i < programCount; i++) { - mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i); + mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i); mapInfo[i].MainProgramId = new ApplicationId(applicationId); - mapInfo[i].ProgramIndex = (byte)i; + mapInfo[i].ProgramIndex = (byte)i; } return device.System.LibHacHorizonManager.NsClient.Fs.RegisterProgramIndexMapInfo(mapInfo[..programCount]); @@ -102,9 +102,9 @@ namespace Ryujinx.HLE.Loaders.Processes control = ref new BlitStruct<ApplicationControlProperty>(1).Value; // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; + control.UserAccountSaveDataSize = 0x4000; control.UserAccountSaveDataJournalSize = 0x4000; - control.SaveDataOwnerId = applicationId.Value; + control.SaveDataOwnerId = applicationId.Value; Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } @@ -137,10 +137,10 @@ namespace Ryujinx.HLE.Loaders.Processes endOffset = kip.BssOffset + kip.BssSize; } - uint codeSize = BitUtils.AlignUp<uint>(kip.TextOffset + endOffset, KPageTableBase.PageSize); - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); + uint codeSize = BitUtils.AlignUp<uint>(kip.TextOffset + endOffset, KPageTableBase.PageSize); + int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL; - ulong codeAddress = codeBaseAddress + kip.TextOffset; + ulong codeAddress = codeBaseAddress + kip.TextOffset; ProcessCreationFlags flags = 0; @@ -161,9 +161,9 @@ namespace Ryujinx.HLE.Loaders.Processes flags |= ProcessCreationFlags.Is64Bit; } - ProcessCreationInfo creationInfo = new(kip.Name, kip.Version, kip.ProgramId, codeAddress, codePagesCount, flags, 0, 0); - MemoryRegion memoryRegion = kip.UsesSecureMemory ? MemoryRegion.Service : MemoryRegion.Application; - KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion]; + ProcessCreationInfo creationInfo = new(kip.Name, kip.Version, kip.ProgramId, codeAddress, codePagesCount, flags, 0, 0); + MemoryRegion memoryRegion = kip.UsesSecureMemory ? MemoryRegion.Service : MemoryRegion.Application; + KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion]; Result result = region.AllocatePages(out KPageList pageList, (ulong)codePagesCount); if (result != Result.Success) @@ -241,15 +241,15 @@ namespace Ryujinx.HLE.Loaders.Processes ref readonly var meta = ref npdm.Meta; ulong argsStart = 0; - uint argsSize = 0; + uint argsSize = 0; ulong codeStart = (meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL; - uint codeSize = 0; + uint codeSize = 0; var buildIds = executables.Select(e => (e switch { NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()), NroExecutable nro => Convert.ToHexString(nro.Header.BuildId), - _ => "" + _ => "", }).ToUpper()); ulong[] nsoBase = new ulong[executables.Length]; @@ -259,7 +259,7 @@ namespace Ryujinx.HLE.Loaders.Processes IExecutable nso = executables[index]; uint textEnd = nso.TextOffset + (uint)nso.Text.Length; - uint roEnd = nso.RoOffset + (uint)nso.Ro.Length; + uint roEnd = nso.RoOffset + (uint)nso.Ro.Length; uint dataEnd = nso.DataOffset + (uint)nso.Data.Length + nso.BssSize; uint nsoSize = textEnd; @@ -290,7 +290,7 @@ namespace Ryujinx.HLE.Loaders.Processes } } - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); + int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); int personalMmHeapPagesCount = (int)(meta.SystemResourceSize / KPageTableBase.PageSize); ProcessCreationInfo creationInfo = new( @@ -335,7 +335,7 @@ namespace Ryujinx.HLE.Loaders.Processes if (result != Result.Success) { - Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values."); + Logger.Error?.Print(LogClass.Loader, "Process initialization failed setting resource limit values."); return ProcessResult.Failed; } @@ -346,7 +346,7 @@ namespace Ryujinx.HLE.Loaders.Processes MemoryRegion memoryRegion = (MemoryRegion)(npdm.Acid.Flags >> 2 & 0xf); if (memoryRegion > MemoryRegion.NvServices) { - Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags."); + Logger.Error?.Print(LogClass.Loader, "Process initialization failed due to invalid ACID flags."); return ProcessResult.Failed; } @@ -420,9 +420,9 @@ namespace Ryujinx.HLE.Loaders.Processes public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) { ulong textStart = baseAddress + image.TextOffset; - ulong roStart = baseAddress + image.RoOffset; + ulong roStart = baseAddress + image.RoOffset; ulong dataStart = baseAddress + image.DataOffset; - ulong bssStart = baseAddress + image.BssOffset; + ulong bssStart = baseAddress + image.BssOffset; ulong end = dataStart + (ulong)image.Data.Length; @@ -432,7 +432,7 @@ namespace Ryujinx.HLE.Loaders.Processes } process.CpuMemory.Write(textStart, image.Text); - process.CpuMemory.Write(roStart, image.Ro); + process.CpuMemory.Write(roStart, image.Ro); process.CpuMemory.Write(dataStart, image.Data); process.CpuMemory.Fill(bssStart, image.BssSize, 0); @@ -464,4 +464,4 @@ namespace Ryujinx.HLE.Loaders.Processes return SetProcessMemoryPermission(dataStart, end - dataStart, KMemoryPermission.ReadAndWrite); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index 40b516cc8..9a7b3cd0e 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -7,7 +7,6 @@ using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.Horizon.Common; using System; -using System.Linq; namespace Ryujinx.HLE.Loaders.Processes { @@ -20,36 +19,36 @@ namespace Ryujinx.HLE.Loaders.Processes public readonly IDiskCacheLoadState DiskCacheLoadState; - public readonly MetaLoader MetaLoader; + public readonly MetaLoader MetaLoader; public readonly ApplicationControlProperty ApplicationControlProperties; - public readonly ulong ProcessId; + public readonly ulong ProcessId; public readonly string Name; public readonly string DisplayVersion; - public readonly ulong ProgramId; + public readonly ulong ProgramId; public readonly string ProgramIdText; - public readonly bool Is64Bit; - public readonly bool DiskCacheEnabled; - public readonly bool AllowCodeMemoryForJit; + public readonly bool Is64Bit; + public readonly bool DiskCacheEnabled; + public readonly bool AllowCodeMemoryForJit; public ProcessResult( - MetaLoader metaLoader, + MetaLoader metaLoader, BlitStruct<ApplicationControlProperty> applicationControlProperties, - bool diskCacheEnabled, - bool allowCodeMemoryForJit, - IDiskCacheLoadState diskCacheLoadState, - ulong pid, - byte mainThreadPriority, - uint mainThreadStackSize, - TitleLanguage titleLanguage) + bool diskCacheEnabled, + bool allowCodeMemoryForJit, + IDiskCacheLoadState diskCacheLoadState, + ulong pid, + byte mainThreadPriority, + uint mainThreadStackSize, + TitleLanguage titleLanguage) { - _mainThreadPriority = mainThreadPriority; + _mainThreadPriority = mainThreadPriority; _mainThreadStackSize = mainThreadStackSize; DiskCacheLoadState = diskCacheLoadState; - ProcessId = pid; + ProcessId = pid; - MetaLoader = metaLoader; + MetaLoader = metaLoader; ApplicationControlProperties = applicationControlProperties.Value; if (metaLoader is not null) @@ -64,12 +63,12 @@ namespace Ryujinx.HLE.Loaders.Processes } DisplayVersion = ApplicationControlProperties.DisplayVersionString.ToString(); - ProgramId = programId; - ProgramIdText = $"{programId:x16}"; - Is64Bit = metaLoader.IsProgram64Bit(); + ProgramId = programId; + ProgramIdText = $"{programId:x16}"; + Is64Bit = metaLoader.IsProgram64Bit(); } - DiskCacheEnabled = diskCacheEnabled; + DiskCacheEnabled = diskCacheEnabled; AllowCodeMemoryForJit = allowCodeMemoryForJit; } diff --git a/src/Ryujinx.HLE/MemoryConfiguration.cs b/src/Ryujinx.HLE/MemoryConfiguration.cs index 25044bb5f..45e8927db 100644 --- a/src/Ryujinx.HLE/MemoryConfiguration.cs +++ b/src/Ryujinx.HLE/MemoryConfiguration.cs @@ -5,18 +5,19 @@ namespace Ryujinx.HLE { public enum MemoryConfiguration { - MemoryConfiguration4GiB = 0, + MemoryConfiguration4GiB = 0, MemoryConfiguration4GiBAppletDev = 1, MemoryConfiguration4GiBSystemDev = 2, - MemoryConfiguration6GiB = 3, + MemoryConfiguration6GiB = 3, MemoryConfiguration6GiBAppletDev = 4, - MemoryConfiguration8GiB = 5 + MemoryConfiguration8GiB = 5, } static class MemoryConfigurationExtensions { private const ulong GiB = 1024 * 1024 * 1024; +#pragma warning disable IDE0055 // Disable formatting public static MemoryArrange ToKernelMemoryArrange(this MemoryConfiguration configuration) { return configuration switch @@ -27,7 +28,7 @@ namespace Ryujinx.HLE MemoryConfiguration.MemoryConfiguration6GiB => MemoryArrange.MemoryArrange6GiB, MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemoryArrange.MemoryArrange6GiBAppletDev, MemoryConfiguration.MemoryConfiguration8GiB => MemoryArrange.MemoryArrange8GiB, - _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".") + _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), }; } @@ -41,7 +42,7 @@ namespace Ryujinx.HLE MemoryConfiguration.MemoryConfiguration6GiB or MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemorySize.MemorySize6GiB, MemoryConfiguration.MemoryConfiguration8GiB => MemorySize.MemorySize8GiB, - _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".") + _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), }; } @@ -55,8 +56,9 @@ namespace Ryujinx.HLE MemoryConfiguration.MemoryConfiguration6GiB or MemoryConfiguration.MemoryConfiguration6GiBAppletDev => 6 * GiB, MemoryConfiguration.MemoryConfiguration8GiB => 8 * GiB, - _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".") + _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), }; } +#pragma warning restore IDE0055 } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/PerformanceStatistics.cs b/src/Ryujinx.HLE/PerformanceStatistics.cs index 95fc2abe3..e7270db30 100644 --- a/src/Ryujinx.HLE/PerformanceStatistics.cs +++ b/src/Ryujinx.HLE/PerformanceStatistics.cs @@ -5,44 +5,44 @@ namespace Ryujinx.HLE { public class PerformanceStatistics { - private const int FrameTypeGame = 0; + private const int FrameTypeGame = 0; private const int PercentTypeFifo = 0; - private double[] _frameRate; - private double[] _accumulatedFrameTime; - private double[] _previousFrameTime; + private readonly double[] _frameRate; + private readonly double[] _accumulatedFrameTime; + private readonly double[] _previousFrameTime; - private double[] _averagePercent; - private double[] _accumulatedActiveTime; - private double[] _percentLastEndTime; - private double[] _percentStartTime; + private readonly double[] _averagePercent; + private readonly double[] _accumulatedActiveTime; + private readonly double[] _percentLastEndTime; + private readonly double[] _percentStartTime; - private long[] _framesRendered; - private double[] _percentTime; + private readonly long[] _framesRendered; + private readonly double[] _percentTime; - private object[] _frameLock; - private object[] _percentLock; + private readonly object[] _frameLock; + private readonly object[] _percentLock; - private double _ticksToSeconds; + private readonly double _ticksToSeconds; - private Timer _resetTimer; + private readonly Timer _resetTimer; public PerformanceStatistics() { - _frameRate = new double[1]; + _frameRate = new double[1]; _accumulatedFrameTime = new double[1]; - _previousFrameTime = new double[1]; + _previousFrameTime = new double[1]; - _averagePercent = new double[1]; + _averagePercent = new double[1]; _accumulatedActiveTime = new double[1]; - _percentLastEndTime = new double[1]; - _percentStartTime = new double[1]; + _percentLastEndTime = new double[1]; + _percentStartTime = new double[1]; _framesRendered = new long[1]; - _percentTime = new double[1]; + _percentTime = new double[1]; - _frameLock = new object[] { new object() }; - _percentLock = new object[] { new object() }; + _frameLock = new[] { new object() }; + _percentLock = new[] { new object() }; _resetTimer = new Timer(750); @@ -71,8 +71,8 @@ namespace Ryujinx.HLE frameRate = _framesRendered[frameType] / _accumulatedFrameTime[frameType]; } - _frameRate[frameType] = frameRate; - _framesRendered[frameType] = 0; + _frameRate[frameType] = frameRate; + _framesRendered[frameType] = 0; _accumulatedFrameTime[frameType] = 0; } } @@ -90,8 +90,8 @@ namespace Ryujinx.HLE percent = (_accumulatedActiveTime[percentType] / _percentTime[percentType]) * 100; } - _averagePercent[percentType] = percent; - _percentTime[percentType] = 0; + _averagePercent[percentType] = percent; + _percentTime[percentType] = 0; _accumulatedActiveTime[percentType] = 0; } } @@ -120,18 +120,18 @@ namespace Ryujinx.HLE private void EndPercentTime(int percentType) { - double currentTime = PerformanceCounter.ElapsedTicks * _ticksToSeconds; - double elapsedTime = currentTime - _percentLastEndTime[percentType]; + double currentTime = PerformanceCounter.ElapsedTicks * _ticksToSeconds; + double elapsedTime = currentTime - _percentLastEndTime[percentType]; double elapsedActiveTime = currentTime - _percentStartTime[percentType]; lock (_percentLock[percentType]) { _accumulatedActiveTime[percentType] += elapsedActiveTime; - _percentTime[percentType] += elapsedTime; + _percentTime[percentType] += elapsedTime; } _percentLastEndTime[percentType] = currentTime; - _percentStartTime[percentType] = 0; + _percentStartTime[percentType] = 0; } private void RecordFrameTime(int frameType) @@ -164,4 +164,4 @@ namespace Ryujinx.HLE return 1000 / _frameRate[FrameTypeGame]; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 62d14a546..ae063a47d 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -15,17 +15,17 @@ namespace Ryujinx.HLE { public class Switch : IDisposable { - public HLEConfiguration Configuration { get; } + public HLEConfiguration Configuration { get; } public IHardwareDeviceDriver AudioDeviceDriver { get; } - public MemoryBlock Memory { get; } - public GpuContext Gpu { get; } - public VirtualFileSystem FileSystem { get; } - public HOS.Horizon System { get; } - public ProcessLoader Processes { get; } - public PerformanceStatistics Statistics { get; } - public Hid Hid { get; } - public TamperMachine TamperMachine { get; } - public IHostUiHandler UiHandler { get; } + public MemoryBlock Memory { get; } + public GpuContext Gpu { get; } + public VirtualFileSystem FileSystem { get; } + public HOS.Horizon System { get; } + public ProcessLoader Processes { get; } + public PerformanceStatistics Statistics { get; } + public Hid Hid { get; } + public TamperMachine TamperMachine { get; } + public IHostUiHandler UiHandler { get; } public bool EnableDeviceVsync { get; set; } = true; @@ -38,13 +38,14 @@ namespace Ryujinx.HLE ArgumentNullException.ThrowIfNull(configuration.UserChannelPersistence); Configuration = configuration; - FileSystem = Configuration.VirtualFileSystem; - UiHandler = Configuration.HostUiHandler; + FileSystem = Configuration.VirtualFileSystem; + UiHandler = Configuration.HostUiHandler; MemoryAllocationFlags memoryAllocationFlags = configuration.MemoryManagerMode == MemoryManagerMode.SoftwarePageTable ? MemoryAllocationFlags.Reserve : MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable; +#pragma warning disable IDE0055 // Disable formatting AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Gpu = new GpuContext(Configuration.GpuRenderer); @@ -63,6 +64,7 @@ namespace Ryujinx.HLE System.EnablePtc = Configuration.EnablePtc; System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; +#pragma warning restore IDE0055 } public bool LoadCart(string exeFsDir, string romFsFile = null) @@ -124,7 +126,7 @@ namespace Ryujinx.HLE public void EnableCheats() { - FileSystem.ModLoader.EnableCheats(Processes.ActiveApplication.ProgramId, TamperMachine); + ModLoader.EnableCheats(Processes.ActiveApplication.ProgramId, TamperMachine); } public bool IsAudioMuted() @@ -139,6 +141,7 @@ namespace Ryujinx.HLE public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); } diff --git a/src/Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs b/src/Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs index c571fb683..cb9ca0dec 100644 --- a/src/Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs +++ b/src/Ryujinx.HLE/Ui/DynamicTextChangedHandler.cs @@ -1,4 +1,4 @@ namespace Ryujinx.HLE.Ui { public delegate void DynamicTextChangedHandler(string text, int cursorBegin, int cursorEnd, bool overwriteMode); -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs b/src/Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs index 6e7b4c495..e530d2c4e 100644 --- a/src/Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs +++ b/src/Ryujinx.HLE/Ui/IDynamicTextInputHandler.cs @@ -13,4 +13,4 @@ namespace Ryujinx.HLE.Ui void SetText(string text, int cursorBegin); void SetText(string text, int cursorBegin, int cursorEnd); } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Ui/IHostUiHandler.cs b/src/Ryujinx.HLE/Ui/IHostUiHandler.cs index 91d8be857..68f78f22d 100644 --- a/src/Ryujinx.HLE/Ui/IHostUiHandler.cs +++ b/src/Ryujinx.HLE/Ui/IHostUiHandler.cs @@ -48,4 +48,4 @@ namespace Ryujinx.HLE.Ui /// </summary> IHostUiTheme HostUiTheme { get; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Ui/Input/NpadReader.cs b/src/Ryujinx.HLE/Ui/Input/NpadReader.cs index a0d78d03a..c4ef5da50 100644 --- a/src/Ryujinx.HLE/Ui/Input/NpadReader.cs +++ b/src/Ryujinx.HLE/Ui/Input/NpadReader.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.Ui.Input class NpadReader { private readonly Switch _device; - private NpadCommonState[] _lastStates; + private readonly NpadCommonState[] _lastStates; public event NpadButtonHandler NpadButtonUpEvent; public event NpadButtonHandler NpadButtonDownEvent; @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.Ui.Input return buttons; } - private ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad) + private static ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad) { switch (npad.StyleSet) { @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.Ui.Input } } - public void Update(bool supressEvents=false) + public void Update(bool supressEvents = false) { ref var npads = ref _device.Hid.SharedMemory.Npads; @@ -81,7 +81,11 @@ namespace Ryujinx.HLE.Ui.Input int firstEntryNum; // Scan the LIFO for the first entry that is newer that what's already processed. - for (firstEntryNum = fullKeyEntries.Length - 1; firstEntryNum >= 0 && fullKeyEntries[firstEntryNum].Object.SamplingNumber <= lastEntry.SamplingNumber; firstEntryNum--) ; + for (firstEntryNum = fullKeyEntries.Length - 1; + firstEntryNum >= 0 && fullKeyEntries[firstEntryNum].Object.SamplingNumber <= lastEntry.SamplingNumber; + firstEntryNum--) + { + } if (firstEntryNum == -1) { diff --git a/src/Ryujinx.HLE/Ui/KeyPressedHandler.cs b/src/Ryujinx.HLE/Ui/KeyPressedHandler.cs index 096bf7314..31e754377 100644 --- a/src/Ryujinx.HLE/Ui/KeyPressedHandler.cs +++ b/src/Ryujinx.HLE/Ui/KeyPressedHandler.cs @@ -3,4 +3,4 @@ using Ryujinx.Common.Configuration.Hid; namespace Ryujinx.HLE.Ui { public delegate bool KeyPressedHandler(Key key); -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Ui/KeyReleasedHandler.cs b/src/Ryujinx.HLE/Ui/KeyReleasedHandler.cs index 4faaa529c..d5b6d2019 100644 --- a/src/Ryujinx.HLE/Ui/KeyReleasedHandler.cs +++ b/src/Ryujinx.HLE/Ui/KeyReleasedHandler.cs @@ -3,4 +3,4 @@ using Ryujinx.Common.Configuration.Hid; namespace Ryujinx.HLE.Ui { public delegate bool KeyReleasedHandler(Key key); -} \ No newline at end of file +} diff --git a/src/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs b/src/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs index 0ba116add..10da74ef3 100644 --- a/src/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs +++ b/src/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs @@ -26,10 +26,20 @@ namespace Ryujinx.HLE.Ui public bool Equals(RenderingSurfaceInfo other) { return ColorFormat == other.ColorFormat && - Width == other.Width && - Height == other.Height && - Pitch == other.Pitch && - Size == other.Size; + Width == other.Width && + Height == other.Height && + Pitch == other.Pitch && + Size == other.Size; + } + + public override bool Equals(object obj) + { + return obj is RenderingSurfaceInfo info && Equals(info); + } + + public override int GetHashCode() + { + return BitConverter.ToInt32(BitConverter.GetBytes(((ulong)ColorFormat) ^ Width ^ Height ^ Pitch ^ Size)); } } } diff --git a/src/Ryujinx.HLE/Utilities/StringUtils.cs b/src/Ryujinx.HLE/Utilities/StringUtils.cs index 1810b1ad7..9b3479eec 100644 --- a/src/Ryujinx.HLE/Utilities/StringUtils.cs +++ b/src/Ryujinx.HLE/Utilities/StringUtils.cs @@ -77,30 +77,28 @@ namespace Ryujinx.HLE.Utilities public static string ReadUtf8String(ServiceCtx context, int index = 0) { ulong position = context.Request.PtrBuff[index].Position; - ulong size = context.Request.PtrBuff[index].Size; + ulong size = context.Request.PtrBuff[index].Size; - using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream()) + using RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(); + while (size-- > 0) { - while (size-- > 0) + byte value = context.Memory.Read<byte>(position++); + + if (value == 0) { - byte value = context.Memory.Read<byte>(position++); - - if (value == 0) - { - break; - } - - ms.WriteByte(value); + break; } - return Encoding.UTF8.GetString(ms.GetReadOnlySequence()); + ms.WriteByte(value); } + + return Encoding.UTF8.GetString(ms.GetReadOnlySequence()); } public static U8Span ReadUtf8Span(ServiceCtx context, int index = 0) { ulong position = context.Request.PtrBuff[index].Position; - ulong size = context.Request.PtrBuff[index].Size; + ulong size = context.Request.PtrBuff[index].Size; ReadOnlySpan<byte> buffer = context.Memory.GetSpan(position, (int)size); @@ -110,24 +108,23 @@ namespace Ryujinx.HLE.Utilities public static string ReadUtf8StringSend(ServiceCtx context, int index = 0) { ulong position = context.Request.SendBuff[index].Position; - ulong size = context.Request.SendBuff[index].Size; + ulong size = context.Request.SendBuff[index].Size; - using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream()) + using RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(); + + while (size-- > 0) { - while (size-- > 0) + byte value = context.Memory.Read<byte>(position++); + + if (value == 0) { - byte value = context.Memory.Read<byte>(position++); - - if (value == 0) - { - break; - } - - ms.WriteByte(value); + break; } - return Encoding.UTF8.GetString(ms.GetReadOnlySequence()); + ms.WriteByte(value); } + + return Encoding.UTF8.GetString(ms.GetReadOnlySequence()); } public static int CompareCStr(ReadOnlySpan<byte> s1, ReadOnlySpan<byte> s2) @@ -156,4 +153,4 @@ namespace Ryujinx.HLE.Utilities return i; } } -} \ No newline at end of file +} diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index 7625e9e6f..a022b5535 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -891,7 +891,7 @@ namespace Ryujinx.Ui if (path.StartsWith("@SystemContent")) { - path = _virtualFileSystem.SwitchPathToSystemPath(path); + path = VirtualFileSystem.SwitchPathToSystemPath(path); isFirmwareTitle = true; } diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 8170b9317..c2e0d8ebc 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -139,7 +139,7 @@ namespace Ryujinx.Ui.Widgets return; } - string saveRootPath = System.IO.Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}"); + string saveRootPath = System.IO.Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}"); if (!Directory.Exists(saveRootPath)) { diff --git a/src/Ryujinx/Ui/Windows/AvatarWindow.cs b/src/Ryujinx/Ui/Windows/AvatarWindow.cs index 3940f17d6..826b0e056 100644 --- a/src/Ryujinx/Ui/Windows/AvatarWindow.cs +++ b/src/Ryujinx/Ui/Windows/AvatarWindow.cs @@ -119,7 +119,7 @@ namespace Ryujinx.Ui.Windows } string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data); - string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); + string avatarPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(avatarPath)) { From 016262514d46cd51f1f2298bc7583ef44504a90d Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 16 Jul 2023 21:39:08 +0200 Subject: [PATCH 710/737] cpu: Hotfix missing ToNearest rounding mode cases --- src/ARMeilleure/Instructions/SoftFloat.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ARMeilleure/Instructions/SoftFloat.cs b/src/ARMeilleure/Instructions/SoftFloat.cs index 05975d04f..a67349e6e 100644 --- a/src/ARMeilleure/Instructions/SoftFloat.cs +++ b/src/ARMeilleure/Instructions/SoftFloat.cs @@ -1448,6 +1448,7 @@ namespace ARMeilleure.Instructions { var overflowToInf = fpcr.GetRoundingMode() switch { + FPRoundingMode.ToNearest => true, FPRoundingMode.TowardsPlusInfinity => !sign, FPRoundingMode.TowardsMinusInfinity => sign, FPRoundingMode.TowardsZero => false, @@ -2879,6 +2880,7 @@ namespace ARMeilleure.Instructions { var overflowToInf = fpcr.GetRoundingMode() switch { + FPRoundingMode.ToNearest => true, FPRoundingMode.TowardsPlusInfinity => !sign, FPRoundingMode.TowardsMinusInfinity => sign, FPRoundingMode.TowardsZero => false, From 732714349ea043f0e09e1c52a568f64790da0849 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 17 Jul 2023 20:47:47 +0200 Subject: [PATCH 711/737] [Hotfix] sockets: Resolve empty port requests to 0 again (#5459) --- src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs index 93960d13e..d0fb6675a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs @@ -586,7 +586,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres if (hostEntry != null) { - if (int.TryParse(service, out int port)) + if (int.TryParse(service, out int port) || string.IsNullOrEmpty(service)) { errno = GaiError.Success; serializedSize = SerializeAddrInfos(context, responseBufferPosition, responseBufferSize, hostEntry, port); From 440abac9f8cde2a8213083e4af2e4a8ae7ed1e02 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Tue, 18 Jul 2023 16:08:48 +0200 Subject: [PATCH 712/737] chore: Update Ryujinx.SDL2-CS to 2.28.1 (#5453) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index df917cbab..4b6bb1917 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,7 +34,7 @@ <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" /> - <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" /> + <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.28.1-build28" /> <PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> From 4cf2419e6cfd3d7959914412abbb01f8cd473a76 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 20 Jul 2023 03:02:31 +0200 Subject: [PATCH 713/737] HLE: Fix corrupted Mii structs (#5468) * StructArrayHelpers: Add PureAttribute to all AsSpan() methods * Fix broken Mii structs --- .../Memory/StructArrayHelpers.cs | 137 ++++++++++++++++++ .../Services/Mii/Types/RandomMiiConstants.cs | 41 ++---- 2 files changed, 153 insertions(+), 25 deletions(-) diff --git a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs index ae5853d1d..1e8976ca4 100644 --- a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs +++ b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.Contracts; using System.Runtime.InteropServices; #pragma warning disable CS0169, IDE0051 // Remove unused private member @@ -9,6 +10,8 @@ namespace Ryujinx.Common.Memory T _e0; public readonly int Length => 1; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -18,6 +21,8 @@ namespace Ryujinx.Common.Memory Array1<T> _other; public readonly int Length => 2; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -27,6 +32,8 @@ namespace Ryujinx.Common.Memory Array2<T> _other; public readonly int Length => 3; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -36,6 +43,8 @@ namespace Ryujinx.Common.Memory Array3<T> _other; public readonly int Length => 4; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -45,6 +54,8 @@ namespace Ryujinx.Common.Memory Array4<T> _other; public readonly int Length => 5; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -54,6 +65,8 @@ namespace Ryujinx.Common.Memory Array5<T> _other; public readonly int Length => 6; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -63,6 +76,8 @@ namespace Ryujinx.Common.Memory Array6<T> _other; public readonly int Length => 7; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -72,6 +87,8 @@ namespace Ryujinx.Common.Memory Array7<T> _other; public readonly int Length => 8; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -81,6 +98,8 @@ namespace Ryujinx.Common.Memory Array8<T> _other; public readonly int Length => 9; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -90,6 +109,8 @@ namespace Ryujinx.Common.Memory Array9<T> _other; public readonly int Length => 10; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -99,6 +120,8 @@ namespace Ryujinx.Common.Memory Array10<T> _other; public readonly int Length => 11; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -108,6 +131,8 @@ namespace Ryujinx.Common.Memory Array11<T> _other; public readonly int Length => 12; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -117,6 +142,8 @@ namespace Ryujinx.Common.Memory Array12<T> _other; public readonly int Length => 13; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -126,6 +153,8 @@ namespace Ryujinx.Common.Memory Array13<T> _other; public readonly int Length => 14; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -135,6 +164,8 @@ namespace Ryujinx.Common.Memory Array14<T> _other; public readonly int Length => 15; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -144,6 +175,8 @@ namespace Ryujinx.Common.Memory Array15<T> _other; public readonly int Length => 16; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -153,6 +186,8 @@ namespace Ryujinx.Common.Memory Array16<T> _other; public readonly int Length => 17; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -162,6 +197,8 @@ namespace Ryujinx.Common.Memory Array17<T> _other; public readonly int Length => 18; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -171,6 +208,8 @@ namespace Ryujinx.Common.Memory Array18<T> _other; public readonly int Length => 19; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -180,6 +219,8 @@ namespace Ryujinx.Common.Memory Array19<T> _other; public readonly int Length => 20; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -189,6 +230,8 @@ namespace Ryujinx.Common.Memory Array20<T> _other; public readonly int Length => 21; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -198,6 +241,8 @@ namespace Ryujinx.Common.Memory Array21<T> _other; public readonly int Length => 22; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -207,6 +252,8 @@ namespace Ryujinx.Common.Memory Array22<T> _other; public readonly int Length => 23; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -217,6 +264,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 24; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -227,6 +276,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 25; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -237,6 +288,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 26; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -247,6 +300,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 27; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -257,6 +312,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 28; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -267,6 +324,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 29; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -277,6 +336,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 30; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -287,6 +348,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 31; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -297,6 +360,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 32; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -307,6 +372,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 33; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -317,6 +384,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 34; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -327,6 +396,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 35; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -337,6 +408,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 36; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -347,6 +420,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 37; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -357,6 +432,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 38; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -367,6 +444,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 39; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -377,6 +456,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 40; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -387,6 +468,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 41; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -397,6 +480,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 42; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -407,6 +492,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 43; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -417,6 +504,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 44; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -427,6 +516,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 45; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -437,6 +528,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 46; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -447,6 +540,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 47; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -457,6 +552,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 48; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -467,6 +564,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 49; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -477,6 +576,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 50; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -487,6 +588,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 51; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -497,6 +600,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 52; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -507,6 +612,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 53; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -517,6 +624,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 54; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -527,6 +636,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 55; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -537,6 +648,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 56; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -547,6 +660,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 57; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -557,6 +672,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 58; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -567,6 +684,8 @@ namespace Ryujinx.Common.Memory public readonly int Length => 59; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -576,6 +695,8 @@ namespace Ryujinx.Common.Memory Array59<T> _other; public readonly int Length => 60; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -585,6 +706,8 @@ namespace Ryujinx.Common.Memory Array60<T> _other; public readonly int Length => 61; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -594,6 +717,8 @@ namespace Ryujinx.Common.Memory Array61<T> _other; public readonly int Length => 62; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -603,6 +728,8 @@ namespace Ryujinx.Common.Memory Array62<T> _other; public readonly int Length => 63; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -612,6 +739,8 @@ namespace Ryujinx.Common.Memory Array63<T> _other; public readonly int Length => 64; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -622,6 +751,8 @@ namespace Ryujinx.Common.Memory Array8<T> _other2; public readonly int Length => 73; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -632,6 +763,8 @@ namespace Ryujinx.Common.Memory Array62<T> _other2; public readonly int Length => 127; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -642,6 +775,8 @@ namespace Ryujinx.Common.Memory Array63<T> _other2; public readonly int Length => 128; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } @@ -652,6 +787,8 @@ namespace Ryujinx.Common.Memory Array127<T> _other2; public readonly int Length => 256; public ref T this[int index] => ref AsSpan()[index]; + + [Pure] public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } } diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs index 17333093e..5599d0dfc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Utilities; +using Ryujinx.Common.Memory; using System; using System.Runtime.InteropServices; @@ -6,14 +6,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types { static class RandomMiiConstants { - public static int[] EyeRotateTable = { + public static readonly int[] EyeRotateTable = { 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, }; - public static int[] EyebrowRotateTable = { + public static readonly int[] EyebrowRotateTable = { 0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05, }; @@ -25,27 +25,17 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types Mustache, } - [StructLayout(LayoutKind.Sequential, Pack = 4, Size = ValuesArraySize)] - public struct RandomMiiValues - { - private const int ValuesArraySize = 0xbc; - - private readonly int _firstValueByte; - - public ReadOnlySpan<int> Values => SpanHelpers.AsSpan<RandomMiiValues, int>(ref this); - } - [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0xCC)] public struct RandomMiiData4 { - public int Gender; - public int Age; - public int Race; - public int ValuesCount; + public readonly int Gender; + public readonly int Age; + public readonly int Race; + public readonly int ValuesCount; - private readonly RandomMiiValues _values; + private Array47<int> _values; - public readonly ReadOnlySpan<int> Values => _values.Values[..ValuesCount]; + public ReadOnlySpan<int> Values => _values.AsSpan()[..ValuesCount]; } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0xC8)] @@ -54,22 +44,23 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types private readonly int _argument1; private readonly int _argument2; - public int ValuesCount; + public readonly int ValuesCount; - private readonly RandomMiiValues _values; + private Array47<int> _values; - public readonly ReadOnlySpan<int> Values => _values.Values[..ValuesCount]; + public ReadOnlySpan<int> Values => _values.AsSpan()[..ValuesCount]; } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0xC4)] public struct RandomMiiData2 { private readonly int _argument; - public int ValuesCount; - private readonly RandomMiiValues _values; + public readonly int ValuesCount; - public readonly ReadOnlySpan<int> Values => _values.Values[..ValuesCount]; + private Array47<int> _values; + + public ReadOnlySpan<int> Values => _values.AsSpan()[..ValuesCount]; } public static ReadOnlySpan<RandomMiiData4> RandomMiiFacelineArray => MemoryMarshal.Cast<byte, RandomMiiData4>(RandomMiiFacelineRawArray); From 9e04e6cba1c264907bff47ce408643d5de157b56 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Fri, 21 Jul 2023 12:24:13 +0100 Subject: [PATCH 714/737] Ava UI: Remove `IsActive` checks from dialog methods (#5456) * Remove `IsActive` checks from dialog methods * Remove old windows bandaid * Remove null dialog code path entirely and return nothing. --- src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs | 2 -- src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs | 10 +++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs index d35f44bf5..a2a944297 100644 --- a/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs +++ b/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs @@ -53,8 +53,6 @@ namespace Ryujinx.Ava.UI.Applet bool opened = false; - _parent.Activate(); - UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent, title, message, diff --git a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index 7dab57b88..c8e4db083 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -315,8 +315,10 @@ namespace Ryujinx.Ava.UI.Helpers Window parent = GetMainWindow(); - if (parent is { IsActive: true } and MainWindow window && window.ViewModel.IsGameRunning) + if (parent is MainWindow window && window.ViewModel.IsGameRunning) { + parent.Activate(); + contentDialogOverlayWindow = new() { Height = parent.Bounds.Height, @@ -369,7 +371,9 @@ namespace Ryujinx.Ava.UI.Helpers } else { - result = await contentDialog.ShowAsync(); + result = ContentDialogResult.None; + + Logger.Warning?.Print(LogClass.Ui, "Content dialog overlay failed to populate. Default value has been returned."); } return result; @@ -390,7 +394,7 @@ namespace Ryujinx.Ava.UI.Helpers { foreach (Window item in al.Windows) { - if (item.IsActive && item is MainWindow window) + if (item is MainWindow window) { return window; } From 487261592eb9e9c31cacd08860f8894027bb1a07 Mon Sep 17 00:00:00 2001 From: Mary <mary@mary.zone> Date: Fri, 21 Jul 2023 22:50:10 +0200 Subject: [PATCH 715/737] ava: Fix regression on title updater and dlc manager window caused by precious commit --- src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs index c8e4db083..086de953d 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs @@ -315,7 +315,7 @@ namespace Ryujinx.Ava.UI.Helpers Window parent = GetMainWindow(); - if (parent is MainWindow window && window.ViewModel.IsGameRunning) + if (parent is MainWindow window) { parent.Activate(); From eb528ae0f05f057e671eb9e92f44f1caa9bcc84b Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Mon, 24 Jul 2023 18:35:04 +0200 Subject: [PATCH 716/737] Add workflow to automatically check code style issues for PRs (#4670) * Add workflow to perform automated checks for PRs * Downgrade Microsoft.CodeAnalysis to 4.4.0 This is a workaround to fix issues with dotnet-format. See: - https://github.com/dotnet/format/issues/1805 - https://github.com/dotnet/format/issues/1800 * Adjust editorconfig to be more compatible with Ryujinx code-style * Adjust .editorconfig line endings to match .gitattributes * Disable 'prefer switch expression' rule * Remove naming styles These are the default rules, so we don't need to override them. * Silence IDE0060 in .editorconfig * Slightly adjust .editorconfig * Add lost workflow changes * Move .editorconfig comment to the top * .editorconfig: private static readonly fields should be _lowerCamelCase * .editorconfig: Remove alignment for declarations as well * editorconfig: Add rule for local constants * Disable CA1822 for HLE services * Disable CA1822 for ViewModels Bindings won't work with static members, but this issue is silently ignored. * Run dotnet format for the whole solution * Check result code of SDL_GetDisplayBounds * Fix dotnet format style issues * Add missing trailing commas * Update Microsoft.CodeAnalysis.CSharp to 4.6.0 Skipping 4.5.0 since it breaks dotnet format * Restore old default naming rules for dotnet format * Add naming rule exception for CPU tests * checks: Include all files before excluding paths * Fix dotnet format issues * Check dotnet format version * checks: Run dotnet format with severity info again * checks: Disable naming style rules until they won't crash the process anymore * Remove unread private member * checks: Attempt to run analyzers 3 times before giving up * checks: Enable naming style rules again with the new retry logic --- .editorconfig | 78 ++++++++++++++----- .github/workflows/build.yml | 13 +--- .github/workflows/checks.yml | 71 +++++++++++++++++ .github/workflows/nightly_pr_comment.yml | 4 +- Directory.Packages.props | 2 +- .../Decoders/OpCode32SimdCvtFFixed.cs | 2 +- .../OpenALHardwareDeviceDriver.cs | 2 +- .../OpenALHardwareDeviceSession.cs | 2 +- .../SDL2HardwareDeviceDriver.cs | 3 +- .../Native/SoundIoBackend.cs | 2 +- .../Native/SoundIoDeviceAim.cs | 2 +- src/Ryujinx.Ava/UI/Helpers/Glyph.cs | 2 +- src/Ryujinx.Common/AsyncWorkQueue.cs | 2 +- .../Configuration/BackendThreading.cs | 2 +- .../Configuration/GraphicsBackend.cs | 2 +- .../Configuration/GraphicsDebugLevel.cs | 2 +- .../Motion/MotionInputBackendType.cs | 2 +- .../Configuration/MemoryManagerMode.cs | 2 +- .../GraphicsDriver/NVAPI/Nvapi.cs | 2 +- src/Ryujinx.Common/Logging/LogLevel.cs | 2 +- src/Ryujinx.Cpu/AddressSpace.cs | 6 +- src/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs | 2 +- src/Ryujinx.Graphics.Device/SizeCalculator.cs | 2 +- .../Multithreading/ThreadedRenderer.cs | 2 +- .../Memory/SupportBufferUpdater.cs | 4 +- src/Ryujinx.Graphics.Gpu/Window.cs | 6 +- src/Ryujinx.Graphics.Host1x/ClassId.cs | 2 +- src/Ryujinx.Graphics.Host1x/Host1xClass.cs | 2 +- src/Ryujinx.Graphics.Host1x/OpCode.cs | 2 +- src/Ryujinx.Graphics.Host1x/ThiDevice.cs | 2 +- .../H264/SpsAndPpsReconstruction.cs | 4 +- .../Native/AVLog.cs | 2 +- .../Native/FFmpegApi.cs | 2 +- src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs | 2 +- .../Types/H264/PictureInfo.cs | 2 +- .../Types/Vp8/PictureInfo.cs | 2 +- .../Types/Vp9/FrameFlags.cs | 2 +- .../Types/Vp9/PictureInfo.cs | 2 +- src/Ryujinx.Graphics.OpenGL/EnumConversion.cs | 2 +- src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 2 +- src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs | 2 +- .../Image/TextureCopyIncompatible.cs | 6 +- .../Image/TextureCopyMS.cs | 2 +- .../Image/TextureView.cs | 6 +- src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 2 +- src/Ryujinx.Graphics.OpenGL/Pipeline.cs | 3 +- .../Queries/CounterQueue.cs | 6 +- .../Queries/Counters.cs | 4 +- src/Ryujinx.Graphics.OpenGL/ResourcePool.cs | 2 +- src/Ryujinx.Graphics.OpenGL/Sync.cs | 2 +- .../BufferDescriptor.cs | 2 +- .../CodeGen/Glsl/OperandManager.cs | 2 +- .../CodeGen/Spirv/Declarations.cs | 9 +-- .../StructuredIr/TextureDefinition.cs | 2 +- .../TextureDescriptor.cs | 2 +- .../Translation/ResourceManager.cs | 2 +- .../Astc/AstcDecoder.cs | 4 +- .../Astc/IntegerEncoded.cs | 8 +- src/Ryujinx.Graphics.Texture/ETC2Decoder.cs | 6 +- .../Encoders/BC7Encoder.cs | 4 +- .../Encoders/EncodeMode.cs | 2 +- .../LayoutConverter.cs | 8 +- .../Utils/BC67Tables.cs | 22 +++--- .../Utils/BC67Utils.cs | 2 +- .../Utils/RgbaColor8.cs | 2 +- .../Image/SurfaceReader.cs | 2 +- .../Types/DeinterlaceMode.cs | 2 +- src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs | 2 +- src/Ryujinx.Graphics.Vic/Types/PixelFormat.cs | 2 +- src/Ryujinx.Graphics.Vic/VicDevice.cs | 2 +- src/Ryujinx.Graphics.Video/FrameField.cs | 2 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 2 + .../Services/Mii/Types/RandomMiiConstants.cs | 2 +- .../Nfc/Nfp/NfpManager/Types/DeviceType.cs | 2 +- .../OpenGL/OpenGLWindow.cs | 11 ++- src/Ryujinx.Horizon.Common/ResultNames.cs | 2 +- .../Hipc/CommandArgType.cs | 2 +- .../Hipc/HipcGenerator.cs | 2 +- .../SyscallGenerator.cs | 2 +- .../Sdk/OsTypes/Impl/MultiWaitImpl.cs | 2 +- .../MultiRegionTrackingTests.cs | 4 +- src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs | 2 +- src/Ryujinx/Ui/MainWindow.cs | 2 +- src/Spv.Generator/Instruction.cs | 2 +- 84 files changed, 252 insertions(+), 160 deletions(-) create mode 100644 .github/workflows/checks.yml diff --git a/.editorconfig b/.editorconfig index 8a3054287..e5a5e6174 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,8 +1,7 @@ # Remove the line below if you want to inherit .editorconfig settings from higher directories root = true -# C# files -[*.cs] +[*] #### Core EditorConfig Options #### @@ -12,8 +11,11 @@ indent_style = space tab_width = 4 # New line preferences -end_of_line = crlf -insert_final_newline = false +end_of_line = lf +insert_final_newline = true + +# C# files +[*.cs] #### .NET Coding Conventions #### @@ -59,7 +61,7 @@ dotnet_style_prefer_simplified_interpolation = true:suggestion dotnet_style_readonly_field = true:suggestion # Parameter preferences -dotnet_code_quality_unused_parameters = all:suggestion +dotnet_code_quality_unused_parameters = all:silent #### C# Coding Conventions #### @@ -85,7 +87,7 @@ csharp_style_expression_bodied_properties = true:silent # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_switch_expression = false:silent # Null-checking preferences csharp_style_conditional_delegate_call = true:suggestion @@ -94,6 +96,7 @@ csharp_style_conditional_delegate_call = true:suggestion csharp_prefer_static_local_function = true:suggestion csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent csharp_style_prefer_readonly_struct = true +csharp_style_prefer_method_group_conversion = true # Code-block preferences csharp_prefer_braces = true:silent @@ -109,6 +112,7 @@ csharp_style_prefer_range_operator = true:suggestion csharp_style_throw_expression = true:suggestion csharp_style_unused_value_assignment_preference = discard_variable:suggestion csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_implicit_object_creation_when_type_is_apparent = true # 'using' directive preferences csharp_using_directive_placement = outside_namespace:silent @@ -140,7 +144,6 @@ csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false @@ -158,23 +161,31 @@ csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_blocks = true -csharp_preserve_single_line_statements = true +csharp_preserve_single_line_statements = false #### Naming styles #### # Naming rules -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i +dotnet_naming_rule.interfaces_should_be_prefixed_with_I.severity = suggestion +dotnet_naming_rule.interfaces_should_be_prefixed_with_I.symbols = interface +dotnet_naming_rule.interfaces_should_be_prefixed_with_I.style = IPascalCase dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.types_should_be_pascal_case.style = PascalCase dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = PascalCase + +dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.symbols = private_static_readonly_fields +dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.severity = suggestion +dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.style = _camelCase + +dotnet_naming_rule.local_constants_should_be_pascal_case.symbols = local_constants +dotnet_naming_rule.local_constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_constants_should_be_pascal_case.style = PascalCase # Symbol specifications @@ -190,14 +201,39 @@ dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, meth dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = static, readonly + +dotnet_naming_symbols.local_constants.applicable_kinds = local +dotnet_naming_symbols.local_constants.applicable_accessibilities = local +dotnet_naming_symbols.local_constants.required_modifiers = const + # Naming styles -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_naming_style._camelCase.required_prefix = _ +dotnet_naming_style._camelCase.required_suffix = +dotnet_naming_style._camelCase.word_separator = +dotnet_naming_style._camelCase.capitalization = camel_case -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case \ No newline at end of file +dotnet_naming_style.PascalCase.required_prefix = +dotnet_naming_style.PascalCase.required_suffix = +dotnet_naming_style.PascalCase.word_separator = +dotnet_naming_style.PascalCase.capitalization = pascal_case + +dotnet_naming_style.IPascalCase.required_prefix = I +dotnet_naming_style.IPascalCase.required_suffix = +dotnet_naming_style.IPascalCase.word_separator = +dotnet_naming_style.IPascalCase.capitalization = pascal_case + +[src/Ryujinx.HLE/HOS/Services/**.cs] +# Disable "mark members as static" rule for services +dotnet_diagnostic.CA1822.severity = none + +[src/Ryujinx.Ava/UI/ViewModels/**.cs] +# Disable "mark members as static" rule for ViewModels +dotnet_diagnostic.CA1822.severity = none + +[src/Ryujinx.Tests/Cpu/*.cs] +# Disable naming rules for CPU tests +dotnet_diagnostic.IDE1006.severity = none diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf8fd000a..c93fd0d30 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,19 +1,10 @@ name: Build job on: - workflow_dispatch: - inputs: {} - pull_request: - branches: [ master ] - paths-ignore: - - '.github/**' - - '*.yml' - - '*.json' - - '*.config' - - 'README.md' + workflow_call: concurrency: - group: pr-checks-${{ github.event.number }} + group: pr-builds-${{ github.event.number }} cancel-in-progress: true env: diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 000000000..c34d196f2 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,71 @@ +name: Perform checks + +on: + pull_request: + branches: [ master ] + paths: + - '**' + - '!.github/**' + - '!*.yml' + - '!*.config' + - '!README.md' + - '.github/workflows/*.yml' + +permissions: + pull-requests: write + checks: write + +concurrency: + group: pr-checks-${{ github.event.number }} + cancel-in-progress: true + +jobs: + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/setup-dotnet@v3 + with: + global-json-file: global.json + + - run: dotnet restore + + - name: Print dotnet format version + run: dotnet format --version + + - name: Run dotnet format whitespace + run: | + dotnet format whitespace --verify-no-changes --report ./whitespace-report.json -v d + + - name: Run dotnet format style + run: | + dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d + + # For some reason this step sometimes fails with exit code 139 (segfault?), + # so should that be the case we'll try again (3 tries max). + - name: Run dotnet format analyzers + run: | + attempt=0 + exit_code=139 + until [ $attempt -ge 3 ] || [ $exit_code -ne 139 ]; do + ((attempt+=1)) + exit_code=0 + echo "Attempt: ${attempt}/3" + dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d || exit_code=$? + done + exit $exit_code + + - name: Upload report + if: failure() + uses: actions/upload-artifact@v3 + with: + name: dotnet-format + path: ./*-report.json + + pr_build: + uses: ./.github/workflows/build.yml + needs: format + secrets: inherit diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index deabae670..f59a6be1f 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -1,8 +1,10 @@ name: Comment PR artifacts links + on: workflow_run: - workflows: ['Build job'] + workflows: ['Perform checks'] types: [completed] + jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' diff --git a/Directory.Packages.props b/Directory.Packages.props index 4b6bb1917..bc740afcf 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -20,7 +20,7 @@ <PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" /> <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> - <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> + <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> diff --git a/src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs b/src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs index f8564d0e5..32faad1ec 100644 --- a/src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs +++ b/src/ARMeilleure/Decoders/OpCode32SimdCvtFFixed.cs @@ -20,4 +20,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs index 92946900f..86a2efa40 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Backends.OpenAL _stillRunning = true; _updaterThread = new Thread(Update) { - Name = "HardwareDeviceDriver.OpenAL" + Name = "HardwareDeviceDriver.OpenAL", }; _updaterThread.Start(); diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs index 4a2d521fe..ee177c311 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs @@ -67,7 +67,7 @@ namespace Ryujinx.Audio.Backends.OpenAL { DriverIdentifier = buffer.DataPointer, BufferId = AL.GenBuffer(), - SampleCount = GetSampleCount(buffer) + SampleCount = GetSampleCount(buffer), }; AL.BufferData(driverBuffer.BufferId, _targetFormat, buffer.Data, (int)RequestedSampleRate); diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index 550cc3491..f1de42b01 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Concurrent; using System.Runtime.InteropServices; using System.Threading; - using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; using static SDL2.SDL; @@ -111,7 +110,7 @@ namespace Ryujinx.Audio.Backends.SDL2 channels = (byte)requestedChannelCount, format = GetSDL2Format(requestedSampleFormat), freq = (int)requestedSampleRate, - samples = (ushort)sampleCount + samples = (ushort)sampleCount, }; } diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs index 4e9123a0f..7094b7d5b 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs @@ -8,6 +8,6 @@ Alsa = 3, CoreAudio = 4, Wasapi = 5, - Dummy = 6 + Dummy = 6, } } diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs index a0689d6d6..a1943810d 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs @@ -3,6 +3,6 @@ public enum SoundIoDeviceAim { SoundIoDeviceAimInput = 0, - SoundIoDeviceAimOutput = 1 + SoundIoDeviceAimOutput = 1, } } diff --git a/src/Ryujinx.Ava/UI/Helpers/Glyph.cs b/src/Ryujinx.Ava/UI/Helpers/Glyph.cs index 4aae854f7..e30de69bb 100644 --- a/src/Ryujinx.Ava/UI/Helpers/Glyph.cs +++ b/src/Ryujinx.Ava/UI/Helpers/Glyph.cs @@ -4,6 +4,6 @@ { List, Grid, - Chip + Chip, } } diff --git a/src/Ryujinx.Common/AsyncWorkQueue.cs b/src/Ryujinx.Common/AsyncWorkQueue.cs index 746ef4cac..e9f758985 100644 --- a/src/Ryujinx.Common/AsyncWorkQueue.cs +++ b/src/Ryujinx.Common/AsyncWorkQueue.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Common _workerThread = new Thread(DoWork) { Name = name, - IsBackground = true + IsBackground = true, }; _workerThread.Start(); } diff --git a/src/Ryujinx.Common/Configuration/BackendThreading.cs b/src/Ryujinx.Common/Configuration/BackendThreading.cs index 8833b3f07..44aa422e4 100644 --- a/src/Ryujinx.Common/Configuration/BackendThreading.cs +++ b/src/Ryujinx.Common/Configuration/BackendThreading.cs @@ -8,6 +8,6 @@ namespace Ryujinx.Common.Configuration { Auto, Off, - On + On, } } diff --git a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs index d74dd6e19..8957c5ccc 100644 --- a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Common.Configuration public enum GraphicsBackend { Vulkan, - OpenGl + OpenGl, } } diff --git a/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs b/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs index ad12302a6..dfe28405c 100644 --- a/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs +++ b/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs @@ -9,6 +9,6 @@ namespace Ryujinx.Common.Configuration None, Error, Slowdowns, - All + All, } } diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs index c65510478..fac4e0f7a 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs @@ -8,6 +8,6 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion { Invalid, GamepadDriver, - CemuHook + CemuHook, } } diff --git a/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs b/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs index f10fd6f1b..8b7150b5c 100644 --- a/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs +++ b/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs @@ -8,6 +8,6 @@ namespace Ryujinx.Common.Configuration { SoftwarePageTable, HostMapped, - HostMappedUnsafe + HostMappedUnsafe, } } diff --git a/src/Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs b/src/Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs index 99eaa68f4..60b9455c7 100644 --- a/src/Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs +++ b/src/Ryujinx.Common/GraphicsDriver/NVAPI/Nvapi.cs @@ -6,6 +6,6 @@ OglThreadControlDefault = 0, OglThreadControlEnable = 1, - OglThreadControlDisable = 2 + OglThreadControlDisable = 2, } } diff --git a/src/Ryujinx.Common/Logging/LogLevel.cs b/src/Ryujinx.Common/Logging/LogLevel.cs index 3786c7561..54261cee5 100644 --- a/src/Ryujinx.Common/Logging/LogLevel.cs +++ b/src/Ryujinx.Common/Logging/LogLevel.cs @@ -14,6 +14,6 @@ namespace Ryujinx.Common.Logging Guest, AccessLog, Notice, - Trace + Trace, } } diff --git a/src/Ryujinx.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs index c467eb622..beea14bee 100644 --- a/src/Ryujinx.Cpu/AddressSpace.cs +++ b/src/Ryujinx.Cpu/AddressSpace.cs @@ -179,7 +179,7 @@ namespace Ryujinx.Cpu { addressSpace = null; - const MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; + const MemoryAllocationFlags AsFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible; ulong minAddressSpaceSize = Math.Min(asSize, 1UL << 36); @@ -191,8 +191,8 @@ namespace Ryujinx.Cpu try { - baseMemory = new MemoryBlock(addressSpaceSize, asFlags); - mirrorMemory = new MemoryBlock(addressSpaceSize, asFlags); + baseMemory = new MemoryBlock(addressSpaceSize, AsFlags); + mirrorMemory = new MemoryBlock(addressSpaceSize, AsFlags); addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize, supports4KBPages); break; diff --git a/src/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs b/src/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs index 95e674327..8935f5247 100644 --- a/src/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs +++ b/src/Ryujinx.Cpu/AppleHv/Arm/ApFlags.cs @@ -22,6 +22,6 @@ namespace Ryujinx.Cpu.AppleHv.Arm UserNoneKernelReadWrite = (1UL << (int)PxnShift) | (1UL << (int)UxnShift) | (0UL << (int)ApShift), UserNoneKernelRead = (1UL << (int)PxnShift) | (1UL << (int)UxnShift) | (2UL << (int)ApShift), - UserReadKernelRead = (1UL << (int)PxnShift) | (1UL << (int)UxnShift) | (3UL << (int)ApShift) + UserReadKernelRead = (1UL << (int)PxnShift) | (1UL << (int)UxnShift) | (3UL << (int)ApShift), } } diff --git a/src/Ryujinx.Graphics.Device/SizeCalculator.cs b/src/Ryujinx.Graphics.Device/SizeCalculator.cs index 7409c0418..d5697fe5e 100644 --- a/src/Ryujinx.Graphics.Device/SizeCalculator.cs +++ b/src/Ryujinx.Graphics.Device/SizeCalculator.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Device TypeCode.Double => sizeof(double), TypeCode.Decimal => sizeof(decimal), TypeCode.Boolean => sizeof(bool), - _ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown.") + _ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown."), }; } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index e59a3928b..b11ccb0a3 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -412,7 +412,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved) { - ThreadedCounterEvent evt = new ThreadedCounterEvent(this, type, _lastSampleCounterClear); + ThreadedCounterEvent evt = new(this, type, _lastSampleCounterClear); New<ReportCounterCommand>().Set(Ref(evt), type, Ref(resultHandler), divisor, hostReserved); QueueCommand(); diff --git a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs index 3ea37c55a..50c042fb9 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs @@ -187,7 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Memory X = scale * 2f / viewportWidth, Y = scale * 2f / viewportHeight, Z = 1, - W = disableTransformF + W = disableTransformF, }); } } @@ -210,7 +210,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ReadOnlySpan<byte> data = MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref _data, 1)); - _renderer.SetBufferData(_handle, _startOffset, data.Slice(_startOffset, _endOffset - _startOffset)); + _renderer.SetBufferData(_handle, _startOffset, data[_startOffset.._endOffset]); _startOffset = -1; _endOffset = -1; diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs index 89addc8a8..1f94122d8 100644 --- a/src/Ryujinx.Graphics.Gpu/Window.cs +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -8,8 +8,6 @@ using System.Threading; namespace Ryujinx.Graphics.Gpu { - using Texture = Image.Texture; - /// <summary> /// GPU image presentation window. /// </summary> @@ -202,13 +200,13 @@ namespace Ryujinx.Graphics.Gpu { pt.AcquireCallback(_context, pt.UserObj); - Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, pt.Range); + Image.Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, pt.Range); pt.Cache.Tick(); texture.SynchronizeMemory(); - ImageCrop crop = new ImageCrop( + ImageCrop crop = new( (int)(pt.Crop.Left * texture.ScaleFactor), (int)MathF.Ceiling(pt.Crop.Right * texture.ScaleFactor), (int)(pt.Crop.Top * texture.ScaleFactor), diff --git a/src/Ryujinx.Graphics.Host1x/ClassId.cs b/src/Ryujinx.Graphics.Host1x/ClassId.cs index dfeadd4c4..6da461ef8 100644 --- a/src/Ryujinx.Graphics.Host1x/ClassId.cs +++ b/src/Ryujinx.Graphics.Host1x/ClassId.cs @@ -15,6 +15,6 @@ Tsec = 0xe0, Tsecb = 0xe1, Nvjpg = 0xc0, - Nvdec = 0xf0 + Nvdec = 0xf0, } } diff --git a/src/Ryujinx.Graphics.Host1x/Host1xClass.cs b/src/Ryujinx.Graphics.Host1x/Host1xClass.cs index 1a1297f91..6f5ba3256 100644 --- a/src/Ryujinx.Graphics.Host1x/Host1xClass.cs +++ b/src/Ryujinx.Graphics.Host1x/Host1xClass.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Host1x _syncMgr = syncMgr; _state = new DeviceState<Host1xClassRegisters>(new Dictionary<string, RwCallback> { - { nameof(Host1xClassRegisters.WaitSyncpt32), new RwCallback(WaitSyncpt32, null) } + { nameof(Host1xClassRegisters.WaitSyncpt32), new RwCallback(WaitSyncpt32, null) }, }); } diff --git a/src/Ryujinx.Graphics.Host1x/OpCode.cs b/src/Ryujinx.Graphics.Host1x/OpCode.cs index 2ec6034b3..39b19b1d1 100644 --- a/src/Ryujinx.Graphics.Host1x/OpCode.cs +++ b/src/Ryujinx.Graphics.Host1x/OpCode.cs @@ -16,6 +16,6 @@ NonIncrW, GatherW, RestartW, - Extend + Extend, } } diff --git a/src/Ryujinx.Graphics.Host1x/ThiDevice.cs b/src/Ryujinx.Graphics.Host1x/ThiDevice.cs index 259c88366..ecdf0f75c 100644 --- a/src/Ryujinx.Graphics.Host1x/ThiDevice.cs +++ b/src/Ryujinx.Graphics.Host1x/ThiDevice.cs @@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Host1x _state = new DeviceState<ThiRegisters>(new Dictionary<string, RwCallback> { { nameof(ThiRegisters.IncrSyncpt), new RwCallback(IncrSyncpt, null) }, - { nameof(ThiRegisters.Method1), new RwCallback(Method1, null) } + { nameof(ThiRegisters.Method1), new RwCallback(Method1, null) }, }); _previousContextId = -1; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs index d8bf6f746..82c5932b7 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs @@ -127,7 +127,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 + 53, 60, 61, 54, 47, 55, 62, 63, }; private static ReadOnlySpan<byte> ZigZagScan => new byte[] @@ -135,7 +135,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4, 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, - 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4 + 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4, }; private static void WriteScalingList(ref H264BitStreamWriter writer, IArray<byte> list) diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs index 4224de9eb..fda014a88 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs @@ -10,6 +10,6 @@ Verbose = 40, Debug = 48, Trace = 56, - MaxOffset = 64 + MaxOffset = 64, } } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs index 262d26431..e04b1fc49 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new() { { AvCodecLibraryName, (58, 59) }, - { AvUtilLibraryName, (56, 57) } + { AvUtilLibraryName, (56, 57) }, }; private static string FormatLibraryNameForCurrentOs(string libraryName, int version) diff --git a/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs b/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs index 4ab7886d0..833ee2d42 100644 --- a/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs +++ b/src/Ryujinx.Graphics.Nvdec/NvdecDevice.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Nvdec _rm = new ResourceManager(gmm, new SurfaceCache(gmm)); _state = new DeviceState<NvdecRegisters>(new Dictionary<string, RwCallback> { - { nameof(NvdecRegisters.Execute), new RwCallback(Execute, null) } + { nameof(NvdecRegisters.Execute), new RwCallback(Execute, null) }, }); _contexts = new ConcurrentDictionary<long, NvdecDecoderContext>(); } diff --git a/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs index c0e0a463d..aff88a965 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/H264/PictureInfo.cs @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.H264 FrameType = 0, PicWidthInMbsMinus1 = PicWidthInMbs - 1, PicHeightInMapUnitsMinus1 = (PicHeightInMbs >> (FrameMbsOnlyFlag != 0 ? 0 : 1)) - 1, - QpprimeYZeroTransformBypassFlag = QpprimeYZeroTransformBypassFlag + QpprimeYZeroTransformBypassFlag = QpprimeYZeroTransformBypassFlag, }; } } diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs index 76e07a3ff..1c4e36c90 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp8/PictureInfo.cs @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp8 FirstPartSize = FirstPartSize, Version = Version, FrameWidth = FrameWidth, - FrameHeight = FrameHeight + FrameHeight = FrameHeight, }; } } diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs index 88f1ac205..966620811 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/FrameFlags.cs @@ -7,6 +7,6 @@ FrameSizeChanged = 1 << 2, ErrorResilientMode = 1 << 3, LastShowFrame = 1 << 4, - IntraOnly = 1 << 5 + IntraOnly = 1 << 5, } } diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs index 50569dbff..371680dee 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs @@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 SegmentFeatureData = Seg.FeatureData, ModeRefDeltaEnabled = Lf.ModeRefDeltaEnabled != 0, RefDeltas = Lf.RefDeltas, - ModeDeltas = Lf.ModeDeltas + ModeDeltas = Lf.ModeDeltas, }; } } diff --git a/src/Ryujinx.Graphics.OpenGL/EnumConversion.cs b/src/Ryujinx.Graphics.OpenGL/EnumConversion.cs index c9a1eaa93..417dc3f60 100644 --- a/src/Ryujinx.Graphics.OpenGL/EnumConversion.cs +++ b/src/Ryujinx.Graphics.OpenGL/EnumConversion.cs @@ -668,7 +668,7 @@ namespace Ryujinx.Graphics.OpenGL ShaderStage.TessellationEvaluation => ShaderType.TessEvaluationShader, ShaderStage.Geometry => ShaderType.GeometryShader, ShaderStage.Fragment => ShaderType.FragmentShader, - _ => ShaderType.VertexShader + _ => ShaderType.VertexShader, }; } } diff --git a/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index d7d993459..617b129ab 100644 --- a/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/src/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.OpenGL AmdUnix, IntelWindows, IntelUnix, - Nvidia + Nvidia, } private static readonly Lazy<GpuVendor> _gpuVendor = new(GetGpuVendor); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs b/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs index 18dd1b72a..6cf048296 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/Sampler.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.OpenGL.Image info.BorderColor.Red, info.BorderColor.Green, info.BorderColor.Blue, - info.BorderColor.Alpha + info.BorderColor.Alpha, }; GL.SamplerParameter(Handle, SamplerParameterName.TextureBorderColor, borderColor); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs index 6b5fb376b..082e6ccf8 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyIncompatible.cs @@ -133,7 +133,7 @@ void main() 1 => SizedInternalFormat.R8ui, 2 => SizedInternalFormat.Rg8ui, 4 => SizedInternalFormat.Rgba8ui, - _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + _ => throw new ArgumentException($"Invalid components count {componentsCount}."), }; } else if (componentSize == 2) @@ -143,7 +143,7 @@ void main() 1 => SizedInternalFormat.R16ui, 2 => SizedInternalFormat.Rg16ui, 4 => SizedInternalFormat.Rgba16ui, - _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + _ => throw new ArgumentException($"Invalid components count {componentsCount}."), }; } else if (componentSize == 4) @@ -153,7 +153,7 @@ void main() 1 => SizedInternalFormat.R32ui, 2 => SizedInternalFormat.Rg32ui, 4 => SizedInternalFormat.Rgba32ui, - _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + _ => throw new ArgumentException($"Invalid components count {componentsCount}."), }; } else diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs index 39f232723..0fa6453dc 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs @@ -173,7 +173,7 @@ void main() 4 => SizedInternalFormat.R32ui, 8 => SizedInternalFormat.Rg32ui, 16 => SizedInternalFormat.Rgba32ui, - _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.") + _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}."), }; } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index f4b1e0dad..0f5fe46a5 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.OpenGL.Image (int)Info.SwizzleR.Convert(), (int)Info.SwizzleG.Convert(), (int)Info.SwizzleB.Convert(), - (int)Info.SwizzleA.Convert() + (int)Info.SwizzleA.Convert(), }; if (Info.Format == Format.A1B5G5R5Unorm) @@ -210,7 +210,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { Target.Texture2DMultisample => Target.Texture2D, Target.Texture2DMultisampleArray => Target.Texture2DArray, - _ => Target + _ => Target, }; TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( @@ -354,7 +354,7 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTarget.TextureCubeMapArray => (layer / 6) * mipSize, TextureTarget.Texture1DArray => layer * mipSize, TextureTarget.Texture2DArray => layer * mipSize, - _ => 0 + _ => 0, }; } diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 1ad927ea6..47b832f28 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -210,7 +210,7 @@ namespace Ryujinx.Graphics.OpenGL GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8)); } - _counters.Initialize(_pipeline); + _counters.Initialize(); // This is required to disable [0, 1] clamping for SNorm outputs on compatibility profiles. // This call is expected to fail if we're running with a core profile, diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 85f5b1139..923c85d7e 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -5,7 +5,6 @@ using Ryujinx.Graphics.OpenGL.Image; using Ryujinx.Graphics.OpenGL.Queries; using Ryujinx.Graphics.Shader; using System; -using System.Runtime.CompilerServices; namespace Ryujinx.Graphics.OpenGL { @@ -44,7 +43,7 @@ namespace Ryujinx.Graphics.OpenGL private CounterQueueEvent _activeConditionalRender; - private Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount]; + private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount]; private readonly (TextureBase, Format)[] _images; private TextureBase _unit0Texture; diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index 1724616ea..c430ffc34 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -13,8 +13,6 @@ namespace Ryujinx.Graphics.OpenGL.Queries public CounterType Type { get; } public bool Disposed { get; private set; } - private readonly Pipeline _pipeline; - private readonly Queue<CounterQueueEvent> _events = new(); private CounterQueueEvent _current; @@ -30,12 +28,10 @@ namespace Ryujinx.Graphics.OpenGL.Queries private readonly Thread _consumerThread; - internal CounterQueue(Pipeline pipeline, CounterType type) + internal CounterQueue(CounterType type) { Type = type; - _pipeline = pipeline; - QueryTarget glType = GetTarget(Type); _queryPool = new Queue<BufferedQuery>(QueryPoolInitialSize); diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index 88e8bf917..1530c9d26 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -14,12 +14,12 @@ namespace Ryujinx.Graphics.OpenGL.Queries _counterQueues = new CounterQueue[count]; } - public void Initialize(Pipeline pipeline) + public void Initialize() { for (int index = 0; index < _counterQueues.Length; index++) { CounterType type = (CounterType)index; - _counterQueues[index] = new CounterQueue(pipeline, type); + _counterQueues[index] = new CounterQueue(type); } } diff --git a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs index 43410c99b..20ef9e87a 100644 --- a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs +++ b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.OpenGL { Info = view.Info, View = view, - RemainingFrames = DisposedLiveFrames + RemainingFrames = DisposedLiveFrames, }); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Sync.cs b/src/Ryujinx.Graphics.OpenGL/Sync.cs index 54e9c6d3f..b7ee3d3c1 100644 --- a/src/Ryujinx.Graphics.OpenGL/Sync.cs +++ b/src/Ryujinx.Graphics.OpenGL/Sync.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.OpenGL SyncHandle handle = new() { ID = id, - Handle = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None) + Handle = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None), }; diff --git a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs index d8cad1df8..ead1c5e67 100644 --- a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Shader { - public struct BufferDescriptor + public readonly struct BufferDescriptor { // New fields should be added to the end of the struct to keep disk shader cache compatibility. diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 17ffad9a5..9346341f8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { class OperandManager { - private Dictionary<AstOperand, string> _locals; + private readonly Dictionary<AstOperand, string> _locals; public OperandManager() { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 2c849cd49..b14fead8a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -13,8 +13,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { static class Declarations { - private static readonly string[] _stagePrefixes = { "cp", "vp", "tcp", "tep", "gp", "fp" }; - public static void DeclareParameters(CodeGenContext context, StructuredFunction function) { DeclareParameters(context, function.InArguments, 0); @@ -192,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv SamplerType.Texture3D => Dim.Dim3D, SamplerType.TextureCube => Dim.Cube, SamplerType.TextureBuffer => Dim.Buffer, - _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\".") + _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."), }; var imageType = context.TypeImage( @@ -519,10 +517,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv : (isOutput ? context.Outputs : context.Inputs); dict.Add(ioDefinition, spvVar); } - - private static string GetStagePrefix(ShaderStage stage) - { - return _stagePrefixes[(int)stage]; - } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs index ba31dae6c..e45c82854 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs @@ -24,4 +24,4 @@ namespace Ryujinx.Graphics.Shader return new TextureDefinition(Set, Binding, Name, Type, Format, Flags | flag); } } -} \ No newline at end of file +} diff --git a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs index 58b8e5a51..1130b63b8 100644 --- a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Shader { - public struct TextureDescriptor + public readonly struct TextureDescriptor { // New fields should be added to the end of the struct to keep disk shader cache compatibility. diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 5991cd25d..555acd35c 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.Shader.Translation { AccurateType = accurateType, Type = type, - UsageFlags = usageFlags + UsageFlags = usageFlags, }; int binding; diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index 6f633e4ae..1f781d2f8 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -889,7 +889,7 @@ namespace Ryujinx.Graphics.Texture.Astc 0 => 0, 1 => 32, 2 => 63, - _ => 0 + _ => 0, }; break; @@ -942,7 +942,7 @@ namespace Ryujinx.Graphics.Texture.Astc 2 => 32, 3 => 47, 4 => 63, - _ => 0 + _ => 0, }; break; diff --git a/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs b/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs index 56e78cc36..361140ddd 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Texture.Astc { JustBits, Quint, - Trit + Trit, } readonly EIntegerEncoding _encoding; @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Texture.Astc IntegerEncoded intEncoded = new(EIntegerEncoding.Quint, numberBitsPerValue) { BitValue = m[i], - QuintValue = encodings[i] + QuintValue = encodings[i], }; listIntegerEncoded.Add(ref intEncoded); @@ -309,7 +309,7 @@ namespace Ryujinx.Graphics.Texture.Astc 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 0, 2, 1, 1, 2, 1, 2, 1, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 1, 2, 0, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 1, 2, 2, 2 + 2, 1, 2, 2, 2, }; private static ReadOnlySpan<byte> QuintEncodings => new byte[] @@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.Texture.Astc 0, 1, 4, 1, 1, 4, 0, 2, 3, 1, 2, 3, 2, 2, 3, 3, 2, 3, 4, 2, 3, 2, 4, 3, 0, 2, 4, 1, 2, 4, 0, 3, 3, 1, 3, 3, 2, 3, 3, 3, 3, 3, 4, 3, 3, - 3, 4, 3, 0, 3, 4, 1, 3, 4 + 3, 4, 3, 0, 3, 4, 1, 3, 4, }; } } diff --git a/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs b/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs index 21ff4be4b..57f2e98d0 100644 --- a/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs +++ b/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs @@ -21,12 +21,12 @@ namespace Ryujinx.Graphics.Texture new int[] { 18, 60, -18, -60 }, new int[] { 24, 80, -24, -80 }, new int[] { 33, 106, -33, -106 }, - new int[] { 47, 183, -47, -183 } + new int[] { 47, 183, -47, -183 }, }; private static readonly int[] _etc2Lut = { - 3, 6, 11, 16, 23, 32, 41, 64 + 3, 6, 11, 16, 23, 32, 41, 64, }; private static readonly int[][] _etc2AlphaLut = @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Texture new int[] { -3, -4, -7, -10, 2, 3, 6, 9 }, new int[] { -1, -2, -3, -10, 0, 1, 2, 9 }, new int[] { -4, -6, -8, -9, 3, 5, 7, 8 }, - new int[] { -3, -5, -7, -9, 2, 4, 6, 8 } + new int[] { -3, -5, -7, -9, 2, 4, 6, 8 }, }; public static byte[] DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) diff --git a/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs b/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs index d0d1666a0..21aa225a7 100644 --- a/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs +++ b/src/Ryujinx.Graphics.Texture/Encoders/BC7Encoder.cs @@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Texture.Encoders private static readonly int[] _mostFrequentPartitions = new int[] { - 0, 13, 2, 1, 15, 14, 10, 23 + 0, 13, 2, 1, 15, 14, 10, 23, }; private static Block CompressBlock(ReadOnlySpan<byte> data, int x, int y, int width, int height, bool fastMode) @@ -233,7 +233,7 @@ namespace Ryujinx.Graphics.Texture.Encoders 1 => new RgbaColor8(255, 0, 0, 0).ToUInt32(), 2 => new RgbaColor8(0, 255, 0, 0).ToUInt32(), 3 => new RgbaColor8(0, 0, 255, 0).ToUInt32(), - _ => new RgbaColor8(0, 0, 0, 255).ToUInt32() + _ => new RgbaColor8(0, 0, 0, 255).ToUInt32(), }; } else diff --git a/src/Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs b/src/Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs index 5734d301e..8db3b3c0d 100644 --- a/src/Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs +++ b/src/Ryujinx.Graphics.Texture/Encoders/EncodeMode.cs @@ -5,6 +5,6 @@ Fast, Exhaustive, ModeMask = 0xff, - Multithreaded = 1 << 8 + Multithreaded = 1 << 8, } } diff --git a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs index 0f7c304e0..d9a666c3f 100644 --- a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Texture 8 => Convert<ulong>(dst, data), 12 => Convert<Bpp12Pixel>(dst, data), 16 => Convert<Vector128<byte>>(dst, data), - _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format.") + _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format."), }; } @@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Texture 8 => Convert<ulong>(output, data), 12 => Convert<Bpp12Pixel>(output, data), 16 => Convert<Vector128<byte>>(output, data), - _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format.") + _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format."), }; } return output; @@ -359,7 +359,7 @@ namespace Ryujinx.Graphics.Texture 8 => Convert<ulong>(dst, data), 12 => Convert<Bpp12Pixel>(dst, data), 16 => Convert<Vector128<byte>>(dst, data), - _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format.") + _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format."), }; } @@ -504,7 +504,7 @@ namespace Ryujinx.Graphics.Texture 8 => Convert<ulong>(output, data), 12 => Convert<Bpp12Pixel>(output, data), 16 => Convert<Vector128<byte>>(output, data), - _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format.") + _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format."), }; } diff --git a/src/Ryujinx.Graphics.Texture/Utils/BC67Tables.cs b/src/Ryujinx.Graphics.Texture/Utils/BC67Tables.cs index d890652cb..f1745d235 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/BC67Tables.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/BC67Tables.cs @@ -11,21 +11,21 @@ new BC7ModeInfo(1, 0, 0, 2, 1, 2, 3, 5, 6), new BC7ModeInfo(1, 0, 0, 2, 0, 2, 2, 7, 8), new BC7ModeInfo(1, 0, 2, 0, 0, 4, 0, 7, 7), - new BC7ModeInfo(2, 6, 4, 0, 0, 2, 0, 5, 5) + new BC7ModeInfo(2, 6, 4, 0, 0, 2, 0, 5, 5), }; public static readonly byte[][] Weights = { new byte[] { 0, 21, 43, 64 }, new byte[] { 0, 9, 18, 27, 37, 46, 55, 64 }, - new byte[] { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 } + new byte[] { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 }, }; public static readonly byte[][] InverseWeights = { new byte[] { 64, 43, 21, 0 }, new byte[] { 64, 55, 46, 37, 27, 18, 9, 0 }, - new byte[] { 64, 60, 55, 51, 47, 43, 38, 34, 30, 26, 21, 17, 13, 9, 4, 0 } + new byte[] { 64, 60, 55, 51, 47, 43, 38, 34, 30, 26, 21, 17, 13, 9, 4, 0 }, }; public static readonly byte[][][] FixUpIndices = new byte[3][][] @@ -47,7 +47,7 @@ new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 } + new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, }, new byte[64][] { @@ -66,7 +66,7 @@ new byte[] { 0, 6, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 6, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 15, 0 } + new byte[] { 0, 15, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 15, 0 }, }, new byte[64][] { @@ -85,8 +85,8 @@ new byte[] { 0, 3, 15 }, new byte[] { 0, 15, 3 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 8, 15 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 10, 15 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 10, 15 }, new byte[] { 0, 8, 15 }, new byte[] { 0, 13, 15 }, - new byte[] { 0, 15, 3 }, new byte[] { 0, 12, 15 }, new byte[] { 0, 3, 15 }, new byte[] { 0, 3, 8 } - } + new byte[] { 0, 15, 3 }, new byte[] { 0, 12, 15 }, new byte[] { 0, 3, 15 }, new byte[] { 0, 3, 8 }, + }, }; public static readonly byte[][][] PartitionTable = new byte[3][][] @@ -156,7 +156,7 @@ new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 60 new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 61 new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 62 - new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // 63 + new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 63 }, new byte[64][] { @@ -223,7 +223,7 @@ new byte[16] { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 }, // 60 new byte[16] { 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, // 61 new byte[16] { 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0 }, // 62 - new byte[16] { 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 } // 63 + new byte[16] { 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 }, // 63 }, new byte[64][] { @@ -290,8 +290,8 @@ new byte[16] { 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1 }, // 60 new byte[16] { 0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2 }, // 61 new byte[16] { 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // 62 - new byte[16] { 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0 } // 63 - } + new byte[16] { 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0 }, // 63 + }, }; } } diff --git a/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs b/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs index 3f69cb4c1..0916cc2ca 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/BC67Utils.cs @@ -1199,7 +1199,7 @@ namespace Ryujinx.Graphics.Texture.Utils RgbaColor32 weightV = new(colorWeight) { - A = alphaWeight + A = alphaWeight, }; RgbaColor32 invWeightV = new RgbaColor32(64) - weightV; diff --git a/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs index 401bec387..eced3d75f 100644 --- a/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs +++ b/src/Ryujinx.Graphics.Texture/Utils/RgbaColor8.cs @@ -77,7 +77,7 @@ namespace Ryujinx.Graphics.Texture.Utils 1 => G, 2 => B, 3 => A, - _ => throw new ArgumentOutOfRangeException(nameof(index)) + _ => throw new ArgumentOutOfRangeException(nameof(index)), }; } } diff --git a/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs b/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs index 079b4ef12..5175d911a 100644 --- a/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs +++ b/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs @@ -417,7 +417,7 @@ namespace Ryujinx.Graphics.Vic.Image 0 => offsets.LumaOffset, 1 => offsets.ChromaUOffset, 2 => offsets.ChromaVOffset, - _ => throw new ArgumentOutOfRangeException(nameof(plane)) + _ => throw new ArgumentOutOfRangeException(nameof(plane)), }; } diff --git a/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs b/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs index 216995b39..f1b729f08 100644 --- a/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs +++ b/src/Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs @@ -7,6 +7,6 @@ namespace Ryujinx.Graphics.Vic.Types Bob, NewBob, Disi1, - WeaveLumaBobFieldChroma + WeaveLumaBobFieldChroma, } } diff --git a/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs b/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs index f6007f924..b8f821631 100644 --- a/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs +++ b/src/Ryujinx.Graphics.Vic/Types/FrameFormat.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Vic.Types TopFieldChromaBottom, BottomFieldChromaTop, SubPicTopFieldChromaBottom, - SubPicBottomFieldChromaTop + SubPicBottomFieldChromaTop, } static class FrameFormatExtensions diff --git a/src/Ryujinx.Graphics.Vic/Types/PixelFormat.cs b/src/Ryujinx.Graphics.Vic/Types/PixelFormat.cs index 72dc78994..8f5eda8b6 100644 --- a/src/Ryujinx.Graphics.Vic/Types/PixelFormat.cs +++ b/src/Ryujinx.Graphics.Vic/Types/PixelFormat.cs @@ -76,6 +76,6 @@ Y8___U8___V8_N422R, Y8___U8___V8_N420, U8, - V8 + V8, } } diff --git a/src/Ryujinx.Graphics.Vic/VicDevice.cs b/src/Ryujinx.Graphics.Vic/VicDevice.cs index b2bc98d81..5053c0cc5 100644 --- a/src/Ryujinx.Graphics.Vic/VicDevice.cs +++ b/src/Ryujinx.Graphics.Vic/VicDevice.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vic _rm = new ResourceManager(gmm, new BufferPool<Pixel>(), new BufferPool<byte>()); _state = new DeviceState<VicRegisters>(new Dictionary<string, RwCallback> { - { nameof(VicRegisters.Execute), new RwCallback(Execute, null) } + { nameof(VicRegisters.Execute), new RwCallback(Execute, null) }, }); } diff --git a/src/Ryujinx.Graphics.Video/FrameField.cs b/src/Ryujinx.Graphics.Video/FrameField.cs index eafd74e07..dbfd5c446 100644 --- a/src/Ryujinx.Graphics.Video/FrameField.cs +++ b/src/Ryujinx.Graphics.Video/FrameField.cs @@ -3,6 +3,6 @@ namespace Ryujinx.Graphics.Video public enum FrameField { Progressive, - Interlaced + Interlaced, } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index b0bc1d0d0..8b931e526 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -709,12 +709,14 @@ namespace Ryujinx.Graphics.Vulkan return CommandBuffer.Handle == cb.Handle; } +#pragma warning disable CA1822 // Mark member as static public void SetAlphaTest(bool enable, float reference, CompareOp op) { // This is currently handled using shader specialization, as Vulkan 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. } +#pragma warning restore CA1822 public void SetBlendState(AdvancedBlendDescriptor blend) { diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs index 5599d0dfc..590f0f01e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs @@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - , }; + }; private static ReadOnlySpan<byte> RandomMiiFacelineColorRawArray => new byte[] { diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs index 44c8a79a4..17b09eb78 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs @@ -2,6 +2,6 @@ { enum DeviceType : uint { - Amiibo + Amiibo, } } diff --git a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs index 355dcf6c7..a2b26bbc5 100644 --- a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs @@ -155,13 +155,18 @@ namespace Ryujinx.Headless.SDL2.OpenGL { // NOTE: grabbing the main display's dimensions directly as OpenGL doesn't scale along like the VulkanWindow. // we might have to amend this if people run this on a non-primary display set to a different resolution. - SDL_Rect displayBounds; - SDL_GetDisplayBounds(0, out displayBounds); + if (SDL_GetDisplayBounds(0, out SDL_Rect displayBounds) < 0) + { + Logger.Warning?.Print(LogClass.Application, $"Could not retrieve display bounds: {SDL_GetError()}"); + + // Fallback to defaults + displayBounds.w = DefaultWidth; + displayBounds.h = DefaultHeight; + } Renderer?.Window.SetSize(displayBounds.w, displayBounds.h); MouseDriver.SetClientSize(displayBounds.w, displayBounds.h); } - else { Renderer?.Window.SetSize(DefaultWidth, DefaultHeight); diff --git a/src/Ryujinx.Horizon.Common/ResultNames.cs b/src/Ryujinx.Horizon.Common/ResultNames.cs index 84058cf2c..55a33d680 100644 --- a/src/Ryujinx.Horizon.Common/ResultNames.cs +++ b/src/Ryujinx.Horizon.Common/ResultNames.cs @@ -1690,7 +1690,7 @@ namespace Ryujinx.Horizon.Common { 0x821AC, "InvalidPackage1" }, { 0x823AC, "InvalidPackage1SectionSize" }, { 0x825AC, "InvalidPackage1MarikoBodySize" }, - { 0x827AC, "InvalidPackage1Pk11Size" } + { 0x827AC, "InvalidPackage1Pk11Size" }, }; public static bool TryGet(int errorCode, out string name) diff --git a/src/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs b/src/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs index b859f1f37..a5e6ec4a0 100644 --- a/src/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs +++ b/src/Ryujinx.Horizon.Generators/Hipc/CommandArgType.cs @@ -13,6 +13,6 @@ OutCopyHandle, OutMoveHandle, OutObject, - ProcessId + ProcessId, } } diff --git a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs index 47b3c8cfb..1b92e9180 100644 --- a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs +++ b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Horizon.Generators.Hipc None, Ref, Out, - In + In, } private readonly struct OutParameter diff --git a/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs index 75f966f39..638b2ea8f 100644 --- a/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs +++ b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Horizon.Kernel.Generators $"{TypeKernelResultName}.TimedOut", $"{TypeKernelResultName}.Cancelled", $"{TypeKernelResultName}.PortRemoteClosed", - $"{TypeKernelResultName}.InvalidState" + $"{TypeKernelResultName}.InvalidState", }; private readonly struct OutParameter diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index ad57152cb..057aac890 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -168,7 +168,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl long minTime = endTime; - foreach (MultiWaitHolder holder in _multiWaits) + foreach (MultiWaitHolderBase holder in _multiWaits) { long currentTime = holder.GetAbsoluteTimeToWakeup(); diff --git a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs index a97571293..564ebd806 100644 --- a/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs +++ b/src/Ryujinx.Tests.Memory/MultiRegionTrackingTests.cs @@ -337,7 +337,7 @@ namespace Ryujinx.Tests.Memory { granular.GetHandles(), singlePages, - doublePages + doublePages, }; MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize, 0); @@ -349,7 +349,7 @@ namespace Ryujinx.Tests.Memory true, true, // Gap. false, true, false, // Individual handles. false, false, true, true, false, false, // Double size handles. - true // Gap. + true, // Gap. }; for (int i = 0; i < 18; i++) diff --git a/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs index 6532beab8..245487771 100644 --- a/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs +++ b/src/Ryujinx.Tests.Unicorn/UnicornAArch32.cs @@ -159,7 +159,7 @@ namespace Ryujinx.Tests.Unicorn Arm.UC_ARM_REG_Q12, Arm.UC_ARM_REG_Q13, Arm.UC_ARM_REG_Q14, - Arm.UC_ARM_REG_Q15 + Arm.UC_ARM_REG_Q15, }; #pragma warning restore IDE0051, IDE0052 diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index a022b5535..8f562a83b 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -1914,4 +1914,4 @@ namespace Ryujinx.Ui UpdateGameTable(); } } -} \ No newline at end of file +} diff --git a/src/Spv.Generator/Instruction.cs b/src/Spv.Generator/Instruction.cs index 1c92d4991..27250e0f9 100644 --- a/src/Spv.Generator/Instruction.cs +++ b/src/Spv.Generator/Instruction.cs @@ -234,7 +234,7 @@ namespace Spv.Generator { { Specification.Op.OpConstant, new [] { "Value" } }, { Specification.Op.OpTypeInt, new [] { "Width", "Signed" } }, - { Specification.Op.OpTypeFloat, new [] { "Width" } } + { Specification.Op.OpTypeFloat, new [] { "Width" } }, }; public override string ToString() From f95b7c58779f01d9077996da67953d8d9acd058c Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sat, 29 Jul 2023 18:47:03 -0300 Subject: [PATCH 717/737] Fix incorrect fragment origin when YNegate is enabled (#4673) * Fix incorrect fragment origin when YNegate is enabled * Shader cache version bump * Do not update support buffer if shader does not read gl_FragCoord * Pass unscaled viewport size to the support buffer --- .../Threed/SpecializationStateUpdater.cs | 14 ++++++ .../Engine/Threed/StateUpdater.cs | 47 +++++++++++++++++-- .../Memory/SupportBufferUpdater.cs | 30 ++++++++++++ .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 6 +++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 9 +++- .../Shader/GpuAccessor.cs | 20 +++++--- .../Shader/GpuChannelGraphicsState.cs | 12 ++++- .../Shader/ShaderSpecializationState.cs | 5 ++ .../CodeGen/Glsl/Declarations.cs | 15 ++++-- .../CodeGen/Spirv/SpirvGenerator.cs | 2 +- src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 21 ++++++--- .../Instructions/InstEmitAttribute.cs | 12 +++++ .../ShaderProgramInfo.cs | 3 ++ .../StructuredIr/ShaderProperties.cs | 7 +++ src/Ryujinx.Graphics.Shader/SupportBuffer.cs | 5 ++ .../Translation/EmitterContextInsts.cs | 5 ++ .../Translation/ShaderConfig.cs | 20 ++++++-- 17 files changed, 207 insertions(+), 26 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs index cbf1573cd..b2935a5b4 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -342,5 +342,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Signal(); } } + + /// <summary> + /// Sets the Y negate enabled state. + /// </summary> + /// <param name="enabled">True if Y negate of the fragment coordinates is enabled</param> + public void SetYNegateEnabled(bool enabled) + { + if (enabled != _graphics.YNegateEnabled) + { + _graphics.YNegateEnabled = enabled; + + Signal(); + } + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index b4f56245e..c0c2d5b30 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private ProgramPipelineState _pipeline; + private bool _fsReadsFragCoord; private bool _vsUsesDrawParameters; private bool _vtgWritesRtLayer; private byte _vsClipDistancesWritten; @@ -692,12 +693,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed var face = _state.State.FaceState; bool disableTransform = _state.State.ViewportTransformEnable == 0; + bool yNegate = yControl.HasFlag(YControl.NegateY); UpdateFrontFace(yControl, face.FrontFace); UpdateDepthMode(); - bool flipY = yControl.HasFlag(YControl.NegateY); - Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports]; for (int index = 0; index < Constants.TotalViewports; index++) @@ -719,7 +719,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed float scaleX = MathF.Abs(transform.ScaleX); float scaleY = transform.ScaleY; - if (flipY) + if (yNegate) { scaleY = -scaleY; } @@ -771,8 +771,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _channel.TextureManager.RenderTargetScale, disableTransform); + // Viewport size is only used on the shader when YNegate is enabled, + // and if the fragment shader accesses gl_FragCoord, + // so there's no need to update it in other cases. + if (yNegate && _fsReadsFragCoord) + { + UpdateSupportBufferViewportSize(); + } + _currentSpecState.SetViewportTransformDisable(disableTransform); _currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne); + _currentSpecState.SetYNegateEnabled(yNegate); } /// <summary> @@ -1415,9 +1424,41 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _currentProgramInfo[stageIndex] = info; } + if (gs.Shaders[5]?.Info.UsesFragCoord == true) + { + // Make sure we update the viewport size on the support buffer if it will be consumed on the new shader. + + if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY)) + { + UpdateSupportBufferViewportSize(); + } + + _fsReadsFragCoord = true; + } + else + { + _fsReadsFragCoord = false; + } + _context.Renderer.Pipeline.SetProgram(gs.HostProgram); } + /// <summary> + /// Updates the viewport size on the support buffer for fragment shader access. + /// </summary> + private void UpdateSupportBufferViewportSize() + { + ref var transform = ref _state.State.ViewportTransform[0]; + + float scaleX = MathF.Abs(transform.ScaleX); + float scaleY = transform.ScaleY; + + float width = scaleX * 2; + float height = scaleY * 2; + + _context.SupportBufferUpdater.SetViewportSize(width, MathF.Abs(height)); + } + /// <summary> /// Updates bindings consumed by the shader on the texture and buffer managers. /// </summary> diff --git a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs index 50c042fb9..b236476e0 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs @@ -112,6 +112,17 @@ namespace Ryujinx.Graphics.Gpu.Memory MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize); } + /// <summary> + /// Updates the viewport size vector. + /// </summary> + /// <param name="data">Viewport size vector</param> + private void UpdateViewportSize(Vector4<float> data) + { + _data.ViewportSize = data; + + MarkDirty(SupportBuffer.ViewportSizeOffset, SupportBuffer.FieldSize); + } + /// <summary> /// Sets the scale of all output render targets (they should all have the same scale). /// </summary> @@ -192,6 +203,25 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// <summary> + /// Sets the viewport size, used to invert the fragment coordinates Y value. + /// </summary> + /// <param name="viewportWidth">Value used as viewport width</param> + /// <param name="viewportHeight">Value used as viewport height</param> + public void SetViewportSize(float viewportWidth, float viewportHeight) + { + if (_data.ViewportSize.X != viewportWidth || _data.ViewportSize.Y != viewportHeight) + { + UpdateViewportSize(new Vector4<float> + { + X = viewportWidth, + Y = viewportHeight, + Z = 1, + W = 0 + }); + } + } + /// <summary> /// Submits all pending buffer updates to the GPU. /// </summary> diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 7f01aca63..b5f9395e7 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -247,6 +247,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.ViewportTransformDisable; } + /// <inheritdoc/> + public bool QueryYNegateEnabled() + { + return _oldSpecState.GraphicsState.YNegateEnabled; + } + /// <inheritdoc/> public void RegisterTexture(int handle, int cbufSlot) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 672b3b8d1..4bab165da 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5266; + private const uint CodeGenVersion = 4675; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; @@ -140,6 +140,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// </summary> public ShaderStage Stage; + /// <summary> + /// Indicates if the fragment shader accesses the fragment coordinate built-in variable. + /// </summary> + public bool UsesFragCoord; + /// <summary> /// Indicates if the shader accesses the Instance ID built-in variable. /// </summary> @@ -781,6 +786,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache ShaderIdentification.None, 0, dataInfo.Stage, + dataInfo.UsesFragCoord, dataInfo.UsesInstanceId, dataInfo.UsesDrawParameters, dataInfo.UsesRtLayer, @@ -807,6 +813,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache TexturesCount = (ushort)info.Textures.Count, ImagesCount = (ushort)info.Images.Count, Stage = info.Stage, + UsesFragCoord = info.UsesFragCoord, UsesInstanceId = info.UsesInstanceId, UsesDrawParameters = info.UsesDrawParameters, UsesRtLayer = info.UsesRtLayer, diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index ca9c883e3..1fcc93c50 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -113,6 +113,13 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.AttributeTypes[location]; } + /// <inheritdoc/> + public bool QueryEarlyZForce() + { + _state.SpecializationState?.RecordEarlyZForce(); + return _state.GraphicsState.EarlyZForce; + } + /// <inheritdoc/> public AttributeType QueryFragmentOutputType(int location) { @@ -275,19 +282,18 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.TransformFeedbackDescriptors[bufferIndex].Stride; } - /// <inheritdoc/> - public bool QueryEarlyZForce() - { - _state.SpecializationState?.RecordEarlyZForce(); - return _state.GraphicsState.EarlyZForce; - } - /// <inheritdoc/> public bool QueryViewportTransformDisable() { return _state.GraphicsState.ViewportTransformDisable; } + /// <inheritdoc/> + public bool QueryYNegateEnabled() + { + return _state.GraphicsState.YNegateEnabled; + } + /// <inheritdoc/> public void RegisterTexture(int handle, int cbufSlot) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 544e689ab..f392491c3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -97,6 +97,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> public bool DualSourceBlendEnable; + /// <summary> + /// Indicates whether Y negate of the fragment coordinates is enabled. + /// </summary> + public bool YNegateEnabled; + /// <summary> /// Creates a new GPU graphics state. /// </summary> @@ -116,7 +121,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> /// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param> /// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param> - /// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param> + /// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param> + /// <param name="yNegateEnabled">Indicates whether Y negate of the fragment coordinates is enabled</param> public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -134,7 +140,8 @@ namespace Ryujinx.Graphics.Gpu.Shader bool hasConstantBufferDrawParameters, bool hasUnalignedStorageBuffer, ref Array8<AttributeType> fragmentOutputTypes, - bool dualSourceBlendEnable) + bool dualSourceBlendEnable, + bool yNegateEnabled) { EarlyZForce = earlyZForce; Topology = topology; @@ -153,6 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Shader HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; FragmentOutputTypes = fragmentOutputTypes; DualSourceBlendEnable = dualSourceBlendEnable; + YNegateEnabled = yNegateEnabled; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 775bfb2af..b33f96c57 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -540,6 +540,11 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + if (graphicsState.YNegateEnabled != GraphicsState.YNegateEnabled) + { + return false; + } + return Matches(channel, ref poolState, checkTextures, isCompute: false); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 2370b49f0..2a45e23de 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -188,10 +188,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryEarlyZForce()) + if (context.Config.Stage == ShaderStage.Fragment) { - context.AppendLine("layout(early_fragment_tests) in;"); - context.AppendLine(); + if (context.Config.GpuAccessor.QueryEarlyZForce()) + { + context.AppendLine("layout (early_fragment_tests) in;"); + context.AppendLine(); + } + + if (context.Config.Properties.OriginUpperLeft) + { + context.AppendLine("layout (origin_upper_left) in vec4 gl_FragCoord;"); + context.AppendLine(); + } } if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index c8fcd75a1..217979757 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -251,7 +251,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else if (context.Config.Stage == ShaderStage.Fragment) { - context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan + context.AddExecutionMode(spvFunc, context.Config.Properties.OriginUpperLeft ? ExecutionMode.OriginUpperLeft : ExecutionMode.OriginLowerLeft); diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 1c2b28097..a47791d3f 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -178,6 +178,15 @@ namespace Ryujinx.Graphics.Shader return 0; } + /// <summary> + /// Queries if host state forces early depth testing. + /// </summary> + /// <returns>True if early depth testing is forced</returns> + bool QueryEarlyZForce() + { + return false; + } + /// <summary> /// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0. /// </summary> @@ -534,19 +543,19 @@ namespace Ryujinx.Graphics.Shader } /// <summary> - /// Queries if host state forces early depth testing. + /// Queries if host state disables the viewport transform. /// </summary> - /// <returns>True if early depth testing is forced</returns> - bool QueryEarlyZForce() + /// <returns>True if the viewport transform is disabled</returns> + bool QueryViewportTransformDisable() { return false; } /// <summary> - /// Queries if host state disables the viewport transform. + /// Queries Y negate enable state. /// </summary> - /// <returns>True if the viewport transform is disabled</returns> - bool QueryViewportTransformDisable() + /// <returns>True if Y negate of the fragment coordinates is enabled, false otherwise</returns> + bool QueryYNegateEnabled() { return false; } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 1876847c4..c7bd0fd63 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -161,6 +161,18 @@ namespace Ryujinx.Graphics.Shader.Instructions // FragCoord X/Y must be divided by the render target scale, if resolution scaling is active, // because the shader code is not expecting scaled values. res = context.FPDivide(res, context.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.RenderScale), Const(0))); + + if (op.Imm10 == AttributeConsts.PositionY && context.Config.Options.TargetApi != TargetApi.OpenGL) + { + // If YNegate is enabled, we need to flip the fragment coordinates vertically, unless + // the API supports changing the origin (only OpenGL does). + if (context.Config.GpuAccessor.QueryYNegateEnabled()) + { + Operand viewportHeight = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.ViewportSize), Const(1)); + + res = context.FPSubtract(viewportHeight, res); + } + } } else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug()) { diff --git a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index e87769bb9..f9776afc0 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Shader public ShaderIdentification Identification { get; } public int GpLayerInputAttribute { get; } public ShaderStage Stage { get; } + public bool UsesFragCoord { get; } public bool UsesInstanceId { get; } public bool UsesDrawParameters { get; } public bool UsesRtLayer { get; } @@ -27,6 +28,7 @@ namespace Ryujinx.Graphics.Shader ShaderIdentification identification, int gpLayerInputAttribute, ShaderStage stage, + bool usesFragCoord, bool usesInstanceId, bool usesDrawParameters, bool usesRtLayer, @@ -41,6 +43,7 @@ namespace Ryujinx.Graphics.Shader Identification = identification; GpLayerInputAttribute = gpLayerInputAttribute; Stage = stage; + UsesFragCoord = usesFragCoord; UsesInstanceId = usesInstanceId; UsesDrawParameters = usesDrawParameters; UsesRtLayer = usesRtLayer; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index 048a260ab..b7e379c6b 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories; public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories; + public readonly bool OriginUpperLeft; + public ShaderProperties() { _constantBuffers = new Dictionary<int, BufferDefinition>(); @@ -28,6 +30,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr _sharedMemories = new Dictionary<int, MemoryDefinition>(); } + public ShaderProperties(bool originUpperLeft) : this() + { + OriginUpperLeft = originUpperLeft; + } + public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition) { _constantBuffers[binding] = definition; diff --git a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs index 24a99345a..0b7a2edd6 100644 --- a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs +++ b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs @@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader FragmentAlphaTest, FragmentIsBgra, ViewportInverse, + ViewportSize, FragmentRenderScaleCount, RenderScale, } @@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Shader public static readonly int FragmentAlphaTestOffset; public static readonly int FragmentIsBgraOffset; public static readonly int ViewportInverseOffset; + public static readonly int ViewportSizeOffset; public static readonly int FragmentRenderScaleCountOffset; public static readonly int GraphicsRenderScaleOffset; public static readonly int ComputeRenderScaleOffset; @@ -56,6 +58,7 @@ namespace Ryujinx.Graphics.Shader FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest); FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra); ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse); + ViewportSizeOffset = OffsetOf(ref instance, ref instance.ViewportSize); FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount); GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale); ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; @@ -68,6 +71,7 @@ namespace Ryujinx.Graphics.Shader new StructureField(AggregateType.U32, "s_alpha_test"), new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount), new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"), + new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_size"), new StructureField(AggregateType.S32, "s_frag_scale_count"), new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount), }); @@ -76,6 +80,7 @@ namespace Ryujinx.Graphics.Shader public Vector4<int> FragmentAlphaTest; public Array8<Vector4<int>> FragmentIsBgra; public Vector4<float> ViewportInverse; + public Vector4<float> ViewportSize; public Vector4<int> FragmentRenderScaleCount; // Render scale max count: 1 + 64 + 8. First scale is fragment output scale, others are textures/image inputs. diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index c92d05838..6cb572381 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -429,6 +429,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.FP32 | Instruction.SquareRoot, Local(), a); } + public static Operand FPSubtract(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) + { + return context.Add(fpType | Instruction.Subtract, Local(), a, b); + } + public static Operand FPTruncate(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32) { return context.Add(fpType | Instruction.Truncate, Local(), a); diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 5741d0288..27b46867d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -123,7 +123,20 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>(); - ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); + ShaderProperties properties; + + switch (stage) + { + case ShaderStage.Fragment: + bool originUpperLeft = options.TargetApi == TargetApi.Vulkan || gpuAccessor.QueryYNegateEnabled(); + properties = new ShaderProperties(originUpperLeft); + break; + default: + properties = new ShaderProperties(); + break; + } + + ResourceManager = new ResourceManager(stage, gpuAccessor, properties); if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) { @@ -135,7 +148,7 @@ namespace Ryujinx.Graphics.Shader.Translation BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); - Properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); + properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); StructureType tfeDataStruct = new(new StructureField[] { @@ -146,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int binding = Constants.TfeBufferBaseBinding + i; BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); - Properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer); + properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer); } } } @@ -615,6 +628,7 @@ namespace Ryujinx.Graphics.Shader.Translation identification, GpLayerInputAttribute, Stage, + UsedFeatures.HasFlag(FeatureFlags.FragCoordXY), UsedFeatures.HasFlag(FeatureFlags.InstanceId), UsedFeatures.HasFlag(FeatureFlags.DrawParameters), UsedFeatures.HasFlag(FeatureFlags.RtLayer), From 2be8b6ea4527239fff1b2fdf9dc7ce1346b173e6 Mon Sep 17 00:00:00 2001 From: Domenico V <35856442+LDj3SNuD@users.noreply.github.com> Date: Mon, 31 Jul 2023 01:57:37 +0200 Subject: [PATCH 718/737] CPU (A64): Add Fmaxp & Fminp Scalar Inst.s, Fast & Slow Paths; with Tests. (#5502) * Add Fmaxp & Fminp Scalar Inst.s, Fast & Slow Paths; with Tests. * Ptc.InternalVersion = 5502 --- src/ARMeilleure/Decoders/OpCodeTable.cs | 2 + .../Instructions/InstEmitSimdArithmetic.cs | 50 +++++++++++++++++++ src/ARMeilleure/Instructions/InstName.cs | 2 + src/ARMeilleure/Translation/PTC/Ptc.cs | 2 +- src/Ryujinx.Tests/Cpu/CpuTestSimd.cs | 4 ++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/ARMeilleure/Decoders/OpCodeTable.cs b/src/ARMeilleure/Decoders/OpCodeTable.cs index 5cfd0bb81..9e13bd9b5 100644 --- a/src/ARMeilleure/Decoders/OpCodeTable.cs +++ b/src/ARMeilleure/Decoders/OpCodeTable.cs @@ -330,6 +330,7 @@ namespace ARMeilleure.Decoders SetA64("011111100x110000110010xxxxxxxxxx", InstName.Fmaxnmp_S, InstEmit.Fmaxnmp_S, OpCodeSimd.Create); SetA64("0>1011100<1xxxxx110001xxxxxxxxxx", InstName.Fmaxnmp_V, InstEmit.Fmaxnmp_V, OpCodeSimdReg.Create); SetA64("0110111000110000110010xxxxxxxxxx", InstName.Fmaxnmv_V, InstEmit.Fmaxnmv_V, OpCodeSimd.Create); + SetA64("011111100x110000111110xxxxxxxxxx", InstName.Fmaxp_S, InstEmit.Fmaxp_S, OpCodeSimd.Create); SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", InstName.Fmaxp_V, InstEmit.Fmaxp_V, OpCodeSimdReg.Create); SetA64("0110111000110000111110xxxxxxxxxx", InstName.Fmaxv_V, InstEmit.Fmaxv_V, OpCodeSimd.Create); SetA64("000111100x1xxxxx010110xxxxxxxxxx", InstName.Fmin_S, InstEmit.Fmin_S, OpCodeSimdReg.Create); @@ -339,6 +340,7 @@ namespace ARMeilleure.Decoders SetA64("011111101x110000110010xxxxxxxxxx", InstName.Fminnmp_S, InstEmit.Fminnmp_S, OpCodeSimd.Create); SetA64("0>1011101<1xxxxx110001xxxxxxxxxx", InstName.Fminnmp_V, InstEmit.Fminnmp_V, OpCodeSimdReg.Create); SetA64("0110111010110000110010xxxxxxxxxx", InstName.Fminnmv_V, InstEmit.Fminnmv_V, OpCodeSimd.Create); + SetA64("011111101x110000111110xxxxxxxxxx", InstName.Fminp_S, InstEmit.Fminp_S, OpCodeSimd.Create); SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", InstName.Fminp_V, InstEmit.Fminp_V, OpCodeSimdReg.Create); SetA64("0110111010110000111110xxxxxxxxxx", InstName.Fminv_V, InstEmit.Fminv_V, OpCodeSimd.Create); SetA64("010111111xxxxxxx0001x0xxxxxxxxxx", InstName.Fmla_Se, InstEmit.Fmla_Se, OpCodeSimdRegElemF.Create); diff --git a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs index 7b308fa96..543aab023 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs @@ -883,6 +883,31 @@ namespace ARMeilleure.Instructions } } + public static void Fmaxp_S(ArmEmitterContext context) + { + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FmaxpS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) + { + EmitSse2ScalarPairwiseOpF(context, (op1, op2) => + { + return EmitSse41ProcessNaNsOpF(context, (op1, op2) => + { + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true); + }, scalar: true, op1, op2); + }); + } + else + { + EmitScalarPairwiseOpF(context, (op1, op2) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2); + }); + } + } + public static void Fmaxp_V(ArmEmitterContext context) { if (Optimizations.UseAdvSimd) @@ -1081,6 +1106,31 @@ namespace ARMeilleure.Instructions } } + public static void Fminp_S(ArmEmitterContext context) + { + if (Optimizations.UseAdvSimd) + { + InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FminpS); + } + else if (Optimizations.FastFP && Optimizations.UseSse41) + { + EmitSse2ScalarPairwiseOpF(context, (op1, op2) => + { + return EmitSse41ProcessNaNsOpF(context, (op1, op2) => + { + return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false); + }, scalar: true, op1, op2); + }); + } + else + { + EmitScalarPairwiseOpF(context, (op1, op2) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2); + }); + } + } + public static void Fminp_V(ArmEmitterContext context) { if (Optimizations.UseAdvSimd) diff --git a/src/ARMeilleure/Instructions/InstName.cs b/src/ARMeilleure/Instructions/InstName.cs index fd71d92e6..32ae38dad 100644 --- a/src/ARMeilleure/Instructions/InstName.cs +++ b/src/ARMeilleure/Instructions/InstName.cs @@ -228,6 +228,7 @@ namespace ARMeilleure.Instructions Fmaxnmp_S, Fmaxnmp_V, Fmaxnmv_V, + Fmaxp_S, Fmaxp_V, Fmaxv_V, Fmin_S, @@ -237,6 +238,7 @@ namespace ARMeilleure.Instructions Fminnmp_S, Fminnmp_V, Fminnmv_V, + Fminp_S, Fminp_V, Fminv_V, Fmla_Se, diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 14d4e471f..ce653383e 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 5343; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5502; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 4c568a8f4..eb763618d 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -764,7 +764,9 @@ namespace Ryujinx.Tests.Cpu { 0x7E30D820u, // FADDP S0, V1.2S 0x7E30C820u, // FMAXNMP S0, V1.2S + 0x7E30F820u, // FMAXP S0, V1.2S 0x7EB0C820u, // FMINNMP S0, V1.2S + 0x7EB0F820u, // FMINP S0, V1.2S }; } @@ -774,7 +776,9 @@ namespace Ryujinx.Tests.Cpu { 0x7E70D820u, // FADDP D0, V1.2D 0x7E70C820u, // FMAXNMP D0, V1.2D + 0x7E70F820u, // FMAXP D0, V1.2D 0x7EF0C820u, // FMINNMP D0, V1.2D + 0x7EF0F820u, // FMINP D0, V1.2D }; } From 86931cc3f1f781f8849651e658a222ca821040fa Mon Sep 17 00:00:00 2001 From: Domenico V <35856442+LDj3SNuD@users.noreply.github.com> Date: Mon, 31 Jul 2023 03:31:57 +0200 Subject: [PATCH 719/737] (Graphics.Shader): Handle EmitSuatom constant dests and EmitSuld zero dest reg. (#5504) * (Graphics.Shader): Handle EmitSuatom constant dests. * Proper fix for EmitSuatom; fix EmitSuld. --- .../Instructions/InstEmitSurface.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index 0b929307e..6bf7de7f0 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return context.Copy(Register(srcB++, RegisterType.Gpr)); } - Operand d = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null; + Operand d = Register(dest, RegisterType.Gpr); List<Operand> sourcesList = new(); @@ -304,6 +304,11 @@ namespace Ryujinx.Graphics.Shader.Instructions bool byteAddress, bool isBindless) { + if (srcB == RegisterConsts.RegisterZeroIndex) + { + return; + } + context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); SamplerType type = ConvertSamplerType(dimensions); From bedee64af5a36a55b82df781b3bc78f9d09844c6 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Tue, 1 Aug 2023 00:15:37 +0200 Subject: [PATCH 720/737] Add slightly better workaround for current workflow issues (#5507) * checks: Add retry logic to dotnet format style step as well I can't imagine dotnet format whitespace ever segfaulting, so hopefully it won't be needed there. * checks: Replace bash scripts with unstable-commands action * build: Add unstable-commands action for test step --- .github/workflows/build.yml | 8 ++++++-- .github/workflows/checks.yml | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c93fd0d30..dc3728707 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,7 +54,11 @@ jobs: run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER - name: Test - run: dotnet test --no-build -c "${{ matrix.configuration }}" + uses: TSRBerry/unstable-commands@v1 + with: + commands: dotnet test --no-build -c "${{ matrix.configuration }}" + timeout-minutes: 10 + retry-codes: 139 - name: Publish Ryujinx run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true @@ -141,4 +145,4 @@ jobs: with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal path: "publish_ava/*.tar.gz" - if: github.event_name == 'pull_request' \ No newline at end of file + if: github.event_name == 'pull_request' diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c34d196f2..94d0a342b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -40,23 +40,23 @@ jobs: run: | dotnet format whitespace --verify-no-changes --report ./whitespace-report.json -v d + # For some unknown reason this step sometimes fails with exit code 139 (segfault?), + # so in that case we'll try again (3 tries max). - name: Run dotnet format style - run: | - dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d + uses: TSRBerry/unstable-commands@v1 + with: + commands: dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d + timeout-minutes: 5 + retry-codes: 139 - # For some reason this step sometimes fails with exit code 139 (segfault?), - # so should that be the case we'll try again (3 tries max). + # For some unknown reason this step sometimes fails with exit code 139 (segfault?), + # so in that case we'll try again (3 tries max). - name: Run dotnet format analyzers - run: | - attempt=0 - exit_code=139 - until [ $attempt -ge 3 ] || [ $exit_code -ne 139 ]; do - ((attempt+=1)) - exit_code=0 - echo "Attempt: ${attempt}/3" - dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d || exit_code=$? - done - exit $exit_code + uses: TSRBerry/unstable-commands@v1 + with: + commands: dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d + timeout-minutes: 5 + retry-codes: 139 - name: Upload report if: failure() From 93aa40f1fb739f12a6a404353580a9656fe6a52f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:19:38 +0200 Subject: [PATCH 721/737] nuget: bump DiscordRichPresence from 1.1.3.18 to 1.2.1.24 (#5515) Bumps [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) from 1.1.3.18 to 1.2.1.24. - [Release notes](https://github.com/Lachee/discord-rpc-csharp/releases) - [Commits](https://github.com/Lachee/discord-rpc-csharp/commits) --- updated-dependencies: - dependency-name: DiscordRichPresence dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index bc740afcf..6191c4984 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,7 +12,7 @@ <PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="Concentus" Version="1.1.7" /> - <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> + <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DynamicData" Version="7.14.2" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> From 5a0aa074b661753d8f0202a73d9f6f3ac6e2ab11 Mon Sep 17 00:00:00 2001 From: sunshineinabox <aqemail@gmail.com> Date: Thu, 3 Aug 2023 13:46:23 -0700 Subject: [PATCH 722/737] Enable VK_EXT_4444_formats (#5525) --- src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 02cfe91d9..6f73397b8 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -42,6 +42,7 @@ namespace Ryujinx.Graphics.Vulkan "VK_NV_viewport_array2", "VK_EXT_depth_clip_control", "VK_KHR_portability_subset", // As per spec, we should enable this if present. + "VK_EXT_4444_formats", }; private static readonly string[] _requiredExtensions = { From 6e784e0aca240b41c83bd3e77aecf5793fdc238d Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sun, 6 Aug 2023 20:29:20 +0100 Subject: [PATCH 723/737] GPU: Don't sync/bind index buffer when it's not in use (#5526) * GPU: Don't sync/bind index buffer when it's not in use Sometimes draws don't use an index buffer. It's not necessary to check or upload data for the current index buffer binding as it won't be used. This fixes Pokemon: Legends Arceus updating a stale index buffer for every draw during its TFB pass, which was all non-indexed draws. This probably didn't cost much on normal PCs, but it had a large impact on MacOS, which the macos1 release build avoided by mirroring index buffers (the PR currently does not). Needs buffer mirrors still for the rest of the performance. There are additional cases where index buffers are bound or checked with non-indexed draws on the backend, but this one was straightforward to fix and has the largest impact. Testing is welcome to ensure nothing weird broke. * Fix case with _rebind --- .../Engine/Threed/StateUpdater.cs | 2 +- .../Memory/BufferManager.cs | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index c0c2d5b30..b08e7f260 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -331,7 +331,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed UpdateShaderState(); } - _channel.BufferManager.CommitGraphicsBindings(); + _channel.BufferManager.CommitGraphicsBindings(_drawState.DrawIndexed); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 10224a6d4..c656b0f64 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -515,24 +515,32 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Ensures that the graphics engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// </summary> - public void CommitGraphicsBindings() + /// <param name="indexed">True if the index buffer is in use</param> + public void CommitGraphicsBindings(bool indexed) { var bufferCache = _channel.MemoryManager.Physical.BufferCache; - if (_indexBufferDirty || _rebind) + if (indexed) { - _indexBufferDirty = false; - - if (_indexBuffer.Address != 0) + if (_indexBufferDirty || _rebind) { - BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size); + _indexBufferDirty = false; - _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); + if (_indexBuffer.Address != 0) + { + BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size); + + _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); + } + } + else if (_indexBuffer.Address != 0) + { + bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); } } - else if (_indexBuffer.Address != 0) + else if (_rebind) { - bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); + _indexBufferDirty = true; } uint vbEnableMask = _vertexBuffersEnableMask; From 3ab0a71c7bfb60b20008894db3fb6534436753e6 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 6 Aug 2023 23:25:02 +0200 Subject: [PATCH 724/737] Fix PR build concurrency and stop auto assigning reviewers for draft PRs (#5519) * build: Remove concurrency It's called by checks anyway. * Only assign reviewers for PRs that are ready for reviews --- .github/workflows/build.yml | 4 ---- .github/workflows/pr_triage.yml | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc3728707..bbc2eca80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,10 +3,6 @@ name: Build job on: workflow_call: -concurrency: - group: pr-builds-${{ github.event.number }} - cancel-in-progress: true - env: POWERSHELL_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1 diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml index 86e5084f2..cd90e645a 100644 --- a/.github/workflows/pr_triage.yml +++ b/.github/workflows/pr_triage.yml @@ -28,6 +28,7 @@ jobs: dot: true - name: Assign reviewers + if: ! github.event.pull_request.draft run: | pip3 install PyGithub python3 .github/update_reviewers.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml From 42750a74f82ee69cabfaf3c5497af6a8ebc13eca Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 7 Aug 2023 12:20:37 -0300 Subject: [PATCH 725/737] Do not add more code after alpha test discard on fragment shader (#5529) * Do not add more code after alpha test discard on fragment shader * Shader cache version bump --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Instructions/InstEmitFlowControl.cs | 14 ++++++++++---- .../Translation/EmitterContext.cs | 16 ++++++++++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 4bab165da..512ae94b4 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4675; + private const uint CodeGenVersion = 5529; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs index fc1a696fa..7462fc5ad 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs @@ -162,8 +162,10 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Ccc == Ccc.T) { - context.PrepareForReturn(); - context.Return(); + if (context.PrepareForReturn()) + { + context.Return(); + } } else { @@ -174,8 +176,12 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand lblSkip = Label(); context.BranchIfFalse(lblSkip, cond); - context.PrepareForReturn(); - context.Return(); + + if (context.PrepareForReturn()) + { + context.Return(); + } + context.MarkLabel(lblSkip); } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 08d4b9156..614b275ba 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -304,11 +304,11 @@ namespace Ryujinx.Graphics.Shader.Translation PrepareForVertexReturn(); } - public void PrepareForReturn() + public bool PrepareForReturn() { if (IsNonMain) { - return; + return true; } if (Config.LastInVertexPipeline && @@ -383,13 +383,13 @@ namespace Ryujinx.Graphics.Shader.Translation AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare(); - if (alphaTestOp != AlphaTestOp.Always && (Config.OmapTargets & 8) != 0) + if (alphaTestOp != AlphaTestOp.Always) { if (alphaTestOp == AlphaTestOp.Never) { this.Discard(); } - else + else if ((Config.OmapTargets & 8) != 0) { Instruction comparator = alphaTestOp switch { @@ -415,6 +415,12 @@ namespace Ryujinx.Graphics.Shader.Translation } } + // We don't need to output anything if alpha test always fails. + if (alphaTestOp == AlphaTestOp.Never) + { + return false; + } + int regIndexBase = 0; for (int rtIndex = 0; rtIndex < 8; rtIndex++) @@ -462,6 +468,8 @@ namespace Ryujinx.Graphics.Shader.Translation } } } + + return true; } private void GenerateAlphaToCoverageDitherDiscard() From 773e239db7ceb2c55aa15f9787add4430edcdfcf Mon Sep 17 00:00:00 2001 From: jcm <john.moody@coloradocollege.edu> Date: Mon, 7 Aug 2023 11:54:05 -0600 Subject: [PATCH 726/737] Implement color space passthrough option (#5531) Co-authored-by: jcm <butt@butts.com> --- .editorconfig | 7 +++ src/Ryujinx.Ava/AppHost.cs | 8 +++ src/Ryujinx.Ava/Assets/Locales/en_US.json | 2 + .../UI/ViewModels/SettingsViewModel.cs | 3 ++ .../Views/Settings/SettingsGraphicsView.axaml | 4 ++ src/Ryujinx.Graphics.GAL/IWindow.cs | 1 + .../Multithreading/ThreadedWindow.cs | 2 + src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs | 5 ++ src/Ryujinx.Graphics.OpenGL/Window.cs | 2 + src/Ryujinx.Graphics.Vulkan/Window.cs | 49 ++++++++++++++----- src/Ryujinx.Graphics.Vulkan/WindowBase.cs | 1 + .../Configuration/ConfigurationFileFormat.cs | 7 ++- .../Configuration/ConfigurationState.cs | 19 +++++++ 13 files changed, 97 insertions(+), 13 deletions(-) diff --git a/.editorconfig b/.editorconfig index e5a5e6174..9d695c7fb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,13 @@ tab_width = 4 end_of_line = lf insert_final_newline = true +# JSON files +[*.json] + +# Indentation and spacing +indent_size = 2 +tab_width = 2 + # C# files [*.cs] diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 7c1ce542c..786b45070 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -186,6 +186,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; + ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; @@ -229,6 +230,11 @@ namespace Ryujinx.Ava _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); } + private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs<bool> e) + { + _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); + } + private void ShowCursor() { Dispatcher.UIThread.Post(() => @@ -461,6 +467,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; + ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough; _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved; _topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved; @@ -887,6 +894,7 @@ namespace Ryujinx.Ava _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); + _renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); Width = (int)RendererHost.Bounds.Width; Height = (int)RendererHost.Bounds.Height; diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 4065a1dfb..efd3187ad 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -620,6 +620,8 @@ "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", "SettingsEnableMacroHLE": "Enable Macro HLE", "SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.", + "SettingsEnableColorSpacePassthrough": "Color Space Passthrough", + "SettingsEnableColorSpacePassthroughTooltip": "Directs the Vulkan backend to pass through color information without specifying a color space. For users with wide gamut displays, this may result in more vibrant colors, at the cost of color correctness.", "VolumeShort": "Vol", "UserProfilesManageSaves": "Manage Saves", "DeleteUserSave": "Do you want to delete user save for this game?", diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index a9eb9c618..1e6d2734f 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -145,6 +145,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableShaderCache { get; set; } public bool EnableTextureRecompression { get; set; } public bool EnableMacroHLE { get; set; } + public bool EnableColorSpacePassthrough { get; set; } public bool EnableFileLog { get; set; } public bool EnableStub { get; set; } public bool EnableInfo { get; set; } @@ -419,6 +420,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableShaderCache = config.Graphics.EnableShaderCache; EnableTextureRecompression = config.Graphics.EnableTextureRecompression; EnableMacroHLE = config.Graphics.EnableMacroHLE; + EnableColorSpacePassthrough = config.Graphics.EnableColorSpacePassthrough; ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1; CustomResolutionScale = config.Graphics.ResScaleCustom; MaxAnisotropy = config.Graphics.MaxAnisotropy == -1 ? 0 : (int)(MathF.Log2(config.Graphics.MaxAnisotropy)); @@ -506,6 +508,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.Graphics.EnableShaderCache.Value = EnableShaderCache; config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression; config.Graphics.EnableMacroHLE.Value = EnableMacroHLE; + config.Graphics.EnableColorSpacePassthrough.Value = EnableColorSpacePassthrough; config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1; config.Graphics.ResScaleCustom.Value = CustomResolutionScale; config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy); diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml index 8e4122f38..670de69c6 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml @@ -73,6 +73,10 @@ ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}"> <TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" /> </CheckBox> + <CheckBox IsChecked="{Binding EnableColorSpacePassthrough}" + ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}"> + <TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" /> + </CheckBox> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock VerticalAlignment="Center" diff --git a/src/Ryujinx.Graphics.GAL/IWindow.cs b/src/Ryujinx.Graphics.GAL/IWindow.cs index 1221d685a..83418e709 100644 --- a/src/Ryujinx.Graphics.GAL/IWindow.cs +++ b/src/Ryujinx.Graphics.GAL/IWindow.cs @@ -13,5 +13,6 @@ namespace Ryujinx.Graphics.GAL void SetAntiAliasing(AntiAliasing antialiasing); void SetScalingFilter(ScalingFilter type); void SetScalingFilterLevel(float level); + void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs index 3c4d54149..4f1b795a4 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -38,5 +38,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading public void SetScalingFilter(ScalingFilter type) { } public void SetScalingFilterLevel(float level) { } + + public void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) { } } } diff --git a/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs b/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs index 4dfb93381..fbb7399ca 100644 --- a/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs +++ b/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs @@ -67,6 +67,11 @@ namespace Ryujinx.Graphics.Gpu /// Enables or disables recompression of compressed textures that are not natively supported by the host. /// </summary> public static bool EnableTextureRecompression = false; + + /// <summary> + /// Enables or disables color space passthrough, if available. + /// </summary> + public static bool EnableColorSpacePassthrough = false; } #pragma warning restore CA2211 } diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index a8e6031b6..6bcfefa4e 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -307,6 +307,8 @@ namespace Ryujinx.Graphics.OpenGL _updateScalingFilter = true; } + public void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) { } + private void UpdateEffect() { if (_updateEffect) diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 6027962cf..2d0ad664c 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan private int _width; private int _height; private bool _vsyncEnabled; - private bool _vsyncModeChanged; + private bool _swapchainIsDirty; private VkFormat _format; private AntiAliasing _currentAntiAliasing; private bool _updateEffect; @@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan private float _scalingFilterLevel; private bool _updateScalingFilter; private ScalingFilter _currentScalingFilter; + private bool _colorSpacePassthroughEnabled; public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device) { @@ -60,7 +61,7 @@ namespace Ryujinx.Graphics.Vulkan private void RecreateSwapchain() { var oldSwapchain = _swapchain; - _vsyncModeChanged = false; + _swapchainIsDirty = false; for (int i = 0; i < _swapchainImageViews.Length; i++) { @@ -106,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan imageCount = capabilities.MaxImageCount; } - var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats); + var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats, _colorSpacePassthroughEnabled); var extent = ChooseSwapExtent(capabilities); @@ -178,22 +179,40 @@ namespace Ryujinx.Graphics.Vulkan return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView)); } - private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats) + private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats, bool colorSpacePassthroughEnabled) { if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined) { return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr); } - - foreach (var format in availableFormats) + var formatToReturn = availableFormats[0]; + if (colorSpacePassthroughEnabled) { - if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr) + foreach (var format in availableFormats) { - return format; + if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.SpacePassThroughExt) + { + formatToReturn = format; + break; + } + else if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr) + { + formatToReturn = format; + } } } - - return availableFormats[0]; + else + { + foreach (var format in availableFormats) + { + if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr) + { + formatToReturn = format; + break; + } + } + } + return formatToReturn; } private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags) @@ -259,7 +278,7 @@ namespace Ryujinx.Graphics.Vulkan if (acquireResult == Result.ErrorOutOfDateKhr || acquireResult == Result.SuboptimalKhr || - _vsyncModeChanged) + _swapchainIsDirty) { RecreateSwapchain(); } @@ -443,6 +462,12 @@ namespace Ryujinx.Graphics.Vulkan _updateScalingFilter = true; } + public override void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) + { + _colorSpacePassthroughEnabled = colorSpacePassthroughEnabled; + _swapchainIsDirty = true; + } + private void UpdateEffect() { if (_updateEffect) @@ -559,7 +584,7 @@ namespace Ryujinx.Graphics.Vulkan public override void ChangeVSyncMode(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; - _vsyncModeChanged = true; + _swapchainIsDirty = true; } protected virtual void Dispose(bool disposing) diff --git a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs index 146cf6607..da1613f41 100644 --- a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs @@ -14,5 +14,6 @@ namespace Ryujinx.Graphics.Vulkan public abstract void SetAntiAliasing(AntiAliasing effect); public abstract void SetScalingFilter(ScalingFilter scalerType); public abstract void SetScalingFilterLevel(float scale); + public abstract void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled); } } diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 434894328..09e7f570a 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration /// <summary> /// The current version of the file format /// </summary> - public const int CurrentVersion = 47; + public const int CurrentVersion = 48; /// <summary> /// Version of the configuration file format @@ -186,6 +186,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public bool EnableMacroHLE { get; set; } + /// <summary> + /// Enables or disables color space passthrough, if available. + /// </summary> + public bool EnableColorSpacePassthrough { get; set; } + /// <summary> /// Enables or disables profiled translation cache persistency /// </summary> diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index 7ab20e329..ee898354b 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -485,6 +485,11 @@ namespace Ryujinx.Ui.Common.Configuration /// </summary> public ReactiveObject<bool> EnableMacroHLE { get; private set; } + /// <summary> + /// Enables or disables color space passthrough, if available. + /// </summary> + public ReactiveObject<bool> EnableColorSpacePassthrough { get; private set; } + /// <summary> /// Graphics backend /// </summary> @@ -535,6 +540,8 @@ namespace Ryujinx.Ui.Common.Configuration PreferredGpu.Event += static (sender, e) => LogValueChange(e, nameof(PreferredGpu)); EnableMacroHLE = new ReactiveObject<bool>(); EnableMacroHLE.Event += static (sender, e) => LogValueChange(e, nameof(EnableMacroHLE)); + EnableColorSpacePassthrough = new ReactiveObject<bool>(); + EnableColorSpacePassthrough.Event += static (sender, e) => LogValueChange(e, nameof(EnableColorSpacePassthrough)); AntiAliasing = new ReactiveObject<AntiAliasing>(); AntiAliasing.Event += static (sender, e) => LogValueChange(e, nameof(AntiAliasing)); ScalingFilter = new ReactiveObject<ScalingFilter>(); @@ -667,6 +674,7 @@ namespace Ryujinx.Ui.Common.Configuration EnableShaderCache = Graphics.EnableShaderCache, EnableTextureRecompression = Graphics.EnableTextureRecompression, EnableMacroHLE = Graphics.EnableMacroHLE, + EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough, EnablePtc = System.EnablePtc, EnableInternetAccess = System.EnableInternetAccess, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, @@ -772,6 +780,7 @@ namespace Ryujinx.Ui.Common.Configuration Graphics.EnableShaderCache.Value = true; Graphics.EnableTextureRecompression.Value = false; Graphics.EnableMacroHLE.Value = true; + Graphics.EnableColorSpacePassthrough.Value = false; Graphics.AntiAliasing.Value = AntiAliasing.None; Graphics.ScalingFilter.Value = ScalingFilter.Bilinear; Graphics.ScalingFilterLevel.Value = 80; @@ -1391,6 +1400,15 @@ namespace Ryujinx.Ui.Common.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 48) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 48."); + + configurationFileFormat.EnableColorSpacePassthrough = false; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1426,6 +1444,7 @@ namespace Ryujinx.Ui.Common.Configuration Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; + Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough; System.EnablePtc.Value = configurationFileFormat.EnablePtc; System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; From 5e9678c8fad4625026268e457f4c3e23bdc22697 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 9 Aug 2023 23:27:45 +0200 Subject: [PATCH 727/737] Allow access to code memory for exefs mods (#5518) * Allow access to code memory for exefs mods * Add ASLR workaround for Skyline * Hardcode allowCodeMemoryForJit to true --- src/ARMeilleure/Translation/PTC/Ptc.cs | 2 +- .../Translation/PTC/PtcFormatter.cs | 20 ++++++++ .../Translation/PTC/PtcProfiler.cs | 46 ++++++++++++++----- .../Extensions/FileSystemExtensions.cs | 5 +- .../Loaders/Processes/ProcessLoaderHelper.cs | 7 ++- 5 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index ce653383e..6f6dfcadf 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 5502; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5518; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/src/ARMeilleure/Translation/PTC/PtcFormatter.cs b/src/ARMeilleure/Translation/PTC/PtcFormatter.cs index ddac31338..60953dcd9 100644 --- a/src/ARMeilleure/Translation/PTC/PtcFormatter.cs +++ b/src/ARMeilleure/Translation/PTC/PtcFormatter.cs @@ -27,6 +27,26 @@ namespace ARMeilleure.Translation.PTC return dictionary; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Dictionary<TKey, TValue> DeserializeAndUpdateDictionary<TKey, TValue>(Stream stream, Func<Stream, TValue> valueFunc, Func<TKey, TValue, (TKey, TValue)> updateFunc) where TKey : struct + { + Dictionary<TKey, TValue> dictionary = new(); + + int count = DeserializeStructure<int>(stream); + + for (int i = 0; i < count; i++) + { + TKey key = DeserializeStructure<TKey>(stream); + TValue value = valueFunc(stream); + + (key, value) = updateFunc(key, value); + + dictionary.Add(key, value); + } + + return dictionary; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<T> DeserializeList<T>(Stream stream) where T : struct { diff --git a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs index 3a4bfcec6..0fe78edab 100644 --- a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -9,10 +9,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; +using System.Timers; using static ARMeilleure.Translation.PTC.PtcFormatter; +using Timer = System.Timers.Timer; namespace ARMeilleure.Translation.PTC { @@ -20,7 +23,11 @@ namespace ARMeilleure.Translation.PTC { private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; - private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5518; //! Not to be incremented manually for each change to the ARMeilleure project. + + private static readonly uint[] _migrateInternalVersions = { + 1866, + }; private const int SaveInterval = 30; // Seconds. @@ -28,7 +35,7 @@ namespace ARMeilleure.Translation.PTC private readonly Ptc _ptc; - private readonly System.Timers.Timer _timer; + private readonly Timer _timer; private readonly ulong _outerHeaderMagic; @@ -51,7 +58,7 @@ namespace ARMeilleure.Translation.PTC { _ptc = ptc; - _timer = new System.Timers.Timer((double)SaveInterval * 1000d); + _timer = new Timer(SaveInterval * 1000d); _timer.Elapsed += PreSave; _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); @@ -168,7 +175,7 @@ namespace ARMeilleure.Translation.PTC return false; } - if (outerHeader.InfoFileVersion != InternalVersion) + if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion)) { InvalidateCompressedStream(compressedStream); @@ -211,7 +218,19 @@ namespace ARMeilleure.Translation.PTC return false; } - ProfiledFuncs = Deserialize(stream); + switch (outerHeader.InfoFileVersion) + { + case InternalVersion: + ProfiledFuncs = Deserialize(stream); + break; + case 1866: + ProfiledFuncs = Deserialize(stream, (address, profile) => (address + 0x500000UL, profile)); + break; + default: + Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache."); + InvalidateCompressedStream(compressedStream); + return false; + } Debug.Assert(stream.Position == stream.Length); @@ -225,9 +244,14 @@ namespace ARMeilleure.Translation.PTC return true; } - private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream) + private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream, Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null) { - return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream)); + if (migrateEntryFunc != null) + { + return DeserializeAndUpdateDictionary(stream, DeserializeStructure<FuncProfile>, migrateEntryFunc); + } + + return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>); } private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream) @@ -240,7 +264,7 @@ namespace ARMeilleure.Translation.PTC compressedStream.SetLength(0L); } - private void PreSave(object source, System.Timers.ElapsedEventArgs e) + private void PreSave(object source, ElapsedEventArgs e) { _waitEvent.Reset(); @@ -277,7 +301,7 @@ namespace ARMeilleure.Translation.PTC { Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); - stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); + stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); lock (_lock) { @@ -288,7 +312,7 @@ namespace ARMeilleure.Translation.PTC Debug.Assert(stream.Position == stream.Length); - stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); + stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); stream.Seek(0L, SeekOrigin.Begin); @@ -332,7 +356,7 @@ namespace ARMeilleure.Translation.PTC private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs) { - SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); + SerializeDictionary(stream, profiledFuncs, SerializeStructure); } [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)] diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index 040d1143d..07bbaf12b 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -89,9 +89,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled."); } - // We allow it for nx-hbloader because it can be used to launch homebrew. - bool allowCodeMemoryForJit = programId == 0x010000000000100DUL || isHomebrew; - string programName = ""; if (!isHomebrew && programId > 0x010000000000FFFF) @@ -119,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions metaLoader, nacpData, enablePtc, - allowCodeMemoryForJit, + true, programName, metaLoader.GetProgramId(), null, diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index d14a013af..292a5c122 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -28,6 +28,11 @@ namespace Ryujinx.HLE.Loaders.Processes { static class ProcessLoaderHelper { + // NOTE: If you want to change this value make sure to increment the InternalVersion of Ptc and PtcProfiler. + // You also need to add a new migration path and adjust the existing ones. + // TODO: Remove this workaround when ASLR is implemented. + private const ulong CodeStartOffset = 0x500000UL; + public static LibHac.Result RegisterProgramMapInfo(Switch device, PartitionFileSystem partitionFileSystem) { ulong applicationId = 0; @@ -242,7 +247,7 @@ namespace Ryujinx.HLE.Loaders.Processes ulong argsStart = 0; uint argsSize = 0; - ulong codeStart = (meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL; + ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset; uint codeSize = 0; var buildIds = executables.Select(e => (e switch From fe15c77d30b94a8b720b520dcacf39a0c832d58f Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Thu, 10 Aug 2023 05:29:15 +0200 Subject: [PATCH 728/737] [Hotfix] hid: Prevent out of bounds array access (#5547) * hid: Prevent out of bounds array access * Cast player to uint * Replace lambda function with local function --- .../Services/Hid/HidDevices/NpadDevices.cs | 5 +++ src/Ryujinx.Tests/Cpu/EnvironmentTests.cs | 33 ++++++++++--------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 240933ada..86c6a825f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -70,6 +70,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid internal void SetSupportedPlayer(PlayerIndex player, bool supported = true) { + if ((uint)player >= _supportedPlayers.Length) + { + return; + } + _supportedPlayers[(int)player] = supported; } diff --git a/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs b/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs index 1ef12de5f..5e6b286bd 100644 --- a/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs +++ b/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs @@ -48,7 +48,22 @@ namespace Ryujinx.Tests.Cpu bool methodCalled = false; bool isFz = false; - var managedMethod = () => + var method = TranslatorTestMethods.GenerateFpFlagsPInvokeTest(); + + // This method sets flush-to-zero and then calls the managed method. + // Before and after setting the flags, it ensures subnormal addition works as expected. + // It returns a positive result if any tests fail, and 0 on success (or if the platform cannot change FP flags) + int result = method(Marshal.GetFunctionPointerForDelegate(ManagedMethod)); + + // Subnormal results are not flushed to zero by default, which we should have returned to exiting the method. + Assert.AreNotEqual(GetDenormal() + GetZero(), 0f); + + Assert.True(result == 0); + Assert.True(methodCalled); + Assert.True(isFz); + return; + + void ManagedMethod() { // Floating point math should not modify fp flags. float test = 2f * 3.5f; @@ -73,21 +88,7 @@ namespace Ryujinx.Tests.Cpu methodCalled = true; } - }; - - var method = TranslatorTestMethods.GenerateFpFlagsPInvokeTest(); - - // This method sets flush-to-zero and then calls the managed method. - // Before and after setting the flags, it ensures subnormal addition works as expected. - // It returns a positive result if any tests fail, and 0 on success (or if the platform cannot change FP flags) - int result = method(Marshal.GetFunctionPointerForDelegate(managedMethod)); - - // Subnormal results are not flushed to zero by default, which we should have returned to exiting the method. - Assert.AreNotEqual(GetDenormal() + GetZero(), 0f); - - Assert.True(result == 0); - Assert.True(methodCalled); - Assert.True(isFz); + } } } } From 7b2225c6b0939b9720c56a89cb0c91311b2e19e5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Sat, 12 Aug 2023 01:47:22 +0100 Subject: [PATCH 729/737] Ava UI: Avalonia 11 & FluentAvalonia 2 Support (#4362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * It builds (Doesn’t run waiting on FluentAvalonia Preview 5 Release) * Enable CompiledBindings by default * Ignore `PointerPressedEventArgs` Init warning * Define MIME and UTI Types * Update `UserProfileImageSelectorView` to StorageProvider API * PFS0 Magic * Update `MainWindowViewModel` to StorageProvider API * Update `SettingsUIView` to StorageProvider API * Update `ApplicationHelper` to StorageProvider API * Use `IsCheckChanged` * Rename events * Update Fluent Avalonia to Preivew 5 * More package updates * Fix long selection bar * return glyph value directly, instead of using a binding * fix menu item checkboxes * Fix build * Update to Preview 6 Unicorn conflict Fix remaining package oopsie * Fix issues from merge * Fix some warnings * Warnings * Squashed commit of the following: commit 79d1c190dba48e405a833f654691e47509a29792 Author: Mary <mary@mary.zone> Date: Sun Apr 16 11:38:07 2023 +0200 chore: Update Silk.NET to 2.17.1 (#4686) commit 2bc88467eb377a0ca1a8b51700300422422c8c37 Author: Ac_K <Acoustik666@gmail.com> Date: Sun Apr 16 09:37:31 2023 +0000 Update README.md commit baf8752e74488a419074ae1d484e54a00bc01973 Author: Vincenzo Nizza <vincenzonizzaufficio@gmail.com> Date: Sun Apr 16 11:19:33 2023 +0200 Ensure the updater doesn't delete hidden or system files (#4626) * Copy desktop.ini to update directory if it exists in HomeDir * EnumerateFilesToDelete() exclude files with "Hidden" and "System" attributes commit d5e4378aea086d9219f890e33cf81d566d96b9ae Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun Apr 16 09:02:06 2023 +0000 nuget: bump DynamicData from 7.13.1 to 7.13.5 (#4654) Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.1 to 7.13.5. - [Release notes](https://github.com/reactiveui/DynamicData/releases) - [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md) - [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.1...7.13.5) --- updated-dependencies: - dependency-name: DynamicData dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit 6dbcdfea47e60aadefd59a75e43549793481f853 Author: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun Apr 16 09:09:02 2023 +0200 Ava: Fix nca extraction window never closing & minor cleanup (#4569) * ava: Remove unused doWhileDeferred parameters * ava: Minimally improve swkbd dialog It's currently impossible to get the dialog to redirect focus to the InputBox. * ava: Fix nca extraction dialog never closing Also contains some minor cleanup commit c5258cf082b10f335f81487f22b7eeb86075e09e Author: NitroTears <73270647+NitroTears@users.noreply.github.com> Date: Sun Apr 16 11:03:35 2023 +1000 Ability to hide file types in Game List (#4555) * Added HiddenFileTypes to config state, and check to file enumeration * Added hiddenfiletypes checkboxes to the UI * Added Ava version of HiddenFileTypes * Inverted Hide to Show with file types, minor formatting * all variables with a reference to 'hidden' is now 'shown' * one more variable name changed * review feedback * added FileTypes extension methof to get the correlating config value * moved extension method to new folder and file in Ryujinx.Ui.Common * added default case for ToggleFileType * changed exception type to OutOfRangeException commit 5c89e22bb98072adc240c2eb2d26d25fa119fe7d Author: Daniel Shala <daniel.shala08@gmail.com> Date: Sat Apr 15 18:11:24 2023 +0200 Added check for eventual symlink when displaying game files. (#4526) * Added check for eventual symlink when displaying game files. * Moved symlink check logic * Moved symlink check logic * Fixed prev commit --------- Co-authored-by: Daniel Shala <danielshala00@gmail.com> commit 11ecff2ff04633d261b9a43db792f6438df63f40 Author: Alex Barney <thealexbarney@gmail.com> Date: Fri Apr 14 16:00:34 2023 -0700 Rename Hipc to Cmif where appropriate (#3880) commit 4c3f09644a033dbf70258c4c0e5a848263b16bbd Author: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Wed Apr 12 20:18:40 2023 +0100 Move swkbd message null check into constructor (#4671) commit e187a8870a6f19ac0a85b08aece3c1a1e196e379 Author: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed Apr 12 03:09:47 2023 +0200 HLE: Deal with empty title names properly (#4643) * hle: Deal with empty titleNames in some languages * gui: Fix displaying the wrong title name * Remove unnecessary bounds check * Fix a NRE when getting the version string * Restore empty string logic commit a64fee29dc6b8e523d61abb7e79ceaa95a558c6c Author: riperiperi <rhy3756547@hotmail.com> Date: Tue Apr 11 08:23:41 2023 +0100 Vulkan: add situational "Fast Flush" mode (#4667) * Flush in the middle of long command buffers. * Vulkan: add situational "Fast Flush" mode The AutoFlushCounter class was added to periodically flush Vulkan command buffers throughout a frame, which reduces latency to the GPU as commands are submitted and processed much sooner. This was done by allowing command buffers to flush when framebuffer attachments changed. However, some games have incredibly long render passes with a large number of draws, and really aggressive data access that forces GPU sync. The Vulkan backend could potentially end up building a single command buffer for 4-5ms if a pass has enough draws, such as in BOTW. In the scenario where sync is waited on immediately after submission, this would have to wait for the completion of a much longer command buffer than usual. The solution is to force command buffer submission periodically in a "fast flush" mode. This will end up splitting render passes, but it will only enable if sync is aggressive enough. This should improve performance in GPU limited scenarios, or in games that aggressively wait on synchronization. In some games, it may only kick in when res scaling. It won't trigger in games like SMO where sync is not an issue. Improves performance in Pokemon Scarlet/Violet (res scaled) and BOTW (in general). * Add conversions in milliseconds next to flush timers. commit 9ef94c8292beda825fa76e05ad2e561c6d571c95 Author: riperiperi <rhy3756547@hotmail.com> Date: Tue Apr 11 07:55:04 2023 +0100 ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext (#4661) * ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext Some games access these system registers several tens of thousands of times in a second from many different threads. While this isn't really crippling, it is a lot of wasted time spent in a reverse pinvoke transition. Example games are Pokemon Scarlet/Violet and BOTW. These games have a lot of different potential bottlenecks so it's unlikely you will see a consistent improvement, but it definitely disappears from the cpu profile. * Remove unreachable code. * Add ulong conversion for offsets * Nit commit 915d6d044cbf8c89935f14b8c7e085ad729f0e28 Author: riperiperi <rhy3756547@hotmail.com> Date: Tue Apr 11 07:32:31 2023 +0100 OpenGL: Fix OBS/Overlays again by binding FB before present (#4668) This seems to have been removed by the Post-Processing PR, but it is required for the display in OBS to be the right way up and properly scaled. I've tested this with AA and FSR on MK8D and it seems to behave properly. Testing is welcome. commit a4780ab33b9ca6b698917ded3ef6db6e6716cad1 Author: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Mon Apr 10 23:04:31 2023 +0100 Force activate parent window before dialog is shown (#4663) * Fix build Extraction dialogue not working * Avalonia Preview 7 Needs Fluent Avalonia update still… * Fix Render Scaling * Update Fluent Avalonia * Remove `pfs0` as runnable file type * Restore Info.plist formatting * Plist Format * Update Avalonia.Svg.Skia * Update theme code (TODO) * swtich to using theme variants for light dark * Fix crashes * Text centering issues * Update `TitleUpdateViewModel` to StorageProvider API * Fixed for new PR (Will crash on launch) * Fixes… * UI: Fix sections extraction (#4820) * UI: Fix sections extraction There is currently an issue when the update NCA doesn't contains the section we want to extract, this is fixed by adding a check. I have fixed the inverted handler of ExeFs/Logo introduced in #4755. Fixes #4521 * Addresses feedback * Fix issues… * Preview 8 * Fix fuck ups * Fixes * More cleanup * Ava 11 RC Maybe there is a god * Update FluentAvalonia * update svg * Second RC (kill me) * It builds * Ava 11 * Remove unnecessary usings * Fix build * Formatting * GAS GAS GAS!!!! * Fix DLC Window Crash * Linux runner try not to crash challenge (impossible) * Add app.manifest * Fix accidental Silk.NET.Vulkan bump * Try fix truncation * Linux fix popup Windows * Fix cutoff text on windows * Status bar styling fixes * Volume Toggle Split Button Fixes * Fix load bar color * Fix shortcuts * Best we're gonna get * Fix spacing Co-authored-by: Exhigh <exhigh01@gmail.com> * Formatting * Fix Profile Dropdown * Fix Window Startup Position * Format Fixes * Fix stupid mistake * Fix accidental change * Scaling Handler (peri pls make sure is working) * Remove Locale Reflection Binding Use + Unsued Usings * Fix formatting Code styling Ughhhh Fix interface Make TimeZoneConverter internal * Remove bell workaround (no longer needed) * Disable accent menu * Update to Ava 11.0.2 * Peri suggestions * Formatting * Cleanup a bunch of jank * Dependency update * Berry fixes and suggestions * Final suggestions * Rename assemblyIdentity to Ryujinx.Emulator.Avalonia --------- Co-authored-by: Emmanuel Hansen <emmausssss@gmail.com> Co-authored-by: Ac_K <Acoustik666@gmail.com> Co-authored-by: Exhigh <exhigh01@gmail.com> Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- Directory.Packages.props | 19 +- distribution/macos/Info.plist | 109 +++++- src/Ryujinx.Ava/App.axaml | 8 + src/Ryujinx.Ava/App.axaml.cs | 39 +- src/Ryujinx.Ava/AppHost.cs | 41 +- src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml | 65 ---- src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml | 57 --- src/Ryujinx.Ava/Assets/Styles/Styles.xaml | 361 +++++++++++------- src/Ryujinx.Ava/Assets/Styles/Themes.xaml | 85 +++++ src/Ryujinx.Ava/Common/ApplicationHelper.cs | 275 ++++++------- .../Input/AvaloniaKeyboardDriver.cs | 7 - src/Ryujinx.Ava/Program.cs | 9 +- src/Ryujinx.Ava/Ryujinx.Ava.csproj | 13 +- .../UI/Applet/ErrorAppletWindow.axaml | 2 +- .../UI/Applet/ErrorAppletWindow.axaml.cs | 10 +- .../UI/Applet/SwkbdAppletDialog.axaml | 6 +- .../UI/Controls/ApplicationContextMenu.axaml | 4 +- .../Controls/ApplicationContextMenu.axaml.cs | 18 +- .../UI/Controls/ApplicationGridView.axaml | 22 +- .../UI/Controls/ApplicationGridView.axaml.cs | 8 +- .../UI/Controls/ApplicationListView.axaml | 18 +- .../UI/Controls/ApplicationListView.axaml.cs | 8 +- .../UI/Helpers/GlyphValueConverter.cs | 10 +- src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs | 52 --- src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs | 15 - .../UI/Helpers/OffscreenTextBox.cs | 1 - .../UI/Helpers/TimeZoneConverter.cs | 28 ++ src/Ryujinx.Ava/UI/Models/UserProfile.cs | 4 +- src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 14 +- .../UI/Renderer/RendererHost.axaml.cs | 10 +- .../UI/ViewModels/AboutWindowViewModel.cs | 19 +- .../DownloadableContentManagerViewModel.cs | 36 +- .../UI/ViewModels/MainWindowViewModel.cs | 215 ++++++----- .../UI/ViewModels/TitleUpdateViewModel.cs | 37 +- .../UI/Views/Input/ControllerInputView.axaml | 19 +- .../Views/Input/ControllerInputView.axaml.cs | 83 ++-- .../UI/Views/Input/MotionInputView.axaml | 1 - .../UI/Views/Input/RumbleInputView.axaml | 1 - .../UI/Views/Main/MainMenuBarView.axaml | 89 +++-- .../UI/Views/Main/MainMenuBarView.axaml.cs | 6 +- .../UI/Views/Main/MainStatusBarView.axaml | 39 +- .../UI/Views/Main/MainStatusBarView.axaml.cs | 6 + .../UI/Views/Main/MainViewControls.axaml | 13 +- .../UI/Views/Settings/SettingsAudioView.axaml | 3 +- .../UI/Views/Settings/SettingsCPUView.axaml | 1 - .../Views/Settings/SettingsGraphicsView.axaml | 5 +- .../Views/Settings/SettingsHotkeysView.axaml | 5 +- .../Settings/SettingsHotkeysView.axaml.cs | 2 +- .../UI/Views/Settings/SettingsInputView.axaml | 1 - .../Views/Settings/SettingsLoggingView.axaml | 1 - .../Views/Settings/SettingsNetworkView.axaml | 3 +- .../Views/Settings/SettingsSystemView.axaml | 110 ++++-- .../Settings/SettingsSystemView.axaml.cs | 19 +- .../UI/Views/Settings/SettingsUIView.axaml | 3 +- .../UI/Views/Settings/SettingsUIView.axaml.cs | 42 +- .../UI/Views/User/UserEditorView.axaml | 1 - .../User/UserFirmwareAvatarSelectorView.axaml | 5 +- .../User/UserProfileImageSelectorView.axaml | 1 - .../UserProfileImageSelectorView.axaml.cs | 38 +- .../UI/Views/User/UserRecovererView.axaml | 3 +- .../UI/Views/User/UserSaveManagerView.axaml | 6 +- .../UI/Views/User/UserSelectorView.axaml | 9 +- src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml | 7 +- src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml | 15 +- src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml | 12 +- .../ContentDialogOverlayWindow.axaml.cs | 9 +- .../DownloadableContentManagerWindow.axaml | 8 +- src/Ryujinx.Ava/UI/Windows/MainWindow.axaml | 20 +- .../UI/Windows/MainWindow.axaml.cs | 65 ++-- .../UI/Windows/SettingsWindow.axaml | 29 +- .../UI/Windows/SettingsWindow.axaml.cs | 6 +- src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs | 4 +- .../UI/Windows/TitleUpdateWindow.axaml | 6 +- .../UI/Windows/TitleUpdateWindow.axaml.cs | 2 +- src/Ryujinx.Ava/app.manifest | 10 + 75 files changed, 1252 insertions(+), 1081 deletions(-) delete mode 100644 src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml delete mode 100644 src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml create mode 100644 src/Ryujinx.Ava/Assets/Styles/Themes.xaml delete mode 100644 src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs create mode 100644 src/Ryujinx.Ava/UI/Helpers/TimeZoneConverter.cs create mode 100644 src/Ryujinx.Ava/app.manifest diff --git a/Directory.Packages.props b/Directory.Packages.props index 6191c4984..fbae486c3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,21 +3,21 @@ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> </PropertyGroup> <ItemGroup> - <PackageVersion Include="Avalonia" Version="0.10.21" /> - <PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" /> - <PackageVersion Include="Avalonia.Desktop" Version="0.10.21" /> - <PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" /> - <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" /> - <PackageVersion Include="Avalonia.Svg" Version="0.10.18" /> - <PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" /> + <PackageVersion Include="Avalonia" Version="11.0.3" /> + <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.3" /> + <PackageVersion Include="Avalonia.Desktop" Version="11.0.3" /> + <PackageVersion Include="Avalonia.Diagnostics" Version="11.0.3" /> + <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.3" /> + <PackageVersion Include="Avalonia.Svg" Version="11.0.0" /> + <PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DynamicData" Version="7.14.2" /> - <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> + <PackageVersion Include="FluentAvaloniaUI" Version="2.0.1" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> - <PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" /> + <PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" /> <PackageVersion Include="LibHac" Version="0.18.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" /> @@ -48,6 +48,5 @@ <PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.2" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> - <PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" /> </ItemGroup> </Project> \ No newline at end of file diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist index 968814f94..6e068ba2d 100644 --- a/distribution/macos/Info.plist +++ b/distribution/macos/Info.plist @@ -44,10 +44,115 @@ <string>public.app-category.games</string> <key>LSMinimumSystemVersion</key> <string>11.0</string> + <key>UTExportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeDescription</key> + <string>Extensible Application Markup Language</string> + <key>UTTypeConformsTo</key> + <array> + <string>public.xml</string> + </array> + <key>UTTypeIdentifier</key> + <string>com.ryujinx.xaml</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>xaml</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeDescription</key> + <string>Nintendo Submission Package</string> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeIdentifier</key> + <string>com.ryujinx.nsp</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>nsp</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeDescription</key> + <string>Nintendo Switch Cartridge</string> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeIdentifier</key> + <string>com.ryujinx.xci</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>xci</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeDescription</key> + <string>Nintendo Content Archive</string> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeIdentifier</key> + <string>com.ryujinx.nca</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>nca</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeDescription</key> + <string>Nintendo Relocatable Object</string> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeIdentifier</key> + <string>com.ryujinx.nro</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>nro</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeDescription</key> + <string>Nintendo Shared Object</string> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeIdentifier</key> + <string>com.ryujinx.nso</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>nso</string> + </array> + </dict> + </dict> + </array> <key>LSEnvironment</key> <dict> - <key>COMPlus_DefaultStackSize</key> + <key>DOTNET_DefaultStackSize</key> <string>200000</string> </dict> </dict> -</plist> +</plist> \ No newline at end of file diff --git a/src/Ryujinx.Ava/App.axaml b/src/Ryujinx.Ava/App.axaml index 72bc0deee..eab318b7b 100644 --- a/src/Ryujinx.Ava/App.axaml +++ b/src/Ryujinx.Ava/App.axaml @@ -3,7 +3,15 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sty="using:FluentAvalonia.Styling"> + <Application.Resources> + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <MergeResourceInclude Source="/Assets/Styles/Themes.xaml"/> + </ResourceDictionary.MergedDictionaries> + </ResourceDictionary> + </Application.Resources> <Application.Styles> <sty:FluentAvaloniaTheme PreferSystemTheme="False" /> + <StyleInclude Source="/Assets/Styles/Styles.xaml"/> </Application.Styles> </Application> \ No newline at end of file diff --git a/src/Ryujinx.Ava/App.axaml.cs b/src/Ryujinx.Ava/App.axaml.cs index 4ecc424a6..031e7e447 100644 --- a/src/Ryujinx.Ava/App.axaml.cs +++ b/src/Ryujinx.Ava/App.axaml.cs @@ -3,7 +3,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Styling; using Avalonia.Threading; -using FluentAvalonia.Styling; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; @@ -24,6 +23,11 @@ namespace Ryujinx.Ava Name = $"Ryujinx {Program.Version}"; AvaloniaXamlLoader.Load(this); + + if (OperatingSystem.IsMacOS()) + { + Process.Start("/usr/bin/defaults", "write org.ryujinx.Ryujinx ApplePressAndHoldEnabled -bool false"); + } } public override void OnFrameworkInitializationCompleted() @@ -89,8 +93,6 @@ namespace Ryujinx.Ava string themePath = ConfigurationState.Instance.Ui.CustomThemePath; bool enableCustomTheme = ConfigurationState.Instance.Ui.EnableCustomTheme; - const string BaseStyleUrl = "avares://Ryujinx.Ava/Assets/Styles/Base{0}.xaml"; - if (string.IsNullOrWhiteSpace(baseStyle)) { ConfigurationState.Instance.Ui.BaseStyle.Value = "Dark"; @@ -98,31 +100,12 @@ namespace Ryujinx.Ava baseStyle = ConfigurationState.Instance.Ui.BaseStyle; } - var theme = AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>(); - - theme.RequestedTheme = baseStyle; - - var currentStyles = this.Styles; - - // Remove all styles except the base style. - if (currentStyles.Count > 1) + RequestedThemeVariant = baseStyle switch { - currentStyles.RemoveRange(1, currentStyles.Count - 1); - } - - IStyle newStyles = null; - - // Load requested style, and fallback to Dark theme if loading failed. - try - { - newStyles = (Styles)AvaloniaXamlLoader.Load(new Uri(string.Format(BaseStyleUrl, baseStyle), UriKind.Absolute)); - } - catch (XamlLoadException) - { - newStyles = (Styles)AvaloniaXamlLoader.Load(new Uri(string.Format(BaseStyleUrl, "Dark"), UriKind.Absolute)); - } - - currentStyles.Add(newStyles); + "Light" => ThemeVariant.Light, + "Dark" => ThemeVariant.Dark, + _ => ThemeVariant.Default + }; if (enableCustomTheme) { @@ -133,7 +116,7 @@ namespace Ryujinx.Ava var themeContent = File.ReadAllText(themePath); var customStyle = AvaloniaRuntimeXamlLoader.Parse<IStyle>(themeContent); - currentStyles.Add(customStyle); + Styles.Add(customStyle); } catch (Exception ex) { diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 786b45070..a8388e9cd 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -3,6 +3,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; +using Avalonia.Rendering; using Avalonia.Threading; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.Dummy; @@ -54,6 +55,7 @@ using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; using Image = SixLabors.ImageSharp.Image; using InputManager = Ryujinx.Input.HLE.InputManager; +using IRenderer = Ryujinx.Graphics.GAL.IRenderer; using Key = Ryujinx.Input.Key; using MouseButton = Ryujinx.Input.MouseButton; using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; @@ -167,9 +169,9 @@ namespace Ryujinx.Ava ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; - _topLevel.PointerMoved += TopLevel_PointerEnterOrMoved; - _topLevel.PointerEnter += TopLevel_PointerEnterOrMoved; - _topLevel.PointerLeave += TopLevel_PointerLeave; + _topLevel.PointerMoved += TopLevel_PointerEnteredOrMoved; + _topLevel.PointerEntered += TopLevel_PointerEnteredOrMoved; + _topLevel.PointerExited += TopLevel_PointerExited; if (OperatingSystem.IsWindows()) { @@ -194,26 +196,23 @@ namespace Ryujinx.Ava _gpuDoneEvent = new ManualResetEvent(false); } - private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e) + private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e) { if (sender is MainWindow window) { _lastCursorMoveTime = Stopwatch.GetTimestamp(); - if (RendererHost.EmbeddedWindow.TransformedBounds != null) - { - var point = e.GetCurrentPoint(window).Position; - var bounds = RendererHost.EmbeddedWindow.TransformedBounds.Value.Clip; + var point = e.GetCurrentPoint(window).Position; + var bounds = RendererHost.EmbeddedWindow.Bounds; - _isCursorInRenderer = point.X >= bounds.X && - point.X <= bounds.Width + bounds.X && - point.Y >= bounds.Y && - point.Y <= bounds.Height + bounds.Y; - } + _isCursorInRenderer = point.X >= bounds.X && + point.X <= bounds.Width + bounds.X && + point.Y >= bounds.Y && + point.Y <= bounds.Height + bounds.Y; } } - private void TopLevel_PointerLeave(object sender, PointerEventArgs e) + private void TopLevel_PointerExited(object sender, PointerEventArgs e) { _isCursorInRenderer = false; } @@ -265,7 +264,7 @@ namespace Ryujinx.Ava { if (_renderer != null) { - double scale = _topLevel.PlatformImpl.RenderScaling; + double scale = _topLevel.RenderScaling; _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); } @@ -359,7 +358,7 @@ namespace Ryujinx.Ava _viewModel.SetUiProgressHandlers(Device); - RendererHost.SizeChanged += Window_SizeChanged; + RendererHost.BoundsChanged += Window_BoundsChanged; _isActive = true; @@ -469,9 +468,9 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough; - _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved; - _topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved; - _topLevel.PointerLeave -= TopLevel_PointerLeave; + _topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved; + _topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved; + _topLevel.PointerExited -= TopLevel_PointerExited; _gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Dispose(); @@ -849,7 +848,7 @@ namespace Ryujinx.Ava return deviceDriver; } - private void Window_SizeChanged(object sender, Size e) + private void Window_BoundsChanged(object sender, Size e) { Width = (int)e.Width; Height = (int)e.Height; @@ -899,7 +898,7 @@ namespace Ryujinx.Ava Width = (int)RendererHost.Bounds.Width; Height = (int)RendererHost.Bounds.Height; - _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling)); + _renderer.Window.SetSize((int)(Width * _topLevel.RenderScaling), (int)(Height * _topLevel.RenderScaling)); _chrono.Start(); diff --git a/src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml b/src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml deleted file mode 100644 index c7f6266fb..000000000 --- a/src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml +++ /dev/null @@ -1,65 +0,0 @@ -<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" /> - <Design.PreviewWith> - <Border Height="2000" Padding="20"> - <StackPanel Spacing="5"> - <TextBlock Text="Code Font Family" /> - <Grid RowDefinitions="*,Auto"> - <Menu Grid.Row="1" Width="100"> - <MenuItem Header="File"> - <MenuItem Header="Test 1" /> - <MenuItem Header="Test 2" /> - <MenuItem Header="Test 3"> - <MenuItem.Icon> - <CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" /> - </MenuItem.Icon> - </MenuItem> - </MenuItem> - </Menu> - <StackPanel Orientation="Horizontal"> - <Button - Name="btnAdd" - HorizontalAlignment="Right" - Content="Add" /> - <Button - Name="btnRem" - HorizontalAlignment="Right" - Content="Add" /> - <TextBox - Width="100" - VerticalAlignment="Center" - Text="Rrrrr" - UseFloatingWatermark="True" - Watermark="Hello" /> - <CheckBox>Test Check</CheckBox> - </StackPanel> - </Grid> - </StackPanel> - </Border> - </Design.PreviewWith> - <Styles.Resources> - <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" /> - <SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" /> - <SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" /> - <Color x:Key="ControlFillColorSecondary">#008AA8</Color> - <SolidColorBrush x:Key="ControlFillColorSecondaryBrush" Color="{StaticResource ControlFillColorSecondary}" /> - <StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="ControlFillColorSecondaryBrush" /> - <Color x:Key="SystemAccentColor">#FF00C3E3</Color> - <Color x:Key="SystemAccentColorDark1">#FF99b000</Color> - <Color x:Key="SystemAccentColorDark2">#FF006d7d</Color> - <Color x:Key="SystemAccentColorDark3">#FF00525E</Color> - <Color x:Key="SystemAccentColorLight1">#FF00dbff</Color> - <Color x:Key="SystemAccentColorLight2">#FF19dfff</Color> - <Color x:Key="SystemAccentColorLight3">#FF33e3ff</Color> - <Color x:Key="DataGridSelectionColor">#FF00FABB</Color> - <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color> - <Color x:Key="ThemeControlBorderColor">#FF505050</Color> - <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color> - <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color> - <Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color> - <Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color> - <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color> - <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color> - <Color x:Key="SecondaryTextColor">#A0FFFFFF</Color> - </Styles.Resources> -</Styles> \ No newline at end of file diff --git a/src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml b/src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml deleted file mode 100644 index 70cb051cf..000000000 --- a/src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml +++ /dev/null @@ -1,57 +0,0 @@ -<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" /> - <Design.PreviewWith> - <Border Height="2000" Padding="20"> - <StackPanel Spacing="5"> - <TextBlock Text="Code Font Family" /> - <Grid RowDefinitions="*,Auto"> - <Menu Grid.Row="1" Width="100"> - <MenuItem Header="File"> - <MenuItem Header="Test 1" /> - <MenuItem Header="Test 2" /> - <MenuItem Header="Test 3"> - <MenuItem.Icon> - <CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" /> - </MenuItem.Icon> - </MenuItem> - </MenuItem> - </Menu> - <StackPanel Orientation="Horizontal"> - <Button - Name="btnAdd" - HorizontalAlignment="Right" - Content="Add" /> - <Button - Name="btnRem" - HorizontalAlignment="Right" - Content="Add" /> - <TextBox - Width="100" - VerticalAlignment="Center" - Text="Rrrrr" - UseFloatingWatermark="True" - Watermark="Hello" /> - <CheckBox>Test Check</CheckBox> - </StackPanel> - </Grid> - </StackPanel> - </Border> - </Design.PreviewWith> - <Styles.Resources> - <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" /> - <SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" /> - <SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" /> - <Color x:Key="SystemAccentColor">#FF00C3E3</Color> - <Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color> - <Color x:Key="DataGridSelectionColor">#FF00FABB</Color> - <Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color> - <Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color> - <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color> - <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color> - <Color x:Key="ThemeForegroundColor">#FF000000</Color> - <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color> - <Color x:Key="AppListBackgroundColor">#b3ffffff</Color> - <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color> - <Color x:Key="SecondaryTextColor">#A0000000</Color> - </Styles.Resources> -</Styles> \ No newline at end of file diff --git a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml index 681b4feaf..146e60b5e 100644 --- a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -3,17 +3,20 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"> <Design.PreviewWith> - <Border Height="2000" Padding="20"> + <Border Height="2000" + Padding="20"> <StackPanel Spacing="5"> <TextBlock Text="Code Font Family" /> <Grid RowDefinitions="*,Auto"> - <Menu Grid.Row="1" Width="100"> + <Menu Grid.Row="1" + Width="100"> <MenuItem Header="File"> <MenuItem Header="Test 1" /> <MenuItem Header="Test 2" /> <MenuItem Header="Test 3"> <MenuItem.Icon> - <CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" /> + <CheckBox Margin="0" + IsChecked="{ReflectionBinding Checkbox, Mode=TwoWay}" /> </MenuItem.Icon> </MenuItem> </MenuItem> @@ -42,57 +45,80 @@ </Border> </Design.PreviewWith> <Style Selector="Border.small"> - <Setter Property="Width" Value="100" /> + <Setter Property="Width" + Value="100" /> </Style> <Style Selector="Border.normal"> - <Setter Property="Width" Value="130" /> + <Setter Property="Width" + Value="130" /> </Style> <Style Selector="Border.large"> - <Setter Property="Width" Value="160" /> + <Setter Property="Width" + Value="160" /> </Style> <Style Selector="Border.huge"> - <Setter Property="Width" Value="200" /> + <Setter Property="Width" + Value="200" /> </Style> <Style Selector="Border.settings"> - <Setter Property="Background" Value="{DynamicResource ThemeDarkColor}" /> - <Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderColor}" /> - <Setter Property="BorderThickness" Value="1" /> - <Setter Property="CornerRadius" Value="5" /> + <Setter Property="Background" + Value="{DynamicResource ThemeDarkColor}" /> + <Setter Property="BorderBrush" + Value="{DynamicResource MenuFlyoutPresenterBorderColor}" /> + <Setter Property="BorderThickness" + Value="1" /> + <Setter Property="CornerRadius" + Value="5" /> </Style> <Style Selector="Image.small"> - <Setter Property="Width" Value="50" /> + <Setter Property="Width" + Value="50" /> </Style> <Style Selector="Image.normal"> - <Setter Property="Width" Value="80" /> + <Setter Property="Width" + Value="80" /> </Style> <Style Selector="Image.large"> - <Setter Property="Width" Value="100" /> + <Setter Property="Width" + Value="100" /> </Style> <Style Selector="Image.huge"> - <Setter Property="Width" Value="120" /> + <Setter Property="Width" + Value="120" /> </Style> <Style Selector="#TitleBarHost > Image"> - <Setter Property="Margin" Value="10" /> + <Setter Property="Margin" + Value="10" /> </Style> <Style Selector="#TitleBarHost > Label"> - <Setter Property="Margin" Value="5" /> - <Setter Property="FontSize" Value="14" /> + <Setter Property="Margin" + Value="5" /> + <Setter Property="FontSize" + Value="14" /> </Style> <Style Selector="Button.SystemCaption"> - <Setter Property="MinWidth" Value="10" /> + <Setter Property="MinWidth" + Value="10" /> </Style> <Style Selector="DataGridColumnHeader"> - <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" /> - <Setter Property="HorizontalContentAlignment" Value="Center" /> - <Setter Property="BorderThickness" Value="1" /> - <Setter Property="VerticalContentAlignment" Value="Center" /> - <Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" /> - <Setter Property="SeparatorBrush" Value="{DynamicResource ThemeControlBorderColor}" /> - <Setter Property="Padding" Value="5" /> - <Setter Property="Background" Value="{DynamicResource ThemeContentBackgroundColor}" /> + <Setter Property="Foreground" + Value="{DynamicResource ThemeForegroundBrush}" /> + <Setter Property="HorizontalContentAlignment" + Value="Center" /> + <Setter Property="BorderThickness" + Value="1" /> + <Setter Property="VerticalContentAlignment" + Value="Center" /> + <Setter Property="SeparatorBrush" + Value="{DynamicResource ThemeControlBorderColor}" /> + <Setter Property="Padding" + Value="5" /> + <Setter Property="Background" + Value="{DynamicResource ThemeContentBackgroundColor}" /> <Setter Property="Template"> <ControlTemplate> - <Grid Background="{TemplateBinding Background}" ColumnDefinitions="*,Auto"> + <Grid Background="{TemplateBinding Background}" + ColumnDefinitions="*,Auto"> <Grid Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" @@ -122,193 +148,262 @@ </Setter> </Style> <Style Selector="DataGrid"> - <Setter Property="RowBackground" Value="{DynamicResource ThemeAccentBrush4}" /> - <Setter Property="AlternatingRowBackground" Value="#00FFFFFF" /> - <Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" /> - <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" /> - <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" /> + <Setter Property="RowBackground" + Value="{DynamicResource ThemeAccentBrush4}" /> + <Setter Property="Background" + Value="{DynamicResource ThemeBackgroundBrush}" /> + <Setter Property="BorderBrush" + Value="{DynamicResource ThemeBorderLowColor}" /> + <Setter Property="BorderThickness" + Value="{DynamicResource ThemeBorderThickness}" /> </Style> <Style Selector="DataGridRow:selected:focus /template/ Rectangle#BackgroundRectangle"> - <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" /> - <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" /> + <Setter Property="Fill" + Value="{DynamicResource SystemAccentColor}" /> + <Setter Property="Opacity" + Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" /> </Style> <Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle"> - <Setter Property="Fill" Value="{DynamicResource SystemListLowColor}" /> + <Setter Property="Fill" + Value="{DynamicResource SystemListLowColor}" /> </Style> <Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle"> - <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" /> - <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" /> + <Setter Property="Fill" + Value="{DynamicResource SystemAccentColor}" /> + <Setter Property="Opacity" + Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" /> </Style> <Style Selector="DataGridRow:selected:pointerover /template/ Rectangle#BackgroundRectangle"> - <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" /> - <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" /> + <Setter Property="Fill" + Value="{DynamicResource SystemAccentColor}" /> + <Setter Property="Opacity" + Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" /> </Style> <Style Selector="DataGridRow:selected:pointerover:focus /template/ Rectangle#BackgroundRectangle"> - <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" /> - <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" /> + <Setter Property="Fill" + Value="{DynamicResource SystemAccentColor}" /> + <Setter Property="Opacity" + Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" /> </Style> <Style Selector="DataGridCell"> - <Setter Property="HorizontalAlignment" Value="Center" /> - <Setter Property="HorizontalContentAlignment" Value="Center" /> + <Setter Property="HorizontalAlignment" + Value="Center" /> + <Setter Property="HorizontalContentAlignment" + Value="Center" /> </Style> <Style Selector="DataGridCell.Left"> - <Setter Property="HorizontalAlignment" Value="Left" /> + <Setter Property="HorizontalAlignment" + Value="Left" /> </Style> <Style Selector="CheckBox"> - <Setter Property="BorderThickness" Value="1" /> + <Setter Property="BorderThickness" + Value="1" /> </Style> <Style Selector="MenuItem"> - <Setter Property="Height" Value="{DynamicResource MenuItemHeight}" /> - <Setter Property="Padding" Value="{DynamicResource MenuItemPadding}" /> - <Setter Property="FontSize" Value="12" /> + <Setter Property="Height" + Value="{DynamicResource MenuItemHeight}" /> + <Setter Property="Padding" + Value="{DynamicResource MenuItemPadding}" /> + <Setter Property="FontSize" + Value="12" /> </Style> <Style Selector="MenuItem:selected /template/ Border#root"> - <Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" /> - <Setter Property="BorderBrush" Value="{DynamicResource ThemeControlBorderColor}" /> + <Setter Property="Background" + Value="{DynamicResource ThemeControlBorderColor}" /> + <Setter Property="BorderBrush" + Value="{DynamicResource ThemeControlBorderColor}" /> </Style> <Style Selector="TabItem > ScrollViewer"> - <Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" /> - <Setter Property="Margin" Value="0,-5,0,0" /> + <Setter Property="Background" + Value="{DynamicResource ThemeBackgroundColor}" /> + <Setter Property="Margin" + Value="0,-5,0,0" /> </Style> <Style Selector="TabItem > ScrollViewer > Border"> - <Setter Property="BorderThickness" Value="0,1,0,0" /> - <Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" /> - <Setter Property="BorderBrush" Value="{DynamicResource HighlightBrush}" /> + <Setter Property="BorderThickness" + Value="0,1,0,0" /> + <Setter Property="Background" + Value="{DynamicResource ThemeBackgroundColor}" /> + <Setter Property="BorderBrush" + Value="{DynamicResource HighlightBrush}" /> </Style> <Style Selector="Button"> - <Setter Property="MinWidth" Value="80" /> + <Setter Property="MinWidth" + Value="80" /> </Style> <Style Selector="ProgressBar /template/ Border#ProgressBarTrack"> - <Setter Property="IsVisible" Value="False" /> + <Setter Property="IsVisible" + Value="False" /> </Style> <Style Selector="ToggleButton"> - <Setter Property="Padding" Value="0,-5,0,0" /> + <Setter Property="Padding" + Value="0,-5,0,0" /> </Style> <Style Selector="TabItem"> - <Setter Property="FontSize" Value="14" /> - <Setter Property="BorderThickness" Value="0,0,1,0" /> - <Setter Property="BorderBrush" Value="{DynamicResource ThemeButtonForegroundColor}" /> - <Setter Property="Background" Value="{DynamicResource HighlightColor}" /> + <Setter Property="FontSize" + Value="14" /> + <Setter Property="BorderThickness" + Value="0,0,1,0" /> + <Setter Property="BorderBrush" + Value="{DynamicResource ThemeButtonForegroundColor}" /> + <Setter Property="Background" + Value="{DynamicResource SystemAccentColorLight2}" /> </Style> <Style Selector="TabItem:pointerover"> - <Setter Property="Foreground" Value="{DynamicResource ThemeButtonForegroundColor}" /> + <Setter Property="Foreground" + Value="{DynamicResource ThemeButtonForegroundColor}" /> </Style> <Style Selector="TabItem:selected"> - <Setter Property="Background" Value="{DynamicResource HighlightColor}" /> - <Setter Property="Foreground" Value="{DynamicResource ThemeBackgroundColor}" /> + <Setter Property="Background" + Value="{DynamicResource SystemAccentColorLight2}" /> + <Setter Property="Foreground" + Value="{DynamicResource ThemeBackgroundColor}" /> </Style> <Style Selector="TextBlock"> - <Setter Property="Margin" Value="{DynamicResource TextMargin}" /> - <Setter Property="FontSize" Value="{DynamicResource FontSize}" /> - <Setter Property="VerticalAlignment" Value="Center" /> - <Setter Property="TextWrapping" Value="WrapWithOverflow" /> + <Setter Property="Margin" + Value="{DynamicResource TextMargin}" /> + <Setter Property="FontSize" + Value="{DynamicResource FontSize}" /> + <Setter Property="VerticalAlignment" + Value="Center" /> + <Setter Property="TextWrapping" + Value="WrapWithOverflow" /> </Style> <Style Selector="TextBlock.h1"> - <Setter Property="Margin" Value="{DynamicResource TextMargin}" /> - <Setter Property="VerticalAlignment" Value="Center" /> - <Setter Property="FontWeight" Value="Bold" /> - <Setter Property="FontSize" Value="16" /> - <Setter Property="TextWrapping" Value="WrapWithOverflow" /> + <Setter Property="Margin" + Value="{DynamicResource TextMargin}" /> + <Setter Property="VerticalAlignment" + Value="Center" /> + <Setter Property="FontWeight" + Value="Bold" /> + <Setter Property="FontSize" + Value="16" /> + <Setter Property="TextWrapping" + Value="WrapWithOverflow" /> </Style> <Style Selector="Separator"> - <Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" /> - <Setter Property="Foreground" Value="{DynamicResource ThemeControlBorderColor}" /> - <Setter Property="MinHeight" Value="1" /> + <Setter Property="Background" + Value="{DynamicResource ThemeControlBorderColor}" /> + <Setter Property="Foreground" + Value="{DynamicResource ThemeControlBorderColor}" /> + <Setter Property="MinHeight" + Value="1" /> </Style> <Style Selector=":is(Button).DateTimeFlyoutButtonStyle"> - <Setter Property="Background" Value="{DynamicResource HighlightColor}" /> - <Setter Property="Foreground" Value="{DynamicResource ThemeBackgroundColor}" /> + <Setter Property="Background" + Value="{DynamicResource SystemAccentColorLight2}" /> + <Setter Property="Foreground" + Value="{DynamicResource ThemeBackgroundColor}" /> </Style> <Style Selector="DatePickerPresenter"> - <Setter Property="BorderThickness" Value="1" /> - <Setter Property="BorderBrush" Value="{DynamicResource ThemeButtonForegroundColor}" /> + <Setter Property="BorderThickness" + Value="1" /> + <Setter Property="BorderBrush" + Value="{DynamicResource ThemeButtonForegroundColor}" /> </Style> <Style Selector="DataGridCell"> - <Setter Property="FontSize" Value="14" /> + <Setter Property="FontSize" + Value="14" /> </Style> <Style Selector="CheckBox TextBlock"> - <Setter Property="Margin" Value="0,5,0,0" /> + <Setter Property="Margin" + Value="0,5,0,0" /> </Style> <Style Selector="ContextMenu"> - <Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" /> - <Setter Property="BorderThickness" Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" /> + <Setter Property="BorderBrush" + Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" /> + <Setter Property="BorderThickness" + Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" /> </Style> <Style Selector="TextBox"> - <Setter Property="VerticalContentAlignment" Value="Center" /> + <Setter Property="VerticalContentAlignment" + Value="Center" /> </Style> <Style Selector="TextBox.NumberBoxTextBoxStyle"> - <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" /> + <Setter Property="Foreground" + Value="{DynamicResource ThemeForegroundColor}" /> </Style> <Style Selector="ListBox ListBoxItem"> - <Setter Property="Padding" Value="0" /> - <Setter Property="Margin" Value="0" /> - <Setter Property="CornerRadius" Value="5" /> - <Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" /> - <Setter Property="BorderThickness" Value="2"/> + <Setter Property="Padding" + Value="0" /> + <Setter Property="Margin" + Value="0" /> + <Setter Property="CornerRadius" + Value="5" /> + <Setter Property="Background" + Value="{DynamicResource AppListBackgroundColor}" /> + <Setter Property="BorderThickness" + Value="2"/> <Style.Animations> <Animation Duration="0:0:0.7"> <KeyFrame Cue="0%"> - <Setter Property="MaxHeight" Value="0" /> - <Setter Property="Opacity" Value="0.0" /> + <Setter Property="MaxHeight" + Value="0" /> + <Setter Property="Opacity" + Value="0.0" /> </KeyFrame> <KeyFrame Cue="50%"> - <Setter Property="MaxHeight" Value="1000" /> - <Setter Property="Opacity" Value="0.3" /> + <Setter Property="MaxHeight" + Value="1000" /> + <Setter Property="Opacity" + Value="0.3" /> </KeyFrame> <KeyFrame Cue="100%"> - <Setter Property="MaxHeight" Value="1000" /> - <Setter Property="Opacity" Value="1.0" /> + <Setter Property="MaxHeight" + Value="1000" /> + <Setter Property="Opacity" + Value="1.0" /> </KeyFrame> </Animation> </Style.Animations> </Style> <Style Selector="ListBox ListBoxItem:selected /template/ ContentPresenter"> - <Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" /> + <Setter Property="Background" + Value="{DynamicResource AppListBackgroundColor}" /> + </Style> + <Style Selector="ListBox"> + <Setter Property="Background" + Value="{DynamicResource ThemeContentBackgroundColor}" /> + </Style> + <Style Selector="FlyoutPresenter, ContextMenu, MenuFlyoutPresenter"> + <Setter Property="BorderBrush" + Value="{DynamicResource MenuFlyoutPresenterBorderColor}" /> </Style> <Style Selector="ListBox ListBoxItem:pointerover /template/ ContentPresenter"> - <Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" /> + <Setter Property="Background" + Value="{DynamicResource AppListHoverBackgroundColor}" /> </Style> <Styles.Resources> - <SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" /> - <StaticResource x:Key="ListViewItemBackgroundSelected" ResourceKey="ThemeAccentColorBrush" /> - <StaticResource x:Key="ListViewItemBackgroundPressed" ResourceKey="SystemAccentColorDark1" /> - <StaticResource x:Key="ListViewItemBackgroundPointerOver" ResourceKey="SystemAccentColorDark2" /> - <StaticResource x:Key="ListViewItemBackgroundSelectedPressed" ResourceKey="ThemeAccentColorBrush" /> - <StaticResource x:Key="ListViewItemBackgroundSelectedPointerOver" ResourceKey="SystemAccentColorDark2" /> + <SolidColorBrush x:Key="ThemeAccentColorBrush" + Color="{DynamicResource SystemAccentColor}" /> + <StaticResource x:Key="ListViewItemBackgroundSelected" + ResourceKey="ThemeAccentColorBrush" /> + <StaticResource x:Key="ListViewItemBackgroundPressed" + ResourceKey="SystemAccentColorDark1" /> + <StaticResource x:Key="ListViewItemBackgroundPointerOver" + ResourceKey="SystemAccentColorDark2" /> + <StaticResource x:Key="ListViewItemBackgroundSelectedPressed" + ResourceKey="ThemeAccentColorBrush" /> + <StaticResource x:Key="ListViewItemBackgroundSelectedPointerOver" + ResourceKey="SystemAccentColorDark2" /> <SolidColorBrush x:Key="DataGridGridLinesBrush" Opacity="0.4" Color="{DynamicResource SystemBaseMediumLowColor}" /> - <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" /> - <SolidColorBrush x:Key="MenuFlyoutPresenterBorderBrush" Color="{DynamicResource MenuFlyoutPresenterBorderColor}" /> - <SolidColorBrush x:Key="FlyoutBorderThemeBrush" Color="{DynamicResource MenuFlyoutPresenterBorderColor}" /> - <SolidColorBrush x:Key="ListBoxBackground" Color="{DynamicResource ThemeContentBackgroundColor}" /> - <SolidColorBrush x:Key="ThemeForegroundBrush" Color="{DynamicResource ThemeForegroundColor}" /> - <SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" /> - <SolidColorBrush x:Key="SplitButtonBackgroundChecked" Color="#00E81123" /> - <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver" Color="#00E81123" /> - <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" Color="#00E81123" /> - <SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled" Color="#00E81123" /> + <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" + Color="{DynamicResource DataGridSelectionColor}" /> + <SolidColorBrush x:Key="SplitButtonBackgroundChecked" + Color="#00E81123" /> + <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver" + Color="#00E81123" /> + <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" + Color="#00E81123" /> + <SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled" + Color="#00E81123" /> <Thickness x:Key="PageMargin">40 0 40 0</Thickness> <Thickness x:Key="Margin">0 5 0 5</Thickness> <Thickness x:Key="MenuItemPadding">5 0 5 0</Thickness> - <Color x:Key="MenuFlyoutPresenterBorderColor">#00000000</Color> - <Color x:Key="SystemAccentColor">#FF00C3E3</Color> - <Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color> - <Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color> - <Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color> - <Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color> - <Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color> - <Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color> - <Color x:Key="DataGridSelectionColor">#FF00FABB</Color> - <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color> - <Color x:Key="ThemeControlBorderColor">#FF505050</Color> - <Color x:Key="VsyncEnabled">#FF2EEAC9</Color> - <Color x:Key="VsyncDisabled">#FFFF4554</Color> - <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color> - <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color> - <Color x:Key="SecondaryTextColor">#A0FFFFFF</Color> <x:Double x:Key="ScrollBarThickness">15</x:Double> <x:Double x:Key="FontSizeSmall">8</x:Double> <x:Double x:Key="FontSizeNormal">10</x:Double> diff --git a/src/Ryujinx.Ava/Assets/Styles/Themes.xaml b/src/Ryujinx.Ava/Assets/Styles/Themes.xaml new file mode 100644 index 000000000..0f323f84b --- /dev/null +++ b/src/Ryujinx.Ava/Assets/Styles/Themes.xaml @@ -0,0 +1,85 @@ +<ResourceDictionary xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <ResourceDictionary.ThemeDictionaries> + <ResourceDictionary x:Key="Default"> + <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" + Color="{DynamicResource DataGridSelectionColor}" /> + <SolidColorBrush x:Key="ThemeAccentColorBrush" + Color="{DynamicResource SystemAccentColor}" /> + <SolidColorBrush x:Key="ThemeAccentBrush4" + Color="{DynamicResource ThemeAccentColor4}" /> + <Color x:Key="SystemAccentColor">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color> + <Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color> + <Color x:Key="DataGridSelectionColor">#FF00FABB</Color> + <Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color> + <Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color> + <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color> + <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color> + <Color x:Key="ThemeForegroundColor">#FF000000</Color> + <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color> + <Color x:Key="AppListBackgroundColor">#b3ffffff</Color> + <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color> + <Color x:Key="SecondaryTextColor">#A0000000</Color> + <Color x:Key="VsyncEnabled">#FF2EEAC9</Color> + <Color x:Key="VsyncDisabled">#FFFF4554</Color> + </ResourceDictionary> + <ResourceDictionary x:Key="Light"> + <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" + Color="{DynamicResource DataGridSelectionColor}" /> + <SolidColorBrush x:Key="ThemeAccentColorBrush" + Color="{DynamicResource SystemAccentColor}" /> + <SolidColorBrush x:Key="ThemeAccentBrush4" + Color="{DynamicResource ThemeAccentColor4}" /> + <Color x:Key="SystemAccentColor">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color> + <Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color> + <Color x:Key="DataGridSelectionColor">#FF00FABB</Color> + <Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color> + <Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color> + <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color> + <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color> + <Color x:Key="ThemeForegroundColor">#FF000000</Color> + <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color> + <Color x:Key="AppListBackgroundColor">#b3ffffff</Color> + <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color> + <Color x:Key="SecondaryTextColor">#A0000000</Color> + </ResourceDictionary> + <ResourceDictionary x:Key="Dark"> + <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" + Color="{DynamicResource DataGridSelectionColor}" /> + <SolidColorBrush x:Key="ThemeAccentColorBrush" + Color="{DynamicResource SystemAccentColor}" /> + <SolidColorBrush x:Key="ThemeAccentBrush4" + Color="{DynamicResource ThemeAccentColor4}" /> + <Color x:Key="ControlFillColorSecondary">#008AA8</Color> + <Color x:Key="SystemAccentColor">#FF00C3E3</Color> + <Color x:Key="SystemAccentColorDark1">#FF99b000</Color> + <Color x:Key="SystemAccentColorDark2">#FF006d7d</Color> + <Color x:Key="SystemAccentColorDark3">#FF00525E</Color> + <Color x:Key="SystemAccentColorLight1">#FF00dbff</Color> + <Color x:Key="SystemAccentColorLight2">#FF19dfff</Color> + <Color x:Key="SystemAccentColorLight3">#FF33e3ff</Color> + <Color x:Key="DataGridSelectionColor">#FF00FABB</Color> + <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color> + <Color x:Key="ThemeControlBorderColor">#FF505050</Color> + <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color> + <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color> + <Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color> + <Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color> + <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color> + <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color> + <Color x:Key="SecondaryTextColor">#A0FFFFFF</Color> + </ResourceDictionary> + </ResourceDictionary.ThemeDictionaries> +</ResourceDictionary> diff --git a/src/Ryujinx.Ava/Common/ApplicationHelper.cs b/src/Ryujinx.Ava/Common/ApplicationHelper.cs index d28d5aac1..9e4757538 100644 --- a/src/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/src/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -1,5 +1,5 @@ -using Avalonia.Controls; using Avalonia.Controls.Notifications; +using Avalonia.Platform.Storage; using Avalonia.Threading; using LibHac; using LibHac.Account; @@ -143,14 +143,20 @@ namespace Ryujinx.Ava.Common } } - public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0) + public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0) { - OpenFolderDialog folderDialog = new() + var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle], - }; + AllowMultiple = false + }); - string destination = await folderDialog.ShowAsync(_owner); + if (result.Count == 0) + { + return; + } + + var destination = result[0].Path.LocalPath; var cancellationToken = new CancellationTokenSource(); UpdateWaitWindow waitingDialog = new( @@ -158,148 +164,145 @@ namespace Ryujinx.Ava.Common LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)), cancellationToken); - if (!string.IsNullOrWhiteSpace(destination)) + Thread extractorThread = new(() => { - Thread extractorThread = new(() => + Dispatcher.UIThread.Post(waitingDialog.Show); + + using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); + + Nca mainNca = null; + Nca patchNca = null; + + string extension = Path.GetExtension(titleFilePath).ToLower(); + if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") { - Dispatcher.UIThread.Post(waitingDialog.Show); + PartitionFileSystem pfs; - using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); - - Nca mainNca = null; - Nca patchNca = null; - - string extension = Path.GetExtension(titleFilePath).ToLower(); - if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") + if (extension == ".xci") { - PartitionFileSystem pfs; + pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure); + } + else + { + pfs = new PartitionFileSystem(file.AsStorage()); + } - if (extension == ".xci") + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef<IFile>(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); + if (nca.Header.ContentType == NcaContentType.Program) { - pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure); - } - else - { - pfs = new PartitionFileSystem(file.AsStorage()); - } - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); - if (nca.Header.ContentType == NcaContentType.Program) + int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); + if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } + patchNca = nca; + } + else + { + mainNca = nca; } } } - else if (extension == ".nca") - { - mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); - } - - if (mainNca == null) - { - Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file"); - - Dispatcher.UIThread.InvokeAsync(async () => - { - waitingDialog.Close(); - - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); - }); - - return; - } - - (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); - if (updatePatchNca != null) - { - patchNca = updatePatchNca; - } - - int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); - - try - { - bool sectionExistsInPatch = false; - if (patchNca != null) - { - sectionExistsInPatch = patchNca.CanOpenSection(index); - } - - IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) - : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); - - FileSystemClient fsClient = _horizonClient.Fs; - - string source = DateTime.Now.ToFileTime().ToString()[10..]; - string output = DateTime.Now.ToFileTime().ToString()[10..]; - - using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem); - using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination)); - - fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); - fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); - - (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token); - - if (!canceled) - { - if (resultCode.Value.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); - - Dispatcher.UIThread.InvokeAsync(async () => - { - waitingDialog.Close(); - - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); - }); - } - else if (resultCode.Value.IsSuccess()) - { - Dispatcher.UIThread.Post(waitingDialog.Close); - - NotificationHelper.Show( - LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle], - $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}", - NotificationType.Information); - } - } - - fsClient.Unmount(source.ToU8Span()); - fsClient.Unmount(output.ToU8Span()); - } - catch (ArgumentException ex) - { - Logger.Error?.Print(LogClass.Application, $"{ex.Message}"); - - Dispatcher.UIThread.InvokeAsync(async () => - { - waitingDialog.Close(); - - await ContentDialogHelper.CreateErrorDialog(ex.Message); - }); - } - }) + } + else if (extension == ".nca") { - Name = "GUI.NcaSectionExtractorThread", - IsBackground = true, - }; - extractorThread.Start(); - } + mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); + } + + if (mainNca == null) + { + Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file"); + + Dispatcher.UIThread.InvokeAsync(async () => + { + waitingDialog.Close(); + + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); + }); + + return; + } + + (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); + if (updatePatchNca != null) + { + patchNca = updatePatchNca; + } + + int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); + + try + { + bool sectionExistsInPatch = false; + if (patchNca != null) + { + sectionExistsInPatch = patchNca.CanOpenSection(index); + } + + IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) + : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); + + FileSystemClient fsClient = _horizonClient.Fs; + + string source = DateTime.Now.ToFileTime().ToString()[10..]; + string output = DateTime.Now.ToFileTime().ToString()[10..]; + + using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem); + using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination)); + + fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); + fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); + + (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token); + + if (!canceled) + { + if (resultCode.Value.IsFailure()) + { + Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); + + Dispatcher.UIThread.InvokeAsync(async () => + { + waitingDialog.Close(); + + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); + }); + } + else if (resultCode.Value.IsSuccess()) + { + Dispatcher.UIThread.Post(waitingDialog.Close); + + NotificationHelper.Show( + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle], + $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}", + NotificationType.Information); + } + } + + fsClient.Unmount(source.ToU8Span()); + fsClient.Unmount(output.ToU8Span()); + } + catch (ArgumentException ex) + { + Logger.Error?.Print(LogClass.Application, $"{ex.Message}"); + + Dispatcher.UIThread.InvokeAsync(async () => + { + waitingDialog.Close(); + + await ContentDialogHelper.CreateErrorDialog(ex.Message); + }); + } + }) + { + Name = "GUI.NcaSectionExtractorThread", + IsBackground = true, + }; + extractorThread.Start(); } public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token) diff --git a/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs index f12d0214b..856ed6f7e 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs @@ -31,7 +31,6 @@ namespace Ryujinx.Ava.Input _control.KeyDown += OnKeyPress; _control.KeyUp += OnKeyRelease; _control.TextInput += Control_TextInput; - _control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble); } private void Control_TextInput(object sender, TextInputEventArgs e) @@ -39,12 +38,6 @@ namespace Ryujinx.Ava.Input TextInput?.Invoke(this, e.Text); } - private void Control_LastChanceTextInput(object sender, TextInputEventArgs e) - { - // Swallow event - e.Handled = true; - } - public event Action<string> OnGamepadConnected { add { } diff --git a/src/Ryujinx.Ava/Program.cs b/src/Ryujinx.Ava/Program.cs index 5241f91f8..86f38c347 100644 --- a/src/Ryujinx.Ava/Program.cs +++ b/src/Ryujinx.Ava/Program.cs @@ -59,15 +59,12 @@ namespace Ryujinx.Ava { EnableMultiTouch = true, EnableIme = true, - UseEGL = false, - UseGpu = true, + RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software }, }) .With(new Win32PlatformOptions { - EnableMultitouch = true, - UseWgl = false, - AllowEglInitialization = false, - CompositionBackdropCornerRadius = 8.0f, + WinUICompositionBackdropCornerRadius = 8.0f, + RenderingMode = new[] { Win32RenderingMode.AngleEgl, Win32RenderingMode.Software }, }) .UseSkia(); } diff --git a/src/Ryujinx.Ava/Ryujinx.Ava.csproj b/src/Ryujinx.Ava/Ryujinx.Ava.csproj index 1fac5400e..a4c1ebf16 100644 --- a/src/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/src/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -10,6 +10,8 @@ <RootNamespace>Ryujinx.Ava</RootNamespace> <ApplicationIcon>Ryujinx.ico</ApplicationIcon> <TieredPGO>true</TieredPGO> + <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> + <ApplicationManifest>app.manifest</ApplicationManifest> </PropertyGroup> <Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))"> @@ -26,7 +28,7 @@ <ItemGroup> <PackageReference Include="Avalonia" /> <PackageReference Include="Avalonia.Desktop" /> - <PackageReference Include="Avalonia.Diagnostics" /> + <PackageReference Include="Avalonia.Diagnostics" Condition="'$(Configuration)'=='Debug'" /> <PackageReference Include="Avalonia.Controls.DataGrid" /> <PackageReference Include="Avalonia.Markup.Xaml.Loader" /> <PackageReference Include="Avalonia.Svg" /> @@ -34,7 +36,6 @@ <PackageReference Include="jp2masa.Avalonia.Flexbox" /> <PackageReference Include="DynamicData" /> <PackageReference Include="FluentAvaloniaUI" /> - <PackageReference Include="XamlNameReferenceGenerator" /> <PackageReference Include="OpenTK.Core" /> <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" /> @@ -97,10 +98,7 @@ <SubType>Designer</SubType> </AvaloniaResource> <AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" /> - <AvaloniaResource Include="Assets\Styles\BaseLight.xaml"> - <Generator>MSBuild:Compile</Generator> - </AvaloniaResource> - <AvaloniaResource Include="Assets\Styles\BaseDark.xaml"> + <AvaloniaResource Include="Assets\Styles\Themes.xaml"> <Generator>MSBuild:Compile</Generator> </AvaloniaResource> <AvaloniaResource Include="Assets\Styles\Styles.xaml" /> @@ -123,8 +121,7 @@ <None Remove="Assets\Locales\zh_CN.json" /> <None Remove="Assets\Locales\zh_TW.json" /> <None Remove="Assets\Styles\Styles.xaml" /> - <None Remove="Assets\Styles\BaseDark.xaml" /> - <None Remove="Assets\Styles\BaseLight.xaml" /> + <None Remove="Assets\Styles\Themes.xaml" /> </ItemGroup> <ItemGroup> diff --git a/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml index 211b47254..a70fc8d44 100644 --- a/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml +++ b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml @@ -38,7 +38,7 @@ Grid.Column="1" Margin="10" VerticalAlignment="Stretch" - Text="{Binding Message}" + Text="{ReflectionBinding Message}" TextWrapping="Wrap" /> <StackPanel Name="ButtonStack" diff --git a/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs index b77cc4020..ec6f76825 100644 --- a/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs @@ -4,9 +4,6 @@ using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Windows; using System.Threading.Tasks; -#if DEBUG -using Avalonia; -#endif namespace Ryujinx.Ava.UI.Applet { @@ -21,9 +18,7 @@ namespace Ryujinx.Ava.UI.Applet Message = message; DataContext = this; InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif + int responseId = 0; if (buttons != null) @@ -44,9 +39,6 @@ namespace Ryujinx.Ava.UI.Applet { DataContext = this; InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif } public string Message { get; set; } diff --git a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml index 655045690..64b23f987 100644 --- a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml +++ b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml @@ -34,13 +34,13 @@ Grid.Row="1" Grid.Column="1" Margin="5" - Text="{Binding MainText}" + Text="{ReflectionBinding MainText}" TextWrapping="Wrap" /> <TextBlock Grid.Row="2" Grid.Column="1" Margin="5" - Text="{Binding SecondaryText}" + Text="{ReflectionBinding SecondaryText}" TextWrapping="Wrap" /> <TextBox Name="Input" @@ -50,7 +50,7 @@ VerticalAlignment="Center" Focusable="True" KeyUp="Message_KeyUp" - Text="{Binding Message}" + Text="{ReflectionBinding Message}" TextInput="Message_TextInput" TextWrapping="Wrap" UseFloatingWatermark="True" /> diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml index 29547f5fe..93638fc53 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml @@ -2,7 +2,9 @@ x:Class="Ryujinx.Ava.UI.Controls.ApplicationContextMenu" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"> + xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + x:DataType="viewModels:MainWindowViewModel"> <MenuItem Click="RunApplication_Click" Header="{locale:Locale GameListContextMenuRunApplication}" /> diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index 77b4f5207..f54e9df8f 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -299,7 +299,11 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + await ApplicationHelper.ExtractSection( + viewModel.StorageProvider, + NcaSectionType.Code, + viewModel.SelectedApplication.Path, + viewModel.SelectedApplication.TitleName); } } @@ -309,7 +313,11 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Data, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + await ApplicationHelper.ExtractSection( + viewModel.StorageProvider, + NcaSectionType.Data, + viewModel.SelectedApplication.Path, + viewModel.SelectedApplication.TitleName); } } @@ -319,7 +327,11 @@ namespace Ryujinx.Ava.UI.Controls if (viewModel?.SelectedApplication != null) { - await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + await ApplicationHelper.ExtractSection( + viewModel.StorageProvider, + NcaSectionType.Logo, + viewModel.SelectedApplication.Path, + viewModel.SelectedApplication.TitleName); } } diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml index 3d55793f9..214fc0755 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml @@ -11,7 +11,9 @@ d:DesignHeight="450" d:DesignWidth="800" Focusable="True" - mc:Ignorable="d"> + mc:Ignorable="d" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + x:DataType="viewModels:MainWindowViewModel"> <UserControl.Resources> <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:ApplicationContextMenu x:Key="ApplicationContextMenu" /> @@ -27,7 +29,7 @@ VerticalAlignment="Stretch" ContextFlyout="{StaticResource ApplicationContextMenu}" DoubleTapped="GameList_DoubleTapped" - Items="{Binding AppsObservableList}" + ItemsSource="{Binding AppsObservableList}" SelectionChanged="GameList_SelectionChanged"> <ListBox.ItemsPanel> <ItemsPanelTemplate> @@ -43,8 +45,8 @@ <Setter Property="Margin" Value="5" /> <Setter Property="CornerRadius" Value="4" /> </Style> - <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator"> - <Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" /> + <Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator"> + <Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.GridItemSelectorSize}" /> </Style> </ListBox.Styles> <ListBox.ItemTemplate> @@ -54,10 +56,10 @@ Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}" - Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}" - Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}" - Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}" + Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}" + Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}" + Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}" + Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}" ClipToBounds="True" CornerRadius="4"> <Grid> @@ -76,9 +78,9 @@ Margin="0,10,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}"> + IsVisible="{ReflectionBinding $parent[UserControl].DataContext.ShowNames}"> <TextBlock - HorizontalAlignment="Stretch" + HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding TitleName}" TextAlignment="Center" diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs index efdc9ab02..821d6fd9f 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs @@ -1,7 +1,6 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ui.App.Common; @@ -25,12 +24,7 @@ namespace Ryujinx.Ava.UI.Controls InitializeComponent(); } - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - public void GameList_DoubleTapped(object sender, RoutedEventArgs args) + public void GameList_DoubleTapped(object sender, TappedEventArgs args) { if (sender is ListBox listBox) { diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml index 227b4723b..75bbf9d0d 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml @@ -10,7 +10,9 @@ d:DesignHeight="450" d:DesignWidth="800" Focusable="True" - mc:Ignorable="d"> + mc:Ignorable="d" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + x:DataType="viewModels:MainWindowViewModel"> <UserControl.Resources> <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:ApplicationContextMenu x:Key="ApplicationContextMenu" /> @@ -27,7 +29,7 @@ VerticalAlignment="Stretch" ContextFlyout="{StaticResource ApplicationContextMenu}" DoubleTapped="GameList_DoubleTapped" - Items="{Binding AppsObservableList}" + ItemsSource="{Binding AppsObservableList}" SelectionChanged="GameList_SelectionChanged"> <ListBox.ItemsPanel> <ItemsPanelTemplate> @@ -39,8 +41,8 @@ </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.Styles> - <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator"> - <Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.ListItemSelectorSize}" /> + <Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator"> + <Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.ListItemSelectorSize}" /> </Style> </ListBox.Styles> <ListBox.ItemTemplate> @@ -65,10 +67,10 @@ Grid.RowSpan="3" Grid.Column="0" Margin="0" - Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}" - Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}" - Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}" - Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}" + Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}" + Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}" + Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}" + Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}" Source="{Binding Icon, Converter={StaticResource ByteImage}}" /> <Border Grid.Column="2" diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs index 1646fc1b1..dd60503af 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs @@ -1,7 +1,6 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ui.App.Common; @@ -25,12 +24,7 @@ namespace Ryujinx.Ava.UI.Controls InitializeComponent(); } - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - public void GameList_DoubleTapped(object sender, RoutedEventArgs args) + public void GameList_DoubleTapped(object sender, TappedEventArgs args) { if (sender is ListBox listBox) { diff --git a/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs index 4e4b971eb..7da23648e 100644 --- a/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs @@ -1,6 +1,4 @@ -using Avalonia.Data; using Avalonia.Markup.Xaml; -using Avalonia.Markup.Xaml.MarkupExtensions; using FluentAvalonia.UI.Controls; using System; using System.Collections.Generic; @@ -38,13 +36,7 @@ namespace Ryujinx.Ava.UI.Helpers public override object ProvideValue(IServiceProvider serviceProvider) { - ReflectionBindingExtension binding = new($"[{_key}]") - { - Mode = BindingMode.OneWay, - Source = this, - }; - - return binding.ProvideValue(serviceProvider); + return this[_key]; } } } diff --git a/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs b/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs deleted file mode 100644 index 4ae903b49..000000000 --- a/src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Input; -using System; -using System.Windows.Input; - -namespace Ryujinx.Ava.UI.Helpers -{ - public class HotKeyControl : ContentControl, ICommandSource - { - public static readonly StyledProperty<object> CommandParameterProperty = - AvaloniaProperty.Register<HotKeyControl, object>(nameof(CommandParameter)); - - public static readonly DirectProperty<HotKeyControl, ICommand> CommandProperty = - AvaloniaProperty.RegisterDirect<HotKeyControl, ICommand>(nameof(Command), - control => control.Command, (control, command) => control.Command = command, enableDataValidation: true); - - public static readonly StyledProperty<KeyGesture> HotKeyProperty = HotKeyManager.HotKeyProperty.AddOwner<Button>(); - - private ICommand _command; - private bool _commandCanExecute; - - public ICommand Command - { - get { return _command; } - set { SetAndRaise(CommandProperty, ref _command, value); } - } - - public KeyGesture HotKey - { - get { return GetValue(HotKeyProperty); } - set { SetValue(HotKeyProperty, value); } - } - - public object CommandParameter - { - get { return GetValue(CommandParameterProperty); } - set { SetValue(CommandParameterProperty, value); } - } - - public void CanExecuteChanged(object sender, EventArgs e) - { - var canExecute = Command == null || Command.CanExecute(CommandParameter); - - if (canExecute != _commandCanExecute) - { - _commandCanExecute = canExecute; - UpdateIsEffectivelyEnabled(); - } - } - } -} diff --git a/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs b/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs index dc8e3f945..0b1789880 100644 --- a/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs @@ -42,21 +42,6 @@ namespace Ryujinx.Ava.UI.Helpers GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, null)); } - public void Log<T0>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0) - { - GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0 })); - } - - public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0, propertyValue1 })); - } - - public void Log<T0, T1, T2>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0, propertyValue1, propertyValue2 })); - } - public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues) { GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, propertyValues)); diff --git a/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs b/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs index 670d3b361..a055f3353 100644 --- a/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs +++ b/src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs @@ -31,7 +31,6 @@ namespace Ryujinx.Ava.UI.Helpers OnTextInput(new TextInputEventArgs { Text = text, - Device = KeyboardDevice.Instance, Source = this, RoutedEvent = TextInputEvent, }); diff --git a/src/Ryujinx.Ava/UI/Helpers/TimeZoneConverter.cs b/src/Ryujinx.Ava/UI/Helpers/TimeZoneConverter.cs new file mode 100644 index 000000000..4ec0ff306 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Helpers/TimeZoneConverter.cs @@ -0,0 +1,28 @@ +using Avalonia.Data.Converters; +using System; +using System.Globalization; +using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; + +namespace Ryujinx.Ava.UI.Helpers +{ + internal class TimeZoneConverter : IValueConverter + { + public static TimeZoneConverter Instance = new(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return null; + } + + var timeZone = (TimeZone)value; + return string.Format("{0} {1} {2}", timeZone.UtcDifference, timeZone.Location, timeZone.Abbreviation); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Ryujinx.Ava/UI/Models/UserProfile.cs b/src/Ryujinx.Ava/UI/Models/UserProfile.cs index e1698b4df..7a9237fe1 100644 --- a/src/Ryujinx.Ava/UI/Models/UserProfile.cs +++ b/src/Ryujinx.Ava/UI/Models/UserProfile.cs @@ -1,4 +1,3 @@ -using Avalonia; using Avalonia.Media; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.ViewModels; @@ -88,7 +87,8 @@ namespace Ryujinx.Ava.UI.Models private void UpdateBackground() { - Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color); + var currentApplication = Avalonia.Application.Current; + currentApplication.Styles.TryGetResource("ControlFillColorSecondary", currentApplication.ActualThemeVariant, out object color); if (color is not null) { diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs index e324b484d..fa55c8d3f 100644 --- a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs +++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Renderer private UpdateBoundsCallbackDelegate _updateBoundsCallback; public event EventHandler<IntPtr> WindowCreated; - public event EventHandler<Size> SizeChanged; + public event EventHandler<Size> BoundsChanged; public EmbeddedWindow() { @@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Renderer private void StateChanged(Rect rect) { - SizeChanged?.Invoke(this, rect.Size); + BoundsChanged?.Invoke(this, rect.Size); _updateBoundsCallback?.Invoke(rect); } @@ -149,9 +149,10 @@ namespace Ryujinx.Ava.UI.Renderer msg == WindowsMessages.Rbuttonup || msg == WindowsMessages.Mousemove) { - Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value; + Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), this).Value; Pointer pointer = new(0, PointerType.Mouse, true); +#pragma warning disable CS0618 // Type or member is obsolete (As of Avalonia 11, the constructors for PointerPressedEventArgs & PointerEventArgs are marked as obsolete) switch (msg) { case WindowsMessages.Lbuttondown: @@ -164,7 +165,7 @@ namespace Ryujinx.Ava.UI.Renderer var evnt = new PointerPressedEventArgs( this, pointer, - VisualRoot, + this, rootVisualPosition, (ulong)Environment.TickCount64, properties, @@ -184,7 +185,7 @@ namespace Ryujinx.Ava.UI.Renderer var evnt = new PointerReleasedEventArgs( this, pointer, - VisualRoot, + this, rootVisualPosition, (ulong)Environment.TickCount64, properties, @@ -201,7 +202,7 @@ namespace Ryujinx.Ava.UI.Renderer PointerMovedEvent, this, pointer, - VisualRoot, + this, rootVisualPosition, (ulong)Environment.TickCount64, new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other), @@ -212,6 +213,7 @@ namespace Ryujinx.Ava.UI.Renderer break; } } +#pragma warning restore CS0618 } } diff --git a/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs index b74265a31..12c18e4a7 100644 --- a/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs +++ b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Renderer public readonly EmbeddedWindow EmbeddedWindow; public event EventHandler<EventArgs> WindowCreated; - public event Action<object, Size> SizeChanged; + public event Action<object, Size> BoundsChanged; public RendererHost() { @@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Renderer private void Initialize() { EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated; - EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged; + EmbeddedWindow.BoundsChanged += CurrentWindow_BoundsChanged; Content = EmbeddedWindow; } @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Renderer if (EmbeddedWindow != null) { EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated; - EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged; + EmbeddedWindow.BoundsChanged -= CurrentWindow_BoundsChanged; } GC.SuppressFinalize(this); @@ -55,9 +55,9 @@ namespace Ryujinx.Ava.UI.Renderer Dispose(); } - private void CurrentWindow_SizeChanged(object sender, Size e) + private void CurrentWindow_BoundsChanged(object sender, Size e) { - SizeChanged?.Invoke(sender, e); + BoundsChanged?.Invoke(sender, e); } private void CurrentWindow_WindowCreated(object sender, IntPtr e) diff --git a/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs index fddcd71a7..70ede4c12 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs @@ -1,4 +1,3 @@ -using Avalonia; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Threading; @@ -88,21 +87,19 @@ namespace Ryujinx.Ava.UI.ViewModels { Version = Program.Version; - var assets = AvaloniaLocator.Current.GetService<IAssetLoader>(); - if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light") { - GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common"))); - DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common"))); - PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common"))); - TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common"))); + GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common"))); + DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common"))); + PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common"))); + TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common"))); } else { - GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common"))); - DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common"))); - PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common"))); - TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.Ui.Common"))); + GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common"))); + DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common"))); + PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common"))); + TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.Ui.Common"))); } Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson); diff --git a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs index f2f569472..74b737511 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -1,6 +1,6 @@ using Avalonia.Collections; -using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform.Storage; using Avalonia.Threading; using DynamicData; using LibHac.Common; @@ -90,12 +90,19 @@ namespace Ryujinx.Ava.UI.ViewModels get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); } + public IStorageProvider StorageProvider; + public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId) { _virtualFileSystem = virtualFileSystem; _titleId = titleId; + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + StorageProvider = desktop.MainWindow.StorageProvider; + } + _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); try @@ -195,29 +202,24 @@ namespace Ryujinx.Ava.UI.ViewModels public async void Add() { - OpenFileDialog dialog = new() + var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], AllowMultiple = true, - }; - - dialog.Filters.Add(new FileDialogFilter - { - Name = "NSP", - Extensions = { "nsp" }, - }); - - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - string[] files = await dialog.ShowAsync(desktop.MainWindow); - - if (files != null) + FileTypeFilter = new List<FilePickerFileType> { - foreach (string file in files) + new("NSP") { - await AddDownloadableContent(file); + Patterns = new[] { "*.nsp" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" }, + MimeTypes = new[] { "application/x-nx-nsp" } } } + }); + + foreach (var file in result) + { + await AddDownloadableContent(file.Path.LocalPath); } } diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index e8d68f769..aa6e0326f 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Media; +using Avalonia.Platform.Storage; using Avalonia.Threading; using DynamicData; using DynamicData.Binding; @@ -105,8 +106,6 @@ namespace Ryujinx.Ava.UI.ViewModels public ApplicationData ListSelectedApplication; public ApplicationData GridSelectedApplication; - public event Action ReloadGameList; - private string TitleName { get; set; } internal AppHost AppHost { get; set; } @@ -131,6 +130,7 @@ namespace Ryujinx.Ava.UI.ViewModels public void Initialize( ContentManager contentManager, + IStorageProvider storageProvider, ApplicationLibrary applicationLibrary, VirtualFileSystem virtualFileSystem, AccountManager accountManager, @@ -144,6 +144,7 @@ namespace Ryujinx.Ava.UI.ViewModels TopLevel topLevel) { ContentManager = contentManager; + StorageProvider = storageProvider; ApplicationLibrary = applicationLibrary; VirtualFileSystem = virtualFileSystem; AccountManager = accountManager; @@ -891,6 +892,7 @@ namespace Ryujinx.Ava.UI.ViewModels } public ContentManager ContentManager { get; private set; } + public IStorageProvider StorageProvider { get; private set; } public ApplicationLibrary ApplicationLibrary { get; private set; } public VirtualFileSystem VirtualFileSystem { get; private set; } public AccountManager AccountManager { get; private set; } @@ -1188,7 +1190,9 @@ namespace Ryujinx.Ava.UI.ViewModels { Application.Current.Styles.TryGetResource(args.VSyncEnabled ? "VsyncEnabled" - : "VsyncDisabled", out object color); + : "VsyncDisabled", + Avalonia.Application.Current.ActualThemeVariant, + out object color); if (color is not null) { @@ -1259,6 +1263,16 @@ namespace Ryujinx.Ava.UI.ViewModels ShowMenuAndStatusBar = false; } + public void ToggleStartGamesInFullscreen() + { + StartGamesInFullscreen = !StartGamesInFullscreen; + } + + public void ToggleShowConsole() + { + ShowConsole = !ShowConsole; + } + public void SetListMode() { Glyph = Glyph.List; @@ -1271,43 +1285,57 @@ namespace Ryujinx.Ava.UI.ViewModels public async void InstallFirmwareFromFile() { - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { - OpenFileDialog dialog = new() { AllowMultiple = false }; - dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); - - string[] file = await dialog.ShowAsync(desktop.MainWindow); - - if (file != null && file.Length > 0) + AllowMultiple = false, + FileTypeFilter = new List<FilePickerFileType> { - await HandleFirmwareInstallation(file[0]); + new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes]) + { + Patterns = new[] { "*.xci", "*.zip" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" }, + MimeTypes = new[] { "application/x-nx-xci", "application/zip" } + }, + new("XCI") + { + Patterns = new[] { "*.xci" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" }, + MimeTypes = new[] { "application/x-nx-xci" } + }, + new("ZIP") + { + Patterns = new[] { "*.zip" }, + AppleUniformTypeIdentifiers = new[] { "public.zip-archive" }, + MimeTypes = new[] { "application/zip" } + }, } + }); + + if (result.Count > 0) + { + await HandleFirmwareInstallation(result[0].Path.LocalPath); } } public async void InstallFirmwareFromFolder() { - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { - OpenFolderDialog dialog = new(); + AllowMultiple = false + }); - string folder = await dialog.ShowAsync(desktop.MainWindow); - - if (!string.IsNullOrEmpty(folder)) - { - await HandleFirmwareInstallation(folder); - } + if (result.Count > 0) + { + await HandleFirmwareInstallation(result[0].Path.LocalPath); } } - public static void OpenRyujinxFolder() + public void OpenRyujinxFolder() { OpenHelper.OpenFolder(AppDataManager.BaseDirPath); } - public static void OpenLogsFolder() + public void OpenLogsFolder() { string logPath = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "Logs"); @@ -1349,25 +1377,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void ToggleFileType(string fileType) - { - _ = fileType switch - { -#pragma warning disable IDE0055 // Disable formatting - "NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP, - "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0, - "XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI, - "NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA, - "NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO, - "NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO, - _ => throw new ArgumentOutOfRangeException(fileType), -#pragma warning restore IDE0055 - }; - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - LoadApplications(); - } - public async void ManageProfiles() { await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient); @@ -1378,78 +1387,84 @@ namespace Ryujinx.Ava.UI.ViewModels AppHost.Device.System.SimulateWakeUpMessage(); } - public async void LoadApplications() - { - await Dispatcher.UIThread.InvokeAsync(() => - { - Applications.Clear(); - - StatusBarVisible = true; - StatusBarProgressMaximum = 0; - StatusBarProgressValue = 0; - - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); - }); - - ReloadGameList?.Invoke(); - } - public async void OpenFile() { - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { - OpenFileDialog dialog = new() + Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle], + AllowMultiple = false, + FileTypeFilter = new List<FilePickerFileType> { - Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle], - }; - - dialog.Filters.Add(new FileDialogFilter - { - Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats], - Extensions = + new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats]) { - "nsp", - "pfs0", - "xci", - "nca", - "nro", - "nso", + Patterns = new[] { "*.nsp", "*.xci", "*.nca", "*.nro", "*.nso" }, + AppleUniformTypeIdentifiers = new[] + { + "com.ryujinx.nsp", + "com.ryujinx.xci", + "com.ryujinx.nca", + "com.ryujinx.nro", + "com.ryujinx.nso" + }, + MimeTypes = new[] + { + "application/x-nx-nsp", + "application/x-nx-xci", + "application/x-nx-nca", + "application/x-nx-nro", + "application/x-nx-nso" + } + }, + new("NSP") + { + Patterns = new[] { "*.nsp" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" }, + MimeTypes = new[] { "application/x-nx-nsp" } + }, + new("XCI") + { + Patterns = new[] { "*.xci" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" }, + MimeTypes = new[] { "application/x-nx-xci" } + }, + new("NCA") + { + Patterns = new[] { "*.nca" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" }, + MimeTypes = new[] { "application/x-nx-nca" } + }, + new("NRO") + { + Patterns = new[] { "*.nro" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" }, + MimeTypes = new[] { "application/x-nx-nro" } + }, + new("NSO") + { + Patterns = new[] { "*.nso" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" }, + MimeTypes = new[] { "application/x-nx-nso" } }, - }); - -#pragma warning disable IDE0055 // Disable formatting - dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); -#pragma warning restore IDE0055 - - string[] files = await dialog.ShowAsync(desktop.MainWindow); - - if (files != null && files.Length > 0) - { - LoadApplication(files[0]); } + }); + + if (result.Count > 0) + { + LoadApplication(result[0].Path.LocalPath); } } public async void OpenFolder() { - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { - OpenFolderDialog dialog = new() - { - Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle], - }; + Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle], + AllowMultiple = false + }); - string folder = await dialog.ShowAsync(desktop.MainWindow); - - if (!string.IsNullOrWhiteSpace(folder) && Directory.Exists(folder)) - { - LoadApplication(folder); - } + if (result.Count > 0) + { + LoadApplication(result[0].Path.LocalPath); } } diff --git a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index 740d888bb..e3bca205d 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Collections; -using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform.Storage; using Avalonia.Threading; using LibHac.Common; using LibHac.Fs; @@ -70,12 +70,19 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public IStorageProvider StorageProvider; + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId) { VirtualFileSystem = virtualFileSystem; TitleId = titleId; + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + StorageProvider = desktop.MainWindow.StorageProvider; + } + TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); try @@ -202,29 +209,23 @@ namespace Ryujinx.Ava.UI.ViewModels public async void Add() { - OpenFileDialog dialog = new() + var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { - Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], AllowMultiple = true, - }; - - dialog.Filters.Add(new FileDialogFilter - { - Name = "NSP", - Extensions = { "nsp" }, - }); - - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - string[] files = await dialog.ShowAsync(desktop.MainWindow); - - if (files != null) + FileTypeFilter = new List<FilePickerFileType> { - foreach (string file in files) + new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats]) { - AddUpdate(file); + Patterns = new[] { "*.nsp" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" }, + MimeTypes = new[] { "application/x-nx-nsp" } } } + }); + + foreach (var file in result) + { + AddUpdate(file.Path.LocalPath); } SortUpdates(); diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml index 2395b353c..65a66d9e2 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml @@ -14,7 +14,6 @@ d:DesignWidth="800" x:Class="Ryujinx.Ava.UI.Views.Input.ControllerInputView" x:DataType="viewModels:ControllerInputViewModel" - x:CompileBindings="True" mc:Ignorable="d" Focusable="True"> <Design.DataContext> @@ -66,7 +65,7 @@ HorizontalAlignment="Stretch" VerticalAlignment="Center" SelectionChanged="PlayerIndexBox_OnSelectionChanged" - Items="{Binding PlayerIndexes}" + ItemsSource="{Binding PlayerIndexes}" SelectedIndex="{Binding PlayerId}"> <ComboBox.ItemTemplate> <DataTemplate> @@ -94,15 +93,15 @@ HorizontalAlignment="Left" VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsProfile}" /> - <ui:ComboBox + <ui:FAComboBox Grid.Column="1" IsEditable="True" Name="ProfileBox" HorizontalAlignment="Stretch" VerticalAlignment="Center" SelectedIndex="0" - Items="{Binding ProfilesList}" - Text="{Binding ProfileName}" /> + ItemsSource="{Binding ProfilesList}" + Text="{Binding ProfileName, Mode=TwoWay}" /> <Button Grid.Column="2" MinWidth="0" @@ -170,7 +169,7 @@ Name="DeviceBox" HorizontalAlignment="Stretch" VerticalAlignment="Center" - Items="{Binding DeviceList}" + ItemsSource="{Binding DeviceList}" SelectedIndex="{Binding Device}" /> <Button Grid.Column="2" @@ -203,8 +202,8 @@ <ComboBox Grid.Column="1" HorizontalAlignment="Stretch" - Items="{ReflectionBinding Controllers}" - SelectedIndex="{ReflectionBinding Controller}"> + ItemsSource="{Binding Controllers}" + SelectedIndex="{Binding Controller}"> <ComboBox.ItemTemplate> <DataTemplate DataType="models:ControllerModel"> <TextBlock Text="{Binding Name}" /> @@ -723,7 +722,7 @@ <Button Margin="10" Grid.Column="1" - Command="{ReflectionBinding ShowMotionConfig}"> + Command="{Binding ShowMotionConfig}"> <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" /> </Button> </Grid> @@ -750,7 +749,7 @@ <Button Margin="10" Grid.Column="1" - Command="{ReflectionBinding ShowRumbleConfig}"> + Command="{Binding ShowRumbleConfig}"> <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" /> </Button> </Grid> diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs index 19009f5f2..351297060 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs @@ -31,8 +31,7 @@ namespace Ryujinx.Ava.UI.Views.Input { if (visual is ToggleButton button && visual is not CheckBox) { - button.Checked += Button_Checked; - button.Unchecked += Button_Unchecked; + button.IsCheckedChanged += Button_IsCheckedChanged; } } } @@ -47,48 +46,56 @@ namespace Ryujinx.Ava.UI.Views.Input } } - private void Button_Checked(object sender, RoutedEventArgs e) + private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) { if (sender is ToggleButton button) { - if (_currentAssigner != null && button == _currentAssigner.ToggledButton) + if ((bool)button.IsChecked) { - return; - } - - bool isStick = button.Tag != null && button.Tag.ToString() == "stick"; - - if (_currentAssigner == null && (bool)button.IsChecked) - { - _currentAssigner = new ButtonKeyAssigner(button); - - FocusManager.Instance.Focus(this, NavigationMethod.Pointer); - - PointerPressed += MouseClick; - - IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. - IButtonAssigner assigner = CreateButtonAssigner(isStick); - - _currentAssigner.ButtonAssigned += (sender, e) => + if (_currentAssigner != null && button == _currentAssigner.ToggledButton) { - if (e.IsAssigned) - { - ViewModel.IsModified = true; - } - }; + return; + } - _currentAssigner.GetInputAndAssign(assigner, keyboard); + bool isStick = button.Tag != null && button.Tag.ToString() == "stick"; + + if (_currentAssigner == null) + { + _currentAssigner = new ButtonKeyAssigner(button); + + this.Focus(NavigationMethod.Pointer); + + PointerPressed += MouseClick; + + IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. + IButtonAssigner assigner = CreateButtonAssigner(isStick); + + _currentAssigner.ButtonAssigned += (sender, e) => + { + if (e.IsAssigned) + { + ViewModel.IsModified = true; + } + }; + + _currentAssigner.GetInputAndAssign(assigner, keyboard); + } + else + { + if (_currentAssigner != null) + { + ToggleButton oldButton = _currentAssigner.ToggledButton; + + _currentAssigner.Cancel(); + _currentAssigner = null; + button.IsChecked = false; + } + } } else { - if (_currentAssigner != null) - { - ToggleButton oldButton = _currentAssigner.ToggledButton; - - _currentAssigner.Cancel(); - _currentAssigner = null; - button.IsChecked = false; - } + _currentAssigner?.Cancel(); + _currentAssigner = null; } } } @@ -120,12 +127,6 @@ namespace Ryujinx.Ava.UI.Views.Input return assigner; } - private void Button_Unchecked(object sender, RoutedEventArgs e) - { - _currentAssigner?.Cancel(); - _currentAssigner = null; - } - private void MouseClick(object sender, PointerPressedEventArgs e) { bool shouldUnbind = false; diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml index b18324379..71d5d7460 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml @@ -8,7 +8,6 @@ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView" - x:CompileBindings="True" x:DataType="viewModels:MotionInputViewModel" Focusable="True"> <Grid Margin="10"> diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml index 3882ebe21..16190d391 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml @@ -8,7 +8,6 @@ mc:Ignorable="d" x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView" x:DataType="viewModels:RumbleInputViewModel" - x:CompileBindings="True" Focusable="True"> <Grid Margin="10"> <Grid.RowDefinitions> diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml index d5b5efcdd..30358adab 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml @@ -7,8 +7,7 @@ mc:Ignorable="d" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" x:DataType="viewModels:MainWindowViewModel" - x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView" - x:CompileBindings="True"> + x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView"> <Design.DataContext> <viewModels:MainWindowViewModel /> </Design.DataContext> @@ -25,12 +24,12 @@ </Menu.ItemsPanel> <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}"> <MenuItem - Command="{ReflectionBinding OpenFile}" + Command="{Binding OpenFile}" Header="{locale:Locale MenuBarFileOpenFromFile}" IsEnabled="{Binding EnableNonGameRunningControls}" ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" /> <MenuItem - Command="{ReflectionBinding OpenFolder}" + Command="{Binding OpenFolder}" Header="{locale:Locale MenuBarFileOpenUnpacked}" IsEnabled="{Binding EnableNonGameRunningControls}" ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" /> @@ -42,11 +41,11 @@ </MenuItem> <Separator /> <MenuItem - Command="{ReflectionBinding OpenRyujinxFolder}" + Command="{Binding OpenRyujinxFolder}" Header="{locale:Locale MenuBarFileOpenEmuFolder}" ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" /> <MenuItem - Command="{ReflectionBinding OpenLogsFolder}" + Command="{Binding OpenLogsFolder}" Header="{locale:Locale MenuBarFileOpenLogsFolder}" ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" /> <Separator /> @@ -57,35 +56,75 @@ </MenuItem> <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}"> <MenuItem - Command="{ReflectionBinding ToggleFullscreen}" + Padding="-10,0,0,0" + Command="{Binding ToggleFullscreen}" Header="{locale:Locale MenuBarOptionsToggleFullscreen}" InputGesture="F11" /> - <MenuItem> + <MenuItem + Padding="0" + Command="{Binding ToggleStartGamesInFullscreen}" + Header="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"> <MenuItem.Icon> - <CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}" - MinWidth="250"> - <TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/> - </CheckBox> + <CheckBox + MinWidth="{DynamicResource CheckBoxSize}" + MinHeight="{DynamicResource CheckBoxSize}" + IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}" + Padding="0" /> </MenuItem.Icon> + <MenuItem.Styles> + <Style Selector="Viewbox#PART_IconPresenter"> + <Setter Property="MaxHeight" Value="36" /> + <Setter Property="MinHeight" Value="36" /> + <Setter Property="MaxWidth" Value="36" /> + <Setter Property="MinWidth" Value="36" /> + </Style> + <Style Selector="ContentPresenter#PART_HeaderPresenter"> + <Setter Property="Padding" Value="-10,0,0,0" /> + </Style> + </MenuItem.Styles> </MenuItem> - <MenuItem IsVisible="{Binding ShowConsoleVisible}"> + <MenuItem + Padding="0" + IsVisible="{Binding ShowConsoleVisible}" + Command="{Binding ToggleShowConsole}" + Header="{locale:Locale MenuBarOptionsShowConsole}"> <MenuItem.Icon> - <CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}" - MinWidth="250"> - <TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/> - </CheckBox> + <CheckBox + MinWidth="{DynamicResource CheckBoxSize}" + MinHeight="{DynamicResource CheckBoxSize}" + IsChecked="{Binding ShowConsole, Mode=TwoWay}" + Padding="0" /> </MenuItem.Icon> + <MenuItem.Styles> + <Style Selector="Viewbox#PART_IconPresenter"> + <Setter Property="MaxHeight" Value="36" /> + <Setter Property="MinHeight" Value="36" /> + <Setter Property="MaxWidth" Value="36" /> + <Setter Property="MinWidth" Value="36" /> + </Style> + <Style Selector="ContentPresenter#PART_HeaderPresenter"> + <Setter Property="Padding" Value="-10,0,0,0" /> + </Style> + </MenuItem.Styles> </MenuItem> <Separator /> - <MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" /> - <MenuItem Name="ToggleFileTypesMenuItem" Header="{locale:Locale MenuBarShowFileTypes}" /> + <MenuItem + Name="ChangeLanguageMenuItem" + Padding="-10,0,0,0" + Header="{locale:Locale MenuBarOptionsChangeLanguage}" /> + <MenuItem + Name="ToggleFileTypesMenuItem" + Padding="-10,0,0,0" + Header="{locale:Locale MenuBarShowFileTypes}" /> <Separator /> <MenuItem Click="OpenSettings" + Padding="-10,0,0,0" Header="{locale:Locale MenuBarOptionsSettings}" ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" /> <MenuItem - Command="{ReflectionBinding ManageProfiles}" + Command="{Binding ManageProfiles}" + Padding="-10,0,0,0" Header="{locale:Locale MenuBarOptionsManageUserProfiles}" IsEnabled="{Binding EnableNonGameRunningControls}" ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" /> @@ -113,7 +152,7 @@ InputGesture="Escape" IsEnabled="{Binding IsGameRunning}" ToolTip.Tip="{locale:Locale StopEmulationTooltip}" /> - <MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" /> + <MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" /> <Separator /> <MenuItem Name="ScanAmiiboMenuItem" @@ -122,12 +161,12 @@ Header="{locale:Locale MenuBarActionsScanAmiibo}" IsEnabled="{Binding IsAmiiboRequested}" /> <MenuItem - Command="{ReflectionBinding TakeScreenshot}" + Command="{Binding TakeScreenshot}" Header="{locale:Locale MenuBarFileToolsTakeScreenshot}" InputGesture="{Binding ScreenshotKey}" IsEnabled="{Binding IsGameRunning}" /> <MenuItem - Command="{ReflectionBinding HideUi}" + Command="{Binding HideUi}" Header="{locale:Locale MenuBarFileToolsHideUi}" InputGesture="{Binding ShowUiKey}" IsEnabled="{Binding IsGameRunning}" /> @@ -138,8 +177,8 @@ </MenuItem> <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}"> <MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}"> - <MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" /> - <MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" /> + <MenuItem Command="{Binding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" /> + <MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" /> </MenuItem> <MenuItem Header="{locale:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}"> <MenuItem Header="{locale:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click"/> diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs index ae52f0719..af8c4dab9 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs @@ -30,8 +30,8 @@ namespace Ryujinx.Ava.UI.Views.Main { InitializeComponent(); - ToggleFileTypesMenuItem.Items = GenerateToggleFileTypeItems(); - ChangeLanguageMenuItem.Items = GenerateLanguageMenuItems(); + ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems(); + ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems(); } private CheckBox[] GenerateToggleFileTypeItems() @@ -45,7 +45,7 @@ namespace Ryujinx.Ava.UI.Views.Main { Content = $".{fileName}", IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes), - Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)), + Command = MiniCommand.Create(() => Window.ToggleFileType(fileName)), }); } diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml index 167056954..58e06a1c2 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml @@ -8,7 +8,6 @@ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView" - x:CompileBindings="True" x:DataType="viewModels:MainWindowViewModel"> <Design.DataContext> <viewModels:MainWindowViewModel /> @@ -46,7 +45,7 @@ Margin="0,0,5,0" VerticalAlignment="Center" Background="Transparent" - Command="{ReflectionBinding LoadApplications}"> + Click="Refresh_OnClick"> <ui:SymbolIcon Width="50" Height="100" @@ -64,7 +63,7 @@ Grid.Column="2" Height="6" VerticalAlignment="Center" - Foreground="{DynamicResource HighlightColor}" + Foreground="{DynamicResource SystemAccentColorLight2}" IsVisible="{Binding StatusBarVisible}" Maximum="{Binding StatusBarProgressMaximum}" Value="{Binding StatusBarProgressValue}" /> @@ -93,6 +92,7 @@ Height="12" Margin="0" BorderBrush="Gray" + Background="Gray" BorderThickness="1" IsVisible="{Binding !ShowLoadProgress}" /> <TextBlock @@ -109,6 +109,7 @@ Height="12" Margin="0" BorderBrush="Gray" + Background="Gray" BorderThickness="1" IsVisible="{Binding !ShowLoadProgress}" /> <TextBlock @@ -125,20 +126,32 @@ Height="12" Margin="0" BorderBrush="Gray" + Background="Gray" BorderThickness="1" IsVisible="{Binding !ShowLoadProgress}" /> - <ui:ToggleSplitButton + <ToggleSplitButton Name="VolumeStatus" Padding="5,0,5,0" HorizontalAlignment="Left" VerticalAlignment="Center" VerticalContentAlignment="Center" - Background="{DynamicResource ThemeContentBackgroundColor}" - BorderThickness="0" Content="{Binding VolumeStatusText}" IsChecked="{Binding VolumeMuted}" - IsVisible="{Binding !ShowLoadProgress}"> - <ui:ToggleSplitButton.Flyout> + IsVisible="{Binding !ShowLoadProgress}" + Background="Transparent" + BorderThickness="0" + CornerRadius="0"> + <ToggleSplitButton.Styles> + <Style Selector=":checked"> + <Style Selector="^:checked ContentPresenter"> + <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" /> + </Style> + </Style> + <Style Selector="Border#SeparatorBorder"> + <Setter Property="Opacity" Value="0" /> + </Style> + </ToggleSplitButton.Styles> + <ToggleSplitButton.Flyout> <Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway"> <Grid Margin="0"> <Slider @@ -156,13 +169,14 @@ Value="{Binding Volume}" /> </Grid> </Flyout> - </ui:ToggleSplitButton.Flyout> - </ui:ToggleSplitButton> + </ToggleSplitButton.Flyout> + </ToggleSplitButton> <Border Width="2" Height="12" Margin="0" BorderBrush="Gray" + Background="Gray" BorderThickness="1" IsVisible="{Binding !ShowLoadProgress}" /> <TextBlock @@ -177,6 +191,7 @@ Height="12" Margin="0" BorderBrush="Gray" + Background="Gray" BorderThickness="1" IsVisible="{Binding !ShowLoadProgress}" /> <TextBlock @@ -191,6 +206,7 @@ Height="12" Margin="0" BorderBrush="Gray" + Background="Gray" BorderThickness="1" IsVisible="{Binding !ShowLoadProgress}" /> <TextBlock @@ -205,6 +221,7 @@ Height="12" Margin="0" BorderBrush="Gray" + Background="Gray" BorderThickness="1" IsVisible="{Binding !ShowLoadProgress}" /> <TextBlock @@ -229,4 +246,4 @@ Text="{locale:Locale StatusBarSystemVersion}" /> </StackPanel> </Grid> -</UserControl> \ No newline at end of file +</UserControl> diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs index 0640869c1..a0acc2779 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Interactivity; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; @@ -48,5 +49,10 @@ namespace Ryujinx.Ava.UI.Views.Main ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; } + + private void Refresh_OnClick(object sender, RoutedEventArgs e) + { + Window.LoadApplications(); + } } } diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml index f7dbf2b21..f5a177424 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml @@ -9,7 +9,6 @@ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Ryujinx.Ava.UI.Views.Main.MainViewControls" - x:CompileBindings="True" x:DataType="viewModels:MainWindowViewModel"> <Design.DataContext> <viewModels:MainWindowViewModel /> @@ -23,7 +22,7 @@ MinWidth="40" Margin="5,2,0,2" VerticalAlignment="Stretch" - Command="{ReflectionBinding SetListMode}" + Command="{Binding SetListMode}" IsEnabled="{Binding IsGrid}"> <ui:FontIcon Margin="0" @@ -37,7 +36,7 @@ MinWidth="40" Margin="5,2,5,2" VerticalAlignment="Stretch" - Command="{ReflectionBinding SetGridMode}" + Command="{Binding SetGridMode}" IsEnabled="{Binding IsList}"> <ui:FontIcon Margin="0" @@ -79,13 +78,13 @@ KeyUp="SearchBox_OnKeyUp" Text="{Binding SearchText}" Watermark="{locale:Locale MenuSearch}" /> - <ui:DropDownButton + <DropDownButton Width="150" HorizontalAlignment="Right" VerticalAlignment="Center" Content="{Binding SortName}" DockPanel.Dock="Right"> - <ui:DropDownButton.Flyout> + <DropDownButton.Flyout> <Flyout Placement="Bottom"> <StackPanel Margin="0" @@ -164,8 +163,8 @@ Tag="Descending" /> </StackPanel> </Flyout> - </ui:DropDownButton.Flyout> - </ui:DropDownButton> + </DropDownButton.Flyout> + </DropDownButton> <TextBlock Margin="10,0" HorizontalAlignment="Right" diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml index 352833534..5dc0fef5d 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml @@ -1,4 +1,4 @@ -<UserControl +<UserControl x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsAudioView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" @@ -8,7 +8,6 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> <Design.DataContext> <viewModels:SettingsViewModel /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml index e98b963c1..c74d3dd57 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml @@ -7,7 +7,6 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> <Design.DataContext> <viewModels:SettingsViewModel /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml index 670de69c6..9dc67dadb 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml @@ -1,4 +1,4 @@ -<UserControl +<UserControl x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsGraphicsView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" @@ -9,7 +9,6 @@ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" Design.Width="1000" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> <Design.DataContext> <viewModels:SettingsViewModel /> @@ -54,7 +53,7 @@ HorizontalContentAlignment="Left" ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}" SelectedIndex="{Binding PreferredGpuIndex}" - Items="{Binding AvailableGpus}"/> + ItemsSource="{Binding AvailableGpus}"/> </StackPanel> </StackPanel> <Separator Height="1" /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml index 361125bfe..a53c1dfe4 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml @@ -1,4 +1,4 @@ -<UserControl +<UserControl x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" @@ -8,7 +8,6 @@ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel" Focusable="True"> <Design.DataContext> @@ -17,7 +16,7 @@ <UserControl.Resources> <helpers:KeyValueConverter x:Key="Key" /> </UserControl.Resources> - <ScrollViewer + <ScrollViewer Name="HotkeysPage" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs index 4bbd0133a..b6f2d6c08 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Settings { _currentAssigner = new ButtonKeyAssigner(button); - FocusManager.Instance?.Focus(this, NavigationMethod.Pointer); + this.Focus(NavigationMethod.Pointer); PointerPressed += MouseClick; diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml index 22ff38f57..81f4b68b7 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml @@ -8,7 +8,6 @@ xmlns:views="clr-namespace:Ryujinx.Ava.UI.Views.Input" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> <Design.DataContext> <viewModels:SettingsViewModel /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml index 948e7181f..0fc9ea1bb 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml @@ -8,7 +8,6 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> <Design.DataContext> <viewModels:SettingsViewModel /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml index ab8a7f6d1..6ce1bb94f 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml @@ -7,7 +7,6 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> <Design.DataContext> <viewModels:SettingsViewModel /> @@ -37,7 +36,7 @@ <ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}" ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}" HorizontalContentAlignment="Left" - Items="{Binding NetworkInterfaceList}" + ItemsSource="{Binding NetworkInterfaceList}" Width="250" /> </StackPanel> </StackPanel> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml index cc60ef24d..e6f7c6e46 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml @@ -3,12 +3,15 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" - x:CompileBindings="True" - x:DataType="viewModels:SettingsViewModel" - mc:Ignorable="d"> + xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" + mc:Ignorable="d" + x:DataType="viewModels:SettingsViewModel"> + <UserControl.Resources> + <helpers:TimeZoneConverter x:Key="TimeZone" /> + </UserControl.Resources> <Design.DataContext> <viewModels:SettingsViewModel /> </Design.DataContext> @@ -24,18 +27,24 @@ HorizontalAlignment="Stretch" Orientation="Vertical" Spacing="10"> - <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" /> - <StackPanel Margin="10,0,0,0" Orientation="Vertical"> - <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> + <TextBlock + Classes="h1" + Text="{locale:Locale SettingsTabSystemCore}" /> + <StackPanel + Margin="10,0,0,0" + Orientation="Vertical"> + <StackPanel + Margin="0,0,0,10" + Orientation="Horizontal"> <TextBlock - Width="250" VerticalAlignment="Center" - Text="{locale:Locale SettingsTabSystemSystemRegion}" /> + Text="{locale:Locale SettingsTabSystemSystemRegion}" + Width="250" /> <ComboBox - Width="350" - HorizontalContentAlignment="Left" SelectedIndex="{Binding Region}" - ToolTip.Tip="{locale:Locale RegionTooltip}"> + ToolTip.Tip="{locale:Locale RegionTooltip}" + HorizontalContentAlignment="Left" + Width="350"> <ComboBoxItem> <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" /> </ComboBoxItem> @@ -59,17 +68,19 @@ </ComboBoxItem> </ComboBox> </StackPanel> - <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> + <StackPanel + Margin="0,0,0,10" + Orientation="Horizontal"> <TextBlock - Width="250" VerticalAlignment="Center" Text="{locale:Locale SettingsTabSystemSystemLanguage}" - ToolTip.Tip="{locale:Locale LanguageTooltip}" /> + ToolTip.Tip="{locale:Locale LanguageTooltip}" + Width="250" /> <ComboBox - Width="350" - HorizontalContentAlignment="Left" SelectedIndex="{Binding Language}" - ToolTip.Tip="{locale:Locale LanguageTooltip}"> + ToolTip.Tip="{locale:Locale LanguageTooltip}" + HorizontalContentAlignment="Left" + Width="350"> <ComboBoxItem> <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" /> </ComboBoxItem> @@ -126,63 +137,84 @@ </ComboBoxItem> </ComboBox> </StackPanel> - <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> + <StackPanel + Margin="0,0,0,10" + Orientation="Horizontal"> <TextBlock - Width="250" VerticalAlignment="Center" Text="{locale:Locale SettingsTabSystemSystemTimeZone}" - ToolTip.Tip="{locale:Locale TimezoneTooltip}" /> + ToolTip.Tip="{locale:Locale TimezoneTooltip}" + Width="250" /> <AutoCompleteBox Name="TimeZoneBox" Width="350" - FilterMode="Contains" - Items="{Binding TimeZones}" MaxDropDownHeight="500" + FilterMode="Contains" + ItemsSource="{Binding TimeZones}" SelectionChanged="TimeZoneBox_OnSelectionChanged" Text="{Binding Path=TimeZone, Mode=OneWay}" TextChanged="TimeZoneBox_OnTextChanged" - ToolTip.Tip="{locale:Locale TimezoneTooltip}" /> + ToolTip.Tip="{locale:Locale TimezoneTooltip}" + ValueMemberBinding="{Binding Mode=OneWay, Converter={StaticResource TimeZone}}" /> </StackPanel> - <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> + <StackPanel + Margin="0,0,0,10" + Orientation="Horizontal"> <TextBlock - Width="250" VerticalAlignment="Center" Text="{locale:Locale SettingsTabSystemSystemTime}" - ToolTip.Tip="{locale:Locale TimeTooltip}" /> + ToolTip.Tip="{locale:Locale TimeTooltip}" + Width="250"/> <DatePicker - Width="350" - VerticalAlignment="Center" + VerticalAlignment="Center" SelectedDate="{Binding CurrentDate}" - ToolTip.Tip="{locale:Locale TimeTooltip}" /> + ToolTip.Tip="{locale:Locale TimeTooltip}" + Width="350" /> </StackPanel> - <StackPanel Margin="250,0,0,10" Orientation="Horizontal"> + <StackPanel + Margin="250,0,0,10" + Orientation="Horizontal"> <TimePicker - Width="350" VerticalAlignment="Center" ClockIdentifier="24HourClock" SelectedTime="{Binding CurrentTime}" + Width="350" ToolTip.Tip="{locale:Locale TimeTooltip}" /> </StackPanel> <CheckBox IsChecked="{Binding EnableVsync}"> - <TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}" ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" /> + <TextBlock + Text="{locale:Locale SettingsTabSystemEnableVsync}" + ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" /> </CheckBox> <CheckBox IsChecked="{Binding EnableFsIntegrityChecks}"> - <TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" /> + <TextBlock + Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" + ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" /> </CheckBox> </StackPanel> <Separator Height="1" /> - <StackPanel Orientation="Vertical" Spacing="2"> - <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" /> - <TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabSystemHacksNote}" /> + <StackPanel + Orientation="Vertical" + Spacing="2"> + <TextBlock + Classes="h1" + Text="{locale:Locale SettingsTabSystemHacks}" /> + <TextBlock + Foreground="{DynamicResource SecondaryTextColor}" + Text="{locale:Locale SettingsTabSystemHacksNote}" /> </StackPanel> <StackPanel Margin="10,0,0,0" HorizontalAlignment="Stretch" Orientation="Vertical"> - <CheckBox IsChecked="{Binding ExpandDramSize}" ToolTip.Tip="{locale:Locale DRamTooltip}"> + <CheckBox + IsChecked="{Binding ExpandDramSize}" + ToolTip.Tip="{locale:Locale DRamTooltip}"> <TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" /> </CheckBox> - <CheckBox IsChecked="{Binding IgnoreMissingServices}" ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}"> + <CheckBox + IsChecked="{Binding IgnoreMissingServices}" + ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}"> <TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" /> </CheckBox> </StackPanel> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs index 4acf2f44c..216561dc9 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs @@ -1,9 +1,5 @@ using Avalonia.Controls; -using Avalonia.Data; -using Avalonia.Data.Converters; using Ryujinx.Ava.UI.ViewModels; -using System; -using System.Linq; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; namespace Ryujinx.Ava.UI.Views.Settings @@ -15,15 +11,6 @@ namespace Ryujinx.Ava.UI.Views.Settings public SettingsSystemView() { InitializeComponent(); - - FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim()); - MultiBinding tzMultiBinding = new() { Converter = converter }; - - tzMultiBinding.Bindings.Add(new Binding("UtcDifference")); - tzMultiBinding.Bindings.Add(new Binding("Location")); - tzMultiBinding.Bindings.Add(new Binding("Abbreviation")); - - TimeZoneBox.ValueMemberBinding = tzMultiBinding; } private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) @@ -39,13 +26,11 @@ namespace Ryujinx.Ava.UI.Views.Settings } } - private void TimeZoneBox_OnTextChanged(object sender, EventArgs e) + private void TimeZoneBox_OnTextChanged(object sender, TextChangedEventArgs e) { if (sender is AutoCompleteBox box && box.SelectedItem is TimeZone timeZone) { - { - ViewModel.ValidateAndSetTimeZone(timeZone.Location); - } + ViewModel.ValidateAndSetTimeZone(timeZone.Location); } } } diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml index c92d56728..b7471d385 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml @@ -7,7 +7,6 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel"> <Design.DataContext> <viewModels:SettingsViewModel /> @@ -66,7 +65,7 @@ <ListBox Name="GameList" MinHeight="230" - Items="{Binding GameDirectories}"> + ItemsSource="{Binding GameDirectories}"> <ListBox.Styles> <Style Selector="ListBoxItem"> <Setter Property="Padding" Value="10" /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs index 132435e04..a38a88655 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs @@ -1,7 +1,7 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Interactivity; +using Avalonia.Platform.Storage; +using Avalonia.VisualTree; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.ViewModels; using System.Collections.Generic; @@ -30,13 +30,16 @@ namespace Ryujinx.Ava.UI.Views.Settings } else { - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (this.GetVisualRoot() is Window window) { - path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow); - - if (!string.IsNullOrWhiteSpace(path)) + var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { - ViewModel.GameDirectories.Add(path); + AllowMultiple = false + }); + + if (result.Count > 0) + { + ViewModel.GameDirectories.Add(result[0].Path.LocalPath); ViewModel.DirectoryChanged = true; } } @@ -61,22 +64,25 @@ namespace Ryujinx.Ava.UI.Views.Settings public async void BrowseTheme(object sender, RoutedEventArgs e) { - var dialog = new OpenFileDialog + var window = this.GetVisualRoot() as Window; + var result = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle], AllowMultiple = false, - }; - - dialog.Filters.Add(new FileDialogFilter { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] }); - - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - var file = await dialog.ShowAsync(desktop.MainWindow); - - if (file != null && file.Length > 0) + FileTypeFilter = new List<FilePickerFileType> { - ViewModel.CustomThemePath = file[0]; + new("xml") + { + Patterns = new[] { "*.xaml" }, + AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" }, + MimeTypes = new[] { "application/xaml+xml" } + } } + }); + + if (result.Count > 0) + { + ViewModel.CustomThemePath = result[0].Path.LocalPath; } } } diff --git a/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml index 7e55f25e4..ab83c2cdd 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml +++ b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml @@ -13,7 +13,6 @@ Padding="0" mc:Ignorable="d" Focusable="True" - x:CompileBindings="True" x:DataType="models:TempProfile"> <UserControl.Resources> <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> diff --git a/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml index d46fcefc2..21dfc9099 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml +++ b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml @@ -12,7 +12,6 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" - x:CompileBindings="True" x:DataType="viewModels:UserFirmwareAvatarSelectorViewModel" Focusable="True"> <Design.DataContext> @@ -36,7 +35,7 @@ BorderThickness="0" SelectedIndex="{Binding SelectedIndex}" Height="400" - Items="{Binding Images}" + ItemsSource="{Binding Images}" HorizontalAlignment="Stretch" VerticalAlignment="Center"> <ListBox.ItemsPanel> @@ -54,7 +53,7 @@ <Setter Property="MaxWidth" Value="85" /> <Setter Property="MinWidth" Value="85" /> </Style> - <Style Selector="ListBoxItem /template/ Border#SelectionIndicator"> + <Style Selector="ListBoxItem /template/ Rectangle#SelectionIndicator"> <Setter Property="MinHeight" Value="70" /> </Style> </ListBox.Styles> diff --git a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml index b9f51fdc7..65fbd4434 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml +++ b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml @@ -9,7 +9,6 @@ Focusable="True" mc:Ignorable="d" x:Class="Ryujinx.Ava.UI.Views.User.UserProfileImageSelectorView" - x:CompileBindings="True" x:DataType="viewModles:UserProfileImageSelectorViewModel" Width="500" d:DesignWidth="500"> diff --git a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs index 26b77dcdc..e9bf4408c 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs @@ -1,5 +1,6 @@ using Avalonia.Controls; using Avalonia.Interactivity; +using Avalonia.Platform.Storage; using Avalonia.VisualTree; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; @@ -10,6 +11,7 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.HLE.FileSystem; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; +using System.Collections.Generic; using System.IO; using Image = SixLabors.ImageSharp.Image; @@ -63,33 +65,25 @@ namespace Ryujinx.Ava.UI.Views.User private async void Import_OnClick(object sender, RoutedEventArgs e) { - OpenFileDialog dialog = new(); - dialog.Filters.Add(new FileDialogFilter + var window = this.GetVisualRoot() as Window; + var result = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { - Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats], - Extensions = { "jpg", "jpeg", "png", "bmp" }, - }); - dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "PNG", Extensions = { "png" } }); - dialog.Filters.Add(new FileDialogFilter { Name = "BMP", Extensions = { "bmp" } }); - - dialog.AllowMultiple = false; - - string[] image = await dialog.ShowAsync(((TopLevel)_parent.GetVisualRoot()) as Window); - - if (image != null) - { - if (image.Length > 0) + AllowMultiple = false, + FileTypeFilter = new List<FilePickerFileType> { - string imageFile = image[0]; - - _profile.Image = ProcessProfileImage(File.ReadAllBytes(imageFile)); - - if (_profile.Image != null) + new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats]) { - _parent.GoBack(); + Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" }, + AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.bmp" }, + MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" } } } + }); + + if (result.Count > 0) + { + _profile.Image = ProcessProfileImage(File.ReadAllBytes(result[0].Path.LocalPath)); + _parent.GoBack(); } } diff --git a/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml index 62b5e1840..debf4b843 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml +++ b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml @@ -12,7 +12,6 @@ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" x:Class="Ryujinx.Ava.UI.Views.User.UserRecovererView" - x:CompileBindings="True" x:DataType="viewModels:UserProfileViewModel" Focusable="True"> <Design.DataContext> @@ -33,7 +32,7 @@ <ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - Items="{Binding LostProfiles}"> + ItemsSource="{Binding LostProfiles}"> <ListBox.ItemTemplate> <DataTemplate> <Border diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml index ec931dd96..8bc5125a7 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml +++ b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml @@ -14,7 +14,6 @@ Height="450" Width="550" x:Class="Ryujinx.Ava.UI.Views.User.UserSaveManagerView" - x:CompileBindings="True" x:DataType="viewModels:UserSaveManagerViewModel" Focusable="True"> <Design.DataContext> @@ -107,8 +106,7 @@ VerticalAlignment="Stretch"> <ListBox Name="SaveList" - VirtualizationMode="None" - Items="{Binding Views}" + ItemsSource="{Binding Views}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <ListBox.Styles> @@ -117,7 +115,7 @@ <Setter Property="Margin" Value="5" /> <Setter Property="CornerRadius" Value="4" /> </Style> - <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator"> + <Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator"> <Setter Property="IsVisible" Value="False" /> </Style> </ListBox.Styles> diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml index 9a6ba054e..818a21d69 100644 --- a/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml +++ b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml @@ -15,7 +15,6 @@ d:DesignWidth="800" mc:Ignorable="d" Focusable="True" - x:CompileBindings="True" x:DataType="viewModels:UserProfileViewModel"> <UserControl.Resources> <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> @@ -38,7 +37,7 @@ VerticalAlignment="Center" SelectionChanged="ProfilesList_SelectionChanged" Background="Transparent" - Items="{Binding Profiles}"> + ItemsSource="{Binding Profiles}"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <flex:FlexPanel @@ -53,7 +52,7 @@ <Setter Property="Margin" Value="5 5 0 5" /> <Setter Property="CornerRadius" Value="5" /> </Style> - <Style Selector="Border#SelectionIndicator"> + <Style Selector="Rectangle#SelectionIndicator"> <Setter Property="Opacity" Value="0" /> </Style> </ListBox.Styles> @@ -61,8 +60,8 @@ <DataTemplate DataType="models:UserProfile"> <Grid - PointerEnter="Grid_PointerEntered" - PointerLeave="Grid_OnPointerExited"> + PointerEntered="Grid_PointerEntered" + PointerExited="Grid_OnPointerExited"> <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" diff --git a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml index cc7556a6b..a8fd11b27 100644 --- a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml @@ -13,7 +13,6 @@ Margin="0,-12,0,0" d:DesignHeight="260" d:DesignWidth="550" - x:CompileBindings="True" x:DataType="viewModel:AboutWindowViewModel" Focusable="True" mc:Ignorable="d"> @@ -64,14 +63,14 @@ FontWeight="Bold" Text="Ryujinx" TextAlignment="Center" - Width="100" /> + Width="110" /> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11" Text="(REE-YOU-JINX)" TextAlignment="Center" - Width="100" /> + Width="110" /> </flex:FlexPanel> </Grid> <TextBlock @@ -265,4 +264,4 @@ </StackPanel> </Grid> </Grid> -</UserControl> \ No newline at end of file +</UserControl> diff --git a/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml index 90d47b8ed..caf7c1f3f 100644 --- a/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml @@ -1,4 +1,4 @@ -<window:StyleableWindow +<window:StyleableWindow xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" @@ -6,14 +6,15 @@ xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" - mc:Ignorable="d" - d:DesignWidth="400" + mc:Ignorable="d" + d:DesignWidth="400" d:DesignHeight="350" x:Class="Ryujinx.Ava.UI.Windows.AmiiboWindow" + x:DataType="viewModels:AmiiboWindowViewModel" CanResize="False" WindowStartupLocation="CenterOwner" - Width="800" - MinHeight="650" + Width="800" + MinHeight="650" Height="650" SizeToContent="Manual" MinWidth="600" @@ -35,11 +36,11 @@ </Grid.ColumnDefinitions> <StackPanel Spacing="10" Orientation="Horizontal" HorizontalAlignment="Left"> <TextBlock VerticalAlignment="Center" Text="{locale:Locale AmiiboSeriesLabel}" /> - <ComboBox SelectedIndex="{Binding SeriesSelectedIndex}" Items="{Binding AmiiboSeries}" MinWidth="100" /> + <ComboBox SelectedIndex="{Binding SeriesSelectedIndex}" ItemsSource="{Binding AmiiboSeries}" MinWidth="100" /> </StackPanel> <StackPanel Spacing="10" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> <TextBlock VerticalAlignment="Center" Text="{locale:Locale AmiiboCharacterLabel}" /> - <ComboBox SelectedIndex="{Binding AmiiboSelectedIndex}" MinWidth="100" Items="{Binding AmiiboList}" /> + <ComboBox SelectedIndex="{Binding AmiiboSelectedIndex}" MinWidth="100" ItemsSource="{Binding AmiiboList}" /> </StackPanel> </Grid> <StackPanel Margin="20" Grid.Row="2"> diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml index 11e86211e..b9cbcb9cc 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml @@ -40,7 +40,7 @@ HorizontalAlignment="Center" VerticalAlignment="Center" LineHeight="18" - Text="{Binding Heading}" + Text="{ReflectionBinding Heading}" TextAlignment="Center" TextWrapping="Wrap" /> <TextBlock @@ -61,7 +61,7 @@ MinWidth="160" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="{Binding BuildId}" + Text="{ReflectionBinding BuildId}" IsReadOnly="True" /> <Border Grid.Row="3" @@ -77,7 +77,7 @@ MinHeight="300" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - Items="{Binding LoadedCheats}"> + ItemsSource="{ReflectionBinding LoadedCheats}"> <TreeView.Styles> <Styles> <Style Selector="TreeViewItem:empty /template/ ItemsPresenter"> @@ -120,15 +120,15 @@ Name="SaveButton" MinWidth="90" Margin="5" - Command="{Binding Save}" - IsVisible="{Binding !NoCheatsFound}"> + Command="{ReflectionBinding Save}" + IsVisible="{ReflectionBinding !NoCheatsFound}"> <TextBlock Text="{locale:Locale SettingsButtonSave}" /> </Button> <Button Name="CancelButton" MinWidth="90" Margin="5" - Command="{Binding Close}"> + Command="{ReflectionBinding Close}"> <TextBlock Text="{locale:Locale InputDialogCancel}" /> </Button> </DockPanel> diff --git a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs index a33ee518b..2b12d72f1 100644 --- a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs @@ -1,8 +1,5 @@ using Avalonia.Controls; using Avalonia.Media; -#if DEBUG -using Avalonia; -#endif namespace Ryujinx.Ava.UI.Windows { @@ -11,11 +8,9 @@ namespace Ryujinx.Ava.UI.Windows public ContentDialogOverlayWindow() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif + ExtendClientAreaToDecorationsHint = true; - TransparencyLevelHint = WindowTransparencyLevel.Transparent; + TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent }; WindowStartupLocation = WindowStartupLocation.Manual; SystemDecorations = SystemDecorations.None; ExtendClientAreaTitleBarHeightHint = 0; diff --git a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml index f4ba4f9c9..1f57f34cc 100644 --- a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml @@ -11,7 +11,6 @@ Width="500" Height="380" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:DownloadableContentManagerViewModel" Focusable="True"> <Grid> @@ -53,8 +52,8 @@ </StackPanel> <TextBox Grid.Column="2" - MinHeight="27" - MaxHeight="27" + MinHeight="29" + MaxHeight="29" HorizontalAlignment="Stretch" Watermark="{locale:Locale Search}" Text="{Binding Search}" /> @@ -71,12 +70,11 @@ Padding="2.5"> <ListBox AutoScrollToSelectedItem="False" - VirtualizationMode="None" SelectionMode="Multiple, Toggle" Background="Transparent" SelectionChanged="OnSelectionChanged" SelectedItems="{Binding SelectedDownloadableContents, Mode=TwoWay}" - Items="{Binding Views}"> + ItemsSource="{Binding Views}"> <ListBox.DataTemplates> <DataTemplate DataType="models:DownloadableContentModel"> diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml index fa07d9773..0d9a59499 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml @@ -18,9 +18,9 @@ MinHeight="672" d:DesignHeight="720" d:DesignWidth="1280" - x:CompileBindings="True" x:DataType="viewModels:MainWindowViewModel" mc:Ignorable="d" + WindowStartupLocation="Manual" Focusable="True"> <Window.Styles> <Style Selector="TitleBar:fullscreen"> @@ -33,19 +33,19 @@ <Window.Resources> <helpers:BitmapArrayValueConverter x:Key="ByteImage" /> </Window.Resources> + <Window.KeyBindings> + <KeyBinding Gesture="Alt+Return" Command="{Binding ToggleFullscreen}" /> + <KeyBinding Gesture="F11" Command="{Binding ToggleFullscreen}" /> + <KeyBinding Gesture="Ctrl+Cmd+F" Command="{Binding ToggleFullscreen}" /> + <KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" /> + <KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" /> + </Window.KeyBindings> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <helpers:OffscreenTextBox Name="HiddenTextBox" Grid.Row="0" /> - <StackPanel Grid.Row="0" IsVisible="False"> - <helpers:HotKeyControl Name="FullscreenHotKey" Command="{ReflectionBinding ToggleFullscreen}" /> - <helpers:HotKeyControl Name="FullscreenHotKey2" Command="{ReflectionBinding ToggleFullscreen}" /> - <helpers:HotKeyControl Name="FullscreenHotKeyMacOS" Command="{ReflectionBinding ToggleFullscreen}" /> - <helpers:HotKeyControl Name="DockToggleHotKey" Command="{ReflectionBinding ToggleDockMode}" /> - <helpers:HotKeyControl Name="ExitHotKey" Command="{ReflectionBinding ExitCurrentState}" /> - </StackPanel> <Grid Grid.Row="1" HorizontalAlignment="Stretch" @@ -67,7 +67,7 @@ VerticalAlignment="Stretch" IsVisible="{Binding ShowMenuAndStatusBar}" Orientation="Vertical"> - <main:MainMenuBarView + <main:MainMenuBarView Name="MenuBarView" /> </StackPanel> <ContentControl @@ -197,7 +197,7 @@ </Grid> </Grid> </Grid> - <main:MainStatusBarView + <main:MainStatusBarView Name="StatusBarView" Grid.Row="2" /> </Grid> diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index d79bdc56a..d32360e07 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Input; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; using Avalonia.Threading; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common; @@ -21,7 +22,6 @@ using Ryujinx.Ui.Common; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; using System; -using System.ComponentModel; using System.IO; using System.Runtime.Versioning; using System.Threading.Tasks; @@ -85,6 +85,7 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.Initialize( ContentManager, + StorageProvider, ApplicationLibrary, VirtualFileSystem, AccountManager, @@ -102,11 +103,16 @@ namespace Ryujinx.Ava.UI.Windows LoadGameList(); this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged); + this.ScalingChanged += OnScalingChanged; } ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; - ViewModel.ReloadGameList += ReloadGameList; + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); NotificationHelper.SetNotificationManager(this); } @@ -130,10 +136,9 @@ namespace Ryujinx.Ava.UI.Windows _isLoading = false; } - protected override void HandleScalingChanged(double scale) + private void OnScalingChanged(object sender, EventArgs e) { - Program.DesktopScaleFactor = scale; - base.HandleScalingChanged(scale); + Program.DesktopScaleFactor = this.RenderScaling; } public void AddApplication(ApplicationData applicationData) @@ -221,16 +226,6 @@ namespace Ryujinx.Ava.UI.Windows }); } - protected override void HandleWindowStateChanged(WindowState state) - { - ViewModel.WindowState = state; - - if (state != WindowState.Minimized) - { - Renderer.Start(); - } - } - private void Initialize() { _userChannelPersistence = new UserChannelPersistence(); @@ -367,14 +362,12 @@ namespace Ryujinx.Ava.UI.Windows ApplicationList.ApplicationOpened += Application_Opened; ApplicationList.DataContext = ViewModel; - - LoadHotKeys(); } private void SetWindowSizePosition() { PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, - ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); + ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor; ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor; @@ -447,18 +440,7 @@ namespace Ryujinx.Ava.UI.Windows #pragma warning restore IDE0055 } - public void LoadHotKeys() - { -#pragma warning disable IDE0055 // Disable formatting - HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); - HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11)); - HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta)); - HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); - HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); -#pragma warning restore IDE0055 - } - - private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e) + private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e) { var volumeSplitButton = sender as ToggleSplitButton; if (ViewModel.IsGameRunning) @@ -476,7 +458,7 @@ namespace Ryujinx.Ava.UI.Windows } } - protected override void OnClosing(CancelEventArgs e) + protected override void OnClosing(WindowClosingEventArgs e) { if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit) { @@ -548,6 +530,25 @@ namespace Ryujinx.Ava.UI.Windows ReloadGameList(); } + public void ToggleFileType(string fileType) + { + _ = fileType switch + { +#pragma warning disable IDE0055 // Disable formatting + "NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP, + "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0, + "XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI, + "NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA, + "NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO, + "NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO, + _ => throw new ArgumentOutOfRangeException(fileType), +#pragma warning restore IDE0055 + }; + + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + LoadApplications(); + } + private void ReloadGameList() { if (_isLoading) diff --git a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml index 4b248db79..a0a75f611 100644 --- a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml @@ -15,7 +15,6 @@ MinWidth="800" MinHeight="480" WindowStartupLocation="CenterOwner" - x:CompileBindings="True" x:DataType="viewModels:SettingsViewModel" mc:Ignorable="d" Focusable="True"> @@ -59,44 +58,44 @@ IsSelected="True" Content="{locale:Locale SettingsTabGeneral}" Tag="UiPage" - Icon="New" /> + IconSource="New" /> <ui:NavigationViewItem Content="{locale:Locale SettingsTabInput}" Tag="InputPage" - Icon="Games" /> + IconSource="Games" /> <ui:NavigationViewItem Content="{locale:Locale SettingsTabHotkeys}" Tag="HotkeysPage" - Icon="Keyboard" /> + IconSource="Keyboard" /> <ui:NavigationViewItem Content="{locale:Locale SettingsTabSystem}" Tag="SystemPage" - Icon="Settings" /> + IconSource="Settings" /> <ui:NavigationViewItem Content="{locale:Locale SettingsTabCpu}" Tag="CpuPage"> - <ui:NavigationViewItem.Icon> - <ui:FontIcon + <ui:NavigationViewItem.IconSource> + <ui:FontIconSource FontFamily="avares://Ryujinx.Ava/Assets/Fonts#Segoe Fluent Icons" Glyph="{helpers:GlyphValueConverter Chip}" /> - </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem.IconSource> </ui:NavigationViewItem> <ui:NavigationViewItem Content="{locale:Locale SettingsTabGraphics}" Tag="GraphicsPage" - Icon="Image" /> + IconSource="Image" /> <ui:NavigationViewItem Content="{locale:Locale SettingsTabAudio}" - Icon="Audio" + IconSource="Audio" Tag="AudioPage" /> <ui:NavigationViewItem Content="{locale:Locale SettingsTabNetwork}" Tag="NetworkPage" - Icon="Globe" /> + IconSource="Globe" /> <ui:NavigationViewItem Content="{locale:Locale SettingsTabLogging}" Tag="LoggingPage" - Icon="Document" /> + IconSource="Document" /> </ui:NavigationView.MenuItems> <ui:NavigationView.Styles> <Style Selector="Grid#PlaceholderGrid"> @@ -115,14 +114,14 @@ HotKey="Enter" Classes="accent" Content="{locale:Locale SettingsButtonOk}" - Command="{ReflectionBinding OkButton}" /> + Command="{Binding OkButton}" /> <Button HotKey="Escape" Content="{locale:Locale SettingsButtonCancel}" - Command="{ReflectionBinding CancelButton}" /> + Command="{Binding CancelButton}" /> <Button Content="{locale:Locale SettingsButtonApply}" - Command="{ReflectionBinding ApplyButton}" /> + Command="{Binding ApplyButton}" /> </ReversibleStackPanel> </Grid> </window:StyleableWindow> diff --git a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs index 518c2d328..d7bb0b883 100644 --- a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs @@ -1,10 +1,10 @@ +using Avalonia.Controls; using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.HLE.FileSystem; using System; -using System.ComponentModel; namespace Ryujinx.Ava.UI.Windows { @@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Windows if (Owner is MainWindow window && ViewModel.DirectoryChanged) { - window.ViewModel.LoadApplications(); + window.LoadApplications(); } } @@ -93,7 +93,7 @@ namespace Ryujinx.Ava.UI.Windows } } - protected override void OnClosing(CancelEventArgs e) + protected override void OnClosing(WindowClosingEventArgs e) { HotkeysPage.Dispose(); InputPage.Dispose(); diff --git a/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs b/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs index ca23a5ba3..43e42804a 100644 --- a/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs +++ b/src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs @@ -11,12 +11,12 @@ namespace Ryujinx.Ava.UI.Windows { public class StyleableWindow : Window { - public IBitmap IconImage { get; set; } + public Bitmap IconImage { get; set; } public StyleableWindow() { WindowStartupLocation = WindowStartupLocation.CenterOwner; - TransparencyLevelHint = WindowTransparencyLevel.None; + TransparencyLevelHint = new[] { WindowTransparencyLevel.None }; using Stream stream = Assembly.GetAssembly(typeof(ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); diff --git a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml index e98580386..78fc6b280 100644 --- a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml @@ -11,7 +11,6 @@ Width="500" Height="300" mc:Ignorable="d" - x:CompileBindings="True" x:DataType="viewModels:TitleUpdateViewModel" Focusable="True"> <Grid> @@ -29,10 +28,9 @@ CornerRadius="5" Padding="2.5"> <ListBox - VirtualizationMode="None" Background="Transparent" SelectedItem="{Binding SelectedUpdate, Mode=TwoWay}" - Items="{Binding Views}"> + ItemsSource="{Binding Views}"> <ListBox.DataTemplates> <DataTemplate DataType="models:TitleUpdateModel"> @@ -103,7 +101,7 @@ <Button Name="AddButton" MinWidth="90" - Command="{ReflectionBinding Add}"> + Command="{Binding Add}"> <TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" /> </Button> <Button diff --git a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index f4fcad319..7ece63355 100644 --- a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Ava.UI.Windows if (VisualRoot is MainWindow window) { - window.ViewModel.LoadApplications(); + window.LoadApplications(); } ((ContentDialog)Parent).Hide(); diff --git a/src/Ryujinx.Ava/app.manifest b/src/Ryujinx.Ava/app.manifest new file mode 100644 index 000000000..920136fb4 --- /dev/null +++ b/src/Ryujinx.Ava/app.manifest @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity version="1.0.0.0" name="Ryujinx.Emulator.Avalonia"/> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- Windows 10 & 11 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> + </application> + </compatibility> +</assembly> From 8c61ddd49d0b300866b602ad9f7fad69cd3bfa40 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Sat, 12 Aug 2023 17:02:22 +0100 Subject: [PATCH 730/737] Ava UI: Allow DPI switching (#5558) ForceDpiAware.Windows has a side effect of forcing the application DPI to be the same as the primary monitor. This isn't good if you have multiple monitors with different DPI. On Avalonia, I don't think there are any downsides to disabling this. When it's disabled, `ForceDpiAware.GetWindowScaleFactor` always returns 1. --- src/Ryujinx.Ava/Program.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Ryujinx.Ava/Program.cs b/src/Ryujinx.Ava/Program.cs index 86f38c347..168e9216d 100644 --- a/src/Ryujinx.Ava/Program.cs +++ b/src/Ryujinx.Ava/Program.cs @@ -100,8 +100,6 @@ namespace Ryujinx.Ava ReloadConfig(); - ForceDpiAware.Windows(); - WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); // Logging system information. From 2efd74b9cbb43b8f67907d39f353e06f4fef3863 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Sat, 12 Aug 2023 21:43:03 +0100 Subject: [PATCH 731/737] Ava UI: Make some settings methods async (#5332) * Ava: Asynchronously load Vulkan device settings items. * Sound checks, timezones and network interface async * Refresh UI items once awaited tasks complete * Remove unused dep * Timezone UI update * Use UIThread dispatcher for thread-unsafe collections + simplify GPU collection. * Remove empty lines * Remove unused string * Dispatch property changes * format changes * format 2 * Use Tasks instead of async void * Make NetworkInterfaceIndex access thread safe. --- .../UI/ViewModels/SettingsViewModel.cs | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 1e6d2734f..441c669d4 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -1,7 +1,6 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Threading; -using DynamicData; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.SDL2; @@ -24,6 +23,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Net.NetworkInformation; using System.Runtime.InteropServices; +using System.Threading.Tasks; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; namespace Ryujinx.Ava.UI.ViewModels @@ -44,7 +44,7 @@ namespace Ryujinx.Ava.UI.ViewModels private float _volume; private bool _isVulkanAvailable = true; private bool _directoryChanged; - private List<string> _gpuIds = new(); + private readonly List<string> _gpuIds = new(); private KeyboardHotkeys _keyboardHotkeys; private int _graphicsBackendIndex; private string _customThemePath; @@ -278,7 +278,7 @@ namespace Ryujinx.Ava.UI.ViewModels _contentManager = contentManager; if (Program.PreviewerDetached) { - LoadTimeZones(); + Task.Run(LoadTimeZones); } } @@ -290,27 +290,34 @@ namespace Ryujinx.Ava.UI.ViewModels _validTzRegions = new List<string>(); _networkInterfaces = new Dictionary<string, string>(); - CheckSoundBackends(); - PopulateNetworkInterfaces(); + Task.Run(CheckSoundBackends); + Task.Run(PopulateNetworkInterfaces); if (Program.PreviewerDetached) { - LoadAvailableGpus(); + Task.Run(LoadAvailableGpus); LoadCurrentConfiguration(); } } - public void CheckSoundBackends() + public async Task CheckSoundBackends() { IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported; IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported; IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported; + + await Dispatcher.UIThread.InvokeAsync(() => + { + OnPropertyChanged(nameof(IsOpenAlEnabled)); + OnPropertyChanged(nameof(IsSoundIoEnabled)); + OnPropertyChanged(nameof(IsSDL2Enabled)); + }); } - private void LoadAvailableGpus() + private async Task LoadAvailableGpus() { - _gpuIds = new List<string>(); - List<string> names = new(); + AvailableGpus.Clear(); + var devices = VulkanRenderer.GetPhysicalDevices(); if (devices.Length == 0) @@ -322,16 +329,23 @@ namespace Ryujinx.Ava.UI.ViewModels { foreach (var device in devices) { - _gpuIds.Add(device.Id); - names.Add($"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}"); + await Dispatcher.UIThread.InvokeAsync(() => + { + _gpuIds.Add(device.Id); + + AvailableGpus.Add(new ComboBoxItem { Content = $"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}" }); + }); } } - AvailableGpus.Clear(); - AvailableGpus.AddRange(names.Select(x => new ComboBoxItem { Content = x })); + // GPU configuration needs to be loaded during the async method or it will always return 0. + PreferredGpuIndex = _gpuIds.Contains(ConfigurationState.Instance.Graphics.PreferredGpu) ? + _gpuIds.IndexOf(ConfigurationState.Instance.Graphics.PreferredGpu) : 0; + + Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex))); } - public void LoadTimeZones() + public async Task LoadTimeZones() { _timeZoneContentManager = new TimeZoneContentManager(); @@ -344,21 +358,34 @@ namespace Ryujinx.Ava.UI.ViewModels string abbr2 = abbr.StartsWith('+') || abbr.StartsWith('-') ? string.Empty : abbr; - TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2)); + await Dispatcher.UIThread.InvokeAsync(() => + { + TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2)); - _validTzRegions.Add(location); + _validTzRegions.Add(location); + }); } + + Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(TimeZone))); } - private void PopulateNetworkInterfaces() + private async Task PopulateNetworkInterfaces() { _networkInterfaces.Clear(); _networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0"); foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces()) { - _networkInterfaces.Add(networkInterface.Name, networkInterface.Id); + await Dispatcher.UIThread.InvokeAsync(() => + { + _networkInterfaces.Add(networkInterface.Name, networkInterface.Id); + }); } + + // Network interface index needs to be loaded during the async method or it will always return 0. + NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); + + Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(NetworkInterfaceIndex))); } public void ValidateAndSetTimeZone(string location) @@ -416,7 +443,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Graphics GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; - PreferredGpuIndex = _gpuIds.Contains(config.Graphics.PreferredGpu) ? _gpuIds.IndexOf(config.Graphics.PreferredGpu) : 0; + // Physical devices are queried asynchronously hence the prefered index config value is loaded in LoadAvailableGpus(). EnableShaderCache = config.Graphics.EnableShaderCache; EnableTextureRecompression = config.Graphics.EnableTextureRecompression; EnableMacroHLE = config.Graphics.EnableMacroHLE; @@ -437,6 +464,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Network EnableInternetAccess = config.System.EnableInternetAccess; + // LAN interface index is loaded asynchronously in PopulateNetworkInterfaces() // Logging EnableFileLog = config.Logger.EnableFileLog; @@ -450,8 +478,6 @@ namespace Ryujinx.Ava.UI.ViewModels EnableFsAccessLog = config.Logger.EnableFsAccessLog; FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode; OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value; - - NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(config.Multiplayer.LanInterfaceId.Value); } public void SaveSettings() From ddefb4fff44509ca99628abf010ad6648a6503d5 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Sun, 13 Aug 2023 21:40:40 +0100 Subject: [PATCH 732/737] Remove animations on listbox items (#5563) --- src/Ryujinx.Ava/Assets/Styles/Styles.xaml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml index 146e60b5e..f7f64be22 100644 --- a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -335,28 +335,6 @@ Value="{DynamicResource AppListBackgroundColor}" /> <Setter Property="BorderThickness" Value="2"/> - <Style.Animations> - <Animation Duration="0:0:0.7"> - <KeyFrame Cue="0%"> - <Setter Property="MaxHeight" - Value="0" /> - <Setter Property="Opacity" - Value="0.0" /> - </KeyFrame> - <KeyFrame Cue="50%"> - <Setter Property="MaxHeight" - Value="1000" /> - <Setter Property="Opacity" - Value="0.3" /> - </KeyFrame> - <KeyFrame Cue="100%"> - <Setter Property="MaxHeight" - Value="1000" /> - <Setter Property="Opacity" - Value="1.0" /> - </KeyFrame> - </Animation> - </Style.Animations> </Style> <Style Selector="ListBox ListBoxItem:selected /template/ ContentPresenter"> <Setter Property="Background" From 8edfb2bc7b8507d0ef51f0544eb32a65f0bf54a1 Mon Sep 17 00:00:00 2001 From: Marco Carvalho <marcolucio27@gmail.com> Date: Sun, 13 Aug 2023 19:07:57 -0300 Subject: [PATCH 733/737] "static readonly" constants should be "const" instead (#5560) * "static readonly" constants should be "const" instead * change fields to PascalCase --- .../Instructions/InstEmitSimdHelper.cs | 2 +- src/Ryujinx.Ava/Modules/Updater/Updater.cs | 24 +++++++++---------- .../SystemInterop/ForceDpiAware.cs | 6 ++--- .../MultiFenceHolder.cs | 4 ++-- .../HOS/Diagnostics/Demangler/Demangler.cs | 4 ++-- .../Loaders/Processes/ProcessConst.cs | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs index 0b5527409..abd0d9acc 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -32,7 +32,7 @@ namespace ARMeilleure.Instructions 15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0, // S }; - public static readonly long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0; + public const long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0; public static ulong X86GetGf2p8LogicalShiftLeft(int shift) { diff --git a/src/Ryujinx.Ava/Modules/Updater/Updater.cs b/src/Ryujinx.Ava/Modules/Updater/Updater.cs index 8216333a7..5fa5241df 100644 --- a/src/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/src/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Modules private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish"); - private static readonly int _connectionCount = 4; + private const int ConnectionCount = 4; private static string _buildVer; private static string _platformExt; @@ -344,22 +344,22 @@ namespace Ryujinx.Modules private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile) { // Multi-Threaded Updater - long chunkSize = _buildSize / _connectionCount; - long remainderChunk = _buildSize % _connectionCount; + long chunkSize = _buildSize / ConnectionCount; + long remainderChunk = _buildSize % ConnectionCount; int completedRequests = 0; int totalProgressPercentage = 0; - int[] progressPercentage = new int[_connectionCount]; + int[] progressPercentage = new int[ConnectionCount]; - List<byte[]> list = new(_connectionCount); - List<WebClient> webClients = new(_connectionCount); + List<byte[]> list = new(ConnectionCount); + List<WebClient> webClients = new(ConnectionCount); - for (int i = 0; i < _connectionCount; i++) + for (int i = 0; i < ConnectionCount; i++) { list.Add(Array.Empty<byte>()); } - for (int i = 0; i < _connectionCount; i++) + for (int i = 0; i < ConnectionCount; i++) { #pragma warning disable SYSLIB0014 // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. @@ -368,7 +368,7 @@ namespace Ryujinx.Modules webClients.Add(client); - if (i == _connectionCount - 1) + if (i == ConnectionCount - 1) { client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); } @@ -385,7 +385,7 @@ namespace Ryujinx.Modules Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - taskDialog.SetProgressBarState(totalProgressPercentage / _connectionCount, TaskDialogProgressState.Normal); + taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal); }; client.DownloadDataCompleted += (_, args) => @@ -404,10 +404,10 @@ namespace Ryujinx.Modules list[index] = args.Result; Interlocked.Increment(ref completedRequests); - if (Equals(completedRequests, _connectionCount)) + if (Equals(completedRequests, ConnectionCount)) { byte[] mergedFileBytes = new byte[_buildSize]; - for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < _connectionCount; connectionIndex++) + for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) { Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); destinationOffset += list[connectionIndex].Length; diff --git a/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs b/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs index 5fa2281db..3af96ba12 100644 --- a/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs +++ b/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs @@ -28,8 +28,8 @@ namespace Ryujinx.Common.SystemInterop [LibraryImport(X11LibraryName)] private static partial int XCloseDisplay(IntPtr display); - private static readonly double _standardDpiScale = 96.0; - private static readonly double _maxScaleFactor = 1.25; + private const double StandardDpiScale = 96.0; + private const double MaxScaleFactor = 1.25; /// <summary> /// Marks the application as DPI-Aware when running on the Windows operating system. @@ -90,7 +90,7 @@ namespace Ryujinx.Common.SystemInterop { double userDpiScale = GetActualScaleFactor(); - return Math.Min(userDpiScale / _standardDpiScale, _maxScaleFactor); + return Math.Min(userDpiScale / StandardDpiScale, MaxScaleFactor); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs index d564f3e16..71769c5e1 100644 --- a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan /// </summary> class MultiFenceHolder { - private static readonly int _bufferUsageTrackingGranularity = 4096; + private const int BufferUsageTrackingGranularity = 4096; private readonly FenceHolder[] _fences; private readonly BufferUsageBitmap _bufferUsageBitmap; @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan public MultiFenceHolder(int size) { _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - _bufferUsageBitmap = new BufferUsageBitmap(size, _bufferUsageTrackingGranularity); + _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); } /// <summary> diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs index ba2d67fca..311488bcb 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler { class Demangler { - private static readonly string _base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; + private const string Base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; private readonly List<BaseNode> _substitutionList = new(); private List<BaseNode> _templateParamList = new(); @@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler for (int i = 0; i < reversedEncoded.Length; i++) { - int value = _base36.IndexOf(reversedEncoded[i]); + int value = Base36.IndexOf(reversedEncoded[i]); if (value == -1) { return -1; diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs index 5df7be29f..9fbf9134b 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessConst.cs @@ -20,7 +20,7 @@ "sdk", }; - public static readonly string MainNpdmPath = "/main.npdm"; + public const string MainNpdmPath = "/main.npdm"; public const int NroAsetMagic = ('A' << 0) | ('S' << 8) | ('E' << 16) | ('T' << 24); From b423197619dd8d9dda1c255a76105bf30e255dae Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 13 Aug 2023 22:26:42 -0300 Subject: [PATCH 734/737] Delete ShaderConfig and organize shader resources/definitions better (#5509) * Move some properties out of ShaderConfig * Stop using ShaderConfig on backends * Replace ShaderConfig usages on Translator and passes * Move remaining properties out of ShaderConfig and delete ShaderConfig * Remove ResourceManager property from TranslatorContext * Move Rewriter passes to separate transform pass files * Fix TransformPasses.RunPass on cases where a node is removed * Move remaining ClipDistancePrimitivesWritten and UsedFeatures updates to decode stage * Reduce excessive parameter passing a bit by using structs more * Remove binding parameter from ShaderProperties methods since it is redundant * Replace decoder instruction checks with switch statement * Put GLSL on the same plan as SPIR-V for input/output declaration * Stop mutating TranslatorContext state when Translate is called * Pass most of the graphics state using a struct instead of individual query methods * Auto-format * Auto-format * Add backend logging interface * Auto-format * Remove unnecessary use of interpolated strings * Remove more modifications of AttributeUsage after decode * PR feedback * gl_Layer is not supported on compute --- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 123 +--- .../Shader/GpuAccessor.cs | 126 +--- .../Shader/GpuAccessorBase.cs | 29 - .../Shader/GpuChannelGraphicsState.cs | 129 ++-- .../Shader/ShaderSpecializationState.cs | 18 - src/Ryujinx.Graphics.Shader/AttributeType.cs | 11 - .../CodeGen/CodeGenParameters.cs | 31 + .../CodeGen/Glsl/CodeGenContext.cs | 16 +- .../CodeGen/Glsl/Declarations.cs | 349 +++++----- .../CodeGen/Glsl/GlslGenerator.cs | 8 +- .../Glsl/Instructions/InstGenBallot.cs | 2 +- .../CodeGen/Glsl/Instructions/InstGenFSI.cs | 6 +- .../Glsl/Instructions/InstGenMemory.cs | 47 +- .../CodeGen/Glsl/Instructions/IoMap.cs | 43 +- .../CodeGen/Glsl/OperandManager.cs | 21 +- .../CodeGen/Spirv/CodeGenContext.cs | 49 +- .../CodeGen/Spirv/Declarations.cs | 82 ++- .../CodeGen/Spirv/Instructions.cs | 34 +- .../CodeGen/Spirv/SpirvGenerator.cs | 69 +- .../Decoders/DecodedProgram.cs | 20 +- .../Decoders/Decoder.cs | 172 ++++- .../GpuGraphicsState.cs | 169 +++++ src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 161 +---- src/Ryujinx.Graphics.Shader/ILogger.cs | 17 + .../Instructions/AttributeMap.cs | 50 +- .../Instructions/InstEmit.cs | 100 +-- .../Instructions/InstEmitAttribute.cs | 74 +- .../Instructions/InstEmitBarrier.cs | 2 +- .../Instructions/InstEmitConversion.cs | 4 +- .../Instructions/InstEmitFloatArithmetic.cs | 2 +- .../Instructions/InstEmitFlowControl.cs | 6 +- .../Instructions/InstEmitIntegerArithmetic.cs | 6 +- .../Instructions/InstEmitMemory.cs | 48 +- .../Instructions/InstEmitMove.cs | 10 +- .../Instructions/InstEmitMultifunction.cs | 2 +- .../Instructions/InstEmitSurface.cs | 29 +- .../Instructions/InstEmitTexture.cs | 24 +- .../Instructions/InstEmitWarp.cs | 2 +- .../StructuredIr/AstOptimizer.cs | 2 +- .../StructuredIr/ShaderProperties.cs | 66 +- .../StructuredIr/StructuredProgram.cs | 15 +- .../StructuredIr/StructuredProgramContext.cs | 27 +- .../StructuredIr/StructuredProgramInfo.cs | 16 - .../Translation/AttributeUsage.cs | 168 +++++ .../Translation/EmitterContext.cs | 121 ++-- .../Translation/FeatureFlags.cs | 3 - .../Translation/HostCapabilities.cs | 34 + .../Optimizations/BindlessElimination.cs | 38 +- .../Optimizations/BindlessToIndexed.cs | 10 +- .../Optimizations/ConstantFolding.cs | 4 +- .../Optimizations/GlobalToStorage.cs | 76 ++- .../Translation/Optimizations/Optimizer.cs | 26 +- .../Translation/ResourceManager.cs | 24 +- .../Translation/ShaderConfig.cs | 639 ------------------ .../Translation/ShaderDefinitions.cs | 315 +++++++++ .../Translation/ShaderIdentifier.cs | 18 +- .../Translation/TransformContext.cs | 33 + .../Translation/TransformFeedbackOutput.cs | 18 + .../Transforms/DrawParametersReplace.cs | 93 +++ .../Transforms/ForcePreciseEnable.cs | 36 + .../Translation/Transforms/ITransformPass.cs | 11 + .../Transforms/SharedAtomicSignedCas.cs | 58 ++ .../Transforms/SharedStoreSmallIntCas.cs | 57 ++ .../TexturePass.cs} | 402 ++--------- .../Translation/Transforms/TransformPasses.cs | 41 ++ .../Transforms/VectorComponentSelect.cs | 96 +++ .../Translation/Translator.cs | 201 +++--- .../Translation/TranslatorContext.cs | 321 +++++++-- 68 files changed, 2653 insertions(+), 2407 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/CodeGenParameters.cs create mode 100644 src/Ryujinx.Graphics.Shader/GpuGraphicsState.cs create mode 100644 src/Ryujinx.Graphics.Shader/ILogger.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/AttributeUsage.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs delete mode 100644 src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/TransformFeedbackOutput.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Transforms/DrawParametersReplace.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Transforms/ITransformPass.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedAtomicSignedCas.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedStoreSmallIntCas.cs rename src/Ryujinx.Graphics.Shader/Translation/{Rewriter.cs => Transforms/TexturePass.cs} (61%) create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Transforms/TransformPasses.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/Transforms/VectorComponentSelect.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index b5f9395e7..93d293f62 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -44,6 +43,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache _newSpecState = newSpecState; _stageIndex = stageIndex; _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; + + if (stageIndex == (int)ShaderStage.Geometry - 1) + { + // Only geometry shaders require the primitive topology. + newSpecState.RecordPrimitiveTopology(); + } } /// <inheritdoc/> @@ -69,48 +74,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return MemoryMarshal.Cast<byte, ulong>(_data.Span[(int)address..]); } - /// <inheritdoc/> - public bool QueryAlphaToCoverageDitherEnable() - { - return _oldSpecState.GraphicsState.AlphaToCoverageEnable && _oldSpecState.GraphicsState.AlphaToCoverageDitherEnable; - } - - /// <inheritdoc/> - public AlphaTestOp QueryAlphaTestCompare() - { - if (!_isVulkan || !_oldSpecState.GraphicsState.AlphaTestEnable) - { - return AlphaTestOp.Always; - } - - return _oldSpecState.GraphicsState.AlphaTestCompare switch - { - CompareOp.Never or CompareOp.NeverGl => AlphaTestOp.Never, - CompareOp.Less or CompareOp.LessGl => AlphaTestOp.Less, - CompareOp.Equal or CompareOp.EqualGl => AlphaTestOp.Equal, - CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => AlphaTestOp.LessOrEqual, - CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater, - CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual, - CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual, - _ => AlphaTestOp.Always, - }; - } - - /// <inheritdoc/> - public float QueryAlphaTestReference() => _oldSpecState.GraphicsState.AlphaTestReference; - - /// <inheritdoc/> - public AttributeType QueryAttributeType(int location) - { - return _oldSpecState.GraphicsState.AttributeTypes[location]; - } - - /// <inheritdoc/> - public AttributeType QueryFragmentOutputType(int location) - { - return _oldSpecState.GraphicsState.FragmentOutputTypes[location]; - } - /// <inheritdoc/> public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX; @@ -133,55 +96,18 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.ConstantBufferUse[_stageIndex]; } + /// <inheritdoc/> + public GpuGraphicsState QueryGraphicsState() + { + return _oldSpecState.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _oldSpecState.GraphicsState.YNegateEnabled); + } + /// <inheritdoc/> public bool QueryHasConstantBufferDrawParameters() { return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; } - /// <inheritdoc/> - public bool QueryDualSourceBlendEnable() - { - return _oldSpecState.GraphicsState.DualSourceBlendEnable; - } - - /// <inheritdoc/> - public InputTopology QueryPrimitiveTopology() - { - _newSpecState.RecordPrimitiveTopology(); - return ConvertToInputTopology(_oldSpecState.GraphicsState.Topology, _oldSpecState.GraphicsState.TessellationMode); - } - - /// <inheritdoc/> - public bool QueryProgramPointSize() - { - return _oldSpecState.GraphicsState.ProgramPointSizeEnable; - } - - /// <inheritdoc/> - public float QueryPointSize() - { - return _oldSpecState.GraphicsState.PointSize; - } - - /// <inheritdoc/> - public bool QueryTessCw() - { - return _oldSpecState.GraphicsState.TessellationMode.UnpackCw(); - } - - /// <inheritdoc/> - public TessPatchType QueryTessPatchType() - { - return _oldSpecState.GraphicsState.TessellationMode.UnpackPatchType(); - } - - /// <inheritdoc/> - public TessSpacing QueryTessSpacing() - { - return _oldSpecState.GraphicsState.TessellationMode.UnpackSpacing(); - } - /// <inheritdoc/> public TextureFormat QueryTextureFormat(int handle, int cbufSlot) { @@ -204,12 +130,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); } - /// <inheritdoc/> - public bool QueryTransformDepthMinusOneToOne() - { - return _oldSpecState.GraphicsState.DepthMode; - } - /// <inheritdoc/> public bool QueryTransformFeedbackEnabled() { @@ -228,31 +148,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.TransformFeedbackDescriptors[bufferIndex].Stride; } - /// <inheritdoc/> - public bool QueryEarlyZForce() - { - _newSpecState.RecordEarlyZForce(); - return _oldSpecState.GraphicsState.EarlyZForce; - } - /// <inheritdoc/> public bool QueryHasUnalignedStorageBuffer() { return _oldSpecState.GraphicsState.HasUnalignedStorageBuffer || _oldSpecState.ComputeState.HasUnalignedStorageBuffer; } - /// <inheritdoc/> - public bool QueryViewportTransformDisable() - { - return _oldSpecState.GraphicsState.ViewportTransformDisable; - } - - /// <inheritdoc/> - public bool QueryYNegateEnabled() - { - return _oldSpecState.GraphicsState.YNegateEnabled; - } - /// <inheritdoc/> public void RegisterTexture(int handle, int cbufSlot) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 1fcc93c50..b4f4a439c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -36,6 +35,12 @@ namespace Ryujinx.Graphics.Gpu.Shader _channel = channel; _state = state; _stageIndex = stageIndex; + + if (stageIndex == (int)ShaderStage.Geometry - 1) + { + // Only geometry shaders require the primitive topology. + _state.SpecializationState.RecordPrimitiveTopology(); + } } /// <summary> @@ -74,58 +79,6 @@ namespace Ryujinx.Graphics.Gpu.Shader return MemoryMarshal.Cast<byte, ulong>(_channel.MemoryManager.GetSpan(address, size)); } - /// <inheritdoc/> - public bool QueryAlphaToCoverageDitherEnable() - { - return _state.GraphicsState.AlphaToCoverageEnable && _state.GraphicsState.AlphaToCoverageDitherEnable; - } - - /// <inheritdoc/> - public AlphaTestOp QueryAlphaTestCompare() - { - if (!_isVulkan || !_state.GraphicsState.AlphaTestEnable) - { - return AlphaTestOp.Always; - } - - return _state.GraphicsState.AlphaTestCompare switch - { - CompareOp.Never or CompareOp.NeverGl => AlphaTestOp.Never, - CompareOp.Less or CompareOp.LessGl => AlphaTestOp.Less, - CompareOp.Equal or CompareOp.EqualGl => AlphaTestOp.Equal, - CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => AlphaTestOp.LessOrEqual, - CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater, - CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual, - CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual, - _ => AlphaTestOp.Always, - }; - } - - /// <inheritdoc/> - public float QueryAlphaTestReference() - { - return _state.GraphicsState.AlphaTestReference; - } - - /// <inheritdoc/> - public AttributeType QueryAttributeType(int location) - { - return _state.GraphicsState.AttributeTypes[location]; - } - - /// <inheritdoc/> - public bool QueryEarlyZForce() - { - _state.SpecializationState?.RecordEarlyZForce(); - return _state.GraphicsState.EarlyZForce; - } - - /// <inheritdoc/> - public AttributeType QueryFragmentOutputType(int location) - { - return _state.GraphicsState.FragmentOutputTypes[location]; - } - /// <inheritdoc/> public int QueryComputeLocalSizeX() => _state.ComputeState.LocalSizeX; @@ -152,6 +105,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return useMask; } + /// <inheritdoc/> + public GpuGraphicsState QueryGraphicsState() + { + return _state.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _state.GraphicsState.YNegateEnabled); + } + /// <inheritdoc/> public bool QueryHasConstantBufferDrawParameters() { @@ -164,49 +123,6 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; } - /// <inheritdoc/> - public bool QueryDualSourceBlendEnable() - { - return _state.GraphicsState.DualSourceBlendEnable; - } - - /// <inheritdoc/> - public InputTopology QueryPrimitiveTopology() - { - _state.SpecializationState?.RecordPrimitiveTopology(); - return ConvertToInputTopology(_state.GraphicsState.Topology, _state.GraphicsState.TessellationMode); - } - - /// <inheritdoc/> - public bool QueryProgramPointSize() - { - return _state.GraphicsState.ProgramPointSizeEnable; - } - - /// <inheritdoc/> - public float QueryPointSize() - { - return _state.GraphicsState.PointSize; - } - - /// <inheritdoc/> - public bool QueryTessCw() - { - return _state.GraphicsState.TessellationMode.UnpackCw(); - } - - /// <inheritdoc/> - public TessPatchType QueryTessPatchType() - { - return _state.GraphicsState.TessellationMode.UnpackPatchType(); - } - - /// <inheritdoc/> - public TessSpacing QueryTessSpacing() - { - return _state.GraphicsState.TessellationMode.UnpackSpacing(); - } - //// <inheritdoc/> public TextureFormat QueryTextureFormat(int handle, int cbufSlot) { @@ -258,12 +174,6 @@ namespace Ryujinx.Graphics.Gpu.Shader } } - /// <inheritdoc/> - public bool QueryTransformDepthMinusOneToOne() - { - return _state.GraphicsState.DepthMode; - } - /// <inheritdoc/> public bool QueryTransformFeedbackEnabled() { @@ -282,18 +192,6 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.TransformFeedbackDescriptors[bufferIndex].Stride; } - /// <inheritdoc/> - public bool QueryViewportTransformDisable() - { - return _state.GraphicsState.ViewportTransformDisable; - } - - /// <inheritdoc/> - public bool QueryYNegateEnabled() - { - return _state.GraphicsState.YNegateEnabled; - } - /// <inheritdoc/> public void RegisterTexture(int handle, int cbufSlot) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 07b7ddc28..6d27f18dc 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -232,33 +231,5 @@ namespace Ryujinx.Graphics.Gpu.Shader #pragma warning restore IDE0055 }; } - - /// <summary> - /// Converts the Maxwell primitive topology to the shader translator topology. - /// </summary> - /// <param name="topology">Maxwell primitive topology</param> - /// <param name="tessellationMode">Maxwell tessellation mode</param> - /// <returns>Shader translator topology</returns> - protected static InputTopology ConvertToInputTopology(PrimitiveTopology topology, TessMode tessellationMode) - { - return topology switch - { - PrimitiveTopology.Points => InputTopology.Points, - PrimitiveTopology.Lines or - PrimitiveTopology.LineLoop or - PrimitiveTopology.LineStrip => InputTopology.Lines, - PrimitiveTopology.LinesAdjacency or - PrimitiveTopology.LineStripAdjacency => InputTopology.LinesAdjacency, - PrimitiveTopology.Triangles or - PrimitiveTopology.TriangleStrip or - PrimitiveTopology.TriangleFan => InputTopology.Triangles, - PrimitiveTopology.TrianglesAdjacency or - PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency, - PrimitiveTopology.Patches => tessellationMode.UnpackPatchType() == TessPatchType.Isolines - ? InputTopology.Lines - : InputTopology.Triangles, - _ => InputTopology.Points, - }; - } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index f392491c3..b5bc4df3c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -103,64 +103,81 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool YNegateEnabled; /// <summary> - /// Creates a new GPU graphics state. + /// Creates a new graphics state from this state that can be used for shader generation. /// </summary> - /// <param name="earlyZForce">Early Z force enable</param> - /// <param name="topology">Primitive topology</param> - /// <param name="tessellationMode">Tessellation mode</param> - /// <param name="alphaToCoverageEnable">Indicates whether alpha-to-coverage is enabled</param> - /// <param name="alphaToCoverageDitherEnable">Indicates whether alpha-to-coverage dithering is enabled</param> - /// <param name="viewportTransformDisable">Indicates whether the viewport transform is disabled</param> - /// <param name="depthMode">Depth mode zero to one or minus one to one</param> - /// <param name="programPointSizeEnable">Indicates if the point size is set on the shader or is fixed</param> - /// <param name="pointSize">Point size if not set from shader</param> - /// <param name="alphaTestEnable">Indicates whether alpha test is enabled</param> - /// <param name="alphaTestCompare">When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded</param> - /// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param> - /// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param> - /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> - /// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param> - /// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param> - /// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param> - /// <param name="yNegateEnabled">Indicates whether Y negate of the fragment coordinates is enabled</param> - public GpuChannelGraphicsState( - bool earlyZForce, - PrimitiveTopology topology, - TessMode tessellationMode, - bool alphaToCoverageEnable, - bool alphaToCoverageDitherEnable, - bool viewportTransformDisable, - bool depthMode, - bool programPointSizeEnable, - float pointSize, - bool alphaTestEnable, - CompareOp alphaTestCompare, - float alphaTestReference, - ref Array32<AttributeType> attributeTypes, - bool hasConstantBufferDrawParameters, - bool hasUnalignedStorageBuffer, - ref Array8<AttributeType> fragmentOutputTypes, - bool dualSourceBlendEnable, - bool yNegateEnabled) + /// <param name="hostSupportsAlphaTest">Indicates if the host API supports alpha test operations</param> + /// <returns>GPU graphics state that can be used for shader translation</returns> + public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool originUpperLeft) { - EarlyZForce = earlyZForce; - Topology = topology; - TessellationMode = tessellationMode; - AlphaToCoverageEnable = alphaToCoverageEnable; - AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable; - ViewportTransformDisable = viewportTransformDisable; - DepthMode = depthMode; - ProgramPointSizeEnable = programPointSizeEnable; - PointSize = pointSize; - AlphaTestEnable = alphaTestEnable; - AlphaTestCompare = alphaTestCompare; - AlphaTestReference = alphaTestReference; - AttributeTypes = attributeTypes; - HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; - HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; - FragmentOutputTypes = fragmentOutputTypes; - DualSourceBlendEnable = dualSourceBlendEnable; - YNegateEnabled = yNegateEnabled; + AlphaTestOp alphaTestOp; + + if (hostSupportsAlphaTest || !AlphaTestEnable) + { + alphaTestOp = AlphaTestOp.Always; + } + else + { + alphaTestOp = AlphaTestCompare switch + { + CompareOp.Never or CompareOp.NeverGl => AlphaTestOp.Never, + CompareOp.Less or CompareOp.LessGl => AlphaTestOp.Less, + CompareOp.Equal or CompareOp.EqualGl => AlphaTestOp.Equal, + CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => AlphaTestOp.LessOrEqual, + CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater, + CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual, + CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual, + _ => AlphaTestOp.Always, + }; + } + + return new GpuGraphicsState( + EarlyZForce, + ConvertToInputTopology(Topology, TessellationMode), + TessellationMode.UnpackCw(), + TessellationMode.UnpackPatchType(), + TessellationMode.UnpackSpacing(), + AlphaToCoverageEnable, + AlphaToCoverageDitherEnable, + ViewportTransformDisable, + DepthMode, + ProgramPointSizeEnable, + PointSize, + alphaTestOp, + AlphaTestReference, + in AttributeTypes, + HasConstantBufferDrawParameters, + in FragmentOutputTypes, + DualSourceBlendEnable, + YNegateEnabled, + originUpperLeft); + } + + /// <summary> + /// Converts the Maxwell primitive topology to the shader translator topology. + /// </summary> + /// <param name="topology">Maxwell primitive topology</param> + /// <param name="tessellationMode">Maxwell tessellation mode</param> + /// <returns>Shader translator topology</returns> + private static InputTopology ConvertToInputTopology(PrimitiveTopology topology, TessMode tessellationMode) + { + return topology switch + { + PrimitiveTopology.Points => InputTopology.Points, + PrimitiveTopology.Lines or + PrimitiveTopology.LineLoop or + PrimitiveTopology.LineStrip => InputTopology.Lines, + PrimitiveTopology.LinesAdjacency or + PrimitiveTopology.LineStripAdjacency => InputTopology.LinesAdjacency, + PrimitiveTopology.Triangles or + PrimitiveTopology.TriangleStrip or + PrimitiveTopology.TriangleFan => InputTopology.Triangles, + PrimitiveTopology.TrianglesAdjacency or + PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency, + PrimitiveTopology.Patches => tessellationMode.UnpackPatchType() == TessPatchType.Isolines + ? InputTopology.Lines + : InputTopology.Triangles, + _ => InputTopology.Points, + }; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index b33f96c57..fcd953754 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -28,9 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Shader [Flags] private enum QueriedStateFlags { - EarlyZForce = 1 << 0, PrimitiveTopology = 1 << 1, - TessellationMode = 1 << 2, TransformFeedback = 1 << 3, } @@ -264,14 +262,6 @@ namespace Ryujinx.Graphics.Gpu.Shader PipelineState = pipelineState; } - /// <summary> - /// Indicates that the shader accesses the early Z force state. - /// </summary> - public void RecordEarlyZForce() - { - _queriedState |= QueriedStateFlags.EarlyZForce; - } - /// <summary> /// Indicates that the shader accesses the primitive topology state. /// </summary> @@ -280,14 +270,6 @@ namespace Ryujinx.Graphics.Gpu.Shader _queriedState |= QueriedStateFlags.PrimitiveTopology; } - /// <summary> - /// Indicates that the shader accesses the tessellation mode state. - /// </summary> - public void RecordTessellationMode() - { - _queriedState |= QueriedStateFlags.TessellationMode; - } - /// <summary> /// Indicates that the shader accesses the constant buffer use state. /// </summary> diff --git a/src/Ryujinx.Graphics.Shader/AttributeType.cs b/src/Ryujinx.Graphics.Shader/AttributeType.cs index 50a39945b..e6adb4b85 100644 --- a/src/Ryujinx.Graphics.Shader/AttributeType.cs +++ b/src/Ryujinx.Graphics.Shader/AttributeType.cs @@ -13,17 +13,6 @@ namespace Ryujinx.Graphics.Shader static class AttributeTypeExtensions { - public static string ToVec4Type(this AttributeType type) - { - return type switch - { - AttributeType.Float => "vec4", - AttributeType.Sint => "ivec4", - AttributeType.Uint => "uvec4", - _ => throw new ArgumentException($"Invalid attribute type \"{type}\"."), - }; - } - public static AggregateType ToAggregateType(this AttributeType type) { return type switch diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/CodeGenParameters.cs b/src/Ryujinx.Graphics.Shader/CodeGen/CodeGenParameters.cs new file mode 100644 index 000000000..f692c428b --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/CodeGen/CodeGenParameters.cs @@ -0,0 +1,31 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader.CodeGen +{ + readonly struct CodeGenParameters + { + public readonly AttributeUsage AttributeUsage; + public readonly ShaderDefinitions Definitions; + public readonly ShaderProperties Properties; + public readonly HostCapabilities HostCapabilities; + public readonly ILogger Logger; + public readonly TargetApi TargetApi; + + public CodeGenParameters( + AttributeUsage attributeUsage, + ShaderDefinitions definitions, + ShaderProperties properties, + HostCapabilities hostCapabilities, + ILogger logger, + TargetApi targetApi) + { + AttributeUsage = attributeUsage; + Definitions = definitions; + Properties = properties; + HostCapabilities = hostCapabilities; + Logger = logger; + TargetApi = targetApi; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs index 551e5cefa..7506f72af 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs @@ -12,7 +12,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public StructuredProgramInfo Info { get; } - public ShaderConfig Config { 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; } @@ -22,10 +27,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private string _indentation; - public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) + public CodeGenContext(StructuredProgramInfo info, CodeGenParameters parameters) { Info = info; - Config = config; + AttributeUsage = parameters.AttributeUsage; + Definitions = parameters.Definitions; + Properties = parameters.Properties; + HostCapabilities = parameters.HostCapabilities; + Logger = parameters.Logger; + TargetApi = parameters.TargetApi; OperandManager = new OperandManager(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 2a45e23de..e181ae98d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; @@ -13,10 +14,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public static void Declare(CodeGenContext context, StructuredProgramInfo info) { - context.AppendLine(context.Config.Options.TargetApi == TargetApi.Vulkan ? "#version 460 core" : "#version 450 core"); + context.AppendLine(context.TargetApi == TargetApi.Vulkan ? "#version 460 core" : "#version 450 core"); context.AppendLine("#extension GL_ARB_gpu_shader_int64 : enable"); - if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot()) + if (context.HostCapabilities.SupportsShaderBallot) { context.AppendLine("#extension GL_ARB_shader_ballot : enable"); } @@ -30,24 +31,24 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine("#extension GL_EXT_shader_image_load_formatted : enable"); context.AppendLine("#extension GL_EXT_texture_shadow_lod : enable"); - if (context.Config.Stage == ShaderStage.Compute) + if (context.Definitions.Stage == ShaderStage.Compute) { context.AppendLine("#extension GL_ARB_compute_shader : enable"); } - else if (context.Config.Stage == ShaderStage.Fragment) + else if (context.Definitions.Stage == ShaderStage.Fragment) { - if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + if (context.HostCapabilities.SupportsFragmentShaderInterlock) { context.AppendLine("#extension GL_ARB_fragment_shader_interlock : enable"); } - else if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel()) + else if (context.HostCapabilities.SupportsFragmentShaderOrderingIntel) { context.AppendLine("#extension GL_INTEL_fragment_shader_ordering : enable"); } } else { - if (context.Config.Stage == ShaderStage.Vertex) + if (context.Definitions.Stage == ShaderStage.Vertex) { context.AppendLine("#extension GL_ARB_shader_draw_parameters : enable"); } @@ -55,12 +56,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable"); } - if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) + if (context.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough) { context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable"); } - if (context.Config.GpuAccessor.QueryHostSupportsViewportMask()) + if (context.HostCapabilities.SupportsViewportMask) { context.AppendLine("#extension GL_NV_viewport_array2 : enable"); } @@ -71,23 +72,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;"); context.AppendLine(); - DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); - DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); - DeclareMemories(context, context.Config.Properties.LocalMemories.Values, isShared: false); - DeclareMemories(context, context.Config.Properties.SharedMemories.Values, isShared: true); - DeclareSamplers(context, context.Config.Properties.Textures.Values); - DeclareImages(context, context.Config.Properties.Images.Values); + DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values); + DeclareStorageBuffers(context, context.Properties.StorageBuffers.Values); + DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); + DeclareMemories(context, context.Properties.SharedMemories.Values, isShared: true); + DeclareSamplers(context, context.Properties.Textures.Values); + DeclareImages(context, context.Properties.Images.Values); - if (context.Config.Stage != ShaderStage.Compute) + if (context.Definitions.Stage != ShaderStage.Compute) { - if (context.Config.Stage == ShaderStage.Geometry) + if (context.Definitions.Stage == ShaderStage.Geometry) { - InputTopology inputTopology = context.Config.GpuAccessor.QueryPrimitiveTopology(); - string inPrimitive = inputTopology.ToGlslString(); + string inPrimitive = context.Definitions.InputTopology.ToGlslString(); - context.AppendLine($"layout (invocations = {context.Config.ThreadsPerInputPrimitive}, {inPrimitive}) in;"); + context.AppendLine($"layout (invocations = {context.Definitions.ThreadsPerInputPrimitive}, {inPrimitive}) in;"); - if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) + if (context.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough) { context.AppendLine($"layout (passthrough) in gl_PerVertex"); context.EnterScope(); @@ -98,87 +98,75 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - string outPrimitive = context.Config.OutputTopology.ToGlslString(); + string outPrimitive = context.Definitions.OutputTopology.ToGlslString(); - int maxOutputVertices = context.Config.GpPassthrough - ? inputTopology.ToInputVertices() - : context.Config.MaxOutputVertices; + int maxOutputVertices = context.Definitions.GpPassthrough + ? context.Definitions.InputTopology.ToInputVertices() + : context.Definitions.MaxOutputVertices; context.AppendLine($"layout ({outPrimitive}, max_vertices = {maxOutputVertices}) out;"); } context.AppendLine(); } - else if (context.Config.Stage == ShaderStage.TessellationControl) + else if (context.Definitions.Stage == ShaderStage.TessellationControl) { - int threadsPerInputPrimitive = context.Config.ThreadsPerInputPrimitive; + int threadsPerInputPrimitive = context.Definitions.ThreadsPerInputPrimitive; context.AppendLine($"layout (vertices = {threadsPerInputPrimitive}) out;"); context.AppendLine(); } - else if (context.Config.Stage == ShaderStage.TessellationEvaluation) + else if (context.Definitions.Stage == ShaderStage.TessellationEvaluation) { - bool tessCw = context.Config.GpuAccessor.QueryTessCw(); + bool tessCw = context.Definitions.TessCw; - if (context.Config.Options.TargetApi == TargetApi.Vulkan) + if (context.TargetApi == TargetApi.Vulkan) { // We invert the front face on Vulkan backend, so we need to do that here aswell. tessCw = !tessCw; } - string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl(); - string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl(); + string patchType = context.Definitions.TessPatchType.ToGlsl(); + string spacing = context.Definitions.TessSpacing.ToGlsl(); string windingOrder = tessCw ? "cw" : "ccw"; context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;"); context.AppendLine(); } - if (context.Config.UsedInputAttributes != 0 || context.Config.GpPassthrough) + static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) { - DeclareInputAttributes(context, info); - - context.AppendLine(); + return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; } - if (context.Config.UsedOutputAttributes != 0 || context.Config.Stage != ShaderStage.Fragment) + static bool IsUserDefinedOutput(ShaderStage stage, IoDefinition ioDefinition) { - DeclareOutputAttributes(context, info); - - context.AppendLine(); + IoVariable ioVariable = stage == ShaderStage.Fragment ? IoVariable.FragmentOutputColor : IoVariable.UserDefined; + return ioDefinition.StorageKind == StorageKind.Output && ioDefinition.IoVariable == ioVariable; } - if (context.Config.UsedInputAttributesPerPatch.Count != 0) + DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); + DeclareOutputAttributes(context, info.IoDefinitions.Where(x => IsUserDefinedOutput(context.Definitions.Stage, x))); + DeclareInputAttributesPerPatch(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.InputPerPatch))); + DeclareOutputAttributesPerPatch(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.OutputPerPatch))); + + if (context.Definitions.TransformFeedbackEnabled && context.Definitions.LastInVertexPipeline) { - DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch); - - context.AppendLine(); - } - - if (context.Config.UsedOutputAttributesPerPatch.Count != 0) - { - DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch); - - context.AppendLine(); - } - - if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) - { - var tfOutput = context.Config.GetTransformFeedbackOutput(AttributeConsts.PositionX); + var tfOutput = context.Definitions.GetTransformFeedbackOutput(AttributeConsts.PositionX); if (tfOutput.Valid) { context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex"); context.EnterScope(); context.AppendLine("vec4 gl_Position;"); - context.LeaveScope(context.Config.Stage == ShaderStage.TessellationControl ? " gl_out[];" : ";"); + context.LeaveScope(context.Definitions.Stage == ShaderStage.TessellationControl ? " gl_out[];" : ";"); } } } else { - string localSizeX = NumberFormatter.FormatInt(context.Config.GpuAccessor.QueryComputeLocalSizeX()); - string localSizeY = NumberFormatter.FormatInt(context.Config.GpuAccessor.QueryComputeLocalSizeY()); - string localSizeZ = NumberFormatter.FormatInt(context.Config.GpuAccessor.QueryComputeLocalSizeZ()); + string localSizeX = NumberFormatter.FormatInt(context.Definitions.ComputeLocalSizeX); + string localSizeY = NumberFormatter.FormatInt(context.Definitions.ComputeLocalSizeY); + string localSizeZ = NumberFormatter.FormatInt(context.Definitions.ComputeLocalSizeZ); context.AppendLine( "layout (" + @@ -188,15 +176,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - if (context.Config.Stage == ShaderStage.Fragment) + if (context.Definitions.Stage == ShaderStage.Fragment) { - if (context.Config.GpuAccessor.QueryEarlyZForce()) + if (context.Definitions.EarlyZForce) { context.AppendLine("layout (early_fragment_tests) in;"); context.AppendLine(); } - if (context.Config.Properties.OriginUpperLeft) + if (context.Definitions.OriginUpperLeft) { context.AppendLine("layout (origin_upper_left) in vec4 gl_FragCoord;"); context.AppendLine(); @@ -251,7 +239,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool precise = true) { - if (context.Config.GpuAccessor.QueryHostReducedPrecision()) + if (context.HostCapabilities.ReducedPrecision) { precise = false; } @@ -305,7 +293,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string set = string.Empty; - if (context.Config.Options.TargetApi == TargetApi.Vulkan) + if (context.TargetApi == TargetApi.Vulkan) { set = $"set = {buffer.Set}, "; } @@ -390,7 +378,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string layout = string.Empty; - if (context.Config.Options.TargetApi == TargetApi.Vulkan) + if (context.TargetApi == TargetApi.Vulkan) { layout = $", set = {definition.Set}"; } @@ -435,7 +423,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl layout = ", " + layout; } - if (context.Config.Options.TargetApi == TargetApi.Vulkan) + if (context.TargetApi == TargetApi.Vulkan) { layout = $", set = {definition.Set}{layout}"; } @@ -444,42 +432,50 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) + private static void DeclareInputAttributes(CodeGenContext context, IEnumerable<IoDefinition> inputs) { - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) + if (context.Definitions.IaIndexing) { - string suffix = context.Config.Stage == ShaderStage.Geometry ? "[]" : string.Empty; + string suffix = context.Definitions.Stage == ShaderStage.Geometry ? "[]" : string.Empty; context.AppendLine($"layout (location = 0) in vec4 {DefaultNames.IAttributePrefix}{suffix}[{Constants.MaxAttributes}];"); + context.AppendLine(); } else { - int usedAttributes = context.Config.UsedInputAttributes | context.Config.PassthroughAttributes; - while (usedAttributes != 0) + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { - int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareInputAttribute(context, info, index); - usedAttributes &= ~(1 << index); + DeclareInputAttribute(context, ioDefinition.Location, ioDefinition.Component); + } + + if (inputs.Any()) + { + context.AppendLine(); } } } - private static void DeclareInputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs) + private static void DeclareInputAttributesPerPatch(CodeGenContext context, IEnumerable<IoDefinition> inputs) { - foreach (int attr in attrs.Order()) + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) { - DeclareInputAttributePerPatch(context, attr); + DeclareInputAttributePerPatch(context, ioDefinition.Location); + } + + if (inputs.Any()) + { + context.AppendLine(); } } - private static void DeclareInputAttribute(CodeGenContext context, StructuredProgramInfo info, int attr) + private static void DeclareInputAttribute(CodeGenContext context, int location, int component) { - string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty; + string suffix = IsArrayAttributeGlsl(context.Definitions.Stage, isOutAttr: false) ? "[]" : string.Empty; string iq = string.Empty; - if (context.Config.Stage == ShaderStage.Fragment) + if (context.Definitions.Stage == ShaderStage.Fragment) { - iq = context.Config.ImapTypes[attr].GetFirstUsedType() switch + iq = context.Definitions.ImapTypes[location].GetFirstUsedType() switch { PixelImap.Constant => "flat ", PixelImap.ScreenLinear => "noperspective ", @@ -487,14 +483,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl }; } - string name = $"{DefaultNames.IAttributePrefix}{attr}"; + string name = $"{DefaultNames.IAttributePrefix}{location}"; - if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment) + if (context.Definitions.TransformFeedbackEnabled && context.Definitions.Stage == ShaderStage.Fragment) { - int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0); + bool hasComponent = context.Definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput: false); - if (components > 1) + if (hasComponent) { + char swzMask = "xyzw"[component]; + + context.AppendLine($"layout (location = {location}, component = {component}) {iq}in float {name}_{swzMask}{suffix};"); + } + else + { + int components = context.Definitions.GetTransformFeedbackOutputComponents(location, 0); + string type = components switch { 2 => "vec2", @@ -503,85 +507,89 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl _ => "float", }; - context.AppendLine($"layout (location = {attr}) in {type} {name};"); - } - - for (int c = components > 1 ? components : 0; c < 4; c++) - { - char swzMask = "xyzw"[c]; - - context.AppendLine($"layout (location = {attr}, component = {c}) {iq}in float {name}_{swzMask}{suffix};"); + context.AppendLine($"layout (location = {location}) in {type} {name};"); } } else { - bool passthrough = (context.Config.PassthroughAttributes & (1 << attr)) != 0; - string pass = passthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough() ? "passthrough, " : string.Empty; - string type; + bool passthrough = (context.AttributeUsage.PassthroughAttributes & (1 << location)) != 0; + string pass = passthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough ? "passthrough, " : string.Empty; + string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(location, isOutput: false), false); - if (context.Config.Stage == ShaderStage.Vertex) - { - type = context.Config.GpuAccessor.QueryAttributeType(attr).ToVec4Type(); - } - else - { - type = AttributeType.Float.ToVec4Type(); - } - - context.AppendLine($"layout ({pass}location = {attr}) {iq}in {type} {name}{suffix};"); + context.AppendLine($"layout ({pass}location = {location}) {iq}in {type} {name}{suffix};"); } } - private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr) + private static void DeclareInputAttributePerPatch(CodeGenContext context, int patchLocation) { - int location = context.Config.GetPerPatchAttributeLocation(attr); - string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; + int location = context.AttributeUsage.GetPerPatchAttributeLocation(patchLocation); + string name = $"{DefaultNames.PerPatchAttributePrefix}{patchLocation}"; context.AppendLine($"layout (location = {location}) patch in vec4 {name};"); } - private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) + private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable<IoDefinition> outputs) { - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing)) + if (context.Definitions.OaIndexing) { context.AppendLine($"layout (location = 0) out vec4 {DefaultNames.OAttributePrefix}[{Constants.MaxAttributes}];"); + context.AppendLine(); } else { - int usedAttributes = context.Config.UsedOutputAttributes; + outputs = outputs.OrderBy(x => x.Location); - if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) + if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) { - int firstOutput = BitOperations.TrailingZeroCount(usedAttributes); - int mask = 3 << firstOutput; + IoDefinition firstOutput = outputs.ElementAtOrDefault(0); + IoDefinition secondOutput = outputs.ElementAtOrDefault(1); - if ((usedAttributes & mask) == mask) + if (firstOutput.Location + 1 == secondOutput.Location) { - usedAttributes &= ~mask; - DeclareOutputDualSourceBlendAttribute(context, firstOutput); + DeclareOutputDualSourceBlendAttribute(context, firstOutput.Location); + outputs = outputs.Skip(2); } } - while (usedAttributes != 0) + foreach (var ioDefinition in outputs) { - int index = BitOperations.TrailingZeroCount(usedAttributes); - DeclareOutputAttribute(context, index); - usedAttributes &= ~(1 << index); + DeclareOutputAttribute(context, ioDefinition.Location, ioDefinition.Component); + } + + if (outputs.Any()) + { + context.AppendLine(); } } } - private static void DeclareOutputAttribute(CodeGenContext context, int attr) + private static void DeclareOutputAttribute(CodeGenContext context, int location, int component) { - string suffix = IsArrayAttributeGlsl(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty; - string name = $"{DefaultNames.OAttributePrefix}{attr}{suffix}"; + string suffix = IsArrayAttributeGlsl(context.Definitions.Stage, isOutAttr: true) ? "[]" : string.Empty; + string name = $"{DefaultNames.OAttributePrefix}{location}{suffix}"; - if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) + if (context.Definitions.TransformFeedbackEnabled && context.Definitions.LastInVertexPipeline) { - int components = context.Config.GetTransformFeedbackOutputComponents(attr, 0); + bool hasComponent = context.Definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput: true); - if (components > 1) + if (hasComponent) { + char swzMask = "xyzw"[component]; + + string xfb = string.Empty; + + var tfOutput = context.Definitions.GetTransformFeedbackOutput(location, component); + if (tfOutput.Valid) + { + xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; + } + + context.AppendLine($"layout (location = {location}, component = {component}{xfb}) out float {name}_{swzMask};"); + } + else + { + int components = context.Definitions.GetTransformFeedbackOutputComponents(location, 0); + string type = components switch { 2 => "vec2", @@ -592,58 +600,59 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string xfb = string.Empty; - var tfOutput = context.Config.GetTransformFeedbackOutput(attr, 0); + var tfOutput = context.Definitions.GetTransformFeedbackOutput(location, 0); if (tfOutput.Valid) { xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; } - context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};"); - } - - for (int c = components > 1 ? components : 0; c < 4; c++) - { - char swzMask = "xyzw"[c]; - - string xfb = string.Empty; - - var tfOutput = context.Config.GetTransformFeedbackOutput(attr, c); - if (tfOutput.Valid) - { - xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; - } - - context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};"); + context.AppendLine($"layout (location = {location}{xfb}) out {type} {name};"); } } else { - string type = context.Config.Stage != ShaderStage.Fragment ? "vec4" : - context.Config.GpuAccessor.QueryFragmentOutputType(attr) switch - { - AttributeType.Sint => "ivec4", - AttributeType.Uint => "uvec4", - _ => "vec4", - }; + string type = context.Definitions.Stage != ShaderStage.Fragment ? "vec4" : + GetVarTypeName(context, context.Definitions.GetFragmentOutputColorType(location), false); - if (context.Config.GpuAccessor.QueryHostReducedPrecision() && context.Config.Stage == ShaderStage.Vertex && attr == 0) + if (context.HostCapabilities.ReducedPrecision && context.Definitions.Stage == ShaderStage.Vertex && location == 0) { - context.AppendLine($"layout (location = {attr}) invariant out {type} {name};"); + context.AppendLine($"layout (location = {location}) invariant out {type} {name};"); } else { - context.AppendLine($"layout (location = {attr}) out {type} {name};"); + context.AppendLine($"layout (location = {location}) out {type} {name};"); } } } - private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr) + private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int location) { - string name = $"{DefaultNames.OAttributePrefix}{attr}"; - string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}"; + string name = $"{DefaultNames.OAttributePrefix}{location}"; + string name2 = $"{DefaultNames.OAttributePrefix}{(location + 1)}"; - context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};"); - context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};"); + context.AppendLine($"layout (location = {location}, index = 0) out vec4 {name};"); + context.AppendLine($"layout (location = {location}, index = 1) out vec4 {name2};"); + } + + private static void DeclareOutputAttributesPerPatch(CodeGenContext context, IEnumerable<IoDefinition> outputs) + { + foreach (var ioDefinition in outputs) + { + DeclareOutputAttributePerPatch(context, ioDefinition.Location); + } + + if (outputs.Any()) + { + context.AppendLine(); + } + } + + private static void DeclareOutputAttributePerPatch(CodeGenContext context, int patchLocation) + { + int location = context.AttributeUsage.GetPerPatchAttributeLocation(patchLocation); + string name = $"{DefaultNames.PerPatchAttributePrefix}{patchLocation}"; + + context.AppendLine($"layout (location = {location}) patch out vec4 {name};"); } private static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr) @@ -660,29 +669,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs) - { - foreach (int attr in attrs.Order()) - { - DeclareOutputAttributePerPatch(context, attr); - } - } - - private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr) - { - int location = context.Config.GetPerPatchAttributeLocation(attr); - string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}"; - - context.AppendLine($"layout (location = {location}) patch out vec4 {name};"); - } - private static void AppendHelperFunction(CodeGenContext context, string filename) { string code = EmbeddedResources.ReadAllText(filename); code = code.Replace("\t", CodeGenContext.Tab); - if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot()) + if (context.HostCapabilities.SupportsShaderBallot) { code = code.Replace("$SUBGROUP_INVOCATION$", "gl_SubGroupInvocationARB"); code = code.Replace("$SUBGROUP_BROADCAST$", "readInvocationARB"); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 0140c1b93..469c4f0a2 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { private const string MainFunctionName = "main"; - public static string Generate(StructuredProgramInfo info, ShaderConfig config) + public static string Generate(StructuredProgramInfo info, CodeGenParameters parameters) { - CodeGenContext context = new(info, config); + CodeGenContext context = new(info, parameters); Declarations.Declare(context, info); @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } }; - bool supportsBarrierDivergence = context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence(); + bool supportsBarrierDivergence = context.HostCapabilities.SupportsShaderBarrierDivergence; bool mayHaveReturned = false; foreach (IAstNode node in visitor.Visit()) @@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl // so skip emitting the barrier for those cases. if (visitor.Block.Type != AstBlockType.Main || mayHaveReturned || !isMainFunction) { - context.Config.GpuAccessor.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); + context.Logger.Log("Shader has barrier on potentially divergent block, the barrier will be removed."); continue; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs index 9a2bfef0c..b44759c0d 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string arg = GetSoureExpr(context, operation.GetSource(0), dstType); - if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot()) + if (context.HostCapabilities.SupportsShaderBallot) { return $"unpackUint2x32(ballotARB({arg})).x"; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs index a3d68028f..1697aa47e 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenFSI.cs @@ -4,11 +4,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { public static string FSIBegin(CodeGenContext context) { - if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + if (context.HostCapabilities.SupportsFragmentShaderInterlock) { return "beginInvocationInterlockARB()"; } - else if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel()) + else if (context.HostCapabilities.SupportsFragmentShaderOrderingIntel) { return "beginFragmentShaderOrderingINTEL()"; } @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions public static string FSIEnd(CodeGenContext context) { - if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + if (context.HostCapabilities.SupportsFragmentShaderInterlock) { return "endInvocationInterlockARB()"; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 7e6d8bb5c..a1f92d11a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = Src(AggregateType.S32); } - string imageName = GetImageName(context.Config, texOp, indexExpr); + string imageName = GetImageName(context.Properties, texOp, indexExpr); texCallBuilder.Append('('); texCallBuilder.Append(imageName); @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); } - string samplerName = GetSamplerName(context.Config, texOp, indexExpr); + string samplerName = GetSamplerName(context.Properties, texOp, indexExpr); int coordsIndex = isBindless || isIndexed ? 1 : 0; @@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // 2D Array and Cube shadow samplers with LOD level or bias requires an extension. // If the extension is not supported, just remove the LOD parameter. - if (isArray && isShadow && (is2D || isCube) && !context.Config.GpuAccessor.QueryHostSupportsTextureShadowLod()) + if (isArray && isShadow && (is2D || isCube) && !context.HostCapabilities.SupportsTextureShadowLod) { hasLodBias = false; hasLodLevel = false; @@ -281,7 +281,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // Cube shadow samplers with LOD level requires an extension. // If the extension is not supported, just remove the LOD level parameter. - if (isShadow && isCube && !context.Config.GpuAccessor.QueryHostSupportsTextureShadowLod()) + if (isShadow && isCube && !context.HostCapabilities.SupportsTextureShadowLod) { hasLodLevel = false; } @@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = Src(AggregateType.S32); } - string samplerName = GetSamplerName(context.Config, texOp, indexExpr); + string samplerName = GetSamplerName(context.Properties, texOp, indexExpr); texCall += "(" + samplerName; @@ -538,7 +538,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); } - string samplerName = GetSamplerName(context.Config, texOp, indexExpr); + string samplerName = GetSamplerName(context.Properties, texOp, indexExpr); if (texOp.Index == 3) { @@ -546,7 +546,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else { - context.Config.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); + context.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; string texCall; @@ -593,8 +593,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions int binding = bindingIndex.Value; BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer - ? context.Config.Properties.ConstantBuffers[binding] - : context.Config.Properties.StorageBuffers[binding]; + ? context.Properties.ConstantBuffers[binding] + : context.Properties.StorageBuffers[binding]; if (operation.GetSource(srcIndex++) is not AstOperand fieldIndex || fieldIndex.Type != OperandType.Constant) { @@ -614,8 +614,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } MemoryDefinition memory = storageKind == StorageKind.LocalMemory - ? context.Config.Properties.LocalMemories[bindingId.Value] - : context.Config.Properties.SharedMemories[bindingId.Value]; + ? context.Properties.LocalMemories[bindingId.Value] + : context.Properties.SharedMemories[bindingId.Value]; varName = memory.Name; varType = memory.Type; @@ -636,7 +636,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions int location = -1; int component = 0; - if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) { if (operation.GetSource(srcIndex++) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) { @@ -648,16 +648,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (operation.SourcesCount > srcIndex && operation.GetSource(srcIndex) is AstOperand elemIndex && elemIndex.Type == OperandType.Constant && - context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) { component = elemIndex.Value; srcIndex++; } } - (varName, varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch); + (varName, varType) = IoMap.GetGlslVariable( + context.Definitions, + context.HostCapabilities, + ioVariable, + location, + component, + isOutput, + isPerPatch); - if (IoMap.IsPerVertexBuiltIn(context.Config.Stage, ioVariable, isOutput)) + if (IoMap.IsPerVertexBuiltIn(context.Definitions.Stage, ioVariable, isOutput)) { // Since those exist both as input and output on geometry and tessellation shaders, // we need the gl_in and gl_out prefixes to disambiguate. @@ -692,7 +699,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { varName += "." + "xyzw"[elementIndex.Value & 3]; } - else if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output) + else if (srcIndex == firstSrcIndex && context.Definitions.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output) { // GLSL requires that for tessellation control shader outputs, // that the index expression must be *exactly* "gl_InvocationID", @@ -715,9 +722,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return varName; } - private static string GetSamplerName(ShaderConfig config, AstTextureOperation texOp, string indexExpr) + private static string GetSamplerName(ShaderProperties resourceDefinitions, AstTextureOperation texOp, string indexExpr) { - string name = config.Properties.Textures[texOp.Binding].Name; + string name = resourceDefinitions.Textures[texOp.Binding].Name; if (texOp.Type.HasFlag(SamplerType.Indexed)) { @@ -727,9 +734,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return name; } - private static string GetImageName(ShaderConfig config, AstTextureOperation texOp, string indexExpr) + private static string GetImageName(ShaderProperties resourceDefinitions, AstTextureOperation texOp, string indexExpr) { - string name = config.Properties.Images[texOp.Binding].Name; + string name = resourceDefinitions.Images[texOp.Binding].Name; if (texOp.Type.HasFlag(SamplerType.Indexed)) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs index 3f88d2b32..b5f453aef 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs @@ -7,7 +7,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions static class IoMap { public static (string, AggregateType) GetGlslVariable( - ShaderConfig config, + ShaderDefinitions definitions, + HostCapabilities hostCapabilities, IoVariable ioVariable, int location, int component, @@ -25,7 +26,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions IoVariable.DrawIndex => ("gl_DrawIDARB", AggregateType.S32), IoVariable.FogCoord => ("gl_FogFragCoord", AggregateType.FP32), // Deprecated. IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32), - IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location), + IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(definitions, location), IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32), IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated. @@ -38,20 +39,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions IoVariable.PointCoord => ("gl_PointCoord", AggregateType.Vector2 | AggregateType.FP32), IoVariable.PointSize => ("gl_PointSize", AggregateType.FP32), IoVariable.Position => ("gl_Position", AggregateType.Vector4 | AggregateType.FP32), - IoVariable.PrimitiveId => GetPrimitiveIdVariableName(config.Stage, isOutput), - IoVariable.SubgroupEqMask => GetSubgroupMaskVariableName(config, "Eq"), - IoVariable.SubgroupGeMask => GetSubgroupMaskVariableName(config, "Ge"), - IoVariable.SubgroupGtMask => GetSubgroupMaskVariableName(config, "Gt"), - IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config), - IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"), - IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"), + IoVariable.PrimitiveId => GetPrimitiveIdVariableName(definitions.Stage, isOutput), + IoVariable.SubgroupEqMask => GetSubgroupMaskVariableName(hostCapabilities.SupportsShaderBallot, "Eq"), + IoVariable.SubgroupGeMask => GetSubgroupMaskVariableName(hostCapabilities.SupportsShaderBallot, "Ge"), + IoVariable.SubgroupGtMask => GetSubgroupMaskVariableName(hostCapabilities.SupportsShaderBallot, "Gt"), + IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(hostCapabilities.SupportsShaderBallot), + IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(hostCapabilities.SupportsShaderBallot, "Le"), + IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(hostCapabilities.SupportsShaderBallot, "Lt"), IoVariable.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32), IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32), IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", AggregateType.Array | AggregateType.FP32), IoVariable.TextureCoord => ("gl_TexCoord", AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32), // Deprecated. IoVariable.ThreadId => ("gl_LocalInvocationID", AggregateType.Vector3 | AggregateType.U32), IoVariable.ThreadKill => ("gl_HelperInvocation", AggregateType.Bool), - IoVariable.UserDefined => GetUserDefinedVariableName(config, location, component, isOutput, isPerPatch), + IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.VertexId => ("gl_VertexID", AggregateType.S32), IoVariable.VertexIndex => ("gl_VertexIndex", AggregateType.S32), IoVariable.ViewportIndex => ("gl_ViewportIndex", AggregateType.S32), @@ -86,16 +87,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return false; } - private static (string, AggregateType) GetFragmentOutputColorVariableName(ShaderConfig config, int location) + private static (string, AggregateType) GetFragmentOutputColorVariableName(ShaderDefinitions definitions, int location) { if (location < 0) { - return (DefaultNames.OAttributePrefix, config.GetFragmentOutputColorType(0)); + return (DefaultNames.OAttributePrefix, definitions.GetFragmentOutputColorType(0)); } string name = DefaultNames.OAttributePrefix + location.ToString(CultureInfo.InvariantCulture); - return (name, config.GetFragmentOutputColorType(location)); + return (name, definitions.GetFragmentOutputColorType(location)); } private static (string, AggregateType) GetPrimitiveIdVariableName(ShaderStage stage, bool isOutput) @@ -104,21 +105,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return (isOutput || stage != ShaderStage.Geometry ? "gl_PrimitiveID" : "gl_PrimitiveIDIn", AggregateType.S32); } - private static (string, AggregateType) GetSubgroupMaskVariableName(ShaderConfig config, string cc) + private static (string, AggregateType) GetSubgroupMaskVariableName(bool supportsShaderBallot, string cc) { - return config.GpuAccessor.QueryHostSupportsShaderBallot() + return supportsShaderBallot ? ($"unpackUint2x32(gl_SubGroup{cc}MaskARB)", AggregateType.Vector2 | AggregateType.U32) : ($"gl_Subgroup{cc}Mask", AggregateType.Vector4 | AggregateType.U32); } - private static (string, AggregateType) GetSubgroupInvocationIdVariableName(ShaderConfig config) + private static (string, AggregateType) GetSubgroupInvocationIdVariableName(bool supportsShaderBallot) { - return config.GpuAccessor.QueryHostSupportsShaderBallot() + return supportsShaderBallot ? ("gl_SubGroupInvocationARB", AggregateType.U32) : ("gl_SubgroupInvocationID", AggregateType.U32); } - private static (string, AggregateType) GetUserDefinedVariableName(ShaderConfig config, int location, int component, bool isOutput, bool isPerPatch) + private static (string, AggregateType) GetUserDefinedVariableName(ShaderDefinitions definitions, int location, int component, bool isOutput, bool isPerPatch) { string name = isPerPatch ? DefaultNames.PerPatchAttributePrefix @@ -126,17 +127,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (location < 0) { - return (name, config.GetUserDefinedType(0, isOutput)); + return (name, definitions.GetUserDefinedType(0, isOutput)); } name += location.ToString(CultureInfo.InvariantCulture); - if (config.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput)) + if (definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput)) { name += "_" + "xyzw"[component & 3]; } - return (name, config.GetUserDefinedType(location, isOutput)); + return (name, definitions.GetUserDefinedType(location, isOutput)); } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 9346341f8..53ecc4531 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -68,8 +68,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer - ? context.Config.Properties.ConstantBuffers[bindingIndex.Value] - : context.Config.Properties.StorageBuffers[bindingIndex.Value]; + ? context.Properties.ConstantBuffers[bindingIndex.Value] + : context.Properties.StorageBuffers[bindingIndex.Value]; StructureField field = buffer.Type.Fields[fieldIndex.Value]; return field.Type & AggregateType.ElementTypeMask; @@ -82,8 +82,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory - ? context.Config.Properties.LocalMemories[bindingId.Value] - : context.Config.Properties.SharedMemories[bindingId.Value]; + ? context.Properties.LocalMemories[bindingId.Value] + : context.Properties.SharedMemories[bindingId.Value]; return memory.Type & AggregateType.ElementTypeMask; @@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl int location = 0; int component = 0; - if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) { if (operation.GetSource(1) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) { @@ -114,13 +114,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (operation.SourcesCount > 2 && operation.GetSource(2) is AstOperand elemIndex && elemIndex.Type == OperandType.Constant && - context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) { component = elemIndex.Value; } } - (_, AggregateType varType) = IoMap.GetGlslVariable(context.Config, ioVariable, location, component, isOutput, isPerPatch); + (_, AggregateType varType) = IoMap.GetGlslVariable( + context.Definitions, + context.HostCapabilities, + ioVariable, + location, + component, + isOutput, + isPerPatch); return varType & AggregateType.ElementTypeMask; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index d4f490458..d385782af 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -20,21 +20,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public StructuredProgramInfo Info { get; } - public ShaderConfig Config { 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 int InputVertices { get; } - public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>(); - public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>(); - public Dictionary<int, Instruction> LocalMemories { get; } = new Dictionary<int, Instruction>(); - public Dictionary<int, Instruction> SharedMemories { get; } = new Dictionary<int, Instruction>(); - public Dictionary<int, SamplerType> SamplersTypes { get; } = new Dictionary<int, SamplerType>(); - public Dictionary<int, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<int, (Instruction, Instruction, Instruction)>(); - public Dictionary<int, (Instruction, Instruction)> Images { get; } = new Dictionary<int, (Instruction, Instruction)>(); - public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>(); - public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>(); - public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); - public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); + public Dictionary<int, Instruction> ConstantBuffers { get; } = new(); + public Dictionary<int, Instruction> StorageBuffers { get; } = new(); + + public Dictionary<int, Instruction> LocalMemories { get; } = new(); + public Dictionary<int, Instruction> SharedMemories { get; } = new(); + + public Dictionary<int, SamplerType> SamplersTypes { get; } = new(); + public Dictionary<int, (Instruction, Instruction, Instruction)> Samplers { get; } = new(); + public Dictionary<int, (Instruction, Instruction)> Images { get; } = new(); + + public Dictionary<IoDefinition, Instruction> Inputs { get; } = new(); + public Dictionary<IoDefinition, Instruction> Outputs { get; } = new(); + public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new(); + public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new(); public StructuredFunction CurrentFunction { get; set; } private readonly Dictionary<AstOperand, Instruction> _locals = new(); @@ -81,25 +89,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public CodeGenContext( StructuredProgramInfo info, - ShaderConfig config, + CodeGenParameters parameters, GeneratorPool<Instruction> instPool, GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool) { Info = info; - Config = config; + AttributeUsage = parameters.AttributeUsage; + Definitions = parameters.Definitions; + Properties = parameters.Properties; + HostCapabilities = parameters.HostCapabilities; + Logger = parameters.Logger; + TargetApi = parameters.TargetApi; - if (config.Stage == ShaderStage.Geometry) + if (parameters.Definitions.Stage == ShaderStage.Geometry) { - InputTopology inPrimitive = config.GpuAccessor.QueryPrimitiveTopology(); - - InputVertices = inPrimitive switch + InputVertices = parameters.Definitions.InputTopology switch { InputTopology.Points => 1, InputTopology.Lines => 2, InputTopology.LinesAdjacency => 2, InputTopology.Triangles => 3, InputTopology.TrianglesAdjacency => 3, - _ => throw new InvalidOperationException($"Invalid input topology \"{inPrimitive}\"."), + _ => throw new InvalidOperationException($"Invalid input topology \"{parameters.Definitions.InputTopology}\"."), }; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index b14fead8a..f3a083c3b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -5,7 +5,6 @@ using Spv.Generator; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Numerics; using static Spv.Specification; using SpvInstruction = Spv.Generator.Instruction; @@ -66,12 +65,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info) { - DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values); - DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); - DeclareMemories(context, context.Config.Properties.LocalMemories, context.LocalMemories, StorageClass.Private); - DeclareMemories(context, context.Config.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup); - DeclareSamplers(context, context.Config.Properties.Textures.Values); - DeclareImages(context, context.Config.Properties.Images.Values); + DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values); + DeclareStorageBuffers(context, context.Properties.StorageBuffers.Values); + DeclareMemories(context, context.Properties.LocalMemories, context.LocalMemories, StorageClass.Private); + DeclareMemories(context, context.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup); + DeclareSamplers(context, context.Properties.Textures.Values); + DeclareImages(context, context.Properties.Images.Values); DeclareInputsAndOutputs(context, info); } @@ -108,7 +107,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv foreach (BufferDefinition buffer in buffers) { - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? buffer.Set : 0; + int setIndex = context.TargetApi == TargetApi.Vulkan ? buffer.Set : 0; int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4; int alignmentMask = alignment - 1; int offset = 0; @@ -181,7 +180,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { foreach (var sampler in samplers) { - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? sampler.Set : 0; + int setIndex = context.TargetApi == TargetApi.Vulkan ? sampler.Set : 0; var dim = (sampler.Type & SamplerType.Mask) switch { @@ -220,7 +219,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { foreach (var image in images) { - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? image.Set : 0; + int setIndex = context.TargetApi == TargetApi.Vulkan ? image.Set : 0; var dim = GetDim(image.Type); @@ -314,16 +313,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static void DeclareInputsAndOutputs(CodeGenContext context, StructuredProgramInfo info) { + int firstLocation = int.MaxValue; + + if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) + { + foreach (var ioDefinition in info.IoDefinitions) + { + if (ioDefinition.IoVariable == IoVariable.FragmentOutputColor && ioDefinition.Location < firstLocation) + { + firstLocation = ioDefinition.Location; + } + } + } + foreach (var ioDefinition in info.IoDefinitions) { PixelImap iq = PixelImap.Unused; - if (context.Config.Stage == ShaderStage.Fragment) + if (context.Definitions.Stage == ShaderStage.Fragment) { var ioVariable = ioDefinition.IoVariable; if (ioVariable == IoVariable.UserDefined) { - iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType(); + iq = context.Definitions.ImapTypes[ioDefinition.Location].GetFirstUsedType(); } else { @@ -340,11 +352,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv bool isOutput = ioDefinition.StorageKind.IsOutput(); bool isPerPatch = ioDefinition.StorageKind.IsPerPatch(); - DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq); + DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq, firstLocation); } } - private static void DeclareInputOrOutput(CodeGenContext context, IoDefinition ioDefinition, bool isOutput, bool isPerPatch, PixelImap iq = PixelImap.Unused) + private static void DeclareInputOrOutput( + CodeGenContext context, + IoDefinition ioDefinition, + bool isOutput, + bool isPerPatch, + PixelImap iq = PixelImap.Unused, + int firstLocation = 0) { IoVariable ioVariable = ioDefinition.IoVariable; var storageClass = isOutput ? StorageClass.Output : StorageClass.Input; @@ -355,12 +373,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (ioVariable == IoVariable.UserDefined) { - varType = context.Config.GetUserDefinedType(ioDefinition.Location, isOutput); + varType = context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput); isBuiltIn = false; } else if (ioVariable == IoVariable.FragmentOutputColor) { - varType = context.Config.GetFragmentOutputColorType(ioDefinition.Location); + varType = context.Definitions.GetFragmentOutputColorType(ioDefinition.Location); isBuiltIn = false; } else @@ -374,16 +392,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } } - bool hasComponent = context.Config.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput); + bool hasComponent = context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput); if (hasComponent) { varType &= AggregateType.ElementTypeMask; } - else if (ioVariable == IoVariable.UserDefined && context.Config.HasTransformFeedbackOutputs(isOutput)) + else if (ioVariable == IoVariable.UserDefined && context.Definitions.HasTransformFeedbackOutputs(isOutput)) { varType &= AggregateType.ElementTypeMask; - varType |= context.Config.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch + varType |= context.Definitions.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch { 2 => AggregateType.Vector2, 3 => AggregateType.Vector3, @@ -395,20 +413,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var spvType = context.GetType(varType, IoMap.GetSpirvBuiltInArrayLength(ioVariable)); bool builtInPassthrough = false; - if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Config.Stage, isOutput)) + if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Definitions.Stage, isOutput)) { - int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32; + int arraySize = context.Definitions.Stage == ShaderStage.Geometry ? context.InputVertices : 32; spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), arraySize)); - if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) + if (context.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough) { builtInPassthrough = true; } } - if (context.Config.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch) + if (context.Definitions.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch) { - spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive)); + spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Definitions.ThreadsPerInputPrimitive)); } var spvPointerType = context.TypePointer(storageClass, spvType); @@ -426,7 +444,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.Decorate(spvVar, Decoration.Patch); } - if (context.Config.GpuAccessor.QueryHostReducedPrecision() && ioVariable == IoVariable.Position) + if (context.HostCapabilities.ReducedPrecision && ioVariable == IoVariable.Position) { context.Decorate(spvVar, Decoration.Invariant); } @@ -439,7 +457,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (ioVariable == IoVariable.UserDefined) { - int location = context.Config.GetPerPatchAttributeLocation(ioDefinition.Location); + int location = context.AttributeUsage.GetPerPatchAttributeLocation(ioDefinition.Location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); } @@ -455,8 +473,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (!isOutput && !isPerPatch && - (context.Config.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 && - context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) + (context.AttributeUsage.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 && + context.HostCapabilities.SupportsGeometryShaderPassthrough) { context.Decorate(spvVar, Decoration.PassthroughNV); } @@ -465,13 +483,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { int location = ioDefinition.Location; - if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) + if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) { - int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes); int index = location - firstLocation; - int mask = 3 << firstLocation; - if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask) + if ((uint)index < 2) { context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation); context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index); @@ -499,7 +515,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } } - else if (context.Config.TryGetTransformFeedbackOutput( + else if (context.Definitions.TryGetTransformFeedbackOutput( ioVariable, ioDefinition.Location, ioDefinition.Component, diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 9489397bc..30ce7f8ba 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -240,10 +240,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { // Barrier on divergent control flow paths may cause the GPU to hang, // so skip emitting the barrier for those cases. - if (!context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence() && + if (!context.HostCapabilities.SupportsShaderBarrierDivergence && (context.CurrentBlock.Type != AstBlockType.Main || context.MayHaveReturned || !context.IsMainFunction)) { - context.Config.GpuAccessor.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); + context.Logger.Log("Shader has barrier on potentially divergent block, the barrier will be removed."); return OperationResult.Invalid; } @@ -546,7 +546,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateFSIBegin(CodeGenContext context, AstOperation operation) { - if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + if (context.HostCapabilities.SupportsFragmentShaderInterlock) { context.BeginInvocationInterlockEXT(); } @@ -556,7 +556,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv private static OperationResult GenerateFSIEnd(CodeGenContext context, AstOperation operation) { - if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + if (context.HostCapabilities.SupportsFragmentShaderInterlock) { context.EndInvocationInterlockEXT(); } @@ -1446,7 +1446,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv lodBias = Src(AggregateType.FP32); } - if (!isGather && !intCoords && !isMultisample && !hasLodLevel && !hasDerivatives && context.Config.Stage != ShaderStage.Fragment) + if (!isGather && !intCoords && !isMultisample && !hasLodLevel && !hasDerivatives && context.Definitions.Stage != ShaderStage.Fragment) { // Implicit LOD is only valid on fragment. // Use the LOD bias as explicit LOD if available. @@ -1804,8 +1804,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer - ? context.Config.Properties.ConstantBuffers[bindingIndex.Value] - : context.Config.Properties.StorageBuffers[bindingIndex.Value]; + ? context.Properties.ConstantBuffers[bindingIndex.Value] + : context.Properties.StorageBuffers[bindingIndex.Value]; StructureField field = buffer.Type.Fields[fieldIndex.Value]; storageClass = StorageClass.Uniform; @@ -1825,13 +1825,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (storageKind == StorageKind.LocalMemory) { storageClass = StorageClass.Private; - varType = context.Config.Properties.LocalMemories[bindingId.Value].Type & AggregateType.ElementTypeMask; + varType = context.Properties.LocalMemories[bindingId.Value].Type & AggregateType.ElementTypeMask; baseObj = context.LocalMemories[bindingId.Value]; } else { storageClass = StorageClass.Workgroup; - varType = context.Config.Properties.SharedMemories[bindingId.Value].Type & AggregateType.ElementTypeMask; + varType = context.Properties.SharedMemories[bindingId.Value].Type & AggregateType.ElementTypeMask; baseObj = context.SharedMemories[bindingId.Value]; } break; @@ -1851,7 +1851,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv int location = 0; int component = 0; - if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) { if (operation.GetSource(srcIndex++) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) { @@ -1863,7 +1863,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (operation.SourcesCount > srcIndex && operation.GetSource(srcIndex) is AstOperand elemIndex && elemIndex.Type == OperandType.Constant && - context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) + context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) { component = elemIndex.Value; srcIndex++; @@ -1872,11 +1872,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (ioVariable == IoVariable.UserDefined) { - varType = context.Config.GetUserDefinedType(location, isOutput); + varType = context.Definitions.GetUserDefinedType(location, isOutput); } else if (ioVariable == IoVariable.FragmentOutputColor) { - varType = context.Config.GetFragmentOutputColorType(location); + varType = context.Definitions.GetFragmentOutputColorType(location); } else { @@ -2076,7 +2076,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) + if (!context.HostCapabilities.ReducedPrecision || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2087,7 +2087,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) + if (!context.HostCapabilities.ReducedPrecision || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2147,7 +2147,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) + if (!context.HostCapabilities.ReducedPrecision || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } @@ -2158,7 +2158,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv { var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3)); - if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise) + if (!context.HostCapabilities.ReducedPrecision || operation.ForcePrecise) { context.Decorate(result, Decoration.NoContraction); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index 217979757..5eee888e4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv HelperFunctionsMask.ShuffleXor | HelperFunctionsMask.SwizzleAdd; - public static byte[] Generate(StructuredProgramInfo info, ShaderConfig config) + public static byte[] Generate(StructuredProgramInfo info, CodeGenParameters parameters) { SpvInstructionPool instPool; SpvLiteralIntegerPool integerPool; @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv integerPool = _integerPool.Allocate(); } - CodeGenContext context = new(info, config, instPool, integerPool); + CodeGenContext context = new(info, parameters, instPool, integerPool); context.AddCapability(Capability.GroupNonUniformBallot); context.AddCapability(Capability.GroupNonUniformShuffle); @@ -56,39 +56,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddCapability(Capability.ImageQuery); context.AddCapability(Capability.SampledBuffer); - if (config.TransformFeedbackEnabled && config.LastInVertexPipeline) + if (parameters.Definitions.TransformFeedbackEnabled && parameters.Definitions.LastInVertexPipeline) { context.AddCapability(Capability.TransformFeedback); } - if (config.Stage == ShaderStage.Fragment) + if (parameters.Definitions.Stage == ShaderStage.Fragment) { if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer))) { context.AddCapability(Capability.Geometry); } - if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + if (context.HostCapabilities.SupportsFragmentShaderInterlock) { context.AddCapability(Capability.FragmentShaderPixelInterlockEXT); context.AddExtension("SPV_EXT_fragment_shader_interlock"); } } - else if (config.Stage == ShaderStage.Geometry) + else if (parameters.Definitions.Stage == ShaderStage.Geometry) { context.AddCapability(Capability.Geometry); - if (config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) + if (parameters.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough) { context.AddExtension("SPV_NV_geometry_shader_passthrough"); context.AddCapability(Capability.GeometryShaderPassthroughNV); } } - else if (config.Stage == ShaderStage.TessellationControl || config.Stage == ShaderStage.TessellationEvaluation) + else if (parameters.Definitions.Stage == ShaderStage.TessellationControl || + parameters.Definitions.Stage == ShaderStage.TessellationEvaluation) { context.AddCapability(Capability.Tessellation); } - else if (config.Stage == ShaderStage.Vertex) + else if (parameters.Definitions.Stage == ShaderStage.Vertex) { context.AddCapability(Capability.DrawParameters); } @@ -170,15 +171,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv if (funcIndex == 0) { - context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface()); + context.AddEntryPoint(context.Definitions.Stage.Convert(), spvFunc, "main", context.GetMainInterface()); - if (context.Config.Stage == ShaderStage.TessellationControl) + if (context.Definitions.Stage == ShaderStage.TessellationControl) { - context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive); + context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Definitions.ThreadsPerInputPrimitive); } - else if (context.Config.Stage == ShaderStage.TessellationEvaluation) + else if (context.Definitions.Stage == ShaderStage.TessellationEvaluation) { - switch (context.Config.GpuAccessor.QueryTessPatchType()) + switch (context.Definitions.TessPatchType) { case TessPatchType.Isolines: context.AddExecutionMode(spvFunc, ExecutionMode.Isolines); @@ -191,7 +192,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } - switch (context.Config.GpuAccessor.QueryTessSpacing()) + switch (context.Definitions.TessSpacing) { case TessSpacing.EqualSpacing: context.AddExecutionMode(spvFunc, ExecutionMode.SpacingEqual); @@ -204,9 +205,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv break; } - bool tessCw = context.Config.GpuAccessor.QueryTessCw(); + bool tessCw = context.Definitions.TessCw; - if (context.Config.Options.TargetApi == TargetApi.Vulkan) + if (context.TargetApi == TargetApi.Vulkan) { // We invert the front face on Vulkan backend, so we need to do that here as well. tessCw = !tessCw; @@ -221,37 +222,35 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCcw); } } - else if (context.Config.Stage == ShaderStage.Geometry) + else if (context.Definitions.Stage == ShaderStage.Geometry) { - InputTopology inputTopology = context.Config.GpuAccessor.QueryPrimitiveTopology(); - - context.AddExecutionMode(spvFunc, inputTopology switch + context.AddExecutionMode(spvFunc, context.Definitions.InputTopology switch { InputTopology.Points => ExecutionMode.InputPoints, InputTopology.Lines => ExecutionMode.InputLines, InputTopology.LinesAdjacency => ExecutionMode.InputLinesAdjacency, InputTopology.Triangles => ExecutionMode.Triangles, InputTopology.TrianglesAdjacency => ExecutionMode.InputTrianglesAdjacency, - _ => throw new InvalidOperationException($"Invalid input topology \"{inputTopology}\"."), + _ => throw new InvalidOperationException($"Invalid input topology \"{context.Definitions.InputTopology}\"."), }); - context.AddExecutionMode(spvFunc, ExecutionMode.Invocations, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive); + context.AddExecutionMode(spvFunc, ExecutionMode.Invocations, (SpvLiteralInteger)context.Definitions.ThreadsPerInputPrimitive); - context.AddExecutionMode(spvFunc, context.Config.OutputTopology switch + context.AddExecutionMode(spvFunc, context.Definitions.OutputTopology switch { OutputTopology.PointList => ExecutionMode.OutputPoints, OutputTopology.LineStrip => ExecutionMode.OutputLineStrip, OutputTopology.TriangleStrip => ExecutionMode.OutputTriangleStrip, - _ => throw new InvalidOperationException($"Invalid output topology \"{context.Config.OutputTopology}\"."), + _ => throw new InvalidOperationException($"Invalid output topology \"{context.Definitions.OutputTopology}\"."), }); - int maxOutputVertices = context.Config.GpPassthrough ? context.InputVertices : context.Config.MaxOutputVertices; + int maxOutputVertices = context.Definitions.GpPassthrough ? context.InputVertices : context.Definitions.MaxOutputVertices; context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)maxOutputVertices); } - else if (context.Config.Stage == ShaderStage.Fragment) + else if (context.Definitions.Stage == ShaderStage.Fragment) { - context.AddExecutionMode(spvFunc, context.Config.Properties.OriginUpperLeft + context.AddExecutionMode(spvFunc, context.Definitions.OriginUpperLeft ? ExecutionMode.OriginUpperLeft : ExecutionMode.OriginLowerLeft); @@ -260,22 +259,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.AddExecutionMode(spvFunc, ExecutionMode.DepthReplacing); } - if (context.Config.GpuAccessor.QueryEarlyZForce()) + if (context.Definitions.EarlyZForce) { context.AddExecutionMode(spvFunc, ExecutionMode.EarlyFragmentTests); } if ((info.HelperFunctionsMask & HelperFunctionsMask.FSI) != 0 && - context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) + context.HostCapabilities.SupportsFragmentShaderInterlock) { context.AddExecutionMode(spvFunc, ExecutionMode.PixelInterlockOrderedEXT); } } - else if (context.Config.Stage == ShaderStage.Compute) + else if (context.Definitions.Stage == ShaderStage.Compute) { - var localSizeX = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeX(); - var localSizeY = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeY(); - var localSizeZ = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeZ(); + var localSizeX = (SpvLiteralInteger)context.Definitions.ComputeLocalSizeX; + var localSizeY = (SpvLiteralInteger)context.Definitions.ComputeLocalSizeY; + var localSizeZ = (SpvLiteralInteger)context.Definitions.ComputeLocalSizeZ; context.AddExecutionMode( spvFunc, @@ -285,7 +284,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv localSizeZ); } - if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) + if (context.Definitions.TransformFeedbackEnabled && context.Definitions.LastInVertexPipeline) { context.AddExecutionMode(spvFunc, ExecutionMode.Xfb); } diff --git a/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs b/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs index 7776ccc52..67304d027 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections; using System.Collections.Generic; @@ -11,11 +12,26 @@ namespace Ryujinx.Graphics.Shader.Decoders private readonly List<DecodedFunction> _functionsWithId; public int FunctionsWithIdCount => _functionsWithId.Count; - public DecodedProgram(DecodedFunction mainFunction, IReadOnlyDictionary<ulong, DecodedFunction> functions) + public AttributeUsage AttributeUsage { get; } + public FeatureFlags UsedFeatures { get; } + public byte ClipDistancesWritten { get; } + public int Cb1DataSize { get; } + + public DecodedProgram( + DecodedFunction mainFunction, + IReadOnlyDictionary<ulong, DecodedFunction> functions, + AttributeUsage attributeUsage, + FeatureFlags usedFeatures, + byte clipDistancesWritten, + int cb1DataSize) { MainFunction = mainFunction; _functions = functions; - _functionsWithId = new List<DecodedFunction>(); + _functionsWithId = new(); + AttributeUsage = attributeUsage; + UsedFeatures = usedFeatures; + ClipDistancesWritten = clipDistancesWritten; + Cb1DataSize = cb1DataSize; } public DecodedFunction GetFunctionByAddress(ulong address) diff --git a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index ba31c0205..d18a9baf8 100644 --- a/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/src/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -9,8 +9,45 @@ namespace Ryujinx.Graphics.Shader.Decoders { static class Decoder { - public static DecodedProgram Decode(ShaderConfig config, ulong startAddress) + private class Context { + public AttributeUsage AttributeUsage { get; } + public FeatureFlags UsedFeatures { get; private set; } + public byte ClipDistancesWritten { get; private set; } + public int Cb1DataSize { get; private set; } + + private readonly IGpuAccessor _gpuAccessor; + + public Context(IGpuAccessor gpuAccessor) + { + _gpuAccessor = gpuAccessor; + AttributeUsage = new(gpuAccessor); + } + + public uint ConstantBuffer1Read(int offset) + { + if (Cb1DataSize < offset + 4) + { + Cb1DataSize = offset + 4; + } + + return _gpuAccessor.ConstantBuffer1Read(offset); + } + + public void SetUsedFeature(FeatureFlags flags) + { + UsedFeatures |= flags; + } + + public void SetClipDistanceWritten(int index) + { + ClipDistancesWritten |= (byte)(1 << index); + } + } + + public static DecodedProgram Decode(ShaderDefinitions definitions, IGpuAccessor gpuAccessor, ulong startAddress) + { + Context context = new(gpuAccessor); Queue<DecodedFunction> functionsQueue = new(); Dictionary<ulong, DecodedFunction> functionsVisited = new(); @@ -89,7 +126,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } } - FillBlock(config, currBlock, limitAddress, startAddress); + FillBlock(definitions, gpuAccessor, context, currBlock, limitAddress, startAddress); if (currBlock.OpCodes.Count != 0) { @@ -148,7 +185,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } // Try to find targets for BRX (indirect branch) instructions. - hasNewTarget = FindBrxTargets(config, blocks, GetBlock); + hasNewTarget = FindBrxTargets(context, blocks, GetBlock); // If we discovered new branch targets from the BRX instruction, // we need another round of decoding to decode the new blocks. @@ -160,7 +197,13 @@ namespace Ryujinx.Graphics.Shader.Decoders currentFunction.SetBlocks(blocks.ToArray()); } - return new DecodedProgram(mainFunction, functionsVisited); + return new DecodedProgram( + mainFunction, + functionsVisited, + context.AttributeUsage, + context.UsedFeatures, + context.ClipDistancesWritten, + context.Cb1DataSize); } private static bool BinarySearch(List<Block> blocks, ulong address, out int index) @@ -198,10 +241,14 @@ namespace Ryujinx.Graphics.Shader.Decoders return false; } - private static void FillBlock(ShaderConfig config, Block block, ulong limitAddress, ulong startAddress) + private static void FillBlock( + ShaderDefinitions definitions, + IGpuAccessor gpuAccessor, + Context context, + Block block, + ulong limitAddress, + ulong startAddress) { - IGpuAccessor gpuAccessor = config.GpuAccessor; - ulong address = block.Address; int bufferOffset = 0; ReadOnlySpan<ulong> buffer = ReadOnlySpan<ulong>.Empty; @@ -235,27 +282,31 @@ namespace Ryujinx.Graphics.Shader.Decoders if (op.Props.HasFlag(InstProps.TexB)) { - config.SetUsedFeature(FeatureFlags.Bindless); + context.SetUsedFeature(FeatureFlags.Bindless); } - if (op.Name == InstName.Ald || op.Name == InstName.Ast || op.Name == InstName.Ipa) + switch (op.Name) { - SetUserAttributeUses(config, op.Name, opCode); - } - else if (op.Name == InstName.Pbk || op.Name == InstName.Pcnt || op.Name == InstName.Ssy) - { - block.AddPushOp(op); - } - else if (op.Name == InstName.Ldl || op.Name == InstName.Stl) - { - config.SetUsedFeature(FeatureFlags.LocalMemory); - } - else if (op.Name == InstName.Atoms || - op.Name == InstName.AtomsCas || - op.Name == InstName.Lds || - op.Name == InstName.Sts) - { - config.SetUsedFeature(FeatureFlags.SharedMemory); + case InstName.Ald: + case InstName.Ast: + case InstName.Ipa: + SetUserAttributeUses(definitions, context, op.Name, opCode); + break; + case InstName.Pbk: + case InstName.Pcnt: + case InstName.Ssy: + block.AddPushOp(op); + break; + case InstName.Ldl: + case InstName.Stl: + context.SetUsedFeature(FeatureFlags.LocalMemory); + break; + case InstName.Atoms: + case InstName.AtomsCas: + case InstName.Lds: + case InstName.Sts: + context.SetUsedFeature(FeatureFlags.SharedMemory); + break; } block.OpCodes.Add(op); @@ -267,7 +318,7 @@ namespace Ryujinx.Graphics.Shader.Decoders block.EndAddress = address; } - private static void SetUserAttributeUses(ShaderConfig config, InstName name, ulong opCode) + private static void SetUserAttributeUses(ShaderDefinitions definitions, Context context, InstName name, ulong opCode) { int offset; int count = 1; @@ -304,13 +355,13 @@ namespace Ryujinx.Graphics.Shader.Decoders { if (isStore) { - config.SetAllOutputUserAttributes(); - config.SetUsedFeature(FeatureFlags.OaIndexing); + context.AttributeUsage.SetAllOutputUserAttributes(); + definitions.EnableOutputIndexing(); } else { - config.SetAllInputUserAttributes(); - config.SetUsedFeature(FeatureFlags.IaIndexing); + context.AttributeUsage.SetAllInputUserAttributes(); + definitions.EnableInputIndexing(); } } else @@ -328,11 +379,11 @@ namespace Ryujinx.Graphics.Shader.Decoders if (isStore) { - config.SetOutputUserAttributePerPatch(index); + context.AttributeUsage.SetOutputUserAttributePerPatch(index); } else { - config.SetInputUserAttributePerPatch(index); + context.AttributeUsage.SetInputUserAttributePerPatch(index); } } } @@ -343,11 +394,11 @@ namespace Ryujinx.Graphics.Shader.Decoders if (isStore) { - config.SetOutputUserAttribute(index); + context.AttributeUsage.SetOutputUserAttribute(index); } else { - config.SetInputUserAttribute(index, (userAttr >> 2) & 3); + context.AttributeUsage.SetInputUserAttribute(index, (userAttr >> 2) & 3); } } @@ -356,7 +407,54 @@ namespace Ryujinx.Graphics.Shader.Decoders (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) || (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd))) { - config.SetUsedFeature(FeatureFlags.FixedFuncAttr); + context.SetUsedFeature(FeatureFlags.FixedFuncAttr); + } + else + { + if (isStore) + { + switch (attr) + { + case AttributeConsts.Layer: + if (definitions.Stage != ShaderStage.Compute && definitions.Stage != ShaderStage.Fragment) + { + context.SetUsedFeature(FeatureFlags.RtLayer); + } + break; + case AttributeConsts.ClipDistance0: + case AttributeConsts.ClipDistance1: + case AttributeConsts.ClipDistance2: + case AttributeConsts.ClipDistance3: + case AttributeConsts.ClipDistance4: + case AttributeConsts.ClipDistance5: + case AttributeConsts.ClipDistance6: + case AttributeConsts.ClipDistance7: + if (definitions.Stage == ShaderStage.Vertex) + { + context.SetClipDistanceWritten((attr - AttributeConsts.ClipDistance0) / 4); + } + break; + } + } + else + { + switch (attr) + { + case AttributeConsts.PositionX: + case AttributeConsts.PositionY: + if (definitions.Stage == ShaderStage.Fragment) + { + context.SetUsedFeature(FeatureFlags.FragCoordXY); + } + break; + case AttributeConsts.InstanceId: + if (definitions.Stage == ShaderStage.Vertex) + { + context.SetUsedFeature(FeatureFlags.InstanceId); + } + break; + } + } } } } @@ -379,7 +477,7 @@ namespace Ryujinx.Graphics.Shader.Decoders return condOp.Pred == RegisterConsts.PredicateTrueIndex && !condOp.PredInv; } - private static bool FindBrxTargets(ShaderConfig config, IEnumerable<Block> blocks, Func<ulong, Block> getBlock) + private static bool FindBrxTargets(Context context, IEnumerable<Block> blocks, Func<ulong, Block> getBlock) { bool hasNewTarget = false; @@ -406,7 +504,7 @@ namespace Ryujinx.Graphics.Shader.Decoders for (int i = 0; i < cbOffsetsCount; i++) { - uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4); + uint targetOffset = context.ConstantBuffer1Read(cbBaseOffset + i * 4); ulong targetAddress = baseOffset + targetOffset; if (visited.Add(targetAddress)) diff --git a/src/Ryujinx.Graphics.Shader/GpuGraphicsState.cs b/src/Ryujinx.Graphics.Shader/GpuGraphicsState.cs new file mode 100644 index 000000000..f16c71d55 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/GpuGraphicsState.cs @@ -0,0 +1,169 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.Graphics.Shader +{ + /// <summary> + /// GPU graphics state that the shader depends on. + /// </summary> + public readonly struct GpuGraphicsState + { + /// <summary> + /// Early Z force enable. + /// </summary> + public readonly bool EarlyZForce; + + /// <summary> + /// Primitive topology of current draw. + /// </summary> + public readonly InputTopology Topology; + + /// <summary> + /// Tessellation winding order. + /// </summary> + public readonly bool TessCw; + + /// <summary> + /// Tessellation patch type. + /// </summary> + public readonly TessPatchType TessPatchType; + + /// <summary> + /// Tessellation spacing. + /// </summary> + public readonly TessSpacing TessSpacing; + + /// <summary> + /// Indicates whether alpha-to-coverage is enabled. + /// </summary> + public readonly bool AlphaToCoverageEnable; + + /// <summary> + /// Indicates whether alpha-to-coverage dithering is enabled. + /// </summary> + public readonly bool AlphaToCoverageDitherEnable; + + /// <summary> + /// Indicates whether the viewport transform is disabled. + /// </summary> + public readonly bool ViewportTransformDisable; + + /// <summary> + /// Depth mode zero to one or minus one to one. + /// </summary> + public readonly bool DepthMode; + + /// <summary> + /// Indicates if the point size is set on the shader or is fixed. + /// </summary> + public readonly bool ProgramPointSizeEnable; + + /// <summary> + /// Point size used if <see cref="ProgramPointSizeEnable" /> is false. + /// </summary> + public readonly float PointSize; + + /// <summary> + /// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded. + /// </summary> + public readonly AlphaTestOp AlphaTestCompare; + + /// <summary> + /// When alpha test is enabled, indicates the value to compare with the fragment output alpha. + /// </summary> + public readonly float AlphaTestReference; + + /// <summary> + /// Type of the vertex attributes consumed by the shader. + /// </summary> + public readonly Array32<AttributeType> AttributeTypes; + + /// <summary> + /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. + /// </summary> + public readonly bool HasConstantBufferDrawParameters; + + /// <summary> + /// Type of the fragment shader outputs. + /// </summary> + public readonly Array8<AttributeType> FragmentOutputTypes; + + /// <summary> + /// Indicates whether dual source blend is enabled. + /// </summary> + public readonly bool DualSourceBlendEnable; + + /// <summary> + /// Indicates if negation of the viewport Y axis is enabled. + /// </summary> + public readonly bool YNegateEnabled; + + /// <summary> + /// If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner. + /// </summary> + public readonly bool OriginUpperLeft; + + /// <summary> + /// Creates a new GPU graphics state. + /// </summary> + /// <param name="earlyZForce">Early Z force enable</param> + /// <param name="topology">Primitive topology</param> + /// <param name="tessCw">Tessellation winding order (clockwise or counter-clockwise)</param> + /// <param name="tessPatchType">Tessellation patch type</param> + /// <param name="tessSpacing">Tessellation spacing</param> + /// <param name="alphaToCoverageEnable">Indicates whether alpha-to-coverage is enabled</param> + /// <param name="alphaToCoverageDitherEnable">Indicates whether alpha-to-coverage dithering is enabled</param> + /// <param name="viewportTransformDisable">Indicates whether the viewport transform is disabled</param> + /// <param name="depthMode">Depth mode zero to one or minus one to one</param> + /// <param name="programPointSizeEnable">Indicates if the point size is set on the shader or is fixed</param> + /// <param name="pointSize">Point size if not set from shader</param> + /// <param name="alphaTestCompare">When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded</param> + /// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param> + /// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param> + /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> + /// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param> + /// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param> + /// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param> + /// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param> + public GpuGraphicsState( + bool earlyZForce, + InputTopology topology, + bool tessCw, + TessPatchType tessPatchType, + TessSpacing tessSpacing, + bool alphaToCoverageEnable, + bool alphaToCoverageDitherEnable, + bool viewportTransformDisable, + bool depthMode, + bool programPointSizeEnable, + float pointSize, + AlphaTestOp alphaTestCompare, + float alphaTestReference, + in Array32<AttributeType> attributeTypes, + bool hasConstantBufferDrawParameters, + in Array8<AttributeType> fragmentOutputTypes, + bool dualSourceBlendEnable, + bool yNegateEnabled, + bool originUpperLeft) + { + EarlyZForce = earlyZForce; + Topology = topology; + TessCw = tessCw; + TessPatchType = tessPatchType; + TessSpacing = tessSpacing; + AlphaToCoverageEnable = alphaToCoverageEnable; + AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable; + ViewportTransformDisable = viewportTransformDisable; + DepthMode = depthMode; + ProgramPointSizeEnable = programPointSizeEnable; + PointSize = pointSize; + AlphaTestCompare = alphaTestCompare; + AlphaTestReference = alphaTestReference; + AttributeTypes = attributeTypes; + HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; + FragmentOutputTypes = fragmentOutputTypes; + DualSourceBlendEnable = dualSourceBlendEnable; + YNegateEnabled = yNegateEnabled; + OriginUpperLeft = originUpperLeft; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index a47791d3f..4c0adc3bb 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -1,21 +1,13 @@ -using System; +using Ryujinx.Graphics.Shader.CodeGen; +using System; namespace Ryujinx.Graphics.Shader { /// <summary> /// GPU state access interface. /// </summary> - public interface IGpuAccessor + public interface IGpuAccessor : ILogger { - /// <summary> - /// Prints a log message. - /// </summary> - /// <param name="message">Message to print</param> - void Log(string message) - { - // No default log output. - } - /// <summary> /// Reads data from the constant buffer 1. /// </summary> @@ -34,44 +26,6 @@ namespace Ryujinx.Graphics.Shader /// <returns>Span of the memory location</returns> ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize); - /// <summary> - /// Queries the alpha test comparison operator that is being used currently. - /// If alpha test is disabled, it should be set to <see cref="AlphaTestOp.Always"/>. - /// </summary> - /// <returns>Current alpha test comparison</returns> - AlphaTestOp QueryAlphaTestCompare() - { - return AlphaTestOp.Always; - } - - /// <summary> - /// Queries the current alpha test reference value used by the comparison. - /// </summary> - /// <returns>Current alpha test reference value</returns> - float QueryAlphaTestReference() - { - return 0f; - } - - /// <summary> - /// Queries the type of the vertex shader input attribute at the specified <paramref name="location"/>. - /// </summary> - /// <param name="location">Location of the input attribute</param> - /// <returns>Input type</returns> - AttributeType QueryAttributeType(int location) - { - return AttributeType.Float; - } - - /// <summary> - /// Queries whenever the alpha-to-coverage dithering feature is enabled. - /// </summary> - /// <returns>True if the feature is enabled, false otherwise</returns> - bool QueryAlphaToCoverageDitherEnable() - { - return false; - } - /// <summary> /// Queries the binding number of a constant buffer. /// </summary> @@ -114,16 +68,6 @@ namespace Ryujinx.Graphics.Shader return index; } - /// <summary> - /// Queries output type for fragment shaders. - /// </summary> - /// <param name="location">Location of the framgent output</param> - /// <returns>Output location</returns> - AttributeType QueryFragmentOutputType(int location) - { - return AttributeType.Float; - } - /// <summary> /// Queries Local Size X for compute shaders. /// </summary> @@ -179,12 +123,12 @@ namespace Ryujinx.Graphics.Shader } /// <summary> - /// Queries if host state forces early depth testing. + /// Queries specialized GPU graphics state that the shader depends on. /// </summary> - /// <returns>True if early depth testing is forced</returns> - bool QueryEarlyZForce() + /// <returns>GPU graphics state</returns> + GpuGraphicsState QueryGraphicsState() { - return false; + return default; } /// <summary> @@ -223,15 +167,6 @@ namespace Ryujinx.Graphics.Shader return false; } - /// <summary> - /// Queries dual source blend state. - /// </summary> - /// <returns>True if blending is enabled with a dual source blend equation, false otherwise</returns> - bool QueryDualSourceBlendEnable() - { - return false; - } - /// <summary> /// Queries host about the presence of the FrontFacing built-in variable bug. /// </summary> @@ -412,25 +347,6 @@ namespace Ryujinx.Graphics.Shader return true; } - /// <summary> - /// Queries the point size from the GPU state, used when it is not explicitly set on the shader. - /// </summary> - /// <returns>Current point size</returns> - float QueryPointSize() - { - return 1f; - } - - /// <summary> - /// Queries the state that indicates if the program point size should be explicitly set on the shader - /// or read from the GPU state. - /// </summary> - /// <returns>True if the shader is expected to set the point size explicitly, false otherwise</returns> - bool QueryProgramPointSize() - { - return true; - } - /// <summary> /// Queries sampler type information. /// </summary> @@ -453,42 +369,6 @@ namespace Ryujinx.Graphics.Shader return true; } - /// <summary> - /// Queries current primitive topology for geometry shaders. - /// </summary> - /// <returns>Current primitive topology</returns> - InputTopology QueryPrimitiveTopology() - { - return InputTopology.Points; - } - - /// <summary> - /// Queries the tessellation evaluation shader primitive winding order. - /// </summary> - /// <returns>True if the primitive winding order is clockwise, false if counter-clockwise</returns> - bool QueryTessCw() - { - return false; - } - - /// <summary> - /// Queries the tessellation evaluation shader abstract patch type. - /// </summary> - /// <returns>Abstract patch type</returns> - TessPatchType QueryTessPatchType() - { - return TessPatchType.Triangles; - } - - /// <summary> - /// Queries the tessellation evaluation shader spacing between tessellated vertices of the patch. - /// </summary> - /// <returns>Spacing between tessellated vertices of the patch</returns> - TessSpacing QueryTessSpacing() - { - return TessSpacing.EqualSpacing; - } - /// <summary> /// Queries texture format information, for shaders using image load or store. /// </summary> @@ -504,15 +384,6 @@ namespace Ryujinx.Graphics.Shader return TextureFormat.R8G8B8A8Unorm; } - /// <summary> - /// Queries depth mode information from the GPU state. - /// </summary> - /// <returns>True if current depth mode is -1 to 1, false if 0 to 1</returns> - bool QueryTransformDepthMinusOneToOne() - { - return false; - } - /// <summary> /// Queries transform feedback enable state. /// </summary> @@ -542,24 +413,6 @@ namespace Ryujinx.Graphics.Shader return 0; } - /// <summary> - /// Queries if host state disables the viewport transform. - /// </summary> - /// <returns>True if the viewport transform is disabled</returns> - bool QueryViewportTransformDisable() - { - return false; - } - - /// <summary> - /// Queries Y negate enable state. - /// </summary> - /// <returns>True if Y negate of the fragment coordinates is enabled, false otherwise</returns> - bool QueryYNegateEnabled() - { - return false; - } - /// <summary> /// Registers a texture used by the shader. /// </summary> diff --git a/src/Ryujinx.Graphics.Shader/ILogger.cs b/src/Ryujinx.Graphics.Shader/ILogger.cs new file mode 100644 index 000000000..c43067f50 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/ILogger.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.Shader.CodeGen +{ + /// <summary> + /// Shader code generation logging interface. + /// </summary> + public interface ILogger + { + /// <summary> + /// Prints a log message. + /// </summary> + /// <param name="message">Message to print</param> + void Log(string message) + { + // No default log output. + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs b/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs index 5e572f5a7..54705acaf 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/AttributeMap.cs @@ -127,25 +127,25 @@ namespace Ryujinx.Graphics.Shader.Instructions { if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry)) { - context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid."); + context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid."); return Const(0); } StagesMask validUseMask = isOutput ? entry.OutputMask : entry.InputMask; - if (((StagesMask)(1 << (int)context.Config.Stage) & validUseMask) == StagesMask.None) + if (((StagesMask)(1 << (int)context.TranslatorContext.Definitions.Stage) & validUseMask) == StagesMask.None) { - context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}."); + context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.TranslatorContext.Definitions.Stage}."); return Const(0); } - if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable)) + if (!IsSupportedByHost(context.TranslatorContext.GpuAccessor, context.TranslatorContext.Definitions.Stage, entry.IoVariable)) { - context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}."); + context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.TranslatorContext.Definitions.Stage}."); return Const(0); } - if (HasInvocationId(context.Config.Stage, isOutput) && !isPerPatch) + if (HasInvocationId(context.TranslatorContext.Definitions.Stage, isOutput) && !isPerPatch) { primVertex = context.Load(StorageKind.Input, IoVariable.InvocationId); } @@ -156,12 +156,12 @@ namespace Ryujinx.Graphics.Shader.Instructions StorageKind storageKind = isPerPatch ? (isOutput ? StorageKind.OutputPerPatch : StorageKind.InputPerPatch) : (isOutput ? StorageKind.Output : StorageKind.Input); - IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry); - AggregateType type = GetType(context.Config, isOutput, innerIndex, in entry); + IoVariable ioVariable = GetIoVariable(context.TranslatorContext.Definitions.Stage, in entry); + AggregateType type = GetType(context.TranslatorContext.Definitions, isOutput, innerIndex, in entry); int elementCount = GetElementCount(type); bool isArray = type.HasFlag(AggregateType.Array); - bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput); + bool hasArrayIndex = isArray || context.TranslatorContext.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput); bool hasElementIndex = elementCount > 1; @@ -190,25 +190,25 @@ namespace Ryujinx.Graphics.Shader.Instructions { if (!(isPerPatch ? _attributesPerPatch : _attributes).TryGetValue(offset, out AttributeEntry entry)) { - context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid."); + context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} is not valid."); return; } - if (((StagesMask)(1 << (int)context.Config.Stage) & entry.OutputMask) == StagesMask.None) + if (((StagesMask)(1 << (int)context.TranslatorContext.Definitions.Stage) & entry.OutputMask) == StagesMask.None) { - context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.Config.Stage}."); + context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not valid for stage {context.TranslatorContext.Definitions.Stage}."); return; } - if (!IsSupportedByHost(context.Config.GpuAccessor, context.Config.Stage, entry.IoVariable)) + if (!IsSupportedByHost(context.TranslatorContext.GpuAccessor, context.TranslatorContext.Definitions.Stage, entry.IoVariable)) { - context.Config.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.Config.Stage}."); + context.TranslatorContext.GpuAccessor.Log($"Attribute offset 0x{offset:X} ({entry.IoVariable}) is not supported by the host for stage {context.TranslatorContext.Definitions.Stage}."); return; } Operand invocationId = null; - if (HasInvocationId(context.Config.Stage, isOutput: true) && !isPerPatch) + if (HasInvocationId(context.TranslatorContext.Definitions.Stage, isOutput: true) && !isPerPatch) { invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId); } @@ -217,12 +217,12 @@ namespace Ryujinx.Graphics.Shader.Instructions int innerIndex = innerOffset / 4; StorageKind storageKind = isPerPatch ? StorageKind.OutputPerPatch : StorageKind.Output; - IoVariable ioVariable = GetIoVariable(context.Config.Stage, in entry); - AggregateType type = GetType(context.Config, isOutput: true, innerIndex, in entry); + IoVariable ioVariable = GetIoVariable(context.TranslatorContext.Definitions.Stage, in entry); + AggregateType type = GetType(context.TranslatorContext.Definitions, isOutput: true, innerIndex, in entry); int elementCount = GetElementCount(type); bool isArray = type.HasFlag(AggregateType.Array); - bool hasArrayIndex = isArray || context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput: true); + bool hasArrayIndex = isArray || context.TranslatorContext.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput: true); bool hasElementIndex = elementCount > 1; @@ -271,7 +271,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return true; } - public static IoVariable GetIoVariable(ShaderConfig config, int offset, out int location) + public static IoVariable GetIoVariable(ShaderDefinitions definitions, int offset, out int location) { location = 0; @@ -280,17 +280,17 @@ namespace Ryujinx.Graphics.Shader.Instructions return IoVariable.Invalid; } - if (((StagesMask)(1 << (int)config.Stage) & entry.OutputMask) == StagesMask.None) + if (((StagesMask)(1 << (int)definitions.Stage) & entry.OutputMask) == StagesMask.None) { return IoVariable.Invalid; } - if (config.HasPerLocationInputOrOutput(entry.IoVariable, isOutput: true)) + if (definitions.HasPerLocationInputOrOutput(entry.IoVariable, isOutput: true)) { location = (offset - entry.BaseOffset) / 16; } - return GetIoVariable(config.Stage, in entry); + return GetIoVariable(definitions.Stage, in entry); } private static IoVariable GetIoVariable(ShaderStage stage, in AttributeEntry entry) @@ -303,17 +303,17 @@ namespace Ryujinx.Graphics.Shader.Instructions return entry.IoVariable; } - private static AggregateType GetType(ShaderConfig config, bool isOutput, int innerIndex, in AttributeEntry entry) + private static AggregateType GetType(ShaderDefinitions definitions, bool isOutput, int innerIndex, in AttributeEntry entry) { AggregateType type = entry.Type; if (entry.IoVariable == IoVariable.UserDefined) { - type = config.GetUserDefinedType(innerIndex / 4, isOutput); + type = definitions.GetUserDefinedType(innerIndex / 4, isOutput); } else if (entry.IoVariable == IoVariable.FragmentOutputColor) { - type = config.GetFragmentOutputColorType(innerIndex / 4); + type = definitions.GetFragmentOutputColorType(innerIndex / 4); } return type; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs index f105505dd..0c2f90b7e 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs @@ -9,350 +9,350 @@ namespace Ryujinx.Graphics.Shader.Instructions { context.GetOp<InstAtomCas>(); - context.Config.GpuAccessor.Log("Shader instruction AtomCas is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction AtomCas is not implemented."); } public static void AtomsCas(EmitterContext context) { context.GetOp<InstAtomsCas>(); - context.Config.GpuAccessor.Log("Shader instruction AtomsCas is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction AtomsCas is not implemented."); } public static void B2r(EmitterContext context) { context.GetOp<InstB2r>(); - context.Config.GpuAccessor.Log("Shader instruction B2r is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction B2r is not implemented."); } public static void Bpt(EmitterContext context) { context.GetOp<InstBpt>(); - context.Config.GpuAccessor.Log("Shader instruction Bpt is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Bpt is not implemented."); } public static void Cctl(EmitterContext context) { context.GetOp<InstCctl>(); - context.Config.GpuAccessor.Log("Shader instruction Cctl is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Cctl is not implemented."); } public static void Cctll(EmitterContext context) { context.GetOp<InstCctll>(); - context.Config.GpuAccessor.Log("Shader instruction Cctll is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Cctll is not implemented."); } public static void Cctlt(EmitterContext context) { context.GetOp<InstCctlt>(); - context.Config.GpuAccessor.Log("Shader instruction Cctlt is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Cctlt is not implemented."); } public static void Cs2r(EmitterContext context) { context.GetOp<InstCs2r>(); - context.Config.GpuAccessor.Log("Shader instruction Cs2r is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Cs2r is not implemented."); } public static void FchkR(EmitterContext context) { context.GetOp<InstFchkR>(); - context.Config.GpuAccessor.Log("Shader instruction FchkR is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction FchkR is not implemented."); } public static void FchkI(EmitterContext context) { context.GetOp<InstFchkI>(); - context.Config.GpuAccessor.Log("Shader instruction FchkI is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction FchkI is not implemented."); } public static void FchkC(EmitterContext context) { context.GetOp<InstFchkC>(); - context.Config.GpuAccessor.Log("Shader instruction FchkC is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction FchkC is not implemented."); } public static void Getcrsptr(EmitterContext context) { context.GetOp<InstGetcrsptr>(); - context.Config.GpuAccessor.Log("Shader instruction Getcrsptr is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Getcrsptr is not implemented."); } public static void Getlmembase(EmitterContext context) { context.GetOp<InstGetlmembase>(); - context.Config.GpuAccessor.Log("Shader instruction Getlmembase is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Getlmembase is not implemented."); } public static void Ide(EmitterContext context) { context.GetOp<InstIde>(); - context.Config.GpuAccessor.Log("Shader instruction Ide is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Ide is not implemented."); } public static void IdpR(EmitterContext context) { context.GetOp<InstIdpR>(); - context.Config.GpuAccessor.Log("Shader instruction IdpR is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction IdpR is not implemented."); } public static void IdpC(EmitterContext context) { context.GetOp<InstIdpC>(); - context.Config.GpuAccessor.Log("Shader instruction IdpC is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction IdpC is not implemented."); } public static void ImadspR(EmitterContext context) { context.GetOp<InstImadspR>(); - context.Config.GpuAccessor.Log("Shader instruction ImadspR is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction ImadspR is not implemented."); } public static void ImadspI(EmitterContext context) { context.GetOp<InstImadspI>(); - context.Config.GpuAccessor.Log("Shader instruction ImadspI is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction ImadspI is not implemented."); } public static void ImadspC(EmitterContext context) { context.GetOp<InstImadspC>(); - context.Config.GpuAccessor.Log("Shader instruction ImadspC is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction ImadspC is not implemented."); } public static void ImadspRc(EmitterContext context) { context.GetOp<InstImadspRc>(); - context.Config.GpuAccessor.Log("Shader instruction ImadspRc is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction ImadspRc is not implemented."); } public static void Jcal(EmitterContext context) { context.GetOp<InstJcal>(); - context.Config.GpuAccessor.Log("Shader instruction Jcal is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Jcal is not implemented."); } public static void Jmp(EmitterContext context) { context.GetOp<InstJmp>(); - context.Config.GpuAccessor.Log("Shader instruction Jmp is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Jmp is not implemented."); } public static void Jmx(EmitterContext context) { context.GetOp<InstJmx>(); - context.Config.GpuAccessor.Log("Shader instruction Jmx is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Jmx is not implemented."); } public static void Ld(EmitterContext context) { context.GetOp<InstLd>(); - context.Config.GpuAccessor.Log("Shader instruction Ld is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Ld is not implemented."); } public static void Lepc(EmitterContext context) { context.GetOp<InstLepc>(); - context.Config.GpuAccessor.Log("Shader instruction Lepc is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Lepc is not implemented."); } public static void Longjmp(EmitterContext context) { context.GetOp<InstLongjmp>(); - context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Longjmp is not implemented."); } public static void Pexit(EmitterContext context) { context.GetOp<InstPexit>(); - context.Config.GpuAccessor.Log("Shader instruction Pexit is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Pexit is not implemented."); } public static void Pixld(EmitterContext context) { context.GetOp<InstPixld>(); - context.Config.GpuAccessor.Log("Shader instruction Pixld is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Pixld is not implemented."); } public static void Plongjmp(EmitterContext context) { context.GetOp<InstPlongjmp>(); - context.Config.GpuAccessor.Log("Shader instruction Plongjmp is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Plongjmp is not implemented."); } public static void Pret(EmitterContext context) { context.GetOp<InstPret>(); - context.Config.GpuAccessor.Log("Shader instruction Pret is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Pret is not implemented."); } public static void PrmtR(EmitterContext context) { context.GetOp<InstPrmtR>(); - context.Config.GpuAccessor.Log("Shader instruction PrmtR is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtR is not implemented."); } public static void PrmtI(EmitterContext context) { context.GetOp<InstPrmtI>(); - context.Config.GpuAccessor.Log("Shader instruction PrmtI is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtI is not implemented."); } public static void PrmtC(EmitterContext context) { context.GetOp<InstPrmtC>(); - context.Config.GpuAccessor.Log("Shader instruction PrmtC is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtC is not implemented."); } public static void PrmtRc(EmitterContext context) { context.GetOp<InstPrmtRc>(); - context.Config.GpuAccessor.Log("Shader instruction PrmtRc is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtRc is not implemented."); } public static void R2b(EmitterContext context) { context.GetOp<InstR2b>(); - context.Config.GpuAccessor.Log("Shader instruction R2b is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction R2b is not implemented."); } public static void Ram(EmitterContext context) { context.GetOp<InstRam>(); - context.Config.GpuAccessor.Log("Shader instruction Ram is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Ram is not implemented."); } public static void Rtt(EmitterContext context) { context.GetOp<InstRtt>(); - context.Config.GpuAccessor.Log("Shader instruction Rtt is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Rtt is not implemented."); } public static void Sam(EmitterContext context) { context.GetOp<InstSam>(); - context.Config.GpuAccessor.Log("Shader instruction Sam is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Sam is not implemented."); } public static void Setcrsptr(EmitterContext context) { context.GetOp<InstSetcrsptr>(); - context.Config.GpuAccessor.Log("Shader instruction Setcrsptr is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Setcrsptr is not implemented."); } public static void Setlmembase(EmitterContext context) { context.GetOp<InstSetlmembase>(); - context.Config.GpuAccessor.Log("Shader instruction Setlmembase is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Setlmembase is not implemented."); } public static void St(EmitterContext context) { context.GetOp<InstSt>(); - context.Config.GpuAccessor.Log("Shader instruction St is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction St is not implemented."); } public static void Stp(EmitterContext context) { context.GetOp<InstStp>(); - context.Config.GpuAccessor.Log("Shader instruction Stp is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Stp is not implemented."); } public static void Txa(EmitterContext context) { context.GetOp<InstTxa>(); - context.Config.GpuAccessor.Log("Shader instruction Txa is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Txa is not implemented."); } public static void Vabsdiff(EmitterContext context) { context.GetOp<InstVabsdiff>(); - context.Config.GpuAccessor.Log("Shader instruction Vabsdiff is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Vabsdiff is not implemented."); } public static void Vabsdiff4(EmitterContext context) { context.GetOp<InstVabsdiff4>(); - context.Config.GpuAccessor.Log("Shader instruction Vabsdiff4 is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Vabsdiff4 is not implemented."); } public static void Vadd(EmitterContext context) { context.GetOp<InstVadd>(); - context.Config.GpuAccessor.Log("Shader instruction Vadd is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Vadd is not implemented."); } public static void Votevtg(EmitterContext context) { context.GetOp<InstVotevtg>(); - context.Config.GpuAccessor.Log("Shader instruction Votevtg is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Votevtg is not implemented."); } public static void Vset(EmitterContext context) { context.GetOp<InstVset>(); - context.Config.GpuAccessor.Log("Shader instruction Vset is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Vset is not implemented."); } public static void Vshl(EmitterContext context) { context.GetOp<InstVshl>(); - context.Config.GpuAccessor.Log("Shader instruction Vshl is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Vshl is not implemented."); } public static void Vshr(EmitterContext context) { context.GetOp<InstVshr>(); - context.Config.GpuAccessor.Log("Shader instruction Vshr is not implemented."); + context.TranslatorContext.GpuAccessor.Log("Shader instruction Vshr is not implemented."); } } } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index c7bd0fd63..542ec74ad 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.Instructions // Some of those attributes are per invocation, // so we should ignore any primitive vertex indexing for those. - bool hasPrimitiveVertex = AttributeMap.HasPrimitiveVertex(context.Config.Stage, op.O) && !op.P; + bool hasPrimitiveVertex = AttributeMap.HasPrimitiveVertex(context.TranslatorContext.Definitions.Stage, op.O) && !op.P; if (!op.Phys) { @@ -52,10 +52,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P) { - int offset = FixedFuncToUserAttribute(context.Config, op.Imm11 + index * 4, op.O); - - context.FlagAttributeRead(offset); - + int offset = FixedFuncToUserAttribute(context.TranslatorContext, op.Imm11 + index * 4, op.O); bool isOutput = op.O && CanLoadOutput(offset); if (!op.P && !isOutput && TryConvertIdToIndexForVulkan(context, offset, out Operand value)) @@ -69,10 +66,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - int offset = FixedFuncToUserAttribute(context.Config, op.Imm11 + index * 4, op.O); - - context.FlagAttributeRead(offset); - + int offset = FixedFuncToUserAttribute(context.TranslatorContext, op.Imm11 + index * 4, op.O); bool isOutput = op.O && CanLoadOutput(offset); context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, false)); @@ -98,7 +92,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand offset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase)); Operand vecIndex = context.ShiftRightU32(offset, Const(4)); Operand elemIndex = context.BitwiseAnd(context.ShiftRightU32(offset, Const(2)), Const(3)); - Operand invocationId = AttributeMap.HasInvocationId(context.Config.Stage, isOutput: true) + Operand invocationId = AttributeMap.HasInvocationId(context.TranslatorContext.Definitions.Stage, isOutput: true) ? context.Load(StorageKind.Input, IoVariable.InvocationId) : null; @@ -110,15 +104,12 @@ namespace Ryujinx.Graphics.Shader.Instructions int offset = op.Imm11 + index * 4; - if (!context.Config.IsUsedOutputAttribute(offset)) + if (!context.TranslatorContext.AttributeUsage.IsUsedOutputAttribute(offset)) { return; } - offset = FixedFuncToUserAttribute(context.Config, offset, isOutput: true); - - context.FlagAttributeWritten(offset); - + offset = FixedFuncToUserAttribute(context.TranslatorContext, offset, isOutput: true); AttributeMap.GenerateAttributeStore(context, offset, op.P, Register(rd)); } } @@ -128,8 +119,6 @@ namespace Ryujinx.Graphics.Shader.Instructions { InstIpa op = context.GetOp<InstIpa>(); - context.FlagAttributeRead(op.Imm10); - Operand res; bool isFixedFunc = false; @@ -151,7 +140,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { int index = (op.Imm10 - AttributeConsts.UserAttributeBase) >> 4; - if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective) + if (context.TranslatorContext.Definitions.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective) { res = context.FPMultiply(res, context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(3))); } @@ -162,11 +151,11 @@ namespace Ryujinx.Graphics.Shader.Instructions // because the shader code is not expecting scaled values. res = context.FPDivide(res, context.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.RenderScale), Const(0))); - if (op.Imm10 == AttributeConsts.PositionY && context.Config.Options.TargetApi != TargetApi.OpenGL) + if (op.Imm10 == AttributeConsts.PositionY && context.TranslatorContext.Options.TargetApi != TargetApi.OpenGL) { // If YNegate is enabled, we need to flip the fragment coordinates vertically, unless // the API supports changing the origin (only OpenGL does). - if (context.Config.GpuAccessor.QueryYNegateEnabled()) + if (context.TranslatorContext.Definitions.YNegateEnabled) { Operand viewportHeight = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.ViewportSize), Const(1)); @@ -174,7 +163,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } } } - else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug()) + else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug()) { // gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs. // This weird trick makes it behave. @@ -231,12 +220,12 @@ namespace Ryujinx.Graphics.Shader.Instructions { if (!(emit || cut)) { - context.Config.GpuAccessor.Log("Invalid OUT encoding."); + context.TranslatorContext.GpuAccessor.Log("Invalid OUT encoding."); } if (emit) { - if (context.Config.LastInVertexPipeline) + if (context.TranslatorContext.Definitions.LastInVertexPipeline) { context.PrepareForVertexReturn(out var tempXLocal, out var tempYLocal, out var tempZLocal); @@ -289,13 +278,13 @@ namespace Ryujinx.Graphics.Shader.Instructions { // TODO: If two sided rendering is enabled, then this should return // FrontColor if the fragment is front facing, and back color otherwise. - selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false)); + selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.TranslatorContext, attr, isOutput: false)); return true; } else if (attr == AttributeConsts.FogCoord) { // TODO: We likely need to emulate the fixed-function functionality for FogCoord here. - selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false)); + selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.TranslatorContext, attr, isOutput: false)); return true; } else if (attr >= AttributeConsts.BackColorDiffuseR && attr < AttributeConsts.ClipDistance0) @@ -305,7 +294,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd) { - selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.Config, attr, isOutput: false)); + selectedAttr = GenerateIpaLoad(context, FixedFuncToUserAttribute(context.TranslatorContext, attr, isOutput: false)); return true; } @@ -318,53 +307,44 @@ namespace Ryujinx.Graphics.Shader.Instructions return AttributeMap.GenerateAttributeLoad(context, null, offset, isOutput: false, isPerPatch: false); } - private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput) + private static int FixedFuncToUserAttribute(TranslatorContext translatorContext, int attr, bool isOutput) { - bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation(); + bool supportsLayerFromVertexOrTess = translatorContext.GpuAccessor.QueryHostSupportsLayerVertexTessellation(); int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1; - if (attr == AttributeConsts.Layer && config.Stage != ShaderStage.Geometry && !supportsLayerFromVertexOrTess) + if (attr == AttributeConsts.Layer && translatorContext.Definitions.Stage != ShaderStage.Geometry && !supportsLayerFromVertexOrTess) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput); - config.SetLayerOutputAttribute(attr); + attr = FixedFuncToUserAttribute(translatorContext, attr, AttributeConsts.Layer, 0, isOutput); + translatorContext.SetLayerOutputAttribute(attr); } else if (attr == AttributeConsts.FogCoord) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FogCoord, fixedStartAttr, isOutput); + attr = FixedFuncToUserAttribute(translatorContext, attr, AttributeConsts.FogCoord, fixedStartAttr, isOutput); } else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr + 1, isOutput); + attr = FixedFuncToUserAttribute(translatorContext, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr + 1, isOutput); } else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd) { - attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 5, isOutput); + attr = FixedFuncToUserAttribute(translatorContext, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 5, isOutput); } return attr; } - private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput) + private static int FixedFuncToUserAttribute(TranslatorContext translatorContext, int attr, int baseAttr, int baseIndex, bool isOutput) { int index = (attr - baseAttr) >> 4; - int userAttrIndex = config.GetFreeUserAttribute(isOutput, baseIndex + index); + int userAttrIndex = translatorContext.AttributeUsage.GetFreeUserAttribute(isOutput, baseIndex + index); if ((uint)userAttrIndex < Constants.MaxAttributes) { attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf); - - if (isOutput) - { - config.SetOutputUserAttributeFixedFunc(userAttrIndex); - } - else - { - config.SetInputUserAttributeFixedFunc(userAttrIndex); - } } else { - config.GpuAccessor.Log($"No enough user attributes for fixed attribute offset 0x{attr:X}."); + translatorContext.GpuAccessor.Log($"No enough user attributes for fixed attribute offset 0x{attr:X}."); } return attr; @@ -372,7 +352,7 @@ namespace Ryujinx.Graphics.Shader.Instructions private static bool TryConvertIdToIndexForVulkan(EmitterContext context, int attr, out Operand value) { - if (context.Config.Options.TargetApi == TargetApi.Vulkan) + if (context.TranslatorContext.Options.TargetApi == TargetApi.Vulkan) { if (attr == AttributeConsts.InstanceId) { diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs index ae5e078f6..8061aec28 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid barrier mode: {op.BarOp}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid barrier mode: {op.BarOp}."); } } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs index 8d59023ae..e7e0fba92 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs @@ -174,7 +174,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { if (dstType == IDstFmt.U64) { - context.Config.GpuAccessor.Log("Unimplemented 64-bits F2I."); + context.TranslatorContext.GpuAccessor.Log("Unimplemented 64-bits F2I."); } Instruction fpType = srcType.ToInstFPType(); @@ -297,7 +297,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { if ((srcType & ~ISrcDstFmt.S8) > ISrcDstFmt.U32 || (dstType & ~ISrcDstFmt.S8) > ISrcDstFmt.U32) { - context.Config.GpuAccessor.Log("Invalid I2I encoding."); + context.TranslatorContext.GpuAccessor.Log("Invalid I2I encoding."); return; } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs index ab643b5c6..04dbd20eb 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs @@ -462,7 +462,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (scaleConst.AsFloat() == 1f) { - context.Config.GpuAccessor.Log($"Invalid FP multiply scale \"{scale}\"."); + context.TranslatorContext.GpuAccessor.Log($"Invalid FP multiply scale \"{scale}\"."); } if (isFP64) diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs index 7462fc5ad..803aaa62d 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (context.CurrBlock.Successors.Count <= startIndex) { - context.Config.GpuAccessor.Log($"Failed to find targets for BRX instruction at 0x{currOp.Address:X}."); + context.TranslatorContext.GpuAccessor.Log($"Failed to find targets for BRX instruction at 0x{currOp.Address:X}."); return; } @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (context.IsNonMain) { - context.Config.GpuAccessor.Log("Invalid exit on non-main function."); + context.TranslatorContext.GpuAccessor.Log("Invalid exit on non-main function."); return; } @@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log("Invalid return on main function."); + context.TranslatorContext.GpuAccessor.Log("Invalid return on main function."); } } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs index c06f4671f..99922f7a1 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs @@ -371,7 +371,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Iadd3 has invalid component selection {part}."); + context.TranslatorContext.GpuAccessor.Log($"Iadd3 has invalid component selection {part}."); } return src; @@ -555,7 +555,7 @@ namespace Ryujinx.Graphics.Shader.Instructions modeConv = XmadCop.Csfu; break; default: - context.Config.GpuAccessor.Log($"Invalid XMAD mode \"{mode}\"."); + context.TranslatorContext.GpuAccessor.Log($"Invalid XMAD mode \"{mode}\"."); return; } @@ -634,7 +634,7 @@ namespace Ryujinx.Graphics.Shader.Instructions break; default: - context.Config.GpuAccessor.Log($"Invalid XMAD mode \"{mode}\"."); + context.TranslatorContext.GpuAccessor.Log($"Invalid XMAD mode \"{mode}\"."); return; } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 006c14b54..40129252a 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -26,9 +26,9 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Atoms(EmitterContext context) { - if (context.Config.Stage != ShaderStage.Compute) + if (context.TranslatorContext.Definitions.Stage != ShaderStage.Compute) { - context.Config.GpuAccessor.Log($"Atoms instruction is not valid on \"{context.Config.Stage}\" stage."); + context.TranslatorContext.GpuAccessor.Log($"Atoms instruction is not valid on \"{context.TranslatorContext.Definitions.Stage}\" stage."); return; } @@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Shader.Instructions _ => AtomSize.U32, }; - Operand id = Const(context.Config.ResourceManager.SharedMemoryId); + Operand id = Const(context.ResourceManager.SharedMemoryId); Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, id, offset, value); context.Copy(GetDest(op.Dest), res); @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.LsSize > LsSize2.B64) { - context.Config.GpuAccessor.Log($"Invalid LDC size: {op.LsSize}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid LDC size: {op.LsSize}."); return; } @@ -119,9 +119,9 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Lds(EmitterContext context) { - if (context.Config.Stage != ShaderStage.Compute) + if (context.TranslatorContext.Definitions.Stage != ShaderStage.Compute) { - context.Config.GpuAccessor.Log($"Lds instruction is not valid on \"{context.Config.Stage}\" stage."); + context.TranslatorContext.GpuAccessor.Log($"Lds instruction is not valid on \"{context.TranslatorContext.Definitions.Stage}\" stage."); return; } @@ -155,9 +155,9 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Sts(EmitterContext context) { - if (context.Config.Stage != ShaderStage.Compute) + if (context.TranslatorContext.Definitions.Stage != ShaderStage.Compute) { - context.Config.GpuAccessor.Log($"Sts instruction is not valid on \"{context.Config.Stage}\" stage."); + context.TranslatorContext.GpuAccessor.Log($"Sts instruction is not valid on \"{context.TranslatorContext.Definitions.Stage}\" stage."); return; } @@ -173,19 +173,19 @@ namespace Ryujinx.Graphics.Shader.Instructions if (slot.Type == OperandType.Constant) { - int binding = context.Config.ResourceManager.GetConstantBufferBinding(slot.Value); + int binding = context.ResourceManager.GetConstantBufferBinding(slot.Value); return context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex); } else { Operand value = Const(0); - uint cbUseMask = context.Config.GpuAccessor.QueryConstantBufferUse(); + uint cbUseMask = context.TranslatorContext.GpuAccessor.QueryConstantBufferUse(); while (cbUseMask != 0) { int cbIndex = BitOperations.TrailingZeroCount(cbUseMask); - int binding = context.Config.ResourceManager.GetConstantBufferBinding(cbIndex); + int binding = context.ResourceManager.GetConstantBufferBinding(cbIndex); Operand isCurrent = context.ICompareEqual(slot, Const(cbIndex)); Operand currentValue = context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex); @@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomOp.And: @@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomOp.Xor: @@ -239,7 +239,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomOp.Or: @@ -249,7 +249,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomOp.Max: @@ -263,7 +263,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomOp.Min: @@ -277,7 +277,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; } @@ -295,13 +295,13 @@ namespace Ryujinx.Graphics.Shader.Instructions { if (size > LsSize2.B128) { - context.Config.GpuAccessor.Log($"Invalid load size: {size}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid load size: {size}."); return; } int id = storageKind == StorageKind.LocalMemory - ? context.Config.ResourceManager.LocalMemoryId - : context.Config.ResourceManager.SharedMemoryId; + ? context.ResourceManager.LocalMemoryId + : context.ResourceManager.SharedMemoryId; bool isSmallInt = size < LsSize2.B32; int count = size switch @@ -376,13 +376,13 @@ namespace Ryujinx.Graphics.Shader.Instructions { if (size > LsSize2.B128) { - context.Config.GpuAccessor.Log($"Invalid store size: {size}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid store size: {size}."); return; } int id = storageKind == StorageKind.LocalMemory - ? context.Config.ResourceManager.LocalMemoryId - : context.Config.ResourceManager.SharedMemoryId; + ? context.ResourceManager.LocalMemoryId + : context.ResourceManager.SharedMemoryId; bool isSmallInt = size < LsSize2.B32; int count = size switch @@ -444,7 +444,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { if (size > LsSize2.B128) { - context.Config.GpuAccessor.Log($"Invalid store size: {size}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid store size: {size}."); return; } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index f6c3bf6f0..9d1c7d087 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -88,24 +88,24 @@ namespace Ryujinx.Graphics.Shader.Instructions break; case SReg.ThreadKill: - src = context.Config.Stage == ShaderStage.Fragment ? context.Load(StorageKind.Input, IoVariable.ThreadKill) : Const(0); + src = context.TranslatorContext.Definitions.Stage == ShaderStage.Fragment ? context.Load(StorageKind.Input, IoVariable.ThreadKill) : Const(0); break; case SReg.InvocationInfo: - if (context.Config.Stage != ShaderStage.Compute && context.Config.Stage != ShaderStage.Fragment) + if (context.TranslatorContext.Definitions.Stage != ShaderStage.Compute && context.TranslatorContext.Definitions.Stage != ShaderStage.Fragment) { // Note: Lowest 8-bits seems to contain some primitive index, // but it seems to be NVIDIA implementation specific as it's only used // to calculate ISBE offsets, so we can just keep it as zero. - if (context.Config.Stage == ShaderStage.TessellationControl || - context.Config.Stage == ShaderStage.TessellationEvaluation) + if (context.TranslatorContext.Definitions.Stage == ShaderStage.TessellationControl || + context.TranslatorContext.Definitions.Stage == ShaderStage.TessellationEvaluation) { src = context.ShiftLeft(context.Load(StorageKind.Input, IoVariable.PatchVertices), Const(16)); } else { - src = Const(context.Config.GpuAccessor.QueryPrimitiveTopology().ToInputVertices() << 16); + src = Const(context.TranslatorContext.Definitions.InputTopology.ToInputVertices() << 16); } } else diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs index 86f154bdb..5c079378e 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs @@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Shader.Instructions break; default: - context.Config.GpuAccessor.Log($"Invalid MUFU operation \"{op.MufuOp}\"."); + context.TranslatorContext.GpuAccessor.Log($"Invalid MUFU operation \"{op.MufuOp}\"."); break; } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index 6bf7de7f0..0aac0ffa8 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; @@ -194,7 +195,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.GpuAccessor.Log("Invalid image atomic sampler type."); + context.TranslatorContext.GpuAccessor.Log("Invalid image atomic sampler type."); return; } @@ -258,7 +259,7 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: FP and 64-bit formats. TextureFormat format = size == SuatomSize.Sd32 || size == SuatomSize.Sd64 - ? (isBindless ? TextureFormat.Unknown : context.Config.GetTextureFormatAtomic(imm)) + ? (isBindless ? TextureFormat.Unknown : ShaderProperties.GetTextureFormatAtomic(context.TranslatorContext.GpuAccessor, imm)) : GetTextureFormat(size); if (compareAndSwap) @@ -277,7 +278,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, @@ -309,13 +310,11 @@ namespace Ryujinx.Graphics.Shader.Instructions return; } - context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); - SamplerType type = ConvertSamplerType(dimensions); if (type == SamplerType.None) { - context.Config.GpuAccessor.Log("Invalid image store sampler type."); + context.TranslatorContext.GpuAccessor.Log("Invalid image store sampler type."); return; } @@ -388,9 +387,9 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - TextureFormat format = isBindless ? TextureFormat.Unknown : context.Config.GetTextureFormat(handle); + TextureFormat format = isBindless ? TextureFormat.Unknown : ShaderProperties.GetTextureFormat(context.TranslatorContext.GpuAccessor, handle); - int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageLoad, type, format, @@ -433,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureFormat format = GetTextureFormat(size); - int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageLoad, type, format, @@ -477,7 +476,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.GpuAccessor.Log("Invalid image reduction sampler type."); + context.TranslatorContext.GpuAccessor.Log("Invalid image reduction sampler type."); return; } @@ -539,7 +538,7 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: FP and 64-bit formats. TextureFormat format = size == SuatomSize.Sd32 || size == SuatomSize.Sd64 - ? (isBindless ? TextureFormat.Unknown : context.Config.GetTextureFormatAtomic(imm)) + ? (isBindless ? TextureFormat.Unknown : ShaderProperties.GetTextureFormatAtomic(context.TranslatorContext.GpuAccessor, imm)) : GetTextureFormat(size); sourcesList.Add(Rb()); @@ -553,7 +552,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, @@ -582,7 +581,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.GpuAccessor.Log("Invalid image store sampler type."); + context.TranslatorContext.GpuAccessor.Log("Invalid image store sampler type."); return; } @@ -647,7 +646,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (!isBindless) { - format = context.Config.GetTextureFormat(imm); + format = ShaderProperties.GetTextureFormat(context.TranslatorContext.GpuAccessor, imm); } } else @@ -680,7 +679,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Coherent; } - int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.ImageStore, type, format, diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 7d3d22d8a..1b2673abf 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -57,8 +57,6 @@ namespace Ryujinx.Graphics.Shader.Instructions { InstTld op = context.GetOp<InstTld>(); - context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); - var lod = op.Lod ? Lod.Ll : Lod.Lz; EmitTex(context, TextureFlags.IntCoords, op.Dim, lod, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Ms, false, op.Toff); @@ -68,8 +66,6 @@ namespace Ryujinx.Graphics.Shader.Instructions { InstTldB op = context.GetOp<InstTldB>(); - context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); - var flags = TextureFlags.IntCoords | TextureFlags.Bindless; var lod = op.Lod ? Lod.Ll : Lod.Lz; @@ -224,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { // For bindless, we don't have any way to know the texture type, // so we assume it's texture buffer when the sampler type is 1D, since that's more common. - bool isTypeBuffer = isBindless || context.Config.GpuAccessor.QuerySamplerType(imm) == SamplerType.TextureBuffer; + bool isTypeBuffer = isBindless || context.TranslatorContext.GpuAccessor.QuerySamplerType(imm) == SamplerType.TextureBuffer; if (isTypeBuffer) { type = SamplerType.TextureBuffer; @@ -386,7 +382,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.GpuAccessor.Log("Invalid texture sampler type."); + context.TranslatorContext.GpuAccessor.Log("Invalid texture sampler type."); return; } @@ -478,16 +474,14 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.GpuAccessor.Log("Invalid texel fetch sampler type."); + context.TranslatorContext.GpuAccessor.Log("Invalid texel fetch sampler type."); return; } - context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); - flags = ConvertTextureFlags(tldsOp.Target) | TextureFlags.IntCoords; if (tldsOp.Target == TldsTarget.Texture1DLodZero && - context.Config.GpuAccessor.QuerySamplerType(tldsOp.TidB) == SamplerType.TextureBuffer) + context.TranslatorContext.GpuAccessor.QuerySamplerType(tldsOp.TidB) == SamplerType.TextureBuffer) { type = SamplerType.TextureBuffer; flags &= ~TextureFlags.LodLevel; @@ -884,7 +878,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return Register(dest++, RegisterType.Gpr); } - int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.Lod, type, TextureFormat.Unknown, @@ -1065,8 +1059,6 @@ namespace Ryujinx.Graphics.Shader.Instructions return; } - context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); - Operand Ra() { if (srcA > RegisterConsts.RegisterZeroIndex) @@ -1106,12 +1098,12 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - type = context.Config.GpuAccessor.QuerySamplerType(imm); + type = context.TranslatorContext.GpuAccessor.QuerySamplerType(imm); } TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None; - int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.TextureSize, type, TextureFormat.Unknown, @@ -1147,7 +1139,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand[] dests, Operand[] sources) { - int binding = flags.HasFlag(TextureFlags.Bindless) ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( + int binding = flags.HasFlag(TextureFlags.Bindless) ? 0 : context.ResourceManager.GetTextureOrImageBinding( Instruction.TextureSample, type, TextureFormat.Unknown, diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs index 67dc3398b..a84944e43 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.GpuAccessor.Log($"Invalid vote operation: {op.VoteMode}."); + context.TranslatorContext.GpuAccessor.Log($"Invalid vote operation: {op.VoteMode}."); } if (op.Dest != RegisterConsts.RegisterZeroIndex) diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs index 4fb5d02b3..5d46ab498 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr // When debug mode is enabled, we disable expression propagation // (this makes comparison with the disassembly easier). - if (!context.Config.Options.Flags.HasFlag(TranslationFlags.DebugMode)) + if (!context.DebugMode) { AstBlockVisitor visitor = new(mainBlock); diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index b7e379c6b..8c12c2aaf 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -18,8 +18,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories; public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories; - public readonly bool OriginUpperLeft; - public ShaderProperties() { _constantBuffers = new Dictionary<int, BufferDefinition>(); @@ -30,29 +28,24 @@ namespace Ryujinx.Graphics.Shader.StructuredIr _sharedMemories = new Dictionary<int, MemoryDefinition>(); } - public ShaderProperties(bool originUpperLeft) : this() + public void AddOrUpdateConstantBuffer(BufferDefinition definition) { - OriginUpperLeft = originUpperLeft; + _constantBuffers[definition.Binding] = definition; } - public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition) + public void AddOrUpdateStorageBuffer(BufferDefinition definition) { - _constantBuffers[binding] = definition; + _storageBuffers[definition.Binding] = definition; } - public void AddOrUpdateStorageBuffer(int binding, BufferDefinition definition) + public void AddOrUpdateTexture(TextureDefinition definition) { - _storageBuffers[binding] = definition; + _textures[definition.Binding] = definition; } - public void AddOrUpdateTexture(int binding, TextureDefinition descriptor) + public void AddOrUpdateImage(TextureDefinition definition) { - _textures[binding] = descriptor; - } - - public void AddOrUpdateImage(int binding, TextureDefinition descriptor) - { - _images[binding] = descriptor; + _images[definition.Binding] = definition; } public int AddLocalMemory(MemoryDefinition definition) @@ -70,5 +63,48 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return id; } + + public static TextureFormat GetTextureFormat(IGpuAccessor gpuAccessor, int handle, int cbufSlot = -1) + { + // When the formatted load extension is supported, we don't need to + // specify a format, we can just declare it without a format and the GPU will handle it. + if (gpuAccessor.QueryHostSupportsImageLoadFormatted()) + { + return TextureFormat.Unknown; + } + + var format = gpuAccessor.QueryTextureFormat(handle, cbufSlot); + + if (format == TextureFormat.Unknown) + { + gpuAccessor.Log($"Unknown format for texture {handle}."); + + format = TextureFormat.R8G8B8A8Unorm; + } + + return format; + } + + private static bool FormatSupportsAtomic(TextureFormat format) + { + return format == TextureFormat.R32Sint || format == TextureFormat.R32Uint; + } + + public static TextureFormat GetTextureFormatAtomic(IGpuAccessor gpuAccessor, int handle, int cbufSlot = -1) + { + // Atomic image instructions do not support GL_EXT_shader_image_load_formatted, + // and must have a type specified. Default to R32Sint if not available. + + var format = gpuAccessor.QueryTextureFormat(handle, cbufSlot); + + if (!FormatSupportsAtomic(format)) + { + gpuAccessor.Log($"Unsupported format for texture {handle}: {format}."); + + format = TextureFormat.R32Sint; + } + + return format; + } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 87acedf62..862fef126 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -8,9 +8,14 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { static class StructuredProgram { - public static StructuredProgramInfo MakeStructuredProgram(IReadOnlyList<Function> functions, ShaderConfig config) + public static StructuredProgramInfo MakeStructuredProgram( + IReadOnlyList<Function> functions, + AttributeUsage attributeUsage, + ShaderDefinitions definitions, + ResourceManager resourceManager, + bool debugMode) { - StructuredProgramContext context = new(config); + StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode); for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { @@ -82,13 +87,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr int location = 0; int component = 0; - if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput)) + if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) { location = operation.GetSource(1).Value; if (operation.SourcesCount > 2 && operation.GetSource(2).Type == OperandType.Constant && - context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput)) + context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput)) { component = operation.GetSource(2).Value; } @@ -98,7 +103,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else if (storageKind == StorageKind.ConstantBuffer && operation.GetSource(0).Type == OperandType.Constant) { - context.Config.ResourceManager.SetUsedConstantBufferBinding(operation.GetSource(0).Value); + context.ResourceManager.SetUsedConstantBufferBinding(operation.GetSource(0).Value); } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index 019fc332b..045662a1e 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -28,17 +28,25 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public StructuredProgramInfo Info { get; } - public ShaderConfig Config { get; } + public ShaderDefinitions Definitions { get; } + public ResourceManager ResourceManager { get; } + public bool DebugMode { get; } - public StructuredProgramContext(ShaderConfig config) + public StructuredProgramContext( + AttributeUsage attributeUsage, + ShaderDefinitions definitions, + ResourceManager resourceManager, + bool debugMode) { Info = new StructuredProgramInfo(); - Config = config; + Definitions = definitions; + ResourceManager = resourceManager; + DebugMode = debugMode; - if (config.GpPassthrough) + if (definitions.GpPassthrough) { - int passthroughAttributes = config.PassthroughAttributes; + int passthroughAttributes = attributeUsage.PassthroughAttributes; while (passthroughAttributes != 0) { int index = BitOperations.TrailingZeroCount(passthroughAttributes); @@ -52,11 +60,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.PointSize)); Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.ClipDistance)); } - else if (config.Stage == ShaderStage.Fragment) - { - // Potentially used for texture coordinate scaling. - Info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)); - } } public void EnterFunction( @@ -304,11 +307,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr int cbufSlot = operand.GetCbufSlot(); int cbufOffset = operand.GetCbufOffset(); - int binding = Config.ResourceManager.GetConstantBufferBinding(cbufSlot); + int binding = ResourceManager.GetConstantBufferBinding(cbufSlot); int vecIndex = cbufOffset >> 2; int elemIndex = cbufOffset & 3; - Config.ResourceManager.SetUsedConstantBufferBinding(binding); + ResourceManager.SetUsedConstantBufferBinding(binding); IAstNode[] sources = new IAstNode[] { diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index 4f18c7fd7..ded2f2a89 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -2,22 +2,6 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.StructuredIr { - readonly struct TransformFeedbackOutput - { - public readonly bool Valid; - public readonly int Buffer; - public readonly int Offset; - public readonly int Stride; - - public TransformFeedbackOutput(int buffer, int offset, int stride) - { - Valid = true; - Buffer = buffer; - Offset = offset; - Stride = stride; - } - } - class StructuredProgramInfo { public List<StructuredFunction> Functions { get; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/AttributeUsage.cs b/src/Ryujinx.Graphics.Shader/Translation/AttributeUsage.cs new file mode 100644 index 000000000..9dab9fdf9 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/AttributeUsage.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Ryujinx.Graphics.Shader.Translation +{ + class AttributeUsage + { + public bool NextUsesFixedFuncAttributes { get; private set; } + public int UsedInputAttributes { get; private set; } + public int UsedOutputAttributes { get; private set; } + public HashSet<int> UsedInputAttributesPerPatch { get; } + public HashSet<int> UsedOutputAttributesPerPatch { get; } + public HashSet<int> NextUsedInputAttributesPerPatch { get; private set; } + public int PassthroughAttributes { get; private set; } + private int _nextUsedInputAttributes; + private int _thisUsedInputAttributes; + private Dictionary<int, int> _perPatchAttributeLocations; + private readonly IGpuAccessor _gpuAccessor; + + public UInt128 NextInputAttributesComponents { get; private set; } + public UInt128 ThisInputAttributesComponents { get; private set; } + + public AttributeUsage(IGpuAccessor gpuAccessor) + { + _gpuAccessor = gpuAccessor; + + UsedInputAttributesPerPatch = new(); + UsedOutputAttributesPerPatch = new(); + } + + public void SetInputUserAttribute(int index, int component) + { + int mask = 1 << index; + + UsedInputAttributes |= mask; + _thisUsedInputAttributes |= mask; + ThisInputAttributesComponents |= UInt128.One << (index * 4 + component); + } + + public void SetInputUserAttributePerPatch(int index) + { + UsedInputAttributesPerPatch.Add(index); + } + + public void SetOutputUserAttribute(int index) + { + UsedOutputAttributes |= 1 << index; + } + + public void SetOutputUserAttributePerPatch(int index) + { + UsedOutputAttributesPerPatch.Add(index); + } + + public void MergeFromtNextStage(bool gpPassthrough, bool nextUsesFixedFunctionAttributes, AttributeUsage nextStage) + { + NextInputAttributesComponents = nextStage.ThisInputAttributesComponents; + NextUsedInputAttributesPerPatch = nextStage.UsedInputAttributesPerPatch; + NextUsesFixedFuncAttributes = nextUsesFixedFunctionAttributes; + MergeOutputUserAttributes(gpPassthrough, nextStage.UsedInputAttributes, nextStage.UsedInputAttributesPerPatch); + + if (UsedOutputAttributesPerPatch.Count != 0) + { + // Regular and per-patch input/output locations can't overlap, + // so we must assign on our location using unused regular input/output locations. + + Dictionary<int, int> locationsMap = new(); + + int freeMask = ~UsedOutputAttributes; + + foreach (int attr in UsedOutputAttributesPerPatch) + { + int location = BitOperations.TrailingZeroCount(freeMask); + if (location == 32) + { + _gpuAccessor.Log($"No enough free locations for patch input/output 0x{attr:X}."); + break; + } + + locationsMap.Add(attr, location); + freeMask &= ~(1 << location); + } + + // Both stages must agree on the locations, so use the same "map" for both. + _perPatchAttributeLocations = locationsMap; + nextStage._perPatchAttributeLocations = locationsMap; + } + } + + private void MergeOutputUserAttributes(bool gpPassthrough, int mask, IEnumerable<int> perPatch) + { + _nextUsedInputAttributes = mask; + + if (gpPassthrough) + { + PassthroughAttributes = mask & ~UsedOutputAttributes; + } + else + { + UsedOutputAttributes |= mask; + UsedOutputAttributesPerPatch.UnionWith(perPatch); + } + } + + public int GetPerPatchAttributeLocation(int index) + { + if (_perPatchAttributeLocations == null || !_perPatchAttributeLocations.TryGetValue(index, out int location)) + { + return index; + } + + return location; + } + + public bool IsUsedOutputAttribute(int attr) + { + // The check for fixed function attributes on the next stage is conservative, + // returning false if the output is just not used by the next stage is also valid. + if (NextUsesFixedFuncAttributes && + attr >= AttributeConsts.UserAttributeBase && + attr < AttributeConsts.UserAttributeEnd) + { + int index = (attr - AttributeConsts.UserAttributeBase) >> 4; + return (_nextUsedInputAttributes & (1 << index)) != 0; + } + + return true; + } + + public int GetFreeUserAttribute(bool isOutput, int index) + { + int useMask = isOutput ? _nextUsedInputAttributes : _thisUsedInputAttributes; + int bit = -1; + + while (useMask != -1) + { + bit = BitOperations.TrailingZeroCount(~useMask); + + if (bit == 32) + { + bit = -1; + break; + } + else if (index < 1) + { + break; + } + + useMask |= 1 << bit; + index--; + } + + return bit; + } + + public void SetAllInputUserAttributes() + { + UsedInputAttributes |= Constants.AllAttributesMask; + ThisInputAttributesComponents |= ~UInt128.Zero >> (128 - Constants.MaxAttributes * 4); + } + + public void SetAllOutputUserAttributes() + { + UsedOutputAttributes |= Constants.AllAttributesMask; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 614b275ba..43263dd4b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -11,7 +11,8 @@ namespace Ryujinx.Graphics.Shader.Translation class EmitterContext { public DecodedProgram Program { get; } - public ShaderConfig Config { get; } + public TranslatorContext TranslatorContext { get; } + public ResourceManager ResourceManager { get; } public bool IsNonMain { get; } @@ -54,10 +55,15 @@ namespace Ryujinx.Graphics.Shader.Translation _labels = new Dictionary<ulong, BlockLabel>(); } - public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) : this() + public EmitterContext( + TranslatorContext translatorContext, + ResourceManager resourceManager, + DecodedProgram program, + bool isNonMain) : this() { + TranslatorContext = translatorContext; + ResourceManager = resourceManager; Program = program; - Config = config; IsNonMain = isNonMain; EmitStart(); @@ -65,12 +71,12 @@ namespace Ryujinx.Graphics.Shader.Translation private void EmitStart() { - if (Config.Stage == ShaderStage.Vertex && - Config.Options.TargetApi == TargetApi.Vulkan && - (Config.Options.Flags & TranslationFlags.VertexA) == 0) + if (TranslatorContext.Definitions.Stage == ShaderStage.Vertex && + TranslatorContext.Options.TargetApi == TargetApi.Vulkan && + (TranslatorContext.Options.Flags & TranslationFlags.VertexA) == 0) { // Vulkan requires the point size to be always written on the shader if the primitive topology is points. - this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(Config.GpuAccessor.QueryPointSize())); + this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(TranslatorContext.Definitions.PointSize)); } } @@ -115,49 +121,6 @@ namespace Ryujinx.Graphics.Shader.Translation _operations.Add(operation); } - public void FlagAttributeRead(int attribute) - { - if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId) - { - Config.SetUsedFeature(FeatureFlags.InstanceId); - } - else if (Config.Stage == ShaderStage.Fragment) - { - switch (attribute) - { - case AttributeConsts.PositionX: - case AttributeConsts.PositionY: - Config.SetUsedFeature(FeatureFlags.FragCoordXY); - break; - } - } - } - - public void FlagAttributeWritten(int attribute) - { - if (Config.Stage == ShaderStage.Vertex) - { - switch (attribute) - { - case AttributeConsts.ClipDistance0: - case AttributeConsts.ClipDistance1: - case AttributeConsts.ClipDistance2: - case AttributeConsts.ClipDistance3: - case AttributeConsts.ClipDistance4: - case AttributeConsts.ClipDistance5: - case AttributeConsts.ClipDistance6: - case AttributeConsts.ClipDistance7: - Config.SetClipDistanceWritten((attribute - AttributeConsts.ClipDistance0) / 4); - break; - } - } - - if (Config.Stage != ShaderStage.Fragment && attribute == AttributeConsts.Layer) - { - Config.SetUsedFeature(FeatureFlags.RtLayer); - } - } - public void MarkLabel(Operand label) { Add(Instruction.MarkLabel, label); @@ -203,14 +166,14 @@ namespace Ryujinx.Graphics.Shader.Translation public void PrepareForVertexReturn() { - if (!Config.GpuAccessor.QueryHostSupportsTransformFeedback() && Config.GpuAccessor.QueryTransformFeedbackEnabled()) + if (!TranslatorContext.GpuAccessor.QueryHostSupportsTransformFeedback() && TranslatorContext.GpuAccessor.QueryTransformFeedbackEnabled()) { Operand vertexCount = this.Load(StorageKind.StorageBuffer, Constants.TfeInfoBinding, Const(1)); for (int tfbIndex = 0; tfbIndex < Constants.TfeBuffersCount; tfbIndex++) { - var locations = Config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); - var stride = Config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); + var locations = TranslatorContext.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); + var stride = TranslatorContext.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); Operand baseOffset = this.Load(StorageKind.StorageBuffer, Constants.TfeInfoBinding, Const(0), Const(tfbIndex)); Operand baseVertex = this.Load(StorageKind.Input, IoVariable.BaseVertex); @@ -242,7 +205,7 @@ namespace Ryujinx.Graphics.Shader.Translation } } - if (Config.GpuAccessor.QueryViewportTransformDisable()) + if (TranslatorContext.Definitions.ViewportTransformDisable) { Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)); Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)); @@ -254,7 +217,7 @@ namespace Ryujinx.Graphics.Shader.Translation this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne)); } - if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl()) + if (TranslatorContext.Definitions.DepthMode && !TranslatorContext.GpuAccessor.QueryHostSupportsDepthClipControl()) { Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)); Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3)); @@ -263,12 +226,10 @@ namespace Ryujinx.Graphics.Shader.Translation this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW)); } - if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute) + if (TranslatorContext.Definitions.Stage != ShaderStage.Geometry && TranslatorContext.HasLayerInputAttribute) { - Config.SetUsedFeature(FeatureFlags.RtLayer); - - int attrVecIndex = Config.GpLayerInputAttribute >> 2; - int attrComponentIndex = Config.GpLayerInputAttribute & 3; + int attrVecIndex = TranslatorContext.GpLayerInputAttribute >> 2; + int attrComponentIndex = TranslatorContext.GpLayerInputAttribute & 3; Operand layer = this.Load(StorageKind.Output, IoVariable.UserDefined, null, Const(attrVecIndex), Const(attrComponentIndex)); @@ -278,7 +239,7 @@ namespace Ryujinx.Graphics.Shader.Translation public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal) { - if (Config.GpuAccessor.QueryViewportTransformDisable()) + if (TranslatorContext.Definitions.ViewportTransformDisable) { oldXLocal = Local(); this.Copy(oldXLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(0))); @@ -291,7 +252,7 @@ namespace Ryujinx.Graphics.Shader.Translation oldYLocal = null; } - if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl()) + if (TranslatorContext.Definitions.DepthMode && !TranslatorContext.GpuAccessor.QueryHostSupportsDepthClipControl()) { oldZLocal = Local(); this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2))); @@ -311,13 +272,13 @@ namespace Ryujinx.Graphics.Shader.Translation return true; } - if (Config.LastInVertexPipeline && - (Config.Stage == ShaderStage.Vertex || Config.Stage == ShaderStage.TessellationEvaluation) && - (Config.Options.Flags & TranslationFlags.VertexA) == 0) + if (TranslatorContext.Definitions.LastInVertexPipeline && + (TranslatorContext.Definitions.Stage == ShaderStage.Vertex || TranslatorContext.Definitions.Stage == ShaderStage.TessellationEvaluation) && + (TranslatorContext.Options.Flags & TranslationFlags.VertexA) == 0) { PrepareForVertexReturn(); } - else if (Config.Stage == ShaderStage.Geometry) + else if (TranslatorContext.Definitions.Stage == ShaderStage.Geometry) { void WritePositionOutput(int primIndex) { @@ -345,20 +306,19 @@ namespace Ryujinx.Graphics.Shader.Translation this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(3), w); } - if (Config.GpPassthrough && !Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) + if (TranslatorContext.Definitions.GpPassthrough && !TranslatorContext.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { - int inputVertices = Config.GpuAccessor.QueryPrimitiveTopology().ToInputVertices(); + int inputVertices = TranslatorContext.Definitions.InputTopology.ToInputVertices(); for (int primIndex = 0; primIndex < inputVertices; primIndex++) { WritePositionOutput(primIndex); - int passthroughAttributes = Config.PassthroughAttributes; + int passthroughAttributes = TranslatorContext.AttributeUsage.PassthroughAttributes; while (passthroughAttributes != 0) { int index = BitOperations.TrailingZeroCount(passthroughAttributes); WriteUserDefinedOutput(index, primIndex); - Config.SetOutputUserAttribute(index); passthroughAttributes &= ~(1 << index); } @@ -368,20 +328,20 @@ namespace Ryujinx.Graphics.Shader.Translation this.EndPrimitive(); } } - else if (Config.Stage == ShaderStage.Fragment) + else if (TranslatorContext.Definitions.Stage == ShaderStage.Fragment) { GenerateAlphaToCoverageDitherDiscard(); - bool supportsBgra = Config.GpuAccessor.QueryHostSupportsBgraFormat(); + bool supportsBgra = TranslatorContext.GpuAccessor.QueryHostSupportsBgraFormat(); - if (Config.OmapDepth) + if (TranslatorContext.Definitions.OmapDepth) { - Operand src = Register(Config.GetDepthRegister(), RegisterType.Gpr); + Operand src = Register(TranslatorContext.GetDepthRegister(), RegisterType.Gpr); this.Store(StorageKind.Output, IoVariable.FragmentOutputDepth, null, src); } - AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare(); + AlphaTestOp alphaTestOp = TranslatorContext.Definitions.AlphaTestCompare; if (alphaTestOp != AlphaTestOp.Always) { @@ -389,7 +349,7 @@ namespace Ryujinx.Graphics.Shader.Translation { this.Discard(); } - else if ((Config.OmapTargets & 8) != 0) + else if ((TranslatorContext.Definitions.OmapTargets & 8) != 0) { Instruction comparator = alphaTestOp switch { @@ -405,7 +365,7 @@ namespace Ryujinx.Graphics.Shader.Translation Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\"."); Operand alpha = Register(3, RegisterType.Gpr); - Operand alphaRef = ConstF(Config.GpuAccessor.QueryAlphaTestReference()); + Operand alphaRef = ConstF(TranslatorContext.Definitions.AlphaTestReference); Operand alphaPass = Add(Instruction.FP32 | comparator, Local(), alpha, alphaRef); Operand alphaPassLabel = Label(); @@ -427,7 +387,7 @@ namespace Ryujinx.Graphics.Shader.Translation { for (int component = 0; component < 4; component++) { - bool componentEnabled = (Config.OmapTargets & (1 << (rtIndex * 4 + component))) != 0; + bool componentEnabled = (TranslatorContext.Definitions.OmapTargets & (1 << (rtIndex * 4 + component))) != 0; if (!componentEnabled) { continue; @@ -460,10 +420,9 @@ namespace Ryujinx.Graphics.Shader.Translation } } - bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0; + bool targetEnabled = (TranslatorContext.Definitions.OmapTargets & (0xf << (rtIndex * 4))) != 0; if (targetEnabled) { - Config.SetOutputUserAttribute(rtIndex); regIndexBase += 4; } } @@ -475,7 +434,7 @@ namespace Ryujinx.Graphics.Shader.Translation private void GenerateAlphaToCoverageDitherDiscard() { // If the feature is disabled, or alpha is not written, then we're done. - if (!Config.GpuAccessor.QueryAlphaToCoverageDitherEnable() || (Config.OmapTargets & 8) == 0) + if (!TranslatorContext.Definitions.AlphaToCoverageDitherEnable || (TranslatorContext.Definitions.OmapTargets & 8) == 0) { return; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 9d4d032af..5b7226acd 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -12,15 +12,12 @@ namespace Ryujinx.Graphics.Shader.Translation None = 0, // Affected by resolution scaling. - IntegerSampling = 1 << 0, FragCoordXY = 1 << 1, Bindless = 1 << 2, InstanceId = 1 << 3, DrawParameters = 1 << 4, RtLayer = 1 << 5, - IaIndexing = 1 << 7, - OaIndexing = 1 << 8, FixedFuncAttr = 1 << 9, LocalMemory = 1 << 10, SharedMemory = 1 << 11, diff --git a/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs b/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs new file mode 100644 index 000000000..2523272b0 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs @@ -0,0 +1,34 @@ +namespace Ryujinx.Graphics.Shader.Translation +{ + class HostCapabilities + { + public readonly bool ReducedPrecision; + public readonly bool SupportsFragmentShaderInterlock; + public readonly bool SupportsFragmentShaderOrderingIntel; + public readonly bool SupportsGeometryShaderPassthrough; + public readonly bool SupportsShaderBallot; + public readonly bool SupportsShaderBarrierDivergence; + public readonly bool SupportsTextureShadowLod; + public readonly bool SupportsViewportMask; + + public HostCapabilities( + bool reducedPrecision, + bool supportsFragmentShaderInterlock, + bool supportsFragmentShaderOrderingIntel, + bool supportsGeometryShaderPassthrough, + bool supportsShaderBallot, + bool supportsShaderBarrierDivergence, + bool supportsTextureShadowLod, + bool supportsViewportMask) + { + ReducedPrecision = reducedPrecision; + SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; + SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; + SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; + SupportsShaderBallot = supportsShaderBallot; + SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; + SupportsTextureShadowLod = supportsTextureShadowLod; + SupportsViewportMask = supportsViewportMask; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index bf087affb..43d98d3cb 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -1,12 +1,13 @@ using Ryujinx.Graphics.Shader.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; using System.Collections.Generic; namespace Ryujinx.Graphics.Shader.Translation.Optimizations { class BindlessElimination { - public static void RunPass(BasicBlock block, ShaderConfig config) + public static void RunPass(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor) { // We can turn a bindless into regular access by recognizing the pattern // produced by the compiler for separate texture and sampler. @@ -43,7 +44,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (bindlessHandle.Type == OperandType.ConstantBuffer) { - SetHandle(config, texOp, bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot(), rewriteSamplerType, isImage: false); + SetHandle( + resourceManager, + gpuAccessor, + texOp, + bindlessHandle.GetCbufOffset(), + bindlessHandle.GetCbufSlot(), + rewriteSamplerType, + isImage: false); + continue; } @@ -140,7 +149,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (handleType == TextureHandleType.SeparateConstantSamplerHandle) { SetHandle( - config, + resourceManager, + gpuAccessor, texOp, TextureHandle.PackOffsets(src0.GetCbufOffset(), ((src1.Value >> 20) & 0xfff), handleType), TextureHandle.PackSlots(src0.GetCbufSlot(), 0), @@ -150,7 +160,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations else if (src1.Type == OperandType.ConstantBuffer) { SetHandle( - config, + resourceManager, + gpuAccessor, texOp, TextureHandle.PackOffsets(src0.GetCbufOffset(), src1.GetCbufOffset(), handleType), TextureHandle.PackSlots(src0.GetCbufSlot(), src1.GetCbufSlot()), @@ -173,17 +184,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { if (texOp.Inst == Instruction.ImageAtomic) { - texOp.Format = config.GetTextureFormatAtomic(cbufOffset, cbufSlot); + texOp.Format = ShaderProperties.GetTextureFormatAtomic(gpuAccessor, cbufOffset, cbufSlot); } else { - texOp.Format = config.GetTextureFormat(cbufOffset, cbufSlot); + texOp.Format = ShaderProperties.GetTextureFormat(gpuAccessor, cbufOffset, cbufSlot); } } bool rewriteSamplerType = texOp.Type == SamplerType.TextureBuffer; - SetHandle(config, texOp, cbufOffset, cbufSlot, rewriteSamplerType, isImage: true); + SetHandle(resourceManager, gpuAccessor, texOp, cbufOffset, cbufSlot, rewriteSamplerType, isImage: true); } } } @@ -220,11 +231,18 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return null; } - private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot, bool rewriteSamplerType, bool isImage) + private static void SetHandle( + ResourceManager resourceManager, + IGpuAccessor gpuAccessor, + TextureOperation texOp, + int cbufOffset, + int cbufSlot, + bool rewriteSamplerType, + bool isImage) { if (rewriteSamplerType) { - SamplerType newType = config.GpuAccessor.QuerySamplerType(cbufOffset, cbufSlot); + SamplerType newType = gpuAccessor.QuerySamplerType(cbufOffset, cbufSlot); if (texOp.Inst.IsTextureQuery()) { @@ -253,7 +271,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - int binding = config.ResourceManager.GetTextureOrImageBinding( + int binding = resourceManager.GetTextureOrImageBinding( texOp.Inst, texOp.Type, texOp.Format, diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs index 4b1bf76e5..2bd31fe1b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { private const int NvnTextureBufferIndex = 2; - public static void RunPass(BasicBlock block, ShaderConfig config) + public static void RunPass(BasicBlock block, ResourceManager resourceManager) { // We can turn a bindless texture access into a indexed access, // as long the following conditions are true: @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand ldcSrc0 = handleAsgOp.GetSource(0); if (ldcSrc0.Type != OperandType.Constant || - !config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) || + !resourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) || src0CbufSlot != NvnTextureBufferIndex) { continue; @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - TurnIntoIndexed(config, texOp, addSrc1.Value / 4); + TurnIntoIndexed(resourceManager, texOp, addSrc1.Value / 4); Operand index = Local(); @@ -102,9 +102,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle) + private static void TurnIntoIndexed(ResourceManager resourceManager, TextureOperation texOp, int handle) { - int binding = config.ResourceManager.GetTextureOrImageBinding( + int binding = resourceManager.GetTextureOrImageBinding( texOp.Inst, texOp.Type | SamplerType.Indexed, texOp.Format, diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs index 0cca0ac6c..3941303b1 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class ConstantFolding { - public static void RunPass(ShaderConfig config, Operation operation) + public static void RunPass(ResourceManager resourceManager, Operation operation) { if (!AreAllSourcesConstant(operation)) { @@ -158,7 +158,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations int binding = operation.GetSource(0).Value; int fieldIndex = operation.GetSource(1).Value; - if (config.ResourceManager.TryGetConstantBufferSlot(binding, out int cbufSlot) && fieldIndex == 0) + if (resourceManager.TryGetConstantBufferSlot(binding, out int cbufSlot) && fieldIndex == 0) { int vecIndex = operation.GetSource(2).Value; int elemIndex = operation.GetSource(3).Value; diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 2433aeb20..0f043f772 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -205,7 +205,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } } - public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) + public static void RunPass( + HelperFunctionManager hfm, + BasicBlock[] blocks, + ResourceManager resourceManager, + IGpuAccessor gpuAccessor, + TargetLanguage targetLanguage) { GtsContext gtsContext = new(hfm); @@ -220,14 +225,20 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (IsGlobalMemory(operation.StorageKind)) { - LinkedListNode<INode> nextNode = ReplaceGlobalMemoryWithStorage(gtsContext, config, block, node); + LinkedListNode<INode> nextNode = ReplaceGlobalMemoryWithStorage( + gtsContext, + resourceManager, + gpuAccessor, + targetLanguage, + block, + node); if (nextNode == null) { // The returned value being null means that the global memory replacement failed, // so we just make loads read 0 and stores do nothing. - config.GpuAccessor.Log($"Failed to reserve storage buffer for global memory operation \"{operation.Inst}\"."); + gpuAccessor.Log($"Failed to reserve storage buffer for global memory operation \"{operation.Inst}\"."); if (operation.Dest != null) { @@ -286,7 +297,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static LinkedListNode<INode> ReplaceGlobalMemoryWithStorage( GtsContext gtsContext, - ShaderConfig config, + ResourceManager resourceManager, + IGpuAccessor gpuAccessor, + TargetLanguage targetLanguage, BasicBlock block, LinkedListNode<INode> node) { @@ -303,7 +316,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand offset = result.Offset; - bool storageUnaligned = config.GpuAccessor.QueryHasUnalignedStorageBuffer(); + bool storageUnaligned = gpuAccessor.QueryHasUnalignedStorageBuffer(); if (storageUnaligned) { @@ -312,7 +325,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand baseAddressMasked = Local(); Operand hostOffset = Local(); - int alignment = config.GpuAccessor.QueryHostStorageBufferOffsetAlignment(); + int alignment = gpuAccessor.QueryHostStorageBufferOffsetAlignment(); Operation maskOp = new(Instruction.BitwiseAnd, baseAddressMasked, baseAddress, Const(-alignment)); Operation subOp = new(Instruction.Subtract, hostOffset, globalAddress, baseAddressMasked); @@ -333,13 +346,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations offset = newOffset; } - if (CanUseInlineStorageOp(operation, config.Options.TargetLanguage)) + if (CanUseInlineStorageOp(operation, targetLanguage)) { - return GenerateInlineStorageOp(config, node, operation, offset, result); + return GenerateInlineStorageOp(resourceManager, node, operation, offset, result); } else { - if (!TryGenerateSingleTargetStorageOp(gtsContext, config, operation, result, out int functionId)) + if (!TryGenerateSingleTargetStorageOp( + gtsContext, + resourceManager, + targetLanguage, + operation, + result, + out int functionId)) { return null; } @@ -354,7 +373,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // the base address might be stored. // Generate a helper function that will check all possible storage buffers and use the right one. - if (!TryGenerateMultiTargetStorageOp(gtsContext, config, block, operation, out int functionId)) + if (!TryGenerateMultiTargetStorageOp( + gtsContext, + resourceManager, + gpuAccessor, + targetLanguage, + block, + operation, + out int functionId)) { return null; } @@ -375,14 +401,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } private static LinkedListNode<INode> GenerateInlineStorageOp( - ShaderConfig config, + ResourceManager resourceManager, LinkedListNode<INode> node, Operation operation, Operand offset, SearchResult result) { bool isStore = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); - if (!config.ResourceManager.TryGetStorageBufferBinding(result.SbCbSlot, result.SbCbOffset, isStore, out int binding)) + if (!resourceManager.TryGetStorageBufferBinding(result.SbCbSlot, result.SbCbOffset, isStore, out int binding)) { return null; } @@ -474,7 +500,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static bool TryGenerateSingleTargetStorageOp( GtsContext gtsContext, - ShaderConfig config, + ResourceManager resourceManager, + TargetLanguage targetLanguage, Operation operation, SearchResult result, out int functionId) @@ -514,7 +541,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } if (!TryGenerateStorageOp( - config, + resourceManager, + targetLanguage, context, operation.Inst, operation.StorageKind, @@ -555,7 +583,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static bool TryGenerateMultiTargetStorageOp( GtsContext gtsContext, - ShaderConfig config, + ResourceManager resourceManager, + IGpuAccessor gpuAccessor, + TargetLanguage targetLanguage, BasicBlock block, Operation operation, out int functionId) @@ -624,7 +654,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (targetCbs.Count == 0) { - config.GpuAccessor.Log($"Failed to find storage buffer for global memory operation \"{operation.Inst}\"."); + gpuAccessor.Log($"Failed to find storage buffer for global memory operation \"{operation.Inst}\"."); } if (gtsContext.TryGetFunctionId(operation, isMultiTarget: true, targetCbs, out functionId)) @@ -685,13 +715,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations SearchResult result = new(sbCbSlot, sbCbOffset); - int alignment = config.GpuAccessor.QueryHostStorageBufferOffsetAlignment(); + int alignment = gpuAccessor.QueryHostStorageBufferOffsetAlignment(); Operand baseAddressMasked = context.BitwiseAnd(baseAddrLow, Const(-alignment)); Operand hostOffset = context.ISubtract(globalAddressLow, baseAddressMasked); if (!TryGenerateStorageOp( - config, + resourceManager, + targetLanguage, context, operation.Inst, operation.StorageKind, @@ -781,7 +812,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations } private static bool TryGenerateStorageOp( - ShaderConfig config, + ResourceManager resourceManager, + TargetLanguage targetLanguage, EmitterContext context, Instruction inst, StorageKind storageKind, @@ -794,7 +826,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations resultValue = null; bool isStore = inst.IsAtomic() || inst == Instruction.Store; - if (!config.ResourceManager.TryGetStorageBufferBinding(result.SbCbSlot, result.SbCbOffset, isStore, out int binding)) + if (!resourceManager.TryGetStorageBufferBinding(result.SbCbSlot, result.SbCbOffset, isStore, out int binding)) { return false; } @@ -820,7 +852,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations resultValue = context.AtomicCompareAndSwap(StorageKind.StorageBuffer, binding, Const(0), wordOffset, compare, value); break; case Instruction.AtomicMaxS32: - if (config.Options.TargetLanguage == TargetLanguage.Spirv) + if (targetLanguage == TargetLanguage.Spirv) { resultValue = context.AtomicMaxS32(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); } @@ -836,7 +868,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations resultValue = context.AtomicMaxU32(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); break; case Instruction.AtomicMinS32: - if (config.Options.TargetLanguage == TargetLanguage.Spirv) + if (targetLanguage == TargetLanguage.Spirv) { resultValue = context.AtomicMinS32(StorageKind.StorageBuffer, binding, Const(0), wordOffset, value); } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index e7805027f..17427a5f9 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -7,40 +7,40 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class Optimizer { - public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) + public static void RunPass(TransformContext context) { - RunOptimizationPasses(blocks, config); + RunOptimizationPasses(context.Blocks, context.ResourceManager); // TODO: Some of those are not optimizations and shouldn't be here. - GlobalToStorage.RunPass(hfm, blocks, config); + GlobalToStorage.RunPass(context.Hfm, context.Blocks, context.ResourceManager, context.GpuAccessor, context.TargetLanguage); - bool hostSupportsShaderFloat64 = config.GpuAccessor.QueryHostSupportsShaderFloat64(); + bool hostSupportsShaderFloat64 = context.GpuAccessor.QueryHostSupportsShaderFloat64(); // Those passes are looking for specific patterns and only needs to run once. - for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + for (int blkIndex = 0; blkIndex < context.Blocks.Length; blkIndex++) { - BindlessToIndexed.RunPass(blocks[blkIndex], config); - BindlessElimination.RunPass(blocks[blkIndex], config); + BindlessToIndexed.RunPass(context.Blocks[blkIndex], context.ResourceManager); + BindlessElimination.RunPass(context.Blocks[blkIndex], context.ResourceManager, context.GpuAccessor); // FragmentCoord only exists on fragment shaders, so we don't need to check other stages. - if (config.Stage == ShaderStage.Fragment) + if (context.Stage == ShaderStage.Fragment) { - EliminateMultiplyByFragmentCoordW(blocks[blkIndex]); + EliminateMultiplyByFragmentCoordW(context.Blocks[blkIndex]); } // If the host does not support double operations, we need to turn them into float operations. if (!hostSupportsShaderFloat64) { - DoubleToFloat.RunPass(hfm, blocks[blkIndex]); + DoubleToFloat.RunPass(context.Hfm, context.Blocks[blkIndex]); } } // Run optimizations one last time to remove any code that is now optimizable after above passes. - RunOptimizationPasses(blocks, config); + RunOptimizationPasses(context.Blocks, context.ResourceManager); } - private static void RunOptimizationPasses(BasicBlock[] blocks, ShaderConfig config) + private static void RunOptimizationPasses(BasicBlock[] blocks, ResourceManager resourceManager) { bool modified; @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - ConstantFolding.RunPass(config, operation); + ConstantFolding.RunPass(resourceManager, operation); Simplification.RunPass(operation); if (DestIsLocalVar(operation)) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 555acd35c..d07d8dce5 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -50,10 +50,10 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderProperties Properties { get; } - public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties) + public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor) { _gpuAccessor = gpuAccessor; - Properties = properties; + Properties = new(); _stage = stage; _stagePrefix = GetShaderStagePrefix(stage); @@ -62,15 +62,15 @@ namespace Ryujinx.Graphics.Shader.Translation _cbSlotToBindingMap.AsSpan().Fill(-1); _sbSlotToBindingMap.AsSpan().Fill(-1); - _sbSlots = new Dictionary<int, int>(); - _sbSlotsReverse = new Dictionary<int, int>(); + _sbSlots = new(); + _sbSlotsReverse = new(); - _usedConstantBufferBindings = new HashSet<int>(); + _usedConstantBufferBindings = new(); - _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); - _usedImages = new Dictionary<TextureInfo, TextureMeta>(); + _usedTextures = new(); + _usedImages = new(); - properties.AddOrUpdateConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); + Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, SupportBuffer.Binding, "support_buffer", SupportBuffer.GetStructureType())); LocalMemoryId = -1; SharedMemoryId = -1; @@ -312,11 +312,11 @@ namespace Ryujinx.Graphics.Shader.Translation if (isImage) { - Properties.AddOrUpdateImage(binding, definition); + Properties.AddOrUpdateImage(definition); } else { - Properties.AddOrUpdateTexture(binding, definition); + Properties.AddOrUpdateTexture(definition); } if (layer == 0) @@ -500,7 +500,7 @@ namespace Ryujinx.Graphics.Shader.Translation new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), }); - Properties.AddOrUpdateConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); + Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, binding, name, type)); } private void AddNewStorageBuffer(int binding, string name) @@ -510,7 +510,7 @@ namespace Ryujinx.Graphics.Shader.Translation new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), }); - Properties.AddOrUpdateStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type)); + Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, 1, binding, name, type)); } public static string GetShaderStagePrefix(ShaderStage stage) diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs deleted file mode 100644 index 27b46867d..000000000 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ /dev/null @@ -1,639 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using System; -using System.Collections.Generic; -using System.Numerics; - -namespace Ryujinx.Graphics.Shader.Translation -{ - class ShaderConfig - { - private const int ThreadsPerWarp = 32; - - public ShaderStage Stage { get; } - - public bool GpPassthrough { get; } - public bool LastInVertexPipeline { get; private set; } - - public bool HasLayerInputAttribute { get; private set; } - public int GpLayerInputAttribute { get; private set; } - public int ThreadsPerInputPrimitive { get; } - - public OutputTopology OutputTopology { get; } - - public int MaxOutputVertices { get; } - - public int LocalMemorySize { get; } - - public ImapPixelType[] ImapTypes { get; } - - public int OmapTargets { get; } - public bool OmapSampleMask { get; } - public bool OmapDepth { get; } - - public IGpuAccessor GpuAccessor { get; } - - public TranslationOptions Options { get; } - - public ShaderProperties Properties => ResourceManager.Properties; - - public ResourceManager ResourceManager { get; set; } - - public bool TransformFeedbackEnabled { get; } - - private TransformFeedbackOutput[] _transformFeedbackOutputs; - - readonly struct TransformFeedbackVariable : IEquatable<TransformFeedbackVariable> - { - public IoVariable IoVariable { get; } - public int Location { get; } - public int Component { get; } - - public TransformFeedbackVariable(IoVariable ioVariable, int location = 0, int component = 0) - { - IoVariable = ioVariable; - Location = location; - Component = component; - } - - public override bool Equals(object other) - { - return other is TransformFeedbackVariable tfbVar && Equals(tfbVar); - } - - public bool Equals(TransformFeedbackVariable other) - { - return IoVariable == other.IoVariable && - Location == other.Location && - Component == other.Component; - } - - public override int GetHashCode() - { - return (int)IoVariable | (Location << 8) | (Component << 16); - } - - public override string ToString() - { - return $"{IoVariable}.{Location}.{Component}"; - } - } - - private readonly Dictionary<TransformFeedbackVariable, TransformFeedbackOutput> _transformFeedbackDefinitions; - - public int Size { get; private set; } - - public byte ClipDistancesWritten { get; private set; } - - public FeatureFlags UsedFeatures { get; private set; } - - public int Cb1DataSize { get; private set; } - - public bool LayerOutputWritten { get; private set; } - public int LayerOutputAttribute { get; private set; } - - public bool NextUsesFixedFuncAttributes { get; private set; } - public int UsedInputAttributes { get; private set; } - public int UsedOutputAttributes { get; private set; } - public HashSet<int> UsedInputAttributesPerPatch { get; } - public HashSet<int> UsedOutputAttributesPerPatch { get; } - public HashSet<int> NextUsedInputAttributesPerPatch { get; private set; } - public int PassthroughAttributes { get; private set; } - private int _nextUsedInputAttributes; - private int _thisUsedInputAttributes; - private Dictionary<int, int> _perPatchAttributeLocations; - - public UInt128 NextInputAttributesComponents { get; private set; } - public UInt128 ThisInputAttributesComponents { get; private set; } - - public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize) - { - Stage = stage; - GpuAccessor = gpuAccessor; - Options = options; - LocalMemorySize = localMemorySize; - - _transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>(); - - TransformFeedbackEnabled = - stage != ShaderStage.Compute && - gpuAccessor.QueryTransformFeedbackEnabled() && - gpuAccessor.QueryHostSupportsTransformFeedback(); - - UsedInputAttributesPerPatch = new HashSet<int>(); - UsedOutputAttributesPerPatch = new HashSet<int>(); - - ShaderProperties properties; - - switch (stage) - { - case ShaderStage.Fragment: - bool originUpperLeft = options.TargetApi == TargetApi.Vulkan || gpuAccessor.QueryYNegateEnabled(); - properties = new ShaderProperties(originUpperLeft); - break; - default: - properties = new ShaderProperties(); - break; - } - - ResourceManager = new ResourceManager(stage, gpuAccessor, properties); - - if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) - { - StructureType tfeInfoStruct = new(new StructureField[] - { - new(AggregateType.Array | AggregateType.U32, "base_offset", 4), - new(AggregateType.U32, "vertex_count"), - }); - - BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); - - properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); - - StructureType tfeDataStruct = new(new StructureField[] - { - new(AggregateType.Array | AggregateType.U32, "data", 0), - }); - - for (int i = 0; i < Constants.TfeBuffersCount; i++) - { - int binding = Constants.TfeBufferBaseBinding + i; - BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); - properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer); - } - } - } - - public ShaderConfig( - ShaderStage stage, - OutputTopology outputTopology, - int maxOutputVertices, - IGpuAccessor gpuAccessor, - TranslationOptions options) : this(stage, gpuAccessor, options, 0) - { - ThreadsPerInputPrimitive = 1; - OutputTopology = outputTopology; - MaxOutputVertices = maxOutputVertices; - } - - public ShaderConfig( - ShaderHeader header, - IGpuAccessor gpuAccessor, - TranslationOptions options) : this(header.Stage, gpuAccessor, options, GetLocalMemorySize(header)) - { - GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; - ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive; - OutputTopology = header.OutputTopology; - MaxOutputVertices = header.MaxOutputVertexCount; - ImapTypes = header.ImapTypes; - OmapTargets = header.OmapTargets; - OmapSampleMask = header.OmapSampleMask; - OmapDepth = header.OmapDepth; - LastInVertexPipeline = header.Stage < ShaderStage.Fragment; - } - - private static int GetLocalMemorySize(ShaderHeader header) - { - return header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize + (header.ShaderLocalMemoryCrsSize / ThreadsPerWarp); - } - - private void EnsureTransformFeedbackInitialized() - { - if (HasTransformFeedbackOutputs() && _transformFeedbackOutputs == null) - { - TransformFeedbackOutput[] transformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; - ulong vecMap = 0UL; - - for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++) - { - var locations = GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); - var stride = GpuAccessor.QueryTransformFeedbackStride(tfbIndex); - - for (int i = 0; i < locations.Length; i++) - { - byte wordOffset = locations[i]; - if (wordOffset < 0xc0) - { - transformFeedbackOutputs[wordOffset] = new TransformFeedbackOutput(tfbIndex, i * 4, stride); - vecMap |= 1UL << (wordOffset / 4); - } - } - } - - _transformFeedbackOutputs = transformFeedbackOutputs; - - while (vecMap != 0) - { - int vecIndex = BitOperations.TrailingZeroCount(vecMap); - - for (int subIndex = 0; subIndex < 4; subIndex++) - { - int wordOffset = vecIndex * 4 + subIndex; - int byteOffset = wordOffset * 4; - - if (transformFeedbackOutputs[wordOffset].Valid) - { - IoVariable ioVariable = Instructions.AttributeMap.GetIoVariable(this, byteOffset, out int location); - int component = 0; - - if (HasPerLocationInputOrOutputComponent(ioVariable, location, subIndex, isOutput: true)) - { - component = subIndex; - } - - var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component); - _transformFeedbackDefinitions.TryAdd(transformFeedbackVariable, transformFeedbackOutputs[wordOffset]); - } - } - - vecMap &= ~(1UL << vecIndex); - } - } - } - - public TransformFeedbackOutput[] GetTransformFeedbackOutputs() - { - EnsureTransformFeedbackInitialized(); - return _transformFeedbackOutputs; - } - - public bool TryGetTransformFeedbackOutput(IoVariable ioVariable, int location, int component, out TransformFeedbackOutput transformFeedbackOutput) - { - EnsureTransformFeedbackInitialized(); - var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component); - return _transformFeedbackDefinitions.TryGetValue(transformFeedbackVariable, out transformFeedbackOutput); - } - - private bool HasTransformFeedbackOutputs() - { - return TransformFeedbackEnabled && (LastInVertexPipeline || Stage == ShaderStage.Fragment); - } - - public bool HasTransformFeedbackOutputs(bool isOutput) - { - return TransformFeedbackEnabled && ((isOutput && LastInVertexPipeline) || (!isOutput && Stage == ShaderStage.Fragment)); - } - - public bool HasPerLocationInputOrOutput(IoVariable ioVariable, bool isOutput) - { - if (ioVariable == IoVariable.UserDefined) - { - return (!isOutput && !UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) || - (isOutput && !UsedFeatures.HasFlag(FeatureFlags.OaIndexing)); - } - - return ioVariable == IoVariable.FragmentOutputColor; - } - - public bool HasPerLocationInputOrOutputComponent(IoVariable ioVariable, int location, int component, bool isOutput) - { - if (ioVariable != IoVariable.UserDefined || !HasTransformFeedbackOutputs(isOutput)) - { - return false; - } - - return GetTransformFeedbackOutputComponents(location, component) == 1; - } - - public TransformFeedbackOutput GetTransformFeedbackOutput(int wordOffset) - { - EnsureTransformFeedbackInitialized(); - - return _transformFeedbackOutputs[wordOffset]; - } - - public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component) - { - return GetTransformFeedbackOutput((AttributeConsts.UserAttributeBase / 4) + location * 4 + component); - } - - public int GetTransformFeedbackOutputComponents(int location, int component) - { - EnsureTransformFeedbackInitialized(); - - int baseIndex = (AttributeConsts.UserAttributeBase / 4) + location * 4; - int index = baseIndex + component; - int count = 1; - - for (; count < 4; count++) - { - ref var prev = ref _transformFeedbackOutputs[baseIndex + count - 1]; - ref var curr = ref _transformFeedbackOutputs[baseIndex + count]; - - int prevOffset = prev.Offset; - int currOffset = curr.Offset; - - if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset) - { - break; - } - } - - if (baseIndex + count <= index) - { - return 1; - } - - return count; - } - - public AggregateType GetFragmentOutputColorType(int location) - { - return AggregateType.Vector4 | GpuAccessor.QueryFragmentOutputType(location).ToAggregateType(); - } - - public AggregateType GetUserDefinedType(int location, bool isOutput) - { - if ((!isOutput && UsedFeatures.HasFlag(FeatureFlags.IaIndexing)) || - (isOutput && UsedFeatures.HasFlag(FeatureFlags.OaIndexing))) - { - return AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32; - } - - AggregateType type = AggregateType.Vector4; - - if (Stage == ShaderStage.Vertex && !isOutput) - { - type |= GpuAccessor.QueryAttributeType(location).ToAggregateType(); - } - else - { - type |= AggregateType.FP32; - } - - return type; - } - - public int GetDepthRegister() - { - // The depth register is always two registers after the last color output. - return BitOperations.PopCount((uint)OmapTargets) + 1; - } - - public uint ConstantBuffer1Read(int offset) - { - if (Cb1DataSize < offset + 4) - { - Cb1DataSize = offset + 4; - } - - return GpuAccessor.ConstantBuffer1Read(offset); - } - - public TextureFormat GetTextureFormat(int handle, int cbufSlot = -1) - { - // When the formatted load extension is supported, we don't need to - // specify a format, we can just declare it without a format and the GPU will handle it. - if (GpuAccessor.QueryHostSupportsImageLoadFormatted()) - { - return TextureFormat.Unknown; - } - - var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot); - - if (format == TextureFormat.Unknown) - { - GpuAccessor.Log($"Unknown format for texture {handle}."); - - format = TextureFormat.R8G8B8A8Unorm; - } - - return format; - } - - private static bool FormatSupportsAtomic(TextureFormat format) - { - return format == TextureFormat.R32Sint || format == TextureFormat.R32Uint; - } - - public TextureFormat GetTextureFormatAtomic(int handle, int cbufSlot = -1) - { - // Atomic image instructions do not support GL_EXT_shader_image_load_formatted, - // and must have a type specified. Default to R32Sint if not available. - - var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot); - - if (!FormatSupportsAtomic(format)) - { - GpuAccessor.Log($"Unsupported format for texture {handle}: {format}."); - - format = TextureFormat.R32Sint; - } - - return format; - } - - public void SizeAdd(int size) - { - Size += size; - } - - public void InheritFrom(ShaderConfig other) - { - ClipDistancesWritten |= other.ClipDistancesWritten; - UsedFeatures |= other.UsedFeatures; - - UsedInputAttributes |= other.UsedInputAttributes; - UsedOutputAttributes |= other.UsedOutputAttributes; - } - - public void SetLayerOutputAttribute(int attr) - { - LayerOutputWritten = true; - LayerOutputAttribute = attr; - } - - public void SetGeometryShaderLayerInputAttribute(int attr) - { - HasLayerInputAttribute = true; - GpLayerInputAttribute = attr; - } - - public void SetLastInVertexPipeline() - { - LastInVertexPipeline = true; - } - - public void SetInputUserAttributeFixedFunc(int index) - { - UsedInputAttributes |= 1 << index; - } - - public void SetOutputUserAttributeFixedFunc(int index) - { - UsedOutputAttributes |= 1 << index; - } - - public void SetInputUserAttribute(int index, int component) - { - int mask = 1 << index; - - UsedInputAttributes |= mask; - _thisUsedInputAttributes |= mask; - ThisInputAttributesComponents |= UInt128.One << (index * 4 + component); - } - - public void SetInputUserAttributePerPatch(int index) - { - UsedInputAttributesPerPatch.Add(index); - } - - public void SetOutputUserAttribute(int index) - { - UsedOutputAttributes |= 1 << index; - } - - public void SetOutputUserAttributePerPatch(int index) - { - UsedOutputAttributesPerPatch.Add(index); - } - - public void MergeFromtNextStage(ShaderConfig config) - { - NextInputAttributesComponents = config.ThisInputAttributesComponents; - NextUsedInputAttributesPerPatch = config.UsedInputAttributesPerPatch; - NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr); - MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch); - - if (UsedOutputAttributesPerPatch.Count != 0) - { - // Regular and per-patch input/output locations can't overlap, - // so we must assign on our location using unused regular input/output locations. - - Dictionary<int, int> locationsMap = new(); - - int freeMask = ~UsedOutputAttributes; - - foreach (int attr in UsedOutputAttributesPerPatch) - { - int location = BitOperations.TrailingZeroCount(freeMask); - if (location == 32) - { - config.GpuAccessor.Log($"No enough free locations for patch input/output 0x{attr:X}."); - break; - } - - locationsMap.Add(attr, location); - freeMask &= ~(1 << location); - } - - // Both stages must agree on the locations, so use the same "map" for both. - _perPatchAttributeLocations = locationsMap; - config._perPatchAttributeLocations = locationsMap; - } - - // We don't consider geometry shaders using the geometry shader passthrough feature - // as being the last because when this feature is used, it can't actually modify any of the outputs, - // so the stage that comes before it is the last one that can do modifications. - if (config.Stage != ShaderStage.Fragment && (config.Stage != ShaderStage.Geometry || !config.GpPassthrough)) - { - LastInVertexPipeline = false; - } - } - - public void MergeOutputUserAttributes(int mask, IEnumerable<int> perPatch) - { - _nextUsedInputAttributes = mask; - - if (GpPassthrough) - { - PassthroughAttributes = mask & ~UsedOutputAttributes; - } - else - { - UsedOutputAttributes |= mask; - UsedOutputAttributesPerPatch.UnionWith(perPatch); - } - } - - public int GetPerPatchAttributeLocation(int index) - { - if (_perPatchAttributeLocations == null || !_perPatchAttributeLocations.TryGetValue(index, out int location)) - { - return index; - } - - return location; - } - - public bool IsUsedOutputAttribute(int attr) - { - // The check for fixed function attributes on the next stage is conservative, - // returning false if the output is just not used by the next stage is also valid. - if (NextUsesFixedFuncAttributes && - attr >= AttributeConsts.UserAttributeBase && - attr < AttributeConsts.UserAttributeEnd) - { - int index = (attr - AttributeConsts.UserAttributeBase) >> 4; - return (_nextUsedInputAttributes & (1 << index)) != 0; - } - - return true; - } - - public int GetFreeUserAttribute(bool isOutput, int index) - { - int useMask = isOutput ? _nextUsedInputAttributes : _thisUsedInputAttributes; - int bit = -1; - - while (useMask != -1) - { - bit = BitOperations.TrailingZeroCount(~useMask); - - if (bit == 32) - { - bit = -1; - break; - } - else if (index < 1) - { - break; - } - - useMask |= 1 << bit; - index--; - } - - return bit; - } - - public void SetAllInputUserAttributes() - { - UsedInputAttributes |= Constants.AllAttributesMask; - ThisInputAttributesComponents |= ~UInt128.Zero >> (128 - Constants.MaxAttributes * 4); - } - - public void SetAllOutputUserAttributes() - { - UsedOutputAttributes |= Constants.AllAttributesMask; - } - - public void SetClipDistanceWritten(int index) - { - ClipDistancesWritten |= (byte)(1 << index); - } - - public void SetUsedFeature(FeatureFlags flags) - { - UsedFeatures |= flags; - } - - public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None) - { - return new ShaderProgramInfo( - ResourceManager.GetConstantBufferDescriptors(), - ResourceManager.GetStorageBufferDescriptors(), - ResourceManager.GetTextureDescriptors(), - ResourceManager.GetImageDescriptors(), - identification, - GpLayerInputAttribute, - Stage, - UsedFeatures.HasFlag(FeatureFlags.FragCoordXY), - UsedFeatures.HasFlag(FeatureFlags.InstanceId), - UsedFeatures.HasFlag(FeatureFlags.DrawParameters), - UsedFeatures.HasFlag(FeatureFlags.RtLayer), - ClipDistancesWritten, - OmapTargets); - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs new file mode 100644 index 000000000..d278c42e4 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs @@ -0,0 +1,315 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Ryujinx.Graphics.Shader.Translation +{ + class ShaderDefinitions + { + private readonly GpuGraphicsState _graphicsState; + + public ShaderStage Stage { get; } + + public int ComputeLocalSizeX { get; } + public int ComputeLocalSizeY { get; } + public int ComputeLocalSizeZ { get; } + + public bool TessCw => _graphicsState.TessCw; + public TessPatchType TessPatchType => _graphicsState.TessPatchType; + public TessSpacing TessSpacing => _graphicsState.TessSpacing; + + public bool AlphaToCoverageDitherEnable => _graphicsState.AlphaToCoverageEnable && _graphicsState.AlphaToCoverageDitherEnable; + public bool ViewportTransformDisable => _graphicsState.ViewportTransformDisable; + + public bool DepthMode => _graphicsState.DepthMode; + + public float PointSize => _graphicsState.PointSize; + + public AlphaTestOp AlphaTestCompare => _graphicsState.AlphaTestCompare; + public float AlphaTestReference => _graphicsState.AlphaTestReference; + + public bool GpPassthrough { get; } + public bool LastInVertexPipeline { get; set; } + + public int ThreadsPerInputPrimitive { get; } + + public InputTopology InputTopology => _graphicsState.Topology; + public OutputTopology OutputTopology { get; } + + public int MaxOutputVertices { get; } + + public bool DualSourceBlend => _graphicsState.DualSourceBlendEnable; + public bool EarlyZForce => _graphicsState.EarlyZForce; + + public bool YNegateEnabled => _graphicsState.YNegateEnabled; + public bool OriginUpperLeft => _graphicsState.OriginUpperLeft; + + public ImapPixelType[] ImapTypes { get; } + public bool IaIndexing { get; private set; } + public bool OaIndexing { get; private set; } + + public int OmapTargets { get; } + public bool OmapSampleMask { get; } + public bool OmapDepth { get; } + + public bool TransformFeedbackEnabled { get; } + + private readonly TransformFeedbackOutput[] _transformFeedbackOutputs; + + readonly struct TransformFeedbackVariable : IEquatable<TransformFeedbackVariable> + { + public IoVariable IoVariable { get; } + public int Location { get; } + public int Component { get; } + + public TransformFeedbackVariable(IoVariable ioVariable, int location = 0, int component = 0) + { + IoVariable = ioVariable; + Location = location; + Component = component; + } + + public override bool Equals(object other) + { + return other is TransformFeedbackVariable tfbVar && Equals(tfbVar); + } + + public bool Equals(TransformFeedbackVariable other) + { + return IoVariable == other.IoVariable && + Location == other.Location && + Component == other.Component; + } + + public override int GetHashCode() + { + return (int)IoVariable | (Location << 8) | (Component << 16); + } + + public override string ToString() + { + return $"{IoVariable}.{Location}.{Component}"; + } + } + + private readonly Dictionary<TransformFeedbackVariable, TransformFeedbackOutput> _transformFeedbackDefinitions; + + public ShaderDefinitions(ShaderStage stage) + { + Stage = stage; + } + + public ShaderDefinitions( + ShaderStage stage, + int computeLocalSizeX, + int computeLocalSizeY, + int computeLocalSizeZ) + { + Stage = stage; + ComputeLocalSizeX = computeLocalSizeX; + ComputeLocalSizeY = computeLocalSizeY; + ComputeLocalSizeZ = computeLocalSizeZ; + } + + public ShaderDefinitions( + ShaderStage stage, + GpuGraphicsState graphicsState, + bool gpPassthrough, + int threadsPerInputPrimitive, + OutputTopology outputTopology, + int maxOutputVertices) + { + Stage = stage; + _graphicsState = graphicsState; + GpPassthrough = gpPassthrough; + ThreadsPerInputPrimitive = threadsPerInputPrimitive; + OutputTopology = outputTopology; + MaxOutputVertices = maxOutputVertices; + } + + public ShaderDefinitions( + ShaderStage stage, + GpuGraphicsState graphicsState, + bool gpPassthrough, + int threadsPerInputPrimitive, + OutputTopology outputTopology, + int maxOutputVertices, + ImapPixelType[] imapTypes, + int omapTargets, + bool omapSampleMask, + bool omapDepth, + bool transformFeedbackEnabled, + ulong transformFeedbackVecMap, + TransformFeedbackOutput[] transformFeedbackOutputs) + { + Stage = stage; + _graphicsState = graphicsState; + GpPassthrough = gpPassthrough; + ThreadsPerInputPrimitive = threadsPerInputPrimitive; + OutputTopology = outputTopology; + MaxOutputVertices = maxOutputVertices; + ImapTypes = imapTypes; + OmapTargets = omapTargets; + OmapSampleMask = omapSampleMask; + OmapDepth = omapDepth; + LastInVertexPipeline = stage < ShaderStage.Fragment; + TransformFeedbackEnabled = transformFeedbackEnabled; + _transformFeedbackOutputs = transformFeedbackOutputs; + _transformFeedbackDefinitions = new(); + + while (transformFeedbackVecMap != 0) + { + int vecIndex = BitOperations.TrailingZeroCount(transformFeedbackVecMap); + + for (int subIndex = 0; subIndex < 4; subIndex++) + { + int wordOffset = vecIndex * 4 + subIndex; + int byteOffset = wordOffset * 4; + + if (transformFeedbackOutputs[wordOffset].Valid) + { + IoVariable ioVariable = Instructions.AttributeMap.GetIoVariable(this, byteOffset, out int location); + int component = 0; + + if (HasPerLocationInputOrOutputComponent(ioVariable, location, subIndex, isOutput: true)) + { + component = subIndex; + } + + var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component); + _transformFeedbackDefinitions.TryAdd(transformFeedbackVariable, transformFeedbackOutputs[wordOffset]); + } + } + + transformFeedbackVecMap &= ~(1UL << vecIndex); + } + } + + public void EnableInputIndexing() + { + IaIndexing = true; + } + + public void EnableOutputIndexing() + { + OaIndexing = true; + } + + public TransformFeedbackOutput[] GetTransformFeedbackOutputs() + { + if (!HasTransformFeedbackOutputs()) + { + return null; + } + + return _transformFeedbackOutputs; + } + + public bool TryGetTransformFeedbackOutput(IoVariable ioVariable, int location, int component, out TransformFeedbackOutput transformFeedbackOutput) + { + if (!HasTransformFeedbackOutputs()) + { + transformFeedbackOutput = default; + return false; + } + + var transformFeedbackVariable = new TransformFeedbackVariable(ioVariable, location, component); + return _transformFeedbackDefinitions.TryGetValue(transformFeedbackVariable, out transformFeedbackOutput); + } + + private bool HasTransformFeedbackOutputs() + { + return TransformFeedbackEnabled && (LastInVertexPipeline || Stage == ShaderStage.Fragment); + } + + public bool HasTransformFeedbackOutputs(bool isOutput) + { + return TransformFeedbackEnabled && ((isOutput && LastInVertexPipeline) || (!isOutput && Stage == ShaderStage.Fragment)); + } + + public bool HasPerLocationInputOrOutput(IoVariable ioVariable, bool isOutput) + { + if (ioVariable == IoVariable.UserDefined) + { + return (!isOutput && !IaIndexing) || (isOutput && !OaIndexing); + } + + return ioVariable == IoVariable.FragmentOutputColor; + } + + public bool HasPerLocationInputOrOutputComponent(IoVariable ioVariable, int location, int component, bool isOutput) + { + if (ioVariable != IoVariable.UserDefined || !HasTransformFeedbackOutputs(isOutput)) + { + return false; + } + + return GetTransformFeedbackOutputComponents(location, component) == 1; + } + + public TransformFeedbackOutput GetTransformFeedbackOutput(int wordOffset) + { + return _transformFeedbackOutputs[wordOffset]; + } + + public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component) + { + return GetTransformFeedbackOutput((AttributeConsts.UserAttributeBase / 4) + location * 4 + component); + } + + public int GetTransformFeedbackOutputComponents(int location, int component) + { + int baseIndex = (AttributeConsts.UserAttributeBase / 4) + location * 4; + int index = baseIndex + component; + int count = 1; + + for (; count < 4; count++) + { + ref var prev = ref _transformFeedbackOutputs[baseIndex + count - 1]; + ref var curr = ref _transformFeedbackOutputs[baseIndex + count]; + + int prevOffset = prev.Offset; + int currOffset = curr.Offset; + + if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset) + { + break; + } + } + + if (baseIndex + count <= index) + { + return 1; + } + + return count; + } + + public AggregateType GetFragmentOutputColorType(int location) + { + return AggregateType.Vector4 | _graphicsState.FragmentOutputTypes[location].ToAggregateType(); + } + + public AggregateType GetUserDefinedType(int location, bool isOutput) + { + if ((!isOutput && IaIndexing) || (isOutput && OaIndexing)) + { + return AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32; + } + + AggregateType type = AggregateType.Vector4; + + if (Stage == ShaderStage.Vertex && !isOutput) + { + type |= _graphicsState.AttributeTypes[location].ToAggregateType(); + } + else + { + type |= AggregateType.FP32; + } + + return type; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs index e9c259942..c077e1cde 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -5,18 +5,22 @@ namespace Ryujinx.Graphics.Shader.Translation { static class ShaderIdentifier { - public static ShaderIdentification Identify(IReadOnlyList<Function> functions, ShaderConfig config) + public static ShaderIdentification Identify( + IReadOnlyList<Function> functions, + IGpuAccessor gpuAccessor, + ShaderStage stage, + InputTopology inputTopology, + out int layerInputAttr) { - if (config.Stage == ShaderStage.Geometry && - config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles && - !config.GpuAccessor.QueryHostSupportsGeometryShader() && - IsLayerPassthroughGeometryShader(functions, out int layerInputAttr)) + if (stage == ShaderStage.Geometry && + inputTopology == InputTopology.Triangles && + !gpuAccessor.QueryHostSupportsGeometryShader() && + IsLayerPassthroughGeometryShader(functions, out layerInputAttr)) { - config.SetGeometryShaderLayerInputAttribute(layerInputAttr); - return ShaderIdentification.GeometryLayerPassthrough; } + layerInputAttr = 0; return ShaderIdentification.None; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs new file mode 100644 index 000000000..fa687eca0 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs @@ -0,0 +1,33 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +namespace Ryujinx.Graphics.Shader.Translation +{ + readonly ref struct TransformContext + { + public readonly HelperFunctionManager Hfm; + public readonly BasicBlock[] Blocks; + public readonly ResourceManager ResourceManager; + public readonly IGpuAccessor GpuAccessor; + public readonly TargetLanguage TargetLanguage; + public readonly ShaderStage Stage; + public readonly ref FeatureFlags UsedFeatures; + + public TransformContext( + HelperFunctionManager hfm, + BasicBlock[] blocks, + ResourceManager resourceManager, + IGpuAccessor gpuAccessor, + TargetLanguage targetLanguage, + ShaderStage stage, + ref FeatureFlags usedFeatures) + { + Hfm = hfm; + Blocks = blocks; + ResourceManager = resourceManager; + GpuAccessor = gpuAccessor; + TargetLanguage = targetLanguage; + Stage = stage; + UsedFeatures = ref usedFeatures; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/TransformFeedbackOutput.cs b/src/Ryujinx.Graphics.Shader/Translation/TransformFeedbackOutput.cs new file mode 100644 index 000000000..7d5c74622 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/TransformFeedbackOutput.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.Shader.Translation +{ + readonly struct TransformFeedbackOutput + { + public readonly bool Valid; + public readonly int Buffer; + public readonly int Offset; + public readonly int Stride; + + public TransformFeedbackOutput(int buffer, int offset, int stride) + { + Valid = true; + Buffer = buffer; + Offset = offset; + Stride = stride; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/DrawParametersReplace.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/DrawParametersReplace.cs new file mode 100644 index 000000000..9e73013db --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/DrawParametersReplace.cs @@ -0,0 +1,93 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Transforms +{ + class DrawParametersReplace : ITransformPass + { + public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures) + { + return stage == ShaderStage.Vertex; + } + + public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node) + { + Operation operation = (Operation)node.Value; + + if (context.GpuAccessor.QueryHasConstantBufferDrawParameters()) + { + if (ReplaceConstantBufferWithDrawParameters(node, operation)) + { + context.UsedFeatures |= FeatureFlags.DrawParameters; + } + } + else if (HasConstantBufferDrawParameters(operation)) + { + context.UsedFeatures |= FeatureFlags.DrawParameters; + } + + return node; + } + + private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation) + { + Operand GenerateLoad(IoVariable ioVariable) + { + Operand value = Local(); + node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.Input, value, Const((int)ioVariable))); + return value; + } + + bool modified = false; + + for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + { + Operand src = operation.GetSource(srcIndex); + + if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0) + { + switch (src.GetCbufOffset()) + { + case Constants.NvnBaseVertexByteOffset / 4: + operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseVertex)); + modified = true; + break; + case Constants.NvnBaseInstanceByteOffset / 4: + operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseInstance)); + modified = true; + break; + case Constants.NvnDrawIndexByteOffset / 4: + operation.SetSource(srcIndex, GenerateLoad(IoVariable.DrawIndex)); + modified = true; + break; + } + } + } + + return modified; + } + + private static bool HasConstantBufferDrawParameters(Operation operation) + { + for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + { + Operand src = operation.GetSource(srcIndex); + + if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0) + { + switch (src.GetCbufOffset()) + { + case Constants.NvnBaseVertexByteOffset / 4: + case Constants.NvnBaseInstanceByteOffset / 4: + case Constants.NvnDrawIndexByteOffset / 4: + return true; + } + } + } + + return false; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs new file mode 100644 index 000000000..6b7e1410f --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs @@ -0,0 +1,36 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation.Transforms +{ + class ForcePreciseEnable : ITransformPass + { + public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures) + { + return stage == ShaderStage.Fragment && gpuAccessor.QueryHostReducedPrecision(); + } + + public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node) + { + // There are some cases where a small bias is added to values to prevent division by zero. + // When operating with reduced precision, it is possible for this bias to get rounded to 0 + // and cause a division by zero. + // To prevent that, we force those operations to be precise even if the host wants + // imprecise operations for performance. + + Operation operation = (Operation)node.Value; + + if (operation.Inst == (Instruction.FP32 | Instruction.Divide) && + operation.GetSource(0).Type == OperandType.Constant && + operation.GetSource(0).AsFloat() == 1f && + operation.GetSource(1).AsgOp is Operation addOp && + addOp.Inst == (Instruction.FP32 | Instruction.Add) && + addOp.GetSource(1).Type == OperandType.Constant) + { + addOp.ForcePrecise = true; + } + + return node; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ITransformPass.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ITransformPass.cs new file mode 100644 index 000000000..0a109d1d2 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ITransformPass.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation.Transforms +{ + interface ITransformPass + { + abstract static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures); + abstract static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node); + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedAtomicSignedCas.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedAtomicSignedCas.cs new file mode 100644 index 000000000..112b3b197 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedAtomicSignedCas.cs @@ -0,0 +1,58 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation.Optimizations; +using System.Collections.Generic; +using System.Diagnostics; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Transforms +{ + class SharedAtomicSignedCas : ITransformPass + { + public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures) + { + return targetLanguage != TargetLanguage.Spirv && stage == ShaderStage.Compute && usedFeatures.HasFlag(FeatureFlags.SharedMemory); + } + + public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node) + { + Operation operation = (Operation)node.Value; + HelperFunctionName name; + + if (operation.Inst == Instruction.AtomicMaxS32) + { + name = HelperFunctionName.SharedAtomicMaxS32; + } + else if (operation.Inst == Instruction.AtomicMinS32) + { + name = HelperFunctionName.SharedAtomicMinS32; + } + else + { + return node; + } + + if (operation.StorageKind != StorageKind.SharedMemory) + { + return node; + } + + Operand result = operation.Dest; + Operand memoryId = operation.GetSource(0); + Operand byteOffset = operation.GetSource(1); + Operand value = operation.GetSource(2); + + Debug.Assert(memoryId.Type == OperandType.Constant); + + int functionId = context.Hfm.GetOrCreateFunctionId(name, memoryId.Value); + + Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value }; + + LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, result, callArgs)); + + Utils.DeleteNode(node, operation); + + return newNode; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedStoreSmallIntCas.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedStoreSmallIntCas.cs new file mode 100644 index 000000000..e58be0a8e --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/SharedStoreSmallIntCas.cs @@ -0,0 +1,57 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation.Optimizations; +using System.Collections.Generic; +using System.Diagnostics; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Transforms +{ + class SharedStoreSmallIntCas : ITransformPass + { + public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures) + { + return stage == ShaderStage.Compute && usedFeatures.HasFlag(FeatureFlags.SharedMemory); + } + + public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node) + { + Operation operation = (Operation)node.Value; + HelperFunctionName name; + + if (operation.StorageKind == StorageKind.SharedMemory8) + { + name = HelperFunctionName.SharedStore8; + } + else if (operation.StorageKind == StorageKind.SharedMemory16) + { + name = HelperFunctionName.SharedStore16; + } + else + { + return node; + } + + if (operation.Inst != Instruction.Store) + { + return node; + } + + Operand memoryId = operation.GetSource(0); + Operand byteOffset = operation.GetSource(1); + Operand value = operation.GetSource(2); + + Debug.Assert(memoryId.Type == OperandType.Constant); + + int functionId = context.Hfm.GetOrCreateFunctionId(name, memoryId.Value); + + Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value }; + + LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, (Operand)null, callArgs)); + + Utils.DeleteNode(node, operation); + + return newNode; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs similarity index 61% rename from src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs rename to src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs index 0fa75203b..5ceed4b7f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs @@ -1,268 +1,45 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation.Optimizations; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; -namespace Ryujinx.Graphics.Shader.Translation +namespace Ryujinx.Graphics.Shader.Translation.Transforms { - static class Rewriter + class TexturePass : ITransformPass { - public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config) + public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures) { - bool isVertexShader = config.Stage == ShaderStage.Vertex; - bool isImpreciseFragmentShader = config.Stage == ShaderStage.Fragment && config.GpuAccessor.QueryHostReducedPrecision(); - bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters(); - bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug(); - bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat(); + return true; + } - for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node) + { + if (node.Value is TextureOperation texOp) { - BasicBlock block = blocks[blkIndex]; + node = InsertTexelFetchScale(context.Hfm, node, context.ResourceManager, context.Stage); + node = InsertTextureSizeUnscale(context.Hfm, node, context.ResourceManager, context.Stage); - for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) + if (texOp.Inst == Instruction.TextureSample) { - if (node.Value is not Operation operation) + node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage); + node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor); + node = InsertConstOffsets(node, context.ResourceManager, context.GpuAccessor); + + if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat()) { - continue; - } - - if (isVertexShader) - { - if (hasConstantBufferDrawParameters) - { - if (ReplaceConstantBufferWithDrawParameters(node, operation)) - { - config.SetUsedFeature(FeatureFlags.DrawParameters); - } - } - else if (HasConstantBufferDrawParameters(operation)) - { - config.SetUsedFeature(FeatureFlags.DrawParameters); - } - } - - if (isImpreciseFragmentShader) - { - EnableForcePreciseIfNeeded(operation); - } - - if (hasVectorIndexingBug) - { - InsertVectorComponentSelect(node, config); - } - - if (operation is TextureOperation texOp) - { - node = InsertTexelFetchScale(hfm, node, config); - node = InsertTextureSizeUnscale(hfm, node, config); - - if (texOp.Inst == Instruction.TextureSample) - { - node = InsertCoordNormalization(hfm, node, config); - node = InsertCoordGatherBias(node, config); - node = InsertConstOffsets(node, config); - - if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat) - { - node = InsertSnormNormalization(node, config); - } - } - } - else - { - node = InsertSharedStoreSmallInt(hfm, node); - - if (config.Options.TargetLanguage != TargetLanguage.Spirv) - { - node = InsertSharedAtomicSigned(hfm, node); - } + node = InsertSnormNormalization(node, context.ResourceManager, context.GpuAccessor); } } } + + return node; } - private static void EnableForcePreciseIfNeeded(Operation operation) - { - // There are some cases where a small bias is added to values to prevent division by zero. - // When operating with reduced precision, it is possible for this bias to get rounded to 0 - // and cause a division by zero. - // To prevent that, we force those operations to be precise even if the host wants - // imprecise operations for performance. - - if (operation.Inst == (Instruction.FP32 | Instruction.Divide) && - operation.GetSource(0).Type == OperandType.Constant && - operation.GetSource(0).AsFloat() == 1f && - operation.GetSource(1).AsgOp is Operation addOp && - addOp.Inst == (Instruction.FP32 | Instruction.Add) && - addOp.GetSource(1).Type == OperandType.Constant) - { - addOp.ForcePrecise = true; - } - } - - private static void InsertVectorComponentSelect(LinkedListNode<INode> node, ShaderConfig config) - { - Operation operation = (Operation)node.Value; - - if (operation.Inst != Instruction.Load || - operation.StorageKind != StorageKind.ConstantBuffer || - operation.SourcesCount < 3) - { - return; - } - - Operand bindingIndex = operation.GetSource(0); - Operand fieldIndex = operation.GetSource(1); - Operand elemIndex = operation.GetSource(operation.SourcesCount - 1); - - if (bindingIndex.Type != OperandType.Constant || - fieldIndex.Type != OperandType.Constant || - elemIndex.Type == OperandType.Constant) - { - return; - } - - BufferDefinition buffer = config.Properties.ConstantBuffers[bindingIndex.Value]; - StructureField field = buffer.Type.Fields[fieldIndex.Value]; - - int elemCount = (field.Type & AggregateType.ElementCountMask) switch - { - AggregateType.Vector2 => 2, - AggregateType.Vector3 => 3, - AggregateType.Vector4 => 4, - _ => 1, - }; - - if (elemCount == 1) - { - return; - } - - Operand result = null; - - for (int i = 0; i < elemCount; i++) - { - Operand value = Local(); - Operand[] inputs = new Operand[operation.SourcesCount]; - - for (int srcIndex = 0; srcIndex < inputs.Length - 1; srcIndex++) - { - inputs[srcIndex] = operation.GetSource(srcIndex); - } - - inputs[^1] = Const(i); - - Operation loadOp = new(Instruction.Load, StorageKind.ConstantBuffer, value, inputs); - - node.List.AddBefore(node, loadOp); - - if (i == 0) - { - result = value; - } - else - { - Operand isCurrentIndex = Local(); - Operand selection = Local(); - - Operation compareOp = new(Instruction.CompareEqual, isCurrentIndex, new Operand[] { elemIndex, Const(i) }); - Operation selectOp = new(Instruction.ConditionalSelect, selection, new Operand[] { isCurrentIndex, value, result }); - - node.List.AddBefore(node, compareOp); - node.List.AddBefore(node, selectOp); - - result = selection; - } - } - - operation.TurnIntoCopy(result); - } - - private static LinkedListNode<INode> InsertSharedStoreSmallInt(HelperFunctionManager hfm, LinkedListNode<INode> node) - { - Operation operation = (Operation)node.Value; - HelperFunctionName name; - - if (operation.StorageKind == StorageKind.SharedMemory8) - { - name = HelperFunctionName.SharedStore8; - } - else if (operation.StorageKind == StorageKind.SharedMemory16) - { - name = HelperFunctionName.SharedStore16; - } - else - { - return node; - } - - if (operation.Inst != Instruction.Store) - { - return node; - } - - Operand memoryId = operation.GetSource(0); - Operand byteOffset = operation.GetSource(1); - Operand value = operation.GetSource(2); - - Debug.Assert(memoryId.Type == OperandType.Constant); - - int functionId = hfm.GetOrCreateFunctionId(name, memoryId.Value); - - Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value }; - - LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, (Operand)null, callArgs)); - - Utils.DeleteNode(node, operation); - - return newNode; - } - - private static LinkedListNode<INode> InsertSharedAtomicSigned(HelperFunctionManager hfm, LinkedListNode<INode> node) - { - Operation operation = (Operation)node.Value; - HelperFunctionName name; - - if (operation.Inst == Instruction.AtomicMaxS32) - { - name = HelperFunctionName.SharedAtomicMaxS32; - } - else if (operation.Inst == Instruction.AtomicMinS32) - { - name = HelperFunctionName.SharedAtomicMinS32; - } - else - { - return node; - } - - if (operation.StorageKind != StorageKind.SharedMemory) - { - return node; - } - - Operand result = operation.Dest; - Operand memoryId = operation.GetSource(0); - Operand byteOffset = operation.GetSource(1); - Operand value = operation.GetSource(2); - - Debug.Assert(memoryId.Type == OperandType.Constant); - - int functionId = hfm.GetOrCreateFunctionId(name, memoryId.Value); - - Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value }; - - LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, result, callArgs)); - - Utils.DeleteNode(node, operation); - - return newNode; - } - - private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertTexelFetchScale( + HelperFunctionManager hfm, + LinkedListNode<INode> node, + ResourceManager resourceManager, + ShaderStage stage) { TextureOperation texOp = (TextureOperation)node.Value; @@ -280,20 +57,20 @@ namespace Ryujinx.Graphics.Shader.Translation (intCoords || isImage) && !isBindless && !isIndexed && - config.Stage.SupportsRenderScale() && + stage.SupportsRenderScale() && TypeSupportsScale(texOp.Type)) { int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); int samplerIndex = isImage - ? config.ResourceManager.GetTextureDescriptors().Length + config.ResourceManager.FindImageDescriptorIndex(texOp.Binding) - : config.ResourceManager.FindTextureDescriptorIndex(texOp.Binding); + ? resourceManager.GetTextureDescriptors().Length + resourceManager.FindImageDescriptorIndex(texOp.Binding) + : resourceManager.FindTextureDescriptorIndex(texOp.Binding); for (int index = 0; index < coordsCount; index++) { Operand scaledCoord = Local(); Operand[] callArgs; - if (config.Stage == ShaderStage.Fragment) + if (stage == ShaderStage.Fragment) { callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex), Const(index) }; } @@ -311,7 +88,11 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static LinkedListNode<INode> InsertTextureSizeUnscale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertTextureSizeUnscale( + HelperFunctionManager hfm, + LinkedListNode<INode> node, + ResourceManager resourceManager, + ShaderStage stage) { TextureOperation texOp = (TextureOperation)node.Value; @@ -322,11 +103,11 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Index < 2 && !isBindless && !isIndexed && - config.Stage.SupportsRenderScale() && + stage.SupportsRenderScale() && TypeSupportsScale(texOp.Type)) { int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale); - int samplerIndex = config.ResourceManager.FindTextureDescriptorIndex(texOp.Binding); + int samplerIndex = resourceManager.FindTextureDescriptorIndex(texOp.Binding); for (int index = texOp.DestsCount - 1; index >= 0; index--) { @@ -356,19 +137,12 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static bool IsImageInstructionWithScale(Instruction inst) - { - // Currently, we don't support scaling images that are modified, - // so we only need to care about the load instruction. - return inst == Instruction.ImageLoad; - } - - private static bool TypeSupportsScale(SamplerType type) - { - return (type & SamplerType.Mask) == SamplerType.Texture2D; - } - - private static LinkedListNode<INode> InsertCoordNormalization(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertCoordNormalization( + HelperFunctionManager hfm, + LinkedListNode<INode> node, + ResourceManager resourceManager, + IGpuAccessor gpuAccessor, + ShaderStage stage) { // Emulate non-normalized coordinates by normalizing the coordinates on the shader. // Without normalization, the coordinates are expected to the in the [0, W or H] range, @@ -386,9 +160,9 @@ namespace Ryujinx.Graphics.Shader.Translation bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - (int cbufSlot, int handle) = config.ResourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding); + (int cbufSlot, int handle) = resourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding); - bool isCoordNormalized = config.GpuAccessor.QueryTextureCoordNormalized(handle, cbufSlot); + bool isCoordNormalized = gpuAccessor.QueryTextureCoordNormalized(handle, cbufSlot); if (isCoordNormalized || intCoords) { @@ -400,8 +174,6 @@ namespace Ryujinx.Graphics.Shader.Translation int coordsCount = texOp.Type.GetDimensions(); int coordsIndex = isBindless || isIndexed ? 1 : 0; - config.SetUsedFeature(FeatureFlags.IntegerSampling); - int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; for (int index = 0; index < normCoordsCount; index++) @@ -429,7 +201,7 @@ namespace Ryujinx.Graphics.Shader.Translation new[] { coordSize }, texSizeSources)); - config.ResourceManager.SetUsageFlagsForTextureQuery(texOp.Binding, texOp.Type); + resourceManager.SetUsageFlagsForTextureQuery(texOp.Binding, texOp.Type); Operand source = texOp.GetSource(coordsIndex + index); @@ -439,13 +211,13 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.SetSource(coordsIndex + index, coordNormalized); - InsertTextureSizeUnscale(hfm, textureSizeNode, config); + InsertTextureSizeUnscale(hfm, textureSizeNode, resourceManager, stage); } return node; } - private static LinkedListNode<INode> InsertCoordGatherBias(LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertCoordGatherBias(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor) { // The gather behavior when the coordinate sits right in the middle of two texels is not well defined. // To ensure the correct texel is sampled, we add a small bias value to the coordinate. @@ -457,25 +229,18 @@ namespace Ryujinx.Graphics.Shader.Translation bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; - int gatherBiasPrecision = config.GpuAccessor.QueryHostGatherBiasPrecision(); + int gatherBiasPrecision = gpuAccessor.QueryHostGatherBiasPrecision(); if (!isGather || gatherBiasPrecision == 0) { return node; } -#pragma warning disable IDE0059 // Remove unnecessary value assignment - bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - - bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; -#pragma warning restore IDE0059 int coordsCount = texOp.Type.GetDimensions(); int coordsIndex = isBindless || isIndexed ? 1 : 0; - config.SetUsedFeature(FeatureFlags.IntegerSampling); - int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount; for (int index = 0; index < normCoordsCount; index++) @@ -524,7 +289,7 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor) { // Non-constant texture offsets are not allowed (according to the spec), // however some GPUs does support that. @@ -540,7 +305,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QueryHostSupportsNonConstantTextureOffset(); + bool hasInvalidOffset = (hasOffset || hasOffsets) && !gpuAccessor.QueryHostSupportsNonConstantTextureOffset(); bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; @@ -673,8 +438,6 @@ namespace Ryujinx.Graphics.Shader.Translation if (isGather && !isShadow) { - config.SetUsedFeature(FeatureFlags.IntegerSampling); - Operand[] newSources = new Operand[sources.Length]; sources.CopyTo(newSources, 0); @@ -741,8 +504,6 @@ namespace Ryujinx.Graphics.Shader.Translation } else { - config.SetUsedFeature(FeatureFlags.IntegerSampling); - Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); for (int index = 0; index < coordsCount; index++) @@ -840,7 +601,7 @@ namespace Ryujinx.Graphics.Shader.Translation return texSizes; } - private static LinkedListNode<INode> InsertSnormNormalization(LinkedListNode<INode> node, ShaderConfig config) + private static LinkedListNode<INode> InsertSnormNormalization(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor) { TextureOperation texOp = (TextureOperation)node.Value; @@ -851,9 +612,9 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - (int cbufSlot, int handle) = config.ResourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding); + (int cbufSlot, int handle) = resourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding); - TextureFormat format = config.GpuAccessor.QueryTextureFormat(handle, cbufSlot); + TextureFormat format = gpuAccessor.QueryTextureFormat(handle, cbufSlot); int maxPositive = format switch { @@ -926,63 +687,16 @@ namespace Ryujinx.Graphics.Shader.Translation return res; } - private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation) + private static bool IsImageInstructionWithScale(Instruction inst) { - Operand GenerateLoad(IoVariable ioVariable) - { - Operand value = Local(); - node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.Input, value, Const((int)ioVariable))); - return value; - } - - bool modified = false; - - for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) - { - Operand src = operation.GetSource(srcIndex); - - if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0) - { - switch (src.GetCbufOffset()) - { - case Constants.NvnBaseVertexByteOffset / 4: - operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseVertex)); - modified = true; - break; - case Constants.NvnBaseInstanceByteOffset / 4: - operation.SetSource(srcIndex, GenerateLoad(IoVariable.BaseInstance)); - modified = true; - break; - case Constants.NvnDrawIndexByteOffset / 4: - operation.SetSource(srcIndex, GenerateLoad(IoVariable.DrawIndex)); - modified = true; - break; - } - } - } - - return modified; + // Currently, we don't support scaling images that are modified, + // so we only need to care about the load instruction. + return inst == Instruction.ImageLoad; } - private static bool HasConstantBufferDrawParameters(Operation operation) + private static bool TypeSupportsScale(SamplerType type) { - for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) - { - Operand src = operation.GetSource(srcIndex); - - if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0) - { - switch (src.GetCbufOffset()) - { - case Constants.NvnBaseVertexByteOffset / 4: - case Constants.NvnBaseInstanceByteOffset / 4: - case Constants.NvnDrawIndexByteOffset / 4: - return true; - } - } - } - - return false; + return (type & SamplerType.Mask) == SamplerType.Texture2D; } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TransformPasses.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TransformPasses.cs new file mode 100644 index 000000000..c3bbe7ddf --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TransformPasses.cs @@ -0,0 +1,41 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation.Transforms +{ + static class TransformPasses + { + public static void RunPass(TransformContext context) + { + RunPass<DrawParametersReplace>(context); + RunPass<ForcePreciseEnable>(context); + RunPass<VectorComponentSelect>(context); + RunPass<TexturePass>(context); + RunPass<SharedStoreSmallIntCas>(context); + RunPass<SharedAtomicSignedCas>(context); + } + + private static void RunPass<T>(TransformContext context) where T : ITransformPass + { + if (!T.IsEnabled(context.GpuAccessor, context.Stage, context.TargetLanguage, context.UsedFeatures)) + { + return; + } + + for (int blkIndex = 0; blkIndex < context.Blocks.Length; blkIndex++) + { + BasicBlock block = context.Blocks[blkIndex]; + + for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next) + { + if (node.Value is not Operation) + { + continue; + } + + node = T.RunPass(context, node); + } + } + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/VectorComponentSelect.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/VectorComponentSelect.cs new file mode 100644 index 000000000..e55f4355d --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/VectorComponentSelect.cs @@ -0,0 +1,96 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Transforms +{ + class VectorComponentSelect : ITransformPass + { + public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures) + { + return gpuAccessor.QueryHostHasVectorIndexingBug(); + } + + public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node) + { + Operation operation = (Operation)node.Value; + + if (operation.Inst != Instruction.Load || + operation.StorageKind != StorageKind.ConstantBuffer || + operation.SourcesCount < 3) + { + return node; + } + + Operand bindingIndex = operation.GetSource(0); + Operand fieldIndex = operation.GetSource(1); + Operand elemIndex = operation.GetSource(operation.SourcesCount - 1); + + if (bindingIndex.Type != OperandType.Constant || + fieldIndex.Type != OperandType.Constant || + elemIndex.Type == OperandType.Constant) + { + return node; + } + + BufferDefinition buffer = context.ResourceManager.Properties.ConstantBuffers[bindingIndex.Value]; + StructureField field = buffer.Type.Fields[fieldIndex.Value]; + + int elemCount = (field.Type & AggregateType.ElementCountMask) switch + { + AggregateType.Vector2 => 2, + AggregateType.Vector3 => 3, + AggregateType.Vector4 => 4, + _ => 1 + }; + + if (elemCount == 1) + { + return node; + } + + Operand result = null; + + for (int i = 0; i < elemCount; i++) + { + Operand value = Local(); + Operand[] inputs = new Operand[operation.SourcesCount]; + + for (int srcIndex = 0; srcIndex < inputs.Length - 1; srcIndex++) + { + inputs[srcIndex] = operation.GetSource(srcIndex); + } + + inputs[^1] = Const(i); + + Operation loadOp = new(Instruction.Load, StorageKind.ConstantBuffer, value, inputs); + + node.List.AddBefore(node, loadOp); + + if (i == 0) + { + result = value; + } + else + { + Operand isCurrentIndex = Local(); + Operand selection = Local(); + + Operation compareOp = new(Instruction.CompareEqual, isCurrentIndex, new Operand[] { elemIndex, Const(i) }); + Operation selectOp = new(Instruction.ConditionalSelect, selection, new Operand[] { isCurrentIndex, value, result }); + + node.List.AddBefore(node, compareOp); + node.List.AddBefore(node, selectOp); + + result = selection; + } + } + + operation.TurnIntoCopy(result); + + return node; + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index 010c80db1..b609ac073 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -1,11 +1,6 @@ -using Ryujinx.Graphics.Shader.CodeGen.Glsl; -using Ryujinx.Graphics.Shader.CodeGen.Spirv; using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation.Optimizations; using System; -using System.Collections.Generic; using System.Linq; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -13,6 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation { public static class Translator { + private const int ThreadsPerWarp = 32; private const int HeaderSize = 0x50; internal readonly struct FunctionCode @@ -30,94 +26,31 @@ namespace Ryujinx.Graphics.Shader.Translation return DecodeShader(address, gpuAccessor, options); } - internal static ShaderProgram Translate(FunctionCode[] functions, ShaderConfig config) - { - var cfgs = new ControlFlowGraph[functions.Length]; - var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; - - for (int i = 0; i < functions.Length; i++) - { - cfgs[i] = ControlFlowGraph.Create(functions[i].Code); - - if (i != 0) - { - frus[i] = RegisterUsage.RunPass(cfgs[i]); - } - } - - List<Function> funcs = new(functions.Length); - - for (int i = 0; i < functions.Length; i++) - { - funcs.Add(null); - } - - HelperFunctionManager hfm = new(funcs, config.Stage); - - for (int i = 0; i < functions.Length; i++) - { - var cfg = cfgs[i]; - - int inArgumentsCount = 0; - int outArgumentsCount = 0; - - if (i != 0) - { - var fru = frus[i]; - - inArgumentsCount = fru.InArguments.Length; - outArgumentsCount = fru.OutArguments.Length; - } - - if (cfg.Blocks.Length != 0) - { - RegisterUsage.FixupCalls(cfg.Blocks, frus); - - Dominance.FindDominators(cfg); - Dominance.FindDominanceFrontiers(cfg.Blocks); - - Ssa.Rename(cfg.Blocks); - - Optimizer.RunPass(hfm, cfg.Blocks, config); - Rewriter.RunPass(hfm, cfg.Blocks, config); - } - - funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); - } - - var identification = ShaderIdentifier.Identify(funcs, config); - - var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); - - var info = config.CreateProgramInfo(identification); - - return config.Options.TargetLanguage switch - { - TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)), - TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)), - _ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()), - }; - } - private static TranslatorContext DecodeShader(ulong address, IGpuAccessor gpuAccessor, TranslationOptions options) { - ShaderConfig config; + int localMemorySize; + ShaderDefinitions definitions; DecodedProgram program; - ulong maxEndAddress = 0; if (options.Flags.HasFlag(TranslationFlags.Compute)) { - config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options, gpuAccessor.QueryComputeLocalMemorySize()); + definitions = CreateComputeDefinitions(gpuAccessor); + localMemorySize = gpuAccessor.QueryComputeLocalMemorySize(); - program = Decoder.Decode(config, address); + program = Decoder.Decode(definitions, gpuAccessor, address); } else { - config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, options); + ShaderHeader header = new(gpuAccessor, address); - program = Decoder.Decode(config, address + HeaderSize); + definitions = CreateGraphicsDefinitions(gpuAccessor, header); + localMemorySize = GetLocalMemorySize(header); + + program = Decoder.Decode(definitions, gpuAccessor, address + HeaderSize); } + ulong maxEndAddress = 0; + foreach (DecodedFunction function in program) { foreach (Block block in function.Blocks) @@ -129,12 +62,76 @@ namespace Ryujinx.Graphics.Shader.Translation } } - config.SizeAdd((int)maxEndAddress + (options.Flags.HasFlag(TranslationFlags.Compute) ? 0 : HeaderSize)); + int size = (int)maxEndAddress + (options.Flags.HasFlag(TranslationFlags.Compute) ? 0 : HeaderSize); - return new TranslatorContext(address, program, config); + return new TranslatorContext(address, size, localMemorySize, definitions, gpuAccessor, options, program); } - internal static FunctionCode[] EmitShader(DecodedProgram program, ShaderConfig config, bool initializeOutputs, out int initializationOperations) + private static ShaderDefinitions CreateComputeDefinitions(IGpuAccessor gpuAccessor) + { + return new ShaderDefinitions( + ShaderStage.Compute, + gpuAccessor.QueryComputeLocalSizeX(), + gpuAccessor.QueryComputeLocalSizeY(), + gpuAccessor.QueryComputeLocalSizeZ()); + } + + private static ShaderDefinitions CreateGraphicsDefinitions(IGpuAccessor gpuAccessor, ShaderHeader header) + { + bool transformFeedbackEnabled = + gpuAccessor.QueryTransformFeedbackEnabled() && + gpuAccessor.QueryHostSupportsTransformFeedback(); + TransformFeedbackOutput[] transformFeedbackOutputs = null; + ulong transformFeedbackVecMap = 0UL; + + if (transformFeedbackEnabled) + { + transformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; + + for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++) + { + var locations = gpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); + var stride = gpuAccessor.QueryTransformFeedbackStride(tfbIndex); + + for (int i = 0; i < locations.Length; i++) + { + byte wordOffset = locations[i]; + if (wordOffset < 0xc0) + { + transformFeedbackOutputs[wordOffset] = new TransformFeedbackOutput(tfbIndex, i * 4, stride); + transformFeedbackVecMap |= 1UL << (wordOffset / 4); + } + } + } + } + + return new ShaderDefinitions( + header.Stage, + gpuAccessor.QueryGraphicsState(), + header.Stage == ShaderStage.Geometry && header.GpPassthrough, + header.ThreadsPerInputPrimitive, + header.OutputTopology, + header.MaxOutputVertexCount, + header.ImapTypes, + header.OmapTargets, + header.OmapSampleMask, + header.OmapDepth, + transformFeedbackEnabled, + transformFeedbackVecMap, + transformFeedbackOutputs); + } + + private static int GetLocalMemorySize(ShaderHeader header) + { + return header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize + (header.ShaderLocalMemoryCrsSize / ThreadsPerWarp); + } + + internal static FunctionCode[] EmitShader( + TranslatorContext translatorContext, + ResourceManager resourceManager, + DecodedProgram program, + bool initializeOutputs, + out int initializationOperations) { initializationOperations = 0; @@ -149,11 +146,11 @@ namespace Ryujinx.Graphics.Shader.Translation for (int index = 0; index < functions.Length; index++) { - EmitterContext context = new(program, config, index != 0); + EmitterContext context = new(translatorContext, resourceManager, program, index != 0); if (initializeOutputs && index == 0) { - EmitOutputsInitialization(context, config); + EmitOutputsInitialization(context, translatorContext.AttributeUsage, translatorContext.GpuAccessor, translatorContext.Stage); initializationOperations = context.OperationsCount; } @@ -168,27 +165,27 @@ namespace Ryujinx.Graphics.Shader.Translation EmitOps(context, block); } - functions[index] = new FunctionCode(context.GetOperations()); + functions[index] = new(context.GetOperations()); } return functions; } - private static void EmitOutputsInitialization(EmitterContext context, ShaderConfig config) + private static void EmitOutputsInitialization(EmitterContext context, AttributeUsage attributeUsage, IGpuAccessor gpuAccessor, ShaderStage stage) { // Compute has no output attributes, and fragment is the last stage, so we // don't need to initialize outputs on those stages. - if (config.Stage == ShaderStage.Compute || config.Stage == ShaderStage.Fragment) + if (stage == ShaderStage.Compute || stage == ShaderStage.Fragment) { return; } - if (config.Stage == ShaderStage.Vertex) + if (stage == ShaderStage.Vertex) { InitializePositionOutput(context); } - UInt128 usedAttributes = context.Config.NextInputAttributesComponents; + UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents; while (usedAttributes != UInt128.Zero) { int index = (int)UInt128.TrailingZeroCount(usedAttributes); @@ -197,7 +194,7 @@ namespace Ryujinx.Graphics.Shader.Translation usedAttributes &= ~(UInt128.One << index); // We don't need to initialize passthrough attributes. - if ((context.Config.PassthroughAttributes & (1 << vecIndex)) != 0) + if ((context.TranslatorContext.AttributeUsage.PassthroughAttributes & (1 << vecIndex)) != 0) { continue; } @@ -205,30 +202,28 @@ namespace Ryujinx.Graphics.Shader.Translation InitializeOutputComponent(context, vecIndex, index & 3, perPatch: false); } - if (context.Config.NextUsedInputAttributesPerPatch != null) + if (context.TranslatorContext.AttributeUsage.NextUsedInputAttributesPerPatch != null) { - foreach (int vecIndex in context.Config.NextUsedInputAttributesPerPatch.Order()) + foreach (int vecIndex in context.TranslatorContext.AttributeUsage.NextUsedInputAttributesPerPatch.Order()) { InitializeOutput(context, vecIndex, perPatch: true); } } - if (config.NextUsesFixedFuncAttributes) + if (attributeUsage.NextUsesFixedFuncAttributes) { - bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation(); + bool supportsLayerFromVertexOrTess = gpuAccessor.QueryHostSupportsLayerVertexTessellation(); int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1; for (int i = fixedStartAttr; i < fixedStartAttr + 5 + AttributeConsts.TexCoordCount; i++) { - int index = config.GetFreeUserAttribute(isOutput: true, i); + int index = attributeUsage.GetFreeUserAttribute(isOutput: true, i); if (index < 0) { break; } InitializeOutput(context, index, perPatch: false); - - config.SetOutputUserAttributeFixedFunc(index); } } } @@ -253,11 +248,11 @@ namespace Ryujinx.Graphics.Shader.Translation { StorageKind storageKind = perPatch ? StorageKind.OutputPerPatch : StorageKind.Output; - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing)) + if (context.TranslatorContext.Definitions.OaIndexing) { Operand invocationId = null; - if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch) + if (context.TranslatorContext.Definitions.Stage == ShaderStage.TessellationControl && !perPatch) { invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId); } @@ -268,7 +263,7 @@ namespace Ryujinx.Graphics.Shader.Translation } else { - if (context.Config.Stage == ShaderStage.TessellationControl && !perPatch) + if (context.TranslatorContext.Definitions.Stage == ShaderStage.TessellationControl && !perPatch) { Operand invocationId = context.Load(StorageKind.Input, IoVariable.InvocationId); context.Store(storageKind, IoVariable.UserDefined, Const(location), invocationId, Const(c), ConstF(c == 3 ? 1f : 0f)); @@ -286,7 +281,7 @@ namespace Ryujinx.Graphics.Shader.Translation { InstOp op = block.OpCodes[opIndex]; - if (context.Config.Options.Flags.HasFlag(TranslationFlags.DebugMode)) + if (context.TranslatorContext.Options.Flags.HasFlag(TranslationFlags.DebugMode)) { string instName; @@ -298,7 +293,7 @@ namespace Ryujinx.Graphics.Shader.Translation { instName = "???"; - context.Config.GpuAccessor.Log($"Invalid instruction at 0x{op.Address:X6} (0x{op.RawOpCode:X16})."); + context.TranslatorContext.GpuAccessor.Log($"Invalid instruction at 0x{op.Address:X6} (0x{op.RawOpCode:X16})."); } string dbgComment = $"0x{op.Address:X6}: 0x{op.RawOpCode:X16} {instName}"; diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index 40a79c544..39ce92c9d 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -1,8 +1,11 @@ -using Ryujinx.Graphics.Shader.CodeGen.Glsl; +using Ryujinx.Graphics.Shader.CodeGen; +using Ryujinx.Graphics.Shader.CodeGen.Glsl; using Ryujinx.Graphics.Shader.CodeGen.Spirv; using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation.Optimizations; +using Ryujinx.Graphics.Shader.Translation.Transforms; using System; using System.Collections.Generic; using System.Linq; @@ -15,22 +18,47 @@ namespace Ryujinx.Graphics.Shader.Translation public class TranslatorContext { private readonly DecodedProgram _program; - private readonly ShaderConfig _config; + private readonly int _localMemorySize; public ulong Address { get; } + public int Size { get; } + public int Cb1DataSize => _program.Cb1DataSize; - public ShaderStage Stage => _config.Stage; - public int Size => _config.Size; - public int Cb1DataSize => _config.Cb1DataSize; - public bool LayerOutputWritten => _config.LayerOutputWritten; + internal bool HasLayerInputAttribute { get; private set; } + internal int GpLayerInputAttribute { get; private set; } - public IGpuAccessor GpuAccessor => _config.GpuAccessor; + internal AttributeUsage AttributeUsage => _program.AttributeUsage; - internal TranslatorContext(ulong address, DecodedProgram program, ShaderConfig config) + internal ShaderDefinitions Definitions { get; } + + public ShaderStage Stage => Definitions.Stage; + + internal IGpuAccessor GpuAccessor { get; } + + internal TranslationOptions Options { get; } + + internal FeatureFlags UsedFeatures { get; private set; } + + public bool LayerOutputWritten { get; private set; } + public int LayerOutputAttribute { get; private set; } + + internal TranslatorContext( + ulong address, + int size, + int localMemorySize, + ShaderDefinitions definitions, + IGpuAccessor gpuAccessor, + TranslationOptions options, + DecodedProgram program) { Address = address; + Size = size; _program = program; - _config = config; + _localMemorySize = localMemorySize; + Definitions = definitions; + GpuAccessor = gpuAccessor; + Options = options; + UsedFeatures = program.UsedFeatures; } private static bool IsLoadUserDefined(Operation operation) @@ -131,63 +159,259 @@ namespace Ryujinx.Graphics.Shader.Translation return output; } - public void SetNextStage(TranslatorContext nextStage) + internal int GetDepthRegister() { - _config.MergeFromtNextStage(nextStage._config); + // The depth register is always two registers after the last color output. + return BitOperations.PopCount((uint)Definitions.OmapTargets) + 1; + } + + public void SetLayerOutputAttribute(int attr) + { + LayerOutputWritten = true; + LayerOutputAttribute = attr; } public void SetGeometryShaderLayerInputAttribute(int attr) { - _config.SetGeometryShaderLayerInputAttribute(attr); + UsedFeatures |= FeatureFlags.RtLayer; + HasLayerInputAttribute = true; + GpLayerInputAttribute = attr; } public void SetLastInVertexPipeline() { - _config.SetLastInVertexPipeline(); + Definitions.LastInVertexPipeline = true; } - public ShaderProgram Translate(TranslatorContext other = null) + public void SetNextStage(TranslatorContext nextStage) { - bool usesLocalMemory = _config.UsedFeatures.HasFlag(FeatureFlags.LocalMemory); + AttributeUsage.MergeFromtNextStage( + Definitions.GpPassthrough, + nextStage.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr), + nextStage.AttributeUsage); - _config.ResourceManager.SetCurrentLocalMemory(_config.LocalMemorySize, usesLocalMemory); - - if (_config.Stage == ShaderStage.Compute) + // We don't consider geometry shaders using the geometry shader passthrough feature + // as being the last because when this feature is used, it can't actually modify any of the outputs, + // so the stage that comes before it is the last one that can do modifications. + if (nextStage.Definitions.Stage != ShaderStage.Fragment && + (nextStage.Definitions.Stage != ShaderStage.Geometry || !nextStage.Definitions.GpPassthrough)) { - bool usesSharedMemory = _config.UsedFeatures.HasFlag(FeatureFlags.SharedMemory); + Definitions.LastInVertexPipeline = false; + } + } - _config.ResourceManager.SetCurrentSharedMemory(GpuAccessor.QueryComputeSharedMemorySize(), usesSharedMemory); + public ShaderProgram Translate() + { + ResourceManager resourceManager = CreateResourceManager(); + + bool usesLocalMemory = _program.UsedFeatures.HasFlag(FeatureFlags.LocalMemory); + + resourceManager.SetCurrentLocalMemory(_localMemorySize, usesLocalMemory); + + if (Stage == ShaderStage.Compute) + { + bool usesSharedMemory = _program.UsedFeatures.HasFlag(FeatureFlags.SharedMemory); + + resourceManager.SetCurrentSharedMemory(GpuAccessor.QueryComputeSharedMemorySize(), usesSharedMemory); } - FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _); + FunctionCode[] code = EmitShader(this, resourceManager, _program, initializeOutputs: true, out _); - if (other != null) + return Translate(code, resourceManager, UsedFeatures, _program.ClipDistancesWritten); + } + + public ShaderProgram Translate(TranslatorContext other) + { + ResourceManager resourceManager = CreateResourceManager(); + + bool usesLocalMemory = _program.UsedFeatures.HasFlag(FeatureFlags.LocalMemory); + resourceManager.SetCurrentLocalMemory(_localMemorySize, usesLocalMemory); + + FunctionCode[] code = EmitShader(this, resourceManager, _program, initializeOutputs: false, out _); + + bool otherUsesLocalMemory = other._program.UsedFeatures.HasFlag(FeatureFlags.LocalMemory); + resourceManager.SetCurrentLocalMemory(other._localMemorySize, otherUsesLocalMemory); + + FunctionCode[] otherCode = EmitShader(other, resourceManager, other._program, initializeOutputs: true, out int aStart); + + code = Combine(otherCode, code, aStart); + + return Translate( + code, + resourceManager, + UsedFeatures | other.UsedFeatures, + (byte)(_program.ClipDistancesWritten | other._program.ClipDistancesWritten)); + } + + private ShaderProgram Translate(FunctionCode[] functions, ResourceManager resourceManager, FeatureFlags usedFeatures, byte clipDistancesWritten) + { + var cfgs = new ControlFlowGraph[functions.Length]; + var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; + + for (int i = 0; i < functions.Length; i++) { - other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>()); + cfgs[i] = ControlFlowGraph.Create(functions[i].Code); - // We need to share the resource manager since both shaders accesses the same constant buffers. - other._config.ResourceManager = _config.ResourceManager; - other._config.ResourceManager.SetCurrentLocalMemory(other._config.LocalMemorySize, other._config.UsedFeatures.HasFlag(FeatureFlags.LocalMemory)); - - FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart); - - code = Combine(otherCode, code, aStart); - - _config.InheritFrom(other._config); + if (i != 0) + { + frus[i] = RegisterUsage.RunPass(cfgs[i]); + } } - return Translator.Translate(code, _config); + List<Function> funcs = new(functions.Length); + + for (int i = 0; i < functions.Length; i++) + { + funcs.Add(null); + } + + HelperFunctionManager hfm = new(funcs, Definitions.Stage); + + for (int i = 0; i < functions.Length; i++) + { + var cfg = cfgs[i]; + + int inArgumentsCount = 0; + int outArgumentsCount = 0; + + if (i != 0) + { + var fru = frus[i]; + + inArgumentsCount = fru.InArguments.Length; + outArgumentsCount = fru.OutArguments.Length; + } + + if (cfg.Blocks.Length != 0) + { + RegisterUsage.FixupCalls(cfg.Blocks, frus); + + Dominance.FindDominators(cfg); + Dominance.FindDominanceFrontiers(cfg.Blocks); + + Ssa.Rename(cfg.Blocks); + + TransformContext context = new( + hfm, + cfg.Blocks, + resourceManager, + GpuAccessor, + Options.TargetLanguage, + Definitions.Stage, + ref usedFeatures); + + Optimizer.RunPass(context); + TransformPasses.RunPass(context); + } + + funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); + } + + var identification = ShaderIdentifier.Identify(funcs, GpuAccessor, Definitions.Stage, Definitions.InputTopology, out int layerInputAttr); + + return Generate( + funcs, + AttributeUsage, + Definitions, + resourceManager, + usedFeatures, + clipDistancesWritten, + identification, + layerInputAttr); + } + + private ShaderProgram Generate( + IReadOnlyList<Function> funcs, + AttributeUsage attributeUsage, + ShaderDefinitions definitions, + ResourceManager resourceManager, + FeatureFlags usedFeatures, + byte clipDistancesWritten, + ShaderIdentification identification = ShaderIdentification.None, + int layerInputAttr = 0) + { + var sInfo = StructuredProgram.MakeStructuredProgram( + funcs, + attributeUsage, + definitions, + resourceManager, + Options.Flags.HasFlag(TranslationFlags.DebugMode)); + + var info = new ShaderProgramInfo( + resourceManager.GetConstantBufferDescriptors(), + resourceManager.GetStorageBufferDescriptors(), + resourceManager.GetTextureDescriptors(), + resourceManager.GetImageDescriptors(), + identification, + layerInputAttr, + definitions.Stage, + usedFeatures.HasFlag(FeatureFlags.FragCoordXY), + usedFeatures.HasFlag(FeatureFlags.InstanceId), + usedFeatures.HasFlag(FeatureFlags.DrawParameters), + usedFeatures.HasFlag(FeatureFlags.RtLayer), + clipDistancesWritten, + definitions.OmapTargets); + + var hostCapabilities = new HostCapabilities( + GpuAccessor.QueryHostReducedPrecision(), + GpuAccessor.QueryHostSupportsFragmentShaderInterlock(), + GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel(), + GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(), + GpuAccessor.QueryHostSupportsShaderBallot(), + GpuAccessor.QueryHostSupportsShaderBarrierDivergence(), + GpuAccessor.QueryHostSupportsTextureShadowLod(), + GpuAccessor.QueryHostSupportsViewportMask()); + + 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)), + _ => throw new NotImplementedException(Options.TargetLanguage.ToString()), + }; + } + + private ResourceManager CreateResourceManager() + { + ResourceManager resourceManager = new(Definitions.Stage, GpuAccessor); + + if (!GpuAccessor.QueryHostSupportsTransformFeedback() && GpuAccessor.QueryTransformFeedbackEnabled()) + { + StructureType tfeInfoStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "base_offset", 4), + new StructureField(AggregateType.U32, "vertex_count") + }); + + BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); + resourceManager.Properties.AddOrUpdateStorageBuffer(tfeInfoBuffer); + + StructureType tfeDataStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + }); + + for (int i = 0; i < Constants.TfeBuffersCount; i++) + { + int binding = Constants.TfeBufferBaseBinding + i; + BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); + resourceManager.Properties.AddOrUpdateStorageBuffer(tfeDataBuffer); + } + } + + return resourceManager; } public ShaderProgram GenerateGeometryPassthrough() { - int outputAttributesMask = _config.UsedOutputAttributes; - int layerOutputAttr = _config.LayerOutputAttribute; + int outputAttributesMask = AttributeUsage.UsedOutputAttributes; + int layerOutputAttr = LayerOutputAttribute; OutputTopology outputTopology; int maxOutputVertices; - switch (GpuAccessor.QueryPrimitiveTopology()) + switch (Definitions.InputTopology) { case InputTopology.Points: outputTopology = OutputTopology.PointList; @@ -204,9 +428,10 @@ namespace Ryujinx.Graphics.Shader.Translation break; } - ShaderConfig config = new(ShaderStage.Geometry, outputTopology, maxOutputVertices, GpuAccessor, _config.Options); + var attributeUsage = new AttributeUsage(GpuAccessor); + var resourceManager = new ResourceManager(ShaderStage.Geometry, GpuAccessor); - EmitterContext context = new(default, config, false); + var context = new EmitterContext(); for (int v = 0; v < maxOutputVertices; v++) { @@ -231,10 +456,7 @@ namespace Ryujinx.Graphics.Shader.Translation else { context.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(attrIndex), Const(c), value); - config.SetOutputUserAttribute(attrIndex); } - - config.SetInputUserAttribute(attrIndex, c); } } @@ -254,16 +476,15 @@ namespace Ryujinx.Graphics.Shader.Translation var cfg = ControlFlowGraph.Create(operations); var function = new Function(cfg.Blocks, "main", false, 0, 0); - var sInfo = StructuredProgram.MakeStructuredProgram(new[] { function }, config); + var definitions = new ShaderDefinitions( + ShaderStage.Geometry, + GpuAccessor.QueryGraphicsState(), + false, + 1, + outputTopology, + maxOutputVertices); - var info = config.CreateProgramInfo(); - - return config.Options.TargetLanguage switch - { - TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)), - TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)), - _ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()), - }; + return Generate(new[] { function }, attributeUsage, definitions, resourceManager, FeatureFlags.RtLayer, 0); } } } From 33f544fd9248361440afd6013e0ef9d69971d6da Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 14 Aug 2023 07:41:11 +0100 Subject: [PATCH 735/737] GPU: Track basic buffer copies that modify texture memory (#5554) This branch changes the buffer copy fast path to notify memory tracking for all resources that aren't buffers. This fixes cases where games would copy buffer data directly into texture memory, which before would only work if the texture did not already exist. I imagine this happens when the guest driver is moving data between allocations or uploading it. Since this only affects the fast path, cases where the source data has been modified from GPU (fast path copy destination doesn't count) will still fail to notify the texture, though I don't imagine games will do this. This should be resolved in future. This should fix some texture issues with guest OpenGL games on switch, such as Dragon Quest Builders. This may also be useful in future for games that move shader data around memory, if we end up using memory tracking for those. --- src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs | 2 +- src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 99c571ba5..f8f572c6a 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -344,7 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory // Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU. dstBuffer.ClearModified(dstAddress, size); - memoryManager.Physical.WriteUntracked(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size)); + memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer); } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index d0b4478e1..1ca6071bd 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -236,6 +236,18 @@ namespace Ryujinx.Graphics.Gpu.Memory _cpuMemory.WriteUntracked(address, data); } + /// <summary> + /// Writes data to the application process, triggering a precise memory tracking event. + /// </summary> + /// <param name="address">Address to write into</param> + /// <param name="data">Data to be written</param> + /// <param name="kind">Kind of the resource being written, which will not be signalled as CPU modified</param> + public void WriteTrackedResource(ulong address, ReadOnlySpan<byte> data, ResourceKind kind) + { + _cpuMemory.SignalMemoryTracking(address, (ulong)data.Length, true, precise: true, exemptId: (int)kind); + _cpuMemory.WriteUntracked(address, data); + } + /// <summary> /// Writes data to the application process. /// </summary> From 550fd4a7338eded794bf961ef6fd0c38643471c8 Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Mon, 14 Aug 2023 13:57:39 -0300 Subject: [PATCH 736/737] Simplify resolution scale updates (#5541) --- .../Image/TextureBindingsManager.cs | 21 ++--------- .../Image/TextureManager.cs | 7 ++-- .../Memory/SupportBufferUpdater.cs | 35 +++++++++---------- .../DescriptorSetUpdater.cs | 2 +- 4 files changed, 21 insertions(+), 44 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index d1454fc9a..8eca18b48 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -61,8 +61,6 @@ namespace Ryujinx.Graphics.Gpu.Image private int _textureBufferIndex; - private readonly float[] _scales; - private bool _scaleChanged; private int _lastFragmentTotal; /// <summary> @@ -72,14 +70,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="channel">The GPU channel that the texture bindings manager belongs to</param> /// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param> /// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param> - /// <param name="scales">Array where the scales for the currently bound textures are stored</param> /// <param name="isCompute">True if the bindings manager is used for the compute engine</param> public TextureBindingsManager( GpuContext context, GpuChannel channel, TexturePoolCache texturePoolCache, SamplerPoolCache samplerPoolCache, - float[] scales, bool isCompute) { _context = context; @@ -87,7 +83,6 @@ namespace Ryujinx.Graphics.Gpu.Image _texturePoolCache = texturePoolCache; _samplerPoolCache = samplerPoolCache; - _scales = scales; _isCompute = isCompute; int stages = isCompute ? 1 : Constants.ShaderStages; @@ -239,12 +234,7 @@ namespace Ryujinx.Graphics.Gpu.Image } } - if (result != _scales[index]) - { - _scaleChanged = true; - - _scales[index] = result; - } + _context.SupportBufferUpdater.UpdateRenderScale(index, result); return changed; } @@ -290,11 +280,6 @@ namespace Ryujinx.Graphics.Gpu.Image // - Vertex stage has bindings that require scale. // - Fragment stage binding count has been updated since last render scale update. - _scaleChanged = true; - } - - if (_scaleChanged) - { if (!_isCompute) { total += fragmentTotal; // Add the fragment bindings to the total. @@ -302,9 +287,7 @@ namespace Ryujinx.Graphics.Gpu.Image _lastFragmentTotal = fragmentTotal; - _context.SupportBufferUpdater.UpdateRenderScale(_scales, total, fragmentTotal); - - _scaleChanged = false; + _context.SupportBufferUpdater.UpdateRenderScaleFragmentCount(total, fragmentTotal); } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 63b9b47c3..ed181640a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -44,11 +44,8 @@ namespace Ryujinx.Graphics.Gpu.Image TexturePoolCache texturePoolCache = new(context); SamplerPoolCache samplerPoolCache = new(context); - float[] scales = new float[64]; - new Span<float>(scales).Fill(1f); - - _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: true); - _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: false); + _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: true); + _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: false); _texturePoolCache = texturePoolCache; _samplerPoolCache = samplerPoolCache; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs index b236476e0..409c7a786 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs @@ -136,33 +136,30 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <summary> /// Updates the render scales for shader input textures or images. /// </summary> - /// <param name="scales">Scale values</param> + /// <param name="index">Index of the scale</param> + /// <param name="scale">Scale value</param> + public void UpdateRenderScale(int index, float scale) + { + if (_data.RenderScale[1 + index].X != scale) + { + _data.RenderScale[1 + index].X = scale; + DirtyRenderScale(1 + index, 1); + } + } + + /// <summary> + /// Updates the render scales for shader input textures or images. + /// </summary> /// <param name="totalCount">Total number of scales across all stages</param> /// <param name="fragmentCount">Total number of scales on the fragment shader stage</param> - public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount) + public void UpdateRenderScaleFragmentCount(int totalCount, int fragmentCount) { - bool changed = false; - - for (int index = 0; index < totalCount; index++) - { - if (_data.RenderScale[1 + index].X != scales[index]) - { - _data.RenderScale[1 + index].X = scales[index]; - changed = true; - } - } - // Only update fragment count if there are scales after it for the vertex stage. if (fragmentCount != totalCount && fragmentCount != _data.FragmentRenderScaleCount.X) { _data.FragmentRenderScaleCount.X = fragmentCount; DirtyFragmentRenderScaleCount(); } - - if (changed) - { - DirtyRenderScale(0, 1 + totalCount); - } } /// <summary> @@ -172,7 +169,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <param name="isBgra">True if the format is BGRA< false otherwise</param> public void SetRenderTargetIsBgra(int index, bool isBgra) { - bool isBgraChanged = (_data.FragmentIsBgra[index].X != 0) != isBgra; + bool isBgraChanged = _data.FragmentIsBgra[index].X != 0 != isBgra; if (isBgraChanged) { diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 894710911..45392b642 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -376,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan var program = _program; var bindingSegments = program.BindingSegments[setIndex]; - if (bindingSegments.Length == 0 && setIndex != PipelineBase.UniformSetIndex) + if (bindingSegments.Length == 0) { return; } From 492a0463358e7706e0fb34537d55810d833ae695 Mon Sep 17 00:00:00 2001 From: riperiperi <rhy3756547@hotmail.com> Date: Mon, 14 Aug 2023 18:18:47 +0100 Subject: [PATCH 737/737] Vulkan: Buffer Mirrors for MacOS performance (#4899) * Initial implementation of buffer mirrors Generally slower right now, goal is to reduce render passes in games that do inline updates Fix support buffer mirrors Reintroduce vertex buffer mirror Add storage buffer support Optimisation part 1 More optimisation Avoid useless data copies. Remove unused cbIndex stuff Properly set write flag for storage buffers. Fix minor issues Not sure why this was here. Fix BufferRangeList Fix some big issues Align storage buffers rather than getting full buffer as a range Improves mirrorability of read-only storage buffers Increase staging buffer size, as it now contains mirrors Fix some issues with buffers not updating Fix buffer SetDataUnchecked offset for one of the paths when using mirrors Fix buffer mirrors interaction with buffer textures Fix mirror rebinding Move GetBuffer calls on indirect draws before BeginRenderPass to avoid draws without render pass Fix mirrors rebase Fix rebase 2023 * Fix crash when using stale vertex buffer Similar to `Get` with a size that's too large, just treat it as a clamp. * Explicitly set support buffer as mirrorable * Address feedback * Remove unused fragment of MVK workaround * Replace logging for staging buffer OOM * Address format issues * Address more format issues * Mini cleanup * Address more things * Rename BufferRangeList * Support bounding range for ClearMirrors and UploadPendingData * Add maximum size for vertex buffer mirrors * Enable index buffer mirrors Enabled on all platforms for the IbStreamer. * Feedback * Remove mystery BufferCache change Probably macos related? * Fix mirrors not creating when staging buffer is empty. * Change log level to debug --- src/Ryujinx.Graphics.GAL/BufferAccess.cs | 1 + src/Ryujinx.Graphics.GAL/BufferRange.cs | 4 +- .../Multithreading/BufferMap.cs | 8 +- .../Engine/Threed/IbStreamer.cs | 4 +- src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 14 +- .../Memory/BufferCache.cs | 8 +- .../Memory/BufferManager.cs | 6 +- .../Memory/SupportBufferUpdater.cs | 2 +- src/Ryujinx.Graphics.Vulkan/Auto.cs | 24 +- src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs | 263 +++++++++++++++ src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 283 +++++++++++++++- src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 13 +- .../BufferMirrorRangeList.cs | 305 ++++++++++++++++++ src/Ryujinx.Graphics.Vulkan/BufferState.cs | 7 +- .../BufferUsageBitmap.cs | 19 +- .../DescriptorSetUpdater.cs | 211 +++++++++--- src/Ryujinx.Graphics.Vulkan/EnumConversion.cs | 1 + .../HardwareCapabilities.cs | 5 +- .../IndexBufferState.cs | 14 +- .../MultiFenceHolder.cs | 17 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 70 ++-- src/Ryujinx.Graphics.Vulkan/PipelineFull.cs | 13 + .../Queries/BufferedQuery.cs | 2 +- src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs | 90 +++++- src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 12 +- .../VertexBufferState.cs | 13 +- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 12 +- 27 files changed, 1285 insertions(+), 136 deletions(-) create mode 100644 src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs create mode 100644 src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs diff --git a/src/Ryujinx.Graphics.GAL/BufferAccess.cs b/src/Ryujinx.Graphics.GAL/BufferAccess.cs index 2f7d994fe..e7d7ceb0a 100644 --- a/src/Ryujinx.Graphics.GAL/BufferAccess.cs +++ b/src/Ryujinx.Graphics.GAL/BufferAccess.cs @@ -4,5 +4,6 @@ namespace Ryujinx.Graphics.GAL { Default, FlushPersistent, + Stream } } diff --git a/src/Ryujinx.Graphics.GAL/BufferRange.cs b/src/Ryujinx.Graphics.GAL/BufferRange.cs index 483747f10..fec82de24 100644 --- a/src/Ryujinx.Graphics.GAL/BufferRange.cs +++ b/src/Ryujinx.Graphics.GAL/BufferRange.cs @@ -10,12 +10,14 @@ namespace Ryujinx.Graphics.GAL public int Offset { get; } public int Size { get; } + public bool Write { get; } - public BufferRange(BufferHandle handle, int offset, int size) + public BufferRange(BufferHandle handle, int offset, int size, bool write = false) { Handle = handle; Offset = offset; Size = size; + Write = write; } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs b/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs index c30df0465..6377e5eac 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading internal BufferRange MapBufferRange(BufferRange range) { - return new BufferRange(MapBuffer(range.Handle), range.Offset, range.Size); + return new BufferRange(MapBuffer(range.Handle), range.Offset, range.Size, range.Write); } internal Span<BufferRange> MapBufferRanges(Span<BufferRange> ranges) @@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading result = BufferHandle.Null; } - range = new BufferRange(result, range.Offset, range.Size); + range = new BufferRange(result, range.Offset, range.Size, range.Write); } } @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading result = BufferHandle.Null; } - assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size)); + assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size, range.Write)); } } @@ -176,7 +176,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading result = BufferHandle.Null; } - range = new BufferRange(result, range.Offset, range.Size); + range = new BufferRange(result, range.Offset, range.Size, range.Write); ranges[i] = new VertexBufferDescriptor(range, ranges[i].Stride, ranges[i].Divisor); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs index 022e12f57..6e15fcfa3 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs @@ -171,7 +171,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (_inlineIndexBuffer == BufferHandle.Null) { - _inlineIndexBuffer = renderer.CreateBuffer(size); + _inlineIndexBuffer = renderer.CreateBuffer(size, BufferAccess.Stream); _inlineIndexBufferSize = size; } else if (_inlineIndexBufferSize < size) @@ -179,7 +179,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed BufferHandle oldBuffer = _inlineIndexBuffer; int oldSize = _inlineIndexBufferSize; - _inlineIndexBuffer = renderer.CreateBuffer(size); + _inlineIndexBuffer = renderer.CreateBuffer(size, BufferAccess.Stream); _inlineIndexBufferSize = size; renderer.Pipeline.CopyBuffer(oldBuffer, _inlineIndexBuffer, 0, 0, oldSize); diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index e27c14a16..c9286a619 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -140,18 +140,21 @@ namespace Ryujinx.Graphics.Gpu.Memory } /// <summary> - /// Gets a sub-range from the buffer, from a start address till the end of the buffer. + /// Gets a sub-range from the buffer, from a start address til a page boundary after the given size. /// </summary> /// <remarks> /// This can be used to bind and use sub-ranges of the buffer on the host API. /// </remarks> /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param> + /// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param> + /// <param name="write">Whether the buffer will be written to by this use</param> /// <returns>The buffer sub-range</returns> - public BufferRange GetRange(ulong address) + public BufferRange GetRangeAligned(ulong address, ulong size, bool write) { + ulong end = ((address + size + MemoryManager.PageMask) & ~MemoryManager.PageMask) - Address; ulong offset = address - Address; - return new BufferRange(Handle, (int)offset, (int)(Size - offset)); + return new BufferRange(Handle, (int)offset, (int)(end - offset), write); } /// <summary> @@ -162,12 +165,13 @@ namespace Ryujinx.Graphics.Gpu.Memory /// </remarks> /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param> /// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param> + /// <param name="write">Whether the buffer will be written to by this use</param> /// <returns>The buffer sub-range</returns> - public BufferRange GetRange(ulong address, ulong size) + public BufferRange GetRange(ulong address, ulong size, bool write) { int offset = (int)(address - Address); - return new BufferRange(Handle, offset, (int)size); + return new BufferRange(Handle, offset, (int)size, write); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index f8f572c6a..05cc312c7 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -372,15 +372,15 @@ namespace Ryujinx.Graphics.Gpu.Memory } /// <summary> - /// Gets a buffer sub-range starting at a given memory address. + /// Gets a buffer sub-range from a start address til a page boundary after the given size. /// </summary> /// <param name="address">Start address of the memory range</param> /// <param name="size">Size in bytes of the memory range</param> /// <param name="write">Whether the buffer will be written to by this use</param> /// <returns>The buffer sub-range starting at the given memory address</returns> - public BufferRange GetBufferRangeTillEnd(ulong address, ulong size, bool write = false) + public BufferRange GetBufferRangeAligned(ulong address, ulong size, bool write = false) { - return GetBuffer(address, size, write).GetRange(address); + return GetBuffer(address, size, write).GetRangeAligned(address, size, write); } /// <summary> @@ -392,7 +392,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// <returns>The buffer sub-range for the given range</returns> public BufferRange GetBufferRange(ulong address, ulong size, bool write = false) { - return GetBuffer(address, size, write).GetRange(address, size); + return GetBuffer(address, size, write).GetRange(address, size, write); } /// <summary> diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index c656b0f64..bf4cb5d05 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -614,7 +614,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (_tfInfoBuffer == BufferHandle.Null) { - _tfInfoBuffer = _context.Renderer.CreateBuffer(TfInfoBufferSize); + _tfInfoBuffer = _context.Renderer.CreateBuffer(TfInfoBufferSize, BufferAccess.Stream); } buffers[0] = new BufferAssignment(0, new BufferRange(_tfInfoBuffer, 0, TfInfoBufferSize)); @@ -727,7 +727,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); var range = isStorage - ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) + ? bufferCache.GetBufferRangeAligned(bounds.Address, bounds.Size, isWrite) : bufferCache.GetBufferRange(bounds.Address, bounds.Size); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); @@ -764,7 +764,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); var range = isStorage - ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) + ? bufferCache.GetBufferRangeAligned(bounds.Address, bounds.Size, isWrite) : bufferCache.GetBufferRange(bounds.Address, bounds.Size); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); diff --git a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs index 409c7a786..c1e91c54b 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs @@ -228,7 +228,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (_handle == BufferHandle.Null) { - _handle = _renderer.CreateBuffer(SupportBuffer.RequiredSize); + _handle = _renderer.CreateBuffer(SupportBuffer.RequiredSize, BufferAccess.Stream); _renderer.Pipeline.ClearBuffer(_handle, 0, SupportBuffer.RequiredSize, 0); var range = new BufferRange(_handle, 0, SupportBuffer.RequiredSize); diff --git a/src/Ryujinx.Graphics.Vulkan/Auto.cs b/src/Ryujinx.Graphics.Vulkan/Auto.cs index fdce7232c..026dd2b60 100644 --- a/src/Ryujinx.Graphics.Vulkan/Auto.cs +++ b/src/Ryujinx.Graphics.Vulkan/Auto.cs @@ -18,6 +18,12 @@ namespace Ryujinx.Graphics.Vulkan void AddCommandBufferDependencies(CommandBufferScoped cbs); } + interface IMirrorable<T> where T : IDisposable + { + Auto<T> GetMirrorable(CommandBufferScoped cbs, ref int offset, int size, out bool mirrored); + void ClearMirrors(CommandBufferScoped cbs, int offset, int size); + } + class Auto<T> : IAutoPrivate, IDisposable where T : IDisposable { private int _referenceCount; @@ -26,6 +32,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly BitMap _cbOwnership; private readonly MultiFenceHolder _waitable; private readonly IAutoPrivate[] _referencedObjs; + private readonly IMirrorable<T> _mirrorable; private bool _disposed; private bool _destroyed; @@ -37,6 +44,11 @@ namespace Ryujinx.Graphics.Vulkan _cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers); } + public Auto(T value, IMirrorable<T> mirrorable, MultiFenceHolder waitable, params IAutoPrivate[] referencedObjs) : this(value, waitable, referencedObjs) + { + _mirrorable = mirrorable; + } + public Auto(T value, MultiFenceHolder waitable, params IAutoPrivate[] referencedObjs) : this(value) { _waitable = waitable; @@ -48,9 +60,17 @@ namespace Ryujinx.Graphics.Vulkan } } - public T Get(CommandBufferScoped cbs, int offset, int size) + public T GetMirrorable(CommandBufferScoped cbs, ref int offset, int size, out bool mirrored) { - _waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size); + var mirror = _mirrorable.GetMirrorable(cbs, ref offset, size, out mirrored); + mirror._waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, false); + return mirror.Get(cbs); + } + + public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false) + { + _mirrorable?.ClearMirrors(cbs, offset, size); + _waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write); return Get(cbs); } diff --git a/src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs b/src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs new file mode 100644 index 000000000..15672e9cb --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs @@ -0,0 +1,263 @@ +using Ryujinx.Common.Memory; +using System; +using System.Numerics; + +namespace Ryujinx.Graphics.Vulkan +{ + interface IBitMapListener + { + void BitMapSignal(int index, int count); + } + + struct BitMapStruct<T> where T : IArray<long> + { + public const int IntSize = 64; + + private const int IntShift = 6; + private const int IntMask = IntSize - 1; + + private T _masks; + + public BitMapStruct() + { + _masks = default; + } + + public bool BecomesUnsetFrom(in BitMapStruct<T> from, ref BitMapStruct<T> into) + { + bool result = false; + + int masks = _masks.Length; + for (int i = 0; i < masks; i++) + { + long fromMask = from._masks[i]; + long unsetMask = (~fromMask) & (fromMask ^ _masks[i]); + into._masks[i] = unsetMask; + + result |= unsetMask != 0; + } + + return result; + } + + public void SetAndSignalUnset<T2>(in BitMapStruct<T> from, ref T2 listener) where T2 : struct, IBitMapListener + { + BitMapStruct<T> result = new(); + + if (BecomesUnsetFrom(from, ref result)) + { + // Iterate the set bits in the result, and signal them. + + int offset = 0; + int masks = _masks.Length; + ref T resultMasks = ref result._masks; + for (int i = 0; i < masks; i++) + { + long value = resultMasks[i]; + while (value != 0) + { + int bit = BitOperations.TrailingZeroCount((ulong)value); + + listener.BitMapSignal(offset + bit, 1); + + value &= ~(1L << bit); + } + + offset += IntSize; + } + } + + _masks = from._masks; + } + + public void SignalSet(Action<int, int> action) + { + // Iterate the set bits in the result, and signal them. + + int offset = 0; + int masks = _masks.Length; + for (int i = 0; i < masks; i++) + { + long value = _masks[i]; + while (value != 0) + { + int bit = BitOperations.TrailingZeroCount((ulong)value); + + action(offset + bit, 1); + + value &= ~(1L << bit); + } + + offset += 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 Set(int bit, bool value) + { + if (value) + { + Set(bit); + } + else + { + Clear(bit); + } + } + + 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] |= -1L; + } + + _masks[endIndex] |= endMask; + } + } + + public BitMapStruct<T> Union(BitMapStruct<T> other) + { + var result = new BitMapStruct<T>(); + + ref var masks = ref _masks; + ref var otherMasks = ref other._masks; + ref var newMasks = ref result._masks; + + for (int i = 0; i < masks.Length; i++) + { + newMasks[i] = masks[i] | otherMasks[i]; + } + + return result; + } + + 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.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 54635631a..c767a57a7 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -10,7 +10,7 @@ using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan { - class BufferHolder : IDisposable + class BufferHolder : IDisposable, IMirrorable<DisposableBuffer>, IMirrorable<DisposableBufferView> { private const int MaxUpdateBufferSize = 0x10000; @@ -64,6 +64,11 @@ namespace Ryujinx.Graphics.Vulkan private List<Action> _swapActions; + private byte[] _pendingData; + private BufferMirrorRangeList _pendingDataRanges; + private Dictionary<ulong, StagingBufferReserved> _mirrors; + private bool _useMirrors; + public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType) { _gd = gd; @@ -71,7 +76,7 @@ namespace Ryujinx.Graphics.Vulkan _allocation = allocation; _allocationAuto = new Auto<MemoryAllocation>(allocation); _waitable = new MultiFenceHolder(size); - _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), _waitable, _allocationAuto); + _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), this, _waitable, _allocationAuto); _bufferHandle = buffer.Handle; Size = size; _map = allocation.HostPointer; @@ -81,6 +86,7 @@ namespace Ryujinx.Graphics.Vulkan DesiredType = currentType; _flushLock = new ReaderWriterLock(); + _useMirrors = gd.IsTBDR; } public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset) @@ -91,7 +97,7 @@ namespace Ryujinx.Graphics.Vulkan _allocationAuto = allocation; _allocationImported = true; _waitable = new MultiFenceHolder(size); - _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), _waitable, _allocationAuto); + _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), this, _waitable, _allocationAuto); _bufferHandle = buffer.Handle; Size = size; _map = _allocation.HostPointer + offset; @@ -110,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan // Only swap if the buffer is not used in any queued command buffer. bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool); - if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld) + if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld && (_pendingData == null || cbs != null)) { var currentAllocation = _allocationAuto; var currentBuffer = _buffer; @@ -120,6 +126,11 @@ namespace Ryujinx.Graphics.Vulkan if (buffer.Handle != 0) { + if (cbs != null) + { + ClearMirrors(cbs.Value, 0, Size); + } + _flushLock.AcquireWriterLock(Timeout.Infinite); ClearFlushFence(); @@ -128,7 +139,7 @@ namespace Ryujinx.Graphics.Vulkan _allocation = allocation; _allocationAuto = new Auto<MemoryAllocation>(allocation); - _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(_gd.Api, _device, buffer), _waitable, _allocationAuto); + _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(_gd.Api, _device, buffer), this, _waitable, _allocationAuto); _bufferHandle = buffer.Handle; _map = allocation.HostPointer; @@ -257,7 +268,7 @@ namespace Ryujinx.Graphics.Vulkan (_swapActions ??= new List<Action>()).Add(invalidateView); - return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer); + return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer); } public void InheritMetrics(BufferHolder other) @@ -302,6 +313,82 @@ namespace Ryujinx.Graphics.Vulkan } } + private static ulong ToMirrorKey(int offset, int size) + { + return ((ulong)offset << 32) | (uint)size; + } + + private static (int offset, int size) FromMirrorKey(ulong key) + { + return ((int)(key >> 32), (int)key); + } + + private unsafe bool TryGetMirror(CommandBufferScoped cbs, ref int offset, int size, out Auto<DisposableBuffer> buffer) + { + size = Math.Min(size, Size - offset); + + // Does this binding need to be mirrored? + + if (!_pendingDataRanges.OverlapsWith(offset, size)) + { + buffer = null; + return false; + } + + var key = ToMirrorKey(offset, size); + + if (_mirrors.TryGetValue(key, out StagingBufferReserved reserved)) + { + buffer = reserved.Buffer.GetBuffer(); + offset = reserved.Offset; + + return true; + } + + // Is this mirror allowed to exist? Can't be used for write in any in-flight write. + if (_waitable.IsBufferRangeInUse(offset, size, true)) + { + // Some of the data is not mirrorable, so upload the whole range. + ClearMirrors(cbs, offset, size); + + buffer = null; + return false; + } + + // Build data for the new mirror. + + var baseData = new Span<byte>((void*)(_map + offset), size); + var modData = _pendingData.AsSpan(offset, size); + + StagingBufferReserved? newMirror = _gd.BufferManager.StagingBuffer.TryReserveData(cbs, size, (int)_gd.Capabilities.MinResourceAlignment); + + if (newMirror != null) + { + var mirror = newMirror.Value; + _pendingDataRanges.FillData(baseData, modData, offset, new Span<byte>((void*)(mirror.Buffer._map + mirror.Offset), size)); + + if (_mirrors.Count == 0) + { + _gd.PipelineInternal.RegisterActiveMirror(this); + } + + _mirrors.Add(key, mirror); + + buffer = mirror.Buffer.GetBuffer(); + offset = mirror.Offset; + + return true; + } + else + { + // Data could not be placed on the mirror, likely out of space. Force the data to flush. + ClearMirrors(cbs, offset, size); + + buffer = null; + return false; + } + } + public Auto<DisposableBuffer> GetBuffer() { return _buffer; @@ -339,6 +426,86 @@ namespace Ryujinx.Graphics.Vulkan return _buffer; } + public Auto<DisposableBuffer> GetMirrorable(CommandBufferScoped cbs, ref int offset, int size, out bool mirrored) + { + if (_pendingData != null && TryGetMirror(cbs, ref offset, size, out Auto<DisposableBuffer> result)) + { + mirrored = true; + return result; + } + + mirrored = false; + return _buffer; + } + + Auto<DisposableBufferView> IMirrorable<DisposableBufferView>.GetMirrorable(CommandBufferScoped cbs, ref int offset, int size, out bool mirrored) + { + // Cannot mirror buffer views right now. + + throw new NotImplementedException(); + } + + public void ClearMirrors() + { + // Clear mirrors without forcing a flush. This happens when the command buffer is switched, + // as all reserved areas on the staging buffer are released. + + if (_pendingData != null) + { + _mirrors.Clear(); + }; + } + + public void ClearMirrors(CommandBufferScoped cbs, int offset, int size) + { + // Clear mirrors in the given range, and submit overlapping pending data. + + if (_pendingData != null) + { + bool hadMirrors = _mirrors.Count > 0 && RemoveOverlappingMirrors(offset, size); + + if (_pendingDataRanges.Count() != 0) + { + UploadPendingData(cbs, offset, size); + } + + if (hadMirrors) + { + _gd.PipelineInternal.Rebind(_buffer, offset, size); + } + }; + } + + public void UseMirrors() + { + _useMirrors = true; + } + + private void UploadPendingData(CommandBufferScoped cbs, int offset, int size) + { + var ranges = _pendingDataRanges.FindOverlaps(offset, size); + + if (ranges != null) + { + _pendingDataRanges.Remove(offset, size); + + foreach (var range in ranges) + { + int rangeOffset = Math.Max(offset, range.Offset); + int rangeSize = Math.Min(offset + size, range.End) - rangeOffset; + + if (_gd.PipelineInternal.CurrentCommandBuffer.CommandBuffer.Handle == cbs.CommandBuffer.Handle) + { + SetData(rangeOffset, _pendingData.AsSpan(rangeOffset, rangeSize), cbs, _gd.PipelineInternal.EndRenderPass, false); + } + else + { + SetData(rangeOffset, _pendingData.AsSpan(rangeOffset, rangeSize), cbs, null, false); + } + } + } + } + public void SignalWrite(int offset, int size) { ConsiderBackingSwap(); @@ -472,7 +639,34 @@ namespace Ryujinx.Graphics.Vulkan throw new InvalidOperationException("The buffer is not host mapped."); } - public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, Action endRenderPass = null) + public bool RemoveOverlappingMirrors(int offset, int size) + { + List<ulong> toRemove = null; + foreach (var key in _mirrors.Keys) + { + (int keyOffset, int keySize) = FromMirrorKey(key); + if (!(offset + size <= keyOffset || offset >= keyOffset + keySize)) + { + toRemove ??= new List<ulong>(); + + toRemove.Add(key); + } + } + + if (toRemove != null) + { + foreach (var key in toRemove) + { + _mirrors.Remove(key); + } + + return true; + } + + return false; + } + + public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, Action endRenderPass = null, bool allowCbsWait = true) { int dataSize = Math.Min(data.Length, Size - offset); if (dataSize == 0) @@ -481,6 +675,7 @@ namespace Ryujinx.Graphics.Vulkan } _setCount++; + bool allowMirror = _useMirrors && allowCbsWait && cbs != null && _currentType <= BufferAllocationType.HostMapped; if (_map != IntPtr.Zero) { @@ -488,7 +683,7 @@ namespace Ryujinx.Graphics.Vulkan bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.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); + bool needsFlush = isRented && _waitable.IsBufferRangeInUse(offset, dataSize, false); if (!needsFlush) { @@ -496,12 +691,48 @@ namespace Ryujinx.Graphics.Vulkan data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize)); + if (_pendingData != null) + { + bool removed = _pendingDataRanges.Remove(offset, dataSize); + if (RemoveOverlappingMirrors(offset, dataSize) || removed) + { + // If any mirrors were removed, rebind the buffer range. + _gd.PipelineInternal.Rebind(_buffer, offset, dataSize); + } + } + SignalWrite(offset, dataSize); return; } } + // If the buffer does not have an in-flight write (including an inline update), then upload data to a pendingCopy. + if (allowMirror && !_waitable.IsBufferRangeInUse(offset, dataSize, true)) + { + if (_pendingData == null) + { + _pendingData = new byte[Size]; + _mirrors = new Dictionary<ulong, StagingBufferReserved>(); + } + + data[..dataSize].CopyTo(_pendingData.AsSpan(offset, dataSize)); + _pendingDataRanges.Add(offset, dataSize); + + // Remove any overlapping mirrors. + RemoveOverlappingMirrors(offset, dataSize); + + // Tell the graphics device to rebind any constant buffer that overlaps the newly modified range, as it should access a mirror. + _gd.PipelineInternal.Rebind(_buffer, offset, dataSize); + + return; + } + + if (_pendingData != null) + { + _pendingDataRanges.Remove(offset, dataSize); + } + if (cbs != null && _gd.PipelineInternal.RenderPassActive && !(_buffer.HasCommandBufferDependency(cbs.Value) && @@ -519,7 +750,37 @@ namespace Ryujinx.Graphics.Vulkan data.Length > MaxUpdateBufferSize || !TryPushData(cbs.Value, endRenderPass, offset, data)) { - _gd.BufferManager.StagingBuffer.PushData(_gd.CommandBufferPool, cbs, endRenderPass, this, offset, data); + if (allowCbsWait) + { + _gd.BufferManager.StagingBuffer.PushData(_gd.CommandBufferPool, cbs, endRenderPass, this, offset, data); + } + else + { + bool rentCbs = cbs == null; + if (rentCbs) + { + cbs = _gd.CommandBufferPool.Rent(); + } + + if (!_gd.BufferManager.StagingBuffer.TryPushData(cbs.Value, endRenderPass, this, offset, data)) + { + // Need to do a slow upload. + BufferHolder srcHolder = _gd.BufferManager.Create(_gd, dataSize, baseType: BufferAllocationType.HostMapped); + srcHolder.SetDataUnchecked(0, data); + + var srcBuffer = srcHolder.GetBuffer(); + var dstBuffer = this.GetBuffer(cbs.Value.CommandBuffer, true); + + Copy(_gd, cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize); + + srcHolder.Dispose(); + } + + if (rentCbs) + { + cbs.Value.Dispose(); + } + } } } @@ -558,7 +819,7 @@ namespace Ryujinx.Graphics.Vulkan endRenderPass?.Invoke(); - var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value; + var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length, true).Value; _writeCount--; @@ -608,7 +869,7 @@ namespace Ryujinx.Graphics.Vulkan bool registerSrcUsage = true) { var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value; - var dstBuffer = dst.Get(cbs, dstOffset, size).Value; + var dstBuffer = dst.Get(cbs, dstOffset, size, true).Value; InsertBufferBarrier( gd, diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index b916a1ef2..380967022 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -100,9 +100,10 @@ namespace Ryujinx.Graphics.Vulkan VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, - BufferHandle storageHint = default) + BufferHandle storageHint = default, + bool forceMirrors = false) { - return CreateWithHandle(gd, size, out _, baseType, storageHint); + return CreateWithHandle(gd, size, out _, baseType, storageHint, forceMirrors); } public BufferHandle CreateWithHandle( @@ -110,7 +111,8 @@ namespace Ryujinx.Graphics.Vulkan int size, out BufferHolder holder, BufferAllocationType baseType = BufferAllocationType.HostMapped, - BufferHandle storageHint = default) + BufferHandle storageHint = default, + bool forceMirrors = false) { holder = Create(gd, size, baseType: baseType, storageHint: storageHint); if (holder == null) @@ -118,6 +120,11 @@ namespace Ryujinx.Graphics.Vulkan return BufferHandle.Null; } + if (forceMirrors) + { + holder.UseMirrors(); + } + BufferCount++; ulong handle64 = (uint)_buffers.Add(holder); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs b/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs new file mode 100644 index 000000000..9e0b7244a --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Vulkan +{ + /// <summary> + /// A structure tracking pending upload ranges for buffers. + /// Where a range is present, pending data exists that can either be used to build mirrors + /// or upload directly to the buffer. + /// </summary> + struct BufferMirrorRangeList + { + internal readonly struct Range + { + public int Offset { get; } + public int Size { get; } + + public int End => Offset + Size; + + public Range(int offset, int size) + { + Offset = offset; + Size = size; + } + + public bool OverlapsWith(int offset, int size) + { + return Offset < offset + size && offset < Offset + Size; + } + } + + private List<Range> _ranges; + + public readonly IEnumerable<Range> All() + { + return _ranges; + } + + public readonly bool Remove(int offset, int size) + { + var list = _ranges; + bool removedAny = false; + if (list != null) + { + int overlapIndex = BinarySearch(list, offset, size); + + if (overlapIndex >= 0) + { + // Overlaps with a range. Search back to find the first one it doesn't overlap with. + + while (overlapIndex > 0 && list[overlapIndex - 1].OverlapsWith(offset, size)) + { + overlapIndex--; + } + + int endOffset = offset + size; + int startIndex = overlapIndex; + + var currentOverlap = list[overlapIndex]; + + // Orphan the start of the overlap. + if (currentOverlap.Offset < offset) + { + list[overlapIndex] = new Range(currentOverlap.Offset, offset - currentOverlap.Offset); + currentOverlap = new Range(offset, currentOverlap.End - offset); + list.Insert(++overlapIndex, currentOverlap); + startIndex++; + + removedAny = true; + } + + // Remove any middle overlaps. + while (currentOverlap.Offset < endOffset) + { + if (currentOverlap.End > endOffset) + { + // Update the end overlap instead of removing it, if it spans beyond the removed range. + list[overlapIndex] = new Range(endOffset, currentOverlap.End - endOffset); + + removedAny = true; + break; + } + + if (++overlapIndex >= list.Count) + { + break; + } + + currentOverlap = list[overlapIndex]; + } + + int count = overlapIndex - startIndex; + + list.RemoveRange(startIndex, count); + + removedAny |= count > 0; + } + } + + return removedAny; + } + + public void Add(int offset, int size) + { + var list = _ranges; + if (list != null) + { + int overlapIndex = BinarySearch(list, offset, size); + if (overlapIndex >= 0) + { + while (overlapIndex > 0 && list[overlapIndex - 1].OverlapsWith(offset, size)) + { + overlapIndex--; + } + + int endOffset = offset + size; + int startIndex = overlapIndex; + + while (overlapIndex < list.Count && list[overlapIndex].OverlapsWith(offset, size)) + { + var currentOverlap = list[overlapIndex]; + var currentOverlapEndOffset = currentOverlap.Offset + currentOverlap.Size; + + if (offset > currentOverlap.Offset) + { + offset = currentOverlap.Offset; + } + + if (endOffset < currentOverlapEndOffset) + { + endOffset = currentOverlapEndOffset; + } + + overlapIndex++; + size = endOffset - offset; + } + + int count = overlapIndex - startIndex; + + list.RemoveRange(startIndex, count); + + overlapIndex = startIndex; + } + else + { + overlapIndex = ~overlapIndex; + } + + list.Insert(overlapIndex, new Range(offset, size)); + } + else + { + _ranges = new List<Range> + { + new Range(offset, size) + }; + } + } + + public readonly bool OverlapsWith(int offset, int size) + { + var list = _ranges; + if (list == null) + { + return false; + } + + return BinarySearch(list, offset, size) >= 0; + } + + public readonly List<Range> FindOverlaps(int offset, int size) + { + var list = _ranges; + if (list == null) + { + return null; + } + + List<Range> result = null; + + int index = BinarySearch(list, offset, size); + + if (index >= 0) + { + while (index > 0 && list[index - 1].OverlapsWith(offset, size)) + { + index--; + } + + do + { + (result ??= new List<Range>()).Add(list[index++]); + } + while (index < list.Count && list[index].OverlapsWith(offset, size)); + } + + return result; + } + + private static int BinarySearch(List<Range> list, int offset, int size) + { + int left = 0; + int right = list.Count - 1; + + while (left <= right) + { + int range = right - left; + + int middle = left + (range >> 1); + + var item = list[middle]; + + if (item.OverlapsWith(offset, size)) + { + return middle; + } + + if (offset < item.Offset) + { + right = middle - 1; + } + else + { + left = middle + 1; + } + } + + return ~left; + } + + public readonly void FillData(Span<byte> baseData, Span<byte> modData, int offset, Span<byte> result) + { + int size = baseData.Length; + int endOffset = offset + size; + + var list = _ranges; + if (list == null) + { + baseData.CopyTo(result); + } + + int srcOffset = offset; + int dstOffset = 0; + bool activeRange = false; + + for (int i = 0; i < list.Count; i++) + { + var range = list[i]; + + int rangeEnd = range.Offset + range.Size; + + if (activeRange) + { + if (range.Offset >= endOffset) + { + break; + } + } + else + { + if (rangeEnd <= offset) + { + continue; + } + + activeRange = true; + } + + int baseSize = range.Offset - srcOffset; + + if (baseSize > 0) + { + baseData.Slice(dstOffset, baseSize).CopyTo(result.Slice(dstOffset, baseSize)); + srcOffset += baseSize; + dstOffset += baseSize; + } + + int modSize = Math.Min(rangeEnd - srcOffset, endOffset - srcOffset); + if (modSize != 0) + { + modData.Slice(dstOffset, modSize).CopyTo(result.Slice(dstOffset, modSize)); + srcOffset += modSize; + dstOffset += modSize; + } + } + + int baseSizeEnd = endOffset - srcOffset; + + if (baseSizeEnd > 0) + { + baseData.Slice(dstOffset, baseSizeEnd).CopyTo(result.Slice(dstOffset, baseSizeEnd)); + } + } + + public readonly int Count() + { + return _ranges?.Count ?? 0; + } + + public void Clear() + { + _ranges = null; + } + } +} diff --git a/src/Ryujinx.Graphics.Vulkan/BufferState.cs b/src/Ryujinx.Graphics.Vulkan/BufferState.cs index ee4badd2f..198ee54d4 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferState.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan { if (_buffer != null) { - var buffer = _buffer.Get(cbs, _offset, _size).Value; + var buffer = _buffer.Get(cbs, _offset, _size, true).Value; gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size); } @@ -40,6 +40,11 @@ namespace Ryujinx.Graphics.Vulkan } } + public readonly bool Overlaps(Auto<DisposableBuffer> buffer, int offset, int size) + { + return buffer == _buffer && offset < _offset + _size && offset + size > _offset; + } + public readonly void Dispose() { _buffer?.DecrementReferenceCount(); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs b/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs index a8ff7c286..19dcaccd9 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferUsageBitmap.cs @@ -6,6 +6,7 @@ private readonly int _size; private readonly int _granularity; private readonly int _bits; + private readonly int _writeBitOffset; private readonly int _intsPerCb; private readonly int _bitsPerCb; @@ -14,7 +15,11 @@ { _size = size; _granularity = granularity; - _bits = (size + (granularity - 1)) / 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; @@ -22,7 +27,7 @@ _bitmap = new BitMap(_bitsPerCb * CommandBufferPool.MaxCommandBuffers); } - public void Add(int cbIndex, int offset, int size) + public void Add(int cbIndex, int offset, int size, bool write) { if (size == 0) { @@ -35,32 +40,32 @@ size = _size - offset; } - int cbBase = cbIndex * _bitsPerCb; + 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) + public bool OverlapsWith(int cbIndex, int offset, int size, bool write = false) { if (size == 0) { return false; } - int cbBase = cbIndex * _bitsPerCb; + 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) + public bool OverlapsWith(int offset, int size, bool write) { for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++) { - if (OverlapsWith(i, offset, size)) + if (OverlapsWith(i, offset, size, write)) { return true; } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 45392b642..14e4c02f0 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -1,9 +1,9 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; using System.Runtime.CompilerServices; -using Buffer = Silk.NET.Vulkan.Buffer; using CompareOp = Ryujinx.Graphics.GAL.CompareOp; using Format = Ryujinx.Graphics.GAL.Format; using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; @@ -12,13 +12,34 @@ namespace Ryujinx.Graphics.Vulkan { class DescriptorSetUpdater { + private const ulong StorageBufferMaxMirrorable = 0x2000; + private record struct BufferRef + { + public Auto<DisposableBuffer> Buffer; + public int Offset; + public bool Write; + + public BufferRef(Auto<DisposableBuffer> buffer) + { + Buffer = buffer; + Offset = 0; + Write = true; + } + + public BufferRef(Auto<DisposableBuffer> buffer, ref BufferRange range) + { + Buffer = buffer; + Offset = range.Offset; + Write = range.Write; + } + } + private readonly VulkanRenderer _gd; private readonly PipelineBase _pipeline; - private ShaderCollection _program; - private readonly Auto<DisposableBuffer>[] _uniformBufferRefs; - private readonly Auto<DisposableBuffer>[] _storageBufferRefs; + private readonly BufferRef[] _uniformBufferRefs; + private readonly BufferRef[] _storageBufferRefs; private readonly Auto<DisposableImageView>[] _textureRefs; private readonly Auto<DisposableSampler>[] _samplerRefs; private readonly Auto<DisposableImageView>[] _imageRefs; @@ -33,8 +54,10 @@ namespace Ryujinx.Graphics.Vulkan private readonly BufferView[] _bufferTextures; private readonly BufferView[] _bufferImages; - private readonly bool[] _uniformSet; - private readonly bool[] _storageSet; + private BitMapStruct<Array2<long>> _uniformSet; + private BitMapStruct<Array2<long>> _storageSet; + private BitMapStruct<Array2<long>> _uniformMirrored; + private BitMapStruct<Array2<long>> _storageMirrored; [Flags] private enum DirtyFlags @@ -61,8 +84,8 @@ namespace Ryujinx.Graphics.Vulkan // Some of the bindings counts needs to be multiplied by 2 because we have buffer and // regular textures/images interleaved on the same descriptor set. - _uniformBufferRefs = new Auto<DisposableBuffer>[Constants.MaxUniformBufferBindings]; - _storageBufferRefs = new Auto<DisposableBuffer>[Constants.MaxStorageBufferBindings]; + _uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; + _storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; _textureRefs = new Auto<DisposableImageView>[Constants.MaxTextureBindings * 2]; _samplerRefs = new Auto<DisposableSampler>[Constants.MaxTextureBindings * 2]; _imageRefs = new Auto<DisposableImageView>[Constants.MaxImageBindings * 2]; @@ -85,9 +108,6 @@ namespace Ryujinx.Graphics.Vulkan _textures.AsSpan().Fill(initialImageInfo); _images.AsSpan().Fill(initialImageInfo); - _uniformSet = new bool[Constants.MaxUniformBufferBindings]; - _storageSet = new bool[Constants.MaxStorageBufferBindings]; - if (gd.Capabilities.SupportsNullDescriptors) { // If null descriptors are supported, we can pass null as the handle. @@ -138,6 +158,63 @@ namespace Ryujinx.Graphics.Vulkan _dummyTexture.SetData(dummyTextureData); } + private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size) + { + return offset < bindingOffset + (int)info.Range && (offset + size) > bindingOffset; + } + + internal void Rebind(Auto<DisposableBuffer> buffer, int offset, int size) + { + if (_program == null) + { + return; + } + + // Check stage bindings + + _uniformMirrored.Union(_uniformSet).SignalSet((int binding, int count) => + { + for (int i = 0; i < count; i++) + { + ref BufferRef bufferRef = ref _uniformBufferRefs[binding]; + if (bufferRef.Buffer == buffer) + { + ref DescriptorBufferInfo info = ref _uniformBuffers[binding]; + int bindingOffset = bufferRef.Offset; + + if (BindingOverlaps(ref info, bindingOffset, offset, size)) + { + _uniformSet.Clear(binding); + SignalDirty(DirtyFlags.Uniform); + } + } + + binding++; + } + }); + + _storageMirrored.Union(_storageSet).SignalSet((int binding, int count) => + { + for (int i = 0; i < count; i++) + { + ref BufferRef bufferRef = ref _storageBufferRefs[binding]; + if (bufferRef.Buffer == buffer) + { + ref DescriptorBufferInfo info = ref _storageBuffers[binding]; + int bindingOffset = bufferRef.Offset; + + if (BindingOverlaps(ref info, bindingOffset, offset, size)) + { + _storageSet.Clear(binding); + SignalDirty(DirtyFlags.Storage); + } + } + + binding++; + } + }); + } + public void SetProgram(ShaderCollection program) { _program = program; @@ -180,22 +257,28 @@ namespace Ryujinx.Graphics.Vulkan var buffer = assignment.Range; int index = assignment.Binding; - Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false, isSSBO: true); - ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index]; + Auto<DisposableBuffer> vkBuffer = buffer.Handle == BufferHandle.Null + ? null + : _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, buffer.Write, isSSBO: true); + + ref BufferRef currentBufferRef = ref _storageBufferRefs[index]; DescriptorBufferInfo info = new() { Offset = (ulong)buffer.Offset, Range = (ulong)buffer.Size, }; + + var newRef = new BufferRef(vkBuffer, ref buffer); + ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index]; - if (vkBuffer != currentVkBuffer || currentInfo.Offset != info.Offset || currentInfo.Range != info.Range) + if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) { - _storageSet[index] = false; + _storageSet.Clear(index); currentInfo = info; - currentVkBuffer = vkBuffer; + currentBufferRef = newRef; } } @@ -209,21 +292,24 @@ namespace Ryujinx.Graphics.Vulkan var vkBuffer = buffers[i]; int index = first + i; - ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index]; + ref BufferRef currentBufferRef = ref _storageBufferRefs[index]; DescriptorBufferInfo info = new() { Offset = 0, Range = Vk.WholeSize, }; + + BufferRef newRef = new(vkBuffer); + ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index]; - if (vkBuffer != currentVkBuffer || currentInfo.Offset != info.Offset || currentInfo.Range != info.Range) + if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) { - _storageSet[index] = false; + _storageSet.Clear(index); currentInfo = info; - currentVkBuffer = vkBuffer; + currentBufferRef = newRef; } } @@ -288,22 +374,28 @@ namespace Ryujinx.Graphics.Vulkan var buffer = assignment.Range; int index = assignment.Binding; - Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); - ref Auto<DisposableBuffer> currentVkBuffer = ref _uniformBufferRefs[index]; + Auto<DisposableBuffer> vkBuffer = buffer.Handle == BufferHandle.Null + ? null + : _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); + + ref BufferRef currentBufferRef = ref _uniformBufferRefs[index]; DescriptorBufferInfo info = new() { Offset = (ulong)buffer.Offset, Range = (ulong)buffer.Size, }; + + BufferRef newRef = new(vkBuffer, ref buffer); + ref DescriptorBufferInfo currentInfo = ref _uniformBuffers[index]; - if (vkBuffer != currentVkBuffer || currentInfo.Offset != info.Offset || currentInfo.Range != info.Range) + if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) { - _uniformSet[index] = false; + _uniformSet.Clear(index); currentInfo = info; - currentVkBuffer = vkBuffer; + currentBufferRef = newRef; } } @@ -353,13 +445,26 @@ namespace Ryujinx.Graphics.Vulkan } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void UpdateBuffer( + private static bool UpdateBuffer( CommandBufferScoped cbs, ref DescriptorBufferInfo info, - Auto<DisposableBuffer> buffer, - Auto<DisposableBuffer> dummyBuffer) + ref BufferRef buffer, + Auto<DisposableBuffer> dummyBuffer, + bool mirrorable) { - info.Buffer = buffer?.Get(cbs, (int)info.Offset, (int)info.Range).Value ?? default; + int offset = buffer.Offset; + bool mirrored = false; + + if (mirrorable) + { + info.Buffer = buffer.Buffer?.GetMirrorable(cbs, ref offset, (int)info.Range, out mirrored).Value ?? default; + } + else + { + info.Buffer = buffer.Buffer?.Get(cbs, offset, (int)info.Range, buffer.Write).Value ?? default; + } + + info.Offset = (ulong)offset; // The spec requires that buffers with null handle have offset as 0 and range as VK_WHOLE_SIZE. if (info.Buffer.Handle == 0) @@ -368,6 +473,8 @@ namespace Ryujinx.Graphics.Vulkan info.Offset = 0; info.Range = Vk.WholeSize; } + + return mirrored; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -404,11 +511,13 @@ namespace Ryujinx.Graphics.Vulkan { int index = binding + i; - if (!_uniformSet[index]) + if (_uniformSet.Set(index)) { - UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); + ref BufferRef buffer = ref _uniformBufferRefs[index]; - _uniformSet[index] = true; + bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true); + + _uniformMirrored.Set(index, mirrored); } } @@ -421,11 +530,19 @@ namespace Ryujinx.Graphics.Vulkan { int index = binding + i; - if (!_storageSet[index]) - { - UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer); + ref BufferRef buffer = ref _storageBufferRefs[index]; - _storageSet[index] = true; + if (_storageSet.Set(index)) + { + ref var info = ref _storageBuffers[index]; + + bool mirrored = UpdateBuffer(cbs, + ref info, + ref _storageBufferRefs[index], + dummyBuffer, + !buffer.Write && info.Range <= StorageBufferMaxMirrorable); + + _storageMirrored.Set(index, mirrored); } } @@ -464,7 +581,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { - bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default; + bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default; } dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer); @@ -489,7 +606,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { - bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default; + bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default; } dsc.UpdateBufferImages(0, binding, bufferImages[..count], DescriptorType.StorageTexelBuffer); @@ -546,10 +663,10 @@ namespace Ryujinx.Graphics.Vulkan { int index = binding + i; - if (!_uniformSet[index]) + if (_uniformSet.Set(index)) { - UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); - _uniformSet[index] = true; + ref BufferRef buffer = ref _uniformBufferRefs[index]; + UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true); doUpdate = true; } } @@ -582,17 +699,17 @@ namespace Ryujinx.Graphics.Vulkan { _dirty = DirtyFlags.All; - Array.Clear(_uniformSet); - Array.Clear(_storageSet); + _uniformSet.Clear(); + _storageSet.Clear(); } - private static void SwapBuffer(Auto<DisposableBuffer>[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) + private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) { for (int i = 0; i < list.Length; i++) { - if (list[i] == from) + if (list[i].Buffer == from) { - list[i] = to; + list[i].Buffer = to; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 9323fcf97..f478c58e2 100644 --- a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -427,6 +427,7 @@ namespace Ryujinx.Graphics.Vulkan return access switch { BufferAccess.FlushPersistent => BufferAllocationType.HostMapped, + BufferAccess.Stream => BufferAllocationType.HostMapped, _ => BufferAllocationType.Auto, }; } diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 11d9c4fb4..e76a332f4 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -52,6 +52,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly PortabilitySubsetFlags PortabilitySubset; public readonly uint VertexBufferAlignment; public readonly uint SubTexelPrecisionBits; + public readonly ulong MinResourceAlignment; public HardwareCapabilities( bool supportsIndexTypeUint8, @@ -89,7 +90,8 @@ namespace Ryujinx.Graphics.Vulkan SampleCountFlags supportedSampleCounts, PortabilitySubsetFlags portabilitySubset, uint vertexBufferAlignment, - uint subTexelPrecisionBits) + uint subTexelPrecisionBits, + ulong minResourceAlignment) { SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsCustomBorderColor = supportsCustomBorderColor; @@ -127,6 +129,7 @@ namespace Ryujinx.Graphics.Vulkan PortabilitySubset = portabilitySubset; VertexBufferAlignment = vertexBufferAlignment; SubTexelPrecisionBits = subTexelPrecisionBits; + MinResourceAlignment = minResourceAlignment; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs index b839619e9..b85f0c7f0 100644 --- a/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/IndexBufferState.cs @@ -5,6 +5,8 @@ namespace Ryujinx.Graphics.Vulkan { internal struct IndexBufferState { + private const int IndexBufferMaxMirrorable = 0x20000; + public static IndexBufferState Null => new(BufferHandle.Null, 0, 0); private readonly int _offset; @@ -37,6 +39,7 @@ namespace Ryujinx.Graphics.Vulkan Auto<DisposableBuffer> autoBuffer; int offset, size; IndexType type = _type; + bool mirrorable = false; if (_type == IndexType.Uint8Ext && !gd.Capabilities.SupportsIndexTypeUint8) { @@ -56,6 +59,8 @@ namespace Ryujinx.Graphics.Vulkan autoBuffer = null; } + mirrorable = _size < IndexBufferMaxMirrorable; + offset = _offset; size = _size; } @@ -64,7 +69,9 @@ namespace Ryujinx.Graphics.Vulkan if (autoBuffer != null) { - gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, offset, size).Value, (ulong)offset, type); + DisposableBuffer buffer = mirrorable ? autoBuffer.GetMirrorable(cbs, ref offset, size, out _) : autoBuffer.Get(cbs, offset, size); + + gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, buffer.Value, (ulong)offset, type); } } @@ -155,5 +162,10 @@ namespace Ryujinx.Graphics.Vulkan _buffer = to; } } + + public readonly bool Overlaps(Auto<DisposableBuffer> buffer, int offset, int size) + { + return buffer == _buffer && offset < _offset + _size && offset + size > _offset; + } } } diff --git a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs index 71769c5e1..4d2d312fe 100644 --- a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs @@ -32,14 +32,20 @@ namespace Ryujinx.Graphics.Vulkan } /// <summary> - /// Adds buffer usage information to the uses list. + /// Adds read/write buffer usage information to the uses list. /// </summary> /// <param name="cbIndex">Index of the command buffer where the buffer is used</param> /// <param name="offset">Offset of the buffer being used</param> /// <param name="size">Size of the buffer region being used, in bytes</param> - public void AddBufferUse(int cbIndex, int offset, int size) + /// <param name="write">Whether the access is a write or not</param> + public void AddBufferUse(int cbIndex, int offset, int size, bool write) { - _bufferUsageBitmap.Add(cbIndex, offset, size); + _bufferUsageBitmap.Add(cbIndex, offset, size, false); + + if (write) + { + _bufferUsageBitmap.Add(cbIndex, offset, size, true); + } } /// <summary> @@ -68,10 +74,11 @@ namespace Ryujinx.Graphics.Vulkan /// </summary> /// <param name="offset">Offset of the buffer being used</param> /// <param name="size">Size of the buffer region being used, in bytes</param> + /// <param name="write">True if only write usages should count</param> /// <returns>True if in use, false otherwise</returns> - public bool IsBufferRangeInUse(int offset, int size) + public bool IsBufferRangeInUse(int offset, int size, bool write) { - return _bufferUsageBitmap.OverlapsWith(offset, size); + return _bufferUsageBitmap.OverlapsWith(offset, size, write); } /// <summary> diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 8b931e526..67b16ec96 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -193,7 +193,7 @@ namespace Ryujinx.Graphics.Vulkan { EndRenderPass(); - var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, offset, size, true).Get(Cbs, offset, size).Value; + var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, offset, size, true).Get(Cbs, offset, size, true).Value; BufferHolder.InsertBufferBarrier( Gd, @@ -469,6 +469,10 @@ namespace Ryujinx.Graphics.Vulkan return; } + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + UpdateIndexBufferPattern(); RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); BeginRenderPass(); @@ -498,10 +502,6 @@ namespace Ryujinx.Graphics.Vulkan } else { - var buffer = Gd.BufferManager - .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - ResumeTransformFeedbackInternal(); Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size); @@ -515,15 +515,19 @@ namespace Ryujinx.Graphics.Vulkan return; } + var countBuffer = Gd.BufferManager + .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) + .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; + + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; + UpdateIndexBufferPattern(); RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); BeginRenderPass(); DrawCount++; - var countBuffer = Gd.BufferManager - .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) - .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; - if (_indexBufferPattern != null) { // Convert the index buffer into a supported topology. @@ -570,10 +574,6 @@ namespace Ryujinx.Graphics.Vulkan } else { - var buffer = Gd.BufferManager - .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - ResumeTransformFeedbackInternal(); if (Gd.Capabilities.SupportsIndirectParameters) @@ -609,15 +609,15 @@ namespace Ryujinx.Graphics.Vulkan // TODO: Support quads and other unsupported topologies. + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value; + RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); BeginRenderPass(); ResumeTransformFeedbackInternal(); DrawCount++; - var buffer = Gd.BufferManager - .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - Gd.Api.CmdDrawIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size); } @@ -634,6 +634,14 @@ namespace Ryujinx.Graphics.Vulkan return; } + var buffer = Gd.BufferManager + .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) + .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value; + + var countBuffer = Gd.BufferManager + .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) + .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size, false).Value; + // TODO: Support quads and other unsupported topologies. RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); @@ -641,14 +649,6 @@ namespace Ryujinx.Graphics.Vulkan ResumeTransformFeedbackInternal(); DrawCount++; - var buffer = Gd.BufferManager - .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - - var countBuffer = Gd.BufferManager - .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) - .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; - Gd.DrawIndirectCountApi.CmdDrawIndirectCount( CommandBuffer, buffer, @@ -709,6 +709,26 @@ namespace Ryujinx.Graphics.Vulkan return CommandBuffer.Handle == cb.Handle; } + internal void Rebind(Auto<DisposableBuffer> buffer, int offset, int size) + { + _descriptorSetUpdater.Rebind(buffer, offset, size); + + if (_indexBuffer.Overlaps(buffer, offset, size)) + { + _indexBuffer.BindIndexBuffer(Gd, Cbs); + } + + for (int i = 0; i < _vertexBuffers.Length; i++) + { + if (_vertexBuffers[i].Overlaps(buffer, offset, size)) + { + _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater); + } + } + + _vertexBufferUpdater.Commit(Cbs); + } + #pragma warning disable CA1822 // Mark member as static public void SetAlphaTest(bool enable, float reference, CompareOp op) { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs index dfdac52fd..dcc6c5300 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Vulkan private CounterQueueEvent _activeConditionalRender; private readonly List<BufferedQuery> _pendingQueryCopies; + private readonly List<BufferHolder> _activeBufferMirrors; private ulong _byteWeight; @@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.Vulkan _activeQueries = new List<(QueryPool, bool)>(); _pendingQueryCopies = new(); _backingSwaps = new(); + _activeBufferMirrors = new(); CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; } @@ -233,6 +235,12 @@ namespace Ryujinx.Graphics.Vulkan Gd.RegisterFlush(); // Restore per-command buffer state. + foreach (BufferHolder buffer in _activeBufferMirrors) + { + buffer.ClearMirrors(); + } + + _activeBufferMirrors.Clear(); foreach ((var queryPool, var isOcclusion) in _activeQueries) { @@ -249,6 +257,11 @@ namespace Ryujinx.Graphics.Vulkan Restore(); } + public void RegisterActiveMirror(BufferHolder buffer) + { + _activeBufferMirrors.Add(buffer); + } + public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool isOcclusion, bool fromSamplePool) { if (needsReset) diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index 8d4fdc196..2a85429fb 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -183,7 +183,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries public void PoolCopy(CommandBufferScoped cbs) { - var buffer = _buffer.GetBuffer(cbs.CommandBuffer, true).Get(cbs, 0, sizeof(long)).Value; + var buffer = _buffer.GetBuffer(cbs.CommandBuffer, true).Get(cbs, 0, sizeof(long), true).Value; QueryResultFlags flags = QueryResultFlags.ResultWaitBit; diff --git a/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs b/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs index 00fa6477d..32ec8c7c6 100644 --- a/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs @@ -1,12 +1,28 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; using System; using System.Collections.Generic; using System.Diagnostics; namespace Ryujinx.Graphics.Vulkan { + 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; + } + } + class StagingBuffer : IDisposable { - private const int BufferSize = 16 * 1024 * 1024; + private const int BufferSize = 32 * 1024 * 1024; private int _freeOffset; private int _freeSize; @@ -130,13 +146,83 @@ namespace Ryujinx.Graphics.Vulkan } } - endRenderPass(); + endRenderPass?.Invoke(); 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 + ); + } + + /// <summary> + /// Reserve a range on the staging buffer for the current command buffer and upload data to it. + /// </summary> + /// <param name="cbs">Command buffer to reserve the data on</param> + /// <param name="data">The data to upload</param> + /// <param name="alignment">The required alignment for the buffer offset</param> + /// <returns>The reserved range of the staging buffer</returns> + public unsafe 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); + } + private bool WaitFreeCompleted(CommandBufferPool cbp) { if (_pendingCopies.TryPeek(out var pc)) diff --git a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index ddcf51f69..285a56498 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -127,24 +127,24 @@ namespace Ryujinx.Graphics.Vulkan ReleaseImpl(); } - public BufferView GetBufferView(CommandBufferScoped cbs) + public BufferView GetBufferView(CommandBufferScoped cbs, bool write) { _bufferView ??= _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size, ReleaseImpl); - return _bufferView?.Get(cbs, _offset, _size).Value ?? default; + return _bufferView?.Get(cbs, _offset, _size, write).Value ?? default; } - public BufferView GetBufferView(CommandBufferScoped cbs, Format format) + public BufferView GetBufferView(CommandBufferScoped cbs, Format format, bool write) { var vkFormat = FormatTable.GetFormat(format); if (vkFormat == VkFormat) { - return GetBufferView(cbs); + return GetBufferView(cbs, write); } if (_selfManagedViews != null && _selfManagedViews.TryGetValue(format, out var bufferView)) { - return bufferView.Get(cbs, _offset, _size).Value; + return bufferView.Get(cbs, _offset, _size, write).Value; } bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size, ReleaseImpl); @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan (_selfManagedViews ??= new Dictionary<Format, Auto<DisposableBufferView>>()).Add(format, bufferView); } - return bufferView?.Get(cbs, _offset, _size).Value ?? default; + return bufferView?.Get(cbs, _offset, _size, write).Value ?? default; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index cbbd829ab..9a943bf98 100644 --- a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -4,6 +4,8 @@ namespace Ryujinx.Graphics.Vulkan { internal struct VertexBufferState { + private const int VertexBufferMaxMirrorable = 0x20000; + public static VertexBufferState Null => new(null, 0, 0, 0); private readonly int _offset; @@ -88,9 +90,11 @@ namespace Ryujinx.Graphics.Vulkan if (autoBuffer != null) { - var buffer = autoBuffer.Get(cbs, _offset, _size).Value; + int offset = _offset; + bool mirrorable = _size <= VertexBufferMaxMirrorable; + var buffer = mirrorable ? autoBuffer.GetMirrorable(cbs, ref offset, _size, out _).Value : autoBuffer.Get(cbs, offset, _size).Value; - updater.BindVertexBuffer(cbs, binding, buffer, (ulong)_offset, (ulong)_size, (ulong)_stride); + updater.BindVertexBuffer(cbs, binding, buffer, (ulong)offset, (ulong)_size, (ulong)_stride); } } @@ -99,6 +103,11 @@ namespace Ryujinx.Graphics.Vulkan return _buffer == buffer; } + public readonly bool Overlaps(Auto<DisposableBuffer> buffer, int offset, int size) + { + return buffer == _buffer && offset < _offset + _size && offset + size > _offset; + } + public readonly bool Matches(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0) { return _buffer == buffer && DescriptorIndex == descriptorIndex && _offset == offset && _size == size && _stride == stride; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 11c3bfe4e..20b32c70b 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -293,6 +293,13 @@ namespace Ryujinx.Graphics.Vulkan ref var properties = ref properties2.Properties; + ulong minResourceAlignment = Math.Max( + Math.Max( + properties.Limits.MinStorageBufferOffsetAlignment, + properties.Limits.MinUniformBufferOffsetAlignment), + properties.Limits.MinTexelBufferOffsetAlignment + ); + SampleCountFlags supportedSampleCounts = properties.Limits.FramebufferColorSampleCounts & properties.Limits.FramebufferDepthSampleCounts & @@ -334,7 +341,8 @@ namespace Ryujinx.Graphics.Vulkan supportedSampleCounts, portabilityFlags, vertexBufferAlignment, - properties.Limits.SubTexelPrecisionBits); + properties.Limits.SubTexelPrecisionBits, + minResourceAlignment); IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice); @@ -397,7 +405,7 @@ namespace Ryujinx.Graphics.Vulkan public BufferHandle CreateBuffer(int size, BufferAccess access) { - return BufferManager.CreateWithHandle(this, size, access.Convert()); + return BufferManager.CreateWithHandle(this, size, access.Convert(), default, access == BufferAccess.Stream); } public BufferHandle CreateBuffer(int size, BufferHandle storageHint)

    z#hYuyt`9N`BZC>U+PA^W9T6Jvv}>U1C+k8HHr2 z;6oo=PT-eOMUO5vZ%V5YJx^*jZA42YEItC(%ZSg{s~CZ&2BVY@C?D?U_;7d&%=l1V zrt93?Ma2nN+B*>m6xkBM21DvB5tfuFS8c8ny_ZD+#3u+o5Bm$I>yHdJfZotgW>?@| z?8oonwE^Ph8fN{A%~dQ{$C_45aZ$y;?H2wLx} zU)_Q_dCI+b0Z*g!_>Y8LNn4}579@~2!r#pe%dX=nkYtyNsolNrlnCa`GeXY^gx$i{e*Q@Yi zD19CLZ+L$F(Q}H^S9pDpv^|Kzbo*!Dwh#*Y0l~Zfn_J157 zuWyNjkH6BLKzwX`@Knb~oA&YP-*bEf+sCnV9oT@ooZH7G1)XajAG16y+|#N&{WPM! zcHzY^@^o`}ef?7YsU=U38J~art~Go<4D>{>CojN@q4=B@0iV~n|3AU!z=qcF`AKB@ ztlb?BpMS>-;q1v61ctH9KV|ImcG;6()(1&*-JblI?gUEnD3>P9Pd$55^H(7>YBk3< zWyG3Uc=P6sy1dq$R;T4*#!ql~llwE6KbWGp=J=*uuAHyvKyr8RpB4ecU*Znd{3mVV z=ciY=H^3DB-Fts$;m3$?Mx4*y%R=B4I}zRxe>GB50vxAwverIj!#9n(yM2J}WDpVE zv;zMS{^6pVx7^1hQ%6KMP3y5Bc@iG}9ueVpu4BSB9Q>~??0EQ1^c{%ya^;4vb*9od zzaK$#qB%dvztS#zKff~~y}TTeUPj-0Lh&75-o9KJg7p{g=>)6?#Wy3$ll)IY;N836 zB!)M(JOo~`)8LJy&%UbBf#mw^`T0QU@z7`Ol(%030Nt@0Aa5s^MU+?j@9ud1X(#+= zM1F@YiB&GSJv`XajDWjn@RM&(yfT5i>049IK=RcT0ua?rvl_(?}sBm*>G1vJ^rG;+a z91Z?Ft3yo7Z|8Lu0R-u<_RRmQq3{pwZ1_Xu9gBbLli0CvM7~<2KCMwoBg>i9^;u8x zxf}e33<*jJT=P*xBWUEE9V0{{$D5T=)^0N1DwZB=2V+Bo{$GT5kjF9gx!d z%jL^C$21=2ttabvZfJ=(KWqLMaeiJ#t*H*2pLXl7XPb4R*Mv=10urW2-O_WH0K8jC)dr!X`ZRbBg>KQbt;oKhqO|F8(ua z(OmrgQzT_1i2qFSS|l?9GdPePi8BU%kW#}E8I_$?gr6T$O@UjXuo?JiN}+^LrG)OA z1Gv$1IwOD^G(J5)sLlDGfP)}fpZ_!IiJtoQ)U41zjJxnQ4Vi4Z2QkH7Sz# zvjgzE7U7bBq|*TWp7q~wNdgSN5A!&Sk#x%N+w?YJJv8a`yjiCLez89_#6JM}2Nwa8 z?)wbsu4}&n3NN(E-LupHgejdr^>?QlcXNBX1L)5N_iJSO+l7M_BJg(-_uB^1-wM1C zj{e@9c?#2C>-Ja4TWwL`i}XaG{(XMNsYZdvY@cL8B(=_=r_mFE___b4|8MX!;q|ug zb0$3zh@YFLpW^swzy1Ew$Aq8A_Pd+z13O4q;R%@IZ|p4ly#wU)5R5Lee17V+i1xvE zC1LF^ybw-4_n3An$mb5g@7m(l@Vo2Pi1>XNFNWH;EAT=%{Jwep|EKuztGbjHb0l*a8>6KmO3Mm)?aPU$M4>cG#?4~M5M37lTP@2Ab->}A0hlc?S+tO znMy#OD)K7hpCRx+edQ?x|KFbrfq&a?IvM`9^z9#3=rD7AJCq)nD1yP_0(ck0|YSl$Je1Sj>)PKp9sY=r;C|4?gve7gprQ-*{ODYPIGs z&c=U7OquAfF$!S{`SVc`5XDx7f;;d*t%@n+-;OV?7pX8N5(p0}|9oq&zdjk)%x~ft zD*on$<~K9xO%J_^pIBuMz4;kFHoIO(!@pEI#wZrgtfH2sf-0e)371_jQh6HWD&oK_ zs{h(w^40Ylf*VIraoK|EJ;hyF;m>;1Ww1!7b#7URc0SbKbPI?4t~#m-+VxRt31=>7 z525gz+Wf*s{}JANUsoL!7^M!Pw5A&}4G^9EIY1W6Apqd_$3&^k3Tm^tT|BHGS*G4_ zXhTg5P`%heD*W%sfHNN_!G~cpuxqYcL4^HIzICWYH zFEIMIOHp8yDk~2w?1$IvZY7#3=T2DW_kS{bsmGdODZ%ey{7SYA%kB7m0l%kVVl7>| zM0E-C6CKkfwo8{dMlr^Eij^v@R3nuz+E}U53MCOmt<7c{%Kj>sOLVP-*Cb^nKUM0} z*O!{SHu)d*b!4h16$ov*j4ojWTE8?(KC&`i?^PR-F<+qCQWud`wB&tMFE3lJ zRK9@|Dxcz&owlKTKMpy2%F^G`w3w)_D~2ho=)KfP1YYtKwIK<%ZG4ug_S7F3IFaX< z*#lv^$Ck>4I4n4*_W?)Qd%ca25w>>~|E&K=xS!rM z>gy6;-x1nc#P?_Q42999MdVV@5NTyhoZ4)97G9r9GVq)6W z`aP|*9-iNk00j4QwQWH*&|71t1ri-PS30u66m&EI&0 zo}eRbk%-ge>xu`y%I>y7T*9tX3KJ-ta8p7XH1I|6Y}Y2VTzSXvv#~W45(;awVSO=Up(|^d z>080pg0m5Hy=1f(L4U7n4(0R3XgGp|4Whrc^tbmLv=ml<&!amuDTe+|X5AiN{hg$B zW9aW!#|0CKraS}WA?B0o&nR8S!saPg9>TN+X|U_|2P=@)_2*fDpf&sk=ucDk=zR>x zAgK54El1b;t1uWN^d3adVaNa4OBw{4{+t>DKqUQn9=eXi-(k>=@qUQ2Wchft-gY+! z4>wY8MPq||Q^#lcw;59y_4kqEOZE5dh!M;>K*yl)qNMUxn)ow+QYjY4n35nQjW``$ zkRTit386v!qUTH6NhBQ5t=;|dW_e={ywD53#gT|Sv5!2{>dhj(Mub3BKCt;0e zQ6TjyJZfh-SL_ad{6l)2WH*jdQLCoDKZe7o32T!@ESW_$;{p|4)B4uDpM0+x_Svs| z3cga&gY?~U#j5s;@V{2ARaE=cjaFSKh2yv3IdZkcV@x^x@Xl0;#PhM%{ra9Q2Mc0@;n2qraHL}>wZn1{aNkzc*!%7olK;B4qp}skw#CU;<9adcEyjpK{G#IhZb9t7sh&}Eqn34uh3FhdvO-s3A7h$ zE~HDx*IqpIrP_^QFD^hgt;+{ve9Ur-Vf`{vm%g5*Ksc3@W_ ziNNQb*)n9|2!)4Jl-u!mh)meFjnC02=yA3n#j^)flOx-`EKBk=WA%L zIyfPDy|Vqv*Zwbi?ZfGkNmzvQ8)+mTZalo@x-ELk+R9-OS`I>t@qPq*C<6*x z-I*j6YIvgM)A+-I?UNyQ27zvfp3G3Bsr(W)UH}PWK2c&bMAq2tXpmVK#&Ht34rfUk z?x=e+0ao5PETOh3Z+@*`lw0`ab;>ii7^G?LI-f3?Pz;o@pFF02XqreH@;0rK<*yp_0Bm4E3-Ji1c z*AYiy^so6BOx=Sjzq>7@ch9b)>;3x3y$8()_otWuAilWauNnl}@DoA+h~(k;^4wsI zw>93|{utcb-@XkVZlvDkhV&LRzCizdb(2oYSSv{?%Rp#O-KjE|Noe*)+u;b2jmMCe zCi^ki<2_zA9W`$^@%MSSrZT;}832IoTa^7(#>GZ?E4!?Gjf+7{??Qw$fa#5JqT!Th zhob1*ZN^6szq|x1PusCs#rvGA^=UA6ck~&+LdJYXkhdQ|xrUXuo8Z_Dx?#yCg4=Gm zYIHg9#5ovitAbb^W90cDkCyjU9%$T$ScF4f3o~wm^4exJ4UvrRwEu{%VGFLbolb4Sa`}&;Q$` zV^x#Sw-_@^?PmkUPi{~lGiEjukcAQ1Mt>pl`q3W)8zF_cXBb@zRg*de8tnzqUm6Tp zv-C$}FQBpDwv6{9(A&K~2ev~{T^AWD)?Qft4IA$t40}KP`Y(p=1g-zhqD#kj{r7Ar zniim&U^IoncgXzFe*bnw82htvgr8JnK7!}-5AAHda42CZeCuX;6$1Z@59>XEwF&i1 zF__|4GthKU%Msx%xw);@P77`AlTWg*fygNK5=kaGVG( z`(VD|0O=+=K>7#{kd{V2-k%)^-j^Q;&emY-l<9BMxzyzizk{oq&XR7(VGmBg0n&Id zih3sJ+B#*V`rv4KFd7f0vj@}gU@x3+tUlF4eX18dg|`U^95dcul9S@?vtr+s++_)p zzf|%hl}K4X3lE5h**atlg~(na<&>RMfGfa0Kpd#jE`Z^u9%5Z3|pQDLHw zk3I;Z4>}?{fIhBf^f6SYk6r=v@$1(Q`5UHb#BsS!9C*q^9P#wjg&Jj?sZ&O>K^b5F z2aog&A`3Y-UYb=7qKIY0VImBv6~gFm5XK`epcO56LJh1oOgU>UF!VGgcUXx* zFJyZ4=e!8j=toidu9 zx`du0xiExUWlxf9fH13OM6gcjNsmD=JYW_K!phjF*%%Sf;x3JLA4w1%z640BUyrsO z8O^Jc@t@GD<41ms&`Tl@^3EZb*g9E{VNV@*{8Idr$`vGtV7l5(m^j2!=e8JiB6QCz{v4_q6)u|VkLT#IXxc66EH}- zi0;}LkQK?rm@Ud+BB?To=+RH~Xk~8((4+ePNA$im`2G8|_ha3DqDRVV&Gd(Rf1FiV z*@e+0+?b%rYEF}NCYr3(X>vcCzsLJ8pQTY7i!Bhf9oz_%|JFp4c#6#*QSSTbp3&qM zdMYc3Ch;VjKSq-k)YzN!SnnX3oNe{QM$z1XApIc7(Lhndd25IiXD?OpDal)`!U~Q| zv~$t@m|_1~;4w;a{}7eCU7c=tSeQY_^$N)&cU_bN%GvWM3yL3 z8Iykop}3a*17^shbMb(jkXa1F*anrfu(xc|h^S4}Qz3!(mf!V1?<)U~`lH$i@%plC>YruqFt)Q;>~Mj#wO zvENUw+ky<`B4@nheR}Q#7E9I#^k{BZ$@31403v->nd6oslAU(XyZvXzW5Li=8s!`e z&Fz&LR5<>8IM=nLUF+|;h6d@TL1u1(xTJ9cmohacC&Y4)G7p;}!%@EsgCpAK+}Xr6S_ z!oVMkf<#z9WIUv|#8;oQwkiG4nAE(3!!(Gss)fW2don_M{|VDx*`Q-@PxgkwcXu1@ z8S5K$_YXECbKg5pP_S?nP+3Me9@VS{o8zZ#pl1Ls3xJ~1su#-(DRZprJYp@!Bv zQW*TSqyKzQxc>jduSV!UZ(!T~N1&GrU(vD1>18C^IwpF#VzJ(mPA|I#9Q*ucVI5+| zvIP2j>KC-PE=+rux6xiZ=F5p@L+Rxvel@~;RrPOszEpco^QT9o*WRQ2S5 z^g8J{_W#q%;pp|!0(Iw4*#4{f9l!BA!}fpVx2=x*x|>f6roYx3DF zrOFi-x(w!w`>07x`~*rKChj$T8A?yu$<}OjzD`N!;~B1%f8-0*M>swq75Qn`l;^ck znI8emyspgdKCO+iz4WcZ6C{rxakoZab6f4X4SWRYgO{-lHI%=bEzPmXT zzNu}s7o_h*awls1An${9^dwpZH}uy6BosGzfM1KC>%KlM%q#^X2P_dz9)$M);BQ0w zzms2!(ErWt>_0rcOj)2~k<&{bG^gA{T3N zZr3K)3$fVn?@W?rxmMv5i#YII?H|L5sJ$Qbd;)v^aWr9i{_)7qM>&&a*LP@sh##&v z(>pWOcc>rjhu{tBFPinYG_b#Ptv}SGHrJEm{%AxgTTreRJ~XkZC?s>hiwY^Rlgk=4bOi8@W$uPGT>b}LxuOZ8ocKl@cv^q z;LT#;Kn5(OcyM5Uo^w>des)*|_V8{F?9U1Ti=QBPP5D0@#SYrOv)ArZ;5gojtP^Di z;ZXicYToSL885jHcDcJRf#toqqKi@1wG_G7^mGkf_uZ%dw=8iVzqmRaW7T9 zcR#-uquo1%do)Yb=I{8uXzkuX+)GyPUB>T4Y4;A`UV?h>9J*JqBNw5b>-OUse7kF@ zy`FSEXqs6Dk`@YK_&J97=RQUyMtuAWeAnXRA3fhIFn&aVMjkebN7zV`dn1v6#gYPo zqH55bf0hAZ>4G1bdT)Ps110!h89-IJ&K7WvVK3TZwTzb$5nzIMNdCp#F5pqRs zt_wXC5;XVp#)R{Ld0WBq!48>)$8GWk-XwXlDNbIWMKrB`fmggW`lC=bW(x#ZFUg+~ z@!j-$!qJyH@kQN<0U}&9C5lft&JNk2jHZX&2cxxn(VS4&y}|rmjCL;u_dsCmUKGC< ztKEymJrE|l_sz|$%{c8|9PWXj*}V#WuZwoC3+{mt*u6LSz4+XI$hsK?K;to>GWF(C zy4eN=wuT=PfjWQoSNSuGHOl!jhR~q#1o5ZFFMT;!I=>KqYW#9w7NAo3<*gakK z&>mAhNn;SdAhTTO7w_LA@=FZ(rM>K84Puur?}EM&v{Q3T40d4;n%L!wckm$QP0cYe z*aZ(7?6Q)c3Qi{A+!9OaCB!WgpwJ?vmJCMNi4q1vx#bN)y1^}N^2_Ie{Bqv!XwHsE z&0F2OKn#AyFC{_z(v==UxRvOa?wR>z&u`hi7^2^X{_aET5 zT^{<15Tr$Un{KoITs{gdVu3=Db4TV7{KQUv zh1HKzRSJyOA%Bka3>zYwq6i9SnHf z%M;RLt%9{!Y${H5#^QUFD6{fVc+*##G{lc;P>JQD$3vBIY>42d$v&~v#p-4Flz0)K z_Z{zzEdY)zi9XT8-o;pkmE_1Jyz&xC`mDdn3_Q^2kpFkBkkP!9+=2?)w=D$90u6$)$n&x>)_9m&r3FVMbK#xxbjI^&nc=Ek@yqne6g zP%n$lO=v2P!iBoS1;x$;*@riK-kB^HyKBbzYLXIto&wfOxvQ3UQlI3jkI%1krb+VP zE1yJ`mYUg{0E>QSk(9YznuQ$~+ZmWpEB@R#hv&ADqlmJ=o23b?LYAH7m$bk%u0IW9 z&=AFtblB$2ja_Qx_z~r#Q7V2!x%X&}pHv6K&#U++tbWdfpLK(Zx}sa}~IXZ74RM#D;3tO+a8 zc*243^Kh0p;x@f}F=?T>fYjdNGj z$@S5<748C=_Q3} ztj+bxCwl`K2>f#$_$QCujw?m&B$a_=lgfDKbWj50oi2=bK1^SQHoUh)W$qACETxNSo+*)D<(1PX%1Ifv0zdd=`5Z7b$MPYkFcz>vy3#_6v%nQ+t z9CCqx8E#CU-#YOR^RS7xd%!H#^x<|-HY5H~MEtf&M*Mj|?+lIjH!2&y4&YNB&o3v-!ThHToZ{&G^Ye|K5(H|Ku|mJ}NGYNdKp6*yv39fB0me|Bu7c zzY>Z5)%8PZlTQEH;q9LyXp|89hZYZVJvV+T`4R$3jx3;I%mO0Ahgm?MW9_3E#eDVP zR~7yh%phh)&=qp;^SMF1aV(qgsfg4EDSRv#CVFlhGhF%Kz!N>?(rTapKM9uLrNaO$ z9eN|ZgDn`aTFCqo3kC)z{7FKAf|p2Br>e%@l7Em%hk>6zQSLp8kNAr*Y{au?(TLf7 zb&0eNDsLi|0K=8t1GEu`usp2+bQ7=U2o?eQw+=D*FyKCE9vX065!rZ|pTW$V=$SkQ zHa^B_hqIKgi2BirXdAOqUuH{3Tc+RUy*qaHQg!+#7}GyNpZ?T=3>n+UhfM#V9pl;T zf7xHdMQ1U5Z+Y`b=pk2{%cQ`d8JEMZ!gXV!vWB+q`0qzR08He&0MD*@HVzDtRU#(hmFVX%=t~3LvWf zvuEQYO?cY4jEK)kE* z;5AViHad&Fkq>cXu8ICTD<9&*R~gL{S(|*uYt}QGH?k2~KHU@}pKiqto&E#XCwujL zpdz$r#KTO!~#`%&mY6+{V@IoQ2<+;0VSIbX($O5cE>q&G#n~G?uZ4Rc3Yq=lRlg# zd^sa;iu`I5P6LCVNuBVE4}V6?+&CoS;C?=M5)5wSt>FfTLc2lKjLE~`G-|%#5F1=C zHn;$4W=0xju7AYA9cLpg;=Fag{*NXlg&?Ovya;j<@!kyLZ6nCZvR98T$jPL$-4iCr zng37SNV`c(;M<_|C<|6tl)p|j19LBUI@A@!DUGptOI&Z@ zn_P_DK1*Cgj!Rr`s~3`d2jY?1>8`BFFV2l)8RslpR(Y`$ogtojjd(43L0V$mE1&A> za1^#Q*gdxBiSzl4#4uF9)Wm zlgK?2BKJRk2-K?qhGk#MNH?&;vrqT3I6V+tAA?~4iINvN{=lN-b*2yd2%9`mPj8|( z_EHsJ#KJ1RO{m7;9A?S2BEQskz6N${!9-kjJOV z-CeUyeC_E0w5X}Ly)h8VSIFjBxY)p|Zcaj}Tgt+nN%;zGC!i1WFq*J?Y@jKYgljXB zaEqP?54%5}BTmOz+B^Y$opMD<01LDIE5q^XOq>=+#ZGZ{0cHn>$vM0p`s9cy$9Zzt zXi|pB!gp2@l`7}4dquGHP@l4dy&6-c?14p#E{E$y^Nw@->#<7g$o6mF+_CL<|Ey!% ze`8z6wm3i%NvpXaiKhz1$kSB4XR z)-e-y8yb0g4+!EZGGXO%x!An7Qe9@%(bmd}D86@)e~D@GX*(+`qMCf#&PpHctgJm; z;KK&7Vxh2(c2@e-{b9u(mP=}GKi`MS%5gv62wJE?o@`5BY`?rLK~4QthiaG4pplM2 zy>p{s~g$m+(lGs9c_2s^M* z86#*v-~`P7f1`kw3(tmYU}d?|y$AbG+gTL>G{AGj1*v?t=Qjy>Zhb>14!y6iGA?Oa zFUxGq@@z44?d(L~Y-66)tanj9gN9Xrnv2|noKkKv71-uGA%%!HGgc#GsrR$X+iSL^$N)YzFt?Q8m-qgsiuoF zZN0vRi@ONxb;kHizTJXWnS67FU9W#YOLJPK+soF{aRJ8uakgG(ENz)6$DS#TMR^0u z{qd{lUqie-Ypq>ypa@V`m6}iyz1u_JrT1y|kDoppx&6R+xf2`&r8|5CRfsFsj@7i}Nr{yOJ0utyq5Ku> zF$<-P@!yahlx{17-@R;>cMR#7KeKZBzv97KFg*WGi#Zw^F}I7##w8U##)m}X@RF07wH~laFM}Mt?^U;J2*epATQob)A=W7 zNf`dw_Id#STqA1wXe;Upg>H8@v=SD;2=eeML1y;Rtikt#twC@S&x~NIYOKajzXm^1 z#D;jvh}zgcB#R9{rl`$QHTEERALI2M@=M{b@qaf+Y_i+_a%tnWD3+o|E5jD zPOpbT>jhOA?T7Ma0UD-?Sd?aA`miAUAcQ5AnO|BaC30kDm&0te33TWjH){uiA_5IIDh$+WP-awMRUkbm6IR8XQ=qYk zDJt_G(8tn%n7>uykCiJ=1^##jVXF4?$A5NclYf#pQtKvmFn?@Ep!cZwW69?liaL=$ z%IuxQAKe}Z!yixlTjh`JYp$Jgoj6&XB2v-5Y$OHz^<9HWm?M;}X;?rKhcw4vNkfYm z!^--trWI~67BCJE54JLk8xC6bvq(Z#&@f|76W;#f`Eio>Nzxah+(VgoKUwh)($_*n zv&aHdzFj~p5)Y?9s0v{X1UO2UY{UGAO`JQw> zV-3S`$STj_q-vq4fUGw{6)VV}b!fXN5c%&!B;{JLioX8vSaP`oX+ zX)vvU#G{`@+@vmJI)XC4GBNm|RRy9@!17PO>&FYT2jLehj7ybQCeh+tL+|KrCiJ$2 z-3ZH)01Wr9)-W7uZ<#W8J2q47x@@z(Wy%!W&5+lznPN}QS8XnHrr4R!!R8|Wu~DCN z=znwum5c!Oa_D&YkXb=rBcKBYg%kN!Vp4cph=X}Fr%3YE#>&i{j`OH(a=qR64YDnN z(2_DG?=$o;6#yR2SoQ{dV_IuAST;v)%<uB%#m?Wt0P0Hm)W4D zeCB;t{iUcCo+HO1!rP0=n=)fi5oZG=J#b_9#3HKAA~&UBA~)vstFi&~593HBnR01FjVv&!y1 zjZVazvgb3@PItyh^1%k|=O|O}^`MN5OV~Y0uzlr_50)0NRBDI3%b}erlaqENC+%D1 zoj!=;uYQkiJ7n@Qjb8_1dx9?ss6x7{h6-(qRL)yu^}IR7sYxfQtI3pg(v>O{Bh7^8 zo%Fyrs_Cs}o>(KKo-m{atf+j>GaT}T_gw-GrMk=bNwC=6-xfJC8>Lx3My7t`14bWQoHX5jbp5gW0K2Tr7_Jp+wmOJ%PL=Z`Nh46S&#G; z!7SFqy@8}fLs6FhL<3+{~yu@7B@V^a$KeZd=vI0)3t;G+DghzY!T zua11i6OWg6C{MKG-~mA%y!k=``(R&GlT7^F2J%l7 z);i~d^=O8OVNm_-2U{x2C7pYztz^8*S(;tQ`7cxeZ9#NFey0#ic_n#Pt{{zAb|N! zBD?SFSiZV+#MIhkpPgOyX|^oOytI3llOkqn^bz`S03zT`vSl-Ba*!wC$Y_GnG@LHl zc5P%Qsq4@L%WO@5N^%z|twhT5-8~eR>`>+OZOheT-PM6@W zY50b{kgUEyjnErC^zXg#y@!^MGoAANc#@yI4kq$NN<#?NO3RXnJ@h2*E923lwDA?|1Ob>($1qAyY&FV@f+ zY#OU#tAq2HU>I+?AkZ)-G^&R2u1IUpklc%7Ph#r%M{NyiGKZggB$R(m_VAO>>J0pU z${xO~j*(ngd-xo7uT6Wn7v;5@?cotct!l>;r`^dOzWsc&4cWFmyrpZ9J$&<;R!4K9 zbUjSSu`ixy9!uNy@Pb5rX1G24^lFUdB(aA-{q-qn4;OsI7$&Sed<(nRwmtmY-MUG} z?P1GDtsx+sy+Pe*TMF1{z?t4on{ZO9onh(aP*8Y z_ORC-%D+@EDvy?=a@>QD>I|&LXZGr6#!-9*{^5UX{^5Z5%!9%48J6cD#ca^yGv8}# z+K~9nbZt%h$DsJkzXRhlCSR~AKEv|~_@SO5A)4nM7*@CtO<6#QhUbHr%VpT!1NpEN zniH61#&8yvAyo^}sQCsQfx$7F zGcIl;MiYv;#CJ8!1v6g237E$t7#a>=*iwU~=)|-@)-%OulG={ZEIi5>jamf<48+cG zC1oY5j@LD#0380|#Yr%9cvX*g3R^nVr_%*ErM zCjIvBhja!DtcS>w5qRPN&qTPFoVIFYQ_VXt6rPV|hsNO4;dxcud&`5JNYP_n+J`ia z_8~MHMLLZnAt?hp-R3Q$W1~~8-g}}X)kWL5$SUuq{nK2Zsp(!Q`IM=ls!!b{RDFsQ<-@4? zw)z|1)C6{jsx!F`ZHa5cY$Ii77ULELH=W&a{AyQQ{#t(eYQY|70xtK*0o!XMp2QkX zf@>Q}9~vBy0qtNwoG*%K82q$40u3bRWE>U>{!8lbBFu!iCYG!l#L0#O`{b-0&d_i3 z-f>vVCspx{^IFeNK`^3&OP(bGjP=jjPaykeAwZ4tR3y7v}nzU+-Q(fs4<8k&RI z)r4g+aSN|r5WLY8H}rF%#r!S&T=Zgd?KEp%)6Wr`-MQ(@lIUf^k)J z#xm4#cP*$navsCc>B!+8vyQ}ZSX@VDQ{d%>$>DV5*Yi0WGySOTXV~?i&b|TbOKlgb zIloU&RzMQbiQVt>Y67;+wR^wMj@L9X9>RW|Q<$yiz=(XODZXE}-{<|hAN)tPpJCOj z8j6BhM{}X6!HtgG@6+7h#4(-|xZmfJt5hv_qe;uH%~iFWn%_efCf0AIZk<5_^c&0c zp%fp<%l4AeO0I4F))+~@{a4d(39R}H={NL`AB4ye58N{(pg%5Q{%bu6^mwytDHNVX^EZ@8AEz}Az2y7IgnU00ft6juG&FnN!zULtV^ zvu-2RW1J3aI-s80ac+O?qK<9<3s=Xse_wvbwm%sD_Kw8AR}N}Fz}~w1K3$!h#P*hF z0*JWX_SUU0YQ{u}aPJ&@>z~OHrTX!)x9l0M?iC8%UoE|$p(t4Vo{=7ybCkY!(>)s|Ct9=W$%O{S$f{LB?g4nUSLo623W%k3X&QoxZ>WA{#5Zp-u|eO6ol zSeNkQS@?c@yPR~o^8VJ(kO{{w%Nr$6j56#QaB};DgFS>o_$LxXcX^cPt~pHGLYxW9 zV#!U4E95!t0nceSe2U_()1!8mreSe7Euj_p*Q;Rw4MnSrhRoo!^-@;lT`>+1g~rr! zVtT!~`X6asK-KltC}EA{y)k_lFts!gKM2aFDWl>1)bRuKbbVSwPdfx%5<^ZGcV(2j z?r>AFR;;x$N_TfQ6;rX+y2D53<3te97H^hyi4C;(ERC|!UHfSt#9c8~Hh48fH4S7D zShhtie#vUL97UZy)zcZS&fXp4Gy!Oe1|FDY7Jvc#(fsFAfj_oyH_bosN5YdD0kq3D zI5GT@71vq(arT=g`~W@mk89}ZT>cmmd$RGzm1Dy2$LBMH_=9#|%!RXa`=1Q%Fg7#3 z0MTWYH-+wJOo1O83eOMw>E_cHHDKhOc z1-lw^GJcS}&yiabXP{B(J)8H=D7)^twClb|^ZsJ%l7G>bL!6IxzJ%=`X+`!d-(5W= zdGp&*%A{?d`9=BT`nND7hg?#ag8#~nOsWEKO|&Gp2rDY{YJX@dPFQjUzHW97dOKA) z9q&j5e(H6RROZ7Qd8mKca0l&@Y(y}SUghuMC5TpMdS2a;w}&mE1~)5TLe+(N`vR$~ zj9QYi%I-s`X!F~{ly~u@T-MM}8cRLhFYv3+<0|V~Sp5+VOWuh3$x6$kAb52rWnkfM zEhR+X66Rk3g@A8eux%0rx{sxLcepK-sNQ{X6sX-)z1s+us#9Q>F$kZOR%~cVDXUnU zi;q7GGZ$BmXmu`@;+-(qWOH%z#hN!enTug~GITC_;%d-bbWv_!im52)dw1n3 zR~>C>6$*DJ`F6)^b5X;qsM9GxmqqU$KwbZNd0@YHlcGr#b z)g>iXdHA#kRBJ~ybyU20#oyC3A1Ky|(m>cR5{_SZ6`W19>9HPGTmYZ3`i#6x5UbCq zP9~1P7S!r5yPbvnZ_mxc&i^r7(PpGHL*y#!!wL0FJ7}JH$|nESC2z z$lRGt{A2?=i7$tl!tN$^YA9JlFJzT1?7D_t&nmmWJLi+T(5A4mOy?3RGpM2}3TDAVvN#f7&d`HRhCV$4%B^m*oMC&!VxwH7g^I{->j9#pvr*ruux9Mc#kAIvO zhCj}^#>^j*?0Whiv?B93k$qs5`^>llr+aq4wqHMdvDh+vay3rgsf(&snm>2dVaZS> zS7vU?ESG&^bE6njEoN0M8FC-a?S1oX9*#;I#J|$Jm zt4YN-VZ0A1lTj%CHE@%!HaV{@nSB)|l#>t05;d<&UU7o2HUW)zySsh{#o>TaoHv>v zMi)gFQ%7Agf&It`SiA=`B_+|Q_w0c%Xb>v`<)FE^e-E-p*S~a2L&ufxX&Iq`%$O z0RzT3i}RvptZZ?DXRl@-R-YEsnZPizUA5>Zuen&gRcyXhN4KmXwqhVBB?kqUl@uEW zXuYIL$iMrAAN=*v2$O=IkeHiHrC~ur=qX38I@Zo)pH<%OQ~Ft9DzDfE@)3OvDX__u zekdhGpb0BlXp-=WuBKQ=Y;zs-1I*k>Hqg;kcaT}T*ADughEY9IcQR0&E(ZQYbTd{O20vsZ7T@?9Pz>YP#L0iNhg2<2Nk)C?$0QT*(Up{6??e}|*>F+jm$RPbVdHdY>bGQ6V~}i;03;v4&l}YFA;1%epOUXr1q89Sb9C@5nFNxk-s^)8Oe7_-o9Z0O zK6$SuC0aze+V_15FlCk!+vsHMMKr~b64@gfRYo356Z@;IuX1AxHsuaX{5*H8n0JLq zBp`U^Oz%IVB!|jBRTHhLSL(~Zg)HHs=1jc%rI^^#;L8?Pl$u3{_PWE{XU_K|4sw4( z6ML10PPwQZDAw5+5H+-Di7O0gT9)0n_TItWXudkuEUXkpsn(P3gSyByHsmp1%TKv!5iDPpa|q- z6D08LGsU92cu@{YUS)jlMG-_30(X6)Qo|>oSW&<(Rjnz0tvH1ghXd>JJdlxyqEg$% zynCUqt_mfOE1<7-;;AtDif%D|)eNsI({eaAoohL|wTs@8OB|ks>2nz6Y!+6OTjiak z+P+I+S1PT-3Ps*0mw}qtg)*z$3`l2+DlHL}aovLlRha=n|63boq=)D_%s4p?vfuf8 z+X%mFzxI7oZkqXSZXf=rP%sAK0rVl{Q-xN!WQfI5vdM8C=T45?pW-1Uvah`9@ z_{=%Ln)R7bPzgJiw;)Vtk<01}@m+&&LX})x|0=rxO$Z<-Y?DjspT-3OP%s#{J|EXW zar2=pO3BkRG73|QLVol;uQtjm|0u76#A^{p{3QCmj|TmICgyz`1&d~6R94(c72i@90w?G6Nfj7)86H$ZFma{7m76tkJIK5Gd-%m1q_0QpH zT#vOld(+7Ca9QoE{sG)-DoJqOfmW_CL{58Dy5t?avc9L~Bc`ubB=t9}hnkFfrrEvM z9 z#1`xfRZ6kN1-E77BM^WlQ0g-F6Y)>b?4R~oL{|Cxz-DT#7?NtIz>VLJ87scJc<2gI z-UKOEC3%y2N(B`T{BsCWC8!u z$YMJrq%E=9iXw&c#U@{ZO)dt?1nX9RrSfGN@U|V8KDsFi9tI``h0o5?3S3&U<*yux zMHyc(Dtw%1X~+G;-3PPp8)`=-HF=Y~u6a97pOiM&VBu1LD@A})+N?O>AWbZ4n1I@z z4VNysL_XO3mD`_v-w^lCadxu8*Wq1yX(M~dr@d6^NGxwi$|1Y%nk%gfGBOfM2Z?3l|!VW2SWK=f%3g7?=oF(-{F@5iuljWNimy=Q6ka*Xm zoD3L~<<=4313={f&Y#4}38Dx;6?WLjzL=JFPN6cm^fQ0MAWU^>_J~q()VwID68a|C z${|3(^5lM=LpBhO7MLDS;kFZ=Y>VgQRBbAeeu% zty$%{$z)#)hPfhFPpeP=@(2HH(m&u)7~>7yi85nl-?Wn1qTk2d=UG*YlHR97BIV+& zYQf=~?Jp;uo^6dOhxM;gq>XW>$d($Rb|4o2;7@P&2fyJ2*XsyBFmUp<`Q%pRVyAwpIzfl5dG#yRlcF!y_T@_si`C6;QkWc)I zFrfsRIdH8796a!KPm8wXN-%`x&W!zR`6#;9k;W2zI}^NjyF__&^%2Ct=sbC*58)lD zU6i*!?(QoP5RXQ-6Vc-CYr>8nHiWl@_@jB68326kEiiBh+ zM@-TD?=y>XU`ITGnw=;!3>&O23MRiT5uuND-YNHIRs=7dZb02Uwsc~D7&RX2l0A}t zVAnv4rT*%rKcF9)^|q#*tamg89juW!`5YkXndx&ok@=N+{H$ovov}36;0j>o?7Vwm zR}8gd&5{iF%wW4uNz$SOjf=pU@I}>(vnLiwiM|qTS5z~1%NWl|&D-cPDaKcV72x&6 zI$gDQk7E5t)-?#@mb#uG-6T#ERx;ng$D3l~#WKG1L+AK6 zSo2e9GsNd-BCV9wvbX{{-9~EJ5th7H{qb;ebhj5&V!IT%b?j-FbMnEB)NgIlZ&f>J zZ388>dmKa}9vY{6bzGRR`Wpw4SI79tzUDe^r87Y6*j=XX7ZWVbO9?8hl-W<7(P5IE zEcTnvcTzMcT4Otxf9Wo+z)li)y9Y|sH?ZzqSfbn~nowYPPf|E>f ze6)^egek$sJPm~JOol+<@^;3w`qkU9ZU)^DMovd6L2ex_@r-%b9wf(4X}I~cATQs0a>^D1H)l+D{HU4_U~jecR%V2rQ+Q#^ts z6?@j9J7Z~4#$82*UBthK^Ao}SiMOos23TGj;Yl18?TapkMwB-1vSw7!LSr@%o7JRyNx z|9Zc?>!tDA&d%Ilq9?aStelF7RNc%mvXN8ca}$79Wa$W}ESp!}3jUNPVzX;OKo!v}CIqU4SYC4RD{vBl*3|>4XN}AhZ6xA({_yP0 zq3m|(^R1Frm2MWf@@ysZWoFCqjd3UcFgVtlGa07xo%HayMDYi9y`L079M_Th zmMnZ`QtnKCkGVHmy#sDb&x01do+i1R4%}mW;VWfo#EXO&YJYFmtpRkXLqDTgAKc3N zy$LL87UE7%B$bf)*NdXwbdZRj;Rl@dj*tDx5DMWZJ81=KpVa-4)*6Qx!T6$QQC zguqZWcyrZYmP+Zy01myjBpm3h%@y); zmP`DGA3vb64JFr25iJ~8&s+SRPNC@z#V?hdZ!-DvS`r(P;oEx9#Pk~O32iTmT9^D|Uw@DF<9FcN_4NXs2 zzXw6g`lp~aw@>uV%wtGxrL4JKc@d!i?-bDvK~XNNfGBRNL?!^UwZE^6dfTG`MLEuR zNzul^uToMGQJX>T4&A#~=WG}6j;g>8J=Wo`;zrf4^04_ zSv1WT0l;?qq18g~7>tt;y&`uQGpvYu%jgfC8wsJ3PZuwfQ8W=g_GBnx-JLL=lOgCQ zLEQg|}Vj_b(A|PR!jmnQU?uBW_LnV3SGYHBMld=pKX6ClyWJ)>&}g%F&`tu@NUygCb1!!TZC zjdOXp-ojXwpPR(1Pru|Se{0VZzf)}z3tQ%{c z+5h+wIBZ}C+Q)w=?c=5t@DC=(T0_r>1nj7FasQA~3ZaIq3Un(KWF)W$=^1QR`p2+u z4*JJ8Yx3k}EvvgHQAUbcIpxX3d1S!|YaUH}nZjoEUaK^Y1wpid63q!Zd(UXL=4RL3 ziCFNR%reoKtQi_AJz7&33LMoMACbYKEvOS2;`jr6Qm!s`R`iWcDs~UCAi%o#ZG1{| zPG#tOu#)lb6CO5~s9R~k=8eO$qJd2OPMY=#V`l#%XO*9Orv7FJ&LuM?X4 zS3Is^GPX*?^zxo3J){X|+#n74i%IwVe@gd#Nr&1}F8r<(W)|eCJx6Nw)!pf+EhW1Y zBUK5V`x;s5?nHH22pH?!*sZBpzcdcVY^~v!^jwOZ&5bqADH+ruXo`EnYuX5r#Qzu0 zGiv3&{(_^USS8e-%%N36`$I}(2h}zS;q;_g-#5agM^eb@_k^rTN27V`p^bgR7akC> z#ebN44E(rLf_~*WQrj@yyFMNzK@@xtj>)NWHE*;QG_ij2uF(FQqULTr@X4$k7Eb1u zqy7}L%}dJm_6D{$CoHWJgMdc=#Qv zdYQ4;SK(j~*en&|YsJdCiyk_j&)P=F0%i3tPFwJC5#jfKU_ z_>fg7z4la{fprrsV9|*VmV)4+kYt^d!p}#&9TD%QNMMsrcEz@`xW#s|ye}yh!#upy zSSttRZu8vpLzmMCpu}v8=Cz5|7t?EJa&!FakT1dHrTan`qH(9hJ&CU?+InGYxMEHg z4VN!XDW?Ts@kePS*`uZD+6B|$@<+&6ytY?V<1qSLlbg-W%x)qE9p1|-sH_JElrR=G zcQIj@9pTb_5tNX$T4a($86vs;8&v`bv=K#`7TICP{*@KY+Zc)^E=y?1O)HzBU?Tpv zguN{JTY65S)U1heW7Yg81X8S~!WahO1a-Nq_t=|y9mderr?Z18wLwK=rnj#V#;4{h zh&)W7VU6o?e?dRiU+KqEgo^z+gK23?6gh7&5?%sYAbjw!X@fhbA*K|7OEu?0K?$A` zuWm!Z4m`9}cHyg=6a9pp=iUfWv?tfC6fI;JMf)kMJeU~}al)>ueY313)6n+OYi(`E zl=xtl3%VdGrXuCTqMILNx}dmM9N1O!9W@?Wr_BiT@Smv`xE+8B9&scw?7B)vMxQU! z2Y-^1n!XJOS2Ivu;6_@}EX2e`>Yi4mIY0MUz9y3ZU1-f9x&XIP%e)2*sjS%2Yq&%! zEkUuiX^lb|gjMV>9DPu9>*?e|8lZp?c&`y{|MLpouV`zJ6`ahWm1+f7o;46czf7Bq@Q-(qBm5&KBmdLo zDu|X+A00wc+Qj0;g0{H|$@%b0QEv8)Sfxh7o28Y>q|`{=k<#wlzRLNQdMuG0 zNp2R8Luj7T`HS!&BODFv)lco7`${D5Q?yB~Re3Kmb*%eEAI9ty#lPx0dKxt%nem99 z#|~syuKNE`znlfe$IrZ7tOA*TC=w7Vyyea7)dE;o+e_Gp?d$OcM8nU5A)Z;dPr*Wi z-43%N49uqg=Bn5q4E6lZeuXLrK*op{ib89 z&sd0@2A#}Pj{2ukf8Dx)VhdP#A^ID1TH10b%OB zLJ@W{IEq>yvxuoWWxbFnxS7NIgh~O8s1`<7x!L~O3(4frlDS2202I9*MzORd()oJs z{d7>-;!!b4{+!;}A=+;Xz)JdreRqN#Wsvd*Rc)ngv=*S390Fp z=%tqW*H;VZ#LNWvfF0^fRY5%WaTkWtY#GheOq5NK?X`KS&#v{T3!%6|*Dgb^)zFMQ zy!v8*Lzh~pBc4YE8T3&8rLO-Nk`*{Xo0~JLN||F(QH1>OI%(^Y_I8)9oK0QiUc;h< z-Vq)mt*!}|?qHe17$?0HU8i5BPvLd(cT*j8+t|~;OS& zI|j{^ZyLTwUA;@&@I8P%xl<@LZK*vCW`*ktc@=V-t`(D|H94tO?qP6N+h@UtR(4ZSKKxQmx!aU;aHl5PX$LP( zLqNN59(mq5D{(zNlt6;i_rz4!zY)sos2POXkJk05H9mGN>c5jZ>WM_v$3f4K`AI0y zF*}zYCA2v+L!|(JH*S!@xyxy1fSvZT*HV|jgBUGJQ^o4uT8i&IGGvVTY=T{+7 z>egmY5OfmFS6*vllFO|GpDtH}7@T(r;m5gW5F(?7&j`B0XdW5}IKVB@(sh7iYsmUe z(w=|_7}upb2g#gt$0hojBpOU2&A5lQI{K;FA!N}S7ZISF8&i^t+#Yu@D`T0Bze~J> zf<60yTC$lwC|ry>xcE)C8-t;15-Z54D4Rk&x*$Sv9=Tpjh%#3CB}^WlSEhhBI07>E z6DyCo!@lv^_mhdA9H*vEOcnI=DQyC{76JPP0uK5s&=9EDA>8>B!{?k8QP_xjFj)6- z7LG!sQSs+U{OL3{MI1eK3W1=6O4+I`EY2DZL#24O_0NlPA6q&09tdq-WjPFltPv%& zbhCZ-HnkiSLhDz`5}+1?^g2+IS_Up+iXGlpGd%VQ9zqlW%}=iapqE+%fC`WUR2~0$ z6_9(rWeq5aXa7N#fHXM=qV7@vjBgtt1F<6>>&g<;&1+tL2_(OKQ~b-H^xXUcO{ISo zZHR`n-eX?|H;tZYiS)3-nMJ91aL8kyeUfH8eHNATyD zn*-V{eM{^}?_2wY%!TI6yO=gc-$c;TpCoY3mn?m?MCQht*Cp~SogE>)ZygY8?lc*f z*kV$f`Aw2Ri*--tAR05aRFwduhPGwP9lsWOz}hV>R`cDR<@M4iZPF%f5|+!*$|9d2 z{K^gqEa|Gqr-d>r~9$A{+Cy%=vl3`Dy0I-gru1^zOQG zU6ItydEKK+9E1`6*CjO}M`KQkRnib{xrkIFPk+LpL1LO-^cM<}<_61O%GkF!x%;6i zGym1F72E>Xpdgkt!f03^KugSUAa-j($jlP5eYXI|$hjx+oi75Rn?{`0vn1gV0bwy8 zy1C6*px|g`ak;6n;6aLNoqs-ZT$cS-=?h6c$u*p>XbXQNN-#PZ6#iCsGf)+a`VxiN0^JP1y4UWn>e^!e zX_TP#7JHq}ua)~#9M1UEjo3lKFDv(ZqAN6F?atTL?Azw6!};3g#O-murpibgF_{+R zyUY0!g{s}_0)k>|q(q5&8%j#TF6V2N^JP0G+r`fev z;$1o!mnpLa&v#fysBMdl`nZcaJ(!YPUKf=ycJ@My*Wzcy-`Iw-DQuymz-cYbK5d0i zRS8*6i1TWX3-;~FeA7swx70NiISnUFJFDg#`2*q0t__E@O=6Cln&{6Cx)$Fw=OZ`V zM3~zTqZ2u;Wuq?@Jx$`-QKgEGsCb=xx#%kby`xP0C)iSF=T!h510l%|Amtsq;Hvo>(CgH{sdPr(?!1O+7sjg2G= zY6T;TbR$$q3#B5h;5($7SUi&?GbI7b(RU=#cz&nq^&2En{H^j`u2e7MSE!7%x-n5! zo@lY}%Bv=YCWhGU5t^i%?}Aa|D=$1GJ~L)_)QqdO=Td;-oVTqlpEosXRdL>Sr5$X- zG9NLw>PF>3`}bGJ8KXV0Ct5=r+ymofUmDz%#P+8(EM$hjDKzw_oVqoQvb_ufBIelM92G-9mfLEz9H|T=PTkY3C+NgPR(1DCuIXUVD1Xt>%ZMt{2Q8`&R zt`u_KNDGr&?bA{mXxsqJTDDH-mGH+986{H=r;eKBfSCKIx^R!Cyc?3eGIZM$=3T7v1 zSD`~f_3CyhAIeD<$1%P?XW#U8Zu%0E$==~+MkZ=hb_rUw;3^sFi|*n6*7#IL07ps^ z0~t?ZFSw{q=dXw@;JYTjBF3Z#C6@}|{@;zplYPEuG|otm#+%aVn2g5L>P<#t1F>OC zXHkEVIi*^D_)F4?E4ygDcr6I*_?PrZS6una87L7D-Lc{AWCJCmq@t7^trn(^3a~DF{(0p$IpNlV3?d%z9=9bM?-0(S=ptvhYVcX0rki5 z2+o-FNO-Q3P+L*iC5dFTrja4ut`f_b^hn%&x++y$PU0q~&}G7Sm}6%~%=;A{o7dRl z)?(h}kceg>5nF`7>jAxR*11Foi4grPHi8*F_4@hiu@78Uu%^x8TO{XbtbVPevh26Br^dHGN3L(7aSH)fD~-8 zU#Qr{ilSn5Yl5Hj7fO0!ZXbl?mX!2mDm~s)tTXq`r5sY-vf$$bCgbN z-8UK9An_%n4U$%pNpeYBbX?l$z$BG6XT`B;KN?QCES$1j2772&xPof&JmCiKDZEA` z%!sjgDpN0F)g7#7)Qu*cFWvA(jJj?D2=}A|eDY?E@DtZeZ$gGXGiuy5nb!>edjfxw zTG(RmvOgEcRkji0!mJjxbwE38K2u!BZ`8Wr-|y$q-8bblyNrBgz8W#Nv9S?rk-hls zPS&YK(q5qRI>{LfaVh~=xq~0g^T-(0(Hwi!8=Xnk4|7{`v0_4F-N~9crLSUkvrJ)= z%+~_jwQ+kXdB3bUVQ|+ADP1JpqD(B=HThxumx}DqXj`IhxJllF-2kq7ed(vV*YLSM+pvHJPg>|BJYLE`QfRc^BU>~b^H%jeDeY0{l zYI#Tax2P9o-zF$UK6*P$`Py6;MtPl>&&>&9e%2-~bN zvk*>2H|7>a8CzrO9+;M}`Ec=Kdb!x#7`0%fgte_uoK?hpQL(tPe!o9KSQZH29(&2R z1z*{Lk6pCTSrxPfQNFq>L&Hvi8I$0r!z7{d%ymeSm#US+ZBJUXJxz-)NYkP^N)URa zH9Vur%A`rtr2Q^2s%(6-_Ilma(Uic|=nliO-M;0MK0O$gZ)X@5%y-8#EEBShWmtAg zhrf0{s0*UZOu?u0y{PwS)O)D$fOhu|B?&&GJORXOuTHg+J)6fEc|+G+v)&-5TpMEz&1PqTEN#|r6uz1NmJYbGg2Q;(_kw3G$HlyH zdHI_;rQAg%WjxC$-dqIF&{G-rsY2SO!W}YU!^P8;$c8o6K6C{G59&>2)!n1r<)}8> z?YqCZ3zV+)yIjHT&e+$?l5vk+DC2&&v-<2wA-(r%nRK1@zpju;;m8d-OHp_BA9mJ@AeH)Yl{9Gkw*)TkIQ;zC_>C zZR*){#pF@oS82LBrmaUhXTZRR3Wq66%hTiSNgrbGLF|>q!__OM=-0{mn|(rm2aR{f z7d+cDYpV43DMX+i{f)Z)4NHF?E&EUX?bD;b+KQahQ_}g#yDW{7GRq52GFp!O?CAT{ zvU+7&@Z!>Bbjj}xT0zwVTER7I`F%}iOCQTk%^jM*@*&--S97ne zk|5g55sEA?r7hZ!%JV36k17L0EUzN~aRQMys}cGNtA0Y2(|ThE&JTD0^at>@=Z)SwWf z_Wg)?Nb^^{PbU}9Nq|!MKvmyR57Dki11+`4suPO5n?fsTg(no?kdM(V zt15U88zb}WnU8cP6@ZM9MHE7<573cE^%?DWM2sxb3eL>Z3c^`_fX8?iM6-&3O*pGe zMtiUQ112dEk0SMqWu0hCj`T&$RyA_qpQI5cKVO-yS!oC93IXJnyn{M092FFCQXMW~h+?V&IsSwSJDHXlNr zgPxi*Wh$-wg%pb(1cu5!Bgscwvnq%gs0-iY=w$Fe!v2zOZ%@x1)#9>ri{0if{zJOC zlXRm(k=q{4?Ady~ZKTjLxF*_ecCrvii|tD#h?bVQEj`6;DKvVU{gb1|w3IQGEo<-? zj(?BC07cQ;0F9I?=9rT=#-ryIF{T`lLHFg_Yx8x80=uDDFNBfUOZ~DvLQmUIbg_wj zw=m-UuD{AOlpZy(Ue`wN5#R9Fb@NAT>X}t2D|#7+APW?u=Et)5M$PxD%+D5AnWGC` z@K$+8!+5-a(!?JLw9o3z?kHTaS*4V21-(q|n~_q^cW#}P7YS^f-3t~nYT8xiTB)@P zPot+~a}=GGIv!Tem{IdDiY?QPXNn)A?Q3~FT7%FN@xPX!sJmYv!LBl=v1m0Sz}UxC zvEgEW?X?keKry~-dTia?0mh;hF}gp!SuCElq#Vm#}}wVP^i zCWM*3tx)&g=1;XE(8hpvi}?=66jZE%Pv)lxN0dG|1DTDl@_SxkWBVU;l0w!yhqc zmXyB~HOB}uuML^G2YZ~%>8VlkXvCqf+K{<$R;N^%cGMhED7rl2b%!QFXEi@$L@Ur2 zLo4B-jJb79lbbYuP4QIC&n>U^_bwNvzog#ywLWufjt;ggCY9;cjlU>%o2BABmWskJSSoVt%4M_*+GBb(zvHXxt`db> zK~5I*yrEd?)e35|{93^ssg;5ewJ)MoW?pC|&0)vO`}wRF==^tEp)4i>$EDsLet~hV zElK1^^CITFLUzXp*-mbcn7d>md5_vxjzVj{y*??OnO$tY=*X>lV8^Ye>7|=R?`BIR=yu%a< zO&;%+dGgztA)XBo)p4)^?Tr;MYO4qrH3`2Jawg)jW|5aA1J zqOMlDcBHI0zxb*!go~Eaep%042^VG`!rp0jSF((5PqF*P#h4C+`-QTLod#`(ELJ%6 zwWWFY;}XQbitWkee_2|Y39hRN?MJbGlJ>`)_Fvkg{oS+XO8Z|bQSHB}Zfzs2_0(45 z8Yvh%-rjMpbVAzfuy(G79nXLIFpNHsJ+xBbqlxIAvYi;i3f<++ZS_rZI5X2n^(|id z0mI<*uQk;_U~z1sQLug){vIb8)qL5|zET<~y38oLV5LtMQAoTGlpeEP-b9ZX_eb;G z1jK{5an>Rg#ZKM4c1Mp%2oWhgrYhtldQ4Gh=d~+La+H~%KYP5Qf@89%+xA--%o5N+ zoxT{K9RA+Lr@xJK@kz-UKauemd!8DPAExl>YQd*b#m5bWgIdl|JU&tl#k7_-PML6u zPY=d4<6=g`MZ{x}EdG08HPeXZiXF!zn%sAj5}nkqZiXf+n*qjS2fYnZpv7~RI|)_) zQ(jM~Br&$M%e2M{bBZhe+V&Z{l8FJ1RK2{gx=sqD@pbx_O@y z=L*k)kuh;_9)CJeITqd+#QHugBo}oM8>qOH}@v<`fQMpLMh z_xTsyypmoT3K?_P`o~kyzuqRXQo*w01 z+@st(dX_s4+Ahy{XPTswh0RCP5eMYi+%_a+4prwyI>&pB*#yuf$aj; z7zXP2>8A2jaOb--o;`_LQL=s+yaob-Zv5QAxp>qV!BVqV;Sk)oHRd$?gQe0VylX7w zKqCh`z0@X43Oz@YXrPxj3UsrvS;{gNs9{ZgRp^GL!mWNEN~g8TE%uYYmIgx$d)qf~ z;GnjAz6^5T#)rv|OIed8FN)<+Wc?U*QhXN^)|oG{cMCN?UQs*o@zwkh|Fnp9XLDLU zUMHU%yyqX`ukx}>xSg22N^)-3hU66M>~C@;GH-`SWX;^b@w1z2dqs!m7tj8o%Eq5D zMH1KI6sn5n6JKP%S7!@VV8^j#MsUVZN#V^@nUO&A&omLw2Ncil4Vx0%P`F;L_RgD< zlqAgQ&xzwBhmIcMIVk&9%)R#GYNSn9&Hd>fN6l^Jh8FW?<^Jk-E}{r(^o8!&lb^?6 zVJ@}-uWVI02PSbk4z}B667$>2_f%c662d-(l6dbQZ&5ExnF!)}$5O1|1Xu2yjP^e2 zGEVTaY+!{!6nCLNK}AsP;0-WeCJl6Lsv=nIvn+o}<_soVI$gwpd+2`cPmy|`$O1Hz zWy8u89NF7+IC|m7L}>f$=9j^mtDsP~`#d#)usH@XHn&xlQkl7uTDk@6WR4h?$|&8k zcg}b@d7(hxEm(NFI!aY5Y}9zzVf4DyIc`znWG1lPLd3W6fsLzzd`8+f$Y^P!?edqx z*3bh4c|xUI*}@gO-Je}E0dczF*Mz~zpm|>jyf$lRVmvZWjBQ?F^WtYoVGYs z9{VY!z)T@%X}vE}Qr{?RP|*8IrT246><|f0dUj#6$b3z_>4enrE5e`%>MnR3a&f#r ziBgIwJp=p`!y8u=LfOTX^=6N~Wr?zmy?KRDM26L6%tdrLXg~L5S!XALZdUGBzzc}( z8(-xNdt=meH0iDLW3&Tx!0pqTo>OuW}4cZ^5z7*+*W%;nJmlW z#z(x{TIWAZG{r0u90yI><}Z@~{*jv#Xy#0{|L_&5+DhSGU)6B?uYZ&D82*n%ctD|C zdO~O{j{WDZhbj7bJUou{ga>LD+g@{@g2xNv1s*qL-~msz$Nt-yNF*`UYp%M@uI=3# zYoyVvbh#g%wkv5o)x{U<%X8-nkXY}`&eoU$jX>ZD4#OgJW>2_mx6l8&z-6q0OU#9f z>A+=6TWlEx%0Kb}m)TQo^BjT8-N(V@{=W)bV3hwGT$Fx9Xuw3AQ}9dy3#!a6rXjQ1 z@r|?}AGj-7{t_f0a3Tp9uBJU0ujc%#Hwd*LSNxkob9&DBK(|bCnE>vLe>yeeBW)XHmIpR+=p|Eq zWso#hzMAZ4UuDPU0X5yPlj**ZNQNT!EqA&XZ6 z=%Y7CTnECAMAx$BIq6PK_ctB)5tch=MOnF0SOws9>S8@G$yZaPn`@T|a8Po+r>6JQ z#k#boeuFd%0nm8R)d&5hSk#`nd5d2p4w%p=nl2(;i36Q!fG#Z%?Dvh)R zN``c!4)S4NlO-J;i>@553~vu(oL?^;>lZ#~52rstjY}j;-9`6zq*EBft@e&y;zHp71K`Det`lMJr>;ERMXeKTp}gtgjQ^~XAAoq`>F1!K_y9p>{&mPl0*Pd zI*Qc#i@t^4R?(7FGd7J@UHv&f($ybGGw$DjsbYDZkbAQCu*oU;LE!bq{l~zo!6~HD zE#!Mr$ao4luBB$vGKx$AOcP1+Sb(YW$5VdivE>&#<^OAx)5sr5`3KjFJta6^anmwU zf->e@3XK*z3kKN=aqk2(e|*$j=+Gi(+#KvzA{(%@k2tLuuZB(ZQPlp*k_>zvr%u+) zAGuYFH=SKe)rzJZ0~}zCcy}8uk57EX5t4*{IUW~!Y}HgI0rOZq{A(@}%qTOzH&qY( zN>(|`(;2uh;8EW=Exytv(9ip26Fi;MP?}#!0IlNz*9u#3ilx?Ms<1s(tA7%e$2Y%@+V`p$ElUla6 z+wV96j2d_~Von9>)v+%lET`aw%}()692#{Hd0PxUIAJTs>Bu-E`T=dKZ&Hg}GY2xZ z)i36~4zR3}KlUw6GO7zy0zlIe9}wHG>DI7z-OOp1#i!pYPZ8-xt8~2td|cXsZkQ=? zT(YSoD{_)?@R*9vpQ2l5s-ngk;lm|Wd53kg9OA}G$#%sI|k11RdzS_QUl9)R0SK*XbQf2;1BCD!nPhi2R48-P+&+^Q>Bx0J&c!`vnP4W{k zm-2%Jdd8laOs%Yl_t&!9BV;(a%HQPDW*qaY`^V+P`6_Wqv@G$Et1?$e@gZ}*6c;L; z?-VzGF<;t(uK+=O)%5z7F0z&HtTY~xR)n}*2S88S*9XtJMNg1aHZplUIUai?VO7H) zm9eaWi~cBNfg{rvM@)Bk&+LW1rRMatq@~}T<(uq+BG%fNT&B&o7u>>Ic{`9*yA_W) zk?>eKg-;OpBh$}+n(8B_(@I0NISs@n1;pxmq=kWd958`*(l5QVUizg6>K!nNJ6;o| zS|hTIdP%R(*f&%6{!(C;f>a_el`3Ms+}8LEsel0uX;dYOg|-?g9fSG`hew%ajO=gB z&PlgtrD_jIbXgxE5cuhBwFmgJfW9T8TUC9}bSNd~{w8a#jF|P4QD%p+)i4c5Emm58 z^9ny_pUmnN4TK8k;BtCJ;jJvPu!GIDuO+?`s#|qE`8{K-{M|KuqQ>v+Iau2Av=t+v zVfFGMrkh zbu+e36YWs!^@F%jN{Ey{jW!PEa3l`oQ?3YJu0$1=gmB=Knmp;|%@d{egW`+hn6#cL zu2Zm+8;r6YLjfCEe6_HG3*YXCdQelAF*8)J)2<6LD-FDmbI&GqMsEeu#-QDyHm=M?yr zZmshjb6aOuvb~{$lRK=Oa>rcd_L(a}fwEee)9b3cq$zrIqR}7m);%vTYAUusmI7K; zDZUl9sJWifX&ZE>C~9`mMy+9FHkBaW2Gy}9YTMma(*8T7^G`ub{joyP}< z(`gN7@v@5hw>kK4UoM_(QROd27EEG6#VX z0KxkKw^H~$o;m%Wlb=Lh$osAGosO~4e8t{A4nT z9e-;}!ka`m2ANW+7Xk9B<(+wW!FZZCnI>~H$D6}sO6iS8X=kx<`W z5O6%lq$|rQC(#DB%(+Sr?3#T=B;~8p0h@+bgtHMNVm`EoA*nL{E)s!y(D_pbo!?gI+*Vu2VNch`-f+;Fb0|{S zT;*W%?O^lU4mKxepBpMog3V~)v#n|av&+fKpe_bRQ6757^d4tzSV|8XTc z$o!Ur%oiwRb`U|q_AkYC^jQ`}L1GbS1*vtD(}=hsPY~)@c$n-neEqXnm*Dfd60oMf z*;iXJ!k@z2F%=0-zP(s9#{?DelQZ#&iH97F2M|$G&ZiUO!RJWwfwHKhxQb^p#Zk{Q;-!yo?X`paYP`ELT z!dq=5kz~p=U%y_4P}DBMJwyVpYIohsZj~NqR4KOGWfLdMDk0du4L{NNtyG>y__VwM zmS;|WI%4iywM_O1U!dd~^b8ARNwj|}l{$AqMFze4ehC*pfQpdP4q|sp4xDup@;}?6)%yEe z#Wh*=a%3w^p!)Hmc5+7jp)#gf!9hf8`Si} zeC1A7In*+EjOSZ?!8cirnB^Y5O)-qTv1j--5i60d1_m!;%|7+Vok`_qYFFj<@F8-} z-!XITJZa5dBQ)L2?c&`C-PBtDXUajnZwS|7_kW>F96L13&&`VBY_ZSYueM9XXP+nyZz+L0@goIrfsc_sx+CYW~>gxf($l_p=TQW<{C)>l6CMy zGyWvan8S-yPRY8K3P_U2qiA@-mCE{QoB$+qv7*=!B$ zAexe3tn8ada83grU=76wNh|-#U*ZDE)`tGjR$Ox~d4@NrtLPNIk<*ufs_nz{p3z+q z@g_L{A=X+E`?TMzSE3N)E^>>8_)8JyRj>GXOO|&hr?J#q6BH-b&a_blPZqhDk!#UE8G5SVBCJ zGy60Sl{eR;jYEsh5TC6g>IIn^ee}Zq^cevh`#&Bmwk5cJ!!mw)u=%N(>`ds`y6)KP zTFdK&EObWw(yM#!4U-gKINx#T_-3+62KN&=Jx?4+$`&x{R<6O1wCabVwSW`9@vkgP zR=@rd>!i&6>Q5X5o2HV;u5+=z1)YTP`I&@SH#-S$=#kL2Pa~nD!eE}@Y}F)-m~NOe zW#(J`#D`5coIta&UB$)bs<_;nn68c<#BgG)T)7Sc=rv0^_Lr;VjeDH0SFlqN*X4ZO z;>1mNiaG~!K%5{2`973_87U|3W9O^P`I>a4N~y-f%01ork_kh;H5aM4prmAWi>j+E zJ-dNF=i?k&mt8Y5wrXOG95ol2_gv3QOF~;m5j9`<*J|%F6eq}JJMU3ZvI2&UL~6x4!{Y^ z)>`4i&dw&kHSmU86rr(ub8Z0c$%@;}Au4K}8;)^wFZ5qquGJkW)^{5(5;E&;1A5d;8OH*S)J+~H>y z5JAUZgle{`SWbzteGx@Czb>YGH1IP8+T9oR_0$DCT7zG5Ajr(D8;$^w&6hFC7vCUd zTLYIX>bu=;)sHD*u9WceR0-QuJ21^jv9J+S17M-@J3_QP&H{}4+ger_*A)t#LE30C?ZD!ZnJOL z2ECAZ)icRScCCJqD0*2(xlOuctB*7P0&C`L`K)2+R{6ix?y8_&ghKnAc^zUJv5%`UJ0i zl)-;wGY4rI6ISxH^K^uoCScf{h);kq@?fNGWK*5wXdAhBG4nTKj9nVi3PyI>4~k)@ zeuFeX(U3I#04jU!JJp#wDOV|vG)~x%+^gix%6+ZQX>ExBLC=(2(s4EX3G-CW?i2Go9_gL2yz zyue?|^0bW>$_rz?poV<|dNZ3{n+dh6Vo z<3#?vskltkM%CS+HFZFYW7E0c;NysQZ5@YC;odf>kG6cYFYM*s^`*d@^J1<1jU;3| zNBhT`SAWAd%DBmg-Ep*kTBR{GE2=%`#S+kQai3c&b@Oun7_*_J4gwG{a@Yq%+nZ`>CN=ww_P!pB9Sc7i5KEQ8p9A@dWnt2=K-K zcx*sUe95r^!MG4XCrf3Ys|#`xb9&_064QeX2Wkr3pnB>MwRhq>JAVs*+UFb`TyZht3!i|S`?V(d?DGhS)VDU#i;&q%P^2-RETUb+Kql%n6bCF* zAd7GTQ$?*HtbjW8+T9I3x+8_id$;3?W%lL*FEQE)v9d>9ml9^{4)yUo&$$%4o*e2~ z%~Q${9O1W(=W(91`JTh?0-glVQl3)@+sE%y{Ccwb@h`hyKd=1e^vji>e*JnmKfQf@ z`sO*W>TiC&pgHnXX8&H9kQ>ou2rK)w=02WP&FFURi8l#hLa{Ml(3-i_xnvOLBo)`a zrVrAOiYQM$8w5(`Yvx8&q+yR-a1ixu^6uq#qNm-vPkqPTZ`->Me2aQ~UM@x_zKHhgzd6c7<&U`E)7|eI)%QY=42%j|;*oXP4HcT~hCZGNZO?>m%7ogyQpFOF%nFrh zp$ChPQ`8bqA9K?p`~NITNbkPZ-f2uH(5ho}qJtTUT%x|0-DR?ar;8YKt8 zxRP$tm)LCKE{W^j4mSnX-utSo{0nG6;gm0nbt=amH;169I#DHzHi!jpl#Znkqma3!- zh?}KJ!g~ye)1)UPG&LY@j>p{`j~$o8X;Mdz9DCdxo7^1j$K`P9IM^dcx0@sG=CCEM zeq&Ik9F4Pb7?5E*bgobL1EYn1hd(Xf3G@HrwW31!=Gw=EKb+t76VZ;miY5zvU-fnl zsgJ-T0|;2le)=y<>R@fNDBlt9N%$ydR&9WXbrf|&9jKYX98wUNS zc9l`xRb|ZBvshcf!rPn;v{+axIQ~C3;(bWyPweQewK!i~ibR}`&g72Y<#GP(sp2iT zo6lF;P+R)>WZj?P<4-koXGUN6@?rGH6JLh35}|Xf>Mbesic6p(o7xTXeY@ z-R83w_7`X6NcG2DmwxAuk?*VXUyWFy9NEqpGDi>N;^=vU#+l*aWvlB1(ij1P_G!;{ zCKDy$Qr!J>f7+Vw8F45)yu_DDP}`e`9}!_6z+@s)O_0j_NuST8K6Ntdt9LAkn4fX5 zjMnfJ=ozujJmk=KFN^(KvxDYbJ}l6pT5}PCD-BUK+6EfA#QCKy z)cM{<)tHMQ;OTf!Q?+LwarQMm+A~iYxUisyQ^{R%W$QCfrL{98_Q%A=j^@^09Ws&3 z4`AUE_PhK*`6jTrEYPl5A2U9sE#@m`x5Iyk(~69D{(>UF-CNoZ* z8@^zi))8S>-jW%o+XPTG)FI;}zBYHMQEH$mj9AF~ZmKaQ(wMRTYhzwtooURdFKWy; zr7hxyUH+mrhh z`!itm#X6WHAwiaJ;F~VJY5nrv0--| zB_*C_Ossy7{00a_NNqwsLQJ!eccZ=(8rv!!E%+TfB;>9aaXJx)^A2Dr4(PJf>M4~x zHqt_&)5QDL?}NoY`L+6K(wKKnq4Va#;%3QH{=EJ3Wia;j8-$#RzGeNi#cQ4$>dB?o zgPw&S_hf(JKR4=xBqszMSHc$;nZ>`1Y%6bYH9gN`b^Ie!&Lm}K?zCNluslSn-M9-k3AR#RBAi({sK{Q3CP}HdPl+b1DXF`|R(l2S>eeDg& zgBX91K>OK5SJKQ~Kqag&ZrP_x*-ulBN`FL^ec;!Wy^Jt(!Z5UDXnP0imfAI9hGkV>CI(D^jT+DP5 zV6NZY8H>+s!f&Woa6|15Yrt8g(iC3IsEABx67So@VTmD?WC$ws0 zUs`Alyps?6p36DF;^ns-d3`t|)1T*xbwfSh($%$ zfByIXCXcG*7>W>2K>B)+2uB}>I~M;$WBEMM&JdQ43<8pjCEUq$96iY#49p3YEXT2R zhpWuEvTVdF&Cg<=)MyKyRWSSJhyb&zdY#!Y|M~5*BD5yEs!Sh~J!Fl~vL}8|sOtTb z!F}D_YS&KKm5kpE)r}7S{^S~ls$Q_F)f3l~>WyvZ5-_dwieSWqr>dMh%^0BL;whnF z&Q`I;!);X}`rR)f5%ZL3rB?8u7z_v zz8q@xB=n?XaC73CeC~JRP9o#hh)>xlD9tuP?8FP@>V`_408drt-BSQWX^g;-JOL{9 z=ve?WrZS-)igDvhk^|@}O{fvPlG|g|Q#JSAxKak-YDbNQ!Nfh^kKGn&Tk87I zT4C=))&tKe7zVky@{3DM{6$r~;=fd!XiS$XmvJN>Q@Kof|DOtvN9nkvltd@(Q04F+ zdTQi$jMW&HIGp^u+e~M|efajZeFX;97f` zi@&^wxwxP3koj`rLZ_-V*n2&RbKMX6=SdX09}J@SekxR1gKM;Bgf)Pjms=SSpIi{6 zGiFTD>sh8Ax|@<0P%@(^q4w9LsyR}XgK1{aS2_IqzM5e~hRhcr@yt%{x3d4qa8}_6 zhA(qYC1(;H#`SosQ|K5e^g=1adf)>VCAVyap7J%u!oz? ztw}TN+22tFNv2`bmr}^`67!AN8|^DqtQrlY5P>s2G^p zZ7HHzazRu`$=Y_lBxpE!0s)?#xPk=L=>%&8)+-Mag(JBGV;PE-hqJRi3BMcC+X)e0 zpL0Kbage8Gh;*@b2qS^i7WBpKG|tGFrUpw!PQhZJz)Xfs#^jwJ(}34m7Aj32ZSN!z zngn}-v}+5&3hI$+Ics$;2dy$s3Dc@gDiOSGIoh;*ijE5QcdnMXdaR42=SQkYfbu_M=O0tJ~CU?uL?L zX>?|`{f{U$4WXt84U3p%w2@62nTi~A2~eAE^?P`jY+zfGCylM&EH*N#Rw>lJpRM-D z9+lQ@>kc-*J%lGa~{Mj&b@wQC7Cf_UMJB96q>23+$ z+Gk0#e{u2zg2wjef*rN#1ZxB(AwyuLzotL5gMCUKB$^`odK1nP_jeUyA6mFe<|Q)B z)=F~}G~A`!m9~Pk@gfSXB*flK+IatT6=cg*)u=jzKBepMkdJ6@;;~dci=2AA ziC?Hys+!f1s4rYpsjB`Z4?xLS@ng5MLtH5(}8ywZ~vnB zPtvr)`Tix}G6S46C!Ggmv&%=TdGO#jj+qAqRPZ4MoPftf2p(T>9l+ekuH+O>-Juik zGyx$vAg;zXJ$0}57VV{J>F2-sO*KN|neb=R#AUCwtQ|->G z6j#@T!p2?yC?BvEqCcp;n3p1SQ@+|kG;(#*R2h<>-+oGFe<=xx^Vd$7I9Z2P+yfG) zIdQ()Xk>V>sCJaf4Z2B+5GpSFCz2|T-qH~T=0BYh1(rT3qQI>nJEFjBKJ57;5e0Uz zgUUVM{d?)OH>tEYJ84Ht+V4r)KT6vBHxBjGzzfT>dGk=uuq{J9<*h?KF`mEh?B#i# zC%_J=Kp}c|S-$I(XiuyyN>|lXoobdW7FLo}TerH;|WS8sYPK=J5Q2=LGr4 zb^HoDtUKPq;nESv7Mxu&q9hcvbM)#TA*t+LZ8Dd`xEK^wTH{U$8=gvQRF=JMimY_^ z2@}$qy-iOl+1eb(si*1eg-Y6x4&`Ab~8q#Of|3;)qbDEp6H zLEdcJVHfA*>+tKYVxdRFArPvmiSuy&zj(FC42eFxenSCMSPp@w;yXB>TM3vdeXwpj z>-XEZ%3E0*PK4Q{vp+t39mpWRp+FWPUq;R{j4zbKk5;#i1CzE6tB8thRVyvw~jbj9RjMjKD;*>Z8&2$urN~P%14z5E;?4>Qs8qd3KXseh`FKw_C~xd&cFpia-{3#dMEp{cDPn=_qy{>5e&lw$M(IPm zNlwyQvUX{UCGrsYNew|p;cXHGkVtO&&9RzUgDzFe<@x@ z<_N}wmacwP);|lusi^m0Ej;#Tp{Mf`r$w~qxN-epEkdhQ6M8zD@P?jV=*$nn2nk(n zr5pdDZ75C3so|!tI4u-|PaTO`J*UuKwGw(mv1C@QFD#Or7cn}P%&F}gHpG;SWG^^Q z3l%1Xtg8a7&{-_c+4k2}$zD0REZFKPaEg}^xIjUj#YaJ1(n=3KPV!*gznDy0JAzFm zP7XOHL#zbg4T?8z--r(XM>`UJ?U#7jBQFK59`-4@ZEf{5CIB<4PL$38>U$mg zksz-SJK05sn?lo_gFM(vj8Kp*6@qi-=bX9EX%Wr5*YU8D@zpM0(C@bT4&DTl@N)~B zr!Yy8k;Md#oXk9nts8IeLMelLc{n!BUPDMj)e=Acl zCeMfG4T3$};1=B$%xa>SJy5uVE%ts+Bye=z%Wgrfp7kyz z)01pgQQbV0dlCK;S=_2qIw6SKqV^H^j_EhTk6Ha5ufv9RwY;1CzDz1xFBBJYe5eJf z^d1{Xgf_V+%i)iKx3hnOx@SVXx#vdYF4w)<3idnMkywjzYj7^Gll52+1)j$ds(7XJ zMH{hyly&itNHrb|OaXQqvxQOIO3V#~_|)aNv3l7mJlLjRkJ_N_-8U1B%`x4hUL*z~Yma-$41W(0-1{ zF|U&PIcb4zMd4OKv(SXp>MLiZk*9#y96i%_4ONF*S4V5i46`a;RpaG(ZIE!->G zU?`L|@U?R~lgc-s93VOV4=2E{H&J7%pMJ6nMVEqO^-WLBsT*0yQpc zXEe(xS3f%&;!^$e;+Mf4b@^AY1SnjE;x|8VRBI4>T_`r!oleg1ajH&{8oF(+9QLUa zBNdfR%VBkJmwk_B#4&ZnJSAay_D8CN`6#jFk8#1}FNls*w>X4C;k7xsyW{)=W{b2pr@Q{ z2Z6Ra2z2R*R`=TTPedTGxaTX2d#|u@g)fmto{gcvfqB?acgB^!E@StYbWEAE9hY>6 zpVF2GIfoUIBiZZ?L&Lprup8zgs$CPhdW!3y{>blAlCA!bNVeaD^J1dh$A|q$8ItY4 z+tT{2+tX>!RB3CRv^PlF+a>MBOxkhjvK%{1FGzut~ z26>nOq^L2o^CN|d-8#V86&FsOMT1JB?LhsdFVpixA{6=g<2 zK@%n+j1L2-c&xhOy6b(hUIY+KKnVyW;Sl1nOF)HZj3Bxz1U%;bR&_tmJaZ%g!F_-K z-}~p|nCIxHySlony1J^m+ATluWN8B3yvP9|>?cA%-Vc;_q*m(l0KcE10|8LgvIQiF z!lXjDIYOiRA`e~R`%HOIAyP#j&}$52OBzQ{8?6%+&%J{t&8GeG7KR{xNQ+HXK+#<+ z49W0#WeOzbVTveq*q7owWsdgobk5flobvOrHiGBNH2|~zH0xlA=pT#*Lp+}SB#E(2LNs}BqnGxCXxg%xM3b@KSERlv`gSb3 zp=!yey=q1|=i!O{y#d%<^eLv_yAT;WFS8JR&4=cEmDYe~10bt+lOFBFxrg{Wk&-SY zuBB6;#!|9H$`p%i2>_GQF47+JsR&}J(5KFA4H2<|_}jh|0gBW#B^06{+M$7S-^FvR ztdZ(&XaR+{x-5Yd2G^<7BAThM}w>5x7S5{0t=zv9o^Tiw-gxE9A zNr@AFLAdCwE4NRD5WT@C01#9>Z#9%P2Y?x!rW`D9;hmz`m)E5r2VlW!q#N;!N8(C6 zXg#eJc39)yO?PdkrBR%~YPDcJB_PSI@LU}3+E%FYq_CqSq_7t>0Cs)VMSyuy)-Pe0 zgM%@jGw<^*H~Z2G%qknjY~EhGzb%p&IFd+J^G*oYeGlr^{?t}$$kjcnx!&NZ0hr1_ zk4#0G(YymDa8pr+f=o{C^$q2_67$+@A}bGTPf!U9U`U^tIg!EHifF2h*vj3zoGeje zoxYF2Vptzg#IS+rA^t@h3(**@;wKL97tbS+ZZFZXaOP7#9omoiq%FeI>PXz~$o*m3 zT|)y&`>pF&bofN7zXoK68oXuZGmuUBnQdvJ>H+9pYT+3w6l1ZTX<8p{e{1vlu=Xl3 z2xli+jf>tK=5Ff0%pb0TK1+x{)S+7c1M~#k(HV&sA+@A{Yfq#PYof|?ysi!j!M zoZjs@yb`)TU(hOtE+F&rNG+roxF7Kq1oLZc>2p`&_sTL}rQ?J%*uY5tR}SWZ%lTr`#86u2 `z^B@aM z1-?qKyb_`1@I-oy6Ax`c#b|o52BoO-Jd6uPmG`*|(@oKbG<}%yceK@COh!i@H728{ zAEC)e>dAX?qaVq{WaM~X>dASW%R)Is8}cXK*U^^WV>K+!y|=hA`)FH`-?IREU^%ZX zTlr=#m>TlrD8!6wl91ohepemo6TK|!UuQvAl-D*ejd!7v6-g*K?dtlk;d=B@_6t%A za@2w;i>Hzv0xMOY#k6G2KuR|lL2sKxEcywI3YU=92G+1u4q`QsCtmD`#UzZGnCjr_ zM2}O<9>4N1_4rva{r-p>J%xHa5}Qz=2g&uoOejHG5n<1*)vlmMKH#G_wYjvR%N2O2 z8&)B%fWN;J_~i}6GM~AkwRkDVd$P7p?NRMa9MTr2mpOAcOl#>|m!|kzAcsbV)2AnR zYZc8|cd#PIn~q-hsB|VO->N-VBW3!uR=)Kp>8w+<*7W{l<2`EP_bvH-YkZ#(@%_1x z-)EpeUPJ*`WPv;sm>5xDN@RhFismmw$EnQe5{jaEcp!_lVL-SDN5y9oNPDgL8u27&(I_GVPr&VTCssKtb|owQl3IIChOY;itruJh-CzY}muhdv4imJl zX~Q2ttVrU1ZBc#t6*ikl%c#Hbxeji-0`=DDui(5Wq~N+k4QP~CSO1tTs2XK@1c7@Dvbx^ zt3q{r#?2K6{amPTZll`4mPL1h(|RjseP=Rgdg!KDwy%QO~fxK=y`m z5=r&=4q75H%vfTWiC`EQcIlW?^B=DVNMVG-7MkBEJ}JY<%a{jI;4aX$)ktX!WaDfJ z0~T!CJ%_a5&+X!5#&q&xN$i*E00Bk0ZU)KEP`)A#BywF=1UWV(0D4a2uJ0R9*|LJy66aXU@Tki&>qVKq(PhX%Y}E6-DANgLD1qrhyn=@= z9}`R=E*-R2r&{mjve|&sv&iO0YBf&QA`Wn9z&yxRIQa0HelurB7bC8SLy+yobMOW1 zh$X#1i9@t^z9aUc4BS{*ED$tg=_BwD*!&ZTUs^M4_4lG2F()#<=qJ?@Kh6_5Dh(B; zXb+2Q|F9(nWlSi;UJR^x`szPE3Nav5o%#!q%$Ra=D!$4@MI1qC_dN-TL(n~pzo6k{ zWYIXEI7~}Qw}Z-Ms#KpPy8XKG#5Q4mX^vH#q}&N^p+nlSKbh@0aI_V2XRDw%IVnpo zGYl9AIE5uDL;v&gmp_X#A4iw5@{3oLd4c%_W;ut3*d?s4s`XT82$VLJ7zE66 znq(4Mb!RxVO<^V(&>D%&AdhK`>r;$ROm+!ySL`JhfL)A;8vH$EuuaeA*k;Ss+|@+t zMwoNH*aprKoPx~Y;1n?g6UxFX0l_hA9;QW}AkN?l02Ah#Rwmahl3X*7=W9vHl%y1X zVl+#ntQF_*Or>>Bv>eu{t#NY(#cnJN~fNNW5vZWp0wSD9#Ekk6)H~H#G8s; zbdSd=XK@3mU`^B!=a5ddM~#|_6wb7^CE z!W&t*lIA;`cSyVmFI!A_k^HhrHM9gwdZ`wkQMJn{QRLrg(z}|+e=9cIW9p!P09Bu} zK!BvJq__csW1`e&4RD4V;0%rj4)zGq{42SJRUeU>Cd&#Ee2!OUNLXIve9A>=UpY9Z z4u2B7_W%)3Fe1ns2^eVpH`*VTpYv|B^M4dNn8$z$dn_t2|9kGv_j9^ZMMJPL&$oN6 z^26!d*MjC;N6s24)!zSA2$7H%LS(*}do6tx{Sb3+U=;WDIwqp|V*1^oKVCyZgii!;`Tt}Jk?W#_ z$mRO|^GS$^tzkj3w%+| zPoIupo?SycYn5SE`Sh+RvpBkpRX#l=%G`%C5o?05d_uep={SwZr|YqvH^h;g3**SA z(&Lg(`-XF0#oFDJPlq-{$tN)ovGVC(ezJ)Q`BZ;iSU82(1&>MLG(Rky^0hwc3LSB7 z3Wu@gtYt`r=2oqWnamB5&{%9sIgH7xi-jm(QW_Twa>f2%kPlavo-p}9hBY>Z@Pa); zK3Es+&FU{}=bRAvz~D44AKF`n;&;(sqR?T;2POe&zmTYCM1M)JtbdMLJ}h%H&s0bA zzEwUvD$2YXUB)UO?i6JxC2u40A(Y#cd{BNSovJzgW$tmwhfOZ->z71#nkgT4R7S}M zF%YrxVO0s_gMFzK5WNfm5vnv-K=I^*eQBTcNlL|9C}XCG_)YpXfdzM=k@qoLRGe(3 zPn2s^i7>5uR(nPKMbGit_s;bJ;U~(z7s(%{ea||7Hfi5mJo^OOD;fdlB+G;z2FP<) zjD63_ue8>vAmZNV`BQ}L&T8LlH-zbz)*TULXTlYOwfF7CI@K zTjymK0tjj08=Aum+q{L3vbvM;f^53xQiG>**wD^9Rtw)B0UUDv&$8Gl`g{l$ZOXzY zh9Yd?>ogk&oi7#Aq`r3?PJM!AazM8bf2|fiWRyvtR8bCQjjw@Qm`B?C()rM>7<*qZ zAE#CcdjT5@RtjSQ<@vCf``B1eEsO;pg^dLctxhW>ClibZB=YnxaTF2kg%r-u#)E^N zQRG*5NprMX_<=ANTpn-zai&;*MCzZ_w0DT?<81F!am*2{uWw>^6fMm;P7Uo%M|&5p z#~aUhJLO|F+QWLB8}v4(*@UNevpuUhPd1DB%zLKl{|m3MOUN`qwhxRyQ}oG$w}yfP zFv76b=lfR*bA7d5bHAa8cpi{egce>efPTpY8k(3i*B=MG$bN7755n-;i+uo5fSP@>kG11@pVs?#^roY$ zEJ(qwX%i_e1_3GA%;T04Z7n7094L{>3=SO~;bZxjU8yB9`HZ}XS^Mflft0zi(yds1B@iXpkRSf46QII^aqUf zw_+|Xr=Gx9a7DQ}7h|LEGJC9p7wq;UkiJ&WNk1xd~?j#x7>@Jt4r{8 zn6=?ykgUDfU~N*god{+wLqYrt*~r3gnhBk#j+_Ol$e{Wm+5IZMp_{NtkRFIvIDrUn zigu9$fj|2u1+y_qW77~SiSi}5deqS~r^a?kXX-|BDX6KNRX_Y0&BzuKFh?g}j$ODQ z5;sa=c(lwp1u17 z%a=yrwYv9zyS@;;HaH4>;qsnM$d@gb0x9P=C0~|MiFS?1mmb;8$(N#U!-~Q0rZ2Pw zjP+A2`ogJY(Q?JAFYpLSxzgco=nHW63w5s#urbcd(=mA~C^>G^I<0s}jd>p?PcA#hb_IrmQx?l&l^R zi`8J3{|#F3m;nJsbPz2okS@hy@gptdZNMiY9xNU_;jI*FsSW+ug3zC&W2`qolzCB< zNslhmj^+coCi96h|6y?4{!4JpipEQV*&GC{dUsWk|DZX{&zu4GFEv}A<8`1xV#wFy(xL|(9WZg7mpv8yf{t3xTi6BaoVd<@p4yV*~o{VmfbMVn9l-|@G+hy;zn zff%|-f6GMPs6v6O|9AW?v_T}w-vSSd^tCkRXz|?3hRi|C<#2;Y4~^jp;Z60MbO%=K zh+OJsctem>EWzpvdH!=khFB|0+eS{KcG}730)0tn(D0TBr;zb|w2d`@ZXLUgHIW)w z%%2)IR=7CywlA6j!J&OaF1dr=mpfq z8Tb^Za#?H+JsQF`2l7ZD3kZ8Q8ie~ot@sW$2U!L#3>!TVuh_HEVDW5x*OX_2Y!mQq zm@bXOIH8W5vih>;Orwd@znDC-PVE!7e<^un)x=Mv8;24+tkYkg2%$1DEL04~O=CiZ z2EyN&go*|6RxfwAKSHS3OZIs`kTS*9v`O}N(+^_&D_7!-#mIn+|7ZLwrDuUs2gT?I zdDxgI7Fs7rKR^rd{7;SfSB_FYh;*z-{Xhk!=WC7X2WNGT6eiL7K?#nvFoZ!^m{?*S ztinXae)AyZ(Q8saC_(30by+}IIA$O0mp#zPbNs2HAFxkD|K^#nhJa*r3h$2x{}E$H zsR%pASycqnW6~Hilg0P_v?er!VSuT=gH=Ol*2BW~0rqdi#8-VUw;u224Ou_3dxJM* zseY-bVfXPK>^}Y;VO82DB&U(PkC6$7Bh_;B`PjC{o()>1!7MTtYe3nFB@YM~%S;$2SoTL9FMJ$3 zYQ{u_;aiAC!?QC_$sb0!93DauQW*>Jm-jt#hOtg?I?xe_6mKN+zA|e8f(#>__+bL;AQ^yz{xEPDxhnl2w!+a& zqOBqaU;O=u5}c;|dkl^UK#RBEN5(NQEFzwNBic9iBic(S?Hxc6jy}03F>El+&v~y| zPhS*1jDUV?e6S^;KRe(5F1h;ZuZUP$3ZfMI``ew)cC9A~Y1Y8Nb_}X06FiN}+>J5- zI)-J%z5tIC$CVOuBOxHqdz@Z#lRgd;mDs`)Ft-@uBqR3NZPT9tg2E!n+)}fo-p~%<-Z+{JAv8pQ9BI#>)l3^o$Ear zp^q$YZ1r~7-|GWjTSa~`q9%(C>nOtX z$;|N~VS!geLg$H`obTlMp&8rt04Gl9IYHEGkNJG|m{xEyQjrS20f9MqGI`5!1nn@!B z$?I1dvdwe986xZfa>U}6Xd%qe#{qI9TXgQRLL(xCd8;wfr|xeNF^ziu1b(EslEZW% zwk1Elh5;4RC-Wm^M9Kg^3eybA>Z$9kEXf2=e@=vK07nBc0#lp10#jnsxeNpp={P*M z$7q<%hVB-ju6hM*ZEOQxxX5Hu)jz(>jU}PqYw)TJ!lATd^(I@+otX^KnwkQUQ5=&O z&0v_uGW3-=OA4X8IcA2MsQ0(mH2waxZB4%)kDOzu3+Mg-+6H?wF75LK- z)0*!I{GSqHRTk9}DdS&$Zn^gcWbL7ebvZ&9kWk*12bMf57YVoU*cpC|#f{#cA1|oG z*GQ~fW-G+ABbT6PCAUB|R&py;l}K#mqZC<1yE%^$r#OPNE63?rSiU2%&IYZBnm&uN z#vpCs>0&Kn+}gFGjPP6UNt5h%Bx^(3+UyXVQaimIoc^ zK}Pt2_Y|t^GE}D6JBV-jA`3;-fG^Jp$}U5fZJJsu2tRkT=M+u-!n|pxsddJUH;rmL z;3UxUYVp%G6^0D%)n-puP5s%tX{)6e*W#AJ zoTjNW&6_lBE(pSA+6hV@-xldc~QN^>Cdokosa2190 zA!>OEf3&$}mQqy9`__BZQf|2ywOlD`p$}2ZI{Z&Fmm8Ha?^slm%xRm*a zS;y3@#RcN)z;{@y1PAn`pgM!I@+psq-c~{0HZa&a1qsl=7Ww^(Q)r6yw(>9H3(6qo z=vUmDSi_s5_!se&8YmhEg6EZWe!skOZ=!#AIWFqxqAtIm&D+AChWiY<&%i|V$ZR<}{HM@TA-}R6z z#Y+a+Ug7~tj7v1`MlKcvNMc4^i&ZL-I}}Si$b#`v%qlM~)Nyo7apn@db7?g$?O(iJ z3bs`PZxaoI0@k~GZguzk_Z5N8A39ms!mhC1sjU}c7nva_w@k7GM3_VUi}*D{($ee>u|?-#MUuZIzqLC)@()Tm zSkXeJv_zK&-%c3wv@Cp}m(AM+NGMz;@RnYcFCy-2W zZG=k<(?pb36NgVlYOS1PTn3&bIzViETc-c|w%S(x){D1>a@X7yt}%^j>?9kY>sFP6 zl9<6TlRt}!F@f?iUX&evf@N6hl|7Fq|Pg2aj6B+U4(Lhs%OImOLffH>U4U9jGrxB)Mm+U7n9u0YaZwPH6i z4N(%u#I0huFf^yL&&MTb{sAWbWdCj3=@ymkk)E93(oQ3o&5eia_WI+Rf=_I1_P z!o^&npNTpOy-(6*x@Pghi^Z@rPKT8G!Nw&#*+LpX=(u5$u| z8+;9^_ays5?Y*b@LYLZ0cheBiUYQSUb@mL^{oY1t^cXzp!vt$w3|6!7+v?r4QtzZIxj-U=_Mo3Ag~hLL%sh7TO1&fy`Z z4gDiGy@xq&gZ@q0C!yXj@NUqb?TACYseNU6D!zA0a^bIP=s*n&c!Pc~pmAxH0F1tQ z&0b5e>3_#xoYk8^5Y)nvpe)N^5U2^1*7HxPwS09qN~3xGPiPxo%ep4wX|N}{L0uyv z3+{&&L^ti|W*2VapvNezcet@Gw46P7fa{$%x5X4j;!kvd%A-1)uAn0l2Yc)cNAQiF z0*5Efr@w6cbL>5+oSS4XE0QqxM<`3@y+aQ}-2M(4q#D=1qE6 z2}oUzL~EZtk{a{P%1`P!@<%>Rs7So#m<5k;N{QV`uEqABav<5QJ@{yVu`oLkbcboLNZArpVGne6GCyr{NNeZRG0%eoXsQ6418{{VW zAw&W+YdL)-avtl4OP`VlB ztgt&p{$f3}ls@HI;Hx$`%EPy}8#;!D_2m1}>Z_7dp16oROjP{~aKOC~Q&yO&Fh77( z(K3i!FTIX?0E#96{jEvZO8@~5gVkltdIA(DiFpATv1Puc$5qiVDx8l_?R zrVmvN;zmHl2E8pBcOk{4=+^#XbZZ+p#1}fTq5IgN5MY=u_Q3i2@uBi;Fwp) zQS3CnovBSfX#!1sJUaC$I0l+}Cq&7oJpikmc@~`^piKv~)W0P3Ct}746`?TRP~46! zIMb|Tk)@JhvXZT(VPRgsOf)vdQtpqk+^e!2kwpDNqFlbETqwgp=6QHZ$ee*M3_*^2 zZPbp?YC~xMat$GiMRJ2)4lF`+VN5|9*w|c6ayvJ%)*{LEnJ+>Rrl4*9=72s0_f3oO zYp&p7B7wcQl_|y-;gV$x;&X5roB&`0gTG*b(hcY$!1IcQK)Do%qx&UuNPJ|9t?(>R z&@&b&16iP4dM1^NFHkDg&DccskVT*@T1^5)?>W9M6x@X`jZt0IU9`5^CtCkNqVYns zj$EA(y8xx+4l%N6JhD&AA|U{bbE{d@Ac^`i7r-MX8$-%8mJ}QHf1-q7BBIXo$5Q!n zNyJ4c)r6+2|KnOf+82-p25+GvP*k173!Eu&I~L#`F9p zG0!*X*FH}K!4nUdO~n+ZEK4Rl_#NOx9GOZIZWIJ>m(5SpQ}7K>kh5C3{5WpJ?>$+c z`xY@gpxLC~o{YnkMqzfZ3|_=tDb6fJW!kS;_u#y`y_c&nP@;PMtCpZAsFO1Rt<#^s zakvu7&z$KCT>>{NSf$djgDeP&?`ylg_}>{~geTHq_igbW6$_B(bE*m`+!eW-BO&p4gSNt*Kp{ez@W@Zc9Zu1md7Rl#vZU+Oe=;A$-B19U+d5ARPXEG=be6mm1W+27M9kWmC&f0{D~s z9Z-o-Uqpj0e{@5!Pt&pPQPm(>)p9Td zfLMqoSxsr}#6eg`L8c~AN`$(?)*UD~!GCun(0h7MHQL*bUG)&wIcUd?u5IP_ee5N3 zz^6pPt7l314?yXD!1Xk{AB6ozso38hPjI4rcLMF~?w-`>CTa=2u z$)q`EDTlygeiC)iLNlNp(}~6zFtu)`SqvT2FKqRBD4333;8c+9c`{Ur?Jd(W$p?RB zNZPqBwZ6HI-h72OOiEZb<=KmMsAZ_4hhi<5+!qB`L>Bx21r5CdgFX9wz$P?RA(L^! z1Wmeu{vQd=y-3W9efkN#aV>(cc~UN};P;px#NT`vXCRc#7+Ex_%7T23D~oc{rk#J!pkyTceehlWAPP z7ORU-^c}bjH+nWQb3`@zZ4x)M7{km7xX@^Cx)#yhX4g(@veAcbj%f6Th(;G-G@}}& zQ{s#s6~f>H`Gs~soMT`LkBSqok z;G_u9DsM(p<{%ti8UxxFi44mJxkR&AZG(i6VNgoMu2`N0L+M=N(zqo`FGPudqJ+^0 z4A`!gO#wJ662L!1066`f7yxqtAXiR$81RUAK}-Fh1Z~t#m$rmenXW_8c@_(#Bc}4* z4!Jtr{EbtCu>^}(Isb@>C(!2;;vpwIFNMIcOPftKIkm@_ZiTvaLm5t4)cUuCE~Wng zODY5VQiB3}wRh)M+KWFq8PFdid-1F)+3Qh!Z^7fA84$rZy8`&4Z)@VuqiJ2AC z1ZnAEfe<4ixZlA8;4O%#1D9Au)D1L<>1Z5pMn}AvegWS63a!HeRttcIg6+Vn;w%l4 z!qvS|@Dmion~@P!jJgVMR){ywh;MS~&0ixcf1Wu-^w}y>#P}Y*k;b=D#+Xrus|+C- z9+QPMfT}jfz>O&yU0R=$fyckU%f?K79IwA5dc%U!O@cBNP#yb7h6Uw@E)giJW*U~g zlJqG|e>d|Ik8OEUw|^9QLh5rZ_K+m**{_JFEn3Mn!SRukrclN33cr=AV|-;A@?kQ0|Q&lrPT5n}@}lg?+8W z^}~61GaYX%SeXfcKxPRo{=0^Q(ti!Zlp^#L3 zN6HA?N2HJF^X{?eA-=V5`hIO`FU{;k%_L8Jj*K(KOFUjuKFULN!uWgP2)>9Q022T^|8X=IaCw$~R6ACd=uE{)Vcmz8ghsw2#Sm0Fb>4f!K#jl;>ENay7QqNq}U~7iC zP6*be0fIfYx+(ZDYGW?aUmDC{!?F{cg@@F!NAU;_ zS?WM{hPfOtW1WZvG_4vRYuEw9LS=BMga`(OUtlN>US`&L6;~iUl)Tzb#Nw1bIh8&M zU2ONQN|o&k>w$hIzF>IB5&Zm}R4T*n8w0h2d>oa&y6=1qc6(`xon=G*Gi8hR%TXc4g~hn+mIE&2%;1prQE+$t59}%fVZDmz&6mI8DJ6;F zftqZ-BWfEA?NmaetY}3_20vh*-y;{e%`c4oE9pl4y>l@h!PK18!^!t#Bf zwBVz7Dr{P=f0I2#nwT&j0+brG9&2SX+0x)u{{>25>Q(~+SFY~8$esJ6=KA+V}lw>dd4$&2|O~4p@ zCwwc=@5wAlm-e%&3XuC@D6*TJ9S_oFNHed@=Z47iwkA%qs7EntImC&TqfMl&*x zqj`KlPDxN3OyaGW$8oe@mrI|9wlMiAHleZnB#kzBGHS-lXX)h?;$<)~vb8gQOKWfb z85JIRGOD$gsVdk*pTx_5ikCs0ehiOb9ag40VYTvw;1PTi#s?^!^^XgWV2v@cgps-g zniKjd=l<1`86RwnmJ_j$W|z3EVotjTOH|shUNWCDYUn|*6#xsjc;5Ba7Ka~ai>NxX zMS2i?6#bUt3M{<_6p|f<=niA+^1E9Zqm5C<@X{#jkNsKTSZG0IHwz^!6LYmK=!mFq z<5AT&D5Ac}R3G!E1EiH1{5PfqBI;g$B5HT<4CCjrF;@Ka!guJ&Zyphv+YLb)p6tD7 zom?B!xWXD$t?t*W2dqVR&zj7L)-w9$k;DQ^{EzM(}$H< zjuD618?E}se~YU30j1RSQH@mn%kHSE-WW1l$dyf3O?gnZt>*%;Aq> z2gV~L5S3-*|8mpu!8Bv^dK)UG>QvCVM4TWpmsYF$ekAA5BjkpVSlhLC2j&U{y=l%~ zzQY;z;#W}@z@Q>xDXDDT1(YbE^{R&HY_G`sX`B^}WDY*y%wl zdc7OIHNrMUJi})w(LT&OspysOyBvG*M8L*Q^>=|$-*+&9YJ8F48N}Z8osve*0k+U< zUh@aK^nU_(kZ;&CM)J)d2gBR5a*^=%c-b1WQQrsn!ykVjKbEGjIN#5b-(SSvi@7rc zht$gIo#UB{?_zp`Qnp|p3HO=61V16Nes>O$XgKbZ{ZkMvn|cs@ zyukWl4;RtjB?1udJL>=Z8g4=m6GwD_-UE-D{gIsjE(|0XbGg3s5v+Ib0W@-|dmKw& zhnAFSP+N`O1*3qMZjQv!mtHRj^A4DnOpseN0`e(gkS~u1@;O$JgWoeYiZcs=8q&~( z_xy#uum=n8dAZP1m1VFq*}|T5XF+Fx%X&Q6INEn>Syh|rJC$LNqG=`2q)~G=%_yq& z;IlAUqcW(qvxP#=FNF7opO|m_DS@^`mXHY%FDeeV0H=4eyikDFK>xW!0vGfH!l6$l z1%C!sx|G zuf;2p`{N{m+|3JOv1*dKP&uPL+Ba`mHLC-VSJ2 z@h4^K!$za+orHo0*kNEHMW=mv5N8f;N@iO{^JHQKa*XklF+&u{^Vm!Ze{>ZbpqpEK z+b|uk%cri}Eem6O?ZFk}K0Dimin>Z-j_9A{e@M|?V+cpHhS@oqFd=ELtV_$|e+4!;*iXWM?nFJVl!tsQ>1;&(TGv+>)B-x2(} zjLo*)i{I1ut-|lrtFvt_ugSLEf}an+zu>nPzdHPm;8$}k>Kd1AyC1*r@M}Fj+tvd= z#zRI%Mgo2<@N4Q{Vp4M4*DYJ6WB_#;ZQHfau%~s1TZSHXr2loI|DEuEoinD?E;j=r_O4s?MZ_3>Z$d-;TH+u!h8e|@%X{tel-K?T{iaroVZ-+%CH zb0hG2Q?{+|B=OA{{Cyk0ki74HbG9vj-!r2BK*F@f(L0L>esN4*L{77`wNtrX<0V8c>t$`Rn|t0VT;lFex}2G8AfaiZ_?6tnC7h zB>&ldB`J6-iCq*)@ZW+I4G8&}LsPn{JyypvJFYtFE&l}-OhG~4j3mrIf6pXnyq)y< z>BOTe=&o|0nDezcBp}tmRg^mZ0|LuYvd=*=?Hd&Pqvb9wLvMgCS2#1X4-~JN*r5oO zF~>8&ox9630#0ladCRai7fLJ3G*t>Zmp=oSEagy;{&Eb9cv9aP0nNSKl%Nf9;INYD zcLqNTp_z-&j06~FcZ=-5*{;BOlHfz`)6_Llvbp3F3v^q`2Ci!@Poj*~B3n9}fO6Mc@J{7+KE<_pEKrmQXV4~)q3iAz-99D0HMe9$@PNd6IT{9U7;@b+yAH8cwH zUucnFTJZYn@Cy%;Lii{fg4oF|{xA1mP9Y)Y$xI(3C0GXh?9>tLfp%)2k{rYS#FvE7 z+N9q+jnpM*7<4e+CHIoLB=>mKltG!aO;X4p&U-pkb5BQCRojU>*&XqcIvDw+G64h! zxHpAgZ~@g1#r|7}b~eZwsDYSd)F82i=FC*J9~|0Y8S3KH-gpUX z0PHlEG2OwIp02*bN!~lKw0g_8B{V==w7l@@fwtfe=p(_7h# zH_{!H_x_WxOV|blt;-Dluy}bMKJ>R-JE$f0%Sb{>QT;=qVa~B##hxGBr3I%$`r(mW z&rX32l?<@MjWsDQ<$8`IZ(!!o^^$F&%E6i793osyXk+oJvL#vn2#5q5+O+9c0_*g8 z5E=wg*Xqi2(6E0HLV!c?yZsD-31BzQre4llo?nQ$G4bQjF68O(g<9GlUF}=l!haGz z?Y(6!I>uFo?h!St9h6LclKv~|Q}8XSPAe@047^~FD?1tffe<-jF%b&q6G3uJAtGkS zctc(e8O1Xm^|?r@!H`s~kM4zq!jOnZJ+Qh8_KkQ{n1e@|5szM{QXL{54LKK&eg*Qv z)gL1BRei*xuP9oAvNkl?DFg7vyHshTVKTclCtRc1yc9k9o#z90Tw1Hr7u{_N9Qbk7 z+**6dK?AoOyDUYHlt3|ZhDN>vCux=)h-q#&yfO6)8hhIZZ-ak=$*!>iBMI5=CJO96 z2L;Y;qCgQ1L~0WSCepBc16N+z8R$Cpok2DVK?~X=xl1x8QjFq>eCNXnw|;)f(U_tb z$H`aekAFhQ1>cYunMh(}GQ>zP79(pWEu1+k+qMzEo%sELU-JD}hv3%}zsv9&f#2Wo z>-YfH6!?w6?=Jiv!S5aXtnxZpO$R#{LyzLW!9O-w*|(R2vFO6fK^D)6@4R1gsUFIRbMd^Rc{y`8aPuFW3Xc8 zkE={AI1J@ym_`&WsL=iije zv0YrSR&=p1APaK(hIkZbs%1+U6EPGArZXvS<);>86vkQmh7)z5*d~1f-UX*7GlQfCtAMq*WMg))8hD7q zQq~TBfz%Y{XfM7a7N@xqt}m;b?%*TVibjiyB0BiPhoXZ|q9Wdsxi)x;n0%TL3hmG$a<72x$jPz>Z2$4fUjoO=;w1RUf<@hL>RmhalxjSV%HC zN^aV{-O}CXZ;!>BvPKM8aFg7*Pdqpi1*IPVdXX#lfHyzP_cey`-waT>z;FV{H!lAf zsKt1w&qvk4#ZV7f!+>@`N(R4|dPWn0DMl0z=^3&Z=40V<&@ZBjMF#Pv=A1V$l5rTs zN?t2Iv7qVxDY0leWwk)l>W#4sVkNJ;tQB1(DvBVl%@v}e8K}smZ89ddDm}|(QA~fj zEi84#0Gt;dfQaR=%Ma`uvkkui_Un~Kl(~3fI?faptIU18fdJy!>;qf|YcNq-372PK zlYXU0)c+2ZQY^8IenP%OMg||^dDa}XbVT~RCfWPZf-ES|x8M;?cK z)Gs!EGvQb1Uk-_GuuA^`qJzDCReBOYuTQtaZ7|%uSQe17I-HbMXsjM+ta8oE38>aA ztSZX3b(;+vxi8z+uNd}L{3eya=8fM4bFyte;pZ-eZ5zK*{1(oIJrydJ1HV=HS@l(^ zA4>ZO2EbT5OSKgSip{#O&aMW!@wO^T9H;e3b>Qq3xsd`>#b({PIfz1vZwz z)&tP2KgognG~~cD-kqg{FhvFm0W@4*v~3QPD7MLA-yCh2gTp2ZB!bTH9=e##b3ayp z21f8 z5*mazY;yrAPIYSYsRDE0HXZ%I^{;Ar;G+6RiC8j}(O452Bc}N3a4ORePB~=Aw8xXA zKJo&h7PDhw&3OMh8nV&TX^x^KBBD3k84idDK@7^SY*&zroqA6+xE+D>u{6keZo(Tl z@7yH1l4mil!GSQASV=Jq=u!Q#8d0&j)kdUP%#JlTVgLF_B*WcAX)jjej_40J4f?yd z0lTm(L#r)o2kg5vvOx;p2y}QhQ1WyfWtYUe?S^R)#mErgqFK8M3#4I8Btc=AFQt_- zC;CaHjhDKH?Q+UT1SotMoAm4X680$cU>td0rcmB_4~&rz#}k4B@Y<9&l$y^FomXe6 z1t4J#K7e$|*G@f)vfC;}U-F;aHTII$P`w~Y#dCVl0EGBvWZ3K_`*G#eI5J_`3|z~} zHpS@^(F%A+D8N9&cP*n6@Qj>G6k9I7g#AsHGd_zb{UFugEGS19g{2BH6vtLY1*+hX zDA}-49W7Y{ZLuMXfsF$Mp%k=sHn8*b~Zw|~JmDhm$k+CyRT6!%Jn)**z%BXPRspOx(Y!m$Rz7NNS z8$kUzRLyXza5#d#AQU(f?r(qHiVbX_E96V3Dr8b=3D4`HE;hCIhg4X_44qksa&(ht z!qp3P5ljT%4iD)rQ0uu&hJLdZrdKvvVUqQ`kE#AYVhfP9{#Mrdhs3LY->=64|H)t+ zYyB@`ab@E4n+#I6W$)Dx)=--l{=sD=H` z_ig;|M@D_Wh89Z4@xAXjzaQA>_rwp`zZ}c>^+n&TMxC| zztmd)$Lpi&kI!#wo-_GPrO(6s7Qg)4+$Qs9`q7l%8ZNKj7EynEdPqco56c2cPj8El zj+&mLzn^xT-}i3xd!mQaema)%`Xl;grH382TIk`Fby4F*^{+f~EcM?LRsTKK`j=Nm z)jt{aj}O<6=46Uy?K4V6`~<|N3xA)D5V14~s1U8+UPYoG@&G};EN+c#MUdi@Q+l|5 za|Q;kbNWZF1B|W4L2367tdUK){a4hAZlhe8 z+dop*ldYXr*D}Vr)6x28*#G@9oeks)Bww>H)wUda*r5LW#jv-(a3@Si{LIgKGWI-g z{iFj*P=4Hw`d-*WVZ@<$(885ju!h^b9hQ+_TYr7d7J5S=*7)W{e8bf!W!t!c4f;I# zRA!6uCq#XHhxnSbT>cDIOg5XnSRXFFWhFS~+uq_^3P3Qv?PGrX2fefSmg5s*zCBdJ zP?MM0_%_M>_8h&Ir&o=g&@tbx5Z{t-*Z8((n}MqH^rywQ^cuZcg*RM^0&p3b7wY$- zgnY*PXHaahS-~axwN!!MM}GGxY$>8;vc?)M-)eq%nSQ2dnO>u{sBe?Sx1{wN-wv%$ zl>>Z=zK17djg{h7^KZ$SY zbu7#y#J3cT4;*RujH^(}Y9(#;5?@mR%h#iOA9MvS?XwvW23C+C(AD#&{J`BQ2pP>s zPJSlag9t%DHtEaLhWr3i-de6#^|!(h>7UcQtigWs0Gvo`lTfh}ZaQz}Dwz3ET6S+{^HBonuBWid#sS8>1FuZ5@I3H1BGZ{X(ZgL zhP0JBUEd;lT8V=?M^2N>`>UZgg9p?sywKdTk~A|2MKH}Q8bDpm3Kkl$U3%i;O{ zo3wnt<5m3r=FD9%7M=E9PAE>9w;L!nb|}1ZgAmAh;fM}H&qrYvulh+h!f-hT3buXe zfR4*T57=xyD(v|cm>2pua!PMR&Ej}G7$e6bUK@$NJ=4T^t7bm%k0~FxGd3Ui5VwCp z25O+jdU9&D@0#i!FJ36lo-Uz^r|127rZd~Jt2 z_i-95d-0V3(p}&q`f&!dsk14;Ry8a%!?70E4)OUIMY|HQR!1^kr&f*=GE&sU8Z|J0 z4_eIc*EYq|8krs%JUH{GBjx1BK?tWjfo&@Tw4^lqI%NsMf!w89jsC)4Lm|!mxDxuP zWWBwlE1C@qPSR$T<~G<%+T+eR#IFUe`?+7VmmFqP`L{P?yzqMjpJcevWN}23FC0aa9nmCk1gucM zusMb*cLf*+&fJ=5$>`+FFWuNm;LI%9;=rt(h zj{QWSfE|5s1k~xWMus|+%YZO#M%7?py(HRZnVz3)XWKvI0raB4p zK)(Oh$&>td!A!(r!Tuihna!O`l1J6tCzI;A`h93&I00igaE?6!B(OzrZNwoMkSBDm z9!Njf;Rp_#k@pyYU;^J${Iym7`@Th=l27~2sywpK!G+V&pUx~tCwp(fHidP#P_*ye zlnj&&_2gxl|8``W?>c?l2v8A>!?615`Y^g7cMRDd2jB+2HW_EhA2u82@; z+_I6)47JWOHk8OW2v!L{K{o7a{re@n!_9=dFt{Jke;P(+TxosZAHJ#8 z6W$^O&zA`9q>?nMBizD-1SX)!Ow1Ip#RHoLf+1@e!4!+d2yQ!GBDhNb{!O~X@Kx!1 z_!79T(m$um0@z;z_a$J*QU)UKXPpWh77}8Nls?6uHjrYZ+3L@dugLN&Z0MI@gy1@M z77U*0_UBh3QS0EDd{BTp_e0OcpnsA($ee&HF=9p-3Cw%Kn0h{=U0e`x8)gt zgWwzp)Bqb)+45`fj000@PzB3OyrIhB5DiyqJ~5(-E1+fxk1CWj>{6D^>$PtP8fywz zkXK{CIyI12mHsf*qJ1QY`J=N5doFn8Kx?&Sg4W6irM?fRT4{~eU+G~lMuutS{#3e~ z*g@zD^r<|!I>7_)=^R!Qg>pDkDumbJSf?qOy=Y8ewg$iH&-18WF`XWfb8SNoULhz> ze+@63+A~xMqn6!oy8gABXk$SXcJV}rLa}j6^`9Ny&mi7VoTRbCK|X<^T5UYkXT6Sk z5pR_X?<7^;$W95)^J3J)e>t?ucmgj>wSFO01sxcU0)*L6Fn2T(h+*Y=HYy%n)xL+% zK)Qku2`H!>hJ~Muz(3T5im6s;*b~+e-ZtnHFr;e1P~;#k2M()-hX&1*vrb9Kd*L!M zVZh2BkzkjcCy{w==X1((Wie+fk!^+pTL!v5pfXC=A%iZLlZuc2@oM> z&hU&`NvD>B5eT`ASBmUjL~C2~i3zE=;PH!u*BCcU?LxbzmYO;<7#)8!4of(!5 z`rIKzNxU0#c@G{6_`qt=3l=5DaK@xBR`J9b>Iwv@h*9T-x`6>b@QNk2CIjstc!{GG zX$vxig}uL+gF?ax2wsCv!bphezv8RFSwygW@gz-*aQH1Vf~8u2-znQb)GCE`QyXJr z+Bo>K(MG)bQzPsDv$g(laq4e)Cb|!gHD13;?sKNM;~u}0<@l{rf4A}5^pa)#CZe6_ z@%#BN(S3gspENOkC$RstgPX+Cy1~C+|B-&u3?H0`c4GSfbWH#8$$zK+6y41IAN2e6 z{{?IR(N0YNpNi=}K8fl-&BkIsQS8jFeTL8rL?_UcBzRhHIE2;gRMKv2er>Cc-H+y^<>)9%Ny3 zk^u{Hel_|Ry(uVOyi8YgFJ8P%0tiP00YP@rk^*zA5d?I6(LaY?Z90e~JX}&=5TOMw z?Xwka7~LFO0H3tju7-?q>qwL30D(z{SPslC|)!B`-=g`+1sU-3f zq03UJ0DWxKg-Y;*U{aO|UcmBlw%j#j)C_w9J;Zz*Cn`X`Q4zin4s8&PnXQDYbme~9 zM7Lc)L}VpcyBkkveoG;z3egK#p|MS5U<4=ailC4rQ!T;%|6hF#t#Kd*EsX>1rn|9= zVNub4-p$6As?DL@IC5bsj$V*!nM<(j4xR^7o1q#IYT{&&v1X5ydJ2@1D9t6ycoO`g zg$VE3pkIy5q&V_GDT7W=jnI%t+oJ{`e3D3H^CHA9LMySHQGh~(?ub;D@=4f0Vw8ym zFIu}HEm@jDWKYjPnzt*C+vTeWMSM|2|OV<^+ zrhEEJKPp3FG)A9XUU&JgAQnSgl^YsVEnV+$xtvZds_N^DqNmd<7F(xNV?AfgH?i}n zs$tGNOcs<{ihRr*b@k~JO?r@Rb| zGy4VKeum+Sn*WX#o-WbwZ2ONDp0&~NeDVtAEd#g!sak)LfwzFW9U6Yq;3f%he=GiO z!YD-GFEbK1T-xJ>-~gnb z!Bz+yUW#Ki>YUn>hHLus=grm82K|}-OmYxtLQK%==#qz-X-qcjOYN&>Gp;<;p!0HaSN@ZMdCJK_AF#g5mpQ9gtpt`y7|C@FE zFNrh$3+4Df+06J`D;i}~WF7wna{N(I#Q1kV;p5-$f86*ZA?4AHKMERSPk)bd{DJD` z#{aL@@z04f{`2MdKheziTPqr6RAe3hfE<5RBw2IS85sKBp9k*$1_a?fdlFba+UgxG z1hDD4zCW4APnfiLxophZ7>7Qj{T70SCj{-t z@(`;PmZTyaxB|6e`NcfOJX^s6hFP*!A3aYD&99FUD&|m8N@%FT+91lJTBB96* z+OsKf zpkeNo?0~WYd%gg}$I9lQf{*|az0S6Mw>F!a#*qVjfMIwYcdYA7R%t73I)cHBW!M$k zypPG$FDrFw4^vZEBQ#%k>>3ef>^e)`B4qBr-@gV63@5sO2P}2V{$c$JTd$#ij!|Ow z_q(*+SiSb%;-*|NwPDp)&j1k!pA3Abox(Q<^p<^ya2C+Y5m`Vvm9MIGP;nLVw2GbK#m@;?=J{9XK_nJ`{1w%)IiSB`QQ#eggzm(=mF}}JT$^KO|ey- zsUoXXbiM`!m6aLPd${ae#yIBl2+2OnF~joM0K$A?j5KZFl@F~$OWjpIuq?>?h8w^V zUwmQUmR9634ag@jfl{<1#|l*G8qR(&pYU34hR*xw31IXitSZN}5z~oEbRDX4I6uDu zj)ab^0*ygs)NUnbUTfaF&_Yt}dWFZh36hE(;|ok*EV-WIxmr7-9RLwc?N6N_Zm|if ziuL2hK8YaWzb!DElLi(XyTY7-=>=A)X2-LM5thr|Jkf-(w~^fm22j~U%;VGqIGY+b z+oelk*)crUv8}`kGC1YKqbjO&A-i7_^vRh}!r!n(&(ad>0`WH&!$y^5Q&u2V4A0tE{ji~p@r>SIm?BGYJ>jI zbBW40bd62kli>B%uvb%f!cF_#CE;)73AwRP5ZF}{E@t?jgP+ffP(Xo`LO`ypK)B8S zN_~y>ng8$kJB07|f4RSdLjeQr=JsKKhhEjVzk?E{OHMP6?up?vtKpcLKfO0e=lVakntgcmLGAt{xJJVR_A$u`Z7@=KlXK56rxXgu>Ko zx=0vp#zH_mC6|X->^I`_uy?l=Z^*Xo!>?ka_)FKn$b0%rKPp4^D~`7nc`qg+7W5x$ z%vjK|a{53(jrLCF5+*-|mW424vqyyDP|wD=A#j#vNgVx9hJy`k6`|xj`+GaE=)()@ zfsrd6IbXt2tcJReN{om4`)45}7lE8u*)-^H;wBuD`~6u&>2X4GX*nxHaw$63+&zIv zeU(id`gUIVa9Hk&5@H0-!LkIGB1Mb-JU^AOv<(YHd?W45Gh%5cTsNMT2ooV3EXoEC zP6V=u4~eFbATvYBGMS--^b$e*rnY0KDDn*lb#WQ?F-O>sX#bv=-pJZgu`5|Qv^ktD zgrPthl?;X?AHlR4Zp2+^0vs|8t4)>vD-;*OD#v`ODBtN+dW^Em#esO@)Xc z#3H)JUi=rla|*4>h)~0=7*Mk_PMKUqzG2V5?2P z(`$z6we398RhonyW35hN+VFE@5j*#YQY!^IIZ}yQ^il6 z0ML%%yfXn&)iPl+n}$Rjvyrv10DE02n$e~HU}`QBPl87)^Jix@C)x775su67z!dcX#0@w2=*5Fj<*8Bi$oE56MH@h&mSBy1JrTq4o0SY!c~TGV#0oduV-;Sim3*R zexu?Ix_-Z4fm@j=ZGGW?b4wsGzPrpiq^I=0_K1U%^V;@C0M#$6|uoEQ@BrMd1#peLA;klCt znCKV#8?K@tFjr=Zb*GZtsano-F`J}@8hDa6JCSVkk6+9Sedcs*7_HWyLuPF?aM21J zwnQs=C@xvp=DYD9so2z(e9?e5Hq7!FI1>tQtMySxeCy~9d5-O;{Z_a@d!B2_sXv}7 z(X@$pfREpNunpCP?|)Qq6-XY6+Ndj8RM%?|^m;$+&lFf62lY*bCRM zbv%w9jIUHnKKG~>Zn1YSq++TzFvb2J#@DfvD(a~euB66oUYtCUm*vzpX^(LyhG(eS zsc$8qk_0MV^11gTs;qduz4)sZm@nrTU3F?ZU4a)!HRk=*iq=2VKkIGmJi7KEZy0W& zReDm3_CCK4yNGAKtyKcXw$u@0z2^T&>x`-Y+xo@!i(Zp#L!gx%HD^I z%;rJz+MO5{x*?1p?ex8QAuqg)_|I3D==9b7oKJ@&xj%6E9ebU=ndQT6-fYFbG(QiP z^C9Vz{Ej!BiFxTg*P)f~e8D7=$pa={;m=<;+?~44tbO9iZE&-X! zu&k;PAOS?|>Y0VU4u?us%8cS4b;zX^Qp^ilZ>5A57Q%Lb)dbo?mcTuUl$H*OqV02D z;4!SyZ+w7|wLs)_X!qC~w3nX<|L4n?@MkRoR~dv)yFm3$_n9A%i_^FJXVi^NF>pocAO?5IdQT6vUr`#a@5w1#CJ5yG!e!?$rThWIql7lspKcWCV!Uaqb>w#o zvrrk}P4sqFeP8}uIcFS`C=Jl~rM*9QR+bWu=pT4GxSmg_owXRk;EhK**aR;rmCOgU z%**$Cp9;Q%RIaKv9!#@hB11|jTnzLvQvHrQ^edUOO+n&&J(VR0Z65|+zPgk|!v?+W zy+8w`NU#7ER?Z<{GGl75zBsV;48Jx$_ynZ*4zVCr&ZQkyxHfEuUElnqh{p&?Hq+bq!qH0%W z;Hme3Y$CJ}`?YQLIIzq6l&Xz_G^z?cED@upr|LMYZmrwDQapK@&sM~q)N;UW-K^$g zzTJtB!+jN;P2x;1M?loZA};d)!+>JcXX>o}H+l)qTb%D-M$A}QM6U>&`doS?|K-sP zLHmDH%t$FbCp<(r((cT{w4mK`Zoanc3w(Ak@7x5MR;GH|`Ia4^iabe*uNu>Jm0A&O zsrH6TzQVWba%?(pc~&_czm{5o_0UIkK!fTp&Md?iDt48VP&=t#FK@?wlasvdoCVlz zuVTCXg;Y%N8ba{=PiM~M^4QIKBSky$eioYArdRq{T;Q$#*A8Skt5baqA#W?}&)tQG zE?R24wQrpj-?nfTeJ%3TeMJcgXyx6@6?l6I821XT+n!;8K6_a7_UrBK>*{w~j`xCo zwz?b%D-#Q&i?5w zHT-5n=;KNs*N0pEogNUfHPp~GXh<6fYPuq$Vo#D^$eRluLha~r& zbMAS0&U2pUInVQ)=lr&>fZw0U-w&|hV9PTw{gOsAqfTMt^LQW@jX$Gs4QoZK3U$=! zkx7n;mPCecHcfB4Up|Z0IkPb*mm3duw+vhZ4NZV}x_fu>rUmxmy{^M4>ShaB7C9}S z{h(^oY$t?=D2GYHzN^&NSE*IXrSWE$*%Ke~#t)T5PZME#)rKm+`xqj zo{X^b4VCirD8F1_w!F@|@c>f5o_UtvPP0;fC7fr@JEWy|mw7wfRu&bvGQYT$QtA$> zR^;izF|Bx2D`lSSHSWlEzmwiKvE_BQg(GGsf{Pl-z}E^R(OQN1OFPpTjn?`u9okUF zz%zn(?l{bMZL|ol3_(c#zP3JS?iLcG$N#{6hm$tJG?t#y$0b2OUN^XEvD|1DJ@MdO zf;P?_nG1icCN_4*LN4q8X0+)!*b#`9n$I2VsE(FF6`8P)6Zz1Y(7|!>RJE@oXr^!@ znfb`koKpiwo9UJov}m$tR$ngceZy>hZ1A5Kb5-X!YK)DG55CLe29a}-qiec|CXpU0 zGrR<^(u8W%>*9qi6Ar;zqp+kHF*Z~(`0T4Le;7+oS`_-dY~C*jsLoU+1Oat(1RP8c z(oT=H=7C~N)Y=}P%kiAoz7W@)o?Y*l9iUzw&w4Dq=(9{o?A&;+G@75yeDO+!Gqf^Q z6&GyJltjxx`D`6AHKJ#`4Gg}M&Q7F9ExQE;lGCfK!TY6O%|qViL#5GDbVsArF-j3a zJg{)$YSB0o)F;J<=FjZ#DqIo*@+@dfE<-3hEX;%`#iHm08$FH5rPVB-YPr@fV6>!# z+FC8dv}azI7|GV!K?FliIp9c z_4rF7w}}5QwFrk)PIx``HqjdT&!ai5l8`lKg*Jt4BG1+Gu*!OUOFuaz-Jsn(P;rBy z-I0P1$10751~oxTzY!K>`6c0S;oL;ZMDk z&@w-h&WoP6KAgN_FqBxz4k5#RUchYty$OJ-R78PsY*S;rny4CP553WdG_oov><~E5 z&3@Lrw+j^0)lwlV(6%M9@p(znLS5;>@_1LNFYRl0R$1aMDIBYfZ&Vy8I%|n@;Vl7c zhkN5g6}qo0VG57S)+lg85_NTJYJ|E~fZVMR;%Y1MiCm6=F-a7U+`?BMHgCeEbT`*? z6`jXpe?4YvO*rJB`NKe*8A(1R5CAtqM0{Fr)AKNgf6SNJo3+hm;d8@GXyFew&|mE? zjdyK!z>~i(mBlHm=waRb#odSlp0nqHMp1cy()pqOir|@ikRl>IgO-uXtw07r4 zo*8a~f17safAO6BXu!WiyZh@r`d_^nN0vzKD)Y^{EaR>wjqiy4-f8w*JzqHrf@*D| zVfVgbtb8t5_h-X9Vs<Hcj&?W5@sn7ot-@u9l?DEZ5D?|?1;nqAx> ztb4Z@E4QF|{Av|lHoI7qxs=w8xVVNuER)u((~MlF4v~%Z$VGZr=($ZHzYT#q(e2GO7jJw)pkeEq@yDEG}A3;l?p4aV#j0j*q~5%s3DboMevh=q}<_{wk8F& z^<8Frh@iw004WT{=nGpbsyI`IE>s~^rjpy8!dNWK?qz1{l(}S?WffZ2D1|6?i+p9_ zmNeqCXU=$4;ZS+RyG38vQZ=Ed%2(>-9*dT_q?z3jv#i9}K%e=S^B4kVc|ZlwgFwjx zrjSTYmx|0}RYrX3sU{sxXXM2$PbrTiNFlQLz}M9>B-M~YMg>8gDh*$cG?OEQQ)Rd& zW%z)=E>h|HU`5!ME*jCO@i2BZn4*DHNTi#Y|93|r^Si^Dk{+4gZfAZmJS}XUaEd*@ zDsFJ5nd`m6M76>$rFAjIC(9HE6J;f%;1M+Yocad1J-nr(Xo`0;#XIzc@l#}qPq4|X zZuVs^k&2O63Ugc2Q#`#y+OlL)>`61yd*r1spOTQDPjZ+~g%y0Z$C*zXDTXTND9rJ7 zsp*X{wdo-~jSGH-L6P)O1(O|4OfPkZWVVkUPkOgI#rc8wxI$Y-(GvMTI0{GJaP{x@ zc`wu>s!Y?_NQ|_<~?_Pok&c+$;pR$hxZyMbr9-FFgax7J)k!Ve^KgS4Ezz2$Hjf1}sY zlS%x&h`$`9+t{JWmm>W*?PB?b&{dm8PuAc zNyiJ#&s7=HEY+Vu^)zI+u;UZ7FoA4Z*V9%7Hw>U-8!6N7{u3QQ`E7lc<9p^n=8wFx zV&c1IQ_%d@10rw74|`*kdgq-Y4#>YuibC;`X)&&2|E45n9#;v|wYKl`K%&SLnQ!vk zxbn{5@dd{IbY@aWTfar!tGNn_uO3d6heP9{hAds^YQzhFv58MIOhCP%@+Aw)rbIRA z;P=@i(!n6L7BLtWtzt0boGtB{LU7W%D-;cG^zH~Jmlf(^nRi=vP!Fnu%n@XA>{cvh z$k;Sl{f0Tcn73fy%(EAx21!fU7F0())ufGdR=ub)!ctTMg~-B~Kr!fw`Y2m5M~IOD zagjR4P#{$dTIcKL3DUD8^J#iE3LquI9E$-mRR;3gTG0AQ_A;4z`Jljb8H)Dg^okqO z?G@US?c~e$V^oErc{#SiTB&foRHz)OMpwALtb&Zqr81dn?taFQWt(t~OyDJX9+(pN z50a6swuTQzD9o2dn$SK2Jyoxgt|RSNxN#Gar}Nn5U{;8rW3$X7eV zsdgvT+K@P7RL!HIF!6Zx2_%lGuTJVyfOruEE%a2gfsiIq0U>&$0N`{0K)<7ttbPud z0;KPfZ_OlPgrM1jQn+{&Eh)tf)tmw-w{fjC2$`~_D1pNG{Fo#nuyBfC`MG>s76?f6u(eq+0LrEM4Ehi}RV#3$q;; zn)G0G#FvWiDT&UNU(@4<8GBE3tMyV`Rq!9`n;z@2HuY-TaC)rI*{^q#tEh`N`XTxJ zO!;*ZX7$)ANN5klE!=sh{E>gzYe?(jC3^)ADS5b*hYj+eLu4M6hXp)*Pae+W;W2qQ zgECLZ!>R0r=7y7B5!Gu@1YdYG2xInH%l_r49JF8IiGDb~uOxb|{5p}XaqLWE zy9Qo{P>c#pd-j-=jbl=5j<&u9j&|Rl_MYpRw2wPL`I!Mn`VxOXcHmsk$KN{FQ~vh3 zo<;oJ%g=B4d4-?X_}RzL2tS^ZQm@y0LRtAZ{&-Jt{*{!TaKd<=$5)L0=N6feFX?ykXf;11qE6gY-^hff`HS5G4hJ)3EMsMWhJN>$sgJV)&s}| zN=BH26hgF5TSl>@}8Mq*=tVP`&13KwD_9MDuosx>>#t>Bq;G=ivcJ}E(eN`25IANbu5D&>REkxiR+ zg@)wMa`UB30dNjR_kS$UjUGHHk8F+0QaJCoo4kf0Za{1N<|$iJ;eE z4`zP^K`$p#n2RkBQ(tB;DXJp*FngNH)QsVmjMF#XC;2br^J#!d%&c)`(?_JjpOTGg z(W-zAL{H6i#Yeo_-QOV5Il}RRRkf4|Hg}U^-(bY%0Ah{$?&2h_RKrUxyMTm8Z|F%ps0up^&@`sUw|HxM^_nh!x7K=c zar56;LQ55-Qaq1PQm+6Qx$|NvGQBQtjKUd{pkL+8ffH z6*oxDuaY>n9uX%-*R$TK=kpg)&yR}g39O?Ipjer!2}>75Eqt5`q~oJ1xU#4McMOj$qJr0H z4-3h!;TN9x>+fi@TX_)hpw71^s?*VZAd1tW^Ob6Ax}z@@7Y(caBt*QP>=bEGf%Po< zG<|{AZFdX}S<1v2uuI9mPAy7-GDCJ=N{3?4`2}<+J4HlgtvhVC){7n`G2I*QD2sQN z8S%Z4;J;@anFiJ8@xKPQ(P}nJfAcu;HDX!EJMehhTN2xokqg7rNp3|U)uc42-_?a# z8M+I~IB3^l{o-z3^`I(ACuIxVY0KSC9&}dd7ULv9TG^t@_{D2G}td%iO)m+$Lb`CB#y~zkT^~_ zc&_IohtBof$d3jC$?u%&$?}ut$4~mD{QQ`oU-0uRKi&N7;K!EL zG_9;$WHt3q)5evQYT9^Bs~A6F^gr@7P4jvu@_W=j5!FT|{B3|yT|JYd$*Q{a5=an)X;~FR^kaRr23#1 z>WM2~4dt5AT^(xOB0d`3flzBIN?7ct_wa4^iNVDEq6Z;2HZF0$vZ$EtX{$6oI6jE> z%tx6Lr;MpA-)4KQcTq`fywrDg_SA#zrO~MlFXbj;Jb*Kuejp)F9Z#Rm%CWAB0feU^ z5*{lek469N@u6}xcUVsl2N%Q7IQvsEe>MqlWJat%u)2c4F3l`fSMu8b@y)4Gq^{ zJ7|XM=_q=YYUym3boO%9*^`Sq+ZFTKJy1=l4yTJdeA0V%_&qvzadGE9EuC}bx1HYT zzLYZ%RML-mW9OkMU6L#-4K?7Uz$p>Ep)c6h{pBjgkF<_r{)+j*#8MnQ78SQ9Fn9^4 z04KEER_pNUVi>$ndv!3ex|BE#HgqGPxou5+|F&)_ZtLDrZ3&jR_;D<}7fAdpP$2Ej z=kNhtF_W-Ah3{WjyQBw<@~0A4EHc}*ucw*$;&B#DxrKj1e#(8LyYSxapGxZzmMv`z zA9zaPn?G%6m!#jLk$7<<-~VS9rfq$zLs6h{0;irCFu{6v?V?i2>Q~zFEwUund9;7 z_r!;)Nx=`Ee6R>rBc|T?FX)sn-b0!_B-M$Cxy4LK54k}DW1Qs1{t*LZ&`gKpsf(AI zWi1~|mbK`KmJzAU43zQsj{R7{qlk5+PU2DgSa_`DvBG|w&f_Hev5H4gO;O+!9;d6v ze8Y+RrD2G{HJq)GhQ#9{p1v4g*aZF|2~wragkHvzG?~yVc#;+qI+|(KU_!6tN!m;3 z(|MBS5_%O+(po}4MLcy@c1yidXE+hA?s#74oijzM4>=U}?ZLW~zlTE}G#O;Zg4(~Q zR?`Z{u{X`lbQ8bxX<2aA6aVDS`q=r5KP6Lxv)GR4N6ff@x3fj1kZLAmthuz=Ti4_7 zGUL*Uwxk#Gr2Tu(>JM1|K>hY?CzKzTJy}l+iL)-27VX9x?8a69RnJkc{dh=&c%nKeRmZHQ)TE@*Pp)gb4WM7zw3u?*R zc$N1%`)yBod{+q$jES$wV97CiZRa$hy$`%A=UxI#g@b=;NRo47QKw`)+QybrOfJyi zC6ARv%i`fkTenD(vn_kUKV+*I_ibxN@r%)v_CdGHdXw>W+QC-+7o(2 z0MG&NJ|S^gM1@eY4*g@%wB_ZWYLw=iRlCUC-H~LAhc2j7Pd#CCi}ee?ym4xA!1|={ zBDMg^`vp!=e0<8LZ%s%*`w^P}t6OCe4gY~|4Ar=-FwBJo@xmKAF+1Z^MX1ZFX3r!G6mHpP?@uOEn7U?WSAJ0ragTCsAhb}qyp8<#L=lX9=X2@@I5|0 z?XdA_20`QP>AxDk^8Y>X7UuuI1>f&fx$ylSu3-O%;rpD#7FWybi1>5;uPv_veE$Od zn}2~o(x!j(@PCT_T}$kO)VTELEmNbhJHzd*4t#=YEgOjN}eL|infz0rB>MF{j^i1TiXEB;d-y>(blQSpZ%Tcxcjs1J1)~)Zi;=!UueZ2aL;vXVUR-12)8L^pR zuV;O?p4yTR$xkFqzU8BXtz}1;2vBuidRha+kJSVSs}=lI`Wb6$Qly4Zln*6^3U|J_qA^(sAX`+0hCJo>d5-R#)mL%%j4i8{kC z8vpyR|KP&>IBw(fW5(gu_y4&5FFO6$9?SaA_qYJB_g(*g%um}^LYt2Ty4zX)22DMJ zY_6rYizpah($TXvkbovG6`jeIAIh`$X-rQZE!4iQrG&-Nv042o zb^dxBN?W11Xx{3CCWxkN9FKXyRNjcrr7hnSnpvst);)`mJuf3x!B{uSA4|%#~ z^b}{gLkOCeEkctR;%s1rbUon7PU1y<7SxO`^RoG7`*2SKHJIJ$k=YI1=|i(?(yE1^ zzi-wSarUkB_|m%V#%t>H7wU^t7gQ&M<|nDYPt?H`y6T|RztgS12l&m`%?|Uj1;agJ zR5iPXdxO62U`>~5iAkzy4>b%>yPiHWn-1tTTWP$~BkeLs+8w3wp(4A=lo?)x7N<8@ z(_TZN6rZZ^TtGSi_)FuL$^ai+!NEQPfI&?PFa#UYdQG3S1(G-Px`5Twhi8Xs+GQC45g@KG zLQTdnpi2d7^aVbBL5;qscsA7V1c6=b@U^9YQ!|PoqYt(5e7zw}9bjJ%4e7pKCP8o5 zB1kwE^j*cHb&5&OdFH|zD5*W{FGKBHYSA~j6R z=6qUbIK7X8uS-F_u9Kq|_kOU1$Cjg#67xrV+qKraN=cD#&3kH7kTDU(;inTt8pnj> zh~fd+e>YigUWf_vM5tY;EtkA87ydOL@DESnkcBZK?0YWMu#-H3UA+*&K6G$L^U&h9 zy|Idr*^%j^?fAQa+vnrz%8k_F{y}bf3}c$<--XD~Z%L#R-~Mp_p0L>w#t=*}riW5E z)MdWGhp|&hm9OOf&9}&FH2aB{YA#{amuk*@R)QjTw=D@OmD6faaL zXQgB1l*&$Ql$ttpsv=u8{C1({YRYMe2*33fY%Y9sZN&FtblzBaA>S%^fi@1jngm_} zYvV%T^%~!YxfK^olYhkN9mfB4a4~inpcP5X;tXy@W;3NYtdhL3WDHc(Ky~;PO3Fwm z0bILWxRwLg>=y;7a}-cjyr2!+ifjTkQ#oNU;K%;84g3r2D}Az)t8kqTK=@~@QB{(T-j zx2@6J9r1T*cd}Hmq#wwvaVv6TE`@29C|#7v`=Z8Ue_D*-F*MzyX|Ai{!w z+WP(C|FagW0K8#vT;1mMu5oRtJ8som4(AwpY&M2Ct#vY}BvVHB(YaQN*MHUZnajaN z@N0Qa3*iT@J#C%JS+7tJK6t<{PsfvW>+lYA+x>&)*0vN4aV)ssqB37k z=1vN>^+^iv*;}>5{Ukt6r0tWTstL0*Jvh4@lDf92=?(;tq)`Ii!ZxH~PFA$_VLwg{ z?~I=3G<+&el5MxjgWW9Wse-;#b|T}F1bH=I#I$T-ed^Y8-FmXDGMFr5<)m)=XAZ9h z>DK35Xq*Uh%j^~PD_-Y$KuB+5{?LLe%A&In$n98wh&aSq&U)VZos1$=9EmU=>B2b>`PSUm~6Z)f`}CuJ;@1ee;c9iRBF37avKy z7CGM$PVboy2I$PRO6WFfxH~W%sS!u13CuixF%xWRei!|a!BQ@E66mb8o^MZm*zC3b zZ2`1LXd|S0dCqW1wWpft7xeZ$*OoJ!`G1~z)HDQcKa)Hg0TvrF7o6K9MPLtwrdk$6 z=Gzp93P>U;nR|KF65r=HL{hpkM<{JujA6-Dz7E;jUqtpYyQM*e6vh3*n3QSi!k|Y4^dR@PmBcm?^tjrE*yJ!{ z*#deHNh14k!HRl?71Ky}utNF|VaO7-f7_)XgfT2yy<`H7+1Pva zUQ73;I^@IVQ6Dy|4};zmp&f(`tgc|k(u_A(85Lb-}k$R&bGo?4+Kp&gNg zXjUpG+5!xxhSS1m3HPxWa=0a*%0x@5wfI6@YLQA}#EF6=D~+K#(rokIJ5ixXL9;`f z;zfR2adn3++h90?QDaB4Z3$OU&ERl8TELB(NQm2~JR{b0i3@%dA z!3;{~yO|ExYz_M+#y-s8@`)+>UG#<4CJeE-fP7{6CPr@-mx}8dX=#}Fcl@If?X0p| zyC!ielI4)L&b#5Fqd7dIawDi0lRs*XdFs6!yIuJ0axnjV%oO_G+#FL2u=I z2vo6vzd{c7H_J$5Q3&K#SQF7uWxfOtXHs3_Jz8eXH6|9-hLM0OGFMYh=qW=$Lig>B zeN(_soA0^Ul|op29bw<*7=&+Gse8~2y1@s9 z#@;NWG4jjC_DqT>0nVGYyS0guja&uD*5iC)m|6dV3FaJ5%I(!Q_L$wbOW7-979 z6(j!QaQ`2(7aIM4Y&7?gnmte4BtN4*P^HPnpY22_zxkXhy@wNQvb)%0_$-)>oOfRy ztV1Qjd3#fhN)2U(1BpWaKd5@OO``XFNUnYJIK6t+?$vaf=+8P`dadrQpt&u3vC+S~ z(Hyp4&PR7=ZN)xwj^_02HMeISs%IIeXFE7yS9h2Lj-k5l{1oOmK4mZ zzTT34cQLad$MOCMbe;>RUsD+jEKG{v{u+s6w94>jSC5a)ZS=o+YmHQZTp%p4*WzNF zh;P5qo0UtCq@?|g+}p>^hFh(l-1QrdRUtyCpf21z`T|H_ZL>|q@zQ&$k-)>+<3voe zQ4#4VWd4@BdPzKN5+HzJfxoB~{|Uo?D1yAb0=FG}+Yn1Ak&bAOzuKrh-b28Vh*P_A zS*|9*NWV_)bd0?iWm6IFOA+rt#6M8ffmsKQn*CD9yvJ@td%T_cx(x6BM%iw5a0;>- zXi*=3c#FxMI-_Xo!*AbFH#Ax+YjB5xsM;>oU4Sn zF&KZde8W^RDFp~c%s%rdP!_;zk8fvUK+K4@H{$PYoRuO2qQUQPDdlZcBjIVkkNDdGSETOk z+b&k&F!Jy=a@&?A%g#oQWF{lt7s7sah`kYU>agLxCF0*=!#gr<|C*jgwTDF*?F{<} z*@rlSs3>Q#)__ym`j2yASbFe$RtrCGYNFprXEfPQA|9y-Ed<(%;DHJI3dlCF+WOQ7 zMIwM&Pa)DFd)byv!fNun$Lp?jO?`@`jp0w9Ad1X=6c=`5UKlaoYBa0Huq9D3!CXMv zFV_a(3FC~K9-zq62}a_<+8(|xa3jULgmD<@z4a}BEbtwdhlz5kz^ZtHy4b9W@z%E# zDXGXS;#5s7?@DD_oAlQ3y%bx&TwN9{jwIo8P1XnIDmXjx;i&H&1=*rT-`3dASvx+qOPyL< z6fsP1zOy-yzq0m^<3Ag>X9dGd}_+g`1$Q%1{*@O8$J(-o&It|IdzbZdsWj}27p zDzMvn1w+R4Nqf{jb~eS!ZQGjS@)cWB@KjL5YSZ zld9LTe>g_m9R&ufE~)|Y^bxUg~%>k$O{0`9;m|& z&YYd-3@4HxsI{6Cb&@7*?qoT}E(pU6Dpjk9AU407F}wq%l*8|K2kQ>&=5RI;?jLG| z#Mp21#Fc6X7MncB+ii0^8dY6{UDCw}QjM=GSofB09>gcP5{CmK3>k#zmo9ss0|H$N z@GmLS=Ilrbe&;(7G>75g^bR4a-guT$Y9|)<;f%ez5^qn88PFrP`bQ#(@2Mb2XjQ9q zQ<%<6k;IQ{oB1SUz8Udi@7Rrlb`Za4I1oHb8izzGjz<-6pdEblMx+K3o;eO_loB0{ z-aUqQ!07x zIZ|`C^u^ybwq|>yQOzKC@zWW>w>$vpqoYG%TJ!caCd(Ez`ZpOU2f2Ys*p%#l!qa*i zwnV&rEPll?BYxcVr9acs5&t&fvi?6C)AHd@N>R((bgHp!Z^X1`Ky(pcslR9SIYt{j zZC8^lY72x>@HgBG)}qwgBkvJkPtHY&*4hEOfD0qX?-Z5@(QZ;ozC2}KAY*^3Z}4fD@-hREt$t*6vy z4l#Qli8y5b1~FYv-dQ5V0num!ed--SQ&fHwQAotsHBgEtq^FX_KOqqVLc`JL_Cq_S zSU;w*P|a`55p@+JOHQxr6B_iIglHA=Y)j&nqDyjJB;P{~SfFV%I`sBmzk~#qe*Lsa za{l^*Msq$C8Ua%%bmBF{zHCyqbW3XiL2Ij=!4RK9&~*iW(86ge*MQNW8te zLyhR#mBcAKDBC4ryX)A>Bx&)8*4T}17%U#;-MU*GdD-P-R!5s!#gAXZVVNXX^bNyzlDI-jTp&E zYy%i@T594$Y7q(pJo>13XQNWX30?EzV-PV@kkghBi4F-3ZRUOK5_HMX|D1m9@-vI&r}x&c z9mZlI0%VaM&g~q3ZG8;-wUdN-JSCbgF@@SB(Kdi$)+{Lh(n=Q1)EBEwD27se&thBm zVM`^5ZqeH)t5tR>#h?;oMN-`&udGBEgQGl~Wf1S8+7r_pqP$iRybBYPNP0**cra0z zcr~Cy^B2c?Vl%<_vxteUp$zq{5h^!?tl+OGf!dV*f!bC)zqyW`4e1Srzi|ngAw?focrM z7@-7igMR0^ioA$)GX`XAY_oZgiCjv*wASzPKzRpj)uX{yUktWW1Z?%kfDNUhf*bZR zC%`DO3L)cQW7rPp$6{Rp@hpR&Pn90e>T0qk0bjUux2+22D8*WN0qg=ycZhPqsht8) zI*N+A=2THU0((Wf4O?pplp}3~V*~2x%)CY*k3JiNfxsK1Ky;c*={*5bTQVhB@yuS9 zKTXJCLnGtsDYE8(uf*d*nFsXS8~U`$bTOhzB)!oJJ;6)`LP1ZpPcjxxamNFoA5syK z@I+}jbXvgUxn-KGzq^4QK=jk0d41VyBnkF(^Gp;>gb9<=oWh5z?RcUU9Z#XJD`Hss z*;@q8oZtM_c~r?}AykL9t6Lx55o*Qg7VPG{Lpn2CzpxwomoRjNw&aP#)U1V27c)ex zhg-2B%az4;b1Jn+aqp~Qy?ISJp<;&9g6gqp!|?0167i6V11&>R<8Zr^)n1&nOR_M> zh#_;&Y($r|H5;dg_TYtFv-CP7BlFfA52tPg-pbV&%^)o`5>My9*_c>{c3@q_qN=SQ z5Qt#FQKw*Z*Sb0)+F1H8sk4Lg28gIiH)Bn*GNUyF~X5 zQ1z11*eo&b)=w(Y*DWa>+qj)oCRy*%4^T^F9A*{s(x8}^+!Sr87@k}M(_jT;s!C~a zwe^$gn==)q-P*=2djC+}A%wRTzz3ozJ#iW;0IEY7f~1X9p^XvMsN>pxX1do3Apl*HZs`lLk*;}msc`O`oZTzIp4$Rx{Aj(Ao`eDm zXSOH8{~Xfik{rEdw;+P!-!a>n(DU(|j7}${vWVOHCI2H%bg&saRk&z=Zf&B&MlCv3 z_Z^Ath7aby=wPa@6@SdhHb-#EN+2AywmS;z&Q8&mb{wM@TC6{e)QSdn8Yssop?zje^seAPz^I*DV+1bLxfwm>4Q@-Hn(p|*$~I2AB2 zgZ8}anJsb_BaW$X9?Oa2jN7*}A(LcjGa+hAfcyXopQVJ@Gr~|p5n@k8^ESyfwe4R^ zhw0`ZyT=sZ?{Kv}xwUF%P$*aM1Qf7-QYicc_uIdba{|kr^NL`J&KTV#JbDnxPfsSA zQMNc$`;3*n_^Uu1=_8;Nq4-n>e)jsgR5+(2L2Dp^H9zbiho||6%ExE2I1p4Q#ulz& zP0B8Qem;w!8cr6b6THyW_Du9sY@zZ}-q6cPd5bua;fRvzPv8nW%DXM@p+E^c8yw9O zy;ypsqM z6U#r@461@M?Z@?m$`=q9VPya^YzrPbCuPi+nop!=J1RSETisJ3Mjydx!$_X{>bX-q z%Fa;GzUa?#NCujG-Ey4tJYaGna|Obqw7M|qH9Y;PW-E!ppa7eBBU+-y|km3cBqE%Ypp}k z|DwCsLmA-?Eho;NJ}&T0p_B75>WQpvpJ7@?@7}O~#LzCWB7PLvXz*T`h1*mxx#1`a zDO7hTS7MxkW0b@Jf;o@((;g9Qe;BTX4l#W;$N z-!9REgt{YK^Ssze5mar}9_sLR|oyI?Oye-%!a zEeK1~Xt&bgX_+-yz$>y(*mnRXDmqK^C4jxnIxj7^W@9iGo@&Txt$=8nmglij{N2T6 zajqf@1Q0F|g=pYCR-;Q9j)Z*Q#WZmfa1ON1VKAs!-Fu&hV?Bi%(P z>k%1>#SoY`f+#Di!Y_8Exj}!w+R4XGbC7$X<3Zx0H)EN5{na8B+fSLV5Nyj`(RG4& ze*n0?boM!9!p=Nm6nQH;H569pM!EP)EmQp&S!|*?ULM{^B3LRpsFNbei}bUnc#>au z1Ig?vx*8>7>y}3Uw!d}tnA`Zse1*01JVI~T0%!4HCaV)K+SuQ#wWInjYv)}$z~ndj zH~*~-@d=ImNi`BE-JD-JnV|VwJ$X((un$KCd|k<3mT;~eTiZl;QSFzd6cU;`=)+#jk)+vDyLmRSEJ^zi)9x; zfTJ^5ccQOTD-E`AeWnD>Ark!@s>G<0$JcxT5%*6Nc?V-LQFUQnvPk7qZUe@hw19ES z!qUvl3#Feb5D3U#AYa@L;#ZA5Pg~OYSJF~0s%t*eb|`&H@STtK4T9~u^4tSex&&1! ziivrz3mGUZdK(megtSxMuP^Dz{}hY?EoQJ+=zZ5(`P*FXsR>lf225|>RU+F0{p@Zm zCjGD5_J@D|JE&`VHsa9##WgCeW!#y7K21=H$Aur=IvhTPW~&tfmZ6oHBW z3L<+HEVCkFEROhhh{FGsuZOT@dT&|9S7jK-#`zk0>@17OFN_jfs-ty^ml%qz{1t zpD|cLWx=ZeQQ~irW|F5Kttnc8^7YfQ0_7432DY@D^;ac|fa*L`7#!YVrprBOv55)i zbWpZUc7G7uhU)FD6U%Lp>iDSL)AP^E{weHC2_p9x5*WrVGDY+o#W;YZgZG?BG2lS2 zg9CdOH`a719EhzD?)OofmX4ZxldF6p63qm3+#LTo9Hp~W_`j54qw8maA> zt@vjtimv~}kS`5yS4NeI%BXT~i!wKhX209)$*f$j)*zb#n5d{u5?qa^SI|u^K|?|l zg}JDvU8VWoq$*`Iw)6v2Jk9M>WN*9Sg8_WuMPpi*Hdgk?fRHqq7EbGNtYQ#!XI6T+ z1|T7FZ#dCZ;q1ob{;NL39B)IaLptDTRtFb7+vy`p{j@$jAS#7W>rNE=a35POheqqK zP}LcsG&ZR~#fTeMWyD_ciYqnU-j%u}o9pLaSFG>On_H=m(- z0_zqxU(h&>(G#lU3bvXM5LMHDb31;rfM_1Rv86IMNDjMvHmDv}o75gLL!<}i^8!?q z{!N%l75hYWk!Ta>r6+Gpv5{kw!^xVEWs?FWi(y>7PZlh84UT|GQIku95ko@Hs0`G> z087>?ip^@J9RBOK8_CJnR=Z~|CW)@I6r@XKNIZNX4=!cXKo4&iOh-ilH!aC4Nnja5*DYX{`a$}kd0Gu7XIB5Jh7?7#VtEUBN5?Gt`o>gBToDD1IgDU$VZ9$x|p2 zc525j2RC;%9NH9#y4fUNjBaEMTM(1^tdOx+Ud8Nz(|t0cyeusf6A{7mZ#S(In&lH~Y>d$P;5Wh?hbmU~U7fo$#`!!X*mqUKv)lJ$n>9i+~%UEx|-=X)OZ zaKCMA>x`BMe#y6id3$xO(p&gsbYqpynGb2K?B^xf(s?_NU);5byDfyN8&1quIzX_By>8^x;HwJpt3PY#nx

kB@G$`Czmv-#*DOx>Sk>Z)E4$q_75FWm?98WY)#^BC-@a6t8D z^Nq8)K76$wLK*=eKBT+0Sx10C-g-@lKNXRey0AhVrUz}Adl}Ai6Yr*Aflqn!0&1hW zMEHXWG!ti&cJg+@tjI`5F2QHOr+E%GWd-lTGHgW4?6u|K*y5~ErJCS^=kJZ-SXOWX z|6LJ24Cwm{$E0Fx7c8h3rpd@yoZYa=SlBd{1LTO@9-bQhQSq0Wbs3un-o@MqU{hBy zoB7gEs6GxhW(B=QCvUCL*`3?og{PLibdUf68L#68CK53$8QkVs2)RHj0?)kGCP*75 zks!5bf>dxd2-2A!fFMn)CMkygOM>NU9a#0=N$7^5a$meP>DBE)ihJp3Y^Hh*?8Y>B z0?#d~LSKE56fRfoNE?l{X%FQFKleW(csGL(IT6Y0a)i}Dut{tPLF8A`T85qEmddGc z+QCU;V^0T(SVjDHFM~rSKg|F;bQ&HX9r`@dp-Dh!xbYQq=fSa+n( z=Clk$du2Y38qW^h&ZER#h*Max#wcwKUavMXp~v*@2=IdEaz6E(o=?AsHAck-1~e#b z2%ewn+FF9 zqRMh)vAi)=_m5Dw`e~kOFExWkQofQw#7GsQo}^$P!+a-H;z^zm0s)Qs8`|r4J3T#H zsMQjg94v4J6zj8!CQEa+!+Y#yU$6Gz_0+sg@`u+0#bie>G;$U+jEgLIxe6bn)?zXa zC7w%??yfbVDBu>osb%U@AGu>_mdl(w%1S?;K5cw7XMei9jt>7P*?qfZHru~#*6%=@ zd_{R&sL1s$&JK>(f`FFaj=}#IliA3S&^qz|>d!_4JpkOLKgR8fn4ivL@qqIq?+VdM;6xqB43u}g2YmyIvx3)n)vN(|rC$aw@rGv&NSAH81m&k8 zW{UmAy>xw1cd^3sY}a+$5T3NkP;LRNZ{7x8(_KUKGAaUpo~rj(9Hua?g_fUxO`)Zg zH5^lDp$N^10-?o83_hF*JYfKa&n98)6$ME1CY+d;5?D5Cl=o(EIkJ#`!-a=iZZVr7 z@R0PP_h4S=MgPh$EH~BbxO^o24H=?IFGi1EZZAfoF6Clm$pv%pU_SZ|7NdXMWyu8= ze>@9Jfs&KZ%hj$`SPvX8NsJkNCVC+H%g6AcKs54#7vGZ&)LugYFr5;{RO8-up^ zo#XOwdW^qx)AVlP=6r7?+aF$2=*P*KpD_A#S%YKQr*{u; zKu&*~SM}ig6YTe>N#1vp_dQU)Fr)m?%<_fE;K|4k$jsnDhRGQjZqLjx*{dQ|=s5G7 zVzr`rE$q^C4QvLNT8jya$1l#^MIvmQSgb-s**%sQFHkElMJTb!TluKl&lHg_U` zey0wr2OnqgZ)y9FmDdMWp~38Zs8xLokAG#VR+?VVigNhdi&dKQg*=(gZxRWCAVaw9 z{H4Y>l z2ZF+KVEBiEro(EJ`Yo*~Y{)h9?UqcmTQat*mcVD)ZiyeKbE8hSg>6c24Ro+P2veHz zC*eG6@#TYa+VDRsGz&wpP=)~OIlg_p#BaC1KfzNXLlGL&)%ORdzAs<3Mna4)(M#N& zM`1BX;aB*sH?o>TfW1tM_@r9As;PRn3+SS8CcHPhw80Hdh*Jl799$nvcMOT%w~R+( zhfYMKXhOfll!p7ISSxm#pZpXtTP(|;Pd&vsk6-GKEG0Nv!;Zcpi=4n=K7Z0)M5>CN z3Cf$9sdrkY`8pFkSCdtH53OgYQFqM)()za%3sFas5>dsk9(#+p%XuGaO-yF>x($)Z z1j+Ms^?$%U@)G+Ib<+Gz9RO7yp;{iPd{%>-KQd!ESqVZkbX5>DPBr&DLM~BDtTDz& z#YjpVz;J!{3u(TZ2G)QLC!8Q@B~!*kWK#WA3~tm{(hiWA~p}1uy_d-U;sAkL1L+b?x};95imV4s9g?9=4s1v z$J3TSlm{uq?F~#|ra=vI+A=m6;S^f+EA$JZ$+eAi3Sbpet+u4%i5ULk&Bl z^0!j}+yQ~acL}K6Ck=UU_#EVb$HaIE$|XKWR88J^10u|1{{3&!vvrsJ$LN`m|E}p- z7nuCS(dpSRt4;qhJ!4BcOh-CB%S)S*|10#Y<49Qa?D+pH^lY@}tLRzLkG~N;`^O1F z&z>SJ$sl^^^sHWIszD~3R3_;e39gU}SPd!C{|of&hr0SRGU?eSo-~q9&t#Ijpl5v_ zr2M4mS$sx{m^n2ugB1tF489DSm?21|P0S)`#0(-{SH!H6#HijYi#fnAN`- z;g>p&j747rFfvC)_@&3$TZ>>Y6$^4Gse zaX##;xwuSf#SHAStRFfo8%Bk?X)BbVCM_X9)8RkPXB}?Mvyhsi*5N2gZAv0kB~V4f3}KtJ?5C$6Z^EAGC0JvV|4@p%(VfU@9_O$o9R455xUS z1V)@L=T(hqC=XTnSRbUFDr72CaPx*k{$}&Q1%mBo&__ZrN=r&*=pV+uP&-7@OS$QX zM0`CR-b3p&KJGwW@Cgq81NgWb_v6$HN3<}L2u(+rN=lgsj4blKv`P~PEz8`3z2=ao zdSg&C@`w(=lDWJ(PYiWuJ_yfO>wWpbt`lFmYX}vAe^9nfWCPyN~jv{50GtSLdVN5EjhP=^0=}J&p}ljd^<$ zv3$@9Ai7>qseh@GSRwjQhr5v5dvJCeezHST(d5Pd*(UexvdO_a?AAOjGY-c@3n*~{ zRdy!ASIXKh*e)&ydqxPhpKNF>r}4KCsP0{knFb=iV(ykRr2r4gPNNNjyP^$x@HK7lv+4(>XcD8Jzmse9 z`=av(-j;(Kj&P}#2^R&&D6`KToXm3rhQ!ipSc1U!sX+9sqS2diC|MFcS6i?jc#wCy zNoof)_7y|HDyk;9DJM3Cn_S_@&yLT$AqpN^P4x$9PG})#;|0wZ8OV87CURyBy~fTzcSqfn*^yK0Aa|N41T(PHR(b#QS>maM zozuhE|HE}Lg`k$&F(6;D>{S{sQ4V-1hh@*z_(l$sc=}jT5>MT=ze9Bvu9n=pK1LlY zPAE>P6^%iyMw-U42}YNEqZfkMHBp7w@Btrm25J|XpkLuYA-t*Htre9h_JIp)CvpRA z@w7Yt4C*lPxSS23<`QXsRmG*;-)jq=Tt=_0{z!q)3z9|9i&Wf^W31<(7l*NC+w{UF z7S9MnbyQ-}q?hn2d<;uC{DLwG#yXrVWT@x$8V<9@!Qhpa{$4MyUyELJVxF-h1Pi%n z{R|vigmv{9ZVs#cE4yqjX-P8*W5LqRlMFXwDJjzO_Fkbk4e1!*KB)#Tvx z^wu1kY-BF!TTJ)aHraT9CXUWa`f)f5kc@B@`juMJ(?%(MN#BZ1-?7h`Q&SDQU}{MR zr(cct^h*KNSaL|S`v*f<(jU<5ep=P(OZp5}y-12apozu~bI%^P{YEV5kofMf zd2=BeBh2r^$PvHIrWiIq?w9xmdc6~ONiOAQZ356Y!p8=L1LCvE*2@<)<=A50R0zuP z5<$Ze8UQQq&(|ddYsl1%E(9wdacq2rMK)#(Ood?&sJx=s1!gj)6yKW%l8<5!?#Qph zR9RkubL3#J6n;Q!0RjL+IRvh8FWa>^!ozOK1H}vvg|iy@&_F) zmznHzXUiq|gOuW{qvcX~_$Mfa_5le5)7-&wImT+4da)S|Vsk(cs?bC%O`2cnsJ+N+ zPgnL!2{qo8{ZbkOeLs!@Y{3^aBLL8?#{-6Bp$rN3m!K6_&$qu1`{nb;R33ET2k0)H zq<9J1Yqd1)H46SX-ACTVe0^sHq=Y?IN3@-+EB|BP<{!h633~{Vsa=~qq+lp+bsmTHWQyanlCf)=i6PF zFE##jFkcGr+UCneXkJG_=YPvyS@2!bvj2$xMVeBeqyNP>vsYH}gb}pq|Jy6Sj{n84 z26Z4lH>cYYg5@dV^ELc0zWce5kH3I?9NmPFuQNr2LTPnG(q8!}VA-};{*iD^SMhJ= zfALoYd;|$QizF=FF$d?5YBHA3BYVD%>GG->Dbpns3-ReGGIC7o<=ttvgrit5CsE^A zh)!D8%lBcu{E&tOo0OCZDM^|Tc+j8Lv31~ZGu@febZq}QqvijWy>cA`h5u*hNtzVY zk)C`rd*v{zP5<9s$rucxTK|Uj%5zTbKx4q*r0kXX{uGTlw!L!OL85nRy*q}z@(+?d z_G?gsu9>Fa+7t(@=i+~j;`~5=gT9zuQ=H%3ltyvB%5?dUpQV{D#e^8Y(~F<@e|kFYS1(8$9i3D32a;J-EL>B#?Q zk{*lyC+7V}t(V$gw}X7<$<)`{`WNvfJ(_TqG@(?Xi8P^fP`{K7CHb*TDBx-m!GA?x zo5sm#eXW*1G7Kqc`j;2A8O0K}YS6Njyw%Y^C6ixFBlVVNM*8>C%_%8)=PS%9cm6Y; zhz-K*K{8BVIa|!LaST&e`j;1N{R@t0NQ!73R;O>$a zA}*$#^)K^$UFl!aY@+G~S_jwQ26t!W1pfN?OHO6MzZ2yf$AH~R$hOzWbq1hYjn8iN$;SHXq z6@f-1O=F6^cAL$=pOAsE;uKCuwTz%U%{Q@uhpIy=?Nb`lP8h3)>2YjikI`4ZYYu=$ z1@FNoC`}logd4R=#;YX<|NV%UIWx?T($ z@RIIj<x=qU~DDir=Yr_Up;5cm=Hsq;;VME-j2bo4M?n!|~1Afz5&^hy(y*NM6 zQr+ILT-$qavi-AiENSz_(Si%i@U5U5U@1EIm|w_Y@QB6dr4o+u7CBNhpw@$Il)~bJ z+2BMiL6H3pbmEmHo$yD7f{i$aC06?=eI0u7ulP&g&~5ErpJ8eda&a|w&49_#Xq~rwZ$wS)b;xQTYG(f zgX?=&(&sm8a;AO7ZgMb`-kM{$Oo&kdB%`8(ZT7!$=neOgbnu(-Da4C;#e8~HXL^w_ zPU$QB9oT0cqd!wu^dfklujU)!19$W`MvRntp zbZ{LkF*g^Y_cVt*B14nrI9Soa{_wTzA)v$>GkMTyradGR0ENMPg7%O+q23+1-V3Z| z$+o4W>)?I5jmyP8L8kpi@mJRNk$<;+;@O#LqmcG-ol7AJ^g4ioj3v}vLsL?Q^PspB zJ?AE{JuPB8(4(_k5y==auq(5}%miP$c|{SLQY2E3?l z>O6m99_!QILrvNpu^NeRL6p@MgBL2!^kmR|#L%Lown*%Gj~|R*pXxm<67~vLc3F}$ z){aJc>a|6pN^0S*s!`_Lo8URP)bbqc<8OG6K7HrY5deO?Ibu(&WA?AI|L1q@I!muT z>}PyEX(NPLn#^q1`x!TDc6L2*0V@-m=u^w-qit?DnKW3N1%JV|sMS-FFovy+Hh`_p z*vgnGU0DV}Jqogw5eR_865%F_8Xe$fGZ&2%0$2vYrdqMQ%I7zm=UYuutDDghOF*q+ zO?Go8R$0xl_A_QS$I=H#b5xXrVjVr4VGp$Y5~MAE0>|a9c^O}@JcFQ>VOSju#zGkk zMw|oArKC+?$&NBmf(CAJmYuN%}o)ab(C@2dWcJk5T;fr4OY8KVIBGWWxub6K^)kJBk z3L|5IPuyiEBvDN-QQC9pOWWml2s{I|4?w4`X?&Z8eeF=6zr?EbI0(K)#RB;1ge1NN zB0qr_fF9ZF#RACI*lt88-AF~&(j`Sq`1eh z+xYoa^5{`M)RnWncE?Qv9nIZnh`#`FAY)5V8}&ySgk;|Uo3_z^XLcJJEdD#IZT>PH zqyLR2XKuL>iUO86cxQTRmJ}_*=f(FP;pp~vkVJn9e581QC{pS880cXOq=Jv)QUDFM zEs(xyUi_VOloSAnpOrxyG>552syD(w#x#-FFk`vv2Y+HKjgkoa?XD13kGe+K21G}U zdy&g_^BczfRkjoE_1XzHWaFx8MvRqn&AHctl}w^#-K<>9+0n5D`q%JYaJtmSaLa*t z@>Ur5!|O_t+hmDPE{Ta}s1Md9i zbFCz+HWcKE@Qx~^J;O8@0&#lSoRdJ9`PVdO0Pa{V+EJKxQb0x(Tro*s`>?n%~u}>Db6qblWo;*^_Ba@&#$1d_~$PEtf`C@1YwN zc1D9&0ClX#=_C-@v5RJtwtJjGgDt`+ouXA5_M=->FG9nt$58)D_Z_*vfpep~kL>Qk zk9agfPv%uM0~Vf^%o;qM36G^E^kKrHR6^)PRyH1$dEI?=13p%0Kj>l)>S6;>tXLT(_DdEUm3%-kkNu#FU8jqcpqQbIN%l(? zyDs^FVw3F$UF-~9Y!Zr1QN~RBW$6 zv9lnEW8Guv&!}Qi+OwYW#ZpBT3!;|A_)&Gs({izv7t)_mOMTk2uH`0O3k#x_X8ahr zjx4aNVH3PoKZ%%v4YR~H!ffp0Pug)1BmCunj4*7(e($H(oDIncenMdOB2Eney4j0> zF91W|$Ql=yz)<(d0=e-6%F9O9^dKmPT;9k6xig~GZvwz%BcE@ir#|G2OsvOE8*kct zBORgaNIsqm`CNz_4{toUnarEXzL8Z`Xn&|%WIliKxX60sukMzCka(frOFWVR2-Dhe zy@yg-$V0g@eQ;RfsNg7Y^|1;Ul1QjFZ}{ErIQ&%}z15#_2!dnOgCrr|=;*|u*6?SE zP@ZZYv^k=f7#}o9Q<;s!H`g{JdX@H{bJo{3hdvwB8j+F_$%3Q2>oG`yD7HS^9#jDY zpY!R zh)x0P89&)jZu7JA5YJDiksGlxBE~%f;}NFHAt_^ku$Qk9idBLa7(XNw)6H6_{v+k> z2$iolF$Gj>fM>&l1&Z}FqR)|X0s=Oa57}Nm<; zH&B~_B&+WH@%ipPa|bTQ{Ww*#iF~T{`GXAzCttID;bqq8spBIHksuePBLEDio+*}wL zlM}ziT%wEkG03(98>VS8avRQxQz4U<3ZK_KFGIkGRH-VQ{oXO0G@HoW7~_*d?Pc!z+qt@FJj z^MfZPnt{X!6!k&Y&p&K1PaT1nqRH4t2xeYhv7y|#2`~I=>xisCH0Q9?)+bID&6_^^gPP7#ogzAovJ6w-RBoNb%CD4K95=RH+L;(Yn;*(H7+t&CJgcL zBwd=LS%#zaZ`S`S{R{6cMxak$KyCFRrj$h`548eCc6kw93c-FUPXwI1)S5H0udr76%H+XFKq=WCKx9<#iW3o+ zxg?CR-T?7QaK1FuvkQS)utVU`ws2c_V&& z(y&@2@thlf#SJ)ckvXvs8I%dx`OZ)!-lj1_k-=dWy}mEuSL4x|O{-wK0I`R--HHYk z8p<@U9C|nrd*bWW^!jUGRZ|{n@V_F(DWBfNM^@O(qf z=6UIrI5$+yK7czTED1O^)`eT9K!u>+M2b@>AIcuTPL1xB(dgi`qc`}kyE+b3dV}9N zi{~bN7(Wk(aD+c|&UiTdSq_3h>5=*Nb}R<1v@_j4V;{2Nlu0y^qhu&34goqODHDy- zzc4LS$T-5@;#T<9|+UVUxDFELi=VWui#BC!T1ngLSU0xHG` z`fmnoG+D$+gowh?0V~MiRSs&Xgo;bzmhKl*8Cd6+%-I4m(jU2dnAXCyd5|E8Yp!S@ZDj^<-okHqvo|3_h>D<m=^=c8$h$&-7bFu$in}oTNG9I; zlbBqBTft=OjS(sCl!BuG0TtzHwgaPTzZC|r97mc?uiBRo42n;;vPgE~6G*xzneSrc zvs(pYJ?IJ=^slr=PR_BL0rhZXOo1pI5$}BistvzTw1@*3yx2gXG)_Fnmnm!t!uDn3 zR)!kGQfSTPe!!D8u%XkEG=X&F^8W&hejK0>8xYYaGr08tpyDyTQ=x!(!(j(c&< zv##y`vzhdX0Ca_xlyw%oJ&&i z^@4UEp_{3sB<2w(QcE68@-^!j*h zH7rp2JR9JyDFA+W8}{~C?qYKxm7##o?Q0vp&76R*b6=WN0-Q2`x8*t?hTrL+PVacJ z1WIJPwXjw00a^bPm#ao!6Y*JLj%?oD}CXm!ME)+-Z2e=pl5M{X`B z9GfMJC^2tBN3k|}!@Il75(`~IO%p*l0LrK4wBn#bV(u_7l3SSUUBUI5hr*W_DxX6D zFE0qez>}Tbz@^|tq51c__a&Of5Cl*M0+lm#@4(ki!^3q5Ito{nnczVurfC6F@!_eI z?0PAcR%12LOX(K9luA>{T|j;&kvBiyvM*6dGK{4jbQ4hF4lhMRWI>DOPM;}@X8^J~ z1#dZW_!U$YP=D3ysPM57i;vX{=Ual&ab&li1)bl}&s<1B*lXVEVj-Z%A{#-_Y_6i4&6T&a4sdK( z7Kp(Z(}2>0YtUxbcHO~QWuNSQjpZpY5 zbpEnA1yENPICfgsNp(2;*t%-)o5Z(F{?8*=7JWfN190&G69}m(dxk6?B!!fvEQS#D zkY`Rlp>2ubFS=2ta{I8aEkmH1I)0|Oe6Ul!2K=4UTs8GaZ2 z&PaWw9;us=n#$PFbReO2n%79P9=6tGSzZC4ea*hu&>>amm3gyy-Yd*cU^x>-1b?CI z+H4Mn1PkCRfF{oo|9PtZAEJI;f!VmqF1q(e=|%U$B|%sD3epYLxfOH4fwFS0ZSj*b zy7bpqNi3uZJ4l#8=<1N`tJZyMh)+PIWebi&eKTZTa~8-H`PeGd1R9hP#3I$T*^SAr zeQUejwLTeLJ04w2j>`X-IVT~&J$MGsFA5x&;i|;-EUtm7!10cCmCwZ)=(yg%^%1U1 zq6Lm0VUGWT-=X*=eM_y!JSk_GW*|OOm*(Mf!oRnHx}^!Ykew>SZ6%aftB^rVP;TFW zhIlsw3yo+?rFp_W?#~;W=MC3qV{M-4!?89`F&K!u_AyvUX)wo$E&W=j#uK$~1fn>` zoceu70CW+j5rDof)cm(4`jXQSxwrM>zm%CDdl{ zvG?)lA2|f*2jN(D@OwA}C)ez%gi01VWql>2jdcjVL2;EQetI>UF;IrCt#& ztWq!W)!@`C+}W?5;H%?OuYl|Js|a8H6A3t!Rs=LIn=R(Hr)A)9qrU&0R4tKE;}U_ySP(3&+5?5!N|eJInlVZ_L`$LRgU$*`;=IF;Hg7c3OiSXFGY|1&C z%&V;oWIM~$6*y*kVwShzgQTu`n;7K6d!TEEjgGqJW+-~~2?l;!(K|T!?J`8>M&%n{ z(8z?UsUePfvVl$JCpT&0e-15pcm*Z&U{OLZEy%a?v|4!}waJ`{Zf;9bW%3~~#5`$% zmgU(y$MRG0Pe&8-?c(y_7<66H%;DRx|FsR2*}{2q<~q_`usvLQkskg$dqyuT|3}Pu z;J4x@OwZSBoNve`^RW#`$u*w@PqvbfO9Eelc0pW2kRmPY2Ot@~vg`oV7Te8PYeao< z00?O3H`D+#R~OM@9qM3_taZAGKUD+^y`}r`mu?AtXC<7qpa9A5qyTZ*dJXH1(S(dx z5Wc_iw69wNYL!ftP9Tz0@_=jR%25md)rCMoQh*Ej?_>g+-}VK?_*u%1N7iGI&Pz-8 z9@=WXx_t#+^-p_crRPAB7FrqY7r71UdabfBDnKfstT>QHD9aTkD8XUGu@a>P^-1pt zdejb^-P;k{fvE=mVnPIkAq_9Ev=x2L_)Gjyl=%W`q@cV;byh}HM`ieg)NdYHN*vLZ zGJcj+n*2P-v{mf@Or*Irm6;P-V$O$N5hM}fxSnV8?dL$L;2@4KgT5xevM^Op^5Pwe z+XN`p^kHbaYzUL$u`6NYOA&&&5Q0}ZO$US5h4*I#M~082$bb)R5w(T+iyH;nV@r@V zNd{WX=~gnh8V8PJBc>La(DDNIF>Z|Q9UXHuhVb$wlN@X3O0P%dHd?vn+#xY?nBnyAPK^mK;s3|EgT}^@>LKNIB@aofQc=O#T zN0sYvm?wkSve??R1)Ox^FbO>(s@D9Tg+p3-XkPaM`4SND zHnTx#!4v&QA`J!{4-CjIvdd5^ZdnOoz!Az_Nw&j0UA_!0qiAxWdxZ?T%Y5M(y~y9Z zavy_OV=zBk#V40xC7rIh}+B%+XrT-eNyM9eX>%I)2LJtDr6zh)I!xi^ z)IImHE3sc=!a6h=J;q$S=)P&aAqkzqe}hOudzCTZ_zp~6d>W2=g>6FARa?yG$!6VX zjns+@&lUOSOxNt+uvWA-H8h64tP^inaZ)Sm6$%HEs}4oZtKX zoOAEoJ2RP}AHVXl=%X6Od><2vN-z}JC-3>gL@Ubc4V9cnFZMpiwLu(+BPqIDb?U}c7y68NQ0CIUj?qvJSJ5Xnn5s3 z$C6<>g#UQqsU03b)~O^mrEg;Y*Ll_?2EEShk?1W!fv`Q;yuLZ`4>(EA0yxdn>CP;H zRHsMB{>n$k&;XS>q$z(xx2uDYzm2|p$j!vlbjiA0t1HNb?_s=Gaqa#(K zSn?bFS~xmIr7!rGMWtDwRu!-~y6;V*g`@l8Pl`l$%h3TM3GzS<)Kb@DCPL-UD~`^X zsM$GCTywvA(xdE}rfKt^e6+~Zif22X!pDj{oABKIEA=eL&oRF)^31^VAfDIp9RDf#TokHaoOKI>7Vf0-jFDb ze4VU0%6tZYi5R{??nhqL{9&Ov5AOzUkhhl(LSA|g!tO@X>7r(T+6rEpKq4e(_tnpF zAm>(us?UX?dp_AuP>b-n0b{m_o#PY2Nlh~T24x2=GzLMvF;q z@+~?8ZL39WQ7SdZfT6+EBh>?^hmtj5H5I8hE`LI6eI*puM^z*G#1;N0pqg^`;3{8p zypL@**<27l%tyLi=mX*QHh}SlU+-;siT-b6@jQwAGGPZ+n~4iyfdtopmzb{(+pz-| zjfe)D%0q76o9@K&Dnnb%{)m`XauD@3@7JQeVo{NsPoXuzhZW|!r-={rV#4H2;^a-G zS!}v!ai_GJv-|LhQ8E8cyu@eS7E~HAW?)!!4(gKVviIO$*Sx)tpX|&_M5@S^g77f7 zf`*GDyD_k(kT_yL$+Ps`B|SDekr2Fl+HmqX{lZLSoTWDR3#t2ZdNj@(Ft>^0~+ z)b1N;y$ND2#$8L0vgEKRRxZ=~qxJ(PI_(i+x31`4-ics?3qP z3`7rcGJ$(&u>4Iq>GpS8kIiATur+SCB@qZbz&2RsHIA2<@BNudL|MPZ^nrpkShz|k zD`gdf5o;Cm@T;t%{sOK%^19E__TIbf zXtQw9fmLP+y5Xb)9X1_!mvlfx%@iGQizZH~M4!SVEIA;Exi}}ILo?Dk1ZTqZ4$T$X z)fvynC+&XwI&8q%iGYmq;pY_*=rBJxk3^u;K?FL?HvP`lKP)%DTw`&zl*OLY1yV#{ z^8bkl9PoP*fyttO&_n>NY=wei5drES+(ZCn4%KBW{X+nrW;l8$1^Ccf+vFG+Z1WZU(_`hw|z_;G_A5>6C|{jZ+weJg9J*C zq3Puvs+Y&N-U6KSE?lYXaz-<+@tO*)&LHI+M%puzR@z&^IfxIls1fQ{E4F?Z91i-? zw`)wv%bK1A z#5@5yFB+$K5}PCSiVGe8n6IiQm#Ej4=d)HWj%@R*P=m%7hznJqwdtV-P~Dk{ue*|Q zio-oOG&5&IzePi-NCPtag-<|wr9o4h$Anq=2U*Zd?&BfVshy>~^~cEA{D?GAy%6!r z>i@0G21EMHwA`>RxBZ!#7aHk+sv6-F6xri+S^I1hByq)e1IvRJ*Rp?i66?r&vrs|FkeAi;y#F^zK_uP zk!E~j^-X=vpMjLXWcgeCz#dsL4co$Z>XHS0Yv9)+%Q<3L?^>fajQ}jUX_lel=;4p+AKp!?h zplD9?^SybR>||vcVepR~E}@mM*qL*=Get}lSrOMgh6gM@74={TP4t4)$}(hm)FGHs zZM`e;CQ5$PMc}0#8%u>MXV)Tl5U3FE4fPA!1fV<|vLfM|`(Dq#+x1BcN^Qk_P+XwL z-(;aZGj3wBxtu>xYW2&e)}SM4JqE2& zZ*0kV*;t|dTw{g5>~4Uni3-M5R#3qwQes<^L;=Q%+d9c>*Yt(3b;4&*P466UDW7LO ztA8V@n2A3yIYJ5{X&BU7Y8r@yQ!YLso4R@5#f2Cx3UVr}UIz@Qg*(h*)n~TmR4S)8 zIx1DI+r!GGQ9kT<@W?LTS#w6P{J-H}=9<}IsitKhwO9b%(oPEgFWv%YBqW8D|I`=iMKgFze>;C)r zS2$1}K|2chY!P*(IW@}Vx7~kha=Y^9JAtJ8{+96Z)$MA+zv{xyMnzTo z1q?$J01?Zj3A@sAVA3v45DD~~@|`4TQW6AhBz|?*O_j9Vp|D@9*8ISBVFi#6x)Tf! zxiaV~&|saWJ7};@H7IYCtyJEio9YXIMyxlY8iZC5M;Bx^v_~~zL~96J8&APIU)@#? zUv_(1UmZPDUucF^ef8CSTPqVa5G0jM!D8kF(eqA@=d>Sb8y2ENU_S>=Z7|n7sBBob z{#p^)73MzAu$0VJBhNlD4Zb_gN7XB;xcU=>bD4&&zXYQrc~7r?X|9Eo7+;7y3OVpa z=_g&p1+sXhaW#1ET?jx?WJcT*&A+s2w!0H-H#lyjDy@LI8`S&2CGa$9-db2Wu#+}k zj`x}~WK7jSsukv$Pjf375^@nMUbX=<&j5dlsd&qU#dd&C` zyuycQzxbzn&;uFcL)LFZxJlm+N(!|sgp;Q4Vbj78py@rMGKcxA`_wRfkAISk_eW_p z+3QgBdyF-+7c`j1d)e*Y{zJ73O!mZIM2Z3JO0dE_;)iOQ27~y8Y}%T*B_qGrY;?Es zU*WnXeT+$woX10@;UB~`S!JFCnMzkXH`ePI%aEl{!PtenG5d;@go1?!l|_OinoP6} zl&dK7rFew+2B=D6i;6ru@m$kbvN&)0Y^dA-O}kLO=_PJ07>Yj_^R z`viWv&I8G(HO4w$8PK!={Piv0@(W;H+4R0zt0C?tcz67!Y!Wr*P;){XI6CKU* z!O$*8Hr*AS_(5`6)W0*b3o#>8aR%V5VeH(@>I#&!O%I5^tz{Gu5>si>T$`YPckn5Jbps1;*w? z0mA;^%w=rljC|cIe0b#R-rk0-cx7AyHUKlX@O3qFa8^z9@ilhg;INER z9p>>-D5I+{C{c46FeYxpH%bSU(O;YZxw(||#C$c;m5d|`!UKKf|MoU~4r^bcNV)K( zTH16s3ELwao-_gHpZ(0sAL7fu!#)gZ_cL@#&y+HcVXpA?9sKC)k6n;O$g3TMO*IhZO9zMqcBeX*Ocw&;U)M80G$SVvkA zx%`+L>=oEm-!*@I2YJ%2pbEzJ*54~d##I0sJYLjrHPdTu5os?%&m8fFLEDAih5q+s zuqmhIJ09o+GVP9!&4e~04H_+oT!w7W!YOF+uYhpcjPR0}5&n%AjIqAbM-QGeee~Wr zoI~T7&Vw#eg*D7Sm4|Itd=Y8F#GQ03oTXnq%U60g9M@>s6ItSNT_S=KA!FjkO4tJZ z#+XX+evVDZg9o(oe5N0CPu|b_hT$u-+-x(HP0{Hqqbvs!?JzbUfN5pih*VnWFhg5) znEJ?2`P)=pc^mG-acgU&;={nvGEL#DQb&76Lg`k})g5}G@ z<)C50lF1oDWvog7gV!RJ1p}NR-S=w|0SQsD>^>Lp@f6J^(vI>K>A+Yxx<|V-gAicq*BP)psIG(d75yV$xO# z<<#B@8Y|5!ad6{guv2Yp0?v?9N_W`_=w#Vz5>e!X{#oR^Om<^A53%#As@vF(CC39Y&t^z0w3zvdF!X)r>%Y`@!=2-#}McQ zt242@tZ;L~d_qN;HNaGWH;=uS;f4MW#J)sCeZ3YFLid!#o>CybuVF*Dn!^toOF8Wr z6z3WF62`5te>D75A`zKw_QR?W$h``~D`a0TWB+mqk}vFLLz6K1cD zKnxF+!2XRqlXlI+Y#48+}z%7XR;GM-48TTzA%7!vCuUo9>Ij`->233| zpQ#Vj3LioHQjCGC1QlGsn2c7TjBB+Pi0ho(+?C{Xg!^I^YKB`Jg*wDYweFR+{Tt;C z#ti;wKeIo^E3v*E=C|`WOI068%$wiXli0`iHpb>b|K7$50N2Fs0%scmWM{eD`0>M(zTQbNM$P%(<+ogbrNmLiV(wTC^&e*e}Jnys(V# z{K!0jCpsB^MbZ^ABZ@6DFNNI1ZYPSo3zsv~r8xj7jQec_Uy%rroGD#=p6r+jW=ht2 z@FRIyEGq8M$e zseT`})anAITdr6rI?Fj&jNNNK&x&6te9{YlP0QX?`~@pQmFR$Itk|21r?X<6tK#Qi zYKc}V&q;S({RVow#QCWKp0HQ7ma^79DYTgO0MV)D1HbX#X_cdD-U|=Zv@aI@BF-&^?=c(X*W9petI2#-_LtB=7wBb+><;1pp8-8X+ZnG{UkgD7Ym zJPt4Qem)7!gH&t`7>}+2;uGnxyc_QSA{m+WfhPUwiI{?av*cca@SAWh%;7Eww8 zy^6GaOCA5;WIIG`=NvpU@H~uXBc7@j z^*n~3M=UAw+=*u%o_FvZ`c9GOas71S=U2<%gR#8GGaAp0c%H|z8qZgFPFev!XFSi~ zc^A(h^n-ozdIBQTY`?%^Tr{tm@|od7TjL**?g zT~VXVmH3Z~9ti3nQF=KvLzWhajxtJDq>Z586@DoP_(Q^n{T0zl;h}}}S**}XCWk={ zxM$7CJV^z>8JVZ8YjCAp0Zd)fyuE*9OTKyXP?DzuGYp~ z6i9MKlS0W-L6Cd#mw6dPF}+}P6iclYw~XwkOP{g!)1KwnPbD8Y_LH$3cLm@KgPttj zaS>hOUH>65vQN^7;X6FD;xDEZvG=2nUjV#P*^k1cV5~6s{8L9~o7evjkl*)@QtfX?+}g*T>J`i37lELV)57Gs`)GPfWurjk_cLXLF-iex<7h- z99=;$g=3Xco+CP4!w7TT3^oh*4apTyQ)n{ia&QD%9ejdb(`$|l)6pr zFH4J?G6-6mlj=}wyU7r&HZA;C_~nilu1Z$z_-2#e)<}kP%ON7U2FnML` z;0I?pb0cD%^Vk;QJjSMDe@apTIB$rb#VYfqBjt-o-vpb~jwC~TV^v+wh;hf?B!NC76Gm*4mZtN}6e$9)5lXx*yq@N7@Fdx#_n34dhdg)14Bo1*_#? z*pGTyx>%<2B|}<*UJlJR3CYC4R{yS)oeD=us;$Jkj9h9vVXFq1AlJ9!oeG{g^F-4j z6jm#gKv&UVRTJh0h2&t;Z4TmVLQt9~4+2JE!-@JgFxi%67vNT(tIn(vbN=C+HFpT? z3BVlY>G=CEk+pu-qD3?cPiyeMQ3K?i*n~@xpyz`MYBjZcX7W0s(!>qwbsznj3-XbC zX=l$Tqf=Gr1Qh87SjZ}49N2;?pcoRHPTpk9H}MkFAuI`c3r1byhf8;~l!o`giLm?) zVA#ar;9M`>(NcjIyYNCzhl)4xr5s;q&FCN5T)=pugCo20YmN^ZmyD>41u?u9BJfh^ zcN4{YX}N~W>y8HGVw&|blFMAC5dD{TkzCxXCTIgazHqLei#Ob*DHrj3l?#T_;z{hW zVT#Z_wXY~CL`FYl&4FfsKLH@-s4mo|r& zEof~zV1^0Us>41ht`ELztIekm#qf~d7895_3QQQ@h4(R|>g`|oc9408`l}b_7USls z=;J%;-xSmybkQocOkBcBWQkmO+$YI~q__SgY-4nr(^m=P!G_NV9VieN;uSnhTx!t0 z7+{GtmL2{#@_magz-&3^Pd8Z8$G3aW%KYp-XmZWtXspjKFI_F;83+gP8Gjc5haZB4v;w)G;IV^DosWY()l(fYZ} z3n=41^ts<1{RkO(K1J$`sx={8=6R-`LZV&T)CsqmhFR8o&(7>$KPRHS4wu`D2Dho_iUZ^4J2hf_w&qd{y(bMTuoIMRs?YWn)t*{L~FuBl3UvTpR*yfoeUIzd0 zjga8hP`UL%F*k#xsuo=}0*Hd;uMQpKo*)y zFgR>YG%*8BNd5SQ;IEJ+D@!dhomhVJUc1(5cCABHEw#VIZVo(GZBQQVFyFsQKq;eL ztoQE2afRMisKA||q$K;NAfanbWDTa9PpZn2S?74p%VGSdkf{;}VulzDd>ey-l^Fu& z2sA9_2w*q5eDTof5?Kv)ZI$`*PmyGfqzI!9vlmlQIF;m(vhydGOJD6I-Bxiwi=Tqx zYG9D1)Y$e+9_9@C#?rTVIPA;3Qta`2*Dj2eHBo!@ZgtWR_s2gU6-*=RHO)Wp zBkh+1vGngo3n&UM8LYik2uM?M6W$`_AKm+c3##MX`Q*PQ)+gFR9YXEobJ>iC7M|oVTlS82$zg zxfitGB(7M>E9WkhU}g^t78Vdqij)s`6<|O$<#NhluVkbtr<|?ewCI$B>wVK;Jqg2z z6W0FS!`ghU4NESu2Sde+b^a_aIWXNZ5#eQY$T@@lILt>X`wcon<&Kl*CF*y@Uq(@G zR(jifvX6TAB;TnG`uM}v24#VjszBnG|F<0p@oqDM%-|)M<;2R z15$1{*b^Zg%U@`36!Xf_pyiNw-!-qjg44(p8{k-R0_)W9t>&SvM@pXK8Jl3E!SgPj zft!mw_v0zwqMmE<^N;_*H$3?t6?sm?b2FYT`We18<>Q&o|62N5>UmWNk>#&-Bg_j( zQO39hQR6@(F>i&^oj|FG3wo483Mb~LgSiS&lzq(k*nDxKTa*KR|AISHQ$r~RqdiQ=o zYe5+?sFcr`#0|!J$VC=Mw*?A}Rc7_TPm)N1jbACBb($0KHy8wMNjv=oPADP{wY6Eo zJtW8j>RPaSBW=}5CG;h8AzP7PDHWYccqIItt(yC4fS&4BW$7ix-9J@L^{D@0?FJ2b zpoZ+W;iW<30QM80A&e8U^28e2c(0Hc*6|42v_tUJ;TXRbhoa1J#RLbM;U-xZQmr61 zT^_%66rGKhnInYn*Du$DNTUVURVI%Bq5}m&-dqyNffZPKCnR9 zOTz4PvR1L;*dG)ypuN^@eQ*Hlc9;bikD4(<&*8pd&SBaWC`w*X*Y5#oe}abSqK)Vx zC3ZhSN!NV$K8ytVa;NA-Ub_;Abv$h^u-$4q&0)3=8M_2Wamp_eXqz_@G!?mcW>R;# zg2DPD%r|GCw>o1gl;0};KT!S-g=Gl~fuIgUMp|@`BNj+U{TyczqOwn*CxDOMt(C?{ zm9gs%@ajAr8~M>{mT+d z^X_GD#o-CEjk<3`oSy8f${6r|)*Eno65VT6`JS|KxU5k~&}{WuTcbj$T;sks4*(v4 zar!ndJVT5`B<%P0Wd~$p09ce5mT61RQPdIaXGKKW^T)i7`Xwe6h_5VP=A+2$uTw8z z=92GB^R4}FFu5eMr_ej^b>Z`|R!nR()48Cnbj^bisggQXoCVe_{mPzz_}NhD+oaF2 zKNacvXOJ0rbRIL?gq>nY?W}EfI3adScNXf9`Vvf8sLiQ;I9umeP`EShcPN7xB@Sf} zf+a*5q*RQUO^{GYlV+zCW7JuiO2}iqX&bA8B}h_ia-`&MkRa@(G1yC4vSZvcPr~#X zlXD>!Nub`ehGxj%?#vh?Wy`EakRc*IARC=lh$v!MYYh5la~DmOY!_CfI-*dR9|LEP zb;sW@Mmc6pVBz^9djbol(@jfYv5BvGOkmM9jq@xCEPw`;=v=96i%#<-#3IChy;qIA z(rqL0Mc7a}WiS~UrmmPc37obc4J(QSuRo=BT46pBBIlM$U7?)PyU7CaqZClFj7nAp zxk`rai!%WjL=N2650$#&%Sz+JIKq=NyZxW@o!zDzOE+CMuC*r{y|pHrNh_Kcx|3Es z;Y?c5YpP<~JIwP6cvM3n zGz$}wky)B}`63qUv^OBx>P@J8yHl?Z+>!VAtZmKEmRfby7w&QE%__5TJf{x9Sfo%> ztxOlPfm-Ju+F9f|8P6CzbMb7#^XO;l`4T_V`0uRowvs%LG5y9)T&{_{h?e65_+QC9 z><-x;W^SwI8i6wv=Gqt8nioE4Z=$oU!z^)vL>K1_VzV6>u+a@_6MeC9_PMCIBGBa# zIK|r>*)ecvBS8kZ9FT0Yb2vA=?O}0%^W-)sjKIPxTc>iVP-|US85{FC4h~rwyASCH z8C{$#?y1mhRX{i0L!B&{>n)#yHuOU?8GbD zv8i7GaU6~r2Fj+*Ff=&o^tZlnQ(14$t#AkSL&HjsF*GQiVOTOW=$hf2zG9qO)%Uh@ ztwU2gu%+!k$~k~N1`l#0SoLw`zPXg0rx%;Vn5|%W$^YL`~L|?Ti8F|b#KoO%=f*^5>fi=+9 zs1kD=xLC6!OOVt+V2S=2OxmyTw=LaS_d))6EDPBQ zVS2s~^1oL&;H2k-ITCQ3IbmG)L0$vBrOFBOhuy?YS5BC{y$|xPE1+<~HEEAwFboL6 z55eqiZvX8Z3aLMNz>0m_(_3E=%;P!)AYr5tu$jku3(3CqLkRW zPLgC;Mq!SR|7@5>1^dl5Fr!**bLgu)WVa<`%b~A20hM)8`H@D?6+dNPYtQo zO%=2}&_okc(8G3=z=5Vv)K%t`)we@)@Ppw^Wl#sv~upE6ket&8HtT7`57j6lHwf z{As_%&|fDb+q@8|wC)Qom8;EHcksZ}zI}K>1>*RSQXqC+0#)YDJY*1;tzx!gz85F4 z|5)*kEER8obVyzI+o-5xStYfDLV9sf#_|Pl(Cd&^E5g-r4Z7{G;+tZf4P0Yc-;_SK z=E3w6_;XBvY5YUquCzCg?e0Uq8G1L%Aw_=TvNs+k)?4wlx8bflDn(oD{@d{mSaH}= zc3}*$8^1M2pnIs@_{}(_S(-2iA>?6XmWNr(leiGrVGhZAad%jmespp?z!_7aQ%dtF0IrUQ zZfnw>?N9=Po{D#&DArXXX%b4wAw92mu_sv2p4@CZ1gQ*p-(uoYuc*k4C-|!8>xe5a zwhtN6{0QrBUm|?-b6|;nzjrO|#+40p)`k~$mX=OHOR21@IOxsC{ZL&6(6-HPwJEnP za}&%5qDdEDYj6D!8C`cb56{kk%j*v@r9g@<^VMe=LQlsq>`5Je4xw-ro%*#yAdX>| zq9xP4QRHw-(eh~=iJ~(*J>1pl@QvMHmAv+%^oeqIxBwk?a)*zI_DavpIJnD_SzT?; z>(tyKI#0n`?vUhBEFz}5Zn&n@b>}T9Ib{^_q?!`H{RqJsNxPiVHQQ5U%?_ktrGK(! ziP{TmF4VknGwT_ol0(0Q>cT4Kgp0g^9r?wcEqK1db523A=LtOHd#UHI_-XHNOTIfW zmQIIV@?B>sW@SBuHcJ06-op=H9SkX9kGz8-y+T`o3-8fZDy#%~1$Bg63_EMA_Hs65#qEOtDME178kzMM8m-MhS_&t8BVvkK85fsAwj0h!MZBwNN* z1l?GaT!V!wG<9WH1%N0H4B1T)kWl__XNprUjN(qMY^5cs=G2$yu%1(#W~r3cjLVfY ztL|*QQ?rA9^~fp$Xo^(?RTsNII4WC}rena|yb}?oLUN@}^l20yQvvBZfP0#2G4CN` z7x5=hwvHZ$AgB^f1+NjhFPaZvHbbDZ$a>hk9eGVQkfE|nht=}@y}tq#G2XX1f&WgCV77bjMt)*!Sa{*IebOH_=kBt z1PfmyUx%1d7D;1PnZ^a(=oDl%)D>|A#$ZSaX6!ZYv_iv@@`u-)uC^8H=AIHP99dIw zDS{tEpZ>+3pX0exKP&O`3;k3NDE4r_Sa+lrr}DNu6_^PvE8@>6B( zfS^AIWF`C7=x>kY_zd_Kn?jr56@Z_bG_{@oFML(d_!xOXh7#u-{FP%6D^7L}A{T#k zLoj^_td5f*{M{2d_^YjBE&l3+ZCUxNTghL|3~2u9a5sMi^|W$QpE5aqDBKnhJEsD< zz#dT}vjb85fyQbK5$=v6bVIJtjFaNA5c>8k_KA2zSj>|uKueH%W3I(cO#BF8VA51g z@5X6pQIz$rwsJNCxQzDl8LL#Fz69m3W>7w2(v21~_9JQzR+}}a1D}*D|G`^1BNAB# zCMi^p<)E_M($PTtR$LT>W7+>I=FFV%1*$n_2K?U6T4^INm%Y)Uk-#&`} z<9@fVBZH{Ky*^d%_33)A59>_Z>jl`ET2R_A`^N4E3zr*h<6rIFc0An6+I=A#x zYfB%TxuvIIJ6G+;nO4EBvEnOntcx6>kzYvmb<4E)akZefz(|HJ=J@PBGI_&?0q{hateCVk4RUJL#| zlnei%^inE=%Qj`jf49mY1OK7eC;r176P}n1)W?AV)ioGHAUHD`i0&UnW>j`2f!iRw z1NR4R%Prui5&TIwaLTuDPZIAnb6063Q>+|9 z4&NxDh_gJoIYr%tlN*>x@VoYoOdsz{pK~X{zv4k4JTs5(1;O8h+a0>!I{Tp|H^SGR z3WT4Sjo>d8-K0e^z4f%dIXSs&=w9F^_|+M}Ez}_!_wWNhG{S?8LoXZ`YdCngzOL?v zYf@cL@AXUPq)(aEYa;x{`xU~UCYg{W(c^=X=utDgN5FYp;rzA#?jGlDnqTCi`Q}xF zpe4Q$LGaMw<3zbK|$M0gKNZ=Qq z5f$l1inKF=is6ywiICfDP1DSCp=pXNnNDUBqS5>~i1{~oyayC}F2nOTJmCY?^DcgJ zPr+g_4B}V2x}FkIdI!oat^IIGWnN8DVsK>4pk8QmSSxNdez0S!9D zSg$YARLZP6mq3@Vb}acXiC-uMUnW|MQil>-Zd8S5#di4hq?Fgl?0~cmh;=bGk<^JuPRoU--Q6eNX8okYX zPr{DRyd&|wqhxfNLGspx5vfPnajSg`|@=;kwQuT00;tgk5yLI9F+y(YUsi0-6&EM&Ff5pDT zWqM3Iyyi-npKyPN+Tycyf%{Q_35|Kbp(HilxrWJ3e8_DKG(+mio|78OMX8^kN>vN* zNL)>~Y+i!O`wtbA#7Qcqadoyk61z4?_Z_)IMiRHm-=b!vrqK2$HIscoF#5sa!R#%w zg)KlPG+=o-G16)Zt}i~s7uG6LFnv3au!L)Wue~C0)_x}oMxEPhBat*5d zS|LO~o4p z3|~di*nlG+f!~S`LCk?lt|a-F;Ej@=kB^4&O#{J6-iS@0yfu`MaHex*AnH)KE9E>* z%j+#Iyx(YtXK8<&fd%0rNg=e}deRxL^E9&^TnZaLsW}v#szM~% zyXH4XvwfZ4!4qd9Ehtj48zX!2ecp$f%_pF3j{gd;ZR@4Z2*-8~t|I%r!rWTM!P%`a z*4%D|8=MVY#D>PJhL{@zosupHN~$!Ju#TCB9ExHpKB%qRVk6*cZ+>=&PaugXKn?!*X*K-q9+if~&%Jv4LK}XfIY~O*<<5VpSQP>y0us6Vhi- zGLRtPMVwuoZb3b`1&i^>atm$|4||eq2roaEJ5SVJ!%d2<*0p(62pjH?G4=-Am^juk zmOSZVyET^aELaeX_CeX>>B$r_no|oTh-Uss1YNz(I5?UUv$4(?G_XY`O;*~3MVEBk zXpT>%bCJX`d*>itKPj=yOd$_gH!zm@RK_wRONY$vr*x2ALxehg-N(uJ5L7TKsXJO> ztTB(`t3tlIl&3Es)*NV7O-d&7t{k{CMHs`30nO&% zWXsMqIFya{N@IgMl>^P2`?1X{c|02}p@L;cdvHNcFcut)Prb*C>a}mU)NiyB3oacl zP|+{%TTSTNTmq<}aRVjsHgzcXDpcJtuVImkPz234vH51*d?3~Qk-GV=<5ln3J_@ff zk7eazX@8S$zfg=Wui5SQVv*00L>nlj>?@x5hxqgQ(qhkIJOxJiGgc z#a>L~ABdi>!e?}asxJe{g8o%j+z*!0Pb`JWLIT6!ax%BW7OsH2L=sSjY0i#R1?JD6IqsYy0Sxpv>6n?pMl#{CAQ?6!;`N7Uh#?J#$qOh)5Adi(28%}#x9>c3YJ?|SrKKs18I(d?c7!bc|( z;@nP5t7Jv`EIb~6a#k0Y%8J5$MY|PfF9XIeSTN)#BN{9Z1iG*2m+*42iqQ#;iF_>A zFD`(d4~xIGH-R-ND?v46Qozib8&8sX*@Rv$-jpPfw&vxXl__^m9)5K9yy__Bwb`Fz zEC0$KQKK3WJH`Qm0kr#Xch!EAWke44I5i@yBApf@8TrT}9|uxF5Xw&m{+PFcAF+}N zRt_yy_+?&C;V7~s;BYboWy@0k=Ir%P&R(BTl~D<7Q;4@N0!?rFsYX)``wI$PZrI#~ zgrBwk*tGhue8>7ZZQ2r1I_xBm*B`$V{#ue|%PH z9A9zjjzIL*HE&}VAu0bL&F;NeuTC?-4MZvOyyN`hOu3kZ-?nK&bp@CEhvL0Z^d)!V>S? zYW@a~wT7me_k7XD>1c&3)V^nT(wO*OplvB}rP7%AJoFSlt&B|@#Fg?bLT`XJb2{s& z2-$^;SO~GyXDss`;R%fD=dJfc0~atV3fb^~8`gOnehJy2cGfK(J9m3vcx&K9^9B@K z+g08*>&#?ZAh{IS62Jwi3De#pBP^MsK zWpv&vU7%4*>knsfxU5&L-_d*FoRd7zI{*iwRS8K?3rlW+LO}3*V`8XWw@I2^T1hDxp7R~%SBX!t5pjRZ zH87H3@qNbHVC-JfNAtW8xOM;f{6K7C8x#)h`aOPa8k2meV+5EGZV_x4U$R9uN_67$ zMEV9GO&}KeJLhDSaFyQ34S44p^$3Zqx8WN8QJ36=QF~*T;tzlMdM|G6gOD`quK0XN zCq&}^pW|H`b<_}MwA56VTK?laXCO{sU(>E)9A$axS%@ml3ov9Jx|h! z6s0%y#;o7jyW+LHL;;RSU*s(=4wB|3@GqC~Df~os0Ib7A?0?N=uM3B=A1FGxsdcc> zi8+nGhV-D`=q|B$kUI3iGR=>pE=TOJ>>s^XwG;w;2Bieu-qgbRvaXfhGLzHdU#NWt zqE+fNf!dREr4JeNPzY-AJ!+`+%#?tXWZwFZ?J-xS#=I;X$Uf$tWs3f3WA-)P3J4f$ zKZHG`4Oc=?l`NE)sV1I;*A{Oeu701B+#%R#>K=$y@mzTvhoI_@>WeV-AXiAlVLbNQ zU~o^rANsP)#~VWzvDXr~tvO>Qs~Hoo7uG>baIy1{21+(OP8y2SO6w|ba3ecVLKWnR zm@4QApKbW>N;U5dRhBPv{L& zZ|XGacUzy*F9aqy3cDH!RKu#5XT9{bjVa`wPaj{Z>!DPy{kqp$<&o+-I{1CM&gg%# zU&3IhsCZ;qNNdYA0)YatzlZKC`A?+2$Zf-bExQ63P*pVo_k5*VhXdAY5lH~fEOYRG zlgYMo3we%m)*ypY;PN41tFf$ZGZkKH z<%n5w!NUw#vmq1Fy*wSPHUrg&?UHCHv(KkE^S}0b=N>_8M6CFj_U%by-s4?o+s+^2 zN9kMCpQFx}R(_ae;EcbHB9zcqn-xb;JinJpgvDqVD+DhSgk7vQk9jjCp3_iE@w{a} zC7#bXOl8hnZF*1&^@(Iq$u1fuGY*#R5jBB2d?tF91zLXzMPZ0n)>f-*TYf*nPkS51 zwFqlcFIStNGS|PLk%~C5wPZ_q11QjcDEf4wB>Ob;^XYD9d30k{({!)tTqSm9pooZ9E_;$ z!Vb)#dRJ$c+roD^%RKR+cDdE|yUXlyi}_CAeq+CT&VF}_{q6z03qi^QwrUna?BKY) zgV{5FKZHT`vg`~PrIP@XtM)fTFvUnO%!?MMaGGC<1ON3Z9C+klYcfwjslAxYgW`X( zJGKie(S64TcGIy}jZfM+Y7r$)hV z;H+X#2+veJVLW%^c@oddc$VOqTwd(ifhP>IYH=m>FM{5FJTwQ#@u;Kz(>mzPr}rVz z)aEI9PsQ=DR@kVZ@uzKyf5rnKu6BEF{{X~XJDrL?3&aMW*RnkctJwyqGT>OGPXJ%) zD)UO?p}LB5Fp`3`zT_5oJLvQ(5?d5M5j+Rq>8vmCRJ4MQO3d+`TL{#!@&9dqZL-8R zz;1cZJPl|{lVv9sv%C6*s4uQ4Xtm318BIBKP(B){^uDbLtNF$N)y@Ll4!nS2c*T{A zR!3maF?ndm=dc9wnuIU+MG|>opZt56FY%{9O^8kJBD^|_57M&ZPz#!q00TWzd2 zm!7R)0}W$E1?DZUeVj~;!YA`Ej7!LZSKpAp<(*leK9>7 z68U8xkV15rKRS@4t4r0doCZPcR@&IxybJ%qX**&g`LFvFD7nHw|9;Bi1JKEe`cJ%f z&&DsXyoSm**Bm}3Ry3T=ZhQ6PWZ4Jsv$Xr!9-DALpu97DvDSKJhQ_-cNYsfsB53SP znIlA(8WPPbKSPj_-Xscw#+SVO?O?F~L*TcC6dC&d(3AU-r0q-$=5f$rjz_m(f`F#v zBW5IrTxnppbnhZlid3q$i_B6-)*Pn!oG`r$*V!Ndt5?m`)Qo-dZ<#T&L^4HyE?2lI ztB%(3g_w`Z(hS>yqxKs;PH=3h3(O0z`LpSW}4s=Fo00`^IY=q<(B1&i=8=) zKs3{A1&+rWm>vcd<+5DBmhj<~-nUkvKM6Rc@s}ol0eld}`ytYYgag*5Cx_Mo90;d& zE%068%=epae@bN*pkeMaV>ieqqb*JChZCy4*-qa&zKAE%23(&tI`U~AQa#|G-njO; z<|oXJglz2A5+}+V$w>Pfl8Xl8C%(Xhz%tZSBo5c5@i{zzRmcn_`tdjX0#^7PupegG zH2Ug-wz^%E^sw~po#6jP9;~IUG-%ZFumph&DeFe2~sVg1Ih zGPU}Ph;Qmmn@{qrwd(elg{m(s@@ZLFfcYENg?*@}3>$2lzyJoqvI~r((V%fR!G)sx zIsj9G3RYhT{WfIP?i+FRRT=@iY&GI*^A&=KDM?w@*S0ElpK!YVWf#*V2E)H`!j;h(I>AOf$W3AE87SOZu+*36sflTl^@wm*zLW`Px`tC| z!+8#?2z3MDELI*G`)ch&3XYxZ)A~YktROpc|C=))N~90l;zl#{52^hyLbzRK68nrt z#R5_&I$jS%*FuJ5?b5yupyQoEIC*~dB9B+Of<}^S{7`q7?qV8$q?*=umy%NBm{50@ z>MJpixthdQrxFuaw=?jkP&DifWA867;Y5K7*sNeG%a_*U5r4&hQr&l*iq-TRpMh`2 zv12K1+Qx_{39>hh^frtE+F}~v^gkXvtGRSAIO$b#Asak?Mc}uxVL{5!$_vrNJo(2I zRk5Vjk$DO)Hz8xBUW3)}4!^K0e=dIcAPI*Bv#9QXhNK;Zkg@e#N_0qgU(o)K8 za#~3N?jQHRS^dI(vHJClNA)WpRWMW#n3~gd_@F;_0f?D6HWVK?9=OzP@ZdoZ=C}@% zxDHdvl1~RWPyynBq^j=>J?HGRYYqipZ~9Jw5Ws9q#!{V)3GQB=#1im#@0kBtt1^!( ze<9+sQVHVYa%vZwxA)Q%C$a_ORoT~4zGgUX`O>wq`}qvWb83enR>aW?4d7DpC z>s3)>9xUO(Pr^Ec|MUiRtr-_Ef}q0dEcVBIZwj_kWXVWyFZKsAMCMyrKH$B_d^Yug zGp({qiX$|$iKMuiGqtNT`5AtZ2^KXq`5DWf+4{nnQ7U%4=|U@r(uUQ6-tbDbpV^9d zuGw|izSwWn8|>k<^WQ15Vdlj3BorHb#s$X$)@O?$-{a>B-y({$<{Iidqmeb}NbOt0 zXp*Vfw~?w1^O5y%j*f~pS?ZvPWEGoOfpp2Inzx9e6r3kU5Y0=g`x8}HoTLa3b=&N^ zXPD)xEKKMqH?BKai^ZrLNr}npA@YGHd@;)n@ttz`i0?n=Bz=->+)RWxN?Z z53AY?Bt`9GQni(7bA>rU)z$^}wFuH1<>)pM+wsjKD%w#;%<{Z9X+IvEs$&bHY2!Kb-<8e&nBs%QgEb zR9>-WJMYeJ3K<=6wzr#q6?##~!cC}LhstaQU5fp;w`HCWK{ezLE{d~!`VC0=%!N?K>_a*yz z=YjlVGG2KZWAnY;jz#buQGp@9gXXuMr^dH>1esq9q|I)2AQilkw%%|&Prg$XyBUH> z8yvBF-T6-1zHI3bY@sBK6?+mbN*h>^Z@3^B&NHG#75C?0G@T?wRCV6uh3b>uZsT5)`S&)Wh;o}%ucsT#rpVtyH$S_<`*3>GI zE3Jztjkdx7$A7_fq!}PH`0vkE{y_7K<8YcnR|NlbUY6d`{9&QF5$^_y2s?tG+@7F{ zhh%%Dmx3OTgxX5Nmo3*&=5Q(v#ZLdpaz;&ycef$Rtp&JoG6)?_>~;F8G-9ZpF9Lt` z8MCSRk{HQA?8Kroq0?ye)OtMit>K^HO!OXs-#wnSyLYy9;bg4Y(XcMoYd?6b``eA? zog0qkS7S{@Bdvd&gZI`Sigg2IjP%LNbPYn|pEpN9AS#|JalcNrgwXBi(lb}%WMwcqDq+O7SbjjDOTUOJ#= z-|wVnEZ9AMBA2KFr1pC@NEL2Kx%NBZv3M?cF8Y!UUhQ{-$Ktu*x#&wacun689*gIK z=cF&$&m+h{j+M(XpXxk9al%XA?+Q`e`)_2n@p+KFVejCv`e+u<0uT7PxWR#+XM^l% z{Pcjw&Ny{Fg)XRB@skxtv*6y&XLd-XE=W1%Q^7;=?%}!TGdsKy-whs$XM<<)6IaRs zL4IOd!@8PXY0S&E3Ky_bUB^1sq|b9nBndyF5^9~g-PgtUlU<;0#cK+X z#MaG!+Us|d177y^%LNbkQWmww$Lbnh2ES{=E9ePcEs(uidIor{kGkNoc&_<;Fu$ky z>;c|`Hh!x4>;azhyv;hFwc~P+?-kr@?%>Z@JlFVoJa3zNfY*AD1y7By2Y5Z6w-tfh z;~Q(!7lFs(xyIMydD|1b3C~*N6a35#&wV~LeT}}Q%bQI-jPG};`9$^6ES_b2K;(Z; zaGbXvyIP!*CRa{ z-vhiMKpDsR%HrL_bFW|4`JB`Pybu58g2&?7;O!LsqX(N#`9T0xyH$Bkb%QKR-+l9u z5y8?wvTSa6;cAQD%sRg5ZeSVw^bi1Z%qNRygJ-SZ$BL|gf6qo&Zg2z3*zZf<23@h_ zp&Ks91eBs*kzDlaMmL-c`n4LH0QBp|oDhyw5OUG4o7`|R#`DMn4*GRdPH3+v`jvg& zYTR%##xsR*>UP)UfDoQ2D3@(j?&zWQrdnzJj%=;tM7`>#4o1hhf3woVS~oB^zPb1T zp@%GPpr-mpoZSRs6M(0#j}hrD?gWgWVztE*N^s!S`9Dz`1XSScDZLVV|?rX zXpfKQ3yWtNA0}dVnPdG{TDxs$OLi^MRM)I#T|cFdLcuKXvhu4*EI`KkCEbwR<8#d? ztB+>!Z1TS554`nnd4vz*;qt@5%ar#me&izIxw_MTr48`0J>J!>b$+n%^p0ESN0@p$&XEU5nz1IHnXc8(IDox($pOr|qggf+j%9Gn4__-2 zB=*Maux@a{a<4zowi;?%wSN^j6we0EI)9z?HydXAhBW8WzWn2ELZiy0qM z&^lkz=x;V$slDF?DP#Q#9lrcc@TLl5v?xypve@%0Fg=_2)Be|Z=Bmagxq{Y~!i zeen|u9;=UL@tot^q7!obYZ;!RyQhul9x9<9qOOYkaIe zn#FU%%Xh%bCJ)vkIwco{`CSSg&ubRX3GV@=5aE1wX4BWz{xLVaS5ok(uVe9?@Q$+J zW#g2a+>@EX@1FtVv@O3hy^smWI^3`k8uJ7(9sVy(+1Ag~1HQcSu(f`yKAOcd!CJ== zz|vz`?}x1XxyL=k40=}fD;plykHvGsW3E&SUbgk~^ss)v`?U>^>&N0b;PtGp^Ynn9 z6Wsc17VjQjntl)jDTBUL^ss*Gud&BRddA{8;DI6dd^jr9f1sun$X1Wvls%9HKLft! zyo&DzkHvGqgWNUdm*NL*}=Y3NT@LF%N;5DK8 zXcq4to_qbWk8e$m@wxCtjSt1M@T0uou+(0^!>uz4K9iqi8Ds5Q7tDHm6yx6YH|FS3oV;)@=}hs#qP@K`*@_}u5;_kp+m z!o7lL$@9+rkdNcKehqgj14i)Wk9Z0DaR8$1uV zzFhFWc*=su>Z4ga3p~2Gmso0xtI|k)X z)N4=rs_G`4Vs4C4Kbt8$yl*@F z?U7Y4Jakx~tu=}N0;$+^c=IFW3L>qR<(UhH9LYQZ0bUX@;>sy@zH5Xw0!T(ui`dVH zzONJcBIO^_YjEA$BC{e=j+I16r?dsu_Sho_U`5dPw+{QEA+mTMK`+K=kRU zs1q<|J&lC11KQ@ki1K)WH~#0*tK@uMD~ms__q`=qa_3G$KRBV4(M2QZuaEq)vHO~& z#^24WW*x=pY&ac~Z^*drr)c;3fKl_bZ)C%9eNnjKWU3fOHH?=(j=~?ty=<^ynfLBT z&_>W`Aao|L9gtj8kCx6xOLcFt4EyDcO~>Csu`v{T?qxI~`aYj=_R#38SCIPP@*R?!Ip1gt53Jkv>?g@2-WzS` z_uUK7PXx&~+=-vKFj9x>7UjN1gy}|<`o4y7# zA8xlW=5Q%EnYW{ffKhe5`5=Em`bgpulUw{jElo#c(HjVO@`MMVDwc(Ra7HEsBDYs0 zYb5DcE3QHgMmBweTo}l}`*tYm-xiGA2@6Nfi3r)PtOx~+^Gc>g{eKP?RFn)y#y0<_ zk$5i#mXDcwezdZEeAU4AQ1ra1As|XH`sfBgRvqN(vXcX)FK_W8D+~}_>VXWg3 zdpuV*N_67RkRgc|0KKm-IlmS+lkj4Xs2`V(ZAEm{h9GXH@XmP-q@rB%^4)te`W7NW3~RuflfNwgSl(2K~v-H6Jmv8tU- za%QgC_Gxluos_vt`ha2_GY4zF!jIT_Pgh18xf$E)_=&S=KE_Y_zluwc^oVEBc*9p2 zGImuOzcEqjXiq6N$qg00=8t$*x)ny%v3NqbiYM9^8Mfl=aUhC!(I?gK zhMG;@d0&B-OE+=an3hIT9H{9F;SXMgz+36Bm_jZi^`CXW)%&qANu7&TZd$thib`X` zq)1mk@7&w-)r{fFg3+-WAo^pKM*Z9`=P^ZD8v&2-B{%tvhp`}kw$^-AoIn0)-D%K5y3X3%(~7WEk|U~rH~ z+2UTZYl zjV||PDs1tD3MXU(|1X2_Y2fZyUm+{?D+INZwyp6u>>kk=2- zKk%Gr6nkdl`2f#ZK*<~Nynx5T@CxCd<<7KSXlK>>V?5@fCLl3VG4v6>7<&aQ0n&n) zQ&*7euOYwc=P_e!2*mD|oa{T6Fcf)kTNL!)+QLX#EU7U%%)5n&MLWzbLRQV2$s<*4A)uopSreX(F)fB72k zBTb2c_}K_X$>V*X$rl|v)mZC`o_}%7m-J)P`J-d6kPRD<4Ga2#6Zu~^aKjq&=G;1; zxnx0*@jkg3+clntVqDDr+-dPIuy?)DPw=lovG^AJ+QV7{JTA_{QX4Jkn4t9Vc3&AT z!4j0-2@G!<($4J?e`s%7!|uqJ?boihpKeF?NxRy2@$=bG&s$D6h0So5G^W}}+iUS} zF_n9@&Fto|!?Lw!@2@!Q65n;&&Ry#p-k!uId7v`5=yqAYW8xB1dRT*4z`5VhH|)2T zz1VL}YQIHReC^YF&Nl>T*W!y_j>Igy{s}bv%2sSYI92G|5$_v>U3lIx*oE`YytmkM z4xTA^Uc~bio-y|odmhHK1b6Y8e`RaXCN46~kM&~KM^9y0 zlm5+Dx2X*O;f2q@|Lx!!nm6|UFdi{{fx6?T*cb_Hpx}y}8=U*2UkzJ?Pfn z=g+MB@4(dlBXzl-d(&u-qs!Q$)(en@_*+9#B~`0w->-Hs>f+EnS;*A;*MN8d36ZxNO7+RxNxmfHrS0IU4LCvP>=? zjcZdpHy;^DHYbk7&6;51`uTP=7lse^zSc6j7WHoK&2Iy1aKlV9F6IdowEOFq-G*Fb zP2v5#3!4Mw?cqL)$UU{5^~+>+ zmivu+ICYD_B103kehV;k{U%>Sb9i9oz~%m^d~Dsh&Nl=^`2m(>FDs38xcBe^W8PXe z0?DkVe?(TG7czEw7jHiKroQU$e(DNtX&qE`{icQ%NX*OgebM=Bi+6G#6l~r#EC^~3 zW_HglV@N60$JQM!74o&z{yNn0H8qE!228c%3;Tz3NlhW|pWB0yHK}BWq`)f-9|=Sw zR`}{*P6kr+4gl3~#p2c@b}i0sLUyo`ptpnO(lWmu96_~y$@Hz%2(~xh7ZBm zRY#cpG|Jy<4NSGw8W5boUhxEnG~+@=XM2^#KSRc{ z3t~m@R~fUp1i|J_g~8^o!m4ulGpwgSUrqmxmi+KApRq!gVg06oAcry^^-Z_!i(2Ps zP(bidSyNS1nsD@<^ZA|O9hDv4K*9^wbz%bUNwW9Z_y||eCs;-cTvh?^Qg|vk(!E7jaMZrPtsK%+%}6ORq66o!h~!js8Wa$|@evV@7KG zK4NvEsr4G3xgG8M&S)PvBmF5+9WM%i3~xVphPL+L83JfRIdWG7{tktyED@jn&na|8 z|JA~Jeq#uX%_LffjrRA7W$L)q(;xcWp9LDVl^x-NZao&rsU7hn2{2@8k#9QDhJ);7 zKSo+bC3r|vEEmgccd=2>Z8*6GK~T|-tmo%9NG%Fb8d%{9LR4uiC$f=SRcfkAgR}e_)A;0P4Hp+9*Qe$*R5tJT`!oK!Y1^F%=;xokJaUb+V>P&Eeh3z z3wp!mL+W*~bt2!!pYjzBcv>Hb%l@9qbt~p-E!zN)uv2!>dNm|8xpjPzlJ?;-?p1AN zg5tV#Ex4^Vc7GpvtPPD9P*@owL7Uo*g7uW?6owBUr@!=-p$pRm5MG(n-*7=ZT(B;Q z+rD@tPeGFE)cV1OIfdZ!a!7}E;sBa19q0%|^61|>%JkJ1bnxS+P>}&331zx!P4=rl z*|bqW){i?-`Ef!V!2>sFvmY7_ml~!X1Rt2e`@?B$a7r2(jH=vbt#goo0TQS)I$X?f zRG3=gUbLJBjav3! zW66JQZbxJs;ur!Xl7MT7R^f!|R`1YIS^)ei>@qrC_YnMYnpE4p-9A2siHxp{gJq6F zF_m$v7hFSo^tlesN#ip41yEdqm6=YQo`vb0R4zVBJ84p!We7QR>^G~`tZ?(U>%Z3i zW{FxJu(|eLWOHmEUlrE(74;s$(f4M1v-6I8dA;%{UHPj0stPjJP@4Q}GPD}}o{NA< zq)y5;Z06bynshw;Y-iEm(q2#}S~*$fe&uh|Z{1oCQBeagsoY=Iz=&JB)D*;PVhi(v zQ}nrP9VsLfn{}=*gE1b#FH{q3vEqsyrdVV&LG$Ws^y*G+X%F~CPzp@(xn19KnBvyr zp9@ori)zA!DQ-{PN;33-5nqk9E^#9tnmJE`tj@%>PE;e_?t5!A<7M3fFZKvEg^Wr` zjJE0J8Z7x=~&NYJm2T}9nT(~Q-3zra|O@+JS%yemE!!Z zj8_ZPWVR^lez|SB{aEBg)*SrbZ?sqJ zW4E6^x49wM2T=<1S}?tS#Pp!sCFz1v#i|BCPgNO?>=?mP8Kf}+R208Rtn@XauCZtB zIFf=w6L+2D5X=bz{r>=SI{kOo-;J{VFfCH}LT+AmuIaLF2p}Lj7p75^1Hv>y4Ko&6 z3+A5O?l6$Uy>)!+F}*K3pxxq)uFvICP#KEZLF={)tbTM~O2Qu9oEMBuZY7teeQ%*& zaG-r}al!lA+C-5#JBYfZ#EOcN1D*r^nBAqIU0b`q(45XasvGU$fK2qQPonST;g+!7 z9?1?XFMIMQdXc$LX*uO_)utyYTnVq&iwbh1UAOf}O37oaMHk>w*v-hxV8%zu*fuh>Xz50}v5|zJ*{E~Y)xsydG zHcC|9ykf1}oE{)XTh&ER>;Ph!_V>m|-|`k5!9c*g?E?2+E2lDTlX=eYXxAtIq>qiG zRsTAj{w?U0`qFLHHk*@&TMr81t$SalaHT@{*IK^!_s4I!T6Q;z$K*z5?OoT=)tuJ3 zIT|CSX_~hwtub=>1IR|p#N1M}k(fq?0X!O>Cx9Tje&rf4?B3 zv!}wvdTyQ|mzK#c=Tza;;8Y;wf~tUdq$zI%V}ZQn8>%+@@z*m|rPphjy>u{;g(52- z$dzUXwTxsYE)(&%QBtQI>$X5`6H7^d@Nt(T<{ zjDuf9#IwupJ)BC(P!SIXF|5n*DamNU8EmwJlSPBH(SB{?koG$K$x-wGx7kB;?uQ+j zY&^}<;-BVeek%pKftCuj@0(nU1iE>T6R^RUz-^Z8;R(>$dqyB3WC4sQ??Nwr?-H3L zPH&Mha&oO`?Y~6FYAty1t=uQZB``nayEPT7PZ`gWR{r@huMr}qV0|mRe@!n0Zi|Sj z0PN`D05|bMNNw!jC0fJneK10;EUk6A_Hetg-4+o=L2u%xB&XvNNJ!#dJ~dMm>l{&* zrc5MKYvud|Bhs0sRQBIx^zVeyy~yykD_8vhBC^x$b|C_Z(=JGj)@@;}Cf@&Q<9Tkg z{Io}6-6rwsHmR18DTpUW(t8QvGidDy7p#XE(Nh~ju{=1wJmfy{TN}{T2d%BitJ5P^ z6rwLZ-~^b58QtHGFa%qWrdhdx=H1MAlZ&6p>KmWtOL8GtAx=Sk4djBpxW;US)a*Hl78n!b3%mHmY3Bcgz zG#7#BO^X@8S%6q*ty<|Ub&8)H$`J6sQER3s)ViV3r#Bh7fzX5=S)e4!M1f@7sIB4y zR&c<+i)y7l`=T1gCU}0Y)^b1SDRBX#mjzxg2Ig=D7Mp@!H6(-?j4S!mJdGDhtK6Z4 zk;qh77!tx2_@^n|M>U0*t?8IvkP?`zc0zk-!a>oo3F=Br=JI*LV5*JpUL-SN{Q;|KiB4=>A(+OqTkBeC{353-^ zAzgb45sg0rB&zmf!Y zFI4@>!aPI;ei8HLc&zp`Y&zfC^!8&Q6LBTEg79A2^ex)Tyt3T(+;!S>-NyE}g0R)# zv3U$ErW`Ux)SLJ&RSEJ$x=~yGg04*yXs51j>e?;qKz(Y{yQR+bD%kB4Y+{#?qeh$l zH>pR@?NAkkw)d64AB19aVW${zvJ97p`w2uGQE*Vz_ErJ4k7R9gvx{wPeFq1?hBng< zS9ht4?>3zZnhVBOA7LTYlpn0|9Q*M_Z8V9 z`*+q>zXI!Fl-m->KKQV_8|8^x&lo%pV5By)iBusE6H~A1_Rxum#YuOw2^GALAt=`^ z>DCSgoBO=JA$NBGZVtT$R~rb!H7Sz|{o3n<>u?3wAj!1Mci=8#LN^2~e9(jO4z)}X zYNTbw8Zf#|ZryE9r_WxgMPXK|gBrpUHc0O?i7l8sRb` zl_e`YB>94p-bEC{W%X801aEnasYm!5QCV>@Em@C>7%6$#IgQ*L{AIHjlZNRM`=g73 z=f@kz*R-Igsx86Q7)AQtG7q;W%-@%B8MqLNF0sA%76qQ5M0Gu;*=4B^Bf|sTyp*$ z;4ElQu2&Mm^uo%bo}7FggBb2PF|1Q)PJc#_l`wquA#p&UFqq&zM8{J{y?2%;_Pw_; zgt}5J6t97judUwvzr{Y>7g)X7dL7*J;xv##m5Y2r+jo}>-4?lC;T~7Id6BwmoW)aH z-TA+*h%8j{k*Xw++nK>UiJ#LPa&QC-oHw-3qo4 zp*EPdhI!Wh*i!=y$&%Lo2;tTpu7>z*H%@((ne~SzydlDSqBej+dJXR`!@INAyS27@ zs}bGqLGc8A3$iG{J|*u|DCET>4DYM4zlZ@E6d;?5^)ds%{mNGfK43DAgB1MkG-hws z3ch8t7OD$a6g=>>Oe6oALK#+}@f1LvhPw0toAO3BWk|B{gpJt?J!th#twf-)aJ;kR zzf?=Ya~KR}UN*|B6F(c~z#b@Ai~g2cj$W7}q%_6sP&&i6Ro9+_WQXDla9Av=_PQA->H>7L~0hXe(Cl4CL%7~;sVQ2(|NShjzAh;gp&aWI`<=0c5; zJ$Jx=6zwHbn=RG(z{p^$raPeuxk@r?8lg%yG>&SzI5&_UF);lEk*;&so7V67YcX87&YU5Q$5=CRkw+3;9D_AU3V*U!*NC=W5z_mh3GhYXpChSzj&K%b84; zezz=rUp8^GH9ay=9Prf@M!|q!K}3@~^w@6&0|1+2Vg0s#YX9d4sg$~9P?T#;dr*z_ z`2iI~+1uYzvUK~>!`xKS{^sc@>9_%M-9(4t7|7R_K16%4(3%|^KUyFFoWbxwQM98S zq~ zTI(Qa9H~F!pk`!nJ`#^ymNAR@|B?S_r(uagZc@)iX@bc@j(q?W#88kAVOcOrFjsnF z705H#g84vj0KXzl6uUE_+d*SEAA3jy-=~1Cozqa$f!YH ztb%tki(wB8S{^!kHgoDt0`z_Z_XEHcBHPYwNF2{nf}7} zP~HZsH!8 znp28e9T95X+?e<0oMi5K^ljJd*ktl0D_{epR z3@Ar$x^0-JQAbG^d^L$i5jp`;0ny#S-R|ZypK%(UM?2vs6r%1X&;ua3)N@@VEEZq%=%9kU#ta`TuA1ocH zdtGZ!NDHn@o|Wzi={65dhl<)s2aZ=mE$Rlw8?<(NX&L&Q%y6gSAwAx%K0@S`_Wwm* zInt3=j&$UeA|p(1TBD#7wbm)Z<)QZbBfpNEQh^@n0%~>?qKbL751urLsn0CdQ@_BiB@Fs|e zvd9(m?xNAj9}HnLl5>R^_dQQvV4iny7;@2AhJ*gfXvnD8vR@{r+_LOVO{mwZw1u|)$5BFoXb;7~ zc}jbxsQLh?{Fj7JYk|uMBbgFZy974;n|1Rg>QXNM1>$g86NFgB9gyB$i zb+ScqR^?i@sjU4$7!xk-v#0bQKuWWuTTTL6k4jTHL0Wer2bZ3|f4vC-ah0l%?)3%tT77sH9w69H$7MuBVMoUhj;+KldWzW&>Z6XhBy zk-JMAyS#kmIw@(h#~yYTLD8orokVpBWot(JoUb1{U&_iSmjCM7eu?u-Ct~?GRqtLngWg-N%$gT4oO;0a%+OvREO;3ZKq;U;=NRwN?F6#r7 zJ2&~ndGX}a3Z#b6gBhe*8+$^)vep{luIo_yyM9KR%EGn?keb`bi}l!~-bUnAXRr3*;1 zKoTqzmDm;Jp&G8Je+*(3LW$#NJ?Iq6l< zn$L$|^7_3=j^W4DxM8a2jo60CW8EK+A`vEGIRc=I^zLB!O7j6%ieF8svxEGm<1HfDh>fS-pzL1@BIMmAEe%Jtn|WA?D0~EkwK=A zEVQl^?AN*_6VucPm`*%+01Rbo? zruB00em^fWdO4`d(TGvj273XZt0Zq?y}ie1fKuV6M;n`@Q8KdX?G;s>ovXE|;f)UD zX{}!aIo4Vkjw;0m+6EE|a`%KM#2q1KF}fsqL&3ubV9O(04tHP2nHf9~^a)tqk623|3Ge)HXJRfh0H!1{ zBjZW#0v21vpGH+wFkoY0RaB6l*7~Rn-2ZJh_Wb#?X5-Y%Z2X-{m=|W_DfK3^@dB~I zn89`3FSDjp%e%%%D=zP&^{@qL@}ILPbmgdtQd$vbZb)~$iIQ1TQ5j0wG%le=?NYYtDG3WdZpIjvOoVyDwk8fn$^Y2CUxTn+WdkVnp( zWF`FgM3qomRnsSlWVYs!AANbm+fBTZ#BENY%Z28$W-ka?4?Ae$8@8wg zth*f)5f>D(Q4qXQZImpWNQ9sWk?$cdx*=;s<)^hCH7=~hb{ISVsRR(jxQQ#n8Reo+0-gzKdS;6ArEuE z*M%iE6Pl4v4KhiW>6g1y6O%Ee)_NbY)ib4WkDwGL@D=ub;gHh)7!7L%Th(^MdM~Rt z!&PsJo7Ogw4f7Vm>QMSk>}95TWU<^3uL{9vTE#|reVJA~tqz@z2#xEDRlh>sod_sZ zhil8bL!vu6VO~hP6Sd_!nC!)3jTFHFQO`mI%RM3U1jAz4Ev|sL5$Fk9bycE5YLPV$ zR@h=d(ivh0f#rE>v{vlVitE^U*_1!fiUa<6+V?j|y}i`9%O`JJK9JY|T%FOs6(xZn zUhKmluM2is5>as2C_gM0?^eHOXwyCoYX3Q~h+_rNiHUu}gIG%x80Xi6ibLjwA#;3b zGPg1wjzOn|tg9Csk}8LZZ*a73UgV zH75z_t`*NJ<{d^<-cQj+UH)sH_kj6V6wGhcM%653Jxr?r_-j^aWqtDd9c@(YBig8M zJkRgn3Z<0#WlFYX;#hbsv(H(;zylPDR0*%)qGwpUA2@U9fyOP?kdErb4tz6 z7v)v|C_^yzN*=3*iMv~n4E7MlZW?$(_P4{uDxtci^x1z>IK7Sxkk`wtHoF{v1JhDZ~c2@S$W~Yjs2a>(f5&q=s86rUSKWx1s1a%T5 z^NPL}K=2@%ZmgDl6h+PA^Q`XZ?s)WIyzx>Fz@S9gZfxIYb%O=~Xw+)MpwX~^;M8{B zGD&(-qz4*B!f)8hK8n%A5B;9V>39JvGpaYOiL{!iKa>B|wWPkq>L50EsHeO?xoHWADFTL`+lyJ-0 zVWuO2xy>h9^StP6KhlYNcT$+hR04uCm1GS>81OWXmwm+6rC9jj5NLMxZ~5qotAc2_ zAyN&8C%+bnhLV?p|Aw6BP%W;=wAgKK{NhY=uQbdmslOttnc1y3x~3!Bp)V$F7jou4 zX|a8w1kut8x1|@lEv=9Z;TOL-s->(sF^Pl*P^Wm@Jx;kg9Aw4kT&~1Yplo5o5pD|^ zD}DoM8UpFCwFjh)wH@;-8`m!C7xN+`g_Ft#@W|$oeZ_Qw)i8DPMZ?%K!7q?>WYyg2L8QrOOFT7Ni>safLg(QWS2CLa|tpjKOkalM!9FI5qb1%5zmX z&=ikM4_V_&?eFgJQyMyh0sF|#WXd`T(&V&dXFB8tRFOc@Uu4sEp-7nVG%J2JqZgAg zXHRMz3*~G~(MuMxE}fxd4BrjK9x7GiN(vZ_W|82AaO$Nbxh6!O3JIHjuKT_txTcE2_Jg&;g<7Q6wW` zt3G@)$W_THj0PkC@iV|~`R#qVMnkV%P`fWxe%RWMbWk`K%eHs@d|xVL{y~VBB>Ss+ zsRgA{KJqoDvDe5~>sGM37v;O6ckJ_)lT9T%dcYixXYqf_pbKYo_q}I=5<2a>r^4}U z`s)ym$3S>u*82=|KC5&Acwn*LB~Y5*?qk|R)`GIiSHspUIG+!+NegyrSM~@Fnj5x` zgq;0Po3t1+(i3VeJ8VsZTiPMVOwVBLyB4awMH+9RboKd~ZiqX8>*876XJ>p>?@<#smjC_HhZ zuS>+Viax>hfWvgf^6yq40eKF1Z1zq7O7idHNBAwdgT#KUiR#z{V^be}^;}Qo0pWP6 z5cH1j5l|AZRQ!?5-^B3Lh*^RMnOcIsM5-5M3pK4L2&CZu%9}u&H+Dvs@C{IneO*7( z)2U=tIpjeD0Y_;++A4WeEk%U|QW~{T!E&}Nx3c`pYJ@v9;OxfU{sThVK3oFhW*=!f9(l(Nq!LA&Q#re8rUtv z8btw>_|23om7_bKN70+9=H=i*2pGfskppvyusMyTu*d@$@HLiQ&8PxyiQ04CcW*(20`1ntfe0W$Ew zT(!V^8cQl)6&OPV&!Y5WEEKX{whzN)sFQ>B=*#i(Ec+ZgwBT1E!mc^(Lq_?E3TR-T zr?EO{&4Mh=KkFq~*{l*tE!_eYTGJ+{GfKDY^G^LRrOqO{1q-fjqSV!N2%_tk4&oko zRv;iuoInodmqL!!Rj*$Y;4{>P-#n&vrd|1JFm}#61bOuGO`+{?M}JtH+c*dExaB8= zfu%3|G0@P^9gbbFsh*GwJr{;7QhUXguyt`o`Nr~1*7o+@-jMg@(01FfE)LX8cwu^K zjOKm4`fm%jCF`K6!xWeA6c@A(V_m6A4CC`UFmGA9Jl#?S4i6FX`&N^LoT1D&~%;hoV|`Ctkgx|1pM$v$aFEdL8HpG$7= zp|!m;+N@GA2>U-|r#GiBP`*#|D&t9T%NF~wzsb6rT^I6h?u`DDwB)pG`LQ;3MVCte z|H#M5rA2e?!&^l#bzd3=N2(6kfBBW9S4>ktS#Ai{qIl=9NyVT^*Z(jciR|&<*v?M- z)ahzGCVON&0Q2;CK$`8hPk2!pa~aDomcNkQ3FKP&l5~B>e1y@ROVmiohOtBqf_))I zxI`{6pX-x8i4RzJ95&exydmRou^NXZ?l|1*j01jhmy=%pk(+V2d9M9ouT&iyG7f(r zqbTQwAb?}XA&mkQzau!n^{NXNKyJ)3WK}C$oA>Pz>yxRQ$D)97E&^awWN@Hk z>_gCVB0p6~K)HAoG@#ZzNdm@{QyRbGF8eb8ryq+Kh!L)+g`%l$f8?(2yJG{60@ zyWC4RN%BE?1M>0;S=OR0_wLYBW6%rH9&3wUzS-&pcT9MR{y=aMbO)q zspok?1A>qQF&vE!oB?4fWi}M!xY>T40aZveGcjpEaDUbQ9sr!@Ja-nf;?DFI-*^c9 zN-Y1&{K&p^zED+NO|UpBY(zhqrE`*c?gF=?-E(`qST?OItJ2p z6gzcnJ?PXiUFvx4W?2z;}vuY z$#)C+x)f4OAwybfwJoQ}^nh&=VQs2Cl#Y+K_X6LBH!2$e>DA%;IgGuc z0>yiWNOmPW5!;(ZYh~J7M~};oZtp__s?(SABlG)j(%!id5g!H|b(S^>Hcoil6GXsb zCSJJL>6oAo$6u`xT_ebldeOVT-*~<(kqYbUbB*f91dA>z zRrd*`txC4rqnCR;-k*v67W+o^Qc1ILw*UwLw%N|xB@KXl-C_5{lc}J!WHBi##fO&G z@*kuPTlX#?1TA~)(i~$-RpU4qeIMrU)x8Vfk=@5tPoiD`?@Kb*=hX()tfVYT{HdCCp#kFj*!dfS}KX;pM9awz)mn* z&+5%=J&=-Cm;Lb_5|L?y%)IkYMg`(HO>Ym<1AYW0b45X8VPpBHUTSrua%nf_tmoe6 zFs;LOmf`vKuf>$`N@SKFEc~t!n+dx=-H;}+PuG=yKu9kX#6PD5O<14?>K$H$nngZ)LKWA?6>yiq9{l@zBb({ z%Fy4AObY`|Wwx#Q=klWm5#PeEN?-U9eU)7lr3;8dPj$45LVt+<&#}mYC>>P2s@E)h zjF^1Bqy2E}r*9g1n`<49x=ssu&gndsrIMQ5H95lJaqxC=p zsUtFvEq2~=8G3fcEb5LyaIJFF(49SBYMv^$#iD`@W#zTo!$P=CTkV0Tl8=e%27p-L z0z+2eo3aX^}Qzx*lc9IuD2&bnb@!w<`tXtk(U3^*Cx^=(nw5Zlv z^(bJXTT7PLaeSqGNgF?*>Lttg(U!(Nv<4(rM{23O@&Jx2@IgTabLA7rBJB{BgktxK zt9=#a$@AD>mar+S1kxHELui2kBlbO*UMT!Q`Xyxj+yTe&r_03wCqGYa26B64kNv(- zkkJmu+88slPTt)>BKKqBV-H4wE3360lgQeJsEkrgb#yTUbjQ??Wyvsy$}MM@tw;GnBY4Bk1>#{cIzhS7-&XhW@-pu}RL;r{2db|w&CQ~CGwoNcQy|4UloOq~6QdF+fu39Fs* zYc*qay7iGB0|(Dr#^n+2{0tj9KbLMf{`^?aC`uQ(ydntDV!-pO$#JL3ou14fwy8mE zxL;aWeZMnI-~kyXqr6#$$*692hABwlZ9pWxbk@b8ME|AB@5E2Le^_t zt#Y#kL&s#bsuD#b(*PHbf^$_%>2hM`nbSv`i_r49le${9hedtYYS~b>i|Jn8FLD_D zn9Oc%!~OHwS@r0lRO98i&8#0LM>f2Iwr4DZ!1Ni`rN#J#T{tXUt(V-4iP5Dcw?O0} zbc{#-mON8$T5~=5J+oqkI~qrX&0p9zi~2%aHJ!!LEFU7;4aXMA@i;zWH-xc$0-J52 zp|ir~Ep>+Z{ra%^9m9TZqHOd*mP1#k@DyV6W-U|QjIPsA@fp!Q`!Ru#5Gj8iZJeNU zW<+;H)lXv?#)(!$xpIC_kN{vo1+}C_E}g4zT=JV z9vbK*dC|SYRU~JcHgD@Y(5s&rPhK4K)nN4a zE!@`reZWYq4_3dq$gh{b_UBXzz1^nvq&I~iufsZQKhxp|GTy6+mA)3N-Ya@b{~ad| zEt0}6u^QhklkT;iE$aqk)kK48Dj|)XOJGE7CHN~yl<+YjE3Z_ID~g3I{+pTSCavgh zFS}BA8SKYst5{oQ6uUMz-ny!!`t|~Vb@*UXY7jh|RBycb3Zwd^MVE%sy6IlXdbFU9 z$r;Y|)^E`-)o8X$ z7$c4|hTt;+Tj5OCpUEr>m0iOhyyhOjQ=YZdv1A9|ZLwc?OqRG1vC$U{wP>!96=Qz_ zj3gf)q$xP4rbV|4s97%rP3#ZN07^QMZ5zHhY0JNQRChaT%#N3!1Ldag6CJ74?FPubQ+#d2cFeA)GT;5yx&WM^I->^-%{Y*6)CCS zzUcbsn+`a0ZZ-|gwGK4@5NLkOf#%eriF$boXoh&()c&3~)rDBZ>bKu*7HEFACU$}1 zmi)o$!*`4xQlbOQdmUgtR{^sF2x@E{*{f`b0>q*$7oavQycb~5C;)Zzcvzgv+4Bh( z!~xIi%7B{D)<|vDG_F)|Aa_<(Qj>3QE_(jJL?@6d2|8|`6RO_4=;91GZ>r@uNy}O) z;D$a2mVc#SS%P%yVF#8~OcpF}N{!7OlMTx|6fAQ+JOdzYj+~3MO9Al++v68nd>*v; zJU}=lAl#Y(Va`j^AK?}@rC6RuM5x{r-8rl2QIC!U3u?yr*l-*HC!hn#?l!Y@+CW3q+Z!ilttXgQbccBx3~S7W>_m~hNS!v6g=AyI3R>|tPn<5}`5ngdJA}>& z7((I9lN!I7UW_rWoH%}^SiPPRl=lDY^#zWqPdbN0fB|FZ^sc38y3H!UGO4nFg9ZHJ znBHY{I59Bg@y)`KM0~a8FV&lf>9JNt-XkhO`gA_`jT+J20?%B{SG}?EbO@Q~x{>&W zfL0lq+%4zE-sbSlTvl+Z90(`Z+_U{48QrFc``*u!CoWoTn#<9newA3SAWO4+U2oBou=q&xCdD}Vm#b4b0*)aP6I zb9p!Q8#|kSM>$x(nL>5j4>T$JVGVPHIIDoB+uGk!j_cz=hspm;p0=i*d?MMfb_J_e z{f5FLGemVo>77gEGbd=S67RKE{;pLtVJ2<)t9rSybE#^JU6g6WBTgf>*gtwhP{bDd zJX41CcTz`Zt13-E-HJ8=Sb##SC^F|Q<9ELsFa!I2;8%Gc>H+3dQ5F#&;g~2H+dzbNY$yB+rvGKI2;%v9^>U5or4+PC8rFvyu z>vka>B&QgSSkdkOU~Xpq6^W{wbhg@hmUL6DVMYz=v6e_BW>oI26^x~P>#%#cITT?Z zZ2OCJS#X(>vt|^0X?gjyfXK{S9x;<2Mj>IyhB>TITH3O zF*phENnts_@Zf(9Cj2H1 zld^8lN@&}+kkHwC*@f%T;0udynAQS;j9Af5`7xb9tF>FjMHj2M{MT4!j@-?3Vq*EX zssyb&00w)Qz+pT4iY<~y0cC~`rwwaodt)cLA#zP!(PCC+y8{pD%(wcq)g z`GG1WASsz)(E_AZxX$=mTbYORa1ry^6XeiiSLLXyWw;~qZa-EVxQHnoett(J+<$jj zlmv*pB7p;U2Or@z^3|aAK!qe~?vt}uxR8>c19yiB3pC1&cI*$@Bk|-gis8ye*3!xz z3`Cq1d5u@dVJ1%Wx%s5m%bd&^_XS`o;d<5ugJmb6f{`5R7;aGJk3NTI)4V zJ*=QGDAljpdRF0FVXu$UDMl|WPG}&FI&JQh0NKH>e{I3efEQ*QEGlcYR(>F1y7?~)%G7a0&FIUf78dda}e-LhPyNB7Aj>8p2w!R%!b zzvER~(DjKxh8yjyCmEB(A09Z$8b|JHY8EhsY41CNPjD&BisO17h*@io#BUcM6c3sz zk@UmLZT^L1NDW_{a?)GVIP*EM)H5QJCL5w$`Lto>V*kNG3rrBS7Cl!03!_dUtLJMg z&)vJ6eHh%cz!h*@Uy3#MM88Mq*BeVKgpF?)(4TH%2#of(@^$OukoWnf%ksI>bLEvI zw3Rb`K`$P7A7xPR_Z2H#PeO)rX7Oz6nlJfgm^b>+8_q1AS7V-o`;_OrNM?I47;#ID zVO>%@i_7Dh)Wqj;HjqR!%*W#rARp7(o{Hb?zR#N$E%XH>RBy5?X0!!;xd}PiiOV48 zV6*IpD5;1Z_^#HHpb7Zvi$~+nMhCvAExnwi2EehhC2v9QIEMM2mMVTj-WOB5=jqYH zQ8{`vj4@|oB^k2jAC=B)f$a(LJDR(yDC=x>d)7RCMBb?6=}JA}ypLWjc+Po8lt%}0 znT_c=cr}p`Y5jmZGId0J)6hNba`@$_?KH$ zU{?zlDVY=EVZ$>GKm6qEivtT!egY{J`&7BYGY?58g?d zZ!QtArWfA)b6M9aZpBT0IDHjSnR+(BC|Q5A);s(eKKjC*9`7!GFY|PJUs2x)_uKZq z0=$JiKJPAmFY}CWb7?gslcAW zGT+ctd0VBd3Q1KZsU}IPvZ1M*vg#yNy`;kRXt2x}n#w6_fuve2slF?z<_}FJW!+Q8 zHpWKqcp0J|`utFtfEyUItWCl*J)y6(uchn>38hz(1147G5KxpHD~9B7%J0p}Aq1t= zAz&&wx`*V*+bU&CJ^LkwItJVv0>qNTJ}QT#cE7;%SGf&U^b+<1_@ke15c+CK z-w%YqeS-rX@UIP=1|JiGwqzh6F;BGD7IB>sHpN$$G^h4b@WR(B0K?cVEIkT@zr>n4(%;kTb$*~85eUcIR=XEj`=C)k$1{CDS) zXPMD_?*bA#iovlLK1PF@?_Hb@f%NTKGM}&E>=9i{1m24U=GX*bt8RpVQk=?l*Crlv zj|zva*A&Us{I&v4EB{Fo(yEd}H!Y=cvODFtfs$)VU%o37W!R*8}_giR7|UbN^U zw)K#CHOHoJC<&R1pmT4tYt4qfTJwgT%d}N&yzyK{i;Wfe=$rW=@6QGQM33AOK^cA_ zDw9HZymRKj-EMh7Y({tR8M}3Y$By&KzJG&oF%96I8%9FcPNq?w#BwiZ9*_96Rym^^ zvW8Wn>bOtlg%f3xFod;0sQsN{6oF{NuvnQM%R7hTO`HXL*?yE6g7t2M##`)T$|Nq_ z04Y#(7S+Nm1lfQ6h~GB*>CzQLujd2*aaw`NWbR(Ibq z?qVxAr9wF;5YGSsyX$-VQpvJlIp*2<<%=hHrX2`QDf0~`hzuv<6e8@OqK6UYAVB5M zO2CU941xH0d*k+pLe?iWt$)WA6i^h3jX&VvcQ2d$zmrs~a~tc?_!`l6nzNPWTr#=3 zUA5+XjMiz0s?nYztvS2AojQMZi)zgIAn;5)aH-n!BjW6$toEEM?YThOlQzP*JE3$h z)`_(CREa&E*yxe`2>1Sd0iqjOwUFPH?^N~x$`v@Hiha!dl($>2TLZ3q7@d{O_cI#v z{kL`E$Y;zOe7{ZY^xJ-sAmv`}Wx%x}AxlN(6GG8}f}mxqDqgL8N92ij0}R#x*A=+M zep`xdql|xPp3b;waGs(!f5tp5B*Oj}RYQ87CdojRN;{b+@q~A`nxz(+!i?$Ox6_T; zc>;|&-2N{%=7Adr8*?D?8IAb|5%&D7#yl>qdxSbNjfu_pC_3;1ZRuj67-CZkK1^_- zJ-g3v&k5r^);&uN&$)}i;SkV=iFaig$PqaWEI?GJ=t~VY9$99T^ zK~)-s-2F1HE5$%qwp%e6maW!6XVs^EpDHZ@3`8MYtbQLa^~rCn=y@59$0#1>3JO~p zl`q=of6Wh0d_mBeGX9yjtbiV0&NHt7dfb!yng86VW0D*bFrjei9oM4)2GiNsjZ!CD6Q_k@KlOE79U z_M~Gx@}S^lj#Nt8$L~KRc@SOhC(yp>r~N4_|EpBO_TrY^(@5FZyJf#Eh8nS=pGw(v zgjsVYV>{ov?am2COr3hLblMZD>c{g*oQthfaZ~RmQLuX5?SHSHQ+3BhSdfNeQ8h#0 zNW5wb+RErRbjw#83shO{sde-*y8BSHk1J3ED{!{aP4?UWh9$@X7(uq%iFNw{-LC12 z*u-lt`3aXosAw)7*CPG>3Q6^?coa(k4^kfSY{t43VTl&g3T=Pto$~NL(`!48N<8rfL@lP zwf=(e=&imScFYF7uqzl{zRbHvmS?UvFW)z8xbJ`c@BflVRdN(YaO9`DI|{q(*T96%l$B2pFa(2| z90!jio+d#SN`XttwwIQ3GHI)e0NZhNU4N~Wz~%y(YK`?t^iZR=^jS4#U;f~2R#(G1 zt7XY}uEjOHovFTB%g17ui>d9r!nc{ChwFiT!`fs|%zrzTxCyKqp7PE5My5*LpWNw5 z7%BC}v04GMdF7V|Lf8o7+HRgX#=zuGLW7)bkJUkK)xi2aEFt25sr>SQq)D7aspMIU z0Ewz<47l3v^ig{`cmRD8G_NmZliDOe8H@$;?VEjXr?ge+qNLzVaorSC+u+0Q9Zw^9 zhF32OPVqNhjx(Kd)x@yVrr4|_-1W>k>eKX2PZEcj&MHwqZ9exqc_)#H*tEkOWRiGO z=Ome)ZjTDJ{H0u*V>izx&~Xp8q5Oe7S0uv ziwf0Kwe}tyE)$S(!|56*0TF?W30sH$ypZ>L?2+fx7zT>}S&7NttBP0s4;3d{Go^|% z>f{QQ%cA%Htnh^R4V9FV=(ruKneuH<<5_^$+D^){pBg6JQ=^KgX;>R#Ks7!I9|bWe zYuo%F!~=@B_@7EGmr})rqBwmKAyCW|V!L?Pg7DLlni@OQet=E$0IaNKKPa(;P?g#zzBum^6CQ)qh z6sSBsjE`xtF`Sg!#Dw@7$I(FMj3s(K+tejGB`>99W>G@zH>MGAvsC3knicTXO!=m- zaWav*^;b|ls}n~$_P2r7S`2frEzPUJCj?&3tW8d#hXRywo|F-L1ihJnk(8}~{Nm3O zH!iFEdWlk1%+j9g;Ii`So!rb6yYdEmLY_iMvJBf!NdwEPtT$r6Yv-$2H5+CL3}(Z9gLkT8^JU2G9P9BxuMacv{AKbw5!U z_BvxMQ?a@~H`kLac0+`PCj=Aas)^-o=Xx3^Nf#rNmY;{uz-=++BFvZ-!nS#weg7voXj=JD zWxQl65RGyYrPwC>a4MNfJeZXa4;fVgtz-jQukhJdW#tRv)M3LiU2r-8!@7_n=8uu7 zQG*}g*mu)zWa(W^J#lLXm=sN&v$E}P9-*d5)D)s&A*+HmVkAFUkpnKt!;tf_qRaLs zq_G9k*yatQX{BnFLY>>$WPf<#V5Lo)2LkcE-ayk^!viDx18o&)_;?yhS+|Pq1E(`M z|ILStnYA^#XSgb=d7X%iWOe_F(h@OA?ZSl&tpgTF8a(!|a)`g_xbZ?~*{u>^n-wp^ zzC}o`f=}sn1pC*VrrNqNg9x(zN~?;HOL-PmqsjZ*oPto(r9Nyvcu8yHo?aAyJ>XsV4qwz}cnNTK`kZ+uf8>+-L7+ zBiD9OP~tV!7tdN#KBke=8SkJRbxHgngDaeGaR)Cez)5r5bwD=z%>lIz!fzh64n|SI zhZJxO91|gM{CY8ixgJySG)$dy2oQRT038a*TE}!X?HMkjMXS3sXKSKqcb?wVJutFc zkBiBtTEk5}MsVc13=!mpc6u7zRh6dd+7Q^d`yb^q0VT)}A{X%DM>ge)jH8ij+vduY z1d8njS^ecCB(69zU*cpRR&g^WPIKaXk#J~Az#o~RaszIXLPx}9e@;>*kXt&ez*pv_ zSpnmEVFhjh*kJ`G@L_-D<3!4Cz6VyoNjox=cDzb^lasc;fwUtf?d6j8A199Uyl|TH zoO1FwPYX|U%s9_>o=Q|JfEC0&T|^S>2lJ)^9XO^d5Gs%JRi`$6weIa z&*GVSoHnHM-aLMsX9l_G5nF^!N1P>A-w4>Yhj5Jgj#1K^tBdp>A1J15}IbSELF@1 z;&N8JjI;G{tBPRR1Xfg0u}g9#jU1A(UQ#zU3PXGCFXX_`gCb+dFy2DKH;lx6k_kEt zBI&)Mr^>u>6rSpEuWvSvDor0g=nRhX<(RjRDx~01{vc6fUIMwJLxUh{O>!BUJkOcOa4-qQf zIVSp#;n7crX-l`zVxxgu#P3B>5xo=%3Snjs~AXsiHAGXNH z?4K+JbIn^egI=efOLJWpi&5Qbwe&^VQ_ZV9X80?kJOMSr;_jwbRph67VE*bVDybF<-JDWY#WfkK08O1<3iNl;en>jaYgE?5VtU{L#sSsUGkB$Qb*8^ zAuIJ6Bqy21^rQj$Ggj&vx7494^^ySx3Y4Vim4iwAw+o0!*X5T;MG8sh!-Ms~a}cB2 zLriCGP>)?&mst?17o8rkq0^tNb?0{z*(s5U#PZv?^_voAS!4R_dp<~}5GqEys?kf=n8?%LA7!MTpC1;Mc;Zd`Z#XBvh%pBdwr(t=`)&&;_cNYHQy z$c0qzM-SyRo*%R}1kJgto#l@A&m8+E)uNxv+`{@GQuV_9H1%}z@vcx%-sq9s#wp2> zfxjS$_^~8Yn+v>?nrK&YgpE_F6l8hH_YwqJ(}v!*VsbmD1{JTewjJoe016n(vkxd? z#EH7PsR`Z3G75IeQfWJL6Cx`}LD6|TJ;Rm0nJ;{_U<%lcsx#EwBd5;oYrph%YHc0` zt8Dt3zB8D;*vwDG>S`Ks;fCLrE~o4e%7pg5qT1MB2F4%*^zM&9V}GJQRhT>}q&>%t znfoI!t%^B#DxCD{Pu=6J4}k~?T^q|Z{zH3;)A(T9nNABu;nPH-PR|LnSM3y>jY#E0 zd_m#lys**HR9<9c&=gfNoV~y}EmV-C$F8Vm2g_mm%C-N7l#(LE(^R0-Gs-DmX5d^k z>L6z|>XKG^_=J-@(DV-$)27})TbYvs3rpv+tVUeNuuCrpPx(_f9Dd{!UUtgMs7?=d z_ikG|J*`QG8BwS^U-%tf>s1|YQKzn$GQ98kl+$G+>dgz4uNU?Da{8>g;T$mRavXPw zeuI795ErA|HhmBKTo$+v#ovzpNSwb4vXfI0m?^A&_Z$!U5>pRwo>*Yc`V^nkc!P-Z z-ozuO=UvU+@&*3xYVPGt7A|&fLF)t-DLk^Mz~PfwXVG1IuFPic#e8quF zN#bBnFjY)1vaeH87=`IVUG0lSuA{u4NkX;c-(M>N%k*;Y`+`iDTJ8=4p=MQPvl_gO zogTEB+1Rbqv*+jlXa|u{+j|jyxL6I9=U?AB2lC|wx9F}wP8+?FG$Y%s+$M$_hB!QgBSO5&P&CLCf2hrmtc#<`&H+m zLo1Ybt7sFc?13IXLyzsT0F{wt@GiK?z0?tV42*`0<&@dw%KgCNklY5hOIy{AX+tR5 zuFM*o3uI(JHuUNjF@!2z&GcdDy?aLNf=QtUEEreY`M`dL9aV3RVkkWTKd65Jw-#s2Po2)M6C z2fSMAyFl^r%fFuTAu)?_v|(K#_2cc=&)SmOR72GuP^@gc0AWRDOG{UosqY~7tBFS; z8FFXrjOcowp6U!%b9_y^XO~EW!BDZW_iaz4lx;vIgXGvh95a5yiJH~$)SsV#s#W9I zaO0B;aO?M^al-%udN}%N3Xh7yfY=Ml0LmLSRduzTY*V)gp7gGnN(X4oRKq^U`hH>c zyIRYC5;bLJo*F$hmeP2_JZd&JIj(Q)ClKzqunjTciqOD?BR?LbE0R~sq*X`nVwi;B z4O*9!s1j94sF-K^X_}Idq^Bn{P`&RmF{i4DX-0|`#(0{Vtt(~t?f-m#a1$fFR z3}H?6N7_9*8Ni_6a8#tfER9pEZoO1f{oWlng&NjlX4Rf`zLAr0FKDYa%-e3G&7LNT zAJ;g?RhmY=aIC^KSUvj~jp=Y#W13nQ!cCH1z2o*@;YK^QXo1pR59)^*FR`z}xq+(j z^G3rLWQNXgc#O?w9Wz5+!vsjdozYHkXYuXNi9fzN8G%c=#tN_Wy0s76BJJMI00O$; zXG**2a7G_3ZmtE;V1z8}*rGw!c;#CkrU)(Axyr-7^9@q)XWfOriM&SwszlB`w z-6HxrJ-XOkPR{h<<;1V1ZnMj${0^x~1_)d}r3vbC}#&0JO;gpbL-Hx<}4F7Jx+KUZ^zg!-D3ezGMb?*6Y>p z+<^{tTSD3Ex-QByR4DYQLr-Zd1NhB^Juv&;8STCiZo#?b>G)GIZfg;F#?S=( zF0VS8_-3=D@b{OP;dCO-ydwHBqKAO;hXuJF;igkdU;@vuTlGF9!8l9K9O0IQxav1(_HAKjuEQ;RLApsO3nHpd}KCmy%KfsIU8!{s~T0bfB<0w&{ zkSJe!s_LasH#2{z^qv{YUaU#&o8zmibvfAoDi5Zr6ttjLngXJ&Qht+TW{y z)kv%=q2^{7XggU>1RNAGiAvivNbf$Wx~}>awQ_`I%F=a^9%Dz1 z^rNq**;k?;C=e#$&~^w31VHdXiqDZZaE4SVX#oVlfSY;lTznmHj8 z%ipPiNQ!TQ{9GE?$LM=b+N5f%l##@Ox=~Gr#^UIw+4Er2SZN?v=;pwTzz>VR)~w6e zYXbzMkT9dpcQb`G<6dy$ee%XX+3F1Ts#M4NOABV1Hy=sdx?4 z)4|w^LWY-22a$0?kk)8|G<-Y+Y3x5BNWYpX1PS?smE!a#?R7bIWU@GiWJ8GT zb_Psw6DfOuXDAnSL(eIMM7%8cZ7jkdQ;J5^rX03hSU>nh!jUB4YVE=*G3Vi!-wN~1 z3(eu5;O8P|T>apd@_LpA3F`>R-F>7nFU6(+O4n@t0Y#gv5)@Sv)?L;JYIz{NR@LTk zrVfQjTqcbAh;e^maca{T-L-dSJ3vyEmuIw=A;D4pN?ONYYo~l@8ZVVGaH*_!T-8xqiCW_KK_@LnNrJ!j^K zUBog^)oO7dFf!~rD_*tbs10R-LPcbOQq>0R)}643foa8G$6=0)g_%^iuXgnri^9w* zZ))zPw|Qr_xy~$0SCi$`c-}uGZ}Z35X4^>1=ZUSaR3p+-h14jvFi_3>8${wUo{$BB zDD_vjHEu~O>A6OvT53%W3tWX@eRff$0%sG}V{evbwQ(pNlDEm_ft8?`xbqN+oIM)G zg%-S7O<_VBDmnCgo{)5-)nO~L)cZ_7YEbX1Lk)O}J?ZW!>U5V>;D*Rot z`wnCd+rM%4AD~U8McFb@4FtNUQhgWdg@9ha9ftou=J5Q#`DZ~8F%#E zFG?>pMS^WjM`voTg5g&Bl}%f-cEC#w3ETxcZ$JuT>^w2x<{i>EwpiS0Ob~5UU_>%E-EfvA%XAyrvP9O|zaV@1JMr{T2IU zF|HRaKRv=l%X-P-u!|O1LQ6+MXgP_Ylrz*PRv&Mj5x*>jIlUo()rA)<|v@=4N~;$WcxRpmFse8@jyp_kAUD<@(S7Kjo(&$D$l@mI>7WmRf5YGf&v05lD3WEj`JP*jw}f@$Ja#3}tQRLSEb-1O zI)Q;8E(ijDKOSB)V{pdFjCtDyL4QP4)rS&a^Bo;T2L}MjgNDQUfLs1ALCAdp;S?S&r@XKOFmJyso?Chw!FP#?)M4UX*sY}PG8aVKX1QG2WuNuNJQXS; zlG9Nx6vReMNn@!zImpBzTnM7cPMwc3evh0kH<8+9c+3TZGW!!h?Op5CyD1+d8;%Hj zSv=B2@q84Jho*qkEK{=8hQv|QNd|C~lZCgegqcLhEJxmT_*3mI3|*n{fBA{bqdGHo zUqzK=F2m(5Eza2l`00_#dyqI+QyAVcX@758#N5bQp}f9Ux&TcP+a}aGDVQM?TBmq+ z$o38K`7@W#Uh}27=upmBjYmEo9N=Z>V>kLHl#F;zIq?}&ZjQob>gL(4M zfT(t-?`BWv5!I`80y&!`fc~CJ8Be_o+Ojcnm0)sW9M$u7oJemK=j`M-qOf-IS*hdT zfrMFD`vgQ|Sjq%YO{_B6JG9Q)<8irY(-TDoDQqw9K?DoROj@2{k2~x&uvVZha(~5qJm+ zDA4_iJ7YKluO6ofmYM1K2BX||r5Q-Dw!h|TK^=uJU8BlpU!tt2kA-3SaLHJZE*=Aa9#redY$LeZ{T`{aZh?d!7kZo)n0(&=h^$^n>D zyyoQ!VJ@y-D){2^kLwCyhG>LQ!$PRBLYOsbivnrbGB7DCVT^jr3&`95xFF3HGHlFE zP0GoY#06Aqnhlb+%N2NVKJK#)oz!}>WIP7^4{Iy4^G>YwNqc{;HbSQGru!7}D z<*&0wU~T`pOJH*jrUdK68 z$2loEPOpS~a&;UG%RAyMQapvQYpn~)6v7_kslP`MRea$Tf~`uy`L6ZZ9fGAD#u|>7 z?SL5C%7|mpUaLKc${}&)E8l3`ZTAm?erViHsH?7r#@$f&+IUN*H|sM7Xq$~6Q);1xj&`2kPC6)lIAc`J zaW-ynHYD`9YZCRpK%cHC`OfH*;)A-TPhBA8!RgcM7if_FP5Pw3(IdPo`jq{jrcX)D z;q-mqBc@M@{KOH{rzPDK&Mp_6O{8Io^yvW|=aJ+%iS+4K9p~r7>A+LO>C+=6hoVoD zd`Ci`>cG7JIel7_q3Wvn{}_FGw>pVF>5k}3pRT!AWFDG6MNf?rr#N5nYnwRT6DLlk z=J0&Cfo)ifIN%9ZPvR3Cs&F{2*Bx_Cg(Wb|x=n|fy!s~$%BS21Ck!^%|Gz*V&N=rx zqYq4VjL9zfmqe!ju>8w^jr>mNg97K^^r3e`SNuo#m!#%!=z|&p_WH+eii7YkDgP7x z<+rH{XVxX-ejo|W-j+00qCh0hJ2d58roqH(yaQciRp$~KaE&A{*d&pT|ZIf|l z#^}SM*-7+4cSL9UaJ~tBFc#SaVyHtP+M8m`OIP&4STsC;nmhbD%vg*>{Ezr+0Sk42 z1BvtJA>{*io&6ol2Mz*IUc!VP z2gr9@C;31kzRFr7EQ5Qe@5de1{)zH|o!>-c{8lMEOjuvrD=)PkSxp>c(aH$^KspK$ z5(Na0+mVh=#?}rJg0&-#P(t9pxeEz_T57NcKQ&20z$)X=5`yl!mcLL~8WR%a{`4?H z0^J*NA;J7Q&f4V+plb4o4(kG0ACSo$0PDFY3JGwMkv}cm2)=gBgeT!ozB<&UlV~kc z$|(|BUZ8{pOSP~-{1zk&3)X32!OFO>pv-JBt3_yz@IXkOwS2gUCKri$vJxJ&`Ysi{ znb(%Dd8%L2;(`mhT7R6N*B>4E&vmjFLe|HeRKFjfpNatuCuYkZ{|$KibP2B!3;?1Hjx1^y3dHp< z%E}x$n{-;4`L)|zSsvJx%1I!Jyu0A~e@g@e3)hKMP6S_WrDvszYTg%l<^+oopV%nu z0*y7w*8Mcf5QXCj&6b@IqfugHDFlZ{$2_Fe5%eMQ*>t8t?c+L@PNZ1rk4_hy*7U%v zk+B#x$UrLQ1pi6oZob)6!>BzIEEjcy2o%0{1PUi1B+&we1Q>PKYJtK4V2u8Vxwuhk zf~;|nKQNI!F$6ENe5++^y^$R==J}a0W zRa|CmkQV+^64aD~v? z?Vds)5$<QfGf)MEwEU5ZY`@cQCglkr-M32oE{$&u|VypEt0h&)f~IBFvtv{CyJ z2epOKF2`6gCJPivT}(_^PIia{Jz02!Tp@D5@D6#=5kw>?0s2$9`6gd#wum55CEJn7 zY)ou6L6Aw3Owc>9MZ(HWC6_GEdQ(J$o~#X?5OT@G&=w&uM@C=1J#oR(E{S3I)!<|T zbBU6t>-q4A_ysUDVR;^-FG_T0Pu$-rzi^Q9x}JXz!7nu76ERZSiM~`1`i}X9gTR}p z9*pxh-On_4{wO#oi_U9jJ)y&-(k>Y{s(#4!1r}P)aJI@v2^A z#5Izde-kczC_J;QGt71Dzo8%NFaj=T|UN!)E%|%)pDeC*H=R5lQi=Fkw zk*g!uCF=eX)sgm+Sc&_^WGG1;xz2j`WCatTh&oaLxz^dw=fE@3f#=`VN7}$V>0#ad z^VZrHnjpoGTg63M6*+9D6rX6u@Rx6?hUA!3ZN`wO@ko#g{37(6ENEl}p;iHo^?)oJ zaDR{>(GxD?Z@{N52of}OIRrXurRH5qLU687zs}Nry`Fkf$H`BQ(<>pLfR6KpA_sa0 zLk9~2rt7uVbH$1%#1)b#T+2+5pi#^QDyO8e1X9B+rhe>4ezN2~S1}gKCiV(7h>?S*bR6?w;xmyd79;)MMQH!QMJ@K&O(?w;w*$` zpk*r-Jz45WxQLzb1y&*#L4c!JlrN$&6iwpJZ=qQ9WJRKb9O*=$DXs|3ha5>;kJy!VE&5RO=s#;8@!mjZnl}Dj z`-qMLj}G<`iP(5p{lXXRH-$Rv?KVMnhCRK@i%Zbe&49844W z)@^Z3B+A7^0ufh6hJ-*MdZ|%-i&~MOjAR`WQ#1U(sEO1J%>M$t_*21mMlTe*7!#;; zpcmgtADL%^^l#D&1U7blS6HQrUaE^6qcjrur)A_szYsxU1NRB3LR7D z0gkoc~)0VOZHyVg#YgT;Ui-D?N#9*wK0tB}RShM~FMFb;KQkn0n;4^PEQLtZ@2Z z4Y?;)J5k|uE8F6uIkdJP9~Vlea(MB`q2!&2E*$-+qffD=7l%-hcCe>jEJ~@;&?{O) z`Y0PwSR09k6oG~;k1pmspKqfdJ&3+Q=~L{r(O-}g7_~3bFBGMfqf!#)96`cFS!L@R zo%PtE^rHtUFHxTo7ZixRGhxl2s2_dz1I9{|#^*$~bDdSs{uEof6RZI|E#OG?-vRVm zOWIMq@ozw#s8xwWZ3O31Y#b;DvEAlQw>VH%+RWCE@d1~Qce*{kU0!&i_08DIqMHH!=r1j@Df4PSjkqC1@^oA4+o} zvI(>oF_pzWPSg^mtF`2EAt_2|QXYClG_K|5+ZCZjqH*=4y=|*$WP|o9t6~~Nr7})b z9L3DRh>CPXXtfZPgoaNfDg)Ts9yMxr`d_nYiuceW*Z&xQ&`E!CQQ!Zw`jfi%8L4AB z@dqV+zGMD?61vha9ZY|6g#1BA#fi-yco^xW=0W*`l}sdu;tw7e+Z8c62!G&tkLDA< zUx(xmYN@(pgwLE{<~uIL~sc9lhY6Aa)XqaMyK- zEJ92paxi2TiSH{b55XZ!0H$beB8PBT4T_QvsQrnZ?594nwJ7EwDsgw> zC+trA46`Y7O?mwwxtE#vU{)T2{)W;)++B&z0?awq-jk%{$z|pmXY=4& z)|I7#I=VNa6FW=p5|rlsIFo?z&MPQZK5=(>^JPVsEZGeU zH(6<#uZn`N>-~l{IET{jDR8ubSy%gcY&!)7i?-K)Q((3C8ydCZwh9m)R0wD9IgATT zOGmtPSWRD2dpHLBiT1w<2Kz%wLobS|FFL-%8cPpKd;c#UC6RqSB#oB14%2ci9Vdb- zMdI8}9DweGvRW?hE9ZDcL2l4h&3k%tzV#JcFX)QKu0FPfkds%G#_lcaxe~!uJ(}&| zlemS@MzuQCZb=JTjCwm;`X%Dq85-=@NNl%*!nbXurw zmX;E`dk2ec#k=p8P8{Z8O>=9=E9wTVoo&AJ%VS9D30=Lof+b=6f9UV$y>;mCzu27oUHqXapOzkT ziTJ7!M|Mp$n`P+o*MOM*e5k)yO{YKDQ=p!G@vr#1u?DJOvxS8Cd|m9SCd`m9 z<$kiM?iC@%{591r=1H;@_iqU&eZ=);4m`ir@a)5nouoDcl2ROQ#^Ea++33FxPa^_Z z0<6{ps5T@x3CKHD@Vy;?{E>t;sS*@?suTsEf?NSvvsFwg#IQm6Wx={*72*WEfHa#m zQuK-9>82?3y;;^la96J#vHZ7}N8Q*h??d)8f4Nb0oi91zgEyBe%pdJ;-TPBW>^< zZF;_ny=0r2&a+PS_phaWO4_(GO;5h_Q3@Lnd!evG=S3%bTqnyXS+VIE;e3>2=f+-0 zR^q(qWVh&KBS_{kJ(Hb}l59%sg=Ce^i%xcyPBxij(@oDE&PU&|re}8SCC8jwO`AFI z{=U*EUwx&jcZZQ%ErLOFuFym$3O-%uKAiYY77G*Jb*C>;SyW3xa*;KkId^eFEXn1S z#CLLOOnBG1%+$F^LULKf&6D8*dBxq7QQKeX$JS}&6%>dJL=y*!Vy_2s=it6z<7f$R zFr!o>ha10DuqUhBIDld#mYx&LlquWaJ+weQ=wnNDID=M)^B_Kr%FYS)kWuQ-bcYtG zCr^FNxs1IfGW2YJ1EuGbH2Tv*6B~JGk%yMjb6oz^P!8{f@?OY8i9D3>P$>_UrRP*v zlY_roXujNeU1-0`U02(K2TlKD%MU8ApXf0aIjS(NQtYOr*}UdH^K%Yd7NTUfxC0+` z=dft?>aN)@P2?M6-Y+D}T|cUQ@A|<0c7L|HYT#PjKNA=oI8alyH-@dPU4^&1f@<<~ zr8U;B^6wwGKBQ8HR1{lK>8*Lb>ZwiVJmSPE)9NuWic=^5 zFwShLUM#$-%h#iNG13p3;5d8PDAS{46IfFi(rZ{)>FBW@)3AD80j1MnYKdZ(m$pY7 zY099hz_*JydJ$je^NM8r!N?L&jIdMBVrQ3Oe^wPNdY*4y97vfTsP$PS+f)BHA(xDh z)fIy_RSf#q`1*b;H2p)4vAWz`t;s5$CdB7NhLO_^f^@WyaUFD{QMNKPCbKo1YP-Or%I_X8 zE#~W1eb-Qzzdwyoy+pTLe)SU7Za&%gBXtj~Dh!QDkKSNCu9KA0$YMt)ZAmv(ii|03 z;tbZK#-!^B=;5M;WXwdV zj>Qu1*;5nt>d1_V)L0ct5#Fk0fvpAQVGGw-bEWuEW_peB=k=pP={0eFLi<0%D;fuY zt0O_Z=7-+;n$0w8d3al<#^;K(<>EfoTtDA^PL6Lt`zojcj_7uz{B(ENDj1H>;+StW z4oj%0T3wOW%!hJw!|)VuefmC6cq>4NQ@pfI_Vi}t{7Sms$Z@*!A({J(+&MZ_cdU_n zw+>yP`#kqyyT9ixm);#`>W&>3nx{H=Txfv`SK#O>%K8KUZk_7)kN=O<&%95(#c}%C z^AN?Ft?6Gs`AtX#nal9g+RdNWx-8w#{N5ODOT(dD*0PXtFDLF<2h7agTeFAMu0lvc zI^OAfK&|87kDbYTVf_6Vc`woL=K817e0^JMEJ`i$^)woG7ss_ z)56O#jYZ4USA~yy{5k#w<4&T7a&S$-izh)VKQA}G@tBcef0zBM9`h&>2%R{f(8ac| z{_V>>HBE}p9LTzD+pxyS7N%rR)pE~`Z4z3Z_Km+gq0JIn-~YotlHVHZ$u*JoB_*tb zJmzLkXz^s+o>~8z&)Er~z(k(CIGF#<0+QEmG=fq`5ZTv1upjrszgHRcns!;LEAjwe z%&|G1`d)R{rMkjxM+aIiXm50f$W>+I33+g)-0SHfvLJIpB>JjT6GJ$0u-8l9PYfv} z07I;0vAm7HFwOm#I0Uy@K}ra5*Z%>Cp5yMqHGFhFWhh8FdK zp;Wb@sGICSu$7Nf8xK}l*M;euS*BUcCr*tA~CZl%yW=LpA?U4+BCqE$RV`|42O)g%l#;$t+a! zsR|h!s@bC+hK6dsP!Ge+aU(XhgaVngxH)P>D3Gll&J6`xpaSNol2G6y^-vlLSn7c> z_^EpEh61vN2)*0}uA8GKhXO8M)t+B-++@$DmiATTt4f&?3Z%!sO|idat8bN|z|HF6 z`cPnodYB#x;8}+(Zw>|SP!BUg0X+5*;tK_;)x#a3Ku|r*4h81Q!yFkTp};)#98}Nq z)$?5ST&bStspsqEdDKMHtmbuZK*)%zt&YS(w9HYLgaSD#w(tP;m!}X;jC%K1Mrjzx zXdgUkw(zkjAQ`w^jjx$0QrCP*l@f9@G@3&%PnWmRo0|g9^NQhPVQ)&E@xa@2ScCH%saDy7NPBBQMov11oq&$03RoB-7&FIj4lK(F&CQ2nX{sK;uR_-P>Jl^; zd<@Nff+q4C3mS^|=cel6 z{KkU%?UkT>eq%vxD(I`kpntSUtQLM_u^REe-c-GU-&oM&oLAjc{S?2kpt(5GZ>pZh zZ!G9$J12f5`2epKtaL zhgxiA)C4k90%jo%pBDQ0KSD1{%+Sl{!re8g!(m*7@-&=xdD^SSo;>;TcxHU+Nwvn5 zQN>^SUe$>_<@inry}X%+X`z?@!L4NO%OB{s7X7xBn~uc$k0iLEy7)rKLF{3Bq~#jgfY!V%91X2i!SfE3?$x z7ij734)4z32#!*7gPYx{W+c3`f2p~pyy}IT#-m2NrUgE}0H>nv@TYkaH}FwGnW?{E zf4OX0xTV{)EDQJP(*i98)9`r7n;8yUS-b|?zGu|E4-B3M-R6RtqTkC2svFw3x|bYF zC0WOakHeyAvQJ6;Dy+JmCD_}{qGL%)Q5gbZ%Inbd}9q1s3 zqT@rG_Iyg35xI150QX^{^R;eyje~x~Db{+~+-7dJ&P|aCX2F+3f7J_RCEMv>HK(rJNMI8PvviA`0s3MdJE;gA5j_-`&T<3&@O`_*F_9tzHbknwIMBE*FPE zlx8)Cci<5L1+F`=)!@j$Qdqp=Acx_iHYh{vVXpgWPwSEiDXxf2G1fz)c={PndUSDu zaOC$80qH_%#pk6Hh2{Gzy&wUoUG`W{@PcC{)W$L*%jG;Y)t%?vAS>w0J>}-Fbh^8X z@9#xO$`a(}_^IFQ`+>)tW8vy%njFSDXAe)FfC@#335=Ju)^r{tPcc3iw`$1BC@!rp&yQqMF#==na&$X3 zWB-!T2mKPbJZA2aEMQge1vGr>1MuZ=!j*&opDA~|_IN@ZnydpOXHp~^^X=T4Ih=40 z`ARcYnB?2Xx@*-g&Q&LtG_KcNEKP(zet)mMV`U6g8nvG@6ozdSE3DD4=oV(E$Fb!D zMxx+0W5^|~q0nUlsJ6+f>b+MmOJo|ny?9Eh%c^Xk9Tt+~L5mB-ihGMT6G%H0_Y=D~ znaiX1>Ri-#=m0Yfp>_065 z>#`xIjLBzK4&$;v)o5VXqJoIzVOnHXoyR zBo(+&k_|c`)fK%a>C5BtC0D+bYOIN6NdC{r$1fl~(UX&ssW0!#mtGCRm8twsONBS* z5X!~UspaNXNXc#jf3_(%|Hx04&mZ!eJXZzRO?R&bH?o!23B|mvPPHlJE6gPjZF1KH$I&o;#=*BAp)QGixX=pUF<}q$_eRxMVw`nm^y3IW} zc`Y;djKUhDIQU#1y}jQZ-jx;jxFGOJ0jD){Z7;yjJ!gp5rt`f#ZB6@@!1fCQ?dKYG zMG!kldD=&Okr*ptF}51MVp%W_ke?jR2{vk5;rlSFDs)A2mO$t@C0rRKD{)wobX6fmbacNDoK*F5!^`OQ-?~mW<)1k9XBDFxsu#j{=4Y;x!bhBn>C3 zBn>C4Bn>BV>sZDUUsJ{aN$`CgWl*OmXAB_EeGGW&#j+ZtrGbp#gYvMSvpP5M$zzUI zL|;)R8J$ZPX`;W*YqnU=^P+GSgQ8|H2X@}IPubT7*Q&x+mg_?oZzPnoYJ#?AUn*qz zUZJe2p{9j$g)Ri@Bm`FLUZJDj&^d(Ilr6|^aNBusy9@~qg{l$dqX2Sg~Z}`tHv|ctnirQLB)4t)asi|h%Dyg zqG7Os5 zSx@m8nZRIC*)gVMKI+)tU;d8%9!wM&yv2ItstH~gq1iI(mS01Ro?XQtGZo`eiD7{c zIyv+g*DBV?U9bZi32hKRc9BM&%y*I2Ds1Rw)ICjrU_+&Y4XvY#V6GMttjLcV{% z*VfSk7`E5QK;C`ayzEsEV=b<7$LXphI8bW(QrODu)eqq z>l~OAO;ao{xDg3J6d;Y}dUF*7FdXS6jk%?J*y@SM(|4{ge1(eNt01DVH#Cm48PsTs zkMy$jr(^J?w-P+KQ}8#p6D+5E98qZ>g8Ch^{oqHWf4fug-X9YDYNz1pTL^xVV2Fa8 zW!G%&`jWx(03nngwz{$I30SGV;Znh3`57QTIjXAH5lfmpmG(T%)tE|qo+`KOh9Ppx zXc){5whdD1l@TuEL{Vz%w~tfo(8NO8R|VQKj5>ob>F43kJA|6CC*77Qw`1hACF!;v za?6&fU%Cz6M$EwIwzbl2YDv{>-S94D2%BM!&yiJG4>}6=n@;nU1|sQHB$7%+@HB3Psf;8xHgSq$hbUJz6iAe8ZbZr8=IXlU zkNlAt;~YR@L12bi5A*^uH$cUp)5ko>V(g5-z6_tkW;9zXPD%w04}EMPK&h_E7r0sR zt(4V|aM#V&m-nT(qOTF;tSoi#tMdtds#EZy`w6~}?9P{FI9~NP`=i^XJ6Fnx)2SDd zY2=5U!>5yLWJ2fgO0tcdO}LEiRjNB!hSDZ#_4y;I_35fh5<6arEB3EQX?Dm6KR^dOtV1Jq)3`4 zC1#4G`MrK3&HeE-2^df*XSs5onxnCg^ z{Cd{w;Fn@(jMtjz(xGXMtVbCzG8xGrp$nvTE|C^mFQLtf{DS01kRnH=2$G^^d=XH( zBBdmx6veaYqB@t8lOE~IWOoGlr>Ojk1fnR*zavPIt5SRpC3YbCD+vz=QEqxfPT4+! z{9XCdM!ynDf5nkOwA^~6)8QPjlun1cT-oXF zCDJQuwuNL%7QZ--_WcOS;1as3A5}_gB&I7T@HLXHwVZx7Y_+tww71}GsT}4_vQK7G z^0Oop{)_XJg!K|Z&qD+ugj!c#pZ>WUiP_%K`L05Qd&gIpbBbY?ShIV~`NBBaE8-!K zup+)*WmGM}iq*j=pxhA>uysm_1j=JVf=nJ2A6IVPEq6J_I+WrBMvfx0rqP4Jdsifj zXLB#-(m)1kn23v0vGy(3!6yP%MjPaC4WzQ&juolvCtOQV&LALa2{WcWban z)D^HJ>*$3pc#*Zll0}jE4q}=CWWYg7!rN0z%{6d_#R86x!iA@@Zi4f1&~Ok5b(%PBXqOa{ zkjRoSTgx~F_Mr($_aNLo2x`WX!p1jS2WC)YRY+k|tLFY$EIWlOR@q(n%dxC~SAVrMu%J-PB(sHT5!NACY|s$JwEH)Uefo z?Z~fNdI(k{5OIgwunTEM+PrlBh3B~}|5zfwNo2{sTG15Od|gUVm$E>YGB2r=c^{JS zemmhp9rbY?^_iro8*=FT=|ny44|%Q!{)`Ob**w>KTno7#e>BhaFkvt9+`{!7*Yi*2 zxt0)i#$$P|bncB@?q`r=@a-d>XY)LPXE#@r_osQkjAsv56YraO&*bXC`*yBZxTZeA z>an1pASE>|J%f9xDCot#w~@ndK|yYxzWwChKkuk~xgULu{1y~^Z@{s~ zO#=RhfVYI}cRbI0B+qpn&p|Gge|PnlBEP9zY3j9m_jLQ7c*{t9;kg@oiasUJe{t>S zN+ItTxWCM`jB7R5daeS(PvRQFHJocA*X3L-z|d;LA+X5RkM>(;haK$uyMLrTiT7Vz z=RTh23UGgx>u#zG`q6JI$?tM5)gCFS zdGbe#t9xFz?&_Y|BQLKfPg&W$ba-z=-E(sLsC(bMyng*@kE<#Bdaj$eZsYnXS2fpC z%Gu2|;i)`V4c8W~xqr%Y_2rU#8JCyqVy-K>uH|}-YdV*}b?MW2u42;2a|3s28@c4Y ztLs|g`?%`3?%_ItF*b_pX5wGRJvq%8gk5BZ-^N`eY$X??hq@H256s>}YPXQN8SS_i z&t~Rl<>nHZS)WSreLqz5ATvz^HWN&uYpgGiNp&rTn7iCfWooX&pkuQ8>Z#XUwH)h- zaXW&4Jzi0hTCqy%r9#7MZ zHG}G#`L+-_X|+d2Gc`)9MXslGa?uB0QOZ&RUF270r-_%~lxZ&CU=aZ&d919ArIMHV zw$c(w{_WlDUWufWzl7|>FkP4UxpauRJiP5_K$q%-4;o{Ussc?wQ@L3|E^%Siq}Enu5N2(J zN^7k@GSgZYJ{E1);8c-w>vEPBXK1)M@By%d+7|(v5@&M=a4qiw^(ga&+!+(jbB!^B zxf8id;dFp!LzKvYZFIvcbLJCHV?88y>r9VG1<;?}td(_=ue8Upgx8m)Mm}O#>o%6A zNNV+Ifo+Bg)qOiHaKP|qyezD%z&C#y`YhF*h3`4t`2g9eW>MopRC}IqwEMKQ)v7M^ zJ}^q!1;F5RD1|1G47x}v*-)y85nSSW{uT@dC+=cq0=*~S;oYh#u*|yB;9TrfeUT&p$E?m zWTXer-baJEl#HGZ&7`|X^1%rJmx#7Z?~k{Exew=6qbWk-=((*D>au} zpKf(p9i38zGIbJ}to1lePI4)w{1K5GtENAnM~x5oj*?dQ!lRW4CJfuCM&Ju`r22M) z5uyeM9Cv`yMkvLHNLcGL9!S*Qi8Rb1X7aBO9(`6vM8cI=J@5Dfi*^fd2#=5zWjnUD5jB<<%){2q;>o`nS`b;qroT!hKk1;6okNIn! z>%iagTo)|LbN!s>aWBBj@%(q5@8fw9&pUa}dl6o4G4HShvx$Ez@2$MQ$o(s>WjxP& zDbF>Q@6BAUl+^z62Vbe~@bc>3qi2@fvwQV6)IB@LzUTJQ?|u7eWhcL)?@ZbXT3Kq| z#!~QoF%m*b>@|NYd>V$1C1vm~;Ye1Qxl8cR9p1@gp;TvXJmf6Sq=|&J@*n}oq$Mpm z4Gt>HDbf}U-Ui5t97I#%eg%ZWX!eCUVm3r12E6D$<-6Giiy*<}*kZ~|4NEuOC@5ij zNsvSY9G1O1L$P-iW;6b$6q6_H-IL#ImLdF84RoKK@N?0@FrO`PK{a$lBe%l(rkj1n-?P-dc&RembGCc zqLYh}1hZE`tb(w2X(_;TJKWOq#1_aox@T6Lyk-f0`AW0GWW;N#(Xh3r7|1BWLbl+O zF>1XQ6Ij`l<9;Qb1d$sk&3JWmjxw3jkda0~M!u12KpFY$fSuOpWZ6%}0dS%T?29YO z?I?kLbPlp+BX~F2BX2f>U!sc;!E>b|SYPm_1!}gDg%R9BkW4IVtzi#|@)d4wJy{Cp zmx>i!xw)WKa2y8?iUFj!KGuzYkiLCa5?%8kn#A9e2a*Kz+|-oLQVt=St&6@wnqHIi zO=jO_>umWnjZe~ljVu1V)-a}vNUB|i9cOS-97E!K03OlJtQFR~BkXCEqt6l9cNg2Zd2=3S zOseIA@#?ZJI?BByU%K+Iz(PL}NA+VDU8N&R<$F#g!HpCuUD1I(d7jo(?1@-F{gD^s zspPI1ziFid*I|mJ_!HGbd#s@)lF4|FTj_B<=6za2Jx7~FVGXq>ZVgo`N@FG0^rk$f z?2tl39or}8L);ola@8to;Ur4gTUBT+i?(W8s5RCPf30{1VRokQ82KHm5$Ya8*)%co z@*uK=MQ87FU`t70OJQJZ4o9H382;{ytEp8q4P!D!^iZ_AZEA)db5U(=|`M|+?w<8xwyXWt1GX7F=C1V{s>XM1?E165B^nmft+V*1O}FOK=A7?=H3_}r zpzA>JgGs+oNQ-nG_$s{!qFPT`YN4h~x8Ak(r?DJ(VgcQz7fGW%)5_9XyDD*oT7w8@ znAcT_|9ze=QCV-zGhVuh5k_X=k5j$&T2m~~M6xa5&$2UHYps4jG2XUqU%Up_l3z#2 zr9Qn$Zhf*{EohV#*SppTslm4Uslf?k3Pyaw93i$f+SEp<0Y+EoFPiDyb{d4Q`jbsf z=F8Vn@^uSerTWR~7f6gkiSZgS?9n0YolHg)I!{p&iFDu%B0bbaAq(Yeg;2WL2Px!n zi7`lGTxk~)!ysqUdnQ&+n#4Opa_XwSSbia2KR^_NC`zde7m$h_t(xV73L`{;d4hZ7 zn?~(FnZA@kktW${7TJ0R71jih-1T!9)g3Sz_U>uV+HHL736*;dA5$uFHBH? z%w#FokVB@CGX&MtbM*i9H8)&A!T_oSa9lt?L)VH(b+Y@|Krc&{?06XMiUso1b0?Q~#HmT#c6YcU&l5b-EDQ%}`Syi&6>nY!k*2OV86`50pT+GSx zeTSGD>|-GTp(VKjWnzw1h2=?Ps|4dQJlIA-?!azXhR5|&n(q>KpdI({!e7h~!OqQqL(^uy(LRQZ}tRB;xNnCz|Bj<}|45oQ8k`x>Ldn zm1Wf%R?RwAy^6JrnSVB0gEPb~<6*Ty+YPH5SW&4AKkcov^Iv>9&-G8PuenZt1(`b6 zt6V#|_P(Cy`oq#Z*G8@nxKdw52f%f5BQkj|`Tke#h2cEcja*x~GM44J26B~g$@d>| zpUd?$*G8@{xxCBKr|_+uyne*>JFXjF%X8hy)yyU7(#iJ*;yumPB=NbjR-m8YI+<%U zm&B86D$ldIe#7-Qu60~duB?^F%(-r{<2mu2dyw#lxnAIUQ!ceW^A=@ZdKqS@+6Z-` ztja3P&+tggPDQpU+7l%*basFaR04A?J7;MM`nS@;dUNuWYp#x*nx4vzs3K2DA2N8N zYeY4>m1Y-t#FDGzJT`^8jM{;WW|?F+oG2E0_s+$j9WJnnz3WFy@I!-Du);5M{o-OZ zG*gJEl+%u(W9Vp)`C++@eD+6AC^tEzDFu#LN{fHcYxPjR6JmwtMcd8GirkB{R8(bn zShKag`yB$^e4t}4B@2~~L;@JL>##K%&z-`nmWe?W)y1fCt@E+e9Fyq|G^Se%76}+C zu`*PJmUPN8zuw*5Nf^ygJL4*#e=r5gbW#c1=D`cJW?Z^suq)d)*v_zF7Uh~3W@0GW zDbE;~=AEn@_F@s--`veHE~VyncG6^(n!8zMcQZd!3*?Wn5&nql z)cwk!--5IFRUyNTx-@1KBeBI>r&s0`hdXqr}p-5 z+I=U>%Qpwqi&6UlbLieN+DyVRI#_GZ)axjNAYNr7YV3yU7Ne5KsRR?x+QzG2fbCV#seeA;e0&A*W)PQu~AStpS z-Kc$&erp|*UlL_>FzG;j$>e!@!D57Ou{_jc4@cPTziwm?)8Lf7Lc1{Vy=-^)M6D$ zTS!`K)HKq)55&uk=QSGRplnJY5tv)&qSkcBFlRt=!Vv+oJ);5 zS-=`W+2X1?dzI>J-|y7t5~Fqm1-6bU%JCaa@-&t*a^8bn+Z+RFYGnHrhBWQ&8u+wI zw^8R7Qc~frucmDq$?C3e{Jqr2YPV7QYd)%%5?($8bCL%8(PRssE*7=;ZzAzRCIOB@ybd{C zM^2FQ^%FBBL*E?u8v874xAqQ*Cz!dw5Pi6F+LJn@9dXdK5g|IAlW%8XMVSky;7si- zp(qhoWu@CUKCrLQcWew-!xBr3+}BxSYN^KEotyp-MA5S%O`i)5Cf9}<*~ydCNB+c! z7SxG|*AlV9d_*^ZuB%;hX;2#03!5P=AP~HDdScslkRgkYe|6fFPsHNdsM>)PHO&n;%pz1I4o zO;xx$p@1y}DcndfR9tH{>xjv1I6NWO3DJ@+YSBmF9g~lTlwQUhd}p;2r`n$rYk}Ly zSbxvGEzY=1kfp0nrqYDlCehJEJe8L?QUpfTwSU{{%Lnc z;TR+M4bL)?s+Ta5K7`wO8e%QOkpZ991>}?MhA5QbxLIXnxE7|)x>)sS>da|qAYIj8 z2M!=D5oH^=>mP(;i>l>fh%)q_c{pmCQ_M{%JE#pClkrq!%g+(PueCnrbhJo0vq;T> zT(Z8+Uvr=@_uI4T1KZoDel6B8*L9h20IpCXIV9`??uGE3_P9yt>7Imk5qT!Tx&)@} znyu0l;kHcLSVZHR!%+ZRKaw&32U<)aqi({;sR+d7ec@t+dettY?sCK`v~B?tg_lP) zjyPB|-m!sJp`P=Ks%bD3?Lu6tXH%C_a+Y>=d5qv1ZtB@<)ONFTDP$&$=W-sow2qMt zJSK6wsMf7>5xgK5h#wqMBq@Skfe)2l~1apPD^i4co zt8F4~aWcNvdiV)yzYtjTXZCpT8Mt_!m>?q*xC3Ve(84MFg;^P@C*r@0=_Q=_>&5dOeQ;= z*2hdWDoMEjz8` z08JKiQligpQ2>_a-(O16*HD(znsE~N6P=m4o0Y2hA-h^@wyQzQ&em{LcCxBf>b8RV zI!c^7{#1_; z=cE4e$^f7h=NTG<6NhdXp=hW0gWn*2HSv>5USLNym+O3sPA8sh&P$4SGx3xdSej}o zFOgwHatMV`iJ!64D{>rxs*heV>V_!<*+-M8kOi~uFoM5#&}Htdu}1Kx&f9FC5{y-+ zEr&MA;!9+du^DL3XXLy>8Z=?i_C4rPmH!uZdGwJH{4wdn z+q=Q*huhJ)H$rGw9$MQjr`=>W?hM=9Jzdo0kN-{lvBbC0ZDt%TCKI2~r;3f*lZX&& z!VC<9$Ot~;WMdh@KEnRXbo8n3 zT6O8?qD-E9jMv(kAk0@ilOih>} zc3WTWMzHI(GfF_%s|m*rrpYbl4r{GfpCQ_bDkYrjZtGP^*1yxZ5dR3`eh_V-7DRe6 zn<+_@R4#w)tvuIxZ)4+<^hayDQsj^OxOcE^hmzdLecRt9ZW^q=0z}(xKdtLYQ9lwZbYq&qf2EMQEI-A)lH6Ad*F8`OPk(AyW#Bwdb_Pa zGY*0MXBl;}9Y)NKD?Rl??mu2p-0u(FBJ5r57cIixu}0!+`xbZLK!*R4vf7RQ31!$s zk1MNP?JsjSFP*!0bdfmF@(r^`GUgXkil&R}vl-GyQzJjz34&mm(?`6v_}@cQCMJ4? zE~9Kc4p$JzPLZjk+4}H(;h{RDU9R*&e8i@WxYk#6TXK}L+Q0kq01<7^&x8+`?3v;v z`=r$8zkApM2L0o(Q6?|#41?^NjY%YdRXLRbY-M}E*gjxwgp*8$+|$$uz=#Ri;w{s7 zZ>w|Rb~F2L+FK|7y7YwBFr%_wJtAyGQS&zn=VM@z=X|cD8)#C4aqpIq0VB+rYP*i}IE0`VRg)%qu}T zbSMf1OU(oLx9V?35x$!T!n+Z@Z}VzXUFDb~{Ao9HIU)zwSMWJB{tQewpbD;_h~&NI zXMwhnMjf&!;dYKXPIEg0K2l4obZ%;Cm9&U2+`+tYNzn{;&B4X`rHujNI-FShsM=jxJ|L}H1_2XC9K zF}%s>d z0ZG~#%ew`s;NDxK?a_R@QFSXCwJ#u8w|+sv%DWAYZAReOrlj1Q>2aLfh>mkP`+2^G zBtN$mo#eJW*HG>cb2alUQ;b|Gsa;JY85xI|W@2+p3A#e9DTH8WOnYvy%QI|+H1*`z zRP#Ag4K7!iWJ=A?opmRACr6XGL#dyzE3}wva7$A^2j6`G#oddmwo{F|YE{c#L%*l@ zvZQg?|&jA*-Hh7y=g7| z8HWV~R!kS2GM<=g_CJ_T&V792$>l+c^bHtNO*YX6-Z%5fcgB!L-hCP5>3;?~FH1NX zJxs(9jbc0;xMr&r2jyA2VXLF}6H7kHRIN5$N)-7z=@hU=w*B*h6zNzaa1}4I&|+o9 z`rCS*c_uQ6Fx?2cUH>FU9UHYnBzXVb(oVtSzhQx zKGH@Wi8y+QShf4kJ+B(3!lzT_6KI0cEs@ovs@Xq3gPhMdf>Q}1kM$I8 z1TT_^lA_#sC~+Rn=Ha|*3NeC%d5i>f+3U&12uimMDUpWiE2SpZpJ>itiu{!rPHl6g zwj}*fT44VqspmM~@j${%Jll7LWOIr9OpqVlmN^M|C5=A2P5rFOBCS|BlFxn8+3R&b z_moUB`0>9az)%ZUbZO20+-{V5vhUBto}}v!FcS5*{20Mf-c+q3r|~3_h|exbtcW64^F59SGN#MM5Ilxpr&WeZtDI*GrT5Q}R>|?* zPSL_!B|Ll zC_R@?=qQLIFTbF@nH?HP8Rb;?cl47<)cPCWvPEX)MD$YX5Jce)mS-6nP{9gx)8nKTFcr5p90PC(PZEJXiL1_A7Ip%lken z&-DpBUk>3f?8tMy#Z~?>^Lx(}xK-JBEnI6i{^TxC`mTSwXX<+o#lt4-&yWS<(D*EF z?;|rup<-!ctEw=sZQ&?yEpV^KA{=La$|7+)&Q|xa@2P+%#l8+Hl<^vzPwxh-3?ukD zv`K98p*)7~R9VoSq3qq^N*QZN;Tp~&f%Qj*gDe2YqPo-@PFuulGw-~uxE^*|)X=&gTd zzPl{P8F;!zOQ=!Je#IbX*X$Pt*{FSsFmL^{f*DwcSJdD9HD-k$rTP1no0p=z1e^L; zr~D9;X)**At)(~GChxN2U_CF2b#$28yNHFwk1}P3?cHRZU$v`!RDHjXNet!{^6MS+ zhA@x9f3LCby>%Dt?4>RhLV`Zl(rL=etM%d!75Yh}r+JK?h9AwZ6_jwvm0FD|dxtB* z;3d86<(|~QbXlQ(Vt}Qrjebm8*=;mK1Sl|kN!`N;lJSE-xwt>WL|=yc-~`PbH6P2W z#{3)>P#8jlxokO*^^dTC-RjHQg$b1XIk+c81`%jG#;ChXda1tGskt@>xL}Xw0B3Ml z93bn$VuaZ-QBkvX#y@3o!ukeB74}M_**f+@Xv_sN0zXMpD;L|b)}_dwBPUDvcETBj zYGu-FohRW2;Rpqa6Ec6BRld8mPI)Ksjq1WP$#7M=c#>j2NEi=q7HdCt$3*(Du@8|- zcSRM?9}iwXPdD1aFLC3x%W5O@$U}sR%;WUH&+}cg9pPWIbrm16H`YVZuAIOtYwCh| z@Qc?=73@k=7410e(tG$a&S?&jN-BS1=S%P~Aw1Mmw-yA^eA|}LBczjTCl6s3cSNtv z)_Z@~s07hggFuUEN`wWTy46?W?E)? zMtX(>bnDi=yY?Tgj|#l31O35yG>s|4g;BH7UPf>tsO?SK+#%$LLA4WY;tKp+a>&jB z+lWn#CmBd*73*1?H7vEvjM(Y);sH@AG`yW%KO=a1%GTNSGW#J0^8F;MQ5 zza0YP(%W}e*^UyaDHRt?r>DiUnGoV@6d{h>KqQA6K1fln z9|g6(yhuy{N?e%T=ZAOn!$pd0sg%8WN59tv0H=S3uh!4=Tx+m607# zZ4etWc!Khbe8W%-?m#b#_g+n)ExM%2AwCCl!I_YMOF)p1ZZYE|hV_D5=i-z? zzeFoOsK5If&cI0?j~IGiY721Gxb&KlimELYRc-bjCK1;c1@;#j!7mUV2lk(Uf{R)O z_766KbCE{|_Mgg0-SV)5I4o|p*IP04Q|@oYQcpcJQsd`7%)wx6@nGD zy5kbIQwX-tI!98LY6ba_)^{%+K~-rWLNX-*b&#v9ct!teW`C9?Qpz+8(#r3VpeQ$$5p^L7==qj94h>M%N)w6)w7% z_j2e&jENH8lSzXHWtv^J^Kl2`IeBpwG zwz04GyK4WdSY6Tn($5psBMzHXS1dz!r$)#o>&Ywanpo%U-@&j^LxI(v+FZgul4y@w%JM+DkOpDC6^`@?d} z+TNk}9^*B*yNarP5CzL{yhjgJprEmMHZDuDw9zN72M zr)U9}d(#&3b+@m!{tMC*Wl*lO9)@@BaN>Zp^T>DpCu1-HnH5=ZUt%w$Asiivv#hHb zm?GAMLPzq6n7Cfd7o92uxuIXO8wNltL3gjCSH zYAj@fi5!L17Z$2|Tt+(-i$&KF0nL3wucS*gQtB!A!{FlWCw&R4k19BtguGV9posxb%5B2rlfD|H{)k&oR3SP> zp7f(o=B>yBn;?=>_(w;`lmtb}>j~{uMSlv>@qSVTQfXv;DEm&4`F8;Zt)Bgsj zpgGNfi5gXE+g_^FdIi$kYis&NdR)+`ntB~H8i|?yQiy&9+v`<}I!33jg$dv@m7>I8 zwA4^>9&Hd;xf?TuHp6jmHeyiowLH7U+BUD@NkhqvcKZMgoUjo5f&8$4K#Zd^o448Q z1wC1DMRhULst`+`!YFWLE{y^y*h@qkATGo&t$;}hP882w1qn_~pf7!a2wp_~6^wf$ zBD+)M&4?uIOGE~Wqq`?SXNcHRF<}ppTHb}z3DN2<9DUD?)*dXqQGJP`8G-9e9Y{Tc%QnTm3N z4{BQ?9zr>c5Dj>UAcWc~gq9*9=ALpeq{Jf!kBS0y7MCeEM$*Xa=zoi|mg?!3)W_>{z!H|nRme}f~<&)G_ab+6W)untI z<_R3K$)`anT0XHMphBGSIINT9alTXig`-4(b)D0?*aXS3=(#I(l- zeuoB#j15}ms6BNah+3Mu912Vw?13YWv>iRL!@vVsJ4ZS`D-w@`q|`fL2CopAcyS-- zM?MED(%S`wtams4?!a=?Q|ZpQ&o7_$D#BI*MZ84Zhn-Cr(BS!)Uq|EB?LX>t=9Hn| zfC?Y~B|?1yGhgp^vP+%Z!#Z`5I4r<|!8Uw;gN~lid6pvai;UfU7_$9zGqza9&Z|Id zLo+r{#%5L`mXi;~--FX6(f>wmHsLl~0h_JIbi9fk6348FjLZI_ED$^_Njcc5?OKBwo2pMhO|^ z{u5CNGU_`SRg8+4w~$fGWz+zah{buk5ne%9fbca!8NvaCX3brWGZ8L9@FEOHxCbE<;TeS5 z`MFQl@$>UiuZ^ES#)}*4dSK^+8xAbbw=r^G2y%;&=jVno^7D8NV`Nbw8M!hpjFG$l z#w~f6Sj%GMQ)TQ*IB2#paswH=lvvAR!b8?!^U_zo=tV6X}c_qtZK3BKG&0{EeyvE%3tnFx@mJ*z_7p2h{@k{}M^!EDGcNvVX3MA&5 zVFn~GStP^EMK%MH>_d=XG#I?OTlS%p)~XM=Nvf5=KR7eP5Mt{-NUEa0lhI4*I+`w4 z^iml;sUf22;F?N3Peu=RBKmKLXcaw6M&F73Nycwh^n)_`b!_G}O04M7GCI%<(Ro(% z02#f2V83QXUo4~VJ{i$uO;SakE~7U!LD!k6;u_1ir+`NJXcf2rC$9D$NQv@+D()v4 zHx8sIze2^WlW`|ey0cZ>A{qA-h)~{A#l0otPHxIGa|nM74($3P>fnIE1A_zf5FQ*x zR)|eJ6T&RLM10{^9jQcoghx3?5O49L>{EvZ)Y_-co=tt~&Xz7m2Eua)|3+Aa@IAs` z2u)hK932sQBBUUULhvCxitrM`M+nrXY9AlYd?cbj)u8XZ(#JMFJc$={D|et@LRS9d z7N(YS?n12dA##3Vx(n)!yXh)i_+cqQP4L+oJH|sLuqiMS_#(ql(s8FdjB zBK6pD;76|h6R5r(JARf?SE9%3v17fAdIuHPW5*I1H4!ELH%ibRp8l}H`R!Ha*s%tW zYV26Tv69bQB-hI4S3OO9{;xJJ$0P(Cta7}8un3_9;a7ws2rb&W9Oochfsl$Y3gH2S z83@lK=&|GY`2C`XkDuTFeYK6>U&Bi^`p~5d>^wauBaEkW@fya{r8^~0H)MJ;Y@ePj8p8?aoH5#nWHO*d4gu+s4zM%GmciBi2&v zUzV}6J0jNN=^Pmwe-2_Tp1x1Uew~0=i>HUn*p0Y|PxG`_MrC$Flv3<3kx>IrN0ics z&XiGKpz~^;cFL$VxWQWU^wDix@pKSL^RywOrg!-7c)C3sN13NXy-c3oheyTJ2FFUC z?%lUmo*p-gc>0odF2?|be<4gl2qL_Jun3_9;a7ws2q&KcIgW5KLO+C?5$;CNJYBo| z9z8h9~(+WxO8_rqXhNQjxH* zyvwE{@p2nfB{g>r59?N6Z()v!Jr`_hb*lw3Hnj(0 zt@-jRGS)?%!s=F!%Gi&)A=c_v_siH9FF>p{%@`qL&$8A?`Gn<7|X05w1hH9bqy;0O1vcj}cZPaDPgI z;)X>(`(a^a%t{KppMdpI)EE|bPvDOwSYd*vec)^w-5 z(?=w!!!=2PXA@gHe)e+&PS z#uIPm{BSNg6tS#jktKA+2ekIMARz-8;3&IZdc(|t364Zs#O6HAQnp1iLmdzQ<}HC%VBmh~wu=mnWS#625c_ z=&aj4jg)VdSR?aSU{~R8y1U#*E7Gv>!PpGi6^ls^(xecFT4IVzj^&<;dX78lqxi z>rYB`X7$6Hzq}C+Bfw62Ze}?ymBck0&8db+4tGRZXQ}k`JR1;gnnCqiD)86&n@fwTF@_fErp{=oCM>&Y&mq9gWSs&7~06c91>clo^+XS3_msV zvHv_r92`5+m)6d#vyq;k+xhzN?`h8L28c77>kl{h-sTU(d>#3Bgl{(gqLII+Gl!so zqrStvjJ5&M3zmo^L7HMnqm@ZVNsB092TAo;h%>7!j0~b2%?L~%*dNzb)*D;>0 zFWH~u1`c&AxgoEpr!E57k$o3VNk-E0#`PzZmQ_%k!)76?s%)j(qBtUC0LJF)Y1+QC1$su zZ`Fy$73}TO6z2;|A`u*P+z9F-{zKdf9XvTCFa>L)E-43*vnqs-~Li+^(u6j7z~%|06t%_cWW{;LPf&pNBcK zUePQ&!kK;TzN#$SyXb$+vR%7aENj#QriwzmC_;MqdtVb{H*|woaUZ#- zyraG+c1)wpki!A@(K)aPKsK{C6T~>+B-%sFjprCVw^GmbJlIu+xLA)f8wPDhaB4f8 z6m77Kj%Hz-%^b`iu%A;+^B?Yxt-m8`Ofn2f^f}tg0E0F|=>xRtOeC$7g`K12l`|Q` zlQde4FDne&nT2gwrDDqIa*CPInW*EZVHpQ|)Q;o-?C7qoNH@tG@R0EUhTa0L>|)OKR;h1eH0H6}Zh-i8~iyoEfE zB{sz zkT|W~%gAw)iQ?s9C^8}@o^2S-aY;dMI}1T-_4bhn8t3c@ z0ZctQ8v_R!1?|eo^?;R0hzlaBv=BM`t1!bp2K3RK#sU0Fdd@t@PS0q_z5r;E$j*>) zRgt|rvgq&I*Me-X$QQ&aRmt%3DUu9tb%L?Yzq)=>EbfiQ5_`PIcP0JKMW3NHbY<4V zdj9jYtAq9yMb<`b(!15T39wAsk3gZk-I-+>wi{r(^VI;z8%hImBe(WNfCEWdhRQF6 z)Pjz3_o87c$MHyAeM;T*j=)!{9*}yKyfZM;*xSFb;});U~C50TkG4={6B&L_&5hPP*bD+%^J2(<#H1*nv$wt2N#NSqXEI$r~KfEs)v>w^YT#g8WQE2I6v? z@Tx?1$k)K#>u7p^Z1*I?Twn-Ha`okz1+}FlE@CIB?Kpzoa;i*na(k$S(37{>5%S}$ zW`wXm(N>-7!*Y8l_e`@oNr91#`~~&hu~-n$6+%6xrC{hm=j6K7RttCVn)=qB*4fO; zX&_J&E3#NLU$l!`aUF*G?k{2A0XNzRTz&=Cmc~vQh3Vvbz+b+c=DWpgwv&)hz6^6_ zp9zz*C;Jy)C*au}n}500jpg-`1g*d^7{=g21~HI}*b*C`8d!y69wZ2h#fMO-j5qKt z3atlphetTQ!iIMgAR50wzE-JKpZJmn7pr-=Wx`JfVW|V$gpKU$ttzW6@33U+ z!qt9v+9}uW@E=Go?zYuf2?gGj|-kQC$vW%;%^& zb?+6(Z3*S0w?L4lHsUcs4i6X?v=EJ51L=qQxE5Gauh(;0>XrIh%aQ<`v~9)^et2jC zjJkkeR58lG5LkO5>@)C1X%g`3=n(hzrN*f7ELIvMI%a3G88cH8aIQnn1|-Lf1bO+1 z{HqPd3y*kc1RPH)6&+AH#ZP?MqvY$V2PnL0YGkDKk#LT|YKR$Q9pE#!a$-LMbq2Xq z6}B~j(5B83wtQOs4wVT=UUxB9brPMb{ zwY=|cvtjb_Sq*?u26Or(bvD_jZM2R$m{SA9qYWX}k#(K8U{xqoe)oxm4<*KgU%V3V zwP&(*;_4@8s|cihu~^xTx)}8~+V?)VI`_!NOGu~pw$Lx1FWC)lrG z4)M`9IMPfLP~*tI0EQq_JKu$NA%_>EAcL@fg?6?q98TmY!g-x_3uRB?0~+~A$^0nO ztrt%qoqul=XBLErbm-KcAI@`T%>hOIp?c12?34qnndS0axYV+7UamR5ACCgdn})S= z9I4Vm8>ZIg;jNZ5YA`dV0Kxwk=oq`1#s24Xh%^3zqW}M(##Lq_g|v(eOQLKj9`$kl zgB|+wQIO?$R6E8N;*s1jfKAC47SsjIMaWf%oWsFOxY-4W?QtN?^xud>NZk&&Pm6Jr zd9NaX8R-@uB5-Hi$@l%=WHoYN6M-b$!-<` zX8G-~9fl9VALx@1=f>$|+(QgoBaWV0XW`XtktKXvM1rq)=6Omc*K7;VejWY+V-TB+!NBaPpK;_#ZJ##Hh*Dega=NY6vZyjD;; zrUL|BE}Qir9^g*wfO6DAdF>g=quQm@P$oP#13-2ewh(#!lRJaJ7jeuCb&UL4>{K9y zd#pMg7sR7J!Gw_xU*bjY{9PfV5wc~jKsQ?AKXQgM=Xa85!8X?|5p?fZ`%({fNzI(T z06%<*fmLK2io{hEKtdO}=YiUb{CXLRB>y!2X^HimGXo4j*PJl{5U{PB9MJd zkUhgd_5?`m2DM???l?du5Xik?QmeY{0BAkO=;}I;1JJgm07QP9EI{!9loJMMHZ1V9 zHPurDXe6=)`n8*1&%ENRL~H>p#L>lt4>VVHD0vf+)i4ckj-s{#x}$)?eqF%~4r4$5 zP&x6oa_);-&QherCQibYzyiyBuOOqiK=q*n)IT!4fjbnR63Z!I`xcWMkQxLez zsSwvbU@pCG(HcSh>xj9Ep|Z6HM8c)lDw?kIWJFlg}vAgLC!tfUFU8Ic~AI z^C43Zp8+%sP>xBOpX9rR$XJ$STdNqK1=O}6g@xLk*J{+JIcZ$StPrxFNnG4)51Bov zloeJfdCgJEvtcmK0t}>Lwib`(g6(-?dcg;~aHN19X}%PXH&juz$^AH=q}s$A(I)V+ zsm~=%Nl4T(pE*Bev2G#J9cb`DzhZGV9^L9fUd0cs_-hJS66aONxNxlowri2ri+(YR zeGB7$4RwCVzEzgbNlySVsf=@ggUfpe?cdz*HKGv5gaN--<-MnLhMA`QF?i`6yu=&X(asFD!{8*Bb+{IZpz9 z!wz_MB;fricECIFRu%Ba*qdzv{u5g-CE)&cz`Y{@zZ3@e@u+}@;gp04_<0lXAI)r} z_ysRvq_`#w@Fh_Jm$bA2PBa0}(Xg~6IXJ+ouO#AISSdDs@2KlICNjq=dwd4wkDO%polqK-6G71hYy9IdF&cB819Q53Bd z9b7-(I=oygax>&zLM7ruz*5XqDwb=&Bs8cy##E{GGMr#Bo<1ci)E*|(o9s}>M?!tC zerK3H)qeJ~+n#MKO4z+D0)FD-%ev4y}#!e{MQ7Ay}-6eB=0OFby$j1xwkfuN= zITF{qalFiU5>RFKdvp%n80)I!c$jFmyo1hhWn_*QtK|5KcngPUR2fYobKF@a$7ZVQ zEoP3=#t|%XS(O~mQa!pN2R2ExbHqCw<^dRt7mv`4L0;4ac9et2#@jG1>_S4F!93Ae zEP5hCU^S8!9IJ<&A^zXk**rkF0~?8%CznIx`gioaM==x@sgNPvmLbA1=7>{gWRbSX^W_XNr)MNx8d85>2yuxR}t0q4C zK&E8n{urou1MM+N``$ir_|UxC!W%|zMi{m^9Vy0~q}Ws(Z5YwE!~2;+BjbKkXk3B} zMj7=fTtb>*YuuSIph({RBoa~!K+;lOO1bT)flE$0y(*NxCY14!P>Ntf4To}w4%6kI zW|7;5&fgDfbUFc$>WLMit@TnV8tWq9CWJ@1u_yv(X7l-Vv$zSbQf$(oHz2J1QVe*F z!RzG@>$bKPo?#aLwx%!nF?rs4sT4Eu!ZJY@UhWAi{Cm7wg%{(^syr1w0w-y`@TY;% z7HR?9g<@8!b(g7v*a>H?#q5vC5hyv4rT&tKLlpd2BJQrwIt=xI(fr7+Qf|bfF_PM) zYVUmydYEkBZyH3fO1CiVNEf&OBdc-NsZjxqHUT{v320|hl+kTDd7;?Z-hw4(OcECl zK%g(@QlpiK%lM7^Ql&T#FDZc``Jgh4N~(#oQG!-vHj7Dk(`02@4k=al00rBeiq=iZ z9a6@`RTQO}|1}@kNqYOeCP~}Jnk04d<5a?ltSAn=8YdB>Rvk2Nz*!(!m4`rl#i*6s zK4EId+G{MtzZFaIrV-B_Lj2GSXE}|pFkRP8XQxsJ2Cb>o!d_5)yi}pmt+v88i)(F# zMN;WmoRp46rPDvOQ|an^Oe*b*F{xzm<48%RZ^c%;8fDbZRjG6V4)#bY-2hrLVf4uO zX&*Z&i1{+<@a}vXp%oYVQf1zD$ZJx#sRo4G61n$}10c;B--;1oV2Q$Cle>l`B;Bjx z_xL1@--3E!_*IHlHb6nCEUSAQgKaKT3MgHSLmilS_-9O3j5W~YHTLqBI$49EfNs_x zSr?^iFyC#j!3%f`=aKoSfsz@*k{Pk6613nlDtrtIx8%j6I4-B;MQ?}MY!~6t=owwD zngR7vV){N>)5P>uyo7P{u|zwj6?hBB^hY9$lx~7>(*%;elQ8}AeV&ie<7**MY&K>+ zG$tjcw{jm?W%)%Y7JsH|oZ4A9m5MGJC#{r?3&VmK$xEJYjsmc((iT)ajP-g}$?-PH zs`KIUHVTi7Gx3-OK#De{;tdT|7v3h$ooq6Vuze1FPIkS2kS@=NCMUDRvMZHEOs5*KKHet<n2B7N6&D4rRt>L9Tf`d3X8=?+^kk-nW6MCCP}!R9*i#RXXyg>9b) zR7Iad5hU4j1y|x6@m5uxFcMoo?n%LvGbJ6=JCM zQYn%(Ts_4a2Dmi7Nr@|x@oFhMy=0*q!LQ{Lbk%JozIV5-?H`r45`VE?D#d2JFl*=% zf4e-a#LalMO8g3M5RMnY9u2F4v`*86F11t*x%N5T$O`B|(1kFmD|mxS(UUqq3@B<) zo^!w)l!N@p!|-%G8jso(N?WBiLBo?xc$!9COR+8zyVm60X+4R;s(d)d%>5%AhKBWt z-94#p5i+pzL9pn{HdwwsvhY`tUA1AQcuK?4!{TdzWeq7$Gvqd1;RMjggGDC_YdK83 z`q}|s9tqeR2KWjI*pKx$qaks;?nesA!=R&yvrD17I z&X&2S5fc>IM1p-zvIEx?nRNM1-C1PnG{0PGr^_b1fi5M$rY4q`Q<9Oa`il*);92bc z%)d-_f9Efg-Cx0@@g|SnRm{?V-l?nC(~fDENKCH?!?df!^jE6+AsQkyH&{CC^8j44 z4!ikd@~!~1#1HfF6iWz39R>%QIQrlY)ud*Qx`fDFdYpX;)6ZFnj)bwU8fXp^+Szt! zmq$YLN@%YUG~7uXjY~fptBH36Bo6E1O=Rvj_9OR)ppazd1(C^G^@yC#{6S)%QDe1O z2#x!adb}bF3^L0*k`B(7+4{>HIJ15L%Vw4z4s~$&aDFhxnf(d4FyNL;4~>G?w-T*P z3~!1odhEqat?v6!UUOim^QmdWmOFIaLU7$_61`<4n2r+6)&pcw)^Zw(WGWO%V2(%_ z+eYI02RVrs9bYl0rYGTk0_I)noT4XoA*e~yZd?&y`~@1&eTeb6utMSF0Zvhb#RVp+ zKA;-b41UlTUFv(PWM}9LJV}b$2W^ae;|(tbA6%Qhu?>ms}Ocn)u< z>xA8?>un9d87o4etN->N!36CEv@bq=_dF(OX)9=*^A|ZodlWx%hE`Pv*?haE%;|P4 z;v%u=5QaqyiN#~2-28~WQBm6K5fkuA5WuY7LMD@~daFTlYt+cY8|u)VWOa6Qz-O6& z@6xcetbT}dM+~{&m4j60fmBhD+yc!(cG?b+ur2^BBQ|=r*UfAKS3-pG;=yiIfd~}p z?F9<+qE}}qVAZ6UzFh)5)}9m<{r1R9Y&d!b*sz8bo->0dZbb!*j63Y{CkifV(b?qT(VDvocH6qZ0=zKCYjtJC1%w6q1RFg*!yCH5J-fg&)1wbb zXPSV=+5r!b1UxPb@CXU`v%dlO+~|P6f@&fAgVPSU9$pwhjd2TrvN~<71pFn;}7=g8cNV8Zy08oNBQ^B4c@hGJMqguPbaKa-fcixO8rqe~-C<8PL%AHsZn8lmaU z*cXa05#cL@hQnPB4}u?|0Kqu|{_7FmLpX$R-bj~Y48j8l83;=ejw0N13-*-WihCOn z)*;+K3h@Xt{^fG48SQekO+%Ro8F;Ry9?n*+oo#eII(F=MMhE;+|KcP5&FVA8<#-Wc zA41|C@Ii~P0wIL(#8~*mywl}4h%k5@eDoviL^$g%_@+f@H{Ru#gV647;D_)v!YQEb z3kVXowrxS>w)!8)O^=P*Hg3yA$Fmb54gu6}-6rCnxmT7R*dJ%q{e_Qp)cXr7XxD7! z@*Sbjr*QQ@%7cr)b3K^H!12FlY)W7*`S4H5OYTPN;?Zm}4`3POCXcnUW!keFCYdm^&+3tfXW883*ZlSaWQ z{OC<971Qwugdjy>&RPdePjownAuT4|L|o&yc?aO!@PApl8-TtQIX|nS#OX+dy*~-0 z(?A^SeLi`p43L_cAb0y@@ps(9jZ1Q~I1Y-B{Ky-As>I=X-L&kp@^|UMv)O1LmxA- zlR1z$cL!PMzv{Q$m#~8c||QKsp0%4`!n30?hc_ z%k9Jx<#aOu)pM;V!=sHxKb^@m%2zbHs1v_x8htu2j7Gnt+Gs=!0aCKQ3KG{qBrzUk zGMU$kf8o(+gS_qo9^0O#bs`VrJ#(t~5mzNBo~;z$STChwC0=+-Gc_-9bSDdY8dh7& zS8PGS*G-}UUNIv`U~6ygkoD+Ce`1P;(SJR8`Xz5LVOHNV&*&Guc-C+ z22_e}JawodE)vaBXIKSQiU+NiQf|MnQXdX4b$ocKBW0z)a~&a(bSuv>=|)5FhmUy)+(fL9e1n5o5+r_ z@T(LzSudp`B@)@;9VN1=RZ`@ulB%LNUePLT$+GeS%&&w?*jnE@@h)^ANurAFHWK}8 zy_AYgcnNEjUloZ|shbpu6fYFw6{W^esR>kSL1vhW(wD*yTe#XkgZvsGGUA(X)5Zk5N7Ski~?hcH=ah4oS@KF3QKeO7e{Yn9LC zt8SHfcm;hj$by2~k)pLqYqQjwt%52=s`XMT5+h5+iK{a zk*)F?RvKkR3xBXxbg}hPDn7tVSVfzi9@Z*MW1-ZLW*l!;1#X%0_anIUzCoLX%<=dy`4VR3+RKF zF#3F{=%b4KK)z~5cm=O0avqAL@l>r9V#r8ck0FD{2u#8C{!d(@lRL7EhmB`*%wgl9 zt>&rO$zNlLx=T{7ab!zdCP&YM2I};W+)}$=;COhK{pO=2J!`q8u#8 zD`MU>M3PH1_BxJpR3)MW-^!6|=4v`i>R!~=0rKPOhLk`IE*sq>512wrinzwSQI%V$ z;5wzcTc}i8zUoS*3HnjY0)J(5XAUj|q4sX*7a<%Wy>gmTt?5z;wR%fvwi`sejYCf2 zDzsZl+8|iwF=Zcw^Tnt}DZHk~pA3*UXL2l;WlzQ*l?ZH>p$EKHqDBlMiUbqy8j{UDDe~Ru#?Rq z4-p2-NDi?OnCbchr123VtJFVL*Z&6hfz>g-fhJ|1$CvV}kv^gtnTR*5@#e&;)ks_` zt06X#c%b%s1=J72_=8izFb0x@G?R0Kh4efCkkqlAg~!qOQ7|X)rR=vFSxC}gs>^XF z!YSY*9X8nBTRnpB2>KiSK96_R_4ud8{wMvrxdU6m4_?j(O_Q7papipy9hU@CuonsU zG`N!g8>C+8%=#Ta{Jc>)f2a6ge4{vSpLw}*iRcR5)(GLFB-q^m%{)aw>4e%8)x@c^ z_l$agMX=Ypn5{hVJGAm56H3EKC}YsmiBS|3lTjW&??9;hL0R;lpr|bk*Ngiax6a0M z?acmLAG3oU*$AF>OAt>~2ZFn6L1#ONShoox4x1y-eilir_PP;X4-!d2 zrUbr9!Cm!fxb&LO4Y~ud?jHH?I4W@CclL77!~bAwhclZkQOfZDAY!F=HcY4B=T%Mu zq`UCCXu!D*yv|@I_aG>!b2VaTx0+V$gN-$$_uL z#gMU!<#qEVV)Omf-?SZi%Z1pa9OZDYyuVJZ4HxD^o`pp73?6(*%Kf>49_8u^*O}@- zG>*Ws5cD95sG4#mtsaS%9frfAOPuBzXf#4rLI!2Q3^I|f_wAN~r*1ejoB{_AJu#j< z_eedAW>B;_O}v;ijZjH}KMf4n@xqq{^pTW=MnkT*-Fg!opYW46^LOe^ zDS_Nkt>B3!_|Amsu&?(B;U@6>LeF!=6i`}Ha;MHq4tz|8HcwW*?-a<$VDpdHo#hZS z^98(!1uJHutpY>tbq9jqqul~I?hX_`>N%SElcQ&bqtJuJ_E0Ey}gP9JD)vyizoRpis4PpB`p`Ez% zre)?KM?CFlKzXIWFW0kkQD_>l&Yk+AJFpsM=Xy3f7xrn{slb_3kn4FPwO8gaSYOg1 zsfxv$Z&rdZ+EK&-Tn&X|D|hIluAV!RWrspix$3k4*1^P!LdJ!#b$EI%x{Ux`lN`t# zhIemvzV9+;hYXMmMD(JET^pCuYx(_6QhK@DIcL!NqPN$z?VLH2=_x?z({U;K6Vzhv z!pyONCEE%95E(7c9p1u1j>?_t^ae62GG!rc+_2JI-Ut_Zlin6cj3Y>UGNw+#M)E8! zoZU_ZQlIm7S?xVv+=o=(RQ6gkwWl{!2peap0Lr>E7oU3_ncC4Ce7q7WgtyCfZ!GP; z$Em$u#T}D7+ z;IZq=M=Dvv_h7`_n9^$&5gQ&ifogIfn;2{vs_$6sndZoy`l{ET=LP8;zNTmel93?) zJWt@5J98(M1HawBHE~X-g-#DR4$bQgc$UF$cdWNSG)r=Rz6Q1=Z$Vk3lvq5iJS!F> zOmZ-HJ`thdrv`m4uYayDjgN1x6YrEB3zd&mIFN=%maoN?_?MEK5f3EYd0YNDjCF+F5tI(!Y_ z1KsC@8XintC{|9U+a}PT0=F-NQU1lG*q=e+JKUkdq+G^4u{Tr|D zgDXSWd)CX0`u>9k|Pkhds}Sn19ZL>2jX#jp1Y7DGyRMo?RS+dw#!X zS$*oM*- zyi>X?_r?|{hw{-bFYP?TQ6A@9=vg+Vi>Kk3+&*yF{E!x&iI3*dz~{|eNjitMjy#+^ zjmUrA%z{}{sm}R^8&|%pRqyVPVvO$eo_PI`p~|<;VCa>%H?ZqvZZB>{!17=K^9Njg z$p#iU7))Q~3K#XZC$4Ra-fXYa|NL zA-bVxa=Z(=^U2QmJJcMUJlD`bgZbi@U&=Z7ZH4d@!+o3fT}Ww_SiC#581`VWd$IU= zjFp+)Fe39bmH8ZGPDx9p4UsUDK>^=v&KQrjr%9*et22QLLoWtfB?bHcZLt>6QKM{6 z?O>;d7}F807`YKB0>f#9Vj@sfB56@4E~|75yK`1^HtFLTLcsL!S#3;vorbLuz;2yb zj^#Xf?Q(*kZZ`!yh} z!$fWRdByD|Fcb}mC&WLz%@pa7*{}UZGkA<$G%dqqp&QT52#4G$R17J0r-m;y^>5BWPA7Mh96ihy9aXNb9f5qt!d;=7*WCl(s7DIue@?Eu9_9}3C z3#*Xo{2|luB8NNe>MHjXqXje`YIa(#E^kCnc!2rG-k-(AE;7czro>D><05KGe(bV> zP|@;E&7mi*<2mSH62Z5O9BxYAwIpA)&3Y8%YXgTVUk4v_vx9uxib|#$Tt1T&IE=ZW za>eR%vMwaLTn+=+b|6S%c7gOL6(|x(Xc*YoW+ssB(q=-e5L@qp77EL0k%%9?D-^g9 zoC$BOLbSx2(znB%O~+fHn}2kWVtlPLZnrQJ$R9p zOQ;NtBuG^$a>j*1{yj0ytoo&%XA|JYahW~?+QVaTfqtl@>N zJy1-5W4wO4V?rfEaXK0H8$uREtH9%J4utsf7I&}TommY~XHW9yKw!iI_tEsvHt(5`cxsZ9E(1!PzAISw`q}h`T|;WW9IF-j5ro8T_n!Oqs;^2 zy-C1tF(!@Eb=L;)*g#FuITDLE}aC6HA>*MLL&^Yun6QA^5u!l7KkZlRh@ zwJ_&mCz z`O|YswtMMp-_ZOkaXShnZ z#pS;#3e?txhr!pRmhI{Py{9Cy+nY)a$_8=YpaFrI%ZZ%cUf=lAlciPMop%-06W9JC zf-T@$k-@D8Y67FaU?QPoz)L+>qz2ZY|8$N={$&4GG1QmjbJ?-vz}JHUGry+71_iRX zP%&{u1t@@g*lb6u0K8yHntLKlWW;3LEkSX-N<>T+OI8e}zQ9j5{nN24z_eJ2E}*4Q ztcnuf_R=6#Bi0HMOP?mVwTU$SAWmgOp%@KINjr>t1kx&Ocgx&J4N7adq4XrB2C%<- z@^l}FaA9dWtkho$A+<=1NSG$R zs$P@=pyH~klZtC1b+QduGh!H8^XIWG{mtq6y-w zz=CAZITa%9k}*p$BPE3j%CHR*Tw^uB&5Ni3ShpL5N4imEFyEU(f;};XF1@1b%m-ix zfO0Y2!rSXR5-5^7rtFfXnuH-kf?8}+_}DOn>;eWW<4IjKO7qm>9V80-@Bb7$ay@m3 z)U+)S+e)bTcO}4?9X|PYK@}+mAM?X@@VEakjz~_yC)(7|joosSaR_^1v-6!<6=*ti z+U3+~u{&~u?7XL>1n6z6+|>}5+G|DnRp`&otm&Mbr&x>3K3$8vLZ2@8C^5GvUb0H9 zLXHK2wA-k(DYQe?jN#ER?=pf$GE)glQ$kjinP$xJ6)*yG&D@+H*JW8%h0rrCNmX!c zRQ^tCu>Mx8AoR9KmO(Vqkg)%wW(a&`adJRqBAu_c9JV^M>AjHGA9U>S6;`W0257+T zyi~I}A^c@cv5$&toEm7HiZzWu%i)G|#qN+MM0jg+Ll0mU`Hvgg!Yeu0nkdy;QwnxD z$y+hyFREv4A*_XDuM?FN@A~Ew(UWkn?u!Hts0LM?AkKZNfH&d|*wUc5D zZ=l8T4bpmSlpM>`KJLHR*^B(PCXrwm)jAvg0eX89Fm{<8$n;u zKiF&=p>_^TH3|!j8`zMMF)?|mfXqdiluRQ+-vSs|xa_FdU{DCdB{>ZXXH}4By-o2P zaFI6$qsmMn(ubkw;{VIRDCvW}y$+K6mJ<|Hesd8P37~uGLgg14*mMlT)0JA`4xnL+ z#7EazLo7`kXo!7(0S~dHouYW1!}#?R8gv?I5~#Mo=i{n>N^WBlmH)T}#)0ksNef6~ zNY)R>f)rxr|N5XQ*_DS_s+~JvP)8UR*h<>gC~x48)HIPIP0A_wFgEjWoWn7}*FN(w z)U64=mci))lWu=y%*2MgPFY^BM@XJ4O#T#R4eZ5+3R5Yr#EYJIBBD$*#X<({_nUwb z7I##MBe3{q9)Y=f%B0TDtR0QeyPzD0p70+z*_qP?HXvB)o*Pd4?Ow?LK<&57EXOu1 z*rXRAwZ9yL?WZt6@`D#sa({V4b;AugCJ;zNC%nE*rb%Kkol{wZM_h!31zyY=>Dxi* zS*c`D*eHI-Bh32QgDEv@eb`8(kEI|+YU8X~;F`9M$x4S%U-&eN)GBn0I(C7|nn{(q zfukWZshIT9uF!zs#d|cn&7khb_gF2#@&T;8m?l6#bE(9DKpuHn6d;y|B$_HzDMHIX zjgbUElbMLj7}X=vbgq)dL>H>Ilp-y~BRs!mbWuDHVe7>^|E4a*v>TIqu0l|Q!xD(#1>o~uo$T_2bwK;$lNy!h z!(_hdh@^0@Bt_v;(M>;Ah_mnr13+3OH9}?oDr8+kd;{~sad3YN-TEr6mt>O3=$hwf zVk1R^Fy6(qob4Bbf?fAu(Ve9+34m}Ztg<4XQp%tsm1ro-NN}6L$#h-~S9h)ohR~B; zPeU66-{4ZvTcueDm-5iY0{07&h2Xt0x$vSMF<2dkR03%tr2@pNNi0$P&aO~!$N_4U zG^@~R$~zuGR7-icpgh$8MEhPPWX)o7FP3T{rj6Wc7Fl4I(;}XKQ^>8l8AC0_!zpaS zG>QhW^jno_P135ui2RZA&J-%ifoHW)*$WRA3SB@}LgfYM_Gk+#c}(u*#_!lf0psHy z%A>$zKC_6{>)GBHQ5_PrY^6{Y4X+M;!L6rO8dhgI&nK#}EB@2*NG6i8WHy0Tr*8= zfgkp7uz`G}lbnt5Mmt9MFfOeMwcCXa6x5h}+Oi4WXd!si0B6px*fQtL`WSyHCLS?G zn94b`o}&pJVjKe|UF^($0h%rLELGs7XNBIyltjzEt5b5*E1`cbY)f0KO2qc-vGV&D zQj>$D&;nLbM{?%o`vyZr&3e9bKB=e$p|Tp`J;z(MO2iWc?~U3@f3TA*{a=(iy|&aZ z(yl4s+JjQ}t1WeEdzrccr9P#$)Xvjn>dlx)8Rgj4R%0zcR(QWdsh8B2I`%9H`(aA` z1X8m_Q!hY}iIWVJdaJnFlO1IN0@FghD!{+I;AY0iUWoZ>*uHshG&n{*LSi5aHYTFA zzlg_EQgA@XAh&4zhLtk821(Ik!^<>plj|j87~Mv}0aqMPNg8A00PUlwi4m_-j5zdK z5NRCtMGzAwg010c25ilHA;6m3C2KCnHV^DuzG)z{=55Etn#~kz?!yGgXoM1K#ODII znI~pjO{uqXaW!spnAY_iIbt(j`-mqtuVombyONF%#@7DfQsmQokK9Q=d$! z&#WzVC9SsucG)1L-UnxaHPYlUx&Z>IKcUpCk@|~7#W^Owh__$d9a?k~qK#NgSUG|D zk(ntQ34#OK8^4gvxF(L6qFNw{N*jjKg8oow83ZY2&&y#mqxMgs_RpMqTN9IgJ(;}T1CugL!Hj)SJk)ld5>cGU7A3Ro$fESZY_=$g*XPn+AKD^d zeirx4w`9c4_<|9C(e1~0~wG5H$j3pv(Ca_ z;sY8`!3Q)?1Rq@6pZVbXFn7m7= zmm+l(Iq=GJkORZP0BYc%HTL5en)HByTv7ux2R;9h_;grKn{R(Ib14p{&S zQ)s@nS$rekP+MpPqSL8lV8G>0o5h@$S8P*3*027;z5vQZ*#W-t$V%CX_mTdDN9Z35RR2+o#6iXgxjHb|* zW!ubs*=(CNR zZF6uIcyKh`vt85Bn%P(i9-|*R@ZS*RnMa%~WQ!o4omns9FUs*YTo4Hx%akFCjh-Pi- z%)!Y?YSuQ_aI+>ISF?^)jPnsJhQ>+;P(!nhQ%o?OQqQg}b=NZ__BT-Kv9+bXO6dt_ zQ)=2%StG;GQcO?@ zdKRr11@M`5G0p8Tam4ekx*G2nUxPx60i(v$Ns0?+QtG)#&C}iTYcsJnT{gg% zM0j-tI_6cq>qJ2_m`z_mQjHhwGV6wSBOQk#!aD5Rt>FS8l_#%exA=#XXZWSTUM#k- z%#WpUge@~m;0ROBfyF266Z55nS-=RZ+KJY2myxhDwfoTJ=!@V(7`-e!ypFi_2*(h* ze&ll8jIbTSKsbo->BsPyh~WGrJRia7jkvoIo<}HD-}mFU)2A*+62hklKOs05M9GV| zyATQyo=3PM555=|xg7iPJm52z;|l}>!S#97ydK0ofKY<)8iLOIH=esJbUEU`a5+j5 z_8>&ocM9U(MQHt{%P|nY2l0E>68K(R>T=wH5Scy`-#$e60pWTC-q@)ASo=wNt2I`1 z-8Xvq&f-L}zoi8JOitUvtGSiR@*%vRF=IB`LRTR{EM{ZgWVm5pF|)P`%lg|wnbkb{7nXY%<&L%I{*I<_X2E$+)RenDYIoW`R1?ST?s0C+r$$&F^d{{xZ)`;fESEHa20BKf| zF0XRuoo1D~6qPV8w-OMx=JGRPCy| z3?EL^D!xox=;(=JwHj?W?P&c(6Ht@qlRm47=g;m9R{RF6Wpcl6Iz~)Iqo$ z>2;GzO5~CzSP4Tw%y_73LKi2TY$e=839qf1a8EtLJ;O@)0VV8QHQ_2w_^6dIixP&w zRS`J8#0h6v35Qd{O-M*=_yJ~ins}|LN_TVeXRPF1Dfv83?i)x-=($XFA2Mb<;sGm- z!;?F2u9f33ur<<=Bjb@IeDBZSU$VY$rSC)V{lqfh9F$-R#slDQs^2<_lrj-!^cd1U zFykBcA!I~^2GY7G&X)G=in#%*34ji2tk_wiSa-+3U8(L;XC znyJ*QTZs6Q8ux%2X^uNA0*ahLkqhvtvc5O9KI!b+56ws?ImMiFQe znlX$(v*D4_B^6?-+sl9iJO@>zfanc%Zz!g?!dYfAbA1K4QA9s?G} zgB)9G#R`f|@13H%CY;b*oP=~Ki;WL z){PXlp0d;y)Ey)FGrcis)CjbhDD z@MxUR_3+(I%pZ$CQq9Bg?+6TRXL&PY8m+pLo7Q_0hi z6eFhIYPT)m*ZB1ZV0nVV28l( zeuNQtruQ6#O$e^_F2^v0WeC5a?1U9AM?Zvd2tkCS2zLRWtq3hwxg7T)e2B0cq04Ic zyF{3W@GC+g@caScB;a`=!Z3tPgl)j<4}^xm>vV)32+0VyBBUeS2RsPZ>Owib#{j?T ze!JpZAB5_DBkeDcDZYd_a_SgQHW!@d?XkfP#}aa<6A9Hzv+V0WaAijFhUey0*M6{v zT(hWJ?c(DEdSa+ngHAEi{Nuy3IHWs*4nM$S0G&p;)exE1dABO|r5otffX&9#2R5d| za4`1TX<+V@z#r_wAT`afjx!ShF7abA{J@NOq=C<)iu6;H@7(wc`d^>G%!_(r0CV?v z{7bwZC~&5`u+yy+Mnvc5-um#FH-q5zDwv3arE*o11sff{mQ!SN<;uPgKA znxtHW5!^p@Fx+W2L7xj=6Y^KKb>{4(E;$DSUXLzh;h#QYo3eQYQiqY>rpx??VCA|U zVhIJRQDQQUjk%s~NrA60D8iAWW!PGBK86U=udLMXs???}D=foI%J3XA!0-PschC*2 zPQqdmV+9w(d5q52p`R>x57A(b(xbz>lHm0VLm>H`nldz{3~j?Q%)t;)eFx&$H2iQyB8sB zzS@T|u>>69pE}Hu{)1Ii+?zEObrFg(dh2X&TiG70u{KA}L$;G-HgGq-EdEsYS@Z@2QQBjqYKh(?CJ~_N07RXF?A5KTwrOhM z#?~=m{c%{0bU2TI(Vjg5X2Ds3<4Xn>)8O-N7~Jk<(V{MO_|f#I1NZuSNq=Fbc;acO zK(NiNov;D}qmNo2AB9%(PaOrRS0NoKqqWi-ibYGnJq<+5rL-ucw>>=bFzpK3WYF@W z|45uOrzL;`8e(0D z)jniIf&6eD0XYWYYJQk6KV%Y|U+o{BkRLh|D4Izu26@oUOTENmzD78L&=qh-B0P>Tcnj|ILYRUu2(X^Q?|TR<)%y?l zJ%HfcD!+He?^OslsP}RB)jAUC573XslRsV`dFGvh`w(tW9_^rXxV`YI1hpYGt&Ef= zELHLLaALZcht;+ja&@wg2o}g`M=m=8-zDL)g$&71&?az#RlGTVcL;NatP;eU`<9kr zXSmvKvlv$z$W1Ei(Ks||gdU=FsjB`_&eh-8J()^|$*8&22L3>C8YpcbHi^Tj&OqpV zn`ku_3j+@E{M!8_GCKp+FJMUozKy)SiphTrmpcaAb0T#Y+M`ts!<08*Anss4Fih{@ znL=s^IW%I=aR+AROcvmh*pkpsX0_M2>a5!9;Nr5HYQJ8@)sE*D;u>H2-__WxhokQ5 z(V+TzZ2g++A#grJwcJ0gN6)r()*ihlR@)veWj)GHTsl%BYH z%x_(1_4vS7Z9S&1;d-n;arG!`RcH11^?_>ZvAB@yvF60pqg~zAV{aW~Z#t1qVRrds^B>bgDqs#QDngoXz|A8Vw>v%K%g?gIf>s+T)lk225+wQ5hHXkkL+6+4pwTX4kSY>Cr z`gK#A=GE6`M*-Jn@IS8&xKirrC6=BZhwDy6XcQfPA-{kk>uzc-o(nyQNAq&CVzKpS zYR?*dHo~^uYX|n<{e*c*nalC>c9-K+gzJBVz4{02t3&6`{{{AAgkpr6-oLYbr*skX zl^Zcvk@imi?|QZZkEJ|YVaKp@r0?S*aXlzY({%Rb+7~aFm`Iu9!qzfxu)&75BII5| z6M{a0XXcJQM0UpYp&jl;*wljx6IDDxcMGw&4X!u{+=$uWV-j{INdS1rQf?C26CmVI z=PK?eVF3+uKM6NMI)(s}Cr!SHY*P~EWAX4yCIoWmj=;OVst`K9WnGb>FvWuzdf3(8!a zPrc!vQ--C}{{zZMp;Q-?X}@fL1b@orMp?VaG*Uf(R$-iQ{AVxXx3P_(_nIM|9R3?aJrU~Lr2s670qDXie znglqJRUddqWYwj)p)OeEqA&M{%mz(AsZQ$i$e76bNV7v-)MxY;w)(u)pib)Z@7p8m zBh3$WQJ<$4+v@XO{W_`7kGDnEN17q(qCU$P+3M4vZt8P#mHJ3?L|xS9&pcawE{Uy^ z_PHu8vVEjkqAu!lroBG5*Q=BI+=Dv+!bQ6@Pt-+yu3NZ2waY-YB5U`KqfTn~$*9QMNuxzw)Nb}?`$J^L$kzj}@|n}ty_5|Z zC%b89pMyqjn<=A6{5xcrdI=6sX}tvAu}^?r^6{;a1gN`-%d-pihur>SuxMmi7L6iN ze9Ww$R69;g%{If&et5U6yNSmapK{G$-M|W|rBUwtyH0BM_sGba(Zqo~SM{o>S(%TV zY{FTzo&gf6qD(e_`h;r+OGlWnXr|y?bvLVMh?>D&t(-~}kU}JTZbK4~!l~A`6eUiz~o{Kc8RT7407B9&zi6Pk+Z*Mq4;gjx|_Y z_wQsHv^d#}s!;~tmeWQ4|$AUXS8jwcYFL#XNf zRoiz;M{B%^L#~(i=itm?=I}?j8nD>mFLEEfxX9WKPZNNe*PQ1_#1tUEk zPdLcf$bn>wJK{@v19`=d7uW0&^vqCRJ><%{6g$bBSw2jEGDAIMC}uvStuyO4h@#BB z3^|K-miYH3I64UW{Ca+0|+jik8~qTQLd*H+tSwXNlSL_TqMrs>DW{dj+w9F&BTYBiHt{Y zVqbCe_y-GK$$BuP$Sx~a4YV!Q!z>KN(VkuX%76KR*mM+QL|yQVi-k|vBx&oK2ZirwBx8cx`-B-(;2&H z8;Qy z2=1K-_oFx{pSc&;3d4Cc_=-BFZz?*aGy6w;QG5`!V_uAUo@g!@ZIub$+wj-_5lU{s z+{gF?4y&wwGx5*=O~2_DR%!Wn>%%?}aMs(|EB3qk}VR2#*()G^S*I z3vSHZn-9t3%xbEK3_1&z8RE$7-pK9_42%P(j15t}xd$p?Rba%)U2K^@g)kJ6@T!ej z<@iFTb}dCCr6tBAYZNg)gL@3C!M_9P{}+Pt>abF8{lC)SpJSe7JONLy7RA2Z^S_~3 zbCjz1WN;&LgA&dsO(lmw-N1mH&I7V1=&eLD+JC0635=$8m<%I_Rg-V*>W1+I?ySw+ zivyKl=|8}|Bw(jxhH$zN`|cmYM~fiSA@9Oj$i}jalfptH6-5Wa9ZT~5L(Rlt$kYof zZwGD=t=saRIzXf!8mCH8S_M=gZ^5qC{vB8AZfPSps3;fLpd7ImQr#KZ@Sj5Wq?(3* zMLMFACP}20Xw!*QbCJ9dM_69)_UmYb5)Cm0Gsd zO~Yw~ExDv9@?K4wF24(!aZ2v{3G>nPq|5tgE>lAHM~o5|!DlJ&V@|<#&a;x+KCg46o!sZ8Kgor{=d{o&9>0k$DUrejrDCvEIFr4?Er za7a+x$?3@4!|AhVBeQ=`qHm1<7{>WF$bIk{sqniFUc>r+a40HNlpY%#x>1b4Dm>P& z)mHUewu5ruDW%0i9>OE=8clt2Wx~~)x?Ze20utt7N}z$Cs(=G>*kt0!N_zn>A9n$1 zRKShLQNRmf1swh1cxzCP3OG=|_8MFsRzMf1`%tlW($qsM`gZ8bse7X|XShXA<{{8t;~CN)9+duvskA0)_k9|z=*mfKlr`Ih5t>2_2=GZc_{ z?8#W_G%s_f!HsfgmTOrZAqx$4nsMALW$0kKn$eo&oG?TTD1*n(5rIk|GtE{&bscAU zfxWf~{~vMh0UuSd{edr`g}M<8Mrjcvf`~@3z@ud$kqvApB8mzcjq)t0s2e((bvK0d zvWkd;9Xpn1dp@LC5ePEH zF&6+cdJCph@3m}_bEHY~tn^YGg|#)wqEggWDD}v89Gw_18AHIjO8$2pI3Goh^r|oL z-*n)iz-)DK>jVYH!#v{yvlQx${b62eB_jh&+~?{``Sc+^s=?4aTy_05D6AwX!h5Ls z5%ZajeQsWeQpqlEq^bX?Fr*Y4tQ+ECMRdEGHAtJ^Jq7m@xWueJvXrZpj-sQ){4UYt z8MyNv5g_c@s$Lk*IHH%d%N;XlwnIQjNpSxzK$_9d_u!NyZ0YM6&Ju^Djg=dbr$A13 zB}5_C(Fb{&=O-ug=z}*Sorgp-4-Cn0I8q$AQQB<`friZ2xdp$Pb19l=h|EcDV`IX! zu?_ZjD3qz@;0S>h`VL(tW+-pLU0StVxnM z&?*ynY3XEMQt8bYj}x_-SJ_Ac@>1|uym(8Dw1LBEdcWV|uqut5dmtl-^Yi(Z!GS0Z#zO02%G29$|3X zdoL1N2ue{(@$VffP1OMitZ6Ma&0K1-%a?wpdasfiqQDtUGVpQISr$Rk>mWt7V%9b9 z>|I$)?7uPgTr2i5X01068<)JAtoj`rh5f0psxxOhT5fxIpoItm{<^YpeQnv;_6xER z#XRMUf;MM+$N8ccvAK&IHvnUYgsfeUO2;aqde)u>^7?9M^NLz(=z3=D+6KhF53#Ll zanVp*eV<6HPcQ6{c@yT5<)kA9jqPga1$y3$xFD7)Nb|Ssg}U7B?XFnz!xEol$$8j%)y;89+2Vbo_qU^dr6M zJ5jMF|Aq?o@k{nRP8Zp<Gy*z*%=+dviP zD2~{W0%A}C52#n?r0?Lw?W*b*>KfqS`xf;r4M$?_5o^)9$R`8I#s}jL$Z9RE`UJyvr(rUA1p_92Kxm9@1+i-dGG)t(Ln5%tno=^OTv zciUBK$=wY=G%W$q!8NraidrIrV^rap35Z_bT`QvH4M6nc>-M5e+fyr|o29cXkR7BF z=`2H~vn-H-xQ^Dj*h(E^VQsB*Q$or|YHuBg>g#QtZV8CqT3c%osMBOk4s~_8uU@m) z#cD7+we;Pq8-Qp*0;0RiYDJXR07S!psK!k5G(>k{Ds^DAlF|NQfs63(9U+6%R{*WT z)>ieEui6W`u-1Y`W?2~Os;-w3uuQJCl#%o5!*VUL*qQk6cTyg8-F679WDb(aK?FPw z_2my?UNFcB;++d9FM*I`i+Tefg(RarhF+gSK2k|_kt&zSN5;Ew>tJ;Wi7aYwMW4le z)ZBN79kberwXbX-^!YTmAnYpFGS{u%!OX&3=* z9wY=oj9vvX=lPVsNjK2@1T<6e`1g?LUD5!&pS{BN=(Km$xl$q#p9p_y+K@=8Avyzl z8Z%E}eze(`_Gk$Vn##2jlPd%z4pljTLJd*Db+^I>Wy>%b&iZI~ z*~?Ir?8ACOg_#A)4h@Gq4|QVImk_B)5@Y9fDSxcL%2-n$_OHFBZaE({^%RtO>dRtN zWD!QvSVOwH%#6hPCX)wrX z%D_4SWT%887dPXmQwS*~uKqNolr_#f(zY9Al&%b_!etx#v>XZsS`OwGC{ul}FztW~ zQk`T+f{Zf#C2f-k=bvC))NNGd6h{kF&CA{)p4_6^PJ_`U3K`r5xB?9&9&2|jZ z3~SR>b@`5BtvlO0f&ka~eF5A<@klY5P8xN=!{HHAFUTrQy?m*b&;0|#%?G9qUGmdV zwe~V7BuS3Qw#2|OyD&aPqBS8DF~TBJe%b#^YT3#j@Ji%E%f$88ZER;4GI48H*xb~$ zZS$h)AP7x7Eo&-{UKQ?oPTY{6+1{r7F}}as{#Hx+h{iQ_HE~I+-ru$4j+U-w4mvcY zkIRtN*#54FAoJBleQYAgocK$n>Wde*z5y4TyWHs&6+2MqBr5FbE7HyTbv-;+uW!6H zzfjW=Wi&n2CU3&l@H==la!Pw+Ov8e!5({0wN_GZrCs36JBgG=g$dn}qxoc;jH~xLm zDIx(Igpt@8sK6NG+nXsEW^)qYlB1&qKyV@p+=()$rvRVT*<*A1=oWf)JNmHd^a82Y z5=z~xSxYRkkSOO@>J)Mhd8@gHEHbLs8X&Y-_q-kN@(+ZUsR!?+7T#lUu#*$;e)oAp z@J20h!ZGk3pMdv#%~sZv-xnHy_nqhL^{@pqH<-hE@Q$+Z9zz^(Z35o?4XWP*5ES_K zzD`OC++~qV7YlgTq?>$8F!H%gt`H-gOXGr(9nac}_M2X6)>AaEh4UCq5lp~)Q%rDn zERyWh0K8`>;QdWA{PpB_T?0f3Up-^5hyD%1JGlXPeF=ESLgTJ}(xKUJtGn050`^S6 zJL~<1;HBAbU-Wg6sST4s2y!qG(-c|C6$@Mh0&)gUw&`*F)cp4XNK7fETlvsNU5vd5yJ_#%K{Im$>-xs)S^3 zYf#VI2kMpL9AgA(o`CltbS3KVd5<&z?`vSu;^e(UFMaD7fp{-LefhmK0q+g(G$g-R zz1NF3`UIOO4Sn1@(Co@ReS00qkvCPii#LG;tHxoQAzHXi!>zc(cb$g!f)K)&Y1mJg zS|AXp%0A6E7#W;_7xC}gi>nM~dvBvyet<8Ug%0g3z7#?0BfG1RKO~=weiz;cMD>L~ zONU>^@M{_nKKi)ePh)s2#OuXhuEVcDQ6tx(RNMQPTjA_H@YK-KY`}EJ*J2XL;I^H)LK|D`n++Xt@E=D;=jGu)>x0=h~NZ~xCg zTrb$jqprE;c8POcWn`*FNOgsDBc$6{B-$vVMdB!@553~|!J5_KImbTp+Fn1n$ zB$ihv*lh@pw3-X9qBZxfHA<+>7FJRCaKk5%8GUTkz%h8(rNCTUKCx_#=X3QKtyR|o)>`)U6YO9mTDv~H9#IySVs;Lsw$H?u+N!w5y)whgX@@s3Cuwc zD+kA@-rtrm*Ai#mCy4VqRITbO>HG0G8H`hkI0MHD6N!dFcjDa8yS?y`4y%pUn%!v% zyVC^&vly^=F8=FFZ46R_ia_`*YsNeV>75$SSxe@udjm@I>*5Ab!Q@eBgYpLC_8DL^ zsbJn#(B2oE*==Ps~9#KhvNiC5)lcRD=v{6IQQZ4GK#!qqQ+ROplr_bJ zs8b-n%u3uts3E_sVXqB}bYU?qonutQCrkBAI_E-&yerj;x5Lo`k4Y+_5ow-+s*d|{ zJ5ke>CrGj-OQ0^QrTVUIJ4~_a2OV}tbNYOgO>@@JISgF_2~fEj1-{V?@N5@z5>}TZ zAI;?dpcBqwv63haL9K>Wca71!84>3gVb@VY#2Z+JW$zGIUWtF(>JS)8cUz zRH>hA%qVGOC}fW%(V!iUWwUmmWEeALHxJcMksA(TJieT&0Gm7o6>1?s)Y1Y}N@!$S zFpxo7#%;7vOzapf-d~txY{n*oR2G$?9XZsn{SsA1;(jpItgmY`(mlp{SLjlT%v2XJ zkhzfYT2fs|g{Na;C|imuclSR;o7p1Ob*&KLsGJ^dxU@IEA@n6revbjuXvtc^VAUdP z^#s!`S*xq@AKu2HThh!T8Jk_(enfGkP@B|;$74UB;4P^FmIb6h?Ov3OrE-Ixq|_Tl zk%W`4B&!t2C_k(hD*zL-CO1TcZ4!zHxHg-g*uD zh3E{Pf?v?yeLj``5Nq@Y{jmS^jIxaU0qNf3ovU`F-LCmKN268UP+-%_16$`Rg!TP6 z*x@)FD#n+2{;Jb(Ef^p$cJ0^x9Ca=aB&7{b!!3b#|2`-U_B-Wb9vQoT=z^*%Gzao8 zJL2Vf^W`OMKS!=GUv@Xe%SiKO0S3>A%Y3;5nzoU1MOlBaqax}%*u_yaeOi{UY-coD z;yZ}zH67EE50pitT^-)5V@$(8?j`Yipl6Q7g!%pvG>MYieEe#nUVH!S|)uj6tZOvV3Bck) z!uNr-Z!|E?(H5>FTo1VO;D*4Bg8Kn(Dja^v$;pixB_|~%CpWf!@M_YuY18Cp$<585 z%+DOJ)2RMn-itv>pEWi{=~!d)maTop# zFVuCU6@2~QBb-8AzYQ<{&v@Cwt=8@81-#>GEtZ|<1(xs?=T^~vzMX>|hoinjLMMIr zY%LoaFLOICt>oPn3{&#u~I*0gK97wb;z&Em7$Ebke7X3X-Y@!5Trv-mvDQlZpYo0G7Z zL`=X>!XiyrY50!s0aHc@|I?-e-m2k0KLorEF!7vj;yuH}dnWLHtHTvMig1|C8UF=b z-WWE=#ecsKIH3LSDoJugW)V@}S<{AiFCcWzEN^!{&o#%L^MUdn9Xw?*f_s4Pghc>` z0=V}c0Dl9J`Z<8J8SihTKRgGJ{_q?$t8gR&tXqHpDF7Y~0cZ{&38yitQCN<(&5E5@ z9Iu{8#abaRh*m1FSX8P_X>2^$2b2BgAn>z*qTlolKp70Ts80mcDov+$PM|H`-=pvD znZQZQ_KVY(TzxYxW=}dsWeZ1HR0a-#{rneDSvJs>?MihO{m|H4v!K7qGAZ(gCbn`J zaAkDe9`~~7?*n)V{gq=7&;#z9@NyY>7O@q9*xF!^p*XF$;N*$*;%p# zf)}IA9ct6@ou6?*mG7{d^IciA0yVt^#{j~-v=i{hP0{?k7Zh;*K)~ZQ9N>`^kA|n? zFl#tV!xPR2e6EJKjse_5!^isochYd$rGQfaGlz~D(`I@n`^xr3??TRSsn}7>R+2N{ zYROr8Zaim4=OE@6$U1WK^g-lcqlSOF4gNHV2mhBE1AbG(XI=vMc?~CB0r(LO@6QK( zFJP&o8P1Zqs2%Tg<{~;*a)BPCbMfaY$wihe7t4|n8(Pjx?H%myh2`GQQ0kN;$WdSI z|5hQwoS|WbXP8$bGCabDkJoU@B*4H$|BTMSa~O$Aoy?qe3pN2+C&&%%3*_d5)XB{{ zH~F^Qywnm{)*vRZ49W!->Z1Z~*9h=S8h(BT!aSkjRXKncYIx822s0P3)Wb}xC8EVr z2VN`H11qHtX4_Id9%Tr7G?pr;S%j$tk5pg5!Ts>L8qR7CxQB+%9R;|PhI{-D7&_q$ z|5zsACV-{fv#cE4rE|bd3MqG?&cS209CYh|9AM{wSRO*^;rBHBQhUJWM$4Fs*gXM|Em!;g1||EYk9vS^kw&<-$=IRhfUBd`Z$6dw-W{)3Io z^P0I>`n(o^$cVz&oWKtz%S-Pi@OBwFDW?v06h#N7Ivg{nUFW@gAjIs`y~FV+n(qA@ z-)4AE=i3bL34ELBEdCrRAhXMG-K90W&&G`r*6==hB8J*o2yohE0B#3xCQR5wZU8V2 zvr8l&lKi5X-ZT9w$(!Dr9IH2FARmQyA;{Ca5o92MgJA&Y0w~4w6zK)v(i;GD190>= z04DoyJ_9#+}3!*6>26_pOGT(f<<- zxBMsI4>bJht$=xe2W8DdS+Ng+qCp_yV2NgUy-cWpe6nOtFGk?pv(b`I(L9q z|4iz94ao_OJ9FC2-m6&oGrbMx|oN?XN2}A(cKln+epH9YIqn^yj{aL zy$E;`V0Mm#;vp$adS0E>)hyg-=^qL_1Srdx^t2(B%IhfKmnG0gBSPB)lxYVT(DVnA{2Q=f9hRJJ_I$J=jgJJIz zAmpF~I|`_$v|Cf^o6=-H;8NIj$#*`M!n=pWCV_0@b`0d$RT%=qu3K`9qclEOwzrk5 zY!3uHG8ybI%PMT+Tz*h4a`pc_ID*9sx`t*clv-S z&f-_#1uXubo6BrLBe7g`D)lUuKr9^Q81-+wxconL#jA_;P^CT+ABsER^CdTU-lh+h zKA3N~&gRmm1^Aw6bG*0v4z}bRFaioW;TD_(dh-5XEUKf?yD(f9Aw4vEcgQxvI3YuL z;~>CKYxqnzV3S6=xd3xzD~M)z+k#gsgyTU)*GSScyknUe z3>QNrQ!~9-_2PY&H(lOmc`aeeW>q>9!jx+nA1tZxOIMxP9Bo1mB0RH_Slx2UNzu+0iCC>^W1j6H-80=o$DFiRnJd|51_^kZ~h3 z;OP?7joiT#Ewv)+OD#S$_7V@KOJm(UrT0M9S;(<*VY0up3C*!BVfn5fD8V2@@T{e+T);Ml8Kz85s&_Hv@%g%7i&cY$GyeUOH2k&2#S$GoKc4MDX zmw$&d+bG3YtIol&CaYHG@--QK2c#EvW{?$VZkSTp(cBExnxVK{XQ%-KGR;sN5)rDg z8R{nl2(L3k4I0qb3OS~bMiGKq117hp^5dIR zJexLNn~iw_6_#yaV{;RvO=Q_!{vW!!jL%f@laL?n#(2KT|0Co_mB#1lc6dQnX^x@L z_Bwc?W6*R{a0K{H@U`@NZ+gt4P`PR|66v~=-+>>ps)8+!z) zg%Ck-s}AYShG3bS23p6@l0aLS4`Xd&POUp|nSclcH>ocKl*BdxG*3CQ8nFR~s?{SP z_g+}QqFuQavIF$ zGi$oH51&QT3-fT7U1LZ-~&%kUM z>8pczSM!NAKzvHueFv)RB7}R3F}dLHHNc#}?(UB!vvv&h)=t8LADj>-Fl)PDqHssB zFOrjp}wagpw^*UM8dZmj8N zwOb|IO@z4-H_T;n?plKE&pg8e>w7HCkZQ9ITPEB!Cw6P8|qM z&Kc8kvA_T@-HQbVmy(({3h6dK=|=E9qd544Qg7)A?mAh+Lw<#{^8yWbsDghb!VhfD zWodh$Et*j{3NhX}gfGYGP^FYM%4#B%dTw992N8n_&vXVjh9Gfg;1T*s1_E=npLeRqN*q~ljm$#17C;iBus+K70| zR9Z&k0gb)iIG1nbs4{E`GjDBRe}pX$48d+{kq7g(%3c)hwiV)}0>?FOM8(<@DterX zl=nHJkU{J+7_f}wAmdBBn3Q!vFJesCOszIFlDD_Ex?pGxDX4D1Xl{Ks2S#(N>+k`l zBV*yO0_w3aC#l9tQSWa}v`=2v{Kc6S_6?y6S8dt2{9KD7WGwU;i94vmzGDnj9TvyN zKXM`5D{Txf+YS~dw91D#hsCx_D?kxl9Qo6y4dN_?9qZ|Or!alG-YLwQ4n3Gq>Z+b- zqALL0e>wmXMiuOIL~;QPz6m~Aj9WCF>#i=2rPI0Y>cY9rnxx8=`({j1+;n^m8x%50 zeF$;B^l@xEvX7J1u(q`mS_1tQEfJSWLn5uzl4n@w!m7-HjZBl>z8#<`k^Z}Yx@WF- z#9Jag*SG^a*|YkZyN%#m^?6<%+K$;1boH@Sx{HbfxdmUj4V>Ucx0EV}(FJeHepgz0 zVM~3($)w3-ETdhWB{Z^(lAgNs=jae4u{rC2A$V?Y>T=M29J%1SA{H=WH$?RR z+1y!jA^>ClBbm1|d(gs(<;anM`Y5(p>PMYS)az4h=_H7WBHCXC|eReF~ z1T5z?2+M3@>2Jq!W&)PO4Z?ChvHan*l}O=*r|DfQupb0yfxcT21f7o`7XggSt)+V)?m^ zEiYf=C0?Rs4Z^bX31E53js+458*;9ZPZomi-OF zl0q!yt!?$P1~2h?DQyszjgJEh&33|YZLu$oz&0TMd>X2zjsekN^x+cSJBuT1U?5)n ze`2UT7eLXS=VF{aU8AKK#p=NK#k*_*FKd(Ipd@;843~Up35n&i-oaf9%Qw6UW*y?i z{tEli^^@i{tWUxV5fL-PSjlcNUiM#@mQ~m)G`2n2pp;2=v_pC{1nq31?VpgJo(+mP zlo68<(f?;tX9=Xb?7mZKcHa>VYScfl)C#`^BvK$tn{0oURN;X01EwnElit&Eo?;JTtgxITCUxB@kCwMoF$wL!RM z5LaK|Vu#AKaDu3gCsC~~u-DqV84anm(}?juumVAh-{ENzyRC~hIR%L7=%Na1kuZ%@ zF#40VMjv;K=r1z*^=9<^TBCn~Q~q_Oej}sz)X~MFBnAoZRQ{bR-L5S@C3L%ouK_c+ z;vGzm;R-CbtF`Vde2w?@#~akwf5v(y{A5jBXC>hJgmq9a1wTq$V}WZG7X8S{7Inkl zDdB%o#C2kUOxQrKg3MKshnjo1-vN2$s)-ARalj(%jF_0phS1jqsc)RE$<&f?7TkfWu?h=6 zEGWTBu^NsRbu6MKqpHz?wc z2U&1LBt0yNBe0Dkdsu^Yj3Q4n`qhXg{3X>Sxx6n|C(Q#Xhf<$mPc&p!hp-?f56BbK z+FUl;myxhm+(eB5LUX}*J}yECH??pd1`%l7@0AGd(||kjG&#|9Y&;ZygYPWEXC#&y ztGCP9ZgrI5yG2OnafyzNQZg$+ya+&L5&@yi^lDb^|l+H_|713rLn3Zoh=}tMQHa_P0EJRDvr*Wfq2(@O!%%F?#%!1}sXzZ0GGbFi zWNRLgdF3x!>U!<1Nu3g~ztcivkNOXR>-6K@xUjGDneBJXLQVb;*mW(cr!cxnv1k#; z<-?KVTq~v{H>OOFb&p94|EN7JIx5=JnOpAT<&IX~Oycb7P*lPn{a)aV-W;Cuj08I?u;AK95=!JaATU|%$w32OX2>6`+ahnqtDH0j{D#k z{$=xd0MD{p@a1!$x{#(t`XkpSAz66!?nK}I*xEp!gWA?LU0Q1St0`ge_cgEk-9Mj>}!KEOtDaaFX=bF#E@my`f^mp6x#P{m%A$%`~ ztLxbr`M(6NuBRy;kLy#Wa349HCiUFLA)c^>1XPq`xPRIG2Qk3!?!TTJ^`iW!Pq=ck zAwl2K6Kr8ApjlsVDO|Oaby}n&8=iF>eZ&edb{J2u32n#oOvS!)a*a>o!6sne6ec5~ z|FbA~RT^_#1$;t7qyDN6&XRYT2iQ+HevZ1A;OySTvrMU>k#{|J4zjRg z;4EH+G1L^QXNPhdVbFo7KD9NMEI2&^#xp)CgV|&Dw}vq4bdk(h2LA@?70%X}EVI^# ziy84p6e=4zaD`mRCY1P3Vm6t-*RA}S*+Dk`MeYDOIyTh^p= zg5*}6O5Cc$<^%`;bUi0wM?-B6GP>Y>^BaP)llB5Y*ZN^H8HaJ`M-;xcs7y2tbhgA2 zgd~2~zjsCbbEcqtAgA)eHtJP3)BiGDhB)f#T>bJEUi^QiI7`w{8uXLkT%NXhRF{RRS&@Q7HF?`q%k>B|FxI_wMDxAa8#g7)G|@q6`$#SGre{-IER z*nS4~0ZUnIcC~6k&@Rsy}#$YPJ2t>H5PnMD?`&!*Tk<=|mOe2hfh7 zs+uW9YXnr0J7T^&weJaw#%eH>Y1Cn#_piRxSx6 zwtp+zv|5KKVQ!dMp;mC!rLjEzb!^jhAwY{W`{@U8X3!Q|$`ZzuBDJc?qy_JK(;R~e z(;U}LuiNdKs=rN{mgaa4j$wZ=pLNAM1z{!v??Z6qaLK^of}08VPdNXKG{?hmuffd& zt`G414(@>Yo-z~XRN#id@%t5c-Ue4*0IOAagU#m4KzP;x zhaSD>xKA8Ytp!lfGC z&M*C~}$4Eij{*;-sGCaOO)O;qP<6V-FMn2DRH z77NX=$xL5t`+}LYY&Of&b}y2tXRzo?b_D95sUSeBBYe9ZyWSpdpu}EDRTaZ099bP%G%$-3^ zU>G&WMPV0WgF6{@w`JAI$eC#(LJiJCE^#t_3U0J^IHovDo&fXb3=G25;NP2uLq=#B z|6WK%_M&YC@FAQ(ikawMgp^FF)2C9+1=*wadS|>)B{4HkD+%;K>#Tx67s;k{FJh=ycyiwF|^_ zpTmoU{LD<__s@30gPlx;0j@$)Me43XHrl;xWK3GCu?Yq3-4o#>^3quApPg&AzWD_< zysP;@u!?lzo|=5L_O9itZu>kO4UL5c9Mj;RqL=O*@zSoPU z4b-Sv5JRdG1vPtO-*}m=N=A_#mam$y%}z_@7A?i8rq{SS$mvbc8F&*`()w-e!BSuE z01RINalR(9JhY)nWK}8)FF>p#fvr;aeJVtTxlsiGqT)GSq|5!SU#h1-ci|V2S&b^m zj_MCoQv#~&II4aCO)6b+2BM>8R$`Q9nWa+9I>N;ORxQMQ(rWk|I}Gt*g>7LT89H7% zB@7;oFczI->y*!ZvMWk2c1kH;JGBsAs0ww@r>wkftjD_m$^ufCI1`18Ro*FhF-tWf zbnYHh--yr|dsY;nV2I7uj}=cF&<|Gc0EyjDXT8zNQMJ`f^?JbgZL1lp)Y60Nt5WDD zX~OfDCg%n<7>wcf829QbDQ0Em87*kwa>K`LKt01?gw^FsCUwV(XyUQwN{ca$ot*0Y zNjSP1+ocXq|DV0*n~6+9eiHi)41d|mYhgA=57)|cB9MzJ*lNMANxGUJ!G2`9Zq|Qp zWwT1L%x;q|C{UCBA1mRKeC#fw>yn46`G%jjNhn(Y~{ z1^L4MttV&e17d|=!y-|qv7_9U#%Fj*XvoH!V+~m){ZFQOHi0xI8vqSj`*2acT5Z&8 zR+OAL`od~htIq#_Zpi!%@rL9$V;jk1Ijml^`Y6Vn2LJUj`3T%IPJT+guxXchAGI3? zXD-8xF5hJCH1LhSe#F2b6-{Rr%XW)=Tud6|JdZPD6X0WF9fKIM2myJfbqqN47IMyANYt^#rwHD(&kOwM$7-rSvy|N zgnfd5sUq)D^P|;x5y4PJiMp@UM-{x-hE0Do8@G|;L<~*le0tau2Q50 z_a2Q-LS}WVO)C37DaR<|N;+c_mpO>3zMY&GH;M%s;6Nbp|EPI4BcACXI(;zH?xP?|tjZdRfVaZ_Dx!LMbinG6C0oF%zfXhQ(?-L2emquI>QmWbKR03M>SFL!T8PF|6Td`3=M zaW)_ASDeE~>lJ0XCrdO1!+&&B8v-iKA^{$Uv@h0Yvl7FTF= zd({aQS-jgB;AKm>#v-}DA=~&cTaJz3uwA$n594xXU6Y9k=GEG8Z0~QeX?K2To-^Gl zM6+GxB{MgpV4v+n;({;Hsi|AogJezj@;a>PE(KcjR-BiEnqDXVrHD$*Iq+YN;crT$ z{c7~DDUsHzF|$pHG^K=mimObu%{I2U{1uHcYo}9!T(17yg={uA@o6l(ngI*WU80Uw zv!!c|x0dnB5z@c*sQ=G4&XVPT-34p0zj*w^W+UjnBD>P8v!@V(8w8uw+z(i8y;A|< z$W}G3c{D1G6gLk000(7U&WG@mf=Ri?r`S6dHXdhrIY`STdkLG)p14g|63o9p~=Xq1sU- zZv2$13p)bKo8q$>o~xGP+6ww#c?Uv10)MI3$Yr{gFIs&PQblDWR`s#s44oF+3sk7n zb4O71+)b5MMN>-~x7ID*RNB8Eo#i0#Af26HOIMyg9 zV-N__U{Z}vJ*;NTx9l-5#pDGAU0f?b zWkVtzTr1M~Z0lNqnQ2I*nQH~Q(-4d{6uyesYNOipGDxBXxc!kP&=jCz+hn{|FWj@p zk6v~ShrLpBdkpi;c6ASj_E2h{cR@nu*C^WLSiE-C;$5WUZF+}7-zT(oY_@g3tn5BP zu;jtSlFuba_RVIGl|g9gS!gr8|ENxaiOVp|)_^RUIRozsknGLim{9yQ-h>pPU5!Dp zTusE7D^p*1K0d9&VJecBx)9#he&#HEfLVYYkR?!9M+J%@{Ocw=uA}j_ERqx&(+*O1 zO=4>NL{wq6{viEwZ4B{1e!`()83qu~Z7d^uD7ON)gEzFRG7b#OFs1Ut&^(mNmnTT& zUp$X9eBfJ7@h}Ju?%mO!P2iotk1&JVbXHhQobKOvXVN-q_#_Tp^)F@7Sd`OzP1-7KLLOEw$=#7#pBS zFIk`Gj;k=kPXa)hP>-`%JDGZg)|d6jGU8g=hFkd}Ot)BH$I_)+o)P$!8O}96$~EpM zH>J5K_1inBrza61S2I~w>fo%!1V2xQ0o3fbb=4}^Z++FO`Ngd>VEN*-WtK1BFrI7S z?ge}yp19r|9_ohZ1fmkfv;(XH9zH!Sn4+dEyDf{{{u8z;);F*-(8>+XGsD$A!!5i-&M|ImQO?zggL`1BW|Y*JWN2-iQHJ`y!emHrT6y{9AsWB=1Dxo%no+X5tCP~bPuEI81Qg{LVP9-##5Js@1 zFvpQqJshP;Vry+xI-%?JS3p-1>h35?3SUeJGgMM|xQ5b`!dZkcGA4!3*AV>9AOy}Z zNp8so1V89KI9A~-bW*sjhTzwPP-prbLc4M7OcG-ZfQpjD)*AAHaNqE2I{iK+)QtJt z00<2ZE5B&)@9^Rbvoqby2j%&R5TtJp_cI&fhxu$8}*CFKvXUs=c8uvI1AN^M-S9XX;S25 zQ~(|)q4apPM}hHZjWRAxiZn&BmSQ!w!D3`wE4Z%UsthEen!kco8HsZ(iweS@){0y7 z;ZsrSNOJ=4qxwKN-<#(0A58alGL@`Mn(of|7OO$jf+Lbl2z3O${FgSyHkW^Ab0mV! zrCxg+%LGYh!T3kh9G&1=qN}|62>K-4WV~m?ErRO~cRaqWfd3b8MR*?x*9GtzxC*#8 z@xBc1e89iJeaJ9)UV~>Q-0y&=;`s#JB*1^e^C+J8;CUyW$#}j4_YmNAcy5KOdJN-V z_wJ37x_58fy?gg2P4Q>@HEXV4l3R4|-V$J|*0zrT+rYIoy_|Ta9A~~*k9OU=x3^$C zk59F|m|%w!x_3`Q`uv@El77biJ9h8h2``Zkob-S%>FH`@zAq<@OdE8|p* z*HckuwnI-m2f+p47Q)SeV;f}RnFD9QT>$5STMG9u+*r65;4X)|8t!_yTj8W08>OZu zrKYAfZh}Aiuc>}%);u*e8DNW+_K%5hE4;RDljxV)Hu2q=lK38bL;Cz3ryn){kv@Oz z^rO!I_H};IAC9+8D5m@%ZV$q`k4f}^M!SusPwzY3;qJ2zI(9HeK=TB+kY-Rv??vbMf8Z z9C|!u8lFb~e`z>^V+OKIjW94b;s`&k{kK71E_}a+7t_C3!~NkOnT^2RFqKB`0PqpU zA46P<#?f^im$!8;nVg+ymk`pU{ zw@RF12;hR8u9Nd=ajjEc}J+Asb| z(YRQw*d(B~64Ld(&T_-TXjqNGMr{J#dHMnmvVa2d)d; zX>gfv{osbcjexrtZX(yWQ`!BKAiDr)xMV+yWu7g9CW9eVYe z+c&@1_tji;Z3T)4*o**9ug_3 z{UwXWdg{G_>gOozdg}!=slMfv7S&U~Q1t?%grz?>#w%b6)6}c}93b|F z#G+--)%8;SB~3x~3re2>9l~vg`vI;J?l4^PvzS-lI>U8`I}5HaoC|I^+*r5?aH(jd z>+rk zB6@=m&$``51#wY?hyl6pH5)IG&`334Bvxv~Z%5$~5KF8wIDfAC!pjwU@Op4m{zF5Z zCFde}cfm$9#CMm;rAaMc|A#Px3-FM2ftE6+TM1Dktvz0#94!fLVi&vSRoOOmW3Tj5 zfMDsNk+ypz+rhLJzSZ@PsH9~FPqxm+SR%;a+3}Msa-POh!w33dD0MuTHhgVal+=Pt z_3f*ecS09u{6?whl!V054{IiCJDQ`9@dSIuQ!st72WN>zwaKm4Ow4*aiiZ$)aJ6{0oMNKcuTW*YXmEzLBRorX^zo2deBGs^c5~$`3U|~i;H75hqRP(mS zUTuFp)x7YEjcWdukoaHOW|>Gffog7jN~mVV2z&AW${f7Nixagv8V&B^Uw}Lg`v7F;ITAI-1on;bV2tQYb{|J{iokf(acU&IS}OR+5^TiTT*W2&)#a zxX6XN5oWz)%k3?ANoa(3Ux?-QO#z|NDi?ub@9axeZfj*}wRUPgLJ51ZN%@wsTZ&QH zsQ_(q&MzUr(G#JXAc3%HwMmTuXx&3cyR%UKp&AgSpjXMIxIEu`$;*%jJP#S?OSl*0 zl{Ck{;U+Hw-+^}zre6p*ldQYv{+nmxCQX}JyfvRS_KP!NS1!wC$l4BPJfNa)?(SK( z<_>|u(p8)OA}c@@N1cIf$e1Y1^O%JI4{E?W(;q!4W>9#GdIzz?d+-gq`YDcdhZn|+ zh0@KfeM8J5ff6m+HoHNvheg=d7ins8(cq@735h3|tZbgk7Hi*E5!PcoYHj9>c4=a!A_2NGH*>J~+N7q9<+)gpkhJ@Y z5KwB6vhVS1^?XW^DEkf%cs{1;CC$S2KyNnfsi$WHb(;O;L^{{p#y&ZB<1%0tFt*-K zx=<53_Is<;bFd@IMiQ29iSH%h4YQ7xm8;uwNbeia-!%8>fgE7q zqg036rgEMm>BtHDl`i0t(I~+V78Lj5+E4POre!QSrjlS83!02IV)YFVv1jY+?3&qn z{t=z6S8>t6|5>)GWfXfakU=}bFXPXoyK>cAXI67WXinDQ!H0Eb8`Y9ozf>u<;dEWr zs6*DO`3?Hr?x0z*m|Ut=BSzM0KGiE0`Q^5>70yOeY8dt%<*-n>ngCmwy7|=@F`KFP z@5L80A?Q>3%w+Zpub6aCkLy8pMKJqdWsX<%_m4v!<6vF^j;7hp?B71boCrwr*P-rY z5aW_)eEg+SjlfHu?73CC3q07b6$|{i+Tfzcn7>hr2f}vo0JPkyi3dQt)x-ms(X|he z=NguHz`Ra*AJJL+bQt^+!~=4FbY0>B8#*_x-=1P!zgXARVt3!9+PS1(>7zIp1xc!}KBP(Y64_6a&do4YY4SwZ2q-h) zr2u6-0ONa%?w*NYBLC=(?HW#mut^MNEOxDKZ0-{|NLHUu^9rB@xr2*Vx2>ZGQBh{! zu2Qq{B2r`shqU}-Cnr11fAaph>W_Ds($>n{2CBbD0Gt9$C2(}Gv-pN2i{lpwRP?H%;T(SqmD!As!oDR6M7CxDO-W*`oqb;qGW?K6n3rAO;g3ysOW8v?vC43h%{D%w= zmJWrGjMcUns#(i5N2pksctN?^`futzhRR%BoPsQx3}l*sLgc(vS$82XW=#J&Jy}kO zsejw}`C9r8I*apdaG%CL+zNuugIUt-H`48HL(`(;pgSRvP0ED^l&%8}Z*-k1mGYmX z(c=APzu1GG!VPB}$#w=-0Px}9N>^BJ=?E-{PKCrH@Wx@xD&$}K$yWGt@jeXf7It(U zj$WU8^VL^>V@X9a#T|E02jyK1ANZU0!|CD^)dFF>7P5aKFfomU zSTE8$*ATx*rx*$~4w4%b*1|1F+Nn{Ju=J&Dr0vEfHqx9Jy=>b232iD36JV9<@enrJ ztBr@`b+}-#jfZj=!IBHB8IQ)s!%8(7FV?P_jEDWdO&kw<;oZOYbnk6)?D-3{N1V1} zomj-S+{Q5H7PF~p5g^!x6CJkZ9JSs7*`Omb>90=*!#Ma=J%O3S+dE>2Qf8?)1T+tF zSlw)PwQeTu@qpPLIM^Hv{b3y z{5RHFe7_xYp+VxQo(N`Lof%IRM{F8$9n@mk=L((^PeXh>)Hp^Tx3Xd-tXs(PEmxfvvzW4R z*-k*IL|iLx_L%C;^KsT#ZQP0$_5Hih08RI8GxrCxxj5%e%1#J1#OQ#CIYeO*ZJ@@W z{mD0A7^qa)c*%CYJwKxqO`#XZ*izyo|Mg&Y7#<(x9@F8kuy^7+j6h4R<5qlm)){yM z5B+7C*QJ41yk}})g|nD~l9+g*JI*y2m!vZno6bXtL#Y?x+YkkY|1m~ebpAMiDZj-v-;L^B0P>Ec(Yzz+<0r*tUA`>hCR}yp z#4F-+6PI$jeXI-O*ghA1n%F)f+`v%;HkHS}DTO55|q ze*QkzG3>|SGBG!fGMH7%qi3i`ZCps|ma`1SLFx`Hhs@pg@r!T}Y`0kD*qg|lX#q%# zIn%wsTElZRwR(d7Lg}vMBYooN17<6ftKS3^Uj;DMApM`PHORyo4U#BtN@J(VWG`_W zUvm!~V!(L+kaiAnRz?M_n1pHsx%5cK6Lt+qIe;*GV_Pnyceji649>x?!sgX~m0-;o3ltW+N>CkI0KERqjjT-K_@_mSI&V@l z9uQaQVzWug)d!4J3x%!4{O%o%1FTR4DPv#FGm<_1H+o_FOr$d|ZRSyKT27GBcvz)% z$!KgtpzsfKG0m}ItFdyJnS_@km4aKqO@Lq>;vJBT`Dpd>{pKviLF$S4OO<*9FE-Y0 zyUO3j!)nrDS73rcuYFKU*K79BehEEPdMb`$Qi&A|lhtvx_j<<)F3(M0)$uIWq$IM6 zD=>?DH~YRh6wv0Sg+>n!@Ueyoa_^=mauypYRQi3OAZYR;l#Qo$hD7wSd4$(>$|^ampy#ucqRmQ3_*AdaNDP>=Kdi!<_z_ZPY*o*`f)iemq+@aTb4%jz=AGh^scMvw_sbDe@s=Htx@ozvUpnAqt@hP7sOhy$TJ}WI&1531{XzHj&WZX*crImgj=Gvpj9RQ ztpLA{fVwigm-E>cm9j+Ui~m6`Wcvbl^`PJV53rF94e!L?{{&l8Vq5gT!UoFW4P^g| zVsfb!r=RMwju^29Y*~kTRxOm7AjFjgZ3T>5YPoEz>zm9J1P}2+U}E@f1jOF6@1r!w zfK6$RUpJ;Xo`D+&_axq%<7sguGG5rU(!MqqCbF(oSE8C-WO~X~|60-<`=lPtJhHZ9^-MzWBf{4dw#g3%Y2?E`J_2z z%ui$^)dT05x!kO#&m$-!?#%*vFxX-(RJu?b$D1cjylym@@3rQ5>&+1R(wV$xG1oZg zF@ij!ooiHP8y6*;GC7>da$^E^gCi-J z;btB;so?`y+fH}rE^u5QHgVIE*4{>i>pkG3QJ;QUVTJxlhV=bPW-T=EwK*9MNHs`c zqYTfWDo=BLsOIULQ`R; zVppTWH?sRaCq+8W74l>xt5?V1ynN8l+e>t{gxZ|&@hPlxoYB2q-A7!8A(xv38#$L3 zGj5>NS^ve#7z}_M6}`(cJFM7qbZlOHj@Z58vA;Q9 z$DSauACQ~TgGtq557x1ZbnJt^SlZjo*j;qmW`K%#fX^6!E7eWvP(QFPp*EZF5ly!M zxTH2+D}LvMjR0RE0KP22mk*`Ipa8nAuPNBzRxNsNUIDDk$e!*{BXEBe7(CxoOe5NS zdNk-OLWzeOi|$|gP#Z_9`{^H)f8S0=-y%ooZbwx#`eGDjkqbNc4mr|sR;ew~EM3~h z5%jSv@dbn%gPh0s$i=g67`sekEZ&RU1!I!?k7@1Ag=G};@*K-rO7^kMrOeZO=5dy( z6qn&jb_HFna|^~~xcZNQH1c^QWg^Qp=COvLgLq66EH!cFX1Id6J$yT)z`l5a^I(>U z1=VFukB#i9`3bPNopP zRO7pB)3nKnVF1!$!ruZ5b~@Syq=Re0!qycy8xGu{GCi$ZI~sO|(VXz(TE_hk6%KOu zKcHp67Kag&5NU=lh6u}rgiK=Xf}3VdwxnwvC95S;4K%jWZ&FKqER|d+FqtzTWw3tS zDnP0<=QC4Z$*S+9iaP6iaviy{rD)b}1M2&|yA$gB#{X4)Yq1F?+W$B8eMuc9`+r^E zJ&NkCZy0d#A8J&1mPNjzf>UsDMh`3~VTg%+IX8B`9FvsRIav!kRDZXkm@AEHp>cr4 z3T=LYNF(keS8Ta)kqM_;jWpLVJs||NdB?{pi(f;3wD=uxI5hkF70WQl%8*rt{Mxo* zZFC{a*BE01Q~teR0!%&91som}b)afS8#8*F4?FOmLXO58F))IEd8e+>EWHjEzDTn- z)#+ESe`z--9THHC7_U3n*GFJ#?Ng)10+IEX+ zlwvVsC5VP|ITTs~5B+65){iK3+*eCWL@-ocT4EQf=vfTZVxiNLl>}iG_YcQuCafg_b)T3AwN(kT`raynjA`N^z~^oF_6s=qyM# z{icX!GNnycSiQ-`4p*a_#=BswtHjLS#(e%)-6?0LNmI4WpVj))%31$c{i$Z}soBH& z#`}ud!)nGlc7Mv9QLR5g&FkU}?4@G`WxtsyG&y6b+qWxU1WI>{4YSZA3+7NrfZg;7 zWfmOlHbGs555d0GeAiir2bQBQu;2y&($eP+XgAq2l zgE=a?U^~!bZPXGXavY{?hNVt+P>>rXMOZ((gHP(>VABxG`j+cUUO@zocy27h-4J<1 z7mSzk7B7RR>zzjUxV~gOGQ-7dcJTGBSZp}EX2F^AfY5!V68$sP=#;LGVk{h#x|NGl5AeBs z?=ukd>I}`01fJ-D$me|YAp|)n*TmP|u=O+MoKKm#Nt0~yilNEQ&RD=TAk6LgcA`}qft<$o`Ow$^6P?XY zEyadMFfZWWF&G3q-QB7TeIqM4t|-@d?1Kt649aso>$*d~K85Z4vzj;xTN`WK{ZP$; zN%)SH6T1pe&2PvKUNM@Sfr}Mu5z6O5Mh|4ep>5yQW*EkU@9{9}^heFxD2qyrt3{XbZGMZqVKUywa9t zr(R@}7&%-0D_EZnZ%a@_c5o?Q@?fv&VkvN8o=Xlvz%ZcOzw>amfehy?L+z!z{j=AM zb`Vn$^(!;lb=Rqdl$Gc!ERo$;I|{2cOuE zv`yNR9!>(pd25islUpTQ*|fBTk!d1^%F$_D&44nHY^EtV@C0+RT+szZY-p^EgE?;h z&yBH-;^4lfHN2uBVzw$=?Kgb@u|KtB5s^Q!HGE{o`rPP3h6fzf#PSB z7^p`>VF*c&hnPff5%|*3ee+;c>J)cBUkc!9eT!D=!Qrf;m8Z)n(H-f#qMt;%SV2;) zFRhJs@@+v6mee0@T-Ywx|I^`|;0qHta^to)G=$rjQ{f3d&w2{~_9rKf;-x4QCW#}r za+09xh77T!pL5q2aZpS~fbjE&82E7sY^=_g$}H#F02NsxbsLo7NbR1+AtE-}ba^3N zW*OSV-QH3RUC|=K-;6eF>)<1aYjFahR)n$wBx{Ks7#dKT&^v08ozc z0tZa-fHF}M@D$>Tn)^7`s4FEq_nA$#;CZX5_Mb0JbpcClHPvEdK(%4c{C6#N6n1wT zmo9K)o!D7MaBF_?U6VE#W0c#tC>%2aEZ!NV^0=qv_CoO-}T-$~MYi11LOl){$s#+0LfM#t}x~q-3;=(InZq z!N#+4j7lT#kuIt<#@Aq0baH(@g{4t!p~`iEjhu_u_&|}-yQX#AU-CNklbU#MqMc?@ zkJq}r5Z;Rp;L;@#k%VV1q%%71EzU zl$PRz8^^-4XeJn$CnqvG8T$z*aRSV$j!&#FWn+SYK~CB2X^Kq%9mv|piIu@#`yYR7 zh~U#$Q>I$_D;l-(6`?=});Ow49NzZMRXf|tHEnVUnRCd-US%}sB(>CazfWq(6$&Co zmdly*5sbzLTe{ZsiaN(y7i^#-U{@DpqY~wP=Y<;*Lg;+3eQfL3_JjX_8X}K2mGJPc^J~#k-%>M!2d~ez;l@`Y7GIc-s&v_<)v@pQaDtjBV z(_&$<01G!^u?=LzG7BmzM=YSBdwIL^+Ezk=l9FKq3fjDA0mZ4TuNDZ zJHrDx;k~*3uDRj<+>$WHx>O%U&SFo=Mp8*@z4)~vTytV;)ZZAd`G@q}fRC@gcTb>- z_8RYb^!{um$hraWEUh1JB)FBmf2;iFC*2?6kumznv0>dZ`@HpI)=!_k|C5Dtkq}Kv z*=t`U3P~$o#!Gp;o9+^<{A(63w1JofMQS+^sg~C99kq{&{7mGh$Z>&$%=Va201SR~ z-3G80RmgGcz6`lH3`2@|r7hzdsL|qe7QK>s)bpQADS76Uag;*(OwWTOjX+sW2J!^y z-18=KggYkls`LERSA4t22K1j%_;tK&pbcYsbuUs1tvKo_Pf!X)3F#>hQVIn;3B0}Z zIu^gQ46$1`!2A|pV?WwJ`c?mQ*#-&^;RR#g*>Ix%eBOpa{rTG)Cg{&6Z|aR4j(ICs7PKF5oYq%nws{Km_RK1jp*}V2d+mI793fC zrVFv+885BzUqnzlz!f7f>)~VF-{lY5XjW>mw*)v=kj<+aA)7sy`Clf1nd@1=xS)c_ zwfvMst}1{>-pJns45%>X&qehxer92bSUk-TBc2Q*(s{})SCFv3iHv3sZKPaQdWVq8 zB>hRdT?c3ALirk;zBTfL8zIZ*qJWU)NNJ;#DV<83?+I6izdFUTrFzvRN}=UC{DgMl zY%BcqaYUc04b@Ul~8y_<0HMz4+OMA5AO6(><*>lI>Z- z>NtZ&N{64DRr8}h-!wm{4gp^C{azneQ8f!4%tw^NoDGpf)cyF=>mx?2^o+nHtRf=) zIuy%c5xFFUxqh**!$@K@`bYrKKjkj)+w-)7I<4W`A^t~0h5w~s1Hd7O?qD5hV;rYG z+*#g4ial6~o0efZg?%uiVLFYLd+!yt(*>9UV^OvN%cqFMUm{^zaXr>m!DdEsh$k=L z$<&i-?v5qHWYh|~sL6vB$a^)rs8X|ws#vpnAq0*@f~Ct1BkmXQ40oyK4%H^`7xv#x zrN#XGCb10xu@2m@plYTekdAis7rR-rlj1$@^8!t{{19aa9)4*tO!VN$H}FU4xD@R5 zD>7Bfn{WtaaGutBVK|ob-p3m+)i$99)7rl?C<)#fr-6kgb^Z`dLedRr*A$e;-Nzg|RID6WgeHF4-Ga*KD&Sw?FAo0Fx_~2A zOlhF|4KPg|!ZzKCE@1rx1=Zh+>II~c8#PD(L1+^V*r&=P0J0)pb2d2)uJgT59tSt! zBm0|dRbXSh69vcz!P*@Zyh7oD-+bgiygU?JaL3uKF?N9pY`g*Ndm1oK1yiE{@nk^6 zW-y!qLFVyR<-=?!dMH9}BSW&>1kVmup~N%@CmA=o2_{5j*Zg8*~hwR&m zeSVhOwmVOg*?TfO*!pQ-d&$+6_I(w9Uvi>oZ}me5;=TqVXvN=e`-olYhU&>(GKc5i z)YsmsAb&h$-_ByP{gdEb@rnaWis7JsRtcgmocdU5u?jqHWIdHmUwMM4tsX`;UD_cigipY)-hX=DYkMH)7x%%+>D- z-th%1kbOe$uv0**%K;sRGZC|-p8T+4rRJ*EF)yT9%@q1PBCO=; zpW&2-5{`FYgI`EoM<&o((AFY3!)T~~U;(QiHEih1| z`ViGs-vo4JHVhcABk@3`1H;Ng-MHNWQ*Dq~%u$>b4Nl3q6Q}JCcoyOJKCq#G?|#p8 z{64@>;XUXXMS@qBR!3xiBtJG^_HWKIrA&;?l-HA`6`S7YyPjZ?x}PZXO`@?vBTdFR z!cbJh z(*mlPMx!=gmDcYW(dXMuBqPIx&tb`A@x6zls>u=w_()VIlveqXTmi)_r)2|z71@rk zlJhjo`^QihG~H{cfz^5O0`d4ZiP!Fncg1`CLa&Ws4||}5@##_ev=yJ2pTNNXT*w~2 zN_=~ezWqi21~;SUY`>D_nM9m$J_?_N41DO&fVsB&eq=>zL1OByBK0rTMToznXCCrF z1z_-ukCC_wGVW%&KeS8ps_^s2(Y7yMHQMI6F1cNsP*@p_I#b)#K%aUqqEFN=n+Hu+ zo3)lcy@OA{buzFb$q8Z;XPys-B!|a3pIkb`tF>fe6Gut4W3hPt0%{04LfG9><2*4SP>VgJoJ+TPz%B&aWOA2N=gSOFX-`hb%%6=cR>-0--=@EY| zz=RFNrn(1NkFH{$$xNZ$5}zr*x`P62x5FAmp(PV=a5-4`%xlT^iC`>Acv7<6yWrZO zIgA|mGsztn)(h-Bj(D^3KOgN!1)G%xOOyPU4z^Qf@QQIO#^CiP&vzhKafwGVk3X`X zcznJHz3$!+011;=PUI*RM_G0Mj&kU%Ccfpu8tnGtgH9sHN?Vo?bd{GlSR3IKk$qjJ z@wc6>i&)5Unz{g1COVO5@3uBMXV-C@E*3bk_A9%8jJhckh?SJ-G=Uann#VDFKW9NO z->%hx{%fLdClhk|Fr}TvU`Bng5+l+;v?C}WL}M}BLoXr&4O2=GpRj0#FM{QHu^#Zc zV0)^hvtTNh11igt2tXPvCz2LbcC6ra=0bnE0^_XezE{1!O#BYnbcp?R#>%1kE1z#xu6H}#h+VJ(n%S>-;<5)2hT_7>Ke~njQir? zMTK1Ew|C^-Pt+007nUR9El2}{xxJS3pHMvCD2{vAC@U}D*YXXuGQA$||JG22F`}X> z6RJ_==98=%k~mqnCtDdwD8@Zgf){J(p-#Vnpn+SObfTK-Z&&00FtN%+AL~05qnALR zRpDbqA3a`i5PZv1@$Cp5Gko>GKy2#EoGZmk zA-)dhU-@--UQ1v5H~1G~#ma zJ*osI>Vrxf`SlyVeohfGENT7g@*();jR_g~<$K#Q^2@eYzeajzkhh-?q{!P!LhD$_ z+e48jOAe&qyXuvHVtmK;r{LQGad1rNn^B*QI#C~bLZ8hc6pw&DJ5+f!1qfu2xA*pC zlvlrA4=n#FHW`v|6}7b;-3uxkwIUac+~M9OkZ@5)0tM&7x%%Y z`?^QgQ2L^ujv*F?E~dO}8{3MN4dtW1K`ewnEnX13kJ}dEPm1XijtFR@FJx7~*ia4N z=hmnv@qk%MofwzcMtN}|i?nyhdG~P(ql_D~+S?S$GeXOwTDpBL@;7*9nTbEai?cwh z6EVl4tP|S1>@BgAFrvIzM2w9`a;`|mtzwke*H+RZ2Y#j5v>7dxwDd6IM__U7#+F!> zB2G{Q@#h1I`x8MAcW{N79?DEg7v?Swynv5_$cSP^sQ*hGBKQ@`s1M9h*IpDPD1%Ax^y0^|hpO=y4LzCAXD&vWoF4yl;qAXT5^ zlxz>^vz&!A%ejsQ-3@$zT`ONBUoBrHW;#jo<15qi-?|5*=euua!-0{Oo-zui((_pq zNJr1Hh($e~=!x|{EV!A*s{*a0OXIybwgJ-kJjmb#>Gd&br#G;juav$dO0Us4!B0r9 z-s43(ndJFb=C6sn2IH?+aD6}q{_4akcPf9;eFN$E>qP_*9M}AnULXAKjU*1!?Vo$; zOCpCohier6Ki7|wAzl(joKBBy`jSYG-@|46|2aL*ZO%lGZ_<}UdOV0|_T!r#L)ypx zw@c6?**;!P>7WMO<-$JpKYMKLMS*nkbn$;4U-I-T z({rF>FnUhTOi!Gemxi9P#`N?Y`^^6mdj99%2czdzO&R&~RuoL-&oUH9$Da*Y=2KmP1V&0mGJQLOi?W;JGdtVI^fn_o2O@>+9Rwf+Zs z{A+f%i}fV#52j^Y^ZF*1H#fekBPra$XAp{q;||u=uZE;g?e9{SH(z+_SkcE=kI2}c zPo?6Ubqw)M@lPStCSjqCy@AW>lxuco>+bdmx|2c1>gHRIW?Zd0^+_(7j>hVyX+D-D zPtxQ6_>N5Y+d^E}rd!?o$sdkB{wDrTBzsip*{%;NZRBrz;uvstl7Hop=zRuo*Bw)d z{DSlUq1@8Zd+)!0t@KVWZ)a{yq52O9uVW%_4?&)c3#Z`w!teiy@m>8&3cgv#4Bt%p z>|fO-6-<(ZKD*{|kn~9CvqO}(Hv@t6ThmRa%{8QvLmgNxd=i&!75c{fAHh0NxR|#3p{9N!iXnn5db3=wC zg#=#wmyAZx7ucYAG=%sd@o9(Ho{5>;b1g(q2BlPbblY<%rY)sQdRHH-E=i529#UUTFsww4 zrOrN{@P9^KD1w!%;lC8yxsJ_{_%iy|2=Nj6HuMm%J~I0DaK%Eh+tF$7nPF#LPHR}s0T8BjDuWyk?S8t>}`aM z^eb7EBhkhG-Y+y4fBZNJ8A+m#X}uP~jGzp*eIanhpbtW7cp#&)B_e>G45B_J2Dab+}m%os6UgKT_PII@`keBMZ5} zh%&@3euk5g{!xTqUlb{Sg*!AMC}2_PLe2mDhH{EOU62(Ie_V!wPyt3zBoCGB#XSh= z7F{p;IoAruG$`_lL4zV`d>#e*UW(!iB^t~PA%Ao%yqhKn1=Vzer&CCCn z()S;K9gMzz%FMs7pkOL}m!d#A`(o5R#{+%8f_?kfmxj^~4^xgr?O?h4c+(EL|4Z}V ziFiMnt*TR0Oh@+=VF^JG>G$YuDXZJje>=wXGuF3<#{bz={Bw^z{weDnh@V8jr)lj= zbI!`-ouzf{?>%q$(Gz1qqqk;UcARo27kEb}Hie8>hKM+4^>W>t8A6uQ$Ero>Sv6S~;Mqa)As}%gNs5y@CFMB8j|3^H>82_R4ZQp`{*D*br8qAsI+Je?3f3d`E6bU7S(KPF>)W@T>A)wliAwrAcm znfj+6LjZG(|qsL(pIMKCLsoroTSD1=t-UL8<94oVO{*qc~O<2j`w7-Tw#< z_j3oQOnv^t`1w+Ot@ZiuPQaC6wf@!kyTIBY$2*J@F{RoYksF7RN-KVV2laSNsrLJL z@jXE&WFm5KpR#Vwfq3^p3+`#s&(f4?=?*tu@)UE)A}Sf6lK5v+mQ%^sM9FdZQ%pD6 z^wN8Ts+Lyt;Mw=Y7dYAmA&OZn;_o=<@5-`_15N zFA5wyCl$w?ic>}ZaUD(-rAr8=tv?YZs{FWSku@v7_!G`hU$y7r36|*# zCs?ZS^GEzN=KWo^0MD;9Rvd^c^Pa@D2>yQbB|24{y^ZsN z#L1})nGXi)sH2k(fW=As+YySwv~)dxb5`2?2EKm*Jb^F2Z-(k7kIcVSy{*b1DE}TM5 zTt@u4oa`nF`nHGlRvPnBew1mmu@#aZ_1dJw)Kv*BS`EiQD~&f%C-B?da5XL+=bA}B znEbc=HtnPnwv;LNLkOike#uH-$X{z5ueFea#R*UvXgUQ7#~By9?61?JtZ@pIj!xqK zNgS+hw2wRlP0H`?OOb-zKSh71Z5*mip6}2}__+B5ktJHq;htiIno!-9yzaMg6D88r zxDrH?oN9pNM*)DKO97I%K!Z!?T#1%7CKA}_FPvb$$w#9#+ZC)+@D~#mI`eko6SzPA z*EF`#RsJq3F8OVYoNDR4pT=??Vh*MHajT3#>XpByI$V--yYP-z;9lmxS?a$eV9px}Vlm?flF&Vwx+_ft!^JMJR}!in@7vcE;B774 zha@?|YSp|{r70JcvJi&}&?~mDBl)}5Yc6FBS5@9a+>gchPihWY^YOKO;n>_7)<9xy z#XEPNGY5oQo`5v0Ff-154U~gp%yHMz)=CM zN)#<5IfFmWII;v0jQY&gVPK7YEVZ_|skLP+|Ke+@<>S>U*cjzg+7reWYF&z`(!$^* zK90B{sXU!pt=_{%{YmY6*mH=re`k=|)5~WB{6QkCJ%bB?3u$jZl#VO-OOl)HO`Mly zGxqyyLb>xtL>;IVO%ifVvx%QUNZib|9&b+?e+ytzNS@#Zz$}Fu5@uFC3E`^XuQ*&q z-gF-~uqt~u<;fdKCgC2?PYE@Y*aNQ~O8kOy;QnWF-S>v`km2u@Beu#bc{-i6NM~K2 z%~AD~E)=L%GMo-Z*9mcq`Ou`RnFwKn{we)iM=>&Dg;bjmymx(Od6-hZHIh>P@s#rF z{VKRdmd?TFPa23%WLMRR!Yu(X42sX{WI8G`yNfCB1w(QQSo5< z*x@`&uQELonLAg zX!g$bwaLxr?3l*hIe1+%=?(r&-`=Z!mJIF2Lx#301)ADE!#~Ogg>fDWV%q7xi7Kp# zErGkjtscV5wvnOx-ADA1!Vlo3k#agf5X;WWFkDjXcjLTxh}Z$uhIeEAu${fR0IM$f zFhdrIdj)7ECaV1O-)IEI5JxXA8z5ngyf_fJ6Lx(Sk1^z5MD}wjJIQWb@~xf{9Jp5C zs0nM+42E9-r-^Dl22}lG^5>Y(Pxwq-=G?1X58x_!5Uw6oUb{1qIH1jF6|Sz&QU)6! z>is`s0c&-M?zp2W4^ z`e-)S@6_ves`c~gow&RO1m(5=UY!G?H#Oq9QMsz*QtBo!I{xCS3)6g0ozFh1MoF3H zvwc_V7^j`jYX0Li^T!9gI-NBXpFwShAkQ+$|FSgrUH;Ya+mec3-eKYQ(`BluQJ&Qylts0_ zba{3)U>#9;b}rhJAkQv*5Kz;~v(d_rkEY5jAbFIV9j_Tuwb2u7&XM(+{u&pn`lF;qYmz-SOsR2Zk1 z(LOG@Gx_Ic@VZm{a}x}Z+EnoE%c*6iso>!LxkDQNzt6j2C$OCq3wgqRAK_ZBL=e7)4IMVt9Malt~?l%cXOK>O=sGDzDS`*S}vX2!OGk)|aZ=D_^3&mMu%} zufSmawE)>u`wKdZud1C!k>H( zsIP^Slm5X8ol3MMdL{T9Af(LeJM9>MZ_qS^TNlRlDM*L2O1=jHwilmzxf_VO@h4qOeDliQr!cKn(Ujr^wh z0v9!=`GQy<$_JV%`2;>uWrBu}p_1yqON)E^ijq19P>lXb@kiDDQD+^USAT|z29Xx# z`jY%(OHvK#VpNulQ35`~pMYzS&Fe=y@6%@Z&_iPV=>DgZ9SVc{Q~tCtV=D~!nt#Cr z9$fjrH7U^Zz9jThGebAiPxmL85TJ1}fX%AHpQJT^<)4z9pUI!{=ZljmK2&@2uS$kC z@sOcara((-U*dYi3*a|R65vKzb;v+yuGE%VO&0G57p3n)-X23=nZ|#z$NPK4kPw-g zx0}ZIc}Ua7cRLqzkOX)B#7NxD=&mjAT;+NsCJp-Ua+J(Qn7;gLIOP*Zx0xPEE-iHOLVGg z^7&4qXQ}>5koW`*Gecxsfms@nZNN*B*SDtee7t&L`eBkrza;TrIeFrR;h?GaTToeM zd7XjZu2gqLn$`FznXwv-U-}dEH=TNMI?aXB&i}@0vm`?+KmQ#f#*tkw_`j)Sssfl~ zGNqw+O8ooorv`w77vVR;(j0TNF_Si07^C<1^`8Qd9uGHjOJj~x{ zbNM&f0{MQI|Klua#XPVjQO!m2vl2H2x?#sI^neDNL`}Zs@p$0;Wx}StR==M;uo|c# zevE&tL$O-;^wNwEpF+#<|=DA;G--cyDQvXEC+s0`7|HBgYoi177i=L0S-Vb zsE)vN<>5BR!9(#U%Q z1{?59Q=6Mo^Hri|y@CGt@Z0Z1h}>)b594h0+ET3%T~@`Ow`cnpWMIHv0JE-ykj% z{cB`q-g8s?H(mWt4_W_GqkbbZ^A;Xj{S%G7@EAn^BNNSgZQFyz6ajPOYxnP4RJzvIZY|AI{IC;gWRzUzp=Uz8C( z>Dx^3XQseUC!a{=4nDr$p0AH@BQx`^TA>vjRKAiPAH4phM*T)+=6xWw{;w)O#t8^% zY4IWZsaB`UU^rQl zD0G@6U@KI!J2@Y?CZ?h z4HG!5lr1<27f(LvKb;N%&DO=*0NNN8QMbALQ`Bj$#Mx#M9# zT>zM83#FA*n%X9D8j@)*w zIrX+-^H$paCBDfOM_6pUKziU@G{rPnG0Uwa=_Wk62LGG*%|Q(3(im>9&Tua4ZkcnO z!#eYRV!1^s%Pk?6JCC#6ZNv-jz&W7t+k-?sIXLb;Hyb8{VsH^Ey|)6V1{X2hzU*c1 zbkE_1h)*3%%m28;$p0bn9Obw9_t}ttq@JJsT2!BZjYC>9%mEHUe4DS!#B@<&x|>v{ z^BKLgde(j*#+hy%I1zmGrgFwUkn&da(k#w&S65+o7w;ux=Dmda*8w)qoJE-()iRi; zD%?q&H=A?bbp95!&34RQ#X)e+YohwM4xq?rwX1l^YDd79g{BeT?E~LUM?+(0mcNrs zKlV1pkF8uI;8YA+nfe$d_Z2MLk+FYOe?jtm-{h|-I~;$7UU&$9LxR7<%bWHcT~#Zj`6ZDYO#jkvFI4NB%ej5!j*05YjKo>;1Y9` zMEY9gl4jW+g%Aq_sHj|y9grQ9mW+|scTTEeP13z_SGJ}5=Z}3DH~a_PcU2|$M|@Av zJhwj{_xZbq=he}Rkr`Hq?+Kda#-;d!A{qXa4+s_u5h{@qUkAO}LjnHKum#^gO8Bm5 z-|3-yPWA!e=hF-v-xHNchXdB)>%ziMJg<)44HUXJ|L#L`i9p>gHuLvD-3K5Y$3EE~#THtOl1c~MK#l+}p@Q^1K@-xrWC+=zcmSSMD7JG~>o-+kO`CGBHzH7>o z@$jLIN2>(gK)%&fDK=q^S5rzNS|aQzkP4_fXb)^)95Uj-j@?C2yQtgisIV+4GVNT> zlNW*I0;=67Fs0?4`P$19h)~tOosY+yIzAP!~cfY==5d zBVU;0Nq6NbL@%+Qy^aJ9%PkMdB`awJ_&c+}RmBiX&M^GH6J`G$?^V5XswIut&H%$JZIciyb4Ar|u4STk!%TUe4nuHb4W)0Ut@)?nYQb z>L`H}qUORl#U}i0&shA~&A4U4#i?Dgc!6$MMiKvPn+3BMVsY+21|9s=ZTY z_bvoK%JC*AtP7|vuItee&H(>lpu45fHx0$;pGZ{4zkc2gE#;|`Xtqan2SX^}II2P2 z)qg!J?L0a!@Y1tS0haJr^Oc7;0xy{s9Vpa*#vz|UB<^?Au)to>9huh$&P@W$xZHxl zC4`B;vohQ{-q)lXW_H%78fGO?265HbCYQ9>TUjLVa0rBv%R}5In*x~y0H2~+WDk?XQJZe8cX3yVzErZQ zb^>_>EqSZ?-*i}Kz<0nH3N?22g3x5Gv6GY5tM;H5|MT0R7&csqRk>BPD(BlJUCd90Qi^(lu-??yrqZye~qr((%!zOF#o#gmzihl&5s&P>E##qfl{i?5~YUL7Y4h)VQqGUkU(&;Op0 z79R}B5b%kO0ae^T34K7+3%pcF%MI8Yp5>W>6@Ot%Bw^HjQxRdwJEgo0LW*ll#;7)y z%OWLARqkZz6m#t^i_>OSw0R2scR|OG;W}QeK0bc&&8MbmHs_@yz)%1Et+eeQ<9m`2 z=Vru{LAF#(50hW!q{k2ZVl0br?!_$ZTE6@(7EQq~ z#@dLH;jsDTzs^WYFW_S=lNjI(ngJhUt;EQX5g)TW$`$e`MWv81nv#sqi>gX_nfi~^ z@+M8Cyuy3U_auoY5?5?nxF?5eOG_^14#;kAf&5~P;T|hgsAadk1#-AC?_nw+rpU?6 z9{)_Y<1~ik{rx5M&sC=<64d`B4Uql*UDmvp>B%g6bbkL5_3xjaSikQ{-QL^9?Y)=j zJsFDDnvDH(sPg*mB=Cm3dkG{?qGuxi8f!CY_~mfzx&J9kB#VkyPGIJHT5EA1CyTiAY1)mzSo;hwRH*5Gn~Li=hgVtsqt294>ucKP@eN2%pud_!x^aDKv5| zjv&y>B|V2}1eJvMAt6wUc2^Jbd5clr)rT0#7Oj?^;y@iyCdD4p;UT}4*#uIDm;N*S zBPu=_z_G}0$|9}(I8V-V7R|_dev?Av!qYaz4_V*7H1%C@81=n#Z7P0!Ck>8Y(s-lDa(%NB zVum5bm0DDbGn0%9IB`P2R^?`qYAb&ymc!37HAg%vUII%TtdRzSr_z;%K8FDRtt->P z-yl*mz_%O%{0#iE0^5QT_~oT3Uy)xPMLeOIDlYKLk5SoI z#K&kUm&YnTN%Hu;O9$hx-Gzfe9)cc8@V~%*qSWzy#$|)SzvT$R_oRjY-W7wv-+kgC zwm&_8WdUTeB!7&m)YMy|W*dT@N&NK`_Bo~U*C^2*^s<@1`ZGuo!CxZ=ZKcq6@G2Yj z5r)oX7wJtC=F#gXq*r$)`-t4=Y0g`nsn?{dr#n!YMx3!7sl=b?MCMA^GLqGxeHuesK|Q>Yz#{$x!^V#hz-; z^pE}O^||v?>w9{r`iAVUUSNsQp1?0%A}vFI%_Nvdj9>mZA{G8aA}s^_m&P1P_|*tf zNp64Jw|H6x_%n|v{0|X0lMMf%yuoavmqs7a_NRg0H7NXK>f|% zL&{U8_g)@QR#21I%R{4V9y{|rMP6UOeqX}ta7pn7Qu(C(xs;#lUnBo72g#r1DG+Ls#NYj2gRhG1!@V!7+lQ0KpXq!GjxyrV=;C@@Z^5?VOa$d_CoXCD zyZ?PF;(Tw%)eEl&FVDi)0^o*y?aDRy?BAEAe#yp{T=k0sUsk+RGbVVsmD(ZIzfSqj zKx#7f{PY=k{6svnX@B$H^!uB4YWti2fdN&G{z2!MVIDxY9zAQiuVby?Qo}_#H1!qeG;Pn34W!E8>2C=3)=b=5fVW zRZ*;}$UYNsM97XXAgubT*?&yg&#U6$><`T1LB*%?w_yIP*>xgp8G}U6|1XpuJDvkn zJ8PGs0ow?F1K;i}_Bx*pb`#-K=yhYS^8^RGV3`x=Q_!@TyrwwA`pWJ*iOA|pLlS=` z^!IMI6VyP5ceA&tvsayu&OV5x9)CRRHgtAF@Upn*(l@B{ZpCNpFHpZ66l}o1T+Y8( zYsQCTg~7`Xpwb2h<6q|B%b*>u#y`5^*Ub>)PsHTleOA(Yank#=r1yfP_d@~D-j zWzYIpTrB0XXrtGSFahc_#g6b_M3QDf_HI@#+NX*gUY355{8`aD&v7ZTtrNb$M?tA!>35QsmBC(A?5P{R4t;L;_(6T*%#o8 zU*JCFE&7aq*mkj^$#WiJ|5_2M7Vfgqh(tVYX)XdRB?mey>Tk*}EkH08_Glxd#^0P< zYLgtT2-GU}SpChoRi~jmq%T7d{w`nm7Bm+I4Z^wtbtEDvW-lkWFh4d%aQgauU@KRI z5y}~MGk57i#VT`zOy!-mO&z z9H_*yDu0((QqikG9wY+!S$Hb(or~Y)xK${tJc4953s7>nb69!Q*XacX%A<0?<(5VE zaK~`k5)5%tjv&}dB#Z3b+2v7x=cvTI%2>*qo04}7RZa9r$nNh5g*%3!QXKifky2rS zi*;CpYq&F)mj_Ao@|123m52QuwCWH4^{cYg;-ES z%=2w;Bt&@M*)&Lmw+5d_QJ1#??CgT2r?F;j{{><{sD4+lsxTh$xwB?k&}V95MZs+y z36P}0APN3f<*9alfQ!9A)g!b-Z}(mYJIRS#!Y*UK;e-fY4;XVnaI%=+&B8oLt~!7vCJY01cV#09Fz745e*#)bucJ&p*w_@s(< zX$7tAIoL%uxnhUpzl*>8UVzKL(iObSjd1`PR9e_Pz?-MRqm2=WXx-eK&7pm7e@%`9 z5%nW!8%hLlcHH)6>`X_+=B3jO`rWVgEL2R~Wn&9%$`xmkQ0M8hudvintixWax71UZlZ}S&f zo1lGj=&rL{=p**NgdoXU6+1RdWraIOJFq>#j$H+3RF(Z!k3R4vh_H&Bg!C}a&Ual~n;A&ID?VJdM*>+c*4+Ks8;AxH@{y5X zR2F^u@6)v7douX$ip|PVuoXF(qD4TFSGE1(mayh zYFrd&<`2Vv$?a_?=_LxQcCuzC+euUl-sY8qH!iPUg~KdC09U(GU9suz9BIv_SZ;O2 z>-SitHLuGEiCz-|da^%M;A-!Z{SCQpdo%8Rth_BMuPp{4sC=W$n$wndNgJ_2CsH8Q zV^bwSpn{NIK_jtzF71!RjG}<%7Er#+N{e;HuC-@zxT-OPjb}JssYzaseQ1`diqPHI zEg6apud0YtNf$?|a7>c4CR&B8$)C^wY``pV*`pLVlm2TlUH_)%ST+K+x>;xeuef7| z+a3li9W6uu^8K!Yv^rfu;|+15i>`0Y^d+id6L4m#wh5{x$`gaWy!@b4Z8fM0jYkA6 z)G-$Xbp;mTga0E?JID1VLP^VCqq`$=q4i2nbp`TEvzslB(kyyC4G-D$P=*I9Jxs@g zjUGNhfrnxA@GU$HUzrd6XTy)XG}l$WMfuk6CX>^`jDs0|bgK#e8UQ5t)z;?f zECP&#>GWiyr!(kjST(*U0NDCyG5|hbU^zcCsxI))S&Fx8s+^!>lWc^#Ncz{)FaBi- z{@!dW#mm%e{xa(u+mX_Ygx9L2S>I@_mJaU3%ipS{>UJc|Zomt^zJr9nN2{e+^B(Dz z@b=H8TcT7B*j7uowBqrd@olSl%~b9Cs5XpJNccMd z$HJ&Kq*bd{=;IYqRrf3^wZ-Pb=nKxvbp*e&++^QL8yw?ur49MxtYLq@_wM0qF>>Ou z;h0*<(#7GGwsEd_1h;=?)nMzl=&1tgsTIHFgq#LD4D&UXbNU;4*MNR!4P_6eH4cs*QhxC#F)4H}UOprLj5K|FVuo zIof8EiY(GbYR^V$&m58hXc7Io3@%w=b6% z14qz9K1ZbbCK;l2EsbHhA_`ZHRPQbwE=w1LR#MHhSr9{|g*ZOT+9Frr3Hk?F}JWz8xYCO^h02lrf+zu!f}aZRjT z7FRW5(qtab^^0)+V7)CLwP%$@ep4PPi>!3y$2q@uX>H(RIQ*;h{7&Qf>L#5O1sID{ za)5!jh!TzCZ9*G5%0odH+I4fjO}adUzl&^WR3#d99h&k3{9Ff-_XGSKB)Nz|-Srk) z7fF&+J09e%<8sP|)^%sqJez=Mp$Soy-mgXt`~aIs4*4>!l;cRSIgXmh(NMS6UXfv4<|L-T>lNG`l<4B zS|^ei=fWF3b}HV~PRz#ZY5bLUqZO}Z{FV2h4X@MrEAPAlyr&ciC{f zev7{*c2G$&rm6PW+Sd(w5WdwFt<{r4T6bg<{ZHcwYPZ-ZWxATeduAA=oS~*9cEXbn zB~dCB{;VYU6|Iv(q6csmN$GUFB$NN5lu9v`Ax9|YJ8o%Xi_n-eV6%pByeM9wrMe49 z_~R=)B&1J^>1&nr9CB>LY@nHPR9PcPZ9I%qZH$kaJTeyvBqeE7;^l;A%`k>OXB7)2jN=PK-6?FzFRrm)Q z65+$1fUZTQ9bJ1YjKr?)zkL9@JHBTq=YDm z%3uo=&xQ{cJ~2mH2_}S5`V#IQ@9zZv^px;V%4B~>uFD<)bMp1M?om~69QZr4kw9*W zDm$i;s=j*zmr$$pfqnfp-c0uO9ByB)g|E$`X}nn_WK5LofKrCen^lQ9N!NBLyoLnb zO9u3G_`spFHUBA0XPdN9DDrISxz_MN0W2%q!8G=R?-!hG(-LNkD!x+^@I85Ge9H;n zax=bdhlcMO;Je0zZ%;;iTZ8HFHRJO+n~4CfLD|N@0(g-6<&J>!Ab_P zpBZDCfzNQwsB=&%3pNpN>%u%^gFO# z?;6l=*vn@64dO3)+($IzO&k9g{+#45dW<9UkF zKFwbQXEdY(EIe3wVMkfWSpiQFPPc*ISon&h`bDK#uZ$X1fK`bVs;oM ze735e4Tn*}XB>?W02pAxcGv7qUku*IG5NpkCVO_ZCbWi)-XmBL$z zspZ9(iDF=r`ot^&4t~mFD}xJ5e1$eOS?gsHEj`s>|I-Q`SF=2GnUG|d;E*^eYbFtr zEH|q43RHd=X)4NRHA=DWAS_1jWQoLScvKrek1*S*xij#HX)HBs28_sAXvnOUiy#gm zBx#19pmSrjI_@U=2G^MA%V$-niCk)=t;MX0Q)dwgLEykGnS^>DPOMksd0{2PXN)sf zyNg{VWX+llLaqIY@X7HHMmJhZk7{cRjMif2huI7t6+QGfeT(<{O)Xa8JyvhDmrHd_ z61o2nwBrwGCJeWP7QSGvM{i%as~GgMN7P2i>&cyrvbk<7i*e6$wOr9$Q{emgOBmi1 zUnPTUV~{Cf?tCM1;>A%q0{J2}C&<2)T1N1$yL1FNDYrq-Dqc0{Tehf>ynDShqYMzw zF9(1P0szQ`XGdw?x!xMdR6FwIsP4bgJjyU)kPIUzgB=-gN~Mn9CL}F4&;uYle>1Sd z`E+~HX0N&V{4TWyCXj2u5N@J2kXoJ}95=#I4)L%C$APt$BetUn2cmUKOYA0~Sh~p1CBl|R4XTy2;s~b1(mZ~!kgsA&YjzM|2aePMY{12}=K#Br z`p*#@HxXX@J1YAiid4UH&_9w743lAF4AX*?Q@G7laQ8kpi}ye1+W_zhGGJ61&Ro@62z zr}v5`x*E$XT@=Cctj4RtPzJbMC9NN!pcQ!f9qZv2_rauxhdU?sNB~A z3kw1+uy7mZL-DY+Rodtp(K=8aSctTRW8x8OD}NioDsiU@Ma9HJZY-@@L$20-97z#& zEpB#M!}J1tWZ%CNeQ~~A95I9Z118519pTQAavo>B1PT!UkP5Iy32kZ(u}nc4mVvMz zV-?(y%bP@vNy(c~InftOIMB1zDgdaF^_{r{CJ_Qwb@IrB9bIjWIv{hgj)iu zAs^8bsHDv@7FkA67Q2=O34B~7)TZG{Z3hWV?O)H`ue@RA;f`Ez*-+hPLhx+nF-<~H z$QYeQ8Yp6yLFh5c#B=fsacV+3l8iKEqg-i;lA0-%EKAI2lPHRF?$oHIVlDVvth?oL z&gPwoFa*(A)rigENL06Wn>&X*;xM!vrDMehqf2-`UC1LL_G!sPXIv{j^PRR)}ECMQR%|5e1PD^&c}z7ts?$M1OUvYkr*8^PHJ=`xMOhy zzpGd)jyZIN2Z|AC;|lLBDsSzLWZ}R+M1+oW26)kg?%IO$=+?Btfm0wAc~P&0HCscd zv2_4WC0Zx?G%>q~r`HA{;(ig3fDn(DQ8F81hDG37lu1`QDxws*L$R&=oC`#GV8zpc z=$@U_Pa#RRM&K&(6L(emcLFfv=#BGe8aqR8yP0=m5k!CkVnI^?t?P8607Z$F4g zG8zJHC&j^`96#QfVC{5Ngw`R#4ksAn>_vw(f5&>n5LqZ2AWo%U$N`uf5VA<~n|U2^ z1kT}D#WuE?f~@dXflnn|aiREd45i-jG>?xr>1WP)WoB00_5;b5a`zDEqr| zRRr*95*?um063l$(OVqxxTPXGsR>Iga927I&V^Mi=^-?^B~iDKyhw^~F*sp2!iP9n z*Z>%+u!`*_<|-tLoD(Gvaos)tGuBhDU$a|c)lOlu)K$t=B+Y9`Fjo^Ahknl{xuaWmy zDt-+re)F5zHXs7zfG5X~8j~eU^96qU%=pc3Lw_aVXCfH&8!$lVA*U3`#BoEf5p58t z37XN^#D__taCa_6;h}rbWq?g-lt6vxIA^DLp%^`9UV_wM;RV5Qz6HU_K0ekd6>rx}fNa`SGgd8x5?c0APQnl-4w~Y$;{1CG7z>VDFWRyoXj?Bc zFUOg9gY%5qKtzJMAbb}$;5VjRa$u#_G*ion+f{x-7x@N&p||D~2WkOp=4~u4k0HS~V8T%(f{j(jNgDy($ zpG`GIoYomTNCL&FgB~$;Pa*Ydy52!&Za&(*qxKKU+U;y74I4BIGd6^8p`S|8nn=w_ z8XxCGb}@wCLCX&mxx)Q(5l?SNnCetMHe&g7s`vy~BXp3(t07Ft z3XmwL4nJaYa~~&hMmWPxC?s}cZX9s?c@qYQ&w z3$Xtg#=2y5GV+6rO_}LsO0uQUSatcrNBzm4iZ*E zFqvf*$*4nX3zt`DC~G0)d4G`&B;=x#@qvSP5|5%Iu`jOJZ4Z%5- z4>9Z+$DS@f_r{gLoX}UL2vX;F8b+YiNie z1RJ@mMTo~ED;v}G&v7Cj53zTS3;9TGiMWW=gMLr8{Ik=Ig_V?tgE>E(tE&&8W6y`l@?&gVv58NwK_11GcPAR4uRwfMV= zFr7ei$h5)UNPp+tvSz-?Ilk;)&S$mvk^sB_e>pZ3f@2RM;D22alFOP&d&b*)a3g@U z{kO~YF5CP5+{C-^a?~5-x-^psd1PFUkLK_hU zroxx~@6RPst-_wmVY?b51l>j8o%YQdG#z-V`w9LmwDbW10g$L^)Pv|Hpj0#LLRfOK zyx@1@w7SyXUlrav7yIR_XcAx{P&B)*DrsJT-Jv7X!%@I95m`%`U9bppQi=#qnxN59$1mv00^&8+syOUX1gf;ckw;h#19~;K z1~ZbAb=QyA!o8Ao83RrAypmH}6ri>EBB>txO41UQ`(&%y_qr>h%STngxeRCR-J^L3 zx2baYIJNqYdiz<6i%8HqP!*Jk66mEcf^%wesr|#?YvmLOrFN5W!{UAl8YW`@R1v0c z!@&}B9c-r@oEMj^VMj&F?WgW>6642Wt6=yXOa1*HPWVYa=?3PbpHdhLj_$ zx6=+RZ6=BN2?DmhPgrAy5^}=L4ku!IylK~+wFL`RG?NVXbG&6>jsM^e&o>Orx2yGI zBb$agWNn++qRzEjZGaOdusUEMk3L{w2#4YDf51`QfN@=>4NSFv;KG1P;PYt^U?tT7 z0`PZPh;~SXj=3}fM*Bk@wB>s)ZTa>RPS{E z{CN)6_A>Rq%p$Cn8p7a#D?ry+$gR{~Grqc_p~j4?%vxhKrxP6&9Jhc-fyS8jm?O(75mhy0Z z4un2#A;fbb{yK2$6(yr1L()7fb2MoAp# z>q5ji;?E%w)#~Q8f!84b^soo5d(pqRVHZU)%K2J?J>073osV2NQJ~Yi+`C-EAHt5u zJyn-Pu*`ret8okn&<%G=obM99BZK~ZIR8%YP|5@kkWBy~Z@P1lgvwDEo+e&OM;G{_ zUHFbK5V}bpkqxm}qNw^w&5vDq$e(Kmq40-BJne;p+ozu1P1VTl{Y3rt0WQ*;OGV@lQ`LD1rIsRndD$0tsAPNALDfh1BX z5!YDqR>0QFb<*_F9;WPSPYKXMVUa3|FTcl-=#>=c&=kw_g-P;Z`1=NUjzV2tRa%5z zqI{?dFOJ~PxsXL}N)+Tv9)IBtq$s(_!r@G7;9HDA6qVp4u-4!`KDLwZhN@l>y5l4W zs5h5Fk*XS`(!#>A6HqZ)H#}y@tV*0Kiqy91lD8>%;*O%~ifu~`Szd`#ELvlQ0M&+5 z9EQh?Rq0CX_s7!Qns!n0ojWE|$pnC{vGFKJ#S-A*FqAc$32B6TpcLV0{?4&%g5{oA zN-Qw&btb-+0KSmz5#kR1OSOZr?nWAl45WH+Pn(rAR!^aowvN(ne3hQd5TU~Osz*7l z8^uy$E7S~DWOtyER{9;Y(LF!mSPmUoK_^n+Kd~fD$LE%2Nseu;-I#58Mu4rRdW$f0 zTV(IvtQ!6fbDEQsstboSET;!1p@Eg}YdkA?r}}k=C}|lT+2M#B%y}RJC`&IqD4X&!jM5Z`LBG zcHS3nf3s+I!OIk;jcA>X2Qcy--)#3xht&>7kxxU=c7Js|T3yk2yP0z_7+|_%ds%yD z3=Vw#g%08>t|7iDt@W>+h zGB$u29ITM=r-j7u=rQyYPAAyTdN$BvFZyb@=Tx?}QmWr3pVrjY14Ind0G!IZ4r^Vz zE8BL(#w5HtactE$*jBiI+O?u%40L6DX?s6L-G;wL-nLA6yVSg0ly{PR+GoJ&b^L5? zQ))w=5k}2K2rR*Z5T4TF%C`SOo<<_dG-7NZV~-%^4LW|IP^7e}j4lqsQ$=nPz4*x^ zY%l9!&6VC=5^C56VT!d@Wo#ZD;;~6uvz-#9HE$?cNa06vGQPk)gAl3YT>+s^XMoTh z8gb;alv$Wlt~ zV4Km+@o2mm&vHq-!ybuQojBJbSQ+>CAGm#@??IY;STGC(O~6n&G_$<6L10z@K7;wk zul(Q(>lonGvcE$D??l06&5@O`8YwO1ov;ibZ2n^K* ztpd3LtwQlz5JVpIH3PLD24ky3NgCC|Jv&^PAj!tWHwGU`XD)ZqF#x1&pIs+I3LbP20Pq+8b ziAF-7iXayZUZOgjF~iBWA{(Jlro2t<3P>t?0G0*OAEs1&Q+tn{^0lEVGnP;ttrUY) z?I>%b@FTpFT}?5C<;_C`I8t7egI%l#I#M4F72;?#7Ei?fqzx00ph!m|1S)%()N?r` zO#(hsbOBfZS+u4FSHllTVBxMT1j4nW9AXuDh#)>9qz=|ORzHS&Y+|S*L_X=eGDK6N(q=^Jls6>>IAXR^M1E9E`e_s<1~`iNFH?MD znGxRzw1KOJby1AaS5wRcj4MC`eiI@z6BI0<| zUUiv>VMN{x`Al()V9$)%OhJ1*8^z{{8Z%;?i5}%`K-Vzl3Lvcc4%9K6G6qou>CB;l zgGV$DPM=XY^-;G-(F=o+Xk<#x3`9!Jm`o;>=;lUHe4`~@d?V}(vj1uRff+*sp77u( z3z1Z6?dK_lFdq;qgp;|j%3l_uWFcY^#>VFkG*zIYWP*aBao}R>UNS%+e=&t9BeC0x z3j_J&MTduRiR@e}k#Z%Nuv<|NMg;(swEzwY`uZr9C6gE6NI()Ioirs&1RD>h0F6g< zu)0j+_5)cB1=y9i?LH%lUuJ7bE4zY|OZicqJHAcFJK-NGhbyz%m4o5Ztd-}-y?ck< z_L2A8(yW@1PMD4BL&!odcki;&tgM^*=Ox&) z0BKkZyQV<9JB)}>c?qmYQVdYGI9@A!WrNtX7=VR*8s_K&kjSn0*#Si!gV^qo$jCWe z7&){9n2ek^@v(!w32U|*c20G?nck~m>Qs}hgQF4FG|-2&WM5;t6S{qhVFq&VB{zoA znUQKMFu+;8GzUN>og`mOrInhOW^`sl?C zkMaYhAv{$DuH0P}SfW$~?&??&SiF5f;HNDM0!yL`0(XVn5Mae_2;6D3`DTW{J6o## zb{5Wn-J2z?z!l4A;!~wjRN@HUn&mA2SZ4n)&d+kJ{IB<@ewI&H_q@v=$DQe1@j=aS z#qvoO-7EhL{zw~MrEIRnQCHs%v~SWoV7!^Ll+&L9Lj?&}oE8|K>cvb1XX55#idxy* zSVaj_}!@8GU^axCIsE|v)254p;BkeadY z;ApzA@>!GP4%{?z7M+6!CfbzihV+c?@~-J?MZX+=pqHWTvL3G9 z!S%4#vmTx_@tT%YuE42`p43a6U=k z5}MqX(gp`HTr~5uqc8qhDVcaH1!8HOImb%X$>N{O#sBf7EdCCl_al1oKb5`b>;Aonbp3z1(E4}mhvz@*uK&7l{ihcaB~SmM@nCOL%FlXbLO{q-1DYio5EUP>9-c)i zYpoP3>tVzZ_${IkixSjgPd`N|#4q;9gn#z=ya|WF(44yHv^m+=zjm;v+)M)n_=_QH zyWey;7N)}^|BCfbg8|k;&F^3^$YYu-pG&OFq7fza3`g1Px`!$q27XGjJ$0#gs+#7FtA`d=}A2Q};%BW7D1P?a!u;$5cj^%z7W^zC13n=9>>GCoGVXdfZ~8Sqd(Ryj7l%rL z2S3|;>b~-`p4(-B_y0U^wlzO%#f$gCm-j*AXK&;1wEyaG;Aan<7RJwB`9cUkJM*%` zil05TOB^!i@6KD$EvkR(m(1w=Z05gAn4e9DeinE1v(tYqBYqxVh4iye z-l|7@Sn#u&_Z=|(Z0fKeBZaS^KhDw5K75PD#UbQpC)Z4bpG}8;re}Ng8X4f+wRy9x z`Pp?h7aHI}<7a2R_b}sU+fEJRXH}mM;b))uE16F)m= z&YR+AKUysk`}vJ|E8Mj=ztXKod|2?awo?umKdXB}rvCwa72s!IxJl#U5c0E&-?Oj$ z?8KX7wvYK~-fU}r_9MH{01p~JyZ+?EjGrA_6UNULuMFX5-~ISu#m`Pr8}9eZhI?QG z(ea=CJXL7I4~>4dqWVqovxk2mEBwq2c`MxIXY0H4hz|>XcH6rS7(cswo$#}5_$t89 z(yKHs4k175p1sfftV?El(|vift@+t+Z!9#xgT~K(JnJyyXV!bd_*o~8Rp#vn|LPK# zpB)DA5SL&h4Ew>`M%fSh%L+Z;dBiRJ%=L#Ktayl@y%Xf^2_@Q}@enun3qB@?_{UW| z#P2&bHVzeeBOc<%@9?buLlqD4!D?CabU=H2#$UWn);#0>^dJ1k=jP(+es!_C{(bvl zFT3$Tz34HwALa?B8*M-AHN79UdJ^};ekRd$X7`U70@PIV$Hwlfi}m|J9=lQEgRo14 zppi$hHTLeNl{*m|bPo2!KnuT>t+BKxHi)rw*mIgWC%XmV+V&SDR_8#+(%l#m5#+?u z{j|zY0N5JSv2^m7vNZ;Ajjb_;9omm}eO+yfO@BAsE^k+(eg0d*=r3DOMjt-fSHQmx z?r6;(Oc(ybHv%6z$)m{pUTp-n}A_+%&0GJ0zUb6j}r51wF$^TKiLF)1e<^_BKql{_hJ+9eu;q# zuK1>Iys9H{Vq;N}v)-{!#>;(|A7nqsh*JWnT z;a(v0lfZ;?VDK|J6Mo(evb=F9A-KHt;Ep;R2KUDk!v?o8Z#84`FgU%MKX<(huI642 z&bOLHNyE&|1se`0-Tkbjg)L#QDQt31=FmKw0=dPDO-?S}k74nSvB@dXs}tMg6z*)L zLN+msB+TF?Wl^YT(PNE3fUYuA#W{V$7&gy&>!)yWAvWv{Sm=DP=HfwOSW<9QIkPu8V zC#K3RyYnSZxvGMF+59@qv3{$~snHhb1c?FBM_cEg-9FlksJ{ipf)Pducbct74q=R8ZIC-5*#0nW!s^mJ}_`$_Z|CH{_7wdNAm%RM*h1s>d~7iB$0 zwkjNGT(4y}x<;D-Z2Y#*xDZ`U@b$=`YUycIy7n6|Rj+&qO=* zD*Ib;T;-vusM%VL_k)o?x8U6~#jV|S#m?B$btC>~DH&+!N>0W5PInY9-Qb+BZbUHa ztlTm+yYC7tQ!G&&&EhIGBbz*{^j^dZ$tt~#G1OeHU&O=6O8qN}Ogm!UmE=k-N@5hu z)xy6UlCoxTlvb8zYwf^Q(=B`?_}aJX402mFBW>6fcq2=%M2t@W>YUoX=`nWUnM2|Ce(3l!Ljyr~;FMsf) zJTuYLP@*0IYmziL<}0&13UrAd14*J z*g?7b1f=tcbs$J|<`GXYu8H&^G0rEbbf18nVaz8`E1?}r0k63u6eLNdTA%y0+c+35YZ)D8cq3b50U`IaV__;6+^&mRm3lq?TX&i&|Hd-+;GtXm|m1Yuo8QQc` znvsT26q96Rli7WvR-ez(>hm9f2&&KL3By%=g$A-ms3N3YSsb@-6pgWZlUchF%tn=H($snS zsY6$&oODcT;5-V%2D%s~K=o`7*Q4`x^g-E4AiruQGIu`7!}u{ zqEK2xRM+G%3e_ZSHI(X_B(g#^Nlgt%gs1LK3>2zK`e`WFHAzT?YRZV>x+c-)*7OdC z1kzB6tPUJE2?e#1NFrQ2+#Nkak6BZPi{WJTrk<&&oU9&_8F$_%gY_Sen9#SGWdOP* zYM6<%iA+?($?866r@%N~b2FT1*l2W}Q!--0T3w1fnGngIRg@E&VN$Y@Rw7qBEk+oQ z(8iHLDB6ld`8k7;)53**F+Wa{)jn6;8Zj#}*>cT7D`LcW3=<32h)l*RKn7CR3E*~6q7KISkMu{vFLf97}Q9|%;#kFE+5mHJB z-j}#mZa@gS;w=E-tP+BEz@_S?XU-lWaYFC{fwf`~5mHG=Wxv&J??N4`dr=};JABn7 zFqkVVt!_)MPQhee@vda`z=O_|!8l?%B@8UFCSF_;GoZL|)=Xe)>5l$a5|_nZF%p+y z$-h2;A2*cuJ5%ED8Dw)ZCy}`j@!E`!WH1FZ)*nUz(S8m;ss#5_fP2e6BH6}M$He(hwwQ*Bmz1g{z= zx~vlUX_oMJRQ)EZty1-yNVa0?x75emw$RihVy(FPO_W-d>Nk;TCCu)H39~kHRSj|X z9(g>C!1kQJDEK4hjFN#a6U$lfmq;VL+(F=f47MjO!T-PE|MEd_8(=hXgU(5ZyfVni zrH#qVRv47S$i|IjJuI($SSFTd)pEggO1?~c8JDD4wVYp8O1|{`AUO@rX?Ck-?2hvj z=~I)1*Q#0FraQ9PR_)LgGn3W5vMQ9l%<&*;0>*RFU~W7d3YJP5L&zJ>bKc=7c_pqL zaq`IRm5>6VI)-#kLTN&V70q??hSq4;I}4~;NvSBJ{W>i%l;TK! ze$!?sjxBMN#zRXTs}uaSB8h(po_5s-QsnUdc++3&0CXZU>Xs5zX~FBe3)B&B4xSU0 zqJ=9_;Ix#oQfc8zlrcgG>J7QjZt$FYhvE;p=zo@qfg*~#DL1Qk3Iy5l7R+3#R!lWYk4^V3E_3^TCr@oAJC~vsU zeWUxqeRRBH0JsZk&eWp_!6`jIEX_l$}v0TQePfpq}S=XODW_|Kg8gr_D ztg$irCs%E#(Df_EtWVy`g{k_-!W^T2a@d9`y8e_g>yytmROBYeJ z*-$G7+N;*L5ASZrGf1@o+B$d&sV5dM>Or$CRL`|t*&me4LiJpWmi1zV>yh<}oy&Sq zF$?wQTEDCZEwfNP*Cu8?D4K=pRrbS*Q$9PmE^bb&iJh5P1789L%V-sTCMlsZ!hVy%p}8 zih+S=mD)tXsC`p6FtBQ+w&Q~<+_#JpYgcMJemGrwn-DIT4aO^kD;k19e;je*_~Uf# ze4GT$<_Y+=~c7OBNS-$mMF*bj# z7>&PHjK*IpM&+-;Qa^tUmInE2#W?tD#W?wEc)fi78eT7-zlPV#=da=Q^7(6cy?p)} zUN5j7A>rd-tW&upxfF-?G?w=by=rvel|m(%#*)6Ku104a{*vo3XG*zVUI`7_4E~i# zf=qJ$M&Yj=dWVCF9^rgTta#kVUoX#FfBF2iL;PrteCPTLL8Ml`D?fWTaR7KwVWqyF#(r=?XnXxk8s*OBeddqN;uZOWCjy&sL1| zv}WR`1IVOj*lIM{d!g; z2jn$JmFB2)Oa-^1fV}2N(;TU;$?JOh;H8y$zkk`agnpn~`d0kKI%$kw|@|u`S6LY#Iuj>;8CUU@p)(K?&I=czn zNyuNgT&=%#cpfV&OtRx!u3V~WWr z$P$U)2>D@^y0~hEz{t)mTo4S0WFP?|Ak{@3*P`kMMd$}t2zioBLx`bMR_WX|jk^+UP@ZtpG~Vwhzw5ZB)Dsw8e1NnOaZ0#`oI!Q*VbaDV zr-W-pJsH|?tr$+vbzCFP;Jf&cX=Ad}hHFASG1_n~7!^(l*MNhhEw?8|-Q!Ihq-Hi3{K%y>p{I znU*YqIS5Ef+LaIB3*Tj?JuO?^R$OGJA`a-RSkj&^&dltQQb5lp4ufznc&>VfN3yN* z0dT&eWLufpHZ8m3qs2vNt{`V&-)2673KvJS8wqcS>H=)ppx?H|vFw9V5Yq+Nt(hN3 zK|K3kQXu|I-~AFjl@DMzMcA>xl=Mu?eijwmaZd+$7I$jqGm>v{N%kTsYbbFGxkH0T zLSZy}hAND@h1{8$&j`H5rP-5IVX0fl9T_|`3S-$xsxamjawld!Bj*;EXS4st=V>T+ z3%LV>L7_07eOwjB-9qlX%*RshSzMX@u__eDqJPZk%9=qq!7xu~AEN-*R z$3jmpF3w&?ctf!&;I0a>kOmgcm4Xsgz)h6-Ab|AZNcMCoaJu522~m>6xKzS6+g-!9?JOH~0kK<4vd7nfz9k%BT+!2J#)F%2a4N7u2r0dIa`s*+n5+u8n?cN`f!QxeLAfg6=EZyvSbA|qcBvFpr~>X)5Z!5@`yweI z+}7bB&Awm}N;i$&at;2fke2k(*sh3>Ps@h~3*nO>a89w7zd@$$}1$bQ49 zq?)5q66W=qFu#WtP3~MTg!z5YPa_!cLN96F{1Go{I%Ii`05w^5@~t|mp@$~VLmihU z9eQja%%w+%7T+U4O^Xg49xFhH4m~&U(cs@d?4v1$XI9liGC(?vk3OvX3jcOJ$nCS{2+Ou-sr|87Sg<7pgeKrRnM|1UYo2Nh%^R zMN*oYUc8v~6g6o=dQM=9kTeag6PThQO+G&rm?9rdIrj)mQH~}WTVRT4G`(CUFhwt# zRN4flNJUf00)Z(i(F9T}u(%+=p^of72~5$3CW~nTQ)HnjV&o!@*P#eOrY9b5R3g}a z@~Q&Q6)La(hKDm#nlK8=tH)8zr45g~`YGWqeLxOsdG!@3&@^&ZPF^jOf^o>J^HH&o zygE(Fas-1Xf_QM`)rqRmrJA$6@~T7?x}<|gMoULt{pmsxNiO}I<&{?hs?a4Oj43Ct zexM3nN+Pdx<<%Ed;aKwO-%%}0UM(OzOkTZT3PR-72~rRuucA^ABCmFQi1!GQR~w`t zL|#201tIe48&VJ=uU1Jxh`jm_DF~5QAC!U+d3Bl;gvhHZDF~5QP`X<{j2^E!K>05w_WIIvfTJQa0Znsnq<6=5ztqBH7_l>*eX=*X)*=d)`Wu;8Dyx>YpcvOX0s+9F|&b6aaSUQuK0M40X- zw%;-!C7@i2+><9Shmh#y6u<7LC`iQgYp zj<<%iC!QN(3}FO6GVO^SLx_N!8Wv82_X!eTt#}2losrm5ge*#w?=zNtvqG1g2}P8; zRVB{f=Bh6Z=b>v@p-cRa=B^>LYCvz}Rw-Boyk~+x?dWjW)`7&IankTfkB<+33Ren& zgHs#?3R1_w-+}WtK9uKgB*gD;d??S~NJzlnC?LPTQ9uEI<3nNoMgive8x@qk9 zjRFk%8wD8jHwrN5ZxmqA-zdPKzfpknF?s!s0u1^a1sL=<3NYwz6kyQbD8T*iZPq_?)s^-)sw=KFdhFDd z_BX1lfWHw>;`$r;+x0ifh4wefh4wefh4wefh4wefh4wefh4wefh4wefh4wef1=j-o zjdG#=jdG#=jdG#=jdG#=jdCG#Z1?FiOA?sA$z}GGkM=aMb14fNe1{zQ5 z-#L8Juu(oFa&cSn(%&e-#xoRzO96$d- z9zQ3<&(D95$Il4~@N)tA`MH1s{QL)D{9J%}{9HkU{9HkU{9HkU{9HkU{9HkU{9Hi; z{2U|57!a{&hVxd4OwT!2A- zF2Eo^7hsT|3vmDQb6Kv6pF8W+%g=w1xm;*|E*F}g%Z29Wa-sRTTxfnS z7n+~Th34mSq4~L7XnrminxD&s=I3%DbG*;-LYHaB^sSC#`d`E`{UE@WJ-Ml-*PI8Q zGL*Qz&m2LF<&m6vYo0ocm$<#x8o|3iacmDK$52pj@lT&Vg@}3f0qE89><&`>i=9qGbXlzx%4b^Jymt4gSi8(UwX8>fj z;Z$E!&7d_86F7)5nRstILJsCFieVrbYh*B)GO=&0Y8krL$RJ}J290@(qNs%{$|qjN zoJ-94BH-orj4?9Um@;{Uu%{gNQAZAOz6SC0xA$>+Q+TZ}j@$HLHgm9rm|6iwy+GVU zfTdm_rc;11FAxtWz;Z7TQz*c=7l?=KX|4o>4~u!jYr0crCH7mG6{&hij_KhPo;s>2 z4KXA7l-~1V$*$b6WI7PKBvRAcuaf?~V`QYP7{8}t6Sn_}{Y|fZL3+I>j9%|4M6dVc z)9XFq^y+~9^m^h*@Ld6^qfI5WHS#d8=^iPEGGLE(wfns>wZ~Jk*?YlO$;~m%Nod%c_dR)f< zd9!xdTkeZCu480;u!2$!)D7ww5+AIf^aZB^W8s4plnUWgU=Vz;g3>CS3XFaaR#1wD zQ-NXc!3vTT%R=;}q^iJ4TKM(qU~f3*zhTrRi$@Th$wN9BY($%{Aq<|U4exYT&udcU zbzS^=p!jtu-m8oE28#De@f*7MjX?1mOqG*Zi8OH|$`Q98<;Oif`EgGH`9Z#tlOJ-G zlOO8RkstSrCO;Ir@yibf=9eED=#w8B=#w8Bh{c_g9~$VB9~$V9A7C)!kRRj_e)$3X zlki7B-{0Vs-=_$nBWgp*Z>#nQ^SD90CEXF;wP%CO-D`+%Xl#_=Sf|Gg?s`TcpCqm? ziG5PHdrfKwVY}B#hsO!J3@Dg~40ycoxWV>a&rm&*5Je008~}@vX@QJ`a8j`a>J7q4 zycUQy2q(>2pw%Fpq-udggK$!)1R$oEL`7q5!A?=*_ab&PA3X#6LC`9h+q7aFzi$Y|rE((#dx@ceJ>s!T?3Ky=bEL^gxvT(7g z%0#9vy>Y+G)4P50^zH)klyshxr*f5(r|Qy?r+1GgPZjz5<*5Vn%To>X$x{vV$x{u) z!pzB24fM%V4fM!U5d1jgDJgazg!xbk$K$WxlZN|Mq9c4g@L;{@51 zpZuw1r~Jk^DL3VJp3H=XN12(EmKua=~mEDF9+xO){gpoYesv%&OgsGI(HW@J#*PsItG&6lfJ)vR*~J8x`jFM zJ2&%8f3%}(8AJN*zD)L*u4S9?vuoKF2N2J^iUSR2bgtjLqHpOW#Awe_`JS^cACF|u zGX5|nMa;=Dk~|N=S_BCsR?Ne^k`}HM!St#!BK2!2R9eg0%+d<8^R*E?f}KGUOH6hd za{TUkZAVFsl3wEHlO&d?TWWRg9$9j1>&o6GN1At8yLK~SG^CoEUih4Fid+3=Cg^n; z47LOLDq)R|avO>+l?AtzM;BO^w{aec_rfFLUVJ2qNGC-!Dnq{=qcXhK;*>;X5OMeb z;C7TSq*#9qU7XHu=$kSEC;Q~+LvRdXab_|>V#(+ahUy}d#kI=%V~m>h$v(vG$@-;Z z_fO`c<_`78Kot8Y>k$Vj`!656e=;V8TGSWZ7M?ZsPqroARQ6xV{?%f7D<^oGUQDRz zq>#j9crLstsSybS_Qssl(D6HWcPx#WoqI->mT>tFs^zZXx>QmsnOU;Os5 ztMBC<%Ml)^Xioey&nb#ZtoR{THc9JrA+49;6l}GRtr=_u{orU>37F}wS9Tx|B%+E? zazTs*aSP|gq(rC&0;tJtV|d#bDI{*ZAj&os29BNOdgGo9iljRhsqW-PC_;Xj3*vGo z6vg$Ou+u0yBMuzE+S|l(Y(Vtj8IdMYKPFJE4gl#YILd@0YQZ|rzK*XF9?##Ab6m(* zsWh*W(k^|KFq%M5O3(CFD$lE=G)`Y73_j44(mhdWpcRW`Do0o8RC*cHf$_sdjAbQQ zTC-_IFOifc0+18QnM=Lj1XGh0c-pJJI8ejch4NMjcH*3E@>Yoidr>=9-YP`1_fed4 zP~Iw&f>qS+mbVJ$!Pg6>0cWPXRmy@@)J~YU3Mb0f3+4&utGrbv2dk)EGjA2nq^}oD zBw{1HRVsp2)DD}s3a8rF3uYSUw!BrQ1gogsIBylM1z#^}j}BD^FwI41IEti^K(-|iVE{*#H-y=L2>*@iS$s|RqDyEO$i(}Lrxw{k-cHKi7EUAdL}SYKCe zh+T-(^;1pc@nfCMGgRMmz~tEfoJRDY=ow7(^r1y#B-4cM+gc!VZ{^`^>3;JJrzviM zc42LhoE^FT(8ztJ@iaoTa0^b)-pXU#{yXt5yqkKuK0cuHbPe$f`w{BS0D9#48Z@{mLXF&Y{LSUGKq;GZGjqasm-BBiHdi>F;N9iKK{=cAxcG$cE@$ZCY_2A2 zf_Ilwc9m?-1LYIGdnMK|QWrD-yH21h?s(T9kMm&h2tFiXf@N=zS=o9TRtNrH#s8ku z8WU-(|E+Va%$B)G$%zF58hiCL3q?%PiT@3W)x#>aLPRCl{k~+Az7Q?}(a+`ne!8DE ze!BmZru#aF?%Rd#R}0-kiJpcm@>V80Ly(gdx);++6EditxjOaiqqd3KY(W-b_kk94 zRTajMK7lWX_*Jh{ArE_n(y~+WONeCcAQB#E>WXN(2MZWylLqn%S(4xY<84D$Aqxo% zV3am}2|S%k_@dIOL4gAl^)O2^epE2sUuGL{lWDJd3P_11dWPGp)_u9XvFfSB>LGls zgKbLtl%2Ilh9Uf?pyFF@jY)$lTi{i6m3-YU_ z`^X98=ju!$uJBXOI@I=smp|-wLp5KBYW5J><3w|`Mm9-Y0+j@)2WIyw`b4M~EY|$N za`?k1g+KfVV3pOf70Vncd^39>xA&0f2d%aNvuy*&emEIfpDf16xdImBdsL+$S-eV# z5Xl~{c*L|;yxzuM&wFIH@CFdSTV^DBHsF4g!r+fR1LTj9^^LfZXb6kJB5FvubrBZwivFdcQFr9^ntW;92;v2_JJhA0@0y5O+Vi8L2{p*g9$rF6n~ z7X<^&sV+E7c_Mcg#G^TRLQDCC?=A`knv)+m?0q74ml;HJ@`4tS^h8t*U|uO0?gFr? zpK}uKZ}Q|HZzVslx2Xf4^z)G?D6mhP+pKXd_wbv z4>-J`O?bmvfOUp%ia_Y;;1BpBz1jm<>mJrXtA7>@Z!KRH61BAjhh90tK3 za1$sFRGz>Pm~&2aCgBO7QW^o{QyUzXfHveMU|5*s>=@b!+HheCHyE$lfHs;Ta6~Gj zIL0(=X0`(#98b(lA&p0x%0zqBlkHW%0&{?bc@m5Pc0<*#Fjk0y9x^}<7Q6xAViC90 z9~uyr<5wOYLpP4{@T5{d9t+6a;ycM6DDS8~K!Mta9>^is135%P>Av(pCc(xc{(YLm zlT?P_aqADW z-<kqTvoc`e83wPfq z{bBa1H>E!~h{xUcNq?BV>P_hn&J6CW{xJJc=?}9GXZ?Y!VLbXnjxkJBf0zxAz(n*1 zwZtc;Kd7ghnEsGs2>Vrkm@P7EU-gICZ(4ttedn9iA7SLXJNp(@PJ^k&;?#|!fr)f{le$!yKl2UW!I_#&ed~|8q z1Gq;KwtEK?X=z~Z?tE(M%E3hAZu_b1FN^SwahuQ6X!qKgcXT8Wxcy)@p2?Qg?Oa#4 zq&)H9mSe7r8l3~hbt^`$&Rs@~oxRZ|vF}BfN+GUFmQ;SPWNA5m6%)33uYrB*7+zTO z;Fcq=I09~9L~~>|$15@^8NWT?dr!QvVqnSJP5Y>?e)FUv9f1)mGCQ~Snl;1p+f)pj zsiV!#$6`6Gn4NzC>Rx_>uOJ74w?!~gAkplWb1b{f?jhPx;UPI;qHgFMFyIXo)IhOvgZyP{iu+ zb}r<~!A@M#{uojy0(3sr`Abauhcml?lFX~|%$~B&t!H!&ZeB6G^i+Ewv!}ds%e55) zCc~OivL>ul6V?=V>+k~-EeZ0dc_KUG-s3Pak6db8e4%~?`{UUQ_q;Kpm2=te18vPg z(@X4Ux$I5#GIs3oxkoz&n(R%6-M0>R;|dkxP%jk2v5(Y6GiMlht0kL8hO#H0GXihH zBXhX5Ab)j>_iO)C&I|M}{>f2Of93Mub-r4%^k3{m_Ir1~F_Mbd3!>St^B3MdxgeIk zO@7Ag1@UZ`{FLaKIV$I*iB{kI+8ZOtE@ZOu9rD|kROrBr_k;@qI1j#(il60`oO>4bqMu=srQ8F11(U?9NEP$(ARmczJ2F# zj=rqag^1zVDCwXGW`s8LEd{_O8r8J?ulnBd?fhXB_pgFSUyI@b3c`fOOc|Q@TGechIRXw0>k30!nAp;M42~D+SYRjY`wB| z`oz|8ES)8^bUsZ>=WEK+*%Qy~F6-QKM(4oh6$7wz`ZBxmu7hhUoW~G*C*0C`|5r3; z`-rx5p345`pTXID{A}G5IerFSMwS-fXUpy-tt$V4!`0|N&|J-BXWD7X&{A{0p_Wj;T+$SW|0mn4IKzAUf2w0FHukl zPaBEk({{{3XZA<4N7XX&vH0#6H5P+fh9gwqrJ-v<2^Dp*PdK-kS^P7;qse z2TZ8rA&(r#a6HIT2`9&U?+78stywA>LXYP-{=8EMbM#1JOm_bl=?Q+ltdQrGo8gU8 zGzU%NN;$+D=@rji>SHr!2xal0xp?L-!we?7e?r%5YwA;*TD#x(Sk4?!E-eTHQ{_q9R6IGZ8DYTJpjA-uJMbbPrZC|~=iR7sZEgX%7KIZ~Ja~;Wb1uA#Z*wT=B9CDE@}F`lyN{k; zcyug!A{AjW9VOi*Esu8oQqmBlVawIhxJzWaFA?fKp91Pfh z^wfuSs^rxH;I!kXr`zla(o-3*n)7%UqMw-+LO)ZEeiQWb4RG8;X5fH+mVEw@)6WSv zhtSXYTYdDi*wFIm-o70DEc>sVCa9$n3}9?JshuXjWKz3H@^&QG&c$vLn5as+C8Fwd zO;0C-p16ZVn#%1WSwE-7hCk0P(#_6Zh}nG`?H|jIpPHSA%_yjK*2+i5vvKs*=dlP5 zr;VeJc8#gni5MyYCTJ-*VrUPBFB?W8gn%`(``Vea0{E%7<7Z^87U*nc1swruZ|$Yd zC&dx)lKo`%v*4!+ML|wdxAQUa1UO1t-HM%b1-uEZEo?_kX>IdYd9}8L*}2ZyDSE5m z!Mjs5<|fhH0-HpkBIebbG>Le2iT>sH4v9d%27 zsS@cyRm-$rMpOaiL!>vSWw{0n-{2&dLiR>k@pZ;Wj1LaF^4I$n4-{JOj-i1ykCpGLMPUZU z0xo@L&F8Q3+!Tk{8@~DDdVdz#=z{CL6sp(w*ZZZvJ;dw%j2qs}dM}anj@2!il7E3C zUcWLst3HPzXMsNoJ7Yo%yb%oWP%iLeuJg5Tx zo4_=fBcqNV$BVb=MRF5x^6vODq)1B@@k4UuJ0N7Cu#1`GLOx^iiA2MyMPh9+(^RN0 z<{ITC)0xRPau3Qkat+Eiatq2gatX?JrQ6M#VkB}%@UyO1eU+#$yrGA{sQQ|uzDm_s znfi*UugU7GTzyrjuekb}qP{BCmr4^L`k;=-yXt>)r0=ztzVCSPy!H}z3SFDY6M>g! zGcf{h89AreOe?j`#P@a4X39mCc`PT_Cz0%a$M$)%igNz*_H~`iTmSWxR`;2MrS z1O|_jib%b8Jp;-( z2nN?%fX#rO(I1G&Jz8x$2~M3ORIkA`c&_CmxSaDDyUSt@<3dtN<= zZ&+minmrHr&!PL7sUCEXh3x`noDbvo|5{@_sGmq^q-(Jq&`7^kS9|qSPo&>=L8I4x z+y9Y%@4vy(QN59V|A`F|@wc1H{@j5@4BhmxpST2T<~H>2Lm_!s-4g z5-oN^JRA|{Xrs1lKbVoHn(S>>=1+*0eNhM1IwymZ2-2M+B>W*w!tEg8R{N#a?rRV` z`wDi|sFNnEw?UCjR=;MZafLsI&ema7srRcm{xM`{Z?&V?5ues<$E!to&Yq3EnVcgY zx(uQKWT)$~kex2Cz2EgT)=T(Kml*uH%`L2$odm<0E;^jh_Ji&a|U7_B7C=gT6(j#YOe0Xl(%sOQh8nh8J-Q zeyI_cPiXtuQBq)gYnnafZJ+TFT74>B3-mayk_4Dnl1_APa4l4kwdXUk7Q4(Ox=ZPt zznq)SpXH|Wm$BML53ik@a(1~uJdHE!DB>AiYcjgF?;6nDvPU}5iN`np`Y&?6MJJxd zI2QzXGpd0An||yP;KHQ+vauYE81_Y_B)dUK)9+c;utbwHXECIH zj@7+}6lp#zQUG$GktSvjK=>nBHcA8zPE^h{5%OF0s8ze}sv}Iuyq&MSFoGa*q~3dN z$FZip&MCtg=f|_pN|~8x`hC)V&2ZGwr2RtDe$2FgjT*_Sy~(OSWfx)V7MTgwP~Mt$ z7Jj+SU|@z;G8>VRaoJ{uy&H^sHze>~EjlMo$MMdoD+$z@%-DBC=BQAeO2)n;mfH!| zY4k+vyd@a;V5GtfEAXF_zjY;O^?pGKb`s6bKOyctYqn*~ifpU0vh^6%=(5{Ska2xo zjcYeB5*uS&j#XVK{OACT>hxb+;UCot7}ahW)$YKkEc=%hX8hZ4_0HBN%+`B{I9s7^ zg8kw~eM@kQ)0v|{`~bYEn5Gk6|4%T`0`ki_Mz962;n8eM<#JG%1^y%oNirS#jxh1o;6>)mogVJ zhBEgO%G^)Syi}cLfuy~blJ;jaJA|hze;g(6_i!DMyz~5f7Ij8S@PxeM6ur<^nVU6< z6EYCJ^OuO|rA#$GtOC9)pts{OzwFJ6XI=MQSNhHku~#L)mBA+vvpTr7(f;<#Z}92m zNLbd&{4uJm(_~~z?v=D!_m#+2tNT8RZIQWO(7NxBsry!MZbckQvU(@vYR;*pPf;QI z>tVFRUo7`N4%03*tNM{-^s0A4^L=Obrauc~Na(l~8!~oyM_sNDM2WrAX>q&>ci4rh z!BnRKJ7J4T_j)?6D|NinFQy|3Q3X-jk3*$?fA+%lTIb8#A0fGUbmapc7Iq9&<(Z!b zU&moHoHVm)p2lG_D9GJ_*WG+Lo_zdsut)MyQ= z(hd_lz;R+wq@VtzUy&vn9Yy+uLN;q&Y|Fj}i7mfe+pHe-`Qg3*3B%vy)t@JQ%cbD| zm-Oc~Sy^(S`tw($Y;65``82OaM*aC4;D@7$ORbh2RQMA${?QlS{v)VyXrLz__3<(E=S%()(4W5u?m8Mn*k6P*j;BBUypN5k zKhMD?i;I7f$Ww4hA08wE{rQfze^~vw>=U8-bKOG$;Tt>;w(6^{^!-QGpOafAs6QX4 z8jMqa-ixqJSJ4yw`M4gf=p7dN^B*rc;QI3g>2c}L?^V^tr9Z!OM^Jx$H9`Cbv|K~HSqWCBbgew9JaFk$1ZM$vCAVj}1(dwluWxP5rBiXYfLsKrEubyj z0%CBJZwp8xRc`?~4tIsX;2zLfNR~9}9#B5!S{~Gx3liRb-)s%T7u>rrUEg|1yPzP!9gzG*8=o(lSpK4|Yc$3VIH85bji{*fpPcU@w1b_$ z=#md>3FnDS|LT1?;!^slN4`COvrAxgIR`JI4<*>4Ti$A-Hd?Qtlh@fcb>vzu;6aY_ znvqQuY0BHHc2W^CDPG;rYl-%ANF{Eg=7N&&HV624zsIyhGa9=;S8O^i-Is1?w72s- zyOL*4fFe?Y)wr(PGqMRJTeCn?h?PNBuyPYw!6q5@UgQKDfm^r6yJ6L5);_wVGJD;x zsB0~q)O~3bwf>SwD~2s;4n`77aVgMi0lxTdkU;?o)4z2@|K6GwD0<@4o3~6L%AgV8xLtqZct<`%IEB}Y$ z4=2isj2;!#iydrWpU&Mmg8ruN%l7K{UI*WXJ^gL)4tJc9o&IYyOqJRLD*Pd?GKy(z z8&EV{Z!ge%<4?y#a}Ah&xcA0`WuxNh@Pof7aFdN0E1p)##{ROTY-iswMxycQ0(r;6 z&|n@@hkSR6I1qCTnBqC5Q649Lu zVdkHw9Au28@pF$HL{Ol}L9}V={`AbfI^M)TKL+RLffsnN$C(}_9d|yZq~oRn(lIwB z*P?MwQclGLeCUUctSmeMWnlhl0)hnZ!T-c$<<7BWWfto%i*hw?>MDnvt8{wcBY zaoGwTTUIU^Z%pBwlq@}*8K6DsmzDpq*p-zR>qIuc$bRoTIhA)}`5#FeqTLqC*LfXs zg1m`*on!WYG14R)$o}_EI*|qsWc>cRk9}LOk;8*kL=@0uL{%(-Ke-Ol+?#@oU( z%S^J@z>C8~i{j~VT|072)IB(kD%T7+!MA)*gScYp0F*;5Gn6a#KFdA5lC0irbzgX2 zQ_Tp%ZMFT=*p-%p7~PT|0r_-}&+hr{3nPiNfbFM{;XQllJ)mUS^cffLtCWl|F$1q& zkhdx9`}-Ec{CRZSRKYl`Hb&up({>!oq3BF?h$OE1g;~4)Gm&J9TVqaQH#h&^HiV=4 z4s>odyDyqK3z)juj~@iJR%Ne-^V7u-&|`gFqh}n9E-uI^MaDzCK4D+ptq_J2*lpMZ zJgUyi>ne8eY+Ds0*q80$If~Cj8fEZKNNbhs3l=(t=}kjAMo6o(8`vb~oBxFitD7_f z^y~(1zthVAee`kaXa5oCE&GZ9erBTzMa2hS8t^! zT1D^bCA)t_s4Gp}6a-FM^6rOOEbjoq2{1#1%N1~c8X{fJ#gWO@>KD!KyXnJhs`(>R zAvSeiQEa)MOUv~|de1L6c72EPrp1n*f;atIRlQbi<}*i{_QszSXK()93kYX?WZP=q z*zSMuM0{s2+zp4?n#DNvvKw!5?%49;&c0X|-?O)I$#FlKk-Z3Q%#|a&>ka0*3|e#` zAL)+EcnWjQudxu(RJB7RytZb^%#N*Z{A5-aJKUIkTX^e#HrK6VPiF19>#$X{@h7vg ze}_=C*X?+Vc{=ZOeL_Kh2dLaSk$#xNvY$de>+IYwW5EmdgNf92NQfI@K5{h#0lxBZ)2c}}cR?g0$Wn0y2qPE8~+tq8L zV1;Z}S?%lamMF{xt~_&r7air!%TB?LWbU}^%3wjaj(h%#H_6+hbOl&M9fQPk||TAZIbiH&SW-#o|yFAIWzg0 zX$hPHJoFu6PzOz|)vpJ3V6m={psvK=dP&X76Jy6tv+}ot5CXmyz z{|=eLDIRj#(ci-cwd<~(+{5Oz>#m=o=w!{aXu5WtOC?PEo;~9B15GCR`P;*Y<*mry z{`C7#Dw3SnlAFG!&q+=1VArq{A;{hGOc-+QCr3l>kI*I*xii1N&&a(1efp7Gn*HC~ z4vTev98mkYe>thhxS#pNoxK0Of?QGnRME#u)M|g99GirsQMtI0AD=G)Lwv~)0((oX$lc35dkW`tE2-<#NALar zauWCOwbv$Z!2Emdwfk?uNAHC~ljh;iRkAD2Uhnqe+a%zY^ z`T=aHYHyX~tIzo}R(7E zyY0of+u z*>5G%lkv}oo{Q0(h8e*B?$b&Jj4nJty15u1Mlp&3mSR*>#U_iIG3Ktr48aQzdehZ%UE6(TF#f&^V`e#i5-KJ zjq=S7ik$JYs2OvSHaHjg?OD`Z!SagGauzjXa?%E8EWbUAny0XQN@zKYnk!k(dCh-5 z7BweW4vQtEToN|!D}64}^w|XqifJzueLf4d*02?QZu8KmVUs=`2JGxehd}K1v{H#OCa{(3n7DfC6VOG%nCMR&&qf2S@Ng>=d_)wO94ESFLwQl(YeA zTw9-4WD4bi9@p065&X?epAuo>hm+GeSe;nSk@ACPCN zBut*E5=|gpd8SId@=VDwFg{`0?+VH@^77r_nTqeeE5j7I3K6w^W?vrOBc_#1>c_#1>c_#1> zc_#1>c_#1>c_#1>c_#1>c_#1>c_#1>c_#1>c_#1>c_#1>c_wfYOx*`f`_I@=n}H3r zfeRe>Mbk@7_9kqi!ASEQfit_sj!WVVdy(htCYTs_WkqDDiRRkpsdP~dtdq|Z)UMce zKux>EOf_w`Yy_7zWw+s2Rgql+fMFM-gh&>RR~D62KqZsYK!X&jj$tUw3>4%25})SX0#1v;|@uS6gvahwne>O!d z*3{wqPq-}YYI>fR_UwMh| z7friqb8~9NGh*!8O+y#(8c~aDA?PzC&Sr0n~ESUM{y4tams+v)kT>B2^;Tmjcef^1q`e znS>7Y>(ARJqiSNU^a6x(E~GYoV?LI{eZ;BZlyo@1F)Pvyz?>NA2){8m%dthq+MJ#a z=l5T?eGkCI8o7a*DCu7&Yx&D4Q}flKeqzexo@&L=$@tpQS`vm8K_&dkevs+s3}s+lEqI+F|n-G})sKdI1s ze(xN2J`Wy$j4J+!uR{Eh{F0Mb#~VVLBPr2q(jkSnTjee3L(dO>$<) zF3BGw4u8Zq`6IX{XM^06{Be@QAMs882qr505#QvGWe$JD_n3rq%M35#9S+%qRGZ*> zlthtCqa9zia>J5SDQIT$<48CKm%fj#!`9SIy=1)oyl6_@)Zt#V z$u?m_hq-~*-)`mH;x`JYV*CsSR zgT7ivFlF|f0ibwpTcB00rk7JHz+5@QN_}1lO*~h>)fRDrSgf`q_#&qp%*5&;tE~tP zPK54ZW?ntftR21%i4li)l$o^~&BTI@Mrx|nX28?CII3zdCuR+ORsdtF=32ea4X*&a7ipO*?aZjI3;dSrNr5x`H34>1DLOQMHe zB-8*Cc>~14nu-PnwS2_w5F?46<)Unj-s)D?VN2Qo4tCJeLC$e{ovph_jHgu>OBR!Xi4j0ak;)HWzksoWF6jHO z&*DmbPbx0{dwM4o^YMA9A+!46R8(%Qex|j0ps{<=Wi8#&;^ss;evFw&b9M*dWnf=r zAPB5?yn1U-$90pj%CQPY_{R42R${?=df!KSO%T`8G|%S8G{p+N@>Oy({GKSCRS7iF z?LFp>EEOQ(g}5Pf_1z@e`xUL?2cDBmrcsFYbP#RQPCS4wd`B$%$n4OKz(uOW0WF9n z?FMYNEQ)2bN zN>J255?NG|Emei}xzZ?0Nd|bP>`G~hhjMycP|8v+^+l!G^-gK2TN-02mouLFhfZnC zEiGp$m+YeQ>=&HUavE?w?v_@vluK_>Wp?x>Xbd?BXK{3D$^kFS3&K@lV^~I_Lm`D>7X<*{@6>L)Pw282UeJzS)x2lqeY6JYF ziJvs^GoVVMs$>#N=&M>ZDf?qpGD(#HYiVLF4Xk}hm6WQIGM3OkwWuunIaN}oN`T8W zahV1#FHt2iRWg|+^hqt6oIO*OOjaepaGDrS1HKsY)tT3GkjK z-qXPQ?oPo0K?|l3PS4Y#DK&%H->DM(WEo~7&Dls}HXczWm8vAc5^k+5N@V|Al_Wf? z)3xrNcmk9P@rq0`o7&ReV z@X=hN0QC7nW)Ko}AiOZ(7wKs(bs)Sz|A53Ci1Y*BMo)9O1K|a33rO67@WOz9qNlmi zf#6IM#?fj&OHMbnwR(uD1dcINr_Hpw>xva$wYoo4Y;}LG#Ol6c#CR|YG`a7W0;k23 zq|YfHNvs~Wy5q%WcU`n-;cJQX5oadS(PJAEtNR*@>RwNzlabe@;$BoNTDTX0G6i@; z0B=}izW15-X^6Yx6M)xEm1pzJF6Ni|lbO19=2V$wSBY}@G2V}~hf0*0c|nvYGqMLf z*?D6U`SH`>k+@Z2#VLw0FZqPb^ppr^8mxGlc^4A?SazRTv)-&;kE@zKvl{t*23iyI zr?#f1CGAy|lt~-P6x2vnI}=uWpf%MZOdB6~;{Mh)u-|DGd<%tc(Uh1!+=N@St)h~U zY-@>*b_>)VXiBw!+p8yP8~ur#>Uf4#6uam>K91f+4CnzQddQ4N9YQWP$||Znyi4i0?m}Us@P#I$PN91j4tf=p9NwjT zTz8>ZQfNk#QSt2Fg|ccD6&v2Aa$I*2VdYGYRlTW4=|Y=w@7p^wv1WRtDX}Kj3Tg%e z?NduHky<;Fa?aTiQx$cNn=HRf@{bOAnOuevz~0Qes2 z1Hc+_0KnuH;3H+mv3_Eg5PZ6=ExkQ6_Wzgf^9MK?NQEaSfj%pCE4EoudCutC`C`#5e zmue8N40_j_%QT2r6fbLb2dBcVaE>Ev0Db&GRypb>n3jw1-T^x$ZK{jB^9%H@D7 z6%j>2AbC)?Ll8i55xM^!S(e>csd%rQId7^7es84^%1$n^j65Ij{QwYK#4WK4`y1^@ zk;=b&+Pm{=QWN0;sF z6Z{bKx%L)vP!u#^rPdCyqz@$xT(athD8hR66mj9+LXOB*Shb!a%=23HKq$i6^%P-p z*WN-7$5vRuo+2(~c~}-<9eax61Ywre-XgAMZ&Bri{mA|%k~--yCZ5A-BtODO40;0j z(R%#O;Cm3R603HIJgCX#K^uhqc;0rb<$oJaM>;BAZu%W=qa)oSn>finzVmLG;E$8< z%sURriEuaoyr#lRfeBqmwzC*)XAzhl4M1bwad;6_VnGB(4t~tL5QtrXDNWT!!+pCtbH7-XF&$52Z1hXgo!oNU?oGf!O~mvxM2^9 zC0vdlpt;4xPMH9kiybKZAJRfWOB`s#2L&}q5AX+h>Vg^sI>`q`ia&wE$Kk14>OjkU z&@u-KkCCS?*uL~I*$18MK;g&m)YW6F@Ifn_y6`D^>XJt-p5lN|LydTfQy0DpPhE1V zmIOcU9yCL6P`p|!TpNX#EfL0iTKS3^`^FT`XJ z5OV>*;^Mefy8#?#Lt|>8GE|ztkyLMqwd+CV=r04wHGilbJ>nAO4JA3)Lb4?R;zqh6 zZsh<=JwUP|sfj0WYmx~Gu-pSA`w?K=1H{~{U0i9^4wEBpfX{WIvSX5M7&`cknL2)^ z@c^|UOw6$lj=*n7tv@$8T>)^?k56>L+Y()KA>rP)#OeZ>S~{vNu$d3E3N}$%O0;)nr2U zhHA2Z?Tz|FW^W|@_C_*bZzTQpMlxt`puQ;nuDy}Ww>Ogc_C_+y-azLx1IQ(F_C_+y z-oRa1lUy=qZzRL)4LlBOl1t|7jbwUUf_J)A`_J)A` z_J)A`_J)8k&Naqle2BdvAYSF{4FN$scQI_ZI4FUP>4FUP>4FMfod!s(g z-lz}Q8}(uKMt!KgA<#T~L!f?pqrRZMQD4~JP(@t0dG>}X3bi*>5f^cuy`hRI@bl~q zRTOG(sG@zfHz5BGuDy}W*&E3adn4(!H$5NMvgA<#T~L!f!~hCuV|4T0v_ z8v@OJUI47ea_yfAI09N57-;H_S+i- zRjfgZP;)q#_GxNJgmP~2ItL0T7>;;@wuUHY^=K2 z(OyP(8tTSUI%ailk1K?rIx#YO?{H`JETcn@y_b(!og3&1g{V#(j@~QnSI@^@>9iQ(Nr|Vl2RRPJK*DgaB|}sL5@E(|EZy2pnp%FNm@Ycz%7< zTe1-TY1JKzRCmtWUl>CXuz)*p^H|@B8a-Dp!VKVd-NbUnF5rHNwY~Z=fp8V9q^n@t zya(|IfjSGhSdFN!5+2Xr(Lyd!qtsWaG_MjDrV;a1!e|0LaXA|0zDnhJmAD8E>Uc6X zd2kFq&=Z%QQHe?eTqYcAeWg?BWlRUgk1Z7}E5Xv5O)Gkdq%;w*#zm6Rg?2b+3%4gL z;7(}-Z}@90q}falzeh-UAj5X(4o>#Z^=SVfNIVj^RQr0tOyexBx5|`Y6*;bp#gOZTYr)qGXCLouNvs*bOmo`n6HXN_7Z|%f z6OV^5ddVkv3IIm{Ohk>Tm{T()ahSN0ouHJ2)b)#h@PNc~V)?Cxn>S|edIm?c&M>P7 zRJbPgq8Pxl8c`^=3f&A;2*z{vfx~s+aK4V`9MJKc)p)PcZp3r;F`l!J@tpmLnd~=G zjIo6HZDBkoYU_Z-6U~AxqzYO=5F_G2wGuQjp0jW(RzWL-owYEYllP(+2mU>+82Z<5 z+CwVV^j4`L`xhX6#B=shRN*Jar{XyWaKSDT>c)uY3=9w~LShcd0+Gp$_w~ke%HxK% zl9*4_m3U4EJKlKCQCd>#VM{X07RGbRJt_Uicv3koz?PVEge&Nl4su5uqbyLiBwKA^ zJg3}~I>A`?WF58?Q%=o{H=c8padLLpl7b1FPR>0gTGKZp-buxL+$uF>R>vLBDNliT z&fOBv*^5;m@tl3w$Llk?iVl4|XUE0BMOZxNze~Yr@tjSlfqh3Eo0*H}e76*Y#d991 zNr;yJ(Ul!nK1e$FWkkLSF|DGiV3 z{8y(mJf3rgQyL!6S>lwA63_Yj3xSxhc+SV9AS|BqhpHqbp7T~!5)#k(Syd7e&-r0h z5)#kZs7gZOIp3v9LgG26s*;d+&Yd6PK7Zc+T09 zHGn}lI-YYjLyN%U35kJewi~nQ=Lhd5M8$K?W^9cwo^v)m{Jwb3+2(BO)xEAxjaBRP{1C_sb^w03vuqcES--j2BA?f9tJ zZ(kR0M=baLD$72?V&FahI;`4(rR9h+Sqgn;UNXLr$dY6(dsXf|2^qz`4~ZV@#XiJSLKA`jmdB(apZAyvuzIQmwsS(| z>=NFeRr8;eb(yE3M~y1cqlPs~WtxpPbsK!2T{=e3)QA)oP+8D0O3ze+Y)3meyL^nE zWq^)CayHO5>Hw+g+(s<~-&Q4Y%*P1gn7F8~ggaxvQB#IsP!zSmfl(|D48+|GEG@eq z=r5A>#l=(B&=pnw3j{b1>4PPnc#yGaU`{9vyrgMhzC#1o3k{qgk<5i?fRPdALwG*^ z)%hs`UTRQ8%lLzw2p$ogs_@d$q6zzD*!`r#FX%1{i$6RAhAL9rT#MN-r&DDqJW7tkvh zB)b~QMXHXG%RPtz?opzjam#Sx;lomAL?mb#>GcRvXGA1|pr@W{gr&}iNVqa`?GX|a zokC&s52rGH2{|yA?l0>{b{Y$>#gp390%#kJO5!K!!yr+@Zbkd zau{9b6yfxTsm^x1?m%$zPp#k(5G9H+LPiq&pRh~sepL?Fg zvWE=2-`uzz*5%3#@EgIE*f=!2Cy89ZZJU;Ewor(}AgM^wo*zSVv_%utH|#z`w}A=T zIAl1DhL%3Z!z643w>?c<6;$77pQs;VqdG3!WIxwr4;%J?Ctl66H=<|H^RVxY!+RR> zhDiI_rJEb8;jkKLOie?}M*AaCYu83}lZ$s7Z~MjYMze$epS!mKjH{~i{`176 zp``_ynjnRs)&{i@v6*I4?z9uPyjc(x+-lYJt+p8fd1-1IX!bgYB9G#_kE{>7vaYWB ztlO5NZ6=X6Nr5))3WchqEyx`LSa5Aps?7iMJ?Gw;Ng7JauFv!T|Epx~J@>r+&hPxr z@9q2!V=@(l_p_Wp`Dn)#xEL&}hz;RUsNTf9-3B13NNuMDA|Afc-I4y`sMizsb|>~l zYx|-dsC;tJaGkB=EfidU`cN5@gNm2g>4|tdM!-W+Qu5$osuny<%>yf_S|y?#<&>q? zON*#bRWZd;EzlD!zoaOrpd_f^WXl6l(}8hsvi!K9JQ_eVfRk+>BZ6sv;+rf#F(_Yg zvgHB3)OlMkooq|y<1=#;^ZE^XbVh2sEqZi<9{rL2qlCkadwq$0Okx+6U|7l_NIZ!J zuJ-6d@H_<;KMXuL>Kh&ZS`)t9L%SZ-$utzbyG{aMqUZOSVomE^)ncm4m~L;_B|Fs{ z;Y(imj4fX>K3n_*@GV!K>9WlizPa*lVOW91_>=BG_-;E;obA9#jlVc7Zx_r-x6gX9 z?U$Zh`%GPFGwf*FAVmHJ;<)Ew&B*hDC$N9q6Yd{hI%fa4$Lt@|BAXh7f@c39EQuV$ z!IK=lguaa4KXz*?*gbmxSXSoDf*oN0u;h~U5bQZx50Fb+s8jn#=W+IraP!d4an}jZ z>yi7#3GOes{o#!Zgd2k3@?YK#M(+i0v>?aW07U&q%+Cq!A36H}Cd+5(|C=nIrT=fT ze3t&d$?{qHf3oFs^q-PF!h#$>@)x83qWgxDzY&7uC~h9bSLqYlU-L?5>AfwiQ)F+< zU*yW?=>3~3pGWU)1+6F7zMG@>Z?1ff-oLr>IeLF`&8R$gYaGl@JY^w_fiEU*MHEh|lVkUj_Ig4k9g?c!)3Y zN*u)J^m-!k5dYynW)z_Mc>Q>Y^T%i5{}+pgD8+bw{5(I5hsY$1j)y4SG!Q1f-Nx47 znfVCg=V?k>rDP@xLD$0gd779oOd@j=#zWNf91{=mnD}{2RuDf=^VMqO=P3Z54VtG} z3*zT7aY6h%&0P>bPf_t~{5;KI5I>Je4C3c$9)rk<3YBN$=V>;B_<2ld5I;|IdZO_V z`*!C~E9VR67@bx?v1B*t49H0K&Zmy3psWj`UX)eRpR{AfK|@46nI7DK44G4iDG0IBH>Zl*Xsf*+%X3Q zj+beSpxRmSG{O<1}mjL z-80~3dgApxai+G(#9fQk?n^u_Q<9lX%a{azN9pH_L)LC2EtBfqXnofQxOS1jwN)fP z2q`LXw|5HJYKb7}?(vD0Xu#YEW?}gy9HM}(x5Z`MW8{yR{A9pp8u7SSroP9#5RSwa zwr?o#G0ukHD#oe_P)=|*a_AUsWTSv64|7!d`ANo&9Hst2V~OO&|5YGpiLq8dK+#=H%st=Irz?yegw!SJY`t zHLYLB@o7A?e=|aXFa3R`&K*?c1-{<-+PMX8-7g7HeaQPT5j%nadPMEYo{Eopd(h~I z8<#y2-$(n}sA6VvJDA)^+kxUnujFo+1Q09b7qty=7O12R{>;3WWZ!-BJ}&$ILYjD9 zw!E{k=TK}6=frWcYl9e(kcmV*xm^gENlv1&I+dq&L$ z&(EsW;Q4nn8azHn_Ojqvy$qkzWDlR8Pd0{+gJiD=vcEv~@cBiuhtDr18^cEvYu4=; zPe^Xx?YI;RhYi8~_C9zMbhz_n@^D$*BbiUuc*7MYzJT`>s8_w`dS=~lK-BRdjegsktS&3grm^zu?Nu5}kdL9*8>=oW z&8BhO*xpj5;RX4|DNWArmyOS+ap2hAMx_x4`55q)#S@foLN<*f$M)7K0U^i-4D3x) zmz8JJICN}pml9lpe1OH?D0SJSY#PUo?JZIQRge#3tGzkuGAElBc%`2hFVBU#hBAIg z*k~gct_49sMJ@&U51!X4Po`d)t`w?euq#q;I(*Z%E(v zy(=vG7o%^iXXx9jF+;teZ(2b%VW~VFvh?lM(e#akQS?nIBk7w`M$xxdL66b&O=-u_ zH6_9<(>JA!rf*6cP2ZF@n!YJ*G<{RrX!@qK(ezDeqv@N{M$PRJ%{v7&mn!&b4cIx9MU&EhxAR)A$`+xNZ<4v z(l z(iTagH{bIrwnTT&(Y6DHZt+vGju|?d+~nOWZFZ#XAUWl`lU!nR?uhLfCTG03s~Pvi zYdN6F4yPJty*eUcNFNN2Z5z zL8XVZJa!MMl4HqDoLXWFR3Dw!>m!Ib14?$Uw4vrqa@#>?>~1Q|o7g`)AP(s=qT^&0 z(Y!Z%7WueYMM~}Psg>~`r&bZzd$VVdkDFCw*8obxsyJ@e3UlTdaNR3+T>uNX#s;j3 zb!QGAo0IS8Bus$Re0x2d*K~9Kxb5J0L9#zL;YSSq^j*5X!g}u#6t(}DfP}~D!>nm2 zKc1TjE`39Y?UMp*|Cqt{ACYX9Ni!aTY@>i{HsGrfii~+@n znp%?Ss3ap1dGT~(83wB%{I;}&kMIFwe}u+fUJ>?jN3M_AR-*`rbHV(elTN~2A%923 zY0buF&BjN{#$YwRjEaMljkSvBBABsoGUn$7dkSLocvdi@D}r7V!A`jeOq}cX$nQ&w zlwri6M`b8c27(U1ZVls5^z$z#Ab2L=ORrhmMP zf-YXWD>#JT%IblNpr32vB>A*vP=uK_sKf*x5h^to_e9s1xw;tv%?_7p9HXKeP5npf zf?rR5UJ$H;yzqwivJ|j;0F5cF;wPUMgGux!!(oOg;xNg>eYULsLP%})-_%bT`Fq+3=%>ElME zTdqLqbRt*%Qp+GHeMGZMEw7+-dYG3V9*c;$>eS>B>6U}wBh`$@dqFOB!KrqY06f{$ z;3T_}8B;FVvKC4%&QG>nhLVA3F1KYklw6vhY4TdV6JWhK*PaI%1^dDjdBCmTy6!{S*&;f5w7*k zB?sKs;Z?e+$kL`yL_Bqj*cC_P*k*?AiMVXJ!9hUZ3L6zO%0R@oeh* z#J&~IZF^UAL=KxOhEKTojFH8MP4QPxxcI(*9nt!$rud&uxcGNQ7XOpnMV$7-uVpgb z%yZx(4Ro^pY@*-tFH!ttu>jAnUy2^yFHAgZ^zg4APv{N#^Di?2hCeS1Xtm|fyGS0B zKgR_g{(SHR_;Yfy!}z24^N8X2XSJLbGW;Vd%(3T+V{6!MJaY`U_IzG#dF;8u+a50; z!ow)`oC>V~F`|Pb*z;n7lNb(t>|W+G=&YDV!iQ|cYygiuR%b^r>B_EnEXU8DWz>~j zky(zPJn14@(t6?Pno+8=sSIt~ifD|hJy{sADIu3&x zFxCC}NTnXdifvNJzxT!Vq*)RyUe-ALdtbJ&sk%v`0nGc^QDafa=>|RZ-#@X9ML;;(A+nqF<$I`+8;*31Ew;r1%3m5}-z&(PizYET#0-$W@ zH9S31sAjvA1i9PJL4c1XOXkLEcg1v7L-XU=c>cOns8UORdN(-`oO@%n z@u8A$5?{41~cI|Z2wKl!x^=_K!U7NCx=wMSvQEd4&*i#;o zT1i7Y+46{=%dbI#en?ubH&p==ex#y#xhEx*Ks3jIsFk{dq|0tv6K#;yiU|MIQt46Yd5e zrw*<7hs46axqhfP^F|$`C+@`iq2kww?|#UH?8(EP6R$(XuMs(UNb~>Z#v-jsWHNs| z>Dz=u=LI-KKXNz(>vMT{U~4oI2|Kb#G-gK5^#+$0?P9r6amM;jqnkTZf!L-K?d~?N zV%dYw6o|XW#Hwn3d=T~U4wAsp9h+%|8(Q{iMQ1|>qO|*Ya2_bzDeF04{~CHXT8R;= zu!HkLfu#=Zb?5}Zu~Rt#Yzv9ocWGH--g}LhmoW$MPi?B})j9LpVz>U(n6oHFD4>|f zjhsqTP)&mWnpB+45Gp19sfbtZb#V@;zCXf8;_+}TLy5PfWJIF2@+-=?mS$usTlf_f z-ja%usdi!G+_trt>ZRx4N#`;)cC9cCtx!u$iq=jevZFHS_t+d}c{P>iqZ7U*md zbl5mMxx}LweF}Pj&hxuqD&x9BV^J@0ipkWdkb%n5h`=>Qp2fmdas4(^(b$=QG}l0e znZ|ByvD7dU2y9IyW7h<4ydttxiC zkRCM$0SzfOn_FeL^3WFBA$Q2pq%*&w)g|I;$I2C3USGE_;>|TIp4*o!JM9S<%eT(4sogj` z!sb%Dt63Y!A}+NagCjD0;|{Zvk!fN7n3NytOW>yto}iKElj)-uM=@3I;il7Z&XqlY@~&A$~c>>ToU1SUgb>3whs` zN*XY>M@UM6vtgKcwl_}po`}_Q6WS5AGk6AqMJ&0NI^mysH|rPKN2r%&a9%1*JT!Wh z<}o-q`V(GaJ?0^3H~CXV)(SWsJa6H2f=jFqiPO0)Bs&j~OPtO^LwoK=`w{oXne!p8 zH_iXoQ|Mqvad3n6BFM+GGXGwaXk%Z@m4r24j-a~2dYm~i;x3=&C&)uNs^f;MMJ4X? zWhMScmDU*ETuo)msVr_1!yt!?DoCvG-(wPk_=b56I9u&e5EzFtXVu%|m;Nkj48=Y{GE@xX->OX7J zO3m%d8u=DR-fz-uM8ZY7m~Gj3{}(3B#v&9Xwg8ELHfc5r;UZniwrqlbk4dxf2L+ie zKxVB;vylfE>4Jo1<^E?)nvFRqNNoX9*O)X;tZ)hRBHhQfY?9w((rlbTL2e6>d#6dW z5e63#a%jsk$3M%Y1+fKln2}o`V<|XvkK`O#b`*h*Ky+w0V!UAtjpYK+X_L}I_^|%G z#jJ+;pqA^E#cH`+2rFjWA+p432?M0yab8)ZmRp4;M!Yr^jnv{OZd^%BHeSnkWgBZV z-XgVx0#YV2H%Q@tHnxfUDMgo*67npTj5)F7Oq~&&=cX= zIl;l5cyns|+^%8=U zpo_=P6|G^4ZS%*gELsBsEUXA>OPtYmq+~UBu1sA|pDNOdrDvL*y4JkwmT;ys=%hm@ zOOc}oPj5R?wt51}R!(@hgJsExtcE3#73wPIgNs!A8)uHz;IXyQj_gEo3lSHNJJGs@ zsumn4Qn!$K;lvZITj*|Kcp`OcK`fjc)SWm}u;Y^7jZ^Xaxw2+*`kz5J3&E*CP$}OO zVNFJB3dPM->8|?Zl{JN2^Jt<0nE(@^JrsLT$*HP(mJn!O=it<~BbBSi>nKa1CgzZy z$~db;#^|YqZ=91UA)}eWH>gr48KS2azj2jv%p<44;5VpKUcu2*OHZIqkb#^A8{VMK zQj>H13Dn8uFkKk26j$edEYM;zt86%rEPZ!9R(VH}3+*uboQun6rCaxiU-v)EvE(@+ zVGp+46N_I^XW}!AieFG?0vG(n;up;8&C%QAMQ8YZKDo_{FVvPosD8C>cVsiz%5l~M zF+}X4R~~HO&E4LOMl%yRzb`{9x52o#&t2ZvT)nr6vvajwvHHFQgppIN+2C+qpUZo- zy?2Mh1a39wCVd6dp>*{=9hNQUyDgFr;(*`3rKa*P*&pBZXTbF5py|)xiS);w^d&x0 z1OPCo?b`*FX2oeV9=-?d&vR~OM{@mZ28uz_0ey)VKgH#^u{}rQ^-tdlgA8=C05eA2 zSp7ciLi{2q?E+jM3dhM{=p3eMN;V!Qzlbj|Ce8;%#@K(YIF^GWhzysxCcED zULz^LiuwevhO4ev=6Wx{MqCdY@z#gKgPH>bu!?w3vN<1NNZBLXn_4wXLw~5-9rt>? z?%2@F9P@3@IP2~Z^YDA(FivLlJhXhQEdv=j+wAeFzi~_altfyz}rc zybDO?orfRtE+CnA9)85TfMniz_*dQqB=gS0j|HuT>uEdq9RgZ7mK`i+}Li9AxQ?)>i4cXOBF9nmQ2ia0r}GVxOqSasoP$yT$LV#->QVO zoWf4pJfQva4|kgV^ZM(q{qQmLoyebBidu+fv$3m?9cSnjEktqlJhtz1)_s@pvwycV z>rZvtQI=o<3leXRWJM=jdS|SIU5j|NL1@x$^Q9FwyEW2L>=CfGvBdl>Gk*hHlF8Hn z%U05WaELJpuWD2%!7p|p^N#Vy{FVj3m~qTIwi@#rm|nO-uDHi66AU3Nmc~N+Q)GXF z#1i|4g+p1eVdw`dg?_L@=m$%Key}Cz2Wx?Tum|V|jlX`->FXz_o*%V87-%-J`P%(D zwr4+EwV~UQ^YOhOjCn_qgEMaRtL*RW-|n7+Y(ue3U#zw}R)6%1r+`{9r?G3q#0-LoAUa-6h1Ef{?Jov4}e;`yuP~Kbv?*JLx&>xIo0oO`E|#nKQ>aTtK+nvQaT@< zIUlLvhK2_1`g0ysKisB3kH;7-ztqK5}^Np{ax@@A%pe8E(tOwf~@r zfyQhNdYz|Fg(eJpsg#fB{FB&&IEUT`J7-cNYX|U~K&N|X?EyU~IB;rUTP|$ujd3DBV+Us>WoBgxe4?9h} zw5tDwvREtIj#k||+w1an3_XdwvuD8D(cVcz&OIryiv8ug(qAT{q8Q(k;(hQ2r{ilR ztbNV2J)?bhqQl$HRZGORY+aMrbazwx?xb7jH1(j3QN!Moq(q&j&Y^uR?+6+`izfMO zTjjwvi{2($-Z?mtaVbHs-s2I~5#g?_OkV^ON;Fwg!1tTJKtzQo-TG7rjngA^`9%Z` zw(uMv)SE`kDP zeixAbJ=sm5-Fs3rl)jT!r{kaayEbFGat0G;eBj@qHSW+}RB;Q~!qNvx zut<^LFDOkX{W91Qx7aZ+=vEaD?)n;ZiN~NzoHB_EK^VPKc#V2b#LFMH=poOnIOxIG zo0i8ddR!YqY*LJ|^cp!dHF%}4Vy?l8Q%Mi7Lj9-9gYU+^COfCOJI$Y9?oT6z$1GQx zL;Y)TIV4rwnMH`l{JX&dVFfz(&nR9+OHG3{Q)E%YJisrzn^gl7g)OG9X@()Yk=B7K z-Y{ccqEBAWjNPgIm8}OV&8yScW9|znTs^L}t1Pv@Og!qO%ht{=fZ|SGvbWooJKSl{Jhg*lG$!VOiD=7Zel~tSXCa(#02p zB~;`z;iLcW#G$34kZ9v>>1;maT+(_=LBXnVn=-c&B$2f6^CK9{p2SVG$=SFkmDT|T z$i#|B>Uk%!cE2pu2!CxyW+kp#hl25bK-k0`j`g`3S=??>Ax8z6vUf*XZyZ;Un8wz1 z9=T8vZRlb+<*7-cSKVC2vULqATa#!=Sa*AG;tDEEz)YD`PJP0Xo#n5YUBGZEHys7V z@u)QBsnmfoGv)$#yT)B^>iOxxE0YDMS~8<3kq+`}zp1pQJGIp8?Rv9~X{9~MZp9A8b%~%?gazSsk&@l5hBZla zQmM0|^Y*SiYIcgHQ0tacn!!ufhc(`%FN_-U!6*_l1oHin*2P7IW=?XsG&|k-8%$vN z6KS0b@lok2N;~Ve@@N|1L#}-Ku|^hI@?GcNHXEwwv`bV{W0ywC_fqx3qQp!s)VZ=R zEG!z`xcyY3Pp{LDh*o7GIGJL(4rsZCDNdsnazU_Q2g?RoMrW=Ko8mNf9@B;ig*LK{fm#r7vu3Fml*vROAFS>a zXF*Z}WvtMdoo$`B)o#Z*E!FQg5q|2@lvL;}AlNG8fR`UwkeJF=D>Vz$IbNR<;f<6- zd;3V{u!XZ>hv|H-*Xe&hFW3|ROZe@j&Jpf2O4df1T^othy{^2}$|1Wf%FM#A>+I%E zqlx8x%9~k}_`T7cWQhy(@h{zutF>pb}F9Dp6sm1ZxhwYK;VR;xrx%p$NYkN&$-I0c}DgfaP@9 zi@fx=g=Mii&+hA;JJX|-S6*>ZZ*y3WL$rt&^heP&I|nIKYE zcN0@`3cCp|Pds{k&ev1ij#X-8B|eYb9~%swOJZMw zJbUnpqD}!V5^aK|rax@-or!Yx?N*FoavM1D1=aezS+^{w`9*!EyJpc2)b=;6+t*T= zN3l%B0Z$G=veZr+Xs>BAy=Z}Uu9WRSG`V=K$nsqDVP<90*T_Tg6l^m4r?J0C!C8*? z`Hy$AtxVG@>oRl9P7_VB6}R^iVl3)JyCVIAiJh#$$k5((`$(|6upt&rGkT#&k=Ff? zq*3Hze55s1#@dg(CdD1>!_@haqJCDhv!!oyJGfNaAK5ct7PZ(JjV=A`Ir;1P|3moL zQjx>Q#2E%3>vkKAWCH^uMgLx5;Vh z13kKRuW&D&bH3hH-ybF-aZn;%OkE-U5;1ts@rc23cm}R%5r5BuR+r48Ze4T3MYgB& zSW)6AvP-(0C5_-_d)F&~Mb$kPaF|Npgq)pgMC>5bm1glTC}{}&(7$j*DO8JVR60sQ~F$G6CiZ-Ho*s8#*07Ti!5nsHff1^V?D9_-y3?0f6; z2FsAW6mEW7*97R%xyEVi%Q4Hj`GyAczb^f^6h6QQ=)dJ5L;7!bhkOhKA5$anQD8U@ zKK75oM~?aSrB`44m&C{S*!h|}8XqTuj}fN{6z;AYfsR4qXl$GmVq-~t>Wl0>BT#Wl zfQl19#Tzj8r%<8vroVkr{tW$v@UhX3@4b2Wh~;9T#+60K|I%1DoX}YRb$wF2|? zQ&wOQtKV|b=v_EZKmo*N<&TYd5R{igsevFP{i&Bw`$+4Rl^XVjIxbRYHOxB{F!q;d z<9<)B`%TTCMO~UdamwcHD7xP(HMvxvnl)ANcr$!3a!nyd{H7S9#5WD zwo2kb;{DpVFAKLzuk!@9&@5Yz#8*)XUmq#z4R{1G_Se$-sX@bHj>9&^Ng?ESQ zXnw&9q=s;dR{e&hA6fsN0|8Ip1dov#7>~wzZJ#(CG4=<_5VW|it)2CO7AKXiikA1% ziN_j~B@^1wm~}QeEJ z2c&F`d*7*PrJE6N7zU>GX5`n)jzy0p$gfiSj3K}3lc8Bd+YREJI)ok9injXnoJx7! zvS5AEV9p+n2SmMt+L&Ho(?kO8LtpB9ub{+x$tG-%dPDT|dTG(9Dk3bivM3n?OhfSd zsI)$lBWwR`M8{HO!IqCV&C9Gk629+FROf$9oMy8+?e8s==!nLxjt8$BrnxhaOU@g@ zHqVAOJMzB^2Vwb5RX&PWXtH8vUg_+182L%zU~$vD z&g2CPw$Mbu+COVTqsa?TaqdP(gEhM~gFrB`$8hC5Z%8?f+t%Tv5-D3<)U+) zW|SlagA0OOt4cwU!FLAbSCyho9jvxhqpC3>5(ZCgTDQB!o&FXH z>Y$Tb!0C0Qu_A7SrPzk~?fMIKFlHEJa_wh*U?+Vdy zTv*t;Uk79c_d?tpMRqz31j#dv@_A}mk|Q}MBI3KrvzC2!cEK-%Bv75{ zIc>r~ZQ95a$*W##n)jLr11;`kh5K)v@mwZ7Mcv7E0Rf=+;ncu1^m^%`BB@EvDxg|} zdl18j;S428bmcl_$QJ6ZfJzxD%ra$*{isK*rs`6JpNWk`YE z!rA=%G^4yo9T?9J0$U7~+vO%JPI23D1E@G9?znwmJ8$B*@0D63T8rND&*;ToXqxvD z{kchdN*yaQxlA+vC=?R)db+BqoVyF-VB_HwQ5~e`ccM>Z`uvxs1GguE^vO-p=wUE1 zDDltDX^*|9QDWEr#$e$$giHHwo7;A@;nugJ=;r>RQ3`ny=_mSl4!cPC-5B?_4ZiQD zi@l>mPq$?nZas&%0XPdGv6-vD1~}^6PBp2pP+z>fXRw0P0T}e0dphN5@#p5^o|}kz zMp6m`U-h=Q*GsPS{d6X?(`fwZ(|PLCEYqh~M)j$|xn~>4&ST|=;OPgy^ zF7-s`y+jB7J|A<*mL5nE|1hHNuT2#XpWqbwZ=3d9W^g7`y{%#1w#fX|h2|{UhHXw0 zCSxsm+5SH%88`Bg$cG~zy6z(%zIKF0f(>tu)kdIL@0FNd(AqLB*zTNw)gCXML{l}! zAu%?xBSP3^ueT}r!3>+gLb*%V>d#dDS%l+s z;%a08%mu~0gYTi&jme5~m^j0u_YR&jG9}tOcxsqC(!PVv5ABXrqb%AJ0d)1fvcb4T zRl(T(FK?QGz)Sp@fDLUqMiZ3BgKiRxH9@Jg6x806E27oxl*WpRdSykFYqcxI-<<`! z==EQl{@L5`PS^eqjYK&*I7yVD#7kYKUs zw75t1N~>0BGSo3*RKLgSSuoZ>ACr^*=@Zse6M3xNOrdy|C zLdxpvg>|m?RLhJ2*-iMXND0K@HXnE@m6>cET_;;XH;%96B>7ZB@Ls>!xYuJ~r8m;fISm-L{@W zEOs}(RM#oX={=p%a&Cu(2R=}UO0B89EAH(`e&9BpxgKdlc>a_uGaSm}Z`OqJy7n68 zBz@rN3}$5L&3r3!Ive_qi}K;*Qz--|Kmi$pEWvTwWvdqoe6(p@Z{id!78sNbT`Mrm zvDW8zXO#e1wKNU{sRQSsbPP;qXRTn>PgkJ_3$WLzFI986GI@RskJt#9|&4#&rIZJmpB=pUa_E;!4EuSsM&aRQD#7XEH zal$bqCOcX!6Q^#KyUY7ALnHI?GEM%Rse!XbY+hM>y>K)QF{BgPpB2we39JSa&6mxV zx@hA#uJz^App>y!7FuHqwhc3L_K(5kmcLZ{wL$yUIitxK)|UK!+5Rk+Wy_o{mMaEu zzc40X=wItU8=rwd$qY1`lYR*2yuBayuAbJqtFN$lqWibS5s);|KRX!0Xd*}a|izb8o@WAQIx zm*`@tCQfUZ_*kxw>z5RzhbYb&)fi^_kHMWO+E=>q~mi6*DNX9<_6*d%jAS3m7nKFsp=FR4-0~x)wdcxdeIi7! z-1U67|HYV20$=LK36UgM_(x~rCK63bnf^T zAjbg39a^yRcrP-H82To*jVk%#umwkvZ;Jal*e4IdnZ`Yw9J{a}KN+ijgzGe`FeJ;V zP@Gh%8Ml17e(!Crf6DoKr;E%+B74L=dUk8mH_gi3|LSHQ!^vlUOigzZ12T4M`o=-P zD_vKt9Pe@BJ8g@2PXO%4{7l7jIQN2X+S6S*<}`@kzdDPRk63$f-WqH$lz|cIU)~yo z9`t4xJ%=mu79pSdv!7tKpu_%!=V;AbViry*MHAD5B6;b#a)r=Lynk9wam)Kc#3d9| zdr#tbqYc=WY~*Oh|H1ygQ{%iK*T0Zt4zF>mdt&vyt2rz{*sT7Xjr#<~WX6N=>1Kz7 z-M6JdVyQME{mY+)cy<=z)qTRQEGe86&(1=;dbi4tngxm`rUymxX5pPGmp7_#=?t%l zR};I1QN^nXdyzLPe9ou_v|m6I3uO2{Q-<%iH|I}YyuBwde>dmM-#os7`TLOuqhhrZ z=Ln!Y)grMvH{R0)iYbx>!eviLG3J3hvMsyS{N7T-Lg{T5TbI7j#zLi*ftCS;?Isb+f~(c|aaFI25l%oih3F0o+;suV+=;3;jp z0JeW2bhko0q_cV8U8~2r{eE+KKQ7aW@8Pn91PJNdT!ub$-9A(%&Dixvbv*rvv$IUZ z|8Ld{4q>C#OV&JZR?OJuc}uS$5s-`)adF{nS}KvFA_1#*GV~&#KoDNmPJkqMvDSCp z@LrJ(5_x!BO92-Fvk2BzA&S#AvkZzVh#;SLR1v6NH#K6X@`x#+O z`Yrw)TGL#==Z4FERwJ3|#zn3CyFoS9uexv-H!p6aexR;CN5obAjU!hpfSEj56>xakwaw~!UcZ9=by zipRw_`WE7w0c+|w_-5_%Iedfd!ahIK;v3K|TNWpJ!F=3}xFj!PBx)l71w(&b0jNh@ z4I9iD0ub&K!KeO^R%g~ohb&C$iC1^JTSH8W*Y9-h_z`a~n$8_kFauatur^QaL~Xa! z%q?iSpc$XAbEU4B!jp?BT+`0aaYSZ%3HY(qS};~)x78BL@em6 z>r~chaSHtKW zdz53Rb9;mwf&Xjeb?LolWYN*+d-Calm&($Eh+NhG-%SrU*6vDL@(BUF7G|U^ubwSd2Jrh_Sk%|D$X5=jj?34M#0H6x}+elC{H94n2P5W8a3 zB)eC|lb7@_on4Up+zV)im(kUh4>6a`^^cvb^z1!GA@4WH&X34#etsZ4%g&=yLy3wK0Gy)p<1Wt75g1rI(ae3!Y*noKw>mAyr#_g) zT{>D^ytlcyzq!8OZQD~Ih4gL-yXD+qS~3&P_QnZr@xJ807tjURLzE)k;{af%|Blyw zhdgV}sU9QLnw^em%+tN;G3USWfrv02DfnkK_h}YAj1PN5Hoz%py$wa{)e>yR!1)RZ zN#k4JU)0cCy~~oiPy;2d!hDG#{DFj7fs8UwhGq6ruMnz1h~DhI5=*^=VyesSSXa|Z zUm|ONCzKbf+>9Mj=cKee*dM%CrOD#7v*eT*r~WB%pLrZ~(Q=`gOTEY<8c=b!nnP6P zR8>>^;lxU5g>dy4Hj9ZR)Z3D1n)eKz3N}HXYw|xQPLE(Xp&qCG640Djx6Zl?c@Vc3 zY}Lo^0_O@aZ)w20qbjWV+p75<>45Lj3icvDD;(@-n5P|LN%Bmcyd1GhOcVf1BR@6X zAH5;)4`r4lFPIst_v6X;o)R%edE7vn>xYdC2&aLuUV_cW|07!VMf^XWX(`vkR!4#*z$t(4`I`@LV$9nU1FJD1`uJld3synBGE_tfL z^hZgLdf%v#`Uq@(_`Gtu(CB;ml!6&2;yup`o*lmH*_obaQ3Ml5>ct}T@fQ|0 zv_6~f0ut@GzowNs1Nh4q#LRi<3liZ>SNV21gLFa$LuZg+t`iM-dQ5M+_}OE+`=fDG ziHm?b=GLXgBAn?C@=(5pe2*M2-$#`%IH=hqWhKa!n5|aJT5q#%BPy(GqK; zd{ES%!jT6>`y<9=2dedoa28}$n+Kz{M?z%f;CXV{Yw_}KXye4_ycY$w^v6p_z%POy zfHaR1j|VPvi@VctGQ`0Lt!&$-k!;lcXz~j$i1vRb?mgkwzq*P*YR|L2Ue#FG2AgK= zGrsKy@ol$hS(lXYk=E`q*<8_-MmB2zl&AN7xcSXqJi9=HsBH2cFG`_24Ry5rrg^Q{ zk0I5qVPZqYZm9ed%{VzJ$r6@i(V$G6?Nt4@3fgqi(_>$l$9c zKE&WA?=>NAJUQvDuATZa%|s{j?rgAqa8UB@sx5AUs65NR83xOEESh>peaAX)s7dZ#{>5 z6Bdw2K?3l+z+ZdtAHa~0tv_BqFnCF`_dAt(*G=4Lzs^9Ha~<*IS8Rk2SxJ%W{C%%V zCm%qcUvs9-d%8JUG8K(e?9``jnr$3*dLpQ&`qMf|jOIxrq6<9r4-OVHTh0cH32;}M z1-JYts{HhKSbZAeC&n1!A7Xoc7Ywm3Q5yIDFnC@#zA8IDq8@2{_;BUMX9jnOak<{} zvD%kn^>8v@-yW}hBK-i6x#^1I4fW6wu{#zy;~r|m?-(?Xd1-g(H%*MW9lusEsP>6? ztp!A6-fss#603h|)sjRhhmsS~c>SK$XVWwVcsC=heL7bARAe3=Pt>?~@EwM>&f6B9 z|HVQ&t~bXx2MJ*5@6$<&$7{2#3)AHbqq%s;FXfs9qyK0I#V)Ix52p`3$u4N_=X%j7 zH{;%~STL-PXzf!moDk@>bbNzzF}e6J;yVgD#=VEpAUm;szwOyVon~$uTrlWldN*}K z!DQY^MT^<;cjPakaMi@U$Nba(Q;=dem9?V*hTe@_hXHT2GZXxvjhA5c%Sa`?ZlLku zHizud^p+VCwR4P|Bwgd;6tCVPu9h!GfgbgCT~deMa@CpE(DBY@GF||4EPB;O@QL(G z6It-cBCKks?FWKopmRW3fOTVn)z%QD0yP8JZ9SbJa|__su8^10yY*+LT%9IRk4#f7 zvA`&ATIgQ4JEWtB7N80+rps&bdI}fxp{+(!E0q<^1a%~SuAXjGMZvnKaQ3vi%_>i* z2kYoELAUZka`iS_U#KQ|K)p?WPSc;o@`d^cIKh&Gst#Vv$d=#?1*{gV>qO%19Xxkb z67Epk7a9}*#EEgSq1`d88bfjuCXvVzKzDC}xHo zEoW>-7e^=f$2Mk+K;^@d>M`a#5lQ~Q7(Y_apC%u?Kl^*OW-<>lr|{&RxUUs5KUJVQ zBH^Cr~RsP947SH?eZUJKeLbx%_cRVe%dk38<|cxy;b9W0@Jr%}4sV zmwp#U>@fP>K(P+3<=^Y7L;tJ(Ap60$^sk|^`72nIIMhMo0x}BaV^|SK+4w+ta8Z;S zG7Rz?N-V~#xw^-a;ST}Cmc?PEm@{A?t2;y1a(GRwzH^mvf#QgFh4Ftni4X}fc_A(+ zAj$*(0DC9y)2BC;r$hzd99oFvx!FJaZEKkXBzIPOpSLUh{lP4}g_mV!y*nE|Sct#M zwuObo{_RiyF0=kRUIU}!by+ornV*xi&x4W%LvsE)p;BJ{(JbU}13SKbzEbw?(Vk8J z7yEOe`ty$54*CZ6_k6FX9LAxDL@7~>!-28awkF1DCU=6EwAIMj4F zD**Mr(P?5F*E-)Ddbz^fPsPhMJXHK0nlNszqWo?9;OYn-`aJWeCIaJgD=6x%&nHtpK_e&YE$mFjJF)O)E%%gsLq~;@uQwX zB1x1%war$mEfF?m=qc@rKqKx%WmNIshkE#eIX70pvQc$6f@W=sNM5D)FhFybLY6A{ zj1n$F+0t`Uy^i+{?QOZpNDAl3R*kz}uVD@c#7oOfAD4XV+rJw&ad5KbH3o@L1}lAF zvp^>|Rlo=A;*dc#kiNE_M+u}uNh%}744`Q8fIXX35!l)CW?2$pQ z38V9~-tqL9m^Lk^g;mHwLJi{SP}xn^)#p%ZbqRewlZU)qVXtjYQ;6*5Ete~%5#%95 z2yZcImCKl#L*{_PapIK?hi5qzuWWqncFyV`PTnR?jJZ!tV~cRw74==f$sLF^A*;eT zWoM%|P@J-si%l@7s$0(sLsT6bqs;6!<-4M_U1>$2&&Db<=}P@AsW;smc*LKDNO-Ha zt?f`@c-Sky6L4|)QOnLYajAl+WpzPUW)qGGMdFxI%Q&EP_Ta}Dp?uL;`R4t~aOm$; z?6K1^({P|8uhn$zpW&0oJlHA?*aS-sB190Y-?h4w5e+(DTp6h)SlknYg=lC!TxdMp zI|Ew%hLkcnX~ui(YJ3eoj3roMW6`hxgaO8*aS>_F&X;CQ>wOHs)Ep9E~v&`;K^R7y5ulBgxA%{=rxA=5^JaMKYvS z@{P*0MVYF}6hz9i@zKsR@zE4E&GjD6N}Ow4@GvKJrgf`>MZxTCmW%;2Qm0$K&EO)A z6pEntu3#9|$B&WbmOnlyE!=NR3WOvhx_@ z7)XT<#PI03U}-YOa-x9v`KY`@dlTQ_OTHJfjC-<=a~yBD*> zhMsDqViq*o^1dH{ZUM|KqdzrU8)i zv+p)l&arP#!))-a(!NFJo44uq?SGwR-sal3e>~f~&9`sU)O<%Bc^c4eCqCb*eS{FH7n9E+Govkiw$ncr}} z)~YQ|>u)O1AiQtwGCw=~N0tz#cYB*s_0Mp3`CG8$_aKyH#9DBxT<@o?29iT*cesk&CJu~gkO8pe>{5q|*c(zloWw8r7x-t)my ze!iuyx}I0|mcjzxd0r^%73jHG&&mIJ_1{k_*veWdaAvjgm#j+${O8ijSMEQp-{iGi zA;2}9&ysuYcgS63vX%RUmCSv8$@T7>OI`lIn6h`a>iN6&dA*+Rw$C@}`40R1zy}Ly z&rqT8QmOQ3_>@0^#IL>Hj9|T)F#@3SmN!8;dDivrZT*Obe1tJn`qie5CDZ*G=HaXQ z=1(@ye^m+TXgSTCPjkI9{9#|iCG_UL1e)@lzI&Rs+^-INftB}y`Sw)-({K1TQ{tIt zw=Gra7c6HX*mwVDt8>rp`(6&y?eLhb>(bx5^I1k_@DZ~6w}*ft9$~Kcb**>qj@|~< zDTs4R#?}7!Sv?B)e5L+e(sr~caTTYdujYuAmm+cltQ^!Y8|0~U*h^${bvdMrR z9J9!plRptzY4(O={6F;)k*hgJ^Lu_e=3}^QBEo?8IF<;%d@&J+KSbmSJW?h{(NSTt z^d9rSF~@>%i9q;PK`%V+nE6=y!nf4#_tRGi7n5k{XnRhE&-aoGA;d+>Qw6BRbTOGv#vfU0Jh|EGv~^B`M0Z zl;7*!wZBxR^t)FHm-SEcH{Ce7=n^0(WxS>=8?6M77jqTC(x!QbVN{vSxR=(kp*!CP zEFwQQbZ5h@*AvzM5TV!ohV^(h#k@z|j;2HYE8k}SLmjH1AKS^veEE0Y_G|{<31{6` zSRu0LpF%yY54F=L41}e;XeYk$=4Uf8FKOWB9de5kzT3fn-q#wy9C*PNTvfNZwF@iV z`i0Y1UBCyI54fC1&1Yxbb*8yomI+|Dw$Q=-u6OGpx5HF~z0UQxVY7BImyti=7C+{% zjLZOGf24Nf$(r}YlHaM(hA72%YOsRaDLLFozl1FQmF_c{`tH?JV9%PpU%JBOM}6l! z<^F7gg^bs9b-Wl5T;yZ z=xCO+vu$@-{?587f~AXF4;nj@kfFw$x72F#X&%Iy&s?|t?V3;JGb0$tfZbJg2(G7N@e2cVwJPJH}W9L zUED7Am&I|lTO9GGHF=L9pcy@6NGGY-7 zNG7pxJDHhyN7I1N>L0j(FftL{%RSrD*E0hgG$b>SDHwbbTx|a#kMrB^;Ju|{ej95Z zl>C+e_vU7!2n-o}HVlhtX5$$R+RVn!%#&v0moynox+Us)()RobZVd#F2I#%Kz63k| zWu!k2PYv3MvU*!HH{=^@qBKk9lkr37(2UQ99{04LCiK4nhL}>ObPEVdo?-W2w#PY-}1(Ce0RC7>rN% zn9Q1!yv%#qNT}7>K<4$f();89;w@Pj^X@Tt?0wjmTcG!4OCmZ&B6f(tyY=q$Rp;x-@rED7wWIg_qW-6x0rn0{x_8G=`W7nF0B4gtYCv; z@@pgZ&v@}5`m9Mmpd?KEN`Qk*=1Rzsxy9vl5sd^Ii#via)0MW_@HdwrnT8_dts<|oDZBEn9F_Nz z5OsZ2-e=AonK!Du?3>yx8}c+B@#NaOZk06jwFkZ{i7(c1*YzwtZgM@emRavu@A9VG z1Up)XNg%x-n^2~w zw!HQ8G{|V@ia`j1L)iTmZ~Zw1$jL+81^) z6sPiX;D3!aW3E7Pw>M1_tL0QKuH<{mzu8J6>@r z7tVc!r;W~>0sVf$nbQ1CXUd1S^82)l3YZPAANoX-tt0~{N?u?GwRwDSFM~& zrF%`KurN*I;l|fgB*wWHHGOwA*O(5L8lu|X>n5)nAM=`5auv(D_!e}rr0)P`x~^qP z(|7$T;&duMyp^5q`}FJMpI`xgcM!MAy2Ekr<3n*T@%y-U>vQq?OoCGYhn;nwXJ+d< z>Gj7qYMpX(lYtL-d}X!|@I(=}7!O<{fq_n}5$B?_iJs zrRkf^?T6RAGckb^GlJY|!Ui?340`7i(BRl^rN7A9i4FbIfamQrFfFf$8BODu#THP^ z)7>sdyd@Pbq1l8v9k+g$eto|_m4J7-qN=zzvWZ?4L=+VC($s_Vd5(58R`@?b#4`Zu zXsn_VTKy&i`M&$<9xT$0RnC+%3!N#^!b*V0d`^iMRsox6;atu3lm7P-&r-VS`|N^C z@Tl6JX)KF*+sw?dc$G&)zrvVuz<>5nf9IXgmto=${hk9@Pjx~V)>B;^3^Mj>vZz%hA2SV zBT-K3T%C9RFz!J!mS)^y7H9oaH*loV(;O0o`-TcUK#LOR9&VUkn3#>EP{rPm*r~eh z${nd$)5Ut%gUQ8i+k~pYU)$B3U9X?1nNEm5bN19_=n&8bpjPl;11Q^!2G9r>96Tep5I=h=>BosPv?*K%R)AQ`fBK%L%zK&bCdFN5lgkCc} zR(wrmv)5ttYW3Z&bLn1;{)q#w_cvh3s_AkAn;Tq@7x(@s_CN0ZzS;ZJUCrL5(*y7} z7hejK`yU7qm9hGsRpU8Lk4rb$V`L5Fl*BW!TJ&4nR+Y!(>y<-Y=Fo#?6jxs?|8NWi!Zg&Mcb z%8P0$ZV-=hcjS$k(@K8*R+A1kbtW2P-kCN2O`jLjg3fK&pUi6Xrm8tQJF3|8l{Q%IcHr*SzLZ+Se8DT3VjcJBLnPCFwZ7ZNV;#S+d|q!0&WUWkH%)ho zZ>kJ?QNNuD9j1p&s8kMI3L$#l6K< zaqku9yp?;MW#&Za^>OdDnB8BT^KQhfG`ZRHyq2&%>VmVnseI6Yx=y~<~&$1n+5%g1CRuTUVoYohdDamClq~!kv;C^}j@~yz72F^+0L+6{7MU{_%;8Y4zyT1*Jd=B)ivJZ!694XB2VKAFv+3G z_a-!AxA@O)CdGKQvks_9@7k)E_vtz8u5;O4=SQ45s}J$#=7H=!o8xBLZ@kFWMyhnZ z^#Uq5FNQb$F}y=XFb$g?uC^3q}k*A2hO5J+sdS$<58>BxioMe zTUO9m>BoLElV=yn=(JmHrMB#6!m`S*4%xD$7nuBXlmb`h7pPYO&)aJ{IO%N^p`VMC zLz0p9Mrx??TV@4)qTUDc>izYv)el?mnfdh|JX7^bfMSP~{?|%Tfj&A}dT^#zq6PaW zzHPuhhaZ9c@x@js8f!mu|`bj$2Be1riP)3`suL4w_DU zI#6(qF&K}#3Df1bE;&% zyL`GMSN7Qu{ZHcTvs2LLcN_25yM{D~97Zzu%#BtU&kaT3bUBj~GL!r^ToLe*1cp2k z4+tT969?QN_%aX8_^7yPoN~R}g=Ely>&*h^99G>v;TW7Xi?WJ<$T9D6|0rDiPYRjo ztL4ysEskp2U3O^ES21Cq`S%xq@(1R+ZC@%YXsM5Si(yMk-~CywY*vYKYgmv%Z{dYu zK@H1a_-`*{=5^*O7Oifj)=dlurt5x7xQBls42bhhn1lPS*s-Pc4?ozzYiv6Q0GQfK z{knb8xdQfh-QST0mHyZ}LEq&)9OrxVYErWNTpYH@>=irX z-uvg)?x=m#+mqT~94mfY;S9a^6IS$>jhU%V@o(y%UbTC0DLi$Y>Q1xOMZKf=4VfC_ z@R$uN?h5PUyp<}I&Fd5ox1J}LlvaMl+==lH z;2zBJD10fcA-S)Qol)yjA5s7T|^?v!^NU`(P5CD5PGP@J&x$(0a`l3&woZEK0ffVD|cDn(B|GrlQfki7jwhU-Yd2o2t|K1k` zhxZ#e+#bN;P78;}w%>jq`Sp*|z~PJ6`^BG9)5%eAc=UUM1B(8U1mIXW1mJkY$UC9| zSHkkOy|5H2(CiJc3|ZC2R{gSn#)qHH)a`==)E`R%P7eqGP!Vw8WBga~sfl%*S!0NR z%nStS<+QriJXspti)OUEJ6QJb0G&oH`<-UlgYjm~zyHUg7L-cAOEf2c#n%sOk!t}2 zEBRWj!o@B9fVko}B)8J0rD^dApz;)xTo-M~nX`FV*s=%n+mXL#wtAu3+i^4OP#u zRY$#p@nngqT`+gE_VZEoGkw$_LCpnUnuIBcWJwz2_^)ID|btCKl&$~<~|D7Mr$KR;_&Ph_IH7u2`4Yes^mt@k( zQi!;P=u49k?s-J>z~_=FIr!XUo4z<`TGl+j ziKeMp$8GX}1AJgtpQLQM=G2tgS>aU9Eb?V;MRXvnN~fmDW%wB{$GDVU4Y?y!aw(#XERan_8ZAsM?gmu zGmu4A$ek-mLE)CXtkAt^e#_~I=4BjuAnf;xd5ZH$vLVx93UiFl>vWgPqp>)aoSJf% zr?^KuR&;W~_(KFU=;Dw}#`?4)lg zmQw5IQ6l;?o*}MP24k`4a|ad+Fn^-nxULU1zm4?X++Wc`3~m+%)`|5y8-fjDAtC*}~6Um)aq zFD)AYfe{mo3*g__Us8jU#8*Ib(=tAZtTRGChDx|fi9!2xZ44iQFMTC7vUuDxI-=#; zBuDAsig9R?aj@J8!FeaUsi?p5Qb$H{WGrGL328f6JTg+BfYdeZZ$j%K$b!RzhK5HM>f)ZkJELb z;lm}U*oy-t+9aEVWJj^_QS~lx9K-SHH>u(NT-4RN_t?o8w#CKA`p zn7;w+hdw>itHWI*++;us2N}S2VorhTpFK9J$4Nc8L9rR{tvlUvQ#MwRC41m;V;!K@&8&XLv9AaZM(C4cyzsU!Y{ofQiC2S@(e{ zVxsqP^gJcB>UNam0OIe0|1*7+F$0q5H1`Tp3zg9aX$}41>El4p@e!VKJ!~gH?Y5)U z*ZTGQ#;N)}6a4zasAYb@~ zn6GR<6DMb*3TqoEE&TlXuYSbE%F=dco&q8NsO@i%Dmr}T=3-kib&UUZ30c^?JL)K|E-#BaaWc2P@j_pf> zN7sL@81WN$8C8ZfXtm!sH!C`(g-*ZcTe?j?{SVSn_y|Zx#cd|(VES!inn1Tpa5McO zs6f6p?s*KNa}41K*aE;tbw^+jZn`2_F-8I!2b_JuxxP%6H19klw(V?+HIz5vWOXB=l})3XhIv^*Hzm!FN7RlEkR-nuFq-iSc?amZw$?_SF$liai+>o;48VHj{H!RP^P#uk z0O|R%AMDw-NE9|!1da@)?p0jKJkzqA5E_J0BAdNRypouQsF@d3jRZhGk&?sXx&$AI z{VHZ&Yx+jpjkU%Z%iKaUNdgD}6WdN_<1zTqPL@juC`ZYdF zkJ|`_x|p`dvZm*DV>*iUSLnxh1XNxTPrXWQ&^d->{a8%l9%6f}clVm}(O9d|_0TH2 zD@xHPZZH;6Y)Wc^P(Ap;((*3u#nqT$;&6-b&xPryyT|8ABUq5V?Z%S4Gmm z0P~T0aC71^2yrqQl)u3s$_3A||1S~;OnANp@ccVxaxG+4gw&3|}z%y(%0J#Zx zR-JcS;QS8>&IcKs>w{Tvu5!D!Pl7YfZCcP=ETQ=UK=VNf&9Ui&lp!%dGo*f7Yd>?w z;8~|$95c4{BWONS6uzAID%swGe>`|*n-V23ACSO&F$1#%1|Hj`IU)4fYcA2GTqdAa z1-wPr~y2$13VWJJQI_RZmZ7Go@kmg89H&52_N~ZDp)_` zI$y!I>0{#HytTOEu)Dk(h5N*_5|&?JSYC@DMLZ#4nPY6QyfxPQ$~AUa-o>!|3+@uN zH+V55Lkz^-j89e)pI!<+y%ZoEBM`2LgYZ^k|Ip*$i=-h}*?O8jRhOgUEBMGg=Sf&L z+Wf)r770wJ6z77aCuoN?sUvb9DsKhw@FyZAci94?@ByeXt$aKd&O#4Xf#gB7Hqs8# zBN=OKf4~?|V7`(Mrl{f2PljMGpH28OVp;^>X)paP2 zIO2l$0wshR#@&=0R+^12c+v5HqQ2}_^)b6o%snWN zN2#BeVB+8m?IV)Fc!&iAwQW z^depwoeb)zz>iuN2!tLnNftQCFRq*H58pHyu0a$`l>)at0e@Tm8`B6={IQs_v6!F< zSZJ>=ydM9(h2wm%3LtOP&bKLhob@TzCrzNTf?#j9B>VKUl-RtL#3y5iNaA3-I8g3S z2|7WgvCA?w$zO{PRxNo4Cx620NLzyV}XOP-)`f7lP7%PcKMjj`T4{tS7e zSilA7zu_%FxQtwUVt>OwP8g$^(Q`vo4WhG8!UapDD=MbV0^~*d0@_T#IAEqxTT9#j zxwKDE#dFM{3XRLvHD;0Z)qHGdcs5Cbde2cvHiT0~J5)XOF8XkL8`)tGxwCf{HmZ8> zTPPR?`RXxzrB2`+%*M5{Y>JE@3T_Ik6pSl*`-3-!QA#^$3~jjg;)lU;PJeA*jO{3m zmqy$6@GO7K7OCnrLJ0qKtG`Cov$dz$1uLk8mYHzI#D_397eGB~g!ysDSik1R$1!vfl7ZaO8jtz2Q7h1SwIRIXk_CPh5h!MY?gUHz#aPBc=1at6XA?x zij^r-OIM!7NoH{pI)z2r%aBa?{#A*D3K~Z8^Vzn9hA|!qZ3;wD$qcwIrU4`{C%frK zmw}?9fwJn_OpZ(a5NIZ)M7oR#r{2#ADt6OPIQ6EvP9(2rl3!cH{54r-W^ammMXvn1 zhNOgg#ZdX>{8QHlr<@k!XF%Or*Wy{mV>L}r# zCMnJ9lLHgIV$S9Bhy|+2>H6dKLI1HiO&StFBo_2E&-wHPyas>d6~FRRqOvABy8)M7 z^wT_ND#AR0w4#DWtPk#|YNB0Gj52T?aB2A_7*xm1e+=#jEJi1c20SCayP0{ii8dukPsIT~!|D!m& zwA0kbWD_ihAE6_&Fd-pCh7p~S)JT}K#pnyU5e(}yh`18eMzW@0_!xfM%a5V|7>XSM zo9}~Z`NFuVnP3C<8X1pWkxdhNoPMAV#$Fddn(90*p>q<((KzD^O3c9b+7W&={si?X z89E6ZVA&dboIwix!ST9Ql3dGR!!JIBB_8eZAcYO5zVJ0FLrvN4@iq8-==HO(3A=8I zPahG&XAGioye`J;^H`|xS_AQ}e%-5hR^n;E)2LKl2X)%@P}b@rj{53HREB7L>PJ*% zW0HLO)r%B&j}cAAbkgXQZJ?U%Bh2tbt29RCYh%n;)G4OW7?9BAG!kUWalMgTMHzu9ogvbiPC{NqXQBa!?&_m}nQEguT%`esYP>q9gR&sG?6;I7)^#RUV@@s# zaZIdbo}-&gk|0iav&0-C8TJw|{D;ckU9J$4L(V*j7?;WJ3=HeTNCz9LnO%^WStYePKoDL7k%L?iWQ zYAAp{3M-e@%rW0f3$=8YheDN*F?3|5*OeThqhYuVL>`Q!^$;aw#5gnPOsN2LIDbX3W{O;&vPx;|M4>PC?b&xnN3h`RJ(&VUD5#AmtGDvIs zI)m<#RCEMIFXSWqrAoJy9?`Q|haun3TuF3JzC)0Qvgnrb5lJ{iPa{(C1%@qFrxbkQ zosq~YP(MA4r zb#?2WCSUn~dOCqQShd7?gQ28$A>N#Xd~3V+bF3m&knzcKLToVVbNNtxxc7*c&j(&} zTxFMj5YBQ$UTlpAqa;2S8|D5CJ`w$nP0si6d%dH<`2l}N%x}Z_0pQK=a5>+{@AZyu zW_+$0KiK@nQ9RD?GruRB-*@u&dH^iW+DorydlR99In+$z!v}Xr#fzG{*o;%JAmN33zL$tf$zP%V`x{0C z^S=!9A5MRkKK!tZdI#TEp%W^hUq#&jmK)x+px}51B*uh1pc($TxEgHgn{5 zkVE!m9K|5V1T%-v%%OIWL)O8}5^~Jsl=E}%G;>VGF9d&!&>jtATVnzYb-juTLzYFH z6cb`*sig3DN33qYN}<+-m^ofFb1Z3-LpG_=mSeY>W0RSqp-m3i?W49FEoP30nZuyC zvW*@>IW#Vuf(aS+wpw;VBHb%s>hg9#Rb7hp0Q;kd&p8*Jn@f&IeBvVWIIGj zKJb*xFBXFh?oC-`v3r~_nfWlUCU2AbceoN1GNal3@;0qRtq%9^;20D- z3tZ|@x!`snBph`>=Qa+KoUXI`!k1XB;l%xcNh`6-ba*RM%e)sLPnBA>U>Xuj#bEEV zKg&k@7tFLm!1~tDos6%pSR?A^61-;;%wZ9PIBO~xDBh{udP`(svlMKC-!)I4FmQ_0 z$Ik<6m4`zrF(sfoQ@Vq%9fn3ZB8xGw+DVtYg3gTkM3+3MJJ=x% zPGC_&vL*1<9?pas5ZbU!fxaWcDHq{55YA4$XUxP5Ve1_Lk2lkWDWf=l2_!QaT~IAH z3tp_h*$}^Na~b{F7}HnsRZ+#^hwd(|DlSR?$QM>p==g~uMh){8G(6a6jPMR0S{*{e zR0y>^#vi+W5{u?~hrpaUHLb@0M`5#fc&;mvAlMZV$1~Bz2ce6h&4CA%KZ}&c(uo)X zax3)F?1et@O;N=s?()|Fp1yE@Hmp#gat`;Nfuv;38khi^Y52N9bKXXC#tti}<<|Td zM&4)$*J$*m)?7HG7Ii*^I?7vS0~z&LG9^J?XiqB=0tJ{Q$$a+ z_8*k|4N59%tZ^X>otol zqGBhbjMJK@SZZRPO5B~y)3t~&-d&X#mu}Sa6ly2ti99dO;aS4|36S`W;{4QV%*sEa zF@L@BG#hiTtugPN(Me-ABEnE@jhRiYyAO558xtP+rPlIG_q>_JF@&#(3bcF?R-dlf z!Tt_0e_k*AsLt)NJv{Q-@yzMDID5G3)xB3Z2r14YO@BBelgd2@bHn4hVYn)jB`FWMJ0 zaRZSvX7@|0h6MUMf_UcM3x3>@-07bg)h~t1er>4Nz0O?KsI`MA_F~Lyd6^sfr<)|2EyJyfWe)QFuxE)QT^f9r18k( zM3->{kP-;i?Uib`=$g~A^8pgKY;bh<~RC_T= z$*!KHtfUNRvf0#Jvr(nI4Zf!@;ub|OX&#lH8GOi=t+Nad4I*+qDvdB4TG^SI=1Dnq z$kiTy!Poa*7Tc)C>LO1OEdg+jg^pE9v%#Q@BL)Rr3J$8$9)2>jD&&V?;(D<$`Zu|2 zKR1nc?O4jzy9PLvRU_!QU%2-f-2lSpK8*vnQ!kPG^%rOMcg(?4dtZOYCOnyV&%tva z-k-yB1Ku5kp2^OXR9Ba-u7CRPU&+Ijw4w-7AE20{@V@aOD50FPAtO8(f`leV(viq& zB(RYb?2_nwrlsGZb0wD5g$K*Aw(eN5h`{CoGSwpSjdncXp0|d_?1wulfL$f)METsy zD`-Wii!~Js7m!^Ek4rK>d7h~16B7XYs@Q6z{$_tHau=wsfB0<^0+^~Yf~q=4M2+zq zj-gUO>5ywYJ^`MpXhNypONIR%g?e!oJUkBERuQ&-Pf&f9>`66#^4t9}_X?{hDmY$T1Bxjwfngs!@HM5V zP9@hnJUeg$oVN_&Ci-QY!lO^R*Vf|P@c^aH5mjSST_EEsUFLW6Aw)*Ph5vvmlbWq7 zIC-U{sye5R0#3Q}%1XjDm4vR?FvQx8im~er45r4JARqGyPiFOU^tuSPf<-V3M7Q9K zfL%IpPZ{^7FQy5YD8*Pa$+j{?m1rA0aRavX%A3UAE8(Zs@)&w>b64jjMwfEME54=T zXhpnKa&8-4!np+MK2_lnKRQk+DN%QV_YAdgSP zg@Q0B)s@*Gh|SDgd|RnQs8n)6XIyD773P9E%@?c)WzjF8G%8w0(#U_ER?&m2X@Nlb z#y((x)nL(u3&ufqS@88%!AKh^#D^S0Ky@*OgRZK%z8GVph#LTxSWE*Ko6`;NZ{erH z$b}n^HjtGvpuN@(?U65GsX`v>-$jH%{tdSpgFgf=?m@UBK8#)}t6H6kLZXAr5A@Fw z%`!hQiDZk%pt8CPJ_^IVaI$PGCd5S!w^hs;Q1o^zQ$P3sCC@|2m_-UT*8UX%GpH&F zX~N?w8h)EAFbt83cn2gN*a`pqMiHR37{)oc=q{xQJ`|3@xMHg;^n>?N#-&t7_^B^| zC}!CVNCl%Txh1T=WYEJ{!k zPq3QCdgU=h!LV13F-^tFW68;mXr>uLTX=*dL3cf9$hj9d0)wfG!NHgb?Ako8h!KYw znp11CXy$ki&daVxX|{UNCYv0vG2bSyamBL>b{Z*0JoW006UNv zA3W;xF@E_3i01Nzd5iX30f>fj5=ybH#$Oj9-{ZD?@FChg?((6N;YQ!#3QfTTVW*Z6|Y()m6CLbE+6GPEPSkNab zlHjssAqop;40{;z%;gKysIg_6Nz;m|IR&4a*=p=uny56it;JJw!08Dc?CR-$%u_kk zf{)eUlogR_yte{Xzl;wWGx4^zw<{M_wvI$bgxwV=EfS{GCS1_aO0a;W0rtU9BmS-q zc4E1r7`+G#D&r-`V}6UCM*1SA2$Mufv%#E4T(*FZDaA2xIu=GpIa=zCWp{v0rKk@`fuo4!}jw*~E4@oo{^8ZZAE6+GE-0s>UtCu`H=g9g@rx}kyDPhmyB&9zl_rg`Tc8r69&jM9pVA9 z8S5WoJh*6fD?ET(#mL`LKsz`_1i|r|7crO{k0oF#{c!{=Lx2Jf2v>s&M`&+X5-o~` zoTRrSp*<-|sG+5MgHqGvGzn;Elj`lhF3tp*p>>WD6RRw)t`Y(pbNxg4n1C$E4}zED zB^$CSSFjHnSzS4aro@wJjCcwS8G?j}%M4DYI9i7}?gEN)%Q#oi?;GyP4vys905?jZ zK*VJvQK|^!mU1i5Cx(5#AQ4;c3GBvJ-3MUDR=|l5qt60t1#TOJt$<8>=%khQn{O!X zT{7)1d>DtqNc%%dn>wq%Z`p?Er56`uDX5-m`=VDmo%*S&K z&l=bbS$VDB@59b(ES?m^^}usBo@w}=gJ&V08a!L@+>bhwQP(YVuwTK`AI}wdta7a1 zxn=zwi}5VOvk}jkc+SC7if1IAn>%QOmG?=lzvB;hs6U6z{){mD(}KFrL!7-|etgeG zzdpyaqJwgC(QY4}lZa14d={Qx;CnWn1$e6QY{Wx-qc+S%c_Fm%X*_-LZS}#0cdH)< zk!BL&J$S}KpxD@c{DTc@KG;@f^U@1!>ytkK{3mapD2&P2U&y zX0aetTnM4BxYq?MQ5>j(CYVqaW&|~KwmKJQ(Xm@)hGi?LB1eVCCQ}+ZB7^%Y3DIdo zJ7x(jN{^F_A-?e@7QUoA+zsg zN80n%nE9d?)j1S1CoTej2#U*ftTe-P6>dvvu$$s=c;m=6jZwCE!-X#6XIKqYjBWUe z-21Ro)%Ic+w#6qF?s^q0f$^n)AnT!C+WxNEi7xJW+tFgRWFw3u=C$Y~lYx9wVb@bM zL13G=7(ZJ8@}VJJoA7>NtXy}Sf(lXpwvm&Pu3|r83QlYd2La@!MlXQD`Q_ELCyI8% z>m_nI0ZT>W$$ZroBab)GoZb`K4*ukz(^HvmmH_Vz1TVC|vx_}QIYS3ZBTu5Ol5!0_ zZc(nn7NT7L4FCj{8-Ndk54n;r`C8z|weoZZ9qPoabdhfXX3$WZLh z2^FP@t0Ip0OJF86*+nO97DU0ykoC}Ho;(u9PV$8x%=Lq>ofdj{woIEvAlbkx;;rqUv9PD_ceEBz2vB z5y`9&b^fA6eb{p#hTDUfx@4~szGhq;A-ETH4p2kc9~<6fn~98%NhT6bRd0YAR%{t9 zdNmnSK?fjI)avtj9(~ZqxGOHlc8ZLFrb$5}ixf@VQf|iC4jV$?*wS_531VPkr>gp7 z%ovU-P0NJwnLa5C5;Qo_!v!dxYsZrUKlX~vUVYL^iSF>0nPe1mi=Lyo#nuNRRVQ{o zp-ps6@)6>ooZ89c!0>2y&+Cvx{F;*S;)1;s7h+0|VB?fR1qmney$c2{w7Ib|6S?K7 zLEfuW*8)0V00kIJG2UZ2M^Bj@x{oR#BL;iSpO`G}KAL6?_^rW0Ff?PN-@yI04dG}_%0oL3jf5=|5tlY})a*{Ehg zjg#CndFmWJWcf4=7xU1ALYwn966^7HL&)Tw(0*Xl)<#cduFL^5OL;>}0RC36R8R8{ z|8oPj`N4j8*-bA!>Krh>Hyc~$sEA^`ArCbt4fnHDJGp)3e0~t~2A6fNm09SesyS19 zLpG9*eKCE~&Q_i>te4)dNXdc4UM0Vrx84-FW4?8rLtFZtu;(XYN2elSrqJ@#`3~qy zbj1UMOM*FZ0zQ)AZJ`G5Av|QRTVK{dU!dRhWsP{FIfI>>SDXPP#U7bd;MgYv&$M;p zjGsgLLPiZUQnZ-s-VY>PQW73ZBtrKxjH_5i!^Fi`UptfJI_xcs5;DqvUQGf^3!Q7~ z;{;6-5Y)_Nmhrf4sdGT9X{T;=j=ik|z&ntH+Nfjk4_6G8XD{@>E_s7VN$NdGmFT67 z)gXnGb)Ls7-0k~2!3a`;+Gi=aRrtXV&4TJ2ux6EY#MY@gzcr_BhP^}*yoL|K0j+RG z>gQOOTX3`k@58r1hxRGGi;S0$%GmugQeMFJnBYmZ8N3s1G7kvA9s@?hnRLobdUBZQ zquYnxcduxGX@gIzWwQo(0R^qcs!{--QuA|GV)|g{y=P?j^1;3mSTF$kak>mga>%Ac zY*2^5z9D5R%t9~{O-I!$LNwp=9s@g}tfYM_tT$Y1vJnUqj@>ZfXe`#JHvrAO1-HX_ zk~Rb=OydSyiSNVa07c^pbF$5KAc9eH%F%q)`FHI0>10&z;UiP3b(DtNEgHRIWIEpz zPe-H1t0g0`T;i&B?N!8!h3mkASA1r1j9L$KHQ2p?Z>i}9&ZQM{E1QbgPFMx1p zd`YvajMw*mCAqkkLoUZo)i%16Se>^3$JgBR-zRA>PSu2a-!pYzjBNw*Fi6t=q22g( zMbzk$Wq+vzSIgsAGWE4-aD(@nI;8~z`VZ|y3?2w+17eJ;1yD}enqObu*T684;7O?; z#X3MZ)TUv-PTZ4TaKv5ydqfQ%mBORPV@Zuin1g3yt8_hMR6)37!Zw%*Q-lUG*!`;n zUlF~DCapkw7{f#iuUCxC;u5(esF+7(qiHN5v8E?JP_NFz#GJ(wQwAxTAI8(DS&XOQ zH?&6*qllsMPT3d35Ed1D>7KtE1L!644HfD0IW?$N5$_fieD>g7zLJeFv#Pb7Z^X&C z4ek}2OLrR3W*3s;$286{m8QY(w^x{Y3$AXbG3{zLTozy5gv2i>k}dumYFvd`qAEQR<4SkIfc*PoFk6H${h^ zZcU?nE(T5_HVm9Z%-RE|7ah-yLuQ`}9(t6MECFb%1fVP1Yu$qvwg({6xTmwmy^B}B z#ubeN&qk%-@Pp8y?uf9xE;*5*j*bOtW_q&;n zYs#J9WuIC*zughq`P~R^9*8pV5LTGWW?|=7c4l0@)jgheDW?s{w8!vabf>g8B+`CF z_Ax9s3vt>7GVK;h`_ThPJ2;W{FY&b3bK2XW$v_*PqqKjav?qF?4fa0|pD0z1(SebVZ@z;)oL^LEgkxMD|Xm9HcXr$rcdx@xl@ofZq^D};0FQ{Y*}eQy)VGltsm+gH*T z>BSbx5$sJVYg3SL`cI?#A$rir2^Ep+e6?iib1)gkX*@WBdX7*sTY48c>kUH5cR@6N zY!S`+kxVmb0rtS+A1gALCn|%BlcT@#kTps-8Z`QgWzS#0hq3a0^xP?X-cJ5v)4%(s zV_JVS8ZBWk-r_?ZYjgM7^XHPN6*#1cH*vN5k88-kR4h-s&ZpFlDt4kPN*ee zs#L+suX`6vV|%fBJ~xM0NAr4M|605V?i6T21q}tz9&pfyiaLY|@`^{fu@Z^76TBWP z+6gc}ax*+nwi)7xb`2hd2GjYvbOrh@yji z)3FMmb&g1PBs5oq$VmjyDEv@+)0_^qWFB?7-YfIh&!cdxaKNhy8Ydi|rk}_L{G)b_ zXs+NH4gT1i+TbN-S!(z>DhY1JN}N`4g8PqdU<*Y0Hy;KlT5M(ri_^$v{{2!pfGqc+h#mH2UGV)p1RN*ymJK0lqUN?Glmm2G>?Jq zq9-N)>q=T5PHl%^BLIXCcGEWV$_#9^-e=-Zg2-!KSRuY93)&+03f%w3)=hz4UZLO? zhRxy{<_}8H%;*67WJzl$%nFaRehsPtpXO<Rl9NU{ z13V2zJGgmlM_-VL)r8;f6)?!8sv&ACC!^sp;fr2JIAjU9?_*2}ne*V9%VP1?7%}Vs z{9I&?4qw#d3u9|w+ed)hJ%&8u2G|sU(rqz*14SF{5}2wH>rQ(F-X74bm9$u#sYM~| z+l2ET1l*6wO|Bm)r}m+AtC8r+>S@hIkl?V-lID@HwPO#KxRDHii_ZEBhSf^a8(D+~ zfu#*@2Xk74q5Vmw(p-}qoQlJbO)mHhELjJ40fC#vW(w#wy(x=+s}yNGpa z$tH3jP*5AVEYY>~P#e+)1s$OcN=XaaZrlPEQ7g?ok!W*d6qrfIeLmIaUQvrxm(M4X?yfK(D4-*H(~87bbiWVwtM3&#-zzHkV*0&f!YN1W`*n2qyJYvR zmRW57j@iEfZSv-%(?UfTl`lySjFB4wxqtf>{{J;%Hd^h_+VTJ5PlO=sA!i+4^4j`K z1b+BShP_Jop^g!kA0>*=U)pz^fC)|ea`iM5D}V8f4Dq{Qc$(G?Sk2EGrJf29irskhF@q` zM=zLj53#{y^Y;~UfiN#kr|f>`=gKOUO5g?>>=|gkqcnT7a+3l#Dq?n4dcldnHHtPn z%f7ei5R{iKoWNOCcH`eSP`y8Lgn7@7NmdH67% zz8j0t{#0{cVDiVa!4wF-_PZfld<9#kW0Tl`!$!$IZC$nx*K7uN_{23$&Fk`m**@VrN(6QgWl^0C zW)7FCZAOl3ntY;Lb8S96&tmVuX?#uv#jeLCs*;RCW0Ltd+Jw_l9H5 zT}@tB&TyA+Ln9$E822y%#SyVEx@4}yA>Hj6M+T8ktg8V7jm8(aPz7!$rmA5BOuM(_R2o={w=Sr}H%+J4evjKGSysdA~PmU!kKF@oPZHzBd>0MR3$yAa3li zC=MjZXenO=g1W;MXVU{FN%s)NB~1YSVqEFW!~X$%@58SSb(mPBX+F$`-x&c@ z?*qA^(N3iaP@Q}Q$V|V;H*VRpk4{>Udmh?~`Ni@|XgV(41m}5yo7ultF?J+$Dau$+ z{>$uiXQU7A9LO0|7hl1RSu^mzFl)wO1m;Q!Dvv39lZO5ndH^W>dML4Lv(Y$QaR39^ z%TRc5N%}^i}v*DuswmG z)sQINMk&^7`<(!07f}{TUkYm70@6;KqX>o*46N z+}~>{8Fw1D4opG9**PUPD47vNjO?D?l=! z+>eN|;0z+r<`HD@ixrdOKswQa(Cb2P62drwqdUTwizUV|P!JVjjM>g-3Xlgj{@0|n zhtlT>Yv2bSbFs6Yy;4zH(rP~OH*OMILOM%A-&-aBYPDvZ)tXTqwFYMt2!Blwr7q?FRs4 zBXGa|GNGI*fTnLTns#l4rZ5Re#$L-^&O3jJs=r}W?D;yh-+I~C$aQ!a0sY`#L{-nmRP#%_uRER7+4la zM19v-dp05Jeu=0L0a4}9-vUIn>L6SlV*Kz4s&^^Z8zFI?4Q0DpDEqvHvUIj0-pW-- zjbnSrl4`Asz~P}-7M+Qk&grVIfrOI4$4oVi-v;6Y&td-HB2WSrRRkarqQquOM%R{8 zro*m5Ttynl0zOZ9ySbW%3kg=Vn0Rr61)6>fBXEmm`xWi{&N0His+rjECzYoA8J4br zJ|&LqbV#4FN%Yc&NMU?TB1OwKQK@3FhZl5Q+3+%BB$jes*3HEnhW2*K#OwKj_lbpJ zx49JzLt>4jOpwb2CegmyFUIqFeqCtIw^JwPe&W<=tDP3uky8~{q5L3Ir(4=nr>(}Z z-LhF?4N)g*%WalE-fGV{t39WpPuv~}_#M%wbRrmLWBv#-|4y z>MsQ9Xb_kR8#+r$#WzqH`=4WUeu+lco<>qCnWk2z(c7kxluD*~R;Kwm(vYL;5LHZf zr80j3?d~??YEK(-bvu!(Z6>+OI4i+XSbW5TXu70Sqs1FXB!!B7JB2z^Hsu3uf=Quv z8U6bah2nd);qEuiN)UyLvl0cylT}g{F)jKd8)1s`60b91LZgVOXw6H&PhK*aabm0mgWtyEDsk6Qu+s%=ZW3dFLT?)gE>U1d1)@+%J?xBWq5ai=jvhRO zJ88c|dVn<$;Ae3wt>}TB$NyG(aPXb)h8}QxI;RI`*e2sYLJ!)EheZz<2+aM?sp&yd z8_wml>A}1I#q{7WZE@XB5B@CEENGj?P7lH|%`ZBq2L+d(iXNQk)1DqY^smu_WZ9ID zThoK|K5giM9P>8xVD5a-1NRCjCZfaR1R+*qF)F8|2ksTavPzZOI+u+k{L~@|r?o6m zlOUD1+{v}23E#7WA<~_aC=fmP&+Yf`#tli|;eNk0{KxnE_Vw_T`~8m_zuWzOC++ED zTb~o{3Cw7>-*1hMyon%Il(sr|x7y3w_xqz4F)H%5th4?8`AFklu`zNn=PJpVLz{CD+C;A8t8{{y_Ixb&ah@lSq=dKjH<+wli+fUnh#Uk-hn9sg&Q zxD0j$ZTS<+bOI~?+uA<-w%sb754RLqrL{G{$#2>8GrqLj??;MDAos%Qx287x{WZL4 zviAFA4}r}-FVNOzpG4Cnoy0N+)offCVB~G19rlXdps>86HV1By{IuNYs@87es*ZLO z=gHkfYyNRNojoA=ScW7Jmy!786`Zah)|Kr(P~S|{cj+d?nDRKkwXyc*vv?u+ZM;fi zy^hzHX~PEW$XSH)Hp&DT~;N10v8fv0Bpbqg^j=QBRrMlIcz;u1=c zVj}ZU76@!L-ufXgr+py-kn952iZ)P%uejlL8X__RJlv3E?fYM3aoxjojD_V*6d_^u zjg1z&eLjqQ7`E;0m2)Ts0>6KJUo3h*IMo_zGcIxb%Ng;N6B{)J8W-BZmO$)`YykG6X>@yq*{t}!UvWb~lalMzb8tGL{BEv zNVLb{!QY0%A3ur~o#5PYG>&#}i^fu_XJRsNTFT!Gj9-@FMBAI3J1%}VWqA^lt1V3S z!RT)#65=oBPzN@uahaYE#j(T=o)tHcSg!UEBz9(k7}8UKj3Vi5S|hZ%2m*8LiX?oHDw|{DW;MMz2K~F}^$C%`vik_THDY~?wCvRMQ3VQNf*Usrl zK=K2mwzZH39u!ZXA9x&oBBQfx{J`(Jv?U~Vet@UKs|ldjsJ=blxesxc#(qj=*MIidUS1$cY?0D}E~hj07S^Q^F-0$N$lY2k`eXc;q#U zq4Z0jm$XXd_<5hk1BhPy)ABdg8j@8?Kcy|a zo*=TVw(t(wESt74E|0g`^S@JHDAoF{8HqMhcvkJVlfuh=(5;oc)%OtPYcGYDX$oYT z{%zApshdpmJ(;G3X+eil_>H-zlDf@vCU*Iz6y9AJoOHE1qY3K?o=W)00 zL;-fKGOWj0EN@gM6J;>9h9r}hd|oRV+z=!Ae%g0k4}E`vb%2&H&N}?x)b~Y)#i^54 z6W3SffHd(4orRY+Y5`ST1Z$?amJr4-<-k|k6Up`bKcOwGMEPwM1OE~|I&J;n-D|8B z+WkLCKe+1+*{uIxKlrce12$@m!sJM+1Ii?If4`M!o@$%Mt`B%nrkRd3o#+RL1ZK1Ts{HOVq)20t&;A#ASpuTX$%I}6gNG6sIXF9SU z_+P9qeBoh6B$oZP=UgOxD3fWPITd}lS*DrUIekdWJ{5h~db|Vr5dN3x!?uUGTanJ_ z!?v&6&RbsD_yx-(zt0#=3NficvFsPO8n`Wu>=(ahX)KDuqi{x-^%b#ZCyF-p6_>P9mDm*_ zxw9}_X+%~mjfSbEVa3LGAsDU0+4RVZ+)+y~3f+UT{i`_o!+P2>OJ-dMorssZ4%j;u z7&lCXNMnj^(_pd&_vTyOwp=A~$NzB0DI`I|(U|Btq!L-*S_Wc?KTak6=vaOU)4)B@ zXZj#Yh@1M8O!f5am!(S6vR{VFGn{=lN^r9*Hat?F*+NFUtk1OeJEx`3Y$NZt?EP6E zVk+8SVd#1tQ-jK+GL!0p?4$MmYOJL{J(WJQlltuXkc57pN~6+3rrqUp&}gN3UK4(jgD|f&(U_W_uy6i1nr&B$B%19m523;Yx9i8XD2ox| z7k~G`OZdr`wu z&Ta7ntbj1q98Pcq@Fb=1@mAm;VRRdopz!uqh-_^II^hTw-g*j--~u!!ntck6;Pe$D z#2;k($>_MX(UUws&;%x-SbURU63nv$Qc+QR5a$8EhL84Q@fl7Tq!x>1Kco|Z#&}$J z$fud4W_nGfX}=Q-OtcIkPwTt@rql+Xt?Ywa!)W5XKstj#tJ6$djd~o9<0W`L3N+6G zx+@;$`CPLy-nd(B_WS>C``Ab`ZljQBAB{+2G4=@e-((SY|+1mzxK?G4t zc^#IA^!WG>^GK}@=~YLakTT1eh>mNu7BIgf z#{(s+_8qHZ>!De&%|B@w3*~mS3}aZ)U}-EggA0oOG|_WDnJa~E_aBPmkgDGeb%31q zHk<0dPlD|qJS$9#_QXAs_LD%kdJ2oyM7Y`Pz$pF{@^5Tn4Pt`ZpO1N+ET7ux!gMIlxaeRUnf&_C+>#uxS1qW%gMgIS8K&p zE`mw;w6+CR(rFf(@aZ%m2IukHnyzz)0L59-vQ0t^(w)+DomK3-e}x;s4%R1A2xZ|f zyh4E9s-jcmrEM!b2(*~ixIo}+5~I};U=`E4w4i#$(j)i&0u?Ns~KPHMB;$0yLCLwg$u;Ro*oJHux^ zuwQLOIiCG(oS>Qe)wxY*Irgh08;CbPfyMtuvpYW4;x7LSDzvw186Sg`hjUe!-nPO; z+NgSghs5I%w+t3@nu&*h?#_6K*QgSCzj25!h?Vv6Eecb z=}JckVKE&^vC`wVyLmH-E5RRLC*zgM#p^UHR3p!hG#Yh0~C>Btome*6R1 z!I`zyKz~6^@NRhMpr+XxYm`&f2yWBLyEypKR;~Ogh1F|`268pBfm|^+o4diSRLl0V zyBRuoY@~z!!bxHS^1_9z;Y16^0#=%nFwJ&99BrMPpSK!)SZj^ zJKQ}uH-0FAuT`ldFaTB#S!$~gF4Z0)OMTER6){vQe&En8)hEicmXz2FJfDJIZ57m$ zf>zptg8itW7oteT-9whZFPtc>?bg6dtaM_uZ2mk|p*C6spy2I5HokY;z9FSin-br_ zOsb*bDH;k-Bc3M2A5Fvu&cSz!?Hdl+$>WS1?YDz&%C5`oN8LRDGHPZbnrqLpeN*k( ziEml^Xqi0|wNq<{CgM4NzU`axdlKI=e|MRGDDry{KQ0l^`6t-EDgUIzx6Hr!N$&kP zqlBB#Zm>3F=*l3cwGiNOgc_kYguz#kLcm z3gmo+Xa*r$s$>xx3ym|P|gSSciHpxGnCnMNZ)vH1%4Bw0K!{79x@;IpQTp4^i zPX49*t^yIOE^xok8|0rUcp0uv39Y0>+Y#s%S_!!e#^7Aq zDOcf*q&*Cn+SY7UmtkP)qi3taZA|(C3$dAyxIZ(k-w7W~^Eta8`*ZM~aHdg@6F^J2 zz+DK)CDA!Au>Ijr(O*)YLjb6TUn64e7mdY3juj8tMO%m32vPJ+SBHCTkn|TKlTALc zNz(Vv$qfa-#GTm%6tFL?F7C`RAQkibuEhIjQND$?1!mpNwYUMIeCLwipepI|X^g&* z6ZrKRS*p88UjEQr3wY{cra&mRt0Oa;Ym?pO&u|Ca#s#a{wN;nU!$Tdx-WZC|Dmj2> zg;wzZ20;F0VeplPfg9M|qp>V#6LgB#ItcJc^D*ROPIth#X#ne_!-dAOF&BN;g_SD$TWBg1zA#u?H>?sXgx0_BzuV^~cCRoLHCRygWJBO*vMZ?Fa$Y zwaqRwX0x`(NiM7VBYQU88d$ZMW~Ta3H$|_Kd80on5{oF5&T#I8FV%e+({td?H`oPv zJb`Yw?x=TB`KFnf9Ph3+kTpbEk#tZ0>0R-&H)DDV9L{&ga$t)aFdYHewt#*V;4uS4 z!xCqpx6&MP0W&;-G03zxw*uX6=5iz+tV zPxT{Ry#6fIe~jCPa$MZJEHgU3U|_$Hi@V^c$&i)Nf~dexA4poGB5JT8f}Az7ka)8? zC<%7)wCMS;=n)zOGK{T_bYiy#h@89L7oKAi+qrPZeP(HGEq=)ld#+id3IEo8!FLV58}Z$U?~PuA+_0IqN+s!0jd0;E2a5op9kCSGUnA*xe(7LR7J}o^On(F>byK zo{n$D!7RIO%toFqMiZ=+?Ytko+xLf$Eha`syq^~ng)Y6!z@Ui2bRd^oFFVQ~nR?lm z{L)J=YvGqHz3dx)>93bLfaRhvTQ5uDmmIx}@E)Gf^s;n*xkxX|m(Suc7SjYb#d ztMINn#Bg6DEQP&_%Mak4Rc22BoMCH}6@{Q{Da`!Np z=*l9R*?Nufz)|z2NQyP++EL1zq$>ceE&A#Jl|5p2y++GY3h3&f7~<=-MttJ0iA2O9 zUHcTD@W2V~nmlmgKLsOT90$x#1We$7szksf4p^87n9Ko-5&_dV;F(0gOb%F_2ngX- zU;R7>FQed)BiS%6+81lq2ncboh}8gFPn^? zcvydO0$*Q77f8p$-odTe2-}UHc-VZBoz<6B;fKPsu{pW(B1 z(>_G20+ea4Bu%sca-90C4*?gH6hD(7F|607QR-}Zn?Mpoyd7hJrMEzzV+_{3q<1e9 z1cvxaImR=Kx6HtK`syVpp;TYJ6hEHI)i2AhSLK(K=x#9)%IP5E5wxA&-zeW?ch0zSjsW*UbC*W zwy8^L+HQK&n$9V$-It0lv98ug!>76kSF)B(OBsMGSvS#@tS}ygX0)yvKRKmz){Y!U z)IW^kX{;apd4OUCagh}(H0$OYXWkSY%ztG5|% zkEe@I;3p6a7(^VqjnL>M?8p{24}B&B8sP0v*|C24Or!p01CW57%r@i08*q#5&rn92 zV#mgdeV}czgHbFO2YbP7#w?U&RYHO~)Cg@)vQIo|T$u#W#Dxc_Xzo#b__Vncmh9K* zl|o!*jH+i@A;xHG5v^No`d+v-8H2TrCmG$Op~p>n+l^oRD9I67jP$U;*qctU){9|h z-@~QK+s6SvDS#t)sgm{q;HY_BW}si2f+KGwJE*kVqp9?hW2=P9f8ap?fCuFxJn|R> zNLD;0&Q;_Ej7XP^yK5s^G95RP;x9_X?|lRDEtugVGXC3%NL-%`zwpcAZ@T#w2r60& zfM+cw9@r!{*G4jMRcP6s5Qv0Q8|hLsutBUtr?8_|Y7MyM{{T#Q5^mB+Yfx&VSq#Y9 zK48{fH@Sk$E#JX+auy*uF-_a=?`{Nsi&6UpDln=S;%yS%Br@R!=E%z+aq=tPk)_Cn zI~kLWDb&qNkQ_KbJ?upo;SKw;B5xoU0^{F*#J5k(XYj*3)6}b?$EvH4sZ_US-hv5; zw=4+oi3J23?iIL+Dai_qtOfcOi*mCxxSW3SD8L$~@mq@FMMs=zT>GuuM{a2!Sx%8@6saK6HXtU!nNRW2VTeDseP)i_Mv>A*ODn*w zqMU!G$b%S-HVs}#kqaquGa^BqOpvdu1~K7c@1w|<+UIc1v^v=l zozW)dd5U?QVkWllBu%_AgCehLlZ|60Qp_Nl28+bftw7rig6e33a8+y~!Zi_hdw{Os zZEDVBy3GSpBAn&Ic29aVow&a0Hz)f$*5dgP&y^ta-D9|)9nS@LW;q5p{)8tjX@H|A zo;*C);khb#fFq3OH9U=Y4&&+W9N@SR&nP^%<0-@Q8$7G=yo={EJn1N(>ad>k@ea)E z>Yhh@C0%?&+OfDB%lFCLU>c5`hI=14z8CjF)sT%V?pRpywU|F=YPSK9du@*GX&zxC z*;@?JkPJ|+HwP9t3b3qbCd>i#+EVH+sxM{1;qNcSv>hO_*E;{z$gheK6EMWbK;aHJ zE1LacB`w3HUU$(kBK_Nq_paF=<2T4cjZOGa#no82A-XN&l(;k_c&$%VW<8sM!g{C$ z$IUDBL?xl6vi3@}2aD7tJ`s|XI>eM()$@HChX7ysm+@4FM8_kHk}*pAY0cTih~0S+ zm0Y;{P1uS<=C;KuyI6#%Wk6(O{%oT1Sj65#ys>r!_Jt)^!+SOEk-2&Tfmj+^YP{#9 zE|NgiIBP7uzDKW_c#ZrXq^Zpiy6Tf17=TRoidk7JW7N}uEv2>a*Mwos+hL9-74Yb| z_%I$d=HNpPqanw~8cZ18`|V@-g}a>4%^VDj;?Q=ZIMnLtw~rzWz~1X<6m3QDD5~x^ zp(pNS6i3cVhLGaA2|n=%%|A4Pxjgf5tbZs_fKQq>YAIf6N>q(Ql2ttyuN304V)RTa zgbI|C0W*zvt-0`t&BmzY_#DhHrq_3AG|TWB=>^KzW;*VNryxv+%e{6Me6StF{l>(^ zl@fCr$3|R4d(1!|ll$3Xc#x3|)F>CzMP{lt5?v%@GH(3=aOii)-=@Z{ zvc`12uB9f1Td~*1xIQpn(Vw?T{J{oBSh5ZPE1E3L&nlXUtTDQRr>rq5?gV)&Xp?iX z%o#`Z7nxBXa1)3RhOm?aNLXM^)3S(Ue^G}3_Q#6 z?87qv_Z-{|9C-(w~|@t5LM)Dj2dL z!zzRz!>W<^kr!6WHXxVbeGzSlAYFA1XQ>{Ih@@wejIBp-l3sI6rsgmN;!WzArXcXT3qRIFOA7DnvpOV;u zb0tO?s0+XD<@2~Q9Av;z<}Jp3gyLd5jfBHE9>KjK;!&ai)jvf*sosOO4B9+N@x`Nr z{(gNpLQFys50P=WzX-}s4rBtf-AZXZ>f0Z~tzFQL$rc%FXMhfYe#6rXky12=$Of5| z8n1ox3GOm(4qQE2%s)mu6s$|Wf?Y(NxFxH2Nzy@wxEb;G5k0aR#=m~L!d-@Nt=kP3 zn!!EaZk$j-gDwSqsD|+m0Rt@`ttYA3 zuAo8PQdDrjJ@0icm4@3$LvYcMFN?)iTtAGFAXv`D;4=qq+&ngl!RHQEmmpIqcgGz0 zw%swiZtEZ%g^q{s!Ov{tHR|6OuV{Cst;WGOld*gORJI#$jiRw*7Oobr5uCt-RUx_{ zaglpL%*$+ot4C*G=txc|BYCH{-FP31Ph>4ZtItHj$WpwCUC{pl$_Z(ATSyD)ndz>0 z0?}*=xkU+&>uG$5rbNmpb5CSOCEjr1XGR@`PC;nxL0s#&tu}`L;1RYk5jPr$MKA6w zb~N$BKR|5rh#X+o1k5yO7AnsU_OMlQ#JCNWFe&>Ac*3MCFhG*BEc`Mlt3Df)J9;gV z8-Mg_A~*i1myq8dy`nmoelDvXOg}%U{yzQWRu840bE_|*pMKSs(NE9neELbR9!@{0 z)c|xlGM5XhZBQDM^cf}aw-T9&mVknbM$rH|jcp)Nz&%}SjqVV<1}?|`F_6t$MY>{w zBo-JP8Z!{h*I+Xq$!{0DR3P}=cELA1hT!g}TiZLJP_1kGVFmJUZWlae4uW567rYDP zDMIJKfl$1z8XL0A`#7$i+=i%@R(+*8~vmc zgF!O|kuU85FjL!Trm&z87U(@OQ`@MHtFuIHt4jG*Je~5b%z^IDk z@qcmv;e6Z%ArKT01r@~uK{Nvi3>pL#1qBpE5pPx|qAqH}OpG{0(Zypu@mlfLT@Szm z0xBpHE=9Z%58fjxC@zG6=Krbc_h#NqqOiMue}A+y{i?gGtGlbatE;QKI{=rd%T7>9mX^Pn_W+ z?S;linz8k!xmCi`B+UThBh3-UN1B7`O>?${vsaVuG%`NY{EALWhU9c-J3e1I!vnNr zJrbC5dsT#6^L9;XYf0T&@GO$lt%cr~jSoCe0WC zqtJ%aTHDk6iYhy*AeE@ZU^7;#>sw?i-p>}j0^&4PqFhv>rTouO z75Y#uX5VPpU<41!`V#)wPR{l+u9PrM^h~e#u0d7E2g)*_qu*o?r=Y3ttJEz8kftiPJkJ&Z@`Aat?8HL@8k-wKhXWD~832&FLs7z}HjA36r@9hW4+wB*wV0 zBs6If!(6STkc32*gw0H^6WW*flID7O)*XrCN$ET*>~a!0KeOHUJ7Zovc3X*kuEc)J zi=8M>26K5u3T5=b74~70;BR#k5M9#5%ioO1ru;K~_{LQAFDX@h z)Dz@%Fex~pzaP4z$YwSvH#QDdR5t>*H&_K@bKaP}dtktRd^FK(*f*67igla}%+_%- z5H;D&G1<*B*?pFh-G`lM{e^D&JtoCmlj1Rx;+d2bZG;zYCdH)LU7O?twrA%A8lhtu ziVNU|w8#n60BeW;6kH43;I=t|{W|0XRuE?ImlNoW{~_EOu2dPs? zT?-AjxpW9E;(r!5*8TmxaEUANrnICjEcPC7%kg*OcL?r4+;CiP;q@}l)f7HDGwr|)-f3q65Z{Gx8(`L;LyoIH| zEnBtL-!|>rw{1tgID!ke6Zadg0r@w>jf7uj;5Omfw#x~GaUI)JZ|?5~e!s+hhx-Bd z3+`;fcgr{F`@>_ekyiY}fk_>SlkfW65aOSOy9_rK_bV<39-rN1Ms)wGBUs%n*;j#C;=TKMK#d|g*i>!Jl zBh7w!CEBf6L3S{BAZUcytCER9X*g9V)8#(`49_HC0I7{nY8zZ@vu!XU}?A6qy z$Zo5s!D}LO3yHIJ>sPQv+j}MY8|c_>b!&N2RVN%AsQXFY4sFbvGv_ecH$17%F z98IC3p-LI&0~J*b#MmbCHTE_e5j>V^?P|?R=8fX1+Q&0D8QYnYD!xkha`sB$T)^@Y z0@0=#_9%$G6nP!gITwgkpH*Yqt8QU zC~9xM*4uwS2#sss9jf;jK5;mkg(zZ&kFj6eEQ)U3irR5xe1obAuVLep7%BcMA}1f^ zBXTSsE|GBp^UIrcwEsE5PvkE>(gGr7pil}pXVSb<@Ywc>9~<;t%g&0s5Wse2o#0SS~TPuBx-6S$AI;3m&sC?Ay(K^Bh@w&`|8puC%vx$8bm+i2Z@#x|`bs zYVEA=q@hqhue66BA?0Q7m;Qs-G}OZ^CH||1T0~pB+v2wUPd?m+!YBx{qz{Ful!vz2 z4bJs9)=&tQ#uDnBCs8Hjqv!9tgId!wo*mkRD zhUQoz!KMLajElhmGjtV91LBp%?y!hLWEqTCH+S5S!kK-73!3;8(B!( zf8D-fcRLmi@I@b_3SFzVguI@R$u(!`$`SVX>s>bZfhe)1US!FOV}+iY zBZ{;t>q=g+l;RG)NN@%e?6b@hVxH}nFwWZ@M;gkjY zMD}H0)z+#_k8Eg%L|r^mcD3^ovel&7|LVdzrfW{1VRvNl+?>E&_>abaE&k8(*Wi!g zZ*xFS;EonjU25NP4|aW4y|0Y*lBPr)K1cy^RTUQZU;z_Ya_6Z)7v zb`)deDeR1J254|L9fW0dy6WroRH;2SMEGHZTdxkr++$+{YtizGU719b@IE-+??P&k zkre9#?0ndtzm7CD$nBxn=?q@)4$Jt+j;ugQbvinHj2^|BwvP-HV*j|YN%xIR5ZE=a zqUN)y^s{h2hZ)d0LJQ}nny3||CeK zLoZOX;PR2oM=R__<>&y?Q^JB~5C2gK`;P5vRD21O;1zbSM~r=#&m?#;v5ZOZo4pby z!N^b5_T`=ZCc(UZ!b}X+t?ieN5IrguRh9O;2ea=x9UP*e^`X5uj`pHdwCzF5#y>}- z1;J!+)noWMk$Oe)2a05@khPVbaBA4r`2h$E_5400G0SR~4Hb&vGE zw*B1R7qs47Tn|eI7i5#MvkXF{%MnRgwjQoDF@?J-WVik?1W>hsbGtOBuGvPf*pxnr zqt)68pHVa4=i&MofE50R{P#AywjT>f-iMwjkdlwnnv&mMAjnx`UFFFWU==ChM2d9Njra=G;vD z6wX-r;P0^59`R5Y^TKG;w=qF6%L@X)Gr6rqy}GIkIniAAFl<9UhE+NCmH z$=(<{OKFsNWfE@-3s~WHf|F5U61>8`K%y)oN-8O{C3J{{J}C(5Aq9Km6?Qj?QAP~6 z!pu6*m>kFHsBJ`~^NEyFOHVvtYN-xs_Y3NGB~|ZwNSiC69dDx!Tl@}bk4cPOw-V!f zS$NWZb!zFEICX0z-dn`0x0bM0USU5cQ646WZq+jr%hFXAr@~hp8xHr74dMOePYc~U zmD}6OuRWlu$D+f9vzsmIDnHqa4&^7M5+1o<&nL(P!`jpg+RqJw4UYiFu!-+Tw?4}2 z*34uJx`0E@7qj!yaOdjafy;I*$(3$n75o(0lm~Cik!4|S*&lWL5LO`euB$-$8H?8) zgL89zyFpzcU|~y;IB~Z*0odBw?4B>=1x-z){OTC>Ol1O87|{zSvYZwoEPUjRPU2FW_M0*M?SECw#*u zzAb$4lE()~?XK~gC$%rQfJ~VtJ>##mMcy2lZ1yIzrVDXa=>oY9rVIabOr`Z>I`2Qn zG@yP==l|!JenPlT;hJOqE<#V80#EwSku}o&P{oLp$O7!!IE|YqEXo1(W?NP@QG_;p)VrRb{q4>YB&JxY2)Pwe zf_9K`O2G#}T59Z0_J;$Ay}WMh=JmwRv7eCG_tuU5Q)GqcZ z5;b4#Am6ngeg_qZE-tE$5Gjwb3K=I0y^|;0pzYBmL)l7{CJF1SG2oU*NbiBdAS)uj z+0{AYm(ETmG{mGEX6fHG$+sjHxPFoZt-F4pWwFPdK`{z zH*ON{R@{rYRk)vU?fc{e3UQa>qeEZ=p!d*(( z1N@FA?)|t0xb?VSaocenj%3}BI~6B!XYl(FZXs?x?q^(!zBz%za0R&F(K&%JxS2SK zKcC;txc0}ekA^G6U4)x~djaCGC^AvZGA8?f3`c{|4u-@-dq?M)MnI*E5gvx_ybO-G*GI(zAoZz@~#yh?5Oy}Tv@6b&1 z{B-v~zhd&3E*mzNb;}#bEgk`@^guXVwA+Wj{0$4E+ivD82CDIi)uC7i0Z-=UBEUM% z$5zjuK(kNciWvn7R#UN%LVi)1Oih?BerBKjCDQK2ym5GYgiY|j3N#792Q~1NDmq&!o zCu?hes|_C{AHVI^53BUWRvaK7k{ie+nm3$O+Kunv#t&C(iMMy&q~vWfr`>2pyP<%QuNIl%R|WGnQ|K@{ z=s=(~7ae4XUXum6Ooetk5IftcZntDsv_`k2Dy0CWv!rboEknDSNo_DLd({(L=dT+3 zwu_jpp9PC#_>Ei3(VNLYGVPoh?j^rnGOf~M!MI~cZl13O?_hDbY>2S|yEs9KFUAyN zq}1_vKw8JglHz1n!f`9OkDQQ@b016Wo0?I_Z53&6EcgON9i^OjqyoA^Ur_^756y)L zx9v?uCvyOmQtJd&*E6eIF$rZuZ48K=S7MVSi9SbrV~qw8_2dwLl#DRKNR%_ zS$=teR-&44s%G3&B9w@TV4sXo=3Ja;eSi5``U!^EN@)e7Sj?8rFD$TBv=v8?j(iTU zHy>GOcarw-B%jSR}#ivhGR2tgyCj@!xth1c9XIkdm^67Bl?X{JS+OTH@@YE`)RC@RVp`FD}x364E`dWl(v(XkHm<-(%#4(r*i}OnUQRx_ScOC zRvE_q^K=BCK&MZ*9?k3NJT-jB! zqKjO}#{BSkN>+gtP4mbquuAjm&!i)n#D?Tr3pX~mW?V#k2q`XV8PDyn9;? zq;2lAIIXnbFcDL0wnx2KXA?PgCYH*TVsgqwe1WfPD}G21Vle=%_U)HK<_MRGCs4a> z+$nP|wfh~a(R#?2{Ok7agG@#s^)lL6E4fJP#-@;qLt9B3<_?ARv~{0wTXOCYt86n3 z5YExQwUEB`WvvxG$PF$YVwH}C6yfL-0~<5yA?c6=Nmf)|C@R@)1?HwtJ6$rIn?Cgl zHbVo&zeaX3` zD^OZ99#YlvSo$dtr+UrgqJz%f^=w?DGG907PV%DNU>O% zwy|#^a!drlWxK{k_y#vz;p0tC zgWN1P4z@}+GBA5tmTaaTQM_}a@B-25sqM?>)5*JO-MriR^A5@3s_z)}&1C^kwnF+p zg>Jc_gw0z<>0Mtnoe;FjIB>?WM=uSOIB5YO(Yx_SoZRr4CA;QQwN~jb0JJ%B6K3d@ z_PjEhqr6?kcbj-i0cnbhX7n;tS=-EFZ9~JmM+46(P7l}@TWsfyhMc;X&H)qh{OYqC z2*L@ixX{kqMK!O0a1X37dDZ=O(;kqNw%hNf)vdg`$-hS5NS=GX-Bjxw!vtI61SZf- zCr5VWhI8Vh_Zt#RF0@%?KhlC?%!VgY2I+f=V%A0U+~y37VEsqSj6l^vDG~qTkN6Fv z2oW#0GQ0{1y6>Kt5=))Bg(q`!*K8lIZB5i|IueL0Z`uuowqIZ#DQ9ldYG8M%bXhqG z8KWZW>AYt{RzAHqC>=MjCs;m%crQh&Pq4~PZ2&u#x3gd6G&y5e)<_+@s=cv`E^Q7m zzOBX!!5D{WEA2}@gGDDpiwWlB9Kmp~8~oQb1P>$FJ&S3k>JOpP=?hdx04PUO) z@EEJ~T_g#wWCzJ{XR$!=i&83r=1T6%w5^n*)DGANbK zab8As>D+!c30jdrE9Ek)?0e9;LhFR^)e619EKmZIt+_98$&DN_n?fmHJ7qoAb}=5Pg{sR#HQL!wRa%K7~zM5TKNxYadL zvyrCP-dYeL>Ix1X{u=R`hMqt3Pa&zJLVb(Qsi3@#!FxX84?DFazP-X5V6@ zYmRi&ejDr&wZbPK(bu@sOqN;`-{Wd4_q26uyJf8yQE2CXya^rbZ(w?o%mY60?tb<; zm~HB;@~Jji=u2IM-NuA(q(Fz9MS((~@&7~qFV=k;0xa}hjb>U7WZ}TLC_I9 zhrbHB-zu90ek=N^2bnePbSt{u`|Jt9Jx%+ zpM8*N#(4-v(LOoF#c`*HLMj@qg5u-oE2S3o(fq#?Lt;sY`N>#Vk`PS%V+h!Wkvr&M(ST~MGx`n z;FGUZ`zW zYJu;q(t~MdHxpkcFCh~w$ChDJF1QCB4epT%a)5<9RcvmE08vqkeb8bCacnT`g_9t= zC-HQG(CYwR1(VYbP~FPyM37Xje{{(LmNFRpo1Ki50&f$dKEDdguXzprixs`s^H+z5 zX_5C>r6abTYMC9q|n;^H^FM++Pov#Q6|M07Xp-?}|{=sZ$FT^5GjPc?c>!p)sPZ^vO zI4&Rixs-oYXdq4g@!LDZeOgTHC-Zy$aEY6dk%4o6)A;iJOV3D8m(Sn*ec$_6?|sf^ zc*^+Lb%>>(aP^R!?1=3|d8?XR7IQzg6wU3{riJFjDCTmm0#~k&6y+__abOSkh(~Hq zw8}n{epTM?#OGB|>ECUX3M%iP?k=)8r&=_$fP0(_hv zK7=YHI(vb}vXd7C^lp}_^NF5E^g1D3FD0Z$eIY~l&?TA?BKH%-^?M>;nXN{1+hqBm z-S*1Uh`EUd9cCT~5Nh*fE{=V~Mz-u2zBY|qWW0B4z$(3i5AFe%`(ydX6&)o2V%X8I zv`=|db}H^v`CM+s&hHlTp172U8OrnJPrJ!yh}=#lZl;nl`hwVaGk31EFXr+t=M-=* zM7syKuCOoqE267=T$Z~$j0J)OoI-#jyC>8fwV3GCE9|}m$)225dL7*{-dLR^#bh8f z)=C^zrcv`zb(m8tMI9D!7Dlq@m>b*xX^UOEF5G+GkkouliMbS@6QmEyXA6$Uy@nPd)K6MVN8bhT3LO^R;qQYeSQv2`3%T_c!4Hl zerI^%4nhP|)e$N9t^=Q%=~P`pNU@K>CJ>Uc%{&~C;Aga8(p+KxI)LWyFU=qD>0(#d z-Q9R{NH!jM=}Jy@MV?!Uia0Muv5%swc6KwANi2Ti?T)=Hu~LMGGbE2u-KG@8R>`u#(7M zYb`PA5SCIE|FfsV^fE+CDFR7aphv(V>#CJ@sP-E@cB3bl2JoTinC*0twdE(-s=&^h zuXXlke964gM>fs-c#}zD?ht~^DgZ}j_O@2!YbvCq7W;_lR_RCLSI#S`;R@yH(=Omb5P{WX!_?E-S_tccEO7}V?~0ydH=*OYOR zgGoRH;Yxc+ghcWlfma=~g%QpVuPv!*Ddjpcyu74__2Tqtox*Q|nC;N%h;kr)Vc=p) z7JgE1LLy`~ksMJI;~yy;a+}1uMgEk^pJE|I2TErJ(pO0hk1|Z3c7heblE zqV38vHD2_GJkMQts@^H7L5Vnh+Tm98OTI~Ys+L}wN%MQxi#`;HmdiO3pF|{`6@AhR zpNmIX!&!>YD_S2Z+CVERwN+9xMT*ukd^M^2Nb_Az7FDP7^#TFmbEUq{mOp36pOIR{ zRf9`vUXb?b9zKzXQ@|HK#_;Ol66+A-KhQ*OFPNdLPTg<9{$J`IGNjMOK5}^w&Ql(= zBkTw|?-I+SyqVN{=~F;<4(a`eMi%p~Fq*{}agAniNVS;(xe~eBK8}y18PG-%qk2c3 z0YBkuSnO_HQ|a?|Oy2*n*DW2*)=RSTVYaLqNl8B2L zICyKqM06hJ*CU>rIHy}>B4}_VT;MfrrO_!)OuuNY>C<{+)W#P$+iG#9@S#*(gvT^g zbR1si1?~tU)*LNRfs_?;?gBYzrIuP_(P0pcA{Qlu=%Wa7?gd^_GfKvl0amnye(}zY zrU{rdb7*$wa^wF%3%8kO&|&NgJkF7B?mEW2FF{h}OFhraq*TZ=c+5~-TFdP|9=kf( zqNa7{RjwGFKJ6MS%5n(eQCMqRY}2m~sf1~k+4h9|c6?U#5sw!8ec~uhOYt}ll0iw$ zhf=4Vt>_DYB%*NpDbNxgM$B-&a}BWsE){V*^p@0|Aw@seiZ;-%JEf-#4tFHZhs3d> zKT=dlZ|2A2+){wu6E2cjeBs5gc&^FhLvV!;ae6D_b~0NJwxSQ3s%s~`$2a?IL`w6j z$4uBMedjzuyo^{-cBjnRhM;*sr0di1os&TA`VoBHUdQt0Jv+*L=G;P5uhL(aNW$o>&Y!}+K6`>k8`65eO>E*h8KD*9vw}r=oJE!$@@Y)P7BGd z=5?uEChyY#LHQo7Q{`9TnBygdc>3aTvGf#}VCjO#S~R z7Om%Bq%5RYd_|RNu;!*Z=t~KFdqC1oEm(;u@HAFAE(1iRV zy=a*0C&(G3vBJ`e=(H!AFDWc(jxh0Fl3vu#^%LZ@F#cb(+ce_?Dx9sfnpc$OLSN|z zzla8k4IqB^HoLutKGV!L z=VBMm?{kC&TBW78YQ@QLxwxuf6aF>jU#nJ4nzU*qzvf@F=B-+_z*&5@)FqNktEeUr z(z$2IGZM%0QgssOo>&%fEqd%{tccsiYEU-0H+S&7k(`Q0PvLHxS8T<;_?s@AMXPQk zhr7{1&DcnLM5_0jb^tFYJ$jE>A*Ic|L{~^9QGWGNqsVXBS0%JIC$p#JyNWgPRv%+~ ze(pI-OKZd5@_qrtP8LJyHCv@c%yuR>RByA3H!(t;p6xY#uCnG1#+8y9RdBGh5CsQz z$xJDQ<AJpmtJV(7>t zYbgr7=@%d?VqkVKL0X0O{y(NP+q)Z+noZO@=548x8d34cicr-%r5{j*q4JmAX5;3} z-)OZ)MIA%2_HLuSNo7+Gdq)-0;43UYXuPMW7j3WAZhM_YeqM`^PWE3=II{D--hOcV zW^VDwBOjWou$MHFwBbenmW^;S;iV_kAFx4d`|BSB_K_VUE4S*3CD%N1{ezk>tV*u= zareZN2PJtL$?w029&&L`pp;*W->LlerNu1+{cvSB)%p4n(RuTV?aK$29+9j@yabUT!+^ z_m{byc<12Uo|rwv<Yzh*pIV>(#=uPJhMbG z`Zw-l6gUFBsFr*Bkaq{UcN1uM5ImQ#&@@Lsqnf+i1tM<_4yFyV)!3r_V!m=nXO|5g2;GZg0&Xq3Ub= z`twS2<@W&%*}-1C-6XH{g>7_ zJ-u<`jK-M_G8+h>VZ%m^viRqz+T!y`690_~db^!{RNu|Db!NA7w^6gnolp(Yb;|^U z4%}6F=f}3A4R2XK6s7o&O}WaIsYTV`GPP`iCy%tsLdug3PP)TYy?sALd9rvpzp8rE zg9SX1;;LyM<{=1G*vgn<2hGsp=a_oiLIa(GDJ4>IGi58@5vi?fE!bfa;3l!?Zfhkwg}=HlhZ!-9z} z>exybhnI<79I?(z>C3>I0&300Qlc#W1#i_cp33|lP+rxsWcPjiS~tP;*tGOSZxgA` zux@&ZPj^3YrTzXn_3@0EgY_D8nG3wldL10!8v)b3FT2YNdFrI-Wg^H%zJ{G}0AVl0 z&gfmj9GNF?QfF6eXAHVR`0uE@vCVp*^;g$$D0mbe}?b6R(vV)+!;Ab|KxR=3cOc%ytAu z*PmL(RT#>C1PN18RWyFVVr-FOC<}S^^^;fQ#GWPhT=Rgb$m?c5akOhh zk{g-cZ35?J9chM8Y}fFK+X^T0gsHYen2I*fN?B&3;QO=T5hK>hhGd>h8#jL=Ts#^I zx5b9`eo|kdYKs!z(o2w0q`{kmmWO(+aSL-&`SH6$kxhH_J}woC-q8d6h)bph<8|97 zQ6LlIFXG)skZWzvTfdprSy2H;%>|^j8;VIwnKnzUI=u?OtEjqob>sM#ucS;`K=g)s zPnXo9ZiTo{!R~Fo%9RQ&j5)(6el>hzwRH7r$o!SIoq!#MQE zj)p+W%;4`g=mi?A3bzJ7~hIo1iR_ISB+AL^JVOY(5B9+tUzEydFF2n6z6y)4=%Wrx}e9K z0@D+HWfreFvuhq&-AkDjhqFID)y3x=NRGNiDm&ICu#C($XsK;GocP#gU&M#PH`wE@ zF|&{yX4Xp{o3bj&kI{fS>#VbPuM=`_70%U)3@$u{0?@kUhSQk|s!i30jQwkX11#2z~EtC?jw|}mZFA1|>{a|ygGZU8dI5FHt zxUd6LNpPXem(Eni@srEj(ll0dGn*`4^MqnE5_~yqz-lYX9$KBoPo=gVK6KWCHQp3E zzVtPPlla(Xf5wN~6l<4KP3)pA+7u#c zUZUQmZmaAWDBr^ZInGmvx8-E#nNNAtBf< z1+pxS(Ma-01Srq>-z0wJGgq#uw4cVi5H&F=R@%>@`VNU{oU)%Db4O;@lkRn^5*gPp zvpsq^rUK`K$oYlkOT3j^ei3iwZFWZDquSm<(cMYS%D3>H)XLJd z!Sd6Zx@}ufKImsCkxs;Gdm&z{j8}+T?)c4X@$M8S-7RTo!0w_$z)MD&#>1!bzA0op zZC2Isa!Nmy=j&#IRfHiRA`1hTmby7_|DBxuG0oZ|tLiwtA0UOqx)JMEK|Bt3--^kH zxf$=uv!uDP_NcTU`^00$r>?7q89%R&8IMcU^#8()2a~l+P-nbxX6$;JP`BQ>g0U&G z zit$R~P0WR{|3+4DMzg_WCSfc3A;>+n|LH{RbR==St9SR+Y^^OEtecw&A zu-lvCZ7yn}t{l^(tmPX%TPlqc_BdcH8cm_}FH5 zmv_?Uxx{M}>L(QYo7+6Jji{$by(={k5#Sr5Q1Z#mGkdxdk2govCpO19*O4=3E+&xZ zYTBi~zR4z@&dr0zJiT7wu(Okx{M)RLb4az_F!8a?-bHn}9P;C3G9tW^P0~o=q-qq32iLssa!iusuA|E%XAh zTUX6riU?kJQtjjASnmYy;y-zv$D*fB^|NSb;<#>8RMe|)YWO0JC;qMN&ysu&g)Q^Wx+%Vi)w5BljUSX`*sIWdQ8i+_Bf}?6Ww1=j= z^$t1a5rAPaeWkomDr>k1+k@Lop0Zknd^^m@tm5MGJy;2j0dE%Z;;+h z{~qg{`CFR44)Z(b2YmauqM757Y-WkRYlE?mL$Md6Y+`wI2MsPea=P3t$1R@xI8upG=ddH)Wa zia;%}t3KXb%N%h{WEV(|Bs@~l)!oeG-HUZ!$#O*SRN7qyLrY@FYCN(ymxuHcXdZLX z)K*kgXbx3h=R-ITWsmB1O~HUqtlQ+so$8nM1&yQL;;Yv>v|GX2CD59Wsaj8R$(May z`=<2_3hp9EMpsxPBaQb2qMTY6QgwMEqZLab7#f7=UaKQZx>g;V2z*Wg36KZ` zF3zpuwhWmqXeImhq?ENwghJ>gRQ9%@St{kT54WI^{w91j&rZjM*#RGhPx8ojJDNwN zR|-+o{Vw@tw_a#?fJuoAC8rscGJG*eLqW2z!}&bvf_=EftK<;m=xynx@ZG}1_pAmXYD(X!^451R!Aem zGlpcTZpa?{uyh|WNUSr)@1CnnPt#qC-IP8QD=$V*tkM!n zSFXjAsqNIuW#@glS$+X1WLGL@smyNc?CvWij5$ds!TsJhd(RfoQ0xJ@!ERwK_B(>* zKBi0$i|h&rmSdL~EMLC=4cpr&De0qZ=PRz))`=#fRB)hz7{<%!AemGt8LYRTBLQ{@ zLTqT6Y;H&vp;$j)eFPTEPUI&OrkmeIZhlviyy{F3fj(4elJUaSn(#mZWN;16R~}?_ z4M6*jyQ!vnUbEBUg-Aa$kYMD7PJ!?^(w7c}+o)+t$?LJ%`hb;5$KUW$~~kC(-(`2=lo2*Z@eAuH|LcfVm_Fr=ShiWeV1 zad{?ok=|G$zp4Q^M|*#rjT9MH3_Y(tb0M!voC{Ov9x<~dque&T z#hdi?s<~>h!jv3{RApLGS{UGB3KcFasd}A}1uNv;0r_pMn=sN67X*>!{{@UqBky_p z1?Q8-nVbSVPkE? zSH5Wmg7wRjkT4K%zsqXDVA2aGud)|Ub}^W-LNUy8G0Xu&e90w|*RfCwF35#0dXRlI zHOvmQ_}C3B_Yw0&R?Y^H3+n%>o2NX(nnkjPl2IcvcJ+L);fD85xgxm zsOeun*#|=QLdjAnp5*1{qxeWSzy6@}6s)L(eRmv6HGD5K}O2;y{pSl-7qc}}Kl&ISk;a;}bWd8mgdyrh$ z5`?D=`~JYwe!GxRtyebW$Nue;Qlc8TuywDr45%U33w?K=S+^Iuh4pzgn6Zx*}%$}>+aWOOa>gdT$X zQ6h66l-aJ*{$ZVH8e;M}T;3AF%AwK@<)@47UjlG9oy1@m7W+U1BJhA@9;xi74F>TK zkG*BW?S>u(4{!z;cwi)fGQ4av^bYZ86oe7S22_A-u&QCM*=G zpgBBstk9Ms<}C}bd&jJ1gg^V4w-{0fYz+_iV|lS~W~#W$!rw#UR?~T)a_b{SK=d!q zoc=^hjM(hAT2Wglf{~1&s<-jo$2VTvX1wObqFui6`uVq0#_MzDGc@nc4Fsl4>l$t! zscOlM1yyO`8fvM^yFfMcJ`4RtKAYxMNA0d)i#2`$hf5%0zT?@>TMRXk!bQ z`}FfFK7)}xmQ}Wqenv%9T(+rpP;AyCxp#VZ`_VgA*iFu-u;uMer<$C;#FRd>!v5tv zg}x!TG33OdUl`~#j;=V3;?U&=dMYPd9Zs#f82)LXmvZva`7{o_-#{PdJh8(P){T3U zf!-#!ojf0hPBG90g7@!n=-CE3H4HQohvpmT=W?IP6jOvF4Cr16s2XoToegLw*I`th zWI#;~C@d^>tN~R|(6YCZSltZhTLXHKf>*UNptT0nk&AY)b_&fb+K3iTgxa8*WrSuH zY;fh)P$ki7cMz-%xV&vet+HCI$Yb6-Dmx0c?321_m+ZpJs!deMNu02OFTr&e`f{`5q zy-F_mNigy{1N}YCUjj72$bU7^IouoKG4f;s zN-GBBGV&P))cHCzpr`4v9wQ%aK=ZBvg-GF022Qx=teMF+Q93%1Ry*ya7y zP)r}sWVZQaF3a!X1GdS$e2YkIY)Lg{x5_Lg&9j$0av5?TxO79*=FEEtQ_$V4%D{;- zN6e`Zl%WAXhmS=rb5XM9Eiot+f>;Pjg~UTdP&5$Jhqjr?;H62X4;_D>+lTa7w^jC@ zvDyW?%YBD~i0XpBHQ-xs1>7qEt~B7G(h1roz^@u`E_b0hzcNI-Nah;waooo0h^?Rt zo@u~Wl>=Ut0AFRmW4SKXK`iiMk2T<^;C?s(&NtwJF~DNs?czSvfd5ejyI<|XIvCjb zkWqEM3u|CtZ!$_$o#MiNI$NuvKUGoH--Uf{VCx0e!-c(TVCPc_Rjpmv%LeuZ`Lg|rJ{!Bfx#L*_ zedP|IiMiu$1I^=D1dvix` z1Nt`vcymVw14@@z?%dJPfX=1(-rTW$w3fXYJ>Hu;HX6{i6x^FTRvOTd+5g?#@!JK$ z(K>g0{i=9X2-<+xojX=4)bM%NC41%bQ<@2%pL##KYTWg>zu}(5Ey8_>tHPx{z#B2R zLvRCeBXJXPSL0^lyt!lF_Ai+uc!JoMdRex9E9R1#1BozEnmwkH^# zuAZC3(?^`CEqIG?S%RlK8t73^08Q|80|PxlxGcfbThCAo_9H+O^3cZy`p<`eCU|5kFj{;5bbcuo9`!LW1PhV=FkKG5;xq|%|13GmsAXgqL zFrd!&0&;o!NCS$|c|D%)VnFxY1IU$!ni)_5mE`gCu2EX*(=Z19Z{(pvWIbhg+Im^M z%F~&6U7p_cl0Xel4_~xbp1!WJ@O1ZwxEl?3D()iO^|-&`p2RJ}eTb{V{f27=A9TYV zi#rK79_R7&-pB8&eyWF`RUE>aC9zgjaj23~>3UVfp$a}^y;{uT)jbL!%hLUO%TD#s zF-A;sMWj<=(Jej^DgH9}eNounwTo`40M;^ zO?0b64D@f$08MnOwg&p_Jow)2R%r(G6a;wP>c>;HtgkXyc-`t#18VyZK<;{ZxdBxO z$n91y8_;1?lGm*sF`(V#NVhT~(eJO9jY#BMFY9{CbgTI<#{1Hf<8Mw)~zv6DiJ&bz^r~N4(!A&o* z@{~VZ52(s7Ha{I6Q1838Aof*g*;4lB*q`_@v8J$}{bzhVQEkuVV`qUL2VpZnq`@gg zqw-^O^y1W#-;ipvtg;HkY%AK7{RV-wniRDZG83F znx5T{?WE6-J=U$5IPp_uSNBB3itI@*YE&}3cOXw}A()g`*{A3&ncVv((kZ&IYvS9) zVi?DBV}}x9XH;K?+e+bz9uLLtmfc9*1!pI+wH#%K zu41N4ZFvx7xEs-X9CB?3MtE!P{szyE{6&`I#Vhvm%m=6j?WNC5B_=;w>440Q8{xX@N_El zX4jtq*-x_tTNo>oy+!Pt7Lqz3N4j&NR#c2zU+2tv1VehoIda-T;Ue`l3!k7DLT)p+ zOHa@bTR$AR+*c!w3WpRv&qO**f!r~Exa8?8W-MOHkiHF)o8IXIGk3YnyNY! z4A>Mtz<`IUMXNFtJZyVuhNqe~N@0RtX?t^L%6pEd}ZuAmX!u)UcQSxP% zIAtS+I4K(`te9O$q#!HP`I;?C;h*4}UC3wH#J8vb;ib8J2@gRmHVe)_UG52toOWz@ zi;%_xapUy2W%v{QZOe0Qau1AzWryF<-%eb04$QgU3m6r?Sp&v|AJ^Y;Rx}LrtDoi$ zNj$O)WCjdDUj9dtzmNCe$|T;Kx)9!LByofxeG!F8UH%+F96`tKrSmm#U#wJg-F#8! z|8dk^3*uM}#ZDOsq&%(McBL%~<-1L0cz^lrL|2+1_7WVZ&dr5Br;r-(Q6n__JJwxf z;o_FFT)vp8bYGT;SwomFW-87>b;w>#f`xfx9g?@no%tUBk2ETFE6R0ZS}rarj#d#* z+pKN)68+5%AFIDKcoZ9MU(T{4?=Q{r`SA*K#w8P}wjsU|TMp{q`WfSQer&NBTZdKHO@if_%|a8Ga|cCQVMYZn ziWg=SFucxMCT5V7#hB8xLi1U7&`k`nhA&m~X$W(sl99JuNj=Z3n{4rWaB@}1!hZ6n z;S*(dT1>U#duc~LDWahr^zP(uf$5jZR#xfd^c2nsx0>f&Ss@l5S9M{Rxcs8b;Te;% zO0dz$<>j?*MC4*6>DE93ya|@)`qS{YaQ*287?2R2!8>8CNl|`7HdnYeSi~jRGi30~RGO*vs3hqz{IE9^c)T;Yfv zLb;_vgroQGGs02$A5uFJkyPYC1KNv5+V?ZqiTr~lr}UOrWt}S+K>x5v`zj}Oc#BP12F0r2)SY2Y55RgRd z5r5wcv9nWC)ZXI~JBq1Th%MyT5PMV-vGGbZjXzXqs;}`6GXY7BPleI`b{m4Q*@a?* z%--~v^rVmUzRP%*dt*@DE_W7Gij8QRUX1x?bva}-hPK`^P7j|ssb6sBkluzcS*PWR zZGA{g7)FXM?3lQL{W#~ZkNC|uQpZ=%c0)&2u3F~40yVZ%JG=PMx|5RZ9IAsUx*n@k zGK{Tu2Ml({FmODP2x>7EGh1WcMG{9O zhD;N(46GP91{aKRQ+W3xc!MmPX(@wjA96BS7h|SV_ilp~c1H?TGD2l&DVrH$<+g+Y z>%%8_1KSB6i7qmaM5e_$8M+XVQ3G5pab-VhA%2biDtlqJC{#qrnA-BL+0MC6+1z-W zyZVYkE`6@6w191bWAbBLuu+lO^rs+OSLQ&1Ops}C4xNi+oQ%c1(zdr!)&h2BR87j= zN5P$o#mL(#ZGof_ERn6h3ETt3g4c>3KxGHZO97dMZTK=1A=fPFOpsaI@*XPEo*s8l zL;BDh`NGnRuJ@7MpRdqFBeq~Il{qY9L{?C^kx?}a`@B1>d}u$8Ovy`q#bl=!#+QB? z&K&SX(Fl&O=mPO|VUl5Tr$%H+rtj=k{>)tWP>^?0roO{h@k1^N8XxDP9)hF8C{}CzW|` zWet@N%1j*LaqZC35|B!}<``iE8Y4fp(S8H9lW9ku^KC7?RG0k*l+A6-l*xQ_3XhLB zc)IX{vImdnZGt_RmJfi`=9thgl+}>)4hvPz{gJ-Hg8_CkfZmbZ*Cs-nIY5q3-eEzWlB$M; z32v7oVlu*c2e=CLth@(Ny8})_r5^k8A2rkQQIDE=spK~qkH#^pNnd`hRfF8AArTTp z)jhix?}EgQ6IEF!Vf^5r8&co(0xj8T_YqMKY4%+Y$c&|Co%4rlCGyA;S6RL7q2WCI*7&N zO4Xh~EdFKj{&Lwo_b4vuxUQkne(nCvweuyGc?45?Wv+Fnyqj7+GRwYyuAHROla|4I zWvC6}>4At+q}QB(?b2-Jl27jRT_(NU<0>C#zz4x`#t3)&B8d)6~<2PZ5ALO?9RDZF!Jjyh*VGbmP5aBXySxr1 zuYuf5DUs9jg1iQz_YSbVQb~-sDnvbMZ~X&oCW0I`$6O37*AMvac|&;9qcL#9M4Npl z(JZnQhzsxiX&$+>VFGe9>>)e1n{NSMYz6k~m+R!Nvg9_qB{6j)$HZH0FCt!KYZKG& z^a8m3VZg{%uB5*Q>La!3R%sid&As)_6U(_YF-W)y<$IBM)hO=wWMEhLW-9r8IoXZl zswy2W`u_ig9OnmV&j0uI(9+q?8uWVc_mN6q`3h|b$yhRl61j6?I^D6QZ+m2wjv|wx(>c1Gbmwp@uzkg@~q_<7bmfV$u-)z>n~&h=!^LS8>@AfQGF5w#PiSQK5jkix?@o9mMoM) zu|ZfP)C`~a6MMey0huE6oa-um_x%n~m&k)#gRHXC=p?+pz+ZX8b8enh#{CvGA2ZJ&n?YUI6xweRCn#VgH?v0I-&e_1}o> z=04~jWfU%NSJ_`5n%jTEa(!s-cz*PuxkQ#N$+AL+Y%j}Ce%pJNLnKQKs{)Z53)8Jx z<%+;7MITUJk%V&SKA_xwJSg+yDElX&><3CQaNv-WTQQmYfO4*&{7EMOkG5Ma*UdLv()q8a;usD&Up7euk4x%1NF&vdV{|J@J<9h!@H)O>1l zSG8u9%9RI+BDcaYbZ@-y?0|1-mkkvj3OGCYBl+a|@+q%3pT)$)aEqOEI;$_o+FSRY z;{acdrEZShd^vXX<=EIQS3e>oJV zjK8>lys$of*i#JzfqQOiib_#D*^kP5atp+@z=dhx`rJq)5^jAUrPw_M_{Cblez_FSJ# z_`Xq%{AZLDLiUp}nLk+1YHl&f?LKPCfFzHsV#%Wt@wT((y&*Ks!Nnb_2t?j zk%zkqir1c&RM$Gqcg1Jd10cf@EvGcsT$)hSpWonMHNAoDzJ zLm_k8ERl!UWnyyI=6z=-TO+2z#wK!n;TCdyAqhtgI4H9AEIT)gK}B1($01^BEM8e2 zX7iP3=R+`Ha@#qS($1{nz4Hpwc_`96*=+h={;}HXQvU}-29u?DJL3&58zRC?05c

9kvHkm;Cr(=e1(8Mw$nhzI-vi(CkxX;0Ufr}Ko536n}3~!X#*R^TVgv6 zw5iRy?*TNzF;@!c-zZ|dozM@&F698{z4_Z?a+3E9NczK19DxLWJ??b7z4Ek9zZH(xV};JupHs_oz|Q5U>9P z1zzW4r6;)2gUK-hi4E~dW37Hvf=hg3HA-$vSYd=sI*Up{t|zn&zOCp_{J=@{PNQW~ z)_>79-~j$&IaZDBZ(ks7#^m?L|61>*Jk0V&f55kD z9Sd|^7h_#*!FR<4d{mKB(#%sY7N}Z9~>o=U!o7z9Os-=C1SHHMaOy@Nu-ta3(%Qlr5pSG&N zh!{T=P9foWYa=8^P2?#&rIaLxV()^|ZlmOr}|rDSWrZ%wKYg^=;mNqSTMr9R=`>wGu3RLKc?IxBPwA zuXC-*VQh$6Ab#x)r5&RVaEvP313LGE6Z)2=JkeK7QRYwxcD4E4I6oF5B%+wS_eQIk zK@g<##FepFbSZ^Lg@Mp7q zF_p|&{FOJyTg*oi1}?hxL~sibv5ijd{2n%5kc}UI@++uSmbyh4tF)**cY(hsWq6W) zf<59mggtt4H5Qe7GkC#Yc(c_QLybe)kO#)etavuptz`ZB89cozf4N(kf9QmPESJ-$ zo|%84FsOac(fGfk1gC23z_)&0z~NndwW5iS+p-UBR%>_fKMb$VUs=pCO${G*W!n|K zE%_?p(k>Gz&ZAHd27VQkMErDrneR(Qi=FIbsxlYXC%Bub&&yJIomga=;A88P6Yw|at|2>a&KA&C7#s zWIuM9Hp{wb&0$uyyJdP`}%0CMnU7tfm*zhfC zfd(m>*IfT~d$7zr#Gqr~NKv6KweEg9XU)g7K95OGb~}eJfai>spmtdno%$a1Czyw7p#m zZ~W;?Z~wa8O{hly+-GTNd8Tad@tLxdRQ3{OJJjA!^-;~*r2QYXJs|_XWl9GAzn=mA z`I)j$f1fF9P+4iF?4rnfe^yy>rtFf)dy`exl`=;lC?onXthk{b{6+Jb}SqC>vw7JmOnpQooAzCpTprB!8 zA@7H7&(*oK*YMT8;%EQuH(h(@(H_!E4$SYoMlNF5;Js&WrNN#wh&(WN=j0+6M1XC&az!ZnXx*`JYm}8TVwZGuc1cc%NRxWQ` z!^)mAeZJZ!G>XLBeW>yk&bW*z5t{Oc1sFee88kEFaKjHbqYIxLId)np97h*0*!xx1 z%=7QR^mC!Y$3jCmAzu_Utm+9()b8ee0=_VIn77o?qY~(Wq@4r!w4?h)wS^^y+1v0X zAB9>Fe{xx6&0K<{esh_%hN{W5<`|)%VYRge*R^N0HZo}Ky`Q98`;l4;nXk(AY@C{- z37V^{S>TUx4ZVlUEG;vT7H&oD7SwJ%%|k7XoO2r?YY_(o>>v|$2P0;&B<=uAl~+H% z`FKwM3>-~8IkcsvHYk`LDcCfXg7boc2OqYPsDg3it;g(^{Tz z&wm_wzKfpU;hx|7MyCE6gh0zU_k54Y^JRK|3C|5#A&3Rbz|{GjYL7MHK9oX}4kW$e z*!%!Rj$W68;C&|YF_eOn|M%YlXMcmk#2_AJ0*a7SmPu zk(;ECTT0JHE0se|3LGQ@?+x;qsT2Itykt>DmX}WOl zW*Vb)B^fUFuV965gnogLU~n>Zl{d32K4@cw5^t);Fg~2d{N$?~-{p@x*;LGjxYq2Y z`1Nmcn`u5=Zg&ovzTqM@T{YGk^xIj43z8v1+pi4S3?b&3)?{TvdgCts6&G3y*Q*7O zW$7x+G^VOz@C~07V#;u0wzqlm=&Qw#bsx%)0z;Z_N9)?VLUueQ3$I`u>HnMp;!nQke+Rrc@jy)Z8Wn>zg>d8^a;8lShczH+H1aEQH2N$$ zHTp#|ntBs-#~A6Oh3F^6HssIXRtu;^uTU~@3m&)-t!0f4t>IZ{E&O+Y7N*735?{9t zmMPc9>o23ropiw%FzZ*LX@{SEL zXV5*#p_|9cCTe>qRe*KW!f+jBPo*vD+V+j#_%?G!#cLzQ68ztgW*|`}37ttZSbntp zvP1(EA&&cRKaPz4r2+o7l!Yx}wPnM)MXI?SSvJJ!brw8gYR47)%%#N zKCb^8uub%@o#^A(Y*>2;*3&x)>+Wn=vmOK1B|8ag zYBsFv1Z(e|gta^y){%nM$TZyn*o6=dxDMM?!tQPbthRfuU zT2A$uJT4beT1MQACBr`P8}9|gsou@cR`(53Ay@~#I`+8CIxzJh4P8ks6)gW=rl3{@ zX9Wdqk%IG7@cp2mcLdZR6&x5842TpQrh=|P!H7sffeJo*OPZVSYtC{i`q>fkn=Y`} zj|^FJ04#I9(8i!{m!R%V)TN@__d5OwA9PUvI8-ivfj?5;dK|(34-e4jbwR<@NWt|g zxF{&NJ5q4D3X&A084d@YrIX?6oLr6rKa%5ThDWod_r+K&XhAMJDx#C~CztPO{@NU^ z5ZB@j+d;TR-valqe+KT|4z62ynk{jQJ-NAle#i?n%M#w4dW3sECP?V=ROGJR-4>-y z^sa72b;4sQ-`g$aH}y>~wCWVwry!OQ)6rRH7#` z#?*s0df=}WKlj-Z>$$=j{|Hz^b0OSZ4XXKbKsJrL{}2nQz)6xtSqLyUpzL_6K-mB_ z>|{~5xvYc(xg6?`;W`6#L%KW*=~~t(Ep-$}knW9p#~&!9e@eklk@{0sTw6mr!VYt( zvIRQ?_fW7&czFPER0QH*1u?m^5PJm>Z;C)1 zE{N;#aapee5!~>xoPTwEjsR7rC0@oiXxbikn>4&qs%EW4z3&#k@kKyLy`OkkM!hd- zJJw89^AE210}Z991(lZcMyDZitzfANYJ-9Sk%E_0a9&U_B2w@t6%3+)nD~t(ZuVaf zthz7FVH)O7k5`=oT%DDi_}lun+8rA~XryXv!nvk`yCVf zQm}^#o(>9nM?h`FJrWbf;L$0z6&YhN9^Z0wNA#Vbv8~(@-`nXG-~F1?NWK1Rf#_ zpW%Zgd?srepI;;=;<=9L2yzWZvm#D2K#l(OFFJ!Y_YCL#3lb9MJ*v|@+ep`8qTD~c z3&!j`zBV_W;tI|ht;}8MPRCZh8Rs|8(1{d%F=9Et`^>L(W+i9PH~BsJ1{DVEySURb zo#zBMOijf(Y0MttnX*RN<5vjb9lWku3Up}F4M;VR{Xh! z9YyT-0`{N-CQfMHv!SiGv%jGfIt7IyZmVh+!@10w>!>aT^1w-%mCwER0q5Kd9KUn? zIVdi%yiGXA1aPKD;QUZCxJ+cKOQ}5;V*_lAAI!?WL^W0joOMuKxgWc3i5KOOG z*2+Rt>Lg!tTkKxoHJ5RRHZg%30QsMZ%JgIvec|&%W zmgh^(vE(E^HcudIp0(eC(_$Mb;#V2 zJnL}${In3qQ?`oZ3;El`@v5`82gd>94aZkzr|AgGyL{DP7LZnnKnsujf{#R4Hu>;| zKld4j<`B?~u+YH)RydK8C1N3H8j|kKvxHU?BFR^ z$yZM_1P`D<f^<)p-3gCD+R2SczZ)L)bi$Fa0TAE&qrByC=%kq&dGf9;Im6LYTA& z3=lkGh%!)uEnnmvlQzW?E_sNp`qNF$6mdK04^|pQK&hvOKn^WKqC?`nCrBzyp!UzG z9mta)huy#bAC6Xiz=nohfb+1wD!j^GfAAoR=kLvLmyQfM4QEcFT<$O8>rkv%I6MyH zM*JC{wgx}bn!JhZ>~I&HxwnL;P(z8i4R(N>J3 zPc`-B9~H%a|7`Zg5wW+o5~<;vTpGPI0m4)cCEZvWBnH%;cArnUesXy)yLU@BG9;;!2NBp>l_Q@Y zHjRHc8+ckazUs!UV7kVQITydNq>Dyx&Mt1Za)Q5genGrmhZTKy?I|kbNE}iD3gVMy z25-kY-4+bqjY}AHwtMiu_BuZv6IiO({)oX<6#F(_3AUw4g2_`BY;gwHU>lg-xGidD z^2$hFA?4<=SF~lk^URkO#U$rsFygo61c!myBQBj${uko5f>yTz#@2-dMA8<13pqswMmeM=bp=rPRdt~qq0SF>0^tk5 zGE*bh>XV9B4L0D0=P{(yw){@O2R4j8IB5M}=X_0ygJo_vBHUqE|M1DwyPSGKcIDx~ z2x|Bz3&Ez71f>A9EZHi8sR)9Ffgjtc-feEo_o#aBXiBoL1X8w@v=m;xP$3u8OMAW?zaGd z7~f&m!7Wcg6JwPzh_{wYUD?2YSy|ajLD_jK8|)g+=Fy!b)R(7cc=VQ^0{qkIX+pi{ z%8UhOOrq=!${3-v6r$Hos<7lvZ{EzJfRi@zuG#*+{eFV9@f?)xMj!DE2eZ>cGYnU4 zW`g}Og8UnogJ3^_9{4)npB*@7G?IX$;2Zs37gJIgbW3a8RL_ffQQcCp&ycokcqL@+ zivs5UlqWXX@fCdg=xSt@eimn1Jj0s|YqcA?%fq32;8fbV)j{3vzJJr~Z9gqDbc-(o z_%Q$vczmTRv!Uyv4i;t_Zq9FL+c(r7j27Vve0^Qg9?sC$r=ewBVcZ_#ZW35(mBWcI z=8c!#!mu>5Fl^8)dnzb+;Ka;I?@<-pML{sP|M%v97_?FpX?~DeIXWnq9%=qC74+We z<}<7FcmL%&T^#{FUo+-K3cgz)ty%vE7FebpYe_;!H9ksFgo(aove+b&7_l2;C{!x@ zzkvcn9fh<+PTUP&nVfik5RIQ4w%|7UOf{v<5FHsl-`#dz-iZX`J9F_#h2e8e(|G+r zJy-I)k6f$s*`xV{-Q?7cx2@-^U$PFcew-Mq?Z?#6@g$4fL-EFjScg}=+H&ir zcj1_Vd)ReU_d%E389XUVJ);dqWvNH>$5wart~2>#6XiMV)s~*F z1xel=lt38~@YV}X1WbsCfY&bt_*4qu1rzI>KQ!JXY9?mn0!cE%WIblgakx^zC$1_e z!Z^N|^y-4z^2Ee7Yy0q1vKA3?N!VsY*bP&gWkEr8q~JsqlmrFM5iAc@!5%?DEb>M- z6>P+V6{0WixuKsQQ3%o!p~lEUU${4u_8`ai=GX*e;El&3QsVVJ`CpGp_2|Kv9$nA> z`Y_1W`#BCAH3%T=yf}%*lhblNVC}`%e7>%>oX`TuW4+>(DH~yi5-GhP& zk%D_v(1xoGx)*@$76b4hWkM_#A?L+$9Nw<>7C zx25($XA5#HrOd`DLXKg2>Di#5C{l2u3jPojJP>K-U=>`iX5^e~dmsnZG65c$LEpK3 zfXRHND|O~|?MRbfBiu7*fbQj%`L>uHw`aJ3bf#$97!MY~=$l9$DrT!Go9~W9*=)j@ z1r`qGp6=+T(NS}pfPz@vKT9!Om(an>rW0~z*r%(=8_Lb%qNQI_Q7lxcb(~A zvds;8o*&S2P#W`ZqG!65t?_Ug^TEQr1xH(y6kDh7_5s`|wC{ERO+OZdl<4wC3)*ku z3h@}Ni7wg7(b5YfA7Z^@%Jq~Da8{yfQ$3R z74s#^!hVm;zVidWXjRPDf67<^YmIqHC+U!-hKzBkA*>Cpi^EpaJ;hC2Z-{k4jc#SHg(~vE?&-L%Dqp;PyapTPd)js+K7CAVW*69Sj%V+NgQ8_r9-`?W< z(PP+!x2ju3NqZuHJvR-T*_kuTf2i-*?cPFO_iviqih>Hh1o0+u7D$2je0h25=3>#q zeLx*psl8G)H#Fu5m(bOzG$bV_V!s;_@>2-k!AOf04CKtwJc-sOe;l^ zo)8q=9VvKF1x5cqn}6r;uKBT%RtBn-X+gn=NWnoWc#r~SR+#qLJAUI`_T=XJ=15A0 zmOn1js?~CiIEfRVCE1%?mnM3|~A4@k!V5lE(PwK|6oH+fnJLpqK$q4r>>n9f6Meoiy`X|yU{YNN|#_!x+kYaWbU5gTxRzJKz+NDzb60g3glnVW-`*| zbHfqjq#m_g5xjI!@X{ZHmktPC>KA!wA}=AD22eF5{7xbI-^U!HpYm6T9u~Cu*kO)1 z)sZ%Pgow@vUYZ)b^u?X7ccX)s#ztORPV^f@Z>8$?BPvO_eiqB&bAp!62wM7W&{BTT z(ruBJu5v9MvU4qcLLgBi_91^oh@zmS2m3lgJQ`^!&$aZTVvpfcLWdxjnW#sh2SEIi zWirZ6#kP(6i63B6jB)^+w$-#z7ACH5W3w%?Fd1Jh8vv23yvwq~qj{HL2LiH?{Ppl~ zOHV)}$(Hmk%@q7w1)Cmq1p^`luc}~WP%t7=@HZ9A4GP9a3VyGGr-OpKBL%-y!QB)n zu!y5^jI9$a=Wkxj78|7hO&U)H;|3koqKS2WCR5kW6c1t>J${*7AY94f)?CsQnaHY1xKr3VNftMQqWrk&r`4k{)FUg z;B?sFmiPxYXBvO0`p8)cYry=(c85n4!M`3)bIGkBc$*52+6sbWR8Z&;3{93Z^8-s| z9b=$lo_=O)ptiqkr5oRFu1MZV>|v{z$)Xs+6Mzy8dq+ssL!`QS=i2BXR)a{y=|L-P z5!2%l_4m-AV0onA78Uex%{w;JZq|##X~X!rwc3_vR%VSJ-OQ{=^Zl;h2kP^Gj?Vmq z-0+;Q$blW{V=uo6#A_!2qP7DF=E)9#xLzQR?f@cphd`Vn5NjUJ?g;*X9l&Cf2Ka9d zMBtS7amu&Qc@_k+=T;~b-G{^QA);~vZlE$Nlvlf+#(|U@?UPo05r6S14dk;}d~O6D z;+Ek%0`r&v=2}><^)ZJBFoj1N38m$Mh#eiF!vfcIs_pAE^%sBAI#+YxBfs1H5vI-*kt-nc{1 zrevUf3olB1Ugco@;lm7O7RmA}1S`2iunuytt}e;oD?Uva+O}T(>NXH;Qr^ZOkW*N zy9;iCEA6?(2>nOd1E2q&>raGE4yUCmT|()0&g~qNB^-A2sDpUOijEYj6U6IX>0KR4 zbNowGdht%QH^4z$`kjuw-%}9xaHXf?wT;TI9RI({9{A$^bpN(%?#!n?qyN|L9sBl( zp#5bBq5Z}|8?uF5Jy+01WT1Tuk8185<6s>Xk#dgTdk9wd9fGwAKODVW9Kp-MYWNAT zrc=7zJdrhbZgFt`7QxZ+>E(iZnJe9Y3&}b}rGs7R`yD8s<9}DBN9;6CU*eOauU&TU zIHF501@018`pYd!pI7N~u5@68PHyg4eC=*Zx0_CqF3g=P9mE&-Bzu%wWB%!C?=)BX zVz=zl9RDzt_T7p0HvPeRf5H|c^5!K#T;@s#b{LTy{{@v!r4(I9B*Gkm`JK(ECgh0) zW)8u>V-T8!eW$DGyheDBLtjGR8&OCIN5V*soW_(^Xn1-qsY-05C(}w=JdXh3uP_bc z_21)v!l|65jfrMuq$8{Oi3`QF6GYi3ubhA`^p zNIxT$m(=0qYX=<=(>~&^r?R;<`CVJDzmG>uAeQpi-zxxd$2LJc8i1J2U%#^habEyJ zzB~Vo8yprt+$Mt#=7&&4+&`Jxdi4>T&-1 zt&?0YucV2s)4G5KidyQ+U;o__>*ciph|2}y(E!9@TVgpUz_K~S@;>)||KR<@^!{bR z`z^h;=;egq{ky~W&v)WJ8BA6T#@Bm<9$s{(U=j4L@Fd(o4Lw zWiaC}JoP`ScT~6(6taUE?<~}rP~~Ir#oxBp=jfsjiE=h+|BlxNRc21djZuk_T^gQ^ ztFwIv1cys2ctJiNfBL%9w@&Jl*|T=hJLanIYh7_JXtJC7GHGl=F4MY3iBI)*(@EzX zqdGR=?J}b^@YrBa%O}lJK`VI|F4EE^@Y<9SbrySQ0GB5Y;J9l}zU_qrx8*O}4M?Hr z{MLU0UXbtc;?milmu<@OGAc&c+Og?)bFB3d|cCsAFV4eKJoT6@RNb-OJaR!_lt zc*kMo8y|(R#28)t*QYWYi9>MDCU^V#K>JYC#ShHT!yLb3pNxiif+q1lAyPOhV*L;fYjc^m*xouoJ ziSs+AW5q81&9@I$#h?W_6*;j)J~9G#WV=7(6x_KF-frY+@O!GctE{;@+_Q9=K`%{K z#0z6&5=P!nIng9-NF~Dx*w^j}HF6(C%C#TKZy;%7jpY&-r$#iv_ z7i%xE)8e1DD(v)G->?#|#AXiLRfY?Z@dhF$c&xQA(7r|B&;5;44MTRvnjw>sJw=?Z zPhLy>N$o6hyBSdcMSeeLL<|pM&obESb&xMD;9y-H2fFO3HJ$U9OFLP>;1KHj7YD6; zM4TyXg$zj=u*N#m7%idVt)*E`*MIuEfF$bZ0-H z<0*$d0PvM+rE(KZ1e`FtybCmsx#S4(1o;qs{PQ%LKXQnqStj`YQ;w?}#Y|@{Z4Eoe zNPWU_>cB_=rqVAUDCHtXYA&Ve_iu}EFz2N1YJ{Vuc-Yyu5-`_^sdgBWXXbuphKe(kvPiQPzSe(C?J@iD~yzF5BtSGb|3a_c}uA4Z=|ducNQPM?T>NV-wc&$XE4&LcVG9< zEtkWar_V~VrA}{bIMR&3tH6Q&U>W^lwcoC8v1p)M4J*5AVtut=Ceeic>O-Qv)wLkU z{JE;DSt`D}%BSNx?uy=e^*4nCLY!&Je%JNn;ajXH&MN6C3CoTz1fC3b zV@WrW()mPiPmn2_VMzse{E=ddN(W5?j<3@)QW)u;F8?Il&?{j8J4gzUhoV1OsVKIK z^~~#B=NEQ&o$oVB25cMo&Z*A1bPVhUdG}3Kp9u8&u?=k2Qj!S!d`GfLnWW}<{*c}_ zfTCRi<;mQcm8tH1$eSkZ2MX9gtKKu+K4+^`Ma}F)-MH$mmJScyNP~`-ea_3t+_gAw zY1jLRq4w(6J~fzi2N3vF5nSOv>j-&X$_UADH#B!|XxooIM^yq{g$(2AY9@WoCiErk zY^!!tQWK_O&0dqsSDO%8g=C*Ev18(B4jkY{Uuj1JBE$Xfl!;7E%7HuUtJo1Zb7BDcJV4fu6fU2@n6X0 zRc(NtuvP^pS;hTzS__Pa4E4b7zuukMC4yCPT!q&Ntw`Ic$lYKR8j_~a#Zc(?PCJ}H z4@i$hd{STL)8QwqQUHIeFgw3sQXe|PQeHKX3x_KUUE_M&Sc4&o^TV%d!utT z)rvILr03@f{tKSL4+kZxq@!9O4OlG)fTO^)v1zaP_16ko!-$-?q&B@_&)5`U+@cE(PbEns@$x`RU{jM{kBu1^-fvDYPpAgYg!Ip zNDM8fKP^2s8pVP(X;7U-wW3*69@SxiV$9MgJE%^2ejm#1WmtrL3>wO`94b?1ZAl_I zWg5|AJJ{;~oj|MiPj;+fBxRor^v@7FjYNU#ejn-4NQ2U&=r?Cv${D&& zZ}(NR--$LGSf2a3XX!@&e_NhcA5X)7zlr{`DWDoTfxn7BEz22D>x5&$r}=3XcJ=Ywz3dua)<<0JVzm8xGhj6 zvoBxc&d;lDS6<+Y9bfIeIe-XHlzn%hgd}=^{X~E#3Qh zz5n{z<++;P;&y~HJqei6|C40f&#j59Wdl{TY+&`U#meEfpF0}tj9@XeSazE0 zzw1E~BqZq4Q$HG|-)84JOtSwB1m78W)*zDrLc;$I;6nmBXqlL+qgIObLA{QTjUmJmU{l3T4tZjW`3UR>c$O zHLY}&>f$~sF~A~owfkXouhwRjhjk;Ibgt_4 zwR!<_Sw&tElz}Sk7NQTAlD^wJa3Fi3YYx z%IgCHY_kE|{10V!2=-JVf1`u#jH{jIplaitW9|Qa90wf-V3r2(>b_jPTAq{eHahfQ z4;l8=vxm~pA{?SZ2qaSZJ%!?j6P?5|^%GbJqm9TxReud%V{Z8u)gZC$D|9w_h94SQ zdSPaUSzuk9^VaPfNlyy*iN4yyjOWG*Sj6<>cre3>1L+?WPD4AC+p&lA6KR_7kEaG7 zv#;+@C6v*T{F(6Q!dUg2g|Q#6D~v7qyfF6Cp1oodd-aOlxnHkXSy8XpCyx~J9rf&g zIoUtEcIxmux5Mwe4!=8h_`OSq-(BK;VrM^I7`yF>!q`V#-~W4I>{hN1xrC3d6P_%L z{gUeiuDquTV4RG#y1qknrFDO zb6+itUHKY)on07a0!R!gTEn8`kOna zFm@)_AGlU?^?#jqxWYQ~g8N=`3u850PjY?5b^5%**zH{JbCt|5jE&)%$#o&PjO7a7 z@A&t3ls(2Zlk45B)cY^b4x}F)-wS{LfU=)*)p1=xU*ij1-Pibg-5c9}FML1xx8I_| z*srG$LOZQ~jYE%p08{+_(FFgBcP6xU>~hqz{OE#UIGa-jJ>T>ZH! zxh~=Q4cA><3mA)@%N?wd{N3ZN!q^2|k8!Q%I`!?sn8&r4Yu|TVy(q3VTX_~OUrzZ+ z?=mkoX209O@1idmQ?B>8qVGiSPx`7b_Ghjhn+jvkab5UzVeECTXdT`6-%L5z0xq8` zr#(|=AMX2eeVWrN_T5gsVxzd8;abn7cTdRe6-#lw%5`jBuh_#}pL3OV?iIU^D{Sjo z?mO+$D>jttJ}#f@*IjzW3cB`+P2gI~b@HygVmEUw=PK^jD>jv@GQU^sC9d;#?-hHL z>kF>ZJ*daEoU0gFF^cOh@{nG!?;P4IHb3%Dr!RF)PQJ33bC_z2dk;_6eNr043@f~~N$<;~_g2z- zRL70^PhBSau69V7ch=Y>g4sKh@A01A-5Z|g5r3>@?@}2DUNvyOW^Tdj4C>)~OO<)38#<8qZsea;&H~(_j`fG{_RE4^6-DAM3Jy>JZ7l%tF zzDGHqICw14zJ&kP+7JT7tp!!|gjz*a7{8oIx0Dn2PFCrsd@o67hU$KWyFW$uOSz}c zK;6I2eWeG1Dj9&wY!n!gx>x%pMkda`aO8QB(U{K{r_U}f(3ogMIC#K5HCyGiFd~uR zxZzT_5}2Ab9HVHha`MB)6R?3QCqG+!lWZXdMV%_u$h-n3Ta3(O?qT-G+z~$1$9O8) zc?KANf_0=h9In-V{4Dh>*m$-)>6I13x68a_UdI7Bbm=-+k>bpldCBe_YsIWqrD}!a z<|T-WXARvuO9aX)EeYE8^mU@>@IG&K44VaYWBHa8(Q+c^8e)A>i*aUfZKx9rr5op( z!x-nD0V^zEoK+Q8hzxe@rOaS|!N-!cM~;?#-`$_6`**ny2Yw;<8u-#m&kaVU=LSbS zFv@r!nHZVP0TLCx&q+3em~^p;il3Kzr$Hbbmv^jvl)*+W zs0XCM*w@Lm&v8V;I~g~fK&j0OT z4!S3U{&ldz}f(F{%Ybt!HKfQNcns$M1~ zH*3{n^*yTa~7K3Bz5lUu!HeVO+f z6GCj$vw|hPWG3g0Fw+XHR-3r61lfoy_)UKicu#n#cmgQKM(>t+b?)AsI2(}D)Hr3* zU{$bsfi;igJw-DHztGAtGHUy&Gu1X{@&N&XrYw_w)66@;Q1w-HdGMjSzsnjXS!%ET zdaE1n1MYa;Z=P!0t$UHFT4cJ+xLHIRE9H`aDH7+mETN{fjOf){Oi?5?6Xgax>C4MJ zzVZcM!w^o-JaLg@h{IqxYAy^)k|WHg6?TKj`FN{LZ@e`gca}magmnzPBdCY2EpPHslqf7wlt%)0`F&cBc!P zUj%x}_{5tancfYimY7E8)cG&O(X)Z9|M&A!V5b#m+K)(_a9Y>e(}nk?{Y3fcj@SnM z()Q9${weF$wb$*&C(C9{qrWlLJc36wS^z$pj{dvkK-J~vwC+N^r1u_doUWQf)qp`6 z3Nfn&!}t`v63ol3@~eiSG{dVEH7%Nz>%tgW-)a*sCzg3e*7g^=>iO)3cK($RiJaZAf{)0*S+TeU z>dyOem)IExcgN&v@}GQT9c%*6TGa8HUueT}<6O3#OLPn{#U z?JLv!N}q!j1V03$w!Gnk&U61+(h#PJxSQMF(H7p0XB%i}T13gf1Yz;b~%3|j>}5Z0xj@LB)?^CYJrgqMk4RB@gD0Nvjz|csS)!*Kgl9^7< z4+|!5*yp78HinWNY3^e0j>(dK?&)gbE@Qm@B%1X1uITDri(-99%qO1k&g)*`%`fxD zk4;XlFTR`ZY4LL#fBj{k7f8yIO;i$%N{x?RawV0X&*Q3+Pwe3%JY-@c8!Ag21UJE% z-Ave|cb`E`Dixwma=<@ljs&&cS9nVsR>79@P!%@coBRRM(j^Taaj3^Uq=B&(6SCFs zMFlx;d9L$K<~(dC!IOicgY+@kW`F27b)Nr3xeSMAt#^obQMj~9{Xy1Nd9SHoeUYqM zgfq(;+ou$R9)SeMq-Q>vVbW8%$Ba8d412tud#rpKD{`0mhq+ous$_pFK_*jmH+B30 zfeYobgvJFUTiX7f5gRtX!-GMsuKPZVh_-;8-CPeokUV;?W2UDGS@&wSgwTaE{eKO2 zT+J2>?7hc}C(s-e4;HT+K;m6H)_=5&=~{O(EWZG9RiyUo;@|Nnoypy%)`uouP&j}y zP8YuaF~mYMFBqIUVn7moG*pskgh?&G(+|IBuc+B2nd(unYZonrup3V!vFIEIudD%; ziXLa0FjWQsjVqn@2e3g@}FCW#pBw`AOtiS3J!%-&tt%j zM*Qo3Aibzh&HGP2VdJ3Nhwaua6O*1JL`W}NZ3Pqy?WJ%YVHI_*9-OwAs?!#ebfWjA zR-DcBhRMJjhGjMeYZ31a|Dq2Zmo@pPpX#Oy7$dxzP>MD!ZUd)EXId~{G(k$%gqm-u z&5b6%@-=2c?s^Cg|1{OL=Z+p&!5NC`wAlg4vecP5JULb@Y9ld}kvi_*e9-}Vm1>fT zPX5J1zi4kA$It$Y{N6^3Hv6HK21hh@wnxj&pl3qbaf}cCd=pCdRa?I|`M2{+E`|YU z&oLW!L5kDgKya%C<|*%?Jr^@Oj_1}t`f-~bw#Yd|_g2H-hg(}oHTiLST3K8z3*{KL zm@yN^rq!p}DB{4#dN7;`{n8@cXB?8==X6GfiCg8YZgz&r0>3d~l!pUCqVG$aqKYSu ztSE7wmtA-W!y*AP=ltLY9oRz7Az zp(8ya%YQBSpakMR^kTenmMt{;v(Nx(lIl*&^9pam&E_wKXU!V{A#5>r_OOgYVk}Jw zk5aehNeYvp!h}Un(0;9y?5~S^Z?WPG-6(D#PId^KA;$(xCJ^<_&NQ5j@+(W zIu6RrL1MgL?zIF*=b@|oPS$IA4k}8%AbHXnZ1mqjTHzLu+GB&xLxvH`QQr$FA6w}? zEp1soX?pGc6{*tp3M{U@%2P>Ui&-g6KIB#0Bfpm7fnKBJP>ksVP)%he(-WL*d&DX7 z9`IXwsSiJ7PcW}oQlWaZay8-WW|&dx%XinM&4i{$jfwiJp&8tZ8lzfSgooJo*j0;g z(gI%#x^r{4POLrlI#fy?Csbz0Y8aT$^sp1nt; zAqD!LgtwMsV=KLu+!;yl526z3(Gyp7ftqxtpXZ~4v`8#V6~D%x*tp%=o9dd{+GhpN z$?luQ)wHmsc-Vv$!x*qmb2ymxviNfw$@ZMEqJQc1&pMS(Slu6J$Ch^5D2a-`_XpA= zDl^^rRax>MleCBO5WfcSW*5hKF|V}K?8=g__;gP0YdXaE*5zPOKIucm?7%Yb;^JfD zw@l@UhlpdLHjqW$bp3qXw0}t>T|c%gb%Z8SEYVIr$)~xA_SfQl%BVA?SZ}8C4omXZ z1KcKS+WgZREuGgsE(06S^GCm;1KMB}-ttG`lgD{^3_`f8MZ`aRs*-nt*6>CK#fJG;-(I1Q7jS`M1X7Y%Tug`HTLfZfQJGO(UXKL(B6N1?%n(`qEZP zTX%B!W!^6l=<*p2pX$SYe2lj_Px{n!nWYXVYn+>beBG|f*DcGhpQsFFZ*F;hd9}oo z-a$TTJ$%t}Xa6nmPd|b6&qtk^TW*<>sB16y#ZPRCxx)!>0O3mSR`Y?B%ud!Y?a>!3 z>ZX-L7pdsferQDOd4zvZ@n3yO;kJ5Lnu!rehz0&V)G?!Nf&cf10%OND)5n@g$CFCl z$9odg{BRmC1-&cpzsQox|K-@6SddvTcBo-lylg4o_zQwJ;MMu?=l%9U?Nb&$RjG}w zE+lsx?XtqLh|S^LTZg~HIxdkqeJyOzSL3!o{J_`Ldb6wx_Fwv6Z*;EB>WJq%FoDda zg0-Jz5852hHbT6$`V3*J^XHp(fhp1Ob_`~+rmVQS+{>F#mY=Bh5|_zy@q@99lnwG+ zjn~u5oYmOm-*tRoHA*X+0{Q5{s25O&VV1n-e3#I#vZhH2_`w&}Vc6d3L^m=GTv@wN z(tdXFW$0Yf()$4@Smc|DrG5qm7P>nKgS4!FAY|Z-J!Af!DC;r{f@sKEI2Ud*)8;K) zbU~kKTK1;}j{Spam^!vFZ>lO;W8K;%{njhPPNna+q#bLV&CL$?Or!Meh*(K z$>p=;YdL=tQucv*#%K%u1?LyQ5`%0f!j0i z0#tZ;%pWu?jsa9mKgKiWyaJYo;XGy+vd%;JCnFwGjdO2VAVms?Sm?tdG3G_B+ zxk&x{PIC^D55>Ug#0#dD`9_mO_1*mcV<1SeDjSo5=XxOzah9@dY)iX2m*LjHJsL@< zX!LLTrxGg8fS`ok%^eyI5&+P@)N1TPB#e{!$Ovutpc9Yt7p6|#d`SCDc2IrLEq|dO z>#w&w=u(YI$A4Hh<;&Ml~`Wqf5 zyza$B2oe}mc_cap6w@)tt2<$2_u zl0{hHe$EO8YR@q*HX@=FmBM&aZiOk5b{Pvo8vvHO1R z-bSaG`|U|m$!1+7R*;|q+bXahNOu3DQyuv^#LH|zxE@~|9w6kn)K0b0_$V4RD}#6o z0j>4+a33Bfyf{qD0+uLO7`JjC{8-Tq$%TnVou0>V*bp!Pd{0k=Jzbn0fK}y4g>h7HfxG_wL=cn5MT)@asO2W3>k{7X-f7V=DxfdCU8(r2 zS$dYv4B}TSfCQ?_62#|V&w8_vqiSHM?B1Kwz1mYVa)~$4W zte{Hw;xex#0h;(kuPpA0Tu)#VqPmdb$oGUZ4fV)kqVDJ9m&`K8b1My7_Z8px`hgqr z)LD4-7gTudeg!nZwB!L}&9kbK)&BSF8JS;thDxg}Y4;E0nf+QWJy`O-aYJ{jXqG2S zp;HLLYUM1`Q>LZdX0KUvmXtETbdhYto1L?sWpQALYVs#etC{^o1bS`8wy zRK*He09AP@nMqZ-jeaXJS!wJC=pXJxJdoxy!#JQ`dx6^1+F(d(zaxUT%(4O)Z&St0 zUjNTPv89FWkbPuV8O<IDzQ=9rkITJ}_=?^EiI-$l zGnx95$H1SWa<5U|nFaoo&t=9qG|2`r)x;)M>h$y8)C*K9liKlr^zNqihE_iL`YNs@ z7oXh#$4UglpqA7p4#M{@BSqvHYTP=iqbN2lx=nj`Xu!Gzc)yu(#CxAH{ropP zg{+3SkkzU9-`09(nQ5|ZAACAKMX(KR3unp;rOp=l$GmOfDa}mHGiIE1L&hOp3*u6o zoe^5DhQ8S!MZ<8!MJkX4_}VR<7CI37PZJTo3{ z74?7M$;;-OOY8jKx`&p|njWOl;lt=4Rr64ZshL577G`f{4^r>P!$Hc@?wLW_@HeZ# zXom(_hf!SSDqz`dV-){M1@+f?YMK6lUxPJPGvh}-X`j*IY_T{+Lc7Y0pwx9dZ*i5D1fL=^VsJpX!Q+&X&kH>)+mE7_=K_FbD-m}f56qIT*<%l#*DS| zM^|^&I`-n?%e2lzBuU?n^8YiKZ8`nddu_-L&ih%z7ItrQ>(SVfsqenIY!_C4SAH5> zyt>N!$Xigi;*{E6Q%gnK*{@obP(%E=ITedPsXF*0lMl{O7K7eN@_o%8;IVqXj z$`VovP;ZI^arzfJsl32Kkk-JcpQ$3V5JfuD>%t6@!U!j^Q2fosQb($$>Su4Sn+ zIH_X=VTKKC%gHStxb_!a%TuTEtaLGMeOX{h#ql-R!Q3ft!E6 zv})i-wI_7w@&1&53y8xVh=H50JxP4w;bawGl=>@PU)O%yx4ir*b!eaF@jIGt_N!Z4 z(`lHMxZ=^22)20rB+w-#KrCHB8yl8SW2dS{rD|U>qkk=sD0gg2Nvvwpr(u-@hySY1 znXb+jEbJdgLDB(FdY-04a^RfWp3IZIZNi9TsqVe~-3$QimzJy-D7`rQSJn)0@nkmv zQ2(9xIwBoQ4h3+zx2~eb7a!Jq9+;pQ6Q-7~f*8i2%XvaH(jK16!*rMuMT$Dc)KM(c zKj0CWLFPQct&Vu(xxXt9nMk^QxmXfqZIbVm9{44Ph`e8s8>XAdk|EYHKqB2@`~5Tz z5k06V)W6;?Iwl=iA9jnTih&=@Epk{D#($;rF6{PLt3aZILc7C>LLni{Nt;PM%g8IW zRG@}kJh8MwISy~raZ*#gb^c`rOIYt#1l9yMSjz~jeok=lgwQGL9)&PcJSLCuIKx^l z)1MwKtU;>DfB#G2r$vN>7CO?_3|c){-ek-8RJQQ~hS~O4ku4vhPPl&Fxno(EMb;6i zA&}UaHQpN)-s=?&9~UJXT8gSBElR}8Uia_4SD}~}ii|}U#mndBHjM7V@qSZgv=%6G zI8DsUzX{33A43A>fA1O2G1=&EKFIh1yEQa%DnXfA1h;rt6<9OJyNB6%b_ek=LG%Kx zN8)ojmrhY`xhu?mAu#ebM9(hfj1Ck}qyNHj?J85|orbMC zZFtJ&jAYw2@Lq@Ua{GZQJiiiee*8IHZ*q8**S}ud#b5Lsf!WUfulO?o_avgEsdhgL zM$zh<{>**b+L@BGXJ{x=mvw4A3~6w(i#Mh6j;!+L7>D^KBxp=Sr;MK}#qzIws@HSM zO;a*nzRH@$-7ahc!&PiE~TYWd{1*(@o3WU7Sue^FK( zNMEw$|Vhpm@mVs`!P$3UtnW|Lr$*c@1sABC`8RQ4mbJXOm>h(*-5AY{e zyN^+Y2<0;WiG85TJIWuLKuPNn#Qu(|@?J$al9EENj6QxZV&0)unDo{pXXB>fJDOVE zh1zO?KZ9HeMcxDmDI3t9)eZ~Mebe8(cN@KX+}hwJ%jjZ_=3Yi%UB#=?cxnmQmS7AB{~VrIyg&>3H-Rp+ z{+QRTId$O-p8Cfa{`JKZ_>qN>0Q5A#O_sfDc?%E!NxgNLRJ4^da~^xG z7WfbG$dYsx$d3)PY{QRz9rZFQ%=j&uTIhX(aaoNf8+=Hh$;uJdK_H?HX=u!LNj1TU zL^|aiiEFCseqdWU*?~=A5MnY9LvImz;j6oXrSs?~Czmg*tij3A99{z23Lwzt@u=dXGx4CTzOvkzQnJbJZ`Pcjj{pdqiT0Yi;cMD%XWY9;a{}394d1+)${li z4`%2=PaaGdH!4vSCxI)-tu=k;+h?|+%;>> z8A79*sg3rmn$ati1I*g!LK{FOre$q+v6fo9RSbN$wx>n!&rEecmda;v+?d~u zrun|wLJ_4(8SLYhLlX6}BKJLY-5MV03#9;^RL{=zP^e2rK!?lXllC>Nd)}-b1YS(b zgYmDs!H{7(f{%4~v0qrZ32Vh+M^*v$>&$7#IcoUVAwjpqCpmefnN;8-L~IaJJrB6~ z%OKXBjm#~YcB!8KcPhwS&plmm$b33!OJpVw*)}qtBCVpxq1=yi)h1i3 zwVh8&<6M%{4E<K?f=N!U06^|DW0IZVPwYT$alzMP6ubq zP)G*BnY1IROx7b2zpg}Uf&h*lWo?)JV7>)whIZVKxs7;kDY>o{X~PPQhW=72bKu|_ zmv_wc)@uLXM`+zxJIu?UCf}=fYF?Q)eC*;62G00(erf&s+H+Hb+9&JbknY|aMB0!o zO^O|L+~%9SJB>EGd~F2UmtxjCGcm1_aj%4XY?9usmOJgu#j95C&7kKKT{gcmBEUry zwLJa`9A~Z11R_=P6ek?^?r;UcN{O{s)@T9kz2#MqK|qL*1?^#V8@s|Q3-0pUrz!|f z)P13<52(srJUG>zRaO1mwZz+752B*&da8_6Cc}ISij+=`lctw)CF1S8p%%qdMDtNf{uc!34=4*f0BOMZX>hCd2L%fll@HG!KI&oFhpb=4e-l=g8O?am{pPQW!4bY zj0nQ2m~thFiH683rD7Q^D4KjXMFTge6LMe;P+k}tfHQevQ%Rgi)3DLs`!ALk7TRlS zMFy8zd6}9(*X`o*rWdy)=yijYqU4%)iC?z+VV<7@9!k+Fwe+oI4TM!MgQ{UH%K!d} z^7Nmrc~-yW8y z%2xV;n07tU7J3Z!?d!mH;7^8-8;?+z`ga{-_!GGuE9DTOyHR>22V$VtbuflgaUsRK zBQM#Rsz0Lq_8=O>A3B7Cnz2VLB1C}OVz7z`#VrV-1%42QSX(SKjE z{V}g$B*98NBr9>4|NKGLuffbuaA!9J2H|PlwKECsVZ+`wb|N7#`ynrbydyC^SCQZH z;wDbRvN;(MDR>ZtY=++9Aro{gSRJoRcqEw$!*HX@0S>XzXUNqsY_Il(l3Yt>v~j0Z9`)Pyht4Z7%e9C7cV)*>pYtoD@#nt?*9<-wym!;LseE z=AR4mKojA!7wC3{jWopRy|&_!0s4=%9496S-P?RQ9mBm9FKJHGa?-W9Nr__Cr6deDR636 zQVzYdltp3|n6xQ`l|kV4GIx-Y_-VKzOE0~ve7v}8Hc4^Q95T4(NIUBst4h&J-V>mD=fQ-`B!!m1h(<_FZwvMW$(403(qatxKt87oQtBcz;}=z zA$hm0KgaBAbk68c4lj`YM5sIci4NL|yy(Cgq5k~qv!Tq0GDSpxM)2E`Un@MRQKxvq z^~9U|7=EWQn{2_5KG)5ZPKP;^;asF>D9=LNdVrhgxJ_$Y^ozt_woBj0qv-c{IF8xM z*#dnp)kM>QYbRa?m2Yzt$4l}1i z&LC49Q`<67Sj5+E#yL5LXyIV6O*#%2@{ybx9pyf6wV zC?ISOt^h$2L_szP2s$AK0R=@2^FCGG)ALL-U6rPL|L^Dhd|q}<&(U4K@AIpwr>d)a zX1ou%!|#_re%^ZJWjH(C2N!qa=v7{V4wtLRlI-2f54RlNX~G% z8Cobz!@v`G+|07cO!kIXQsHTH@&G>GAKrpI7$}hv$P;Y{qyz2(gk@Um8Ca$**goEO zUzRkT@$uBR;PQliqmyS4=J~Xi$&<X*NTd2n9 z+BbY(!6|SEDv&NjL1hG~IeHI?TFr@7_z0@P4iX~$z5NW)@>?Q*PJPehahqj-2IBFKj9E!A*tr0PjB|8_4Td^0&e0K0yrL_Qb0{~4 zO7a8A@Rse9m-=%oNAHk!y+tj=aH-zKlTx^nUU4`(z8-a& z@x>_gD#O_EUDU3ZhV@p%FNo9TEcuzq(Hr`cW54XYqGTe^U57EA{fMXo~y^ zXY&-Bxc;P_)mdMyF;Ctxra!TKo-(~W`Av{Nr)royZf{fHmw>AFuM0Z;Id4WhL)bhG zSGhc)zv$%2!aUpm%k(FQ&y%5-rz#xzb2po(rh)6vO1(T5%(Ln!(;p|F=Yn3Ibz#V# zwrrlQe{%gv`&DQA>4-XBl?iQ22pCoi>+ zhKq9iLG^6VST&xH=VHA>2LtR7R*dM+1NFC4Rm}G7{)OvL8@)W|FC(5IY@UV-T%J(9 zJXx4$`vFyd;DWk2$IoLY>tl1^>Vj{bA;|$(dEg0L_#wCQ(xnGjGOHGwFunehiq^%> z{4Ux+KMCm+Wgn3*`#}FEdJX60_#3OYUPbR2tXK0T4f7g3w|2d`DtgVaUXF@htaiQ8 zDtd4HhWu>Ue{Fo^dS3B4+0UT)bf{Rkmi5g}k}jQZ6pFgz%ccCC!qIbS*IR!{jFalc zDD*0SHaxyGthc&S>K;;jnH;_Ah_Cu0im!)4ul}qYpIZK21eWTWFAb6a#Pz>@s`-Gl zr%XQ2O1(U#zlw%Xo_ID-Wj)s)k{@*W(+%@1-plkSh0jx_muJN<$e(aFPw`J&e@MR8 znPnVDh-@xc=y_uk+^-&k#0G!x@<;XuduKm8Et5E6LOR zd1$v}hm_CU9KF-p^+K^;wn8tKqjyoe-lB7&z2f=ruE6+;YuSEYhh8VFSE$fS;pip6 z$lZm#4HI<%@VWPbGYjJZJr)lU|-~m}l{Jraviso;G@UR{Vtg31{;Z zpO*aznorFDqp?4BlH|qtZ+T$Kd!>K{PVjT6l(fXUtpulequ3!26 zweget6@4*ZNByQ@+0~_i^_$7ji_u=c)wL+h9tyqslh?+lR=+oCtl!~S_=}RD^_!zX zf0M@gJ>o%u-ydAR{=~KMv$U zTQD4QO{Tf_*5I8#h!#<_E+X0}dS9uOZ)(2d`csVvuIqjn3x-&=mE%wUAVds*P9Zw& z{=9P<`O{29ll>vUZQJvZgRUDj0ka_zLEr8rhDfL8a*tVf&ZlZ zOqKFe8eRVPr^FCw`TdmTH`D~@k8O1M1-SelwkY_Qsg&OU)k115$bEhae{RL)KZnaF z>mw=t@`KJd4yo&qLD|#aPF{4^`{wjQ}QzH72Ex!4UuRnY`(^U*F? zc>ZUSv4P`viU$2=k89I^|2q_T%i#K34>vyk_ciKw!TR$z29IBZey@od<3EPuSLn<0 zl`Y@M4#+d8Ygz!BuKsdIjaF`$`kf zDqTGNFwgtzgZVQ;8_(TO>hWjex5%HHFpqRTPxN1p#&)7zzdUjZoaUZd9^dP(AR zq36wY)C~D^ioW#^4f^Smwdrp5>fYc=Zk!uoHn4IaM+{pnLQ#(x;c zuh5tC>)tBafyU(fwz+!R<82ZW&96lt2kRfbwehUd#q-KHsDE6BZ?lBCo?`r2b3l_n zcR!`apC*`RLQtOe+IYUw#WVG5<%~IH=C+0-=s_8G~a%>hMJ+w_n8{>i!|z2 z92A46`ga7^ui4l5_`5uiqkcs-j$ffK=li33Wd|CQ?}tp&(;n}U zkZ8Vt{rzD5qrWzuQ@VKGsY3muSx}xWdo=lzH(ifEH)5XY?*;Rxt2Umt&*`wDH8x(BscI%v14BFn?z3*5uEpx_G|ckNoL{d8B+_ zze}d!=KJ)S+Vb6SNu1`}FIH1Cl=(hKgMNiZ{UeoP@Kpc);QIAD8z29KSsLTV`d_RH z9=`_t%Nq5M?8EUZ^yPeCw?lTIG5LP+Y(4#>pM*s7{rhhR>mMVu@x(u?hiBtn)IV+t z%CmR7CV#f;;<*j;6s-*APj78JW9R7c=aoIkpUW$P@vJG=+xr51@h;}TfzJh$~5^CK35OV54#ahI_8n`eM6~C!_D`v1B#aQoC%UR&A0phP0di| z`&ko=4%KK@M_^(SEceanN#uR%Y3p2qlh;`kN%a=yRvAK8J% z8+A);}g{K|Q#@*Lf!$)C2*>EXEt^Su6MFn{`M z;_kVx?7umI@bSiNzne1sX_my7q#hEY{l^h*RR>w`1s$~sGpAYKU^Fb zzuaF(_z4=*Kddk5X^$JfK>gz;5lL}L0Q{y|wY=Ezsl7 zD_f91mlq)(slOI~D!aq&FE=gJ)?ROt#Oe4pQxsSB*F+8auWQtQeKYDWe=ZDcufF&{ zX?*;>7HNzh>(30XpQu5f)LDIqDxrUPApt_D5K!$)ClG_4xC{XNV^q^GNNrVXaKV?XSI-Xlt(%Byrkb z_q`a{UL^fl8uT}5)UVx$+N&eDe#6I&kAL_p8so?M``p3f*PvgkQNMNrj$ffKx5q0V z$qqE8zs`GAPkS6DA&KiB^Mmz|iQ0JDzNUv~=clNDbP39Hv{;irt90?)gLz&TkUvs? zar$Yv{Wbk{ZS8f_C#b#LEQ%|~*UArNH@Ny08ufc&{Wo6-+FzU+^e4QbG5+;9{^0tR zYZ@Q_WsUm1u>PAxf$_`zwZEUBG5vM%Qa%0Sopq>xG!u~&@ znem<`f5tA?5ayEJLb_CWxZJ(y=?9AUHA~iH&me)X}sQCtarK~ zFurB0*?wL}d~L9vOQDy@(YucJvT_ab)2z_jx{4iNkGC|p&+b@n$=pCcogBT@+V#pl zK=CCg^lIMb#J5s&d>L48Lw;a_SUQ|L9Ul;e{}Z<1ep-{$}yTN@1_`mT6h z*XNtFBmg?;-#$kQ*!RKjz|XVg@Og6e@>IPi8bW#QX7kjn;QG@^FOLQDta_H|kCV^S zT`$kNcacAB**sg{lKlxfUf2Kz*5tK}Pu(SXIzBC#9oRpd#_N^6gZf8;La*lE?D%4| z$CrWiHp~i)FUNSjldDmDHicfpaydS={=fchz5QPT5c~g3X8X?J^DNTKQ?&}U@4MMN zHOsjEr0M0cV4hVonEp8VJl*y3ta}^z(^j2_e*eRpvOkUe{?t(5gOnhCI|usupZg?P z`b7=Po}o5e^Zlt#?RqE0E_#jLo|R&lR4-9QuV$&{_|~iF{R8W5ogO^C9PN6GRP@fQ zK=C<)=#lS_Xm~@8FKGTQ)SlmRC0&}o4^5MrGd({izaJ?071e$}@EnL5`}={%-V(i_ zzt3k8nN)s1ueZxMOZ^hHpGZnu6e7=E(^DX~3@-HM9pLD)j{F>}u z(DvjKJHM9sKqpC?wx`0WQhSp0QaE};wd<{4F2+jrVibCnud?I24!tz2x7wxbFC+Yl zYVFbZ`u=Dcs_)h!lS+Hs@`~(;8sA%nx5rkPZ%Q7kzqIG`oim(o(woS?s;5|dGnR1u zi(6%MzS+MN@pWVJ9bL@jn`}7WotST7F3Z2(e7+LH`Cfbj`FDN_i*M=6T>s7)&Uf~8 z#5ah^cV!WmFM74n?Qa0)TQ`~I-v~ZmlHq(Gy@vd2t;$EfuWrji*+2OVik$KKR#50S zV*0=T#Y$Mo7etUd)yM-)-&c2|#6?e~rawu2Q(TYfs*UGvT|CddD*8luY9|HbS-Rl= z(4R}MAfDmZ@R0du3qOzAcs0=Q_B2xBqVvzv99Dau_>%0G8s8|x`L-=V?Y$$DZ|{p- zzR8C3b-;Z2PXzH#nhz%W>E!w6HN8>#ewOKgNqS*pzn}K?#iB`cV)*Ao3Zc9{UhJ0L z;p%sJN1OglSbt`4{X`A=6&m$le;LRBcwqeU`Q4iNvIA=Eck;VNk0)0bq4t|4qN&VR zTk!c(-!qzTB<3rf5TyOe{!J9P{zboUG~c#`$iI%k_@w?-`GQO*k9Lad1!aUz8sV<% zdO^e`5&L>f?l2#3q*4$PdJ;WxQOdd_YZXX(;D;S{WG{fa(im; zCsON=qX?0q>lu??LhY$)9IL(0cwTl)jc0n!PRzG3JBWY2 z_V1@t>yH=owzm~--2O!vdE8U<$KvN?x72t#tcvOT)q;+`TAhK6{CZ+ciF%Ge70w|9qYdy*&9vxc!e}^0?=6{aK`!=lt`C zXNU@q+@7xF%S3AZv4jvA+8+lk;pSU+9B_tR$r5;-sAfB*X1&xuyh%Y;otDgXD=Z+%vdKA?Q($Biw& zFE0O`Od5*6{7jYd6W2Ai{O=19e?MG4z5X{_=2y#CZ9dVNud*Zpny>Q!|U}&s@Dc7RZZ#?YV(0}gvikOKpzQ_&IeXJOf6HHpZDkU#ceQ}Z~3#Lg_JLZ$+u=2 z*S~>=^Ie^d__Btv{Af73;EZP$+veZm+!dYd>t@f z{*WO4N#{F>emd3p%tpQWe6VD(XE1ReODK1Z$UYz+PdJ;Wcna5_xq5k8W1hT+nEu4_dDiRY$$uL8b7}yS z$325r4ovq&$`x;*4hTQ<+u9Iij>_40JYJoEZ9{Ym8WoYc#+@G0cauMaYLmOa7s zr_&~#{VN3XjAipg@_D-J<(ZO;{P`-K>Cdc*Tz^LE$WBGlA>hTEqFSK8g6U9th%}v>qPmrwdrG zt^uURt%tuXNzwK2OZ}*nf4#bRyd3>?mY3{(I!|{q6ssas+Da z|A683*GGa9+rKSH{mFd&`F!UL=UbkG+E)mZZ_PNaf6<$b&L3BwKzvzNmVYhyd`X7$ zjl_JVeOY`Hv$_7I8qT+EBJ!^zlW*@>F5f`I`8r^}{Inqc`PP5@blm$dhXbaj`#&ES zO%m__v``3n-AB?d9wWQM)o->%oBmB$e`av~L=F1yYt(;z0**g5Fn&2dtr;ylpq4)e zeqnh3ACKB^mWZY@e`vwyn`}7WNX%FI_aN<8_HSYq*S`gZ^KJVl@~(=bJqa`FA3P#V0(*_3wb;e8;j8pCuTdl)vgn%5>cPbp$ZA&} z1kImc+#i@fB>fx>`bAr{=^q&@nn3mM53XOI+4%UoY}2Mc9P5A4J1~BK|MwH9<x21L^kn(hpU*eYaK7cEkbfb;_0n!>IlS_mCW+51)uML;d~=8UupLse7^bL5ZOI${yYMhTJqQ4^H5-?pL1zm814y#u&>C5H2L zz9-F=R06H-|QjCzY|?qe1enf-*LnFjtxe97A9YvgUff$aK1j6Z^i8_|N8U! zq9GX@n_re^Apb&`d~5o1{Yx^O@9H4Lm(?YRf71La(oZMPukiPY4^)V@!AuB1(; zRi`^sZTb7Xmpv%E5mc{-cD*)O&!x~yG+uAzK+#(opBd@V@4rl!;|rKCrKrahW-^)3 z*IxFOil_69cm780^Us$uRmyLGE(mkqM$8Ake@Miq<@ZyT-(U~UzqQfj7vS=LxJ|*o zOr`u;jV}LIT>f*)@>5jGPib`dQG z?f&rfozDc`9vOe?p<@>8h`8m}l{=On*}NJe~CNtmu#Y31{;ZTe<$E>E&sSdGb0j z{fXuCWa#C|e-Qa|sw0!f-Iwc6u3nz=>4;|to2Ma-%d=Z_slM3wWiy%yOqH9q6##~mr(6~3-~KI7-dvmZeIo#+sRPs-=+ zRM|bX@ufSN9qSqI&iBLZUxZN`?;8Hj<;l>?lZAP<#|CfzzV%N&Pp)2`DjV|WZWSK6 zKOXHPJEYd$=MW-8?+3n9LZtn1;mtwXlg!tf&-a$$d@ov2dq3Zv#kVwt>tBiCd}sS2 zzCldBEBABxjvLN50Q0T;E6cwTe7?&JCwXE@*fRK(Yf$#=9DmoLe1zB@7B!oLLZPZ}>${B&yLX&Rx^ zGoG&ayJ(j9{f8oq+IU*rQ+7*@XOUi>)|e;nMrw{4Pb{COS}#w2ALP%e7$%SVUfG|Z z>tW^{s_Qyx>u;fwJYAp4_Q^}_BbK9AxKp#y&l@*tL}~B>!HxAzndMOS-ak7 zthb|0V0^PUdWUvvuD=WSq4^ddQWtF`OR#d@b(2gbK7neC@byIvct=Thh;a`dvb z>#gjC{4^`{wsvR7*F(ErcdWN0I?zuiN6)NXudFAEFF~PK(~TY9A?^7r1M6*w3XCs@ zqqkbS-pPAWd^Uw%LlQeamv+5etaqqYV0>p4L9zcEWmv3cVDL-UjV@>wBR1VibCnce3NVj`>6y)?3{&(9cYc-carFRo{)` z>!HxAPh`iJpj~e?*4xn{FuqwFy^GrCgBOxfd_xs_ksQ4p+V$pQz0=JD<6CwI+s`H1 z_1a)PmqIU*qnE8+Z)JDnr&*!5wJSTm9@_P~W4$HK0{wJy^seLlp{yHavfUjd?f?xZHNerFNdSITD#uKBov=bq1Vub9iL0PUM|)<)HEY(G!KQk};3(h2JoD)dq~dKg?EsZK=k^-$>5-^PybB3umASbw9j-j49V_-1kRHfYzoa0iNS zs6sE2qvz7DHy7)j4hxKLSpwV79@_QVU_F;YFOj2nQT@IZwS2v@EArE<(Ayf%j&Fl@ zz3y0VNob&-PL7^SyI$GtD82-RUd^rS_R3> zeqIzm^QWc$I$^y+gxo_36pi%Uk#r)vo7HHvIG3BPAw! zJ-_r%>VeArD-%1&HK4{9XE@)sc+oTQ{vRga-dHZ*0r+A9WABIPfcf&Tu>9-F=bK|V z-|SnFe<%K6@d-C`{Yx^O?^q|qXJPWywde93H~jlK`e42lms$Sx=kpo={V>ZrBL6~| zd~5#7^)J;meja zu9tPkdP~j+`sw88ZP2b)b`y#(L7`XEh8&&|=h2w#4$vAu+1y=;YEEJtsLcD+S^L4IC58yH`4G~3T5+VwhN zy+Vav3P&$nyWaX6QG789y~-$dd_A=5rD46*^?`n7a`epF^{Qh~d_5F;^{v?P9fBVl z(bzsmW4#?e1;#gvqqkbS-i5X(zM%@eNRFOMyWU)^ce*YxzGabYKW*Ce+F(7GLNAe{ z7o%No)#y1`Sv$Q zeBGFQN5i;$sfP32iTM_uVENaZ&u226@5N@wzw^ggd`m;Q{*}OwnH!w{A`ssoCf}71 zF5eu(`37LVb>Ff48^PyGHJtCGrpUk6Ouj89u74)O`C4JVDaTm;wdeDdtTnp*O=^Pt ztEyq~&G0rc*^{@EPiU&n{|x8bZ$^CGn0!aCa`{pX=era0E&MObzutU4li_?Xh9m#Z zA7$|^ZQ%M>^0CqF?`#<28^q+h@+X&Xj^TU*FyFdwS^kaS^Q9Wj_faVFuQiiz%N4GF zCd2t!VZJFxSpK!=^ObyLbo-kWg8ZvG%;KBz2iLzjhV$(=A---*zN449e5r=>-HG`Y ze#7#wH=oaBINyuj3ov7I#MAYS^Ix<0mj2H5ucX-M_ILIw;v2-|yYd^CZ;s)712EsZ zLoEMB@cB{==liGu`PZ7sx8)MoKa=5ntuWt|uUP)I=kt|(XmtCV^e6JK>L816#zn4w za}4L(e+BV%WAYvSmCKiEINzO^Z(%jdzutU4li_?X{(<~EU&Z2E`U}^;k~K!Rzq6MS z-ykO6l?z2dr=Swx5@1x(5f32B(Th4R+Ga1g;3iD0*lI34}K3~ZP zMz_C7zajss_OtkAoa6d8$8f&=mk?h!Cg0JYxqPXH^WBO07FM$S>&@pg8P50OMdaW4 zeJsAEXSx2Byl-^-JNql*8^q+hQqSd^V>sUc%(reY%fAtPzEs2cKKcdu*P6+<-HG`Y z?q>Pdo6l!5obSbR$iMTuSbR%sx&D>BYjpcN`!nJj#N@l;;qpy3oNoZ;Tep+t-v~Zm zoZ);Qokjk&X7X+Mk?Y@qcZ{yzR+w+f4wirI`FwK>=bKcI{Hxl|;+ydU*S{pg`S$;W z__{Iqj-KZ79bav9{oaZB7M8R8>&@p|U^w54b;!T-Wh}m>r?~#58qRn24B{KaABED`+zN5#ve4`BKyA$&*+{W^+H=i%saK0CRK>nTI%Hmu4 z9oN5-l}6X^+0%$`5R>o9F)rU^!}$hazI9)){2RgNi!+?>qf^Mg)=a)FHC+D=tT4KM zTVcK_TY~uK`~GJ?UC__p6#|;Z`>hv!FM1<>|MO<5Eq~sx_`k9nLG^6f^*Uj_LWN!m zN6)NXZ~aLWUyMSp@+dpL9dBv&GY#vl-W2F(CP%MOyI%DP6kiX8Uj4W1_-xwsMq|Al zp9jV_i=$`Ou6N-$if^bwFOs9TvhL^OEw1j>E!6`SgyJL%8sG<5)^th->~B=)UKC-^)_q>j4y|y zXVb2CvIfOxQ|L8(&5qBkT`w2w9r`pdJ~u~i$1=_J7mD?=6?(B8y+ZAJi~fuJy!c6A ze8q>@e%iF_b;5du3cVDLo>{xz`lBem7=>QtSM2z9ys5eV(y-p@^?`n7a`Xzd>s5b? z;_IQ%t3Sw&&!$~(G}ha(E-=1X96hsky$eTBd_xs_ksQ4pOEuTuT&#C`ZD4%Ms@Z-P zYS(Ln^;|wZ`SX5V{fcV$Q>TKcv7a}XeOUCI-Vb-;W9os*=lz5#*%39qb8i^UckCO) zXJPWy9pLi4WjJ3S%(vnrmVf>Ed;<;VTmCijFNDdr=1Z=BCd2uz9zuLs#Vr3?@cGuh zZgl$_iTO%DWbsYh&-HJV;e6Y^LjHAR^6jnU@(cXkmR0-2fa&j9-t7a}7x+(VPc=K_ z_|E*Fto>gy4h6Lj5lX#qbo|Sre*6l_9v}N;b-OlxAy#f{{{4^`{wwAEtJEXn-x?{a1 z%LDy%a`djFy_D@l@g*qqYW`z*d>L48!?M8ma{PK~YsP}0D_MQ^kw0zOJX^PN{pqBarz7T>x0LBmBA=(bUY>>J z$e&-|VDc>cg6og&_7j46#MY&y+Ic&sVQA{h75z_9y80mjIQeb^I$8<;C&O zC+{2ojMtls^-jMgh3gytHX9ya8?5K@>G{|H{fcVq>v5z982Wu{vr9zJ#r6MJsRwH7 z|C?k-)cB$e=R5Wv#Ajjh)qT$8J3vMtL;dT6`BuEb@~{8@8+^;RA^$>{d}}`A`d4DO ze^<96zN{rI|61_*)*8+?67!WVX7Nqj$n|fs;e6Y^K>l@P^6lO5{{mkJ%$NUi5dWln zo9L$tI-Xp|deq7-qBr9Dm#8hTM{WI7b|a|XP^c=6<5zdAw`38GRn~KI^j2#hFUvNg z_!1O)HJ`BKTcTYr1M6*A7#LrUUr%kmJ(r9Qdgj|zo5X;{^)DZQ%KU%59RL4}$AWoQ zEszWe&g10sr0Ml%-RH=kwrrlQ>$v{t=IMxe=Do!9C(+Lnv_D>iOII5EtsK8mdfl8&P}-3cZ?-+3{V6UIy0N;0}y0hoe^rw+?8m zzmpqKd^Uw%!$<7+u0t;u>m8aO7@wP?H&lCkp;#|lp%-hs-l9*DpBII|_=<~VKh^T( zdblM|SN?7-0nq%N=K~0yFJt*Ui}dp3e?7sAYJvPs^cp^p<8Q3qN%5A%YxJJMdNt3} zFt5>bYu8(^qSqYj<*4YzYS&w&qW8vH598XZa>-coLdKf(Kpx=y$_l+kem+J-z%rAdOcf3BYEZ%;%olcN^{U;UvHmzrMn2dMq_Q0Uda z&5rLn^hRU79r=Or%`#r^!uu$`p$ff7j$VRxKj&h-(=cJN7i^dF-LjQzKd(ct4c2oh z^b-AgYV+?4)^6fQxctSAGST;{2 zpC?o=&y;tOKVQve`ZMb-u0Ngh@|<`F@mSS)NPe#Wx6ISn`7q&w6m-pe_;E>=4rG+8@V?Amil| zoxuJW$>+(?%QIyaYHwf7V7LEeTz_))@|<`Z@mSeB^>1={7U|^~gn2%FhUrfxpJ%;Z zp3;@bpLjM;y+#%#$~b z=}#=5N4G!uZy|q9J=4p71%afs(CkyjzpUU(nhtCtL zm#1nu^5%w#muKBFGC{^;iEhyK`p5X>`{%@gV833~p%0nE{OK0SA-Xa_xiKb7o+%W3cUt5 zJHA73@}zM*%*A?#Ci>%}>;Hbe#^%#pG30CVcXLUH=In^CA>qY!E$q{X>xBQ_Ghm9$opnn*>7h z_u>i6e30^gz_VhZXbj~EXY&-l@W1z`HRj11&-5qu|9~fd0rKb6Kbbu4qW`@==U+lR zL)bhG&&xbP^ZO3?=@_l~Pm-tO$)RzQ9a4UGbM%&I*9*mZ*$TZ_j$XEQy+tpI_R{z+ zW(USs{4chjJ+$j}!g_@Yy%dg~S-akPH;OMtp;!4FJHA7(IHR$>q+z|)V*~xnT~qlA{-+U2iVdJ3TruzGd^+ zex4QwV=eR5HdxQ4&`ad#UB~*y$`_EIW`*9?0(N}YvA)qA>n+I&^wY`FyN>mZvLX~; zfQtv+Vc| zUB~@TSZ{S^pr4r>z17vwCjz=dOIEsjBgf4&!%1P!aNk;P=#J3 zM=wUZ-dwDAdPHD+%Vx3tJgt7;pW1p&8?5J2=p}OWHfYydS%CaBEA+O`WXD&iU9UUV zTk?-UKb;)Cq1yGz=A!r#6nZr?*zqN3*UP|q8-@qQm&4J!sD2-+T6;N}kK(f_^ctRF z$G1bfUM|)<^hjWQZjRm(?RueDFI%A(%hAi$uD56o^7G=uf$#ctl#TVn#lfPeK?=(3=weK%DVEFItbdZ?n_bcQNqn@jLzd~0&Uo@Cv?DrSU zo-KMNe*eQz7N77m*FWQbFYDMW#Ajjh)lKE{Eil}_KA3OC5SD-a`FyE{^DUo={0m|7 zt#NVvJ7;+RxH<#zWesNe*MiTNWVnALF<)s0i*I5c*T2bz^KE+u`PY%jxA!S7-&(`@ zI$*y1K`j5e^7+mg&Nq8H^6$hz7N3yI^)Cq~RmSG$W780yg~?Yph08bDaK1j6Z^c6_ z|N8U!)*8;Y{AuK02$OHkWUhbb4ClK#74c;aVENaA&u9Ga&5p!;rA`*##3#A_O@@(A#qZn%HTbC7=_OujYaxc)6LobT!r zh%d{^@~;J-FV%3qk(jTvZxBA;{g2tQdusQWT_Ce(J)aM%nkbq@@8`N(gi*UcxMnPu zr&=$M1@o**qvojbIQcwt_42HH9Qo6h&9ik3*PmRyJRLF5Jd3J7j*PJiTsH&rOePymBD1QFs?^5+h{drlI>{n2|)9P`l z>9xUnE`?sA@p>!&DH=-SGb{AAjxs#H?pSY0pFlsI9K8+N{VW@Y;!9BI)jYh7{ zu-=B0!1!`FdWG8KJDH8*vnli%MzZ5utz9n{>m9m3Fg`a&?~r!AP^_1&(2M2hnYH)# zMPrel7kdZBSDeZAGeNsvC#+Yf&`aUyT}M7!KL*7YqtL5-lpWu7_?d?FR^J!sXC_DQ zqW1PuJsQQ=L!nncf*oIWvF7n%G}hbED=@xUem%AMUIs|&n(v*+5(B35J*y8O_~)_>gDN%c^2QR>JOQZrTBRo z`*|C}2e)Wn^Le7pkBK(WiC?TJd(G#2Dj$*K*Q@s+)+@e;hIx%%rglBjUxLN=?MM_~ zii%$S!bKts;op`BnWeIes-B-R*l2=K1t) z)%H#N$>j5_)Z5=mA4UGevw13qa{VdO%hL_>EKX+nlfvgYsh4NP2;@&Vo2PgP*Pqaj zbdG1OF;8B1ra!TKo=$ps^8bPSIn|BH;~vcQr@LOB^TQF(5H?Rk2A8LeUY;z>vptFF zPY$0)cYReog8aFg%~Lao>yK_83+7pM7tv%>t$fQ4R`qCBlFoDzg}bKmt=ekHs3olM2w5hXPu(XHS@~`ryO5n z^+1A~opX=dK`#507=)9~Q` zo+k_QZ12qUC&$kdG@llVjZ@3#MNbYC?V$PF=98E5X+ydke^9;a$Zxq=@6g|*a3wuA zM=wEpe4$t`TcH=r(YvU9J#x`Q$j^(n`Qsz`yVx%K+1Px#NDTR!^@!Ug9h$%0qRut> zG)24KN)^4$1H`z*^$!)j$_F&Zm#d=pAl55Rpgvv`U#51w(JFf1I#GNnLG(!euJ0$u zr~eK_%B)p@t#`fakLM!VrFoqXK_@+T?ZsZxG@qsu?|AnFG9D9f+!8=Sv* zZDaYL!sUP65%H7$<@T2!biB!4r>diNJvdYnq~lw*C@6pbek@0?hjzV1>7s#D?_!+P zJf-oiI8F91Xnfb9*9q$tD)dr}*IRE#@x>_gDlP2z%-a1-!+NW43G_45c)jWeP<%ZU zdiAO7_ztP}1GV-!8td)o5E$Pqj^1^&mka$+d_xs_ksQ4x+UsvF);k>=7~itLv;DlN zU9Sz+b1C!^IeLdSXwEMyZOBivLT_syc6>WFYS!zH^_JWm=%uqQs7+(%Y?~r!AlYLQqHicfp{p|R9Y}V{&F4jBrS6_TZkH$yai=K#Y zHw=b~RJ)KrFvMiCY=S=~VfZw=aKR$%b_ivbgD335d+&w9VM3+nw$Fa{79P#J-R|z| zwHKDVI$GWRy$)fgUHBT_whNEO!=&Je_)+kaGpQqMAKCn~S2$=dni)Tk9Gu>x)|*^S z_xs&*KRmG)<;5pCg#Bdp+8IjOYzA?%dmP>Au;(uafA-qlZSNvS*DZ$w+-0>+&;R1Xir$sn#7mirm14BHm{tbl-zuUWB zDZG?-U%}asygSLCP6by(@@^#G-RX{8+SF{?{6#a9$&(Ljj&*l!hP(PrbvJB=o4-wU zEjPnGxTZSu=Dx5Q;BJ{rl;hy{0dKJjdp*&IAq?;aT;63DW{Mu!?Lr>-Qm{SAG7^3q zL&5v7Wtin*LMv5_O*ls|gl{cEtwZ>n)JMATC1uYb#t7#eLV0@E-{3{43s0-PKYP=K zm(IOsGCkK5(ZRe9;^@I!%y$6gpxh=u~)t1uyRru*`YI3AK4Ffm;#uv zyXW-|(msSx;h*drc<3|F^ShP$Gul4pp>|eb@HG3F2ii&wMGFr{!0)@+iAm5{kDU!S z5wGk*c&X$U>0~`6!-S^bFFa2wJsDvI^E&635g*CJI8U2n!CXuoJk&D8G8AmIL)|)r zlQLl~qzi|bB3zMBs|DNbmQ2f|nIpOeo^lAJYXq^stE!ETGPp=o8gX0{+@3gs(Sz1e8aR}$&MQ7KG^u+=2qD64T zw}g)16beb<>B86GMS9nZVsmkfset`G=1-u>3q#_=$-&}|r62Zg9)AwXyu(2*+Byh9HB{vhNpa^x*<)Oz9ZUF7i%;^RBX7ceC2f# z(vbKFQVzVg1-yj!=rP~Hdk#n>sSZIji)0n(Ghin)eSZZg@}WQ9Ll>s`9@0E*Uo^pc zUP~kGD{9_+u)m-W^r1>AygL^R6t!i#@;g<;_icWmch!%f0~b|hhrX<`)yL|cV^ zZLRLM3rXus7p&#KOKnaV1{G3CT3#w?TRll#Lz9zgns_EQJb2WZW@yi$|3c%FGXCY) zNZtWyk|@TLKK(@eWQcB{FJJQF*=-TySrIgz&F(+FRtN~X%a?WGCaW+Kx(kWO7FKO- zF${W`7?@qKmi|t>fM6gJJBcI}h#{KqAt6Ch$}cCq2j!+Mi!D zKP1bmjC%8hlsa@69gd-LX-kA5*)G&d!-S`m6v;#wGX&c-K;F0J2beFs8UH-QMyz~9 z_`PUe{2X|eKBkGIXl(pI2MoD(nl-G#7x|<5bud|@HhviK7Cf*N{1^rquhbjnu|`Sh z<2bxf;Ejm-*HS2hCXr^q?GRq1nMpWo5&E^XcRf~m-0TS3ZzlP{K4v#GO&Ho?7_}EI zj$Z%=!VHN{1fVcamjge0H$@PlU8P?5*A)ic^3oHad@%iBEyNH3F$}nMer;+}2NOA< zGh`ZDas{D}gI(bzi+fOnr!<2kV6ptV`;x0YlV1U5Qge`Kx*oF$JD^0U5sy9L>b1v* zdQN;!XHCgOV#5*|E-f17Ik!Pc7HIPp;md=N!J)N(3kAU|ZS7sJdfwY|1q^`H3W@F; za`ZRFFpU_~#O{uDe((b{9_M(NK3ED)M8W&Q`~u=}3+E{62kc!t@(;l57i|#^S%vxW zbHE9rOPiH6PD5t2lHEP^KhgGbFHva?jFy6y@gYuO`(`r#Brne2|1${MrU6^@gTlPJ z&+j3g!CZ}uBw&Tq7VSb2nOBHuWEYHRa5#)(WNuJ;!b~2-kz53|XBEmk_kQz-FK?2z zB0#H==3`>RqRDfVW#q`ra%nuicK(@Xth-?-54VSr&CPaq%XDaG zc6bbZwdDdBR=#`>8o%)2?zICunCk9f9QeF=P}_8S*a0~1Rq?n3YX@{N!AuG!QU{Nx zlbO{4Ps`hDy~4xLr1rxM0#f;5m}b#@+7r@SlJIa_;&5khG!7U|o>>5?I3oVE%eTgeG z3K}A)Em2lzoPEI)vWW*Do+b9UQ?|bgR*dNj^Qe~KP+yqrw0+(#eDO^Zu_ncLwEddH zr-=up%p=G9kEKSz;nbnQ;KP2;%FnNOh5ix3;G3g99t?Bi{xHYt3k_~&d{1b)Nw5pG zhRl_O!O@;E=NtT!PT2`Mq4niDvi0RVINqP{;QT`zc{}tL-6#V$NQZ+IJ5U%ojV3yh z(ndMlDSO`e!J8Jfaehyd(GFfAsJoIHpnu*79U1yvgvDE8i~6`9bnf}DlCDX*b-{@+ zYt+Z(mV?J(|MW43J=;GE=;M{1U*0Eu{Kn*pg4v@YK92?fD61u$EE2d)-$ z?kFDSiM&rVq#Sst8`O44`_v83{<}!vt-zH}SN}jV8X3wg!kaIVxMA0V@54MFY^0G| z1u>gddM=aG9g^z6xvk_)clcfQupLfeH;E{Yo)eL@nzbj0BN4JA&?Z@qwDe@1ZGa>iH!tqF3mwrrk?w*MTdZGd)F%R>Bi8uiQ?0kR8$k%triq1!@pFj#*eG z=8yw}p`+XiJ2eTi(>Q6TB-lxKC_;E3TuvBA6G*~B zi@x}B0kJ4ikxS6NQYz{t?M}@aOqDsfM0U1hf)5frk|VdUh>@_DYMGeFMqMWDN#R2YlJsv-KWgQ{GIWm6%&Hr;!W- zQ*-E2o;i0kc!dFE_6u`dZ&}pG!vOyvnIKfc1VI?q&hz3YC~J^0VC!mF+M#JeoJYd! zxGmIwlF)>-0!T)rM|u9z9L{i|p2f64sYpBUbIIue>K~S(P<#c?w7rD}C#4Brj8XGG zBQ*_?hDW{hA!z|8dx$MyK0J|9g%sC&T_G;g-yw{V;SiqZmGz1^PQwZl1W3C52k_ni zclf>OVLJm-0tt7(P7*H30T3=Zr-6)v!zEMWI8WzhI9w8`q8jK6qR`6_FIO3e^-hyY_nc%z=PF*3^ zyco|%8-Io*FD>(V-hyX!H+a$mUhECe;4~Tbr<3U~1Zm=V7+$e=t)VZz3okl^<4yr) z#!wiU#5%g3_Ka$O$tyerr{rJT51xV7JWqD4^%ndcCfDckrd#NeB%L19NpIh`f2N$m z1MTu|u!}S8^i5>G4ga34Ye64c$lqrROPuaI?r;h_Hm8#KqihwV@Qm#+JtpTcu=3~t zBRfOco5{4D&cn$blm7$pg^S1T_bcCY6vhQOJ14tjz5s+^lF&4-#U_$&t?ntmgLrdz zXxRoe0ci?^tx>k~FvEu z(y*37j@-0@_;LT81pg88iDf%UCj;$aHFP|MQw0*E-Li>hTzAZ-WpJYc9CzZ53lJs6 z9CnFJo@l+cfEeg$N2SL^AJQEzJ}<6xOm;wDf@LPLDcfP$qXaxAUCaUfBYB_aPY|)^ zZzsJ6)+Ax^iHs=)KXopw&g)fhCM2&j`Ez5zA0c_o#ST)iy>q!|eN(b(>nVUu)JC#& z)ww)fI6$_wQBabzuq0|pDSg@wdKC!=k~yq#`iE<%w+jxK(N=gmK^r2gC%fT5&-*aC zzHsp2{0c8ZQhvx2)&id01J6A0@d5bocg#j| zm#;hWUD$Z~Mba^^i)W#@J6S?}PJI7q@_rn=zssBig_35M+THuYUQZKPr%s*cb^YDr zfJfxKv7o0Z4_2yndj?a?ymn;0og6^U2ZS((5E%-ES-e$Vb7?{o6ZNPrl=Nq^vIPmN zCrRXX@FEUo_nW{llg9*QFYt}sWW-KNec`fT&WGa*OP-As4^D+ck{&Df?k+Nqhc`Sg znI&i93Q}gn3!$!iJ#UlH`@tKY9?*db62`&nCJ5v2z?xj0`ofo<;qd6v;r2%hz7O%N zhBt-sx|Jft40vb3f_-)Ya^)j&j-u3dMFXLJ>=DCAK82;|bm5%v2UJmEwJWMu%&GV$ zre~Wbm(-qx2;6Djg5N`)w!w`6;pbrG4yG2($w?%sow~N(n4jV9Tj4NC^w1FIt%p~^ zHL{$y-}6?3*Xw`lRs2>uTzM7WD)5H5?twzP!(qwQ4WXZ6w zB(IfkA8*VwkS+A)nG3vOu2fm^PNbOE1&{}cN4lE19|+UJ4rV)qn-j>HkhAx}r*0f)fpmG!>0W9&=k;24hLKlYkc;71uq6V5 zjfP-JZh&S7mzqdNh)ym^u83M`6-sT%N3BA&CI8%{yzD0|`4=Wk%1(z<65(8Og~i=B z#Ma_K)JjYK`3JHmxr)N$Z7r&xoN(bXoM}eDyt<`pguNiLDFo(CIQMsNlzo@^=QwEo zb4;d`k|rUhyv`}3nuMD2I;Kn}o4=aZUj7rBbllVNb{ICv|G=53w@aUbb69gd6k{pa z9bzpqpM-~XP2sg8@ZfXOnXPX7mEPxO-bSzi#43fp+MSQRQQ`2FOo1c0LiVrzpFFhG&Z*hQL zwt=O___$8x_m#UI7R)!m-UYiOc9}!r0U0%l%qDnbxl|sP*Rx=E+%9wNElo_2C*X}A z;nC+NQ`iCPt%t3>54)hDK}qq)??3K}EQs6<@1_>TZi5YJyq!rdf{Tr$vL2&r6LCZB z0$j@YDGoZ)^JK3r-B*5>_&T|5yLY)O&t8z-4l=N_oj0a=XLwDp!Q|8y?TrbCXF`d+ z8+mLJAG@7#;`5}x!Fx8y&rWEC$K&kD@Hp8PZBH(>OA{0*=~j4>?3c1fSxfCjX0rrp zxBmvdn9p_qBIs(c4h3Tv>5$R(f|SQ#ncC!P;*EI{g#4q}J{T!t!6v&mVF0`VZP6VW z3D0Z)3v~czey!m#EyvTe22OAaBH}$i!1M?ngo-_tXn5d@&LL~(bVZ|2GhoY?I&zlS2xd&Dyyq+xBO>&^EXNdTu8TgeB z59`jn0QPNYvkqDb)MV$vqb^uDi|kGczKK#pD{@EP zN1itmqqq}XP97Kaq&cx;D1^7$`%Bcb<`CEc+F*0okf&q|Pj7(DPIFBxbbIPJAdo;! zoFw@j~&u;dhe>dcEW=#9lc9)o5LmAuu@0w zl3Yl-Wu5E3gUK-r>ZGN#EqEs0G>bfH4(+lDY{}|m!S;6LqDL6UrmNV|KXG!WKNQeiuUv{`S=U!hExJ&}n&lxKyG;&VAmDKLBRItetoUYlYNHGJM9t6^Fgu%y4wdcG%Rlff*`^A5!0= z@r1KH!ruG4+*+}xlKv^ygYVdXi^smEI5tk@Sh4=$yqZuC22}9f8o0b|86;42^)=Q>y8Bcnt`fZVCl+D!V{Gd!s${v-gRuFfjFm9+VHwi@4QO z^x52+JQvK5lTDHN=wHb*Vq=l{J2-Tgc>}ycWOtcAhDXHoUFJ>Fqt7Eq!4pfMz{qvv z$X($fMA`qk5V@`$DcBJn=X(*(mLeDXUW5bNorXIVyre1$;7$cHUmHdoOa&*rF<0UU z`6s_ap~>{b9wtbmU@qv5xv3+(1F`n=lhpfU+76Q|NVUd!W4?s~ zy}Js$p)=QkUg~2bX%3^q7-%P#^fKQMY%mEBND?{@lkBmMgx{rd*}JD>iYP5(}&f79TvG?u`$a7?vB zI0A_c=4=Ijgt_3#mnDe=-^SkSk?1<}gS{TDY8SP*1Wx0k(u!_g_+ZhaRWOT%oKOx| z{oJYWcyH7#RxqqryS)3nWp*!|PC_p$xEwz7Cs<~33aLqOmg%at2$q(8i^5+{7c8Co z7g-NRTZ)FaOfMSVSuCuudUlO{%pQwieg02zH|y>QX}?FG|1-Vl(cLg>CbGml_$Rk% zyU>iDb45K|l3WtiI?P_w7v>Vu2qNxF{*qJOd5fX4gAAh3Ant>_H=JY-=-f%^a6Tzq zfK1R6_J>9S{qTW^^j@hEu6A%|O`Gpw&6Qk~gBG^`B($(WaGL0V_7F$cIUM3wn~^L2 zbaly-OL72N59#3fktjeEFa*oX#H6V}P!!9SOll*&v@UcDRJ_STFIS|y2gAZa8JvXJ zOFeN=HdxAxLn@-WJ}=eJU5BCtAAVT-br!9~Gn=>=E~vXF-+uPed=RYI@i(-+Rc zB1kwCa=<2(!qv-gakq1De5w1vP|I&6a8OBC3M;H`ONcefUfS2) zwwWc$UR79aaoa=S(eALlu4rfBQ48EkkxoEKHPOG`3Oh_;>AyMlgC1Yqk@^Ty4RO#f z;hwd0$oIk@o;sRBJ@!H`b^Xy3;JP`4y8Z|}_8f&-y&a}EMKdCbo{IMTL@ESamF6@> za8;Vja%k>EdcOO&Y;e~RRMdD<)uhTDLMVum+ZUkP&hLbGXhVr}3Iik3y?dOu9&}FJ z)6*Fi;du(enFH4WLG5r};-v?oL-PJ+7ye=w9&Bgt^!;Ga3jJ0A5kZ<53S#dJybI^8SmJiKCTta<>rV+`sK)+xezzSz#1xz1b4Z2Qg&SdV5jF2Qc--qQ*9~+v zw9?{*8**UITKERM?1h=wPUywthxK$n7~#qOo?KFZ zH7Z!XqdhPgnnCL(@E=?!hd*)YMg5ZOUJK+2_^-F6-BfrqFMbXbPb1!G3W4(S!W>2S z+Q*bzitc#MSu}GeoL)*MN!?^TsYr4+9=Y=h2E#G1jAh*!U3Y|3E&UhhPoOmpp^a03 zGjzDU#vz=rktUWAVfXGyzx8YT#4|n9!)_n9Ao-}>JuBMUD{q;JoNC<*ix_#)^wuP} zad4l-dkAK%J?zAiU0@0L@s(Z3flOKN+M$FUrGOQ7)*Tb`RjN%moGu)JHH8Q}S%(1^ z-~^MjIdZj?v^BeMLtt|Y7l*G@I+*x^g{>5?Z*2n_?WQ;=&GYGzAG{C?EI-1kC|nY- z7fpsa&m+0P zYuIk`=P>!>fng7>TEqRLE;#F;rxI`j7g<1)I~(Pj8Q+dBP{k#~+U|LKujvs}(+G7E zNs~PVxRtX59NqyuJet>L0gNMdVMLN=9)W>=^0VE$3z}cg^xm^0a=~l5KnDk>3#~k( zU>~}y2X}~3uSeDvAN~Q~9ZUQf5+6sbg8-wx`qR0H*t<$4@N{iS59&Y zV2!c}Fp`ryumEowy`)7}&w&;uo8uNZgd|7UdH;G0NmTCH(4mf{yC=0H@BhU~`cpS3 z&H{_eg%wfreg=2R#k!lwbpvOAAO#ElkP1^2RLW%w20iz{t=CwJ3EAC4sr)` zNxF~@`K7{s>u$%yDi}3dk6QpuAl>~SBsMw!wuUOm>yY02U|t;D*zG`1evnN;A-UZVm`8oJWCXTSM2Q8usx=$Gq zPXO_{7?@bfFCQc?w}O|wF_UjZ$J6r{k_6~bNjtna{ZFzBm+fvCed&caZ_Ii)3XJjW z+4BRr+f1Cq!L=Q-G92NJ5y;yGu*5IU=Y4!|Gi^Wj&7tYgPfy+g6Em}Aa~wceyH;<8 zGhkQ}2+y}fZLFAc;ub4(?}I07z00QEHqPJPtj!{9UCW?-g-Zv*UT*vG!iwpqZKS_j zeuIH@<6b!a;2F!URn5p6EdEE@8!Wt%!b{JXVFG`t>=sx_D5Il5{4EsX_T2~1^u4w1 z0#~dJnp(8QyQiCLA#Bd84;uLMhp#le^2s*TbOomZkp&cfypx z0c7PctQ4Lm13h#V@(xUs;N|od&<7-Chp^|jD&j=44er=;$FzjxNqVqvJsiww?R2My z2&JX95%%N*Q5(xF$t~xm7kENk-~U!6lq&opMRkF4wtpj*1NTlgx4SdM;~mMnp}bO3 z08Ev=u9MdmK>Ig2*%vP=dt+_|7k)e3zT~&V-rX{_-!RfYVbFy^5T=-r5@2=-b52Lp z$KsQN>0p=>+I8s}n9IS087BR4>Eub&#|NNdU<1~a(iuN}rOXj_n7j`~(MO&*q0AXp zMrIE!$+6C`az_}bSjh@uhhh6vXlKpj?P!=T&}vMdcJ5?%K?3oGnV>2O1M zX?oXEsgp*-E3jDGlw1jGr|^%&(@wlBNGc6(Mmp>yrz)N}8+?W!>f>*nQ6IyN)Ffyc zNgavgu)XwWATGBP_FBSDA9>>7fXlEi+=MqXK7#Z$@|hUvI2s8(e`qH~6m5k(MZzL- z)(|VX4W7ObTJBBQ0+&l<+x`8^dt81IJwWd#TxbK!J>-V}IJ1!pQ$Vq9kJhd;rk zAM|pZ=M8ve0e!RF|Ax5pl{3DbA*sNX{SX1F97ZuRn{&7$A3aG1EZbyZ=y?Ko*WTiU)NZC6R#wbHgs+8&d(rfFii(b6_f+TJN``$*eQXNdbPm$q}I z?RaUMA#LxMwkM_SZfSc-+BTmlmUEl5wMyGj()M|2nqCM>3Q6Ak>gM4`EkX5$E5f7OWWPj_6updUfRARZQqo(i=^$>($=T@ z;xnS&?b7SrrL9%k&XKlLrTzXPZF@@F4$`)zw7n|re?i**AZ?$Qwm#i)((@tGwu$2X z3yQc)?la9Hr+dEboM&&M&)-sPedWhZ7T@7o8{orm^)%$AYy*hBrhz=sK+jLXT+c`|o;T-MaA2~T8=ej+;#4v=1(^kiP#-Dn;=lAsQ?s6!F!nCjq^DeyCqJ2+mpwKvD|gKJ33=IL@?5#u@?n;Y zzIHfD+COf5)}*l@SHS!3nhGHe#>V?zfx zEdvMItdgE$|EF^yF!_LoGt!6H0*_GaKQ23O%y@bKezqZf?ZNjS|K$I}-n+mzc2(!% z$Ky%H36K$Q^A?bR14+<4GI2tZk;gL=JIq7OD<%wB9$Paq@k1lo69-ZdGzC*=Dh|()-TwPfOMC0`=-jpTTI-yBmd~-Qu|3M~*L#vP zTkBhEul?<{pXVWY{I2N4JsHn=&tYsJ@i#n{o5)TMPGD^%XuXGy9_sHsbfW7(uh4bv zxqhPqnT#C?s2f%64pWhd@u;cMwVCWVHW)M>XOuFiiisnK@rR}0ID!E?ogE*Ij*X99 zi{Ud}M&&Y*^fE!d{U#7@#?(}5aB?CjE5*poEA=19o|zoAyJ6Rn+mMmRXL1ZW;+e7K zRp4fYDBX6*ZCAJcq1mKy{rRkVgyxiHwsZEv_&9xfcyJi&03&LAGCSzC^^tu?47>$gfXm{dxG&MDt%J|xRUtjmW#4+y7Q2&YH(bV|lgn4`ew>HdM4RB}H!Ix!l zhP(yN*TDHRIDY{X(JxwI+YAQ3*b4hoF!;r8$N48P_}BBeUu|)IcD@zTEzU2_Z*~3? z41RHbH|Smi>3Qe-U;vktt&nc{`uX!)0ei-e&Y#~6+uYZjO^r_86ul~kC%&YJa{ojc zJ4pW^{5!$(Pxtub$Po0MiRd6S*QqQhq7%b|cbQC(Z$C^z(1g%22S!H52T@IZ3mlJ3 zUhd>32C@^;n{SDx#)iP$J-Tl?c{~TR!O7bvM}`N+PEvqRW>cB*+{wwYVVD`lP9mw3 z1A~L9k&_dHXHHIJ2gY(EP{g%R)^&r%m&ea7f9RgEfzjc?sF=g_cnaDwmBo5yCbIp& z^Df|g%PrAu(dRrTI+V&yTzl)_>5+jmIhc&P_sbZ?0tPda{lnw^+0@|pXa=f!6U zk<&13MUU-?o`w=KgST9jF^q!$iN2TD6;8zp*BX3wCr9FaWT){A_>~X?Fo(9akI7utIX!&V8P1+{ za_0t}-Me?2b98*n88|iJ%ISMD&LngN(PNssC+DobKR|~%lbSd=kR3fa3@=e-VH(L@ zmliJGc=X0%Z0ahioESlxeCw0Vy1o?$o^|{jKI6F5$B*G&%W96~*xHu|4_S#U|y zEAYTRpbvbWznY^_9#hN%CZblHoFoQe@mLDs5&x;bzO@G9F>S}g3IOu3L^lL&a4{Hk ztd&QQPnbf3;ERIINL*Ua!+h@*iE&x!n1%HNk0Pf!F3|?trzkvN_zRB~Xqb9Dh{gK- z>+xuLnDE&AFyYbGR30ViA6syPLhb!%=pV<{ujwz?Q!~f!gKk`4wdctd-_ZG=8kdLM zctCUVTGEVoRD{Q6{%#dEpN)j9d;Z*wOI4ePwa|s}NN*62e-s{2g%%#auY=FCjaO++ zIhL3QKBn@j>hjgvuw)_kKKB-=H+H4-&Kh}ds^uHV95wUsj zJhmP@8jVNP=D~ZL_2AKHJgyNQmvb#A6Fw;92ol z>$qK3xjI0?>Zu#O4{*%o0ULK&Zy_#gBZ)8 zQh2~i`?4QHu3_t~)Z-eC-Ibi`>nX8YZ8~K0;C_lTNj)B`^}kxX5#|A7r4{2LvsoCA zvKXiWYAr2!tVYS9w0b^xeTlK1${r{-WBw2#`At-h{Hv%Qavx6a*%kSDk?3QsWRG7; z#6$8I*iV%8&nC4KXIb)#|10b0Z)SZyB>#l;E4;aEjrWT^|4j*x%itU4+`pqhN%m4) z4L6x$t}ehI%3M#u<69`sBGg)c)TRnfk$4&16;0n z%-iwc@sflbYi*ZCc`OJIcvT161VfUCv(fwE#mi}YR5%3}4INdB2Xf%135|z1pJ7A% z{yrYUgCzL(5&m)O;vpXvEgmhf&Iy-B<56v}TurAYJT7U$?T5=FtbgeIBCqxSjq`Te zk2%N#I*7#MV)KXykIOficwk?}BaFvv$UL^zctkB8cxz2~l#xupF&Dxkz_cOg8s@Rp zx-X9o!r~#$g^$E%webkMAG_1$A;DM$j|Vv}pWt+@JmS(WkMzb%9(Xi5ZY55!3mm^a z5|1{%m;;B%qtW|PHT9lRqtB1}Y#!V$>%pVZ=SOLq2hY*#!K2Z5Ou0O8)Y6fM<7Q_}pQMc{rEBeGuS*T{a2l z#pa>wg>p07r|lhdU4?h4yd6pS{{816_fvJfB9CnBdN(3GpkZttVeNxIq#blb8!R`- zhsK)OKJ6SA{Juad%x^r^hW6Wtx82v(Sd-krJk0kvc%DUvq3y$^#h6c{yuz;YI_-Gy zIL4yF?q~LFP&|6^`591;6 zz#kdFW21PX?`DqQIf;w)7$6Fq!uo3>qOId~a@%E-`y4S2&cf#RjfzLxJNdc$72f2YT!F`QNIZ(%F0sd6Si7_{ z4+po4&=A-b@h0%-aO2_i$};ot5CG~N(odt@KQq!#>*N!4=L$T6`l;m8sqlzAj@l)` zskRG~d)#(`ehNldb{WpFehR9yxA>Pq*(eX`r}@Y5Ji@7zQ!h=s z@i3P)7aWh(@(7E^l*GgF7P^Oke52nZ$?&*MUcuuwBmiMN@@_m}o#Sy^-asB9^qeWj~P22Fk#4eX|!Ed%OflvvsOH0-Wmequ=O?*GOx?HEpNTBd3|mL z9@0*B9g1~$h^_9A_14>Yw_RW!<93O2JjAD+Zd5!Lta!*wChcOEFQ4-v@sRl=F7ubH zFRR@Lx$%H`Y!&g?C>~2Lk4^Y`nLA$UUB^g#BrWl{t+&qUek1wYX83vc6~Ke%u{bEh zc!cc>g@^dn<<;w_?KTgqUBY;TjhD6Z5dL=BjO)4>$7Ac3%{GX*Wtn0olJx6 zBKhpJnfkdGOGQLXI#VpRL2A%H@|D z

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 z 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